Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/src/regress/noexec/check_noexec.c
1532 views
1
/*
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 2016, 2022 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/types.h>
22
#include <sys/wait.h>
23
#include <stdio.h>
24
#include <stdlib.h>
25
#ifdef HAVE_STDBOOL_H
26
# include <stdbool.h>
27
#else
28
# include <compat/stdbool.h>
29
#endif /* HAVE_STDBOOL_H */
30
#include <string.h>
31
#ifdef HAVE_WORDEXP_H
32
# include <wordexp.h>
33
#endif
34
#include <signal.h>
35
#include <unistd.h>
36
#include <limits.h>
37
#include <errno.h>
38
39
#include <sudo_compat.h>
40
#include <sudo_fatal.h>
41
#include <sudo_util.h>
42
#include <sudo_queue.h>
43
#include <sudo_exec.h>
44
45
static bool verbose;
46
47
sudo_dso_public int main(int argc, char *argv[], char *envp[]);
48
49
static bool
50
report_status(int status, const char *what)
51
{
52
bool ret = false;
53
54
/* system() returns -1 for exec failure. */
55
if (status == -1) {
56
if (verbose)
57
printf("%s: OK (%s)\n", getprogname(), what);
58
return true;
59
}
60
61
/* check exit value, expecting 127 for failure */
62
if (WIFEXITED(status)) {
63
int exitval = WEXITSTATUS(status);
64
if (exitval == 127) {
65
if (verbose)
66
printf("%s: OK (%s)\n", getprogname(), what);
67
ret = true;
68
} else {
69
printf("%s: FAIL (%s) [%d]\n", getprogname(), what, exitval);
70
}
71
} else if (WIFSIGNALED(status)) {
72
printf("%s: FAIL (%s) [signal %d]\n", getprogname(), what,
73
WTERMSIG(status));
74
} else {
75
/* should not happen */
76
printf("%s: FAIL (%s) [status %d]\n", getprogname(), what, status);
77
}
78
79
return ret;
80
}
81
82
static int
83
try_execl(void)
84
{
85
pid_t child, pid;
86
int status;
87
88
child = fork();
89
switch (child) {
90
case -1:
91
sudo_fatal_nodebug("fork");
92
case 0:
93
/* child */
94
/* Try to exec /bin/true, else exit with value 127. */
95
execl("/bin/true", "true", (char *)0);
96
_exit(127);
97
default:
98
/* parent */
99
do {
100
pid = waitpid(child, &status, 0);
101
} while (pid == -1 && errno == EINTR);
102
if (pid == -1)
103
sudo_fatal_nodebug("waitpid");
104
105
if (report_status(status, "execl"))
106
return 0;
107
return 1;
108
}
109
}
110
111
static int
112
try_system(void)
113
{
114
int status;
115
116
/* Try to run /bin/true, system() returns 127 on exec failure. */
117
status = system("/bin/true > /dev/null 2>&1");
118
119
if (report_status(status, "system"))
120
return 0;
121
return 1;
122
}
123
124
#ifdef HAVE_WORDEXP_H
125
static int
126
try_wordexp(void)
127
{
128
wordexp_t we;
129
int rc, ret = 1;
130
131
/*
132
* sudo_noexec.so prevents command substitution via the WRDE_NOCMD flag
133
* where possible.
134
*/
135
rc = wordexp("$(/bin/echo foo)", &we, 0);
136
switch (rc) {
137
case -1:
138
/* sudo's wordexp() wrapper returns -1 if RTLD_NEXT is not supported. */
139
case 127:
140
/* Solaris 10 wordexp() returns 127 for execve() failure. */
141
#ifdef WRDE_ERRNO
142
case WRDE_ERRNO:
143
/* Solaris 11 wordexp() returns WRDE_ERRNO for execve() failure. */
144
#endif
145
if (verbose)
146
printf("%s: OK (wordexp) [%d]\n", getprogname(), rc);
147
ret = 0;
148
break;
149
case WRDE_SYNTAX:
150
/* FreeBSD returns WRDE_SYNTAX if it can't write to the shell process */
151
if (verbose)
152
printf("%s: OK (wordexp) [WRDE_SYNTAX]\n", getprogname());
153
ret = 0;
154
break;
155
case WRDE_CMDSUB:
156
if (verbose)
157
printf("%s: OK (wordexp) [WRDE_CMDSUB]\n", getprogname());
158
ret = 0;
159
break;
160
case 0:
161
/*
162
* On HP-UX 11.00 we don't seem to be able to add WRDE_NOCMD
163
* but the execve() wrapper prevents the command substitution.
164
*/
165
if (we.we_wordc == 0) {
166
if (verbose)
167
printf("%s: OK (wordexp) [%d]\n", getprogname(), rc);
168
wordfree(&we);
169
ret = 0;
170
break;
171
}
172
wordfree(&we);
173
FALLTHROUGH;
174
default:
175
printf("%s: FAIL (wordexp) [%d]\n", getprogname(), rc);
176
break;
177
}
178
return ret;
179
}
180
#endif
181
182
sudo_noreturn static void
183
usage(void)
184
{
185
fprintf(stderr, "usage: %s [-v] rexec | /path/to/sudo_noexec.so\n",
186
getprogname());
187
exit(EXIT_FAILURE);
188
}
189
190
int
191
main(int argc, char *argv[], char *envp[])
192
{
193
int ch, errors = 0, ntests = 0;
194
195
initprogname(argc > 0 ? argv[0] : "check_noexec");
196
197
while ((ch = getopt(argc, argv, "v")) != -1) {
198
switch (ch) {
199
case 'v':
200
verbose = true;
201
break;
202
default:
203
usage();
204
}
205
}
206
207
if (argc - optind != 1)
208
usage();
209
210
/* Disable execution for post-exec and re-exec ourself. */
211
if (strcmp(argv[optind], "rexec") != 0) {
212
const char *noexec = argv[optind];
213
argv[optind] = (char *)"rexec";
214
execve(argv[0], argv, disable_execute(envp, noexec));
215
sudo_fatalx_nodebug("execve");
216
}
217
218
ntests++;
219
errors += try_execl();
220
ntests++;
221
errors += try_system();
222
#ifdef HAVE_WORDEXP_H
223
ntests++;
224
errors += try_wordexp();
225
#endif
226
227
if (ntests != 0) {
228
printf("%s: %d tests run, %d errors, %d%% success rate\n",
229
getprogname(), ntests, errors, (ntests - errors) * 100 / ntests);
230
}
231
return errors;
232
}
233
234