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 <fcntl.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #ifdef HAVE_PRIV_SET
32 # include <priv.h>
33 #endif
34 #include <errno.h>
35
36 #include "sudo.h"
37 #include "sudo_exec.h"
38
39 /*
40 * Disable execution of child processes in the command we are about
41 * to run. On systems with privilege sets, we can remove the exec
42 * privilege. On other systems we use LD_PRELOAD and the like.
43 */
44 char **
disable_execute(char * envp[],const char * dso)45 disable_execute(char *envp[], const char *dso)
46 {
47 debug_decl(disable_execute, SUDO_DEBUG_UTIL);
48
49 #ifdef HAVE_PRIV_SET
50 /* Solaris privileges, remove PRIV_PROC_EXEC post-execve. */
51 (void)priv_set(PRIV_ON, PRIV_INHERITABLE, "PRIV_FILE_DAC_READ", NULL);
52 (void)priv_set(PRIV_ON, PRIV_INHERITABLE, "PRIV_FILE_DAC_WRITE", NULL);
53 (void)priv_set(PRIV_ON, PRIV_INHERITABLE, "PRIV_FILE_DAC_SEARCH", NULL);
54 if (priv_set(PRIV_OFF, PRIV_LIMIT, "PRIV_PROC_EXEC", NULL) == 0)
55 debug_return_ptr(envp);
56 sudo_warn("%s", U_("unable to remove PRIV_PROC_EXEC from PRIV_LIMIT"));
57 #endif /* HAVE_PRIV_SET */
58
59 #ifdef RTLD_PRELOAD_VAR
60 if (dso != NULL)
61 envp = sudo_preload_dso(envp, dso, -1);
62 #endif /* RTLD_PRELOAD_VAR */
63
64 debug_return_ptr(envp);
65 }
66
67 /*
68 * Trap execution of child processes in the command we are about to run.
69 * Uses LD_PRELOAD and the like to perform a policy check on child commands.
70 */
71 static char **
enable_intercept(char * envp[],const char * dso,int intercept_fd)72 enable_intercept(char *envp[], const char *dso, int intercept_fd)
73 {
74 debug_decl(enable_intercept, SUDO_DEBUG_UTIL);
75
76 if (dso != NULL) {
77 #ifdef RTLD_PRELOAD_VAR
78 if (intercept_fd == -1)
79 sudo_fatalx("%s: no intercept fd", __func__);
80
81 envp = sudo_preload_dso(envp, dso, intercept_fd);
82 #else
83 /* Intercept not supported, envp unchanged. */
84 if (intercept_fd != -1)
85 close(intercept_fd);
86 #endif /* RTLD_PRELOAD_VAR */
87 }
88
89 debug_return_ptr(envp);
90 }
91
92 /*
93 * Like execve(2) but falls back to running through /bin/sh
94 * ala execvp(3) if we get ENOEXEC.
95 */
96 int
sudo_execve(int fd,const char * path,char * const argv[],char * envp[],int intercept_fd,int flags)97 sudo_execve(int fd, const char *path, char *const argv[], char *envp[],
98 int intercept_fd, int flags)
99 {
100 debug_decl(sudo_execve, SUDO_DEBUG_UTIL);
101
102 sudo_debug_execve(SUDO_DEBUG_INFO, path, argv, envp);
103
104 /* Modify the environment as needed to trap execve(). */
105 if (ISSET(flags, CD_NOEXEC))
106 envp = disable_execute(envp, sudo_conf_noexec_path());
107 else if (ISSET(flags, CD_INTERCEPT|CD_LOG_SUBCMDS))
108 envp = enable_intercept(envp, sudo_conf_intercept_path(), intercept_fd);
109
110 #ifdef HAVE_FEXECVE
111 if (fd != -1)
112 fexecve(fd, argv, envp);
113 else
114 #endif
115 execve(path, argv, envp);
116 if (fd == -1 && errno == ENOEXEC) {
117 int argc;
118 char **nargv;
119
120 for (argc = 0; argv[argc] != NULL; argc++)
121 continue;
122 nargv = reallocarray(NULL, argc + 2, sizeof(char *));
123 if (nargv != NULL) {
124 nargv[0] = "sh";
125 nargv[1] = (char *)path;
126 memcpy(nargv + 2, argv + 1, argc * sizeof(char *));
127 execve(_PATH_SUDO_BSHELL, nargv, envp);
128 free(nargv);
129 }
130 }
131 debug_return_int(-1);
132 }
133