a46dbe
diff -up Linux-PAM-1.3.1/modules/pam_faillock/faillock.c.faillock-update Linux-PAM-1.3.1/modules/pam_faillock/faillock.c
a46dbe
--- Linux-PAM-1.3.1/modules/pam_faillock/faillock.c.faillock-update	2019-10-16 16:49:27.026893768 +0200
a46dbe
+++ Linux-PAM-1.3.1/modules/pam_faillock/faillock.c	2019-12-16 17:55:27.042001068 +0100
a46dbe
@@ -1,5 +1,6 @@
a46dbe
 /*
a46dbe
  * Copyright (c) 2010 Tomas Mraz <tmraz@redhat.com>
a46dbe
+ * Copyright (c) 2010, 2016, 2017 Red Hat, Inc.
a46dbe
  *
a46dbe
  * Redistribution and use in source and binary forms, with or without
a46dbe
  * modification, are permitted provided that the following conditions
a46dbe
@@ -47,6 +48,8 @@
a46dbe
 
a46dbe
 #include "faillock.h"
a46dbe
 
a46dbe
+#define ignore_return(x) if (1==((int)x)) {;}
a46dbe
+
a46dbe
 int
a46dbe
 open_tally (const char *dir, const char *user, uid_t uid, int create)
a46dbe
 {
a46dbe
@@ -54,7 +57,7 @@ open_tally (const char *dir, const char
a46dbe
 	int flags = O_RDWR;
a46dbe
 	int fd;
a46dbe
 
a46dbe
-	if (strstr(user, "../") != NULL)
a46dbe
+	if (dir == NULL || strstr(user, "../") != NULL)
a46dbe
 	/* just a defensive programming as the user must be a
a46dbe
 	 * valid user on the system anyway
a46dbe
 	 */
a46dbe
@@ -83,7 +86,7 @@ open_tally (const char *dir, const char
a46dbe
 		while (flock(fd, LOCK_EX) == -1 && errno == EINTR);
a46dbe
 		if (fstat(fd, &st) == 0) {
a46dbe
 			if (st.st_uid != uid) {
a46dbe
-				fchown(fd, uid, -1);
a46dbe
+				ignore_return(fchown(fd, uid, -1));
a46dbe
 			}
a46dbe
 		}
a46dbe
 	}
a46dbe
@@ -121,7 +124,7 @@ read_tally(int fd, struct tally_data *ta
a46dbe
 		if (count >= MAX_RECORDS)
a46dbe
 			break;
a46dbe
 	}
a46dbe
-	while (chunk == CHUNK_SIZE); 
a46dbe
+	while (chunk == CHUNK_SIZE);
a46dbe
 
a46dbe
 	tallies->records = data;
a46dbe
 	tallies->count = count;
