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 #ifdef __TANDEM
25 # include <floss.h>
26 #endif
27 
28 #include <config.h>
29 
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/wait.h>
33 #include <sys/resource.h>
34 #include <sys/socket.h>
35 #include <stddef.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <ctype.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <limits.h>
44 #include <signal.h>
45 #include <grp.h>
46 #include <pwd.h>
47 #include <time.h>
48 #ifdef HAVE_SELINUX
49 # include <selinux/selinux.h>		/* for is_selinux_enabled() */
50 #endif
51 #ifdef HAVE_SETAUTHDB
52 # include <usersec.h>
53 #endif /* HAVE_SETAUTHDB */
54 #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
55 # ifdef __hpux
56 #  undef MAXINT
57 #  include <hpsecurity.h>
58 # else
59 #  include <sys/security.h>
60 # endif /* __hpux */
61 # include <prot.h>
62 #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
63 
64 #include <sudo_usage.h>
65 #include "sudo.h"
66 #include "sudo_plugin.h"
67 #include "sudo_plugin_int.h"
68 
69 /*
70  * Local variables
71  */
72 struct plugin_container policy_plugin;
73 struct plugin_container_list io_plugins = TAILQ_HEAD_INITIALIZER(io_plugins);
74 struct plugin_container_list audit_plugins = TAILQ_HEAD_INITIALIZER(audit_plugins);
75 struct plugin_container_list approval_plugins = TAILQ_HEAD_INITIALIZER(approval_plugins);
76 struct user_details user_details;
77 const char *list_user; /* extern for parse_args.c */
78 int sudo_debug_instance = SUDO_DEBUG_INSTANCE_INITIALIZER;
79 static struct command_details command_details;
80 static int sudo_mode;
81 static struct sudo_event_base *sudo_event_base;
82 
83 struct sudo_gc_entry {
84     SLIST_ENTRY(sudo_gc_entry) entries;
85     enum sudo_gc_types type;
86     union {
87 	char **vec;
88 	void *ptr;
89     } u;
90 };
91 SLIST_HEAD(sudo_gc_list, sudo_gc_entry);
92 #ifdef NO_LEAKS
93 static struct sudo_gc_list sudo_gc_list = SLIST_HEAD_INITIALIZER(sudo_gc_list);
94 #endif
95 
96 /*
97  * Local functions
98  */
99 static void fix_fds(void);
100 static void sudo_check_suid(const char *path);
101 static char **get_user_info(struct user_details *);
102 static void command_info_to_details(char * const info[],
103     struct command_details *details);
104 static void gc_init(void);
105 
106 /* Policy plugin convenience functions. */
107 static void policy_open(void);
108 static void policy_close(int exit_status, int error);
109 static int policy_show_version(int verbose);
110 static bool policy_check(int argc, char * const argv[], char *env_add[],
111     char **command_info[], char **run_argv[], char **run_envp[]);
112 static void policy_list(int argc, char * const argv[],
113     int verbose, const char *user);
114 static void policy_validate(char * const argv[]);
115 static void policy_invalidate(int unlinkit);
116 
117 /* I/O log plugin convenience functions. */
118 static bool iolog_open(char * const command_info[], int run_argc,
119     char * const run_argv[], char * const run_envp[]);
120 static void iolog_close(int exit_status, int error);
121 static void iolog_show_version(int verbose, int argc, char * const argv[],
122     char * const envp[]);
123 static void unlink_plugin(struct plugin_container_list *plugin_list, struct plugin_container *plugin);
124 static void free_plugin_container(struct plugin_container *plugin, bool ioplugin);
125 
126 /* Audit plugin convenience functions (some are public). */
127 static void audit_open(void);
128 static void audit_close(int exit_status, int error);
129 static void audit_show_version(int verbose);
130 
131 /* Approval plugin convenience functions (some are public). */
132 static void approval_show_version(int verbose);
133 
134 sudo_dso_public int main(int argc, char *argv[], char *envp[]);
135 
136 static struct sudo_settings *sudo_settings;
137 static char * const *user_info, * const *submit_argv, * const *submit_envp;
138 static int submit_optind;
139 
140 int
main(int argc,char * argv[],char * envp[])141 main(int argc, char *argv[], char *envp[])
142 {
143     int nargc, status = 0;
144     char **nargv, **env_add;
145     char **command_info = NULL, **argv_out = NULL, **run_envp = NULL;
146     const char * const allowed_prognames[] = { "sudo", "sudoedit", NULL };
147     sigset_t mask;
148     debug_decl_vars(main, SUDO_DEBUG_MAIN);
149 
150     /* Only allow "sudo" or "sudoedit" as the program name. */
151     initprogname2(argc > 0 ? argv[0] : "sudo", allowed_prognames);
152 
153     /* Crank resource limits to unlimited. */
154     unlimit_sudo();
155 
156     /* Make sure fds 0-2 are open and do OS-specific initialization. */
157     fix_fds();
158     os_init(argc, argv, envp);
159 
160     setlocale(LC_ALL, "");
161     bindtextdomain(PACKAGE_NAME, LOCALEDIR);
162     textdomain(PACKAGE_NAME);
163 
164     (void) tzset();
165 
166     /* Must be done before we do any password lookups */
167 #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
168     (void) set_auth_parameters(argc, argv);
169 # ifdef HAVE_INITPRIVS
170     initprivs();
171 # endif
172 #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
173 
174     /* Initialize the debug subsystem. */
175     if (sudo_conf_read(NULL, SUDO_CONF_DEBUG) == -1)
176 	exit(EXIT_FAILURE);
177     sudo_debug_instance = sudo_debug_register(getprogname(),
178 	NULL, NULL, sudo_conf_debug_files(getprogname()), -1);
179     if (sudo_debug_instance == SUDO_DEBUG_INSTANCE_ERROR)
180 	exit(EXIT_FAILURE);
181 
182     /* Make sure we are setuid root. */
183     sudo_check_suid(argc > 0 ? argv[0] : "sudo");
184 
185     /* Save original signal state and setup default signal handlers. */
186     save_signals();
187     init_signals();
188 
189     /* Reset signal mask to the default value (unblock). */
190     (void) sigemptyset(&mask);
191     (void) sigprocmask(SIG_SETMASK, &mask, NULL);
192 
193     /* Parse the rest of sudo.conf. */
194     sudo_conf_read(NULL, SUDO_CONF_ALL & ~SUDO_CONF_DEBUG);
195 
196     /* Fill in user_info with user name, uid, cwd, etc. */
197     if ((user_info = get_user_info(&user_details)) == NULL)
198 	exit(EXIT_FAILURE); /* get_user_info printed error message */
199 
200     /* Disable core dumps if not enabled in sudo.conf. */
201     if (sudo_conf_disable_coredump())
202 	disable_coredump();
203 
204     /* Parse command line arguments, preserving the original argv/envp. */
205     submit_argv = argv;
206     submit_envp = envp;
207     sudo_mode = parse_args(argc, argv, &submit_optind, &nargc, &nargv,
208 	&sudo_settings, &env_add);
209     sudo_debug_printf(SUDO_DEBUG_DEBUG, "sudo_mode %d", sudo_mode);
210 
211     /* Print sudo version early, in case of plugin init failure. */
212     if (ISSET(sudo_mode, MODE_VERSION)) {
213 	printf(_("Sudo version %s\n"), PACKAGE_VERSION);
214 	if (user_details.cred.uid == ROOT_UID)
215 	    (void) printf(_("Configure options: %s\n"), CONFIGURE_ARGS);
216     }
217 
218     /* Use conversation function for sudo_(warn|fatal)x? for plugins. */
219     sudo_warn_set_conversation(sudo_conversation);
220 
221     /* Load plugins. */
222     if (!sudo_load_plugins())
223 	sudo_fatalx("%s", U_("fatal error, unable to load plugins"));
224 
225     /* Allocate event base so plugin can use it. */
226     if ((sudo_event_base = sudo_ev_base_alloc()) == NULL)
227 	sudo_fatalx("%s", U_("unable to allocate memory"));
228 
229     /* Open policy and audit plugins. */
230     /* XXX - audit policy_open errors */
231     audit_open();
232     policy_open();
233 
234     switch (sudo_mode & MODE_MASK) {
235 	case MODE_VERSION:
236 	    policy_show_version(!user_details.cred.uid);
237 	    iolog_show_version(!user_details.cred.uid, nargc, nargv,
238 		submit_envp);
239 	    approval_show_version(!user_details.cred.uid);
240 	    audit_show_version(!user_details.cred.uid);
241 	    break;
242 	case MODE_VALIDATE:
243 	case MODE_VALIDATE|MODE_INVALIDATE:
244 	    policy_validate(nargv);
245 	    break;
246 	case MODE_KILL:
247 	case MODE_INVALIDATE:
248 	    policy_invalidate(sudo_mode == MODE_KILL);
249 	    break;
250 	case MODE_CHECK:
251 	case MODE_CHECK|MODE_INVALIDATE:
252 	case MODE_LIST:
253 	case MODE_LIST|MODE_INVALIDATE:
254 	    policy_list(nargc, nargv, ISSET(sudo_mode, MODE_LONG_LIST),
255 		list_user);
256 	    break;
257 	case MODE_EDIT:
258 	case MODE_RUN:
259 	    if (!policy_check(nargc, nargv, env_add, &command_info, &argv_out,
260 		    &run_envp))
261 		goto access_denied;
262 
263 	    /* Reset nargv/nargc based on argv_out. */
264 	    /* XXX - leaks old nargv in shell mode */
265 	    for (nargv = argv_out, nargc = 0; nargv[nargc] != NULL; nargc++)
266 		continue;
267 	    if (nargc == 0)
268 		sudo_fatalx("%s",
269 		    U_("plugin did not return a command to execute"));
270 
271 	    /* Approval plugins run after policy plugin accepts the command. */
272 	    if (!approval_check(command_info, nargv, run_envp))
273 		goto access_denied;
274 
275 	    /* Open I/O plugin once policy and approval plugins succeed. */
276 	    if (!iolog_open(command_info, nargc, nargv, run_envp))
277 		goto access_denied;
278 
279 	    /* Audit the accept event on behalf of the sudo front-end. */
280 	    if (!audit_accept("sudo", SUDO_FRONT_END, command_info,
281 		    nargv, run_envp))
282 		goto access_denied;
283 
284 	    /* Setup command details and run command/edit. */
285 	    command_info_to_details(command_info, &command_details);
286 	    command_details.tty = user_details.tty;
287 	    command_details.argv = argv_out;
288 	    command_details.envp = run_envp;
289 	    command_details.evbase = sudo_event_base;
290 	    if (ISSET(sudo_mode, MODE_LOGIN_SHELL))
291 		SET(command_details.flags, CD_LOGIN_SHELL);
292 	    if (ISSET(sudo_mode, MODE_BACKGROUND))
293 		SET(command_details.flags, CD_BACKGROUND);
294 	    if (ISSET(command_details.flags, CD_SUDOEDIT)) {
295 		status = sudo_edit(&command_details);
296 	    } else {
297 		status = run_command(&command_details);
298 	    }
299 	    /* The close method was called by sudo_edit/run_command. */
300 	    break;
301 	default:
302 	    sudo_fatalx(U_("unexpected sudo mode 0x%x"), sudo_mode);
303     }
304 
305     /*
306      * If the command was terminated by a signal, sudo needs to terminated
307      * the same way.  Otherwise, the shell may ignore a keyboard-generated
308      * signal.  However, we want to avoid having sudo dump core itself.
309      */
310     if (WIFSIGNALED(status)) {
311 	struct sigaction sa;
312 
313 	if (WCOREDUMP(status))
314 	    disable_coredump();
315 
316 	memset(&sa, 0, sizeof(sa));
317 	sigemptyset(&sa.sa_mask);
318 	sa.sa_handler = SIG_DFL;
319 	sigaction(WTERMSIG(status), &sa, NULL);
320 	sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys,
321 	    WTERMSIG(status) | 128);
322 	kill(getpid(), WTERMSIG(status));
323     }
324     sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys,
325 	WEXITSTATUS(status));
326     exit(WEXITSTATUS(status));
327 
328 access_denied:
329     /* Policy/approval failure, close policy and audit plugins before exit. */
330     if (policy_plugin.u.policy->version >= SUDO_API_MKVERSION(1, 15))
331 	policy_close(0, EACCES);
332     audit_close(SUDO_PLUGIN_NO_STATUS, 0);
333     sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys,
334 	EXIT_FAILURE);
335     exit(EXIT_FAILURE);
336 }
337 
338 int
os_init_common(int argc,char * argv[],char * envp[])339 os_init_common(int argc, char *argv[], char *envp[])
340 {
341 #ifdef STATIC_SUDOERS_PLUGIN
342     preload_static_symbols();
343 #endif
344     gc_init();
345     return 0;
346 }
347 
348 /*
349  * Ensure that stdin, stdout and stderr are open; set to /dev/null if not.
350  * Some operating systems do this automatically in the kernel or libc.
351  */
352 static void
fix_fds(void)353 fix_fds(void)
354 {
355     int miss[3], devnull = -1;
356     debug_decl(fix_fds, SUDO_DEBUG_UTIL);
357 
358     /*
359      * stdin, stdout and stderr must be open; set them to /dev/null
360      * if they are closed.
361      */
362     miss[STDIN_FILENO] = fcntl(STDIN_FILENO, F_GETFL, 0) == -1;
363     miss[STDOUT_FILENO] = fcntl(STDOUT_FILENO, F_GETFL, 0) == -1;
364     miss[STDERR_FILENO] = fcntl(STDERR_FILENO, F_GETFL, 0) == -1;
365     if (miss[STDIN_FILENO] || miss[STDOUT_FILENO] || miss[STDERR_FILENO]) {
366 	devnull = open(_PATH_DEVNULL, O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
367 	if (devnull == -1)
368 	    sudo_fatal(U_("unable to open %s"), _PATH_DEVNULL);
369 	if (miss[STDIN_FILENO] && dup2(devnull, STDIN_FILENO) == -1)
370 	    sudo_fatal("dup2");
371 	if (miss[STDOUT_FILENO] && dup2(devnull, STDOUT_FILENO) == -1)
372 	    sudo_fatal("dup2");
373 	if (miss[STDERR_FILENO] && dup2(devnull, STDERR_FILENO) == -1)
374 	    sudo_fatal("dup2");
375 	if (devnull > STDERR_FILENO)
376 	    close(devnull);
377     }
378     debug_return;
379 }
380 
381 /*
382  * Allocate space for groups and fill in using sudo_getgrouplist2()
383  * for when we cannot (or don't want to) use getgroups().
384  * Returns 0 on success and -1 on failure.
385  */
386 static int
fill_group_list(const char * user,struct sudo_cred * cred)387 fill_group_list(const char *user, struct sudo_cred *cred)
388 {
389     int ret = -1;
390     debug_decl(fill_group_list, SUDO_DEBUG_UTIL);
391 
392     /*
393      * If user specified a max number of groups, use it, otherwise let
394      * sudo_getgrouplist2() allocate the group vector.
395      */
396     cred->ngroups = sudo_conf_max_groups();
397     if (cred->ngroups > 0) {
398 	cred->groups = reallocarray(NULL, cred->ngroups, sizeof(GETGROUPS_T));
399 	if (cred->groups != NULL) {
400 	    /* Clamp to max_groups if insufficient space for all groups. */
401 	    if (sudo_getgrouplist2(user, cred->gid, &cred->groups,
402 		    &cred->ngroups) == -1) {
403 		cred->ngroups = sudo_conf_max_groups();
404 	    }
405 	    ret = 0;
406 	}
407     } else {
408 	cred->groups = NULL;
409 	ret = sudo_getgrouplist2(user, cred->gid, &cred->groups,
410 	    &cred->ngroups);
411     }
412     if (ret == -1) {
413 	sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO,
414 	    "%s: %s: unable to get groups via sudo_getgrouplist2()",
415 	    __func__, user);
416     } else {
417 	sudo_debug_printf(SUDO_DEBUG_INFO,
418 	    "%s: %s: got %d groups via sudo_getgrouplist2()",
419 	    __func__, user, cred->ngroups);
420     }
421     debug_return_int(ret);
422 }
423 
424 static char *
get_user_groups(const char * user,struct sudo_cred * cred)425 get_user_groups(const char *user, struct sudo_cred *cred)
426 {
427     char *cp, *gid_list = NULL;
428     size_t glsize;
429     int i, len, group_source;
430     debug_decl(get_user_groups, SUDO_DEBUG_UTIL);
431 
432     cred->groups = NULL;
433     group_source = sudo_conf_group_source();
434     if (group_source != GROUP_SOURCE_DYNAMIC) {
435 	int maxgroups = (int)sysconf(_SC_NGROUPS_MAX);
436 	if (maxgroups < 0)
437 	    maxgroups = NGROUPS_MAX;
438 
439 	/* Note that macOS may return ngroups > NGROUPS_MAX. */
440 	cred->ngroups = getgroups(0, NULL); // -V575
441 	if (cred->ngroups > 0) {
442 	    /* Use groups from kernel if not at limit or source is static. */
443 	    if (cred->ngroups != maxgroups || group_source == GROUP_SOURCE_STATIC) {
444 		cred->groups = reallocarray(NULL, cred->ngroups, sizeof(GETGROUPS_T));
445 		if (cred->groups == NULL)
446 		    goto done;
447 		cred->ngroups = getgroups(cred->ngroups, cred->groups);
448 		if (cred->ngroups < 0) {
449 		    sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO,
450 			"%s: unable to get %d groups via getgroups()",
451 			__func__, cred->ngroups);
452 		    free(cred->groups);
453 		    cred->groups = NULL;
454 		} else {
455 		    sudo_debug_printf(SUDO_DEBUG_INFO,
456 			"%s: got %d groups via getgroups()",
457 			__func__, cred->ngroups);
458 		}
459 	    }
460 	}
461     }
462     if (cred->groups == NULL) {
463 	/*
464 	 * Query group database if kernel list is too small or disabled.
465 	 * Typically, this is because NFS can only support up to 16 groups.
466 	 */
467 	if (fill_group_list(user, cred) == -1)
468 	    goto done;
469     }
470 
471     /*
472      * Format group list as a comma-separated string of gids.
473      */
474     glsize = sizeof("groups=") - 1 + (cred->ngroups * (MAX_UID_T_LEN + 1));
475     if ((gid_list = malloc(glsize)) == NULL)
476 	goto done;
477     memcpy(gid_list, "groups=", sizeof("groups=") - 1);
478     cp = gid_list + sizeof("groups=") - 1;
479     for (i = 0; i < cred->ngroups; i++) {
480 	len = snprintf(cp, glsize - (cp - gid_list), "%s%u",
481 	    i ? "," : "", (unsigned int)cred->groups[i]);
482 	if (len < 0 || (size_t)len >= glsize - (cp - gid_list))
483 	    sudo_fatalx(U_("internal error, %s overflow"), __func__);
484 	cp += len;
485     }
486 done:
487     debug_return_str(gid_list);
488 }
489 
490 /*
491  * Return user information as an array of name=value pairs.
492  * and fill in struct user_details (which shares the same strings).
493  */
494 static char **
get_user_info(struct user_details * ud)495 get_user_info(struct user_details *ud)
496 {
497     char *cp, **info, path[PATH_MAX];
498     size_t info_max = 32 + RLIM_NLIMITS;
499     unsigned int i = 0;
500     mode_t mask;
501     struct passwd *pw;
502     int fd, n;
503     debug_decl(get_user_info, SUDO_DEBUG_UTIL);
504 
505     /*
506      * On BSD systems you can set a hint to keep the password and
507      * group databases open instead of having to open and close
508      * them all the time.  Since sudo does a lot of password and
509      * group lookups, keeping the file open can speed things up.
510      */
511 #ifdef HAVE_SETPASSENT
512     setpassent(1);
513 #endif /* HAVE_SETPASSENT */
514 #ifdef HAVE_SETGROUPENT
515     setgroupent(1);
516 #endif /* HAVE_SETGROUPENT */
517 
518     memset(ud, 0, sizeof(*ud));
519 
520     /* XXX - bound check number of entries */
521     info = reallocarray(NULL, info_max, sizeof(char *));
522     if (info == NULL)
523 	goto oom;
524 
525     ud->pid = getpid();
526     ud->ppid = getppid();
527     ud->pgid = getpgid(0);
528     fd = open(_PATH_TTY, O_RDWR);
529     if (fd != -1) {
530 	if ((ud->tcpgid = tcgetpgrp(fd)) == -1)
531 	    ud->tcpgid = 0;
532 	close(fd);
533     }
534     if ((ud->sid = getsid(0)) == -1)
535 	ud->sid = 0;
536 
537     ud->cred.uid = getuid();
538     ud->cred.euid = geteuid();
539     ud->cred.gid = getgid();
540     ud->cred.egid = getegid();
541 
542 #ifdef HAVE_SETAUTHDB
543     aix_setauthdb(IDtouser(ud->cred.uid), NULL);
544 #endif
545     pw = getpwuid(ud->cred.uid);
546 #ifdef HAVE_SETAUTHDB
547     aix_restoreauthdb();
548 #endif
549     if (pw == NULL)
550 	sudo_fatalx(U_("you do not exist in the %s database"), "passwd");
551 
552     info[i] = sudo_new_key_val("user", pw->pw_name);
553     if (info[i] == NULL)
554 	goto oom;
555     ud->username = info[i] + sizeof("user=") - 1;
556 
557     /* Stash user's shell for use with the -s flag; don't pass to plugin. */
558     if ((ud->shell = getenv("SHELL")) == NULL || ud->shell[0] == '\0') {
559 	ud->shell = pw->pw_shell[0] ? pw->pw_shell : _PATH_SUDO_BSHELL;
560     }
561     if ((ud->shell = strdup(ud->shell)) == NULL)
562 	goto oom;
563 
564     if (asprintf(&info[++i], "pid=%d", (int)ud->pid) == -1)
565 	goto oom;
566     if (asprintf(&info[++i], "ppid=%d", (int)ud->ppid) == -1)
567 	goto oom;
568     if (asprintf(&info[++i], "pgid=%d", (int)ud->pgid) == -1)
569 	goto oom;
570     if (asprintf(&info[++i], "tcpgid=%d", (int)ud->tcpgid) == -1)
571 	goto oom;
572     if (asprintf(&info[++i], "sid=%d", (int)ud->sid) == -1)
573 	goto oom;
574     if (asprintf(&info[++i], "uid=%u", (unsigned int)ud->cred.uid) == -1)
575 	goto oom;
576     if (asprintf(&info[++i], "euid=%u", (unsigned int)ud->cred.euid) == -1)
577 	goto oom;
578     if (asprintf(&info[++i], "gid=%u", (unsigned int)ud->cred.gid) == -1)
579 	goto oom;
580     if (asprintf(&info[++i], "egid=%u", (unsigned int)ud->cred.egid) == -1)
581 	goto oom;
582 
583     if ((cp = get_user_groups(ud->username, &ud->cred)) == NULL)
584 	goto oom;
585     info[++i] = cp;
586 
587     mask = umask(0);
588     umask(mask);
589     if (asprintf(&info[++i], "umask=0%o", (unsigned int)mask) == -1)
590 	goto oom;
591 
592     if (getcwd(path, sizeof(path)) != NULL) {
593 	info[++i] = sudo_new_key_val("cwd", path);
594 	if (info[i] == NULL)
595 	    goto oom;
596 	ud->cwd = info[i] + sizeof("cwd=") - 1;
597     }
598 
599     if (get_process_ttyname(path, sizeof(path)) != NULL) {
600 	info[++i] = sudo_new_key_val("tty", path);
601 	if (info[i] == NULL)
602 	    goto oom;
603 	ud->tty = info[i] + sizeof("tty=") - 1;
604     } else {
605 	/* tty may not always be present */
606 	if (errno != ENOENT)
607 	    sudo_warn("%s", U_("unable to determine tty"));
608     }
609 
610     cp = sudo_gethostname();
611     info[++i] = sudo_new_key_val("host", cp ? cp : "localhost");
612     free(cp);
613     if (info[i] == NULL)
614 	goto oom;
615     ud->host = info[i] + sizeof("host=") - 1;
616 
617     sudo_get_ttysize(&ud->ts_rows, &ud->ts_cols);
618     if (asprintf(&info[++i], "lines=%d", ud->ts_rows) == -1)
619 	goto oom;
620     if (asprintf(&info[++i], "cols=%d", ud->ts_cols) == -1)
621 	goto oom;
622 
623     n = serialize_limits(&info[i + 1], info_max - (i + 1));
624     if (n == -1)
625 	goto oom;
626     i += n;
627 
628     info[++i] = NULL;
629 
630     /* Add to list of vectors to be garbage collected at exit. */
631     if (!gc_add(GC_VECTOR, info))
632 	goto bad;
633 
634     debug_return_ptr(info);
635 oom:
636     sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
637 bad:
638     while (i--)
639 	free(info[i]);
640     free(info);
641     debug_return_ptr(NULL);
642 }
643 
644 /*
645  * Convert a command_info array into a command_details structure.
646  */
647 static void
command_info_to_details(char * const info[],struct command_details * details)648 command_info_to_details(char * const info[], struct command_details *details)
649 {
650     int i;
651     id_t id;
652     char *cp;
653     const char *errstr;
654     debug_decl(command_info_to_details, SUDO_DEBUG_PCOMM);
655 
656     memset(details, 0, sizeof(*details));
657     details->info = info;
658     details->closefrom = -1;
659     details->execfd = -1;
660     details->flags = CD_SUDOEDIT_CHECKDIR | CD_SET_GROUPS;
661     TAILQ_INIT(&details->preserved_fds);
662 
663 #define SET_STRING(s, n) \
664     if (strncmp(s, info[i], sizeof(s) - 1) == 0 && info[i][sizeof(s) - 1]) { \
665 	details->n = info[i] + sizeof(s) - 1; \
666 	break; \
667     }
668 #define SET_FLAG(s, n) \
669     if (strncmp(s, info[i], sizeof(s) - 1) == 0) { \
670 	switch (sudo_strtobool(info[i] + sizeof(s) - 1)) { \
671 	    case true: \
672 		SET(details->flags, n); \
673 		break; \
674 	    case false: \
675 		CLR(details->flags, n); \
676 		break; \
677 	    default: \
678 		sudo_debug_printf(SUDO_DEBUG_ERROR, \
679 		    "invalid boolean value for %s", info[i]); \
680 		break; \
681 	} \
682 	break; \
683     }
684 
685     sudo_debug_printf(SUDO_DEBUG_INFO, "command info from plugin:");
686     for (i = 0; info[i] != NULL; i++) {
687 	sudo_debug_printf(SUDO_DEBUG_INFO, "    %d: %s", i, info[i]);
688 	switch (info[i][0]) {
689 	    case 'c':
690 		SET_STRING("chroot=", chroot)
691 		SET_STRING("command=", command)
692 		SET_STRING("cwd=", cwd)
693 		if (strncmp("cwd_optional=", info[i], sizeof("cwd_optional=") - 1) == 0) {
694 		    cp = info[i] + sizeof("cwd_optional=") - 1;
695 		    details->cwd_optional = sudo_strtobool(cp);
696 		    if (details->cwd_optional == -1) {
697 			errno = EINVAL;
698 			sudo_fatal("%s", info[i]);
699 		    }
700 		    break;
701 		}
702 		if (strncmp("closefrom=", info[i], sizeof("closefrom=") - 1) == 0) {
703 		    cp = info[i] + sizeof("closefrom=") - 1;
704 		    details->closefrom = sudo_strtonum(cp, 0, INT_MAX, &errstr);
705 		    if (errstr != NULL)
706 			sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
707 		    break;
708 		}
709 		break;
710 	    case 'e':
711 		SET_FLAG("exec_background=", CD_EXEC_BG)
712 		if (strncmp("execfd=", info[i], sizeof("execfd=") - 1) == 0) {
713 		    cp = info[i] + sizeof("execfd=") - 1;
714 		    details->execfd = sudo_strtonum(cp, 0, INT_MAX, &errstr);
715 		    if (errstr != NULL)
716 			sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
717 #ifdef HAVE_FEXECVE
718 		    /* Must keep fd open during exec. */
719 		    add_preserved_fd(&details->preserved_fds, details->execfd);
720 #else
721 		    /* Plugin thinks we support fexecve() but we don't. */
722 		    (void)fcntl(details->execfd, F_SETFD, FD_CLOEXEC);
723 		    details->execfd = -1;
724 #endif
725 		    break;
726 		}
727 		break;
728 	    case 'i':
729 		SET_FLAG("intercept=", CD_INTERCEPT)
730 		break;
731 	    case 'l':
732 		SET_STRING("login_class=", login_class)
733 		SET_FLAG("log_subcmds=", CD_LOG_SUBCMDS)
734 		break;
735 	    case 'n':
736 		if (strncmp("nice=", info[i], sizeof("nice=") - 1) == 0) {
737 		    cp = info[i] + sizeof("nice=") - 1;
738 		    details->priority = sudo_strtonum(cp, INT_MIN, INT_MAX,
739 			&errstr);
740 		    if (errstr != NULL)
741 			sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
742 		    SET(details->flags, CD_SET_PRIORITY);
743 		    break;
744 		}
745 		SET_FLAG("noexec=", CD_NOEXEC)
746 		break;
747 	    case 'p':
748 		SET_FLAG("preserve_groups=", CD_PRESERVE_GROUPS)
749 		if (strncmp("preserve_fds=", info[i], sizeof("preserve_fds=") - 1) == 0) {
750 		    parse_preserved_fds(&details->preserved_fds,
751 			info[i] + sizeof("preserve_fds=") - 1);
752 		    break;
753 		}
754 		break;
755 	    case 'r':
756 		if (strncmp("runas_egid=", info[i], sizeof("runas_egid=") - 1) == 0) {
757 		    cp = info[i] + sizeof("runas_egid=") - 1;
758 		    id = sudo_strtoid(cp, &errstr);
759 		    if (errstr != NULL)
760 			sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
761 		    details->cred.egid = (gid_t)id;
762 		    SET(details->flags, CD_SET_EGID);
763 		    break;
764 		}
765 		if (strncmp("runas_euid=", info[i], sizeof("runas_euid=") - 1) == 0) {
766 		    cp = info[i] + sizeof("runas_euid=") - 1;
767 		    id = sudo_strtoid(cp, &errstr);
768 		    if (errstr != NULL)
769 			sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
770 		    details->cred.euid = (uid_t)id;
771 		    SET(details->flags, CD_SET_EUID);
772 		    break;
773 		}
774 		if (strncmp("runas_gid=", info[i], sizeof("runas_gid=") - 1) == 0) {
775 		    cp = info[i] + sizeof("runas_gid=") - 1;
776 		    id = sudo_strtoid(cp, &errstr);
777 		    if (errstr != NULL)
778 			sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
779 		    details->cred.gid = (gid_t)id;
780 		    SET(details->flags, CD_SET_GID);
781 		    break;
782 		}
783 		if (strncmp("runas_groups=", info[i], sizeof("runas_groups=") - 1) == 0) {
784 		    cp = info[i] + sizeof("runas_groups=") - 1;
785 		    details->cred.ngroups = sudo_parse_gids(cp, NULL, &details->cred.groups);
786 		    /* sudo_parse_gids() will print a warning on error. */
787 		    if (details->cred.ngroups == -1)
788 			exit(EXIT_FAILURE); /* XXX */
789 		    break;
790 		}
791 		if (strncmp("runas_uid=", info[i], sizeof("runas_uid=") - 1) == 0) {
792 		    cp = info[i] + sizeof("runas_uid=") - 1;
793 		    id = sudo_strtoid(cp, &errstr);
794 		    if (errstr != NULL)
795 			sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
796 		    details->cred.uid = (uid_t)id;
797 		    SET(details->flags, CD_SET_UID);
798 		    break;
799 		}
800 #ifdef HAVE_PRIV_SET
801 		if (strncmp("runas_privs=", info[i], sizeof("runas_privs=") - 1) == 0) {
802                     const char *endp;
803 		    cp = info[i] + sizeof("runas_privs=") - 1;
804 	            if (*cp != '\0') {
805 			details->privs = priv_str_to_set(cp, ",", &endp);
806 			if (details->privs == NULL)
807 			    sudo_warn("invalid runas_privs %s", endp);
808 		    }
809 		    break;
810 		}
811 		if (strncmp("runas_limitprivs=", info[i], sizeof("runas_limitprivs=") - 1) == 0) {
812                     const char *endp;
813 		    cp = info[i] + sizeof("runas_limitprivs=") - 1;
814 	            if (*cp != '\0') {
815 			details->limitprivs = priv_str_to_set(cp, ",", &endp);
816 			if (details->limitprivs == NULL)
817 			    sudo_warn("invalid runas_limitprivs %s", endp);
818 		    }
819 		    break;
820 		}
821 #endif /* HAVE_PRIV_SET */
822 		SET_STRING("runas_user=", runas_user)
823 		break;
824 	    case 's':
825 		SET_STRING("selinux_role=", selinux_role)
826 		SET_STRING("selinux_type=", selinux_type)
827 		SET_FLAG("set_utmp=", CD_SET_UTMP)
828 		SET_FLAG("sudoedit=", CD_SUDOEDIT)
829 		SET_FLAG("sudoedit_checkdir=", CD_SUDOEDIT_CHECKDIR)
830 		SET_FLAG("sudoedit_follow=", CD_SUDOEDIT_FOLLOW)
831 		break;
832 	    case 't':
833 		if (strncmp("timeout=", info[i], sizeof("timeout=") - 1) == 0) {
834 		    cp = info[i] + sizeof("timeout=") - 1;
835 		    details->timeout = sudo_strtonum(cp, 0, INT_MAX, &errstr);
836 		    if (errstr != NULL)
837 			sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
838 		    SET(details->flags, CD_SET_TIMEOUT);
839 		    break;
840 		}
841 		break;
842 	    case 'u':
843 		if (strncmp("umask=", info[i], sizeof("umask=") - 1) == 0) {
844 		    cp = info[i] + sizeof("umask=") - 1;
845 		    details->umask = sudo_strtomode(cp, &errstr);
846 		    if (errstr != NULL)
847 			sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
848 		    SET(details->flags, CD_SET_UMASK);
849 		    break;
850 		}
851 		SET_FLAG("umask_override=", CD_OVERRIDE_UMASK)
852 		SET_FLAG("use_pty=", CD_USE_PTY)
853 		SET_STRING("utmp_user=", utmp_user)
854 		break;
855 	}
856     }
857 
858     if (!ISSET(details->flags, CD_SET_EUID))
859 	details->cred.euid = details->cred.uid;
860     if (!ISSET(details->flags, CD_SET_EGID))
861 	details->cred.egid = details->cred.gid;
862     if (!ISSET(details->flags, CD_SET_UMASK))
863 	CLR(details->flags, CD_OVERRIDE_UMASK);
864 
865 #ifdef HAVE_SETAUTHDB
866     aix_setauthdb(IDtouser(details->cred.euid), NULL);
867 #endif
868     if (details->runas_user != NULL)
869 	details->pw = getpwnam(details->runas_user);
870     if (details->pw == NULL)
871 	details->pw = getpwuid(details->cred.euid);
872 #ifdef HAVE_SETAUTHDB
873     aix_restoreauthdb();
874 #endif
875     if (details->pw != NULL && (details->pw = pw_dup(details->pw)) == NULL)
876 	sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
877 
878 #ifdef HAVE_SELINUX
879     if (details->selinux_role != NULL && is_selinux_enabled() > 0)
880 	SET(details->flags, CD_RBAC_ENABLED);
881 #endif
882     debug_return;
883 }
884 
885 static void
sudo_check_suid(const char * sudo)886 sudo_check_suid(const char *sudo)
887 {
888     char pathbuf[PATH_MAX];
889     struct stat sb;
890     bool qualified;
891     debug_decl(sudo_check_suid, SUDO_DEBUG_PCOMM);
892 
893     if (geteuid() != ROOT_UID) {
894 	/* Search for sudo binary in PATH if not fully qualified. */
895 	qualified = strchr(sudo, '/') != NULL;
896 	if (!qualified) {
897 	    char *path = getenv_unhooked("PATH");
898 	    if (path != NULL) {
899 		const char *cp, *ep;
900 		const char *pathend = path + strlen(path);
901 
902 		for (cp = sudo_strsplit(path, pathend, ":", &ep); cp != NULL;
903 		    cp = sudo_strsplit(NULL, pathend, ":", &ep)) {
904 
905 		    int len = snprintf(pathbuf, sizeof(pathbuf), "%.*s/%s",
906 			(int)(ep - cp), cp, sudo);
907 		    if (len < 0 || len >= ssizeof(pathbuf))
908 			continue;
909 		    if (access(pathbuf, X_OK) == 0) {
910 			sudo = pathbuf;
911 			qualified = true;
912 			break;
913 		    }
914 		}
915 	    }
916 	}
917 
918 	if (qualified && stat(sudo, &sb) == 0) {
919 	    /* Try to determine why sudo was not running as root. */
920 	    if (sb.st_uid != ROOT_UID || !ISSET(sb.st_mode, S_ISUID)) {
921 		sudo_fatalx(
922 		    U_("%s must be owned by uid %d and have the setuid bit set"),
923 		    sudo, ROOT_UID);
924 	    } else {
925 		sudo_fatalx(U_("effective uid is not %d, is %s on a file system "
926 		    "with the 'nosuid' option set or an NFS file system without"
927 		    " root privileges?"), ROOT_UID, sudo);
928 	    }
929 	} else {
930 	    sudo_fatalx(
931 		U_("effective uid is not %d, is sudo installed setuid root?"),
932 		ROOT_UID);
933 	}
934     }
935     debug_return;
936 }
937 
938 bool
set_user_groups(struct command_details * details)939 set_user_groups(struct command_details *details)
940 {
941     bool ret = false;
942     debug_decl(set_user_groups, SUDO_DEBUG_EXEC);
943 
944     if (!ISSET(details->flags, CD_PRESERVE_GROUPS)) {
945 	if (details->cred.ngroups >= 0) {
946 	    if (sudo_setgroups(details->cred.ngroups, details->cred.groups) < 0) {
947 		sudo_warn("%s", U_("unable to set supplementary group IDs"));
948 		goto done;
949 	    }
950 	}
951     }
952 #ifdef HAVE_SETEUID
953     if (ISSET(details->flags, CD_SET_EGID) && setegid(details->cred.egid)) {
954 	sudo_warn(U_("unable to set effective gid to runas gid %u"),
955 	    (unsigned int)details->cred.egid);
956 	goto done;
957     }
958 #endif
959     if (ISSET(details->flags, CD_SET_GID) && setgid(details->cred.gid)) {
960 	sudo_warn(U_("unable to set gid to runas gid %u"),
961 	    (unsigned int)details->cred.gid);
962 	goto done;
963     }
964     ret = true;
965 
966 done:
967     CLR(details->flags, CD_SET_GROUPS);
968     debug_return_bool(ret);
969 }
970 
971 /*
972  * Run the command and wait for it to complete.
973  * Returns wait status suitable for use with the wait(2) macros.
974  */
975 int
run_command(struct command_details * details)976 run_command(struct command_details *details)
977 {
978     struct command_status cstat;
979     int status = W_EXITCODE(1, 0);
980     debug_decl(run_command, SUDO_DEBUG_EXEC);
981 
982     cstat.type = CMD_INVALID;
983     cstat.val = 0;
984 
985     sudo_execute(details, &cstat);
986 
987     switch (cstat.type) {
988     case CMD_ERRNO:
989 	/* exec_setup() or execve() returned an error. */
990 	iolog_close(0, cstat.val);
991 	policy_close(0, cstat.val);
992 	audit_close(SUDO_PLUGIN_EXEC_ERROR, cstat.val);
993 	break;
994     case CMD_WSTATUS:
995 	/* Command ran, exited or was killed. */
996 	status = cstat.val;
997 	iolog_close(status, 0);
998 	policy_close(status, 0);
999 	audit_close(SUDO_PLUGIN_WAIT_STATUS, cstat.val);
1000 	break;
1001     default:
1002 	/* TODO: handle front end error conditions. */
1003 	sudo_warnx(U_("unexpected child termination condition: %d"), cstat.type);
1004 	break;
1005     }
1006     debug_return_int(status);
1007 }
1008 
1009 /*
1010  * Format struct sudo_settings as name=value pairs for the plugin
1011  * to consume.  Returns a NULL-terminated plugin-style array of pairs.
1012  */
1013 static char **
format_plugin_settings(struct plugin_container * plugin)1014 format_plugin_settings(struct plugin_container *plugin)
1015 {
1016     size_t plugin_settings_size;
1017     struct sudo_debug_file *debug_file;
1018     struct sudo_settings *setting;
1019     char **plugin_settings;
1020     unsigned int i = 0;
1021     debug_decl(format_plugin_settings, SUDO_DEBUG_PCOMM);
1022 
1023     /* Determine sudo_settings array size (including plugin_path and NULL) */
1024     plugin_settings_size = 2;
1025     for (setting = sudo_settings; setting->name != NULL; setting++)
1026 	plugin_settings_size++;
1027     if (plugin->debug_files != NULL) {
1028 	TAILQ_FOREACH(debug_file, plugin->debug_files, entries)
1029 	    plugin_settings_size++;
1030     }
1031 
1032     /* Allocate and fill in. */
1033     plugin_settings = reallocarray(NULL, plugin_settings_size, sizeof(char *));
1034     if (plugin_settings == NULL)
1035 	goto bad;
1036     plugin_settings[i] = sudo_new_key_val("plugin_path", plugin->path);
1037     if (plugin_settings[i] == NULL)
1038 	goto bad;
1039     for (setting = sudo_settings; setting->name != NULL; setting++) {
1040         if (setting->value != NULL) {
1041             sudo_debug_printf(SUDO_DEBUG_INFO, "settings: %s=%s",
1042                 setting->name, setting->value);
1043 	    plugin_settings[++i] =
1044 		sudo_new_key_val(setting->name, setting->value);
1045 	    if (plugin_settings[i] == NULL)
1046 		goto bad;
1047         }
1048     }
1049     if (plugin->debug_files != NULL) {
1050 	TAILQ_FOREACH(debug_file, plugin->debug_files, entries) {
1051 	    /* XXX - quote filename? */
1052 	    if (asprintf(&plugin_settings[++i], "debug_flags=%s %s",
1053 		debug_file->debug_file, debug_file->debug_flags) == -1)
1054 		goto bad;
1055 	}
1056     }
1057     plugin_settings[++i] = NULL;
1058 
1059     /* Add to list of vectors to be garbage collected at exit. */
1060     if (!gc_add(GC_VECTOR, plugin_settings))
1061 	sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
1062 
1063     debug_return_ptr(plugin_settings);
1064 bad:
1065     while (i--)
1066 	free(plugin_settings[i]);
1067     free(plugin_settings);
1068     debug_return_ptr(NULL);
1069 }
1070 
1071 static void
policy_open(void)1072 policy_open(void)
1073 {
1074     char **plugin_settings;
1075     const char *errstr = NULL;
1076     int ok;
1077     debug_decl(policy_open, SUDO_DEBUG_PCOMM);
1078 
1079     /* Convert struct sudo_settings to plugin_settings[] */
1080     plugin_settings = format_plugin_settings(&policy_plugin);
1081     if (plugin_settings == NULL)
1082 	sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
1083 
1084     /*
1085      * Backward compatibility for older API versions
1086      */
1087     sudo_debug_set_active_instance(SUDO_DEBUG_INSTANCE_INITIALIZER);
1088     switch (policy_plugin.u.generic->version) {
1089     case SUDO_API_MKVERSION(1, 0):
1090     case SUDO_API_MKVERSION(1, 1):
1091 	ok = policy_plugin.u.policy_1_0->open(policy_plugin.u.io_1_0->version,
1092 	    sudo_conversation_1_7, sudo_conversation_printf, plugin_settings,
1093 	    user_info, submit_envp);
1094 	break;
1095     default:
1096 	ok = policy_plugin.u.policy->open(SUDO_API_VERSION, sudo_conversation,
1097 	    sudo_conversation_printf, plugin_settings, user_info, submit_envp,
1098 	    policy_plugin.options, &errstr);
1099     }
1100 
1101     /* Stash plugin debug instance ID if set in open() function. */
1102     policy_plugin.debug_instance = sudo_debug_get_active_instance();
1103     sudo_debug_set_active_instance(sudo_debug_instance);
1104 
1105     if (ok != 1) {
1106 	if (ok == -2)
1107 	    usage();
1108 	else {
1109 	    /* XXX - audit */
1110 	    sudo_fatalx("%s", U_("unable to initialize policy plugin"));
1111 	}
1112     }
1113 
1114     debug_return;
1115 }
1116 
1117 static void
policy_close(int exit_status,int error_code)1118 policy_close(int exit_status, int error_code)
1119 {
1120     debug_decl(policy_close, SUDO_DEBUG_PCOMM);
1121 
1122     if (error_code != 0) {
1123 	sudo_debug_printf(SUDO_DEBUG_DEBUG,
1124 	    "%s: calling policy close with errno %d",
1125 	    policy_plugin.name, error_code);
1126     } else {
1127 	sudo_debug_printf(SUDO_DEBUG_DEBUG,
1128 	    "%s: calling policy close with wait status %d",
1129 	    policy_plugin.name, exit_status);
1130     }
1131     if (policy_plugin.u.policy->close != NULL) {
1132 	sudo_debug_set_active_instance(policy_plugin.debug_instance);
1133 	policy_plugin.u.policy->close(exit_status, error_code);
1134 	sudo_debug_set_active_instance(sudo_debug_instance);
1135     } else if (error_code != 0) {
1136 	if (command_details.command != NULL) {
1137 	    errno = error_code;
1138 	    sudo_warn(U_("unable to execute %s"), command_details.command);
1139 	}
1140     }
1141 
1142     debug_return;
1143 }
1144 
1145 static int
policy_show_version(int verbose)1146 policy_show_version(int verbose)
1147 {
1148     int ret = true;
1149     debug_decl(policy_show_version, SUDO_DEBUG_PCOMM);
1150 
1151     sudo_debug_set_active_instance(policy_plugin.debug_instance);
1152     if (policy_plugin.u.policy->show_version != NULL)
1153 	ret = policy_plugin.u.policy->show_version(verbose);
1154     if (policy_plugin.u.policy->version >= SUDO_API_MKVERSION(1, 15)) {
1155 	if (policy_plugin.u.policy->close != NULL)
1156 	    policy_plugin.u.policy->close(0, 0);
1157     }
1158     sudo_debug_set_active_instance(sudo_debug_instance);
1159 
1160     debug_return_int(ret);
1161 }
1162 
1163 static bool
policy_check(int argc,char * const argv[],char * env_add[],char ** command_info[],char ** run_argv[],char ** run_envp[])1164 policy_check(int argc, char * const argv[], char *env_add[],
1165     char **command_info[], char **run_argv[], char **run_envp[])
1166 {
1167     const char *errstr = NULL;
1168     int ok;
1169     debug_decl(policy_check, SUDO_DEBUG_PCOMM);
1170 
1171     if (policy_plugin.u.policy->check_policy == NULL) {
1172 	sudo_fatalx(U_("policy plugin %s is missing the \"check_policy\" method"),
1173 	    policy_plugin.name);
1174     }
1175     sudo_debug_set_active_instance(policy_plugin.debug_instance);
1176     ok = policy_plugin.u.policy->check_policy(argc, argv, env_add,
1177 	command_info, run_argv, run_envp, &errstr);
1178     sudo_debug_set_active_instance(sudo_debug_instance);
1179     sudo_debug_printf(SUDO_DEBUG_INFO, "policy plugin returns %d (%s)",
1180 	ok, errstr ? errstr : "");
1181 
1182     /* On success, the close method will be called by sudo_edit/run_command. */
1183     if (ok != 1) {
1184 	switch (ok) {
1185 	case 0:
1186 	    audit_reject(policy_plugin.name, SUDO_POLICY_PLUGIN,
1187 		errstr ? errstr : _("command rejected by policy"),
1188 		*command_info);
1189 	    break;
1190 	case -1:
1191 	    audit_error(policy_plugin.name, SUDO_POLICY_PLUGIN,
1192 		errstr ? errstr : _("policy plugin error"),
1193 		*command_info);
1194 	    break;
1195 	case -2:
1196 	    usage();
1197 	    break;
1198 	}
1199 	debug_return_bool(false);
1200     }
1201     debug_return_bool(audit_accept(policy_plugin.name, SUDO_POLICY_PLUGIN,
1202 	*command_info, *run_argv, *run_envp));
1203 }
1204 
1205 static void
policy_list(int argc,char * const argv[],int verbose,const char * user)1206 policy_list(int argc, char * const argv[], int verbose, const char *user)
1207 {
1208     const char *errstr = NULL;
1209     /* TODO: add list_user */
1210     char * const command_info[] = {
1211 	"command=list",
1212 	NULL
1213     };
1214     int ok;
1215     debug_decl(policy_list, SUDO_DEBUG_PCOMM);
1216 
1217     if (policy_plugin.u.policy->list == NULL) {
1218 	sudo_fatalx(U_("policy plugin %s does not support listing privileges"),
1219 	    policy_plugin.name);
1220     }
1221     sudo_debug_set_active_instance(policy_plugin.debug_instance);
1222     ok = policy_plugin.u.policy->list(argc, argv, verbose, user, &errstr);
1223     sudo_debug_set_active_instance(sudo_debug_instance);
1224 
1225     switch (ok) {
1226     case 1:
1227 	audit_accept(policy_plugin.name, SUDO_POLICY_PLUGIN,
1228 	    command_info, argv, submit_envp);
1229 	break;
1230     case 0:
1231 	audit_reject(policy_plugin.name, SUDO_POLICY_PLUGIN,
1232 	    errstr ? errstr : _("command rejected by policy"),
1233 	    command_info);
1234 	break;
1235     default:
1236 	audit_error(policy_plugin.name, SUDO_POLICY_PLUGIN,
1237 	    errstr ? errstr : _("policy plugin error"),
1238 	    command_info);
1239 	break;
1240     }
1241 
1242     /* Policy must be closed after auditing to avoid use after free. */
1243     if (policy_plugin.u.policy->version >= SUDO_API_MKVERSION(1, 15))
1244 	policy_close(0, 0);
1245     audit_close(SUDO_PLUGIN_NO_STATUS, 0);
1246 
1247     exit(ok != 1);
1248 }
1249 
1250 static void
policy_validate(char * const argv[])1251 policy_validate(char * const argv[])
1252 {
1253     const char *errstr = NULL;
1254     char * const command_info[] = {
1255 	"command=validate",
1256 	NULL
1257     };
1258     int ok = 0;
1259     debug_decl(policy_validate, SUDO_DEBUG_PCOMM);
1260 
1261     if (policy_plugin.u.policy->validate == NULL) {
1262 	sudo_fatalx(U_("policy plugin %s does not support the -v option"),
1263 	    policy_plugin.name);
1264     }
1265     sudo_debug_set_active_instance(policy_plugin.debug_instance);
1266     ok = policy_plugin.u.policy->validate(&errstr);
1267     sudo_debug_set_active_instance(sudo_debug_instance);
1268 
1269     switch (ok) {
1270     case 1:
1271 	audit_accept(policy_plugin.name, SUDO_POLICY_PLUGIN, command_info,
1272 	    argv, submit_envp);
1273 	break;
1274     case 0:
1275 	audit_reject(policy_plugin.name, SUDO_POLICY_PLUGIN,
1276 	    errstr ? errstr : _("command rejected by policy"),
1277 	    command_info);
1278 	break;
1279     default:
1280 	audit_error(policy_plugin.name, SUDO_POLICY_PLUGIN,
1281 	    errstr ? errstr : _("policy plugin error"),
1282 	    command_info);
1283 	break;
1284     }
1285 
1286     /* Policy must be closed after auditing to avoid use after free. */
1287     if (policy_plugin.u.policy->version >= SUDO_API_MKVERSION(1, 15))
1288 	policy_close(0, 0);
1289     audit_close(SUDO_PLUGIN_NO_STATUS, 0);
1290 
1291     exit(ok != 1);
1292 }
1293 
1294 static void
policy_invalidate(int unlinkit)1295 policy_invalidate(int unlinkit)
1296 {
1297     debug_decl(policy_invalidate, SUDO_DEBUG_PCOMM);
1298 
1299     if (policy_plugin.u.policy->invalidate == NULL) {
1300 	sudo_fatalx(U_("policy plugin %s does not support the -k/-K options"),
1301 	    policy_plugin.name);
1302     }
1303     sudo_debug_set_active_instance(policy_plugin.debug_instance);
1304     policy_plugin.u.policy->invalidate(unlinkit);
1305     if (policy_plugin.u.policy->version >= SUDO_API_MKVERSION(1, 15)) {
1306 	if (policy_plugin.u.policy->close != NULL)
1307 	    policy_plugin.u.policy->close(0, 0);
1308     }
1309     sudo_debug_set_active_instance(sudo_debug_instance);
1310 
1311     audit_close(SUDO_PLUGIN_NO_STATUS, 0);
1312 
1313     exit(EXIT_SUCCESS);
1314 }
1315 
1316 int
policy_init_session(struct command_details * details)1317 policy_init_session(struct command_details *details)
1318 {
1319     const char *errstr = NULL;
1320     int ret = true;
1321     debug_decl(policy_init_session, SUDO_DEBUG_PCOMM);
1322 
1323     /*
1324      * We set groups, including supplementary group vector,
1325      * as part of the session setup.  This allows for dynamic
1326      * groups to be set via pam_group(8) in pam_setcred(3).
1327      */
1328     if (ISSET(details->flags, CD_SET_GROUPS)) {
1329 	/* set_user_groups() prints error message on failure. */
1330 	if (!set_user_groups(details))
1331 	    goto done;
1332     }
1333 
1334     /* Session setup may override sudoers umask so set it first. */
1335     if (ISSET(details->flags, CD_SET_UMASK))
1336 	(void) umask(details->umask);
1337 
1338     if (policy_plugin.u.policy->init_session) {
1339 	/*
1340 	 * Backward compatibility for older API versions
1341 	 */
1342 	sudo_debug_set_active_instance(policy_plugin.debug_instance);
1343 	switch (policy_plugin.u.generic->version) {
1344 	case SUDO_API_MKVERSION(1, 0):
1345 	case SUDO_API_MKVERSION(1, 1):
1346 	    ret = policy_plugin.u.policy_1_0->init_session(details->pw);
1347 	    break;
1348 	default:
1349 	    ret = policy_plugin.u.policy->init_session(details->pw,
1350 		&details->envp, &errstr);
1351 	}
1352 	sudo_debug_set_active_instance(sudo_debug_instance);
1353 	if (ret != 1) {
1354 	    audit_error(policy_plugin.name, SUDO_POLICY_PLUGIN,
1355 		errstr ? errstr : _("policy plugin error"),
1356 		details->info);
1357 	}
1358     }
1359 done:
1360     debug_return_int(ret);
1361 }
1362 
1363 static int
iolog_open_int(struct plugin_container * plugin,char * const command_info[],int argc,char * const argv[],char * const run_envp[],const char ** errstr)1364 iolog_open_int(struct plugin_container *plugin, char * const command_info[],
1365     int argc, char * const argv[], char * const run_envp[], const char **errstr)
1366 {
1367     char **plugin_settings;
1368     int ret;
1369     debug_decl(iolog_open_int, SUDO_DEBUG_PCOMM);
1370 
1371     /* Convert struct sudo_settings to plugin_settings[] */
1372     plugin_settings = format_plugin_settings(plugin);
1373     if (plugin_settings == NULL) {
1374 	sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
1375 	debug_return_int(-1);
1376     }
1377 
1378     /*
1379      * Backward compatibility for older API versions
1380      */
1381     sudo_debug_set_active_instance(plugin->debug_instance);
1382     switch (plugin->u.generic->version) {
1383     case SUDO_API_MKVERSION(1, 0):
1384 	ret = plugin->u.io_1_0->open(plugin->u.io_1_0->version,
1385 	    sudo_conversation_1_7, sudo_conversation_printf, plugin_settings,
1386 	    user_info, argc, argv, run_envp);
1387 	break;
1388     case SUDO_API_MKVERSION(1, 1):
1389 	ret = plugin->u.io_1_1->open(plugin->u.io_1_1->version,
1390 	    sudo_conversation_1_7, sudo_conversation_printf, plugin_settings,
1391 	    user_info, command_info, argc, argv, run_envp);
1392 	break;
1393     default:
1394 	ret = plugin->u.io->open(SUDO_API_VERSION, sudo_conversation,
1395 	    sudo_conversation_printf, plugin_settings, user_info, command_info,
1396 	    argc, argv, run_envp, plugin->options, errstr);
1397     }
1398 
1399     /* Stash plugin debug instance ID if set in open() function. */
1400     plugin->debug_instance = sudo_debug_get_active_instance();
1401     sudo_debug_set_active_instance(sudo_debug_instance);
1402 
1403     debug_return_int(ret);
1404 }
1405 
1406 static bool
iolog_open(char * const command_info[],int argc,char * const argv[],char * const run_envp[])1407 iolog_open(char * const command_info[], int argc, char * const argv[],
1408     char * const run_envp[])
1409 {
1410     struct plugin_container *plugin, *next;
1411     const char *errstr = NULL;
1412     debug_decl(iolog_open, SUDO_DEBUG_PCOMM);
1413 
1414     TAILQ_FOREACH_SAFE(plugin, &io_plugins, entries, next) {
1415 	int ok = iolog_open_int(plugin, command_info, argc, argv, run_envp,
1416 	    &errstr);
1417 	switch (ok) {
1418 	case 1:
1419 	    break;
1420 	case 0:
1421 	    /* I/O plugin asked to be disabled, remove and free. */
1422 	    unlink_plugin(&io_plugins, plugin);
1423 	    break;
1424 	case -2:
1425 	    usage();
1426 	    break;
1427 	default:
1428 	    sudo_warnx(U_("error initializing I/O plugin %s"),
1429 		plugin->name);
1430 	    audit_error(plugin->name, SUDO_IO_PLUGIN,
1431 		errstr ? errstr : _("error initializing I/O plugin"),
1432 		command_info);
1433 	    debug_return_bool(false);
1434 	}
1435     }
1436 
1437     debug_return_bool(true);
1438 }
1439 
1440 static void
iolog_close(int exit_status,int error_code)1441 iolog_close(int exit_status, int error_code)
1442 {
1443     struct plugin_container *plugin;
1444     debug_decl(iolog_close, SUDO_DEBUG_PCOMM);
1445 
1446     TAILQ_FOREACH(plugin, &io_plugins, entries) {
1447 	if (plugin->u.io->close != NULL) {
1448 	    if (error_code != 0) {
1449 		sudo_debug_printf(SUDO_DEBUG_DEBUG,
1450 		    "%s: calling I/O close with errno %d",
1451 		    plugin->name, error_code);
1452 	    } else {
1453 		sudo_debug_printf(SUDO_DEBUG_DEBUG,
1454 		    "%s: calling I/O close with wait status %d",
1455 			plugin->name, exit_status);
1456 	    }
1457 	    sudo_debug_set_active_instance(plugin->debug_instance);
1458 	    plugin->u.io->close(exit_status, error_code);
1459 	    sudo_debug_set_active_instance(sudo_debug_instance);
1460 	}
1461     }
1462 
1463     debug_return;
1464 }
1465 
1466 static void
iolog_show_version(int verbose,int argc,char * const argv[],char * const envp[])1467 iolog_show_version(int verbose, int argc, char * const argv[],
1468     char * const envp[])
1469 {
1470     const char *errstr = NULL;
1471     struct plugin_container *plugin;
1472     debug_decl(iolog_show_version, SUDO_DEBUG_PCOMM);
1473 
1474     TAILQ_FOREACH(plugin, &io_plugins, entries) {
1475 	int ok = iolog_open_int(plugin, NULL, argc, argv, envp, &errstr);
1476 	if (ok != -1) {
1477 	    sudo_debug_set_active_instance(plugin->debug_instance);
1478 	    if (plugin->u.io->show_version != NULL) {
1479 		/* Return value of show_version currently ignored. */
1480 		plugin->u.io->show_version(verbose);
1481 	    }
1482 	    if (plugin->u.io->version >= SUDO_API_MKVERSION(1, 15)) {
1483 		if (plugin->u.io->close != NULL)
1484 		    plugin->u.io->close(0, 0);
1485 	    }
1486 	    sudo_debug_set_active_instance(sudo_debug_instance);
1487 	}
1488     }
1489 
1490     debug_return;
1491 }
1492 
1493 /*
1494  * Remove the specified plugin from the plugins list.
1495  * Deregisters any hooks before unlinking, then frees the container.
1496  */
1497 static void
unlink_plugin(struct plugin_container_list * plugin_list,struct plugin_container * plugin)1498 unlink_plugin(struct plugin_container_list *plugin_list,
1499     struct plugin_container *plugin)
1500 {
1501     void (*deregister_hooks)(int , int (*)(struct sudo_hook *)) = NULL;
1502     debug_decl(unlink_plugin, SUDO_DEBUG_PCOMM);
1503 
1504     /* Deregister hooks, if any. */
1505     if (plugin->u.generic->version >= SUDO_API_MKVERSION(1, 2)) {
1506 	switch (plugin->u.generic->type) {
1507 	case SUDO_IO_PLUGIN:
1508 	    deregister_hooks = plugin->u.io->deregister_hooks;
1509 	    break;
1510 	case SUDO_AUDIT_PLUGIN:
1511 	    deregister_hooks = plugin->u.audit->deregister_hooks;
1512 	    break;
1513 	default:
1514 	    sudo_debug_printf(SUDO_DEBUG_ERROR,
1515 		"%s: unsupported plugin type %d", __func__,
1516 		plugin->u.generic->type);
1517 	    break;
1518 	}
1519     }
1520     if (deregister_hooks != NULL) {
1521 	sudo_debug_set_active_instance(plugin->debug_instance);
1522 	deregister_hooks(SUDO_HOOK_VERSION, deregister_hook);
1523 	sudo_debug_set_active_instance(sudo_debug_instance);
1524     }
1525 
1526     /* Remove from plugin list and free. */
1527     TAILQ_REMOVE(plugin_list, plugin, entries);
1528     free_plugin_container(plugin, true);
1529 
1530     debug_return;
1531 }
1532 
1533 static int
audit_open_int(struct plugin_container * plugin,const char ** errstr)1534 audit_open_int(struct plugin_container *plugin, const char **errstr)
1535 {
1536     char **plugin_settings;
1537     int ret;
1538     debug_decl(audit_open_int, SUDO_DEBUG_PCOMM);
1539 
1540     /* Convert struct sudo_settings to plugin_settings[] */
1541     plugin_settings = format_plugin_settings(plugin);
1542     if (plugin_settings == NULL) {
1543 	sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
1544 	debug_return_int(-1);
1545     }
1546 
1547     sudo_debug_set_active_instance(plugin->debug_instance);
1548     ret = plugin->u.audit->open(SUDO_API_VERSION, sudo_conversation,
1549 	sudo_conversation_printf, plugin_settings, user_info,
1550 	submit_optind, submit_argv, submit_envp, plugin->options, errstr);
1551 
1552     /* Stash plugin debug instance ID if set in open() function. */
1553     plugin->debug_instance = sudo_debug_get_active_instance();
1554     sudo_debug_set_active_instance(sudo_debug_instance);
1555 
1556     debug_return_int(ret);
1557 }
1558 
1559 static void
audit_open(void)1560 audit_open(void)
1561 {
1562     struct plugin_container *plugin, *next;
1563     const char *errstr = NULL;
1564     debug_decl(audit_open, SUDO_DEBUG_PCOMM);
1565 
1566     TAILQ_FOREACH_SAFE(plugin, &audit_plugins, entries, next) {
1567 	int ok = audit_open_int(plugin, &errstr);
1568 	switch (ok) {
1569 	case 1:
1570 	    break;
1571 	case 0:
1572 	    /* Audit plugin asked to be disabled, remove and free. */
1573 	    unlink_plugin(&audit_plugins, plugin);
1574 	    break;
1575 	case -2:
1576 	    usage();
1577 	    break;
1578 	default:
1579 	    /* TODO: pass error message to other audit plugins */
1580 	    sudo_fatalx(U_("error initializing audit plugin %s"),
1581 		plugin->name);
1582 	}
1583     }
1584 
1585     debug_return;
1586 }
1587 
1588 static void
audit_close(int status_type,int status)1589 audit_close(int status_type, int status)
1590 {
1591     struct plugin_container *plugin;
1592     debug_decl(audit_close, SUDO_DEBUG_PCOMM);
1593 
1594     TAILQ_FOREACH(plugin, &audit_plugins, entries) {
1595 	if (plugin->u.audit->close != NULL) {
1596 	    sudo_debug_set_active_instance(plugin->debug_instance);
1597 	    plugin->u.audit->close(status_type, status);
1598 	    sudo_debug_set_active_instance(sudo_debug_instance);
1599 	}
1600     }
1601 
1602     debug_return;
1603 }
1604 
1605 static void
audit_show_version(int verbose)1606 audit_show_version(int verbose)
1607 {
1608     struct plugin_container *plugin;
1609     debug_decl(audit_show_version, SUDO_DEBUG_PCOMM);
1610 
1611     TAILQ_FOREACH(plugin, &audit_plugins, entries) {
1612 	sudo_debug_set_active_instance(plugin->debug_instance);
1613 	if (plugin->u.audit->show_version != NULL) {
1614 	    /* Return value of show_version currently ignored. */
1615 	    plugin->u.audit->show_version(verbose);
1616 	}
1617 	if (plugin->u.audit->close != NULL)
1618 	    plugin->u.audit->close(SUDO_PLUGIN_NO_STATUS, 0);
1619 	sudo_debug_set_active_instance(sudo_debug_instance);
1620     }
1621 
1622     debug_return;
1623 }
1624 
1625 /*
1626  * Error from plugin or front-end.
1627  * The error will not be sent to plugin source, if specified.
1628  */
1629 static bool
audit_error2(struct plugin_container * source,const char * plugin_name,unsigned int plugin_type,const char * audit_msg,char * const command_info[])1630 audit_error2(struct plugin_container *source, const char *plugin_name,
1631     unsigned int plugin_type, const char *audit_msg, char * const command_info[])
1632 {
1633     struct plugin_container *plugin;
1634     const char *errstr = NULL;
1635     bool ret = true;
1636     int ok;
1637     debug_decl(audit_error2, SUDO_DEBUG_PCOMM);
1638 
1639     TAILQ_FOREACH(plugin, &audit_plugins, entries) {
1640 	if (plugin->u.audit->error == NULL)
1641 	    continue;
1642 
1643 	/* Avoid a loop if the audit plugin itself has an error. */
1644 	if (plugin == source)
1645 	    continue;
1646 
1647 	sudo_debug_set_active_instance(plugin->debug_instance);
1648 	ok = plugin->u.audit->error(plugin_name, plugin_type,
1649 	    audit_msg, command_info, &errstr);
1650 	sudo_debug_set_active_instance(sudo_debug_instance);
1651 	if (ok != 1) {
1652 	    /*
1653 	     * Don't propagate the error to other audit plugins.
1654 	     * It is not worth the trouble to avoid potential loops.
1655 	     */
1656 	    sudo_debug_printf(SUDO_DEBUG_ERROR,
1657 		"%s: plugin %s error failed, ret %d", __func__,
1658 		plugin->name, ok);
1659 	    sudo_warnx(U_("%s: unable to log error event%s%s"),
1660 		plugin->name, errstr ? ": " : "", errstr ? errstr : "");
1661 	    ret = false;
1662 	}
1663     }
1664 
1665     debug_return_bool(ret);
1666 }
1667 
1668 /*
1669  * Command accepted by policy.
1670  * See command_info[] for additional info.
1671  * XXX - actual environment may be updated by policy_init_session().
1672  */
1673 bool
audit_accept(const char * plugin_name,unsigned int plugin_type,char * const command_info[],char * const run_argv[],char * const run_envp[])1674 audit_accept(const char *plugin_name, unsigned int plugin_type,
1675     char * const command_info[], char * const run_argv[],
1676     char * const run_envp[])
1677 {
1678     struct plugin_container *plugin;
1679     const char *errstr = NULL;
1680     int ok;
1681     debug_decl(audit_accept, SUDO_DEBUG_PCOMM);
1682 
1683     TAILQ_FOREACH(plugin, &audit_plugins, entries) {
1684 	if (plugin->u.audit->accept == NULL)
1685 	    continue;
1686 
1687 	sudo_debug_set_active_instance(plugin->debug_instance);
1688 	ok = plugin->u.audit->accept(plugin_name, plugin_type,
1689 	    command_info, run_argv, run_envp, &errstr);
1690 	sudo_debug_set_active_instance(sudo_debug_instance);
1691 	if (ok != 1) {
1692 	    sudo_debug_printf(SUDO_DEBUG_ERROR,
1693 		"%s: plugin %s accept failed, ret %d", __func__,
1694 		plugin->name, ok);
1695 	    sudo_warnx(U_("%s: unable to log accept event%s%s"),
1696 		plugin->name, errstr ? ": " : "", errstr ? errstr : "");
1697 
1698 	    /* Notify other audit plugins and return. */
1699 	    audit_error2(plugin, plugin->name, SUDO_AUDIT_PLUGIN,
1700 		errstr ? errstr : _("audit plugin error"), command_info);
1701 	    debug_return_bool(false);
1702 	}
1703     }
1704 
1705     debug_return_bool(true);
1706 }
1707 
1708 /*
1709  * Command rejected by policy or I/O plugin.
1710  */
1711 bool
audit_reject(const char * plugin_name,unsigned int plugin_type,const char * audit_msg,char * const command_info[])1712 audit_reject(const char *plugin_name, unsigned int plugin_type,
1713     const char *audit_msg, char * const command_info[])
1714 {
1715     struct plugin_container *plugin;
1716     const char *errstr = NULL;
1717     bool ret = true;
1718     int ok;
1719     debug_decl(audit_reject, SUDO_DEBUG_PCOMM);
1720 
1721     TAILQ_FOREACH(plugin, &audit_plugins, entries) {
1722 	if (plugin->u.audit->reject == NULL)
1723 	    continue;
1724 
1725 	sudo_debug_set_active_instance(plugin->debug_instance);
1726 	ok = plugin->u.audit->reject(plugin_name, plugin_type,
1727 	    audit_msg, command_info, &errstr);
1728 	sudo_debug_set_active_instance(sudo_debug_instance);
1729 	if (ok != 1) {
1730 	    sudo_debug_printf(SUDO_DEBUG_ERROR,
1731 		"%s: plugin %s reject failed, ret %d", __func__,
1732 		plugin->name, ok);
1733 	    sudo_warnx(U_("%s: unable to log reject event%s%s"),
1734 		plugin->name, errstr ? ": " : "", errstr ? errstr : "");
1735 
1736 	    /* Notify other audit plugins. */
1737 	    audit_error2(plugin, plugin->name, SUDO_AUDIT_PLUGIN,
1738 		errstr ? errstr : _("audit plugin error"), command_info);
1739 
1740 	    ret = false;
1741 	    break;
1742 	}
1743     }
1744 
1745     debug_return_bool(ret);
1746 }
1747 
1748 /*
1749  * Error from plugin or front-end.
1750  */
1751 bool
audit_error(const char * plugin_name,unsigned int plugin_type,const char * audit_msg,char * const command_info[])1752 audit_error(const char *plugin_name, unsigned int plugin_type,
1753     const char *audit_msg, char * const command_info[])
1754 {
1755     return audit_error2(NULL, plugin_name, plugin_type, audit_msg,
1756 	command_info);
1757 }
1758 
1759 static int
approval_open_int(struct plugin_container * plugin)1760 approval_open_int(struct plugin_container *plugin)
1761 {
1762     char **plugin_settings;
1763     const char *errstr = NULL;
1764     int ret;
1765     debug_decl(approval_open_int, SUDO_DEBUG_PCOMM);
1766 
1767     /* Convert struct sudo_settings to plugin_settings[] */
1768     plugin_settings = format_plugin_settings(plugin);
1769     if (plugin_settings == NULL)
1770 	sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
1771 
1772     sudo_debug_set_active_instance(SUDO_DEBUG_INSTANCE_INITIALIZER);
1773     ret = plugin->u.approval->open(SUDO_API_VERSION, sudo_conversation,
1774 	sudo_conversation_printf, plugin_settings, user_info, submit_optind,
1775 	submit_argv, submit_envp, plugin->options, &errstr);
1776 
1777     /* Stash plugin debug instance ID if set in open() function. */
1778     plugin->debug_instance = sudo_debug_get_active_instance();
1779     sudo_debug_set_active_instance(sudo_debug_instance);
1780 
1781     switch (ret) {
1782     case 1:
1783 	break;
1784     case 0:
1785 	/* approval plugin asked to be disabled, remove and free. */
1786 	unlink_plugin(&approval_plugins, plugin);
1787 	break;
1788     case -2:
1789 	usage();
1790 	break;
1791     default:
1792 	/* XXX - audit */
1793 	sudo_fatalx(U_("error initializing approval plugin %s"),
1794 	    plugin->name);
1795     }
1796 
1797     debug_return_int(ret);
1798 }
1799 
1800 static void
approval_show_version(int verbose)1801 approval_show_version(int verbose)
1802 {
1803     struct plugin_container *plugin, *next;
1804     int ok;
1805     debug_decl(approval_show_version, SUDO_DEBUG_PCOMM);
1806 
1807     /*
1808      * Approval plugin us only open for the life of the show_version() call.
1809      */
1810     TAILQ_FOREACH_SAFE(plugin, &approval_plugins, entries, next) {
1811 	if (plugin->u.approval->show_version == NULL)
1812 	    continue;
1813 
1814 	ok = approval_open_int(plugin);
1815 	if (ok == 1) {
1816 	    /* Return value of show_version currently ignored. */
1817 	    sudo_debug_set_active_instance(plugin->debug_instance);
1818 	    plugin->u.approval->show_version(verbose);
1819 	    if (plugin->u.approval->close != NULL)
1820 		plugin->u.approval->close();
1821 	    sudo_debug_set_active_instance(sudo_debug_instance);
1822 	}
1823     }
1824 
1825     debug_return;
1826 }
1827 
1828 /*
1829  * Run approval checks (there may be more than one).
1830  * This is a "one-shot" plugin that has no open/close and is only
1831  * called if the policy plugin accepts the command first.
1832  */
1833 bool
approval_check(char * const command_info[],char * const run_argv[],char * const run_envp[])1834 approval_check(char * const command_info[], char * const run_argv[],
1835     char * const run_envp[])
1836 {
1837     struct plugin_container *plugin, *next;
1838     const char *errstr = NULL;
1839     int ok;
1840     debug_decl(approval_check, SUDO_DEBUG_PCOMM);
1841 
1842     /*
1843      * Approval plugin is only open for the life of the check() call.
1844      */
1845     TAILQ_FOREACH_SAFE(plugin, &approval_plugins, entries, next) {
1846 	if (plugin->u.approval->check == NULL)
1847 	    continue;
1848 
1849 	ok = approval_open_int(plugin);
1850 	if (ok != 1)
1851 	    continue;
1852 
1853 	sudo_debug_set_active_instance(plugin->debug_instance);
1854 	ok = plugin->u.approval->check(command_info, run_argv, run_envp,
1855 	    &errstr);
1856 	sudo_debug_set_active_instance(sudo_debug_instance);
1857 	sudo_debug_printf(SUDO_DEBUG_INFO, "approval plugin %s returns %d (%s)",
1858 	    plugin->name, ok, errstr ? errstr : "");
1859 
1860 	switch (ok) {
1861 	case 0:
1862 	    audit_reject(plugin->name, SUDO_APPROVAL_PLUGIN,
1863 		errstr ? errstr : _("command rejected by approver"),
1864 		command_info);
1865 	    break;
1866 	case 1:
1867 	    if (!audit_accept(plugin->name, SUDO_APPROVAL_PLUGIN, command_info,
1868 		    run_argv, run_envp))
1869 		ok = -1;
1870 	    break;
1871 	case -1:
1872 	    audit_error(plugin->name, SUDO_APPROVAL_PLUGIN,
1873 		errstr ? errstr : _("approval plugin error"),
1874 		command_info);
1875 	    break;
1876 	case -2:
1877 	    usage();
1878 	    break;
1879 	}
1880 
1881 	/* Close approval plugin now that errstr has been consumed. */
1882 	if (plugin->u.approval->close != NULL) {
1883 	    sudo_debug_set_active_instance(plugin->debug_instance);
1884 	    plugin->u.approval->close();
1885 	    sudo_debug_set_active_instance(sudo_debug_instance);
1886 	}
1887 
1888 	if (ok != 1)
1889 	    debug_return_bool(false);
1890     }
1891 
1892     debug_return_bool(true);
1893 }
1894 
1895 static void
plugin_event_callback(int fd,int what,void * v)1896 plugin_event_callback(int fd, int what, void *v)
1897 {
1898     struct sudo_plugin_event_int *ev_int = v;
1899     int old_instance;
1900     debug_decl(plugin_event_callback, SUDO_DEBUG_PCOMM);
1901 
1902     /* Run the real callback using the plugin's debug instance. */
1903     old_instance = sudo_debug_set_active_instance(ev_int->debug_instance);
1904     ev_int->callback(fd, what, ev_int->closure);
1905     sudo_debug_set_active_instance(old_instance);
1906 
1907     debug_return;
1908 }
1909 
1910 /*
1911  * Fill in a previously allocated struct sudo_plugin_event.
1912  */
1913 static int
plugin_event_set(struct sudo_plugin_event * pev,int fd,int events,sudo_ev_callback_t callback,void * closure)1914 plugin_event_set(struct sudo_plugin_event *pev, int fd, int events,
1915     sudo_ev_callback_t callback, void *closure)
1916 {
1917     struct sudo_plugin_event_int *ev_int;
1918     debug_decl(plugin_event_set, SUDO_DEBUG_PCOMM);
1919 
1920     ev_int = __containerof(pev, struct sudo_plugin_event_int, public);
1921     if (sudo_ev_set(&ev_int->private, fd, events, plugin_event_callback, ev_int) == -1)
1922 	debug_return_int(-1);
1923 
1924     /* Stash active instance so we can restore it when callback runs. */
1925     ev_int->debug_instance = sudo_debug_get_active_instance();
1926 
1927     /* Actual user-specified callback and closure. */
1928     ev_int->callback = callback;
1929     ev_int->closure = closure;
1930 
1931     /* Plugin can only operate on the main event loop. */
1932     ev_int->private.base = sudo_event_base;
1933 
1934     debug_return_int(1);
1935 }
1936 
1937 /*
1938  * Add a struct sudo_plugin_event to the main event loop.
1939  */
1940 static int
plugin_event_add(struct sudo_plugin_event * pev,struct timespec * timo)1941 plugin_event_add(struct sudo_plugin_event *pev, struct timespec *timo)
1942 {
1943     struct sudo_plugin_event_int *ev_int;
1944     debug_decl(plugin_event_add, SUDO_DEBUG_PCOMM);
1945 
1946     ev_int = __containerof(pev, struct sudo_plugin_event_int, public);
1947     if (sudo_ev_add(NULL, &ev_int->private, timo, 0) == -1)
1948 	debug_return_int(-1);
1949     debug_return_int(1);
1950 }
1951 
1952 /*
1953  * Delete a struct sudo_plugin_event from the main event loop.
1954  */
1955 static int
plugin_event_del(struct sudo_plugin_event * pev)1956 plugin_event_del(struct sudo_plugin_event *pev)
1957 {
1958     struct sudo_plugin_event_int *ev_int;
1959     debug_decl(plugin_event_del, SUDO_DEBUG_PCOMM);
1960 
1961     ev_int = __containerof(pev, struct sudo_plugin_event_int, public);
1962     if (sudo_ev_del(NULL, &ev_int->private) == -1)
1963 	debug_return_int(-1);
1964     debug_return_int(1);
1965 }
1966 
1967 /*
1968  * Get the amount of time remaining in a timeout event.
1969  */
1970 static int
plugin_event_pending(struct sudo_plugin_event * pev,int events,struct timespec * ts)1971 plugin_event_pending(struct sudo_plugin_event *pev, int events,
1972     struct timespec *ts)
1973 {
1974     struct sudo_plugin_event_int *ev_int;
1975     debug_decl(plugin_event_pending, SUDO_DEBUG_PCOMM);
1976 
1977     ev_int = __containerof(pev, struct sudo_plugin_event_int, public);
1978     debug_return_int(sudo_ev_pending(&ev_int->private, events, ts));
1979 }
1980 
1981 /*
1982  * Get the file descriptor associated with an event.
1983  */
1984 static int
plugin_event_fd(struct sudo_plugin_event * pev)1985 plugin_event_fd(struct sudo_plugin_event *pev)
1986 {
1987     struct sudo_plugin_event_int *ev_int;
1988     debug_decl(plugin_event_fd, SUDO_DEBUG_PCOMM);
1989 
1990     ev_int = __containerof(pev, struct sudo_plugin_event_int, public);
1991     debug_return_int(sudo_ev_get_fd(&ev_int->private));
1992 }
1993 
1994 /*
1995  * Break out of the event loop, killing the command if it is running.
1996  */
1997 static void
plugin_event_loopbreak(struct sudo_plugin_event * pev)1998 plugin_event_loopbreak(struct sudo_plugin_event *pev)
1999 {
2000     struct sudo_plugin_event_int *ev_int;
2001     debug_decl(plugin_event_loopbreak, SUDO_DEBUG_PCOMM);
2002 
2003     ev_int = __containerof(pev, struct sudo_plugin_event_int, public);
2004     sudo_ev_loopbreak(ev_int->private.base);
2005     debug_return;
2006 }
2007 
2008 /*
2009  * Reset the event base of a struct sudo_plugin_event.
2010  * The event is removed from the old base (if any) first.
2011  * A NULL base can be used to set the default sudo event base.
2012  */
2013 static void
plugin_event_setbase(struct sudo_plugin_event * pev,void * base)2014 plugin_event_setbase(struct sudo_plugin_event *pev, void *base)
2015 {
2016     struct sudo_plugin_event_int *ev_int;
2017     debug_decl(plugin_event_setbase, SUDO_DEBUG_PCOMM);
2018 
2019     ev_int = __containerof(pev, struct sudo_plugin_event_int, public);
2020     if (ev_int->private.base != NULL)
2021 	sudo_ev_del(ev_int->private.base, &ev_int->private);
2022     ev_int->private.base = base ? base : sudo_event_base;
2023     debug_return;
2024 }
2025 
2026 /*
2027  * Free a struct sudo_plugin_event allocated by plugin_event_alloc().
2028  */
2029 static void
plugin_event_free(struct sudo_plugin_event * pev)2030 plugin_event_free(struct sudo_plugin_event *pev)
2031 {
2032     struct sudo_plugin_event_int *ev_int;
2033     debug_decl(plugin_event_free, SUDO_DEBUG_PCOMM);
2034 
2035     /* The private field is first so sudo_ev_free() can free the struct. */
2036     ev_int = __containerof(pev, struct sudo_plugin_event_int, public);
2037     sudo_ev_free(&ev_int->private);
2038 
2039     debug_return;
2040 }
2041 
2042 /*
2043  * Allocate a struct sudo_plugin_event and fill in the public fields.
2044  */
2045 struct sudo_plugin_event *
sudo_plugin_event_alloc(void)2046 sudo_plugin_event_alloc(void)
2047 {
2048     struct sudo_plugin_event_int *ev_int;
2049     debug_decl(plugin_event_alloc, SUDO_DEBUG_PCOMM);
2050 
2051     if ((ev_int = malloc(sizeof(*ev_int))) == NULL)
2052 	debug_return_ptr(NULL);
2053 
2054     /* Init public fields. */
2055     ev_int->public.set = plugin_event_set;
2056     ev_int->public.add = plugin_event_add;
2057     ev_int->public.del = plugin_event_del;
2058     ev_int->public.fd = plugin_event_fd;
2059     ev_int->public.pending = plugin_event_pending;
2060     ev_int->public.setbase = plugin_event_setbase;
2061     ev_int->public.loopbreak = plugin_event_loopbreak;
2062     ev_int->public.free = plugin_event_free;
2063 
2064     /* Debug instance to use with the callback. */
2065     ev_int->debug_instance = SUDO_DEBUG_INSTANCE_INITIALIZER;
2066 
2067     /* Clear private portion in case caller tries to use us uninitialized. */
2068     memset(&ev_int->private, 0, sizeof(ev_int->private));
2069 
2070     debug_return_ptr(&ev_int->public);
2071 }
2072 
2073 static void
free_plugin_container(struct plugin_container * plugin,bool ioplugin)2074 free_plugin_container(struct plugin_container *plugin, bool ioplugin)
2075 {
2076     debug_decl(free_plugin_container, SUDO_DEBUG_PLUGIN);
2077 
2078     free(plugin->path);
2079     free(plugin->name);
2080     if (plugin->options != NULL) {
2081 	int i = 0;
2082 	while (plugin->options[i] != NULL)
2083 	    free(plugin->options[i++]);
2084 	free(plugin->options);
2085     }
2086     if (ioplugin)
2087 	free(plugin);
2088 
2089     debug_return;
2090 }
2091 
2092 bool
gc_add(enum sudo_gc_types type,void * v)2093 gc_add(enum sudo_gc_types type, void *v)
2094 {
2095 #ifdef NO_LEAKS
2096     struct sudo_gc_entry *gc;
2097     debug_decl(gc_add, SUDO_DEBUG_MAIN);
2098 
2099     if (v == NULL)
2100 	debug_return_bool(false);
2101 
2102     gc = calloc(1, sizeof(*gc));
2103     if (gc == NULL) {
2104 	sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
2105 	debug_return_bool(false);
2106     }
2107     switch (type) {
2108     case GC_PTR:
2109 	gc->u.ptr = v;
2110 	break;
2111     case GC_VECTOR:
2112 	gc->u.vec = v;
2113 	break;
2114     default:
2115 	free(gc);
2116 	sudo_warnx("unexpected garbage type %d", type);
2117 	debug_return_bool(false);
2118     }
2119     gc->type = type;
2120     SLIST_INSERT_HEAD(&sudo_gc_list, gc, entries);
2121     debug_return_bool(true);
2122 #else
2123     return true;
2124 #endif /* NO_LEAKS */
2125 }
2126 
2127 #ifdef NO_LEAKS
2128 static void
gc_run(void)2129 gc_run(void)
2130 {
2131     struct plugin_container *plugin;
2132     struct sudo_gc_entry *gc;
2133     char **cur;
2134     debug_decl(gc_run, SUDO_DEBUG_MAIN);
2135 
2136     /* Collect garbage. */
2137     while ((gc = SLIST_FIRST(&sudo_gc_list))) {
2138 	SLIST_REMOVE_HEAD(&sudo_gc_list, entries);
2139 	switch (gc->type) {
2140 	case GC_PTR:
2141 	    free(gc->u.ptr);
2142 	    free(gc);
2143 	    break;
2144 	case GC_VECTOR:
2145 	    for (cur = gc->u.vec; *cur != NULL; cur++)
2146 		free(*cur);
2147 	    free(gc->u.vec);
2148 	    free(gc);
2149 	    break;
2150 	default:
2151 	    sudo_warnx("unexpected garbage type %d", gc->type);
2152 	}
2153     }
2154 
2155     /* Free plugin structs. */
2156     free_plugin_container(&policy_plugin, false);
2157     while ((plugin = TAILQ_FIRST(&io_plugins))) {
2158 	TAILQ_REMOVE(&io_plugins, plugin, entries);
2159 	free_plugin_container(plugin, true);
2160     }
2161 
2162     debug_return;
2163 }
2164 #endif /* NO_LEAKS */
2165 
2166 static void
gc_init(void)2167 gc_init(void)
2168 {
2169 #ifdef NO_LEAKS
2170     atexit(gc_run);
2171 #endif
2172 }
2173