* 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;
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; hashindex<HASHTABLESIZE; hashindex++) {
- if(hashtable[hashindex]) {
- cur = hashtable[hashindex];
- while(cur) {
- next = cur->next;
- 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.
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++;
/* 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);
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';
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';
/*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);
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) {
}
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;
}
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';
/* 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++;
}
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; hashindex<HASHTABLESIZE; hashindex++) {
+ if(cache->hashtable[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;
/* 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;
}
/* 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;
}
* 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;
/*
* 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;
}
* 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;
}
"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,
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)
*/
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);
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);
*/
static int unix_authenticate(void *instance, REQUEST *request)
{
+#define inst ((struct unix_instance *)instance)
char *name, *passwd;
struct passwd *pwd;
char *encpw;
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
return RLM_MODULE_REJECT;
return RLM_MODULE_OK;
+#undef inst
}
/*
unix_authenticate, /* authentication */
NULL, /* preaccounting */
unix_accounting, /* accounting */
- NULL, /* detach */
+ unix_detach, /* detach */
unix_destroy, /* destroy */
};