2 * rlm_unix.c authentication: Unix user authentication
3 * accounting: Functions to write radwtmp file.
4 * Also contains handler for "Group".
6 * Version: @(#)rlm_unix.c 1.00 08-Aug-1999 miquels@cistron.nl
9 char rlm_unix_sccsid[] =
10 "@(#)rlm_unix.c 1.00 Copyright 1998-1999 Cistron Internet Services B.V.";
14 #include <sys/types.h>
41 # include <sys/security.h>
54 static char trans[64] =
55 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
56 #define ENC(c) trans[c]
59 * The Group = handler.
61 static int groupcmp(VALUE_PAIR *request, VALUE_PAIR *check,
62 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
70 username = request->strvalue;
72 if (cache_passwd && (retval = H_groupcmp(check, username)) != -2)
75 if ((pwd = rad_getpwnam(username)) == NULL)
78 if ((grp = getgrnam(check->strvalue)) == NULL)
81 retval = (pwd->pw_gid == grp->gr_gid) ? 0 : -1;
83 for (member = grp->gr_mem; *member && retval; member++) {
84 if (strcmp(*member, pwd->pw_name) == 0)
93 * FIXME: We really should have an 'init' which makes
96 static int unix_init(int argc, char **argv)
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);
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");
110 if (buildGrpList() < 0) {
111 log(L_ERR, "HASH: unable to cache groups file. "
112 "disable caching and run debugs");
124 static int unix_detach(void)
126 paircompare_unregister(PW_GROUP, groupcmp);
128 paircompare_unregister(PW_GROUP_NAME, groupcmp);
135 * Check the users password against the standard UNIX
138 static int unix_authenticate(REQUEST *request, char *name, char *passwd)
142 char *encrypted_pass;
152 struct pr_passwd *pr_pw;
154 #ifdef HAVE_GETUSERSHELL
158 if (cache_passwd && (ret = H_unix_pass(name, passwd)) != -2)
159 return (ret == 0) ? RLM_AUTH_OK : RLM_AUTH_REJECT;
162 if ((pr_pw = getprpwnam(name)) == NULL)
164 encrypted_pass = pr_pw->ufld.fd_encrypt;
167 * Get encrypted password from password file
169 if ((pwd = rad_getpwnam(name)) == NULL) {
170 return RLM_AUTH_REJECT;
172 encrypted_pass = pwd->pw_passwd;
177 * See if there is a shadow password.
179 if ((spwd = getspnam(name)) != NULL)
181 encrypted_pass = spwd->pw_passwd;
183 encrypted_pass = spwd->sp_pwdp;
185 #endif /* HAVE_GETSPNAM */
189 * Undocumented temporary compatibility for iphil.NET
190 * Users with a certain shell are always denied access.
192 if (strcmp(pwd->pw_shell, DENY_SHELL) == 0) {
193 log(L_AUTH, "rlm_unix: [%s]: invalid shell", name);
194 return RLM_AUTH_REJECT;
198 #if HAVE_GETUSERSHELL
200 * Check /etc/shells for a valid shell. If that file
201 * contains /RADIUSD/ANY/SHELL then any shell will do.
203 while ((shell = getusershell()) != NULL) {
204 if (strcmp(shell, pwd->pw_shell) == 0 ||
205 strcmp(shell, "/RADIUSD/ANY/SHELL") == 0) {
211 return RLM_AUTH_REJECT;
214 #if defined(HAVE_GETSPNAM) && !defined(M_UNIX)
216 * Check if password has expired.
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;
225 #if defined(__FreeBSD__) || defined(bsdi) || defined(_PWF_EXPIRE)
227 * Check if password has expired.
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;
237 * Check if account is locked.
239 if (pr_pw->uflg.fg_lock!=1) {
240 log(L_AUTH, "rlm_unix: [%s]: account locked", name);
241 return RLM_AUTH_REJECT;
246 * We might have a passwordless account.
248 if (encrypted_pass[0] == 0)
252 * Check encrypted password.
254 encpw = crypt(passwd, encrypted_pass);
255 if (strcmp(encpw, encrypted_pass))
256 return RLM_AUTH_REJECT;
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.
266 static char *uue(void *in)
269 static unsigned char res[7];
270 unsigned char *data = (char *)in;
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 );
277 res[4] = ENC( data[3] >> 2 );
278 res[5] = ENC( (data[3] << 4) & 060 );
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");
291 * Unix accounting - write a wtmp file.
293 static int unix_accounting(REQUEST *request)
305 int framed_address = 0;
309 int nas_port_type = 0;
312 * Which type is this.
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;
321 * FIXME: handle PW_STATUS_ALIVE like 1.5.4.3 did.
323 if (status != PW_STATUS_START &&
324 status != PW_STATUS_STOP)
328 * We're only interested in accounting messages
329 * with a username in it.
331 if ((vp = pairfind(request->packet->vps, PW_USER_NAME)) == NULL)
335 memset(&ut, 0, sizeof(ut));
338 * First, find the interesting attributes.
340 for (vp = request->packet->vps; vp; vp = vp->next) {
341 switch (vp->attribute) {
343 strncpy(ut.ut_name, vp->strvalue, UT_NAMESIZE);
345 case PW_LOGIN_IP_HOST:
346 case PW_FRAMED_IP_ADDRESS:
347 framed_address = vp->lvalue;
349 case PW_FRAMED_PROTOCOL:
350 protocol = vp->lvalue;
352 case PW_NAS_IP_ADDRESS:
353 nas_address = vp->lvalue;
356 nas_port = vp->lvalue;
359 case PW_ACCT_DELAY_TIME:
362 case PW_NAS_PORT_TYPE:
363 nas_port_type = vp->lvalue;
369 * We don't store !root sessions, or sessions
370 * where we didn't see a PW_NAS_PORT_ID.
372 if (strncmp(ut.ut_name, "!root", sizeof(ut.ut_name)) == 0 || !port_seen)
376 * If we didn't find out the NAS address, use the
377 * originator's IP address.
379 if (nas_address == 0)
380 nas_address = request->packet->src_ipaddr;
384 * Linux has a field for the client address.
386 ut.ut_addr = framed_address;
389 * We use the tty field to store the terminal servers' port
390 * and address so that the tty field is unique.
393 if ((cl = nas_find(nas_address)) != NULL)
395 if (s == NULL || s[0] == 0) s = uue(&(nas_address));
397 sprintf(buf, "%03d:%s", nas_port, s);
399 sprintf(buf, "%02d%s", nas_port, s);
401 strncpy(ut.ut_line, buf, UT_LINESIZE);
404 * We store the dynamic IP address in the hostname field.
407 if (framed_address) {
408 ip_ntoa(buf, framed_address);
409 strncpy(ut.ut_host, buf, UT_HOSTSIZE);
413 ut.ut_xtime = t- delay;
415 ut.ut_time = t - delay;
419 * And we can use the ID field to store
422 if (protocol == PW_PPP)
423 strcpy(ut.ut_id, "P");
424 else if (protocol == PW_SLIP)
425 strcpy(ut.ut_id, "S");
427 strcpy(ut.ut_id, "T");
428 ut.ut_type = status == PW_STATUS_STOP ? DEAD_PROCESS : USER_PROCESS;
430 if (status == PW_STATUS_STOP)
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 ..
440 if ((fp = fopen(RADWTMP, "a")) != NULL) {
441 fwrite(&ut, sizeof(ut), 1, fp);
448 /* globally exported name */
449 module_t rlm_unix = {
451 0, /* type: reserved */
452 unix_init, /* initialization */
453 NULL, /* authorization */
454 unix_authenticate, /* authentication */
455 unix_accounting, /* accounting */
456 unix_detach, /* detach */