Moved copy_string && copy_var from exec.c to util.c, as public
authoraland <aland>
Mon, 28 Feb 2005 22:32:13 +0000 (22:32 +0000)
committeraland <aland>
Mon, 28 Feb 2005 22:32:13 +0000 (22:32 +0000)
functions, so that others may use them.

(mostly) copied "split into argv" code from exec.c into xlat_config,
to avoid injection attacks.  i.e. "split into argv, and then xlat",
rather than "xlat, and then split into argv".

This also allows the use of "." in section/pair names.

src/include/radiusd.h
src/main/exec.c
src/main/mainconfig.c
src/main/util.c

index 2ae7c9f..478505a 100644 (file)
@@ -333,6 +333,8 @@ int         request_data_add(REQUEST *request,
                                 void *opaque, void (*free_opaque)(void *));
 void           *request_data_get(REQUEST *request,
                                  void *unique_ptr, int unique_int);
+int            rad_copy_string(char *dst, const char *src);
+int            rad_copy_variable(char *dst, const char *from);
 
 /* request_process.c */
 int rad_respond(REQUEST *request, RAD_REQUEST_FUNP fun);
index 104d22c..d73fe95 100644 (file)
@@ -44,95 +44,6 @@ static const char rcsid[] = "$Id$";
 #include "radiusd.h"
 #include "rad_assert.h"
 
