Package: libpam-modules
Version: 1.1.8-3.7
Severity: wishlist
Tags: upstream

Dear Maintainer,

I was not able to configure an automatic shutdown on my laptop like ios have
one, and android has one, too with lockdown. To be clear, this is a security
feature
especially designed for devices that have a high risk of being stolen, a
powered down
Mobile device has much less attach surface as a powered up one because of its
disk encryption.
I did wait for some feedback from upstream for a few days now, so I decided to
add an additional
wishlist bug for this feature to my favorite Distribution.

I made two patches to pam_tally2:

The first is a cmd_onerr=/sbin/powerdown which is called whenever a user is
locked out.
With a standard systemd installation this will cause the Computer to powerdown.

The second is to allow user access to the tallylog with an extra parameter
called user_access. The parameter changes the behavior of
file=/var/log/tallylog
expecting a directory instead where every user has its own
tallylogfile below --> /var/log/tallylog/username. This makes it possible to
run pam_tally2 with screenlock programs like i3lock and gnome-screensaver.
This would also be a solution to #524866


After installing my modified pam-tally2 module
and creation of a tallylogdir:

mkdir /var/log/tallylog
chmod 711 /var/log/tallylog


and adding the following line to /etc/pam.d/common-auth:

auth required pam_tally2.so deny=4 user_access even_deny_root
cmd_onerr=/sbin/poweroff onerr=fail unlock_time=1200


My Laptop shuts down as soon as someone guesses the password wrong 4 times in a
row.

The Patch is available on github as a pull request:

https://github.com/linux-pam/linux-pam/pull/37


So I hope you might be able to review the patch and add it to sid.


regards


      Hans



-- System Information:
Debian Release: buster/sid
  APT prefers testing
  APT policy: (500, 'testing'), (500, 'stable'), (1, 'experimental')
Architecture: amd64 (x86_64)

Kernel: Linux 4.13.0-1-amd64 (SMP w/4 CPU cores)
Locale: LANG=de_DE.UTF-8, LC_CTYPE=de_DE.UTF-8 (charmap=UTF-8), 
LANGUAGE=de_DE.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
Init: systemd (via /run/systemd/system)

Versions of packages libpam-modules depends on:
ii  debconf             1.5.63
ii  libaudit1           1:2.8.1-2
ii  libc6               2.24-17
ii  libdb5.3            5.3.28-13.1
ii  libpam-modules-bin  1.1.8-3.7
ii  libpam0g            1.1.8-3.7
ii  libselinux1         2.7-2

libpam-modules recommends no packages.

libpam-modules suggests no packages.

-- debconf information excluded
diff --git a/modules/pam_tally2/pam_tally2.8.xml 
b/modules/pam_tally2/pam_tally2.8.xml
index cf5d76d..bf1bd88 100644
--- a/modules/pam_tally2/pam_tally2.8.xml
+++ b/modules/pam_tally2/pam_tally2.8.xml
@@ -24,6 +24,9 @@
       <arg choice="opt">
         
onerr=[<replaceable>fail</replaceable>|<replaceable>succeed</replaceable>]
       </arg>
+      <arg choice="opt">
+        user_access
+      </arg>
       <arg choice="opt">
         magic_root
       </arg>
@@ -42,6 +45,9 @@
       <arg choice="opt">
         root_unlock_time=<replaceable>n</replaceable>
       </arg>
+      <arg choice="opt">
+        cmd_onerr=<replaceable>/path/to/halt</replaceable>
+      </arg>
       <arg choice="opt">
         serialize
       </arg>
@@ -142,6 +148,32 @@
                 </para>
               </listitem>
             </varlistentry>
+            <varlistentry>
+              <term>
+                
<option>cmd_onerr=<replaceable>/path/to/halt</replaceable></option>
+              </term>
+              <listitem>
+                <para>
+                  Optional path to a command that is executed if a login is 
denied. For example 
+                  <filename>/sbin/halt</filename>. This can be used together 
with disk encryption 
+                  to automatically shut down your computer if someone starts 
messing around, assuming 
+                  that unlocked disk encryption is harder to break than a 
running os. 
+                </para>
+              </listitem>
+            </varlistentry>
+            <varlistentry>
+              <term>
+                <option>user_access</option>
+              </term>
+              <listitem>
+                <para>
+                  This parameter will allow users to access the tallylog and 
enables pam_tally2 will work 
+                  with non suid screenlockers. The parameter changes the 
behavior of the parameter file=. It expects /var/log/tallylog 
+                  or whatever you have set to be a directory writeable by root 
and accessable by others. Within that 
+                  directory each user gets its own logfile, named by the 
username.
+                </para>
+              </listitem>
+            </varlistentry>
             <varlistentry>
               <term>
                 <option>audit</option>
diff --git a/modules/pam_tally2/pam_tally2.c b/modules/pam_tally2/pam_tally2.c
index da1c048..e1f9bd4 100644
--- a/modules/pam_tally2/pam_tally2.c
+++ b/modules/pam_tally2/pam_tally2.c
@@ -97,6 +97,7 @@
 
 /*---------------------------------------------------------------------*/
 
+#define DEFAULT_CMD_ONERR ""
 #define DEFAULT_LOGFILE "/var/log/tallylog"
 #define MODULE_NAME     "pam_tally2"
 
