1 /*
2 * SPDX-License-Identifier: ISC
3 *
4 * Copyright (c) 2009-2021 Todd C. Miller <Todd.Miller@sudo.ws>
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 /*
20 * This is an open source non-commercial project. Dear PVS-Studio, please check it.
21 * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
22 */
23
24 #include <config.h>
25
26 #include <sys/resource.h>
27 #include <sys/stat.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <pwd.h>
34 #include <signal.h>
35 #ifdef HAVE_LOGIN_CAP_H
36 # include <login_cap.h>
37 # ifndef LOGIN_SETENV
38 # define LOGIN_SETENV 0
39 # endif
40 #endif
41 #ifdef HAVE_PROJECT_H
42 # include <project.h>
43 # include <sys/task.h>
44 #endif
45
46 #include "sudo.h"
47 #include "sudo_exec.h"
48 #include "sudo_plugin.h"
49 #include "sudo_plugin_int.h"
50
51 static void
close_fds(struct command_details * details,int errfd,int intercept_fd)52 close_fds(struct command_details *details, int errfd, int intercept_fd)
53 {
54 int fd, maxfd;
55 unsigned char *debug_fds;
56 debug_decl(close_fds, SUDO_DEBUG_EXEC);
57
58 if (details->closefrom < 0)
59 debug_return;
60
61 /* Preserve debug fds and error pipe as needed. */
62 maxfd = sudo_debug_get_fds(&debug_fds);
63 for (fd = 0; fd <= maxfd; fd++) {
64 if (sudo_isset(debug_fds, fd))
65 add_preserved_fd(&details->preserved_fds, fd);
66 }
67 if (errfd != -1)
68 add_preserved_fd(&details->preserved_fds, errfd);
69 if (intercept_fd != -1)
70 add_preserved_fd(&details->preserved_fds, intercept_fd);
71
72 /* Close all fds except those explicitly preserved. */
73 closefrom_except(details->closefrom, &details->preserved_fds);
74
75 debug_return;
76 }
77
78 /*
79 * Setup the execution environment immediately prior to the call to execve().
80 * Group setup is performed by policy_init_session(), called earlier.
81 * Returns true on success and false on failure.
82 */
83 static bool
exec_setup(struct command_details * details,int intercept_fd,int errfd)84 exec_setup(struct command_details *details, int intercept_fd, int errfd)
85 {
86 bool ret = false;
87 debug_decl(exec_setup, SUDO_DEBUG_EXEC);
88
89 if (details->pw != NULL) {
90 #ifdef HAVE_PROJECT_H
91 set_project(details->pw);
92 #endif
93 #ifdef HAVE_PRIV_SET
94 if (details->privs != NULL) {
95 if (setppriv(PRIV_SET, PRIV_INHERITABLE, details->privs) != 0) {
96 sudo_warn("unable to set privileges");
97 goto done;
98 }
99 }
100 if (details->limitprivs != NULL) {
101 if (setppriv(PRIV_SET, PRIV_LIMIT, details->limitprivs) != 0) {
102 sudo_warn("unable to set limit privileges");
103 goto done;
104 }
105 } else if (details->privs != NULL) {
106 if (setppriv(PRIV_SET, PRIV_LIMIT, details->privs) != 0) {
107 sudo_warn("unable to set limit privileges");
108 goto done;
109 }
110 }
111 #endif /* HAVE_PRIV_SET */
112
113 #ifdef HAVE_GETUSERATTR
114 if (aix_prep_user(details->pw->pw_name, details->tty) != 0) {
115 /* error message displayed by aix_prep_user */
116 goto done;
117 }
118 #endif
119 #ifdef HAVE_LOGIN_CAP_H
120 if (details->login_class) {
121 int flags;
122 login_cap_t *lc;
123
124 /*
125 * We only use setusercontext() to set the nice value, rlimits
126 * and umask unless this is a login shell (sudo -i).
127 */
128 lc = login_getclass((char *)details->login_class);
129 if (!lc) {
130 sudo_warnx(U_("unknown login class %s"), details->login_class);
131 errno = ENOENT;
132 goto done;
133 }
134 if (ISSET(details->flags, CD_LOGIN_SHELL)) {
135 /* Set everything except user, group and login name. */
136 flags = LOGIN_SETALL;
137 CLR(flags, LOGIN_SETGROUP|LOGIN_SETLOGIN|LOGIN_SETUSER|LOGIN_SETENV|LOGIN_SETPATH);
138 } else {
139 flags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY|LOGIN_SETUMASK;
140 }
141 if (setusercontext(lc, details->pw, details->pw->pw_uid, flags)) {
142 sudo_warn("%s", U_("unable to set user context"));
143 if (details->pw->pw_uid != ROOT_UID)
144 goto done;
145 }
146 }
147 #endif /* HAVE_LOGIN_CAP_H */
148 }
149
150 if (ISSET(details->flags, CD_SET_GROUPS)) {
151 /* set_user_groups() prints error message on failure. */
152 if (!set_user_groups(details))
153 goto done;
154 }
155
156 if (ISSET(details->flags, CD_SET_PRIORITY)) {
157 if (setpriority(PRIO_PROCESS, 0, details->priority) != 0) {
158 sudo_warn("%s", U_("unable to set process priority"));
159 goto done;
160 }
161 }
162
163 /* Policy may override umask in PAM or login.conf. */
164 if (ISSET(details->flags, CD_OVERRIDE_UMASK))
165 (void) umask(details->umask);
166
167 /* Close fds before chroot (need /dev) or uid change (prlimit on Linux). */
168 close_fds(details, errfd, intercept_fd);
169
170 if (details->chroot) {
171 if (chroot(details->chroot) != 0 || chdir("/") != 0) {
172 sudo_warn(U_("unable to change root to %s"), details->chroot);
173 goto done;
174 }
175 }
176
177 /*
178 * Unlimit the number of processes since Linux's setuid() will
179 * return EAGAIN if RLIMIT_NPROC would be exceeded by the uid switch.
180 */
181 unlimit_nproc();
182
183 #if defined(HAVE_SETRESUID)
184 if (setresuid(details->cred.uid, details->cred.euid, details->cred.euid) != 0) {
185 sudo_warn(U_("unable to change to runas uid (%u, %u)"),
186 (unsigned int)details->cred.uid, (unsigned int)details->cred.euid);
187 goto done;
188 }
189 #elif defined(HAVE_SETREUID)
190 if (setreuid(details->cred.uid, details->cred.euid) != 0) {
191 sudo_warn(U_("unable to change to runas uid (%u, %u)"),
192 (unsigned int)details->cred.uid, (unsigned int)details->cred.euid);
193 goto done;
194 }
195 #else
196 /* Cannot support real user-ID that is different from effective user-ID. */
197 if (setuid(details->cred.euid) != 0) {
198 sudo_warn(U_("unable to change to runas uid (%u, %u)"),
199 (unsigned int)details->cred.euid, (unsigned int)details->cred.euid);
200 goto done;
201 }
202 #endif /* !HAVE_SETRESUID && !HAVE_SETREUID */
203
204 /* Restore previous value of RLIMIT_NPROC. */
205 restore_nproc();
206
207 /*
208 * Only change cwd if we have chroot()ed or the policy modules
209 * specifies a different cwd. Must be done after uid change.
210 */
211 if (details->cwd != NULL) {
212 if (details->chroot != NULL || user_details.cwd == NULL ||
213 strcmp(details->cwd, user_details.cwd) != 0) {
214 /* Note: cwd is relative to the new root, if any. */
215 if (chdir(details->cwd) == -1) {
216 sudo_warn(U_("unable to change directory to %s"), details->cwd);
217 if (!details->cwd_optional)
218 goto done;
219 if (details->chroot != NULL)
220 sudo_warnx(U_("starting from %s"), "/");
221 }
222 }
223 }
224
225 ret = true;
226
227 done:
228 debug_return_bool(ret);
229 }
230
231 /*
232 * Setup the execution environment and execute the command.
233 * If SELinux is enabled, run the command via sesh, otherwise
234 * execute it directly.
235 * If the exec fails, cstat is filled in with the value of errno.
236 */
237 void
exec_cmnd(struct command_details * details,int intercept_fd,int errfd)238 exec_cmnd(struct command_details *details, int intercept_fd, int errfd)
239 {
240 debug_decl(exec_cmnd, SUDO_DEBUG_EXEC);
241
242 restore_signals();
243 if (exec_setup(details, intercept_fd, errfd) == true) {
244 /* headed for execve() */
245 #ifdef HAVE_SELINUX
246 if (ISSET(details->flags, CD_RBAC_ENABLED)) {
247 selinux_execve(details->execfd, details->command, details->argv,
248 details->envp, ISSET(details->flags, CD_NOEXEC));
249 } else
250 #endif
251 {
252 sudo_execve(details->execfd, details->command, details->argv,
253 details->envp, intercept_fd, details->flags);
254 }
255 }
256 sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to exec %s: %s",
257 details->command, strerror(errno));
258 debug_return;
259 }
260
261 /*
262 * Check for caught signals sent to sudo before command execution.
263 * Also suspends the process if SIGTSTP was caught.
264 * Returns true if we should terminate, else false.
265 */
266 bool
sudo_terminated(struct command_status * cstat)267 sudo_terminated(struct command_status *cstat)
268 {
269 int signo;
270 bool sigtstp = false;
271 debug_decl(sudo_terminated, SUDO_DEBUG_EXEC);
272
273 for (signo = 0; signo < NSIG; signo++) {
274 if (signal_pending(signo)) {
275 switch (signo) {
276 case SIGCHLD:
277 /* Ignore. */
278 break;
279 case SIGTSTP:
280 /* Suspend below if not terminated. */
281 sigtstp = true;
282 break;
283 default:
284 /* Terminal signal, do not exec command. */
285 cstat->type = CMD_WSTATUS;
286 cstat->val = signo + 128;
287 debug_return_bool(true);
288 break;
289 }
290 }
291 }
292 if (sigtstp) {
293 struct sigaction sa;
294 sigset_t set, oset;
295
296 /* Send SIGTSTP to ourselves, unblocking it if needed. */
297 memset(&sa, 0, sizeof(sa));
298 sigemptyset(&sa.sa_mask);
299 sa.sa_flags = SA_RESTART;
300 sa.sa_handler = SIG_DFL;
301 if (sudo_sigaction(SIGTSTP, &sa, NULL) != 0)
302 sudo_warn(U_("unable to set handler for signal %d"), SIGTSTP);
303 sigemptyset(&set);
304 sigaddset(&set, SIGTSTP);
305 sigprocmask(SIG_UNBLOCK, &set, &oset);
306 if (kill(getpid(), SIGTSTP) != 0)
307 sudo_warn("kill(%d, SIGTSTP)", (int)getpid());
308 sigprocmask(SIG_SETMASK, &oset, NULL);
309 /* No need to restore old SIGTSTP handler. */
310 }
311 debug_return_bool(false);
312 }
313
314 #if SUDO_API_VERSION != SUDO_API_MKVERSION(1, 17)
315 # error "Update sudo_needs_pty() after changing the plugin API"
316 #endif
317 static bool
sudo_needs_pty(struct command_details * details)318 sudo_needs_pty(struct command_details *details)
319 {
320 struct plugin_container *plugin;
321
322 if (ISSET(details->flags, CD_USE_PTY|CD_INTERCEPT|CD_LOG_SUBCMDS))
323 return true;
324
325 TAILQ_FOREACH(plugin, &io_plugins, entries) {
326 if (plugin->u.io->log_ttyin != NULL ||
327 plugin->u.io->log_ttyout != NULL ||
328 plugin->u.io->log_stdin != NULL ||
329 plugin->u.io->log_stdout != NULL ||
330 plugin->u.io->log_stderr != NULL ||
331 plugin->u.io->change_winsize != NULL ||
332 plugin->u.io->log_suspend != NULL)
333 return true;
334 }
335 return false;
336 }
337
338 /*
339 * If we are not running the command in a pty, we were not invoked as
340 * sudoedit, there is no command timeout and there is no close function,
341 * sudo can exec the command directly (and not wait).
342 */
343 static bool
direct_exec_allowed(struct command_details * details)344 direct_exec_allowed(struct command_details *details)
345 {
346 struct plugin_container *plugin;
347 debug_decl(direct_exec_allowed, SUDO_DEBUG_EXEC);
348
349 /* Assumes sudo_needs_pty() was already checked. */
350 if (ISSET(details->flags, CD_RBAC_ENABLED|CD_SET_TIMEOUT|CD_SUDOEDIT) ||
351 policy_plugin.u.policy->close != NULL)
352 debug_return_bool(false);
353
354 TAILQ_FOREACH(plugin, &audit_plugins, entries) {
355 if (plugin->u.audit->close != NULL)
356 debug_return_bool(false);
357 }
358
359 debug_return_bool(true);
360 }
361
362 /*
363 * Execute a command, potentially in a pty with I/O logging, and
364 * wait for it to finish.
365 * This is a little bit tricky due to how POSIX job control works and
366 * we fact that we have two different controlling terminals to deal with.
367 */
368 int
sudo_execute(struct command_details * details,struct command_status * cstat)369 sudo_execute(struct command_details *details, struct command_status *cstat)
370 {
371 debug_decl(sudo_execute, SUDO_DEBUG_EXEC);
372
373 /* If running in background mode, fork and exit. */
374 if (ISSET(details->flags, CD_BACKGROUND)) {
375 switch (sudo_debug_fork()) {
376 case -1:
377 cstat->type = CMD_ERRNO;
378 cstat->val = errno;
379 debug_return_int(-1);
380 case 0:
381 /* child continues without controlling terminal */
382 (void)setpgid(0, 0);
383 break;
384 default:
385 /* parent exits (but does not flush buffers) */
386 sudo_debug_exit_int(__func__, __FILE__, __LINE__,
387 sudo_debug_subsys, 0);
388 _exit(EXIT_SUCCESS);
389 }
390 }
391
392 /*
393 * Restore resource limits before running.
394 * We must do this *before* calling the PAM session module.
395 */
396 restore_limits();
397
398 /*
399 * Run the command in a new pty if there is an I/O plugin or the policy
400 * has requested a pty. If /dev/tty is unavailable and no I/O plugin
401 * is configured, this returns false and we run the command without a pty.
402 */
403 if (sudo_needs_pty(details)) {
404 if (exec_pty(details, cstat))
405 goto done;
406 }
407
408 /*
409 * If we are not running the command in a pty, we may be able to
410 * exec directly, depending on the plugins used.
411 */
412 if (direct_exec_allowed(details)) {
413 if (!sudo_terminated(cstat)) {
414 exec_cmnd(details, -1, -1);
415 cstat->type = CMD_ERRNO;
416 cstat->val = errno;
417 }
418 goto done;
419 }
420
421 /*
422 * Run the command in the existing tty (if any) and wait for it to finish.
423 */
424 exec_nopty(details, cstat);
425
426 done:
427 /* The caller will run any plugin close functions. */
428 debug_return_int(cstat->type == CMD_ERRNO ? -1 : 0);
429 }
430
431 /*
432 * Kill command with increasing urgency.
433 */
434 void
terminate_command(pid_t pid,bool use_pgrp)435 terminate_command(pid_t pid, bool use_pgrp)
436 {
437 debug_decl(terminate_command, SUDO_DEBUG_EXEC);
438
439 /* Avoid killing more than a single process or process group. */
440 if (pid <= 0)
441 debug_return;
442
443 /*
444 * Note that SIGCHLD will interrupt the sleep()
445 */
446 if (use_pgrp) {
447 sudo_debug_printf(SUDO_DEBUG_INFO, "killpg %d SIGHUP", (int)pid);
448 killpg(pid, SIGHUP);
449 sudo_debug_printf(SUDO_DEBUG_INFO, "killpg %d SIGTERM", (int)pid);
450 killpg(pid, SIGTERM);
451 sleep(2);
452 sudo_debug_printf(SUDO_DEBUG_INFO, "killpg %d SIGKILL", (int)pid);
453 killpg(pid, SIGKILL);
454 } else {
455 sudo_debug_printf(SUDO_DEBUG_INFO, "kill %d SIGHUP", (int)pid);
456 kill(pid, SIGHUP);
457 sudo_debug_printf(SUDO_DEBUG_INFO, "kill %d SIGTERM", (int)pid);
458 kill(pid, SIGTERM);
459 sleep(2);
460 sudo_debug_printf(SUDO_DEBUG_INFO, "kill %d SIGKILL", (int)pid);
461 kill(pid, SIGKILL);
462 }
463
464 debug_return;
465 }
466