From: Carlos Santos Date: Wed, 11 Sep 2019 02:08:30 +0000 (-0300) Subject: pam_lastlog: prevent crash due to reduced 'fsize' limit X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9349333a9ae958205294cd25e97fd6b4805bd82b;p=linux-pam 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 --- diff --git a/modules/pam_lastlog/pam_lastlog.c b/modules/pam_lastlog/pam_lastlog.c index e980c047..a135c9f7 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"));