From 9a2b37a4e68b3af02b3c91fde1719c18175dcc63 Mon Sep 17 00:00:00 2001 From: Gabe Date: Sep 12 2019 17:18:25 +0000 Subject: Add local_users_only option to pam_faillock (#1566637) --- diff --git a/CHANGELOG.pam-redhat b/CHANGELOG.pam-redhat index bf48781..e3f355d 100644 --- a/CHANGELOG.pam-redhat +++ b/CHANGELOG.pam-redhat @@ -4,8 +4,9 @@ Changelog of pam-redhat modules This changelog describes only changes after branching of pam-redhat modules without autoconf support. -1.1.1: +1.1.1: Thu Sep 12 2019 * pam_faillock: moved the faillock.conf man page to section 5 +* pam_faillock: added local_users_only option 1.1.0: Mon Sep 9 2019 * pam_faillock: added support for reading /etc/security/faillock.conf diff --git a/pam_faillock/faillock.conf b/pam_faillock/faillock.conf index d1e5e81..f5288dc 100644 --- a/pam_faillock/faillock.conf +++ b/pam_faillock/faillock.conf @@ -17,6 +17,15 @@ # Enabled if option is present. # no_log_info # +# Only track failed user authentications attempts for local users +# in /etc/passwd and ignore centralized (AD, IdM, LDAP, etc.) users. +# The `faillock` command will also no longer track user failed +# authentication attempts. Enabling this option will prevent a +# double-lockout scenario where a user is locked out locally and +# in the centralized mechanism. +# Enabled if option is present. +# local_users_only +# # Deny access if the number of consecutive authentication failures # for this user during the recent interval exceeds n tries. # The default is 3. diff --git a/pam_faillock/faillock.conf.5.xml b/pam_faillock/faillock.conf.5.xml index 5a1cb77..2f76b8f 100644 --- a/pam_faillock/faillock.conf.5.xml +++ b/pam_faillock/faillock.conf.5.xml @@ -80,6 +80,22 @@ + + + + Only track failed user authentications attempts for local users + in /etc/passwd and ignore centralized (AD, IdM, LDAP, etc.) users. + The faillock8 + command will also no longer track user failed + authentication attempts. Enabling this option will prevent a + double-lockout scenario where a user is locked out locally and + in the centralized mechanism. + + + + + diff --git a/pam_faillock/pam_faillock.c b/pam_faillock/pam_faillock.c index 76e6686..e4be874 100644 --- a/pam_faillock/pam_faillock.c +++ b/pam_faillock/pam_faillock.c @@ -73,6 +73,9 @@ #define FAILLOCK_CONF_MAX_LINELEN 1023 #define FAILLOCK_ERROR_CONF_OPEN -3 #define FAILLOCK_ERROR_CONF_MALFORMED -4 +#define DEFAULT_LOCAL_USERS_ONLY 0 + +#define PATH_PASSWD "/etc/passwd" struct options { unsigned int action; @@ -86,6 +89,7 @@ struct options { const char *user; const char *admin_group; int failures; + int local_users_only; uint64_t latest_time; uid_t uid; int is_admin; @@ -119,6 +123,7 @@ args_parse(pam_handle_t *pamh, int argc, const char **argv, opts->fail_interval = 900; opts->unlock_time = 600; opts->root_unlock_time = MAX_TIME_INTERVAL+1; + opts->local_users_only = DEFAULT_LOCAL_USERS_ONLY; if ((rv=read_config_file(pamh, opts, opts->conf)) != PAM_SUCCESS) { pam_syslog(pamh, LOG_DEBUG, @@ -311,11 +316,55 @@ void set_conf_opt(pam_handle_t *pamh, struct options *opts, const char *name, co else if (strcmp(name, "no_log_info") == 0) { opts->flags |= FAILLOCK_FLAG_NO_LOG_INFO; } + else if (strcmp(name, "local_users_only") == 0) { + opts->local_users_only = 1; + } else { pam_syslog(pamh, LOG_ERR, "Unknown option: %s", name); } } +static int check_local_user (pam_handle_t *pamh, const char *user) +{ + struct passwd pw, *pwp; + char buf[4096]; + int found = 0; + FILE *fp; + int errn; + + fp = fopen(PATH_PASSWD, "r"); + if (fp == NULL) { + pam_syslog(pamh, LOG_ERR, "unable to open %s: %m", + PATH_PASSWD); + return -1; + } + + for (;;) { + errn = fgetpwent_r(fp, &pw, buf, sizeof (buf), &pwp); + if (errn == ERANGE) { + pam_syslog(pamh, LOG_WARNING, "%s contains very long lines; corrupted?", + PATH_PASSWD); + /* we can continue here as next call will read further */ + continue; + } + if (errn != 0) + break; + if (strcmp(pwp->pw_name, user) == 0) { + found = 1; + break; + } + } + + fclose (fp); + + if (errn != 0 && errn != ENOENT) { + pam_syslog(pamh, LOG_ERR, "unable to enumerate local accounts: %m"); + return -1; + } else { + return found; + } +} + static int get_pam_user(pam_handle_t *pamh, struct options *opts) { const char *user; @@ -615,28 +664,34 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, return rv; } - switch (opts.action) { - case FAILLOCK_ACTION_PREAUTH: - rv = check_tally(pamh, &opts, &tallies, &fd); - if (rv == PAM_AUTH_ERR && !(opts.flags & FAILLOCK_FLAG_SILENT)) { - faillock_message(pamh, &opts); - } - break; + if (opts.local_users_only && check_local_user (pamh, opts.user) == 0) { + /* skip the check if a non-local user */ + rv = 0; + } else { - case FAILLOCK_ACTION_AUTHSUCC: - rv = check_tally(pamh, &opts, &tallies, &fd); - if (rv == PAM_SUCCESS) { - reset_tally(pamh, &opts, &fd); - } - break; + switch (opts.action) { + case FAILLOCK_ACTION_PREAUTH: + rv = check_tally(pamh, &opts, &tallies, &fd); + if (rv == PAM_AUTH_ERR && !(opts.flags & FAILLOCK_FLAG_SILENT)) { + faillock_message(pamh, &opts); + } + break; - case FAILLOCK_ACTION_AUTHFAIL: - rv = check_tally(pamh, &opts, &tallies, &fd); - if (rv == PAM_SUCCESS) { - rv = PAM_IGNORE; /* this return value should be ignored */ - write_tally(pamh, &opts, &tallies, &fd); - } - break; + case FAILLOCK_ACTION_AUTHSUCC: + rv = check_tally(pamh, &opts, &tallies, &fd); + if (rv == PAM_SUCCESS) { + reset_tally(pamh, &opts, &fd); + } + break; + + case FAILLOCK_ACTION_AUTHFAIL: + rv = check_tally(pamh, &opts, &tallies, &fd); + if (rv == PAM_SUCCESS) { + rv = PAM_IGNORE; /* this return value should be ignored */ + write_tally(pamh, &opts, &tallies, &fd); + } + break; + } } tally_cleanup(&tallies, fd); @@ -673,8 +728,14 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, return rv; } - check_tally(pamh, &opts, &tallies, &fd); /* for auditing */ - reset_tally(pamh, &opts, &fd); + if (opts.local_users_only && check_local_user (pamh, opts.user) == 0) { + /* skip the check if a non-local user */ + rv = 0; + } else { + + check_tally(pamh, &opts, &tallies, &fd); /* for auditing */ + reset_tally(pamh, &opts, &fd); + } tally_cleanup(&tallies, fd);