summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG1
-rw-r--r--doc/modules/pam_tally.sgml131
-rw-r--r--modules/pam_tally/README86
-rw-r--r--modules/pam_tally/pam_tally.c555
4 files changed, 444 insertions, 329 deletions
diff --git a/CHANGELOG b/CHANGELOG
index d618e325..bb78fde9 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -70,6 +70,7 @@ BerliOS Bugs are marked with (BerliOS #XXXX).
changes the password (Bug 872945 - kukuk)
* pam_limits: support for new Linux kernel 2.6 limits (from toby cabot
- t8m)
+* pam_tally: major rewrite of the module (t8m)
0.78: Do Nov 18 14:48:36 CET 2004
diff --git a/doc/modules/pam_tally.sgml b/doc/modules/pam_tally.sgml
index eeb05518..44c6f4ed 100644
--- a/doc/modules/pam_tally.sgml
+++ b/doc/modules/pam_tally.sgml
@@ -18,6 +18,7 @@ pam_tally
<tag><bf>Author[s]:</bf></tag>
Tim Baverstock
+Tomas Mraz
<tag><bf>Maintainer:</bf></tag>
@@ -61,9 +62,7 @@ want to use the supplied appliction.
<p>
Note, there are some outstanding issues with this module:
<tt>pam_tally</tt> is very dependant on <tt>getpw*()</tt> - a database
-of usernames would be much more flexible; the `keep a count of current
-logins' bit has been <tt>#ifdef</tt>'d out and you can only reset the
-counter on successful authentication, for now.
+of usernames would be much more flexible
<sect3>Generic options accepted by both components
<p>
@@ -84,23 +83,46 @@ counter on successful authentication, for now.
<tag><bf>Recognized arguments:</bf></tag>
<tt>onerr=</tt>(<tt>succeed</tt>|<tt>fail</tt>);
<tt>file=</tt>/where/to/keep/counts;
-<tt>no_magic_root</tt>
+<tt>deny=</tt><em>n</em>;
+<tt>lock_time=</tt><em>n</em>;
+<tt>unlock_time=</tt><em>n</em>;
+<tt>magic_root</tt>;
+<tt>even_deny_root_account</tt>;
+<tt>per_user</tt>;
+<tt>no_lock_time</tt>
+<tt>no_reset</tt>;
<tag><bf>Description:</bf></tag>
<p>
-The authentication component of this module increments the attempted
-login counter.
+The authentication component first checks if the user should be denied
+access and if not it increments attempted login counter.
+Then on call to <tt>pam_setcred</tt> it resets the attempts counter
+if the user is NOT magic root.
<p>
<tag><bf>Examples/suggested usage:</bf></tag>
<p>
-The module argument <tt>no_magic_root</tt> is used to indicate that if
-the module is invoked by a user with uid=0, then the counter is
-incremented. The sys-admin should use this for daemon-launched
-services, like <tt>telnet</tt>/<tt>rsh</tt>/<tt>login</tt>. For user
-launched services, like <tt>su</tt>, this argument should be omitted.
+The <tt>deny=</tt><em>n</em> option is used to deny access if tally
+for this user exceeds <em>n</em>.
+
+<p>
+The <tt>lock_time=</tt><em>n</em> option is used to always deny access
+for at least <em>n</em> seconds after a failed attempt.
+
+<p>
+The <tt>unlock_time=</tt><em>n</em> option is used to allow access after
+<em>n</em> seconds after the last failed attempt with exceeded tally.
+If this option is used the user will be locked out only for the specified
+amount of time after he exceeded his maximum allowed attempts. Otherwise
+the lock is removed only by a manual intervention of the system administrator.
+
+<p>
+The <tt>magic_root</tt> option is used to indicate that if
+the module is invoked by a user with uid=0, then the counter is not
+incremented. The sys-admin should use this for user launched services,
+like <tt>su</tt>, otherwise this argument should be omitted.
<p>
By way of more explanation, when a process already running as root
@@ -109,9 +131,33 @@ bypasses <tt>pam_tally</tt>'s checks: this is handy for <tt>su</tt>ing
from root into an account otherwise blocked. However, for services
like <tt>telnet</tt> or <tt>login</tt>, which always effectively run
from the root account, root (ie everyone) shouldn't be granted this
-magic status, and the flag `no_magic_root' should be set in this
+magic status, and the flag `magic_root' should not be set in this
situation, as noted in the summary above.
+<p>
+Normally, failed attempts to access root will <bf>NOT</bf> cause the
+root account to become blocked, to prevent denial-of-service: if your
+users aren't given shell accounts and root may only login via
+<tt>su</tt> or at the machine console (not
+<tt>telnet</tt>/<tt>rsh</tt>, etc), this is safe. If you really want
+root to be blocked for some given service, use
+<tt>even_deny_root_account</tt>.
+
+<p>
+If <tt>/var/log/faillog</tt> contains a non-zero <tt>.fail_max/.fail_locktime</tt>
+field for this user then the <tt>per_user</tt> module argument will
+ensure that the module uses this value and not the global
+<tt>deny/lock_time=</tt><em>n</em> parameter.
+
+<p>
+The <tt>no_lock_time</tt> option is for ensuring that the module does
+not use the <tt>.fail_locktime</tt> field in /var/log/faillog for this
+user.
+
+<p>
+The <tt>no_reset</tt> option is used to instruct the module to not reset
+the count on successful entry.
+
</descrip>
<sect2>Account component
@@ -122,67 +168,28 @@ situation, as noted in the summary above.
<tag><bf>Recognized arguments:</bf></tag>
<tt>onerr=</tt>(<tt>succeed</tt>|<tt>fail</tt>);
<tt>file=</tt>/where/to/keep/counts;
-<tt>deny=</tt><em>n</em>;
-<tt>no_magic_root</tt>;
-<tt>even_deny_root_account</tt>;
-<tt>reset</tt>;
+<tt>magic_root</tt>;
<tt>no_reset</tt>;
-<tt>per_user</tt>;
-<tt>no_lock_time</tt>
<tag><bf>Description:</bf></tag>
<p>
-The account component can deny access and/or reset the attempts
-counter. It also checks to make sure that the counts file is a plain
-file and not world writable.
+The account component resets attempts counter if the user is NOT
+magic root. This phase can be used optionaly for services which don't call
+pam_setcred correctly or if the reset should be done regardless
+of the failure of the account phase of other modules.
<tag><bf>Examples/suggested usage:</bf></tag>
<p>
-The <tt>deny=</tt><em>n</em> option is used to deny access if tally
-for this user exceeds <em>n</em>. The presence of
-<tt>deny=</tt><em>n</em> changes the default for
-<tt>reset</tt>/<tt>no_reset</tt> to <tt>reset</tt>, unless the user
-trying to gain access is root and the <tt>no_magic_root</tt> option
-has NOT been specified.
+The <tt>magic_root</tt> option is used to indicate that if
+the module is invoked by a user with uid=0, then the counter is not
+decremented/reset. The sys-admin should use this for user launched services,
+like <tt>su</tt>, otherwise this argument should be omitted.
<p>
-The <tt>no_magic_root</tt> option ensures that access attempts by root
-DON'T ignore deny. Use this for daemon-based stuff, like
-<tt>telnet</tt>/<tt>rsh</tt>/<tt>login</tt>.
-
-<p>
-The <tt>even_deny_root_account</tt> option is used to ensure that the
-root account can become unavailable. <bf>Note</bf> that magic root
-trying to gain root bypasses this, but normal users can be locked out.
-
-<p>
-The <tt>reset</tt> option instructs the module to reset count to 0 on
-successful entry, even for magic root. The <tt>no_reset</tt> option is
-used to instruct the module to not reset the count on successful
-entry. This is the default unless <tt>deny</tt> exists and the user
-attempting access is NOT magic root.
-
-<p>
-If <tt>/var/log/faillog</tt> contains a non-zero <tt>.fail_max</tt>
-field for this user then the <tt>per_user</tt> module argument will
-ensure that the module uses this value and not the global
-<tt>deny=</tt><em>n</em> parameter.
-
-<p>
-The <tt>no_lock_time</tt> option is for ensuring that the module does
-not use the <tt>.fail_locktime</tt> field in /var/log/faillog for this
-user.
-
-<p>
-Normally, failed attempts to access root will <bf>NOT</bf> cause the
-root account to become blocked, to prevent denial-of-service: if your
-users aren't given shell accounts and root may only login via
-<tt>su</tt> or at the machine console (not
-<tt>telnet</tt>/<tt>rsh</tt>, etc), this is safe. If you really want
-root to be blocked for some given service, use
-<tt>even_deny_root_account</tt>.
+The <tt>no_reset</tt> option is used to instruct the module to not reset
+the count on successful entry.
</descrip>
diff --git a/modules/pam_tally/README b/modules/pam_tally/README
index 4c421648..6c7d87f4 100644
--- a/modules/pam_tally/README
+++ b/modules/pam_tally/README
@@ -1,5 +1,5 @@
SUMMARY:
- pam_tally:
+ pam_tally.so:
Maintains a count of attempted accesses, can reset count on success,
can deny access if too many attempts fail.
@@ -11,41 +11,54 @@ SUMMARY:
* file=/where/to/keep/counts (default /var/log/faillog)
(auth)
- Authentication phase increments attempted login counter.
- * no_magic_root (root DOES increment counter. Use for
- daemon-based stuff, like telnet/rsh/login)
+ Authentication phase first checks if user should be denied access
+ and if not it increments attempted login counter. Then on call to
+ pam_setcred it resets the attempts counter if the user is NOT
+ magic root.
+ * deny=n (deny access if tally for this user exceeds n)
+
+ * lock_time=n (always deny for n seconds after failed attempt)
+
+ * unlock_time=n (allow access after n seconds after the last
+ failed attempt with exceeded tally)
- (account)
- Account phase can deny access and/or reset attempts counter.
- * deny=n (deny access if tally for this user exceeds n;
- The presence of deny=n changes the default for
- reset/no_reset to reset, unless the user trying to
- gain access is root and the no_magic_root option
- has NOT been specified.)
-
- * no_magic_root (access attempts by root DON'T ignore deny.
- Use this for daemon-based stuff, like telnet/rsh/login)
+ * magic_root (access attempts by root as requesting user ignore
+ deny and don't change counter.
+ Use this for su and similar services.)
+
* even_deny_root_account (Root can become unavailable. BEWARE.
Note that magic root trying to gain root bypasses this,
but normal users can be locked out.)
- * reset (reset count to 0 on successful entry, even for
- magic root)
- * no_reset (don't reset count on successful entry)
- This is the default unless deny exists and the
- user attempting access is NOT magic root.
-
* per_user (If /var/log/faillog contains a non-zero
- .fail_max field for this user then use it
- instead of deny=n parameter)
+ .fail_max/.fail_locktime field for this user then use it
+ instead of deny=n/lock_time=n parameter.)
* no_lock_time (Don't use .fail_locktime filed in
/var/log/faillog for this user)
+ * no_reset (don't reset count on successful entry,
+ only decrement)
+
+
+ (account)
+ Account phase resets attempts counter if the user is NOT magic root.
+ This phase can be used optionaly for services which don't call
+ pam_setcred correctly or if the reset should be done regardless
+ of the failure of the account phase of other modules.
+
+ * magic_root (access attempts by root as requesting user
+ don't change counter.
+ Use this for su and similar services.)
+
+ * no_reset (don't reset count on successful entry,
+ only decrement)
+
Also checks to make sure that the counts file is a plain
file and not world writable.
- Tim Baverstock <warwick@sable.demon.co.uk>, v0.1 5 March 1997
+ - Tomas Mraz <tmraz@redhat.com>, v0.2 5 January 2005
LONGER:
@@ -53,20 +66,20 @@ pam_tally comes in two parts: pam_tally.so and pam_tally.
pam_tally.so sits in a pam config file, in the auth and account sections.
-In the auth section, it increments a per-uid counter for each attempted
-login, in the account section, it denies access if attempted logins
-exceed some threashold and/or resets that counter to zero on successful
-login.
+In the auth section, it denies access if attempted logins exceed some
+threshold and it increments a per-uid counter for each attempted login,
+in the account section, it resets that counter to zero on successful
+login. If the module isn't used in the account section it resets the counter
+to zero on call to pam_setcred.
Root is treated specially:
-1. When a process already running as root tries to access some service, the
-access is `magic', and bypasses pam_tally's checks: handy for `su'ing from
-root into an account otherwise blocked. However, for services like telnet or
-login which always effectively run from the root account, root (ie everyone)
-shouldn't be granted this magic status, and the flag `no_magic_root' should
-be set in this situation, as noted in the summary above. [This option may
-be obsolete, with `sufficient root' processing.]
+1. When a process already running as root tries to access some service and the
+'magic_root' flag is set, the access is `magic', and bypasses pam_tally's
+checks: handy for `su'ing from root into an account otherwise blocked.
+NOTE: This was changed from the previous version of pam_tally where the default
+was to treat root as magic and there were the 'no_magic_root' flag. However
+for most of services the current default make sense.
2. Normally, failed attempts to access root will NOT cause the root
account to become blocked, to prevent denial-of-service: if your users aren't
@@ -93,3 +106,10 @@ The (4.0 Redhat) utilities seem to do funny things with uid, and I'm
not wholly sure I understood what I should have been doing anyway so
the `keep a count of current logins' bit has been #ifdef'd out and you
can only reset the counter on successful authentication, for now.
+
+IMPORTANT NOTICE:
+In the original version of pam_tally there was a bug where the information
+if the password was correct or not was leaked by returning error from
+different pam management phases. This was solved by moving the denying
+functionality to the auth phase. However it's necessary to update the pam
+configuration by moving the required options (as deny=N) to the auth phase.
diff --git a/modules/pam_tally/pam_tally.c b/modules/pam_tally/pam_tally.c
index 341f448e..134f7f32 100644
--- a/modules/pam_tally/pam_tally.c
+++ b/modules/pam_tally/pam_tally.c
@@ -9,6 +9,8 @@
* 5 March 1997
*
* Stuff stolen from pam_rootok and pam_listfile
+ *
+ * Changes by Tomas Mraz <tmraz@redhat.com> 5 January 2005
*/
#include <security/_pam_aconf.h>
@@ -56,12 +58,6 @@
#define DEFAULT_LOGFILE "/var/log/faillog"
#define MODULE_NAME "pam_tally"
-enum TALLY_RESET {
- TALLY_RESET_DEFAULT,
- TALLY_RESET_RESET,
- TALLY_RESET_NO_RESET
-};
-
#define tally_t unsigned short int
#define TALLY_FMT "%hu"
#define TALLY_HI ((tally_t)~0L)
@@ -79,6 +75,27 @@ struct fail_s {
#endif /* ndef MAIN */
};
+struct tally_options {
+ const char *filename;
+ tally_t deny;
+ long lock_time;
+ long unlock_time;
+ unsigned int ctrl;
+};
+
+#define PHASE_UNKNOWN 0
+#define PHASE_AUTH 1
+#define PHASE_ACCOUNT 2
+#define PHASE_SESSION 3
+
+#define OPT_MAGIC_ROOT 01
+#define OPT_FAIL_ON_ERROR 02
+#define OPT_DENY_ROOT 04
+#define OPT_PER_USER 010
+#define OPT_NO_LOCK_TIME 020
+#define OPT_NO_RESET 040
+
+
/*---------------------------------------------------------------------*/
/* some syslogging */
@@ -101,6 +118,91 @@ static void _pam_log(int err, const char *format, ...)
/*---------------------------------------------------------------------*/
+/* --- Support function: parse arguments --- */
+
+static void log_phase_no_auth( int phase, const char *argv )
+{
+ if ( phase != PHASE_AUTH ) {
+ _pam_log(LOG_ERR,
+ MODULE_NAME ": option %s allowed in auth phase only", argv);
+ }
+}
+
+static int tally_parse_args( struct tally_options *opts, int phase,
+ int argc, const char **argv )
+{
+ memset(opts, 0, sizeof(*opts));
+ opts->filename = DEFAULT_LOGFILE;
+
+ for ( ; argc-- > 0; ++argv ) {
+
+ if ( ! strncmp( *argv, "file=", 5 ) ) {
+ const char *from = *argv + 5;
+ if ( *from!='/' || strlen(from)>FILENAME_MAX-1 ) {
+ _pam_log(LOG_ERR,
+ MODULE_NAME ": filename not /rooted or too long; ",
+ *argv);
+ return PAM_AUTH_ERR;
+ }
+ opts->filename = from;
+ }
+ else if ( ! strcmp( *argv, "onerr=fail" ) ) {
+ opts->ctrl |= OPT_FAIL_ON_ERROR;
+ }
+ else if ( ! strcmp( *argv, "onerr=succeed" ) ) {
+ opts->ctrl &= ~OPT_FAIL_ON_ERROR;
+ }
+ else if ( ! strcmp( *argv, "magic_root" ) ) {
+ opts->ctrl |= OPT_MAGIC_ROOT;
+ }
+ else if ( ! strcmp( *argv, "even_deny_root_account" ) ) {
+ log_phase_no_auth(phase, *argv);
+ opts->ctrl |= OPT_DENY_ROOT;
+ }
+ else if ( ! strncmp( *argv, "deny=", 5 ) ) {
+ log_phase_no_auth(phase, *argv);
+ if ( sscanf((*argv)+5,TALLY_FMT,&opts->deny) != 1 ) {
+ _pam_log(LOG_ERR,"bad number supplied; %s",*argv);
+ return PAM_AUTH_ERR;
+ }
+ }
+ else if ( ! strncmp( *argv, "lock_time=", 10 ) ) {
+ log_phase_no_auth(phase, *argv);
+ if ( sscanf((*argv)+10,"%ld",&opts->lock_time) != 1 ) {
+ _pam_log(LOG_ERR,"bad number supplied; %s",*argv);
+ return PAM_AUTH_ERR;
+ }
+ }
+ else if ( ! strncmp( *argv, "unlock_time=", 12 ) ) {
+ log_phase_no_auth(phase, *argv);
+ if ( sscanf((*argv)+12,"%ld",&opts->unlock_time) != 1 ) {
+ _pam_log(LOG_ERR,"bad number supplied; %s",*argv);
+ return PAM_AUTH_ERR;
+ }
+ }
+ else if ( ! strcmp( *argv, "per_user" ) )
+ {
+ log_phase_no_auth(phase, *argv);
+ opts->ctrl |= OPT_PER_USER;
+ }
+ else if ( ! strcmp( *argv, "no_lock_time") )
+ {
+ log_phase_no_auth(phase, *argv);
+ opts->ctrl |= OPT_NO_LOCK_TIME;
+ }
+ else if ( ! strcmp( *argv, "no_reset" ) ) {
+ opts->ctrl |= OPT_NO_RESET;
+ }
+ else {
+ _pam_log(LOG_ERR, MODULE_NAME ": unknown option; %s",*argv);
+ }
+ }
+
+ return PAM_SUCCESS;
+}
+
+/*---------------------------------------------------------------------*/
+
/* --- Support function: get uid (and optionally username) from PAM or
cline_user --- */
@@ -136,6 +238,42 @@ static int pam_get_uid( pam_handle_t *pamh, uid_t *uid, const char **userp )
/*---------------------------------------------------------------------*/
+/* --- Support functions: set/get tally data --- */
+
+static void _cleanup( pam_handle_t *pamh, void *data, int error_status )
+ {
+ free(data);
+ }
+
+static void tally_set_data( pam_handle_t *pamh, time_t oldtime )
+ {
+ time_t *data;
+
+ if ( (data=malloc(sizeof(time_t))) != NULL ) {
+ *data = oldtime;
+ pam_set_data(pamh, MODULE_NAME, (void *)data, _cleanup);
+ }
+ }
+
+static int tally_get_data( pam_handle_t *pamh, time_t *oldtime )
+ {
+ int rv;
+ const void *data;
+
+ rv = pam_get_data(pamh, MODULE_NAME, &data);
+ if ( rv == PAM_SUCCESS && oldtime != NULL ) {
+ *oldtime = *(const time_t *)data;
+ pam_set_data(pamh, MODULE_NAME, NULL, NULL);
+ }
+ else {
+ rv = -1;
+ *oldtime = 0;
+ }
+ return rv;
+ }
+
+/*---------------------------------------------------------------------*/
+
/* --- Support function: open/create tallyfile and return tally for uid --- */
/* If on entry *tally==TALLY_HI, tallyfile is opened READONLY */
@@ -255,82 +393,42 @@ static int set_tally( tally_t tally,
#define PAM_FUNCTION(name) \
PAM_EXTERN int name (pam_handle_t *pamh,int flags,int argc,const char **argv)
-#define RETURN_ERROR(i) return ((fail_on_error)?(i):(PAM_SUCCESS))
+#define RETURN_ERROR(i) return ((opts->ctrl & OPT_FAIL_ON_ERROR)?(i):(PAM_SUCCESS))
/*---------------------------------------------------------------------*/
/* --- tally bump function: bump tally for uid by (signed) inc --- */
-static int tally_bump (int inc,
+static int tally_bump (int inc, time_t *oldtime,
pam_handle_t *pamh,
- int flags,
- int argc,
- const char **argv) {
- uid_t uid;
-
- int
- fail_on_error = FALSE;
+ uid_t uid,
+ const char *user,
+ struct tally_options *opts) {
tally_t
tally = 0; /* !TALLY_HI --> Log opened for update */
- char
- no_magic_root = FALSE;
-
- char
- filename[ FILENAME_MAX ] = DEFAULT_LOGFILE;
-
- /* Should probably decode the parameters before anything else. */
-
- {
- for ( ; argc-- > 0; ++argv ) {
-
- /* generic options.. um, ignored. :] */
-
- if ( ! strcmp( *argv, "no_magic_root" ) ) {
- no_magic_root = TRUE;
- }
- else if ( ! strncmp( *argv, "file=", 5 ) ) {
- char const
- *from = (*argv)+5;
- char
- *to = filename;
- if ( *from!='/' || strlen(from)>FILENAME_MAX-1 ) {
- _pam_log(LOG_ERR,
- MODULE_NAME ": filename not /rooted or too long; ",
- *argv);
- RETURN_ERROR( PAM_AUTH_ERR );
- }
- while ( ( *to++ = *from++ ) );
- }
- else if ( ! strcmp( *argv, "onerr=fail" ) ) {
- fail_on_error=TRUE;
- }
- else if ( ! strcmp( *argv, "onerr=succeed" ) ) {
- fail_on_error=FALSE;
- }
- else {
- _pam_log(LOG_ERR, MODULE_NAME ": unknown option; %s",*argv);
- }
- } /* for() */
- }
-
- {
FILE
*TALLY = NULL;
const char
- *user = NULL,
*remote_host = NULL,
*cur_tty = NULL;
struct fail_s fs, *fsp = &fs;
+ int i;
- int i=pam_get_uid(pamh, &uid, &user);
- if ( i != PAM_SUCCESS ) RETURN_ERROR( i );
-
- i=get_tally( &tally, uid, filename, &TALLY, fsp );
+ i=get_tally( &tally, uid, opts->filename, &TALLY, fsp );
/* to remember old fail time (for locktime) */
fsp->fs_fail_time = fsp->fs_faillog.fail_time;
- fsp->fs_faillog.fail_time = time(NULL);
+ if ( inc > 0 ) {
+ if ( oldtime ) {
+ *oldtime = fsp->fs_faillog.fail_time;
+ }
+ fsp->fs_faillog.fail_time = time(NULL);
+ } else {
+ if ( oldtime ) {
+ fsp->fs_faillog.fail_time = *oldtime;
+ }
+ }
(void) pam_get_item(pamh, PAM_RHOST, (const void **)&remote_host);
if (!remote_host) {
@@ -352,7 +450,7 @@ static int tally_bump (int inc,
}
if ( i != PAM_SUCCESS ) { if (TALLY) fclose(TALLY); RETURN_ERROR( i ); }
- if ( no_magic_root || getuid() ) { /* no_magic_root kills uid test */
+ if ( !(opts->ctrl & OPT_MAGIC_ROOT) || getuid() ) { /* magic_root doesn't change tally */
tally+=inc;
@@ -363,217 +461,215 @@ static int tally_bump (int inc,
}
}
- i=set_tally( tally, uid, filename, &TALLY, fsp );
+ i=set_tally( tally, uid, opts->filename, &TALLY, fsp );
if ( i != PAM_SUCCESS ) { if (TALLY) fclose(TALLY); RETURN_ERROR( i ); }
- }
- return PAM_SUCCESS;
+ return PAM_SUCCESS;
}
-/*---------------------------------------------------------------------*/
-
-/* --- authentication management functions (only) --- */
-
-#ifdef PAM_SM_AUTH
-
-PAM_FUNCTION( pam_sm_authenticate ) {
- return tally_bump( 1, pamh, flags, argc, argv);
-}
-
-/* --- Seems to need this function. Ho hum. --- */
-
-PAM_FUNCTION( pam_sm_setcred ) { return PAM_SUCCESS; }
-
-#endif
-
-/*---------------------------------------------------------------------*/
-
-/* --- session management functions (only) --- */
-
-/*
- * Unavailable until .so files can be suid
- */
-
-#ifdef PAM_SM_SESSION
-
-/* To maintain a balance-tally of successful login/outs */
-
-PAM_FUNCTION( pam_sm_open_session ) {
- return tally_bump( 1, pamh, flags, argc, argv);
-}
-
-PAM_FUNCTION( pam_sm_close_session ) {
- return tally_bump(-1, pamh, flags, argc, argv);
-}
-
-#endif
-
-/*---------------------------------------------------------------------*/
-
-/* --- authentication management functions (only) --- */
-
-#ifdef PAM_SM_AUTH
-
-/* To lock out a user with an unacceptably high tally */
-
-PAM_FUNCTION( pam_sm_acct_mgmt ) {
- uid_t
- uid;
-
- int
- fail_on_error = FALSE;
+static int tally_check (time_t oldtime,
+ pam_handle_t *pamh,
+ uid_t uid,
+ const char *user,
+ struct tally_options *opts) {
tally_t
- deny = 0;
+ deny = opts->deny;
tally_t
tally = 0; /* !TALLY_HI --> Log opened for update */
+ long
+ lock_time = opts->lock_time;
- char
- no_magic_root = FALSE,
- even_deny_root_account = FALSE;
- char per_user = FALSE; /* if true then deny=.fail_max for user */
- char no_lock_time = FALSE; /* if true then don't use .fail_locktime */
-
- const char
- *user = NULL;
-
- enum TALLY_RESET
- reset = TALLY_RESET_DEFAULT;
-
- char
- filename[ FILENAME_MAX ] = DEFAULT_LOGFILE;
-
- /* Should probably decode the parameters before anything else. */
-
- {
- for ( ; argc-- > 0; ++argv ) {
-
- /* generic options.. um, ignored. :] */
-
- if ( ! strcmp( *argv, "no_magic_root" ) ) {
- no_magic_root = TRUE;
- }
- else if ( ! strcmp( *argv, "even_deny_root_account" ) ) {
- even_deny_root_account = TRUE;
- }
- else if ( ! strcmp( *argv, "reset" ) ) {
- reset = TALLY_RESET_RESET;
- }
- else if ( ! strcmp( *argv, "no_reset" ) ) {
- reset = TALLY_RESET_NO_RESET;
- }
- else if ( ! strncmp( *argv, "file=", 5 ) ) {
- char const
- *from = (*argv)+5;
- char
- *to = filename;
- if ( *from != '/' || strlen(from) > FILENAME_MAX-1 ) {
- _pam_log(LOG_ERR,
- MODULE_NAME ": filename not /rooted or too long; ",
- *argv);
- RETURN_ERROR( PAM_AUTH_ERR );
- }
- while ( ( *to++ = *from++ ) );
- }
- else if ( ! strncmp( *argv, "deny=", 5 ) ) {
- if ( sscanf((*argv)+5,TALLY_FMT,&deny) != 1 ) {
- _pam_log(LOG_ERR,"bad number supplied; %s",*argv);
- RETURN_ERROR( PAM_AUTH_ERR );
- }
- }
- else if ( ! strcmp( *argv, "onerr=fail" ) ) {
- fail_on_error=TRUE;
- }
- else if ( ! strcmp( *argv, "onerr=succeed" ) ) {
- fail_on_error=FALSE;
- }
- else if ( ! strcmp( *argv, "per_user" ) )
- {
- per_user = TRUE;
- }
- else if ( ! strcmp( *argv, "no_lock_time") )
- {
- no_lock_time = TRUE;
- }
- else {
- _pam_log(LOG_ERR, MODULE_NAME ": unknown option; %s",*argv);
- }
- } /* for() */
- }
-
- {
struct fail_s fs, *fsp = &fs;
FILE *TALLY=0;
- int i=pam_get_uid(pamh, &uid, &user);
- if ( i != PAM_SUCCESS ) RETURN_ERROR( i );
+ int i;
- i=get_tally( &tally, uid, filename, &TALLY, fsp );
- if ( i != PAM_SUCCESS ) { if (TALLY) fclose(TALLY); RETURN_ERROR( i ); }
+ i=get_tally( &tally, uid, opts->filename, &TALLY, fsp );
+ if (TALLY) fclose(TALLY);
+ if ( i != PAM_SUCCESS ) { RETURN_ERROR( i ); }
- if ( no_magic_root || getuid() ) { /* no_magic_root kills uid test */
+ if ( !(opts->ctrl & OPT_MAGIC_ROOT) || getuid() ) { /* magic_root skips tally check */
/* To deny or not to deny; that is the question */
/* if there's .fail_max entry and per_user=TRUE then deny=.fail_max */
- if ( (fsp->fs_faillog.fail_max) && (per_user) ) {
+ if ( (fsp->fs_faillog.fail_max) && (opts->ctrl & OPT_PER_USER) ) {
deny = fsp->fs_faillog.fail_max;
}
- if (fsp->fs_faillog.fail_locktime && fsp->fs_fail_time
- && (!no_lock_time) )
+ if ( (fsp->fs_faillog.fail_locktime) && (opts->ctrl & OPT_PER_USER) ) {
+ lock_time = fsp->fs_faillog.fail_locktime;
+ }
+ if (lock_time && oldtime
+ && !(opts->ctrl & OPT_NO_LOCK_TIME) )
{
- if ( (fsp->fs_faillog.fail_locktime + fsp->fs_fail_time) > time(NULL) )
+ if ( lock_time + oldtime > time(NULL) )
{
_pam_log(LOG_NOTICE,
"user %s ("UID_FMT") has time limit [%lds left]"
" since last failure.",
user,uid,
- fsp->fs_fail_time+fsp->fs_faillog.fail_locktime
+ oldtime+lock_time
-time(NULL));
- if (TALLY)
- fclose(TALLY);
return PAM_AUTH_ERR;
}
}
+ if (opts->unlock_time && oldtime)
+ {
+ if ( opts->unlock_time + oldtime <= time(NULL) )
+ { /* ignore deny check after unlock_time elapsed */
+ return PAM_SUCCESS;
+ }
+ }
if (
( deny != 0 ) && /* deny==0 means no deny */
( tally > deny ) && /* tally>deny means exceeded */
- ( even_deny_root_account || uid ) /* even_deny stops uid check */
+ ( ((opts->ctrl & OPT_DENY_ROOT) || uid) ) /* even_deny stops uid check */
) {
_pam_log(LOG_NOTICE,"user %s ("UID_FMT") tally "TALLY_FMT", deny "TALLY_FMT,
user, uid, tally, deny);
- if (TALLY)
- fclose(TALLY);
return PAM_AUTH_ERR; /* Only unconditional failure */
}
-
- /* resets for explicit reset
- * or by default if deny exists and not magic-root
- */
-
- if ( ( reset == TALLY_RESET_RESET ) ||
- ( reset == TALLY_RESET_DEFAULT && deny ) ) { tally=0; }
}
- else /* is magic root */ {
- /* Magic root skips deny test... */
+ return PAM_SUCCESS;
+}
+
+static int tally_reset (pam_handle_t *pamh,
+ uid_t uid,
+ const char *user,
+ struct tally_options *opts) {
+ tally_t
+ tally = 0; /* !TALLY_HI --> Log opened for update */
+
+ struct fail_s fs, *fsp = &fs;
+ FILE *TALLY=0;
+ int i;
+
+ i=get_tally( &tally, uid, opts->filename, &TALLY, fsp );
+ if ( i != PAM_SUCCESS ) { if (TALLY) fclose(TALLY); RETURN_ERROR( i ); }
+
+ /* resets if not magic root
+ */
- /* Magic root only resets on explicit reset, regardless of deny */
+ if ( (!(opts->ctrl & OPT_MAGIC_ROOT) || getuid())
+ && !(opts->ctrl & OPT_NO_RESET) )
+ { tally=0; }
- if ( reset == TALLY_RESET_RESET ) { tally=0; }
- }
if (tally == 0)
{
fsp->fs_faillog.fail_time = (time_t) 0;
strcpy(fsp->fs_faillog.fail_line, "");
}
- i=set_tally( tally, uid, filename, &TALLY, fsp );
+
+ i=set_tally( tally, uid, opts->filename, &TALLY, fsp );
if ( i != PAM_SUCCESS ) { if (TALLY) fclose(TALLY); RETURN_ERROR( i ); }
- }
- return PAM_SUCCESS;
+ return PAM_SUCCESS;
+}
+
+/*---------------------------------------------------------------------*/
+
+/* --- authentication management functions (only) --- */
+
+#ifdef PAM_SM_AUTH
+
+PAM_FUNCTION( pam_sm_authenticate ) {
+ int
+ rvcheck, rvbump;
+ time_t
+ oldtime = 0;
+ struct tally_options
+ options, *opts = &options;
+ uid_t
+ uid;
+ const char
+ *user;
+
+ rvcheck = tally_parse_args(opts, PHASE_AUTH, argc, argv);
+ if ( rvcheck != PAM_SUCCESS )
+ RETURN_ERROR( rvcheck );
+
+ rvcheck = pam_get_uid(pamh, &uid, &user);
+ if ( rvcheck != PAM_SUCCESS )
+ RETURN_ERROR( rvcheck );
+
+ rvbump = tally_bump(1, &oldtime, pamh, uid, user, opts);
+ rvcheck = tally_check(oldtime, pamh, uid, user, opts);
+
+ tally_set_data(pamh, oldtime);
+
+ return rvcheck != PAM_SUCCESS ? rvcheck : rvbump;
+}
+
+PAM_FUNCTION( pam_sm_setcred ) {
+ int
+ rv;
+ time_t
+ oldtime = 0;
+ struct tally_options
+ options, *opts = &options;
+ uid_t
+ uid;
+ const char
+ *user;
+
+ rv = tally_parse_args(opts, PHASE_AUTH, argc, argv);
+ if ( rv != PAM_SUCCESS )
+ RETURN_ERROR( rv );
+
+ rv = pam_get_uid(pamh, &uid, &user);
+ if ( rv != PAM_SUCCESS )
+ RETURN_ERROR( rv );
+
+ if ( tally_get_data(pamh, &oldtime) != 0 )
+ /* no data found */
+ return PAM_SUCCESS;
+
+ if ( (rv=tally_bump(-1, &oldtime, pamh, uid, user, opts)) != PAM_SUCCESS )
+ return rv;
+ return tally_reset(pamh, uid, user, opts);
+}
+
+#endif
+
+/*---------------------------------------------------------------------*/
+
+/* --- authentication management functions (only) --- */
+
+#ifdef PAM_SM_ACCOUNT
+
+/* To reset failcount of user on successfull login */
+
+PAM_FUNCTION( pam_sm_acct_mgmt ) {
+ int
+ rv;
+ time_t
+ oldtime = 0;
+ struct tally_options
+ options, *opts = &options;
+ uid_t
+ uid;
+ const char
+ *user;
+
+ rv = tally_parse_args(opts, PHASE_ACCOUNT, argc, argv);
+ if ( rv != PAM_SUCCESS )
+ RETURN_ERROR( rv );
+
+ rv = pam_get_uid(pamh, &uid, &user);
+ if ( rv != PAM_SUCCESS )
+ RETURN_ERROR( rv );
+
+ if ( tally_get_data(pamh, &oldtime) != 0 )
+ /* no data found */
+ return PAM_SUCCESS;
+
+ if ( (rv=tally_bump(-1, &oldtime, pamh, uid, user, opts)) != PAM_SUCCESS )
+ return rv;
+ return tally_reset(pamh, uid, user, opts);
}
-#endif /* #ifdef PAM_SM_AUTH */
+#endif /* #ifdef PAM_SM_ACCOUNT */
/*-----------------------------------------------------------------------*/
@@ -595,18 +691,9 @@ struct pam_module _pam_tally_modstruct = {
#else
NULL,
#endif
-#ifdef PAM_SM_SESSION
- pam_sm_open_session,
- pam_sm_close_session,
-#else
NULL,
NULL,
-#endif
-#ifdef PAM_SM_PASSWORD
- pam_sm_chauthtok,
-#else
NULL,
-#endif
};
#endif /* #ifdef PAM_STATIC */