diff --git a/SOURCES/pam-1.3.1-authtok-verify-fix.patch b/SOURCES/pam-1.3.1-authtok-verify-fix.patch new file mode 100644 index 0000000..6c40cdc --- /dev/null +++ b/SOURCES/pam-1.3.1-authtok-verify-fix.patch @@ -0,0 +1,88 @@ +From 27d04a849fd9f9cfd4b35eb80d687817830183df Mon Sep 17 00:00:00 2001 +From: Tomas Mraz +Date: Wed, 7 Aug 2019 12:22:55 +0200 +Subject: [PATCH] pam_get_authtok_verify: Avoid duplicate password verification + +If password was already verified by previous modules in the stack +it does not need to be verified by pam_get_authtok_verify either. + +* libpam/pam_get_authtok.c (pam_get_authtok_internal): Set the authtok_verified + appropriately. + (pam_get_authtok_verify): Do not prompt if authtok_verified is set and + set it when the password is verified. +* libpam/pam_private.h: Add authtok_verified to the pam handle struct. +* libpam/pam_start.c (pam_start): Initialize authtok_verified. +--- + libpam/pam_get_authtok.c | 10 ++++++++++ + libpam/pam_private.h | 1 + + libpam/pam_start.c | 1 + + 3 files changed, 12 insertions(+) + +diff --git a/libpam/pam_get_authtok.c b/libpam/pam_get_authtok.c +index 800c6e5..99eb25f 100644 +--- a/libpam/pam_get_authtok.c ++++ b/libpam/pam_get_authtok.c +@@ -140,6 +140,8 @@ pam_get_authtok_internal (pam_handle_t *pamh, int item, + } + else if (chpass) + { ++ pamh->authtok_verified = 0; ++ + retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[0], + PROMPT1, authtok_type, + strlen (authtok_type) > 0?" ":""); +@@ -184,6 +186,9 @@ pam_get_authtok_internal (pam_handle_t *pamh, int item, + if (retval != PAM_SUCCESS) + return retval; + ++ if (chpass > 1) ++ pamh->authtok_verified = 1; ++ + return pam_get_item(pamh, item, (const void **)authtok); + } + +@@ -214,6 +219,9 @@ pam_get_authtok_verify (pam_handle_t *pamh, const char **authtok, + if (authtok == NULL || pamh->choice != PAM_CHAUTHTOK) + return PAM_SYSTEM_ERR; + ++ if (pamh->authtok_verified) ++ return pam_get_item (pamh, PAM_AUTHTOK, (const void **)authtok); ++ + if (prompt != NULL) + { + retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp, +@@ -252,5 +260,7 @@ pam_get_authtok_verify (pam_handle_t *pamh, const char **authtok, + if (retval != PAM_SUCCESS) + return retval; + ++ pamh->authtok_verified = 1; ++ + return pam_get_item(pamh, PAM_AUTHTOK, (const void **)authtok); + } +diff --git a/libpam/pam_private.h b/libpam/pam_private.h +index 7ff9f75..58a26f5 100644 +--- a/libpam/pam_private.h ++++ b/libpam/pam_private.h +@@ -172,6 +172,7 @@ struct pam_handle { + #ifdef HAVE_LIBAUDIT + int audit_state; /* keep track of reported audit messages */ + #endif ++ int authtok_verified; + }; + + /* Values for select arg to _pam_dispatch() */ +diff --git a/libpam/pam_start.c b/libpam/pam_start.c +index 328416d..e27c64b 100644 +--- a/libpam/pam_start.c ++++ b/libpam/pam_start.c +@@ -94,6 +94,7 @@ int pam_start ( + #endif + (*pamh)->xdisplay = NULL; + (*pamh)->authtok_type = NULL; ++ (*pamh)->authtok_verified = 0; + memset (&((*pamh)->xauth), 0, sizeof ((*pamh)->xauth)); + + if (((*pamh)->pam_conversation = (struct pam_conv *) +-- +2.20.1 + diff --git a/SOURCES/pam-1.3.1-faillock-update.patch b/SOURCES/pam-1.3.1-faillock-update.patch new file mode 100644 index 0000000..c001f48 --- /dev/null +++ b/SOURCES/pam-1.3.1-faillock-update.patch @@ -0,0 +1,1186 @@ +diff -up Linux-PAM-1.3.1/modules/pam_faillock/faillock.c.faillock-update Linux-PAM-1.3.1/modules/pam_faillock/faillock.c +--- Linux-PAM-1.3.1/modules/pam_faillock/faillock.c.faillock-update 2019-10-16 16:49:27.026893768 +0200 ++++ Linux-PAM-1.3.1/modules/pam_faillock/faillock.c 2019-12-16 17:55:27.042001068 +0100 +@@ -1,5 +1,6 @@ + /* + * Copyright (c) 2010 Tomas Mraz ++ * Copyright (c) 2010, 2016, 2017 Red Hat, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions +@@ -47,6 +48,8 @@ + + #include "faillock.h" + ++#define ignore_return(x) if (1==((int)x)) {;} ++ + int + open_tally (const char *dir, const char *user, uid_t uid, int create) + { +@@ -54,7 +57,7 @@ open_tally (const char *dir, const char + int flags = O_RDWR; + int fd; + +- if (strstr(user, "../") != NULL) ++ if (dir == NULL || strstr(user, "../") != NULL) + /* just a defensive programming as the user must be a + * valid user on the system anyway + */ +@@ -83,7 +86,7 @@ open_tally (const char *dir, const char + while (flock(fd, LOCK_EX) == -1 && errno == EINTR); + if (fstat(fd, &st) == 0) { + if (st.st_uid != uid) { +- fchown(fd, uid, -1); ++ ignore_return(fchown(fd, uid, -1)); + } + } + } +@@ -121,7 +124,7 @@ read_tally(int fd, struct tally_data *ta + if (count >= MAX_RECORDS) + break; + } +- while (chunk == CHUNK_SIZE); ++ while (chunk == CHUNK_SIZE); + + tallies->records = data; + tallies->count = count; +diff -up Linux-PAM-1.3.1/modules/pam_faillock/faillock.conf.5.xml.faillock-update Linux-PAM-1.3.1/modules/pam_faillock/faillock.conf.5.xml +--- Linux-PAM-1.3.1/modules/pam_faillock/faillock.conf.5.xml.faillock-update 2019-10-16 17:34:58.073276020 +0200 ++++ Linux-PAM-1.3.1/modules/pam_faillock/faillock.conf.5.xml 2019-10-16 16:26:05.000000000 +0200 +@@ -0,0 +1,243 @@ ++ ++ ++ ++ ++ ++ ++ faillock.conf ++ 5 ++ Linux-PAM Manual ++ ++ ++ ++ faillock.conf ++ pam_faillock configuration file ++ ++ ++ ++ ++ DESCRIPTION ++ ++ 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. ++ ++ ++ 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 to the user. Please note that when ++ this option is not used there will be difference in the authentication ++ behavior for users which exist on the system and non-existing users. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Don't log informative messages via syslog3. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ 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. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ 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 ++unlock_time=1200 ++silent ++ ++ ++ ++ ++ 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 -up Linux-PAM-1.3.1/modules/pam_faillock/faillock.conf.faillock-update Linux-PAM-1.3.1/modules/pam_faillock/faillock.conf +--- Linux-PAM-1.3.1/modules/pam_faillock/faillock.conf.faillock-update 2019-10-16 17:34:50.607405060 +0200 ++++ Linux-PAM-1.3.1/modules/pam_faillock/faillock.conf 2019-10-16 16:26:05.000000000 +0200 +@@ -0,0 +1,62 @@ ++# 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. ++# 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 ++# ++# 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. ++# 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 = +diff -up Linux-PAM-1.3.1/modules/pam_faillock/faillock.h.faillock-update Linux-PAM-1.3.1/modules/pam_faillock/faillock.h +--- Linux-PAM-1.3.1/modules/pam_faillock/faillock.h.faillock-update 2019-10-16 16:49:27.026893768 +0200 ++++ Linux-PAM-1.3.1/modules/pam_faillock/faillock.h 2019-10-16 16:51:40.431628566 +0200 +@@ -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 -up Linux-PAM-1.3.1/modules/pam_faillock/Makefile.am.faillock-update Linux-PAM-1.3.1/modules/pam_faillock/Makefile.am +--- Linux-PAM-1.3.1/modules/pam_faillock/Makefile.am.faillock-update 2019-10-16 16:49:27.026893768 +0200 ++++ Linux-PAM-1.3.1/modules/pam_faillock/Makefile.am 2019-10-16 16:50:15.065078080 +0200 +@@ -1,6 +1,6 @@ + # + # Copyright (c) 2005, 2006, 2007, 2009 Thorsten Kukuk +-# Copyright (c) 2008 Red Hat, Inc. ++# Copyright (c) 2008, 2018 Red Hat, Inc. + # Copyright (c) 2010 Tomas Mraz + # + +@@ -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.5 ++XMLS = README.xml pam_faillock.8.xml faillock.8.xml faillock.conf.5.xml + + TESTS = tst-pam_faillock + +@@ -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 -up Linux-PAM-1.3.1/modules/pam_faillock/pam_faillock.8.xml.faillock-update Linux-PAM-1.3.1/modules/pam_faillock/pam_faillock.8.xml +--- Linux-PAM-1.3.1/modules/pam_faillock/pam_faillock.8.xml.faillock-update 2019-10-16 16:49:27.030893701 +0200 ++++ Linux-PAM-1.3.1/modules/pam_faillock/pam_faillock.8.xml 2019-10-16 16:53:39.544606052 +0200 +@@ -126,141 +126,13 @@ + + + +- +- +- +- +- +- +- 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. +- +- +- + ++ ++ The options for configuring the module behavior are described in the ++ faillock.conf5 ++ manual page. The options specified on the module command ++ line override the values from the configuration file. ++ + + + +@@ -306,19 +178,23 @@ + + NOTES + +- pam_faillock setup in the PAM stack is different ++ Configuring options on the module command line is not recommend. The ++ /etc/security/faillock.conf should be used instead. ++ ++ ++ The setup of pam_faillock in the PAM stack is different + from the pam_tally2 module setup. + + +- The individual files with the failure records are created as owned by ++ Individual files with the failure records are created as owned by + the user. This allows pam_faillock.so module + to work correctly when it is called from a screensaver. + + + 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 +217,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 ++unlock_time=1200 ++silent ++ ++ ++ /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 +@@ -361,17 +248,18 @@ session required pam_selinux.so o + + In the second example the module is called both in the auth + and account phases and the module gives the authenticating +- user message when the account is locked ++ user message when the account is locked if option is not ++ specified in the faillock.conf. + + + 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 +282,12 @@ session required pam_selinux.so o + the files logging the authentication failures for users + + ++ ++ /etc/security/faillock.conf ++ ++ the config file for pam_faillock options ++ ++ + + + +@@ -404,6 +298,9 @@ session required pam_selinux.so o + faillock8 + , + ++ faillock.conf5 ++ , ++ + pam.conf5 + , + +diff -up Linux-PAM-1.3.1/modules/pam_faillock/pam_faillock.c.faillock-update Linux-PAM-1.3.1/modules/pam_faillock/pam_faillock.c +--- Linux-PAM-1.3.1/modules/pam_faillock/pam_faillock.c.faillock-update 2019-10-16 16:49:27.030893701 +0200 ++++ Linux-PAM-1.3.1/modules/pam_faillock/pam_faillock.c 2019-12-16 17:55:11.403270018 +0100 +@@ -1,5 +1,6 @@ + /* +- * Copyright (c) 2010, 2017 Tomas Mraz ++ * Copyright (c) 2010, 2017, 2019 Tomas Mraz ++ * Copyright (c) 2010, 2017, 2019 Red Hat, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions +@@ -43,6 +44,7 @@ + #include + #include + #include ++#include + + #ifdef HAVE_LIBAUDIT + #include +@@ -66,8 +68,14 @@ + #define FAILLOCK_FLAG_SILENT 0x4 + #define FAILLOCK_FLAG_NO_LOG_INFO 0x8 + #define FAILLOCK_FLAG_UNLOCKED 0x10 ++#define FAILLOCK_FLAG_LOCAL_ONLY 0x20 + + #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 ++ ++#define PATH_PASSWD "/etc/passwd" + + struct options { + unsigned int action; +@@ -76,117 +84,295 @@ struct options { + unsigned int fail_interval; + unsigned int unlock_time; + unsigned int root_unlock_time; +- const char *dir; ++ char *dir; ++ const char *conf; + const char *user; +- const char *admin_group; ++ char *admin_group; + int failures; + uint64_t latest_time; + uid_t uid; + int is_admin; + uint64_t now; ++ int fatal_error; + }; + ++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->dir = strdup(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 ((rv=read_config_file(pamh, opts, opts->conf)) != PAM_SUCCESS) { ++ pam_syslog(pamh, LOG_DEBUG, ++ "Configuration file missing"); ++ } + +- if (strncmp(argv[i], "dir=", 4) == 0) { +- if (argv[i][4] != '/') { +- pam_syslog(pamh, LOG_ERR, +- "Tally directory is not absolute path (%s); keeping default", argv[i]); +- } else { +- opts->dir = argv[i]+4; +- } +- } +- else if (strncmp(argv[i], "deny=", 5) == 0) { +- if (sscanf(argv[i]+5, "%hu", &opts->deny) != 1) { +- pam_syslog(pamh, LOG_ERR, +- "Bad number supplied for deny argument"); +- } +- } +- else if (strncmp(argv[i], "fail_interval=", 14) == 0) { +- unsigned int temp; +- if (sscanf(argv[i]+14, "%u", &temp) != 1 || +- temp > MAX_TIME_INTERVAL) { +- pam_syslog(pamh, LOG_ERR, +- "Bad number supplied for fail_interval argument"); +- } else { +- opts->fail_interval = temp; +- } ++ for (i = 0; i < argc; ++i) { ++ if (strcmp(argv[i], "preauth") == 0) { ++ opts->action = FAILLOCK_ACTION_PREAUTH; ++ } ++ else if (strcmp(argv[i], "authfail") == 0) { ++ opts->action = FAILLOCK_ACTION_AUTHFAIL; ++ } ++ else if (strcmp(argv[i], "authsucc") == 0) { ++ opts->action = FAILLOCK_ACTION_AUTHSUCC; + } +- else if (strncmp(argv[i], "unlock_time=", 12) == 0) { +- unsigned int temp; ++ else { ++ char buf[FAILLOCK_CONF_MAX_LINELEN + 1]; ++ char *val; + +- if (strcmp(argv[i]+12, "never") == 0) { +- opts->unlock_time = 0; +- } +- else if (sscanf(argv[i]+12, "%u", &temp) != 1 || +- temp > MAX_TIME_INTERVAL) { +- pam_syslog(pamh, LOG_ERR, +- "Bad number supplied for unlock_time argument"); ++ strncpy(buf, argv[i], sizeof(buf) - 1); ++ buf[sizeof(buf) - 1] = '\0'; ++ ++ val = strchr(buf, '='); ++ if (val != NULL) { ++ *val = '\0'; ++ ++val; + } + else { +- opts->unlock_time = temp; ++ val = buf + sizeof(buf) - 1; + } ++ set_conf_opt(pamh, opts, buf, val); + } +- else if (strncmp(argv[i], "root_unlock_time=", 17) == 0) { +- unsigned int temp; ++ } + +- if (strcmp(argv[i]+17, "never") == 0) { +- opts->root_unlock_time = 0; +- } +- else if (sscanf(argv[i]+17, "%u", &temp) != 1 || +- temp > MAX_TIME_INTERVAL) { +- pam_syslog(pamh, LOG_ERR, +- "Bad number supplied for root_unlock_time argument"); ++ 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; ++ ++ if (opts->dir == NULL) { ++ pam_syslog(pamh, LOG_CRIT, "Error allocating memory: %m"); ++ opts->fatal_error = 1; ++ } ++} ++ ++/* 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 { +- opts->root_unlock_time = temp; ++ eq = 1; + } ++ ++ptr; + } +- else if (strncmp(argv[i], "admin_group=", 12) == 0) { +- opts->admin_group = argv[i] + 12; ++ ++ /* 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 (strcmp(name, "dir") == 0) { ++ if (value[0] != '/') { ++ pam_syslog(pamh, LOG_ERR, ++ "Tally directory is not absolute path (%s); keeping default", value); ++ } else { ++ free(opts->dir); ++ opts->dir = strdup(value); + } +- else if (strcmp(argv[i], "preauth") == 0) { +- opts->action = FAILLOCK_ACTION_PREAUTH; ++ } ++ else if (strcmp(name, "deny") == 0) { ++ if (sscanf(value, "%hu", &opts->deny) != 1) { ++ pam_syslog(pamh, LOG_ERR, ++ "Bad number supplied for deny argument"); ++ } ++ } ++ else if (strcmp(name, "fail_interval") == 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 (strcmp(argv[i], "authfail") == 0) { +- opts->action = FAILLOCK_ACTION_AUTHFAIL; ++ } ++ else if (strcmp(name, "unlock_time") == 0) { ++ unsigned int temp; ++ ++ if (strcmp(value, "never") == 0) { ++ opts->unlock_time = 0; + } +- else if (strcmp(argv[i], "authsucc") == 0) { +- opts->action = FAILLOCK_ACTION_AUTHSUCC; ++ else if (sscanf(value, "%u", &temp) != 1 || ++ temp > MAX_TIME_INTERVAL) { ++ pam_syslog(pamh, LOG_ERR, ++ "Bad number supplied for unlock_time argument"); + } +- else if (strcmp(argv[i], "even_deny_root") == 0) { +- opts->flags |= FAILLOCK_FLAG_DENY_ROOT; ++ else { ++ opts->unlock_time = temp; + } +- else if (strcmp(argv[i], "audit") == 0) { +- opts->flags |= FAILLOCK_FLAG_AUDIT; ++ } ++ else if (strcmp(name, "root_unlock_time") == 0) { ++ unsigned int temp; ++ ++ if (strcmp(value, "never") == 0) { ++ opts->root_unlock_time = 0; + } +- else if (strcmp(argv[i], "silent") == 0) { +- opts->flags |= FAILLOCK_FLAG_SILENT; ++ 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 (strcmp(argv[i], "no_log_info") == 0) { +- opts->flags |= FAILLOCK_FLAG_NO_LOG_INFO; ++ } ++ else if (strcmp(name, "admin_group") == 0) { ++ free(opts->admin_group); ++ opts->admin_group = strdup(value); ++ if (opts->admin_group == NULL) { ++ opts->fatal_error = 1; ++ pam_syslog(pamh, LOG_CRIT, "Error allocating memory: %m"); + } +- else { +- pam_syslog(pamh, LOG_ERR, "Unknown option: %s", argv[i]); ++ } ++ 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 if (strcmp(name, "local_users_only") == 0) { ++ opts->flags |= FAILLOCK_FLAG_LOCAL_ONLY; ++ } ++ 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; + } + } + +- 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; ++ 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) +@@ -393,7 +579,7 @@ write_tally(pam_handle_t *pamh, struct o + + strncpy(tallies->records[oldest].source, source, sizeof(tallies->records[oldest].source)); + /* source does not have to be null terminated */ +- ++ + tallies->records[oldest].time = opts->now; + + ++failures; +@@ -468,6 +654,13 @@ tally_cleanup(struct tally_data *tallies + free(tallies->records); + } + ++static void ++opts_cleanup(struct options *opts) ++{ ++ free(opts->dir); ++ free(opts->admin_group); ++} ++ + /*---------------------------------------------------------------------*/ + + PAM_EXTERN int +@@ -481,39 +674,49 @@ pam_sm_authenticate(pam_handle_t *pamh, + memset(&tallies, 0, sizeof(tallies)); + + args_parse(pamh, argc, argv, flags, &opts); ++ if (opts.fatal_error) { ++ rv = PAM_BUF_ERR; ++ goto err; ++ } + + pam_fail_delay(pamh, 2000000); /* 2 sec delay for on failure */ + + if ((rv=get_pam_user(pamh, &opts)) != PAM_SUCCESS) { +- return rv; ++ goto err; + } + +- 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_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; ++ if (!(opts.flags & FAILLOCK_FLAG_LOCAL_ONLY) || ++ check_local_user (pamh, opts.user) != 0) { ++ 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_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); + ++err: ++ opts_cleanup(&opts); ++ + return rv; + } + +@@ -540,18 +743,29 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int + + args_parse(pamh, argc, argv, flags, &opts); + ++ if (opts.fatal_error) { ++ rv = PAM_BUF_ERR; ++ goto err; ++ } ++ + opts.action = FAILLOCK_ACTION_AUTHSUCC; + + if ((rv=get_pam_user(pamh, &opts)) != PAM_SUCCESS) { +- return rv; ++ goto err; + } + +- check_tally(pamh, &opts, &tallies, &fd); /* for auditing */ +- reset_tally(pamh, &opts, &fd); ++ if (!(opts.flags & FAILLOCK_FLAG_LOCAL_ONLY) || ++ check_local_user (pamh, opts.user) != 0) { ++ check_tally(pamh, &opts, &tallies, &fd); /* for auditing */ ++ reset_tally(pamh, &opts, &fd); ++ } + + tally_cleanup(&tallies, fd); + +- return PAM_SUCCESS; ++err: ++ opts_cleanup(&opts); ++ ++ return rv; + } + + /*-----------------------------------------------------------------------*/ diff --git a/SOURCES/pam-1.3.1-fds-closing.patch b/SOURCES/pam-1.3.1-fds-closing.patch new file mode 100644 index 0000000..7f5f772 --- /dev/null +++ b/SOURCES/pam-1.3.1-fds-closing.patch @@ -0,0 +1,70 @@ +diff -up Linux-PAM-1.3.1/libpam/pam_modutil_sanitize.c.fds-closing Linux-PAM-1.3.1/libpam/pam_modutil_sanitize.c +--- Linux-PAM-1.3.1/libpam/pam_modutil_sanitize.c.fds-closing 2017-02-10 11:10:15.000000000 +0100 ++++ Linux-PAM-1.3.1/libpam/pam_modutil_sanitize.c 2019-10-16 16:07:31.259021159 +0200 +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + + /* + * Creates a pipe, closes its write end, redirects fd to its read end. +@@ -116,27 +117,45 @@ redirect_out(pam_handle_t *pamh, enum pa + static void + close_fds(void) + { ++ DIR *dir = NULL; ++ struct dirent *dent; ++ int dfd = -1; ++ int fd; ++ struct rlimit rlim; ++ + /* + * An arbitrary upper limit for the maximum file descriptor number + * returned by RLIMIT_NOFILE. + */ +- const int MAX_FD_NO = 65535; ++ const unsigned int MAX_FD_NO = 65535; + + /* The lower limit is the same as for _POSIX_OPEN_MAX. */ +- const int MIN_FD_NO = 20; ++ const unsigned int MIN_FD_NO = 20; + +- int fd; +- struct rlimit rlim; +- +- if (getrlimit(RLIMIT_NOFILE, &rlim) || rlim.rlim_max > MAX_FD_NO) +- fd = MAX_FD_NO; +- else if (rlim.rlim_max < MIN_FD_NO) +- fd = MIN_FD_NO; +- else +- fd = rlim.rlim_max - 1; ++ /* If /proc is mounted, we can optimize which fd can be closed. */ ++ if ((dir = opendir("/proc/self/fd")) != NULL) { ++ if ((dfd = dirfd(dir)) >= 0) { ++ while ((dent = readdir(dir)) != NULL) { ++ fd = atoi(dent->d_name); ++ if (fd > STDERR_FILENO && fd != dfd) ++ close(fd); ++ } ++ } ++ closedir(dir); ++ } ++ ++ /* If /proc isn't available, fallback to the previous behavior. */ ++ if (dfd < 0) { ++ if (getrlimit(RLIMIT_NOFILE, &rlim) || rlim.rlim_max > MAX_FD_NO) ++ fd = MAX_FD_NO; ++ else if (rlim.rlim_max < MIN_FD_NO) ++ fd = MIN_FD_NO; ++ else ++ fd = rlim.rlim_max - 1; + +- for (; fd > STDERR_FILENO; --fd) +- close(fd); ++ for (; fd > STDERR_FILENO; --fd) ++ close(fd); ++ } + } + + int diff --git a/SOURCES/pam-1.3.1-lastlog-no-showfailed.patch b/SOURCES/pam-1.3.1-lastlog-no-showfailed.patch new file mode 100644 index 0000000..6b68d9d --- /dev/null +++ b/SOURCES/pam-1.3.1-lastlog-no-showfailed.patch @@ -0,0 +1,82 @@ +From c426914fa166ffb0482b6f6ad659ddf17d5dfaa1 Mon Sep 17 00:00:00 2001 +From: Nir Soffer +Date: Wed, 9 Jan 2019 23:41:16 +0200 +Subject: [PATCH] pam_lastlog: Improve silent option documentation + +The silent option explicitly silents only the last login message and not +bad logins. Add a note to the manual to make this clear. + +* modules/pam_lastlog/pam_lastlog.8.xml: Clearify "silent showfailed" +--- + modules/pam_lastlog/pam_lastlog.8.xml | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/modules/pam_lastlog/pam_lastlog.8.xml b/modules/pam_lastlog/pam_lastlog.8.xml +index c8f247e..bc2e1be 100644 +--- a/modules/pam_lastlog/pam_lastlog.8.xml ++++ b/modules/pam_lastlog/pam_lastlog.8.xml +@@ -102,6 +102,7 @@ + + Don't inform the user about any previous login, + just update the /var/log/lastlog file. ++ This option does not affect display of bad login attempts. + + + +-- +2.20.1 + +From 7d036249a9772c546ede1f38ad68b3f1575216d6 Mon Sep 17 00:00:00 2001 +From: Nir Soffer +Date: Sun, 6 Jan 2019 00:36:27 +0200 +Subject: [PATCH] pam_lastlog: Respect PAM_SILENT flag + +pam_lastlog module will not log info about failed login if the session +was opened with PAM_SILENT flag. + +Example use case enabled by this change: + + sudo --non-interactive program + +If this command is run by another program expecting specific output from +the command run by sudo, the unexpected info about failed logins will +break this program. + +* modules/pam_lastlog/pam_lastlog.c: Respect silent option. + (_pam_session_parse): Unset LASTLOG_BTMP if PAM_SILENT is set. +--- + modules/pam_lastlog/pam_lastlog.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/modules/pam_lastlog/pam_lastlog.c b/modules/pam_lastlog/pam_lastlog.c +index 18bf7be..e980c04 100644 +--- a/modules/pam_lastlog/pam_lastlog.c ++++ b/modules/pam_lastlog/pam_lastlog.c +@@ -135,11 +135,6 @@ _pam_session_parse(pam_handle_t *pamh, int flags, int argc, const char **argv) + { + int ctrl=(LASTLOG_DATE|LASTLOG_HOST|LASTLOG_LINE|LASTLOG_WTMP|LASTLOG_UPDATE); + +- /* does the appliction require quiet? */ +- if (flags & PAM_SILENT) { +- ctrl |= LASTLOG_QUIET; +- } +- + /* step through arguments */ + for (; argc-- > 0; ++argv) { + +@@ -168,6 +163,12 @@ _pam_session_parse(pam_handle_t *pamh, int flags, int argc, const char **argv) + } + } + ++ /* does the appliction require quiet? */ ++ if (flags & PAM_SILENT) { ++ ctrl |= LASTLOG_QUIET; ++ ctrl &= ~LASTLOG_BTMP; ++ } ++ + D(("ctrl = %o", ctrl)); + return ctrl; + } +-- +2.20.1 + diff --git a/SOURCES/pam-1.3.1-lastlog-unlimited-fsize.patch b/SOURCES/pam-1.3.1-lastlog-unlimited-fsize.patch new file mode 100644 index 0000000..1067d6f --- /dev/null +++ b/SOURCES/pam-1.3.1-lastlog-unlimited-fsize.patch @@ -0,0 +1,227 @@ +From 3a3e70739834cd5cbd17469907ef718c81ae40c0 Mon Sep 17 00:00:00 2001 +From: Carlos Santos +Date: Wed, 11 Sep 2019 11:50:28 -0300 +Subject: [PATCH] pam_lastlog: document the 'unlimited' option + +Signed-off-by: Carlos Santos +--- + modules/pam_lastlog/pam_lastlog.8.xml | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +diff --git a/modules/pam_lastlog/pam_lastlog.8.xml b/modules/pam_lastlog/pam_lastlog.8.xml +index bc2e1be..f10e94a 100644 +--- a/modules/pam_lastlog/pam_lastlog.8.xml ++++ b/modules/pam_lastlog/pam_lastlog.8.xml +@@ -48,6 +48,9 @@ + + inactive=<days> + ++ ++ unlimited ++ + + + +@@ -196,6 +199,18 @@ + + + ++ ++ ++ ++ ++ ++ ++ If the fsize limit is set, this option can be ++ used to override it, preventing failures on systems with large UID ++ values that lead lastlog to become a huge sparse file. ++ ++ ++ + + + +@@ -300,6 +315,9 @@ + + SEE ALSO + ++ ++ limits.conf5 ++ , + + pam.conf5 + , +-- +2.20.1 + +From 9349333a9ae958205294cd25e97fd6b4805bd82b Mon Sep 17 00:00:00 2001 +From: Carlos Santos +Date: Tue, 10 Sep 2019 23:08:30 -0300 +Subject: [PATCH] pam_lastlog: prevent crash due to reduced 'fsize' limit + +It a reduced fsize limit is set in /etc/security/limits.conf and +pam_limits is in use pam_lastlog may cause a crash, e.g. + + ----- begin /etc/pam.d/su ---- + auth sufficient pam_rootok.so + auth required pam_wheel.so use_uid + auth required pam_env.so + auth required pam_unix.so nullok + account required pam_unix.so + password required pam_unix.so nullok + session required pam_limits.so + session required pam_env.so + session required pam_unix.so + session optional pam_lastlog.so + ----- end /etc/pam.d/su ----- + + ----- begin /etc/security/limits.d/fsize.conf ----- + * soft fsize 1710 + * hard fsize 1710 + ----- end /etc/security/limits.d/fsize.conf ----- + + # id user1 + uid=1000(user1) gid=1000(user1) groups=1000(user1) + # su - user1 + Last login: Wed Sep 11 01:52:44 UTC 2019 on console + $ exit + # id user2 + uid=60000(user2) gid=60000(user2) groups=60000(user2) + # su - user2 + File size limit exceeded + +This happens because pam_limits sets RLIMIT_FSIZE before pam_lastlog +attempts to write /var/log/lastlog, leading to a SIGXFSZ signal. + +In order to fix this, and an 'unlimited' option, which leads to saving +the 'fsize' limit and set it to unlimited before writing lastlog. After +that, restore the saved value. If 'fsize' is already unlimited nothing +is done. + +Failing to set the 'fsize' limit is not a fatal error. With luck the +configured limit will suffice, so we try to write lastlog anyway, even +under the risk of dying due to a SIGXFSZ. + +Failing to restore the 'fsize' limit is a fatal error, since we don't +want to keep it unlimited. + +Signed-off-by: Carlos Santos +--- + modules/pam_lastlog/pam_lastlog.c | 66 ++++++++++++++++++++++++++----- + 1 file changed, 57 insertions(+), 9 deletions(-) + +diff --git a/modules/pam_lastlog/pam_lastlog.c b/modules/pam_lastlog/pam_lastlog.c +index e980c04..a135c9f 100644 +--- a/modules/pam_lastlog/pam_lastlog.c ++++ b/modules/pam_lastlog/pam_lastlog.c +@@ -25,6 +25,8 @@ + #include + #include + #include ++#include ++#include + #include + #include + +@@ -82,15 +84,16 @@ struct lastlog { + + /* argument parsing */ + +-#define LASTLOG_DATE 01 /* display the date of the last login */ +-#define LASTLOG_HOST 02 /* display the last host used (if set) */ +-#define LASTLOG_LINE 04 /* display the last terminal used */ +-#define LASTLOG_NEVER 010 /* display a welcome message for first login */ +-#define LASTLOG_DEBUG 020 /* send info to syslog(3) */ +-#define LASTLOG_QUIET 040 /* keep quiet about things */ +-#define LASTLOG_WTMP 0100 /* log to wtmp as well as lastlog */ +-#define LASTLOG_BTMP 0200 /* display failed login info from btmp */ +-#define LASTLOG_UPDATE 0400 /* update the lastlog and wtmp files (default) */ ++#define LASTLOG_DATE 01 /* display the date of the last login */ ++#define LASTLOG_HOST 02 /* display the last host used (if set) */ ++#define LASTLOG_LINE 04 /* display the last terminal used */ ++#define LASTLOG_NEVER 010 /* display a welcome message for first login */ ++#define LASTLOG_DEBUG 020 /* send info to syslog(3) */ ++#define LASTLOG_QUIET 040 /* keep quiet about things */ ++#define LASTLOG_WTMP 0100 /* log to wtmp as well as lastlog */ ++#define LASTLOG_BTMP 0200 /* display failed login info from btmp */ ++#define LASTLOG_UPDATE 0400 /* update the lastlog and wtmp files (default) */ ++#define LASTLOG_UNLIMITED 01000 /* unlimited file size (ignore 'fsize' limit) */ + + static int + _pam_auth_parse(pam_handle_t *pamh, int flags, int argc, const char **argv, +@@ -158,6 +161,8 @@ _pam_session_parse(pam_handle_t *pamh, int flags, int argc, const char **argv) + ctrl &= ~(LASTLOG_WTMP|LASTLOG_UPDATE); + } else if (!strcmp(*argv,"showfailed")) { + ctrl |= LASTLOG_BTMP; ++ } else if (!strcmp(*argv,"unlimited")) { ++ ctrl |= LASTLOG_UNLIMITED; + } else { + pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv); + } +@@ -373,6 +378,12 @@ static int + last_login_write(pam_handle_t *pamh, int announce, int last_fd, + uid_t uid, const char *user) + { ++ static struct rlimit no_limit = { ++ RLIM_INFINITY, ++ RLIM_INFINITY ++ }; ++ struct rlimit old_limit; ++ int setrlimit_res; + struct flock last_lock; + struct lastlog last_login; + time_t ll_time; +@@ -427,6 +438,31 @@ last_login_write(pam_handle_t *pamh, int announce, int last_fd, + sleep(LASTLOG_IGNORE_LOCK_TIME); + } + ++ /* ++ * Failing to set the 'fsize' limit is not a fatal error. We try to write ++ * lastlog anyway, under the risk of dying due to a SIGXFSZ. ++ */ ++ D(("setting limit for 'fsize'")); ++ ++ if ((announce & LASTLOG_UNLIMITED) == 0) { /* don't set to unlimted */ ++ setrlimit_res = -1; ++ } else if (getrlimit(RLIMIT_FSIZE, &old_limit) == 0) { ++ if (old_limit.rlim_cur == RLIM_INFINITY) { /* already unlimited */ ++ setrlimit_res = -1; ++ } else { ++ setrlimit_res = setrlimit(RLIMIT_FSIZE, &no_limit); ++ if (setrlimit_res != 0) ++ pam_syslog(pamh, LOG_WARNING, "Could not set limit for 'fsize': %m"); ++ } ++ } else { ++ setrlimit_res = -1; ++ if (errno == EINVAL) { ++ pam_syslog(pamh, LOG_INFO, "Limit for 'fsize' not supported: %m"); ++ } else { ++ pam_syslog(pamh, LOG_WARNING, "Could not get limit for 'fsize': %m"); ++ } ++ } ++ + D(("writing to the lastlog file")); + if (pam_modutil_write (last_fd, (char *) &last_login, + sizeof (last_login)) != sizeof(last_login)) { +@@ -434,6 +470,18 @@ last_login_write(pam_handle_t *pamh, int announce, int last_fd, + retval = PAM_SERVICE_ERR; + } + ++ /* ++ * Failing to restore the 'fsize' limit is a fatal error. ++ */ ++ D(("restoring limit for 'fsize'")); ++ if (setrlimit_res == 0) { ++ setrlimit_res = setrlimit(RLIMIT_FSIZE, &old_limit); ++ if (setrlimit_res != 0) { ++ pam_syslog(pamh, LOG_ERR, "Could not restore limit for 'fsize': %m"); ++ retval = PAM_SERVICE_ERR; ++ } ++ } ++ + last_lock.l_type = F_UNLCK; + (void) fcntl(last_fd, F_SETLK, &last_lock); /* unlock */ + D(("unlocked")); +-- +2.20.1 + diff --git a/SOURCES/pam-1.3.1-motd-manpage.patch b/SOURCES/pam-1.3.1-motd-manpage.patch new file mode 100644 index 0000000..b8e2a1e --- /dev/null +++ b/SOURCES/pam-1.3.1-motd-manpage.patch @@ -0,0 +1,25 @@ +diff -up Linux-PAM-1.3.1/modules/pam_motd/pam_motd.8.xml.motd-manpage Linux-PAM-1.3.1/modules/pam_motd/pam_motd.8.xml +--- Linux-PAM-1.3.1/modules/pam_motd/pam_motd.8.xml.motd-manpage 2018-05-18 11:50:46.000000000 +0200 ++++ Linux-PAM-1.3.1/modules/pam_motd/pam_motd.8.xml 2019-12-19 10:29:36.243558251 +0100 +@@ -31,10 +31,19 @@ + + pam_motd is a PAM module that can be used to display + arbitrary motd (message of the day) files after a successful +- login. By default the /etc/motd file is ++ login. By default the /etc/motd file and ++ all files from /etc/motd.d are + shown. The message size is limited to 64KB. + +- ++ ++ To silence a message, ++ a symbolic link with target /dev/null ++ may be placed in /etc/motd.d with ++ the same filename as the message to be silenced. Example: ++ ++ ++ ln -sfn /dev/null /etc/motd.d/my_motd ++ + + + diff --git a/SOURCES/pam-1.3.1-namespace-mntopts.patch b/SOURCES/pam-1.3.1-namespace-mntopts.patch new file mode 100644 index 0000000..3249157 --- /dev/null +++ b/SOURCES/pam-1.3.1-namespace-mntopts.patch @@ -0,0 +1,131 @@ +diff --git a/modules/pam_namespace/namespace.conf.5.xml b/modules/pam_namespace/namespace.conf.5.xml +index c7698cb..a94b49e 100644 +--- a/modules/pam_namespace/namespace.conf.5.xml ++++ b/modules/pam_namespace/namespace.conf.5.xml +@@ -122,9 +122,14 @@ + mntopts=value + - value of this flag is passed to the mount call when the tmpfs mount is + done. It allows for example the specification of the maximum size of the +- tmpfs instance that is created by the mount call. See +- mount8 +- for details. ++ tmpfs instance that is created by the mount call. In addition to ++ options specified in the ++ tmpfs5 ++ manual the nosuid, ++ noexec, and nodev flags ++ can be used to respectively disable setuid bit effect, disable running ++ executables, and disable devices to be interpreted on the mounted ++ tmpfs filesystem. + + + +diff --git a/modules/pam_namespace/pam_namespace.c b/modules/pam_namespace/pam_namespace.c +index f541f89..660c7a1 100644 +--- a/modules/pam_namespace/pam_namespace.c ++++ b/modules/pam_namespace/pam_namespace.c +@@ -230,6 +230,73 @@ static int parse_iscript_params(char *params, struct polydir_s *poly) + return 0; + } + ++struct mntflag { ++ const char *name; ++ size_t len; ++ unsigned long flag; ++}; ++ ++#define LITERAL_AND_LEN(x) x, sizeof(x) - 1 ++ ++static const struct mntflag mntflags[] = { ++ { LITERAL_AND_LEN("noexec"), MS_NOEXEC }, ++ { LITERAL_AND_LEN("nosuid"), MS_NOSUID }, ++ { LITERAL_AND_LEN("nodev"), MS_NODEV } ++ }; ++ ++static int filter_mntopts(const char *opts, char **filtered, ++ unsigned long *mountflags) ++{ ++ size_t origlen = strlen(opts); ++ const char *end; ++ char *dest; ++ ++ dest = *filtered = NULL; ++ *mountflags = 0; ++ ++ if (origlen == 0) ++ return 0; ++ ++ do { ++ size_t len; ++ int i; ++ ++ end = strchr(opts, ','); ++ if (end == NULL) { ++ len = strlen(opts); ++ } else { ++ len = end - opts; ++ } ++ ++ for (i = 0; i < (int)(sizeof(mntflags)/sizeof(mntflags[0])); i++) { ++ if (mntflags[i].len != len) ++ continue; ++ if (memcmp(mntflags[i].name, opts, len) == 0) { ++ *mountflags |= mntflags[i].flag; ++ opts = end; ++ break; ++ } ++ } ++ ++ if (opts != end) { ++ if (dest != NULL) { ++ *dest = ','; ++ ++dest; ++ } else { ++ dest = *filtered = calloc(1, origlen + 1); ++ if (dest == NULL) ++ return -1; ++ } ++ memcpy(dest, opts, len); ++ dest += len; ++ } ++ ++ opts = end + 1; ++ } while (end != NULL); ++ ++ return 0; ++} ++ + static int parse_method(char *method, struct polydir_s *poly, + struct instance_data *idata) + { +@@ -289,7 +356,8 @@ static int parse_method(char *method, struct polydir_s *poly, + break; + } + free(poly->mount_opts); /* if duplicate mntopts specified */ +- if ((poly->mount_opts = strdup(flag+namelen+1)) == NULL) { ++ poly->mount_opts = NULL; ++ if (filter_mntopts(flag+namelen+1, &poly->mount_opts, &poly->mount_flags) != 0) { + pam_syslog(idata->pamh, LOG_CRIT, "Memory allocation error"); + return -1; + } +@@ -1484,7 +1552,7 @@ static int ns_setup(struct polydir_s *polyptr, + } + + if (polyptr->method == TMPFS) { +- if (mount("tmpfs", polyptr->dir, "tmpfs", 0, polyptr->mount_opts) < 0) { ++ if (mount("tmpfs", polyptr->dir, "tmpfs", polyptr->mount_flags, polyptr->mount_opts) < 0) { + pam_syslog(idata->pamh, LOG_ERR, "Error mounting tmpfs on %s, %m", + polyptr->dir); + return PAM_SESSION_ERR; +diff --git a/modules/pam_namespace/pam_namespace.h b/modules/pam_namespace/pam_namespace.h +index 47ebcc3..1522386 100644 +--- a/modules/pam_namespace/pam_namespace.h ++++ b/modules/pam_namespace/pam_namespace.h +@@ -166,6 +166,7 @@ struct polydir_s { + unsigned int flags; /* polydir flags */ + char *init_script; /* path to init script */ + char *mount_opts; /* mount options for tmpfs mount */ ++ unsigned long mount_flags; /* mount flags for tmpfs mount */ + uid_t owner; /* user which should own the polydir */ + gid_t group; /* group which should own the polydir */ + mode_t mode; /* mode of the polydir */ diff --git a/SOURCES/pam-1.3.1-tty-audit-manfix.patch b/SOURCES/pam-1.3.1-tty-audit-manfix.patch new file mode 100644 index 0000000..3d17963 --- /dev/null +++ b/SOURCES/pam-1.3.1-tty-audit-manfix.patch @@ -0,0 +1,33 @@ +From e31dd6c7d0faa7a06d3ebd50a0b6957b9f822d15 Mon Sep 17 00:00:00 2001 +From: Tomas Mraz +Date: Wed, 7 Aug 2019 18:13:57 +0200 +Subject: [PATCH] pam_tty_audit: Manual page clarification about password + logging + +* modules/pam_tty_audit/pam_tty_audit.8.xml: Explanation why passwords +can be sometimes logged even when the option is not set. +--- + modules/pam_tty_audit/pam_tty_audit.8.xml | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/modules/pam_tty_audit/pam_tty_audit.8.xml b/modules/pam_tty_audit/pam_tty_audit.8.xml +index 59a3406..e346c68 100644 +--- a/modules/pam_tty_audit/pam_tty_audit.8.xml ++++ b/modules/pam_tty_audit/pam_tty_audit.8.xml +@@ -149,6 +149,13 @@ + greater than or equal to min_uid will be + matched. + ++ ++ Please note that passwords in some circumstances may be logged by TTY auditing ++ even if the is not used. For example, all input to ++ an ssh session will be logged - even if there is a password being typed into ++ some software running at the remote host because only the local TTY state ++ affects the local TTY auditing. ++ + + + +-- +2.20.1 + diff --git a/SOURCES/pam-1.3.1-unix-improve-logging.patch b/SOURCES/pam-1.3.1-unix-improve-logging.patch new file mode 100644 index 0000000..682b4d5 --- /dev/null +++ b/SOURCES/pam-1.3.1-unix-improve-logging.patch @@ -0,0 +1,57 @@ +From a6845905869ccabb5eb802be37241eabec085dc7 Mon Sep 17 00:00:00 2001 +From: Tomas Mraz +Date: Mon, 14 Oct 2019 16:52:46 +0200 +Subject: [PATCH] pam_unix: Add logging useful for debugging problems + +Two messages added about obtaining the username are guarded +by the debug option as these should not be normally +logged - they can be useful for debugging but they do not +indicate any special condition. + +The message about authenticating user with blank password is +still just LOG_DEBUG priority but it is logged unconditionally +because it is somewhat extraordinary condition to have an user +with blank password. + +* modules/pam_unix/pam_unix_auth.c (pam_sm_authenticate): Replace + D() macro calls which are not enabled on production builds with + regular pam_syslog() calls. +--- + modules/pam_unix/pam_unix_auth.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/modules/pam_unix/pam_unix_auth.c b/modules/pam_unix/pam_unix_auth.c +index 681e49d..3fca945 100644 +--- a/modules/pam_unix/pam_unix_auth.c ++++ b/modules/pam_unix/pam_unix_auth.c +@@ -130,15 +130,16 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) + AUTH_RETURN; + } + if (on(UNIX_DEBUG, ctrl)) +- D(("username [%s] obtained", name)); ++ pam_syslog(pamh, LOG_DEBUG, "username [%s] obtained", name); + } else { +- D(("trouble reading username")); + if (retval == PAM_CONV_AGAIN) { + D(("pam_get_user/conv() function is not ready yet")); + /* it is safe to resume this function so we translate this + * retval to the value that indicates we're happy to resume. + */ + retval = PAM_INCOMPLETE; ++ } else if (on(UNIX_DEBUG, ctrl)) { ++ pam_syslog(pamh, LOG_DEBUG, "could not obtain username"); + } + AUTH_RETURN; + } +@@ -146,7 +147,7 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) + /* if this user does not have a password... */ + + if (_unix_blankpasswd(pamh, ctrl, name)) { +- D(("user '%s' has blank passwd", name)); ++ pam_syslog(pamh, LOG_DEBUG, "user [%s] has blank password; authenticated without it", name); + name = NULL; + retval = PAM_SUCCESS; + AUTH_RETURN; +-- +2.20.1 + diff --git a/SPECS/pam.spec b/SPECS/pam.spec index 7eb2c7f..315e533 100644 --- a/SPECS/pam.spec +++ b/SPECS/pam.spec @@ -3,7 +3,7 @@ Summary: An extensible library which provides authentication for applications Name: pam Version: 1.3.1 -Release: 4%{?dist} +Release: 8%{?dist} # The library is BSD licensed with option to relicense as GPLv2+ # - this option is redundant as the BSD license allows that anyway. # pam_timestamp, pam_loginuid, and pam_console modules are GPLv2+. @@ -44,6 +44,15 @@ Patch32: pam-1.2.1-console-devname.patch Patch33: pam-1.3.0-unix-nomsg.patch Patch34: pam-1.3.1-coverity.patch Patch35: pam-1.3.1-console-build.patch +Patch36: pam-1.3.1-faillock-update.patch +Patch37: pam-1.3.1-namespace-mntopts.patch +Patch38: pam-1.3.1-lastlog-no-showfailed.patch +Patch39: pam-1.3.1-lastlog-unlimited-fsize.patch +Patch40: pam-1.3.1-unix-improve-logging.patch +Patch41: pam-1.3.1-tty-audit-manfix.patch +Patch42: pam-1.3.1-fds-closing.patch +Patch43: pam-1.3.1-authtok-verify-fix.patch +Patch44: pam-1.3.1-motd-manpage.patch %define _pamlibdir %{_libdir} %define _moduledir %{_libdir}/security @@ -127,6 +136,15 @@ cp %{SOURCE18} . %patch33 -p1 -b .nomsg %patch34 -p1 -b .coverity %patch35 -p1 -b .console-build +%patch36 -p1 -b .faillock-update +%patch37 -p1 -b .mntopts +%patch38 -p1 -b .no-showfailed +%patch39 -p1 -b .unlimited-fsize +%patch40 -p1 -b .improve-logging +%patch41 -p1 -b .tty-audit-manfix +%patch42 -p1 -b .fds-closing +%patch43 -p1 -b .authtok-verify-fix +%patch44 -p1 -b .motd-manpage autoreconf -i %build @@ -152,6 +170,9 @@ for readme in modules/pam_*/README ; do cp -f ${readme} doc/txts/README.`dirname ${readme} | sed -e 's|^modules/||'` done +rm -rf doc/txts/README.pam_tally* +rm -rf doc/sag/html/*pam_tally* + # Install the binaries, libraries, and modules. make install DESTDIR=$RPM_BUILD_ROOT LDCONFIG=: @@ -176,7 +197,6 @@ install -m 644 %{SOURCE10} $RPM_BUILD_ROOT%{_pamconfdir}/config-util install -m 644 %{SOURCE16} $RPM_BUILD_ROOT%{_pamconfdir}/postlogin install -m 600 /dev/null $RPM_BUILD_ROOT%{_secconfdir}/opasswd install -d -m 755 $RPM_BUILD_ROOT/var/log -install -m 600 /dev/null $RPM_BUILD_ROOT/var/log/tallylog install -d -m 755 $RPM_BUILD_ROOT/var/run/faillock # Install man pages. @@ -336,6 +356,7 @@ done %config(noreplace) %{_secconfdir}/chroot.conf %config %{_secconfdir}/console.perms %config(noreplace) %{_secconfdir}/console.handlers +%config(noreplace) %{_secconfdir}/faillock.conf %config(noreplace) %{_secconfdir}/group.conf %config(noreplace) %{_secconfdir}/limits.conf %dir %{_secconfdir}/limits.d @@ -352,7 +373,6 @@ done %config(noreplace) %{_secconfdir}/sepermit.conf %dir /var/run/sepermit %endif -%ghost %verify(not md5 size mtime) /var/log/tallylog %dir /var/run/faillock %{_prefix}/lib/tmpfiles.d/pam.conf %{_mandir}/man5/* @@ -369,6 +389,24 @@ done %doc doc/specs/rfc86.0.txt %changelog +* Thu Dec 19 2019 Tomáš Mráz 1.3.1-8 +- pam_motd: Document how to properly silence unwanted motd messages + +* Mon Dec 16 2019 Tomáš Mráz 1.3.1-6 +- pam_faillock: Fix regression in admin_group support + +* Wed Oct 16 2019 Tomáš Mráz 1.3.1-5 +- pam_faillock: Support configuration file /etc/security/faillock.conf +- pam_faillock: Support local_users_only option +- pam_namespace: Support noexec, nosuid and nodev flags for tmpfs mounts +- Drop tallylog and pam_tally[2] documentation +- pam_lastlog: Do not display failed attempts with PAM_SILENT flag +- pam_lastlog: Support unlimited option to override fsize limit +- pam_unix: Log if user authenticated without password +- pam_tty_audit: Improve manual page +- Optimize closing fds when spawning helpers +- Fix duplicate password verification in pam_authtok_verify() + * Fri Dec 7 2018 Tomáš Mráz 1.3.1-4 - Drop pam_tally2 which was obsoleted and deprecated long time ago