Coverage Report

Created: 2026-03-31 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/server/exec.c
Line
Count
Source
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: 14bb1a11c213ce1e2dc1226f354533ddbee6ee1d $
19
 *
20
 * @file src/lib/server/exec.c
21
 * @brief Execute external programs.
22
 *
23
 * @copyright 2022-2023 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24
 * @copyright 2000-2004,2006 The FreeRADIUS server project
25
 */
26
RCSID("$Id: 14bb1a11c213ce1e2dc1226f354533ddbee6ee1d $")
27
28
#include <stdint.h>
29
30
#include <freeradius-devel/server/log.h>
31
#include <freeradius-devel/server/exec.h>
32
#include <freeradius-devel/server/exec_priv.h>
33
#include <freeradius-devel/server/util.h>
34
35
#define MAX_ENVP 1024
36
37
static _Thread_local char *env_exec_arr[MAX_ENVP];  /* Avoid allocing 8k on the stack */
38
39
/** Flatten a list into individual "char *" argv-style array
40
 *
41
 * @param[in] ctx to allocate boxes in.
42
 * @param[out] argv_p where output strings go
43
 * @param[in] in  boxes to flatten
44
 * @return
45
 *  - >= 0 number of array elements in argv
46
 *  - <0 on error
47
 */
48
int fr_exec_value_box_list_to_argv(TALLOC_CTX *ctx, char ***argv_p, fr_value_box_list_t const *in)
49
0
{
50
0
  char      **argv;
51
0
  unsigned int    i = 0;
52
0
  size_t      argc = fr_value_box_list_num_elements(in);
53
0
  fr_value_box_t const  *first;
54
55
  /*
56
   *  Check that we're not trying to run a program from
57
   *  a tainted source.
58
   */
59
0
  first = fr_value_box_list_head(in);
60
0
  if (first->type == FR_TYPE_GROUP) first = fr_value_box_list_head(&first->vb_group);
61
0
  if (!first) {
62
0
    fr_strerror_const("No program to run");
63
0
    return -1;
64
0
  }
65
0
  if (first->tainted) {
66
0
    fr_strerror_printf("Program to run comes from tainted source - %pV", first);
67
0
    return -1;
68
0
  }
69
70
0
  argv = talloc_zero_array(ctx, char *, argc + 1);
71
0
  if (!argv) return -1;
72
73
0
  fr_value_box_list_foreach(in, vb) {
74
    /*
75
     *  Print the children of each group into the argv array.
76
     */
77
0
    argv[i] = fr_value_box_list_aprint(argv, &vb->vb_group, NULL, NULL);
78
0
    if (!argv[i]) {
79
0
      talloc_free(argv);
80
0
      return -1;
81
0
    }
82
0
    i++;
83
0
  }
84
85
0
  *argv_p = argv;
86
87
0
  return argc;
88
0
}
89
90
/** Print debug information showing the arguments and environment for a process
91
 *
92
 * @param[in] request   The current request, may be NULL.
93
 * @param[in] argv_in   arguments to pass to process.
94
 * @param[in] env_in    environment to pass to process.
95
 * @param[in] env_inherit print debug for the environment from the environment.
96
 */
97
static inline CC_HINT(always_inline) void exec_debug(request_t *request, char **argv_in, char **env_in, bool env_inherit)
98
0
{
99
0
  char **p;
100
101
0
  if (argv_in) for (p = argv_in; *p; p++) ROPTIONAL(RDEBUG3, DEBUG3, "arg[%d] %s", (unsigned int)(p - argv_in), *p);
102
0
  if (env_in) for (p = env_in; *p; p++) ROPTIONAL(RDEBUG3, DEBUG3, "export %s", *p);
103
0
  if (env_inherit) for (p = environ; *p; p++) ROPTIONAL(RDEBUG3, DEBUG3, "export %s", *p);
104
0
}
105
106
/** Convert pairs from a request and a list of pairs into environmental variables
107
 *
108
 * @param[out] env_p    Where to write an array of \0 terminated strings.
109
 * @param[in] env_len   Length of env_p.
110
 * @param[out] env_sbuff  To write environmental variables too. Each variable
111
 *        will be written to the buffer, and separated with
112
 *        a '\0'.
113
 * @param[in] env_m   an array of markers of the same length as env_len.
114
 * @param[in] request   Will look for &control.Exec-Export items to convert to
115
 *            env vars.
116
 * @param[in] env_pairs   Other items to convert to environmental variables.
117
 *        The dictionary attribute name will be converted to
118
 *        uppercase, and all '-' converted to '_' and will form
119
 *        the variable name.
120
 * @param[in] env_escape  Wrap string values in double quotes, and apply doublequote
121
 *        escaping to all environmental variable values.
122
 * @return
123
 *      - The number of environmental variables created.
124
 *  - -1 on failure.
125
 */
126
static inline CC_HINT(nonnull(1,3,4,5)) CC_HINT(always_inline)
127
int exec_pair_to_env(char **env_p, size_t env_len,
128
         fr_sbuff_t *env_sbuff, fr_sbuff_marker_t env_m[],
129
         request_t *request, fr_pair_list_t *env_pairs, bool env_escape)
