--- coreutils-5.92/src/Makefile.am.pam 2005-10-24 17:58:21.000000000 +0100 +++ coreutils-5.92/src/Makefile.am 2005-10-24 17:58:21.000000000 +0100 @@ -93,7 +93,7 @@ uptime_LDADD = $(LDADD) $(GETLOADAVG_LIBS) -su_LDADD = $(LDADD) $(LIB_CRYPT) +su_LDADD = $(LDADD) $(LIB_CRYPT) @LIB_PAM@ $(PROGRAMS): ../lib/libcoreutils.a --- coreutils-5.92/src/su.c.pam 2005-10-24 17:58:21.000000000 +0100 +++ coreutils-5.92/src/su.c 2005-10-24 18:06:22.000000000 +0100 @@ -38,6 +38,16 @@ restricts who can su to UID 0 accounts. RMS considers that to be fascist. +#ifdef USE_PAM + + Actually, with PAM, su has nothing to do with whether or not a + wheel group is enforced by su. RMS tries to restrict your access + to a su which implements the wheel group, but PAM considers that + to be fascist, and gives the user/sysadmin the opportunity to + enforce a wheel group by proper editing of /etc/pam.conf + +#endif + Options: -, -l, --login Make the subshell a login shell. Unset all environment variables except @@ -81,6 +91,15 @@ prototype (returning `int') in . */ #define getusershell _getusershell_sys_proto_ +#ifdef USE_PAM +# include +# include +# include +# include +# include +# include +#endif /* USE_PAM */ + #include "system.h" #include "dirname.h" @@ -150,7 +169,9 @@ /* The user to become if none is specified. */ #define DEFAULT_USER "root" +#ifndef USE_PAM char *crypt (); +#endif char *getpass (); char *getusershell (); void endusershell (); @@ -158,8 +179,13 @@ extern char **environ; -static void run_shell (char const *, char const *, char **, size_t) +static void run_shell (char const *, char const *, char **, size_t, + const struct passwd *) +#ifdef USE_PAM + ; +#else ATTRIBUTE_NORETURN; +#endif /* The name this program was run with. */ char *program_name; @@ -248,7 +274,22 @@ } #endif +#ifdef USE_PAM +static pam_handle_t *pamh = NULL; +static int retval; +static struct pam_conv conv = { + misc_conv, + NULL +}; + +#define PAM_BAIL_P if (retval) { \ + pam_end(pamh, PAM_SUCCESS); \ + return 0; \ +} +#endif + /* Ask the user for a password. + If PAM is in use, let PAM ask for the password if necessary. Return true if the user gives the correct password for entry PW, false if not. Return true without asking for a password if run by UID 0 or if PW has an empty password. */ @@ -256,6 +297,44 @@ static bool correct_password (const struct passwd *pw) { +#ifdef USE_PAM + struct passwd *caller; + char *tty_name, *ttyn; + retval = pam_start(PROGRAM_NAME, pw->pw_name, &conv, &pamh); + PAM_BAIL_P; + + if (getuid() != 0 && !isatty(0)) { + fprintf(stderr, "standard in must be a tty\n"); + exit(1); + } + + caller = getpwuid(getuid()); + if(caller != NULL && caller->pw_name != NULL) { + retval = pam_set_item(pamh, PAM_RUSER, caller->pw_name); + PAM_BAIL_P; + } + + ttyn = ttyname(0); + if (ttyn) { + if (strncmp(ttyn, "/dev/", 5) == 0) + tty_name = ttyn+5; + else + tty_name = ttyn; + retval = pam_set_item(pamh, PAM_TTY, tty_name); + PAM_BAIL_P; + } + retval = pam_authenticate(pamh, 0); + PAM_BAIL_P; + retval = pam_acct_mgmt(pamh, 0); + if (retval == PAM_NEW_AUTHTOK_REQD) { + /* password has expired. Offer option to change it. */ + retval = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK); + PAM_BAIL_P; + } + PAM_BAIL_P; + /* must be authenticated if this point was reached */ + return 1; +#else /* !USE_PAM */ char *unencrypted, *encrypted, *correct; #if HAVE_GETSPNAM && HAVE_STRUCT_SPWD_SP_PWDP /* Shadow passwd stuff for SVR3 and maybe other systems. */ @@ -280,6 +359,7 @@ encrypted = crypt (unencrypted, correct); memset (unencrypted, 0, strlen (unencrypted)); return STREQ (encrypted, correct); +#endif /* !USE_PAM */ } /* Update `environ' for the new shell based on PW, with SHELL being @@ -293,12 +373,18 @@ /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH. Unset all other environment variables. */ char const *term = getenv ("TERM"); + char const *display = getenv ("DISPLAY"); + char const *xauthority = getenv ("XAUTHORITY"); if (term) term = xstrdup (term); environ = xmalloc ((6 + !!term) * sizeof (char *)); environ[0] = NULL; if (term) xsetenv ("TERM", term); + if (display) + xsetenv ("DISPLAY", display); + if (xauthority) + xsetenv ("XAUTHORITY", xauthority); xsetenv ("HOME", pw->pw_dir); xsetenv ("SHELL", shell); xsetenv ("USER", pw->pw_name); @@ -331,8 +417,13 @@ { #ifdef HAVE_INITGROUPS errno = 0; - if (initgroups (pw->pw_name, pw->pw_gid) == -1) + if (initgroups (pw->pw_name, pw->pw_gid) == -1) { +#ifdef USE_PAM + pam_close_session(pamh, 0); + pam_end(pamh, PAM_ABORT); +#endif error (EXIT_FAIL, errno, _("cannot set groups")); + } endgrent (); #endif if (setgid (pw->pw_gid)) @@ -341,6 +432,31 @@ error (EXIT_FAIL, errno, _("cannot set user id")); } +#ifdef USE_PAM +static int caught=0; +/* Signal handler for parent process later */ +static void su_catch_sig(int sig) +{ + ++caught; +} + +int +pam_copyenv (pam_handle_t *pamh) +{ + char **env; + + env = pam_getenvlist(pamh); + if(env) { + while(*env) { + if (putenv (*env)) + xalloc_die (); + env++; + } + } + return(0); +} +#endif + /* Run SHELL, or DEFAULT_SHELL if SHELL is empty. If COMMAND is nonzero, pass it to the shell with the -c option. Pass ADDITIONAL_ARGS to the shell as more arguments; there @@ -348,17 +464,49 @@ static void run_shell (char const *shell, char const *command, char **additional_args, - size_t n_additional_args) + size_t n_additional_args, const struct passwd *pw) { size_t n_args = 1 + fast_startup + 2 * !!command + n_additional_args + 1; char const **args = xnmalloc (n_args, sizeof *args); size_t argno = 1; +#ifdef USE_PAM + int child; + sigset_t ourset; + int status; + + retval = pam_open_session(pamh,0); + if (retval != PAM_SUCCESS) { + fprintf (stderr, "could not open session\n"); + exit (1); + } + +/* do this at the last possible moment, because environment variables may + be passed even in the session phase +*/ + if(pam_copyenv(pamh) != PAM_SUCCESS) + fprintf (stderr, "error copying PAM environment\n"); + + /* Credentials should be set in the parent */ + if (pam_setcred(pamh, PAM_ESTABLISH_CRED) != PAM_SUCCESS) { + pam_close_session(pamh, 0); + fprintf(stderr, "could not set PAM credentials\n"); + exit(1); + } + + child = fork(); + if (child == 0) { /* child shell */ + change_identity (pw); + pam_end(pamh, 0); +#endif if (simulate_login) { char *arg0; char *shell_basename; + if(chdir(pw->pw_dir)) + error(0, errno, _("warning: cannot change directory to %s"), pw->pw_dir); + shell_basename = base_name (shell); arg0 = xmalloc (strlen (shell_basename) + 2); arg0[0] = '-'; @@ -383,6 +531,66 @@ error (0, errno, "%s", shell); exit (exit_status); } +#ifdef USE_PAM + } else if (child == -1) { + fprintf(stderr, "can not fork user shell: %s", strerror(errno)); + pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT); + pam_close_session(pamh, 0); + pam_end(pamh, PAM_ABORT); + exit(1); + } + /* parent only */ + sigfillset(&ourset); + if (sigprocmask(SIG_BLOCK, &ourset, NULL)) { + fprintf(stderr, "%s: signal malfunction\n", PROGRAM_NAME); + caught = 1; + } + if (!caught) { + struct sigaction action; + action.sa_handler = su_catch_sig; + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + sigemptyset(&ourset); + if (sigaddset(&ourset, SIGTERM) + || sigaddset(&ourset, SIGALRM) + || sigaction(SIGTERM, &action, NULL) + || sigprocmask(SIG_UNBLOCK, &ourset, NULL)) { + fprintf(stderr, "%s: signal masking malfunction\n", PROGRAM_NAME); + caught = 1; + } + } + if (!caught) { + do { + int pid; + + pid = waitpid(-1, &status, WUNTRACED); + + if (WIFSTOPPED(status)) { + kill(getpid(), SIGSTOP); + /* once we get here, we must have resumed */ + kill(pid, SIGCONT); + } + } while (WIFSTOPPED(status)); + } + + if (caught) { + fprintf(stderr, "\nSession terminated, killing shell..."); + kill (child, SIGTERM); + } + /* Not checking retval on this because we need to call close session */ + pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT); + retval = pam_close_session(pamh, 0); + PAM_BAIL_P; + retval = pam_end(pamh, PAM_SUCCESS); + PAM_BAIL_P; + if (caught) { + sleep(2); + kill(child, SIGKILL); + fprintf(stderr, " ...killed.\n"); + exit(-1); + } + exit (WEXITSTATUS(status)); +#endif /* USE_PAM */ } /* Return true if SHELL is a restricted shell (one not returned by @@ -550,9 +758,11 @@ shell = xstrdup (shell ? shell : pw->pw_shell); modify_environment (pw, shell); +#ifndef USE_PAM change_identity (pw); +#endif if (simulate_login && chdir (pw->pw_dir) != 0) error (0, errno, _("warning: cannot change directory to %s"), pw->pw_dir); - run_shell (shell, command, argv + optind, MAX (0, argc - optind)); + run_shell (shell, command, argv + optind, MAX (0, argc - optind), pw); } --- coreutils-5.92/configure.ac.pam 2005-10-24 17:58:21.000000000 +0100 +++ coreutils-5.92/configure.ac 2005-10-24 17:58:21.000000000 +0100 @@ -28,6 +28,13 @@ AB_INIT() AM_INIT_AUTOMAKE([1.8.3 gnits dist-bzip2]) +dnl Give the chance to enable PAM +AC_ARG_ENABLE(pam, dnl +[ --enable-pam Enable use of the PAM libraries], +[AC_DEFINE(USE_PAM, 1, [Define if you want to use PAM]) +LIB_PAM="-ldl -lpam -lpam_misc" +AC_SUBST(LIB_PAM)]) + gl_DEFAULT_POSIX2_VERSION gl_USE_SYSTEM_EXTENSIONS gl_PERL --- coreutils-5.92/config.hin.pam 2005-10-24 17:58:21.000000000 +0100 +++ coreutils-5.92/config.hin 2005-10-24 17:58:21.000000000 +0100 @@ -1526,6 +1526,9 @@ /* Define if you want access control list support. */ #undef USE_ACL +/* Define if you want to use PAM */ +#undef USE_PAM + /* Version number of package */ #undef VERSION