2 * rlm_unix.c authentication: Unix user authentication
3 * accounting: Functions to write radwtmp file.
4 * Also contains handler for "Group".
9 static const char rcsid[] = "$Id$";
33 # include <sys/security.h>
43 static char trans[64] =
44 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
45 #define ENC(c) trans[c]
48 * Cache the password by default.
50 static int cache_passwd = TRUE;
51 static char *passwd_file = NULL;
52 static char *shadow_file = NULL;
53 static char *group_file = NULL;
55 static CONF_PARSER module_config[] = {
56 { "cache", PW_TYPE_BOOLEAN, &cache_passwd, "yes" },
57 { "passwd", PW_TYPE_STRING_PTR, &passwd_file, NULL },
58 { "shadow", PW_TYPE_STRING_PTR, &shadow_file, NULL },
59 { "group", PW_TYPE_STRING_PTR, &group_file, NULL },
61 { NULL, -1, NULL, NULL } /* end the list */
65 * The Group = handler.
67 static int groupcmp(VALUE_PAIR *request, VALUE_PAIR *check,
68 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
75 check_pairs = check_pairs; reply_pairs = reply_pairs;
77 username = (char *)request->strvalue;
79 if (cache_passwd && (retval = H_groupcmp(check, username)) != -2)
82 if ((pwd = getpwnam(username)) == NULL)
85 if ((grp = getgrnam((char *)check->strvalue)) == NULL)
88 retval = (pwd->pw_gid == grp->gr_gid) ? 0 : -1;
90 for (member = grp->gr_mem; *member && retval; member++) {
91 if (strcmp(*member, pwd->pw_name) == 0)
100 * FIXME: We really should have an 'init' which makes
101 * System auth == Unix
103 static int unix_init(void)
105 paircompare_register(PW_GROUP, PW_USER_NAME, groupcmp);
106 #ifdef PW_GROUP_NAME /* compat */
107 paircompare_register(PW_GROUP_NAME, PW_USER_NAME, groupcmp);
112 static int unix_instantiate(CONF_SECTION *conf, void **instance)
115 * Not yet multiple-instance-aware. groupcmp is a real
118 static int alreadydone=0;
122 "rlm_unix: can't handle multiple authentication instances");
125 if (cf_section_parse(conf, module_config) < 0) {
130 radlog(L_INFO, "HASH: Reinitializing hash structures "
131 "and lists for caching...");
132 if (unix_buildHashTable(passwd_file, shadow_file) < 0) {
133 radlog(L_ERR, "HASH: unable to create user "
134 "hash table. disable caching and run debugs");
137 if (unix_buildGrpList() < 0) {
138 radlog(L_ERR, "HASH: unable to cache groups file. "
139 "disable caching and run debugs");
152 static int unix_destroy(void)
154 paircompare_unregister(PW_GROUP, groupcmp);
156 paircompare_unregister(PW_GROUP_NAME, groupcmp);
163 * Check the users password against the standard UNIX
166 static int unix_authenticate(void *instance, REQUEST *request)
171 char *encrypted_pass;
181 struct pr_passwd *pr_pw;
183 #ifdef HAVE_GETUSERSHELL
189 * We can only authenticate user requests which HAVE
190 * a User-Name attribute.
192 if (!request->username) {
193 radlog(L_AUTH, "rlm_unix: Attribute \"User-Name\" is required for authentication.");
194 return RLM_MODULE_INVALID;
198 * We can only authenticate user requests which HAVE
199 * a Password attribute.
201 if (!request->password) {
202 radlog(L_AUTH, "rlm_unix: Attribute \"Password\" is required for authentication.");
203 return RLM_MODULE_INVALID;
207 * Ensure that we're being passed a plain-text password,
208 * and not anything else.
210 if (request->password->attribute != PW_PASSWORD) {
211 radlog(L_AUTH, "rlm_unix: Attribute \"Password\" is required for authentication. Cannot use \"%s\".", request->password->name);
212 return RLM_MODULE_INVALID;
215 name = (char *)request->username->strvalue;
216 passwd = (char *)request->password->strvalue;
218 if (cache_passwd && (ret = H_unix_pass(name, passwd, &request->reply->vps)) != -2)
219 return (ret == 0) ? RLM_MODULE_OK : RLM_MODULE_REJECT;
222 if ((pr_pw = getprpwnam(name)) == NULL)
223 return RLM_MODULE_NOTFOUND;
224 encrypted_pass = pr_pw->ufld.fd_encrypt;
227 * Get encrypted password from password file
229 if ((pwd = getpwnam(name)) == NULL) {
230 return RLM_MODULE_NOTFOUND;
232 encrypted_pass = pwd->pw_passwd;
237 * See if there is a shadow password.
239 if ((spwd = getspnam(name)) != NULL)
241 encrypted_pass = spwd->pw_passwd;
243 encrypted_pass = spwd->sp_pwdp;
245 #endif /* HAVE_GETSPNAM */
249 * Undocumented temporary compatibility for iphil.NET
250 * Users with a certain shell are always denied access.
252 if (strcmp(pwd->pw_shell, DENY_SHELL) == 0) {
253 radlog(L_AUTH, "rlm_unix: [%s]: invalid shell", name);
254 return RLM_MODULE_REJECT;
258 #if HAVE_GETUSERSHELL
260 * Check /etc/shells for a valid shell. If that file
261 * contains /RADIUSD/ANY/SHELL then any shell will do.
263 while ((shell = getusershell()) != NULL) {
264 if (strcmp(shell, pwd->pw_shell) == 0 ||
265 strcmp(shell, "/RADIUSD/ANY/SHELL") == 0) {
271 return RLM_MODULE_REJECT;
274 #if defined(HAVE_GETSPNAM) && !defined(M_UNIX)
276 * Check if password has expired.
278 if (spwd && spwd->sp_expire > 0 &&
279 (request->timestamp / 86400) > spwd->sp_expire) {
280 radlog(L_AUTH, "rlm_unix: [%s]: password has expired", name);
281 return RLM_MODULE_REJECT;
285 #if defined(__FreeBSD__) || defined(bsdi) || defined(_PWF_EXPIRE)
287 * Check if password has expired.
289 if ((pwd->pw_expire > 0) &&
290 (request->timestamp > pwd->pw_expire)) {
291 radlog(L_AUTH, "rlm_unix: [%s]: password has expired", name);
292 return RLM_MODULE_REJECT;
298 * Check if account is locked.
300 if (pr_pw->uflg.fg_lock!=1) {
301 radlog(L_AUTH, "rlm_unix: [%s]: account locked", name);
302 return RLM_MODULE_USERLOCK;
307 * We might have a passwordless account.
309 if (encrypted_pass[0] == 0)
310 return RLM_MODULE_OK;
313 * Check encrypted password.
315 encpw = crypt(passwd, encrypted_pass);
316 if (strcmp(encpw, encrypted_pass))
317 return RLM_MODULE_REJECT;
319 return RLM_MODULE_OK;
323 * UUencode 4 bits base64. We use this to turn a 4 byte field
324 * (an IP address) into 6 bytes of ASCII. This is used for the
325 * wtmp file if we didn't find a short name in the naslist file.
327 static char *uue(void *in)
330 static unsigned char res[7];
331 unsigned char *data = (unsigned char *)in;
333 res[0] = ENC( data[0] >> 2 );
334 res[1] = ENC( ((data[0] << 4) & 060) + ((data[1] >> 4) & 017) );
335 res[2] = ENC( ((data[1] << 2) & 074) + ((data[2] >> 6) & 03) );
336 res[3] = ENC( data[2] & 077 );
338 res[4] = ENC( data[3] >> 2 );
339 res[5] = ENC( (data[3] << 4) & 060 );
342 for(i = 0; i < 6; i++) {
343 if (res[i] == ' ') res[i] = '`';
344 if (res[i] < 32 || res[i] > 127)
345 printf("uue: protocol error ?!\n");
352 * Unix accounting - write a wtmp file.
354 static int unix_accounting(void *instance, REQUEST *request)
366 int framed_address = 0;
370 int nas_port_type = 0;
375 * Which type is this.
377 if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE))==NULL) {
378 radlog(L_ERR, "Accounting: no Accounting-Status-Type record.");
379 return RLM_MODULE_NOOP;
384 * FIXME: handle PW_STATUS_ALIVE like 1.5.4.3 did.
386 if (status != PW_STATUS_START &&
387 status != PW_STATUS_STOP)
388 return RLM_MODULE_NOOP;
391 * We're only interested in accounting messages
392 * with a username in it.
394 if ((vp = pairfind(request->packet->vps, PW_USER_NAME)) == NULL)
395 return RLM_MODULE_NOOP;
397 t = request->timestamp;
398 memset(&ut, 0, sizeof(ut));
401 * First, find the interesting attributes.
403 for (vp = request->packet->vps; vp; vp = vp->next) {
404 switch (vp->attribute) {
406 strNcpy(ut.ut_name, (char *)vp->strvalue, sizeof(ut.ut_name));
408 case PW_LOGIN_IP_HOST:
409 case PW_FRAMED_IP_ADDRESS:
410 framed_address = vp->lvalue;
412 case PW_FRAMED_PROTOCOL:
413 protocol = vp->lvalue;
415 case PW_NAS_IP_ADDRESS:
416 nas_address = vp->lvalue;
419 nas_port = vp->lvalue;
422 case PW_ACCT_DELAY_TIME:
425 case PW_NAS_PORT_TYPE:
426 nas_port_type = vp->lvalue;
432 * We don't store !root sessions, or sessions
433 * where we didn't see a PW_NAS_PORT_ID.
435 if (strncmp(ut.ut_name, "!root", sizeof(ut.ut_name)) == 0 || !port_seen)
436 return RLM_MODULE_NOOP;
439 * If we didn't find out the NAS address, use the
440 * originator's IP address.
442 if (nas_address == 0)
443 nas_address = request->packet->src_ipaddr;
447 * Linux has a field for the client address.
449 ut.ut_addr = framed_address;
452 * We use the tty field to store the terminal servers' port
453 * and address so that the tty field is unique.
456 if ((cl = nas_find(nas_address)) != NULL)
458 if (s == NULL || s[0] == 0) s = uue(&(nas_address));
459 sprintf(buf, "%03d:%s", nas_port, s);
460 strNcpy(ut.ut_line, buf, sizeof(ut.ut_line));
463 * We store the dynamic IP address in the hostname field.
466 if (framed_address) {
467 ip_ntoa(buf, framed_address);
468 strncpy(ut.ut_host, buf, UT_HOSTSIZE);
472 ut.ut_xtime = t- delay;
474 ut.ut_time = t - delay;
478 * And we can use the ID field to store
481 if (protocol == PW_PPP)
482 strcpy(ut.ut_id, "P");
483 else if (protocol == PW_SLIP)
484 strcpy(ut.ut_id, "S");
486 strcpy(ut.ut_id, "T");
487 ut.ut_type = status == PW_STATUS_STOP ? DEAD_PROCESS : USER_PROCESS;
489 if (status == PW_STATUS_STOP)
493 * Write a RADIUS wtmp log file.
494 * FIXME: return correct error.
495 * Check if file is there. If not, we don't write the
496 * wtmp file. If it is, we try to write. If we fail,
497 * return RLM_MODULE_FAIL ..
499 if ((fp = fopen(RADWTMP, "a")) != NULL) {
500 fwrite(&ut, sizeof(ut), 1, fp);
504 return RLM_MODULE_OK;
507 /* globally exported name */
508 module_t rlm_unix = {
510 0, /* type: reserved */
511 unix_init, /* initialization */
512 unix_instantiate, /* instantiation */
513 NULL, /* authorization */
514 unix_authenticate, /* authentication */
515 NULL, /* preaccounting */
516 unix_accounting, /* accounting */
518 unix_destroy, /* destroy */