130
0
{
131
0
  char      *p;
132
0
  size_t      i, j;
133
0
  fr_dcursor_t    cursor;
134
0
  fr_dict_attr_t const  *da;
135
0
  fr_sbuff_t    sbuff = FR_SBUFF_BIND_CURRENT(env_sbuff);
136
137
0
  if (!env_pairs) {
138
0
    env_p[0] = NULL;
139
0
    return 0;
140
0
  }
141
142
  /*
143
   *  Set up the environment variables in the
144
   *  parent, so we don't call libc functions that
145
   *  hold mutexes.  They might be locked when we fork,
146
   *  and will remain locked in the child.
147
   */
148
0
  i = 0;
149
0
  fr_pair_list_foreach_leaf(env_pairs, vp) {
150
0
    fr_sbuff_marker(&env_m[i], &sbuff);
151
152
0
        if (fr_sbuff_in_strcpy(&sbuff, vp->da->name) <= 0) {
153
0
          fr_strerror_printf("Out of buffer space adding attribute name");
154
0
          return -1;
155
0
        }
156
157
    /*
158
     *  POSIX only allows names to contain
159
     *  uppercase chars, digits, and
160
     *  underscores.  Digits are not allowed
161
     *  for the first char.
162
     */
163
0
    p = fr_sbuff_current(&env_m[i]);
164
0
    if (isdigit((uint8_t)*p)) *p++ = '_';
165
0
    for (; p < fr_sbuff_current(&sbuff); p++) {
166
0
      if (isalpha((uint8_t)*p)) *p = toupper((uint8_t) *p);
167
0
      else if (*p == '-') *p = '_';
168
0
      else if (isdigit((uint8_t)*p)) goto next;
169
0
      else *p = '_';
170
0
    }
171
172
0
    if (fr_sbuff_in_char(&sbuff, '=') <= 0) {
173
0
      fr_strerror_printf("Out of buffer space");
174
0
      return -1;
175
0
    }
176
177
0
    if (env_escape) {
178
0
      if (fr_value_box_print_quoted(&sbuff, &vp->data, T_DOUBLE_QUOTED_STRING) < 0) {
179
0
        fr_strerror_printf("Out of buffer space adding attribute value for %pV", &vp->data);
180
0
        return -1;
181
0
      }
182
0
    } else {
183
      /*
184
       *  This can be zero length for empty strings
185
       *
186
       *  Note we don't do double quote escaping here,
187
       *  we just escape unprintable chars.
188
       *
189
       *  Environmental variable values are not
190
       *  restricted we likely wouldn't need to do
191
       *  any escaping if we weren't dealing with C
192
       *  strings.
193
       *
194
       *  If we end up passing binary data through
195
       *  then the user can unescape the octal
196
       *  sequences on the other side.
197
       *
198
       *  We unfortunately still need to escape '\'
199
       *  because of this.
200
       */
201
0
      if (fr_value_box_print(&sbuff, &vp->data, &fr_value_escape_unprintables) < 0) {
202
0
        fr_strerror_printf("Out of buffer space adding attribute value for %pV", &vp->data);
203
0
        return -1;
204
0
      }
205
0
    }
206
0
    if (fr_sbuff_in_char(&sbuff, '\0') <= 0) {
207
0
      fr_strerror_printf("Out of buffer space");
208
0
      return -1;
209
0
    }
210
211
0
  next:
212
0
    i++;
213
0
    if (i == (env_len - 1)) break;
214
0
  }
215
216
  /*
217
   *  Do this as a separate step so that if env_sbuff
218
   *  is extended at any point during the conversion
219
   *  the sbuff we use is the final one.
220
   */
221
0
  for (j = 0; j < i; j++) {
222
0
    env_p[j] = fr_sbuff_current(&env_m[j]);
223
0
  }
224
225
0
  da = fr_dict_attr_child_by_num(fr_dict_root(fr_dict_internal()), FR_EXEC_EXPORT);
226
0
  if (da) {
227
0
    fr_pair_t *vp;
228
229
0
    for (vp = fr_pair_dcursor_by_da_init(&cursor, &request->control_pairs, da);
230
0
         vp;
231
0
         vp = fr_dcursor_next(&cursor)) {
232
0
      env_p[i++] = UNCONST(char *, vp->vp_strvalue);
233
0
    }
234
0
  }
235
236
0
  if (unlikely(i == (env_len - 1))) {
237
0
    fr_strerror_printf("Out of space for environmental variables");
238
0
    return -1;
239
0
  }
240
241
  /*
242
   *  NULL terminate for execve
243
   */
244
0
  env_p[i] = NULL;
245
246
0
  return i;
247
0
}
248
249
/** Convert env_pairs into an array of environmental variables using thread local buffers
250
 *
251
 * @param[in] request   Will be searched for control.Exec-Export pairs.
252
 * @param[in] env_pairs   env_pairs to put into into the environment.  May be NULL.
253
 * @param[in] env_escape  Wrap string values in double quotes, and apply doublequote
254
 *        escaping to all environmental variable values.
255
 * @return
256
 *  - An array of environmental variable definitions, valid until the next call
257
 *    to fr_exec_pair_to_env within the same thread.
258
 *  - NULL on error.  Error retrievable fr_strerror().
259
 */
260
char **fr_exec_pair_to_env(request_t *request, fr_pair_list_t *env_pairs, bool env_escape)
261
0
{
262
0
  static _Thread_local char *env_arr[MAX_ENVP];       /* Avoid allocing 8k on the stack */
263
0
  static _Thread_local char env_buff[NUM_ELEMENTS(env_arr) * 128];  /* Avoid allocing 128k on the stack */
264
0
  static _Thread_local fr_sbuff_marker_t env_m[NUM_ELEMENTS(env_arr)];
265
266
0
  if (exec_pair_to_env(env_arr, NUM_ELEMENTS(env_arr),
267
0
           &FR_SBUFF_OUT(env_buff, sizeof(env_buff)), env_m,
268
0
           request, env_pairs, env_escape) < 0) return NULL;
269
270
0
  return env_arr;
271
0
}
272
273
/** Start a child process
274
 *
275
 * We try to be fail-safe here. So if ANYTHING goes wrong, we exit with status 1.
276
 *
277
 * @param[in] argv    array of arguments to pass to child.
278
 * @param[in] envp    array of environment variables in form `<attr>=<val>`
279
 * @param[in] exec_wait   if true, redirect child process' stdin, stdout, stderr
280
 *        to the pipes provided, redirecting any to /dev/null
281
 *        where no pipe was provided.  If false redirect
282
 *        stdin, and stdout to /dev/null.
283
 * @param[in] debug   If true, and !exec_wait, don't molest stderr.
284
 * @param[in] stdin_pipe  the pipe used to write data to the process. STDIN will
285
 *        be set to stdin_pipe[0], stdin_pipe[1] will be closed.
286
 * @param[in] stdout_pipe the pipe used to read data from the process.
287
 *        STDOUT will be set to stdout_pipe[1], stdout_pipe[0]
288
 *        will be closed.
289
 * @param[in] stderr_pipe the pipe used to read error text from the process.
290
 *        STDERR will be set to stderr_pipe[1], stderr_pipe[0]
291
 *        will be closed.
292
 */
