Ejemplo n.º 1
0
int main(int argc, char **argv)
{
  PAIRLIST *list;

  DBUG_PUSH("d:t");

  if(!(list = pairlist_init(&compare_string_ci_eq, &compare_string_ci_eq)))
  {
    fprintf(stderr, "Couldn't init pairlist\n");
    return 1;
  }

  pairlist_add(list, "name", "Jeremy Cole");
  pairlist_add(list, "age", "23");
  pairlist_add(list, "height", "6'1\"");

  printf("name=%s age=%s height=%s\n",
	 pairlist_get_value(list, "name"),
	 pairlist_get_value(list, "age"),
	 pairlist_get_value(list, "height"));

  pairlist_dump(list);

  pairlist_free(list);

  return 0;
}
Ejemplo n.º 2
0
/*
 *	Clean up.
 */
static int attr_filter_detach(void *instance)
{
        struct attr_filter_instance *inst = instance;
        pairlist_free(&inst->attrs);
        free(inst->attrsfile);
        free(inst);
	return 0;
}
Ejemplo n.º 3
0
int rad_load_credentials(TALLOC_CTX *ctx, ikev2_ctx *i2,char *filename,char *authtype_name)
{
	rad_assert(i2 && filename && authtype_name);
	int authtype;

	authtype=AuthtypeFromName(authtype_name);
	if(authtype==-1) {
	ERROR(IKEv2_LOG_PREFIX "Unsupported 'default_auth_type' value (%s), using both",authtype_name);
	authtype=IKEv2_AUTH_BOTH;
	}

	PAIR_LIST *users=NULL;
	if(getusersfile(ctx, filename,&users,"no")!=0) {
	ERROR(IKEv2_LOG_PREFIX "Error while loading %s userfile",filename);
	return -1;
	}
	PAIR_LIST *tusers=users;
	while(tusers) {
	if(strcmp(tusers->name,"DEFAULT")) {
		rad_update_shared_seclist(&i2->sslist,tusers->name,tusers->check,authtype);
	}
	tusers=tusers->next;
	}
	pairlist_free(&users);
	//print sslist
//	struct sharedSecList *sslist=i2->sslist;
//	while(sslist) {
//	ERROR("sslist:id=%s",sslist->id);
//	ERROR("sslist:idlen=%d",sslist->idlen);
//	ERROR("sslist:pwd=%s",sslist->pwd);
//	ERROR("sslist:pwdlen=%d",sslist->pwdlen);
//	ERROR("sslist:idtype= %d",sslist->idtype);
//	ERROR("sslist:authtype=%d",sslist->authtype);
//	sslist=sslist->next;
//	}
	return 0;


}
Ejemplo n.º 4
0
static int getusersfile(TALLOC_CTX *ctx, char const *filename, fr_hash_table_t **pht,
			char *compat_mode_str)
{
	int rcode;
	PAIR_LIST *users = NULL;
	PAIR_LIST *entry, *next;
	fr_hash_table_t *ht, *tailht;
	int order = 0;

	if (!filename) {
		*pht = NULL;
		return 0;
	}

	rcode = pairlist_read(ctx, filename, &users, 1);
	if (rcode < 0) {
		return -1;
	}

	/*
	 *	Walk through the 'users' file list, if we're debugging,
	 *	or if we're in compat_mode.
	 */
	if ((debug_flag) ||
	    (strcmp(compat_mode_str, "cistron") == 0)) {
		VALUE_PAIR *vp;
		int compat_mode = false;

		if (strcmp(compat_mode_str, "cistron") == 0) {
			compat_mode = true;
		}

		entry = users;
		while (entry) {
			vp_cursor_t cursor;
			if (compat_mode) {
				DEBUG("[%s]:%d Cistron compatibility checks for entry %s ...",
						filename, entry->lineno,
						entry->name);
			}

			/*
			 *	Look for improper use of '=' in the
			 *	check items.  They should be using
			 *	'==' for on-the-wire RADIUS attributes,
			 *	and probably ':=' for server
			 *	configuration items.
			 */
			for (vp = paircursor(&cursor, &entry->check); vp; vp = pairnext(&cursor)) {
				/*
				 *	Ignore attributes which are set
				 *	properly.
				 */
				if (vp->op != T_OP_EQ) {
					continue;
				}

				/*
				 *	If it's a vendor attribute,
				 *	or it's a wire protocol,
				 *	ensure it has '=='.
				 */
				if ((vp->da->vendor != 0) ||
						(vp->da->attr < 0x100)) {
					if (!compat_mode) {
						WDEBUG("[%s]:%d Changing '%s =' to '%s =='\n\tfor comparing RADIUS attribute in check item list for user %s",
								filename, entry->lineno,
								vp->da->name, vp->da->name,
								entry->name);
					} else {
						DEBUG("\tChanging '%s =' to '%s =='",
								vp->da->name, vp->da->name);
					}
					vp->op = T_OP_CMP_EQ;
					continue;
				}

				/*
				 *	Cistron Compatibility mode.
				 *
				 *	Re-write selected attributes
				 *	to be '+=', instead of '='.
				 *
				 *	All others get set to '=='
				 */
				if (compat_mode) {
					/*
					 *	Non-wire attributes become +=
					 *
					 *	On the write attributes
					 *	become ==
					 */
					if ((vp->da->attr >= 0x100) &&
							(vp->da->attr <= 0xffff) &&
							(vp->da->attr != PW_HINT) &&
							(vp->da->attr != PW_HUNTGROUP_NAME)) {
						DEBUG("\tChanging '%s =' to '%s +='", vp->da->name, vp->da->name);
						
						vp->op = T_OP_ADD;
					} else {
						DEBUG("\tChanging '%s =' to '%s =='", vp->da->name, vp->da->name);
						
						vp->op = T_OP_CMP_EQ;
					}
				}

			} /* end of loop over check items */

			/*
			 *	Look for server configuration items
			 *	in the reply list.
			 *
			 *	It's a common enough mistake, that it's
			 *	worth doing.
			 */
			for (vp = paircursor(&cursor, &entry->reply); vp; vp = pairnext(&cursor)) {
				/*
				 *	If it's NOT a vendor attribute,
				 *	and it's NOT a wire protocol
				 *	and we ignore Fall-Through,
				 *	then bitch about it, giving a
				 *	good warning message.
				 */
				 if ((vp->da->vendor == 0) &&
					(vp->da->attr > 0xff) &&
					(vp->da->attr > 1000)) {
					WDEBUG("[%s]:%d Check item \"%s\"\n"
					       "\tfound in reply item list for user \"%s\".\n"
					       "\tThis attribute MUST go on the first line"
					       " with the other check items", filename, entry->lineno, vp->da->name,
					       entry->name);
				}
			}

			entry = entry->next;
		}

	}

	ht = fr_hash_table_create(pairlist_hash, pairlist_cmp,
				    my_pairlist_free);
	if (!ht) {
		pairlist_free(&users);
		return -1;
	}

	tailht = fr_hash_table_create(pairlist_hash, pairlist_cmp,
					NULL);
	if (!tailht) {
		fr_hash_table_free(ht);
		pairlist_free(&users);
		return -1;
	}

	/*
	 *	Now that we've read it in, put the entries into a hash
	 *	for faster access.
	 */
	for (entry = users; entry != NULL; entry = next) {
		PAIR_LIST *tail;

		next = entry->next;
		entry->next = NULL;
		entry->order = order++;

		/*
		 *	Insert it into the hash table, and remember
		 *	the tail of the linked list.
		 */
		tail = fr_hash_table_finddata(tailht, entry);
		if (!tail) {
			/*
			 *	Insert it into the head & tail.
			 */
			if (!fr_hash_table_insert(ht, entry) ||
			    !fr_hash_table_insert(tailht, entry)) {
				pairlist_free(&next);
				fr_hash_table_free(ht);
				fr_hash_table_free(tailht);
				return -1;
			}
		} else {
			tail->next = entry;
			if (!fr_hash_table_replace(tailht, entry)) {
				pairlist_free(&next);
				fr_hash_table_free(ht);
				fr_hash_table_free(tailht);
				return -1;
			}
		}
	}

	fr_hash_table_free(tailht);
	*pht = ht;

	return 0;
}
Ejemplo n.º 5
0
static void my_pairlist_free(void *data)
{
	PAIR_LIST *pl = data;

	pairlist_free(&pl);
}
Ejemplo n.º 6
0
/*
 *	Read the users, huntgroups or hints file.
 *	Return a PAIR_LIST.
 */
