summaryrefslogtreecommitdiffstats
path: root/libpam
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 14:22:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 14:22:51 +0000
commit9ada0093e92388590c7368600ca4e9e3e376f0d0 (patch)
treea56fe41110023676d7082028cbaa47ca4b6e6164 /libpam
parentInitial commit. (diff)
downloadpam-9ada0093e92388590c7368600ca4e9e3e376f0d0.tar.xz
pam-9ada0093e92388590c7368600ca4e9e3e376f0d0.zip
Adding upstream version 1.5.2.upstream/1.5.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--libpam/Makefile.am46
-rw-r--r--libpam/Makefile.in953
-rw-r--r--libpam/include/pam_cc_compat.h66
-rw-r--r--libpam/include/pam_inline.h118
-rw-r--r--libpam/include/security/_pam_compat.h126
-rw-r--r--libpam/include/security/_pam_macros.h196
-rw-r--r--libpam/include/security/_pam_types.h333
-rw-r--r--libpam/include/security/pam_appl.h104
-rw-r--r--libpam/include/security/pam_ext.h91
-rw-r--r--libpam/include/security/pam_modules.h124
-rw-r--r--libpam/include/security/pam_modutil.h160
-rw-r--r--libpam/include/test_assert.h55
-rw-r--r--libpam/libpam.map89
-rw-r--r--libpam/pam.pc.in9
-rw-r--r--libpam/pam_account.c23
-rw-r--r--libpam/pam_audit.c242
-rw-r--r--libpam/pam_auth.c73
-rw-r--r--libpam/pam_data.c166
-rw-r--r--libpam/pam_delay.c160
-rw-r--r--libpam/pam_dispatch.c447
-rw-r--r--libpam/pam_dynamic.c138
-rw-r--r--libpam/pam_end.c98
-rw-r--r--libpam/pam_env.c392
-rw-r--r--libpam/pam_get_authtok.c280
-rw-r--r--libpam/pam_handlers.c1045
-rw-r--r--libpam/pam_item.c396
-rw-r--r--libpam/pam_misc.c360
-rw-r--r--libpam/pam_modutil_check_user.c92
-rw-r--r--libpam/pam_modutil_cleanup.c19
-rw-r--r--libpam/pam_modutil_getgrgid.c138
-rw-r--r--libpam/pam_modutil_getgrnam.c127
-rw-r--r--libpam/pam_modutil_getlogin.c80
-rw-r--r--libpam/pam_modutil_getpwnam.c127
-rw-r--r--libpam/pam_modutil_getpwuid.c138
-rw-r--r--libpam/pam_modutil_getspnam.c127
-rw-r--r--libpam/pam_modutil_ingroup.c130
-rw-r--r--libpam/pam_modutil_ioloop.c53
-rw-r--r--libpam/pam_modutil_priv.c179
-rw-r--r--libpam/pam_modutil_private.h24
-rw-r--r--libpam/pam_modutil_sanitize.c147
-rw-r--r--libpam/pam_modutil_searchkey.c128
-rw-r--r--libpam/pam_password.c61
-rw-r--r--libpam/pam_prelude.c454
-rw-r--r--libpam/pam_prelude.h15
-rw-r--r--libpam/pam_private.h358
-rw-r--r--libpam/pam_session.c45
-rw-r--r--libpam/pam_start.c179
-rw-r--r--libpam/pam_strerror.c106
-rw-r--r--libpam/pam_syslog.c115
-rw-r--r--libpam/pam_tokens.h112
-rw-r--r--libpam/pam_vprompt.c115
-rw-r--r--libpam_misc/Makefile.am26
-rw-r--r--libpam_misc/Makefile.in806
-rw-r--r--libpam_misc/help_env.c88
-rw-r--r--libpam_misc/include/security/pam_misc.h52
-rw-r--r--libpam_misc/libpam_misc.map17
-rw-r--r--libpam_misc/misc_conv.c401
-rw-r--r--libpam_misc/pam_misc.pc.in9
-rw-r--r--libpamc/License41
-rw-r--r--libpamc/Makefile.am28
-rw-r--r--libpamc/Makefile.in924
-rw-r--r--libpamc/include/security/pam_client.h197
-rw-r--r--libpamc/libpamc.h66
-rw-r--r--libpamc/libpamc.map12
-rw-r--r--libpamc/pamc.pc.in9
-rw-r--r--libpamc/pamc_client.c189
-rw-r--r--libpamc/pamc_converse.c211
-rw-r--r--libpamc/pamc_load.c477
-rw-r--r--libpamc/test/Makefile.am11
-rw-r--r--libpamc/test/Makefile.in526
-rwxr-xr-xlibpamc/test/agents/secret@here307
-rw-r--r--libpamc/test/modules/Makefile9
-rw-r--r--libpamc/test/modules/pam_secret.c669
-rw-r--r--libpamc/test/regress/Makefile7
-rwxr-xr-xlibpamc/test/regress/run_test.sh6
-rw-r--r--libpamc/test/regress/test.libpamc.c343
-rwxr-xr-xlibpamc/test/regress/test.secret@here151
77 files changed, 14941 insertions, 0 deletions
diff --git a/libpam/Makefile.am b/libpam/Makefile.am
new file mode 100644
index 0000000..55222af
--- /dev/null
+++ b/libpam/Makefile.am
@@ -0,0 +1,46 @@
+#
+# Copyright (c) 2005, 2006, 2007, 2009 Thorsten Kukuk <kukuk@thkukuk.de>
+#
+
+AM_CFLAGS = -DDEFAULT_MODULE_PATH=\"$(SECUREDIR)/\" -DLIBPAM_COMPILE \
+ -I$(srcdir)/include $(LIBPRELUDE_CFLAGS) $(ECONF_CFLAGS) \
+ -DPAM_VERSION=\"$(VERSION)\" -DSYSCONFDIR=\"$(sysconfdir)\" \
+ $(WARN_CFLAGS)
+
+CLEANFILES = *~
+
+EXTRA_DIST = libpam.map
+
+include_HEADERS = include/security/_pam_compat.h \
+ include/security/_pam_macros.h include/security/_pam_types.h \
+ include/security/pam_appl.h include/security/pam_modules.h \
+ include/security/pam_ext.h include/security/pam_modutil.h
+
+noinst_HEADERS = pam_prelude.h pam_private.h pam_tokens.h \
+ pam_modutil_private.h include/pam_cc_compat.h \
+ include/pam_inline.h include/test_assert.h
+
+libpam_la_LDFLAGS = -no-undefined -version-info 85:1:85
+libpam_la_LIBADD = @LIBAUDIT@ $(LIBPRELUDE_LIBS) $(ECONF_LIBS) @LIBDL@
+
+if HAVE_VERSIONING
+ libpam_la_LDFLAGS += -Wl,--version-script=$(srcdir)/libpam.map
+endif
+
+lib_LTLIBRARIES = libpam.la
+
+libpam_la_SOURCES = pam_account.c pam_auth.c pam_data.c pam_delay.c \
+ pam_dispatch.c pam_end.c pam_env.c pam_get_authtok.c \
+ pam_handlers.c pam_item.c \
+ pam_misc.c pam_password.c pam_prelude.c \
+ pam_session.c pam_start.c pam_strerror.c \
+ pam_vprompt.c pam_syslog.c pam_dynamic.c pam_audit.c \
+ pam_modutil_check_user.c \
+ pam_modutil_cleanup.c pam_modutil_getpwnam.c pam_modutil_ioloop.c \
+ pam_modutil_getgrgid.c pam_modutil_getpwuid.c pam_modutil_getgrnam.c \
+ pam_modutil_getspnam.c pam_modutil_getlogin.c pam_modutil_ingroup.c \
+ pam_modutil_priv.c pam_modutil_sanitize.c pam_modutil_searchkey.c
+
+# Pkg-config script.
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = pam.pc
diff --git a/libpam/Makefile.in b/libpam/Makefile.in
new file mode 100644
index 0000000..0c2333c
--- /dev/null
+++ b/libpam/Makefile.in
@@ -0,0 +1,953 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+#
+# Copyright (c) 2005, 2006, 2007, 2009 Thorsten Kukuk <kukuk@thkukuk.de>
+#
+
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@HAVE_VERSIONING_TRUE@am__append_1 = -Wl,--version-script=$(srcdir)/libpam.map
+subdir = libpam
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/attribute.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/intlmacosx.m4 \
+ $(top_srcdir)/m4/jh_path_xml_catalog.m4 \
+ $(top_srcdir)/m4/ld-O1.m4 $(top_srcdir)/m4/ld-as-needed.m4 \
+ $(top_srcdir)/m4/ld-no-undefined.m4 \
+ $(top_srcdir)/m4/ld-z-now.m4 $(top_srcdir)/m4/lib-ld.m4 \
+ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+ $(top_srcdir)/m4/libprelude.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 \
+ $(top_srcdir)/m4/warn_lang_flags.m4 \
+ $(top_srcdir)/m4/warnings.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(include_HEADERS) \
+ $(noinst_HEADERS) $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES = pam.pc
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" \
+ "$(DESTDIR)$(includedir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libpam_la_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_libpam_la_OBJECTS = pam_account.lo pam_auth.lo pam_data.lo \
+ pam_delay.lo pam_dispatch.lo pam_end.lo pam_env.lo \
+ pam_get_authtok.lo pam_handlers.lo pam_item.lo pam_misc.lo \
+ pam_password.lo pam_prelude.lo pam_session.lo pam_start.lo \
+ pam_strerror.lo pam_vprompt.lo pam_syslog.lo pam_dynamic.lo \
+ pam_audit.lo pam_modutil_check_user.lo pam_modutil_cleanup.lo \
+ pam_modutil_getpwnam.lo pam_modutil_ioloop.lo \
+ pam_modutil_getgrgid.lo pam_modutil_getpwuid.lo \
+ pam_modutil_getgrnam.lo pam_modutil_getspnam.lo \
+ pam_modutil_getlogin.lo pam_modutil_ingroup.lo \
+ pam_modutil_priv.lo pam_modutil_sanitize.lo \
+ pam_modutil_searchkey.lo
+libpam_la_OBJECTS = $(am_libpam_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libpam_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libpam_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/pam_account.Plo \
+ ./$(DEPDIR)/pam_audit.Plo ./$(DEPDIR)/pam_auth.Plo \
+ ./$(DEPDIR)/pam_data.Plo ./$(DEPDIR)/pam_delay.Plo \
+ ./$(DEPDIR)/pam_dispatch.Plo ./$(DEPDIR)/pam_dynamic.Plo \
+ ./$(DEPDIR)/pam_end.Plo ./$(DEPDIR)/pam_env.Plo \
+ ./$(DEPDIR)/pam_get_authtok.Plo ./$(DEPDIR)/pam_handlers.Plo \
+ ./$(DEPDIR)/pam_item.Plo ./$(DEPDIR)/pam_misc.Plo \
+ ./$(DEPDIR)/pam_modutil_check_user.Plo \
+ ./$(DEPDIR)/pam_modutil_cleanup.Plo \
+ ./$(DEPDIR)/pam_modutil_getgrgid.Plo \
+ ./$(DEPDIR)/pam_modutil_getgrnam.Plo \
+ ./$(DEPDIR)/pam_modutil_getlogin.Plo \
+ ./$(DEPDIR)/pam_modutil_getpwnam.Plo \
+ ./$(DEPDIR)/pam_modutil_getpwuid.Plo \
+ ./$(DEPDIR)/pam_modutil_getspnam.Plo \
+ ./$(DEPDIR)/pam_modutil_ingroup.Plo \
+ ./$(DEPDIR)/pam_modutil_ioloop.Plo \
+ ./$(DEPDIR)/pam_modutil_priv.Plo \
+ ./$(DEPDIR)/pam_modutil_sanitize.Plo \
+ ./$(DEPDIR)/pam_modutil_searchkey.Plo \
+ ./$(DEPDIR)/pam_password.Plo ./$(DEPDIR)/pam_prelude.Plo \
+ ./$(DEPDIR)/pam_session.Plo ./$(DEPDIR)/pam_start.Plo \
+ ./$(DEPDIR)/pam_strerror.Plo ./$(DEPDIR)/pam_syslog.Plo \
+ ./$(DEPDIR)/pam_vprompt.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libpam_la_SOURCES)
+DIST_SOURCES = $(libpam_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+DATA = $(pkgconfig_DATA)
+HEADERS = $(include_HEADERS) $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/pam.pc.in \
+ $(top_srcdir)/build-aux/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BROWSER = @BROWSER@
+BUILD_CFLAGS = @BUILD_CFLAGS@
+BUILD_CPPFLAGS = @BUILD_CPPFLAGS@
+BUILD_LDFLAGS = @BUILD_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRYPTO_LIBS = @CRYPTO_LIBS@
+CRYPT_CFLAGS = @CRYPT_CFLAGS@
+CRYPT_LIBS = @CRYPT_LIBS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+ECONF_CFLAGS = @ECONF_CFLAGS@
+ECONF_LIBS = @ECONF_LIBS@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXE_CFLAGS = @EXE_CFLAGS@
+EXE_LDFLAGS = @EXE_LDFLAGS@
+FGREP = @FGREP@
+FO2PDF = @FO2PDF@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBAUDIT = @LIBAUDIT@
+LIBCRYPT = @LIBCRYPT@
+LIBDB = @LIBDB@
+LIBDL = @LIBDL@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBPRELUDE_CFLAGS = @LIBPRELUDE_CFLAGS@
+LIBPRELUDE_CONFIG = @LIBPRELUDE_CONFIG@
+LIBPRELUDE_CONFIG_PREFIX = @LIBPRELUDE_CONFIG_PREFIX@
+LIBPRELUDE_LDFLAGS = @LIBPRELUDE_LDFLAGS@
+LIBPRELUDE_LIBS = @LIBPRELUDE_LIBS@
+LIBPRELUDE_PREFIX = @LIBPRELUDE_PREFIX@
+LIBPRELUDE_PTHREAD_CFLAGS = @LIBPRELUDE_PTHREAD_CFLAGS@
+LIBS = @LIBS@
+LIBSELINUX = @LIBSELINUX@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NIS_CFLAGS = @NIS_CFLAGS@
+NIS_LIBS = @NIS_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NSL_CFLAGS = @NSL_CFLAGS@
+NSL_LIBS = @NSL_LIBS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POSUB = @POSUB@
+RANLIB = @RANLIB@
+SCONFIGDIR = @SCONFIGDIR@
+SECUREDIR = @SECUREDIR@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRINGPARAM_HMAC = @STRINGPARAM_HMAC@
+STRINGPARAM_VENDORDIR = @STRINGPARAM_VENDORDIR@
+STRIP = @STRIP@
+TIRPC_CFLAGS = @TIRPC_CFLAGS@
+TIRPC_LIBS = @TIRPC_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WARN_CFLAGS = @WARN_CFLAGS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+XMLCATALOG = @XMLCATALOG@
+XMLLINT = @XMLLINT@
+XML_CATALOG_FILE = @XML_CATALOG_FILE@
+XSLTPROC = @XSLTPROC@
+YACC = @YACC@
+YFLAGS = @YFLAGS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pam_xauth_path = @pam_xauth_path@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+systemdunitdir = @systemdunitdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CFLAGS = -DDEFAULT_MODULE_PATH=\"$(SECUREDIR)/\" -DLIBPAM_COMPILE \
+ -I$(srcdir)/include $(LIBPRELUDE_CFLAGS) $(ECONF_CFLAGS) \
+ -DPAM_VERSION=\"$(VERSION)\" -DSYSCONFDIR=\"$(sysconfdir)\" \
+ $(WARN_CFLAGS)
+
+CLEANFILES = *~
+EXTRA_DIST = libpam.map
+include_HEADERS = include/security/_pam_compat.h \
+ include/security/_pam_macros.h include/security/_pam_types.h \
+ include/security/pam_appl.h include/security/pam_modules.h \
+ include/security/pam_ext.h include/security/pam_modutil.h
+
+noinst_HEADERS = pam_prelude.h pam_private.h pam_tokens.h \
+ pam_modutil_private.h include/pam_cc_compat.h \
+ include/pam_inline.h include/test_assert.h
+
+libpam_la_LDFLAGS = -no-undefined -version-info 85:1:85 \
+ $(am__append_1)
+libpam_la_LIBADD = @LIBAUDIT@ $(LIBPRELUDE_LIBS) $(ECONF_LIBS) @LIBDL@
+lib_LTLIBRARIES = libpam.la
+libpam_la_SOURCES = pam_account.c pam_auth.c pam_data.c pam_delay.c \
+ pam_dispatch.c pam_end.c pam_env.c pam_get_authtok.c \
+ pam_handlers.c pam_item.c \
+ pam_misc.c pam_password.c pam_prelude.c \
+ pam_session.c pam_start.c pam_strerror.c \
+ pam_vprompt.c pam_syslog.c pam_dynamic.c pam_audit.c \
+ pam_modutil_check_user.c \
+ pam_modutil_cleanup.c pam_modutil_getpwnam.c pam_modutil_ioloop.c \
+ pam_modutil_getgrgid.c pam_modutil_getpwuid.c pam_modutil_getgrnam.c \
+ pam_modutil_getspnam.c pam_modutil_getlogin.c pam_modutil_ingroup.c \
+ pam_modutil_priv.c pam_modutil_sanitize.c pam_modutil_searchkey.c
+
+
+# Pkg-config script.
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = pam.pc
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libpam/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu libpam/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+pam.pc: $(top_builddir)/config.status $(srcdir)/pam.pc.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libpam.la: $(libpam_la_OBJECTS) $(libpam_la_DEPENDENCIES) $(EXTRA_libpam_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libpam_la_LINK) -rpath $(libdir) $(libpam_la_OBJECTS) $(libpam_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_account.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_audit.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_auth.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_data.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_delay.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_dispatch.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_dynamic.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_end.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_env.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_get_authtok.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_handlers.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_item.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_misc.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_modutil_check_user.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_modutil_cleanup.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_modutil_getgrgid.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_modutil_getgrnam.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_modutil_getlogin.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_modutil_getpwnam.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_modutil_getpwuid.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_modutil_getspnam.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_modutil_ingroup.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_modutil_ioloop.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_modutil_priv.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_modutil_sanitize.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_modutil_searchkey.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_password.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_prelude.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_session.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_start.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_strerror.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_syslog.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_vprompt.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-pkgconfigDATA: $(pkgconfig_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \
+ done
+
+uninstall-pkgconfigDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir)
+install-includeHEADERS: $(include_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \
+ done
+
+uninstall-includeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(includedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/pam_account.Plo
+ -rm -f ./$(DEPDIR)/pam_audit.Plo
+ -rm -f ./$(DEPDIR)/pam_auth.Plo
+ -rm -f ./$(DEPDIR)/pam_data.Plo
+ -rm -f ./$(DEPDIR)/pam_delay.Plo
+ -rm -f ./$(DEPDIR)/pam_dispatch.Plo
+ -rm -f ./$(DEPDIR)/pam_dynamic.Plo
+ -rm -f ./$(DEPDIR)/pam_end.Plo
+ -rm -f ./$(DEPDIR)/pam_env.Plo
+ -rm -f ./$(DEPDIR)/pam_get_authtok.Plo
+ -rm -f ./$(DEPDIR)/pam_handlers.Plo
+ -rm -f ./$(DEPDIR)/pam_item.Plo
+ -rm -f ./$(DEPDIR)/pam_misc.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_check_user.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_cleanup.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_getgrgid.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_getgrnam.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_getlogin.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_getpwnam.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_getpwuid.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_getspnam.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_ingroup.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_ioloop.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_priv.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_sanitize.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_searchkey.Plo
+ -rm -f ./$(DEPDIR)/pam_password.Plo
+ -rm -f ./$(DEPDIR)/pam_prelude.Plo
+ -rm -f ./$(DEPDIR)/pam_session.Plo
+ -rm -f ./$(DEPDIR)/pam_start.Plo
+ -rm -f ./$(DEPDIR)/pam_strerror.Plo
+ -rm -f ./$(DEPDIR)/pam_syslog.Plo
+ -rm -f ./$(DEPDIR)/pam_vprompt.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-includeHEADERS install-pkgconfigDATA
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/pam_account.Plo
+ -rm -f ./$(DEPDIR)/pam_audit.Plo
+ -rm -f ./$(DEPDIR)/pam_auth.Plo
+ -rm -f ./$(DEPDIR)/pam_data.Plo
+ -rm -f ./$(DEPDIR)/pam_delay.Plo
+ -rm -f ./$(DEPDIR)/pam_dispatch.Plo
+ -rm -f ./$(DEPDIR)/pam_dynamic.Plo
+ -rm -f ./$(DEPDIR)/pam_end.Plo
+ -rm -f ./$(DEPDIR)/pam_env.Plo
+ -rm -f ./$(DEPDIR)/pam_get_authtok.Plo
+ -rm -f ./$(DEPDIR)/pam_handlers.Plo
+ -rm -f ./$(DEPDIR)/pam_item.Plo
+ -rm -f ./$(DEPDIR)/pam_misc.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_check_user.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_cleanup.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_getgrgid.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_getgrnam.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_getlogin.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_getpwnam.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_getpwuid.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_getspnam.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_ingroup.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_ioloop.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_priv.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_sanitize.Plo
+ -rm -f ./$(DEPDIR)/pam_modutil_searchkey.Plo
+ -rm -f ./$(DEPDIR)/pam_password.Plo
+ -rm -f ./$(DEPDIR)/pam_prelude.Plo
+ -rm -f ./$(DEPDIR)/pam_session.Plo
+ -rm -f ./$(DEPDIR)/pam_start.Plo
+ -rm -f ./$(DEPDIR)/pam_strerror.Plo
+ -rm -f ./$(DEPDIR)/pam_syslog.Plo
+ -rm -f ./$(DEPDIR)/pam_vprompt.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES \
+ uninstall-pkgconfigDATA
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libLTLIBRARIES clean-libtool cscopelist-am \
+ ctags ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am \
+ install-includeHEADERS install-info install-info-am \
+ install-libLTLIBRARIES install-man install-pdf install-pdf-am \
+ install-pkgconfigDATA install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am uninstall-includeHEADERS \
+ uninstall-libLTLIBRARIES uninstall-pkgconfigDATA
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libpam/include/pam_cc_compat.h b/libpam/include/pam_cc_compat.h
new file mode 100644
index 0000000..6919036
--- /dev/null
+++ b/libpam/include/pam_cc_compat.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2020 Dmitry V. Levin <ldv@altlinux.org>
+ */
+
+#ifndef PAM_CC_COMPAT_H
+#define PAM_CC_COMPAT_H
+
+#include "config.h"
+#include <security/_pam_types.h>
+
+#if defined __clang__ && defined __clang_major__ && defined __clang_minor__
+# define PAM_CLANG_PREREQ(maj, min) \
+ ((__clang_major__ << 16) + __clang_minor__ >= ((maj) << 16) + (min))
+#else
+# define PAM_CLANG_PREREQ(maj, min) 0
+#endif
+
+#if PAM_GNUC_PREREQ(2, 7)
+# define PAM_ATTRIBUTE_ALIGNED(arg) __attribute__((__aligned__(arg)))
+#else
+# define PAM_ATTRIBUTE_ALIGNED(arg) /* empty */
+#endif
+
+#if PAM_GNUC_PREREQ(4, 6)
+# define DIAG_PUSH_IGNORE_CAST_QUAL \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wcast-qual\"")
+# define DIAG_POP_IGNORE_CAST_QUAL \
+ _Pragma("GCC diagnostic pop")
+# define DIAG_PUSH_IGNORE_CAST_ALIGN \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wcast-align\"")
+# define DIAG_POP_IGNORE_CAST_ALIGN \
+ _Pragma("GCC diagnostic pop")
+#elif PAM_CLANG_PREREQ(2, 6)
+# define DIAG_PUSH_IGNORE_CAST_QUAL \
+ _Pragma("clang diagnostic push"); \
+ _Pragma("clang diagnostic ignored \"-Wcast-qual\"")
+# define DIAG_POP_IGNORE_CAST_QUAL \
+ _Pragma("clang diagnostic pop")
+# define DIAG_PUSH_IGNORE_CAST_ALIGN \
+ _Pragma("clang diagnostic push"); \
+ _Pragma("clang diagnostic ignored \"-Wcast-align\"")
+# define DIAG_POP_IGNORE_CAST_ALIGN \
+ _Pragma("clang diagnostic pop")
+#else
+# define DIAG_PUSH_IGNORE_CAST_QUAL /* empty */
+# define DIAG_POP_IGNORE_CAST_QUAL /* empty */
+# define DIAG_PUSH_IGNORE_CAST_ALIGN /* empty */
+# define DIAG_POP_IGNORE_CAST_ALIGN /* empty */
+#endif
+
+/*
+ * Evaluates to
+ * 1, if the given two types are known to be the same
+ * 0, otherwise.
+ */
+#if PAM_GNUC_PREREQ(3, 0)
+# define PAM_IS_SAME_TYPE(x_, y_) \
+ __builtin_types_compatible_p(__typeof__(x_), __typeof__(y_))
+#else
+/* Cannot tell whether these types are the same. */
+# define PAM_IS_SAME_TYPE(x_, y_) 0
+#endif
+
+#endif /* PAM_CC_COMPAT_H */
diff --git a/libpam/include/pam_inline.h b/libpam/include/pam_inline.h
new file mode 100644
index 0000000..ec2f3bf
--- /dev/null
+++ b/libpam/include/pam_inline.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2020 Dmitry V. Levin <ldv@altlinux.org>
+ *
+ * Handy inline functions and macros providing some convenient functionality
+ * to libpam and its modules.
+ */
+
+#ifndef PAM_INLINE_H
+#define PAM_INLINE_H
+
+#include "pam_cc_compat.h"
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+/*
+ * Evaluates to
+ * - a syntax error if the argument is 0,
+ * 0, otherwise.
+ */
+#define PAM_FAIL_BUILD_ON_ZERO(e_) (sizeof(int[-1 + 2 * !!(e_)]) * 0)
+
+/*
+ * Evaluates to
+ * 1, if the given type is known to be a non-array type
+ * 0, otherwise.
+ */
+#define PAM_IS_NOT_ARRAY(a_) PAM_IS_SAME_TYPE((a_), &(a_)[0])
+
+/*
+ * Evaluates to
+ * - a syntax error if the argument is not an array,
+ * 0, otherwise.
+ */
+#define PAM_MUST_BE_ARRAY(a_) PAM_FAIL_BUILD_ON_ZERO(!PAM_IS_NOT_ARRAY(a_))
+
+/* Evaluates to the number of elements in the specified array. */
+#define PAM_ARRAY_SIZE(a_) (sizeof(a_) / sizeof((a_)[0]) + PAM_MUST_BE_ARRAY(a_))
+
+/*
+ * Returns NULL if STR does not start with PREFIX,
+ * or a pointer to the first char in STR after PREFIX.
+ * The length of PREFIX is specified by PREFIX_LEN.
+ */
+static inline const char *
+pam_str_skip_prefix_len(const char *str, const char *prefix, size_t prefix_len)
+{
+ return strncmp(str, prefix, prefix_len) ? NULL : str + prefix_len;
+}
+
+#define pam_str_skip_prefix(str_, prefix_) \
+ pam_str_skip_prefix_len((str_), (prefix_), sizeof(prefix_) - 1 + PAM_MUST_BE_ARRAY(prefix_))
+
+/*
+ * Returns NULL if STR does not start with PREFIX
+ * (ignoring the case of the characters),
+ * or a pointer to the first char in STR after PREFIX.
+ * The length of PREFIX is specified by PREFIX_LEN.
+ */
+static inline const char *
+pam_str_skip_icase_prefix_len(const char *str, const char *prefix, size_t prefix_len)
+{
+ return strncasecmp(str, prefix, prefix_len) ? NULL : str + prefix_len;
+}
+
+#define pam_str_skip_icase_prefix(str_, prefix_) \
+ pam_str_skip_icase_prefix_len((str_), (prefix_), sizeof(prefix_) - 1 + PAM_MUST_BE_ARRAY(prefix_))
+
+static inline int
+pam_read_passwords(int fd, int npass, char **passwords)
+{
+ /*
+ * The passwords array must contain npass preallocated
+ * buffers of length PAM_MAX_RESP_SIZE + 1.
+ */
+ int rbytes = 0;
+ int offset = 0;
+ int i = 0;
+ char *pptr;
+ while (npass > 0) {
+ rbytes = read(fd, passwords[i]+offset, PAM_MAX_RESP_SIZE+1-offset);
+
+ if (rbytes < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ break;
+ }
+ if (rbytes == 0) {
+ break;
+ }
+
+ while (npass > 0 &&
+ (pptr = memchr(passwords[i] + offset, '\0', rbytes)) != NULL) {
+ ++pptr; /* skip the '\0' */
+ rbytes -= pptr - (passwords[i] + offset);
+ i++;
+ offset = 0;
+ npass--;
+ if (rbytes > 0) {
+ if (npass > 0) {
+ memcpy(passwords[i], pptr, rbytes);
+ }
+ memset(pptr, '\0', rbytes);
+ }
+ }
+ offset += rbytes;
+ }
+
+ /* clear up */
+ if (offset > 0 && npass > 0) {
+ memset(passwords[i], '\0', offset);
+ }
+
+ return i;
+}
+
+#endif /* PAM_INLINE_H */
diff --git a/libpam/include/security/_pam_compat.h b/libpam/include/security/_pam_compat.h
new file mode 100644
index 0000000..a5f58e4
--- /dev/null
+++ b/libpam/include/security/_pam_compat.h
@@ -0,0 +1,126 @@
+#ifndef _PAM_COMPAT_H
+#define _PAM_COMPAT_H
+
+/*
+ * This file was contributed by Derrick J Brashear <shadow@dementia.org>
+ * slight modification by Brad M. Garcia <bgarcia@fore.com>
+ *
+ * A number of operating systems have started to implement PAM.
+ * unfortunately, they have a different set of numeric values for
+ * certain constants. This file is included for compatibility's sake.
+ */
+
+/* Solaris uses different constants. We redefine to those here */
+#if defined(solaris) || (defined(__SVR4) && defined(sun))
+
+# ifdef _SECURITY_PAM_MODULES_H
+
+/* flags for pam_chauthtok() */
+# undef PAM_PRELIM_CHECK
+# define PAM_PRELIM_CHECK 0x1
+
+# undef PAM_UPDATE_AUTHTOK
+# define PAM_UPDATE_AUTHTOK 0x2
+
+# endif /* _SECURITY_PAM_MODULES_H */
+
+# ifdef _SECURITY__PAM_TYPES_H
+
+/* generic for pam_* functions */
+# undef PAM_SILENT
+# define PAM_SILENT 0x80000000
+
+# undef PAM_CHANGE_EXPIRED_AUTHTOK
+# define PAM_CHANGE_EXPIRED_AUTHTOK 0x4
+
+/* flags for pam_setcred() */
+# undef PAM_ESTABLISH_CRED
+# define PAM_ESTABLISH_CRED 0x1
+
+# undef PAM_DELETE_CRED
+# define PAM_DELETE_CRED 0x2
+
+# undef PAM_REINITIALIZE_CRED
+# define PAM_REINITIALIZE_CRED 0x4
+
+# undef PAM_REFRESH_CRED
+# define PAM_REFRESH_CRED 0x8
+
+/* another binary incompatibility comes from the return codes! */
+
+# undef PAM_CONV_ERR
+# define PAM_CONV_ERR 6
+
+# undef PAM_PERM_DENIED
+# define PAM_PERM_DENIED 7
+
+# undef PAM_MAXTRIES
+# define PAM_MAXTRIES 8
+
+# undef PAM_AUTH_ERR
+# define PAM_AUTH_ERR 9
+
+# undef PAM_NEW_AUTHTOK_REQD
+# define PAM_NEW_AUTHTOK_REQD 10
+
+# undef PAM_CRED_INSUFFICIENT
+# define PAM_CRED_INSUFFICIENT 11
+
+# undef PAM_AUTHINFO_UNAVAIL
+# define PAM_AUTHINFO_UNAVAIL 12
+
+# undef PAM_USER_UNKNOWN
+# define PAM_USER_UNKNOWN 13
+
+# undef PAM_CRED_UNAVAIL
+# define PAM_CRED_UNAVAIL 14
+
+# undef PAM_CRED_EXPIRED
+# define PAM_CRED_EXPIRED 15
+
+# undef PAM_CRED_ERR
+# define PAM_CRED_ERR 16
+
+# undef PAM_ACCT_EXPIRED
+# define PAM_ACCT_EXPIRED 17
+
+# undef PAM_AUTHTOK_EXPIRED
+# define PAM_AUTHTOK_EXPIRED 18
+
+# undef PAM_SESSION_ERR
+# define PAM_SESSION_ERR 19
+
+# undef PAM_AUTHTOK_ERR
+# define PAM_AUTHTOK_ERR 20
+
+# undef PAM_AUTHTOK_RECOVERY_ERR
+# define PAM_AUTHTOK_RECOVERY_ERR 21
+
+# undef PAM_AUTHTOK_LOCK_BUSY
+# define PAM_AUTHTOK_LOCK_BUSY 22
+
+# undef PAM_AUTHTOK_DISABLE_AGING
+# define PAM_AUTHTOK_DISABLE_AGING 23
+
+# undef PAM_NO_MODULE_DATA
+# define PAM_NO_MODULE_DATA 24
+
+# undef PAM_IGNORE
+# define PAM_IGNORE 25
+
+# undef PAM_ABORT
+# define PAM_ABORT 26
+
+# undef PAM_TRY_AGAIN
+# define PAM_TRY_AGAIN 27
+
+#endif /* _SECURITY__PAM_TYPES_H */
+
+#else
+
+/* For compatibility with old Linux-PAM implementations. */
+#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR
+
+#endif /* defined(solaris) || (defined(__SVR4) && defined(sun)) */
+
+#endif /* _PAM_COMPAT_H */
diff --git a/libpam/include/security/_pam_macros.h b/libpam/include/security/_pam_macros.h
new file mode 100644
index 0000000..e891e22
--- /dev/null
+++ b/libpam/include/security/_pam_macros.h
@@ -0,0 +1,196 @@
+#ifndef PAM_MACROS_H
+#define PAM_MACROS_H
+
+/*
+ * All kind of macros used by PAM, but usable in some other
+ * programs too.
+ * Organized by Cristian Gafton <gafton@redhat.com>
+ */
+
+/* a 'safe' version of strdup */
+
+#include <stdlib.h>
+#include <string.h>
+
+#define x_strdup(s) ( (s) ? strdup(s):NULL )
+
+/* Good policy to strike out passwords with some characters not just
+ free the memory */
+
+#define _pam_overwrite(x) \
+do { \
+ register char *__xx__; \
+ if ((__xx__=(x))) \
+ while (*__xx__) \
+ *__xx__++ = '\0'; \
+} while (0)
+
+#define _pam_overwrite_n(x,n) \
+do { \
+ register char *__xx__; \
+ register unsigned int __i__ = 0; \
+ if ((__xx__=(x))) \
+ for (;__i__<n; __i__++) \
+ __xx__[__i__] = 0; \
+} while (0)
+
+/*
+ * Don't just free it, forget it too.
+ */
+
+#define _pam_drop(X) \
+do { \
+ if (X) { \
+ free(X); \
+ X=NULL; \
+ } \
+} while (0)
+
+#define _pam_drop_reply(/* struct pam_response * */ reply, /* int */ replies) \
+do { \
+ int reply_i; \
+ \
+ for (reply_i=0; reply_i<replies; ++reply_i) { \
+ if (reply[reply_i].resp) { \
+ _pam_overwrite(reply[reply_i].resp); \
+ free(reply[reply_i].resp); \
+ } \
+ } \
+ if (reply) \
+ free(reply); \
+} while (0)
+
+/* some debugging code */
+
+#ifdef PAM_DEBUG
+
+/*
+ * This provides the necessary function to do debugging in PAM.
+ * Cristian Gafton <gafton@redhat.com>
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+/*
+ * This is for debugging purposes ONLY. DO NOT use on live systems !!!
+ * You have been warned :-) - CG
+ *
+ * to get automated debugging to the log file, it must be created manually.
+ * _PAM_LOGFILE must exist and be writable to the programs you debug.
+ */
+
+#ifndef _PAM_LOGFILE
+#define _PAM_LOGFILE "/var/run/pam-debug.log"
+#endif
+
+static void _pam_output_debug_info(const char *file, const char *fn
+ , const int line)
+{
+ FILE *logfile;
+ int must_close = 1, fd;
+
+#ifdef O_NOFOLLOW
+ if ((fd = open(_PAM_LOGFILE, O_WRONLY|O_NOFOLLOW|O_APPEND)) != -1) {
+#else
+ if ((fd = open(_PAM_LOGFILE, O_WRONLY|O_APPEND)) != -1) {
+#endif
+ if (!(logfile = fdopen(fd,"a"))) {
+ logfile = stderr;
+ must_close = 0;
+ close(fd);
+ }
+ } else {
+ logfile = stderr;
+ must_close = 0;
+ }
+ fprintf(logfile,"[%s:%s(%d)] ",file, fn, line);
+ fflush(logfile);
+ if (must_close)
+ fclose(logfile);
+}
+
+static void _pam_output_debug(const char *format, ...)
+{
+ va_list args;
+ FILE *logfile;
+ int must_close = 1, fd;
+
+ va_start(args, format);
+
+#ifdef O_NOFOLLOW
+ if ((fd = open(_PAM_LOGFILE, O_WRONLY|O_NOFOLLOW|O_APPEND)) != -1) {
+#else
+ if ((fd = open(_PAM_LOGFILE, O_WRONLY|O_APPEND)) != -1) {
+#endif
+ if (!(logfile = fdopen(fd,"a"))) {
+ logfile = stderr;
+ must_close = 0;
+ close(fd);
+ }
+ } else {
+ logfile = stderr;
+ must_close = 0;
+ }
+ vfprintf(logfile, format, args);
+ fprintf(logfile, "\n");
+ fflush(logfile);
+ if (must_close)
+ fclose(logfile);
+
+ va_end(args);
+}
+
+#define D(x) do { \
+ _pam_output_debug_info(__FILE__, __FUNCTION__, __LINE__); \
+ _pam_output_debug x ; \
+} while (0)
+
+#define _pam_show_mem(X,XS) do { \
+ int i; \
+ register unsigned char *x; \
+ x = (unsigned char *)X; \
+ fprintf(stderr, " <start at %p>\n", X); \
+ for (i = 0; i < XS ; ++x, ++i) { \
+ fprintf(stderr, " %02X. <%p:%02X>\n", i, x, *x); \
+ } \
+ fprintf(stderr, " <end for %p after %d bytes>\n", X, XS); \
+} while (0)
+
+#define _pam_show_reply(/* struct pam_response * */reply, /* int */replies) \
+do { \
+ int reply_i; \
+ setbuf(stderr, NULL); \
+ fprintf(stderr, "array at %p of size %d\n",reply,replies); \
+ fflush(stderr); \
+ if (reply) { \
+ for (reply_i = 0; reply_i < replies; reply_i++) { \
+ fprintf(stderr, " elem# %d at %p: resp = %p, retcode = %d\n", \
+ reply_i, reply+reply_i, reply[reply_i].resp, \
+ reply[reply_i].resp, _retcode); \
+ fflush(stderr); \
+ if (reply[reply_i].resp) { \
+ fprintf(stderr, " resp[%d] = '%s'\n", \
+ strlen(reply[reply_i].resp), reply[reply_i].resp); \
+ fflush(stderr); \
+ } \
+ } \
+ } \
+ fprintf(stderr, "done here\n"); \
+ fflush(stderr); \
+} while (0)
+
+#else
+
+#define D(x) do { } while (0)
+#define _pam_show_mem(X,XS) do { } while (0)
+#define _pam_show_reply(reply, replies) do { } while (0)
+
+#endif /* PAM_DEBUG */
+
+#endif /* PAM_MACROS_H */
diff --git a/libpam/include/security/_pam_types.h b/libpam/include/security/_pam_types.h
new file mode 100644
index 0000000..2abb7ee
--- /dev/null
+++ b/libpam/include/security/_pam_types.h
@@ -0,0 +1,333 @@
+/*
+ * <security/_pam_types.h>
+ *
+ * This file defines all of the types common to the Linux-PAM library
+ * applications and modules.
+ *
+ * Note, the copyright+license information is at end of file.
+ */
+
+#ifndef _SECURITY__PAM_TYPES_H
+#define _SECURITY__PAM_TYPES_H
+
+/* This is a blind structure; users aren't allowed to see inside a
+ * pam_handle_t, so we don't define struct pam_handle here. This is
+ * defined in a file private to the PAM library. (i.e., it's private
+ * to PAM service modules, too!) */
+
+typedef struct pam_handle pam_handle_t;
+
+/* ---------------- The Linux-PAM Version defines ----------------- */
+
+/* Major and minor version number of the Linux-PAM package. Use
+ these macros to test for features in specific releases. */
+#define __LINUX_PAM__ 1
+#define __LINUX_PAM_MINOR__ 0
+
+/* ----------------- The Linux-PAM return values ------------------ */
+
+#define PAM_SUCCESS 0 /* Successful function return */
+#define PAM_OPEN_ERR 1 /* dlopen() failure when dynamically */
+ /* loading a service module */
+#define PAM_SYMBOL_ERR 2 /* Symbol not found */
+#define PAM_SERVICE_ERR 3 /* Error in service module */
+#define PAM_SYSTEM_ERR 4 /* System error */
+#define PAM_BUF_ERR 5 /* Memory buffer error */
+#define PAM_PERM_DENIED 6 /* Permission denied */
+#define PAM_AUTH_ERR 7 /* Authentication failure */
+#define PAM_CRED_INSUFFICIENT 8 /* Can not access authentication data */
+ /* due to insufficient credentials */
+#define PAM_AUTHINFO_UNAVAIL 9 /* Underlying authentication service */
+ /* can not retrieve authentication */
+ /* information */
+#define PAM_USER_UNKNOWN 10 /* User not known to the underlying */
+ /* authentication module */
+#define PAM_MAXTRIES 11 /* An authentication service has */
+ /* maintained a retry count which has */
+ /* been reached. No further retries */
+ /* should be attempted */
+#define PAM_NEW_AUTHTOK_REQD 12 /* New authentication token required. */
+ /* This is normally returned if the */
+ /* machine security policies require */
+ /* that the password should be changed */
+ /* because the password is NULL or it */
+ /* has aged */
+#define PAM_ACCT_EXPIRED 13 /* User account has expired */
+#define PAM_SESSION_ERR 14 /* Can not make/remove an entry for */
+ /* the specified session */
+#define PAM_CRED_UNAVAIL 15 /* Underlying authentication service */
+ /* can not retrieve user credentials */
+ /* unavailable */
+#define PAM_CRED_EXPIRED 16 /* User credentials expired */
+#define PAM_CRED_ERR 17 /* Failure setting user credentials */
+#define PAM_NO_MODULE_DATA 18 /* No module specific data is present */
+#define PAM_CONV_ERR 19 /* Conversation error */
+#define PAM_AUTHTOK_ERR 20 /* Authentication token manipulation error */
+#define PAM_AUTHTOK_RECOVERY_ERR 21 /* Authentication information */
+ /* cannot be recovered */
+#define PAM_AUTHTOK_LOCK_BUSY 22 /* Authentication token lock busy */
+#define PAM_AUTHTOK_DISABLE_AGING 23 /* Authentication token aging disabled */
+#define PAM_TRY_AGAIN 24 /* Preliminary check by password service */
+#define PAM_IGNORE 25 /* Ignore underlying account module */
+ /* regardless of whether the control */
+ /* flag is required, optional, or sufficient */
+#define PAM_ABORT 26 /* Critical error (?module fail now request) */
+#define PAM_AUTHTOK_EXPIRED 27 /* user's authentication token has expired */
+#define PAM_MODULE_UNKNOWN 28 /* module is not known */
+
+#define PAM_BAD_ITEM 29 /* Bad item passed to pam_*_item() */
+#define PAM_CONV_AGAIN 30 /* conversation function is event driven
+ and data is not available yet */
+#define PAM_INCOMPLETE 31 /* please call this function again to
+ complete authentication stack. Before
+ calling again, verify that conversation
+ is completed */
+
+/*
+ * Add new #define's here - take care to also extend the libpam code:
+ * pam_strerror() and "libpam/pam_tokens.h" .
+ */
+
+#define _PAM_RETURN_VALUES 32 /* this is the number of return values */
+
+
+/* ---------------------- The Linux-PAM flags -------------------- */
+
+/* Authentication service should not generate any messages */
+#define PAM_SILENT 0x8000U
+
+/* Note: these flags are used by pam_authenticate{,_secondary}() */
+
+/* The authentication service should return PAM_AUTH_ERROR if the
+ * user has a null authentication token */
+#define PAM_DISALLOW_NULL_AUTHTOK 0x0001U
+
+/* Note: these flags are used for pam_setcred() */
+
+/* Set user credentials for an authentication service */
+#define PAM_ESTABLISH_CRED 0x0002U
+
+/* Delete user credentials associated with an authentication service */
+#define PAM_DELETE_CRED 0x0004U
+
+/* Reinitialize user credentials */
+#define PAM_REINITIALIZE_CRED 0x0008U
+
+/* Extend lifetime of user credentials */
+#define PAM_REFRESH_CRED 0x0010U
+
+/* Note: these flags are used by pam_chauthtok */
+
+/* The password service should only update those passwords that have
+ * aged. If this flag is not passed, the password service should
+ * update all passwords. */
+#define PAM_CHANGE_EXPIRED_AUTHTOK 0x0020U
+
+/* ------------------ The Linux-PAM item types ------------------- */
+
+/* These defines are used by pam_set_item() and pam_get_item().
+ Please check the spec which are allowed for use by applications
+ and which are only allowed for use by modules. */
+
+#define PAM_SERVICE 1 /* The service name */
+#define PAM_USER 2 /* The user name */
+#define PAM_TTY 3 /* The tty name */
+#define PAM_RHOST 4 /* The remote host name */
+#define PAM_CONV 5 /* The pam_conv structure */
+#define PAM_AUTHTOK 6 /* The authentication token (password) */
+#define PAM_OLDAUTHTOK 7 /* The old authentication token */
+#define PAM_RUSER 8 /* The remote user name */
+#define PAM_USER_PROMPT 9 /* the prompt for getting a username */
+/* Linux-PAM extensions */
+#define PAM_FAIL_DELAY 10 /* app supplied function to override failure
+ delays */
+#define PAM_XDISPLAY 11 /* X display name */
+#define PAM_XAUTHDATA 12 /* X server authentication data */
+#define PAM_AUTHTOK_TYPE 13 /* The type for pam_get_authtok */
+
+/* -------------- Special defines used by Linux-PAM -------------- */
+
+#if defined(__GNUC__) && defined(__GNUC_MINOR__)
+# define PAM_GNUC_PREREQ(maj, min) \
+ ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
+#else
+# define PAM_GNUC_PREREQ(maj, min) 0
+#endif
+
+#if PAM_GNUC_PREREQ(2,5)
+# define PAM_FORMAT(params) __attribute__((__format__ params))
+#else
+# define PAM_FORMAT(params)
+#endif
+
+#if PAM_GNUC_PREREQ(3,3) && !defined(LIBPAM_COMPILE)
+# define PAM_NONNULL(params) __attribute__((__nonnull__ params))
+#else
+# define PAM_NONNULL(params)
+#endif
+
+/* ---------- Common Linux-PAM application/module PI ----------- */
+
+extern int PAM_NONNULL((1))
+pam_set_item(pam_handle_t *pamh, int item_type, const void *item);
+
+extern int PAM_NONNULL((1))
+pam_get_item(const pam_handle_t *pamh, int item_type, const void **item);
+
+extern const char *
+pam_strerror(pam_handle_t *pamh, int errnum);
+
+extern int PAM_NONNULL((1,2))
+pam_putenv(pam_handle_t *pamh, const char *name_value);
+
+extern const char * PAM_NONNULL((1,2))
+pam_getenv(pam_handle_t *pamh, const char *name);
+
+extern char ** PAM_NONNULL((1))
+pam_getenvlist(pam_handle_t *pamh);
+
+/* ---------- Common Linux-PAM application/module PI ----------- */
+
+/*
+ * here are some proposed error status definitions for the
+ * 'error_status' argument used by the cleanup function associated
+ * with data items they should be logically OR'd with the error_status
+ * of the latest return from libpam -- new with .52 and positive
+ * impression from Sun although not official as of 1996/9/4
+ * [generally the other flags are to be found in pam_modules.h]
+ */
+
+#define PAM_DATA_SILENT 0x40000000 /* used to suppress messages... */
+
+/*
+ * here we define an externally (by apps or modules) callable function
+ * that primes the libpam library to delay when a stacked set of
+ * modules results in a failure. In the case of PAM_SUCCESS this delay
+ * is ignored.
+ *
+ * Note, the pam_[gs]et_item(... PAM_FAIL_DELAY ...) can be used to set
+ * a function pointer which can override the default fail-delay behavior.
+ * This item was added to accommodate event driven programs that need to
+ * manage delays more carefully. The function prototype for this data
+ * item is
+ * void (*fail_delay)(int status, unsigned int delay, void *appdata_ptr);
+ */
+
+#define HAVE_PAM_FAIL_DELAY
+extern int pam_fail_delay(pam_handle_t *pamh, unsigned int musec_delay);
+
+/* ------------ The Linux-PAM conversation structures ------------ */
+
+/* Message styles */
+
+#define PAM_PROMPT_ECHO_OFF 1
+#define PAM_PROMPT_ECHO_ON 2
+#define PAM_ERROR_MSG 3
+#define PAM_TEXT_INFO 4
+
+/* Linux-PAM specific types */
+
+#define PAM_RADIO_TYPE 5 /* yes/no/maybe conditionals */
+
+/* This is for server client non-human interaction.. these are NOT
+ part of the X/Open PAM specification. */
+
+#define PAM_BINARY_PROMPT 7
+
+/* maximum size of messages/responses etc.. (these are mostly
+ arbitrary so Linux-PAM should handle longer values). */
+
+#define PAM_MAX_NUM_MSG 32
+#define PAM_MAX_MSG_SIZE 512
+#define PAM_MAX_RESP_SIZE 512
+
+/* Used to pass prompting text, error messages, or other informatory
+ * text to the user. This structure is allocated and freed by the PAM
+ * library (or loaded module). */
+
+struct pam_message {
+ int msg_style;
+ const char *msg;
+};
+
+/* if the pam_message.msg_style = PAM_BINARY_PROMPT
+ the 'pam_message.msg' is a pointer to a 'const *' for the following
+ pseudo-structure. When used with a PAM_BINARY_PROMPT, the returned
+ pam_response.resp pointer points to an object with the following
+ structure:
+
+ struct {
+ u32 length; # network byte order
+ unsigned char type;
+ unsigned char data[length-5];
+ };
+
+ The 'libpamc' library is designed around this flavor of
+ message and should be used to handle this flavor of msg_style.
+ */
+
+/* Used to return the user's response to the PAM library. This
+ structure is allocated by the application program, and free()'d by
+ the Linux-PAM library (or calling module). */
+
+struct pam_response {
+ char *resp;
+ int resp_retcode; /* currently un-used, zero expected */
+};
+
+/* The actual conversation structure itself */
+
+struct pam_conv {
+ int (*conv)(int num_msg, const struct pam_message **msg,
+ struct pam_response **resp, void *appdata_ptr);
+ void *appdata_ptr;
+};
+
+/* Used by the PAM_XAUTHDATA pam item. Contains X authentication
+ data used by modules to connect to the user's X display. Note:
+ this structure is intentionally compatible with xcb_auth_info_t. */
+
+struct pam_xauth_data {
+ int namelen;
+ char *name;
+ int datalen;
+ char *data;
+};
+
+/* ... adapted from the pam_appl.h file created by Theodore Ts'o and
+ *
+ * Copyright Theodore Ts'o, 1996. All rights reserved.
+ * Copyright (c) Andrew G. Morgan <morgan@linux.kernel.org>, 1996-8
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#endif /* _SECURITY__PAM_TYPES_H */
diff --git a/libpam/include/security/pam_appl.h b/libpam/include/security/pam_appl.h
new file mode 100644
index 0000000..cf97a49
--- /dev/null
+++ b/libpam/include/security/pam_appl.h
@@ -0,0 +1,104 @@
+/*
+ * <security/pam_appl.h>
+ *
+ * This header file collects definitions for the PAM API --- that is,
+ * public interface between the PAM library and an application program
+ * that wishes to use it.
+ *
+ * Note, the copyright information is at end of file.
+ */
+
+#ifndef _SECURITY_PAM_APPL_H
+#define _SECURITY_PAM_APPL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <security/_pam_types.h> /* Linux-PAM common defined types */
+
+/* -------------- The Linux-PAM Framework layer API ------------- */
+
+extern int PAM_NONNULL((1,3,4))
+pam_start(const char *service_name, const char *user,
+ const struct pam_conv *pam_conversation,
+ pam_handle_t **pamh);
+
+extern int PAM_NONNULL((1,3,5))
+pam_start_confdir(const char *service_name, const char *user,
+ const struct pam_conv *pam_conversation,
+ const char *confdir, pam_handle_t **pamh);
+
+extern int PAM_NONNULL((1))
+pam_end(pam_handle_t *pamh, int pam_status);
+
+/* Authentication API's */
+
+extern int PAM_NONNULL((1))
+pam_authenticate(pam_handle_t *pamh, int flags);
+
+extern int PAM_NONNULL((1))
+pam_setcred(pam_handle_t *pamh, int flags);
+
+/* Account Management API's */
+
+extern int PAM_NONNULL((1))
+pam_acct_mgmt(pam_handle_t *pamh, int flags);
+
+/* Session Management API's */
+
+extern int PAM_NONNULL((1))
+pam_open_session(pam_handle_t *pamh, int flags);
+
+extern int PAM_NONNULL((1))
+pam_close_session(pam_handle_t *pamh, int flags);
+
+/* Password Management API's */
+
+extern int PAM_NONNULL((1))
+pam_chauthtok(pam_handle_t *pamh, int flags);
+
+
+/* take care of any compatibility issues */
+#include <security/_pam_compat.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+/*
+ * Copyright Theodore Ts'o, 1996. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#endif /* _SECURITY_PAM_APPL_H */
diff --git a/libpam/include/security/pam_ext.h b/libpam/include/security/pam_ext.h
new file mode 100644
index 0000000..7542861
--- /dev/null
+++ b/libpam/include/security/pam_ext.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2005, 2006, 2008, 2009 Thorsten Kukuk.
+ *
+ * <security/pam_ext.h>
+ *
+ * This header file collects definitions for the extended PAM API.
+ * This is a public interface of the PAM library for PAM modules,
+ * which makes the life of PAM developers easier, but are not documented
+ * in any standard and are not portable between different PAM
+ * implementations.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SECURITY__PAM_EXT_H_
+#define _SECURITY__PAM_EXT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <security/_pam_types.h>
+#include <stdarg.h>
+
+extern void PAM_FORMAT((printf, 3, 0)) PAM_NONNULL((3))
+pam_vsyslog (const pam_handle_t *pamh, int priority,
+ const char *fmt, va_list args);
+
+extern void PAM_FORMAT((printf, 3, 4)) PAM_NONNULL((3))
+pam_syslog (const pam_handle_t *pamh, int priority, const char *fmt, ...);
+
+extern int PAM_FORMAT((printf, 4, 0)) PAM_NONNULL((1,4))
+pam_vprompt (pam_handle_t *pamh, int style, char **response,
+ const char *fmt, va_list args);
+
+extern int PAM_FORMAT((printf, 4, 5)) PAM_NONNULL((1,4))
+pam_prompt (pam_handle_t *pamh, int style, char **response,
+ const char *fmt, ...);
+
+#define pam_error(pamh, fmt...) \
+ pam_prompt(pamh, PAM_ERROR_MSG, NULL, fmt)
+#define pam_verror(pamh, fmt, args) \
+ pam_vprompt(pamh, PAM_ERROR_MSG, NULL, fmt, args)
+
+#define pam_info(pamh, fmt...) pam_prompt(pamh, PAM_TEXT_INFO, NULL, fmt)
+#define pam_vinfo(pamh, fmt, args) pam_vprompt(pamh, PAM_TEXT_INFO, NULL, fmt, args)
+
+extern int PAM_NONNULL((1,3))
+pam_get_authtok (pam_handle_t *pamh, int item, const char **authtok,
+ const char *prompt);
+extern int PAM_NONNULL((1,2))
+pam_get_authtok_noverify (pam_handle_t *pamh, const char **authtok,
+ const char *prompt);
+extern int PAM_NONNULL((1,2))
+pam_get_authtok_verify (pam_handle_t *pamh, const char **authtok,
+ const char *prompt);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libpam/include/security/pam_modules.h b/libpam/include/security/pam_modules.h
new file mode 100644
index 0000000..ec65e3e
--- /dev/null
+++ b/libpam/include/security/pam_modules.h
@@ -0,0 +1,124 @@
+/*
+ * <security/pam_modules.h>
+ *
+ * This header file collects definitions for the PAM API --- that is,
+ * public interface between the PAM library and PAM modules.
+ *
+ * Note, the copyright information is at end of file.
+ */
+
+#ifndef _SECURITY_PAM_MODULES_H
+#define _SECURITY_PAM_MODULES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <security/_pam_types.h> /* Linux-PAM common defined types */
+
+/* -------------- The Linux-PAM Module PI ------------- */
+
+extern int PAM_NONNULL((1,2))
+pam_set_data(pam_handle_t *pamh, const char *module_data_name, void *data,
+ void (*cleanup)(pam_handle_t *pamh, void *data,
+ int error_status));
+
+extern int PAM_NONNULL((1,2,3))
+pam_get_data(const pam_handle_t *pamh, const char *module_data_name,
+ const void **data);
+
+extern int PAM_NONNULL((1,2))
+pam_get_user(pam_handle_t *pamh, const char **user, const char *prompt);
+
+/* Authentication API's */
+int pam_sm_authenticate(pam_handle_t *pamh, int flags,
+ int argc, const char **argv);
+int pam_sm_setcred(pam_handle_t *pamh, int flags,
+ int argc, const char **argv);
+
+/* Account Management API's */
+int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
+ int argc, const char **argv);
+
+/* Session Management API's */
+int pam_sm_open_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv);
+
+int pam_sm_close_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv);
+
+/* Password Management API's */
+int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
+ int argc, const char **argv);
+
+/* The following two flags are for use across the Linux-PAM/module
+ * interface only. The Application is not permitted to use these
+ * tokens.
+ *
+ * The password service should only perform preliminary checks. No
+ * passwords should be updated. */
+#define PAM_PRELIM_CHECK 0x4000
+
+/* The password service should update passwords Note: PAM_PRELIM_CHECK
+ * and PAM_UPDATE_AUTHTOK cannot both be set simultaneously! */
+#define PAM_UPDATE_AUTHTOK 0x2000
+
+
+/*
+ * here are some proposed error status definitions for the
+ * 'error_status' argument used by the cleanup function associated
+ * with data items they should be logically OR'd with the error_status
+ * of the latest return from libpam -- new with .52 and positive
+ * impression from Sun although not official as of 1996/9/4 there are
+ * others in _pam_types.h -- they are for common module/app use.
+ */
+
+#define PAM_DATA_REPLACE 0x20000000 /* used when replacing a data item */
+
+/* PAM_EXTERN isn't needed anymore, but don't remove it to not break
+ lot of external code using it. */
+#define PAM_EXTERN extern
+
+/* take care of any compatibility issues */
+#include <security/_pam_compat.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Copyright (C) Theodore Ts'o, 1996.
+ * Copyright (C) Andrew Morgan, 1996-8.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU General Public License, in which case the provisions of the
+ * GNU GPL are required INSTEAD OF the above restrictions. (This
+ * clause is necessary due to a potential bad interaction between the
+ * GNU GPL and the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#endif /* _SECURITY_PAM_MODULES_H */
diff --git a/libpam/include/security/pam_modutil.h b/libpam/include/security/pam_modutil.h
new file mode 100644
index 0000000..33f87b9
--- /dev/null
+++ b/libpam/include/security/pam_modutil.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2001-2002 Andrew Morgan <morgan@kernel.org>
+ *
+ * <security/pam_modutil.h>
+ *
+ * This file is a list of handy libc wrappers that attempt to provide some
+ * thread-safe and other convenient functionality to modules in a common form.
+ *
+ * A number of these functions reserve space in a pam_[sg]et_data item.
+ * In all cases, the name of the item is prefixed with "pam_modutil_*".
+ *
+ * On systems that simply can't support thread safe programming, these
+ * functions don't support it either - sorry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SECURITY__PAM_MODUTIL_H
+#define _SECURITY__PAM_MODUTIL_H
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include <shadow.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <security/_pam_types.h>
+
+extern int PAM_NONNULL((1,2))
+pam_modutil_check_user_in_passwd(pam_handle_t *pamh,
+ const char *user_name,
+ const char *file_name);
+
+extern struct passwd * PAM_NONNULL((1,2))
+pam_modutil_getpwnam(pam_handle_t *pamh, const char *user);
+
+extern struct passwd * PAM_NONNULL((1))
+pam_modutil_getpwuid(pam_handle_t *pamh, uid_t uid);
+
+extern struct group * PAM_NONNULL((1,2))
+pam_modutil_getgrnam(pam_handle_t *pamh, const char *group);
+
+extern struct group * PAM_NONNULL((1))
+pam_modutil_getgrgid(pam_handle_t *pamh, gid_t gid);
+
+extern struct spwd * PAM_NONNULL((1,2))
+pam_modutil_getspnam(pam_handle_t *pamh, const char *user);
+
+extern int PAM_NONNULL((1,2,3))
+pam_modutil_user_in_group_nam_nam(pam_handle_t *pamh,
+ const char *user,
+ const char *group);
+
+extern int PAM_NONNULL((1,2))
+pam_modutil_user_in_group_nam_gid(pam_handle_t *pamh,
+ const char *user,
+ gid_t group);
+
+extern int PAM_NONNULL((1,3))
+pam_modutil_user_in_group_uid_nam(pam_handle_t *pamh,
+ uid_t user,
+ const char *group);
+
+extern int PAM_NONNULL((1))
+pam_modutil_user_in_group_uid_gid(pam_handle_t *pamh,
+ uid_t user,
+ gid_t group);
+
+extern const char * PAM_NONNULL((1))
+pam_modutil_getlogin(pam_handle_t *pamh);
+
+extern int
+pam_modutil_read(int fd, char *buffer, int count);
+
+extern int
+pam_modutil_write(int fd, const char *buffer, int count);
+
+extern int PAM_NONNULL((1,3))
+pam_modutil_audit_write(pam_handle_t *pamh, int type,
+ const char *message, int retval);
+
+struct pam_modutil_privs {
+ gid_t *grplist;
+ int number_of_groups;
+ int allocated;
+ gid_t old_gid;
+ uid_t old_uid;
+ int is_dropped;
+};
+
+#define PAM_MODUTIL_NGROUPS 64
+#define PAM_MODUTIL_DEF_PRIVS(n) \
+ gid_t n##_grplist[PAM_MODUTIL_NGROUPS]; \
+ struct pam_modutil_privs n = { n##_grplist, PAM_MODUTIL_NGROUPS, 0, -1, -1, 0 }
+
+extern int PAM_NONNULL((1,2,3))
+pam_modutil_drop_priv(pam_handle_t *pamh,
+ struct pam_modutil_privs *p,
+ const struct passwd *pw);
+
+extern int PAM_NONNULL((1,2))
+pam_modutil_regain_priv(pam_handle_t *pamh,
+ struct pam_modutil_privs *p);
+
+enum pam_modutil_redirect_fd {
+ PAM_MODUTIL_IGNORE_FD, /* do not redirect */
+ PAM_MODUTIL_PIPE_FD, /* redirect to a pipe */
+ PAM_MODUTIL_NULL_FD, /* redirect to /dev/null */
+};
+
+/* redirect standard descriptors, close all other descriptors. */
+extern int PAM_NONNULL((1))
+pam_modutil_sanitize_helper_fds(pam_handle_t *pamh,
+ enum pam_modutil_redirect_fd redirect_stdin,
+ enum pam_modutil_redirect_fd redirect_stdout,
+ enum pam_modutil_redirect_fd redirect_stderr);
+
+/* lookup a value for key in login.defs file or similar key value format */
+extern char * PAM_NONNULL((1,2,3))
+pam_modutil_search_key(pam_handle_t *pamh,
+ const char *file_name,
+ const char *key);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SECURITY__PAM_MODUTIL_H */
diff --git a/libpam/include/test_assert.h b/libpam/include/test_assert.h
new file mode 100644
index 0000000..9d30d62
--- /dev/null
+++ b/libpam/include/test_assert.h
@@ -0,0 +1,55 @@
+/*
+ * Assert definitions for tests.
+ *
+ * Copyright (c) 2020 Dmitry V. Levin <ldv@altlinux.org>
+ */
+
+#ifndef TEST_ASSERT_H
+# define TEST_ASSERT_H
+
+# ifdef HAVE_CONFIG_H
+# include <config.h>
+# endif
+
+# include <stdio.h>
+# include <stdlib.h>
+
+# define ASSERT_(expected_, expected_str_, op_, seen_, seen_str_) \
+ do { \
+ __typeof__(expected_) e_ = (expected_); \
+ __typeof__(seen_) s_ = (seen_); \
+ if (e_ op_ s_) break; \
+ fprintf(stderr, \
+ "%s:%d: Assertion failed: %s (%#lx) %s %s (%#lx)\n", \
+ __FILE__, __LINE__, \
+ (expected_str_), (unsigned long) e_, #op_, \
+ (seen_str_), (unsigned long) s_); \
+ abort(); \
+ } while (0) \
+/* End of ASSERT_ definition. */
+
+# define ASSERT_EQ(expected_, seen_) \
+ ASSERT_((expected_), #expected_, ==, (seen_), #seen_) \
+/* End of ASSERT_EQ definition. */
+
+# define ASSERT_NE(expected_, seen_) \
+ ASSERT_((expected_), #expected_, !=, (seen_), #seen_) \
+/* End of ASSERT_NE definition. */
+
+# define ASSERT_LT(expected_, seen_) \
+ ASSERT_((expected_), #expected_, <, (seen_), #seen_) \
+/* End of ASSERT_LT definition. */
+
+# define ASSERT_LE(expected_, seen_) \
+ ASSERT_((expected_), #expected_, <=, (seen_), #seen_) \
+/* End of ASSERT_LT definition. */
+
+# define ASSERT_GT(expected_, seen_) \
+ ASSERT_((expected_), #expected_, >, (seen_), #seen_) \
+/* End of ASSERT_LT definition. */
+
+# define ASSERT_GE(expected_, seen_) \
+ ASSERT_((expected_), #expected_, >=, (seen_), #seen_) \
+/* End of ASSERT_LT definition. */
+
+#endif /* TEST_ASSERT_H */
diff --git a/libpam/libpam.map b/libpam/libpam.map
new file mode 100644
index 0000000..3cc7ef3
--- /dev/null
+++ b/libpam/libpam.map
@@ -0,0 +1,89 @@
+LIBPAM_1.0 {
+ global:
+ pam_acct_mgmt;
+ pam_authenticate;
+ pam_chauthtok;
+ pam_close_session;
+ pam_end;
+ pam_open_session;
+ pam_setcred;
+ pam_start;
+ pam_getenv;
+ pam_putenv;
+ pam_getenvlist;
+ pam_set_item;
+ pam_get_item;
+ pam_strerror;
+ pam_fail_delay;
+ pam_set_data;
+ pam_get_data;
+ pam_get_user;
+
+ local:
+ *;
+};
+LIBPAM_EXTENSION_1.0 {
+ global:
+ pam_prompt;
+ pam_vprompt;
+ pam_syslog;
+ pam_vsyslog;
+};
+
+LIBPAM_EXTENSION_1.1 {
+ global:
+ pam_get_authtok;
+} LIBPAM_EXTENSION_1.0;
+
+LIBPAM_EXTENSION_1.1.1 {
+ global:
+ pam_get_authtok_noverify;
+ pam_get_authtok_verify;
+} LIBPAM_EXTENSION_1.1;
+
+LIBPAM_MODUTIL_1.0 {
+ global:
+ pam_modutil_getpwnam;
+ pam_modutil_getpwuid;
+ pam_modutil_getgrnam;
+ pam_modutil_getgrgid;
+ pam_modutil_getspnam;
+ pam_modutil_user_in_group_nam_nam;
+ pam_modutil_user_in_group_nam_gid;
+ pam_modutil_user_in_group_uid_nam;
+ pam_modutil_user_in_group_uid_gid;
+ pam_modutil_getlogin;
+ pam_modutil_read;
+ pam_modutil_write;
+};
+
+LIBPAM_MODUTIL_1.1 {
+ global:
+ pam_modutil_audit_write;
+} LIBPAM_MODUTIL_1.0;
+
+LIBPAM_MODUTIL_1.1.3 {
+ global:
+ pam_modutil_drop_priv;
+ pam_modutil_regain_priv;
+} LIBPAM_MODUTIL_1.1;
+
+LIBPAM_MODUTIL_1.1.9 {
+ global:
+ pam_modutil_sanitize_helper_fds;
+} LIBPAM_MODUTIL_1.1.3;
+
+LIBPAM_MODUTIL_1.3.2 {
+ global:
+ pam_modutil_search_key;
+} LIBPAM_MODUTIL_1.1.9;
+
+LIBPAM_1.4 {
+ global:
+ pam_start_confdir;
+} LIBPAM_1.0;
+
+LIBPAM_MODUTIL_1.4.1 {
+ global:
+ pam_modutil_check_user_in_passwd;
+} LIBPAM_MODUTIL_1.3.2;
diff --git a/libpam/pam.pc.in b/libpam/pam.pc.in
new file mode 100644
index 0000000..a7cf852
--- /dev/null
+++ b/libpam/pam.pc.in
@@ -0,0 +1,9 @@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: PAM
+Description: The primary Linux-PAM library. It is used by PAM modules and PAM-aware applications.
+URL: http://www.linux-pam.org/
+Version: @VERSION@
+Cflags: -I${includedir}
+Libs: -L${libdir} -lpam
diff --git a/libpam/pam_account.c b/libpam/pam_account.c
new file mode 100644
index 0000000..3a4fb1f
--- /dev/null
+++ b/libpam/pam_account.c
@@ -0,0 +1,23 @@
+/* pam_account.c - PAM Account Management */
+
+#include "pam_private.h"
+
+#include <stdio.h>
+
+int pam_acct_mgmt(pam_handle_t *pamh, int flags)
+{
+ int retval;
+
+ D(("called"));
+
+ IF_NO_PAMH("pam_acct_mgmt", pamh, PAM_SYSTEM_ERR);
+
+ if (__PAM_FROM_MODULE(pamh)) {
+ D(("called from module!?"));
+ return PAM_SYSTEM_ERR;
+ }
+
+ retval = _pam_dispatch(pamh, flags, PAM_ACCOUNT);
+
+ return retval;
+}
diff --git a/libpam/pam_audit.c b/libpam/pam_audit.c
new file mode 100644
index 0000000..97a9a92
--- /dev/null
+++ b/libpam/pam_audit.c
@@ -0,0 +1,242 @@
+/* pam_audit.c -- Instrumentation code for Linux Auditing System */
+
+/* (C) 2005-2006 Red Hat, Inc. -- Licensing details are in the COPYING
+ file accompanying the Linux-PAM source distribution.
+
+ Authors:
+ Steve Grubb <sgrubb@redhat.com> */
+
+#include "pam_private.h"
+#include "pam_modutil_private.h"
+
+#ifdef HAVE_LIBAUDIT
+#include <stdio.h>
+#include <syslog.h>
+#include <libaudit.h>
+#include <pwd.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#define PAMAUDIT_LOGGED 1
+
+static int
+_pam_audit_writelog(pam_handle_t *pamh, int audit_fd, int type,
+ const char *message, const char *grantors, int retval)
+{
+ static int old_errno = -1;
+ int rc = -ENOMEM;
+ char *buf;
+ const char *grantors_field = " grantors=";
+
+ if (grantors == NULL) {
+ grantors = "";
+ grantors_field = "";
+ }
+
+ if (asprintf(&buf, "PAM:%s%s%s", message, grantors_field, grantors) >= 0) {
+ rc = audit_log_acct_message(audit_fd, type, NULL, buf,
+ (retval != PAM_USER_UNKNOWN && pamh->user) ? pamh->user : "?",
+ -1, pamh->rhost, NULL, pamh->tty, retval == PAM_SUCCESS);
+ free(buf);
+ }
+
+ /* libaudit sets errno to his own negative error code. This can be
+ an official errno number, but must not. It can also be a audit
+ internal error code. Which makes errno useless :-((. Try the
+ best to fix it. */
+ errno = -rc;
+
+ pamh->audit_state |= PAMAUDIT_LOGGED;
+
+ if (rc < 0) {
+ if (rc == -EPERM)
+ return 0;
+ if (errno != old_errno) {
+ old_errno = errno;
+ pam_syslog (pamh, LOG_CRIT, "audit_log_acct_message() failed: %m");
+ }
+ }
+ return rc;
+}
+
+static int
+_pam_audit_open(pam_handle_t *pamh)
+{
+ int audit_fd;
+ audit_fd = audit_open();
+ if (audit_fd < 0) {
+ /* You get these error codes only when the kernel doesn't have
+ * audit compiled in. */
+ if (errno == EINVAL || errno == EPROTONOSUPPORT ||
+ errno == EAFNOSUPPORT)
+ return -2;
+
+ /* this should only fail in case of extreme resource shortage,
+ * need to prevent login in that case for CAPP compliance.
+ */
+ pam_syslog(pamh, LOG_CRIT, "audit_open() failed: %m");
+ return -1;
+ }
+
+ return audit_fd;
+}
+
+static int
+_pam_list_grantors(struct handler *hlist, int retval, char **list)
+{
+ *list = NULL;
+
+ if (retval == PAM_SUCCESS) {
+ struct handler *h;
+ char *p = NULL;
+ size_t len = 0;
+
+ for (h = hlist; h != NULL; h = h->next) {
+ if (h->grantor) {
+ len += strlen(h->mod_name) + 1;
+ }
+ }
+
+ if (len == 0) {
+ return 0;
+ }
+
+ *list = malloc(len);
+ if (*list == NULL) {
+ return -1;
+ }
+
+ for (h = hlist; h != NULL; h = h->next) {
+ if (h->grantor) {
+ if (p == NULL) {
+ p = *list;
+ } else {
+ p = stpcpy(p, ",");
+ }
+
+ p = stpcpy(p, h->mod_name);
+ }
+ }
+ }
+
+ return 0;
+}
+
+int
+_pam_auditlog(pam_handle_t *pamh, int action, int retval, int flags, struct handler *h)
+{
+ const char *message;
+ int type;
+ int audit_fd;
+ char *grantors;
+
+ if ((audit_fd=_pam_audit_open(pamh)) == -1) {
+ return PAM_SYSTEM_ERR;
+ } else if (audit_fd == -2) {
+ return retval;
+ }
+
+ switch (action) {
+ case PAM_AUTHENTICATE:
+ message = "authentication";
+ type = AUDIT_USER_AUTH;
+ break;
+ case PAM_OPEN_SESSION:
+ message = "session_open";
+ type = AUDIT_USER_START;
+ break;
+ case PAM_CLOSE_SESSION:
+ message = "session_close";
+ type = AUDIT_USER_END;
+ break;
+ case PAM_ACCOUNT:
+ message = "accounting";
+ type = AUDIT_USER_ACCT;
+ break;
+ case PAM_CHAUTHTOK:
+ message = "chauthtok";
+ type = AUDIT_USER_CHAUTHTOK;
+ break;
+ case PAM_SETCRED:
+ message = "setcred";
+ if (flags & PAM_ESTABLISH_CRED)
+ type = AUDIT_CRED_ACQ;
+ else if ((flags & PAM_REINITIALIZE_CRED) || (flags & PAM_REFRESH_CRED))
+ type = AUDIT_CRED_REFR;
+ else if (flags & PAM_DELETE_CRED)
+ type = AUDIT_CRED_DISP;
+ else
+ type = AUDIT_USER_ERR;
+ break;
+ case _PAM_ACTION_DONE:
+ message = "bad_ident";
+ type = AUDIT_USER_ERR;
+ break;
+ default:
+ message = "UNKNOWN";
+ type = AUDIT_USER_ERR;
+ pam_syslog(pamh, LOG_CRIT, "_pam_auditlog() should never get here");
+ retval = PAM_SYSTEM_ERR;
+ }
+
+ if (_pam_list_grantors(h, retval, &grantors) < 0) {
+ /* allocation failure */
+ pam_syslog(pamh, LOG_CRIT, "_pam_list_grantors() failed: %m");
+ retval = PAM_SYSTEM_ERR;
+ }
+
+ if (_pam_audit_writelog(pamh, audit_fd, type, message,
+ grantors ? grantors : "?", retval) < 0)
+ retval = PAM_SYSTEM_ERR;
+
+ free(grantors);
+
+ audit_close(audit_fd);
+ return retval;
+}
+
+int
+_pam_audit_end(pam_handle_t *pamh, int status UNUSED)
+{
+ if (! (pamh->audit_state & PAMAUDIT_LOGGED)) {
+ /* PAM library is being shut down without any of the auditted
+ * stacks having been run. Assume that this is sshd faking
+ * things for an unknown user.
+ */
+ _pam_auditlog(pamh, _PAM_ACTION_DONE, PAM_USER_UNKNOWN, 0, NULL);
+ }
+
+ return 0;
+}
+
+int
+pam_modutil_audit_write(pam_handle_t *pamh, int type,
+ const char *message, int retval)
+{
+ int audit_fd;
+ int rc;
+
+ if ((audit_fd=_pam_audit_open(pamh)) == -1) {
+ return PAM_SYSTEM_ERR;
+ } else if (audit_fd == -2) {
+ return retval;
+ }
+
+ rc = _pam_audit_writelog(pamh, audit_fd, type, message, NULL, retval);
+
+ audit_close(audit_fd);
+
+ return rc < 0 ? PAM_SYSTEM_ERR : PAM_SUCCESS;
+}
+
+#else
+int pam_modutil_audit_write(pam_handle_t *pamh UNUSED, int type UNUSED,
+ const char *message UNUSED, int retval UNUSED)
+{
+ return PAM_SUCCESS;
+}
+#endif /* HAVE_LIBAUDIT */
diff --git a/libpam/pam_auth.c b/libpam/pam_auth.c
new file mode 100644
index 0000000..1e7bc6e
--- /dev/null
+++ b/libpam/pam_auth.c
@@ -0,0 +1,73 @@
+/*
+ * pam_auth.c -- PAM authentication
+ *
+ * $Id$
+ *
+ */
+
+#include "pam_private.h"
+#include "pam_prelude.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int pam_authenticate(pam_handle_t *pamh, int flags)
+{
+ int retval;
+
+ D(("pam_authenticate called"));
+
+ IF_NO_PAMH("pam_authenticate", pamh, PAM_SYSTEM_ERR);
+
+ if (__PAM_FROM_MODULE(pamh)) {
+ D(("called from module!?"));
+ return PAM_SYSTEM_ERR;
+ }
+
+ if (pamh->former.choice == PAM_NOT_STACKED) {
+ _pam_sanitize(pamh);
+ _pam_start_timer(pamh); /* we try to make the time for a failure
+ independent of the time it takes to
+ fail */
+ }
+
+ retval = _pam_dispatch(pamh, flags, PAM_AUTHENTICATE);
+
+ if (retval != PAM_INCOMPLETE) {
+ _pam_sanitize(pamh);
+ _pam_await_timer(pamh, retval); /* if unsuccessful then wait now */
+ D(("pam_authenticate exit"));
+ } else {
+ D(("will resume when ready"));
+ }
+
+#ifdef PRELUDE
+ prelude_send_alert(pamh, retval);
+#endif
+
+ return retval;
+}
+
+int pam_setcred(pam_handle_t *pamh, int flags)
+{
+ int retval;
+
+ D(("pam_setcred called"));
+
+ IF_NO_PAMH("pam_setcred", pamh, PAM_SYSTEM_ERR);
+
+ if (__PAM_FROM_MODULE(pamh)) {
+ D(("called from module!?"));
+ return PAM_SYSTEM_ERR;
+ }
+
+ if (! flags) {
+ flags = PAM_ESTABLISH_CRED;
+ }
+
+ retval = _pam_dispatch(pamh, flags, PAM_SETCRED);
+
+ D(("pam_setcred exit"));
+
+ return retval;
+}
diff --git a/libpam/pam_data.c b/libpam/pam_data.c
new file mode 100644
index 0000000..30570af
--- /dev/null
+++ b/libpam/pam_data.c
@@ -0,0 +1,166 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "pam_private.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+static struct pam_data *_pam_locate_data(const pam_handle_t *pamh,
+ const char *name)
+{
+ struct pam_data *data;
+
+ D(("called"));
+
+ IF_NO_PAMH("_pam_locate_data", pamh, NULL);
+
+ data = pamh->data;
+
+ while (data) {
+ if (!strcmp(data->name, name)) {
+ return data;
+ }
+ data = data->next;
+ }
+
+ return NULL;
+}
+
+int pam_set_data(
+ pam_handle_t *pamh,
+ const char *module_data_name,
+ void *data,
+ void (*cleanup)(pam_handle_t *pamh, void *data, int error_status))
+{
+ struct pam_data *data_entry;
+
+ D(("called"));
+
+ IF_NO_PAMH("pam_set_data", pamh, PAM_SYSTEM_ERR);
+
+ if (__PAM_FROM_APP(pamh)) {
+ D(("called from application!?"));
+ return PAM_SYSTEM_ERR;
+ }
+
+ /* module_data_name should not be NULL */
+ if (module_data_name == NULL) {
+ D(("called with NULL as module_data_name"));
+ return PAM_SYSTEM_ERR;
+ }
+
+ /* first check if there is some data already. If so clean it up */
+
+ if ((data_entry = _pam_locate_data(pamh, module_data_name))) {
+ if (data_entry->cleanup) {
+ data_entry->cleanup(pamh, data_entry->data,
+ PAM_DATA_REPLACE | PAM_SUCCESS );
+ }
+ } else if ((data_entry = malloc(sizeof(*data_entry)))) {
+ char *tname;
+
+ if ((tname = _pam_strdup(module_data_name)) == NULL) {
+ pam_syslog(pamh, LOG_CRIT,
+ "pam_set_data: no memory for data name");
+ _pam_drop(data_entry);
+ return PAM_BUF_ERR;
+ }
+ data_entry->next = pamh->data;
+ pamh->data = data_entry;
+ data_entry->name = tname;
+ } else {
+ pam_syslog(pamh, LOG_CRIT,
+ "pam_set_data: cannot allocate data entry");
+ return PAM_BUF_ERR;
+ }
+
+ data_entry->data = data; /* note this could be NULL */
+ data_entry->cleanup = cleanup;
+
+ return PAM_SUCCESS;
+}
+
+int pam_get_data(
+ const pam_handle_t *pamh,
+ const char *module_data_name,
+ const void **datap)
+{
+ struct pam_data *data;
+
+ D(("called"));
+
+ IF_NO_PAMH("pam_get_data", pamh, PAM_SYSTEM_ERR);
+
+ if (__PAM_FROM_APP(pamh)) {
+ D(("called from application!?"));
+ return PAM_SYSTEM_ERR;
+ }
+
+ /* module_data_name should not be NULL */
+ if (module_data_name == NULL) {
+ D(("called with NULL as module_data_name"));
+ return PAM_SYSTEM_ERR;
+ }
+
+ data = _pam_locate_data(pamh, module_data_name);
+ if (data) {
+ *datap = data->data;
+ return PAM_SUCCESS;
+ }
+
+ return PAM_NO_MODULE_DATA;
+}
+
+void _pam_free_data(pam_handle_t *pamh, int status)
+{
+ struct pam_data *last;
+ struct pam_data *data;
+
+ D(("called"));
+
+ IF_NO_PAMH("_pam_free_data", pamh, /* no return value for void fn */);
+ data = pamh->data;
+
+ while (data) {
+ last = data;
+ data = data->next;
+ if (last->cleanup) {
+ last->cleanup(pamh, last->data, status);
+ }
+ _pam_drop(last->name);
+ _pam_drop(last);
+ }
+}
diff --git a/libpam/pam_delay.c b/libpam/pam_delay.c
new file mode 100644
index 0000000..549da89
--- /dev/null
+++ b/libpam/pam_delay.c
@@ -0,0 +1,160 @@
+/*
+ * pam_delay.c
+ *
+ * Copyright (c) Andrew G. Morgan <morgan@kernel.org> 1996-9
+ * All rights reserved.
+ *
+ * $Id$
+ *
+ */
+
+/*
+ * This is a simple implementation of a delay on failure mechanism; an
+ * attempt to overcome authentication-time attacks in a simple manner.
+ */
+
+#include "pam_private.h"
+#include <unistd.h>
+#include <time.h>
+
+/* **********************************************************************
+ * initialize the time as unset, this is set on the return from the
+ * authenticating pair of of the libpam pam_XXX calls.
+ */
+
+void _pam_reset_timer(pam_handle_t *pamh)
+{
+ D(("setting pamh->fail_delay.set to FALSE"));
+ pamh->fail_delay.set = PAM_FALSE;
+}
+
+/* **********************************************************************
+ * this function sets the start time for possible delayed failing.
+ *
+ * Eventually, it may set the timer so libpam knows how long the program
+ * has already been executing. Currently, this value is used to seed
+ * a pseudo-random number generator...
+ */
+
+void _pam_start_timer(pam_handle_t *pamh)
+{
+ pamh->fail_delay.begin = time(NULL);
+ D(("starting timer..."));
+}
+
+/* *******************************************************************
+ * Compute a pseudo random time. The value is base*(1 +/- 1/5) where
+ * the distribution is pseudo gaussian (the sum of three evenly
+ * distributed random numbers -- central limit theorem and all ;^) The
+ * linear random numbers are based on a formulae given in Knuth's
+ * Seminumerical recipes that was reproduced in `Numerical Recipes
+ * in C'. It is *not* a cryptographically strong generator, but it is
+ * probably "good enough" for our purposes here.
+ *
+ * /dev/random might be a better place to look for some numbers...
+ */
+
+static unsigned int _pam_rand(unsigned int seed)
+{
+#define N1 1664525
+#define N2 1013904223
+ return N1*seed + N2;
+}
+
+static unsigned int _pam_compute_delay(unsigned int seed, unsigned int base)
+{
+ int i;
+ double sum;
+ unsigned int ans;
+
+ for (sum=i=0; i<3; ++i) {
+ seed = _pam_rand(seed);
+ sum += (double) ((seed / 10) % 1000000);
+ }
+ sum = (sum/3.)/1e6 - .5; /* rescale */
+ ans = (unsigned int) ( base*(1.+sum) );
+ D(("random number: base=%u -> ans=%u\n", base, ans));
+
+ return ans;
+}
+
+/* **********************************************************************
+ * By default, the following function sleeps for a random time. The
+ * actual time slept is computed above. It is based on the requested
+ * time but will differ by up to +/- 50%. If the PAM_FAIL_DELAY item is
+ * set by the client, this function will call the function referenced by
+ * that item, overriding the default behavior.
+ */
+
+void _pam_await_timer(pam_handle_t *pamh, int status)
+{
+ unsigned int delay;
+ D(("waiting?..."));
+
+ delay = _pam_compute_delay(pamh->fail_delay.begin,
+ pamh->fail_delay.delay);
+ if (pamh->fail_delay.delay_fn_ptr) {
+ union {
+ const void *value;
+ void (*fn)(int, unsigned, void *);
+ } hack_fn_u;
+ void *appdata_ptr;
+
+ if (pamh->pam_conversation) {
+ appdata_ptr = pamh->pam_conversation->appdata_ptr;
+ } else {
+ appdata_ptr = NULL;
+ }
+
+ /* always call the applications delay function, even if
+ the delay is zero - indicate status */
+ hack_fn_u.value = pamh->fail_delay.delay_fn_ptr;
+ hack_fn_u.fn(status, delay, appdata_ptr);
+
+ } else if (status != PAM_SUCCESS && pamh->fail_delay.set) {
+
+ D(("will wait %u usec", delay));
+
+ if (delay > 0) {
+ struct timeval tval;
+
+ tval.tv_sec = delay / 1000000;
+ tval.tv_usec = delay % 1000000;
+ select(0, NULL, NULL, NULL, &tval);
+ }
+ }
+
+ _pam_reset_timer(pamh);
+ D(("waiting done"));
+}
+
+/* **********************************************************************
+ * this function is known to both the module and the application, it
+ * keeps a running score of the largest-requested delay so far, as
+ * specified by either modules or an application.
+ */
+
+int pam_fail_delay(pam_handle_t *pamh, unsigned int usec)
+{
+ unsigned int largest;
+
+ IF_NO_PAMH("pam_fail_delay", pamh, PAM_SYSTEM_ERR);
+
+ D(("setting delay to %u",usec));
+
+ if (pamh->fail_delay.set) {
+ largest = pamh->fail_delay.delay;
+ } else {
+ pamh->fail_delay.set = PAM_TRUE;
+ largest = 0;
+ }
+
+ D(("largest = %u",largest));
+
+ if (largest < usec) {
+ D(("resetting largest delay"));
+ pamh->fail_delay.delay = usec;
+ }
+
+ return PAM_SUCCESS;
+}
diff --git a/libpam/pam_dispatch.c b/libpam/pam_dispatch.c
new file mode 100644
index 0000000..974104a
--- /dev/null
+++ b/libpam/pam_dispatch.c
@@ -0,0 +1,447 @@
+/* pam_dispatch.c - handles module function dispatch */
+
+/*
+ * Copyright (c) 1998, 2005 Andrew G. Morgan <morgan@kernel.org>
+ *
+ */
+
+#include "pam_private.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * this is the return code we return when a function pointer is NULL
+ * or, the handler structure indicates a broken module config line
+ */
+#define PAM_MUST_FAIL_CODE PAM_PERM_DENIED
+
+/* impression codes - this gives some sense to the logical choices */
+#define _PAM_UNDEF 0
+#define _PAM_POSITIVE +1
+#define _PAM_NEGATIVE -1
+
+/* frozen chain required codes */
+#define _PAM_PLEASE_FREEZE 0
+#define _PAM_MAY_BE_FROZEN 1
+#define _PAM_MUST_BE_FROZEN 2
+
+/*
+ * walk a stack of modules. Interpret the administrator's instructions
+ * when combining the return code of each module.
+ */
+
+static int _pam_dispatch_aux(pam_handle_t *pamh, int flags, struct handler *h,
+ _pam_boolean resumed, int use_cached_chain)
+{
+ int depth, impression, status, skip_depth, prev_level, stack_level;
+ struct _pam_substack_state *substates = NULL;
+
+ IF_NO_PAMH("_pam_dispatch_aux", pamh, PAM_SYSTEM_ERR);
+
+ if (h == NULL) {
+ const void *service=NULL;
+
+ (void) pam_get_item(pamh, PAM_SERVICE, &service);
+ pam_syslog(pamh, LOG_ERR, "no modules loaded for `%s' service",
+ service ? (const char *)service:"<unknown>" );
+ service = NULL;
+ return PAM_MUST_FAIL_CODE;
+ }
+
+ /* if we are recalling this module stack because a former call did
+ not complete, we restore the state of play from pamh. */
+ if (resumed) {
+ skip_depth = pamh->former.depth;
+ status = pamh->former.status;
+ impression = pamh->former.impression;
+ substates = pamh->former.substates;
+ /* forget all that */
+ pamh->former.impression = _PAM_UNDEF;
+ pamh->former.status = PAM_MUST_FAIL_CODE;
+ pamh->former.depth = 0;
+ pamh->former.substates = NULL;
+ } else {
+ skip_depth = 0;
+ substates = malloc(PAM_SUBSTACK_MAX_LEVEL * sizeof(*substates));
+ if (substates == NULL) {
+ pam_syslog(pamh, LOG_CRIT,
+ "_pam_dispatch_aux: no memory for substack states");
+ return PAM_BUF_ERR;
+ }
+ substates[0].impression = impression = _PAM_UNDEF;
+ substates[0].status = status = PAM_MUST_FAIL_CODE;
+ }
+
+ prev_level = 0;
+
+ /* Loop through module logic stack */
+ for (depth=0 ; h != NULL ; prev_level = stack_level, h = h->next, ++depth) {
+ int retval, cached_retval, action;
+
+ stack_level = h->stack_level;
+
+ /* skip leading modules if they have already returned */
+ if (depth < skip_depth) {
+ continue;
+ }
+
+ /* remember state if we are entering a substack */
+ if (prev_level < stack_level) {
+ substates[stack_level].impression = impression;
+ substates[stack_level].status = status;
+ }
+
+ /* attempt to call the module */
+ if (h->handler_type == PAM_HT_MUST_FAIL) {
+ D(("module poorly listed in PAM config; forcing failure"));
+ retval = PAM_MUST_FAIL_CODE;
+ } else if (h->handler_type == PAM_HT_SUBSTACK) {
+ D(("skipping substack handler"));
+ continue;
+ } else if (h->func == NULL) {
+ D(("module function is not defined, indicating failure"));
+ retval = PAM_MODULE_UNKNOWN;
+ } else {
+ D(("passing control to module..."));
+ pamh->mod_name=h->mod_name;
+ pamh->mod_argc = h->argc;
+ pamh->mod_argv = h->argv;
+ retval = h->func(pamh, flags, h->argc, h->argv);
+ pamh->mod_name=NULL;
+ pamh->mod_argc = 0;
+ pamh->mod_argv = NULL;
+ D(("module returned: %s", pam_strerror(pamh, retval)));
+ }
+
+ /*
+ * PAM_INCOMPLETE return is special. It indicates that the
+ * module wants to wait for the application before continuing.
+ * In order to return this, the module will have saved its
+ * state so it can resume from an equivalent position when it
+ * is called next time. (This was added as of 0.65)
+ */
+ if (retval == PAM_INCOMPLETE) {
+ pamh->former.impression = impression;
+ pamh->former.status = status;
+ pamh->former.depth = depth;
+ pamh->former.substates = substates;
+
+ D(("module %d returned PAM_INCOMPLETE", depth));
+ return retval;
+ }
+
+ /*
+ * use_cached_chain is how we ensure that the setcred and
+ * close_session modules are called in the same order as they did
+ * when they were invoked as auth/open_session. This feature was
+ * added in 0.75 to make the behavior of pam_setcred sane.
+ */
+ if (use_cached_chain != _PAM_PLEASE_FREEZE) {
+
+ /* a former stack execution should have frozen the chain */
+
+ cached_retval = *(h->cached_retval_p);
+ if (cached_retval == _PAM_INVALID_RETVAL) {
+
+ /* This may be a problem condition. It implies that
+ the application is running setcred, close_session,
+ chauthtok(2nd) without having first run
+ authenticate, open_session, chauthtok(1st)
+ [respectively]. */
+
+ D(("use_cached_chain is set to [%d],"
+ " but cached_retval == _PAM_INVALID_RETVAL",
+ use_cached_chain));
+
+ /* In the case of close_session and setcred there is a
+ backward compatibility reason for allowing this, in
+ the chauthtok case we have encountered a bug in
+ libpam! */
+
+ if (use_cached_chain == _PAM_MAY_BE_FROZEN) {
+ /* (not ideal) force non-frozen stack control. */
+ cached_retval = retval;
+ } else {
+ D(("BUG in libpam -"
+ " chain is required to be frozen but isn't"));
+
+ /* cached_retval is already _PAM_INVALID_RETVAL */
+ }
+ }
+ } else {
+ /* this stack execution is defining the frozen chain */
+ cached_retval = h->cached_retval = retval;
+ }
+
+ /* verify that the return value is a valid one */
+ if ((cached_retval < PAM_SUCCESS)
+ || (cached_retval >= _PAM_RETURN_VALUES)) {
+
+ retval = PAM_MUST_FAIL_CODE;
+ action = _PAM_ACTION_BAD;
+ } else {
+ /* We treat the current retval with some respect. It may
+ (for example, in the case of setcred) have a value that
+ needs to be propagated to the user. We want to use the
+ cached_retval to determine the modules to be executed
+ in the stacked chain, but we want to treat each
+ non-ignored module in the cached chain as now being
+ 'required'. We only need to treat the,
+ _PAM_ACTION_IGNORE, _PAM_ACTION_IS_JUMP and
+ _PAM_ACTION_RESET actions specially. */
+
+ action = h->actions[cached_retval];
+ }
+
+ D(("use_cached_chain=%d action=%d cached_retval=%d retval=%d",
+ use_cached_chain, action, cached_retval, retval));
+
+ /* decide what to do */
+ switch (action) {
+ case _PAM_ACTION_RESET:
+
+ impression = substates[stack_level].impression;
+ status = substates[stack_level].status;
+ break;
+
+ case _PAM_ACTION_OK:
+ case _PAM_ACTION_DONE:
+
+ if ( impression == _PAM_UNDEF
+ || (impression == _PAM_POSITIVE && status == PAM_SUCCESS) ) {
+ /* in case of using cached chain
+ we could get here with PAM_IGNORE - don't return it */
+ if ( retval != PAM_IGNORE || cached_retval == retval ) {
+ impression = _PAM_POSITIVE;
+ status = retval;
+ }
+ }
+ if ( impression == _PAM_POSITIVE ) {
+ if ( retval == PAM_SUCCESS ) {
+ h->grantor = 1;
+ }
+
+ if ( action == _PAM_ACTION_DONE ) {
+ goto decision_made;
+ }
+ }
+ break;
+
+ case _PAM_ACTION_BAD:
+ case _PAM_ACTION_DIE:
+#ifdef PAM_FAIL_NOW_ON
+ if ( cached_retval == PAM_ABORT ) {
+ impression = _PAM_NEGATIVE;
+ status = PAM_PERM_DENIED;
+ goto decision_made;
+ }
+#endif /* PAM_FAIL_NOW_ON */
+ if ( impression != _PAM_NEGATIVE ) {
+ impression = _PAM_NEGATIVE;
+ /* Don't return with PAM_IGNORE as status */
+ if ( retval == PAM_IGNORE )
+ status = PAM_MUST_FAIL_CODE;
+ else
+ status = retval;
+ }
+ if ( action == _PAM_ACTION_DIE ) {
+ goto decision_made;
+ }
+ break;
+
+ case _PAM_ACTION_IGNORE:
+ break;
+
+ /* if we get here, we expect action is a positive number --
+ this is what the ...JUMP macro checks. */
+
+ default:
+ if ( _PAM_ACTION_IS_JUMP(action) ) {
+
+ /* If we are evaluating a cached chain, we treat this
+ module as required (aka _PAM_ACTION_OK) as well as
+ executing the jump. */
+
+ if (use_cached_chain) {
+ if (impression == _PAM_UNDEF
+ || (impression == _PAM_POSITIVE
+ && status == PAM_SUCCESS) ) {
+ if ( retval != PAM_IGNORE || cached_retval == retval ) {
+ if ( impression == _PAM_UNDEF && retval == PAM_SUCCESS ) {
+ h->grantor = 1;
+ }
+ impression = _PAM_POSITIVE;
+ status = retval;
+ }
+ }
+ }
+
+ /* this means that we need to skip #action stacked modules */
+ while (h->next != NULL && h->next->stack_level >= stack_level && action > 0) {
+ do {
+ h = h->next;
+ ++depth;
+ } while (h->next != NULL && h->next->stack_level > stack_level);
+ --action;
+ }
+
+ /* note if we try to skip too many modules action is
+ still non-zero and we snag the next if. */
+ }
+
+ /* this case is a syntax error: we can't succeed */
+ if (action) {
+ pam_syslog(pamh, LOG_ERR, "bad jump in stack");
+ impression = _PAM_NEGATIVE;
+ status = PAM_MUST_FAIL_CODE;
+ }
+ }
+ continue;
+
+decision_made: /* by getting here we have made a decision */
+ while (h->next != NULL && h->next->stack_level >= stack_level) {
+ h = h->next;
+ ++depth;
+ }
+ }
+
+ /* Sanity check */
+ if ( status == PAM_SUCCESS && impression != _PAM_POSITIVE ) {
+ D(("caught on sanity check -- this is probably a config error!"));
+ status = PAM_MUST_FAIL_CODE;
+ }
+
+ free(substates);
+ /* We have made a decision about the modules executed */
+ return status;
+}
+
+static void _pam_clear_grantors(struct handler *h)
+{
+ for (; h != NULL; h = h->next) {
+ h->grantor = 0;
+ }
+}
+
+/*
+ * This function translates the module dispatch request into a pointer
+ * to the stack of modules that will actually be run. the
+ * _pam_dispatch_aux() function (above) is responsible for walking the
+ * module stack.
+ */
+
+int _pam_dispatch(pam_handle_t *pamh, int flags, int choice)
+{
+ struct handler *h = NULL;
+ int retval = PAM_SYSTEM_ERR, use_cached_chain;
+ _pam_boolean resumed;
+
+ IF_NO_PAMH("_pam_dispatch", pamh, PAM_SYSTEM_ERR);
+
+ if (__PAM_FROM_MODULE(pamh)) {
+ D(("called from a module!?"));
+ goto end;
+ }
+
+ /* Load all modules, resolve all symbols */
+
+ if ((retval = _pam_init_handlers(pamh)) != PAM_SUCCESS) {
+ pam_syslog(pamh, LOG_ERR, "unable to dispatch function");
+ goto end;
+ }
+
+ use_cached_chain = _PAM_PLEASE_FREEZE;
+
+ switch (choice) {
+ case PAM_AUTHENTICATE:
+ h = pamh->handlers.conf.authenticate;
+ break;
+ case PAM_SETCRED:
+ h = pamh->handlers.conf.setcred;
+ use_cached_chain = _PAM_MAY_BE_FROZEN;
+ break;
+ case PAM_ACCOUNT:
+ h = pamh->handlers.conf.acct_mgmt;
+ break;
+ case PAM_OPEN_SESSION:
+ h = pamh->handlers.conf.open_session;
+ break;
+ case PAM_CLOSE_SESSION:
+ h = pamh->handlers.conf.close_session;
+ use_cached_chain = _PAM_MAY_BE_FROZEN;
+ break;
+ case PAM_CHAUTHTOK:
+ h = pamh->handlers.conf.chauthtok;
+ break;
+ default:
+ pam_syslog(pamh, LOG_ERR, "undefined fn choice; %d", choice);
+ retval = PAM_ABORT;
+ goto end;
+ }
+
+ if (h == NULL) { /* there was no handlers.conf... entry; will use
+ * handlers.other... */
+ switch (choice) {
+ case PAM_AUTHENTICATE:
+ h = pamh->handlers.other.authenticate;
+ break;
+ case PAM_SETCRED:
+ h = pamh->handlers.other.setcred;
+ break;
+ case PAM_ACCOUNT:
+ h = pamh->handlers.other.acct_mgmt;
+ break;
+ case PAM_OPEN_SESSION:
+ h = pamh->handlers.other.open_session;
+ break;
+ case PAM_CLOSE_SESSION:
+ h = pamh->handlers.other.close_session;
+ break;
+ case PAM_CHAUTHTOK:
+ h = pamh->handlers.other.chauthtok;
+ break;
+ }
+ }
+
+ /* Did a module return an "incomplete state" last time? */
+ if (pamh->former.choice != PAM_NOT_STACKED) {
+ if (pamh->former.choice != choice) {
+ pam_syslog(pamh, LOG_ERR,
+ "application failed to re-exec stack [%d:%d]",
+ pamh->former.choice, choice);
+ retval = PAM_ABORT;
+ goto end;
+ }
+ resumed = PAM_TRUE;
+ } else {
+ resumed = PAM_FALSE;
+ _pam_clear_grantors(h);
+ }
+
+ __PAM_TO_MODULE(pamh);
+
+ /* call the list of module functions */
+ pamh->choice = choice;
+ retval = _pam_dispatch_aux(pamh, flags, h, resumed, use_cached_chain);
+
+ __PAM_TO_APP(pamh);
+
+ /* Should we recall where to resume next time? */
+ if (retval == PAM_INCOMPLETE) {
+ D(("module [%d] returned PAM_INCOMPLETE"));
+ pamh->former.choice = choice;
+ } else {
+ pamh->former.choice = PAM_NOT_STACKED;
+ }
+
+end:
+
+#ifdef HAVE_LIBAUDIT
+ if (choice != PAM_CHAUTHTOK || flags & PAM_UPDATE_AUTHTOK || retval != PAM_SUCCESS) {
+ retval = _pam_auditlog(pamh, choice, retval, flags, h);
+ }
+#endif
+
+ return retval;
+}
diff --git a/libpam/pam_dynamic.c b/libpam/pam_dynamic.c
new file mode 100644
index 0000000..c063083
--- /dev/null
+++ b/libpam/pam_dynamic.c
@@ -0,0 +1,138 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pam_private.h"
+
+#ifdef PAM_SHL
+# include <dl.h>
+#elif defined(PAM_DYLD)
+# include <mach-o/dyld.h>
+#else /* PAM_SHL */
+# include <dlfcn.h>
+#endif /* PAM_SHL */
+
+#ifndef SHLIB_SYM_PREFIX
+#define SHLIB_SYM_PREFIX "_"
+#endif
+
+void *_pam_dlopen(const char *mod_path)
+{
+#ifdef PAM_SHL
+ return shl_load(mod_path, BIND_IMMEDIATE, 0L);
+#elif defined(PAM_DYLD)
+ NSObjectFileImage ofile;
+ void *ret = NULL;
+
+ if (NSCreateObjectFileImageFromFile(mod_path, &ofile) !=
+ NSObjectFileImageSuccess )
+ return NULL;
+
+ ret = NSLinkModule(ofile, mod_path, NSLINKMODULE_OPTION_PRIVATE | NSLINKMODULE_OPTION_BINDNOW);
+ NSDestroyObjectFileImage(ofile);
+
+ return ret;
+#else
+ return dlopen(mod_path, RTLD_NOW);
+#endif
+}
+
+servicefn _pam_dlsym(void *handle, const char *symbol)
+{
+#ifdef PAM_SHL
+ char *_symbol = NULL;
+ servicefn ret;
+
+ if( symbol == NULL )
+ return NULL;
+
+ if( shl_findsym(&handle, symbol, (short) TYPE_PROCEDURE, &ret ){
+ _symbol = malloc( strlen(symbol) + sizeof(SHLIB_SYM_PREFIX) + 1 );
+ if( _symbol == NULL )
+ return NULL;
+ strcpy(_symbol, SHLIB_SYM_PREFIX);
+ strcat(_symbol, symbol);
+ if( shl_findsym(&handle, _symbol,
+ (short) TYPE_PROCEDURE, &ret ){
+ free(_symbol);
+ return NULL;
+ }
+ free(_symbol);
+ }
+
+ return ret;
+
+#elif defined(PAM_DYLD)
+ NSSymbol nsSymbol;
+ char *_symbol;
+
+ if( symbol == NULL )
+ return NULL;
+ _symbol = malloc( strlen(symbol) + 2 );
+ if( _symbol == NULL )
+ return NULL;
+ strcpy(_symbol, SHLIB_SYM_PREFIX);
+ strcat(_symbol, symbol);
+
+ nsSymbol = NSLookupSymbolInModule(handle, _symbol);
+ if( nsSymbol == NULL )
+ return NULL;
+ free(_symbol);
+
+ return (servicefn)NSAddressOfSymbol(nsSymbol);
+#else
+ return (servicefn) dlsym(handle, symbol);
+#endif
+}
+
+void _pam_dlclose(void *handle)
+{
+#ifdef PAM_SHL
+ shl_unload(handle);
+#elif defined(PAM_DYLD)
+ NSUnLinkModule((NSModule)handle, NSUNLINKMODULE_OPTION_NONE);
+#else
+ dlclose(handle);
+#endif
+
+ return;
+}
+
+const char *
+_pam_dlerror (void)
+{
+#if defined(PAM_SHL) || defined(PAM_DYLD)
+ return "unknown";
+#else
+ return dlerror ();
+#endif
+}
diff --git a/libpam/pam_end.c b/libpam/pam_end.c
new file mode 100644
index 0000000..406b147
--- /dev/null
+++ b/libpam/pam_end.c
@@ -0,0 +1,98 @@
+/* pam_end.c */
+
+/*
+ * $Id$
+ */
+
+#include "pam_private.h"
+
+#include <stdlib.h>
+
+int pam_end(pam_handle_t *pamh, int pam_status)
+{
+ int ret;
+
+ D(("entering pam_end()"));
+
+ IF_NO_PAMH("pam_end", pamh, PAM_SYSTEM_ERR);
+
+ if (__PAM_FROM_MODULE(pamh)) {
+ D(("called from module!?"));
+ return PAM_SYSTEM_ERR;
+ }
+
+#ifdef HAVE_LIBAUDIT
+ _pam_audit_end(pamh, pam_status);
+#endif
+
+ /* first liberate the modules (it is not inconcevible that the
+ modules may need to use the service_name etc. to clean up) */
+
+ _pam_free_data(pamh, pam_status);
+
+ /* now drop all modules */
+
+ if ((ret = _pam_free_handlers(pamh)) != PAM_SUCCESS) {
+ return ret; /* error occurred */
+ }
+
+ /* from this point we cannot call the modules any more. Free the remaining
+ memory used by the Linux-PAM interface */
+
+ _pam_drop_env(pamh); /* purge the environment */
+
+ _pam_overwrite(pamh->authtok); /* blank out old token */
+ _pam_drop(pamh->authtok);
+
+ _pam_overwrite(pamh->oldauthtok); /* blank out old token */
+ _pam_drop(pamh->oldauthtok);
+
+ _pam_overwrite(pamh->former.prompt);
+ _pam_drop(pamh->former.prompt); /* drop saved prompt */
+
+ _pam_overwrite(pamh->service_name);
+ _pam_drop(pamh->service_name);
+
+ _pam_overwrite(pamh->user);
+ _pam_drop(pamh->user);
+
+ _pam_overwrite(pamh->confdir);
+ _pam_drop(pamh->confdir);
+
+ _pam_overwrite(pamh->prompt);
+ _pam_drop(pamh->prompt); /* prompt for pam_get_user() */
+
+ _pam_overwrite(pamh->tty);
+ _pam_drop(pamh->tty);
+
+ _pam_overwrite(pamh->rhost);
+ _pam_drop(pamh->rhost);
+
+ _pam_overwrite(pamh->ruser);
+ _pam_drop(pamh->ruser);
+
+ _pam_drop(pamh->pam_conversation);
+ pamh->fail_delay.delay_fn_ptr = NULL;
+
+ _pam_drop(pamh->former.substates);
+
+ _pam_overwrite(pamh->xdisplay);
+ _pam_drop(pamh->xdisplay);
+
+ _pam_overwrite(pamh->xauth.name);
+ _pam_drop(pamh->xauth.name);
+ _pam_overwrite_n(pamh->xauth.data, (unsigned int)pamh->xauth.datalen);
+ _pam_drop(pamh->xauth.data);
+ _pam_overwrite_n((char *)&pamh->xauth, sizeof(pamh->xauth));
+
+ _pam_overwrite(pamh->authtok_type);
+ _pam_drop(pamh->authtok_type);
+
+ /* and finally liberate the memory for the pam_handle structure */
+
+ _pam_drop(pamh);
+
+ D(("exiting pam_end() successfully"));
+
+ return PAM_SUCCESS;
+}
diff --git a/libpam/pam_env.c b/libpam/pam_env.c
new file mode 100644
index 0000000..1c8403d
--- /dev/null
+++ b/libpam/pam_env.c
@@ -0,0 +1,392 @@
+/*
+ * pam_env.c
+ *
+ * Copyright (c) Andrew G. Morgan <morgan@parc.power.net> 1996,1997
+ * All rights reserved.
+ *
+ * This file was written from a "hint" provided by the people at SUN.
+ * and the X/Open XSSO draft of March 1997.
+ *
+ * $Id$
+ */
+
+#include "pam_private.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef sunos
+#define memmove(x,y,z) bcopy(y,x,z)
+#endif
+
+/* helper functions */
+
+#ifdef PAM_DEBUG
+static void _pam_dump_env(pam_handle_t *pamh)
+{
+ int i;
+
+ D(("Listing environment of pamh=%p", pamh));
+ D(("pamh->env = %p", pamh->env));
+ D(("environment entries used = %d [of %d allocated]"
+ , pamh->env->requested, pamh->env->entries));
+
+ for (i=0; i<pamh->env->requested; ++i) {
+ _pam_output_debug(">%-3d [%9p]:[%s]"
+ , i, pamh->env->list[i], pamh->env->list[i]);
+ }
+ _pam_output_debug("*NOTE* the last item should be (nil)");
+}
+#else
+#define _pam_dump_env(x)
+#endif
+
+/*
+ * Create the environment
+ */
+
+int _pam_make_env(pam_handle_t *pamh)
+{
+ D(("called."));
+
+ IF_NO_PAMH("_pam_make_env", pamh, PAM_ABORT);
+
+ /*
+ * get structure memory
+ */
+
+ pamh->env = (struct pam_environ *) malloc(sizeof(struct pam_environ));
+ if (pamh->env == NULL) {
+ pam_syslog(pamh, LOG_CRIT, "_pam_make_env: out of memory");
+ return PAM_BUF_ERR;
+ }
+
+ /*
+ * get list memory
+ */
+
+ pamh->env->list = (char **)calloc( PAM_ENV_CHUNK, sizeof(char *) );
+ if (pamh->env->list == NULL) {
+ pam_syslog(pamh, LOG_CRIT, "_pam_make_env: no memory for list");
+ _pam_drop(pamh->env);
+ return PAM_BUF_ERR;
+ }
+
+ /*
+ * fill entries in pamh->env
+ */
+
+ pamh->env->entries = PAM_ENV_CHUNK;
+ pamh->env->requested = 1;
+ pamh->env->list[0] = NULL;
+
+ _pam_dump_env(pamh); /* only active when debugging */
+
+ return PAM_SUCCESS;
+}
+
+/*
+ * purge the environment
+ */
+
+void _pam_drop_env(pam_handle_t *pamh)
+{
+ D(("called."));
+ IF_NO_PAMH("_pam_make_env", pamh, /* nothing to return */);
+
+ if (pamh->env != NULL) {
+ int i;
+ /* we will only purge the pamh->env->requested number of elements */
+
+ for (i=pamh->env->requested-1; i-- > 0; ) {
+ D(("dropping #%3d>%s<", i, pamh->env->list[i]));
+ _pam_overwrite(pamh->env->list[i]); /* clean */
+ _pam_drop(pamh->env->list[i]); /* forget */
+ }
+ pamh->env->requested = 0;
+ pamh->env->entries = 0;
+ _pam_drop(pamh->env->list); /* forget */
+ _pam_drop(pamh->env); /* forget */
+ } else {
+ D(("no environment present in pamh?"));
+ }
+}
+
+/*
+ * Return the item number of the given variable = first 'length' chars
+ * of 'name_value'. Since this is a static function, it is safe to
+ * assume its supplied arguments are well defined.
+ */
+
+static int _pam_search_env(const struct pam_environ *env
+ , const char *name_value, int length)
+{
+ int i;
+
+ for (i=env->requested-1; i-- > 0; ) {
+ if (strncmp(name_value,env->list[i],length) == 0
+ && env->list[i][length] == '=') {
+
+ return i; /* Got it! */
+
+ }
+ }
+
+ return -1; /* no luck */
+}
+
+/*
+ * externally visible functions
+ */
+
+/*
+ * pam_putenv(): Add/replace/delete a PAM-environment variable.
+ *
+ * Add/replace:
+ * name_value = "NAME=VALUE" or "NAME=" (for empty value="\0")
+ *
+ * delete:
+ * name_value = "NAME"
+ */
+
+int pam_putenv(pam_handle_t *pamh, const char *name_value)
+{
+ int l2eq, item, retval;
+
+ D(("called."));
+ IF_NO_PAMH("pam_putenv", pamh, PAM_ABORT);
+
+ if (name_value == NULL) {
+ pam_syslog(pamh, LOG_ERR, "pam_putenv: no variable indicated");
+ return PAM_PERM_DENIED;
+ }
+
+ /*
+ * establish if we are setting or deleting; scan for '='
+ */
+
+ for (l2eq=0; name_value[l2eq] && name_value[l2eq] != '='; ++l2eq);
+ if (l2eq <= 0) {
+ pam_syslog(pamh, LOG_ERR, "pam_putenv: bad variable");
+ return PAM_BAD_ITEM;
+ }
+
+ /*
+ * Look first for environment.
+ */
+
+ if (pamh->env == NULL || pamh->env->list == NULL) {
+ pam_syslog(pamh, LOG_ERR, "pam_putenv: no env%s found",
+ pamh->env == NULL ? "":"-list");
+ return PAM_ABORT;
+ }
+
+ /* find the item to replace */
+
+ item = _pam_search_env(pamh->env, name_value, l2eq);
+
+ if (name_value[l2eq]) { /* (re)setting */
+
+ if (item == -1) { /* new variable */
+ D(("adding item: %s", name_value));
+ /* enough space? */
+ if (pamh->env->entries <= pamh->env->requested) {
+ register int i;
+ register char **tmp;
+
+ /* get some new space */
+ tmp = calloc( pamh->env->entries + PAM_ENV_CHUNK
+ , sizeof(char *) );
+ if (tmp == NULL) {
+ /* nothing has changed - old env intact */
+ pam_syslog(pamh, LOG_CRIT,
+ "pam_putenv: cannot grow environment");
+ return PAM_BUF_ERR;
+ }
+
+ /* copy old env-item pointers/forget old */
+ for (i=0; i<pamh->env->requested; ++i) {
+ tmp[i] = pamh->env->list[i];
+ pamh->env->list[i] = NULL;
+ }
+
+ /* drop old list and replace with new */
+ _pam_drop(pamh->env->list);
+ pamh->env->list = tmp;
+ pamh->env->entries += PAM_ENV_CHUNK;
+
+ D(("resized env list"));
+ _pam_dump_env(pamh); /* only when debugging */
+ }
+
+ item = pamh->env->requested-1; /* old last item (NULL) */
+
+ /* add a new NULL entry at end; increase counter */
+ pamh->env->list[pamh->env->requested++] = NULL;
+
+ } else { /* replace old */
+ D(("replacing item: %s\n with: %s"
+ , pamh->env->list[item], name_value));
+ _pam_overwrite(pamh->env->list[item]);
+ _pam_drop(pamh->env->list[item]);
+ }
+
+ /*
+ * now we have a place to put the new env-item, insert at 'item'
+ */
+
+ pamh->env->list[item] = _pam_strdup(name_value);
+ if (pamh->env->list[item] != NULL) {
+ _pam_dump_env(pamh); /* only when debugging */
+ return PAM_SUCCESS;
+ }
+
+ /* something went wrong; we should delete the item - fall through */
+
+ retval = PAM_BUF_ERR; /* an error occurred */
+ } else {
+ retval = PAM_SUCCESS; /* we requested delete */
+ }
+
+ /* getting to here implies we are deleting an item */
+
+ if (item < 0) {
+ pam_syslog(pamh, LOG_ERR,
+ "pam_putenv: delete non-existent entry; %s", name_value);
+ return PAM_BAD_ITEM;
+ }
+
+ /*
+ * remove item: purge memory; reset counter; resize [; display-env]
+ */
+
+ D(("deleting: env#%3d:[%s]", item, pamh->env->list[item]));
+ _pam_overwrite(pamh->env->list[item]);
+ _pam_drop(pamh->env->list[item]);
+ --(pamh->env->requested);
+ D(("mmove: item[%d]+%d -> item[%d]"
+ , item+1, ( pamh->env->requested - item ), item));
+ (void) memmove(&pamh->env->list[item], &pamh->env->list[item+1]
+ , ( pamh->env->requested - item )*sizeof(char *) );
+
+ _pam_dump_env(pamh); /* only when debugging */
+
+ /*
+ * deleted.
+ */
+
+ return retval;
+}
+
+/*
+ * Return the value of the requested environment variable
+ */
+
+const char *pam_getenv(pam_handle_t *pamh, const char *name)
+{
+ int item;
+
+ D(("called."));
+ IF_NO_PAMH("pam_getenv", pamh, NULL);
+
+ if (name == NULL) {
+ pam_syslog(pamh, LOG_ERR, "pam_getenv: no variable indicated");
+ return NULL;
+ }
+
+ if (pamh->env == NULL || pamh->env->list == NULL) {
+ pam_syslog(pamh, LOG_ERR, "pam_getenv: no env%s found",
+ pamh->env == NULL ? "":"-list" );
+ return NULL;
+ }
+
+ /* find the requested item */
+
+ item = _pam_search_env(pamh->env, name, strlen(name));
+ if (item != -1) {
+
+ D(("env-item: %s, found!", name));
+ return (pamh->env->list[item] + 1 + strlen(name));
+
+ } else {
+
+ D(("env-item: %s, not found", name));
+ return NULL;
+
+ }
+}
+
+static char **_copy_env(pam_handle_t *pamh)
+{
+ char **dump;
+ int i = pamh->env->requested; /* reckon size of environment */
+ char *const *env = pamh->env->list;
+
+ D(("now get some memory for dump"));
+
+ /* allocate some memory for this (plus the null tail-pointer) */
+ dump = (char **) calloc(i, sizeof(char *));
+ D(("dump = %p", dump));
+ if (dump == NULL) {
+ return NULL;
+ }
+
+ /* now run through entries and copy the variables over */
+ dump[--i] = NULL;
+ while (i-- > 0) {
+ D(("env[%d]=`%s'", i,env[i]));
+ dump[i] = _pam_strdup(env[i]);
+ D(("->dump[%d]=`%s'", i,dump[i]));
+ if (dump[i] == NULL) {
+ /* out of memory */
+
+ while (dump[++i]) {
+ _pam_overwrite(dump[i]);
+ _pam_drop(dump[i]);
+ }
+ _pam_drop(dump);
+ return NULL;
+ }
+ }
+
+ env = NULL; /* forget now */
+
+ /* return transcribed environment */
+ return dump;
+}
+
+char **pam_getenvlist(pam_handle_t *pamh)
+{
+ int i;
+
+ D(("called."));
+ IF_NO_PAMH("pam_getenvlist", pamh, NULL);
+
+ if (pamh->env == NULL || pamh->env->list == NULL) {
+ pam_syslog(pamh, LOG_ERR, "pam_getenvlist: no env%s found",
+ pamh->env == NULL ? "":"-list" );
+ return NULL;
+ }
+
+ /* some quick checks */
+
+ if (pamh->env->requested > pamh->env->entries) {
+ pam_syslog(pamh, LOG_ERR, "pam_getenvlist: environment corruption");
+ _pam_dump_env(pamh); /* only active when debugging */
+ return NULL;
+ }
+
+ for (i=pamh->env->requested-1; i-- > 0; ) {
+ if (pamh->env->list[i] == NULL) {
+ pam_syslog(pamh, LOG_ERR, "pam_getenvlist: environment broken");
+ _pam_dump_env(pamh); /* only active when debugging */
+ return NULL; /* somehow we've broken the environment!? */
+ }
+ }
+
+ /* Seems fine; copy environment */
+
+ _pam_dump_env(pamh); /* only active when debugging */
+
+ return _copy_env(pamh);
+}
diff --git a/libpam/pam_get_authtok.c b/libpam/pam_get_authtok.c
new file mode 100644
index 0000000..3fa7f7d
--- /dev/null
+++ b/libpam/pam_get_authtok.c
@@ -0,0 +1,280 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "pam_private.h"
+
+#include <security/pam_ext.h>
+
+#define PROMPT _("Password: ")
+/* For Translators: "%s" is replaced with "<service>". */
+#define PROMPT_CURRENT_ARG _("Current %s password: ")
+#define PROMPT_CURRENT_NOARG _("Current password: ")
+/* For Translators: "%s" is replaced with "<service>". */
+#define PROMPT_NEW_ARG _("New %s password: ")
+#define PROMPT_NEW_NOARG _("New password: ")
+/* For Translators: "%s" is replaced with "<service>". */
+#define PROMPT_RETYPE_ARG _("Retype new %s password: ")
+#define PROMPT_RETYPE_NOARG _("Retype new password: ")
+#define MISTYPED_PASS _("Sorry, passwords do not match.")
+
+#define PAM_GETAUTHTOK_NOVERIFY 1
+
+static const char *
+get_option (pam_handle_t *pamh, const char *option)
+{
+ int i;
+ size_t len;
+
+
+ if (option == NULL || pamh == NULL ||
+ pamh->mod_argc == 0 || pamh->mod_argv == NULL)
+ return NULL;
+
+ len = strlen (option);
+
+ for (i = 0; i < pamh->mod_argc; i++)
+ {
+ if (strncmp (option, pamh->mod_argv[i], len) == 0)
+ {
+ if (pamh->mod_argv[i][len] == '=')
+ return &(pamh->mod_argv[i][len+1]);
+ else if (pamh->mod_argv[i][len] == '\0')
+ return "";
+ }
+ }
+ return NULL;
+}
+
+
+static int
+pam_get_authtok_internal (pam_handle_t *pamh, int item,
+ const char **authtok, const char *prompt,
+ unsigned int flags)
+
+{
+ char *resp[2] = {NULL, NULL};
+ const void *prevauthtok;
+ const char *authtok_type = "";
+ int chpass = 0; /* Password change, ask twice for it */
+ int retval;
+
+ if (authtok == NULL)
+ return PAM_SYSTEM_ERR;
+
+ /* PAM_AUTHTOK in password stack returns new password,
+ which needs to be verified. */
+ if (pamh->choice == PAM_CHAUTHTOK)
+ {
+ if (item == PAM_AUTHTOK)
+ {
+ chpass = 1;
+ if (!(flags & PAM_GETAUTHTOK_NOVERIFY))
+ ++chpass;
+ }
+ authtok_type = get_option (pamh, "authtok_type");
+ if (authtok_type == NULL)
+ {
+ retval = pam_get_item (pamh, PAM_AUTHTOK_TYPE, (const void **)&authtok_type);
+ if (retval != PAM_SUCCESS || authtok_type == NULL)
+ authtok_type = "";
+ }
+ else
+ pam_set_item(pamh, PAM_AUTHTOK_TYPE, authtok_type);
+ }
+
+ retval = pam_get_item (pamh, item, &prevauthtok);
+ if (retval == PAM_SUCCESS && prevauthtok != NULL)
+ {
+ *authtok = prevauthtok;
+ return PAM_SUCCESS;
+ }
+ else if (get_option (pamh, "use_first_pass") ||
+ (chpass && get_option (pamh, "use_authtok")))
+ {
+ if (prevauthtok == NULL)
+ {
+ if (chpass)
+ return PAM_AUTHTOK_ERR;
+ else
+ return PAM_AUTH_ERR;
+ }
+ else
+ return retval;
+ }
+
+ if (prompt != NULL)
+ {
+ retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[0],
+ "%s", prompt);
+ if (retval == PAM_SUCCESS && chpass > 1 && resp[0] != NULL)
+ retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[1],
+ _("Retype %s"), prompt);
+ }
+ else if (chpass)
+ {
+ pamh->authtok_verified = 0;
+
+ retval = *authtok_type ?
+ pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[0],
+ PROMPT_NEW_ARG, authtok_type) :
+ pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[0],
+ "%s", PROMPT_NEW_NOARG);
+ if (retval == PAM_SUCCESS && chpass > 1 && resp[0] != NULL)
+ {
+ retval = *authtok_type ?
+ pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[1],
+ PROMPT_RETYPE_ARG, authtok_type) :
+ pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[1],
+ "%s", PROMPT_RETYPE_NOARG);
+ }
+ }
+ else if (item == PAM_OLDAUTHTOK)
+ {
+ retval = *authtok_type ?
+ pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[0],
+ PROMPT_CURRENT_ARG, authtok_type) :
+ pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[0],
+ "%s", PROMPT_CURRENT_NOARG);
+ }
+ else
+ retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[0], "%s", PROMPT);
+
+ if (retval != PAM_SUCCESS || resp[0] == NULL ||
+ (chpass > 1 && resp[1] == NULL))
+ {
+ /* We want to abort */
+ if (chpass)
+ pam_error (pamh, _("Password change has been aborted."));
+ return PAM_AUTHTOK_ERR;
+ }
+
+ if (chpass > 1 && strcmp (resp[0], resp[1]) != 0)
+ {
+ pam_error (pamh, MISTYPED_PASS);
+ _pam_overwrite (resp[0]);
+ _pam_drop (resp[0]);
+ _pam_overwrite (resp[1]);
+ _pam_drop (resp[1]);
+ return PAM_TRY_AGAIN;
+ }
+
+ _pam_overwrite (resp[1]);
+ _pam_drop (resp[1]);
+
+ retval = pam_set_item (pamh, item, resp[0]);
+ _pam_overwrite (resp[0]);
+ _pam_drop (resp[0]);
+ if (retval != PAM_SUCCESS)
+ return retval;
+
+ if (chpass > 1)
+ pamh->authtok_verified = 1;
+
+ return pam_get_item(pamh, item, (const void **)authtok);
+}
+
+int
+pam_get_authtok (pam_handle_t *pamh, int item, const char **authtok,
+ const char *prompt)
+{
+ return pam_get_authtok_internal (pamh, item, authtok, prompt, 0);
+}
+
+
+int
+pam_get_authtok_noverify (pam_handle_t *pamh, const char **authtok,
+ const char *prompt)
+{
+ return pam_get_authtok_internal (pamh, PAM_AUTHTOK, authtok, prompt,
+ PAM_GETAUTHTOK_NOVERIFY);
+}
+
+int
+pam_get_authtok_verify (pam_handle_t *pamh, const char **authtok,
+ const char *prompt)
+{
+ char *resp = NULL;
+ const char *authtok_type = "";
+ int retval;
+
+ if (authtok == NULL || pamh->choice != PAM_CHAUTHTOK)
+ return PAM_SYSTEM_ERR;
+
+ if (pamh->authtok_verified)
+ return pam_get_item (pamh, PAM_AUTHTOK, (const void **)authtok);
+
+ if (prompt != NULL)
+ {
+ retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp,
+ _("Retype %s"), prompt);
+ }
+ else
+ {
+ retval = pam_get_item (pamh, PAM_AUTHTOK_TYPE, (const void **)&authtok_type);
+ if (retval != PAM_SUCCESS || authtok_type == NULL)
+ authtok_type = "";
+ retval = *authtok_type ?
+ pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp,
+ PROMPT_RETYPE_ARG, authtok_type) :
+ pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp,
+ "%s", PROMPT_RETYPE_NOARG);
+ }
+
+ if (retval != PAM_SUCCESS || resp == NULL)
+ {
+ /* We want to abort the password change */
+ pam_set_item (pamh, PAM_AUTHTOK, NULL);
+ pam_error (pamh, _("Password change has been aborted."));
+ return PAM_AUTHTOK_ERR;
+ }
+
+ if (strcmp (*authtok, resp) != 0)
+ {
+ pam_set_item (pamh, PAM_AUTHTOK, NULL);
+ pam_error (pamh, MISTYPED_PASS);
+ _pam_overwrite (resp);
+ _pam_drop (resp);
+ return PAM_TRY_AGAIN;
+ }
+
+ retval = pam_set_item (pamh, PAM_AUTHTOK, resp);
+ _pam_overwrite (resp);
+ _pam_drop (resp);
+ if (retval != PAM_SUCCESS)
+ return retval;
+
+ pamh->authtok_verified = 1;
+
+ return pam_get_item(pamh, PAM_AUTHTOK, (const void **)authtok);
+}
diff --git a/libpam/pam_handlers.c b/libpam/pam_handlers.c
new file mode 100644
index 0000000..ffa5e4a
--- /dev/null
+++ b/libpam/pam_handlers.c
@@ -0,0 +1,1045 @@
+/* pam_handlers.c -- pam config file parsing and module loading */
+
+/*
+ * created by Marc Ewing.
+ * Currently maintained by Andrew G. Morgan <morgan@kernel.org>
+ *
+ */
+
+#include "pam_private.h"
+#include "pam_inline.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#define BUF_SIZE 1024
+#define MODULE_CHUNK 4
+#define UNKNOWN_MODULE "<*unknown module*>"
+#ifndef _PAM_ISA
+#define _PAM_ISA "."
+#endif
+
+static int _pam_assemble_line(FILE *f, char *buf, int buf_len);
+
+static void _pam_free_handlers_aux(struct handler **hp);
+
+static int _pam_add_handler(pam_handle_t *pamh
+ , int must_fail, int other, int stack_level, int type
+ , int *actions, const char *mod_path
+ , int argc, char **argv, int argvlen);
+
+/* Values for module type */
+
+#define PAM_T_ANY 0
+#define PAM_T_AUTH 1
+#define PAM_T_SESS 2
+#define PAM_T_ACCT 4
+#define PAM_T_PASS 8
+
+static int _pam_load_conf_file(pam_handle_t *pamh, const char *config_name
+ , const char *service /* specific file */
+ , int module_type /* specific type */
+ , int stack_level /* level of substack */
+#ifdef PAM_READ_BOTH_CONFS
+ , int not_other
+#endif /* PAM_READ_BOTH_CONFS */
+ );
+
+static int _pam_parse_conf_file(pam_handle_t *pamh, FILE *f
+ , const char *known_service /* specific file */
+ , int requested_module_type /* specific type */
+ , int stack_level /* level of substack */
+#ifdef PAM_READ_BOTH_CONFS
+ , int not_other
+#endif /* PAM_READ_BOTH_CONFS */
+ )
+{
+ char buf[BUF_SIZE];
+ int x; /* read a line from the FILE *f ? */
+ /*
+ * read a line from the configuration (FILE *) f
+ */
+ while ((x = _pam_assemble_line(f, buf, BUF_SIZE)) > 0) {
+ char *tok, *nexttok=NULL;
+ const char *this_service;
+ const char *mod_path;
+ int module_type, actions[_PAM_RETURN_VALUES];
+ int other; /* set if module is for PAM_DEFAULT_SERVICE */
+ int res; /* module added successfully? */
+ int handler_type = PAM_HT_MODULE; /* regular handler from a module */
+ int argc;
+ char **argv;
+ int argvlen;
+
+ D(("_pam_init_handler: LINE: %s", buf));
+ if (known_service != NULL) {
+ nexttok = buf;
+ /* No service field: all lines are for the known service. */
+ this_service = known_service;
+ } else {
+ this_service = tok = _pam_StrTok(buf, " \n\t", &nexttok);
+ }
+
+#ifdef PAM_READ_BOTH_CONFS
+ if (not_other)
+ other = 0;
+ else
+#endif /* PAM_READ_BOTH_CONFS */
+ other = !strcasecmp(this_service, PAM_DEFAULT_SERVICE);
+
+ /* accept "service name" or PAM_DEFAULT_SERVICE modules */
+ if (!strcasecmp(this_service, pamh->service_name) || other) {
+ int pam_include = 0;
+ int substack = 0;
+
+ /* This is a service we are looking for */
+ D(("_pam_init_handlers: Found PAM config entry for: %s"
+ , this_service));
+
+ tok = _pam_StrTok(NULL, " \n\t", &nexttok);
+ if (tok == NULL) {
+ /* module type does not exist */
+ D(("_pam_init_handlers: empty module type for %s", this_service));
+ pam_syslog(pamh, LOG_ERR,
+ "(%s) empty module type", this_service);
+ module_type = (requested_module_type != PAM_T_ANY) ?
+ requested_module_type : PAM_T_AUTH; /* most sensitive */
+ handler_type = PAM_HT_MUST_FAIL; /* install as normal but fail when dispatched */
+ } else {
+ if (tok[0] == '-') { /* do not log module load errors */
+ handler_type = PAM_HT_SILENT_MODULE;
+ ++tok;
+ }
+ if (!strcasecmp("auth", tok)) {
+ module_type = PAM_T_AUTH;
+ } else if (!strcasecmp("session", tok)) {
+ module_type = PAM_T_SESS;
+ } else if (!strcasecmp("account", tok)) {
+ module_type = PAM_T_ACCT;
+ } else if (!strcasecmp("password", tok)) {
+ module_type = PAM_T_PASS;
+ } else {
+ /* Illegal module type */
+ D(("_pam_init_handlers: bad module type: %s", tok));
+ pam_syslog(pamh, LOG_ERR, "(%s) illegal module type: %s",
+ this_service, tok);
+ module_type = (requested_module_type != PAM_T_ANY) ?
+ requested_module_type : PAM_T_AUTH; /* most sensitive */
+ handler_type = PAM_HT_MUST_FAIL; /* install as normal but fail when dispatched */
+ }
+ }
+ D(("Using %s config entry: %s", handler_type?"BAD ":"", tok));
+ if (requested_module_type != PAM_T_ANY &&
+ module_type != requested_module_type) {
+ D(("Skipping config entry: %s (requested=%d, found=%d)",
+ tok, requested_module_type, module_type));
+ continue;
+ }
+
+ /* reset the actions to .._UNDEF's -- this is so that
+ we can work out which entries are not yet set (for default). */
+ {
+ int i;
+ for (i=0; i<_PAM_RETURN_VALUES;
+ actions[i++] = _PAM_ACTION_UNDEF);
+ }
+ tok = _pam_StrTok(NULL, " \n\t", &nexttok);
+ if (tok == NULL) {
+ /* no module name given */
+ D(("_pam_init_handlers: no control flag supplied"));
+ pam_syslog(pamh, LOG_ERR,
+ "(%s) no control flag supplied", this_service);
+ _pam_set_default_control(actions, _PAM_ACTION_BAD);
+ handler_type = PAM_HT_MUST_FAIL;
+ } else if (!strcasecmp("required", tok)) {
+ D(("*PAM_F_REQUIRED*"));
+ actions[PAM_SUCCESS] = _PAM_ACTION_OK;
+ actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_OK;
+ actions[PAM_IGNORE] = _PAM_ACTION_IGNORE;
+ _pam_set_default_control(actions, _PAM_ACTION_BAD);
+ } else if (!strcasecmp("requisite", tok)) {
+ D(("*PAM_F_REQUISITE*"));
+ actions[PAM_SUCCESS] = _PAM_ACTION_OK;
+ actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_OK;
+ actions[PAM_IGNORE] = _PAM_ACTION_IGNORE;
+ _pam_set_default_control(actions, _PAM_ACTION_DIE);
+ } else if (!strcasecmp("optional", tok)) {
+ D(("*PAM_F_OPTIONAL*"));
+ actions[PAM_SUCCESS] = _PAM_ACTION_OK;
+ actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_OK;
+ _pam_set_default_control(actions, _PAM_ACTION_IGNORE);
+ } else if (!strcasecmp("sufficient", tok)) {
+ D(("*PAM_F_SUFFICIENT*"));
+ actions[PAM_SUCCESS] = _PAM_ACTION_DONE;
+ actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_DONE;
+ _pam_set_default_control(actions, _PAM_ACTION_IGNORE);
+ } else if (!strcasecmp("include", tok)) {
+ D(("*PAM_F_INCLUDE*"));
+ pam_include = 1;
+ substack = 0;
+ } else if (!strcasecmp("substack", tok)) {
+ D(("*PAM_F_SUBSTACK*"));
+ pam_include = 1;
+ substack = 1;
+ } else {
+ D(("will need to parse %s", tok));
+ _pam_parse_control(actions, tok);
+ /* by default the default is to treat as failure */
+ _pam_set_default_control(actions, _PAM_ACTION_BAD);
+ }
+
+ tok = _pam_StrTok(NULL, " \n\t", &nexttok);
+ if (pam_include) {
+ if (substack) {
+ res = _pam_add_handler(pamh, PAM_HT_SUBSTACK, other,
+ stack_level, module_type, actions, tok,
+ 0, NULL, 0);
+ if (res != PAM_SUCCESS) {
+ pam_syslog(pamh, LOG_ERR, "error adding substack %s", tok);
+ D(("failed to load module - aborting"));
+ return PAM_ABORT;
+ }
+ }
+ if (_pam_load_conf_file(pamh, tok, this_service, module_type,
+ stack_level + substack
+#ifdef PAM_READ_BOTH_CONFS
+ , !other
+#endif /* PAM_READ_BOTH_CONFS */
+ ) == PAM_SUCCESS)
+ continue;
+ _pam_set_default_control(actions, _PAM_ACTION_BAD);
+ mod_path = NULL;
+ handler_type = PAM_HT_MUST_FAIL;
+ nexttok = NULL;
+ } else if (tok != NULL) {
+ mod_path = tok;
+ D(("mod_path = %s",mod_path));
+ } else {
+ /* no module name given */
+ D(("_pam_init_handlers: no module name supplied"));
+ pam_syslog(pamh, LOG_ERR,
+ "(%s) no module name supplied", this_service);
+ mod_path = NULL;
+ handler_type = PAM_HT_MUST_FAIL;
+ }
+
+ /* nexttok points to remaining arguments... */
+
+ if (nexttok != NULL) {
+ D(("list: %s",nexttok));
+ argvlen = _pam_mkargv(nexttok, &argv, &argc);
+ D(("argvlen = %d",argvlen));
+ } else { /* there are no arguments so fix by hand */
+ D(("_pam_init_handlers: empty argument list"));
+ argvlen = argc = 0;
+ argv = NULL;
+ }
+
+#ifdef PAM_DEBUG
+ {
+ int y;
+
+ D(("CONF%s: %s%s %d %s %d"
+ , handler_type==PAM_HT_MUST_FAIL?"<*will fail*>":""
+ , this_service, other ? "(backup)":""
+ , module_type
+ , mod_path, argc));
+ for (y = 0; y < argc; y++) {
+ D(("CONF: %s", argv[y]));
+ }
+ for (y = 0; y<_PAM_RETURN_VALUES; ++y) {
+ D(("RETURN %s(%d) -> %d %s",
+ _pam_token_returns[y], y, actions[y],
+ actions[y]>0 ? "jump":
+ _pam_token_actions[-actions[y]]));
+ }
+ }
+#endif
+
+ res = _pam_add_handler(pamh, handler_type, other, stack_level
+ , module_type, actions, mod_path
+ , argc, argv, argvlen);
+ if (res != PAM_SUCCESS) {
+ pam_syslog(pamh, LOG_ERR, "error loading %s", mod_path);
+ D(("failed to load module - aborting"));
+ return PAM_ABORT;
+ }
+ }
+ }
+
+ return ( (x < 0) ? PAM_ABORT:PAM_SUCCESS );
+}
+
+static int
+_pam_open_config_file(pam_handle_t *pamh
+ , const char *service
+ , char **path
+ , FILE **file)
+{
+ const char *pamd_dirs[] = { PAM_CONFIG_DF, PAM_CONFIG_DIST_DF
+#ifdef VENDORDIR
+ , PAM_CONFIG_DIST2_DF
+#endif
+ };
+ char *p = NULL;
+ FILE *f;
+ size_t i;
+
+ /* Absolute path */
+ if (service[0] == '/') {
+ p = _pam_strdup(service);
+ if (p == NULL) {
+ pam_syslog(pamh, LOG_CRIT, "strdup failed");
+ return PAM_BUF_ERR;
+ }
+ } else if (pamh->confdir != NULL) {
+ if (asprintf (&p, "%s/%s", pamh->confdir, service) < 0) {
+ pam_syslog(pamh, LOG_CRIT, "asprintf failed");
+ return PAM_BUF_ERR;
+ }
+ }
+
+ if (p != NULL) {
+ D(("opening %s", p));
+ f = fopen(p, "r");
+ if (f != NULL) {
+ *path = p;
+ *file = f;
+ return PAM_SUCCESS;
+ }
+ _pam_drop(p);
+ return PAM_ABORT;
+ }
+
+ for (i = 0; i < PAM_ARRAY_SIZE(pamd_dirs); i++) {
+ if (asprintf (&p, pamd_dirs[i], service) < 0) {
+ pam_syslog(pamh, LOG_CRIT, "asprintf failed");
+ return PAM_BUF_ERR;
+ }
+
+ D(("opening %s", p));
+ f = fopen(p, "r");
+ if (f != NULL) {
+ *path = p;
+ *file = f;
+ return PAM_SUCCESS;
+ }
+ _pam_drop(p);
+ }
+
+ return PAM_ABORT;
+}
+
+static int _pam_load_conf_file(pam_handle_t *pamh, const char *config_name
+ , const char *service /* specific file */
+ , int module_type /* specific type */
+ , int stack_level /* level of substack */
+#ifdef PAM_READ_BOTH_CONFS
+ , int not_other
+#endif /* PAM_READ_BOTH_CONFS */
+ )
+{
+ FILE *f;
+ char *path = NULL;
+ int retval = PAM_ABORT;
+
+ D(("_pam_load_conf_file called"));
+
+ if (stack_level >= PAM_SUBSTACK_MAX_LEVEL) {
+ D(("maximum level of substacks reached"));
+ pam_syslog(pamh, LOG_ERR, "maximum level of substacks reached");
+ return PAM_ABORT;
+ }
+
+ if (config_name == NULL) {
+ D(("no config file supplied"));
+ pam_syslog(pamh, LOG_ERR, "(%s) no config name supplied", service);
+ return PAM_ABORT;
+ }
+
+ if (_pam_open_config_file(pamh, config_name, &path, &f) == PAM_SUCCESS) {
+ retval = _pam_parse_conf_file(pamh, f, service, module_type, stack_level
+#ifdef PAM_READ_BOTH_CONFS
+ , not_other
+#endif /* PAM_READ_BOTH_CONFS */
+ );
+ if (retval != PAM_SUCCESS)
+ pam_syslog(pamh, LOG_ERR,
+ "_pam_load_conf_file: error reading %s: %s",
+ path, pam_strerror(pamh, retval));
+ _pam_drop(path);
+ fclose(f);
+ } else {
+ D(("unable to open %s", config_name));
+ pam_syslog(pamh, LOG_ERR,
+ "_pam_load_conf_file: unable to open config for %s",
+ config_name);
+ }
+
+ return retval;
+}
+
+/* Parse config file, allocate handler structures, dlopen() */
+int _pam_init_handlers(pam_handle_t *pamh)
+{
+ FILE *f;
+ int retval;
+
+ D(("_pam_init_handlers called"));
+ IF_NO_PAMH("_pam_init_handlers",pamh,PAM_SYSTEM_ERR);
+
+ /* Return immediately if everything is already loaded */
+ if (pamh->handlers.handlers_loaded) {
+ return PAM_SUCCESS;
+ }
+
+ D(("_pam_init_handlers: initializing"));
+
+ /* First clean the service structure */
+
+ _pam_free_handlers(pamh);
+ if (! pamh->handlers.module) {
+ if ((pamh->handlers.module =
+ malloc(MODULE_CHUNK * sizeof(struct loaded_module))) == NULL) {
+ pam_syslog(pamh, LOG_CRIT,
+ "_pam_init_handlers: no memory loading module");
+ return PAM_BUF_ERR;
+ }
+ pamh->handlers.modules_allocated = MODULE_CHUNK;
+ pamh->handlers.modules_used = 0;
+ }
+
+ if (pamh->service_name == NULL) {
+ return PAM_BAD_ITEM; /* XXX - better error? */
+ }
+
+#ifdef PAM_LOCKING
+ /* Is the PAM subsystem locked? */
+ {
+ int fd_tmp;
+
+ if ((fd_tmp = open( PAM_LOCK_FILE, O_RDONLY )) != -1) {
+ pam_syslog(pamh, LOG_ERR,
+ "_pam_init_handlers: PAM lockfile ("
+ PAM_LOCK_FILE ") exists - aborting");
+ (void) close(fd_tmp);
+ /*
+ * to avoid swamping the system with requests
+ */
+ _pam_start_timer(pamh);
+ pam_fail_delay(pamh, 5000000);
+ _pam_await_timer(pamh, PAM_ABORT);
+
+ return PAM_ABORT;
+ }
+ }
+#endif /* PAM_LOCKING */
+
+ /*
+ * Now parse the config file(s) and add handlers
+ */
+ {
+ struct stat test_d;
+
+ /* Is there a PAM_CONFIG_D directory? */
+ if (pamh->confdir != NULL ||
+ (stat(PAM_CONFIG_D, &test_d) == 0 && S_ISDIR(test_d.st_mode)) ||
+ (stat(PAM_CONFIG_DIST_D, &test_d) == 0 && S_ISDIR(test_d.st_mode))
+#ifdef PAM_CONFIG_DIST2_D
+ || (stat(PAM_CONFIG_DIST2_D, &test_d) == 0
+ && S_ISDIR(test_d.st_mode))
+#endif
+ ) {
+ char *path = NULL;
+ int read_something=0;
+
+ if (_pam_open_config_file(pamh, pamh->service_name, &path, &f) == PAM_SUCCESS) {
+ retval = _pam_parse_conf_file(pamh, f, pamh->service_name,
+ PAM_T_ANY, 0
+#ifdef PAM_READ_BOTH_CONFS
+ , 0
+#endif /* PAM_READ_BOTH_CONFS */
+ );
+ if (retval != PAM_SUCCESS) {
+ pam_syslog(pamh, LOG_ERR,
+ "_pam_init_handlers: error reading %s",
+ path);
+ pam_syslog(pamh, LOG_ERR, "_pam_init_handlers: [%s]",
+ pam_strerror(pamh, retval));
+ } else {
+ read_something = 1;
+ }
+ _pam_drop(path);
+ fclose(f);
+ } else {
+ D(("unable to open configuration for %s", pamh->service_name));
+#ifdef PAM_READ_BOTH_CONFS
+ D(("checking %s", PAM_CONFIG));
+
+ if (pamh->confdir == NULL
+ && (f = fopen(PAM_CONFIG,"r")) != NULL) {
+ retval = _pam_parse_conf_file(pamh, f, NULL, PAM_T_ANY, 0, 1);
+ fclose(f);
+ } else
+#endif /* PAM_READ_BOTH_CONFS */
+ retval = PAM_SUCCESS;
+ /*
+ * XXX - should we log an error? Some people want to always
+ * use "other"
+ */
+ }
+
+ if (retval == PAM_SUCCESS) {
+ /* now parse the PAM_DEFAULT_SERVICE */
+
+ if (_pam_open_config_file(pamh, PAM_DEFAULT_SERVICE, &path, &f) == PAM_SUCCESS) {
+ /* would test magic here? */
+ retval = _pam_parse_conf_file(pamh, f, PAM_DEFAULT_SERVICE,
+ PAM_T_ANY, 0
+#ifdef PAM_READ_BOTH_CONFS
+ , 0
+#endif /* PAM_READ_BOTH_CONFS */
+ );
+ if (retval != PAM_SUCCESS) {
+ pam_syslog(pamh, LOG_ERR,
+ "_pam_init_handlers: error reading %s",
+ path);
+ pam_syslog(pamh, LOG_ERR,
+ "_pam_init_handlers: [%s]",
+ pam_strerror(pamh, retval));
+ } else {
+ read_something = 1;
+ }
+ _pam_drop(path);
+ fclose(f);
+ } else {
+ D(("unable to open %s", PAM_DEFAULT_SERVICE));
+ pam_syslog(pamh, LOG_ERR,
+ "_pam_init_handlers: no default config %s",
+ PAM_DEFAULT_SERVICE);
+ }
+ if (!read_something) { /* nothing read successfully */
+ retval = PAM_ABORT;
+ }
+ }
+ } else {
+ if ((f = fopen(PAM_CONFIG, "r")) == NULL) {
+ pam_syslog(pamh, LOG_ERR, "_pam_init_handlers: could not open "
+ PAM_CONFIG );
+ return PAM_ABORT;
+ }
+
+ retval = _pam_parse_conf_file(pamh, f, NULL, PAM_T_ANY, 0
+#ifdef PAM_READ_BOTH_CONFS
+ , 0
+#endif /* PAM_READ_BOTH_CONFS */
+ );
+
+ D(("closing configuration file"));
+ fclose(f);
+ }
+ }
+
+ if (retval != PAM_SUCCESS) {
+ /* Read error */
+ pam_syslog(pamh, LOG_ERR, "error reading PAM configuration file");
+ return PAM_ABORT;
+ }
+
+ pamh->handlers.handlers_loaded = 1;
+
+ D(("_pam_init_handlers exiting"));
+ return PAM_SUCCESS;
+}
+
+/*
+ * This is where we read a line of the PAM config file. The line may be
+ * preceded by lines of comments and also extended with "\\\n"
+ */
+
+static int _pam_assemble_line(FILE *f, char *buffer, int buf_len)
+{
+ char *p = buffer;
+ char *endp = buffer + buf_len;
+ char *s, *os;
+ int used = 0;
+
+ /* loop broken with a 'break' when a non-'\\n' ended line is read */
+
+ D(("called."));
+ for (;;) {
+ if (p >= endp) {
+ /* Overflow */
+ D(("_pam_assemble_line: overflow"));
+ return -1;
+ }
+ if (fgets(p, endp - p, f) == NULL) {
+ if (used) {
+ /* Incomplete read */
+ return -1;
+ } else {
+ /* EOF */
+ return 0;
+ }
+ }
+
+ /* skip leading spaces --- line may be blank */
+
+ s = p + strspn(p, " \n\t");
+ if (*s && (*s != '#')) {
+ os = s;
+
+ /*
+ * we are only interested in characters before the first '#'
+ * character
+ */
+
+ while (*s && *s != '#')
+ ++s;
+ if (*s == '#') {
+ *s = '\0';
+ used += strlen(os);
+ break; /* the line has been read */
+ }
+
+ s = os;
+
+ /*
+ * Check for backslash by scanning back from the end of
+ * the entered line, the '\n' has been included since
+ * normally a line is terminated with this
+ * character. fgets() should only return one though!
+ */
+
+ s += strlen(s);
+ while (s > os && ((*--s == ' ') || (*s == '\t')
+ || (*s == '\n')));
+
+ /* check if it ends with a backslash */
+ if (*s == '\\') {
+ *s++ = ' '; /* replace backslash with ' ' */
+ *s = '\0'; /* truncate the line here */
+ used += strlen(os);
+ p = s; /* there is more ... */
+ } else {
+ /* End of the line! */
+ used += strlen(os);
+ break; /* this is the complete line */
+ }
+
+ } else {
+ /* Nothing in this line */
+ /* Don't move p */
+ }
+ }
+
+ return used;
+}
+
+static char *
+extract_modulename(const char *mod_path)
+{
+ const char *p = strrchr (mod_path, '/');
+ char *dot, *retval;
+
+ if (p == NULL)
+ p = mod_path;
+ else
+ p++;
+
+ if ((retval = _pam_strdup (p)) == NULL)
+ return NULL;
+
+ dot = strrchr (retval, '.');
+ if (dot)
+ *dot = '\0';
+
+ if (*retval == '\0' || strcmp(retval, "?") == 0) {
+ /* do not allow empty module name or "?" to avoid confusing audit trail */
+ _pam_drop(retval);
+ return NULL;
+ }
+
+ return retval;
+}
+
+static struct loaded_module *
+_pam_load_module(pam_handle_t *pamh, const char *mod_path, int handler_type)
+{
+ int x = 0;
+ int success;
+ struct loaded_module *mod;
+
+ D(("_pam_load_module: loading module `%s'", mod_path));
+
+ mod = pamh->handlers.module;
+
+ /* First, ensure the module is loaded */
+ while (x < pamh->handlers.modules_used) {
+ if (!strcmp(mod[x].name, mod_path)) { /* case sensitive ! */
+ break;
+ }
+ x++;
+ }
+ if (x == pamh->handlers.modules_used) {
+ /* Not found */
+ if (pamh->handlers.modules_allocated == pamh->handlers.modules_used) {
+ /* will need more memory */
+ void *tmp = realloc(pamh->handlers.module,
+ (pamh->handlers.modules_allocated+MODULE_CHUNK)
+ *sizeof(struct loaded_module));
+ if (tmp == NULL) {
+ D(("cannot enlarge module pointer memory"));
+ pam_syslog(pamh, LOG_CRIT,
+ "realloc returned NULL in _pam_load_module");
+ return NULL;
+ }
+ pamh->handlers.module = tmp;
+ pamh->handlers.modules_allocated += MODULE_CHUNK;
+ }
+ mod = &(pamh->handlers.module[x]);
+ /* Be pessimistic... */
+ success = PAM_ABORT;
+
+ D(("_pam_load_module: _pam_dlopen(%s)", mod_path));
+ mod->dl_handle = _pam_dlopen(mod_path);
+ D(("_pam_load_module: _pam_dlopen'ed"));
+ D(("_pam_load_module: dlopen'ed"));
+ if (mod->dl_handle == NULL) {
+ const char *isa = strstr(mod_path, "$ISA");
+ size_t isa_len = strlen("$ISA");
+
+ if (isa != NULL) {
+ size_t pam_isa_len = strlen(_PAM_ISA);
+ char *mod_full_isa_path =
+ malloc(strlen(mod_path) - isa_len + pam_isa_len + 1);
+
+ if (mod_full_isa_path == NULL) {
+ D(("_pam_load_module: couldn't get memory for mod_path"));
+ pam_syslog(pamh, LOG_CRIT, "no memory for module path");
+ success = PAM_ABORT;
+ } else {
+ char *p = mod_full_isa_path;
+
+ memcpy(p, mod_path, isa - mod_path);
+ p += isa - mod_path;
+ memcpy(p, _PAM_ISA, pam_isa_len);
+ p += pam_isa_len;
+ strcpy(p, isa + isa_len);
+
+ mod->dl_handle = _pam_dlopen(mod_full_isa_path);
+ _pam_drop(mod_full_isa_path);
+ }
+ }
+ }
+ if (mod->dl_handle == NULL) {
+ D(("_pam_load_module: _pam_dlopen(%s) failed", mod_path));
+ if (handler_type != PAM_HT_SILENT_MODULE)
+ pam_syslog(pamh, LOG_ERR, "unable to dlopen(%s): %s", mod_path,
+ _pam_dlerror());
+ /* Don't abort yet; static code may be able to find function.
+ * But defaults to abort if nothing found below... */
+ } else {
+ D(("module added successfully"));
+ success = PAM_SUCCESS;
+ mod->type = PAM_MT_DYNAMIC_MOD;
+ pamh->handlers.modules_used++;
+ }
+
+ if (success != PAM_SUCCESS) { /* add a malformed module */
+ mod->dl_handle = NULL;
+ mod->type = PAM_MT_FAULTY_MOD;
+ pamh->handlers.modules_used++;
+ if (handler_type != PAM_HT_SILENT_MODULE)
+ pam_syslog(pamh, LOG_ERR, "adding faulty module: %s", mod_path);
+ success = PAM_SUCCESS; /* We have successfully added a module */
+ }
+
+ /* indicate its name - later we will search for it by this */
+ if ((mod->name = _pam_strdup(mod_path)) == NULL) {
+ D(("_pam_load_module: couldn't get memory for mod_path"));
+ pam_syslog(pamh, LOG_CRIT, "no memory for module path");
+ success = PAM_ABORT;
+ }
+
+ } else { /* x != pamh->handlers.modules_used */
+ mod += x; /* the located module */
+ success = PAM_SUCCESS;
+ }
+ return success == PAM_SUCCESS ? mod : NULL;
+}
+
+int _pam_add_handler(pam_handle_t *pamh
+ , int handler_type, int other, int stack_level, int type
+ , int *actions, const char *mod_path
+ , int argc, char **argv, int argvlen)
+{
+ struct loaded_module *mod = NULL;
+ struct handler **handler_p;
+ struct handler **handler_p2;
+ struct handlers *the_handlers;
+ const char *sym, *sym2;
+ char *mod_full_path;
+ servicefn func, func2;
+ int mod_type = PAM_MT_FAULTY_MOD;
+
+ D(("called."));
+ IF_NO_PAMH("_pam_add_handler",pamh,PAM_SYSTEM_ERR);
+
+ D(("_pam_add_handler: adding type %d, handler_type %d, module `%s'",
+ type, handler_type, mod_path));
+
+ if ((handler_type == PAM_HT_MODULE || handler_type == PAM_HT_SILENT_MODULE) &&
+ mod_path != NULL) {
+ if (mod_path[0] == '/') {
+ mod = _pam_load_module(pamh, mod_path, handler_type);
+ } else if (asprintf(&mod_full_path, "%s%s",
+ DEFAULT_MODULE_PATH, mod_path) >= 0) {
+ mod = _pam_load_module(pamh, mod_full_path, handler_type);
+ _pam_drop(mod_full_path);
+ } else {
+ pam_syslog(pamh, LOG_CRIT, "cannot malloc full mod path");
+ return PAM_ABORT;
+ }
+
+ if (mod == NULL) {
+ /* if we get here with NULL it means allocation error */
+ return PAM_ABORT;
+ }
+
+ mod_type = mod->type;
+ }
+
+ if (mod_path == NULL)
+ mod_path = UNKNOWN_MODULE;
+
+ /*
+ * At this point 'mod' points to the stored/loaded module.
+ */
+
+ /* Now define the handler(s) based on mod->dlhandle and type */
+
+ /* decide which list of handlers to use */
+ the_handlers = (other) ? &pamh->handlers.other : &pamh->handlers.conf;
+
+ handler_p = handler_p2 = NULL;
+ func = func2 = NULL;
+ sym2 = NULL;
+
+ /* point handler_p's at the root addresses of the function stacks */
+ switch (type) {
+ case PAM_T_AUTH:
+ handler_p = &the_handlers->authenticate;
+ sym = "pam_sm_authenticate";
+ handler_p2 = &the_handlers->setcred;
+ sym2 = "pam_sm_setcred";
+ break;
+ case PAM_T_SESS:
+ handler_p = &the_handlers->open_session;
+ sym = "pam_sm_open_session";
+ handler_p2 = &the_handlers->close_session;
+ sym2 = "pam_sm_close_session";
+ break;
+ case PAM_T_ACCT:
+ handler_p = &the_handlers->acct_mgmt;
+ sym = "pam_sm_acct_mgmt";
+ break;
+ case PAM_T_PASS:
+ handler_p = &the_handlers->chauthtok;
+ sym = "pam_sm_chauthtok";
+ break;
+ default:
+ /* Illegal module type */
+ D(("_pam_add_handler: illegal module type %d", type));
+ return PAM_ABORT;
+ }
+
+ /* are the modules reliable? */
+ if (mod_type != PAM_MT_DYNAMIC_MOD &&
+ mod_type != PAM_MT_FAULTY_MOD) {
+ D(("_pam_add_handlers: illegal module library type; %d", mod_type));
+ pam_syslog(pamh, LOG_ERR,
+ "internal error: module library type not known: %s;%d",
+ sym, mod_type);
+ return PAM_ABORT;
+ }
+
+ /* now identify this module's functions - for non-faulty modules */
+
+ if ((mod_type == PAM_MT_DYNAMIC_MOD) &&
+ !(func = _pam_dlsym(mod->dl_handle, sym)) ) {
+ pam_syslog(pamh, LOG_ERR, "unable to resolve symbol: %s", sym);
+ }
+ if (sym2) {
+ if ((mod_type == PAM_MT_DYNAMIC_MOD) &&
+ !(func2 = _pam_dlsym(mod->dl_handle, sym2)) ) {
+ pam_syslog(pamh, LOG_ERR, "unable to resolve symbol: %s", sym2);
+ }
+ }
+
+ /* here func (and perhaps func2) point to the appropriate functions */
+
+ /* add new handler to end of existing list */
+ while (*handler_p != NULL) {
+ handler_p = &((*handler_p)->next);
+ }
+
+ if ((*handler_p = malloc(sizeof(struct handler))) == NULL) {
+ pam_syslog(pamh, LOG_CRIT, "cannot malloc struct handler #1");
+ return (PAM_ABORT);
+ }
+
+ (*handler_p)->handler_type = handler_type;
+ (*handler_p)->stack_level = stack_level;
+ (*handler_p)->func = func;
+ memcpy((*handler_p)->actions,actions,sizeof((*handler_p)->actions));
+ (*handler_p)->cached_retval = _PAM_INVALID_RETVAL;
+ (*handler_p)->cached_retval_p = &((*handler_p)->cached_retval);
+ (*handler_p)->argc = argc;
+ (*handler_p)->argv = argv; /* not a copy */
+ if (((*handler_p)->mod_name = extract_modulename(mod_path)) == NULL)
+ return PAM_ABORT;
+ (*handler_p)->grantor = 0;
+ (*handler_p)->next = NULL;
+
+ /* some of the modules have a second calling function */
+ if (handler_p2) {
+ /* add new handler to end of existing list */
+ while (*handler_p2) {
+ handler_p2 = &((*handler_p2)->next);
+ }
+
+ if ((*handler_p2 = malloc(sizeof(struct handler))) == NULL) {
+ pam_syslog(pamh, LOG_CRIT, "cannot malloc struct handler #2");
+ return (PAM_ABORT);
+ }
+
+ (*handler_p2)->handler_type = handler_type;
+ (*handler_p2)->stack_level = stack_level;
+ (*handler_p2)->func = func2;
+ memcpy((*handler_p2)->actions,actions,sizeof((*handler_p2)->actions));
+ (*handler_p2)->cached_retval = _PAM_INVALID_RETVAL; /* ignored */
+ /* Note, this next entry points to the handler_p value! */
+ (*handler_p2)->cached_retval_p = &((*handler_p)->cached_retval);
+ (*handler_p2)->argc = argc;
+ if (argv) {
+ if (((*handler_p2)->argv = malloc(argvlen)) == NULL) {
+ pam_syslog(pamh, LOG_CRIT, "cannot malloc argv for handler #2");
+ return (PAM_ABORT);
+ }
+ memcpy((*handler_p2)->argv, argv, argvlen);
+ } else {
+ (*handler_p2)->argv = NULL; /* no arguments */
+ }
+ if (((*handler_p2)->mod_name = extract_modulename(mod_path)) == NULL)
+ return PAM_ABORT;
+ (*handler_p2)->grantor = 0;
+ (*handler_p2)->next = NULL;
+ }
+
+ D(("_pam_add_handler: returning successfully"));
+
+ return PAM_SUCCESS;
+}
+
+/* Free various allocated structures and dlclose() the libs */
+int _pam_free_handlers(pam_handle_t *pamh)
+{
+ struct loaded_module *mod;
+
+ D(("called."));
+ IF_NO_PAMH("_pam_free_handlers",pamh,PAM_SYSTEM_ERR);
+
+ mod = pamh->handlers.module;
+
+ /* Close all loaded modules */
+
+ while (pamh->handlers.modules_used) {
+ D(("_pam_free_handlers: dlclose(%s)", mod->name));
+ free(mod->name);
+ if (mod->type == PAM_MT_DYNAMIC_MOD) {
+ _pam_dlclose(mod->dl_handle);
+ }
+ mod++;
+ pamh->handlers.modules_used--;
+ }
+
+ /* Free all the handlers */
+
+ _pam_free_handlers_aux(&(pamh->handlers.conf.authenticate));
+ _pam_free_handlers_aux(&(pamh->handlers.conf.setcred));
+ _pam_free_handlers_aux(&(pamh->handlers.conf.acct_mgmt));
+ _pam_free_handlers_aux(&(pamh->handlers.conf.open_session));
+ _pam_free_handlers_aux(&(pamh->handlers.conf.close_session));
+ _pam_free_handlers_aux(&(pamh->handlers.conf.chauthtok));
+
+ _pam_free_handlers_aux(&(pamh->handlers.other.authenticate));
+ _pam_free_handlers_aux(&(pamh->handlers.other.setcred));
+ _pam_free_handlers_aux(&(pamh->handlers.other.acct_mgmt));
+ _pam_free_handlers_aux(&(pamh->handlers.other.open_session));
+ _pam_free_handlers_aux(&(pamh->handlers.other.close_session));
+ _pam_free_handlers_aux(&(pamh->handlers.other.chauthtok));
+
+ /* no more loaded modules */
+
+ _pam_drop(pamh->handlers.module);
+
+ /* Indicate that handlers are not initialized for this pamh */
+
+ pamh->handlers.handlers_loaded = 0;
+
+ return PAM_SUCCESS;
+}
+
+void _pam_start_handlers(pam_handle_t *pamh)
+{
+ D(("called."));
+ /* NB. There is no check for a NULL pamh here, since no return
+ * value to communicate the fact! */
+
+ /* Indicate that handlers are not initialized for this pamh */
+ pamh->handlers.handlers_loaded = 0;
+
+ pamh->handlers.modules_allocated = 0;
+ pamh->handlers.modules_used = 0;
+ pamh->handlers.module = NULL;
+
+ /* initialize the .conf and .other entries */
+
+ pamh->handlers.conf.authenticate = NULL;
+ pamh->handlers.conf.setcred = NULL;
+ pamh->handlers.conf.acct_mgmt = NULL;
+ pamh->handlers.conf.open_session = NULL;
+ pamh->handlers.conf.close_session = NULL;
+ pamh->handlers.conf.chauthtok = NULL;
+
+ pamh->handlers.other.authenticate = NULL;
+ pamh->handlers.other.setcred = NULL;
+ pamh->handlers.other.acct_mgmt = NULL;
+ pamh->handlers.other.open_session = NULL;
+ pamh->handlers.other.close_session = NULL;
+ pamh->handlers.other.chauthtok = NULL;
+}
+
+void _pam_free_handlers_aux(struct handler **hp)
+{
+ struct handler *h = *hp;
+ struct handler *last;
+
+ D(("called."));
+ while (h) {
+ last = h;
+ _pam_drop(h->argv); /* This is all allocated in a single chunk */
+ _pam_drop(h->mod_name);
+ h = h->next;
+ memset(last, 0, sizeof(*last));
+ free(last);
+ }
+
+ *hp = NULL;
+}
diff --git a/libpam/pam_item.c b/libpam/pam_item.c
new file mode 100644
index 0000000..d6af710
--- /dev/null
+++ b/libpam/pam_item.c
@@ -0,0 +1,396 @@
+/* pam_item.c */
+
+/*
+ * $Id$
+ */
+
+#include "pam_private.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#define TRY_SET(X, Y) \
+{ \
+ if ((X) != (Y)) { \
+ char *_TMP_ = _pam_strdup(Y); \
+ if (_TMP_ == NULL && (Y) != NULL) \
+ return PAM_BUF_ERR; \
+ free(X); \
+ (X) = _TMP_; \
+ } \
+}
+
+/* functions */
+
+int pam_set_item (pam_handle_t *pamh, int item_type, const void *item)
+{
+ int retval;
+
+ D(("called"));
+
+ IF_NO_PAMH("pam_set_item", pamh, PAM_SYSTEM_ERR);
+
+ retval = PAM_SUCCESS;
+
+ switch (item_type) {
+
+ case PAM_SERVICE:
+ /* Setting handlers_loaded to 0 will cause the handlers
+ * to be reloaded on the next call to a service module.
+ */
+ pamh->handlers.handlers_loaded = 0;
+ TRY_SET(pamh->service_name, item);
+ {
+ char *tmp;
+ for (tmp=pamh->service_name; *tmp; ++tmp)
+ *tmp = tolower(*tmp); /* require lower case */
+ }
+ break;
+
+ case PAM_USER:
+ TRY_SET(pamh->user, item);
+ pamh->former.fail_user = PAM_SUCCESS;
+ break;
+
+ case PAM_USER_PROMPT:
+ TRY_SET(pamh->prompt, item);
+ pamh->former.fail_user = PAM_SUCCESS;
+ break;
+
+ case PAM_TTY:
+ D(("setting tty to %s", item));
+ TRY_SET(pamh->tty, item);
+ break;
+
+ case PAM_RUSER:
+ TRY_SET(pamh->ruser, item);
+ break;
+
+ case PAM_RHOST:
+ TRY_SET(pamh->rhost, item);
+ break;
+
+ case PAM_AUTHTOK:
+ /*
+ * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from
+ * modules.
+ */
+ if (__PAM_FROM_MODULE(pamh)) {
+ if (pamh->authtok != item) {
+ _pam_overwrite(pamh->authtok);
+ TRY_SET(pamh->authtok, item);
+ }
+ } else {
+ retval = PAM_BAD_ITEM;
+ }
+
+ break;
+
+ case PAM_OLDAUTHTOK:
+ /*
+ * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from
+ * modules.
+ */
+ if (__PAM_FROM_MODULE(pamh)) {
+ if (pamh->oldauthtok != item) {
+ _pam_overwrite(pamh->oldauthtok);
+ TRY_SET(pamh->oldauthtok, item);
+ }
+ } else {
+ retval = PAM_BAD_ITEM;
+ }
+
+ break;
+
+ case PAM_CONV: /* want to change the conversation function */
+ if (item == NULL) {
+ pam_syslog(pamh, LOG_ERR,
+ "pam_set_item: attempt to set conv() to NULL");
+ retval = PAM_PERM_DENIED;
+ } else {
+ struct pam_conv *tconv;
+
+ if ((tconv=
+ (struct pam_conv *) malloc(sizeof(struct pam_conv))
+ ) == NULL) {
+ pam_syslog(pamh, LOG_CRIT,
+ "pam_set_item: malloc failed for pam_conv");
+ retval = PAM_BUF_ERR;
+ } else {
+ memcpy(tconv, item, sizeof(struct pam_conv));
+ _pam_drop(pamh->pam_conversation);
+ pamh->pam_conversation = tconv;
+ pamh->former.fail_user = PAM_SUCCESS;
+ }
+ }
+ break;
+
+ case PAM_FAIL_DELAY:
+ pamh->fail_delay.delay_fn_ptr = item;
+ break;
+
+ case PAM_XDISPLAY:
+ TRY_SET(pamh->xdisplay, item);
+ break;
+
+ case PAM_XAUTHDATA:
+ if (&pamh->xauth == item)
+ break;
+ if (pamh->xauth.namelen) {
+ _pam_overwrite(pamh->xauth.name);
+ free(pamh->xauth.name);
+ }
+ if (pamh->xauth.datalen) {
+ _pam_overwrite_n(pamh->xauth.data,
+ (unsigned int) pamh->xauth.datalen);
+ free(pamh->xauth.data);
+ }
+ pamh->xauth = *((const struct pam_xauth_data *) item);
+ if ((pamh->xauth.name=_pam_strdup(pamh->xauth.name)) == NULL) {
+ memset(&pamh->xauth, '\0', sizeof(pamh->xauth));
+ return PAM_BUF_ERR;
+ }
+ if ((pamh->xauth.data=_pam_memdup(pamh->xauth.data,
+ pamh->xauth.datalen)) == NULL) {
+ _pam_overwrite(pamh->xauth.name);
+ free(pamh->xauth.name);
+ memset(&pamh->xauth, '\0', sizeof(pamh->xauth));
+ return PAM_BUF_ERR;
+ }
+ break;
+
+ case PAM_AUTHTOK_TYPE:
+ TRY_SET(pamh->authtok_type, item);
+ break;
+
+ default:
+ retval = PAM_BAD_ITEM;
+ }
+
+ return retval;
+}
+
+int pam_get_item (const pam_handle_t *pamh, int item_type, const void **item)
+{
+ int retval = PAM_SUCCESS;
+
+ D(("called."));
+ IF_NO_PAMH("pam_get_item", pamh, PAM_SYSTEM_ERR);
+
+ if (item == NULL) {
+ pam_syslog(pamh, LOG_ERR,
+ "pam_get_item: nowhere to place requested item");
+ return PAM_PERM_DENIED;
+ }
+ else
+ *item = NULL;
+
+ switch (item_type) {
+ case PAM_SERVICE:
+ *item = pamh->service_name;
+ break;
+
+ case PAM_USER:
+ D(("returning user=%s", pamh->user));
+ *item = pamh->user;
+ break;
+
+ case PAM_USER_PROMPT:
+ D(("returning userprompt=%s", pamh->user));
+ *item = pamh->prompt;
+ break;
+
+ case PAM_TTY:
+ D(("returning tty=%s", pamh->tty));
+ *item = pamh->tty;
+ break;
+
+ case PAM_RUSER:
+ *item = pamh->ruser;
+ break;
+
+ case PAM_RHOST:
+ *item = pamh->rhost;
+ break;
+
+ case PAM_AUTHTOK:
+ /*
+ * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from
+ * modules.
+ */
+ if (__PAM_FROM_MODULE(pamh)) {
+ *item = pamh->authtok;
+ } else {
+ retval = PAM_BAD_ITEM;
+ }
+ break;
+
+ case PAM_OLDAUTHTOK:
+ /*
+ * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from
+ * modules.
+ */
+ if (__PAM_FROM_MODULE(pamh)) {
+ *item = pamh->oldauthtok;
+ } else {
+ retval = PAM_BAD_ITEM;
+ }
+ break;
+
+ case PAM_CONV:
+ *item = pamh->pam_conversation;
+ break;
+
+ case PAM_FAIL_DELAY:
+ *item = pamh->fail_delay.delay_fn_ptr;
+ break;
+
+ case PAM_XDISPLAY:
+ *item = pamh->xdisplay;
+ break;
+
+ case PAM_XAUTHDATA:
+ *item = &pamh->xauth;
+ break;
+
+ case PAM_AUTHTOK_TYPE:
+ *item = pamh->authtok_type;
+ break;
+
+ default:
+ retval = PAM_BAD_ITEM;
+ }
+
+ return retval;
+}
+
+/*
+ * This function is the 'preferred method to obtain the username'.
+ */
+
+int pam_get_user(pam_handle_t *pamh, const char **user, const char *prompt)
+{
+ const char *use_prompt;
+ int retval;
+ struct pam_message msg;
+ const struct pam_message *pmsg;
+ struct pam_response *resp;
+
+ D(("called."));
+
+ IF_NO_PAMH("pam_get_user", pamh, PAM_SYSTEM_ERR);
+
+ if (user == NULL) {
+ /* ensure that the module has supplied a destination */
+ pam_syslog(pamh, LOG_ERR, "pam_get_user: nowhere to record username");
+ return PAM_SYSTEM_ERR;
+ } else
+ *user = NULL;
+
+ if (pamh->pam_conversation == NULL) {
+ pam_syslog(pamh, LOG_ERR, "pam_get_user: no conv element in pamh");
+ return PAM_SYSTEM_ERR;
+ }
+
+ if (pamh->user) { /* have one so return it */
+ *user = pamh->user;
+ return PAM_SUCCESS;
+ }
+
+ if (pamh->former.fail_user != PAM_SUCCESS)
+ return pamh->former.fail_user;
+
+ /* will need a prompt */
+ if (prompt != NULL)
+ use_prompt = prompt;
+ else if (pamh->prompt != NULL)
+ use_prompt = pamh->prompt;
+ else
+ use_prompt = _("login:");
+
+ /* If we are resuming an old conversation, we verify that the prompt
+ is the same. Anything else is an error. */
+ if (pamh->former.want_user) {
+ /* must have a prompt to resume with */
+ if (! pamh->former.prompt) {
+ pam_syslog(pamh, LOG_ERR,
+ "pam_get_user: failed to resume with prompt"
+ );
+ return PAM_ABORT;
+ }
+
+ /* must be the same prompt as last time */
+ if (strcmp(pamh->former.prompt, use_prompt)) {
+ pam_syslog(pamh, LOG_ERR,
+ "pam_get_user: resumed with different prompt");
+ return PAM_ABORT;
+ }
+
+ /* ok, we can resume where we left off last time */
+ pamh->former.want_user = PAM_FALSE;
+ _pam_overwrite(pamh->former.prompt);
+ _pam_drop(pamh->former.prompt);
+ }
+
+ /* converse with application -- prompt user for a username */
+ pmsg = &msg;
+ msg.msg_style = PAM_PROMPT_ECHO_ON;
+ msg.msg = use_prompt;
+ resp = NULL;
+
+ retval = pamh->pam_conversation->
+ conv(1, &pmsg, &resp, pamh->pam_conversation->appdata_ptr);
+
+ switch (retval) {
+ case PAM_SUCCESS:
+ case PAM_BUF_ERR:
+ case PAM_CONV_AGAIN:
+ case PAM_CONV_ERR:
+ break;
+ default:
+ retval = PAM_CONV_ERR;
+ }
+
+ switch (retval) {
+ case PAM_CONV_AGAIN:
+ /* conversation function is waiting for an event - save state */
+ D(("conversation function is not ready yet"));
+ pamh->former.want_user = PAM_TRUE;
+ pamh->former.prompt = _pam_strdup(use_prompt);
+ break;
+ case PAM_SUCCESS:
+ if (resp != NULL && resp->resp != NULL) {
+ /*
+ * now we set the PAM_USER item -- this was missing from pre.53
+ * releases. However, reading the Sun manual, it is part of
+ * the standard API.
+ */
+ retval = pam_set_item(pamh, PAM_USER, resp->resp);
+ *user = pamh->user;
+ break;
+ } else {
+ /* conversation should have given a response */
+ D(("pam_get_user: no response provided"));
+ retval = PAM_CONV_ERR;
+ }
+ /* fallthrough */
+ default:
+ pamh->former.fail_user = retval;
+ }
+
+ if (resp) {
+ if (retval != PAM_SUCCESS)
+ pam_syslog(pamh, LOG_WARNING,
+ "unexpected response from failed conversation function");
+ /*
+ * note 'resp' is allocated by the application and is
+ * correctly free()'d here
+ */
+ _pam_drop_reply(resp, 1);
+ }
+
+ D(("completed"));
+ return retval; /* pass on any error from conversation */
+}
diff --git a/libpam/pam_misc.c b/libpam/pam_misc.c
new file mode 100644
index 0000000..996f23c
--- /dev/null
+++ b/libpam/pam_misc.c
@@ -0,0 +1,360 @@
+/* pam_misc.c -- This is random stuff
+ *
+ * Copyright (c) Andrew G. Morgan <morgan@kernel.org> 2000-2003
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pam_private.h"
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <ctype.h>
+
+char *_pam_StrTok(char *from, const char *format, char **next)
+/*
+ * this function is a variant of the standard strtok, it differs in that
+ * it takes an additional argument and doesn't nul terminate tokens until
+ * they are actually reached.
+ */
+{
+ char table[256], *end;
+ int i;
+
+ if (from == NULL && (from = *next) == NULL)
+ return from;
+
+ /* initialize table */
+ for (i=1; i<256; table[i++] = '\0');
+ for (i=0; format[i] ;
+ table[(unsigned char)format[i++]] = 'y');
+
+ /* look for first non-format char */
+ while (*from && table[(unsigned char)*from]) {
+ ++from;
+ }
+
+ if (*from == '[') {
+ /*
+ * special case, "[...]" is considered to be a single
+ * object. Note, however, if one of the format[] chars is
+ * '[' this single string will not be read correctly.
+ * Note, any '[' inside the outer "[...]" pair will survive.
+ * Note, the first ']' will terminate this string, but
+ * that "\]" will get compressed into "]". That is:
+ *
+ * "[..[..\]..]..." --> "..[..].."
+ */
+ char *to;
+ for (to=end=++from; *end && *end != ']'; ++to, ++end) {
+ if (*end == '\\' && end[1] == ']')
+ ++end;
+ if (to != end) {
+ *to = *end;
+ }
+ }
+ if (to != end) {
+ *to = '\0';
+ }
+ /* note, this string is stripped of its edges: "..." is what
+ remains */
+ } else if (*from) {
+ /* simply look for next blank char */
+ for (end=from; *end && !table[(unsigned char)*end]; ++end);
+ } else {
+ return (*next = NULL); /* no tokens left */
+ }
+
+ /* now terminate what we have */
+ if (*end)
+ *end++ = '\0';
+
+ /* indicate what it left */
+ if (*end) {
+ *next = end;
+ } else {
+ *next = NULL; /* have found last token */
+ }
+
+ /* return what we have */
+ return from;
+}
+
+/*
+ * Safe duplication of character strings. "Paranoid"; don't leave
+ * evidence of old token around for later stack analysis.
+ */
+
+char *_pam_strdup(const char *x)
+{
+ register char *new=NULL;
+
+ if (x != NULL) {
+ register int len;
+
+ len = strlen (x) + 1; /* length of string including NUL */
+ if ((new = malloc(len)) == NULL) {
+ len = 0;
+ pam_syslog(NULL, LOG_CRIT, "_pam_strdup: failed to get memory");
+ } else {
+ strcpy (new, x);
+ }
+ x = NULL;
+ }
+
+ return new; /* return the duplicate or NULL on error */
+}
+
+/*
+ * Safe duplication of memory buffers. "Paranoid"; don't leave
+ * evidence of old token around for later stack analysis.
+ */
+
+char *_pam_memdup(const char *x, int len)
+{
+ register char *new=NULL;
+
+ if (x != NULL) {
+ if ((new = malloc(len)) == NULL) {
+ len = 0;
+ pam_syslog(NULL, LOG_CRIT, "_pam_memdup: failed to get memory");
+ } else {
+ memcpy (new, x, len);
+ }
+ x = NULL;
+ }
+
+ return new; /* return the duplicate or NULL on error */
+}
+
+/* Generate argv, argc from s */
+/* caller must free(argv) */
+
+int _pam_mkargv(const char *s, char ***argv, int *argc)
+{
+ int l;
+ int argvlen = 0;
+ char *sbuf, *sbuf_start;
+ char **our_argv = NULL;
+ char **argvbuf;
+ char *argvbufp;
+#ifdef PAM_DEBUG
+ int count=0;
+#endif
+
+ D(("_pam_mkargv called: %s",s));
+
+ *argc = 0;
+
+ l = strlen(s);
+ if (l) {
+ if ((sbuf = sbuf_start = _pam_strdup(s)) == NULL) {
+ pam_syslog(NULL, LOG_CRIT,
+ "pam_mkargv: null returned by _pam_strdup");
+ D(("arg NULL"));
+ } else {
+ /* Overkill on the malloc, but not large */
+ argvlen = (l + 1) * ((sizeof(char)) + sizeof(char *));
+ if ((our_argv = argvbuf = malloc(argvlen)) == NULL) {
+ pam_syslog(NULL, LOG_CRIT,
+ "pam_mkargv: null returned by malloc");
+ } else {
+ char *tmp=NULL;
+
+ argvbufp = (char *) argvbuf + (l * sizeof(char *));
+ D(("[%s]",sbuf));
+ while ((sbuf = _pam_StrTok(sbuf, " \n\t", &tmp))) {
+ D(("arg #%d",++count));
+ D(("->[%s]",sbuf));
+ strcpy(argvbufp, sbuf);
+ D(("copied token"));
+ *argvbuf = argvbufp;
+ argvbufp += strlen(argvbufp) + 1;
+ D(("stepped in argvbufp"));
+ (*argc)++;
+ argvbuf++;
+ sbuf = NULL;
+ D(("loop again?"));
+ }
+ }
+ _pam_drop(sbuf_start);
+ }
+ }
+
+ *argv = our_argv;
+
+ D(("_pam_mkargv returned"));
+
+ return(argvlen);
+}
+
+/*
+ * this function is used to protect the modules from accidental or
+ * semi-mallicious harm that an application may do to confuse the API.
+ */
+
+void _pam_sanitize(pam_handle_t *pamh)
+{
+ int old_caller_is = pamh->caller_is;
+
+ /*
+ * this is for security. We reset the auth-tokens here.
+ */
+ __PAM_TO_MODULE(pamh);
+ pam_set_item(pamh, PAM_AUTHTOK, NULL);
+ pam_set_item(pamh, PAM_OLDAUTHTOK, NULL);
+ pamh->caller_is = old_caller_is;
+}
+
+/*
+ * This function scans the array and replaces the _PAM_ACTION_UNDEF
+ * entries with the default action.
+ */
+
+void _pam_set_default_control(int *control_array, int default_action)
+{
+ int i;
+
+ for (i=0; i<_PAM_RETURN_VALUES; ++i) {
+ if (control_array[i] == _PAM_ACTION_UNDEF) {
+ control_array[i] = default_action;
+ }
+ }
+}
+
+/*
+ * This function is used to parse a control string. This string is a
+ * series of tokens of the following form:
+ *
+ * "[ ]*return_code[ ]*=[ ]*action/[ ]".
+ */
+
+#include "pam_tokens.h"
+
+void _pam_parse_control(int *control_array, char *tok)
+{
+ const char *error;
+ int ret;
+
+ while (*tok) {
+ int act, len;
+
+ /* skip leading space */
+ while (isspace((int)*tok) && *++tok);
+ if (!*tok)
+ break;
+
+ /* identify return code */
+ for (ret=0; ret<=_PAM_RETURN_VALUES; ++ret) {
+ len = strlen(_pam_token_returns[ret]);
+ if (!strncmp(_pam_token_returns[ret], tok, len)) {
+ break;
+ }
+ }
+ if (ret > _PAM_RETURN_VALUES || !*(tok += len)) {
+ error = "expecting return value";
+ goto parse_error;
+ }
+
+ /* observe '=' */
+ while (isspace((int)*tok) && *++tok);
+ if (!*tok || *tok++ != '=') {
+ error = "expecting '='";
+ goto parse_error;
+ }
+
+ /* skip leading space */
+ while (isspace((int)*tok) && *++tok);
+ if (!*tok) {
+ error = "expecting action";
+ goto parse_error;
+ }
+
+ /* observe action type */
+ for (act=0; act < (-(_PAM_ACTION_UNDEF)); ++act) {
+ len = strlen(_pam_token_actions[act]);
+ if (!strncmp(_pam_token_actions[act], tok, len)) {
+ act *= -1;
+ tok += len;
+ break;
+ }
+ }
+ if (act > 0) {
+ /*
+ * Either we have a number or we have hit an error. In
+ * principle, there is nothing to stop us accepting
+ * negative offsets. (Although we would have to think of
+ * another way of encoding the tokens.) However, I really
+ * think this would be both hard to administer and easily
+ * cause looping problems. So, for now, we will just
+ * allow forward jumps. (AGM 1998/1/7)
+ */
+ if (!isdigit((int)*tok)) {
+ error = "expecting jump number";
+ goto parse_error;
+ }
+ /* parse a number */
+ act = 0;
+ do {
+ act *= 10;
+ act += *tok - '0'; /* XXX - this assumes ascii behavior */
+ } while (*++tok && isdigit((int)*tok));
+ if (! act) {
+ /* we do not allow 0 jumps. There is a token ('ignore')
+ for that */
+ error = "expecting non-zero";
+ goto parse_error;
+ }
+ }
+
+ /* set control_array element */
+ if (ret != _PAM_RETURN_VALUES) {
+ control_array[ret] = act;
+ } else {
+ /* set the default to 'act' */
+ _pam_set_default_control(control_array, act);
+ }
+ }
+
+ /* that was a success */
+ return;
+
+parse_error:
+ /* treat everything as bad */
+ pam_syslog(NULL, LOG_ERR, "pam_parse: %s; [...%s]", error, tok);
+ for (ret=0; ret<_PAM_RETURN_VALUES; control_array[ret++]=_PAM_ACTION_BAD);
+
+}
diff --git a/libpam/pam_modutil_check_user.c b/libpam/pam_modutil_check_user.c
new file mode 100644
index 0000000..cf1bd1b
--- /dev/null
+++ b/libpam/pam_modutil_check_user.c
@@ -0,0 +1,92 @@
+#include "pam_modutil_private.h"
+#include <security/pam_ext.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+
+int
+pam_modutil_check_user_in_passwd(pam_handle_t *pamh,
+ const char *user_name,
+ const char *file_name)
+{
+ int rc;
+ size_t user_len;
+ FILE *fp;
+ char line[BUFSIZ];
+
+ /* Validate the user name. */
+ if ((user_len = strlen(user_name)) == 0) {
+ pam_syslog(pamh, LOG_NOTICE, "user name is not valid");
+ return PAM_SERVICE_ERR;
+ }
+
+ if (user_len > sizeof(line) - sizeof(":")) {
+ pam_syslog(pamh, LOG_NOTICE, "user name is too long");
+ return PAM_SERVICE_ERR;
+ }
+
+ if (strchr(user_name, ':') != NULL) {
+ /*
+ * "root:x" is not a local user name even if the passwd file
+ * contains a line starting with "root:x:".
+ */
+ return PAM_PERM_DENIED;
+ }
+
+ /* Open the passwd file. */
+ if (file_name == NULL) {
+ file_name = "/etc/passwd";
+ }
+ if ((fp = fopen(file_name, "r")) == NULL) {
+ pam_syslog(pamh, LOG_ERR, "error opening %s: %m", file_name);
+ return PAM_SERVICE_ERR;
+ }
+
+ /*
+ * Scan the file using fgets() instead of fgetpwent_r() because
+ * the latter is not flexible enough in handling long lines
+ * in passwd files.
+ */
+ rc = PAM_PERM_DENIED;
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ size_t line_len;
+ const char *str;
+
+ /*
+ * Does this line start with the user name
+ * followed by a colon?
+ */
+ if (strncmp(user_name, line, user_len) == 0 &&
+ line[user_len] == ':') {
+ rc = PAM_SUCCESS;
+ /*
+ * Continue reading the file to avoid timing attacks.
+ */
+ }
+ /* Has a newline been read? */
+ line_len = strlen(line);
+ if (line_len < sizeof(line) - 1 ||
+ line[line_len - 1] == '\n') {
+ /* Yes, continue with the next line. */
+ continue;
+ }
+
+ /* No, read till the end of this line first. */
+ while ((str = fgets(line, sizeof(line), fp)) != NULL) {
+ line_len = strlen(line);
+ if (line_len == 0 ||
+ line[line_len - 1] == '\n') {
+ break;
+ }
+ }
+ if (str == NULL) {
+ /* fgets returned NULL, we are done. */
+ break;
+ }
+ /* Continue with the next line. */
+ }
+
+ fclose(fp);
+ return rc;
+}
diff --git a/libpam/pam_modutil_cleanup.c b/libpam/pam_modutil_cleanup.c
new file mode 100644
index 0000000..8224ce6
--- /dev/null
+++ b/libpam/pam_modutil_cleanup.c
@@ -0,0 +1,19 @@
+/*
+ * $Id$
+ *
+ * This function provides a common pam_set_data() friendly version of free().
+ */
+
+#include "pam_modutil_private.h"
+
+#include <stdlib.h>
+
+void
+pam_modutil_cleanup (pam_handle_t *pamh UNUSED, void *data,
+ int error_status UNUSED)
+{
+ if (data) {
+ /* junk it */
+ (void) free(data);
+ }
+}
diff --git a/libpam/pam_modutil_getgrgid.c b/libpam/pam_modutil_getgrgid.c
new file mode 100644
index 0000000..386d6f4
--- /dev/null
+++ b/libpam/pam_modutil_getgrgid.c
@@ -0,0 +1,138 @@
+/*
+ * $Id$
+ *
+ * This function provides a thread safer version of getgrgid() for use
+ * with PAM modules that care about this sort of thing.
+ *
+ * XXX - or at least it should provide a thread-safe alternative.
+ */
+
+#include "pam_modutil_private.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <grp.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static int intlen(int number)
+{
+ int len = 2;
+ while (number != 0) {
+ number /= 10;
+ len++;
+ }
+ return len;
+}
+
+static int longlen(long number)
+{
+ int len = 2;
+ while (number != 0) {
+ number /= 10;
+ len++;
+ }
+ return len;
+}
+
+struct group *
+pam_modutil_getgrgid(pam_handle_t *pamh, gid_t gid)
+{
+#ifdef HAVE_GETGRGID_R
+
+ void *buffer=NULL;
+ size_t length = PWD_INITIAL_LENGTH;
+
+ do {
+ int status;
+ void *new_buffer;
+ struct group *result = NULL;
+
+ new_buffer = realloc(buffer, sizeof(struct group) + length);
+ if (new_buffer == NULL) {
+
+ D(("out of memory"));
+
+ /* no memory for the user - so delete the memory */
+ if (buffer) {
+ free(buffer);
+ }
+ return NULL;
+ }
+ buffer = new_buffer;
+
+ /* make the re-entrant call to get the grp structure */
+ errno = 0;
+ status = getgrgid_r(gid, buffer,
+ sizeof(struct group) + (char *) buffer,
+ length, &result);
+ if (!status && (result == buffer)) {
+ char *data_name;
+ const void *ignore;
+ int i;
+
+ data_name = malloc(strlen("_pammodutil_getgrgid") + 1 +
+ longlen((long)gid) + 1 + intlen(INT_MAX) + 1);
+ if ((pamh != NULL) && (data_name == NULL)) {
+ D(("was unable to register the data item [%s]",
+ pam_strerror(pamh, status)));
+ free(buffer);
+ return NULL;
+ }
+
+ if (pamh != NULL) {
+ for (i = 0; i < INT_MAX; i++) {
+ sprintf(data_name, "_pammodutil_getgrgid_%ld_%d",
+ (long) gid, i);
+ status = PAM_NO_MODULE_DATA;
+ if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) {
+ status = pam_set_data(pamh, data_name,
+ result, pam_modutil_cleanup);
+ }
+ if (status == PAM_SUCCESS) {
+ break;
+ }
+ }
+ } else {
+ status = PAM_SUCCESS;
+ }
+
+ free(data_name);
+
+ if (status == PAM_SUCCESS) {
+ D(("success"));
+ return result;
+ }
+
+ D(("was unable to register the data item [%s]",
+ pam_strerror(pamh, status)));
+
+ free(buffer);
+ return NULL;
+
+ } else if (errno != ERANGE && errno != EINTR) {
+ /* no sense in repeating the call */
+ break;
+ }
+
+ length <<= PWD_LENGTH_SHIFT;
+
+ } while (length < PWD_ABSURD_PWD_LENGTH);
+
+ D(("grp structure took %u bytes or so of memory",
+ length+sizeof(struct group)));
+
+ free(buffer);
+ return NULL;
+
+#else /* ie. ifndef HAVE_GETGRGID_R */
+
+ /*
+ * Sorry, there does not appear to be a reentrant version of
+ * getgrgid(). So, we use the standard libc function.
+ */
+
+ return getgrgid(gid);
+
+#endif /* def HAVE_GETGRGID_R */
+}
diff --git a/libpam/pam_modutil_getgrnam.c b/libpam/pam_modutil_getgrnam.c
new file mode 100644
index 0000000..cbb1551
--- /dev/null
+++ b/libpam/pam_modutil_getgrnam.c
@@ -0,0 +1,127 @@
+/*
+ * $Id$
+ *
+ * This function provides a thread safer version of getgrnam() for use
+ * with PAM modules that care about this sort of thing.
+ *
+ * XXX - or at least it should provide a thread-safe alternative.
+ */
+
+#include "pam_modutil_private.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <grp.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static int intlen(int number)
+{
+ int len = 2;
+ while (number != 0) {
+ number /= 10;
+ len++;
+ }
+ return len;
+}
+
+struct group *
+pam_modutil_getgrnam(pam_handle_t *pamh, const char *group)
+{
+#ifdef HAVE_GETGRNAM_R
+
+ void *buffer=NULL;
+ size_t length = PWD_INITIAL_LENGTH;
+
+ do {
+ int status;
+ void *new_buffer;
+ struct group *result = NULL;
+
+ new_buffer = realloc(buffer, sizeof(struct group) + length);
+ if (new_buffer == NULL) {
+
+ D(("out of memory"));
+
+ /* no memory for the group - so delete the memory */
+ if (buffer) {
+ free(buffer);
+ }
+ return NULL;
+ }
+ buffer = new_buffer;
+
+ /* make the re-entrant call to get the grp structure */
+ errno = 0;
+ status = getgrnam_r(group, buffer,
+ sizeof(struct group) + (char *) buffer,
+ length, &result);
+ if (!status && (result == buffer)) {
+ char *data_name;
+ const void *ignore;
+ int i;
+
+ data_name = malloc(strlen("_pammodutil_getgrnam") + 1 +
+ strlen(group) + 1 + intlen(INT_MAX) + 1);
+ if ((pamh != NULL) && (data_name == NULL)) {
+ D(("was unable to register the data item [%s]",
+ pam_strerror(pamh, status)));
+ free(buffer);
+ return NULL;
+ }
+
+ if (pamh != NULL) {
+ for (i = 0; i < INT_MAX; i++) {
+ sprintf(data_name, "_pammodutil_getgrnam_%s_%d", group, i);
+ status = PAM_NO_MODULE_DATA;
+ if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) {
+ status = pam_set_data(pamh, data_name,
+ result, pam_modutil_cleanup);
+ }
+ if (status == PAM_SUCCESS) {
+ break;
+ }
+ }
+ } else {
+ status = PAM_SUCCESS;
+ }
+
+ free(data_name);
+
+ if (status == PAM_SUCCESS) {
+ D(("success"));
+ return result;
+ }
+
+ D(("was unable to register the data item [%s]",
+ pam_strerror(pamh, status)));
+
+ free(buffer);
+ return NULL;
+
+ } else if (errno != ERANGE && errno != EINTR) {
+ /* no sense in repeating the call */
+ break;
+ }
+
+ length <<= PWD_LENGTH_SHIFT;
+
+ } while (length < PWD_ABSURD_PWD_LENGTH);
+
+ D(("grp structure took %u bytes or so of memory",
+ length+sizeof(struct group)));
+
+ free(buffer);
+ return NULL;
+
+#else /* ie. ifndef HAVE_GETGRNAM_R */
+
+ /*
+ * Sorry, there does not appear to be a reentrant version of
+ * getgrnam(). So, we use the standard libc function.
+ */
+
+ return getgrnam(group);
+
+#endif /* def HAVE_GETGRNAM_R */
+}
diff --git a/libpam/pam_modutil_getlogin.c b/libpam/pam_modutil_getlogin.c
new file mode 100644
index 0000000..04a20fd
--- /dev/null
+++ b/libpam/pam_modutil_getlogin.c
@@ -0,0 +1,80 @@
+/*
+ * $Id$
+ *
+ * A central point for invoking getlogin(). Hopefully, this is a
+ * little harder to spoof than all the other versions that are out
+ * there.
+ */
+
+#include "pam_modutil_private.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <utmp.h>
+
+#define _PAMMODUTIL_GETLOGIN "_pammodutil_getlogin"
+
+const char *
+pam_modutil_getlogin(pam_handle_t *pamh)
+{
+ int status;
+ const void *logname;
+ const void *void_curr_tty;
+ const char *curr_tty;
+ char *curr_user;
+ struct utmp *ut, line;
+
+ status = pam_get_data(pamh, _PAMMODUTIL_GETLOGIN, &logname);
+ if (status == PAM_SUCCESS) {
+ return logname;
+ }
+
+ status = pam_get_item(pamh, PAM_TTY, &void_curr_tty);
+ if ((status != PAM_SUCCESS) || (void_curr_tty == NULL))
+ curr_tty = ttyname(0);
+ else
+ curr_tty = (const char*)void_curr_tty;
+
+ if (curr_tty == NULL) {
+ return NULL;
+ }
+
+ if (curr_tty[0] == '/') { /* full path */
+ const char *t;
+ curr_tty++;
+ if ((t = strchr(curr_tty, '/')) != NULL) {
+ curr_tty = t + 1;
+ }
+ }
+ logname = NULL;
+
+ setutent();
+ strncpy(line.ut_line, curr_tty, sizeof(line.ut_line));
+
+ if ((ut = getutline(&line)) == NULL) {
+ goto clean_up_and_go_home;
+ }
+
+ curr_user = calloc(sizeof(line.ut_user)+1, 1);
+ if (curr_user == NULL) {
+ goto clean_up_and_go_home;
+ }
+
+ strncpy(curr_user, ut->ut_user, sizeof(ut->ut_user));
+ /* calloc already zeroed the memory */
+
+ status = pam_set_data(pamh, _PAMMODUTIL_GETLOGIN, curr_user,
+ pam_modutil_cleanup);
+ if (status != PAM_SUCCESS) {
+ free(curr_user);
+ goto clean_up_and_go_home;
+ }
+
+ logname = curr_user;
+
+clean_up_and_go_home:
+
+ endutent();
+
+ return logname;
+}
diff --git a/libpam/pam_modutil_getpwnam.c b/libpam/pam_modutil_getpwnam.c
new file mode 100644
index 0000000..8132c76
--- /dev/null
+++ b/libpam/pam_modutil_getpwnam.c
@@ -0,0 +1,127 @@
+/*
+ * $Id$
+ *
+ * This function provides a thread safer version of getpwnam() for use
+ * with PAM modules that care about this sort of thing.
+ *
+ * XXX - or at least it should provide a thread-safe alternative.
+ */
+
+#include "pam_modutil_private.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static int intlen(int number)
+{
+ int len = 2;
+ while (number != 0) {
+ number /= 10;
+ len++;
+ }
+ return len;
+}
+
+struct passwd *
+pam_modutil_getpwnam(pam_handle_t *pamh, const char *user)
+{
+#ifdef HAVE_GETPWNAM_R
+
+ void *buffer=NULL;
+ size_t length = PWD_INITIAL_LENGTH;
+
+ do {
+ int status;
+ void *new_buffer;
+ struct passwd *result = NULL;
+
+ new_buffer = realloc(buffer, sizeof(struct passwd) + length);
+ if (new_buffer == NULL) {
+
+ D(("out of memory"));
+
+ /* no memory for the user - so delete the memory */
+ if (buffer) {
+ free(buffer);
+ }
+ return NULL;
+ }
+ buffer = new_buffer;
+
+ /* make the re-entrant call to get the pwd structure */
+ errno = 0;
+ status = getpwnam_r(user, buffer,
+ sizeof(struct passwd) + (char *) buffer,
+ length, &result);
+ if (!status && (result == buffer)) {
+ char *data_name;
+ const void *ignore;
+ int i;
+
+ data_name = malloc(strlen("_pammodutil_getpwnam") + 1 +
+ strlen(user) + 1 + intlen(INT_MAX) + 1);
+ if ((pamh != NULL) && (data_name == NULL)) {
+ D(("was unable to register the data item [%s]",
+ pam_strerror(pamh, status)));
+ free(buffer);
+ return NULL;
+ }
+
+ if (pamh != NULL) {
+ for (i = 0; i < INT_MAX; i++) {
+ sprintf(data_name, "_pammodutil_getpwnam_%s_%d", user, i);
+ status = PAM_NO_MODULE_DATA;
+ if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) {
+ status = pam_set_data(pamh, data_name,
+ result, pam_modutil_cleanup);
+ }
+ if (status == PAM_SUCCESS) {
+ break;
+ }
+ }
+ } else {
+ status = PAM_SUCCESS;
+ }
+
+ free(data_name);
+
+ if (status == PAM_SUCCESS) {
+ D(("success"));
+ return result;
+ }
+
+ D(("was unable to register the data item [%s]",
+ pam_strerror(pamh, status)));
+
+ free(buffer);
+ return NULL;
+
+ } else if (errno != ERANGE && errno != EINTR) {
+ /* no sense in repeating the call */
+ break;
+ }
+
+ length <<= PWD_LENGTH_SHIFT;
+
+ } while (length < PWD_ABSURD_PWD_LENGTH);
+
+ D(("pwd structure took %u bytes or so of memory",
+ length+sizeof(struct passwd)));
+
+ free(buffer);
+ return NULL;
+
+#else /* ie. ifndef HAVE_GETPWNAM_R */
+
+ /*
+ * Sorry, there does not appear to be a reentrant version of
+ * getpwnam(). So, we use the standard libc function.
+ */
+
+ return getpwnam(user);
+
+#endif /* def HAVE_GETPWNAM_R */
+}
diff --git a/libpam/pam_modutil_getpwuid.c b/libpam/pam_modutil_getpwuid.c
new file mode 100644
index 0000000..3a43593
--- /dev/null
+++ b/libpam/pam_modutil_getpwuid.c
@@ -0,0 +1,138 @@
+/*
+ * $Id$
+ *
+ * This function provides a thread safer version of getpwuid() for use
+ * with PAM modules that care about this sort of thing.
+ *
+ * XXX - or at least it should provide a thread-safe alternative.
+ */
+
+#include "pam_modutil_private.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static int intlen(int number)
+{
+ int len = 2;
+ while (number != 0) {
+ number /= 10;
+ len++;
+ }
+ return len;
+}
+
+static int longlen(long number)
+{
+ int len = 2;
+ while (number != 0) {
+ number /= 10;
+ len++;
+ }
+ return len;
+}
+
+struct passwd *
+pam_modutil_getpwuid(pam_handle_t *pamh, uid_t uid)
+{
+#ifdef HAVE_GETPWUID_R
+
+ void *buffer=NULL;
+ size_t length = PWD_INITIAL_LENGTH;
+
+ do {
+ int status;
+ void *new_buffer;
+ struct passwd *result = NULL;
+
+ new_buffer = realloc(buffer, sizeof(struct passwd) + length);
+ if (new_buffer == NULL) {
+
+ D(("out of memory"));
+
+ /* no memory for the user - so delete the memory */
+ if (buffer) {
+ free(buffer);
+ }
+ return NULL;
+ }
+ buffer = new_buffer;
+
+ /* make the re-entrant call to get the pwd structure */
+ errno = 0;
+ status = getpwuid_r(uid, buffer,
+ sizeof(struct passwd) + (char *) buffer,
+ length, &result);
+ if (!status && (result == buffer)) {
+ char *data_name;
+ const void *ignore;
+ int i;
+
+ data_name = malloc(strlen("_pammodutil_getpwuid") + 1 +
+ longlen((long) uid) + 1 + intlen(INT_MAX) + 1);
+ if ((pamh != NULL) && (data_name == NULL)) {
+ D(("was unable to register the data item [%s]",
+ pam_strerror(pamh, status)));
+ free(buffer);
+ return NULL;
+ }
+
+ if (pamh != NULL) {
+ for (i = 0; i < INT_MAX; i++) {
+ sprintf(data_name, "_pammodutil_getpwuid_%ld_%d",
+ (long) uid, i);
+ status = PAM_NO_MODULE_DATA;
+ if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) {
+ status = pam_set_data(pamh, data_name,
+ result, pam_modutil_cleanup);
+ }
+ if (status == PAM_SUCCESS) {
+ break;
+ }
+ }
+ } else {
+ status = PAM_SUCCESS;
+ }
+
+ free(data_name);
+
+ if (status == PAM_SUCCESS) {
+ D(("success"));
+ return result;
+ }
+
+ D(("was unable to register the data item [%s]",
+ pam_strerror(pamh, status)));
+
+ free(buffer);
+ return NULL;
+
+ } else if (errno != ERANGE && errno != EINTR) {
+ /* no sense in repeating the call */
+ break;
+ }
+
+ length <<= PWD_LENGTH_SHIFT;
+
+ } while (length < PWD_ABSURD_PWD_LENGTH);
+
+ D(("pwd structure took %u bytes or so of memory",
+ length+sizeof(struct passwd)));
+
+ free(buffer);
+ return NULL;
+
+#else /* ie. ifndef HAVE_GETPWUID_R */
+
+ /*
+ * Sorry, there does not appear to be a reentrant version of
+ * getpwuid(). So, we use the standard libc function.
+ */
+
+ return getpwuid(uid);
+
+#endif /* def HAVE_GETPWUID_R */
+}
diff --git a/libpam/pam_modutil_getspnam.c b/libpam/pam_modutil_getspnam.c
new file mode 100644
index 0000000..032709e
--- /dev/null
+++ b/libpam/pam_modutil_getspnam.c
@@ -0,0 +1,127 @@
+/*
+ * $Id$
+ *
+ * This function provides a thread safer version of getspnam() for use
+ * with PAM modules that care about this sort of thing.
+ *
+ * XXX - or at least it should provide a thread-safe alternative.
+ */
+
+#include "pam_modutil_private.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <shadow.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static int intlen(int number)
+{
+ int len = 2;
+ while (number != 0) {
+ number /= 10;
+ len++;
+ }
+ return len;
+}
+
+struct spwd *
+pam_modutil_getspnam(pam_handle_t *pamh, const char *user)
+{
+#ifdef HAVE_GETSPNAM_R
+
+ void *buffer=NULL;
+ size_t length = PWD_INITIAL_LENGTH;
+
+ do {
+ int status;
+ void *new_buffer;
+ struct spwd *result = NULL;
+
+ new_buffer = realloc(buffer, sizeof(struct spwd) + length);
+ if (new_buffer == NULL) {
+
+ D(("out of memory"));
+
+ /* no memory for the user - so delete the memory */
+ if (buffer) {
+ free(buffer);
+ }
+ return NULL;
+ }
+ buffer = new_buffer;
+
+ /* make the re-entrant call to get the spwd structure */
+ errno = 0;
+ status = getspnam_r(user, buffer,
+ sizeof(struct spwd) + (char *) buffer,
+ length, &result);
+ if (!status && (result == buffer)) {
+ char *data_name;
+ const void *ignore;
+ int i;
+
+ data_name = malloc(strlen("_pammodutil_getspnam") + 1 +
+ strlen(user) + 1 + intlen(INT_MAX) + 1);
+ if ((pamh != NULL) && (data_name == NULL)) {
+ D(("was unable to register the data item [%s]",
+ pam_strerror(pamh, status)));
+ free(buffer);
+ return NULL;
+ }
+
+ if (pamh != NULL) {
+ for (i = 0; i < INT_MAX; i++) {
+ sprintf(data_name, "_pammodutil_getspnam_%s_%d", user, i);
+ status = PAM_NO_MODULE_DATA;
+ if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) {
+ status = pam_set_data(pamh, data_name,
+ result, pam_modutil_cleanup);
+ }
+ if (status == PAM_SUCCESS) {
+ break;
+ }
+ }
+ } else {
+ status = PAM_SUCCESS;
+ }
+
+ free(data_name);
+
+ if (status == PAM_SUCCESS) {
+ D(("success"));
+ return result;
+ }
+
+ D(("was unable to register the data item [%s]",
+ pam_strerror(pamh, status)));
+
+ free(buffer);
+ return NULL;
+
+ } else if (errno != ERANGE && errno != EINTR) {
+ /* no sense in repeating the call */
+ break;
+ }
+
+ length <<= PWD_LENGTH_SHIFT;
+
+ } while (length < PWD_ABSURD_PWD_LENGTH);
+
+ D(("spwd structure took %u bytes or so of memory",
+ length+sizeof(struct spwd)));
+
+ free(buffer);
+ return NULL;
+
+#else /* ie. ifndef HAVE_GETSPNAM_R */
+
+ /*
+ * Sorry, there does not appear to be a reentrant version of
+ * getspnam(). So, we use the standard libc function.
+ */
+
+ return getspnam(user);
+
+#endif /* def HAVE_GETSPNAM_R */
+}
diff --git a/libpam/pam_modutil_ingroup.c b/libpam/pam_modutil_ingroup.c
new file mode 100644
index 0000000..356302e
--- /dev/null
+++ b/libpam/pam_modutil_ingroup.c
@@ -0,0 +1,130 @@
+/*
+ * $Id$
+ *
+ * This function provides common methods for checking if a user is in a
+ * specified group.
+ */
+
+#include "pam_modutil_private.h"
+
+#include <stdlib.h>
+#include <pwd.h>
+#include <grp.h>
+
+#ifdef HAVE_GETGROUPLIST
+
+#define NGROUPS_MIN 100
+#define NGROUPS_MAX 65536
+
+static int checkgrouplist(const char *user, gid_t primary, gid_t target)
+{
+ int ngroups, pgroups, i;
+
+ ngroups = NGROUPS_MIN;
+ do {
+ gid_t *grouplist;
+
+ pgroups = ngroups;
+ grouplist = malloc(sizeof(gid_t) * ngroups);
+ if (grouplist == NULL) {
+ return 0;
+ }
+ i = getgrouplist(user, primary, grouplist, &ngroups);
+ if (i >= 0) {
+ for (i = 0; i < ngroups; i++) {
+ if (grouplist[i] == target) {
+ free(grouplist);
+ return 1;
+ }
+ }
+ }
+ free(grouplist);
+ } while (i < 0 && ngroups > 0 && ngroups != pgroups && ngroups <= NGROUPS_MAX);
+ return 0;
+}
+#endif
+
+static int
+pam_modutil_user_in_group_common(pam_handle_t *pamh UNUSED,
+ struct passwd *pwd,
+ struct group *grp)
+{
+ int i;
+
+ if (pwd == NULL) {
+ return 0;
+ }
+ if (grp == NULL) {
+ return 0;
+ }
+
+ if (pwd->pw_gid == grp->gr_gid) {
+ return 1;
+ }
+
+ for (i = 0; (grp->gr_mem != NULL) && (grp->gr_mem[i] != NULL); i++) {
+ if (strcmp(pwd->pw_name, grp->gr_mem[i]) == 0) {
+ return 1;
+ }
+ }
+
+#ifdef HAVE_GETGROUPLIST
+ if (checkgrouplist(pwd->pw_name, pwd->pw_gid, grp->gr_gid)) {
+ return 1;
+ }
+#endif
+
+ return 0;
+}
+
+int
+pam_modutil_user_in_group_nam_nam(pam_handle_t *pamh,
+ const char *user, const char *group)
+{
+ struct passwd *pwd;
+ struct group *grp;
+
+ pwd = pam_modutil_getpwnam(pamh, user);
+ grp = pam_modutil_getgrnam(pamh, group);
+
+ return pam_modutil_user_in_group_common(pamh, pwd, grp);
+}
+
+int
+pam_modutil_user_in_group_nam_gid(pam_handle_t *pamh,
+ const char *user, gid_t group)
+{
+ struct passwd *pwd;
+ struct group *grp;
+
+ pwd = pam_modutil_getpwnam(pamh, user);
+ grp = pam_modutil_getgrgid(pamh, group);
+
+ return pam_modutil_user_in_group_common(pamh, pwd, grp);
+}
+
+int
+pam_modutil_user_in_group_uid_nam(pam_handle_t *pamh,
+ uid_t user, const char *group)
+{
+ struct passwd *pwd;
+ struct group *grp;
+
+ pwd = pam_modutil_getpwuid(pamh, user);
+ grp = pam_modutil_getgrnam(pamh, group);
+
+ return pam_modutil_user_in_group_common(pamh, pwd, grp);
+}
+
+int
+pam_modutil_user_in_group_uid_gid(pam_handle_t *pamh,
+ uid_t user, gid_t group)
+{
+ struct passwd *pwd;
+ struct group *grp;
+
+ pwd = pam_modutil_getpwuid(pamh, user);
+ grp = pam_modutil_getgrgid(pamh, group);
+
+ return pam_modutil_user_in_group_common(pamh, pwd, grp);
+}
diff --git a/libpam/pam_modutil_ioloop.c b/libpam/pam_modutil_ioloop.c
new file mode 100644
index 0000000..54ab0e5
--- /dev/null
+++ b/libpam/pam_modutil_ioloop.c
@@ -0,0 +1,53 @@
+/*
+ * $Id$
+ *
+ * These functions provides common methods for ensure a complete read or
+ * write occurs. It handles EINTR and partial read/write returns.
+ */
+
+#include "pam_modutil_private.h"
+
+#include <unistd.h>
+#include <errno.h>
+
+int
+pam_modutil_read(int fd, char *buffer, int count)
+{
+ int block, offset = 0;
+
+ while (count > 0) {
+ block = read(fd, &buffer[offset], count);
+
+ if (block < 0) {
+ if (errno == EINTR) continue;
+ return block;
+ }
+ if (block == 0) return offset;
+
+ offset += block;
+ count -= block;
+ }
+
+ return offset;
+}
+
+int
+pam_modutil_write(int fd, const char *buffer, int count)
+{
+ int block, offset = 0;
+
+ while (count > 0) {
+ block = write(fd, &buffer[offset], count);
+
+ if (block < 0) {
+ if (errno == EINTR) continue;
+ return block;
+ }
+ if (block == 0) return offset;
+
+ offset += block;
+ count -= block;
+ }
+
+ return offset;
+}
diff --git a/libpam/pam_modutil_priv.c b/libpam/pam_modutil_priv.c
new file mode 100644
index 0000000..a463e06
--- /dev/null
+++ b/libpam/pam_modutil_priv.c
@@ -0,0 +1,179 @@
+/*
+ * $Id$
+ *
+ * This file provides two functions:
+ * pam_modutil_drop_priv:
+ * temporarily lower process fs privileges by switching to another uid/gid,
+ * pam_modutil_regain_priv:
+ * regain process fs privileges lowered by pam_modutil_drop_priv().
+ */
+
+#include "pam_modutil_private.h"
+#include <security/pam_ext.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/fsuid.h>
+
+/*
+ * Two setfsuid() calls in a row are necessary to check
+ * whether setfsuid() succeeded or not.
+ */
+static int change_uid(uid_t uid, uid_t *save)
+{
+ uid_t tmp = setfsuid(uid);
+ if (save)
+ *save = tmp;
+ return (uid_t) setfsuid(uid) == uid ? 0 : -1;
+}
+static int change_gid(gid_t gid, gid_t *save)
+{
+ gid_t tmp = setfsgid(gid);
+ if (save)
+ *save = tmp;
+ return (gid_t) setfsgid(gid) == gid ? 0 : -1;
+}
+
+static int cleanup(struct pam_modutil_privs *p)
+{
+ if (p->allocated) {
+ p->allocated = 0;
+ free(p->grplist);
+ }
+ p->grplist = NULL;
+ p->number_of_groups = 0;
+ return -1;
+}
+
+#define PRIV_MAGIC 0x1004000a
+#define PRIV_MAGIC_DONOTHING 0xdead000a
+
+int pam_modutil_drop_priv(pam_handle_t *pamh,
+ struct pam_modutil_privs *p,
+ const struct passwd *pw)
+{
+ int res;
+
+ if (p->is_dropped) {
+ pam_syslog(pamh, LOG_CRIT,
+ "pam_modutil_drop_priv: called with dropped privileges");
+ return -1;
+ }
+
+ /*
+ * If not root, we can do nothing.
+ * If switching to root, we have nothing to do.
+ * That is, in both cases, we do not care.
+ */
+ if (geteuid() != 0 || pw->pw_uid == 0) {
+ p->is_dropped = PRIV_MAGIC_DONOTHING;
+ return 0;
+ }
+
+ if (!p->grplist || p->number_of_groups <= 0) {
+ pam_syslog(pamh, LOG_CRIT,
+ "pam_modutil_drop_priv: called without room for supplementary groups");
+ return -1;
+ }
+ res = getgroups(0, NULL);
+ if (res < 0) {
+ pam_syslog(pamh, LOG_ERR,
+ "pam_modutil_drop_priv: getgroups failed: %m");
+ return -1;
+ }
+
+ p->allocated = 0;
+ if (res > p->number_of_groups) {
+ p->grplist = calloc(res, sizeof(gid_t));
+ if (!p->grplist) {
+ pam_syslog(pamh, LOG_CRIT, "out of memory");
+ return cleanup(p);
+ }
+ p->allocated = 1;
+ p->number_of_groups = res;
+ }
+
+ res = getgroups(p->number_of_groups, p->grplist);
+ if (res < 0) {
+ pam_syslog(pamh, LOG_ERR,
+ "pam_modutil_drop_priv: getgroups failed: %m");
+ return cleanup(p);
+ }
+
+ p->number_of_groups = res;
+
+ /*
+ * We should care to leave process credentials in consistent state.
+ * That is, e.g. if change_gid() succeeded but change_uid() failed,
+ * we should try to restore old gid.
+ *
+ * We try to add the supplementary groups on a best-effort
+ * basis. If it fails, it's not fatal: we fall back to using an
+ * empty list.
+ */
+ if (initgroups(pw->pw_name, pw->pw_gid)) {
+ pam_syslog(pamh, LOG_WARNING,
+ "pam_modutil_drop_priv: initgroups failed: %m");
+
+ if (setgroups(0, NULL)) {
+ pam_syslog(pamh, LOG_ERR,
+ "pam_modutil_drop_priv: setgroups failed: %m");
+ return cleanup(p);
+ }
+ }
+ if (change_gid(pw->pw_gid, &p->old_gid)) {
+ pam_syslog(pamh, LOG_ERR,
+ "pam_modutil_drop_priv: change_gid failed: %m");
+ (void) setgroups(p->number_of_groups, p->grplist);
+ return cleanup(p);
+ }
+ if (change_uid(pw->pw_uid, &p->old_uid)) {
+ pam_syslog(pamh, LOG_ERR,
+ "pam_modutil_drop_priv: change_uid failed: %m");
+ (void) change_gid(p->old_gid, NULL);
+ (void) setgroups(p->number_of_groups, p->grplist);
+ return cleanup(p);
+ }
+
+ p->is_dropped = PRIV_MAGIC;
+ return 0;
+}
+
+int pam_modutil_regain_priv(pam_handle_t *pamh,
+ struct pam_modutil_privs *p)
+{
+ switch (p->is_dropped) {
+ case PRIV_MAGIC_DONOTHING:
+ p->is_dropped = 0;
+ return 0;
+
+ case PRIV_MAGIC:
+ break;
+
+ default:
+ pam_syslog(pamh, LOG_CRIT,
+ "pam_modutil_regain_priv: called with invalid state");
+ return -1;
+ }
+
+ if (change_uid(p->old_uid, NULL)) {
+ pam_syslog(pamh, LOG_ERR,
+ "pam_modutil_regain_priv: change_uid failed: %m");
+ return cleanup(p);
+ }
+ if (change_gid(p->old_gid, NULL)) {
+ pam_syslog(pamh, LOG_ERR,
+ "pam_modutil_regain_priv: change_gid failed: %m");
+ return cleanup(p);
+ }
+ if (setgroups(p->number_of_groups, p->grplist)) {
+ pam_syslog(pamh, LOG_ERR,
+ "pam_modutil_regain_priv: setgroups failed: %m");
+ return cleanup(p);
+ }
+
+ p->is_dropped = 0;
+ cleanup(p);
+ return 0;
+}
diff --git a/libpam/pam_modutil_private.h b/libpam/pam_modutil_private.h
new file mode 100644
index 0000000..98a30f6
--- /dev/null
+++ b/libpam/pam_modutil_private.h
@@ -0,0 +1,24 @@
+#ifndef PAMMODUTIL_PRIVATE_H
+#define PAMMODUTIL_PRIVATE_H
+
+/*
+ * $Id$
+ *
+ * Copyright (c) 2001 Andrew Morgan <morgan@kernel.org>
+ */
+
+#include "config.h"
+
+#include <security/_pam_macros.h>
+#include <security/pam_modules.h>
+#include <security/pam_modutil.h>
+
+#define PWD_INITIAL_LENGTH 0x400
+#define PWD_ABSURD_PWD_LENGTH 0x40001
+#define PWD_LENGTH_SHIFT 4 /* 2^4 == 16 */
+
+extern void
+pam_modutil_cleanup(pam_handle_t *pamh, void *data,
+ int error_status);
+
+#endif /* PAMMODUTIL_PRIVATE_H */
diff --git a/libpam/pam_modutil_sanitize.c b/libpam/pam_modutil_sanitize.c
new file mode 100644
index 0000000..f26e8ec
--- /dev/null
+++ b/libpam/pam_modutil_sanitize.c
@@ -0,0 +1,147 @@
+/*
+ * This file implements the following functions:
+ * pam_modutil_sanitize_helper_fds:
+ * redirects standard descriptors, closes all other descriptors.
+ */
+
+#include "pam_modutil_private.h"
+#include <security/pam_ext.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/resource.h>
+
+/*
+ * Creates a pipe, closes its write end, redirects fd to its read end.
+ * Returns fd on success, -1 otherwise.
+ */
+static int
+redirect_in_pipe(pam_handle_t *pamh, int fd, const char *name)
+{
+ int in[2];
+
+ if (pipe(in) < 0) {
+ pam_syslog(pamh, LOG_ERR, "Could not create pipe: %m");
+ return -1;
+ }
+
+ close(in[1]);
+
+ if (in[0] == fd)
+ return fd;
+
+ if (dup2(in[0], fd) != fd) {
+ pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", name);
+ fd = -1;
+ }
+
+ close(in[0]);
+ return fd;
+}
+
+/*
+ * Opens /dev/null for writing, redirects fd there.
+ * Returns fd on success, -1 otherwise.
+ */
+static int
+redirect_out_null(pam_handle_t *pamh, int fd, const char *name)
+{
+ int null = open("/dev/null", O_WRONLY);
+
+ if (null < 0) {
+ pam_syslog(pamh, LOG_ERR, "open of %s failed: %m", "/dev/null");
+ return -1;
+ }
+
+ if (null == fd)
+ return fd;
+
+ if (dup2(null, fd) != fd) {
+ pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", name);
+ fd = -1;
+ }
+
+ close(null);
+ return fd;
+}
+
+static int
+redirect_out(pam_handle_t *pamh, enum pam_modutil_redirect_fd mode,
+ int fd, const char *name)
+{
+ switch (mode) {
+ case PAM_MODUTIL_PIPE_FD:
+ if (redirect_in_pipe(pamh, fd, name) < 0)
+ return -1;
+ break;
+ case PAM_MODUTIL_NULL_FD:
+ if (redirect_out_null(pamh, fd, name) < 0)
+ return -1;
+ break;
+ case PAM_MODUTIL_IGNORE_FD:
+ break;
+ }
+ return fd;
+}
+
+/* Closes all descriptors after stderr. */
+static void
+close_fds(void)
+{
+ /*
+ * An arbitrary upper limit for the maximum file descriptor number
+ * returned by RLIMIT_NOFILE.
+ */
+ const int MAX_FD_NO = 65535;
+
+ /* The lower limit is the same as for _POSIX_OPEN_MAX. */
+ const int MIN_FD_NO = 20;
+
+ int fd;
+ struct rlimit rlim;
+
+ if (getrlimit(RLIMIT_NOFILE, &rlim) || rlim.rlim_max > (rlim_t)MAX_FD_NO)
+ fd = MAX_FD_NO;
+ else if (rlim.rlim_max < (rlim_t)MIN_FD_NO)
+ fd = MIN_FD_NO;
+ else
+ fd = (int)rlim.rlim_max - 1;
+
+ for (; fd > STDERR_FILENO; --fd)
+ close(fd);
+}
+
+int
+pam_modutil_sanitize_helper_fds(pam_handle_t *pamh,
+ enum pam_modutil_redirect_fd stdin_mode,
+ enum pam_modutil_redirect_fd stdout_mode,
+ enum pam_modutil_redirect_fd stderr_mode)
+{
+ if (stdin_mode != PAM_MODUTIL_IGNORE_FD &&
+ redirect_in_pipe(pamh, STDIN_FILENO, "stdin") < 0) {
+ return -1;
+ }
+
+ if (redirect_out(pamh, stdout_mode, STDOUT_FILENO, "stdout") < 0)
+ return -1;
+
+ /*
+ * If stderr should not be ignored and
+ * redirect mode for stdout and stderr are the same,
+ * optimize by redirecting stderr to stdout.
+ */
+ if (stderr_mode != PAM_MODUTIL_IGNORE_FD &&
+ stdout_mode == stderr_mode) {
+ if (dup2(STDOUT_FILENO, STDERR_FILENO) != STDERR_FILENO) {
+ pam_syslog(pamh, LOG_ERR,
+ "dup2 of %s failed: %m", "stderr");
+ return -1;
+ }
+ } else {
+ if (redirect_out(pamh, stderr_mode, STDERR_FILENO, "stderr") < 0)
+ return -1;
+ }
+
+ close_fds();
+ return 0;
+}
diff --git a/libpam/pam_modutil_searchkey.c b/libpam/pam_modutil_searchkey.c
new file mode 100644
index 0000000..ba023e5
--- /dev/null
+++ b/libpam/pam_modutil_searchkey.c
@@ -0,0 +1,128 @@
+/*
+ * This file implements the following functions:
+ * pam_modutil_search_key:
+ * lookup a value for key in login.defs file or similar key value format
+ */
+
+#include "config.h"
+
+#include "pam_private.h"
+#include "pam_modutil_private.h"
+#include <security/pam_ext.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#ifdef USE_ECONF
+#include <libeconf.h>
+#endif
+
+#define BUF_SIZE 8192
+
+#ifdef USE_ECONF
+#define LOGIN_DEFS "/etc/login.defs"
+
+#ifndef VENDORDIR
+#define VENDORDIR NULL
+#endif
+
+static char *
+econf_search_key (const char *name, const char *suffix, const char *key)
+{
+ econf_file *key_file = NULL;
+ char *val;
+
+ if (econf_readDirs (&key_file, VENDORDIR, SYSCONFDIR, name, suffix,
+ " \t", "#"))
+ return NULL;
+
+ if (econf_getStringValue (key_file, NULL, key, &val)) {
+ econf_free (key_file);
+ return NULL;
+ }
+
+ econf_free (key_file);
+
+ return val;
+}
+
+#endif
+
+/* lookup a value for key in login.defs file or similar key value format */
+char *
+pam_modutil_search_key(pam_handle_t *pamh UNUSED,
+ const char *file_name,
+ const char *key)
+{
+ FILE *fp;
+ char *buf = NULL;
+ size_t buflen = 0;
+ char *retval = NULL;
+
+#ifdef USE_ECONF
+ if (strcmp (file_name, LOGIN_DEFS) == 0)
+ return econf_search_key ("login", ".defs", key);
+#endif
+
+ fp = fopen(file_name, "r");
+ if (NULL == fp)
+ return NULL;
+
+ 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 = BUF_SIZE;
+ buf = malloc(buflen);
+ if (buf == NULL) {
+ fclose(fp);
+ return NULL;
+ }
+ }
+ buf[0] = '\0';
+ if (fgets(buf, buflen - 1, fp) == NULL)
+ break;
+ else if (buf != NULL)
+ n = strlen(buf);
+ else
+ n = 0;
+#endif /* HAVE_GETLINE / HAVE_GETDELIM */
+ cp = buf;
+
+ if (n < 1)
+ break;
+ if (cp[n - 1] == '\n')
+ cp[n - 1] = '\0';
+
+ 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;
+
+ tmp = strsep (&cp, " \t=");
+ if (cp != NULL)
+ while (isspace((int)*cp) || *cp == '=')
+ ++cp;
+ else
+ cp = buf + n; /* empty string */
+
+ if (strcasecmp(tmp, key) == 0) {
+ retval = strdup(cp);
+ break;
+ }
+ }
+ fclose(fp);
+
+ free(buf);
+
+ return retval;
+}
diff --git a/libpam/pam_password.c b/libpam/pam_password.c
new file mode 100644
index 0000000..592e01f
--- /dev/null
+++ b/libpam/pam_password.c
@@ -0,0 +1,61 @@
+/* pam_password.c - PAM Password Management */
+
+/*
+ * $Id$
+ */
+
+#include "pam_private.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int pam_chauthtok(pam_handle_t *pamh, int flags)
+{
+ int retval;
+
+ D(("called."));
+
+ IF_NO_PAMH("pam_chauthtok", pamh, PAM_SYSTEM_ERR);
+
+ if (__PAM_FROM_MODULE(pamh)) {
+ D(("called from module!?"));
+ return PAM_SYSTEM_ERR;
+ }
+
+ /* applications are not allowed to set this flags */
+ if (flags & (PAM_PRELIM_CHECK | PAM_UPDATE_AUTHTOK)) {
+ pam_syslog (pamh, LOG_ERR,
+ "PAM_PRELIM_CHECK or PAM_UPDATE_AUTHTOK set by application");
+ return PAM_SYSTEM_ERR;
+ }
+
+ if (pamh->former.choice == PAM_NOT_STACKED) {
+ _pam_start_timer(pamh); /* we try to make the time for a failure
+ independent of the time it takes to
+ fail */
+ _pam_sanitize(pamh);
+ pamh->former.update = PAM_FALSE;
+ }
+
+ /* first call to check if there will be a problem */
+ if (pamh->former.update ||
+ (retval = _pam_dispatch(pamh, flags|PAM_PRELIM_CHECK,
+ PAM_CHAUTHTOK)) == PAM_SUCCESS) {
+ D(("completed check ok: former=%d", pamh->former.update));
+ pamh->former.update = PAM_TRUE;
+ retval = _pam_dispatch(pamh, flags|PAM_UPDATE_AUTHTOK,
+ PAM_CHAUTHTOK);
+ }
+
+ /* if we completed we should clean up */
+ if (retval != PAM_INCOMPLETE) {
+ _pam_sanitize(pamh);
+ pamh->former.update = PAM_FALSE;
+ _pam_await_timer(pamh, retval); /* if unsuccessful then wait now */
+ D(("pam_chauthtok exit %d - %d", retval, pamh->former.choice));
+ } else {
+ D(("will resume when ready", retval));
+ }
+
+ return retval;
+}
diff --git a/libpam/pam_prelude.c b/libpam/pam_prelude.c
new file mode 100644
index 0000000..6c73bf5
--- /dev/null
+++ b/libpam/pam_prelude.c
@@ -0,0 +1,454 @@
+/*
+ * pam_prelude.c -- prelude reporting
+ * http://www.prelude-ids.org
+ *
+ * (C) Sebastien Tricaud 2005 <toady@gscore.org>
+ */
+
+#include <stdio.h>
+#include <syslog.h>
+
+#ifdef PRELUDE
+
+#include <libprelude/prelude.h>
+#include <libprelude/prelude-log.h>
+#include <libprelude/idmef-message-print.h>
+
+#include "pam_prelude.h"
+#include "pam_private.h"
+
+
+#define ANALYZER_CLASS "pam"
+#define ANALYZER_MODEL "PAM"
+#define ANALYZER_MANUFACTURER "Sebastien Tricaud, http://www.kernel.org/pub/linux/libs/pam/"
+
+#define DEFAULT_ANALYZER_NAME "PAM"
+
+static const char *
+pam_get_item_service(const pam_handle_t *pamh)
+{
+ const void *service = NULL;
+
+ pam_get_item(pamh, PAM_SERVICE, &service);
+
+ return service;
+}
+
+static const char *
+pam_get_item_user(const pam_handle_t *pamh)
+{
+ const void *user = NULL;
+
+ pam_get_item(pamh, PAM_USER, &user);
+
+ return user;
+}
+
+static const char *
+pam_get_item_user_prompt(const pam_handle_t *pamh)
+{
+ const void *user_prompt = NULL;
+
+ pam_get_item(pamh, PAM_USER_PROMPT, &user_prompt);
+
+ return user_prompt;
+}
+
+static const char *
+pam_get_item_tty(const pam_handle_t *pamh)
+{
+ const void *tty = NULL;
+
+ pam_get_item(pamh, PAM_TTY, &tty);
+
+ return tty;
+}
+
+static const char *
+pam_get_item_ruser(const pam_handle_t *pamh)
+{
+ const void *ruser = NULL;
+
+ pam_get_item(pamh, PAM_RUSER, &ruser);
+
+ return ruser;
+}
+
+static const char *
+pam_get_item_rhost(const pam_handle_t *pamh)
+{
+ const void *rhost = NULL;
+
+ pam_get_item(pamh, PAM_RHOST, &rhost);
+
+ return rhost;
+}
+
+/* Courteously stolen from prelude-lml */
+static int
+generate_additional_data(idmef_alert_t *alert, const char *meaning,
+ const char *data)
+{
+ int ret;
+ prelude_string_t *str;
+ idmef_additional_data_t *adata;
+
+ ret = idmef_alert_new_additional_data(alert, &adata, -1);
+ if ( ret < 0 )
+ return ret;
+
+ ret = idmef_additional_data_new_meaning(adata, &str);
+ if ( ret < 0 )
+ return ret;
+
+ ret = prelude_string_set_ref(str, meaning);
+ if ( ret < 0 )
+ return ret;
+
+ return idmef_additional_data_set_string_ref(adata, data);
+}
+
+static int
+setup_analyzer(const pam_handle_t *pamh, idmef_analyzer_t *analyzer)
+{
+ int ret;
+ prelude_string_t *string;
+
+ ret = idmef_analyzer_new_model(analyzer, &string);
+ if ( ret < 0 )
+ goto err;
+ prelude_string_set_constant(string, ANALYZER_MODEL);
+
+ ret = idmef_analyzer_new_class(analyzer, &string);
+ if ( ret < 0 )
+ goto err;
+ prelude_string_set_constant(string, ANALYZER_CLASS);
+
+ ret = idmef_analyzer_new_manufacturer(analyzer, &string);
+ if ( ret < 0 )
+ goto err;
+ prelude_string_set_constant(string, ANALYZER_MANUFACTURER);
+
+ ret = idmef_analyzer_new_version(analyzer, &string);
+ if ( ret < 0 )
+ goto err;
+ prelude_string_set_constant(string, PAM_VERSION);
+
+
+ return 0;
+
+ err:
+ pam_syslog(pamh, LOG_WARNING,
+ "%s: IDMEF error: %s.\n",
+ prelude_strsource(ret), prelude_strerror(ret));
+
+ return -1;
+}
+
+static void
+pam_alert_prelude(const char *msg, void *data,
+ pam_handle_t *pamh, int authval)
+{
+ int ret;
+ idmef_time_t *clienttime;
+ idmef_alert_t *alert;
+ prelude_string_t *str;
+ idmef_message_t *idmef = NULL;
+ idmef_classification_t *class;
+ prelude_client_t *client = (prelude_client_t *)data;
+ idmef_source_t *source;
+ idmef_target_t *target;
+ idmef_user_t *user;
+ idmef_user_id_t *user_id;
+ idmef_process_t *process;
+ idmef_classification_t *classification;
+ idmef_impact_t *impact;
+ idmef_assessment_t *assessment;
+ idmef_node_t *node;
+ idmef_analyzer_t *analyzer;
+
+
+ ret = idmef_message_new(&idmef);
+ if ( ret < 0 )
+ goto err;
+
+ ret = idmef_message_new_alert(idmef, &alert);
+ if ( ret < 0 )
+ goto err;
+
+ ret = idmef_alert_new_classification(alert, &class);
+ if ( ret < 0 )
+ goto err;
+
+ ret = idmef_classification_new_text(class, &str);
+ if ( ret < 0 )
+ goto err;
+
+ ret = prelude_string_new_ref(&str, msg);
+ if ( ret < 0 )
+ goto err;
+
+ idmef_classification_set_text(class, str);
+
+ ret = idmef_time_new_from_gettimeofday(&clienttime);
+ if ( ret < 0 )
+ goto err;
+ idmef_alert_set_create_time(alert, clienttime);
+
+ idmef_alert_set_analyzer(alert,
+ idmef_analyzer_ref(prelude_client_get_analyzer(client)),
+ 0);
+
+ /**********
+ * SOURCE *
+ **********/
+ ret = idmef_alert_new_source(alert, &source, -1);
+ if ( ret < 0 )
+ goto err;
+
+ /* BEGIN: Sets the user doing authentication stuff */
+ ret = idmef_source_new_user(source, &user);
+ if ( ret < 0 )
+ goto err;
+ idmef_user_set_category(user, IDMEF_USER_CATEGORY_APPLICATION);
+
+ ret = idmef_user_new_user_id(user, &user_id, 0);
+ if ( ret < 0 )
+ goto err;
+ idmef_user_id_set_type(user_id, IDMEF_USER_ID_TYPE_ORIGINAL_USER);
+
+ if ( pam_get_item_ruser(pamh) ) {
+ ret = prelude_string_new(&str);
+ if ( ret < 0 )
+ goto err;
+
+ ret = prelude_string_set_ref(str, pam_get_item_ruser(pamh));
+ if ( ret < 0 )
+ goto err;
+
+ idmef_user_id_set_name(user_id, str);
+ }
+ /* END */
+ /* BEGIN: Adds TTY infos */
+ if ( pam_get_item_tty(pamh) ) {
+ ret = prelude_string_new(&str);
+ if ( ret < 0 )
+ goto err;
+
+ ret = prelude_string_set_ref(str, pam_get_item_tty(pamh));
+ if ( ret < 0 )
+ goto err;
+
+ idmef_user_id_set_tty(user_id, str);
+ }
+ /* END */
+ /* BEGIN: Sets the source node (rhost) */
+ ret = idmef_source_new_node(source, &node);
+ if ( ret < 0 )
+ goto err;
+ idmef_node_set_category(node, IDMEF_NODE_CATEGORY_HOSTS);
+
+ if ( pam_get_item_rhost(pamh) ) {
+ ret = prelude_string_new(&str);
+ if ( ret < 0 )
+ goto err;
+
+ ret = prelude_string_set_ref(str, pam_get_item_rhost(pamh));
+ if ( ret < 0 )
+ goto err;
+
+ idmef_node_set_name(node, str);
+ }
+ /* END */
+ /* BEGIN: Describe the service */
+ ret = idmef_source_new_process(source, &process);
+ if ( ret < 0 )
+ goto err;
+ idmef_process_set_pid(process, getpid());
+
+ if ( pam_get_item_service(pamh) ) {
+ ret = prelude_string_new(&str);
+ if ( ret < 0 )
+ goto err;
+
+ ret = prelude_string_set_ref(str, pam_get_item_service(pamh));
+ if ( ret < 0 )
+ goto err;
+
+ idmef_process_set_name(process, str);
+ }
+ /* END */
+
+ /**********
+ * TARGET *
+ **********/
+
+ ret = idmef_alert_new_target(alert, &target, -1);
+ if ( ret < 0 )
+ goto err;
+
+
+ /* BEGIN: Sets the target node */
+ analyzer = prelude_client_get_analyzer(client);
+ if ( ! analyzer ) goto err;
+
+ node = idmef_analyzer_get_node(analyzer);
+ if ( ! node ) goto err;
+ idmef_target_set_node(target, node);
+ node = idmef_node_ref(node);
+ if ( ! node ) goto err;
+ /* END */
+ /* BEGIN: Sets the user doing authentication stuff */
+ ret = idmef_target_new_user(target, &user);
+ if ( ret < 0 )
+ goto err;
+ idmef_user_set_category(user, IDMEF_USER_CATEGORY_APPLICATION);
+
+ ret = idmef_user_new_user_id(user, &user_id, 0);
+ if ( ret < 0 )
+ goto err;
+ idmef_user_id_set_type(user_id, IDMEF_USER_ID_TYPE_TARGET_USER);
+
+ if ( pam_get_item_user(pamh) ) {
+ ret = prelude_string_new(&str);
+ if ( ret < 0 )
+ goto err;
+
+ ret = prelude_string_set_ref(str, pam_get_item_user(pamh));
+ if ( ret < 0 )
+ goto err;
+
+ idmef_user_id_set_name(user_id, str);
+ }
+ /* END */
+ /* BEGIN: Short description of the alert */
+ ret = idmef_alert_new_classification(alert, &classification);
+ if ( ret < 0 )
+ goto err;
+
+ ret = prelude_string_new(&str);
+ if ( ret < 0 )
+ goto err;
+
+ ret = prelude_string_set_ref(str,
+ authval == PAM_SUCCESS ?
+ "Authentication Success" : "Authentication Failure");
+ if ( ret < 0 )
+ goto err;
+
+ idmef_classification_set_text(classification, str);
+ /* END */
+ /* BEGIN: Long description of the alert */
+ ret = idmef_alert_new_assessment(alert, &assessment);
+ if ( ret < 0 )
+ goto err;
+
+ ret = idmef_assessment_new_impact(assessment, &impact);
+ if ( ret < 0 )
+ goto err;
+
+ ret = prelude_string_new(&str);
+ if ( ret < 0 )
+ goto err;
+
+ ret = prelude_string_set_ref(str, pam_strerror (pamh, authval));
+ if ( ret < 0 )
+ goto err;
+
+ idmef_impact_set_description(impact, str);
+ /* END */
+ /* BEGIN: Adding additional data */
+ if ( pam_get_item_user_prompt(pamh) ) {
+ ret = generate_additional_data(alert, "Local User Prompt",
+ pam_get_item_user_prompt(pamh));
+ if ( ret < 0 )
+ goto err;
+ }
+ /* END */
+
+ prelude_client_send_idmef(client, idmef);
+
+ if ( idmef )
+ idmef_message_destroy(idmef);
+
+ return;
+ err:
+ pam_syslog(pamh, LOG_WARNING, "%s: IDMEF error: %s.\n",
+ prelude_strsource(ret), prelude_strerror(ret));
+
+ if ( idmef )
+ idmef_message_destroy(idmef);
+
+}
+
+static int
+pam_alert_prelude_init(pam_handle_t *pamh, int authval)
+{
+
+ int ret;
+ prelude_client_t *client = NULL;
+
+ ret = prelude_init(NULL, NULL);
+ if ( ret < 0 ) {
+ pam_syslog(pamh, LOG_WARNING,
+ "%s: Unable to initialize the Prelude library: %s.\n",
+ prelude_strsource(ret), prelude_strerror(ret));
+ return -1;
+ }
+
+ ret = prelude_client_new(&client, DEFAULT_ANALYZER_NAME);
+ if ( ! client ) {
+ pam_syslog(pamh, LOG_WARNING,
+ "%s: Unable to create a prelude client object: %s.\n",
+ prelude_strsource(ret), prelude_strerror(ret));
+
+ return -1;
+ }
+
+
+ ret = setup_analyzer(pamh, prelude_client_get_analyzer(client));
+ if ( ret < 0 ) {
+ pam_syslog(pamh, LOG_WARNING,
+ "%s: Unable to setup analyzer: %s\n",
+ prelude_strsource(ret), prelude_strerror(ret));
+
+ prelude_client_destroy(client, PRELUDE_CLIENT_EXIT_STATUS_FAILURE);
+
+ return -1;
+ }
+
+ ret = prelude_client_start(client);
+ if ( ret < 0 ) {
+ pam_syslog(pamh, LOG_WARNING,
+ "%s: Unable to initialize prelude client: %s.\n",
+ prelude_strsource(ret), prelude_strerror(ret));
+
+ prelude_client_destroy(client, PRELUDE_CLIENT_EXIT_STATUS_FAILURE);
+
+ return -1;
+ }
+
+ pam_alert_prelude("libpam alert" , client, pamh, authval);
+
+ prelude_client_destroy(client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS);
+
+ return 0;
+}
+
+void
+prelude_send_alert(pam_handle_t *pamh, int authval)
+{
+
+ int ret;
+
+ prelude_log_set_flags(PRELUDE_LOG_FLAGS_SYSLOG);
+
+ ret = pam_alert_prelude_init(pamh, authval);
+ if ( ret < 0 )
+ pam_syslog(pamh, LOG_WARNING, "No prelude alert sent");
+
+ prelude_deinit();
+
+}
+
+#endif /* PRELUDE */
diff --git a/libpam/pam_prelude.h b/libpam/pam_prelude.h
new file mode 100644
index 0000000..196b141
--- /dev/null
+++ b/libpam/pam_prelude.h
@@ -0,0 +1,15 @@
+/*
+ * pam_prelude.h -- prelude ids reporting
+ * http://www.prelude-ids.org
+ *
+ * (C) Sebastien Tricaud 2005 <toady@gscore.org>
+ */
+
+#ifndef _SECURITY_PAM_PRELUDE_H
+#define _SECURITY_PAM_PRELUDE_H
+
+#include <security/_pam_types.h>
+
+void prelude_send_alert(pam_handle_t *pamh, int authval);
+
+#endif /* _SECURITY_PAM_PRELUDE_H */
diff --git a/libpam/pam_private.h b/libpam/pam_private.h
new file mode 100644
index 0000000..508527c
--- /dev/null
+++ b/libpam/pam_private.h
@@ -0,0 +1,358 @@
+/*
+ * pam_private.h
+ *
+ * This is the Linux-PAM Library Private Header. It contains things
+ * internal to the Linux-PAM library. Things not needed by either an
+ * application or module.
+ *
+ * Please see end of file for copyright.
+ *
+ * Creator: Marc Ewing.
+ * Maintained: CVS
+ */
+
+#ifndef _PAM_PRIVATE_H
+#define _PAM_PRIVATE_H
+
+#include "config.h"
+
+#include <syslog.h>
+
+#include <security/pam_appl.h>
+#include <security/pam_modules.h>
+#include <security/pam_ext.h>
+
+/* the Linux-PAM configuration file */
+
+#define PAM_CONFIG "/etc/pam.conf"
+#define PAM_CONFIG_D "/etc/pam.d"
+#define PAM_CONFIG_DF "/etc/pam.d/%s"
+#define PAM_CONFIG_DIST_D "/usr/lib/pam.d"
+#define PAM_CONFIG_DIST_DF "/usr/lib/pam.d/%s"
+#ifdef VENDORDIR
+#define PAM_CONFIG_DIST2_D VENDORDIR"/pam.d"
+#define PAM_CONFIG_DIST2_DF VENDORDIR"/pam.d/%s"
+#endif
+
+
+#define PAM_DEFAULT_SERVICE "other" /* lower case */
+
+#ifdef PAM_LOCKING
+/*
+ * the Linux-PAM lock file. If it exists Linux-PAM will abort. Use it
+ * to block access to libpam
+ */
+#define PAM_LOCK_FILE "/var/lock/subsys/PAM"
+#endif
+
+/* components of the pam_handle structure */
+
+#define _PAM_INVALID_RETVAL -1 /* default value for cached_retval */
+
+struct handler {
+ int handler_type;
+ int (*func)(pam_handle_t *pamh, int flags, int argc, char **argv);
+ int actions[_PAM_RETURN_VALUES];
+ /* set by authenticate, open_session, chauthtok(1st)
+ consumed by setcred, close_session, chauthtok(2nd) */
+ int cached_retval; int *cached_retval_p;
+ int argc;
+ char **argv;
+ struct handler *next;
+ char *mod_name;
+ int stack_level;
+ int grantor;
+};
+
+#define PAM_HT_MODULE 0
+#define PAM_HT_MUST_FAIL 1
+#define PAM_HT_SUBSTACK 2
+#define PAM_HT_SILENT_MODULE 3
+
+struct loaded_module {
+ char *name;
+ int type; /* PAM_STATIC_MOD or PAM_DYNAMIC_MOD */
+ void *dl_handle;
+};
+
+#define PAM_MT_DYNAMIC_MOD 0
+#define PAM_MT_STATIC_MOD 1
+#define PAM_MT_FAULTY_MOD 2
+
+struct handlers {
+ struct handler *authenticate;
+ struct handler *setcred;
+ struct handler *acct_mgmt;
+ struct handler *open_session;
+ struct handler *close_session;
+ struct handler *chauthtok;
+};
+
+struct service {
+ struct loaded_module *module; /* Array of modules */
+ int modules_allocated;
+ int modules_used;
+ int handlers_loaded;
+
+ struct handlers conf; /* the configured handlers */
+ struct handlers other; /* the default handlers */
+};
+
+/*
+ * Environment helper functions
+ */
+
+#define PAM_ENV_CHUNK 10 /* chunks of memory calloc()'d *
+ * at once */
+
+struct pam_environ {
+ int entries; /* the number of pointers available */
+ int requested; /* the number of pointers used: *
+ * 1 <= requested <= entries */
+ char **list; /* the environment storage (a list *
+ * of pointers to malloc() memory) */
+};
+
+#include <sys/time.h>
+
+typedef enum { PAM_FALSE, PAM_TRUE } _pam_boolean;
+
+struct _pam_fail_delay {
+ _pam_boolean set;
+ unsigned int delay;
+ time_t begin;
+ const void *delay_fn_ptr;
+};
+
+/* initial state in substack */
+struct _pam_substack_state {
+ int impression;
+ int status;
+};
+
+struct _pam_former_state {
+/* this is known and set by _pam_dispatch() */
+ int choice; /* which flavor of module function did we call? */
+
+/* state info for the _pam_dispatch_aux() function */
+ int depth; /* how deep in the stack were we? */
+ int impression; /* the impression at that time */
+ int status; /* the status before returning incomplete */
+ struct _pam_substack_state *substates; /* array of initial substack states */
+
+/* state info used by pam_get_user() function */
+ int fail_user;
+ int want_user;
+ char *prompt; /* saved prompt information */
+
+/* state info for the pam_chauthtok() function */
+ _pam_boolean update;
+};
+
+struct pam_handle {
+ char *authtok;
+ unsigned caller_is;
+ struct pam_conv *pam_conversation;
+ char *oldauthtok;
+ char *prompt; /* for use by pam_get_user() */
+ char *service_name;
+ char *user;
+ char *rhost;
+ char *ruser;
+ char *tty;
+ char *xdisplay;
+ char *authtok_type; /* PAM_AUTHTOK_TYPE */
+ struct pam_data *data;
+ struct pam_environ *env; /* structure to maintain environment list */
+ struct _pam_fail_delay fail_delay; /* helper function for easy delays */
+ struct pam_xauth_data xauth; /* auth info for X display */
+ struct service handlers;
+ struct _pam_former_state former; /* library state - support for
+ event driven applications */
+ const char *mod_name; /* Name of the module currently executed */
+ int mod_argc; /* Number of module arguments */
+ char **mod_argv; /* module arguments */
+ int choice; /* Which function we call from the module */
+
+#ifdef HAVE_LIBAUDIT
+ int audit_state; /* keep track of reported audit messages */
+#endif
+ int authtok_verified;
+ char *confdir;
+};
+
+/* Values for select arg to _pam_dispatch() */
+#define PAM_NOT_STACKED 0
+#define PAM_AUTHENTICATE 1
+#define PAM_SETCRED 2
+#define PAM_ACCOUNT 3
+#define PAM_OPEN_SESSION 4
+#define PAM_CLOSE_SESSION 5
+#define PAM_CHAUTHTOK 6
+
+#define _PAM_ACTION_IS_JUMP(x) ((x) > 0)
+#define _PAM_ACTION_IGNORE 0
+#define _PAM_ACTION_OK -1
+#define _PAM_ACTION_DONE -2
+#define _PAM_ACTION_BAD -3
+#define _PAM_ACTION_DIE -4
+#define _PAM_ACTION_RESET -5
+/* Add any new entries here. Will need to change ..._UNDEF and then
+ * need to change pam_tokens.h */
+#define _PAM_ACTION_UNDEF -6 /* this is treated as an error
+ ( = _PAM_ACTION_BAD) */
+
+#define PAM_SUBSTACK_MAX_LEVEL 16 /* maximum level of substacks */
+
+/* character tables for parsing config files */
+extern const char * const _pam_token_actions[-_PAM_ACTION_UNDEF];
+extern const char * const _pam_token_returns[_PAM_RETURN_VALUES+1];
+
+/*
+ * internally defined functions --- these should not be directly
+ * called by applications or modules
+ */
+int _pam_dispatch(pam_handle_t *pamh, int flags, int choice);
+
+/* Free various allocated structures and dlclose() the libs */
+int _pam_free_handlers(pam_handle_t *pamh);
+
+/* Parse config file, allocate handler structures, dlopen() */
+int _pam_init_handlers(pam_handle_t *pamh);
+
+/* Set all handler stuff to 0/NULL - called once from pam_start() */
+void _pam_start_handlers(pam_handle_t *pamh);
+
+/* environment helper functions */
+
+/* create the environment structure */
+int _pam_make_env(pam_handle_t *pamh);
+
+/* delete the environment structure */
+void _pam_drop_env(pam_handle_t *pamh);
+
+/* these functions deal with failure delays as required by the
+ authentication modules and application. Their *interface* is likely
+ to remain the same although their function is hopefully going to
+ improve */
+
+/* reset the timer to no-delay */
+void _pam_reset_timer(pam_handle_t *pamh);
+
+/* this sets the clock ticking */
+void _pam_start_timer(pam_handle_t *pamh);
+
+/* this waits for the clock to stop ticking if status != PAM_SUCCESS */
+void _pam_await_timer(pam_handle_t *pamh, int status);
+
+typedef void (*voidfunc(void))(void);
+typedef int (*servicefn)(pam_handle_t *, int, int, char **);
+
+void *_pam_dlopen (const char *mod_path);
+servicefn _pam_dlsym (void *handle, const char *symbol);
+void _pam_dlclose (void *handle);
+const char *_pam_dlerror (void);
+
+/* For now we just use a stack and linear search for module data. */
+/* If it becomes apparent that there is a lot of data, it should */
+/* changed to either a sorted list or a hash table. */
+
+struct pam_data {
+ char *name;
+ void *data;
+ void (*cleanup)(pam_handle_t *pamh, void *data, int error_status);
+ struct pam_data *next;
+};
+
+void _pam_free_data(pam_handle_t *pamh, int status);
+
+char *_pam_StrTok(char *from, const char *format, char **next);
+
+char *_pam_strdup(const char *s);
+
+char *_pam_memdup(const char *s, int len);
+
+int _pam_mkargv(const char *s, char ***argv, int *argc);
+
+void _pam_sanitize(pam_handle_t *pamh);
+
+void _pam_set_default_control(int *control_array, int default_action);
+
+void _pam_parse_control(int *control_array, char *tok);
+
+#define _PAM_SYSTEM_LOG_PREFIX "PAM"
+
+/*
+ * XXX - Take care with this. It could confuse the logic of a trailing
+ * else
+ */
+
+#define IF_NO_PAMH(X,pamh,ERR) \
+if ((pamh) == NULL) { \
+ syslog(LOG_ERR, _PAM_SYSTEM_LOG_PREFIX " " X ": NULL pam handle passed"); \
+ return ERR; \
+}
+
+/*
+ * include some helpful macros
+ */
+
+#include <security/_pam_macros.h>
+
+/* used to work out where control currently resides (in an application
+ or in a module) */
+
+#define _PAM_CALLED_FROM_MODULE 1
+#define _PAM_CALLED_FROM_APP 2
+
+#define __PAM_FROM_MODULE(pamh) ((pamh)->caller_is == _PAM_CALLED_FROM_MODULE)
+#define __PAM_FROM_APP(pamh) ((pamh)->caller_is == _PAM_CALLED_FROM_APP)
+#define __PAM_TO_MODULE(pamh) \
+ do { (pamh)->caller_is = _PAM_CALLED_FROM_MODULE; } while (0)
+#define __PAM_TO_APP(pamh) \
+ do { (pamh)->caller_is = _PAM_CALLED_FROM_APP; } while (0)
+
+#ifdef HAVE_LIBAUDIT
+extern int _pam_auditlog(pam_handle_t *pamh, int action, int retval, int flags, struct handler *h);
+extern int _pam_audit_end(pam_handle_t *pamh, int pam_status);
+#endif
+
+/*
+ * Copyright (C) 1995 by Red Hat Software, Marc Ewing
+ * Copyright (c) 1996-8,2001 by Andrew G. Morgan <morgan@kernel.org>
+ *
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#endif /* _PAM_PRIVATE_H_ */
diff --git a/libpam/pam_session.c b/libpam/pam_session.c
new file mode 100644
index 0000000..cb393c1
--- /dev/null
+++ b/libpam/pam_session.c
@@ -0,0 +1,45 @@
+/* pam_session.c - PAM Session Management */
+
+/*
+ * $Id$
+ */
+
+#include "pam_private.h"
+
+#include <stdio.h>
+
+int pam_open_session(pam_handle_t *pamh, int flags)
+{
+ int retval;
+
+ D(("called"));
+
+ IF_NO_PAMH("pam_open_session", pamh, PAM_SYSTEM_ERR);
+
+ if (__PAM_FROM_MODULE(pamh)) {
+ D(("called from module!?"));
+ return PAM_SYSTEM_ERR;
+ }
+ retval = _pam_dispatch(pamh, flags, PAM_OPEN_SESSION);
+
+ return retval;
+}
+
+int pam_close_session(pam_handle_t *pamh, int flags)
+{
+ int retval;
+
+ D(("called"));
+
+ IF_NO_PAMH("pam_close_session", pamh, PAM_SYSTEM_ERR);
+
+ if (__PAM_FROM_MODULE(pamh)) {
+ D(("called from module!?"));
+ return PAM_SYSTEM_ERR;
+ }
+
+ retval = _pam_dispatch(pamh, flags, PAM_CLOSE_SESSION);
+
+ return retval;
+
+}
diff --git a/libpam/pam_start.c b/libpam/pam_start.c
new file mode 100644
index 0000000..99dd038
--- /dev/null
+++ b/libpam/pam_start.c
@@ -0,0 +1,179 @@
+/* pam_start.c */
+
+/* Creator Marc Ewing
+ * Maintained by AGM
+ *
+ * $Id$
+ *
+ */
+
+#include "pam_private.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+
+static int _pam_start_internal (
+ const char *service_name,
+ const char *user,
+ const struct pam_conv *pam_conversation,
+ const char *confdir,
+ pam_handle_t **pamh)
+{
+ D(("called pam_start: [%s] [%s] [%p] [%p]"
+ ,service_name, user, pam_conversation, pamh));
+
+ if (pamh == NULL) {
+ pam_syslog(NULL, LOG_CRIT,
+ "pam_start: invalid argument: pamh == NULL");
+ return (PAM_SYSTEM_ERR);
+ }
+
+ if (service_name == NULL) {
+ pam_syslog(NULL, LOG_CRIT,
+ "pam_start: invalid argument: service == NULL");
+ return (PAM_SYSTEM_ERR);
+ }
+
+ if (pam_conversation == NULL) {
+ pam_syslog(NULL, LOG_CRIT,
+ "pam_start: invalid argument: conv == NULL");
+ return (PAM_SYSTEM_ERR);
+ }
+
+ if ((*pamh = calloc(1, sizeof(**pamh))) == NULL) {
+ pam_syslog(NULL, LOG_CRIT, "pam_start: calloc failed for *pamh");
+ return (PAM_BUF_ERR);
+ }
+
+ /* All service names should be files below /etc/pam.d and nothing
+ else. Forbid paths. */
+ if (strrchr(service_name, '/') != NULL)
+ service_name = strrchr(service_name, '/') + 1;
+
+ /* Mark the caller as the application - permission to do certain
+ things is limited to a module or an application */
+
+ __PAM_TO_APP(*pamh);
+
+ if (((*pamh)->service_name = _pam_strdup(service_name)) == NULL) {
+ pam_syslog(*pamh, LOG_CRIT,
+ "pam_start: _pam_strdup failed for service name");
+ _pam_drop(*pamh);
+ return (PAM_BUF_ERR);
+ } else {
+ char *tmp;
+
+ for (tmp=(*pamh)->service_name; *tmp; ++tmp)
+ *tmp = tolower(*tmp); /* require lower case */
+ }
+
+ if (user) {
+ if (((*pamh)->user = _pam_strdup(user)) == NULL) {
+ pam_syslog(*pamh, LOG_CRIT,
+ "pam_start: _pam_strdup failed for user");
+ _pam_drop((*pamh)->service_name);
+ _pam_drop(*pamh);
+ return (PAM_BUF_ERR);
+ }
+ } else
+ (*pamh)->user = NULL;
+
+ if (confdir) {
+ if (((*pamh)->confdir = _pam_strdup(confdir)) == NULL) {
+ pam_syslog(*pamh, LOG_CRIT,
+ "pam_start: _pam_strdup failed for confdir");
+ _pam_drop((*pamh)->service_name);
+ _pam_drop((*pamh)->user);
+ _pam_drop(*pamh);
+ return (PAM_BUF_ERR);
+ }
+ } else
+ (*pamh)->confdir = NULL;
+
+ (*pamh)->tty = NULL;
+ (*pamh)->prompt = NULL; /* prompt for pam_get_user() */
+ (*pamh)->ruser = NULL;
+ (*pamh)->rhost = NULL;
+ (*pamh)->authtok = NULL;
+ (*pamh)->oldauthtok = NULL;
+ (*pamh)->fail_delay.delay_fn_ptr = NULL;
+ (*pamh)->former.choice = PAM_NOT_STACKED;
+ (*pamh)->former.substates = NULL;
+#ifdef HAVE_LIBAUDIT
+ (*pamh)->audit_state = 0;
+#endif
+ (*pamh)->xdisplay = NULL;
+ (*pamh)->authtok_type = NULL;
+ (*pamh)->authtok_verified = 0;
+ memset (&((*pamh)->xauth), 0, sizeof ((*pamh)->xauth));
+
+ if (((*pamh)->pam_conversation = (struct pam_conv *)
+ malloc(sizeof(struct pam_conv))) == NULL) {
+ pam_syslog(*pamh, LOG_CRIT, "pam_start: malloc failed for pam_conv");
+ _pam_drop((*pamh)->service_name);
+ _pam_drop((*pamh)->user);
+ _pam_drop((*pamh)->confdir);
+ _pam_drop(*pamh);
+ return (PAM_BUF_ERR);
+ } else {
+ memcpy((*pamh)->pam_conversation, pam_conversation,
+ sizeof(struct pam_conv));
+ }
+
+ (*pamh)->data = NULL;
+ if ( _pam_make_env(*pamh) != PAM_SUCCESS ) {
+ pam_syslog(*pamh,LOG_ERR,"pam_start: failed to initialize environment");
+ _pam_drop((*pamh)->pam_conversation);
+ _pam_drop((*pamh)->service_name);
+ _pam_drop((*pamh)->user);
+ _pam_drop((*pamh)->confdir);
+ _pam_drop(*pamh);
+ return PAM_ABORT;
+ }
+
+ _pam_reset_timer(*pamh); /* initialize timer support */
+
+ _pam_start_handlers(*pamh); /* cannot fail */
+
+ /* According to the SunOS man pages, loading modules and resolving
+ * symbols happens on the first call from the application. */
+
+ if ( _pam_init_handlers(*pamh) != PAM_SUCCESS ) {
+ pam_syslog(*pamh, LOG_ERR, "pam_start: failed to initialize handlers");
+ _pam_drop_env(*pamh); /* purge the environment */
+ _pam_drop((*pamh)->pam_conversation);
+ _pam_drop((*pamh)->service_name);
+ _pam_drop((*pamh)->user);
+ _pam_drop((*pamh)->confdir);
+ _pam_drop(*pamh);
+ return PAM_ABORT;
+ }
+
+ D(("exiting pam_start successfully"));
+
+ return PAM_SUCCESS;
+}
+
+int pam_start_confdir (
+ const char *service_name,
+ const char *user,
+ const struct pam_conv *pam_conversation,
+ const char *confdir,
+ pam_handle_t **pamh)
+{
+ return _pam_start_internal(service_name, user, pam_conversation,
+ confdir, pamh);
+}
+
+int pam_start (
+ const char *service_name,
+ const char *user,
+ const struct pam_conv *pam_conversation,
+ pam_handle_t **pamh)
+{
+ return _pam_start_internal(service_name, user, pam_conversation,
+ NULL, pamh);
+}
diff --git a/libpam/pam_strerror.c b/libpam/pam_strerror.c
new file mode 100644
index 0000000..17c8194
--- /dev/null
+++ b/libpam/pam_strerror.c
@@ -0,0 +1,106 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pam_private.h"
+
+const char *pam_strerror(pam_handle_t *pamh UNUSED, int errnum)
+{
+ switch (errnum) {
+ case PAM_SUCCESS:
+ return _("Success");
+ case PAM_ABORT:
+ return _("Critical error - immediate abort");
+ case PAM_OPEN_ERR:
+ return _("Failed to load module");
+ case PAM_SYMBOL_ERR:
+ return _("Symbol not found");
+ case PAM_SERVICE_ERR:
+ return _("Error in service module");
+ case PAM_SYSTEM_ERR:
+ return _("System error");
+ case PAM_BUF_ERR:
+ return _("Memory buffer error");
+ case PAM_PERM_DENIED:
+ return _("Permission denied");
+ case PAM_AUTH_ERR:
+ return _("Authentication failure");
+ case PAM_CRED_INSUFFICIENT:
+ return _("Insufficient credentials to access authentication data");
+ case PAM_AUTHINFO_UNAVAIL:
+ return _("Authentication service cannot retrieve authentication info");
+ case PAM_USER_UNKNOWN:
+ return _("User not known to the underlying authentication module");
+ case PAM_MAXTRIES:
+ return _("Have exhausted maximum number of retries for service");
+ case PAM_NEW_AUTHTOK_REQD:
+ return _("Authentication token is no longer valid; new one required");
+ case PAM_ACCT_EXPIRED:
+ return _("User account has expired");
+ case PAM_SESSION_ERR:
+ return _("Cannot make/remove an entry for the specified session");
+ case PAM_CRED_UNAVAIL:
+ return _("Authentication service cannot retrieve user credentials");
+ case PAM_CRED_EXPIRED:
+ return _("User credentials expired");
+ case PAM_CRED_ERR:
+ return _("Failure setting user credentials");
+ case PAM_NO_MODULE_DATA:
+ return _("No module specific data is present");
+ case PAM_BAD_ITEM:
+ return _("Bad item passed to pam_*_item()");
+ case PAM_CONV_ERR:
+ return _("Conversation error");
+ case PAM_AUTHTOK_ERR:
+ return _("Authentication token manipulation error");
+ case PAM_AUTHTOK_RECOVERY_ERR:
+ return _("Authentication information cannot be recovered");
+ case PAM_AUTHTOK_LOCK_BUSY:
+ return _("Authentication token lock busy");
+ case PAM_AUTHTOK_DISABLE_AGING:
+ return _("Authentication token aging disabled");
+ case PAM_TRY_AGAIN:
+ return _("Failed preliminary check by password service");
+ case PAM_IGNORE:
+ return _("The return value should be ignored by PAM dispatch");
+ case PAM_MODULE_UNKNOWN:
+ return _("Module is unknown");
+ case PAM_AUTHTOK_EXPIRED:
+ return _("Authentication token expired");
+ case PAM_CONV_AGAIN:
+ return _("Conversation is waiting for event");
+ case PAM_INCOMPLETE:
+ return _("Application needs to call libpam again");
+ }
+
+ return _("Unknown PAM error");
+}
diff --git a/libpam/pam_syslog.c b/libpam/pam_syslog.c
new file mode 100644
index 0000000..c5a6fec
--- /dev/null
+++ b/libpam/pam_syslog.c
@@ -0,0 +1,115 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+#include <security/pam_ext.h>
+
+#include "pam_private.h"
+
+#ifndef LOG_AUTHPRIV
+#define LOG_AUTHPRIV LOG_AUTH
+#endif
+
+static const char *
+_pam_choice2str (int choice)
+{
+ switch (choice)
+ {
+ case PAM_AUTHENTICATE:
+ return "auth";
+ case PAM_SETCRED:
+ return "setcred";
+ case PAM_ACCOUNT:
+ return "account";
+ case PAM_OPEN_SESSION:
+ case PAM_CLOSE_SESSION:
+ return "session";
+ case PAM_CHAUTHTOK:
+ return "chauthtok";
+ }
+ return "";
+}
+
+void
+pam_vsyslog (const pam_handle_t *pamh, int priority,
+ const char *fmt, va_list args)
+{
+ char *msgbuf1 = NULL, *msgbuf2 = NULL;
+ int save_errno = errno;
+
+ if (pamh && pamh->mod_name)
+ {
+ if (asprintf (&msgbuf1, "%s(%s:%s):", pamh->mod_name,
+ pamh->service_name?pamh->service_name:"<unknown>",
+ _pam_choice2str (pamh->choice)) < 0)
+ {
+ syslog (LOG_AUTHPRIV|LOG_ERR, "asprintf: %m");
+ return;
+ }
+ }
+
+ errno = save_errno;
+ if (vasprintf (&msgbuf2, fmt, args) < 0)
+ {
+ syslog (LOG_AUTHPRIV|LOG_ERR, "vasprintf: %m");
+ _pam_drop (msgbuf1);
+ return;
+ }
+
+ errno = save_errno;
+ syslog (LOG_AUTHPRIV|priority, "%s %s",
+ (msgbuf1 ? msgbuf1 : _PAM_SYSTEM_LOG_PREFIX), msgbuf2);
+
+ _pam_drop (msgbuf1);
+ _pam_drop (msgbuf2);
+}
+
+void
+pam_syslog (const pam_handle_t *pamh, int priority,
+ const char *fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ pam_vsyslog (pamh, priority, fmt, args);
+ va_end (args);
+}
diff --git a/libpam/pam_tokens.h b/libpam/pam_tokens.h
new file mode 100644
index 0000000..1412fa3
--- /dev/null
+++ b/libpam/pam_tokens.h
@@ -0,0 +1,112 @@
+/*
+ * pam_tokens.h
+ *
+ * $Id$
+ *
+ * This is a Linux-PAM Library Private Header file. It contains tokens
+ * that are used when we parse the configuration file(s).
+ *
+ * Please see end of file for copyright.
+ *
+ * Creator: Andrew Morgan.
+ *
+ */
+
+#ifndef _PAM_TOKENS_H
+#define _PAM_TOKENS_H
+
+/* an array of actions */
+
+#ifndef LIBPAM_COMPILE
+static
+#endif
+const char * const _pam_token_actions[-_PAM_ACTION_UNDEF] = {
+ "ignore", /* 0 */
+ "ok", /* -1 */
+ "done", /* -2 */
+ "bad", /* -3 */
+ "die", /* -4 */
+ "reset", /* -5 */
+};
+
+/* an array of possible return values */
+
+#ifndef LIBPAM_COMPILE
+static
+#endif
+const char * const _pam_token_returns[_PAM_RETURN_VALUES+1] = {
+ "success", /* 0 */
+ "open_err", /* 1 */
+ "symbol_err", /* 2 */
+ "service_err", /* 3 */
+ "system_err", /* 4 */
+ "buf_err", /* 5 */
+ "perm_denied", /* 6 */
+ "auth_err", /* 7 */
+ "cred_insufficient", /* 8 */
+ "authinfo_unavail", /* 9 */
+ "user_unknown", /* 10 */
+ "maxtries", /* 11 */
+ "new_authtok_reqd", /* 12 */
+ "acct_expired", /* 13 */
+ "session_err", /* 14 */
+ "cred_unavail", /* 15 */
+ "cred_expired", /* 16 */
+ "cred_err", /* 17 */
+ "no_module_data", /* 18 */
+ "conv_err", /* 19 */
+ "authtok_err", /* 20 */
+ "authtok_recover_err", /* 21 */
+ "authtok_lock_busy", /* 22 */
+ "authtok_disable_aging", /* 23 */
+ "try_again", /* 24 */
+ "ignore", /* 25 */
+ "abort", /* 26 */
+ "authtok_expired", /* 27 */
+ "module_unknown", /* 28 */
+ "bad_item", /* 29 */
+ "conv_again", /* 30 */
+ "incomplete", /* 31 */
+/* add new return codes here */
+ "default" /* this is _PAM_RETURN_VALUES and indicates
+ the default return action */
+};
+
+/*
+ * Copyright (C) 1998,2001 Andrew G. Morgan <morgan@kernel.org>
+ *
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#endif /* _PAM_PRIVATE_H_ */
diff --git a/libpam/pam_vprompt.c b/libpam/pam_vprompt.c
new file mode 100644
index 0000000..c53079b
--- /dev/null
+++ b/libpam/pam_vprompt.c
@@ -0,0 +1,115 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+#include <security/pam_ext.h>
+
+#include "pam_private.h"
+
+int
+pam_vprompt (pam_handle_t *pamh, int style, char **response,
+ const char *fmt, va_list args)
+{
+ struct pam_message msg;
+ struct pam_response *pam_resp = NULL;
+ const struct pam_message *pmsg;
+ const struct pam_conv *conv;
+ const void *convp;
+ char *msgbuf;
+ int retval;
+
+ if (response)
+ *response = NULL;
+
+ retval = pam_get_item (pamh, PAM_CONV, &convp);
+ if (retval != PAM_SUCCESS)
+ return retval;
+ conv = convp;
+ if (conv == NULL || conv->conv == NULL)
+ {
+ pam_syslog (pamh, LOG_ERR, "no conversation function");
+ return PAM_SYSTEM_ERR;
+ }
+
+ if (vasprintf (&msgbuf, fmt, args) < 0)
+ {
+ pam_syslog (pamh, LOG_ERR, "vasprintf: %m");
+ return PAM_BUF_ERR;
+ }
+
+ msg.msg_style = style;
+ msg.msg = msgbuf;
+ pmsg = &msg;
+
+ retval = conv->conv (1, &pmsg, &pam_resp, conv->appdata_ptr);
+ if (retval != PAM_SUCCESS && pam_resp != NULL)
+ pam_syslog(pamh, LOG_WARNING,
+ "unexpected response from failed conversation function");
+ if (response)
+ *response = pam_resp == NULL ? NULL : pam_resp->resp;
+ else if (pam_resp && pam_resp->resp)
+ {
+ _pam_overwrite (pam_resp->resp);
+ _pam_drop (pam_resp->resp);
+ }
+ _pam_overwrite (msgbuf);
+ _pam_drop (pam_resp);
+ free (msgbuf);
+ if (retval != PAM_SUCCESS)
+ pam_syslog (pamh, LOG_ERR, "conversation failed");
+
+ return retval;
+}
+
+int
+pam_prompt (pam_handle_t *pamh, int style, char **response,
+ const char *fmt, ...)
+{
+ va_list args;
+ int retval;
+
+ va_start (args, fmt);
+ retval = pam_vprompt (pamh, style, response, fmt, args);
+ va_end (args);
+
+ return retval;
+}
diff --git a/libpam_misc/Makefile.am b/libpam_misc/Makefile.am
new file mode 100644
index 0000000..4f01d32
--- /dev/null
+++ b/libpam_misc/Makefile.am
@@ -0,0 +1,26 @@
+#
+# Copyright (c) 2005 Thorsten Kukuk <kukuk@suse.de>
+#
+
+CLEANFILES = *~
+
+EXTRA_DIST = libpam_misc.map
+
+include_HEADERS = include/security/pam_misc.h
+
+AM_CFLAGS = -I$(top_srcdir)/libpam/include \
+ -I$(top_srcdir)/libpamc/include -I$(srcdir)/include $(WARN_CFLAGS)
+
+libpam_misc_la_LDFLAGS = -no-undefined -version-info 82:1:82
+if HAVE_VERSIONING
+ libpam_misc_la_LDFLAGS += -Wl,--version-script=$(srcdir)/libpam_misc.map
+endif
+libpam_misc_la_LIBADD = $(top_builddir)/libpam/libpam.la
+
+lib_LTLIBRARIES = libpam_misc.la
+
+libpam_misc_la_SOURCES = help_env.c misc_conv.c
+
+# Pkg-config script.
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = pam_misc.pc
diff --git a/libpam_misc/Makefile.in b/libpam_misc/Makefile.in
new file mode 100644
index 0000000..9ce0691
--- /dev/null
+++ b/libpam_misc/Makefile.in
@@ -0,0 +1,806 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+#
+# Copyright (c) 2005 Thorsten Kukuk <kukuk@suse.de>
+#
+
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@HAVE_VERSIONING_TRUE@am__append_1 = -Wl,--version-script=$(srcdir)/libpam_misc.map
+subdir = libpam_misc
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/attribute.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/intlmacosx.m4 \
+ $(top_srcdir)/m4/jh_path_xml_catalog.m4 \
+ $(top_srcdir)/m4/ld-O1.m4 $(top_srcdir)/m4/ld-as-needed.m4 \
+ $(top_srcdir)/m4/ld-no-undefined.m4 \
+ $(top_srcdir)/m4/ld-z-now.m4 $(top_srcdir)/m4/lib-ld.m4 \
+ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+ $(top_srcdir)/m4/libprelude.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 \
+ $(top_srcdir)/m4/warn_lang_flags.m4 \
+ $(top_srcdir)/m4/warnings.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(include_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES = pam_misc.pc
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" \
+ "$(DESTDIR)$(includedir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+libpam_misc_la_DEPENDENCIES = $(top_builddir)/libpam/libpam.la
+am_libpam_misc_la_OBJECTS = help_env.lo misc_conv.lo
+libpam_misc_la_OBJECTS = $(am_libpam_misc_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libpam_misc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libpam_misc_la_LDFLAGS) $(LDFLAGS) -o \
+ $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/help_env.Plo \
+ ./$(DEPDIR)/misc_conv.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libpam_misc_la_SOURCES)
+DIST_SOURCES = $(libpam_misc_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+DATA = $(pkgconfig_DATA)
+HEADERS = $(include_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/pam_misc.pc.in \
+ $(top_srcdir)/build-aux/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BROWSER = @BROWSER@
+BUILD_CFLAGS = @BUILD_CFLAGS@
+BUILD_CPPFLAGS = @BUILD_CPPFLAGS@
+BUILD_LDFLAGS = @BUILD_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRYPTO_LIBS = @CRYPTO_LIBS@
+CRYPT_CFLAGS = @CRYPT_CFLAGS@
+CRYPT_LIBS = @CRYPT_LIBS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+ECONF_CFLAGS = @ECONF_CFLAGS@
+ECONF_LIBS = @ECONF_LIBS@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXE_CFLAGS = @EXE_CFLAGS@
+EXE_LDFLAGS = @EXE_LDFLAGS@
+FGREP = @FGREP@
+FO2PDF = @FO2PDF@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBAUDIT = @LIBAUDIT@
+LIBCRYPT = @LIBCRYPT@
+LIBDB = @LIBDB@
+LIBDL = @LIBDL@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBPRELUDE_CFLAGS = @LIBPRELUDE_CFLAGS@
+LIBPRELUDE_CONFIG = @LIBPRELUDE_CONFIG@
+LIBPRELUDE_CONFIG_PREFIX = @LIBPRELUDE_CONFIG_PREFIX@
+LIBPRELUDE_LDFLAGS = @LIBPRELUDE_LDFLAGS@
+LIBPRELUDE_LIBS = @LIBPRELUDE_LIBS@
+LIBPRELUDE_PREFIX = @LIBPRELUDE_PREFIX@
+LIBPRELUDE_PTHREAD_CFLAGS = @LIBPRELUDE_PTHREAD_CFLAGS@
+LIBS = @LIBS@
+LIBSELINUX = @LIBSELINUX@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NIS_CFLAGS = @NIS_CFLAGS@
+NIS_LIBS = @NIS_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NSL_CFLAGS = @NSL_CFLAGS@
+NSL_LIBS = @NSL_LIBS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POSUB = @POSUB@
+RANLIB = @RANLIB@
+SCONFIGDIR = @SCONFIGDIR@
+SECUREDIR = @SECUREDIR@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRINGPARAM_HMAC = @STRINGPARAM_HMAC@
+STRINGPARAM_VENDORDIR = @STRINGPARAM_VENDORDIR@
+STRIP = @STRIP@
+TIRPC_CFLAGS = @TIRPC_CFLAGS@
+TIRPC_LIBS = @TIRPC_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WARN_CFLAGS = @WARN_CFLAGS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+XMLCATALOG = @XMLCATALOG@
+XMLLINT = @XMLLINT@
+XML_CATALOG_FILE = @XML_CATALOG_FILE@
+XSLTPROC = @XSLTPROC@
+YACC = @YACC@
+YFLAGS = @YFLAGS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pam_xauth_path = @pam_xauth_path@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+systemdunitdir = @systemdunitdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+CLEANFILES = *~
+EXTRA_DIST = libpam_misc.map
+include_HEADERS = include/security/pam_misc.h
+AM_CFLAGS = -I$(top_srcdir)/libpam/include \
+ -I$(top_srcdir)/libpamc/include -I$(srcdir)/include $(WARN_CFLAGS)
+
+libpam_misc_la_LDFLAGS = -no-undefined -version-info 82:1:82 \
+ $(am__append_1)
+libpam_misc_la_LIBADD = $(top_builddir)/libpam/libpam.la
+lib_LTLIBRARIES = libpam_misc.la
+libpam_misc_la_SOURCES = help_env.c misc_conv.c
+
+# Pkg-config script.
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = pam_misc.pc
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libpam_misc/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu libpam_misc/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+pam_misc.pc: $(top_builddir)/config.status $(srcdir)/pam_misc.pc.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libpam_misc.la: $(libpam_misc_la_OBJECTS) $(libpam_misc_la_DEPENDENCIES) $(EXTRA_libpam_misc_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libpam_misc_la_LINK) -rpath $(libdir) $(libpam_misc_la_OBJECTS) $(libpam_misc_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/help_env.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc_conv.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-pkgconfigDATA: $(pkgconfig_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \
+ done
+
+uninstall-pkgconfigDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir)
+install-includeHEADERS: $(include_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \
+ done
+
+uninstall-includeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(includedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/help_env.Plo
+ -rm -f ./$(DEPDIR)/misc_conv.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-includeHEADERS install-pkgconfigDATA
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/help_env.Plo
+ -rm -f ./$(DEPDIR)/misc_conv.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES \
+ uninstall-pkgconfigDATA
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libLTLIBRARIES clean-libtool cscopelist-am \
+ ctags ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am \
+ install-includeHEADERS install-info install-info-am \
+ install-libLTLIBRARIES install-man install-pdf install-pdf-am \
+ install-pkgconfigDATA install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am uninstall-includeHEADERS \
+ uninstall-libLTLIBRARIES uninstall-pkgconfigDATA
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libpam_misc/help_env.c b/libpam_misc/help_env.c
new file mode 100644
index 0000000..601c5f4
--- /dev/null
+++ b/libpam_misc/help_env.c
@@ -0,0 +1,88 @@
+/*
+ * $Id$
+ *
+ * This file was written by Andrew G. Morgan <morgan@parc.power.net>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <security/pam_misc.h>
+
+/*
+ * This function should be used to carefully dispose of the copied
+ * environment.
+ *
+ * usage: env = pam_misc_drop_env(env);
+ */
+
+char **pam_misc_drop_env(char **dump)
+{
+ int i;
+
+ for (i=0; dump[i] != NULL; ++i) {
+ D(("dump[%d]=`%s'", i, dump[i]));
+ _pam_overwrite(dump[i]);
+ _pam_drop(dump[i]);
+ }
+ _pam_drop(dump);
+
+ return NULL;
+}
+
+/*
+ * This function takes the supplied environment and uploads it to be
+ * the PAM one.
+ */
+
+int pam_misc_paste_env(pam_handle_t *pamh, const char * const * user_env)
+{
+ for (; user_env && *user_env; ++user_env) {
+ int retval;
+
+ D(("uploading: %s", *user_env));
+ retval = pam_putenv(pamh, *user_env);
+ if (retval != PAM_SUCCESS) {
+ D(("error setting %s: %s", *user_env, pam_strerror(pamh,retval)));
+ return retval;
+ }
+ }
+ D(("done."));
+ return PAM_SUCCESS;
+}
+
+/*
+ * This is a wrapper to make pam behave in the way that setenv() does.
+ */
+
+int pam_misc_setenv(pam_handle_t *pamh, const char *name
+ , const char *value, int readonly)
+{
+ char *tmp;
+ int retval;
+
+ if (readonly) {
+ const char *etmp;
+
+ /* we check if the variable is there already */
+ etmp = pam_getenv(pamh, name);
+ if (etmp != NULL) {
+ D(("failed to set readonly variable: %s", name));
+ return PAM_PERM_DENIED; /* not allowed to overwrite */
+ }
+ }
+ if (asprintf(&tmp, "%s=%s", name, value) >= 0) {
+ D(("pam_putt()ing: %s", tmp));
+ retval = pam_putenv(pamh, tmp);
+ _pam_overwrite(tmp); /* purge */
+ _pam_drop(tmp); /* forget */
+ } else {
+ D(("malloc failure"));
+ retval = PAM_BUF_ERR;
+ }
+
+ return retval;
+}
diff --git a/libpam_misc/include/security/pam_misc.h b/libpam_misc/include/security/pam_misc.h
new file mode 100644
index 0000000..fca2422
--- /dev/null
+++ b/libpam_misc/include/security/pam_misc.h
@@ -0,0 +1,52 @@
+/* $Id$ */
+
+#ifndef __PAMMISC_H
+#define __PAMMISC_H
+
+#include <security/pam_appl.h>
+#include <security/pam_client.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* include some useful macros */
+
+#include <security/_pam_macros.h>
+
+/* functions defined in pam_misc.* libraries */
+
+extern int misc_conv(int num_msg, const struct pam_message **msgm,
+ struct pam_response **response, void *appdata_ptr);
+
+#include <time.h>
+
+extern time_t pam_misc_conv_warn_time; /* time that we should warn user */
+extern time_t pam_misc_conv_die_time; /* cut-off time for input */
+extern const char *pam_misc_conv_warn_line; /* warning notice */
+extern const char *pam_misc_conv_die_line; /* cut-off remark */
+extern int pam_misc_conv_died; /* 1 = cut-off time reached (0 not) */
+extern int (*pam_binary_handler_fn)(void *appdata, pamc_bp_t *prompt_p);
+extern void (*pam_binary_handler_free)(void *appdata, pamc_bp_t *prompt_p);
+/*
+ * Environment helper functions
+ */
+
+/* transcribe given environment (to pam) */
+extern int pam_misc_paste_env(pam_handle_t *pamh
+ , const char * const * user_env);
+
+/* delete environment as obtained from (pam_getenvlist) */
+extern char **pam_misc_drop_env(char **env);
+
+/* provide something like the POSIX setenv function for the (Linux-)PAM
+ * environment. */
+
+extern int pam_misc_setenv(pam_handle_t *pamh, const char *name
+ , const char *value, int readonly);
+
+#ifdef __cplusplus
+}
+#endif /* def __cplusplus */
+
+#endif /* ndef __PAMMISC_H */
diff --git a/libpam_misc/libpam_misc.map b/libpam_misc/libpam_misc.map
new file mode 100644
index 0000000..2f28d5c
--- /dev/null
+++ b/libpam_misc/libpam_misc.map
@@ -0,0 +1,17 @@
+LIBPAM_MISC_1.0 {
+ global:
+ misc_conv;
+ pam_misc_conv_warn_time;
+ pam_misc_conv_die_time;
+ pam_misc_conv_warn_line;
+ pam_misc_conv_die_line;
+ pam_misc_conv_died;
+ pam_binary_handler_fn;
+ pam_binary_handler_free;
+ pam_misc_paste_env;
+ pam_misc_drop_env;
+ pam_misc_setenv;
+
+ local:
+ *;
+};
diff --git a/libpam_misc/misc_conv.c b/libpam_misc/misc_conv.c
new file mode 100644
index 0000000..908ee89
--- /dev/null
+++ b/libpam_misc/misc_conv.c
@@ -0,0 +1,401 @@
+/*
+ * A generic conversation function for text based applications
+ *
+ * Written by Andrew Morgan <morgan@linux.kernel.org>
+ */
+
+#include "config.h"
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <security/pam_appl.h>
+#include <security/pam_misc.h>
+
+#define INPUTSIZE PAM_MISC_CONV_BUFSIZE /* maximum length of input+1 */
+#define CONV_ECHO_ON 1 /* types of echo state */
+#define CONV_ECHO_OFF 0
+
+/*
+ * external timeout definitions - these can be overridden by the
+ * application.
+ */
+
+time_t pam_misc_conv_warn_time = 0; /* time when we warn */
+time_t pam_misc_conv_die_time = 0; /* time when we timeout */
+
+const char *pam_misc_conv_warn_line = N_("...Time is running out...\n");
+const char *pam_misc_conv_die_line = N_("...Sorry, your time is up!\n");
+
+int pam_misc_conv_died=0; /* application can probe this for timeout */
+
+/*
+ * These functions are for binary prompt manipulation.
+ * The manner in which a binary prompt is processed is application
+ * specific, so these function pointers are provided and can be
+ * initialized by the application prior to the conversation function
+ * being used.
+ */
+
+static void pam_misc_conv_delete_binary(void *appdata UNUSED,
+ pamc_bp_t *delete_me)
+{
+ PAM_BP_RENEW(delete_me, 0, 0);
+}
+
+int (*pam_binary_handler_fn)(void *appdata, pamc_bp_t *prompt_p) = NULL;
+void (*pam_binary_handler_free)(void *appdata, pamc_bp_t *prompt_p)
+ = pam_misc_conv_delete_binary;
+
+/* the following code is used to get text input */
+
+static volatile int expired=0;
+
+/* return to the previous signal handling */
+static void reset_alarm(struct sigaction *o_ptr)
+{
+ (void) alarm(0); /* stop alarm clock - if still ticking */
+ (void) sigaction(SIGALRM, o_ptr, NULL);
+}
+
+/* this is where we intercept the alarm signal */
+static void time_is_up(int ignore UNUSED)
+{
+ expired = 1;
+}
+
+/* set the new alarm to hit the time_is_up() function */
+static int set_alarm(int delay, struct sigaction *o_ptr)
+{
+ struct sigaction new_sig;
+
+ sigemptyset(&new_sig.sa_mask);
+ new_sig.sa_flags = 0;
+ new_sig.sa_handler = time_is_up;
+ if ( sigaction(SIGALRM, &new_sig, o_ptr) ) {
+ return 1; /* setting signal failed */
+ }
+ if ( alarm(delay) ) {
+ (void) sigaction(SIGALRM, o_ptr, NULL);
+ return 1; /* failed to set alarm */
+ }
+ return 0; /* all seems to have worked */
+}
+
+/* return the number of seconds to next alarm. 0 = no delay, -1 = expired */
+static int get_delay(void)
+{
+ time_t now;
+
+ expired = 0; /* reset flag */
+ (void) time(&now);
+
+ /* has the quit time past? */
+ if (pam_misc_conv_die_time && now >= pam_misc_conv_die_time) {
+ fprintf(stderr,"%s",pam_misc_conv_die_line);
+
+ pam_misc_conv_died = 1; /* note we do not reset the die_time */
+ return -1; /* time is up */
+ }
+
+ /* has the warning time past? */
+ if (pam_misc_conv_warn_time && now >= pam_misc_conv_warn_time) {
+ fprintf(stderr, "%s", pam_misc_conv_warn_line);
+ pam_misc_conv_warn_time = 0; /* reset warn_time */
+
+ /* indicate remaining delay - if any */
+
+ return (pam_misc_conv_die_time ? pam_misc_conv_die_time - now:0 );
+ }
+
+ /* indicate possible warning delay */
+
+ if (pam_misc_conv_warn_time)
+ return (pam_misc_conv_warn_time - now);
+ else if (pam_misc_conv_die_time)
+ return (pam_misc_conv_die_time - now);
+ else
+ return 0;
+}
+
+/* read a line of input string, giving prompt when appropriate */
+static int read_string(int echo, const char *prompt, char **retstr)
+{
+ struct termios term_before, term_tmp;
+ char line[INPUTSIZE];
+ struct sigaction old_sig;
+ int delay, nc = -1, have_term = 0;
+ sigset_t oset, nset;
+
+ D(("called with echo='%s', prompt='%s'.", echo ? "ON":"OFF" , prompt));
+
+ if (isatty(STDIN_FILENO)) { /* terminal state */
+
+ /* is a terminal so record settings and flush it */
+ if ( tcgetattr(STDIN_FILENO, &term_before) != 0 ) {
+ D(("<error: failed to get terminal settings>"));
+ *retstr = NULL;
+ return -1;
+ }
+ memcpy(&term_tmp, &term_before, sizeof(term_tmp));
+ if (!echo) {
+ term_tmp.c_lflag &= ~(ECHO);
+ }
+ have_term = 1;
+
+ /*
+ * We make a simple attempt to block TTY signals from suspending
+ * the conversation without giving PAM a chance to clean up.
+ */
+
+ sigemptyset(&nset);
+ sigaddset(&nset, SIGTSTP);
+ (void) sigprocmask(SIG_BLOCK, &nset, &oset);
+
+ } else if (!echo) {
+ D(("<warning: cannot turn echo off>"));
+ }
+
+ /* set up the signal handling */
+ delay = get_delay();
+
+ /* reading the line */
+ while (delay >= 0) {
+ /* this may, or may not set echo off -- drop pending input */
+ if (have_term)
+ (void) tcsetattr(STDIN_FILENO, TCSAFLUSH, &term_tmp);
+
+ fprintf(stderr, "%s", prompt);
+
+ if ( delay > 0 && set_alarm(delay, &old_sig) ) {
+ D(("<failed to set alarm>"));
+ break;
+ } else {
+ if (have_term)
+ nc = read(STDIN_FILENO, line, INPUTSIZE-1);
+ else /* we must read one line only */
+ for (nc = 0; nc < INPUTSIZE-1 && (nc?line[nc-1]:0) != '\n';
+ nc++) {
+ int rv;
+ if ((rv=read(STDIN_FILENO, line+nc, 1)) != 1) {
+ if (rv < 0) {
+ _pam_overwrite_n(line, (unsigned int) nc);
+ nc = rv;
+ }
+ break;
+ }
+ }
+ if (have_term) {
+ (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &term_before);
+ if (!echo || expired) /* do we need a newline? */
+ fprintf(stderr, "\n");
+ }
+ if ( delay > 0 ) {
+ reset_alarm(&old_sig);
+ }
+ if (expired) {
+ delay = get_delay();
+ } else if (nc > 0) { /* we got some user input */
+ D(("we got some user input"));
+
+ if (line[nc-1] == '\n') { /* <NUL> terminate */
+ line[--nc] = '\0';
+ } else {
+ if (echo) {
+ fprintf(stderr, "\n");
+ }
+ line[nc] = '\0';
+ }
+ *retstr = strdup(line);
+ _pam_overwrite(line);
+ if (!*retstr) {
+ D(("no memory for response string"));
+ nc = -1;
+ }
+
+ goto cleanexit; /* return malloc()ed string */
+
+ } else if (nc == 0) { /* Ctrl-D */
+ D(("user did not want to type anything"));
+
+ *retstr = NULL;
+ if (echo) {
+ fprintf(stderr, "\n");
+ }
+ goto cleanexit; /* return malloc()ed "" */
+ } else if (nc == -1) {
+ /* Don't loop forever if read() returns -1. */
+ D(("error reading input from the user: %m"));
+ if (echo) {
+ fprintf(stderr, "\n");
+ }
+ *retstr = NULL;
+ goto cleanexit; /* return NULL */
+ }
+ }
+ }
+
+ /* getting here implies that the timer expired */
+
+ D(("the timer appears to have expired"));
+
+ *retstr = NULL;
+ _pam_overwrite_n(line, sizeof(line));
+
+ cleanexit:
+
+ if (have_term) {
+ (void) sigprocmask(SIG_SETMASK, &oset, NULL);
+ (void) tcsetattr(STDIN_FILENO, TCSAFLUSH, &term_before);
+ }
+
+ return nc;
+}
+
+/* end of read_string functions */
+
+/*
+ * This conversation function is supposed to be a generic PAM one.
+ * Unfortunately, it is _not_ completely compatible with the Solaris PAM
+ * codebase.
+ *
+ * Namely, for msgm's that contain multiple prompts, this function
+ * interprets "const struct pam_message **msgm" as equivalent to
+ * "const struct pam_message *msgm[]". The Solaris module
+ * implementation interprets the **msgm object as a pointer to a
+ * pointer to an array of "struct pam_message" objects (that is, a
+ * confusing amount of pointer indirection).
+ */
+
+int misc_conv(int num_msg, const struct pam_message **msgm,
+ struct pam_response **response, void *appdata_ptr)
+{
+ int count=0;
+ struct pam_response *reply;
+
+ if (num_msg <= 0)
+ return PAM_CONV_ERR;
+
+ D(("allocating empty response structure array."));
+
+ reply = (struct pam_response *) calloc(num_msg,
+ sizeof(struct pam_response));
+ if (reply == NULL) {
+ D(("no memory for responses"));
+ return PAM_CONV_ERR;
+ }
+
+ D(("entering conversation function."));
+
+ for (count=0; count < num_msg; ++count) {
+ char *string=NULL;
+ int nc;
+
+ switch (msgm[count]->msg_style) {
+ case PAM_PROMPT_ECHO_OFF:
+ nc = read_string(CONV_ECHO_OFF,msgm[count]->msg, &string);
+ if (nc < 0) {
+ goto failed_conversation;
+ }
+ break;
+ case PAM_PROMPT_ECHO_ON:
+ nc = read_string(CONV_ECHO_ON,msgm[count]->msg, &string);
+ if (nc < 0) {
+ goto failed_conversation;
+ }
+ break;
+ case PAM_ERROR_MSG:
+ if (fprintf(stderr,"%s\n",msgm[count]->msg) < 0) {
+ goto failed_conversation;
+ }
+ break;
+ case PAM_TEXT_INFO:
+ if (fprintf(stdout,"%s\n",msgm[count]->msg) < 0) {
+ goto failed_conversation;
+ }
+ break;
+ case PAM_BINARY_PROMPT:
+ {
+ pamc_bp_t binary_prompt = NULL;
+
+ if (!msgm[count]->msg || !pam_binary_handler_fn) {
+ goto failed_conversation;
+ }
+
+ PAM_BP_RENEW(&binary_prompt,
+ PAM_BP_RCONTROL(msgm[count]->msg),
+ PAM_BP_LENGTH(msgm[count]->msg));
+ PAM_BP_FILL(binary_prompt, 0, PAM_BP_LENGTH(msgm[count]->msg),
+ PAM_BP_RDATA(msgm[count]->msg));
+
+ if (pam_binary_handler_fn(appdata_ptr,
+ &binary_prompt) != PAM_SUCCESS
+ || (binary_prompt == NULL)) {
+ goto failed_conversation;
+ }
+ string = (char *) binary_prompt;
+ binary_prompt = NULL;
+
+ break;
+ }
+ default:
+ fprintf(stderr, _("erroneous conversation (%d)\n"),
+ msgm[count]->msg_style);
+ goto failed_conversation;
+ }
+
+ if (string) { /* must add to reply array */
+ /* add string to list of responses */
+
+ reply[count].resp_retcode = 0;
+ reply[count].resp = string;
+ string = NULL;
+ }
+ }
+
+ *response = reply;
+ reply = NULL;
+
+ return PAM_SUCCESS;
+
+failed_conversation:
+
+ D(("the conversation failed"));
+
+ if (reply) {
+ for (count=0; count<num_msg; ++count) {
+ if (reply[count].resp == NULL) {
+ continue;
+ }
+ switch (msgm[count]->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ case PAM_PROMPT_ECHO_OFF:
+ _pam_overwrite(reply[count].resp);
+ free(reply[count].resp);
+ break;
+ case PAM_BINARY_PROMPT:
+ {
+ void *bt_ptr = reply[count].resp;
+ pam_binary_handler_free(appdata_ptr, bt_ptr);
+ break;
+ }
+ case PAM_ERROR_MSG:
+ case PAM_TEXT_INFO:
+ /* should not actually be able to get here... */
+ free(reply[count].resp);
+ }
+ reply[count].resp = NULL;
+ }
+ /* forget reply too */
+ free(reply);
+ reply = NULL;
+ }
+
+ return PAM_CONV_ERR;
+}
diff --git a/libpam_misc/pam_misc.pc.in b/libpam_misc/pam_misc.pc.in
new file mode 100644
index 0000000..0c8898c
--- /dev/null
+++ b/libpam_misc/pam_misc.pc.in
@@ -0,0 +1,9 @@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: pam_misc
+Description: Miscellaneous functions that make the job of writing PAM-aware applications easier.
+URL: http://www.linux-pam.org/
+Version: @VERSION@
+Cflags: -I${includedir}
+Libs: -L${libdir} -lpam_misc
diff --git a/libpamc/License b/libpamc/License
new file mode 100644
index 0000000..9316078
--- /dev/null
+++ b/libpamc/License
@@ -0,0 +1,41 @@
+Unless otherwise *explicitly* stated the following text describes the
+licensed conditions under which the contents of this libpamc release
+may be distributed:
+
+-------------------------------------------------------------------------
+Redistribution and use in source and binary forms of libpamc,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions of source code must retain any existing copyright
+ notice, and this entire permission notice in its entirety,
+ including the disclaimer of warranties.
+
+2. Redistributions in binary form must reproduce all prior and current
+ copyright notices, this list of conditions, and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+3. The name of any author may not be used to endorse or promote
+ products derived from this software without their specific prior
+ written permission.
+
+ALTERNATIVELY, this product may be distributed under the terms of the
+GNU Library General Public License (LGPL), in which case the
+provisions of the GNU LGPL are required INSTEAD OF the above
+restrictions. (This clause is necessary due to a potential conflict
+between the GNU LGPL and the restrictions contained in a BSD-style
+copyright.)
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+-------------------------------------------------------------------------
diff --git a/libpamc/Makefile.am b/libpamc/Makefile.am
new file mode 100644
index 0000000..2ebeadc
--- /dev/null
+++ b/libpamc/Makefile.am
@@ -0,0 +1,28 @@
+#
+# Copyright (c) 2005 Thorsten Kukuk <kukuk@suse.de>
+#
+
+SUBDIRS = test
+
+CLEANFILES = *~
+
+EXTRA_DIST = License libpamc.map
+
+include_HEADERS = include/security/pam_client.h
+
+noinst_HEADERS = libpamc.h
+
+AM_CFLAGS=-I$(top_srcdir)/libpam/include -I$(srcdir)/include $(WARN_CFLAGS)
+
+libpamc_la_LDFLAGS = -no-undefined -version-info 82:1:82
+if HAVE_VERSIONING
+ libpamc_la_LDFLAGS += -Wl,--version-script=$(srcdir)/libpamc.map
+endif
+
+lib_LTLIBRARIES = libpamc.la
+
+libpamc_la_SOURCES = pamc_client.c pamc_converse.c pamc_load.c
+
+# Pkg-config script.
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = pamc.pc
diff --git a/libpamc/Makefile.in b/libpamc/Makefile.in
new file mode 100644
index 0000000..424c175
--- /dev/null
+++ b/libpamc/Makefile.in
@@ -0,0 +1,924 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+#
+# Copyright (c) 2005 Thorsten Kukuk <kukuk@suse.de>
+#
+
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@HAVE_VERSIONING_TRUE@am__append_1 = -Wl,--version-script=$(srcdir)/libpamc.map
+subdir = libpamc
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/attribute.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/intlmacosx.m4 \
+ $(top_srcdir)/m4/jh_path_xml_catalog.m4 \
+ $(top_srcdir)/m4/ld-O1.m4 $(top_srcdir)/m4/ld-as-needed.m4 \
+ $(top_srcdir)/m4/ld-no-undefined.m4 \
+ $(top_srcdir)/m4/ld-z-now.m4 $(top_srcdir)/m4/lib-ld.m4 \
+ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+ $(top_srcdir)/m4/libprelude.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 \
+ $(top_srcdir)/m4/warn_lang_flags.m4 \
+ $(top_srcdir)/m4/warnings.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(include_HEADERS) \
+ $(noinst_HEADERS) $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES = pamc.pc
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" \
+ "$(DESTDIR)$(includedir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+libpamc_la_LIBADD =
+am_libpamc_la_OBJECTS = pamc_client.lo pamc_converse.lo pamc_load.lo
+libpamc_la_OBJECTS = $(am_libpamc_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libpamc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libpamc_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/pamc_client.Plo \
+ ./$(DEPDIR)/pamc_converse.Plo ./$(DEPDIR)/pamc_load.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libpamc_la_SOURCES)
+DIST_SOURCES = $(libpamc_la_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+DATA = $(pkgconfig_DATA)
+HEADERS = $(include_HEADERS) $(noinst_HEADERS)
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/pamc.pc.in \
+ $(top_srcdir)/build-aux/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BROWSER = @BROWSER@
+BUILD_CFLAGS = @BUILD_CFLAGS@
+BUILD_CPPFLAGS = @BUILD_CPPFLAGS@
+BUILD_LDFLAGS = @BUILD_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRYPTO_LIBS = @CRYPTO_LIBS@
+CRYPT_CFLAGS = @CRYPT_CFLAGS@
+CRYPT_LIBS = @CRYPT_LIBS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+ECONF_CFLAGS = @ECONF_CFLAGS@
+ECONF_LIBS = @ECONF_LIBS@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXE_CFLAGS = @EXE_CFLAGS@
+EXE_LDFLAGS = @EXE_LDFLAGS@
+FGREP = @FGREP@
+FO2PDF = @FO2PDF@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBAUDIT = @LIBAUDIT@
+LIBCRYPT = @LIBCRYPT@
+LIBDB = @LIBDB@
+LIBDL = @LIBDL@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBPRELUDE_CFLAGS = @LIBPRELUDE_CFLAGS@
+LIBPRELUDE_CONFIG = @LIBPRELUDE_CONFIG@
+LIBPRELUDE_CONFIG_PREFIX = @LIBPRELUDE_CONFIG_PREFIX@
+LIBPRELUDE_LDFLAGS = @LIBPRELUDE_LDFLAGS@
+LIBPRELUDE_LIBS = @LIBPRELUDE_LIBS@
+LIBPRELUDE_PREFIX = @LIBPRELUDE_PREFIX@
+LIBPRELUDE_PTHREAD_CFLAGS = @LIBPRELUDE_PTHREAD_CFLAGS@
+LIBS = @LIBS@
+LIBSELINUX = @LIBSELINUX@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NIS_CFLAGS = @NIS_CFLAGS@
+NIS_LIBS = @NIS_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NSL_CFLAGS = @NSL_CFLAGS@
+NSL_LIBS = @NSL_LIBS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POSUB = @POSUB@
+RANLIB = @RANLIB@
+SCONFIGDIR = @SCONFIGDIR@
+SECUREDIR = @SECUREDIR@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRINGPARAM_HMAC = @STRINGPARAM_HMAC@
+STRINGPARAM_VENDORDIR = @STRINGPARAM_VENDORDIR@
+STRIP = @STRIP@
+TIRPC_CFLAGS = @TIRPC_CFLAGS@
+TIRPC_LIBS = @TIRPC_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WARN_CFLAGS = @WARN_CFLAGS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+XMLCATALOG = @XMLCATALOG@
+XMLLINT = @XMLLINT@
+XML_CATALOG_FILE = @XML_CATALOG_FILE@
+XSLTPROC = @XSLTPROC@
+YACC = @YACC@
+YFLAGS = @YFLAGS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pam_xauth_path = @pam_xauth_path@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+systemdunitdir = @systemdunitdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = test
+CLEANFILES = *~
+EXTRA_DIST = License libpamc.map
+include_HEADERS = include/security/pam_client.h
+noinst_HEADERS = libpamc.h
+AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(srcdir)/include $(WARN_CFLAGS)
+libpamc_la_LDFLAGS = -no-undefined -version-info 82:1:82 \
+ $(am__append_1)
+lib_LTLIBRARIES = libpamc.la
+libpamc_la_SOURCES = pamc_client.c pamc_converse.c pamc_load.c
+
+# Pkg-config script.
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = pamc.pc
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libpamc/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu libpamc/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+pamc.pc: $(top_builddir)/config.status $(srcdir)/pamc.pc.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libpamc.la: $(libpamc_la_OBJECTS) $(libpamc_la_DEPENDENCIES) $(EXTRA_libpamc_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libpamc_la_LINK) -rpath $(libdir) $(libpamc_la_OBJECTS) $(libpamc_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pamc_client.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pamc_converse.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pamc_load.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-pkgconfigDATA: $(pkgconfig_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \
+ done
+
+uninstall-pkgconfigDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir)
+install-includeHEADERS: $(include_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \
+ done
+
+uninstall-includeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(includedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f ./$(DEPDIR)/pamc_client.Plo
+ -rm -f ./$(DEPDIR)/pamc_converse.Plo
+ -rm -f ./$(DEPDIR)/pamc_load.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-includeHEADERS install-pkgconfigDATA
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f ./$(DEPDIR)/pamc_client.Plo
+ -rm -f ./$(DEPDIR)/pamc_converse.Plo
+ -rm -f ./$(DEPDIR)/pamc_load.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES \
+ uninstall-pkgconfigDATA
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-am clean clean-generic \
+ clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \
+ ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am \
+ install-includeHEADERS install-info install-info-am \
+ install-libLTLIBRARIES install-man install-pdf install-pdf-am \
+ install-pkgconfigDATA install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs installdirs-am \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \
+ uninstall-includeHEADERS uninstall-libLTLIBRARIES \
+ uninstall-pkgconfigDATA
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libpamc/include/security/pam_client.h b/libpamc/include/security/pam_client.h
new file mode 100644
index 0000000..41f83da
--- /dev/null
+++ b/libpamc/include/security/pam_client.h
@@ -0,0 +1,197 @@
+/*
+ * $Id$
+ *
+ * Copyright (c) 1999 Andrew G. Morgan <morgan@linux.kernel.org>
+ *
+ * This header file provides the prototypes for the PAM client API
+ */
+
+#ifndef PAM_CLIENT_H
+#define PAM_CLIENT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* def __cplusplus */
+
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+/* opaque agent handling structure */
+
+typedef struct pamc_handle_s *pamc_handle_t;
+
+/* binary prompt structure pointer */
+typedef struct { uint32_t length; uint8_t control; } *pamc_bp_t;
+
+/*
+ * functions provided by libpamc
+ */
+
+/*
+ * Initialize the agent abstraction library
+ */
+
+pamc_handle_t pamc_start(void);
+
+/*
+ * Terminate the authentication process
+ */
+
+int pamc_end(pamc_handle_t *pch);
+
+/*
+ * force the loading of a specified agent
+ */
+
+int pamc_load(pamc_handle_t pch, const char *agent_id);
+
+/*
+ * Single conversation interface for binary prompts
+ */
+
+int pamc_converse(pamc_handle_t pch, pamc_bp_t *prompt_p);
+
+/*
+ * disable an agent
+ */
+
+int pamc_disable(pamc_handle_t pch, const char *agent_id);
+
+/*
+ * obtain a list of available agents
+ */
+
+char **pamc_list_agents(pamc_handle_t pch);
+
+/*
+ * PAM_BP_ MACROS for creating, destroying and manipulating binary prompts
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#ifndef PAM_BP_ASSERT
+# ifdef NDEBUG
+# define PAM_BP_ASSERT(x) do {} while (0)
+# else
+# define PAM_BP_ASSERT(x) do { printf(__FILE__ "(%d): %s\n", \
+ __LINE__, x) ; exit(1); } while (0)
+# endif /* NDEBUG */
+#endif /* PAM_BP_ASSERT */
+
+#ifndef PAM_BP_CALLOC
+# define PAM_BP_CALLOC calloc
+#endif /* PAM_BP_CALLOC */
+
+#ifndef PAM_BP_FREE
+# define PAM_BP_FREE free
+#endif /* PAM_BP_FREE */
+
+#define __PAM_BP_WOCTET(x,y) (*((y) + (uint8_t *)(x)))
+#define __PAM_BP_ROCTET(x,y) (*((y) + (const uint8_t *)(x)))
+
+#define PAM_BP_MIN_SIZE (sizeof(uint32_t) + sizeof(uint8_t))
+#define PAM_BP_MAX_LENGTH 0x20000 /* an advisory limit */
+#define PAM_BP_WCONTROL(x) (__PAM_BP_WOCTET(x,4))
+#define PAM_BP_RCONTROL(x) (__PAM_BP_ROCTET(x,4))
+#define PAM_BP_SIZE(x) ((__PAM_BP_ROCTET(x,0)<<24)+ \
+ (__PAM_BP_ROCTET(x,1)<<16)+ \
+ (__PAM_BP_ROCTET(x,2)<< 8)+ \
+ (__PAM_BP_ROCTET(x,3) ))
+#define PAM_BP_LENGTH(x) (PAM_BP_SIZE(x) - PAM_BP_MIN_SIZE)
+#define PAM_BP_WDATA(x) (PAM_BP_MIN_SIZE + (uint8_t *) (x))
+#define PAM_BP_RDATA(x) (PAM_BP_MIN_SIZE + (const uint8_t *) (x))
+
+/* Note, this macro always '\0' terminates renewed packets */
+
+#define PAM_BP_RENEW(old_p, cntrl, data_length) \
+do { \
+ if ((old_p) != NULL) { \
+ if (*(old_p)) { \
+ uint32_t __size; \
+ __size = PAM_BP_SIZE(*(old_p)); \
+ memset(*(old_p), 0, __size); \
+ PAM_BP_FREE(*(old_p)); \
+ } \
+ if (cntrl) { \
+ uint32_t __size; \
+ \
+ __size = PAM_BP_MIN_SIZE + data_length; \
+ if ((*(old_p) = PAM_BP_CALLOC(1, 1+__size))) { \
+ __PAM_BP_WOCTET(*(old_p), 3) = __size & 0xFF; \
+ __PAM_BP_WOCTET(*(old_p), 2) = (__size>>=8) & 0xFF; \
+ __PAM_BP_WOCTET(*(old_p), 1) = (__size>>=8) & 0xFF; \
+ __PAM_BP_WOCTET(*(old_p), 0) = (__size>>=8) & 0xFF; \
+ (*(old_p))->control = cntrl; \
+ } else { \
+ PAM_BP_ASSERT("out of memory for binary prompt"); \
+ } \
+ } else { \
+ *old_p = NULL; \
+ } \
+ } else { \
+ PAM_BP_ASSERT("programming error, invalid binary prompt pointer"); \
+ } \
+} while (0)
+
+#define PAM_BP_FILL(prmpt, offset, length, data) \
+do { \
+ size_t bp_length; \
+ uint8_t *prompt = (uint8_t *) (prmpt); \
+ bp_length = PAM_BP_LENGTH(prompt); \
+ if (bp_length < ((length)+(offset))) { \
+ PAM_BP_ASSERT("attempt to write over end of prompt"); \
+ } \
+ memcpy((offset) + PAM_BP_WDATA(prompt), (data), (length)); \
+} while (0)
+
+#define PAM_BP_EXTRACT(prmpt, offset, length, data) \
+do { \
+ size_t __bp_length; \
+ const uint8_t *__prompt = (const uint8_t *) (prmpt); \
+ __bp_length = PAM_BP_LENGTH(__prompt); \
+ if (((offset) < 0) || (__bp_length < ((length)+(offset))) \
+ || ((length) < 0)) { \
+ PAM_BP_ASSERT("invalid extraction from prompt"); \
+ } \
+ memcpy((data), (offset) + PAM_BP_RDATA(__prompt), (length)); \
+} while (0)
+
+
+/* Control types */
+
+#define PAM_BPC_FALSE 0
+#define PAM_BPC_TRUE 1
+
+#define PAM_BPC_OK 0x01 /* continuation packet */
+#define PAM_BPC_SELECT 0x02 /* initialization packet */
+#define PAM_BPC_DONE 0x03 /* termination packet */
+#define PAM_BPC_FAIL 0x04 /* unable to execute */
+
+/* The following control characters are only legal for echanges
+ between an agent and a client (it is the responsibility of the
+ client to enforce this rule in the face of a rogue server): */
+
+#define PAM_BPC_GETENV 0x41 /* obtain client env.var */
+#define PAM_BPC_PUTENV 0x42 /* set client env.var */
+#define PAM_BPC_TEXT 0x43 /* display message */
+#define PAM_BPC_ERROR 0x44 /* display error message */
+#define PAM_BPC_PROMPT 0x45 /* echo'd text prompt */
+#define PAM_BPC_PASS 0x46 /* non-echo'd text prompt*/
+
+/* quick check for prompts that are legal for the client (by
+ implication the server too) to send to libpamc */
+
+#define PAM_BPC_FOR_CLIENT(/* pamc_bp_t */ prompt) \
+ (((prompt)->control <= PAM_BPC_FAIL && (prompt)->control >= PAM_BPC_OK) \
+ ? PAM_BPC_TRUE:PAM_BPC_FALSE)
+
+#ifdef __cplusplus
+}
+#endif /* def __cplusplus */
+
+#endif /* PAM_CLIENT_H */
diff --git a/libpamc/libpamc.h b/libpamc/libpamc.h
new file mode 100644
index 0000000..cdc7724
--- /dev/null
+++ b/libpamc/libpamc.h
@@ -0,0 +1,66 @@
+/*
+ * $Id$
+ *
+ * Copyright (c) Andrew G. Morgan <morgan@ftp.kernel.org>
+ *
+ */
+
+#ifndef LIBPAMC_H
+#define LIBPAMC_H
+
+#include "config.h"
+
+#include <security/pam_client.h>
+#include <security/_pam_macros.h>
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/wait.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+
+#define _PAMC_DEFAULT_TOP_FD 10
+
+struct pamc_handle_s {
+ struct pamc_agent_s *current;
+ struct pamc_agent_s *chain;
+ struct pamc_blocked_s *blocked_agents;
+ int max_path;
+ char **agent_paths;
+ int combined_status;
+ int highest_fd_to_close;
+};
+
+typedef struct pamc_blocked_s {
+ char *id; /* <NUL> terminated */
+ struct pamc_blocked_s *next;
+} pamc_blocked_t;
+
+typedef struct pamc_agent_s {
+ char *id;
+ int id_length;
+ struct pamc_agent_s *next;
+ int writer; /* write to agent */
+ int reader; /* read from agent */
+ pid_t pid; /* agent process id */
+} pamc_agent_t;
+
+/* used to build a tree of unique, sorted agent ids */
+
+typedef struct pamc_id_node {
+ struct pamc_id_node *left, *right;
+ int child_count;
+ char *agent_id;
+} pamc_id_node_t;
+
+/* internal function */
+int __pamc_valid_agent_id(int id_length, const char *id);
+
+#define PAMC_SYSTEM_AGENT_PATH "/lib/pamc:/usr/lib/pamc"
+#define PAMC_SYSTEM_AGENT_SEPARATOR ':'
+
+#endif /* LIBPAMC_H */
diff --git a/libpamc/libpamc.map b/libpamc/libpamc.map
new file mode 100644
index 0000000..cc61a33
--- /dev/null
+++ b/libpamc/libpamc.map
@@ -0,0 +1,12 @@
+LIBPAMC_1.0 {
+ global:
+ pamc_start;
+ pamc_end;
+ pamc_load;
+ pamc_converse;
+ pamc_disable;
+ pamc_list_agents;
+
+ local:
+ *;
+};
diff --git a/libpamc/pamc.pc.in b/libpamc/pamc.pc.in
new file mode 100644
index 0000000..25a6385
--- /dev/null
+++ b/libpamc/pamc.pc.in
@@ -0,0 +1,9 @@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libpamc
+URL: http://www.linux-pam.org/
+Description: The PAM client API library and binary prompt support. Rarely used.
+Version: @VERSION@
+Cflags: -I${includedir}
+Libs: -L${libdir} -lpamc
diff --git a/libpamc/pamc_client.c b/libpamc/pamc_client.c
new file mode 100644
index 0000000..175f424
--- /dev/null
+++ b/libpamc/pamc_client.c
@@ -0,0 +1,189 @@
+/*
+ * $Id$
+ *
+ * Copyright (c) Andrew G. Morgan <morgan@ftp.kernel.org>
+ *
+ * pamc_start and pamc_end
+ */
+
+#include "libpamc.h"
+
+/*
+ * liberate path list
+ */
+
+static void __pamc_delete_path_list(pamc_handle_t pch)
+{
+ int i;
+
+ for (i=0; pch->agent_paths[i]; ++i) {
+ free(pch->agent_paths[i]);
+ pch->agent_paths[i] = NULL;
+ }
+
+ free(pch->agent_paths);
+ pch->agent_paths = NULL;
+}
+
+/*
+ * open the pamc library
+ */
+
+pamc_handle_t pamc_start(void)
+{
+ int i, count, last, this;
+ const char *default_path;
+ pamc_handle_t pch;
+
+ pch = calloc(1, sizeof(struct pamc_handle_s));
+ if (pch == NULL) {
+ D(("no memory for *pch"));
+ return NULL;
+ }
+
+ pch->highest_fd_to_close = _PAMC_DEFAULT_TOP_FD;
+
+ default_path = getenv("PAMC_AGENT_PATH");
+ if (default_path == NULL) {
+ default_path = PAMC_SYSTEM_AGENT_PATH;
+ }
+
+ /* number of individual paths */
+ for (count=1, i=0; default_path[i]; ++i) {
+ if (default_path[i] == PAMC_SYSTEM_AGENT_SEPARATOR) {
+ ++count;
+ }
+ }
+
+ pch->agent_paths = calloc(count+1, sizeof(char *));
+ if (pch->agent_paths == NULL) {
+ D(("no memory for path list"));
+ goto drop_pch;
+ }
+
+ this = last = i = 0;
+ while ( default_path[i] || (i != last) ) {
+ if ( default_path[i] == PAMC_SYSTEM_AGENT_SEPARATOR
+ || !default_path[i] ) {
+ int length;
+
+ pch->agent_paths[this] = malloc(length = 1+i-last);
+
+ if (pch->agent_paths[this] == NULL) {
+ D(("no memory for next path"));
+ goto drop_list;
+ }
+
+ memcpy(pch->agent_paths[this], default_path + last, i-last);
+ pch->agent_paths[this][i-last] = '\0';
+ if (length > pch->max_path) {
+ pch->max_path = length;
+ }
+
+ if (++this == count) {
+ break;
+ }
+
+ last = ++i;
+ } else {
+ ++i;
+ }
+ }
+
+ return pch;
+
+drop_list:
+ __pamc_delete_path_list(pch);
+
+drop_pch:
+ free(pch);
+
+ return NULL;
+}
+
+/*
+ * shutdown each of the loaded agents and
+ */
+
+static int __pamc_shutdown_agents(pamc_handle_t pch)
+{
+ int retval = PAM_BPC_TRUE;
+
+ D(("called"));
+
+ while (pch->chain) {
+ pid_t pid;
+ int status;
+ pamc_agent_t *this;
+
+ this = pch->chain;
+ D(("cleaning up agent %p", this));
+ pch->chain = pch->chain->next;
+ this->next = NULL;
+ D(("cleaning up agent: %s", this->id));
+
+ /* close off contact with agent and wait for it to shutdown */
+
+ close(this->writer);
+ this->writer = -1;
+ close(this->reader);
+ this->reader = -1;
+
+ pid = waitpid(this->pid, &status, 0);
+ if (pid == this->pid) {
+
+ D(("is exit:%d, exit val:%d",
+ WIFEXITED(status), WEXITSTATUS(status)));
+
+ if (!(WIFEXITED(status) && (WEXITSTATUS(status) == 0))) {
+ retval = PAM_BPC_FALSE;
+ }
+ } else {
+ D(("problem shutting down agent (%s): pid(%d) != waitpid(%d)!?",
+ this->id, this->pid, pid));
+ retval = PAM_BPC_FALSE;
+ }
+ pid = this->pid = 0;
+
+ memset(this->id, 0, this->id_length);
+ free(this->id);
+ this->id = NULL;
+ this->id_length = 0;
+
+ free(this);
+ this = NULL;
+ }
+
+ return retval;
+}
+
+/*
+ * close the pamc library
+ */
+
+int pamc_end(pamc_handle_t *pch_p)
+{
+ int retval;
+
+ if (pch_p == NULL) {
+ D(("called with no pch_p"));
+ return PAM_BPC_FALSE;
+ }
+
+ if (*pch_p == NULL) {
+ D(("called with no *pch_p"));
+ return PAM_BPC_FALSE;
+ }
+
+ D(("removing path_list"));
+ __pamc_delete_path_list(*pch_p);
+
+ D(("shutting down agents"));
+ retval = __pamc_shutdown_agents(*pch_p);
+
+ D(("freeing *pch_p"));
+ free(*pch_p);
+ *pch_p = NULL;
+
+ return retval;
+}
diff --git a/libpamc/pamc_converse.c b/libpamc/pamc_converse.c
new file mode 100644
index 0000000..f8f60ed
--- /dev/null
+++ b/libpamc/pamc_converse.c
@@ -0,0 +1,211 @@
+/*
+ * $Id$
+ *
+ * Copyright (c) Andrew G. Morgan <morgan@ftp.kernel.org>
+ *
+ * pamc_converse
+ */
+
+#include "libpamc.h"
+
+/*
+ * select agent
+ */
+
+static int __pamc_select_agent(pamc_handle_t pch, char *agent_id)
+{
+ pamc_agent_t *agent;
+
+ for (agent = pch->chain; agent; agent = agent->next) {
+ if (!strcmp(agent->id, agent_id)) {
+ pch->current = agent;
+ return PAM_BPC_TRUE;
+ }
+ }
+
+ D(("failed to locate agent"));
+ pch->current = NULL;
+ return PAM_BPC_FALSE;
+}
+
+/*
+ * pass a binary prompt to the active agent and wait for a reply prompt
+ */
+
+int pamc_converse(pamc_handle_t pch, pamc_bp_t *prompt_p)
+{
+ uint32_t size, offset=0;
+ uint8_t control, raw[PAM_BP_MIN_SIZE];
+
+ D(("called"));
+
+ if (pch == NULL) {
+ D(("null pch"));
+ goto pamc_converse_failure;
+ }
+
+ if (prompt_p == NULL) {
+ D(("null prompt_p"));
+ goto pamc_converse_failure;
+ }
+
+ if (*prompt_p == NULL) {
+ D(("null *prompt_p"));
+ goto pamc_converse_failure;
+ }
+
+ /* from here on, failures are interoperability problems.. */
+
+ size = PAM_BP_SIZE(*prompt_p);
+ if (size < PAM_BP_MIN_SIZE) {
+ D(("problem with size being too short (%u)", size));
+ goto pamc_unknown_prompt;
+ }
+
+ if (PAM_BPC_FOR_CLIENT(*prompt_p) != PAM_BPC_TRUE) {
+ D(("*prompt_p is not legal for the client to use"));
+ goto pamc_unknown_prompt;
+ }
+
+ /* do we need to select the agent? */
+ if ((*prompt_p)->control == PAM_BPC_SELECT) {
+ char *rawh;
+ size_t i;
+ int retval;
+
+ D(("selecting a specified agent"));
+
+ rawh = (char *) *prompt_p;
+ for (i = PAM_BP_MIN_SIZE; i<size; ++i) {
+ if (rawh[i] == '/') {
+ break;
+ }
+ }
+
+ if ( (i >= size)
+ || !__pamc_valid_agent_id(i-PAM_BP_MIN_SIZE,
+ rawh + PAM_BP_MIN_SIZE) ) {
+ goto pamc_unknown_prompt;
+ }
+
+ rawh[i] = '\0';
+ retval = pamc_load(pch, PAM_BP_MIN_SIZE + rawh);
+ if (retval == PAM_BPC_TRUE) {
+ retval = __pamc_select_agent(pch, PAM_BP_MIN_SIZE + rawh);
+ }
+ rawh[i] = '/';
+
+ if (retval != PAM_BPC_TRUE) {
+ goto pamc_unknown_prompt;
+ }
+
+ D(("agent is loaded"));
+ }
+
+ if (pch->current == NULL) {
+ D(("unable to address agent"));
+ goto pamc_unknown_prompt;
+ }
+
+ /* pump all of the prompt into the agent */
+ do {
+ int rval = write(pch->current->writer,
+ offset + (const uint8_t *) (*prompt_p),
+ size - offset);
+ if (rval == -1) {
+ switch (errno) {
+ case EINTR:
+ break;
+ default:
+ D(("problem writing to agent: %m"));
+ goto pamc_unknown_prompt;
+ }
+ } else {
+ offset += rval;
+ }
+ } while (offset < size);
+
+ D(("whole prompt sent to agent"));
+
+ /* read size and control for response prompt */
+
+ offset = 0;
+ memset(raw, 0, sizeof(raw));
+ do {
+ int rval;
+
+ rval = read(pch->current->reader, raw + offset,
+ PAM_BP_MIN_SIZE - offset);
+
+ if (rval == -1) {
+ switch (errno) {
+ case EINTR:
+ break;
+ default:
+ D(("problem reading from agent: %m"));
+ goto pamc_unknown_prompt;
+ }
+ } else if (rval) {
+ offset += rval;
+ } else {
+ D(("agent has closed its output pipe - nothing more to read"));
+ goto pamc_converse_failure;
+ }
+ } while (offset < PAM_BP_MIN_SIZE);
+
+ /* construct the whole reply prompt */
+
+ size = PAM_BP_SIZE(raw);
+ control = PAM_BP_RCONTROL(raw);
+ memset(raw, 0, sizeof(raw));
+
+ D(("agent replied with prompt of size %d and control %u",
+ size, control));
+
+ PAM_BP_RENEW(prompt_p, control, size - PAM_BP_MIN_SIZE);
+ if (*prompt_p == NULL) {
+ D(("problem making a new prompt for reply"));
+ goto pamc_unknown_prompt;
+ }
+
+ /* read the rest of the reply prompt -- note offset has the correct
+ value from the previous loop */
+
+ while (offset < size) {
+ int rval = read(pch->current->reader, offset + (uint8_t *) *prompt_p,
+ size-offset);
+
+ if (rval == -1) {
+ switch (errno) {
+ case EINTR:
+ break;
+ default:
+ D(("problem reading from agent: %m"));
+ goto pamc_unknown_prompt;
+ }
+ } else if (rval) {
+ offset += rval;
+ } else {
+ D(("problem reading prompt (%d) with %d to go",
+ size, size-offset));
+ goto pamc_converse_failure;
+ }
+ }
+
+ D(("returning success"));
+
+ return PAM_BPC_TRUE;
+
+pamc_converse_failure:
+
+ D(("conversation failure"));
+ PAM_BP_RENEW(prompt_p, 0, 0);
+ return PAM_BPC_FALSE;
+
+pamc_unknown_prompt:
+
+ /* the server is trying something that the client does not support */
+ D(("unknown prompt"));
+ PAM_BP_RENEW(prompt_p, PAM_BPC_FAIL, 0);
+ return PAM_BPC_TRUE;
+}
diff --git a/libpamc/pamc_load.c b/libpamc/pamc_load.c
new file mode 100644
index 0000000..24a65df
--- /dev/null
+++ b/libpamc/pamc_load.c
@@ -0,0 +1,477 @@
+/*
+ * $Id$
+ *
+ * Copyright (c) 1999 Andrew G. Morgan <morgan@ftp.kernel.org>
+ *
+ * pamc_load
+ */
+
+#include "libpamc.h"
+
+static int __pamc_exec_agent(pamc_handle_t pch, pamc_agent_t *agent)
+{
+ char *full_path;
+ int found_agent, length, reset_length, to_agent[2], from_agent[2];
+ int return_code = PAM_BPC_FAIL;
+
+ if (agent->id[agent->id_length] != '\0') {
+ PAM_BP_ASSERT("libpamc: internal error agent_id not terminated");
+ }
+
+ for (length=0; (length < agent->id_length); ++length) {
+ switch (agent->id[length]) {
+ case '/':
+ D(("ill formed agent id"));
+ return PAM_BPC_FAIL;
+ }
+ }
+
+ /* enough memory for any path + this agent */
+ reset_length = 3 + pch->max_path + agent->id_length;
+ D(("reset_length = %d (3+%d+%d)",
+ reset_length, pch->max_path, agent->id_length));
+ full_path = malloc(reset_length);
+ if (full_path == NULL) {
+ D(("no memory for agent path"));
+ return PAM_BPC_FAIL;
+ }
+
+ found_agent = 0;
+ for (length=0; pch->agent_paths[length]; ++length) {
+ struct stat buf;
+
+ D(("path: [%s]", pch->agent_paths[length]));
+ D(("agent id: [%s]", agent->id));
+
+ sprintf(full_path, "%s/%s", pch->agent_paths[length], agent->id);
+
+ D(("looking for agent here: [%s]\n", full_path));
+ if (stat(full_path, &buf) == 0) {
+ D(("file existis"));
+ found_agent = 1;
+ break;
+ }
+ }
+
+ if (! found_agent) {
+ D(("no agent was found"));
+ goto free_and_return;
+ }
+
+ if (pipe(to_agent)) {
+ D(("failed to open pipe to agent"));
+ goto free_and_return;
+ }
+
+ if (pipe(from_agent)) {
+ D(("failed to open pipe from agent"));
+ goto close_the_agent;
+ }
+
+ agent->pid = fork();
+ if (agent->pid == -1) {
+
+ D(("failed to fork for agent"));
+ goto close_both_pipes;
+
+ } else if (agent->pid == 0) {
+
+ int i;
+
+ dup2(from_agent[1], STDOUT_FILENO);
+ dup2(to_agent[0], STDIN_FILENO);
+
+ /* we close all of the files that have filedescriptors lower
+ and equal to twice the highest we have seen, The idea is
+ that we don't want to leak filedescriptors to agents from a
+ privileged client application.
+
+ XXX - this is a heuristic at this point. There is a growing
+ need for an extra 'set param' libpamc function, that could
+ be used to supply info like the highest fd to close etc..
+ */
+
+ if (from_agent[1] > pch->highest_fd_to_close) {
+ pch->highest_fd_to_close = 2*from_agent[1];
+ }
+
+ for (i=0; i <= pch->highest_fd_to_close; ++i) {
+ switch (i) {
+ case STDOUT_FILENO:
+ case STDERR_FILENO:
+ case STDIN_FILENO:
+ /* only these three remain open */
+ break;
+ default:
+ (void) close(i); /* don't care if its not open */
+ }
+ }
+
+ /* we make no attempt to drop other privileges - this library
+ has no idea how that would be done in the general case. It
+ is up to the client application (when calling
+ pamc_converse) to make sure no privilege will leak into an
+ (untrusted) agent. */
+
+ /* we propagate no environment - future versions of this
+ library may have the ability to audit all agent
+ transactions. */
+
+ D(("exec'ing agent %s", full_path));
+ execle(full_path, "pam-agent", NULL, NULL);
+
+ D(("exec failed"));
+ _exit(1);
+
+ }
+
+ close(to_agent[0]);
+ close(from_agent[1]);
+
+ agent->writer = to_agent[1];
+ agent->reader = from_agent[0];
+
+ return_code = PAM_BPC_TRUE;
+ goto free_and_return;
+
+close_both_pipes:
+ close(from_agent[0]);
+ close(from_agent[1]);
+
+close_the_agent:
+ close(to_agent[0]);
+ close(to_agent[1]);
+
+free_and_return:
+ memset(full_path, 0, reset_length);
+ free(full_path);
+
+ D(("returning %d", return_code));
+
+ return return_code;
+}
+
+/*
+ * has the named agent been loaded?
+ */
+
+static int __pamc_agent_is_enabled(pamc_handle_t pch, const char *agent_id)
+{
+ pamc_agent_t *agent;
+
+ for (agent = pch->chain; agent; agent = agent->next) {
+ if (!strcmp(agent->id, agent_id)) {
+ D(("agent already loaded"));
+ return PAM_BPC_TRUE;
+ }
+ }
+
+ D(("agent is not loaded"));
+ return PAM_BPC_FALSE;
+}
+
+/*
+ * has the named agent been disabled?
+ */
+
+static int __pamc_agent_is_disabled(pamc_handle_t pch, const char *agent_id)
+{
+ pamc_blocked_t *blocked;
+
+ for (blocked=pch->blocked_agents; blocked; blocked = blocked->next) {
+ if (!strcmp(agent_id, blocked->id)) {
+ D(("agent is disabled"));
+ return PAM_BPC_TRUE;
+ }
+ }
+
+ D(("agent is not disabled"));
+ return PAM_BPC_FALSE;
+}
+
+/*
+ * disable an agent
+ */
+
+int pamc_disable(pamc_handle_t pch, const char *agent_id)
+{
+ pamc_blocked_t *block;
+
+ if (pch == NULL) {
+ D(("pch is NULL"));
+ return PAM_BPC_FALSE;
+ }
+
+ if (agent_id == NULL) {
+ D(("agent_id is NULL"));
+ return PAM_BPC_FALSE;
+ }
+
+ if (__pamc_agent_is_enabled(pch, agent_id) != PAM_BPC_FALSE) {
+ D(("agent is already loaded"));
+ return PAM_BPC_FALSE;
+ }
+
+ if (__pamc_agent_is_disabled(pch, agent_id) != PAM_BPC_FALSE) {
+ D(("agent is already disabled"));
+ return PAM_BPC_TRUE;
+ }
+
+ block = calloc(1, sizeof(pamc_blocked_t));
+ if (block == NULL) {
+ D(("no memory for new blocking structure"));
+ return PAM_BPC_FALSE;
+ }
+
+ block->id = malloc(1 + strlen(agent_id));
+ if (block->id == NULL) {
+ D(("no memory for agent id"));
+ free(block);
+ return PAM_BPC_FALSE;
+ }
+
+ strcpy(block->id, agent_id);
+ block->next = pch->blocked_agents;
+ pch->blocked_agents = block;
+
+ return PAM_BPC_TRUE;
+}
+
+/*
+ * force the loading of a particular agent
+ */
+
+int pamc_load(pamc_handle_t pch, const char *agent_id)
+{
+ pamc_agent_t *agent;
+ int length;
+
+ /* santity checking */
+
+ if (pch == NULL) {
+ D(("pch is NULL"));
+ return PAM_BPC_FALSE;
+ }
+
+ if (agent_id == NULL) {
+ D(("agent_id is NULL"));
+ return PAM_BPC_FALSE;
+ }
+
+ if (__pamc_agent_is_disabled(pch, agent_id) != PAM_BPC_FALSE) {
+ D(("sorry agent is disabled"));
+ return PAM_BPC_FALSE;
+ }
+
+ length = strlen(agent_id);
+
+ /* scan list to see if agent is loaded */
+
+ if (__pamc_agent_is_enabled(pch, agent_id) == PAM_BPC_TRUE) {
+ D(("no need to load an already loaded agent (%s)", agent_id));
+ return PAM_BPC_TRUE;
+ }
+
+ /* not in the list, so we need to load it and add it to the head
+ of the chain */
+
+ agent = calloc(1, sizeof(pamc_agent_t));
+ if (agent == NULL) {
+ D(("no memory for new agent"));
+ return PAM_BPC_FALSE;
+ }
+ agent->id = calloc(1, 1+length);
+ if (agent->id == NULL) {
+ D(("no memory for new agent's id"));
+ goto fail_free_agent;
+ }
+ memcpy(agent->id, agent_id, length);
+ agent->id[length] = '\0';
+ agent->id_length = length;
+
+ if (__pamc_exec_agent(pch, agent) != PAM_BPC_TRUE) {
+ D(("unable to exec agent"));
+ goto fail_free_agent_id;
+ }
+
+ agent->next = pch->chain;
+ pch->chain = agent;
+
+ return PAM_BPC_TRUE;
+
+fail_free_agent_id:
+
+ memset(agent->id, 0, agent->id_length);
+ free(agent->id);
+
+ memset(agent, 0, sizeof(*agent));
+
+fail_free_agent:
+
+ free(agent);
+ return PAM_BPC_FALSE;
+}
+
+/*
+ * what's a valid agent name?
+ */
+
+int __pamc_valid_agent_id(int id_length, const char *id)
+{
+ int post, i;
+
+ for (i=post=0 ; i < id_length; ++i) {
+ int ch = id[i++];
+
+ if (isalpha(ch) || isdigit(ch) || (ch == '_')) {
+ continue;
+ } else if (post && (ch == '.')) {
+ continue;
+ } else if ((i > 1) && (!post) && (ch == '@')) {
+ post = 1;
+ } else {
+ D(("id=%s contains '%c' which is illegal", id, ch));
+ return 0;
+ }
+ }
+
+ if (!i) {
+ D(("length of id is 0"));
+ return 0;
+ } else {
+ return 1; /* id is valid */
+ }
+}
+
+/*
+ * building a tree of available agent names
+ */
+
+static pamc_id_node_t *__pamc_add_node(pamc_id_node_t *root, const char *id,
+ int *counter)
+{
+ if (root) {
+
+ int cmp;
+
+ if ((cmp = strcmp(id, root->agent_id))) {
+ if (cmp > 0) {
+ root->right = __pamc_add_node(root->right, id,
+ &(root->child_count));
+ } else {
+ root->left = __pamc_add_node(root->left, id,
+ &(root->child_count));
+ }
+ }
+
+ return root;
+
+ } else {
+
+ pamc_id_node_t *node = calloc(1, sizeof(pamc_id_node_t));
+
+ if (node) {
+ node->agent_id = malloc(1+strlen(id));
+ if (node->agent_id) {
+ strcpy(node->agent_id, id);
+ } else {
+ free(node);
+ node = NULL;
+ }
+ }
+
+ (*counter)++;
+ return node;
+ }
+}
+
+/*
+ * drop all of the tree and any remaining ids
+ */
+
+static pamc_id_node_t *__pamc_liberate_nodes(pamc_id_node_t *tree)
+{
+ if (tree) {
+ if (tree->agent_id) {
+ free(tree->agent_id);
+ tree->agent_id = NULL;
+ }
+
+ tree->left = __pamc_liberate_nodes(tree->left);
+ tree->right = __pamc_liberate_nodes(tree->right);
+
+ tree->child_count = 0;
+ free(tree);
+ }
+
+ return NULL;
+}
+
+/*
+ * fill a list with the contents of the tree (in ascii order)
+ */
+
+static void __pamc_fill_list_from_tree(pamc_id_node_t *tree, char **agent_list,
+ int *counter)
+{
+ if (tree) {
+ __pamc_fill_list_from_tree(tree->left, agent_list, counter);
+ agent_list[(*counter)++] = tree->agent_id;
+ tree->agent_id = NULL;
+ __pamc_fill_list_from_tree(tree->right, agent_list, counter);
+ }
+}
+
+/*
+ * get a list of the available agents
+ */
+
+char **pamc_list_agents(pamc_handle_t pch)
+{
+ int i, total_agent_count=0;
+ pamc_id_node_t *tree = NULL;
+ char **agent_list;
+
+ /* loop over agent paths */
+
+ for (i=0; pch->agent_paths[i]; ++i) {
+ DIR *dir;
+
+ dir = opendir(pch->agent_paths[i]);
+ if (dir) {
+ struct dirent *item;
+
+ while ((item = readdir(dir))) {
+
+ /* this is a cheat on recognizing agent_ids */
+ if (!__pamc_valid_agent_id(strlen(item->d_name),
+ item->d_name)) {
+ continue;
+ }
+
+ tree = __pamc_add_node(tree, item->d_name, &total_agent_count);
+ }
+
+ closedir(dir);
+ }
+ }
+
+ /* now, we build a list of ids */
+ D(("total of %d available agents\n", total_agent_count));
+
+ agent_list = calloc(total_agent_count+1, sizeof(char *));
+ if (agent_list) {
+ int counter=0;
+
+ __pamc_fill_list_from_tree(tree, agent_list, &counter);
+ if (counter != total_agent_count) {
+ PAM_BP_ASSERT("libpamc: internal error transcribing tree");
+ }
+ } else {
+ D(("no memory for agent list"));
+ }
+
+ __pamc_liberate_nodes(tree);
+
+ return agent_list;
+}
diff --git a/libpamc/test/Makefile.am b/libpamc/test/Makefile.am
new file mode 100644
index 0000000..fc0cac4
--- /dev/null
+++ b/libpamc/test/Makefile.am
@@ -0,0 +1,11 @@
+#
+# Copyright (c) 2005 Thorsten Kukuk <kukuk@suse.de>
+#
+
+AUTOMAKE_OPTIONS = 1.6 gnits
+
+CLEANFILES = *~ */*~
+
+EXTRA_DIST = agents/secret@here modules/Makefile modules/pam_secret.c \
+ regress/Makefile regress/run_test.sh regress/test.libpamc.c \
+ regress/test.secret@here
diff --git a/libpamc/test/Makefile.in b/libpamc/test/Makefile.in
new file mode 100644
index 0000000..e2f360b
--- /dev/null
+++ b/libpamc/test/Makefile.in
@@ -0,0 +1,526 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+#
+# Copyright (c) 2005 Thorsten Kukuk <kukuk@suse.de>
+#
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = libpamc/test
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/attribute.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/intlmacosx.m4 \
+ $(top_srcdir)/m4/jh_path_xml_catalog.m4 \
+ $(top_srcdir)/m4/ld-O1.m4 $(top_srcdir)/m4/ld-as-needed.m4 \
+ $(top_srcdir)/m4/ld-no-undefined.m4 \
+ $(top_srcdir)/m4/ld-z-now.m4 $(top_srcdir)/m4/lib-ld.m4 \
+ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+ $(top_srcdir)/m4/libprelude.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 \
+ $(top_srcdir)/m4/warn_lang_flags.m4 \
+ $(top_srcdir)/m4/warnings.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BROWSER = @BROWSER@
+BUILD_CFLAGS = @BUILD_CFLAGS@
+BUILD_CPPFLAGS = @BUILD_CPPFLAGS@
+BUILD_LDFLAGS = @BUILD_LDFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRYPTO_LIBS = @CRYPTO_LIBS@
+CRYPT_CFLAGS = @CRYPT_CFLAGS@
+CRYPT_LIBS = @CRYPT_LIBS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+ECONF_CFLAGS = @ECONF_CFLAGS@
+ECONF_LIBS = @ECONF_LIBS@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXE_CFLAGS = @EXE_CFLAGS@
+EXE_LDFLAGS = @EXE_LDFLAGS@
+FGREP = @FGREP@
+FO2PDF = @FO2PDF@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBAUDIT = @LIBAUDIT@
+LIBCRYPT = @LIBCRYPT@
+LIBDB = @LIBDB@
+LIBDL = @LIBDL@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBPRELUDE_CFLAGS = @LIBPRELUDE_CFLAGS@
+LIBPRELUDE_CONFIG = @LIBPRELUDE_CONFIG@
+LIBPRELUDE_CONFIG_PREFIX = @LIBPRELUDE_CONFIG_PREFIX@
+LIBPRELUDE_LDFLAGS = @LIBPRELUDE_LDFLAGS@
+LIBPRELUDE_LIBS = @LIBPRELUDE_LIBS@
+LIBPRELUDE_PREFIX = @LIBPRELUDE_PREFIX@
+LIBPRELUDE_PTHREAD_CFLAGS = @LIBPRELUDE_PTHREAD_CFLAGS@
+LIBS = @LIBS@
+LIBSELINUX = @LIBSELINUX@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NIS_CFLAGS = @NIS_CFLAGS@
+NIS_LIBS = @NIS_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NSL_CFLAGS = @NSL_CFLAGS@
+NSL_LIBS = @NSL_LIBS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POSUB = @POSUB@
+RANLIB = @RANLIB@
+SCONFIGDIR = @SCONFIGDIR@
+SECUREDIR = @SECUREDIR@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRINGPARAM_HMAC = @STRINGPARAM_HMAC@
+STRINGPARAM_VENDORDIR = @STRINGPARAM_VENDORDIR@
+STRIP = @STRIP@
+TIRPC_CFLAGS = @TIRPC_CFLAGS@
+TIRPC_LIBS = @TIRPC_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WARN_CFLAGS = @WARN_CFLAGS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+XMLCATALOG = @XMLCATALOG@
+XMLLINT = @XMLLINT@
+XML_CATALOG_FILE = @XML_CATALOG_FILE@
+XSLTPROC = @XSLTPROC@
+YACC = @YACC@
+YFLAGS = @YFLAGS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pam_xauth_path = @pam_xauth_path@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+systemdunitdir = @systemdunitdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AUTOMAKE_OPTIONS = 1.6 gnits
+CLEANFILES = *~ */*~
+EXTRA_DIST = agents/secret@here modules/Makefile modules/pam_secret.c \
+ regress/Makefile regress/run_test.sh regress/test.libpamc.c \
+ regress/test.secret@here
+
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnits libpamc/test/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnits libpamc/test/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libpamc/test/agents/secret@here b/libpamc/test/agents/secret@here
new file mode 100755
index 0000000..8d82c01
--- /dev/null
+++ b/libpamc/test/agents/secret@here
@@ -0,0 +1,307 @@
+#!/usr/bin/perl
+#
+# This is a simple example PAM authentication agent, it implements a
+# simple shared secret authentication scheme. The PAM module pam_secret.so
+# is its counter part. Both the agent and the remote server are able to
+# authenticate one another, but the server is given the opportunity to
+# ignore a failed authentication.
+#
+
+$^W = 1;
+use strict;
+use IPC::Open2;
+$| = 1;
+
+# display extra information to STDERR
+my $debug = 0;
+if (scalar @ARGV) {
+ $debug = 1;
+}
+
+# Globals
+
+my %state;
+my $default_key;
+
+my $next_key = $$;
+
+# loop over binary prompts
+for (;;) {
+ my ($control, $data) = ReadBinaryPrompt();
+ my ($reply_control, $reply_data);
+
+ if ($control == 0) {
+ if ($debug) {
+ print STDERR "agent: no packet to read\n";
+ }
+ last;
+ } elsif ($control == 0x02) {
+ ($reply_control, $reply_data) = HandleAgentSelection($data);
+ } elsif ($control == 0x01) {
+ ($reply_control, $reply_data) = HandleContinuation($data);
+ } else {
+ if ($debug) {
+ print STDERR
+ "agent: unrecognized packet $control {$data} to read\n";
+ }
+ ($reply_control, $reply_data) = (0x04, "");
+ }
+
+ WriteBinaryPrompt($reply_control, $reply_data);
+}
+
+# Only willing to exit well if we've completed our authentication exchange
+
+if (scalar keys %state) {
+ if ($debug) {
+ print STDERR "The following sessions are still active:\n ";
+ print STDERR join ', ', keys %state;
+ print STDERR "\n";
+ }
+ exit 1;
+} else {
+ exit 0;
+}
+
+sub HandleAgentSelection ($) {
+ my ($data) = @_;
+
+ unless ( $data =~ /^([a-zA-Z0-9_]+\@?[a-zA-Z0-9_.]*)\/(.*)$/ ) {
+ return (0x04, "");
+ }
+
+ my ($agent_name, $payload) = ($1, $2);
+ if ($debug) {
+ print STDERR "agent: ". "agent=$agent_name, payload=$payload\n";
+ }
+
+ # this agent has a defined name
+ if ($agent_name ne "secret\@here") {
+ if ($debug) {
+ print STDERR "bad agent name: [$agent_name]\n";
+ }
+ return (0x04, "");
+ }
+
+ # the selection request is acompanied with a hexadecimal cookie
+ my @tokens = split '\|', $payload;
+
+ unless ((scalar @tokens) == 2) {
+ if ($debug) {
+ print STDERR "bad payload\n";
+ }
+ return (0x04, "");
+ }
+
+ unless ($tokens[1] =~ /^[a-z0-9]+$/) {
+ if ($debug) {
+ print STDERR "bad server cookie\n";
+ }
+ return (0x04, "");
+ }
+
+ my $shared_secret = IdentifyLocalSecret($tokens[0]);
+
+ unless (defined $shared_secret) {
+ # make a secret up
+ if ($debug) {
+ print STDERR "agent: cannot authenticate user\n";
+ }
+ $shared_secret = GetRandom();
+ }
+
+ my $local_cookie = GetRandom();
+ $default_key = $next_key++;
+
+ $state{$default_key} = $local_cookie ."|". $tokens[1] ."|". $shared_secret;
+
+ if ($debug) {
+ print STDERR "agent: \$state{$default_key} = $state{$default_key}\n";
+ }
+
+ return (0x01, $default_key ."|". $local_cookie);
+}
+
+sub HandleContinuation ($) {
+ my ($data) = @_;
+
+ my ($key, $server_digest) = split '\|', $data;
+
+ unless (defined $state{$key}) {
+ # retries and out of sequence prompts are not permitted
+ return (0x04, "");
+ }
+
+ my $expected_digest = CreateDigest($state{$key});
+ my ($local_cookie, $remote_cookie, $shared_secret)
+ = split '\|', $state{$key};
+ delete $state{$key};
+
+ unless ($expected_digest eq $server_digest) {
+ if ($debug) {
+ print STDERR "agent: don't trust server - faking reply\n";
+ print STDERR "agent: got ($server_digest)\n";
+ print STDERR "agent: expected ($expected_digest)\n";
+ }
+
+ ## FIXME: Agent should exchange a prompt with the client warning
+ ## that the server is faking us out.
+
+ return (0x03, CreateDigest($expected_digest . $data . GetRandom()));
+ }
+
+ if ($debug) {
+ print STDERR "agent: server appears to know the secret\n";
+ }
+
+ my $session_authenticated_ticket =
+ CreateDigest($remote_cookie."|".$shared_secret."|".$local_cookie);
+
+ # FIXME: Agent should set a derived session key environment
+ # variable (available for the client (and its children) to sign
+ # future data exchanges.
+
+ if ($debug) {
+ print STDERR "agent: should putenv("
+ ."\"AUTH_SESSION_TICKET=$session_authenticated_ticket\")\n";
+ }
+
+ # return agent's authenticating digest
+ return (0x03, CreateDigest($shared_secret."|".$remote_cookie
+ ."|".$local_cookie));
+}
+
+sub ReadBinaryPrompt {
+ my $buffer = " ";
+ my $count = read(STDIN, $buffer, 5);
+ if ($count == 0) {
+ # no more packets to read
+ return (0, "");
+ }
+
+ if ($count != 5) {
+ # broken packet header
+ return (-1, "");
+ }
+
+ my ($length, $control) = unpack("N C", $buffer);
+ if ($length < 5) {
+ # broken packet length
+ return (-1, "");
+ }
+
+ my $data = "";
+ $length -= 5;
+ while ($count = read(STDIN, $buffer, $length)) {
+ $data .= $buffer;
+ if ($count != $length) {
+ $length -= $count;
+ next;
+ }
+
+ if ($debug) {
+ print STDERR "agent: ". "data is [$data]\n";
+ }
+
+ return ($control, $data);
+ }
+
+ # broken packet data
+ return (-1, "");
+}
+
+sub WriteBinaryPrompt ($$) {
+ my ($control, $data) = @_;
+
+ my $length = 5 + length($data);
+ if ($debug) {
+ printf STDERR "agent: ". "{%d|0x%.2x|%s}\n", $length, $control, $data;
+ }
+ my $bp = pack("N C a*", $length, $control, $data);
+ print STDOUT $bp;
+ if ($debug) {
+ printf STDERR "agent: ". "agent has replied\n";
+ }
+}
+
+##
+## Here is where we parse the simple secret file
+## The format of this file is a list of lines of the following form:
+##
+## user@client0.host.name secret_string1
+## user@client1.host.name secret_string2
+## user@client2.host.name secret_string3
+##
+
+sub IdentifyLocalSecret ($) {
+ my ($identifier) = @_;
+ my $secret;
+
+ if (open SECRETS, "< ". (getpwuid($<))[7] ."/.secret\@here") {
+ my $line;
+ while (defined ($line = <SECRETS>)) {
+ my ($id, $sec) = split /[\s]+/, $line;
+ if ((defined $id) && ($id eq $identifier)) {
+ $secret = $sec;
+ last;
+ }
+ }
+ close SECRETS;
+ }
+
+ return $secret;
+}
+
+## Here is where we generate a message digest
+
+sub CreateDigest ($) {
+ my ($data) = @_;
+
+ my $pid = open2(\*MD5out, \*MD5in, "/usr/bin/md5sum -")
+ or die "you'll need /usr/bin/md5sum installed";
+
+ my $oldfd = select MD5in; $|=1; select $oldfd;
+ if ($debug) {
+ print STDERR "agent: ". "telling md5: <$data>\n";
+ }
+ print MD5in "$data";
+ close MD5in;
+ my $reply = <MD5out>;
+ ($reply) = split /\s/, $reply;
+ if ($debug) {
+ print STDERR "agent: ". "md5 said: <$reply>\n";
+ }
+ close MD5out;
+
+ return $reply;
+}
+
+## get a random number
+
+sub GetRandom {
+
+ if ( -r "/dev/urandom" ) {
+ open RANDOM, "< /dev/urandom" or die "crazy";
+
+ my $i;
+ my $reply = "";
+
+ for ($i=0; $i<4; ++$i) {
+ my $buffer = " ";
+ while (read(RANDOM, $buffer, 4) != 4) {
+ ;
+ }
+ $reply .= sprintf "%.8x", unpack("N", $buffer);
+ if ($debug) {
+ print STDERR "growing reply: [$reply]\n";
+ }
+ }
+ close RANDOM;
+
+ return $reply;
+ } else {
+ print STDERR "agent: ". "[got linux?]\n";
+ return "%.8x%.8x%.8x%.8x", time, time, time, time;
+ }
+
+}
diff --git a/libpamc/test/modules/Makefile b/libpamc/test/modules/Makefile
new file mode 100644
index 0000000..4806546
--- /dev/null
+++ b/libpamc/test/modules/Makefile
@@ -0,0 +1,9 @@
+CFLAGS = -g -fPIC -I"../../include"
+
+pam_secret.so: pam_secret.o
+ ld -x --shared -o pam_secret.so pam_secret.o -lc
+
+.o.c:
+
+clean:
+ rm -f *.so *.o
diff --git a/libpamc/test/modules/pam_secret.c b/libpamc/test/modules/pam_secret.c
new file mode 100644
index 0000000..f1c74c6
--- /dev/null
+++ b/libpamc/test/modules/pam_secret.c
@@ -0,0 +1,669 @@
+/*
+ * $Id$
+ *
+ * Copyright (c) 1999 Andrew G. Morgan <morgan@linux.kernel.org>
+ */
+
+/*
+ * WARNING: AS WRITTEN THIS CODE IS NOT SECURE. THE MD5 IMPLEMENTATION
+ * NEEDS TO BE INTEGRATED MORE NATIVELY.
+ */
+
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <security/pam_modules.h>
+#include <security/pam_client.h>
+#include <security/_pam_macros.h>
+
+/*
+ * This is a sample module that demonstrates the use of binary prompts
+ * and how they can be used to implement sophisticated authentication
+ * schemes.
+ */
+
+struct ps_state_s {
+ int retval; /* last retval returned by the authentication fn */
+ int state; /* what state the module was in when it
+ returned incomplete */
+
+ char *username; /* the name of the local user */
+
+ char server_cookie[33]; /* storage for 32 bytes of server cookie */
+ char client_cookie[33]; /* storage for 32 bytes of client cookie */
+
+ char *secret_data; /* pointer to <NUL> terminated secret_data */
+ int invalid_secret; /* indication of whether the secret is valid */
+
+ pamc_bp_t current_prompt; /* place to store the current prompt */
+ pamc_bp_t current_reply; /* place to receive the reply prompt */
+};
+
+#define PS_STATE_ID "PAM_SECRET__STATE"
+#define PS_AGENT_ID "secret@here"
+#define PS_STATE_DEAD 0
+#define PS_STATE_INIT 1
+#define PS_STATE_PROMPT1 2
+#define PS_STATE_PROMPT2 3
+
+#define MAX_LEN_HOSTNAME 512
+#define MAX_FILE_LINE_LEN 1024
+
+/*
+ * Routine for generating 16*8 bits of random data represented in ASCII hex
+ */
+
+static int generate_cookie(unsigned char *buffer_33)
+{
+ static const char hexarray[] = "0123456789abcdef";
+ int i, fd;
+
+ /* fill buffer_33 with 32 hex characters (lower case) + '\0' */
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd < 0) {
+ D(("failed to open /dev/urandom"));
+ return 0;
+ }
+ read(fd, buffer_33 + 16, 16);
+ close(fd);
+
+ /* expand top 16 bytes into 32 nibbles */
+ for (i=0; i<16; ++i) {
+ buffer_33[2*i ] = hexarray[(buffer_33[16+i] & 0xf0)>>4];
+ buffer_33[2*i+1] = hexarray[(buffer_33[16+i] & 0x0f)];
+ }
+
+ buffer_33[32] = '\0';
+
+ return 1;
+}
+
+/*
+ * XXX - This is a hack, and is fundamentally insecure. Its subject to
+ * all sorts of attacks not to mention the fact that all our secrets
+ * will be displayed on the command line for someone doing 'ps' to
+ * see. This is just for programming convenience in this instance, it
+ * needs to be replaced with the md5 code. Although I am loath to
+ * add yet another instance of md5 code to the Linux-PAM source code.
+ * [Need to think of a cleaner way to do this for the distribution as
+ * a whole...]
+ */
+
+#define COMMAND_FORMAT "/bin/echo -n '%s|%s|%s'|/usr/bin/md5sum -"
+
+int create_digest(const char *d1, const char *d2, const char *d3,
+ char *buffer_33)
+{
+ int length;
+ char *buffer;
+ FILE *pipe;
+
+ length = strlen(d1)+strlen(d2)+strlen(d3)+sizeof(COMMAND_FORMAT);
+ buffer = malloc(length);
+ if (buffer == NULL) {
+ D(("out of memory"));
+ return 0;
+ }
+
+ sprintf(buffer, COMMAND_FORMAT, d1,d2,d3);
+
+ D(("executing pipe [%s]", buffer));
+ pipe = popen(buffer, "r");
+ memset(buffer, 0, length);
+ free(buffer);
+
+ if (pipe == NULL) {
+ D(("failed to launch pipe"));
+ return 0;
+ }
+
+ if (fgets(buffer_33, 33, pipe) == NULL) {
+ D(("failed to read digest"));
+ return 0;
+ }
+
+ if (strlen(buffer_33) != 32) {
+ D(("digest was not 32 chars"));
+ return 0;
+ }
+
+ fclose(pipe);
+
+ D(("done [%s]", buffer_33));
+
+ return 1;
+}
+
+/*
+ * method to attempt to instruct the application's conversation function
+ */
+
+static int converse(pam_handle_t *pamh, struct ps_state_s *new)
+{
+ int retval;
+ struct pam_conv *conv;
+
+ D(("called"));
+
+ retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
+ if (retval == PAM_SUCCESS) {
+ struct pam_message msg;
+ struct pam_response *single_reply;
+ const struct pam_message *msg_ptr;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_style = PAM_BINARY_PROMPT;
+ msg.msg = (const char *) new->current_prompt;
+ msg_ptr = &msg;
+
+ single_reply = NULL;
+ retval = conv->conv(1, &msg_ptr, &single_reply, conv->appdata_ptr);
+ if (retval == PAM_SUCCESS) {
+ if ((single_reply == NULL) || (single_reply->resp == NULL)) {
+ retval == PAM_CONV_ERR;
+ } else {
+ new->current_reply = (pamc_bp_t) single_reply->resp;
+ single_reply->resp = NULL;
+ }
+ }
+
+ if (single_reply) {
+ free(single_reply);
+ }
+ }
+
+#ifdef PAM_DEBUG
+ if (retval == PAM_SUCCESS) {
+ D(("reply has length=%d and control=%u",
+ PAM_BP_LENGTH(new->current_reply),
+ PAM_BP_CONTROL(new->current_reply)));
+ }
+ D(("returning %s", pam_strerror(pamh, retval)));
+#endif
+
+ return retval;
+}
+
+/*
+ * identify the secret in question
+ */
+
+#define SECRET_FILE_FORMAT "%s/.secret@here"
+
+char *identify_secret(char *identity, const char *user)
+{
+ struct passwd *pwd;
+ char *temp;
+ FILE *secrets;
+ int length_id;
+
+ pwd = getpwnam(user);
+ if ((pwd == NULL) || (pwd->pw_dir == NULL)) {
+ D(("user [%s] is not known", user));
+ return NULL;
+ }
+
+ length_id = strlen(pwd->pw_dir) + sizeof(SECRET_FILE_FORMAT);
+ temp = malloc(length_id);
+ if (temp == NULL) {
+ D(("out of memory"));
+ pwd = NULL;
+ return NULL;
+ }
+
+ sprintf(temp, SECRET_FILE_FORMAT, pwd->pw_dir);
+ pwd = NULL;
+
+ D(("opening key file [%s]", temp));
+ secrets = fopen(temp, "r");
+ memset(temp, 0, length_id);
+
+ if (secrets == NULL) {
+ D(("failed to open key file"));
+ return NULL;
+ }
+
+ length_id = strlen(identity);
+ temp = malloc(MAX_FILE_LINE_LEN);
+
+ for (;;) {
+ char *secret = NULL;
+
+ if (fgets(temp, MAX_FILE_LINE_LEN, secrets) == NULL) {
+ fclose(secrets);
+ return NULL;
+ }
+
+ D(("cf[%s][%s]", identity, temp));
+ if (memcmp(temp, identity, length_id)) {
+ continue;
+ }
+
+ D(("found entry"));
+ fclose(secrets);
+
+ for (secret=temp+length_id; *secret; ++secret) {
+ if (!(*secret == ' ' || *secret == '\n' || *secret == '\t')) {
+ break;
+ }
+ }
+
+ memmove(temp, secret, MAX_FILE_LINE_LEN-(secret-(temp+length_id)));
+ secret = temp;
+
+ for (; *secret; ++secret) {
+ if (*secret == ' ' || *secret == '\n' || *secret == '\t') {
+ break;
+ }
+ }
+
+ if (*secret) {
+ *secret = '\0';
+ }
+
+ D(("secret found [%s]", temp));
+
+ return temp;
+ }
+
+ /* NOT REACHED */
+}
+
+/*
+ * function to perform the two message authentication process
+ * (with support for event driven conversation functions)
+ */
+
+static int auth_sequence(pam_handle_t *pamh,
+ const struct ps_state_s *old, struct ps_state_s *new)
+{
+ const char *rhostname;
+ const char *rusername;
+ int retval;
+
+ retval = pam_get_item(pamh, PAM_RUSER, (const void **) &rusername);
+ if ((retval != PAM_SUCCESS) || (rusername == NULL)) {
+ D(("failed to obtain an rusername"));
+ new->state = PS_STATE_DEAD;
+ return PAM_AUTH_ERR;
+ }
+
+ retval = pam_get_item(pamh, PAM_RHOST, (const void **) &rhostname);
+ if ((retval != PAM_SUCCESS) || (rhostname == NULL)) {
+ D(("failed to identify local hostname: ", pam_strerror(pamh, retval)));
+ new->state = PS_STATE_DEAD;
+ return PAM_AUTH_ERR;
+ }
+
+ D(("switch on new->state=%d [%s@%s]", new->state, rusername, rhostname));
+ switch (new->state) {
+
+ case PS_STATE_INIT:
+ {
+ const char *user = NULL;
+
+ retval = pam_get_user(pamh, &user, NULL);
+
+ if ((retval == PAM_SUCCESS) && (user == NULL)) {
+ D(("success but no username?"));
+ new->state = PS_STATE_DEAD;
+ retval = PAM_USER_UNKNOWN;
+ }
+
+ if (retval != PAM_SUCCESS) {
+ if (retval == PAM_CONV_AGAIN) {
+ retval = PAM_INCOMPLETE;
+ } else {
+ new->state = PS_STATE_DEAD;
+ }
+ D(("state init failed: %s", pam_strerror(pamh, retval)));
+ return retval;
+ }
+
+ /* nothing else in this 'case' can be retried */
+
+ new->username = strdup(user);
+ if (new->username == NULL) {
+ D(("out of memory"));
+ new->state = PS_STATE_DEAD;
+ return PAM_BUF_ERR;
+ }
+
+ if (! generate_cookie(new->server_cookie)) {
+ D(("problem generating server cookie"));
+ new->state = PS_STATE_DEAD;
+ return PAM_ABORT;
+ }
+
+ new->current_prompt = NULL;
+ PAM_BP_RENEW(&new->current_prompt, PAM_BPC_SELECT,
+ sizeof(PS_AGENT_ID) + strlen(rusername) + 1
+ + strlen(rhostname) + 1 + 32);
+ sprintf(PAM_BP_WDATA(new->current_prompt),
+ PS_AGENT_ID "/%s@%s|%.32s", rusername, rhostname,
+ new->server_cookie);
+
+ /* note, the BP is guaranteed by the spec to be <NUL> terminated */
+ D(("initialization packet [%s]", PAM_BP_DATA(new->current_prompt)));
+
+ /* fall through */
+ new->state = PS_STATE_PROMPT1;
+
+ D(("fall through to state_prompt1"));
+ }
+
+ case PS_STATE_PROMPT1:
+ {
+ int i, length;
+
+ /* send {secret@here/jdoe@client.host|<s_cookie>} */
+ retval = converse(pamh, new);
+ if (retval != PAM_SUCCESS) {
+ if (retval == PAM_CONV_AGAIN) {
+ D(("conversation failed to complete"));
+ return PAM_INCOMPLETE;
+ } else {
+ new->state = PS_STATE_DEAD;
+ return retval;
+ }
+ }
+
+ if (retval != PAM_SUCCESS) {
+ D(("failed to read ruser@rhost"));
+ new->state = PS_STATE_DEAD;
+ return PAM_AUTH_ERR;
+ }
+
+ /* expect to receive the following {<seqid>|<a_cookie>} */
+ if (new->current_reply == NULL) {
+ D(("converstation returned [%s] but gave no reply",
+ pam_strerror(pamh, retval)));
+ new->state = PS_STATE_DEAD;
+ return PAM_CONV_ERR;
+ }
+
+ /* find | */
+ length = PAM_BP_LENGTH(new->current_reply);
+ for (i=0; i<length; ++i) {
+ if (PAM_BP_RDATA(new->current_reply)[i] == '|') {
+ break;
+ }
+ }
+ if (i >= length) {
+ D(("malformed response (no |) of length %d", length));
+ new->state = PS_STATE_DEAD;
+ return PAM_CONV_ERR;
+ }
+ if ((length - ++i) != 32) {
+ D(("cookie is incorrect length (%d,%d) %d != 32",
+ length, i, length-i));
+ new->state = PS_STATE_DEAD;
+ return PAM_CONV_ERR;
+ }
+
+ /* copy client cookie */
+ memcpy(new->client_cookie, PAM_BP_RDATA(new->current_reply)+i, 32);
+
+ /* generate a prompt that is length(seqid) + length(|) + 32 long */
+ PAM_BP_RENEW(&new->current_prompt, PAM_BPC_OK, i+32);
+ /* copy the head of the response prompt */
+ memcpy(PAM_BP_WDATA(new->current_prompt),
+ PAM_BP_RDATA(new->current_reply), i);
+ PAM_BP_RENEW(&new->current_reply, 0, 0);
+
+ /* look up the secret */
+ new->invalid_secret = 0;
+
+ if (new->secret_data == NULL) {
+ char *ruser_rhost;
+
+ ruser_rhost = malloc(strlen(rusername)+2+strlen(rhostname));
+ if (ruser_rhost == NULL) {
+ D(("out of memory"));
+ new->state = PS_STATE_DEAD;
+ return PAM_BUF_ERR;
+ }
+ sprintf(ruser_rhost, "%s@%s", rusername, rhostname);
+ new->secret_data = identify_secret(ruser_rhost, new->username);
+
+ memset(ruser_rhost, 0, strlen(ruser_rhost));
+ free(ruser_rhost);
+ }
+
+ if (new->secret_data == NULL) {
+ D(("secret not found for user"));
+ new->invalid_secret = 1;
+
+ /* need to make up a secret */
+ new->secret_data = malloc(32 + 1);
+ if (new->secret_data == NULL) {
+ D(("out of memory"));
+ new->state = PS_STATE_DEAD;
+ return PAM_BUF_ERR;
+ }
+ if (! generate_cookie(new->secret_data)) {
+ D(("what's up - no fake cookie generated?"));
+ new->state = PS_STATE_DEAD;
+ return PAM_ABORT;
+ }
+ }
+
+ /* construct md5[<client_cookie>|<server_cookie>|<secret_data>] */
+ if (! create_digest(new->client_cookie, new->server_cookie,
+ new->secret_data,
+ PAM_BP_WDATA(new->current_prompt)+i)) {
+ D(("md5 digesting failed"));
+ new->state = PS_STATE_DEAD;
+ return PAM_ABORT;
+ }
+
+ /* prompt2 is now constructed - fall through to send it */
+ }
+
+ case PS_STATE_PROMPT2:
+ {
+ /* send {<seqid>|md5[<client_cookie>|<server_cookie>|<secret_data>]} */
+ retval = converse(pamh, new);
+ if (retval != PAM_SUCCESS) {
+ if (retval == PAM_CONV_AGAIN) {
+ D(("conversation failed to complete"));
+ return PAM_INCOMPLETE;
+ } else {
+ new->state = PS_STATE_DEAD;
+ return retval;
+ }
+ }
+
+ /* After we complete this section, we should not be able to
+ recall this authentication function. So, we force all
+ future calls into the weeds. */
+
+ new->state = PS_STATE_DEAD;
+
+ /* expect reply:{md5[<secret_data>|<server_cookie>|<client_cookie>]} */
+
+ {
+ int cf;
+ char expectation[33];
+
+ if (!create_digest(new->secret_data, new->server_cookie,
+ new->client_cookie, expectation)) {
+ new->state = PS_STATE_DEAD;
+ return PAM_ABORT;
+ }
+
+ cf = strcmp(expectation, PAM_BP_RDATA(new->current_reply));
+ memset(expectation, 0, sizeof(expectation));
+ if (cf || new->invalid_secret) {
+ D(("failed to authenticate"));
+ return PAM_AUTH_ERR;
+ }
+ }
+
+ D(("correctly authenticated :)"));
+ return PAM_SUCCESS;
+ }
+
+ default:
+ new->state = PS_STATE_DEAD;
+
+ case PS_STATE_DEAD:
+
+ D(("state is currently dead/unknown"));
+ return PAM_AUTH_ERR;
+ }
+
+ fprintf(stderr, "pam_secret: this should not be reached\n");
+ return PAM_ABORT;
+}
+
+static void clean_data(pam_handle_t *pamh, void *datum, int error_status)
+{
+ struct ps_state_s *data = datum;
+
+ D(("liberating datum=%p", datum));
+
+ if (data) {
+ D(("renew prompt"));
+ PAM_BP_RENEW(&data->current_prompt, 0, 0);
+ D(("renew reply"));
+ PAM_BP_RENEW(&data->current_reply, 0, 0);
+ D(("overwrite datum"));
+ memset(data, 0, sizeof(struct ps_state_s));
+ D(("liberate datum"));
+ free(data);
+ }
+
+ D(("done."));
+}
+
+/*
+ * front end for the authentication function
+ */
+
+int pam_sm_authenticate(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int retval;
+ struct ps_state_s *new_data;
+ const struct ps_state_s *old_data;
+
+ D(("called"));
+
+ new_data = calloc(1, sizeof(struct ps_state_s));
+ if (new_data == NULL) {
+ D(("out of memory"));
+ return PAM_BUF_ERR;
+ }
+ new_data->retval = PAM_SUCCESS;
+
+ retval = pam_get_data(pamh, PS_STATE_ID, (const void **) &old_data);
+ if (retval == PAM_SUCCESS) {
+ new_data->state = old_data->state;
+ memcpy(new_data->server_cookie, old_data->server_cookie, 32);
+ memcpy(new_data->client_cookie, old_data->client_cookie, 32);
+ if (old_data->username) {
+ new_data->username = strdup(old_data->username);
+ }
+ if (old_data->secret_data) {
+ new_data->secret_data = strdup(old_data->secret_data);
+ }
+ if (old_data->current_prompt) {
+ int length;
+
+ length = PAM_BP_LENGTH(old_data->current_prompt);
+ PAM_BP_RENEW(&new_data->current_prompt,
+ PAM_BP_CONTROL(old_data->current_prompt), length);
+ PAM_BP_FILL(new_data->current_prompt, 0, length,
+ PAM_BP_RDATA(old_data->current_prompt));
+ }
+ /* don't need to duplicate current_reply */
+ } else {
+ old_data = NULL;
+ new_data->state = PS_STATE_INIT;
+ }
+
+ D(("call auth_sequence"));
+ new_data->retval = auth_sequence(pamh, old_data, new_data);
+ D(("returned from auth_sequence"));
+
+ retval = pam_set_data(pamh, PS_STATE_ID, new_data, clean_data);
+ if (retval != PAM_SUCCESS) {
+ D(("unable to store new_data"));
+ } else {
+ retval = new_data->retval;
+ }
+
+ old_data = new_data = NULL;
+
+ D(("done (%d)", retval));
+ return retval;
+}
+
+/*
+ * front end for the credential setting function
+ */
+
+#define AUTH_SESSION_TICKET_ENV_FORMAT "AUTH_SESSION_TICKET="
+
+int pam_sm_setcred(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int retval;
+ const struct ps_state_s *old_data;
+
+ D(("called"));
+
+ /* XXX - need to pay attention to the various flavors of call */
+
+ /* XXX - need provide an option to turn this feature on/off: if
+ other modules want to supply an AUTH_SESSION_TICKET, we should
+ leave it up to the admin which module dominiates. */
+
+ retval = pam_get_data(pamh, PS_STATE_ID, (const void **) &old_data);
+ if (retval != PAM_SUCCESS) {
+ D(("no data to base decision on"));
+ return PAM_AUTH_ERR;
+ }
+
+ /*
+ * If ok, export a derived shared secret session ticket to the
+ * client's PAM environment - the ticket has the form
+ *
+ * AUTH_SESSION_TICKET =
+ * md5[<server_cookie>|<secret_data>|<client_cookie>]
+ *
+ * This is a precursor to supporting a spoof resistant trusted
+ * path mechanism. This shared secret ticket can be used to add
+ * a hard-to-guess checksum to further authentication data.
+ */
+
+ retval = old_data->retval;
+ if (retval == PAM_SUCCESS) {
+ char envticket[sizeof(AUTH_SESSION_TICKET_ENV_FORMAT)+32];
+
+ memcpy(envticket, AUTH_SESSION_TICKET_ENV_FORMAT,
+ sizeof(AUTH_SESSION_TICKET_ENV_FORMAT));
+
+ if (! create_digest(old_data->server_cookie, old_data->secret_data,
+ old_data->client_cookie,
+ envticket+sizeof(AUTH_SESSION_TICKET_ENV_FORMAT)-1
+ )) {
+ D(("unable to generate a digest for session ticket"));
+ return PAM_ABORT;
+ }
+
+ D(("putenv[%s]", envticket));
+ retval = pam_putenv(pamh, envticket);
+ memset(envticket, 0, sizeof(envticket));
+ }
+
+ old_data = NULL;
+ D(("done (%d)", retval));
+
+ return retval;
+}
diff --git a/libpamc/test/regress/Makefile b/libpamc/test/regress/Makefile
new file mode 100644
index 0000000..cba474f
--- /dev/null
+++ b/libpamc/test/regress/Makefile
@@ -0,0 +1,7 @@
+CFLAGS = -g -I ../../include
+
+test.libpamc: test.libpamc.o
+ $(CC) -o $@ $(CFLAGS) $< -L ../.. -lpamc
+
+clean:
+ rm -f test.libpamc test.libpamc.o
diff --git a/libpamc/test/regress/run_test.sh b/libpamc/test/regress/run_test.sh
new file mode 100755
index 0000000..6922f03
--- /dev/null
+++ b/libpamc/test/regress/run_test.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+LD_LIBRARY_PATH=../.. ; export LD_LIBRARY_PATH
+PAMC_AGENT_PATH="../agents" ; export PAMC_AGENT_PATH
+
+./test.libpamc
diff --git a/libpamc/test/regress/test.libpamc.c b/libpamc/test/regress/test.libpamc.c
new file mode 100644
index 0000000..4251b4f
--- /dev/null
+++ b/libpamc/test/regress/test.libpamc.c
@@ -0,0 +1,343 @@
+/*
+ * This is a small test program for testing libpamc against the
+ * secret@here agent. It does the same as the test.secret@here perl
+ * script in this directory, but via the libpamc API.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <security/pam_client.h>
+#include <ctype.h>
+
+struct internal_packet {
+ int length;
+ int at;
+ char *buffer;
+};
+
+
+void append_data(struct internal_packet *packet, int extra, const char *data)
+{
+ if ((extra + packet->at) >= packet->length) {
+ if (packet->length == 0) {
+ packet->length = 1000;
+ }
+ /* make sure we have at least a char extra space available */
+ while (packet->length <= (extra + packet->at)) {
+ packet->length <<= 1;
+ }
+ packet->buffer = realloc(packet->buffer, packet->length);
+ if (packet->buffer == NULL) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ }
+
+ if (data != NULL) {
+ memcpy(packet->at + packet->buffer, data, extra);
+ }
+ packet->at += extra;
+
+ /* assisting string manipulation */
+ packet->buffer[packet->at] = '\0';
+}
+
+void append_string(struct internal_packet *packet, const char *string,
+ int with_nul)
+{
+ append_data(packet, strlen(string) + (with_nul ? 1:0), string);
+}
+
+char *identify_secret(char *identity)
+{
+ struct internal_packet temp_packet;
+ FILE *secrets;
+ int length_id;
+
+ temp_packet.length = temp_packet.at = 0;
+ temp_packet.buffer = NULL;
+
+ append_string(&temp_packet, "/home/", 0);
+ append_string(&temp_packet, getlogin(), 0);
+ append_string(&temp_packet, "/.secret@here", 1);
+
+ secrets = fopen(temp_packet.buffer, "r");
+ if (secrets == NULL) {
+ fprintf(stderr, "server: failed to open\n [%s]\n",
+ temp_packet.buffer);
+ exit(1);
+ }
+
+ length_id = strlen(identity);
+ for (;;) {
+ char *secret = NULL;
+ temp_packet.at = 0;
+
+ if (fgets(temp_packet.buffer, temp_packet.length, secrets) == NULL) {
+ fclose(secrets);
+ return NULL;
+ }
+
+ if (memcmp(temp_packet.buffer, identity, length_id)) {
+ continue;
+ }
+
+ fclose(secrets);
+ for (secret=temp_packet.buffer; *secret; ++secret) {
+ if (*secret == ' ' || *secret == '\n' || *secret == '\t') {
+ break;
+ }
+ }
+ for (; *secret; ++secret) {
+ if (!(*secret == ' ' || *secret == '\n' || *secret == '\t')) {
+ break;
+ }
+ }
+
+ for (temp_packet.buffer=secret; *temp_packet.buffer;
+ ++temp_packet.buffer) {
+ if (*temp_packet.buffer == ' ' || *temp_packet.buffer == '\n'
+ || *temp_packet.buffer == '\t') {
+ break;
+ }
+ }
+ if (*temp_packet.buffer) {
+ *temp_packet.buffer = '\0';
+ }
+
+ return secret;
+ }
+
+ /* NOT REACHED */
+}
+
+/*
+ * This is a hack, and is fundamentally insecure. All our secrets will be
+ * displayed on the command line for someone doing 'ps' to see. This
+ * is just for programming convenience in this instance, since this
+ * program is simply a regression test. The pam_secret module should
+ * not do this, but make use of md5 routines directly.
+ */
+
+char *create_digest(int length, const char *raw)
+{
+ struct internal_packet temp_packet;
+ FILE *pipe;
+
+ temp_packet.length = temp_packet.at = 0;
+ temp_packet.buffer = NULL;
+
+ append_string(&temp_packet, "echo -n '", 0);
+ append_string(&temp_packet, raw, 0);
+ append_string(&temp_packet, "'|/usr/bin/md5sum -", 1);
+
+ fprintf(stderr, "am attempting to run [%s]\n", temp_packet.buffer);
+
+ pipe = popen(temp_packet.buffer, "r");
+ if (pipe == NULL) {
+ fprintf(stderr, "server: failed to run\n [%s]\n", temp_packet.buffer);
+ exit(1);
+ }
+
+ temp_packet.at = 0;
+ append_data(&temp_packet, 32, NULL);
+
+ if (fgets(temp_packet.buffer, 33, pipe) == NULL) {
+ fprintf(stderr, "server: failed to read digest\n");
+ exit(1);
+ }
+ if (strlen(temp_packet.buffer) != 32) {
+ fprintf(stderr, "server: digest was not 32 chars?? [%s]\n",
+ temp_packet.buffer);
+ exit(1);
+ }
+
+ fclose(pipe);
+
+ return temp_packet.buffer;
+}
+
+void packet_to_prompt(pamc_bp_t *prompt_p, uint8_t control,
+ struct internal_packet *packet)
+{
+ PAM_BP_RENEW(prompt_p, control, packet->at);
+ PAM_BP_FILL(*prompt_p, 0, packet->at, packet->buffer);
+ packet->at = 0;
+}
+
+void prompt_to_packet(pamc_bp_t prompt, struct internal_packet *packet)
+{
+ int data_length;
+
+ data_length = PAM_BP_LENGTH(prompt);
+ packet->at = 0;
+ append_data(packet, data_length, NULL);
+
+ PAM_BP_EXTRACT(prompt, 0, data_length, packet->buffer);
+
+ fprintf(stderr, "server received[%d]: {%d|0x%.2x|%s}\n",
+ data_length,
+ PAM_BP_SIZE(prompt), PAM_BP_RCONTROL(prompt),
+ PAM_BP_RDATA(prompt));
+}
+
+int main(int argc, char **argv)
+{
+ pamc_handle_t pch;
+ pamc_bp_t prompt = NULL;
+ struct internal_packet packet_data, *packet;
+ char *temp_string, *secret, *user, *a_cookie, *seqid, *digest;
+ const char *cookie = "123451234512345";
+ int retval;
+
+ packet = &packet_data;
+ packet->length = 0;
+ packet->at = 0;
+ packet->buffer = NULL;
+
+ pch = pamc_start();
+ if (pch == NULL) {
+ fprintf(stderr, "server: unable to get a handle from libpamc\n");
+ exit(1);
+ }
+
+ temp_string = getlogin();
+ if (temp_string == NULL) {
+ fprintf(stderr, "server: who are you?\n");
+ exit(1);
+ }
+#define DOMAIN "@local.host"
+ user = malloc(1+strlen(temp_string)+strlen(DOMAIN));
+ if (user == NULL) {
+ fprintf(stderr, "server: out of memory for user id\n");
+ exit(1);
+ }
+ sprintf(user, "%s%s", temp_string, DOMAIN);
+
+ append_string(packet, "secret@here/", 0);
+ append_string(packet, user, 0);
+ append_string(packet, "|", 0);
+ append_string(packet, cookie, 0);
+ packet_to_prompt(&prompt, PAM_BPC_SELECT, packet);
+
+ /* get the library to accept the first packet (which should load
+ the secret@here agent) */
+
+ retval = pamc_converse(pch, &prompt);
+ fprintf(stderr, "server: after conversation\n");
+ if (PAM_BP_RCONTROL(prompt) != PAM_BPC_OK) {
+ fprintf(stderr, "server: prompt had unexpected control type: %u\n",
+ PAM_BP_RCONTROL(prompt));
+ exit(1);
+ }
+
+ fprintf(stderr, "server: got a prompt back\n");
+
+ prompt_to_packet(prompt, packet);
+
+ temp_string = strtok(packet->buffer, "|");
+ if (temp_string == NULL) {
+ fprintf(stderr, "server: prompt does not contain anything");
+ exit(1);
+ }
+ seqid = strdup(temp_string);
+ if (seqid == NULL) {
+ fprintf(stderr, "server: unable to store sequence id\n");
+ }
+
+ temp_string = strtok(NULL, "|");
+ if (temp_string == NULL) {
+ fprintf(stderr, "server: no cookie from agent\n");
+ exit(1);
+ }
+ a_cookie = strdup(temp_string);
+ if (a_cookie == NULL) {
+ fprintf(stderr, "server: no memory to store agent cookie\n");
+ exit(1);
+ }
+
+ fprintf(stderr, "server: agent responded with {%s|%s}\n", seqid, a_cookie);
+ secret = identify_secret(user);
+ fprintf(stderr, "server: secret=%s\n", secret);
+
+ /* now, we construct the response */
+ packet->at = 0;
+ append_string(packet, a_cookie, 0);
+ append_string(packet, "|", 0);
+ append_string(packet, cookie, 0);
+ append_string(packet, "|", 0);
+ append_string(packet, secret, 0);
+
+ fprintf(stderr, "server: get digest of %s\n", packet->buffer);
+
+ digest = create_digest(packet->at, packet->buffer);
+
+ fprintf(stderr, "server: secret=%s, digest=%s\n", secret, digest);
+
+ packet->at = 0;
+ append_string(packet, seqid, 0);
+ append_string(packet, "|", 0);
+ append_string(packet, digest, 0);
+ packet_to_prompt(&prompt, PAM_BPC_OK, packet);
+
+ retval = pamc_converse(pch, &prompt);
+ fprintf(stderr, "server: after 2nd conversation\n");
+ if (PAM_BP_RCONTROL(prompt) != PAM_BPC_DONE) {
+ fprintf(stderr, "server: 2nd prompt had unexpected control type: %u\n",
+ PAM_BP_RCONTROL(prompt));
+ exit(1);
+ }
+
+ prompt_to_packet(prompt, packet);
+ PAM_BP_RENEW(&prompt, 0, 0);
+
+ temp_string = strtok(packet->buffer, "|");
+ if (temp_string == NULL) {
+ fprintf(stderr, "no digest from agent\n");
+ exit(1);
+ }
+ temp_string = strdup(temp_string);
+
+ packet->at = 0;
+ append_string(packet, secret, 0);
+ append_string(packet, "|", 0);
+ append_string(packet, cookie, 0);
+ append_string(packet, "|", 0);
+ append_string(packet, a_cookie, 0);
+
+ fprintf(stderr, "server: get digest of %s\n", packet->buffer);
+
+ digest = create_digest(packet->at, packet->buffer);
+
+ fprintf(stderr, "server: digest=%s\n", digest);
+
+ if (strcmp(digest, temp_string)) {
+ fprintf(stderr, "server: agent doesn't know the secret\n");
+ fprintf(stderr, "server: agent says: [%s]\n"
+ "server: server says: [%s]\n", temp_string, digest);
+ exit(1);
+ } else {
+ fprintf(stderr, "server: agent seems to know the secret\n");
+
+ packet->at = 0;
+ append_string(packet, cookie, 0);
+ append_string(packet, "|", 0);
+ append_string(packet, secret, 0);
+ append_string(packet, "|", 0);
+ append_string(packet, a_cookie, 0);
+
+ digest = create_digest(packet->at, packet->buffer);
+
+ fprintf(stderr, "server: putenv(\"AUTH_SESSION_TICKET=%s\")\n",
+ digest);
+ }
+
+
+ retval = pamc_end(&pch);
+
+ fprintf(stderr, "server: agent(s) were %shappy to terminate\n",
+ retval == PAM_BPC_TRUE ? "":"un");
+
+ exit(!retval);
+}
diff --git a/libpamc/test/regress/test.secret@here b/libpamc/test/regress/test.secret@here
new file mode 100755
index 0000000..67fe22e
--- /dev/null
+++ b/libpamc/test/regress/test.secret@here
@@ -0,0 +1,151 @@
+#!/usr/bin/perl
+
+##
+## this is a test script for regressing changes to the secret@here PAM
+## agent
+##
+
+$^W = 1;
+use strict;
+use IPC::Open2;
+
+$| = 1;
+
+my $whoami = `/usr/bin/whoami`; chomp $whoami;
+my $cookie = "12345";
+my $user_domain = "$whoami\@local.host";
+
+my $pid = open2(\*Reader, \*Writer, "../agents/secret\@here blah")
+ or die "failed to load secret\@here agent";
+
+unless (-f (getpwuid($<))[7]."/.secret\@here") {
+ print STDERR "server: ". "no " .(getpwuid($<))[7]. "/.secret\@here file\n";
+ die "no config file";
+}
+
+WriteBinaryPrompt(\*Writer, 0x02, "secret\@here/$user_domain|$cookie");
+
+my ($control, $data) = ReadBinaryPrompt(\*Reader);
+
+print STDERR "server: ". "reply: control=$control, data=$data\n";
+if ($control != 1) {
+ die "expected 1 (OK) for the first agent reply; got $control";
+}
+my ($seqid, $a_cookie) = split '\|', $data;
+
+# server needs to convince agent that it knows the secret before
+# agent will give a valid response
+my $secret = IdentifyLocalSecret($user_domain);
+my $digest = CreateDigest($a_cookie."|".$cookie."|".$secret);
+
+print STDERR "server: ". "digest = $digest\n";
+WriteBinaryPrompt(\*Writer, 0x01, "$seqid|$digest");
+
+# The agent will authenticate us and then reply with its
+# authenticating digest. we check that before we're done.
+
+($control, $data) = ReadBinaryPrompt(\*Reader);
+if ($control != 0x03) {
+ die "server: agent did not reply with a 'done' prompt ($control)\n";
+}
+
+unless ($data eq CreateDigest($secret."|".$cookie."|".$a_cookie)) {
+ die "server: agent is not authenticated\n";
+}
+
+print STDERR "server: agent appears to know secret\n";
+
+my $session_authenticated_ticket
+ = CreateDigest($cookie."|".$secret."|".$a_cookie);
+
+print STDERR "server: should putenv("
+ ."\"AUTH_SESSION_TICKET=$session_authenticated_ticket\")\n";
+
+exit 0;
+
+sub CreateDigest ($) {
+ my ($data) = @_;
+
+ my $pid = open2(\*MD5out, \*MD5in, "/usr/bin/md5sum -")
+ or die "you'll need /usr/bin/md5sum installed";
+
+ my $oldfd = select MD5in; $|=1; select $oldfd;
+ print MD5in "$data";
+ close MD5in;
+ my $reply = <MD5out>;
+ ($reply) = split /\s/, $reply;
+ print STDERR "server: ". "md5 said: <$reply>\n";
+ close MD5out;
+
+ return $reply;
+}
+
+sub ReadBinaryPrompt ($) {
+ my ($fd) = @_;
+
+ my $buffer = " ";
+ my $count = read($fd, $buffer, 5);
+ if ($count == 0) {
+ # no more packets to read
+ return (0, "");
+ }
+
+ if ($count != 5) {
+ # broken packet header
+ return (-1, "");
+ }
+
+ my ($length, $control) = unpack("N C", $buffer);
+ if ($length < 5) {
+ # broken packet length
+ return (-1, "");
+ }
+
+ my $data = "";
+ $length -= 5;
+ while ($count = read($fd, $buffer, $length)) {
+ $data .= $buffer;
+ if ($count != $length) {
+ $length -= $count;
+ next;
+ }
+
+ print STDERR "server: ". "data is [$data]\n";
+
+ return ($control, $data);
+ }
+
+ # broken packet data
+ return (-1, "");
+}
+
+sub WriteBinaryPrompt ($$$) {
+ my ($fd, $control, $data) = @_;
+
+ my $length = 5 + length($data);
+ printf STDERR "server: ". "{%d|0x%.2x|%s}\n", $length, $control, $data;
+ my $bp = pack("N C a*", $length, $control, $data);
+ print $fd $bp;
+
+ print STDERR "server: ". "control passed to agent\@here\n";
+}
+
+sub IdentifyLocalSecret ($) {
+ my ($identifier) = @_;
+ my $secret;
+
+ my $whoami = `/usr/bin/whoami` ; chomp $whoami;
+ if (open SECRETS, "< " .(getpwuid($<))[7]. "/.secret\@here") {
+ my $line;
+ while (defined ($line = <SECRETS>)) {
+ my ($id, $sec) = split /[\s]/, $line;
+ if ((defined $id) && ($id eq $identifier)) {
+ $secret = $sec;
+ last;
+ }
+ }
+ close SECRETS;
+ }
+
+ return $secret;
+}