293
static NEVER_RETURNS void exec_child(char **argv, char **envp,
294
             bool exec_wait, bool debug,
295
             int stdin_pipe[static 2], int stdout_pipe[static 2], int stderr_pipe[static 2])
296
0
{
297
0
  int devnull;
298
299
  /*
300
   *  Open STDIN to /dev/null
301
   */
302
0
  devnull = open("/dev/null", O_RDWR);
303
0
  if (devnull < 0) {
304
0
    fprintf(stderr, "Failed opening /dev/null: %s\n", fr_syserror(errno));
305
306
    /*
307
     *  Where the status code is interpreted as a module rcode
308
     *  one is subtracted from it, to allow 0 to equal success
309
     *
310
     *  2 is RLM_MODULE_FAIL + 1
311
     */
312
0
    exit(2);
313
0
  }
314
315
  /*
316
   *  Only massage the pipe handles if the parent
317
   *  has created them.
318
   */
319
0
  if (exec_wait) {
320
0
    if (stdin_pipe[1] >= 0) {
321
0
      close(stdin_pipe[1]);
322
0
      dup2(stdin_pipe[0], STDIN_FILENO);
323
0
    } else {
324
0
      dup2(devnull, STDIN_FILENO);
325
0
    }
326
327
0
    if (stdout_pipe[1] >= 0) {
328
0
      close(stdout_pipe[0]);
329
0
      dup2(stdout_pipe[1], STDOUT_FILENO);
330
0
    } else {
331
0
      dup2(devnull, STDOUT_FILENO);
332
0
    }
333
334
0
    if (stderr_pipe[1] >= 0) {
335
0
      close(stderr_pipe[0]);
336
0
      dup2(stderr_pipe[1], STDERR_FILENO);
337
0
    } else {
338
0
      dup2(devnull, STDERR_FILENO);
339
0
    }
340
0
  } else { /* no pipe, STDOUT should be /dev/null */
341
0
    dup2(devnull, STDIN_FILENO);
342
0
    dup2(devnull, STDOUT_FILENO);
343
344
    /*
345
     *  If we're not debugging, then we can't do
346
     *  anything with the error messages, so we throw
347
     *  them away.
348
     *
349
     *  If we are debugging, then we want the error
350
     *  messages to go to the STDERR of the server.
351
     */
352
0
    if (!debug) dup2(devnull, STDERR_FILENO);
353
0
  }
354
355
0
  close(devnull);
356
357
  /*
358
   *  The server may have MANY FD's open.  We don't
359
   *  want to leave dangling FD's for the child process
360
   *  to play funky games with, so we close them.
361
   */
362
0
  fr_closefrom(STDERR_FILENO + 1);
363
364
  /*
365
   *  Disarm the thread local destructors
366
   *
367
   *  It's not safe to free memory between fork and exec.
368
   */
369
0
  fr_atexit_thread_local_disarm_all();
370
371
  /*
372
   *  Disarm the global destructors for the same reason
373
   */
374
0
  fr_atexit_global_disarm_all();
375
376
  /*
377
   *  I swear the signature for execve is wrong and should
378
   *  take 'char const * const argv[]'.
379
   *
380
   *  Note: execve(), unlike system(), treats all the space
381
   *  delimited arguments as literals, so there's no need
382
   *  to perform additional escaping.
383
   */
384
0
  execve(argv[0], argv, envp);
385
0
  printf("Failed to execute \"%s\": %s", argv[0], fr_syserror(errno)); /* fork output will be captured */
386
387
  /*
388
   *  Where the status code is interpreted as a module rcode
389
   *  one is subtracted from it, to allow 0 to equal success
390
   *
391
   *  2 is RLM_MODULE_FAIL + 1
392
   */
393
0
  exit(2);
394
0
}
395
396
/** Merge extra environmental variables and potentially the inherited environment
397
 *
398
 * @param[in] env_in    to merge.
399
 * @param[in] env_inherit inherite environment from radiusd.
400
 * @return merged environmental variables.
401
 */
402
static
403
char **exec_build_env(char **env_in, bool env_inherit)
404
0
{
405
0
  size_t num_in, num_environ;
406
407
  /*
408
   *  Not inheriting the radiusd environment, just return whatever we were given.
409
   */
410
0
  if (!env_inherit) {
411
0
    return env_in;
412
0
  }
413
414
  /*
415
   *  No additional environment variables, just return the ones from radiusd.
416
   */
417
0
  if (!env_in) return environ;
418
419
0
  for (num_environ = 0; environ[num_environ] != NULL; num_environ++) {
420
    /* nothing */
421
0
  }
422
423
  /*
424
   *  No room to copy anything after the environment variables.
425
   */
426
0
  if (((num_environ + 1) >= NUM_ELEMENTS(env_exec_arr))) {
427
0
    return environ;
428
0
  }
429
430
  /*
431
   *  Copy the radiusd environment to the local array
432
   */
433
0
  memcpy(env_exec_arr, environ, (num_environ + 1) * sizeof(environ[0]));
434
435
0
  for (num_in = 0; env_in[num_in] != NULL; num_in++) {
436
0
    if ((num_environ + num_in + 1) >= NUM_ELEMENTS(env_exec_arr)) break;
437
0
  }
438
439
0
  memcpy(env_exec_arr + num_environ, env_in, num_in * sizeof(environ[0]));
440
0
  env_exec_arr[num_environ + num_in] = NULL;
441
442
0
  return env_exec_arr;
443
0
}
444
445
/** Execute a program without waiting for the program to finish.
446
 *
447
 * @param[in] el    event list to insert reaper child into.
448
 * @param[in] argv_in   arg[0] is the path to the program, arg[...] are arguments
449
 *        to pass to the program.
450
 * @param[in] env_in    any additional environmental variables to pass to the program.
451
 * @param[in] env_inherit Inherit the environment from the current process.
452
 *        This will be merged with any variables from env_pairs.
453
 * @param[in] debug   If true, STDERR will be left open and pointing to the stderr
454
 *        descriptor of the parent.
455
 * @return
456
 *  - <0 on error.  Error retrievable fr_strerror().
457
 *  - 0 on success
458
 *
459
 *  @todo - maybe take an fr_dcursor_t instead of env_pairs?  That
460
 *  would allow finer-grained control over the attributes to put into
461
 *  the environment.
462
 */
