From 62368315c0479657742f34074822b1f9b8608c65 Mon Sep 17 00:00:00 2001 From: Thorsten Kukuk Date: Fri, 6 Sep 2024 11:55:46 +0200 Subject: [PATCH] pam_limits: use systemd-logind instead of utmp (#822) The utmp database is unreliable for counting logged in users, since there is no standard which defines who should create an entry at which time for which reason. And it has a Y2038 problem with glibc/x86-64. Query systemd-logind for the number of user sessions instead. --- modules/pam_limits/Makefile.am | 4 +- modules/pam_limits/pam_limits.c | 80 +++++++++++++++++++++++++++++++-- 2 files changed, 79 insertions(+), 5 deletions(-) diff --git a/modules/pam_limits/Makefile.am b/modules/pam_limits/Makefile.am index ab3cf33ed..3f64d79bb 100644 --- a/modules/pam_limits/Makefile.am +++ b/modules/pam_limits/Makefile.am @@ -24,7 +24,7 @@ limits_conf_dir = $(SCONFIGDIR)/limits.d AM_CFLAGS = -I$(top_srcdir)/libpam/include \ -DLIMITS_FILE_DIR=\"$(limits_conf_dir)\" \ - $(WARN_CFLAGS) + $(LOGIND_CFLAGS) $(WARN_CFLAGS) AM_LDFLAGS = -no-undefined -avoid-version -module if HAVE_VERSIONING AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map @@ -32,7 +32,7 @@ endif securelib_LTLIBRARIES = pam_limits.la pam_limits_la_LIBADD = $(top_builddir)/libpam_internal/libpam_internal.la \ - $(top_builddir)/libpam/libpam.la + $(top_builddir)/libpam/libpam.la $(SYSTEMD_LIBS) dist_secureconf_DATA = limits.conf diff --git a/modules/pam_limits/pam_limits.c b/modules/pam_limits/pam_limits.c index 75c584fca..4b4a45f18 100644 --- a/modules/pam_limits/pam_limits.c +++ b/modules/pam_limits/pam_limits.c @@ -36,7 +36,12 @@ #include #include #include +#ifdef USE_LOGIND +#include +#else #include +#endif + #ifndef UT_USER /* some systems have ut_name instead of ut_user */ #define UT_USER ut_user #endif @@ -240,7 +245,6 @@ static int check_logins (pam_handle_t *pamh, const char *name, int limit, int ctrl, struct pam_limit_s *pl) { - struct utmp *ut; int count; if (ctrl & PAM_DEBUG_ARG) { @@ -255,8 +259,6 @@ check_logins (pam_handle_t *pamh, const char *name, int limit, int ctrl, return LOGIN_ERR; } - setutent(); - /* Because there is no definition about when an application actually adds a utmp entry, some applications bizarrely do the utmp call before the have PAM authenticate them to the system: @@ -273,6 +275,77 @@ check_logins (pam_handle_t *pamh, const char *name, int limit, int ctrl, count = 1; } +#ifdef USE_LOGIND + char **sessions_list; + int sessions = sd_get_sessions(&sessions_list); + + /* maxlogins needs to be 2 with systemd-logind because + of the systemd --user process started with first login by + pam_systemd. + Which is also calling pam_limits, but in this very first special + case the session does already exist and is counted twice. + With start of the second session, session manager is already running + and no longer counted. */ + if (limit == 1) { + pam_syslog(pamh, LOG_WARNING, "Maxlogin limit needs to be 2 or higher with systemd-logind"); + return LIMIT_ERR; + } + + if (sessions < 0) { + pam_syslog(pamh, LOG_ERR, "logind error getting session list: %s", + strerror(-sessions)); + return LIMIT_ERR; + } else if (sessions > 0 && sessions_list != NULL && !pl->flag_numsyslogins) { + int i; + + for (i = 0; i < sessions; i++) { + char *user = NULL; + char *class = NULL; + + if (sd_session_get_class(sessions_list[i], &class) < 0 || class == NULL) + continue; + + if (strncmp(class, "user", 4) != 0) { /* user, user-early, user-incomplete */ + free (class); + continue; + } + + if (sd_session_get_username(sessions_list[i], &user) < 0 || user == NULL) { + pam_syslog(pamh, LOG_ERR, "logind error getting username: %s", + strerror(-sessions)); + return LIMIT_ERR; + } + + if (((pl->login_limit_def == LIMITS_DEF_USER) + || (pl->login_limit_def == LIMITS_DEF_GROUP) + || (pl->login_limit_def == LIMITS_DEF_DEFAULT)) + && strcmp(name, user) != 0) { + free(user); + continue; + } + if ((pl->login_limit_def == LIMITS_DEF_ALLGROUP) + && pl->login_group != NULL + && !pam_modutil_user_in_group_nam_nam(pamh, user, pl->login_group)) { + free(user); + continue; + } + free(user); + + if (++count > limit) { + break; + } + } + for (i = 0; i < sessions; i++) + free(sessions_list[i]); + free(sessions_list); + } else { + count = sessions; + } +#else + struct utmp *ut; + + setutent(); + while((ut = getutent())) { #ifdef USER_PROCESS if (ut->ut_type != USER_PROCESS) { @@ -311,6 +384,7 @@ check_logins (pam_handle_t *pamh, const char *name, int limit, int ctrl, } } endutent(); +#endif if (count > limit) { if (name) { pam_syslog(pamh, LOG_NOTICE,