diff --git a/Linux-PAM-0.99.3.0-pbuild-rh.patch b/Linux-PAM-0.99.3.0-pbuild-rh.patch
deleted file mode 100644
index 123084e..0000000
--- a/Linux-PAM-0.99.3.0-pbuild-rh.patch
+++ /dev/null
@@ -1,11 +0,0 @@
---- Linux-PAM-0.99.3.0/modules/pam_console/Makefile.am.pbuild-rh 2005-12-15 23:07:37.000000000 +0100
-+++ Linux-PAM-0.99.3.0/modules/pam_console/Makefile.am 2006-01-28 00:26:20.000000000 +0100
-@@ -57,6 +57,8 @@
- pam_console_la_CFLAGS = $(AM_CFLAGS)
- pam_console_apply_CFLAGS = $(AM_CFLAGS)
-
-+configfile.tab.h: configfile.tab.c
-+
- configfile.tab.c: configfile.y
- $(YACC) $(BISON_OPTS) -o $@ -p _pc_yy $<
- sh $(srcdir)/sed-static $@
diff --git a/pam-1.1.1-faillock.patch b/pam-1.1.1-faillock.patch
new file mode 100644
index 0000000..5f4bfc6
--- /dev/null
+++ b/pam-1.1.1-faillock.patch
@@ -0,0 +1,1730 @@
+diff -up Linux-PAM-1.1.1/configure.in.faillock Linux-PAM-1.1.1/configure.in
+--- Linux-PAM-1.1.1/configure.in.faillock 2010-10-20 15:46:34.000000000 +0200
++++ Linux-PAM-1.1.1/configure.in 2011-01-25 18:24:43.000000000 +0100
+@@ -539,7 +539,7 @@ AC_CONFIG_FILES([Makefile libpam/Makefil
+ modules/pam_access/Makefile modules/pam_cracklib/Makefile \
+ modules/pam_debug/Makefile modules/pam_deny/Makefile \
+ modules/pam_echo/Makefile modules/pam_env/Makefile \
+- modules/pam_faildelay/Makefile \
++ modules/pam_faildelay/Makefile modules/pam_faillock/Makefile \
+ modules/pam_filter/Makefile modules/pam_filter/upperLOWER/Makefile \
+ modules/pam_ftp/Makefile modules/pam_group/Makefile \
+ modules/pam_issue/Makefile modules/pam_keyinit/Makefile \
+diff -up Linux-PAM-1.1.1/doc/sag/pam_faillock.xml.faillock Linux-PAM-1.1.1/doc/sag/pam_faillock.xml
+--- Linux-PAM-1.1.1/doc/sag/pam_faillock.xml.faillock 2011-01-25 18:24:43.000000000 +0100
++++ Linux-PAM-1.1.1/doc/sag/pam_faillock.xml 2011-01-25 18:24:43.000000000 +0100
+@@ -0,0 +1,38 @@
++
++
++
++ pam_faillock - temporarily locking access based on failed authentication attempts during an interval
++
++
++
++
++
++
++
++
++
++
++
++
++
+diff -up Linux-PAM-1.1.1/modules/Makefile.am.faillock Linux-PAM-1.1.1/modules/Makefile.am
+--- Linux-PAM-1.1.1/modules/Makefile.am.faillock 2010-10-20 15:46:34.000000000 +0200
++++ Linux-PAM-1.1.1/modules/Makefile.am 2011-01-25 18:24:43.000000000 +0100
+@@ -3,7 +3,7 @@
+ #
+
+ SUBDIRS = pam_access pam_cracklib pam_debug pam_deny pam_echo \
+- pam_chroot pam_console pam_postgresok \
++ pam_chroot pam_console pam_postgresok pam_faillock \
+ 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 \
+diff -up Linux-PAM-1.1.1/modules/pam_faillock/faillock.c.faillock Linux-PAM-1.1.1/modules/pam_faillock/faillock.c
+--- Linux-PAM-1.1.1/modules/pam_faillock/faillock.c.faillock 2011-01-25 18:24:43.000000000 +0100
++++ Linux-PAM-1.1.1/modules/pam_faillock/faillock.c 2011-01-25 18:24:56.000000000 +0100
+@@ -0,0 +1,158 @@
++/*
++ * Copyright (c) 2010 Tomas Mraz
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, and the entire permission notice in its entirety,
++ * including the disclaimer of warranties.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. The name of the author may not be used to endorse or promote
++ * products derived from this software without specific prior
++ * written permission.
++ *
++ * ALTERNATIVELY, this product may be distributed under the terms of
++ * the GNU Public License, in which case the provisions of the GPL are
++ * required INSTEAD OF the above restrictions. (This clause is
++ * necessary due to a potential bad interaction between the GPL and
++ * the restrictions contained in a BSD-style copyright.)
++ *
++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
++ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
++ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
++ * OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include "config.h"
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++
++#include "faillock.h"
++
++int
++open_tally (const char *dir, const char *user, uid_t uid, int create)
++{
++ char *path;
++ int flags = O_RDWR;
++ int fd;
++
++ if (strstr(user, "../") != NULL)
++ /* just a defensive programming as the user must be a
++ * valid user on the system anyway
++ */
++ return -1;
++ path = malloc(strlen(dir) + strlen(user) + 2);
++ if (path == NULL)
++ return -1;
++
++ strcpy(path, dir);
++ if (*dir && dir[strlen(dir) - 1] != '/') {
++ strcat(path, "/");
++ }
++ strcat(path, user);
++
++ if (create) {
++ flags |= O_CREAT;
++ }
++
++ fd = open(path, flags, 0600);
++
++ free(path);
++
++ if (fd != -1) {
++ struct stat st;
++
++ while (flock(fd, LOCK_EX) == -1 && errno == EINTR);
++ if (fstat(fd, &st) == 0) {
++ if (st.st_uid != uid) {
++ fchown(fd, uid, -1);
++ }
++ }
++ }
++
++ return fd;
++}
++
++#define CHUNK_SIZE (64 * sizeof(struct tally))
++#define MAX_RECORDS 1024
++
++int
++read_tally(int fd, struct tally_data *tallies)
++{
++ void *data = NULL, *newdata;
++ unsigned int count = 0;
++ ssize_t chunk = 0;
++
++ do {
++ newdata = realloc(data, count * sizeof(struct tally) + CHUNK_SIZE);
++ if (newdata == NULL) {
++ free(data);
++ return -1;
++ }
++
++ data = newdata;
++
++ chunk = pam_modutil_read(fd, (char *)data + count * sizeof(struct tally), CHUNK_SIZE);
++ if (chunk < 0) {
++ free(data);
++ return -1;
++ }
++
++ count += chunk/sizeof(struct tally);
++
++ if (count >= MAX_RECORDS)
++ break;
++ }
++ while (chunk == CHUNK_SIZE);
++
++ tallies->records = data;
++ tallies->count = count;
++
++ return 0;
++}
++
++int
++update_tally(int fd, struct tally_data *tallies)
++{
++ void *data = tallies->records;
++ unsigned int count = tallies->count;
++ ssize_t chunk;
++
++ if (tallies->count > MAX_RECORDS) {
++ data = tallies->records + (count - MAX_RECORDS);
++ count = MAX_RECORDS;
++ }
++
++ if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
++ return -1;
++ }
++
++ chunk = pam_modutil_write(fd, data, count * sizeof(struct tally));
++
++ if (chunk != (ssize_t)(count * sizeof(struct tally))) {
++ return -1;
++ }
++
++ if (ftruncate(fd, count * sizeof(struct tally)) == -1)
++ return -1;
++
++ return 0;
++}
+diff -up Linux-PAM-1.1.1/modules/pam_faillock/faillock.h.faillock Linux-PAM-1.1.1/modules/pam_faillock/faillock.h
+--- Linux-PAM-1.1.1/modules/pam_faillock/faillock.h.faillock 2011-01-25 18:24:43.000000000 +0100
++++ Linux-PAM-1.1.1/modules/pam_faillock/faillock.h 2011-01-25 18:24:56.000000000 +0100
+@@ -0,0 +1,73 @@
++/*
++ * Copyright (c) 2010 Tomas Mraz
++ *
++ * 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.
++ */
++
++/*
++ * faillock.h - authentication failure data file record structure
++ *
++ * Each record in the file represents an instance of login failure of
++ * the user at the recorded time
++ */
++
++
++#ifndef _FAILLOCK_H
++#define _FAILLOCK_H
++
++#include
++#include
++
++#define TALLY_STATUS_VALID 0x1 /* the tally file entry is valid */
++#define TALLY_STATUS_RHOST 0x2 /* the source is rhost */
++#define TALLY_STATUS_TTY 0x4 /* the source is tty - if both TALLY_FLAG_RHOST and TALLY_FLAG_TTY are not set the source is service */
++
++struct tally {
++ char source[52]; /* rhost or tty of the login failure (not necessarily NULL terminated) */
++ uint16_t reserved; /* reserved for future use */
++ uint16_t status; /* record status */
++ uint64_t time; /* time of the login failure */
++};
++/* 64 bytes per entry */
++
++struct tally_data {
++ struct tally *records; /* array of tallies */
++ unsigned int count; /* number of records */
++};
++
++#define FAILLOCK_DEFAULT_TALLYDIR "/var/run/faillock"
++
++int open_tally(const char *dir, const char *user, uid_t uid, int create);
++int read_tally(int fd, struct tally_data *tallies);
++int update_tally(int fd, struct tally_data *tallies);
++#endif
++
+diff -up Linux-PAM-1.1.1/modules/pam_faillock/faillock.8.xml.faillock Linux-PAM-1.1.1/modules/pam_faillock/faillock.8.xml
+--- Linux-PAM-1.1.1/modules/pam_faillock/faillock.8.xml.faillock 2011-01-25 18:24:43.000000000 +0100
++++ Linux-PAM-1.1.1/modules/pam_faillock/faillock.8.xml 2011-01-25 18:24:43.000000000 +0100
+@@ -0,0 +1,123 @@
++
++
++
++
++
++
++ faillock
++ 8
++ Linux-PAM Manual
++
++
++
++ faillock
++ Tool for displaying and modifying the authentication failure record files
++
++
++
++
++ faillock
++
++ --dir /path/to/tally-directory
++
++
++ --user username
++
++
++ --reset
++
++
++
++
++
++
++ DESCRIPTION
++
++
++ The pam_faillock.so module maintains a list of
++ failed authentication attempts per user during a specified interval
++ and locks the account in case there were more than
++ deny consecutive failed authentications.
++ It stores the failure records into per-user files in the tally
++ directory.
++
++
++ The faillock command is an application which
++ can be used to examine and modify the contents of the
++ the tally files. It can display the recent failed authentication
++ attempts of the username or clear the tally
++ files of all or individual usernames.
++
++
++
++
++
++ OPTIONS
++
++
++
++
++
++
++
++ The directory where the user files with the failure records are kept. The
++ default is /var/run/faillock.
++
++
++
++
++
++
++
++
++
++ The user whose failure records should be displayed or cleared.
++
++
++
++
++
++
++
++
++
++ Instead of displaying the user's failure records, clear them.
++
++
++
++
++
++
++
++ FILES
++
++
++ /var/run/faillock/*
++
++ the files logging the authentication failures for users
++
++
++
++
++
++
++ SEE ALSO
++
++
++ pam_faillock8
++ ,
++
++ pam8
++
++
++
++
++
++ AUTHOR
++
++ faillock was written by Tomas Mraz.
++
++
++
++
+diff -up Linux-PAM-1.1.1/modules/pam_faillock/main.c.faillock Linux-PAM-1.1.1/modules/pam_faillock/main.c
+--- Linux-PAM-1.1.1/modules/pam_faillock/main.c.faillock 2011-01-25 18:24:43.000000000 +0100
++++ Linux-PAM-1.1.1/modules/pam_faillock/main.c 2011-01-25 18:24:56.000000000 +0100
+@@ -0,0 +1,235 @@
++/*
++ * Copyright (c) 2010 Tomas Mraz
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, and the entire permission notice in its entirety,
++ * including the disclaimer of warranties.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. The name of the author may not be used to endorse or promote
++ * products derived from this software without specific prior
++ * written permission.
++ *
++ * ALTERNATIVELY, this product may be distributed under the terms of
++ * the GNU Public License, in which case the provisions of the GPL are
++ * required INSTEAD OF the above restrictions. (This clause is
++ * necessary due to a potential bad interaction between the GPL and
++ * the restrictions contained in a BSD-style copyright.)
++ *
++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
++ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
++ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
++ * OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include "config.h"
++
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#ifdef HAVE_LIBAUDIT
++#include
++#endif
++
++#include "faillock.h"
++
++struct options {
++ unsigned int reset;
++ const char *dir;
++ const char *user;
++ const char *progname;
++};
++
++static int
++args_parse(int argc, char **argv, struct options *opts)
++{
++ int i;
++ memset(opts, 0, sizeof(*opts));
++
++ opts->dir = FAILLOCK_DEFAULT_TALLYDIR;
++ opts->progname = argv[0];
++
++ for (i = 1; i < argc; ++i) {
++
++ if (strcmp(argv[i], "--dir") == 0) {
++ ++i;
++ if (i >= argc || strlen(argv[i]) == 0) {
++ fprintf(stderr, "%s: No directory supplied.\n", argv[0]);
++ return -1;
++ }
++ opts->dir = argv[i];
++ }
++ else if (strcmp(argv[i], "--user") == 0) {
++ ++i;
++ if (i >= argc || strlen(argv[i]) == 0) {
++ fprintf(stderr, "%s: No user name supplied.\n", argv[0]);
++ return -1;
++ }
++ opts->user = argv[i];
++ }
++ else if (strcmp(argv[i], "--reset") == 0) {
++ opts->reset = 1;
++ }
++ else {
++ fprintf(stderr, "%s: Unknown option: %s\n", argv[0], argv[i]);
++ return -1;
++ }
++ }
++ return 0;
++}
++
++static void
++usage(const char *progname)
++{
++ fprintf(stderr, _("Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset]\n"),
++ progname);
++}
++
++static int
++do_user(struct options *opts, const char *user)
++{
++ int fd;
++ int rv;
++ struct tally_data tallies;
++ struct passwd *pwd;
++
++ pwd = getpwnam(user);
++
++ fd = open_tally(opts->dir, user, pwd != NULL ? pwd->pw_uid : 0, 0);
++
++ if (fd == -1) {
++ if (errno == ENOENT) {
++ return 0;
++ }
++ else {
++ fprintf(stderr, "%s: Error opening the tally file for %s:",
++ opts->progname, user);
++ perror(NULL);
++ return 3;
++ }
++ }
++ if (opts->reset) {
++#ifdef HAVE_LIBAUDIT
++ char buf[64];
++ int audit_fd;
++#endif
++
++ while ((rv=ftruncate(fd, 0)) == -1 && errno == EINTR);
++ if (rv == -1) {
++ fprintf(stderr, "%s: Error clearing the tally file for %s:",
++ opts->progname, user);
++ perror(NULL);
++#ifdef HAVE_LIBAUDIT
++ }
++ if ((audit_fd=audit_open()) >= 0) {
++
++ if (pwd != NULL) {
++ snprintf(buf, sizeof(buf), "faillock reset uid=%u",
++ pwd->pw_uid);
++ audit_log_user_message(audit_fd, AUDIT_USER_ACCT,
++ buf, NULL, NULL, NULL, rv == 0);
++ }
++ close(audit_fd);
++ }
++ if (rv == -1) {
++#endif
++ close(fd);
++ return 4;
++ }
++ }
++ else {
++ unsigned int i;
++
++ memset(&tallies, 0, sizeof(tallies));
++ if ((rv=read_tally(fd, &tallies)) == -1) {
++ fprintf(stderr, "%s: Error reading the tally file for %s:",
++ opts->progname, user);
++ perror(NULL);
++ close(fd);
++ return 5;
++ }
++
++ printf("%s:\n", user);
++ printf("%-19s %-5s %-48s %-5s\n", "When", "Type", "Source", "Valid");
++
++ for (i = 0; i < tallies.count; i++) {
++ struct tm *tm;
++ char timebuf[80];
++ uint16_t status = tallies.records[i].status;
++ time_t when = tallies.records[i].time;
++
++ tm = localtime(&when);
++ strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", tm);
++ printf("%-19s %-5s %-52.52s %s\n", timebuf,
++ status & TALLY_STATUS_RHOST ? "RHOST" : (status & TALLY_STATUS_TTY ? "TTY" : "SVC"),
++ tallies.records[i].source, status & TALLY_STATUS_VALID ? "V":"I");
++ }
++ free(tallies.records);
++ }
++ close(fd);
++ return 0;
++}
++
++static int
++do_allusers(struct options *opts)
++{
++ struct dirent **userlist;
++ int rv, i;
++
++ rv = scandir(opts->dir, &userlist, NULL, alphasort);
++ if (rv < 0) {
++ fprintf(stderr, "%s: Error reading tally directory: ", opts->progname);
++ perror(NULL);
++ return 2;
++ }
++
++ for (i = 0; i < rv; i++) {
++ if (userlist[i]->d_name[0] == '.') {
++ if ((userlist[i]->d_name[1] == '.' && userlist[i]->d_name[2] == '\0') ||
++ userlist[i]->d_name[1] == '\0')
++ continue;
++ }
++ do_user(opts, userlist[i]->d_name);
++ free(userlist[i]);
++ }
++ free(userlist);
++
++ return 0;
++}
++
++
++/*-----------------------------------------------------------------------*/
++int
++main (int argc, char *argv[])
++{
++ struct options opts;
++
++ if (args_parse(argc, argv, &opts)) {
++ usage(argv[0]);
++ return 1;
++ }
++
++ if (opts.user == NULL) {
++ return do_allusers(&opts);
++ }
++
++ return do_user(&opts, opts.user);
++}
++
+diff -up Linux-PAM-1.1.1/modules/pam_faillock/Makefile.am.faillock Linux-PAM-1.1.1/modules/pam_faillock/Makefile.am
+--- Linux-PAM-1.1.1/modules/pam_faillock/Makefile.am.faillock 2011-01-25 18:24:43.000000000 +0100
++++ Linux-PAM-1.1.1/modules/pam_faillock/Makefile.am 2011-01-25 18:24:43.000000000 +0100
+@@ -0,0 +1,43 @@
++#
++# Copyright (c) 2005, 2006, 2007, 2009 Thorsten Kukuk
++# Copyright (c) 2008 Red Hat, Inc.
++# Copyright (c) 2010 Tomas Mraz
++#
++
++CLEANFILES = *~
++MAINTAINERCLEANFILES = $(MANS) README
++
++EXTRA_DIST = README $(MANS) $(XMLS) tst-pam_faillock
++
++man_MANS = pam_faillock.8 faillock.8
++XMLS = README.xml pam_faillock.8.xml faillock.8.xml
++
++TESTS = tst-pam_faillock
++
++securelibdir = $(SECUREDIR)
++secureconfdir = $(SCONFIGDIR)
++
++noinst_HEADERS = faillock.h
++
++faillock_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include
++pam_faillock_la_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include
++
++pam_faillock_la_LDFLAGS = -no-undefined -avoid-version -module
++pam_faillock_la_LIBADD = -L$(top_builddir)/libpam -lpam $(LIBAUDIT)
++if HAVE_VERSIONING
++ pam_faillock_la_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
++endif
++
++faillock_LDADD = -L$(top_builddir)/libpam -lpam $(LIBAUDIT)
++
++securelib_LTLIBRARIES = pam_faillock.la
++sbin_PROGRAMS = faillock
++
++pam_faillock_la_SOURCES = pam_faillock.c faillock.c
++faillock_SOURCES = main.c faillock.c
++
++if ENABLE_REGENERATE_MAN
++noinst_DATA = README
++README: pam_faillock.8.xml
++-include $(top_srcdir)/Make.xml.rules
++endif
+diff -up Linux-PAM-1.1.1/modules/pam_faillock/pam_faillock.c.faillock Linux-PAM-1.1.1/modules/pam_faillock/pam_faillock.c
+--- Linux-PAM-1.1.1/modules/pam_faillock/pam_faillock.c.faillock 2011-01-25 18:24:43.000000000 +0100
++++ Linux-PAM-1.1.1/modules/pam_faillock/pam_faillock.c 2011-01-25 18:24:56.000000000 +0100
+@@ -0,0 +1,556 @@
++/*
++ * Copyright (c) 2010 Tomas Mraz
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, and the entire permission notice in its entirety,
++ * including the disclaimer of warranties.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. The name of the author may not be used to endorse or promote
++ * products derived from this software without specific prior
++ * written permission.
++ *
++ * ALTERNATIVELY, this product may be distributed under the terms of
++ * the GNU Public License, in which case the provisions of the GPL are
++ * required INSTEAD OF the above restrictions. (This clause is
++ * necessary due to a potential bad interaction between the GPL and
++ * the restrictions contained in a BSD-style copyright.)
++ *
++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
++ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
++ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
++ * OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include "config.h"
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++
++#ifdef HAVE_LIBAUDIT
++#include
++#endif
++
++#include
++#include
++#include
++
++#include "faillock.h"
++
++#define PAM_SM_AUTH
++#define PAM_SM_ACCOUNT
++
++#define FAILLOCK_ACTION_PREAUTH 0
++#define FAILLOCK_ACTION_AUTHSUCC 1
++#define FAILLOCK_ACTION_AUTHFAIL 2
++
++#define FAILLOCK_FLAG_DENY_ROOT 0x1
++#define FAILLOCK_FLAG_AUDIT 0x2
++#define FAILLOCK_FLAG_SILENT 0x4
++#define FAILLOCK_FLAG_NO_LOG_INFO 0x8
++#define FAILLOCK_FLAG_UNLOCKED 0x10
++
++#define MAX_TIME_INTERVAL 604800 /* 7 days */
++
++struct options {
++ unsigned int action;
++ unsigned int flags;
++ unsigned short deny;
++ unsigned int fail_interval;
++ unsigned int unlock_time;
++ unsigned int root_unlock_time;
++ const char *dir;
++ const char *user;
++ int failures;
++ uint64_t latest_time;
++ uid_t uid;
++ uint64_t now;
++};
++
++static void
++args_parse(pam_handle_t *pamh, int argc, const char **argv,
++ int flags, struct options *opts)
++{
++ int i;
++ memset(opts, 0, sizeof(*opts));
++
++ opts->dir = FAILLOCK_DEFAULT_TALLYDIR;
++ opts->deny = 3;
++ opts->fail_interval = 900;
++ opts->unlock_time = 600;
++ opts->root_unlock_time = MAX_TIME_INTERVAL+1;
++
++ for (i = 0; i < argc; ++i) {
++
++ if (strncmp(argv[i], "dir=", 4) == 0) {
++ if (argv[i][4] != '/') {
++ pam_syslog(pamh, LOG_ERR,
++ "Tally directory is not absolute path (%s); keeping default", argv[i]);
++ } else {
++ opts->dir = argv[i]+4;
++ }
++ }
++ else if (strncmp(argv[i], "deny=", 5) == 0) {
++ if (sscanf(argv[i]+5, "%hu", &opts->deny) != 1) {
++ pam_syslog(pamh, LOG_ERR,
++ "Bad number supplied for deny argument");
++ }
++ }
++ else if (strncmp(argv[i], "fail_interval=", 14) == 0) {
++ unsigned int temp;
++ if (sscanf(argv[i]+14, "%u", &temp) != 1 ||
++ temp > MAX_TIME_INTERVAL) {
++ pam_syslog(pamh, LOG_ERR,
++ "Bad number supplied for fail_interval argument");
++ } else {
++ opts->fail_interval = temp;
++ }
++ }
++ else if (strncmp(argv[i], "unlock_time=", 12) == 0) {
++ unsigned int temp;
++ if (sscanf(argv[i]+12, "%u", &temp) != 1 ||
++ temp > MAX_TIME_INTERVAL) {
++ pam_syslog(pamh, LOG_ERR,
++ "Bad number supplied for unlock_time argument");
++ } else {
++ opts->unlock_time = temp;
++ }
++ }
++ else if (strncmp(argv[i], "root_unlock_time=", 17) == 0) {
++ unsigned int temp;
++ if (sscanf(argv[i]+17, "%u", &temp) != 1 ||
++ temp > MAX_TIME_INTERVAL) {
++ pam_syslog(pamh, LOG_ERR,
++ "Bad number supplied for root_unlock_time argument");
++ } else {
++ opts->root_unlock_time = temp;
++ }
++ }
++ else if (strcmp(argv[i], "preauth") == 0) {
++ opts->action = FAILLOCK_ACTION_PREAUTH;
++ }
++ else if (strcmp(argv[i], "authfail") == 0) {
++ opts->action = FAILLOCK_ACTION_AUTHFAIL;
++ }
++ else if (strcmp(argv[i], "authsucc") == 0) {
++ opts->action = FAILLOCK_ACTION_AUTHSUCC;
++ }
++ else if (strcmp(argv[i], "even_deny_root") == 0) {
++ opts->flags |= FAILLOCK_FLAG_DENY_ROOT;
++ }
++ else if (strcmp(argv[i], "audit") == 0) {
++ opts->flags |= FAILLOCK_FLAG_AUDIT;
++ }
++ else if (strcmp(argv[i], "silent") == 0) {
++ opts->flags |= FAILLOCK_FLAG_SILENT;
++ }
++ else if (strcmp(argv[i], "no_log_info") == 0) {
++ opts->flags |= FAILLOCK_FLAG_NO_LOG_INFO;
++ }
++ else {
++ pam_syslog(pamh, LOG_ERR, "Unknown option: %s", argv[i]);
++ }
++ }
++
++ if (opts->root_unlock_time == MAX_TIME_INTERVAL+1)
++ opts->root_unlock_time = opts->unlock_time;
++ if (flags & PAM_SILENT)
++ opts->flags |= FAILLOCK_FLAG_SILENT;
++}
++
++static int get_pam_user(pam_handle_t *pamh, struct options *opts)
++{
++ const char *user;
++ int rv;
++ struct passwd *pwd;
++
++ if ((rv=pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS) {
++ return rv;
++ }
++
++ if (*user == '\0') {
++ return PAM_IGNORE;
++ }
++
++ if ((pwd=pam_modutil_getpwnam(pamh, user)) == NULL) {
++ if (opts->flags & FAILLOCK_FLAG_AUDIT) {
++ pam_syslog(pamh, LOG_ERR, "User unknown: %s", user);
++ }
++ else {
++ pam_syslog(pamh, LOG_ERR, "User unknown");
++ }
++ return PAM_IGNORE;
++ }
++ opts->user = user;
++ opts->uid = pwd->pw_uid;
++ return PAM_SUCCESS;
++}
++
++static int
++check_tally(pam_handle_t *pamh, struct options *opts, struct tally_data *tallies, int *fd)
++{
++ int tfd;
++ unsigned int i;
++ uint64_t latest_time;
++ int failures;
++
++ opts->now = time(NULL);
++
++ tfd = open_tally(opts->dir, opts->user, opts->uid, 0);
++
++ *fd = tfd;
++
++ if (tfd == -1) {
++ if (errno == EACCES || errno == ENOENT) {
++ return PAM_SUCCESS;
++ }
++ pam_syslog(pamh, LOG_ERR, "Error opening the tally file for %s: %m", opts->user);
++ return PAM_SYSTEM_ERR;
++ }
++
++ if (read_tally(tfd, tallies) != 0) {
++ pam_syslog(pamh, LOG_ERR, "Error reading the tally file for %s: %m", opts->user);
++ return PAM_SYSTEM_ERR;
++ }
++
++ if (opts->uid == 0 && !(opts->flags & FAILLOCK_FLAG_DENY_ROOT)) {
++ return PAM_SUCCESS;
++ }
++
++ latest_time = 0;
++ for(i = 0; i < tallies->count; i++) {
++ if ((tallies->records[i].status & TALLY_STATUS_VALID) &&
++ tallies->records[i].time > latest_time)
++ latest_time = tallies->records[i].time;
++ }
++
++ opts->latest_time = latest_time;
++
++ failures = 0;
++ for(i = 0; i < tallies->count; i++) {
++ if ((tallies->records[i].status & TALLY_STATUS_VALID) &&
++ latest_time - tallies->records[i].time < opts->fail_interval) {
++ ++failures;
++ }
++ }
++
++ opts->failures = failures;
++
++ if (opts->uid == 0 && !(opts->flags & FAILLOCK_FLAG_DENY_ROOT)) {
++ return PAM_SUCCESS;
++ }
++
++ if (opts->deny && failures >= opts->deny) {
++ if ((opts->uid && latest_time + opts->unlock_time < opts->now) ||
++ (!opts->uid && latest_time + opts->root_unlock_time < opts->now)) {
++#ifdef HAVE_LIBAUDIT
++ if (opts->action != FAILLOCK_ACTION_PREAUTH) { /* do not audit in preauth */
++ char buf[64];
++ int audit_fd;
++ const void *rhost = NULL, *tty = NULL;
++
++ audit_fd = audit_open();
++ /* If there is an error & audit support is in the kernel report error */
++ if ((audit_fd < 0) && !(errno == EINVAL || errno == EPROTONOSUPPORT ||
++ errno == EAFNOSUPPORT))
++ return PAM_SYSTEM_ERR;
++
++ (void)pam_get_item(pamh, PAM_TTY, &tty);
++ (void)pam_get_item(pamh, PAM_RHOST, &rhost);
++ snprintf(buf, sizeof(buf), "pam_faillock uid=%u ", opts->uid);
++ audit_log_user_message(audit_fd, AUDIT_RESP_ACCT_UNLOCK_TIMED, buf,
++ rhost, NULL, tty, 1);
++ }
++#endif
++ opts->flags |= FAILLOCK_FLAG_UNLOCKED;
++ return PAM_SUCCESS;
++ }
++ return PAM_AUTH_ERR;
++ }
++ return PAM_SUCCESS;
++}
++
++static void
++reset_tally(pam_handle_t *pamh, struct options *opts, int *fd)
++{
++ int rv;
++
++ if (*fd == -1) {
++ *fd = open_tally(opts->dir, opts->user, opts->uid, 1);
++ }
++ else {
++ while ((rv=ftruncate(*fd, 0)) == -1 && errno == EINTR);
++ if (rv == -1) {
++ pam_syslog(pamh, LOG_ERR, "Error clearing the tally file for %s: %m", opts->user);
++ }
++ }
++}
++
++static int
++write_tally(pam_handle_t *pamh, struct options *opts, struct tally_data *tallies, int *fd)
++{
++ struct tally *records;
++ unsigned int i;
++ int failures;
++ unsigned int oldest;
++ uint64_t oldtime;
++ const void *source = NULL;
++
++ if (*fd == -1) {
++ *fd = open_tally(opts->dir, opts->user, opts->uid, 1);
++ }
++ if (*fd == -1) {
++ if (errno == EACCES) {
++ return PAM_SUCCESS;
++ }
++ pam_syslog(pamh, LOG_ERR, "Error opening the tally file for %s: %m", opts->user);
++ return PAM_SYSTEM_ERR;
++ }
++
++ oldtime = 0;
++ oldest = 0;
++ failures = 0;
++
++ for (i = 0; i < tallies->count; ++i) {
++ if (tallies->records[i].time < oldtime) {
++ oldtime = tallies->records[i].time;
++ oldest = i;
++ }
++ if (opts->flags & FAILLOCK_FLAG_UNLOCKED ||
++ opts->now - tallies->records[i].time >= opts->fail_interval ) {
++ tallies->records[i].status &= ~TALLY_STATUS_VALID;
++ } else {
++ ++failures;
++ }
++ }
++
++ if (oldest >= tallies->count || (tallies->records[oldest].status & TALLY_STATUS_VALID)) {
++ oldest = tallies->count;
++
++ if ((records=realloc(tallies->records, (oldest+1) * sizeof (*tallies->records))) == NULL) {
++ pam_syslog(pamh, LOG_CRIT, "Error allocating memory for tally records: %m");
++ return PAM_BUF_ERR;
++ }
++
++ ++tallies->count;
++ tallies->records = records;
++ }
++
++ memset(&tallies->records[oldest], 0, sizeof (*tallies->records));
++
++ tallies->records[oldest].status = TALLY_STATUS_VALID;
++ if (pam_get_item(pamh, PAM_RHOST, &source) != PAM_SUCCESS || source == NULL) {
++ if (pam_get_item(pamh, PAM_TTY, &source) != PAM_SUCCESS || source == NULL) {
++ if (pam_get_item(pamh, PAM_SERVICE, &source) != PAM_SUCCESS || source == NULL) {
++ source = "";
++ }
++ }
++ else {
++ tallies->records[oldest].status |= TALLY_STATUS_TTY;
++ }
++ }
++ else {
++ tallies->records[oldest].status |= TALLY_STATUS_RHOST;
++ }
++
++ strncpy(tallies->records[oldest].source, source, sizeof(tallies->records[oldest].source));
++ /* source does not have to be null terminated */
++
++ tallies->records[oldest].time = opts->now;
++
++ ++failures;
++
++ if (opts->deny && failures == opts->deny) {
++#ifdef HAVE_LIBAUDIT
++ char buf[64];
++ int audit_fd;
++
++ audit_fd = audit_open();
++ /* If there is an error & audit support is in the kernel report error */
++ if ((audit_fd < 0) && !(errno == EINVAL || errno == EPROTONOSUPPORT ||
++ errno == EAFNOSUPPORT))
++ return PAM_SYSTEM_ERR;
++
++ snprintf(buf, sizeof(buf), "pam_faillock uid=%u ", opts->uid);
++ audit_log_user_message(audit_fd, AUDIT_ANOM_LOGIN_FAILURES, buf,
++ NULL, NULL, NULL, 1);
++
++ if (opts->uid != 0 || (opts->flags & FAILLOCK_FLAG_DENY_ROOT)) {
++ audit_log_user_message(audit_fd, AUDIT_RESP_ACCT_LOCK, buf,
++ NULL, NULL, NULL, 1);
++ }
++ close(audit_fd);
++#endif
++ if (!(opts->flags & FAILLOCK_FLAG_NO_LOG_INFO)) {
++ pam_syslog(pamh, LOG_INFO, "Consecutive login failures for user %s account temporarily locked",
++ opts->user);
++ }
++ }
++
++ if (update_tally(*fd, tallies) == 0)
++ return PAM_SUCCESS;
++
++ return PAM_SYSTEM_ERR;
++}
++
++static void
++faillock_message(pam_handle_t *pamh, struct options *opts)
++{
++ int64_t left;
++
++ if (!(opts->flags & FAILLOCK_FLAG_SILENT)) {
++ if (opts->uid) {
++ left = opts->latest_time + opts->unlock_time - opts->now;
++ }
++ else {
++ left = opts->latest_time + opts->root_unlock_time - opts->now;
++ }
++
++ left /= 60; /* minutes */
++
++ pam_info(pamh, _("Account temporarily locked due to %d failed logins"),
++ opts->failures);
++ pam_info(pamh, _("(%d minutes left to unlock)"), (int)left);
++ }
++}
++
++static void
++tally_cleanup(struct tally_data *tallies, int fd)
++{
++ if (fd != -1) {
++ close(fd);
++ }
++
++ free(tallies->records);
++}
++
++/*---------------------------------------------------------------------*/
++
++PAM_EXTERN int
++pam_sm_authenticate(pam_handle_t *pamh, int flags,
++ int argc, const char **argv)
++{
++ struct options opts;
++ int rv, fd = -1;
++ struct tally_data tallies;
++
++ memset(&tallies, 0, sizeof(tallies));
++
++ args_parse(pamh, argc, argv, flags, &opts);
++
++ pam_fail_delay(pamh, 2000000); /* 2 sec delay for on failure */
++
++ if ((rv=get_pam_user(pamh, &opts)) != PAM_SUCCESS) {
++ return rv;
++ }
++
++ switch (opts.action) {
++ case FAILLOCK_ACTION_PREAUTH:
++ rv = check_tally(pamh, &opts, &tallies, &fd);
++ if (rv == PAM_AUTH_ERR && !(opts.flags & FAILLOCK_FLAG_SILENT)) {
++ faillock_message(pamh, &opts);
++ }
++ break;
++
++ case FAILLOCK_ACTION_AUTHSUCC:
++ rv = check_tally(pamh, &opts, &tallies, &fd);
++ if (rv == PAM_SUCCESS) {
++ reset_tally(pamh, &opts, &fd);
++ }
++ break;
++
++ case FAILLOCK_ACTION_AUTHFAIL:
++ rv = check_tally(pamh, &opts, &tallies, &fd);
++ if (rv == PAM_SUCCESS) {
++ rv = PAM_IGNORE; /* this return value should be ignored */
++ write_tally(pamh, &opts, &tallies, &fd);
++ }
++ break;
++ }
++
++ tally_cleanup(&tallies, fd);
++
++ return rv;
++}
++
++/*---------------------------------------------------------------------*/
++
++PAM_EXTERN int
++pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED,
++ int argc UNUSED, const char **argv UNUSED)
++{
++ return PAM_SUCCESS;
++}
++
++/*---------------------------------------------------------------------*/
++
++PAM_EXTERN int
++pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
++ int argc, const char **argv)
++{
++ struct options opts;
++ int rv, fd = -1;
++ struct tally_data tallies;
++
++ memset(&tallies, 0, sizeof(tallies));
++
++ args_parse(pamh, argc, argv, flags, &opts);
++
++ opts.action = FAILLOCK_ACTION_AUTHSUCC;
++
++ if ((rv=get_pam_user(pamh, &opts)) != PAM_SUCCESS) {
++ return rv;
++ }
++
++ check_tally(pamh, &opts, &tallies, &fd); /* for auditing */
++ reset_tally(pamh, &opts, &fd);
++
++ tally_cleanup(&tallies, fd);
++
++ return PAM_SUCCESS;
++}
++
++/*-----------------------------------------------------------------------*/
++
++#ifdef PAM_STATIC
++
++/* static module data */
++
++struct pam_module _pam_faillock_modstruct = {
++ MODULE_NAME,
++#ifdef PAM_SM_AUTH
++ pam_sm_authenticate,
++ pam_sm_setcred,
++#else
++ NULL,
++ NULL,
++#endif
++#ifdef PAM_SM_ACCOUNT
++ pam_sm_acct_mgmt,
++#else
++ NULL,
++#endif
++ NULL,
++ NULL,
++ NULL,
++};
++
++#endif /* #ifdef PAM_STATIC */
++
+diff -up Linux-PAM-1.1.1/modules/pam_faillock/pam_faillock.8.xml.faillock Linux-PAM-1.1.1/modules/pam_faillock/pam_faillock.8.xml
+--- Linux-PAM-1.1.1/modules/pam_faillock/pam_faillock.8.xml.faillock 2011-01-25 18:24:43.000000000 +0100
++++ Linux-PAM-1.1.1/modules/pam_faillock/pam_faillock.8.xml 2011-01-25 18:24:56.000000000 +0100
+@@ -0,0 +1,392 @@
++
++
++
++
++
++
++ pam_faillock
++ 8
++ Linux-PAM Manual
++
++
++
++ pam_faillock
++ Module counting authentication failures during a specified interval
++
++
++
++
++ auth ... pam_faillock.so
++
++ preauth|authfail|authsucc
++
++
++ dir=/path/to/tally-directory
++
++
++ even_deny_root
++
++
++ deny=n
++
++
++ fail_interval=n
++
++
++ unlock_time=n
++
++
++ root_unlock_time=n
++
++
++ audit
++
++
++ silent
++
++
++ no_log_info
++
++
++
++ account ... pam_faillock.so
++
++ dir=/path/to/tally-directory
++
++
++ no_log_info
++
++
++
++
++
++
++ DESCRIPTION
++
++
++ This module maintains a list of failed authentication attempts per
++ user during a specified interval and locks the account in case
++ there were more than deny consecutive
++ failed authentications.
++
++
++ Normally, failed attempts to authenticate root will
++ not cause the root account to become
++ blocked, to prevent denial-of-service: if your users aren't given
++ shell accounts and root may only login via su or
++ at the machine console (not telnet/rsh, etc), this is safe.
++
++
++
++
++
++ OPTIONS
++
++
++
++
++
++
++
++ This argument must be set accordingly to the position of this module
++ instance in the PAM stack.
++
++
++ The preauth argument must be used when the module
++ is called before the modules which ask for the user credentials such
++ as the password. The module just examines whether the user should
++ be blocked from accessing the service in case there were anomalous
++ number of failed consecutive authentication attempts recently. This
++ call is optional if authsucc is used.
++
++
++ The authfail argument must be used when the module
++ is called after the modules which determine the authentication outcome,
++ failed. Unless the user is already blocked due to previous authentication
++ failures, the module will record the failure into the appropriate user
++ tally file.
++
++
++ The authsucc argument must be used when the module
++ is called after the modules which determine the authentication outcome,
++ succeded. Unless the user is already blocked due to previous authentication
++ failures, the module will then clear the record of the failures in the
++ respective user tally file. Otherwise it will return authentication error.
++ If this call is not done, the pam_faillock will not distinguish between
++ consecutive and non-consecutive failed authentication attempts. The
++ preauth call must be used in such case. Due to
++ complications in the way the PAM stack can be configured it is also
++ possible to call pam_faillock as an account module.
++ In such configuration the module must be also called in the
++ preauth stage.
++
++
++
++
++
++
++
++
++
++ The directory where the user files with the failure records are kept. The
++ default is /var/run/faillock.
++
++
++
++
++
++
++
++
++
++ Will log the user name into the system log if the user is not found.
++
++
++
++
++
++
++
++
++
++ Don't print informative messages. This option is implicite
++ in the authfail and authsucc
++ functions.
++
++
++
++
++
++
++
++
++
++ Don't log informative messages via syslog3.
++
++
++
++
++
++
++
++
++
++ Deny access if the number of consecutive authentication failures
++ for this user during the recent interval exceeds
++ n. The default is 3.
++
++
++
++
++
++
++
++
++
++ The length of the interval during which the consecutive
++ authentication failures must happen for the user account
++ lock out is n seconds.
++ The default is 900 (15 minutes).
++
++
++
++
++
++
++
++
++
++ The access will be reenabled after
++ n seconds after the lock out.
++ The default is 600 (10 minutes).
++
++
++
++
++
++
++
++
++
++ Root account can become locked as well as regular accounts.
++
++
++
++
++
++
++
++
++
++ This option implies option.
++ Allow access after n seconds
++ to root account after the account is locked. In case the
++ option is not specified the value is the same as of the
++ option.
++
++
++
++
++
++
++
++ MODULE TYPES PROVIDED
++
++ The and module types are
++ provided.
++
++
++
++
++ RETURN VALUES
++
++
++ PAM_AUTH_ERR
++
++
++ A invalid option was given, the module was not able
++ to retrieve the user name, no valid counter file
++ was found, or too many failed logins.
++
++
++
++
++ PAM_SUCCESS
++
++
++ Everything was successful.
++
++
++
++
++ PAM_IGNORE
++
++
++ User not present in passwd database.
++
++
++
++
++
++
++
++ NOTES
++
++ pam_faillock setup in the PAM stack is different
++ from the pam_tally2 module setup.
++
++
++ The individual files with the failure records are created as owned by
++ the user. This allows pam_faillock.so module
++ to work correctly when it is called from a screensaver.
++
++
++ Note that using the module in without the
++ option or with requisite
++ control field leaks an information about existence or
++ non-existence of an user account in the system because
++ the failures are not recorded for the unknown users. The message
++ about the user account being locked is never displayed for nonexisting
++ user accounts allowing the adversary to infer that a particular account
++ is not existing on a system.
++
++
++
++
++ EXAMPLES
++
++ Here are two possible configuration examples for /etc/pam.d/login.
++ They make pam_faillock to lock the account after 4 consecutive
++ failed logins during the default interval of 15 minutes. Root account will be locked
++ as well. The accounts will be automatically unlocked after 20 minutes.
++
++
++ In the first example the module is called only in the auth
++ phase and the module does not print any information about the account blocking
++ by pam_faillock. The preauth call can
++ be added to tell the user that his login is blocked by the module and also to abort
++ the authentication without even asking for password in such case.
++
++
++auth required pam_securetty.so
++auth required pam_env.so
++auth required pam_nologin.so
++# optionally call: auth requisite pam_faillock.so preauth deny=4 even_deny_root unlock_time=1200
++# to display the message about account being locked
++auth [success=1 default=bad] pam_unix.so
++auth [default=die] pam_faillock.so authfail deny=4 even_deny_root unlock_time=1200
++auth sufficient pam_faillock.so authsucc deny=4 even_deny_root unlock_time=1200
++auth required pam_deny.so
++account required pam_unix.so
++password required pam_unix.so shadow
++session required pam_selinux.so close
++session required pam_loginuid.so
++session required pam_unix.so
++session required pam_selinux.so open
++
++
++ In the second example the module is called both in the auth
++ and account phases and the module gives the authenticating
++ user message when the account is locked
++
++
++auth required pam_securetty.so
++auth required pam_env.so
++auth required pam_nologin.so
++auth required pam_faillock.so preauth silent deny=4 even_deny_root unlock_time=1200
++# optionally use requisite above if you do not want to prompt for the password
++# on locked accounts, possibly with removing the silent option as well
++auth sufficient pam_unix.so
++auth [default=die] pam_faillock.so authfail deny=4 even_deny_root unlock_time=1200
++auth required pam_deny.so
++account required pam_faillock.so
++# if you drop the above call to pam_faillock.so the lock will be done also
++# on non-consecutive authentication failures
++account required pam_unix.so
++password required pam_unix.so shadow
++session required pam_selinux.so close
++session required pam_loginuid.so
++session required pam_unix.so
++session required pam_selinux.so open
++
++
++
++
++ FILES
++
++
++ /var/run/faillock/*
++
++ the files logging the authentication failures for users
++
++
++
++
++
++
++ SEE ALSO
++
++
++ faillock8
++ ,
++
++ pam.conf5
++ ,
++
++ pam.d5
++ ,
++
++ pam8
++
++
++
++
++
++ AUTHOR
++
++ pam_faillock was written by Tomas Mraz.
++
++
++
++
+diff -up Linux-PAM-1.1.1/modules/pam_faillock/README.xml.faillock Linux-PAM-1.1.1/modules/pam_faillock/README.xml
+--- Linux-PAM-1.1.1/modules/pam_faillock/README.xml.faillock 2011-01-25 18:24:43.000000000 +0100
++++ Linux-PAM-1.1.1/modules/pam_faillock/README.xml 2011-01-25 18:24:43.000000000 +0100
+@@ -0,0 +1,46 @@
++
++
++-->
++]>
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
+diff -up Linux-PAM-1.1.1/modules/pam_faillock/tst-pam_faillock.faillock Linux-PAM-1.1.1/modules/pam_faillock/tst-pam_faillock
+--- Linux-PAM-1.1.1/modules/pam_faillock/tst-pam_faillock.faillock 2011-01-25 18:24:43.000000000 +0100
++++ Linux-PAM-1.1.1/modules/pam_faillock/tst-pam_faillock 2011-01-25 18:24:43.000000000 +0100
+@@ -0,0 +1,2 @@
++#!/bin/sh
++../../tests/tst-dlopen .libs/pam_faillock.so
diff --git a/pam-1.1.8-audit-grantor.patch b/pam-1.1.8-audit-grantor.patch
new file mode 100644
index 0000000..451634b
--- /dev/null
+++ b/pam-1.1.8-audit-grantor.patch
@@ -0,0 +1,435 @@
+From 0d29e379601819c7f7ed8de18b54de803a9f4049 Mon Sep 17 00:00:00 2001
+From: Tomas Mraz
+Date: Fri, 5 Sep 2014 09:09:37 +0200
+Subject: [PATCH] Add grantor field to audit records of libpam.
+
+The grantor field gives audit trail of PAM modules which granted access
+for successful return from libpam calls. In case of failed return
+the grantor field is set to '?'.
+libpam/pam_account.c (pam_acct_mgmt): Remove _pam_auditlog() call.
+libpam/pam_auth.c (pam_authenticate, pam_setcred): Likewise.
+libpam/pam_password.c (pam_chauthtok): Likewise.
+libpam/pam_session.c (pam_open_session, pam_close_session): Likewise.
+libpam/pam_audit.c (_pam_audit_writelog): Add grantors parameter,
+add grantor= field to the message if grantors is set.
+(_pam_list_grantors): New function creating the string with grantors list.
+(_pam_auditlog): Add struct handler pointer parameter, call _pam_list_grantors()
+to list the grantors from the handler list.
+(_pam_audit_end): Add NULL handler parameter to _pam_auditlog() call.
+(pam_modutil_audit_write): Add NULL grantors parameter to _pam_audit_writelog().
+libpam/pam_dispatch.c (_pam_dispatch_aux): Set h->grantor where appropriate.
+(_pam_clear_grantors): New function to clear grantor field of handler.
+(_pam_dispatch): Call _pam_clear_grantors() before executing the stack.
+Call _pam_auditlog() when appropriate.
+libpam/pam_handlers.c (extract_modulename): Do not allow empty module name
+or just "?" to avoid confusing audit trail.
+(_pam_add_handler): Test for NULL return from extract_modulename().
+Clear grantor field of handler.
+libpam/pam_private.h: Add grantor field to struct handler, add handler pointer
+parameter to _pam_auditlog().
+---
+ libpam/pam_account.c | 4 ---
+ libpam/pam_audit.c | 84 +++++++++++++++++++++++++++++++++++++++++++--------
+ libpam/pam_auth.c | 8 -----
+ libpam/pam_dispatch.c | 41 ++++++++++++++++++++-----
+ libpam/pam_handlers.c | 14 +++++++--
+ libpam/pam_password.c | 4 ---
+ libpam/pam_private.h | 3 +-
+ libpam/pam_session.c | 7 -----
+ 8 files changed, 119 insertions(+), 46 deletions(-)
+
+diff --git a/libpam/pam_account.c b/libpam/pam_account.c
+index 572acc4..3a4fb1f 100644
+--- a/libpam/pam_account.c
++++ b/libpam/pam_account.c
+@@ -19,9 +19,5 @@ int pam_acct_mgmt(pam_handle_t *pamh, int flags)
+
+ retval = _pam_dispatch(pamh, flags, PAM_ACCOUNT);
+
+-#ifdef HAVE_LIBAUDIT
+- retval = _pam_auditlog(pamh, PAM_ACCOUNT, retval, flags);
+-#endif
+-
+ return retval;
+ }
+diff --git a/libpam/pam_audit.c b/libpam/pam_audit.c
+index 531746a..24fb799 100644
+--- a/libpam/pam_audit.c
++++ b/libpam/pam_audit.c
+@@ -6,12 +6,12 @@
+ Authors:
+ Steve Grubb */
+
+-#include
+-#include
+ #include "pam_private.h"
+ #include "pam_modutil_private.h"
+
+ #ifdef HAVE_LIBAUDIT
++#include
++#include
+ #include
+ #include
+ #include
+@@ -25,17 +25,24 @@
+
+ static int
+ _pam_audit_writelog(pam_handle_t *pamh, int audit_fd, int type,
+- const char *message, int retval)
++ const char *message, const char *grantors, int retval)
+ {
+ static int old_errno = -1;
+- int rc;
+- char buf[32];
++ int rc = -ENOMEM;
++ char *buf;
++ const char *grantors_field = " grantors=";
+
+- snprintf(buf, sizeof(buf), "PAM:%s", message);
++ if (grantors == NULL) {
++ grantors = "";
++ grantors_field = "";
++ }
+
+- rc = audit_log_acct_message (audit_fd, type, NULL, buf,
+- (retval != PAM_USER_UNKNOWN && pamh->user) ? pamh->user : "?",
+- -1, pamh->rhost, NULL, pamh->tty, retval == PAM_SUCCESS );
++ if (asprintf(&buf, "PAM:%s%s%s", message, grantors_field, grantors) >= 0) {
++ rc = audit_log_acct_message(audit_fd, type, NULL, buf,
++ (retval != PAM_USER_UNKNOWN && pamh->user) ? pamh->user : "?",
++ -1, pamh->rhost, NULL, pamh->tty, retval == PAM_SUCCESS);
++ free(buf);
++ }
+
+ /* libaudit sets errno to his own negative error code. This can be
+ an official errno number, but must not. It can also be a audit
+@@ -78,12 +85,54 @@ _pam_audit_open(pam_handle_t *pamh)
+ return audit_fd;
+ }
+
++static int
++_pam_list_grantors(struct handler *hlist, int retval, char **list)
++{
++ *list = NULL;
++
++ if (retval == PAM_SUCCESS) {
++ struct handler *h;
++ char *p = NULL;
++ size_t len = 0;
++
++ for (h = hlist; h != NULL; h = h->next) {
++ if (h->grantor) {
++ len += strlen(h->mod_name) + 1;
++ }
++ }
++
++ if (len == 0) {
++ return 0;
++ }
++
++ *list = malloc(len);
++ if (*list == NULL) {
++ return -1;
++ }
++
++ for (h = hlist; h != NULL; h = h->next) {
++ if (h->grantor) {
++ if (p == NULL) {
++ p = *list;
++ } else {
++ p = stpcpy(p, ",");
++ }
++
++ p = stpcpy(p, h->mod_name);
++ }
++ }
++ }
++
++ return 0;
++}
++
+ int
+-_pam_auditlog(pam_handle_t *pamh, int action, int retval, int flags)
++_pam_auditlog(pam_handle_t *pamh, int action, int retval, int flags, struct handler *h)
+ {
+ const char *message;
+ int type;
+ int audit_fd;
++ char *grantors;
+
+ if ((audit_fd=_pam_audit_open(pamh)) == -1) {
+ return PAM_SYSTEM_ERR;
+@@ -134,8 +183,17 @@ _pam_auditlog(pam_handle_t *pamh, int action, int retval, int flags)
+ retval = PAM_SYSTEM_ERR;
+ }
+
+- if (_pam_audit_writelog(pamh, audit_fd, type, message, retval) < 0)
++ if (_pam_list_grantors(h, retval, &grantors) < 0) {
++ /* allocation failure */
++ pam_syslog(pamh, LOG_CRIT, "_pam_list_grantors() failed: %m");
+ retval = PAM_SYSTEM_ERR;
++ }
++
++ if (_pam_audit_writelog(pamh, audit_fd, type, message,
++ grantors ? grantors : "?", retval) < 0)
++ retval = PAM_SYSTEM_ERR;
++
++ free(grantors);
+
+ audit_close(audit_fd);
+ return retval;
+@@ -149,7 +207,7 @@ _pam_audit_end(pam_handle_t *pamh, int status UNUSED)
+ * stacks having been run. Assume that this is sshd faking
+ * things for an unknown user.
+ */
+- _pam_auditlog(pamh, _PAM_ACTION_DONE, PAM_USER_UNKNOWN, 0);
++ _pam_auditlog(pamh, _PAM_ACTION_DONE, PAM_USER_UNKNOWN, 0, NULL);
+ }
+
+ return 0;
+@@ -168,7 +226,7 @@ pam_modutil_audit_write(pam_handle_t *pamh, int type,
+ return retval;
+ }
+
+- rc = _pam_audit_writelog(pamh, audit_fd, type, message, retval);
++ rc = _pam_audit_writelog(pamh, audit_fd, type, message, NULL, retval);
+
+ audit_close(audit_fd);
+
+diff --git a/libpam/pam_auth.c b/libpam/pam_auth.c
+index 5984fa5..1e7bc6e 100644
+--- a/libpam/pam_auth.c
++++ b/libpam/pam_auth.c
+@@ -45,10 +45,6 @@ int pam_authenticate(pam_handle_t *pamh, int flags)
+ prelude_send_alert(pamh, retval);
+ #endif
+
+-#ifdef HAVE_LIBAUDIT
+- retval = _pam_auditlog(pamh, PAM_AUTHENTICATE, retval, flags);
+-#endif
+-
+ return retval;
+ }
+
+@@ -71,10 +67,6 @@ int pam_setcred(pam_handle_t *pamh, int flags)
+
+ retval = _pam_dispatch(pamh, flags, PAM_SETCRED);
+
+-#ifdef HAVE_LIBAUDIT
+- retval = _pam_auditlog(pamh, PAM_SETCRED, retval, flags);
+-#endif
+-
+ D(("pam_setcred exit"));
+
+ return retval;
+diff --git a/libpam/pam_dispatch.c b/libpam/pam_dispatch.c
+index eb52c82..cf632e8 100644
+--- a/libpam/pam_dispatch.c
++++ b/libpam/pam_dispatch.c
+@@ -217,8 +217,14 @@ static int _pam_dispatch_aux(pam_handle_t *pamh, int flags, struct handler *h,
+ status = retval;
+ }
+ }
+- if ( impression == _PAM_POSITIVE && action == _PAM_ACTION_DONE ) {
+- goto decision_made;
++ if ( impression == _PAM_POSITIVE ) {
++ if ( retval == PAM_SUCCESS ) {
++ h->grantor = 1;
++ }
++
++ if ( action == _PAM_ACTION_DONE ) {
++ goto decision_made;
++ }
+ }
+ break;
+
+@@ -262,6 +268,9 @@ static int _pam_dispatch_aux(pam_handle_t *pamh, int flags, struct handler *h,
+ || (impression == _PAM_POSITIVE
+ && status == PAM_SUCCESS) ) {
+ if ( retval != PAM_IGNORE || cached_retval == retval ) {
++ if ( impression == _PAM_UNDEF && retval == PAM_SUCCESS ) {
++ h->grantor = 1;
++ }
+ impression = _PAM_POSITIVE;
+ status = retval;
+ }
+@@ -308,6 +317,13 @@ decision_made: /* by getting here we have made a decision */
+ return status;
+ }
+
++static void _pam_clear_grantors(struct handler *h)
++{
++ for (; h != NULL; h = h->next) {
++ h->grantor = 0;
++ }
++}
++
+ /*
+ * This function translates the module dispatch request into a pointer
+ * to the stack of modules that will actually be run. the
+@@ -318,21 +334,21 @@ decision_made: /* by getting here we have made a decision */
+ int _pam_dispatch(pam_handle_t *pamh, int flags, int choice)
+ {
+ struct handler *h = NULL;
+- int retval, use_cached_chain;
++ int retval = PAM_SYSTEM_ERR, use_cached_chain;
+ _pam_boolean resumed;
+
+ IF_NO_PAMH("_pam_dispatch", pamh, PAM_SYSTEM_ERR);
+
+ if (__PAM_FROM_MODULE(pamh)) {
+ D(("called from a module!?"));
+- return PAM_SYSTEM_ERR;
++ goto end;
+ }
+
+ /* Load all modules, resolve all symbols */
+
+ if ((retval = _pam_init_handlers(pamh)) != PAM_SUCCESS) {
+ pam_syslog(pamh, LOG_ERR, "unable to dispatch function");
+- return retval;
++ goto end;
+ }
+
+ use_cached_chain = _PAM_PLEASE_FREEZE;
+@@ -360,7 +376,8 @@ int _pam_dispatch(pam_handle_t *pamh, int flags, int choice)
+ break;
+ default:
+ pam_syslog(pamh, LOG_ERR, "undefined fn choice; %d", choice);
+- return PAM_ABORT;
++ retval = PAM_ABORT;
++ goto end;
+ }
+
+ if (h == NULL) { /* there was no handlers.conf... entry; will use
+@@ -393,11 +410,13 @@ int _pam_dispatch(pam_handle_t *pamh, int flags, int choice)
+ pam_syslog(pamh, LOG_ERR,
+ "application failed to re-exec stack [%d:%d]",
+ pamh->former.choice, choice);
+- return PAM_ABORT;
++ retval = PAM_ABORT;
++ goto end;
+ }
+ resumed = PAM_TRUE;
+ } else {
+ resumed = PAM_FALSE;
++ _pam_clear_grantors(h);
+ }
+
+ __PAM_TO_MODULE(pamh);
+@@ -417,5 +436,13 @@ int _pam_dispatch(pam_handle_t *pamh, int flags, int choice)
+ pamh->former.choice = PAM_NOT_STACKED;
+ }
+
++end:
++
++#ifdef HAVE_LIBAUDIT
++ if (choice != PAM_CHAUTHTOK || flags & PAM_UPDATE_AUTHTOK || retval != PAM_SUCCESS) {
++ retval = _pam_auditlog(pamh, choice, retval, flags, h);
++ }
++#endif
++
+ return retval;
+ }
+diff --git a/libpam/pam_handlers.c b/libpam/pam_handlers.c
+index 02714f7..df3a1d9 100644
+--- a/libpam/pam_handlers.c
++++ b/libpam/pam_handlers.c
+@@ -611,6 +611,12 @@ extract_modulename(const char *mod_path)
+ if (dot)
+ *dot = '\0';
+
++ if (*retval == '\0' || strcmp(retval, "?") == 0) {
++ /* do not allow empty module name or "?" to avoid confusing audit trail */
++ _pam_drop(retval);
++ return NULL;
++ }
++
+ return retval;
+ }
+
+@@ -888,7 +894,9 @@ int _pam_add_handler(pam_handle_t *pamh
+ (*handler_p)->cached_retval_p = &((*handler_p)->cached_retval);
+ (*handler_p)->argc = argc;
+ (*handler_p)->argv = argv; /* not a copy */
+- (*handler_p)->mod_name = extract_modulename(mod_path);
++ if (((*handler_p)->mod_name = extract_modulename(mod_path)) == NULL)
++ return PAM_ABORT;
++ (*handler_p)->grantor = 0;
+ (*handler_p)->next = NULL;
+
+ /* some of the modules have a second calling function */
+@@ -920,7 +928,9 @@ int _pam_add_handler(pam_handle_t *pamh
+ } else {
+ (*handler_p2)->argv = NULL; /* no arguments */
+ }
+- (*handler_p2)->mod_name = extract_modulename(mod_path);
++ if (((*handler_p2)->mod_name = extract_modulename(mod_path)) == NULL)
++ return PAM_ABORT;
++ (*handler_p2)->grantor = 0;
+ (*handler_p2)->next = NULL;
+ }
+
+diff --git a/libpam/pam_password.c b/libpam/pam_password.c
+index 75db5e5..592e01f 100644
+--- a/libpam/pam_password.c
++++ b/libpam/pam_password.c
+@@ -57,9 +57,5 @@ int pam_chauthtok(pam_handle_t *pamh, int flags)
+ D(("will resume when ready", retval));
+ }
+
+-#ifdef HAVE_LIBAUDIT
+- retval = _pam_auditlog(pamh, PAM_CHAUTHTOK, retval, flags);
+-#endif
+-
+ return retval;
+ }
+diff --git a/libpam/pam_private.h b/libpam/pam_private.h
+index 134dc72..d93283c 100644
+--- a/libpam/pam_private.h
++++ b/libpam/pam_private.h
+@@ -55,6 +55,7 @@ struct handler {
+ struct handler *next;
+ char *mod_name;
+ int stack_level;
++ int grantor;
+ };
+
+ #define PAM_HT_MODULE 0
+@@ -316,7 +317,7 @@ if ((pamh) == NULL) { \
+ do { (pamh)->caller_is = _PAM_CALLED_FROM_APP; } while (0)
+
+ #ifdef HAVE_LIBAUDIT
+-extern int _pam_auditlog(pam_handle_t *pamh, int action, int retval, int flags);
++extern int _pam_auditlog(pam_handle_t *pamh, int action, int retval, int flags, struct handler *h);
+ extern int _pam_audit_end(pam_handle_t *pamh, int pam_status);
+ #endif
+
+diff --git a/libpam/pam_session.c b/libpam/pam_session.c
+index 512153f..cb393c1 100644
+--- a/libpam/pam_session.c
++++ b/libpam/pam_session.c
+@@ -22,9 +22,6 @@ int pam_open_session(pam_handle_t *pamh, int flags)
+ }
+ retval = _pam_dispatch(pamh, flags, PAM_OPEN_SESSION);
+
+-#ifdef HAVE_LIBAUDIT
+- retval = _pam_auditlog(pamh, PAM_OPEN_SESSION, retval, flags);
+-#endif
+ return retval;
+ }
+
+@@ -43,10 +40,6 @@ int pam_close_session(pam_handle_t *pamh, int flags)
+
+ retval = _pam_dispatch(pamh, flags, PAM_CLOSE_SESSION);
+
+-#ifdef HAVE_LIBAUDIT
+- retval = _pam_auditlog(pamh, PAM_CLOSE_SESSION, retval, flags);
+-#endif
+-
+ return retval;
+
+ }
+--
+1.8.3.1
+
diff --git a/pam-1.1.8-audit-user-mgmt.patch b/pam-1.1.8-audit-user-mgmt.patch
new file mode 100644
index 0000000..990a1b1
--- /dev/null
+++ b/pam-1.1.8-audit-user-mgmt.patch
@@ -0,0 +1,53 @@
+--- a/modules/pam_faillock/main.c.audit-user-mgmt 2014-10-17 12:09:12.928490104 +0200
++++ b/modules/pam_faillock/main.c 2014-10-17 12:09:43.001169008 +0200
+@@ -127,7 +127,6 @@ do_user(struct options *opts, const char
+ }
+ if (opts->reset) {
+ #ifdef HAVE_LIBAUDIT
+- char buf[64];
+ int audit_fd;
+ #endif
+
+@@ -141,10 +140,8 @@ do_user(struct options *opts, const char
+ if ((audit_fd=audit_open()) >= 0) {
+
+ if (pwd != NULL) {
+- snprintf(buf, sizeof(buf), "faillock reset uid=%u",
+- pwd->pw_uid);
+- audit_log_user_message(audit_fd, AUDIT_USER_ACCT,
+- buf, NULL, NULL, NULL, rv == 0);
++ audit_log_acct_message(audit_fd, AUDIT_USER_MGMT, NULL,
++ "faillock-reset", NULL, pwd->pw_uid, NULL, NULL, NULL, rv == 0);
+ }
+ close(audit_fd);
+ }
+--- a/modules/pam_tally2/pam_tally2.c.audit-user-mgmt 2013-06-18 16:11:21.000000000 +0200
++++ b/modules/pam_tally2/pam_tally2.c 2014-10-17 12:09:12.965490940 +0200
+@@ -997,9 +997,9 @@ main( int argc UNUSED, char **argv )
+ #ifdef HAVE_LIBAUDIT
+ char buf[64];
+ int audit_fd = audit_open();
+- snprintf(buf, sizeof(buf), "pam_tally2 uid=%u reset=%hu", uid, cline_reset);
+- audit_log_user_message(audit_fd, AUDIT_USER_ACCT,
+- buf, NULL, NULL, ttyname(STDIN_FILENO), 1);
++ snprintf(buf, sizeof(buf), "pam_tally2 reset=%hu", cline_reset);
++ audit_log_acct_message(audit_fd, AUDIT_USER_MGMT, NULL,
++ buf, NULL, uid, NULL, NULL, ttyname(STDIN_FILENO), 1);
+ if (audit_fd >=0)
+ close(audit_fd);
+ #endif
+@@ -1040,11 +1040,10 @@ main( int argc UNUSED, char **argv )
+ }
+ else if ( !cline_reset ) {
+ #ifdef HAVE_LIBAUDIT
+- char buf[64];
+ int audit_fd = audit_open();
+- snprintf(buf, sizeof(buf), "pam_tally2 uid=all reset=0");
+- audit_log_user_message(audit_fd, AUDIT_USER_ACCT,
+- buf, NULL, NULL, ttyname(STDIN_FILENO), 1);
++ audit_log_acct_message(audit_fd, AUDIT_USER_MGMT, NULL,
++ "pam_tally2-reset-all-accts reset=0", "*", -1,
++ NULL, NULL, ttyname(STDIN_FILENO), 1);
+ if (audit_fd >=0)
+ close(audit_fd);
+ #endif
diff --git a/pam-1.1.8-canonicalize-username.patch b/pam-1.1.8-canonicalize-username.patch
new file mode 100644
index 0000000..a3786be
--- /dev/null
+++ b/pam-1.1.8-canonicalize-username.patch
@@ -0,0 +1,21 @@
+diff -up Linux-PAM-1.1.8/modules/pam_selinux/pam_selinux.c.canonicalize Linux-PAM-1.1.8/modules/pam_selinux/pam_selinux.c
+--- Linux-PAM-1.1.8/modules/pam_selinux/pam_selinux.c.canonicalize 2013-06-18 16:11:21.000000000 +0200
++++ Linux-PAM-1.1.8/modules/pam_selinux/pam_selinux.c 2014-03-06 12:03:54.429639972 +0100
+@@ -491,12 +491,17 @@ compute_exec_context(pam_handle_t *pamh,
+ char *level = NULL;
+ security_context_t *contextlist = NULL;
+ int num_contexts = 0;
++ const struct passwd *pwd;
+
+ if (!(username = get_item(pamh, PAM_USER))) {
+ pam_syslog(pamh, LOG_ERR, "Cannot obtain the user name");
+ return PAM_USER_UNKNOWN;
+ }
+
++ if ((pwd = pam_modutil_getpwnam(pamh, username)) != NULL) {
++ username = pwd->pw_name;
++ } /* ignore error and keep using original username */
++
+ /* compute execute context */
+ #ifdef HAVE_GETSEUSER
+ if (!(service = get_item(pamh, PAM_SERVICE))) {
diff --git a/pam-1.1.8-full-relro.patch b/pam-1.1.8-full-relro.patch
new file mode 100644
index 0000000..e4eba78
--- /dev/null
+++ b/pam-1.1.8-full-relro.patch
@@ -0,0 +1,108 @@
+diff -up Linux-PAM-1.1.8/modules/pam_console/Makefile.am.relro Linux-PAM-1.1.8/modules/pam_console/Makefile.am
+--- Linux-PAM-1.1.8/modules/pam_console/Makefile.am.relro 2014-08-13 16:02:49.000000000 +0200
++++ Linux-PAM-1.1.8/modules/pam_console/Makefile.am 2014-09-10 17:14:33.245554314 +0200
+@@ -33,6 +33,8 @@ pam_console_la_LIBADD = -L$(top_builddir
+
+ pam_console_apply_LDADD = -L$(top_builddir)/libpam -lpam
+
++pam_console_apply_LDFLAGS = -Wl,-z,now @PIE_LDFLAGS@
++
+ securelib_LTLIBRARIES = pam_console.la
+ sbin_PROGRAMS = pam_console_apply
+
+@@ -47,7 +49,7 @@ pam_console_apply_SOURCES = pam_console_
+ configfile.c configfile.h hashtable.c hashtable.h hashtable_private.h
+
+ pam_console_la_CFLAGS = $(AM_CFLAGS)
+-pam_console_apply_CFLAGS = $(AM_CFLAGS)
++pam_console_apply_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@
+
+ configfile.tab.c: configfile.y
+ $(YACC) $(BISON_OPTS) -o $@ -p _pc_yy $<
+diff -up Linux-PAM-1.1.8/modules/pam_faillock/Makefile.am.relro Linux-PAM-1.1.8/modules/pam_faillock/Makefile.am
+--- Linux-PAM-1.1.8/modules/pam_faillock/Makefile.am.relro 2014-08-13 16:02:49.000000000 +0200
++++ Linux-PAM-1.1.8/modules/pam_faillock/Makefile.am 2014-09-10 17:16:11.102808189 +0200
+@@ -19,7 +19,7 @@ secureconfdir = $(SCONFIGDIR)
+
+ noinst_HEADERS = faillock.h
+
+-faillock_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include
++faillock_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include @PIE_CFLAGS@
+ pam_faillock_la_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include
+
+ pam_faillock_la_LDFLAGS = -no-undefined -avoid-version -module
+@@ -28,6 +28,7 @@ if HAVE_VERSIONING
+ pam_faillock_la_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
+ endif
+
++faillock_LDFLAGS = -Wl,-z,now @PIE_LDFLAGS@
+ faillock_LDADD = -L$(top_builddir)/libpam -lpam $(LIBAUDIT)
+
+ securelib_LTLIBRARIES = pam_faillock.la
+diff -up Linux-PAM-1.1.8/modules/pam_filter/upperLOWER/Makefile.am.relro Linux-PAM-1.1.8/modules/pam_filter/upperLOWER/Makefile.am
+--- Linux-PAM-1.1.8/modules/pam_filter/upperLOWER/Makefile.am.relro 2014-09-10 17:17:20.273401344 +0200
++++ Linux-PAM-1.1.8/modules/pam_filter/upperLOWER/Makefile.am 2014-09-10 17:17:07.857115369 +0200
+@@ -9,7 +9,7 @@ securelibfilterdir = $(SECUREDIR)/pam_fi
+
+ AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
+ -I$(srcdir)/.. @PIE_CFLAGS@
+-AM_LDFLAGS = @PIE_LDFLAGS@
++AM_LDFLAGS = -Wl,-z,now @PIE_LDFLAGS@
+ LDADD = $(top_builddir)/libpam/libpam.la
+
+ securelibfilter_PROGRAMS = upperLOWER
+diff -up Linux-PAM-1.1.8/modules/pam_mkhomedir/Makefile.am.relro Linux-PAM-1.1.8/modules/pam_mkhomedir/Makefile.am
+--- Linux-PAM-1.1.8/modules/pam_mkhomedir/Makefile.am.relro 2013-06-18 16:11:21.000000000 +0200
++++ Linux-PAM-1.1.8/modules/pam_mkhomedir/Makefile.am 2014-09-10 17:18:42.922304935 +0200
+@@ -30,6 +30,8 @@ endif
+
+ sbin_PROGRAMS = mkhomedir_helper
+ mkhomedir_helper_SOURCES = mkhomedir_helper.c
++mkhomedir_helper_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@
++mkhomedir_helper_LDFLAGS = -Wl,-z,now @PIE_LDFLAGS@
+ mkhomedir_helper_LDADD = $(top_builddir)/libpam/libpam.la
+
+ if ENABLE_REGENERATE_MAN
+diff -up Linux-PAM-1.1.8/modules/pam_tally2/Makefile.am.relro Linux-PAM-1.1.8/modules/pam_tally2/Makefile.am
+--- Linux-PAM-1.1.8/modules/pam_tally2/Makefile.am.relro 2013-06-18 16:11:21.000000000 +0200
++++ Linux-PAM-1.1.8/modules/pam_tally2/Makefile.am 2014-09-10 17:22:04.339944040 +0200
+@@ -26,6 +26,8 @@ if HAVE_VERSIONING
+ pam_tally2_la_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
+ endif
+
++pam_tally2_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@
++pam_tally2_LDFLAGS = -Wl,-z,now @PIE_LDFLAGS@
+ pam_tally2_LDADD = $(top_builddir)/libpam/libpam.la $(LIBAUDIT)
+
+ securelib_LTLIBRARIES = pam_tally2.la
+diff -up Linux-PAM-1.1.8/modules/pam_timestamp/Makefile.am.relro Linux-PAM-1.1.8/modules/pam_timestamp/Makefile.am
+--- Linux-PAM-1.1.8/modules/pam_timestamp/Makefile.am.relro 2013-06-18 16:11:21.000000000 +0200
++++ Linux-PAM-1.1.8/modules/pam_timestamp/Makefile.am 2014-08-13 16:02:49.906688139 +0200
+@@ -36,7 +36,7 @@ pam_timestamp_la_CFLAGS = $(AM_CFLAGS)
+ pam_timestamp_check_SOURCES = pam_timestamp_check.c
+ pam_timestamp_check_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@
+ pam_timestamp_check_LDADD = $(top_builddir)/libpam/libpam.la
+-pam_timestamp_check_LDFLAGS = @PIE_LDFLAGS@
++pam_timestamp_check_LDFLAGS = -Wl,-z,now @PIE_LDFLAGS@
+
+ hmacfile_SOURCES = hmacfile.c hmacsha1.c sha1.c
+ hmacfile_LDADD = $(top_builddir)/libpam/libpam.la
+diff -up Linux-PAM-1.1.8/modules/pam_unix/Makefile.am.relro Linux-PAM-1.1.8/modules/pam_unix/Makefile.am
+--- Linux-PAM-1.1.8/modules/pam_unix/Makefile.am.relro 2013-06-18 16:11:21.000000000 +0200
++++ Linux-PAM-1.1.8/modules/pam_unix/Makefile.am 2014-08-13 16:02:49.906688139 +0200
+@@ -55,13 +55,13 @@ bigcrypt_LDADD = @LIBCRYPT@
+ unix_chkpwd_SOURCES = unix_chkpwd.c md5_good.c md5_broken.c bigcrypt.c \
+ passverify.c
+ unix_chkpwd_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@ -DHELPER_COMPILE=\"unix_chkpwd\"
+-unix_chkpwd_LDFLAGS = @PIE_LDFLAGS@
++unix_chkpwd_LDFLAGS = -Wl,-z,now @PIE_LDFLAGS@
+ unix_chkpwd_LDADD = @LIBCRYPT@ @LIBSELINUX@ @LIBAUDIT@
+
+ unix_update_SOURCES = unix_update.c md5_good.c md5_broken.c bigcrypt.c \
+ passverify.c
+ unix_update_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@ -DHELPER_COMPILE=\"unix_update\"
+-unix_update_LDFLAGS = @PIE_LDFLAGS@
++unix_update_LDFLAGS = -Wl,-z,now @PIE_LDFLAGS@
+ unix_update_LDADD = @LIBCRYPT@ @LIBSELINUX@
+
+ if ENABLE_REGENERATE_MAN
diff --git a/pam-1.1.8-lastlog-uninitialized.patch b/pam-1.1.8-lastlog-uninitialized.patch
new file mode 100644
index 0000000..8c545d9
--- /dev/null
+++ b/pam-1.1.8-lastlog-uninitialized.patch
@@ -0,0 +1,37 @@
+diff -up Linux-PAM-1.1.8/modules/pam_lastlog/pam_lastlog.c.uninitialized Linux-PAM-1.1.8/modules/pam_lastlog/pam_lastlog.c
+--- Linux-PAM-1.1.8/modules/pam_lastlog/pam_lastlog.c.uninitialized 2013-06-18 16:11:21.000000000 +0200
++++ Linux-PAM-1.1.8/modules/pam_lastlog/pam_lastlog.c 2014-08-25 16:44:24.365174752 +0200
+@@ -350,6 +350,8 @@ last_login_write(pam_handle_t *pamh, int
+ return PAM_SERVICE_ERR;
+ }
+
++ memset(&last_login, 0, sizeof(last_login));
++
+ /* set this login date */
+ D(("set the most recent login time"));
+ (void) time(&ll_time); /* set the time */
+@@ -364,14 +366,12 @@ last_login_write(pam_handle_t *pamh, int
+ }
+
+ /* copy to last_login */
+- last_login.ll_host[0] = '\0';
+ strncat(last_login.ll_host, remote_host, sizeof(last_login.ll_host)-1);
+
+ /* set the terminal line */
+ terminal_line = get_tty(pamh);
+
+ /* copy to last_login */
+- last_login.ll_line[0] = '\0';
+ strncat(last_login.ll_line, terminal_line, sizeof(last_login.ll_line)-1);
+ terminal_line = NULL;
+
+@@ -628,7 +628,8 @@ pam_sm_authenticate(pam_handle_t *pamh,
+ lltime = (time(NULL) - lltime) / (24*60*60);
+
+ if (lltime > inactive_days) {
+- pam_syslog(pamh, LOG_INFO, "user %s inactive for %d days - denied", user, lltime);
++ pam_syslog(pamh, LOG_INFO, "user %s inactive for %ld days - denied",
++ user, (long) lltime);
+ return PAM_AUTH_ERR;
+ }
+
diff --git a/pam-1.1.8-limits-check-process.patch b/pam-1.1.8-limits-check-process.patch
new file mode 100644
index 0000000..f37f799
--- /dev/null
+++ b/pam-1.1.8-limits-check-process.patch
@@ -0,0 +1,41 @@
+diff -up Linux-PAM-1.1.8/modules/pam_limits/pam_limits.c.check-process Linux-PAM-1.1.8/modules/pam_limits/pam_limits.c
+--- Linux-PAM-1.1.8/modules/pam_limits/pam_limits.c.check-process 2013-06-18 16:11:21.000000000 +0200
++++ Linux-PAM-1.1.8/modules/pam_limits/pam_limits.c 2014-09-10 16:39:36.263256066 +0200
+@@ -27,6 +27,7 @@
+ #include
+ #include
+ #include
++#include
+ #include
+ #include
+ #include
+@@ -269,16 +270,27 @@ check_logins (pam_handle_t *pamh, const
+ continue;
+ }
+ if (!pl->flag_numsyslogins) {
++ char user[sizeof(ut->UT_USER) + 1];
++ user[0] = '\0';
++ strncat(user, ut->UT_USER, sizeof(ut->UT_USER));
++
+ if (((pl->login_limit_def == LIMITS_DEF_USER)
+ || (pl->login_limit_def == LIMITS_DEF_GROUP)
+ || (pl->login_limit_def == LIMITS_DEF_DEFAULT))
+- && strncmp(name, ut->UT_USER, sizeof(ut->UT_USER)) != 0) {
++ && strcmp(name, user) != 0) {
+ continue;
+ }
+ if ((pl->login_limit_def == LIMITS_DEF_ALLGROUP)
+- && !pam_modutil_user_in_group_nam_nam(pamh, ut->UT_USER, pl->login_group)) {
++ && !pam_modutil_user_in_group_nam_nam(pamh, user, pl->login_group)) {
+ continue;
+ }
++ if (kill(ut->ut_pid, 0) == -1 && errno == ESRCH) {
++ /* process does not exist anymore */
++ pam_syslog(pamh, LOG_WARNING,
++ "Stale utmp entry (pid %d) for '%s' ignored",
++ ut->ut_pid, user);
++ continue;
++ }
+ }
+ if (++count > limit) {
+ break;
diff --git a/pam-1.1.8-limits-docfix.patch b/pam-1.1.8-limits-docfix.patch
new file mode 100644
index 0000000..37703e3
--- /dev/null
+++ b/pam-1.1.8-limits-docfix.patch
@@ -0,0 +1,54 @@
+diff -up Linux-PAM-1.1.8/modules/pam_limits/limits.conf.docfix Linux-PAM-1.1.8/modules/pam_limits/limits.conf
+--- Linux-PAM-1.1.8/modules/pam_limits/limits.conf.docfix 2014-07-14 14:58:05.000000000 +0200
++++ Linux-PAM-1.1.8/modules/pam_limits/limits.conf 2014-09-10 16:42:51.254747161 +0200
+@@ -32,7 +32,7 @@
+ # - data - max data size (KB)
+ # - fsize - maximum filesize (KB)
+ # - memlock - max locked-in-memory address space (KB)
+-# - nofile - max number of open files
++# - nofile - max number of open file descriptors
+ # - rss - max resident set size (KB)
+ # - stack - max stack size (KB)
+ # - cpu - max CPU time (MIN)
+diff -up Linux-PAM-1.1.8/modules/pam_limits/limits.conf.5.xml.docfix Linux-PAM-1.1.8/modules/pam_limits/limits.conf.5.xml
+--- Linux-PAM-1.1.8/modules/pam_limits/limits.conf.5.xml.docfix 2013-06-18 16:11:21.000000000 +0200
++++ Linux-PAM-1.1.8/modules/pam_limits/limits.conf.5.xml 2014-09-10 16:44:01.624367933 +0200
+@@ -178,7 +178,7 @@
+
+
+
+- maximum number of open files
++ maximum number of open file descriptors
+
+
+
+@@ -214,14 +214,17 @@
+
+
+
+- maximum number of logins for this user except
+- for this with uid=0
++ maximum number of logins for this user (this limit does
++ not apply to user with uid=0)
+
+
+
+
+
+- maximum number of all logins on system
++ maximum number of all logins on system; user is not
++ allowed to log-in if total number of all users' logins is
++ greater than specified number (this limit does not apply to
++ user with uid=0)
+
+
+
+@@ -292,7 +295,7 @@
+ permanent; existing only for the duration of the session.
+ One exception is the maxlogin option, this one
+ is system wide. But there is a race, concurrent logins at the same
+- time will not always be detect as such but only counted as one.
++ time will not always be detected as such but only counted as one.
+
+
+ In the limits configuration file, the
diff --git a/pam-1.1.8-loginuid-container.patch b/pam-1.1.8-loginuid-container.patch
new file mode 100644
index 0000000..278829a
--- /dev/null
+++ b/pam-1.1.8-loginuid-container.patch
@@ -0,0 +1,151 @@
+diff -up Linux-PAM-1.1.8/modules/pam_loginuid/pam_loginuid.c.container Linux-PAM-1.1.8/modules/pam_loginuid/pam_loginuid.c
+--- Linux-PAM-1.1.8/modules/pam_loginuid/pam_loginuid.c.container 2013-06-18 16:11:21.000000000 +0200
++++ Linux-PAM-1.1.8/modules/pam_loginuid/pam_loginuid.c 2014-01-27 17:24:53.000000000 +0100
+@@ -47,25 +47,56 @@
+
+ /*
+ * This function writes the loginuid to the /proc system. It returns
+- * 0 on success and 1 on failure.
++ * PAM_SUCCESS on success,
++ * PAM_IGNORE when /proc/self/loginuid does not exist,
++ * PAM_SESSION_ERR in case of any other error.
+ */
+ static int set_loginuid(pam_handle_t *pamh, uid_t uid)
+ {
+- int fd, count, rc = 0;
+- char loginuid[24];
++ int fd, count, rc = PAM_SESSION_ERR;
++ char loginuid[24], buf[24];
++ static const char host_uid_map[] = " 0 0 4294967295\n";
++ char uid_map[sizeof(host_uid_map)];
++
++ /* loginuid in user namespaces currently isn't writable and in some
++ case, not even readable, so consider any failure as ignorable (but try
++ anyway, in case we hit a kernel which supports it). */
++ fd = open("/proc/self/uid_map", O_RDONLY);
++ if (fd >= 0) {
++ count = pam_modutil_read(fd, uid_map, sizeof(uid_map));
++ if (strncmp(uid_map, host_uid_map, count) != 0)
++ rc = PAM_IGNORE;
++ close(fd);
++ }
+
+- count = snprintf(loginuid, sizeof(loginuid), "%lu", (unsigned long)uid);
+- fd = open("/proc/self/loginuid", O_NOFOLLOW|O_WRONLY|O_TRUNC);
++ fd = open("/proc/self/loginuid", O_NOFOLLOW|O_RDWR);
+ if (fd < 0) {
+- if (errno != ENOENT) {
+- rc = 1;
+- pam_syslog(pamh, LOG_ERR,
+- "Cannot open /proc/self/loginuid: %m");
++ if (errno == ENOENT) {
++ rc = PAM_IGNORE;
++ }
++ if (rc != PAM_IGNORE) {
++ pam_syslog(pamh, LOG_ERR, "Cannot open %s: %m",
++ "/proc/self/loginuid");
+ }
+ return rc;
+ }
+- if (pam_modutil_write(fd, loginuid, count) != count)
+- rc = 1;
++
++ count = snprintf(loginuid, sizeof(loginuid), "%lu", (unsigned long)uid);
++ if (pam_modutil_read(fd, buf, sizeof(buf)) == count &&
++ memcmp(buf, loginuid, count) == 0) {
++ rc = PAM_SUCCESS;
++ goto done; /* already correct */
++ }
++ if (lseek(fd, 0, SEEK_SET) == 0 && ftruncate(fd, 0) == 0 &&
++ pam_modutil_write(fd, loginuid, count) == count) {
++ rc = PAM_SUCCESS;
++ } else {
++ if (rc != PAM_IGNORE) {
++ pam_syslog(pamh, LOG_ERR, "Error writing %s: %m",
++ "/proc/self/loginuid");
++ }
++ }
++ done:
+ close(fd);
+ return rc;
+ }
+@@ -165,6 +196,7 @@ _pam_loginuid(pam_handle_t *pamh, int fl
+ {
+ const char *user = NULL;
+ struct passwd *pwd;
++ int ret;
+ #ifdef HAVE_LIBAUDIT
+ int require_auditd = 0;
+ #endif
+@@ -183,9 +215,14 @@ _pam_loginuid(pam_handle_t *pamh, int fl
+ return PAM_SESSION_ERR;
+ }
+
+- if (set_loginuid(pamh, pwd->pw_uid)) {
+- pam_syslog(pamh, LOG_ERR, "set_loginuid failed\n");
+- return PAM_SESSION_ERR;
++ ret = set_loginuid(pamh, pwd->pw_uid);
++ switch (ret) {
++ case PAM_SUCCESS:
++ case PAM_IGNORE:
++ break;
++ default:
++ pam_syslog(pamh, LOG_ERR, "set_loginuid failed");
++ return ret;
+ }
+
+ #ifdef HAVE_LIBAUDIT
+@@ -195,11 +232,12 @@ _pam_loginuid(pam_handle_t *pamh, int fl
+ argv++;
+ }
+
+- if (require_auditd)
+- return check_auditd();
+- else
++ if (require_auditd) {
++ int rc = check_auditd();
++ return rc != PAM_SUCCESS ? rc : ret;
++ } else
+ #endif
+- return PAM_SUCCESS;
++ return ret;
+ }
+
+ /*
+diff -up Linux-PAM-1.1.8/modules/pam_loginuid/pam_loginuid.8.xml.container Linux-PAM-1.1.8/modules/pam_loginuid/pam_loginuid.8.xml
+--- Linux-PAM-1.1.8/modules/pam_loginuid/pam_loginuid.8.xml.container 2013-06-18 16:11:21.000000000 +0200
++++ Linux-PAM-1.1.8/modules/pam_loginuid/pam_loginuid.8.xml 2014-05-22 11:33:14.000000000 +0200
+@@ -69,14 +69,31 @@
+
+
+
++ PAM_SUCCESS
++
++
++ The loginuid value is set and auditd is running if check requested.
++
++
++
++
++ PAM_IGNORE
++
++
++ The /proc/self/loginuid file is not present on the system or the
++ login process runs inside uid namespace and kernel does not support
++ overwriting loginuid.
++
++
++
++
+ PAM_SESSION_ERR
+
+
+- An error occurred during session management.
++ Any other error prevented setting loginuid or auditd is not running.
+
+
+
+-
+
+
+
diff --git a/pam-1.1.8-man-dbsuffix.patch b/pam-1.1.8-man-dbsuffix.patch
new file mode 100644
index 0000000..be2e231
--- /dev/null
+++ b/pam-1.1.8-man-dbsuffix.patch
@@ -0,0 +1,22 @@
+diff -up Linux-PAM-1.1.8/modules/pam_userdb/pam_userdb.8.xml.dbsuffix Linux-PAM-1.1.8/modules/pam_userdb/pam_userdb.8.xml
+--- Linux-PAM-1.1.8/modules/pam_userdb/pam_userdb.8.xml.dbsuffix 2013-06-18 16:11:21.000000000 +0200
++++ Linux-PAM-1.1.8/modules/pam_userdb/pam_userdb.8.xml 2014-09-10 16:28:19.916678273 +0200
+@@ -89,7 +89,8 @@
+ Use the /path/database database for
+ performing lookup. There is no default; the module will
+ return PAM_IGNORE if no
+- database is provided.
++ database is provided. Note that the path to the database file
++ should be specified without the .db suffix.
+
+
+
+@@ -260,7 +261,7 @@
+
+ EXAMPLES
+
+-auth sufficient pam_userdb.so icase db=/etc/dbtest.db
++auth sufficient pam_userdb.so icase db=/etc/dbtest
+
+
+
diff --git a/pam-1.1.8-opasswd-tolerant.patch b/pam-1.1.8-opasswd-tolerant.patch
new file mode 100644
index 0000000..fb9b198
--- /dev/null
+++ b/pam-1.1.8-opasswd-tolerant.patch
@@ -0,0 +1,50 @@
+diff --git a/modules/pam_pwhistory/opasswd.c b/modules/pam_pwhistory/opasswd.c
+index 836d713..c36628e 100644
+--- a/modules/pam_pwhistory/opasswd.c
++++ b/modules/pam_pwhistory/opasswd.c
+@@ -82,10 +82,15 @@ parse_entry (char *line, opwd *data)
+ {
+ const char delimiters[] = ":";
+ char *endptr;
++ char *count;
+
+ data->user = strsep (&line, delimiters);
+ data->uid = strsep (&line, delimiters);
+- data->count = strtol (strsep (&line, delimiters), &endptr, 10);
++ count = strsep (&line, delimiters);
++ if (data->user == NULL || data->uid == NULL || count == NULL)
++ return 1;
++
++ data->count = strtol (count, &endptr, 10);
+ if (endptr != NULL && *endptr != '\0')
+ return 1;
+
+diff --git a/modules/pam_unix/passverify.c b/modules/pam_unix/passverify.c
+index 4840bb2..7f7bc49 100644
+--- a/modules/pam_unix/passverify.c
++++ b/modules/pam_unix/passverify.c
+@@ -639,11 +639,23 @@ save_old_password(pam_handle_t *pamh, const char *forwho, const char *oldpass,
+ continue;
+ buf[strlen(buf) - 1] = '\0';
+ s_luser = strtok_r(buf, ":", &sptr);
++ if (s_luser == NULL) {
++ found = 0;
++ continue;
++ }
+ s_uid = strtok_r(NULL, ":", &sptr);
++ if (s_uid == NULL) {
++ found = 0;
++ continue;
++ }
+ s_npas = strtok_r(NULL, ":", &sptr);
++ if (s_npas == NULL) {
++ found = 0;
++ continue;
++ }
+ s_pas = strtok_r(NULL, ":", &sptr);
+ npas = strtol(s_npas, NULL, 10) + 1;
+- while (npas > howmany) {
++ while (npas > howmany && s_pas != NULL) {
+ s_pas = strpbrk(s_pas, ",");
+ if (s_pas != NULL)
+ s_pas++;
diff --git a/pam-1.1.8-pbuild.patch b/pam-1.1.8-pbuild.patch
new file mode 100644
index 0000000..db4d612
--- /dev/null
+++ b/pam-1.1.8-pbuild.patch
@@ -0,0 +1,11 @@
+diff -ur Linux-PAM-1.1.8.old/modules/pam_console/Makefile.am Linux-PAM-1.1.8/modules/pam_console/Makefile.am
+--- Linux-PAM-1.1.8.old/modules/pam_console/Makefile.am 2015-01-21 13:49:13.000000000 +0300
++++ Linux-PAM-1.1.8/modules/pam_console/Makefile.am 2015-01-21 13:54:33.000000000 +0300
+@@ -51,6 +51,7 @@
+ pam_console_la_CFLAGS = $(AM_CFLAGS)
+ pam_console_apply_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@
+
++configfile.tab.h: configfile.tab.c
+ configfile.tab.c: configfile.y
+ $(YACC) $(BISON_OPTS) -o $@ -p _pc_yy $<
+ sh $(srcdir)/sed-static $@
diff --git a/pam-1.1.8-pwhistory-helper.patch b/pam-1.1.8-pwhistory-helper.patch
new file mode 100644
index 0000000..505b75f
--- /dev/null
+++ b/pam-1.1.8-pwhistory-helper.patch
@@ -0,0 +1,813 @@
+diff --git a/modules/pam_pwhistory/Makefile.am b/modules/pam_pwhistory/Makefile.am
+index 4bb4d6d..9157b91 100644
+--- a/modules/pam_pwhistory/Makefile.am
++++ b/modules/pam_pwhistory/Makefile.am
+@@ -1,5 +1,6 @@
+ #
+ # Copyright (c) 2008, 2009 Thorsten Kukuk
++# Copyright (c) 2013 Red Hat, Inc.
+ #
+
+ CLEANFILES = *~
+@@ -9,25 +10,34 @@ EXTRA_DIST = README $(MANS) $(XMLS) tst-pam_pwhistory
+
+ TESTS = tst-pam_pwhistory
+
+-man_MANS = pam_pwhistory.8
++man_MANS = pam_pwhistory.8 pwhistory_helper.8
+
+-XMLS = README.xml pam_pwhistory.8.xml
++XMLS = README.xml pam_pwhistory.8.xml pwhistory_helper.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_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
++ -DPWHISTORY_HELPER=\"$(sbindir)/pwhistory_helper\"
++
++pam_pwhistory_la_LDFLAGS = -no-undefined -avoid-version -module
+ if HAVE_VERSIONING
+- AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
++ pam_pwhistory_la_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
+ endif
+
+ noinst_HEADERS = opasswd.h
+
+ securelib_LTLIBRARIES = pam_pwhistory.la
+-pam_pwhistory_la_LIBADD = $(top_builddir)/libpam/libpam.la @LIBCRYPT@
++pam_pwhistory_la_CFLAGS = $(AM_CFLAGS)
++pam_pwhistory_la_LIBADD = $(top_builddir)/libpam/libpam.la @LIBCRYPT@ @LIBSELINUX@
+ pam_pwhistory_la_SOURCES = pam_pwhistory.c opasswd.c
+
++sbin_PROGRAMS = pwhistory_helper
++pwhistory_helper_CFLAGS = $(AM_CFLAGS) -DHELPER_COMPILE=\"pwhistory_helper\" @PIE_CFLAGS@
++pwhistory_helper_SOURCES = pwhistory_helper.c opasswd.c
++pwhistory_helper_LDFLAGS = -Wl,-z,now @PIE_LDFLAGS@
++pwhistory_helper_LDADD = $(top_builddir)/libpam/libpam.la @LIBCRYPT@
++
+ if ENABLE_REGENERATE_MAN
+ noinst_DATA = README
+ README: pam_pwhistory.8.xml
+diff --git a/modules/pam_pwhistory/opasswd.c b/modules/pam_pwhistory/opasswd.c
+index 836d713..e319ff3 100644
+--- a/modules/pam_pwhistory/opasswd.c
++++ b/modules/pam_pwhistory/opasswd.c
+@@ -1,5 +1,6 @@
+ /*
+ * Copyright (c) 2008 Thorsten Kukuk
++ * Copyright (c) 2013 Red Hat, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+@@ -38,6 +39,7 @@
+ #endif
+
+ #include
++#include
+ #include
+ #include
+ #include
+@@ -47,6 +49,7 @@
+ #include
+ #include
+ #include
++#include
+ #include
+
+ #if defined (HAVE_XCRYPT_H)
+@@ -55,7 +58,14 @@
+ #include
+ #endif
+
++#ifdef HELPER_COMPILE
++#define pam_modutil_getpwnam(h,n) getpwnam(n)
++#define pam_modutil_getspnam(h,n) getspnam(n)
++#define pam_syslog(h,a,...) helper_log_err(a,__VA_ARGS__)
++#else
++#include
+ #include
++#endif
+ #include
+
+ #include "opasswd.h"
+@@ -76,6 +86,19 @@ typedef struct {
+ char *old_passwords;
+ } opwd;
+
++#ifdef HELPER_COMPILE
++void
++helper_log_err(int err, const char *format, ...)
++{
++ va_list args;
++
++ va_start(args, format);
++ openlog(HELPER_COMPILE, LOG_CONS | LOG_PID, LOG_AUTHPRIV);
++ vsyslog(err, format, args);
++ va_end(args);
++ closelog();
++}
++#endif
+
+ static int
+ parse_entry (char *line, opwd *data)
+@@ -112,8 +135,8 @@ compare_password(const char *newpass, const char *oldpass)
+ }
+
+ /* Check, if the new password is already in the opasswd file. */
+-int
+-check_old_pass (pam_handle_t *pamh, const char *user,
++PAMH_ARG_DECL(int
++check_old_pass, const char *user,
+ const char *newpass, int debug)
+ {
+ int retval = PAM_SUCCESS;
+@@ -123,6 +146,11 @@ check_old_pass (pam_handle_t *pamh, const char *user,
+ opwd entry;
+ int found = 0;
+
++#ifndef HELPER_COMPILE
++ if (SELINUX_ENABLED)
++ return PAM_PWHISTORY_RUN_HELPER;
++#endif
++
+ if ((oldpf = fopen (OLD_PASSWORDS_FILE, "r")) == NULL)
+ {
+ if (errno != ENOENT)
+@@ -208,9 +236,9 @@ check_old_pass (pam_handle_t *pamh, const char *user,
+ return retval;
+ }
+
+-int
+-save_old_pass (pam_handle_t *pamh, const char *user, uid_t uid,
+- const char *oldpass, int howmany, int debug UNUSED)
++PAMH_ARG_DECL(int
++save_old_pass, const char *user,
++ int howmany, int debug UNUSED)
+ {
+ char opasswd_tmp[] = TMP_PASSWORDS_FILE;
+ struct stat opasswd_stat;
+@@ -221,10 +249,35 @@ save_old_pass (pam_handle_t *pamh, const char *user, uid_t uid,
+ char *buf = NULL;
+ size_t buflen = 0;
+ int found = 0;
++ struct passwd *pwd;
++ const char *oldpass;
++
++ pwd = pam_modutil_getpwnam (pamh, user);
++ if (pwd == NULL)
++ return PAM_USER_UNKNOWN;
+
+ if (howmany <= 0)
+ return PAM_SUCCESS;
+
++#ifndef HELPER_COMPILE
++ if (SELINUX_ENABLED)
++ return PAM_PWHISTORY_RUN_HELPER;
++#endif
++
++ 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;
++ oldpass = spw->sp_pwdp;
++ }
++ else
++ oldpass = pwd->pw_passwd;
++
+ if (oldpass == NULL || *oldpass == '\0')
+ return PAM_SUCCESS;
+
+@@ -447,7 +500,7 @@ save_old_pass (pam_handle_t *pamh, const char *user, uid_t uid,
+ {
+ char *out;
+
+- if (asprintf (&out, "%s:%d:1:%s\n", user, uid, oldpass) < 0)
++ if (asprintf (&out, "%s:%d:1:%s\n", user, pwd->pw_uid, oldpass) < 0)
+ {
+ retval = PAM_AUTHTOK_ERR;
+ if (oldpf)
+diff --git a/modules/pam_pwhistory/opasswd.h b/modules/pam_pwhistory/opasswd.h
+index db3e656..1b08699 100644
+--- a/modules/pam_pwhistory/opasswd.h
++++ b/modules/pam_pwhistory/opasswd.h
+@@ -1,5 +1,6 @@
+ /*
+ * Copyright (c) 2008 Thorsten Kukuk
++ * Copyright (c) 2013 Red Hat, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+@@ -36,10 +37,32 @@
+ #ifndef __OPASSWD_H__
+ #define __OPASSWD_H__
+
+-extern int check_old_pass (pam_handle_t *pamh, const char *user,
+- const char *newpass, int debug);
+-extern int save_old_pass (pam_handle_t *pamh, const char *user,
+- uid_t uid, const char *oldpass,
+- int howmany, int debug);
++#define PAM_PWHISTORY_RUN_HELPER PAM_CRED_INSUFFICIENT
++
++#ifdef WITH_SELINUX
++#include
++#define SELINUX_ENABLED is_selinux_enabled()>0
++#else
++#define SELINUX_ENABLED 0
++#endif
++
++#ifdef HELPER_COMPILE
++#define PAMH_ARG_DECL(fname, ...) fname(__VA_ARGS__)
++#define PAMH_ARG(...) __VA_ARGS__
++#else
++#define PAMH_ARG_DECL(fname, ...) fname(pam_handle_t *pamh, __VA_ARGS__)
++#define PAMH_ARG(...) pamh, __VA_ARGS__
++#endif
++
++#ifdef HELPER_COMPILE
++void
++helper_log_err(int err, const char *format, ...);
++#endif
++
++PAMH_ARG_DECL(int
++check_old_pass, const char *user, const char *newpass, int debug);
++
++PAMH_ARG_DECL(int
++save_old_pass, const char *user, int howmany, int debug);
+
+ #endif /* __OPASSWD_H__ */
+diff --git a/modules/pam_pwhistory/pam_pwhistory.c b/modules/pam_pwhistory/pam_pwhistory.c
+index 654edd3..d6c5c47 100644
+--- a/modules/pam_pwhistory/pam_pwhistory.c
++++ b/modules/pam_pwhistory/pam_pwhistory.c
+@@ -1,6 +1,7 @@
+ /*
+ * Copyright (c) 2008, 2012 Thorsten Kukuk
+ * Author: Thorsten Kukuk
++ * Copyright (c) 2013 Red Hat, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+@@ -46,10 +47,14 @@
+ #include
+ #include
+ #include
+-#include
+ #include
+ #include
+ #include
++#include
++#include
++#include
++#include
++#include
+
+ #include
+ #include
+@@ -59,6 +64,7 @@
+ #include "opasswd.h"
+
+ #define DEFAULT_BUFLEN 2048
++#define MAX_FD_NO 20000
+
+ struct options_t {
+ int debug;
+@@ -102,6 +108,184 @@ parse_option (pam_handle_t *pamh, const char *argv, options_t *options)
+ pam_syslog (pamh, LOG_ERR, "pam_pwhistory: unknown option: %s", argv);
+ }
+
++static int
++run_save_helper(pam_handle_t *pamh, const char *user,
++ int howmany, int debug)
++{
++ int retval, child;
++ struct sigaction newsa, oldsa;
++
++ memset(&newsa, '\0', sizeof(newsa));
++ newsa.sa_handler = SIG_DFL;
++ sigaction(SIGCHLD, &newsa, &oldsa);
++
++ child = fork();
++ if (child == 0)
++ {
++ int i = 0;
++ struct rlimit rlim;
++ int dummyfds[2];
++ static char *envp[] = { NULL };
++ char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL };
++
++ /* replace std file descriptors with a dummy pipe */
++ if (pipe2(dummyfds, O_NONBLOCK) == 0)
++ {
++ dup2(dummyfds[0], STDIN_FILENO);
++ dup2(dummyfds[1], STDOUT_FILENO);
++ dup2(dummyfds[1], STDERR_FILENO);
++ }
++
++ if (getrlimit(RLIMIT_NOFILE,&rlim) == 0)
++ {
++ if (rlim.rlim_max >= MAX_FD_NO)
++ rlim.rlim_max = MAX_FD_NO;
++ for (i = STDERR_FILENO + 1; i < (int)rlim.rlim_max; i++)
++ {
++ if (i != dummyfds[0])
++ close(i);
++ }
++ }
++
++ /* exec binary helper */
++ args[0] = strdup(PWHISTORY_HELPER);
++ args[1] = strdup("save");
++ args[2] = x_strdup(user);
++ asprintf(&args[3], "%d", howmany);
++ asprintf(&args[4], "%d", debug);
++
++ execve(args[0], args, envp);
++
++ _exit(PAM_SYSTEM_ERR);
++ }
++ else if (child > 0)
++ {
++ /* wait for child */
++ int rc = 0;
++ rc = waitpid(child, &retval, 0); /* wait for helper to complete */
++ if (rc < 0)
++ {
++ pam_syslog(pamh, LOG_ERR, "pwhistory_helper save waitpid returned %d: %m", rc);
++ retval = PAM_SYSTEM_ERR;
++ }
++ else if (!WIFEXITED(retval))
++ {
++ pam_syslog(pamh, LOG_ERR, "pwhistory_helper save abnormal exit: %d", retval);
++ retval = PAM_SYSTEM_ERR;
++ }
++ else
++ {
++ retval = WEXITSTATUS(retval);
++ }
++ }
++ else
++ {
++ retval = PAM_SYSTEM_ERR;
++ }
++
++ sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */
++
++ return retval;
++}
++
++static int
++run_check_helper(pam_handle_t *pamh, const char *user,
++ const char *newpass, int debug)
++{
++ int retval, child, fds[2];
++ struct sigaction newsa, oldsa;
++
++ /* create a pipe for the password */
++ if (pipe(fds) != 0)
++ return PAM_SYSTEM_ERR;
++
++ memset(&newsa, '\0', sizeof(newsa));
++ newsa.sa_handler = SIG_DFL;
++ sigaction(SIGCHLD, &newsa, &oldsa);
++
++ child = fork();
++ if (child == 0)
++ {
++ int i = 0;
++ struct rlimit rlim;
++ int dummyfds[2];
++ static char *envp[] = { NULL };
++ char *args[] = { NULL, NULL, NULL, NULL, NULL };
++
++ /* reopen stdin as pipe */
++ dup2(fds[0], STDIN_FILENO);
++
++ /* replace std file descriptors with a dummy pipe */
++ if (pipe2(dummyfds, O_NONBLOCK) == 0)
++ {
++ dup2(dummyfds[1], STDOUT_FILENO);
++ dup2(dummyfds[1], STDERR_FILENO);
++ }
++
++ if (getrlimit(RLIMIT_NOFILE,&rlim) == 0)
++ {
++ if (rlim.rlim_max >= MAX_FD_NO)
++ rlim.rlim_max = MAX_FD_NO;
++ for (i = STDERR_FILENO + 1; i < (int)rlim.rlim_max; i++)
++ {
++ if (i != dummyfds[0])
++ close(i);
++ }
++ }
++
++ /* exec binary helper */
++ args[0] = strdup(PWHISTORY_HELPER);
++ args[1] = strdup("check");
++ args[2] = x_strdup(user);
++ asprintf(&args[3], "%d", debug);
++
++ execve(args[0], args, envp);
++
++ _exit(PAM_SYSTEM_ERR);
++ }
++ else if (child > 0)
++ {
++ /* wait for child */
++ int rc = 0;
++ if (newpass == NULL)
++ newpass = "";
++
++ /* send the password to the child */
++ if (write(fds[1], newpass, strlen(newpass)+1) == -1)
++ {
++ pam_syslog(pamh, LOG_ERR, "Cannot send password to helper: %m");
++ retval = PAM_SYSTEM_ERR;
++ }
++ newpass = NULL;
++ close(fds[0]); /* close here to avoid possible SIGPIPE above */
++ close(fds[1]);
++ rc = waitpid(child, &retval, 0); /* wait for helper to complete */
++ if (rc < 0)
++ {
++ pam_syslog(pamh, LOG_ERR, "pwhistory_helper check waitpid returned %d: %m", rc);
++ retval = PAM_SYSTEM_ERR;
++ }
++ else if (!WIFEXITED(retval))
++ {
++ pam_syslog(pamh, LOG_ERR, "pwhistory_helper check abnormal exit: %d", retval);
++ retval = PAM_SYSTEM_ERR;
++ }
++ else
++ {
++ retval = WEXITSTATUS(retval);
++ }
++ }
++ else
++ {
++ close(fds[0]);
++ close(fds[1]);
++ retval = PAM_SYSTEM_ERR;
++ }
++
++ sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */
++
++ return retval;
++}
+
+ /* This module saves the current crypted password in /etc/security/opasswd
+ and then compares the new password with all entries in this file. */
+@@ -109,7 +293,6 @@ parse_option (pam_handle_t *pamh, const char *argv, options_t *options)
+ 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;
+@@ -154,31 +337,13 @@ pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv)
+ return PAM_SUCCESS;
+ }
+
+- pwd = pam_modutil_getpwnam (pamh, user);
+- if (pwd == NULL)
+- return PAM_USER_UNKNOWN;
++ retval = save_old_pass (pamh, user, options.remember, options.debug);
+
+- 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;
++ if (retval == PAM_PWHISTORY_RUN_HELPER)
++ retval = run_save_helper(pamh, user, options.remember, options.debug);
+
+- retval = save_old_pass (pamh, user, pwd->pw_uid, spw->sp_pwdp,
+- options.remember, options.debug);
+- if (retval != PAM_SUCCESS)
+- return retval;
+- }
+- else
+- {
+- retval = save_old_pass (pamh, user, pwd->pw_uid, pwd->pw_passwd,
+- options.remember, options.debug);
+- if (retval != PAM_SUCCESS)
+- return retval;
+- }
++ if (retval != PAM_SUCCESS)
++ return retval;
+
+ newpass = NULL;
+ tries = 0;
+@@ -207,8 +372,11 @@ pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv)
+ if (options.debug)
+ pam_syslog (pamh, LOG_DEBUG, "check against old password file");
+
+- if (check_old_pass (pamh, user, newpass,
+- options.debug) != PAM_SUCCESS)
++ retval = check_old_pass (pamh, user, newpass, options.debug);
++ if (retval == PAM_PWHISTORY_RUN_HELPER)
++ retval = run_check_helper(pamh, user, newpass, options.debug);
++
++ if (retval != PAM_SUCCESS)
+ {
+ if (getuid() || options.enforce_for_root ||
+ (flags & PAM_CHANGE_EXPIRED_AUTHTOK))
+diff --git a/modules/pam_pwhistory/pwhistory_helper.8.xml b/modules/pam_pwhistory/pwhistory_helper.8.xml
+new file mode 100644
+index 0000000..a030176
+--- /dev/null
++++ b/modules/pam_pwhistory/pwhistory_helper.8.xml
+@@ -0,0 +1,68 @@
++
++
++
++
++
++
++ pwhistory_helper
++ 8
++ Linux-PAM Manual
++
++
++
++ pwhistory_helper
++ Helper binary that transfers password hashes from passwd or shadow to opasswd
++
++
++
++
++ pwhistory_helper
++
++ ...
++
++
++
++
++
++
++ DESCRIPTION
++
++
++ pwhistory_helper is a helper program for the
++ pam_pwhistory module that transfers password hashes
++ from passwd or shadow file to the opasswd file and checks a password
++ supplied by user against the existing hashes in the opasswd file.
++
++
++
++ The purpose of the helper is to enable tighter confinement of
++ login and password changing services. The helper is thus called only
++ when SELinux is enabled on the system.
++
++
++
++ The interface of the helper - command line options, and input/output
++ data format are internal to the pam_pwhistory
++ module and it should not be called directly from applications.
++
++
++
++
++ SEE ALSO
++
++
++ pam_pwhistory8
++
++
++
++
++
++ AUTHOR
++
++ Written by Tomas Mraz based on the code originally in
++ pam_pwhistory and pam_unix modules.
++
++
++
++
+diff --git a/modules/pam_pwhistory/pwhistory_helper.c b/modules/pam_pwhistory/pwhistory_helper.c
+new file mode 100644
+index 0000000..b07ab81
+--- /dev/null
++++ b/modules/pam_pwhistory/pwhistory_helper.c
+@@ -0,0 +1,209 @@
++/*
++ * Copyright (c) 2013 Red Hat, Inc.
++ * Author: Tomas Mraz
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, and the entire permission notice in its entirety,
++ * including the disclaimer of warranties.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. The name of the author may not be used to endorse or promote
++ * products derived from this software without specific prior
++ * written permission.
++ *
++ * ALTERNATIVELY, this product may be distributed under the terms of
++ * the GNU Public License, in which case the provisions of the GPL are
++ * required INSTEAD OF the above restrictions. (This clause is
++ * necessary due to a potential bad interaction between the GPL and
++ * the restrictions contained in a BSD-style copyright.)
++ *
++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
++ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
++ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
++ * OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include "config.h"
++
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include "opasswd.h"
++
++#define MAXPASS 200
++
++static void
++su_sighandler(int sig)
++{
++#ifndef SA_RESETHAND
++ /* emulate the behaviour of the SA_RESETHAND flag */
++ if ( sig == SIGILL || sig == SIGTRAP || sig == SIGBUS || sig = SIGSERV ) {
++ struct sigaction sa;
++ memset(&sa, '\0', sizeof(sa));
++ sa.sa_handler = SIG_DFL;
++ sigaction(sig, &sa, NULL);
++ }
++#endif
++ if (sig > 0) {
++ _exit(sig);
++ }
++}
++
++static void
++setup_signals(void)
++{
++ struct sigaction action; /* posix signal structure */
++
++ /*
++ * Setup signal handlers
++ */
++ (void) memset((void *) &action, 0, sizeof(action));
++ action.sa_handler = su_sighandler;
++#ifdef SA_RESETHAND
++ action.sa_flags = SA_RESETHAND;
++#endif
++ (void) sigaction(SIGILL, &action, NULL);
++ (void) sigaction(SIGTRAP, &action, NULL);
++ (void) sigaction(SIGBUS, &action, NULL);
++ (void) sigaction(SIGSEGV, &action, NULL);
++ action.sa_handler = SIG_IGN;
++ action.sa_flags = 0;
++ (void) sigaction(SIGTERM, &action, NULL);
++ (void) sigaction(SIGHUP, &action, NULL);
++ (void) sigaction(SIGINT, &action, NULL);
++ (void) sigaction(SIGQUIT, &action, NULL);
++}
++
++static int
++read_passwords(int fd, int npass, char **passwords)
++{
++ int rbytes = 0;
++ int offset = 0;
++ int i = 0;
++ char *pptr;
++ while (npass > 0)
++ {
++ rbytes = read(fd, passwords[i]+offset, MAXPASS-offset);
++
++ if (rbytes < 0)
++ {
++ if (errno == EINTR) continue;
++ break;
++ }
++ if (rbytes == 0)
++ break;
++
++ while (npass > 0 && (pptr=memchr(passwords[i]+offset, '\0', rbytes))
++ != NULL)
++ {
++ rbytes -= pptr - (passwords[i]+offset) + 1;
++ i++;
++ offset = 0;
++ npass--;
++ if (rbytes > 0)
++ {
++ if (npass > 0)
++ memcpy(passwords[i], pptr+1, rbytes);
++ memset(pptr+1, '\0', rbytes);
++ }
++ }
++ offset += rbytes;
++ }
++
++ /* clear up */
++ if (offset > 0 && npass > 0)
++ memset(passwords[i], '\0', offset);
++
++ return i;
++}
++
++
++static int
++check_history(const char *user, const char *debug)
++{
++ char pass[MAXPASS + 1];
++ char *passwords[] = { pass };
++ int npass;
++ int dbg = atoi(debug); /* no need to be too fancy here */
++ int retval;
++
++ /* read the password from stdin (a pipe from the pam_pwhistory module) */
++ npass = read_passwords(STDIN_FILENO, 1, passwords);
++
++ if (npass != 1)
++ { /* is it a valid password? */
++ helper_log_err(LOG_DEBUG, "no password supplied");
++ return PAM_AUTHTOK_ERR;
++ }
++
++ retval = check_old_pass(user, pass, dbg);
++
++ memset(pass, '\0', MAXPASS); /* clear memory of the password */
++
++ return retval;
++}
++
++static int
++save_history(const char *user, const char *howmany, const char *debug)
++{
++ int num = atoi(howmany);
++ int dbg = atoi(debug); /* no need to be too fancy here */
++ int retval;
++
++ retval = save_old_pass(user, num, dbg);
++
++ return retval;
++}
++
++int
++main(int argc, char *argv[])
++{
++ const char *option;
++ const char *user;
++
++ /*
++ * Catch or ignore as many signal as possible.
++ */
++ setup_signals();
++
++ /*
++ * we establish that this program is running with non-tty stdin.
++ * this is to discourage casual use.
++ */
++
++ if (isatty(STDIN_FILENO) || argc < 4)
++ {
++ fprintf(stderr,
++ "This binary is not designed for running in this way.\n");
++ sleep(10); /* this should discourage/annoy the user */
++ return PAM_SYSTEM_ERR;
++ }
++
++ option = argv[1];
++ user = argv[2];
++
++ if (strcmp(option, "check") == 0 && argc == 4)
++ return check_history(user, argv[3]);
++ else if (strcmp(option, "save") == 0 && argc == 5)
++ return save_history(user, argv[3], argv[4]);
++
++ return PAM_SYSTEM_ERR;
++}
++
diff --git a/pam-CVE-2013-7041.patch b/pam-CVE-2013-7041.patch
index 61f86b2..96fa916 100644
--- a/pam-CVE-2013-7041.patch
+++ b/pam-CVE-2013-7041.patch
@@ -1,24 +1,37 @@
-diff -pruN a/modules/pam_userdb/pam_userdb.c b/modules/pam_userdb/pam_userdb.c
---- a/modules/pam_userdb/pam_userdb.c 2011-06-21 16:04:56.000000000 +0700
-+++ b/modules/pam_userdb/pam_userdb.c 2014-08-28 17:41:35.243954732 +0700
-@@ -214,24 +214,23 @@ user_lookup (pam_handle_t *pamh, const c
- /* crypt(3) password storage */
-
- char *cryptpw;
-- char salt[2];
-
-- if (data.dsize != 13) {
-+ if (data.dsize < 13) {
- compare = -2;
- } else if (ctrl & PAM_ICASE_ARG) {
- compare = -2;
+From 57a1e2b274d0a6376d92ada9926e5c5741e7da20 Mon Sep 17 00:00:00 2001
+From: "Dmitry V. Levin"
+Date: Fri, 24 Jan 2014 22:18:32 +0000
+Subject: [PATCH] pam_userdb: fix password hash comparison
+
+Starting with commit Linux-PAM-0-77-28-g0b3e583 that introduced hashed
+passwords support in pam_userdb, hashes are compared case-insensitively.
+This bug leads to accepting hashes for completely different passwords in
+addition to those that should be accepted.
+
+Additionally, commit Linux-PAM-1_1_6-13-ge2a8187 that added support for
+modern password hashes with different lengths and settings, did not
+update the hash comparison accordingly, which leads to accepting
+computed hashes longer than stored hashes when the latter is a prefix
+of the former.
+
+* modules/pam_userdb/pam_userdb.c (user_lookup): Reject the computed
+hash whose length differs from the stored hash length.
+Compare computed and stored hashes case-sensitively.
+Fixes CVE-2013-7041.
+
+Bug-Debian: http://bugs.debian.org/731368
+---
+ modules/pam_userdb/pam_userdb.c | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/modules/pam_userdb/pam_userdb.c b/modules/pam_userdb/pam_userdb.c
+index de8b5b1..ff040e6 100644
+--- a/modules/pam_userdb/pam_userdb.c
++++ b/modules/pam_userdb/pam_userdb.c
+@@ -222,12 +222,15 @@ user_lookup (pam_handle_t *pamh, const char *database, const char *cryptmode,
} else {
-- salt[0] = *data.dptr;
-- salt[1] = *(data.dptr + 1);
-+ cryptpw = crypt (pass, data.dptr);
+ cryptpw = crypt (pass, data.dptr);
-- cryptpw = crypt (pass, salt);
--
- if (cryptpw) {
- compare = strncasecmp (data.dptr, cryptpw, data.dsize);
+ if (cryptpw && strlen(cryptpw) == (size_t)data.dsize) {
@@ -28,9 +41,12 @@ diff -pruN a/modules/pam_userdb/pam_userdb.c b/modules/pam_userdb/pam_userdb.c
if (ctrl & PAM_DEBUG_ARG) {
- pam_syslog(pamh, LOG_INFO, "crypt() returned NULL");
+ if (cryptpw)
-+ pam_syslog(pamh, LOG_INFO, "lengths of computed and stored hashes differ");
++ pam_syslog(pamh, LOG_INFO, "lengths of computed and stored hashes differ");
+ else
-+ pam_syslog(pamh, LOG_INFO, "crypt() returned NULL");
++ pam_syslog(pamh, LOG_INFO, "crypt() returned NULL");
}
};
+--
+1.8.3.1
+
diff --git a/pam-CVE-2014-2583.patch b/pam-CVE-2014-2583.patch
index a5c3221..f2aa2de 100644
--- a/pam-CVE-2014-2583.patch
+++ b/pam-CVE-2014-2583.patch
@@ -1,3 +1,24 @@
+From 9dcead87e6d7f66d34e7a56d11a30daca367dffb Mon Sep 17 00:00:00 2001
+From: "Dmitry V. Levin"
+Date: Wed, 26 Mar 2014 22:17:23 +0000
+Subject: [PATCH] pam_timestamp: fix potential directory traversal issue
+ (ticket #27)
+
+pam_timestamp uses values of PAM_RUSER and PAM_TTY as components of
+the timestamp pathname it creates, so extra care should be taken to
+avoid potential directory traversal issues.
+
+* modules/pam_timestamp/pam_timestamp.c (check_tty): Treat
+"." and ".." tty values as invalid.
+(get_ruser): Treat "." and ".." ruser values, as well as any ruser
+value containing '/', as invalid.
+
+Fixes CVE-2014-2583.
+
+Reported-by: Sebastian Krahmer
+---
+ modules/pam_timestamp/pam_timestamp.c | 13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/modules/pam_timestamp/pam_timestamp.c b/modules/pam_timestamp/pam_timestamp.c
index 5193733..b3f08b1 100644
@@ -30,4 +51,6 @@ index 5193733..b3f08b1 100644
}
if (ruser == NULL || strlen(ruser) >= ruserbuflen) {
*ruserbuf = '\0';
+--
+1.8.3.1
diff --git a/pam.spec b/pam.spec
index 32fd4d8..ff850a0 100644
--- a/pam.spec
+++ b/pam.spec
@@ -19,7 +19,7 @@ Epoch: 1
Summary: A security tool which provides authentication for applications
Name: pam
Version: 1.1.8
-Release: 3
+Release: 22
# The library is BSD licensed with option to relicense as GPLv2+ - this option is redundant
# as the BSD license allows that anyway. pam_timestamp and pam_console modules are GPLv2+,
License: BSD and GPLv2+
@@ -51,8 +51,20 @@ Patch9: pam-1.1.6-noflex.patch
Patch10: pam-1.1.3-nouserenv.patch
Patch11: pam-1.1.3-console-abstract.patch
Patch13: pam-1.1.5-limits-user.patch
+Patch14: pam-1.1.1-faillock.patch
Patch22: pam-1.1.7-unix-build.patch
Patch32: pam-1.1.7-tty-audit-init.patch
+Patch33: pam-1.1.8-audit-grantor.patch
+Patch34: pam-1.1.8-audit-user-mgmt.patch
+Patch35: pam-1.1.8-canonicalize-username.patch
+Patch36: pam-1.1.8-full-relro.patch
+Patch37: pam-1.1.8-lastlog-uninitialized.patch
+Patch38: pam-1.1.8-limits-check-process.patch
+Patch39: pam-1.1.8-limits-docfix.patch
+Patch40: pam-1.1.8-loginuid-container.patch
+Patch41: pam-1.1.8-man-dbsuffix.patch
+Patch42: pam-1.1.8-opasswd-tolerant.patch
+Patch43: pam-1.1.8-pwhistory-helper.patch
# ROSA specific sources/patches
# (fl) fix infinite loop
@@ -64,8 +76,6 @@ Patch508: Linux-PAM-0.99.3.0-pamtimestampadm.patch
##Patch512: Linux-PAM-1.1.1-xauth-groups.patch
# (tv/blino) add defaults for nice/rtprio in /etc/security/limits.conf
Patch517: Linux-PAM-0.99.3.0-enable_rt.patch
-# (blino) fix parallel build (pam_console)
-Patch521: Linux-PAM-0.99.3.0-pbuild-rh.patch
Patch700: pam_fix_static_pam_console.patch
# (fc) do not output error when no file is in /etc/security/console.perms.d/
@@ -75,8 +85,10 @@ Patch702: Linux-PAM-1.1.4-add-now-missing-nis-constant.patch
# (akdengi> add user to default group users which need for Samba
Patch801: Linux-PAM-1.1.4-group_add_users.patch
-#Patch802: pam-CVE-2014-2583.patch
-#Patch803: pam-CVE-2013-7041.patch
+Patch802: pam-CVE-2014-2583.patch
+Patch803: pam-CVE-2013-7041.patch
+
+Patch804: pam-1.1.8-pbuild.patch
BuildRequires: selinux-devel >= 2.1.6-7
BuildRequires: bison
@@ -169,23 +181,23 @@ mv pam-redhat-%{pam_redhat_version}/* modules
install -m644 %{SOURCE501} %{SOURCE502} modules/pam_tty_audit/
-mkdir -p doc/txts
-for readme in modules/pam_*/README ; do
- cp -f ${readme} doc/txts/README.`dirname ${readme} | sed -e 's|^modules/||'`
-done
+#mkdir -p doc/txts
+#for readme in modules/pam_*/README ; do
+# cp -f ${readme} doc/txts/README.`dirname ${readme} | sed -e 's|^modules/||'`
+#done
#libtoolize -cf
-autoreconf -ifs -I m4
+autoreconf -i
%build
-export BROWSER=""
-CFLAGS="$RPM_OPT_FLAGS -fPIC -I%{_includedir}/db_nss -D_GNU_SOURCE" \
%configure2_5x \
--sbindir=/sbin \
--libdir=/%{_lib} \
--includedir=%{_includedir}/security \
--with-db-uniquename=_nss \
--docdir=%{_docdir}/%{name} \
+ --disable-static \
+ --disable-prelude \
--enable-selinux \
--enable-audit
%make
@@ -229,16 +241,11 @@ cp -f %{buildroot}/etc/pam.d/system-auth %{buildroot}/etc/pam.d/system-auth-defa
%find_lang Linux-PAM
%check
-# (blino) we don't want to test if SE Linux is built, it's disabled
-# Make sure every module subdirectory gave us a module. Yes, this is hackish.
for dir in modules/pam_* ; do
-#if [ -d ${dir} ] && [[ "${dir}" != "modules/pam_selinux" ]] && [[ "${dir}" != "modules/pam_sepermit" ]]; then
-# [[ "${dir}" = "modules/pam_tally" ]] && continue
if ! ls -1 %{buildroot}/%{_lib}/security/`basename ${dir}`*.so ; then
echo ERROR `basename ${dir}` did not build a module.
exit 1
fi
-#fi
done
# Check for module problems. Specifically, check that every module we just
@@ -262,9 +269,6 @@ fi
if [ ! -a /var/log/tallylog ] ; then
install -m 600 /dev/null /var/log/tallylog
fi
-#if [ -f /etc/login.defs ] && ! grep -q USE_TCB /etc/login.defs; then
-# /usr/sbin/set_tcb --auto --migrate
-#fi
%files -f Linux-PAM.lang
%doc NEWS