463
int fr_exec_fork_nowait(fr_event_list_t *el, char **argv_in, char **env_in, bool env_inherit, bool debug)
464
0
{
465
0
  char    **env;
466
0
  pid_t   pid;
467
468
0
  env = exec_build_env(env_in, env_inherit);
469
0
  pid = fork();
470
  /*
471
   *  The child never returns from calling exec_child();
472
   */
473
0
  if (pid == 0) {
474
0
    int unused[2] = { -1, -1 };
475
476
0
    exec_child(argv_in, env, false, debug, unused, unused, unused);
477
0
  }
478
479
0
  if (pid < 0) {
480
0
    fr_strerror_printf("Couldn't fork %s", argv_in[0]);
481
0
  error:
482
0
    return -1;
483
0
  }
484
485
  /*
486
   *  Ensure that we can clean up any child processes.  We
487
   *  don't want them left over as zombies.
488
   */
489
0
  if (fr_event_pid_reap(el, pid, NULL, NULL) < 0) {
490
0
    int status;
491
492
    /*
493
     *  Try and cleanup... really we have
494
     *  no idea what state things are in.
495
     */
496
0
    kill(pid, SIGKILL);
497
0
    waitpid(pid, &status, WNOHANG);
498
0
    goto error;
499
0
  }
500
501
0
  return 0;
502
0
}
503
504
/** Execute a program assuming that the caller waits for it to finish.
505
 *
506
 * The caller takes responsibility for calling waitpid() on the returned PID.
507
 *
508
 * The caller takes responsibility for reading from the returned FD,
509
 * and closing it.
510
 *
511
 * @param[out] pid_p    The PID of the child
512
 * @param[out] stdin_fd   The stdin FD of the child.
513
 * @param[out] stdout_fd  The stdout FD of the child.
514
 * @param[out] stderr_fd  The stderr FD of the child.
515
 * @param[in] argv_in   arg[0] is the path to the program, arg[...] are arguments
516
 *        to pass to the program.
517
 * @param[in] env_in    Environmental variables to pass to the program.
518
 * @param[in] env_inherit Inherit the environment from the current process.
519
 *        This will be merged with any variables from env_pairs.
520
 * @param[in] debug   If true, STDERR will be left open and pointing to the stderr
521
 *        descriptor of the parent, if no stderr_fd pointer is provided.
522
 * @return
523
 *  - <0 on error.  Error retrievable fr_strerror().
524
 *  - 0 on success.
525
 *
526
 *  @todo - maybe take an fr_dcursor_t instead of env_pairs?  That
527
 *  would allow finer-grained control over the attributes to put into
528
 *  the environment.
529
 */
530
int fr_exec_fork_wait(pid_t *pid_p,
531
          int *stdin_fd, int *stdout_fd, int *stderr_fd,
532
          char **argv_in, char **env_in, bool env_inherit, bool debug)
533
0
{
534
0
  char    **env;
535
0
  pid_t   pid;
536
0
  int   stdin_pipe[2] = {-1, -1};
537
0
  int   stderr_pipe[2] = {-1, -1};
538
0
  int   stdout_pipe[2] = {-1, -1};
539
540
0
  if (stdin_fd) {
541
0
    if (pipe(stdin_pipe) < 0) {
542
0
      fr_strerror_const("Failed opening pipe to write to child");
543
544
0
    error1:
545
0
      return -1;
546
0
    }
547
0
    if (fr_nonblock(stdin_pipe[1]) < 0) {
548
0
      fr_strerror_const("Error setting stdin to nonblock");
549
0
      goto error2;
550
0
    }
551
0
  }
552
553
0
  if (stdout_fd) {
554
0
    if (pipe(stdout_pipe) < 0) {
555
0
      fr_strerror_const("Failed opening pipe to read from child");
556
557
0
    error2:
558
0
      close(stdin_pipe[0]);
559
0
      close(stdin_pipe[1]);
560
0
      goto error1;
561
0
    }
562
0
    if (fr_nonblock(stdout_pipe[0]) < 0) {
563
0
      fr_strerror_const("Error setting stdout to nonblock");
564
0
      goto error3;
565
0
    }
566
0
  }
567
568
0
  if (stderr_fd) {
569
0
    if (pipe(stderr_pipe) < 0) {
570
0
      fr_strerror_const("Failed opening pipe to read from child");
571
572
0
    error3:
573
0
      close(stdout_pipe[0]);
574
0
      close(stdout_pipe[1]);
575
0
      goto error2;
576
0
    }
577
0
    if (fr_nonblock(stderr_pipe[0]) < 0) {
578
0
      fr_strerror_const("Error setting stderr to nonblock");
579
0
      close(stderr_pipe[0]);
580
0
      close(stderr_pipe[1]);
581
0
      goto error3;
582
0
    }
583
0
  }
584
585
0
  env = exec_build_env(env_in, env_inherit);
586
0
  pid = fork();
587
588
  /*
589
   *  The child never returns from calling exec_child();
590
   */
591
0
  if (pid == 0) exec_child(argv_in, env, true, debug, stdin_pipe, stdout_pipe, stderr_pipe);
592
0
  if (pid < 0) {
593
0
    fr_strerror_printf("Couldn't fork %s", argv_in[0]);
594
0
    *pid_p = -1;  /* Ensure the PID is set even if the caller didn't check the return code */
595
0
    goto error3;
596
0
  }
597
598
  /*
599
   *  Tell the caller the childs PID, and the FD to read from.
600
   */
601
0
  *pid_p = pid;
602
603
0
  if (stdin_fd) {
604
0
    *stdin_fd = stdin_pipe[1];
605
0
    close(stdin_pipe[0]);
606
0
  }
607
608
0
  if (stdout_fd) {
609
0
    *stdout_fd = stdout_pipe[0];
610
0
    close(stdout_pipe[1]);
611
0
  }
612
613
0
  if (stderr_fd) {
614
0
    *stderr_fd = stderr_pipe[0];
615
0
    close(stderr_pipe[1]);
616
0
  }
617
618
0
  return 0;
619
0
}
620
621
/** Similar to fr_exec_oneshot, but does not attempt to parse output
622
 *
623
 * @param[in] request   currently being processed, may be NULL.
624
 * @param[in] args    to call as a fr_value_box_list_t.  Program will
625
 *            be the first box and arguments in the subsequent boxes.
626
 * @param[in] env_pairs   list of pairs to be presented as environment variables
627
 *        to the child.
628
 * @param[in] env_escape  Wrap string values in double quotes, and apply doublequote
629
 *        escaping to all environmental variable values.
630
 * @param[in] env_inherit Inherit the environment from the current process.
631
 *        This will be merged with any variables from env_pairs.
632
 * @return
633
 *  - 0 on success.
634
 *  - -1 on error.
635
 */
