16ff9f0b0f59df9d760726ec2ccdeeffb82a7c65
[freeradius.git] / src / modules / rlm_unix / rlm_unix.c
1 /*
2  * rlm_unix.c   authentication: Unix user authentication
3  *              accounting:     Functions to write radwtmp file.
4  *              Also contains handler for "Group".
5  *
6  * Version:     $Id$
7  *
8  */
9 static const char rcsid[] = "$Id$";
10
11 #include        "autoconf.h"
12
13 #include        <stdlib.h>
14 #include        <string.h>
15 #include        <grp.h>
16 #include        <pwd.h>
17
18 #include "config.h"
19
20 #if HAVE_MALLOC_H
21 #  include <malloc.h>
22 #endif
23
24 #if HAVE_SHADOW_H
25 #  include      <shadow.h>
26 #endif
27
28 #if HAVE_CRYPT_H
29 #  include <crypt.h>
30 #endif
31
32 #ifdef OSFC2
33 #  include      <sys/security.h>
34 #  include      <prot.h>
35 #endif
36
37 #include        "radiusd.h"
38 #include        "modules.h"
39 #include        "sysutmp.h"
40 #include        "cache.h"
41 #include        "conffile.h"
42
43 static char trans[64] =
44    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
45 #define ENC(c) trans[c]
46
47 struct unix_instance {
48         int cache_passwd;
49         char *passwd_file;
50         char *shadow_file;
51         char *group_file;
52         int usegroup;
53         struct pwcache *cache;
54 };
55
56 static struct unix_instance config;
57
58 static CONF_PARSER module_config[] = {
59         /*
60          *      Cache the password by default.
61          */
62         { "cache",    PW_TYPE_BOOLEAN,    &config.cache_passwd, "yes" },
63         { "passwd",   PW_TYPE_STRING_PTR, &config.passwd_file,  NULL },
64         { "shadow",   PW_TYPE_STRING_PTR, &config.shadow_file,  NULL },
65         { "group",    PW_TYPE_STRING_PTR, &config.group_file,   NULL },
66         { "usegroup", PW_TYPE_BOOLEAN,    &config.usegroup,     "no" },
67         
68         { NULL, -1, NULL, NULL }                /* end the list */
69 };
70
71 /*
72  * groupcmp is part of autz. But it uses the data from an auth instance. So
73  * here is where it gets it. By default this will be the first configured
74  * auth instance. That can be changed by putting "usegroup = yes" inside an
75  * auth instance to explicitly bind all Group checks to it.
76  */
77
78 /* binds "Group=" to an instance (a particular passwd file) */
79 static struct unix_instance *group_inst;
80
81 /* Tells if the above binding was explicit (usegroup=yes specified in config
82  * file) or not ("Group=" was bound to the first instance of rlm_unix */
83 static int group_inst_explicit;
84
85 /*
86  *      The Group = handler.
87  */
88 static int groupcmp(VALUE_PAIR *request, VALUE_PAIR *check,
89         VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
90 {
91         struct passwd   *pwd;
92         struct group    *grp;
93         char            **member;
94         char            *username;
95         int             retval;
96         check_pairs = check_pairs; reply_pairs = reply_pairs;
97
98         if (!group_inst) {
99                 radlog(L_ERR, "groupcmp: no group list known.");
100                 return 1;
101         }
102
103         username = (char *)request->strvalue;
104
105         if (group_inst->cache_passwd &&
106             (retval = H_groupcmp(group_inst->cache, check, username)) != -2)
107                 return retval;
108
109         if ((pwd = getpwnam(username)) == NULL)
110                 return -1;
111
112         if ((grp = getgrnam((char *)check->strvalue)) == NULL)
113                 return -1;
114
115         retval = (pwd->pw_gid == grp->gr_gid) ? 0 : -1;
116         if (retval < 0) {
117                 for (member = grp->gr_mem; *member && retval; member++) {
118                         if (strcmp(*member, pwd->pw_name) == 0)
119                                 retval = 0;
120                 }
121         }
122         return retval;
123 }
124
125
126 /*
127  *      FIXME:  We really should have an 'init' which makes
128  *      System auth == Unix
129  */
130 static int unix_init(void)
131 {
132         /* FIXME - delay these until a group file has been read so we know
133          * groupcmp can actually do something */
134         paircompare_register(PW_GROUP, PW_USER_NAME, groupcmp);
135 #ifdef PW_GROUP_NAME /* compat */
136         paircompare_register(PW_GROUP_NAME, PW_USER_NAME, groupcmp);
137 #endif
138         return 0;
139 }
140
141 static int unix_instantiate(CONF_SECTION *conf, void **instance)
142 {
143         *instance = malloc(sizeof(struct unix_instance));
144         if(!*instance) {
145               return -1;
146         }
147
148 #define inst ((struct unix_instance *)*instance)
149         if (cf_section_parse(conf, module_config) < 0) {
150                 free(*instance);
151                 return -1;
152         }
153
154         /*
155          *      Copy the configuration into the instance data
156          */
157         inst->cache_passwd = config.cache_passwd;
158         inst->passwd_file = config.passwd_file;
159         inst->shadow_file = config.shadow_file;
160         inst->group_file = config.group_file;
161         inst->usegroup = config.usegroup;
162         config.passwd_file = NULL;
163         config.shadow_file = NULL;
164         config.group_file = NULL;
165
166         if (inst->cache_passwd) {
167                 radlog(L_INFO, "HASH:  Reinitializing hash structures "
168                         "and lists for caching...");
169                 if ((inst->cache = unix_buildpwcache(inst->passwd_file,
170                                                      inst->shadow_file))==NULL)
171                 {
172                         radlog(L_ERR, "HASH:  unable to create user "
173                                 "hash table.  disable caching and run debugs");
174                 }
175         } else {
176                 inst->cache = NULL;
177         }
178
179         if (inst->usegroup) {
180                 if (group_inst_explicit) {
181                         radlog(L_ERR, "Only one group list may be active");
182                 } else {
183                         group_inst = inst;
184                         group_inst_explicit = 1;
185                 }
186         } else if (!group_inst) {
187                 group_inst = inst;
188         }
189 #undef inst
190
191         return 0;
192 }
193
194 /*
195  *      Detach.
196  */
197 static int unix_detach(void *instance)
198 {
199 #define inst ((struct unix_instance *)instance)
200         if (group_inst == inst) {
201                 group_inst = NULL;
202                 group_inst_explicit = 0;
203         }
204         free(inst->passwd_file);
205         free(inst->shadow_file);
206         free(inst->group_file);
207         if (inst->cache) {
208                 unix_freepwcache(inst->cache);
209         }
210 #undef inst
211         free(instance);
212         return 0;
213 }
214
215 static int unix_destroy(void)
216 {
217         paircompare_unregister(PW_GROUP, groupcmp);
218 #ifdef PW_GROUP_NAME
219         paircompare_unregister(PW_GROUP_NAME, groupcmp);
220 #endif
221         return 0;
222 }
223
224
225 /*
226  *      Check the users password against the standard UNIX
227  *      password table.
228  */
229 static int unix_authenticate(void *instance, REQUEST *request)
230 {
231 #define inst ((struct unix_instance *)instance)
232         char *name, *passwd;
233         struct passwd   *pwd;
234         char            *encpw;
235         char            *encrypted_pass;
236         int             ret;
237 #if HAVE_GETSPNAM
238 #if defined(M_UNIX)
239         struct passwd   *spwd;
240 #else
241         struct spwd     *spwd;
242 #endif
243 #endif
244 #ifdef OSFC2
245         struct pr_passwd *pr_pw;
246 #endif
247 #ifdef HAVE_GETUSERSHELL
248         char            *shell;
249 #endif
250         instance = instance;
251
252         /*
253          *      We can only authenticate user requests which HAVE
254          *      a User-Name attribute.
255          */
256         if (!request->username) {
257                 radlog(L_AUTH, "rlm_unix: Attribute \"User-Name\" is required for authentication.");
258                 return RLM_MODULE_INVALID;
259         }
260
261         /*
262          *      We can only authenticate user requests which HAVE
263          *      a Password attribute.
264          */
265         if (!request->password) {
266                 radlog(L_AUTH, "rlm_unix: Attribute \"Password\" is required for authentication.");
267                 return RLM_MODULE_INVALID;
268         }
269
270         /*
271          *  Ensure that we're being passed a plain-text password,
272          *  and not anything else.
273          */
274         if (request->password->attribute != PW_PASSWORD) {
275                 radlog(L_AUTH, "rlm_unix: Attribute \"Password\" is required for authentication.  Cannot use \"%s\".", request->password->name);
276                 return RLM_MODULE_INVALID;
277         }
278
279         name = (char *)request->username->strvalue;
280         passwd = (char *)request->password->strvalue;
281
282         if (inst->cache_passwd &&
283             (ret = H_unix_pass(inst->cache, name, passwd, &request->reply->vps)) != -2)
284                 return (ret == 0) ? RLM_MODULE_OK : RLM_MODULE_REJECT;
285
286 #ifdef OSFC2
287         if ((pr_pw = getprpwnam(name)) == NULL)
288                 return RLM_MODULE_NOTFOUND;
289         encrypted_pass = pr_pw->ufld.fd_encrypt;
290 #else /* OSFC2 */
291         /*
292          *      Get encrypted password from password file
293          */
294         if ((pwd = getpwnam(name)) == NULL) {
295                 return RLM_MODULE_NOTFOUND;
296         }
297         encrypted_pass = pwd->pw_passwd;
298 #endif /* OSFC2 */
299
300 #if HAVE_GETSPNAM
301         /*
302          *      See if there is a shadow password.
303          */
304         if ((spwd = getspnam(name)) != NULL)
305 #if defined(M_UNIX)
306                 encrypted_pass = spwd->pw_passwd;
307 #else
308                 encrypted_pass = spwd->sp_pwdp;
309 #endif  /* M_UNIX */
310 #endif  /* HAVE_GETSPNAM */
311
312 #ifdef DENY_SHELL
313         /*
314          *      Undocumented temporary compatibility for iphil.NET
315          *      Users with a certain shell are always denied access.
316          */
317         if (strcmp(pwd->pw_shell, DENY_SHELL) == 0) {
318                 radlog(L_AUTH, "rlm_unix: [%s]: invalid shell", name);
319                 return RLM_MODULE_REJECT;
320         }
321 #endif
322
323 #if HAVE_GETUSERSHELL
324         /*
325          *      Check /etc/shells for a valid shell. If that file
326          *      contains /RADIUSD/ANY/SHELL then any shell will do.
327          */
328         while ((shell = getusershell()) != NULL) {
329                 if (strcmp(shell, pwd->pw_shell) == 0 ||
330                     strcmp(shell, "/RADIUSD/ANY/SHELL") == 0) {
331                         break;
332                 }
333         }
334         endusershell();
335         if (shell == NULL)
336                 return RLM_MODULE_REJECT;
337 #endif
338
339 #if defined(HAVE_GETSPNAM) && !defined(M_UNIX)
340         /*
341          *      Check if password has expired.
342          */
343         if (spwd && spwd->sp_expire > 0 &&
344             (request->timestamp / 86400) > spwd->sp_expire) {
345                 radlog(L_AUTH, "rlm_unix: [%s]: password has expired", name);
346                 return RLM_MODULE_REJECT;
347         }
348 #endif
349
350 #if defined(__FreeBSD__) || defined(bsdi) || defined(_PWF_EXPIRE)
351         /*
352          *      Check if password has expired.
353          */
354         if ((pwd->pw_expire > 0) &&
355             (request->timestamp > pwd->pw_expire)) {
356                 radlog(L_AUTH, "rlm_unix: [%s]: password has expired", name);
357                 return RLM_MODULE_REJECT;
358         }
359 #endif
360
361 #ifdef OSFC2
362         /*
363          *      Check if account is locked.
364          */
365         if (pr_pw->uflg.fg_lock!=1) {
366                 radlog(L_AUTH, "rlm_unix: [%s]: account locked", name);
367                 return RLM_MODULE_USERLOCK;
368         }
369 #endif /* OSFC2 */
370
371         /*
372          *      We might have a passwordless account.
373          */
374         if (encrypted_pass[0] == 0)
375                 return RLM_MODULE_OK;
376
377         /*
378          *      Check encrypted password.
379          */
380         encpw = crypt(passwd, encrypted_pass);
381         if (strcmp(encpw, encrypted_pass))
382                 return RLM_MODULE_REJECT;
383
384         return RLM_MODULE_OK;
385 #undef inst
386 }
387
388 /*
389  *      UUencode 4 bits base64. We use this to turn a 4 byte field
390  *      (an IP address) into 6 bytes of ASCII. This is used for the
391  *      wtmp file if we didn't find a short name in the naslist file.
392  */
393 static char *uue(void *in)
394 {
395         int i;
396         static unsigned char res[7];
397         unsigned char *data = (unsigned char *)in;
398
399         res[0] = ENC( data[0] >> 2 );
400         res[1] = ENC( ((data[0] << 4) & 060) + ((data[1] >> 4) & 017) );
401         res[2] = ENC( ((data[1] << 2) & 074) + ((data[2] >> 6) & 03) );
402         res[3] = ENC( data[2] & 077 );
403
404         res[4] = ENC( data[3] >> 2 );
405         res[5] = ENC( (data[3] << 4) & 060 );
406         res[6] = 0;
407
408         for(i = 0; i < 6; i++) {
409                 if (res[i] == ' ') res[i] = '`';
410                 if (res[i] < 32 || res[i] > 127)
411                         printf("uue: protocol error ?!\n");
412         }
413         return (char *)res;
414 }
415
416
417 /*
418  *      Unix accounting - write a wtmp file.
419  */
420 static int unix_accounting(void *instance, REQUEST *request)
421 {
422         VALUE_PAIR      *vp;
423         NAS             *cl;
424         FILE            *fp;
425         struct utmp     ut;
426         time_t          t;
427         char            buf[64];
428         const char      *s;
429         int             delay = 0;
430         int             status = -1;
431         int             nas_address = 0;
432         int             framed_address = 0;
433         int             protocol = -1;
434         int             nas_port = 0;
435         int             port_seen = 0;
436         int             nas_port_type = 0;
437
438         instance = instance;
439
440         /*
441          *      Which type is this.
442          */
443         if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE))==NULL) {
444                 radlog(L_ERR, "Accounting: no Accounting-Status-Type record.");
445                 return RLM_MODULE_NOOP;
446         }
447         status = vp->lvalue;
448
449         /*
450          *      FIXME: handle PW_STATUS_ALIVE like 1.5.4.3 did.
451          */
452         if (status != PW_STATUS_START &&
453             status != PW_STATUS_STOP)
454                 return RLM_MODULE_NOOP;
455
456         /*
457          *      We're only interested in accounting messages
458          *      with a username in it.
459          */
460         if ((vp = pairfind(request->packet->vps, PW_USER_NAME)) == NULL)
461                 return RLM_MODULE_NOOP;
462
463         t = request->timestamp;
464         memset(&ut, 0, sizeof(ut));
465
466         /*
467          *      First, find the interesting attributes.
468          */
469         for (vp = request->packet->vps; vp; vp = vp->next) {
470                 switch (vp->attribute) {
471                         case PW_USER_NAME:
472                                 strNcpy(ut.ut_name, (char *)vp->strvalue, sizeof(ut.ut_name));
473                                 break;
474                         case PW_LOGIN_IP_HOST:
475                         case PW_FRAMED_IP_ADDRESS:
476                                 framed_address = vp->lvalue;
477                                 break;
478                         case PW_FRAMED_PROTOCOL:
479                                 protocol = vp->lvalue;
480                                 break;
481                         case PW_NAS_IP_ADDRESS:
482                                 nas_address = vp->lvalue;
483                                 break;
484                         case PW_NAS_PORT_ID:
485                                 nas_port = vp->lvalue;
486                                 port_seen = 1;
487                                 break;
488                         case PW_ACCT_DELAY_TIME:
489                                 delay = vp->lvalue;
490                                 break;
491                         case PW_NAS_PORT_TYPE:
492                                 nas_port_type = vp->lvalue;
493                                 break;
494                 }
495         }
496
497         /*
498          *      We don't store !root sessions, or sessions
499          *      where we didn't see a PW_NAS_PORT_ID.
500          */
501         if (strncmp(ut.ut_name, "!root", sizeof(ut.ut_name)) == 0 || !port_seen)
502                 return RLM_MODULE_NOOP;
503
504         /*
505          *      If we didn't find out the NAS address, use the
506          *      originator's IP address.
507          */
508         if (nas_address == 0)
509                 nas_address = request->packet->src_ipaddr;
510
511 #ifdef __linux__
512         /*
513          *      Linux has a field for the client address.
514          */
515         ut.ut_addr = framed_address;
516 #endif
517         /*
518          *      We use the tty field to store the terminal servers' port
519          *      and address so that the tty field is unique.
520          */
521         s = "";
522         if ((cl = nas_find(nas_address)) != NULL)
523                 s = cl->shortname;
524         if (s == NULL || s[0] == 0) s = uue(&(nas_address));
525         sprintf(buf, "%03d:%s", nas_port, s);
526         strNcpy(ut.ut_line, buf, sizeof(ut.ut_line));
527
528         /*
529          *      We store the dynamic IP address in the hostname field.
530          */
531 #ifdef UT_HOSTSIZE
532         if (framed_address) {
533                 ip_ntoa(buf, framed_address);
534                 strncpy(ut.ut_host, buf, UT_HOSTSIZE);
535         }
536 #endif
537 #ifdef HAVE_UTMPX_H
538         ut.ut_xtime = t- delay;
539 #else
540         ut.ut_time = t - delay;
541 #endif
542 #ifdef USER_PROCESS
543         /*
544          *      And we can use the ID field to store
545          *      the protocol.
546          */
547         if (protocol == PW_PPP)
548                 strcpy(ut.ut_id, "P");
549         else if (protocol == PW_SLIP)
550                 strcpy(ut.ut_id, "S");
551         else
552                 strcpy(ut.ut_id, "T");
553         ut.ut_type = status == PW_STATUS_STOP ? DEAD_PROCESS : USER_PROCESS;
554 #endif
555         if (status == PW_STATUS_STOP)
556                 ut.ut_name[0] = 0;
557
558         /*
559          *      Write a RADIUS wtmp log file.
560          *      FIXME: return correct error.
561          *      Check if file is there. If not, we don't write the
562          *      wtmp file. If it is, we try to write. If we fail,
563          *      return RLM_MODULE_FAIL ..
564          */
565         if ((fp = fopen(RADWTMP, "a")) != NULL) {
566                 fwrite(&ut, sizeof(ut), 1, fp);
567                 fclose(fp);
568         }
569
570         return RLM_MODULE_OK;
571 }
572
573 /* globally exported name */
574 module_t rlm_unix = {
575   "System",
576   0,                            /* type: reserved */
577   unix_init,                    /* initialization */
578   unix_instantiate,             /* instantiation */
579   NULL,                         /* authorization */
580   unix_authenticate,            /* authentication */
581   NULL,                         /* preaccounting */
582   unix_accounting,              /* accounting */
583   unix_detach,                  /* detach */
584   unix_destroy,                  /* destroy */
585 };
586