SHA256
1
0
forked from pool/coreutils
coreutils/coreutils-6.8-su.diff

1024 lines
27 KiB
Diff

--- Makefile.in
+++ Makefile.in
@@ -732,6 +732,7 @@ PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
+PAM_LIBS = @PAM_LIBS@
PATH_SEPARATOR = @PATH_SEPARATOR@
PERL = @PERL@
POSIX_SHELL = @POSIX_SHELL@
--- configure
+++ configure
@@ -612,6 +612,7 @@ OPTIONAL_BIN_PROGS
INSTALL_SU
LIB_GMP
LIB_CRYPT
+PAM_LIBS
WERROR_CFLAGS
SEQ_LIBM
LIB_CAP
@@ -1231,6 +1232,7 @@ with_included_regex
enable_xattr
enable_libcap
enable_gcc_warnings
+enable_pam
with_gmp
enable_install_program
enable_no_install_program
@@ -1877,6 +1879,7 @@ Optional Features:
--disable-xattr do not support extended attributes
--disable-libcap disable libcap support
--enable-gcc-warnings turn on lots of GCC warnings (not recommended)
+ --disable-pam Enable PAM support in su (default=auto)
--enable-install-program=PROG_LIST
install the programs in PROG_LIST (comma-separated,
default: none)
@@ -26931,7 +26934,6 @@ fi
-
XGETTEXT_EXTRA_OPTIONS="$XGETTEXT_EXTRA_OPTIONS --keyword='proper_name:1,\"This is a proper name. See the gettext manual, section Names.\"'"
@@ -39096,6 +39098,111 @@ $as_echo "#define HAVE_WORKING_FORK 1" >
fi
+# Check whether --enable-pam was given.
+if test "${enable_pam+set}" = set; then
+ enableval=$enable_pam;
+else
+ enable_pam=yes
+fi
+
+if test "x$enable_pam" != xno; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pam_start in -lpam" >&5
+$as_echo_n "checking for pam_start in -lpam... " >&6; }
+if test "${ac_cv_lib_pam_pam_start+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpam $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pam_start ();
+int
+main ()
+{
+return pam_start ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then
+ ac_cv_lib_pam_pam_start=yes
+else
+ ac_cv_lib_pam_pam_start=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pam_pam_start" >&5
+$as_echo "$ac_cv_lib_pam_pam_start" >&6; }
+if test "x$ac_cv_lib_pam_pam_start" = x""yes; then
+ enable_pam=yes
+else
+ enable_pam=no
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for misc_conv in -lpam_misc" >&5
+$as_echo_n "checking for misc_conv in -lpam_misc... " >&6; }
+if test "${ac_cv_lib_pam_misc_misc_conv+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpam_misc $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char misc_conv ();
+int
+main ()
+{
+return misc_conv ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then
+ ac_cv_lib_pam_misc_misc_conv=yes
+else
+ ac_cv_lib_pam_misc_misc_conv=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pam_misc_misc_conv" >&5
+$as_echo "$ac_cv_lib_pam_misc_misc_conv" >&6; }
+if test "x$ac_cv_lib_pam_misc_misc_conv" = x""yes; then
+ :
+else
+ enable_pam=no
+fi
+
+ if test "x$enable_pam" != xno; then
+
+$as_echo "#define USE_PAM 1" >>confdefs.h
+
+ PAM_LIBS="-lpam -lpam_misc"
+
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable PAM support in su" >&5
+$as_echo_n "checking whether to enable PAM support in su... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_pam" >&5
+$as_echo "$enable_pam" >&6; }
+
optional_bin_progs=
for ac_func in uname
do
--- configure.ac
+++ configure.ac
@@ -79,6 +79,20 @@ fi
AC_FUNC_FORK
+AC_ARG_ENABLE(pam, AS_HELP_STRING([--disable-pam],
+ [Enable PAM support in su (default=auto)]), , [enable_pam=yes])
+if test "x$enable_pam" != xno; then
+ AC_CHECK_LIB([pam], [pam_start], [enable_pam=yes], [enable_pam=no])
+ AC_CHECK_LIB([pam_misc], [misc_conv], [:], [enable_pam=no])
+ if test "x$enable_pam" != xno; then
+ AC_DEFINE(USE_PAM, 1, [Define if you want to use PAM])
+ PAM_LIBS="-lpam -lpam_misc"
+ AC_SUBST(PAM_LIBS)
+ fi
+fi
+AC_MSG_CHECKING([whether to enable PAM support in su])
+AC_MSG_RESULT([$enable_pam])
+
optional_bin_progs=
AC_CHECK_FUNCS([uname],
gl_ADD_PROG([optional_bin_progs], [uname]))
--- doc/Makefile.in
+++ doc/Makefile.in
@@ -713,6 +713,7 @@ PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
+PAM_LIBS = @PAM_LIBS@
PATH_SEPARATOR = @PATH_SEPARATOR@
PERL = @PERL@
POSIX_SHELL = @POSIX_SHELL@
--- gnulib-tests/Makefile.in
+++ gnulib-tests/Makefile.in
@@ -1421,6 +1421,7 @@ PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
+PAM_LIBS = @PAM_LIBS@
PATH_SEPARATOR = @PATH_SEPARATOR@
PERL = @PERL@
POSIX_SHELL = @POSIX_SHELL@
--- lib/Makefile.in
+++ lib/Makefile.in
@@ -763,6 +763,7 @@ PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
+PAM_LIBS = @PAM_LIBS@
PATH_SEPARATOR = @PATH_SEPARATOR@
PERL = @PERL@
POSIX_SHELL = @POSIX_SHELL@
--- man/Makefile.in
+++ man/Makefile.in
@@ -703,6 +703,7 @@ PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
+PAM_LIBS = @PAM_LIBS@
PATH_SEPARATOR = @PATH_SEPARATOR@
PERL = @PERL@
POSIX_SHELL = @POSIX_SHELL@
--- src/Makefile.am
+++ src/Makefile.am
@@ -147,7 +147,8 @@ tail_LDADD = $(nanosec_libs)
# If necessary, add -lm to resolve use of pow in lib/strtod.c.
uptime_LDADD = $(LDADD) $(POW_LIB) $(GETLOADAVG_LIBS)
-su_LDADD = $(LDADD) $(LIB_CRYPT)
+su_SOURCES = su.c getdef.c
+su_LDADD = $(LDADD) $(LIB_CRYPT) $(PAM_LIBS)
dir_LDADD += $(LIB_ACL)
ls_LDADD += $(LIB_ACL)
--- src/Makefile.in
+++ src/Makefile.in
@@ -605,9 +605,10 @@ stty_OBJECTS = stty.$(OBJEXT)
stty_LDADD = $(LDADD)
stty_DEPENDENCIES = libver.a ../lib/libcoreutils.a \
$(am__DEPENDENCIES_1) ../lib/libcoreutils.a
-su_SOURCES = su.c
-su_OBJECTS = su.$(OBJEXT)
-su_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1)
+am_su_OBJECTS = su.$(OBJEXT) getdef.$(OBJEXT)
+su_OBJECTS = $(am_su_OBJECTS)
+su_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
sum_SOURCES = sum.c
sum_OBJECTS = sum.$(OBJEXT)
sum_LDADD = $(LDADD)
@@ -735,11 +736,11 @@ SOURCES = $(nodist_libver_a_SOURCES) $(_
$(rm_SOURCES) $(rmdir_SOURCES) runcon.c seq.c setuidgid.c \
$(sha1sum_SOURCES) $(sha224sum_SOURCES) $(sha256sum_SOURCES) \
$(sha384sum_SOURCES) $(sha512sum_SOURCES) shred.c shuf.c \
- sleep.c sort.c split.c stat.c stty.c su.c sum.c sync.c tac.c \
- tail.c tee.c test.c $(timeout_SOURCES) touch.c tr.c true.c \
- truncate.c tsort.c tty.c $(uname_SOURCES) unexpand.c uniq.c \
- unlink.c uptime.c users.c $(vdir_SOURCES) wc.c who.c whoami.c \
- yes.c
+ sleep.c sort.c split.c stat.c stty.c $(su_SOURCES) sum.c \
+ sync.c tac.c tail.c tee.c test.c $(timeout_SOURCES) touch.c \
+ tr.c true.c truncate.c tsort.c tty.c $(uname_SOURCES) \
+ unexpand.c uniq.c unlink.c uptime.c users.c $(vdir_SOURCES) \
+ wc.c who.c whoami.c yes.c
DIST_SOURCES = $(__SOURCES) $(arch_SOURCES) base64.c basename.c cat.c \
chcon.c $(chgrp_SOURCES) chmod.c $(chown_SOURCES) chroot.c \
cksum.c comm.c $(cp_SOURCES) csplit.c cut.c date.c dd.c df.c \
@@ -754,10 +755,10 @@ DIST_SOURCES = $(__SOURCES) $(arch_SOURC
$(rmdir_SOURCES) runcon.c seq.c setuidgid.c $(sha1sum_SOURCES) \
$(sha224sum_SOURCES) $(sha256sum_SOURCES) $(sha384sum_SOURCES) \
$(sha512sum_SOURCES) shred.c shuf.c sleep.c sort.c split.c \
- stat.c stty.c su.c sum.c sync.c tac.c tail.c tee.c test.c \
- $(timeout_SOURCES) touch.c tr.c true.c truncate.c tsort.c \
- tty.c $(uname_SOURCES) unexpand.c uniq.c unlink.c uptime.c \
- users.c $(vdir_SOURCES) wc.c who.c whoami.c yes.c
+ stat.c stty.c $(su_SOURCES) sum.c sync.c tac.c tail.c tee.c \
+ test.c $(timeout_SOURCES) touch.c tr.c true.c truncate.c \
+ tsort.c tty.c $(uname_SOURCES) unexpand.c uniq.c unlink.c \
+ uptime.c users.c $(vdir_SOURCES) wc.c who.c whoami.c yes.c
HEADERS = $(noinst_HEADERS)
ETAGS = etags
CTAGS = ctags
@@ -1209,6 +1210,7 @@ PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
+PAM_LIBS = @PAM_LIBS@
PATH_SEPARATOR = @PATH_SEPARATOR@
PERL = @PERL@
POSIX_SHELL = @POSIX_SHELL@
@@ -1511,7 +1513,8 @@ tail_LDADD = $(nanosec_libs)
# If necessary, add -lm to resolve use of pow in lib/strtod.c.
uptime_LDADD = $(LDADD) $(POW_LIB) $(GETLOADAVG_LIBS)
-su_LDADD = $(LDADD) $(LIB_CRYPT)
+su_SOURCES = su.c getdef.c
+su_LDADD = $(LDADD) $(LIB_CRYPT) $(PAM_LIBS)
stat_LDADD = $(LDADD) $(LIB_SELINUX)
# programs that use getaddrinfo (e.g., via canon_host)
@@ -2040,6 +2043,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/false.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fmt.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fold.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getdef.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getlimits.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ginstall-copy.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ginstall-cp-hash.Po@am__quote@
--- src/getdef.c
+++ src/getdef.c
@@ -0,0 +1,259 @@
+/* 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 ((unsigned char) *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 ((unsigned char) *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;
+
+ errno = 0;
+ 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;
+
+ errno = 0;
+ 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
@@ -37,6 +37,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.d/su
+
+#endif
+
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.
@@ -52,6 +62,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
@@ -65,6 +82,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
@@ -98,19 +118,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"
@@ -118,13 +132,22 @@
/* The user to become if none is specified. */
#define DEFAULT_USER "root"
+#ifndef USE_PAM
char *crypt (char const *key, char const *salt);
+#endif
char *getusershell (void);
void endusershell (void);
void setusershell (void);
extern char **environ;
+#ifdef USE_PAM
+static bool _pam_session_opened;
+static bool _pam_cred_established;
+static void export_pamenv (void);
+static void create_watching_parent (void);
+#endif
+
static void run_shell (char const *, char const *, char **, size_t)
ATTRIBUTE_NORETURN;
@@ -212,7 +235,162 @@ log_su (struct passwd const *pw, bool su
}
#endif
+#ifdef USE_PAM
+#define PAM_SERVICE_NAME PROGRAM_NAME
+#define PAM_SERVICE_NAME_L PROGRAM_NAME "-l"
+static bool caught_signal = false;
+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, retval); \
+ a; \
+ }
+
+static void
+cleanup_pam (int retcode)
+{
+ if (_pam_session_opened)
+ pam_close_session (pamh, 0);
+
+ if (_pam_cred_established)
+ pam_setcred (pamh, PAM_DELETE_CRED | PAM_SILENT);
+
+ pam_end(pamh, retcode);
+}
+
+/* Signal handler for parent process. */
+static void
+su_catch_sig (int sig)
+{
+ caught_signal = true;
+}
+
+/* Export env variables declared by PAM modules. */
+static void
+export_pamenv (void)
+{
+ char **env;
+
+ /* This is a copy but don't care to free as we exec later anyways. */
+ env = pam_getenvlist (pamh);
+ while (env && *env)
+ {
+ if (putenv (*env) != 0)
+ xalloc_die ();
+ env++;
+ }
+}
+
+static void
+create_watching_parent (void)
+{
+ pid_t child;
+ sigset_t ourset;
+ int status;
+
+ retval = pam_open_session (pamh, 0);
+ if (retval != PAM_SUCCESS)
+ {
+ cleanup_pam (retval);
+ error (EXIT_FAILURE, 0, _("cannot not open session: %s"),
+ pam_strerror (pamh, retval));
+ }
+ else
+ _pam_session_opened = 1;
+
+ child = fork ();
+ if (child == (pid_t) -1)
+ {
+ cleanup_pam (PAM_ABORT);
+ error (EXIT_FAILURE, errno, _("cannot create child process"));
+ }
+
+ /* the child proceeds to run the shell */
+ if (child == 0)
+ return;
+
+ /* In the parent watch the child. */
+
+ /* su without pam support does not have a helper that keeps
+ sitting on any directory so let's go to /. */
+ if (chdir ("/") != 0)
+ error (0, errno, _("warning: cannot change directory to %s"), "/");
+
+ sigfillset (&ourset);
+ if (sigprocmask (SIG_BLOCK, &ourset, NULL))
+ {
+ error (0, errno, _("cannot block signals"));
+ caught_signal = true;
+ }
+ if (!caught_signal)
+ {
+ 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_signal = true;
+ }
+ }
+ if (!caught_signal)
+ {
+ 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_signal)
+ {
+ fprintf (stderr, _("\nSession terminated, killing shell..."));
+ kill (child, SIGTERM);
+ }
+
+ cleanup_pam (PAM_SUCCESS);
+
+ if (caught_signal)
+ {
+ sleep (2);
+ kill (child, SIGKILL);
+ fprintf (stderr, _(" ...killed.\n"));
+ }
+ exit (status);
+}
+#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. */
@@ -220,10 +398,52 @@ log_su (struct passwd const *pw, bool su
static bool
correct_password (const struct passwd *pw)
{
+#ifdef USE_PAM
+ const struct passwd *lpw;
+ const char *cp;
+
+ retval = pam_start (simulate_login ? PAM_SERVICE_NAME_L : PAM_SERVICE_NAME,
+ 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);
+ }
+#if 0 /* Manpage discourages use of getlogin. */
+ cp = getlogin ();
+ if (!(cp && *cp && (lpw = getpwnam (cp)) != NULL && lpw->pw_uid == getuid ()))
+#endif
+ lpw = getpwuid (getuid ());
+ if (lpw && lpw->pw_name)
+ {
+ 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)
@@ -244,6 +464,7 @@ correct_password (const struct passwd *p
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
@@ -268,8 +489,8 @@ modify_environment (const struct passwd
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
{
@@ -279,6 +500,12 @@ modify_environment (const struct passwd
{
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);
@@ -286,19 +513,41 @@ modify_environment (const struct passwd
}
}
}
+
+#ifdef USE_PAM
+ export_pamenv ();
+#endif
}
/* Become the user and group(s) specified by PW. */
static void
-change_identity (const struct passwd *pw)
+init_groups (const struct passwd *pw)
{
#ifdef HAVE_INITGROUPS
errno = 0;
if (initgroups (pw->pw_name, pw->pw_gid) == -1)
- error (EXIT_FAILURE, errno, _("cannot set groups"));
+ {
+#ifdef USE_PAM
+ cleanup_pam (PAM_ABORT);
+#endif
+ error (EXIT_FAILURE, errno, _("cannot set groups"));
+ }
endgrent ();
#endif
+
+#ifdef USE_PAM
+ retval = pam_setcred (pamh, PAM_ESTABLISH_CRED);
+ if (retval != PAM_SUCCESS)
+ error (EXIT_FAILURE, 0, "%s", pam_strerror (pamh, retval));
+ else
+ _pam_cred_established = 1;
+#endif
+}
+
+static void
+change_identity (const struct passwd *pw)
+{
if (setgid (pw->pw_gid))
error (EXIT_FAILURE, errno, _("cannot set group id"));
if (setuid (pw->pw_uid))
@@ -491,6 +740,7 @@ main (int argc, char **argv)
#ifdef SYSLOG_FAILURE
log_su (pw, false);
#endif
+ sleep (getdef_num ("FAIL_DELAY", 1));
error (EXIT_FAILURE, 0, _("incorrect password"));
}
#ifdef SYSLOG_SUCCESS
@@ -512,9 +762,21 @@ main (int argc, char **argv)
shell = NULL;
}
shell = xstrdup (shell ? shell : pw->pw_shell);
- modify_environment (pw, shell);
+
+ init_groups (pw);
+
+#ifdef USE_PAM
+ create_watching_parent ();
+ /* Now we're in the child. */
+#endif
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);
--- tests/Makefile.in
+++ tests/Makefile.in
@@ -677,6 +677,7 @@ PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
+PAM_LIBS = @PAM_LIBS@
PATH_SEPARATOR = @PATH_SEPARATOR@
PERL = @PERL@
POSIX_SHELL = @POSIX_SHELL@