The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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  */
25 RCSID("$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 
35 static 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;
42  fr_pair_t *vp;
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  */
105 static 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  */
227 pid_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) {
329  *stdout_fd = stdout_pipe[0];
330  close(stdout_pipe[1]);
331  }
332  if (stderr_fd) {
333  *stderr_fd = stderr_pipe[0];
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  */
357 int 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  */
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  */
474 int 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 
529 wait:
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:574
int n
Definition: acutest.h:577
#define fr_atexit_thread_local_disarm_all(...)
Definition: atexit.h:232
#define RCSID(id)
Definition: build.h:444
#define NEVER_RETURNS
Should be placed before the function return type.
Definition: build.h:311
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition: dcursor.h:287
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
#define DEBUG(fmt,...)
Definition: dhcpclient.c:39
static fr_time_delta_t timeout
Definition: dhcpclient.c:54
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition: dict_util.c:1997
fr_dict_t const * fr_dict_internal(void)
Definition: dict_util.c:4204
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:2925
#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.
Definition: exec_legacy.c:474
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.
Definition: exec_legacy.c:227
#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])
Definition: exec_legacy.c:105
int radius_readfrom_program_legacy(int fd, pid_t pid, fr_time_delta_t timeout, char *answer, int left)
Read from the child process.
Definition: exec_legacy.c:357
#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:1745
#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:597
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:58
static int stdout_fd
The original unmolested stdout file descriptor.
Definition: log.c:50
static int stdout_pipe[2]
Pipe we use to transport stdout data.
Definition: log.c:55
static int stderr_pipe[2]
Pipe we use to transport stderr data.
Definition: log.c:56
static int stderr_fd
The original unmolested stderr file descriptor.
Definition: log.c:49
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
int fr_nonblock(UNUSED int fd)
Definition: misc.c:284
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
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
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:333
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:588
#define fr_time_delta_to_timeval(_delta)
Convert a delta to a timeval.
Definition: time.h:654
#define NSEC
Definition: time.h:377
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:282
#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
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_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:627
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:40
#define fr_box_strvalue_len(_val, _len)
Definition: value.h:279
static size_t char ** out
Definition: value.h:984