From 114ed318bea9b5859ab89144261946716776e2ed Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Fri, 17 Oct 2008 11:29:55 +0000 Subject: Relevant BUGIDs: Purpose of commit: new feature Commit summary: --------------- 2008-10-17 Tomas Mraz * configure.in: Add modules/pam_tally2/Makefile. * doc/sag/Linux-PAM_SAG.xml: Include pam_tally2.xml. * doc/sag/pam_tally2.xml: New. * libpam/pam_static_modules.h: Add pam_tally2 static struct. * modules/Makefile.am: Add pam_tally2 directory. * modules/pam_tally2/Makefile.am: New. * modules/pam_tally2/README.xml: New. * modules/pam_tally2/tallylog.h: New. * modules/pam_tally2/pam_tally2.8.xml: New. * modules/pam_tally2/pam_tally2.c: New. * modules/pam_tally2/pam_tally2_app.c: New. * modules/pam_tally2/tst-pam_tally2: New. * po/POTFILES.in: Add pam_tally2 sources. --- modules/pam_tally2/.cvsignore | 9 + modules/pam_tally2/Makefile.am | 40 ++ modules/pam_tally2/README.xml | 46 ++ modules/pam_tally2/pam_tally2.8.xml | 439 ++++++++++++++++ modules/pam_tally2/pam_tally2.c | 985 ++++++++++++++++++++++++++++++++++++ modules/pam_tally2/pam_tally2_app.c | 7 + modules/pam_tally2/tallylog.h | 52 ++ modules/pam_tally2/tst-pam_tally2 | 2 + 8 files changed, 1580 insertions(+) create mode 100644 modules/pam_tally2/.cvsignore create mode 100644 modules/pam_tally2/Makefile.am create mode 100644 modules/pam_tally2/README.xml create mode 100644 modules/pam_tally2/pam_tally2.8.xml create mode 100644 modules/pam_tally2/pam_tally2.c create mode 100644 modules/pam_tally2/pam_tally2_app.c create mode 100644 modules/pam_tally2/tallylog.h create mode 100755 modules/pam_tally2/tst-pam_tally2 (limited to 'modules/pam_tally2') diff --git a/modules/pam_tally2/.cvsignore b/modules/pam_tally2/.cvsignore new file mode 100644 index 00000000..c20ebb92 --- /dev/null +++ b/modules/pam_tally2/.cvsignore @@ -0,0 +1,9 @@ +*.la +*.lo +.deps +.libs +Makefile +Makefile.in +pam_tally2 +README +pam_tally2.8 diff --git a/modules/pam_tally2/Makefile.am b/modules/pam_tally2/Makefile.am new file mode 100644 index 00000000..6f843e1f --- /dev/null +++ b/modules/pam_tally2/Makefile.am @@ -0,0 +1,40 @@ +# +# Copyright (c) 2005, 2006, 2007 Thorsten Kukuk +# Copyright (c) 2008 Red Hat, Inc. +# + +CLEANFILES = *~ + +EXTRA_DIST = README $(MANS) $(XMLS) tst-pam_tally2 + +man_MANS = pam_tally2.8 +XMLS = README.xml pam_tally2.8.xml + +TESTS = tst-pam_tally2 + +securelibdir = $(SECUREDIR) +secureconfdir = $(SCONFIGDIR) + +noinst_HEADERS = tallylog.h + +AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include + +pam_tally2_la_LDFLAGS = -no-undefined -avoid-version -module +pam_tally2_la_LIBADD = -L$(top_builddir)/libpam -lpam $(LIBAUDIT) +if HAVE_VERSIONING + pam_tally2_la_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map +endif + +pam_tally2_LDADD = $(LIBAUDIT) + +securelib_LTLIBRARIES = pam_tally2.la +sbin_PROGRAMS = pam_tally2 + +pam_tally2_la_SOURCES = pam_tally2.c +pam_tally2_SOURCES = pam_tally2_app.c + +if ENABLE_REGENERATE_MAN +noinst_DATA = README +README: pam_tally2.8.xml +-include $(top_srcdir)/Make.xml.rules +endif diff --git a/modules/pam_tally2/README.xml b/modules/pam_tally2/README.xml new file mode 100644 index 00000000..aa470570 --- /dev/null +++ b/modules/pam_tally2/README.xml @@ -0,0 +1,46 @@ + + +--> +]> + +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" + href="pam_tally2.8.xml" xpointer='xpointer(//refnamediv[@id = "pam_tally2-name"]/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
diff --git a/modules/pam_tally2/pam_tally2.8.xml b/modules/pam_tally2/pam_tally2.8.xml new file mode 100644 index 00000000..dc284a1d --- /dev/null +++ b/modules/pam_tally2/pam_tally2.8.xml @@ -0,0 +1,439 @@ + + + + + + + pam_tally2 + 8 + Linux-PAM Manual + + + + pam_tally2 + The login counter (tallying) module + + + + + pam_tally2.so + + file=/path/to/counter + + + onerr=[fail|succeed] + + + magic_root + + + even_deny_root + + + deny=n + + + lock_time=n + + + unlock_time=n + + + root_unlock_time=n + + + audit + + + silent + + + no_log_info + + + + pam_tally2 + + --file /path/to/counter + + + --user username + + + --reset[=n] + + + --quiet + + + + + + + DESCRIPTION + + + This module maintains a count of attempted accesses, can + reset count on success, can deny access if too many attempts fail. + + + pam_tally2 comes in two parts: + pam_tally2.so and + pam_tally2. The former is the PAM module and + the latter, a stand-alone program. pam_tally2 + is an (optional) application which can be used to interrogate and + manipulate the counter file. It can display users' counts, set + individual counts, or clear all counts. Setting artificially high + counts may be useful for blocking users without changing their + passwords. For example, one might find it useful to clear all counts + every midnight from a cron job. + + + 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 given + shell accounts and root may only login via su or + at the machine console (not telnet/rsh, etc), this is safe. + + + + + + OPTIONS + + + + GLOBAL OPTIONS + + + + This can be used for auth and + account module types. + + + + + + + + + If something weird happens (like unable to open the file), + return with PAM_SUCESS if + + is given, else with the corresponding PAM error code. + + + + + + + + + + File where to keep counts. Default is + /var/log/tallylog. + + + + + + + + + + Will log the user name into the system log if the user is not found. + + + + + + + + + + Don't print informative messages. + + + + + + + + + + Don't log informative messages via syslog3. + + + + + + + + + + AUTH OPTIONS + + + + Authentication phase first increments attempted login counter and + checks if user should be denied access. If the user is authenticated + and the login process continues on call to + pam_setcred3 + it resets the attempts counter. + + + + + + + + + Deny access if tally for this user exceeds + n. + + + + + + + + + + Always deny for n seconds + after failed attempt. + + + + + + + + + + Allow access after n seconds + after failed attempt. If this option is used the user will + be locked out for the specified amount of time after he + exceeded his maximum allowed attempts. Otherwise the + account is locked until the lock is removed by a manual + intervention of the system administrator. + + + + + + + + + + If the module is invoked by a user with uid=0 the + counter is not incremented. The sys-admin should use this + for user launched services, like su, + otherwise this argument should be omitted. + + + + + + + + + + Do not use the .fail_locktime field in + /var/log/faillog for this user. + + + + + + + + + + Don't reset count on successful entry, only decrement. + + + + + + + + + + Root account can become unavailable. + + + + + + + + + + This option implies option. + Allow access after n seconds + to root acccount after failed attempt. If this option is used + the root user will be locked out for the specified amount of + time after he exceeded his maximum allowed attempts. + + + + + + + + + + + ACCOUNT OPTIONS + + + + 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_setcred3 + correctly or if the reset should be done regardless + of the failure of the account phase of other modules. + + + + + + + + + If the module is invoked by a user with uid=0 the + counter is not changed. The sys-admin should use this + for user launched services, like su, + otherwise this argument should be omitted. + + + + + + + + + + + MODULE TYPES PROVIDED + + The and + module types are provided. + + + + + RETURN VALUES + + + PAM_AUTH_ERR + + + A invalid option was given, the module was not able + to retrive the user name, no valid counter file + was found, or too many failed logins. + + + + + PAM_SUCCESS + + + Everything was successfull. + + + + + PAM_USER_UNKNOWN + + + User not known. + + + + + + + + NOTES + + pam_tally2 is not compatible with the old pam_tally faillog file format. + This is caused by requirement of compatibility of the tallylog file + format between 32bit and 64bit architectures on multiarch systems. + + + There is no setuid wrapper for access to the data file such as when the + pam_tally2.so module is called from + xscreensaver. As this would make it impossible to share PAM configuration + with such services the following workaround is used: If the data file + cannot be opened because of insufficient permissions + (EPERM) the module returns + PAM_IGNORE. + + + + + EXAMPLES + + Add the following line to /etc/pam.d/login to + lock the account after 4 failed logins. Root account will be locked + as well. The accounts will be automatically unlocked after 20 minutes. + The module does not have to be called in the account phase because the + login calls + pam_setcred3 + correctly. + + +auth required pam_securetty.so +auth required pam_tally2.so deny=4 even_deny_root unlock_time=1200 +auth required pam_env.so +auth required pam_unix.so +auth required pam_nologin.so +account required pam_unix.so +password required pam_unix.so +session required pam_limits.so +session required pam_unix.so +session required pam_lastlog.so nowtmp +session optional pam_mail.so standard + + + + + FILES + + + /var/log/tallylog + + failure count logging file + + + + + + + SEE ALSO + + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_tally was written by Tim Baverstock and Tomas Mraz. + + + + + diff --git a/modules/pam_tally2/pam_tally2.c b/modules/pam_tally2/pam_tally2.c new file mode 100644 index 00000000..9ae3180d --- /dev/null +++ b/modules/pam_tally2/pam_tally2.c @@ -0,0 +1,985 @@ +/* + * pam_tally2.c + * + */ + + +/* By Tim Baverstock , Multi Media Machine Ltd. + * 5 March 1997 + * + * Stuff stolen from pam_rootok and pam_listfile + * + * Changes by Tomas Mraz 5 January 2005, 26 January 2006 + * Audit option added for Tomas patch by Sebastien Tricaud 13 January 2005 + * Portions Copyright 2006, Red Hat, Inc. + * Portions Copyright 1989 - 1993, Julianne Frances Haugh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Julianne F. Haugh nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "config.h" + +#if defined(MAIN) && defined(MEMORY_DEBUG) +# undef exit +#endif /* defined(MAIN) && defined(MEMORY_DEBUG) */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LIBAUDIT +#include +#endif + +#include +#include +#include +#include "tallylog.h" + +#ifndef TRUE +#define TRUE 1L +#define FALSE 0L +#endif + +#ifndef HAVE_FSEEKO +#define fseeko fseek +#endif + +/* + * here, we make a definition for the externally accessible function + * in this file (this definition is required for static a module + * but strongly encouraged generally) it is used to instruct the + * modules include file to define the function prototypes. + */ + +#ifndef MAIN +#define PAM_SM_AUTH +#define PAM_SM_ACCOUNT +/* #define PAM_SM_SESSION */ +/* #define PAM_SM_PASSWORD */ + +#include +#include +#endif +#include + +/*---------------------------------------------------------------------*/ + +#define DEFAULT_LOGFILE "/var/log/tallylog" +#define MODULE_NAME "pam_tally2" + +#define tally_t uint16_t +#define TALLY_HI ((tally_t)~0L) + +struct tally_options { + const char *filename; + tally_t deny; + long lock_time; + long unlock_time; + long root_unlock_time; + unsigned int ctrl; +}; + +#define PHASE_UNKNOWN 0 +#define PHASE_AUTH 1 +#define PHASE_ACCOUNT 2 +#define PHASE_SESSION 3 + +#define OPT_MAGIC_ROOT 01 +#define OPT_FAIL_ON_ERROR 02 +#define OPT_DENY_ROOT 04 +#define OPT_QUIET 040 +#define OPT_AUDIT 0100 +#define OPT_NOLOGNOTICE 0400 + + +/*---------------------------------------------------------------------*/ + +/* some syslogging */ + +#ifdef MAIN +#define pam_syslog tally_log +static void +tally_log (const pam_handle_t *pamh UNUSED, int priority UNUSED, + const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + fprintf(stderr, "%s: ", MODULE_NAME); + vfprintf(stderr, fmt, args); + fprintf(stderr,"\n"); + va_end(args); +} + +#define pam_modutil_getpwnam(pamh, user) getpwnam(user) +#endif + +/*---------------------------------------------------------------------*/ + +/* --- Support function: parse arguments --- */ + +#ifndef MAIN + +static void +log_phase_no_auth(pam_handle_t *pamh, int phase, const char *argv) +{ + if ( phase != PHASE_AUTH ) { + pam_syslog(pamh, LOG_ERR, + "option %s allowed in auth phase only", argv); + } +} + +static int +tally_parse_args(pam_handle_t *pamh, struct tally_options *opts, + int phase, int argc, const char **argv) +{ + memset(opts, 0, sizeof(*opts)); + opts->filename = DEFAULT_LOGFILE; + opts->ctrl = OPT_FAIL_ON_ERROR; + opts->root_unlock_time = -1; + + for ( ; argc-- > 0; ++argv ) { + + if ( ! strncmp( *argv, "file=", 5 ) ) { + const char *from = *argv + 5; + if ( *from!='/' ) { + pam_syslog(pamh, LOG_ERR, + "filename not /rooted; %s", *argv); + return PAM_AUTH_ERR; + } + opts->filename = from; + } + else if ( ! strcmp( *argv, "onerr=fail" ) ) { + opts->ctrl |= OPT_FAIL_ON_ERROR; + } + else if ( ! strcmp( *argv, "onerr=succeed" ) ) { + opts->ctrl &= ~OPT_FAIL_ON_ERROR; + } + else if ( ! strcmp( *argv, "magic_root" ) ) { + opts->ctrl |= OPT_MAGIC_ROOT; + } + else if ( ! strcmp( *argv, "even_deny_root_account" ) || + ! strcmp( *argv, "even_deny_root" ) ) { + log_phase_no_auth(pamh, phase, *argv); + opts->ctrl |= OPT_DENY_ROOT; + } + else if ( ! strncmp( *argv, "deny=", 5 ) ) { + log_phase_no_auth(pamh, phase, *argv); + if ( sscanf((*argv)+5,"%hu",&opts->deny) != 1 ) { + pam_syslog(pamh, LOG_ERR, "bad number supplied: %s", *argv); + return PAM_AUTH_ERR; + } + } + else if ( ! strncmp( *argv, "lock_time=", 10 ) ) { + log_phase_no_auth(pamh, phase, *argv); + if ( sscanf((*argv)+10,"%ld",&opts->lock_time) != 1 ) { + pam_syslog(pamh, LOG_ERR, "bad number supplied: %s", *argv); + return PAM_AUTH_ERR; + } + } + else if ( ! strncmp( *argv, "unlock_time=", 12 ) ) { + log_phase_no_auth(pamh, phase, *argv); + if ( sscanf((*argv)+12,"%ld",&opts->unlock_time) != 1 ) { + pam_syslog(pamh, LOG_ERR, "bad number supplied: %s", *argv); + return PAM_AUTH_ERR; + } + } + else if ( ! strncmp( *argv, "root_unlock_time=", 17 ) ) { + log_phase_no_auth(pamh, phase, *argv); + if ( sscanf((*argv)+17,"%ld",&opts->root_unlock_time) != 1 ) { + pam_syslog(pamh, LOG_ERR, "bad number supplied: %s", *argv); + return PAM_AUTH_ERR; + } + opts->ctrl |= OPT_DENY_ROOT; /* even_deny_root implied */ + } + else if ( ! strcmp( *argv, "quiet" ) || + ! strcmp ( *argv, "silent")) { + opts->ctrl |= OPT_QUIET; + } + else if ( ! strcmp ( *argv, "no_log_info") ) { + opts->ctrl |= OPT_NOLOGNOTICE; + } + else if ( ! strcmp ( *argv, "audit") ) { + opts->ctrl |= OPT_AUDIT; + } + else { + pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv); + } + } + + if (opts->root_unlock_time == -1) + opts->root_unlock_time = opts->unlock_time; + + return PAM_SUCCESS; +} + +#endif /* #ifndef MAIN */ + +/*---------------------------------------------------------------------*/ + +/* --- Support function: get uid (and optionally username) from PAM or + cline_user --- */ + +#ifdef MAIN +static char *cline_user=0; /* cline_user is used in the administration prog */ +#endif + +static int +pam_get_uid(pam_handle_t *pamh, uid_t *uid, const char **userp, struct tally_options *opts) +{ + const char *user = NULL; + struct passwd *pw; + +#ifdef MAIN + user = cline_user; +#else + if ((pam_get_user( pamh, &user, NULL )) != PAM_SUCCESS) { + user = NULL; + } +#endif + + if ( !user || !*user ) { + pam_syslog(pamh, LOG_ERR, "pam_get_uid; user?"); + return PAM_AUTH_ERR; + } + + if ( ! ( pw = pam_modutil_getpwnam( pamh, user ) ) ) { + opts->ctrl & OPT_AUDIT ? + pam_syslog(pamh, LOG_ERR, "pam_get_uid; no such user %s", user) : + pam_syslog(pamh, LOG_ERR, "pam_get_uid; no such user"); + return PAM_USER_UNKNOWN; + } + + if ( uid ) *uid = pw->pw_uid; + if ( userp ) *userp = user; + return PAM_SUCCESS; +} + +/*---------------------------------------------------------------------*/ + +/* --- Support functions: set/get tally data --- */ + +#ifndef MAIN + +static void +_cleanup(pam_handle_t *pamh UNUSED, void *data, int error_status UNUSED) +{ + free(data); +} + + +static void +tally_set_data( pam_handle_t *pamh, time_t oldtime ) +{ + time_t *data; + + if ( (data=malloc(sizeof(time_t))) != NULL ) { + *data = oldtime; + pam_set_data(pamh, MODULE_NAME, (void *)data, _cleanup); + } +} + +static int +tally_get_data( pam_handle_t *pamh, time_t *oldtime ) +{ + int rv; + const void *data; + + rv = pam_get_data(pamh, MODULE_NAME, &data); + if ( rv == PAM_SUCCESS && data != NULL && oldtime != NULL ) { + *oldtime = *(const time_t *)data; + pam_set_data(pamh, MODULE_NAME, NULL, NULL); + } + else { + rv = -1; + *oldtime = 0; + } + return rv; +} +#endif /* #ifndef MAIN */ + +/*---------------------------------------------------------------------*/ + +/* --- Support function: open/create tallyfile and return tally for uid --- */ + +/* If on entry tallyfile doesn't exist, creation is attempted. */ + +static int +get_tally(pam_handle_t *pamh, uid_t uid, const char *filename, + FILE **tfile, struct tallylog *tally) +{ + struct stat fileinfo; + int lstat_ret; + + lstat_ret = lstat(filename, &fileinfo); + if (lstat_ret) { + int save_errno; + int oldmask = umask(077); + *tfile=fopen(filename, "a"); + save_errno = errno; + /* Create file, or append-open in pathological case. */ + umask(oldmask); + if ( !*tfile ) { +#ifndef MAIN + if (save_errno == EPERM) { + return PAM_IGNORE; /* called with insufficient access rights */ + } +#endif + errno = save_errno; + pam_syslog(pamh, LOG_ALERT, "Couldn't create %s: %m", filename); + return PAM_AUTH_ERR; + } + lstat_ret = fstat(fileno(*tfile),&fileinfo); + fclose(*tfile); + *tfile = NULL; + } + + if ( lstat_ret ) { + pam_syslog(pamh, LOG_ALERT, "Couldn't stat %s", filename); + return PAM_AUTH_ERR; + } + + if ((fileinfo.st_mode & S_IWOTH) || !S_ISREG(fileinfo.st_mode)) { + /* If the file is world writable or is not a + normal file, return error */ + pam_syslog(pamh, LOG_ALERT, + "%s is either world writable or not a normal file", + filename); + return PAM_AUTH_ERR; + } + + if (!(*tfile = fopen(filename, "r+"))) { +#ifndef MAIN + if (errno == EPERM) /* called with insufficient access rights */ + return PAM_IGNORE; +#endif + pam_syslog(pamh, LOG_ALERT, "Error opening %s for update: %m", filename); + + return PAM_AUTH_ERR; + } + + if (fseeko(*tfile, (off_t)uid*(off_t)sizeof(*tally), SEEK_SET)) { + pam_syslog(pamh, LOG_ALERT, "fseek failed for %s: %m", filename); + fclose(*tfile); + *tfile = NULL; + return PAM_AUTH_ERR; + } + + if (fileinfo.st_size < (off_t)(uid+1)*(off_t)sizeof(*tally)) { + memset(tally, 0, sizeof(*tally)); + } else if (fread(tally, sizeof(*tally), 1, *tfile) == 0) { + memset(tally, 0, sizeof(*tally)); + /* Shouldn't happen */ + } + + tally->fail_line[sizeof(tally->fail_line)-1] = '\0'; + + return PAM_SUCCESS; +} + +/*---------------------------------------------------------------------*/ + +/* --- Support function: update and close tallyfile with tally!=TALLY_HI --- */ + +static int +set_tally(pam_handle_t *pamh, uid_t uid, + const char *filename, FILE **tfile, struct tallylog *tally) +{ + if (tally->fail_cnt != TALLY_HI) { + if (fseeko(*tfile, (off_t)uid * sizeof(*tally), SEEK_SET)) { + pam_syslog(pamh, LOG_ALERT, "fseek failed for %s: %m", filename); + return PAM_AUTH_ERR; + } + if (fwrite(tally, sizeof(*tally), 1, *tfile) == 0) { + pam_syslog(pamh, LOG_ALERT, "update (fwrite) failed for %s: %m", filename); + return PAM_AUTH_ERR; + } + } + + if (fclose(*tfile)) { + *tfile = NULL; + pam_syslog(pamh, LOG_ALERT, "update (fclose) failed for %s: %m", filename); + return PAM_AUTH_ERR; + } + *tfile=NULL; + return PAM_SUCCESS; +} + +/*---------------------------------------------------------------------*/ + +/* --- PAM bits --- */ + +#ifndef MAIN + +#define RETURN_ERROR(i) return ((opts->ctrl & OPT_FAIL_ON_ERROR)?(i):(PAM_SUCCESS)) + +/*---------------------------------------------------------------------*/ + +static int +tally_check (tally_t oldcnt, time_t oldtime, pam_handle_t *pamh, uid_t uid, + const char *user, struct tally_options *opts, + struct tallylog *tally) +{ + int rv = PAM_SUCCESS; +#ifdef HAVE_LIBAUDIT + char buf[64]; + int audit_fd = -1; +#endif + + if ((opts->ctrl & OPT_MAGIC_ROOT) && getuid() == 0) { + return PAM_SUCCESS; + } + /* magic_root skips tally check */ +#ifdef HAVE_LIBAUDIT + audit_fd = audit_open(); + /* If there is an error & audit support is in the kernel report error */ + if ((audit_fd < 0) && !(errno == EINVAL || errno == EPROTONOSUPPORT || + errno == EAFNOSUPPORT)) + return PAM_SYSTEM_ERR; +#endif + if (opts->deny != 0 && /* deny==0 means no deny */ + tally->fail_cnt > opts->deny && /* tally>deny means exceeded */ + ((opts->ctrl & OPT_DENY_ROOT) || uid)) { /* even_deny stops uid check */ +#ifdef HAVE_LIBAUDIT + if (tally->fail_cnt == opts->deny+1) { + /* First say that max number was hit. */ + snprintf(buf, sizeof(buf), "pam_tally2 uid=%u ", uid); + audit_log_user_message(audit_fd, AUDIT_ANOM_LOGIN_FAILURES, buf, + NULL, NULL, NULL, 1); + } +#endif + if (uid) { + /* Unlock time check */ + if (opts->unlock_time && oldtime) { + if (opts->unlock_time + oldtime <= time(NULL)) { + /* ignore deny check after unlock_time elapsed */ +#ifdef HAVE_LIBAUDIT + snprintf(buf, sizeof(buf), "pam_tally2 uid=%u ", uid); + audit_log_user_message(audit_fd, AUDIT_RESP_ACCT_UNLOCK_TIMED, buf, + NULL, NULL, NULL, 1); +#endif + rv = PAM_SUCCESS; + goto cleanup; + } + } + } else { + /* Root unlock time check */ + if (opts->root_unlock_time && oldtime) { + if (opts->root_unlock_time + oldtime <= time(NULL)) { + /* ignore deny check after unlock_time elapsed */ +#ifdef HAVE_LIBAUDIT + snprintf(buf, sizeof(buf), "pam_tally2 uid=%u ", uid); + audit_log_user_message(audit_fd, AUDIT_RESP_ACCT_UNLOCK_TIMED, buf, + NULL, NULL, NULL, 1); +#endif + rv = PAM_SUCCESS; + goto cleanup; + } + } + } + +#ifdef HAVE_LIBAUDIT + if (tally->fail_cnt == opts->deny+1) { + /* First say that max number was hit. */ + audit_log_user_message(audit_fd, AUDIT_RESP_ACCT_LOCK, buf, + NULL, NULL, NULL, 1); + } +#endif + + if (!(opts->ctrl & OPT_QUIET)) { + pam_info(pamh, _("Account locked due to %hu failed logins"), + tally->fail_cnt); + } + if (!(opts->ctrl & OPT_NOLOGNOTICE)) { + pam_syslog(pamh, LOG_NOTICE, + "user %s (%lu) tally %hu, deny %hu", + user, (unsigned long)uid, tally->fail_cnt, opts->deny); + } + rv = PAM_AUTH_ERR; /* Only unconditional failure */ + goto cleanup; + } + + /* Lock time check */ + if (opts->lock_time && oldtime) { + if (opts->lock_time + oldtime > time(NULL)) { + /* don't increase fail_cnt or update fail_time when + lock_time applies */ + tally->fail_cnt = oldcnt; + tally->fail_time = oldtime; + + if (!(opts->ctrl & OPT_QUIET)) { + pam_info(pamh, _("Account temporary locked (%ld seconds left)"), + oldtime+opts->lock_time-time(NULL)); + } + if (!(opts->ctrl & OPT_NOLOGNOTICE)) { + pam_syslog(pamh, LOG_NOTICE, + "user %s (%lu) has time limit [%lds left]" + " since last failure.", + user, (unsigned long)uid, + oldtime+opts->lock_time-time(NULL)); + } + rv = PAM_AUTH_ERR; + goto cleanup; + } + } + +cleanup: +#ifdef HAVE_LIBAUDIT + if (audit_fd != -1) { + close(audit_fd); + } +#endif + return rv; +} + +/* --- tally bump function: bump tally for uid by (signed) inc --- */ + +static int +tally_bump (int inc, time_t *oldtime, pam_handle_t *pamh, + uid_t uid, const char *user, struct tally_options *opts) +{ + struct tallylog tally; + tally_t oldcnt; + FILE *tfile = NULL; + const void *remote_host = NULL; + int i, rv; + + tally.fail_cnt = 0; /* !TALLY_HI --> Log opened for update */ + + i = get_tally(pamh, uid, opts->filename, &tfile, &tally); + if (i != PAM_SUCCESS) { + if (tfile) + fclose(tfile); + RETURN_ERROR(i); + } + + /* to remember old fail time (for locktime) */ + if (oldtime) { + *oldtime = (time_t)tally.fail_time; + } + + tally.fail_time = time(NULL); + + (void) pam_get_item(pamh, PAM_RHOST, &remote_host); + if (!remote_host) { + (void) pam_get_item(pamh, PAM_TTY, &remote_host); + if (!remote_host) { + remote_host = "unknown"; + } + } + + strncpy(tally.fail_line, remote_host, + sizeof(tally.fail_line)-1); + tally.fail_line[sizeof(tally.fail_line)-1] = 0; + + oldcnt = tally.fail_cnt; + + if (!(opts->ctrl & OPT_MAGIC_ROOT) || getuid()) { + /* magic_root doesn't change tally */ + tally.fail_cnt += inc; + + if (tally.fail_cnt == TALLY_HI) { /* Overflow *and* underflow. :) */ + tally.fail_cnt -= inc; + pam_syslog(pamh, LOG_ALERT, "Tally %sflowed for user %s", + (inc<0)?"under":"over",user); + } + } + + rv = tally_check(oldcnt, *oldtime, pamh, uid, user, opts, &tally); + + i = set_tally(pamh, uid, opts->filename, &tfile, &tally); + if (i != PAM_SUCCESS) { + if (tfile) + fclose(tfile); + if (rv == PAM_SUCCESS) + RETURN_ERROR( i ); + /* fallthrough */ + } + + return rv; +} + +static int +tally_reset (pam_handle_t *pamh, uid_t uid, struct tally_options *opts) +{ + struct tallylog tally; + FILE *tfile = NULL; + int i; + + /* resets only if not magic root */ + + if ((opts->ctrl & OPT_MAGIC_ROOT) && getuid() == 0) { + return PAM_SUCCESS; + } + + tally.fail_cnt = 0; /* !TALLY_HI --> Log opened for update */ + + i=get_tally(pamh, uid, opts->filename, &tfile, &tally); + if (i != PAM_SUCCESS) { + if (tfile) + fclose(tfile); + RETURN_ERROR(i); + } + + memset(&tally, 0, sizeof(tally)); + + i=set_tally(pamh, uid, opts->filename, &tfile, &tally); + if (i != PAM_SUCCESS) { + if (tfile) + fclose(tfile); + RETURN_ERROR(i); + } + + return PAM_SUCCESS; +} + +/*---------------------------------------------------------------------*/ + +/* --- authentication management functions (only) --- */ + +PAM_EXTERN int +pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + int + rv; + time_t + oldtime = 0; + struct tally_options + options, *opts = &options; + uid_t + uid; + const char + *user; + + rv = tally_parse_args(pamh, opts, PHASE_AUTH, argc, argv); + if (rv != PAM_SUCCESS) + RETURN_ERROR(rv); + + if (flags & PAM_SILENT) + opts->ctrl |= OPT_QUIET; + + rv = pam_get_uid(pamh, &uid, &user, opts); + if (rv != PAM_SUCCESS) + RETURN_ERROR(rv); + + rv = tally_bump(1, &oldtime, pamh, uid, user, opts); + + tally_set_data(pamh, oldtime); + + return rv; +} + +PAM_EXTERN int +pam_sm_setcred(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + int + rv; + time_t + oldtime = 0; + struct tally_options + options, *opts = &options; + uid_t + uid; + const char + *user; + + rv = tally_parse_args(pamh, opts, PHASE_AUTH, argc, argv); + if ( rv != PAM_SUCCESS ) + RETURN_ERROR( rv ); + + rv = pam_get_uid(pamh, &uid, &user, opts); + if ( rv != PAM_SUCCESS ) + RETURN_ERROR( rv ); + + if ( tally_get_data(pamh, &oldtime) != 0 ) + /* no data found */ + return PAM_SUCCESS; + + return tally_reset(pamh, uid, opts); +} + +/*---------------------------------------------------------------------*/ + +/* --- authentication management functions (only) --- */ + +/* To reset failcount of user on successfull login */ + +PAM_EXTERN int +pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + int + rv; + time_t + oldtime = 0; + struct tally_options + options, *opts = &options; + uid_t + uid; + const char + *user; + + rv = tally_parse_args(pamh, opts, PHASE_ACCOUNT, argc, argv); + if ( rv != PAM_SUCCESS ) + RETURN_ERROR( rv ); + + rv = pam_get_uid(pamh, &uid, &user, opts); + if ( rv != PAM_SUCCESS ) + RETURN_ERROR( rv ); + + if ( tally_get_data(pamh, &oldtime) != 0 ) + /* no data found */ + return PAM_SUCCESS; + + return tally_reset(pamh, uid, opts); +} + +/*-----------------------------------------------------------------------*/ + +#ifdef PAM_STATIC + +/* static module data */ + +struct pam_module _pam_tally_modstruct = { + MODULE_NAME, +#ifdef PAM_SM_AUTH + pam_sm_authenticate, + pam_sm_setcred, +#else + NULL, + NULL, +#endif +#ifdef PAM_SM_ACCOUNT + pam_sm_acct_mgmt, +#else + NULL, +#endif + NULL, + NULL, + NULL, +}; + +#endif /* #ifdef PAM_STATIC */ + +/*-----------------------------------------------------------------------*/ + +#else /* #ifndef MAIN */ + +static const char *cline_filename = DEFAULT_LOGFILE; +static tally_t cline_reset = TALLY_HI; /* Default is `interrogate only' */ +static int cline_quiet = 0; + +/* + * Not going to link with pamlib just for these.. :) + */ + +static const char * +pam_errors( int i ) +{ + switch (i) { + case PAM_AUTH_ERR: return _("Authentication error"); + case PAM_SERVICE_ERR: return _("Service error"); + case PAM_USER_UNKNOWN: return _("Unknown user"); + default: return _("Unknown error"); + } +} + +static int +getopts( char **argv ) +{ + const char *pname = *argv; + for ( ; *argv ; (void)(*argv && ++argv) ) { + if ( !strcmp (*argv,"--file") ) cline_filename=*++argv; + else if ( !strcmp(*argv,"-f") ) cline_filename=*++argv; + else if ( !strncmp(*argv,"--file=",7) ) cline_filename=*argv+7; + else if ( !strcmp (*argv,"--user") ) cline_user=*++argv; + else if ( !strcmp (*argv,"-u") ) cline_user=*++argv; + else if ( !strncmp(*argv,"--user=",7) ) cline_user=*argv+7; + else if ( !strcmp (*argv,"--reset") ) cline_reset=0; + else if ( !strcmp (*argv,"-r") ) cline_reset=0; + else if ( !strncmp(*argv,"--reset=",8)) { + if ( sscanf(*argv+8,"%hu",&cline_reset) != 1 ) + fprintf(stderr,_("%s: Bad number given to --reset=\n"),pname), exit(0); + } + else if ( !strcmp (*argv,"--quiet") ) cline_quiet=1; + else { + fprintf(stderr,_("%s: Unrecognised option %s\n"),pname,*argv); + return FALSE; + } + } + return TRUE; +} + +static void +print_one(const struct tallylog *tally, uid_t uid) +{ + static int once; + char *cp; + time_t fail_time; + struct tm *tm; + struct passwd *pwent; + const char *username = "[NONAME]"; + char ptime[80]; + + pwent = getpwuid(uid); + fail_time = tally->fail_time; + tm = localtime(&fail_time); + strftime (ptime, sizeof (ptime), "%D %H:%M:%S", tm); + cp = ptime; + if (pwent) { + username = pwent->pw_name; + } + if (!once) { + printf (_("Login Failures Latest failure From\n")); + once++; + } + printf ("%-15.15s %5hu ", username, tally->fail_cnt); + if (tally->fail_time) { + printf ("%-17.17s %s", cp, tally->fail_line); + } + putchar ('\n'); +} + +int +main( int argc UNUSED, char **argv ) +{ + struct tallylog tally; + + if ( ! getopts( argv+1 ) ) { + printf(_("%s: [-f rooted-filename] [--file rooted-filename]\n" + " [-u username] [--user username]\n" + " [-r] [--reset[=n]] [--quiet]\n"), + *argv); + exit(2); + } + + umask(077); + + /* + * Major difference between individual user and all users: + * --user just handles one user, just like PAM. + * without --user it handles all users, sniffing cline_filename for nonzeros + */ + + if ( cline_user ) { + uid_t uid; + FILE *tfile=0; + struct tally_options opts; + int i; + + memset(&opts, 0, sizeof(opts)); + opts.ctrl = OPT_AUDIT; + i=pam_get_uid(NULL, &uid, NULL, &opts); + if ( i != PAM_SUCCESS ) { + fprintf(stderr,"%s: %s\n",*argv,pam_errors(i)); + exit(1); + } + + i=get_tally(NULL, uid, cline_filename, &tfile, &tally); + if ( i != PAM_SUCCESS ) { + if (tfile) + fclose(tfile); + fprintf(stderr, "%s: %s\n", *argv, pam_errors(i)); + exit(1); + } + + if ( !cline_quiet ) + print_one(&tally, uid); + + if (cline_reset != TALLY_HI) { +#ifdef HAVE_LIBAUDIT + char buf[64]; + int audit_fd = audit_open(); + snprintf(buf, sizeof(buf), "pam_tally2 uid=%u reset=%hu", uid, cline_reset); + audit_log_user_message(audit_fd, AUDIT_USER_ACCT, + buf, NULL, NULL, NULL, 1); + if (audit_fd >=0) + close(audit_fd); +#endif + if (cline_reset == 0) { + memset(&tally, 0, sizeof(tally)); + } else { + tally.fail_cnt = cline_reset; + } + i=set_tally(NULL, uid, cline_filename, &tfile, &tally); + if (i != PAM_SUCCESS) { + if (tfile) fclose(tfile); + fprintf(stderr,"%s: %s\n",*argv,pam_errors(i)); + exit(1); + } + } else { + fclose(tfile); + } + } + else /* !cline_user (ie, operate on all users) */ { + FILE *tfile=fopen(cline_filename, "r"); + uid_t uid=0; + if (!tfile && cline_reset != 0) { + perror(*argv); + exit(1); + } + + for ( ; tfile && !feof(tfile); uid++ ) { + if ( !fread(&tally, sizeof(tally), 1, tfile) + || !tally.fail_cnt ) { + continue; + } + print_one(&tally, uid); + } + if (tfile) + fclose(tfile); + if ( cline_reset!=0 && cline_reset!=TALLY_HI ) { + fprintf(stderr,_("%s: Can't reset all users to non-zero\n"),*argv); + } + else if ( !cline_reset ) { +#ifdef HAVE_LIBAUDIT + char buf[64]; + int audit_fd = audit_open(); + snprintf(buf, sizeof(buf), "pam_tally2 uid=all reset=0"); + audit_log_user_message(audit_fd, AUDIT_USER_ACCT, + buf, NULL, NULL, NULL, 1); + if (audit_fd >=0) + close(audit_fd); +#endif + tfile=fopen(cline_filename, "w"); + if ( !tfile ) perror(*argv), exit(0); + fclose(tfile); + } + } + return 0; +} + + +#endif /* #ifndef MAIN */ diff --git a/modules/pam_tally2/pam_tally2_app.c b/modules/pam_tally2/pam_tally2_app.c new file mode 100644 index 00000000..681ed690 --- /dev/null +++ b/modules/pam_tally2/pam_tally2_app.c @@ -0,0 +1,7 @@ +/* + # This seemed like such a good idea at the time. :) + */ + +#define MAIN +#include "pam_tally2.c" + diff --git a/modules/pam_tally2/tallylog.h b/modules/pam_tally2/tallylog.h new file mode 100644 index 00000000..596b1dac --- /dev/null +++ b/modules/pam_tally2/tallylog.h @@ -0,0 +1,52 @@ +/* + * Copyright 2006, Red Hat, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Red Hat, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RED HAT, INC. AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * tallylog.h - login failure data file format + * + * The new login failure file is not compatible with the old faillog(8) format + * Each record in the file represents a separate UID and the file + * is indexed in that fashion. + */ + + +#ifndef _TALLYLOG_H +#define _TALLYLOG_H + +#include + +struct tallylog { + char fail_line[52]; /* rhost or tty of last failure */ + uint16_t reserved; /* reserved for future use */ + uint16_t fail_cnt; /* failures since last success */ + uint64_t fail_time; /* time of last failure */ +}; +/* 64 bytes / entry */ + +#endif diff --git a/modules/pam_tally2/tst-pam_tally2 b/modules/pam_tally2/tst-pam_tally2 new file mode 100755 index 00000000..83c71f41 --- /dev/null +++ b/modules/pam_tally2/tst-pam_tally2 @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_tally2.so -- cgit v1.2.3 From 4a67d64dd0cb01c40e675f48f0c6ea3d08e53664 Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Fri, 28 Nov 2008 14:29:12 +0000 Subject: Relevant BUGIDs: Purpose of commit: new feature Commit summary: --------------- 2008-11-28 Tomas Mraz * modules/pam_tally2/pam_tally2.c (tally_check): Fix info format to be the same as in pam_tally. * configure.in: Add modules/pam_timestamp/Makefile. * doc/sag/Linux-PAM_SAG.xml: Include pam_timestamp.xml. * doc/sag/pam_timestamp.xml: New. * libpam/pam_static_modules.h: Add pam_timestamp static struct. * modules/Makefile.am: Add pam_timestamp directory. * modules/pam_timestamp/Makefile.am: New. * modules/pam_timestamp/README.xml: New. * modules/pam_timestamp/hmacsha1.h: New. * modules/pam_timestamp/sha1.h: New. * modules/pam_timestamp/pam_timestamp.8.xml: New. * modules/pam_timestamp/pam_timestamp_check.8.xml: New. * modules/pam_timestamp/pam_timestamp.c: New. * modules/pam_timestamp/pam_timestamp_check.c: New. * modules/pam_timestamp/hmacfile.c: New. * modules/pam_timestamp/hmacsha1.c: New. * modules/pam_timestamp/sha1.c: New. * modules/pam_timestamp/tst-pam_timestamp: New. * po/POTFILES.in: Add pam_timestamp sources. * po/*.po: Regenerate. * po/cs.po: Updated translations. --- modules/pam_tally2/pam_tally2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'modules/pam_tally2') diff --git a/modules/pam_tally2/pam_tally2.c b/modules/pam_tally2/pam_tally2.c index 9ae3180d..5924edf9 100644 --- a/modules/pam_tally2/pam_tally2.c +++ b/modules/pam_tally2/pam_tally2.c @@ -517,8 +517,8 @@ tally_check (tally_t oldcnt, time_t oldtime, pam_handle_t *pamh, uid_t uid, #endif if (!(opts->ctrl & OPT_QUIET)) { - pam_info(pamh, _("Account locked due to %hu failed logins"), - tally->fail_cnt); + pam_info(pamh, _("Account locked due to %u failed logins"), + (unsigned int)tally->fail_cnt); } if (!(opts->ctrl & OPT_NOLOGNOTICE)) { pam_syslog(pamh, LOG_NOTICE, -- cgit v1.2.3 From 54febaaeb968aa3d9aa85583cd49ff058c6e1368 Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Fri, 12 Dec 2008 14:05:41 +0000 Subject: Relevant BUGIDs: Purpose of commit: bugfix Commit summary: --------------- 2008-12-12 Tomas Mraz * modules/pam_tally2/pam_tally2.c (get_tally): Test for EACCES instead of EPERM. * modules/pam_tally2/pam_tally2.8.xml: Fix documentation. --- modules/pam_tally2/pam_tally2.8.xml | 2 +- modules/pam_tally2/pam_tally2.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'modules/pam_tally2') diff --git a/modules/pam_tally2/pam_tally2.8.xml b/modules/pam_tally2/pam_tally2.8.xml index dc284a1d..a7a3fc47 100644 --- a/modules/pam_tally2/pam_tally2.8.xml +++ b/modules/pam_tally2/pam_tally2.8.xml @@ -370,7 +370,7 @@ xscreensaver. As this would make it impossible to share PAM configuration with such services the following workaround is used: If the data file cannot be opened because of insufficient permissions - (EPERM) the module returns + (EACCES) the module returns PAM_IGNORE. diff --git a/modules/pam_tally2/pam_tally2.c b/modules/pam_tally2/pam_tally2.c index 5924edf9..faa6942e 100644 --- a/modules/pam_tally2/pam_tally2.c +++ b/modules/pam_tally2/pam_tally2.c @@ -351,7 +351,7 @@ get_tally(pam_handle_t *pamh, uid_t uid, const char *filename, umask(oldmask); if ( !*tfile ) { #ifndef MAIN - if (save_errno == EPERM) { + if (save_errno == EACCES) { return PAM_IGNORE; /* called with insufficient access rights */ } #endif @@ -380,7 +380,7 @@ get_tally(pam_handle_t *pamh, uid_t uid, const char *filename, if (!(*tfile = fopen(filename, "r+"))) { #ifndef MAIN - if (errno == EPERM) /* called with insufficient access rights */ + if (errno == EACCES) /* called with insufficient access rights */ return PAM_IGNORE; #endif pam_syslog(pamh, LOG_ALERT, "Error opening %s for update: %m", filename); -- cgit v1.2.3 From 42f4743cc3ca046833afcaeec01f9793d74bbfb4 Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Fri, 27 Feb 2009 14:29:39 +0000 Subject: Relevant BUGIDs: Purpose of commit: new feature Commit summary: --------------- 2009-02-27 Tomas Mraz * modules/pam_mkhomedir/pam_mkhomedir.c(create_homedir): Replace signal() with sigaction(). * modules/pam_namespace/pam_namespace.c(inst_init, cleanup_tmpdirs): Likewise. * modules/pam_unix/pam_unix_acct.c(_unix_run_verify_binary): Likewise. * modules/pam_unix/pam_unix_passwd.c(_unix_run_update_binary): Likewise. * modules/pam_unix/passverify.c(su_sighandler): Likewise. * modules/pam_unix/support.c(_unix_run_helper_binary): Likewise. * modules/pam_tally2/Makefile.am: Link the pam_tally2 app to libpam for auxiliary functions. * modules/pam_tally2/pam_tally2.8.xml: Drop non-existing no_reset option. Document new serialize option. * modules/pam_tally2/pam_tally2.c: Add support for the new serialize option. (_cleanup, tally_set_data, tally_get_data): Add tally file handle to tally PAM data. Needed for fcntl() locking. (get_tally): Use low level file access instead of stdio buffered FILE. If serialize option is used lock the tally file access. (set_tally, tally_bump, tally_reset): Use low level file access instead of stdio buffered FILE. Close the file handle only when it is not owned by PAM data. (pam_sm_authenticate, pam_sm_setcred, pam_sm_acct_mgmt): Pass the tally file handle to tally_set_data(). Get it from tally_get_data(). (main): Use low level file access instead of stdio buffered FILE. --- modules/pam_tally2/Makefile.am | 2 +- modules/pam_tally2/pam_tally2.8.xml | 32 ++++-- modules/pam_tally2/pam_tally2.c | 216 ++++++++++++++++++++++++------------ 3 files changed, 166 insertions(+), 84 deletions(-) (limited to 'modules/pam_tally2') diff --git a/modules/pam_tally2/Makefile.am b/modules/pam_tally2/Makefile.am index 6f843e1f..06cdf554 100644 --- a/modules/pam_tally2/Makefile.am +++ b/modules/pam_tally2/Makefile.am @@ -25,7 +25,7 @@ if HAVE_VERSIONING pam_tally2_la_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map endif -pam_tally2_LDADD = $(LIBAUDIT) +pam_tally2_LDADD = -L$(top_builddir)/libpam -lpam $(LIBAUDIT) securelib_LTLIBRARIES = pam_tally2.la sbin_PROGRAMS = pam_tally2 diff --git a/modules/pam_tally2/pam_tally2.8.xml b/modules/pam_tally2/pam_tally2.8.xml index a7a3fc47..255fcea4 100644 --- a/modules/pam_tally2/pam_tally2.8.xml +++ b/modules/pam_tally2/pam_tally2.8.xml @@ -42,6 +42,9 @@ root_unlock_time=n + + serialize + audit @@ -244,16 +247,6 @@ - - - - - - - Don't reset count on successful entry, only decrement. - - - @@ -278,6 +271,23 @@ + + + + + + + Serialize access to the tally file using locks. This option might + be used only for non-multithreaded services because it depends on + the fcntl locking of the tally file. Also it is a good idea to use + this option only in such configurations where the time between auth + phase and account or setcred phase is not dependent on the + authenticating client. Otherwise the authenticating client will be + able to prevent simultaneous authentications by the same user by + simply artificially prolonging the time the file record lock is held. + + + @@ -431,7 +441,7 @@ session optional pam_mail.so standard AUTHOR - pam_tally was written by Tim Baverstock and Tomas Mraz. + pam_tally2 was written by Tim Baverstock and Tomas Mraz. diff --git a/modules/pam_tally2/pam_tally2.c b/modules/pam_tally2/pam_tally2.c index faa6942e..3490aa15 100644 --- a/modules/pam_tally2/pam_tally2.c +++ b/modules/pam_tally2/pam_tally2.c @@ -63,6 +63,9 @@ #include #include #include +#include +#include +#include #include "tallylog.h" #ifndef TRUE @@ -87,9 +90,9 @@ /* #define PAM_SM_SESSION */ /* #define PAM_SM_PASSWORD */ -#include #include #endif +#include #include /*---------------------------------------------------------------------*/ @@ -120,7 +123,9 @@ struct tally_options { #define OPT_QUIET 040 #define OPT_AUDIT 0100 #define OPT_NOLOGNOTICE 0400 +#define OPT_SERIALIZE 01000 +#define MAX_LOCK_WAITING_TIME 10 /*---------------------------------------------------------------------*/ @@ -188,6 +193,9 @@ tally_parse_args(pam_handle_t *pamh, struct tally_options *opts, else if ( ! strcmp( *argv, "magic_root" ) ) { opts->ctrl |= OPT_MAGIC_ROOT; } + else if ( ! strcmp( *argv, "serialize" ) ) { + opts->ctrl |= OPT_SERIALIZE; + } else if ( ! strcmp( *argv, "even_deny_root_account" ) || ! strcmp( *argv, "even_deny_root" ) ) { log_phase_no_auth(pamh, phase, *argv); @@ -291,34 +299,44 @@ pam_get_uid(pam_handle_t *pamh, uid_t *uid, const char **userp, struct tally_opt #ifndef MAIN +struct tally_data { + time_t time; + int tfile; +}; + static void -_cleanup(pam_handle_t *pamh UNUSED, void *data, int error_status UNUSED) +_cleanup(pam_handle_t *pamh UNUSED, void *void_data, int error_status UNUSED) { + struct tally_data *data = void_data; + if (data->tfile != -1) + close(data->tfile); free(data); } - static void -tally_set_data( pam_handle_t *pamh, time_t oldtime ) +tally_set_data( pam_handle_t *pamh, time_t oldtime, int tfile ) { - time_t *data; + struct tally_data *data; - if ( (data=malloc(sizeof(time_t))) != NULL ) { - *data = oldtime; + if ( (data=malloc(sizeof(*data))) != NULL ) { + data->time = oldtime; + data->tfile = tfile; pam_set_data(pamh, MODULE_NAME, (void *)data, _cleanup); } } static int -tally_get_data( pam_handle_t *pamh, time_t *oldtime ) +tally_get_data( pam_handle_t *pamh, time_t *oldtime, int *tfile ) { int rv; - const void *data; - - rv = pam_get_data(pamh, MODULE_NAME, &data); - if ( rv == PAM_SUCCESS && data != NULL && oldtime != NULL ) { - *oldtime = *(const time_t *)data; - pam_set_data(pamh, MODULE_NAME, NULL, NULL); + const void *void_data; + const struct tally_data *data; + + rv = pam_get_data(pamh, MODULE_NAME, &void_data); + if ( rv == PAM_SUCCESS && void_data != NULL && oldtime != NULL ) { + data = void_data; + *oldtime = data->time; + *tfile = data->tfile; } else { rv = -1; @@ -334,36 +352,44 @@ tally_get_data( pam_handle_t *pamh, time_t *oldtime ) /* If on entry tallyfile doesn't exist, creation is attempted. */ +static void +alarm_handler(int sig UNUSED) +{ /* we just need to ignore it */ +} + static int get_tally(pam_handle_t *pamh, uid_t uid, const char *filename, - FILE **tfile, struct tallylog *tally) + int *tfile, struct tallylog *tally, unsigned int ctrl) { struct stat fileinfo; int lstat_ret; + void *void_tally = tally; + int preopened = 0; + + if (*tfile != -1) { + preopened = 1; + goto skip_open; + } lstat_ret = lstat(filename, &fileinfo); if (lstat_ret) { - int save_errno; - int oldmask = umask(077); - *tfile=fopen(filename, "a"); - save_errno = errno; + *tfile=open(filename, O_APPEND|O_CREAT, 0700); /* Create file, or append-open in pathological case. */ - umask(oldmask); - if ( !*tfile ) { + if (*tfile == -1) { #ifndef MAIN - if (save_errno == EACCES) { + if (errno == EACCES) { return PAM_IGNORE; /* called with insufficient access rights */ } #endif - errno = save_errno; pam_syslog(pamh, LOG_ALERT, "Couldn't create %s: %m", filename); return PAM_AUTH_ERR; } - lstat_ret = fstat(fileno(*tfile),&fileinfo); - fclose(*tfile); - *tfile = NULL; + lstat_ret = fstat(*tfile, &fileinfo); + close(*tfile); } + *tfile = -1; + if ( lstat_ret ) { pam_syslog(pamh, LOG_ALERT, "Couldn't stat %s", filename); return PAM_AUTH_ERR; @@ -378,7 +404,7 @@ get_tally(pam_handle_t *pamh, uid_t uid, const char *filename, return PAM_AUTH_ERR; } - if (!(*tfile = fopen(filename, "r+"))) { + if ((*tfile = open(filename, O_RDWR)) == -1) { #ifndef MAIN if (errno == EACCES) /* called with insufficient access rights */ return PAM_IGNORE; @@ -388,16 +414,46 @@ get_tally(pam_handle_t *pamh, uid_t uid, const char *filename, return PAM_AUTH_ERR; } - if (fseeko(*tfile, (off_t)uid*(off_t)sizeof(*tally), SEEK_SET)) { - pam_syslog(pamh, LOG_ALERT, "fseek failed for %s: %m", filename); - fclose(*tfile); - *tfile = NULL; +skip_open: + if (lseek(*tfile, (off_t)uid*(off_t)sizeof(*tally), SEEK_SET) == (off_t)-1) { + pam_syslog(pamh, LOG_ALERT, "lseek failed for %s: %m", filename); + if (!preopened) { + close(*tfile); + *tfile = -1; + } return PAM_AUTH_ERR; } + if (!preopened && (ctrl & OPT_SERIALIZE)) { + /* this code is not thread safe as it uses fcntl locks and alarm() + so never use serialize with multithreaded services */ + struct sigaction newsa, oldsa; + unsigned int oldalarm; + int rv; + + memset(&newsa, '\0', sizeof(newsa)); + newsa.sa_handler = alarm_handler; + sigaction(SIGALRM, &newsa, &oldsa); + oldalarm = alarm(MAX_LOCK_WAITING_TIME); + + rv = lockf(*tfile, F_LOCK, sizeof(*tally)); + /* lock failure is not fatal, we attempt to read the tally anyway */ + + /* reinstate the eventual old alarm handler */ + if (rv == -1 && errno == EINTR) { + if (oldalarm > MAX_LOCK_WAITING_TIME) { + oldalarm -= MAX_LOCK_WAITING_TIME; + } else if (oldalarm > 0) { + oldalarm = 1; + } + } + sigaction(SIGALRM, &oldsa, NULL); + alarm(oldalarm); + } + if (fileinfo.st_size < (off_t)(uid+1)*(off_t)sizeof(*tally)) { memset(tally, 0, sizeof(*tally)); - } else if (fread(tally, sizeof(*tally), 1, *tfile) == 0) { + } else if (pam_modutil_read(*tfile, void_tally, sizeof(*tally)) != sizeof(*tally)) { memset(tally, 0, sizeof(*tally)); /* Shouldn't happen */ } @@ -409,29 +465,28 @@ get_tally(pam_handle_t *pamh, uid_t uid, const char *filename, /*---------------------------------------------------------------------*/ -/* --- Support function: update and close tallyfile with tally!=TALLY_HI --- */ +/* --- Support function: update tallyfile with tally!=TALLY_HI --- */ static int set_tally(pam_handle_t *pamh, uid_t uid, - const char *filename, FILE **tfile, struct tallylog *tally) + const char *filename, int *tfile, struct tallylog *tally) { + void *void_tally = tally; if (tally->fail_cnt != TALLY_HI) { - if (fseeko(*tfile, (off_t)uid * sizeof(*tally), SEEK_SET)) { - pam_syslog(pamh, LOG_ALERT, "fseek failed for %s: %m", filename); + if (lseek(*tfile, (off_t)uid * sizeof(*tally), SEEK_SET) == (off_t)-1) { + pam_syslog(pamh, LOG_ALERT, "lseek failed for %s: %m", filename); return PAM_AUTH_ERR; } - if (fwrite(tally, sizeof(*tally), 1, *tfile) == 0) { - pam_syslog(pamh, LOG_ALERT, "update (fwrite) failed for %s: %m", filename); + if (pam_modutil_write(*tfile, void_tally, sizeof(*tally)) != sizeof(*tally)) { + pam_syslog(pamh, LOG_ALERT, "update (write) failed for %s: %m", filename); return PAM_AUTH_ERR; } } - if (fclose(*tfile)) { - *tfile = NULL; - pam_syslog(pamh, LOG_ALERT, "update (fclose) failed for %s: %m", filename); + if (fsync(*tfile)) { + pam_syslog(pamh, LOG_ALERT, "update (fsync) failed for %s: %m", filename); return PAM_AUTH_ERR; } - *tfile=NULL; return PAM_SUCCESS; } @@ -566,20 +621,21 @@ cleanup: static int tally_bump (int inc, time_t *oldtime, pam_handle_t *pamh, - uid_t uid, const char *user, struct tally_options *opts) + uid_t uid, const char *user, struct tally_options *opts, int *tfile) { struct tallylog tally; tally_t oldcnt; - FILE *tfile = NULL; const void *remote_host = NULL; int i, rv; tally.fail_cnt = 0; /* !TALLY_HI --> Log opened for update */ - i = get_tally(pamh, uid, opts->filename, &tfile, &tally); + i = get_tally(pamh, uid, opts->filename, tfile, &tally, opts->ctrl); if (i != PAM_SUCCESS) { - if (tfile) - fclose(tfile); + if (*tfile != -1) { + close(*tfile); + *tfile = -1; + } RETURN_ERROR(i); } @@ -617,23 +673,28 @@ tally_bump (int inc, time_t *oldtime, pam_handle_t *pamh, rv = tally_check(oldcnt, *oldtime, pamh, uid, user, opts, &tally); - i = set_tally(pamh, uid, opts->filename, &tfile, &tally); + i = set_tally(pamh, uid, opts->filename, tfile, &tally); if (i != PAM_SUCCESS) { - if (tfile) - fclose(tfile); + if (*tfile != -1) { + close(*tfile); + *tfile = -1; + } if (rv == PAM_SUCCESS) RETURN_ERROR( i ); /* fallthrough */ + } else if (!(opts->ctrl & OPT_SERIALIZE)) { + close(*tfile); + *tfile = -1; } return rv; } static int -tally_reset (pam_handle_t *pamh, uid_t uid, struct tally_options *opts) +tally_reset (pam_handle_t *pamh, uid_t uid, struct tally_options *opts, int old_tfile) { struct tallylog tally; - FILE *tfile = NULL; + int tfile = old_tfile; int i; /* resets only if not magic root */ @@ -644,10 +705,10 @@ tally_reset (pam_handle_t *pamh, uid_t uid, struct tally_options *opts) tally.fail_cnt = 0; /* !TALLY_HI --> Log opened for update */ - i=get_tally(pamh, uid, opts->filename, &tfile, &tally); + i=get_tally(pamh, uid, opts->filename, &tfile, &tally, opts->ctrl); if (i != PAM_SUCCESS) { - if (tfile) - fclose(tfile); + if (tfile != old_tfile) /* the descriptor is not owned by pam data */ + close(tfile); RETURN_ERROR(i); } @@ -655,11 +716,14 @@ tally_reset (pam_handle_t *pamh, uid_t uid, struct tally_options *opts) i=set_tally(pamh, uid, opts->filename, &tfile, &tally); if (i != PAM_SUCCESS) { - if (tfile) - fclose(tfile); + if (tfile != old_tfile) /* the descriptor is not owned by pam data */ + close(tfile); RETURN_ERROR(i); } + if (tfile != old_tfile) + close(tfile); + return PAM_SUCCESS; } @@ -672,7 +736,7 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED, int argc, const char **argv) { int - rv; + rv, tfile = -1; time_t oldtime = 0; struct tally_options @@ -693,9 +757,9 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED, if (rv != PAM_SUCCESS) RETURN_ERROR(rv); - rv = tally_bump(1, &oldtime, pamh, uid, user, opts); + rv = tally_bump(1, &oldtime, pamh, uid, user, opts, &tfile); - tally_set_data(pamh, oldtime); + tally_set_data(pamh, oldtime, tfile); return rv; } @@ -705,7 +769,7 @@ pam_sm_setcred(pam_handle_t *pamh, int flags UNUSED, int argc, const char **argv) { int - rv; + rv, tfile = -1; time_t oldtime = 0; struct tally_options @@ -723,11 +787,15 @@ pam_sm_setcred(pam_handle_t *pamh, int flags UNUSED, if ( rv != PAM_SUCCESS ) RETURN_ERROR( rv ); - if ( tally_get_data(pamh, &oldtime) != 0 ) + if ( tally_get_data(pamh, &oldtime, &tfile) != 0 ) /* no data found */ return PAM_SUCCESS; - return tally_reset(pamh, uid, opts); + rv = tally_reset(pamh, uid, opts, tfile); + + pam_set_data(pamh, MODULE_NAME, NULL, NULL); + + return rv; } /*---------------------------------------------------------------------*/ @@ -741,7 +809,7 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED, int argc, const char **argv) { int - rv; + rv, tfile = -1; time_t oldtime = 0; struct tally_options @@ -759,11 +827,15 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED, if ( rv != PAM_SUCCESS ) RETURN_ERROR( rv ); - if ( tally_get_data(pamh, &oldtime) != 0 ) + if ( tally_get_data(pamh, &oldtime, &tfile) != 0 ) /* no data found */ return PAM_SUCCESS; - return tally_reset(pamh, uid, opts); + rv = tally_reset(pamh, uid, opts, tfile); + + pam_set_data(pamh, MODULE_NAME, NULL, NULL); + + return rv; } /*-----------------------------------------------------------------------*/ @@ -895,7 +967,7 @@ main( int argc UNUSED, char **argv ) if ( cline_user ) { uid_t uid; - FILE *tfile=0; + int tfile = -1; struct tally_options opts; int i; @@ -907,10 +979,10 @@ main( int argc UNUSED, char **argv ) exit(1); } - i=get_tally(NULL, uid, cline_filename, &tfile, &tally); + i=get_tally(NULL, uid, cline_filename, &tfile, &tally, 0); if ( i != PAM_SUCCESS ) { - if (tfile) - fclose(tfile); + if (tfile != -1) + close(tfile); fprintf(stderr, "%s: %s\n", *argv, pam_errors(i)); exit(1); } @@ -934,13 +1006,13 @@ main( int argc UNUSED, char **argv ) tally.fail_cnt = cline_reset; } i=set_tally(NULL, uid, cline_filename, &tfile, &tally); + close(tfile); if (i != PAM_SUCCESS) { - if (tfile) fclose(tfile); fprintf(stderr,"%s: %s\n",*argv,pam_errors(i)); exit(1); } } else { - fclose(tfile); + close(tfile); } } else /* !cline_user (ie, operate on all users) */ { -- cgit v1.2.3 From fbd40f8764ac17611e1e7f9464565a1b3e7792a2 Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Mon, 1 Jun 2009 07:03:19 +0000 Subject: Relevant BUGIDs: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Purpose of commit: cleanup Commit summary: --------------- 2009-06-01 Ville Skyttä * modules/pam_limits/pam_limits.8.xml: Only *.conf files are parsed. Spelling fixes. * modules/pam_access/pam_access.8.xml: Spelling fixes. * modules/pam_cracklib/pam_cracklib.8.xml: Likewise. * modules/pam_echo/pam_echo.8.xml: Likewise. * modules/pam_env/pam_env.8.xml: Likewise. * modules/pam_exec/pam_exec.8.xml: Likewise. * modules/pam_filter/pam_filter.8.xml: Likewise. * modules/pam_ftp/pam_ftp.8.xml: Likewise. * modules/pam_group/pam_group.8.xml: Likewise. * modules/pam_issue/pam_issue.8.xml: Likewise. * modules/pam_lastlog/pam_lastlog.8.xml: Likewise. * modules/pam_listfile/pam_listfile.8.xml: Likewise. * modules/pam_localuser/pam_localuser.8.xml: Likewise. * modules/pam_loginuid/pam_loginuid.8.xml: Likewise. * modules/pam_mkhomedir/pam_mkhomedir.8.xml: Likewise. * modules/pam_motd/pam_motd.8.xml: Likewise. * modules/pam_namespace/pam_namespace.8.xml: Likewise. * modules/pam_pwhistory/pam_pwhistory.8.xml: Likewise. * modules/pam_selinux/pam_selinux.8.xml: Likewise. * modules/pam_succeed_if/pam_succeed_if.8.xml: Likewise. * modules/pam_tally/pam_tally.8.xml: Likewise. * modules/pam_tally2/pam_tally2.8.xml: Likewise. * modules/pam_time/pam_time.8.xml: Likewise. * modules/pam_timestamp/pam_timestamp.8.xml: Likewise. * modules/pam_timestamp/pam_timestamp_check.8.xml: Likewise. * modules/pam_tty_audit/pam_tty_audit.8.xml: Likewise. * modules/pam_umask/pam_umask.8.xml: Likewise. * modules/pam_unix/pam_unix.8.xml: Likewise. * modules/pam_xauth/pam_xauth.8.xml: Likewise. --- modules/pam_tally2/pam_tally2.8.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'modules/pam_tally2') diff --git a/modules/pam_tally2/pam_tally2.8.xml b/modules/pam_tally2/pam_tally2.8.xml index 255fcea4..4ad529fd 100644 --- a/modules/pam_tally2/pam_tally2.8.xml +++ b/modules/pam_tally2/pam_tally2.8.xml @@ -122,7 +122,7 @@ If something weird happens (like unable to open the file), - return with PAM_SUCESS if + return with PAM_SUCCESS if is given, else with the corresponding PAM error code. @@ -230,7 +230,7 @@ If the module is invoked by a user with uid=0 the - counter is not incremented. The sys-admin should use this + counter is not incremented. The sysadmin should use this for user launched services, like su, otherwise this argument should be omitted. @@ -265,7 +265,7 @@ This option implies option. Allow access after n seconds - to root acccount after failed attempt. If this option is used + to root account after failed attempt. If this option is used the root user will be locked out for the specified amount of time after he exceeded his maximum allowed attempts. @@ -301,7 +301,7 @@ Account phase resets attempts counter if the user is not magic root. - This phase can be used optionaly for services which don't call + This phase can be used optionally for services which don't call pam_setcred3 correctly or if the reset should be done regardless @@ -315,7 +315,7 @@ If the module is invoked by a user with uid=0 the - counter is not changed. The sys-admin should use this + counter is not changed. The sysadmin should use this for user launched services, like su, otherwise this argument should be omitted. @@ -343,7 +343,7 @@ A invalid option was given, the module was not able - to retrive the user name, no valid counter file + to retrieve the user name, no valid counter file was found, or too many failed logins. @@ -352,7 +352,7 @@ PAM_SUCCESS - Everything was successfull. + Everything was successful. -- cgit v1.2.3