From 2920bf9f9dcfb488bcab62e6ff6766d9df5ff5ea Mon Sep 17 00:00:00 2001 From: Dmitry Fedorov Date: Wed, 21 Jan 2015 14:01:39 +0300 Subject: [PATCH] adopt pbuild patch --- Linux-PAM-0.99.3.0-pbuild-rh.patch | 11 - pam-1.1.1-faillock.patch | 1730 +++++++++++++++++++++++++ pam-1.1.8-audit-grantor.patch | 435 +++++++ pam-1.1.8-audit-user-mgmt.patch | 53 + pam-1.1.8-canonicalize-username.patch | 21 + pam-1.1.8-full-relro.patch | 108 ++ pam-1.1.8-lastlog-uninitialized.patch | 37 + pam-1.1.8-limits-check-process.patch | 41 + pam-1.1.8-limits-docfix.patch | 54 + pam-1.1.8-loginuid-container.patch | 151 +++ pam-1.1.8-man-dbsuffix.patch | 22 + pam-1.1.8-opasswd-tolerant.patch | 50 + pam-1.1.8-pbuild.patch | 11 + pam-1.1.8-pwhistory-helper.patch | 813 ++++++++++++ pam-CVE-2013-7041.patch | 58 +- pam-CVE-2014-2583.patch | 23 + pam.spec | 44 +- 17 files changed, 3610 insertions(+), 52 deletions(-) delete mode 100644 Linux-PAM-0.99.3.0-pbuild-rh.patch create mode 100644 pam-1.1.1-faillock.patch create mode 100644 pam-1.1.8-audit-grantor.patch create mode 100644 pam-1.1.8-audit-user-mgmt.patch create mode 100644 pam-1.1.8-canonicalize-username.patch create mode 100644 pam-1.1.8-full-relro.patch create mode 100644 pam-1.1.8-lastlog-uninitialized.patch create mode 100644 pam-1.1.8-limits-check-process.patch create mode 100644 pam-1.1.8-limits-docfix.patch create mode 100644 pam-1.1.8-loginuid-container.patch create mode 100644 pam-1.1.8-man-dbsuffix.patch create mode 100644 pam-1.1.8-opasswd-tolerant.patch create mode 100644 pam-1.1.8-pbuild.patch create mode 100644 pam-1.1.8-pwhistory-helper.patch 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 @@ ++ ++ ++--> ++]> ++ ++
++ ++ ++ ++ ++ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" ++ href="pam_faillock.8.xml" xpointer='xpointer(//refnamediv[@id = "pam_faillock-name"]/*)'/> ++ ++ ++ ++ ++
++ ++
++ ++
++ ++
++ ++
++ ++
++ ++
++ ++
++ ++
++ ++
++ ++
+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