summaryrefslogtreecommitdiff
path: root/modules/pam_pwhistory
diff options
context:
space:
mode:
Diffstat (limited to 'modules/pam_pwhistory')
-rw-r--r--modules/pam_pwhistory/Makefile.am35
-rw-r--r--modules/pam_pwhistory/Makefile.in690
-rw-r--r--modules/pam_pwhistory/README65
-rw-r--r--modules/pam_pwhistory/README.xml41
-rw-r--r--modules/pam_pwhistory/opasswd.c484
-rw-r--r--modules/pam_pwhistory/opasswd.h45
-rw-r--r--modules/pam_pwhistory/pam_pwhistory.8329
-rw-r--r--modules/pam_pwhistory/pam_pwhistory.8.xml245
-rw-r--r--modules/pam_pwhistory/pam_pwhistory.c249
-rwxr-xr-xmodules/pam_pwhistory/tst-pam_pwhistory2
10 files changed, 2185 insertions, 0 deletions
diff --git a/modules/pam_pwhistory/Makefile.am b/modules/pam_pwhistory/Makefile.am
new file mode 100644
index 00000000..018d0b52
--- /dev/null
+++ b/modules/pam_pwhistory/Makefile.am
@@ -0,0 +1,35 @@
+#
+# Copyright (c) 2008 Thorsten Kukuk <kukuk@suse.de>
+#
+
+CLEANFILES = *~
+
+EXTRA_DIST = README $(MANS) $(XMLS) tst-pam_pwhistory
+
+TESTS = tst-pam_pwhistory
+
+man_MANS = pam_pwhistory.8
+
+XMLS = README.xml pam_pwhistory.8.xml
+
+securelibdir = $(SECUREDIR)
+secureconfdir = $(SCONFIGDIR)
+
+AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include
+AM_LDFLAGS = -no-undefined -avoid-version -module
+if HAVE_VERSIONING
+ AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
+endif
+
+noinst_HEADERS = opasswd.h
+
+securelib_LTLIBRARIES = pam_pwhistory.la
+pam_pwhistory_la_LIBADD = -L$(top_builddir)/libpam -lpam @LIBCRYPT@
+pam_pwhistory_la_SOURCES = pam_pwhistory.c opasswd.c
+
+if ENABLE_REGENERATE_MAN
+noinst_DATA = README
+README: pam_pwhistory.8.xml
+-include $(top_srcdir)/Make.xml.rules
+endif
+
diff --git a/modules/pam_pwhistory/Makefile.in b/modules/pam_pwhistory/Makefile.in
new file mode 100644
index 00000000..25c95b44
--- /dev/null
+++ b/modules/pam_pwhistory/Makefile.in
@@ -0,0 +1,690 @@
+# Makefile.in generated by automake 1.10.2 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008 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) 2008 Thorsten Kukuk <kukuk@suse.de>
+#
+
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@HAVE_VERSIONING_TRUE@am__append_1 = -Wl,--version-script=$(srcdir)/../modules.map
+subdir = modules/pam_pwhistory
+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/intlmacosx.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/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 = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_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 = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(securelibdir)" "$(DESTDIR)$(man8dir)"
+securelibLTLIBRARIES_INSTALL = $(INSTALL)
+LTLIBRARIES = $(securelib_LTLIBRARIES)
+pam_pwhistory_la_DEPENDENCIES =
+am_pam_pwhistory_la_OBJECTS = pam_pwhistory.lo opasswd.lo
+pam_pwhistory_la_OBJECTS = $(am_pam_pwhistory_la_OBJECTS)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+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_pwhistory_la_SOURCES)
+DIST_SOURCES = $(pam_pwhistory_la_SOURCES)
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man_MANS)
+DATA = $(noinst_DATA)
+HEADERS = $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BROWSER = @BROWSER@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+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@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+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@
+LIBNSL = @LIBNSL@
+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@
+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_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+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@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+XMLCATALOG = @XMLCATALOG@
+XMLLINT = @XMLLINT@
+XML_CATALOG_FILE = @XML_CATALOG_FILE@
+XSLTPROC = @XSLTPROC@
+YACC = @YACC@
+YFLAGS = @YFLAGS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_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@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pam_cv_ld_as_needed = @pam_cv_ld_as_needed@
+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 = *~
+EXTRA_DIST = README $(MANS) $(XMLS) tst-pam_pwhistory
+TESTS = tst-pam_pwhistory
+man_MANS = pam_pwhistory.8
+XMLS = README.xml pam_pwhistory.8.xml
+securelibdir = $(SECUREDIR)
+secureconfdir = $(SCONFIGDIR)
+AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include
+AM_LDFLAGS = -no-undefined -avoid-version -module $(am__append_1)
+noinst_HEADERS = opasswd.h
+securelib_LTLIBRARIES = pam_pwhistory.la
+pam_pwhistory_la_LIBADD = -L$(top_builddir)/libpam -lpam @LIBCRYPT@
+pam_pwhistory_la_SOURCES = pam_pwhistory.c opasswd.c
+@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_pwhistory/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu modules/pam_pwhistory/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
+install-securelibLTLIBRARIES: $(securelib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(securelibdir)" || $(MKDIR_P) "$(DESTDIR)$(securelibdir)"
+ @list='$(securelib_LTLIBRARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ f=$(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(securelibLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(securelibdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(securelibLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(securelibdir)/$$f"; \
+ else :; fi; \
+ done
+
+uninstall-securelibLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(securelib_LTLIBRARIES)'; for p in $$list; do \
+ p=$(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(securelibdir)/$$p'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(securelibdir)/$$p"; \
+ 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_pwhistory.la: $(pam_pwhistory_la_OBJECTS) $(pam_pwhistory_la_DEPENDENCIES)
+ $(LINK) -rpath $(securelibdir) $(pam_pwhistory_la_OBJECTS) $(pam_pwhistory_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/opasswd.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_pwhistory.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ mv -f $(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@ mv -f $(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@ mv -f $(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 $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man8: $(man8_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man8dir)" || $(MKDIR_P) "$(DESTDIR)$(man8dir)"
+ @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.8*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $$i; then file=$$i; \
+ else file=$(srcdir)/$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 8*) ;; \
+ *) ext='8' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst"; \
+ done
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.8*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 8*) ;; \
+ *) ext='8' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f '$(DESTDIR)$(man8dir)/$$inst'"; \
+ rm -f "$(DESTDIR)$(man8dir)/$$inst"; \
+ done
+
+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)
+ tags=; \
+ 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; }; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ 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)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && 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) '; \
+ 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`; \
+ echo "XPASS: $$tst"; \
+ ;; \
+ *) \
+ echo "PASS: $$tst"; \
+ ;; \
+ esac; \
+ elif test $$? -ne 77; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xfail=`expr $$xfail + 1`; \
+ echo "XFAIL: $$tst"; \
+ ;; \
+ *) \
+ failed=`expr $$failed + 1`; \
+ echo "FAIL: $$tst"; \
+ ;; \
+ esac; \
+ else \
+ skip=`expr $$skip + 1`; \
+ echo "SKIP: $$tst"; \
+ fi; \
+ 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`; \
+ echo "$$dashes"; \
+ echo "$$banner"; \
+ test -z "$$skipped" || echo "$$skipped"; \
+ test -z "$$report" || echo "$$report"; \
+ echo "$$dashes"; \
+ test "$$failed" -eq 0; \
+ else :; fi
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$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) $(MANS) $(DATA) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(securelibdir)" "$(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)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool 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
+
+info: info-am
+
+info-am:
+
+install-data-am: install-man install-securelibLTLIBRARIES
+
+install-dvi: install-dvi-am
+
+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
+
+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-securelibLTLIBRARIES
+
+uninstall-man: uninstall-man8
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-TESTS check-am clean \
+ clean-generic clean-libtool 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-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-securelibLTLIBRARIES
+
+@ENABLE_REGENERATE_MAN_TRUE@README: pam_pwhistory.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_pwhistory/README b/modules/pam_pwhistory/README
new file mode 100644
index 00000000..167b4172
--- /dev/null
+++ b/modules/pam_pwhistory/README
@@ -0,0 +1,65 @@
+pam_pwhistory — PAM module to remember last passwords
+
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+DESCRIPTION
+
+This module saves the last passwords for each user in order to force password
+change history and keep the user from alternating between the same password too
+frequently.
+
+This module does not work together with kerberos. In general, it does not make
+much sense to use this module in conjunction with NIS or LDAP, since the old
+passwords are stored on the local machine and are not available on another
+machine for password history checking.
+
+OPTIONS
+
+debug
+
+ Turns on debugging via syslog(3).
+
+use_authtok
+
+ When password changing enforce the module to use the new password provided
+ by a previously stacked password module (this is used in the example of the
+ stacking of the pam_cracklib module documented below).
+
+enforce_for_root
+
+ If this option is set, the check is enforced for root, too.
+
+remember=N
+
+ The last N passwords for each user are saved in /etc/security/opasswd. The
+ default is 10.
+
+retry=N
+
+ Prompt user at most N times before returning with error. The default is 1.
+
+authtok_type=STRING
+
+ See pam_get_authtok(3) for more details.
+
+EXAMPLES
+
+An example password section would be:
+
+#%PAM-1.0
+password required pam_pwhistory.so
+password required pam_unix.so use_authtok
+
+
+In combination with pam_cracklib:
+
+#%PAM-1.0
+password required pam_cracklib.so retry=3
+password required pam_pwhistory.so use_authtok
+password required pam_unix.so use_authtok
+
+
+AUTHOR
+
+pam_pwhistory was written by Thorsten Kukuk <kukuk@thkukuk.de>
+
diff --git a/modules/pam_pwhistory/README.xml b/modules/pam_pwhistory/README.xml
new file mode 100644
index 00000000..f048e321
--- /dev/null
+++ b/modules/pam_pwhistory/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_pwhistory.8.xml">
+-->
+]>
+
+<article>
+
+ <articleinfo>
+
+ <title>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="pam_pwhistory.8.xml" xpointer='xpointer(//refnamediv[@id = "pam_pwhistory-name"]/*)'/>
+ </title>
+
+ </articleinfo>
+
+ <section>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="pam_pwhistory.8.xml" xpointer='xpointer(//refsect1[@id = "pam_pwhistory-description"]/*)'/>
+ </section>
+
+ <section>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="pam_pwhistory.8.xml" xpointer='xpointer(//refsect1[@id = "pam_pwhistory-options"]/*)'/>
+ </section>
+
+ <section>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="pam_pwhistory.8.xml" xpointer='xpointer(//refsect1[@id = "pam_pwhistory-examples"]/*)'/>
+ </section>
+
+ <section>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="pam_pwhistory.8.xml" xpointer='xpointer(//refsect1[@id = "pam_pwhistory-author"]/*)'/>
+ </section>
+
+</article>
diff --git a/modules/pam_pwhistory/opasswd.c b/modules/pam_pwhistory/opasswd.c
new file mode 100644
index 00000000..3c8e5cff
--- /dev/null
+++ b/modules/pam_pwhistory/opasswd.c
@@ -0,0 +1,484 @@
+/*
+ * Copyright (c) 2008 Thorsten Kukuk <kukuk@suse.de>
+ *
+ * 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.
+ */
+
+#if defined(HAVE_CONFIG_H)
+#include <config.h>
+#endif
+
+#include <pwd.h>
+#include <time.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <sys/stat.h>
+
+#if defined (HAVE_XCRYPT_H)
+#include <xcrypt.h>
+#elif defined (HAVE_CRYPT_H)
+#include <crypt.h>
+#endif
+
+#include <security/pam_ext.h>
+#include <security/pam_modules.h>
+
+#include "opasswd.h"
+
+#ifndef RANDOM_DEVICE
+#define RANDOM_DEVICE "/dev/urandom"
+#endif
+
+#define OLD_PASSWORDS_FILE "/etc/security/opasswd"
+#define TMP_PASSWORDS_FILE OLD_PASSWORDS_FILE".tmpXXXXXX"
+
+#define DEFAULT_BUFLEN 4096
+
+typedef struct {
+ char *user;
+ char *uid;
+ int count;
+ char *old_passwords;
+} opwd;
+
+
+static int
+parse_entry (char *line, opwd *data)
+{
+ const char delimiters[] = ":";
+ char *endptr;
+
+ data->user = strsep (&line, delimiters);
+ data->uid = strsep (&line, delimiters);
+ data->count = strtol (strsep (&line, delimiters), &endptr, 10);
+ if (endptr != NULL && *endptr != '\0')
+ return 1;
+
+ data->old_passwords = strsep (&line, delimiters);
+
+ return 0;
+}
+
+/* Check, if the new password is already in the opasswd file. */
+int
+check_old_password (pam_handle_t *pamh, const char *user,
+ const char *newpass, int debug)
+{
+ int retval = PAM_SUCCESS;
+ FILE *oldpf;
+ char *buf = NULL;
+ size_t buflen = 0;
+ opwd entry;
+ int found = 0;
+
+ if ((oldpf = fopen (OLD_PASSWORDS_FILE, "r")) == NULL)
+ {
+ if (errno != ENOENT)
+ pam_syslog (pamh, LOG_ERR, "Cannot open %s: %m", OLD_PASSWORDS_FILE);
+ return PAM_SUCCESS;
+ }
+
+ while (!feof (oldpf))
+ {
+ char *cp, *tmp;
+#if defined(HAVE_GETLINE)
+ ssize_t n = getline (&buf, &buflen, oldpf);
+#elif defined (HAVE_GETDELIM)
+ ssize_t n = getdelim (&buf, &buflen, '\n', oldpf);
+#else
+ ssize_t n;
+
+ if (buf == NULL)
+ {
+ buflen = DEFAULT_BUFLEN;
+ buf = malloc (buflen);
+ if (buf == NULL)
+ return PAM_BUF_ERR;
+ }
+ buf[0] = '\0';
+ fgets (buf, buflen - 1, oldpf);
+ n = strlen (buf);
+#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';
+
+ if (strncmp (cp, user, strlen (user)) == 0 &&
+ cp[strlen (user)] == ':')
+ {
+ /* We found the line we needed */
+ if (parse_entry (cp, &entry) == 0)
+ {
+ found = 1;
+ break;
+ }
+ }
+ }
+
+ fclose (oldpf);
+
+ if (found)
+ {
+ const char delimiters[] = ",";
+ struct crypt_data output;
+ char *running;
+ char *oldpass;
+
+ memset (&output, 0, sizeof (output));
+
+ running = strdupa (entry.old_passwords);
+ if (running == NULL)
+ return PAM_BUF_ERR;
+
+ do {
+ oldpass = strsep (&running, delimiters);
+ if (oldpass && strlen (oldpass) > 0 &&
+ strcmp (crypt_r (newpass, oldpass, &output), oldpass) == 0)
+ {
+ if (debug)
+ pam_syslog (pamh, LOG_DEBUG, "New password already used");
+ retval = PAM_AUTHTOK_ERR;
+ break;
+ }
+ } while (oldpass != NULL);
+ }
+
+ if (buf)
+ free (buf);
+
+ return retval;
+}
+
+int
+save_old_password (pam_handle_t *pamh, const char *user, uid_t uid,
+ const char *oldpass, int howmany, int debug UNUSED)
+{
+ char opasswd_tmp[] = TMP_PASSWORDS_FILE;
+ struct stat opasswd_stat;
+ FILE *oldpf, *newpf;
+ int newpf_fd;
+ int do_create = 0;
+ int retval = PAM_SUCCESS;
+ char *buf = NULL;
+ size_t buflen = 0;
+ int found = 0;
+
+ if (howmany <= 0)
+ return PAM_SUCCESS;
+
+ if (oldpass == NULL || *oldpass == '\0')
+ return PAM_SUCCESS;
+
+ if ((oldpf = fopen (OLD_PASSWORDS_FILE, "r")) == NULL)
+ {
+ if (errno == ENOENT)
+ {
+ pam_syslog (pamh, LOG_NOTICE, "Creating %s",
+ OLD_PASSWORDS_FILE);
+ do_create = 1;
+ }
+ else
+ {
+ pam_syslog (pamh, LOG_ERR, "Cannot open %s: %m",
+ OLD_PASSWORDS_FILE);
+ return PAM_AUTHTOK_ERR;
+ }
+ }
+ else if (fstat (fileno (oldpf), &opasswd_stat) < 0)
+ {
+ pam_syslog (pamh, LOG_ERR, "Cannot stat %s: %m", OLD_PASSWORDS_FILE);
+ fclose (oldpf);
+ return PAM_AUTHTOK_ERR;
+ }
+
+ /* Open a temp passwd file */
+ newpf_fd = mkstemp (opasswd_tmp);
+ if (newpf_fd == -1)
+ {
+ pam_syslog (pamh, LOG_ERR, "Cannot create %s temp file: %m",
+ OLD_PASSWORDS_FILE);
+ if (oldpf)
+ fclose (oldpf);
+ return PAM_AUTHTOK_ERR;
+ }
+ if (do_create)
+ {
+ if (fchmod (newpf_fd, S_IRUSR|S_IWUSR) != 0)
+ pam_syslog (pamh, LOG_ERR,
+ "Cannot set permissions of %s temp file: %m",
+ OLD_PASSWORDS_FILE);
+ if (fchown (newpf_fd, 0, 0) != 0)
+ pam_syslog (pamh, LOG_ERR,
+ "Cannot set owner/group of %s temp file: %m",
+ OLD_PASSWORDS_FILE);
+ }
+ else
+ {
+ if (fchmod (newpf_fd, opasswd_stat.st_mode) != 0)
+ pam_syslog (pamh, LOG_ERR,
+ "Cannot set permissions of %s temp file: %m",
+ OLD_PASSWORDS_FILE);
+ if (fchown (newpf_fd, opasswd_stat.st_uid, opasswd_stat.st_gid) != 0)
+ pam_syslog (pamh, LOG_ERR,
+ "Cannot set owner/group of %s temp file: %m",
+ OLD_PASSWORDS_FILE);
+ }
+ newpf = fdopen (newpf_fd, "w+");
+ if (newpf == NULL)
+ {
+ pam_syslog (pamh, LOG_ERR, "Cannot fdopen %s: %m", opasswd_tmp);
+ if (oldpf)
+ fclose (oldpf);
+ close (newpf_fd);
+ retval = PAM_AUTHTOK_ERR;
+ goto error_opasswd;
+ }
+
+ if (!do_create)
+ while (!feof (oldpf))
+ {
+ char *cp, *tmp, *save;
+#if defined(HAVE_GETLINE)
+ ssize_t n = getline (&buf, &buflen, oldpf);
+#elif defined (HAVE_GETDELIM)
+ ssize_t n = getdelim (&buf, &buflen, '\n', oldpf);
+#else
+ ssize_t n;
+
+ if (buf == NULL)
+ {
+ buflen = DEFAULT_BUFLEN;
+ buf = malloc (buflen);
+ if (buf == NULL)
+ return PAM_BUF_ERR;
+
+ }
+ buf[0] = '\0';
+ fgets (buf, buflen - 1, oldpf);
+ n = strlen (buf);
+#endif /* HAVE_GETLINE / HAVE_GETDELIM */
+
+ cp = buf;
+ save = strdup (buf); /* Copy to write the original data back. */
+ if (save == NULL)
+ return PAM_BUF_ERR;
+
+ 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 */
+ goto write_old_data;
+
+ if (cp[strlen (cp) - 1] == '\n')
+ cp[strlen (cp) - 1] = '\0';
+
+ if (strncmp (cp, user, strlen (user)) == 0 &&
+ cp[strlen (user)] == ':')
+ {
+ /* We found the line we needed */
+ opwd entry;
+
+ if (parse_entry (cp, &entry) == 0)
+ {
+ char *out = NULL;
+
+ found = 1;
+
+ /* Don't save the current password twice */
+ if (entry.old_passwords)
+ {
+ /* there is only one password */
+ if (strcmp (entry.old_passwords, oldpass) == 0)
+ goto write_old_data;
+ else
+ {
+ /* check last entry */
+ cp = strstr (entry.old_passwords, oldpass);
+
+ if (cp && strcmp (cp, oldpass) == 0)
+ { /* the end is the same, check that there
+ is a "," before. */
+ --cp;
+ if (*cp == ',')
+ goto write_old_data;
+ }
+ }
+ }
+
+ /* increase count. */
+ entry.count++;
+
+ /* check that we don't remember to many passwords. */
+ while (entry.count > howmany)
+ {
+ char *p = strpbrk (entry.old_passwords, ",");
+ if (p != NULL)
+ entry.old_passwords = ++p;
+ entry.count--;
+ }
+
+ if (entry.old_passwords == NULL)
+ {
+ if (asprintf (&out, "%s:%s:%d:%s\n",
+ entry.user, entry.uid, entry.count,
+ oldpass) < 0)
+ {
+ retval = PAM_AUTHTOK_ERR;
+ fclose (oldpf);
+ fclose (newpf);
+ goto error_opasswd;
+ }
+ }
+ else
+ {
+ if (asprintf (&out, "%s:%s:%d:%s,%s\n",
+ entry.user, entry.uid, entry.count,
+ entry.old_passwords, oldpass) < 0)
+ {
+ retval = PAM_AUTHTOK_ERR;
+ fclose (oldpf);
+ fclose (newpf);
+ goto error_opasswd;
+ }
+ }
+
+ if (fputs (out, newpf) < 0)
+ {
+ free (out);
+ free (save);
+ retval = PAM_AUTHTOK_ERR;
+ fclose (oldpf);
+ fclose (newpf);
+ goto error_opasswd;
+ }
+ free (out);
+ }
+ }
+ else
+ {
+ write_old_data:
+ if (fputs (save, newpf) < 0)
+ {
+ free (save);
+ retval = PAM_AUTHTOK_ERR;
+ fclose (oldpf);
+ fclose (newpf);
+ goto error_opasswd;
+ }
+ }
+ free (save);
+ }
+
+ if (!found)
+ {
+ char *out;
+
+ if (asprintf (&out, "%s:%d:1:%s\n", user, uid, oldpass) < 0)
+ {
+ retval = PAM_AUTHTOK_ERR;
+ if (oldpf)
+ fclose (oldpf);
+ fclose (newpf);
+ goto error_opasswd;
+ }
+ if (fputs (out, newpf) < 0)
+ {
+ free (out);
+ retval = PAM_AUTHTOK_ERR;
+ if (oldpf)
+ fclose (oldpf);
+ fclose (newpf);
+ goto error_opasswd;
+ }
+ free (out);
+ }
+
+ if (oldpf)
+ if (fclose (oldpf) != 0)
+ {
+ pam_syslog (pamh, LOG_ERR, "Error while closing old opasswd file: %m");
+ retval = PAM_AUTHTOK_ERR;
+ fclose (newpf);
+ goto error_opasswd;
+ }
+
+ if (fflush (newpf) != 0 || fsync (fileno (newpf)) != 0)
+ {
+ pam_syslog (pamh, LOG_ERR,
+ "Error while syncing temporary opasswd file: %m");
+ retval = PAM_AUTHTOK_ERR;
+ fclose (newpf);
+ goto error_opasswd;
+ }
+
+ if (fclose (newpf) != 0)
+ {
+ pam_syslog (pamh, LOG_ERR,
+ "Error while closing temporary opasswd file: %m");
+ retval = PAM_AUTHTOK_ERR;
+ goto error_opasswd;
+ }
+
+ unlink (OLD_PASSWORDS_FILE".old");
+ if (link (OLD_PASSWORDS_FILE, OLD_PASSWORDS_FILE".old") != 0 &&
+ errno != ENOENT)
+ pam_syslog (pamh, LOG_ERR, "Cannot create backup file of %s: %m",
+ OLD_PASSWORDS_FILE);
+ rename (opasswd_tmp, OLD_PASSWORDS_FILE);
+ error_opasswd:
+ unlink (opasswd_tmp);
+
+ return retval;
+}
diff --git a/modules/pam_pwhistory/opasswd.h b/modules/pam_pwhistory/opasswd.h
new file mode 100644
index 00000000..e8a20139
--- /dev/null
+++ b/modules/pam_pwhistory/opasswd.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2008 Thorsten Kukuk <kukuk@suse.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __OPASSWD_H__
+#define __OPASSWD_H__
+
+extern int check_old_password (pam_handle_t *pamh, const char *user,
+ const char *newpass, int debug);
+extern int save_old_password (pam_handle_t *pamh, const char *user,
+ uid_t uid, const char *oldpass,
+ int howmany, int debug);
+
+#endif /* __OPASSWD_H__ */
diff --git a/modules/pam_pwhistory/pam_pwhistory.8 b/modules/pam_pwhistory/pam_pwhistory.8
new file mode 100644
index 00000000..4c16952f
--- /dev/null
+++ b/modules/pam_pwhistory/pam_pwhistory.8
@@ -0,0 +1,329 @@
+.\" Title: pam_pwhistory
+.\" Author: [see the "AUTHOR" section]
+.\" Generator: DocBook XSL Stylesheets v1.74.0 <http://docbook.sf.net/>
+.\" Date: 06/16/2009
+.\" Manual: Linux-PAM Manual
+.\" Source: Linux-PAM Manual
+.\" Language: English
+.\"
+.TH "PAM_PWHISTORY" "8" "06/16/2009" "Linux-PAM Manual" "Linux\-PAM Manual"
+.\" -----------------------------------------------------------------
+.\" * (re)Define some macros
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" toupper - uppercase a string (locale-aware)
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.de toupper
+.tr aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ
+\\$*
+.tr aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz
+..
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" SH-xref - format a cross-reference to an SH section
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.de SH-xref
+.ie n \{\
+.\}
+.toupper \\$*
+.el \{\
+\\$*
+.\}
+..
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" SH - level-one heading that works better for non-TTY output
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.de1 SH
+.\" put an extra blank line of space above the head in non-TTY output
+.if t \{\
+.sp 1
+.\}
+.sp \\n[PD]u
+.nr an-level 1
+.set-an-margin
+.nr an-prevailing-indent \\n[IN]
+.fi
+.in \\n[an-margin]u
+.ti 0
+.HTML-TAG ".NH \\n[an-level]"
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+\." make the size of the head bigger
+.ps +3
+.ft B
+.ne (2v + 1u)
+.ie n \{\
+.\" if n (TTY output), use uppercase
+.toupper \\$*
+.\}
+.el \{\
+.nr an-break-flag 0
+.\" if not n (not TTY), use normal case (not uppercase)
+\\$1
+.in \\n[an-margin]u
+.ti 0
+.\" if not n (not TTY), put a border/line under subheading
+.sp -.6
+\l'\n(.lu'
+.\}
+..
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" SS - level-two heading that works better for non-TTY output
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.de1 SS
+.sp \\n[PD]u
+.nr an-level 1
+.set-an-margin
+.nr an-prevailing-indent \\n[IN]
+.fi
+.in \\n[IN]u
+.ti \\n[SN]u
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.ps \\n[PS-SS]u
+\." make the size of the head bigger
+.ps +2
+.ft B
+.ne (2v + 1u)
+.if \\n[.$] \&\\$*
+..
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" BB/BE - put background/screen (filled box) around block of text
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.de BB
+.if t \{\
+.sp -.5
+.br
+.in +2n
+.ll -2n
+.gcolor red
+.di BX
+.\}
+..
+.de EB
+.if t \{\
+.if "\\$2"adjust-for-leading-newline" \{\
+.sp -1
+.\}
+.br
+.di
+.in
+.ll
+.gcolor
+.nr BW \\n(.lu-\\n(.i
+.nr BH \\n(dn+.5v
+.ne \\n(BHu+.5v
+.ie "\\$2"adjust-for-leading-newline" \{\
+\M[\\$1]\h'1n'\v'+.5v'\D'P \\n(BWu 0 0 \\n(BHu -\\n(BWu 0 0 -\\n(BHu'\M[]
+.\}
+.el \{\
+\M[\\$1]\h'1n'\v'-.5v'\D'P \\n(BWu 0 0 \\n(BHu -\\n(BWu 0 0 -\\n(BHu'\M[]
+.\}
+.in 0
+.sp -.5v
+.nf
+.BX
+.in
+.sp .5v
+.fi
+.\}
+..
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" BM/EM - put colored marker in margin next to block of text
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.de BM
+.if t \{\
+.br
+.ll -2n
+.gcolor red
+.di BX
+.\}
+..
+.de EM
+.if t \{\
+.br
+.di
+.ll
+.gcolor
+.nr BH \\n(dn
+.ne \\n(BHu
+\M[\\$1]\D'P -.75n 0 0 \\n(BHu -(\\n[.i]u - \\n(INu - .75n) 0 0 -\\n(BHu'\M[]
+.in 0
+.nf
+.BX
+.in
+.fi
+.\}
+..
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "Name"
+pam_pwhistory \- PAM module to remember last passwords
+.SH "Synopsis"
+.fam C
+.HP \w'\fBpam_pwhistory\&.so\fR\ 'u
+\fBpam_pwhistory\&.so\fR [debug] [use_authtok] [enforce_for_root] [remember=\fIN\fR] [retry=\fIN\fR] [authtok_type=\fISTRING\fR]
+.fam
+.SH "DESCRIPTION"
+.PP
+This module saves the last passwords for each user in order to force password change history and keep the user from alternating between the same password too frequently\&.
+.PP
+This module does not work together with kerberos\&. In general, it does not make much sense to use this module in conjunction with NIS or LDAP, since the old passwords are stored on the local machine and are not available on another machine for password history checking\&.
+.SH "OPTIONS"
+.PP
+\fBdebug\fR
+.RS 4
+Turns on debugging via
+\fBsyslog\fR(3)\&.
+.RE
+.PP
+\fBuse_authtok\fR
+.RS 4
+When password changing enforce the module to use the new password 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
+\fBenforce_for_root\fR
+.RS 4
+If this option is set, the check is enforced for root, too\&.
+.RE
+.PP
+\fBremember=\fR\fB\fIN\fR\fR
+.RS 4
+The last
+\fIN\fR
+passwords for each user are saved in
+\FC/etc/security/opasswd\F[]\&. The default is
+\fI10\fR\&.
+.RE
+.PP
+\fBretry=\fR\fB\fIN\fR\fR
+.RS 4
+Prompt user at most
+\fIN\fR
+times before returning with error\&. The default is
+\fI1\fR\&.
+.RE
+.PP
+\fBauthtok_type=\fR\fB\fISTRING\fR\fR
+.RS 4
+See
+\fBpam_get_authtok\fR(3)
+for more details\&.
+.RE
+.SH "MODULE TYPES PROVIDED"
+.PP
+Only the
+\fBpassword\fR
+module type is provided\&.
+.SH "RETURN VALUES"
+.PP
+PAM_AUTHTOK_ERR
+.RS 4
+No new password was entered, the user aborted password change or new password couldn\'t be set\&.
+.RE
+.PP
+PAM_IGNORE
+.RS 4
+Password history was disabled\&.
+.RE
+.PP
+PAM_MAXTRIES
+.RS 4
+Password was rejected too often\&.
+.RE
+.PP
+PAM_USER_UNKNOWN
+.RS 4
+User is not known to system\&.
+.RE
+.SH "EXAMPLES"
+.PP
+An example password section would be:
+.sp
+.if n \{\
+.RS 4
+.\}
+.fam C
+.ps -1
+.nf
+.if t \{\
+.sp -1
+.\}
+.BB lightgray adjust-for-leading-newline
+.sp -1
+
+#%PAM\-1\&.0
+password required pam_pwhistory\&.so
+password required pam_unix\&.so use_authtok
+
+.EB lightgray adjust-for-leading-newline
+.if t \{\
+.sp 1
+.\}
+.fi
+.fam
+.ps +1
+.if n \{\
+.RE
+.\}
+.PP
+In combination with
+\fBpam_cracklib\fR:
+.sp
+.if n \{\
+.RS 4
+.\}
+.fam C
+.ps -1
+.nf
+.if t \{\
+.sp -1
+.\}
+.BB lightgray adjust-for-leading-newline
+.sp -1
+
+#%PAM\-1\&.0
+password required pam_cracklib\&.so retry=3
+password required pam_pwhistory\&.so use_authtok
+password required pam_unix\&.so use_authtok
+
+.EB lightgray adjust-for-leading-newline
+.if t \{\
+.sp 1
+.\}
+.fi
+.fam
+.ps +1
+.if n \{\
+.RE
+.\}
+.sp
+.SH "FILES"
+.PP
+\FC/etc/security/opasswd\F[]
+.RS 4
+File with password history
+.RE
+.SH "SEE ALSO"
+.PP
+
+\fBpam.conf\fR(5),
+\fBpam.d\fR(5),
+\fBpam\fR(8)
+\fBpam_get_authtok\fR(3)
+.SH "AUTHOR"
+.PP
+pam_pwhistory was written by Thorsten Kukuk <kukuk@thkukuk\&.de>
diff --git a/modules/pam_pwhistory/pam_pwhistory.8.xml b/modules/pam_pwhistory/pam_pwhistory.8.xml
new file mode 100644
index 00000000..7696353f
--- /dev/null
+++ b/modules/pam_pwhistory/pam_pwhistory.8.xml
@@ -0,0 +1,245 @@
+<?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_pwhistory">
+
+ <refmeta>
+ <refentrytitle>pam_pwhistory</refentrytitle>
+ <manvolnum>8</manvolnum>
+ <refmiscinfo class="sectdesc">Linux-PAM Manual</refmiscinfo>
+ </refmeta>
+
+ <refnamediv id="pam_pwhistory-name">
+ <refname>pam_pwhistory</refname>
+ <refpurpose>PAM module to remember last passwords</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis id="pam_pwhistory-cmdsynopsis">
+ <command>pam_pwhistory.so</command>
+ <arg choice="opt">
+ debug
+ </arg>
+ <arg choice="opt">
+ use_authtok
+ </arg>
+ <arg choice="opt">
+ enforce_for_root
+ </arg>
+ <arg choice="opt">
+ remember=<replaceable>N</replaceable>
+ </arg>
+ <arg choice="opt">
+ retry=<replaceable>N</replaceable>
+ </arg>
+ <arg choice="opt">
+ authtok_type=<replaceable>STRING</replaceable>
+ </arg>
+
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1 id="pam_pwhistory-description">
+
+ <title>DESCRIPTION</title>
+
+ <para>
+ This module saves the last passwords for each user in order
+ to force password change history and keep the user from
+ alternating between the same password too frequently.
+ </para>
+ <para>
+ This module does not work together with kerberos. In general,
+ it does not make much sense to use this module in conjunction
+ with NIS or LDAP, since the old passwords are stored on the
+ local machine and are not available on another machine for
+ password history checking.
+ </para>
+ </refsect1>
+
+ <refsect1 id="pam_pwhistory-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>use_authtok</option>
+ </term>
+ <listitem>
+ <para>
+ When password changing enforce the module to use the new password
+ 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>enforce_for_root</option>
+ </term>
+ <listitem>
+ <para>
+ If this option is set, the check is enforced for root, too.
+ </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>.
+ The default is <emphasis>10</emphasis>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>retry=<replaceable>N</replaceable></option>
+ </term>
+ <listitem>
+ <para>
+ Prompt user at most <replaceable>N</replaceable> times
+ before returning with error. The default is
+ <emphasis>1</emphasis>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>authtok_type=<replaceable>STRING</replaceable></option>
+ </term>
+ <listitem>
+ <para>
+ See <citerefentry>
+ <refentrytitle>pam_get_authtok</refentrytitle><manvolnum>3</manvolnum>
+ </citerefentry> for more details.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+ </refsect1>
+
+ <refsect1 id="pam_pwhistory-types">
+ <title>MODULE TYPES PROVIDED</title>
+ <para>
+ Only the <option>password</option> module type is provided.
+ </para>
+ </refsect1>
+
+ <refsect1 id='pam_pwhistory-return_values'>
+ <title>RETURN VALUES</title>
+ <variablelist>
+ <varlistentry>
+ <term>PAM_AUTHTOK_ERR</term>
+ <listitem>
+ <para>
+ No new password was entered, the user aborted password
+ change or new password couldn't be set.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>PAM_IGNORE</term>
+ <listitem>
+ <para>
+ Password history was disabled.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>PAM_MAXTRIES</term>
+ <listitem>
+ <para>
+ Password was rejected too often.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>PAM_USER_UNKNOWN</term>
+ <listitem>
+ <para>
+ User is not known to system.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1 id='pam_pwhistory-examples'>
+ <title>EXAMPLES</title>
+ <para>
+ An example password section would be:
+ <programlisting>
+#%PAM-1.0
+password required pam_pwhistory.so
+password required pam_unix.so use_authtok
+ </programlisting>
+ </para>
+ <para>
+ In combination with <command>pam_cracklib</command>:
+ <programlisting>
+#%PAM-1.0
+password required pam_cracklib.so retry=3
+password required pam_pwhistory.so use_authtok
+password required pam_unix.so use_authtok
+ </programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1 id="pam_pwhistory-files">
+ <title>FILES</title>
+ <variablelist>
+ <varlistentry>
+ <term><filename>/etc/security/opasswd</filename></term>
+ <listitem>
+ <para>File with password history</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1 id='pam_pwhistory-see_also'>
+ <title>SEE ALSO</title>
+ <para>
+ <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>
+ <citerefentry>
+ <refentrytitle>pam_get_authtok</refentrytitle><manvolnum>3</manvolnum>
+ </citerefentry>
+ </para>
+ </refsect1>
+
+ <refsect1 id='pam_pwhistory-author'>
+ <title>AUTHOR</title>
+ <para>
+ pam_pwhistory was written by Thorsten Kukuk &lt;kukuk@thkukuk.de&gt;
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/modules/pam_pwhistory/pam_pwhistory.c b/modules/pam_pwhistory/pam_pwhistory.c
new file mode 100644
index 00000000..0f6ffca3
--- /dev/null
+++ b/modules/pam_pwhistory/pam_pwhistory.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2008 Thorsten Kukuk
+ * Author: Thorsten Kukuk <kukuk@suse.de>
+ *
+ * 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.
+ */
+
+#if defined(HAVE_CONFIG_H)
+#include <config.h>
+#endif
+
+#define PAM_SM_PASSWORD
+
+#include <pwd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <shadow.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <security/pam_modules.h>
+#include <security/pam_modutil.h>
+#include <security/pam_ext.h>
+#include <security/_pam_macros.h>
+
+#include "opasswd.h"
+
+#define DEFAULT_BUFLEN 2048
+
+struct options_t {
+ int debug;
+ int enforce_for_root;
+ int remember;
+ int tries;
+};
+typedef struct options_t options_t;
+
+
+static void
+parse_option (pam_handle_t *pamh, const char *argv, options_t *options)
+{
+ if (strcasecmp (argv, "try_first_pass") == 0)
+ /* ignore */;
+ else if (strcasecmp (argv, "use_first_pass") == 0)
+ /* ignore */;
+ else if (strcasecmp (argv, "use_authtok") == 0)
+ /* ignore, handled by pam_get_authtok */;
+ else if (strcasecmp (argv, "debug") == 0)
+ options->debug = 1;
+ else if (strncasecmp (argv, "remember=", 9) == 0)
+ {
+ options->remember = strtol(&argv[9], NULL, 10);
+ if (options->remember < 0)
+ options->remember = 0;
+ if (options->remember > 400)
+ options->remember = 400;
+ }
+ else if (strncasecmp (argv, "retry=", 6) == 0)
+ {
+ options->tries = strtol(&argv[6], NULL, 10);
+ if (options->tries < 0)
+ options->tries = 1;
+ }
+ else if (strcasecmp (argv, "enforce_for_root") == 0)
+ options->enforce_for_root = 1;
+ else if (strncasecmp (argv, "authtok_type=", 13) == 0)
+ { /* ignore, for pam_get_authtok */; }
+ else
+ pam_syslog (pamh, LOG_ERR, "pam_pwhistory: unknown option: %s", argv);
+}
+
+
+PAM_EXTERN int
+pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+ struct passwd *pwd;
+ const char *newpass;
+ const char *user;
+ int retval, tries;
+ options_t options;
+
+ memset (&options, 0, sizeof (options));
+
+ /* Set some default values, which could be overwritten later. */
+ options.remember = 10;
+ options.tries = 1;
+
+ /* Parse parameters for module */
+ for ( ; argc-- > 0; argv++)
+ parse_option (pamh, *argv, &options);
+
+ if (options.debug)
+ pam_syslog (pamh, LOG_DEBUG, "pam_sm_chauthtok entered");
+
+
+ if (options.remember == 0)
+ return PAM_IGNORE;
+
+ retval = pam_get_user (pamh, &user, NULL);
+ if (retval != PAM_SUCCESS)
+ return retval;
+
+ if (user == NULL || strlen (user) == 0)
+ {
+ if (options.debug)
+ pam_syslog (pamh, LOG_DEBUG,
+ "User is not known to system");
+
+ return PAM_USER_UNKNOWN;
+ }
+
+ if (flags & PAM_PRELIM_CHECK)
+ {
+ if (options.debug)
+ pam_syslog (pamh, LOG_DEBUG,
+ "pam_sm_chauthtok(PAM_PRELIM_CHECK)");
+
+ return PAM_SUCCESS;
+ }
+
+ pwd = pam_modutil_getpwnam (pamh, user);
+ if (pwd == NULL)
+ return PAM_USER_UNKNOWN;
+
+ /* Ignore root if not enforced */
+ if (pwd->pw_uid == 0 && !options.enforce_for_root)
+ return PAM_SUCCESS;
+
+ if ((strcmp(pwd->pw_passwd, "x") == 0) ||
+ ((pwd->pw_passwd[0] == '#') &&
+ (pwd->pw_passwd[1] == '#') &&
+ (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0)))
+ {
+ struct spwd *spw = pam_modutil_getspnam (pamh, user);
+ if (spw == NULL)
+ return PAM_USER_UNKNOWN;
+
+ retval = save_old_password (pamh, user, pwd->pw_uid, spw->sp_pwdp,
+ options.remember, options.debug);
+ if (retval != PAM_SUCCESS)
+ return retval;
+ }
+ else
+ {
+ retval = save_old_password (pamh, user, pwd->pw_uid, pwd->pw_passwd,
+ options.remember, options.debug);
+ if (retval != PAM_SUCCESS)
+ return retval;
+ }
+
+ newpass = NULL;
+ tries = 0;
+ while ((newpass == NULL) && (tries < options.tries))
+ {
+ retval = pam_get_authtok (pamh, PAM_AUTHTOK, &newpass, NULL);
+ if (retval != PAM_SUCCESS && retval != PAM_TRY_AGAIN)
+ return retval;
+ tries++;
+
+ if (newpass == NULL || retval == PAM_TRY_AGAIN)
+ continue;
+
+ if (options.debug)
+ {
+ if (newpass)
+ pam_syslog (pamh, LOG_DEBUG, "got new auth token");
+ else
+ pam_syslog (pamh, LOG_DEBUG, "got no auth token");
+ }
+
+ if (retval != PAM_SUCCESS || newpass == NULL)
+ {
+ if (retval == PAM_CONV_AGAIN)
+ retval = PAM_INCOMPLETE;
+ return retval;
+ }
+
+ if (options.debug)
+ pam_syslog (pamh, LOG_DEBUG, "check against old password file");
+
+ if (check_old_password (pamh, user, newpass,
+ options.debug) != PAM_SUCCESS)
+ {
+ pam_error (pamh,
+ _("Password has been already used. Choose another."));
+ newpass = NULL;
+ /* Remove password item, else following module will use it */
+ pam_set_item (pamh, PAM_AUTHTOK, (void *) NULL);
+ continue;
+ }
+ }
+
+ if (newpass == NULL && tries >= options.tries)
+ {
+ if (options.debug)
+ pam_syslog (pamh, LOG_DEBUG, "Aborted, too many tries");
+ return PAM_MAXTRIES;
+ }
+
+ /* Remember new password */
+ return pam_set_item (pamh, PAM_AUTHTOK, newpass);
+}
+
+
+#ifdef PAM_STATIC
+/* static module data */
+struct pam_module _pam_pwhistory_modstruct = {
+ "pam_pwhistory",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ pam_sm_chauthtok
+};
+#endif
diff --git a/modules/pam_pwhistory/tst-pam_pwhistory b/modules/pam_pwhistory/tst-pam_pwhistory
new file mode 100755
index 00000000..3531a88a
--- /dev/null
+++ b/modules/pam_pwhistory/tst-pam_pwhistory
@@ -0,0 +1,2 @@
+#!/bin/sh
+../../tests/tst-dlopen .libs/pam_pwhistory.so