From 43d230bb77f2d14977cf2d060cb8351af453d5467964b56ca812f5303e478a6e Mon Sep 17 00:00:00 2001 From: OBS User unknown Date: Sat, 11 Oct 2008 22:27:19 +0000 Subject: [PATCH] OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/pam?expand=0&rev=18 --- Linux-PAM-1.0.2-SUSE-docs.tar.bz2 | 4 +- Linux-PAM-docu.diff | 25 - pam.changes | 6 + pam.spec | 16 +- pam_lastlog.diff | 325 ++++++ pam_pwhistory-0.1.diff | 1725 +++++++++++++++++++++++++++++ 6 files changed, 2072 insertions(+), 29 deletions(-) create mode 100644 pam_lastlog.diff create mode 100644 pam_pwhistory-0.1.diff diff --git a/Linux-PAM-1.0.2-SUSE-docs.tar.bz2 b/Linux-PAM-1.0.2-SUSE-docs.tar.bz2 index 13eac9b..85035f6 100644 --- a/Linux-PAM-1.0.2-SUSE-docs.tar.bz2 +++ b/Linux-PAM-1.0.2-SUSE-docs.tar.bz2 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2edaf8a8c29b7a214f99871aeb19a427c4a368604bc40281c655adfffb7852bc -size 475385 +oid sha256:bd4474f01ec6fcb5e8af676b8aa5677a8784f5a027eaca0b64875d5d1dc467f3 +size 477831 diff --git a/Linux-PAM-docu.diff b/Linux-PAM-docu.diff index d1a460a..bd2f5b3 100644 --- a/Linux-PAM-docu.diff +++ b/Linux-PAM-docu.diff @@ -944,31 +944,6 @@ , - pam.d8 -+ pam.d5 - , - - pam8 ---- Linux-PAM-1.0/modules/pam_lastlog/pam_lastlog.8.xml 2006-06-09 18:44:07.000000000 +0200 -+++ Linux-PAM/modules/pam_lastlog/pam_lastlog.8.xml 2008-08-20 20:56:26.000000000 +0200 -@@ -140,10 +140,10 @@ - - - -- -- MODULE SERVICES PROVIDED -+ -+ MODULE TYPES PROVIDED - -- Only the service is supported. -+ Only the module type is provided. - - - -@@ -213,7 +213,7 @@ - pam.conf5 - , - -- pam.d8 + pam.d5 , diff --git a/pam.changes b/pam.changes index 569c5f8..a0bc560 100644 --- a/pam.changes +++ b/pam.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Sat Oct 11 17:06:49 CEST 2008 - kukuk@suse.de + +- Enhance pam_lastlog with status output +- Add pam_pwhistory as tech preview + ------------------------------------------------------------------- Fri Sep 26 13:44:21 CEST 2008 - kukuk@suse.de diff --git a/pam.spec b/pam.spec index b0c7948..b8d91f0 100644 --- a/pam.spec +++ b/pam.spec @@ -35,9 +35,8 @@ License: BSD 3-Clause; GPL v2 or later Group: System/Libraries AutoReqProv: on Version: 1.0.2 -Release: 8 +Release: 9 Summary: A Security Tool that Provides Authentication for Applications -Obsoletes: pam-laus Source: Linux-PAM-%{version}.tar.bz2 Source1: Linux-PAM-%{version}-SUSE-docs.tar.bz2 Source2: securetty @@ -57,6 +56,8 @@ Patch5: pam-1.0.0-selinux-env-params.patch Patch6: Linux-PAM-docu-generated.diff Patch7: pam_mail.diff Patch8: pam_tally-fdleak.diff +Patch9: pam_pwhistory-0.1.diff +Patch10: pam_lastlog.diff %description PAM (Pluggable Authentication Modules) is a system security tool that @@ -107,8 +108,15 @@ building both PAM-aware applications and modules for use with PAM. %patch6 -p1 %patch7 -p0 %patch8 -p0 +%patch9 -p0 +chmod 755 modules/pam_pwhistory/tst-pam_pwhistory +%patch10 -p0 %build +aclocal -I m4 --install --force +libtoolize --force --automake --copy +automake --add-missing --copy +autoreconf CFLAGS="$RPM_OPT_FLAGS" \ ./configure \ --infodir=%{_infodir} \ @@ -263,6 +271,7 @@ rm -rf $RPM_BUILD_ROOT %endif /%{_lib}/security/pam_nologin.so /%{_lib}/security/pam_permit.so +/%{_lib}/security/pam_pwhistory.so /%{_lib}/security/pam_rhosts.so /%{_lib}/security/pam_rootok.so /%{_lib}/security/pam_securetty.so @@ -308,6 +317,9 @@ rm -rf $RPM_BUILD_ROOT %{_libdir}/libpam_misc.so %changelog +* Sat Oct 11 2008 kukuk@suse.de +- Enhance pam_lastlog with status output +- Add pam_pwhistory as tech preview * Fri Sep 26 2008 kukuk@suse.de - pam_tally: fix fd leak - pam_mail: fix "quiet" option diff --git a/pam_lastlog.diff b/pam_lastlog.diff new file mode 100644 index 0000000..80c6de4 --- /dev/null +++ b/pam_lastlog.diff @@ -0,0 +1,325 @@ +2008-09-30 Tomas Mraz + + * modules/pam_lastlog/pam_lastlog.8.xml: Document new options + noupdate and showfailed. + * modules/pam_lastlog/pam_lastlog.c(pam_parse): Recognize the new + options. + (last_login_read): New output parameter lltime. Do not display + the last login message if it would be empty. + (last_login_date): New output parameter lltime. Do not write the + last login info when LASTLOG_UPDATE is not set. + (last_login_failed): New function to display the last bad login + attempt from btmp. + (pam_sm_open_session): Obtain lltime from last_login_date() and + call last_login_failed() when appropriate. + +--- modules/pam_lastlog/pam_lastlog.8.xml 9 Jun 2006 16:44:07 -0000 1.2 ++++ modules/pam_lastlog/pam_lastlog.8.xml 30 Sep 2008 14:40:39 -0000 1.5 +@@ -39,6 +39,12 @@ + + nowtmp + ++ ++ noupdate ++ ++ ++ showfailed ++ + + + +@@ -137,13 +143,35 @@ + + + ++ ++ ++ ++ ++ ++ ++ Don't update any file. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Display number of failed login attempts and the date of the ++ last failed attempt from btmp. The date is not displayed ++ when is specified. ++ ++ ++ + + + +- +- MODULE SERVICES PROVIDED ++ ++ MODULE TYPES PROVIDED + +- Only the service is supported. ++ Only the module type is provided. + + + +@@ -213,7 +241,7 @@ + pam.conf5 + , + +- pam.d8 ++ pam.d5 + , + + pam8 +--- modules/pam_lastlog/pam_lastlog.c 24 Aug 2006 18:29:30 -0000 1.23 ++++ modules/pam_lastlog/pam_lastlog.c 30 Sep 2008 14:40:39 -0000 1.24 +@@ -46,6 +46,10 @@ + }; + #endif /* hpux */ + ++#ifndef _PATH_BTMP ++# define _PATH_BTMP "/var/log/btmp" ++#endif ++ + /* XXX - time before ignoring lock. Is 1 sec enough? */ + #define LASTLOG_IGNORE_LOCK_TIME 1 + +@@ -75,11 +79,13 @@ + #define LASTLOG_DEBUG 020 /* send info to syslog(3) */ + #define LASTLOG_QUIET 040 /* keep quiet about things */ + #define LASTLOG_WTMP 0100 /* log to wtmp as well as lastlog */ ++#define LASTLOG_BTMP 0200 /* display failed login info from btmp */ ++#define LASTLOG_UPDATE 0400 /* update the lastlog and wtmp files (default) */ + + static int + _pam_parse(pam_handle_t *pamh, int flags, int argc, const char **argv) + { +- int ctrl=(LASTLOG_DATE|LASTLOG_HOST|LASTLOG_LINE|LASTLOG_WTMP); ++ int ctrl=(LASTLOG_DATE|LASTLOG_HOST|LASTLOG_LINE|LASTLOG_WTMP|LASTLOG_UPDATE); + + /* does the appliction require quiet? */ + if (flags & PAM_SILENT) { +@@ -105,6 +111,10 @@ + ctrl |= LASTLOG_NEVER; + } else if (!strcmp(*argv,"nowtmp")) { + ctrl &= ~LASTLOG_WTMP; ++ } else if (!strcmp(*argv,"noupdate")) { ++ ctrl &= ~(LASTLOG_WTMP|LASTLOG_UPDATE); ++ } else if (!strcmp(*argv,"showfailed")) { ++ ctrl |= LASTLOG_BTMP; + } else { + pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv); + } +@@ -135,7 +145,7 @@ + } + + static int +-last_login_read(pam_handle_t *pamh, int announce, int last_fd, uid_t uid) ++last_login_read(pam_handle_t *pamh, int announce, int last_fd, uid_t uid, time_t *lltime) + { + struct flock last_lock; + struct lastlog last_login; +@@ -166,6 +176,7 @@ + last_lock.l_type = F_UNLCK; + (void) fcntl(last_fd, F_SETLK, &last_lock); /* unlock */ + ++ *lltime = last_login.ll_time; + if (!last_login.ll_time) { + if (announce & LASTLOG_DEBUG) { + pam_syslog(pamh, LOG_DEBUG, +@@ -216,8 +227,9 @@ + } + } + +- /* TRANSLATORS: "Last login: from on " */ +- retval = pam_info(pamh, _("Last login:%s%s%s"), ++ if (date != NULL || host != NULL || line != NULL) ++ /* TRANSLATORS: "Last login: from on " */ ++ retval = pam_info(pamh, _("Last login:%s%s%s"), + date ? date : "", + host ? host : "", + line ? line : ""); +@@ -320,13 +332,13 @@ + } + + static int +-last_login_date(pam_handle_t *pamh, int announce, uid_t uid, const char *user) ++last_login_date(pam_handle_t *pamh, int announce, uid_t uid, const char *user, time_t *lltime) + { + int retval; + int last_fd; + + /* obtain the last login date and all the relevant info */ +- last_fd = open(_PATH_LASTLOG, O_RDWR); ++ last_fd = open(_PATH_LASTLOG, announce&LASTLOG_UPDATE ? O_RDWR : O_RDONLY); + if (last_fd < 0) { + if (errno == ENOENT) { + last_fd = open(_PATH_LASTLOG, O_RDWR|O_CREAT, +@@ -353,7 +365,7 @@ + return PAM_SERVICE_ERR; + } + +- retval = last_login_read(pamh, announce, last_fd, uid); ++ retval = last_login_read(pamh, announce, last_fd, uid, lltime); + if (retval != PAM_SUCCESS) + { + close(last_fd); +@@ -361,7 +373,9 @@ + return retval; + } + +- retval = last_login_write(pamh, announce, last_fd, uid, user); ++ if (announce & LASTLOG_UPDATE) { ++ retval = last_login_write(pamh, announce, last_fd, uid, user); ++ } + + close(last_fd); + D(("all done with last login")); +@@ -369,6 +383,121 @@ + return retval; + } + ++static int ++last_login_failed(pam_handle_t *pamh, int announce, const char *user, time_t lltime) ++{ ++ int retval; ++ int fd; ++ struct utmp ut; ++ struct utmp utuser; ++ int failed = 0; ++ char the_time[256]; ++ char *date = NULL; ++ char *host = NULL; ++ char *line = NULL; ++ ++ if (strlen(user) > UT_NAMESIZE) { ++ pam_syslog(pamh, LOG_WARNING, "username too long, output might be inaccurate"); ++ } ++ ++ /* obtain the failed login attempt records from btmp */ ++ fd = open(_PATH_BTMP, O_RDONLY); ++ if (fd < 0) { ++ pam_syslog(pamh, LOG_ERR, "unable to open %s: %m", _PATH_BTMP); ++ D(("unable to open %s file", _PATH_BTMP)); ++ return PAM_SERVICE_ERR; ++ } ++ ++ while ((retval=pam_modutil_read(fd, (void *)&ut, ++ sizeof(ut))) == sizeof(ut)) { ++ if (ut.ut_tv.tv_sec >= lltime && strncmp(ut.ut_user, user, UT_NAMESIZE) == 0) { ++ memcpy(&utuser, &ut, sizeof(utuser)); ++ failed++; ++ } ++ } ++ ++ if (failed) { ++ /* we want the date? */ ++ if (announce & LASTLOG_DATE) { ++ struct tm *tm, tm_buf; ++ time_t lf_time; ++ ++ lf_time = utuser.ut_tv.tv_sec; ++ tm = localtime_r (&lf_time, &tm_buf); ++ strftime (the_time, sizeof (the_time), ++ /* TRANSLATORS: "strftime options for date of last login" */ ++ _(" %a %b %e %H:%M:%S %Z %Y"), tm); ++ ++ date = the_time; ++ } ++ ++ /* we want & have the host? */ ++ if ((announce & LASTLOG_HOST) ++ && (utuser.ut_host[0] != '\0')) { ++ /* TRANSLATORS: " from " */ ++ if (asprintf(&host, _(" from %.*s"), UT_HOSTSIZE, ++ utuser.ut_host) < 0) { ++ pam_syslog(pamh, LOG_ERR, "out of memory"); ++ retval = PAM_BUF_ERR; ++ goto cleanup; ++ } ++ } ++ ++ /* we want and have the terminal? */ ++ if ((announce & LASTLOG_LINE) ++ && (utuser.ut_line[0] != '\0')) { ++ /* TRANSLATORS: " on " */ ++ if (asprintf(&line, _(" on %.*s"), UT_LINESIZE, ++ utuser.ut_line) < 0) { ++ pam_syslog(pamh, LOG_ERR, "out of memory"); ++ retval = PAM_BUF_ERR; ++ goto cleanup; ++ } ++ } ++ ++ if (line != NULL || date != NULL || host != NULL) { ++ /* TRANSLATORS: "Last failed login: from on " */ ++ pam_info(pamh, _("Last failed login:%s%s%s"), ++ date ? date : "", ++ host ? host : "", ++ line ? line : ""); ++ } ++ ++ _pam_drop(line); ++#if defined HAVE_DNGETTEXT && defined ENABLE_NLS ++ retval = asprintf (&line, dngettext(PACKAGE, ++ "There was %d failed login attempt since the last successful login.", ++ "There were %d failed login attempts since the last successful login.", ++ failed), ++ failed); ++#else ++ if (daysleft == 1) ++ retval = asprintf(&line, ++ _("There was %d failed login attempt since the last successful login."), ++ failed); ++ else ++ retval = asprintf(&line, ++ /* TRANSLATORS: only used if dngettext is not supported */ ++ _("There were %d failed login attempts since the last successful login."), ++ failed); ++#endif ++ if (retval >= 0) ++ retval = pam_info(pamh, "%s", line); ++ else { ++ retval = PAM_BUF_ERR; ++ line = NULL; ++ } ++ } ++ ++cleanup: ++ free(host); ++ free(line); ++ close(fd); ++ D(("all done with btmp")); ++ ++ return retval; ++} ++ + /* --- authentication management functions (only) --- */ + + PAM_EXTERN int +@@ -379,6 +508,7 @@ + const void *user; + const struct passwd *pwd; + uid_t uid; ++ time_t lltime = 0; + + /* + * this module gets the uid of the PAM_USER. Uses it to display +@@ -407,7 +537,11 @@ + + /* process the current login attempt (indicate last) */ + +- retval = last_login_date(pamh, ctrl, uid, user); ++ retval = last_login_date(pamh, ctrl, uid, user, &lltime); ++ ++ if ((ctrl & LASTLOG_BTMP) && retval == PAM_SUCCESS) { ++ retval = last_login_failed(pamh, ctrl, user, lltime); ++ } + + /* indicate success or failure */ + diff --git a/pam_pwhistory-0.1.diff b/pam_pwhistory-0.1.diff new file mode 100644 index 0000000..692696b --- /dev/null +++ b/pam_pwhistory-0.1.diff @@ -0,0 +1,1725 @@ +2008-10-10 Thorsten Kukuk + + * configure.in: add modules/pam_pwhistory/Makefile. + * doc/sag/Linux-PAM_SAG.xml: Include pam_pwhistory.xml. + * doc/sag/pam_pwhistory.xml: New. + * libpam/pam_static_modules.h: Add pam_pwhistory data. + * modules/Makefile.am: Add pam_pwhistory directory. + * modules/pam_pwhistory/Makefile.am: New. + * modules/pam_pwhistory/README.xml: New. + * modules/pam_pwhistory/opasswd.c: New. + * modules/pam_pwhistory/opasswd.h: New. + * modules/pam_pwhistory/pam_pwhistory.8.xml: New. + * modules/pam_pwhistory/pam_pwhistory.c: New. + * modules/pam_pwhistory/tst-pam_pwhistory: New. + * xtests/Makefile.am: New. + * xtests/run-xtests.sh: New. + * xtests/tst-pam_pwhistory1.c: New. + * xtests/tst-pam_pwhistory1.pamd: New. + * xtests/tst-pam_pwhistory1.sh: New. + * po/POTFILES.in: Add modules/pam_pwhistory/. + +--- configure.in 18 Aug 2008 13:29:21 -0000 1.126 ++++ configure.in 10 Oct 2008 06:52:40 -0000 +@@ -542,7 +542,7 @@ + modules/pam_mkhomedir/Makefile modules/pam_motd/Makefile \ + modules/pam_namespace/Makefile \ + modules/pam_nologin/Makefile modules/pam_permit/Makefile \ +- modules/pam_rhosts/Makefile \ ++ modules/pam_pwhistory/Makefile modules/pam_rhosts/Makefile \ + modules/pam_rootok/Makefile modules/pam_exec/Makefile \ + modules/pam_securetty/Makefile modules/pam_selinux/Makefile \ + modules/pam_sepermit/Makefile \ +--- doc/sag/Linux-PAM_SAG.xml 4 Apr 2008 10:23:00 -0000 1.8 ++++ doc/sag/Linux-PAM_SAG.xml 10 Oct 2008 06:52:40 -0000 +@@ -443,6 +443,8 @@ + + ++ + +Index: doc/sag/pam_pwhistory.xml +=================================================================== +RCS file: doc/sag/pam_pwhistory.xml +diff -N doc/sag/pam_pwhistory.xml +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ doc/sag/pam_pwhistory.xml 10 Oct 2008 06:52:40 -0000 +@@ -0,0 +1,38 @@ ++ ++ ++
++ pam_pwhistory - grant access using .pwhistory file ++ ++ ++ ++
++ ++
++
++ ++
++
++ ++
++
++ ++
++
++ ++
++
++ ++
++
++ ++
++
+Index: libpam/pam_static_modules.h +=================================================================== +RCS file: /cvsroot/pam/Linux-PAM/libpam/pam_static_modules.h,v +retrieving revision 1.8 +diff -u -r1.8 pam_static_modules.h +--- libpam/pam_static_modules.h 4 Feb 2008 13:37:35 -0000 1.8 ++++ libpam/pam_static_modules.h 10 Oct 2008 06:52:40 -0000 +@@ -61,6 +61,7 @@ + #endif + extern struct pam_module _pam_nologin_modstruct; + extern struct pam_module _pam_permit_modstruct; ++extern struct pam_module _pam_pwhistory_modstruct; + extern struct pam_module _pam_rhosts_modstruct; + extern struct pam_module _pam_rhosts_auth_modstruct; + extern struct pam_module _pam_rootok_modstruct; +@@ -119,6 +120,7 @@ + #endif + &_pam_nologin_modstruct, + &_pam_permit_modstruct, ++ &_pam_pwhistory_modstruct, + &_pam_rhosts_modstruct, + &_pam_rhosts_auth_modstruct, + &_pam_rootok_modstruct, +Index: modules/Makefile.am +=================================================================== +RCS file: /cvsroot/pam/Linux-PAM/modules/Makefile.am,v +retrieving revision 1.14 +diff -u -r1.14 Makefile.am +--- modules/Makefile.am 4 Feb 2008 14:00:20 -0000 1.14 ++++ modules/Makefile.am 10 Oct 2008 06:52:40 -0000 +@@ -1,15 +1,16 @@ + # +-# Copyright (c) 2005, 2006 Thorsten Kukuk ++# Copyright (c) 2005, 2006, 2008 Thorsten Kukuk + # + + SUBDIRS = pam_access pam_cracklib pam_debug pam_deny pam_echo \ +- pam_env pam_filter pam_ftp pam_group pam_issue pam_keyinit \ +- pam_lastlog pam_limits pam_listfile pam_localuser pam_mail \ +- pam_mkhomedir pam_motd pam_nologin pam_permit pam_rhosts pam_rootok \ +- pam_securetty pam_selinux pam_sepermit pam_shells pam_stress \ ++ pam_env pam_exec pam_faildelay pam_filter pam_ftp \ ++ pam_group pam_issue pam_keyinit pam_lastlog pam_limits \ ++ pam_listfile pam_localuser pam_loginuid pam_mail \ ++ pam_mkhomedir pam_motd pam_namespace pam_nologin \ ++ pam_permit pam_pwhistory pam_rhosts pam_rootok pam_securetty \ ++ pam_selinux pam_sepermit pam_shells pam_stress \ + pam_succeed_if pam_tally pam_time pam_tty_audit pam_umask \ +- pam_unix pam_userdb pam_warn pam_wheel pam_xauth pam_exec \ +- pam_namespace pam_loginuid pam_faildelay ++ pam_unix pam_userdb pam_warn pam_wheel pam_xauth + + CLEANFILES = *~ + +Index: modules/pam_pwhistory/.cvsignore +=================================================================== +RCS file: modules/pam_pwhistory/.cvsignore +diff -N modules/pam_pwhistory/.cvsignore +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ modules/pam_pwhistory/.cvsignore 10 Oct 2008 06:52:40 -0000 +@@ -0,0 +1,8 @@ ++*.la ++*.lo ++.deps ++.libs ++Makefile ++Makefile.in ++README ++pam_pwhistory.8 +Index: modules/pam_pwhistory/Makefile.am +=================================================================== +RCS file: modules/pam_pwhistory/Makefile.am +diff -N modules/pam_pwhistory/Makefile.am +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ modules/pam_pwhistory/Makefile.am 10 Oct 2008 06:52:40 -0000 +@@ -0,0 +1,35 @@ ++# ++# Copyright (c) 2008 Thorsten Kukuk ++# ++ ++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 ++ +Index: modules/pam_pwhistory/README.xml +=================================================================== +RCS file: modules/pam_pwhistory/README.xml +diff -N modules/pam_pwhistory/README.xml +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ modules/pam_pwhistory/README.xml 10 Oct 2008 06:52:40 -0000 +@@ -0,0 +1,41 @@ ++ ++ ++--> ++]> ++ ++
++ ++ ++ ++ ++ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" ++ href="pam_pwhistory.8.xml" xpointer='xpointer(//refnamediv[@id = "pam_pwhistory-name"]/*)'/> ++ ++ ++ ++ ++
++ ++
++ ++
++ ++
++ ++
++ ++
++ ++
++ ++
++ ++
+Index: modules/pam_pwhistory/opasswd.c +=================================================================== +RCS file: modules/pam_pwhistory/opasswd.c +diff -N modules/pam_pwhistory/opasswd.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ modules/pam_pwhistory/opasswd.c 10 Oct 2008 06:52:40 -0000 +@@ -0,0 +1,473 @@ ++/* ++ * Copyright (c) 2008 Thorsten Kukuk ++ * ++ * 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 ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if defined (HAVE_XCRYPT_H) ++#include ++#elif defined (HAVE_CRYPT_H) ++#include ++#endif ++ ++#include ++#include ++ ++#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); ++ 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); ++ 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:%si%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 (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; ++} +Index: modules/pam_pwhistory/opasswd.h +=================================================================== +RCS file: modules/pam_pwhistory/opasswd.h +diff -N modules/pam_pwhistory/opasswd.h +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ modules/pam_pwhistory/opasswd.h 10 Oct 2008 06:52:40 -0000 +@@ -0,0 +1,45 @@ ++/* ++ * Copyright (c) 2008 Thorsten Kukuk ++ * ++ * 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__ */ +Index: modules/pam_pwhistory/pam_pwhistory.8.xml +=================================================================== +RCS file: modules/pam_pwhistory/pam_pwhistory.8.xml +diff -N modules/pam_pwhistory/pam_pwhistory.8.xml +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ modules/pam_pwhistory/pam_pwhistory.8.xml 10 Oct 2008 06:52:40 -0000 +@@ -0,0 +1,226 @@ ++ ++ ++ ++ ++ ++ ++ pam_pwhistory ++ 8 ++ Linux-PAM Manual ++ ++ ++ ++ pam_pwhistory ++ PAM module to remember last passwords ++ ++ ++ ++ ++ pam_pwhistory.so ++ ++ debug ++ ++ ++ use_authtok ++ ++ ++ enforce_for_root ++ ++ ++ remember=N ++ ++ ++ retry=N ++ ++ ++ ++ ++ ++ ++ ++ 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 togehter with kerberos. In general, ++ it does not make much sense to use this module in conjuction ++ 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 ++ ++ ++ ++ ++ ++ ++ ++ Turns on debugging via ++ ++ syslog3 ++ . ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ When password changing enforce the module to use the new password ++ provided by a previously stacked ++ module (this is used in the example of the stacking of the ++ pam_cracklib module documented below). ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ If this option is set, the check is enforced for root, too. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ The last N passwords for each ++ user are saved in /etc/security/opasswd. ++ The default is 10. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Prompt user at most N times ++ before returning with error. The default is ++ 1. ++ ++ ++ ++ ++ ++ ++ ++ ++ MODULE TYPES PROVIDED ++ ++ Only the module type is provided. ++ ++ ++ ++ ++ RETURN VALUES ++ ++ ++ PAM_AUTHTOK_ERR ++ ++ ++ No new password was entered, the user aborted password ++ change or new password couldn't be set. ++ ++ ++ ++ ++ PAM_IGNORE ++ ++ ++ Password history was disabled. ++ ++ ++ ++ ++ PAM_MAXTRIES ++ ++ ++ Password was rejected too often. ++ ++ ++ ++ ++ PAM_USER_UNKNOWN ++ ++ ++ User is not known to system. ++ ++ ++ ++ ++ ++ ++ ++ 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 ++ ++ ++ ++ ++ ++ FILES ++ ++ ++ /etc/security/opasswd ++ ++ File with password history ++ ++ ++ ++ ++ ++ ++ SEE ALSO ++ ++ ++ pam.conf5 ++ , ++ ++ pam.d5 ++ , ++ ++ pam8 ++ ++ ++ ++ ++ ++ AUTHOR ++ ++ pam_pwhistory was written by Thorsten Kukuk <kukuk@thkukuk.de> ++ ++ ++ ++ +Index: modules/pam_pwhistory/pam_pwhistory.c +=================================================================== +RCS file: modules/pam_pwhistory/pam_pwhistory.c +diff -N modules/pam_pwhistory/pam_pwhistory.c +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ modules/pam_pwhistory/pam_pwhistory.c 10 Oct 2008 06:52:40 -0000 +@@ -0,0 +1,319 @@ ++/* ++ * Copyright (c) 2008 Thorsten Kukuk ++ * Author: Thorsten Kukuk ++ * ++ * 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 ++#endif ++ ++#define PAM_SM_PASSWORD ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "opasswd.h" ++ ++#define NEW_PASSWORD_PROMPT _("New %s%spassword: ") ++#define AGAIN_PASSWORD_PROMPT _("Retype new %s%spassword: ") ++#define MISTYPED_PASSWORD _("Sorry, passwords do not match.") ++ ++#define DEFAULT_BUFLEN 2048 ++ ++struct options_t { ++ int debug; ++ int use_authtok; ++ 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, "use_first_pass") == 0) ++ /* ignore */; ++ else if (strcasecmp (argv, "use_first_pass") == 0) ++ /* ignore */; ++ else if (strcasecmp (argv, "use_authtok") == 0) ++ options->use_authtok = 1; ++ 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 ++ 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; ++ char *newpass; ++ const char *user; ++ void *newpass_void; ++ 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; ++ } ++ ++ retval = pam_get_item (pamh, PAM_AUTHTOK, (const void **) &newpass_void); ++ newpass = (char *) newpass_void; ++ if (retval != PAM_SUCCESS) ++ return retval; ++ if (options.debug) ++ { ++ if (newpass) ++ pam_syslog (pamh, LOG_DEBUG, "got new auth token"); ++ else ++ pam_syslog (pamh, LOG_DEBUG, "new auth token not set"); ++ } ++ ++ /* If we haven't been given a password yet, prompt for one... */ ++ if (newpass == NULL) ++ { ++ if (options.use_authtok) ++ /* We are not allowed to ask for a new password */ ++ return PAM_AUTHTOK_ERR; ++ ++ tries = 0; ++ ++ while ((newpass == NULL) && (tries++ < options.tries)) ++ { ++ retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &newpass, ++ NEW_PASSWORD_PROMPT, "UNIX", " "); ++ if (retval != PAM_SUCCESS) ++ { ++ _pam_drop (newpass); ++ if (retval == PAM_CONV_AGAIN) ++ retval = PAM_INCOMPLETE; ++ return retval; ++ } ++ ++ if (newpass == NULL) ++ { ++ /* We want to abort the password change */ ++ pam_error (pamh, _("Password change aborted.")); ++ return PAM_AUTHTOK_ERR; ++ } ++ ++ 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.")); ++ _pam_overwrite (newpass); ++ _pam_drop (newpass); ++ if (tries >= options.tries) ++ { ++ if (options.debug) ++ pam_syslog (pamh, LOG_DEBUG, ++ "Aborted, too many tries"); ++ return PAM_MAXTRIES; ++ } ++ } ++ else ++ { ++ int failed; ++ char *new2; ++ ++ retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &new2, ++ AGAIN_PASSWORD_PROMPT, "UNIX", " "); ++ if (retval != PAM_SUCCESS) ++ return retval; ++ ++ if (new2 == NULL) ++ { /* Aborting password change... */ ++ pam_error (pamh, _("Password change aborted.")); ++ return PAM_AUTHTOK_ERR; ++ } ++ ++ failed = (strcmp (newpass, new2) != 0); ++ ++ _pam_overwrite (new2); ++ _pam_drop (new2); ++ ++ if (failed) ++ { ++ pam_error (pamh, MISTYPED_PASSWORD); ++ _pam_overwrite (newpass); ++ _pam_drop (newpass); ++ if (tries >= options.tries) ++ { ++ if (options.debug) ++ pam_syslog (pamh, LOG_DEBUG, ++ "Aborted, too many tries"); ++ return PAM_MAXTRIES; ++ } ++ } ++ } ++ } ++ ++ /* Remember new password */ ++ pam_set_item (pamh, PAM_AUTHTOK, (void *) newpass); ++ } ++ else /* newpass != NULL, we found an old password */ ++ { ++ if (options.debug) ++ pam_syslog (pamh, LOG_DEBUG, "look in old password file"); ++ ++ if (check_old_password (pamh, user, newpass, ++ options.debug) != PAM_SUCCESS) ++ { ++ pam_error (pamh, ++ _("Password has been already used. Choose another.")); ++ /* We are only here, because old password was set. ++ So overwrite it, else it will be stored! */ ++ pam_set_item (pamh, PAM_AUTHTOK, (void *) NULL); ++ ++ return PAM_AUTHTOK_ERR; ++ } ++ } ++ ++ return PAM_SUCCESS; ++} ++ ++ ++#ifdef PAM_STATIC ++/* static module data */ ++struct pam_module _pam_pwhistory_modstruct = { ++ "pam_pwhistory", ++ NULL, ++ NULL, ++ NULL, ++ NULL, ++ NULL, ++ pam_sm_chauthtok ++}; ++#endif +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ modules/pam_pwhistory/tst-pam_pwhistory 10 Oct 2008 06:52:40 -0000 +@@ -0,0 +1,2 @@ ++#!/bin/sh ++../../tests/tst-dlopen .libs/pam_pwhistory.so +--- po/POTFILES.in 13 Feb 2008 14:39:41 -0000 1.15 ++++ po/POTFILES.in 10 Oct 2008 06:52:40 -0000 +@@ -58,6 +58,8 @@ + ./modules/pam_namespace/pam_namespace.c + ./modules/pam_nologin/pam_nologin.c + ./modules/pam_permit/pam_permit.c ++./modules/pam_pwhistory/opasswd.c ++./modules/pam_pwhistory/pam_pwhistory.c + ./modules/pam_rhosts/pam_rhosts.c + ./modules/pam_rootok/pam_rootok.c + ./modules/pam_securetty/pam_securetty.c +--- xtests/Makefile.am 18 Feb 2008 17:57:34 -0000 1.18 ++++ xtests/Makefile.am 10 Oct 2008 06:52:42 -0000 +@@ -28,7 +28,8 @@ + tst-pam_substack3.pamd tst-pam_substack3a.pamd tst-pam_substack3.sh \ + tst-pam_substack4.pamd tst-pam_substack4a.pamd tst-pam_substack4.sh \ + tst-pam_substack5.pamd tst-pam_substack5a.pamd tst-pam_substack5.sh \ +- tst-pam_assemble_line1.pamd tst-pam_assemble_line1.sh ++ tst-pam_assemble_line1.pamd tst-pam_assemble_line1.sh \ ++ tst-pam_pwhistory1.pamd tst-pam_pwhistory1.sh + + XTESTS = tst-pam_dispatch1 tst-pam_dispatch2 tst-pam_dispatch3 \ + tst-pam_dispatch4 tst-pam_dispatch5 \ +@@ -36,7 +37,8 @@ + tst-pam_unix1 tst-pam_unix2 tst-pam_unix3 \ + tst-pam_access1 tst-pam_access2 tst-pam_access3 \ + tst-pam_access4 tst-pam_limits1 tst-pam_succeed_if1 \ +- tst-pam_group1 tst-pam_authfail tst-pam_authsucceed ++ tst-pam_group1 tst-pam_authfail tst-pam_authsucceed \ ++ tst-pam_pwhistory1 + + NOSRCTESTS = tst-pam_substack1 tst-pam_substack2 tst-pam_substack3 \ + tst-pam_substack4 tst-pam_substack5 tst-pam_assemble_line1 +--- xtests/run-xtests.sh 19 Oct 2007 17:06:29 -0000 1.8 ++++ xtests/run-xtests.sh 10 Oct 2008 06:52:42 -0000 +@@ -23,6 +23,8 @@ + install -m 644 "${SRCDIR}"/group.conf /etc/security/group.conf + cp /etc/security/limits.conf /etc/security/limits.conf-pam-xtests + install -m 644 "${SRCDIR}"/limits.conf /etc/security/limits.conf ++mv /etc/security/opasswd /etc/security/opasswd-pam-xtests ++ + for testname in $XTESTS ; do + for cfg in "${SRCDIR}"/$testname*.pamd ; do + install -m 644 $cfg /etc/pam.d/$(basename $cfg .pamd) +@@ -49,6 +51,7 @@ + mv /etc/security/access.conf-pam-xtests /etc/security/access.conf + mv /etc/security/group.conf-pam-xtests /etc/security/group.conf + mv /etc/security/limits.conf-pam-xtests /etc/security/limits.conf ++mv /etc/security/opasswd-pam-xtests /etc/security/opasswd + if test "$failed" -ne 0; then + echo "===================" + echo "$failed of $all tests failed" +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ xtests/tst-pam_pwhistory1.c 10 Oct 2008 06:52:42 -0000 +@@ -0,0 +1,169 @@ ++/* ++ * 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. ++ */ ++ ++/* ++ * Check remember handling ++ * Change ten times the password ++ * Try the ten passwords again, should always be rejected ++ * Try a new password, should succeed ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include ++#include ++#include ++#include ++ ++static int in_test; ++ ++static const char *passwords[] = { ++ "pamhistory01", "pamhistory02", "pamhistory03", ++ "pamhistory04", "pamhistory05", "pamhistory06", ++ "pamhistory07", "pamhistory08", "pamhistory09", ++ "pamhistory10", ++ "pamhistory01", "pamhistory02", "pamhistory03", ++ "pamhistory04", "pamhistory05", "pamhistory06", ++ "pamhistory07", "pamhistory08", "pamhistory09", ++ "pamhistory10", ++ "pamhistory11", ++ "pamhistory01", "pamhistory02", "pamhistory03", ++ "pamhistory04", "pamhistory05", "pamhistory06", ++ "pamhistory07", "pamhistory08", "pamhistory09", ++ "pamhistory10"}; ++ ++static int debug; ++ ++/* A conversation function which uses an internally-stored value for ++ the responses. */ ++static int ++fake_conv (int num_msg, const struct pam_message **msgm, ++ struct pam_response **response, void *appdata_ptr UNUSED) ++{ ++ struct pam_response *reply; ++ int count; ++ ++ /* Sanity test. */ ++ if (num_msg <= 0) ++ return PAM_CONV_ERR; ++ ++ if (debug) ++ fprintf (stderr, "msg_style=%d, msg=%s\n", msgm[0]->msg_style, ++ msgm[0]->msg); ++ ++ if (msgm[0]->msg_style != 1) ++ return PAM_SUCCESS; ++ ++ /* Allocate memory for the responses. */ ++ reply = calloc (num_msg, sizeof (struct pam_response)); ++ if (reply == NULL) ++ return PAM_CONV_ERR; ++ ++ /* Each prompt elicits the same response. */ ++ for (count = 0; count < num_msg; ++count) ++ { ++ reply[count].resp_retcode = 0; ++ reply[count].resp = strdup (passwords[in_test]); ++ if (debug) ++ fprintf (stderr, "send password %s\n", reply[count].resp); ++ } ++ ++ /* Set the pointers in the response structure and return. */ ++ *response = reply; ++ return PAM_SUCCESS; ++} ++ ++static struct pam_conv conv = { ++ fake_conv, ++ NULL ++}; ++ ++ ++int ++main(int argc, char *argv[]) ++{ ++ pam_handle_t *pamh=NULL; ++ const char *user="tstpampwhistory"; ++ int retval; ++ ++ if (argc > 1 && strcmp (argv[1], "-d") == 0) ++ debug = 1; ++ ++ for (in_test = 0; ++ in_test < (int)(sizeof (passwords)/sizeof (char *)); in_test++) ++ { ++ ++ retval = pam_start("tst-pam_pwhistory1", user, &conv, &pamh); ++ if (retval != PAM_SUCCESS) ++ { ++ if (debug) ++ fprintf (stderr, "pwhistory1-%d: pam_start returned %d\n", ++ in_test, retval); ++ return 1; ++ } ++ ++ retval = pam_chauthtok (pamh, 0); ++ if (in_test < 10 || in_test == 20) ++ { ++ if (retval != PAM_SUCCESS) ++ { ++ if (debug) ++ fprintf (stderr, "pwhistory1-%d: pam_chauthtok returned %d\n", ++ in_test, retval); ++ return 1; ++ } ++ } ++ else if (in_test < 20) ++ { ++ if (retval != PAM_MAXTRIES) ++ { ++ if (debug) ++ fprintf (stderr, "pwhistory1-%d: pam_chauthtok returned %d\n", ++ in_test, retval); ++ return 1; ++ } ++ } ++ ++ retval = pam_end (pamh,retval); ++ if (retval != PAM_SUCCESS) ++ { ++ if (debug) ++ fprintf (stderr, "pwhistory1: pam_end returned %d\n", retval); ++ return 1; ++ } ++ } ++ ++ return 0; ++} +Index: xtests/tst-pam_pwhistory1.pamd +=================================================================== +RCS file: xtests/tst-pam_pwhistory1.pamd +diff -N xtests/tst-pam_pwhistory1.pamd +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ xtests/tst-pam_pwhistory1.pamd 10 Oct 2008 06:52:42 -0000 +@@ -0,0 +1,7 @@ ++#%PAM-1.0 ++auth required pam_permit.so ++account required pam_permit.so ++password required pam_pwhistory.so remember=10 retry=1 debug ++password required pam_unix.so use_authtok md5 ++session required pam_permit.so ++ +Index: xtests/tst-pam_pwhistory1.sh +=================================================================== +RCS file: xtests/tst-pam_pwhistory1.sh +diff -N xtests/tst-pam_pwhistory1.sh +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ xtests/tst-pam_pwhistory1.sh 10 Oct 2008 06:52:42 -0000 +@@ -0,0 +1,7 @@ ++#!/bin/bash ++ ++/usr/sbin/useradd tstpampwhistory ++./tst-pam_pwhistory1 ++RET=$? ++/usr/sbin/userdel -r tstpampwhistory 2> /dev/null ++exit $RET +--- /dev/null 2008-06-06 22:36:48.000000000 +0200 ++++ modules/pam_pwhistory/pam_pwhistory.8 2008-10-11 16:23:45.000000000 +0200 +@@ -0,0 +1,127 @@ ++.\" Title: pam_pwhistory ++.\" Author: ++.\" Generator: DocBook XSL Stylesheets v1.73.2 ++.\" Date: 10/11/2008 ++.\" Manual: Linux-PAM Manual ++.\" Source: Linux-PAM Manual ++.\" ++.TH "PAM_PWHISTORY" "8" "10/11/2008" "Linux-PAM Manual" "Linux\-PAM Manual" ++.\" disable hyphenation ++.nh ++.\" disable justification (adjust text to left margin only) ++.ad l ++.SH "NAME" ++pam_pwhistory - PAM module to remember last passwords ++.SH "SYNOPSIS" ++.HP 17 ++\fBpam_pwhistory\.so\fR [debug] [use_authtok] [enforce_for_root] [remember=\fIN\fR] [retry=\fIN\fR] ++.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 togehter with kerberos\. In general, it does not make much sense to use this module in conjuction 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 ++\fI/etc/security/opasswd\fR\. 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 ++.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 ++.RS 4 ++.nf ++#%PAM\-1\.0 ++password required pam_pwhistory\.so ++password required pam_unix\.so use_authtok ++ ++.fi ++.RE ++.PP ++In combination with ++\fBpam_cracklib\fR: ++.sp ++.RS 4 ++.nf ++#%PAM\-1\.0 ++password required pam_cracklib\.so retry=3 ++password required pam_pwhistory\.so use_authtok ++password required pam_unix\.so use_authtok ++ ++.fi ++.RE ++.sp ++.SH "FILES" ++.PP ++\fI/etc/security/opasswd\fR ++.RS 4 ++File with password history ++.RE ++.SH "SEE ALSO" ++.PP ++ ++\fBpam.conf\fR(5), ++\fBpam.d\fR(5), ++\fBpam\fR(8) ++.SH "AUTHOR" ++.PP ++pam_pwhistory was written by Thorsten Kukuk