The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
exec_legacy.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 1435f94150996de950a092d0d3fb0c28f7ba2dd0 $
19 *
20 * @file src/lib/server/exec_legacy.c
21 * @brief Execute external programs.
22 *
23 * @copyright 2000-2004,2006 The FreeRADIUS server project
24 */
25RCSID("$Id: 1435f94150996de950a092d0d3fb0c28f7ba2dd0 $")
26
27#include <freeradius-devel/util/debug.h>
28#include <freeradius-devel/server/request.h>
29#include <freeradius-devel/server/util.h>
30#include <freeradius-devel/server/exec_legacy.h>
31#include <freeradius-devel/server/exec_priv.h>
32
33#define MAX_ARGV (256)
34
35static void exec_pair_to_env_legacy(request_t *request, fr_pair_list_t *input_pairs, char **envp,
36 size_t envlen, bool shell_escape)
37{
38 char *p;
39 size_t i;
40 fr_dcursor_t cursor;
41 fr_dict_attr_t const *da;
43 char buffer[1024];
44
45 /*
46 * Set up the environment variables in the
47 * parent, so we don't call libc functions that
48 * hold mutexes. They might be locked when we fork,
49 * and will remain locked in the child.
50 */
51 for (vp = fr_pair_list_head(input_pairs), i = 0;
52 vp && (i < envlen - 1);
53 vp = fr_pair_list_next(input_pairs, vp)) {
54 size_t n;
55
56 /*
57 * Hmm... maybe we shouldn't pass the
58 * user's password in an environment
59 * variable...
60 */
61 snprintf(buffer, sizeof(buffer), "%s=", vp->da->name);
62 if (shell_escape) {
63 for (p = buffer; *p != '='; p++) {
64 if (*p == '-') {
65 *p = '_';
66 } else if (isalpha((uint8_t) *p)) {
67 *p = toupper((uint8_t) *p);
68 }
69 }
70 }
71
72 n = strlen(buffer);
74 shell_escape ? T_DOUBLE_QUOTED_STRING : T_BARE_WORD);
75
76 DEBUG3("export %s", buffer);
77 envp[i++] = talloc_typed_strdup(envp, buffer);
78 }
79
80 if (request) {
82 if (da) {
83 for (vp = fr_pair_dcursor_by_da_init(&cursor, &request->control_pairs, da);
84 vp && (i < (envlen - 1));
85 vp = fr_dcursor_next(&cursor)) {
86 DEBUG3("export %pV", &vp->data);
87 memcpy(&envp[i++], &vp->vp_strvalue, sizeof(*envp));
88 }
89
90 /*
91 * NULL terminate for execve
92 */
93 envp[i] = NULL;
94 }
95 }
96}
97
98
99/*
100 * Child process.
101 *
102 * We try to be fail-safe here. So if ANYTHING
103 * goes wrong, we exit with status 1.
104 */
105static NEVER_RETURNS void exec_child_legacy(request_t *request, char **argv, char **envp,
106 bool exec_wait,
107 int stdin_pipe[static 2], int stdout_pipe[static 2], int stderr_pipe[static 2])
108{
109 int devnull;
110
111 /*
112 * Open STDIN to /dev/null
113 */
114 devnull = open("/dev/null", O_RDWR);
115 if (devnull < 0) {
116 fprintf(stderr, "Failed opening /dev/null: %s\n", fr_syserror(errno));
117
118 /*
119 * Where the status code is interpreted as a module rcode
120 * one is subtracted from it, to allow 0 to equal success
121 *
122 * 2 is RLM_MODULE_FAIL + 1
123 */
124 exit(2);
125 }
126
127 /*
128 * Only massage the pipe handles if the parent
129 * has created them.
130 */
131 if (exec_wait) {
132 if (stdin_pipe[1] >= 0) {
133 close(stdin_pipe[1]);
134 dup2(stdin_pipe[0], STDIN_FILENO);
135 } else {
136 dup2(devnull, STDIN_FILENO);
137 }
138
139 if (stdout_pipe[1] >= 0) {
140 close(stdout_pipe[0]);
141 dup2(stdout_pipe[1], STDOUT_FILENO);
142 } else {
143 dup2(devnull, STDOUT_FILENO);
144 }
145
146 if (stderr_pipe[1] >= 0) {
147 close(stderr_pipe[0]);
148 dup2(stderr_pipe[1], STDERR_FILENO);
149 } else {
150 dup2(devnull, STDERR_FILENO);
151 }
152 } else { /* no pipe, STDOUT should be /dev/null */
153 dup2(devnull, STDIN_FILENO);
154 dup2(devnull, STDOUT_FILENO);
155
156 /*
157 * If we're not debugging, then we can't do
158 * anything with the error messages, so we throw
159 * them away.
160 *
161 * If we are debugging, then we want the error
162 * messages to go to the STDERR of the server.
163 */
164 if (!request || !RDEBUG_ENABLED) dup2(devnull, STDERR_FILENO);
165 }
166
167 close(devnull);
168
169 /*
170 * The server may have MANY FD's open. We don't
171 * want to leave dangling FD's for the child process
172 * to play funky games with, so we close them.
173 */
174 fr_closefrom(STDERR_FILENO + 1);
175
176 /*
177 * Disarm the thread local destructors
178 *
179 * It's not safe to free memory between fork and exec.
180 */
182
183 /*
184 * I swear the signature for execve is wrong and should
185 * take 'char const * const argv[]'.
186 *
187 * Note: execve(), unlike system(), treats all the space
188 * delimited arguments as literals, so there's no need
189 * to perform additional escaping.
190 */
191 execve(argv[0], argv, envp);
192 printf("Failed to execute \"%s\": %s", argv[0], fr_syserror(errno)); /* fork output will be captured */
193
194 /*
195 * Where the status code is interpreted as a module rcode
196 * one is subtracted from it, to allow 0 to equal success
197 *
198 * 2 is RLM_MODULE_FAIL + 1
199 */
200 exit(2);
201}
202
203
204/** Start a process
205 *
206 * @param[out] stdin_fd pointer to int, receives the stdin file
207 * descriptor. Set to NULL and the child
208 * will have /dev/null on stdin.
209 * @param[out] stdout_fd pointer to int, receives the stdout file
210 * descriptor. Set to NULL and the child
211 * will have /dev/null on stdout.
212 * @param[out] stderr_fd pointer to int, receives the stderr file
213 * descriptor. Set to NULL and the child
214 * will have /dev/null on stderr.
215 * @param[in] cmd Command to execute. This is parsed into argv[]
216 * parts, then each individual argv part is
217 * xlat'ed.
218 * @param[in] request Current request
219 * @param[in] exec_wait set to true to read from or write to child.
220 * @param[in] input_pairs list of value pairs - these will be put into
221 * the environment variables of the child.
222 * @param[in] shell_escape values before passing them as arguments.
223 * @return
224 * - PID of the child process.
225 * - -1 on failure.
226 */
227pid_t radius_start_program_legacy(int *stdin_fd, int *stdout_fd, int *stderr_fd,
228 char const *cmd, request_t *request, bool exec_wait,
229 fr_pair_list_t *input_pairs, bool shell_escape)
230{
231 int stdin_pipe[2] = {-1, -1};
232 int stdout_pipe[2] = {-1, -1};
233 int stderr_pipe[2] = {-1, -1};
234 pid_t pid;
235 int argc;
236 int i;
237 char const **argv_p;
238 char *argv[MAX_ARGV], **argv_start = argv;
239 char argv_buf[4096];
240#define MAX_ENVP 1024
241 char **envp;
242
243 /*
244 * Stupid array decomposition...
245 *
246 * If we do memcpy(&argv_p, &argv, sizeof(argv_p)) src ends up being a char **
247 * pointing to the value of the first element.
248 */
249 memcpy(&argv_p, &argv_start, sizeof(argv_p));
250 argc = rad_expand_xlat(request, cmd, MAX_ARGV, argv_p, true, sizeof(argv_buf), argv_buf);
251 if (argc <= 0) {
252 ROPTIONAL(RPEDEBUG, PERROR, "Invalid command '%s'", cmd);
253 return -1;
254 }
255
256 if (DEBUG_ENABLED3) {
257 for (i = 0; i < argc; i++) DEBUG3("arg[%d] %s", i, argv[i]);
258 }
259
260 /*
261 * Open a pipe for child/parent communication, if necessary.
262 */
263 if (exec_wait) {
264 if (stdin_fd) {
265 if (pipe(stdin_pipe) != 0) {
266 ERROR("Couldn't open pipe to child: %s", fr_syserror(errno));
267 return -1;
268 }
269 }
270 if (stdout_fd) {
271 if (pipe(stdout_pipe) != 0) {
272 ERROR("Couldn't open pipe from child: %s", fr_syserror(errno));
273 /* safe because these either need closing or are == -1 */
274 error:
275 close(stdin_pipe[0]);
276 close(stdin_pipe[1]);
277 close(stdout_pipe[0]);
278 close(stdout_pipe[1]);
279 close(stderr_pipe[0]);
280 close(stderr_pipe[1]);
281 return -1;
282 }
283 }
284 if (stderr_fd) {
285 if (pipe(stderr_pipe) != 0) {
286 ERROR("Couldn't open pipe from child: %s", fr_syserror(errno));
287
288 goto error;
289 }
290 }
291 }
292
293 MEM(envp = talloc_zero_array(request, char *, MAX_ENVP));
294 envp[0] = NULL;
295 if (input_pairs) exec_pair_to_env_legacy(request, input_pairs, envp, MAX_ENVP, shell_escape);
296
297 pid = fork();
298 if (pid == 0) {
299 exec_child_legacy(request, argv, envp, exec_wait, stdin_pipe, stdout_pipe, stderr_pipe);
300 }
301
302 /*
303 * Free child environment variables
304 */
305 talloc_free(envp);
306
307 /*
308 * Parent process.
309 */
310 if (pid < 0) {
311 ERROR("Couldn't fork %s: %s", argv[0], fr_syserror(errno));
312 if (exec_wait) goto error;
313 }
314
315 /*
316 * We're done. Do any necessary cleanups.
317 */
318 if (exec_wait) {
319 /*
320 * Close the ends of the pipe(s) the child is using
321 * return the ends of the pipe(s) our caller wants
322 *
323 */
324 if (stdin_fd) {
325 *stdin_fd = stdin_pipe[1];
326 close(stdin_pipe[0]);
327 }
328 if (stdout_fd) {
330 close(stdout_pipe[1]);
331 }
332 if (stderr_fd) {
334 close(stderr_pipe[1]);
335 }
336 } else {
338
339 (void) fr_event_pid_wait(el, el, NULL, pid, NULL, NULL);
340 }
341
342 return pid;
343}
344
345
346/** Read from the child process.
347 *
348 * @param fd file descriptor to read from.
349 * @param pid pid of child, will be reaped if it dies.
350 * @param timeout amount of time to wait, in seconds.
351 * @param answer buffer to write into.
352 * @param left length of buffer.
353 * @return
354 * - -1 on failure.
355 * - Length of output.
356 */
357int radius_readfrom_program_legacy(int fd, pid_t pid, fr_time_delta_t timeout, char *answer, int left)
358{
359 int done = 0;
360 int status;
361 fr_time_t start;
362
363 fr_nonblock(fd);
364
365 /*
366 * Minimum timeout period is one section
367 */
368 if (fr_time_delta_unwrap(timeout) < NSEC) timeout = fr_time_delta_from_sec(1);
369
370 /*
371 * Read from the pipe until we doesn't get any more or
372 * until the message is full.
373 */
374 start = fr_time();
375 while (1) {
376 int rcode;
377 fd_set fds;
378 fr_time_delta_t elapsed;
379
380 FD_ZERO(&fds);
381 FD_SET(fd, &fds);
382
383 elapsed = fr_time_sub(fr_time(), start);
384 if (fr_time_delta_gteq(elapsed, timeout)) goto too_long;
385
386 rcode = select(fd + 1, &fds, NULL, NULL, &fr_time_delta_to_timeval(fr_time_delta_sub(timeout, elapsed)));
387 if (rcode == 0) {
388 too_long:
389 DEBUG("Child PID %u is taking too much time: forcing failure and killing child.", pid);
390 kill(pid, SIGTERM);
391 close(fd); /* should give SIGPIPE to child, too */
392
393 /*
394 * Clean up the child entry.
395 */
396 waitpid(pid, &status, 0);
397 return -1;
398 }
399 if (rcode < 0) {
400 if (errno == EINTR) continue;
401 break;
402 }
403
404#ifdef O_NONBLOCK
405 /*
406 * Read as many bytes as possible. The kernel
407 * will return the number of bytes available.
408 */
409 status = read(fd, answer + done, left);
410#else
411 /*
412 * There's at least 1 byte ready: read it.
413 * This is a terrible hack for non-blocking IO.
414 */
415 status = read(fd, answer + done, 1);
416#endif
417
418 /*
419 * Nothing more to read: stop.
420 */
421 if (status == 0) {
422 break;
423 }
424
425 /*
426 * Error: See if we have to continue.
427 */
428 if (status < 0) {
429 /*
430 * We were interrupted: continue reading.
431 */
432 if (errno == EINTR) {
433 continue;
434 }
435
436 /*
437 * There was another error. Most likely
438 * The child process has finished, and
439 * exited.
440 */
441 break;
442 }
443
444 done += status;
445 left -= status;
446 if (left <= 0) break;
447 }
448
449 /* Strip trailing new lines */
450 while ((done > 0) && (answer[done - 1] == '\n')) {
451 answer[--done] = '\0';
452 }
453
454 return done;
455}
456
457/** Execute a program.
458 *
459 * @param[out] out buffer to append plaintext (non valuepair) output.
460 * @param[in] outlen length of out buffer.
461 * @param[in] request Current request (may be NULL).
462 * @param[in] cmd Command to execute. This is parsed into argv[] parts, then each individual argv
463 * part is xlat'ed.
464 * @param[in] input_pairs list of value pairs - these will be available in the environment of the
465 * child.
466 * @param[in] exec_wait set to 1 if you want to read from or write to child.
467 * @param[in] shell_escape values before passing them as arguments.
468 * @param[in] timeout amount of time to wait, in seconds.
469 * @return
470 * - 0 if exec_wait==0.
471 * - exit code if exec_wait!=0.
472 * - -1 on failure.
473 */
474int radius_exec_program_legacy(char *out, size_t outlen,
475 request_t *request, char const *cmd, fr_pair_list_t *input_pairs,
476 bool exec_wait, bool shell_escape, fr_time_delta_t timeout)
477{
478 pid_t pid;
479 int stdout_pipe;
480 pid_t child_pid;
481 int status;
482 ssize_t len;
483 char answer[4096];
484
485 RDEBUG2("Executing: %s", cmd);
486
487 if (out) *out = '\0';
488
489 pid = radius_start_program_legacy(NULL, &stdout_pipe, NULL, cmd, request, exec_wait, input_pairs, shell_escape);
490 if (pid < 0) {
491 return -1;
492 }
493
494 if (!exec_wait) {
495 return 0;
496 }
497
498 len = radius_readfrom_program_legacy(stdout_pipe, pid, timeout, answer, sizeof(answer));
499 if (len < 0) {
500 /*
501 * Failure - radius_readfrom_program_legacy will
502 * have called close(stdout_pipe) for us
503 */
504 RERROR("Failed to read from child output");
505 return -1;
506
507 }
508 answer[len] = '\0';
509
510 /*
511 * Make sure that the writer can't block while writing to
512 * a pipe that no one is reading from anymore.
513 */
515
516 if (len == 0) {
517 goto wait;
518 }
519
520 if (out) {
521 /*
522 * We've not been told to extract output pairs,
523 * just copy the programs output to the out
524 * buffer.
525 */
526 strlcpy(out, answer, outlen);
527 }
528
529wait:
530 child_pid = waitpid(pid, &status, 0);
531 if (child_pid == 0) {
532 RERROR("Timeout waiting for child");
533
534 return -2;
535 }
536
537 if (child_pid == pid) {
538 if (WIFEXITED(status)) {
539 status = WEXITSTATUS(status);
540 if (status != 0) {
541 RERROR("Program returned code (%d) and output \"%pV\"", status,
542 fr_box_strvalue_len(answer, len));
543 } else {
544 RDEBUG2("Program returned code (%d) and output \"%pV\"", status,
545 fr_box_strvalue_len(answer, len));
546 }
547
548 return status;
549 }
550 }
551
552 RERROR("Abnormal child exit: %s", fr_syserror(errno));
553
554 return -1;
555}
static int const char char buffer[256]
Definition acutest.h:576
int n
Definition acutest.h:577
#define fr_atexit_thread_local_disarm_all(...)
Definition atexit.h:232
#define RCSID(id)
Definition build.h:483
#define NEVER_RETURNS
Should be placed before the function return type.
Definition build.h:313
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition dcursor.h:288
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:41
#define DEBUG(fmt,...)
Definition dhcpclient.c:39
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2400
fr_dict_t const * fr_dict_internal(void)
Definition dict_util.c:4610
fr_dict_attr_t const * fr_dict_attr_child_by_num(fr_dict_attr_t const *parent, unsigned int attr)
Check if a child attribute exists in a parent using an attribute number.
Definition dict_util.c:3328
#define fr_event_pid_wait(...)
Definition event.h:265
int radius_exec_program_legacy(char *out, size_t outlen, request_t *request, char const *cmd, fr_pair_list_t *input_pairs, bool exec_wait, bool shell_escape, fr_time_delta_t timeout)
Execute a program.
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)
Start a process.
#define MAX_ENVP
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 radius_readfrom_program_legacy(int fd, pid_t pid, fr_time_delta_t timeout, char *answer, int left)
Read from the child process.
#define MAX_ARGV
Definition exec_legacy.c:33
static void exec_pair_to_env_legacy(request_t *request, fr_pair_list_t *input_pairs, char **envp, size_t envlen, bool shell_escape)
Definition exec_legacy.c:35
#define fr_closefrom
Definition exec_priv.h:81
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
Definition interpret.c:1764
#define PERROR(_fmt,...)
Definition log.h:228
#define DEBUG3(_fmt,...)
Definition log.h:266
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition log.h:528
#define RERROR(fmt,...)
Definition log.h:298
#define RPEDEBUG(fmt,...)
Definition log.h:376
#define DEBUG_ENABLED3
True if global debug level 1-3 messages are enabled.
Definition log.h:259
int rad_expand_xlat(request_t *request, char const *cmd, int max_argc, char const *argv[], bool can_fail, size_t argv_buflen, char *argv_buf)
Split string into words and expand each one.
Definition util.c:599
waitpid(reap->pid_ev->pid, &status, 0)
talloc_free(reap)
Stores all information relating to an event list.
Definition event.c:411
static FILE * devnull
File handle for /dev/null.
Definition log.c:59
static int stdout_fd
The original unmolested stdout file descriptor.
Definition log.c:51
static int stdout_pipe[2]
Pipe we use to transport stdout data.
Definition log.c:56
static int stderr_pipe[2]
Pipe we use to transport stderr data.
Definition log.c:57
static int stderr_fd
The original unmolested stderr file descriptor.
Definition log.c:50
long int ssize_t
unsigned char uint8_t
int fr_nonblock(UNUSED int fd)
Definition misc.c:293
static bool done
Definition radclient.c:80
#define RDEBUG2(fmt,...)
Definition radclient.h:54
#define RDEBUG_ENABLED()
Definition radclient.h:49
#define WIFEXITED(stat_val)
Definition radiusd.c:72
#define WEXITSTATUS(stat_val)
Definition radiusd.c:69
#define FR_SBUFF_OUT(_start, _len_or_end)
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
fr_pair_t * vp
#define fr_time()
Allow us to arbitrarily manipulate time.
Definition state_test.c:8
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition pair.h:69
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition talloc.c:445
static int64_t fr_time_delta_unwrap(fr_time_delta_t time)
Definition time.h:154
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition time.h:590
#define fr_time_delta_to_timeval(_delta)
Convert a delta to a timeval.
Definition time.h:656
#define NSEC
Definition time.h:379
static fr_time_delta_t fr_time_delta_sub(fr_time_delta_t a, fr_time_delta_t b)
Definition time.h:261
#define fr_time_delta_gteq(_a, _b)
Definition time.h:284
#define fr_time_sub(_a, _b)
Subtract one time from another.
Definition time.h:229
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
@ T_BARE_WORD
Definition token.h:120
@ T_DOUBLE_QUOTED_STRING
Definition token.h:121
close(uq->fd)
static fr_event_list_t * el
#define fr_pair_dcursor_by_da_init(_cursor, _list, _da)
Initialise a cursor that will return only attributes matching the specified fr_dict_attr_t.
Definition pair.h:628
fr_pair_t * fr_pair_list_next(fr_pair_list_t const *list, fr_pair_t const *item))
Get the next item in a valuepair list after a specific entry.
Definition pair_inline.c:70
ssize_t fr_pair_print_value_quoted(fr_sbuff_t *out, fr_pair_t const *vp, fr_token_t quote)
Print the value of an attribute to a string.
Definition pair_print.c:53
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
Definition pair_inline.c:43
#define fr_box_strvalue_len(_val, _len)
Definition value.h:286
static size_t char ** out
Definition value.h:997