Massively cleaned up #include's, so they're in a consistent
[freeradius.git] / src / main / conffile.c
index 950bd79..deac55e 100644 (file)
  *
  *   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,
@@ -83,6 +71,8 @@ struct conf_part {
        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;
 };
 
 
@@ -264,6 +254,59 @@ static int data_cmp(const void *a, const void *b)
        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
  */
@@ -273,6 +316,10 @@ void cf_section_free(CONF_SECTION **cs)
 
        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;
 
@@ -334,8 +381,7 @@ static CONF_SECTION *cf_section_alloc(const char *name1, const char *name2,
 {
        CONF_SECTION    *cs;
 
-       if (name1 == NULL || !name1[0])
-               name1 = "main";
+       if (!name1) return NULL;
 
        cs = rad_malloc(sizeof(*cs));
        memset(cs, 0, sizeof(*cs));
@@ -598,7 +644,9 @@ static const char *cf_expand_variables(const char *cf, int *lineno,
                                                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;
                                        }
                                }
@@ -706,14 +754,24 @@ int cf_item_parse(CONF_SECTION *cs, const char *name,
                        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:
@@ -741,9 +799,15 @@ int cf_item_parse(CONF_SECTION *cs, const char *name,
                        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;
                
@@ -777,13 +841,19 @@ int cf_item_parse(CONF_SECTION *cs, const char *name,
                        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;
@@ -813,9 +883,15 @@ int cf_item_parse(CONF_SECTION *cs, const char *name,
                        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;
                
@@ -824,9 +900,15 @@ int cf_item_parse(CONF_SECTION *cs, const char *name,
                        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;
@@ -843,7 +925,7 @@ int cf_item_parse(CONF_SECTION *cs, const char *name,
 /*
  *     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;
@@ -869,13 +951,15 @@ int cf_section_parse(const CONF_SECTION *cs, void *base,
                         */
                        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;
@@ -887,6 +971,7 @@ int cf_section_parse(const CONF_SECTION *cs, void *base,
                        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;
                }
 
@@ -895,95 +980,26 @@ int cf_section_parse(const CONF_SECTION *cs, void *base,
                 */
                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;
@@ -995,21 +1011,7 @@ static CONF_SECTION *cf_section_read(const char *cf, int *lineno, FILE *fp,
        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)
@@ -1029,12 +1031,11 @@ static CONF_SECTION *cf_section_read(const char *cf, int *lineno, FILE *fp,
                 *      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;
                }
 
                /*
@@ -1062,11 +1063,25 @@ static CONF_SECTION *cf_section_read(const char *cf, int *lineno, FILE *fp,
                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;
                }
 
                /*
@@ -1076,15 +1091,10 @@ static CONF_SECTION *cf_section_read(const char *cf, int *lineno, FILE *fp,
                 *      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
                        /*
@@ -1102,10 +1112,9 @@ static CONF_SECTION *cf_section_read(const char *cf, int *lineno, FILE *fp,
                                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;
                                }
 
                                /*
@@ -1132,24 +1141,22 @@ static CONF_SECTION *cf_section_read(const char *cf, int *lineno, FILE *fp,
                                                 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 */
@@ -1166,30 +1173,23 @@ static CONF_SECTION *cf_section_read(const char *cf, int *lineno, FILE *fp,
                }
 
                /*
-                *      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;
                }
 
@@ -1207,9 +1207,8 @@ static CONF_SECTION *cf_section_read(const char *cf, int *lineno, FILE *fp,
                } 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;
                }
 
                /*
@@ -1218,70 +1217,111 @@ static CONF_SECTION *cf_section_read(const char *cf, int *lineno, FILE *fp,
                 */
                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.
  */
@@ -1553,6 +1593,10 @@ int cf_item_is_section(CONF_ITEM *item)
 {
        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,