diff -Nur radiusd-cistron-1.6.4/src/Make.inc radiusd-cistron-ldap-1.6.4/src/Make.inc --- radiusd-cistron-1.6.4/src/Make.inc Mon Aug 21 18:13:27 2000 +++ radiusd-cistron-ldap-1.6.4/src/Make.inc Tue Mar 27 18:27:35 2001 @@ -4,17 +4,17 @@ # # -RADIUS_DIR = /etc/raddb +RADIUS_DIR = /usr/local/etc/raddb SERVER_OBJS = radiusd.o dict.o files.o util.o md5.o attrprint.o \ acct.o radius.o pam.o log.o version.o proxy.o \ - exec.o auth.o timestr.o cache.o + exec.o auth.o timestr.o cache.o radius_ldap.o SERVERDBM_OBJS = radiusddbm.o dict.o filesdbm.o util.o md5.o attrprint.o \ acct.o radius.o pam.o log.o versiondbm.o proxy.o \ exec.o auth.o timestr.o cache.o SERVER_SRCS = radiusd.c dict.c files.c util.c md5.c attrprint.c acct.c \ radius.c pam.c log.c version.c proxy.c \ - exec.c auth.c timestr.c cache.c + exec.c auth.c timestr.c cache.c radius_ldap.c INCLUDES = radius.h conf.h all: radiusd radwho radzap raduse radtest @@ -119,7 +119,7 @@ install -m 755 -s raduse $(BINDIR) install -m 755 -s radzap $(BINDIR) install -m 755 -s radtest $(BINDIR) - install -m 755 checkrad.pl $(SBINDIR)/checkrad + install -c -m 755 checkrad.pl $(SBINDIR)/checkrad install -m 755 radlast $(BINDIR) # Install the config files @echo "Creating/updating files in $(RADIUS_DIR)"; \ diff -Nur radiusd-cistron-1.6.4/src/Makefile radiusd-cistron-ldap-1.6.4/src/Makefile --- radiusd-cistron-1.6.4/src/Makefile Sun Sep 19 04:10:41 1999 +++ radiusd-cistron-ldap-1.6.4/src/Makefile Tue Mar 27 15:49:16 2001 @@ -1,31 +1,22 @@ # -# Makefile Radius Makefile for Linux (2.0.x, lib5 or libc6) +# Makefile Radius Makefile for BSD (FreeBSD, NetBSD, etc) # # -# -# Autoselect -lshadow and -lcrypt -# -ifneq ($(wildcard /usr/lib/libshadow.a),) -LSHADOW = -lshadow -endif -ifneq ($(wildcard /usr/lib/libcrypt.a),) -LCRYPT = -lcrypt -endif - CC = gcc -CFLAGS = -Wall -g # -DNOSHADOW -LDFLAGS = # -s # tatic -LIBS = $(LSHADOW) +CFLAGS = -Wall -g -DNOSHADOW -DUSE_LDAP -I/usr/local/include +#LDFLAGS = # -s #tatic +LDFLAGS = -L/usr/local/lib +LIBS = -lldap -llber +LCRYPT = -lcrypt -DBM = -DNDBM -DBMLIB = -ldb +DBM = -DNDBM +DBMLIB = #-ldb -# Uncomment these if you want PAM support #PAM = -DPAM -#PAMLIB = -lpam -ldl +#PAMLIB = -lpam BINDIR = /usr/local/bin SBINDIR = /usr/local/sbin -include Make.inc +.include "Make.inc" diff -Nur radiusd-cistron-1.6.4/src/Makefile.BSD radiusd-cistron-ldap-1.6.4/src/Makefile.BSD --- radiusd-cistron-1.6.4/src/Makefile.BSD Sun Sep 19 04:10:41 1999 +++ radiusd-cistron-ldap-1.6.4/src/Makefile.BSD Sat Mar 31 18:43:24 2001 @@ -4,9 +4,10 @@ # CC = gcc -CFLAGS = -Wall -g -DNOSHADOW -LDFLAGS = # -s #tatic -LIBS = +CFLAGS = -Wall -g -DNOSHADOW -DUSE_LDAP -I/usr/local/include +#LDFLAGS = # -s #tatic +LDFLAGS = -L/usr/local/lib +LIBS = -lldap -llber LCRYPT = -lcrypt DBM = -DNDBM diff -Nur radiusd-cistron-1.6.4/src/Makefile.lnx radiusd-cistron-ldap-1.6.4/src/Makefile.lnx --- radiusd-cistron-1.6.4/src/Makefile.lnx Sun Sep 19 04:10:41 1999 +++ radiusd-cistron-ldap-1.6.4/src/Makefile.lnx Sat Mar 31 18:45:36 2001 @@ -14,9 +14,9 @@ endif CC = gcc -CFLAGS = -Wall -g # -DNOSHADOW -LDFLAGS = # -s # tatic -LIBS = $(LSHADOW) +CFLAGS = -Wall -g -DUSE_LDAP -I/usr/local/include # -DNOSHADOW +LDFLAGS = -L/usr/local/lib # -s # tatic +LIBS = $(LSHADOW) -lldap -llber DBM = -DNDBM DBMLIB = -ldb diff -Nur radiusd-cistron-1.6.4/src/README.ldap radiusd-cistron-ldap-1.6.4/src/README.ldap --- radiusd-cistron-1.6.4/src/README.ldap Thu Jan 1 06:00:00 1970 +++ radiusd-cistron-ldap-1.6.4/src/README.ldap Tue Apr 3 10:35:08 2001 @@ -0,0 +1,59 @@ + ====================== + Cistron LDAP Patch + ====================== + +For the latest version of this patch, please go to: + + http://works.agni.com/cistron-ldap.html + + +Directory Structure: +-------------------- + +The RDN must be "uid=". An example DN looks like: + + uid=mojahed,ou=dialin,dc=agni,dc=com + +In this scheme, we don't have to write extra (and error prone) code +the user add scripts to check duplicate uids. + + +Schema: +------- + +You can find our LDAP schema in the website. If you only want to put +the "users" file in LDAP you can use the aRadiusAccount object class +directly. To put more information, you can derive a new objectClass +from aRadiusAccount. + + +Configuration file: +------------------- + +LDAP configuration is read from ldap.conf in the radius configuration +directory, which is normally /etc/raddb. The patch changes this +directory to /usr/local/etc. I feel this is a better place for it. +In the config file, all blank lines and lines starting with '#' are +ignored. The configuration parameters are: + + server: Host name of the LDAP server. + port: Port number the LDAP server is listening on. Default + is 389. + base: This is appended to the "uid=" RDN to get the + full DN of the account. + filter: This filter is applied to the LDAP search. You must + specify one. If you can not come up with any that + works, you may want to use "objectClass=*". + auth_dn: The DN Cistron should bind as. Make sure this DN has + read permission to all the RADIUS attributes, especially + userPassword. + auth_pw: Password for auth_dn. + +An example config file is included in the patch. + + +Changes: +-------- + + 2001-04-01: Initial public release + 2001-04-03: Replaced strlcpy with strncpy for portability diff -Nur radiusd-cistron-1.6.4/src/conf.h radiusd-cistron-ldap-1.6.4/src/conf.h --- radiusd-cistron-1.6.4/src/conf.h Thu Mar 30 21:24:59 2000 +++ radiusd-cistron-ldap-1.6.4/src/conf.h Sun Mar 25 15:09:21 2001 @@ -1,6 +1,6 @@ /* Default Database File Names */ -#define RADIUS_DIR "/etc/raddb" +#define RADIUS_DIR "/usr/local/etc/raddb" #define RADLOG_DIR "/var/log" #ifdef aix diff -Nur radiusd-cistron-1.6.4/src/files.c radiusd-cistron-ldap-1.6.4/src/files.c --- radiusd-cistron-1.6.4/src/files.c Tue Aug 22 00:39:12 2000 +++ radiusd-cistron-ldap-1.6.4/src/files.c Wed Mar 28 14:40:48 2001 @@ -55,6 +55,10 @@ #include "radiusd.h" +#ifdef USE_LDAP +# include "radius_ldap.h" +#endif + static int huntgroup_match(VALUE_PAIR *, char *); #ifdef NDBM static DBM *dbmfile; @@ -439,7 +443,11 @@ * Compare two pair lists except for the password information. * Return 0 on match. */ +#ifdef USE_LDAP +int paircmp(VALUE_PAIR *request, VALUE_PAIR *check) +#else static int paircmp(VALUE_PAIR *request, VALUE_PAIR *check) +#endif { VALUE_PAIR *check_item = check; VALUE_PAIR *auth_item; @@ -752,7 +760,11 @@ * If Password or Crypt-Password is set, but there is no * Auth-Type, add one (kludge!). */ +#ifdef USE_LDAP +void auth_type_fixup(VALUE_PAIR *check) +#else static void auth_type_fixup(VALUE_PAIR *check) +#endif { VALUE_PAIR *vp; VALUE_PAIR *c = NULL; @@ -1139,7 +1151,7 @@ VALUE_PAIR *tmp; PAIR_LIST *pl; int found = 0; -#ifdef USE_DBM +#if defined(USE_DBM) || defined(USE_LDAP) int i, r; char buffer[256]; #endif @@ -1210,6 +1222,36 @@ */ #endif +#ifdef USE_LDAP + + if (!rad_ldap_init()) { + log(L_ERR|L_CONS, "cannot init LDAP connection"); + return 0; + } + + r = rad_ldap_find(name, request_pairs, check_pairs, reply_pairs); + if (r > 0) found = 1; + if (r <= 0 || fallthrough(*reply_pairs)) { + + pairdelete(reply_pairs, PW_FALL_THROUGH); + + sprintf(buffer, "DEFAULT"); + i = 0; + while ((r = rad_ldap_find(buffer, request_pairs, + check_pairs, reply_pairs)) >= 0 || + i < 2) { + if (r > 0) { + found = 1; + if (!fallthrough(*reply_pairs)) + break; + pairdelete(reply_pairs,PW_FALL_THROUGH); + } + sprintf(buffer, "DEFAULT%d", i++); + } + } + rad_ldap_close(); +#else + for(pl = users; pl; pl = pl->next) { if (strcmp(name, pl->name) && strcmp(pl->name, "DEFAULT")) @@ -1234,6 +1276,8 @@ } } +#endif + /* * See if we succeeded. */ @@ -2196,8 +2240,13 @@ checkdbm(users->name, ".db") == 0)) log(L_INFO|L_CONS, "DBM files found but no -b flag given - NOT using DBM"); #endif +#if USE_LDAP + if (!read_ldap_config()) + return -1; +#else if (!use_dbm && (users = file_read(buffer, &err)) == NULL) return -1; +#endif sprintf(buffer, "%.200s/%.50s", radius_dir, RADIUS_HUNTGROUPS); if (stat(buffer, &st) == 0 || errno != ENOENT) { diff -Nur radiusd-cistron-1.6.4/src/ldap.conf radiusd-cistron-ldap-1.6.4/src/ldap.conf --- radiusd-cistron-1.6.4/src/ldap.conf Thu Jan 1 06:00:00 1970 +++ radiusd-cistron-ldap-1.6.4/src/ldap.conf Wed Mar 28 18:32:02 2001 @@ -0,0 +1,6 @@ +server = localhost +port = 389 +base = "ou=dialin,dc=agni,dc=com" +filter = "(|(objectClass=agniRemoteUser)(objectClass=aRadiusAccount))" +auth_dn = "cn=Radius,ou=system,dc=agni,dc=com" +auth_pw = "dimbo" diff -Nur radiusd-cistron-1.6.4/src/radius_ldap.c radiusd-cistron-ldap-1.6.4/src/radius_ldap.c --- radiusd-cistron-1.6.4/src/radius_ldap.c Thu Jan 1 06:00:00 1970 +++ radiusd-cistron-ldap-1.6.4/src/radius_ldap.c Tue Apr 3 10:32:18 2001 @@ -0,0 +1,336 @@ +/* + * Cistron RADIUS LDAP Patch + * + * radius_ldap.c + * + * Use LDAP for authentication. The full users file with + * password and RADIUS check/reply attributes is stored on + * LDAP. + * + * Date: March 25, 2001 + * Author: Mojahedul Hoque Abul Hasanat + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "radiusd.h" + +/* --- START user servicable parts --- */ + +#define LDAP_CONF_FNAME (RADIUS_DIR "/ldap.conf") +#define LDAP_DEFAULT_PORT "389" +#define LDAP_SEARCH_TIMEOUT 40 + +/* --- END user servicable parts --- */ + + +typedef enum { + CHECK_ITEM = 0, + REPLY_ITEM = 1 +} radius_item_type; + +typedef struct _ldap_conf { + char server[128], port[16]; + char base[256]; + char filter[1024]; + char auth_dn[128], auth_pw[128]; +} ldap_conf; + +typedef struct _ldap_radius_entry { + char *ldap_attr; + char *radius_attr; + radius_item_type type; +} ldap_radius_entry; + +ldap_radius_entry ldap_radius_map[] = { + {"radiusAuthType", "Auth-Type", CHECK_ITEM}, + {"userPassword", "Crypt-Password", CHECK_ITEM}, + {"radiusSimultaneousUse", "Simultaneous-Use", CHECK_ITEM}, + {"radiusLoginTime", "Login-Time", CHECK_ITEM}, + {"radiusHuntgroupName", "Huntgroup-Name", CHECK_ITEM}, + {"radiusServiceType", "Service-Type", REPLY_ITEM}, + {"radiusFramedProtocol", "Framed-Protocol", REPLY_ITEM}, + {"radiusFramedIPAddress", "Framed-IP-Address", REPLY_ITEM}, + {"radiusFramedIPNetmask", "Framed-IP-Netmask", REPLY_ITEM}, + {"radiusFramedRoute", "Framed-Route", REPLY_ITEM}, + {"radiusFramedRouting", "Framed-Routing", REPLY_ITEM}, + {"radiusFilterId", "Filter-Id", REPLY_ITEM}, + {"radiusFramedMTU", "Framed-MTU", REPLY_ITEM}, + {"radiusFramedCompression", "Framed-Compression", REPLY_ITEM}, + {"radiusLoginIPHost", "Login-IP-Host", REPLY_ITEM}, + {"radiusLoginService", "Login-Service", REPLY_ITEM}, + {"radiusLoginTCPPort", "Login-TCP-Port", REPLY_ITEM}, + {"radiusSessionTimeout", "Session-Timeout", REPLY_ITEM}, + {"radiusIdleTimeout", "Idle-Timeout", REPLY_ITEM}, + {"radiusPortLimit", "Port-Limit", CHECK_ITEM}, + {"radiusFallThrough", "Fall-Through", REPLY_ITEM} +}; + +#define LDAP_RADIUS_MAP_SIZE (sizeof(ldap_radius_map)/sizeof(ldap_radius_entry)) + +char *strim(char *str); + +/* + * Global Vars + */ + +ldap_conf ld_config; +LDAP *ld_inst; + +/* + * From files.c + */ + +int userparse(char *buffer, VALUE_PAIR **first_pair); +void auth_type_fixup(VALUE_PAIR *check); +int paircmp(VALUE_PAIR *request, VALUE_PAIR *check); + + + +/* + * Read and parse LDAP config file + */ + +int read_ldap_config(void) +{ + FILE *fp; + char buf[2048]; /* large filters! */ + char *ap, *vp, *tmp, *notfound; + int lnum; + + if (!(fp = fopen(LDAP_CONF_FNAME, "r"))) { + log(L_ERR|L_CONS, "Could not open LDAP configuration file: %s", + LDAP_CONF_FNAME); + return 0; + } + + lnum = 1; + bzero(&ld_config, sizeof(ldap_conf)); + strncpy(ld_config.port, LDAP_DEFAULT_PORT, sizeof(ld_config.port)-1); + + while (fgets(buf, sizeof(buf)-1, fp)) { + ap = buf; + + /* ignore empty and remark lines */ + if (*ap == '#') continue; + if (*ap == NULL) continue; + + /* spearate and strip attributes and values */ + + while (isspace(*ap)) ap++; + tmp = ap; + while (*tmp && (*tmp != '=')) tmp++; + if (!*tmp) { + log(L_ERR|L_CONS, "Error in parsing LDAP configuration " + "file, line: %d", lnum); + return 0; + } + *tmp = NULL; + vp = tmp-- + 1; + while ((tmp > ap) && isspace(*tmp)) *tmp-- = NULL; + + /* strip out enclosing quotes if present */ + + while (isspace(*vp)) vp++; + if (*vp == '"') vp++; + tmp = vp + (strlen(vp) - 1); + while ((tmp > vp) && isspace(*tmp)) *tmp-- = NULL; + if ((tmp > vp) && (*tmp == '"')) *tmp = NULL; + + if (strcmp(ap, "server") == 0) { + strncpy(ld_config.server, vp, sizeof(ld_config.server)-1); + } else if (strcmp(ap, "port") == 0) { + strncpy(ld_config.port, vp, sizeof(ld_config.port)-1); + } else if (strcmp(ap, "base") == 0) { + strncpy(ld_config.base, vp, sizeof(ld_config.base)-1); + } else if (strcmp(ap, "filter") == 0) { + strncpy(ld_config.filter, vp, sizeof(ld_config.filter)-1); + } else if (strcmp(ap, "auth_dn") == 0) { + strncpy(ld_config.auth_dn, vp, + sizeof(ld_config.auth_dn)-1); + } else if (strcmp(ap, "auth_pw") == 0) { + strncpy(ld_config.auth_pw, vp, + sizeof(ld_config.auth_pw)-1); + } else { + log(L_ERR|L_CONS, "Unknown LDAP configuration " + "attribute, line: %d", lnum); + return 0; + } + + lnum++; + } + fclose(fp); + + notfound = NULL; + if (ld_config.server[0] == NULL) notfound = "server"; + if (ld_config.base[0] == NULL) notfound = "base"; + if (ld_config.filter[0] == NULL) notfound = "filter"; + if (ld_config.auth_dn[0] == NULL) notfound = "auth_dn"; + if (ld_config.auth_pw[0] == NULL) notfound = "auth_pw"; + if (notfound) { + log(L_ERR|L_CONS, "Did not find mandatory LDAP configuration " + "attribute: %s", notfound); + return 0; + } + + log(L_INFO, "Read LDAP configuration"); + + return 1; +} + + +/* + * Initialize and bind to LDAP server + */ + +int rad_ldap_init(void) +{ + int rc; + + if (!(ld_inst = ldap_init(ld_config.server, atoi(ld_config.port)))) { + log(L_ERR|L_CONS, "Failed to do ldap_init: %s", + strerror(errno)); + return 0; + } + + rc = ldap_simple_bind_s(ld_inst, ld_config.auth_dn, ld_config.auth_pw); + if (rc != LDAP_SUCCESS) { + log(L_ERR|L_CONS, "Failed to bind to LDAP server: %s", + ldap_err2string(rc)); + return 0; + } + + return 1; +} + +void rad_ldap_close(void) +{ + ldap_unbind(ld_inst); + ld_inst = NULL; +} + +/* + * Find user attributes from LDAP + * + * The interface is exactly like dbm_find. + * Returns: -1 not found or error + * 0 found but doesn't match + * 1 found and matched + */ + +int rad_ldap_find(const char *name, VALUE_PAIR *request_pairs, + VALUE_PAIR **check_pairs, VALUE_PAIR **reply_pairs) +{ + char *ldap_attrs[LDAP_RADIUS_MAP_SIZE+2]; /* accountDisabled + NULL */ + LDAPMessage *result = NULL; + char dn[512]; + int disabled, rc, ret; + struct timeval timeout; + char **vals, buf[1024]; + VALUE_PAIR *check_tmp, *reply_tmp; + size_t i; + + timeout.tv_sec = LDAP_SEARCH_TIMEOUT; + timeout.tv_usec = 0; + + sprintf(dn, "uid=%.16s,%.256s", name, ld_config.base); + ldap_attrs[0] = "accountDisabled"; + for (i = 0; i < LDAP_RADIUS_MAP_SIZE; i++) + ldap_attrs[i+1] = ldap_radius_map[i].ldap_attr; + ldap_attrs[LDAP_RADIUS_MAP_SIZE+1] = NULL; + + /* + * Search LDAP + */ + + rc = ldap_search_st(ld_inst, dn, LDAP_SCOPE_SUBTREE, ld_config.filter, + ldap_attrs, 0, &timeout, &result); + if (rc != LDAP_SUCCESS) { + log(L_ERR|L_CONS, "Failed in LDAP search: %s", + ldap_err2string(rc)); + return -1; + } + + if ((result = ldap_first_entry(ld_inst, result)) == NULL) { + ldap_msgfree(result); + return 0; + } + + /* is account disabled? */ + + disabled = 0; + vals = ldap_get_values(ld_inst, result, "accountDisabled"); + if (vals != NULL) { + if ((strcmp(vals[0], "1") == 0) || + (strcasecmp(vals[0], "true") == 0)) + disabled = 1; + ldap_value_free(vals); + } + + if (disabled) { + ldap_msgfree(result); + return 0; + } + + check_tmp = NULL; + reply_tmp = NULL; + + /* + * Parse attributes. + * NOTE: Depends on userfind + */ + + for (i = 0; i < LDAP_RADIUS_MAP_SIZE; i++) { + vals = ldap_get_values(ld_inst, result, + ldap_radius_map[i].ldap_attr); + if (vals == NULL) + continue; + + if (strcmp(ldap_radius_map[i].ldap_attr, "userPassword") == 0) { + if (strncasecmp(vals[0], "{CRYPT}", 7) == 0) + memmove(vals[0], vals[0]+7, strlen(vals[0])-7+1); + } + + sprintf(buf, "%.128s=%.512s", ldap_radius_map[i].radius_attr, + vals[0]); + + DEBUG2("ldap reply: %s", buf); + + rc = userparse(buf, (ldap_radius_map[i].type == CHECK_ITEM) ? + &check_tmp : &reply_tmp); + if (rc != 0) { + log(L_ERR|L_CONS, "Parse error for user %s, " + "RADIUS attribute %s", name, + ldap_radius_map[i].radius_attr); + ldap_value_free(vals); + ldap_msgfree(result); + return -1; + } + ldap_value_free(vals); + } + ldap_msgfree(result); /* ldap cleaup */ + + /* + * See if the check_pairs match. + */ + + ret = 0; + auth_type_fixup(check_tmp); + if (paircmp(request_pairs, check_tmp) == 0) { + ret = 1; + pairmove(reply_pairs, &reply_tmp); + pairmove2(reply_pairs, &reply_tmp, PW_FALL_THROUGH); + pairmove(check_pairs, &check_tmp); + } + pairfree(reply_tmp); + pairfree(check_tmp); + + return ret; +} diff -Nur radiusd-cistron-1.6.4/src/radius_ldap.h radiusd-cistron-ldap-1.6.4/src/radius_ldap.h --- radiusd-cistron-1.6.4/src/radius_ldap.h Thu Jan 1 06:00:00 1970 +++ radiusd-cistron-ldap-1.6.4/src/radius_ldap.h Sat Mar 31 18:34:01 2001 @@ -0,0 +1,24 @@ +/* + * Cistron RADIUS LDAP Patch + * + * radius_ldap.h + * + * Use LDAP for authentication. The full users file with + * password and RADIUS check/reply attributes is stored on + * LDAP. + * + * Date: March 25, 2001 + * Author: Mojahedul Hoque Abul Hasanat + * + */ + +#ifndef _RADIUS_LDAP_H_INCLUDED_ +#define _RADIUS_LDAP_H_INCLUDED_ + +int read_ldap_config(void); +int rad_ldap_init(void); +int rad_ldap_find(const char *name, VALUE_PAIR *request_pairs, + VALUE_PAIR **check_pairs, VALUE_PAIR **reply_pairs); +void rad_ldap_close(void); + +#endif