1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <dirent.h>
4 #include <errno.h>
5 #include <sys/prctl.h>
6 #include <sys/types.h>
7 #include <unistd.h>
8 #include <stdio.h>
9
10 #include "alloc-util.h"
11 #include "conf-files.h"
12 #include "env-file.h"
13 #include "env-util.h"
14 #include "errno-util.h"
15 #include "exec-util.h"
16 #include "fd-util.h"
17 #include "fileio.h"
18 #include "hashmap.h"
19 #include "macro.h"
20 #include "missing_syscall.h"
21 #include "process-util.h"
22 #include "rlimit-util.h"
23 #include "serialize.h"
24 #include "set.h"
25 #include "signal-util.h"
26 #include "stat-util.h"
27 #include "string-table.h"
28 #include "string-util.h"
29 #include "strv.h"
30 #include "terminal-util.h"
31 #include "tmpfile-util.h"
32 #include "util.h"
33
34 /* Put this test here for a lack of better place */
35 assert_cc(EAGAIN == EWOULDBLOCK);
36
do_spawn(const char * path,char * argv[],int stdout_fd,pid_t * pid,bool set_systemd_exec_pid)37 static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid, bool set_systemd_exec_pid) {
38 pid_t _pid;
39 int r;
40
41 if (null_or_empty_path(path)) {
42 log_debug("%s is empty (a mask).", path);
43 return 0;
44 }
45
46 r = safe_fork("(direxec)", FORK_DEATHSIG|FORK_LOG, &_pid);
47 if (r < 0)
48 return r;
49 if (r == 0) {
50 char *_argv[2];
51
52 if (stdout_fd >= 0) {
53 r = rearrange_stdio(STDIN_FILENO, TAKE_FD(stdout_fd), STDERR_FILENO);
54 if (r < 0)
55 _exit(EXIT_FAILURE);
56 }
57
58 (void) rlimit_nofile_safe();
59
60 if (set_systemd_exec_pid) {
61 r = setenv_systemd_exec_pid(false);
62 if (r < 0)
63 log_warning_errno(r, "Failed to set $SYSTEMD_EXEC_PID, ignoring: %m");
64 }
65
66 if (!argv) {
67 _argv[0] = (char*) path;
68 _argv[1] = NULL;
69 argv = _argv;
70 } else
71 argv[0] = (char*) path;
72
73 execv(path, argv);
74 log_error_errno(errno, "Failed to execute %s: %m", path);
75 _exit(EXIT_FAILURE);
76 }
77
78 *pid = _pid;
79 return 1;
80 }
81
do_execute(char ** directories,usec_t timeout,gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],void * const callback_args[_STDOUT_CONSUME_MAX],int output_fd,char * argv[],char * envp[],ExecDirFlags flags)82 static int do_execute(
83 char **directories,
84 usec_t timeout,
85 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
86 void* const callback_args[_STDOUT_CONSUME_MAX],
87 int output_fd,
88 char *argv[],
89 char *envp[],
90 ExecDirFlags flags) {
91
92 _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
93 _cleanup_strv_free_ char **paths = NULL;
94 int r;
95 bool parallel_execution;
96
97 /* We fork this all off from a child process so that we can somewhat cleanly make
98 * use of SIGALRM to set a time limit.
99 *
100 * We attempt to perform parallel execution if configured by the user, however
101 * if `callbacks` is nonnull, execution must be serial.
102 */
103 parallel_execution = FLAGS_SET(flags, EXEC_DIR_PARALLEL) && !callbacks;
104
105 r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE|CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char* const*) directories);
106 if (r < 0)
107 return log_error_errno(r, "Failed to enumerate executables: %m");
108
109 if (parallel_execution) {
110 pids = hashmap_new(NULL);
111 if (!pids)
112 return log_oom();
113 }
114
115 /* Abort execution of this process after the timeout. We simply rely on SIGALRM as
116 * default action terminating the process, and turn on alarm(). */
117
118 if (timeout != USEC_INFINITY)
119 alarm(DIV_ROUND_UP(timeout, USEC_PER_SEC));
120
121 STRV_FOREACH(e, envp)
122 if (putenv(*e) != 0)
123 return log_error_errno(errno, "Failed to set environment variable: %m");
124
125 STRV_FOREACH(path, paths) {
126 _cleanup_free_ char *t = NULL;
127 _cleanup_close_ int fd = -1;
128 pid_t pid;
129
130 t = strdup(*path);
131 if (!t)
132 return log_oom();
133
134 if (callbacks) {
135 fd = open_serialization_fd(basename(*path));
136 if (fd < 0)
137 return log_error_errno(fd, "Failed to open serialization file: %m");
138 }
139
140 r = do_spawn(t, argv, fd, &pid, FLAGS_SET(flags, EXEC_DIR_SET_SYSTEMD_EXEC_PID));
141 if (r <= 0)
142 continue;
143
144 if (parallel_execution) {
145 r = hashmap_put(pids, PID_TO_PTR(pid), t);
146 if (r < 0)
147 return log_oom();
148 t = NULL;
149 } else {
150 r = wait_for_terminate_and_check(t, pid, WAIT_LOG);
151 if (FLAGS_SET(flags, EXEC_DIR_IGNORE_ERRORS)) {
152 if (r < 0)
153 continue;
154 } else if (r > 0)
155 return r;
156
157 if (callbacks) {
158 if (lseek(fd, 0, SEEK_SET) < 0)
159 return log_error_errno(errno, "Failed to seek on serialization fd: %m");
160
161 r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]);
162 fd = -1;
163 if (r < 0)
164 return log_error_errno(r, "Failed to process output from %s: %m", *path);
165 }
166 }
167 }
168
169 if (callbacks) {
170 r = callbacks[STDOUT_COLLECT](output_fd, callback_args[STDOUT_COLLECT]);
171 if (r < 0)
172 return log_error_errno(r, "Callback two failed: %m");
173 }
174
175 while (!hashmap_isempty(pids)) {
176 _cleanup_free_ char *t = NULL;
177 pid_t pid;
178
179 pid = PTR_TO_PID(hashmap_first_key(pids));
180 assert(pid > 0);
181
182 t = hashmap_remove(pids, PID_TO_PTR(pid));
183 assert(t);
184
185 r = wait_for_terminate_and_check(t, pid, WAIT_LOG);
186 if (!FLAGS_SET(flags, EXEC_DIR_IGNORE_ERRORS) && r > 0)
187 return r;
188 }
189
190 return 0;
191 }
192
execute_directories(const char * const * directories,usec_t timeout,gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],void * const callback_args[_STDOUT_CONSUME_MAX],char * argv[],char * envp[],ExecDirFlags flags)193 int execute_directories(
194 const char* const* directories,
195 usec_t timeout,
196 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
197 void* const callback_args[_STDOUT_CONSUME_MAX],
198 char *argv[],
199 char *envp[],
200 ExecDirFlags flags) {
201
202 char **dirs = (char**) directories;
203 _cleanup_close_ int fd = -1;
204 char *name;
205 int r;
206 pid_t executor_pid;
207
208 assert(!strv_isempty(dirs));
209
210 name = basename(dirs[0]);
211 assert(!isempty(name));
212
213 if (callbacks) {
214 assert(callback_args);
215 assert(callbacks[STDOUT_GENERATE]);
216 assert(callbacks[STDOUT_COLLECT]);
217 assert(callbacks[STDOUT_CONSUME]);
218
219 fd = open_serialization_fd(name);
220 if (fd < 0)
221 return log_error_errno(fd, "Failed to open serialization file: %m");
222 }
223
224 /* Executes all binaries in the directories serially or in parallel and waits for
225 * them to finish. Optionally a timeout is applied. If a file with the same name
226 * exists in more than one directory, the earliest one wins. */
227
228 r = safe_fork("(sd-executor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &executor_pid);
229 if (r < 0)
230 return r;
231 if (r == 0) {
232 r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv, envp, flags);
233 _exit(r < 0 ? EXIT_FAILURE : r);
234 }
235
236 r = wait_for_terminate_and_check("(sd-executor)", executor_pid, 0);
237 if (r < 0)
238 return r;
239 if (!FLAGS_SET(flags, EXEC_DIR_IGNORE_ERRORS) && r > 0)
240 return r;
241
242 if (!callbacks)
243 return 0;
244
245 if (lseek(fd, 0, SEEK_SET) < 0)
246 return log_error_errno(errno, "Failed to rewind serialization fd: %m");
247
248 r = callbacks[STDOUT_CONSUME](fd, callback_args[STDOUT_CONSUME]);
249 fd = -1;
250 if (r < 0)
251 return log_error_errno(r, "Failed to parse returned data: %m");
252 return 0;
253 }
254
gather_environment_generate(int fd,void * arg)255 static int gather_environment_generate(int fd, void *arg) {
256 char ***env = arg;
257 _cleanup_fclose_ FILE *f = NULL;
258 _cleanup_strv_free_ char **new = NULL;
259 int r;
260
261 /* Read a series of VAR=value assignments from fd, use them to update the list of
262 * variables in env. Also update the exported environment.
263 *
264 * fd is always consumed, even on error.
265 */
266
267 assert(env);
268
269 f = fdopen(fd, "r");
270 if (!f) {
271 safe_close(fd);
272 return -errno;
273 }
274
275 r = load_env_file_pairs(f, NULL, &new);
276 if (r < 0)
277 return r;
278
279 STRV_FOREACH_PAIR(x, y, new) {
280 if (!env_name_is_valid(*x)) {
281 log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x);
282 continue;
283 }
284
285 r = strv_env_assign(env, *x, *y);
286 if (r < 0)
287 return r;
288
289 if (setenv(*x, *y, true) < 0)
290 return -errno;
291 }
292
293 return 0;
294 }
295
gather_environment_collect(int fd,void * arg)296 static int gather_environment_collect(int fd, void *arg) {
297 _cleanup_fclose_ FILE *f = NULL;
298 char ***env = arg;
299 int r;
300
301 /* Write out a series of env=cescape(VAR=value) assignments to fd. */
302
303 assert(env);
304
305 f = fdopen(fd, "w");
306 if (!f) {
307 safe_close(fd);
308 return -errno;
309 }
310
311 r = serialize_strv(f, "env", *env);
312 if (r < 0)
313 return r;
314
315 r = fflush_and_check(f);
316 if (r < 0)
317 return r;
318
319 return 0;
320 }
321
gather_environment_consume(int fd,void * arg)322 static int gather_environment_consume(int fd, void *arg) {
323 _cleanup_fclose_ FILE *f = NULL;
324 char ***env = arg;
325 int r = 0;
326
327 /* Read a series of env=cescape(VAR=value) assignments from fd into env. */
328
329 assert(env);
330
331 f = fdopen(fd, "r");
332 if (!f) {
333 safe_close(fd);
334 return -errno;
335 }
336
337 for (;;) {
338 _cleanup_free_ char *line = NULL;
339 const char *v;
340 int k;
341
342 k = read_line(f, LONG_LINE_MAX, &line);
343 if (k < 0)
344 return k;
345 if (k == 0)
346 break;
347
348 v = startswith(line, "env=");
349 if (!v) {
350 log_debug("Serialization line \"%s\" unexpectedly didn't start with \"env=\".", line);
351 if (r == 0)
352 r = -EINVAL;
353
354 continue;
355 }
356
357 k = deserialize_environment(v, env);
358 if (k < 0) {
359 log_debug_errno(k, "Invalid serialization line \"%s\": %m", line);
360
361 if (r == 0)
362 r = k;
363 }
364 }
365
366 return r;
367 }
368
exec_command_flags_from_strv(char ** ex_opts,ExecCommandFlags * flags)369 int exec_command_flags_from_strv(char **ex_opts, ExecCommandFlags *flags) {
370 ExecCommandFlags ex_flag, ret_flags = 0;
371
372 assert(flags);
373
374 STRV_FOREACH(opt, ex_opts) {
375 ex_flag = exec_command_flags_from_string(*opt);
376 if (ex_flag < 0)
377 return ex_flag;
378 ret_flags |= ex_flag;
379 }
380
381 *flags = ret_flags;
382
383 return 0;
384 }
385
exec_command_flags_to_strv(ExecCommandFlags flags,char *** ex_opts)386 int exec_command_flags_to_strv(ExecCommandFlags flags, char ***ex_opts) {
387 _cleanup_strv_free_ char **ret_opts = NULL;
388 ExecCommandFlags it = flags;
389 const char *str;
390 int i, r;
391
392 assert(ex_opts);
393
394 if (flags < 0)
395 return flags;
396
397 for (i = 0; it != 0; it &= ~(1 << i), i++) {
398 if (FLAGS_SET(flags, (1 << i))) {
399 str = exec_command_flags_to_string(1 << i);
400 if (!str)
401 return -EINVAL;
402
403 r = strv_extend(&ret_opts, str);
404 if (r < 0)
405 return r;
406 }
407 }
408
409 *ex_opts = TAKE_PTR(ret_opts);
410
411 return 0;
412 }
413
414 const gather_stdout_callback_t gather_environment[] = {
415 gather_environment_generate,
416 gather_environment_collect,
417 gather_environment_consume,
418 };
419
420 static const char* const exec_command_strings[] = {
421 "ignore-failure", /* EXEC_COMMAND_IGNORE_FAILURE */
422 "privileged", /* EXEC_COMMAND_FULLY_PRIVILEGED */
423 "no-setuid", /* EXEC_COMMAND_NO_SETUID */
424 "ambient", /* EXEC_COMMAND_AMBIENT_MAGIC */
425 "no-env-expand", /* EXEC_COMMAND_NO_ENV_EXPAND */
426 };
427
exec_command_flags_to_string(ExecCommandFlags i)428 const char* exec_command_flags_to_string(ExecCommandFlags i) {
429 size_t idx;
430
431 for (idx = 0; idx < ELEMENTSOF(exec_command_strings); idx++)
432 if (i == (1 << idx))
433 return exec_command_strings[idx];
434
435 return NULL;
436 }
437
exec_command_flags_from_string(const char * s)438 ExecCommandFlags exec_command_flags_from_string(const char *s) {
439 ssize_t idx;
440
441 idx = string_table_lookup(exec_command_strings, ELEMENTSOF(exec_command_strings), s);
442
443 if (idx < 0)
444 return _EXEC_COMMAND_FLAGS_INVALID;
445 else
446 return 1 << idx;
447 }
448
fexecve_or_execve(int executable_fd,const char * executable,char * const argv[],char * const envp[])449 int fexecve_or_execve(int executable_fd, const char *executable, char *const argv[], char *const envp[]) {
450 /* Refuse invalid fds, regardless if fexecve() use is enabled or not */
451 if (executable_fd < 0)
452 return -EBADF;
453
454 /* Block any attempts on exploiting Linux' liberal argv[] handling, i.e. CVE-2021-4034 and suchlike */
455 if (isempty(executable) || strv_isempty(argv))
456 return -EINVAL;
457
458 #if ENABLE_FEXECVE
459
460 execveat(executable_fd, "", argv, envp, AT_EMPTY_PATH);
461
462 if (IN_SET(errno, ENOSYS, ENOENT) || ERRNO_IS_PRIVILEGE(errno))
463 /* Old kernel or a script or an overzealous seccomp filter? Let's fall back to execve().
464 *
465 * fexecve(3): "If fd refers to a script (i.e., it is an executable text file that names a
466 * script interpreter with a first line that begins with the characters #!) and the
467 * close-on-exec flag has been set for fd, then fexecve() fails with the error ENOENT. This
468 * error occurs because, by the time the script interpreter is executed, fd has already been
469 * closed because of the close-on-exec flag. Thus, the close-on-exec flag can't be set on fd
470 * if it refers to a script."
471 *
472 * Unfortunately, if we unset close-on-exec, the script will be executed just fine, but (at
473 * least in case of bash) the script name, $0, will be shown as /dev/fd/nnn, which breaks
474 * scripts which make use of $0. Thus, let's fall back to execve() in this case.
475 */
476 #endif
477 execve(executable, argv, envp);
478 return -errno;
479 }
480
fork_agent(const char * name,const int except[],size_t n_except,pid_t * ret_pid,const char * path,...)481 int fork_agent(const char *name, const int except[], size_t n_except, pid_t *ret_pid, const char *path, ...) {
482 bool stdout_is_tty, stderr_is_tty;
483 size_t n, i;
484 va_list ap;
485 char **l;
486 int r;
487
488 assert(path);
489
490 /* Spawns a temporary TTY agent, making sure it goes away when we go away */
491
492 r = safe_fork_full(name,
493 except,
494 n_except,
495 FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_REOPEN_LOG,
496 ret_pid);
497 if (r < 0)
498 return r;
499 if (r > 0)
500 return 0;
501
502 /* In the child: */
503
504 stdout_is_tty = isatty(STDOUT_FILENO);
505 stderr_is_tty = isatty(STDERR_FILENO);
506
507 if (!stdout_is_tty || !stderr_is_tty) {
508 int fd;
509
510 /* Detach from stdout/stderr and reopen /dev/tty for them. This is important to ensure that
511 * when systemctl is started via popen() or a similar call that expects to read EOF we
512 * actually do generate EOF and not delay this indefinitely by keeping an unused copy of
513 * stdin around. */
514 fd = open("/dev/tty", O_WRONLY);
515 if (fd < 0) {
516 if (errno != ENXIO) {
517 log_error_errno(errno, "Failed to open /dev/tty: %m");
518 _exit(EXIT_FAILURE);
519 }
520
521 /* If we get ENXIO here we have no controlling TTY even though stdout/stderr are
522 * connected to a TTY. That's a weird setup, but let's handle it gracefully: let's
523 * skip the forking of the agents, given the TTY setup is not in order. */
524 } else {
525 if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) {
526 log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
527 _exit(EXIT_FAILURE);
528 }
529
530 if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) {
531 log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
532 _exit(EXIT_FAILURE);
533 }
534
535 fd = safe_close_above_stdio(fd);
536 }
537 }
538
539 (void) rlimit_nofile_safe();
540
541 /* Count arguments */
542 va_start(ap, path);
543 for (n = 0; va_arg(ap, char*); n++)
544 ;
545 va_end(ap);
546
547 /* Allocate strv */
548 l = newa(char*, n + 1);
549
550 /* Fill in arguments */
551 va_start(ap, path);
552 for (i = 0; i <= n; i++)
553 l[i] = va_arg(ap, char*);
554 va_end(ap);
555
556 execv(path, l);
557 _exit(EXIT_FAILURE);
558 }
559