Blob Blame History Raw
--- 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 <unistd.h>.  */
 #define getusershell _getusershell_sys_proto_
 
+#ifdef USE_PAM
+# include <security/pam_appl.h>
+# include <security/pam_misc.h>
+# include <signal.h>
+# include <sys/wait.h>
+# include <sys/fsuid.h>
+#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