1024 lines
27 KiB
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@
|