Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/src/exec.c
1532 views
1
/*
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 2009-2023 Todd C. Miller <[email protected]>
5
*
6
* Permission to use, copy, modify, and distribute this software for any
7
* purpose with or without fee is hereby granted, provided that the above
8
* copyright notice and this permission notice appear in all copies.
9
*
10
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
*/
18
19
#include <config.h>
20
21
#include <sys/resource.h>
22
#include <sys/stat.h>
23
#include <stdlib.h>
24
#include <string.h>
25
#include <unistd.h>
26
#include <errno.h>
27
#include <fcntl.h>
28
#include <pwd.h>
29
#include <signal.h>
30
#ifdef HAVE_LOGIN_CAP_H
31
# include <login_cap.h>
32
# ifndef LOGIN_SETENV
33
# define LOGIN_SETENV 0
34
# endif
35
#endif
36
#ifdef HAVE_PROJECT_H
37
# include <project.h>
38
# include <sys/task.h>
39
#endif
40
41
#include <sudo.h>
42
#include <sudo_exec.h>
43
#include <sudo_plugin.h>
44
#include <sudo_plugin_int.h>
45
46
#ifdef HAVE_PTRACE_INTERCEPT
47
static void
48
handler(int signo)
49
{
50
/* just return */
51
}
52
#endif /* HAVE_PTRACE_INTERCEPT */
53
54
static void
55
close_fds(struct command_details *details, int errfd, int intercept_fd)
56
{
57
int fd, maxfd;
58
unsigned char *debug_fds;
59
debug_decl(close_fds, SUDO_DEBUG_EXEC);
60
61
if (details->closefrom < 0)
62
debug_return;
63
64
/* Preserve debug fds and error pipe as needed. */
65
maxfd = sudo_debug_get_fds(&debug_fds);
66
for (fd = 0; fd <= maxfd; fd++) {
67
if (sudo_isset(debug_fds, fd))
68
add_preserved_fd(&details->preserved_fds, fd);
69
}
70
if (errfd != -1)
71
add_preserved_fd(&details->preserved_fds, errfd);
72
if (intercept_fd != -1)
73
add_preserved_fd(&details->preserved_fds, intercept_fd);
74
75
/* Close all fds except those explicitly preserved. */
76
closefrom_except(details->closefrom, &details->preserved_fds);
77
78
debug_return;
79
}
80
81
/*
82
* Set up the execution environment immediately prior to the call to execve().
83
* Group setup is performed by policy_init_session(), called earlier.
84
* Returns true on success and false on failure.
85
*/
86
static bool
87
exec_setup(struct command_details *details, int intercept_fd, int errfd)
88
{
89
bool ret = false;
90
debug_decl(exec_setup, SUDO_DEBUG_EXEC);
91
92
#ifdef HAVE_PTRACE_INTERCEPT
93
if (ISSET(details->flags, CD_USE_PTRACE)) {
94
if (!set_exec_filter())
95
goto done;
96
}
97
#endif /* HAVE_PTRACE_INTERCEPT */
98
99
if (details->pw != NULL) {
100
#ifdef HAVE_PROJECT_H
101
set_project(details->pw);
102
#endif
103
#ifdef HAVE_PRIV_SET
104
if (details->privs != NULL) {
105
if (setppriv(PRIV_SET, PRIV_INHERITABLE, details->privs) != 0) {
106
sudo_warn("%s", U_("unable to set privileges"));
107
goto done;
108
}
109
}
110
if (details->limitprivs != NULL) {
111
if (setppriv(PRIV_SET, PRIV_LIMIT, details->limitprivs) != 0) {
112
sudo_warn("%s", U_("unable to set limit privileges"));
113
goto done;
114
}
115
} else if (details->privs != NULL) {
116
if (setppriv(PRIV_SET, PRIV_LIMIT, details->privs) != 0) {
117
sudo_warn("%s", U_("unable to set limit privileges"));
118
goto done;
119
}
120
}
121
#endif /* HAVE_PRIV_SET */
122
123
#ifdef HAVE_GETUSERATTR
124
if (aix_prep_user(details->pw->pw_name, details->tty) != 0) {
125
/* error message displayed by aix_prep_user */
126
goto done;
127
}
128
#endif
129
#ifdef HAVE_LOGIN_CAP_H
130
if (details->login_class) {
131
unsigned int flags;
132
login_cap_t *lc;
133
134
/*
135
* We only use setusercontext() to set the nice value, rlimits
136
* and umask unless this is a login shell (sudo -i).
137
*/
138
lc = login_getclass((char *)details->login_class);
139
if (!lc) {
140
sudo_warnx(U_("unknown login class %s"), details->login_class);
141
errno = ENOENT;
142
goto done;
143
}
144
if (ISSET(details->flags, CD_LOGIN_SHELL)) {
145
/* Set everything except user, group and login name. */
146
flags = LOGIN_SETALL;
147
CLR(flags, LOGIN_SETGROUP|LOGIN_SETLOGIN|LOGIN_SETUSER|LOGIN_SETENV|LOGIN_SETPATH);
148
} else {
149
flags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY|LOGIN_SETUMASK;
150
}
151
if (setusercontext(lc, details->pw, details->pw->pw_uid, flags)) {
152
sudo_warn("%s", U_("unable to set user context"));
153
if (details->pw->pw_uid != ROOT_UID)
154
goto done;
155
}
156
}
157
#endif /* HAVE_LOGIN_CAP_H */
158
}
159
160
if (ISSET(details->flags, CD_SET_GROUPS)) {
161
/* set_user_groups() prints error message on failure. */
162
if (!set_user_groups(details))
163
goto done;
164
}
165
166
if (ISSET(details->flags, CD_SET_PRIORITY)) {
167
if (setpriority(PRIO_PROCESS, 0, details->priority) != 0) {
168
sudo_warn("%s", U_("unable to set process priority"));
169
goto done;
170
}
171
}
172
173
/* Policy may override umask in PAM or login.conf. */
174
if (ISSET(details->flags, CD_OVERRIDE_UMASK))
175
(void) umask(details->umask);
176
177
/* Apply resource limits specified by the policy, if any. */
178
set_policy_rlimits();
179
180
/* Close fds before chroot (need /dev) or uid change (prlimit on Linux). */
181
close_fds(details, errfd, intercept_fd);
182
183
if (details->chroot) {
184
if (chroot(details->chroot) != 0 || chdir("/") != 0) {
185
sudo_warn(U_("unable to change root to %s"), details->chroot);
186
goto done;
187
}
188
}
189
190
/*
191
* Unlimit the number of processes since Linux's setuid() will
192
* return EAGAIN if RLIMIT_NPROC would be exceeded by the uid switch.
193
*/
194
unlimit_nproc();
195
196
#if defined(HAVE_SETRESUID)
197
if (setresuid(details->cred.uid, details->cred.euid, details->cred.euid) != 0) {
198
sudo_warn(U_("unable to change to runas uid (%u, %u)"),
199
(unsigned int)details->cred.uid, (unsigned int)details->cred.euid);
200
goto done;
201
}
202
#elif defined(HAVE_SETREUID)
203
if (setreuid(details->cred.uid, details->cred.euid) != 0) {
204
sudo_warn(U_("unable to change to runas uid (%u, %u)"),
205
(unsigned int)details->cred.uid, (unsigned int)details->cred.euid);
206
goto done;
207
}
208
#else
209
/* Cannot support real user-ID that is different from effective user-ID. */
210
if (setuid(details->cred.euid) != 0) {
211
sudo_warn(U_("unable to change to runas uid (%u, %u)"),
212
(unsigned int)details->cred.euid, (unsigned int)details->cred.euid);
213
goto done;
214
}
215
#endif /* !HAVE_SETRESUID && !HAVE_SETREUID */
216
217
/* Restore previous value of RLIMIT_NPROC. */
218
restore_nproc();
219
220
/*
221
* Only change cwd if we have chroot()ed or the policy modules
222
* specifies a different cwd. Must be done after uid change.
223
*/
224
if (details->runcwd != NULL) {
225
if (details->chroot != NULL || details->submitcwd == NULL ||
226
strcmp(details->runcwd, details->submitcwd) != 0) {
227
if (ISSET(details->flags, CD_RBAC_ENABLED)) {
228
/* For SELinux, chdir(2) in sesh after the context change. */
229
SET(details->flags, CD_RBAC_SET_CWD);
230
} else {
231
/* Note: runcwd is relative to the new root, if any. */
232
if (chdir(details->runcwd) == -1) {
233
sudo_warn(U_("unable to change directory to %s"),
234
details->runcwd);
235
if (!ISSET(details->flags, CD_CWD_OPTIONAL))
236
goto done;
237
if (details->chroot != NULL)
238
sudo_warnx(U_("starting from %s"), "/");
239
}
240
}
241
}
242
}
243
244
ret = true;
245
246
done:
247
debug_return_bool(ret);
248
}
249
250
/*
251
* Set up the execution environment and execute the command.
252
* If SELinux is enabled, run the command via sesh, otherwise
253
* execute it directly.
254
* If the exec fails, cstat is filled in with the value of errno.
255
*/
256
void
257
exec_cmnd(struct command_details *details, sigset_t *mask,
258
int intercept_fd, int errfd)
259
{
260
debug_decl(exec_cmnd, SUDO_DEBUG_EXEC);
261
262
#ifdef HAVE_PTRACE_INTERCEPT
263
if (ISSET(details->flags, CD_USE_PTRACE)) {
264
struct sigaction sa;
265
sigset_t set;
266
267
/* Tracer will send us SIGUSR1 when it is time to proceed. */
268
memset(&sa, 0, sizeof(sa));
269
sigemptyset(&sa.sa_mask);
270
sa.sa_flags = SA_RESTART;
271
sa.sa_handler = handler;
272
if (sudo_sigaction(SIGUSR1, &sa, NULL) != 0) {
273
sudo_warn(U_("unable to set handler for signal %d"),
274
SIGUSR1);
275
}
276
277
/* Suspend child until tracer seizes control and sends SIGUSR1. */
278
sigfillset(&set);
279
sigdelset(&set, SIGUSR1);
280
sigsuspend(&set);
281
}
282
#endif /* HAVE_PTRACE_INTERCEPT */
283
284
if (mask != NULL)
285
sigprocmask(SIG_SETMASK, mask, NULL);
286
restore_signals();
287
if (exec_setup(details, intercept_fd, errfd) == true) {
288
/* headed for execve() */
289
#ifdef HAVE_SELINUX
290
if (ISSET(details->flags, CD_RBAC_ENABLED)) {
291
selinux_execve(details->execfd, details->command, details->argv,
292
details->envp, details->runcwd, details->flags);
293
} else
294
#endif
295
{
296
sudo_execve(details->execfd, details->command, details->argv,
297
details->envp, intercept_fd, details->flags);
298
}
299
}
300
sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to exec %s: %s",
301
details->command, strerror(errno));
302
debug_return;
303
}
304
305
/*
306
* Check for caught signals sent to sudo before command execution.
307
* Also suspends the process if SIGTSTP was caught.
308
* Returns true if we should terminate, else false.
309
*/
310
bool
311
sudo_terminated(struct command_status *cstat)
312
{
313
int signo;
314
bool sigtstp = false;
315
debug_decl(sudo_terminated, SUDO_DEBUG_EXEC);
316
317
for (signo = 0; signo < NSIG; signo++) {
318
if (signal_pending(signo)) {
319
switch (signo) {
320
case SIGCHLD:
321
/* Ignore. */
322
break;
323
case SIGTSTP:
324
/* Suspend below if not terminated. */
325
sigtstp = true;
326
break;
327
default:
328
/* Terminal signal, do not exec command. */
329
cstat->type = CMD_WSTATUS;
330
cstat->val = signo + 128;
331
debug_return_bool(true);
332
break;
333
}
334
}
335
}
336
if (sigtstp) {
337
struct sigaction sa;
338
sigset_t set, oset;
339
340
/* Send SIGTSTP to ourselves, unblocking it if needed. */
341
memset(&sa, 0, sizeof(sa));
342
sigemptyset(&sa.sa_mask);
343
sa.sa_flags = SA_RESTART;
344
sa.sa_handler = SIG_DFL;
345
if (sudo_sigaction(SIGTSTP, &sa, NULL) != 0)
346
sudo_warn(U_("unable to set handler for signal %d"), SIGTSTP);
347
sigemptyset(&set);
348
sigaddset(&set, SIGTSTP);
349
sigprocmask(SIG_UNBLOCK, &set, &oset);
350
if (kill(getpid(), SIGTSTP) != 0)
351
sudo_warn("kill(%d, SIGTSTP)", (int)getpid());
352
sigprocmask(SIG_SETMASK, &oset, NULL);
353
/* No need to restore old SIGTSTP handler. */
354
}
355
debug_return_bool(false);
356
}
357
358
static bool
359
sudo_needs_pty(const struct command_details *details)
360
{
361
struct plugin_container *plugin;
362
363
if (ISSET(details->flags, CD_USE_PTY))
364
return true;
365
366
TAILQ_FOREACH(plugin, &io_plugins, entries) {
367
if (plugin->u.io->log_ttyin != NULL ||
368
plugin->u.io->log_ttyout != NULL)
369
return true;
370
}
371
return false;
372
}
373
374
/*
375
* Check whether the specified fd is a terminal with the specified
376
* controlling process group. Always fills in sb (zeroed on error).
377
* Returns true on match, else false.
378
*/
379
bool
380
fd_matches_pgrp(int fd, pid_t pgrp, struct stat *sb)
381
{
382
debug_decl(fd_matches_pgrp, SUDO_DEBUG_EXEC);
383
384
if (!sudo_isatty(fd, sb))
385
debug_return_bool(false);
386
387
if (pgrp != -1) {
388
if (pgrp != tcgetpgrp(fd))
389
debug_return_bool(false);
390
}
391
debug_return_bool(true);
392
}
393
394
/*
395
* If we are not running the command in a pty, we were not invoked as
396
* sudoedit, there is no command timeout and there is no close function,
397
* sudo can exec the command directly (and not wait).
398
*/
399
static bool
400
direct_exec_allowed(const struct command_details *details)
401
{
402
struct plugin_container *plugin;
403
debug_decl(direct_exec_allowed, SUDO_DEBUG_EXEC);
404
405
/* Assumes sudo_needs_pty() was already checked. */
406
if (policy_plugin.u.policy->close != NULL)
407
debug_return_bool(false);
408
if (ISSET(details->flags, CD_RBAC_ENABLED|CD_SET_TIMEOUT|CD_SUDOEDIT))
409
debug_return_bool(false);
410
if (ISSET(details->flags, CD_INTERCEPT|CD_LOG_SUBCMDS))
411
debug_return_bool(false);
412
413
TAILQ_FOREACH(plugin, &audit_plugins, entries) {
414
if (plugin->u.audit->close != NULL)
415
debug_return_bool(false);
416
}
417
418
debug_return_bool(true);
419
}
420
421
/*
422
* Execute a command, potentially in a pty with I/O logging, and
423
* wait for it to finish.
424
* This is a little bit tricky due to how POSIX job control works and
425
* we fact that we have two different controlling terminals to deal with.
426
*/
427
int
428
sudo_execute(struct command_details *details,
429
const struct user_details *user_details,
430
struct sudo_event_base *evbase, struct command_status *cstat)
431
{
432
debug_decl(sudo_execute, SUDO_DEBUG_EXEC);
433
434
#if defined(HAVE_SELINUX) && !defined(HAVE_PTRACE_INTERCEPT)
435
/*
436
* SELinux prevents LD_PRELOAD from functioning so we must use
437
* ptrace-based intercept mode.
438
*/
439
if (details->selinux_role != NULL || details->selinux_type != NULL) {
440
if (ISSET(details->flags, CD_INTERCEPT)) {
441
sudo_warnx("%s",
442
U_("intercept mode is not supported with SELinux RBAC on this system"));
443
CLR(details->flags, CD_INTERCEPT);
444
}
445
if (ISSET(details->flags, CD_LOG_SUBCMDS)) {
446
sudo_warnx("%s",
447
U_("unable to log sub-commands with SELinux RBAC on this system"));
448
CLR(details->flags, CD_LOG_SUBCMDS);
449
}
450
}
451
#endif /* HAVE_SELINUX && !HAVE_PTRACE_INTERCEPT */
452
453
/* If running in background mode, fork and exit. */
454
if (ISSET(details->flags, CD_BACKGROUND)) {
455
switch (sudo_debug_fork()) {
456
case -1:
457
cstat->type = CMD_ERRNO;
458
cstat->val = errno;
459
debug_return_int(-1);
460
case 0:
461
/*
462
* Child continues in an orphaned process group.
463
* Reads from the terminal fail with EIO.
464
* Writes succeed unless tostop is set on the terminal.
465
*/
466
(void)setpgid(0, 0);
467
break;
468
default:
469
/* parent exits (but does not flush buffers) */
470
sudo_debug_exit_int(__func__, __FILE__, __LINE__,
471
sudo_debug_subsys, 0);
472
_exit(EXIT_SUCCESS);
473
}
474
}
475
476
/*
477
* Restore resource limits before running.
478
* We must do this *before* calling the PAM session module.
479
*/
480
restore_limits();
481
482
/*
483
* Run the command in a new pty if there is an I/O plugin or the policy
484
* has requested a pty. If /dev/tty is unavailable and no I/O plugin
485
* is configured, this returns false and we run the command without a pty.
486
*/
487
if (sudo_needs_pty(details)) {
488
if (exec_pty(details, user_details, evbase, cstat))
489
goto done;
490
}
491
492
/*
493
* If we are not running the command in a pty, we may be able to
494
* exec directly, depending on the plugins used.
495
*/
496
if (direct_exec_allowed(details)) {
497
if (!sudo_terminated(cstat)) {
498
exec_cmnd(details, NULL, -1, -1);
499
cstat->type = CMD_ERRNO;
500
cstat->val = errno;
501
}
502
goto done;
503
}
504
505
/*
506
* Run the command in the existing tty (if any) and wait for it to finish.
507
*/
508
exec_nopty(details, user_details, evbase, cstat);
509
510
done:
511
/* The caller will run any plugin close functions. */
512
debug_return_int(cstat->type == CMD_ERRNO ? -1 : 0);
513
}
514
515
/*
516
* Kill command with increasing urgency.
517
*/
518
void
519
terminate_command(pid_t pid, bool use_pgrp)
520
{
521
debug_decl(terminate_command, SUDO_DEBUG_EXEC);
522
523
/* Avoid killing more than a single process or process group. */
524
if (pid <= 0)
525
debug_return;
526
527
/*
528
* Note that SIGCHLD will interrupt the sleep()
529
*/
530
if (use_pgrp) {
531
sudo_debug_printf(SUDO_DEBUG_INFO, "killpg %d SIGHUP", (int)pid);
532
killpg(pid, SIGHUP);
533
sudo_debug_printf(SUDO_DEBUG_INFO, "killpg %d SIGTERM", (int)pid);
534
killpg(pid, SIGTERM);
535
sleep(2);
536
sudo_debug_printf(SUDO_DEBUG_INFO, "killpg %d SIGKILL", (int)pid);
537
killpg(pid, SIGKILL);
538
} else {
539
sudo_debug_printf(SUDO_DEBUG_INFO, "kill %d SIGHUP", (int)pid);
540
kill(pid, SIGHUP);
541
sudo_debug_printf(SUDO_DEBUG_INFO, "kill %d SIGTERM", (int)pid);
542
kill(pid, SIGTERM);
543
sleep(2);
544
sudo_debug_printf(SUDO_DEBUG_INFO, "kill %d SIGKILL", (int)pid);
545
kill(pid, SIGKILL);
546
}
547
548
debug_return;
549
}
550
551
/*
552
* Free the dynamically-allocated contents of the exec closure.
553
*/
554
void
555
free_exec_closure(struct exec_closure *ec)
556
{
557
debug_decl(free_exec_closure, SUDO_DEBUG_EXEC);
558
559
/* Free any remaining intercept resources. */
560
intercept_cleanup(ec);
561
562
sudo_ev_base_free(ec->evbase);
563
sudo_ev_free(ec->backchannel_event);
564
sudo_ev_free(ec->fwdchannel_event);
565
sudo_ev_free(ec->sigint_event);
566
sudo_ev_free(ec->sigquit_event);
567
sudo_ev_free(ec->sigtstp_event);
568
sudo_ev_free(ec->sigterm_event);
569
sudo_ev_free(ec->sighup_event);
570
sudo_ev_free(ec->sigalrm_event);
571
sudo_ev_free(ec->sigpipe_event);
572
sudo_ev_free(ec->sigusr1_event);
573
sudo_ev_free(ec->sigusr2_event);
574
sudo_ev_free(ec->sigchld_event);
575
sudo_ev_free(ec->sigcont_event);
576
sudo_ev_free(ec->siginfo_event);
577
sudo_ev_free(ec->sigwinch_event);
578
free(ec->ptyname);
579
580
debug_return;
581
}
582
583