From 9ada0093e92388590c7368600ca4e9e3e376f0d0 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 16:22:51 +0200 Subject: Adding upstream version 1.5.2. Signed-off-by: Daniel Baumann --- libpam/Makefile.am | 46 ++ libpam/Makefile.in | 953 ++++++++++++++++++++++++++++++ libpam/include/pam_cc_compat.h | 66 +++ libpam/include/pam_inline.h | 118 ++++ libpam/include/security/_pam_compat.h | 126 ++++ libpam/include/security/_pam_macros.h | 196 +++++++ libpam/include/security/_pam_types.h | 333 +++++++++++ libpam/include/security/pam_appl.h | 104 ++++ libpam/include/security/pam_ext.h | 91 +++ libpam/include/security/pam_modules.h | 124 ++++ libpam/include/security/pam_modutil.h | 160 +++++ libpam/include/test_assert.h | 55 ++ libpam/libpam.map | 89 +++ libpam/pam.pc.in | 9 + libpam/pam_account.c | 23 + libpam/pam_audit.c | 242 ++++++++ libpam/pam_auth.c | 73 +++ libpam/pam_data.c | 166 ++++++ libpam/pam_delay.c | 160 +++++ libpam/pam_dispatch.c | 447 ++++++++++++++ libpam/pam_dynamic.c | 138 +++++ libpam/pam_end.c | 98 ++++ libpam/pam_env.c | 392 +++++++++++++ libpam/pam_get_authtok.c | 280 +++++++++ libpam/pam_handlers.c | 1045 +++++++++++++++++++++++++++++++++ libpam/pam_item.c | 396 +++++++++++++ libpam/pam_misc.c | 360 ++++++++++++ libpam/pam_modutil_check_user.c | 92 +++ libpam/pam_modutil_cleanup.c | 19 + libpam/pam_modutil_getgrgid.c | 138 +++++ libpam/pam_modutil_getgrnam.c | 127 ++++ libpam/pam_modutil_getlogin.c | 80 +++ libpam/pam_modutil_getpwnam.c | 127 ++++ libpam/pam_modutil_getpwuid.c | 138 +++++ libpam/pam_modutil_getspnam.c | 127 ++++ libpam/pam_modutil_ingroup.c | 130 ++++ libpam/pam_modutil_ioloop.c | 53 ++ libpam/pam_modutil_priv.c | 179 ++++++ libpam/pam_modutil_private.h | 24 + libpam/pam_modutil_sanitize.c | 147 +++++ libpam/pam_modutil_searchkey.c | 128 ++++ libpam/pam_password.c | 61 ++ libpam/pam_prelude.c | 454 ++++++++++++++ libpam/pam_prelude.h | 15 + libpam/pam_private.h | 358 +++++++++++ libpam/pam_session.c | 45 ++ libpam/pam_start.c | 179 ++++++ libpam/pam_strerror.c | 106 ++++ libpam/pam_syslog.c | 115 ++++ libpam/pam_tokens.h | 112 ++++ libpam/pam_vprompt.c | 115 ++++ 51 files changed, 9359 insertions(+) create mode 100644 libpam/Makefile.am create mode 100644 libpam/Makefile.in create mode 100644 libpam/include/pam_cc_compat.h create mode 100644 libpam/include/pam_inline.h create mode 100644 libpam/include/security/_pam_compat.h create mode 100644 libpam/include/security/_pam_macros.h create mode 100644 libpam/include/security/_pam_types.h create mode 100644 libpam/include/security/pam_appl.h create mode 100644 libpam/include/security/pam_ext.h create mode 100644 libpam/include/security/pam_modules.h create mode 100644 libpam/include/security/pam_modutil.h create mode 100644 libpam/include/test_assert.h create mode 100644 libpam/libpam.map create mode 100644 libpam/pam.pc.in create mode 100644 libpam/pam_account.c create mode 100644 libpam/pam_audit.c create mode 100644 libpam/pam_auth.c create mode 100644 libpam/pam_data.c create mode 100644 libpam/pam_delay.c create mode 100644 libpam/pam_dispatch.c create mode 100644 libpam/pam_dynamic.c create mode 100644 libpam/pam_end.c create mode 100644 libpam/pam_env.c create mode 100644 libpam/pam_get_authtok.c create mode 100644 libpam/pam_handlers.c create mode 100644 libpam/pam_item.c create mode 100644 libpam/pam_misc.c create mode 100644 libpam/pam_modutil_check_user.c create mode 100644 libpam/pam_modutil_cleanup.c create mode 100644 libpam/pam_modutil_getgrgid.c create mode 100644 libpam/pam_modutil_getgrnam.c create mode 100644 libpam/pam_modutil_getlogin.c create mode 100644 libpam/pam_modutil_getpwnam.c create mode 100644 libpam/pam_modutil_getpwuid.c create mode 100644 libpam/pam_modutil_getspnam.c create mode 100644 libpam/pam_modutil_ingroup.c create mode 100644 libpam/pam_modutil_ioloop.c create mode 100644 libpam/pam_modutil_priv.c create mode 100644 libpam/pam_modutil_private.h create mode 100644 libpam/pam_modutil_sanitize.c create mode 100644 libpam/pam_modutil_searchkey.c create mode 100644 libpam/pam_password.c create mode 100644 libpam/pam_prelude.c create mode 100644 libpam/pam_prelude.h create mode 100644 libpam/pam_private.h create mode 100644 libpam/pam_session.c create mode 100644 libpam/pam_start.c create mode 100644 libpam/pam_strerror.c create mode 100644 libpam/pam_syslog.c create mode 100644 libpam/pam_tokens.h create mode 100644 libpam/pam_vprompt.c (limited to 'libpam') 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 +# + +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 +# + + + +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 + */ + +#ifndef PAM_CC_COMPAT_H +#define PAM_CC_COMPAT_H + +#include "config.h" +#include + +#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 + * + * 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 +#include +#include + +/* + * 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 + * slight modification by Brad M. Garcia + * + * 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 + */ + +/* a 'safe' version of strdup */ + +#include +#include + +#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__ + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * 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, " \n", X); \ + for (i = 0; i < XS ; ++x, ++i) { \ + fprintf(stderr, " %02X. <%p:%02X>\n", i, x, *x); \ + } \ + fprintf(stderr, " \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 @@ +/* + * + * + * 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 , 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 @@ +/* + * + * + * 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 /* 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 + +#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. + * + * + * + * 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 +#include + +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 @@ +/* + * + * + * 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 /* 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 + +#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 + * + * + * + * 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 +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +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 + */ + +#ifndef TEST_ASSERT_H +# define TEST_ASSERT_H + +# ifdef HAVE_CONFIG_H +# include +# endif + +# include +# include + +# 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 + +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 */ + +#include "pam_private.h" +#include "pam_modutil_private.h" + +#ifdef HAVE_LIBAUDIT +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include + +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 +#include + +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 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 +#include + +/* ********************************************************************** + * 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 + * + */ + +#include "pam_private.h" + +#include +#include + +/* + * 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:"" ); + 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 +#elif defined(PAM_DYLD) +# include +#else /* PAM_SHL */ +# include +#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 + +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 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 +#include + +#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; ienv->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; ienv->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 + +#define PROMPT _("Password: ") +/* For Translators: "%s" is replaced with "". */ +#define PROMPT_CURRENT_ARG _("Current %s password: ") +#define PROMPT_CURRENT_NOARG _("Current password: ") +/* For Translators: "%s" is replaced with "". */ +#define PROMPT_NEW_ARG _("New %s password: ") +#define PROMPT_NEW_NOARG _("New password: ") +/* For Translators: "%s" is replaced with "". */ +#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 + * + */ + +#include "pam_private.h" +#include "pam_inline.h" + +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include + +#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 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 +#include +#include +#include +#include +#include + +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 + +#include +#include +#include + +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 + +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 +#include +#include +#include +#include + +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 +#include +#include +#include +#include + +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 +#include +#include + +#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 +#include +#include +#include +#include + +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 +#include +#include +#include +#include + +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 +#include +#include +#include +#include + +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 +#include +#include + +#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 +#include + +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 +#include +#include +#include +#include +#include + +/* + * 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 + */ + +#include "config.h" + +#include +#include +#include + +#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 +#include +#include +#include +#include + +/* + * 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 +#include +#include +#include +#include +#ifdef USE_ECONF +#include +#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 +#include + +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 + */ + +#include +#include + +#ifdef PRELUDE + +#include +#include +#include + +#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 + */ + +#ifndef _SECURITY_PAM_PRELUDE_H +#define _SECURITY_PAM_PRELUDE_H + +#include + +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 + +#include +#include +#include + +/* 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 + +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 + +/* 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 + * + * 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 + +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 +#include +#include +#include +#include + +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 +#include +#include +#include +#include + +#include +#include +#include + +#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:"", + _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 + * + * 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 +#include +#include +#include +#include + +#include +#include +#include + +#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; +} -- cgit v1.2.3