From 832f5cd5241e14cf85ae9a40e99636a713ec4859 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Thu, 13 Oct 2016 14:22:52 +0200 Subject: su: add control struct * setup logindefs loader by function rather than by global pointer * move basic booleans to the su_context struct Signed-off-by: Karel Zak --- login-utils/su-common.c | 251 ++++++++++++++++++++++-------------------------- 1 file changed, 114 insertions(+), 137 deletions(-) (limited to 'login-utils/su-common.c') diff --git a/login-utils/su-common.c b/login-utils/su-common.c index 98774051c..efc5d1583 100644 --- a/login-utils/su-common.c +++ b/login-utils/su-common.c @@ -80,33 +80,29 @@ enum { EXIT_ENOENT = 127 }; -static void run_shell(char const *, char const *, char **, size_t) - __attribute__ ((__noreturn__)); - -/* If true, pass the `-f' option to the subshell. */ -static bool fast_startup; - -/* If true, simulate a login instead of just starting a shell. */ -static bool simulate_login; - -/* If true, change some environment vars to indicate the user su'd to. */ -static bool change_environment; - -/* If true, then don't call setsid() with a command. */ -static int same_session = 0; +/* + * su/runuser control struct + */ +struct su_context { + pam_handle_t *pamh; /* PAM handler */ + struct pam_conv conv; /* PAM conversation */ + + unsigned int runuser :1, /* flase=su, true=runuser */ + fast_startup :1, /* pass the `-f' option to the subshell. */ + simulate_login :1, /* simulate a login instead of just starting a shell. */ + change_environment :1, /* change some environment vars to indicate the user su'd to.*/ + same_session :1, /* don't call setsid() with a command. */ + suppress_pam_info:1, /* don't print PAM info messages (Last login, etc.). */ + pam_has_session :1, /* PAM session opened */ + pam_has_cred :1, /* PAM cred established */ + restricted :1; /* false for root user */ +}; -/* SU_MODE_{RUNUSER,SU} */ -static int su_mode; -/* Don't print PAM info messages (Last login, etc.). */ -static int suppress_pam_info; +static void run_shell(struct su_context *, char const *, char const *, char **, size_t); -static bool _pam_session_opened; -static bool _pam_cred_established; static sig_atomic_t volatile caught_signal = false; -static pam_handle_t *pamh = NULL; -static int restricted = 1; /* zero for root user */ static const struct passwd * current_getpwuid(void) @@ -131,7 +127,7 @@ current_getpwuid(void) if SUCCESSFUL is true, they gave the correct password, etc. */ static void -log_syslog(struct passwd const * const pw, const bool successful) +log_syslog(struct su_context *su, struct passwd const *pw, bool successful) { const char *new_user, *old_user, *tty; @@ -152,7 +148,7 @@ log_syslog(struct passwd const * const pw, const bool successful) openlog(program_invocation_short_name, 0, LOG_AUTH); syslog(LOG_NOTICE, "%s(to %s) %s on %s", successful ? "" : - su_mode == RUNUSER_MODE ? "FAILED RUNUSER " : "FAILED SU ", + su->runuser ? "FAILED RUNUSER " : "FAILED SU ", new_user, old_user, tty); closelog(); } @@ -191,9 +187,12 @@ static int su_pam_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) { - if (suppress_pam_info + struct su_context *su = (struct su_context *) appdata_ptr; + + if (su->suppress_pam_info && num_msg == 1 && msg && msg[0]->msg_style == PAM_TEXT_INFO) return PAM_SUCCESS; + #ifdef HAVE_SECURITY_PAM_MISC_H return misc_conv(num_msg, msg, resp, appdata_ptr); #elif defined(HAVE_SECURITY_OPENPAM_H) @@ -201,23 +200,18 @@ su_pam_conv(int num_msg, const struct pam_message **msg, #endif } -static struct pam_conv conv = { - su_pam_conv, - NULL -}; - static void -cleanup_pam(const int retcode) +cleanup_pam(struct su_context *su, int retcode) { const int saved_errno = errno; - if (_pam_session_opened) - pam_close_session(pamh, 0); + if (su->pam_has_session) + pam_close_session(su->pamh, 0); - if (_pam_cred_established) - pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT); + if (su->pam_has_cred) + pam_setcred(su->pamh, PAM_DELETE_CRED | PAM_SILENT); - pam_end(pamh, retcode); + pam_end(su->pamh, retcode); errno = saved_errno; } @@ -231,12 +225,12 @@ su_catch_sig(int sig) /* Export env variables declared by PAM modules. */ static void -export_pamenv(void) +export_pamenv(struct su_context *su) { char **env; /* This is a copy but don't care to free as we exec later anyways. */ - env = pam_getenvlist(pamh); + env = pam_getenvlist(su->pamh); while (env && *env) { if (putenv(*env) != 0) err(EXIT_FAILURE, NULL); @@ -245,7 +239,7 @@ export_pamenv(void) } static void -create_watching_parent(void) +create_watching_parent(struct su_context *su) { pid_t child; sigset_t ourset; @@ -253,19 +247,19 @@ create_watching_parent(void) int status = 0; int retval; - retval = pam_open_session(pamh, 0); + retval = pam_open_session(su->pamh, 0); if (is_pam_failure(retval)) { - cleanup_pam(retval); + cleanup_pam(su, retval); errx(EXIT_FAILURE, _("cannot open session: %s"), - pam_strerror(pamh, retval)); + pam_strerror(su->pamh, retval)); } else - _pam_session_opened = 1; + su->pam_has_session = 1; memset(oldact, 0, sizeof(oldact)); child = fork(); if (child == (pid_t) - 1) { - cleanup_pam(PAM_ABORT); + cleanup_pam(su, PAM_ABORT); err(EXIT_FAILURE, _("cannot create child process")); } @@ -291,7 +285,7 @@ create_watching_parent(void) sigemptyset(&action.sa_mask); action.sa_flags = 0; sigemptyset(&ourset); - if (!same_session) { + if (!su->same_session) { if (sigaddset(&ourset, SIGINT) || sigaddset(&ourset, SIGQUIT)) { warn(_("cannot set signal handler")); @@ -307,7 +301,7 @@ create_watching_parent(void) warn(_("cannot set signal handler")); caught_signal = true; } - if (!caught_signal && !same_session + if (!caught_signal && !su->same_session && (sigaction(SIGINT, &action, &oldact[1]) || sigaction(SIGQUIT, &action, &oldact[2]))) { warn(_("cannot set signal handler")); @@ -347,7 +341,7 @@ create_watching_parent(void) kill(child, SIGTERM); } - cleanup_pam(PAM_SUCCESS); + cleanup_pam(su, PAM_SUCCESS); if (caught_signal) { sleep(2); @@ -382,27 +376,17 @@ create_watching_parent(void) } static void -authenticate(const struct passwd *pw) +authenticate(struct su_context *su, const struct passwd *pw) { const struct passwd *lpw = NULL; const char *cp, *srvname = NULL; int retval; - switch (su_mode) { - case SU_MODE: - srvname = simulate_login ? PAM_SRVNAME_SU_L : PAM_SRVNAME_SU; - break; - case RUNUSER_MODE: - srvname = - simulate_login ? PAM_SRVNAME_RUNUSER_L : - PAM_SRVNAME_RUNUSER; - break; - default: - abort(); - break; - } + srvname = su->runuser ? + (su->simulate_login ? PAM_SRVNAME_RUNUSER_L : PAM_SRVNAME_RUNUSER) : + (su->simulate_login ? PAM_SRVNAME_SU_L : PAM_SRVNAME_SU); - retval = pam_start(srvname, pw->pw_name, &conv, &pamh); + retval = pam_start(srvname, pw->pw_name, &su->conv, &su->pamh); if (is_pam_failure(retval)) goto done; @@ -413,51 +397,49 @@ authenticate(const struct passwd *pw) tty = cp + 5; else tty = cp; - retval = pam_set_item(pamh, PAM_TTY, tty); + retval = pam_set_item(su->pamh, PAM_TTY, tty); if (is_pam_failure(retval)) goto done; } lpw = current_getpwuid(); if (lpw && lpw->pw_name) { - retval = - pam_set_item(pamh, PAM_RUSER, (const void *)lpw->pw_name); + retval = pam_set_item(su->pamh, PAM_RUSER, (const void *)lpw->pw_name); if (is_pam_failure(retval)) goto done; } - if (su_mode == RUNUSER_MODE) { + if (su->runuser) { /* * This is the only difference between runuser(1) and su(1). The command * runuser(1) does not required authentication, because user is root. */ - if (restricted) - errx(EXIT_FAILURE, - _("may not be used by non-root users")); + if (su->restricted) + errx(EXIT_FAILURE, _("may not be used by non-root users")); return; } - retval = pam_authenticate(pamh, 0); + retval = pam_authenticate(su->pamh, 0); if (is_pam_failure(retval)) goto done; - retval = pam_acct_mgmt(pamh, 0); + retval = pam_acct_mgmt(su->pamh, 0); if (retval == PAM_NEW_AUTHTOK_REQD) { /* Password has expired. Offer option to change it. */ - retval = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK); + retval = pam_chauthtok(su->pamh, PAM_CHANGE_EXPIRED_AUTHTOK); } done: - log_syslog(pw, !is_pam_failure(retval)); + log_syslog(su, pw, !is_pam_failure(retval)); if (is_pam_failure(retval)) { const char *msg; log_btmp(pw); - msg = pam_strerror(pamh, retval); - pam_end(pamh, retval); + msg = pam_strerror(su->pamh, retval); + pam_end(su->pamh, retval); sleep(getlogindefs_num("FAIL_DELAY", 1)); errx(EXIT_FAILURE, "%s", msg ? msg : _("incorrect password")); } @@ -482,9 +464,9 @@ set_path(const struct passwd * const pw) the value for the SHELL environment variable. */ static void -modify_environment (const struct passwd * const pw, const char * const shell) +modify_environment(struct su_context *su, const struct passwd *pw, const char *shell) { - if (simulate_login) { + if (su->simulate_login) { /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH. Unset all other environment variables. */ char *term = getenv("TERM"); @@ -505,7 +487,7 @@ modify_environment (const struct passwd * const pw, const char * const shell) } else { /* Set HOME, SHELL, and (if not becoming a superuser) USER and LOGNAME. */ - if (change_environment) { + if (su->change_environment) { xsetenv("HOME", pw->pw_dir, 1); if (shell) xsetenv("SHELL", shell, 1); @@ -519,13 +501,13 @@ modify_environment (const struct passwd * const pw, const char * const shell) } } - export_pamenv(); + export_pamenv(su); } /* Become the user and group(s) specified by PW. */ static void -init_groups (const struct passwd * const pw, const gid_t * const groups, const size_t num_groups) +init_groups(struct su_context *su, const struct passwd *pw, gid_t * groups, size_t num_groups) { int retval; @@ -537,16 +519,16 @@ init_groups (const struct passwd * const pw, const gid_t * const groups, const s retval = initgroups(pw->pw_name, pw->pw_gid); if (retval == -1) { - cleanup_pam(PAM_ABORT); + cleanup_pam(su, PAM_ABORT); err(EXIT_FAILURE, _("cannot set groups")); } endgrent(); - retval = pam_setcred(pamh, PAM_ESTABLISH_CRED); + retval = pam_setcred(su->pamh, PAM_ESTABLISH_CRED); if (is_pam_failure(retval)) - errx(EXIT_FAILURE, "%s", pam_strerror(pamh, retval)); + errx(EXIT_FAILURE, "%s", pam_strerror(su->pamh, retval)); else - _pam_cred_established = 1; + su->pam_has_cred = 1; } static void @@ -564,15 +546,16 @@ change_identity (const struct passwd * const pw) are N_ADDITIONAL_ARGS extra arguments. */ static void -run_shell (char const * const shell, char const * const command, char ** const additional_args, - const size_t n_additional_args) +run_shell(struct su_context *su, + char const *shell, char const *command, char **additional_args, + size_t n_additional_args) { size_t n_args = - 1 + fast_startup + 2 * ! !command + n_additional_args + 1; + 1 + su->fast_startup + 2 * ! !command + n_additional_args + 1; char const **args = xcalloc(n_args, sizeof *args); size_t argno = 1; - if (simulate_login) { + if (su->simulate_login) { char *arg0; char *shell_basename; @@ -583,7 +566,7 @@ run_shell (char const * const shell, char const * const command, char ** const a args[0] = arg0; } else args[0] = basename(shell); - if (fast_startup) + if (su->fast_startup) args[argno++] = "-f"; if (command) { args[argno++] = "-c"; @@ -620,10 +603,9 @@ restricted_shell (const char * const shell) return true; } - -static void __attribute__ ((__noreturn__)) usage(int status) +static void __attribute__ ((__noreturn__)) usage(int status, int mode) { - if (su_mode == RUNUSER_MODE) { + if (mode == RUNUSER_MODE) { fputs(USAGE_HEADER, stdout); printf(_(" %s [options] -u \n"), program_invocation_short_name); @@ -681,22 +663,15 @@ static void __attribute__ ((__noreturn__)) usage(int status) fputs(USAGE_SEPARATOR, stdout); printf(USAGE_HELP_OPTIONS(22)); - printf(USAGE_MAN_TAIL(su_mode == SU_MODE ? "su(1)" : "runuser(1)")); + printf(USAGE_MAN_TAIL(mode == SU_MODE ? "su(1)" : "runuser(1)")); exit(status); } -static void -load_config(void) +static void load_config(void *data) { - switch (su_mode) { - case SU_MODE: - logindefs_load_file(_PATH_LOGINDEFS_SU); - break; - case RUNUSER_MODE: - logindefs_load_file(_PATH_LOGINDEFS_RUNUSER); - break; - } + struct su_context *su = (struct su_context *) data; + logindefs_load_file(su->runuser ? _PATH_LOGINDEFS_RUNUSER : _PATH_LOGINDEFS_SU); logindefs_load_file(_PATH_LOGINDEFS); } @@ -739,6 +714,12 @@ add_supp_group(const char *name, gid_t ** groups, size_t * ngroups) int su_main(int argc, char **argv, int mode) { + struct su_context _su = { + .conv = { su_pam_conv, NULL }, + .runuser = (mode == RUNUSER_MODE ? 1 : 0), + .change_environment = 1 + }, *su = &_su; + int optc; const char *new_user = DEFAULT_USER, *runuser_user = NULL; char *command = NULL; @@ -773,10 +754,7 @@ su_main(int argc, char **argv, int mode) textdomain(PACKAGE); atexit(close_stdout); - su_mode = mode; - fast_startup = false; - simulate_login = false; - change_environment = true; + su->conv.appdata_ptr = (void *) su; while ((optc = getopt_long(argc, argv, "c:fg:G:lmps:u:hV", longopts, @@ -792,7 +770,7 @@ su_main(int argc, char **argv, int mode) break; case 'f': - fast_startup = true; + su->fast_startup = true; break; case 'g': @@ -806,12 +784,12 @@ su_main(int argc, char **argv, int mode) break; case 'l': - simulate_login = true; + su->simulate_login = true; break; case 'm': case 'p': - change_environment = false; + su->change_environment = false; break; case 's': @@ -819,42 +797,42 @@ su_main(int argc, char **argv, int mode) break; case 'u': - if (su_mode != RUNUSER_MODE) - usage(EXIT_FAILURE); + if (!su->runuser) + usage(mode, EXIT_FAILURE); runuser_user = optarg; break; case 'h': - usage(0); + usage(mode, 0); case 'V': printf(UTIL_LINUX_VERSION); exit(EXIT_SUCCESS); default: - usage(EXIT_FAILURE); + usage(mode, EXIT_FAILURE); } } - restricted = evaluate_uid(); + su->restricted = evaluate_uid(); if (optind < argc && !strcmp(argv[optind], "-")) { - simulate_login = true; + su->simulate_login = true; ++optind; } - if (simulate_login && !change_environment) { + if (su->simulate_login && !su->change_environment) { warnx(_ ("ignoring --preserve-environment, it's mutually exclusive with --login")); - change_environment = true; + su->change_environment = true; } - switch (su_mode) { + switch (mode) { case RUNUSER_MODE: if (runuser_user) { /* runuser -u */ new_user = runuser_user; - if (shell || fast_startup || command || simulate_login) { + if (shell || su->fast_startup || command || su->simulate_login) { errx(EXIT_FAILURE, _ ("options --{shell,fast,command,session-command,login} and " @@ -875,11 +853,11 @@ su_main(int argc, char **argv, int mode) break; } - if ((use_supp || use_gid) && restricted) + if ((use_supp || use_gid) && su->restricted) errx(EXIT_FAILURE, _("only root can specify alternative groups")); - logindefs_load_defaults = load_config; + logindefs_set_loader(load_config, (void *) su); pw = getpwnam(new_user); if (!(pw && pw->pw_name && pw->pw_name[0] && pw->pw_dir && pw->pw_dir[0] @@ -906,16 +884,16 @@ su_main(int argc, char **argv, int mode) else if (use_gid) pw->pw_gid = gid; - authenticate(pw); + authenticate(su, pw); if (request_same_session || !command || !pw->pw_uid) - same_session = 1; + su->same_session = 1; /* initialize shell variable only if "-u " not specified */ if (runuser_user) { shell = NULL; } else { - if (!shell && !change_environment) + if (!shell && !su->change_environment) shell = getenv("SHELL"); if (shell && getuid() != 0 && restricted_shell(pw->pw_shell)) { /* The user being su'd to has a nonstandard shell, and so is @@ -928,30 +906,29 @@ su_main(int argc, char **argv, int mode) shell = xstrdup(shell ? shell : pw->pw_shell); } - init_groups(pw, groups, ngroups); + init_groups(su, pw, groups, ngroups); - if (!simulate_login || command) - suppress_pam_info = 1; /* don't print PAM info messages */ + if (!su->simulate_login || command) + su->suppress_pam_info = 1; /* don't print PAM info messages */ - create_watching_parent(); + create_watching_parent(su); /* Now we're in the child. */ change_identity(pw); - if (!same_session) + if (!su->same_session) setsid(); /* Set environment after pam_open_session, which may put KRB5CCNAME into the pam_env, etc. */ - modify_environment(pw, shell); + modify_environment(su, pw, shell); - if (simulate_login && chdir(pw->pw_dir) != 0) + if (su->simulate_login && chdir(pw->pw_dir) != 0) warn(_("warning: cannot change directory to %s"), pw->pw_dir); if (shell) - run_shell(shell, command, argv + optind, max(0, argc - optind)); - else { - execvp(argv[optind], &argv[optind]); - err(EXIT_FAILURE, _("failed to execute %s"), argv[optind]); - } + run_shell(su, shell, command, argv + optind, max(0, argc - optind)); + + execvp(argv[optind], &argv[optind]); + err(EXIT_FAILURE, _("failed to execute %s"), argv[optind]); } -- cgit v1.2.3-55-g7522