Factor out argv splitting into rad_expand_xlat
authorBrian Candler <b.candler@pobox.com>
Fri, 8 Feb 2013 17:41:58 +0000 (17:41 +0000)
committerBrian Candler <b.candler@pobox.com>
Tue, 26 Feb 2013 17:42:23 +0000 (17:42 +0000)
src/include/radiusd.h
src/main/exec.c
src/main/util.c

index 70a1348..bbfb491 100644 (file)
@@ -626,6 +626,9 @@ int         rad_copy_string(char *dst, const char *src);
 int            rad_copy_variable(char *dst, const char *from);
 int            rad_pps(int *past, int *present, time_t *then,
                        struct timeval *now);
+int            rad_expand_xlat(REQUEST *request, const char *cmd,
+                               int max_argc, const char *argv[], int can_fail,
+                               size_t argv_buflen, char *argv_buf);
 
 /* client.c */
 RADCLIENT_LIST *clients_init(CONF_SECTION *cs);
index 9b1279b..f068de8 100644 (file)
@@ -90,8 +90,6 @@ pid_t radius_start_program(const char *cmd, REQUEST *request,
                        VALUE_PAIR *input_pairs,
                        int shell_escape)
 {
-       const char *from;
-       char *to;
 #ifndef __MINGW32__
        char *p;
        VALUE_PAIR *vp;
@@ -100,147 +98,20 @@ pid_t radius_start_program(const char *cmd, REQUEST *request,
        int from_child[2] = {-1, -1};
        pid_t pid;
 #endif
-       int argc = -1;
+       int argc;
        int i;
-       int left;
        char *argv[MAX_ARGV];
        char argv_buf[4096];
 #define MAX_ENVP 1024
        char *envp[MAX_ENVP];
-       char mycmd[1024];
 
-       if (strlen(cmd) > (sizeof(mycmd) - 1)) {
-               radlog(L_ERR, "Command line is too long");
-               return -1;
-       }
-
-       /*
-        *      Check for bad escapes.
-        */
-       if (cmd[strlen(cmd) - 1] == '\\') {
-               radlog(L_ERR, "Command line has final backslash, without a following character");
-               return -1;
-       }
-
-       strlcpy(mycmd, cmd, sizeof(mycmd));
-
-       /*
-        *      Split the string into argv's BEFORE doing radius_xlat...
-        */
-       from = cmd;
-       to = mycmd;
-       argc = 0;
-       while (*from) {
-               int length;
-
-               /*
-                *      Skip spaces.
-                */
-               if ((*from == ' ') || (*from == '\t')) {
-                       from++;
-                       continue;
-               }
-
-               argv[argc] = to;
-               argc++;
-
-               if (argc >= (MAX_ARGV - 1)) break;
-
-               /*
-                *      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 = rad_copy_string(to, from);
-                               if (length < 0) {
-                                       radlog(L_ERR, "Invalid string passed as argument for external program");
-                                       return -1;
-                               }
-                               from += length;
-                               to += length;
-                               break;
-
-                       case '%':
-                               if (from[1] == '{') {
-                                       *(to++) = *(from++);
-
-                                       length = rad_copy_variable(to, from);
-                                       if (length < 0) {
-                                               radlog(L_ERR, "Invalid variable expansion passed as argument for external program");
-                                               return -1;
-                                       }
-                                       from += length;
-                                       to += length;
-                               } else { /* FIXME: catch %%{ ? */
-                                       *(to++) = *(from++);
-                               }
-                               break;
-
-                       case '\\':
-                               if (from[1] == ' ') from++;
-                               /* FALL-THROUGH */
-
-                       default:
-                               *(to++) = *(from++);
-                       }
-               } /* end of string, or found a space */
-
-               *(to++) = '\0'; /* terminate the string */
-       }
-
-       /*
-        *      We have to have SOMETHING, at least.
-        */
+       argc = rad_expand_xlat(request, cmd, MAX_ARGV, argv, 1,
+                               sizeof(argv_buf), argv_buf);
        if (argc <= 0) {
-               radlog(L_ERR, "Exec-Program: empty command line.");
+               radlog(L_ERR, "Exec-Program: invalid command line.");
                return -1;
        }
 