636
int fr_exec_oneshot_nowait(request_t *request,
637
         fr_value_box_list_t *args, fr_pair_list_t *env_pairs,
638
         bool env_escape, bool env_inherit)
639
0
{
640
0
  char **argv = NULL;
641
0
  char **env = NULL;
642
0
  int ret;
643
644
0
  if (unlikely(fr_exec_value_box_list_to_argv(unlang_interpret_frame_talloc_ctx(request), &argv, args) < 0)) {
645
0
    RPEDEBUG("Failed converting boxes to argument strings");
646
0
    return -1;
647
0
  }
648
649
0
  if (env_pairs) {
650
0
    env = fr_exec_pair_to_env(request, env_pairs, env_escape);
651
0
    if (unlikely(env == NULL)) {
652
0
      RPEDEBUG("Failed creating environment pairs");
653
0
      return -1;
654
0
    }
655
0
  }
656
657
0
  if (RDEBUG_ENABLED3) exec_debug(request, argv, env, env_inherit);
658
0
  ret = fr_exec_fork_nowait(unlang_interpret_event_list(request), argv, env,
659
0
                env_inherit, ROPTIONAL_ENABLED(RDEBUG_ENABLED2, DEBUG_ENABLED2));
660
0
  talloc_free(argv);
661
0
  if (unlikely(ret < 0)) RPEDEBUG("Failed executing program");
662
663
0
  return ret;
664
0
}
665
666
/** Cleans up an exec'd process on error
667
 *
668
 * This function is intended to be called at any point after a successful
669
 * #fr_exec_oneshot call in order to release resources and cleanup
670
 * zombie processes.
671
 *
672
 * @param[in] exec  state to cleanup.
673
 * @param[in] signal  If non-zero, and we think the process is still
674
 *      running, send it a signal to cause it to exit.
675
 *      The PID reaper we insert here will cleanup its
676
 *      state so it doesn't become a zombie.
677
 *
678
 */
679
void fr_exec_oneshot_cleanup(fr_exec_state_t *exec, int signal)
680
0
{
681
0
  request_t *request = exec->request;
682
0
  fr_event_list_t *el = unlang_interpret_event_list(request);
683
684
0
  if (exec->pid >= 0) {
685
0
    RDEBUG3("Cleaning up exec state for PID %u", exec->pid);
686
687
0
  } else if (exec->failed != FR_EXEC_FAIL_NONE) {
688
0
    RDEBUG3("Cleaning up failed exec");
689
0
  }
690
691
  /*
692
   *  There's still an EV_PROC event installed
693
   *  for the PID remove it (there's a destructor).
694
   */
695
0
  if (exec->ev_pid) {
696
0
    talloc_const_free(exec->ev_pid);
697
0
    fr_assert(!exec->ev_pid);  /* Should be NULLified by destructor */
698
0
  }
699
700
0
  if (exec->stdout_fd >= 0) {
701
0
    if (fr_event_fd_delete(el, exec->stdout_fd, FR_EVENT_FILTER_IO) < 0){
702
0
      RPERROR("Failed removing stdout handler");
703
0
    }
704
0
    close(exec->stdout_fd);
705
0
    exec->stdout_fd = -1;
706
0
  }
707
708
0
  if (exec->stderr_fd >= 0) {
709
0
    if (fr_event_fd_delete(el, exec->stderr_fd, FR_EVENT_FILTER_IO) < 0) {
710
0
      RPERROR("Failed removing stderr handler");
711
0
    }
712
0
    close(exec->stderr_fd);
713
0
    exec->stderr_fd = -1;
714
0
  }
715
716
0
  if (exec->pid >= 0) {
717
0
    if (signal > 0) kill(exec->pid, signal);
718
719
0
    if (unlikely(fr_event_pid_reap(el, exec->pid, NULL, NULL) < 0)) {
720
0
      int status;
721
722
0
      RPERROR("Failed setting up async PID reaper, PID %u may now be a zombie", exec->pid);
723
724
      /*
725
       *  Try and cleanup... really we have
726
       *  no idea what state things are in.
727
       */
728
0
      kill(exec->pid, SIGKILL);
729
0
      waitpid(exec->pid, &status, WNOHANG);
730
0
    }
731
0
    exec->pid = -1;
732
0
  }
733
734
0
  FR_TIMER_DELETE(&exec->ev);
735
0
}
736
737
/*
738
 *  Callback when exec has completed.  Record the status and tidy up.
739
 */
