All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
rlm_unix.c
Go to the documentation of this file.
1 /*
2  * This program is is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or (at
5  * your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: 329ed2916ae82a2a17f60377f26d5a21d7777f3d $
19  * @file rlm_unix.c
20  * @brief Unixy things
21  *
22  * authentication: Unix user authentication
23  * accounting: Functions to write radwtmp file.
24  * Also contains handler for "Group".
25  *
26  * @copyright 2000,2006 The FreeRADIUS server project
27  * @copyright 2000 Jeff Carneal <jeff@apex.net>
28  * @copyright 2000 Alan Curry <pacman@world.std.com>
29  */
30 RCSID("$Id: 329ed2916ae82a2a17f60377f26d5a21d7777f3d $")
32 
33 #include <freeradius-devel/radiusd.h>
34 
35 #include <grp.h>
36 #include <pwd.h>
37 #include <sys/stat.h>
38 
39 #include "config.h"
40 
41 #ifdef HAVE_SHADOW_H
42 # include <shadow.h>
43 #endif
44 
45 #ifdef OSFC2
46 # include <sys/security.h>
47 # include <prot.h>
48 #endif
49 
50 #ifdef OSFSIA
51 # include <sia.h>
52 # include <siad.h>
53 #endif
54 
55 #include <freeradius-devel/modules.h>
56 #include <freeradius-devel/sysutmp.h>
57 
58 static char trans[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
59 #define ENC(c) trans[c]
60 
61 typedef struct rlm_unix {
62  char const *name; //!< Instance name.
63  char const *radwtmp;
64 } rlm_unix_t;
65 
66 static const CONF_PARSER module_config[] = {
67  { FR_CONF_OFFSET("radwtmp", PW_TYPE_FILE_OUTPUT | PW_TYPE_REQUIRED, rlm_unix_t, radwtmp), .dflt = "NULL" },
69 };
70 
71 /*
72  * The Group = handler.
73  */
74 static int groupcmp(UNUSED void *instance, REQUEST *request, UNUSED VALUE_PAIR *req_vp,
75  VALUE_PAIR *check, UNUSED VALUE_PAIR *check_pairs,
76  UNUSED VALUE_PAIR **reply_pairs)
77 {
78  struct passwd *pwd;
79  struct group *grp;
80  char **member;
81  int retval = -1;
82 
83  /*
84  * No user name, can't compare.
85  */
86  if (!request->username) return -1;
87 
88  if (rad_getpwnam(request, &pwd, request->username->vp_strvalue) < 0) {
89  RDEBUG("%s", fr_strerror());
90  return -1;
91  }
92 
93  if (rad_getgrnam(request, &grp, check->vp_strvalue) < 0) {
94  RDEBUG("%s", fr_strerror());
95  talloc_free(pwd);
96  return -1;
97  }
98 
99  /*
100  * The users default group isn't the one we're looking for,
101  * look through the list of group members.
102  */
103  if (pwd->pw_gid == grp->gr_gid) {
104  retval = 0;
105 
106  } else {
107  for (member = grp->gr_mem; *member && retval; member++) {
108  if (strcmp(*member, pwd->pw_name) == 0) {
109  retval = 0;
110  break;
111  }
112  }
113  }
114 
115  /* lifo */
116  talloc_free(grp);
117  talloc_free(pwd);
118 
119  return retval;
120 }
121 
122 
123 /*
124  * Read the config
125  */
126 static int mod_bootstrap(CONF_SECTION *conf, void *instance)
127 {
128  rlm_unix_t *inst = instance;
129 
130  fr_dict_attr_t const *group_da, *user_name_da;
131 
132  inst->name = cf_section_name2(conf);
133  if (!inst->name) {
134  inst->name = cf_section_name1(conf);
135  }
136 
137  group_da = fr_dict_attr_by_num(NULL, 0, PW_GROUP);
138  if (!group_da) {
139  ERROR("rlm_unix (%s): 'Group' attribute not found in dictionary", inst->name);
140  return -1;
141  }
142 
143  user_name_da = fr_dict_attr_by_num(NULL, 0, PW_USER_NAME);
144  if (!user_name_da) {
145  ERROR("rlm_unix (%s): 'User-Name' attribute not found in dictionary", inst->name);
146  return -1;
147  }
148 
149  /* FIXME - delay these until a group file has been read so we know
150  * groupcmp can actually do something */
151  paircompare_register(group_da, user_name_da, false, groupcmp, inst);
152 
153 #ifdef PW_GROUP_NAME /* compat */
154  {
155  fr_dict_attr_t const *group_name_da;
156 
157  group_name_da = fr_dict_attr_by_num(NULL, 0, PW_GROUP_NAME);
158  if (!group_name_da) {
159  ERROR("rlm_unix (%s): 'Group-Name' attribute not found in dictionary", inst->name);
160  return -1;
161  }
162  paircompare_register(group_name_da, user_name_da, true, groupcmp, inst);
163  }
164 #endif
165 
166  if (paircompare_register_byname("Unix-Group", user_name_da, false, groupcmp, inst) < 0) {
167  ERROR("rlm_unix (%s): Failed registering Unix-Group: %s", inst->name,
168  fr_strerror());
169  return -1;
170  }
171 
172  return 0;
173 }
174 
175 
176 /*
177  * Pull the users password from where-ever, and add it to
178  * the given vp list.
179  */
180 static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST *request)
181 {
182  char const *name;
183  char const *encrypted_pass;
184 #ifdef HAVE_GETSPNAM
185  struct spwd *spwd = NULL;
186 #endif
187 #ifdef OSFC2
188  struct pr_passwd *pr_pw;
189 #else
190  struct passwd *pwd;
191 #endif
192 #ifdef HAVE_GETUSERSHELL
193  char *shell;
194 #endif
195  VALUE_PAIR *vp;
196 
197  /*
198  * We can only authenticate user requests which HAVE
199  * a User-Name attribute.
200  */
201  if (!request->username) {
202  return RLM_MODULE_NOOP;
203  }
204 
205  name = request->username->vp_strvalue;
206  encrypted_pass = NULL;
207 
208 #ifdef OSFC2
209  if ((pr_pw = getprpwnam(name)) == NULL)
210  return RLM_MODULE_NOTFOUND;
211  encrypted_pass = pr_pw->ufld.fd_encrypt;
212 
213  /*
214  * Check if account is locked.
215  */
216  if (pr_pw->uflg.fg_lock!=1) {
217  AUTH("rlm_unix: [%s]: account locked", name);
218  return RLM_MODULE_USERLOCK;
219  }
220 #else /* OSFC2 */
221  if ((pwd = getpwnam(name)) == NULL) {
222  return RLM_MODULE_NOTFOUND;
223  }
224  encrypted_pass = pwd->pw_passwd;
225 #endif /* OSFC2 */
226 
227 #ifdef HAVE_GETSPNAM
228  /*
229  * See if there is a shadow password.
230  *
231  * Only query the _system_ shadow file if the encrypted
232  * password from the passwd file is < 10 characters (i.e.
233  * a valid password would never crypt() to it). This will
234  * prevents users from using NULL password fields as things
235  * stand right now.
236  */
237  if ((!encrypted_pass) || (strlen(encrypted_pass) < 10)) {
238  if ((spwd = getspnam(name)) == NULL) {
239  return RLM_MODULE_NOTFOUND;
240  }
241  encrypted_pass = spwd->sp_pwdp;
242  }
243 #endif /* HAVE_GETSPNAM */
244 
245 /*
246  * These require 'pwd != NULL', which isn't true on OSFC2
247  */
248 #ifndef OSFC2
249 #ifdef DENY_SHELL
250  /*
251  * Users with a particular shell are denied access
252  */
253  if (strcmp(pwd->pw_shell, DENY_SHELL) == 0) {
254  RAUTH("rlm_unix: [%s]: invalid shell", name);
255  return RLM_MODULE_REJECT;
256  }
257 #endif
258 
259 #ifdef HAVE_GETUSERSHELL
260  /*
261  * Check /etc/shells for a valid shell. If that file
262  * contains /RADIUSD/ANY/SHELL then any shell will do.
263  */
264  while ((shell = getusershell()) != NULL) {
265  if (strcmp(shell, pwd->pw_shell) == 0 ||
266  strcmp(shell, "/RADIUSD/ANY/SHELL") == 0) {
267  break;
268  }
269  }
270  endusershell();
271  if (!shell) {
272  RAUTH("[%s]: invalid shell [%s]",
273  name, pwd->pw_shell);
274  return RLM_MODULE_REJECT;
275  }
276 #endif
277 #endif /* OSFC2 */
278 
279 #if defined(HAVE_GETSPNAM) && !defined(M_UNIX)
280  /*
281  * Check if password has expired.
282  */
283  if (spwd && spwd->sp_lstchg > 0 && spwd->sp_max >= 0 &&
284  (request->timestamp.tv_sec / 86400) > (spwd->sp_lstchg + spwd->sp_max)) {
285  RAUTH("[%s]: password has expired", name);
286  return RLM_MODULE_REJECT;
287  }
288  /*
289  * Check if account has expired.
290  */
291  if (spwd && spwd->sp_expire > 0 &&
292  (request->timestamp.tv_sec / 86400) > spwd->sp_expire) {
293  RAUTH("[%s]: account has expired", name);
294  return RLM_MODULE_REJECT;
295  }
296 #endif
297 
298 #if defined(__FreeBSD__) || defined(bsdi) || defined(_PWF_EXPIRE)
299  /*
300  * Check if password has expired.
301  */
302  if ((pwd->pw_expire > 0) &&
303  (request->timestamp.tv_sec > pwd->pw_expire)) {
304  RAUTH("[%s]: password has expired", name);
305  return RLM_MODULE_REJECT;
306  }
307 #endif
308 
309  /*
310  * We might have a passwordless account.
311  *
312  * FIXME: Maybe add Auth-Type := Accept?
313  */
314  if (encrypted_pass[0] == 0)
315  return RLM_MODULE_NOOP;
316 
317  vp = pair_make_config("Crypt-Password", encrypted_pass, T_OP_SET);
318  if (!vp) return RLM_MODULE_FAIL;
319 
320  return RLM_MODULE_UPDATED;
321 }
322 
323 
324 /*
325  * UUencode 4 bits base64. We use this to turn a 4 byte field
326  * (an IP address) into 6 bytes of ASCII. This is used for the
327  * wtmp file if we didn't find a short name in the naslist file.
328  */
329 static char *uue(void *in)
330 {
331  int i;
332  static unsigned char res[7];
333  unsigned char *data = (unsigned char *)in;
334 
335  res[0] = ENC( data[0] >> 2 );
336  res[1] = ENC( ((data[0] << 4) & 060) + ((data[1] >> 4) & 017) );
337  res[2] = ENC( ((data[1] << 2) & 074) + ((data[2] >> 6) & 03) );
338  res[3] = ENC( data[2] & 077 );
339 
340  res[4] = ENC( data[3] >> 2 );
341  res[5] = ENC( (data[3] << 4) & 060 );
342  res[6] = 0;
343 
344  for(i = 0; i < 6; i++) {
345  if (res[i] == ' ') res[i] = '`';
346  if (res[i] < 32 || res[i] > 127)
347  printf("uue: protocol error ?!\n");
348  }
349  return (char *)res;
350 }
351 
352 
353 /*
354  * Unix accounting - write a wtmp file.
355  */
356 static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *request)
357 {
358  VALUE_PAIR *vp;
359  vp_cursor_t cursor;
360  FILE *fp;
361  struct utmp ut;
362  time_t t;
363  char buf[64];
364  char const *s;
365  int delay = 0;
366  int status = -1;
367  int nas_address = 0;
368  int framed_address = 0;
369 #ifdef USER_PROCESS
370  int protocol = -1;
371 #endif
372  uint32_t nas_port = 0;
373  bool port_seen = true;
374  rlm_unix_t *inst = (rlm_unix_t *) instance;
375 
376  /*
377  * No radwtmp. Don't do anything.
378  */
379  if (!inst->radwtmp) {
380  RDEBUG2("No radwtmp file configured. Ignoring accounting request");
381  return RLM_MODULE_NOOP;
382  }
383 
384  if (request->packet->src_ipaddr.af != AF_INET) {
385  RDEBUG2("IPv6 is not supported!");
386  return RLM_MODULE_NOOP;
387  }
388 
389  /*
390  * Which type is this.
391  */
392  if ((vp = fr_pair_find_by_num(request->packet->vps, 0, PW_ACCT_STATUS_TYPE, TAG_ANY)) == NULL) {
393  RDEBUG("no Accounting-Status-Type attribute in request");
394  return RLM_MODULE_NOOP;
395  }
396  status = vp->vp_integer;
397 
398  /*
399  * FIXME: handle PW_STATUS_ALIVE like 1.5.4.3 did.
400  */
401  if (status != PW_STATUS_START &&
402  status != PW_STATUS_STOP)
403  return RLM_MODULE_NOOP;
404 
405  /*
406  * We're only interested in accounting messages
407  * with a username in it.
408  */
409  if (fr_pair_find_by_num(request->packet->vps, 0, PW_USER_NAME, TAG_ANY) == NULL)
410  return RLM_MODULE_NOOP;
411 
412  t = request->timestamp.tv_sec;
413  memset(&ut, 0, sizeof(ut));
414 
415  /*
416  * First, find the interesting attributes.
417  */
418  for (vp = fr_cursor_init(&cursor, &request->packet->vps);
419  vp;
420  vp = fr_cursor_next(&cursor)) {
421  if (!vp->da->vendor) switch (vp->da->attr) {
422  case PW_USER_NAME:
423  if (vp->vp_length >= sizeof(ut.ut_name)) {
424  memcpy(ut.ut_name, vp->vp_strvalue, sizeof(ut.ut_name));
425  } else {
426  strlcpy(ut.ut_name, vp->vp_strvalue, sizeof(ut.ut_name));
427  }
428  break;
429 
430  case PW_LOGIN_IP_HOST:
431  case PW_FRAMED_IP_ADDRESS:
432  framed_address = vp->vp_ipaddr;
433  break;
434 #ifdef USER_PROCESS
435  case PW_FRAMED_PROTOCOL:
436  protocol = vp->vp_integer;
437  break;
438 #endif
439  case PW_NAS_IP_ADDRESS:
440  nas_address = vp->vp_ipaddr;
441  break;
442 
443  case PW_NAS_PORT:
444  nas_port = vp->vp_integer;
445  port_seen = true;
446  break;
447 
448  case PW_ACCT_DELAY_TIME:
449  delay = vp->vp_ipaddr;
450  break;
451  }
452  }
453 
454  /*
455  * We don't store !root sessions, or sessions
456  * where we didn't see a NAS-Port attribute.
457  */
458  if (strncmp(ut.ut_name, "!root", sizeof(ut.ut_name)) == 0 || !port_seen)
459  return RLM_MODULE_NOOP;
460 
461  /*
462  * If we didn't find out the NAS address, use the
463  * originator's IP address.
464  */
465  if (nas_address == 0) {
466  nas_address = request->packet->src_ipaddr.ipaddr.ip4addr.s_addr;
467  }
468  s = request->client->shortname;
469  if (!s || s[0] == 0) s = uue(&(nas_address));
470 
471 #ifdef __linux__
472  /*
473  * Linux has a field for the client address.
474  */
475  ut.ut_addr = framed_address;
476 #endif
477  /*
478  * We use the tty field to store the terminal servers' port
479  * and address so that the tty field is unique.
480  */
481  snprintf(buf, sizeof(buf), "%03d:%s", nas_port, s);
482  strlcpy(ut.ut_line, buf, sizeof(ut.ut_line));
483 
484  /*
485  * We store the dynamic IP address in the hostname field.
486  */
487 #ifdef UT_HOSTSIZE
488  if (framed_address) {
489  inet_ntop(AF_INET, &framed_address, buf, sizeof(buf));
490  strlcpy(ut.ut_host, buf, sizeof(ut.ut_host));
491  }
492 #endif
493 #ifdef HAVE_UTMPX_H
494  ut.ut_xtime = t- delay;
495 #else
496  ut.ut_time = t - delay;
497 #endif
498 #ifdef USER_PROCESS
499  /*
500  * And we can use the ID field to store
501  * the protocol.
502  */
503  if (protocol == PW_PPP)
504  strcpy(ut.ut_id, "P");
505  else if (protocol == PW_SLIP)
506  strcpy(ut.ut_id, "S");
507  else
508  strcpy(ut.ut_id, "T");
509  ut.ut_type = status == PW_STATUS_STOP ? DEAD_PROCESS : USER_PROCESS;
510 #endif
511  if (status == PW_STATUS_STOP)
512  ut.ut_name[0] = 0;
513 
514  /*
515  * Write a RADIUS wtmp log file.
516  *
517  * Try to open the file if we can't, we don't write the
518  * wtmp file. If we can try to write. If we fail,
519  * return RLM_MODULE_FAIL ..
520  */
521  if ((fp = fopen(inst->radwtmp, "a")) != NULL) {
522  if ((fwrite(&ut, sizeof(ut), 1, fp)) != 1) {
523  fclose(fp);
524  return RLM_MODULE_FAIL;
525  }
526  fclose(fp);
527  } else
528  return RLM_MODULE_FAIL;
529 
530  return RLM_MODULE_OK;
531 }
532 
533 /* globally exported name */
534 extern module_t rlm_unix;
535 module_t rlm_unix = {
537  .name = "unix",
538  .type = RLM_TYPE_THREAD_UNSAFE,
539  .inst_size = sizeof(rlm_unix_t),
540  .config = module_config,
541  .bootstrap = mod_bootstrap,
542  .methods = {
545  },
546 };
#define PW_PPP
Definition: radius.h:186
int paircompare_register(fr_dict_attr_t const *attribute, fr_dict_attr_t const *from, bool first_only, RAD_COMPARE_FUNC func, void *instance)
Register a function as compare function.
Definition: pair.c:395
#define AUTH(fmt,...)
Definition: log.h:139
#define PW_SLIP
Definition: radius.h:187
static rlm_rcode_t CC_HINT(nonnull)
Definition: rlm_unix.c:180
static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
Write accounting data to Couchbase documents.
static char * uue(void *in)
Definition: rlm_unix.c:329
The module is OK, continue.
Definition: radiusd.h:91
#define ENC(c)
Definition: rlm_unix.c:59
Metadata exported by the module.
Definition: modules.h:134
Dictionary attribute.
Definition: dict.h:77
#define RLM_TYPE_THREAD_UNSAFE
Module is not threadsafe.
Definition: modules.h:76
struct rlm_unix rlm_unix_t
static char const * name
static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
Handle authorization requests using Couchbase document data.
VALUE_PAIR * username
Cached username VALUE_PAIR from request RADIUS_PACKET.
Definition: radiusd.h:222
#define UNUSED
Definition: libradius.h:134
#define RLM_MODULE_INIT
Definition: modules.h:86
#define CONF_PARSER_TERMINATOR
Definition: conffile.h:289
char const * inet_ntop(int af, void const *src, char *dst, size_t cnt)
Definition: missing.c:538
VALUE_PAIR * fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR *const *node)
Setup a cursor to iterate over attribute pairs.
Definition: cursor.c:60
Definition: sysutmp.h:107
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:686
#define PW_STATUS_START
Definition: radius.h:191
#define inst
Defines a CONF_PAIR to C data type mapping.
Definition: conffile.h:267
Abstraction to allow iterating over different configurations of VALUE_PAIRs.
Definition: pair.h:144
Reject the request (user is locked out).
Definition: radiusd.h:94
module_t rlm_unix
Definition: rlm_unix.c:535
#define pair_make_config(_a, _b, _c)
Definition: radiusd.h:547
#define USER_PROCESS
Definition: sysutmp.h:101
unsigned int attr
Attribute number.
Definition: dict.h:79
Immediately reject the request.
Definition: radiusd.h:89
3 methods index for accounting section.
Definition: modules.h:44
unsigned int vendor
Vendor that defines this attribute.
Definition: dict.h:78
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
int rad_getpwnam(TALLOC_CTX *ctx, struct passwd **out, char const *name)
Resolve a username to a passwd entry.
Definition: util.c:1051
static int groupcmp(UNUSED void *instance, REQUEST *request, UNUSED VALUE_PAIR *req_vp, VALUE_PAIR *check, UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs)
Definition: rlm_unix.c:74
static const CONF_PARSER module_config[]
Definition: rlm_unix.c:66
Definition: token.h:45
enum rlm_rcodes rlm_rcode_t
Return codes indicating the result of the module call.
static rs_t * conf
Definition: radsniff.c:46
char const * name
Instance name.
Definition: rlm_unix.c:62
char const * fr_strerror(void)
Get the last library error.
Definition: log.c:212
char const * cf_section_name1(CONF_SECTION const *cs)
Definition: conffile.c:3592
Module succeeded without doing anything.
Definition: radiusd.h:96
#define RDEBUG2(fmt,...)
Definition: log.h:244
uint8_t data[]
Definition: eap_pwd.h:625
uint64_t magic
Used to validate module struct.
Definition: modules.h:135
Module failed, don't reply.
Definition: radiusd.h:90
#define TAG_ANY
Definition: pair.h:191
#define PW_TYPE_FILE_OUTPUT
File matching value must exist, and must be writeable.
Definition: conffile.h:205
int rad_getgrnam(TALLOC_CTX *ctx, struct group **out, char const *name)
Resolve a group name to a group database entry.
Definition: util.c:1185
#define FR_CONF_OFFSET(_n, _t, _s, _f)
Definition: conffile.h:168
VALUE_PAIR * fr_cursor_next(vp_cursor_t *cursor)
Advanced the cursor to the next VALUE_PAIR.
Definition: cursor.c:263
int paircompare_register_byname(char const *name, fr_dict_attr_t const *from, bool first_only, RAD_COMPARE_FUNC func, void *instance)
Register a function as compare function.
Definition: pair.c:351
static int mod_bootstrap(CONF_SECTION *conf, void *instance)
Definition: rlm_unix.c:126
#define RAUTH(fmt,...)
Definition: log.h:202
#define DEAD_PROCESS
Definition: sysutmp.h:102
#define PW_TYPE_REQUIRED
Error out if no matching CONF_PAIR is found, and no dflt value is set.
Definition: conffile.h:200
VALUE_PAIR * fr_pair_find_by_num(VALUE_PAIR *head, unsigned int vendor, unsigned int attr, int8_t tag)
Find the pair with the matching attribute.
Definition: pair.c:639
#define PW_STATUS_STOP
Definition: radius.h:192
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition: strlcpy.c:38
char const * radwtmp
Definition: rlm_unix.c:63
fr_dict_attr_t const * da
Dictionary attribute defines the attribute.
Definition: pair.h:113
fr_dict_attr_t const * fr_dict_attr_by_num(fr_dict_t *dict, unsigned int vendor, unsigned int attr)
Lookup a fr_dict_attr_t by its vendor and attribute numbers.
Definition: dict.c:3519
1 methods index for authorize section.
Definition: modules.h:42
User not found.
Definition: radiusd.h:95
#define RCSID(id)
Definition: build.h:135
OK (pairs modified).
Definition: radiusd.h:97
#define RDEBUG(fmt,...)
Definition: log.h:243
#define ERROR(fmt,...)
Definition: log.h:145
char const * cf_section_name2(CONF_SECTION const *cs)
Definition: conffile.c:3601
#define USES_APPLE_DEPRECATED_API
Definition: build.h:122
static USES_APPLE_DEPRECATED_API char trans[64]
Definition: rlm_unix.c:58