| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #include <config.h> |
| |
| #ident "$Id$" |
| |
| #include <getopt.h> |
| #include <grp.h> |
| #include <pwd.h> |
| #include <signal.h> |
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| #ifndef USE_PAM |
| #include <sys/ioctl.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #endif /* !USE_PAM */ |
| #include "prototypes.h" |
| #include "defines.h" |
| #include "pwauth.h" |
| #include "getdef.h" |
| #ifdef USE_PAM |
| #include "pam_defs.h" |
| #endif /* USE_PAM */ |
| |
| #include "exitcodes.h" |
| |
| |
| |
| |
| const char *Prog; |
| static const char *caller_tty = NULL; |
| static bool caller_is_root = false; |
| static uid_t caller_uid; |
| #ifndef USE_PAM |
| static bool caller_on_console = false; |
| #ifdef SU_ACCESS |
| static char *caller_pass; |
| #endif |
| #endif /* !USE_PAM */ |
| static bool doshell = false; |
| static bool fakelogin = false; |
| static const char *shellstr; |
| static char *command = NULL; |
| |
| |
| |
| static char name[BUFSIZ]; |
| static char caller_name[BUFSIZ]; |
| |
| |
| static bool change_environment = true; |
| |
| #ifdef USE_PAM |
| static char kill_msg[256]; |
| static char wait_msg[256]; |
| static pam_handle_t *pamh = NULL; |
| static int caught = 0; |
| |
| static pid_t pid_child = 0; |
| #endif |
| |
| |
| |
| |
| |
| extern char **newenvp; |
| extern size_t newenvc; |
| |
| |
| |
| static void execve_shell (const char *shellname, |
| char *args[], |
| char *const envp[]); |
| #ifdef USE_PAM |
| static RETSIGTYPE kill_child (int unused(s)); |
| static void prepare_pam_close_session (void); |
| #else /* !USE_PAM */ |
| static RETSIGTYPE die (int); |
| static bool iswheel (const char *); |
| #endif /* !USE_PAM */ |
| static bool restricted_shell (const char *shellname); |
| static void su_failure (const char *tty, bool su_to_root); |
| static struct passwd * check_perms (void); |
| #ifdef USE_PAM |
| static void check_perms_pam (const struct passwd *pw); |
| #else /* !USE_PAM */ |
| static void check_perms_nopam (const struct passwd *pw); |
| #endif /* !USE_PAM */ |
| static void save_caller_context (char **argv); |
| static void process_flags (int argc, char **argv); |
| static void set_environment (struct passwd *pw); |
| |
| #ifndef USE_PAM |
| |
| |
| |
| |
| |
| |
| |
| static RETSIGTYPE die (int killed) |
| { |
| static TERMIO sgtty; |
| |
| if (killed != 0) { |
| STTY (0, &sgtty); |
| } else { |
| GTTY (0, &sgtty); |
| } |
| |
| if (killed != 0) { |
| _exit (128+killed); |
| } |
| } |
| |
| static bool iswheel (const char *username) |
| { |
| struct group *grp; |
| |
| grp = getgrnam ("wheel"); |
| if ( (NULL ==grp) |
| || (NULL == grp->gr_mem)) { |
| return false; |
| } |
| return is_on_list (grp->gr_mem, username); |
| } |
| #else /* USE_PAM */ |
| static RETSIGTYPE kill_child (int unused(s)) |
| { |
| if (0 != pid_child) { |
| (void) kill (-pid_child, SIGKILL); |
| (void) write (STDERR_FILENO, kill_msg, strlen (kill_msg)); |
| } else { |
| (void) write (STDERR_FILENO, wait_msg, strlen (wait_msg)); |
| } |
| _exit (255); |
| } |
| #endif /* USE_PAM */ |
| |
| |
| static bool restricted_shell (const char *shellname) |
| { |
| const char *line; |
| |
| setusershell (); |
| while ((line = getusershell ()) != NULL) { |
| if (('#' != *line) && (strcmp (line, shellname) == 0)) { |
| endusershell (); |
| return false; |
| } |
| } |
| endusershell (); |
| return true; |
| } |
| |
| static void su_failure (const char *tty, bool su_to_root) |
| { |
| sulog (tty, false, caller_name, name); |
| #ifdef USE_SYSLOG |
| if (getdef_bool ("SYSLOG_SU_ENAB")) { |
| SYSLOG ((su_to_root ? LOG_NOTICE : LOG_INFO, |
| "- %s %s:%s", tty, |
| ('\0' != caller_name[0]) ? caller_name : "???", |
| ('\0' != name[0]) ? name : "???")); |
| } |
| closelog (); |
| #endif |
| |
| #ifdef WITH_AUDIT |
| audit_fd = audit_open (); |
| audit_log_acct_message (audit_fd, |
| AUDIT_USER_ROLE_CHANGE, |
| NULL, |
| "su", |
| ('\0' != caller_name[0]) ? caller_name : "???", |
| AUDIT_NO_ID, |
| "localhost", |
| NULL, |
| tty, |
| 0); |
| close (audit_fd); |
| #endif /* WITH_AUDIT */ |
| |
| exit (1); |
| } |
| |
| |
| |
| |
| |
| static void execve_shell (const char *shellname, |
| char *args[], |
| char *const envp[]) |
| { |
| int err; |
| (void) execve (shellname, (char **) args, envp); |
| err = errno; |
| |
| if (access (shellname, R_OK|X_OK) == 0) { |
| |
| |
| |
| |
| size_t n_args = 0; |
| char **targs; |
| while (NULL != args[n_args]) { |
| n_args++; |
| } |
| targs = (char **) xmalloc ((n_args + 3) * sizeof (args[0])); |
| targs[0] = "sh"; |
| targs[1] = "-"; |
| targs[2] = xstrdup (shellname); |
| targs[n_args+2] = NULL; |
| while (1 != n_args) { |
| targs[n_args+1] = args[n_args - 1]; |
| n_args--; |
| } |
| |
| (void) execve (SHELL, targs, envp); |
| } else { |
| errno = err; |
| } |
| } |
| |
| #ifdef USE_PAM |
| |
| static void catch_signals (int sig) |
| { |
| caught = sig; |
| } |
| |
| |
| |
| |
| |
| |
| |
| static void prepare_pam_close_session (void) |
| { |
| sigset_t ourset; |
| int status; |
| int ret; |
| |
| pid_child = fork (); |
| if (pid_child == 0) { |
| return; |
| } else if ((pid_t)-1 == pid_child) { |
| (void) fprintf (stderr, |
| _("%s: Cannot fork user shell\n"), |
| Prog); |
| SYSLOG ((LOG_WARN, "Cannot execute %s", shellstr)); |
| closelog (); |
| exit (1); |
| |
| } |
| |
| |
| sigfillset (&ourset); |
| if (sigprocmask (SIG_BLOCK, &ourset, NULL) != 0) { |
| (void) fprintf (stderr, |
| _("%s: signal malfunction\n"), |
| Prog); |
| caught = SIGTERM; |
| } |
| if (0 == caught) { |
| struct sigaction action; |
| |
| action.sa_handler = catch_signals; |
| sigemptyset (&action.sa_mask); |
| action.sa_flags = 0; |
| sigemptyset (&ourset); |
| |
| if ( (sigaddset (&ourset, SIGTERM) != 0) |
| || (sigaddset (&ourset, SIGALRM) != 0) |
| || (sigaction (SIGTERM, &action, NULL) != 0) |
| || ( !doshell |
| |
| |
| |
| |
| && ( (sigaddset (&ourset, SIGINT) != 0) |
| || (sigaddset (&ourset, SIGQUIT) != 0) |
| || (sigaddset (&ourset, SIGTSTP) != 0) |
| || (sigaction (SIGINT, &action, NULL) != 0) |
| || (sigaction (SIGQUIT, &action, NULL) != 0) |
| || (sigaction (SIGTSTP, &action, NULL) != 0))) |
| || (sigprocmask (SIG_UNBLOCK, &ourset, NULL) != 0) |
| ) { |
| fprintf (stderr, |
| _("%s: signal masking malfunction\n"), |
| Prog); |
| caught = SIGTERM; |
| } |
| } |
| |
| if (0 == caught) { |
| bool stop = true; |
| |
| do { |
| pid_t pid; |
| stop = true; |
| |
| pid = waitpid (-1, &status, WUNTRACED); |
| |
| |
| |
| |
| |
| if ( ((pid_t)-1 == pid) |
| && (EINTR == errno) |
| && (SIGTSTP == caught)) { |
| caught = 0; |
| |
| |
| |
| |
| |
| kill (pid_child, SIGSTOP); |
| stop = false; |
| } else if ( ((pid_t)-1 != pid) |
| && (0 != WIFSTOPPED (status))) { |
| |
| |
| kill (getpid (), SIGSTOP); |
| |
| kill (pid, SIGCONT); |
| stop = false; |
| } else if ( (pid_t)-1 != pid) { |
| pid_child = 0; |
| } |
| } while (!stop); |
| } |
| |
| if (0 != caught && 0 != pid_child) { |
| (void) fputs ("\n", stderr); |
| (void) fputs (_("Session terminated, terminating shell..."), |
| stderr); |
| (void) kill (-pid_child, caught); |
| |
| snprintf (kill_msg, sizeof kill_msg, _(" ...killed.\n")); |
| snprintf (wait_msg, sizeof wait_msg, _(" ...waiting for child to terminate.\n")); |
| |
| (void) signal (SIGALRM, kill_child); |
| (void) signal (SIGCHLD, catch_signals); |
| (void) alarm (2); |
| |
| sigemptyset (&ourset); |
| if ((sigaddset (&ourset, SIGALRM) != 0) |
| || (sigprocmask (SIG_BLOCK, &ourset, NULL) != 0)) { |
| fprintf (stderr, _("%s: signal masking malfunction\n"), Prog); |
| kill_child (0); |
| } else { |
| while (0 == waitpid (pid_child, &status, WNOHANG)) { |
| sigsuspend (&ourset); |
| } |
| pid_child = 0; |
| (void) sigprocmask (SIG_UNBLOCK, &ourset, NULL); |
| } |
| |
| (void) fputs (_(" ...terminated.\n"), stderr); |
| } |
| |
| ret = pam_close_session (pamh, 0); |
| if (PAM_SUCCESS != ret) { |
| SYSLOG ((LOG_ERR, "pam_close_session: %s", |
| pam_strerror (pamh, ret))); |
| fprintf (stderr, _("%s: %s\n"), Prog, pam_strerror (pamh, ret)); |
| } |
| |
| (void) pam_setcred (pamh, PAM_DELETE_CRED); |
| (void) pam_end (pamh, PAM_SUCCESS); |
| |
| exit ((0 != WIFEXITED (status)) ? WEXITSTATUS (status) |
| : WTERMSIG (status) + 128); |
| |
| } |
| #endif /* USE_PAM */ |
| |
| |
| |
| |
| static void usage (int status) |
| { |
| (void) |
| fputs (_("Usage: su [options] [-] [username [args]]\n" |
| "\n" |
| "Options:\n" |
| " -c, --command COMMAND pass COMMAND to the invoked shell\n" |
| " -h, --help display this help message and exit\n" |
| " -, -l, --login make the shell a login shell\n" |
| " -m, -p,\n" |
| " --preserve-environment do not reset environment variables, and\n" |
| " keep the same shell\n" |
| " -s, --shell SHELL use SHELL instead of the default in passwd\n" |
| "\n" |
| "If no username is given, assume root.\n"), (E_SUCCESS != status) ? stderr : stdout); |
| exit (status); |
| } |
| |
| #ifdef USE_PAM |
| static void check_perms_pam (const struct passwd *pw) |
| { |
| int ret; |
| ret = pam_authenticate (pamh, 0); |
| if (PAM_SUCCESS != ret) { |
| SYSLOG (((pw->pw_uid != 0)? LOG_NOTICE : LOG_WARN, "pam_authenticate: %s", |
| pam_strerror (pamh, ret))); |
| fprintf (stderr, _("%s: %s\n"), Prog, pam_strerror (pamh, ret)); |
| (void) pam_end (pamh, ret); |
| su_failure (caller_tty, 0 == pw->pw_uid); |
| } |
| |
| ret = pam_acct_mgmt (pamh, 0); |
| if (PAM_SUCCESS != ret) { |
| if (caller_is_root) { |
| fprintf (stderr, |
| _("%s: %s\n(Ignored)\n"), |
| Prog, pam_strerror (pamh, ret)); |
| } else if (PAM_NEW_AUTHTOK_REQD == ret) { |
| ret = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK); |
| if (PAM_SUCCESS != ret) { |
| SYSLOG ((LOG_ERR, "pam_chauthtok: %s", |
| pam_strerror (pamh, ret))); |
| fprintf (stderr, |
| _("%s: %s\n"), |
| Prog, pam_strerror (pamh, ret)); |
| (void) pam_end (pamh, ret); |
| su_failure (caller_tty, 0 == pw->pw_uid); |
| } |
| } else { |
| SYSLOG ((LOG_ERR, "pam_acct_mgmt: %s", |
| pam_strerror (pamh, ret))); |
| fprintf (stderr, |
| _("%s: %s\n"), |
| Prog, pam_strerror (pamh, ret)); |
| (void) pam_end (pamh, ret); |
| su_failure (caller_tty, 0 == pw->pw_uid); |
| } |
| } |
| } |
| #else /* !USE_PAM */ |
| static void check_perms_nopam (const struct passwd *pw) |
| { |
| const struct spwd *spwd = NULL; |
| const char *password = pw->pw_passwd; |
| RETSIGTYPE (*oldsig) (int); |
| |
| if (caller_is_root) { |
| return; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| if ( (0 == pw->pw_uid) |
| && getdef_bool ("SU_WHEEL_ONLY") |
| && !iswheel (caller_name)) { |
| fprintf (stderr, |
| _("You are not authorized to su %s\n"), |
| name); |
| exit (1); |
| } |
| spwd = getspnam (name); |
| #ifdef SU_ACCESS |
| if (strcmp (pw->pw_passwd, SHADOW_PASSWD_STRING) == 0) { |
| if (NULL != spwd) { |
| password = spwd->sp_pwdp; |
| } |
| } |
| |
| switch (check_su_auth (caller_name, name, 0 == pw->pw_uid)) { |
| case 0: |
| break; |
| case 1: |
| password = ""; |
| break; |
| case 2: |
| (void) puts (_("(Enter your own password)")); |
| password = caller_pass; |
| break; |
| default: |
| fprintf (stderr, |
| _("You are not authorized to su %s\n"), |
| name); |
| exit (1); |
| } |
| #endif /* SU_ACCESS */ |
| |
| |
| |
| die (0); |
| oldsig = signal (SIGQUIT, die); |
| |
| |
| |
| |
| |
| |
| if (pw_auth (password, name, PW_SU, (char *) 0) != 0) { |
| SYSLOG (((pw->pw_uid != 0)? LOG_NOTICE : LOG_WARN, |
| "Authentication failed for %s", name)); |
| fprintf(stderr, _("%s: Authentication failure\n"), Prog); |
| su_failure (caller_tty, 0 == pw->pw_uid); |
| } |
| (void) signal (SIGQUIT, oldsig); |
| |
| |
| |
| |
| |
| |
| if (NULL != spwd) { |
| (void) expire (pw, spwd); |
| } |
| |
| |
| |
| |
| |
| |
| |
| if (!isttytime (name, "SU", time ((time_t *) 0))) { |
| SYSLOG (((0 != pw->pw_uid) ? LOG_WARN : LOG_CRIT, |
| "SU by %s to restricted account %s", |
| caller_name, name)); |
| fprintf (stderr, |
| _("%s: You are not authorized to su at that time\n"), |
| Prog); |
| su_failure (caller_tty, 0 == pw->pw_uid); |
| } |
| } |
| #endif /* !USE_PAM */ |
| |
| |
| |
| |
| |
| |
| |
| static struct passwd * check_perms (void) |
| { |
| #ifdef USE_PAM |
| const char *tmp_name; |
| int ret; |
| #endif /* !USE_PAM */ |
| |
| |
| |
| |
| struct passwd *pw = xgetpwnam (name); |
| if (NULL == pw) { |
| (void) fprintf (stderr, |
| _("No passwd entry for user '%s'\n"), name); |
| SYSLOG ((LOG_NOTICE, "No passwd entry for user '%s'", name)); |
| su_failure (caller_tty, true); |
| } |
| |
| (void) signal (SIGINT, SIG_IGN); |
| (void) signal (SIGQUIT, SIG_IGN); |
| |
| #ifdef USE_PAM |
| check_perms_pam (pw); |
| |
| ret = pam_get_item(pamh, PAM_USER, (const void **) &tmp_name); |
| if (ret != PAM_SUCCESS) { |
| SYSLOG((LOG_ERR, "pam_get_item: internal PAM error\n")); |
| (void) fprintf (stderr, |
| "%s: Internal PAM error retrieving username\n", |
| Prog); |
| (void) pam_end (pamh, ret); |
| su_failure (caller_tty, 0 == pw->pw_uid); |
| } |
| if (strcmp (name, tmp_name) != 0) { |
| SYSLOG ((LOG_INFO, |
| "Change user from '%s' to '%s' as requested by PAM", |
| name, tmp_name)); |
| strncpy (name, tmp_name, sizeof(name) - 1); |
| name[sizeof(name) - 1] = '\0'; |
| pw = xgetpwnam (name); |
| if (NULL == pw) { |
| (void) fprintf (stderr, |
| _("No passwd entry for user '%s'\n"), |
| name); |
| SYSLOG ((LOG_NOTICE, |
| "No passwd entry for user '%s'", name)); |
| su_failure (caller_tty, true); |
| } |
| } |
| #else /* !USE_PAM */ |
| check_perms_nopam (pw); |
| #endif /* !USE_PAM */ |
| |
| (void) signal (SIGINT, SIG_DFL); |
| (void) signal (SIGQUIT, SIG_DFL); |
| |
| |
| |
| |
| |
| |
| if ('*' == pw->pw_shell[0]) { |
| subsystem (pw); |
| endpwent (); |
| endspent (); |
| pw_free (pw); |
| return check_perms (); |
| } |
| |
| return pw; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static void save_caller_context (char **argv) |
| { |
| struct passwd *pw = NULL; |
| #ifndef USE_PAM |
| #ifdef SU_ACCESS |
| const char *password = NULL; |
| #endif /* SU_ACCESS */ |
| #endif /* !USE_PAM */ |
| |
| |
| |
| |
| Prog = Basename (argv[0]); |
| |
| caller_uid = getuid (); |
| caller_is_root = (caller_uid == 0); |
| |
| |
| |
| |
| |
| caller_tty = ttyname (0); |
| if ((isatty (0) != 0) && (NULL != caller_tty)) { |
| #ifndef USE_PAM |
| caller_on_console = console (caller_tty); |
| #endif /* !USE_PAM */ |
| } else { |
| |
| |
| |
| if (!caller_is_root) { |
| fprintf (stderr, |
| _("%s: must be run from a terminal\n"), |
| Prog); |
| exit (1); |
| } |
| caller_tty = "???"; |
| } |
| |
| |
| |
| |
| |
| pw = get_my_pwent (); |
| if (NULL == pw) { |
| fprintf (stderr, |
| _("%s: Cannot determine your user name.\n"), |
| Prog); |
| SYSLOG ((LOG_WARN, "Cannot determine the user name of the caller (UID %lu)", |
| (unsigned long) caller_uid)); |
| su_failure (caller_tty, true); |
| } |
| STRFCPY (caller_name, pw->pw_name); |
| |
| #ifndef USE_PAM |
| #ifdef SU_ACCESS |
| |
| |
| |
| |
| password = pw->pw_passwd; |
| if (strcmp (pw->pw_passwd, SHADOW_PASSWD_STRING) == 0) { |
| const struct spwd *spwd = getspnam (caller_name); |
| if (NULL != spwd) { |
| password = spwd->sp_pwdp; |
| } |
| } |
| free (caller_pass); |
| caller_pass = xstrdup (password); |
| #endif /* SU_ACCESS */ |
| #endif /* !USE_PAM */ |
| pw_free (pw); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| static void process_flags (int argc, char **argv) |
| { |
| int c; |
| static struct option long_options[] = { |
| {"command", required_argument, NULL, 'c'}, |
| {"help", no_argument, NULL, 'h'}, |
| {"login", no_argument, NULL, 'l'}, |
| {"preserve-environment", no_argument, NULL, 'p'}, |
| {"shell", required_argument, NULL, 's'}, |
| {NULL, 0, NULL, '\0'} |
| }; |
| |
| while ((c = getopt_long (argc, argv, "c:hlmps:", |
| long_options, NULL)) != -1) { |
| switch (c) { |
| case 'c': |
| command = optarg; |
| break; |
| case 'h': |
| usage (E_SUCCESS); |
| break; |
| case 'l': |
| fakelogin = true; |
| break; |
| case 'm': |
| case 'p': |
| |
| |
| |
| |
| change_environment = false; |
| break; |
| case 's': |
| shellstr = optarg; |
| break; |
| default: |
| usage (E_USAGE); |
| } |
| } |
| |
| if ((optind < argc) && (strcmp (argv[optind], "-") == 0)) { |
| fakelogin = true; |
| optind++; |
| } |
| |
| if (optind < argc) { |
| STRFCPY (name, argv[optind++]); |
| } |
| if ('\0' == name[0]) { |
| struct passwd *root_pw = getpwnam ("root"); |
| if ((NULL != root_pw) && (0 == root_pw->pw_uid)) { |
| (void) strcpy (name, "root"); |
| } else { |
| root_pw = getpwuid (0); |
| if (NULL == root_pw) { |
| SYSLOG ((LOG_CRIT, "There is no UID 0 user.")); |
| su_failure (caller_tty, true); |
| } |
| (void) strcpy (name, root_pw->pw_name); |
| } |
| } |
| |
| doshell = (argc == optind); |
| if (NULL != command) { |
| doshell = false; |
| } |
| } |
| |
| static void set_environment (struct passwd *pw) |
| { |
| const char *cp; |
| |
| |
| |
| |
| if (change_environment && fakelogin) { |
| |
| |
| |
| |
| cp = getenv ("TERM"); |
| if (NULL != cp) { |
| addenv ("TERM", cp); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| cp = getenv ("COLORTERM"); |
| if (NULL != cp) { |
| addenv ("COLORTERM", cp); |
| } |
| |
| #ifndef USE_PAM |
| cp = getdef_str ("ENV_TZ"); |
| if (NULL != cp) { |
| addenv (('/' == *cp) ? tz (cp) : cp, NULL); |
| } |
| |
| |
| |
| |
| cp = getdef_str ("ENV_HZ"); |
| if (NULL != cp) { |
| addenv (cp, NULL); |
| } |
| #endif /* !USE_PAM */ |
| |
| |
| |
| |
| |
| cp = getenv ("DISPLAY"); |
| if (NULL != cp) { |
| addenv ("DISPLAY", cp); |
| } |
| cp = getenv ("XAUTHORITY"); |
| if (NULL != cp) { |
| addenv ("XAUTHORITY", cp); |
| } |
| } else { |
| char **envp = environ; |
| while (NULL != *envp) { |
| addenv (*envp, NULL); |
| envp++; |
| } |
| } |
| |
| cp = getdef_str ((pw->pw_uid == 0) ? "ENV_SUPATH" : "ENV_PATH"); |
| if (NULL == cp) { |
| addenv ((pw->pw_uid == 0) ? "PATH=/sbin:/bin:/usr/sbin:/usr/bin" : "PATH=/bin:/usr/bin", NULL); |
| } else if (strchr (cp, '=') != NULL) { |
| addenv (cp, NULL); |
| } else { |
| addenv ("PATH", cp); |
| } |
| |
| if (getenv ("IFS") != NULL) { |
| addenv ("IFS= \t\n", NULL); |
| } |
| |
| #ifdef USE_PAM |
| |
| |
| |
| |
| environ = newenvp; |
| |
| if (change_environment) { |
| |
| char **envcp = pam_getenvlist (pamh); |
| if (NULL != envcp) { |
| while (NULL != *envcp) { |
| addenv (*envcp, NULL); |
| envcp++; |
| } |
| } |
| } |
| |
| #else /* !USE_PAM */ |
| environ = newenvp; |
| #endif /* !USE_PAM */ |
| |
| if (change_environment) { |
| if (fakelogin) { |
| if (shellstr != pw->pw_shell) { |
| free (pw->pw_shell); |
| pw->pw_shell = xstrdup (shellstr); |
| } |
| setup_env (pw); |
| } else { |
| addenv ("HOME", pw->pw_dir); |
| addenv ("USER", pw->pw_name); |
| addenv ("LOGNAME", pw->pw_name); |
| addenv ("SHELL", shellstr); |
| } |
| } |
| |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| int main (int argc, char **argv) |
| { |
| const char *cp; |
| struct passwd *pw = NULL; |
| |
| #ifdef USE_PAM |
| int ret; |
| #endif /* USE_PAM */ |
| |
| (void) setlocale (LC_ALL, ""); |
| (void) bindtextdomain (PACKAGE, LOCALEDIR); |
| (void) textdomain (PACKAGE); |
| |
| save_caller_context (argv); |
| |
| OPENLOG ("su"); |
| |
| process_flags (argc, argv); |
| |
| initenv (); |
| |
| #ifdef USE_PAM |
| ret = pam_start ("su", name, &conv, &pamh); |
| if (PAM_SUCCESS != ret) { |
| SYSLOG ((LOG_ERR, "pam_start: error %d", ret); |
| fprintf (stderr, |
| _("%s: pam_start: error %d\n"), |
| Prog, ret)); |
| exit (1); |
| } |
| |
| ret = pam_set_item (pamh, PAM_TTY, (const void *) caller_tty); |
| if (PAM_SUCCESS == ret) { |
| ret = pam_set_item (pamh, PAM_RUSER, (const void *) caller_name); |
| } |
| if (PAM_SUCCESS != ret) { |
| SYSLOG ((LOG_ERR, "pam_set_item: %s", |
| pam_strerror (pamh, ret))); |
| fprintf (stderr, _("%s: %s\n"), Prog, pam_strerror (pamh, ret)); |
| pam_end (pamh, ret); |
| exit (1); |
| } |
| #endif /* USE_PAM */ |
| |
| pw = check_perms (); |
| |
| |
| |
| |
| |
| if ((NULL == shellstr) && !change_environment) { |
| shellstr = getenv ("SHELL"); |
| } |
| |
| |
| |
| |
| |
| if ( !caller_is_root |
| && restricted_shell (pw->pw_shell)) { |
| shellstr = NULL; |
| change_environment = true; |
| } |
| |
| |
| |
| |
| if (NULL == shellstr) { |
| shellstr = pw->pw_shell; |
| } |
| |
| |
| |
| |
| if ((NULL == shellstr) || ('\0' == shellstr[0])) { |
| shellstr = SHELL; |
| } |
| |
| sulog (caller_tty, true, caller_name, name); |
| #ifdef USE_SYSLOG |
| if (getdef_bool ("SYSLOG_SU_ENAB")) { |
| SYSLOG ((LOG_INFO, "+ %s %s:%s", caller_tty, |
| ('\0' != caller_name[0]) ? caller_name : "???", |
| ('\0' != name[0]) ? name : "???")); |
| } |
| #endif |
| |
| #ifdef USE_PAM |
| |
| if (setup_groups (pw) != 0) { |
| pam_end (pamh, PAM_ABORT); |
| exit (1); |
| } |
| |
| |
| |
| |
| |
| ret = pam_setcred (pamh, PAM_ESTABLISH_CRED); |
| if (PAM_SUCCESS != ret) { |
| SYSLOG ((LOG_ERR, "pam_setcred: %s", pam_strerror (pamh, ret))); |
| fprintf (stderr, _("%s: %s\n"), Prog, pam_strerror (pamh, ret)); |
| (void) pam_end (pamh, ret); |
| exit (1); |
| } |
| |
| ret = pam_open_session (pamh, 0); |
| if (PAM_SUCCESS != ret) { |
| SYSLOG ((LOG_ERR, "pam_open_session: %s", |
| pam_strerror (pamh, ret))); |
| fprintf (stderr, _("%s: %s\n"), Prog, pam_strerror (pamh, ret)); |
| pam_setcred (pamh, PAM_DELETE_CRED); |
| (void) pam_end (pamh, ret); |
| exit (1); |
| } |
| |
| prepare_pam_close_session (); |
| |
| |
| if (change_uid (pw) != 0) { |
| exit (1); |
| } |
| #else /* !USE_PAM */ |
| |
| if (!caller_is_root || fakelogin) { |
| setup_limits (pw); |
| } |
| |
| if (setup_uid_gid (pw, caller_on_console) != 0) { |
| exit (1); |
| } |
| #endif /* !USE_PAM */ |
| |
| #ifdef WITH_AUDIT |
| audit_fd = audit_open (); |
| audit_log_acct_message (audit_fd, |
| AUDIT_USER_ROLE_CHANGE, |
| NULL, |
| "su", |
| ('\0' != caller_name[0]) ? caller_name : "???", |
| AUDIT_NO_ID, |
| "localhost", |
| NULL, |
| caller_tty, |
| 1); |
| close (audit_fd); |
| #endif /* WITH_AUDIT */ |
| |
| set_environment (pw); |
| |
| if (!doshell) { |
| |
| |
| |
| int err = -1; |
| |
| #ifdef USE_PAM |
| |
| err = setsid (); |
| #else |
| |
| int fd = open ("/dev/tty", O_RDWR); |
| |
| if (fd >= 0) { |
| err = ioctl (fd, TIOCNOTTY, (char *) 0); |
| (void) close (fd); |
| } else if (ENXIO == errno) { |
| |
| err = 0; |
| } |
| #endif /* USE_PAM */ |
| |
| if (-1 == err) { |
| (void) fprintf (stderr, |
| _("%s: Cannot drop the controlling terminal\n"), |
| Prog); |
| exit (1); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| endpwent (); |
| endspent (); |
| |
| |
| |
| |
| |
| |
| |
| closelog (); |
| |
| |
| |
| |
| |
| if (fakelogin) { |
| char *arg0; |
| |
| cp = getdef_str ("SU_NAME"); |
| if (NULL == cp) { |
| cp = Basename (shellstr); |
| } |
| |
| arg0 = xmalloc (strlen (cp) + 2); |
| arg0[0] = '-'; |
| strcpy (arg0 + 1, cp); |
| cp = arg0; |
| } else { |
| cp = Basename (shellstr); |
| } |
| |
| if (!doshell) { |
| int err; |
| |
| argv += optind; |
| if (NULL != command) { |
| argv -= 2; |
| argv[0] = "-c"; |
| argv[1] = command; |
| } |
| |
| |
| |
| |
| argv[-1] = cp; |
| execve_shell (shellstr, &argv[-1], environ); |
| err = errno; |
| (void) fprintf (stderr, |
| _("Cannot execute %s\n"), shellstr); |
| errno = err; |
| } else { |
| (void) shell (shellstr, cp, environ); |
| } |
| |
| pw_free (pw); |
| |
| return (errno == ENOENT ? E_CMD_NOTFOUND : E_CMD_NOEXEC); |
| } |
| |