-/*
- *     Copy a quoted string.
- */
-static int copy_string(const char *from, char *to)
-{
-       int length = 0;
-       char quote = *from;
-
-       do {
-               if (*from == '\\') {
-                       *(to++) = *(from++);
-                       length++;
-               }
-               *(to++) = *(from++);
-               length++;
-       } while (*from && (*from != quote));
-
-       if (*from != quote) return -1; /* not properly quoted */
-
-       *(to++) = quote;
-       length++;
-       *to = '\0';
-
-       return length;
-}
-
-
-/*
- *     Copy a %{} string.
- */
-static int copy_var(const char *from, char *to)
-{
-       int length = 0;
-       int sublen;
-
-       *(to++) = *(from++);
-       length++;
-
-       while (*from) {
-               switch (*from) {
-               case '"':
-               case '\'':
-                       sublen = copy_string(from, to);
-                       if (sublen < 0) return sublen;
-                       from += sublen;
-                       to += sublen;
-                       break;
-
-               case '}':       /* end of variable expansion */
-                       *(to++) = *(from++);
-                       *to = '\0';
-                       length++;
-                       return length; /* proper end of variable */
-
-               case '\\':
-                       *(to++) = *(from++);
-                       *(to++) = *(from++);
-                       length += 2;
-                       break;
-
-               case '%':       /* start of variable expansion */
-                       if (from[1] == '{') {
-                               *(to++) = *(from++);
-                               length++;
-                               
-                               sublen = copy_var(from, to);
-                               if (sublen < 0) return sublen;
-                               from += sublen;
-                               to += sublen;
-                               length += sublen;
-                       } /* else FIXME: catch %%{ ?*/
-
-                       /* FALL-THROUGH */
-                       break;
-
-               default:
-                       *(to++) = *(from++);
-                       length++;
-                       break;
-               }
-       } /* loop over the input string */
-
-       /*
-        *      We ended the string before a trailing '}'
-        */
-
-       return -1;
-}
-
 #define MAX_ARGV (256)
 /*
  *     Execute a program on successful authentication.
@@ -206,10 +117,14 @@ int radius_exec_program(const char *cmd, REQUEST *request,
                 *      Copy the argv over to our buffer.
                 */
                while (*from && (*from != ' ') && (*from != '\t')) {
+                       if (to >= mycmd + sizeof(mycmd) - 1) {
+                               return -1; /* ran out of space */
+                       }
+
                        switch (*from) {
                        case '"':
                        case '\'':
-                               length = copy_string(from, to);
+                               length = rad_copy_string(to, from);
                                if (length < 0) {
                                        radlog(L_ERR|L_CONS, "Invalid string passed as argument for external program");
                                        return -1;
@@ -222,7 +137,7 @@ int radius_exec_program(const char *cmd, REQUEST *request,
                                if (from[1] == '{') {
                                        *(to++) = *(from++);
                                        
-                                       length = copy_var(from, to);
+                                       length = rad_copy_variable(to, from);
                                        if (length < 0) {
                                                radlog(L_ERR|L_CONS, "Invalid variable expansion passed as argument for external program");
                                                return -1;
index eb0acd9..332b5ef 100644 (file)
@@ -233,6 +233,8 @@ static CONF_PARSER server_config[] = {
        { NULL, -1, 0, NULL, NULL }
 };
 
+
+#define MAX_ARGV (256)
 /*
  *     Xlat for %{config:section.subsection.attribute}
  */
@@ -243,10 +245,13 @@ static int xlat_config(void *instance, REQUEST *request,
 {
        CONF_SECTION *cs;
        CONF_PAIR *cp;
-       char buffer[1024];
+       int i, argc, left;
+       const char *from, *value;
+       char *to;
        char xlat_buffer[1024];
-       char *p, *value;
-       const char *start = fmt;
+       char myfmt[1024];
+       char argv_buf[1024];
+       char *argv[MAX_ARGV];
 
        request = request;      /* -Wunused */
        instance = instance;    /* -Wunused */
@@ -255,120 +260,181 @@ static int xlat_config(void *instance, REQUEST *request,
        cs = NULL;
 
        /*
-        *      Weird hacks...
+        *      Split the string into argv's BEFORE doing radius_xlat...
+        *      Copied from exec.c
         */
-       p = strchr(fmt, '%');
-       if (p) {
-               radius_xlat(xlat_buffer, sizeof(xlat_buffer), fmt, request, NULL);
-               start = fmt = xlat_buffer;
-       }
-
-       while (cp == NULL) {
-               int flag = 0;
-               char *name2;
-
+       from = fmt;
+       to = myfmt; 
+       argc = 0;
+       while (*from) {
+               int flag, length;
+               
+               flag = 0;
+               argv[argc] = to;
+               argc++;
+               
+               if (argc >= (MAX_ARGV - 1)) break;
+               
                /*
-                *      Find the next section.
+                *      Copy the argv over to our buffer.
                 */
-               name2 = NULL;
-               for (p = buffer; (*fmt != 0); p++, fmt++) {
-                       if ((p - buffer) >= (sizeof(buffer) - 1)) break;
+               while (*from) {
+                       if (to >= myfmt + sizeof(myfmt) - 1) {
+                               return 0; /* no error msg */
+                       }
 
-                       /*
-                        *      Allow '.' in names, by skipping over them
-                        *      in array references.  Geez, what a hack..
-                        */
-                       *p = *fmt;
-                       if (*p == '[') {
-                               if (flag > 0) {
-                                       radlog(L_ERR, "config: Nested '[' in \"%s\"", start);
+                       switch (*from) {
+                       case '"':
+                       case '\'':
+                               length = rad_copy_string(to, from);
+                               if (length < 0) {
+                                       return -1;
+                               }
+                               from += length;
+                               to += length;
+                               break;
+
+                       case '%':
+                               if (from[1] == '{') {
+                                       *(to++) = *(from++);
+                                       
+                                       length = rad_copy_variable(to, from);
+                                       if (length < 0) {
+                                               return -1;
+                                       }
+                                       from += length;
+                                       to += length;
+                               } else { /* FIXME: catch %%{ ? */
+                                       *(to++) = *(from++);
+                               }
+                               break;
+
+                       case '[':
+                               if (flag != 0) {
+                                       radlog(L_ERR, "config: Unexpected nested '[' in \"%s\"", fmt);
                                        return 0;
                                }
                                flag++;
-                               if (!name2) name2 = p;
-                       }
-                       if (*p == ']') {
+                               *(to++) = *(from++);
+                               break;
+
+                       case ']':
                                if (flag == 0) {
-                                       radlog(L_ERR, "config: Unbalanced ']'");
+                                       radlog(L_ERR, "config: Unbalanced ']' in \"%s\"", fmt);
                                        return 0;
                                }
+                               if (from[1] != '.') {
+                                       radlog(L_ERR, "config: Unexpected text after ']' in \"%s\"", fmt);
+                                       return 0;
+                               }
+
                                flag--;
+                               *(to++) = *(from++);
+                               break;
+
+                       case '.':
+                               if (flag == 0) break;
+                               /* FALL-THROUGH */
+
+                       default:
+                               *(to++) = *(from++);
+                               break;
                        }
 
-                       if (*p == '.') {
-                               if (flag > 0) continue;
+                       if ((*from == '.') && (flag == 0)) {
+                               from++;
                                break;
                        }
+               } /* end of string, or found a period */
+
+               if (flag != 0) {
+                       radlog(L_ERR, "config: Unbalanced '[' in \"%s\"", fmt);
+                       return 0;
                }
-               *p = '\0';
+
+               *(to++) = '\0'; /* terminate the string. */
+       }
+
+       /*
+        *      Expand each string, as appropriate
+        */
+       to = argv_buf;
+       left = sizeof(argv_buf);
+       for (i = 0; i < argc; i++) {
+               int sublen;
 
                /*
-                *      Rip out name2, if applicable.
+                *      Don't touch argv's which won't be translated.
                 */
-               p = NULL;
-               if (name2) {
-                       *name2 = '\0';
-                       name2++;
+               if (strchr(argv[i], '%') == NULL) continue;
 
-                       p = strchr(name2, ']');
-                       *p = '\0';
-                       p++;
-                       if (!*p) p = NULL;
+               sublen = radius_xlat(to, left - 1, argv[i], request, NULL);
+               if (sublen <= 0) {
+                       /*
+                        *      Fail to be backwards compatible.
+                        *
+                        *      It's yucky, but it won't break anything,
+                        *      and it won't cause security problems.
+                        */
+                       sublen = 0;
                }
                
+               argv[i] = to;
+               to += sublen;
+               *(to++) = '\0';
+               left -= sublen;
+               left--;
+
+               if (left <= 0) {
+                       return 0;
+               }
+       }
+       argv[argc] = NULL;
+
+       cs = cf_section_find(NULL); /* get top-level section */
+
+       /*
+        *      Root through section & subsection references.
+        *      The last entry of argv is the CONF_PAIR.
+        */
+       for (i = 0; i < argc - 1; i++) {
+               char *name2 = NULL;
+               CONF_SECTION *subcs;
+
                /*
-                *  The character is a '.', find a section (as the user
-                *  has given us a subsection to find)
+                *      FIXME: What about RADIUS attributes containing '['?
                 */
-               if ((name2 != NULL) || (*fmt == '.')) {
-                       CONF_SECTION *next;
-                       
-                       if (*fmt == '.') fmt++; /* skip the period */
-
-                       if (!cs && !name2) {
-                               next = cf_section_find(buffer);
-                       } else if (name2) {
-                               next = cf_section_sub_find_name2(cs, buffer,
-                                                                name2);
-                       } else {
-                               next = cf_subsection_find_next(cs, NULL,
-                                                              buffer);
-                       }
-                       if (!next) {
-                               if (name2) {
-                                       radlog(L_ERR, "config: section \"%s %s{}\" not found while dereferencing \"%s\"", buffer, name2, start);
-                               } else {
-                                       radlog(L_ERR, "config: section \"%s {}\" not found while dereferencing \"%s\"", buffer, start);
-                               }
-                               return 0;
-                       }
-                       cs = next;
-
-                       if (p) {
-                               char *q;
-
-                               *p = '\0';
-                               p++;
-                               q = strchr(p, ']');
-                               if (q) *q = '\0';
-                               
-                               cp = cf_pair_find(cs, p);
-                               
-                               if (!cp) {
-                                       radlog(L_ERR, "config: item \"%s\" not found in section \"%s %s{}\"while dereferencing \"%s\"", p, buffer, name2, start);
-                                       return 0;
-                               }
-                       }
-
-               } else {        /* no period or name2, must be a conf-part */
-                       cp = cf_pair_find(cs, buffer);
+               name2 = strchr(argv[i], '[');
+               if (name2) {
+                       char *p = strchr(name2, ']');
+                       rad_assert(p != NULL);
+                       rad_assert(p[1] =='\0');
+                       *p = '\0';
+                       *name2 = '\0';
+                       name2++;
+               }
 
-                       if (!cp) {
-                               radlog(L_ERR, "config: item \"%s\" not found while dereferencing \"%s\"", buffer, start);
-                               return 0;
-                       }
+               if (name2) {
+                       subcs = cf_section_sub_find_name2(cs, argv[i],
+                                                         name2);
+               } else {
+                       subcs = cf_section_sub_find(cs, argv[i]);
+               }
+               if (!subcs) {
+                       radlog(L_ERR, "config: section \"%s {}\" not found while dereferencing \"%s\"", argv[i], fmt);
+                       return 0;
                }
-       } /* until cp is non-NULL */
+               cs = subcs;
+       } /* until argc - 1 */
+
+       /*
+        *      This can now have embedded periods in it.
+        */
+       cp = cf_pair_find(cs, argv[argc]);
+       if (!cp) {
+               radlog(L_ERR, "config: item \"%s\" not found while dereferencing \"%s\"", argv[argc], fmt);
+               return 0;
+       }
 
        /*
         *  Ensure that we only copy what's necessary.
index ea0e705..1ef1ee7 100644 (file)
@@ -407,3 +407,92 @@ REQUEST *request_alloc_fake(REQUEST *oldreq)
 }
 
 
+/*
+ *     Copy a quoted string.
+ */
+int rad_copy_string(char *to, const char *from)
+{
+       int length = 0;
+       char quote = *from;
+
+       do {
+               if (*from == '\\') {
+                       *(to++) = *(from++);
+                       length++;
+               }
+               *(to++) = *(from++);
+               length++;
+       } while (*from && (*from != quote));
+
+       if (*from != quote) return -1; /* not properly quoted */
+
+       *(to++) = quote;
+       length++;
+       *to = '\0';
+
+       return length;
+}
+
+
+/*
+ *     Copy a %{} string.
+ */
+int rad_copy_variable(char *to, const char *from)
+{
+       int length = 0;
+       int sublen;
+
+       *(to++) = *(from++);
+       length++;
+
+       while (*from) {
+               switch (*from) {
+               case '"':
+               case '\'':
+                       sublen = rad_copy_string(to, from);
+                       if (sublen < 0) return sublen;
+                       from += sublen;
+                       to += sublen;
+                       break;
+
+               case '}':       /* end of variable expansion */
+                       *(to++) = *(from++);
+                       *to = '\0';
+                       length++;
+                       return length; /* proper end of variable */
+
+               case '\\':
+                       *(to++) = *(from++);
+                       *(to++) = *(from++);
+                       length += 2;
+                       break;
+
+               case '%':       /* start of variable expansion */
+                       if (from[1] == '{') {
+                               *(to++) = *(from++);
+                               length++;
+                               
+                               sublen = rad_copy_variable(to, from);
+                               if (sublen < 0) return sublen;
+                               from += sublen;
+                               to += sublen;
+                               length += sublen;
+                       } /* else FIXME: catch %%{ ?*/
+
+                       /* FALL-THROUGH */
+                       break;
+
+               default:
+                       *(to++) = *(from++);
+                       length++;
+                       break;
+               }
+       } /* loop over the input string */
+
+       /*
+        *      We ended the string before a trailing '}'
+        */
+
+       return -1;
+}
+