740
static void exec_reap(fr_event_list_t *el, pid_t pid, int status, void *uctx)
741
0
{
742
0
  fr_exec_state_t *exec = uctx; /* may not be talloced */
743
0
  request_t *request = exec->request;
744
0
  int   wait_status = 0;
745
0
  int   ret;
746
747
0
  if (!fr_cond_assert(pid == exec->pid)) RWDEBUG("Event PID %u and exec->pid %u do not match", pid, exec->pid);
748
749
  /*
750
   *  Reap the process.  This is needed so the processes
751
   *  don't stick around indefinitely.  libkqueue/kqueue
752
   *  does not do this for us!
753
   */
754
0
  ret = waitpid(exec->pid, &wait_status, WNOHANG);
755
0
  if (ret < 0) {
756
0
    RWDEBUG("Failed reaping PID %i: %s", exec->pid, fr_syserror(errno));
757
  /*
758
   *  Either something cleaned up the process before us
759
   *  (bad!), or the notification system is broken
760
   *  (also bad!)
761
   *
762
   *  This could be caused by 3rd party libraries.
763
   */
764
0
  } else if (ret == 0) {
765
0
    RWDEBUG("Something reaped PID %d before us!", exec->pid);
766
0
    wait_status = status;
767
0
  }
768
769
  /*
770
   *  kevent should be returning an identical status value
771
   *  to waitpid.
772
   */
773
0
  if (wait_status != status) RWDEBUG("Exit status from waitpid (%d) and kevent (%d) disagree",
774
0
             wait_status, status);
775
776
0
  if (WIFEXITED(wait_status)) {
777
0
    RDEBUG("Program exited with status code %d", WEXITSTATUS(wait_status));
778
0
    exec->status = WEXITSTATUS(wait_status);
779
0
  } else if (WIFSIGNALED(wait_status)) {
780
0
    RDEBUG("Program exited due to signal with status code %d", WTERMSIG(wait_status));
781
0
    exec->status = -WTERMSIG(wait_status);
782
0
  } else {
783
0
    RDEBUG("Program exited due to unknown status %d", wait_status);
784
0
    exec->status = -wait_status;
785
0
  }
786
0
  exec->pid = -1; /* pid_t is signed */
787
788
0
  FR_TIMER_DELETE(&exec->ev);
789
790
  /*
791
   *  Process exit notifications (EV_PROC) and file
792
   *  descriptor read events (EV_READ) can race.
793
   *
794
   *  So... If the process has exited, trigger the IO
795
   *  handlers manually.
796
   *
797
   *  This is icky, but the only other option is to
798
   *      enhance our event loop so we can look for
799
   *  pending events associated with file
800
   *  descriptors...
801
   *
802
   *  Even then we might get the file readable
803
   *  notification and the process exited notification
804
   *  in different kevent() calls on busy systems.
805
   */
806
0
  if (exec->stdout_fd >= 0) {
807
0
    fr_event_fd_t   *ef;
808
0
    fr_event_fd_cb_t  cb;
809
810
0
    ef = fr_event_fd_handle(el, exec->stdout_fd, FR_EVENT_FILTER_IO);
811
0
    if (!fr_cond_assert_msg(ef, "no event associated with processes's stdout fd (%i)",
812
0
          exec->stdout_fd)) goto close_stdout;
813
814
0
    cb = fr_event_fd_cb(ef, EVFILT_READ, 0);
815
0
    if (!fr_cond_assert_msg(cb, "no read callback associated with processes's stdout_fd (%i)",
816
0
          exec->stdout_fd)) goto close_stdout;
817
818
    /*
819
     *  Call the original read callback that
820
     *  was setup here to ensure that there's
821
     *  no pending data.
822
     */
823
0
    cb(el, exec->stdout_fd, 0, fr_event_fd_uctx(ef));
824
825
    /*
826
     *  ...and delete the event from the event
827
     *  loop.  This should also suppress the
828
     *      EVFILT_READ event if there was one.
829
     */
830
0
    (void) fr_event_fd_delete(el, exec->stdout_fd, FR_EVENT_FILTER_IO);
831
0
  close_stdout:
832
0
    close(exec->stdout_fd);
833
0
    exec->stdout_fd = -1;
834
0
  }
835
836
0
  if (exec->stderr_fd >= 0) {
837
0
    fr_event_fd_t   *ef;
838
0
    fr_event_fd_cb_t  cb;
839
840
0
    ef = fr_event_fd_handle(el, exec->stderr_fd, FR_EVENT_FILTER_IO);
841
0
    if (!fr_cond_assert_msg(ef, "no event associated with processes's stderr fd (%i)",
842
0
          exec->stderr_fd)) goto close_stderr;
843
844
0
    cb = fr_event_fd_cb(ef, EVFILT_READ, 0);
845
0
    if (!fr_cond_assert_msg(cb, "no read callback associated with processes's stderr_fd (%i)",
846
0
          exec->stderr_fd)) goto close_stderr;
847
848
0
    cb(el, exec->stderr_fd, 0, fr_event_fd_uctx(ef));
849
0
    (void) fr_event_fd_delete(el, exec->stderr_fd, FR_EVENT_FILTER_IO);
850
0
  close_stderr:
851
0
    close(exec->stderr_fd);
852
0
    exec->stderr_fd = -1;
853
0
  }
854
855
0
  unlang_interpret_mark_runnable(exec->request);
856
0
}
857
858
/*
859
 *  Callback when an exec times out.
860
 */
861
static void exec_timeout(UNUSED fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx)
862
0
{
863
0
  fr_exec_state_t *exec = uctx; /* may not be talloced */
864
0
  bool    exit_timeout;
865
866
  /*
867
   *  Some race conditions cause fr_exec_oneshot_cleanup to insert
868
   *  a new event, which calls fr_strerror_clear(), resulting in
869
   *  inconsistent error messages.
870
   *  Recording the condition to drive the error message here and
871
   *  then setting after tidying up keeps things consistent.
872
   */
873
0
  exit_timeout = (exec->stdout_fd < 0);
874
875
0
  exec->failed = FR_EXEC_FAIL_TIMEOUT;
876
0
  fr_exec_oneshot_cleanup(exec, SIGKILL);
877
878
0
  if (exit_timeout) {
879
0
    fr_strerror_const("Timeout waiting for program to exit");
880
0
  } else {
881
0
    fr_strerror_const("Timeout running program");
882
0
  }
883
884
0
  unlang_interpret_mark_runnable(exec->request);
885
0
}
886
887
/*
888
 *  Callback to read stdout from an exec into the pre-prepared extensible sbuff
889
 */