a46dbe
diff -up Linux-PAM-1.3.1/modules/pam_faillock/faillock.conf.5.xml.faillock-update Linux-PAM-1.3.1/modules/pam_faillock/faillock.conf.5.xml
a46dbe
--- Linux-PAM-1.3.1/modules/pam_faillock/faillock.conf.5.xml.faillock-update	2019-10-16 17:34:58.073276020 +0200
a46dbe
+++ Linux-PAM-1.3.1/modules/pam_faillock/faillock.conf.5.xml	2019-10-16 16:26:05.000000000 +0200
a46dbe
@@ -0,0 +1,243 @@
a46dbe
+
a46dbe
+
a46dbe
+	"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
a46dbe
+
a46dbe
+<refentry id="faillock.conf">
a46dbe
+
a46dbe
+  <refmeta>
a46dbe
+    <refentrytitle>faillock.conf</refentrytitle>
a46dbe
+    <manvolnum>5</manvolnum>
a46dbe
+    <refmiscinfo class="sectdesc">Linux-PAM Manual</refmiscinfo>
a46dbe
+  </refmeta>
a46dbe
+
a46dbe
+  <refnamediv id="faillock.conf-name">
a46dbe
+    <refname>faillock.conf</refname>
a46dbe
+    <refpurpose>pam_faillock configuration file</refpurpose>
a46dbe
+  </refnamediv>
a46dbe
+
a46dbe
+  <refsect1 id="faillock.conf-description">
a46dbe
+
a46dbe
+    <title>DESCRIPTION</title>
a46dbe
+    <para>
a46dbe
+       <emphasis remap='B'>faillock.conf</emphasis> provides a way to configure the
a46dbe
+       default settings for locking the user after multiple failed authentication attempts.
a46dbe
+       This file is read by the <emphasis>pam_faillock</emphasis> module and is the
a46dbe
+       preferred method over configuring <emphasis>pam_faillock</emphasis> directly.
a46dbe
+    </para>
a46dbe
+    <para>
a46dbe
+       The file has a very simple <emphasis>name = value</emphasis> format with possible comments
a46dbe
+       starting with <emphasis>#</emphasis> character. The whitespace at the beginning of line, end
a46dbe
+       of line, and around the <emphasis>=</emphasis> sign is ignored.
a46dbe
+    </para>
a46dbe
+  </refsect1>
a46dbe
+
a46dbe
+  <refsect1 id="faillock.conf-options">
a46dbe
+
a46dbe
+    <title>OPTIONS</title>
a46dbe
+         <variablelist>
a46dbe
+            <varlistentry>
a46dbe
+              <term>
a46dbe
+                <option>dir=<replaceable>/path/to/tally-directory</replaceable></option>
a46dbe
+              </term>
a46dbe
+              <listitem>
a46dbe
+                <para>
a46dbe
+                  The directory where the user files with the failure records are kept. The
a46dbe
+                  default is <filename>/var/run/faillock</filename>.
a46dbe
+                </para>
a46dbe
+              </listitem>
a46dbe
+            </varlistentry>
a46dbe
+            <varlistentry>
a46dbe
+              <term>
a46dbe
+                <option>audit</option>
a46dbe
+              </term>
a46dbe
+              <listitem>
a46dbe
+                <para>
a46dbe
+                  Will log the user name into the system log if the user is not found.
a46dbe
+                </para>
a46dbe
+              </listitem>
a46dbe
+            </varlistentry>
a46dbe
+            <varlistentry>
a46dbe
+              <term>
a46dbe
+                <option>silent</option>
a46dbe
+              </term>
a46dbe
+              <listitem>
a46dbe
+                <para>
a46dbe
+                  Don't print informative messages to the user. Please note that when
a46dbe
+                  this option is not used there will be difference in the authentication
a46dbe
+                  behavior for users which exist on the system and non-existing users.
a46dbe
+                </para>
a46dbe
+              </listitem>
a46dbe
+            </varlistentry>
a46dbe
+            <varlistentry>
a46dbe
+              <term>
a46dbe
+                <option>no_log_info</option>
a46dbe
+              </term>
a46dbe
+              <listitem>
a46dbe
+                <para>
a46dbe
+                  Don't log informative messages via <citerefentry><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
a46dbe
+                </para>
a46dbe
+              </listitem>
a46dbe
+            </varlistentry>
a46dbe
+            <varlistentry>
a46dbe
+              <term>
a46dbe
+                <option>local_users_only</option>
a46dbe
+              </term>
a46dbe
+              <listitem>
a46dbe
+                <para>
a46dbe
+                  Only track failed user authentications attempts for local users
a46dbe
+                  in /etc/passwd and ignore centralized (AD, IdM, LDAP, etc.) users.
a46dbe
+                  The <citerefentry><refentrytitle>faillock</refentrytitle><manvolnum>8</manvolnum></citerefentry>
a46dbe
+                  command will also no longer track user failed
a46dbe
+                  authentication attempts. Enabling this option will prevent a
a46dbe
+                  double-lockout scenario where a user is locked out locally and
a46dbe
+                  in the centralized mechanism.
a46dbe
+                </para>
a46dbe
+              </listitem>
a46dbe
+            </varlistentry>
a46dbe
+            <varlistentry>
a46dbe
+              <term>
a46dbe
+                <option>deny=<replaceable>n</replaceable></option>
a46dbe
+              </term>
a46dbe
+              <listitem>
a46dbe
+                <para>
a46dbe
+                  Deny access if the number of consecutive authentication failures
a46dbe
+                  for this user during the recent interval exceeds
a46dbe
+                  <replaceable>n</replaceable>. The default is 3.
a46dbe
+                </para>
a46dbe
+              </listitem>
a46dbe
+            </varlistentry>
a46dbe
+            <varlistentry>
a46dbe
+              <term>
a46dbe
+                <option>fail_interval=<replaceable>n</replaceable></option>
a46dbe
+              </term>
a46dbe
+              <listitem>
a46dbe
+                <para>
a46dbe
+                  The length of the interval during which the consecutive
a46dbe
+                  authentication failures must happen for the user account
a46dbe
+                  lock out is <replaceable>n</replaceable> seconds.
a46dbe
+                  The default is 900 (15 minutes).
a46dbe
+                </para>
a46dbe
+              </listitem>
a46dbe
+            </varlistentry>
a46dbe
+            <varlistentry>
a46dbe
+              <term>
a46dbe
+                <option>unlock_time=<replaceable>n</replaceable></option>
a46dbe
+              </term>
a46dbe
+              <listitem>
a46dbe
+                <para>
a46dbe
+                  The access will be reenabled after
a46dbe
+                  <replaceable>n</replaceable> seconds after the lock out.
a46dbe
+                  The value 0 has the same meaning as value
a46dbe
+                  <emphasis>never</emphasis> - the access
a46dbe
+                  will not be reenabled without resetting the faillock
a46dbe
+                  entries by the <citerefentry><refentrytitle>faillock</refentrytitle><manvolnum>8</manvolnum></citerefentry> command.
a46dbe
+                  The default is 600 (10 minutes).
a46dbe
+                </para>
a46dbe
+                <para>
a46dbe
+                  Note that the default directory that <emphasis>pam_faillock</emphasis>
a46dbe
+                  uses is usually cleared on system boot so the access will be also reenabled
a46dbe
+                  after system reboot. If that is undesirable a different tally directory
a46dbe
+                  must be set with the <option>dir</option> option.
a46dbe
+                </para>
a46dbe
+                <para>
a46dbe
+                  Also note that it is usually undesirable to permanently lock
a46dbe
+                  out the users as they can become easily a target of denial of service
a46dbe
+                  attack unless the usernames are random and kept secret to potential
a46dbe
+                  attackers.
a46dbe
+                </para>
a46dbe
+              </listitem>
a46dbe
+            </varlistentry>
a46dbe
+            <varlistentry>
a46dbe
+              <term>
a46dbe
+                <option>even_deny_root</option>
a46dbe
+              </term>
a46dbe
+              <listitem>
a46dbe
+                <para>
a46dbe
+                  Root account can become locked as well as regular accounts.
a46dbe
+                </para>
a46dbe
+              </listitem>
a46dbe
+            </varlistentry>
a46dbe
+            <varlistentry>
a46dbe
+              <term>
a46dbe
+                <option>root_unlock_time=<replaceable>n</replaceable></option>
a46dbe
+              </term>
a46dbe
+              <listitem>
a46dbe
+                <para>
a46dbe
+                  This option implies <option>even_deny_root</option> option.
a46dbe
+                  Allow access after <replaceable>n</replaceable> seconds
a46dbe
+                  to root account after the account is locked. In case the
a46dbe
+                  option is not specified the value is the same as of the
a46dbe
+                  <option>unlock_time</option> option.
a46dbe
+                </para>
a46dbe
+              </listitem>
a46dbe
+            </varlistentry>
a46dbe
+            <varlistentry>
a46dbe
+              <term>
a46dbe
+                <option>admin_group=<replaceable>name</replaceable></option>
a46dbe
+              </term>
a46dbe
+              <listitem>
a46dbe
+                <para>
a46dbe
+                  If a group name is specified with this option, members
a46dbe
+                  of the group will be handled by this module the same as
a46dbe
+                  the root account (the options <option>even_deny_root</option>
a46dbe
+                  and <option>root_unlock_time</option> will apply to them.
a46dbe
+                  By default the option is not set.
a46dbe
+                </para>
a46dbe
+              </listitem>
a46dbe
+            </varlistentry>
a46dbe
+        </variablelist>
a46dbe
+  </refsect1>
a46dbe
+
a46dbe
+  <refsect1 id='faillock.conf-examples'>
a46dbe
+    <title>EXAMPLES</title>
a46dbe
+    <para>
a46dbe
+      /etc/security/faillock.conf file example:
a46dbe
+    </para>
a46dbe
+    <programlisting>
a46dbe
+deny=4
a46dbe
+unlock_time=1200
a46dbe
+silent
a46dbe
+    </programlisting>
a46dbe
+  </refsect1>
a46dbe
+
a46dbe
+  <refsect1 id="faillock.conf-files">
a46dbe
+    <title>FILES</title>
a46dbe
+    <variablelist>
a46dbe
+      <varlistentry>
a46dbe
+        <term><filename>/etc/security/faillock.conf</filename></term>
a46dbe
+        <listitem>
a46dbe
+          <para>the config file for custom options</para>
a46dbe
+        </listitem>
a46dbe
+      </varlistentry>
a46dbe
+    </variablelist>
a46dbe
+  </refsect1>
a46dbe
+
a46dbe
+  <refsect1 id='faillock.conf-see_also'>
a46dbe
+    <title>SEE ALSO</title>
a46dbe
+    <para>
a46dbe
+      <citerefentry>
a46dbe
+        <refentrytitle>faillock</refentrytitle><manvolnum>8</manvolnum>
a46dbe
+      </citerefentry>,
a46dbe
+      <citerefentry>
a46dbe
+        <refentrytitle>pam_faillock</refentrytitle><manvolnum>8</manvolnum>
a46dbe
+      </citerefentry>,
a46dbe
+      <citerefentry>
a46dbe
+        <refentrytitle>pam.conf</refentrytitle><manvolnum>5</manvolnum>
a46dbe
+      </citerefentry>,
a46dbe
+      <citerefentry>
a46dbe
+        <refentrytitle>pam.d</refentrytitle><manvolnum>5</manvolnum>
a46dbe
+      </citerefentry>,
a46dbe
+      <citerefentry>
a46dbe
+        <refentrytitle>pam</refentrytitle><manvolnum>8</manvolnum>
a46dbe
+      </citerefentry>
a46dbe
+    </para>
a46dbe
+  </refsect1>
a46dbe
+
a46dbe
+  <refsect1 id='faillock.conf-author'>
a46dbe
+    <title>AUTHOR</title>
a46dbe
+      <para>
a46dbe
+        pam_faillock was written by Tomas Mraz. The support for faillock.conf was written by Brian Ward.
a46dbe
+      </para>
a46dbe
+  </refsect1>
a46dbe
+
a46dbe
+</refentry>
a46dbe
diff -up Linux-PAM-1.3.1/modules/pam_faillock/faillock.conf.faillock-update Linux-PAM-1.3.1/modules/pam_faillock/faillock.conf
a46dbe
--- Linux-PAM-1.3.1/modules/pam_faillock/faillock.conf.faillock-update	2019-10-16 17:34:50.607405060 +0200
a46dbe
+++ Linux-PAM-1.3.1/modules/pam_faillock/faillock.conf	2019-10-16 16:26:05.000000000 +0200
a46dbe
@@ -0,0 +1,62 @@
a46dbe
+# Configuration for locking the user after multiple failed
a46dbe
+# authentication attempts.
a46dbe
+#
a46dbe
+# The directory where the user files with the failure records are kept.
a46dbe
+# The default is /var/run/faillock.
a46dbe
+# dir = /var/run/faillock
a46dbe
+#
a46dbe
+# Will log the user name into the system log if the user is not found.
a46dbe
+# Enabled if option is present.
a46dbe
+# audit
a46dbe
+#
a46dbe
+# Don't print informative messages.
a46dbe
+# Enabled if option is present.
a46dbe
+# silent
a46dbe
+#
a46dbe
+# Don't log informative messages via syslog.
a46dbe
+# Enabled if option is present.
a46dbe
+# no_log_info
a46dbe
+#
a46dbe
+# Only track failed user authentications attempts for local users
a46dbe
+# in /etc/passwd and ignore centralized (AD, IdM, LDAP, etc.) users.
a46dbe
+# The `faillock` command will also no longer track user failed
a46dbe
+# authentication attempts. Enabling this option will prevent a
a46dbe
+# double-lockout scenario where a user is locked out locally and
a46dbe
+# in the centralized mechanism.
a46dbe
+# Enabled if option is present.
a46dbe
+# local_users_only
a46dbe
+#
a46dbe
+# Deny access if the number of consecutive authentication failures
a46dbe
+# for this user during the recent interval exceeds n tries.
a46dbe
+# The default is 3.
a46dbe
+# deny = 3
a46dbe
+#
a46dbe
+# The length of the interval during which the consecutive
a46dbe
+# authentication failures must happen for the user account
a46dbe
+# lock out is <replaceable>n</replaceable> seconds.
a46dbe
+# The default is 900 (15 minutes).
a46dbe
+# fail_interval = 900
a46dbe
+#
a46dbe
+# The access will be reenabled after n seconds after the lock out.
a46dbe
+# The value 0 has the same meaning as value `never` - the access
a46dbe
+# will not be reenabled without resetting the faillock
a46dbe
+# entries by the `faillock` command.
a46dbe
+# The default is 600 (10 minutes).
a46dbe
+# unlock_time = 600
a46dbe
+#
a46dbe
+# Root account can become locked as well as regular accounts.
a46dbe
+# Enabled if option is present.
a46dbe
+# even_deny_root
a46dbe
+#
a46dbe
+# This option implies the `even_deny_root` option.
a46dbe
+# Allow access after n seconds to root account after the
a46dbe
+# account is locked. In case the option is not specified
a46dbe
+# the value is the same as of the `unlock_time` option.
a46dbe
+# root_unlock_time = 900
a46dbe
+#
a46dbe
+# If a group name is specified with this option, members
a46dbe
+# of the group will be handled by this module the same as
a46dbe
+# the root account (the options `even_deny_root>` and
a46dbe
+# `root_unlock_time` will apply to them.
a46dbe
+# By default, the option is not set.
a46dbe
+# admin_group = <admin_group_name>
a46dbe
diff -up Linux-PAM-1.3.1/modules/pam_faillock/faillock.h.faillock-update Linux-PAM-1.3.1/modules/pam_faillock/faillock.h
a46dbe
--- Linux-PAM-1.3.1/modules/pam_faillock/faillock.h.faillock-update	2019-10-16 16:49:27.026893768 +0200
a46dbe
+++ Linux-PAM-1.3.1/modules/pam_faillock/faillock.h	2019-10-16 16:51:40.431628566 +0200
a46dbe
@@ -65,6 +65,7 @@ struct tally_data {
a46dbe
 };
a46dbe
 
a46dbe
 #define FAILLOCK_DEFAULT_TALLYDIR "/var/run/faillock"
a46dbe
+#define FAILLOCK_DEFAULT_CONF "/etc/security/faillock.conf"
a46dbe
 
a46dbe
 int open_tally(const char *dir, const char *user, uid_t uid, int create);
a46dbe
 int read_tally(int fd, struct tally_data *tallies);
a46dbe
diff -up Linux-PAM-1.3.1/modules/pam_faillock/Makefile.am.faillock-update Linux-PAM-1.3.1/modules/pam_faillock/Makefile.am
a46dbe
--- Linux-PAM-1.3.1/modules/pam_faillock/Makefile.am.faillock-update	2019-10-16 16:49:27.026893768 +0200
a46dbe
+++ Linux-PAM-1.3.1/modules/pam_faillock/Makefile.am	2019-10-16 16:50:15.065078080 +0200
a46dbe
@@ -1,6 +1,6 @@
a46dbe
 #
a46dbe
 # Copyright (c) 2005, 2006, 2007, 2009 Thorsten Kukuk <kukuk@thkukuk.de>
a46dbe
-# Copyright (c) 2008 Red Hat, Inc.
a46dbe
+# Copyright (c) 2008, 2018 Red Hat, Inc.
a46dbe
 # Copyright (c) 2010 Tomas Mraz <tmraz@redhat.com>
a46dbe
 #
a46dbe
 
a46dbe
@@ -9,8 +9,8 @@ MAINTAINERCLEANFILES = $(MANS) README
a46dbe
 
a46dbe
 EXTRA_DIST = README $(MANS) $(XMLS) tst-pam_faillock
a46dbe
 
a46dbe
-man_MANS = pam_faillock.8 faillock.8
a46dbe
-XMLS = README.xml pam_faillock.8.xml faillock.8.xml
a46dbe
+man_MANS = pam_faillock.8 faillock.8 faillock.conf.5
a46dbe
+XMLS = README.xml pam_faillock.8.xml faillock.8.xml faillock.conf.5.xml
a46dbe
 
a46dbe
 TESTS = tst-pam_faillock
a46dbe
 
a46dbe
@@ -31,6 +31,8 @@ endif
a46dbe
 faillock_LDFLAGS = -Wl,-z,now @PIE_LDFLAGS@
a46dbe
 faillock_LDADD = -L$(top_builddir)/libpam -lpam $(LIBAUDIT)
a46dbe
 
a46dbe
+secureconf_DATA = faillock.conf
a46dbe
+
a46dbe
 securelib_LTLIBRARIES = pam_faillock.la
a46dbe
 sbin_PROGRAMS = faillock
a46dbe
 
a46dbe
diff -up Linux-PAM-1.3.1/modules/pam_faillock/pam_faillock.8.xml.faillock-update Linux-PAM-1.3.1/modules/pam_faillock/pam_faillock.8.xml
a46dbe
--- Linux-PAM-1.3.1/modules/pam_faillock/pam_faillock.8.xml.faillock-update	2019-10-16 16:49:27.030893701 +0200
a46dbe
+++ Linux-PAM-1.3.1/modules/pam_faillock/pam_faillock.8.xml	2019-10-16 16:53:39.544606052 +0200
a46dbe
@@ -126,141 +126,13 @@
a46dbe
                 </para>
a46dbe
               </listitem>
a46dbe
             </varlistentry>
a46dbe
-            <varlistentry>
a46dbe
-              <term>
a46dbe
-                <option>dir=<replaceable>/path/to/tally-directory</replaceable></option>
a46dbe
-              </term>
a46dbe
-              <listitem>
a46dbe
-                <para>
a46dbe
-                  The directory where the user files with the failure records are kept. The
a46dbe
-                  default is <filename>/var/run/faillock</filename>.
a46dbe
-                </para>
a46dbe
-              </listitem>
a46dbe
-            </varlistentry>
a46dbe
-            <varlistentry>
a46dbe
-              <term>
a46dbe
-                <option>audit</option>
a46dbe
-              </term>
a46dbe
-              <listitem>
a46dbe
-                <para>
a46dbe
-                  Will log the user name into the system log if the user is not found.
a46dbe
-                </para>
a46dbe
-              </listitem>
a46dbe
-            </varlistentry>
a46dbe
-            <varlistentry>
a46dbe
-              <term>
a46dbe
-                <option>silent</option>
a46dbe
-              </term>
a46dbe
-              <listitem>
a46dbe
-                <para>
a46dbe
-                  Don't print informative messages. This option is implicite
a46dbe
-                  in the <emphasis>authfail</emphasis> and <emphasis>authsucc</emphasis>
a46dbe
-                  functions.
a46dbe
-                </para>
a46dbe
-              </listitem>
a46dbe
-            </varlistentry>
a46dbe
-            <varlistentry>
a46dbe
-              <term>
a46dbe
-                <option>no_log_info</option>
a46dbe
-              </term>
a46dbe
-              <listitem>
a46dbe
-                <para>
a46dbe
-                  Don't log informative messages via <citerefentry><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
a46dbe
-                </para>
a46dbe
-              </listitem>
a46dbe
-            </varlistentry>
a46dbe
-            <varlistentry>
a46dbe
-              <term>
a46dbe
-                <option>deny=<replaceable>n</replaceable></option>
a46dbe
-              </term>
a46dbe
-              <listitem>
a46dbe
-                <para>
a46dbe
-                  Deny access if the number of consecutive authentication failures
a46dbe
-                  for this user during the recent interval exceeds
a46dbe
-                  <replaceable>n</replaceable>. The default is 3.
a46dbe
-                </para>
a46dbe
-              </listitem>
a46dbe
-            </varlistentry>
a46dbe
-            <varlistentry>
a46dbe
-              <term>
a46dbe
-                <option>fail_interval=<replaceable>n</replaceable></option>
a46dbe
-              </term>
a46dbe
-              <listitem>
a46dbe
-                <para>
a46dbe
-                  The length of the interval during which the consecutive
a46dbe
-                  authentication failures must happen for the user account
a46dbe
-                  lock out is <replaceable>n</replaceable> seconds.
a46dbe
-                  The default is 900 (15 minutes).
a46dbe
-                </para>
a46dbe
-              </listitem>
a46dbe
-            </varlistentry>
a46dbe
-            <varlistentry>
a46dbe
-              <term>
a46dbe
-                <option>unlock_time=<replaceable>n</replaceable></option>
a46dbe
-              </term>
a46dbe
-              <listitem>
a46dbe
-                <para>
a46dbe
-                  The access will be reenabled after
a46dbe
-                  <replaceable>n</replaceable> seconds after the lock out.
a46dbe
-                  The value 0 has the same meaning as value
a46dbe
-                  <emphasis>never</emphasis> - the access
a46dbe
-                  will not be reenabled without resetting the faillock
a46dbe
-                  entries by the <citerefentry><refentrytitle>faillock</refentrytitle><manvolnum>8</manvolnum></citerefentry> command.
a46dbe
-                  The default is 600 (10 minutes).
a46dbe
-                </para>
a46dbe
-                <para>
a46dbe
-                  Note that the default directory that <emphasis>pam_faillock</emphasis>
a46dbe
-                  uses is usually cleared on system boot so the access will be also reenabled
a46dbe
-                  after system reboot. If that is undesirable a different tally directory
a46dbe
-                  must be set with the <option>dir</option> option.
a46dbe
-                </para>
a46dbe
-                <para>
a46dbe
-                  Also note that it is usually undesirable to permanently lock
a46dbe
-                  out the users as they can become easily a target of denial of service
a46dbe
-                  attack unless the usernames are random and kept secret to potential
a46dbe
-                  attackers.
a46dbe
-                </para>
a46dbe
-              </listitem>
a46dbe
-            </varlistentry>
a46dbe
-            <varlistentry>
a46dbe
-              <term>
a46dbe
-                <option>even_deny_root</option>
a46dbe
-              </term>
a46dbe
-              <listitem>
a46dbe
-                <para>
a46dbe
-                  Root account can become locked as well as regular accounts.
a46dbe
-                </para>
a46dbe
-              </listitem>
a46dbe
-            </varlistentry>
a46dbe
-            <varlistentry>
a46dbe
-              <term>
a46dbe
-                <option>root_unlock_time=<replaceable>n</replaceable></option>
a46dbe
-              </term>
a46dbe
-              <listitem>
a46dbe
-                <para>
a46dbe
-                  This option implies <option>even_deny_root</option> option.
a46dbe
-                  Allow access after <replaceable>n</replaceable> seconds
a46dbe
-                  to root account after the account is locked. In case the
a46dbe
-                  option is not specified the value is the same as of the
a46dbe
-                  <option>unlock_time</option> option.
a46dbe
-                </para>
a46dbe
-              </listitem>
a46dbe
-            </varlistentry>
a46dbe
-            <varlistentry>
a46dbe
-              <term>
a46dbe
-                <option>admin_group=<replaceable>name</replaceable></option>
a46dbe
-              </term>
a46dbe
-              <listitem>
a46dbe
-                <para>
a46dbe
-                  If a group name is specified with this option, members
a46dbe
-                  of the group will be handled by this module the same as
a46dbe
-                  the root account (the options <option>even_deny_root></option>
a46dbe
-                  and <option>root_unlock_time</option> will apply to them.
a46dbe
-                  By default the option is not set.
a46dbe
-                </para>
a46dbe
-              </listitem>
a46dbe
-            </varlistentry>
a46dbe
         </variablelist>
a46dbe
+        <para>
a46dbe
+          The options for configuring the module behavior are described in the 
a46dbe
+          <citerefentry><refentrytitle>faillock.conf</refentrytitle><manvolnum>5</manvolnum>
a46dbe
+          </citerefentry> manual page. The options specified on the module command
a46dbe
+          line override the values from the configuration file.
a46dbe
+        </para>
a46dbe
   </refsect1>
a46dbe
 
a46dbe
   <refsect1 id="pam_faillock-types">
a46dbe
@@ -306,19 +178,23 @@
a46dbe
   <refsect1 id='pam_faillock-notes'>
a46dbe
     <title>NOTES</title>
a46dbe
     <para>
a46dbe
-      <emphasis>pam_faillock</emphasis> setup in the PAM stack is different
a46dbe
+      Configuring options on the module command line is not recommend. The
a46dbe
+      <emphasis>/etc/security/faillock.conf</emphasis> should be used instead.
a46dbe
+    </para>
a46dbe
+    <para>
a46dbe
+      The setup of <emphasis>pam_faillock</emphasis> in the PAM stack is different
a46dbe
       from the <emphasis>pam_tally2</emphasis> module setup.
a46dbe
     </para>
a46dbe
     <para>
a46dbe
-      The individual files with the failure records are created as owned by
a46dbe
+      Individual files with the failure records are created as owned by
a46dbe
       the user. This allows <emphasis remap='B'>pam_faillock.so</emphasis> module
a46dbe
       to work correctly when it is called from a screensaver.
a46dbe
     </para>
a46dbe
     <para>
a46dbe
       Note that using the module in <option>preauth</option> without the
a46dbe
-      <option>silent</option> option or with <emphasis>requisite</emphasis>
a46dbe
-      control field leaks an information about existence or
a46dbe
-      non-existence of an user account in the system because
a46dbe
+      <option>silent</option> option specified in <filename>/etc/security/faillock.conf</filename>
a46dbe
+      or with <emphasis>requisite</emphasis> control field leaks an information about
a46dbe
+      existence or non-existence of an user account in the system because
a46dbe
       the failures are not recorded for the unknown users. The message
a46dbe
       about the user account being locked is never displayed for nonexisting
a46dbe
       user accounts allowing the adversary to infer that a particular account
a46dbe
@@ -341,15 +217,26 @@
a46dbe
       be added to tell the user that his login is blocked by the module and also to abort
a46dbe
       the authentication without even asking for password in such case.
a46dbe
     </para>
a46dbe
+    <para>
a46dbe
+      /etc/security/faillock.conf file example:
a46dbe
+    </para>
a46dbe
+    <programlisting>
a46dbe
+deny=4
a46dbe
+unlock_time=1200
a46dbe
+silent
a46dbe
+    </programlisting>
a46dbe
+    <para>
a46dbe
+      /etc/pam.d/config file example:
a46dbe
+    </para>
a46dbe
     <programlisting>
a46dbe
 auth     required       pam_securetty.so
a46dbe
 auth     required       pam_env.so
a46dbe
 auth     required       pam_nologin.so
a46dbe
-# optionally call: auth requisite pam_faillock.so preauth deny=4 even_deny_root unlock_time=1200
a46dbe
+# optionally call: auth requisite pam_faillock.so preauth
a46dbe
 # to display the message about account being locked
a46dbe
 auth     [success=1 default=bad] pam_unix.so
a46dbe
-auth     [default=die]  pam_faillock.so authfail deny=4 even_deny_root unlock_time=1200
a46dbe
-auth     sufficient     pam_faillock.so authsucc deny=4 even_deny_root unlock_time=1200
a46dbe
+auth     [default=die]  pam_faillock.so authfail
a46dbe
+auth     sufficient     pam_faillock.so authsucc
a46dbe
 auth     required       pam_deny.so
a46dbe
 account  required       pam_unix.so
a46dbe
 password required       pam_unix.so shadow
a46dbe
@@ -361,17 +248,18 @@ session  required       pam_selinux.so o
a46dbe
     <para>
a46dbe
       In the second example the module is called both in the <emphasis>auth</emphasis>
a46dbe
       and <emphasis>account</emphasis> phases and the module gives the authenticating
a46dbe
-      user message when the account is locked 
a46dbe
+      user message when the account is locked if <option>silent</option> option is not
a46dbe
+      specified in the <filename>faillock.conf</filename>.
a46dbe
     </para>
a46dbe
     <programlisting>
a46dbe
 auth     required       pam_securetty.so
a46dbe
 auth     required       pam_env.so
a46dbe
 auth     required       pam_nologin.so
a46dbe
-auth     required       pam_faillock.so preauth silent deny=4 even_deny_root unlock_time=1200
a46dbe
+auth     required       pam_faillock.so preauth
a46dbe
 # optionally use requisite above if you do not want to prompt for the password
a46dbe
-# on locked accounts, possibly with removing the silent option as well
a46dbe
+# on locked accounts
a46dbe
 auth     sufficient     pam_unix.so
a46dbe
-auth     [default=die]  pam_faillock.so authfail deny=4 even_deny_root unlock_time=1200
a46dbe
+auth     [default=die]  pam_faillock.so authfail
a46dbe
 auth     required       pam_deny.so
a46dbe
 account  required       pam_faillock.so
a46dbe
 # if you drop the above call to pam_faillock.so the lock will be done also
a46dbe
@@ -394,6 +282,12 @@ session  required       pam_selinux.so o
a46dbe
           <para>the files logging the authentication failures for users</para>
a46dbe
         </listitem>
a46dbe
       </varlistentry>
a46dbe
+      <varlistentry>
a46dbe
+        <term><filename>/etc/security/faillock.conf</filename></term>
a46dbe
+        <listitem>
a46dbe
+          <para>the config file for pam_faillock options</para>
a46dbe
+        </listitem>
a46dbe
+      </varlistentry>
a46dbe
     </variablelist>
a46dbe
   </refsect1>
a46dbe
 
a46dbe
@@ -404,6 +298,9 @@ session  required       pam_selinux.so o
a46dbe
         <refentrytitle>faillock</refentrytitle><manvolnum>8</manvolnum>
a46dbe
       </citerefentry>,
a46dbe
       <citerefentry>
a46dbe
+        <refentrytitle>faillock.conf</refentrytitle><manvolnum>5</manvolnum>
a46dbe
+      </citerefentry>,
a46dbe
+      <citerefentry>
a46dbe
         <refentrytitle>pam.conf</refentrytitle><manvolnum>5</manvolnum>
a46dbe
       </citerefentry>,
a46dbe
       <citerefentry>
a46dbe
diff -up Linux-PAM-1.3.1/modules/pam_faillock/pam_faillock.c.faillock-update Linux-PAM-1.3.1/modules/pam_faillock/pam_faillock.c
a46dbe
--- Linux-PAM-1.3.1/modules/pam_faillock/pam_faillock.c.faillock-update	2019-10-16 16:49:27.030893701 +0200
a46dbe
+++ Linux-PAM-1.3.1/modules/pam_faillock/pam_faillock.c	2019-12-16 17:55:11.403270018 +0100
a46dbe
@@ -1,5 +1,6 @@
a46dbe
 /*
a46dbe
- * Copyright (c) 2010, 2017 Tomas Mraz <tmraz@redhat.com>
a46dbe
+ * Copyright (c) 2010, 2017, 2019 Tomas Mraz <tmraz@redhat.com>
a46dbe
+ * Copyright (c) 2010, 2017, 2019 Red Hat, Inc.
a46dbe
  *
a46dbe
  * Redistribution and use in source and binary forms, with or without
a46dbe
  * modification, are permitted provided that the following conditions
a46dbe
@@ -43,6 +44,7 @@
a46dbe
 #include <time.h>
a46dbe
 #include <pwd.h>
a46dbe
 #include <syslog.h>
a46dbe
+#include <ctype.h>
a46dbe
 
a46dbe
 #ifdef HAVE_LIBAUDIT
a46dbe
 #include <libaudit.h>
a46dbe
@@ -66,8 +68,14 @@
a46dbe
 #define FAILLOCK_FLAG_SILENT		0x4
a46dbe
 #define FAILLOCK_FLAG_NO_LOG_INFO	0x8
a46dbe
 #define FAILLOCK_FLAG_UNLOCKED		0x10
a46dbe
+#define FAILLOCK_FLAG_LOCAL_ONLY	0x20
a46dbe
 
a46dbe
 #define MAX_TIME_INTERVAL 604800 /* 7 days */
a46dbe
+#define FAILLOCK_CONF_MAX_LINELEN 1023
a46dbe
+#define FAILLOCK_ERROR_CONF_OPEN -3
a46dbe
+#define FAILLOCK_ERROR_CONF_MALFORMED -4
a46dbe
+
a46dbe
+#define PATH_PASSWD "/etc/passwd"
a46dbe
 
a46dbe
 struct options {
a46dbe
 	unsigned int action;
a46dbe
@@ -76,117 +84,295 @@ struct options {
a46dbe
 	unsigned int fail_interval;
a46dbe
 	unsigned int unlock_time;
a46dbe
 	unsigned int root_unlock_time;
a46dbe
-	const char *dir;
a46dbe
+	char *dir;
a46dbe
+	const char *conf;
a46dbe
 	const char *user;
a46dbe
-	const char *admin_group;
a46dbe
+	char *admin_group;
a46dbe
 	int failures;
a46dbe
 	uint64_t latest_time;
a46dbe
 	uid_t uid;
a46dbe
 	int is_admin;
a46dbe
 	uint64_t now;
a46dbe
+	int fatal_error;
a46dbe
 };
a46dbe
 
a46dbe
+int read_config_file(
a46dbe
+	pam_handle_t *pamh,
a46dbe
+	struct options *opts,
a46dbe
+	const char *cfgfile
a46dbe
+);
a46dbe
+
a46dbe
+void set_conf_opt(
a46dbe
+	pam_handle_t *pamh,
a46dbe
+	struct options *opts,
a46dbe
+	const char *name,
a46dbe
+	const char *value
a46dbe
+);
a46dbe
+
a46dbe
 static void
a46dbe
 args_parse(pam_handle_t *pamh, int argc, const char **argv,
a46dbe
 		int flags, struct options *opts)
a46dbe
 {
a46dbe
 	int i;
a46dbe
+	int rv;
a46dbe
 	memset(opts, 0, sizeof(*opts));
a46dbe
 
a46dbe
-	opts->dir = FAILLOCK_DEFAULT_TALLYDIR;
a46dbe
+	opts->dir = strdup(FAILLOCK_DEFAULT_TALLYDIR);
a46dbe
+	opts->conf = FAILLOCK_DEFAULT_CONF;
a46dbe
 	opts->deny = 3;
a46dbe
 	opts->fail_interval = 900;
a46dbe
 	opts->unlock_time = 600;
a46dbe
 	opts->root_unlock_time = MAX_TIME_INTERVAL+1;
a46dbe
 
a46dbe
-	for (i = 0; i < argc; ++i) {
a46dbe
+	if ((rv=read_config_file(pamh, opts, opts->conf)) != PAM_SUCCESS) {
a46dbe
+		pam_syslog(pamh, LOG_DEBUG,
a46dbe
+					"Configuration file missing");
a46dbe
+	}
a46dbe
 
a46dbe
-		if (strncmp(argv[i], "dir=", 4) == 0) {
a46dbe
-			if (argv[i][4] != '/') {
a46dbe
-				pam_syslog(pamh, LOG_ERR,
a46dbe
-					"Tally directory is not absolute path (%s); keeping default", argv[i]);
a46dbe
-	        	} else {
a46dbe
-			        opts->dir = argv[i]+4;
a46dbe
-			}
a46dbe
-		} 
a46dbe
-		else if (strncmp(argv[i], "deny=", 5) == 0) {
a46dbe
-			if (sscanf(argv[i]+5, "%hu", &opts->deny) != 1) {
a46dbe
-				pam_syslog(pamh, LOG_ERR,
a46dbe
-					"Bad number supplied for deny argument");
a46dbe
-        		}
a46dbe
-		}
a46dbe
-		else if (strncmp(argv[i], "fail_interval=", 14) == 0) {
a46dbe
-			unsigned int temp;
a46dbe
-			if (sscanf(argv[i]+14, "%u", &temp) != 1 ||
a46dbe
-				temp > MAX_TIME_INTERVAL) {
a46dbe
-				pam_syslog(pamh, LOG_ERR,
a46dbe
-					"Bad number supplied for fail_interval argument");
a46dbe
-	        	} else {
a46dbe
-				opts->fail_interval = temp;
a46dbe
-			}
a46dbe
+	for (i = 0; i < argc; ++i) {
a46dbe
+		if (strcmp(argv[i], "preauth") == 0) {
a46dbe
+			opts->action = FAILLOCK_ACTION_PREAUTH;
a46dbe
+		}
a46dbe
+		else if (strcmp(argv[i], "authfail") == 0) {
a46dbe
+			opts->action = FAILLOCK_ACTION_AUTHFAIL;
a46dbe
+		}
a46dbe
+		else if (strcmp(argv[i], "authsucc") == 0) {
a46dbe
+			opts->action = FAILLOCK_ACTION_AUTHSUCC;
a46dbe
 		}
a46dbe
-		else if (strncmp(argv[i], "unlock_time=", 12) == 0) {
a46dbe
-			unsigned int temp;
a46dbe
+		else {
a46dbe
+			char buf[FAILLOCK_CONF_MAX_LINELEN + 1];
a46dbe
+			char *val;
a46dbe
 
a46dbe
-			if (strcmp(argv[i]+12, "never") == 0) {
a46dbe
-				opts->unlock_time = 0;
a46dbe
-			}
a46dbe
-			else if (sscanf(argv[i]+12, "%u", &temp) != 1 ||
a46dbe
-				temp > MAX_TIME_INTERVAL) {
a46dbe
-				pam_syslog(pamh, LOG_ERR,
a46dbe
-					"Bad number supplied for unlock_time argument");
a46dbe
+			strncpy(buf, argv[i], sizeof(buf) - 1);
a46dbe
+			buf[sizeof(buf) - 1] = '\0';
a46dbe
+
a46dbe
+			val = strchr(buf, '=');
a46dbe
+			if (val != NULL) {
a46dbe
+				*val = '\0';
a46dbe
+				++val;
a46dbe
 			}
a46dbe
 			else {
a46dbe
-				opts->unlock_time = temp;
a46dbe
+				val = buf + sizeof(buf) - 1;
a46dbe
 			}
a46dbe
+			set_conf_opt(pamh, opts, buf, val);
a46dbe
 		}
a46dbe
-		else if (strncmp(argv[i], "root_unlock_time=", 17) == 0) {
a46dbe
-			unsigned int temp;
a46dbe
+	}
a46dbe
 
a46dbe
-			if (strcmp(argv[i]+17, "never") == 0) {
a46dbe
-				opts->root_unlock_time = 0;
a46dbe
-			}
a46dbe
-			else if (sscanf(argv[i]+17, "%u", &temp) != 1 ||
a46dbe
-				temp > MAX_TIME_INTERVAL) {
a46dbe
-				pam_syslog(pamh, LOG_ERR,
a46dbe
-					"Bad number supplied for root_unlock_time argument");
a46dbe
+	if (opts->root_unlock_time == MAX_TIME_INTERVAL+1)
a46dbe
+		opts->root_unlock_time = opts->unlock_time;
a46dbe
+	if (flags & PAM_SILENT)
a46dbe
+		opts->flags |= FAILLOCK_FLAG_SILENT;
a46dbe
+
a46dbe
+	if (opts->dir == NULL) {
a46dbe
+		pam_syslog(pamh, LOG_CRIT, "Error allocating memory: %m");
a46dbe
+		opts->fatal_error = 1;
a46dbe
+	}
a46dbe
+}
a46dbe
+
a46dbe
+/* parse a single configuration file */
a46dbe
+int
a46dbe
+read_config_file(pam_handle_t *pamh, struct options *opts, const char *cfgfile)
a46dbe
+{
a46dbe
+	FILE *f;
a46dbe
+	char linebuf[FAILLOCK_CONF_MAX_LINELEN+1];
a46dbe
+
a46dbe
+	f = fopen(cfgfile, "r");
a46dbe
+	if (f == NULL) {
a46dbe
+		/* ignore non-existent default config file */
a46dbe
+		if (errno == ENOENT && strcmp(cfgfile, FAILLOCK_DEFAULT_CONF) == 0)
a46dbe
+			return 0;
a46dbe
+		return FAILLOCK_ERROR_CONF_OPEN;
a46dbe
+	}
a46dbe
+
a46dbe
+	while (fgets(linebuf, sizeof(linebuf), f) != NULL) {
a46dbe
+		size_t len;
a46dbe
+		char *ptr;
a46dbe
+		char *name;
a46dbe
+		int eq;
a46dbe
+
a46dbe
+		len = strlen(linebuf);
a46dbe
+		/* len cannot be 0 unless there is a bug in fgets */
a46dbe
+		if (len && linebuf[len - 1] != '\n' && !feof(f)) {
a46dbe
+			(void) fclose(f);
a46dbe
+			return FAILLOCK_ERROR_CONF_MALFORMED;
a46dbe
+		}
a46dbe
+
a46dbe
+		if ((ptr=strchr(linebuf, '#')) != NULL) {
a46dbe
+			*ptr = '\0';
a46dbe
+		} else {
a46dbe
+			ptr = linebuf + len;
a46dbe
+		}
a46dbe
+
a46dbe
+		/* drop terminating whitespace including the \n */
a46dbe
+		while (ptr > linebuf) {
a46dbe
+			if (!isspace(*(ptr-1))) {
a46dbe
+				*ptr = '\0';
a46dbe
+				break;
a46dbe
+			}
a46dbe
+			--ptr;
a46dbe
+		}
a46dbe
+
a46dbe
+		/* skip initial whitespace */
a46dbe
+		for (ptr = linebuf; isspace(*ptr); ptr++);
a46dbe
+		if (*ptr == '\0')
a46dbe
+			continue;
a46dbe
+
a46dbe
+		/* grab the key name */
a46dbe
+		eq = 0;
a46dbe
+		name = ptr;
a46dbe
+		while (*ptr != '\0') {
a46dbe
+			if (isspace(*ptr) || *ptr == '=') {
a46dbe
+				eq = *ptr == '=';
a46dbe
+				*ptr = '\0';
a46dbe
+				++ptr;
a46dbe
+				break;
a46dbe
+			}
a46dbe
+			++ptr;
a46dbe
+		}
a46dbe
+
a46dbe
+		/* grab the key value */
a46dbe
+		while (*ptr != '\0') {
a46dbe
+			if (*ptr != '=' || eq) {
a46dbe
+				if (!isspace(*ptr)) {
a46dbe
+					break;
a46dbe
+				}
a46dbe
 			} else {
a46dbe
-				opts->root_unlock_time = temp;
a46dbe
+				eq = 1;
a46dbe
 			}
a46dbe
+			++ptr;
a46dbe
 		}
a46dbe
-		else if (strncmp(argv[i], "admin_group=", 12) == 0) {
a46dbe
-			opts->admin_group = argv[i] + 12;
a46dbe
+
a46dbe
+		/* set the key:value pair on opts */
a46dbe
+		set_conf_opt(pamh, opts, name, ptr);
a46dbe
+	}
a46dbe
+
a46dbe
+	(void)fclose(f);
a46dbe
+	return PAM_SUCCESS;
a46dbe
+}
a46dbe
+
a46dbe
+void set_conf_opt(pam_handle_t *pamh, struct options *opts, const char *name, const char *value)
a46dbe
+{
a46dbe
+	if (strcmp(name, "dir") == 0) {
a46dbe
+		if (value[0] != '/') {
a46dbe
+			pam_syslog(pamh, LOG_ERR,
a46dbe
+				"Tally directory is not absolute path (%s); keeping default", value);
a46dbe
+		} else {
a46dbe
+			free(opts->dir);
a46dbe
+			opts->dir = strdup(value);
a46dbe
 		}
a46dbe
- 		else if (strcmp(argv[i], "preauth") == 0) {
a46dbe
-			opts->action = FAILLOCK_ACTION_PREAUTH;
a46dbe
+	}
a46dbe
+	else if (strcmp(name, "deny") == 0) {
a46dbe
+		if (sscanf(value, "%hu", &opts->deny) != 1) {
a46dbe
+			pam_syslog(pamh, LOG_ERR,
a46dbe
+				"Bad number supplied for deny argument");
a46dbe
+		}
a46dbe
+	}
a46dbe
+	else if (strcmp(name, "fail_interval") == 0) {
a46dbe
+		unsigned int temp;
a46dbe
+		if (sscanf(value, "%u", &temp) != 1 ||
a46dbe
+			temp > MAX_TIME_INTERVAL) {
a46dbe
+			pam_syslog(pamh, LOG_ERR,
a46dbe
+				"Bad number supplied for fail_interval argument");
a46dbe
+		} else {
a46dbe
+			opts->fail_interval = temp;
a46dbe
 		}
a46dbe
- 		else if (strcmp(argv[i], "authfail") == 0) {
a46dbe
-			opts->action = FAILLOCK_ACTION_AUTHFAIL;
a46dbe
+	}
a46dbe
+	else if (strcmp(name, "unlock_time") == 0) {
a46dbe
+		unsigned int temp;
a46dbe
+
a46dbe
+		if (strcmp(value, "never") == 0) {
a46dbe
+			opts->unlock_time = 0;
a46dbe
 		}
a46dbe
-	 	else if (strcmp(argv[i], "authsucc") == 0) {
a46dbe
-			opts->action = FAILLOCK_ACTION_AUTHSUCC;
a46dbe
+		else if (sscanf(value, "%u", &temp) != 1 ||
a46dbe
+			temp > MAX_TIME_INTERVAL) {
a46dbe
+			pam_syslog(pamh, LOG_ERR,
a46dbe
+				"Bad number supplied for unlock_time argument");
a46dbe
 		}
a46dbe
-	 	else if (strcmp(argv[i], "even_deny_root") == 0) {
a46dbe
-			opts->flags |= FAILLOCK_FLAG_DENY_ROOT;
a46dbe
+		else {
a46dbe
+			opts->unlock_time = temp;
a46dbe
 		}
a46dbe
-	 	else if (strcmp(argv[i], "audit") == 0) {
a46dbe
-			opts->flags |= FAILLOCK_FLAG_AUDIT;
a46dbe
+	}
a46dbe
+	else if (strcmp(name, "root_unlock_time") == 0) {
a46dbe
+		unsigned int temp;
a46dbe
+
a46dbe
+		if (strcmp(value, "never") == 0) {
a46dbe
+			opts->root_unlock_time = 0;
a46dbe
 		}
a46dbe
-	 	else if (strcmp(argv[i], "silent") == 0) {
a46dbe
-			opts->flags |= FAILLOCK_FLAG_SILENT;
a46dbe
+		else if (sscanf(value, "%u", &temp) != 1 ||
a46dbe
+			temp > MAX_TIME_INTERVAL) {
a46dbe
+			pam_syslog(pamh, LOG_ERR,
a46dbe
+				"Bad number supplied for root_unlock_time argument");
a46dbe
+		} else {
a46dbe
+			opts->root_unlock_time = temp;
a46dbe
 		}
a46dbe
-	 	else if (strcmp(argv[i], "no_log_info") == 0) {
a46dbe
-			opts->flags |= FAILLOCK_FLAG_NO_LOG_INFO;
a46dbe
+	}
a46dbe
+	else if (strcmp(name, "admin_group") == 0) {
a46dbe
+		free(opts->admin_group);
a46dbe
+		opts->admin_group = strdup(value);
a46dbe
+		if (opts->admin_group == NULL) {
a46dbe
+			opts->fatal_error = 1;
a46dbe
+			pam_syslog(pamh, LOG_CRIT, "Error allocating memory: %m");
a46dbe
 		}
a46dbe
-		else {
a46dbe
-			pam_syslog(pamh, LOG_ERR, "Unknown option: %s", argv[i]);
a46dbe
+	}
a46dbe
+	else if (strcmp(name, "even_deny_root") == 0) {
a46dbe
+		opts->flags |= FAILLOCK_FLAG_DENY_ROOT;
a46dbe
+	}
a46dbe
+	else if (strcmp(name, "audit") == 0) {
a46dbe
+		opts->flags |= FAILLOCK_FLAG_AUDIT;
a46dbe
+	}
a46dbe
+	else if (strcmp(name, "silent") == 0) {
a46dbe
+		opts->flags |= FAILLOCK_FLAG_SILENT;
a46dbe
+	}
a46dbe
+	else if (strcmp(name, "no_log_info") == 0) {
a46dbe
+		opts->flags |= FAILLOCK_FLAG_NO_LOG_INFO;
a46dbe
+	}
a46dbe
+	else if (strcmp(name, "local_users_only") == 0) {
a46dbe
+		opts->flags |= FAILLOCK_FLAG_LOCAL_ONLY;
a46dbe
+	}
a46dbe
+	else {
a46dbe
+		pam_syslog(pamh, LOG_ERR, "Unknown option: %s", name);
a46dbe
+	}
a46dbe
+}
a46dbe
+
a46dbe
+static int check_local_user (pam_handle_t *pamh, const char *user)
a46dbe
+{
a46dbe
+	struct passwd pw, *pwp;
a46dbe
+	char buf[4096];
a46dbe
+	int found = 0;
a46dbe
+	FILE *fp;
a46dbe
+	int errn;
a46dbe
+
a46dbe
+	fp = fopen(PATH_PASSWD, "r");
a46dbe
+	if (fp == NULL) {
a46dbe
+		pam_syslog(pamh, LOG_ERR, "unable to open %s: %m",
a46dbe
+			   PATH_PASSWD);
a46dbe
+		return -1;
a46dbe
+	}
a46dbe
+
a46dbe
+	for (;;) {
a46dbe
+		errn = fgetpwent_r(fp, &pw, buf, sizeof (buf), &pwp);
a46dbe
+		if (errn == ERANGE) {
a46dbe
+			pam_syslog(pamh, LOG_WARNING, "%s contains very long lines; corrupted?",
a46dbe
+				   PATH_PASSWD);
a46dbe
+			/* we can continue here as next call will read further */
a46dbe
+			continue;
a46dbe
+		}
a46dbe
+		if (errn != 0)
a46dbe
+			break;
a46dbe
+		if (strcmp(pwp->pw_name, user) == 0) {
a46dbe
+			found = 1;
a46dbe
+			break;
a46dbe
 		}
a46dbe
 	}
a46dbe
 
a46dbe
-	if (opts->root_unlock_time == MAX_TIME_INTERVAL+1)
a46dbe
-		opts->root_unlock_time = opts->unlock_time;
a46dbe
-	if (flags & PAM_SILENT)
a46dbe
-		opts->flags |= FAILLOCK_FLAG_SILENT;
a46dbe
+	fclose (fp);
a46dbe
+
a46dbe
+	if (errn != 0 && errn != ENOENT) {
a46dbe
+		pam_syslog(pamh, LOG_ERR, "unable to enumerate local accounts: %m");
a46dbe
+		return -1;
a46dbe
+	} else {
a46dbe
+		return found;
a46dbe
+	}
a46dbe
 }
a46dbe
 
a46dbe
 static int get_pam_user(pam_handle_t *pamh, struct options *opts)
a46dbe
@@ -393,7 +579,7 @@ write_tally(pam_handle_t *pamh, struct o
a46dbe
 
a46dbe
 	strncpy(tallies->records[oldest].source, source, sizeof(tallies->records[oldest].source));
a46dbe
 	/* source does not have to be null terminated */
a46dbe
-	
a46dbe
+
a46dbe
 	tallies->records[oldest].time = opts->now;
a46dbe
 
a46dbe
 	++failures;
a46dbe
@@ -468,6 +654,13 @@ tally_cleanup(struct tally_data *tallies
a46dbe
 	free(tallies->records);
a46dbe
 }
a46dbe
 
a46dbe
+static void
a46dbe
+opts_cleanup(struct options *opts)
a46dbe
+{
a46dbe
+	free(opts->dir);
a46dbe
+	free(opts->admin_group);
a46dbe
+}
a46dbe
+
a46dbe
 /*---------------------------------------------------------------------*/
a46dbe
 
a46dbe
 PAM_EXTERN int
a46dbe
@@ -481,39 +674,49 @@ pam_sm_authenticate(pam_handle_t *pamh,
a46dbe
 	memset(&tallies, 0, sizeof(tallies));
a46dbe
 
a46dbe
 	args_parse(pamh, argc, argv, flags, &opts);
a46dbe
+	if (opts.fatal_error) {
a46dbe
+		rv = PAM_BUF_ERR;
a46dbe
+		goto err;
a46dbe
+	}
a46dbe
 
a46dbe
 	pam_fail_delay(pamh, 2000000);	/* 2 sec delay for on failure */
a46dbe
 
a46dbe
 	if ((rv=get_pam_user(pamh, &opts)) != PAM_SUCCESS) {
a46dbe
-		return rv;
a46dbe
+		goto err;
a46dbe
 	}
a46dbe
 
a46dbe
-	switch (opts.action) {
a46dbe
-		case FAILLOCK_ACTION_PREAUTH:
a46dbe
-			rv = check_tally(pamh, &opts, &tallies, &fd;;
a46dbe
-			if (rv == PAM_AUTH_ERR && !(opts.flags & FAILLOCK_FLAG_SILENT)) {
a46dbe
-				faillock_message(pamh, &opts);
a46dbe
-			}
a46dbe
-                        break;
a46dbe
-
a46dbe
-		case FAILLOCK_ACTION_AUTHSUCC:
a46dbe
-			rv = check_tally(pamh, &opts, &tallies, &fd;;
a46dbe
-			if (rv == PAM_SUCCESS) {
a46dbe
-				reset_tally(pamh, &opts, &fd;;
a46dbe
-			}
a46dbe
-                        break;
a46dbe
-
a46dbe
-		case FAILLOCK_ACTION_AUTHFAIL:
a46dbe
-			rv = check_tally(pamh, &opts, &tallies, &fd;;
a46dbe
-			if (rv == PAM_SUCCESS) {
a46dbe
-				rv = PAM_IGNORE; /* this return value should be ignored */
a46dbe
-				write_tally(pamh, &opts, &tallies, &fd;;
a46dbe
-			}
a46dbe
-			break;
a46dbe
+	if (!(opts.flags & FAILLOCK_FLAG_LOCAL_ONLY) ||
a46dbe
+		check_local_user (pamh, opts.user) != 0) {
a46dbe
+		switch (opts.action) {
a46dbe
+			case FAILLOCK_ACTION_PREAUTH:
a46dbe
+				rv = check_tally(pamh, &opts, &tallies, &fd;;
a46dbe
+				if (rv == PAM_AUTH_ERR && !(opts.flags & FAILLOCK_FLAG_SILENT)) {
a46dbe
+					faillock_message(pamh, &opts);
a46dbe
+				}
a46dbe
+				break;
a46dbe
+
a46dbe
+			case FAILLOCK_ACTION_AUTHSUCC:
a46dbe
+				rv = check_tally(pamh, &opts, &tallies, &fd;;
a46dbe
+				if (rv == PAM_SUCCESS) {
a46dbe
+					reset_tally(pamh, &opts, &fd;;
a46dbe
+				}
a46dbe
+				break;
a46dbe
+
a46dbe
+			case FAILLOCK_ACTION_AUTHFAIL:
a46dbe
+				rv = check_tally(pamh, &opts, &tallies, &fd;;
a46dbe
+				if (rv == PAM_SUCCESS) {
a46dbe
+					rv = PAM_IGNORE; /* this return value should be ignored */
a46dbe
+					write_tally(pamh, &opts, &tallies, &fd;;
a46dbe
+				}
a46dbe
+				break;
a46dbe
+		}
a46dbe
 	}
a46dbe
 
a46dbe
 	tally_cleanup(&tallies, fd);
a46dbe
 
a46dbe
+err:
a46dbe
+	opts_cleanup(&opts);
a46dbe
+
a46dbe
 	return rv;
a46dbe
 }
a46dbe
 
a46dbe
@@ -540,18 +743,29 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int
a46dbe
 
a46dbe
 	args_parse(pamh, argc, argv, flags, &opts);
a46dbe
 
a46dbe
+	if (opts.fatal_error) {
a46dbe
+		rv = PAM_BUF_ERR;
a46dbe
+		goto err;
a46dbe
+	}
a46dbe
+
a46dbe
 	opts.action = FAILLOCK_ACTION_AUTHSUCC;
a46dbe
 
a46dbe
 	if ((rv=get_pam_user(pamh, &opts)) != PAM_SUCCESS) {
a46dbe
-		return rv;
a46dbe
+		goto err;
a46dbe
 	}
a46dbe
 
a46dbe
-	check_tally(pamh, &opts, &tallies, &fd;; /* for auditing */
a46dbe
-	reset_tally(pamh, &opts, &fd;;
a46dbe
+	if (!(opts.flags & FAILLOCK_FLAG_LOCAL_ONLY) ||
a46dbe
+		check_local_user (pamh, opts.user) != 0) {
a46dbe
+		check_tally(pamh, &opts, &tallies, &fd;; /* for auditing */
a46dbe
+		reset_tally(pamh, &opts, &fd;;
a46dbe
+	}
a46dbe
 
a46dbe
 	tally_cleanup(&tallies, fd);
a46dbe
 
a46dbe
-	return PAM_SUCCESS;
a46dbe
+err:
a46dbe
+	opts_cleanup(&opts);
a46dbe
+
a46dbe
+	return rv;
a46dbe
 }
a46dbe
 
a46dbe
 /*-----------------------------------------------------------------------*/