diff options
Diffstat (limited to 'src/main/files.c')
-rw-r--r-- | src/main/files.c | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/src/main/files.c b/src/main/files.c new file mode 100644 index 0000000..f191393 --- /dev/null +++ b/src/main/files.c @@ -0,0 +1,361 @@ +/* + * files.c Read config files into memory. + * + * Version: $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Copyright 2000,2006 The FreeRADIUS server project + * Copyright 2000 Miquel van Smoorenburg <miquels@cistron.nl> + * Copyright 2000 Alan DeKok <aland@ox.org> + */ + +RCSID("$Id$") + +#include <freeradius-devel/radiusd.h> +#include <freeradius-devel/rad_assert.h> + +#include <sys/stat.h> + +#include <ctype.h> +#include <fcntl.h> + +/* + * Debug code. + */ +#if 0 +static void debug_pair_list(PAIR_LIST *pl) +{ + VALUE_PAIR *vp; + + while(pl) { + printf("Pair list: %s\n", pl->name); + printf("** Check:\n"); + for(vp = pl->check; vp; vp = vp->next) { + printf(" "); + fprint_attr_val(stdout, vp); + printf("\n"); + } + printf("** Reply:\n"); + for(vp = pl->reply; vp; vp = vp->next) { + printf(" "); + fprint_attr_val(stdout, vp); + printf("\n"); + } + pl = pl->next; + } +} +#endif + +/* + * Free a PAIR_LIST + */ +void pairlist_free(PAIR_LIST **pl) +{ + talloc_free(*pl); + *pl = NULL; +} + + +#define FIND_MODE_NAME 0 +#define FIND_MODE_WANT_REPLY 1 +#define FIND_MODE_HAVE_REPLY 2 + +/* + * 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 order = 0; + 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)->order = order++; + 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; + t->order = order++; + 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; +} |