diff -urN radiusd-cistron-1.6-dist/doc/README.db-file radiusd-cistron-1.6/doc/README.db-file --- radiusd-cistron-1.6-dist/doc/README.db-file 1969-12-31 18:00:00.000000000 -0600 +++ radiusd-cistron-1.6/doc/README.db-file 2003-06-02 11:43:07.000000000 -0500 @@ -0,0 +1,50 @@ +Berkeley DB file authentication support for Cistron RADIUSD + +0. Introduction + + Berkeley DB file authentication was written by Chris Adams + . + +1. Usage + + Use DB-File = "passwd.db", Auth-Type = DB-File in the users file as + check items. This will cause the specified Berkeley DB file (in HASH + format) to be opened and the username used as a key to find an entry. + Entries are in the same form as the Apache auth_db file; the username + is the key and the value is in the form: + + cryptedpass:group[,group...] + + You can also use a check item list like DB-File = "passwd.db", + Group = "Mailbox", Auth-Type = Reject to lookup an entry, check for a + specific group, and reject the auth request based on the group. + +2. Notes + + This uses flock() to get a read lock on the file with a 3 second + timeout. Any updates to the database should also use flock() to keep + radiusd from seeing an update in process (which could cause the + radiusd process to crash or see incorrect data). + + This uses the old-style (version 1) DB access methods. If db.h is + from a newer version and you have a db_185.h, add -DDB185 to the + DBFILE definition. + +3. TODO + + Other locking methods should be supported for platforms where flock() + doesn't work right (or if the .db file lives on NFS). + + Update to use a newer interface to the DB library (maybe one that + includes locking in the library so radiusd doesn't have to worry + about it at all). + +4. Example + +DEFAULT DB-File = "passwd.db", Group = "Mailbox", Auth-Type = Reject + Reply-Message = "Mailbox accounts don't get dialup access" + +DEFAULT DB-File = "passwd.db", Auth-Type = DB-File + Session-Timeout = 86400, + Framed-Protocol = PPP + diff -urN radiusd-cistron-1.6-dist/raddb/dictionary.standard radiusd-cistron-1.6/raddb/dictionary.standard --- radiusd-cistron-1.6-dist/raddb/dictionary.standard 2003-04-10 09:17:10.000000000 -0500 +++ radiusd-cistron-1.6/raddb/dictionary.standard 2003-06-02 11:27:06.000000000 -0500 @@ -113,6 +113,7 @@ ATTRIBUTE Pam-Auth 1041 string ATTRIBUTE Login-Time 1042 string ATTRIBUTE Realm 1045 string +ATTRIBUTE DB-File 1046 string ATTRIBUTE Client-IP-Address 1052 ipaddr # @@ -231,6 +232,7 @@ # # Cistron extensions # +VALUE Auth-Type DB-File 252 VALUE Auth-Type Pam 253 VALUE Auth-Type Accept 254 diff -urN radiusd-cistron-1.6-dist/src/Make.inc radiusd-cistron-1.6/src/Make.inc --- radiusd-cistron-1.6-dist/src/Make.inc 2003-06-02 11:23:22.000000000 -0500 +++ radiusd-cistron-1.6/src/Make.inc 2003-06-02 11:26:03.000000000 -0500 @@ -15,7 +15,7 @@ SERVER_OBJS = radiusd.o files.o acct.o pam.o version.o proxy.o \ exec.o auth.o timestr.o cache.o readusers.o \ - crypt16.o + crypt16.o dbfile.o LIB_OBJS = dict.o util.o md5.o attrprint.o radius.o log.o pair.o \ encrattr.o filters.o @@ -32,7 +32,7 @@ radiusd: $(SERVER_OBJS) $(LIB_OBJS) $(CC) $(LDFLAGS) -o radiusd $(SERVER_OBJS) \ $(LIB_OBJS) \ - $(LIBS) $(LCRYPT) $(PAMLIB) $(DBMLIB) + $(LIBS) $(LCRYPT) $(PAMLIB) $(DBMLIB) $(DBLIB) radiusd.o: radiusd.c $(INCLUDES) $(CC) $(CFLAGS) $(DIRS) $(DBM) -c radiusd.c @@ -50,7 +50,7 @@ $(CC) $(CFLAGS) $(DIRS) -c dict.c files.o: files.c $(INCLUDES) - $(CC) $(CFLAGS) $(DIRS) $(DBM) $(PAM) -c files.c + $(CC) $(CFLAGS) $(DIRS) $(DBM) $(PAM) $(DBFILE) -c files.c radius.o: radius.c $(INCLUDES) $(CC) $(CFLAGS) $(DIRS) -c radius.c @@ -77,7 +77,7 @@ $(CC) $(CFLAGS) $(DIRS) -c exec.c auth.o: auth.c $(INCLUDES) - $(CC) $(CFLAGS) $(DIRS) $(PAM) -c auth.c + $(CC) $(CFLAGS) $(DIRS) $(PAM) $(DBFILE) -c auth.c pair.o: pair.c $(INCLUDES) $(CC) $(CFLAGS) $(DIRS) $(PAM) -c pair.c @@ -85,6 +85,9 @@ version.o: version.c $(INCLUDES) $(CC) $(CFLAGS) $(DIRS) $(DBM) $(PAM) -o version.o -c version.c +dbfile.o: dbfile.c $(INCLUDES) + $(CC) $(CFLAGS) $(DIRS) $(DBFILE) -c dbfile.c + crypt16.o: crypt16.c $(INCLUDES) $(CC) $(CFLAGS) $(DIRS) -c crypt16.c diff -urN radiusd-cistron-1.6-dist/src/Makefile radiusd-cistron-1.6/src/Makefile --- radiusd-cistron-1.6-dist/src/Makefile 2003-05-23 10:54:09.000000000 -0500 +++ radiusd-cistron-1.6/src/Makefile 2003-06-02 11:23:33.000000000 -0500 @@ -31,6 +31,9 @@ INSTALL = install DINSTALL = install -d +DBFILE = -DDBFILE -DDB185 +DBLIB = -ldb + BINDIR = /usr/local/bin SBINDIR = /usr/local/sbin MANDIR = /usr/local/man diff -urN radiusd-cistron-1.6-dist/src/Makefile.lnx radiusd-cistron-1.6/src/Makefile.lnx --- radiusd-cistron-1.6-dist/src/Makefile.lnx 2003-05-23 10:54:09.000000000 -0500 +++ radiusd-cistron-1.6/src/Makefile.lnx 2003-06-02 11:23:33.000000000 -0500 @@ -31,6 +31,9 @@ INSTALL = install DINSTALL = install -d +DBFILE = -DDBFILE -DDB185 +DBLIB = -ldb + BINDIR = /usr/local/bin SBINDIR = /usr/local/sbin MANDIR = /usr/local/man diff -urN radiusd-cistron-1.6-dist/src/auth.c radiusd-cistron-1.6/src/auth.c --- radiusd-cistron-1.6-dist/src/auth.c 2003-04-03 11:23:17.000000000 -0600 +++ radiusd-cistron-1.6/src/auth.c 2003-06-02 11:23:33.000000000 -0500 @@ -278,6 +278,10 @@ VALUE_PAIR *pampair; char *pamauth = NULL; #endif +#ifdef DBFILE + VALUE_PAIR *dbpair; + char *dbfile = NULL; +#endif char string[AUTH_STRING_LEN]; char name[AUTH_STRING_LEN]; char *ptr; @@ -318,6 +322,12 @@ if (auth_item == NULL) return result; +#ifdef DBFILE + if ((dbpair = pairfind(check_item, PW_DBFILE)) != NULL) { + dbfile = dbpair->strvalue; + } +#endif + /* * Decrypt the password. */ @@ -394,6 +404,16 @@ result = -1; #endif break; + case PW_AUTHTYPE_DB: +#ifdef DBFILE + DEBUG2(" auth: DB"); + if (db_pass(name, string, dbfile) != 0) + result = -1; +#else + log(L_ERR, "%s: DB file auth not available", name); + result = -1; +#endif + break; case PW_AUTHTYPE_CRYPT: DEBUG2(" auth: Crypt"); if (password_pair == NULL) { diff -urN radiusd-cistron-1.6-dist/src/dbfile.c radiusd-cistron-1.6/src/dbfile.c --- radiusd-cistron-1.6-dist/src/dbfile.c 1969-12-31 18:00:00.000000000 -0600 +++ radiusd-cistron-1.6/src/dbfile.c 2003-06-02 11:23:33.000000000 -0500 @@ -0,0 +1,266 @@ +#ifdef DBFILE + +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 1 +#endif +#ifndef _BSD_SOURCE +#define _BSD_SOURCE 1 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DB185 +#include +#else +#include +#endif + +#include "radiusd.h" + +#define TIMEOUT 3 + +/* User record structure */ +typedef struct { + char file[256]; + char *user; + char *pass; + char **groups; +} DBUSER; + +/* Single user "cache" */ +static DBUSER dbcache; + +/* Used for alarm */ +sigjmp_buf env; + +void +myalarm (num) + int num; +{ + siglongjmp (env, 1); +} + + +/* + * Lookup an entry in a DB file + */ +DBUSER * +lookup_entry (dbfile, user) + char *dbfile; + char *user; +{ + DB *db; + DBT key, data; + int lock; + int result; + char *p, *q; + char *str; + int size; + int oldalarm; + struct sigaction newsig, oldsig; + int i; + char buf[256]; + + /* declare these static to avoid warnings about setlongjmp */ + static int timeout; + static char *dbf; + + /* Build the full path */ + dbf = dbfile; + if (dbf[0] != '/') { + sprintf (buf, "%.200s/%.50s", radius_dir, dbf); + dbf = buf; + } + + /* Check the "cache" */ + if (dbcache.file[0] && (strcmp (dbcache.file, dbf) == 0) + && (strcmp (dbcache.user, user) == 0)) + return (&dbcache); + + /* Clear the "cache" */ + if (dbcache.file[0]) { + free (dbcache.user); + free (dbcache.pass); + free (dbcache.groups); + dbcache.file[0] = '\0'; + } + + /* Lock the database */ + timeout = 0; + newsig.sa_handler = myalarm; + newsig.sa_flags = 0; + + if ((lock = open (dbf, O_RDONLY)) == -1) { + log (L_ERR, "dbfile open %s failed: %m", dbf); + return (NULL); + } + if (sigaction (SIGALRM, &newsig, &oldsig)) { + log (L_ERR, "dbfile set signal: %m"); + close (lock); + return (NULL); + } + if (sigsetjmp (env, 1) != 0) + timeout = 1; + + oldalarm = alarm (0); + if (timeout == 0) { + alarm (TIMEOUT); + result = flock (lock, LOCK_SH); + } else + result = -1; + + sigaction (SIGALRM, &oldsig, NULL); + alarm (oldalarm); + if (result != 0) { + if (timeout) + log (L_ERR, "dbfile flock timeout"); + else + log (L_ERR, "dbfile flock SH failed: %m"); + close (lock); + return (NULL); + } + + /* Open the database */ + db = dbopen (dbf, O_RDONLY, 0, DB_HASH, NULL); + if (db == NULL) { + log (L_ERR, "dbfile dbopen %s failed: %m", dbf); + flock (lock, LOCK_UN); + close (lock); + return (NULL); + } + + /* Look up the user */ + str = NULL; + key.size = strlen (user); + key.data = user; + result = db->get (db, &key, &data, 0); + if (result == -1) + /* Oops, something bad happened */ + log (L_ERR, "db get failed: %m"); + else if (result == 0) { + /* Got a key match, save the value */ + p = data.data; + size = data.size; + if (p[size - 1]) + /* Not null terminated */ + size ++; + if ((str = malloc (sizeof (char) * size)) == NULL) { + log (L_ERR, "malloc str failed: %m"); + str = NULL; + } else { + strncpy (str, p, data.size); + str[data.size] = '\0'; + } + } + + /* Close and unlock the database */ + db->close (db); + flock (lock, LOCK_UN); + close (lock); + + /* If there is not a result to parse, return */ + if (! str) + return (NULL); + + /* Save the password */ + p = strchr (str, ':'); + dbcache.pass = str; + + if (p) { + /* Count the groups */ + *p++ = '\0'; + for (i = 1, q = p; q != NULL; i++) + if ((q = strchr (q, ',')) != NULL) + q++; + } else { + /* No groups */ + i = 1; + } + /* Save the groups */ + if ((dbcache.groups = malloc (sizeof (char *) * i)) == NULL) { + log (L_ERR, "malloc groups failed: %m"); + free (dbcache.pass); + return (NULL); + } + for (i = 0, q = p; q != NULL; i++) { + dbcache.groups[i] = q; + if ((q = strchr (q, ',')) != NULL) + q++; + } + dbcache.groups[i] = NULL; + + /* Save the username */ + if ((dbcache.user = strdup (user)) == NULL) { + log (L_ERR, "dup user failed: %m"); + free (dbcache.pass); + free (dbcache.groups); + return (NULL); + } + + /* Save the filename */ + strncpy (dbcache.file, dbf, 255); + + return (&dbcache); +} + + +/* + * Check a user's password + */ +int +db_pass (user, pass, dbfile) + char *user; + char *pass; + char *dbfile; +{ + DBUSER *ent; + + if ((ent = lookup_entry (dbfile, user)) == NULL) + return (-1); + + if (ent->pass[0] == '!') { + log (L_AUTH, "User blocked: [%s]", user); + return (-1); + } + + if (strcmp (crypt (pass, ent->pass), ent->pass) != 0) + return (-1); + + return (0); +} + + +/* + * See if a user is a member of a group + */ +int +db_group (user, group, dbfile) + char *user; + char *group; + char *dbfile; +{ + DBUSER *ent; + int i; + + if ((ent = lookup_entry (dbfile, user)) == NULL) + return (-1); + + for (i = 0; ent->groups[i]; i++) + if (strcmp (ent->groups[i], group) == 0) + return (0); + + return (-1); +} + +#endif diff -urN radiusd-cistron-1.6-dist/src/files.c radiusd-cistron-1.6/src/files.c --- radiusd-cistron-1.6-dist/src/files.c 2003-06-02 11:23:22.000000000 -0500 +++ radiusd-cistron-1.6/src/files.c 2003-06-02 11:23:33.000000000 -0500 @@ -294,6 +294,10 @@ int result = 0; char username[AUTH_STRING_LEN]; int compare; +#ifdef DBFILE + int do_dbfile = 0; + char *dbfile = NULL; +#endif username[0] = '\0'; while (result == 0 && check_item != NULL) { @@ -302,11 +306,21 @@ * Attributes we skip during comparison. * These are "server" check items. */ +#ifdef DBFILE + case PW_DBFILE: + dbfile = check_item->strvalue; + check_item = check_item->next; + continue; + case PW_AUTHTYPE: + if (check_item->lvalue == PW_AUTHTYPE_DB) + do_dbfile = 1; +#else + case PW_AUTHTYPE: +#endif case PW_EXPIRATION: case PW_LOGIN_TIME: case PW_PASSWORD: case PW_CRYPT_PASSWORD: - case PW_AUTHTYPE: #ifdef PAM /* cjd 19980706 */ case PAM_AUTH_ATTR: #endif @@ -380,7 +394,15 @@ else if (check_item->attribute == PW_GROUP_NAME || check_item->attribute == PW_GROUP) { - compare = groupcmp(check_item, username); +#ifdef DBFILE + if (dbfile) + compare = db_group(username, + check_item->strvalue, + dbfile); + else +#endif + compare = groupcmp(check_item, + username); } else if (check_item->attribute == PW_HUNTGROUP_NAME){ @@ -464,6 +486,10 @@ VALUE_PAIR *check_item = check; VALUE_PAIR *auth_item; int result = -1; +#ifdef DBFILE + int do_dbfile = 0; + char *dbfile = NULL; +#endif if (check == NULL) return 0; @@ -473,9 +499,19 @@ * Attributes we skip during comparison. * These are "server" check items. */ +#ifdef DBFILE + case PW_DBFILE: + dbfile = check_item->strvalue; + check_item = check_item->next; + continue; + case PW_AUTHTYPE: + if (check_item->lvalue == PW_AUTHTYPE_DB) + do_dbfile = 1; +#else + case PW_AUTHTYPE: +#endif case PW_EXPIRATION: case PW_PASSWORD: - case PW_AUTHTYPE: /* cjd 19980706 */ #ifdef PAM @@ -511,6 +547,14 @@ case PW_TYPE_STRING: if (check_item->attribute == PW_GROUP_NAME || check_item->attribute == PW_GROUP) { +#ifdef DBFILE + if (dbfile && + db_group(auth_item->strvalue, + check_item->strvalue, + dbfile)) + result = 0; + else +#endif if (groupcmp(check_item, auth_item->strvalue)==0) result = 0; } diff -urN radiusd-cistron-1.6-dist/src/radius.h radiusd-cistron-1.6/src/radius.h --- radiusd-cistron-1.6-dist/src/radius.h 2003-06-02 11:23:22.000000000 -0500 +++ radiusd-cistron-1.6/src/radius.h 2003-06-02 11:23:33.000000000 -0500 @@ -145,6 +145,7 @@ #define PAM_AUTH_ATTR 1041 #define PW_LOGIN_TIME 1042 #define PW_REALM 1045 +#define PW_DBFILE 1046 #define PW_CLIENT_IP_ADDRESS 1052 /* @@ -188,6 +189,7 @@ #define PW_AUTHTYPE_SECURID 2 #define PW_AUTHTYPE_CRYPT 3 #define PW_AUTHTYPE_REJECT 4 +#define PW_AUTHTYPE_DB 252 #define PW_AUTHTYPE_PAM 253 #define PW_AUTHTYPE_ACCEPT 254 diff -urN radiusd-cistron-1.6-dist/src/radiusd.h radiusd-cistron-1.6/src/radiusd.h --- radiusd-cistron-1.6-dist/src/radiusd.h 2003-06-02 11:23:22.000000000 -0500 +++ radiusd-cistron-1.6/src/radiusd.h 2003-06-02 11:23:33.000000000 -0500 @@ -360,3 +360,7 @@ #define crypt crypt16 char * crypt16 (char *, char *); +/* dbfile.c */ +int db_pass (char *user, char *pass, char *dbfile); +int db_group (char *user, char *group, char *dbfile); +