diff --git a/pkgs/tools/misc/su/default.nix b/pkgs/tools/misc/su/default.nix index 825005d07a38..1cfc80376560 100644 --- a/pkgs/tools/misc/su/default.nix +++ b/pkgs/tools/misc/su/default.nix @@ -14,7 +14,6 @@ stdenv.mkDerivation { # PAM patch taken from SUSE's coreutils-6.7-5.src.rpm. ./su-pam.patch ]; - patchFlags = "-p0"; buildInputs = [pam]; buildPhase = " make -C lib diff --git a/pkgs/tools/misc/su/su-pam.patch b/pkgs/tools/misc/su/su-pam.patch index 6bd4ea2c5e86..3f81a47f12dc 100644 --- a/pkgs/tools/misc/su/su-pam.patch +++ b/pkgs/tools/misc/su/su-pam.patch @@ -1,622 +1,1018 @@ ---- src/getdef.c -+++ src/getdef.c -@@ -0,0 +1,257 @@ -+/* Copyright (C) 2003, 2004, 2005 Thorsten Kukuk -+ Author: Thorsten Kukuk -+ -+ 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 -+#endif -+ -+#define _GNU_SOURCE -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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 -+ -+ 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 - #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 -@@ -66,6 +79,9 @@ - - #if HAVE_SYSLOG_H && HAVE_SYSLOG - # include -+# 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 - #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 -rcN coreutils-6.7-orig/getdef.c coreutils-6.7/getdef.c +*** coreutils-6.7-orig/getdef.c Thu Jan 1 00:00:00 1970 +--- coreutils-6.7/getdef.c Tue Jan 16 22:18:41 2007 +*************** +*** 0 **** +--- 1,257 ---- ++ /* Copyright (C) 2003, 2004, 2005 Thorsten Kukuk ++ Author: Thorsten Kukuk ++ ++ 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 ++ #endif ++ ++ #define _GNU_SOURCE ++ ++ #include ++ #include ++ #include ++ #include ++ #include ++ #include ++ ++ #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 +diff -rcN coreutils-6.7-orig/getdef.h coreutils-6.7/getdef.h +*** coreutils-6.7-orig/getdef.h Thu Jan 1 00:00:00 1970 +--- coreutils-6.7/getdef.h Tue Jan 16 22:18:41 2007 +*************** +*** 0 **** +--- 1,29 ---- ++ /* Copyright (C) 2003, 2005 Thorsten Kukuk ++ Author: Thorsten Kukuk ++ ++ 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_ */ +diff -rcN coreutils-6.7-orig/src/getdef.c coreutils-6.7/src/getdef.c +*** coreutils-6.7-orig/src/getdef.c Thu Jan 1 00:00:00 1970 +--- coreutils-6.7/src/getdef.c Tue Jan 16 22:18:57 2007 +*************** +*** 0 **** +--- 1,257 ---- ++ /* Copyright (C) 2003, 2004, 2005 Thorsten Kukuk ++ Author: Thorsten Kukuk ++ ++ 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 ++ #endif ++ ++ #define _GNU_SOURCE ++ ++ #include ++ #include ++ #include ++ #include ++ #include ++ #include ++ ++ #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 +diff -rcN coreutils-6.7-orig/src/getdef.h coreutils-6.7/src/getdef.h +*** coreutils-6.7-orig/src/getdef.h Thu Jan 1 00:00:00 1970 +--- coreutils-6.7/src/getdef.h Tue Jan 16 22:18:57 2007 +*************** +*** 0 **** +--- 1,29 ---- ++ /* Copyright (C) 2003, 2005 Thorsten Kukuk ++ Author: Thorsten Kukuk ++ ++ 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_ */ +diff -rcN coreutils-6.7-orig/src/su.c coreutils-6.7/src/su.c +*** coreutils-6.7-orig/src/su.c Sun Oct 22 16:54:15 2006 +--- coreutils-6.7/src/su.c Tue Jan 16 22:19:02 2007 +*************** +*** 38,43 **** +--- 38,49 ---- + 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,58 **** +--- 59,71 ---- + #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 +*************** +*** 66,71 **** +--- 79,87 ---- + + #if HAVE_SYSLOG_H && HAVE_SYSLOG + # include ++ # define SYSLOG_SUCCESS 1 ++ # define SYSLOG_FAILURE 1 ++ # define SYSLOG_NON_ROOT 1 + #else + # undef SYSLOG_SUCCESS + # undef SYSLOG_FAILURE +*************** +*** 99,117 **** + # include + #endif + + /* 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 + + /* 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 + + /* The shell to run if none is given in the user's passwd entry. */ + #define DEFAULT_SHELL "/bin/sh" +--- 115,127 ---- + # include + #endif + ++ #include "getdef.h" ++ + /* The default PATH for simulated logins to non-superuser accounts. */ +! #define DEFAULT_LOGIN_PATH "/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin" + + /* The default PATH for simulated logins to superuser accounts. */ +! #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,125 **** +--- 129,137 ---- + /* 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,222 **** +--- 228,253 ---- + } + #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,233 **** + static bool + correct_password (const struct passwd *pw) + { + 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); + + endspent (); + if (sp) +--- 255,303 ---- + 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. */ +! const struct spwd *sp = getspnam (pw->pw_name); + + endspent (); + if (sp) +*************** +*** 248,253 **** +--- 318,324 ---- + 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,279 **** + xsetenv ("USER", pw->pw_name); + xsetenv ("LOGNAME", pw->pw_name); + xsetenv ("PATH", (pw->pw_uid +! ? DEFAULT_LOGIN_PATH +! : DEFAULT_ROOT_LOGIN_PATH)); + } + else + { +--- 343,350 ---- + xsetenv ("USER", pw->pw_name); + xsetenv ("LOGNAME", pw->pw_name); + xsetenv ("PATH", (pw->pw_uid +! ? getdef_str ("PATH", DEFAULT_LOGIN_PATH) +! : getdef_str ("SUPATH", DEFAULT_ROOT_LOGIN_PATH))); + } + else + { +*************** +*** 283,288 **** +--- 354,365 ---- + { + 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,314 **** +--- 380,414 ---- + 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,326 **** +--- 421,523 ---- + 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 */ ++ ++ /* Export env variables declared by PAM modules */ ++ { ++ const char *const *env; ++ ++ env = (const char *const *) pam_getenvlist (pamh); ++ while (env && *env) ++ { ++ ++ if (putenv (*env) != 0) ++ xalloc_die (); ++ env++; ++ } ++ } ++ ++ pam_end (pamh, 0); ++ #endif + + if (simulate_login) + { +*************** +*** 339,344 **** +--- 536,546 ---- + 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,500 **** +--- 697,705 ---- + #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 +*************** +*** 516,524 **** + shell = NULL; + } + shell = xstrdup (shell ? shell : pw->pw_shell); + modify_environment (pw, shell); + +- change_identity (pw); + if (simulate_login && chdir (pw->pw_dir) != 0) + error (0, errno, _("warning: cannot change directory to %s"), pw->pw_dir); + +--- 721,732 ---- + shell = NULL; + } + shell = xstrdup (shell ? shell : pw->pw_shell); ++ change_identity (pw); ++ ++ /* Set environment after pam_open_session, which may put KRB5CCNAME ++ into the pam_env, etc. */ + modify_environment (pw, shell); + + if (simulate_login && chdir (pw->pw_dir) != 0) + error (0, errno, _("warning: cannot change directory to %s"), pw->pw_dir); +