Initial revision
[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:     @(#)rlm_unix.c  1.00  08-Aug-1999  miquels@cistron.nl
7  *
8  */
9 char rlm_unix_sccsid[] =
10 "@(#)rlm_unix.c 1.00 Copyright 1998-1999 Cistron Internet Services B.V.";
11
12 #include        "autoconf.h"
13
14 #include        <sys/types.h>
15 #include        <sys/time.h>
16
17 #include        <stdio.h>
18 #include        <stdlib.h>
19 #include        <string.h>
20 #include        <time.h>
21 #include        <unistd.h>
22 #include        <grp.h>
23 #include        <pwd.h>
24 #include        <errno.h>
25
26 #include "config.h"
27
28 #if HAVE_MALLOC_H
29 #  include <malloc.h>
30 #endif
31
32 #if HAVE_SHADOW_H
33 #  include      <shadow.h>
34 #endif
35
36 #if HAVE_CRYPT_H
37 #  include <crypt.h>
38 #endif
39
40 #ifdef OSFC2
41 #  include      <sys/security.h>
42 #  include      <prot.h>
43 #endif
44
45 #include        "radiusd.h"
46 #include        "modules.h"
47 #include        "sysutmp.h"
48 #include        "cache.h"
49
50 #if !HAVE_CRYPT_H
51   extern char *crypt();
52 #endif
53
54 static char trans[64] =
55    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
56 #define ENC(c) trans[c]
57
58 /*
59  *      The Group = handler.
60  */
61 static int groupcmp(VALUE_PAIR *request, VALUE_PAIR *check,
62         VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
63 {
64         struct passwd   *pwd;
65         struct group    *grp;
66         char            **member;
67         char            *username;
68         int             retval;
69
70         username = request->strvalue;
71
72         if (cache_passwd && (retval = H_groupcmp(check, username)) != -2)
73                 return retval;
74
75         if ((pwd = rad_getpwnam(username)) == NULL)
76                 return -1;
77
78         if ((grp = getgrnam(check->strvalue)) == NULL)
79                 return -1;
80
81         retval = (pwd->pw_gid == grp->gr_gid) ? 0 : -1;
82         if (retval < 0) {
83                 for (member = grp->gr_mem; *member && retval; member++) {
84                         if (strcmp(*member, pwd->pw_name) == 0)
85                                 retval = 0;
86                 }
87         }
88         return retval;
89 }
90
91
92 /*
93  *      FIXME:  We really should have an 'init' which makes
94  *      System auth == Unix
95  */
96 static int unix_init(int argc, char **argv)
97 {
98         paircompare_register(PW_GROUP, PW_USER_NAME, groupcmp);
99 #ifdef PW_GROUP_NAME /* compat */
100         paircompare_register(PW_GROUP_NAME, PW_USER_NAME, groupcmp);
101 #endif
102         if (cache_passwd) {
103                 log(L_INFO, "HASH:  Reinitializing hash structures "
104                         "and lists for caching...");
105                 if(buildHashTable() < 0) {
106                         log(L_ERR, "HASH:  unable to create user "
107                                 "hash table.  disable caching and run debugs");
108                         return -1;
109                 }
110                 if (buildGrpList() < 0) {
111                         log(L_ERR, "HASH:  unable to cache groups file.  "
112                                 "disable caching and run debugs");
113                         return -1;
114                 }
115         }
116
117         return 0;
118 }
119
120
121 /*
122  *      Detach.
123  */
124 static int unix_detach(void)
125 {
126         paircompare_unregister(PW_GROUP, groupcmp);
127 #ifdef PW_GROUP_NAME
128         paircompare_unregister(PW_GROUP_NAME, groupcmp);
129 #endif
130         return 0;
131 }
132
133
134 /*
135  *      Check the users password against the standard UNIX
136  *      password table.
137  */
138 static int unix_authenticate(REQUEST *request, char *name, char *passwd)
139 {
140         struct passwd   *pwd;
141         char            *encpw;
142         char            *encrypted_pass;
143         int             ret;
144 #if HAVE_GETSPNAM
145 #if defined(M_UNIX)
146         struct passwd   *spwd;
147 #else
148         struct spwd     *spwd;
149 #endif
150 #endif
151 #ifdef OSFC2
152         struct pr_passwd *pr_pw;
153 #endif
154 #ifdef HAVE_GETUSERSHELL
155         char            *shell;
156 #endif
157
158         if (cache_passwd && (ret = H_unix_pass(name, passwd)) != -2)
159                 return (ret == 0) ? RLM_AUTH_OK : RLM_AUTH_REJECT;
160
161 #ifdef OSFC2
162         if ((pr_pw = getprpwnam(name)) == NULL)
163                 return -1;
164         encrypted_pass = pr_pw->ufld.fd_encrypt;
165 #else /* OSFC2 */
166         /*
167          *      Get encrypted password from password file
168          */
169         if ((pwd = rad_getpwnam(name)) == NULL) {
170                 return RLM_AUTH_REJECT;
171         }
172         encrypted_pass = pwd->pw_passwd;
173 #endif /* OSFC2 */
174
175 #if HAVE_GETSPNAM
176         /*
177          *      See if there is a shadow password.
178          */
179         if ((spwd = getspnam(name)) != NULL)
180 #if defined(M_UNIX)
181                 encrypted_pass = spwd->pw_passwd;
182 #else
183                 encrypted_pass = spwd->sp_pwdp;
184 #endif  /* M_UNIX */
185 #endif  /* HAVE_GETSPNAM */
186
187 #ifdef DENY_SHELL
188         /*
189          *      Undocumented temporary compatibility for iphil.NET
190          *      Users with a certain shell are always denied access.
191          */
192         if (strcmp(pwd->pw_shell, DENY_SHELL) == 0) {
193                 log(L_AUTH, "rlm_unix: [%s]: invalid shell", name);
194                 return RLM_AUTH_REJECT;
195         }
196 #endif
197
198 #if HAVE_GETUSERSHELL
199         /*
200          *      Check /etc/shells for a valid shell. If that file
201          *      contains /RADIUSD/ANY/SHELL then any shell will do.
202          */
203         while ((shell = getusershell()) != NULL) {
204                 if (strcmp(shell, pwd->pw_shell) == 0 ||
205                     strcmp(shell, "/RADIUSD/ANY/SHELL") == 0) {
206                         break;
207                 }
208         }
209         endusershell();
210         if (shell == NULL)
211                 return RLM_AUTH_REJECT;
212 #endif
213
214 #if defined(HAVE_GETSPNAM) && !defined(M_UNIX)
215         /*
216          *      Check if password has expired.
217          */
218         if (spwd && spwd->sp_expire > 0 &&
219             (time(NULL) / 86400) > spwd->sp_expire) {
220                 log(L_AUTH, "rlm_unix: [%s]: password has expired", name);
221                 return RLM_AUTH_REJECT;
222         }
223 #endif
224
225 #if defined(__FreeBSD__) || defined(bsdi) || defined(_PWF_EXPIRE)
226         /*
227          *      Check if password has expired.
228          */
229         if (pwd->pw_expire > 0 && time(NULL) > pwd->pw_expire) {
230                 log(L_AUTH, "rlm_unix: [%s]: password has expired", name);
231                 return RLM_AUTH_REJECT;
232         }
233 #endif
234
235 #ifdef OSFC2
236         /*
237          *      Check if account is locked.
238          */
239         if (pr_pw->uflg.fg_lock!=1) {
240                 log(L_AUTH, "rlm_unix: [%s]: account locked", name);
241                 return RLM_AUTH_REJECT;
242         }
243 #endif /* OSFC2 */
244
245         /*
246          *      We might have a passwordless account.
247          */
248         if (encrypted_pass[0] == 0)
249                 return RLM_AUTH_OK;
250
251         /*
252          *      Check encrypted password.
253          */
254         encpw = crypt(passwd, encrypted_pass);
255         if (strcmp(encpw, encrypted_pass))
256                 return RLM_AUTH_REJECT;
257
258         return RLM_AUTH_OK;
259 }
260
261 /*
262  *      UUencode 4 bits base64. We use this to turn a 4 byte field
263  *      (an IP adres) into 6 bytes of ASCII. This is used for the
264  *      wtmp file if we didn't find a short name in the naslist file.
265  */
266 static char *uue(void *in)
267 {
268         int i;
269         static unsigned char res[7];
270         unsigned char *data = (char *)in;
271
272         res[0] = ENC( data[0] >> 2 );
273         res[1] = ENC( ((data[0] << 4) & 060) + ((data[1] >> 4) & 017) );
274         res[2] = ENC( ((data[1] << 2) & 074) + ((data[2] >> 6) & 03) );
275         res[3] = ENC( data[2] & 077 );
276
277         res[4] = ENC( data[3] >> 2 );
278         res[5] = ENC( (data[3] << 4) & 060 );
279         res[6] = 0;
280
281         for(i = 0; i < 6; i++) {
282                 if (res[i] == ' ') res[i] = '`';
283                 if (res[i] < 32 || res[i] > 127)
284                         printf("uue: protocol error ?!\n");
285         }
286         return res;
287 }
288
289
290 /*
291  *      Unix accounting - write a wtmp file.
292  */
293 static int unix_accounting(REQUEST *request)
294 {
295         VALUE_PAIR      *vp;
296         NAS             *cl;
297         FILE            *fp;
298         struct utmp     ut;
299         time_t          t;
300         char            buf[64];
301         char            *s;
302         int             delay = 0;
303         int             status = -1;
304         int             nas_address = 0;
305         int             framed_address = 0;
306         int             protocol = -1;
307         int             nas_port = 0;
308         int             port_seen = 0;
309         int             nas_port_type = 0;
310
311         /*
312          *      Which type is this.
313          */
314         if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE))==NULL) {
315                 log(L_ERR, "Accounting: no Accounting-Status-Type record.");
316                 return RLM_ACCT_FAIL_SOFT;
317         }
318         status = vp->lvalue;
319
320         /*
321          *      FIXME: handle PW_STATUS_ALIVE like 1.5.4.3 did.
322          */
323         if (status != PW_STATUS_START &&
324             status != PW_STATUS_STOP)
325                 return RLM_ACCT_OK;
326
327         /*
328          *      We're only interested in accounting messages
329          *      with a username in it.
330          */
331         if ((vp = pairfind(request->packet->vps, PW_USER_NAME)) == NULL)
332                 return RLM_ACCT_OK;
333
334         time(&t);
335         memset(&ut, 0, sizeof(ut));
336
337         /*
338          *      First, find the interesting attributes.
339          */
340         for (vp = request->packet->vps; vp; vp = vp->next) {
341                 switch (vp->attribute) {
342                         case PW_USER_NAME:
343                                 strncpy(ut.ut_name, vp->strvalue, UT_NAMESIZE);
344                                 break;
345                         case PW_LOGIN_IP_HOST:
346                         case PW_FRAMED_IP_ADDRESS:
347                                 framed_address = vp->lvalue;
348                                 break;
349                         case PW_FRAMED_PROTOCOL:
350                                 protocol = vp->lvalue;
351                                 break;
352                         case PW_NAS_IP_ADDRESS:
353                                 nas_address = vp->lvalue;
354                                 break;
355                         case PW_NAS_PORT_ID:
356                                 nas_port = vp->lvalue;
357                                 port_seen = 1;
358                                 break;
359                         case PW_ACCT_DELAY_TIME:
360                                 delay = vp->lvalue;
361                                 break;
362                         case PW_NAS_PORT_TYPE:
363                                 nas_port_type = vp->lvalue;
364                                 break;
365                 }
366         }
367
368         /*
369          *      We don't store !root sessions, or sessions
370          *      where we didn't see a PW_NAS_PORT_ID.
371          */
372         if (strncmp(ut.ut_name, "!root", sizeof(ut.ut_name)) == 0 || !port_seen)
373                 return RLM_ACCT_OK;
374
375         /*
376          *      If we didn't find out the NAS address, use the
377          *      originator's IP address.
378          */
379         if (nas_address == 0)
380                 nas_address = request->packet->src_ipaddr;
381
382 #ifdef __linux__
383         /*
384          *      Linux has a field for the client address.
385          */
386         ut.ut_addr = framed_address;
387 #endif
388         /*
389          *      We use the tty field to store the terminal servers' port
390          *      and address so that the tty field is unique.
391          */
392         s = "";
393         if ((cl = nas_find(nas_address)) != NULL)
394                 s = cl->shortname;
395         if (s == NULL || s[0] == 0) s = uue(&(nas_address));
396 #if UT_LINESIZE > 9
397         sprintf(buf, "%03d:%s", nas_port, s);
398 #else
399         sprintf(buf, "%02d%s", nas_port, s);
400 #endif
401         strncpy(ut.ut_line, buf, UT_LINESIZE);
402
403         /*
404          *      We store the dynamic IP address in the hostname field.
405          */
406 #ifdef UT_HOSTSIZE
407         if (framed_address) {
408                 ip_ntoa(buf, framed_address);
409                 strncpy(ut.ut_host, buf, UT_HOSTSIZE);
410         }
411 #endif
412 #ifdef __svr4__
413         ut.ut_xtime = t- delay;
414 #else
415         ut.ut_time = t - delay;
416 #endif
417 #ifdef USER_PROCESS
418         /*
419          *      And we can use the ID field to store
420          *      the protocol.
421          */
422         if (protocol == PW_PPP)
423                 strcpy(ut.ut_id, "P");
424         else if (protocol == PW_SLIP)
425                 strcpy(ut.ut_id, "S");
426         else
427                 strcpy(ut.ut_id, "T");
428         ut.ut_type = status == PW_STATUS_STOP ? DEAD_PROCESS : USER_PROCESS;
429 #endif
430         if (status == PW_STATUS_STOP)
431                 ut.ut_name[0] = 0;
432
433         /*
434          *      Write a RADIUS wtmp log file.
435          *      FIXME: return correct error.
436          *      Check if file is there. If not, we don't write the
437          *      wtmp file. If it is, we try to write. If we fail,
438          *      return RLM_ACCT_FAIL ..
439          */
440         if ((fp = fopen(RADWTMP, "a")) != NULL) {
441                 fwrite(&ut, sizeof(ut), 1, fp);
442                 fclose(fp);
443         }
444
445         return RLM_ACCT_OK;
446 }
447
448 /* globally exported name */
449 module_t rlm_unix = {
450   "System",
451   0,                            /* type: reserved */
452   unix_init,                    /* initialization */
453   NULL,                         /* authorization */
454   unix_authenticate,            /* authentication */
455   unix_accounting,              /* accounting */
456   unix_detach,                  /* detach */
457 };
458