summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomas Mraz <tm@t8m.info>2008-10-17 11:29:55 +0000
committerTomas Mraz <tm@t8m.info>2008-10-17 11:29:55 +0000
commit114ed318bea9b5859ab89144261946716776e2ed (patch)
tree2fda76ee01feeece41b805203bdc52701a539fce
parent955caa5f8c4840931ce49bcd8f59c8803c8f0266 (diff)
Relevant BUGIDs:
Purpose of commit: new feature Commit summary: --------------- 2008-10-17 Tomas Mraz <t8m@centrum.cz> * configure.in: Add modules/pam_tally2/Makefile. * doc/sag/Linux-PAM_SAG.xml: Include pam_tally2.xml. * doc/sag/pam_tally2.xml: New. * libpam/pam_static_modules.h: Add pam_tally2 static struct. * modules/Makefile.am: Add pam_tally2 directory. * modules/pam_tally2/Makefile.am: New. * modules/pam_tally2/README.xml: New. * modules/pam_tally2/tallylog.h: New. * modules/pam_tally2/pam_tally2.8.xml: New. * modules/pam_tally2/pam_tally2.c: New. * modules/pam_tally2/pam_tally2_app.c: New. * modules/pam_tally2/tst-pam_tally2: New. * po/POTFILES.in: Add pam_tally2 sources.
-rw-r--r--ChangeLog16
-rw-r--r--NEWS2
-rw-r--r--configure.in1
-rw-r--r--doc/sag/Linux-PAM_SAG.xml2
-rw-r--r--doc/sag/pam_tally2.xml46
-rw-r--r--libpam/pam_static_modules.h2
-rw-r--r--modules/Makefile.am2
-rw-r--r--modules/pam_tally2/.cvsignore9
-rw-r--r--modules/pam_tally2/Makefile.am40
-rw-r--r--modules/pam_tally2/README.xml46
-rw-r--r--modules/pam_tally2/pam_tally2.8.xml439
-rw-r--r--modules/pam_tally2/pam_tally2.c985
-rw-r--r--modules/pam_tally2/pam_tally2_app.c7
-rw-r--r--modules/pam_tally2/tallylog.h52
-rwxr-xr-xmodules/pam_tally2/tst-pam_tally22
-rw-r--r--po/POTFILES.in2
16 files changed, 1652 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index bb810c41..dff2085a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2008-10-17 Tomas Mraz <t8m@centrum.cz>
+
+ * configure.in: Add modules/pam_tally2/Makefile.
+ * doc/sag/Linux-PAM_SAG.xml: Include pam_tally2.xml.
+ * doc/sag/pam_tally2.xml: New.
+ * libpam/pam_static_modules.h: Add pam_tally2 static struct.
+ * modules/Makefile.am: Add pam_tally2 directory.
+ * modules/pam_tally2/Makefile.am: New.
+ * modules/pam_tally2/README.xml: New.
+ * modules/pam_tally2/tallylog.h: New.
+ * modules/pam_tally2/pam_tally2.8.xml: New.
+ * modules/pam_tally2/pam_tally2.c: New.
+ * modules/pam_tally2/pam_tally2_app.c: New.
+ * modules/pam_tally2/tst-pam_tally2: New.
+ * po/POTFILES.in: Add pam_tally2 sources.
+
2008-10-17 Xavier Queralt Mateu <xqueralt@gmail.com>
* po/ca.po: Updated translations.
diff --git a/NEWS b/NEWS
index 144757a0..c406472f 100644
--- a/NEWS
+++ b/NEWS
@@ -15,6 +15,8 @@ Release 1.0.90
* New options for pam_lastlog to show last failed login attempt and
to disable lastlog update
* New pam_pwhistory module to store last used passwords
+* New pam_tally2 module similar to pam_tally with wordsize independent
+ tally data format
Release 1.0.2
diff --git a/configure.in b/configure.in
index 63ba9ddd..087d88cf 100644
--- a/configure.in
+++ b/configure.in
@@ -548,6 +548,7 @@ AC_CONFIG_FILES([Makefile libpam/Makefile libpamc/Makefile libpamc/test/Makefile
modules/pam_sepermit/Makefile \
modules/pam_shells/Makefile modules/pam_stress/Makefile \
modules/pam_succeed_if/Makefile modules/pam_tally/Makefile \
+ modules/pam_tally2/Makefile \
modules/pam_time/Makefile modules/pam_tty_audit/Makefile \
modules/pam_umask/Makefile \
modules/pam_unix/Makefile modules/pam_userdb/Makefile \
diff --git a/doc/sag/Linux-PAM_SAG.xml b/doc/sag/Linux-PAM_SAG.xml
index b5a1781a..a1989c9a 100644
--- a/doc/sag/Linux-PAM_SAG.xml
+++ b/doc/sag/Linux-PAM_SAG.xml
@@ -459,6 +459,8 @@ session required pam_warn.so
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
href="pam_tally.xml"/>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="pam_tally2.xml"/>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
href="pam_time.xml"/>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
href="pam_umask.xml"/>
diff --git a/doc/sag/pam_tally2.xml b/doc/sag/pam_tally2.xml
new file mode 100644
index 00000000..085003d3
--- /dev/null
+++ b/doc/sag/pam_tally2.xml
@@ -0,0 +1,46 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+ "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
+<section id='sag-pam_tally2'>
+ <title>pam_tally2 - login counter (tallying) module</title>
+ <cmdsynopsis>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="../../modules/pam_tally2/pam_tally2.8.xml" xpointer='xpointer(//cmdsynopsis[@id = "pam_tally2-cmdsynopsis1"]/*)'/>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="../../modules/pam_tally2/pam_tally2.8.xml" xpointer='xpointer(//cmdsynopsis[@id = "pam_tally2-cmdsynopsis2"]/*)'/>
+ </cmdsynopsis>
+ <section id='sag-pam_tally2-description'>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="../../modules/pam_tally2/pam_tally2.8.xml" xpointer='xpointer(//refsect1[@id = "pam_tally2-description"]/*)'/>
+ </section>
+ <section id='sag-pam_tally2-options'>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="../../modules/pam_tally2/pam_tally2.8.xml" xpointer='xpointer(//refsect1[@id = "pam_tally2-options"]/*)'/>
+ </section>
+ <section id='sag-pam_tally2-types'>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="../../modules/pam_tally2/pam_tally2.8.xml" xpointer='xpointer(//refsect1[@id = "pam_tally2-types"]/*)'/>
+ </section>
+ <section id='sag-pam_tally2-return_values'>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="../../modules/pam_tally2/pam_tally2.8.xml" xpointer='xpointer(//refsect1[@id = "pam_tally2-return_values"]/*)'/>
+ </section>
+ <section id='sag-pam_tally2-notes'>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="../../modules/pam_tally2/pam_tally2.8.xml" xpointer='xpointer(//refsect1[@id = "pam_tally2-notes"]/*)'/>
+ </section>
+ <section id='sag-pam_tally2-examples'>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="../../modules/pam_tally2/pam_tally2.8.xml" xpointer='xpointer(//refsect1[@id = "pam_tally2-examples"]/*)'/>
+ </section>
+ <section id='sag-pam_tally2-files'>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="../../modules/pam_tally2/pam_tally2.8.xml" xpointer='xpointer(//refsect1[@id = "pam_tally2-files"]/*)'/>
+ </section>
+ <section id='sag-pam_tally2-author'>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="../../modules/pam_tally2/pam_tally2.8.xml" xpointer='xpointer(//refsect1[@id = "pam_tally2-author"]/*)'/>
+ </section>
+</section>
diff --git a/libpam/pam_static_modules.h b/libpam/pam_static_modules.h
index d45f2977..2d80cecb 100644
--- a/libpam/pam_static_modules.h
+++ b/libpam/pam_static_modules.h
@@ -74,6 +74,7 @@ extern struct pam_module _pam_shells_modstruct;
extern struct pam_module _pam_stress_modstruct;
extern struct pam_module _pam_succeed_if_modstruct;
extern struct pam_module _pam_tally_modstruct;
+extern struct pam_module _pam_tally2_modstruct;
extern struct pam_module _pam_time_modstruct;
#ifdef HAVE_AUDIT_TTY_STATUS
extern struct pam_module _pam_tty_audit_modstruct;
@@ -133,6 +134,7 @@ static struct pam_module *static_modules[] = {
&_pam_stress_modstruct,
&_pam_succeed_if_modstruct,
&_pam_tally_modstruct,
+ &_pam_tally2_modstruct,
&_pam_time_modstruct,
#ifdef HAVE_AUDIT_TTY_STATUS
&_pam_tty_audit_modstruct,
diff --git a/modules/Makefile.am b/modules/Makefile.am
index f21d52e8..37d5a739 100644
--- a/modules/Makefile.am
+++ b/modules/Makefile.am
@@ -9,7 +9,7 @@ SUBDIRS = pam_access pam_cracklib pam_debug pam_deny pam_echo \
pam_mkhomedir pam_motd pam_namespace pam_nologin \
pam_permit pam_pwhistory pam_rhosts pam_rootok pam_securetty \
pam_selinux pam_sepermit pam_shells pam_stress \
- pam_succeed_if pam_tally pam_time pam_tty_audit pam_umask \
+ pam_succeed_if pam_tally pam_tally2 pam_time pam_tty_audit pam_umask \
pam_unix pam_userdb pam_warn pam_wheel pam_xauth
CLEANFILES = *~
diff --git a/modules/pam_tally2/.cvsignore b/modules/pam_tally2/.cvsignore
new file mode 100644
index 00000000..c20ebb92
--- /dev/null
+++ b/modules/pam_tally2/.cvsignore
@@ -0,0 +1,9 @@
+*.la
+*.lo
+.deps
+.libs
+Makefile
+Makefile.in
+pam_tally2
+README
+pam_tally2.8
diff --git a/modules/pam_tally2/Makefile.am b/modules/pam_tally2/Makefile.am
new file mode 100644
index 00000000..6f843e1f
--- /dev/null
+++ b/modules/pam_tally2/Makefile.am
@@ -0,0 +1,40 @@
+#
+# Copyright (c) 2005, 2006, 2007 Thorsten Kukuk <kukuk@thkukuk.de>
+# Copyright (c) 2008 Red Hat, Inc.
+#
+
+CLEANFILES = *~
+
+EXTRA_DIST = README $(MANS) $(XMLS) tst-pam_tally2
+
+man_MANS = pam_tally2.8
+XMLS = README.xml pam_tally2.8.xml
+
+TESTS = tst-pam_tally2
+
+securelibdir = $(SECUREDIR)
+secureconfdir = $(SCONFIGDIR)
+
+noinst_HEADERS = tallylog.h
+
+AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include
+
+pam_tally2_la_LDFLAGS = -no-undefined -avoid-version -module
+pam_tally2_la_LIBADD = -L$(top_builddir)/libpam -lpam $(LIBAUDIT)
+if HAVE_VERSIONING
+ pam_tally2_la_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
+endif
+
+pam_tally2_LDADD = $(LIBAUDIT)
+
+securelib_LTLIBRARIES = pam_tally2.la
+sbin_PROGRAMS = pam_tally2
+
+pam_tally2_la_SOURCES = pam_tally2.c
+pam_tally2_SOURCES = pam_tally2_app.c
+
+if ENABLE_REGENERATE_MAN
+noinst_DATA = README
+README: pam_tally2.8.xml
+-include $(top_srcdir)/Make.xml.rules
+endif
diff --git a/modules/pam_tally2/README.xml b/modules/pam_tally2/README.xml
new file mode 100644
index 00000000..aa470570
--- /dev/null
+++ b/modules/pam_tally2/README.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding='UTF-8'?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+"http://www.docbook.org/xml/4.3/docbookx.dtd"
+[
+<!--
+<!ENTITY pamaccess SYSTEM "pam_tally2.8.xml">
+-->
+]>
+
+<article>
+
+ <articleinfo>
+
+ <title>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="pam_tally2.8.xml" xpointer='xpointer(//refnamediv[@id = "pam_tally2-name"]/*)'/>
+ </title>
+
+ </articleinfo>
+
+ <section>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="pam_tally2.8.xml" xpointer='xpointer(//refsect1[@id = "pam_tally2-description"]/*)'/>
+ </section>
+
+ <section>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="pam_tally2.8.xml" xpointer='xpointer(//refsect1[@id = "pam_tally2-options"]/*)'/>
+ </section>
+
+ <section>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="pam_tally2.8.xml" xpointer='xpointer(//refsect1[@id = "pam_tally2-notes"]/*)'/>
+ </section>
+
+ <section>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="pam_tally2.8.xml" xpointer='xpointer(//refsect1[@id = "pam_tally2-examples"]/*)'/>
+ </section>
+
+ <section>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+ href="pam_tally2.8.xml" xpointer='xpointer(//refsect1[@id = "pam_tally2-author"]/*)'/>
+ </section>
+
+</article>
diff --git a/modules/pam_tally2/pam_tally2.8.xml b/modules/pam_tally2/pam_tally2.8.xml
new file mode 100644
index 00000000..dc284a1d
--- /dev/null
+++ b/modules/pam_tally2/pam_tally2.8.xml
@@ -0,0 +1,439 @@
+<?xml version="1.0" encoding='UTF-8'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+ "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
+
+<refentry id="pam_tally2">
+
+ <refmeta>
+ <refentrytitle>pam_tally2</refentrytitle>
+ <manvolnum>8</manvolnum>
+ <refmiscinfo class="sectdesc">Linux-PAM Manual</refmiscinfo>
+ </refmeta>
+
+ <refnamediv id="pam_tally2-name">
+ <refname>pam_tally2</refname>
+ <refpurpose>The login counter (tallying) module</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis id="pam_tally2-cmdsynopsis1">
+ <command>pam_tally2.so</command>
+ <arg choice="opt">
+ file=<replaceable>/path/to/counter</replaceable>
+ </arg>
+ <arg choice="opt">
+ onerr=[<replaceable>fail</replaceable>|<replaceable>succeed</replaceable>]
+ </arg>
+ <arg choice="opt">
+ magic_root
+ </arg>
+ <arg choice="opt">
+ even_deny_root
+ </arg>
+ <arg choice="opt">
+ deny=<replaceable>n</replaceable>
+ </arg>
+ <arg choice="opt">
+ lock_time=<replaceable>n</replaceable>
+ </arg>
+ <arg choice="opt">
+ unlock_time=<replaceable>n</replaceable>
+ </arg>
+ <arg choice="opt">
+ root_unlock_time=<replaceable>n</replaceable>
+ </arg>
+ <arg choice="opt">
+ audit
+ </arg>
+ <arg choice="opt">
+ silent
+ </arg>
+ <arg choice="opt">
+ no_log_info
+ </arg>
+ </cmdsynopsis>
+ <cmdsynopsis id="pam_tally2-cmdsynopsis2">
+ <command>pam_tally2</command>
+ <arg choice="opt">
+ --file <replaceable>/path/to/counter</replaceable>
+ </arg>
+ <arg choice="opt">
+ --user <replaceable>username</replaceable>
+ </arg>
+ <arg choice="opt">
+ --reset[=<replaceable>n</replaceable>]
+ </arg>
+ <arg choice="opt">
+ --quiet
+ </arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1 id="pam_tally2-description">
+
+ <title>DESCRIPTION</title>
+
+ <para>
+ This module maintains a count of attempted accesses, can
+ reset count on success, can deny access if too many attempts fail.
+ </para>
+ <para>
+ pam_tally2 comes in two parts:
+ <emphasis remap='B'>pam_tally2.so</emphasis> and
+ <command>pam_tally2</command>. The former is the PAM module and
+ the latter, a stand-alone program. <command>pam_tally2</command>
+ is an (optional) application which can be used to interrogate and
+ manipulate the counter file. It can display users' counts, set
+ individual counts, or clear all counts. Setting artificially high
+ counts may be useful for blocking users without changing their
+ passwords. For example, one might find it useful to clear all counts
+ every midnight from a cron job.
+ </para>
+ <para>
+ Normally, failed attempts to access <emphasis>root</emphasis> will
+ <emphasis remap='B'>not</emphasis> 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 <command>su</command> or
+ at the machine console (not telnet/rsh, etc), this is safe.
+ </para>
+ </refsect1>
+
+ <refsect1 id="pam_tally2-options">
+
+ <title>OPTIONS</title>
+ <variablelist>
+ <varlistentry>
+ <term>
+ GLOBAL OPTIONS
+ </term>
+ <listitem>
+ <para>
+ This can be used for <emphasis>auth</emphasis> and
+ <emphasis>account</emphasis> module types.
+ </para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <option>onerr=[<replaceable>fail</replaceable>|<replaceable>succeed</replaceable>]</option>
+ </term>
+ <listitem>
+ <para>
+ If something weird happens (like unable to open the file),
+ return with <errorcode>PAM_SUCESS</errorcode> if
+ <option>onerr=<replaceable>succeed</replaceable></option>
+ is given, else with the corresponding PAM error code.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>file=<replaceable>/path/to/counter</replaceable></option>
+ </term>
+ <listitem>
+ <para>
+ File where to keep counts. Default is
+ <filename>/var/log/tallylog</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>audit</option>
+ </term>
+ <listitem>
+ <para>
+ Will log the user name into the system log if the user is not found.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>silent</option>
+ </term>
+ <listitem>
+ <para>
+ Don't print informative messages.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>no_log_info</option>
+ </term>
+ <listitem>
+ <para>
+ Don't log informative messages via <citerefentry><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ AUTH OPTIONS
+ </term>
+ <listitem>
+ <para>
+ Authentication phase first increments attempted login counter and
+ checks if user should be denied access. If the user is authenticated
+ and the login process continues on call to <citerefentry>
+ <refentrytitle>pam_setcred</refentrytitle><manvolnum>3</manvolnum>
+ </citerefentry> it resets the attempts counter.
+ </para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <option>deny=<replaceable>n</replaceable></option>
+ </term>
+ <listitem>
+ <para>
+ Deny access if tally for this user exceeds
+ <replaceable>n</replaceable>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>lock_time=<replaceable>n</replaceable></option>
+ </term>
+ <listitem>
+ <para>
+ Always deny for <replaceable>n</replaceable> seconds
+ after failed attempt.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>unlock_time=<replaceable>n</replaceable></option>
+ </term>
+ <listitem>
+ <para>
+ Allow access after <replaceable>n</replaceable> seconds
+ after failed attempt. If this option is used the user will
+ be locked out for the specified amount of time after he
+ exceeded his maximum allowed attempts. Otherwise the
+ account is locked until the lock is removed by a manual
+ intervention of the system administrator.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>magic_root</option>
+ </term>
+ <listitem>
+ <para>
+ If the module is invoked by a user with uid=0 the
+ counter is not incremented. The sys-admin should use this
+ for user launched services, like <command>su</command>,
+ otherwise this argument should be omitted.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>no_lock_time</option>
+ </term>
+ <listitem>
+ <para>
+ Do not use the .fail_locktime field in
+ <filename>/var/log/faillog</filename> for this user.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>no_reset</option>
+ </term>
+ <listitem>
+ <para>
+ Don't reset count on successful entry, only decrement.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>even_deny_root</option>
+ </term>
+ <listitem>
+ <para>
+ Root account can become unavailable.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>root_unlock_time=<replaceable>n</replaceable></option>
+ </term>
+ <listitem>
+ <para>
+ This option implies <option>even_deny_root</option> option.
+ Allow access after <replaceable>n</replaceable> seconds
+ to root acccount after failed attempt. If this option is used
+ the root user will be locked out for the specified amount of
+ time after he exceeded his maximum allowed attempts.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </listitem>
+ </varlistentry>
+
+
+ <varlistentry>
+ <term>
+ ACCOUNT OPTIONS
+ </term>
+ <listitem>
+ <para>
+ Account phase resets attempts counter if the user is
+ <emphasis remap='B'>not</emphasis> magic root.
+ This phase can be used optionaly for services which don't call
+ <citerefentry>
+ <refentrytitle>pam_setcred</refentrytitle><manvolnum>3</manvolnum>
+ </citerefentry> correctly or if the reset should be done regardless
+ of the failure of the account phase of other modules.
+ </para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <option>magic_root</option>
+ </term>
+ <listitem>
+ <para>
+ If the module is invoked by a user with uid=0 the
+ counter is not changed. The sys-admin should use this
+ for user launched services, like <command>su</command>,
+ otherwise this argument should be omitted.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1 id="pam_tally2-types">
+ <title>MODULE TYPES PROVIDED</title>
+ <para>
+ The <option>auth</option> and <option>account</option>
+ module types are provided.
+ </para>
+ </refsect1>
+
+ <refsect1 id='pam_tally2-return_values'>
+ <title>RETURN VALUES</title>
+ <variablelist>
+ <varlistentry>
+ <term>PAM_AUTH_ERR</term>
+ <listitem>
+ <para>
+ A invalid option was given, the module was not able
+ to retrive the user name, no valid counter file
+ was found, or too many failed logins.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>PAM_SUCCESS</term>
+ <listitem>
+ <para>
+ Everything was successfull.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>PAM_USER_UNKNOWN</term>
+ <listitem>
+ <para>
+ User not known.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1 id='pam_tally2-notes'>
+ <title>NOTES</title>
+ <para>
+ pam_tally2 is not compatible with the old pam_tally faillog file format.
+ This is caused by requirement of compatibility of the tallylog file
+ format between 32bit and 64bit architectures on multiarch systems.
+ </para>
+ <para>
+ There is no setuid wrapper for access to the data file such as when the
+ <emphasis remap='B'>pam_tally2.so</emphasis> module is called from
+ xscreensaver. As this would make it impossible to share PAM configuration
+ with such services the following workaround is used: If the data file
+ cannot be opened because of insufficient permissions
+ (<errorcode>EPERM</errorcode>) the module returns
+ <errorcode>PAM_IGNORE</errorcode>.
+ </para>
+ </refsect1>
+
+ <refsect1 id='pam_tally2-examples'>
+ <title>EXAMPLES</title>
+ <para>
+ Add the following line to <filename>/etc/pam.d/login</filename> to
+ lock the account after 4 failed logins. Root account will be locked
+ as well. The accounts will be automatically unlocked after 20 minutes.
+ The module does not have to be called in the account phase because the
+ <command>login</command> calls <citerefentry>
+ <refentrytitle>pam_setcred</refentrytitle><manvolnum>3</manvolnum>
+ </citerefentry> correctly.
+ </para>
+ <programlisting>
+auth required pam_securetty.so
+auth required pam_tally2.so deny=4 even_deny_root unlock_time=1200
+auth required pam_env.so
+auth required pam_unix.so
+auth required pam_nologin.so
+account required pam_unix.so
+password required pam_unix.so
+session required pam_limits.so
+session required pam_unix.so
+session required pam_lastlog.so nowtmp
+session optional pam_mail.so standard
+ </programlisting>
+ </refsect1>
+
+ <refsect1 id="pam_tally2-files">
+ <title>FILES</title>
+ <variablelist>
+ <varlistentry>
+ <term><filename>/var/log/tallylog</filename></term>
+ <listitem>
+ <para>failure count logging file</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1 id='pam_tally2-see_also'>
+ <title>SEE ALSO</title>
+ <para>
+ <citerefentry>
+ <refentrytitle>pam.conf</refentrytitle><manvolnum>5</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>pam.d</refentrytitle><manvolnum>5</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>pam</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>
+ </para>
+ </refsect1>
+
+ <refsect1 id='pam_tally2-author'>
+ <title>AUTHOR</title>
+ <para>
+ pam_tally was written by Tim Baverstock and Tomas Mraz.
+ </para>
+ </refsect1>
+
+</refentry>
+
diff --git a/modules/pam_tally2/pam_tally2.c b/modules/pam_tally2/pam_tally2.c
new file mode 100644
index 00000000..9ae3180d
--- /dev/null
+++ b/modules/pam_tally2/pam_tally2.c
@@ -0,0 +1,985 @@
+/*
+ * pam_tally2.c
+ *
+ */
+
+
+/* By Tim Baverstock <warwick@mmm.co.uk>, Multi Media Machine Ltd.
+ * 5 March 1997
+ *
+ * Stuff stolen from pam_rootok and pam_listfile
+ *
+ * Changes by Tomas Mraz <tmraz@redhat.com> 5 January 2005, 26 January 2006
+ * Audit option added for Tomas patch by Sebastien Tricaud <toady@gscore.org> 13 January 2005
+ * Portions Copyright 2006, Red Hat, Inc.
+ * Portions Copyright 1989 - 1993, Julianne Frances Haugh
+ * All rights reserved.
+ *
+ * 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, this list of conditions and the following disclaimer.
+ * 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. Neither the name of Julianne F. Haugh nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``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 JULIE HAUGH OR CONTRIBUTORS 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"
+
+#if defined(MAIN) && defined(MEMORY_DEBUG)
+# undef exit
+#endif /* defined(MAIN) && defined(MEMORY_DEBUG) */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <pwd.h>
+#include <time.h>
+#include <stdint.h>
+#include <errno.h>
+#ifdef HAVE_LIBAUDIT
+#include <libaudit.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include "tallylog.h"
+
+#ifndef TRUE
+#define TRUE 1L
+#define FALSE 0L
+#endif
+
+#ifndef HAVE_FSEEKO
+#define fseeko fseek
+#endif
+
+/*
+ * here, we make a definition for the externally accessible function
+ * in this file (this definition is required for static a module
+ * but strongly encouraged generally) it is used to instruct the
+ * modules include file to define the function prototypes.
+ */
+
+#ifndef MAIN
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+/* #define PAM_SM_SESSION */
+/* #define PAM_SM_PASSWORD */
+
+#include <security/pam_modutil.h>
+#include <security/pam_ext.h>
+#endif
+#include <security/pam_modules.h>
+
+/*---------------------------------------------------------------------*/
+
+#define DEFAULT_LOGFILE "/var/log/tallylog"
+#define MODULE_NAME "pam_tally2"
+
+#define tally_t uint16_t
+#define TALLY_HI ((tally_t)~0L)
+
+struct tally_options {
+ const char *filename;
+ tally_t deny;
+ long lock_time;
+ long unlock_time;
+ long root_unlock_time;
+ unsigned int ctrl;
+};
+
+#define PHASE_UNKNOWN 0
+#define PHASE_AUTH 1
+#define PHASE_ACCOUNT 2
+#define PHASE_SESSION 3
+
+#define OPT_MAGIC_ROOT 01
+#define OPT_FAIL_ON_ERROR 02
+#define OPT_DENY_ROOT 04
+#define OPT_QUIET 040
+#define OPT_AUDIT 0100
+#define OPT_NOLOGNOTICE 0400
+
+
+/*---------------------------------------------------------------------*/
+
+/* some syslogging */
+
+#ifdef MAIN
+#define pam_syslog tally_log
+static void
+tally_log (const pam_handle_t *pamh UNUSED, int priority UNUSED,
+ const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ fprintf(stderr, "%s: ", MODULE_NAME);
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr,"\n");
+ va_end(args);
+}
+
+#define pam_modutil_getpwnam(pamh, user) getpwnam(user)
+#endif
+
+/*---------------------------------------------------------------------*/
+
+/* --- Support function: parse arguments --- */
+
+#ifndef MAIN
+
+static void
+log_phase_no_auth(pam_handle_t *pamh, int phase, const char *argv)
+{
+ if ( phase != PHASE_AUTH ) {
+ pam_syslog(pamh, LOG_ERR,
+ "option %s allowed in auth phase only", argv);
+ }
+}
+
+static int
+tally_parse_args(pam_handle_t *pamh, struct tally_options *opts,
+ int phase, int argc, const char **argv)
+{
+ memset(opts, 0, sizeof(*opts));
+ opts->filename = DEFAULT_LOGFILE;
+ opts->ctrl = OPT_FAIL_ON_ERROR;
+ opts->root_unlock_time = -1;
+
+ for ( ; argc-- > 0; ++argv ) {
+
+ if ( ! strncmp( *argv, "file=", 5 ) ) {
+ const char *from = *argv + 5;
+ if ( *from!='/' ) {
+ pam_syslog(pamh, LOG_ERR,
+ "filename not /rooted; %s", *argv);
+ return PAM_AUTH_ERR;
+ }
+ opts->filename = from;
+ }
+ else if ( ! strcmp( *argv, "onerr=fail" ) ) {
+ opts->ctrl |= OPT_FAIL_ON_ERROR;
+ }
+ else if ( ! strcmp( *argv, "onerr=succeed" ) ) {
+ opts->ctrl &= ~OPT_FAIL_ON_ERROR;
+ }
+ else if ( ! strcmp( *argv, "magic_root" ) ) {
+ opts->ctrl |= OPT_MAGIC_ROOT;
+ }
+ else if ( ! strcmp( *argv, "even_deny_root_account" ) ||
+ ! strcmp( *argv, "even_deny_root" ) ) {
+ log_phase_no_auth(pamh, phase, *argv);
+ opts->ctrl |= OPT_DENY_ROOT;
+ }
+ else if ( ! strncmp( *argv, "deny=", 5 ) ) {
+ log_phase_no_auth(pamh, phase, *argv);
+ if ( sscanf((*argv)+5,"%hu",&opts->deny) != 1 ) {
+ pam_syslog(pamh, LOG_ERR, "bad number supplied: %s", *argv);
+ return PAM_AUTH_ERR;
+ }
+ }
+ else if ( ! strncmp( *argv, "lock_time=", 10 ) ) {
+ log_phase_no_auth(pamh, phase, *argv);
+ if ( sscanf((*argv)+10,"%ld",&opts->lock_time) != 1 ) {
+ pam_syslog(pamh, LOG_ERR, "bad number supplied: %s", *argv);
+ return PAM_AUTH_ERR;
+ }
+ }
+ else if ( ! strncmp( *argv, "unlock_time=", 12 ) ) {
+ log_phase_no_auth(pamh, phase, *argv);
+ if ( sscanf((*argv)+12,"%ld",&opts->unlock_time) != 1 ) {
+ pam_syslog(pamh, LOG_ERR, "bad number supplied: %s", *argv);
+ return PAM_AUTH_ERR;
+ }
+ }
+ else if ( ! strncmp( *argv, "root_unlock_time=", 17 ) ) {
+ log_phase_no_auth(pamh, phase, *argv);
+ if ( sscanf((*argv)+17,"%ld",&opts->root_unlock_time) != 1 ) {
+ pam_syslog(pamh, LOG_ERR, "bad number supplied: %s", *argv);
+ return PAM_AUTH_ERR;
+ }
+ opts->ctrl |= OPT_DENY_ROOT; /* even_deny_root implied */
+ }
+ else if ( ! strcmp( *argv, "quiet" ) ||
+ ! strcmp ( *argv, "silent")) {
+ opts->ctrl |= OPT_QUIET;
+ }
+ else if ( ! strcmp ( *argv, "no_log_info") ) {
+ opts->ctrl |= OPT_NOLOGNOTICE;
+ }
+ else if ( ! strcmp ( *argv, "audit") ) {
+ opts->ctrl |= OPT_AUDIT;
+ }
+ else {
+ pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
+ }
+ }
+
+ if (opts->root_unlock_time == -1)
+ opts->root_unlock_time = opts->unlock_time;
+
+ return PAM_SUCCESS;
+}
+
+#endif /* #ifndef MAIN */
+
+/*---------------------------------------------------------------------*/
+
+/* --- Support function: get uid (and optionally username) from PAM or
+ cline_user --- */
+
+#ifdef MAIN
+static char *cline_user=0; /* cline_user is used in the administration prog */
+#endif
+
+static int
+pam_get_uid(pam_handle_t *pamh, uid_t *uid, const char **userp, struct tally_options *opts)
+{
+ const char *user = NULL;
+ struct passwd *pw;
+
+#ifdef MAIN
+ user = cline_user;
+#else
+ if ((pam_get_user( pamh, &user, NULL )) != PAM_SUCCESS) {
+ user = NULL;
+ }
+#endif
+
+ if ( !user || !*user ) {
+ pam_syslog(pamh, LOG_ERR, "pam_get_uid; user?");
+ return PAM_AUTH_ERR;
+ }
+
+ if ( ! ( pw = pam_modutil_getpwnam( pamh, user ) ) ) {
+ opts->ctrl & OPT_AUDIT ?
+ pam_syslog(pamh, LOG_ERR, "pam_get_uid; no such user %s", user) :
+ pam_syslog(pamh, LOG_ERR, "pam_get_uid; no such user");
+ return PAM_USER_UNKNOWN;
+ }
+
+ if ( uid ) *uid = pw->pw_uid;
+ if ( userp ) *userp = user;
+ return PAM_SUCCESS;
+}
+
+/*---------------------------------------------------------------------*/
+
+/* --- Support functions: set/get tally data --- */
+
+#ifndef MAIN
+
+static void
+_cleanup(pam_handle_t *pamh UNUSED, void *data, int error_status UNUSED)
+{
+ free(data);
+}
+
+
+static void
+tally_set_data( pam_handle_t *pamh, time_t oldtime )
+{
+ time_t *data;
+
+ if ( (data=malloc(sizeof(time_t))) != NULL ) {
+ *data = oldtime;
+ pam_set_data(pamh, MODULE_NAME, (void *)data, _cleanup);
+ }
+}
+
+static int
+tally_get_data( pam_handle_t *pamh, time_t *oldtime )
+{
+ int rv;
+ const void *data;
+
+ rv = pam_get_data(pamh, MODULE_NAME, &data);
+ if ( rv == PAM_SUCCESS && data != NULL && oldtime != NULL ) {
+ *oldtime = *(const time_t *)data;
+ pam_set_data(pamh, MODULE_NAME, NULL, NULL);
+ }
+ else {
+ rv = -1;
+ *oldtime = 0;
+ }
+ return rv;
+}
+#endif /* #ifndef MAIN */
+
+/*---------------------------------------------------------------------*/
+
+/* --- Support function: open/create tallyfile and return tally for uid --- */
+
+/* If on entry tallyfile doesn't exist, creation is attempted. */
+
+static int
+get_tally(pam_handle_t *pamh, uid_t uid, const char *filename,
+ FILE **tfile, struct tallylog *tally)
+{
+ struct stat fileinfo;
+ int lstat_ret;
+
+ lstat_ret = lstat(filename, &fileinfo);
+ if (lstat_ret) {
+ int save_errno;
+ int oldmask = umask(077);
+ *tfile=fopen(filename, "a");
+ save_errno = errno;
+ /* Create file, or append-open in pathological case. */
+ umask(oldmask);
+ if ( !*tfile ) {
+#ifndef MAIN
+ if (save_errno == EPERM) {
+ return PAM_IGNORE; /* called with insufficient access rights */
+ }
+#endif
+ errno = save_errno;
+ pam_syslog(pamh, LOG_ALERT, "Couldn't create %s: %m", filename);
+ return PAM_AUTH_ERR;
+ }
+ lstat_ret = fstat(fileno(*tfile),&fileinfo);
+ fclose(*tfile);
+ *tfile = NULL;
+ }
+
+ if ( lstat_ret ) {
+ pam_syslog(pamh, LOG_ALERT, "Couldn't stat %s", filename);
+ return PAM_AUTH_ERR;
+ }
+
+ if ((fileinfo.st_mode & S_IWOTH) || !S_ISREG(fileinfo.st_mode)) {
+ /* If the file is world writable or is not a
+ normal file, return error */
+ pam_syslog(pamh, LOG_ALERT,
+ "%s is either world writable or not a normal file",
+ filename);
+ return PAM_AUTH_ERR;
+ }
+
+ if (!(*tfile = fopen(filename, "r+"))) {
+#ifndef MAIN
+ if (errno == EPERM) /* called with insufficient access rights */
+ return PAM_IGNORE;
+#endif
+ pam_syslog(pamh, LOG_ALERT, "Error opening %s for update: %m", filename);
+
+ return PAM_AUTH_ERR;
+ }
+
+ if (fseeko(*tfile, (off_t)uid*(off_t)sizeof(*tally), SEEK_SET)) {
+ pam_syslog(pamh, LOG_ALERT, "fseek failed for %s: %m", filename);
+ fclose(*tfile);
+ *tfile = NULL;
+ return PAM_AUTH_ERR;
+ }
+
+ if (fileinfo.st_size < (off_t)(uid+1)*(off_t)sizeof(*tally)) {
+ memset(tally, 0, sizeof(*tally));
+ } else if (fread(tally, sizeof(*tally), 1, *tfile) == 0) {
+ memset(tally, 0, sizeof(*tally));
+ /* Shouldn't happen */
+ }
+
+ tally->fail_line[sizeof(tally->fail_line)-1] = '\0';
+
+ return PAM_SUCCESS;
+}
+
+/*---------------------------------------------------------------------*/
+
+/* --- Support function: update and close tallyfile with tally!=TALLY_HI --- */
+
+static int
+set_tally(pam_handle_t *pamh, uid_t uid,
+ const char *filename, FILE **tfile, struct tallylog *tally)
+{
+ if (tally->fail_cnt != TALLY_HI) {
+ if (fseeko(*tfile, (off_t)uid * sizeof(*tally), SEEK_SET)) {
+ pam_syslog(pamh, LOG_ALERT, "fseek failed for %s: %m", filename);
+ return PAM_AUTH_ERR;
+ }
+ if (fwrite(tally, sizeof(*tally), 1, *tfile) == 0) {
+ pam_syslog(pamh, LOG_ALERT, "update (fwrite) failed for %s: %m", filename);
+ return PAM_AUTH_ERR;
+ }
+ }
+
+ if (fclose(*tfile)) {
+ *tfile = NULL;
+ pam_syslog(pamh, LOG_ALERT, "update (fclose) failed for %s: %m", filename);
+ return PAM_AUTH_ERR;
+ }
+ *tfile=NULL;
+ return PAM_SUCCESS;
+}
+
+/*---------------------------------------------------------------------*/
+
+/* --- PAM bits --- */
+
+#ifndef MAIN
+
+#define RETURN_ERROR(i) return ((opts->ctrl & OPT_FAIL_ON_ERROR)?(i):(PAM_SUCCESS))
+
+/*---------------------------------------------------------------------*/
+
+static int
+tally_check (tally_t oldcnt, time_t oldtime, pam_handle_t *pamh, uid_t uid,
+ const char *user, struct tally_options *opts,
+ struct tallylog *tally)
+{
+ int rv = PAM_SUCCESS;
+#ifdef HAVE_LIBAUDIT
+ char buf[64];
+ int audit_fd = -1;
+#endif
+
+ if ((opts->ctrl & OPT_MAGIC_ROOT) && getuid() == 0) {
+ return PAM_SUCCESS;
+ }
+ /* magic_root skips tally check */
+#ifdef HAVE_LIBAUDIT
+ 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;
+#endif
+ if (opts->deny != 0 && /* deny==0 means no deny */
+ tally->fail_cnt > opts->deny && /* tally>deny means exceeded */
+ ((opts->ctrl & OPT_DENY_ROOT) || uid)) { /* even_deny stops uid check */
+#ifdef HAVE_LIBAUDIT
+ if (tally->fail_cnt == opts->deny+1) {
+ /* First say that max number was hit. */
+ snprintf(buf, sizeof(buf), "pam_tally2 uid=%u ", uid);
+ audit_log_user_message(audit_fd, AUDIT_ANOM_LOGIN_FAILURES, buf,
+ NULL, NULL, NULL, 1);
+ }
+#endif
+ if (uid) {
+ /* Unlock time check */
+ if (opts->unlock_time && oldtime) {
+ if (opts->unlock_time + oldtime <= time(NULL)) {
+ /* ignore deny check after unlock_time elapsed */
+#ifdef HAVE_LIBAUDIT
+ snprintf(buf, sizeof(buf), "pam_tally2 uid=%u ", uid);
+ audit_log_user_message(audit_fd, AUDIT_RESP_ACCT_UNLOCK_TIMED, buf,
+ NULL, NULL, NULL, 1);
+#endif
+ rv = PAM_SUCCESS;
+ goto cleanup;
+ }
+ }
+ } else {
+ /* Root unlock time check */
+ if (opts->root_unlock_time && oldtime) {
+ if (opts->root_unlock_time + oldtime <= time(NULL)) {
+ /* ignore deny check after unlock_time elapsed */
+#ifdef HAVE_LIBAUDIT
+ snprintf(buf, sizeof(buf), "pam_tally2 uid=%u ", uid);
+ audit_log_user_message(audit_fd, AUDIT_RESP_ACCT_UNLOCK_TIMED, buf,
+ NULL, NULL, NULL, 1);
+#endif
+ rv = PAM_SUCCESS;
+ goto cleanup;
+ }
+ }
+ }
+
+#ifdef HAVE_LIBAUDIT
+ if (tally->fail_cnt == opts->deny+1) {
+ /* First say that max number was hit. */
+ audit_log_user_message(audit_fd, AUDIT_RESP_ACCT_LOCK, buf,
+ NULL, NULL, NULL, 1);
+ }
+#endif
+
+ if (!(opts->ctrl & OPT_QUIET)) {
+ pam_info(pamh, _("Account locked due to %hu failed logins"),
+ tally->fail_cnt);
+ }
+ if (!(opts->ctrl & OPT_NOLOGNOTICE)) {
+ pam_syslog(pamh, LOG_NOTICE,
+ "user %s (%lu) tally %hu, deny %hu",
+ user, (unsigned long)uid, tally->fail_cnt, opts->deny);
+ }
+ rv = PAM_AUTH_ERR; /* Only unconditional failure */
+ goto cleanup;
+ }
+
+ /* Lock time check */
+ if (opts->lock_time && oldtime) {
+ if (opts->lock_time + oldtime > time(NULL)) {
+ /* don't increase fail_cnt or update fail_time when
+ lock_time applies */
+ tally->fail_cnt = oldcnt;
+ tally->fail_time = oldtime;
+
+ if (!(opts->ctrl & OPT_QUIET)) {
+ pam_info(pamh, _("Account temporary locked (%ld seconds left)"),
+ oldtime+opts->lock_time-time(NULL));
+ }
+ if (!(opts->ctrl & OPT_NOLOGNOTICE)) {
+ pam_syslog(pamh, LOG_NOTICE,
+ "user %s (%lu) has time limit [%lds left]"
+ " since last failure.",
+ user, (unsigned long)uid,
+ oldtime+opts->lock_time-time(NULL));
+ }
+ rv = PAM_AUTH_ERR;
+ goto cleanup;
+ }
+ }
+
+cleanup:
+#ifdef HAVE_LIBAUDIT
+ if (audit_fd != -1) {
+ close(audit_fd);
+ }
+#endif
+ return rv;
+}
+
+/* --- tally bump function: bump tally for uid by (signed) inc --- */
+
+static int
+tally_bump (int inc, time_t *oldtime, pam_handle_t *pamh,
+ uid_t uid, const char *user, struct tally_options *opts)
+{
+ struct tallylog tally;
+ tally_t oldcnt;
+ FILE *tfile = NULL;
+ const void *remote_host = NULL;
+ int i, rv;
+
+ tally.fail_cnt = 0; /* !TALLY_HI --> Log opened for update */
+
+ i = get_tally(pamh, uid, opts->filename, &tfile, &tally);
+ if (i != PAM_SUCCESS) {
+ if (tfile)
+ fclose(tfile);
+ RETURN_ERROR(i);
+ }
+
+ /* to remember old fail time (for locktime) */
+ if (oldtime) {
+ *oldtime = (time_t)tally.fail_time;
+ }
+
+ tally.fail_time = time(NULL);
+
+ (void) pam_get_item(pamh, PAM_RHOST, &remote_host);
+ if (!remote_host) {
+ (void) pam_get_item(pamh, PAM_TTY, &remote_host);
+ if (!remote_host) {
+ remote_host = "unknown";
+ }
+ }
+
+ strncpy(tally.fail_line, remote_host,
+ sizeof(tally.fail_line)-1);
+ tally.fail_line[sizeof(tally.fail_line)-1] = 0;
+
+ oldcnt = tally.fail_cnt;
+
+ if (!(opts->ctrl & OPT_MAGIC_ROOT) || getuid()) {
+ /* magic_root doesn't change tally */
+ tally.fail_cnt += inc;
+
+ if (tally.fail_cnt == TALLY_HI) { /* Overflow *and* underflow. :) */
+ tally.fail_cnt -= inc;
+ pam_syslog(pamh, LOG_ALERT, "Tally %sflowed for user %s",
+ (inc<0)?"under":"over",user);
+ }
+ }
+
+ rv = tally_check(oldcnt, *oldtime, pamh, uid, user, opts, &tally);
+
+ i = set_tally(pamh, uid, opts->filename, &tfile, &tally);
+ if (i != PAM_SUCCESS) {
+ if (tfile)
+ fclose(tfile);
+ if (rv == PAM_SUCCESS)
+ RETURN_ERROR( i );
+ /* fallthrough */
+ }
+
+ return rv;
+}
+
+static int
+tally_reset (pam_handle_t *pamh, uid_t uid, struct tally_options *opts)
+{
+ struct tallylog tally;
+ FILE *tfile = NULL;
+ int i;
+
+ /* resets only if not magic root */
+
+ if ((opts->ctrl & OPT_MAGIC_ROOT) && getuid() == 0) {
+ return PAM_SUCCESS;
+ }
+
+ tally.fail_cnt = 0; /* !TALLY_HI --> Log opened for update */
+
+ i=get_tally(pamh, uid, opts->filename, &tfile, &tally);
+ if (i != PAM_SUCCESS) {
+ if (tfile)
+ fclose(tfile);
+ RETURN_ERROR(i);
+ }
+
+ memset(&tally, 0, sizeof(tally));
+
+ i=set_tally(pamh, uid, opts->filename, &tfile, &tally);
+ if (i != PAM_SUCCESS) {
+ if (tfile)
+ fclose(tfile);
+ RETURN_ERROR(i);
+ }
+
+ return PAM_SUCCESS;
+}
+
+/*---------------------------------------------------------------------*/
+
+/* --- authentication management functions (only) --- */
+
+PAM_EXTERN int
+pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED,
+ int argc, const char **argv)
+{
+ int
+ rv;
+ time_t
+ oldtime = 0;
+ struct tally_options
+ options, *opts = &options;
+ uid_t
+ uid;
+ const char
+ *user;
+
+ rv = tally_parse_args(pamh, opts, PHASE_AUTH, argc, argv);
+ if (rv != PAM_SUCCESS)
+ RETURN_ERROR(rv);
+
+ if (flags & PAM_SILENT)
+ opts->ctrl |= OPT_QUIET;
+
+ rv = pam_get_uid(pamh, &uid, &user, opts);
+ if (rv != PAM_SUCCESS)
+ RETURN_ERROR(rv);
+
+ rv = tally_bump(1, &oldtime, pamh, uid, user, opts);
+
+ tally_set_data(pamh, oldtime);
+
+ return rv;
+}
+
+PAM_EXTERN int
+pam_sm_setcred(pam_handle_t *pamh, int flags UNUSED,
+ int argc, const char **argv)
+{
+ int
+ rv;
+ time_t
+ oldtime = 0;
+ struct tally_options
+ options, *opts = &options;
+ uid_t
+ uid;
+ const char
+ *user;
+
+ rv = tally_parse_args(pamh, opts, PHASE_AUTH, argc, argv);
+ if ( rv != PAM_SUCCESS )
+ RETURN_ERROR( rv );
+
+ rv = pam_get_uid(pamh, &uid, &user, opts);
+ if ( rv != PAM_SUCCESS )
+ RETURN_ERROR( rv );
+
+ if ( tally_get_data(pamh, &oldtime) != 0 )
+ /* no data found */
+ return PAM_SUCCESS;
+
+ return tally_reset(pamh, uid, opts);
+}
+
+/*---------------------------------------------------------------------*/
+
+/* --- authentication management functions (only) --- */
+
+/* To reset failcount of user on successfull login */
+
+PAM_EXTERN int
+pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED,
+ int argc, const char **argv)
+{
+ int
+ rv;
+ time_t
+ oldtime = 0;
+ struct tally_options
+ options, *opts = &options;
+ uid_t
+ uid;
+ const char
+ *user;
+
+ rv = tally_parse_args(pamh, opts, PHASE_ACCOUNT, argc, argv);
+ if ( rv != PAM_SUCCESS )
+ RETURN_ERROR( rv );
+
+ rv = pam_get_uid(pamh, &uid, &user, opts);
+ if ( rv != PAM_SUCCESS )
+ RETURN_ERROR( rv );
+
+ if ( tally_get_data(pamh, &oldtime) != 0 )
+ /* no data found */
+ return PAM_SUCCESS;
+
+ return tally_reset(pamh, uid, opts);
+}
+
+/*-----------------------------------------------------------------------*/
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_tally_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 */
+
+/*-----------------------------------------------------------------------*/
+
+#else /* #ifndef MAIN */
+
+static const char *cline_filename = DEFAULT_LOGFILE;
+static tally_t cline_reset = TALLY_HI; /* Default is `interrogate only' */
+static int cline_quiet = 0;
+
+/*
+ * Not going to link with pamlib just for these.. :)
+ */
+
+static const char *
+pam_errors( int i )
+{
+ switch (i) {
+ case PAM_AUTH_ERR: return _("Authentication error");
+ case PAM_SERVICE_ERR: return _("Service error");
+ case PAM_USER_UNKNOWN: return _("Unknown user");
+ default: return _("Unknown error");
+ }
+}
+
+static int
+getopts( char **argv )
+{
+ const char *pname = *argv;
+ for ( ; *argv ; (void)(*argv && ++argv) ) {
+ if ( !strcmp (*argv,"--file") ) cline_filename=*++argv;
+ else if ( !strcmp(*argv,"-f") ) cline_filename=*++argv;
+ else if ( !strncmp(*argv,"--file=",7) ) cline_filename=*argv+7;
+ else if ( !strcmp (*argv,"--user") ) cline_user=*++argv;
+ else if ( !strcmp (*argv,"-u") ) cline_user=*++argv;
+ else if ( !strncmp(*argv,"--user=",7) ) cline_user=*argv+7;
+ else if ( !strcmp (*argv,"--reset") ) cline_reset=0;
+ else if ( !strcmp (*argv,"-r") ) cline_reset=0;
+ else if ( !strncmp(*argv,"--reset=",8)) {
+ if ( sscanf(*argv+8,"%hu",&cline_reset) != 1 )
+ fprintf(stderr,_("%s: Bad number given to --reset=\n"),pname), exit(0);
+ }
+ else if ( !strcmp (*argv,"--quiet") ) cline_quiet=1;
+ else {
+ fprintf(stderr,_("%s: Unrecognised option %s\n"),pname,*argv);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static void
+print_one(const struct tallylog *tally, uid_t uid)
+{
+ static int once;
+ char *cp;
+ time_t fail_time;
+ struct tm *tm;
+ struct passwd *pwent;
+ const char *username = "[NONAME]";
+ char ptime[80];
+
+ pwent = getpwuid(uid);
+ fail_time = tally->fail_time;
+ tm = localtime(&fail_time);
+ strftime (ptime, sizeof (ptime), "%D %H:%M:%S", tm);
+ cp = ptime;
+ if (pwent) {
+ username = pwent->pw_name;
+ }
+ if (!once) {
+ printf (_("Login Failures Latest failure From\n"));
+ once++;
+ }
+ printf ("%-15.15s %5hu ", username, tally->fail_cnt);
+ if (tally->fail_time) {
+ printf ("%-17.17s %s", cp, tally->fail_line);
+ }
+ putchar ('\n');
+}
+
+int
+main( int argc UNUSED, char **argv )
+{
+ struct tallylog tally;
+
+ if ( ! getopts( argv+1 ) ) {
+ printf(_("%s: [-f rooted-filename] [--file rooted-filename]\n"
+ " [-u username] [--user username]\n"
+ " [-r] [--reset[=n]] [--quiet]\n"),
+ *argv);
+ exit(2);
+ }
+
+ umask(077);
+
+ /*
+ * Major difference between individual user and all users:
+ * --user just handles one user, just like PAM.
+ * without --user it handles all users, sniffing cline_filename for nonzeros
+ */
+
+ if ( cline_user ) {
+ uid_t uid;
+ FILE *tfile=0;
+ struct tally_options opts;
+ int i;
+
+ memset(&opts, 0, sizeof(opts));
+ opts.ctrl = OPT_AUDIT;
+ i=pam_get_uid(NULL, &uid, NULL, &opts);
+ if ( i != PAM_SUCCESS ) {
+ fprintf(stderr,"%s: %s\n",*argv,pam_errors(i));
+ exit(1);
+ }
+
+ i=get_tally(NULL, uid, cline_filename, &tfile, &tally);
+ if ( i != PAM_SUCCESS ) {
+ if (tfile)
+ fclose(tfile);
+ fprintf(stderr, "%s: %s\n", *argv, pam_errors(i));
+ exit(1);
+ }
+
+ if ( !cline_quiet )
+ print_one(&tally, uid);
+
+ if (cline_reset != TALLY_HI) {
+#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, NULL, 1);
+ if (audit_fd >=0)
+ close(audit_fd);
+#endif
+ if (cline_reset == 0) {
+ memset(&tally, 0, sizeof(tally));
+ } else {
+ tally.fail_cnt = cline_reset;
+ }
+ i=set_tally(NULL, uid, cline_filename, &tfile, &tally);
+ if (i != PAM_SUCCESS) {
+ if (tfile) fclose(tfile);
+ fprintf(stderr,"%s: %s\n",*argv,pam_errors(i));
+ exit(1);
+ }
+ } else {
+ fclose(tfile);
+ }
+ }
+ else /* !cline_user (ie, operate on all users) */ {
+ FILE *tfile=fopen(cline_filename, "r");
+ uid_t uid=0;
+ if (!tfile && cline_reset != 0) {
+ perror(*argv);
+ exit(1);
+ }
+
+ for ( ; tfile && !feof(tfile); uid++ ) {
+ if ( !fread(&tally, sizeof(tally), 1, tfile)
+ || !tally.fail_cnt ) {
+ continue;
+ }
+ print_one(&tally, uid);
+ }
+ if (tfile)
+ fclose(tfile);
+ if ( cline_reset!=0 && cline_reset!=TALLY_HI ) {
+ fprintf(stderr,_("%s: Can't reset all users to non-zero\n"),*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, NULL, 1);
+ if (audit_fd >=0)
+ close(audit_fd);
+#endif
+ tfile=fopen(cline_filename, "w");
+ if ( !tfile ) perror(*argv), exit(0);
+ fclose(tfile);
+ }
+ }
+ return 0;
+}
+
+
+#endif /* #ifndef MAIN */
diff --git a/modules/pam_tally2/pam_tally2_app.c b/modules/pam_tally2/pam_tally2_app.c
new file mode 100644
index 00000000..681ed690
--- /dev/null
+++ b/modules/pam_tally2/pam_tally2_app.c
@@ -0,0 +1,7 @@
+/*
+ # This seemed like such a good idea at the time. :)
+ */
+
+#define MAIN
+#include "pam_tally2.c"
+
diff --git a/modules/pam_tally2/tallylog.h b/modules/pam_tally2/tallylog.h
new file mode 100644
index 00000000..596b1dac
--- /dev/null
+++ b/modules/pam_tally2/tallylog.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2006, Red Hat, Inc.
+ * All rights reserved.
+ *
+ * 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, this list of conditions and the following disclaimer.
+ * 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. Neither the name of Red Hat, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RED HAT, INC. AND CONTRIBUTORS ``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 JULIE HAUGH OR CONTRIBUTORS 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.
+ */
+
+/*
+ * tallylog.h - login failure data file format
+ *
+ * The new login failure file is not compatible with the old faillog(8) format
+ * Each record in the file represents a separate UID and the file
+ * is indexed in that fashion.
+ */
+
+
+#ifndef _TALLYLOG_H
+#define _TALLYLOG_H
+
+#include <stdint.h>
+
+struct tallylog {
+ char fail_line[52]; /* rhost or tty of last failure */
+ uint16_t reserved; /* reserved for future use */
+ uint16_t fail_cnt; /* failures since last success */
+ uint64_t fail_time; /* time of last failure */
+};
+/* 64 bytes / entry */
+
+#endif
diff --git a/modules/pam_tally2/tst-pam_tally2 b/modules/pam_tally2/tst-pam_tally2
new file mode 100755
index 00000000..83c71f41
--- /dev/null
+++ b/modules/pam_tally2/tst-pam_tally2
@@ -0,0 +1,2 @@
+#!/bin/sh
+../../tests/tst-dlopen .libs/pam_tally2.so
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 39889b06..5ca1caa9 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -71,6 +71,8 @@
./modules/pam_succeed_if/pam_succeed_if.c
./modules/pam_tally/pam_tally_app.c
./modules/pam_tally/pam_tally.c
+./modules/pam_tally2/pam_tally2_app.c
+./modules/pam_tally2/pam_tally2.c
./modules/pam_time/pam_time.c
./modules/pam_tty_audit/pam_tty_audit.c
./modules/pam_umask/pam_umask.c