/* * 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; length += 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; break; } /* else FIXME: catch %%{ ?*/ /* FALL-THROUGH */ default: *(to++) = *(from++); length++; break; } } /* loop over the input string */ /* * We ended the string before a trailing '}' */ return -1; }
/** * @brief Decode an attribute name into a string. * * This expands the various formats: * - %{Name} * - %{xlat:name} * - %{Name:-Other} * * calls radius_xlat() to do most of the work * * @param from string to expand * @param to buffer for output * @param freespace space remaining in output buffer * @param request current server request * @param func optional function to escape output; passed to radius_xlat() * @return 0 on success, -1 on failure */ static int decode_attribute(const char **from, char **to, int freespace, REQUEST *request, RADIUS_ESCAPE_STRING func, void *funcarg) { int do_length = 0; const char *module_name, *xlat_str; char *p, *q, *l, *next = NULL; int retlen=0; const xlat_t *c; int varlen; char buffer[8192]; q = *to; *q = '\0'; /* * Copy the input string to an intermediate buffer where * we can mangle it. */ varlen = rad_copy_variable(buffer, *from); if (varlen < 0) { RDEBUG2("ERROR: Badly formatted variable: %s", *from); return -1; } *from += varlen; /* * Kill the %{} around the data we are looking for. */ p = buffer; p[varlen - 1] = '\0'; /* */ p += 2; if (*p == '#') { p++; do_length = 1; } /* * Handle %{%{foo}:-%{bar}}, which is useful, too. * * Did I mention that this parser is garbage? */ if ((p[0] == '%') && (p[1] == '{')) { int len1, len2; int expand2 = FALSE; /* * 'p' is after the start of 'buffer', so we can * safely do this. */ len1 = rad_copy_variable(buffer, p); if (len1 < 0) { RDEBUG2("ERROR: Badly formatted variable: %s", p); return -1; } /* * They did %{%{foo}}, which is stupid, but allowed. */ if (!p[len1]) { RDEBUG2("Improperly nested variable; %%{%s}", p); return -1; } /* * It SHOULD be %{%{foo}:-%{bar}}. If not, it's * an error. */ if ((p[len1] != ':') || (p[len1 + 1] != '-')) { RDEBUG2("No trailing :- after variable at %s", p); return -1; } /* * Parse the second bit. The second bit can be * either %{foo}, or a string "foo", or a string * 'foo', or just a bare word: foo */ p += len1 + 2; l = buffer + len1 + 1; if ((p[0] == '%') && (p[1] == '{')) { len2 = rad_copy_variable(l, p); if (len2 < 0) { RDEBUG2("ERROR: Invalid text after :- at %s", p); return -1; } p += len2; expand2 = TRUE; } else if ((p[0] == '"') || p[0] == '\'') { getstring((const char **) &p, l, strlen(l)); } else { l = p; } /* * Expand the first one. If we did, exit the * conditional. */ retlen = radius_xlat(q, freespace, buffer, request, func, funcarg); if (retlen) { q += retlen; goto done; } RDEBUG2("\t... expanding second conditional"); /* * Expand / copy the second string if required. */ if (expand2) { retlen = radius_xlat(q, freespace, l, request, func, funcarg); if (retlen) { q += retlen; } } else { strlcpy(q, l, freespace); q += strlen(q); } /* * Else the output is an empty string. */ goto done; } /* * See if we're supposed to expand a module name. */ module_name = NULL; for (l = p; *l != '\0'; l++) { /* * module:string */ if (*l == ':') { module_name = p; /* start of name */ *l = '\0'; p = l + 1; break; } /* * Module names can't have spaces. */ if ((*l == ' ') || (*l == '\t')) break; } /* * %{name} is a simple attribute reference, * or regex reference. */ if (!module_name) { if (isdigit(*p) && !p[1]) { /* regex 0..8 */ module_name = xlat_str = p; } else { xlat_str = p; } goto do_xlat; } /* * Maybe it's the old-style %{foo:-bar} */ if (*p == '-') { RDEBUG2("WARNING: Deprecated conditional expansion \":-\". See \"man unlang\" for details"); p++; xlat_str = module_name; next = p; goto do_xlat; } /* * FIXME: For backwards "WTF" compatibility, check for * {...}, (after the :), and copy that, too. */ /* module name, followed by (possibly) per-module string */ xlat_str = p; do_xlat: /* * Just "foo". Maybe it's a magic attr, which doesn't * really exist. * * If we can't find that, then assume it's a dictionary * attribute in the request. * * Else if it's module:foo, look for module, and pass it "foo". */ if (!module_name) { c = xlat_find(xlat_str); if (!c) c = xlat_find("request"); } else { c = xlat_find(module_name); } if (!c) { if (!module_name) { RDEBUG2("WARNING: Unknown Attribute \"%s\" in string expansion \"%%%s\"", xlat_str, *from); } else { RDEBUG2("WARNING: Unknown module \"%s\" in string expansion \"%%%s\"", module_name, *from); } return -1; } if (!c->internal) RDEBUG3("radius_xlat: Running registered xlat function of module %s for string \'%s\'", c->module, xlat_str); if (func) { /* xlat to a temporary buffer, then escape */ char tmpbuf[8192]; retlen = c->do_xlat(c->instance, request, xlat_str, tmpbuf, sizeof(tmpbuf)); if (retlen > 0) { retlen = func(request, q, freespace, tmpbuf, funcarg); if (retlen > 0) { RDEBUG2("string escaped from \'%s\' to \'%s\'", tmpbuf, q); } else if (retlen < 0) { RDEBUG2("string escape failed"); } } } else { retlen = c->do_xlat(c->instance, request, xlat_str, q, freespace); } if (retlen > 0) { if (do_length) { snprintf(q, freespace, "%d", retlen); retlen = strlen(q); } } else if (next) { /* * Expand the second bit. */ RDEBUG2("\t... expanding second conditional"); retlen = radius_xlat(q, freespace, next, request, func, funcarg); } q += retlen; done: *to = q; return 0; }
/* * Execute a program on successful authentication. * Return 0 if exec_wait == 0. * Return the exit code of the called program if exec_wait != 0. * Return -1 on fork/other errors in the parent process. */ int radius_exec_program(const char *cmd, REQUEST *request, int exec_wait, char *user_msg, int msg_len, VALUE_PAIR *input_pairs, VALUE_PAIR **output_pairs, int shell_escape) { VALUE_PAIR *vp; char mycmd[1024]; const char *from; char *p, *to; int pd[2]; pid_t pid, child_pid; int argc = -1; int comma = 0; int status; int i; int n, left, done; char *argv[MAX_ARGV]; char answer[4096]; char argv_buf[4096]; #define MAX_ENVP 1024 char *envp[MAX_ENVP]; struct timeval start; #ifdef O_NONBLOCK int nonblock = TRUE; #endif if (user_msg) *user_msg = '\0'; if (output_pairs) *output_pairs = NULL; if (strlen(cmd) > (sizeof(mycmd) - 1)) { radlog(L_ERR|L_CONS, "Command line is too long"); return -1; } /* * Check for bad escapes. */ if (cmd[strlen(cmd) - 1] == '\\') { radlog(L_ERR|L_CONS, "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|L_CONS, "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|L_CONS, "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. */ if (argc <= 0) { radlog(L_ERR, "Exec-Program: empty 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); 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. */ if (exec_wait) { if (pipe(pd) != 0) { radlog(L_ERR|L_CONS, "Couldn't open pipe: %s", strerror(errno)); return -1; } } else { /* * We're not waiting, so we don't look for a * message, or VP's. */ user_msg = NULL; output_pairs = NULL; } envp[0] = NULL; if (input_pairs) { int envlen; char buffer[1024]; /* * Set up the environment variables in the * parent, so we don't call libc functions that * hold mutexes. They might be locked when we fork, * and will remain locked in the child. */ envlen = 0; for (vp = input_pairs; vp != NULL; vp = vp->next) { /* * Hmm... maybe we shouldn't pass the * user's password in an environment * variable... */ snprintf(buffer, sizeof(buffer), "%s=", vp->name); if (shell_escape) { for (p = buffer; *p != '='; p++) { if (*p == '-') { *p = '_'; } else if (isalpha((int) *p)) { *p = toupper(*p); } } } n = strlen(buffer); vp_prints_value(buffer+n, sizeof(buffer) - n, vp, shell_escape); envp[envlen++] = strdup(buffer); /* * Don't add too many attributes. */ if (envlen == (MAX_ENVP - 1)) break; } envp[envlen] = NULL; } if (exec_wait) { pid = rad_fork(); /* remember PID */ } else { pid = fork(); /* don't wait */ } if (pid == 0) { int devnull; /* * Child process. * * We try to be fail-safe here. So if ANYTHING * goes wrong, we exit with status 1. */ /* * Open STDIN to /dev/null */ devnull = open("/dev/null", O_RDWR); if (devnull < 0) { radlog(L_ERR|L_CONS, "Failed opening /dev/null: %s\n", strerror(errno)); exit(1); } dup2(devnull, STDIN_FILENO); /* * Only massage the pipe handles if the parent * has created them. */ if (exec_wait) { /* * pd[0] is the FD the child will read from, * which we don't want. */ if (close(pd[0]) != 0) { radlog(L_ERR|L_CONS, "Can't close pipe: %s", strerror(errno)); exit(1); } /* * pd[1] is the FD that the child will write to, * so we make it STDOUT. */ if (dup2(pd[1], STDOUT_FILENO) != 1) { radlog(L_ERR|L_CONS, "Can't dup stdout: %s", strerror(errno)); exit(1); } } else { /* no pipe, STDOUT should be /dev/null */ dup2(devnull, STDOUT_FILENO); } /* * If we're not debugging, then we can't do * anything with the error messages, so we throw * them away. * * If we are debugging, then we want the error * messages to go to the STDERR of the server. */ if (debug_flag == 0) { dup2(devnull, STDERR_FILENO); } close(devnull); /* * The server may have MANY FD's open. We don't * want to leave dangling FD's for the child process * to play funky games with, so we close them. */ closefrom(3); execve(argv[0], argv, envp); radlog(L_ERR, "Exec-Program: FAILED to execute %s: %s", argv[0], strerror(errno)); exit(1); } /* * Free child environment variables */ for (i = 0; envp[i] != NULL; i++) { free(envp[i]); } /* * Parent process. */ if (pid < 0) { radlog(L_ERR|L_CONS, "Couldn't fork %s: %s", argv[0], strerror(errno)); if (exec_wait) { close(pd[0]); close(pd[1]); } return -1; } /* * We're not waiting, exit, and ignore any child's status. */ if (!exec_wait) { return 0; } /* * Close the FD to which the child writes it's data. * * If we can't close it, then we close pd[0], and return an * error. */ if (close(pd[1]) != 0) { radlog(L_ERR|L_CONS, "Can't close pipe: %s", strerror(errno)); close(pd[0]); return -1; } #ifdef O_NONBLOCK /* * Try to set it non-blocking. */ do { int flags; if ((flags = fcntl(pd[0], F_GETFL, NULL)) < 0) { nonblock = FALSE; break; } flags |= O_NONBLOCK; if( fcntl(pd[0], F_SETFL, flags) < 0) { nonblock = FALSE; break; } } while (0); #endif /* * Read from the pipe until we doesn't get any more or * until the message is full. */ done = 0; left = sizeof(answer) - 1; gettimeofday(&start, NULL); while (1) { int rcode; fd_set fds; struct timeval when, elapsed, wake; FD_ZERO(&fds); FD_SET(pd[0], &fds); gettimeofday(&when, NULL); tv_sub(&when, &start, &elapsed); if (elapsed.tv_sec >= 10) goto too_long; when.tv_sec = 10; when.tv_usec = 0; tv_sub(&when, &elapsed, &wake); rcode = select(pd[0] + 1, &fds, NULL, NULL, &wake); if (rcode == 0) { too_long: radlog(L_ERR, "Child PID %u (%s) is taking too much time: forcing failure and killing child.", pid, argv[0]); kill(pid, SIGTERM); close(pd[0]); /* should give SIGPIPE to child, too */ /* * Clean up the child entry. */ rad_waitpid(pid, &status); return 1; } if (rcode < 0) { if (errno == EINTR) continue; break; } #ifdef O_NONBLOCK /* * Read as many bytes as possible. The kernel * will return the number of bytes available. */ if (nonblock) { status = read(pd[0], answer + done, left); } else #endif /* * There's at least 1 byte ready: read it. */ status = read(pd[0], answer + done, 1); /* * Nothing more to read: stop. */ if (status == 0) { break; } /* * Error: See if we have to continue. */ if (status < 0) { /* * We were interrupted: continue reading. */ if (errno == EINTR) { continue; } /* * There was another error. Most likely * The child process has finished, and * exited. */ break; } done += status; left -= status; if (left <= 0) break; } answer[done] = 0; /* * Make sure that the writer can't block while writing to * a pipe that no one is reading from anymore. */ close(pd[0]); DEBUG2("Exec-Program output: %s", answer); /* * Parse the output, if any. */ if (done) { n = T_OP_INVALID; if (output_pairs) { /* * For backwards compatibility, first check * for plain text (user_msg). */ vp = NULL; n = userparse(answer, &vp); if (vp) { pairfree(&vp); } } if (n == T_OP_INVALID) { DEBUG("Exec-Program-Wait: plaintext: %s", answer); if (user_msg) { strlcpy(user_msg, answer, msg_len); } } else { /* * HACK: Replace '\n' with ',' so that * userparse() can parse the buffer in * one go (the proper way would be to * fix userparse(), but oh well). */ for (p = answer; *p; p++) { if (*p == '\n') { *p = comma ? ' ' : ','; p++; comma = 0; } if (*p == ',') comma++; } /* * Replace any trailing comma by a NUL. */ if (answer[strlen(answer) - 1] == ',') { answer[strlen(answer) - 1] = '\0'; } radlog(L_DBG,"Exec-Program-Wait: value-pairs: %s", answer); if (userparse(answer, &vp) == T_OP_INVALID) { radlog(L_ERR, "Exec-Program-Wait: %s: unparsable reply", cmd); } else { /* * Tell the caller about the value * pairs. */ *output_pairs = vp; } } /* else the answer was a set of VP's, not a text message */ } /* else we didn't read anything from the child */ /* * Call rad_waitpid (should map to waitpid on non-threaded * or single-server systems). */ child_pid = rad_waitpid(pid, &status); if (child_pid == 0) { radlog(L_DBG, "Exec-Program: Timeout waiting for child"); return 2; } if (child_pid == pid) { if (WIFEXITED(status)) { status = WEXITSTATUS(status); radlog(L_DBG, "Exec-Program: returned: %d", status); return status; } } radlog(L_ERR|L_CONS, "Exec-Program: Abnormal child exit: %s", strerror(errno)); return 1; #else msg_len = msg_len; /* -Wunused */ if (exec_wait) { radlog(L_ERR, "Exec-Program-Wait is not supported"); return -1; } /* * We're not waiting, so we don't look for a * message, or VP's. */ user_msg = NULL; output_pairs = NULL; { /* * The _spawn and _exec families of functions are * found in Windows compiler libraries for * portability from UNIX. There is a variety of * functions, including the ability to pass * either a list or array of parameters, to * search in the PATH or otherwise, and whether * or not to pass an environment (a set of * environment variables). Using _spawn, you can * also specify whether you want the new process * to close your program (_P_OVERLAY), to wait * until the new process is finished (_P_WAIT) or * for the two to run concurrently (_P_NOWAIT). * _spawn and _exec are useful for instances in * which you have simple requirements for running * the program, don't want the overhead of the * Windows header file, or are interested * primarily in portability. */ /* * FIXME: check return code... what is it? */ _spawnve(_P_NOWAIT, argv[0], argv, envp); } return 0; #endif }
/** Start a process * * @param cmd Command to execute. This is parsed into argv[] parts, * then each individual argv part is xlat'ed. * @param request Current reuqest * @param exec_wait set to 1 if you want to read from or write to child * @param[in,out] input_fd pointer to int, receives the stdin file. * descriptor. Set to NULL and the child will have /dev/null on stdin * @param[in,out] output_fd pinter to int, receives the stdout file * descriptor. Set to NULL and child will have /dev/null on stdout. * @param input_pairs list of value pairs - these will be put into * the environment variables of the child. * @param shell_escape * @return PID of the child process, -1 on error. */ pid_t radius_start_program(const char *cmd, REQUEST *request, int exec_wait, int *input_fd, int *output_fd, VALUE_PAIR *input_pairs, int shell_escape) { const char *from; char *to; #ifndef __MINGW32__ char *p; VALUE_PAIR *vp; int n; int to_child[2] = {-1, -1}; int from_child[2] = {-1, -1}; pid_t pid; #endif int argc = -1; 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|L_CONS, "Command line is too long"); return -1; } /* * Check for bad escapes. */ if (cmd[strlen(cmd) - 1] == '\\') { radlog(L_ERR|L_CONS, "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|L_CONS, "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|L_CONS, "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. */ if (argc <= 0) { radlog(L_ERR, "Exec-Program: empty 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); 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. */ if (exec_wait) { if (input_fd) { if (pipe(to_child) != 0) { radlog(L_ERR|L_CONS, "Couldn't open pipe to child: %s", strerror(errno)); return -1; } } if (output_fd) { if (pipe(from_child) != 0) { radlog(L_ERR|L_CONS, "Couldn't open pipe from child: %s", strerror(errno)); /* safe because these either need closing or are == -1 */ close(to_child[0]); close(to_child[1]); return -1; } } } envp[0] = NULL; if (input_pairs) { int envlen; char buffer[1024]; /* * Set up the environment variables in the * parent, so we don't call libc functions that * hold mutexes. They might be locked when we fork, * and will remain locked in the child. */ envlen = 0; for (vp = input_pairs; vp != NULL; vp = vp->next) { /* * Hmm... maybe we shouldn't pass the * user's password in an environment * variable... */ snprintf(buffer, sizeof(buffer), "%s=", vp->name); if (shell_escape) { for (p = buffer; *p != '='; p++) { if (*p == '-') { *p = '_'; } else if (isalpha((int) *p)) { *p = toupper(*p); } } } n = strlen(buffer); vp_prints_value(buffer+n, sizeof(buffer) - n, vp, shell_escape); envp[envlen++] = strdup(buffer); /* * Don't add too many attributes. */ if (envlen == (MAX_ENVP - 1)) break; } envp[envlen] = NULL; } if (exec_wait) { pid = rad_fork(); /* remember PID */ } else { pid = fork(); /* don't wait */ } if (pid == 0) { int devnull; /* * Child process. * * We try to be fail-safe here. So if ANYTHING * goes wrong, we exit with status 1. */ /* * Open STDIN to /dev/null */ devnull = open("/dev/null", O_RDWR); if (devnull < 0) { radlog(L_ERR|L_CONS, "Failed opening /dev/null: %s\n", strerror(errno)); exit(1); } /* * Only massage the pipe handles if the parent * has created them. */ if (exec_wait) { if (input_fd) { close(to_child[1]); dup2(to_child[0], STDIN_FILENO); } else { dup2(devnull, STDIN_FILENO); } if (output_fd) { close(from_child[0]); dup2(from_child[1], STDOUT_FILENO); } else { dup2(devnull, STDOUT_FILENO); } } else { /* no pipe, STDOUT should be /dev/null */ dup2(devnull, STDIN_FILENO); dup2(devnull, STDOUT_FILENO); } /* * If we're not debugging, then we can't do * anything with the error messages, so we throw * them away. * * If we are debugging, then we want the error * messages to go to the STDERR of the server. */ if (debug_flag == 0) { dup2(devnull, STDERR_FILENO); } close(devnull); /* * The server may have MANY FD's open. We don't * want to leave dangling FD's for the child process * to play funky games with, so we close them. */ closefrom(3); execve(argv[0], argv, envp); radlog(L_ERR, "Exec-Program: FAILED to execute %s: %s", argv[0], strerror(errno)); exit(1); } /* * Free child environment variables */ for (i = 0; envp[i] != NULL; i++) { free(envp[i]); } /* * Parent process. */ if (pid < 0) { radlog(L_ERR|L_CONS, "Couldn't fork %s: %s", argv[0], strerror(errno)); if (exec_wait) { /* safe because these either need closing or are == -1 */ close(to_child[0]); close(to_child[1]); close(from_child[0]); close(from_child[0]); } return -1; } /* * We're not waiting, exit, and ignore any child's status. */ if (exec_wait) { /* * Close the ends of the pipe(s) the child is using * return the ends of the pipe(s) our caller wants * */ if (input_fd) { *input_fd = to_child[1]; close(to_child[0]); } if (output_fd) { *output_fd = from_child[0]; close(from_child[1]); } } return pid; #else if (exec_wait) { radlog(L_ERR, "Exec-Program-Wait is not supported"); return -1; } { /* * The _spawn and _exec families of functions are * found in Windows compiler libraries for * portability from UNIX. There is a variety of * functions, including the ability to pass * either a list or array of parameters, to * search in the PATH or otherwise, and whether * or not to pass an environment (a set of * environment variables). Using _spawn, you can * also specify whether you want the new process * to close your program (_P_OVERLAY), to wait * until the new process is finished (_P_WAIT) or * for the two to run concurrently (_P_NOWAIT). * _spawn and _exec are useful for instances in * which you have simple requirements for running * the program, don't want the overhead of the * Windows header file, or are interested * primarily in portability. */ /* * FIXME: check return code... what is it? */ _spawnve(_P_NOWAIT, argv[0], argv, envp); } return 0; #endif }
int rad_expand_xlat(REQUEST *request, char const *cmd, int max_argc, char *argv[], bool can_fail, size_t argv_buflen, char *argv_buf) { char const *from; char *to; int argc = -1; int i; int left; if (strlen(cmd) > (argv_buflen - 1)) { ERROR("rad_expand_xlat: Command line is too long"); return -1; } /* * Check for bad escapes. */ if (cmd[strlen(cmd) - 1] == '\\') { ERROR("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) { ERROR("rad_expand_xlat: Ran out of space in command line"); return -1; } switch (*from) { case '"': case '\'': length = rad_copy_string_bare(to, from); if (length < 0) { ERROR("rad_expand_xlat: Invalid string passed as argument"); return -1; } from += length+2; to += length; break; case '%': if (from[1] == '{') { *(to++) = *(from++); length = rad_copy_variable(to, from); if (length < 0) { ERROR("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) { ERROR("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, request, argv[i], 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 { ERROR("rad_expand_xlat: xlat failed"); return -1; } } argv[i] = to; to += sublen; *(to++) = '\0'; left -= sublen; left--; if (left <= 0) { ERROR("rad_expand_xlat: Ran out of space while expanding arguments."); return -1; } } argv[argc] = NULL; return argc; }