Annotation of embedaddon/sudo/plugins/sudoers/sudoers.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (c) 1993-1996, 1998-2011 Todd C. Miller <Todd.Miller@courtesan.com>
! 3: *
! 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.
! 7: *
! 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.
! 15: *
! 16: * Sponsored in part by the Defense Advanced Research Projects
! 17: * Agency (DARPA) and Air Force Research Laboratory, Air Force
! 18: * Materiel Command, USAF, under agreement number F39502-99-1-0512.
! 19: *
! 20: * For a brief history of sudo, please see the HISTORY file included
! 21: * with this distribution.
! 22: */
! 23:
! 24: #define _SUDO_MAIN
! 25:
! 26: #ifdef __TANDEM
! 27: # include <floss.h>
! 28: #endif
! 29:
! 30: #include <config.h>
! 31:
! 32: #include <sys/types.h>
! 33: #include <sys/stat.h>
! 34: #include <sys/param.h>
! 35: #include <sys/socket.h>
! 36: #include <stdio.h>
! 37: #ifdef STDC_HEADERS
! 38: # include <stdlib.h>
! 39: # include <stddef.h>
! 40: #else
! 41: # ifdef HAVE_STDLIB_H
! 42: # include <stdlib.h>
! 43: # endif
! 44: #endif /* STDC_HEADERS */
! 45: #ifdef HAVE_STRING_H
! 46: # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
! 47: # include <memory.h>
! 48: # endif
! 49: # include <string.h>
! 50: #endif /* HAVE_STRING_H */
! 51: #ifdef HAVE_STRINGS_H
! 52: # include <strings.h>
! 53: #endif /* HAVE_STRINGS_H */
! 54: #ifdef HAVE_UNISTD_H
! 55: # include <unistd.h>
! 56: #endif /* HAVE_UNISTD_H */
! 57: #include <pwd.h>
! 58: #include <errno.h>
! 59: #include <fcntl.h>
! 60: #include <signal.h>
! 61: #include <grp.h>
! 62: #include <time.h>
! 63: #ifdef HAVE_SETLOCALE
! 64: # include <locale.h>
! 65: #endif
! 66: #include <netinet/in.h>
! 67: #include <netdb.h>
! 68: #ifdef HAVE_LOGIN_CAP_H
! 69: # include <login_cap.h>
! 70: # ifndef LOGIN_DEFROOTCLASS
! 71: # define LOGIN_DEFROOTCLASS "daemon"
! 72: # endif
! 73: #endif
! 74: #ifdef HAVE_SELINUX
! 75: # include <selinux/selinux.h>
! 76: #endif
! 77: #include <ctype.h>
! 78: #include <setjmp.h>
! 79:
! 80: #include "sudoers.h"
! 81: #include "interfaces.h"
! 82: #include "sudoers_version.h"
! 83: #include "auth/sudo_auth.h"
! 84:
! 85: /*
! 86: * Prototypes
! 87: */
! 88: static void init_vars(char * const *);
! 89: static int set_cmnd(void);
! 90: static void set_loginclass(struct passwd *);
! 91: static void set_runaspw(const char *);
! 92: static void set_runasgr(const char *);
! 93: static int cb_runas_default(const char *);
! 94: static int sudoers_policy_version(int verbose);
! 95: static int deserialize_info(char * const settings[], char * const user_info[]);
! 96: static char *find_editor(int nfiles, char **files, char ***argv_out);
! 97: static void create_admin_success_flag(void);
! 98:
! 99: /*
! 100: * Globals
! 101: */
! 102: const char *sudoers_file = _PATH_SUDOERS;
! 103: mode_t sudoers_mode = SUDOERS_MODE;
! 104: uid_t sudoers_uid = SUDOERS_UID;
! 105: gid_t sudoers_gid = SUDOERS_GID;
! 106: struct sudo_user sudo_user;
! 107: struct passwd *list_pw;
! 108: struct interface *interfaces;
! 109: int long_list;
! 110: int debug_level;
! 111: uid_t timestamp_uid;
! 112: extern int errorlineno;
! 113: extern int parse_error;
! 114: extern char *errorfile;
! 115: #ifdef HAVE_LOGIN_CAP_H
! 116: login_cap_t *lc;
! 117: #endif /* HAVE_LOGIN_CAP_H */
! 118: #ifdef HAVE_BSD_AUTH_H
! 119: char *login_style;
! 120: #endif /* HAVE_BSD_AUTH_H */
! 121: sudo_conv_t sudo_conv;
! 122: sudo_printf_t sudo_printf;
! 123: int sudo_mode;
! 124:
! 125: static char *prev_user;
! 126: static char *runas_user;
! 127: static char *runas_group;
! 128: static struct sudo_nss_list *snl;
! 129: static const char *interfaces_string;
! 130: static sigaction_t saved_sa_int, saved_sa_quit, saved_sa_tstp;
! 131:
! 132: /* XXX - must be extern for audit bits of sudo_auth.c */
! 133: int NewArgc;
! 134: char **NewArgv;
! 135:
! 136: /* plugin_error.c */
! 137: extern sigjmp_buf error_jmp;
! 138:
! 139: static int
! 140: sudoers_policy_open(unsigned int version, sudo_conv_t conversation,
! 141: sudo_printf_t plugin_printf, char * const settings[],
! 142: char * const user_info[], char * const envp[])
! 143: {
! 144: volatile int sources = 0;
! 145: sigaction_t sa;
! 146: struct sudo_nss *nss;
! 147:
! 148: if (!sudo_conv)
! 149: sudo_conv = conversation;
! 150: if (!sudo_printf)
! 151: sudo_printf = plugin_printf;
! 152:
! 153: if (sigsetjmp(error_jmp, 1)) {
! 154: /* called via error(), errorx() or log_error() */
! 155: rewind_perms();
! 156: return -1;
! 157: }
! 158:
! 159: bindtextdomain("sudoers", LOCALEDIR);
! 160:
! 161: /*
! 162: * Signal setup:
! 163: * Ignore keyboard-generated signals so the user cannot interrupt
! 164: * us at some point and avoid the logging.
! 165: * Install handler to wait for children when they exit.
! 166: */
! 167: zero_bytes(&sa, sizeof(sa));
! 168: sigemptyset(&sa.sa_mask);
! 169: sa.sa_flags = SA_RESTART;
! 170: sa.sa_handler = SIG_IGN;
! 171: (void) sigaction(SIGINT, &sa, &saved_sa_int);
! 172: (void) sigaction(SIGQUIT, &sa, &saved_sa_quit);
! 173: (void) sigaction(SIGTSTP, &sa, &saved_sa_tstp);
! 174:
! 175: sudo_setpwent();
! 176: sudo_setgrent();
! 177:
! 178: /* Initialize environment functions (including replacements). */
! 179: env_init(envp);
! 180:
! 181: /* Setup defaults data structures. */
! 182: init_defaults();
! 183:
! 184: /* Parse settings and user_info */
! 185: sudo_mode = deserialize_info(settings, user_info);
! 186:
! 187: init_vars(envp); /* XXX - move this later? */
! 188:
! 189: /* Parse nsswitch.conf for sudoers order. */
! 190: snl = sudo_read_nss();
! 191:
! 192: /* LDAP or NSS may modify the euid so we need to be root for the open. */
! 193: set_perms(PERM_INITIAL);
! 194: set_perms(PERM_ROOT);
! 195:
! 196: /* Open and parse sudoers, set global defaults */
! 197: tq_foreach_fwd(snl, nss) {
! 198: if (nss->open(nss) == 0 && nss->parse(nss) == 0) {
! 199: sources++;
! 200: if (nss->setdefs(nss) != 0)
! 201: log_error(NO_STDERR|NO_EXIT, _("problem with defaults entries"));
! 202: }
! 203: }
! 204: if (sources == 0) {
! 205: warningx(_("no valid sudoers sources found, quitting"));
! 206: return -1;
! 207: }
! 208:
! 209: /* XXX - collect post-sudoers parse settings into a function */
! 210:
! 211: /*
! 212: * Initialize external group plugin, if any.
! 213: */
! 214: if (def_group_plugin) {
! 215: if (group_plugin_load(def_group_plugin) != TRUE)
! 216: def_group_plugin = NULL;
! 217: }
! 218:
! 219: /*
! 220: * Set runas passwd/group entries based on command line or sudoers.
! 221: * Note that if runas_group was specified without runas_user we
! 222: * defer setting runas_pw so the match routines know to ignore it.
! 223: */
! 224: if (runas_group != NULL) {
! 225: set_runasgr(runas_group);
! 226: if (runas_user != NULL)
! 227: set_runaspw(runas_user);
! 228: } else
! 229: set_runaspw(runas_user ? runas_user : def_runas_default);
! 230:
! 231: if (!update_defaults(SETDEF_RUNAS))
! 232: log_error(NO_STDERR|NO_EXIT, _("problem with defaults entries"));
! 233:
! 234: if (def_fqdn)
! 235: set_fqdn(); /* deferred until after sudoers is parsed */
! 236:
! 237: /* Set login class if applicable. */
! 238: set_loginclass(sudo_user.pw);
! 239:
! 240: restore_perms();
! 241:
! 242: return TRUE;
! 243: }
! 244:
! 245: static void
! 246: sudoers_policy_close(int exit_status, int error_code)
! 247: {
! 248: if (sigsetjmp(error_jmp, 1)) {
! 249: /* called via error(), errorx() or log_error() */
! 250: return;
! 251: }
! 252:
! 253: /* We do not currently log the exit status. */
! 254: if (error_code)
! 255: warningx(_("unable to execute %s: %s"), safe_cmnd, strerror(error_code));
! 256:
! 257: /* Close the session we opened in sudoers_policy_init_session(). */
! 258: if (ISSET(sudo_mode, MODE_RUN|MODE_EDIT))
! 259: (void)sudo_auth_end_session(runas_pw);
! 260:
! 261: /* Free remaining references to password and group entries. */
! 262: pw_delref(sudo_user.pw);
! 263: pw_delref(runas_pw);
! 264: if (runas_gr != NULL)
! 265: gr_delref(runas_gr);
! 266: if (user_group_list != NULL)
! 267: grlist_delref(user_group_list);
! 268: }
! 269:
! 270: /*
! 271: * The init_session function is called before executing the command
! 272: * and before uid/gid changes occur.
! 273: */
! 274: static int
! 275: sudoers_policy_init_session(struct passwd *pwd)
! 276: {
! 277: if (sigsetjmp(error_jmp, 1)) {
! 278: /* called via error(), errorx() or log_error() */
! 279: return -1;
! 280: }
! 281:
! 282: return sudo_auth_begin_session(pwd);
! 283: }
! 284:
! 285: static int
! 286: sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
! 287: char **command_infop[], char **argv_out[], char **user_env_out[])
! 288: {
! 289: static char *command_info[32]; /* XXX */
! 290: char **edit_argv = NULL;
! 291: struct sudo_nss *nss;
! 292: int cmnd_status = -1, validated;
! 293: volatile int info_len = 0;
! 294: volatile int rval = TRUE;
! 295:
! 296: if (sigsetjmp(error_jmp, 1)) {
! 297: /* error recovery via error(), errorx() or log_error() */
! 298: rval = -1;
! 299: goto done;
! 300: }
! 301:
! 302: /* Is root even allowed to run sudo? */
! 303: if (user_uid == 0 && !def_root_sudo) {
! 304: warningx(_("sudoers specifies that root is not allowed to sudo"));
! 305: goto bad;
! 306: }
! 307:
! 308: /* Check for -C overriding def_closefrom. */
! 309: if (user_closefrom >= 0 && user_closefrom != def_closefrom) {
! 310: if (!def_closefrom_override) {
! 311: warningx(_("you are not permitted to use the -C option"));
! 312: goto bad;
! 313: }
! 314: def_closefrom = user_closefrom;
! 315: }
! 316:
! 317: set_perms(PERM_INITIAL);
! 318:
! 319: /* Environment variables specified on the command line. */
! 320: if (env_add != NULL && env_add[0] != NULL)
! 321: sudo_user.env_vars = env_add;
! 322:
! 323: /*
! 324: * Make a local copy of argc/argv, with special handling
! 325: * for pseudo-commands and the '-i' option.
! 326: */
! 327: if (argc == 0) {
! 328: NewArgc = 1;
! 329: NewArgv = emalloc2(NewArgc + 1, sizeof(char *));
! 330: NewArgv[0] = user_cmnd;
! 331: NewArgv[1] = NULL;
! 332: } else {
! 333: /* Must leave an extra slot before NewArgv for bash's --login */
! 334: NewArgc = argc;
! 335: NewArgv = emalloc2(NewArgc + 2, sizeof(char *));
! 336: memcpy(++NewArgv, argv, argc * sizeof(char *));
! 337: NewArgv[NewArgc] = NULL;
! 338: if (ISSET(sudo_mode, MODE_LOGIN_SHELL))
! 339: NewArgv[0] = estrdup(runas_pw->pw_shell);
! 340: }
! 341:
! 342: /* If given the -P option, set the "preserve_groups" flag. */
! 343: if (ISSET(sudo_mode, MODE_PRESERVE_GROUPS))
! 344: def_preserve_groups = TRUE;
! 345:
! 346: /* Find command in path */
! 347: cmnd_status = set_cmnd();
! 348: if (cmnd_status == -1) {
! 349: rval = -1;
! 350: goto done;
! 351: }
! 352:
! 353: #ifdef HAVE_SETLOCALE
! 354: if (!setlocale(LC_ALL, def_sudoers_locale)) {
! 355: warningx(_("unable to set locale to \"%s\", using \"C\""),
! 356: def_sudoers_locale);
! 357: setlocale(LC_ALL, "C");
! 358: }
! 359: #endif
! 360:
! 361: /*
! 362: * Check sudoers sources.
! 363: */
! 364: validated = FLAG_NO_USER | FLAG_NO_HOST;
! 365: tq_foreach_fwd(snl, nss) {
! 366: validated = nss->lookup(nss, validated, pwflag);
! 367:
! 368: if (ISSET(validated, VALIDATE_OK)) {
! 369: /* Handle "= auth" in netsvc.conf */
! 370: if (nss->ret_if_found)
! 371: break;
! 372: } else {
! 373: /* Handle [NOTFOUND=return] */
! 374: if (nss->ret_if_notfound)
! 375: break;
! 376: }
! 377: }
! 378:
! 379: if (safe_cmnd == NULL)
! 380: safe_cmnd = estrdup(user_cmnd);
! 381:
! 382: #ifdef HAVE_SETLOCALE
! 383: setlocale(LC_ALL, "");
! 384: #endif
! 385:
! 386: /* If only a group was specified, set runas_pw based on invoking user. */
! 387: if (runas_pw == NULL)
! 388: set_runaspw(user_name);
! 389:
! 390: /*
! 391: * Look up the timestamp dir owner if one is specified.
! 392: */
! 393: if (def_timestampowner) {
! 394: struct passwd *pw;
! 395:
! 396: if (*def_timestampowner == '#')
! 397: pw = sudo_getpwuid(atoi(def_timestampowner + 1));
! 398: else
! 399: pw = sudo_getpwnam(def_timestampowner);
! 400: if (!pw)
! 401: log_error(0, _("timestamp owner (%s): No such user"),
! 402: def_timestampowner);
! 403: timestamp_uid = pw->pw_uid;
! 404: pw_delref(pw);
! 405: }
! 406:
! 407: /* If no command line args and "shell_noargs" is not set, error out. */
! 408: if (ISSET(sudo_mode, MODE_IMPLIED_SHELL) && !def_shell_noargs) {
! 409: rval = -2; /* usage error */
! 410: goto done;
! 411: }
! 412:
! 413: /* Bail if a tty is required and we don't have one. */
! 414: if (def_requiretty) {
! 415: int fd = open(_PATH_TTY, O_RDWR|O_NOCTTY);
! 416: if (fd == -1) {
! 417: audit_failure(NewArgv, _("no tty"));
! 418: warningx(_("sorry, you must have a tty to run sudo"));
! 419: goto bad;
! 420: } else
! 421: (void) close(fd);
! 422: }
! 423:
! 424: /*
! 425: * We don't reset the environment for sudoedit or if the user
! 426: * specified the -E command line flag and they have setenv privs.
! 427: */
! 428: if (ISSET(sudo_mode, MODE_EDIT) ||
! 429: (ISSET(sudo_mode, MODE_PRESERVE_ENV) && def_setenv))
! 430: def_env_reset = FALSE;
! 431:
! 432: /* Build a new environment that avoids any nasty bits. */
! 433: rebuild_env();
! 434:
! 435: /* Require a password if sudoers says so. */
! 436: rval = check_user(validated, sudo_mode);
! 437: if (rval != TRUE)
! 438: goto done;
! 439:
! 440: /* If run as root with SUDO_USER set, set sudo_user.pw to that user. */
! 441: /* XXX - causes confusion when root is not listed in sudoers */
! 442: if (sudo_mode & (MODE_RUN | MODE_EDIT) && prev_user != NULL) {
! 443: if (user_uid == 0 && strcmp(prev_user, "root") != 0) {
! 444: struct passwd *pw;
! 445:
! 446: if ((pw = sudo_getpwnam(prev_user)) != NULL) {
! 447: if (sudo_user.pw != NULL)
! 448: pw_delref(sudo_user.pw);
! 449: sudo_user.pw = pw;
! 450: }
! 451: }
! 452: }
! 453:
! 454: /* If the user was not allowed to run the command we are done. */
! 455: if (!ISSET(validated, VALIDATE_OK)) {
! 456: if (ISSET(validated, FLAG_NO_USER | FLAG_NO_HOST)) {
! 457: audit_failure(NewArgv, _("No user or host"));
! 458: log_denial(validated, 1);
! 459: } else {
! 460: if (def_path_info) {
! 461: /*
! 462: * We'd like to not leak path info at all here, but that can
! 463: * *really* confuse the users. To really close the leak we'd
! 464: * have to say "not allowed to run foo" even when the problem
! 465: * is just "no foo in path" since the user can trivially set
! 466: * their path to just contain a single dir.
! 467: */
! 468: log_denial(validated,
! 469: !(cmnd_status == NOT_FOUND_DOT || cmnd_status == NOT_FOUND));
! 470: if (cmnd_status == NOT_FOUND)
! 471: warningx(_("%s: command not found"), user_cmnd);
! 472: else if (cmnd_status == NOT_FOUND_DOT)
! 473: warningx(_("ignoring `%s' found in '.'\nUse `sudo ./%s' if this is the `%s' you wish to run."), user_cmnd, user_cmnd, user_cmnd);
! 474: } else {
! 475: /* Just tell the user they are not allowed to run foo. */
! 476: log_denial(validated, 1);
! 477: }
! 478: audit_failure(NewArgv, _("validation failure"));
! 479: }
! 480: goto bad;
! 481: }
! 482:
! 483: /* Create Ubuntu-style dot file to indicate sudo was successful. */
! 484: create_admin_success_flag();
! 485:
! 486: /* Finally tell the user if the command did not exist. */
! 487: if (cmnd_status == NOT_FOUND_DOT) {
! 488: audit_failure(NewArgv, _("command in current directory"));
! 489: warningx(_("ignoring `%s' found in '.'\nUse `sudo ./%s' if this is the `%s' you wish to run."), user_cmnd, user_cmnd, user_cmnd);
! 490: goto bad;
! 491: } else if (cmnd_status == NOT_FOUND) {
! 492: audit_failure(NewArgv, _("%s: command not found"), user_cmnd);
! 493: warningx(_("%s: command not found"), user_cmnd);
! 494: goto bad;
! 495: }
! 496:
! 497: /* If user specified env vars make sure sudoers allows it. */
! 498: if (ISSET(sudo_mode, MODE_RUN) && !def_setenv) {
! 499: if (ISSET(sudo_mode, MODE_PRESERVE_ENV)) {
! 500: warningx(_("sorry, you are not allowed to preserve the environment"));
! 501: goto bad;
! 502: } else
! 503: validate_env_vars(sudo_user.env_vars);
! 504: }
! 505:
! 506: if (ISSET(sudo_mode, (MODE_RUN | MODE_EDIT)) && (def_log_input || def_log_output)) {
! 507: if (def_iolog_file && def_iolog_dir) {
! 508: command_info[info_len++] = expand_iolog_path("iolog_path=",
! 509: def_iolog_dir, def_iolog_file, &sudo_user.iolog_file);
! 510: sudo_user.iolog_file++;
! 511: }
! 512: if (def_log_input) {
! 513: command_info[info_len++] = estrdup("iolog_stdin=true");
! 514: command_info[info_len++] = estrdup("iolog_ttyin=true");
! 515: }
! 516: if (def_log_output) {
! 517: command_info[info_len++] = estrdup("iolog_stdout=true");
! 518: command_info[info_len++] = estrdup("iolog_stderr=true");
! 519: command_info[info_len++] = estrdup("iolog_ttyout=true");
! 520: }
! 521: if (def_compress_io)
! 522: command_info[info_len++] = estrdup("iolog_compress=true");
! 523: }
! 524:
! 525: log_allowed(validated);
! 526: if (ISSET(sudo_mode, MODE_CHECK))
! 527: rval = display_cmnd(snl, list_pw ? list_pw : sudo_user.pw);
! 528: else if (ISSET(sudo_mode, MODE_LIST))
! 529: display_privs(snl, list_pw ? list_pw : sudo_user.pw); /* XXX - return val */
! 530:
! 531: /* Cleanup sudoers sources */
! 532: tq_foreach_fwd(snl, nss) {
! 533: nss->close(nss);
! 534: }
! 535: if (def_group_plugin)
! 536: group_plugin_unload();
! 537:
! 538: if (ISSET(sudo_mode, (MODE_VALIDATE|MODE_CHECK|MODE_LIST))) {
! 539: /* rval already set appropriately */
! 540: goto done;
! 541: }
! 542:
! 543: /*
! 544: * Set umask based on sudoers.
! 545: * If user's umask is more restrictive, OR in those bits too
! 546: * unless umask_override is set.
! 547: */
! 548: if (def_umask != 0777) {
! 549: mode_t mask = def_umask;
! 550: if (!def_umask_override) {
! 551: mode_t omask = umask(mask);
! 552: mask |= omask;
! 553: umask(omask);
! 554: }
! 555: easprintf(&command_info[info_len++], "umask=0%o", (unsigned int)mask);
! 556: }
! 557:
! 558: if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
! 559: char *p;
! 560:
! 561: /* Convert /bin/sh -> -sh so shell knows it is a login shell */
! 562: if ((p = strrchr(NewArgv[0], '/')) == NULL)
! 563: p = NewArgv[0];
! 564: *p = '-';
! 565: NewArgv[0] = p;
! 566:
! 567: /* Set cwd to run user's homedir. */
! 568: command_info[info_len++] = fmt_string("cwd", runas_pw->pw_dir);
! 569:
! 570: /*
! 571: * Newer versions of bash require the --login option to be used
! 572: * in conjunction with the -c option even if the shell name starts
! 573: * with a '-'. Unfortunately, bash 1.x uses -login, not --login
! 574: * so this will cause an error for that.
! 575: */
! 576: if (NewArgc > 1 && strcmp(NewArgv[0], "-bash") == 0 &&
! 577: strcmp(NewArgv[1], "-c") == 0) {
! 578: /* Use the extra slot before NewArgv so we can store --login. */
! 579: NewArgv--;
! 580: NewArgc++;
! 581: NewArgv[0] = NewArgv[1];
! 582: NewArgv[1] = "--login";
! 583: }
! 584:
! 585: #if defined(__linux__) || defined(_AIX)
! 586: /* Insert system-wide environment variables. */
! 587: read_env_file(_PATH_ENVIRONMENT, TRUE);
! 588: #endif
! 589: }
! 590:
! 591: /* Insert system-wide environment variables. */
! 592: if (def_env_file)
! 593: read_env_file(def_env_file, FALSE);
! 594:
! 595: /* Insert user-specified environment variables. */
! 596: insert_env_vars(sudo_user.env_vars);
! 597:
! 598: /* Restore signal handlers before we exec. */
! 599: (void) sigaction(SIGINT, &saved_sa_int, NULL);
! 600: (void) sigaction(SIGQUIT, &saved_sa_quit, NULL);
! 601: (void) sigaction(SIGTSTP, &saved_sa_tstp, NULL);
! 602:
! 603: if (ISSET(sudo_mode, MODE_EDIT)) {
! 604: char *editor = find_editor(NewArgc - 1, NewArgv + 1, &edit_argv);
! 605: if (editor == NULL)
! 606: goto bad;
! 607: command_info[info_len++] = fmt_string("command", editor);
! 608: command_info[info_len++] = estrdup("sudoedit=true");
! 609: } else {
! 610: command_info[info_len++] = fmt_string("command", safe_cmnd);
! 611: }
! 612: if (def_stay_setuid) {
! 613: easprintf(&command_info[info_len++], "runas_uid=%u",
! 614: (unsigned int)user_uid);
! 615: easprintf(&command_info[info_len++], "runas_gid=%u",
! 616: (unsigned int)user_gid);
! 617: easprintf(&command_info[info_len++], "runas_euid=%u",
! 618: (unsigned int)runas_pw->pw_uid);
! 619: easprintf(&command_info[info_len++], "runas_egid=%u",
! 620: runas_gr ? (unsigned int)runas_gr->gr_gid :
! 621: (unsigned int)runas_pw->pw_gid);
! 622: } else {
! 623: easprintf(&command_info[info_len++], "runas_uid=%u",
! 624: (unsigned int)runas_pw->pw_uid);
! 625: easprintf(&command_info[info_len++], "runas_gid=%u",
! 626: runas_gr ? (unsigned int)runas_gr->gr_gid :
! 627: (unsigned int)runas_pw->pw_gid);
! 628: }
! 629: if (def_preserve_groups) {
! 630: command_info[info_len++] = "preserve_groups=true";
! 631: } else {
! 632: int i, len;
! 633: size_t glsize;
! 634: char *cp, *gid_list;
! 635: struct group_list *grlist = get_group_list(runas_pw);
! 636:
! 637: glsize = sizeof("runas_groups=") - 1 + (grlist->ngids * (MAX_UID_T_LEN + 1));
! 638: gid_list = emalloc(glsize);
! 639: memcpy(gid_list, "runas_groups=", sizeof("runas_groups=") - 1);
! 640: cp = gid_list + sizeof("runas_groups=") - 1;
! 641: for (i = 0; i < grlist->ngids; i++) {
! 642: /* XXX - check rval */
! 643: len = snprintf(cp, glsize - (cp - gid_list), "%s%u",
! 644: i ? "," : "", (unsigned int) grlist->gids[i]);
! 645: cp += len;
! 646: }
! 647: command_info[info_len++] = gid_list;
! 648: grlist_delref(grlist);
! 649: }
! 650: if (def_closefrom >= 0)
! 651: easprintf(&command_info[info_len++], "closefrom=%d", def_closefrom);
! 652: if (def_noexec)
! 653: command_info[info_len++] = estrdup("noexec=true");
! 654: if (def_noexec_file)
! 655: command_info[info_len++] = fmt_string("noexec_file", def_noexec_file);
! 656: if (def_set_utmp)
! 657: command_info[info_len++] = estrdup("set_utmp=true");
! 658: if (def_use_pty)
! 659: command_info[info_len++] = estrdup("use_pty=true");
! 660: if (def_utmp_runas)
! 661: command_info[info_len++] = fmt_string("utmp_user", runas_pw->pw_name);
! 662: #ifdef HAVE_LOGIN_CAP_H
! 663: if (lc != NULL)
! 664: command_info[info_len++] = fmt_string("login_class", lc->lc_class);
! 665: #endif /* HAVE_LOGIN_CAP_H */
! 666: #ifdef HAVE_SELINUX
! 667: if (user_role != NULL)
! 668: command_info[info_len++] = fmt_string("selinux_role", user_role);
! 669: if (user_type != NULL)
! 670: command_info[info_len++] = fmt_string("selinux_type", user_type);
! 671: #endif /* HAVE_SELINUX */
! 672:
! 673: /* Must audit before uid change. */
! 674: audit_success(NewArgv);
! 675:
! 676: *command_infop = command_info;
! 677:
! 678: *argv_out = edit_argv ? edit_argv : NewArgv;
! 679: *user_env_out = env_get(); /* our private copy */
! 680:
! 681: goto done;
! 682:
! 683: bad:
! 684: rval = FALSE;
! 685:
! 686: done:
! 687: rewind_perms();
! 688:
! 689: /* Close the password and group files and free up memory. */
! 690: sudo_endpwent();
! 691: sudo_endgrent();
! 692:
! 693: return rval;
! 694: }
! 695:
! 696: static int
! 697: sudoers_policy_check(int argc, char * const argv[], char *env_add[],
! 698: char **command_infop[], char **argv_out[], char **user_env_out[])
! 699: {
! 700: if (!ISSET(sudo_mode, MODE_EDIT))
! 701: SET(sudo_mode, MODE_RUN);
! 702:
! 703: return sudoers_policy_main(argc, argv, 0, env_add, command_infop,
! 704: argv_out, user_env_out);
! 705: }
! 706:
! 707: static int
! 708: sudoers_policy_validate(void)
! 709: {
! 710: user_cmnd = "validate";
! 711: SET(sudo_mode, MODE_VALIDATE);
! 712:
! 713: return sudoers_policy_main(0, NULL, I_VERIFYPW, NULL, NULL, NULL, NULL);
! 714: }
! 715:
! 716: static void
! 717: sudoers_policy_invalidate(int remove)
! 718: {
! 719: user_cmnd = "kill";
! 720: if (sigsetjmp(error_jmp, 1) == 0) {
! 721: remove_timestamp(remove);
! 722: plugin_cleanup(0);
! 723: }
! 724: }
! 725:
! 726: static int
! 727: sudoers_policy_list(int argc, char * const argv[], int verbose,
! 728: const char *list_user)
! 729: {
! 730: int rval;
! 731:
! 732: user_cmnd = "list";
! 733: if (argc)
! 734: SET(sudo_mode, MODE_CHECK);
! 735: else
! 736: SET(sudo_mode, MODE_LIST);
! 737: if (verbose)
! 738: long_list = 1;
! 739: if (list_user) {
! 740: list_pw = sudo_getpwnam(list_user);
! 741: if (list_pw == NULL) {
! 742: warningx(_("unknown user: %s"), list_user);
! 743: return -1;
! 744: }
! 745: }
! 746: rval = sudoers_policy_main(argc, argv, I_LISTPW, NULL, NULL, NULL, NULL);
! 747: if (list_user) {
! 748: pw_delref(list_pw);
! 749: list_pw = NULL;
! 750: }
! 751:
! 752: return rval;
! 753: }
! 754:
! 755: /*
! 756: * Initialize timezone, set umask, fill in ``sudo_user'' struct and
! 757: * load the ``interfaces'' array.
! 758: */
! 759: static void
! 760: init_vars(char * const envp[])
! 761: {
! 762: char * const * ep;
! 763:
! 764: #ifdef HAVE_TZSET
! 765: (void) tzset(); /* set the timezone if applicable */
! 766: #endif /* HAVE_TZSET */
! 767:
! 768: for (ep = envp; *ep; ep++) {
! 769: /* XXX - don't fill in if empty string */
! 770: switch (**ep) {
! 771: case 'K':
! 772: if (strncmp("KRB5CCNAME=", *ep, 11) == 0)
! 773: user_ccname = *ep + 11;
! 774: break;
! 775: case 'P':
! 776: if (strncmp("PATH=", *ep, 5) == 0)
! 777: user_path = *ep + 5;
! 778: break;
! 779: case 'S':
! 780: if (!user_prompt && strncmp("SUDO_PROMPT=", *ep, 12) == 0)
! 781: user_prompt = *ep + 12;
! 782: else if (strncmp("SUDO_USER=", *ep, 10) == 0)
! 783: prev_user = *ep + 10;
! 784: break;
! 785: }
! 786: }
! 787:
! 788: /*
! 789: * Get a local copy of the user's struct passwd with the shadow password
! 790: * if necessary. It is assumed that euid is 0 at this point so we
! 791: * can read the shadow passwd file if necessary.
! 792: */
! 793: if ((sudo_user.pw = sudo_getpwuid(user_uid)) == NULL) {
! 794: /*
! 795: * It is not unusual for users to place "sudo -k" in a .logout
! 796: * file which can cause sudo to be run during reboot after the
! 797: * YP/NIS/NIS+/LDAP/etc daemon has died.
! 798: */
! 799: if (sudo_mode == MODE_KILL || sudo_mode == MODE_INVALIDATE)
! 800: errorx(1, _("unknown uid: %u"), (unsigned int) user_uid);
! 801:
! 802: /* Need to make a fake struct passwd for the call to log_error(). */
! 803: sudo_user.pw = sudo_fakepwnamid(user_name, user_uid, user_gid);
! 804: log_error(0, _("unknown uid: %u"), (unsigned int) user_uid);
! 805: /* NOTREACHED */
! 806: }
! 807:
! 808: /*
! 809: * Get group list.
! 810: */
! 811: if (user_group_list == NULL)
! 812: user_group_list = get_group_list(sudo_user.pw);
! 813:
! 814: /* Set runas callback. */
! 815: sudo_defs_table[I_RUNAS_DEFAULT].callback = cb_runas_default;
! 816:
! 817: /* It is now safe to use log_error() and set_perms() */
! 818: }
! 819:
! 820: /*
! 821: * Fill in user_cmnd, user_args, user_base and user_stat variables
! 822: * and apply any command-specific defaults entries.
! 823: */
! 824: static int
! 825: set_cmnd(void)
! 826: {
! 827: int rval;
! 828: char *path = user_path;
! 829:
! 830: /* Resolve the path and return. */
! 831: rval = FOUND;
! 832: user_stat = emalloc(sizeof(struct stat));
! 833:
! 834: /* Default value for cmnd, overridden below. */
! 835: if (user_cmnd == NULL)
! 836: user_cmnd = NewArgv[0];
! 837:
! 838: if (sudo_mode & (MODE_RUN | MODE_EDIT | MODE_CHECK)) {
! 839: if (ISSET(sudo_mode, MODE_RUN | MODE_CHECK)) {
! 840: if (def_secure_path && !user_is_exempt())
! 841: path = def_secure_path;
! 842: set_perms(PERM_RUNAS);
! 843: rval = find_path(NewArgv[0], &user_cmnd, user_stat, path,
! 844: def_ignore_dot);
! 845: restore_perms();
! 846: if (rval != FOUND) {
! 847: /* Failed as root, try as invoking user. */
! 848: set_perms(PERM_USER);
! 849: rval = find_path(NewArgv[0], &user_cmnd, user_stat, path,
! 850: def_ignore_dot);
! 851: restore_perms();
! 852: }
! 853: }
! 854:
! 855: /* set user_args */
! 856: if (NewArgc > 1) {
! 857: char *to, *from, **av;
! 858: size_t size, n;
! 859:
! 860: /* Alloc and build up user_args. */
! 861: for (size = 0, av = NewArgv + 1; *av; av++)
! 862: size += strlen(*av) + 1;
! 863: user_args = emalloc(size);
! 864: if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) {
! 865: /*
! 866: * When running a command via a shell, the sudo front-end
! 867: * escapes potential meta chars. We unescape non-spaces
! 868: * for sudoers matching and logging purposes.
! 869: */
! 870: for (to = user_args, av = NewArgv + 1; (from = *av); av++) {
! 871: while (*from) {
! 872: if (from[0] == '\\' && !isspace((unsigned char)from[1]))
! 873: from++;
! 874: *to++ = *from++;
! 875: }
! 876: *to++ = ' ';
! 877: }
! 878: *--to = '\0';
! 879: } else {
! 880: for (to = user_args, av = NewArgv + 1; *av; av++) {
! 881: n = strlcpy(to, *av, size - (to - user_args));
! 882: if (n >= size - (to - user_args))
! 883: errorx(1, _("internal error, set_cmnd() overflow"));
! 884: to += n;
! 885: *to++ = ' ';
! 886: }
! 887: *--to = '\0';
! 888: }
! 889: }
! 890: }
! 891: if (strlen(user_cmnd) >= PATH_MAX)
! 892: errorx(1, _("%s: %s"), user_cmnd, strerror(ENAMETOOLONG));
! 893:
! 894: if ((user_base = strrchr(user_cmnd, '/')) != NULL)
! 895: user_base++;
! 896: else
! 897: user_base = user_cmnd;
! 898:
! 899: if (!update_defaults(SETDEF_CMND))
! 900: log_error(NO_STDERR|NO_EXIT, _("problem with defaults entries"));
! 901:
! 902: return rval;
! 903: }
! 904:
! 905: /*
! 906: * Open sudoers and sanity check mode/owner/type.
! 907: * Returns a handle to the sudoers file or NULL on error.
! 908: */
! 909: FILE *
! 910: open_sudoers(const char *sudoers, int doedit, int *keepopen)
! 911: {
! 912: struct stat statbuf;
! 913: FILE *fp = NULL;
! 914: int rootstat;
! 915:
! 916: /*
! 917: * Fix the mode and group on sudoers file from old default.
! 918: * Only works if file system is readable/writable by root.
! 919: */
! 920: if ((rootstat = stat_sudoers(sudoers, &statbuf)) == 0 &&
! 921: sudoers_uid == statbuf.st_uid && sudoers_mode != 0400 &&
! 922: (statbuf.st_mode & 0007777) == 0400) {
! 923:
! 924: if (chmod(sudoers, sudoers_mode) == 0) {
! 925: warningx(_("fixed mode on %s"), sudoers);
! 926: SET(statbuf.st_mode, sudoers_mode);
! 927: if (statbuf.st_gid != sudoers_gid) {
! 928: if (chown(sudoers, (uid_t) -1, sudoers_gid) == 0) {
! 929: warningx(_("set group on %s"), sudoers);
! 930: statbuf.st_gid = sudoers_gid;
! 931: } else
! 932: warning(_("unable to set group on %s"), sudoers);
! 933: }
! 934: } else
! 935: warning(_("unable to fix mode on %s"), sudoers);
! 936: }
! 937:
! 938: /*
! 939: * Sanity checks on sudoers file. Must be done as sudoers
! 940: * file owner. We already did a stat as root, so use that
! 941: * data if we can't stat as sudoers file owner.
! 942: */
! 943: set_perms(PERM_SUDOERS);
! 944:
! 945: if (rootstat != 0 && stat_sudoers(sudoers, &statbuf) != 0)
! 946: log_error(USE_ERRNO|NO_EXIT, _("unable to stat %s"), sudoers);
! 947: else if (!S_ISREG(statbuf.st_mode))
! 948: log_error(NO_EXIT, _("%s is not a regular file"), sudoers);
! 949: else if ((statbuf.st_mode & 07577) != sudoers_mode)
! 950: log_error(NO_EXIT, _("%s is mode 0%o, should be 0%o"), sudoers,
! 951: (unsigned int) (statbuf.st_mode & 07777),
! 952: (unsigned int) sudoers_mode);
! 953: else if (statbuf.st_uid != sudoers_uid)
! 954: log_error(NO_EXIT, _("%s is owned by uid %u, should be %u"), sudoers,
! 955: (unsigned int) statbuf.st_uid, (unsigned int) sudoers_uid);
! 956: else if (statbuf.st_gid != sudoers_gid && ISSET(statbuf.st_mode, S_IRGRP|S_IWGRP))
! 957: log_error(NO_EXIT, _("%s is owned by gid %u, should be %u"), sudoers,
! 958: (unsigned int) statbuf.st_gid, (unsigned int) sudoers_gid);
! 959: else if ((fp = fopen(sudoers, "r")) == NULL)
! 960: log_error(USE_ERRNO|NO_EXIT, _("unable to open %s"), sudoers);
! 961: else {
! 962: /*
! 963: * Make sure we can actually read sudoers so we can present the
! 964: * user with a reasonable error message (unlike the lexer).
! 965: */
! 966: if (statbuf.st_size != 0 && fgetc(fp) == EOF) {
! 967: log_error(USE_ERRNO|NO_EXIT, _("unable to read %s"), sudoers);
! 968: fclose(fp);
! 969: fp = NULL;
! 970: }
! 971: }
! 972:
! 973: if (fp != NULL) {
! 974: rewind(fp);
! 975: (void) fcntl(fileno(fp), F_SETFD, 1);
! 976: }
! 977:
! 978: restore_perms(); /* change back to root */
! 979: return fp;
! 980: }
! 981:
! 982: #ifdef HAVE_LOGIN_CAP_H
! 983: static void
! 984: set_loginclass(struct passwd *pw)
! 985: {
! 986: int errflags;
! 987:
! 988: /*
! 989: * Don't make it a fatal error if the user didn't specify the login
! 990: * class themselves. We do this because if login.conf gets
! 991: * corrupted we want the admin to be able to use sudo to fix it.
! 992: */
! 993: if (login_class)
! 994: errflags = NO_MAIL|MSG_ONLY;
! 995: else
! 996: errflags = NO_MAIL|MSG_ONLY|NO_EXIT;
! 997:
! 998: if (login_class && strcmp(login_class, "-") != 0) {
! 999: if (user_uid != 0 &&
! 1000: strcmp(runas_user ? runas_user : def_runas_default, "root") != 0)
! 1001: errorx(1, _("only root can use `-c %s'"), login_class);
! 1002: } else {
! 1003: login_class = pw->pw_class;
! 1004: if (!login_class || !*login_class)
! 1005: login_class =
! 1006: (pw->pw_uid == 0) ? LOGIN_DEFROOTCLASS : LOGIN_DEFCLASS;
! 1007: }
! 1008:
! 1009: lc = login_getclass(login_class);
! 1010: if (!lc || !lc->lc_class || strcmp(lc->lc_class, login_class) != 0) {
! 1011: log_error(errflags, _("unknown login class: %s"), login_class);
! 1012: if (!lc)
! 1013: lc = login_getclass(NULL); /* needed for login_getstyle() later */
! 1014: }
! 1015: }
! 1016: #else
! 1017: static void
! 1018: set_loginclass(struct passwd *pw)
! 1019: {
! 1020: }
! 1021: #endif /* HAVE_LOGIN_CAP_H */
! 1022:
! 1023: /*
! 1024: * Look up the fully qualified domain name and set user_host and user_shost.
! 1025: */
! 1026: void
! 1027: set_fqdn(void)
! 1028: {
! 1029: #ifdef HAVE_GETADDRINFO
! 1030: struct addrinfo *res0, hint;
! 1031: #else
! 1032: struct hostent *hp;
! 1033: #endif
! 1034: char *p;
! 1035:
! 1036: #ifdef HAVE_GETADDRINFO
! 1037: zero_bytes(&hint, sizeof(hint));
! 1038: hint.ai_family = PF_UNSPEC;
! 1039: hint.ai_flags = AI_CANONNAME;
! 1040: if (getaddrinfo(user_host, NULL, &hint, &res0) != 0) {
! 1041: #else
! 1042: if (!(hp = gethostbyname(user_host))) {
! 1043: #endif
! 1044: log_error(MSG_ONLY|NO_EXIT,
! 1045: _("unable to resolve host %s"), user_host);
! 1046: } else {
! 1047: if (user_shost != user_host)
! 1048: efree(user_shost);
! 1049: efree(user_host);
! 1050: #ifdef HAVE_GETADDRINFO
! 1051: user_host = estrdup(res0->ai_canonname);
! 1052: freeaddrinfo(res0);
! 1053: #else
! 1054: user_host = estrdup(hp->h_name);
! 1055: #endif
! 1056: }
! 1057: if ((p = strchr(user_host, '.')) != NULL)
! 1058: user_shost = estrndup(user_host, (size_t)(p - user_host));
! 1059: else
! 1060: user_shost = user_host;
! 1061: }
! 1062:
! 1063: /*
! 1064: * Get passwd entry for the user we are going to run commands as
! 1065: * and store it in runas_pw. By default, commands run as "root".
! 1066: */
! 1067: void
! 1068: set_runaspw(const char *user)
! 1069: {
! 1070: if (runas_pw != NULL)
! 1071: pw_delref(runas_pw);
! 1072: if (*user == '#') {
! 1073: if ((runas_pw = sudo_getpwuid(atoi(user + 1))) == NULL)
! 1074: runas_pw = sudo_fakepwnam(user, runas_gr ? runas_gr->gr_gid : 0);
! 1075: } else {
! 1076: if ((runas_pw = sudo_getpwnam(user)) == NULL)
! 1077: log_error(NO_MAIL|MSG_ONLY, _("unknown user: %s"), user);
! 1078: }
! 1079: }
! 1080:
! 1081: /*
! 1082: * Get group entry for the group we are going to run commands as
! 1083: * and store it in runas_gr.
! 1084: */
! 1085: static void
! 1086: set_runasgr(const char *group)
! 1087: {
! 1088: if (runas_gr != NULL)
! 1089: gr_delref(runas_gr);
! 1090: if (*group == '#') {
! 1091: if ((runas_gr = sudo_getgrgid(atoi(group + 1))) == NULL)
! 1092: runas_gr = sudo_fakegrnam(group);
! 1093: } else {
! 1094: if ((runas_gr = sudo_getgrnam(group)) == NULL)
! 1095: log_error(NO_MAIL|MSG_ONLY, _("unknown group: %s"), group);
! 1096: }
! 1097: }
! 1098:
! 1099: /*
! 1100: * Callback for runas_default sudoers setting.
! 1101: */
! 1102: static int
! 1103: cb_runas_default(const char *user)
! 1104: {
! 1105: /* Only reset runaspw if user didn't specify one. */
! 1106: if (!runas_user && !runas_group)
! 1107: set_runaspw(user);
! 1108: return TRUE;
! 1109: }
! 1110:
! 1111: /*
! 1112: * Cleanup hook for error()/errorx()
! 1113: */
! 1114: void
! 1115: plugin_cleanup(int gotsignal)
! 1116: {
! 1117: struct sudo_nss *nss;
! 1118:
! 1119: if (!gotsignal) {
! 1120: if (snl != NULL) {
! 1121: tq_foreach_fwd(snl, nss)
! 1122: nss->close(nss);
! 1123: }
! 1124: if (def_group_plugin)
! 1125: group_plugin_unload();
! 1126: sudo_endpwent();
! 1127: sudo_endgrent();
! 1128: }
! 1129: }
! 1130:
! 1131: static int
! 1132: sudoers_policy_version(int verbose)
! 1133: {
! 1134: if (sigsetjmp(error_jmp, 1)) {
! 1135: /* error recovery via error(), errorx() or log_error() */
! 1136: return -1;
! 1137: }
! 1138:
! 1139: sudo_printf(SUDO_CONV_INFO_MSG, _("Sudoers policy plugin version %s\n"),
! 1140: PACKAGE_VERSION);
! 1141: sudo_printf(SUDO_CONV_INFO_MSG, _("Sudoers file grammar version %d\n"),
! 1142: SUDOERS_GRAMMAR_VERSION);
! 1143:
! 1144: if (verbose) {
! 1145: sudo_printf(SUDO_CONV_INFO_MSG, _("\nSudoers path: %s\n"), sudoers_file);
! 1146: #ifdef HAVE_LDAP
! 1147: # ifdef _PATH_NSSWITCH_CONF
! 1148: sudo_printf(SUDO_CONV_INFO_MSG, _("nsswitch path: %s\n"), _PATH_NSSWITCH_CONF);
! 1149: # endif
! 1150: sudo_printf(SUDO_CONV_INFO_MSG, _("ldap.conf path: %s\n"), _PATH_LDAP_CONF);
! 1151: sudo_printf(SUDO_CONV_INFO_MSG, _("ldap.secret path: %s\n"), _PATH_LDAP_SECRET);
! 1152: #endif
! 1153: dump_auth_methods();
! 1154: dump_defaults();
! 1155: sudo_printf(SUDO_CONV_INFO_MSG, "\n");
! 1156: dump_interfaces(interfaces_string);
! 1157: sudo_printf(SUDO_CONV_INFO_MSG, "\n");
! 1158: }
! 1159: return TRUE;
! 1160: }
! 1161:
! 1162: static int
! 1163: deserialize_info(char * const settings[], char * const user_info[])
! 1164: {
! 1165: char * const *cur;
! 1166: const char *p, *groups = NULL;
! 1167: int flags = 0;
! 1168:
! 1169: #define MATCHES(s, v) (strncmp(s, v, sizeof(v) - 1) == 0)
! 1170:
! 1171: /* Parse command line settings. */
! 1172: user_closefrom = -1;
! 1173: for (cur = settings; *cur != NULL; cur++) {
! 1174: if (MATCHES(*cur, "closefrom=")) {
! 1175: user_closefrom = atoi(*cur + sizeof("closefrom=") - 1);
! 1176: continue;
! 1177: }
! 1178: if (MATCHES(*cur, "debug_level=")) {
! 1179: debug_level = atoi(*cur + sizeof("debug_level=") - 1);
! 1180: continue;
! 1181: }
! 1182: if (MATCHES(*cur, "runas_user=")) {
! 1183: runas_user = *cur + sizeof("runas_user=") - 1;
! 1184: continue;
! 1185: }
! 1186: if (MATCHES(*cur, "runas_group=")) {
! 1187: runas_group = *cur + sizeof("runas_group=") - 1;
! 1188: continue;
! 1189: }
! 1190: if (MATCHES(*cur, "prompt=")) {
! 1191: user_prompt = *cur + sizeof("prompt=") - 1;
! 1192: def_passprompt_override = TRUE;
! 1193: continue;
! 1194: }
! 1195: if (MATCHES(*cur, "set_home=")) {
! 1196: if (atobool(*cur + sizeof("set_home=") - 1) == TRUE)
! 1197: SET(flags, MODE_RESET_HOME);
! 1198: continue;
! 1199: }
! 1200: if (MATCHES(*cur, "preserve_environment=")) {
! 1201: if (atobool(*cur + sizeof("preserve_environment=") - 1) == TRUE)
! 1202: SET(flags, MODE_PRESERVE_ENV);
! 1203: continue;
! 1204: }
! 1205: if (MATCHES(*cur, "run_shell=")) {
! 1206: if (atobool(*cur + sizeof("run_shell=") - 1) == TRUE)
! 1207: SET(flags, MODE_SHELL);
! 1208: continue;
! 1209: }
! 1210: if (MATCHES(*cur, "login_shell=")) {
! 1211: if (atobool(*cur + sizeof("login_shell=") - 1) == TRUE) {
! 1212: SET(flags, MODE_LOGIN_SHELL);
! 1213: def_env_reset = TRUE;
! 1214: }
! 1215: continue;
! 1216: }
! 1217: if (MATCHES(*cur, "implied_shell=")) {
! 1218: if (atobool(*cur + sizeof("implied_shell=") - 1) == TRUE)
! 1219: SET(flags, MODE_IMPLIED_SHELL);
! 1220: continue;
! 1221: }
! 1222: if (MATCHES(*cur, "preserve_groups=")) {
! 1223: if (atobool(*cur + sizeof("preserve_groups=") - 1) == TRUE)
! 1224: SET(flags, MODE_PRESERVE_GROUPS);
! 1225: continue;
! 1226: }
! 1227: if (MATCHES(*cur, "ignore_ticket=")) {
! 1228: if (atobool(*cur + sizeof("ignore_ticket=") - 1) == TRUE)
! 1229: SET(flags, MODE_IGNORE_TICKET);
! 1230: continue;
! 1231: }
! 1232: if (MATCHES(*cur, "noninteractive=")) {
! 1233: if (atobool(*cur + sizeof("noninteractive=") - 1) == TRUE)
! 1234: SET(flags, MODE_NONINTERACTIVE);
! 1235: continue;
! 1236: }
! 1237: if (MATCHES(*cur, "sudoedit=")) {
! 1238: if (atobool(*cur + sizeof("sudoedit=") - 1) == TRUE)
! 1239: SET(flags, MODE_EDIT);
! 1240: continue;
! 1241: }
! 1242: if (MATCHES(*cur, "login_class=")) {
! 1243: login_class = *cur + sizeof("login_class=") - 1;
! 1244: def_use_loginclass = TRUE;
! 1245: continue;
! 1246: }
! 1247: #ifdef HAVE_SELINUX
! 1248: if (MATCHES(*cur, "selinux_role=")) {
! 1249: user_role = *cur + sizeof("selinux_role=") - 1;
! 1250: continue;
! 1251: }
! 1252: if (MATCHES(*cur, "selinux_type=")) {
! 1253: user_type = *cur + sizeof("selinux_type=") - 1;
! 1254: continue;
! 1255: }
! 1256: #endif /* HAVE_SELINUX */
! 1257: #ifdef HAVE_BSD_AUTH_H
! 1258: if (MATCHES(*cur, "bsdauth_type=")) {
! 1259: login_style = *cur + sizeof("bsdauth_type=") - 1;
! 1260: continue;
! 1261: }
! 1262: #endif /* HAVE_BSD_AUTH_H */
! 1263: #if !defined(HAVE_GETPROGNAME) && !defined(HAVE___PROGNAME)
! 1264: if (MATCHES(*cur, "progname=")) {
! 1265: setprogname(*cur + sizeof("progname=") - 1);
! 1266: continue;
! 1267: }
! 1268: #endif
! 1269: if (MATCHES(*cur, "network_addrs=")) {
! 1270: interfaces_string = *cur + sizeof("network_addrs=") - 1;
! 1271: set_interfaces(interfaces_string);
! 1272: continue;
! 1273: }
! 1274: if (MATCHES(*cur, "sudoers_file=")) {
! 1275: sudoers_file = *cur + sizeof("sudoers_file=") - 1;
! 1276: continue;
! 1277: }
! 1278: if (MATCHES(*cur, "sudoers_uid=")) {
! 1279: sudoers_uid = (uid_t) atoi(*cur + sizeof("sudoers_uid=") - 1);
! 1280: continue;
! 1281: }
! 1282: if (MATCHES(*cur, "sudoers_gid=")) {
! 1283: sudoers_gid = (gid_t) atoi(*cur + sizeof("sudoers_gid=") - 1);
! 1284: continue;
! 1285: }
! 1286: if (MATCHES(*cur, "sudoers_mode=")) {
! 1287: sudoers_mode = (mode_t) strtol(*cur + sizeof("sudoers_mode=") - 1,
! 1288: NULL, 8);
! 1289: continue;
! 1290: }
! 1291: }
! 1292:
! 1293: for (cur = user_info; *cur != NULL; cur++) {
! 1294: if (MATCHES(*cur, "user=")) {
! 1295: user_name = estrdup(*cur + sizeof("user=") - 1);
! 1296: continue;
! 1297: }
! 1298: if (MATCHES(*cur, "uid=")) {
! 1299: user_uid = (uid_t) atoi(*cur + sizeof("uid=") - 1);
! 1300: continue;
! 1301: }
! 1302: if (MATCHES(*cur, "gid=")) {
! 1303: p = *cur + sizeof("gid=") - 1;
! 1304: user_gid = (gid_t) atoi(p);
! 1305: continue;
! 1306: }
! 1307: if (MATCHES(*cur, "groups=")) {
! 1308: groups = *cur + sizeof("groups=") - 1;
! 1309: continue;
! 1310: }
! 1311: if (MATCHES(*cur, "cwd=")) {
! 1312: user_cwd = estrdup(*cur + sizeof("cwd=") - 1);
! 1313: continue;
! 1314: }
! 1315: if (MATCHES(*cur, "tty=")) {
! 1316: user_tty = user_ttypath = estrdup(*cur + sizeof("tty=") - 1);
! 1317: if (strncmp(user_tty, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
! 1318: user_tty += sizeof(_PATH_DEV) - 1;
! 1319: continue;
! 1320: }
! 1321: if (MATCHES(*cur, "host=")) {
! 1322: user_host = user_shost = estrdup(*cur + sizeof("host=") - 1);
! 1323: if ((p = strchr(user_host, '.')))
! 1324: user_shost = estrndup(user_host, (size_t)(p - user_host));
! 1325: continue;
! 1326: }
! 1327: if (MATCHES(*cur, "lines=")) {
! 1328: sudo_user.lines = atoi(*cur + sizeof("lines=") - 1);
! 1329: continue;
! 1330: }
! 1331: if (MATCHES(*cur, "cols=")) {
! 1332: sudo_user.cols = atoi(*cur + sizeof("cols=") - 1);
! 1333: continue;
! 1334: }
! 1335: }
! 1336: if (user_cwd == NULL)
! 1337: user_cwd = "unknown";
! 1338: if (user_tty == NULL)
! 1339: user_tty = "unknown"; /* user_ttypath remains NULL */
! 1340:
! 1341: if (groups != NULL && groups[0] != '\0') {
! 1342: const char *cp;
! 1343: GETGROUPS_T *gids;
! 1344: int ngids;
! 1345:
! 1346: /* Count number of groups, including passwd gid. */
! 1347: ngids = 2;
! 1348: for (cp = groups; *cp != '\0'; cp++) {
! 1349: if (*cp == ',')
! 1350: ngids++;
! 1351: }
! 1352:
! 1353: /* The first gid in the list is the passwd group gid. */
! 1354: gids = emalloc2(ngids, sizeof(GETGROUPS_T));
! 1355: gids[0] = user_gid;
! 1356: ngids = 1;
! 1357: cp = groups;
! 1358: for (;;) {
! 1359: gids[ngids] = atoi(cp);
! 1360: if (gids[0] != gids[ngids])
! 1361: ngids++;
! 1362: cp = strchr(cp, ',');
! 1363: if (cp == NULL)
! 1364: break;
! 1365: cp++; /* skip over comma */
! 1366: }
! 1367: set_group_list(user_name, gids, ngids);
! 1368: efree(gids);
! 1369: }
! 1370:
! 1371: #undef MATCHES
! 1372: return flags;
! 1373: }
! 1374:
! 1375: static char *
! 1376: resolve_editor(char *editor, int nfiles, char **files, char ***argv_out)
! 1377: {
! 1378: char *cp, **nargv, *editor_path = NULL;
! 1379: int ac, i, nargc, wasblank;
! 1380:
! 1381: editor = estrdup(editor); /* becomes part of argv_out */
! 1382:
! 1383: /*
! 1384: * Split editor into an argument vector; editor is reused (do not free).
! 1385: * The EDITOR and VISUAL environment variables may contain command
! 1386: * line args so look for those and alloc space for them too.
! 1387: */
! 1388: nargc = 1;
! 1389: for (wasblank = FALSE, cp = editor; *cp != '\0'; cp++) {
! 1390: if (isblank((unsigned char) *cp))
! 1391: wasblank = TRUE;
! 1392: else if (wasblank) {
! 1393: wasblank = FALSE;
! 1394: nargc++;
! 1395: }
! 1396: }
! 1397: /* If we can't find the editor in the user's PATH, give up. */
! 1398: cp = strtok(editor, " \t");
! 1399: if (cp == NULL ||
! 1400: find_path(cp, &editor_path, NULL, getenv("PATH"), 0) != FOUND) {
! 1401: efree(editor);
! 1402: return NULL;
! 1403: }
! 1404: nargv = (char **) emalloc2(nargc + 1 + nfiles + 1, sizeof(char *));
! 1405: for (ac = 0; cp != NULL && ac < nargc; ac++) {
! 1406: nargv[ac] = cp;
! 1407: cp = strtok(NULL, " \t");
! 1408: }
! 1409: nargv[ac++] = "--";
! 1410: for (i = 0; i < nfiles; )
! 1411: nargv[ac++] = files[i++];
! 1412: nargv[ac] = NULL;
! 1413:
! 1414: *argv_out = nargv;
! 1415: return editor_path;
! 1416: }
! 1417:
! 1418: /*
! 1419: * Determine which editor to use. We don't need to worry about restricting
! 1420: * this to a "safe" editor since it runs with the uid of the invoking user,
! 1421: * not the runas (privileged) user.
! 1422: */
! 1423: static char *
! 1424: find_editor(int nfiles, char **files, char ***argv_out)
! 1425: {
! 1426: char *cp, *editor, *editor_path = NULL, **ev, *ev0[4];
! 1427:
! 1428: /*
! 1429: * If any of SUDO_EDITOR, VISUAL or EDITOR are set, choose the first one.
! 1430: */
! 1431: ev0[0] = "SUDO_EDITOR";
! 1432: ev0[1] = "VISUAL";
! 1433: ev0[2] = "EDITOR";
! 1434: ev0[3] = NULL;
! 1435: for (ev = ev0; *ev != NULL; ev++) {
! 1436: if ((editor = getenv(*ev)) != NULL && *editor != '\0') {
! 1437: editor_path = resolve_editor(editor, nfiles, files, argv_out);
! 1438: if (editor_path != NULL)
! 1439: break;
! 1440: }
! 1441: }
! 1442: if (editor_path == NULL) {
! 1443: /* def_editor could be a path, split it up */
! 1444: editor = estrdup(def_editor);
! 1445: cp = strtok(editor, ":");
! 1446: while (cp != NULL && editor_path == NULL) {
! 1447: editor_path = resolve_editor(cp, nfiles, files, argv_out);
! 1448: cp = strtok(NULL, ":");
! 1449: }
! 1450: if (editor_path)
! 1451: efree(editor);
! 1452: }
! 1453: if (!editor_path) {
! 1454: audit_failure(NewArgv, _("%s: command not found"), editor);
! 1455: warningx(_("%s: command not found"), editor);
! 1456: }
! 1457: return editor_path;
! 1458: }
! 1459:
! 1460: #ifdef USE_ADMIN_FLAG
! 1461: static void
! 1462: create_admin_success_flag(void)
! 1463: {
! 1464: struct stat statbuf;
! 1465: char flagfile[PATH_MAX];
! 1466: int fd, n;
! 1467:
! 1468: /* Check whether the user is in the admin group. */
! 1469: if (!user_in_group(sudo_user.pw, "admin"))
! 1470: return;
! 1471:
! 1472: /* Build path to flag file. */
! 1473: n = snprintf(flagfile, sizeof(flagfile), "%s/.sudo_as_admin_successful",
! 1474: user_dir);
! 1475: if (n <= 0 || n >= sizeof(flagfile))
! 1476: return;
! 1477:
! 1478: /* Create admin flag file if it doesn't already exist. */
! 1479: set_perms(PERM_USER);
! 1480: if (stat(flagfile, &statbuf) != 0) {
! 1481: fd = open(flagfile, O_CREAT|O_WRONLY|O_EXCL, 0644);
! 1482: close(fd);
! 1483: }
! 1484: restore_perms();
! 1485: }
! 1486: #else /* !USE_ADMIN_FLAG */
! 1487: static void
! 1488: create_admin_success_flag(void)
! 1489: {
! 1490: /* STUB */
! 1491: }
! 1492: #endif /* USE_ADMIN_FLAG */
! 1493:
! 1494: struct policy_plugin sudoers_policy = {
! 1495: SUDO_POLICY_PLUGIN,
! 1496: SUDO_API_VERSION,
! 1497: sudoers_policy_open,
! 1498: sudoers_policy_close,
! 1499: sudoers_policy_version,
! 1500: sudoers_policy_check,
! 1501: sudoers_policy_list,
! 1502: sudoers_policy_validate,
! 1503: sudoers_policy_invalidate,
! 1504: sudoers_policy_init_session
! 1505: };
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>