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