summary refs log tree commit diff
path: root/pkgs/tools
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2007-01-11 15:22:59 +0000
committerEelco Dolstra <eelco.dolstra@logicblox.com>2007-01-11 15:22:59 +0000
commit4cb58da8e60af35c6b636d8caa4884391e22fe8f (patch)
treebf83f8a8c96479b4703bb03732e14ef3aa2b326a /pkgs/tools
parent7bc63c5312f7505a5a881b69b9f67a0ab2593571 (diff)
downloadnixlib-4cb58da8e60af35c6b636d8caa4884391e22fe8f.tar
nixlib-4cb58da8e60af35c6b636d8caa4884391e22fe8f.tar.gz
nixlib-4cb58da8e60af35c6b636d8caa4884391e22fe8f.tar.bz2
nixlib-4cb58da8e60af35c6b636d8caa4884391e22fe8f.tar.lz
nixlib-4cb58da8e60af35c6b636d8caa4884391e22fe8f.tar.xz
nixlib-4cb58da8e60af35c6b636d8caa4884391e22fe8f.tar.zst
nixlib-4cb58da8e60af35c6b636d8caa4884391e22fe8f.zip
* Add a working PAM-enabled su.
svn path=/nixpkgs/trunk/; revision=7633
Diffstat (limited to 'pkgs/tools')
-rw-r--r--pkgs/tools/misc/su/default.nix27
-rw-r--r--pkgs/tools/misc/su/su-pam.patch622
2 files changed, 649 insertions, 0 deletions
diff --git a/pkgs/tools/misc/su/default.nix b/pkgs/tools/misc/su/default.nix
new file mode 100644
index 000000000000..825005d07a38
--- /dev/null
+++ b/pkgs/tools/misc/su/default.nix
@@ -0,0 +1,27 @@
+{stdenv, fetchurl, pam}:
+
+# This is just coreutils, except that we only build su, with the PAM
+# patch.  We build su separately because we don't want to give all of
+# coreutils a dependency on PAM.
+
+stdenv.mkDerivation {
+  name = "su-6.7";
+  src = fetchurl {
+    url = ftp://ftp.nluug.nl/pub/gnu/coreutils/coreutils-6.7.tar.bz2;
+    md5 = "a16465d0856cd011a1acc1c21040b7f4";
+  };
+  patches = [
+    # PAM patch taken from SUSE's coreutils-6.7-5.src.rpm.
+    ./su-pam.patch
+  ];
+  patchFlags = "-p0";
+  buildInputs = [pam];
+  buildPhase = "
+    make -C lib
+    make -C src su su_OBJECTS=\"su.o getdef.o\" CFLAGS=\"-DUSE_PAM\" LDFLAGS=\"-lpam -lpam_misc -ldl\"
+  ";
+  installPhase = "
+    ensureDir $out/bin
+    cp src/su $out/bin
+  ";
+}
diff --git a/pkgs/tools/misc/su/su-pam.patch b/pkgs/tools/misc/su/su-pam.patch
new file mode 100644
index 000000000000..6bd4ea2c5e86
--- /dev/null
+++ b/pkgs/tools/misc/su/su-pam.patch
@@ -0,0 +1,622 @@
+--- src/getdef.c
++++ src/getdef.c
+@@ -0,0 +1,257 @@
++/* Copyright (C) 2003, 2004, 2005 Thorsten Kukuk
++   Author: Thorsten Kukuk <kukuk@suse.de>
++
++   This program is free software; you can redistribute it and/or modify
++   it under the terms of the GNU General Public License version 2 as
++   published by the Free Software Foundation.
++
++   This program is distributed in the hope that it will be useful,
++   but WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++   GNU General Public License for more details.
++
++   You should have received a copy of the GNU General Public License
++   along with this program; if not, write to the Free Software Foundation,
++   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
++
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#define _GNU_SOURCE
++
++#include <errno.h>
++#include <ctype.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <limits.h>
++
++#include "getdef.h"
++
++struct item {
++  char *name;         /* name of the option.  */
++  char *value;        /* value of the option.  */
++  struct item *next;  /* pointer to next option.  */
++};
++
++static struct item *list = NULL;
++
++void
++free_getdef_data (void)
++{
++  struct item *ptr;
++
++  ptr = list;
++  while (ptr != NULL)
++    {
++      struct item *tmp;
++      tmp = ptr->next;
++      free (ptr->name);
++      free (ptr->value);
++      free (ptr);
++      ptr = tmp;
++    }
++
++  list = NULL;
++}
++
++/* Add a new entry to the list.  */
++static void
++store (const char *name, const char *value)
++{
++  struct item *new = malloc (sizeof (struct item));
++
++  if (new == NULL)
++    abort ();
++
++  if (name == NULL)
++    abort ();
++
++  new->name = strdup (name);
++  new->value = strdup (value?:"");
++  new->next = list;
++  list = new;
++}
++
++/* search a special entry in the list and return the value.  */
++static const char *
++search (const char *name)
++{
++  struct item *ptr;
++
++  ptr = list;
++  while (ptr != NULL)
++    {
++      if (strcasecmp (name, ptr->name) == 0)
++	return ptr->value;
++      ptr = ptr->next;
++    }
++
++  return NULL;
++}
++
++/* Load the login.defs file (/etc/login.defs)  */
++static void
++load_defaults_internal (const char *filename)
++{
++  FILE *fp;
++  char *buf = NULL;
++  size_t buflen = 0;
++
++  fp = fopen (filename, "r");
++  if (NULL == fp)
++    return;
++
++  while (!feof (fp))
++    {
++      char *tmp, *cp;
++#if defined(HAVE_GETLINE)
++      ssize_t n = getline (&buf, &buflen, fp);
++#elif defined (HAVE_GETDELIM)
++      ssize_t n = getdelim (&buf, &buflen, '\n', fp);
++#else
++      ssize_t n;
++
++      if (buf == NULL)
++        {
++          buflen = 8096;
++          buf = malloc (buflen);
++        }
++      buf[0] = '\0';
++      fgets (buf, buflen - 1, fp);
++      if (buf != NULL)
++        n = strlen (buf);
++      else
++        n = 0;
++#endif /* HAVE_GETLINE / HAVE_GETDELIM */
++      cp = buf;
++
++      if (n < 1)
++        break;
++
++      tmp = strchr (cp, '#');  /* remove comments */
++      if (tmp)
++        *tmp = '\0';
++      while (isspace ((int)*cp))    /* remove spaces and tabs */
++        ++cp;
++      if (*cp == '\0')        /* ignore empty lines */
++        continue;
++
++      if (cp[strlen (cp) - 1] == '\n')
++        cp[strlen (cp) - 1] = '\0';
++
++      tmp = strsep (&cp, " \t=");
++      if (cp != NULL)
++	while (isspace ((int)*cp) || *cp == '=')
++	  ++cp;
++
++      store (tmp, cp);
++    }
++  fclose (fp);
++
++  if (buf)
++    free (buf);
++}
++
++static void
++load_defaults (void)
++{
++  load_defaults_internal ("/etc/default/su");
++  load_defaults_internal ("/etc/login.defs");
++}
++
++int
++getdef_bool (const char *name, int dflt)
++{
++  const char *val;
++
++  if (list == NULL)
++    load_defaults ();
++
++  val = search (name);
++
++  if (val == NULL)
++    return dflt;
++
++  return (strcasecmp (val, "yes") == 0);
++}
++
++long
++getdef_num (const char *name, long dflt)
++{
++  const char *val;
++  char *cp;
++  long retval;
++
++  if (list == NULL)
++    load_defaults ();
++
++  val = search (name);
++
++  if (val == NULL)
++    return dflt;
++
++  retval = strtol (val, &cp, 0);
++  if (*cp != '\0' ||
++      ((retval == LONG_MAX || retval == LONG_MIN) && errno == ERANGE))
++    {
++      fprintf (stderr,
++	       "%s contains invalid numerical value: %s!\n",
++	       name, val);
++      retval = dflt;
++    }
++  return retval;
++}
++
++unsigned long
++getdef_unum (const char *name, unsigned long dflt)
++{
++  const char *val;
++  char *cp;
++  unsigned long retval;
++
++  if (list == NULL)
++    load_defaults ();
++
++  val = search (name);
++
++  if (val == NULL)
++    return dflt;
++
++  retval = strtoul (val, &cp, 0);
++  if (*cp != '\0' || (retval == ULONG_MAX && errno == ERANGE))
++    {
++      fprintf (stderr,
++	       "%s contains invalid numerical value: %s!\n",
++	       name, val);
++      retval = dflt;
++    }
++  return retval;
++}
++
++const char *
++getdef_str (const char *name, const char *dflt)
++{
++  const char *retval;
++
++  if (list == NULL)
++    load_defaults ();
++
++  retval = search (name);
++
++  return retval ?: dflt;
++}
++
++#if defined(TEST)
++
++int
++main ()
++{
++  printf ("CYPT=%s\n", getdef_str ("cRypt", "no"));
++  printf ("LOG_UNKFAIL_ENAB=%s\n", getdef_str ("log_unkfail_enab",""));
++  printf ("DOESNOTEXIST=%s\n", getdef_str ("DOESNOTEXIST","yes"));
++  return 0;
++}
++
++#endif
+--- src/getdef.h
++++ src/getdef.h
+@@ -0,0 +1,29 @@
++/* Copyright (C) 2003, 2005 Thorsten Kukuk
++   Author: Thorsten Kukuk <kukuk@suse.de>
++
++   This program is free software; you can redistribute it and/or modify
++   it under the terms of the GNU General Public License version 2 as
++   published by the Free Software Foundation.
++
++   This program is distributed in the hope that it will be useful,
++   but WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++   GNU General Public License for more details.
++
++   You should have received a copy of the GNU General Public License
++   along with this program; if not, write to the Free Software Foundation,
++   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
++
++#ifndef _GETDEF_H_
++
++#define _GETDEF_H_ 1
++
++extern int getdef_bool (const char *name, int dflt);
++extern long getdef_num (const char *name, long dflt);
++extern unsigned long getdef_unum (const char *name, unsigned long dflt);
++extern const char *getdef_str (const char *name, const char *dflt);
++
++/* Free all data allocated by getdef_* calls before.  */
++extern void free_getdef_data (void);
++
++#endif /* _GETDEF_H_ */
+--- src/su.c
++++ src/su.c
+@@ -38,6 +38,12 @@
+    restricts who can su to UID 0 accounts.  RMS considers that to
+    be fascist.
+ 
++   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.d/su
++
+    Compile-time options:
+    -DSYSLOG_SUCCESS	Log successful su's (by default, to root) with syslog.
+    -DSYSLOG_FAILURE	Log failed su's (by default, to root) with syslog.
+@@ -53,6 +59,13 @@
+ #include <sys/types.h>
+ #include <pwd.h>
+ #include <grp.h>
++#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
+ 
+ /* Hide any system prototype for getusershell.
+    This is necessary because some Cray systems have a conflicting
+@@ -66,6 +79,9 @@
+ 
+ #if HAVE_SYSLOG_H && HAVE_SYSLOG
+ # include <syslog.h>
++# define SYSLOG_SUCCESS  1
++# define SYSLOG_FAILURE  1
++# define SYSLOG_NON_ROOT 1
+ #else
+ # undef SYSLOG_SUCCESS
+ # undef SYSLOG_FAILURE
+@@ -99,19 +115,13 @@
+ # include <paths.h>
+ #endif
+ 
++#include "getdef.h"
++
+ /* The default PATH for simulated logins to non-superuser accounts.  */
+-#ifdef _PATH_DEFPATH
+-# define DEFAULT_LOGIN_PATH _PATH_DEFPATH
+-#else
+-# define DEFAULT_LOGIN_PATH ":/usr/ucb:/bin:/usr/bin"
+-#endif
++#define DEFAULT_LOGIN_PATH "/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin"
+ 
+ /* The default PATH for simulated logins to superuser accounts.  */
+-#ifdef _PATH_DEFPATH_ROOT
+-# define DEFAULT_ROOT_LOGIN_PATH _PATH_DEFPATH_ROOT
+-#else
+-# define DEFAULT_ROOT_LOGIN_PATH "/usr/ucb:/bin:/usr/bin:/etc"
+-#endif
++#define DEFAULT_ROOT_LOGIN_PATH "/usr/sbin:/bin:/usr/bin:/sbin:/usr/X11R6/bin"
+ 
+ /* The shell to run if none is given in the user's passwd entry.  */
+ #define DEFAULT_SHELL "/bin/sh"
+@@ -119,7 +129,9 @@
+ /* The user to become if none is specified.  */
+ #define DEFAULT_USER "root"
+ 
++#ifndef USE_PAM
+ char *crypt ();
++#endif
+ char *getusershell ();
+ void endusershell ();
+ void setusershell ();
+@@ -216,7 +228,26 @@
+ }
+ #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(a) \
++  if (retval) \
++    { \
++      pam_end (pamh, PAM_SUCCESS); \
++      a; \
++    }
++#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.  */
+@@ -224,10 +255,49 @@
+ static bool
+ correct_password (const struct passwd *pw)
+ {
++#ifdef USE_PAM
++  const struct passwd *lpw;
++  const char *cp;
++
++  retval = pam_start ("su", pw->pw_name, &conv, &pamh);
++  PAM_BAIL_P (return false);
++
++  if (isatty (0) && (cp = ttyname (0)) != NULL)
++    {
++      const char *tty;
++
++      if (strncmp (cp, "/dev/", 5) == 0)
++	tty = cp + 5;
++      else
++	tty = cp;
++      retval = pam_set_item (pamh, PAM_TTY, tty);
++      PAM_BAIL_P (return false);
++    }
++  cp = getlogin ();
++  if (!(cp && *cp && (lpw = getpwnam (cp)) != NULL && lpw->pw_uid == getuid ()))
++    lpw = getpwuid (getuid ());
++  if (lpw)
++    {
++      retval = pam_set_item (pamh, PAM_RUSER, (const void *) lpw->pw_name);
++      PAM_BAIL_P (return false);
++    }
++  retval = pam_authenticate (pamh, 0);
++  PAM_BAIL_P (return false);
++  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 (return false);
++    }
++  PAM_BAIL_P (return false);
++  /* must be authenticated if this point was reached */
++  return true;
++#else /* !USE_PAM */
+   char *unencrypted, *encrypted, *correct;
+ #if HAVE_GETSPNAM && HAVE_STRUCT_SPWD_SP_PWDP
+   /* Shadow passwd stuff for SVR3 and maybe other systems.  */
+-  struct spwd *sp = getspnam (pw->pw_name);
++  const struct spwd *sp = getspnam (pw->pw_name);
+ 
+   endspent ();
+   if (sp)
+@@ -248,6 +318,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
+@@ -272,8 +343,8 @@
+       xsetenv ("USER", pw->pw_name);
+       xsetenv ("LOGNAME", pw->pw_name);
+       xsetenv ("PATH", (pw->pw_uid
+-			? DEFAULT_LOGIN_PATH
+-			: DEFAULT_ROOT_LOGIN_PATH));
++			? getdef_str ("PATH", DEFAULT_LOGIN_PATH)
++			: getdef_str ("SUPATH", DEFAULT_ROOT_LOGIN_PATH)));
+     }
+   else
+     {
+@@ -283,6 +354,12 @@
+ 	{
+ 	  xsetenv ("HOME", pw->pw_dir);
+ 	  xsetenv ("SHELL", shell);
++	  if (getdef_bool ("ALWAYS_SET_PATH", 0))
++	    xsetenv ("PATH", (pw->pw_uid
++			      ? getdef_str ("PATH",
++					    DEFAULT_LOGIN_PATH)
++			      : getdef_str ("SUPATH",
++					    DEFAULT_ROOT_LOGIN_PATH)));
+ 	  if (pw->pw_uid)
+ 	    {
+ 	      xsetenv ("USER", pw->pw_name);
+@@ -303,12 +380,35 @@
+     error (EXIT_FAIL, errno, _("cannot set groups"));
+   endgrent ();
+ #endif
++#ifdef USE_PAM
++  retval = pam_setcred (pamh, PAM_ESTABLISH_CRED);
++  if (retval != PAM_SUCCESS)
++    error (EXIT_FAIL, 0, "%s", pam_strerror (pamh, retval));
++
++  retval = pam_open_session (pamh,0);
++  if (retval != PAM_SUCCESS)
++    {
++       pam_setcred (pamh, PAM_DELETE_CRED);
++       error (EXIT_FAIL, 0, "could not open session: %s",
++              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 bool caught = false;
++/* Signal handler for parent process later */
++static void
++su_catch_sig (int sig)
++{
++  caught = true;
++}
++#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
+@@ -321,6 +421,88 @@
+   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
++  pid_t child;
++  sigset_t ourset;
++  int status;
++
++  child = fork ();
++  if (child == (pid_t) -1)
++    error (EXIT_FAILURE, errno, "cannot fork");
++
++  if (child != 0)
++    {
++      /* parent only */
++      sigfillset (&ourset);
++      if (sigprocmask (SIG_BLOCK, &ourset, NULL))
++	{
++	  error (0, errno, "cannot block signals");
++	  caught = true;
++	}
++      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))
++	    {
++	      error (0, errno, "cannot set signal handler");
++	      caught = true;
++	    }
++	}
++      if (!caught)
++	{
++	  for (;;)
++	    {
++	      pid_t pid;
++
++	      pid = waitpid (child, &status, WUNTRACED);
++
++	      if (WIFSTOPPED (status))
++		{
++		  kill (getpid (), SIGSTOP);
++		  /* once we get here, we must have resumed */
++		  kill (pid, SIGCONT);
++		}
++	      else
++		break;
++	    }
++	  if (WIFSIGNALED (status))
++	    status = WTERMSIG (status) + 128;
++	  else
++	    status = WEXITSTATUS (status);
++	}
++      else
++	status = 1;
++
++      if (caught)
++	{
++	  fprintf (stderr, "\nSession terminated, killing shell...");
++	  kill (child, SIGTERM);
++	}
++      retval = pam_setcred (pamh, PAM_DELETE_CRED);
++      PAM_BAIL_P (exit (EXIT_FAILURE));
++      retval = pam_close_session (pamh, 0);
++      PAM_BAIL_P (exit (EXIT_FAILURE));
++      retval = pam_end (pamh, PAM_SUCCESS);
++      PAM_BAIL_P (exit (EXIT_FAILURE));
++      if (caught)
++	{
++	  sleep (2);
++	  kill (child, SIGKILL);
++	  fprintf (stderr, " ...killed.\n");
++	}
++      exit (status);
++    }
++
++  /* child shell */
++  pam_end (pamh, 0);
++#endif
+ 
+   if (simulate_login)
+     {
+@@ -339,6 +521,11 @@
+     args[argno++] = "-f";
+   if (command)
+     {
++      if (simulate_login)
++	/* Bash 2.0 have to be invoked as `-su'.  See the comments in
++	   `shell.c (run_startup_files)'.  */
++	args[0] = "-su";
++
+       args[argno++] = "-c";
+       args[argno++] = command;
+     }
+@@ -495,6 +682,9 @@
+ #ifdef SYSLOG_FAILURE
+       log_su (pw, false);
+ #endif
++#ifdef USE_PAM
++      sleep (getdef_num ("FAIL_DELAY", 1));
++#endif
+       error (EXIT_FAIL, 0, _("incorrect password"));
+     }
+ #ifdef SYSLOG_SUCCESS