890
0
static void exec_stdout_read(UNUSED fr_event_list_t *el, int fd, int flags, void *uctx) {
891
0
  fr_exec_state_t   *exec = uctx;
892
0
  request_t   *request = exec->request;
893
0
  ssize_t     data_len, remaining;
894
0
  fr_sbuff_marker_t start_m;
895
896
0
  fr_sbuff_marker(&start_m, &exec->stdout_buff);
897
898
0
  do {
899
    /*
900
     *  Read in 128 byte chunks
901
     */
902
0
    remaining = fr_sbuff_extend_lowat(NULL, &exec->stdout_buff, 128);
903
904
    /*
905
     *  Ran out of buffer space.
906
     */
907
0
    if (unlikely(!remaining)) {
908
0
      REDEBUG("Too much output from program - killing it and failing the request");
909
910
0
    error:
911
0
      exec->failed = FR_EXEC_FAIL_TOO_MUCH_DATA;
912
0
      fr_exec_oneshot_cleanup(exec, SIGKILL);
913
0
      break;
914
0
    }
915
916
0
    data_len = read(fd, fr_sbuff_current(&exec->stdout_buff), remaining);
917
0
    if (data_len < 0) {
918
0
      if (errno == EINTR) continue;
919
920
      /*
921
       *  This can happen when the callback is called
922
       *  manually when we're reaping the process.
923
       *
924
       *  It's pretty much an identical condition to
925
       *  data_len == 0.
926
       */
927
0
      if (errno == EWOULDBLOCK) break;
928
929
0
      REDEBUG("Error reading from child program - %s", fr_syserror(errno));
930
0
      goto error;
931
0
    }
932
933
    /*
934
     *  Even if we get 0 now the process may write more data later
935
     *  before it completes, so we leave the fd handlers in place.
936
     */
937
0
    if (data_len == 0) break;
938
939
0
    fr_sbuff_advance(&exec->stdout_buff, data_len);
940
0
  } while (remaining == data_len);  /* If process returned maximum output, loop again */
941
942
0
  if (flags & EV_EOF) {
943
    /*
944
     *  We've received EOF - so the process has finished writing
945
     *  Remove event and tidy up
946
     */
947
0
    (void) fr_event_fd_delete(unlang_interpret_event_list(exec->request), fd, FR_EVENT_FILTER_IO);
948
0
    close(fd);
949
0
    exec->stdout_fd = -1;
950
951
0
    if (exec->pid < 0) {
952
      /*
953
       *  Child has already exited - unlang can resume
954
       */
955
0
      FR_TIMER_DELETE(&exec->ev);
956
0
      unlang_interpret_mark_runnable(exec->request);
957
0
    }
958
0
  }
959
960
  /*
961
   *  Only print if we got additional data
962
   */
963
0
  if (RDEBUG_ENABLED2 && fr_sbuff_behind(&start_m)) {
964
0
    RDEBUG2("pid %u (stdout) - %pV", exec->pid,
965
0
      fr_box_strvalue_len(fr_sbuff_current(&start_m), fr_sbuff_behind(&start_m)));
966
0
  }
967
968
0
  fr_sbuff_marker_release(&start_m);
969
0
}
970
971
/** Call an child program, optionally reading it's output
972
 *
973
 * @note If the caller set need_stdin = true, it is the caller's
974
 *   responsibility to close exec->std_in and remove it from any event loops
975
 *   if this function returns 0 (success).
976
 *
977
 * @param[in] ctx   to allocate events in.
978
 * @param[in,out] exec    structure holding the state of the external call.
979
 * @param[in] request   currently being processed, may be NULL.
980
 * @param[in] args    to call as a fr_value_box_list_t.  Program will
981
 *            be the first box and arguments in the subsequent boxes.
982
 * @param[in] env_pairs   list of pairs to be presented as environment variables
983
 *        to the child.
984
 * @param[in] env_escape  Wrap string values in double quotes, and apply doublequote
985
 *        escaping to all environmental variable values.
986
 * @param[in] env_inherit Inherit the environment from the current process.
987
 *        This will be merged with any variables from env_pairs.
988
 * @param[in] need_stdin  If true, allocate a pipe that will allow us to send data to the
989
 *            process.
990
 * @param[in] store_stdout  if true keep a copy of stdout in addition to logging
991
 *        it if RDEBUG_ENABLED2.
992
 * @param[in] stdout_ctx  ctx to alloc stdout data in.
993
 * @param[in] timeout   to wait for child to complete.
994
 * @return
995
 *  - 0 on success
996
 *  - -1 on failure
997
 */
998
int fr_exec_oneshot(TALLOC_CTX *ctx, fr_exec_state_t *exec, request_t *request,
999
        fr_value_box_list_t *args,
1000
        fr_pair_list_t *env_pairs, bool env_escape, bool env_inherit,
1001
        bool need_stdin,
1002
        bool store_stdout, TALLOC_CTX *stdout_ctx,
1003
        fr_time_delta_t timeout)