@@ -104,6 +105,7 @@
 #define TALLY_HI   ((tally_t)~0L)
 
 struct tally_options {
+    const char *cmd_onerr;
     const char *filename;
     tally_t deny;
     long lock_time;
@@ -120,6 +122,12 @@ struct tally_options {
 #define OPT_MAGIC_ROOT                   01
 #define OPT_FAIL_ON_ERROR                02
 #define OPT_DENY_ROOT                    04
+/* 
+   enable tally user access, this one requires filename 
+   to be a directory which is read and accessable by others. (755) 
+   within this directory every user gets its own tallylog file. 
+*/
+#define OPT_TALLY_USER_ACCESS            010
 #define OPT_QUIET                        040
 #define OPT_AUDIT                       0100
 #define OPT_NOLOGNOTICE                 0400
@@ -170,6 +178,7 @@ tally_parse_args(pam_handle_t *pamh, struct tally_options 
*opts,
                    int phase, int argc, const char **argv)
 {
     memset(opts, 0, sizeof(*opts));
+    opts->cmd_onerr = DEFAULT_CMD_ONERR;
     opts->filename = DEFAULT_LOGFILE;
     opts->ctrl = OPT_FAIL_ON_ERROR;
     opts->root_unlock_time = -1;
@@ -185,12 +194,23 @@ tally_parse_args(pam_handle_t *pamh, struct tally_options 
*opts,
         }
         opts->filename = from;
       }
+      else if ( ! strcmp( *argv, "user_access" ) ) {
+        opts->ctrl |= OPT_TALLY_USER_ACCESS;
+      }
       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 ( ! strncmp( *argv, "cmd_onerr=", 10 ) ) {
+        const char *cmd = *argv + 10;
+        if ( ! strncmp( cmd, "", 1 ) ) { 
+            pam_syslog(pamh, LOG_ERR, "zero length onerr command supplied");
+            return PAM_AUTH_ERR; 
+        }
+        opts->cmd_onerr = cmd;
+      }
       else if ( ! strcmp( *argv, "magic_root" ) ) {
         opts->ctrl |= OPT_MAGIC_ROOT;
       }
@@ -367,6 +387,7 @@ get_tally(pam_handle_t *pamh, uid_t uid, const char 
*filename,
 {
     struct stat fileinfo;
     int lstat_ret;
+    int chown_ret;
     void *void_tally = tally;
     int preopened = 0;
 
@@ -388,6 +409,13 @@ get_tally(pam_handle_t *pamh, uid_t uid, const char 
*filename,
         pam_syslog(pamh, LOG_ALERT, "Couldn't create %s: %m", filename);
         return PAM_AUTH_ERR;
       }
+      if (ctrl & OPT_TALLY_USER_ACCESS) {
+         chown_ret=chown(filename, uid, 0);
+         if ( chown_ret == -1 ) {
+            pam_syslog(pamh, LOG_ALERT, "Couldn't create %s: %m", filename);
+            return PAM_AUTH_ERR;
+         }
+      } 
       lstat_ret = fstat(*tfile, &fileinfo);
       close(*tfile);
     }
@@ -508,6 +536,7 @@ tally_check (tally_t oldcnt, time_t oldtime, pam_handle_t 
*pamh, uid_t uid,
 {
     int rv = PAM_SUCCESS;
     int loglevel = LOG_DEBUG;
+    int r;
 #ifdef HAVE_LIBAUDIT
     char buf[64];
     int audit_fd = -1;
@@ -620,9 +649,35 @@ cleanup:
         close(audit_fd);
     }
 #endif
+    if (( rv != PAM_SUCCESS ) && (opts->cmd_onerr != NULL)) { 
+        if (!(opts->ctrl & OPT_NOLOGNOTICE) && (loglevel != LOG_DEBUG || 
opts->ctrl & OPT_DEBUG)) {
+           pam_syslog(pamh, loglevel, "executing pam tally onerr cmd %s", 
opts->cmd_onerr);
+        }
+        r=system(opts->cmd_onerr);
+    }
     return rv;
 }
 
+// This function builds a filename out of tally options and the user name 
+// it is either path/username or just path depending on the fact if user 
+// access was set. 
+char * build_filename(struct tally_options *opts, const char *user) {
+    if (opts->ctrl & OPT_TALLY_USER_ACCESS) {
+       return strcat(
+          strcat(
+             strcpy(
+               malloc(strlen(opts->filename)+1+strlen(user)+1),
+               opts->filename
+             ),
+             "/"
+          ),
+          user
+       );
+    } else {
+       return opts->filename;
+    }
+}
+
 /* --- tally bump function: bump tally for uid by (signed) inc --- */
 
 static int
@@ -636,7 +691,7 @@ tally_bump (int inc, time_t *oldtime, pam_handle_t *pamh,
 
     tally.fail_cnt = 0;  /* !TALLY_HI --> Log opened for update */
 
-    i = get_tally(pamh, uid, opts->filename, tfile, &tally, opts->ctrl);
+    i = get_tally(pamh, uid, build_filename(opts, user), tfile, &tally, 
opts->ctrl);
     if (i != PAM_SUCCESS) {
         if (*tfile != -1) {
             close(*tfile);
@@ -697,7 +752,7 @@ tally_bump (int inc, time_t *oldtime, pam_handle_t *pamh,
 }
 
 static int
-tally_reset (pam_handle_t *pamh, uid_t uid, struct tally_options *opts, int 
old_tfile)
+tally_reset (pam_handle_t *pamh, uid_t uid, const char *user, struct 
tally_options *opts, int old_tfile)
 {
     struct tallylog tally;
     int tfile = old_tfile;
@@ -711,7 +766,7 @@ tally_reset (pam_handle_t *pamh, uid_t uid, struct 
tally_options *opts, int old_
 
     tally.fail_cnt = 0;  /* !TALLY_HI --> Log opened for update */
 
-    i=get_tally(pamh, uid, opts->filename, &tfile, &tally, opts->ctrl);
+    i=get_tally(pamh, uid, build_filename(opts, user), &tfile, &tally, 
opts->ctrl);
     if (i != PAM_SUCCESS) {
         if (tfile != old_tfile) /* the descriptor is not owned by pam data */
             close(tfile);
@@ -797,7 +852,7 @@ pam_sm_setcred(pam_handle_t *pamh, int flags UNUSED,
   /* no data found */
       return PAM_SUCCESS;
 
-  rv = tally_reset(pamh, uid, opts, tfile);
+  rv = tally_reset(pamh, uid, user, opts, tfile);
 
   pam_set_data(pamh, MODULE_NAME, NULL, NULL);
 
@@ -837,7 +892,7 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED,
   /* no data found */
       return PAM_SUCCESS;
 
-  rv = tally_reset(pamh, uid, opts, tfile);
+  rv = tally_reset(pamh, uid, user, opts, tfile);
 
   pam_set_data(pamh, MODULE_NAME, NULL, NULL);
 
diff --git a/modules/pam_tally2/pam_tally2.8.xml 
b/modules/pam_tally2/pam_tally2.8.xml
index cf5d76d..bf1bd88 100644
--- a/modules/pam_tally2/pam_tally2.8.xml
+++ b/modules/pam_tally2/pam_tally2.8.xml
@@ -24,6 +24,9 @@
       <arg choice="opt">
         
onerr=[<replaceable>fail</replaceable>|<replaceable>succeed</replaceable>]
       </arg>
+      <arg choice="opt">
+        user_access
+      </arg>
       <arg choice="opt">
         magic_root
       </arg>
@@ -42,6 +45,9 @@
       <arg choice="opt">
         root_unlock_time=<replaceable>n</replaceable>
       </arg>
+      <arg choice="opt">
+        cmd_onerr=<replaceable>/path/to/halt</replaceable>
+      </arg>
       <arg choice="opt">
         serialize
       </arg>
@@ -142,6 +148,32 @@
                 </para>
               </listitem>
             </varlistentry>
+            <varlistentry>
+              <term>
+                
<option>cmd_onerr=<replaceable>/path/to/halt</replaceable></option>
+              </term>
+              <listitem>
+                <para>
+                  Optional path to a command that is executed if a login is 
denied. For example 
+                  <filename>/sbin/halt</filename>. This can be used together 
with disk encryption 
+                  to automatically shut down your computer if someone starts 
messing around, assuming 
+                  that unlocked disk encryption is harder to break than a 
running os. 
+                </para>
+              </listitem>
+            </varlistentry>
+            <varlistentry>
+              <term>
+                <option>user_access</option>
+              </term>
+              <listitem>
+                <para>
+                  This parameter will allow users to access the tallylog and 
enables pam_tally2 will work 
+                  with non suid screenlockers. The parameter changes the 
behavior of the parameter file=. It expects /var/log/tallylog 
+                  or whatever you have set to be a directory writeable by root 
and accessable by others. Within that 
+                  directory each user gets its own logfile, named by the 
username.
+                </para>
+              </listitem>
+            </varlistentry>
             <varlistentry>
               <term>
                 <option>audit</option>
diff --git a/modules/pam_tally2/pam_tally2.c b/modules/pam_tally2/pam_tally2.c
index da1c048..e1f9bd4 100644
--- a/modules/pam_tally2/pam_tally2.c
+++ b/modules/pam_tally2/pam_tally2.c
@@ -97,6 +97,7 @@
 
 /*---------------------------------------------------------------------*/
 
+#define DEFAULT_CMD_ONERR ""
 #define DEFAULT_LOGFILE "/var/log/tallylog"
 #define MODULE_NAME     "pam_tally2"
 
@@ -104,6 +105,7 @@
 #define TALLY_HI   ((tally_t)~0L)
 
 struct tally_options {
+    const char *cmd_onerr;
     const char *filename;
     tally_t deny;
     long lock_time;
@@ -120,6 +122,12 @@ struct tally_options {
 #define OPT_MAGIC_ROOT                   01
 #define OPT_FAIL_ON_ERROR                02
 #define OPT_DENY_ROOT                    04
+/* 
+   enable tally user access, this one requires filename 
+   to be a directory which is read and accessable by others. (755) 
+   within this directory every user gets its own tallylog file. 
+*/
+#define OPT_TALLY_USER_ACCESS            010
 #define OPT_QUIET                        040
 #define OPT_AUDIT                       0100
 #define OPT_NOLOGNOTICE                 0400
@@ -170,6 +178,7 @@ tally_parse_args(pam_handle_t *pamh, struct tally_options 
*opts,
                    int phase, int argc, const char **argv)
 {
     memset(opts, 0, sizeof(*opts));
+    opts->cmd_onerr = DEFAULT_CMD_ONERR;
     opts->filename = DEFAULT_LOGFILE;
     opts->ctrl = OPT_FAIL_ON_ERROR;
     opts->root_unlock_time = -1;
@@ -185,12 +194,23 @@ tally_parse_args(pam_handle_t *pamh, struct tally_options 
*opts,
         }
         opts->filename = from;
       }
+      else if ( ! strcmp( *argv, "user_access" ) ) {
+        opts->ctrl |= OPT_TALLY_USER_ACCESS;
+      }
       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 ( ! strncmp( *argv, "cmd_onerr=", 10 ) ) {
+        const char *cmd = *argv + 10;
+        if ( ! strncmp( cmd, "", 1 ) ) { 
+            pam_syslog(pamh, LOG_ERR, "zero length onerr command supplied");
+            return PAM_AUTH_ERR; 
+        }
+        opts->cmd_onerr = cmd;
+      }
       else if ( ! strcmp( *argv, "magic_root" ) ) {
         opts->ctrl |= OPT_MAGIC_ROOT;
       }
@@ -367,6 +387,7 @@ get_tally(pam_handle_t *pamh, uid_t uid, const char 
*filename,
 {
     struct stat fileinfo;
     int lstat_ret;
+    int chown_ret;
     void *void_tally = tally;
     int preopened = 0;
 
@@ -388,6 +409,13 @@ get_tally(pam_handle_t *pamh, uid_t uid, const char 
*filename,
         pam_syslog(pamh, LOG_ALERT, "Couldn't create %s: %m", filename);
         return PAM_AUTH_ERR;
       }
+      if (ctrl & OPT_TALLY_USER_ACCESS) {
+         chown_ret=chown(filename, uid, 0);
+         if ( chown_ret == -1 ) {
+            pam_syslog(pamh, LOG_ALERT, "Couldn't create %s: %m", filename);
+            return PAM_AUTH_ERR;
+         }
+      } 
       lstat_ret = fstat(*tfile, &fileinfo);
       close(*tfile);
     }
@@ -508,6 +536,7 @@ tally_check (tally_t oldcnt, time_t oldtime, pam_handle_t 
*pamh, uid_t uid,
 {
     int rv = PAM_SUCCESS;
     int loglevel = LOG_DEBUG;
+    int r;
 #ifdef HAVE_LIBAUDIT
     char buf[64];
     int audit_fd = -1;
@@ -620,9 +649,35 @@ cleanup:
         close(audit_fd);
     }
 #endif
+    if (( rv != PAM_SUCCESS ) && (opts->cmd_onerr != NULL)) { 
+        if (!(opts->ctrl & OPT_NOLOGNOTICE) && (loglevel != LOG_DEBUG || 
opts->ctrl & OPT_DEBUG)) {
+           pam_syslog(pamh, loglevel, "executing pam tally onerr cmd %s", 
opts->cmd_onerr);
+        }
+        r=system(opts->cmd_onerr);
+    }
     return rv;
 }
 
+// This function builds a filename out of tally options and the user name 
+// it is either path/username or just path depending on the fact if user 
+// access was set. 
+char * build_filename(struct tally_options *opts, const char *user) {
+    if (opts->ctrl & OPT_TALLY_USER_ACCESS) {
+       return strcat(
+          strcat(
+             strcpy(
+               malloc(strlen(opts->filename)+1+strlen(user)+1),
+               opts->filename
+             ),
+             "/"
+          ),
+          user
+       );
+    } else {
+       return opts->filename;
+    }
+}
+
 /* --- tally bump function: bump tally for uid by (signed) inc --- */
 
 static int
@@ -636,7 +691,7 @@ tally_bump (int inc, time_t *oldtime, pam_handle_t *pamh,
 
     tally.fail_cnt = 0;  /* !TALLY_HI --> Log opened for update */
 
-    i = get_tally(pamh, uid, opts->filename, tfile, &tally, opts->ctrl);
+    i = get_tally(pamh, uid, build_filename(opts, user), tfile, &tally, 
opts->ctrl);
     if (i != PAM_SUCCESS) {
         if (*tfile != -1) {
             close(*tfile);
@@ -697,7 +752,7 @@ tally_bump (int inc, time_t *oldtime, pam_handle_t *pamh,
 }
 
 static int
-tally_reset (pam_handle_t *pamh, uid_t uid, struct tally_options *opts, int 
old_tfile)
+tally_reset (pam_handle_t *pamh, uid_t uid, const char *user, struct 
tally_options *opts, int old_tfile)
 {
     struct tallylog tally;
     int tfile = old_tfile;
@@ -711,7 +766,7 @@ tally_reset (pam_handle_t *pamh, uid_t uid, struct 
tally_options *opts, int old_
 
     tally.fail_cnt = 0;  /* !TALLY_HI --> Log opened for update */
 
-    i=get_tally(pamh, uid, opts->filename, &tfile, &tally, opts->ctrl);
+    i=get_tally(pamh, uid, build_filename(opts, user), &tfile, &tally, 
opts->ctrl);
     if (i != PAM_SUCCESS) {
         if (tfile != old_tfile) /* the descriptor is not owned by pam data */
             close(tfile);
@@ -797,7 +852,7 @@ pam_sm_setcred(pam_handle_t *pamh, int flags UNUSED,
   /* no data found */
       return PAM_SUCCESS;
 
-  rv = tally_reset(pamh, uid, opts, tfile);
+  rv = tally_reset(pamh, uid, user, opts, tfile);
 
   pam_set_data(pamh, MODULE_NAME, NULL, NULL);
 
@@ -837,7 +892,7 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED,
   /* no data found */
       return PAM_SUCCESS;
 
-  rv = tally_reset(pamh, uid, opts, tfile);
+  rv = tally_reset(pamh, uid, user, opts, tfile);
 
   pam_set_data(pamh, MODULE_NAME, NULL, NULL);
 
diff --git a/modules/pam_tally2/pam_tally2.8.xml 
b/modules/pam_tally2/pam_tally2.8.xml
index cf5d76d..bf1bd88 100644
--- a/modules/pam_tally2/pam_tally2.8.xml
+++ b/modules/pam_tally2/pam_tally2.8.xml
@@ -24,6 +24,9 @@
       <arg choice="opt">
         
onerr=[<replaceable>fail</replaceable>|<replaceable>succeed</replaceable>]
       </arg>
+      <arg choice="opt">
+        user_access
+      </arg>
       <arg choice="opt">
         magic_root
       </arg>
@@ -42,6 +45,9 @@
       <arg choice="opt">
         root_unlock_time=<replaceable>n</replaceable>
       </arg>
+      <arg choice="opt">
+        cmd_onerr=<replaceable>/path/to/halt</replaceable>
+      </arg>
       <arg choice="opt">
         serialize
       </arg>
@@ -142,6 +148,32 @@
                 </para>
               </listitem>
             </varlistentry>
+            <varlistentry>
+              <term>
+                
<option>cmd_onerr=<replaceable>/path/to/halt</replaceable></option>
+              </term>
+              <listitem>
+                <para>
+                  Optional path to a command that is executed if a login is 
denied. For example 
+                  <filename>/sbin/halt</filename>. This can be used together 
with disk encryption 
+                  to automatically shut down your computer if someone starts 
messing around, assuming 
+                  that unlocked disk encryption is harder to break than a 
running os. 
+                </para>
+              </listitem>
+            </varlistentry>
+            <varlistentry>
+              <term>
+                <option>user_access</option>
+              </term>
+              <listitem>
+                <para>
+                  This parameter will allow users to access the tallylog and 
enables pam_tally2 will work 
+                  with non suid screenlockers. The parameter changes the 
behavior of the parameter file=. It expects /var/log/tallylog 
+                  or whatever you have set to be a directory writeable by root 
and accessable by others. Within that 
+                  directory each user gets its own logfile, named by the 
username.
+                </para>
+              </listitem>
+            </varlistentry>
             <varlistentry>
               <term>
                 <option>audit</option>
diff --git a/modules/pam_tally2/pam_tally2.c b/modules/pam_tally2/pam_tally2.c
index da1c048..e1f9bd4 100644
--- a/modules/pam_tally2/pam_tally2.c
+++ b/modules/pam_tally2/pam_tally2.c
@@ -97,6 +97,7 @@
 
 /*---------------------------------------------------------------------*/
 
+#define DEFAULT_CMD_ONERR ""
 #define DEFAULT_LOGFILE "/var/log/tallylog"
 #define MODULE_NAME     "pam_tally2"
 
@@ -104,6 +105,7 @@
 #define TALLY_HI   ((tally_t)~0L)
 
 struct tally_options {
+    const char *cmd_onerr;
     const char *filename;
     tally_t deny;
     long lock_time;
@@ -120,6 +122,12 @@ struct tally_options {
 #define OPT_MAGIC_ROOT                   01
 #define OPT_FAIL_ON_ERROR                02
 #define OPT_DENY_ROOT                    04
+/* 
+   enable tally user access, this one requires filename 
+   to be a directory which is read and accessable by others. (755) 
+   within this directory every user gets its own tallylog file. 
+*/
+#define OPT_TALLY_USER_ACCESS            010
 #define OPT_QUIET                        040
 #define OPT_AUDIT                       0100
 #define OPT_NOLOGNOTICE                 0400
@@ -170,6 +178,7 @@ tally_parse_args(pam_handle_t *pamh, struct tally_options 
*opts,
                    int phase, int argc, const char **argv)
 {
     memset(opts, 0, sizeof(*opts));
+    opts->cmd_onerr = DEFAULT_CMD_ONERR;
     opts->filename = DEFAULT_LOGFILE;
     opts->ctrl = OPT_FAIL_ON_ERROR;
     opts->root_unlock_time = -1;
@@ -185,12 +194,23 @@ tally_parse_args(pam_handle_t *pamh, struct tally_options 
*opts,
         }
         opts->filename = from;
       }
+      else if ( ! strcmp( *argv, "user_access" ) ) {
+        opts->ctrl |= OPT_TALLY_USER_ACCESS;
+      }
       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 ( ! strncmp( *argv, "cmd_onerr=", 10 ) ) {
+        const char *cmd = *argv + 10;
+        if ( ! strncmp( cmd, "", 1 ) ) { 
+            pam_syslog(pamh, LOG_ERR, "zero length onerr command supplied");
+            return PAM_AUTH_ERR; 
+        }
+        opts->cmd_onerr = cmd;
+      }
       else if ( ! strcmp( *argv, "magic_root" ) ) {
         opts->ctrl |= OPT_MAGIC_ROOT;
       }
@@ -367,6 +387,7 @@ get_tally(pam_handle_t *pamh, uid_t uid, const char 
*filename,
 {
     struct stat fileinfo;
     int lstat_ret;
+    int chown_ret;
     void *void_tally = tally;
     int preopened = 0;
 
@@ -388,6 +409,13 @@ get_tally(pam_handle_t *pamh, uid_t uid, const char 
*filename,
         pam_syslog(pamh, LOG_ALERT, "Couldn't create %s: %m", filename);
         return PAM_AUTH_ERR;
       }
+      if (ctrl & OPT_TALLY_USER_ACCESS) {
+         chown_ret=chown(filename, uid, 0);
+         if ( chown_ret == -1 ) {
+            pam_syslog(pamh, LOG_ALERT, "Couldn't create %s: %m", filename);
+            return PAM_AUTH_ERR;
+         }
+      } 
       lstat_ret = fstat(*tfile, &fileinfo);
       close(*tfile);
     }
@@ -508,6 +536,7 @@ tally_check (tally_t oldcnt, time_t oldtime, pam_handle_t 
*pamh, uid_t uid,
 {
     int rv = PAM_SUCCESS;
     int loglevel = LOG_DEBUG;
+    int r;
 #ifdef HAVE_LIBAUDIT
     char buf[64];
     int audit_fd = -1;
@@ -620,9 +649,35 @@ cleanup:
         close(audit_fd);
     }
 #endif
+    if (( rv != PAM_SUCCESS ) && (opts->cmd_onerr != NULL)) { 
+        if (!(opts->ctrl & OPT_NOLOGNOTICE) && (loglevel != LOG_DEBUG || 
opts->ctrl & OPT_DEBUG)) {
+           pam_syslog(pamh, loglevel, "executing pam tally onerr cmd %s", 
opts->cmd_onerr);
+        }
+        r=system(opts->cmd_onerr);
+    }
     return rv;
 }
 
+// This function builds a filename out of tally options and the user name 
+// it is either path/username or just path depending on the fact if user 
+// access was set. 
+char * build_filename(struct tally_options *opts, const char *user) {
+    if (opts->ctrl & OPT_TALLY_USER_ACCESS) {
+       return strcat(
+          strcat(
+             strcpy(
+               malloc(strlen(opts->filename)+1+strlen(user)+1),
+               opts->filename
+             ),
+             "/"
+          ),
+          user
+       );
+    } else {
+       return opts->filename;
+    }
+}
+
 /* --- tally bump function: bump tally for uid by (signed) inc --- */
 
 static int
@@ -636,7 +691,7 @@ tally_bump (int inc, time_t *oldtime, pam_handle_t *pamh,
 
     tally.fail_cnt = 0;  /* !TALLY_HI --> Log opened for update */
 
-    i = get_tally(pamh, uid, opts->filename, tfile, &tally, opts->ctrl);
+    i = get_tally(pamh, uid, build_filename(opts, user), tfile, &tally, 
opts->ctrl);
     if (i != PAM_SUCCESS) {
         if (*tfile != -1) {
             close(*tfile);
@@ -697,7 +752,7 @@ tally_bump (int inc, time_t *oldtime, pam_handle_t *pamh,
 }
 
 static int
-tally_reset (pam_handle_t *pamh, uid_t uid, struct tally_options *opts, int 
old_tfile)
+tally_reset (pam_handle_t *pamh, uid_t uid, const char *user, struct 
tally_options *opts, int old_tfile)
 {
     struct tallylog tally;
     int tfile = old_tfile;
@@ -711,7 +766,7 @@ tally_reset (pam_handle_t *pamh, uid_t uid, struct 
tally_options *opts, int old_
 
     tally.fail_cnt = 0;  /* !TALLY_HI --> Log opened for update */
 
-    i=get_tally(pamh, uid, opts->filename, &tfile, &tally, opts->ctrl);
+    i=get_tally(pamh, uid, build_filename(opts, user), &tfile, &tally, 
opts->ctrl);
     if (i != PAM_SUCCESS) {
         if (tfile != old_tfile) /* the descriptor is not owned by pam data */
             close(tfile);
@@ -797,7 +852,7 @@ pam_sm_setcred(pam_handle_t *pamh, int flags UNUSED,
   /* no data found */
       return PAM_SUCCESS;
 
-  rv = tally_reset(pamh, uid, opts, tfile);
+  rv = tally_reset(pamh, uid, user, opts, tfile);
 
   pam_set_data(pamh, MODULE_NAME, NULL, NULL);
 
@@ -837,7 +892,7 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED,
   /* no data found */
       return PAM_SUCCESS;
 
-  rv = tally_reset(pamh, uid, opts, tfile);
+  rv = tally_reset(pamh, uid, user, opts, tfile);
 
   pam_set_data(pamh, MODULE_NAME, NULL, NULL);
 
diff --git a/modules/pam_tally2/pam_tally2.8.xml 
b/modules/pam_tally2/pam_tally2.8.xml
index cf5d76d..bf1bd88 100644
--- a/modules/pam_tally2/pam_tally2.8.xml
+++ b/modules/pam_tally2/pam_tally2.8.xml
@@ -24,6 +24,9 @@
       <arg choice="opt">
         
onerr=[<replaceable>fail</replaceable>|<replaceable>succeed</replaceable>]
       </arg>
+      <arg choice="opt">
+        user_access
+      </arg>
       <arg choice="opt">
         magic_root
       </arg>
@@ -42,6 +45,9 @@
       <arg choice="opt">
         root_unlock_time=<replaceable>n</replaceable>
       </arg>
+      <arg choice="opt">
+        cmd_onerr=<replaceable>/path/to/halt</replaceable>
+      </arg>
       <arg choice="opt">
         serialize
       </arg>
@@ -142,6 +148,32 @@
                 </para>
               </listitem>
             </varlistentry>
+            <varlistentry>
+              <term>
+                
<option>cmd_onerr=<replaceable>/path/to/halt</replaceable></option>
+              </term>
+              <listitem>
+                <para>
+                  Optional path to a command that is executed if a login is 
denied. For example 
+                  <filename>/sbin/halt</filename>. This can be used together 
with disk encryption 
+                  to automatically shut down your computer if someone starts 
messing around, assuming 
+                  that unlocked disk encryption is harder to break than a 
running os. 
+                </para>
+              </listitem>
+            </varlistentry>
+            <varlistentry>
+              <term>
+                <option>user_access</option>
+              </term>
+              <listitem>
+                <para>
+                  This parameter will allow users to access the tallylog and 
enables pam_tally2 will work 
+                  with non suid screenlockers. The parameter changes the 
behavior of the parameter file=. It expects /var/log/tallylog 
+                  or whatever you have set to be a directory writeable by root 
and accessable by others. Within that 
+                  directory each user gets its own logfile, named by the 
username.
+                </para>
+              </listitem>
+            </varlistentry>
             <varlistentry>
               <term>
                 <option>audit</option>
diff --git a/modules/pam_tally2/pam_tally2.c b/modules/pam_tally2/pam_tally2.c
index da1c048..e1f9bd4 100644
--- a/modules/pam_tally2/pam_tally2.c
+++ b/modules/pam_tally2/pam_tally2.c
@@ -97,6 +97,7 @@
 
 /*---------------------------------------------------------------------*/
 
+#define DEFAULT_CMD_ONERR ""
 #define DEFAULT_LOGFILE "/var/log/tallylog"
 #define MODULE_NAME     "pam_tally2"
 
@@ -104,6 +105,7 @@
 #define TALLY_HI   ((tally_t)~0L)
 
 struct tally_options {
+    const char *cmd_onerr;
     const char *filename;
     tally_t deny;
     long lock_time;
@@ -120,6 +122,12 @@ struct tally_options {
 #define OPT_MAGIC_ROOT                   01
 #define OPT_FAIL_ON_ERROR                02
 #define OPT_DENY_ROOT                    04
+/* 
+   enable tally user access, this one requires filename 
+   to be a directory which is read and accessable by others. (755) 
+   within this directory every user gets its own tallylog file. 
+*/
+#define OPT_TALLY_USER_ACCESS            010
 #define OPT_QUIET                        040
 #define OPT_AUDIT                       0100
 #define OPT_NOLOGNOTICE                 0400
@@ -170,6 +178,7 @@ tally_parse_args(pam_handle_t *pamh, struct tally_options 
*opts,
                    int phase, int argc, const char **argv)
 {
     memset(opts, 0, sizeof(*opts));
+    opts->cmd_onerr = DEFAULT_CMD_ONERR;
     opts->filename = DEFAULT_LOGFILE;
     opts->ctrl = OPT_FAIL_ON_ERROR;
     opts->root_unlock_time = -1;
@@ -185,12 +194,23 @@ tally_parse_args(pam_handle_t *pamh, struct tally_options 
*opts,
         }
         opts->filename = from;
       }
+      else if ( ! strcmp( *argv, "user_access" ) ) {
+        opts->ctrl |= OPT_TALLY_USER_ACCESS;
+      }
       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 ( ! strncmp( *argv, "cmd_onerr=", 10 ) ) {
+        const char *cmd = *argv + 10;
+        if ( ! strncmp( cmd, "", 1 ) ) { 
+            pam_syslog(pamh, LOG_ERR, "zero length onerr command supplied");
+            return PAM_AUTH_ERR; 
+        }
+        opts->cmd_onerr = cmd;
+      }
       else if ( ! strcmp( *argv, "magic_root" ) ) {
         opts->ctrl |= OPT_MAGIC_ROOT;
       }
@@ -367,6 +387,7 @@ get_tally(pam_handle_t *pamh, uid_t uid, const char 
*filename,
 {
     struct stat fileinfo;
     int lstat_ret;
+    int chown_ret;
     void *void_tally = tally;
     int preopened = 0;
 
@@ -388,6 +409,13 @@ get_tally(pam_handle_t *pamh, uid_t uid, const char 
*filename,
         pam_syslog(pamh, LOG_ALERT, "Couldn't create %s: %m", filename);
         return PAM_AUTH_ERR;
       }
+      if (ctrl & OPT_TALLY_USER_ACCESS) {
+         chown_ret=chown(filename, uid, 0);
+         if ( chown_ret == -1 ) {
+            pam_syslog(pamh, LOG_ALERT, "Couldn't create %s: %m", filename);
+            return PAM_AUTH_ERR;
+         }
+      } 
       lstat_ret = fstat(*tfile, &fileinfo);
       close(*tfile);
     }
@@ -508,6 +536,7 @@ tally_check (tally_t oldcnt, time_t oldtime, pam_handle_t 
*pamh, uid_t uid,
 {
     int rv = PAM_SUCCESS;
     int loglevel = LOG_DEBUG;
+    int r;
 #ifdef HAVE_LIBAUDIT
     char buf[64];
     int audit_fd = -1;
@@ -620,9 +649,35 @@ cleanup:
         close(audit_fd);
     }
 #endif
+    if (( rv != PAM_SUCCESS ) && (opts->cmd_onerr != NULL)) { 
+        if (!(opts->ctrl & OPT_NOLOGNOTICE) && (loglevel != LOG_DEBUG || 
opts->ctrl & OPT_DEBUG)) {
+           pam_syslog(pamh, loglevel, "executing pam tally onerr cmd %s", 
opts->cmd_onerr);
+        }
+        r=system(opts->cmd_onerr);
+    }
     return rv;
 }
 
+// This function builds a filename out of tally options and the user name 
+// it is either path/username or just path depending on the fact if user 
+// access was set. 
+char * build_filename(struct tally_options *opts, const char *user) {
+    if (opts->ctrl & OPT_TALLY_USER_ACCESS) {
+       return strcat(
+          strcat(
+             strcpy(
+               malloc(strlen(opts->filename)+1+strlen(user)+1),
+               opts->filename
+             ),
+             "/"
+          ),
+          user
+       );
+    } else {
+       return opts->filename;
+    }
+}
+
 /* --- tally bump function: bump tally for uid by (signed) inc --- */
 
 static int
@@ -636,7 +691,7 @@ tally_bump (int inc, time_t *oldtime, pam_handle_t *pamh,
 
     tally.fail_cnt = 0;  /* !TALLY_HI --> Log opened for update */
 
-    i = get_tally(pamh, uid, opts->filename, tfile, &tally, opts->ctrl);
+    i = get_tally(pamh, uid, build_filename(opts, user), tfile, &tally, 
opts->ctrl);
     if (i != PAM_SUCCESS) {
         if (*tfile != -1) {
             close(*tfile);
@@ -697,7 +752,7 @@ tally_bump (int inc, time_t *oldtime, pam_handle_t *pamh,
 }
 
 static int
-tally_reset (pam_handle_t *pamh, uid_t uid, struct tally_options *opts, int 
old_tfile)
+tally_reset (pam_handle_t *pamh, uid_t uid, const char *user, struct 
tally_options *opts, int old_tfile)
 {
     struct tallylog tally;
     int tfile = old_tfile;
@@ -711,7 +766,7 @@ tally_reset (pam_handle_t *pamh, uid_t uid, struct 
tally_options *opts, int old_
 
     tally.fail_cnt = 0;  /* !TALLY_HI --> Log opened for update */
 
-    i=get_tally(pamh, uid, opts->filename, &tfile, &tally, opts->ctrl);
+    i=get_tally(pamh, uid, build_filename(opts, user), &tfile, &tally, 
opts->ctrl);
     if (i != PAM_SUCCESS) {
         if (tfile != old_tfile) /* the descriptor is not owned by pam data */
             close(tfile);
@@ -797,7 +852,7 @@ pam_sm_setcred(pam_handle_t *pamh, int flags UNUSED,
   /* no data found */
       return PAM_SUCCESS;
 
-  rv = tally_reset(pamh, uid, opts, tfile);
+  rv = tally_reset(pamh, uid, user, opts, tfile);
 
   pam_set_data(pamh, MODULE_NAME, NULL, NULL);
 
@@ -837,7 +892,7 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED,
   /* no data found */
       return PAM_SUCCESS;
 
-  rv = tally_reset(pamh, uid, opts, tfile);
+  rv = tally_reset(pamh, uid, user, opts, tfile);
 
   pam_set_data(pamh, MODULE_NAME, NULL, NULL);
 
diff --git a/modules/pam_tally2/pam_tally2.8.xml 
b/modules/pam_tally2/pam_tally2.8.xml
index cf5d76d..bf1bd88 100644
--- a/modules/pam_tally2/pam_tally2.8.xml
+++ b/modules/pam_tally2/pam_tally2.8.xml
@@ -24,6 +24,9 @@
       <arg choice="opt">
         
onerr=[<replaceable>fail</replaceable>|<replaceable>succeed</replaceable>]
       </arg>
+      <arg choice="opt">
+        user_access
+      </arg>
       <arg choice="opt">
         magic_root
       </arg>
@@ -42,6 +45,9 @@
       <arg choice="opt">
         root_unlock_time=<replaceable>n</replaceable>
       </arg>
+      <arg choice="opt">
+        cmd_onerr=<replaceable>/path/to/halt</replaceable>
+      </arg>
       <arg choice="opt">
         serialize
       </arg>
@@ -142,6 +148,32 @@
                 </para>
               </listitem>
             </varlistentry>
+            <varlistentry>
+              <term>
+                
<option>cmd_onerr=<replaceable>/path/to/halt</replaceable></option>
+              </term>
+              <listitem>
+                <para>
+                  Optional path to a command that is executed if a login is 
denied. For example 
+                  <filename>/sbin/halt</filename>. This can be used together 
with disk encryption 
+                  to automatically shut down your computer if someone starts 
messing around, assuming 
+                  that unlocked disk encryption is harder to break than a 
running os. 
+                </para>
+              </listitem>
+            </varlistentry>
+            <varlistentry>
+              <term>
+                <option>user_access</option>
+              </term>
+              <listitem>
+                <para>
+                  This parameter will allow users to access the tallylog and 
enables pam_tally2 will work 
+                  with non suid screenlockers. The parameter changes the 
behavior of the parameter file=. It expects /var/log/tallylog 
+                  or whatever you have set to be a directory writeable by root 
and accessable by others. Within that 
+                  directory each user gets its own logfile, named by the 
username.
+                </para>
+              </listitem>
+            </varlistentry>
             <varlistentry>
               <term>
                 <option>audit</option>
diff --git a/modules/pam_tally2/pam_tally2.c b/modules/pam_tally2/pam_tally2.c
index da1c048..e1f9bd4 100644
--- a/modules/pam_tally2/pam_tally2.c
+++ b/modules/pam_tally2/pam_tally2.c
@@ -97,6 +97,7 @@
 
 /*---------------------------------------------------------------------*/
 
+#define DEFAULT_CMD_ONERR ""
 #define DEFAULT_LOGFILE "/var/log/tallylog"
 #define MODULE_NAME     "pam_tally2"
 
@@ -104,6 +105,7 @@
 #define TALLY_HI   ((tally_t)~0L)
 
 struct tally_options {
+    const char *cmd_onerr;
     const char *filename;
     tally_t deny;
     long lock_time;
@@ -120,6 +122,12 @@ struct tally_options {
 #define OPT_MAGIC_ROOT                   01
 #define OPT_FAIL_ON_ERROR                02
 #define OPT_DENY_ROOT                    04
+/* 
+   enable tally user access, this one requires filename 
+   to be a directory which is read and accessable by others. (755) 
+   within this directory every user gets its own tallylog file. 
+*/
+#define OPT_TALLY_USER_ACCESS            010
 #define OPT_QUIET                        040
 #define OPT_AUDIT                       0100
 #define OPT_NOLOGNOTICE                 0400
@@ -170,6 +178,7 @@ tally_parse_args(pam_handle_t *pamh, struct tally_options 
*opts,
                    int phase, int argc, const char **argv)
 {
     memset(opts, 0, sizeof(*opts));
+    opts->cmd_onerr = DEFAULT_CMD_ONERR;
     opts->filename = DEFAULT_LOGFILE;
     opts->ctrl = OPT_FAIL_ON_ERROR;
     opts->root_unlock_time = -1;
@@ -185,12 +194,23 @@ tally_parse_args(pam_handle_t *pamh, struct tally_options 
*opts,
         }
         opts->filename = from;
       }
+      else if ( ! strcmp( *argv, "user_access" ) ) {
+        opts->ctrl |= OPT_TALLY_USER_ACCESS;
+      }
       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 ( ! strncmp( *argv, "cmd_onerr=", 10 ) ) {
+        const char *cmd = *argv + 10;
+        if ( ! strncmp( cmd, "", 1 ) ) { 
+            pam_syslog(pamh, LOG_ERR, "zero length onerr command supplied");
+            return PAM_AUTH_ERR; 
+        }
+        opts->cmd_onerr = cmd;
+      }
       else if ( ! strcmp( *argv, "magic_root" ) ) {
         opts->ctrl |= OPT_MAGIC_ROOT;
       }
@@ -367,6 +387,7 @@ get_tally(pam_handle_t *pamh, uid_t uid, const char 
*filename,
 {
     struct stat fileinfo;
     int lstat_ret;
+    int chown_ret;
     void *void_tally = tally;
     int preopened = 0;
 
@@ -388,6 +409,13 @@ get_tally(pam_handle_t *pamh, uid_t uid, const char 
*filename,
         pam_syslog(pamh, LOG_ALERT, "Couldn't create %s: %m", filename);
         return PAM_AUTH_ERR;
       }
+      if (ctrl & OPT_TALLY_USER_ACCESS) {
+         chown_ret=chown(filename, uid, 0);
+         if ( chown_ret == -1 ) {
+            pam_syslog(pamh, LOG_ALERT, "Couldn't create %s: %m", filename);
+            return PAM_AUTH_ERR;
+         }
+      } 
       lstat_ret = fstat(*tfile, &fileinfo);
       close(*tfile);
     }
@@ -508,6 +536,7 @@ tally_check (tally_t oldcnt, time_t oldtime, pam_handle_t 
*pamh, uid_t uid,
 {
     int rv = PAM_SUCCESS;
     int loglevel = LOG_DEBUG;
+    int r;
 #ifdef HAVE_LIBAUDIT
     char buf[64];
     int audit_fd = -1;
@@ -620,9 +649,35 @@ cleanup:
         close(audit_fd);
     }
 #endif
+    if (( rv != PAM_SUCCESS ) && (opts->cmd_onerr != NULL)) { 
+        if (!(opts->ctrl & OPT_NOLOGNOTICE) && (loglevel != LOG_DEBUG || 
opts->ctrl & OPT_DEBUG)) {
+           pam_syslog(pamh, loglevel, "executing pam tally onerr cmd %s", 
opts->cmd_onerr);
+        }
+        r=system(opts->cmd_onerr);
+    }
     return rv;
 }
 
+// This function builds a filename out of tally options and the user name 
+// it is either path/username or just path depending on the fact if user 
+// access was set. 
+char * build_filename(struct tally_options *opts, const char *user) {
+    if (opts->ctrl & OPT_TALLY_USER_ACCESS) {
+       return strcat(
+          strcat(
+             strcpy(
+               malloc(strlen(opts->filename)+1+strlen(user)+1),
+               opts->filename
+             ),
+             "/"
+          ),
+          user
+       );
+    } else {
+       return opts->filename;
+    }
+}
+
 /* --- tally bump function: bump tally for uid by (signed) inc --- */
 
 static int
@@ -636,7 +691,7 @@ tally_bump (int inc, time_t *oldtime, pam_handle_t *pamh,
 
     tally.fail_cnt = 0;  /* !TALLY_HI --> Log opened for update */
 
-    i = get_tally(pamh, uid, opts->filename, tfile, &tally, opts->ctrl);
+    i = get_tally(pamh, uid, build_filename(opts, user), tfile, &tally, 
opts->ctrl);
     if (i != PAM_SUCCESS) {
         if (*tfile != -1) {
             close(*tfile);
@@ -697,7 +752,7 @@ tally_bump (int inc, time_t *oldtime, pam_handle_t *pamh,
 }
 
 static int
-tally_reset (pam_handle_t *pamh, uid_t uid, struct tally_options *opts, int 
old_tfile)
+tally_reset (pam_handle_t *pamh, uid_t uid, const char *user, struct 
tally_options *opts, int old_tfile)
 {
     struct tallylog tally;
     int tfile = old_tfile;
@@ -711,7 +766,7 @@ tally_reset (pam_handle_t *pamh, uid_t uid, struct 
tally_options *opts, int old_
 
     tally.fail_cnt = 0;  /* !TALLY_HI --> Log opened for update */
 
-    i=get_tally(pamh, uid, opts->filename, &tfile, &tally, opts->ctrl);
+    i=get_tally(pamh, uid, build_filename(opts, user), &tfile, &tally, 
opts->ctrl);
     if (i != PAM_SUCCESS) {
         if (tfile != old_tfile) /* the descriptor is not owned by pam data */
             close(tfile);
@@ -797,7 +852,7 @@ pam_sm_setcred(pam_handle_t *pamh, int flags UNUSED,
   /* no data found */
       return PAM_SUCCESS;
 
-  rv = tally_reset(pamh, uid, opts, tfile);
+  rv = tally_reset(pamh, uid, user, opts, tfile);
 
   pam_set_data(pamh, MODULE_NAME, NULL, NULL);
 
@@ -837,7 +892,7 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED,
   /* no data found */
       return PAM_SUCCESS;
 
-  rv = tally_reset(pamh, uid, opts, tfile);
+  rv = tally_reset(pamh, uid, user, opts, tfile);
 
   pam_set_data(pamh, MODULE_NAME, NULL, NULL);
 
diff --git a/modules/pam_tally2/pam_tally2.8.xml 
b/modules/pam_tally2/pam_tally2.8.xml
index cf5d76d..bf1bd88 100644
--- a/modules/pam_tally2/pam_tally2.8.xml
+++ b/modules/pam_tally2/pam_tally2.8.xml
@@ -24,6 +24,9 @@
       <arg choice="opt">
         
onerr=[<replaceable>fail</replaceable>|<replaceable>succeed</replaceable>]
       </arg>
+      <arg choice="opt">
+        user_access
+      </arg>
       <arg choice="opt">
         magic_root
       </arg>
@@ -42,6 +45,9 @@
       <arg choice="opt">
         root_unlock_time=<replaceable>n</replaceable>
       </arg>
+      <arg choice="opt">
+        cmd_onerr=<replaceable>/path/to/halt</replaceable>
+      </arg>
       <arg choice="opt">
         serialize
       </arg>
@@ -142,6 +148,32 @@
                 </para>
               </listitem>
             </varlistentry>
+            <varlistentry>
+              <term>
+                
<option>cmd_onerr=<replaceable>/path/to/halt</replaceable></option>
+              </term>
+              <listitem>
+                <para>
+                  Optional path to a command that is executed if a login is 
denied. For example 
+                  <filename>/sbin/halt</filename>. This can be used together 
with disk encryption 
+                  to automatically shut down your computer if someone starts 
messing around, assuming 
+                  that unlocked disk encryption is harder to break than a 
running os. 
+                </para>
+              </listitem>
+            </varlistentry>
+            <varlistentry>
+              <term>
+                <option>user_access</option>
+              </term>
+              <listitem>
+                <para>
+                  This parameter will allow users to access the tallylog and 
enables pam_tally2 will work 
+                  with non suid screenlockers. The parameter changes the 
behavior of the parameter file=. It expects /var/log/tallylog 
+                  or whatever you have set to be a directory writeable by root 
and accessable by others. Within that 
+                  directory each user gets its own logfile, named by the 
username.
+                </para>
+              </listitem>
+            </varlistentry>
             <varlistentry>
               <term>
                 <option>audit</option>
diff --git a/modules/pam_tally2/pam_tally2.c b/modules/pam_tally2/pam_tally2.c
index da1c048..e1f9bd4 100644
--- a/modules/pam_tally2/pam_tally2.c
+++ b/modules/pam_tally2/pam_tally2.c
@@ -97,6 +97,7 @@
 
 /*---------------------------------------------------------------------*/
 
+#define DEFAULT_CMD_ONERR ""
 #define DEFAULT_LOGFILE "/var/log/tallylog"
 #define MODULE_NAME     "pam_tally2"
 
@@ -104,6 +105,7 @@
 #define TALLY_HI   ((tally_t)~0L)
 
 struct tally_options {
+    const char *cmd_onerr;
     const char *filename;
     tally_t deny;
     long lock_time;
@@ -120,6 +122,12 @@ struct tally_options {
 #define OPT_MAGIC_ROOT                   01
 #define OPT_FAIL_ON_ERROR                02
 #define OPT_DENY_ROOT                    04
+/* 
+   enable tally user access, this one requires filename 
+   to be a directory which is read and accessable by others. (755) 
+   within this directory every user gets its own tallylog file. 
+*/
+#define OPT_TALLY_USER_ACCESS            010
 #define OPT_QUIET                        040
 #define OPT_AUDIT                       0100
 #define OPT_NOLOGNOTICE                 0400
@@ -170,6 +178,7 @@ tally_parse_args(pam_handle_t *pamh, struct tally_options 
*opts,
                    int phase, int argc, const char **argv)
 {
     memset(opts, 0, sizeof(*opts));
+    opts->cmd_onerr = DEFAULT_CMD_ONERR;
     opts->filename = DEFAULT_LOGFILE;
     opts->ctrl = OPT_FAIL_ON_ERROR;
     opts->root_unlock_time = -1;
@@ -185,12 +194,23 @@ tally_parse_args(pam_handle_t *pamh, struct tally_options 
*opts,
         }
         opts->filename = from;
       }
+      else if ( ! strcmp( *argv, "user_access" ) ) {
+        opts->ctrl |= OPT_TALLY_USER_ACCESS;
+      }
       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 ( ! strncmp( *argv, "cmd_onerr=", 10 ) ) {
+        const char *cmd = *argv + 10;
+        if ( ! strncmp( cmd, "", 1 ) ) { 
+            pam_syslog(pamh, LOG_ERR, "zero length onerr command supplied");
+            return PAM_AUTH_ERR; 
+        }
+        opts->cmd_onerr = cmd;
+      }
       else if ( ! strcmp( *argv, "magic_root" ) ) {
         opts->ctrl |= OPT_MAGIC_ROOT;
       }
@@ -367,6 +387,7 @@ get_tally(pam_handle_t *pamh, uid_t uid, const char 
*filename,
 {
     struct stat fileinfo;
     int lstat_ret;
+    int chown_ret;
     void *void_tally = tally;
     int preopened = 0;
 
@@ -388,6 +409,13 @@ get_tally(pam_handle_t *pamh, uid_t uid, const char 
*filename,
         pam_syslog(pamh, LOG_ALERT, "Couldn't create %s: %m", filename);
         return PAM_AUTH_ERR;
       }
+      if (ctrl & OPT_TALLY_USER_ACCESS) {
+         chown_ret=chown(filename, uid, 0);
+         if ( chown_ret == -1 ) {
+            pam_syslog(pamh, LOG_ALERT, "Couldn't create %s: %m", filename);
+            return PAM_AUTH_ERR;
+         }
+      } 
       lstat_ret = fstat(*tfile, &fileinfo);
       close(*tfile);
     }
@@ -508,6 +536,7 @@ tally_check (tally_t oldcnt, time_t oldtime, pam_handle_t 
*pamh, uid_t uid,
 {
     int rv = PAM_SUCCESS;
     int loglevel = LOG_DEBUG;
+    int r;
 #ifdef HAVE_LIBAUDIT
     char buf[64];
     int audit_fd = -1;
@@ -620,9 +649,35 @@ cleanup:
         close(audit_fd);
     }
 #endif
+    if (( rv != PAM_SUCCESS ) && (opts->cmd_onerr != NULL)) { 
+        if (!(opts->ctrl & OPT_NOLOGNOTICE) && (loglevel != LOG_DEBUG || 
opts->ctrl & OPT_DEBUG)) {
+           pam_syslog(pamh, loglevel, "executing pam tally onerr cmd %s", 
opts->cmd_onerr);
+        }
+        r=system(opts->cmd_onerr);
+    }
     return rv;
 }
 
+// This function builds a filename out of tally options and the user name 
+// it is either path/username or just path depending on the fact if user 
+// access was set. 
+char * build_filename(struct tally_options *opts, const char *user) {
+    if (opts->ctrl & OPT_TALLY_USER_ACCESS) {
+       return strcat(
+          strcat(
+             strcpy(
+               malloc(strlen(opts->filename)+1+strlen(user)+1),
+               opts->filename
+             ),
+             "/"
+          ),
+          user
+       );
+    } else {
+       return opts->filename;
+    }
+}
+
 /* --- tally bump function: bump tally for uid by (signed) inc --- */
 
 static int
@@ -636,7 +691,7 @@ tally_bump (int inc, time_t *oldtime, pam_handle_t *pamh,
 
     tally.fail_cnt = 0;  /* !TALLY_HI --> Log opened for update */
 
-    i = get_tally(pamh, uid, opts->filename, tfile, &tally, opts->ctrl);
+    i = get_tally(pamh, uid, build_filename(opts, user), tfile, &tally, 
opts->ctrl);
     if (i != PAM_SUCCESS) {
         if (*tfile != -1) {
             close(*tfile);
@@ -697,7 +752,7 @@ tally_bump (int inc, time_t *oldtime, pam_handle_t *pamh,
 }
 
 static int
-tally_reset (pam_handle_t *pamh, uid_t uid, struct tally_options *opts, int 
old_tfile)
+tally_reset (pam_handle_t *pamh, uid_t uid, const char *user, struct 
tally_options *opts, int old_tfile)
 {
     struct tallylog tally;
     int tfile = old_tfile;
@@ -711,7 +766,7 @@ tally_reset (pam_handle_t *pamh, uid_t uid, struct 
tally_options *opts, int old_
 
     tally.fail_cnt = 0;  /* !TALLY_HI --> Log opened for update */
 
-    i=get_tally(pamh, uid, opts->filename, &tfile, &tally, opts->ctrl);
+    i=get_tally(pamh, uid, build_filename(opts, user), &tfile, &tally, 
opts->ctrl);
     if (i != PAM_SUCCESS) {
         if (tfile != old_tfile) /* the descriptor is not owned by pam data */
             close(tfile);
@@ -797,7 +852,7 @@ pam_sm_setcred(pam_handle_t *pamh, int flags UNUSED,
   /* no data found */
       return PAM_SUCCESS;
 
-  rv = tally_reset(pamh, uid, opts, tfile);
+  rv = tally_reset(pamh, uid, user, opts, tfile);
 
   pam_set_data(pamh, MODULE_NAME, NULL, NULL);
 
@@ -837,7 +892,7 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED,
   /* no data found */
       return PAM_SUCCESS;
 
-  rv = tally_reset(pamh, uid, opts, tfile);
+  rv = tally_reset(pamh, uid, user, opts, tfile);
 
   pam_set_data(pamh, MODULE_NAME, NULL, NULL);
 

Reply via email to