int pairlist_read(TALLOC_CTX *ctx, char const *file, PAIR_LIST **list, int complain)
{
	FILE *fp;
	int mode = FIND_MODE_NAME;
	char entry[256];
	char buffer[8192];
	char const *ptr;
	VALUE_PAIR *check_tmp;
	VALUE_PAIR *reply_tmp;
	PAIR_LIST *pl = NULL, *t;
	PAIR_LIST **last = &pl;
	int lineno = 0;
	int old_lineno = 0;
	FR_TOKEN parsecode;
	char newfile[8192];

	DEBUG2("reading pairlist file %s", file);

	/*
	 *	Open the file.  The error message should be a little
	 *	more useful...
	 */
	if ((fp = fopen(file, "r")) == NULL) {
		if (!complain)
			return -1;
		ERROR("Couldn't open %s for reading: %s", file, fr_syserror(errno));
		return -1;
	}

	parsecode = T_EOL;

	/*
	 *	Read the entire file into memory for speed.
	 */
	while(fgets(buffer, sizeof(buffer), fp) != NULL) {
		lineno++;
		if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
			fclose(fp);
			ERROR("%s[%d]: line too long", file, lineno);
			pairlist_free(&pl);
			return -1;
		}
		if (buffer[0] == '#' || buffer[0] == '\n') continue;

		/*
		 *	If the line contains nothing but whitespace,
		 *	ignore it.
		 */
		ptr = buffer;
		while (isspace((int) *ptr)) ptr++;
		if (*ptr == '\0') continue;

parse_again:
		if(mode == FIND_MODE_NAME) {
			/*
			 *	Find the entry starting with the users name
			 */
			if (isspace((int) buffer[0]))  {
				if (parsecode != T_EOL) {
					ERROR("%s[%d]: Unexpected trailing comma for entry %s",
					       file, lineno, entry);
					fclose(fp);
					return -1;
				}
				continue;
			}

			ptr = buffer;
			getword(&ptr, entry, sizeof(entry));

			/*
			 *	Include another file if we see
			 *	$INCLUDE filename
			 */
			if (strcasecmp(entry, "$INCLUDE") == 0) {
				while(isspace((int) *ptr))
					ptr++;

				/*
				 *	If it's an absolute pathname,
				 *	then use it verbatim.
				 *
				 *	If not, then make the $include
				 *	files *relative* to the current
				 *	file.
				 */
				if (FR_DIR_IS_RELATIVE(ptr)) {
					char *p;

					strlcpy(newfile, file,
						sizeof(newfile));
					p = strrchr(newfile, FR_DIR_SEP);
					if (!p) {
						p = newfile + strlen(newfile);
						*p = FR_DIR_SEP;
					}
					getword(&ptr, p + 1,
						sizeof(newfile) - 1 - (p - newfile));
				} else {
					getword(&ptr, newfile,
						sizeof(newfile));
				}

				t = NULL;

				if (pairlist_read(ctx, newfile, &t, 0) != 0) {
					pairlist_free(&pl);
					ERROR("%s[%d]: Could not open included file %s: %s",
					      file, lineno, newfile, fr_syserror(errno));
					fclose(fp);
					return -1;
				}
				*last = t;

				/*
				 *	t may be NULL, it may have one
				 *	entry, or it may be a linked list
				 *	of entries.  Go to the end of the
				 *	list.
				 */
				while (*last)
					last = &((*last)->next);
				continue;
			}

			/*
			 *	Parse the check values
			 */
			check_tmp = NULL;
			reply_tmp = NULL;
			old_lineno = lineno;
			parsecode = userparse(ctx, ptr, &check_tmp);
			if (parsecode == T_OP_INVALID) {
				pairlist_free(&pl);
				ERROR("%s[%d]: Parse error (check) for entry %s: %s",
					file, lineno, entry, fr_strerror());
				fclose(fp);
				return -1;
			} else if (parsecode == T_COMMA) {
				ERROR("%s[%d]: Unexpected trailing comma in check item list for entry %s",
				       file, lineno, entry);
				fclose(fp);
				return -1;
			}
			mode = FIND_MODE_REPLY;
			parsecode = T_COMMA;
		}
		else {
			if(*buffer == ' ' || *buffer == '\t') {
				if (parsecode != T_COMMA) {
					ERROR("%s[%d]: Syntax error: Previous line is missing a trailing comma for entry %s",
					       file, lineno, entry);
					fclose(fp);
					return -1;
				}

				/*
				 *	Parse the reply values
				 */
				parsecode = userparse(ctx, buffer, &reply_tmp);
				/* valid tokens are 1 or greater */
				if (parsecode < 1) {
					pairlist_free(&pl);
					ERROR("%s[%d]: Parse error (reply) for entry %s: %s",
					       file, lineno, entry, fr_strerror());
					fclose(fp);
					return -1;
				}
			} else {
				/*
				 *	Done with this entry...
				 */
				MEM(t = talloc_zero(ctx, PAIR_LIST));

				t->check = check_tmp;
				t->reply = reply_tmp;
				t->lineno = old_lineno;
				check_tmp = NULL;
				reply_tmp = NULL;

				t->name = talloc_typed_strdup(t, entry);

				*last = t;
				last = &(t->next);

				mode = FIND_MODE_NAME;
				if (buffer[0] != 0)
					goto parse_again;
			}
		}
	}
	/*
	 *	Make sure that we also read the last line of the file!
	 */
	if (mode == FIND_MODE_REPLY) {
		buffer[0] = 0;
		goto parse_again;
	}
	fclose(fp);

	*list = pl;
	return 0;
}
Ejemplo n.º 7
0
/*
 *	Read the users, huntgroups or hints file.
 *	Return a PAIR_LIST.
 */
