/src/sudo/plugins/sudoers/sudoers.c
Line | Count | Source |
1 | | /* |
2 | | * SPDX-License-Identifier: ISC |
3 | | * |
4 | | * Copyright (c) 1993-1996, 1998-2023 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 | | * Sponsored in part by the Defense Advanced Research Projects |
19 | | * Agency (DARPA) and Air Force Research Laboratory, Air Force |
20 | | * Materiel Command, USAF, under agreement number F39502-99-1-0512. |
21 | | */ |
22 | | |
23 | | #ifdef __TANDEM |
24 | | # include <floss.h> |
25 | | #endif |
26 | | |
27 | | #include <config.h> |
28 | | |
29 | | #include <sys/types.h> |
30 | | #include <sys/resource.h> |
31 | | #include <sys/stat.h> |
32 | | #include <sys/socket.h> |
33 | | #include <stdio.h> |
34 | | #include <stdlib.h> |
35 | | #include <string.h> |
36 | | #include <unistd.h> |
37 | | #include <pwd.h> |
38 | | #include <errno.h> |
39 | | #include <fcntl.h> |
40 | | #include <grp.h> |
41 | | #include <netdb.h> |
42 | | #ifdef HAVE_LOGIN_CAP_H |
43 | | # include <login_cap.h> |
44 | | # ifndef LOGIN_DEFROOTCLASS |
45 | | # define LOGIN_DEFROOTCLASS "daemon" |
46 | | # endif |
47 | | # ifndef LOGIN_SETENV |
48 | | # define LOGIN_SETENV 0 |
49 | | # endif |
50 | | #endif |
51 | | #ifdef HAVE_SELINUX |
52 | | # include <selinux/selinux.h> |
53 | | #endif |
54 | | #include <ctype.h> |
55 | | #ifndef HAVE_GETADDRINFO |
56 | | # include <compat/getaddrinfo.h> |
57 | | #endif |
58 | | |
59 | | #include <sudoers.h> |
60 | | #include <timestamp.h> |
61 | | #include <sudo_iolog.h> |
62 | | |
63 | | /* |
64 | | * Prototypes |
65 | | */ |
66 | | static int set_cmnd(struct sudoers_context *ctx); |
67 | | static bool init_vars(struct sudoers_context *ctx, char * const *); |
68 | | static bool set_loginclass(struct sudoers_context *); |
69 | | static bool set_runaspw(struct sudoers_context *ctx, const char *, bool); |
70 | | static bool set_runasgr(struct sudoers_context *ctx, const char *, bool); |
71 | | |
72 | | /* |
73 | | * Globals |
74 | | */ |
75 | | static char *prev_user; |
76 | | static struct sudoers_context sudoers_ctx = SUDOERS_CONTEXT_INITIALIZER; |
77 | | static struct sudo_nss_list *snl; |
78 | | static bool unknown_runas_uid; |
79 | | static bool unknown_runas_gid; |
80 | | static int cmnd_status = NOT_FOUND_ERROR; |
81 | | static struct defaults_list initial_defaults = TAILQ_HEAD_INITIALIZER(initial_defaults); |
82 | | |
83 | | #ifdef __linux__ |
84 | | static struct rlimit nproclimit; |
85 | | #endif |
86 | | |
87 | | #ifdef SUDOERS_LOG_CLIENT |
88 | 6.02k | # define remote_iologs (!SLIST_EMPTY(&def_log_servers)) |
89 | | #else |
90 | | # define remote_iologs 0 |
91 | | #endif |
92 | | |
93 | | /* |
94 | | * Unlimit the number of processes since Linux's setuid() will |
95 | | * apply resource limits when changing uid and return EAGAIN if |
96 | | * nproc would be exceeded by the uid switch. |
97 | | */ |
98 | | static void |
99 | | unlimit_nproc(void) |
100 | 26.9k | { |
101 | 26.9k | #ifdef __linux__ |
102 | 26.9k | struct rlimit rl; |
103 | 26.9k | debug_decl(unlimit_nproc, SUDOERS_DEBUG_UTIL); |
104 | | |
105 | 26.9k | if (getrlimit(RLIMIT_NPROC, &nproclimit) != 0) |
106 | 0 | sudo_warn("getrlimit(RLIMIT_NPROC)"); |
107 | 26.9k | rl.rlim_cur = rl.rlim_max = RLIM_INFINITY; |
108 | 26.9k | if (setrlimit(RLIMIT_NPROC, &rl) != 0) { |
109 | 0 | rl.rlim_cur = rl.rlim_max = nproclimit.rlim_max; |
110 | 0 | if (setrlimit(RLIMIT_NPROC, &rl) != 0) |
111 | 0 | sudo_warn("setrlimit(RLIMIT_NPROC)"); |
112 | 0 | } |
113 | 26.9k | debug_return; |
114 | 26.9k | #endif /* __linux__ */ |
115 | 26.9k | } |
116 | | |
117 | | /* |
118 | | * Restore saved value of RLIMIT_NPROC. |
119 | | */ |
120 | | static void |
121 | | restore_nproc(void) |
122 | 26.9k | { |
123 | 26.9k | #ifdef __linux__ |
124 | 26.9k | debug_decl(restore_nproc, SUDOERS_DEBUG_UTIL); |
125 | | |
126 | 26.9k | if (setrlimit(RLIMIT_NPROC, &nproclimit) != 0) |
127 | 0 | sudo_warn("setrlimit(RLIMIT_NPROC)"); |
128 | | |
129 | 26.9k | debug_return; |
130 | 26.9k | #endif /* __linux__ */ |
131 | 26.9k | } |
132 | | |
133 | | /* |
134 | | * Re-initialize Defaults settings. |
135 | | * We do not warn, log or send mail for errors when reinitializing, |
136 | | * this would have already been done the first time through. |
137 | | */ |
138 | | static bool |
139 | | sudoers_reinit_defaults(struct sudoers_context *ctx) |
140 | 5.94k | { |
141 | 5.94k | struct sudo_nss *nss, *nss_next; |
142 | 5.94k | sudoers_logger_t logger = sudoers_error_hook; |
143 | 5.94k | debug_decl(sudoers_reinit_defaults, SUDOERS_DEBUG_PLUGIN); |
144 | | |
145 | 5.94k | if (!init_defaults()) { |
146 | 0 | sudo_warnx("%s", U_("unable to initialize sudoers default values")); |
147 | 0 | debug_return_bool(false); |
148 | 0 | } |
149 | | |
150 | | /* It should not be possible for the initial defaults to fail to apply. */ |
151 | 5.94k | if (!update_defaults(ctx, NULL, &initial_defaults, |
152 | 5.94k | SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false)) |
153 | 0 | debug_return_bool(false); |
154 | | |
155 | | /* Disable error logging while re-processing defaults. */ |
156 | 5.94k | sudoers_error_hook = NULL; |
157 | | |
158 | 5.94k | TAILQ_FOREACH_SAFE(nss, snl, entries, nss_next) { |
159 | | /* Missing/invalid defaults is not a fatal error. */ |
160 | 5.94k | if (nss->getdefs(ctx, nss) != -1) { |
161 | 5.94k | (void)update_defaults(ctx, nss->parse_tree, NULL, |
162 | 5.94k | SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, true); |
163 | 5.94k | } |
164 | 5.94k | } |
165 | | |
166 | | /* Restore error logging. */ |
167 | 5.94k | sudoers_error_hook = logger; |
168 | | |
169 | | /* No need to check the admin flag file multiple times. */ |
170 | 5.94k | if (ISSET(ctx->mode, MODE_POLICY_INTERCEPTED)) { |
171 | 0 | free(def_admin_flag); |
172 | 0 | def_admin_flag = NULL; |
173 | 0 | } |
174 | | |
175 | 5.94k | debug_return_bool(true); |
176 | 5.94k | } |
177 | | |
178 | | int |
179 | | sudoers_init(void *info, sudoers_logger_t logger, char * const envp[]) |
180 | 25.7k | { |
181 | 25.7k | struct sudo_nss *nss, *nss_next; |
182 | 25.7k | int oldlocale, sources = 0; |
183 | 25.7k | static int ret = -1; |
184 | 25.7k | debug_decl(sudoers_init, SUDOERS_DEBUG_PLUGIN); |
185 | | |
186 | | /* Only initialize once. */ |
187 | 25.7k | if (snl != NULL) |
188 | 0 | debug_return_int(ret); |
189 | | |
190 | 25.7k | bindtextdomain("sudoers", LOCALEDIR); |
191 | | |
192 | | /* Hook up logging function for parse errors. */ |
193 | 25.7k | sudoers_error_hook = logger; |
194 | | |
195 | | /* Register fatal/fatalx callback. */ |
196 | 25.7k | sudo_fatal_callback_register(sudoers_cleanup); |
197 | | |
198 | | /* Initialize environment functions (including replacements). */ |
199 | 25.7k | if (!env_init(envp)) |
200 | 0 | debug_return_int(-1); |
201 | | |
202 | | /* Setup defaults data structures. */ |
203 | 25.7k | if (!init_defaults()) { |
204 | 0 | sudo_warnx("%s", U_("unable to initialize sudoers default values")); |
205 | 0 | debug_return_int(-1); |
206 | 0 | } |
207 | | |
208 | | /* Parse info from front-end. */ |
209 | 25.7k | sudoers_ctx.mode = sudoers_policy_deserialize_info(&sudoers_ctx, info, |
210 | 25.7k | &initial_defaults); |
211 | 25.7k | if (ISSET(sudoers_ctx.mode, MODE_ERROR)) |
212 | 5.24k | debug_return_int(-1); |
213 | | |
214 | 20.4k | if (!init_vars(&sudoers_ctx, envp)) |
215 | 381 | debug_return_int(-1); |
216 | | |
217 | | /* Parse nsswitch.conf for sudoers order. */ |
218 | 20.1k | snl = sudo_read_nss(); |
219 | | |
220 | | /* LDAP or NSS may modify the euid so we need to be root for the open. */ |
221 | 20.1k | if (!set_perms(NULL, PERM_ROOT)) |
222 | 0 | debug_return_int(-1); |
223 | | |
224 | | /* Use the C locale unless another is specified in sudoers. */ |
225 | 20.1k | sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale); |
226 | 20.1k | sudo_warn_set_locale_func(sudoers_warn_setlocale); |
227 | | |
228 | | /* Update defaults set by front-end. */ |
229 | 20.1k | if (!update_defaults(&sudoers_ctx, NULL, &initial_defaults, |
230 | 20.1k | SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false)) { |
231 | 0 | goto cleanup; |
232 | 0 | } |
233 | | |
234 | | /* Open and parse sudoers, set global defaults. */ |
235 | 20.1k | TAILQ_FOREACH_SAFE(nss, snl, entries, nss_next) { |
236 | 20.1k | if (nss->open(&sudoers_ctx, nss) == -1 || (nss->parse_tree = nss->parse(&sudoers_ctx, nss)) == NULL) { |
237 | 0 | TAILQ_REMOVE(snl, nss, entries); |
238 | 0 | continue; |
239 | 0 | } |
240 | 20.1k | sources++; |
241 | | |
242 | | /* Missing/invalid defaults is not a fatal error. */ |
243 | 20.1k | if (nss->getdefs(&sudoers_ctx, nss) == -1) { |
244 | 0 | log_warningx(&sudoers_ctx, SLOG_PARSE_ERROR|SLOG_NO_STDERR, |
245 | 0 | N_("unable to get defaults from %s"), nss->source); |
246 | 20.1k | } else { |
247 | 20.1k | (void)update_defaults(&sudoers_ctx, nss->parse_tree, NULL, |
248 | 20.1k | SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false); |
249 | 20.1k | } |
250 | 20.1k | } |
251 | 20.1k | if (sources == 0) { |
252 | | /* Display an extra warning if there are multiple sudoers sources. */ |
253 | 0 | if (TAILQ_FIRST(snl) != TAILQ_LAST(snl, sudo_nss_list)) |
254 | 0 | sudo_warnx("%s", U_("no valid sudoers sources found, quitting")); |
255 | 0 | goto cleanup; |
256 | 0 | } |
257 | | |
258 | | /* Set login class if applicable (after sudoers is parsed). */ |
259 | 20.1k | if (set_loginclass(&sudoers_ctx)) |
260 | 20.1k | ret = true; |
261 | | |
262 | 20.1k | cleanup: |
263 | 20.1k | mail_parse_errors(&sudoers_ctx); |
264 | | |
265 | 20.1k | if (!restore_perms()) |
266 | 0 | ret = -1; |
267 | | |
268 | | /* Restore user's locale. */ |
269 | 20.1k | sudo_warn_set_locale_func(NULL); |
270 | 20.1k | sudoers_setlocale(oldlocale, NULL); |
271 | | |
272 | 20.1k | debug_return_int(ret); |
273 | 20.1k | } |
274 | | |
275 | | /* |
276 | | * Expand I/O log dir and file into a full path. |
277 | | * Returns the full I/O log path prefixed with "iolog_path=". |
278 | | * Sets ctx->iolog_file and ctx->iolog_path as a side effect. |
279 | | */ |
280 | | static char * |
281 | | format_iolog_path(struct sudoers_context *ctx) |
282 | 2.79k | { |
283 | 2.79k | char dir[PATH_MAX], file[PATH_MAX]; |
284 | 2.79k | char *iolog_path = NULL; |
285 | 2.79k | int oldlocale; |
286 | 2.79k | bool ok; |
287 | 2.79k | debug_decl(format_iolog_path, SUDOERS_DEBUG_PLUGIN); |
288 | | |
289 | | /* Use sudoers locale for strftime() */ |
290 | 2.79k | sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale); |
291 | 2.79k | ok = expand_iolog_path(def_iolog_dir, dir, sizeof(dir), |
292 | 2.79k | &sudoers_iolog_path_escapes[1], ctx); |
293 | 2.79k | if (ok) { |
294 | 2.79k | ctx->iolog_dir = dir; |
295 | 2.79k | ok = expand_iolog_path(def_iolog_file, file, sizeof(file), |
296 | 2.79k | &sudoers_iolog_path_escapes[0], ctx); |
297 | 2.79k | ctx->iolog_dir = NULL; |
298 | 2.79k | } |
299 | 2.79k | sudoers_setlocale(oldlocale, NULL); |
300 | 2.79k | if (!ok) |
301 | 0 | goto done; |
302 | | |
303 | 2.79k | if (asprintf(&iolog_path, "iolog_path=%s/%s", dir, file) == -1) { |
304 | 0 | iolog_path = NULL; |
305 | 0 | goto done; |
306 | 0 | } |
307 | | |
308 | | /* Stash pointer to the I/O log for the event log. */ |
309 | 2.79k | ctx->iolog_path = iolog_path + sizeof("iolog_path=") - 1; |
310 | 2.79k | ctx->iolog_file = ctx->iolog_path + 1 + strlen(dir); |
311 | | |
312 | 2.79k | done: |
313 | 2.79k | debug_return_str(iolog_path); |
314 | 2.79k | } |
315 | | |
316 | | static void |
317 | | cb_lookup(const struct sudoers_parse_tree *parse_tree, |
318 | | const struct userspec *us, int user_match, const struct privilege *priv, |
319 | | int host_match, const struct cmndspec *cs, int date_match, int runas_match, |
320 | | int cmnd_match, void *closure) |
321 | 0 | { |
322 | 0 | struct sudoers_match_info *info = closure; |
323 | |
|
324 | 0 | if (cmnd_match != UNSPEC) { |
325 | 0 | info->us = us; |
326 | 0 | info->priv = priv; |
327 | 0 | info->cs = cs; |
328 | 0 | } |
329 | 0 | } |
330 | | |
331 | | /* |
332 | | * Find the command, perform a sudoers lookup, ask for a password as |
333 | | * needed, and perform post-lookup checks. Logs success/failure. |
334 | | * This is used by the check, list and validate plugin methods. |
335 | | * |
336 | | * Returns true if allowed, false if denied, -1 on error and |
337 | | * -2 for usage error. |
338 | | */ |
339 | | static int |
340 | | sudoers_check_common(struct sudoers_context *ctx, int pwflag) |
341 | 26.9k | { |
342 | 26.9k | struct sudoers_match_info match_info = { NULL }; |
343 | 26.9k | int oldlocale, ret = -1; |
344 | 26.9k | unsigned int validated; |
345 | 26.9k | time_t now; |
346 | 26.9k | debug_decl(sudoers_check_common, SUDOERS_DEBUG_PLUGIN); |
347 | | |
348 | | /* The user may only specify a host for "sudo -l". */ |
349 | 26.9k | if (!ISSET(ctx->mode, MODE_LIST|MODE_CHECK)) { |
350 | 17.1k | if (strcmp(ctx->runas.host, ctx->user.host) != 0) { |
351 | 0 | log_warningx(ctx, SLOG_NO_STDERR|SLOG_AUDIT, |
352 | 0 | N_("user not allowed to set remote host for command")); |
353 | 0 | sudo_warnx("%s", |
354 | 0 | U_("a remote host may only be specified when listing privileges.")); |
355 | 0 | ret = false; |
356 | 0 | goto done; |
357 | 0 | } |
358 | 17.1k | } |
359 | | |
360 | | /* If given the -P option, set the "preserve_groups" flag. */ |
361 | 26.9k | if (ISSET(ctx->mode, MODE_PRESERVE_GROUPS)) |
362 | 57 | def_preserve_groups = true; |
363 | | |
364 | | /* Find command in path and apply per-command Defaults. */ |
365 | 26.9k | cmnd_status = set_cmnd(ctx); |
366 | 26.9k | if (cmnd_status == NOT_FOUND_ERROR) |
367 | 0 | goto done; |
368 | | |
369 | | /* Is root even allowed to run sudo? */ |
370 | 26.9k | if (ctx->user.uid == 0 && !def_root_sudo) { |
371 | | /* Not an audit event (should it be?). */ |
372 | 0 | sudo_warnx("%s", |
373 | 0 | U_("sudoers specifies that root is not allowed to sudo")); |
374 | 0 | ret = false; |
375 | 0 | goto done; |
376 | 0 | } |
377 | | |
378 | | /* Check for -C overriding def_closefrom. */ |
379 | 26.9k | if (ctx->user.closefrom >= 0 && ctx->user.closefrom != def_closefrom) { |
380 | 1.11k | if (!def_closefrom_override) { |
381 | 1.11k | log_warningx(ctx, SLOG_NO_STDERR|SLOG_AUDIT, |
382 | 1.11k | N_("user not allowed to override closefrom limit")); |
383 | 1.11k | sudo_warnx("%s", U_("you are not permitted to use the -C option")); |
384 | 1.11k | goto bad; |
385 | 1.11k | } |
386 | 0 | def_closefrom = ctx->user.closefrom; |
387 | 0 | } |
388 | | |
389 | | /* |
390 | | * Check sudoers sources, using the locale specified in sudoers. |
391 | | */ |
392 | 25.7k | time(&now); |
393 | 25.7k | sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale); |
394 | 25.7k | validated = sudoers_lookup(snl, ctx, now, cb_lookup, &match_info, |
395 | 25.7k | &cmnd_status, pwflag); |
396 | 25.7k | sudoers_setlocale(oldlocale, NULL); |
397 | 25.7k | if (ISSET(validated, VALIDATE_ERROR)) { |
398 | | /* The lookup function should have printed an error. */ |
399 | 0 | goto done; |
400 | 0 | } |
401 | | |
402 | 25.7k | if (match_info.us != NULL && match_info.us->file != NULL) { |
403 | 0 | free(ctx->source); |
404 | 0 | if (match_info.us->line != 0) { |
405 | 0 | if (asprintf(&ctx->source, "%s:%d:%d", match_info.us->file, |
406 | 0 | match_info.us->line, match_info.us->column) == -1) |
407 | 0 | ctx->source = NULL; |
408 | 0 | } else { |
409 | 0 | ctx->source = strdup(match_info.us->file); |
410 | 0 | } |
411 | 0 | if (ctx->source == NULL) { |
412 | 0 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); |
413 | 0 | goto done; |
414 | 0 | } |
415 | 0 | } |
416 | | |
417 | 25.7k | if (ctx->runas.cmnd == NULL) { |
418 | 25.7k | if ((ctx->runas.cmnd = strdup(ctx->user.cmnd)) == NULL) { |
419 | 0 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); |
420 | 0 | goto done; |
421 | 0 | } |
422 | 25.7k | } |
423 | | |
424 | | /* Defer uid/gid checks until after defaults have been updated. */ |
425 | 25.7k | if (unknown_runas_uid && !def_runas_allow_unknown_id) { |
426 | 52 | log_warningx(ctx, SLOG_AUDIT, N_("unknown user %s"), |
427 | 52 | ctx->runas.pw->pw_name); |
428 | 52 | goto done; |
429 | 52 | } |
430 | 25.7k | if (ctx->runas.gr != NULL) { |
431 | 284 | if (unknown_runas_gid && !def_runas_allow_unknown_id) { |
432 | 67 | log_warningx(ctx, SLOG_AUDIT, N_("unknown group %s"), |
433 | 67 | ctx->runas.gr->gr_name); |
434 | 67 | goto done; |
435 | 67 | } |
436 | 284 | } |
437 | | |
438 | | /* If no command line args and "shell_noargs" is not set, error out. */ |
439 | 25.6k | if (ISSET(ctx->mode, MODE_IMPLIED_SHELL) && !def_shell_noargs) { |
440 | | /* Not an audit event. */ |
441 | 56 | ret = -2; /* usage error */ |
442 | 56 | goto done; |
443 | 56 | } |
444 | | |
445 | | /* Bail if a tty is required and we don't have one. */ |
446 | 25.6k | if (def_requiretty && !sudoers_tty_present(ctx)) { |
447 | 0 | log_warningx(ctx, SLOG_NO_STDERR|SLOG_AUDIT, N_("no tty")); |
448 | 0 | sudo_warnx("%s", U_("sorry, you must have a tty to run sudo")); |
449 | 0 | goto bad; |
450 | 0 | } |
451 | | |
452 | | /* Check runas user's shell if running (or checking) a command. */ |
453 | 25.6k | if (ISSET(ctx->mode, MODE_RUN|MODE_CHECK)) { |
454 | 15.1k | if (!user_shell_valid(ctx->runas.pw)) { |
455 | 0 | log_warningx(ctx, SLOG_RAW_MSG|SLOG_AUDIT, |
456 | 0 | N_("invalid shell for user %s: %s"), |
457 | 0 | ctx->runas.pw->pw_name, ctx->runas.pw->pw_shell); |
458 | 0 | goto bad; |
459 | 0 | } |
460 | 15.1k | } |
461 | | |
462 | | /* |
463 | | * We don't reset the environment for sudoedit or if the user |
464 | | * specified the -E command line flag and they have setenv privs. |
465 | | */ |
466 | 25.6k | if (ISSET(ctx->mode, MODE_EDIT) || |
467 | 24.3k | (ISSET(ctx->mode, MODE_PRESERVE_ENV) && def_setenv)) |
468 | 1.27k | def_env_reset = false; |
469 | | |
470 | | /* Build a new environment that avoids any nasty bits. */ |
471 | 25.6k | if (!rebuild_env(ctx)) |
472 | 0 | goto bad; |
473 | | |
474 | | /* Require a password if sudoers says so. */ |
475 | 25.6k | switch (check_user(ctx, validated, ctx->mode)) { |
476 | 25.6k | case AUTH_SUCCESS: |
477 | | /* user authenticated successfully. */ |
478 | 25.6k | break; |
479 | 0 | case AUTH_FAILURE: |
480 | | /* Note: log_denial() calls audit for us. */ |
481 | 0 | if (!ISSET(validated, VALIDATE_SUCCESS)) { |
482 | | /* Only display a denial message if no password was read. */ |
483 | 0 | if (!log_denial(ctx, validated, def_passwd_tries <= 0)) |
484 | 0 | goto done; |
485 | 0 | } |
486 | 0 | goto bad; |
487 | 0 | default: |
488 | | /* some other error, ret is -1. */ |
489 | 0 | goto done; |
490 | 25.6k | } |
491 | | |
492 | | /* Check whether ctx->runas.chroot is permitted (if specified). */ |
493 | 25.6k | switch (check_user_runchroot(ctx->runas.chroot)) { |
494 | 25.6k | case true: |
495 | 25.6k | break; |
496 | 0 | case false: |
497 | 0 | log_warningx(ctx, SLOG_NO_STDERR|SLOG_AUDIT, |
498 | 0 | N_("user not allowed to change root directory to %s"), |
499 | 0 | ctx->runas.chroot); |
500 | 0 | sudo_warnx(U_("you are not permitted to use the -R option with %s"), |
501 | 0 | ctx->user.cmnd); |
502 | 0 | goto bad; |
503 | 0 | default: |
504 | 0 | goto done; |
505 | 25.6k | } |
506 | | |
507 | | /* Check whether ctx->runas.cwd is permitted (if specified). */ |
508 | 25.6k | switch (check_user_runcwd(ctx->runas.cwd)) { |
509 | 25.6k | case true: |
510 | 25.6k | break; |
511 | 0 | case false: |
512 | 0 | log_warningx(ctx, SLOG_NO_STDERR|SLOG_AUDIT, |
513 | 0 | N_("user not allowed to change directory to %s"), ctx->runas.cwd); |
514 | 0 | sudo_warnx(U_("you are not permitted to use the -D option with %s"), |
515 | 0 | ctx->user.cmnd); |
516 | 0 | goto bad; |
517 | 0 | default: |
518 | 0 | goto done; |
519 | 25.6k | } |
520 | | |
521 | | /* If run as root with SUDO_USER set, set ctx->user.pw to that user. */ |
522 | | /* XXX - causes confusion when root is not listed in sudoers */ |
523 | 25.6k | if (ISSET(ctx->mode, MODE_RUN|MODE_EDIT) && prev_user != NULL) { |
524 | 0 | if (ctx->user.uid == 0 && strcmp(prev_user, "root") != 0) { |
525 | 0 | struct passwd *pw; |
526 | |
|
527 | 0 | if ((pw = sudo_getpwnam(prev_user)) != NULL) { |
528 | 0 | if (ctx->user.pw != NULL) |
529 | 0 | sudo_pw_delref(ctx->user.pw); |
530 | 0 | ctx->user.pw = pw; |
531 | 0 | } |
532 | 0 | } |
533 | 0 | } |
534 | | |
535 | | /* If the user was not allowed to run the command we are done. */ |
536 | 25.6k | if (!ISSET(validated, VALIDATE_SUCCESS)) { |
537 | | /* Note: log_failure() calls audit for us. */ |
538 | 0 | if (!log_failure(ctx, validated, cmnd_status)) |
539 | 0 | goto done; |
540 | 0 | goto bad; |
541 | 0 | } |
542 | | |
543 | | /* |
544 | | * Check if the user is trying to run a setid binary in intercept mode. |
545 | | * For the DSO intercept_type, we reject attempts to run setid binaries |
546 | | * by default since the dynamic loader will clear LD_PRELOAD, defeating |
547 | | * intercept. |
548 | | */ |
549 | 25.6k | if (def_intercept || ISSET(ctx->mode, MODE_POLICY_INTERCEPTED)) { |
550 | 0 | if (!def_intercept_allow_setid && ctx->user.cmnd_stat != NULL) { |
551 | 0 | if (ISSET(ctx->user.cmnd_stat->st_mode, S_ISUID|S_ISGID)) { |
552 | 0 | CLR(validated, VALIDATE_SUCCESS); |
553 | 0 | if (!log_denial(ctx, validated|FLAG_INTERCEPT_SETID, true)) |
554 | 0 | goto done; |
555 | 0 | goto bad; |
556 | 0 | } |
557 | 0 | } |
558 | 0 | } |
559 | | |
560 | | /* Create Ubuntu-style dot file to indicate sudo was successful. */ |
561 | 25.6k | if (create_admin_success_flag(ctx) == -1) |
562 | 0 | goto done; |
563 | | |
564 | | /* Finally tell the user if the command did not exist. */ |
565 | 25.6k | if (cmnd_status == NOT_FOUND_DOT) { |
566 | 3.49k | audit_failure(ctx, ctx->runas.argv, N_("command in current directory")); |
567 | 3.49k | sudo_warnx(U_("ignoring \"%s\" found in '.'\nUse \"sudo ./%s\" if this is the \"%s\" you wish to run."), ctx->user.cmnd, ctx->user.cmnd, ctx->user.cmnd); |
568 | 3.49k | goto bad; |
569 | 22.1k | } else if (cmnd_status == NOT_FOUND) { |
570 | 1.77k | if (ISSET(ctx->mode, MODE_CHECK)) { |
571 | 0 | audit_failure(ctx, ctx->runas.argv, N_("%s: command not found"), |
572 | 0 | ctx->runas.argv[1]); |
573 | 0 | sudo_warnx(U_("%s: command not found"), ctx->runas.argv[1]); |
574 | 1.77k | } else { |
575 | 1.77k | audit_failure(ctx, ctx->runas.argv, N_("%s: command not found"), |
576 | 1.77k | ctx->user.cmnd); |
577 | 1.77k | sudo_warnx(U_("%s: command not found"), ctx->user.cmnd); |
578 | 1.77k | if (strncmp(ctx->user.cmnd, "cd", 2) == 0 && (ctx->user.cmnd[2] == '\0' || |
579 | 0 | isblank((unsigned char)ctx->user.cmnd[2]))) { |
580 | 0 | sudo_warnx("%s", |
581 | 0 | U_("\"cd\" is a shell built-in command, it cannot be run directly.")); |
582 | 0 | sudo_warnx("%s", |
583 | 0 | U_("the -s option may be used to run a privileged shell.")); |
584 | 0 | sudo_warnx("%s", |
585 | 0 | U_("the -D option may be used to run a command in a specific directory.")); |
586 | 0 | } |
587 | 1.77k | } |
588 | 1.77k | goto bad; |
589 | 1.77k | } |
590 | | |
591 | | /* If user specified a timeout make sure sudoers allows it. */ |
592 | 20.3k | if (!def_user_command_timeouts && ctx->user.timeout > 0) { |
593 | 631 | log_warningx(ctx, SLOG_NO_STDERR|SLOG_AUDIT, |
594 | 631 | N_("user not allowed to set a command timeout")); |
595 | 631 | sudo_warnx("%s", |
596 | 631 | U_("sorry, you are not allowed set a command timeout")); |
597 | 631 | goto bad; |
598 | 631 | } |
599 | | |
600 | | /* If user specified env vars make sure sudoers allows it. */ |
601 | 19.7k | if (ISSET(ctx->mode, MODE_RUN) && !def_setenv) { |
602 | 6.66k | if (ISSET(ctx->mode, MODE_PRESERVE_ENV)) { |
603 | 20 | log_warningx(ctx, SLOG_NO_STDERR|SLOG_AUDIT, |
604 | 20 | N_("user not allowed to preserve the environment")); |
605 | 20 | sudo_warnx("%s", |
606 | 20 | U_("sorry, you are not allowed to preserve the environment")); |
607 | 20 | goto bad; |
608 | 6.64k | } else { |
609 | 6.64k | if (!validate_env_vars(ctx, ctx->user.env_add)) |
610 | 1.77k | goto bad; |
611 | 6.64k | } |
612 | 6.66k | } |
613 | | |
614 | 17.9k | ret = true; |
615 | 17.9k | goto done; |
616 | | |
617 | 8.81k | bad: |
618 | 8.81k | ret = false; |
619 | 26.9k | done: |
620 | 26.9k | debug_return_int(ret); |
621 | 26.9k | } |
622 | | |
623 | | static bool need_reinit; |
624 | | |
625 | | /* |
626 | | * Check whether the user is allowed to run the specified command. |
627 | | * Returns true if allowed, false if denied, -1 on error and |
628 | | * -2 for usage error. |
629 | | */ |
630 | | int |
631 | | sudoers_check_cmnd(int argc, char * const argv[], char *env_add[], |
632 | | void *closure) |
633 | 13.9k | { |
634 | 13.9k | char *iolog_path = NULL; |
635 | 13.9k | mode_t cmnd_umask = ACCESSPERMS; |
636 | 13.9k | int ret = -1; |
637 | 13.9k | debug_decl(sudoers_check_cmnd, SUDOERS_DEBUG_PLUGIN); |
638 | | |
639 | 13.9k | sudo_warn_set_locale_func(sudoers_warn_setlocale); |
640 | | |
641 | 13.9k | if (argc == 0) { |
642 | 0 | sudo_warnx("%s", U_("no command specified")); |
643 | 0 | debug_return_int(-1); |
644 | 0 | } |
645 | | |
646 | 13.9k | if (need_reinit) { |
647 | | /* Was previous command intercepted? */ |
648 | 5.94k | if (ISSET(sudoers_ctx.mode, MODE_RUN) && def_intercept) |
649 | 0 | SET(sudoers_ctx.mode, MODE_POLICY_INTERCEPTED); |
650 | | |
651 | | /* Only certain mode flags are legal for intercepted commands. */ |
652 | 5.94k | if (ISSET(sudoers_ctx.mode, MODE_POLICY_INTERCEPTED)) |
653 | 0 | sudoers_ctx.mode &= MODE_INTERCEPT_MASK; |
654 | | |
655 | | /* Re-initialize defaults if we are called multiple times. */ |
656 | 5.94k | if (!sudoers_reinit_defaults(&sudoers_ctx)) |
657 | 0 | debug_return_int(-1); |
658 | 5.94k | } |
659 | 13.9k | need_reinit = true; |
660 | | |
661 | 13.9k | unlimit_nproc(); |
662 | | |
663 | 13.9k | if (!set_perms(&sudoers_ctx, PERM_INITIAL)) |
664 | 0 | goto bad; |
665 | | |
666 | | /* Environment variables specified on the command line. */ |
667 | 13.9k | if (env_add != NULL && env_add[0] != NULL) |
668 | 5.85k | sudoers_ctx.user.env_add = env_add; |
669 | | |
670 | | /* |
671 | | * Make a local copy of argc/argv, with special handling for the |
672 | | * '-i' option. We also allocate an extra slot for bash's --login. |
673 | | */ |
674 | 13.9k | if (sudoers_ctx.runas.argv != NULL && sudoers_ctx.runas.argv != sudoers_ctx.runas.argv_saved) { |
675 | 3.23k | sudoers_gc_remove(GC_PTR, sudoers_ctx.runas.argv); |
676 | 3.23k | free(sudoers_ctx.runas.argv); |
677 | 3.23k | } |
678 | 13.9k | sudoers_ctx.runas.argv = reallocarray(NULL, (size_t)argc + 2, sizeof(char *)); |
679 | 13.9k | if (sudoers_ctx.runas.argv == NULL) { |
680 | 0 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); |
681 | 0 | goto error; |
682 | 0 | } |
683 | 13.9k | sudoers_gc_add(GC_PTR, sudoers_ctx.runas.argv); |
684 | 13.9k | memcpy(sudoers_ctx.runas.argv, argv, (size_t)argc * sizeof(char *)); |
685 | 13.9k | sudoers_ctx.runas.argc = argc; |
686 | 13.9k | sudoers_ctx.runas.argv[sudoers_ctx.runas.argc] = NULL; |
687 | 13.9k | if (ISSET(sudoers_ctx.mode, MODE_LOGIN_SHELL) && sudoers_ctx.runas.pw != NULL) { |
688 | 336 | sudoers_ctx.runas.argv[0] = strdup(sudoers_ctx.runas.pw->pw_shell); |
689 | 336 | if (sudoers_ctx.runas.argv[0] == NULL) { |
690 | 0 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); |
691 | 0 | goto error; |
692 | 0 | } |
693 | 336 | sudoers_gc_add(GC_PTR, sudoers_ctx.runas.argv[0]); |
694 | 336 | } |
695 | | |
696 | 13.9k | ret = sudoers_check_common(&sudoers_ctx, 0); |
697 | 13.9k | if (ret != true) |
698 | 7.93k | goto done; |
699 | | |
700 | 6.02k | if (!remote_iologs) { |
701 | 2.79k | if (iolog_enabled && def_iolog_file && def_iolog_dir) { |
702 | 2.79k | if ((iolog_path = format_iolog_path(&sudoers_ctx)) == NULL) { |
703 | 0 | if (!def_ignore_iolog_errors) |
704 | 0 | goto error; |
705 | | /* Unable to expand I/O log path, disable I/O logging. */ |
706 | 0 | def_log_input = false; |
707 | 0 | def_log_output = false; |
708 | 0 | def_log_stdin = false; |
709 | 0 | def_log_stdout = false; |
710 | 0 | def_log_stderr = false; |
711 | 0 | def_log_ttyin = false; |
712 | 0 | def_log_ttyout = false; |
713 | 0 | } |
714 | 2.79k | } |
715 | 2.79k | } |
716 | | |
717 | | /* |
718 | | * Set umask based on sudoers. |
719 | | * If user's umask is more restrictive, OR in those bits too |
720 | | * unless umask_override is set. |
721 | | */ |
722 | 6.02k | if (def_umask != ACCESSPERMS) { |
723 | 6.02k | cmnd_umask = def_umask; |
724 | 6.02k | if (!def_umask_override) |
725 | 6.02k | cmnd_umask |= sudoers_ctx.user.umask; |
726 | 6.02k | } |
727 | | |
728 | 6.02k | if (ISSET(sudoers_ctx.mode, MODE_LOGIN_SHELL)) { |
729 | 168 | char *p; |
730 | | |
731 | | /* Convert /bin/sh -> -sh so shell knows it is a login shell */ |
732 | 168 | if ((p = strrchr(sudoers_ctx.runas.argv[0], '/')) == NULL) |
733 | 0 | p = sudoers_ctx.runas.argv[0]; |
734 | 168 | *p = '-'; |
735 | 168 | sudoers_ctx.runas.argv[0] = p; |
736 | | |
737 | | /* |
738 | | * Newer versions of bash require the --login option to be used |
739 | | * in conjunction with the -c option even if the shell name starts |
740 | | * with a '-'. Unfortunately, bash 1.x uses -login, not --login |
741 | | * so this will cause an error for that. |
742 | | */ |
743 | 168 | if (sudoers_ctx.runas.argc > 1 && strcmp(sudoers_ctx.runas.argv[0], "-bash") == 0 && |
744 | 100 | strcmp(sudoers_ctx.runas.argv[1], "-c") == 0) { |
745 | | /* We allocated extra space for the --login above. */ |
746 | 0 | memmove(&sudoers_ctx.runas.argv[2], &sudoers_ctx.runas.argv[1], |
747 | 0 | (size_t)sudoers_ctx.runas.argc * sizeof(char *)); |
748 | 0 | sudoers_ctx.runas.argv[1] = (char *)"--login"; |
749 | 0 | sudoers_ctx.runas.argc++; |
750 | 0 | } |
751 | | |
752 | 168 | #ifdef _PATH_ENVIRONMENT |
753 | | /* Insert system-wide environment variables. */ |
754 | 168 | if (!read_env_file(&sudoers_ctx, _PATH_ENVIRONMENT, true, false)) |
755 | 0 | sudo_warn("%s", _PATH_ENVIRONMENT); |
756 | 168 | #endif |
757 | | #ifdef HAVE_LOGIN_CAP_H |
758 | | /* Set environment based on login class. */ |
759 | | if (sudoers_ctx.runas.class) { |
760 | | login_cap_t *lc = login_getclass(sudoers_ctx.runas.class); |
761 | | if (lc != NULL) { |
762 | | setusercontext(lc, sudoers_ctx.runas.pw, |
763 | | sudoers_ctx.runas.pw->pw_uid, LOGIN_SETPATH|LOGIN_SETENV); |
764 | | login_close(lc); |
765 | | } |
766 | | } |
767 | | #endif /* HAVE_LOGIN_CAP_H */ |
768 | 168 | } |
769 | | |
770 | | /* Insert system-wide environment variables. */ |
771 | 6.02k | if (def_restricted_env_file) { |
772 | 6.02k | if (!read_env_file(&sudoers_ctx, def_restricted_env_file, false, true)) |
773 | 0 | sudo_warn("%s", def_restricted_env_file); |
774 | 6.02k | } |
775 | 6.02k | if (def_env_file) { |
776 | 6.02k | if (!read_env_file(&sudoers_ctx, def_env_file, false, false)) |
777 | 0 | sudo_warn("%s", def_env_file); |
778 | 6.02k | } |
779 | | |
780 | | /* Insert user-specified environment variables. */ |
781 | 6.02k | if (!insert_env_vars(sudoers_ctx.user.env_add)) { |
782 | 144 | sudo_warnx("%s", |
783 | 144 | U_("error setting user-specified environment variables")); |
784 | 144 | goto error; |
785 | 144 | } |
786 | | |
787 | | /* Note: must call audit before uid change. */ |
788 | 5.87k | if (ISSET(sudoers_ctx.mode, MODE_EDIT)) { |
789 | 1.11k | const char *env_editor = NULL; |
790 | 1.11k | char **edit_argv; |
791 | 1.11k | int edit_argc; |
792 | | |
793 | 1.11k | sudoers_ctx.sudoedit_nfiles = sudoers_ctx.runas.argc - 1; |
794 | 1.11k | free(sudoers_ctx.runas.cmnd); |
795 | 1.11k | sudoers_ctx.runas.cmnd = find_editor(sudoers_ctx.sudoedit_nfiles, |
796 | 1.11k | sudoers_ctx.runas.argv + 1, &edit_argc, &edit_argv, NULL, &env_editor); |
797 | 1.11k | if (sudoers_ctx.runas.cmnd == NULL) { |
798 | 424 | switch (errno) { |
799 | 424 | case ENOENT: |
800 | 424 | audit_failure(&sudoers_ctx, sudoers_ctx.runas.argv, |
801 | 424 | N_("%s: command not found"), |
802 | 424 | env_editor ? env_editor : def_editor); |
803 | 424 | sudo_warnx(U_("%s: command not found"), |
804 | 424 | env_editor ? env_editor : def_editor); |
805 | 424 | goto error; |
806 | 0 | case EINVAL: |
807 | 0 | if (def_env_editor && env_editor != NULL) { |
808 | | /* User tried to do something funny with the editor. */ |
809 | 0 | log_warningx(&sudoers_ctx, |
810 | 0 | SLOG_NO_STDERR|SLOG_AUDIT|SLOG_SEND_MAIL, |
811 | 0 | "invalid user-specified editor: %s", env_editor); |
812 | 0 | goto error; |
813 | 0 | } |
814 | 0 | FALLTHROUGH; |
815 | 0 | default: |
816 | 0 | goto error; |
817 | 424 | } |
818 | 424 | } |
819 | | /* find_editor() already g/c'd edit_argv[] */ |
820 | 686 | if (sudoers_ctx.runas.argv != sudoers_ctx.runas.argv_saved) { |
821 | 686 | sudoers_gc_remove(GC_PTR, sudoers_ctx.runas.argv); |
822 | 686 | free(sudoers_ctx.runas.argv); |
823 | 686 | } |
824 | 686 | sudoers_ctx.runas.argv = edit_argv; |
825 | 686 | sudoers_ctx.runas.argc = edit_argc; |
826 | | |
827 | | /* We want to run the editor with the unmodified environment. */ |
828 | 686 | env_swap_old(); |
829 | 686 | } |
830 | | |
831 | | /* Save the initial command and argv so we have it for exit logging. */ |
832 | 5.45k | if (sudoers_ctx.runas.cmnd_saved == NULL) { |
833 | 2.74k | sudoers_ctx.runas.cmnd_saved = strdup(sudoers_ctx.runas.cmnd); |
834 | 2.74k | if (sudoers_ctx.runas.cmnd_saved == NULL) { |
835 | 0 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); |
836 | 0 | goto error; |
837 | 0 | } |
838 | 2.74k | sudoers_ctx.runas.argv_saved = sudoers_ctx.runas.argv; |
839 | 2.74k | } |
840 | | |
841 | 5.45k | ret = true; |
842 | 5.45k | goto done; |
843 | | |
844 | 0 | bad: |
845 | 0 | ret = false; |
846 | 0 | goto done; |
847 | | |
848 | 568 | error: |
849 | 568 | ret = -1; |
850 | | |
851 | 13.9k | done: |
852 | 13.9k | mail_parse_errors(&sudoers_ctx); |
853 | | |
854 | 13.9k | if (def_group_plugin) |
855 | 0 | group_plugin_unload(); |
856 | 13.9k | reset_parser(); |
857 | | |
858 | 13.9k | if (ret == -1) { |
859 | | /* Free locally-allocated strings. */ |
860 | 631 | free(iolog_path); |
861 | 13.3k | } else { |
862 | | /* Store settings to pass back to front-end. */ |
863 | 13.3k | if (!sudoers_policy_store_result(&sudoers_ctx, ret, |
864 | 13.3k | sudoers_ctx.runas.argv, env_get(), cmnd_umask, iolog_path, closure)) |
865 | 0 | ret = -1; |
866 | 13.3k | } |
867 | | |
868 | | /* Zero out stashed copy of environment, it is owned by the front-end. */ |
869 | 13.9k | (void)env_init(NULL); |
870 | | |
871 | 13.9k | if (!rewind_perms()) |
872 | 0 | ret = -1; |
873 | | |
874 | 13.9k | restore_nproc(); |
875 | | |
876 | 13.9k | sudo_warn_set_locale_func(NULL); |
877 | | |
878 | 13.9k | debug_return_int(ret); |
879 | 13.9k | } |
880 | | |
881 | | /* |
882 | | * Validate the user and update their timestamp file entry. |
883 | | * Returns true if allowed, false if denied, -1 on error and |
884 | | * -2 for usage error. |
885 | | */ |
886 | | int |
887 | | sudoers_validate_user(void) |
888 | 3.23k | { |
889 | 3.23k | int ret = -1; |
890 | 3.23k | debug_decl(sudoers_validate_user, SUDOERS_DEBUG_PLUGIN); |
891 | | |
892 | 3.23k | sudo_warn_set_locale_func(sudoers_warn_setlocale); |
893 | | |
894 | 3.23k | unlimit_nproc(); |
895 | | |
896 | 3.23k | if (!set_perms(&sudoers_ctx, PERM_INITIAL)) |
897 | 0 | goto done; |
898 | | |
899 | 3.23k | sudoers_ctx.runas.argv = reallocarray(NULL, 2, sizeof(char *)); |
900 | 3.23k | if (sudoers_ctx.runas.argv == NULL) { |
901 | 0 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); |
902 | 0 | goto done; |
903 | 0 | } |
904 | 3.23k | sudoers_gc_add(GC_PTR, sudoers_ctx.runas.argv); |
905 | 3.23k | sudoers_ctx.runas.argv[0] = (char *)"validate"; |
906 | 3.23k | sudoers_ctx.runas.argv[1] = NULL; |
907 | 3.23k | sudoers_ctx.runas.argc = 2; |
908 | | |
909 | 3.23k | ret = sudoers_check_common(&sudoers_ctx, I_VERIFYPW); |
910 | | |
911 | 3.23k | done: |
912 | 3.23k | mail_parse_errors(&sudoers_ctx); |
913 | | |
914 | 3.23k | if (def_group_plugin) |
915 | 0 | group_plugin_unload(); |
916 | 3.23k | reset_parser(); |
917 | 3.23k | env_init(NULL); |
918 | | |
919 | 3.23k | if (!rewind_perms()) |
920 | 0 | ret = -1; |
921 | | |
922 | 3.23k | restore_nproc(); |
923 | | |
924 | 3.23k | sudo_warn_set_locale_func(NULL); |
925 | | |
926 | 3.23k | debug_return_int(ret); |
927 | 3.23k | } |
928 | | |
929 | | /* |
930 | | * List a user's privileges or check whether a specific command may be run. |
931 | | * Returns true if allowed, false if denied, -1 on error and |
932 | | * -2 for usage error. |
933 | | */ |
934 | | int |
935 | | sudoers_list(int argc, char * const argv[], const char *list_user, int verbose) |
936 | 9.71k | { |
937 | 9.71k | struct passwd *pw; |
938 | 9.71k | int ret = -1; |
939 | 9.71k | debug_decl(sudoers_list, SUDOERS_DEBUG_PLUGIN); |
940 | | |
941 | 9.71k | sudo_warn_set_locale_func(sudoers_warn_setlocale); |
942 | | |
943 | 9.71k | unlimit_nproc(); |
944 | | |
945 | 9.71k | if (!set_perms(&sudoers_ctx, PERM_INITIAL)) |
946 | 0 | goto done; |
947 | | |
948 | 9.71k | if (list_user) { |
949 | 3.23k | if (sudoers_ctx.runas.list_pw != NULL) |
950 | 1.61k | sudo_pw_delref(sudoers_ctx.runas.list_pw); |
951 | 3.23k | sudoers_ctx.runas.list_pw = sudo_getpwnam(list_user); |
952 | 3.23k | if (sudoers_ctx.runas.list_pw == NULL) { |
953 | 0 | sudo_warnx(U_("unknown user %s"), list_user); |
954 | 0 | goto done; |
955 | 0 | } |
956 | 3.23k | } |
957 | | |
958 | 9.71k | sudoers_ctx.runas.argv = reallocarray(NULL, (size_t)argc + 2, sizeof(char *)); |
959 | 9.71k | if (sudoers_ctx.runas.argv == NULL) { |
960 | 0 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); |
961 | 0 | goto done; |
962 | 0 | } |
963 | 9.71k | sudoers_gc_add(GC_PTR, sudoers_ctx.runas.argv); |
964 | 9.71k | sudoers_ctx.runas.argv[0] = (char *)"list"; |
965 | 9.71k | if (argc != 0) |
966 | 3.23k | memcpy(sudoers_ctx.runas.argv + 1, argv, (size_t)argc * sizeof(char *)); |
967 | 9.71k | sudoers_ctx.runas.argc = argc + 1; |
968 | 9.71k | sudoers_ctx.runas.argv[sudoers_ctx.runas.argc] = NULL; |
969 | | |
970 | 9.71k | ret = sudoers_check_common(&sudoers_ctx, I_LISTPW); |
971 | 9.71k | if (ret != true) |
972 | 798 | goto done; |
973 | | |
974 | 8.91k | pw = sudoers_ctx.runas.list_pw ? sudoers_ctx.runas.list_pw : sudoers_ctx.user.pw; |
975 | 8.91k | if (ISSET(sudoers_ctx.mode, MODE_CHECK)) |
976 | 2.97k | ret = display_cmnd(&sudoers_ctx, snl, pw, verbose); |
977 | 5.94k | else |
978 | 5.94k | ret = display_privs(&sudoers_ctx, snl, pw, verbose); |
979 | | |
980 | 9.71k | done: |
981 | 9.71k | mail_parse_errors(&sudoers_ctx); |
982 | | |
983 | 9.71k | if (def_group_plugin) |
984 | 0 | group_plugin_unload(); |
985 | 9.71k | reset_parser(); |
986 | 9.71k | env_init(NULL); |
987 | | |
988 | 9.71k | if (!rewind_perms()) |
989 | 0 | ret = -1; |
990 | | |
991 | 9.71k | restore_nproc(); |
992 | | |
993 | 9.71k | sudo_warn_set_locale_func(NULL); |
994 | | |
995 | 9.71k | debug_return_int(ret); |
996 | 9.71k | } |
997 | | |
998 | | /* |
999 | | * Initialize timezone and fill in ctx->user. |
1000 | | */ |
1001 | | static bool |
1002 | | init_vars(struct sudoers_context *ctx, char * const envp[]) |
1003 | 20.4k | { |
1004 | 20.4k | char * const * ep; |
1005 | 20.4k | bool unknown_user = false; |
1006 | 20.4k | debug_decl(init_vars, SUDOERS_DEBUG_PLUGIN); |
1007 | | |
1008 | 20.4k | if (!sudoers_initlocale(setlocale(LC_ALL, NULL), def_sudoers_locale)) { |
1009 | 0 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); |
1010 | 0 | debug_return_bool(false); |
1011 | 0 | } |
1012 | | |
1013 | 20.4k | #define MATCHES(s, v) \ |
1014 | 245k | (strncmp((s), (v), sizeof(v) - 1) == 0 && (s)[sizeof(v) - 1] != '\0') |
1015 | | |
1016 | 20.4k | ctx->user.envp = envp; |
1017 | 778k | for (ep = ctx->user.envp; *ep; ep++) { |
1018 | 757k | switch (**ep) { |
1019 | 20.4k | case 'K': |
1020 | 20.4k | if (MATCHES(*ep, "KRB5CCNAME=")) |
1021 | 20.4k | ctx->user.ccname = *ep + sizeof("KRB5CCNAME=") - 1; |
1022 | 20.4k | break; |
1023 | 40.9k | case 'P': |
1024 | 40.9k | if (MATCHES(*ep, "PATH=")) |
1025 | 20.4k | ctx->user.path = *ep + sizeof("PATH=") - 1; |
1026 | 40.9k | break; |
1027 | 102k | case 'S': |
1028 | 102k | if (MATCHES(*ep, "SUDO_PROMPT=")) { |
1029 | | /* Don't override "sudo -p prompt" */ |
1030 | 20.4k | if (ctx->user.prompt == NULL) |
1031 | 20.0k | ctx->user.prompt = *ep + sizeof("SUDO_PROMPT=") - 1; |
1032 | 20.4k | break; |
1033 | 20.4k | } |
1034 | 81.9k | if (MATCHES(*ep, "SUDO_USER=")) |
1035 | 0 | prev_user = *ep + sizeof("SUDO_USER=") - 1; |
1036 | 81.9k | break; |
1037 | 757k | } |
1038 | 757k | } |
1039 | 20.4k | #undef MATCHES |
1040 | | |
1041 | 20.4k | if (ctx->user.pw == NULL) { |
1042 | | /* Fake a struct passwd for the call to log_warningx(). */ |
1043 | 251 | ctx->user.pw = sudo_mkpwent(ctx->user.name, ctx->user.uid, |
1044 | 251 | ctx->user.gid, NULL, NULL); |
1045 | 251 | unknown_user = true; |
1046 | 251 | } |
1047 | 20.4k | if (ctx->user.gid_list == NULL) |
1048 | 20.4k | ctx->user.gid_list = sudo_get_gidlist(ctx->user.pw, ENTRY_TYPE_ANY); |
1049 | | |
1050 | | /* Store initialize permissions so we can restore them later. */ |
1051 | 20.4k | if (!set_perms(ctx, PERM_INITIAL)) |
1052 | 0 | debug_return_bool(false); |
1053 | | |
1054 | | /* Set parse callbacks */ |
1055 | 20.4k | set_callbacks(); |
1056 | | |
1057 | | /* It is now safe to use log_warningx() and set_perms() */ |
1058 | 20.4k | if (unknown_user) { |
1059 | 251 | log_warningx(ctx, SLOG_SEND_MAIL, N_("unknown user %s"), ctx->user.name); |
1060 | 251 | debug_return_bool(false); |
1061 | 251 | } |
1062 | | |
1063 | | /* |
1064 | | * Set runas passwd/group entries based on command line or sudoers. |
1065 | | * Note that if runas_group was specified without runas_user we |
1066 | | * run the command as the invoking user. |
1067 | | */ |
1068 | 20.2k | if (ctx->runas.group != NULL) { |
1069 | 729 | if (!set_runasgr(ctx, ctx->runas.group, false)) |
1070 | 116 | debug_return_bool(false); |
1071 | 613 | if (!set_runaspw(ctx, ctx->runas.user ? |
1072 | 613 | ctx->runas.user : ctx->user.name, false)) |
1073 | 3 | debug_return_bool(false); |
1074 | 19.5k | } else { |
1075 | 19.5k | if (!set_runaspw(ctx, ctx->runas.user ? |
1076 | 19.5k | ctx->runas.user : def_runas_default, false)) |
1077 | 11 | debug_return_bool(false); |
1078 | 19.5k | } |
1079 | | |
1080 | 20.1k | debug_return_bool(true); |
1081 | 20.1k | } |
1082 | | |
1083 | | /* |
1084 | | * Fill in ctx->user.cmnd and ctx->user.cmnd_stat variables. |
1085 | | * Does not fill in ctx->user.cmnd_base. |
1086 | | */ |
1087 | | int |
1088 | | set_cmnd_path(struct sudoers_context *ctx, const char *runchroot) |
1089 | 22.0k | { |
1090 | 22.0k | const char *cmnd_in; |
1091 | 22.0k | char *cmnd_out = NULL; |
1092 | 22.0k | char *path = ctx->user.path; |
1093 | 22.0k | int ret; |
1094 | 22.0k | debug_decl(set_cmnd_path, SUDOERS_DEBUG_PLUGIN); |
1095 | | |
1096 | 22.0k | cmnd_in = ISSET(ctx->mode, MODE_CHECK) ? |
1097 | 18.8k | ctx->runas.argv[1] : ctx->runas.argv[0]; |
1098 | | |
1099 | 22.0k | free(ctx->user.cmnd_list); |
1100 | 22.0k | ctx->user.cmnd_list = NULL; |
1101 | 22.0k | free(ctx->user.cmnd); |
1102 | 22.0k | ctx->user.cmnd = NULL; |
1103 | 22.0k | canon_path_free(ctx->user.cmnd_dir); |
1104 | 22.0k | ctx->user.cmnd_dir = NULL; |
1105 | 22.0k | if (def_secure_path && !user_is_exempt(ctx)) |
1106 | 0 | path = def_secure_path; |
1107 | | |
1108 | 22.0k | ret = resolve_cmnd(ctx, cmnd_in, &cmnd_out, path, runchroot); |
1109 | 22.0k | if (ret == FOUND) { |
1110 | 14.5k | char *slash = strrchr(cmnd_out, '/'); |
1111 | 14.5k | if (slash != NULL) { |
1112 | 14.5k | *slash = '\0'; |
1113 | 14.5k | ctx->user.cmnd_dir = canon_path(cmnd_out); |
1114 | 14.5k | if (ctx->user.cmnd_dir == NULL && errno == ENOMEM) |
1115 | 0 | goto error; |
1116 | 14.5k | *slash = '/'; |
1117 | 14.5k | } |
1118 | 14.5k | } |
1119 | | |
1120 | 22.0k | if (ISSET(ctx->mode, MODE_CHECK)) |
1121 | 3.23k | ctx->user.cmnd_list = cmnd_out; |
1122 | 18.8k | else |
1123 | 18.8k | ctx->user.cmnd = cmnd_out; |
1124 | | |
1125 | 22.0k | debug_return_int(ret); |
1126 | 0 | error: |
1127 | 0 | free(cmnd_out); |
1128 | 0 | debug_return_int(NOT_FOUND_ERROR); |
1129 | 0 | } |
1130 | | |
1131 | | /* |
1132 | | * Fill in ctx->user.cmnd, ctx->user.cmnd_stat and cmnd_status variables. |
1133 | | * Does not fill in ctx->user.cmnd_base. |
1134 | | */ |
1135 | | void |
1136 | | set_cmnd_status(struct sudoers_context *ctx, const char *runchroot) |
1137 | 5.94k | { |
1138 | 5.94k | cmnd_status = set_cmnd_path(ctx, runchroot); |
1139 | 5.94k | } |
1140 | | |
1141 | | /* |
1142 | | * Fill in ctx->user.cmnd, ctx->user.cmnd_args, ctx->user.cmnd_base and |
1143 | | * ctx->user.cmnd_stat variables and apply any command-specific defaults entries. |
1144 | | */ |
1145 | | static int |
1146 | | set_cmnd(struct sudoers_context *ctx) |
1147 | 26.9k | { |
1148 | 26.9k | struct sudo_nss *nss; |
1149 | 26.9k | int ret = FOUND; |
1150 | 26.9k | debug_decl(set_cmnd, SUDOERS_DEBUG_PLUGIN); |
1151 | | |
1152 | | /* Allocate ctx->user.cmnd_stat for find_path() and match functions. */ |
1153 | 26.9k | free(ctx->user.cmnd_stat); |
1154 | 26.9k | ctx->user.cmnd_stat = calloc(1, sizeof(struct stat)); |
1155 | 26.9k | if (ctx->user.cmnd_stat == NULL) { |
1156 | 0 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); |
1157 | 0 | debug_return_int(NOT_FOUND_ERROR); |
1158 | 0 | } |
1159 | | |
1160 | | /* Re-initialize for when we are called multiple times. */ |
1161 | 26.9k | free(ctx->runas.cmnd); |
1162 | 26.9k | ctx->runas.cmnd = NULL; |
1163 | | |
1164 | 26.9k | if (ISSET(ctx->mode, MODE_RUN|MODE_EDIT|MODE_CHECK)) { |
1165 | 17.1k | if (!ISSET(ctx->mode, MODE_EDIT)) { |
1166 | 16.1k | const char *runchroot = ctx->runas.chroot; |
1167 | 16.1k | if (runchroot == NULL && def_runchroot != NULL && |
1168 | 16.0k | strcmp(def_runchroot, "*") != 0) |
1169 | 16.0k | runchroot = def_runchroot; |
1170 | | |
1171 | 16.1k | ret = set_cmnd_path(ctx, runchroot); |
1172 | 16.1k | if (ret == NOT_FOUND_ERROR) { |
1173 | 0 | if (errno == ENAMETOOLONG) { |
1174 | 0 | audit_failure(ctx, ctx->runas.argv, N_("command too long")); |
1175 | 0 | } |
1176 | 0 | log_warning(ctx, 0, "%s", ctx->runas.argv[0]); |
1177 | 0 | debug_return_int(ret); |
1178 | 0 | } |
1179 | 16.1k | } |
1180 | | |
1181 | | /* set ctx->user.cmnd_args */ |
1182 | 17.1k | free(ctx->user.cmnd_args); |
1183 | 17.1k | ctx->user.cmnd_args = NULL; |
1184 | 17.1k | if (ISSET(ctx->mode, MODE_CHECK)) { |
1185 | 3.23k | if (ctx->runas.argc > 2) { |
1186 | | /* Skip the command being listed in ctx->runas.argv[1]. */ |
1187 | 208 | ctx->user.cmnd_args = strvec_join(ctx->runas.argv + 2, ' ', NULL); |
1188 | 208 | if (ctx->user.cmnd_args == NULL) |
1189 | 0 | debug_return_int(NOT_FOUND_ERROR); |
1190 | 208 | } |
1191 | 13.9k | } else if (ctx->runas.argc > 1) { |
1192 | 1.57k | if (ISSET(ctx->mode, MODE_SHELL|MODE_LOGIN_SHELL) && |
1193 | 760 | ISSET(ctx->mode, MODE_RUN)) { |
1194 | | /* |
1195 | | * When running a command via a shell, the sudo front-end |
1196 | | * escapes potential meta chars. We unescape non-spaces |
1197 | | * for sudoers matching and logging purposes. |
1198 | | * TODO: move escaping to the policy plugin instead |
1199 | | */ |
1200 | 760 | ctx->user.cmnd_args = strvec_join(ctx->runas.argv + 1, ' ', |
1201 | 760 | strlcpy_unescape); |
1202 | 816 | } else { |
1203 | 816 | ctx->user.cmnd_args = strvec_join(ctx->runas.argv + 1, ' ', |
1204 | 816 | NULL); |
1205 | 816 | } |
1206 | 1.57k | if (ctx->user.cmnd_args == NULL) |
1207 | 0 | debug_return_int(NOT_FOUND_ERROR); |
1208 | 1.57k | } |
1209 | 17.1k | } |
1210 | 26.9k | if (ctx->user.cmnd == NULL) { |
1211 | 14.3k | ctx->user.cmnd = strdup(ctx->runas.argv[0]); |
1212 | 14.3k | if (ctx->user.cmnd == NULL) |
1213 | 0 | debug_return_int(NOT_FOUND_ERROR); |
1214 | 14.3k | } |
1215 | 26.9k | ctx->user.cmnd_base = sudo_basename(ctx->user.cmnd); |
1216 | | |
1217 | | /* Convert "sudo sudoedit" -> "sudoedit" */ |
1218 | 26.9k | if (ISSET(ctx->mode, MODE_RUN) && strcmp(ctx->user.cmnd_base, "sudoedit") == 0) { |
1219 | 240 | char *new_cmnd; |
1220 | | |
1221 | 240 | CLR(ctx->mode, MODE_RUN); |
1222 | 240 | SET(ctx->mode, MODE_EDIT); |
1223 | 240 | sudo_warnx("%s", U_("sudoedit doesn't need to be run via sudo")); |
1224 | 240 | if ((new_cmnd = strdup("sudoedit")) == NULL) { |
1225 | 0 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); |
1226 | 0 | debug_return_int(NOT_FOUND_ERROR); |
1227 | 0 | } |
1228 | 240 | free(ctx->user.cmnd); |
1229 | 240 | ctx->user.cmnd_base = ctx->user.cmnd = new_cmnd; |
1230 | 240 | } |
1231 | | |
1232 | 26.9k | TAILQ_FOREACH(nss, snl, entries) { |
1233 | | /* Missing/invalid defaults is not a fatal error. */ |
1234 | 26.9k | (void)update_defaults(ctx, nss->parse_tree, NULL, SETDEF_CMND, false); |
1235 | 26.9k | } |
1236 | | |
1237 | 26.9k | debug_return_int(ret); |
1238 | 26.9k | } |
1239 | | |
1240 | | static int |
1241 | | open_file(const char *path, int flags) |
1242 | 0 | { |
1243 | 0 | int fd; |
1244 | 0 | debug_decl(open_file, SUDOERS_DEBUG_PLUGIN); |
1245 | |
|
1246 | 0 | if (!set_perms(NULL, PERM_SUDOERS)) |
1247 | 0 | debug_return_int(-1); |
1248 | | |
1249 | 0 | fd = open(path, flags); |
1250 | 0 | if (fd == -1 && errno == EACCES && geteuid() != ROOT_UID) { |
1251 | | /* |
1252 | | * If we tried to open sudoers as non-root but got EACCES, |
1253 | | * try again as root. |
1254 | | */ |
1255 | 0 | int serrno = errno; |
1256 | 0 | if (restore_perms() && set_perms(NULL, PERM_ROOT)) |
1257 | 0 | fd = open(path, flags); |
1258 | 0 | errno = serrno; |
1259 | 0 | } |
1260 | 0 | if (!restore_perms()) { |
1261 | | /* unable to change back to root */ |
1262 | 0 | if (fd != -1) { |
1263 | 0 | close(fd); |
1264 | 0 | fd = -1; |
1265 | 0 | } |
1266 | 0 | } |
1267 | |
|
1268 | 0 | debug_return_int(fd); |
1269 | 0 | } |
1270 | | |
1271 | | /* |
1272 | | * Open sudoers file and check mode/owner/type. |
1273 | | * Returns a handle to the sudoers file or NULL on error. |
1274 | | */ |
1275 | | FILE * |
1276 | | open_sudoers(const char *path, char **outfile, bool doedit, bool *keepopen) |
1277 | 0 | { |
1278 | 0 | char fname[PATH_MAX]; |
1279 | 0 | FILE *fp = NULL; |
1280 | 0 | struct stat sb; |
1281 | 0 | int error, fd; |
1282 | 0 | debug_decl(open_sudoers, SUDOERS_DEBUG_PLUGIN); |
1283 | |
|
1284 | 0 | fd = sudo_open_conf_path(path, fname, sizeof(fname), open_file); |
1285 | 0 | if (sudoers_ctx.parser_conf.ignore_perms) { |
1286 | | /* Skip sudoers security checks when ignore_perms is set. */ |
1287 | 0 | if (fd == -1 || fstat(fd, &sb) == -1) |
1288 | 0 | error = SUDO_PATH_MISSING; |
1289 | 0 | else |
1290 | 0 | error = SUDO_PATH_SECURE; |
1291 | 0 | } else { |
1292 | 0 | error = sudo_secure_fd(fd, S_IFREG, sudoers_file_uid(), |
1293 | 0 | sudoers_file_gid(), &sb); |
1294 | 0 | } |
1295 | 0 | switch (error) { |
1296 | 0 | case SUDO_PATH_SECURE: |
1297 | | /* |
1298 | | * Make sure we can read the file so we can present the |
1299 | | * user with a reasonable error message (unlike the lexer). |
1300 | | */ |
1301 | 0 | if ((fp = fdopen(fd, "r")) == NULL) { |
1302 | 0 | log_warning(&sudoers_ctx, SLOG_PARSE_ERROR, |
1303 | 0 | N_("unable to open %s"), fname); |
1304 | 0 | } else { |
1305 | 0 | fd = -1; |
1306 | 0 | if (sb.st_size != 0 && fgetc(fp) == EOF) { |
1307 | 0 | log_warning(&sudoers_ctx, SLOG_PARSE_ERROR, |
1308 | 0 | N_("unable to read %s"), fname); |
1309 | 0 | fclose(fp); |
1310 | 0 | fp = NULL; |
1311 | 0 | } else { |
1312 | | /* Rewind fp and set close on exec flag. */ |
1313 | 0 | rewind(fp); |
1314 | 0 | (void)fcntl(fileno(fp), F_SETFD, FD_CLOEXEC); |
1315 | 0 | if (outfile != NULL) { |
1316 | 0 | *outfile = sudo_rcstr_dup(fname); |
1317 | 0 | if (*outfile == NULL) { |
1318 | 0 | sudo_warnx(U_("%s: %s"), __func__, |
1319 | 0 | U_("unable to allocate memory")); |
1320 | 0 | fclose(fp); |
1321 | 0 | fp = NULL; |
1322 | 0 | } |
1323 | 0 | } |
1324 | 0 | } |
1325 | 0 | } |
1326 | 0 | break; |
1327 | 0 | case SUDO_PATH_MISSING: |
1328 | 0 | log_warning(&sudoers_ctx, SLOG_PARSE_ERROR, |
1329 | 0 | N_("unable to open %s"), path); |
1330 | 0 | break; |
1331 | 0 | case SUDO_PATH_BAD_TYPE: |
1332 | 0 | log_warningx(&sudoers_ctx, SLOG_PARSE_ERROR, |
1333 | 0 | N_("%s is not a regular file"), fname); |
1334 | 0 | break; |
1335 | 0 | case SUDO_PATH_WRONG_OWNER: |
1336 | 0 | log_warningx(&sudoers_ctx, SLOG_PARSE_ERROR, |
1337 | 0 | N_("%s is owned by uid %u, should be %u"), fname, |
1338 | 0 | (unsigned int)sb.st_uid, (unsigned int)sudoers_file_uid()); |
1339 | 0 | break; |
1340 | 0 | case SUDO_PATH_WORLD_WRITABLE: |
1341 | 0 | log_warningx(&sudoers_ctx, SLOG_PARSE_ERROR, |
1342 | 0 | N_("%s is world writable"), fname); |
1343 | 0 | break; |
1344 | 0 | case SUDO_PATH_GROUP_WRITABLE: |
1345 | 0 | log_warningx(&sudoers_ctx, SLOG_PARSE_ERROR, |
1346 | 0 | N_("%s is owned by gid %u, should be %u"), fname, |
1347 | 0 | (unsigned int)sb.st_gid, (unsigned int)sudoers_file_gid()); |
1348 | 0 | break; |
1349 | 0 | default: |
1350 | 0 | sudo_warnx("%s: internal error, unexpected error %d", __func__, error); |
1351 | 0 | break; |
1352 | 0 | } |
1353 | | |
1354 | 0 | if (fp == NULL && fd != -1) |
1355 | 0 | close(fd); |
1356 | |
|
1357 | 0 | debug_return_ptr(fp); |
1358 | 0 | } |
1359 | | |
1360 | | #ifdef HAVE_LOGIN_CAP_H |
1361 | | static bool |
1362 | | set_loginclass(struct sudoers_context *ctx) |
1363 | | { |
1364 | | const struct passwd *pw = ctx->runas.pw ? ctx->runas.pw : ctx->user.pw; |
1365 | | const unsigned int errflags = SLOG_RAW_MSG; |
1366 | | login_cap_t *lc; |
1367 | | bool ret = true; |
1368 | | debug_decl(set_loginclass, SUDOERS_DEBUG_PLUGIN); |
1369 | | |
1370 | | if (!def_use_loginclass) |
1371 | | goto done; |
1372 | | |
1373 | | if (ctx->runas.class && strcmp(ctx->runas.class, "-") != 0) { |
1374 | | if (ctx->user.uid != 0 && pw->pw_uid != 0) { |
1375 | | sudo_warnx(U_("only root can use \"-c %s\""), ctx->runas.class); |
1376 | | ret = false; |
1377 | | goto done; |
1378 | | } |
1379 | | } else { |
1380 | | ctx->runas.class = pw->pw_class; |
1381 | | if (!ctx->runas.class || !*ctx->runas.class) |
1382 | | ctx->runas.class = (char *) |
1383 | | ((pw->pw_uid == 0) ? LOGIN_DEFROOTCLASS : LOGIN_DEFCLASS); |
1384 | | } |
1385 | | |
1386 | | /* Make sure specified login class is valid. */ |
1387 | | lc = login_getclass(ctx->runas.class); |
1388 | | if (!lc || !lc->lc_class || strcmp(lc->lc_class, ctx->runas.class) != 0) { |
1389 | | /* |
1390 | | * Don't make it an error if the user didn't specify the login |
1391 | | * class themselves. We do this because if login.conf gets |
1392 | | * corrupted we want the admin to be able to use sudo to fix it. |
1393 | | */ |
1394 | | log_warningx(ctx, errflags, N_("unknown login class %s"), |
1395 | | ctx->runas.class); |
1396 | | def_use_loginclass = false; |
1397 | | if (ctx->runas.class) |
1398 | | ret = false; |
1399 | | } |
1400 | | login_close(lc); |
1401 | | done: |
1402 | | debug_return_bool(ret); |
1403 | | } |
1404 | | #else |
1405 | | static bool |
1406 | | set_loginclass(struct sudoers_context *ctx) |
1407 | 20.1k | { |
1408 | 20.1k | return true; |
1409 | 20.1k | } |
1410 | | #endif /* HAVE_LOGIN_CAP_H */ |
1411 | | |
1412 | | /* |
1413 | | * Get passwd entry for the user we are going to run commands as |
1414 | | * and store it in ctx->runas.pw. By default, commands run as "root". |
1415 | | */ |
1416 | | static bool |
1417 | | set_runaspw(struct sudoers_context *ctx, const char *user, bool quiet) |
1418 | 45.0k | { |
1419 | 45.0k | struct passwd *pw = NULL; |
1420 | 45.0k | debug_decl(set_runaspw, SUDOERS_DEBUG_PLUGIN); |
1421 | | |
1422 | 45.0k | unknown_runas_uid = false; |
1423 | 45.0k | if (*user == '#') { |
1424 | 253 | const char *errstr; |
1425 | 253 | uid_t uid = sudo_strtoid(user + 1, &errstr); |
1426 | 253 | if (errstr == NULL) { |
1427 | 250 | if ((pw = sudo_getpwuid(uid)) == NULL) { |
1428 | 90 | unknown_runas_uid = true; |
1429 | 90 | pw = sudo_fakepwnam(user, ctx->user.gid); |
1430 | 90 | } |
1431 | 250 | } |
1432 | 253 | } |
1433 | 45.0k | if (pw == NULL) { |
1434 | 44.7k | if ((pw = sudo_getpwnam(user)) == NULL) { |
1435 | 14 | if (!quiet) |
1436 | 14 | log_warningx(ctx, SLOG_AUDIT, N_("unknown user %s"), user); |
1437 | 14 | debug_return_bool(false); |
1438 | 14 | } |
1439 | 44.7k | } |
1440 | 45.0k | if (ctx->runas.pw != NULL) |
1441 | 24.9k | sudo_pw_delref(ctx->runas.pw); |
1442 | 45.0k | ctx->runas.pw = pw; |
1443 | 45.0k | debug_return_bool(true); |
1444 | 45.0k | } |
1445 | | |
1446 | | /* |
1447 | | * Get group entry for the group we are going to run commands as |
1448 | | * and store it in ctx->runas.gr. |
1449 | | */ |
1450 | | static bool |
1451 | | set_runasgr(struct sudoers_context *ctx, const char *group, bool quiet) |
1452 | 729 | { |
1453 | 729 | struct group *gr = NULL; |
1454 | 729 | debug_decl(set_runasgr, SUDOERS_DEBUG_PLUGIN); |
1455 | | |
1456 | 729 | unknown_runas_gid = false; |
1457 | 729 | if (*group == '#') { |
1458 | 563 | const char *errstr; |
1459 | 563 | gid_t gid = sudo_strtoid(group + 1, &errstr); |
1460 | 563 | if (errstr == NULL) { |
1461 | 562 | if ((gr = sudo_getgrgid(gid)) == NULL) { |
1462 | 461 | unknown_runas_gid = true; |
1463 | 461 | gr = sudo_fakegrnam(group); |
1464 | 461 | } |
1465 | 562 | } |
1466 | 563 | } |
1467 | 729 | if (gr == NULL) { |
1468 | 167 | if ((gr = sudo_getgrnam(group)) == NULL) { |
1469 | 116 | if (!quiet) |
1470 | 116 | log_warningx(ctx, SLOG_AUDIT, N_("unknown group %s"), group); |
1471 | 116 | debug_return_bool(false); |
1472 | 116 | } |
1473 | 167 | } |
1474 | 613 | if (ctx->runas.gr != NULL) |
1475 | 0 | sudo_gr_delref(ctx->runas.gr); |
1476 | 613 | ctx->runas.gr = gr; |
1477 | 613 | debug_return_bool(true); |
1478 | 613 | } |
1479 | | |
1480 | | /* |
1481 | | * Callback for runas_default sudoers setting. |
1482 | | */ |
1483 | | bool |
1484 | | cb_runas_default(struct sudoers_context *ctx, const char *file, int line, |
1485 | | int column, const union sudo_defs_val *sd_un, int op) |
1486 | 26.0k | { |
1487 | 26.0k | debug_decl(cb_runas_default, SUDOERS_DEBUG_PLUGIN); |
1488 | | |
1489 | | /* Only reset runaspw if user didn't specify one. */ |
1490 | 26.0k | if (ctx->runas.user == NULL && ctx->runas.group == NULL) |
1491 | 24.9k | debug_return_bool(set_runaspw(ctx, sd_un->str, true)); |
1492 | 1.13k | debug_return_bool(true); |
1493 | 1.13k | } |
1494 | | |
1495 | | /* |
1496 | | * Cleanup hook for sudo_fatal()/sudo_fatalx() |
1497 | | * Also called at policy close time. |
1498 | | */ |
1499 | | void |
1500 | | sudoers_cleanup(void) |
1501 | 25.7k | { |
1502 | 25.7k | struct sudo_nss *nss; |
1503 | 25.7k | struct defaults *def; |
1504 | 25.7k | debug_decl(sudoers_cleanup, SUDOERS_DEBUG_PLUGIN); |
1505 | | |
1506 | 25.7k | if (snl != NULL) { |
1507 | 20.1k | TAILQ_FOREACH(nss, snl, entries) { |
1508 | 20.1k | nss->close(&sudoers_ctx, nss); |
1509 | 20.1k | } |
1510 | 20.1k | snl = NULL; |
1511 | 20.1k | reset_parser(); |
1512 | 20.1k | } |
1513 | 44.0k | while ((def = TAILQ_FIRST(&initial_defaults)) != NULL) { |
1514 | 18.3k | TAILQ_REMOVE(&initial_defaults, def, entries); |
1515 | 18.3k | free(def->var); |
1516 | 18.3k | free(def->val); |
1517 | 18.3k | free(def); |
1518 | 18.3k | } |
1519 | 25.7k | need_reinit = false; |
1520 | 25.7k | if (def_group_plugin) |
1521 | 0 | group_plugin_unload(); |
1522 | 25.7k | sudoers_ctx_free(&sudoers_ctx); |
1523 | 25.7k | sudo_freepwcache(); |
1524 | 25.7k | sudo_freegrcache(); |
1525 | 25.7k | canon_path_free_cache(); |
1526 | | |
1527 | | /* We must free the cached environment before running g/c. */ |
1528 | 25.7k | env_free(); |
1529 | | |
1530 | | /* Run garbage collector. */ |
1531 | 25.7k | sudoers_gc_run(); |
1532 | | |
1533 | | /* Clear globals */ |
1534 | 25.7k | prev_user = NULL; |
1535 | | |
1536 | 25.7k | debug_return; |
1537 | 25.7k | } |
1538 | | |
1539 | | bool |
1540 | | sudoers_set_mode(unsigned int flags, unsigned int mask) |
1541 | 34.1k | { |
1542 | 34.1k | SET(sudoers_ctx.mode, flags); |
1543 | 34.1k | return ((sudoers_ctx.mode & mask) == sudoers_ctx.mode); |
1544 | 34.1k | } |
1545 | | |
1546 | | const struct sudoers_context * |
1547 | | sudoers_get_context(void) |
1548 | 67.9k | { |
1549 | 67.9k | return &sudoers_ctx; |
1550 | 67.9k | } |
1551 | | |
1552 | | bool |
1553 | | sudoers_set_log_format(enum def_tuple tuple) |
1554 | 0 | { |
1555 | 0 | enum eventlog_format format; |
1556 | 0 | debug_decl(cb_log_format, SUDOERS_DEBUG_PLUGIN); |
1557 | | |
1558 | | /* FFR - make "json" an alias for EVLOG_JSON_COMPACT instead. */ |
1559 | 0 | switch (tuple) { |
1560 | 0 | case json_compact: |
1561 | 0 | format = EVLOG_JSON_COMPACT; |
1562 | 0 | break; |
1563 | 0 | case json: |
1564 | 0 | case json_pretty: |
1565 | 0 | format = EVLOG_JSON_PRETTY; |
1566 | 0 | break; |
1567 | 0 | case sudo: |
1568 | 0 | format = EVLOG_SUDO; |
1569 | 0 | break; |
1570 | 0 | default: |
1571 | 0 | debug_return_bool(false); |
1572 | 0 | } |
1573 | 0 | eventlog_set_format(format); |
1574 | |
|
1575 | | debug_return_bool(true); |
1576 | 0 | } |