From: Arran Cudbard-Bell Date: Mon, 30 Aug 2021 17:16:46 +0000 (-0500) Subject: Split out legacy exec functions to make them easier to remove in future X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4e249c571f7cf9396c993e4250b2dca76c2de626;p=thirdparty%2Ffreeradius-server.git Split out legacy exec functions to make them easier to remove in future --- diff --git a/src/lib/server/exec.c b/src/lib/server/exec.c index d8759b192df..6a0afde2131 100644 --- a/src/lib/server/exec.c +++ b/src/lib/server/exec.c @@ -51,10 +51,10 @@ RCSID("$Id$") # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) #endif #ifndef WIFEXITED -# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +# define WIFEXITED(stat_val) (((stat_val) & 0x7f) == 0) #endif -#define MAX_ARGV (256) +#define MAX_ENVP (1024) /** Flatten a list into individual "char *" argv-style array * @@ -65,7 +65,7 @@ RCSID("$Id$") * - >= 0 number of array elements in argv * - <0 on error */ -static int fr_exec_value_box_list_to_argv(TALLOC_CTX *ctx, char ***argv_p, fr_value_box_list_t const *in) +static int exec_value_box_list_to_argv(TALLOC_CTX *ctx, char ***argv_p, fr_value_box_list_t const *in) { char **argv; fr_value_box_t *vb = NULL; @@ -92,7 +92,8 @@ static int fr_exec_value_box_list_to_argv(TALLOC_CTX *ctx, char ***argv_p, fr_va return argc; } -static void fr_exec_pair_to_env(request_t *request, fr_pair_list_t *input_pairs, char **envp, size_t envlen, bool shell_escape) +static void exec_pair_to_env(request_t *request, fr_pair_list_t *input_pairs, char **envp, + size_t envlen, bool shell_escape) { char *p; size_t i; @@ -160,9 +161,9 @@ static void fr_exec_pair_to_env(request_t *request, fr_pair_list_t *input_pairs, * We try to be fail-safe here. So if ANYTHING * goes wrong, we exit with status 1. */ -static NEVER_RETURNS void fr_exec_child(request_t *request, char **argv, char **envp, - bool exec_wait, - int stdin_pipe[static 2], int stdout_pipe[static 2], int stderr_pipe[static 2]) +static NEVER_RETURNS void exec_child(request_t *request, char **argv, char **envp, + bool exec_wait, + int stdin_pipe[static 2], int stdout_pipe[static 2], int stderr_pipe[static 2]) { int devnull; @@ -259,409 +260,6 @@ static NEVER_RETURNS void fr_exec_child(request_t *request, char **argv, char ** exit(2); } -/** Start a process - * - * @param[out] stdin_fd pointer to int, receives the stdin file - * descriptor. Set to NULL and the child - * will have /dev/null on stdin. - * @param[out] stdout_fd pointer to int, receives the stdout file - * descriptor. Set to NULL and the child - * will have /dev/null on stdout. - * @param[out] stderr_fd pointer to int, receives the stderr file - * descriptor. Set to NULL and the child - * will have /dev/null on stderr. - * @param[in] cmd Command to execute. This is parsed into argv[] - * parts, then each individual argv part is - * xlat'ed. - * @param[in] request Current reuqest - * @param[in] exec_wait set to true to read from or write to child. - * @param[in] input_pairs list of value pairs - these will be put into - * the environment variables of the child. - * @param[in] shell_escape values before passing them as arguments. - * @return - * - PID of the child process. - * - -1 on failure. - */ -pid_t radius_start_program(int *stdin_fd, int *stdout_fd, int *stderr_fd, - char const *cmd, request_t *request, bool exec_wait, - fr_pair_list_t *input_pairs, bool shell_escape) -{ - int stdin_pipe[2] = {-1, -1}; - int stdout_pipe[2] = {-1, -1}; - int stderr_pipe[2] = {-1, -1}; - pid_t pid; - int argc; - int i; - char const **argv_p; - char *argv[MAX_ARGV], **argv_start = argv; - char argv_buf[4096]; -#define MAX_ENVP 1024 - char **envp; - - /* - * Stupid array decomposition... - * - * If we do memcpy(&argv_p, &argv, sizeof(argv_p)) src ends up being a char ** - * pointing to the value of the first element. - */ - memcpy(&argv_p, &argv_start, sizeof(argv_p)); - argc = rad_expand_xlat(request, cmd, MAX_ARGV, argv_p, true, sizeof(argv_buf), argv_buf); - if (argc <= 0) { - ROPTIONAL(RPEDEBUG, PERROR, "Invalid command '%s'", cmd); - return -1; - } - - if (DEBUG_ENABLED3) { - for (i = 0; i < argc; i++) DEBUG3("arg[%d] %s", i, argv[i]); - } - - /* - * Open a pipe for child/parent communication, if necessary. - */ - if (exec_wait) { - if (stdin_fd) { - if (pipe(stdin_pipe) != 0) { - ERROR("Couldn't open pipe to child: %s", fr_syserror(errno)); - return -1; - } - } - if (stdout_fd) { - if (pipe(stdout_pipe) != 0) { - ERROR("Couldn't open pipe from child: %s", fr_syserror(errno)); - /* safe because these either need closing or are == -1 */ - error: - close(stdin_pipe[0]); - close(stdin_pipe[1]); - close(stdout_pipe[0]); - close(stdout_pipe[1]); - close(stderr_pipe[0]); - close(stderr_pipe[1]); - return -1; - } - } - if (stderr_fd) { - if (pipe(stderr_pipe) != 0) { - ERROR("Couldn't open pipe from child: %s", fr_syserror(errno)); - - goto error; - } - } - } - - MEM(envp = talloc_zero_array(request, char *, MAX_ENVP)); - envp[0] = NULL; - if (input_pairs) fr_exec_pair_to_env(request, input_pairs, envp, MAX_ENVP, shell_escape); - - pid = fork(); - if (pid == 0) { - fr_exec_child(request, argv, envp, exec_wait, stdin_pipe, stdout_pipe, stderr_pipe); - } - - /* - * Free child environment variables - */ - talloc_free(envp); - - /* - * Parent process. - */ - if (pid < 0) { - ERROR("Couldn't fork %s: %s", argv[0], fr_syserror(errno)); - if (exec_wait) goto error; - } - - /* - * We're done. Do any necessary cleanups. - */ - 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 (stdin_fd) { - *stdin_fd = stdin_pipe[1]; - close(stdin_pipe[0]); - } - if (stdout_fd) { - *stdout_fd = stdout_pipe[0]; - close(stdout_pipe[1]); - } - if (stderr_fd) { - *stderr_fd = stderr_pipe[0]; - close(stderr_pipe[1]); - } - } else { - (void) fr_event_pid_wait(request->el, request->el, NULL, pid, NULL, NULL); - } - - return pid; -} - - -/** Read from the child process. - * - * @param fd file descriptor to read from. - * @param pid pid of child, will be reaped if it dies. - * @param timeout amount of time to wait, in seconds. - * @param answer buffer to write into. - * @param left length of buffer. - * @return - * - -1 on failure. - * - Length of output. - */ -int radius_readfrom_program(int fd, pid_t pid, fr_time_delta_t timeout, - char *answer, int left) -{ - int done = 0; - int status; - fr_time_t start; - - fr_nonblock(fd); - - /* - * Minimum timeout period is one section - */ - if (timeout < NSEC) timeout = fr_time_delta_from_sec(1); - - /* - * Read from the pipe until we doesn't get any more or - * until the message is full. - */ - start = fr_time(); - while (1) { - int rcode; - fd_set fds; - fr_time_delta_t elapsed; - - FD_ZERO(&fds); - FD_SET(fd, &fds); - - elapsed = fr_time() - start; - if (elapsed >= timeout) goto too_long; - - rcode = select(fd + 1, &fds, NULL, NULL, &fr_time_delta_to_timeval(timeout - elapsed)); - if (rcode == 0) { - too_long: - DEBUG("Child PID %u is taking too much time: forcing failure and killing child.", pid); - kill(pid, SIGTERM); - close(fd); /* should give SIGPIPE to child, too */ - - /* - * Clean up the child entry. - */ - waitpid(pid, &status, 0); - 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. - */ - status = read(fd, answer + done, left); -#else - /* - * There's at least 1 byte ready: read it. - * This is a terrible hack for non-blocking IO. - */ - status = read(fd, answer + done, 1); -#endif - - /* - * 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; - } - - /* Strip trailing new lines */ - while ((done > 0) && (answer[done - 1] == '\n')) { - answer[--done] = '\0'; - } - - return done; -} - -/** Execute a program. - * - * @param[in,out] ctx to allocate new fr_pair_t (s) in. - * @param[out] out buffer to append plaintext (non valuepair) output. - * @param[in] outlen length of out buffer. - * @param[out] output_pairs list of value pairs - Data on child's stdout will be parsed and - * added into this list of value pairs. - * @param[in] request Current request (may be NULL). - * @param[in] cmd Command to execute. This is parsed into argv[] parts, then each individual argv - * part is xlat'ed. - * @param[in] input_pairs list of value pairs - these will be available in the environment of the - * child. - * @param[in] exec_wait set to 1 if you want to read from or write to child. - * @param[in] shell_escape values before passing them as arguments. - * @param[in] timeout amount of time to wait, in seconds. - * @return - * - 0 if exec_wait==0. - * - exit code if exec_wait!=0. - * - -1 on failure. - */ -int radius_exec_program(TALLOC_CTX *ctx, char *out, size_t outlen, fr_pair_list_t *output_pairs, - request_t *request, char const *cmd, fr_pair_list_t *input_pairs, - bool exec_wait, bool shell_escape, fr_time_delta_t timeout) - -{ - pid_t pid; - int stdout_pipe; - char *p; - pid_t child_pid; - int comma = 0; - int status, ret = 0; - ssize_t len; - char answer[4096]; - - RDEBUG2("Executing: %s", cmd); - - if (out) *out = '\0'; - - pid = radius_start_program(NULL, &stdout_pipe, NULL, cmd, request, exec_wait, input_pairs, shell_escape); - if (pid < 0) { - return -1; - } - - if (!exec_wait) { - return 0; - } - - len = radius_readfrom_program(stdout_pipe, pid, timeout, answer, sizeof(answer)); - if (len < 0) { - /* - * Failure - radius_readfrom_program will - * have called close(stdout_pipe) for us - */ - RERROR("Failed to read from child output"); - return -1; - - } - answer[len] = '\0'; - - /* - * Make sure that the writer can't block while writing to - * a pipe that no one is reading from anymore. - */ - close(stdout_pipe); - - if (len == 0) { - goto wait; - } - - /* - * Parse the output, if any. - */ - if (output_pairs) { - fr_pair_list_t vps; - - fr_pair_list_init(&vps); - /* - * HACK: Replace '\n' with ',' so that - * fr_pair_list_afrom_str() can parse the buffer in - * one go (the proper way would be to - * fix fr_pair_list_afrom_str(), 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[len - 1] == ',') { - answer[--len] = '\0'; - } - - if (fr_pair_list_afrom_str(ctx, request->dict, answer, sizeof(answer), &vps) == T_INVALID) { - RPERROR("Failed parsing output from: %s", cmd); - if (out) strlcpy(out, answer, len); - ret = -1; - } - - /* - * We want to mark the new attributes as tainted, - * but not the existing ones. - */ - fr_pair_list_tainted(&vps); - fr_pair_list_append(output_pairs, &vps); - - } else if (out) { - /* - * We've not been told to extract output pairs, - * just copy the programs output to the out - * buffer. - */ - strlcpy(out, answer, outlen); - } - -wait: - child_pid = waitpid(pid, &status, 0); - if (child_pid == 0) { - RERROR("Timeout waiting for child"); - - return -2; - } - - if (child_pid == pid) { - if (WIFEXITED(status)) { - status = WEXITSTATUS(status); - if ((status != 0) || (ret < 0)) { - RERROR("Program returned code (%d) and output \"%pV\"", status, - fr_box_strvalue_len(answer, len)); - } else { - RDEBUG2("Program returned code (%d) and output \"%pV\"", status, - fr_box_strvalue_len(answer, len)); - } - - return ret < 0 ? ret : status; - } - } - - RERROR("Abnormal child exit: %s", fr_syserror(errno)); - - return -1; -} - - /** Execute a program without waiting for the program to finish. * * @param request the request @@ -675,7 +273,7 @@ wait: * would allow finer-grained control over the attributes to put into * the environment. */ -int fr_exec_nowait(request_t *request, fr_value_box_list_t *vb_list, fr_pair_list_t *env_pairs) +int fr_exec_fork_nowait(request_t *request, fr_value_box_list_t *vb_list, fr_pair_list_t *env_pairs) { int argc; char **envp; @@ -698,13 +296,13 @@ int fr_exec_nowait(request_t *request, fr_value_box_list_t *vb_list, fr_pair_lis */ if (env_pairs && !fr_pair_list_empty(env_pairs)) { MEM(envp = talloc_zero_array(request, char *, MAX_ENVP)); - fr_exec_pair_to_env(request, env_pairs, envp, MAX_ENVP, true); + exec_pair_to_env(request, env_pairs, envp, MAX_ENVP, true); } else { MEM(envp = talloc_zero_array(request, char *, 1)); envp[0] = NULL; } - argc = fr_exec_value_box_list_to_argv(request, &argv, vb_list); + argc = exec_value_box_list_to_argv(request, &argv, vb_list); if (argc < 0) { talloc_free(envp); return -1; @@ -719,12 +317,12 @@ int fr_exec_nowait(request_t *request, fr_value_box_list_t *vb_list, fr_pair_lis pid = fork(); /* - * The child never returns from calling fr_exec_child(); + * The child never returns from calling exec_child(); */ if (pid == 0) { int unused[2]; - fr_exec_child(request, argv, envp, false, unused, unused, unused); + exec_child(request, argv, envp, false, unused, unused, unused); } /* @@ -769,8 +367,8 @@ int fr_exec_nowait(request_t *request, fr_value_box_list_t *vb_list, fr_pair_lis * would allow finer-grained control over the attributes to put into * the environment. */ -int fr_exec_wait_start(pid_t *pid_p, int *stdin_fd, int *stdout_fd, int *stderr_fd, - request_t *request, fr_value_box_list_t *vb_list, fr_pair_list_t *env_pairs) +int fr_exec_fork_wait(pid_t *pid_p, int *stdin_fd, int *stdout_fd, int *stderr_fd, + request_t *request, fr_value_box_list_t *vb_list, fr_pair_list_t *env_pairs) { int argc; char **envp; @@ -796,13 +394,13 @@ int fr_exec_wait_start(pid_t *pid_p, int *stdin_fd, int *stdout_fd, int *stderr_ */ if (env_pairs) { MEM(envp = talloc_zero_array(request, char *, MAX_ENVP)); - fr_exec_pair_to_env(request, env_pairs, envp, MAX_ENVP, true); + exec_pair_to_env(request, env_pairs, envp, MAX_ENVP, true); } else { MEM(envp = talloc_zero_array(request, char *, 1)); envp[0] = NULL; } - argc = fr_exec_value_box_list_to_argv(request, &argv, vb_list); + argc = exec_value_box_list_to_argv(request, &argv, vb_list); if (argc < 0) { error: talloc_free(envp); @@ -847,9 +445,9 @@ int fr_exec_wait_start(pid_t *pid_p, int *stdin_fd, int *stdout_fd, int *stderr_ pid = fork(); /* - * The child never returns from calling fr_exec_child(); + * The child never returns from calling exec_child(); */ - if (pid == 0) fr_exec_child(request, argv, envp, true, stdin_pipe, stdout_pipe, stderr_pipe); + if (pid == 0) exec_child(request, argv, envp, true, stdin_pipe, stdout_pipe, stderr_pipe); /* * Parent process. Do all necessary cleanups. @@ -895,7 +493,7 @@ int fr_exec_wait_start(pid_t *pid_p, int *stdin_fd, int *stdout_fd, int *stderr_ /** Cleans up an exec'd process on error * * This function is intended to be called at any point after a successful - * #fr_exec_wait_start_io call in order to release resources and cleanup + * #fr_exec_start call in order to release resources and cleanup * zombie processes. * * @param[in] exec state to cleanup. @@ -951,7 +549,7 @@ void fr_exec_cleanup(fr_exec_state_t *exec, int signal) /* * Callback when exec has completed. Record the status and tidy up. */ -static void exec_waitpid(fr_event_list_t *el, pid_t pid, int status, void *uctx) +static void exec_reap(fr_event_list_t *el, pid_t pid, int status, void *uctx) { fr_exec_state_t *exec = uctx; /* may not be talloced */ request_t *request = exec->request; @@ -1195,11 +793,11 @@ static void exec_stdout_read(UNUSED fr_event_list_t *el, int fd, int flags, void * - 0 on success * - -1 on failure */ -int fr_exec_wait_start_io(TALLOC_CTX *ctx, fr_exec_state_t *exec, request_t *request, - fr_value_box_list_t *cmd, fr_pair_list_t *env_pairs, - bool need_stdin, - bool store_stdout, TALLOC_CTX *stdout_ctx, - fr_time_delta_t timeout) +int fr_exec_start(TALLOC_CTX *ctx, fr_exec_state_t *exec, request_t *request, + fr_value_box_list_t *cmd, fr_pair_list_t *env_pairs, + bool need_stdin, + bool store_stdout, TALLOC_CTX *stdout_ctx, + fr_time_delta_t timeout) { int *stdout_fd = (store_stdout || RDEBUG_ENABLED2) ? &exec->stdout_fd : NULL; @@ -1216,7 +814,7 @@ int fr_exec_wait_start_io(TALLOC_CTX *ctx, fr_exec_state_t *exec, request_t *req .stdout_ctx = stdout_ctx }; - if (fr_exec_wait_start(&exec->pid, exec->stdin_used ? &exec->stdin_fd : NULL, + if (fr_exec_fork_wait(&exec->pid, exec->stdin_used ? &exec->stdin_fd : NULL, stdout_fd, &exec->stderr_fd, request, cmd, exec->vps) < 0) { RPEDEBUG("Failed executing program"); fail: @@ -1235,7 +833,7 @@ int fr_exec_wait_start_io(TALLOC_CTX *ctx, fr_exec_state_t *exec, request_t *req /* * Tell the event loop that it needs to wait for this PID */ - if (fr_event_pid_wait(ctx, request->el, &exec->ev_pid, exec->pid, exec_waitpid, exec) < 0) { + if (fr_event_pid_wait(ctx, request->el, &exec->ev_pid, exec->pid, exec_reap, exec) < 0) { exec->pid = -1; RPEDEBUG("Failed adding watcher for child process"); @@ -1261,7 +859,8 @@ int fr_exec_wait_start_io(TALLOC_CTX *ctx, fr_exec_state_t *exec, request_t *req /* * Setup event to kill the child process after a period of time. */ - if (fr_event_timer_in(ctx, request->el, &exec->ev, timeout, exec_timeout, exec) < 0) goto fail_and_close; + if ((timeout > 0) && fr_event_timer_in(ctx, request->el, &exec->ev, + timeout, exec_timeout, exec) < 0) goto fail_and_close; /* * If we need to parse stdout, insert a special IO handler that diff --git a/src/lib/server/exec.h b/src/lib/server/exec.h index 2a01a91a8bb..44c18825e32 100644 --- a/src/lib/server/exec.h +++ b/src/lib/server/exec.h @@ -77,30 +77,18 @@ typedef struct { } fr_exec_state_t; - -pid_t radius_start_program(int *stdin_fd, int *stdout_fd, int *stderr_fd, - char const *cmd, request_t *request, bool exec_wait, - fr_pair_list_t *input_pairs, bool shell_escape); - -int radius_readfrom_program(int fd, pid_t pid, fr_time_delta_t timeout, - char *answer, int left); - -int radius_exec_program(TALLOC_CTX *ctx, char *out, size_t outlen, fr_pair_list_t *output_pairs, - request_t *request, char const *cmd, fr_pair_list_t *input_pairs, - bool exec_wait, bool shell_escape, fr_time_delta_t timeout) CC_HINT(nonnull (5, 6)); - void fr_exec_cleanup(fr_exec_state_t *exec, int signal); -int fr_exec_nowait(request_t *request, fr_value_box_list_t *vb_list, fr_pair_list_t *env_pairs); +int fr_exec_fork_nowait(request_t *request, fr_value_box_list_t *vb_list, fr_pair_list_t *env_pairs); -int fr_exec_wait_start(pid_t *pid_p, int *stdin_fd, int *stdout_fd, int *stderr_fd, +int fr_exec_fork_wait(pid_t *pid_p, int *stdin_fd, int *stdout_fd, int *stderr_fd, request_t *request, fr_value_box_list_t *vb_list, fr_pair_list_t *env_pairs); -int fr_exec_wait_start_io(TALLOC_CTX *ctx, fr_exec_state_t *exec, request_t *request, - fr_value_box_list_t *vb_list, fr_pair_list_t *env_pairs, - bool need_stdin, - bool store_stdout, TALLOC_CTX *stdout_ctx, - fr_time_delta_t timeout); +int fr_exec_start(TALLOC_CTX *ctx, fr_exec_state_t *exec, request_t *request, + fr_value_box_list_t *vb_list, fr_pair_list_t *env_pairs, + bool need_stdin, + bool store_stdout, TALLOC_CTX *stdout_ctx, + fr_time_delta_t timeout); #ifdef __cplusplus } #endif diff --git a/src/lib/server/exec_legacy.c b/src/lib/server/exec_legacy.c new file mode 100644 index 00000000000..ce5069848d3 --- /dev/null +++ b/src/lib/server/exec_legacy.c @@ -0,0 +1,628 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * $Id$ + * + * @file src/lib/server/exec_legacy.c + * @brief Execute external programs. + * + * @copyright 2000-2004,2006 The FreeRADIUS server project + */ +RCSID("$Id$") + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#ifdef HAVE_SYS_WAIT_H +# include +#endif +#ifndef WEXITSTATUS +# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) +#endif +#ifndef WIFEXITED +# define WIFEXITED(stat_val) (((stat_val) & 0x7f) == 0) +#endif + +#define MAX_ARGV (256) + +static void exec_pair_to_env_legacy(request_t *request, fr_pair_list_t *input_pairs, char **envp, + size_t envlen, bool shell_escape) +{ + char *p; + size_t i; + fr_dcursor_t cursor; + fr_dict_attr_t const *da; + fr_pair_t *vp; + 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. + */ + for (vp = fr_pair_list_head(input_pairs), i = 0; + vp && (i < envlen - 1); + vp = fr_pair_list_next(input_pairs, vp)) { + size_t n; + + /* + * Hmm... maybe we shouldn't pass the + * user's password in an environment + * variable... + */ + snprintf(buffer, sizeof(buffer), "%s=", vp->da->name); + if (shell_escape) { + for (p = buffer; *p != '='; p++) { + if (*p == '-') { + *p = '_'; + } else if (isalpha((int) *p)) { + *p = toupper(*p); + } + } + } + + n = strlen(buffer); + fr_pair_print_value_quoted(&FR_SBUFF_OUT(buffer + n, sizeof(buffer) - n), vp, + shell_escape ? T_DOUBLE_QUOTED_STRING : T_BARE_WORD); + + DEBUG3("export %s", buffer); + envp[i++] = talloc_typed_strdup(envp, buffer); + } + + if (request) { + da = fr_dict_attr_child_by_num(fr_dict_root(fr_dict_internal()), FR_EXEC_EXPORT); + if (da) { + for (vp = fr_dcursor_iter_by_da_init(&cursor, &request->control_pairs, da); + vp && (i < (envlen - 1)); + vp = fr_dcursor_next(&cursor)) { + DEBUG3("export %pV", &vp->data); + memcpy(&envp[i++], &vp->vp_strvalue, sizeof(*envp)); + } + + /* + * NULL terminate for execve + */ + envp[i] = NULL; + } + } +} + + +/* + * Child process. + * + * We try to be fail-safe here. So if ANYTHING + * goes wrong, we exit with status 1. + */ +static NEVER_RETURNS void exec_child_legacy(request_t *request, char **argv, char **envp, + bool exec_wait, + int stdin_pipe[static 2], int stdout_pipe[static 2], int stderr_pipe[static 2]) +{ + int devnull; + + /* + * Open STDIN to /dev/null + */ + devnull = open("/dev/null", O_RDWR); + if (devnull < 0) { + fprintf(stderr, "Failed opening /dev/null: %s\n", fr_syserror(errno)); + + /* + * Where the status code is interpreted as a module rcode + * one is subtracted from it, to allow 0 to equal success + * + * 2 is RLM_MODULE_FAIL + 1 + */ + exit(2); + } + + /* + * Only massage the pipe handles if the parent + * has created them. + */ + if (exec_wait) { + if (stdin_pipe[1] >= 0) { + close(stdin_pipe[1]); + dup2(stdin_pipe[0], STDIN_FILENO); + } else { + dup2(devnull, STDIN_FILENO); + } + + if (stdout_pipe[1] >= 0) { + close(stdout_pipe[0]); + dup2(stdout_pipe[1], STDOUT_FILENO); + } else { + dup2(devnull, STDOUT_FILENO); + } + + if (stderr_pipe[1] >= 0) { + close(stderr_pipe[0]); + dup2(stderr_pipe[1], STDERR_FILENO); + } else { + dup2(devnull, STDERR_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 (!request || !RDEBUG_ENABLED) 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(STDERR_FILENO + 1); + + /* + * Disarm the thread local destructors + * + * FIXME - Leaving them enabled causes issues in child + * execd processes, but we should really track down why. + */ + fr_atexit_thread_local_disarm_all(); + + /* + * I swear the signature for execve is wrong and should + * take 'char const * const argv[]'. + * + * Note: execve(), unlike system(), treats all the space + * delimited arguments as literals, so there's no need + * to perform additional escaping. + */ + execve(argv[0], argv, envp); + printf("Failed to execute \"%s\": %s", argv[0], fr_syserror(errno)); /* fork output will be captured */ + + /* + * Where the status code is interpreted as a module rcode + * one is subtracted from it, to allow 0 to equal success + * + * 2 is RLM_MODULE_FAIL + 1 + */ + exit(2); +} + + +/** Start a process + * + * @param[out] stdin_fd pointer to int, receives the stdin file + * descriptor. Set to NULL and the child + * will have /dev/null on stdin. + * @param[out] stdout_fd pointer to int, receives the stdout file + * descriptor. Set to NULL and the child + * will have /dev/null on stdout. + * @param[out] stderr_fd pointer to int, receives the stderr file + * descriptor. Set to NULL and the child + * will have /dev/null on stderr. + * @param[in] cmd Command to execute. This is parsed into argv[] + * parts, then each individual argv part is + * xlat'ed. + * @param[in] request Current reuqest + * @param[in] exec_wait set to true to read from or write to child. + * @param[in] input_pairs list of value pairs - these will be put into + * the environment variables of the child. + * @param[in] shell_escape values before passing them as arguments. + * @return + * - PID of the child process. + * - -1 on failure. + */ +pid_t radius_start_program_legacy(int *stdin_fd, int *stdout_fd, int *stderr_fd, + char const *cmd, request_t *request, bool exec_wait, + fr_pair_list_t *input_pairs, bool shell_escape) +{ + int stdin_pipe[2] = {-1, -1}; + int stdout_pipe[2] = {-1, -1}; + int stderr_pipe[2] = {-1, -1}; + pid_t pid; + int argc; + int i; + char const **argv_p; + char *argv[MAX_ARGV], **argv_start = argv; + char argv_buf[4096]; +#define MAX_ENVP 1024 + char **envp; + + /* + * Stupid array decomposition... + * + * If we do memcpy(&argv_p, &argv, sizeof(argv_p)) src ends up being a char ** + * pointing to the value of the first element. + */ + memcpy(&argv_p, &argv_start, sizeof(argv_p)); + argc = rad_expand_xlat(request, cmd, MAX_ARGV, argv_p, true, sizeof(argv_buf), argv_buf); + if (argc <= 0) { + ROPTIONAL(RPEDEBUG, PERROR, "Invalid command '%s'", cmd); + return -1; + } + + if (DEBUG_ENABLED3) { + for (i = 0; i < argc; i++) DEBUG3("arg[%d] %s", i, argv[i]); + } + + /* + * Open a pipe for child/parent communication, if necessary. + */ + if (exec_wait) { + if (stdin_fd) { + if (pipe(stdin_pipe) != 0) { + ERROR("Couldn't open pipe to child: %s", fr_syserror(errno)); + return -1; + } + } + if (stdout_fd) { + if (pipe(stdout_pipe) != 0) { + ERROR("Couldn't open pipe from child: %s", fr_syserror(errno)); + /* safe because these either need closing or are == -1 */ + error: + close(stdin_pipe[0]); + close(stdin_pipe[1]); + close(stdout_pipe[0]); + close(stdout_pipe[1]); + close(stderr_pipe[0]); + close(stderr_pipe[1]); + return -1; + } + } + if (stderr_fd) { + if (pipe(stderr_pipe) != 0) { + ERROR("Couldn't open pipe from child: %s", fr_syserror(errno)); + + goto error; + } + } + } + + MEM(envp = talloc_zero_array(request, char *, MAX_ENVP)); + envp[0] = NULL; + if (input_pairs) exec_pair_to_env_legacy(request, input_pairs, envp, MAX_ENVP, shell_escape); + + pid = fork(); + if (pid == 0) { + exec_child_legacy(request, argv, envp, exec_wait, stdin_pipe, stdout_pipe, stderr_pipe); + } + + /* + * Free child environment variables + */ + talloc_free(envp); + + /* + * Parent process. + */ + if (pid < 0) { + ERROR("Couldn't fork %s: %s", argv[0], fr_syserror(errno)); + if (exec_wait) goto error; + } + + /* + * We're done. Do any necessary cleanups. + */ + 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 (stdin_fd) { + *stdin_fd = stdin_pipe[1]; + close(stdin_pipe[0]); + } + if (stdout_fd) { + *stdout_fd = stdout_pipe[0]; + close(stdout_pipe[1]); + } + if (stderr_fd) { + *stderr_fd = stderr_pipe[0]; + close(stderr_pipe[1]); + } + } else { + (void) fr_event_pid_wait(request->el, request->el, NULL, pid, NULL, NULL); + } + + return pid; +} + + +/** Read from the child process. + * + * @param fd file descriptor to read from. + * @param pid pid of child, will be reaped if it dies. + * @param timeout amount of time to wait, in seconds. + * @param answer buffer to write into. + * @param left length of buffer. + * @return + * - -1 on failure. + * - Length of output. + */ +int radius_readfrom_program_legacy(int fd, pid_t pid, fr_time_delta_t timeout, char *answer, int left) +{ + int done = 0; + int status; + fr_time_t start; + + fr_nonblock(fd); + + /* + * Minimum timeout period is one section + */ + if (timeout < NSEC) timeout = fr_time_delta_from_sec(1); + + /* + * Read from the pipe until we doesn't get any more or + * until the message is full. + */ + start = fr_time(); + while (1) { + int rcode; + fd_set fds; + fr_time_delta_t elapsed; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + + elapsed = fr_time() - start; + if (elapsed >= timeout) goto too_long; + + rcode = select(fd + 1, &fds, NULL, NULL, &fr_time_delta_to_timeval(timeout - elapsed)); + if (rcode == 0) { + too_long: + DEBUG("Child PID %u is taking too much time: forcing failure and killing child.", pid); + kill(pid, SIGTERM); + close(fd); /* should give SIGPIPE to child, too */ + + /* + * Clean up the child entry. + */ + waitpid(pid, &status, 0); + 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. + */ + status = read(fd, answer + done, left); +#else + /* + * There's at least 1 byte ready: read it. + * This is a terrible hack for non-blocking IO. + */ + status = read(fd, answer + done, 1); +#endif + + /* + * 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; + } + + /* Strip trailing new lines */ + while ((done > 0) && (answer[done - 1] == '\n')) { + answer[--done] = '\0'; + } + + return done; +} + +/** Execute a program. + * + * @param[in,out] ctx to allocate new fr_pair_t (s) in. + * @param[out] out buffer to append plaintext (non valuepair) output. + * @param[in] outlen length of out buffer. + * @param[out] output_pairs list of value pairs - Data on child's stdout will be parsed and + * added into this list of value pairs. + * @param[in] request Current request (may be NULL). + * @param[in] cmd Command to execute. This is parsed into argv[] parts, then each individual argv + * part is xlat'ed. + * @param[in] input_pairs list of value pairs - these will be available in the environment of the + * child. + * @param[in] exec_wait set to 1 if you want to read from or write to child. + * @param[in] shell_escape values before passing them as arguments. + * @param[in] timeout amount of time to wait, in seconds. + * @return + * - 0 if exec_wait==0. + * - exit code if exec_wait!=0. + * - -1 on failure. + */ +int radius_exec_program_legacy(TALLOC_CTX *ctx, char *out, size_t outlen, fr_pair_list_t *output_pairs, + request_t *request, char const *cmd, fr_pair_list_t *input_pairs, + bool exec_wait, bool shell_escape, fr_time_delta_t timeout) +{ + pid_t pid; + int stdout_pipe; + char *p; + pid_t child_pid; + int comma = 0; + int status, ret = 0; + ssize_t len; + char answer[4096]; + + RDEBUG2("Executing: %s", cmd); + + if (out) *out = '\0'; + + pid = radius_start_program_legacy(NULL, &stdout_pipe, NULL, cmd, request, exec_wait, input_pairs, shell_escape); + if (pid < 0) { + return -1; + } + + if (!exec_wait) { + return 0; + } + + len = radius_readfrom_program_legacy(stdout_pipe, pid, timeout, answer, sizeof(answer)); + if (len < 0) { + /* + * Failure - radius_readfrom_program_legacy will + * have called close(stdout_pipe) for us + */ + RERROR("Failed to read from child output"); + return -1; + + } + answer[len] = '\0'; + + /* + * Make sure that the writer can't block while writing to + * a pipe that no one is reading from anymore. + */ + close(stdout_pipe); + + if (len == 0) { + goto wait; + } + + /* + * Parse the output, if any. + */ + if (output_pairs) { + fr_pair_list_t vps; + + fr_pair_list_init(&vps); + /* + * HACK: Replace '\n' with ',' so that + * fr_pair_list_afrom_str() can parse the buffer in + * one go (the proper way would be to + * fix fr_pair_list_afrom_str(), 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[len - 1] == ',') { + answer[--len] = '\0'; + } + + if (fr_pair_list_afrom_str(ctx, request->dict, answer, sizeof(answer), &vps) == T_INVALID) { + RPERROR("Failed parsing output from: %s", cmd); + if (out) strlcpy(out, answer, len); + ret = -1; + } + + /* + * We want to mark the new attributes as tainted, + * but not the existing ones. + */ + fr_pair_list_tainted(&vps); + fr_pair_list_append(output_pairs, &vps); + + } else if (out) { + /* + * We've not been told to extract output pairs, + * just copy the programs output to the out + * buffer. + */ + strlcpy(out, answer, outlen); + } + +wait: + child_pid = waitpid(pid, &status, 0); + if (child_pid == 0) { + RERROR("Timeout waiting for child"); + + return -2; + } + + if (child_pid == pid) { + if (WIFEXITED(status)) { + status = WEXITSTATUS(status); + if ((status != 0) || (ret < 0)) { + RERROR("Program returned code (%d) and output \"%pV\"", status, + fr_box_strvalue_len(answer, len)); + } else { + RDEBUG2("Program returned code (%d) and output \"%pV\"", status, + fr_box_strvalue_len(answer, len)); + } + + return ret < 0 ? ret : status; + } + } + + RERROR("Abnormal child exit: %s", fr_syserror(errno)); + + return -1; +} diff --git a/src/lib/server/exec_legacy.h b/src/lib/server/exec_legacy.h new file mode 100644 index 00000000000..57792662342 --- /dev/null +++ b/src/lib/server/exec_legacy.h @@ -0,0 +1,51 @@ +#pragma once +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * $Id$ + * + * @file lib/server/exec_legacy.h + * @brief Legacy synchronous exec functions + * + * @copyright 2014 The FreeRADIUS server project + */ +RCSIDH(exec_legacy_h, "$Id$") + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifdef __cplusplus +} +#endif + +pid_t radius_start_program_legacy(int *stdin_fd, int *stdout_fd, int *stderr_fd, + char const *cmd, request_t *request, bool exec_wait, + fr_pair_list_t *input_pairs, bool shell_escape); + +int radius_readfrom_program_legacy(int fd, pid_t pid, fr_time_delta_t timeout, + char *answer, int left); + +int radius_exec_program_legacy(TALLOC_CTX *ctx, char *out, size_t outlen, fr_pair_list_t *output_pairs, + request_t *request, char const *cmd, fr_pair_list_t *input_pairs, + bool exec_wait, bool shell_escape, fr_time_delta_t timeout) CC_HINT(nonnull (5, 6)); + +#ifdef __cplusplus +} +#endif diff --git a/src/lib/server/libfreeradius-server.mk b/src/lib/server/libfreeradius-server.mk index 928c0a9776c..3e85b8cc2d4 100644 --- a/src/lib/server/libfreeradius-server.mk +++ b/src/lib/server/libfreeradius-server.mk @@ -14,6 +14,7 @@ SOURCES := \ dependency.c \ dl_module.c \ exec.c \ + exec_legacy.c \ exfile.c \ log.c \ main_config.c \ diff --git a/src/lib/server/map.c b/src/lib/server/map.c index ece9bfdc6b0..118d0f8e157 100644 --- a/src/lib/server/map.c +++ b/src/lib/server/map.c @@ -29,6 +29,7 @@ RCSID("$Id$") #include +#include #include #include #include @@ -1036,7 +1037,7 @@ static int map_exec_to_vp(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *reque * if dst is an attribute, then we create an attribute of that type and then * call fr_pair_value_from_str on the output of the script. */ - result = radius_exec_program(ctx, answer, sizeof(answer), + result = radius_exec_program_legacy(ctx, answer, sizeof(answer), tmpl_is_list(map->lhs) ? &output_pairs : NULL, request, map->rhs->name, input_pairs ? input_pairs : NULL, true, true, fr_time_delta_from_sec(EXEC_TIMEOUT)); diff --git a/src/lib/server/tmpl_eval.c b/src/lib/server/tmpl_eval.c index a473dd59376..8b1f54def8e 100644 --- a/src/lib/server/tmpl_eval.c +++ b/src/lib/server/tmpl_eval.c @@ -30,6 +30,7 @@ RCSID("$Id$") #include #include +#include #include #include @@ -310,7 +311,7 @@ ssize_t _tmpl_to_type(void *out, return -1; } - if (radius_exec_program(request, (char *)buff, bufflen, NULL, request, vpt->name, NULL, + if (radius_exec_program_legacy(request, (char *)buff, bufflen, NULL, request, vpt->name, NULL, true, false, fr_time_delta_from_sec(EXEC_TIMEOUT)) != 0) return -1; fr_value_box_strdup_shallow(&value_to_cast, NULL, (char *)buff, true); src_type = FR_TYPE_STRING; @@ -589,7 +590,7 @@ ssize_t _tmpl_to_atype(TALLOC_CTX *ctx, void *out, RDEBUG4("EXPAND TMPL EXEC"); fr_value_box_bstr_alloc(tmp_ctx, &buff, &value, NULL, 1024, true); - if (radius_exec_program(request, buff, 1024, NULL, request, vpt->name, NULL, + if (radius_exec_program_legacy(request, buff, 1024, NULL, request, vpt->name, NULL, true, false, fr_time_delta_from_sec(EXEC_TIMEOUT)) != 0) { error: talloc_free(tmp_ctx); diff --git a/src/lib/server/trigger.c b/src/lib/server/trigger.c index a0ce6d8cdca..bc47fedb439 100644 --- a/src/lib/server/trigger.c +++ b/src/lib/server/trigger.c @@ -202,7 +202,7 @@ static unlang_action_t trigger_resume(rlm_rcode_t *p_result, UNUSED int *priorit * continuing. This blocks the executing thread. */ if (trigger->synchronous) { - if (fr_exec_wait_start(&trigger->pid, NULL, NULL, NULL, request, &trigger->args, NULL) < 0) { + if (fr_exec_fork_wait(&trigger->pid, NULL, NULL, NULL, request, &trigger->args, NULL) < 0) { RPERROR("Failed running trigger %s", trigger->name); RETURN_MODULE_FAIL; } @@ -216,7 +216,7 @@ static unlang_action_t trigger_resume(rlm_rcode_t *p_result, UNUSED int *priorit * Execute the program without waiting for the result. */ } else { - if (fr_exec_nowait(request, &trigger->args, NULL) < 0) { + if (fr_exec_fork_nowait(request, &trigger->args, NULL) < 0) { RPERROR("Failed running trigger %s", trigger->name); RETURN_MODULE_FAIL; } @@ -365,7 +365,7 @@ int trigger_exec(unlang_interpret_t *intp, request_t *request, } /* - * radius_exec_program always needs a request. + * radius_exec_program_legacy always needs a request. */ child = request_alloc_internal(NULL, (&(request_init_args_t){ .parent = request, .detachable = true })); diff --git a/src/lib/unlang/tmpl.c b/src/lib/unlang/tmpl.c index ba6e916e2be..7bd9b200174 100644 --- a/src/lib/unlang/tmpl.c +++ b/src/lib/unlang/tmpl.c @@ -166,7 +166,7 @@ static unlang_action_t unlang_tmpl_exec_wait_resume(rlm_rcode_t *p_result, reque { unlang_frame_state_tmpl_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_tmpl_t); - if (fr_exec_wait_start_io(state->ctx, &state->exec, request, + if (fr_exec_start(state->ctx, &state->exec, request, &state->box, state->args.exec.env, false, (state->out != NULL), state, @@ -189,7 +189,7 @@ static unlang_action_t unlang_tmpl_exec_nowait_resume(rlm_rcode_t *p_result, req { unlang_frame_state_tmpl_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_tmpl_t); - if (fr_exec_nowait(request, &state->box, state->exec.vps) < 0) { + if (fr_exec_fork_nowait(request, &state->box, state->exec.vps) < 0) { RPEDEBUG("Failed executing program"); *p_result = RLM_MODULE_FAIL; diff --git a/src/modules/rlm_exec/rlm_exec.c b/src/modules/rlm_exec/rlm_exec.c index 66578cefa1c..e0ab03e9cda 100644 --- a/src/modules/rlm_exec/rlm_exec.c +++ b/src/modules/rlm_exec/rlm_exec.c @@ -133,13 +133,13 @@ static xlat_action_t exec_xlat(TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, reques if (!inst->wait) { /* Not waiting for the response */ - fr_exec_nowait(request, in, input_pairs); + fr_exec_fork_nowait(request, in, input_pairs); return XLAT_ACTION_DONE; } MEM(exec = talloc_zero(request, fr_exec_state_t)); /* Fixme - Should be frame ctx */ - if (fr_exec_wait_start_io(exec, exec, request, in, input_pairs, + if (fr_exec_start(exec, exec, request, in, input_pairs, false, inst->wait, ctx, inst->timeout) < 0) { @@ -297,7 +297,7 @@ static unlang_action_t mod_exec_nowait_resume(rlm_rcode_t *p_result, module_ctx_ } } - if (fr_exec_nowait(request, box, env_pairs) < 0) { + if (fr_exec_fork_nowait(request, box, env_pairs) < 0) { RPEDEBUG("Failed executing program"); RETURN_MODULE_FAIL; } diff --git a/src/modules/rlm_mschap/rlm_mschap.c b/src/modules/rlm_mschap/rlm_mschap.c index 068015b8a5f..93cadcfc8dd 100644 --- a/src/modules/rlm_mschap/rlm_mschap.c +++ b/src/modules/rlm_mschap/rlm_mschap.c @@ -29,6 +29,7 @@ RCSID("$Id$") #define LOG_PREFIX_ARGS dl_module_instance_name_by_data(inst) #include +#include #include #include #include @@ -820,7 +821,7 @@ static int CC_HINT(nonnull (1, 2, 4, 5)) do_mschap_cpw(rlm_mschap_t const *inst, * Start up ntlm_auth with a pipe on stdin and stdout */ - pid = radius_start_program(&to_child, &from_child, NULL, inst->ntlm_cpw, request, true, NULL, false); + pid = radius_start_program_legacy(&to_child, &from_child, NULL, inst->ntlm_cpw, request, true, NULL, false); if (pid < 0) { REDEBUG("could not exec ntlm_auth cpw command"); return -1; @@ -909,9 +910,9 @@ static int CC_HINT(nonnull (1, 2, 4, 5)) do_mschap_cpw(rlm_mschap_t const *inst, /* * Read from the child */ - len = radius_readfrom_program(from_child, pid, fr_time_delta_from_sec(10), buf, sizeof(buf)); + len = radius_readfrom_program_legacy(from_child, pid, fr_time_delta_from_sec(10), buf, sizeof(buf)); if (len < 0) { - /* radius_readfrom_program will have closed from_child for us */ + /* radius_readfrom_program_legacy will have closed from_child for us */ REDEBUG("Failure reading from child"); return -1; } @@ -1186,7 +1187,7 @@ static int CC_HINT(nonnull (1, 2, 4, 5, 6)) do_mschap(rlm_mschap_t const *inst, /* * Run the program, and expect that we get 16 */ - result = radius_exec_program(request, buffer, sizeof(buffer), NULL, request, inst->ntlm_auth, NULL, + result = radius_exec_program_legacy(request, buffer, sizeof(buffer), NULL, request, inst->ntlm_auth, NULL, true, true, inst->ntlm_auth_timeout); if (result != 0) { char *p;