diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 14:22:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 14:22:51 +0000 |
commit | 9ada0093e92388590c7368600ca4e9e3e376f0d0 (patch) | |
tree | a56fe41110023676d7082028cbaa47ca4b6e6164 /libpam | |
parent | Initial commit. (diff) | |
download | pam-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 '')
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; +} |