--- sh-utils-2.0/doc/sh-utils.texi.nopam Sat Jul 31 19:19:04 1999 +++ sh-utils-2.0/doc/sh-utils.texi Sat Aug 28 13:47:08 1999 @@ -2990,33 +2990,6 @@ @end table -@cindex wheel group, not supported -@cindex group wheel, not supported -@cindex fascism -@heading Why GNU @code{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 -@code{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 Delaying @chapter Delaying --- sh-utils-2.0/src/su.c.nopam Wed Mar 31 14:36:01 1999 +++ sh-utils-2.0/src/su.c Sat Aug 28 14:07:53 1999 @@ -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 @@ -75,6 +85,13 @@ #include #include #include +#ifdef USE_PAM +#include +#include +#include +#include +#include +#endif /* Hide any system prototype for getusershell. This is necessary because some Cray systems have a conflicting @@ -143,7 +160,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 (); @@ -264,7 +283,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. */ @@ -272,6 +306,28 @@ static int correct_password (const struct passwd *pw) { +#ifdef USE_PAM + + /* root always succeeds; this isn't an authentication question (no + * extra privs are being granted) so it shouldn't authenticate with PAM. + * However, we want to create the pam_handle so that proper credentials + * are created later with pam_setcred(). */ + retval = pam_start("su", pw->pw_name, &conv, &pamh); + PAM_BAIL_P; + if (getuid () == 0) + return 1; + 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; #ifdef HAVE_SHADOW_H /* Shadow passwd stuff for SVR3 and maybe other systems. */ @@ -296,6 +352,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 @@ -351,23 +408,51 @@ error (1, 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 (1, errno, _("cannot set group id")); if (setuid (pw->pw_uid)) error (1, 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; +} +#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); + } + child = fork(); + if (child == 0) { /* child shell */ + change_identity (pw); + pam_end(pamh, 0); +#endif if (additional_args) args = (const char **) xmalloc (sizeof (char *) * (10 + elements (additional_args))); @@ -399,6 +485,58 @@ args[argno] = NULL; execv (shell, (char **) args); error (1, errno, _("cannot run %s"), shell); +#ifdef USE_PAM + } + /* parent only */ + sigfillset(&ourset); + if (sigprocmask(SIG_BLOCK, &ourset, NULL)) { + fprintf(stderr, "su: signal malfunction\n"); + 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, "su: signal masking malfunction\n"); + 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)); + } else + status = -1; + + 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 (status); +#endif /* USE_PAM */ } /* Return 1 if SHELL is a restricted shell (one not returned by @@ -568,9 +706,13 @@ } modify_environment (pw, shell); +#ifdef USE_PAM + setfsuid(pw->pw_uid); +#else change_identity (pw); +#endif if (simulate_login && chdir (pw->pw_dir)) error (0, errno, _("warning: cannot change directory to %s"), pw->pw_dir); - run_shell (shell, command, additional_args); + run_shell (shell, command, additional_args, pw); } --- sh-utils-2.0/src/Makefile.in.nopam Sat Aug 28 12:57:51 1999 +++ sh-utils-2.0/src/Makefile.in Sat Aug 28 14:15:09 1999 @@ -513,7 +513,7 @@ su: $(su_OBJECTS) $(su_DEPENDENCIES) @rm -f su - $(LINK) $(su_LDFLAGS) $(su_OBJECTS) $(su_LDADD) $(LIBS) + $(LINK) $(su_LDFLAGS) $(su_OBJECTS) $(su_LDADD) $(LIBS) $(PAMLIBS) tee: $(tee_OBJECTS) $(tee_DEPENDENCIES) @rm -f tee --- sh-utils-2.0/Makefile.in.nopam Sun Aug 15 00:42:33 1999 +++ sh-utils-2.0/Makefile.in Sat Aug 28 14:25:30 1999 @@ -216,7 +216,7 @@ else \ local_target="$$target"; \ fi; \ - (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target PAMLIBS="$(PAMLIBS)") \ || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ done; \ if test "$$dot_seen" = "no"; then \ @@ -245,7 +245,7 @@ done && test -z "$$fail" tags-recursive: list='$(SUBDIRS)'; for subdir in $$list; do \ - test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags PAMLIBS="$(PAMLIBS)"); \ done tags: TAGS