*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
- * Copyright 2000 The FreeRADIUS server project
+ * Copyright 2000,2006 The FreeRADIUS server project
* Copyright 2000 Miquel van Smoorenburg <miquels@cistron.nl>
* Copyright 2000 Alan DeKok <aland@ox.org>
*/
-#include "autoconf.h"
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
-#include <stdlib.h>
-#include <string.h>
-
-#ifdef HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/rad_assert.h>
#ifdef HAVE_DIRENT_H
#include <dirent.h>
#include <ctype.h>
-#include "radiusd.h"
-#include "rad_assert.h"
-#include "conffile.h"
-#include "token.h"
-#include "modules.h"
-
-static const char rcsid[] =
-"$Id$";
-
typedef enum conf_type {
CONF_ITEM_INVALID = 0,
CONF_ITEM_PAIR,
rbtree_t *section_tree; /* no jokes here */
rbtree_t *name2_tree; /* for sections of the same name2 */
rbtree_t *data_tree;
+ void *base;
+ const CONF_PARSER *variables;
};
return strcmp(one->name, two->name);
}
+
+/*
+ * Free strings we've parsed into data structures.
+ */
+static void cf_section_parse_free(void *base, const CONF_PARSER *variables)
+{
+ int i;
+
+ /*
+ * Don't automatically free the strings if we're being
+ * called from a module. This is also for clients.c,
+ * where client_free() expects to be able to free the
+ * client structure. If we moved everything to key off
+ * of the config files, we might solve some problems...
+ */
+ if (!variables) return;
+
+ /*
+ * Free up dynamically allocated string pointers.
+ */
+ for (i = 0; variables[i].name != NULL; i++) {
+ char **p;
+
+ if ((variables[i].type != PW_TYPE_STRING_PTR) &&
+ (variables[i].type != PW_TYPE_FILENAME)) {
+ continue;
+ }
+
+ /*
+ * No base struct offset, data must be the pointer.
+ * If data doesn't exist, ignore the entry, there
+ * must be something wrong.
+ */
+ if (!base) {
+ if (!variables[i].data) {
+ continue;
+ }
+
+ p = (char **) variables[i].data;;
+
+ } else if (variables[i].data) {
+ p = (char **) variables[i].data;;
+
+ } else {
+ p = (char **) (((char *)base) + variables[i].offset);
+ }
+
+ free(*p);
+ *p = NULL;
+ }
+}
+
+
/*
* Free a CONF_SECTION
*/
if (!cs || !*cs) return;
+ if ((*cs)->variables) {
+ cf_section_parse_free((*cs)->base, (*cs)->variables);
+ }
+
for (ci = (*cs)->children; ci; ci = next) {
next = ci->next;
{
CONF_SECTION *cs;
- if (name1 == NULL || !name1[0])
- name1 = "main";
+ if (!name1) return NULL;
cs = rad_malloc(sizeof(*cs));
memset(cs, 0, sizeof(*cs));
if (!up) cp = cf_pair_find(parentcs, name);
}
if (cp == NULL) {
- radlog(L_ERR, "config: No such entry %s for string %s", name, input);
+ radlog(L_ERR, "config: No such configuration item %s in section %s when expanding string \"%s\"", name,
+ cf_section_name1(cs),
+ input);
return NULL;
}
}
radlog(L_ERR, "Bad value \"%s\" for boolean variable %s", value, name);
return -1;
}
- DEBUG2(" %s: %s = %s", cs->name1, name, value);
+ if (cs->name2) {
+ DEBUG2(" %s %s: %s = %s", cs->name1, cs->name2, name, value);
+ } else {
+ DEBUG2(" %s: %s = %s", cs->name1, name, value);
+ }
break;
case PW_TYPE_INTEGER:
*(int *)data = strtol(value, 0, 0);
- DEBUG2(" %s: %s = %d",
- cs->name1, name,
- *(int *)data);
+ if (cs->name2) {
+ DEBUG2(" %s %s: %s = %d",
+ cs->name1, cs->name2, name,
+ *(int *)data);
+ } else {
+ DEBUG2(" %s: %s = %d",
+ cs->name1, name,
+ *(int *)data);
+ }
break;
case PW_TYPE_STRING_PTR:
if (!value) return -1;
}
- DEBUG2(" %s: %s = \"%s\"",
- cs->name1, name,
- value ? value : "(null)");
+ if (cs->name2) {
+ DEBUG2(" %s %s: %s = \"%s\"",
+ cs->name1, cs->name2, name,
+ value ? value : "(null)");
+ } else {
+ DEBUG2(" %s: %s = \"%s\"",
+ cs->name1, name,
+ value ? value : "(null)");
+ }
*q = value ? strdup(value) : NULL;
break;
if (!value) return -1;
}
- DEBUG2(" %s: %s = \"%s\"",
- cs->name1, name,
- value ? value : "(null)");
+ if (cs->name2) {
+ DEBUG2(" %s %s: %s = \"%s\"",
+ cs->name1, cs->name2, name,
+ value ? value : "(null)");
+ } else {
+ DEBUG2(" %s: %s = \"%s\"",
+ cs->name1, name,
+ value ? value : "(null)");
+ }
*q = value ? strdup(value) : NULL;
/*
- * And now we "stat" the file.XXX
+ * And now we "stat" the file.
*/
if (*q) {
struct stat buf;
radlog(L_ERR, "Can't find IP address for host %s", value);
return -1;
}
- DEBUG2(" %s: %s = %s IP address [%s]",
- cs->name1, name, value,
- ip_ntoh(&ipaddr, ipbuf, sizeof(ipbuf)));
+ if (cs->name2) {
+ DEBUG2(" %s %s: %s = %s IP address [%s]",
+ cs->name1, cs->name2, name, value,
+ ip_ntoh(&ipaddr, ipbuf, sizeof(ipbuf)));
+ } else {
+ DEBUG2(" %s: %s = %s IP address [%s]",
+ cs->name1, name, value,
+ ip_ntoh(&ipaddr, ipbuf, sizeof(ipbuf)));
+ }
*(uint32_t *) data = ipaddr.ipaddr.ip4addr.s_addr;
break;
radlog(L_ERR, "Can't find IPv6 address for host %s", value);
return -1;
}
- DEBUG2(" %s: %s = %s IPv6 address [%s]",
- cs->name1, name, value,
- ip_ntoh(&ipaddr, ipbuf, sizeof(ipbuf)));
+ if (cs->name2) {
+ DEBUG2(" %s %s: %s = %s IPv6 address [%s]",
+ cs->name1, cs->name2, name, value,
+ ip_ntoh(&ipaddr, ipbuf, sizeof(ipbuf)));
+ } else {
+ DEBUG2(" %s: %s = %s IPv6 address [%s]",
+ cs->name1, name, value,
+ ip_ntoh(&ipaddr, ipbuf, sizeof(ipbuf)));
+ }
memcpy(data, &ipaddr.ipaddr.ip6addr,
sizeof(ipaddr.ipaddr.ip6addr));
break;
/*
* Parse a configuration section into user-supplied variables.
*/
-int cf_section_parse(const CONF_SECTION *cs, void *base,
+int cf_section_parse(CONF_SECTION *cs, void *base,
const CONF_PARSER *variables)
{
int i;
*/
if (!subcs) continue;
- if (!variables[i].data) {
+ if (!variables[i].dflt) {
DEBUG2("Internal sanity check 1 failed in cf_section_parse");
+ cf_section_parse_free(base, variables);
return -1;
}
if (cf_section_parse(subcs, base,
(const CONF_PARSER *) variables[i].dflt) < 0) {
+ cf_section_parse_free(base, variables);
return -1;
}
continue;
data = ((char *)base) + variables[i].offset;
} else {
DEBUG2("Internal sanity check 2 failed in cf_section_parse");
+ cf_section_parse_free(base, variables);
return -1;
}
*/
if (cf_item_parse(cs, variables[i].name, variables[i].type,
data, variables[i].dflt) < 0) {
+ cf_section_parse_free(base, variables);
return -1;
}
} /* for all variables in the configuration section */
- return 0;
-}
-
-
-/*
- * Free strings we've parsed into data structures.
- */
-void cf_section_parse_free_strings(void *base, const CONF_PARSER *variables)
-{
- int i;
-
- if (!variables) return;
-
- /*
- * Free up dynamically allocated string pointers.
- */
- for (i = 0; variables[i].name != NULL; i++) {
- char **p;
-
- if ((variables[i].type != PW_TYPE_STRING_PTR) &&
- (variables[i].type != PW_TYPE_FILENAME)) {
- continue;
- }
-
- /*
- * Prefer the data, if it's there.
- * Else use the base + offset.
- */
- if (variables[i].data) {
- p = (char **) &(variables[i].data);
- } else {
- p = (char **) (((char *)base) + variables[i].offset);
- }
- free(*p);
- *p = NULL;
- }
-}
+ cs->base = base;
+ cs->variables = variables;
-
-/*
- * Used in a few places, so in one function for clarity.
- */
-static void cf_fixup_children(CONF_SECTION *cs, CONF_SECTION *is)
-{
- /*
- * Add the included conf
- * to our CONF_SECTION
- */
- if (is->children != NULL) {
- CONF_ITEM *ci;
-
- /*
- * Re-write the parent of the
- * moved children to be the
- * upper-layer section.
- */
- for (ci = is->children; ci; ci = ci->next) {
- ci->parent = cs;
- }
-
- /*
- * If there are children, then
- * move them up a layer.
- */
- if (is->children) {
- cf_item_add(cs, is->children);
- }
- is->children = NULL;
- }
- /*
- * Always free the section for the
- * $INCLUDEd file.
- */
- cf_section_free(&is);
+ return 0;
}
/*
* Read a part of the config file.
*/
-static CONF_SECTION *cf_section_read(const char *cf, int *lineno, FILE *fp,
- const char *name1, const char *name2,
- CONF_SECTION *parent)
+static int cf_section_read(const char *file, int *lineno, FILE *fp,
+ CONF_SECTION *current)
+
{
- CONF_SECTION *cs, *css;
+ CONF_SECTION *this, *css;
CONF_PAIR *cpn;
char *ptr;
const char *value;
char *cbuf = buf;
int len;
- /*
- * Ensure that the user can't add CONF_SECTIONs
- * with 'internal' names;
- */
- if ((name1 != NULL) && (name1[0] == '_')) {
- radlog(L_ERR, "%s[%d]: Illegal configuration section name",
- cf, *lineno);
- return NULL;
- }
-
- /*
- * Allocate new section.
- */
- cs = cf_section_alloc(name1, name2, parent);
- cs->item.lineno = *lineno;
+ this = current; /* add items here */
/*
* Read, checking for line continuations ('\\' at EOL)
* We've filled the buffer, and there isn't
* a CR in it. Die!
*/
- if ((len == sizeof(buf)) &&
+ if ((len == (sizeof(buf) - 1)) &&
(cbuf[len - 1] != '\n')) {
radlog(L_ERR, "%s[%d]: Line too long",
- cf, *lineno);
- cf_section_free(&cs);
- return NULL;
+ file, *lineno);
+ return -1;
}
/*
ptr = cbuf = buf;
t1 = gettoken(&ptr, buf1, sizeof(buf1));
+ if ((*buf1 == '#') || (*buf1 == '\0')) {
+ continue;
+ }
+
/*
- * Skip comments and blank lines immediately.
+ * The caller eats "name1 name2 {", and calls us
+ * for the data inside of the section. So if we
+ * receive a closing brace, then it must mean the
+ * end of the section.
*/
- if ((*buf1 == '#') || (*buf1 == '\0')) {
- continue;
+ if (t1 == T_RCBRACE) {
+ if (this == current) {
+ radlog(L_ERR, "%s[%d]: Too many closing braces",
+ file, *lineno);
+ return -1;
+
+ }
+ this = this->item.parent;
+ continue;
}
/*
* I really really really hate this file. -cparker
*/
if (strcasecmp(buf1, "$INCLUDE") == 0) {
- CONF_SECTION *is;
-
t2 = getword(&ptr, buf2, sizeof(buf2));
- value = cf_expand_variables(cf, lineno, cs, buf, buf2);
- if (value == NULL) {
- cf_section_free(&cs);
- return NULL;
- }
+ value = cf_expand_variables(file, lineno, this, buf, buf2);
+ if (!value) return -1;
#ifdef HAVE_DIRENT_H
/*
dir = opendir(value);
if (!dir) {
radlog(L_ERR, "%s[%d]: Error reading directory %s: %s",
- cf, *lineno, value,
+ file, *lineno, value,
strerror(errno));
- cf_section_free(&cs);
- return NULL;
+ return -1;
}
/*
value, dp->d_name);
if ((stat(buf2, &stat_buf) != 0) ||
S_ISDIR(stat_buf.st_mode)) continue;
- if ((is = conf_read(cf, *lineno, buf2, parent)) == NULL) {
+ /*
+ * Read the file into the current
+ * configuration sectoin.
+ */
+ if (cf_file_include(buf2, this) < 0) {
closedir(dir);
- cf_section_free(&cs);
- return NULL;
+ return -1;
}
-
- cf_fixup_children(cs, is);
}
closedir(dir);
} else
#endif
{ /* it was a normal file */
- DEBUG2( "Config: including file: %s", value );
- if ((is = conf_read(cf, *lineno, value, parent)) == NULL) {
- cf_section_free(&cs);
- return NULL;
+ if (cf_file_include(value, this) < 0) {
+ return -1;
}
- cf_fixup_children(cs, is);
}
continue;
} /* we were in an include */
}
/*
- * See if it's the end of a section.
- */
- if (t1 == T_RCBRACE) {
- if (name1 == NULL || buf2[0]) {
- radlog(L_ERR, "%s[%d]: Unexpected end of section",
- cf, *lineno);
- cf_section_free(&cs);
- return NULL;
- }
- return cs;
- }
-
- /*
* Perhaps a subsection.
*/
if (t2 == T_LCBRACE || t3 == T_LCBRACE) {
- css = cf_section_read(cf, lineno, fp, buf1,
- t2==T_LCBRACE ? NULL : buf2, cs);
- if (css == NULL) {
- cf_section_free(&cs);
- return NULL;
+ css = cf_section_alloc(buf1,
+ t2 == T_LCBRACE ? NULL : buf2,
+ this);
+ if (!css) {
+ radlog(L_ERR, "%s[%d]: Failed allocating memory for section",
+ file, *lineno);
}
- cf_item_add(cs, cf_sectiontoitem(css));
+ cf_item_add(this, cf_sectiontoitem(css));
+ css->item.lineno = *lineno;
+ /*
+ * The current section is now the child section.
+ */
+ this = css;
continue;
}
} else if (buf1[0] == 0 || buf2[0] == 0 ||
(t2 < T_EQSTART || t2 > T_EQEND)) {
radlog(L_ERR, "%s[%d]: Line is not in 'attribute = value' format",
- cf, *lineno);
- cf_section_free(&cs);
- return NULL;
+ file, *lineno);
+ return -1;
}
/*
*/
if (buf1[0] == '_') {
radlog(L_ERR, "%s[%d]: Illegal configuration pair name \"%s\"",
- cf, *lineno, buf1);
- cf_section_free(&cs);
- return NULL;
+ file, *lineno, buf1);
+ return -1;
}
/*
* Handle variable substitution via ${foo}
*/
- value = cf_expand_variables(cf, lineno, cs, buf, buf3);
- if (!value) {
- cf_section_free(&cs);
- return NULL;
- }
+ value = cf_expand_variables(file, lineno, this, buf, buf3);
+ if (!value) return -1;
/*
* Add this CONF_PAIR to our CONF_SECTION
*/
- cpn = cf_pair_alloc(buf1, value, t2, parent);
+ cpn = cf_pair_alloc(buf1, value, t2, this);
cpn->item.lineno = *lineno;
- cf_item_add(cs, cf_pairtoitem(cpn));
+ cf_item_add(this, cf_pairtoitem(cpn));
}
/*
* See if EOF was unexpected ..
*/
- if (name1 != NULL) {
- radlog(L_ERR, "%s[%d]: Unexpected end of file", cf, *lineno);
- cf_section_free(&cs);
- return NULL;
+ if (feof(fp) && (this != current)) {
+ radlog(L_ERR, "%s[%d]: EOF reached without closing brace for section %s starting at line %d",
+ file, *lineno,
+ cf_section_name1(this), cf_section_lineno(this));
+ return -1;
}
- return cs;
+ return 0;
}
/*
- * Read the config file.
+ * Include one config file in another.
*/
-CONF_SECTION *conf_read(const char *fromfile, int fromline,
- const char *conffile, CONF_SECTION *parent)
+int cf_file_include(const char *file, CONF_SECTION *cs)
{
FILE *fp;
int lineno = 0;
- CONF_SECTION *cs;
+ struct stat statbuf;
+ time_t *mtime;
- if ((fp = fopen(conffile, "r")) == NULL) {
- if (fromfile) {
- radlog(L_ERR|L_CONS, "%s[%d]: Unable to open file \"%s\": %s",
- fromfile, fromline, conffile, strerror(errno));
- } else {
- radlog(L_ERR|L_CONS, "Unable to open file \"%s\": %s",
- conffile, strerror(errno));
+ DEBUG2( "Config: including file: %s", file);
+
+ if (stat(file, &statbuf) == 0) {
+ if ((statbuf.st_mode & S_IWOTH) != 0) {
+ radlog(L_ERR|L_CONS, "Configuration file %s is globally writable. Refusing to start due to insecure configuration.",
+ file);
+ return -1;
}
- return NULL;
+
+ if (0 && (statbuf.st_mode & S_IROTH) != 0) {
+ radlog(L_ERR|L_CONS, "Configuration file %s is globally readable. Refusing to start due to insecure configuration.",
+ file);
+ return -1;
+ }
+ }
+
+ fp = fopen(file, "r");
+ if (!fp) {
+ radlog(L_ERR|L_CONS, "Unable to open file \"%s\": %s",
+ file, strerror(errno));
+ return -1;
+ }
+
+ /*
+ * Read the section. It's OK to have EOF without a
+ * matching close brace.
+ */
+ if (cf_section_read(file, &lineno, fp, cs) < 0) {
+ fclose(fp);
+ return -1;
}
- cs = cf_section_read(conffile, &lineno, fp, NULL, NULL, parent);
+ /*
+ * Add the filename to the section
+ */
+ mtime = rad_malloc(sizeof(*mtime));
+ *mtime = statbuf.st_mtime;
+ /* FIXME: error? */
+ cf_data_add_internal(cs, file, mtime, free,
+ PW_TYPE_FILENAME);
fclose(fp);
+ return 0;
+}
+
+/*
+ * Bootstrap a config file.
+ */
+CONF_SECTION *cf_file_read(const char *file)
+{
+ CONF_SECTION *cs;
+
+ cs = cf_section_alloc("main", NULL, NULL);
+ if (!cs) return NULL;
+
+ if (cf_file_include(file, cs) < 0) {
+ cf_section_free(&cs);
+ return NULL;
+ }
return cs;
}
-
/*
* Return a CONF_PAIR within a CONF_SECTION.
*/
{
return item->type == CONF_ITEM_SECTION;
}
+int cf_item_is_pair(CONF_ITEM *item)
+{
+ return item->type == CONF_ITEM_PAIR;
+}
static CONF_DATA *cf_data_alloc(CONF_SECTION *parent, const char *name,