2 * Copyright (c) 2010-2013 Todd C. Miller <Todd.Miller@courtesan.com>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/types.h>
21 #include <netinet/in.h>
30 #endif /* STDC_HEADERS */
32 # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
36 #endif /* HAVE_STRING_H */
39 #endif /* HAVE_STRINGS_H */
42 #endif /* HAVE_UNISTD_H */
48 #include "sudoers_version.h"
49 #include "interfaces.h"
52 * Info passed in from the sudo front-end.
54 struct sudoers_policy_open_info {
55 char * const *settings;
56 char * const *user_info;
57 char * const *plugin_args;
61 * Command execution args to be filled in: argv, envp and command info.
63 struct sudoers_exec_args {
69 static int sudo_version;
70 static const char *interfaces_string;
71 sudo_conv_t sudo_conv;
72 const char *path_ldap_conf = _PATH_LDAP_CONF;
73 const char *path_ldap_secret = _PATH_LDAP_SECRET;
75 extern __dso_public struct policy_plugin sudoers_policy;
77 #ifdef HAVE_BSD_AUTH_H
78 extern char *login_style;
79 #endif /* HAVE_BSD_AUTH_H */
82 * Deserialize args, settings and user_info arrays.
83 * Fills in struct sudo_user and other common sudoers state.
86 sudoers_policy_deserialize_info(void *v, char **runas_user, char **runas_group)
88 struct sudoers_policy_open_info *info = v;
90 const char *p, *groups = NULL;
91 const char *debug_flags = NULL;
93 debug_decl(sudoers_policy_deserialize_info, SUDO_DEBUG_PLUGIN)
95 #define MATCHES(s, v) (strncmp(s, v, sizeof(v) - 1) == 0)
97 /* Parse sudo.conf plugin args. */
98 if (info->plugin_args != NULL) {
99 for (cur = info->plugin_args; *cur != NULL; cur++) {
100 if (MATCHES(*cur, "sudoers_file=")) {
101 sudoers_file = *cur + sizeof("sudoers_file=") - 1;
104 if (MATCHES(*cur, "sudoers_uid=")) {
105 sudoers_uid = (uid_t) atoi(*cur + sizeof("sudoers_uid=") - 1);
108 if (MATCHES(*cur, "sudoers_gid=")) {
109 sudoers_gid = (gid_t) atoi(*cur + sizeof("sudoers_gid=") - 1);
112 if (MATCHES(*cur, "sudoers_mode=")) {
113 sudoers_mode = (mode_t) strtol(*cur + sizeof("sudoers_mode=") - 1,
117 if (MATCHES(*cur, "ldap_conf=")) {
118 path_ldap_conf = *cur + sizeof("ldap_conf=") - 1;
121 if (MATCHES(*cur, "ldap_secret=")) {
122 path_ldap_secret = *cur + sizeof("ldap_secret=") - 1;
128 /* Parse command line settings. */
130 sudo_user.max_groups = -1;
131 for (cur = info->settings; *cur != NULL; cur++) {
132 if (MATCHES(*cur, "closefrom=")) {
133 user_closefrom = atoi(*cur + sizeof("closefrom=") - 1);
136 if (MATCHES(*cur, "debug_flags=")) {
137 debug_flags = *cur + sizeof("debug_flags=") - 1;
140 if (MATCHES(*cur, "runas_user=")) {
141 *runas_user = *cur + sizeof("runas_user=") - 1;
142 sudo_user.flags |= RUNAS_USER_SPECIFIED;
145 if (MATCHES(*cur, "runas_group=")) {
146 *runas_group = *cur + sizeof("runas_group=") - 1;
147 sudo_user.flags |= RUNAS_GROUP_SPECIFIED;
150 if (MATCHES(*cur, "prompt=")) {
151 user_prompt = *cur + sizeof("prompt=") - 1;
152 def_passprompt_override = true;
155 if (MATCHES(*cur, "set_home=")) {
156 if (atobool(*cur + sizeof("set_home=") - 1) == true)
157 SET(flags, MODE_RESET_HOME);
160 if (MATCHES(*cur, "preserve_environment=")) {
161 if (atobool(*cur + sizeof("preserve_environment=") - 1) == true)
162 SET(flags, MODE_PRESERVE_ENV);
165 if (MATCHES(*cur, "run_shell=")) {
166 if (atobool(*cur + sizeof("run_shell=") - 1) == true)
167 SET(flags, MODE_SHELL);
170 if (MATCHES(*cur, "login_shell=")) {
171 if (atobool(*cur + sizeof("login_shell=") - 1) == true) {
172 SET(flags, MODE_LOGIN_SHELL);
173 def_env_reset = true;
177 if (MATCHES(*cur, "implied_shell=")) {
178 if (atobool(*cur + sizeof("implied_shell=") - 1) == true)
179 SET(flags, MODE_IMPLIED_SHELL);
182 if (MATCHES(*cur, "preserve_groups=")) {
183 if (atobool(*cur + sizeof("preserve_groups=") - 1) == true)
184 SET(flags, MODE_PRESERVE_GROUPS);
187 if (MATCHES(*cur, "ignore_ticket=")) {
188 if (atobool(*cur + sizeof("ignore_ticket=") - 1) == true)
189 SET(flags, MODE_IGNORE_TICKET);
192 if (MATCHES(*cur, "noninteractive=")) {
193 if (atobool(*cur + sizeof("noninteractive=") - 1) == true)
194 SET(flags, MODE_NONINTERACTIVE);
197 if (MATCHES(*cur, "sudoedit=")) {
198 if (atobool(*cur + sizeof("sudoedit=") - 1) == true)
199 SET(flags, MODE_EDIT);
202 if (MATCHES(*cur, "login_class=")) {
203 login_class = *cur + sizeof("login_class=") - 1;
204 def_use_loginclass = true;
208 if (MATCHES(*cur, "runas_privs=")) {
209 def_privs = *cur + sizeof("runas_privs=") - 1;
212 if (MATCHES(*cur, "runas_limitprivs=")) {
213 def_limitprivs = *cur + sizeof("runas_limitprivs=") - 1;
216 #endif /* HAVE_PRIV_SET */
218 if (MATCHES(*cur, "selinux_role=")) {
219 user_role = *cur + sizeof("selinux_role=") - 1;
222 if (MATCHES(*cur, "selinux_type=")) {
223 user_type = *cur + sizeof("selinux_type=") - 1;
226 #endif /* HAVE_SELINUX */
227 #ifdef HAVE_BSD_AUTH_H
228 if (MATCHES(*cur, "bsdauth_type=")) {
229 login_style = *cur + sizeof("bsdauth_type=") - 1;
232 #endif /* HAVE_BSD_AUTH_H */
233 #if !defined(HAVE_GETPROGNAME) && !defined(HAVE___PROGNAME)
234 if (MATCHES(*cur, "progname=")) {
235 setprogname(*cur + sizeof("progname=") - 1);
239 if (MATCHES(*cur, "network_addrs=")) {
240 interfaces_string = *cur + sizeof("network_addrs=") - 1;
241 set_interfaces(interfaces_string);
244 if (MATCHES(*cur, "max_groups=")) {
245 sudo_user.max_groups = atoi(*cur + sizeof("max_groups=") - 1);
250 for (cur = info->user_info; *cur != NULL; cur++) {
251 if (MATCHES(*cur, "user=")) {
252 user_name = estrdup(*cur + sizeof("user=") - 1);
255 if (MATCHES(*cur, "uid=")) {
256 user_uid = (uid_t) atoi(*cur + sizeof("uid=") - 1);
259 if (MATCHES(*cur, "gid=")) {
260 p = *cur + sizeof("gid=") - 1;
261 user_gid = (gid_t) atoi(p);
264 if (MATCHES(*cur, "groups=")) {
265 groups = *cur + sizeof("groups=") - 1;
268 if (MATCHES(*cur, "cwd=")) {
269 user_cwd = estrdup(*cur + sizeof("cwd=") - 1);
272 if (MATCHES(*cur, "tty=")) {
273 user_tty = user_ttypath = estrdup(*cur + sizeof("tty=") - 1);
274 if (strncmp(user_tty, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
275 user_tty += sizeof(_PATH_DEV) - 1;
278 if (MATCHES(*cur, "host=")) {
279 user_host = user_shost = estrdup(*cur + sizeof("host=") - 1);
280 if ((p = strchr(user_host, '.')))
281 user_shost = estrndup(user_host, (size_t)(p - user_host));
284 if (MATCHES(*cur, "lines=")) {
285 sudo_user.lines = atoi(*cur + sizeof("lines=") - 1);
288 if (MATCHES(*cur, "cols=")) {
289 sudo_user.cols = atoi(*cur + sizeof("cols=") - 1);
292 if (MATCHES(*cur, "sid=")) {
293 sudo_user.sid = atoi(*cur + sizeof("sid=") - 1);
297 if (user_cwd == NULL)
298 user_cwd = "unknown";
299 if (user_tty == NULL)
300 user_tty = "unknown"; /* user_ttypath remains NULL */
302 if (groups != NULL && groups[0] != '\0') {
307 /* Count number of groups, including passwd gid. */
309 for (cp = groups; *cp != '\0'; cp++) {
314 /* The first gid in the list is the passwd group gid. */
315 gids = emalloc2(ngids, sizeof(GETGROUPS_T));
320 gids[ngids] = atoi(cp);
321 if (gids[0] != gids[ngids])
323 cp = strchr(cp, ',');
326 cp++; /* skip over comma */
332 /* Stash initial umask for later use. */
333 user_umask = umask(SUDO_UMASK);
336 /* Setup debugging if indicated. */
337 if (debug_flags != NULL) {
338 sudo_debug_init(NULL, debug_flags);
339 for (cur = info->settings; *cur != NULL; cur++)
340 sudo_debug_printf(SUDO_DEBUG_INFO, "settings: %s", *cur);
341 for (cur = info->user_info; *cur != NULL; cur++)
342 sudo_debug_printf(SUDO_DEBUG_INFO, "user_info: %s", *cur);
346 debug_return_int(flags);
350 * Setup the execution environment.
351 * Builds up the command_info list and sets argv and envp.
352 * Returns 1 on success and -1 on error.
355 sudoers_policy_exec_setup(char *argv[], char *envp[], mode_t cmnd_umask,
356 char *iolog_path, void *v)
358 struct sudoers_exec_args *exec_args = v;
361 debug_decl(sudoers_policy_exec_setup, SUDO_DEBUG_PLUGIN)
363 /* Increase the length of command_info as needed, it is *not* checked. */
364 command_info = ecalloc(32, sizeof(char **));
366 command_info[info_len++] = fmt_string("command", safe_cmnd);
367 if (def_log_input || def_log_output) {
369 command_info[info_len++] = iolog_path;
371 command_info[info_len++] = estrdup("iolog_stdin=true");
372 command_info[info_len++] = estrdup("iolog_ttyin=true");
374 if (def_log_output) {
375 command_info[info_len++] = estrdup("iolog_stdout=true");
376 command_info[info_len++] = estrdup("iolog_stderr=true");
377 command_info[info_len++] = estrdup("iolog_ttyout=true");
379 if (def_compress_io) {
380 command_info[info_len++] = estrdup("iolog_compress=true");
383 easprintf(&command_info[info_len++], "maxseq=%u", def_maxseq);
386 if (ISSET(sudo_mode, MODE_EDIT))
387 command_info[info_len++] = estrdup("sudoedit=true");
388 if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
389 /* Set cwd to run user's homedir. */
390 command_info[info_len++] = fmt_string("cwd", runas_pw->pw_dir);
392 if (def_stay_setuid) {
393 easprintf(&command_info[info_len++], "runas_uid=%u",
394 (unsigned int)user_uid);
395 easprintf(&command_info[info_len++], "runas_gid=%u",
396 (unsigned int)user_gid);
397 easprintf(&command_info[info_len++], "runas_euid=%u",
398 (unsigned int)runas_pw->pw_uid);
399 easprintf(&command_info[info_len++], "runas_egid=%u",
400 runas_gr ? (unsigned int)runas_gr->gr_gid :
401 (unsigned int)runas_pw->pw_gid);
403 easprintf(&command_info[info_len++], "runas_uid=%u",
404 (unsigned int)runas_pw->pw_uid);
405 easprintf(&command_info[info_len++], "runas_gid=%u",
406 runas_gr ? (unsigned int)runas_gr->gr_gid :
407 (unsigned int)runas_pw->pw_gid);
409 if (def_preserve_groups) {
410 command_info[info_len++] = "preserve_groups=true";
416 struct group_list *grlist = sudo_get_grlist(runas_pw);
418 /* We reserve an extra spot in the list for the effective gid. */
419 glsize = sizeof("runas_groups=") - 1 +
420 ((grlist->ngids + 1) * (MAX_UID_T_LEN + 1));
421 gid_list = emalloc(glsize);
422 memcpy(gid_list, "runas_groups=", sizeof("runas_groups=") - 1);
423 cp = gid_list + sizeof("runas_groups=") - 1;
425 /* On BSD systems the effective gid is the first group in the list. */
426 egid = runas_gr ? (unsigned int)runas_gr->gr_gid :
427 (unsigned int)runas_pw->pw_gid;
428 len = snprintf(cp, glsize - (cp - gid_list), "%u", egid);
429 if (len < 0 || len >= glsize - (cp - gid_list))
430 fatalx(_("internal error, %s overflow"), "runas_groups");
432 for (i = 0; i < grlist->ngids; i++) {
433 if (grlist->gids[i] != egid) {
434 len = snprintf(cp, glsize - (cp - gid_list), ",%u",
435 (unsigned int) grlist->gids[i]);
436 if (len < 0 || len >= glsize - (cp - gid_list))
437 fatalx(_("internal error, %s overflow"), "runas_groups");
441 command_info[info_len++] = gid_list;
442 sudo_grlist_delref(grlist);
444 if (def_closefrom >= 0)
445 easprintf(&command_info[info_len++], "closefrom=%d", def_closefrom);
447 command_info[info_len++] = estrdup("noexec=true");
448 if (def_exec_background)
449 command_info[info_len++] = estrdup("exec_background=true");
451 command_info[info_len++] = estrdup("set_utmp=true");
453 command_info[info_len++] = estrdup("use_pty=true");
455 command_info[info_len++] = fmt_string("utmp_user", runas_pw->pw_name);
456 if (cmnd_umask != 0777)
457 easprintf(&command_info[info_len++], "umask=0%o", (unsigned int)cmnd_umask);
458 #ifdef HAVE_LOGIN_CAP_H
459 if (def_use_loginclass)
460 command_info[info_len++] = fmt_string("login_class", login_class);
461 #endif /* HAVE_LOGIN_CAP_H */
463 if (user_role != NULL)
464 command_info[info_len++] = fmt_string("selinux_role", user_role);
465 if (user_type != NULL)
466 command_info[info_len++] = fmt_string("selinux_type", user_type);
467 #endif /* HAVE_SELINUX */
469 if (runas_privs != NULL)
470 command_info[info_len++] = fmt_string("runas_privs", runas_privs);
471 if (runas_limitprivs != NULL)
472 command_info[info_len++] = fmt_string("runas_limitprivs", runas_limitprivs);
473 #endif /* HAVE_SELINUX */
475 /* Fill in exec environment info */
476 *(exec_args->argv) = argv;
477 *(exec_args->envp) = envp;
478 *(exec_args->info) = command_info;
480 debug_return_bool(true);
484 sudoers_policy_open(unsigned int version, sudo_conv_t conversation,
485 sudo_printf_t plugin_printf, char * const settings[],
486 char * const user_info[], char * const envp[], char * const args[])
488 struct sudoers_policy_open_info info;
489 debug_decl(sudoers_policy_open, SUDO_DEBUG_PLUGIN)
491 sudo_version = version;
492 sudo_conv = conversation;
493 sudo_printf = plugin_printf;
495 /* Plugin args are only specified for API version 1.2 and higher. */
496 if (sudo_version < SUDO_API_MKVERSION(1, 2))
499 if (fatal_setjmp() != 0) {
500 /* called via fatal(), fatalx() or log_fatal() */
502 fatal_disable_setjmp();
503 debug_return_bool(-1);
506 /* Call the sudoers init function. */
507 info.settings = settings;
508 info.user_info = user_info;
509 info.plugin_args = args;
510 debug_return_bool(sudoers_policy_init(&info, envp));
514 sudoers_policy_close(int exit_status, int error_code)
516 debug_decl(sudoers_policy_close, SUDO_DEBUG_PLUGIN)
518 if (fatal_setjmp() != 0) {
519 /* called via fatal(), fatalx() or log_fatal() */
520 fatal_disable_setjmp();
524 /* We do not currently log the exit status. */
527 warning(_("unable to execute %s"), safe_cmnd);
530 /* Close the session we opened in sudoers_policy_init_session(). */
531 if (ISSET(sudo_mode, MODE_RUN|MODE_EDIT))
532 (void)sudo_auth_end_session(runas_pw);
534 /* Free remaining references to password and group entries. */
535 /* XXX - move cleanup to function in sudoers.c */
536 sudo_pw_delref(sudo_user.pw);
538 sudo_pw_delref(runas_pw);
540 if (runas_gr != NULL) {
541 sudo_gr_delref(runas_gr);
544 if (user_group_list != NULL) {
545 sudo_grlist_delref(user_group_list);
546 user_group_list = NULL;
555 * The init_session function is called before executing the command
556 * and before uid/gid changes occur.
557 * Returns 1 on success, 0 on failure and -1 on error.
560 sudoers_policy_init_session(struct passwd *pwd, char **user_env[])
562 debug_decl(sudoers_policy_init_session, SUDO_DEBUG_PLUGIN)
564 /* user_env is only specified for API version 1.2 and higher. */
565 if (sudo_version < SUDO_API_MKVERSION(1, 2))
568 if (fatal_setjmp() != 0) {
569 /* called via fatal(), fatalx() or log_fatal() */
570 fatal_disable_setjmp();
571 debug_return_bool(-1);
574 debug_return_bool(sudo_auth_begin_session(pwd, user_env));
578 sudoers_policy_check(int argc, char * const argv[], char *env_add[],
579 char **command_infop[], char **argv_out[], char **user_env_out[])
581 struct sudoers_exec_args exec_args;
583 debug_decl(sudoers_policy_check, SUDO_DEBUG_PLUGIN)
585 if (!ISSET(sudo_mode, MODE_EDIT))
586 SET(sudo_mode, MODE_RUN);
588 exec_args.argv = argv_out;
589 exec_args.envp = user_env_out;
590 exec_args.info = command_infop;
592 rval = sudoers_policy_main(argc, argv, 0, env_add, &exec_args);
593 if (rval == true && sudo_version >= SUDO_API_MKVERSION(1, 3)) {
594 /* Unset close function if we don't need it to avoid extra process. */
595 if (!def_log_input && !def_log_output && !def_use_pty &&
596 !sudo_auth_needs_end_session())
597 sudoers_policy.close = NULL;
599 debug_return_bool(rval);
603 sudoers_policy_validate(void)
605 debug_decl(sudoers_policy_validate, SUDO_DEBUG_PLUGIN)
607 user_cmnd = "validate";
608 SET(sudo_mode, MODE_VALIDATE);
610 debug_return_bool(sudoers_policy_main(0, NULL, I_VERIFYPW, NULL, NULL));
614 sudoers_policy_invalidate(int remove)
616 debug_decl(sudoers_policy_invalidate, SUDO_DEBUG_PLUGIN)
619 if (fatal_setjmp() == 0) {
620 remove_timestamp(remove);
623 fatal_disable_setjmp();
629 sudoers_policy_list(int argc, char * const argv[], int verbose,
630 const char *list_user)
633 debug_decl(sudoers_policy_list, SUDO_DEBUG_PLUGIN)
637 SET(sudo_mode, MODE_CHECK);
639 SET(sudo_mode, MODE_LIST);
643 list_pw = sudo_getpwnam(list_user);
644 if (list_pw == NULL) {
645 warningx(_("unknown user: %s"), list_user);
646 debug_return_bool(-1);
649 rval = sudoers_policy_main(argc, argv, I_LISTPW, NULL, NULL);
651 sudo_pw_delref(list_pw);
655 debug_return_bool(rval);
659 sudoers_policy_version(int verbose)
661 debug_decl(sudoers_policy_version, SUDO_DEBUG_PLUGIN)
663 if (fatal_setjmp() != 0) {
664 /* error recovery via fatal(), fatalx() or log_fatal() */
665 fatal_disable_setjmp();
666 debug_return_bool(-1);
669 sudo_printf(SUDO_CONV_INFO_MSG, _("Sudoers policy plugin version %s\n"),
671 sudo_printf(SUDO_CONV_INFO_MSG, _("Sudoers file grammar version %d\n"),
672 SUDOERS_GRAMMAR_VERSION);
675 sudo_printf(SUDO_CONV_INFO_MSG, _("\nSudoers path: %s\n"), sudoers_file);
677 # ifdef _PATH_NSSWITCH_CONF
678 sudo_printf(SUDO_CONV_INFO_MSG, _("nsswitch path: %s\n"), _PATH_NSSWITCH_CONF);
680 sudo_printf(SUDO_CONV_INFO_MSG, _("ldap.conf path: %s\n"), path_ldap_conf);
681 sudo_printf(SUDO_CONV_INFO_MSG, _("ldap.secret path: %s\n"), path_ldap_secret);
685 sudo_printf(SUDO_CONV_INFO_MSG, "\n");
686 if (interfaces_string != NULL) {
687 dump_interfaces(interfaces_string);
688 sudo_printf(SUDO_CONV_INFO_MSG, "\n");
691 debug_return_bool(true);
695 sudoers_policy_register_hooks(int version, int (*register_hook)(struct sudo_hook *hook))
697 struct sudo_hook hook;
699 memset(&hook, 0, sizeof(hook));
700 hook.hook_version = SUDO_HOOK_VERSION;
702 hook.hook_type = SUDO_HOOK_SETENV;
703 hook.hook_fn = sudoers_hook_setenv;
704 register_hook(&hook);
706 hook.hook_type = SUDO_HOOK_UNSETENV;
707 hook.hook_fn = sudoers_hook_unsetenv;
708 register_hook(&hook);
710 hook.hook_type = SUDO_HOOK_GETENV;
711 hook.hook_fn = sudoers_hook_getenv;
712 register_hook(&hook);
714 hook.hook_type = SUDO_HOOK_PUTENV;
715 hook.hook_fn = sudoers_hook_putenv;
716 register_hook(&hook);
719 __dso_public struct policy_plugin sudoers_policy = {
723 sudoers_policy_close,
724 sudoers_policy_version,
725 sudoers_policy_check,
727 sudoers_policy_validate,
728 sudoers_policy_invalidate,
729 sudoers_policy_init_session,
730 sudoers_policy_register_hooks