--- coreutils-5.2.0/src/Makefile.am.pam 2004-02-23 17:40:54.000000000 +0000 +++ coreutils-5.2.0/src/Makefile.am 2004-02-23 17:40:54.000000000 +0000 @@ -66,7 +66,7 @@ uptime_LDADD = $(LDADD) $(GETLOADAVG_LIBS) -su_LDADD = $(LDADD) $(LIB_CRYPT) +su_LDADD = $(LDADD) $(LIB_CRYPT) @LIB_PAM@ $(PROGRAMS): ../lib/libfetish.a --- coreutils-5.2.0/src/su.c.pam 2004-02-23 17:40:54.000000000 +0000 +++ coreutils-5.2.0/src/su.c 2004-02-23 17:40:54.000000000 +0000 @@ -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,14 @@ prototype (returning `int') in . */ #define getusershell _getusershell_sys_proto_ +#ifdef USE_PAM +# include +# include +# include +# include +# include +#endif /* USE_PAM */ + #include "system.h" #include "dirname.h" @@ -150,7 +168,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 +178,12 @@ extern char **environ; -static void run_shell (const char *, const char *, char **) +static void run_shell (const char *, const char *, char **, const struct passwd *) +#ifdef USE_PAM + ; +#else ATTRIBUTE_NORETURN; +#endif /* The name this program was run with. */ char *program_name; @@ -271,7 +295,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 1 if the user gives the correct password for entry PW, 0 if not. Return 1 without asking for a password if run by UID 0 or if PW has an empty password. */ @@ -279,6 +318,34 @@ static int correct_password (const struct passwd *pw) { +#ifdef USE_PAM + struct passwd *caller; + 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; + } + + 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. */ @@ -303,6 +370,7 @@ encrypted = crypt (unencrypted, correct); memset (unencrypted, 0, strlen (unencrypted)); return strcmp (encrypted, correct) == 0; +#endif /* !USE_PAM */ } /* Update `environ' for the new shell based on PW, with SHELL being @@ -312,16 +380,24 @@ modify_environment (const struct passwd *pw, const char *shell) { char *term; + char *display; + char *xauthority; if (simulate_login) { - /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH. + /* Leave TERM, DISPLAY, XAUTHORITY unchanged. Set HOME, SHELL, USER, LOGNAME, PATH. Unset all other environment variables. */ term = getenv ("TERM"); + display = getenv ("DISPLAY"); + xauthority = getenv ("XAUTHORITY"); environ = xmalloc (2 * sizeof (char *)); environ[0] = 0; if (term) xputenv (concat ("TERM", "=", term)); + if (display) + xputenv (concat ("DISPLAY", "=", display)); + if (xauthority) + xputenv (concat ("XAUTHORITY", "=", xauthority)); xputenv (concat ("HOME", "=", pw->pw_dir)); xputenv (concat ("SHELL", "=", shell)); xputenv (concat ("USER", "=", pw->pw_name)); @@ -358,22 +434,73 @@ error (EXIT_FAIL, errno, _("cannot set groups")); endgrent (); #endif +#ifdef USE_PAM + retval = pam_setcred(pamh, PAM_ESTABLISH_CRED); + if (retval != PAM_SUCCESS) + error (1, 0, pam_strerror(pamh, retval)); +#endif /* USE_PAM */ if (setgid (pw->pw_gid)) error (EXIT_FAIL, errno, _("cannot set group id")); if (setuid (pw->pw_uid)) 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) { + xputenv(*env); + 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. If ADDITIONAL_ARGS is nonzero, pass it to the shell as more arguments. */ static void -run_shell (const char *shell, const char *command, char **additional_args) +run_shell (const char *shell, const char *command, char **additional_args, const struct passwd *pw) { const char **args; int 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"); + + child = fork(); + if (child == 0) { /* child shell */ + change_identity (pw); + pam_end(pamh, 0); +#endif if (additional_args) args = xmalloc (sizeof (char *) @@ -385,6 +512,9 @@ 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] = '-'; @@ -411,6 +541,61 @@ 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)); + 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); + } + 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 1 if SHELL is a restricted shell (one not returned by @@ -586,9 +771,13 @@ } modify_environment (pw, shell); + +#ifdef USE_PAM + setfsuid(pw->pw_uid); + setfsgid(pw->pw_gid); +#else change_identity (pw); - if (simulate_login && chdir (pw->pw_dir)) - error (0, errno, _("warning: cannot change directory to %s"), pw->pw_dir); +#endif - run_shell (shell, command, additional_args); + run_shell (shell, command, additional_args, pw); } --- coreutils-5.2.0/configure.ac.pam 2004-02-23 17:40:54.000000000 +0000 +++ coreutils-5.2.0/configure.ac 2004-02-23 17:40:54.000000000 +0000 @@ -7,6 +7,13 @@ AM_INIT_AUTOMAKE([1.8 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 jm_PERL --- coreutils-5.2.0/config.hin.pam 2004-02-23 17:40:54.000000000 +0000 +++ coreutils-5.2.0/config.hin 2004-02-23 17:40:54.000000000 +0000 @@ -1365,6 +1365,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 --- coreutils-5.2.1/doc/coreutils.texi.pam 2004-05-18 11:41:14.026354659 +0100 +++ coreutils-5.2.1/doc/coreutils.texi 2004-05-18 11:48:27.056915340 +0100 @@ -11855,8 +11855,11 @@ @findex syslog @command{su} can optionally be compiled to use @code{syslog} to report failed, and optionally successful, @command{su} attempts. (If the system -supports @code{syslog}.) However, GNU @command{su} does not check if the -user is a member of the @code{wheel} group; see below. +supports @code{syslog}.) + +This version of @command{su} has support for using PAM for +authentication. You can edit @file{/etc/pam.d/su} to customize its +behaviour. The program accepts the following options. Also see @ref{Common options}. @@ -11937,33 +11940,6 @@ the exit status of the subshell otherwise @end display -@cindex wheel group, not supported -@cindex group wheel, not supported -@cindex fascism -@subsection Why GNU @command{su} does not support the @samp{wheel} group - -(This section is by Richard Stallman.) - -@cindex Twenex -@cindex MIT AI lab -Sometimes a few of the users try to hold total power over all the -rest. For example, in 1984, a few users at the MIT AI lab decided to -seize power by changing the operator password on the Twenex system and -keeping it secret from everyone else. (I was able to thwart this coup -and give power back to the users by patching the kernel, but I -wouldn't know how to do that in Unix.) - -However, occasionally the rulers do tell someone. Under the usual -@command{su} mechanism, once someone learns the root password who -sympathizes with the ordinary users, he or she can tell the rest. The -``wheel group'' feature would make this impossible, and thus cement the -power of the rulers. - -I'm on the side of the masses, not that of the rulers. If you are -used to supporting the bosses and sysadmins in whatever they do, you -might find this idea strange at first. - - @node Process control @chapter Process control