-       /*
-        *      Expand each string, as appropriate.
-        */
-       to = argv_buf;
-       left = sizeof(argv_buf);
-       for (i = 0; i < argc; i++) {
-               int sublen;
-
-               /*
-                *      Don't touch argv's which won't be translated.
-                */
-               if (strchr(argv[i], '%') == NULL) continue;
-
-               if (!request) continue;
-
-               sublen = radius_xlat(to, left - 1, argv[i], request, NULL, 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) {
-                       radlog(L_ERR, "Exec-Program: Ran out of space while expanding arguments.");
-                       return -1;
-               }
-       }
-       argv[argc] = NULL;
-
 #ifndef __MINGW32__
        /*
         *      Open a pipe for child/parent communication, if necessary.
index a5777d3..42cd53f 100644 (file)
@@ -686,3 +686,159 @@ int rad_pps(int *past, int *present, time_t *then, struct timeval *now)
 
        return pps;
 }
+
+/*
+ * Split a string into words, xlat each one and write into argv array.
+ * Return argc or -1 on failure.
+ */
+
+int rad_expand_xlat(REQUEST *request, const char *cmd,
+                   int max_argc, const char *argv[], int can_fail,
+                   size_t argv_buflen, char *argv_buf)
+{
+       const char *from;
+       char *to;
+       int argc = -1;
+       int i;
+       int left;
+
+       if (strlen(cmd) > (argv_buflen - 1)) {
+               radlog(L_ERR, "rad_expand_xlat: Command line is too long");
+               return -1;
+       }
+
+       /*
+        *      Check for bad escapes.
+        */
+       if (cmd[strlen(cmd) - 1] == '\\') {
+               radlog(L_ERR, "rad_expand_xlat: Command line has final backslash, without a following character");
+               return -1;
+       }
+
+       strlcpy(argv_buf, cmd, argv_buflen);
+
+       /*
+        *      Split the string into argv's BEFORE doing radius_xlat...
+        */
+       from = cmd;
+       to = argv_buf;
+       argc = 0;
+       while (*from) {
+               int length;
+
+               /*
+                *      Skip spaces.
+                */
+               if ((*from == ' ') || (*from == '\t')) {
+                       from++;
+                       continue;
+               }
+
+               argv[argc] = to;
+               argc++;
+
+               if (argc >= (max_argc - 1)) break;
+
+               /*
+                *      Copy the argv over to our buffer.
+                */
+               while (*from && (*from != ' ') && (*from != '\t')) {
+                       if (to >= argv_buf + argv_buflen - 1) {
+                               radlog(L_ERR, "rad_expand_xlat: Ran out of space in command line");
+                               return -1;
+                       }
+
+                       switch (*from) {
+                       case '"':
+                       case '\'':
+                               length = rad_copy_string(to, from);
+                               if (length < 0) {
+                                       radlog(L_ERR, "rad_expand_xlat: Invalid string passed as argument");
+                                       return -1;
+                               }
+                               from += length;
+                               to += length;
+                               break;
+
+                       case '%':
+                               if (from[1] == '{') {
+                                       *(to++) = *(from++);
+
+                                       length = rad_copy_variable(to, from);
+                                       if (length < 0) {
+                                               radlog(L_ERR, "rad_expand_xlat: Invalid variable expansion passed as argument");
+                                               return -1;
+                                       }
+                                       from += length;
+                                       to += length;
+                               } else { /* FIXME: catch %%{ ? */
+                                       *(to++) = *(from++);
+                               }
+                               break;
+
+                       case '\\':
+                               if (from[1] == ' ') from++;
+                               /* FALL-THROUGH */
+
+                       default:
+                               *(to++) = *(from++);
+                       }
+               } /* end of string, or found a space */
+
+               *(to++) = '\0'; /* terminate the string */
+       }
+
+       /*
+        *      We have to have SOMETHING, at least.
+        */
+       if (argc <= 0) {
+               radlog(L_ERR, "rad_expand_xlat: Empty command line.");
+               return -1;
+       }
+
+       /*
+        *      Expand each string, as appropriate.
+        */
+       left = argv_buf + argv_buflen - to;
+       for (i = 0; i < argc; i++) {
+               int sublen;
+
+               /*
+                *      Don't touch argv's which won't be translated.
+                */
+               if (strchr(argv[i], '%') == NULL) continue;
+
+               if (!request) continue;
+
+               sublen = radius_xlat(to, left - 1, argv[i], request, NULL, NULL);
+               if (sublen <= 0) {
+                       if (can_fail) {
+                               /*
+                                *      Fail to be backwards compatible.
+                                *
+                                *      It's yucky, but it won't break anything,
+                                *      and it won't cause security problems.
+                                */
+                               sublen = 0;
+                       } else {
+                               radlog(L_ERR, "rad_expand_xlat: xlat failed");
+                               return -1;
+                       }
+               }
+
+               argv[i] = to;
+               to += sublen;
+               *(to++) = '\0';
+               left -= sublen;
+               left--;
+
+               if (left <= 0) {
+                       radlog(L_ERR, "rad_expand_xlat: Ran out of space while expanding arguments.");
+                       return -1;
+               }
+       }
+       argv[argc] = NULL;
+
+       return argc;
+}
+