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 diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index d4c20a937bff..064985b32eee 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -485,6 +485,10 @@ rec { inherit (gtkLibs) glib; }; + su = import ../tools/misc/su { + inherit fetchurl stdenv pam; + }; + tightvnc = import ../tools/admin/tightvnc { inherit fetchurl stdenv x11 zlib libjpeg; inherit (xlibs) imake gccmakedep libXmu libXaw libXpm libXp;