From 0eb6967f27868332dcda4f1359ad3e1dfedf9a54 Mon Sep 17 00:00:00 2001 From: pacman Date: Wed, 18 Oct 2000 19:42:52 +0000 Subject: [PATCH] Allow multiple instances of "unix" module for authentication out of several passwd files. --- src/modules/rlm_unix/cache.c | 258 ++++++++++++++++++++++++---------------- src/modules/rlm_unix/cache.h | 16 ++- src/modules/rlm_unix/rlm_unix.c | 132 +++++++++++++++----- 3 files changed, 265 insertions(+), 141 deletions(-) diff --git a/src/modules/rlm_unix/cache.c b/src/modules/rlm_unix/cache.c index 19a9323..916595b 100644 --- a/src/modules/rlm_unix/cache.c +++ b/src/modules/rlm_unix/cache.c @@ -49,18 +49,16 @@ static const char rcsid[] = "$Id$"; * Static prototypes */ static void chgLoggedin(char *user, int diff); -static struct mypasswd *findHashUser(const char *user); -static int storeHashUser(struct mypasswd *new, int idx); +static struct mypasswd *findHashUser(struct pwcache *cache, const char *user); +static int storeHashUser(struct pwcache *cache, struct mypasswd *new, int idx); static int hashUserName(const char *s); -/* Make the tables global since so many functions rely on them */ -static struct mypasswd *hashtable[HASHTABLESIZE]; -static struct mygroup *grphead = NULL; - /* Builds the hash table up by storing passwd/shadow fields - * in memory. Returns -1 on failure, 0 on success. + * in memory. Returns NULL on failure, pointer to the cache on success. */ -int unix_buildHashTable(const char *passwd_file, const char *shadow_file) { +struct pwcache *unix_buildpwcache(const char *passwd_file, + const char *shadow_file) +{ FILE *passwd; #if HAVE_SHADOW_H FILE *shadow; @@ -70,27 +68,25 @@ int unix_buildHashTable(const char *passwd_file, const char *shadow_file) { char username[MAXUSERNAME]; char *ptr, *bufptr; int len, hashindex, numread=0; - struct mypasswd *new, *cur, *next; + struct mypasswd *new; - memset((char *)username, 0, MAXUSERNAME); + int len2, idx; + struct group *grp; + struct mygroup *g_new; + char **member; - /* Initialize the table. This works even if we're rebuilding it */ - for(hashindex=0; hashindexnext; - free(cur->pw_name); - free(cur->pw_passwd); - free(cur->pw_gecos); - free(cur); - cur = next; - } - } - } + struct pwcache *cache; + + if((cache = malloc(sizeof *cache)) == NULL) { + radlog(L_ERR, "HASH: Out of memory!"); + return NULL; + } + + memset(username, 0, MAXUSERNAME); /* Init hash array */ - memset((struct mypasswd *)hashtable, 0, (HASHTABLESIZE*(sizeof(struct mypasswd *)))); + memset(cache->hashtable, 0, sizeof cache->hashtable); + cache->grphead = NULL; /* * If not set, use pre-defined defaults. @@ -102,7 +98,8 @@ int unix_buildHashTable(const char *passwd_file, const char *shadow_file) { if ((passwd = fopen(passwd_file, "r")) == NULL) { radlog(L_ERR, "HASH: Can't open file %s: %s", passwd_file, strerror(errno)); - return -1; + unix_freepwcache(cache); + return NULL; } else { while(fgets(buffer, BUFSIZE , passwd) != (char *)NULL) { numread++; @@ -124,14 +121,19 @@ int unix_buildHashTable(const char *passwd_file, const char *shadow_file) { /* Allocate space for structure to go in hashtable */ if((new = (struct mypasswd *)malloc(sizeof(struct mypasswd))) == NULL) { radlog(L_ERR, "HASH: Out of memory!"); - return -1; + fclose(passwd); + unix_freepwcache(cache); + return NULL; } memset((struct mypasswd *)new, 0, sizeof(struct mypasswd)); /* Put username into new structure */ if((new->pw_name = (char *)malloc(strlen(username)+1)) == NULL) { radlog(L_ERR, "HASH: Out of memory!"); - return -1; + free(new); + fclose(passwd); + unix_freepwcache(cache); + return NULL; } strncpy(new->pw_name, username, strlen(username)+1); @@ -147,7 +149,11 @@ int unix_buildHashTable(const char *passwd_file, const char *shadow_file) { len = ptr - bufptr; if((new->pw_passwd = (char *)malloc(len+1)) == NULL) { radlog(L_ERR, "HASH: Out of memory!"); - return -1; + free(new->pw_name); + free(new); + fclose(passwd); + unix_freepwcache(cache); + return NULL; } strncpy(new->pw_passwd, bufptr, len); new->pw_passwd[len] = '\0'; @@ -189,7 +195,12 @@ int unix_buildHashTable(const char *passwd_file, const char *shadow_file) { len = ptr - bufptr; if((new->pw_gecos = (char *)malloc(len+1)) == NULL) { radlog(L_ERR, "HASH: Out of memory!"); - return -1; + free(new->pw_passwd); + free(new->pw_name); + free(new); + fclose(passwd); + unix_freepwcache(cache); + return NULL; } strncpy(new->pw_gecos, bufptr, len); new->pw_gecos[len] = '\0'; @@ -201,8 +212,8 @@ int unix_buildHashTable(const char *passwd_file, const char *shadow_file) { /*printf("User: %s, UID: %d, GID: %d\n", new->pw_name, new->pw_uid, new->pw_gid);*/ /* Store user in the hash */ - storeHashUser(new, hashindex); - } /* End while(fgets(buffer, BUFSIZE , passwd) != (char *)NULL) { */ + storeHashUser(cache, new, hashindex); + } /* End while(fgets(buffer, BUFSIZE , passwd) != (char *)NULL) */ } /* End if */ fclose(passwd); @@ -220,7 +231,8 @@ int unix_buildHashTable(const char *passwd_file, const char *shadow_file) { if ((shadow = fopen(shadow_file, "r")) == NULL) { radlog(L_ERR, "HASH: Can't open file %s: %s", shadow_file, strerror(errno)); - return -1; + unix_freepwcache(cache); + return NULL; } else { while(fgets(buffer, BUFSIZE , shadow) != (char *)NULL) { @@ -233,7 +245,7 @@ int unix_buildHashTable(const char *passwd_file, const char *shadow_file) { } strncpy(username, buffer, len); username[len] = '\0'; - if((new = findHashUser(username)) == NULL) { + if((new = findHashUser(cache, username)) == NULL) { radlog(L_ERR, "HASH: Username %s in shadow but not passwd??", username); continue; } @@ -265,7 +277,9 @@ int unix_buildHashTable(const char *passwd_file, const char *shadow_file) { if((new->pw_passwd = (char *)malloc(len+1)) == NULL) { radlog(L_ERR, "HASH: Out of memory!"); - return -1; + fclose(shadow); + unix_freepwcache(cache); + return NULL; } strncpy(new->pw_passwd, bufptr, len); new->pw_passwd[len] = '\0'; @@ -277,100 +291,92 @@ int unix_buildHashTable(const char *passwd_file, const char *shadow_file) { /* log how many entries we stored from the passwd file */ radlog(L_INFO, "HASH: Stored %d entries from %s", numread, passwd_file); - return 0; -} - -/* This function caches the /etc/group file, so it's one less thing - * we have to lookup on disk. it uses getgrent(), which is quite slow, - * but the group file is generally small enough that it won't matter - * As a side note, caching the user list per group was a major pain - * in the ass, and I won't even need it. I really hope that somebody - * out there needs and appreciates it. - * Returns -1 on failure, and 0 on success - */ -int unix_buildGrpList(void) { - - int len, len2, idx, numread=0; - struct group *grp; - struct mygroup *new, *cur, *next; - char **member; - - cur = grphead; - - /* Free up former grp list (we can use this as a rebuild function too */ - while(cur) { - next = cur->next; + /* The remainder of this function caches the /etc/group file, so it's + * one less thing we have to lookup on disk. it uses getgrent(), + * which is quite slow, but the group file is generally small enough + * that it won't matter + * As a side note, caching the user list per group was a major pain + * in the ass, and I won't even need it. I really hope that somebody + * out there needs and appreciates it. + */ - /* Free name, name, member list */ - for(member = cur->gr_mem; *member; member++) { - free(*member); - } - free(cur->gr_mem); - free(cur->gr_name); - free(cur->gr_passwd); - free(cur); - cur = next; - } - grphead = NULL; - /* Make sure to begin at beginning */ setgrent(); + numread = 0; + /* Get next entry from the group file */ while((grp = getgrent()) != NULL) { /* Make new mygroup structure in mem */ - if((new = (struct mygroup *)malloc(sizeof(struct mygroup))) == NULL) { + if((g_new = (struct mygroup *)malloc(sizeof(struct mygroup))) == NULL) { radlog(L_ERR, "HASH: (buildGrplist) Out of memory!"); - return -1; + unix_freepwcache(cache); + return NULL; } - memset((struct mygroup*)new, 0, sizeof(struct mygroup)); + memset((struct mygroup*)g_new, 0, sizeof(struct mygroup)); /* copy grp entries to my structure */ len = strlen(grp->gr_name); - if((new->gr_name = (char *)malloc(len+1)) == NULL) { + if((g_new->gr_name = (char *)malloc(len+1)) == NULL) { radlog(L_ERR, "HASH: (buildGrplist) Out of memory!"); - return -1; + free(g_new); + unix_freepwcache(cache); + return NULL; } - strncpy(new->gr_name, grp->gr_name, len); - new->gr_name[len] = '\0'; + strncpy(g_new->gr_name, grp->gr_name, len); + g_new->gr_name[len] = '\0'; len = strlen(grp->gr_passwd); - if((new->gr_passwd= (char *)malloc(len+1)) == NULL) { + if((g_new->gr_passwd= (char *)malloc(len+1)) == NULL) { radlog(L_ERR, "HASH: (buildGrplist) Out of memory!"); - return -1; + free(g_new->gr_name); + free(g_new); + unix_freepwcache(cache); + return NULL; } - strncpy(new->gr_passwd, grp->gr_passwd, len); - new->gr_passwd[len] = '\0'; + strncpy(g_new->gr_passwd, grp->gr_passwd, len); + g_new->gr_passwd[len] = '\0'; - new->gr_gid = grp->gr_gid; + g_new->gr_gid = grp->gr_gid; /* Allocate space for user list, as much as I hate doing groups * that way. */ for(member = grp->gr_mem; *member!=NULL; member++); len = member - grp->gr_mem; - if((new->gr_mem = (char **)malloc((len+1)*sizeof(char **))) == NULL) { + if((g_new->gr_mem = (char **)malloc((len+1)*sizeof(char **))) == NULL) { radlog(L_ERR, "HASH: (buildGrplist) Out of memory!"); - return -1; + free(g_new->gr_passwd); + free(g_new->gr_name); + free(g_new); + unix_freepwcache(cache); + return NULL; } /* Now go back and copy individual users into it */ for(member = grp->gr_mem; *member; member++) { len2 = strlen(*member); idx = member - grp->gr_mem; - if((new->gr_mem[idx] = (char *)malloc(len2+1)) == NULL) { + if((g_new->gr_mem[idx] = (char *)malloc(len2+1)) == NULL) { radlog(L_ERR, "HASH: (buildGrplist) Out of memory!"); - return -1; + for(--idx;idx>=0;--idx) { + free(g_new->gr_mem[idx]); + } + free(g_new->gr_passwd); + free(g_new->gr_name); + free(g_new); + unix_freepwcache(cache); + return NULL; } - strncpy(new->gr_mem[idx], *member, len2); - new->gr_mem[idx][len2] = '\0'; + strncpy(g_new->gr_mem[idx], *member, len2); + g_new->gr_mem[idx][len2] = '\0'; } /* Make sure last entry in user list is 0 so we can loop thru it */ - new->gr_mem[len] = 0; + g_new->gr_mem[len] = 0; /* Insert at beginning of list */ - new->next = grphead; - grphead = new; + g_new->next = cache->grphead; + cache->grphead = g_new; numread++; } @@ -380,14 +386,56 @@ int unix_buildGrpList(void) { radlog(L_INFO, "HASH: Stored %d entries from /etc/group", numread); - return 0; + return cache; +} + +void unix_freepwcache(struct pwcache *cache) +{ + int hashindex; + struct mypasswd *cur, *next; + + struct mygroup *g_cur, *g_next; + char **member; + + for(hashindex=0; hashindexhashtable[hashindex]) { + cur = cache->hashtable[hashindex]; + while(cur) { + next = cur->next; + free(cur->pw_name); + free(cur->pw_passwd); + free(cur->pw_gecos); + free(cur); + cur = next; + } + } + } + + g_cur = cache->grphead; + + while(g_cur) { + g_next = g_cur->next; + + /* Free name, name, member list */ + for(member = g_cur->gr_mem; *member; member++) { + free(*member); + } + free(g_cur->gr_mem); + free(g_cur->gr_name); + free(g_cur->gr_passwd); + free(g_cur); + g_cur = g_next; + } + + free(cache); } /* * Looks up user in hashtable. If user can't be found, returns 0. * Otherwise returns a pointer to the structure for the user */ -static struct mypasswd *findHashUser(const char *user) { +static struct mypasswd *findHashUser(struct pwcache *cache, const char *user) +{ struct mypasswd *cur; int idx; @@ -395,7 +443,7 @@ static struct mypasswd *findHashUser(const char *user) { /* first hash the username and get the index into the hashtable */ idx = hashUserName(user); - cur = hashtable[idx]; + cur = cache->hashtable[idx]; while((cur != NULL) && (strcmp(cur->pw_name, user))) { cur = cur->next; @@ -411,11 +459,12 @@ static struct mypasswd *findHashUser(const char *user) { } /* Stores the username sent into the hashtable */ -static int storeHashUser(struct mypasswd *new, int idx) { +static int storeHashUser(struct pwcache *cache, struct mypasswd *new, int idx) +{ /* store new record at beginning of list */ - new->next = hashtable[idx]; - hashtable[idx] = new; + new->next = cache->hashtable[idx]; + cache->hashtable[idx] = new; return 1; } @@ -438,7 +487,9 @@ static int hashUserName(const char *s) { * return -1 on failure * return -2 on error (let caller fall back to old method) */ -int H_unix_pass(char *name, char *passwd, VALUE_PAIR **reply_items) { +int H_unix_pass(struct pwcache *cache, char *name, char *passwd, + VALUE_PAIR **reply_items) +{ struct mypasswd *pwd; char *encrypted_pass; char *encpw; @@ -446,7 +497,7 @@ int H_unix_pass(char *name, char *passwd, VALUE_PAIR **reply_items) { /* * Get encrypted password from password file */ - if ((pwd = findHashUser(name)) == NULL) { + if ((pwd = findHashUser(cache, name)) == NULL) { /* Default to old way if user isn't hashed */ return -2; } @@ -508,18 +559,19 @@ int H_unix_pass(char *name, char *passwd, VALUE_PAIR **reply_items) { * return -2 on error (let caller fall back to old method), * -1 on match fail, or 0 on success */ -int H_groupcmp(VALUE_PAIR *check, char *username) { +int H_groupcmp(struct pwcache *cache, VALUE_PAIR *check, char *username) +{ struct mypasswd *pwd; struct mygroup *cur; char **member; /* get the user from the hash */ - if (!(pwd = findHashUser(username))) + if (!(pwd = findHashUser(cache, username))) return -2; /* let's find this group */ - if(grphead) { - cur = grphead; + if(cache->grphead) { + cur = cache->grphead; while((cur) && (strcmp(cur->gr_name, (char *)check->strvalue))){ cur = cur->next; } diff --git a/src/modules/rlm_unix/cache.h b/src/modules/rlm_unix/cache.h index 6e696ff..a8bc5af 100644 --- a/src/modules/rlm_unix/cache.h +++ b/src/modules/rlm_unix/cache.h @@ -32,9 +32,15 @@ struct mygroup { struct mygroup *next; /* next */ }; +struct pwcache { + struct mypasswd *hashtable[HASHTABLESIZE]; + struct mygroup *grphead; +}; + /* Function prototypes */ -int unix_buildHashTable(const char *passwd_file, const char *shadow_file); -int unix_buildGrpList(void); -int unix_hashradutmp(void); -int H_unix_pass(char *name, char *passwd, VALUE_PAIR **reply_items); -int H_groupcmp(VALUE_PAIR *check, char *username); +struct pwcache *unix_buildpwcache(const char *passwd_file, + const char *shadow_file); +int H_unix_pass(struct pwcache *cache, char *name, char *passwd, + VALUE_PAIR **reply_items); +int H_groupcmp(struct pwcache *cache, VALUE_PAIR *check, char *username); +void unix_freepwcache(struct pwcache *cache); diff --git a/src/modules/rlm_unix/rlm_unix.c b/src/modules/rlm_unix/rlm_unix.c index c3311e4..16ff9f0 100644 --- a/src/modules/rlm_unix/rlm_unix.c +++ b/src/modules/rlm_unix/rlm_unix.c @@ -44,24 +44,45 @@ static char trans[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; #define ENC(c) trans[c] -/* - * Cache the password by default. - */ -static int cache_passwd = TRUE; -static char *passwd_file = NULL; -static char *shadow_file = NULL; -static char *group_file = NULL; +struct unix_instance { + int cache_passwd; + char *passwd_file; + char *shadow_file; + char *group_file; + int usegroup; + struct pwcache *cache; +}; + +static struct unix_instance config; static CONF_PARSER module_config[] = { - { "cache", PW_TYPE_BOOLEAN, &cache_passwd, "yes" }, - { "passwd", PW_TYPE_STRING_PTR, &passwd_file, NULL }, - { "shadow", PW_TYPE_STRING_PTR, &shadow_file, NULL }, - { "group", PW_TYPE_STRING_PTR, &group_file, NULL }, + /* + * Cache the password by default. + */ + { "cache", PW_TYPE_BOOLEAN, &config.cache_passwd, "yes" }, + { "passwd", PW_TYPE_STRING_PTR, &config.passwd_file, NULL }, + { "shadow", PW_TYPE_STRING_PTR, &config.shadow_file, NULL }, + { "group", PW_TYPE_STRING_PTR, &config.group_file, NULL }, + { "usegroup", PW_TYPE_BOOLEAN, &config.usegroup, "no" }, { NULL, -1, NULL, NULL } /* end the list */ }; /* + * groupcmp is part of autz. But it uses the data from an auth instance. So + * here is where it gets it. By default this will be the first configured + * auth instance. That can be changed by putting "usegroup = yes" inside an + * auth instance to explicitly bind all Group checks to it. + */ + +/* binds "Group=" to an instance (a particular passwd file) */ +static struct unix_instance *group_inst; + +/* Tells if the above binding was explicit (usegroup=yes specified in config + * file) or not ("Group=" was bound to the first instance of rlm_unix */ +static int group_inst_explicit; + +/* * The Group = handler. */ static int groupcmp(VALUE_PAIR *request, VALUE_PAIR *check, @@ -74,9 +95,15 @@ static int groupcmp(VALUE_PAIR *request, VALUE_PAIR *check, int retval; check_pairs = check_pairs; reply_pairs = reply_pairs; + if (!group_inst) { + radlog(L_ERR, "groupcmp: no group list known."); + return 1; + } + username = (char *)request->strvalue; - if (cache_passwd && (retval = H_groupcmp(check, username)) != -2) + if (group_inst->cache_passwd && + (retval = H_groupcmp(group_inst->cache, check, username)) != -2) return retval; if ((pwd = getpwnam(username)) == NULL) @@ -102,6 +129,8 @@ static int groupcmp(VALUE_PAIR *request, VALUE_PAIR *check, */ static int unix_init(void) { + /* FIXME - delay these until a group file has been read so we know + * groupcmp can actually do something */ paircompare_register(PW_GROUP, PW_USER_NAME, groupcmp); #ifdef PW_GROUP_NAME /* compat */ paircompare_register(PW_GROUP_NAME, PW_USER_NAME, groupcmp); @@ -111,44 +140,78 @@ static int unix_init(void) static int unix_instantiate(CONF_SECTION *conf, void **instance) { - /* - * Not yet multiple-instance-aware. groupcmp is a real - * obstacle. - */ - static int alreadydone=0; - - if (alreadydone) { - radlog(L_ERR, - "rlm_unix: can't handle multiple authentication instances"); - return -1; + *instance = malloc(sizeof(struct unix_instance)); + if(!*instance) { + return -1; } + +#define inst ((struct unix_instance *)*instance) if (cf_section_parse(conf, module_config) < 0) { + free(*instance); return -1; } - if (cache_passwd) { + /* + * Copy the configuration into the instance data + */ + inst->cache_passwd = config.cache_passwd; + inst->passwd_file = config.passwd_file; + inst->shadow_file = config.shadow_file; + inst->group_file = config.group_file; + inst->usegroup = config.usegroup; + config.passwd_file = NULL; + config.shadow_file = NULL; + config.group_file = NULL; + + if (inst->cache_passwd) { radlog(L_INFO, "HASH: Reinitializing hash structures " "and lists for caching..."); - if (unix_buildHashTable(passwd_file, shadow_file) < 0) { + if ((inst->cache = unix_buildpwcache(inst->passwd_file, + inst->shadow_file))==NULL) + { radlog(L_ERR, "HASH: unable to create user " "hash table. disable caching and run debugs"); - return -1; } - if (unix_buildGrpList() < 0) { - radlog(L_ERR, "HASH: unable to cache groups file. " - "disable caching and run debugs"); - return -1; + } else { + inst->cache = NULL; + } + + if (inst->usegroup) { + if (group_inst_explicit) { + radlog(L_ERR, "Only one group list may be active"); + } else { + group_inst = inst; + group_inst_explicit = 1; } + } else if (!group_inst) { + group_inst = inst; } +#undef inst - alreadydone = 1; - *instance = 0; return 0; } /* * Detach. */ +static int unix_detach(void *instance) +{ +#define inst ((struct unix_instance *)instance) + if (group_inst == inst) { + group_inst = NULL; + group_inst_explicit = 0; + } + free(inst->passwd_file); + free(inst->shadow_file); + free(inst->group_file); + if (inst->cache) { + unix_freepwcache(inst->cache); + } +#undef inst + free(instance); + return 0; +} + static int unix_destroy(void) { paircompare_unregister(PW_GROUP, groupcmp); @@ -165,6 +228,7 @@ static int unix_destroy(void) */ static int unix_authenticate(void *instance, REQUEST *request) { +#define inst ((struct unix_instance *)instance) char *name, *passwd; struct passwd *pwd; char *encpw; @@ -215,7 +279,8 @@ static int unix_authenticate(void *instance, REQUEST *request) name = (char *)request->username->strvalue; passwd = (char *)request->password->strvalue; - if (cache_passwd && (ret = H_unix_pass(name, passwd, &request->reply->vps)) != -2) + if (inst->cache_passwd && + (ret = H_unix_pass(inst->cache, name, passwd, &request->reply->vps)) != -2) return (ret == 0) ? RLM_MODULE_OK : RLM_MODULE_REJECT; #ifdef OSFC2 @@ -317,6 +382,7 @@ static int unix_authenticate(void *instance, REQUEST *request) return RLM_MODULE_REJECT; return RLM_MODULE_OK; +#undef inst } /* @@ -514,7 +580,7 @@ module_t rlm_unix = { unix_authenticate, /* authentication */ NULL, /* preaccounting */ unix_accounting, /* accounting */ - NULL, /* detach */ + unix_detach, /* detach */ unix_destroy, /* destroy */ }; -- 2.1.4