1004
0
{
1005
0
  int   *stdout_fd = (store_stdout || RDEBUG_ENABLED2) ? &exec->stdout_fd : NULL;
1006
0
  fr_event_list_t *el = unlang_interpret_event_list(request);
1007
0
  char    **env = NULL;
1008
0
  char    **argv;
1009
0
  int   ret;
1010
1011
0
  if (unlikely(fr_exec_value_box_list_to_argv(unlang_interpret_frame_talloc_ctx(request), &argv, args) < 0)) {
1012
0
    RPEDEBUG("Failed converting boxes to argument strings");
1013
0
    return -1;
1014
0
  }
1015
1016
0
  if (env_pairs) {
1017
0
    env = fr_exec_pair_to_env(request, env_pairs, env_escape);
1018
0
    if (unlikely(!env)) {
1019
0
      RPEDEBUG("Failed creating environment pairs");
1020
0
      return -1;
1021
0
    }
1022
0
  }
1023
1024
0
  if (RDEBUG_ENABLED3) exec_debug(request, argv, env, env_inherit);
1025
0
  *exec = (fr_exec_state_t){
1026
0
    .request = request,
1027
0
    .env_pairs = env_pairs,
1028
0
    .pid = -1,
1029
0
    .stdout_fd = -1,
1030
0
    .stderr_fd = -1,
1031
0
    .stdin_fd = -1,
1032
0
    .status = -1,       /* default to program didn't work */
1033
0
    .stdin_used = need_stdin,
1034
0
    .stdout_used = store_stdout,
1035
0
    .stdout_ctx = stdout_ctx
1036
0
  };
1037
0
  ret = fr_exec_fork_wait(&exec->pid,
1038
0
            exec->stdin_used ? &exec->stdin_fd : NULL,
1039
0
            stdout_fd, &exec->stderr_fd,
1040
0
            argv, env,
1041
0
            env_inherit, ROPTIONAL_ENABLED(RDEBUG_ENABLED2, DEBUG_ENABLED2));
1042
0
  talloc_free(argv);
1043
0
  if (ret < 0) {
1044
0
  fail:
1045
0
    RPEDEBUG("Failed executing program");
1046
1047
    /*
1048
     *  Not done in fr_exec_oneshot_cleanup as it's
1049
     *  usually the caller's responsibility.
1050
     */
1051
0
    if (exec->stdin_fd >= 0) {
1052
0
      close(exec->stdin_fd);
1053
0
      exec->stdin_fd = -1;
1054
0
    }
1055
0
    fr_exec_oneshot_cleanup(exec, 0);
1056
0
    return -1;
1057
0
  }
1058
1059
  /*
1060
   *  First setup I/O events for the child process. This needs
1061
   *  to be done before we call fr_event_pid_wait, as it may
1062
   *  immediately trigger the PID callback if there's a race
1063
   *  between kevent and the child exiting, and that callback
1064
   *  will expect file descriptor events to have been created.
1065
   */
1066
1067
  /*
1068
   *  If we need to parse stdout, insert a special IO handler that
1069
   *  aggregates all stdout data into an expandable buffer.
1070
   */
1071
0
  if (exec->stdout_used) {
1072
    /*
1073
     *  Accept a maximum of 32k of data from the process.
1074
     */
1075
0
    fr_sbuff_init_talloc(exec->stdout_ctx, &exec->stdout_buff, &exec->stdout_tctx, 128, 32 * 1024);
1076
0
    if (fr_event_fd_insert(ctx, NULL, el, exec->stdout_fd, exec_stdout_read, NULL, NULL, exec) < 0) {
1077
0
      RPEDEBUG("Failed adding event listening to stdout");
1078
0
      goto fail_and_close;
1079
0
    }
1080
1081
  /*
1082
   *  If the caller doesn't want the output box, we still want to copy stdout
1083
   *  into the request log if we're logging at a high enough level of verbosity.
1084
   */
1085
0
  } else if (RDEBUG_ENABLED2) {
1086
0
    snprintf(exec->stdout_prefix, sizeof(exec->stdout_prefix), "pid %u (stdout)", exec->pid);
1087
0
    exec->stdout_uctx = (log_fd_event_ctx_t) {
1088
0
      .type = L_DBG,
1089
0
      .lvl = L_DBG_LVL_2,
1090
0
      .request = request,
1091
0
      .prefix = exec->stdout_prefix
1092
0
    };
1093
1094
0
    if (fr_event_fd_insert(ctx, NULL, el, exec->stdout_fd, log_request_fd_event,
1095
0
               NULL, NULL, &exec->stdout_uctx) < 0){
1096
0
      RPEDEBUG("Failed adding event listening to stdout");
1097
0
      goto fail_and_close;
1098
0
    }
1099
0
  }
1100
1101
  /*
1102
   *  Send stderr to the request log as error messages with a custom prefix
1103
   */
1104
0
  snprintf(exec->stderr_prefix, sizeof(exec->stderr_prefix), "pid %u (stderr)", exec->pid);
1105
0
  exec->stderr_uctx = (log_fd_event_ctx_t) {
1106
0
    .type = L_DBG_ERR,
1107
0
    .lvl = L_DBG_LVL_1,
1108
0
    .request = request,
1109
0
    .prefix = exec->stderr_prefix
1110
0
  };
1111
1112
0
  if (fr_event_fd_insert(ctx, NULL, el, exec->stderr_fd, log_request_fd_event,
1113
0
             NULL, NULL, &exec->stderr_uctx) < 0) {
1114
0
    RPEDEBUG("Failed adding event listening to stderr");
1115
0
    close(exec->stderr_fd);
1116
0
    exec->stderr_fd = -1;
1117
0
    goto fail;
1118
0
  }
1119
1120
  /*
1121
   *  Tell the event loop that it needs to wait for this PID
1122
   */
1123
0
  if (fr_event_pid_wait(ctx, el, &exec->ev_pid, exec->pid, exec_reap, exec) < 0) {
1124
0
    exec->pid = -1;
1125
0
    RPEDEBUG("Failed adding watcher for child process");
1126
1127
0
  fail_and_close:
1128
    /*
1129
     *  Avoid spurious errors in fr_exec_oneshot_cleanup
1130
     *  when it tries to remove FDs from the
1131
     *  event loop that were never added.
1132
     */
1133
0
    if (exec->stdout_fd >= 0) {
1134
0
      close(exec->stdout_fd);
1135
0
      exec->stdout_fd = -1;
1136
0
    }
1137
1138
0
    if (exec->stderr_fd >= 0) {
1139
0
      close(exec->stderr_fd);
1140
0
      exec->stderr_fd = -1;
1141
0
    }
1142
1143
0
    goto fail;
1144
0
  }
1145
1146
  /*
1147
   *  Setup event to kill the child process after a period of time.
1148
   */
1149
0
  if (fr_time_delta_ispos(timeout) &&
1150
0
    (fr_timer_in(ctx, el->tl, &exec->ev, timeout, true, exec_timeout, exec) < 0)) goto fail_and_close;
1151
1152
0
  return 0;
1153
0
}