diff options
-rw-r--r-- | meta/recipes-extended/sudo/files/CVE-2021-23240.patch | 419 | ||||
-rw-r--r-- | meta/recipes-extended/sudo/sudo_1.9.3.bb | 1 |
2 files changed, 420 insertions, 0 deletions
diff --git a/meta/recipes-extended/sudo/files/CVE-2021-23240.patch b/meta/recipes-extended/sudo/files/CVE-2021-23240.patch new file mode 100644 index 0000000000..740a13cd90 --- /dev/null +++ b/meta/recipes-extended/sudo/files/CVE-2021-23240.patch | |||
@@ -0,0 +1,419 @@ | |||
1 | Upstream-Status: Backport [https://www.sudo.ws/repos/sudo/rev/8fcb36ef422a] | ||
2 | Signed-off-by: Anuj Mittal <anuj.mittal@intel.com> | ||
3 | CVE: CVE-2021-23240 | ||
4 | |||
5 | # HG changeset patch | ||
6 | # User Todd C. Miller <Todd.Miller@sudo.ws> | ||
7 | # Date 1609953360 25200 | ||
8 | # Node ID 8fcb36ef422a251fe33738a347551439944a4a37 | ||
9 | # Parent ea19d0073c02951bbbf35342dd63304da83edce8 | ||
10 | Add security checks before using temp files for SELinux RBAC sudoedit. | ||
11 | Otherwise, it may be possible for the user running sudoedit to | ||
12 | replace the newly-created temporary files with a symbolic link and | ||
13 | have sudoedit set the owner of an arbitrary file. | ||
14 | Problem reported by Matthias Gerstner of SUSE. | ||
15 | |||
16 | diff -r ea19d0073c02 -r 8fcb36ef422a src/copy_file.c | ||
17 | --- a/src/copy_file.c Wed Jan 06 10:16:00 2021 -0700 | ||
18 | +++ b/src/copy_file.c Wed Jan 06 10:16:00 2021 -0700 | ||
19 | @@ -1,7 +1,7 @@ | ||
20 | /* | ||
21 | * SPDX-License-Identifier: ISC | ||
22 | * | ||
23 | - * Copyright (c) 2020 Todd C. Miller <Todd.Miller@sudo.ws> | ||
24 | + * Copyright (c) 2020-2021 Todd C. Miller <Todd.Miller@sudo.ws> | ||
25 | * | ||
26 | * Permission to use, copy, modify, and distribute this software for any | ||
27 | * purpose with or without fee is hereby granted, provided that the above | ||
28 | @@ -23,6 +23,8 @@ | ||
29 | |||
30 | #include <config.h> | ||
31 | |||
32 | +#include <sys/stat.h> | ||
33 | + | ||
34 | #include <stdlib.h> | ||
35 | #include <unistd.h> | ||
36 | #include <errno.h> | ||
37 | @@ -134,3 +136,34 @@ | ||
38 | sudo_warn(U_("unable to write to %s"), dst); | ||
39 | debug_return_int(-1); | ||
40 | } | ||
41 | + | ||
42 | +#ifdef HAVE_SELINUX | ||
43 | +bool | ||
44 | +sudo_check_temp_file(int tfd, const char *tfile, uid_t uid, struct stat *sb) | ||
45 | +{ | ||
46 | + struct stat sbuf; | ||
47 | + debug_decl(sudo_check_temp_file, SUDO_DEBUG_UTIL); | ||
48 | + | ||
49 | + if (sb == NULL) | ||
50 | + sb = &sbuf; | ||
51 | + | ||
52 | + if (fstat(tfd, sb) == -1) { | ||
53 | + sudo_warn(U_("unable to stat %s"), tfile); | ||
54 | + debug_return_bool(false); | ||
55 | + } | ||
56 | + if (!S_ISREG(sb->st_mode)) { | ||
57 | + sudo_warnx(U_("%s: not a regular file"), tfile); | ||
58 | + debug_return_bool(false); | ||
59 | + } | ||
60 | + if ((sb->st_mode & ALLPERMS) != (S_IRUSR|S_IWUSR)) { | ||
61 | + sudo_warnx(U_("%s: bad file mode: 0%o"), tfile, sb->st_mode & ALLPERMS); | ||
62 | + debug_return_bool(false); | ||
63 | + } | ||
64 | + if (sb->st_uid != uid) { | ||
65 | + sudo_warnx(U_("%s is owned by uid %u, should be %u"), | ||
66 | + tfile, (unsigned int)sb->st_uid, (unsigned int)uid); | ||
67 | + debug_return_bool(false); | ||
68 | + } | ||
69 | + debug_return_bool(true); | ||
70 | +} | ||
71 | +#endif /* SELINUX */ | ||
72 | diff -r ea19d0073c02 -r 8fcb36ef422a src/sesh.c | ||
73 | --- a/src/sesh.c Wed Jan 06 10:16:00 2021 -0700 | ||
74 | +++ b/src/sesh.c Wed Jan 06 10:16:00 2021 -0700 | ||
75 | @@ -1,7 +1,7 @@ | ||
76 | /* | ||
77 | * SPDX-License-Identifier: ISC | ||
78 | * | ||
79 | - * Copyright (c) 2008, 2010-2018, 2020 Todd C. Miller <Todd.Miller@sudo.ws> | ||
80 | + * Copyright (c) 2008, 2010-2018, 2020-2021 Todd C. Miller <Todd.Miller@sudo.ws> | ||
81 | * | ||
82 | * Permission to use, copy, modify, and distribute this software for any | ||
83 | * purpose with or without fee is hereby granted, provided that the above | ||
84 | @@ -132,7 +132,7 @@ | ||
85 | static int | ||
86 | sesh_sudoedit(int argc, char *argv[]) | ||
87 | { | ||
88 | - int i, oflags_dst, post, ret = SESH_ERR_FAILURE; | ||
89 | + int i, oflags_src, oflags_dst, post, ret = SESH_ERR_FAILURE; | ||
90 | int fd_src = -1, fd_dst = -1, follow = 0; | ||
91 | struct stat sb; | ||
92 | struct timespec times[2]; | ||
93 | @@ -174,10 +174,12 @@ | ||
94 | debug_return_int(SESH_ERR_BAD_PATHS); | ||
95 | |||
96 | /* | ||
97 | - * Use O_EXCL if we are not in the post editing stage | ||
98 | - * so that it's ensured that the temporary files are | ||
99 | - * created by us and that we are not opening any symlinks. | ||
100 | + * In the pre-editing stage, use O_EXCL to ensure that the temporary | ||
101 | + * files are created by us and that we are not opening any symlinks. | ||
102 | + * In the post-editing stage, use O_NOFOLLOW so we don't follow symlinks | ||
103 | + * when opening the temporary files. | ||
104 | */ | ||
105 | + oflags_src = O_RDONLY|(post ? O_NONBLOCK|O_NOFOLLOW : follow); | ||
106 | oflags_dst = O_WRONLY|O_CREAT|(post ? follow : O_EXCL); | ||
107 | for (i = 0; i < argc - 1; i += 2) { | ||
108 | const char *path_src = argv[i]; | ||
109 | @@ -187,7 +189,7 @@ | ||
110 | * doesn't exist, that's OK, we'll create an empty | ||
111 | * destination file. | ||
112 | */ | ||
113 | - if ((fd_src = open(path_src, O_RDONLY|follow, S_IRUSR|S_IWUSR)) < 0) { | ||
114 | + if ((fd_src = open(path_src, oflags_src, S_IRUSR|S_IWUSR)) < 0) { | ||
115 | if (errno != ENOENT) { | ||
116 | sudo_warn("%s", path_src); | ||
117 | if (post) { | ||
118 | @@ -197,6 +199,14 @@ | ||
119 | goto cleanup_0; | ||
120 | } | ||
121 | } | ||
122 | + if (post) { | ||
123 | + /* Make sure the temporary file is safe and has the proper owner. */ | ||
124 | + if (!sudo_check_temp_file(fd_src, path_src, geteuid(), &sb)) { | ||
125 | + ret = SESH_ERR_SOME_FILES; | ||
126 | + goto nocleanup; | ||
127 | + } | ||
128 | + fcntl(fd_src, F_SETFL, fcntl(fd_src, F_GETFL, 0) & ~O_NONBLOCK); | ||
129 | + } | ||
130 | |||
131 | if ((fd_dst = open(path_dst, oflags_dst, post ? | ||
132 | (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) : (S_IRUSR|S_IWUSR))) < 0) { | ||
133 | @@ -214,10 +224,7 @@ | ||
134 | off_t len_dst = -1; | ||
135 | |||
136 | if (post) { | ||
137 | - if (fstat(fd_src, &sb) != 0) { | ||
138 | - ret = SESH_ERR_SOME_FILES; | ||
139 | - goto nocleanup; | ||
140 | - } | ||
141 | + /* sudo_check_temp_file() filled in sb for us. */ | ||
142 | len_src = sb.st_size; | ||
143 | if (fstat(fd_dst, &sb) != 0) { | ||
144 | ret = SESH_ERR_SOME_FILES; | ||
145 | diff -r ea19d0073c02 -r 8fcb36ef422a src/sudo_edit.c | ||
146 | --- a/src/sudo_edit.c Wed Jan 06 10:16:00 2021 -0700 | ||
147 | +++ b/src/sudo_edit.c Wed Jan 06 10:16:00 2021 -0700 | ||
148 | @@ -1,7 +1,7 @@ | ||
149 | /* | ||
150 | * SPDX-License-Identifier: ISC | ||
151 | * | ||
152 | - * Copyright (c) 2004-2008, 2010-2020 Todd C. Miller <Todd.Miller@sudo.ws> | ||
153 | + * Copyright (c) 2004-2008, 2010-2021 Todd C. Miller <Todd.Miller@sudo.ws> | ||
154 | * | ||
155 | * Permission to use, copy, modify, and distribute this software for any | ||
156 | * purpose with or without fee is hereby granted, provided that the above | ||
157 | @@ -259,8 +259,10 @@ | ||
158 | } else { | ||
159 | len = asprintf(tfile, "%s/%s.XXXXXXXX", edit_tmpdir, cp); | ||
160 | } | ||
161 | - if (len == -1) | ||
162 | - sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); | ||
163 | + if (len == -1) { | ||
164 | + sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); | ||
165 | + debug_return_int(-1); | ||
166 | + } | ||
167 | tfd = mkstemps(*tfile, suff ? strlen(suff) : 0); | ||
168 | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, | ||
169 | "%s -> %s, fd %d", ofile, *tfile, tfd); | ||
170 | @@ -735,7 +737,8 @@ | ||
171 | |||
172 | #ifdef HAVE_SELINUX | ||
173 | static int | ||
174 | -selinux_run_helper(char *argv[], char *envp[]) | ||
175 | +selinux_run_helper(uid_t uid, gid_t gid, int ngroups, GETGROUPS_T *groups, | ||
176 | + char *const argv[], char *const envp[]) | ||
177 | { | ||
178 | int status, ret = SESH_ERR_FAILURE; | ||
179 | const char *sesh; | ||
180 | @@ -755,8 +758,10 @@ | ||
181 | break; | ||
182 | case 0: | ||
183 | /* child runs sesh in new context */ | ||
184 | - if (selinux_setcon() == 0) | ||
185 | + if (selinux_setcon() == 0) { | ||
186 | + switch_user(uid, gid, ngroups, groups); | ||
187 | execve(sesh, argv, envp); | ||
188 | + } | ||
189 | _exit(SESH_ERR_FAILURE); | ||
190 | default: | ||
191 | /* parent waits */ | ||
192 | @@ -775,7 +780,7 @@ | ||
193 | struct tempfile *tf, char *files[], int nfiles) | ||
194 | { | ||
195 | char **sesh_args, **sesh_ap; | ||
196 | - int i, rc, sesh_nargs; | ||
197 | + int i, error, sesh_nargs, ret = -1; | ||
198 | struct stat sb; | ||
199 | debug_decl(selinux_edit_create_tfiles, SUDO_DEBUG_EDIT); | ||
200 | |||
201 | @@ -787,7 +792,7 @@ | ||
202 | sesh_args = sesh_ap = reallocarray(NULL, sesh_nargs, sizeof(char *)); | ||
203 | if (sesh_args == NULL) { | ||
204 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); | ||
205 | - debug_return_int(-1); | ||
206 | + goto done; | ||
207 | } | ||
208 | *sesh_ap++ = "sesh"; | ||
209 | *sesh_ap++ = "-e"; | ||
210 | @@ -795,7 +800,6 @@ | ||
211 | *sesh_ap++ = "-h"; | ||
212 | *sesh_ap++ = "0"; | ||
213 | |||
214 | - /* XXX - temp files should be created with user's context */ | ||
215 | for (i = 0; i < nfiles; i++) { | ||
216 | char *tfile, *ofile = files[i]; | ||
217 | int tfd; | ||
218 | @@ -813,8 +817,7 @@ | ||
219 | if (tfd == -1) { | ||
220 | sudo_warn("mkstemps"); | ||
221 | free(tfile); | ||
222 | - free(sesh_args); | ||
223 | - debug_return_int(-1); | ||
224 | + goto done; | ||
225 | } | ||
226 | /* Helper will re-create temp file with proper security context. */ | ||
227 | close(tfd); | ||
228 | @@ -825,8 +828,10 @@ | ||
229 | *sesh_ap = NULL; | ||
230 | |||
231 | /* Run sesh -e [-h] 0 <o1> <t1> ... <on> <tn> */ | ||
232 | - rc = selinux_run_helper(sesh_args, command_details->envp); | ||
233 | - switch (rc) { | ||
234 | + error = selinux_run_helper(command_details->uid, command_details->gid, | ||
235 | + command_details->ngroups, command_details->groups, sesh_args, | ||
236 | + command_details->envp); | ||
237 | + switch (error) { | ||
238 | case SESH_SUCCESS: | ||
239 | break; | ||
240 | case SESH_ERR_BAD_PATHS: | ||
241 | @@ -836,21 +841,35 @@ | ||
242 | case SESH_ERR_KILLED: | ||
243 | sudo_fatalx("%s", U_("sesh: killed by a signal")); | ||
244 | default: | ||
245 | - sudo_fatalx(U_("sesh: unknown error %d"), rc); | ||
246 | + sudo_warnx(U_("sesh: unknown error %d"), error); | ||
247 | + goto done; | ||
248 | } | ||
249 | |||
250 | - /* Chown to user's UID so they can edit the temporary files. */ | ||
251 | for (i = 0; i < nfiles; i++) { | ||
252 | - if (chown(tf[i].tfile, user_details.uid, user_details.gid) != 0) { | ||
253 | + int tfd = open(tf[i].tfile, O_RDONLY|O_NONBLOCK|O_NOFOLLOW); | ||
254 | + if (tfd == -1) { | ||
255 | + sudo_warn(U_("unable to open %s"), tf[i].tfile); | ||
256 | + goto done; | ||
257 | + } | ||
258 | + if (!sudo_check_temp_file(tfd, tf[i].tfile, command_details->uid, NULL)) { | ||
259 | + close(tfd); | ||
260 | + goto done; | ||
261 | + } | ||
262 | + if (fchown(tfd, user_details.uid, user_details.gid) != 0) { | ||
263 | sudo_warn("unable to chown(%s) to %d:%d for editing", | ||
264 | tf[i].tfile, user_details.uid, user_details.gid); | ||
265 | + close(tfd); | ||
266 | + goto done; | ||
267 | } | ||
268 | + close(tfd); | ||
269 | } | ||
270 | + ret = nfiles; | ||
271 | |||
272 | +done: | ||
273 | /* Contents of tf will be freed by caller. */ | ||
274 | free(sesh_args); | ||
275 | |||
276 | - return (nfiles); | ||
277 | + debug_return_int(ret); | ||
278 | } | ||
279 | |||
280 | static int | ||
281 | @@ -858,7 +877,8 @@ | ||
282 | struct tempfile *tf, int nfiles, struct timespec *times) | ||
283 | { | ||
284 | char **sesh_args, **sesh_ap; | ||
285 | - int i, rc, sesh_nargs, ret = 1; | ||
286 | + int i, error, sesh_nargs, ret = 1; | ||
287 | + int tfd = -1; | ||
288 | struct timespec ts; | ||
289 | struct stat sb; | ||
290 | debug_decl(selinux_edit_copy_tfiles, SUDO_DEBUG_EDIT); | ||
291 | @@ -879,33 +899,43 @@ | ||
292 | |||
293 | /* Construct args for sesh -e 1 */ | ||
294 | for (i = 0; i < nfiles; i++) { | ||
295 | - if (stat(tf[i].tfile, &sb) == 0) { | ||
296 | - mtim_get(&sb, ts); | ||
297 | - if (tf[i].osize == sb.st_size && sudo_timespeccmp(&tf[i].omtim, &ts, ==)) { | ||
298 | - /* | ||
299 | - * If mtime and size match but the user spent no measurable | ||
300 | - * time in the editor we can't tell if the file was changed. | ||
301 | - */ | ||
302 | - if (sudo_timespeccmp(×[0], ×[1], !=)) { | ||
303 | - sudo_warnx(U_("%s unchanged"), tf[i].ofile); | ||
304 | - unlink(tf[i].tfile); | ||
305 | - continue; | ||
306 | - } | ||
307 | + if (tfd != -1) | ||
308 | + close(tfd); | ||
309 | + if ((tfd = open(tf[i].tfile, O_RDONLY|O_NONBLOCK|O_NOFOLLOW)) == -1) { | ||
310 | + sudo_warn(U_("unable to open %s"), tf[i].tfile); | ||
311 | + continue; | ||
312 | + } | ||
313 | + if (!sudo_check_temp_file(tfd, tf[i].tfile, user_details.uid, &sb)) | ||
314 | + continue; | ||
315 | + mtim_get(&sb, ts); | ||
316 | + if (tf[i].osize == sb.st_size && sudo_timespeccmp(&tf[i].omtim, &ts, ==)) { | ||
317 | + /* | ||
318 | + * If mtime and size match but the user spent no measurable | ||
319 | + * time in the editor we can't tell if the file was changed. | ||
320 | + */ | ||
321 | + if (sudo_timespeccmp(×[0], ×[1], !=)) { | ||
322 | + sudo_warnx(U_("%s unchanged"), tf[i].ofile); | ||
323 | + unlink(tf[i].tfile); | ||
324 | + continue; | ||
325 | } | ||
326 | } | ||
327 | *sesh_ap++ = tf[i].tfile; | ||
328 | *sesh_ap++ = tf[i].ofile; | ||
329 | - if (chown(tf[i].tfile, command_details->uid, command_details->gid) != 0) { | ||
330 | + if (fchown(tfd, command_details->uid, command_details->gid) != 0) { | ||
331 | sudo_warn("unable to chown(%s) back to %d:%d", tf[i].tfile, | ||
332 | command_details->uid, command_details->gid); | ||
333 | } | ||
334 | } | ||
335 | *sesh_ap = NULL; | ||
336 | + if (tfd != -1) | ||
337 | + close(tfd); | ||
338 | |||
339 | if (sesh_ap - sesh_args > 3) { | ||
340 | /* Run sesh -e 1 <t1> <o1> ... <tn> <on> */ | ||
341 | - rc = selinux_run_helper(sesh_args, command_details->envp); | ||
342 | - switch (rc) { | ||
343 | + error = selinux_run_helper(command_details->uid, command_details->gid, | ||
344 | + command_details->ngroups, command_details->groups, sesh_args, | ||
345 | + command_details->envp); | ||
346 | + switch (error) { | ||
347 | case SESH_SUCCESS: | ||
348 | ret = 0; | ||
349 | break; | ||
350 | @@ -921,7 +951,7 @@ | ||
351 | sudo_warnx("%s", U_("sesh: killed by a signal")); | ||
352 | break; | ||
353 | default: | ||
354 | - sudo_warnx(U_("sesh: unknown error %d"), rc); | ||
355 | + sudo_warnx(U_("sesh: unknown error %d"), error); | ||
356 | break; | ||
357 | } | ||
358 | if (ret != 0) | ||
359 | @@ -943,7 +973,7 @@ | ||
360 | { | ||
361 | struct command_details saved_command_details; | ||
362 | char **nargv = NULL, **ap, **files = NULL; | ||
363 | - int errors, i, ac, nargc, rc; | ||
364 | + int errors, i, ac, nargc, ret; | ||
365 | int editor_argc = 0, nfiles = 0; | ||
366 | struct timespec times[2]; | ||
367 | struct tempfile *tf = NULL; | ||
368 | @@ -1038,7 +1068,7 @@ | ||
369 | command_details->ngroups = user_details.ngroups; | ||
370 | command_details->groups = user_details.groups; | ||
371 | command_details->argv = nargv; | ||
372 | - rc = run_command(command_details); | ||
373 | + ret = run_command(command_details); | ||
374 | if (sudo_gettime_real(×[1]) == -1) { | ||
375 | sudo_warn("%s", U_("unable to read the clock")); | ||
376 | goto cleanup; | ||
377 | @@ -1062,14 +1092,14 @@ | ||
378 | errors = sudo_edit_copy_tfiles(command_details, tf, nfiles, times); | ||
379 | if (errors) { | ||
380 | /* Preserve the edited temporary files. */ | ||
381 | - rc = W_EXITCODE(1, 0); | ||
382 | + ret = W_EXITCODE(1, 0); | ||
383 | } | ||
384 | |||
385 | for (i = 0; i < nfiles; i++) | ||
386 | free(tf[i].tfile); | ||
387 | free(tf); | ||
388 | free(nargv); | ||
389 | - debug_return_int(rc); | ||
390 | + debug_return_int(ret); | ||
391 | |||
392 | cleanup: | ||
393 | /* Clean up temp files and return. */ | ||
394 | diff -r ea19d0073c02 -r 8fcb36ef422a src/sudo_exec.h | ||
395 | --- a/src/sudo_exec.h Wed Jan 06 10:16:00 2021 -0700 | ||
396 | +++ b/src/sudo_exec.h Wed Jan 06 10:16:00 2021 -0700 | ||
397 | @@ -1,7 +1,7 @@ | ||
398 | /* | ||
399 | * SPDX-License-Identifier: ISC | ||
400 | * | ||
401 | - * Copyright (c) 2010-2016 Todd C. Miller <Todd.Miller@sudo.ws> | ||
402 | + * Copyright (c) 2010-2017, 2020-2021 Todd C. Miller <Todd.Miller@sudo.ws> | ||
403 | * | ||
404 | * Permission to use, copy, modify, and distribute this software for any | ||
405 | * purpose with or without fee is hereby granted, provided that the above | ||
406 | @@ -84,9 +84,11 @@ | ||
407 | */ | ||
408 | struct command_details; | ||
409 | struct command_status; | ||
410 | +struct stat; | ||
411 | |||
412 | /* copy_file.c */ | ||
413 | int sudo_copy_file(const char *src, int src_fd, off_t src_len, const char *dst, int dst_fd, off_t dst_len); | ||
414 | +bool sudo_check_temp_file(int tfd, const char *tname, uid_t uid, struct stat *sb); | ||
415 | |||
416 | /* exec.c */ | ||
417 | void exec_cmnd(struct command_details *details, int errfd); | ||
418 | |||
419 | |||
diff --git a/meta/recipes-extended/sudo/sudo_1.9.3.bb b/meta/recipes-extended/sudo/sudo_1.9.3.bb index 132d9a8cb9..4edcbfc607 100644 --- a/meta/recipes-extended/sudo/sudo_1.9.3.bb +++ b/meta/recipes-extended/sudo/sudo_1.9.3.bb | |||
@@ -4,6 +4,7 @@ SRC_URI = "https://www.sudo.ws/dist/sudo-${PV}.tar.gz \ | |||
4 | ${@bb.utils.contains('DISTRO_FEATURES', 'pam', '${PAM_SRC_URI}', '', d)} \ | 4 | ${@bb.utils.contains('DISTRO_FEATURES', 'pam', '${PAM_SRC_URI}', '', d)} \ |
5 | file://0001-sudo.conf.in-fix-conflict-with-multilib.patch \ | 5 | file://0001-sudo.conf.in-fix-conflict-with-multilib.patch \ |
6 | file://CVE-2021-23239.patch \ | 6 | file://CVE-2021-23239.patch \ |
7 | file://CVE-2021-23240.patch \ | ||
7 | " | 8 | " |
8 | 9 | ||
9 | PAM_SRC_URI = "file://sudo.pam" | 10 | PAM_SRC_URI = "file://sudo.pam" |