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);