From 30135d362da215339fe0cedb878942189db0b864 Mon Sep 17 00:00:00 2001 From: Brian Ward Date: Sep 04 2019 21:06:33 +0000 Subject: [PATCH 1/3] Adds the capability for pam_faillock to read from /etc/security/faillock.conf (#1537242) --- diff --git a/pam_faillock/Makefile.am b/pam_faillock/Makefile.am index 1ecd359..4e3e5d8 100644 --- a/pam_faillock/Makefile.am +++ b/pam_faillock/Makefile.am @@ -9,8 +9,8 @@ MAINTAINERCLEANFILES = $(MANS) README EXTRA_DIST = README $(MANS) $(XMLS) tst-pam_faillock -man_MANS = pam_faillock.8 faillock.8 -XMLS = README.xml pam_faillock.8.xml faillock.8.xml +man_MANS = pam_faillock.8 faillock.8 faillock.conf.8 +XMLS = README.xml pam_faillock.8.xml faillock.8.xml faillock.conf.8.xml TESTS = tst-pam_faillock diff --git a/pam_faillock/faillock.conf.8.xml b/pam_faillock/faillock.conf.8.xml new file mode 100644 index 0000000..7d7e7c2 --- /dev/null +++ b/pam_faillock/faillock.conf.8.xml @@ -0,0 +1,228 @@ +E + + + + + + + faillock.conf + 8 + Linux-PAM Manual + + + + faillock.conf + pam_faillock configuration file + + + + + DESCRIPTION + + pwquality.confwquality.conf provides a way to + configure the default failed authentication requirements for failed system access. + This file is read by the pam_faillock module and is the + preferred method over configuring pam_faillock directly. + + + The file has a very simple name = value format with possible comments + starting with # character. The whitespace at the beginning of line, end + of line, and around the = sign is ignored. + + + + + + OPTIONS + + + + + + + + The directory where the user files with the failure records are kept. The + default is /var/run/faillock. + + + + + + + + + + Will log the user name into the system log if the user is not found. + + + + + + + + + + Don't print informative messages. This option is implicit + in the authfail and authsucc + functions. + + + + + + + + + + Don't log informative messages via syslog3. + + + + + + + + + + Deny access if the number of consecutive authentication failures + for this user during the recent interval exceeds + n. The default is 3. + + + + + + + + + + The length of the interval during which the consecutive + authentication failures must happen for the user account + lock out is n seconds. + The default is 900 (15 minutes). + + + + + + + + + + The access will be reenabled after + n seconds after the lock out. + The value 0 has the same meaning as value + never - the access + will not be reenabled without resetting the faillock + entries by the faillock8 command. + The default is 600 (10 minutes). + + + Note that the default directory that pam_faillock + uses is usually cleared on system boot so the access will be also reenabled + after system reboot. If that is undesirable a different tally directory + must be set with the option. + + + Also note that it is usually undesirable to permanently lock + out the users as they can become easily a target of denial of service + attack unless the usernames are random and kept secret to potential + attackers. + + + + + + + + + + Root account can become locked as well as regular accounts. + + + + + + + + + + This option implies option. + Allow access after n seconds + to root account after the account is locked. In case the + option is not specified the value is the same as of the + option. + + + + + + + + + + If a group name is specified with this option, members + of the group will be handled by this module the same as + the root account (the options + and will apply to them. + By default the option is not set. + + + + + + + + EXAMPLES + + /etc/security/faillock.conf file example: + + +deny=4 +even_deny_root +unlock_time=1200 + + + + + FILES + + + /etc/security/faillock.conf + + the config file for custom options + + + + + + + SEE ALSO + + + faillock8 + , + + pam_faillock8 + , + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_faillock was written by Tomas Mraz. The support for faillock.conf was written by Brian Ward. + + + + diff --git a/pam_faillock/faillock.h b/pam_faillock/faillock.h index 3b732a8..5f38657 100644 --- a/pam_faillock/faillock.h +++ b/pam_faillock/faillock.h @@ -65,6 +65,7 @@ struct tally_data { }; #define FAILLOCK_DEFAULT_TALLYDIR "/var/run/faillock" +#define FAILLOCK_DEFAULT_CONF "/etc/security/faillock.conf" int open_tally(const char *dir, const char *user, uid_t uid, int create); int read_tally(int fd, struct tally_data *tallies); diff --git a/pam_faillock/pam_faillock.8.xml b/pam_faillock/pam_faillock.8.xml index d3fe79d..c9fcefd 100644 --- a/pam_faillock/pam_faillock.8.xml +++ b/pam_faillock/pam_faillock.8.xml @@ -126,141 +126,11 @@ - - - - - - - The directory where the user files with the failure records are kept. The - default is /var/run/faillock. - - - - - - - - - - Will log the user name into the system log if the user is not found. - - - - - - - - - - Don't print informative messages. This option is implicite - in the authfail and authsucc - functions. - - - - - - - - - - Don't log informative messages via syslog3. - - - - - - - - - - Deny access if the number of consecutive authentication failures - for this user during the recent interval exceeds - n. The default is 3. - - - - - - - - - - The length of the interval during which the consecutive - authentication failures must happen for the user account - lock out is n seconds. - The default is 900 (15 minutes). - - - - - - - - - - The access will be reenabled after - n seconds after the lock out. - The value 0 has the same meaning as value - never - the access - will not be reenabled without resetting the faillock - entries by the faillock8 command. - The default is 600 (10 minutes). - - - Note that the default directory that pam_faillock - uses is usually cleared on system boot so the access will be also reenabled - after system reboot. If that is undesirable a different tally directory - must be set with the option. - - - Also note that it is usually undesirable to permanently lock - out the users as they can become easily a target of denial of service - attack unless the usernames are random and kept secret to potential - attackers. - - - - - - - - - - Root account can become locked as well as regular accounts. - - - - - - - - - - This option implies option. - Allow access after n seconds - to root account after the account is locked. In case the - option is not specified the value is the same as of the - option. - - - - - - - - - - If a group name is specified with this option, members - of the group will be handled by this module the same as - the root account (the options - and will apply to them. - By default the option is not set. - - - + + Options configured on the module command line is not recommend, and + /etc/security/faillock.conf should be used instead. + @@ -306,6 +176,9 @@ NOTES + Options configured on the module command line is not recommend, and + /etc/security/faillock.conf should be used instead. + pam_faillock setup in the PAM stack is different from the pam_tally2 module setup. @@ -316,9 +189,9 @@ Note that using the module in without the - option or with requisite - control field leaks an information about existence or - non-existence of an user account in the system because + option specified in /etc/security/faillock.conf + or with requisite control field leaks an information about + existence or non-existence of an user account in the system because the failures are not recorded for the unknown users. The message about the user account being locked is never displayed for nonexisting user accounts allowing the adversary to infer that a particular account @@ -341,15 +214,26 @@ be added to tell the user that his login is blocked by the module and also to abort the authentication without even asking for password in such case. + + /etc/security/faillock.conf file example: + + +deny=4 +even_deny_root +unlock_time=1200 + + + /etc/pam.d/config file example: + auth required pam_securetty.so auth required pam_env.so auth required pam_nologin.so -# optionally call: auth requisite pam_faillock.so preauth deny=4 even_deny_root unlock_time=1200 +# optionally call: auth requisite pam_faillock.so preauth # to display the message about account being locked auth [success=1 default=bad] pam_unix.so -auth [default=die] pam_faillock.so authfail deny=4 even_deny_root unlock_time=1200 -auth sufficient pam_faillock.so authsucc deny=4 even_deny_root unlock_time=1200 +auth [default=die] pam_faillock.so authfail +auth sufficient pam_faillock.so authsucc auth required pam_deny.so account required pam_unix.so password required pam_unix.so shadow @@ -367,11 +251,11 @@ session required pam_selinux.so open auth required pam_securetty.so auth required pam_env.so auth required pam_nologin.so -auth required pam_faillock.so preauth silent deny=4 even_deny_root unlock_time=1200 +auth required pam_faillock.so preauth # optionally use requisite above if you do not want to prompt for the password -# on locked accounts, possibly with removing the silent option as well +# on locked accounts auth sufficient pam_unix.so -auth [default=die] pam_faillock.so authfail deny=4 even_deny_root unlock_time=1200 +auth [default=die] pam_faillock.so authfail auth required pam_deny.so account required pam_faillock.so # if you drop the above call to pam_faillock.so the lock will be done also @@ -394,6 +278,12 @@ session required pam_selinux.so open the files logging the authentication failures for users + + /etc/security/faillock.conf + + the config file for pam_faillock options + + @@ -404,6 +294,9 @@ session required pam_selinux.so open faillock8 , + faillock.conf8 + , + pam.conf5 , diff --git a/pam_faillock/pam_faillock.c b/pam_faillock/pam_faillock.c index e570fbe..e6ea219 100644 --- a/pam_faillock/pam_faillock.c +++ b/pam_faillock/pam_faillock.c @@ -44,6 +44,7 @@ #include #include #include +#include #ifdef HAVE_LIBAUDIT #include @@ -69,6 +70,9 @@ #define FAILLOCK_FLAG_UNLOCKED 0x10 #define MAX_TIME_INTERVAL 604800 /* 7 days */ +#define FAILLOCK_CONF_MAX_LINELEN 1023 +#define FAILLOCK_ERROR_CONF_OPEN -3 +#define FAILLOCK_ERROR_CONF_MALFORMED -4 struct options { unsigned int action; @@ -78,6 +82,7 @@ struct options { unsigned int unlock_time; unsigned int root_unlock_time; const char *dir; + const char *conf; const char *user; const char *admin_group; int failures; @@ -87,21 +92,35 @@ struct options { uint64_t now; }; +int read_config_file( + pam_handle_t *pamh, + struct options *opts, + const char *cfgfile +); + +void set_conf_opt( + pam_handle_t *pamh, + struct options *opts, + const char *name, + const char *value +); + static void args_parse(pam_handle_t *pamh, int argc, const char **argv, int flags, struct options *opts) { int i; + int rv; memset(opts, 0, sizeof(*opts)); opts->dir = FAILLOCK_DEFAULT_TALLYDIR; + opts->conf = FAILLOCK_DEFAULT_CONF; opts->deny = 3; opts->fail_interval = 900; opts->unlock_time = 600; opts->root_unlock_time = MAX_TIME_INTERVAL+1; for (i = 0; i < argc; ++i) { - if (strncmp(argv[i], "dir=", 4) == 0) { if (argv[i][4] != '/') { pam_syslog(pamh, LOG_ERR, @@ -184,12 +203,173 @@ args_parse(pam_handle_t *pamh, int argc, const char **argv, } } + if ((rv=read_config_file(pamh, opts, opts->conf)) != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, + "Error opening conf file. Using defaults."); + } + if (opts->root_unlock_time == MAX_TIME_INTERVAL+1) opts->root_unlock_time = opts->unlock_time; if (flags & PAM_SILENT) opts->flags |= FAILLOCK_FLAG_SILENT; } +/* parse a single configuration file */ +int +read_config_file(pam_handle_t *pamh, struct options *opts, const char *cfgfile) +{ + FILE *f; + char linebuf[FAILLOCK_CONF_MAX_LINELEN+1]; + + f = fopen(cfgfile, "r"); + if (f == NULL) { + /* ignore non-existent default config file */ + if (errno == ENOENT && strcmp(cfgfile, FAILLOCK_DEFAULT_CONF) == 0) + return 0; + return FAILLOCK_ERROR_CONF_OPEN; + } + + while (fgets(linebuf, sizeof(linebuf), f) != NULL) { + size_t len; + char *ptr; + char *name; + int eq; + + len = strlen(linebuf); + /* len cannot be 0 unless there is a bug in fgets */ + if (len && linebuf[len - 1] != '\n' && !feof(f)) { + (void) fclose(f); + return FAILLOCK_ERROR_CONF_MALFORMED; + } + + if ((ptr=strchr(linebuf, '#')) != NULL) { + *ptr = '\0'; + } else { + ptr = linebuf + len; + } + + /* drop terminating whitespace including the \n */ + while (ptr > linebuf) { + if (!isspace(*(ptr-1))) { + *ptr = '\0'; + break; + } + --ptr; + } + + /* skip initial whitespace */ + for (ptr = linebuf; isspace(*ptr); ptr++); + if (*ptr == '\0') + continue; + + /* grab the key name */ + eq = 0; + name = ptr; + while (*ptr != '\0') { + if (isspace(*ptr) || *ptr == '=') { + eq = *ptr == '='; + *ptr = '\0'; + ++ptr; + break; + } + ++ptr; + } + + /* grab the key value */ + while (*ptr != '\0') { + if (*ptr != '=' || eq) { + if (!isspace(*ptr)) { + break; + } + } else { + eq = 1; + } + ++ptr; + } + + /* set the key:value pair on opts */ + set_conf_opt(pamh, opts, name, ptr); + } + + (void)fclose(f); + return PAM_SUCCESS; +} + +void set_conf_opt(pam_handle_t *pamh, struct options *opts, const char *name, const char *value) +{ + if (strncmp(name, "dir", 3) == 0) { + if (value[0] != '/') { + pam_syslog(pamh, LOG_ERR, + "Tally directory is not absolute path (%s); keeping default", value); + } else { + opts->dir = value; + } + } + else if (strncmp(name, "deny", 4) == 0) { + if (sscanf(value, "%hu", &opts->deny) != 1) { + pam_syslog(pamh, LOG_ERR, + "Bad number supplied for deny argument"); + } + } + else if (strncmp(name, "fail_interval", 13) == 0) { + unsigned int temp; + if (sscanf(value, "%u", &temp) != 1 || + temp > MAX_TIME_INTERVAL) { + pam_syslog(pamh, LOG_ERR, + "Bad number supplied for fail_interval argument"); + } else { + opts->fail_interval = temp; + } + } + else if (strncmp(name, "unlock_time", 11) == 0) { + unsigned int temp; + + if (strcmp(value, "never") == 0) { + opts->unlock_time = 0; + } + else if (sscanf(value, "%u", &temp) != 1 || + temp > MAX_TIME_INTERVAL) { + pam_syslog(pamh, LOG_ERR, + "Bad number supplied for unlock_time argument"); + } + else { + opts->unlock_time = temp; + } + } + else if (strncmp(name, "root_unlock_time", 16) == 0) { + unsigned int temp; + + if (strcmp(value, "never") == 0) { + opts->root_unlock_time = 0; + } + else if (sscanf(value, "%u", &temp) != 1 || + temp > MAX_TIME_INTERVAL) { + pam_syslog(pamh, LOG_ERR, + "Bad number supplied for root_unlock_time argument"); + } else { + opts->root_unlock_time = temp; + } + } + else if (strncmp(name, "admin_group", 11) == 0) { + opts->admin_group = value; + } + else if (strcmp(name, "even_deny_root") == 0) { + opts->flags |= FAILLOCK_FLAG_DENY_ROOT; + } + else if (strcmp(name, "audit") == 0) { + opts->flags |= FAILLOCK_FLAG_AUDIT; + } + else if (strcmp(name, "silent") == 0) { + opts->flags |= FAILLOCK_FLAG_SILENT; + } + else if (strcmp(name, "no_log_info") == 0) { + opts->flags |= FAILLOCK_FLAG_NO_LOG_INFO; + } + else { + pam_syslog(pamh, LOG_ERR, "Unknown option: %s", name); + } +} + static int get_pam_user(pam_handle_t *pamh, struct options *opts) { const char *user; From da5ba42ce36e3504ca9a774e7def8ebffd040ce2 Mon Sep 17 00:00:00 2001 From: Gabe Date: Sep 04 2019 21:59:23 +0000 Subject: [PATCH 2/3] Add faillock.conf --- diff --git a/pam_faillock/Makefile.am b/pam_faillock/Makefile.am index 4e3e5d8..c03ffdf 100644 --- a/pam_faillock/Makefile.am +++ b/pam_faillock/Makefile.am @@ -31,6 +31,8 @@ endif faillock_LDFLAGS = -Wl,-z,now @PIE_LDFLAGS@ faillock_LDADD = -L$(top_builddir)/libpam -lpam $(LIBAUDIT) +secureconf_DATA = faillock.conf + securelib_LTLIBRARIES = pam_faillock.la sbin_PROGRAMS = faillock diff --git a/pam_faillock/faillock.conf b/pam_faillock/faillock.conf new file mode 100644 index 0000000..b96b53c --- /dev/null +++ b/pam_faillock/faillock.conf @@ -0,0 +1,52 @@ +# Configuration of systemwide failed authentication attempts +# +# The directory where the user files with the failure records are kept. +# The default is /var/run/faillock. +# dir = /var/run/faillock +# +# Will log the user name into the system log if the user is not found. +# Enabled if option is present. +# audit +# +# Don't print informative messages. +# Enabled if option is present. +# silent +# +# Don't log informative messages via syslog. +# Enabled if option is present. +# no_log_info +# +# Deny access if the number of consecutive authentication failures +# for this user during the recent interval exceeds n tries. +# The default is 3. +# deny = 3 +# +# The length of the interval during which the consecutive +# authentication failures must happen for the user account +# lock out is n seconds. +# The default is 900 (15 minutes). +# fail_interval = 900 +# +# The access will be reenabled after n seconds after the lock out. +# The value 0 has the same meaning as value `never` - the access +# will not be reenabled without resetting the faillock +# entries by the `faillock` command. +# The default is 600 (10 minutes). +# unlock_time = 600 +# +# Root account can become locked as well as regular accounts. +# Enabled if option is present. +# even_deny_root +# +# This option implies the `even_deny_root` option. +# Allow access after n seconds to root account after the +# account is locked. In case the option is not specified +# the value is the same as of the `unlock_time` option. +# root_unlock_time = 900 +# +# If a group name is specified with this option, members +# of the group will be handled by this module the same as +# the root account (the options `even_deny_root>` and +# `root_unlock_time` will apply to them. +# By default, the option is not set. +# admin_group = From 7bc98759a7da103af63ef977b22b80b6e15dfef2 Mon Sep 17 00:00:00 2001 From: Gabe Date: Sep 06 2019 18:06:13 +0000 Subject: [PATCH 3/3] Fix typos and update descriptions --- diff --git a/pam_faillock/faillock.conf b/pam_faillock/faillock.conf index b96b53c..d1e5e81 100644 --- a/pam_faillock/faillock.conf +++ b/pam_faillock/faillock.conf @@ -1,4 +1,5 @@ -# Configuration of systemwide failed authentication attempts +# Configuration for locking the user after multiple failed +# authentication attempts. # # The directory where the user files with the failure records are kept. # The default is /var/run/faillock. diff --git a/pam_faillock/faillock.conf.8.xml b/pam_faillock/faillock.conf.8.xml index 7d7e7c2..7d69228 100644 --- a/pam_faillock/faillock.conf.8.xml +++ b/pam_faillock/faillock.conf.8.xml @@ -1,4 +1,3 @@ -E @@ -20,8 +19,8 @@ E DESCRIPTION - pwquality.confwquality.conf provides a way to - configure the default failed authentication requirements for failed system access. + faillock.conf provides a way to configure the + default settings for locking the user after multiple failed authentication attempts. This file is read by the pam_faillock module and is the preferred method over configuring pam_faillock directly.