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