int pairlist_read(TALLOC_CTX *ctx, char const *file, PAIR_LIST **list, int complain)
{
	FILE *fp;
	int mode = FIND_MODE_NAME;
	char entry[256];
	char buffer[8192];
	char const *ptr;
	VALUE_PAIR *check_tmp = NULL;
	VALUE_PAIR *reply_tmp = NULL;
	PAIR_LIST *pl = NULL, *t;
	PAIR_LIST **last = &pl;
	int lineno = 0;
	int entry_lineno = 0;
	FR_TOKEN parsecode;
#ifdef HAVE_REGEX_H
	VALUE_PAIR *vp;
	vp_cursor_t cursor;
#endif
	char newfile[8192];

	DEBUG2("reading pairlist file %s", file);

	/*
	 *	Open the file.  The error message should be a little
	 *	more useful...
	 */
	if ((fp = fopen(file, "r")) == NULL) {
		if (!complain)
			return -1;
		ERROR("Couldn't open %s for reading: %s",
				file, fr_syserror(errno));
		return -1;
	}

	/*
	 *	Read the entire file into memory for speed.
	 */
	while (fgets(buffer, sizeof(buffer), fp) != NULL) {
		lineno++;

		if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
			fclose(fp);
			ERROR("%s[%d]: line too long", file, lineno);
			pairlist_free(&pl);
			return -1;
		}

		/*
		 *	If the line contains nothing but whitespace,
		 *	ignore it.
		 */
		ptr = buffer;
		while (isspace((int) *ptr)) ptr++;

		if (*ptr == '#' || *ptr == '\n' || !*ptr) continue;

parse_again:
		if (mode == FIND_MODE_NAME) {
			/*
			 *	The user's name MUST be the first text on the line.
			 */
			if (isspace((int) buffer[0]))  {
				ERROR("%s[%d]: Entry does not begin with a user name",
				      file, lineno);
				fclose(fp);
				return -1;
			}

			/*
			 *	Get the name.
			 */		      
			ptr = buffer;
			getword(&ptr, entry, sizeof(entry), false);
			entry_lineno = lineno;

			/*
			 *	Include another file if we see
			 *	$INCLUDE filename
			 */
			if (strcasecmp(entry, "$INCLUDE") == 0) {
				while (isspace((int) *ptr)) ptr++;

				/*
				 *	If it's an absolute pathname,
				 *	then use it verbatim.
				 *
				 *	If not, then make the $include
				 *	files *relative* to the current
				 *	file.
				 */
				if (FR_DIR_IS_RELATIVE(ptr)) {
					char *p;

					strlcpy(newfile, file,
						sizeof(newfile));
					p = strrchr(newfile, FR_DIR_SEP);
					if (!p) {
						p = newfile + strlen(newfile);
						*p = FR_DIR_SEP;
					}
					getword(&ptr, p + 1, sizeof(newfile) - 1 - (p - newfile), false);
				} else {
					getword(&ptr, newfile, sizeof(newfile), false);
				}

				t = NULL;

				if (pairlist_read(ctx, newfile, &t, 0) != 0) {
					pairlist_free(&pl);
					ERROR("%s[%d]: Could not open included file %s: %s",
					       file, lineno, newfile, fr_syserror(errno));
					fclose(fp);
					return -1;
				}
				*last = t;

				/*
				 *	t may be NULL, it may have one
				 *	entry, or it may be a linked list
				 *	of entries.  Go to the end of the
				 *	list.
				 */
				while (*last)
					last = &((*last)->next);
				continue;
			} /* $INCLUDE ... */

			/*
			 *	Parse the check values
			 */
			rad_assert(check_tmp == NULL);
			rad_assert(reply_tmp == NULL);
			parsecode = fr_pair_list_afrom_str(ctx, ptr, &check_tmp);
			if (parsecode == T_INVALID) {
				pairlist_free(&pl);
				ERROR("%s[%d]: Parse error (check) for entry %s: %s",
					file, lineno, entry, fr_strerror());
				fclose(fp);
				return -1;
			}

			if (parsecode != T_EOL) {
				pairlist_free(&pl);
				talloc_free(check_tmp);
				ERROR("%s[%d]: Invalid text after check attributes for entry %s",
				      file, lineno, entry);
				fclose(fp);
				return -1;
			}

#ifdef HAVE_REGEX_H
			/*
			 *	Do some more sanity checks.
			 */
			for (vp = fr_cursor_init(&cursor, &check_tmp);
			     vp;
			     vp = fr_cursor_next(&cursor)) {
				if (((vp->op == T_OP_REG_EQ) ||
				     (vp->op == T_OP_REG_NE)) &&
				    (vp->da->type != PW_TYPE_STRING)) {
					pairlist_free(&pl);
					talloc_free(check_tmp);
					ERROR("%s[%d]: Cannot use regular expressions for non-string attributes in entry %s",
					      file, lineno, entry);
					fclose(fp);
					return -1;
				}
			}
#endif

			/*
			 *	The reply MUST be on a new line.
			 */
			mode = FIND_MODE_WANT_REPLY;
			continue;
		}

		/*
		 *	We COULD have a reply, OR we could have a new entry.
		 */
		if (mode == FIND_MODE_WANT_REPLY) {
			if (!isspace((int) buffer[0])) goto create_entry;

			mode = FIND_MODE_HAVE_REPLY;
		}

		/*
		 *	mode == FIND_MODE_HAVE_REPLY
		 */

		/*
		 *	The previous line ended with a comma, and then
		 *	we have the start of a new entry!
		 */
		if (!isspace((int) buffer[0])) {
		trailing_comma:
			pairlist_free(&pl);
			talloc_free(check_tmp);
			talloc_free(reply_tmp);
			ERROR("%s[%d]: Invalid comma after the reply attributes.  Please delete it.",
			      file, lineno);
			fclose(fp);
			return -1;
		}

		/*
		 *	Parse the reply values.  If there's a trailing
		 *	comma, keep parsing the reply values.
		 */
		parsecode = fr_pair_list_afrom_str(ctx, buffer, &reply_tmp);
		if (parsecode == T_COMMA) {
			continue;
		}

		/*
		 *	We expect an EOL.  Anything else is an error.
		 */
		if (parsecode != T_EOL) {
			pairlist_free(&pl);
			talloc_free(check_tmp);
			talloc_free(reply_tmp);
			ERROR("%s[%d]: Parse error (reply) for entry %s: %s",
			      file, lineno, entry, fr_strerror());
			fclose(fp);
			return -1;
		}

	create_entry:
		/*
		 *	Done with this entry...
		 */
		MEM(t = talloc_zero(ctx, PAIR_LIST));

		if (check_tmp) fr_pair_steal(t, check_tmp);
		if (reply_tmp) fr_pair_steal(t, reply_tmp);

		t->check = check_tmp;
		t->reply = reply_tmp;
		t->lineno = entry_lineno;
		check_tmp = NULL;
		reply_tmp = NULL;

		t->name = talloc_typed_strdup(t, entry);

		*last = t;
		last = &(t->next);

		/*
		 *	Look for a name.  If we came here because
		 *	there were no reply attributes, then re-parse
		 *	the current line, instead of reading another one.
		 */
		mode = FIND_MODE_NAME;
		if (feof(fp)) break;
		if (!isspace((int) buffer[0])) goto parse_again;
	}

	/*
	 *	We're at EOF.  If we're supposed to read more, that's
	 *	an error.
	 */
	if (mode == FIND_MODE_HAVE_REPLY) goto trailing_comma;

	/*
	 *	We had an entry, but no reply attributes.  That's OK.
	 */
	if (mode == FIND_MODE_WANT_REPLY) goto create_entry;

	/*
	 *	Else we were looking for an entry.  We didn't get one
	 *	because we were at EOF, so that's OK.
	 */

	fclose(fp);

	*list = pl;
	return 0;
}
Ejemplo n.º 8
0
static int getusersfile(TALLOC_CTX *ctx, char const *filename, rbtree_t **ptree)
{
	int rcode;
	VALUE_PAIR *vp;
	PAIR_LIST *users = NULL;
	PAIR_LIST *entry, *next;
	PAIR_LIST *user_list, *default_list, **default_tail;
	rbtree_t *tree;

	if (!filename) {
		*ptree = NULL;
		return 0;
	}

	rcode = pairlist_read(ctx, dict_radius, filename, &users, 1);
	if (rcode < 0) {
		return -1;
	}

	/*
	 *	Walk through the 'users' file list
	 */
	entry = users;
	while (entry) {
		fr_cursor_t cursor;

		/*
		 *	Look for improper use of '=' in the
		 *	check items.  They should be using
		 *	'==' for on-the-wire RADIUS attributes,
		 *	and probably ':=' for server
		 *	configuration items.
		 */
		for (vp = fr_cursor_init(&cursor, &entry->check);
		     vp;
		     vp = fr_cursor_next(&cursor)) {
			/*
			 *	Ignore attributes which are set
			 *	properly.
			 */
			if (vp->op != T_OP_EQ) {
				continue;
			}

			/*
			 *	If it's a vendor attribute,
			 *	or it's a wire protocol,
			 *	ensure it has '=='.
			 */
			if ((fr_dict_vendor_num_by_da(vp->da) != 0) ||
			    (vp->da->attr < 0x100)) {
				WARN("[%s]:%d Changing '%s =' to '%s =='\n\tfor comparing RADIUS attribute in check item list for user %s",
				     filename, entry->lineno,
				     vp->da->name, vp->da->name,
				     entry->name);
				vp->op = T_OP_CMP_EQ;
				continue;
			}
		} /* end of loop over check items */

		/*
		 *	Look for server configuration items
		 *	in the reply list.
		 *
		 *	It's a common enough mistake, that it's
		 *	worth doing.
		 */
		for (vp = fr_cursor_init(&cursor, &entry->reply);
		     vp;
		     vp = fr_cursor_next(&cursor)) {
			/*
			 *	If it's NOT a vendor attribute,
			 *	and it's NOT a wire protocol
			 *	and we ignore Fall-Through,
			 *	then bitch about it, giving a
			 *	good warning message.
			 */
			 if (fr_dict_attr_is_top_level(vp->da) && (vp->da->attr > 1000)) {
				WARN("[%s]:%d Check item \"%s\"\n"
				       "\tfound in reply item list for user \"%s\".\n"
				       "\tThis attribute MUST go on the first line"
				       " with the other check items", filename, entry->lineno, vp->da->name,
				       entry->name);
			}
		}

		entry = entry->next;
	}

	tree = rbtree_create(ctx, pairlist_cmp, NULL, RBTREE_FLAG_NONE);
	if (!tree) {
		pairlist_free(&users);
		return -1;
	}

	default_list = NULL;
	default_tail = &default_list;

	/*
	 *	We've read the entries in linearly, but putting them
	 *	into an indexed data structure would be much faster.
	 *	Let's go fix that now.
	 */
	for (entry = users; entry != NULL; entry = next) {
		/*
		 *	Remove this entry from the input list.
		 */
		next = entry->next;
		entry->next = NULL;

		/*
		 *	DEFAULT entries get their own list.
		 */
		if (strcmp(entry->name, "DEFAULT") == 0) {
			if (!default_list) {
				default_list = entry;

				/*
				 *	Insert the first DEFAULT into the tree.
				 */
				if (!rbtree_insert(tree, entry)) {
				error:
					pairlist_free(&entry);
					pairlist_free(&next);
					talloc_free(tree);
					return -1;
				}

			} else {
				/*
				 *	Tack this entry onto the tail
				 *	of the DEFAULT list.
				 */
				*default_tail = entry;
			}

			default_tail = &entry->next;
			continue;
		}

		/*
		 *	Not DEFAULT, must be a normal user.
		 */
		user_list = rbtree_finddata(tree, entry);
		if (!user_list) {
			/*
			 *	Insert the first one.
			 */
			if (!rbtree_insert(tree, entry)) goto error;
		} else {
			/*
			 *	Find the tail of this list, and add it
			 *	there.
			 */
			while (user_list->next) user_list = user_list->next;

			user_list->next = entry;
		}
	}

	*ptree = tree;

	return 0;
}