diff options
Diffstat (limited to 'modules/pam_unix')
35 files changed, 7633 insertions, 0 deletions
diff --git a/modules/pam_unix/CHANGELOG b/modules/pam_unix/CHANGELOG new file mode 100644 index 00000000..c18acc27 --- /dev/null +++ b/modules/pam_unix/CHANGELOG @@ -0,0 +1,54 @@ +$Id$ + +* Mon Aug 16 1999 Jan Rkorajski <baggins@pld.org.pl> +- fixed reentrancy problems + +* Sun Jul 4 21:03:42 PDT 1999 + +- temporarily removed the crypt16 stuff. I'm really paranoid about + crypto stuff and exporting it, and there are a few too many 's-box' + references in the code for my liking.. + +* Wed Jun 30 1999 Steve Langasek <vorlon@netexpress.net> +- further NIS+ fixes + +* Sun Jun 27 1999 Steve Langasek <vorlon@netexpress.net> +- fix to uid-handling code for NIS+ + +* Sat Jun 26 1999 Jan Rkorajski <baggins@mimuw.edu.pl> +- merged MD5 fix and early failure syslog + by Andrey Vladimirovich Savochkin <saw@msu.ru> +- minor fixes +- added signal handler to unix_chkpwd + +* Fri Jun 25 1999 Stephen Langasek <vorlon@netexpress.net> +- reorganized the code to let it build as separate C files + +* Sun Jun 20 1999 Jan Rkorajski <baggins@mimuw.edu.pl> +- fixes in pam_unix_auth, it incorrectly saved and restored return + value when likeauth option was used + +* Tue Jun 15 1999 Jan Rkorajski <baggins@mimuw.edu.pl> +- added NIS+ support + +* Mon Jun 14 1999 Jan Rkorajski <baggins@mimuw.edu.pl> +- total rewrite based on pam_pwdb module, now there is ONE pam_unix.so + module, it accepts the same options as pam_pwdb - all of them correctly ;) + (pam_pwdb dosn't understand what DISALLOW_NULL_AUTHTOK means) + +* Tue Apr 20 1999 Jan Rkorajski <baggins@mimuw.edu.pl> +- Arghhh, pam_unix_passwd was not updating /etc/shadow when used with + pam_cracklib. + +* Mon Apr 19 1999 Jan Rkorajski <baggins@mimuw.edu.pl> +- added "remember=XXX" option that means 'remember XXX old passwords' + Old passwords are stored in /etc/security/opasswd, there can be + maximum of 400 passwords per user. + +* Sat Mar 27 1999 Jan Rkorajski <baggins@mimuw.edu.pl> +- added crypt16 to pam_unix_auth and pam_unix_passwd (check only, this algorithm + is too lame to use it in real life) + +* Sun Mar 21 1999 Jan Rkorajski <baggins@mimuw.edu.pl> +- pam_unix_auth now correctly behave when user has NULL AUTHTOK +- pam_unix_auth returns PAM_PERM_DENIED when seteuid fails diff --git a/modules/pam_unix/Makefile.am b/modules/pam_unix/Makefile.am new file mode 100644 index 00000000..56ed5916 --- /dev/null +++ b/modules/pam_unix/Makefile.am @@ -0,0 +1,71 @@ +# +# Copyright (c) 2005, 2006, 2009, 2011 Thorsten Kukuk <kukuk@suse.de> +# + +CLEANFILES = *~ +MAINTAINERCLEANFILES = $(MANS) README + +EXTRA_DIST = README md5.c md5_crypt.c lckpwdf.-c $(MANS) CHANGELOG \ + tst-pam_unix $(XMLS) + +man_MANS = pam_unix.8 unix_chkpwd.8 unix_update.8 +XMLS = README.xml pam_unix.8.xml unix_chkpwd.8.xml unix_update.8.xml + +TESTS = tst-pam_unix + +securelibdir = $(SECUREDIR) +secureconfdir = $(SCONFIGDIR) + +AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \ + -DCHKPWD_HELPER=\"$(sbindir)/unix_chkpwd\" \ + -DUPDATE_HELPER=\"$(sbindir)/unix_update\" \ + $(NIS_CFLAGS) + +if HAVE_LIBSELINUX + AM_CFLAGS += -D"WITH_SELINUX" +endif + +pam_unix_la_LDFLAGS = -no-undefined -avoid-version -module +if HAVE_VERSIONING + pam_unix_la_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map +endif +pam_unix_la_LIBADD = $(top_builddir)/libpam/libpam.la \ + @LIBCRYPT@ @LIBSELINUX@ $(NIS_LIBS) + +securelib_LTLIBRARIES = pam_unix.la + +noinst_HEADERS = md5.h support.h yppasswd.h bigcrypt.h passverify.h \ + pam_unix_static.h + +sbin_PROGRAMS = unix_chkpwd unix_update + +noinst_PROGRAMS = bigcrypt + +pam_unix_la_SOURCES = bigcrypt.c pam_unix_acct.c \ + pam_unix_auth.c pam_unix_passwd.c pam_unix_sess.c support.c \ + passverify.c yppasswd_xdr.c md5_good.c md5_broken.c +if STATIC_MODULES +pam_unix_la_SOURCES += pam_unix_static.c +endif + +bigcrypt_SOURCES = bigcrypt.c bigcrypt_main.c +bigcrypt_CFLAGS = $(AM_CFLAGS) +bigcrypt_LDADD = @LIBCRYPT@ + +unix_chkpwd_SOURCES = unix_chkpwd.c md5_good.c md5_broken.c bigcrypt.c \ + passverify.c +unix_chkpwd_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@ -DHELPER_COMPILE=\"unix_chkpwd\" +unix_chkpwd_LDFLAGS = @PIE_LDFLAGS@ +unix_chkpwd_LDADD = @LIBCRYPT@ @LIBSELINUX@ @LIBAUDIT@ + +unix_update_SOURCES = unix_update.c md5_good.c md5_broken.c bigcrypt.c \ + passverify.c +unix_update_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@ -DHELPER_COMPILE=\"unix_update\" +unix_update_LDFLAGS = @PIE_LDFLAGS@ +unix_update_LDADD = @LIBCRYPT@ @LIBSELINUX@ + +if ENABLE_REGENERATE_MAN +noinst_DATA = README +README: pam_unix.8.xml +-include $(top_srcdir)/Make.xml.rules +endif diff --git a/modules/pam_unix/Makefile.in b/modules/pam_unix/Makefile.in new file mode 100644 index 00000000..e87ceead --- /dev/null +++ b/modules/pam_unix/Makefile.in @@ -0,0 +1,1088 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 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, 2009, 2011 Thorsten Kukuk <kukuk@suse.de> +# + + + + +VPATH = @srcdir@ +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_LIBSELINUX_TRUE@am__append_1 = -D"WITH_SELINUX" +@HAVE_VERSIONING_TRUE@am__append_2 = -Wl,--version-script=$(srcdir)/../modules.map +sbin_PROGRAMS = unix_chkpwd$(EXEEXT) unix_update$(EXEEXT) +noinst_PROGRAMS = bigcrypt$(EXEEXT) +@STATIC_MODULES_TRUE@am__append_3 = pam_unix_static.c +subdir = modules/pam_unix +DIST_COMMON = README $(noinst_HEADERS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/japhar_grep_cflags.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/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)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +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__installdirs = "$(DESTDIR)$(securelibdir)" "$(DESTDIR)$(sbindir)" \ + "$(DESTDIR)$(man8dir)" +LTLIBRARIES = $(securelib_LTLIBRARIES) +am__DEPENDENCIES_1 = +pam_unix_la_DEPENDENCIES = $(top_builddir)/libpam/libpam.la \ + $(am__DEPENDENCIES_1) +am__pam_unix_la_SOURCES_DIST = bigcrypt.c pam_unix_acct.c \ + pam_unix_auth.c pam_unix_passwd.c pam_unix_sess.c support.c \ + passverify.c yppasswd_xdr.c md5_good.c md5_broken.c \ + pam_unix_static.c +@STATIC_MODULES_TRUE@am__objects_1 = pam_unix_static.lo +am_pam_unix_la_OBJECTS = bigcrypt.lo pam_unix_acct.lo pam_unix_auth.lo \ + pam_unix_passwd.lo pam_unix_sess.lo support.lo passverify.lo \ + yppasswd_xdr.lo md5_good.lo md5_broken.lo $(am__objects_1) +pam_unix_la_OBJECTS = $(am_pam_unix_la_OBJECTS) +pam_unix_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(pam_unix_la_LDFLAGS) $(LDFLAGS) -o $@ +PROGRAMS = $(noinst_PROGRAMS) $(sbin_PROGRAMS) +am_bigcrypt_OBJECTS = bigcrypt-bigcrypt.$(OBJEXT) \ + bigcrypt-bigcrypt_main.$(OBJEXT) +bigcrypt_OBJECTS = $(am_bigcrypt_OBJECTS) +bigcrypt_DEPENDENCIES = +bigcrypt_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(bigcrypt_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +am_unix_chkpwd_OBJECTS = unix_chkpwd-unix_chkpwd.$(OBJEXT) \ + unix_chkpwd-md5_good.$(OBJEXT) \ + unix_chkpwd-md5_broken.$(OBJEXT) \ + unix_chkpwd-bigcrypt.$(OBJEXT) \ + unix_chkpwd-passverify.$(OBJEXT) +unix_chkpwd_OBJECTS = $(am_unix_chkpwd_OBJECTS) +unix_chkpwd_DEPENDENCIES = +unix_chkpwd_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(unix_chkpwd_CFLAGS) \ + $(CFLAGS) $(unix_chkpwd_LDFLAGS) $(LDFLAGS) -o $@ +am_unix_update_OBJECTS = unix_update-unix_update.$(OBJEXT) \ + unix_update-md5_good.$(OBJEXT) \ + unix_update-md5_broken.$(OBJEXT) \ + unix_update-bigcrypt.$(OBJEXT) \ + unix_update-passverify.$(OBJEXT) +unix_update_OBJECTS = $(am_unix_update_OBJECTS) +unix_update_DEPENDENCIES = +unix_update_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(unix_update_CFLAGS) \ + $(CFLAGS) $(unix_update_LDFLAGS) $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(pam_unix_la_SOURCES) $(bigcrypt_SOURCES) \ + $(unix_chkpwd_SOURCES) $(unix_update_SOURCES) +DIST_SOURCES = $(am__pam_unix_la_SOURCES_DIST) $(bigcrypt_SOURCES) \ + $(unix_chkpwd_SOURCES) $(unix_update_SOURCES) +man8dir = $(mandir)/man8 +NROFF = nroff +MANS = $(man_MANS) +DATA = $(noinst_DATA) +HEADERS = $(noinst_HEADERS) +ETAGS = etags +CTAGS = ctags +am__tty_colors = \ +red=; grn=; lgn=; blu=; std= +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BROWSER = @BROWSER@ +BUILD_CFLAGS = @BUILD_CFLAGS@ +BUILD_LDFLAGS = @BUILD_LDFLAGS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FO2PDF = @FO2PDF@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GREP = @GREP@ +HAVE_KEY_MANAGEMENT = @HAVE_KEY_MANAGEMENT@ +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@ +LIBCRACK = @LIBCRACK@ +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@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NIS_CFLAGS = @NIS_CFLAGS@ +NIS_LIBS = @NIS_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +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@ +PIE_CFLAGS = @PIE_CFLAGS@ +PIE_LDFLAGS = @PIE_LDFLAGS@ +PKG_CONFIG = @PKG_CONFIG@ +POSUB = @POSUB@ +RANLIB = @RANLIB@ +SCONFIGDIR = @SCONFIGDIR@ +SECUREDIR = @SECUREDIR@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +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_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@ +libc_cv_fpie = @libc_cv_fpie@ +libdir = @libdir@ +libexecdir = @libexecdir@ +libtirpc_CFLAGS = @libtirpc_CFLAGS@ +libtirpc_LIBS = @libtirpc_LIBS@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pam_cv_ld_O1 = @pam_cv_ld_O1@ +pam_cv_ld_as_needed = @pam_cv_ld_as_needed@ +pam_cv_ld_no_undefined = @pam_cv_ld_no_undefined@ +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@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +CLEANFILES = *~ +MAINTAINERCLEANFILES = $(MANS) README +EXTRA_DIST = README md5.c md5_crypt.c lckpwdf.-c $(MANS) CHANGELOG \ + tst-pam_unix $(XMLS) + +man_MANS = pam_unix.8 unix_chkpwd.8 unix_update.8 +XMLS = README.xml pam_unix.8.xml unix_chkpwd.8.xml unix_update.8.xml +TESTS = tst-pam_unix +securelibdir = $(SECUREDIR) +secureconfdir = $(SCONFIGDIR) +AM_CFLAGS = -I$(top_srcdir)/libpam/include \ + -I$(top_srcdir)/libpamc/include \ + -DCHKPWD_HELPER=\"$(sbindir)/unix_chkpwd\" \ + -DUPDATE_HELPER=\"$(sbindir)/unix_update\" $(NIS_CFLAGS) \ + $(am__append_1) +pam_unix_la_LDFLAGS = -no-undefined -avoid-version -module \ + $(am__append_2) +pam_unix_la_LIBADD = $(top_builddir)/libpam/libpam.la \ + @LIBCRYPT@ @LIBSELINUX@ $(NIS_LIBS) + +securelib_LTLIBRARIES = pam_unix.la +noinst_HEADERS = md5.h support.h yppasswd.h bigcrypt.h passverify.h \ + pam_unix_static.h + +pam_unix_la_SOURCES = bigcrypt.c pam_unix_acct.c pam_unix_auth.c \ + pam_unix_passwd.c pam_unix_sess.c support.c passverify.c \ + yppasswd_xdr.c md5_good.c md5_broken.c $(am__append_3) +bigcrypt_SOURCES = bigcrypt.c bigcrypt_main.c +bigcrypt_CFLAGS = $(AM_CFLAGS) +bigcrypt_LDADD = @LIBCRYPT@ +unix_chkpwd_SOURCES = unix_chkpwd.c md5_good.c md5_broken.c bigcrypt.c \ + passverify.c + +unix_chkpwd_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@ -DHELPER_COMPILE=\"unix_chkpwd\" +unix_chkpwd_LDFLAGS = @PIE_LDFLAGS@ +unix_chkpwd_LDADD = @LIBCRYPT@ @LIBSELINUX@ @LIBAUDIT@ +unix_update_SOURCES = unix_update.c md5_good.c md5_broken.c bigcrypt.c \ + passverify.c + +unix_update_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@ -DHELPER_COMPILE=\"unix_update\" +unix_update_LDFLAGS = @PIE_LDFLAGS@ +unix_update_LDADD = @LIBCRYPT@ @LIBSELINUX@ +@ENABLE_REGENERATE_MAN_TRUE@noinst_DATA = README +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 modules/pam_unix/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu modules/pam_unix/Makefile +.PRECIOUS: 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__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + 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): +install-securelibLTLIBRARIES: $(securelib_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(securelibdir)" || $(MKDIR_P) "$(DESTDIR)$(securelibdir)" + @list='$(securelib_LTLIBRARIES)'; test -n "$(securelibdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(securelibdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(securelibdir)"; \ + } + +uninstall-securelibLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(securelib_LTLIBRARIES)'; test -n "$(securelibdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(securelibdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(securelibdir)/$$f"; \ + done + +clean-securelibLTLIBRARIES: + -test -z "$(securelib_LTLIBRARIES)" || rm -f $(securelib_LTLIBRARIES) + @list='$(securelib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +pam_unix.la: $(pam_unix_la_OBJECTS) $(pam_unix_la_DEPENDENCIES) + $(pam_unix_la_LINK) -rpath $(securelibdir) $(pam_unix_la_OBJECTS) $(pam_unix_la_LIBADD) $(LIBS) + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)" + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p || test -f $$p1; \ + then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(sbindir)" && rm -f $$files + +clean-sbinPROGRAMS: + @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +bigcrypt$(EXEEXT): $(bigcrypt_OBJECTS) $(bigcrypt_DEPENDENCIES) + @rm -f bigcrypt$(EXEEXT) + $(bigcrypt_LINK) $(bigcrypt_OBJECTS) $(bigcrypt_LDADD) $(LIBS) +unix_chkpwd$(EXEEXT): $(unix_chkpwd_OBJECTS) $(unix_chkpwd_DEPENDENCIES) + @rm -f unix_chkpwd$(EXEEXT) + $(unix_chkpwd_LINK) $(unix_chkpwd_OBJECTS) $(unix_chkpwd_LDADD) $(LIBS) +unix_update$(EXEEXT): $(unix_update_OBJECTS) $(unix_update_DEPENDENCIES) + @rm -f unix_update$(EXEEXT) + $(unix_update_LINK) $(unix_update_OBJECTS) $(unix_update_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bigcrypt-bigcrypt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bigcrypt-bigcrypt_main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bigcrypt.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5_broken.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5_good.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_unix_acct.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_unix_auth.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_unix_passwd.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_unix_sess.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_unix_static.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/passverify.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/support.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unix_chkpwd-bigcrypt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unix_chkpwd-md5_broken.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unix_chkpwd-md5_good.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unix_chkpwd-passverify.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unix_chkpwd-unix_chkpwd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unix_update-bigcrypt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unix_update-md5_broken.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unix_update-md5_good.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unix_update-passverify.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unix_update-unix_update.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/yppasswd_xdr.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +bigcrypt-bigcrypt.o: bigcrypt.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(bigcrypt_CFLAGS) $(CFLAGS) -MT bigcrypt-bigcrypt.o -MD -MP -MF $(DEPDIR)/bigcrypt-bigcrypt.Tpo -c -o bigcrypt-bigcrypt.o `test -f 'bigcrypt.c' || echo '$(srcdir)/'`bigcrypt.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/bigcrypt-bigcrypt.Tpo $(DEPDIR)/bigcrypt-bigcrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bigcrypt.c' object='bigcrypt-bigcrypt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(bigcrypt_CFLAGS) $(CFLAGS) -c -o bigcrypt-bigcrypt.o `test -f 'bigcrypt.c' || echo '$(srcdir)/'`bigcrypt.c + +bigcrypt-bigcrypt.obj: bigcrypt.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(bigcrypt_CFLAGS) $(CFLAGS) -MT bigcrypt-bigcrypt.obj -MD -MP -MF $(DEPDIR)/bigcrypt-bigcrypt.Tpo -c -o bigcrypt-bigcrypt.obj `if test -f 'bigcrypt.c'; then $(CYGPATH_W) 'bigcrypt.c'; else $(CYGPATH_W) '$(srcdir)/bigcrypt.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/bigcrypt-bigcrypt.Tpo $(DEPDIR)/bigcrypt-bigcrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bigcrypt.c' object='bigcrypt-bigcrypt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(bigcrypt_CFLAGS) $(CFLAGS) -c -o bigcrypt-bigcrypt.obj `if test -f 'bigcrypt.c'; then $(CYGPATH_W) 'bigcrypt.c'; else $(CYGPATH_W) '$(srcdir)/bigcrypt.c'; fi` + +bigcrypt-bigcrypt_main.o: bigcrypt_main.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(bigcrypt_CFLAGS) $(CFLAGS) -MT bigcrypt-bigcrypt_main.o -MD -MP -MF $(DEPDIR)/bigcrypt-bigcrypt_main.Tpo -c -o bigcrypt-bigcrypt_main.o `test -f 'bigcrypt_main.c' || echo '$(srcdir)/'`bigcrypt_main.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/bigcrypt-bigcrypt_main.Tpo $(DEPDIR)/bigcrypt-bigcrypt_main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bigcrypt_main.c' object='bigcrypt-bigcrypt_main.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(bigcrypt_CFLAGS) $(CFLAGS) -c -o bigcrypt-bigcrypt_main.o `test -f 'bigcrypt_main.c' || echo '$(srcdir)/'`bigcrypt_main.c + +bigcrypt-bigcrypt_main.obj: bigcrypt_main.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(bigcrypt_CFLAGS) $(CFLAGS) -MT bigcrypt-bigcrypt_main.obj -MD -MP -MF $(DEPDIR)/bigcrypt-bigcrypt_main.Tpo -c -o bigcrypt-bigcrypt_main.obj `if test -f 'bigcrypt_main.c'; then $(CYGPATH_W) 'bigcrypt_main.c'; else $(CYGPATH_W) '$(srcdir)/bigcrypt_main.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/bigcrypt-bigcrypt_main.Tpo $(DEPDIR)/bigcrypt-bigcrypt_main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bigcrypt_main.c' object='bigcrypt-bigcrypt_main.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(bigcrypt_CFLAGS) $(CFLAGS) -c -o bigcrypt-bigcrypt_main.obj `if test -f 'bigcrypt_main.c'; then $(CYGPATH_W) 'bigcrypt_main.c'; else $(CYGPATH_W) '$(srcdir)/bigcrypt_main.c'; fi` + +unix_chkpwd-unix_chkpwd.o: unix_chkpwd.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_chkpwd_CFLAGS) $(CFLAGS) -MT unix_chkpwd-unix_chkpwd.o -MD -MP -MF $(DEPDIR)/unix_chkpwd-unix_chkpwd.Tpo -c -o unix_chkpwd-unix_chkpwd.o `test -f 'unix_chkpwd.c' || echo '$(srcdir)/'`unix_chkpwd.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unix_chkpwd-unix_chkpwd.Tpo $(DEPDIR)/unix_chkpwd-unix_chkpwd.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='unix_chkpwd.c' object='unix_chkpwd-unix_chkpwd.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_chkpwd_CFLAGS) $(CFLAGS) -c -o unix_chkpwd-unix_chkpwd.o `test -f 'unix_chkpwd.c' || echo '$(srcdir)/'`unix_chkpwd.c + +unix_chkpwd-unix_chkpwd.obj: unix_chkpwd.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_chkpwd_CFLAGS) $(CFLAGS) -MT unix_chkpwd-unix_chkpwd.obj -MD -MP -MF $(DEPDIR)/unix_chkpwd-unix_chkpwd.Tpo -c -o unix_chkpwd-unix_chkpwd.obj `if test -f 'unix_chkpwd.c'; then $(CYGPATH_W) 'unix_chkpwd.c'; else $(CYGPATH_W) '$(srcdir)/unix_chkpwd.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unix_chkpwd-unix_chkpwd.Tpo $(DEPDIR)/unix_chkpwd-unix_chkpwd.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='unix_chkpwd.c' object='unix_chkpwd-unix_chkpwd.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_chkpwd_CFLAGS) $(CFLAGS) -c -o unix_chkpwd-unix_chkpwd.obj `if test -f 'unix_chkpwd.c'; then $(CYGPATH_W) 'unix_chkpwd.c'; else $(CYGPATH_W) '$(srcdir)/unix_chkpwd.c'; fi` + +unix_chkpwd-md5_good.o: md5_good.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_chkpwd_CFLAGS) $(CFLAGS) -MT unix_chkpwd-md5_good.o -MD -MP -MF $(DEPDIR)/unix_chkpwd-md5_good.Tpo -c -o unix_chkpwd-md5_good.o `test -f 'md5_good.c' || echo '$(srcdir)/'`md5_good.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unix_chkpwd-md5_good.Tpo $(DEPDIR)/unix_chkpwd-md5_good.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md5_good.c' object='unix_chkpwd-md5_good.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_chkpwd_CFLAGS) $(CFLAGS) -c -o unix_chkpwd-md5_good.o `test -f 'md5_good.c' || echo '$(srcdir)/'`md5_good.c + +unix_chkpwd-md5_good.obj: md5_good.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_chkpwd_CFLAGS) $(CFLAGS) -MT unix_chkpwd-md5_good.obj -MD -MP -MF $(DEPDIR)/unix_chkpwd-md5_good.Tpo -c -o unix_chkpwd-md5_good.obj `if test -f 'md5_good.c'; then $(CYGPATH_W) 'md5_good.c'; else $(CYGPATH_W) '$(srcdir)/md5_good.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unix_chkpwd-md5_good.Tpo $(DEPDIR)/unix_chkpwd-md5_good.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md5_good.c' object='unix_chkpwd-md5_good.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_chkpwd_CFLAGS) $(CFLAGS) -c -o unix_chkpwd-md5_good.obj `if test -f 'md5_good.c'; then $(CYGPATH_W) 'md5_good.c'; else $(CYGPATH_W) '$(srcdir)/md5_good.c'; fi` + +unix_chkpwd-md5_broken.o: md5_broken.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_chkpwd_CFLAGS) $(CFLAGS) -MT unix_chkpwd-md5_broken.o -MD -MP -MF $(DEPDIR)/unix_chkpwd-md5_broken.Tpo -c -o unix_chkpwd-md5_broken.o `test -f 'md5_broken.c' || echo '$(srcdir)/'`md5_broken.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unix_chkpwd-md5_broken.Tpo $(DEPDIR)/unix_chkpwd-md5_broken.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md5_broken.c' object='unix_chkpwd-md5_broken.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_chkpwd_CFLAGS) $(CFLAGS) -c -o unix_chkpwd-md5_broken.o `test -f 'md5_broken.c' || echo '$(srcdir)/'`md5_broken.c + +unix_chkpwd-md5_broken.obj: md5_broken.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_chkpwd_CFLAGS) $(CFLAGS) -MT unix_chkpwd-md5_broken.obj -MD -MP -MF $(DEPDIR)/unix_chkpwd-md5_broken.Tpo -c -o unix_chkpwd-md5_broken.obj `if test -f 'md5_broken.c'; then $(CYGPATH_W) 'md5_broken.c'; else $(CYGPATH_W) '$(srcdir)/md5_broken.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unix_chkpwd-md5_broken.Tpo $(DEPDIR)/unix_chkpwd-md5_broken.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md5_broken.c' object='unix_chkpwd-md5_broken.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_chkpwd_CFLAGS) $(CFLAGS) -c -o unix_chkpwd-md5_broken.obj `if test -f 'md5_broken.c'; then $(CYGPATH_W) 'md5_broken.c'; else $(CYGPATH_W) '$(srcdir)/md5_broken.c'; fi` + +unix_chkpwd-bigcrypt.o: bigcrypt.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_chkpwd_CFLAGS) $(CFLAGS) -MT unix_chkpwd-bigcrypt.o -MD -MP -MF $(DEPDIR)/unix_chkpwd-bigcrypt.Tpo -c -o unix_chkpwd-bigcrypt.o `test -f 'bigcrypt.c' || echo '$(srcdir)/'`bigcrypt.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unix_chkpwd-bigcrypt.Tpo $(DEPDIR)/unix_chkpwd-bigcrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bigcrypt.c' object='unix_chkpwd-bigcrypt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_chkpwd_CFLAGS) $(CFLAGS) -c -o unix_chkpwd-bigcrypt.o `test -f 'bigcrypt.c' || echo '$(srcdir)/'`bigcrypt.c + +unix_chkpwd-bigcrypt.obj: bigcrypt.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_chkpwd_CFLAGS) $(CFLAGS) -MT unix_chkpwd-bigcrypt.obj -MD -MP -MF $(DEPDIR)/unix_chkpwd-bigcrypt.Tpo -c -o unix_chkpwd-bigcrypt.obj `if test -f 'bigcrypt.c'; then $(CYGPATH_W) 'bigcrypt.c'; else $(CYGPATH_W) '$(srcdir)/bigcrypt.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unix_chkpwd-bigcrypt.Tpo $(DEPDIR)/unix_chkpwd-bigcrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bigcrypt.c' object='unix_chkpwd-bigcrypt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_chkpwd_CFLAGS) $(CFLAGS) -c -o unix_chkpwd-bigcrypt.obj `if test -f 'bigcrypt.c'; then $(CYGPATH_W) 'bigcrypt.c'; else $(CYGPATH_W) '$(srcdir)/bigcrypt.c'; fi` + +unix_chkpwd-passverify.o: passverify.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_chkpwd_CFLAGS) $(CFLAGS) -MT unix_chkpwd-passverify.o -MD -MP -MF $(DEPDIR)/unix_chkpwd-passverify.Tpo -c -o unix_chkpwd-passverify.o `test -f 'passverify.c' || echo '$(srcdir)/'`passverify.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unix_chkpwd-passverify.Tpo $(DEPDIR)/unix_chkpwd-passverify.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='passverify.c' object='unix_chkpwd-passverify.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_chkpwd_CFLAGS) $(CFLAGS) -c -o unix_chkpwd-passverify.o `test -f 'passverify.c' || echo '$(srcdir)/'`passverify.c + +unix_chkpwd-passverify.obj: passverify.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_chkpwd_CFLAGS) $(CFLAGS) -MT unix_chkpwd-passverify.obj -MD -MP -MF $(DEPDIR)/unix_chkpwd-passverify.Tpo -c -o unix_chkpwd-passverify.obj `if test -f 'passverify.c'; then $(CYGPATH_W) 'passverify.c'; else $(CYGPATH_W) '$(srcdir)/passverify.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unix_chkpwd-passverify.Tpo $(DEPDIR)/unix_chkpwd-passverify.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='passverify.c' object='unix_chkpwd-passverify.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_chkpwd_CFLAGS) $(CFLAGS) -c -o unix_chkpwd-passverify.obj `if test -f 'passverify.c'; then $(CYGPATH_W) 'passverify.c'; else $(CYGPATH_W) '$(srcdir)/passverify.c'; fi` + +unix_update-unix_update.o: unix_update.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_update_CFLAGS) $(CFLAGS) -MT unix_update-unix_update.o -MD -MP -MF $(DEPDIR)/unix_update-unix_update.Tpo -c -o unix_update-unix_update.o `test -f 'unix_update.c' || echo '$(srcdir)/'`unix_update.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unix_update-unix_update.Tpo $(DEPDIR)/unix_update-unix_update.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='unix_update.c' object='unix_update-unix_update.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_update_CFLAGS) $(CFLAGS) -c -o unix_update-unix_update.o `test -f 'unix_update.c' || echo '$(srcdir)/'`unix_update.c + +unix_update-unix_update.obj: unix_update.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_update_CFLAGS) $(CFLAGS) -MT unix_update-unix_update.obj -MD -MP -MF $(DEPDIR)/unix_update-unix_update.Tpo -c -o unix_update-unix_update.obj `if test -f 'unix_update.c'; then $(CYGPATH_W) 'unix_update.c'; else $(CYGPATH_W) '$(srcdir)/unix_update.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unix_update-unix_update.Tpo $(DEPDIR)/unix_update-unix_update.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='unix_update.c' object='unix_update-unix_update.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_update_CFLAGS) $(CFLAGS) -c -o unix_update-unix_update.obj `if test -f 'unix_update.c'; then $(CYGPATH_W) 'unix_update.c'; else $(CYGPATH_W) '$(srcdir)/unix_update.c'; fi` + +unix_update-md5_good.o: md5_good.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_update_CFLAGS) $(CFLAGS) -MT unix_update-md5_good.o -MD -MP -MF $(DEPDIR)/unix_update-md5_good.Tpo -c -o unix_update-md5_good.o `test -f 'md5_good.c' || echo '$(srcdir)/'`md5_good.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unix_update-md5_good.Tpo $(DEPDIR)/unix_update-md5_good.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md5_good.c' object='unix_update-md5_good.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_update_CFLAGS) $(CFLAGS) -c -o unix_update-md5_good.o `test -f 'md5_good.c' || echo '$(srcdir)/'`md5_good.c + +unix_update-md5_good.obj: md5_good.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_update_CFLAGS) $(CFLAGS) -MT unix_update-md5_good.obj -MD -MP -MF $(DEPDIR)/unix_update-md5_good.Tpo -c -o unix_update-md5_good.obj `if test -f 'md5_good.c'; then $(CYGPATH_W) 'md5_good.c'; else $(CYGPATH_W) '$(srcdir)/md5_good.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unix_update-md5_good.Tpo $(DEPDIR)/unix_update-md5_good.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md5_good.c' object='unix_update-md5_good.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_update_CFLAGS) $(CFLAGS) -c -o unix_update-md5_good.obj `if test -f 'md5_good.c'; then $(CYGPATH_W) 'md5_good.c'; else $(CYGPATH_W) '$(srcdir)/md5_good.c'; fi` + +unix_update-md5_broken.o: md5_broken.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_update_CFLAGS) $(CFLAGS) -MT unix_update-md5_broken.o -MD -MP -MF $(DEPDIR)/unix_update-md5_broken.Tpo -c -o unix_update-md5_broken.o `test -f 'md5_broken.c' || echo '$(srcdir)/'`md5_broken.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unix_update-md5_broken.Tpo $(DEPDIR)/unix_update-md5_broken.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md5_broken.c' object='unix_update-md5_broken.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_update_CFLAGS) $(CFLAGS) -c -o unix_update-md5_broken.o `test -f 'md5_broken.c' || echo '$(srcdir)/'`md5_broken.c + +unix_update-md5_broken.obj: md5_broken.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_update_CFLAGS) $(CFLAGS) -MT unix_update-md5_broken.obj -MD -MP -MF $(DEPDIR)/unix_update-md5_broken.Tpo -c -o unix_update-md5_broken.obj `if test -f 'md5_broken.c'; then $(CYGPATH_W) 'md5_broken.c'; else $(CYGPATH_W) '$(srcdir)/md5_broken.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unix_update-md5_broken.Tpo $(DEPDIR)/unix_update-md5_broken.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md5_broken.c' object='unix_update-md5_broken.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_update_CFLAGS) $(CFLAGS) -c -o unix_update-md5_broken.obj `if test -f 'md5_broken.c'; then $(CYGPATH_W) 'md5_broken.c'; else $(CYGPATH_W) '$(srcdir)/md5_broken.c'; fi` + +unix_update-bigcrypt.o: bigcrypt.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_update_CFLAGS) $(CFLAGS) -MT unix_update-bigcrypt.o -MD -MP -MF $(DEPDIR)/unix_update-bigcrypt.Tpo -c -o unix_update-bigcrypt.o `test -f 'bigcrypt.c' || echo '$(srcdir)/'`bigcrypt.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unix_update-bigcrypt.Tpo $(DEPDIR)/unix_update-bigcrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bigcrypt.c' object='unix_update-bigcrypt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_update_CFLAGS) $(CFLAGS) -c -o unix_update-bigcrypt.o `test -f 'bigcrypt.c' || echo '$(srcdir)/'`bigcrypt.c + +unix_update-bigcrypt.obj: bigcrypt.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_update_CFLAGS) $(CFLAGS) -MT unix_update-bigcrypt.obj -MD -MP -MF $(DEPDIR)/unix_update-bigcrypt.Tpo -c -o unix_update-bigcrypt.obj `if test -f 'bigcrypt.c'; then $(CYGPATH_W) 'bigcrypt.c'; else $(CYGPATH_W) '$(srcdir)/bigcrypt.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unix_update-bigcrypt.Tpo $(DEPDIR)/unix_update-bigcrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bigcrypt.c' object='unix_update-bigcrypt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_update_CFLAGS) $(CFLAGS) -c -o unix_update-bigcrypt.obj `if test -f 'bigcrypt.c'; then $(CYGPATH_W) 'bigcrypt.c'; else $(CYGPATH_W) '$(srcdir)/bigcrypt.c'; fi` + +unix_update-passverify.o: passverify.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_update_CFLAGS) $(CFLAGS) -MT unix_update-passverify.o -MD -MP -MF $(DEPDIR)/unix_update-passverify.Tpo -c -o unix_update-passverify.o `test -f 'passverify.c' || echo '$(srcdir)/'`passverify.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unix_update-passverify.Tpo $(DEPDIR)/unix_update-passverify.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='passverify.c' object='unix_update-passverify.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_update_CFLAGS) $(CFLAGS) -c -o unix_update-passverify.o `test -f 'passverify.c' || echo '$(srcdir)/'`passverify.c + +unix_update-passverify.obj: passverify.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_update_CFLAGS) $(CFLAGS) -MT unix_update-passverify.obj -MD -MP -MF $(DEPDIR)/unix_update-passverify.Tpo -c -o unix_update-passverify.obj `if test -f 'passverify.c'; then $(CYGPATH_W) 'passverify.c'; else $(CYGPATH_W) '$(srcdir)/passverify.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unix_update-passverify.Tpo $(DEPDIR)/unix_update-passverify.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='passverify.c' object='unix_update-passverify.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unix_update_CFLAGS) $(CFLAGS) -c -o unix_update-passverify.obj `if test -f 'passverify.c'; then $(CYGPATH_W) 'passverify.c'; else $(CYGPATH_W) '$(srcdir)/passverify.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-man8: $(man_MANS) + @$(NORMAL_INSTALL) + test -z "$(man8dir)" || $(MKDIR_P) "$(DESTDIR)$(man8dir)" + @list=''; test -n "$(man8dir)" || exit 0; \ + { for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.8[a-z]*$$/p'; \ + } | while read p; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; echo "$$p"; \ + done | \ + sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ + sed 'N;N;s,\n, ,g' | { \ + list=; while read file base inst; do \ + if test "$$base" = "$$inst"; then list="$$list $$file"; else \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \ + fi; \ + done; \ + for i in $$list; do echo "$$i"; done | $(am__base_list) | \ + while read files; do \ + test -z "$$files" || { \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \ + done; } + +uninstall-man8: + @$(NORMAL_UNINSTALL) + @list=''; test -n "$(man8dir)" || exit 0; \ + files=`{ for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.8[a-z]*$$/p'; \ + } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ + test -z "$$files" || { \ + echo " ( cd '$(DESTDIR)$(man8dir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(man8dir)" && rm -f $$files; } + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + 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 +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + 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" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + echo "$$grn$$dashes"; \ + else \ + echo "$$red$$dashes"; \ + fi; \ + echo "$$banner"; \ + test -z "$$skipped" || echo "$$skipped"; \ + test -z "$$report" || echo "$$report"; \ + echo "$$dashes$$std"; \ + test "$$failed" -eq 0; \ + else :; fi + +distdir: $(DISTFILES) + @list='$(MANS)'; if test -n "$$list"; then \ + list=`for p in $$list; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; else :; fi; done`; \ + if test -n "$$list" && \ + grep 'ab help2man is required to generate this page' $$list >/dev/null; then \ + echo "error: found man pages containing the \`missing help2man' replacement text:" >&2; \ + grep -l 'ab help2man is required to generate this page' $$list | sed 's/^/ /' >&2; \ + echo " to fix them, install help2man, remove and regenerate the man pages;" >&2; \ + echo " typically \`make maintainer-clean' will remove them" >&2; \ + exit 1; \ + else :; fi; \ + else :; fi + @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 + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-am +all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(MANS) $(DATA) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(securelibdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; 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: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +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." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ + clean-sbinPROGRAMS clean-securelibLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -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-man install-securelibLTLIBRARIES + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-sbinPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: install-man8 + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -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-man uninstall-sbinPROGRAMS \ + uninstall-securelibLTLIBRARIES + +uninstall-man: uninstall-man8 + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-TESTS check-am clean \ + clean-generic clean-libtool clean-noinstPROGRAMS \ + clean-sbinPROGRAMS clean-securelibLTLIBRARIES ctags 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-info install-info-am install-man \ + install-man8 install-pdf install-pdf-am install-ps \ + install-ps-am install-sbinPROGRAMS \ + install-securelibLTLIBRARIES 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 uninstall uninstall-am uninstall-man uninstall-man8 \ + uninstall-sbinPROGRAMS uninstall-securelibLTLIBRARIES + +@ENABLE_REGENERATE_MAN_TRUE@README: pam_unix.8.xml +@ENABLE_REGENERATE_MAN_TRUE@-include $(top_srcdir)/Make.xml.rules + +# 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/modules/pam_unix/README b/modules/pam_unix/README new file mode 100644 index 00000000..26c06e23 --- /dev/null +++ b/modules/pam_unix/README @@ -0,0 +1,172 @@ +pam_unix — Module for traditional password authentication + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +DESCRIPTION + +This is the standard Unix authentication module. It uses standard calls from +the system's libraries to retrieve and set account information as well as +authentication. Usually this is obtained from the /etc/passwd and the /etc/ +shadow file as well if shadow is enabled. + +The account component performs the task of establishing the status of the +user's account and password based on the following shadow elements: expire, +last_change, max_change, min_change, warn_change. In the case of the latter, it +may offer advice to the user on changing their password or, through the +PAM_AUTHTOKEN_REQD return, delay giving service to the user until they have +established a new password. The entries listed above are documented in the +shadow(5) manual page. Should the user's record not contain one or more of +these entries, the corresponding shadow check is not performed. + +The authentication component performs the task of checking the users +credentials (password). The default action of this module is to not permit the +user access to a service if their official password is blank. + +A helper binary, unix_chkpwd(8), is provided to check the user's password when +it is stored in a read protected database. This binary is very simple and will +only check the password of the user invoking it. It is called transparently on +behalf of the user by the authenticating component of this module. In this way +it is possible for applications like xlock(1) to work without being +setuid-root. The module, by default, will temporarily turn off SIGCHLD handling +for the duration of execution of the helper binary. This is generally the right +thing to do, as many applications are not prepared to handle this signal from a +child they didn't know was fork()d. The noreap module argument can be used to +suppress this temporary shielding and may be needed for use with certain +applications. + +The password component of this module performs the task of updating the user's +password. The default encryption hash is taken from the ENCRYPT_METHOD variable +from /etc/login.defs + +The session component of this module logs when a user logins or leave the +system. + +Remaining arguments, supported by others functions of this module, are silently +ignored. Other arguments are logged as errors through syslog(3). + +OPTIONS + +debug + + Turns on debugging via syslog(3). + +audit + + A little more extreme than debug. + +nullok + + The default action of this module is to not permit the user access to a + service if their official password is blank. The nullok argument overrides + this default. + +try_first_pass + + Before prompting the user for their password, the module first tries the + previous stacked module's password in case that satisfies this module as + well. + +use_first_pass + + The argument use_first_pass forces the module to use a previous stacked + modules password and will never prompt the user - if no password is + available or the password is not appropriate, the user will be denied + access. + +nodelay + + This argument can be used to discourage the authentication component from + requesting a delay should the authentication as a whole fail. The default + action is for the module to request a delay-on-failure of the order of two + second. + +use_authtok + + When password changing enforce the module to set the new password to the + one provided by a previously stacked password module (this is used in the + example of the stacking of the pam_cracklib module documented below). + +not_set_pass + + This argument is used to inform the module that it is not to pay attention + to/make available the old or new passwords from/to other (stacked) password + modules. + +nis + + NIS RPC is used for setting new passwords. + +remember=n + + The last n passwords for each user are saved in /etc/security/opasswd in + order to force password change history and keep the user from alternating + between the same password too frequently. Instead of this option the + pam_pwhistory module should be used. + +shadow + + Try to maintain a shadow based system. + +md5 + + When a user changes their password next, encrypt it with the MD5 algorithm. + +bigcrypt + + When a user changes their password next, encrypt it with the DEC C2 + algorithm. + +sha256 + + When a user changes their password next, encrypt it with the SHA256 + algorithm. If the SHA256 algorithm is not known to the crypt(3) function, + fall back to MD5. + +sha512 + + When a user changes their password next, encrypt it with the SHA512 + algorithm. If the SHA512 algorithm is not known to the crypt(3) function, + fall back to MD5. + +blowfish + + When a user changes their password next, encrypt it with the blowfish + algorithm. If the blowfish algorithm is not known to the crypt(3) function, + fall back to MD5. + +rounds=n + + Set the optional number of rounds of the SHA256, SHA512 and blowfish + password hashing algorithms to n. + +broken_shadow + + Ignore errors reading shadow information for users in the account + management module. + +minlen=n + + Set a minimum password length of n characters. The max. for DES crypt based + passwords are 8 characters. + +Invalid arguments are logged with syslog(3). + +EXAMPLES + +An example usage for /etc/pam.d/login would be: + +# Authenticate the user +auth required pam_unix.so +# Ensure users account and password are still active +account required pam_unix.so +# Change the users password, but at first check the strength +# with pam_cracklib(8) +password required pam_cracklib.so retry=3 minlen=6 difok=3 +password required pam_unix.so use_authtok nullok md5 +session required pam_unix.so + + +AUTHOR + +pam_unix was written by various people. + diff --git a/modules/pam_unix/README.xml b/modules/pam_unix/README.xml new file mode 100644 index 00000000..7fd340b3 --- /dev/null +++ b/modules/pam_unix/README.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding='UTF-8'?> +<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN" +"http://www.docbook.org/xml/4.3/docbookx.dtd" +[ +<!-- +<!ENTITY pamaccess SYSTEM "pam_unix.8.xml"> +--> +]> + +<article> + + <articleinfo> + + <title> + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" + href="pam_unix.8.xml" xpointer='xpointer(//refnamediv[@id = "pam_unix-name"]/*)'/> + </title> + + </articleinfo> + + <section> + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" + href="pam_unix.8.xml" xpointer='xpointer(//refsect1[@id = "pam_unix-description"]/*)'/> + </section> + + <section> + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" + href="pam_unix.8.xml" xpointer='xpointer(//refsect1[@id = "pam_unix-options"]/*)'/> + </section> + + <section> + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" + href="pam_unix.8.xml" xpointer='xpointer(//refsect1[@id = "pam_unix-examples"]/*)'/> + </section> + + <section> + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" + href="pam_unix.8.xml" xpointer='xpointer(//refsect1[@id = "pam_unix-author"]/*)'/> + </section> + +</article> diff --git a/modules/pam_unix/bigcrypt.c b/modules/pam_unix/bigcrypt.c new file mode 100644 index 00000000..e1d57a07 --- /dev/null +++ b/modules/pam_unix/bigcrypt.c @@ -0,0 +1,159 @@ +/* + * This function implements the "bigcrypt" algorithm specifically for + * Linux-PAM. + * + * This algorithm is algorithm 0 (default) shipped with the C2 secure + * implementation of Digital UNIX. + * + * Disclaimer: This work is not based on the source code to Digital + * UNIX, nor am I connected to Digital Equipment Corp, in any way + * other than as a customer. This code is based on published + * interfaces and reasonable guesswork. + * + * Description: The cleartext is divided into blocks of SEGMENT_SIZE=8 + * characters or less. Each block is encrypted using the standard UNIX + * libc crypt function. The result of the encryption for one block + * provides the salt for the suceeding block. + * + * Restrictions: The buffer used to hold the encrypted result is + * statically allocated. (see MAX_PASS_LEN below). This is necessary, + * as the returned pointer points to "static data that are overwritten + * by each call", (XPG3: XSI System Interface + Headers pg 109), and + * this is a drop in replacement for crypt(); + * + * Andy Phillips <atp@mssl.ucl.ac.uk> + */ + +#include "config.h" + +#include <string.h> +#include <stdlib.h> +#include <security/_pam_macros.h> +#ifdef HAVE_LIBXCRYPT +#include <xcrypt.h> +#elif defined(HAVE_CRYPT_H) +#include <crypt.h> +#endif + +#include "bigcrypt.h" + +/* + * Max cleartext password length in segments of 8 characters this + * function can deal with (16 segments of 8 chars= max 128 character + * password). + */ + +#define MAX_PASS_LEN 16 +#define SEGMENT_SIZE 8 +#define SALT_SIZE 2 +#define KEYBUF_SIZE ((MAX_PASS_LEN*SEGMENT_SIZE)+SALT_SIZE) +#define ESEGMENT_SIZE 11 +#define CBUF_SIZE ((MAX_PASS_LEN*ESEGMENT_SIZE)+SALT_SIZE+1) + +char *bigcrypt(const char *key, const char *salt) +{ + char *dec_c2_cryptbuf; +#ifdef HAVE_CRYPT_R + struct crypt_data *cdata; +#endif + unsigned long int keylen, n_seg, j; + char *cipher_ptr, *plaintext_ptr, *tmp_ptr, *salt_ptr; + char keybuf[KEYBUF_SIZE + 1]; + + D(("called with key='%s', salt='%s'.", key, salt)); + + /* reset arrays */ + dec_c2_cryptbuf = malloc(CBUF_SIZE); + if (!dec_c2_cryptbuf) { + return NULL; + } +#ifdef HAVE_CRYPT_R + cdata = malloc(sizeof(*cdata)); + if(!cdata) { + free(dec_c2_cryptbuf); + return NULL; + } + cdata->initialized = 0; +#endif + memset(keybuf, 0, KEYBUF_SIZE + 1); + memset(dec_c2_cryptbuf, 0, CBUF_SIZE); + + /* fill KEYBUF_SIZE with key */ + strncpy(keybuf, key, KEYBUF_SIZE); + + /* deal with case that we are doing a password check for a + conventially encrypted password: the salt will be + SALT_SIZE+ESEGMENT_SIZE long. */ + if (strlen(salt) == (SALT_SIZE + ESEGMENT_SIZE)) + keybuf[SEGMENT_SIZE] = '\0'; /* terminate password early(?) */ + + keylen = strlen(keybuf); + + if (!keylen) { + n_seg = 1; + } else { + /* work out how many segments */ + n_seg = 1 + ((keylen - 1) / SEGMENT_SIZE); + } + + if (n_seg > MAX_PASS_LEN) + n_seg = MAX_PASS_LEN; /* truncate at max length */ + + /* set up some pointers */ + cipher_ptr = dec_c2_cryptbuf; + plaintext_ptr = keybuf; + + /* do the first block with supplied salt */ +#ifdef HAVE_CRYPT_R + tmp_ptr = crypt_r(plaintext_ptr, salt, cdata); /* libc crypt_r() */ +#else + tmp_ptr = crypt(plaintext_ptr, salt); /* libc crypt() */ +#endif + if (tmp_ptr == NULL) { + free(dec_c2_cryptbuf); + return NULL; + } + /* and place in the static area */ + strncpy(cipher_ptr, tmp_ptr, 13); + cipher_ptr += ESEGMENT_SIZE + SALT_SIZE; + plaintext_ptr += SEGMENT_SIZE; /* first block of SEGMENT_SIZE */ + + /* change the salt (1st 2 chars of previous block) - this was found + by dowsing */ + + salt_ptr = cipher_ptr - ESEGMENT_SIZE; + + /* so far this is identical to "return crypt(key, salt);", if + there is more than one block encrypt them... */ + + if (n_seg > 1) { + for (j = 2; j <= n_seg; j++) { + +#ifdef HAVE_CRYPT_R + tmp_ptr = crypt_r(plaintext_ptr, salt_ptr, cdata); +#else + tmp_ptr = crypt(plaintext_ptr, salt_ptr); +#endif + if (tmp_ptr == NULL) { + _pam_overwrite(dec_c2_cryptbuf); + free(dec_c2_cryptbuf); + return NULL; + } + + /* skip the salt for seg!=0 */ + strncpy(cipher_ptr, (tmp_ptr + SALT_SIZE), ESEGMENT_SIZE); + + cipher_ptr += ESEGMENT_SIZE; + plaintext_ptr += SEGMENT_SIZE; + salt_ptr = cipher_ptr - ESEGMENT_SIZE; + } + } + D(("key=|%s|, salt=|%s|\nbuf=|%s|\n", key, salt, dec_c2_cryptbuf)); + +#ifdef HAVE_CRYPT_R + free(cdata); +#endif + + /* this is the <NUL> terminated encrypted password */ + return dec_c2_cryptbuf; +} diff --git a/modules/pam_unix/bigcrypt.h b/modules/pam_unix/bigcrypt.h new file mode 100644 index 00000000..a66a96e6 --- /dev/null +++ b/modules/pam_unix/bigcrypt.h @@ -0,0 +1 @@ +extern char *bigcrypt(const char *key, const char *salt); diff --git a/modules/pam_unix/bigcrypt_main.c b/modules/pam_unix/bigcrypt_main.c new file mode 100644 index 00000000..fab212d9 --- /dev/null +++ b/modules/pam_unix/bigcrypt_main.c @@ -0,0 +1,18 @@ +#include <stdio.h> +#include <string.h> + +#include "bigcrypt.h" + +int +main(int argc, char **argv) +{ + if (argc < 3) { + fprintf(stderr, "Usage: %s password salt\n", + strchr(argv[0], '/') ? + (strchr(argv[0], '/') + 1) : + argv[0]); + return 0; + } + fprintf(stdout, "%s\n", bigcrypt(argv[1], argv[2])); + return 0; +} diff --git a/modules/pam_unix/lckpwdf.-c b/modules/pam_unix/lckpwdf.-c new file mode 100644 index 00000000..7145617e --- /dev/null +++ b/modules/pam_unix/lckpwdf.-c @@ -0,0 +1,142 @@ +/* + * This is a hack, but until libc and glibc both include this function + * by default (libc only includes it if nys is not being used, at the + * moment, and glibc doesn't appear to have it at all) we need to have + * it here, too. :-( + * + * This should not become an official part of PAM. + * + * BEGIN_HACK + */ + +/* + * lckpwdf.c -- prevent simultaneous updates of password files + * + * Before modifying any of the password files, call lckpwdf(). It may block + * for up to 15 seconds trying to get the lock. Return value is 0 on success + * or -1 on failure. When you are done, call ulckpwdf() to release the lock. + * The lock is also released automatically when the process exits. Only one + * process at a time may hold the lock. + * + * These functions are supposed to be conformant with AT&T SVID Issue 3. + * + * Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>, + * public domain. + */ + +#include <fcntl.h> +#include <signal.h> +#ifdef WITH_SELINUX +#include <selinux/selinux.h> +#endif + +#define LOCKFILE "/etc/.pwd.lock" +#define TIMEOUT 15 + +static int lockfd = -1; + +static int set_close_on_exec(int fd) +{ + int flags = fcntl(fd, F_GETFD, 0); + if (flags == -1) + return -1; + flags |= FD_CLOEXEC; + return fcntl(fd, F_SETFD, flags); +} + +static int do_lock(int fd) +{ + struct flock fl; + + memset(&fl, 0, sizeof fl); + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + return fcntl(fd, F_SETLKW, &fl); +} + +static void alarm_catch(int sig) +{ +/* does nothing, but fcntl F_SETLKW will fail with EINTR */ +} + +static int lckpwdf(void) +{ + struct sigaction act, oldact; + sigset_t set, oldset; + + if (lockfd != -1) + return -1; + +#ifdef WITH_SELINUX + if(is_selinux_enabled()>0) + { + lockfd = open(LOCKFILE, O_WRONLY); + if(lockfd == -1 && errno == ENOENT) + { + security_context_t create_context; + int rc; + + if(getfilecon("/etc/passwd", &create_context)) + return -1; + rc = setfscreatecon(create_context); + freecon(create_context); + if(rc) + return -1; + lockfd = open(LOCKFILE, O_CREAT | O_WRONLY, 0600); + if(setfscreatecon(NULL)) + return -1; + } + } + else +#endif + lockfd = open(LOCKFILE, O_CREAT | O_WRONLY, 0600); + if (lockfd == -1) + return -1; + if (set_close_on_exec(lockfd) == -1) + goto cleanup_fd; + + memset(&act, 0, sizeof act); + act.sa_handler = alarm_catch; + act.sa_flags = 0; + sigfillset(&act.sa_mask); + if (sigaction(SIGALRM, &act, &oldact) == -1) + goto cleanup_fd; + + sigemptyset(&set); + sigaddset(&set, SIGALRM); + if (sigprocmask(SIG_UNBLOCK, &set, &oldset) == -1) + goto cleanup_sig; + + alarm(TIMEOUT); + if (do_lock(lockfd) == -1) + goto cleanup_alarm; + alarm(0); + sigprocmask(SIG_SETMASK, &oldset, NULL); + sigaction(SIGALRM, &oldact, NULL); + return 0; + + cleanup_alarm: + alarm(0); + sigprocmask(SIG_SETMASK, &oldset, NULL); + cleanup_sig: + sigaction(SIGALRM, &oldact, NULL); + cleanup_fd: + close(lockfd); + lockfd = -1; + return -1; +} + +static int ulckpwdf(void) +{ + unlink(LOCKFILE); + if (lockfd == -1) + return -1; + + if (close(lockfd) == -1) { + lockfd = -1; + return -1; + } + lockfd = -1; + return 0; +} +/* END_HACK */ diff --git a/modules/pam_unix/md5.c b/modules/pam_unix/md5.c new file mode 100644 index 00000000..94f0485b --- /dev/null +++ b/modules/pam_unix/md5.c @@ -0,0 +1,255 @@ +/* + * $Id$ + * + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + */ + +#include <string.h> +#include "md5.h" + +#ifndef HIGHFIRST +#define byteReverse(buf, len) /* Nothing */ +#else +static void byteReverse(unsigned char *buf, unsigned longs); + +#ifndef ASM_MD5 +/* + * Note: this code is harmless on little-endian machines. + */ +static void byteReverse(unsigned char *buf, unsigned longs) +{ + uint32 t; + do { + t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(uint32 *) buf = t; + buf += 4; + } while (--longs); +} +#endif +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Name(MD5Init)(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301U; + ctx->buf[1] = 0xefcdab89U; + ctx->buf[2] = 0x98badcfeU; + ctx->buf[3] = 0x10325476U; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Name(MD5Update)(struct MD5Context *ctx, unsigned const char *buf, unsigned len) +{ + uint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Name(MD5Final)(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + memcpy((uint32 *)ctx->in + 14, ctx->bits, 2*sizeof(uint32)); + + MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void MD5Name(MD5Transform)(uint32 buf[4], uint32 const in[16]) +{ + register uint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478U, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756U, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070dbU, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceeeU, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0fafU, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62aU, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613U, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501U, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8U, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7afU, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1U, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7beU, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122U, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193U, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438eU, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821U, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562U, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340U, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51U, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aaU, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105dU, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453U, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681U, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8U, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6U, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6U, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87U, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14edU, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905U, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8U, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9U, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8aU, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942U, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681U, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122U, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380cU, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44U, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9U, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60U, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70U, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6U, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127faU, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085U, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05U, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039U, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5U, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8U, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665U, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244U, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97U, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7U, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039U, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3U, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92U, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47dU, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1U, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4fU, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0U, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314U, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1U, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82U, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235U, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bbU, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391U, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#endif diff --git a/modules/pam_unix/md5.h b/modules/pam_unix/md5.h new file mode 100644 index 00000000..103f168a --- /dev/null +++ b/modules/pam_unix/md5.h @@ -0,0 +1,31 @@ + +#ifndef MD5_H +#define MD5_H + +typedef unsigned int uint32; + +struct MD5Context { + uint32 buf[4]; + uint32 bits[2]; + unsigned char in[64]; +}; + +void GoodMD5Init(struct MD5Context *); +void GoodMD5Update(struct MD5Context *, unsigned const char *, unsigned); +void GoodMD5Final(unsigned char digest[16], struct MD5Context *); +void GoodMD5Transform(uint32 buf[4], uint32 const in[16]); +void BrokenMD5Init(struct MD5Context *); +void BrokenMD5Update(struct MD5Context *, unsigned const char *, unsigned); +void BrokenMD5Final(unsigned char digest[16], struct MD5Context *); +void BrokenMD5Transform(uint32 buf[4], uint32 const in[16]); + +char *Goodcrypt_md5(const char *pw, const char *salt); +char *Brokencrypt_md5(const char *pw, const char *salt); + +/* + * This is needed to make RSAREF happy on some MS-DOS compilers. + */ + +typedef struct MD5Context MD5_CTX; + +#endif /* MD5_H */ diff --git a/modules/pam_unix/md5_broken.c b/modules/pam_unix/md5_broken.c new file mode 100644 index 00000000..193daebb --- /dev/null +++ b/modules/pam_unix/md5_broken.c @@ -0,0 +1,4 @@ +#define MD5Name(x) Broken##x + +#include "md5.c" +#include "md5_crypt.c" diff --git a/modules/pam_unix/md5_crypt.c b/modules/pam_unix/md5_crypt.c new file mode 100644 index 00000000..53972fcc --- /dev/null +++ b/modules/pam_unix/md5_crypt.c @@ -0,0 +1,154 @@ +/* + * $Id$ + * + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * ---------------------------------------------------------------------------- + * + * Origin: Id: crypt.c,v 1.3 1995/05/30 05:42:22 rgrimes Exp + * + */ + +#include <string.h> +#include <stdlib.h> +#include "md5.h" + +static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ +"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static void to64(char *s, unsigned long v, int n) +{ + while (--n >= 0) { + *s++ = itoa64[v & 0x3f]; + v >>= 6; + } +} + +/* + * UNIX password + * + * Use MD5 for what it is best at... + */ + +char *MD5Name(crypt_md5)(const char *pw, const char *salt) +{ + const char *magic = "$1$"; + /* This string is magic for this algorithm. Having + * it this way, we can get get better later on */ + char *passwd, *p; + const char *sp, *ep; + unsigned char final[16]; + int sl, pl, i, j; + MD5_CTX ctx, ctx1; + unsigned long l; + + /* Refine the Salt first */ + sp = salt; + + /* TODO: now that we're using malloc'ed memory, get rid of the + strange constant buffer size. */ + passwd = malloc(120); + + /* If it starts with the magic string, then skip that */ + if (!strncmp(sp, magic, strlen(magic))) + sp += strlen(magic); + + /* It stops at the first '$', max 8 chars */ + for (ep = sp; *ep && *ep != '$' && ep < (sp + 8); ep++) + continue; + + /* get the length of the true salt */ + sl = ep - sp; + + MD5Name(MD5Init)(&ctx); + + /* The password first, since that is what is most unknown */ + MD5Name(MD5Update)(&ctx,(unsigned const char *)pw,strlen(pw)); + + /* Then our magic string */ + MD5Name(MD5Update)(&ctx,(unsigned const char *)magic,strlen(magic)); + + /* Then the raw salt */ + MD5Name(MD5Update)(&ctx,(unsigned const char *)sp,sl); + + /* Then just as many characters of the MD5(pw,salt,pw) */ + MD5Name(MD5Init)(&ctx1); + MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw)); + MD5Name(MD5Update)(&ctx1,(unsigned const char *)sp,sl); + MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw)); + MD5Name(MD5Final)(final,&ctx1); + for (pl = strlen(pw); pl > 0; pl -= 16) + MD5Name(MD5Update)(&ctx,(unsigned const char *)final,pl>16 ? 16 : pl); + + /* Don't leave anything around in vm they could use. */ + memset(final, 0, sizeof final); + + /* Then something really weird... */ + for (j = 0, i = strlen(pw); i; i >>= 1) + if (i & 1) + MD5Name(MD5Update)(&ctx, (unsigned const char *)final+j, 1); + else + MD5Name(MD5Update)(&ctx, (unsigned const char *)pw+j, 1); + + /* Now make the output string */ + strcpy(passwd, magic); + strncat(passwd, sp, sl); + strcat(passwd, "$"); + + MD5Name(MD5Final)(final,&ctx); + + /* + * and now, just to make sure things don't run too fast + * On a 60 Mhz Pentium this takes 34 msec, so you would + * need 30 seconds to build a 1000 entry dictionary... + */ + for (i = 0; i < 1000; i++) { + MD5Name(MD5Init)(&ctx1); + if (i & 1) + MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw)); + else + MD5Name(MD5Update)(&ctx1,(unsigned const char *)final,16); + + if (i % 3) + MD5Name(MD5Update)(&ctx1,(unsigned const char *)sp,sl); + + if (i % 7) + MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw)); + + if (i & 1) + MD5Name(MD5Update)(&ctx1,(unsigned const char *)final,16); + else + MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw)); + MD5Name(MD5Final)(final,&ctx1); + } + + p = passwd + strlen(passwd); + + l = (final[0] << 16) | (final[6] << 8) | final[12]; + to64(p, l, 4); + p += 4; + l = (final[1] << 16) | (final[7] << 8) | final[13]; + to64(p, l, 4); + p += 4; + l = (final[2] << 16) | (final[8] << 8) | final[14]; + to64(p, l, 4); + p += 4; + l = (final[3] << 16) | (final[9] << 8) | final[15]; + to64(p, l, 4); + p += 4; + l = (final[4] << 16) | (final[10] << 8) | final[5]; + to64(p, l, 4); + p += 4; + l = final[11]; + to64(p, l, 2); + p += 2; + *p = '\0'; + + /* Don't leave anything around in vm they could use. */ + memset(final, 0, sizeof final); + + return passwd; +} diff --git a/modules/pam_unix/md5_good.c b/modules/pam_unix/md5_good.c new file mode 100644 index 00000000..131e4516 --- /dev/null +++ b/modules/pam_unix/md5_good.c @@ -0,0 +1,5 @@ +#define HIGHFIRST +#define MD5Name(x) Good##x + +#include "md5.c" +#include "md5_crypt.c" diff --git a/modules/pam_unix/pam_unix.8 b/modules/pam_unix/pam_unix.8 new file mode 100644 index 00000000..59d5f173 --- /dev/null +++ b/modules/pam_unix/pam_unix.8 @@ -0,0 +1,237 @@ +'\" t +.\" Title: pam_unix +.\" Author: [see the "AUTHOR" section] +.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/> +.\" Date: 09/19/2013 +.\" Manual: Linux-PAM Manual +.\" Source: Linux-PAM Manual +.\" Language: English +.\" +.TH "PAM_UNIX" "8" "09/19/2013" "Linux-PAM Manual" "Linux\-PAM Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +pam_unix \- Module for traditional password authentication +.SH "SYNOPSIS" +.HP \w'\fBpam_unix\&.so\fR\ 'u +\fBpam_unix\&.so\fR [\&.\&.\&.] +.SH "DESCRIPTION" +.PP +This is the standard Unix authentication module\&. It uses standard calls from the system\*(Aqs libraries to retrieve and set account information as well as authentication\&. Usually this is obtained from the /etc/passwd and the /etc/shadow file as well if shadow is enabled\&. +.PP +The account component performs the task of establishing the status of the user\*(Aqs account and password based on the following +\fIshadow\fR +elements: expire, last_change, max_change, min_change, warn_change\&. In the case of the latter, it may offer advice to the user on changing their password or, through the +\fBPAM_AUTHTOKEN_REQD\fR +return, delay giving service to the user until they have established a new password\&. The entries listed above are documented in the +\fBshadow\fR(5) +manual page\&. Should the user\*(Aqs record not contain one or more of these entries, the corresponding +\fIshadow\fR +check is not performed\&. +.PP +The authentication component performs the task of checking the users credentials (password)\&. The default action of this module is to not permit the user access to a service if their official password is blank\&. +.PP +A helper binary, +\fBunix_chkpwd\fR(8), is provided to check the user\*(Aqs password when it is stored in a read protected database\&. This binary is very simple and will only check the password of the user invoking it\&. It is called transparently on behalf of the user by the authenticating component of this module\&. In this way it is possible for applications like +\fBxlock\fR(1) +to work without being setuid\-root\&. The module, by default, will temporarily turn off SIGCHLD handling for the duration of execution of the helper binary\&. This is generally the right thing to do, as many applications are not prepared to handle this signal from a child they didn\*(Aqt know was +\fBfork()\fRd\&. The +\fBnoreap\fR +module argument can be used to suppress this temporary shielding and may be needed for use with certain applications\&. +.PP +The password component of this module performs the task of updating the user\*(Aqs password\&. The default encryption hash is taken from the +\fBENCRYPT_METHOD\fR +variable from +\fI/etc/login\&.defs\fR +.PP +The session component of this module logs when a user logins or leave the system\&. +.PP +Remaining arguments, supported by others functions of this module, are silently ignored\&. Other arguments are logged as errors through +\fBsyslog\fR(3)\&. +.SH "OPTIONS" +.PP +\fBdebug\fR +.RS 4 +Turns on debugging via +\fBsyslog\fR(3)\&. +.RE +.PP +\fBaudit\fR +.RS 4 +A little more extreme than debug\&. +.RE +.PP +\fBnullok\fR +.RS 4 +The default action of this module is to not permit the user access to a service if their official password is blank\&. The +\fBnullok\fR +argument overrides this default\&. +.RE +.PP +\fBtry_first_pass\fR +.RS 4 +Before prompting the user for their password, the module first tries the previous stacked module\*(Aqs password in case that satisfies this module as well\&. +.RE +.PP +\fBuse_first_pass\fR +.RS 4 +The argument +\fBuse_first_pass\fR +forces the module to use a previous stacked modules password and will never prompt the user \- if no password is available or the password is not appropriate, the user will be denied access\&. +.RE +.PP +\fBnodelay\fR +.RS 4 +This argument can be used to discourage the authentication component from requesting a delay should the authentication as a whole fail\&. The default action is for the module to request a delay\-on\-failure of the order of two second\&. +.RE +.PP +\fBuse_authtok\fR +.RS 4 +When password changing enforce the module to set the new password to the one provided by a previously stacked +\fBpassword\fR +module (this is used in the example of the stacking of the +\fBpam_cracklib\fR +module documented below)\&. +.RE +.PP +\fBnot_set_pass\fR +.RS 4 +This argument is used to inform the module that it is not to pay attention to/make available the old or new passwords from/to other (stacked) password modules\&. +.RE +.PP +\fBnis\fR +.RS 4 +NIS RPC is used for setting new passwords\&. +.RE +.PP +\fBremember=\fR\fB\fIn\fR\fR +.RS 4 +The last +\fIn\fR +passwords for each user are saved in +/etc/security/opasswd +in order to force password change history and keep the user from alternating between the same password too frequently\&. Instead of this option the +\fBpam_pwhistory\fR +module should be used\&. +.RE +.PP +\fBshadow\fR +.RS 4 +Try to maintain a shadow based system\&. +.RE +.PP +\fBmd5\fR +.RS 4 +When a user changes their password next, encrypt it with the MD5 algorithm\&. +.RE +.PP +\fBbigcrypt\fR +.RS 4 +When a user changes their password next, encrypt it with the DEC C2 algorithm\&. +.RE +.PP +\fBsha256\fR +.RS 4 +When a user changes their password next, encrypt it with the SHA256 algorithm\&. If the SHA256 algorithm is not known to the +\fBcrypt\fR(3) +function, fall back to MD5\&. +.RE +.PP +\fBsha512\fR +.RS 4 +When a user changes their password next, encrypt it with the SHA512 algorithm\&. If the SHA512 algorithm is not known to the +\fBcrypt\fR(3) +function, fall back to MD5\&. +.RE +.PP +\fBblowfish\fR +.RS 4 +When a user changes their password next, encrypt it with the blowfish algorithm\&. If the blowfish algorithm is not known to the +\fBcrypt\fR(3) +function, fall back to MD5\&. +.RE +.PP +\fBrounds=\fR\fB\fIn\fR\fR +.RS 4 +Set the optional number of rounds of the SHA256, SHA512 and blowfish password hashing algorithms to +\fIn\fR\&. +.RE +.PP +\fBbroken_shadow\fR +.RS 4 +Ignore errors reading shadow information for users in the account management module\&. +.RE +.PP +\fBminlen=\fR\fB\fIn\fR\fR +.RS 4 +Set a minimum password length of +\fIn\fR +characters\&. The max\&. for DES crypt based passwords are 8 characters\&. +.RE +.PP +Invalid arguments are logged with +\fBsyslog\fR(3)\&. +.SH "MODULE TYPES PROVIDED" +.PP +All module types (\fBaccount\fR, +\fBauth\fR, +\fBpassword\fR +and +\fBsession\fR) are provided\&. +.SH "RETURN VALUES" +.PP +PAM_IGNORE +.RS 4 +Ignore this module\&. +.RE +.SH "EXAMPLES" +.PP +An example usage for +/etc/pam\&.d/login +would be: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# Authenticate the user +auth required pam_unix\&.so +# Ensure users account and password are still active +account required pam_unix\&.so +# Change the users password, but at first check the strength +# with pam_cracklib(8) +password required pam_cracklib\&.so retry=3 minlen=6 difok=3 +password required pam_unix\&.so use_authtok nullok md5 +session required pam_unix\&.so + +.fi +.if n \{\ +.RE +.\} +.sp +.SH "SEE ALSO" +.PP +\fBlogin.defs\fR(5), +\fBpam.conf\fR(5), +\fBpam.d\fR(5), +\fBpam\fR(8) +.SH "AUTHOR" +.PP +pam_unix was written by various people\&. diff --git a/modules/pam_unix/pam_unix.8.xml b/modules/pam_unix/pam_unix.8.xml new file mode 100644 index 00000000..9ce084e3 --- /dev/null +++ b/modules/pam_unix/pam_unix.8.xml @@ -0,0 +1,419 @@ +<?xml version="1.0" encoding='UTF-8'?> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN" + "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"> + +<refentry id="pam_unix"> + + <refmeta> + <refentrytitle>pam_unix</refentrytitle> + <manvolnum>8</manvolnum> + <refmiscinfo class="sectdesc">Linux-PAM Manual</refmiscinfo> + </refmeta> + + <refnamediv id="pam_unix-name"> + <refname>pam_unix</refname> + <refpurpose>Module for traditional password authentication</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis id="pam_unix-cmdsynopsis"> + <command>pam_unix.so</command> + <arg choice="opt"> + ... + </arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1 id="pam_unix-description"> + + <title>DESCRIPTION</title> + + <para> + This is the standard Unix authentication module. It uses standard + calls from the system's libraries to retrieve and set account + information as well as authentication. Usually this is obtained + from the /etc/passwd and the /etc/shadow file as well if shadow is + enabled. + </para> + + <para> + The account component performs the task of establishing the status + of the user's account and password based on the following + <emphasis>shadow</emphasis> elements: expire, last_change, max_change, + min_change, warn_change. In the case of the latter, it may offer advice + to the user on changing their password or, through the + <emphasis remap='B'>PAM_AUTHTOKEN_REQD</emphasis> return, delay + giving service to the user until they have established a new password. + The entries listed above are documented in the <citerefentry> + <refentrytitle>shadow</refentrytitle><manvolnum>5</manvolnum> + </citerefentry> manual page. Should the user's record not contain + one or more of these entries, the corresponding + <emphasis>shadow</emphasis> check is not performed. + </para> + + <para> + The authentication component performs the task of checking the + users credentials (password). The default action of this module + is to not permit the user access to a service if their official + password is blank. + </para> + + <para> + A helper binary, <citerefentry> + <refentrytitle>unix_chkpwd</refentrytitle><manvolnum>8</manvolnum> + </citerefentry>, is provided + to check the user's password when it is stored in a read + protected database. This binary is very simple and will only + check the password of the user invoking it. It is called + transparently on behalf of the user by the authenticating + component of this module. In this way it is possible + for applications like <citerefentry> + <refentrytitle>xlock</refentrytitle><manvolnum>1</manvolnum> + </citerefentry> to work without + being setuid-root. The module, by default, will temporarily turn + off SIGCHLD handling for the duration of execution of the helper + binary. This is generally the right thing to do, as many applications + are not prepared to handle this signal from a child they didn't know + was <function>fork()</function>d. The <option>noreap</option> module + argument can be used to suppress this temporary shielding and may be + needed for use with certain applications. + </para> + + <para> + The password component of this module performs the task of updating + the user's password. The default encryption hash is taken from the + <emphasis remap='B'>ENCRYPT_METHOD</emphasis> variable from + <emphasis>/etc/login.defs</emphasis> + </para> + + <para> + The session component of this module logs when a user logins + or leave the system. + </para> + + <para> + Remaining arguments, supported by others functions of this + module, are silently ignored. Other arguments are logged as + errors through <citerefentry> + <refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum> + </citerefentry>. + </para> + </refsect1> + + <refsect1 id="pam_unix-options"> + + <title>OPTIONS</title> + <variablelist> + <varlistentry> + <term> + <option>debug</option> + </term> + <listitem> + <para> + Turns on debugging via + <citerefentry> + <refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum> + </citerefentry>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <option>audit</option> + </term> + <listitem> + <para> + A little more extreme than debug. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <option>nullok</option> + </term> + <listitem> + <para> + The default action of this module is to not permit the + user access to a service if their official password is blank. + The <option>nullok</option> argument overrides this default. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <option>try_first_pass</option> + </term> + <listitem> + <para> + Before prompting the user for their password, the module first + tries the previous stacked module's password in case that + satisfies this module as well. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <option>use_first_pass</option> + </term> + <listitem> + <para> + The argument <option>use_first_pass</option> forces the module + to use a previous stacked modules password and will never prompt + the user - if no password is available or the password is not + appropriate, the user will be denied access. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <option>nodelay</option> + </term> + <listitem> + <para> + This argument can be used to discourage the authentication + component from requesting a delay should the authentication + as a whole fail. The default action is for the module to + request a delay-on-failure of the order of two second. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <option>use_authtok</option> + </term> + <listitem> + <para> + When password changing enforce the module to set the new + password to the one provided by a previously stacked + <option>password</option> module (this is used in the + example of the stacking of the <command>pam_cracklib</command> + module documented below). + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <option>not_set_pass</option> + </term> + <listitem> + <para> + This argument is used to inform the module that it is not to + pay attention to/make available the old or new passwords from/to + other (stacked) password modules. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <option>nis</option> + </term> + <listitem> + <para> + NIS RPC is used for setting new passwords. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <option>remember=<replaceable>n</replaceable></option> + </term> + <listitem> + <para> + The last <replaceable>n</replaceable> passwords for each + user are saved in <filename>/etc/security/opasswd</filename> + in order to force password change history and keep the user + from alternating between the same password too frequently. + Instead of this option the <command>pam_pwhistory</command> + module should be used. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <option>shadow</option> + </term> + <listitem> + <para> + Try to maintain a shadow based system. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <option>md5</option> + </term> + <listitem> + <para> + When a user changes their password next, encrypt + it with the MD5 algorithm. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <option>bigcrypt</option> + </term> + <listitem> + <para> + When a user changes their password next, + encrypt it with the DEC C2 algorithm. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <option>sha256</option> + </term> + <listitem> + <para> + When a user changes their password next, + encrypt it with the SHA256 algorithm. If the + SHA256 algorithm is not known to the <citerefentry> + <refentrytitle>crypt</refentrytitle><manvolnum>3</manvolnum> + </citerefentry> function, + fall back to MD5. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <option>sha512</option> + </term> + <listitem> + <para> + When a user changes their password next, + encrypt it with the SHA512 algorithm. If the + SHA512 algorithm is not known to the <citerefentry> + <refentrytitle>crypt</refentrytitle><manvolnum>3</manvolnum> + </citerefentry> function, + fall back to MD5. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <option>blowfish</option> + </term> + <listitem> + <para> + When a user changes their password next, + encrypt it with the blowfish algorithm. If the + blowfish algorithm is not known to the <citerefentry> + <refentrytitle>crypt</refentrytitle><manvolnum>3</manvolnum> + </citerefentry> function, + fall back to MD5. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <option>rounds=<replaceable>n</replaceable></option> + </term> + <listitem> + <para> + Set the optional number of rounds of the SHA256, SHA512 + and blowfish password hashing algorithms to + <replaceable>n</replaceable>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <option>broken_shadow</option> + </term> + <listitem> + <para> + Ignore errors reading shadow information for + users in the account management module. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <option>minlen=<replaceable>n</replaceable></option> + </term> + <listitem> + <para> + Set a minimum password length of <replaceable>n</replaceable> + characters. The max. for DES crypt based passwords are 8 + characters. + </para> + </listitem> + </varlistentry> + </variablelist> + <para> + Invalid arguments are logged with <citerefentry> + <refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum> + </citerefentry>. + </para> + </refsect1> + + <refsect1 id="pam_unix-types"> + <title>MODULE TYPES PROVIDED</title> + <para> + All module types (<option>account</option>, <option>auth</option>, + <option>password</option> and <option>session</option>) are provided. + </para> + </refsect1> + + <refsect1 id='pam_unix-return_values'> + <title>RETURN VALUES</title> + <variablelist> + <varlistentry> + <term>PAM_IGNORE</term> + <listitem> + <para> + Ignore this module. + </para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1 id='pam_unix-examples'> + <title>EXAMPLES</title> + <para> + An example usage for <filename>/etc/pam.d/login</filename> + would be: + <programlisting> +# Authenticate the user +auth required pam_unix.so +# Ensure users account and password are still active +account required pam_unix.so +# Change the users password, but at first check the strength +# with pam_cracklib(8) +password required pam_cracklib.so retry=3 minlen=6 difok=3 +password required pam_unix.so use_authtok nullok md5 +session required pam_unix.so + </programlisting> + </para> + </refsect1> + + <refsect1 id='pam_unix-see_also'> + <title>SEE ALSO</title> + <para> + <citerefentry> + <refentrytitle>login.defs</refentrytitle><manvolnum>5</manvolnum> + </citerefentry>, + <citerefentry> + <refentrytitle>pam.conf</refentrytitle><manvolnum>5</manvolnum> + </citerefentry>, + <citerefentry> + <refentrytitle>pam.d</refentrytitle><manvolnum>5</manvolnum> + </citerefentry>, + <citerefentry> + <refentrytitle>pam</refentrytitle><manvolnum>8</manvolnum> + </citerefentry> + </para> + </refsect1> + + <refsect1 id='pam_unix-author'> + <title>AUTHOR</title> + <para> + pam_unix was written by various people. + </para> + </refsect1> + +</refentry> diff --git a/modules/pam_unix/pam_unix_acct.c b/modules/pam_unix/pam_unix_acct.c new file mode 100644 index 00000000..8ec44492 --- /dev/null +++ b/modules/pam_unix/pam_unix_acct.c @@ -0,0 +1,304 @@ +/* + * Copyright Elliot Lee, 1996. All rights reserved. + * Copyright Jan Rkorajski, 1999. 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 "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/resource.h> +#include <syslog.h> +#include <pwd.h> +#include <shadow.h> +#include <time.h> /* for time() */ +#include <errno.h> +#include <sys/wait.h> + +#include <security/_pam_macros.h> + +/* indicate that the following groups are defined */ + +#ifdef PAM_STATIC +# include "pam_unix_static.h" +#else +# define PAM_SM_ACCOUNT +#endif + +#include <security/pam_modules.h> +#include <security/pam_ext.h> +#include <security/pam_modutil.h> + +#include "support.h" +#include "passverify.h" + +int _unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl, + const char *user, int *daysleft) +{ + int retval=0, child, fds[2]; + struct sigaction newsa, oldsa; + D(("running verify_binary")); + + /* create a pipe for the messages */ + if (pipe(fds) != 0) { + D(("could not make pipe")); + pam_syslog(pamh, LOG_ERR, "Could not make pipe: %m"); + return PAM_AUTH_ERR; + } + D(("called.")); + + if (off(UNIX_NOREAP, ctrl)) { + /* + * This code arranges that the demise of the child does not cause + * the application to receive a signal it is not expecting - which + * may kill the application or worse. + * + * The "noreap" module argument is provided so that the admin can + * override this behavior. + */ + memset(&newsa, '\0', sizeof(newsa)); + newsa.sa_handler = SIG_DFL; + sigaction(SIGCHLD, &newsa, &oldsa); + } + + /* fork */ + child = fork(); + if (child == 0) { + int i=0; + struct rlimit rlim; + static char *envp[] = { NULL }; + char *args[] = { NULL, NULL, NULL, NULL }; + + /* reopen stdout as pipe */ + dup2(fds[1], STDOUT_FILENO); + + /* XXX - should really tidy up PAM here too */ + + if (getrlimit(RLIMIT_NOFILE,&rlim)==0) { + if (rlim.rlim_max >= MAX_FD_NO) + rlim.rlim_max = MAX_FD_NO; + for (i=0; i < (int)rlim.rlim_max; i++) { + if (i != STDOUT_FILENO) { + close(i); + } + } + } + + if (geteuid() == 0) { + /* must set the real uid to 0 so the helper will not error + out if pam is called from setuid binary (su, sudo...) */ + if (setuid(0) == -1) { + pam_syslog(pamh, LOG_ERR, "setuid failed: %m"); + printf("-1\n"); + fflush(stdout); + _exit(PAM_AUTHINFO_UNAVAIL); + } + } + + /* exec binary helper */ + args[0] = x_strdup(CHKPWD_HELPER); + args[1] = x_strdup(user); + args[2] = x_strdup("chkexpiry"); + + execve(CHKPWD_HELPER, args, envp); + + pam_syslog(pamh, LOG_ERR, "helper binary execve failed: %m"); + /* should not get here: exit with error */ + D(("helper binary is not available")); + printf("-1\n"); + fflush(stdout); + _exit(PAM_AUTHINFO_UNAVAIL); + } else { + close(fds[1]); + if (child > 0) { + char buf[32]; + int rc=0; + /* wait for helper to complete: */ + while ((rc=waitpid(child, &retval, 0)) < 0 && errno == EINTR); + if (rc<0) { + pam_syslog(pamh, LOG_ERR, "unix_chkpwd waitpid returned %d: %m", rc); + retval = PAM_AUTH_ERR; + } else if (!WIFEXITED(retval)) { + pam_syslog(pamh, LOG_ERR, "unix_chkpwd abnormal exit: %d", retval); + retval = PAM_AUTH_ERR; + } else { + retval = WEXITSTATUS(retval); + rc = pam_modutil_read(fds[0], buf, sizeof(buf) - 1); + if(rc > 0) { + buf[rc] = '\0'; + if (sscanf(buf,"%d", daysleft) != 1 ) + retval = PAM_AUTH_ERR; + } + else { + pam_syslog(pamh, LOG_ERR, "read unix_chkpwd output error %d: %m", rc); + retval = PAM_AUTH_ERR; + } + } + } else { + pam_syslog(pamh, LOG_ERR, "Fork failed: %m"); + D(("fork failed")); + retval = PAM_AUTH_ERR; + } + close(fds[0]); + } + + if (off(UNIX_NOREAP, ctrl)) { + sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */ + } + + D(("Returning %d",retval)); + return retval; +} + +/* + * PAM framework looks for this entry-point to pass control to the + * account management module. + */ + +int +pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + unsigned int ctrl; + const void *void_uname; + const char *uname; + int retval, daysleft; + struct spwd *spent; + struct passwd *pwent; + char buf[256]; + + D(("called.")); + + ctrl = _set_ctrl(pamh, flags, NULL, NULL, NULL, argc, argv); + + retval = pam_get_item(pamh, PAM_USER, &void_uname); + uname = void_uname; + D(("user = `%s'", uname)); + if (retval != PAM_SUCCESS || uname == NULL) { + pam_syslog(pamh, LOG_ALERT, + "could not identify user (from uid=%lu)", + (unsigned long int)getuid()); + return PAM_USER_UNKNOWN; + } + + retval = get_account_info(pamh, uname, &pwent, &spent); + if (retval == PAM_USER_UNKNOWN) { + pam_syslog(pamh, LOG_ALERT, + "could not identify user (from getpwnam(%s))", + uname); + return retval; + } + + if (retval == PAM_SUCCESS && spent == NULL) + return PAM_SUCCESS; + + if (retval == PAM_UNIX_RUN_HELPER) { + retval = _unix_run_verify_binary(pamh, ctrl, uname, &daysleft); + if (retval == PAM_AUTHINFO_UNAVAIL && + on(UNIX_BROKEN_SHADOW, ctrl)) + return PAM_SUCCESS; + } else if (retval != PAM_SUCCESS) { + if (on(UNIX_BROKEN_SHADOW,ctrl)) + return PAM_SUCCESS; + else + return retval; + } else + retval = check_shadow_expiry(pamh, spent, &daysleft); + + switch (retval) { + case PAM_ACCT_EXPIRED: + pam_syslog(pamh, LOG_NOTICE, + "account %s has expired (account expired)", + uname); + _make_remark(pamh, ctrl, PAM_ERROR_MSG, + _("Your account has expired; please contact your system administrator")); + break; + case PAM_NEW_AUTHTOK_REQD: + if (daysleft == 0) { + pam_syslog(pamh, LOG_NOTICE, + "expired password for user %s (root enforced)", + uname); + _make_remark(pamh, ctrl, PAM_ERROR_MSG, + _("You are required to change your password immediately (root enforced)")); + } else { + pam_syslog(pamh, LOG_DEBUG, + "expired password for user %s (password aged)", + uname); + _make_remark(pamh, ctrl, PAM_ERROR_MSG, + _("You are required to change your password immediately (password aged)")); + } + break; + case PAM_AUTHTOK_EXPIRED: + pam_syslog(pamh, LOG_NOTICE, + "account %s has expired (failed to change password)", + uname); + _make_remark(pamh, ctrl, PAM_ERROR_MSG, + _("Your account has expired; please contact your system administrator")); + break; + case PAM_AUTHTOK_ERR: + retval = PAM_SUCCESS; + /* fallthrough */ + case PAM_SUCCESS: + if (daysleft >= 0) { + pam_syslog(pamh, LOG_DEBUG, + "password for user %s will expire in %d days", + uname, daysleft); +#if defined HAVE_DNGETTEXT && defined ENABLE_NLS + snprintf (buf, sizeof (buf), + dngettext(PACKAGE, + "Warning: your password will expire in %d day", + "Warning: your password will expire in %d days", + daysleft), + daysleft); +#else + if (daysleft == 1) + snprintf(buf, sizeof (buf), + _("Warning: your password will expire in %d day"), + daysleft); + else + snprintf(buf, sizeof (buf), + /* TRANSLATORS: only used if dngettext is not supported */ + _("Warning: your password will expire in %d days"), + daysleft); +#endif + _make_remark(pamh, ctrl, PAM_TEXT_INFO, buf); + } + } + + D(("all done")); + + return retval; +} diff --git a/modules/pam_unix/pam_unix_auth.c b/modules/pam_unix/pam_unix_auth.c new file mode 100644 index 00000000..44573e6c --- /dev/null +++ b/modules/pam_unix/pam_unix_auth.c @@ -0,0 +1,218 @@ +/* + * Copyright Alexander O. Yuriev, 1996. All rights reserved. + * NIS+ support by Thorsten Kukuk <kukuk@weber.uni-paderborn.de> + * Copyright Jan Rkorajski, 1999. 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 "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <syslog.h> + +/* indicate the following groups are defined */ + +#ifdef PAM_STATIC +# include "pam_unix_static.h" +#else +# define PAM_SM_AUTH +#endif + +#define _PAM_EXTERN_FUNCTIONS +#include <security/_pam_macros.h> +#include <security/pam_modules.h> +#include <security/pam_ext.h> + +#include "support.h" + +/* + * PAM framework looks for these entry-points to pass control to the + * authentication module. + */ + +/* Fun starts here :) + + * pam_sm_authenticate() performs UNIX/shadow authentication + * + * First, if shadow support is available, attempt to perform + * authentication using shadow passwords. If shadow is not + * available, or user does not have a shadow password, fallback + * onto a normal UNIX authentication + */ + +#define _UNIX_AUTHTOK "-UN*X-PASS" + +#define AUTH_RETURN \ +do { \ + if (on(UNIX_LIKE_AUTH, ctrl) && ret_data) { \ + D(("recording return code for next time [%d]", \ + retval)); \ + *ret_data = retval; \ + pam_set_data(pamh, "unix_setcred_return", \ + (void *) ret_data, setcred_free); \ + } else if (ret_data) \ + free (ret_data); \ + D(("done. [%s]", pam_strerror(pamh, retval))); \ + return retval; \ +} while (0) + + +static void +setcred_free (pam_handle_t *pamh UNUSED, void *ptr, int err UNUSED) +{ + if (ptr) + free (ptr); +} + +int +pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + unsigned int ctrl; + int retval, *ret_data = NULL; + const char *name; + const void *p; + + D(("called.")); + + ctrl = _set_ctrl(pamh, flags, NULL, NULL, NULL, argc, argv); + + /* Get a few bytes so we can pass our return value to + pam_sm_setcred(). */ + if (on(UNIX_LIKE_AUTH, ctrl)) + ret_data = malloc(sizeof(int)); + + /* get the user'name' */ + + retval = pam_get_user(pamh, &name, NULL); + if (retval == PAM_SUCCESS) { + /* + * Various libraries at various times have had bugs related to + * '+' or '-' as the first character of a user name. Don't + * allow this characters here. + */ + if (name == NULL || name[0] == '-' || name[0] == '+') { + pam_syslog(pamh, LOG_ERR, "bad username [%s]", name); + retval = PAM_USER_UNKNOWN; + AUTH_RETURN; + } + if (on(UNIX_DEBUG, ctrl)) + D(("username [%s] obtained", name)); + } else { + D(("trouble reading username")); + if (retval == PAM_CONV_AGAIN) { + D(("pam_get_user/conv() function is not ready yet")); + /* it is safe to resume this function so we translate this + * retval to the value that indicates we're happy to resume. + */ + retval = PAM_INCOMPLETE; + } + AUTH_RETURN; + } + + /* if this user does not have a password... */ + + if (_unix_blankpasswd(pamh, ctrl, name)) { + D(("user '%s' has blank passwd", name)); + name = NULL; + retval = PAM_SUCCESS; + AUTH_RETURN; + } + /* get this user's authentication token */ + + retval = _unix_read_password(pamh, ctrl, NULL, _("Password: "), NULL + ,_UNIX_AUTHTOK, &p); + if (retval != PAM_SUCCESS) { + if (retval != PAM_CONV_AGAIN) { + pam_syslog(pamh, LOG_CRIT, + "auth could not identify password for [%s]", name); + } else { + D(("conversation function is not ready yet")); + /* + * it is safe to resume this function so we translate this + * retval to the value that indicates we're happy to resume. + */ + retval = PAM_INCOMPLETE; + } + name = NULL; + AUTH_RETURN; + } + D(("user=%s, password=[%s]", name, p)); + + /* verify the password of this user */ + retval = _unix_verify_password(pamh, name, p, ctrl); + name = p = NULL; + + AUTH_RETURN; +} + + +/* + * The only thing _pam_set_credentials_unix() does is initialization of + * UNIX group IDs. + * + * Well, everybody but me on linux-pam is convinced that it should not + * initialize group IDs, so I am not doing it but don't say that I haven't + * warned you. -- AOY + */ + +int +pam_sm_setcred (pam_handle_t *pamh, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + int retval; + const void *pretval = NULL; + + D(("called.")); + + retval = PAM_SUCCESS; + + D(("recovering return code from auth call")); + /* We will only find something here if UNIX_LIKE_AUTH is set -- + don't worry about an explicit check of argv. */ + if (pam_get_data(pamh, "unix_setcred_return", &pretval) == PAM_SUCCESS + && pretval) { + retval = *(const int *)pretval; + pam_set_data(pamh, "unix_setcred_return", NULL, NULL); + D(("recovered data indicates that old retval was %d", retval)); + } + + return retval; +} diff --git a/modules/pam_unix/pam_unix_passwd.c b/modules/pam_unix/pam_unix_passwd.c new file mode 100644 index 00000000..9aae3b03 --- /dev/null +++ b/modules/pam_unix/pam_unix_passwd.c @@ -0,0 +1,830 @@ +/* + * Main coding by Elliot Lee <sopwith@redhat.com>, Red Hat Software. + * Copyright (C) 1996. + * Copyright (c) Jan Rêkorajski, 1999. + * Copyright (c) Red Hat, Inc., 2007, 2008. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <malloc.h> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include <pwd.h> +#include <syslog.h> +#include <shadow.h> +#include <time.h> /* for time() */ +#include <fcntl.h> +#include <ctype.h> +#include <sys/time.h> +#include <sys/stat.h> + +#include <signal.h> +#include <errno.h> +#include <sys/wait.h> +#include <sys/resource.h> + +#include <security/_pam_macros.h> + +/* indicate the following groups are defined */ + +#ifdef PAM_STATIC +# include "pam_unix_static.h" +#else +# define PAM_SM_PASSWORD +#endif + +#include <security/pam_modules.h> +#include <security/pam_ext.h> +#include <security/pam_modutil.h> + +#include "md5.h" +#include "support.h" +#include "passverify.h" +#include "bigcrypt.h" + +#if (HAVE_YP_GET_DEFAULT_DOMAIN || HAVE_GETDOMAINNAME) && HAVE_YP_MASTER +# define HAVE_NIS +#endif + +#ifdef HAVE_NIS +# include <rpc/rpc.h> + +# if HAVE_RPCSVC_YP_PROT_H +# include <rpcsvc/yp_prot.h> +# endif + +# if HAVE_RPCSVC_YPCLNT_H +# include <rpcsvc/ypclnt.h> +# endif + +# include "yppasswd.h" + +# if !HAVE_DECL_GETRPCPORT +extern int getrpcport(const char *host, unsigned long prognum, + unsigned long versnum, unsigned int proto); +# endif /* GNU libc 2.1 */ +#endif + +/* + How it works: + Gets in username (has to be done) from the calling program + Does authentication of user (only if we are not running as root) + Gets new password/checks for sanity + Sets it. + */ + +/* data tokens */ + +#define _UNIX_OLD_AUTHTOK "-UN*X-OLD-PASS" +#define _UNIX_NEW_AUTHTOK "-UN*X-NEW-PASS" + +#define MAX_PASSWD_TRIES 3 + +#ifdef HAVE_NIS +static char *getNISserver(pam_handle_t *pamh, unsigned int ctrl) +{ + char *master; + char *domainname; + int port, err; + +#ifdef HAVE_YP_GET_DEFAULT_DOMAIN + if ((err = yp_get_default_domain(&domainname)) != 0) { + pam_syslog(pamh, LOG_WARNING, "can't get local yp domain: %s", + yperr_string(err)); + return NULL; + } +#elif defined(HAVE_GETDOMAINNAME) + char domainname_res[256]; + + if (getdomainname (domainname_res, sizeof (domainname_res)) == 0) + { + if (strcmp (domainname_res, "(none)") == 0) + { + /* If domainname is not set, some systems will return "(none)" */ + domainname_res[0] = '\0'; + } + domainname = domainname_res; + } + else domainname = NULL; +#endif + + if ((err = yp_master(domainname, "passwd.byname", &master)) != 0) { + pam_syslog(pamh, LOG_WARNING, "can't find the master ypserver: %s", + yperr_string(err)); + return NULL; + } + port = getrpcport(master, YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP); + if (port == 0) { + pam_syslog(pamh, LOG_WARNING, + "yppasswdd not running on NIS master host"); + return NULL; + } + if (port >= IPPORT_RESERVED) { + pam_syslog(pamh, LOG_WARNING, + "yppasswd daemon running on illegal port"); + return NULL; + } + if (on(UNIX_DEBUG, ctrl)) { + pam_syslog(pamh, LOG_DEBUG, "Use NIS server on %s with port %d", + master, port); + } + return master; +} +#endif + +#ifdef WITH_SELINUX + +static int _unix_run_update_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user, + const char *fromwhat, const char *towhat, int remember) +{ + int retval, child, fds[2]; + struct sigaction newsa, oldsa; + + D(("called.")); + /* create a pipe for the password */ + if (pipe(fds) != 0) { + D(("could not make pipe")); + return PAM_AUTH_ERR; + } + + if (off(UNIX_NOREAP, ctrl)) { + /* + * This code arranges that the demise of the child does not cause + * the application to receive a signal it is not expecting - which + * may kill the application or worse. + * + * The "noreap" module argument is provided so that the admin can + * override this behavior. + */ + memset(&newsa, '\0', sizeof(newsa)); + newsa.sa_handler = SIG_DFL; + sigaction(SIGCHLD, &newsa, &oldsa); + } + + /* fork */ + child = fork(); + if (child == 0) { + int i=0; + struct rlimit rlim; + static char *envp[] = { NULL }; + char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL }; + char buffer[16]; + + /* XXX - should really tidy up PAM here too */ + + /* reopen stdin as pipe */ + dup2(fds[0], STDIN_FILENO); + + if (getrlimit(RLIMIT_NOFILE,&rlim)==0) { + if (rlim.rlim_max >= MAX_FD_NO) + rlim.rlim_max = MAX_FD_NO; + for (i=0; i < (int)rlim.rlim_max; i++) { + if (i != STDIN_FILENO) + close(i); + } + } + + /* exec binary helper */ + args[0] = x_strdup(UPDATE_HELPER); + args[1] = x_strdup(user); + args[2] = x_strdup("update"); + if (on(UNIX_SHADOW, ctrl)) + args[3] = x_strdup("1"); + else + args[3] = x_strdup("0"); + + snprintf(buffer, sizeof(buffer), "%d", remember); + args[4] = x_strdup(buffer); + + execve(UPDATE_HELPER, args, envp); + + /* should not get here: exit with error */ + D(("helper binary is not available")); + _exit(PAM_AUTHINFO_UNAVAIL); + } else if (child > 0) { + /* wait for child */ + /* if the stored password is NULL */ + int rc=0; + if (fromwhat) + pam_modutil_write(fds[1], fromwhat, strlen(fromwhat)+1); + else + pam_modutil_write(fds[1], "", 1); + if (towhat) { + pam_modutil_write(fds[1], towhat, strlen(towhat)+1); + } + else + pam_modutil_write(fds[1], "", 1); + + close(fds[0]); /* close here to avoid possible SIGPIPE above */ + close(fds[1]); + /* wait for helper to complete: */ + while ((rc=waitpid(child, &retval, 0)) < 0 && errno == EINTR); + if (rc<0) { + pam_syslog(pamh, LOG_ERR, "unix_update waitpid failed: %m"); + retval = PAM_AUTHTOK_ERR; + } else if (!WIFEXITED(retval)) { + pam_syslog(pamh, LOG_ERR, "unix_update abnormal exit: %d", retval); + retval = PAM_AUTHTOK_ERR; + } else { + retval = WEXITSTATUS(retval); + } + } else { + D(("fork failed")); + close(fds[0]); + close(fds[1]); + retval = PAM_AUTH_ERR; + } + + if (off(UNIX_NOREAP, ctrl)) { + sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */ + } + + return retval; +} +#endif + +static int check_old_password(const char *forwho, const char *newpass) +{ + static char buf[16384]; + char *s_luser, *s_uid, *s_npas, *s_pas; + int retval = PAM_SUCCESS; + FILE *opwfile; + size_t len = strlen(forwho); + + opwfile = fopen(OLD_PASSWORDS_FILE, "r"); + if (opwfile == NULL) + return PAM_ABORT; + + while (fgets(buf, 16380, opwfile)) { + if (!strncmp(buf, forwho, len) && (buf[len] == ':' || + buf[len] == ',')) { + char *sptr; + buf[strlen(buf) - 1] = '\0'; + s_luser = strtok_r(buf, ":,", &sptr); + s_uid = strtok_r(NULL, ":,", &sptr); + s_npas = strtok_r(NULL, ":,", &sptr); + s_pas = strtok_r(NULL, ":,", &sptr); + while (s_pas != NULL) { + char *md5pass = Goodcrypt_md5(newpass, s_pas); + if (!strcmp(md5pass, s_pas)) { + _pam_delete(md5pass); + retval = PAM_AUTHTOK_ERR; + break; + } + s_pas = strtok_r(NULL, ":,", &sptr); + _pam_delete(md5pass); + } + break; + } + } + fclose(opwfile); + + return retval; +} + +static int _do_setpass(pam_handle_t* pamh, const char *forwho, + const char *fromwhat, + char *towhat, unsigned int ctrl, int remember) +{ + struct passwd *pwd = NULL; + int retval = 0; + int unlocked = 0; + char *master = NULL; + + D(("called")); + + pwd = getpwnam(forwho); + + if (pwd == NULL) { + retval = PAM_AUTHTOK_ERR; + goto done; + } + + if (on(UNIX_NIS, ctrl) && _unix_comesfromsource(pamh, forwho, 0, 1)) { +#ifdef HAVE_NIS + if ((master=getNISserver(pamh, ctrl)) != NULL) { + struct timeval timeout; + struct yppasswd yppwd; + CLIENT *clnt; + int status; + enum clnt_stat err; + + /* Unlock passwd file to avoid deadlock */ + unlock_pwdf(); + unlocked = 1; + + /* Initialize password information */ + yppwd.newpw.pw_passwd = pwd->pw_passwd; + yppwd.newpw.pw_name = pwd->pw_name; + yppwd.newpw.pw_uid = pwd->pw_uid; + yppwd.newpw.pw_gid = pwd->pw_gid; + yppwd.newpw.pw_gecos = pwd->pw_gecos; + yppwd.newpw.pw_dir = pwd->pw_dir; + yppwd.newpw.pw_shell = pwd->pw_shell; + yppwd.oldpass = fromwhat ? strdup (fromwhat) : strdup (""); + yppwd.newpw.pw_passwd = towhat; + + D(("Set password %s for %s", yppwd.newpw.pw_passwd, forwho)); + + /* The yppasswd.x file said `unix authentication required', + * so I added it. This is the only reason it is in here. + * My yppasswdd doesn't use it, but maybe some others out there + * do. --okir + */ + clnt = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp"); + clnt->cl_auth = authunix_create_default(); + memset((char *) &status, '\0', sizeof(status)); + timeout.tv_sec = 25; + timeout.tv_usec = 0; + err = clnt_call(clnt, YPPASSWDPROC_UPDATE, + (xdrproc_t) xdr_yppasswd, (char *) &yppwd, + (xdrproc_t) xdr_int, (char *) &status, + timeout); + + free (yppwd.oldpass); + + if (err) { + _make_remark(pamh, ctrl, PAM_TEXT_INFO, + clnt_sperrno(err)); + } else if (status) { + D(("Error while changing NIS password.\n")); + } + D(("The password has%s been changed on %s.", + (err || status) ? " not" : "", master)); + pam_syslog(pamh, LOG_NOTICE, "password%s changed for %s on %s", + (err || status) ? " not" : "", pwd->pw_name, master); + + auth_destroy(clnt->cl_auth); + clnt_destroy(clnt); + if (err || status) { + _make_remark(pamh, ctrl, PAM_TEXT_INFO, + _("NIS password could not be changed.")); + retval = PAM_TRY_AGAIN; + } +#ifdef PAM_DEBUG + sleep(5); +#endif + } else { + retval = PAM_TRY_AGAIN; + } +#else + if (on(UNIX_DEBUG, ctrl)) { + pam_syslog(pamh, LOG_DEBUG, "No NIS support available"); + } + + retval = PAM_TRY_AGAIN; +#endif + } + + if (_unix_comesfromsource(pamh, forwho, 1, 0)) { + if(unlocked) { + if (lock_pwdf() != PAM_SUCCESS) { + return PAM_AUTHTOK_LOCK_BUSY; + } + } +#ifdef WITH_SELINUX + if (unix_selinux_confined()) + return _unix_run_update_binary(pamh, ctrl, forwho, fromwhat, towhat, remember); +#endif + /* first, save old password */ + if (save_old_password(pamh, forwho, fromwhat, remember)) { + retval = PAM_AUTHTOK_ERR; + goto done; + } + if (on(UNIX_SHADOW, ctrl) || is_pwd_shadowed(pwd)) { + retval = unix_update_shadow(pamh, forwho, towhat); + if (retval == PAM_SUCCESS) + if (!is_pwd_shadowed(pwd)) + retval = unix_update_passwd(pamh, forwho, "x"); + } else { + retval = unix_update_passwd(pamh, forwho, towhat); + } + } + + +done: + unlock_pwdf(); + + return retval; +} + +static int _unix_verify_shadow(pam_handle_t *pamh, const char *user, unsigned int ctrl) +{ + struct passwd *pwent = NULL; /* Password and shadow password */ + struct spwd *spent = NULL; /* file entries for the user */ + int daysleft; + int retval; + + retval = get_account_info(pamh, user, &pwent, &spent); + if (retval == PAM_USER_UNKNOWN) { + return retval; + } + + if (retval == PAM_SUCCESS && spent == NULL) + return PAM_SUCCESS; + + if (retval == PAM_UNIX_RUN_HELPER) { + retval = _unix_run_verify_binary(pamh, ctrl, user, &daysleft); + if (retval == PAM_AUTH_ERR || retval == PAM_USER_UNKNOWN) + return retval; + } + else if (retval == PAM_SUCCESS) + retval = check_shadow_expiry(pamh, spent, &daysleft); + + if (on(UNIX__IAMROOT, ctrl) || retval == PAM_NEW_AUTHTOK_REQD) + return PAM_SUCCESS; + + return retval; +} + +static int _pam_unix_approve_pass(pam_handle_t * pamh + ,unsigned int ctrl + ,const char *pass_old + ,const char *pass_new, + int pass_min_len) +{ + const void *user; + const char *remark = NULL; + int retval = PAM_SUCCESS; + + D(("&new=%p, &old=%p", pass_old, pass_new)); + D(("new=[%s]", pass_new)); + D(("old=[%s]", pass_old)); + + if (pass_new == NULL || (pass_old && !strcmp(pass_old, pass_new))) { + if (on(UNIX_DEBUG, ctrl)) { + pam_syslog(pamh, LOG_DEBUG, "bad authentication token"); + } + _make_remark(pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ? + _("No password supplied") : _("Password unchanged")); + return PAM_AUTHTOK_ERR; + } + /* + * if one wanted to hardwire authentication token strength + * checking this would be the place - AGM + */ + + retval = pam_get_item(pamh, PAM_USER, &user); + if (retval != PAM_SUCCESS) { + if (on(UNIX_DEBUG, ctrl)) { + pam_syslog(pamh, LOG_ERR, "Can not get username"); + return PAM_AUTHTOK_ERR; + } + } + if (off(UNIX__IAMROOT, ctrl)) { + if (strlen(pass_new) < pass_min_len) + remark = _("You must choose a longer password"); + D(("length check [%s]", remark)); + if (on(UNIX_REMEMBER_PASSWD, ctrl)) { + if ((retval = check_old_password(user, pass_new)) == PAM_AUTHTOK_ERR) + remark = _("Password has been already used. Choose another."); + if (retval == PAM_ABORT) { + pam_syslog(pamh, LOG_ERR, "can't open %s file to check old passwords", + OLD_PASSWORDS_FILE); + return retval; + } + } + } + if (remark) { + _make_remark(pamh, ctrl, PAM_ERROR_MSG, remark); + retval = PAM_AUTHTOK_ERR; + } + return retval; +} + +int +pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + unsigned int ctrl, lctrl; + int retval; + int remember = -1; + int rounds = -1; + int pass_min_len = 0; + + /* <DO NOT free() THESE> */ + const char *user; + const void *pass_old, *pass_new; + /* </DO NOT free() THESE> */ + + D(("called.")); + + ctrl = _set_ctrl(pamh, flags, &remember, &rounds, &pass_min_len, + argc, argv); + + /* + * First get the name of a user + */ + retval = pam_get_user(pamh, &user, NULL); + if (retval == PAM_SUCCESS) { + /* + * Various libraries at various times have had bugs related to + * '+' or '-' as the first character of a user name. Don't + * allow them. + */ + if (user == NULL || user[0] == '-' || user[0] == '+') { + pam_syslog(pamh, LOG_ERR, "bad username [%s]", user); + return PAM_USER_UNKNOWN; + } + if (retval == PAM_SUCCESS && on(UNIX_DEBUG, ctrl)) + pam_syslog(pamh, LOG_DEBUG, "username [%s] obtained", + user); + } else { + if (on(UNIX_DEBUG, ctrl)) + pam_syslog(pamh, LOG_DEBUG, + "password - could not identify user"); + return retval; + } + + D(("Got username of %s", user)); + + /* + * Before we do anything else, check to make sure that the user's + * info is in one of the databases we can modify from this module, + * which currently is 'files' and 'nis'. We have to do this because + * getpwnam() doesn't tell you *where* the information it gives you + * came from, nor should it. That's our job. + */ + if (_unix_comesfromsource(pamh, user, 1, on(UNIX_NIS, ctrl)) == 0) { + pam_syslog(pamh, LOG_DEBUG, + "user \"%s\" does not exist in /etc/passwd%s", + user, on(UNIX_NIS, ctrl) ? " or NIS" : ""); + return PAM_USER_UNKNOWN; + } else { + struct passwd *pwd; + _unix_getpwnam(pamh, user, 1, 1, &pwd); + if (pwd == NULL) { + pam_syslog(pamh, LOG_DEBUG, + "user \"%s\" has corrupted passwd entry", + user); + return PAM_USER_UNKNOWN; + } + } + + /* + * This is not an AUTH module! + */ + if (on(UNIX__NONULL, ctrl)) + set(UNIX__NULLOK, ctrl); + + if (on(UNIX__PRELIM, ctrl)) { + /* + * obtain and verify the current password (OLDAUTHTOK) for + * the user. + */ + char *Announce; + + D(("prelim check")); + + if (_unix_blankpasswd(pamh, ctrl, user)) { + return PAM_SUCCESS; + } else if (off(UNIX__IAMROOT, ctrl)) { + /* instruct user what is happening */ + if (asprintf(&Announce, _("Changing password for %s."), + user) < 0) { + pam_syslog(pamh, LOG_CRIT, + "password - out of memory"); + return PAM_BUF_ERR; + } + + lctrl = ctrl; + set(UNIX__OLD_PASSWD, lctrl); + retval = _unix_read_password(pamh, lctrl + ,Announce + ,_("(current) UNIX password: ") + ,NULL + ,_UNIX_OLD_AUTHTOK + ,&pass_old); + free(Announce); + + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, + "password - (old) token not obtained"); + return retval; + } + /* verify that this is the password for this user */ + + retval = _unix_verify_password(pamh, user, pass_old, ctrl); + } else { + D(("process run by root so do nothing this time around")); + pass_old = NULL; + retval = PAM_SUCCESS; /* root doesn't have too */ + } + + if (retval != PAM_SUCCESS) { + D(("Authentication failed")); + pass_old = NULL; + return retval; + } + retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old); + pass_old = NULL; + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_CRIT, + "failed to set PAM_OLDAUTHTOK"); + } + retval = _unix_verify_shadow(pamh,user, ctrl); + if (retval == PAM_AUTHTOK_ERR) { + if (off(UNIX__IAMROOT, ctrl)) + _make_remark(pamh, ctrl, PAM_ERROR_MSG, + _("You must wait longer to change your password")); + else + retval = PAM_SUCCESS; + } + } else if (on(UNIX__UPDATE, ctrl)) { + /* + * tpass is used below to store the _pam_md() return; it + * should be _pam_delete()'d. + */ + + char *tpass = NULL; + int retry = 0; + + /* + * obtain the proposed password + */ + + D(("do update")); + + /* + * get the old token back. NULL was ok only if root [at this + * point we assume that this has already been enforced on a + * previous call to this function]. + */ + + if (off(UNIX_NOT_SET_PASS, ctrl)) { + retval = pam_get_item(pamh, PAM_OLDAUTHTOK + ,&pass_old); + } else { + retval = pam_get_data(pamh, _UNIX_OLD_AUTHTOK + ,&pass_old); + if (retval == PAM_NO_MODULE_DATA) { + retval = PAM_SUCCESS; + pass_old = NULL; + } + } + D(("pass_old [%s]", pass_old)); + + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, "user not authenticated"); + return retval; + } + + D(("get new password now")); + + lctrl = ctrl; + + if (on(UNIX_USE_AUTHTOK, lctrl)) { + set(UNIX_USE_FIRST_PASS, lctrl); + } + retry = 0; + retval = PAM_AUTHTOK_ERR; + while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) { + /* + * use_authtok is to force the use of a previously entered + * password -- needed for pluggable password strength checking + */ + + retval = _unix_read_password(pamh, lctrl + ,NULL + ,_("Enter new UNIX password: ") + ,_("Retype new UNIX password: ") + ,_UNIX_NEW_AUTHTOK + ,&pass_new); + + if (retval != PAM_SUCCESS) { + if (on(UNIX_DEBUG, ctrl)) { + pam_syslog(pamh, LOG_ALERT, + "password - new password not obtained"); + } + pass_old = NULL; /* tidy up */ + return retval; + } + D(("returned to _unix_chauthtok")); + + /* + * At this point we know who the user is and what they + * propose as their new password. Verify that the new + * password is acceptable. + */ + + if (*(const char *)pass_new == '\0') { /* "\0" password = NULL */ + pass_new = NULL; + } + retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, + pass_new, pass_min_len); + + if (retval != PAM_SUCCESS && off(UNIX_NOT_SET_PASS, ctrl)) { + pam_set_item(pamh, PAM_AUTHTOK, NULL); + } + } + + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, + "new password not acceptable"); + pass_new = pass_old = NULL; /* tidy up */ + return retval; + } + if (lock_pwdf() != PAM_SUCCESS) { + return PAM_AUTHTOK_LOCK_BUSY; + } + + if (pass_old) { + retval = _unix_verify_password(pamh, user, pass_old, ctrl); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, "user password changed by another process"); + unlock_pwdf(); + return retval; + } + } + + retval = _unix_verify_shadow(pamh, user, ctrl); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, "user shadow entry expired"); + unlock_pwdf(); + return retval; + } + + retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new, + pass_min_len); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, + "new password not acceptable 2"); + pass_new = pass_old = NULL; /* tidy up */ + unlock_pwdf(); + return retval; + } + + /* + * By reaching here we have approved the passwords and must now + * rebuild the password database file. + */ + + /* + * First we encrypt the new password. + */ + + tpass = create_password_hash(pamh, pass_new, ctrl, rounds); + if (tpass == NULL) { + pam_syslog(pamh, LOG_CRIT, + "crypt() failure or out of memory for password"); + pass_new = pass_old = NULL; /* tidy up */ + unlock_pwdf(); + return PAM_BUF_ERR; + } + + D(("password processed")); + + /* update the password database(s) -- race conditions..? */ + + retval = _do_setpass(pamh, user, pass_old, tpass, ctrl, + remember); + /* _do_setpass has called unlock_pwdf for us */ + + _pam_delete(tpass); + pass_old = pass_new = NULL; + } else { /* something has broken with the module */ + pam_syslog(pamh, LOG_ALERT, + "password received unknown request"); + retval = PAM_ABORT; + } + + D(("retval was %d", retval)); + + return retval; +} diff --git a/modules/pam_unix/pam_unix_sess.c b/modules/pam_unix/pam_unix_sess.c new file mode 100644 index 00000000..d1376732 --- /dev/null +++ b/modules/pam_unix/pam_unix_sess.c @@ -0,0 +1,133 @@ +/* + * $Id$ + * + * Copyright Alexander O. Yuriev, 1996. All rights reserved. + * Copyright Jan Rkorajski, 1999. 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 "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> + +/* indicate the following groups are defined */ + +#ifdef PAM_STATIC +# include "pam_unix_static.h" +#else +# define PAM_SM_SESSION +#endif + +#include <security/_pam_macros.h> +#include <security/pam_modules.h> +#include <security/pam_ext.h> +#include <security/pam_modutil.h> + +#include "support.h" + +/* + * PAM framework looks for these entry-points to pass control to the + * session module. + */ + +int +pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + char *user_name, *service; + unsigned int ctrl; + int retval; + const char *login_name; + + D(("called.")); + + ctrl = _set_ctrl(pamh, flags, NULL, NULL, NULL, argc, argv); + + retval = pam_get_item(pamh, PAM_USER, (void *) &user_name); + if (user_name == NULL || *user_name == '\0' || retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_CRIT, + "open_session - error recovering username"); + return PAM_SESSION_ERR; /* How did we get authenticated with + no username?! */ + } + retval = pam_get_item(pamh, PAM_SERVICE, (void *) &service); + if (service == NULL || *service == '\0' || retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_CRIT, + "open_session - error recovering service"); + return PAM_SESSION_ERR; + } + login_name = pam_modutil_getlogin(pamh); + if (login_name == NULL) { + login_name = ""; + } + pam_syslog(pamh, LOG_INFO, "session opened for user %s by %s(uid=%lu)", + user_name, login_name, (unsigned long)getuid()); + + return PAM_SUCCESS; +} + +int +pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + char *user_name, *service; + unsigned int ctrl; + int retval; + + D(("called.")); + + ctrl = _set_ctrl(pamh, flags, NULL, NULL, NULL, argc, argv); + + retval = pam_get_item(pamh, PAM_USER, (void *) &user_name); + if (user_name == NULL || *user_name == '\0' || retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_CRIT, + "close_session - error recovering username"); + return PAM_SESSION_ERR; /* How did we get authenticated with + no username?! */ + } + retval = pam_get_item(pamh, PAM_SERVICE, (void *) &service); + if (service == NULL || *service == '\0' || retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_CRIT, + "close_session - error recovering service"); + return PAM_SESSION_ERR; + } + pam_syslog(pamh, LOG_INFO, "session closed for user %s", + user_name); + + return PAM_SUCCESS; +} diff --git a/modules/pam_unix/pam_unix_static.c b/modules/pam_unix/pam_unix_static.c new file mode 100644 index 00000000..160268c9 --- /dev/null +++ b/modules/pam_unix/pam_unix_static.c @@ -0,0 +1,23 @@ +#include "config.h" + +#ifdef PAM_STATIC + +#define static extern +#define PAM_SM_ACCOUNT +#define PAM_SM_AUTH +#define PAM_SM_PASSWORD +#define PAM_SM_SESSION +#include "pam_unix_static.h" +#include <security/pam_modules.h> + +struct pam_module _pam_unix_modstruct = { + "pam_unix", + pam_sm_authenticate, + pam_sm_setcred, + pam_sm_acct_mgmt, + pam_sm_open_session, + pam_sm_close_session, + pam_sm_chauthtok, +}; + +#endif diff --git a/modules/pam_unix/pam_unix_static.h b/modules/pam_unix/pam_unix_static.h new file mode 100644 index 00000000..39b05efe --- /dev/null +++ b/modules/pam_unix/pam_unix_static.h @@ -0,0 +1,6 @@ +#define pam_sm_acct_mgmt _pam_unix_sm_acct_mgmt +#define pam_sm_authenticate _pam_unix_sm_authenticate +#define pam_sm_setcred _pam_unix_sm_setcred +#define pam_sm_chauthtok _pam_unix_sm_chauthtok +#define pam_sm_open_session _pam_unix_sm_open_session +#define pam_sm_close_session _pam_unix_sm_close_session diff --git a/modules/pam_unix/passverify.c b/modules/pam_unix/passverify.c new file mode 100644 index 00000000..4840bb2d --- /dev/null +++ b/modules/pam_unix/passverify.c @@ -0,0 +1,1163 @@ +/* + * Copyright information at end of file. + */ +#include "config.h" +#include <security/_pam_macros.h> +#include <security/pam_modules.h> +#include "support.h" +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> +#include <pwd.h> +#include <shadow.h> +#include <syslog.h> +#include <stdarg.h> +#include <signal.h> +#include <errno.h> +#include <time.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <fcntl.h> +#ifdef HAVE_LIBXCRYPT +#include <xcrypt.h> +#elif defined(HAVE_CRYPT_H) +#include <crypt.h> +#endif + +#include "md5.h" +#include "bigcrypt.h" +#include "passverify.h" + +#ifdef WITH_SELINUX +#include <selinux/selinux.h> +#define SELINUX_ENABLED is_selinux_enabled()>0 +#else +#define SELINUX_ENABLED 0 +#endif + +#ifdef HELPER_COMPILE +#define pam_modutil_getpwnam(h,n) getpwnam(n) +#define pam_modutil_getspnam(h,n) getspnam(n) +#define pam_syslog(h,a,b,c) helper_log_err(a,b,c) +#else +#include <security/pam_modutil.h> +#include <security/pam_ext.h> +#endif + +#if defined(USE_LCKPWDF) && !defined(HAVE_LCKPWDF) +# include "./lckpwdf.-c" +#endif + +static void +strip_hpux_aging(char *hash) +{ + static const char valid[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789./"; + if ((*hash != '$') && (strlen(hash) > 13)) { + for (hash += 13; *hash != '\0'; hash++) { + if (strchr(valid, *hash) == NULL) { + *hash = '\0'; + break; + } + } + } +} + +int +verify_pwd_hash(const char *p, char *hash, unsigned int nullok) +{ + size_t hash_len; + char *pp = NULL; + int retval; + D(("called")); + + strip_hpux_aging(hash); + hash_len = strlen(hash); + if (!hash_len) { + /* the stored password is NULL */ + if (nullok) { /* this means we've succeeded */ + D(("user has empty password - access granted")); + retval = PAM_SUCCESS; + } else { + D(("user has empty password - access denied")); + retval = PAM_AUTH_ERR; + } + } else if (!p || *hash == '*' || *hash == '!') { + retval = PAM_AUTH_ERR; + } else { + if (!strncmp(hash, "$1$", 3)) { + pp = Goodcrypt_md5(p, hash); + if (pp && strcmp(pp, hash) != 0) { + _pam_delete(pp); + pp = Brokencrypt_md5(p, hash); + } + } else if (*hash != '$' && hash_len >= 13) { + pp = bigcrypt(p, hash); + if (pp && hash_len == 13 && strlen(pp) > hash_len) { + _pam_overwrite(pp + hash_len); + } + } else { + /* + * Ok, we don't know the crypt algorithm, but maybe + * libcrypt knows about it? We should try it. + */ +#ifdef HAVE_CRYPT_R + struct crypt_data *cdata; + cdata = malloc(sizeof(*cdata)); + if (cdata != NULL) { + cdata->initialized = 0; + pp = x_strdup(crypt_r(p, hash, cdata)); + memset(cdata, '\0', sizeof(*cdata)); + free(cdata); + } +#else + pp = x_strdup(crypt(p, hash)); +#endif + } + p = NULL; /* no longer needed here */ + + /* the moment of truth -- do we agree with the password? */ + D(("comparing state of pp[%s] and hash[%s]", pp, hash)); + + if (pp && strcmp(pp, hash) == 0) { + retval = PAM_SUCCESS; + } else { + retval = PAM_AUTH_ERR; + } + } + + if (pp) + _pam_delete(pp); + D(("done [%d].", retval)); + + return retval; +} + +int +is_pwd_shadowed(const struct passwd *pwd) +{ + if (pwd != NULL) { + if (strcmp(pwd->pw_passwd, "x") == 0) { + return 1; + } + if ((pwd->pw_passwd[0] == '#') && + (pwd->pw_passwd[1] == '#') && + (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0)) { + return 1; + } + } + return 0; +} + +PAMH_ARG_DECL(int get_account_info, + const char *name, struct passwd **pwd, struct spwd **spwdent) +{ + /* UNIX passwords area */ + *pwd = pam_modutil_getpwnam(pamh, name); /* Get password file entry... */ + *spwdent = NULL; + + if (*pwd != NULL) { + if (strcmp((*pwd)->pw_passwd, "*NP*") == 0) + { /* NIS+ */ +#ifdef HELPER_COMPILE + uid_t save_euid, save_uid; + + save_euid = geteuid(); + save_uid = getuid(); + if (save_uid == (*pwd)->pw_uid) + setreuid(save_euid, save_uid); + else { + setreuid(0, -1); + if (setreuid(-1, (*pwd)->pw_uid) == -1) { + setreuid(-1, 0); + setreuid(0, -1); + if(setreuid(-1, (*pwd)->pw_uid) == -1) + return PAM_CRED_INSUFFICIENT; + } + } + + *spwdent = pam_modutil_getspnam(pamh, name); + if (save_uid == (*pwd)->pw_uid) + setreuid(save_uid, save_euid); + else { + setreuid(-1, 0); + setreuid(save_uid, -1); + setreuid(-1, save_euid); + } + + if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL) + return PAM_AUTHINFO_UNAVAIL; +#else + /* we must run helper for NIS+ passwords */ + return PAM_UNIX_RUN_HELPER; +#endif + } else if (is_pwd_shadowed(*pwd)) { + /* + * ...and shadow password file entry for this user, + * if shadowing is enabled + */ +#ifndef HELPER_COMPILE + if (geteuid() || SELINUX_ENABLED) + return PAM_UNIX_RUN_HELPER; +#endif + *spwdent = pam_modutil_getspnam(pamh, name); + if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL) + return PAM_AUTHINFO_UNAVAIL; + } + } else { + return PAM_USER_UNKNOWN; + } + return PAM_SUCCESS; +} + +PAMH_ARG_DECL(int get_pwd_hash, + const char *name, struct passwd **pwd, char **hash) +{ + int retval; + struct spwd *spwdent = NULL; + + retval = get_account_info(PAMH_ARG(name, pwd, &spwdent)); + if (retval != PAM_SUCCESS) { + return retval; + } + + if (spwdent) + *hash = x_strdup(spwdent->sp_pwdp); + else + *hash = x_strdup((*pwd)->pw_passwd); + if (*hash == NULL) + return PAM_BUF_ERR; + + return PAM_SUCCESS; +} + +PAMH_ARG_DECL(int check_shadow_expiry, + struct spwd *spent, int *daysleft) +{ + long int curdays; + *daysleft = -1; + curdays = (long int)(time(NULL) / (60 * 60 * 24)); + D(("today is %d, last change %d", curdays, spent->sp_lstchg)); + if ((curdays >= spent->sp_expire) && (spent->sp_expire != -1)) { + D(("account expired")); + return PAM_ACCT_EXPIRED; + } + if (spent->sp_lstchg == 0) { + D(("need a new password")); + *daysleft = 0; + return PAM_NEW_AUTHTOK_REQD; + } + if (curdays < spent->sp_lstchg) { + pam_syslog(pamh, LOG_DEBUG, + "account %s has password changed in future", + spent->sp_namp); + return PAM_SUCCESS; + } + if ((curdays - spent->sp_lstchg > spent->sp_max) + && (curdays - spent->sp_lstchg > spent->sp_inact) + && (curdays - spent->sp_lstchg > spent->sp_max + spent->sp_inact) + && (spent->sp_max != -1) && (spent->sp_inact != -1)) { + *daysleft = (int)((spent->sp_lstchg + spent->sp_max) - curdays); + D(("authtok expired")); + return PAM_AUTHTOK_EXPIRED; + } + if ((curdays - spent->sp_lstchg > spent->sp_max) && (spent->sp_max != -1)) { + D(("need a new password 2")); + return PAM_NEW_AUTHTOK_REQD; + } + if ((curdays - spent->sp_lstchg > spent->sp_max - spent->sp_warn) + && (spent->sp_max != -1) && (spent->sp_warn != -1)) { + *daysleft = (int)((spent->sp_lstchg + spent->sp_max) - curdays); + D(("warn before expiry")); + } + if ((curdays - spent->sp_lstchg < spent->sp_min) + && (spent->sp_min != -1)) { + /* + * The last password change was too recent. This error will be ignored + * if no password change is attempted. + */ + D(("password change too recent")); + return PAM_AUTHTOK_ERR; + } + return PAM_SUCCESS; +} + +/* passwd/salt conversion macros */ + +#define PW_TMPFILE "/etc/npasswd" +#define SH_TMPFILE "/etc/nshadow" +#define OPW_TMPFILE "/etc/security/nopasswd" + +/* + * i64c - convert an integer to a radix 64 character + */ +static int +i64c(int i) +{ + if (i < 0) + return ('.'); + else if (i > 63) + return ('z'); + if (i == 0) + return ('.'); + if (i == 1) + return ('/'); + if (i >= 2 && i <= 11) + return ('0' - 2 + i); + if (i >= 12 && i <= 37) + return ('A' - 12 + i); + if (i >= 38 && i <= 63) + return ('a' - 38 + i); + return ('\0'); +} + +/* <where> must point to a buffer of at least <length>+1 length */ +static void +crypt_make_salt(char *where, int length) +{ + struct timeval tv; + MD5_CTX ctx; + unsigned char tmp[16]; + unsigned char *src = (unsigned char *)where; + int i; +#ifdef PAM_PATH_RANDOMDEV + int fd; + int rv; + + if ((rv = fd = open(PAM_PATH_RANDOMDEV, O_RDONLY)) != -1) { + while ((rv = read(fd, where, length)) != length && errno == EINTR); + close (fd); + } + if (rv != length) { +#endif + /* + * Code lifted from Marek Michalkiewicz's shadow suite. (CG) + * removed use of static variables (AGM) + * + * will work correctly only for length <= 16 */ + src = tmp; + GoodMD5Init(&ctx); + gettimeofday(&tv, (struct timezone *) 0); + GoodMD5Update(&ctx, (void *) &tv, sizeof tv); + i = getpid(); + GoodMD5Update(&ctx, (void *) &i, sizeof i); + i = clock(); + GoodMD5Update(&ctx, (void *) &i, sizeof i); + GoodMD5Update(&ctx, src, length); + GoodMD5Final(tmp, &ctx); +#ifdef PAM_PATH_RANDOMDEV + } +#endif + for (i = 0; i < length; i++) + *where++ = i64c(src[i] & 077); + *where = '\0'; +} + +char * +crypt_md5_wrapper(const char *pass_new) +{ + unsigned char result[16]; + char *cp = (char *) result; + + cp = stpcpy(cp, "$1$"); /* magic for the MD5 */ + crypt_make_salt(cp, 8); + + /* no longer need cleartext */ + cp = Goodcrypt_md5(pass_new, (const char *) result); + pass_new = NULL; + + return cp; +} + +PAMH_ARG_DECL(char * create_password_hash, + const char *password, unsigned int ctrl, int rounds) +{ + const char *algoid; + char salt[64]; /* contains rounds number + max 16 bytes of salt + algo id */ + char *sp; + + if (on(UNIX_MD5_PASS, ctrl)) { + /* algoid = "$1" */ + return crypt_md5_wrapper(password); + } else if (on(UNIX_BLOWFISH_PASS, ctrl)) { + algoid = "$2a$"; + } else if (on(UNIX_SHA256_PASS, ctrl)) { + algoid = "$5$"; + } else if (on(UNIX_SHA512_PASS, ctrl)) { + algoid = "$6$"; + } else { /* must be crypt/bigcrypt */ + char tmppass[9]; + char *crypted; + + crypt_make_salt(salt, 2); + if (off(UNIX_BIGCRYPT, ctrl) && strlen(password) > 8) { + strncpy(tmppass, password, sizeof(tmppass)-1); + tmppass[sizeof(tmppass)-1] = '\0'; + password = tmppass; + } + crypted = bigcrypt(password, salt); + memset(tmppass, '\0', sizeof(tmppass)); + password = NULL; + return crypted; + } + +#ifdef HAVE_CRYPT_GENSALT_R + if (on(UNIX_BLOWFISH_PASS, ctrl)) { + char entropy[17]; + crypt_make_salt(entropy, sizeof(entropy) - 1); + sp = crypt_gensalt_r (algoid, rounds, + entropy, sizeof(entropy), + salt, sizeof(salt)); + } else { +#endif + sp = stpcpy(salt, algoid); + if (on(UNIX_ALGO_ROUNDS, ctrl)) { + sp += snprintf(sp, sizeof(salt) - 3, "rounds=%u$", rounds); + } + crypt_make_salt(sp, 8); + /* For now be conservative so the resulting hashes + * are not too long. 8 bytes of salt prevents dictionary + * attacks well enough. */ +#ifdef HAVE_CRYPT_GENSALT_R + } +#endif + sp = crypt(password, salt); + if (!sp || strncmp(algoid, sp, strlen(algoid)) != 0) { + /* libxcrypt/libc doesn't know the algorithm, use MD5 */ + pam_syslog(pamh, LOG_ERR, + "Algo %s not supported by the crypto backend, " + "falling back to MD5\n", + on(UNIX_BLOWFISH_PASS, ctrl) ? "blowfish" : + on(UNIX_SHA256_PASS, ctrl) ? "sha256" : + on(UNIX_SHA512_PASS, ctrl) ? "sha512" : algoid); + if(sp) { + memset(sp, '\0', strlen(sp)); + } + return crypt_md5_wrapper(password); + } + + return x_strdup(sp); +} + +#ifdef WITH_SELINUX +int +unix_selinux_confined(void) +{ + static int confined = -1; + int fd; + char tempfile[]="/etc/.pwdXXXXXX"; + + if (confined != -1) + return confined; + + /* cannot be confined without SELinux enabled */ + if (!SELINUX_ENABLED){ + confined = 0; + return confined; + } + + /* let's try opening shadow read only */ + if ((fd=open("/etc/shadow", O_RDONLY)) != -1) { + close(fd); + confined = 0; + return confined; + } + + if (errno == EACCES) { + confined = 1; + return confined; + } + + /* shadow opening failed because of other reasons let's try + creating a file in /etc */ + if ((fd=mkstemp(tempfile)) != -1) { + unlink(tempfile); + close(fd); + confined = 0; + return confined; + } + + confined = 1; + return confined; +} + +#else +int +unix_selinux_confined(void) +{ + return 0; +} +#endif + +#ifdef USE_LCKPWDF +int +lock_pwdf(void) +{ + int i; + int retval; + +#ifndef HELPER_COMPILE + if (unix_selinux_confined()) { + return PAM_SUCCESS; + } +#endif + /* These values for the number of attempts and the sleep time + are, of course, completely arbitrary. + My reading of the PAM docs is that, once pam_chauthtok() has been + called with PAM_UPDATE_AUTHTOK, we are obliged to take any + reasonable steps to make sure the token is updated; so retrying + for 1/10 sec. isn't overdoing it. */ + i=0; + while((retval = lckpwdf()) != 0 && i < 100) { + usleep(1000); + i++; + } + if(retval != 0) { + return PAM_AUTHTOK_LOCK_BUSY; + } + return PAM_SUCCESS; +} + +void +unlock_pwdf(void) +{ +#ifndef HELPER_COMPILE + if (unix_selinux_confined()) { + return; + } +#endif + ulckpwdf(); +} +#else +int +lock_pwdf(void) +{ + return PAM_SUCCESS; +} + +void +unlock_pwdf(void) +{ + return; +} +#endif + +#ifdef HELPER_COMPILE +int +save_old_password(const char *forwho, const char *oldpass, + int howmany) +#else +int +save_old_password(pam_handle_t *pamh, const char *forwho, const char *oldpass, + int howmany) +#endif +{ + static char buf[16384]; + static char nbuf[16384]; + char *s_luser, *s_uid, *s_npas, *s_pas, *pass; + int npas; + FILE *pwfile, *opwfile; + int err = 0; + int oldmask; + int found = 0; + struct passwd *pwd = NULL; + struct stat st; + size_t len = strlen(forwho); +#ifdef WITH_SELINUX + security_context_t prev_context=NULL; +#endif + + if (howmany < 0) { + return PAM_SUCCESS; + } + + if (oldpass == NULL) { + return PAM_SUCCESS; + } + + oldmask = umask(077); + +#ifdef WITH_SELINUX + if (SELINUX_ENABLED) { + security_context_t passwd_context=NULL; + if (getfilecon("/etc/passwd",&passwd_context)<0) { + return PAM_AUTHTOK_ERR; + }; + if (getfscreatecon(&prev_context)<0) { + freecon(passwd_context); + return PAM_AUTHTOK_ERR; + } + if (setfscreatecon(passwd_context)) { + freecon(passwd_context); + freecon(prev_context); + return PAM_AUTHTOK_ERR; + } + freecon(passwd_context); + } +#endif + pwfile = fopen(OPW_TMPFILE, "w"); + umask(oldmask); + if (pwfile == NULL) { + err = 1; + goto done; + } + + opwfile = fopen(OLD_PASSWORDS_FILE, "r"); + if (opwfile == NULL) { + fclose(pwfile); + err = 1; + goto done; + } + + if (fstat(fileno(opwfile), &st) == -1) { + fclose(opwfile); + fclose(pwfile); + err = 1; + goto done; + } + + if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) { + fclose(opwfile); + fclose(pwfile); + err = 1; + goto done; + } + if (fchmod(fileno(pwfile), st.st_mode) == -1) { + fclose(opwfile); + fclose(pwfile); + err = 1; + goto done; + } + + while (fgets(buf, 16380, opwfile)) { + if (!strncmp(buf, forwho, len) && strchr(":,\n", buf[len]) != NULL) { + char *sptr = NULL; + found = 1; + if (howmany == 0) + continue; + buf[strlen(buf) - 1] = '\0'; + s_luser = strtok_r(buf, ":", &sptr); + s_uid = strtok_r(NULL, ":", &sptr); + s_npas = strtok_r(NULL, ":", &sptr); + s_pas = strtok_r(NULL, ":", &sptr); + npas = strtol(s_npas, NULL, 10) + 1; + while (npas > howmany) { + s_pas = strpbrk(s_pas, ","); + if (s_pas != NULL) + s_pas++; + npas--; + } + pass = crypt_md5_wrapper(oldpass); + if (s_pas == NULL) + snprintf(nbuf, sizeof(nbuf), "%s:%s:%d:%s\n", + s_luser, s_uid, npas, pass); + else + snprintf(nbuf, sizeof(nbuf),"%s:%s:%d:%s,%s\n", + s_luser, s_uid, npas, s_pas, pass); + _pam_delete(pass); + if (fputs(nbuf, pwfile) < 0) { + err = 1; + break; + } + } else if (fputs(buf, pwfile) < 0) { + err = 1; + break; + } + } + fclose(opwfile); + + if (!found) { + pwd = pam_modutil_getpwnam(pamh, forwho); + if (pwd == NULL) { + err = 1; + } else { + pass = crypt_md5_wrapper(oldpass); + snprintf(nbuf, sizeof(nbuf), "%s:%lu:1:%s\n", + forwho, (unsigned long)pwd->pw_uid, pass); + _pam_delete(pass); + if (fputs(nbuf, pwfile) < 0) { + err = 1; + } + } + } + + if (fflush(pwfile) || fsync(fileno(pwfile))) { + D(("fflush or fsync error writing entries to old passwords file: %m")); + err = 1; + } + + if (fclose(pwfile)) { + D(("fclose error writing entries to old passwords file: %m")); + err = 1; + } + +done: + if (!err) { + if (rename(OPW_TMPFILE, OLD_PASSWORDS_FILE)) + err = 1; + } +#ifdef WITH_SELINUX + if (SELINUX_ENABLED) { + if (setfscreatecon(prev_context)) { + err = 1; + } + if (prev_context) + freecon(prev_context); + prev_context=NULL; + } +#endif + if (!err) { + return PAM_SUCCESS; + } else { + unlink(OPW_TMPFILE); + return PAM_AUTHTOK_ERR; + } +} + +PAMH_ARG_DECL(int unix_update_passwd, + const char *forwho, const char *towhat) +{ + struct passwd *tmpent = NULL; + struct stat st; + FILE *pwfile, *opwfile; + int err = 1; + int oldmask; +#ifdef WITH_SELINUX + security_context_t prev_context=NULL; +#endif + + oldmask = umask(077); +#ifdef WITH_SELINUX + if (SELINUX_ENABLED) { + security_context_t passwd_context=NULL; + if (getfilecon("/etc/passwd",&passwd_context)<0) { + return PAM_AUTHTOK_ERR; + }; + if (getfscreatecon(&prev_context)<0) { + freecon(passwd_context); + return PAM_AUTHTOK_ERR; + } + if (setfscreatecon(passwd_context)) { + freecon(passwd_context); + freecon(prev_context); + return PAM_AUTHTOK_ERR; + } + freecon(passwd_context); + } +#endif + pwfile = fopen(PW_TMPFILE, "w"); + umask(oldmask); + if (pwfile == NULL) { + err = 1; + goto done; + } + + opwfile = fopen("/etc/passwd", "r"); + if (opwfile == NULL) { + fclose(pwfile); + err = 1; + goto done; + } + + if (fstat(fileno(opwfile), &st) == -1) { + fclose(opwfile); + fclose(pwfile); + err = 1; + goto done; + } + + if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) { + fclose(opwfile); + fclose(pwfile); + err = 1; + goto done; + } + if (fchmod(fileno(pwfile), st.st_mode) == -1) { + fclose(opwfile); + fclose(pwfile); + err = 1; + goto done; + } + + tmpent = fgetpwent(opwfile); + while (tmpent) { + if (!strcmp(tmpent->pw_name, forwho)) { + /* To shut gcc up */ + union { + const char *const_charp; + char *charp; + } assigned_passwd; + assigned_passwd.const_charp = towhat; + + tmpent->pw_passwd = assigned_passwd.charp; + err = 0; + } + if (putpwent(tmpent, pwfile)) { + D(("error writing entry to password file: %m")); + err = 1; + break; + } + tmpent = fgetpwent(opwfile); + } + fclose(opwfile); + + if (fflush(pwfile) || fsync(fileno(pwfile))) { + D(("fflush or fsync error writing entries to password file: %m")); + err = 1; + } + + if (fclose(pwfile)) { + D(("fclose error writing entries to password file: %m")); + err = 1; + } + +done: + if (!err) { + if (!rename(PW_TMPFILE, "/etc/passwd")) + pam_syslog(pamh, + LOG_NOTICE, "password changed for %s", forwho); + else + err = 1; + } +#ifdef WITH_SELINUX + if (SELINUX_ENABLED) { + if (setfscreatecon(prev_context)) { + err = 1; + } + if (prev_context) + freecon(prev_context); + prev_context=NULL; + } +#endif + if (!err) { + return PAM_SUCCESS; + } else { + unlink(PW_TMPFILE); + return PAM_AUTHTOK_ERR; + } +} + +PAMH_ARG_DECL(int unix_update_shadow, + const char *forwho, char *towhat) +{ + struct spwd spwdent, *stmpent = NULL; + struct stat st; + FILE *pwfile, *opwfile; + int err = 0; + int oldmask; + int wroteentry = 0; +#ifdef WITH_SELINUX + security_context_t prev_context=NULL; +#endif + + oldmask = umask(077); + +#ifdef WITH_SELINUX + if (SELINUX_ENABLED) { + security_context_t shadow_context=NULL; + if (getfilecon("/etc/shadow",&shadow_context)<0) { + return PAM_AUTHTOK_ERR; + }; + if (getfscreatecon(&prev_context)<0) { + freecon(shadow_context); + return PAM_AUTHTOK_ERR; + } + if (setfscreatecon(shadow_context)) { + freecon(shadow_context); + freecon(prev_context); + return PAM_AUTHTOK_ERR; + } + freecon(shadow_context); + } +#endif + pwfile = fopen(SH_TMPFILE, "w"); + umask(oldmask); + if (pwfile == NULL) { + err = 1; + goto done; + } + + opwfile = fopen("/etc/shadow", "r"); + if (opwfile == NULL) { + fclose(pwfile); + err = 1; + goto done; + } + + if (fstat(fileno(opwfile), &st) == -1) { + fclose(opwfile); + fclose(pwfile); + err = 1; + goto done; + } + + if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) { + fclose(opwfile); + fclose(pwfile); + err = 1; + goto done; + } + if (fchmod(fileno(pwfile), st.st_mode) == -1) { + fclose(opwfile); + fclose(pwfile); + err = 1; + goto done; + } + + stmpent = fgetspent(opwfile); + while (stmpent) { + + if (!strcmp(stmpent->sp_namp, forwho)) { + stmpent->sp_pwdp = towhat; + stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24); + if (stmpent->sp_lstchg == 0) + stmpent->sp_lstchg = -1; /* Don't request passwort change + only because time isn't set yet. */ + wroteentry = 1; + D(("Set password %s for %s", stmpent->sp_pwdp, forwho)); + } + + if (putspent(stmpent, pwfile)) { + D(("error writing entry to shadow file: %m")); + err = 1; + break; + } + + stmpent = fgetspent(opwfile); + } + + fclose(opwfile); + + if (!wroteentry && !err) { + spwdent.sp_namp = forwho; + spwdent.sp_pwdp = towhat; + spwdent.sp_lstchg = time(NULL) / (60 * 60 * 24); + if (spwdent.sp_lstchg == 0) + spwdent.sp_lstchg = -1; /* Don't request passwort change + only because time isn't set yet. */ + spwdent.sp_min = spwdent.sp_max = spwdent.sp_warn = spwdent.sp_inact = + spwdent.sp_expire = -1; + spwdent.sp_flag = (unsigned long)-1l; + if (putspent(&spwdent, pwfile)) { + D(("error writing entry to shadow file: %m")); + err = 1; + } + } + + if (fflush(pwfile) || fsync(fileno(pwfile))) { + D(("fflush or fsync error writing entries to shadow file: %m")); + err = 1; + } + + if (fclose(pwfile)) { + D(("fclose error writing entries to shadow file: %m")); + err = 1; + } + + done: + if (!err) { + if (!rename(SH_TMPFILE, "/etc/shadow")) + pam_syslog(pamh, + LOG_NOTICE, "password changed for %s", forwho); + else + err = 1; + } + +#ifdef WITH_SELINUX + if (SELINUX_ENABLED) { + if (setfscreatecon(prev_context)) { + err = 1; + } + if (prev_context) + freecon(prev_context); + prev_context=NULL; + } +#endif + + if (!err) { + return PAM_SUCCESS; + } else { + unlink(SH_TMPFILE); + return PAM_AUTHTOK_ERR; + } +} + +#ifdef HELPER_COMPILE + +int +helper_verify_password(const char *name, const char *p, int nullok) +{ + struct passwd *pwd = NULL; + char *salt = NULL; + int retval; + + retval = get_pwd_hash(name, &pwd, &salt); + + if (pwd == NULL || salt == NULL) { + helper_log_err(LOG_WARNING, "check pass; user unknown"); + retval = PAM_USER_UNKNOWN; + } else { + retval = verify_pwd_hash(p, salt, nullok); + } + + if (salt) { + _pam_overwrite(salt); + _pam_drop(salt); + } + + p = NULL; /* no longer needed here */ + + return retval; +} + +void +helper_log_err(int err, const char *format, ...) +{ + va_list args; + + va_start(args, format); + openlog(HELPER_COMPILE, LOG_CONS | LOG_PID, LOG_AUTHPRIV); + vsyslog(err, format, args); + va_end(args); + closelog(); +} + +static void +su_sighandler(int sig) +{ +#ifndef SA_RESETHAND + /* emulate the behaviour of the SA_RESETHAND flag */ + if ( sig == SIGILL || sig == SIGTRAP || sig == SIGBUS || sig = SIGSERV ) { + struct sigaction sa; + memset(&sa, '\0', sizeof(sa)); + sa.sa_handler = SIG_DFL; + sigaction(sig, &sa, NULL); + } +#endif + if (sig > 0) { + _exit(sig); + } +} + +void +setup_signals(void) +{ + struct sigaction action; /* posix signal structure */ + + /* + * Setup signal handlers + */ + (void) memset((void *) &action, 0, sizeof(action)); + action.sa_handler = su_sighandler; +#ifdef SA_RESETHAND + action.sa_flags = SA_RESETHAND; +#endif + (void) sigaction(SIGILL, &action, NULL); + (void) sigaction(SIGTRAP, &action, NULL); + (void) sigaction(SIGBUS, &action, NULL); + (void) sigaction(SIGSEGV, &action, NULL); + action.sa_handler = SIG_IGN; + action.sa_flags = 0; + (void) sigaction(SIGTERM, &action, NULL); + (void) sigaction(SIGHUP, &action, NULL); + (void) sigaction(SIGINT, &action, NULL); + (void) sigaction(SIGQUIT, &action, NULL); +} + +char * +getuidname(uid_t uid) +{ + struct passwd *pw; + static char username[256]; + + pw = getpwuid(uid); + if (pw == NULL) + return NULL; + + strncpy(username, pw->pw_name, sizeof(username)); + username[sizeof(username) - 1] = '\0'; + + return username; +} + +int +read_passwords(int fd, int npass, char **passwords) +{ + int rbytes = 0; + int offset = 0; + int i = 0; + char *pptr; + while (npass > 0) { + rbytes = read(fd, passwords[i]+offset, MAXPASS-offset); + + if (rbytes < 0) { + if (errno == EINTR) continue; + break; + } + if (rbytes == 0) + break; + + while (npass > 0 && (pptr=memchr(passwords[i]+offset, '\0', rbytes)) + != NULL) { + rbytes -= pptr - (passwords[i]+offset) + 1; + i++; + offset = 0; + npass--; + if (rbytes > 0) { + if (npass > 0) + memcpy(passwords[i], pptr+1, rbytes); + memset(pptr+1, '\0', rbytes); + } + } + offset += rbytes; + } + + /* clear up */ + if (offset > 0 && npass > 0) { + memset(passwords[i], '\0', offset); + } + + return i; +} + +#endif +/* ****************************************************************** * + * Copyright (c) Jan Rêkorajski 1999. + * Copyright (c) Andrew G. Morgan 1996-8. + * Copyright (c) Alex O. Yuriev, 1996. + * Copyright (c) Cristian Gafton 1996. + * Copyright (c) Red Hat, Inc. 1996, 2007, 2008. + * + * 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. + */ diff --git a/modules/pam_unix/passverify.h b/modules/pam_unix/passverify.h new file mode 100644 index 00000000..3de67593 --- /dev/null +++ b/modules/pam_unix/passverify.h @@ -0,0 +1,119 @@ +/* + * Copyright information at end of file. + */ + +#include <sys/types.h> +#include <pwd.h> +#include <security/pam_modules.h> + +#define PAM_UNIX_RUN_HELPER PAM_CRED_INSUFFICIENT + +#define MAXPASS 200 /* the maximum length of a password */ + +#define OLD_PASSWORDS_FILE "/etc/security/opasswd" + +int +verify_pwd_hash(const char *p, char *hash, unsigned int nullok); + +int +is_pwd_shadowed(const struct passwd *pwd); + +char * +crypt_md5_wrapper(const char *pass_new); + +int +unix_selinux_confined(void); + +int +lock_pwdf(void); + +void +unlock_pwdf(void); + +#ifdef HELPER_COMPILE +int +save_old_password(const char *forwho, const char *oldpass, + int howmany); +#else +int +save_old_password(pam_handle_t *pamh, const char *forwho, const char *oldpass, + int howmany); +#endif + +#ifdef HELPER_COMPILE +void +helper_log_err(int err, const char *format,...); + +int +helper_verify_password(const char *name, const char *p, int nullok); + +void +setup_signals(void); + +char * +getuidname(uid_t uid); + +int +read_passwords(int fd, int npass, char **passwords); +#endif + +#ifdef HELPER_COMPILE +#define PAMH_ARG_DECL(fname, ...) fname(__VA_ARGS__) +#define PAMH_ARG(...) __VA_ARGS__ +#else +#define PAMH_ARG_DECL(fname, ...) fname(pam_handle_t *pamh, __VA_ARGS__) +#define PAMH_ARG(...) pamh, __VA_ARGS__ +#endif + +PAMH_ARG_DECL(char * create_password_hash, + const char *password, unsigned int ctrl, int rounds); + +PAMH_ARG_DECL(int get_account_info, + const char *name, struct passwd **pwd, struct spwd **spwdent); + +PAMH_ARG_DECL(int get_pwd_hash, + const char *name, struct passwd **pwd, char **hash); + +PAMH_ARG_DECL(int check_shadow_expiry, + struct spwd *spent, int *daysleft); + +PAMH_ARG_DECL(int unix_update_passwd, + const char *forwho, const char *towhat); + +PAMH_ARG_DECL(int unix_update_shadow, + const char *forwho, char *towhat); + +/* ****************************************************************** * + * Copyright (c) Red Hat, Inc. 2007. + * + * 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. + */ diff --git a/modules/pam_unix/support.c b/modules/pam_unix/support.c new file mode 100644 index 00000000..19d72e66 --- /dev/null +++ b/modules/pam_unix/support.c @@ -0,0 +1,1050 @@ +/* + * Copyright information at end of file. + */ + +#include "config.h" + +#include <stdlib.h> +#include <unistd.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <malloc.h> +#include <pwd.h> +#include <shadow.h> +#include <limits.h> +#include <utmp.h> +#include <errno.h> +#include <signal.h> +#include <ctype.h> +#include <syslog.h> +#include <sys/resource.h> +#ifdef HAVE_RPCSVC_YPCLNT_H +#include <rpcsvc/ypclnt.h> +#endif + +#include <security/_pam_macros.h> +#include <security/pam_modules.h> +#include <security/pam_ext.h> +#include <security/pam_modutil.h> + +#include "support.h" +#include "passverify.h" +#ifdef WITH_SELINUX +#include <selinux/selinux.h> +#define SELINUX_ENABLED is_selinux_enabled()>0 +#else +#define SELINUX_ENABLED 0 +#endif + +static char * +search_key (const char *key, const char *filename) +{ + FILE *fp; + char *buf = NULL; + size_t buflen = 0; + char *retval = NULL; + + fp = fopen (filename, "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; + + 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; + + if (cp[strlen (cp) - 1] == '\n') + cp[strlen (cp) - 1] = '\0'; + + tmp = strsep (&cp, " \t="); + if (cp != NULL) + while (isspace ((int)*cp) || *cp == '=') + ++cp; + + if (strcasecmp (tmp, key) == 0) + { + retval = strdup (cp); + break; + } + } + fclose (fp); + + free (buf); + + return retval; +} + + +/* this is a front-end for module-application conversations */ + +int _make_remark(pam_handle_t * pamh, unsigned int ctrl, + int type, const char *text) +{ + int retval = PAM_SUCCESS; + + if (off(UNIX__QUIET, ctrl)) { + retval = pam_prompt(pamh, type, NULL, "%s", text); + } + return retval; +} + +/* + * set the control flags for the UNIX module. + */ + +int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int *rounds, + int *pass_min_len, int argc, const char **argv) +{ + unsigned int ctrl; + char *val; + int j; + + D(("called.")); + + ctrl = UNIX_DEFAULTS; /* the default selection of options */ + + /* set some flags manually */ + + if (getuid() == 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) { + D(("IAMROOT")); + set(UNIX__IAMROOT, ctrl); + } + if (flags & PAM_UPDATE_AUTHTOK) { + D(("UPDATE_AUTHTOK")); + set(UNIX__UPDATE, ctrl); + } + if (flags & PAM_PRELIM_CHECK) { + D(("PRELIM_CHECK")); + set(UNIX__PRELIM, ctrl); + } + if (flags & PAM_SILENT) { + D(("SILENT")); + set(UNIX__QUIET, ctrl); + } + + /* preset encryption method with value from /etc/login.defs */ + val = search_key ("ENCRYPT_METHOD", LOGIN_DEFS); + if (val) { + for (j = 0; j < UNIX_CTRLS_; ++j) { + if (unix_args[j].token && unix_args[j].is_hash_algo + && !strncasecmp(val, unix_args[j].token, strlen(unix_args[j].token))) { + break; + } + } + if (j >= UNIX_CTRLS_) { + pam_syslog(pamh, LOG_WARNING, "unrecognized ENCRYPT_METHOD value [%s]", val); + } else { + ctrl &= unix_args[j].mask; /* for turning things off */ + ctrl |= unix_args[j].flag; /* for turning things on */ + } + free (val); + + /* read number of rounds for crypt algo */ + if (rounds && (on(UNIX_SHA256_PASS, ctrl) || on(UNIX_SHA512_PASS, ctrl))) { + val=search_key ("SHA_CRYPT_MAX_ROUNDS", LOGIN_DEFS); + + if (val) { + *rounds = strtol(val, NULL, 10); + free (val); + } + } + } + + /* now parse the arguments to this module */ + + for (; argc-- > 0; ++argv) { + + D(("pam_unix arg: %s", *argv)); + + for (j = 0; j < UNIX_CTRLS_; ++j) { + if (unix_args[j].token + && !strncmp(*argv, unix_args[j].token, strlen(unix_args[j].token))) { + break; + } + } + + if (j >= UNIX_CTRLS_) { + pam_syslog(pamh, LOG_ERR, + "unrecognized option [%s]", *argv); + } else { + /* special cases */ + if (j == UNIX_REMEMBER_PASSWD) { + if (remember == NULL) { + pam_syslog(pamh, LOG_ERR, + "option remember not allowed for this module type"); + continue; + } + *remember = strtol(*argv + 9, NULL, 10); + if ((*remember == INT_MIN) || (*remember == INT_MAX)) + *remember = -1; + if (*remember > 400) + *remember = 400; + } else if (j == UNIX_MIN_PASS_LEN) { + if (pass_min_len == NULL) { + pam_syslog(pamh, LOG_ERR, + "option minlen not allowed for this module type"); + continue; + } + *pass_min_len = atoi(*argv + 7); + } else if (j == UNIX_ALGO_ROUNDS) { + if (rounds == NULL) { + pam_syslog(pamh, LOG_ERR, + "option rounds not allowed for this module type"); + continue; + } + *rounds = strtol(*argv + 7, NULL, 10); + } + + ctrl &= unix_args[j].mask; /* for turning things off */ + ctrl |= unix_args[j].flag; /* for turning things on */ + } + } + + if (UNIX_DES_CRYPT(ctrl) + && pass_min_len && *pass_min_len > 8) + { + pam_syslog (pamh, LOG_NOTICE, "Password minlen reset to 8 characters"); + *pass_min_len = 8; + } + + if (flags & PAM_DISALLOW_NULL_AUTHTOK) { + D(("DISALLOW_NULL_AUTHTOK")); + set(UNIX__NONULL, ctrl); + } + + /* Set default rounds for blowfish */ + if (on(UNIX_BLOWFISH_PASS, ctrl) && off(UNIX_ALGO_ROUNDS, ctrl) && rounds != NULL) { + *rounds = 5; + set(UNIX_ALGO_ROUNDS, ctrl); + } + + /* Enforce sane "rounds" values */ + if (on(UNIX_ALGO_ROUNDS, ctrl)) { + if (on(UNIX_BLOWFISH_PASS, ctrl)) { + if (*rounds < 4 || *rounds > 31) + *rounds = 5; + } else if (on(UNIX_SHA256_PASS, ctrl) || on(UNIX_SHA512_PASS, ctrl)) { + if ((*rounds < 1000) || (*rounds == INT_MAX)) + /* don't care about bogus values */ + unset(UNIX_ALGO_ROUNDS, ctrl); + if (*rounds >= 10000000) + *rounds = 9999999; + } + } + + /* auditing is a more sensitive version of debug */ + + if (on(UNIX_AUDIT, ctrl)) { + set(UNIX_DEBUG, ctrl); + } + /* return the set of flags */ + + D(("done.")); + return ctrl; +} + +static void _cleanup(pam_handle_t * pamh UNUSED, void *x, int error_status UNUSED) +{ + _pam_delete(x); +} + +/* ************************************************************** * + * Useful non-trivial functions * + * ************************************************************** */ + + /* + * the following is used to keep track of the number of times a user fails + * to authenticate themself. + */ + +#define FAIL_PREFIX "-UN*X-FAIL-" +#define UNIX_MAX_RETRIES 3 + +struct _pam_failed_auth { + char *user; /* user that's failed to be authenticated */ + char *name; /* attempt from user with name */ + int uid; /* uid of calling user */ + int euid; /* euid of calling process */ + int count; /* number of failures so far */ +}; + +#ifndef PAM_DATA_REPLACE +#error "Need to get an updated libpam 0.52 or better" +#endif + +static void _cleanup_failures(pam_handle_t * pamh, void *fl, int err) +{ + int quiet; + const void *service = NULL; + const void *ruser = NULL; + const void *rhost = NULL; + const void *tty = NULL; + struct _pam_failed_auth *failure; + + D(("called")); + + quiet = err & PAM_DATA_SILENT; /* should we log something? */ + err &= PAM_DATA_REPLACE; /* are we just replacing data? */ + failure = (struct _pam_failed_auth *) fl; + + if (failure != NULL) { + + if (!quiet && !err) { /* under advisement from Sun,may go away */ + + /* log the number of authentication failures */ + if (failure->count > 1) { + (void) pam_get_item(pamh, PAM_SERVICE, + &service); + (void) pam_get_item(pamh, PAM_RUSER, + &ruser); + (void) pam_get_item(pamh, PAM_RHOST, + &rhost); + (void) pam_get_item(pamh, PAM_TTY, + &tty); + pam_syslog(pamh, LOG_NOTICE, + "%d more authentication failure%s; " + "logname=%s uid=%d euid=%d " + "tty=%s ruser=%s rhost=%s " + "%s%s", + failure->count - 1, failure->count == 2 ? "" : "s", + failure->name, failure->uid, failure->euid, + tty ? (const char *)tty : "", ruser ? (const char *)ruser : "", + rhost ? (const char *)rhost : "", + (failure->user && failure->user[0] != '\0') + ? " user=" : "", failure->user + ); + + if (failure->count > UNIX_MAX_RETRIES) { + pam_syslog(pamh, LOG_ALERT, + "service(%s) ignoring max retries; %d > %d", + service == NULL ? "**unknown**" : (const char *)service, + failure->count, + UNIX_MAX_RETRIES); + } + } + } + _pam_delete(failure->user); /* tidy up */ + _pam_delete(failure->name); /* tidy up */ + free(failure); + } +} + +/* + * _unix_getpwnam() searches only /etc/passwd and NIS to find user information + */ +static void _unix_cleanup(pam_handle_t *pamh UNUSED, void *data, int error_status UNUSED) +{ + free(data); +} + +int _unix_getpwnam(pam_handle_t *pamh, const char *name, + int files, int nis, struct passwd **ret) +{ + FILE *passwd; + char buf[16384]; + int matched = 0, buflen; + char *slogin, *spasswd, *suid, *sgid, *sgecos, *shome, *sshell, *p; + + memset(buf, 0, sizeof(buf)); + + if (!matched && files) { + int userlen = strlen(name); + passwd = fopen("/etc/passwd", "r"); + if (passwd != NULL) { + while (fgets(buf, sizeof(buf), passwd) != NULL) { + if ((buf[userlen] == ':') && + (strncmp(name, buf, userlen) == 0)) { + p = buf + strlen(buf) - 1; + while (isspace(*p) && (p >= buf)) { + *p-- = '\0'; + } + matched = 1; + break; + } + } + fclose(passwd); + } + } + +#if defined(HAVE_YP_GET_DEFAULT_DOMAIN) && defined (HAVE_YP_BIND) && defined (HAVE_YP_MATCH) && defined (HAVE_YP_UNBIND) + if (!matched && nis) { + char *userinfo = NULL, *domain = NULL; + int len = 0, i; + len = yp_get_default_domain(&domain); + if (len == YPERR_SUCCESS) { + len = yp_bind(domain); + } + if (len == YPERR_SUCCESS) { + i = yp_match(domain, "passwd.byname", name, + strlen(name), &userinfo, &len); + yp_unbind(domain); + if ((i == YPERR_SUCCESS) && ((size_t)len < sizeof(buf))) { + strncpy(buf, userinfo, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; + matched = 1; + } + } + } +#else + /* we don't have NIS support, make compiler happy. */ + nis = 0; +#endif + + if (matched && (ret != NULL)) { + *ret = NULL; + + slogin = buf; + + spasswd = strchr(slogin, ':'); + if (spasswd == NULL) { + return matched; + } + *spasswd++ = '\0'; + + suid = strchr(spasswd, ':'); + if (suid == NULL) { + return matched; + } + *suid++ = '\0'; + + sgid = strchr(suid, ':'); + if (sgid == NULL) { + return matched; + } + *sgid++ = '\0'; + + sgecos = strchr(sgid, ':'); + if (sgecos == NULL) { + return matched; + } + *sgecos++ = '\0'; + + shome = strchr(sgecos, ':'); + if (shome == NULL) { + return matched; + } + *shome++ = '\0'; + + sshell = strchr(shome, ':'); + if (sshell == NULL) { + return matched; + } + *sshell++ = '\0'; + + buflen = sizeof(struct passwd) + + strlen(slogin) + 1 + + strlen(spasswd) + 1 + + strlen(sgecos) + 1 + + strlen(shome) + 1 + + strlen(sshell) + 1; + *ret = malloc(buflen); + if (*ret == NULL) { + return matched; + } + memset(*ret, '\0', buflen); + + (*ret)->pw_uid = strtol(suid, &p, 10); + if ((strlen(suid) == 0) || (*p != '\0')) { + free(*ret); + *ret = NULL; + return matched; + } + + (*ret)->pw_gid = strtol(sgid, &p, 10); + if ((strlen(sgid) == 0) || (*p != '\0')) { + free(*ret); + *ret = NULL; + return matched; + } + + p = ((char*)(*ret)) + sizeof(struct passwd); + (*ret)->pw_name = strcpy(p, slogin); + p += strlen(p) + 1; + (*ret)->pw_passwd = strcpy(p, spasswd); + p += strlen(p) + 1; + (*ret)->pw_gecos = strcpy(p, sgecos); + p += strlen(p) + 1; + (*ret)->pw_dir = strcpy(p, shome); + p += strlen(p) + 1; + (*ret)->pw_shell = strcpy(p, sshell); + + snprintf(buf, sizeof(buf), "_pam_unix_getpwnam_%s", name); + + if (pam_set_data(pamh, buf, + *ret, _unix_cleanup) != PAM_SUCCESS) { + free(*ret); + *ret = NULL; + } + } + + return matched; +} + +/* + * _unix_comsefromsource() is a quick check to see if information about a given + * user comes from a particular source (just files and nis for now) + * + */ +int _unix_comesfromsource(pam_handle_t *pamh, + const char *name, int files, int nis) +{ + return _unix_getpwnam(pamh, name, files, nis, NULL); +} + +/* + * verify the password of a user + */ + +#include <sys/types.h> +#include <sys/wait.h> + +static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd, + unsigned int ctrl, const char *user) +{ + int retval, child, fds[2]; + struct sigaction newsa, oldsa; + + D(("called.")); + /* create a pipe for the password */ + if (pipe(fds) != 0) { + D(("could not make pipe")); + return PAM_AUTH_ERR; + } + + if (off(UNIX_NOREAP, ctrl)) { + /* + * This code arranges that the demise of the child does not cause + * the application to receive a signal it is not expecting - which + * may kill the application or worse. + * + * The "noreap" module argument is provided so that the admin can + * override this behavior. + */ + memset(&newsa, '\0', sizeof(newsa)); + newsa.sa_handler = SIG_DFL; + sigaction(SIGCHLD, &newsa, &oldsa); + } + + /* fork */ + child = fork(); + if (child == 0) { + int i=0; + struct rlimit rlim; + static char *envp[] = { NULL }; + char *args[] = { NULL, NULL, NULL, NULL }; + + /* XXX - should really tidy up PAM here too */ + + /* reopen stdin as pipe */ + dup2(fds[0], STDIN_FILENO); + + if (getrlimit(RLIMIT_NOFILE,&rlim)==0) { + if (rlim.rlim_max >= MAX_FD_NO) + rlim.rlim_max = MAX_FD_NO; + for (i=0; i < (int)rlim.rlim_max; i++) { + if (i != STDIN_FILENO) + close(i); + } + } + + if (geteuid() == 0) { + /* must set the real uid to 0 so the helper will not error + out if pam is called from setuid binary (su, sudo...) */ + if (setuid(0) == -1) { + D(("setuid failed")); + _exit(PAM_AUTHINFO_UNAVAIL); + } + } + + /* exec binary helper */ + args[0] = strdup(CHKPWD_HELPER); + args[1] = x_strdup(user); + if (off(UNIX__NONULL, ctrl)) { /* this means we've succeeded */ + args[2]=strdup("nullok"); + } else { + args[2]=strdup("nonull"); + } + + execve(CHKPWD_HELPER, args, envp); + + /* should not get here: exit with error */ + D(("helper binary is not available")); + _exit(PAM_AUTHINFO_UNAVAIL); + } else if (child > 0) { + /* wait for child */ + /* if the stored password is NULL */ + int rc=0; + if (passwd != NULL) { /* send the password to the child */ + if (write(fds[1], passwd, strlen(passwd)+1) == -1) { + pam_syslog (pamh, LOG_ERR, "Cannot send password to helper: %m"); + retval = PAM_AUTH_ERR; + } + passwd = NULL; + } else { /* blank password */ + if (write(fds[1], "", 1) == -1) { + pam_syslog (pamh, LOG_ERR, "Cannot send password to helper: %m"); + retval = PAM_AUTH_ERR; + } + } + close(fds[0]); /* close here to avoid possible SIGPIPE above */ + close(fds[1]); + /* wait for helper to complete: */ + while ((rc=waitpid(child, &retval, 0)) < 0 && errno == EINTR); + if (rc<0) { + pam_syslog(pamh, LOG_ERR, "unix_chkpwd waitpid returned %d: %m", rc); + retval = PAM_AUTH_ERR; + } else if (!WIFEXITED(retval)) { + pam_syslog(pamh, LOG_ERR, "unix_chkpwd abnormal exit: %d", retval); + retval = PAM_AUTH_ERR; + } else { + retval = WEXITSTATUS(retval); + } + } else { + D(("fork failed")); + close(fds[0]); + close(fds[1]); + retval = PAM_AUTH_ERR; + } + + if (off(UNIX_NOREAP, ctrl)) { + sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */ + } + + D(("returning %d", retval)); + return retval; +} + +/* + * _unix_blankpasswd() is a quick check for a blank password + * + * returns TRUE if user does not have a password + * - to avoid prompting for one in such cases (CG) + */ + +int +_unix_blankpasswd (pam_handle_t *pamh, unsigned int ctrl, const char *name) +{ + struct passwd *pwd = NULL; + char *salt = NULL; + int retval; + + D(("called")); + + /* + * This function does not have to be too smart if something goes + * wrong, return FALSE and let this case to be treated somewhere + * else (CG) + */ + + if (on(UNIX__NONULL, ctrl)) + return 0; /* will fail but don't let on yet */ + + /* UNIX passwords area */ + + retval = get_pwd_hash(pamh, name, &pwd, &salt); + + if (retval == PAM_UNIX_RUN_HELPER) { + /* salt will not be set here so we can return immediately */ + if (_unix_run_helper_binary(pamh, NULL, ctrl, name) == PAM_SUCCESS) + return 1; + else + return 0; + } + + /* Does this user have a password? */ + if (salt == NULL) { + retval = 0; + } else { + if (strlen(salt) == 0) + retval = 1; + else + retval = 0; + } + + /* tidy up */ + + if (salt) + _pam_delete(salt); + + return retval; +} + +int _unix_verify_password(pam_handle_t * pamh, const char *name + ,const char *p, unsigned int ctrl) +{ + struct passwd *pwd = NULL; + char *salt = NULL; + char *data_name; + int retval; + + + D(("called")); + +#ifdef HAVE_PAM_FAIL_DELAY + if (off(UNIX_NODELAY, ctrl)) { + D(("setting delay")); + (void) pam_fail_delay(pamh, 2000000); /* 2 sec delay for on failure */ + } +#endif + + /* locate the entry for this user */ + + D(("locating user's record")); + + retval = get_pwd_hash(pamh, name, &pwd, &salt); + + data_name = (char *) malloc(sizeof(FAIL_PREFIX) + strlen(name)); + if (data_name == NULL) { + pam_syslog(pamh, LOG_CRIT, "no memory for data-name"); + } else { + strcpy(data_name, FAIL_PREFIX); + strcpy(data_name + sizeof(FAIL_PREFIX) - 1, name); + } + + if (retval != PAM_SUCCESS) { + if (retval == PAM_UNIX_RUN_HELPER) { + D(("running helper binary")); + retval = _unix_run_helper_binary(pamh, p, ctrl, name); + } else { + D(("user's record unavailable")); + p = NULL; + if (on(UNIX_AUDIT, ctrl)) { + /* this might be a typo and the user has given a password + instead of a username. Careful with this. */ + pam_syslog(pamh, LOG_WARNING, + "check pass; user (%s) unknown", name); + } else { + name = NULL; + if (on(UNIX_DEBUG, ctrl) || pwd == NULL) { + pam_syslog(pamh, LOG_WARNING, + "check pass; user unknown"); + } else { + /* don't log failure as another pam module can succeed */ + goto cleanup; + } + } + } + } else { + retval = verify_pwd_hash(p, salt, off(UNIX__NONULL, ctrl)); + } + + if (retval == PAM_SUCCESS) { + if (data_name) /* reset failures */ + pam_set_data(pamh, data_name, NULL, _cleanup_failures); + } else { + if (data_name != NULL) { + struct _pam_failed_auth *new = NULL; + const struct _pam_failed_auth *old = NULL; + + /* get a failure recorder */ + + new = (struct _pam_failed_auth *) + malloc(sizeof(struct _pam_failed_auth)); + + if (new != NULL) { + + const char *login_name; + const void *void_old; + + + login_name = pam_modutil_getlogin(pamh); + if (login_name == NULL) { + login_name = ""; + } + + new->user = x_strdup(name ? name : ""); + new->uid = getuid(); + new->euid = geteuid(); + new->name = x_strdup(login_name); + + /* any previous failures for this user ? */ + if (pam_get_data(pamh, data_name, &void_old) + == PAM_SUCCESS) + old = void_old; + else + old = NULL; + + if (old != NULL) { + new->count = old->count + 1; + if (new->count >= UNIX_MAX_RETRIES) { + retval = PAM_MAXTRIES; + } + } else { + const void *service=NULL; + const void *ruser=NULL; + const void *rhost=NULL; + const void *tty=NULL; + + (void) pam_get_item(pamh, PAM_SERVICE, + &service); + (void) pam_get_item(pamh, PAM_RUSER, + &ruser); + (void) pam_get_item(pamh, PAM_RHOST, + &rhost); + (void) pam_get_item(pamh, PAM_TTY, + &tty); + + pam_syslog(pamh, LOG_NOTICE, + "authentication failure; " + "logname=%s uid=%d euid=%d " + "tty=%s ruser=%s rhost=%s " + "%s%s", + new->name, new->uid, new->euid, + tty ? (const char *)tty : "", + ruser ? (const char *)ruser : "", + rhost ? (const char *)rhost : "", + (new->user && new->user[0] != '\0') + ? " user=" : "", + new->user + ); + new->count = 1; + } + + pam_set_data(pamh, data_name, new, _cleanup_failures); + + } else { + pam_syslog(pamh, LOG_CRIT, + "no memory for failure recorder"); + } + } + } + +cleanup: + if (data_name) + _pam_delete(data_name); + if (salt) + _pam_delete(salt); + + D(("done [%d].", retval)); + + return retval; +} + +/* + * obtain a password from the user + */ + +int _unix_read_password(pam_handle_t * pamh + ,unsigned int ctrl + ,const char *comment + ,const char *prompt1 + ,const char *prompt2 + ,const char *data_name + ,const void **pass) +{ + int authtok_flag; + int retval = PAM_SUCCESS; + char *token; + + D(("called")); + + /* + * make sure nothing inappropriate gets returned + */ + + *pass = token = NULL; + + /* + * which authentication token are we getting? + */ + + authtok_flag = on(UNIX__OLD_PASSWD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK; + + /* + * should we obtain the password from a PAM item ? + */ + + if (on(UNIX_TRY_FIRST_PASS, ctrl) || on(UNIX_USE_FIRST_PASS, ctrl)) { + retval = pam_get_item(pamh, authtok_flag, pass); + if (retval != PAM_SUCCESS) { + /* very strange. */ + pam_syslog(pamh, LOG_ALERT, + "pam_get_item returned error to unix-read-password" + ); + return retval; + } else if (*pass != NULL) { /* we have a password! */ + return PAM_SUCCESS; + } else if (on(UNIX_USE_AUTHTOK, ctrl) + && off(UNIX__OLD_PASSWD, ctrl)) { + return PAM_AUTHTOK_ERR; + } else if (on(UNIX_USE_FIRST_PASS, ctrl)) { + return PAM_AUTHTOK_RECOVERY_ERR; /* didn't work */ + } + } + /* + * getting here implies we will have to get the password from the + * user directly. + */ + + { + int replies=1; + char *resp[2] = { NULL, NULL }; + + if (comment != NULL && off(UNIX__QUIET, ctrl)) { + retval = pam_info(pamh, "%s", comment); + } + + if (retval == PAM_SUCCESS) { + retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, + &resp[0], "%s", prompt1); + + if (retval == PAM_SUCCESS && prompt2 != NULL) { + retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, + &resp[1], "%s", prompt2); + ++replies; + } + } + + if (resp[0] != NULL && resp[replies-1] != NULL) { + /* interpret the response */ + + if (retval == PAM_SUCCESS) { /* a good conversation */ + + token = resp[0]; + if (token != NULL) { + if (replies == 2) { + /* verify that password entered correctly */ + if (strcmp(token, resp[replies - 1])) { + /* mistyped */ + retval = PAM_AUTHTOK_RECOVERY_ERR; + _make_remark(pamh, ctrl, + PAM_ERROR_MSG, MISTYPED_PASS); + } + } + } else { + pam_syslog(pamh, LOG_NOTICE, + "could not recover authentication token"); + } + + } + + } else { + retval = (retval == PAM_SUCCESS) + ? PAM_AUTHTOK_RECOVERY_ERR : retval; + } + + resp[0] = NULL; + if (replies > 1) + _pam_delete(resp[1]); + } + + if (retval != PAM_SUCCESS) { + _pam_delete(token); + + if (on(UNIX_DEBUG, ctrl)) + pam_syslog(pamh, LOG_DEBUG, + "unable to obtain a password"); + return retval; + } + /* 'token' is the entered password */ + + if (off(UNIX_NOT_SET_PASS, ctrl)) { + + /* we store this password as an item */ + + retval = pam_set_item(pamh, authtok_flag, token); + _pam_delete(token); /* clean it up */ + if (retval != PAM_SUCCESS + || (retval = pam_get_item(pamh, authtok_flag, pass)) + != PAM_SUCCESS) { + + *pass = NULL; + pam_syslog(pamh, LOG_CRIT, "error manipulating password"); + return retval; + + } + } else { + /* + * then store it as data specific to this module. pam_end() + * will arrange to clean it up. + */ + + retval = pam_set_data(pamh, data_name, (void *) token, _cleanup); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_CRIT, + "error manipulating password data [%s]", + pam_strerror(pamh, retval)); + _pam_delete(token); + return retval; + } + *pass = token; + token = NULL; /* break link to password */ + } + + return PAM_SUCCESS; +} + +/* ****************************************************************** * + * Copyright (c) Jan Rêkorajski 1999. + * Copyright (c) Andrew G. Morgan 1996-8. + * Copyright (c) Alex O. Yuriev, 1996. + * Copyright (c) Cristian Gafton 1996. + * Copyright (c) Red Hat, Inc. 2007. + * + * 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. + */ diff --git a/modules/pam_unix/support.h b/modules/pam_unix/support.h new file mode 100644 index 00000000..65759384 --- /dev/null +++ b/modules/pam_unix/support.h @@ -0,0 +1,175 @@ +/* + * $Id$ + */ + +#ifndef _PAM_UNIX_SUPPORT_H +#define _PAM_UNIX_SUPPORT_H + +#include <pwd.h> + +/* + * File to read value of ENCRYPT_METHOD from. + */ +#define LOGIN_DEFS "/etc/login.defs" + + +/* + * here is the string to inform the user that the new passwords they + * typed were not the same. + */ + +#define MISTYPED_PASS "Sorry, passwords do not match" + +/* type definition for the control options */ + +typedef struct { + const char *token; + unsigned int mask; /* shall assume 32 bits of flags */ + unsigned int flag; + unsigned int is_hash_algo; +} UNIX_Ctrls; + +/* + * macro to determine if a given flag is on + */ + +#define on(x,ctrl) (unix_args[x].flag & ctrl) + +/* + * macro to determine that a given flag is NOT on + */ + +#define off(x,ctrl) (!on(x,ctrl)) + +/* + * macro to turn on/off a ctrl flag manually + */ + +#define set(x,ctrl) (ctrl = ((ctrl)&unix_args[x].mask)|unix_args[x].flag) +#define unset(x,ctrl) (ctrl &= ~(unix_args[x].flag)) + +/* the generic mask */ + +#define _ALL_ON_ (~0U) + +/* end of macro definitions definitions for the control flags */ + +/* ****************************************************************** * + * ctrl flags proper.. + */ + +/* + * here are the various options recognized by the unix module. They + * are enumerated here and then defined below. Internal arguments are + * given NULL tokens. + */ + +#define UNIX__OLD_PASSWD 0 /* internal */ +#define UNIX__VERIFY_PASSWD 1 /* internal */ +#define UNIX__IAMROOT 2 /* internal */ + +#define UNIX_AUDIT 3 /* print more things than debug.. + some information may be sensitive */ +#define UNIX_USE_FIRST_PASS 4 +#define UNIX_TRY_FIRST_PASS 5 +#define UNIX_NOT_SET_PASS 6 /* don't set the AUTHTOK items */ + +#define UNIX__PRELIM 7 /* internal */ +#define UNIX__UPDATE 8 /* internal */ +#define UNIX__NONULL 9 /* internal */ +#define UNIX__QUIET 10 /* internal */ +#define UNIX_USE_AUTHTOK 11 /* insist on reading PAM_AUTHTOK */ +#define UNIX_SHADOW 12 /* signal shadow on */ +#define UNIX_MD5_PASS 13 /* force the use of MD5 passwords */ +#define UNIX__NULLOK 14 /* Null token ok */ +#define UNIX_DEBUG 15 /* send more info to syslog(3) */ +#define UNIX_NODELAY 16 /* admin does not want a fail-delay */ +#define UNIX_NIS 17 /* wish to use NIS for pwd */ +#define UNIX_BIGCRYPT 18 /* use DEC-C2 crypt()^x function */ +#define UNIX_LIKE_AUTH 19 /* need to auth for setcred to work */ +#define UNIX_REMEMBER_PASSWD 20 /* Remember N previous passwords */ +#define UNIX_NOREAP 21 /* don't reap child process */ +#define UNIX_BROKEN_SHADOW 22 /* ignore errors reading password aging + * information during acct management */ +#define UNIX_SHA256_PASS 23 /* new password hashes will use SHA256 */ +#define UNIX_SHA512_PASS 24 /* new password hashes will use SHA512 */ +#define UNIX_ALGO_ROUNDS 25 /* optional number of rounds for new + password hash algorithms */ +#define UNIX_BLOWFISH_PASS 26 /* new password hashes will use blowfish */ +#define UNIX_MIN_PASS_LEN 27 /* min length for password */ +/* -------------- */ +#define UNIX_CTRLS_ 28 /* number of ctrl arguments defined */ + +#define UNIX_DES_CRYPT(ctrl) (off(UNIX_MD5_PASS,ctrl)&&off(UNIX_BIGCRYPT,ctrl)&&off(UNIX_SHA256_PASS,ctrl)&&off(UNIX_SHA512_PASS,ctrl)&&off(UNIX_BLOWFISH_PASS,ctrl)) + +static const UNIX_Ctrls unix_args[UNIX_CTRLS_] = +{ +/* symbol token name ctrl mask ctrl * + * ----------------------- ------------------- --------------------- -------- */ + +/* UNIX__OLD_PASSWD */ {NULL, _ALL_ON_, 01, 0}, +/* UNIX__VERIFY_PASSWD */ {NULL, _ALL_ON_, 02, 0}, +/* UNIX__IAMROOT */ {NULL, _ALL_ON_, 04, 0}, +/* UNIX_AUDIT */ {"audit", _ALL_ON_, 010, 0}, +/* UNIX_USE_FIRST_PASS */ {"use_first_pass", _ALL_ON_^(060), 020, 0}, +/* UNIX_TRY_FIRST_PASS */ {"try_first_pass", _ALL_ON_^(060), 040, 0}, +/* UNIX_NOT_SET_PASS */ {"not_set_pass", _ALL_ON_, 0100, 0}, +/* UNIX__PRELIM */ {NULL, _ALL_ON_^(0600), 0200, 0}, +/* UNIX__UPDATE */ {NULL, _ALL_ON_^(0600), 0400, 0}, +/* UNIX__NONULL */ {NULL, _ALL_ON_, 01000, 0}, +/* UNIX__QUIET */ {NULL, _ALL_ON_, 02000, 0}, +/* UNIX_USE_AUTHTOK */ {"use_authtok", _ALL_ON_, 04000, 0}, +/* UNIX_SHADOW */ {"shadow", _ALL_ON_, 010000, 0}, +/* UNIX_MD5_PASS */ {"md5", _ALL_ON_^(0260420000), 020000, 1}, +/* UNIX__NULLOK */ {"nullok", _ALL_ON_^(01000), 0, 0}, +/* UNIX_DEBUG */ {"debug", _ALL_ON_, 040000, 0}, +/* UNIX_NODELAY */ {"nodelay", _ALL_ON_, 0100000, 0}, +/* UNIX_NIS */ {"nis", _ALL_ON_, 0200000, 0}, +/* UNIX_BIGCRYPT */ {"bigcrypt", _ALL_ON_^(0260420000), 0400000, 1}, +/* UNIX_LIKE_AUTH */ {"likeauth", _ALL_ON_, 01000000, 0}, +/* UNIX_REMEMBER_PASSWD */ {"remember=", _ALL_ON_, 02000000, 0}, +/* UNIX_NOREAP */ {"noreap", _ALL_ON_, 04000000, 0}, +/* UNIX_BROKEN_SHADOW */ {"broken_shadow", _ALL_ON_, 010000000, 0}, +/* UNIX_SHA256_PASS */ {"sha256", _ALL_ON_^(0260420000), 020000000, 1}, +/* UNIX_SHA512_PASS */ {"sha512", _ALL_ON_^(0260420000), 040000000, 1}, +/* UNIX_ALGO_ROUNDS */ {"rounds=", _ALL_ON_, 0100000000, 0}, +/* UNIX_BLOWFISH_PASS */ {"blowfish", _ALL_ON_^(0260420000), 0200000000, 1}, +/* UNIX_MIN_PASS_LEN */ {"minlen=", _ALL_ON_, 0400000000, 0}, +}; + +#define UNIX_DEFAULTS (unix_args[UNIX__NONULL].flag) + +#define MAX_FD_NO 2000000 + +/* use this to free strings. ESPECIALLY password strings */ + +#define _pam_delete(xx) \ +{ \ + _pam_overwrite(xx); \ + _pam_drop(xx); \ +} + +extern int _make_remark(pam_handle_t * pamh, unsigned int ctrl + ,int type, const char *text); +extern int _set_ctrl(pam_handle_t * pamh, int flags, int *remember, int *rounds, + int *pass_min_len, int argc, const char **argv); +extern int _unix_getpwnam (pam_handle_t *pamh, + const char *name, int files, int nis, + struct passwd **ret); +extern int _unix_comesfromsource (pam_handle_t *pamh, + const char *name, int files, int nis); +extern int _unix_blankpasswd(pam_handle_t *pamh,unsigned int ctrl, + const char *name); +extern int _unix_verify_password(pam_handle_t * pamh, const char *name + ,const char *p, unsigned int ctrl); +extern int _unix_read_password(pam_handle_t * pamh + ,unsigned int ctrl + ,const char *comment + ,const char *prompt1 + ,const char *prompt2 + ,const char *data_name + ,const void **pass); + +extern int _unix_run_verify_binary(pam_handle_t *pamh, + unsigned int ctrl, const char *user, int *daysleft); +#endif /* _PAM_UNIX_SUPPORT_H */ diff --git a/modules/pam_unix/tst-pam_unix b/modules/pam_unix/tst-pam_unix new file mode 100755 index 00000000..22922800 --- /dev/null +++ b/modules/pam_unix/tst-pam_unix @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_unix.so diff --git a/modules/pam_unix/unix_chkpwd.8 b/modules/pam_unix/unix_chkpwd.8 new file mode 100644 index 00000000..b16e0c94 --- /dev/null +++ b/modules/pam_unix/unix_chkpwd.8 @@ -0,0 +1,53 @@ +'\" t +.\" Title: unix_chkpwd +.\" Author: [see the "AUTHOR" section] +.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/> +.\" Date: 09/19/2013 +.\" Manual: Linux-PAM Manual +.\" Source: Linux-PAM Manual +.\" Language: English +.\" +.TH "UNIX_CHKPWD" "8" "09/19/2013" "Linux-PAM Manual" "Linux\-PAM Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +unix_chkpwd \- Helper binary that verifies the password of the current user +.SH "SYNOPSIS" +.HP \w'\fBunix_chkpwd\fR\ 'u +\fBunix_chkpwd\fR [\&.\&.\&.] +.SH "DESCRIPTION" +.PP +\fIunix_chkpwd\fR +is a helper program for the +\fIpam_unix\fR +module that verifies the password of the current user\&. It also checks password and account expiration dates in +\fIshadow\fR\&. It is not intended to be run directly from the command line and logs a security violation if done so\&. +.PP +It is typically installed setuid root or setgid shadow\&. +.PP +The interface of the helper \- command line options, and input/output data format are internal to the +\fIpam_unix\fR +module and it should not be called directly from applications\&. +.SH "SEE ALSO" +.PP +\fBpam_unix\fR(8) +.SH "AUTHOR" +.PP +Written by Andrew Morgan and other various people\&. diff --git a/modules/pam_unix/unix_chkpwd.8.xml b/modules/pam_unix/unix_chkpwd.8.xml new file mode 100644 index 00000000..a10dbe33 --- /dev/null +++ b/modules/pam_unix/unix_chkpwd.8.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding='UTF-8'?> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN" + "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"> + +<refentry id="unix_chkpwd"> + + <refmeta> + <refentrytitle>unix_chkpwd</refentrytitle> + <manvolnum>8</manvolnum> + <refmiscinfo class="sectdesc">Linux-PAM Manual</refmiscinfo> + </refmeta> + + <refnamediv id="unix_chkpwd-name"> + <refname>unix_chkpwd</refname> + <refpurpose>Helper binary that verifies the password of the current user</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis id="unix_chkpwd-cmdsynopsis"> + <command>unix_chkpwd</command> + <arg choice="opt"> + ... + </arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1 id="unix_chkpwd-description"> + + <title>DESCRIPTION</title> + + <para> + <emphasis>unix_chkpwd</emphasis> is a helper program for the + <emphasis>pam_unix</emphasis> module that verifies the + password of the current user. It also checks password and account + expiration dates in <emphasis>shadow</emphasis>. It is not intended to + be run directly from the command line and logs a security violation if + done so. + </para> + + <para> + It is typically installed setuid root or setgid shadow. + </para> + + <para> + The interface of the helper - command line options, and input/output + data format are internal to the <emphasis>pam_unix</emphasis> + module and it should not be called directly from applications. + </para> + </refsect1> + + <refsect1 id='unix_chkpwd-see_also'> + <title>SEE ALSO</title> + <para> + <citerefentry> + <refentrytitle>pam_unix</refentrytitle><manvolnum>8</manvolnum> + </citerefentry> + </para> + </refsect1> + + <refsect1 id='unix_chkpwd-author'> + <title>AUTHOR</title> + <para> + Written by Andrew Morgan and other various people. + </para> + </refsect1> + +</refentry> diff --git a/modules/pam_unix/unix_chkpwd.c b/modules/pam_unix/unix_chkpwd.c new file mode 100644 index 00000000..61675ed2 --- /dev/null +++ b/modules/pam_unix/unix_chkpwd.c @@ -0,0 +1,238 @@ +/* + * This program is designed to run setuid(root) or with sufficient + * privilege to read all of the unix password databases. It is designed + * to provide a mechanism for the current user (defined by this + * process' uid) to verify their own password. + * + * The password is read from the standard input. The exit status of + * this program indicates whether the user is authenticated or not. + * + * Copyright information is located at the end of the file. + * + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <pwd.h> +#include <shadow.h> +#include <signal.h> +#include <time.h> +#include <errno.h> +#ifdef HAVE_LIBAUDIT +#include <libaudit.h> +#endif + +#include <security/_pam_types.h> +#include <security/_pam_macros.h> + +#include "passverify.h" + +static int _check_expiry(const char *uname) +{ + struct spwd *spent; + struct passwd *pwent; + int retval; + int daysleft; + + retval = get_account_info(uname, &pwent, &spent); + if (retval != PAM_SUCCESS) { + helper_log_err(LOG_ALERT, "could not obtain user info (%s)", uname); + printf("-1\n"); + return retval; + } + + if (spent == NULL) { + printf("-1\n"); + return retval; + } + + retval = check_shadow_expiry(spent, &daysleft); + printf("%d\n", daysleft); + return retval; +} + +#ifdef HAVE_LIBAUDIT +static int _audit_log(int type, const char *uname, int rc) +{ + 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 PAM_SUCCESS; + + helper_log_err(LOG_CRIT, "audit_open() failed: %m"); + return PAM_AUTH_ERR; + } + + rc = audit_log_acct_message(audit_fd, type, NULL, "PAM:unix_chkpwd", + uname, -1, NULL, NULL, NULL, rc == PAM_SUCCESS); + if (rc == -EPERM && geteuid() != 0) { + rc = 0; + } + + audit_close(audit_fd); + + return rc < 0 ? PAM_AUTH_ERR : PAM_SUCCESS; +} +#endif + +int main(int argc, char *argv[]) +{ + char pass[MAXPASS + 1]; + char *option; + int npass, nullok; + int blankpass = 0; + int retval = PAM_AUTH_ERR; + char *user; + char *passwords[] = { pass }; + + /* + * Catch or ignore as many signal as possible. + */ + setup_signals(); + + /* + * we establish that this program is running with non-tty stdin. + * this is to discourage casual use. It does *NOT* prevent an + * intruder from repeatadly running this program to determine the + * password of the current user (brute force attack, but one for + * which the attacker must already have gained access to the user's + * account). + */ + + if (isatty(STDIN_FILENO) || argc != 3 ) { + helper_log_err(LOG_NOTICE + ,"inappropriate use of Unix helper binary [UID=%d]" + ,getuid()); +#ifdef HAVE_LIBAUDIT + _audit_log(AUDIT_ANOM_EXEC, getuidname(getuid()), PAM_SYSTEM_ERR); +#endif + fprintf(stderr + ,"This binary is not designed for running in this way\n" + "-- the system administrator has been informed\n"); + sleep(10); /* this should discourage/annoy the user */ + return PAM_SYSTEM_ERR; + } + + /* + * Determine what the current user's name is. + * We must thus skip the check if the real uid is 0. + */ + if (getuid() == 0) { + user=argv[1]; + } + else { + user = getuidname(getuid()); + /* if the caller specifies the username, verify that user + matches it */ + if (strcmp(user, argv[1])) { + user = argv[1]; + /* no match -> permanently change to the real user and proceed */ + if (setuid(getuid()) != 0) + return PAM_AUTH_ERR; + } + } + + option=argv[2]; + + if (strcmp(option, "chkexpiry") == 0) + /* Check account information from the shadow file */ + return _check_expiry(argv[1]); + /* read the nullok/nonull option */ + else if (strcmp(option, "nullok") == 0) + nullok = 1; + else if (strcmp(option, "nonull") == 0) + nullok = 0; + else { +#ifdef HAVE_LIBAUDIT + _audit_log(AUDIT_ANOM_EXEC, getuidname(getuid()), PAM_SYSTEM_ERR); +#endif + return PAM_SYSTEM_ERR; + } + /* read the password from stdin (a pipe from the pam_unix module) */ + + npass = read_passwords(STDIN_FILENO, 1, passwords); + + if (npass != 1) { /* is it a valid password? */ + helper_log_err(LOG_DEBUG, "no password supplied"); + *pass = '\0'; + } + + if (*pass == '\0') { + blankpass = 1; + } + + retval = helper_verify_password(user, pass, nullok); + + memset(pass, '\0', MAXPASS); /* clear memory of the password */ + + /* return pass or fail */ + + if (retval != PAM_SUCCESS) { + if (!nullok || !blankpass) { + /* no need to log blank pass test */ +#ifdef HAVE_LIBAUDIT + if (getuid() != 0) + _audit_log(AUDIT_USER_AUTH, user, PAM_AUTH_ERR); +#endif + helper_log_err(LOG_NOTICE, "password check failed for user (%s)", user); + } + return PAM_AUTH_ERR; + } else { + if (getuid() != 0) { +#ifdef HAVE_LIBAUDIT + return _audit_log(AUDIT_USER_AUTH, user, PAM_SUCCESS); +#else + return PAM_SUCCESS; +#endif + } + return PAM_SUCCESS; + } +} + +/* + * Copyright (c) Andrew G. Morgan, 1996. All rights reserved + * Copyright (c) Red Hat, Inc., 2007,2008. 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. + */ diff --git a/modules/pam_unix/unix_update.8 b/modules/pam_unix/unix_update.8 new file mode 100644 index 00000000..1ee82c85 --- /dev/null +++ b/modules/pam_unix/unix_update.8 @@ -0,0 +1,52 @@ +'\" t +.\" Title: unix_update +.\" Author: [see the "AUTHOR" section] +.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/> +.\" Date: 09/19/2013 +.\" Manual: Linux-PAM Manual +.\" Source: Linux-PAM Manual +.\" Language: English +.\" +.TH "UNIX_UPDATE" "8" "09/19/2013" "Linux-PAM Manual" "Linux\-PAM Manual" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +unix_update \- Helper binary that updates the password of a given user +.SH "SYNOPSIS" +.HP \w'\fBunix_update\fR\ 'u +\fBunix_update\fR [\&.\&.\&.] +.SH "DESCRIPTION" +.PP +\fIunix_update\fR +is a helper program for the +\fIpam_unix\fR +module that updates the password of a given user\&. It is not intended to be run directly from the command line and logs a security violation if done so\&. +.PP +The purpose of the helper is to enable tighter confinement of login and password changing services\&. The helper is thus called only when SELinux is enabled on the system\&. +.PP +The interface of the helper \- command line options, and input/output data format are internal to the +\fIpam_unix\fR +module and it should not be called directly from applications\&. +.SH "SEE ALSO" +.PP +\fBpam_unix\fR(8) +.SH "AUTHOR" +.PP +Written by Tomas Mraz and other various people\&. diff --git a/modules/pam_unix/unix_update.8.xml b/modules/pam_unix/unix_update.8.xml new file mode 100644 index 00000000..6c7467b9 --- /dev/null +++ b/modules/pam_unix/unix_update.8.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding='UTF-8'?> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN" + "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"> + +<refentry id="unix_update"> + + <refmeta> + <refentrytitle>unix_update</refentrytitle> + <manvolnum>8</manvolnum> + <refmiscinfo class="sectdesc">Linux-PAM Manual</refmiscinfo> + </refmeta> + + <refnamediv id="unix_update-name"> + <refname>unix_update</refname> + <refpurpose>Helper binary that updates the password of a given user</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis id="unix_update-cmdsynopsis"> + <command>unix_update</command> + <arg choice="opt"> + ... + </arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1 id="unix_update-description"> + + <title>DESCRIPTION</title> + + <para> + <emphasis>unix_update</emphasis> is a helper program for the + <emphasis>pam_unix</emphasis> module that updates the + password of a given user. It is not intended to be run directly + from the command line and logs a security violation if done so. + </para> + + <para> + The purpose of the helper is to enable tighter confinement of + login and password changing services. The helper is thus called only + when SELinux is enabled on the system. + </para> + + <para> + The interface of the helper - command line options, and input/output + data format are internal to the <emphasis>pam_unix</emphasis> + module and it should not be called directly from applications. + </para> + </refsect1> + + <refsect1 id='unix_update-see_also'> + <title>SEE ALSO</title> + <para> + <citerefentry> + <refentrytitle>pam_unix</refentrytitle><manvolnum>8</manvolnum> + </citerefentry> + </para> + </refsect1> + + <refsect1 id='unix_update-author'> + <title>AUTHOR</title> + <para> + Written by Tomas Mraz and other various people. + </para> + </refsect1> + +</refentry> diff --git a/modules/pam_unix/unix_update.c b/modules/pam_unix/unix_update.c new file mode 100644 index 00000000..6ea7ea51 --- /dev/null +++ b/modules/pam_unix/unix_update.c @@ -0,0 +1,191 @@ +/* + * This program is designed to run with sufficient privilege + * to read and write all of the unix password databases. + * Its purpose is to allow updating the databases when + * SELinux confinement of the caller domain prevents them to + * do that themselves. + * + * The password is read from the standard input. The exit status of + * this program indicates whether the password was updated or not. + * + * Copyright information is located at the end of the file. + * + */ + +#include "config.h" + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <pwd.h> +#include <shadow.h> +#include <signal.h> +#include <time.h> +#include <sys/time.h> + +#include <security/_pam_types.h> +#include <security/_pam_macros.h> + +#include "passverify.h" + +static int +set_password(const char *forwho, const char *shadow, const char *remember) +{ + struct passwd *pwd = NULL; + int retval; + char pass[MAXPASS + 1]; + char towhat[MAXPASS + 1]; + int npass = 0; + /* we don't care about number format errors because the helper + should be called internally only */ + int doshadow = atoi(shadow); + int nremember = atoi(remember); + char *passwords[] = { pass, towhat }; + + /* read the password from stdin (a pipe from the pam_unix module) */ + + npass = read_passwords(STDIN_FILENO, 2, passwords); + + if (npass != 2) { /* is it a valid password? */ + if (npass == 1) { + helper_log_err(LOG_DEBUG, "no new password supplied"); + memset(pass, '\0', MAXPASS); + } else { + helper_log_err(LOG_DEBUG, "no valid passwords supplied"); + } + return PAM_AUTHTOK_ERR; + } + + if (lock_pwdf() != PAM_SUCCESS) + return PAM_AUTHTOK_LOCK_BUSY; + + pwd = getpwnam(forwho); + + if (pwd == NULL) { + retval = PAM_USER_UNKNOWN; + goto done; + } + + /* If real caller uid is not root we must verify that + received old pass agrees with the current one. + We always allow change from null pass. */ + if (getuid()) { + retval = helper_verify_password(forwho, pass, 1); + if (retval != PAM_SUCCESS) { + goto done; + } + } + + /* first, save old password */ + if (save_old_password(forwho, pass, nremember)) { + retval = PAM_AUTHTOK_ERR; + goto done; + } + + if (doshadow || is_pwd_shadowed(pwd)) { + retval = unix_update_shadow(forwho, towhat); + if (retval == PAM_SUCCESS) + if (!is_pwd_shadowed(pwd)) + retval = unix_update_passwd(forwho, "x"); + } else { + retval = unix_update_passwd(forwho, towhat); + } + +done: + memset(pass, '\0', MAXPASS); + memset(towhat, '\0', MAXPASS); + + unlock_pwdf(); + + if (retval == PAM_SUCCESS) { + return PAM_SUCCESS; + } else { + return PAM_AUTHTOK_ERR; + } +} + +int main(int argc, char *argv[]) +{ + char *option; + + /* + * Catch or ignore as many signal as possible. + */ + setup_signals(); + + /* + * we establish that this program is running with non-tty stdin. + * this is to discourage casual use. It does *NOT* prevent an + * intruder from repeatadly running this program to determine the + * password of the current user (brute force attack, but one for + * which the attacker must already have gained access to the user's + * account). + */ + + if (isatty(STDIN_FILENO) || argc != 5 ) { + helper_log_err(LOG_NOTICE + ,"inappropriate use of Unix helper binary [UID=%d]" + ,getuid()); + fprintf(stderr + ,"This binary is not designed for running in this way\n" + "-- the system administrator has been informed\n"); + sleep(10); /* this should discourage/annoy the user */ + return PAM_SYSTEM_ERR; + } + + /* We must be root to read/update shadow. + */ + if (geteuid() != 0) { + return PAM_CRED_INSUFFICIENT; + } + + option = argv[2]; + + if (strcmp(option, "update") == 0) { + /* Attempting to change the password */ + return set_password(argv[1], argv[3], argv[4]); + } + + return PAM_SYSTEM_ERR; +} + +/* + * Copyright (c) Andrew G. Morgan, 1996. All rights reserved + * Copyright (c) Red Hat, Inc., 2007, 2008. 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. + */ diff --git a/modules/pam_unix/yppasswd.h b/modules/pam_unix/yppasswd.h new file mode 100644 index 00000000..5f947071 --- /dev/null +++ b/modules/pam_unix/yppasswd.h @@ -0,0 +1,51 @@ +/* + * yppasswdd + * Copyright 1994, 1995, 1996 Olaf Kirch, <okir@lst.de> + * + * This program is covered by the GNU General Public License, version 2 + * or later. It is provided in the hope that it is useful. However, the author + * disclaims ALL WARRANTIES, expressed or implied. See the GPL for details. + * + * This file was generated automatically by rpcgen from yppasswd.x, and + * editied manually. + */ + +#ifndef _YPPASSWD_H_ +#define _YPPASSWD_H_ + +#define YPPASSWDPROG ((u_long)100009) +#define YPPASSWDVERS ((u_long)1) +#define YPPASSWDPROC_UPDATE ((u_long)1) + +/* + * The password struct passed by the update call. I renamed it to + * xpasswd to avoid a type clash with the one defined in <pwd.h>. + */ +#ifndef __sgi +typedef struct xpasswd { + char *pw_name; + char *pw_passwd; + int pw_uid; + int pw_gid; + char *pw_gecos; + char *pw_dir; + char *pw_shell; +} xpasswd; + +#else +#include <pwd.h> +typedef struct xpasswd xpasswd; +#endif + +/* The updated password information, plus the old password. + */ +typedef struct yppasswd { + char *oldpass; + xpasswd newpw; +} yppasswd; + +/* XDR encoding/decoding routines */ +bool_t xdr_xpasswd(XDR * xdrs, xpasswd * objp); +bool_t xdr_yppasswd(XDR * xdrs, yppasswd * objp); + +#endif /* _YPPASSWD_H_ */ diff --git a/modules/pam_unix/yppasswd_xdr.c b/modules/pam_unix/yppasswd_xdr.c new file mode 100644 index 00000000..f2b86a56 --- /dev/null +++ b/modules/pam_unix/yppasswd_xdr.c @@ -0,0 +1,40 @@ +/* + * yppasswdd + * Copyright 1994, 1995, 1996 Olaf Kirch, <okir@lst.de> + * + * This program is covered by the GNU General Public License, version 2 + * or later. It is provided in the hope that it is useful. However, the author + * disclaims ALL WARRANTIES, expressed or implied. See the GPL for details. + * + * This file was generated automatically by rpcgen from yppasswd.x, and + * editied manually. + */ + +#include "config.h" + +#ifdef HAVE_RPC_RPC_H + +#include <rpc/rpc.h> +#include "yppasswd.h" + +bool_t +xdr_xpasswd(XDR * xdrs, xpasswd * objp) +{ + return xdr_string(xdrs, &objp->pw_name, ~0) + && xdr_string(xdrs, &objp->pw_passwd, ~0) + && xdr_int(xdrs, &objp->pw_uid) + && xdr_int(xdrs, &objp->pw_gid) + && xdr_string(xdrs, &objp->pw_gecos, ~0) + && xdr_string(xdrs, &objp->pw_dir, ~0) + && xdr_string(xdrs, &objp->pw_shell, ~0); +} + + +bool_t +xdr_yppasswd(XDR * xdrs, yppasswd * objp) +{ + return xdr_string(xdrs, &objp->oldpass, ~0) + && xdr_xpasswd(xdrs, &objp->newpw); +} + +#endif |