From: Daan De Meyer Date: Fri, 15 May 2026 14:54:04 +0000 (+0000) Subject: tree-wide: dlopen libpam in pam plugins X-Git-Tag: v261-rc1~104^2~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=62053ee28955ed339d44e7725bb1b555c51e7143;p=thirdparty%2Fsystemd.git tree-wide: dlopen libpam in pam plugins Same reasoning as for cryptsetup tokens. It means we can include the pam plugins in the main systemd package without the package manager introducing a dependency on libpam. It also makes things more consistent and makes writing the upcoming linking test script a lot simpler. At the same time, we get rid of the libpam_misc dependency as the one symbol we were using from it is trivial to reimplement ourselves. --- diff --git a/meson.build b/meson.build index c69b96e1c6d..92129bee7d7 100644 --- a/meson.build +++ b/meson.build @@ -1133,12 +1133,7 @@ if not libpam.found() # Debian older than bookworm and Ubuntu older than 22.10 do not provide the .pc file. libpam = cc.find_library('pam', required : feature) endif -libpam_misc = dependency('pam_misc', - required : feature.disabled() ? feature : false) -if not libpam_misc.found() - libpam_misc = cc.find_library('pam_misc', required : feature) -endif -conf.set10('HAVE_PAM', libpam.found() and libpam_misc.found()) +conf.set10('HAVE_PAM', libpam.found()) libpam_cflags = libpam.partial_dependency(includes: true, compile_args: true) libmicrohttpd = dependency('libmicrohttpd', @@ -1949,21 +1944,7 @@ pam_template = { libshared_static, ], 'dependencies' : [ - # Note: our PAM modules also call dlopen_libpam() and use - # symbols acquired through that, hence the explicit dep here is - # strictly speaking unnecessary. We put it in place anyway, - # since for the PAM modules we cannot avoid libpam anyway, - # after all they are loaded *by* libpam, and hence there's no - # loss in having explicit deps here, but there's a win: it - # makes the deps more visible. - # - # (In case you wonder why we do dlopen_libpam() from the PAM - # modules in the first place: that's mostly so that all our PAM - # code (regardless if our PAM modules or our PAM consuming - # programs) can use the same helpers, which hence go via - # dlopen_libpam(). - libpam_misc, - libpam, + libpam_cflags, threads, ], 'install' : true, diff --git a/src/home/meson.build b/src/home/meson.build index 600f00b4ac9..631eeb13aa8 100644 --- a/src/home/meson.build +++ b/src/home/meson.build @@ -116,8 +116,7 @@ modules += [ 'sources' : pam_systemd_home_sources, 'dependencies' : [ libintl, - libpam_misc, - libpam, + libpam_cflags, threads, ], 'version-script' : meson.current_source_dir() / 'pam_systemd_home.sym', diff --git a/src/home/pam_systemd_home.c b/src/home/pam_systemd_home.c index 1de66b4c032..5fe6dcbec20 100644 --- a/src/home/pam_systemd_home.c +++ b/src/home/pam_systemd_home.c @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include #include "sd-bus.h" @@ -45,7 +44,7 @@ static int parse_argv( k = parse_boolean(v); if (k < 0) - pam_syslog(pamh, LOG_WARNING, "Failed to parse suspend= argument, ignoring: %s", v); + sym_pam_syslog(pamh, LOG_WARNING, "Failed to parse suspend= argument, ignoring: %s", v); else if (flags) SET_FLAG(*flags, ACQUIRE_PLEASE_SUSPEND, k); @@ -57,12 +56,12 @@ static int parse_argv( int k; k = parse_boolean(v); if (k < 0) - pam_syslog(pamh, LOG_WARNING, "Failed to parse debug= argument, ignoring: %s", v); + sym_pam_syslog(pamh, LOG_WARNING, "Failed to parse debug= argument, ignoring: %s", v); else if (debug) *debug = k; } else - pam_syslog(pamh, LOG_WARNING, "Unknown parameter '%s', ignoring.", argv[i]); + sym_pam_syslog(pamh, LOG_WARNING, "Unknown parameter '%s', ignoring.", argv[i]); } return 0; @@ -76,7 +75,7 @@ static int parse_env(pam_handle_t *pamh, AcquireHomeFlags *flags) { * easy to declare the features of a display manager in code rather than configuration, and this is * really a feature of code */ - v = pam_getenv(pamh, "SYSTEMD_HOME_SUSPEND"); + v = sym_pam_getenv(pamh, "SYSTEMD_HOME_SUSPEND"); if (!v) { /* Also check the process env block, so that people can control this via an env var from the * outside of our process. */ @@ -87,7 +86,7 @@ static int parse_env(pam_handle_t *pamh, AcquireHomeFlags *flags) { r = parse_boolean(v); if (r < 0) - pam_syslog(pamh, LOG_WARNING, "Failed to parse $SYSTEMD_HOME_SUSPEND argument, ignoring: %s", v); + sym_pam_syslog(pamh, LOG_WARNING, "Failed to parse $SYSTEMD_HOME_SUSPEND argument, ignoring: %s", v); else if (flags) SET_FLAG(*flags, ACQUIRE_PLEASE_SUSPEND, r); @@ -106,7 +105,7 @@ static int acquire_user_record( assert(pamh); if (!username) { - r = pam_get_user(pamh, &username, NULL); + r = sym_pam_get_user(pamh, &username, NULL); if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to get user name: @PAMERR@"); if (isempty(username)) @@ -149,7 +148,7 @@ static int acquire_user_record( return pam_log_oom(pamh); /* Let's use the cache, so that we can share it between the session and the authentication hooks */ - r = pam_get_data(pamh, homed_field, (const void**) &json); + r = sym_pam_get_data(pamh, homed_field, (const void**) &json); if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to get PAM user record data: @PAMERR@"); if (r == PAM_SUCCESS && json) { @@ -211,13 +210,13 @@ static int acquire_user_record( return pam_syslog_pam_error(pamh, LOG_ERR, PAM_SERVICE_ERR, "Acquired user record does not match user name."); - /* Update the 'username' pointer to point to our own record now. The pam_set_item() call below is + /* Update the 'username' pointer to point to our own record now. The sym_pam_set_item() call below is * going to invalidate the old version after all */ username = ur->user_name; /* We passed all checks. Let's now make sure the rest of the PAM stack continues with the primary, * normalized name of the user record (i.e. not an alias or so). */ - r = pam_set_item(pamh, PAM_USER, ur->user_name); + r = sym_pam_set_item(pamh, PAM_USER, ur->user_name); if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to update username PAM item to '%s': @PAMERR@", ur->user_name); @@ -230,7 +229,7 @@ static int acquire_user_record( if (!json_copy) return pam_log_oom(pamh); - r = pam_set_data(pamh, homed_field, json_copy, pam_cleanup_free); + r = sym_pam_set_data(pamh, homed_field, json_copy, pam_cleanup_free); if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to set PAM user record data '%s': @PAMERR@", homed_field); @@ -246,7 +245,7 @@ static int acquire_user_record( if (!generic_field) return pam_log_oom(pamh); - r = pam_set_data(pamh, generic_field, json_copy, pam_cleanup_free); + r = sym_pam_set_data(pamh, generic_field, json_copy, pam_cleanup_free); if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to set PAM user record data '%s': @PAMERR@", generic_field); @@ -256,7 +255,7 @@ static int acquire_user_record( /* Let's store the area we parsed out of the name in an env var, so that pam_systemd later can honour it. */ if (area) { - r = pam_misc_setenv(pamh, "XDG_AREA", area, /* readonly= */ 0); + r = pam_putenv_assign(pamh, "XDG_AREA", area); if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to set environment variable $XDG_AREA to '%s': @PAMERR@", area); @@ -269,7 +268,7 @@ static int acquire_user_record( user_unknown: /* Cache this, so that we don't check again */ - r = pam_set_data(pamh, homed_field, POINTER_MAX, NULL); + r = sym_pam_set_data(pamh, homed_field, POINTER_MAX, NULL); if (r != PAM_SUCCESS) pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to set PAM user record data '%s' to invalid, ignoring: @PAMERR@", @@ -289,7 +288,7 @@ static int release_user_record(pam_handle_t *pamh, const char *username) { if (!homed_field) return pam_log_oom(pamh); - r = pam_set_data(pamh, homed_field, NULL, NULL); + r = sym_pam_set_data(pamh, homed_field, NULL, NULL); if (r != PAM_SUCCESS) pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to release PAM user record data '%s': @PAMERR@", homed_field); @@ -298,7 +297,7 @@ static int release_user_record(pam_handle_t *pamh, const char *username) { if (!generic_field) return pam_log_oom(pamh); - k = pam_set_data(pamh, generic_field, NULL, NULL); + k = sym_pam_set_data(pamh, generic_field, NULL, NULL); if (k != PAM_SUCCESS) pam_syslog_pam_error(pamh, LOG_ERR, k, "Failed to release PAM user record data '%s': @PAMERR@", generic_field); @@ -569,7 +568,7 @@ static int acquire_home( * prompt the user for the missing unlock credentials, and then chainload the real shell. */ - r = pam_get_user(pamh, &username, NULL); + r = sym_pam_get_user(pamh, &username, NULL); if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to get user name: @PAMERR@"); if (isempty(username)) @@ -580,7 +579,7 @@ static int acquire_home( if (!fd_field) return pam_log_oom(pamh); - r = pam_get_data(pamh, fd_field, &home_fd_ptr); + r = sym_pam_get_data(pamh, fd_field, &home_fd_ptr); if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to retrieve PAM home reference fd: @PAMERR@"); @@ -618,7 +617,7 @@ static int acquire_home( /* If there's already a cached password, use it. But if not let's authenticate * without anything, maybe some other authentication mechanism systemd-homed * implements (such as PKCS#11) allows us to authenticate without anything else. */ - r = pam_get_item(pamh, PAM_AUTHTOK, (const void**) &cached_password); + r = sym_pam_get_item(pamh, PAM_AUTHTOK, (const void**) &cached_password); if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to get cached password: @PAMERR@"); @@ -681,7 +680,7 @@ static int acquire_home( (void) pam_prompt_graceful(pamh, PAM_ERROR_MSG, NULL, _("Home of user %s is currently locked, please unlock locally first."), ur->user_name); if (FLAGS_SET(flags, ACQUIRE_MUST_AUTHENTICATE)) - pam_syslog(pamh, LOG_ERR, "Failed to prompt for password/prompt."); + sym_pam_syslog(pamh, LOG_ERR, "Failed to prompt for password/prompt."); else pam_debug_syslog(pamh, debug, "Failed to prompt for password/prompt."); @@ -720,12 +719,12 @@ static int acquire_home( /* Later PAM modules may need the auth token, but only during pam_authenticate. */ if (FLAGS_SET(flags, ACQUIRE_MUST_AUTHENTICATE) && !strv_isempty(secret->password)) { - r = pam_set_item(pamh, PAM_AUTHTOK, *secret->password); + r = sym_pam_set_item(pamh, PAM_AUTHTOK, *secret->password); if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to set PAM auth token: @PAMERR@"); } - r = pam_set_data(pamh, fd_field, FD_TO_PTR(acquired_fd), cleanup_home_fd); + r = sym_pam_set_data(pamh, fd_field, FD_TO_PTR(acquired_fd), cleanup_home_fd); if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to set PAM bus data: @PAMERR@"); TAKE_FD(acquired_fd); @@ -744,13 +743,13 @@ static int acquire_home( * manager for us (since it would see an inaccessible home directory). Hence set an environment * variable that pam_systemd looks for). */ if (unrestricted) { - r = pam_putenv(pamh, "XDG_SESSION_INCOMPLETE=1"); + r = sym_pam_putenv(pamh, "XDG_SESSION_INCOMPLETE=1"); if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_WARNING, r, "Failed to set XDG_SESSION_INCOMPLETE= environment variable: @PAMERR@"); - pam_syslog(pamh, LOG_NOTICE, "Home for user %s acquired in incomplete mode, requires later activation.", ur->user_name); + sym_pam_syslog(pamh, LOG_NOTICE, "Home for user %s acquired in incomplete mode, requires later activation.", ur->user_name); } else - pam_syslog(pamh, LOG_NOTICE, "Home for user %s successfully acquired.", ur->user_name); + sym_pam_syslog(pamh, LOG_NOTICE, "Home for user %s successfully acquired.", ur->user_name); return PAM_SUCCESS; } @@ -767,13 +766,13 @@ static int release_home_fd(pam_handle_t *pamh, const char *username) { if (!fd_field) return pam_log_oom(pamh); - r = pam_get_data(pamh, fd_field, &home_fd_ptr); + r = sym_pam_get_data(pamh, fd_field, &home_fd_ptr); if (r == PAM_NO_MODULE_DATA || (r == PAM_SUCCESS && PTR_TO_FD(home_fd_ptr) < 0)) return PAM_NO_MODULE_DATA; if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to retrieve PAM home reference fd: @PAMERR@"); - r = pam_set_data(pamh, fd_field, NULL, NULL); + r = sym_pam_set_data(pamh, fd_field, NULL, NULL); if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to release PAM home reference fd: @PAMERR@"); @@ -887,12 +886,12 @@ _public_ PAM_EXTERN int pam_sm_open_session( if (r != PAM_SUCCESS) return r; - r = pam_putenv(pamh, "SYSTEMD_HOME=1"); + r = sym_pam_putenv(pamh, "SYSTEMD_HOME=1"); if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to set PAM environment variable $SYSTEMD_HOME: @PAMERR@"); - r = pam_putenv(pamh, FLAGS_SET(flags, ACQUIRE_PLEASE_SUSPEND) ? "SYSTEMD_HOME_SUSPEND=1" : "SYSTEMD_HOME_SUSPEND=0"); + r = sym_pam_putenv(pamh, FLAGS_SET(flags, ACQUIRE_PLEASE_SUSPEND) ? "SYSTEMD_HOME_SUSPEND=1" : "SYSTEMD_HOME_SUSPEND=0"); if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to set PAM environment variable $SYSTEMD_HOME_SUSPEND: @PAMERR@"); @@ -911,6 +910,10 @@ _public_ PAM_EXTERN int pam_sm_close_session( bool debug = false; int r; + r = dlopen_libpam(LOG_DEBUG); + if (r < 0) + return PAM_SERVICE_ERR; + pam_log_setup(); if (parse_argv(pamh, @@ -921,7 +924,7 @@ _public_ PAM_EXTERN int pam_sm_close_session( pam_debug_syslog(pamh, debug, "pam-systemd-homed: closing session..."); - r = pam_get_user(pamh, &username, NULL); + r = sym_pam_get_user(pamh, &username, NULL); if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to get user name: @PAMERR@"); if (isempty(username)) @@ -954,7 +957,7 @@ _public_ PAM_EXTERN int pam_sm_close_session( return pam_syslog_pam_error(pamh, LOG_ERR, PAM_SESSION_ERR, "Failed to release user home: %s", bus_error_message(&error, r)); - pam_syslog(pamh, LOG_NOTICE, "Not deactivating home directory of %s, as it is still used.", username); + sym_pam_syslog(pamh, LOG_NOTICE, "Not deactivating home directory of %s, as it is still used.", username); } return PAM_SUCCESS; @@ -1005,7 +1008,7 @@ _public_ PAM_EXTERN int pam_sm_acct_mgmt( switch (r) { case -ESTALE: - pam_syslog(pamh, LOG_WARNING, "User record for '%s' is newer than current system time, assuming incorrect system clock, allowing access.", ur->user_name); + sym_pam_syslog(pamh, LOG_WARNING, "User record for '%s' is newer than current system time, assuming incorrect system clock, allowing access.", ur->user_name); break; case -ENOLCK: @@ -1062,7 +1065,7 @@ _public_ PAM_EXTERN int pam_sm_acct_mgmt( case -ESTALE: /* If the system clock is wrong, let's log but continue */ - pam_syslog(pamh, LOG_WARNING, "Couldn't check if password change is required, last change is in the future, system clock likely wrong."); + sym_pam_syslog(pamh, LOG_WARNING, "Couldn't check if password change is required, last change is in the future, system clock likely wrong."); break; case -EROFS: @@ -1121,7 +1124,7 @@ _public_ PAM_EXTERN int pam_sm_chauthtok( /* No, it's not cached, then let's ask for the password and its verification, and cache * it. */ - r = pam_get_authtok_noverify(pamh, &new_password, "New password: "); + r = sym_pam_get_authtok_noverify(pamh, &new_password, "New password: "); if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to get new password: @PAMERR@"); @@ -1130,7 +1133,7 @@ _public_ PAM_EXTERN int pam_sm_chauthtok( return PAM_AUTHTOK_ERR; } - r = pam_get_authtok_verify(pamh, &new_password, "new password: "); /* Lower case, since PAM prefixes 'Repeat' */ + r = sym_pam_get_authtok_verify(pamh, &new_password, "new password: "); /* Lower case, since PAM prefixes 'Repeat' */ if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to get password again: @PAMERR@"); diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c index 4e571e70565..1b0adfae9ea 100644 --- a/src/login/pam_systemd.c +++ b/src/login/pam_systemd.c @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -88,7 +87,7 @@ static int parse_caps( c = capability_from_name(s); if (c < 0) { - pam_syslog(pamh, LOG_WARNING, "Unknown capability, ignoring: %s", s); + sym_pam_syslog(pamh, LOG_WARNING, "Unknown capability, ignoring: %s", s); continue; } @@ -146,7 +145,7 @@ static int parse_argv( } else if ((p = startswith(argv[i], "area="))) { if (!isempty(p) && !filename_is_valid(p)) - pam_syslog(pamh, LOG_WARNING, "Area name specified among PAM module parameters is not valid, ignoring: %s", p); + sym_pam_syslog(pamh, LOG_WARNING, "Area name specified among PAM module parameters is not valid, ignoring: %s", p); else if (area) *area = p; @@ -157,22 +156,22 @@ static int parse_argv( } else if ((p = startswith(argv[i], "debug="))) { r = parse_boolean(p); if (r < 0) - pam_syslog(pamh, LOG_WARNING, "Failed to parse debug= argument, ignoring: %s", p); + sym_pam_syslog(pamh, LOG_WARNING, "Failed to parse debug= argument, ignoring: %s", p); else if (debug) *debug = r; } else if ((p = startswith(argv[i], "default-capability-bounding-set="))) { r = parse_caps(pamh, p, default_capability_bounding_set); if (r < 0) - pam_syslog(pamh, LOG_WARNING, "Failed to parse default-capability-bounding-set= argument, ignoring: %s", p); + sym_pam_syslog(pamh, LOG_WARNING, "Failed to parse default-capability-bounding-set= argument, ignoring: %s", p); } else if ((p = startswith(argv[i], "default-capability-ambient-set="))) { r = parse_caps(pamh, p, default_capability_ambient_set); if (r < 0) - pam_syslog(pamh, LOG_WARNING, "Failed to parse default-capability-ambient-set= argument, ignoring: %s", p); + sym_pam_syslog(pamh, LOG_WARNING, "Failed to parse default-capability-ambient-set= argument, ignoring: %s", p); } else - pam_syslog(pamh, LOG_WARNING, "Unknown parameter '%s', ignoring.", argv[i]); + sym_pam_syslog(pamh, LOG_WARNING, "Unknown parameter '%s', ignoring.", argv[i]); } return 0; @@ -184,7 +183,7 @@ static int acquire_user_record(pam_handle_t *pamh, UserRecord **ret_record) { assert(pamh); const char *username = NULL; - r = pam_get_user(pamh, &username, NULL); + r = sym_pam_get_user(pamh, &username, NULL); if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to get user name: @PAMERR@"); if (isempty(username)) @@ -198,7 +197,7 @@ static int acquire_user_record(pam_handle_t *pamh, UserRecord **ret_record) { _cleanup_(user_record_unrefp) UserRecord *ur = NULL; const char *json = NULL; - r = pam_get_data(pamh, field, (const void**) &json); + r = sym_pam_get_data(pamh, field, (const void**) &json); if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to get PAM user record data: @PAMERR@"); if (r == PAM_SUCCESS && json) { @@ -240,7 +239,7 @@ static int acquire_user_record(pam_handle_t *pamh, UserRecord **ret_record) { return pam_syslog_errno(pamh, LOG_ERR, r, "Failed to format user JSON: %m"); /* And cache it for everyone else */ - r = pam_set_data(pamh, field, formatted, pam_cleanup_free); + r = sym_pam_set_data(pamh, field, formatted, pam_cleanup_free); if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to set PAM user record data '%s': @PAMERR@", field); @@ -382,7 +381,7 @@ static int append_session_memory_max(pam_handle_t *pamh, sd_bus_message *m, cons uint64_t val; r = parse_size(limit, 1024, &val); if (r < 0) { - pam_syslog(pamh, LOG_WARNING, "Failed to parse systemd.memory_max, ignoring: %s", limit); + sym_pam_syslog(pamh, LOG_WARNING, "Failed to parse systemd.memory_max, ignoring: %s", limit); return 0; } @@ -402,7 +401,7 @@ static int append_session_runtime_max_sec(pam_handle_t *pamh, sd_bus_message *m, usec_t val; r = parse_sec(limit, &val); if (r < 0) { - pam_syslog(pamh, LOG_WARNING, "Failed to parse systemd.runtime_max_sec: %s, ignoring.", limit); + sym_pam_syslog(pamh, LOG_WARNING, "Failed to parse systemd.runtime_max_sec: %s, ignoring.", limit); return 0; } @@ -422,7 +421,7 @@ static int append_session_tasks_max(pam_handle_t *pamh, sd_bus_message *m, const uint64_t val; r = safe_atou64(limit, &val); if (r < 0) { - pam_syslog(pamh, LOG_WARNING, "Failed to parse systemd.tasks_max, ignoring: %s", limit); + sym_pam_syslog(pamh, LOG_WARNING, "Failed to parse systemd.tasks_max, ignoring: %s", limit); return 0; } @@ -441,7 +440,7 @@ static int append_session_cpu_weight(pam_handle_t *pamh, sd_bus_message *m, cons uint64_t val; r = cg_cpu_weight_parse(limit, &val); if (r < 0) { - pam_syslog(pamh, LOG_WARNING, "Failed to parse systemd.cpu_weight, ignoring: %s", limit); + sym_pam_syslog(pamh, LOG_WARNING, "Failed to parse systemd.cpu_weight, ignoring: %s", limit); return 0; } @@ -460,7 +459,7 @@ static int append_session_io_weight(pam_handle_t *pamh, sd_bus_message *m, const uint64_t val; r = cg_weight_parse(limit, &val); if (r < 0) { - pam_syslog(pamh, LOG_WARNING, "Failed to parse systemd.io_weight, ignoring: %s", limit); + sym_pam_syslog(pamh, LOG_WARNING, "Failed to parse systemd.io_weight, ignoring: %s", limit); return 0; } @@ -479,7 +478,7 @@ static const char* getenv_harder(pam_handle_t *pamh, const char *key, const char * PAM services don't have to be reworked to set systemd-specific properties, but these properties * can still be set from the unit file Environment= block. */ - v = pam_getenv(pamh, key); + v = sym_pam_getenv(pamh, key); if (!isempty(v)) return v; @@ -507,9 +506,9 @@ static bool getenv_harder_bool(pam_handle_t *pamh, const char *key, bool fallbac r = parse_boolean(v); if (r < 0) { - pam_syslog(pamh, LOG_WARNING, - "Failed to parse environment variable value '%s' of '%s', falling back to using '%s'.", - v, key, true_false(fallback)); + sym_pam_syslog(pamh, LOG_WARNING, + "Failed to parse environment variable value '%s' of '%s', falling back to using '%s'.", + v, key, true_false(fallback)); return fallback; } @@ -529,9 +528,9 @@ static uint32_t getenv_harder_uint32(pam_handle_t *pamh, const char *key, uint32 uint32_t u; r = safe_atou32(v, &u); if (r < 0) { - pam_syslog(pamh, LOG_WARNING, - "Failed to parse environment variable value '%s' of '%s' as unsigned integer, falling back to using %" PRIu32 ".", - v, key, fallback); + sym_pam_syslog(pamh, LOG_WARNING, + "Failed to parse environment variable value '%s' of '%s' as unsigned integer, falling back to using %" PRIu32 ".", + v, key, fallback); return fallback; } @@ -548,14 +547,14 @@ static int update_environment(pam_handle_t *pamh, const char *key, const char *v * about errors. */ if (isempty(value)) { - /* Unset the variable if set. Note that pam_putenv() would log nastily behind our back if we + /* Unset the variable if set. Note that sym_pam_putenv() would log nastily behind our back if we * call it without the variable actually being set. Hence we check explicitly if it's set * before. */ - if (!pam_getenv(pamh, key)) + if (!sym_pam_getenv(pamh, key)) return PAM_SUCCESS; - r = pam_putenv(pamh, key); + r = sym_pam_putenv(pamh, key); if (!IN_SET(r, PAM_SUCCESS, PAM_BAD_ITEM)) return pam_syslog_pam_error(pamh, LOG_WARNING, r, "Failed to unset %s environment variable: @PAMERR@", key); @@ -563,7 +562,7 @@ static int update_environment(pam_handle_t *pamh, const char *key, const char *v return PAM_SUCCESS; } - r = pam_misc_setenv(pamh, key, value, /* readonly= */ false); + r = pam_putenv_assign(pamh, key, value); if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to set environment variable %s: @PAMERR@", key); @@ -588,7 +587,7 @@ static int propagate_credential_to_environment(pam_handle_t *pamh, bool debug, c return PAM_SUCCESS; } - r = pam_misc_setenv(pamh, varname, value, 0); + r = pam_putenv_assign(pamh, varname, value); if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to set environment variable %s: @PAMERR@", varname); @@ -610,7 +609,7 @@ static bool validate_runtime_directory(pam_handle_t *pamh, const char *path, uid * otherwise we might end up setting $XDG_RUNTIME_DIR to some directory owned by the wrong user. */ if (!path_is_absolute(path)) { - pam_syslog(pamh, LOG_ERR, "Provided runtime directory '%s' is not absolute.", path); + sym_pam_syslog(pamh, LOG_ERR, "Provided runtime directory '%s' is not absolute.", path); goto fail; } @@ -620,19 +619,19 @@ static bool validate_runtime_directory(pam_handle_t *pamh, const char *path, uid } if (!S_ISDIR(st.st_mode)) { - pam_syslog(pamh, LOG_ERR, "Runtime directory '%s' is not actually a directory.", path); + sym_pam_syslog(pamh, LOG_ERR, "Runtime directory '%s' is not actually a directory.", path); goto fail; } if (st.st_uid != uid) { - pam_syslog(pamh, LOG_ERR, "Runtime directory '%s' is not owned by UID " UID_FMT ", as it should.", path, uid); + sym_pam_syslog(pamh, LOG_ERR, "Runtime directory '%s' is not owned by UID " UID_FMT ", as it should.", path, uid); goto fail; } return true; fail: - pam_syslog(pamh, LOG_WARNING, "Not setting $XDG_RUNTIME_DIR, as the directory is not in order."); + sym_pam_syslog(pamh, LOG_WARNING, "Not setting $XDG_RUNTIME_DIR, as the directory is not in order."); return false; } @@ -642,7 +641,7 @@ static int pam_putenv_and_log(pam_handle_t *pamh, const char *e, bool debug) { assert(pamh); assert(e); - r = pam_putenv(pamh, e); + r = sym_pam_putenv(pamh, e); if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to set PAM environment variable %s: @PAMERR@", e); @@ -992,7 +991,7 @@ static void session_context_mangle( c->area = ur->default_area; if (!isempty(c->area) && !filename_is_valid(c->area)) { - pam_syslog(pamh, LOG_WARNING, "Specified area '%s' is not a valid filename, ignoring area request.", c->area); + sym_pam_syslog(pamh, LOG_WARNING, "Specified area '%s' is not a valid filename, ignoring area request.", c->area); c->area = NULL; } @@ -1048,7 +1047,7 @@ static void session_context_mangle( if (streq(c->class, "user")) c->class = "user-incomplete"; else - pam_syslog(pamh, LOG_WARNING, "PAM session of class '%s' is incomplete, which is not supported, ignoring.", c->class); + sym_pam_syslog(pamh, LOG_WARNING, "PAM session of class '%s' is incomplete, which is not supported, ignoring.", c->class); } c->remote = !isempty(c->remote_host) && !is_localhost(c->remote_host); @@ -1253,8 +1252,7 @@ static int register_session( return PAM_SUCCESS; } - pam_syslog(pamh, LOG_ERR, - "Failed to create session: %s", bus_error_message(&error, r)); + sym_pam_syslog(pamh, LOG_ERR, "Failed to create session: %s", bus_error_message(&error, r)); return PAM_SESSION_ERR; } @@ -1282,7 +1280,7 @@ static int register_session( if (fd < 0) return pam_syslog_errno(pamh, LOG_ERR, errno, "Failed to dup session fd: %m"); - r = pam_set_data(pamh, "systemd.session-fd", FD_TO_PTR(fd), NULL); + r = sym_pam_set_data(pamh, "systemd.session-fd", FD_TO_PTR(fd), NULL); if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to install session fd: @PAMERR@"); TAKE_FD(fd); @@ -1580,7 +1578,7 @@ static int setup_environment( pam_syslog_errno(pamh, LOG_WARNING, r, "Path '%s' of requested user area '%s' is not accessible, reverting to regular home directory: %m", j, area); /* Also tell the user directly at login, but a bit more vague */ - pam_info(pamh, "Path '%s' of requested user area '%s' is not accessible, reverting to regular home directory.", j, area); + sym_pam_info(pamh, "Path '%s' of requested user area '%s' is not accessible, reverting to regular home directory.", j, area); area = NULL; } else { /* Validate that the target is definitely owned by user */ @@ -1589,10 +1587,10 @@ static int setup_environment( return pam_syslog_errno(pamh, LOG_ERR, errno, "Unable to fstat() target area directory '%s': %m", ha); if (st.st_uid != ur->uid) { - pam_syslog(pamh, LOG_ERR, "Path '%s' of requested user area '%s' is not owned by user, reverting to regular home directory.", ha, area); + sym_pam_syslog(pamh, LOG_ERR, "Path '%s' of requested user area '%s' is not owned by user, reverting to regular home directory.", ha, area); /* Also tell the user directly at login. */ - pam_info(pamh, "Path '%s' of requested user area '%s' is not owned by user, reverting to regular home directory.", ha, area); + sym_pam_info(pamh, "Path '%s' of requested user area '%s' is not owned by user, reverting to regular home directory.", ha, area); area = NULL; } else { /* All good, now make a copy of the area string, since we quite likely are @@ -1631,13 +1629,13 @@ static int open_osc_context(pam_handle_t *pamh, const char *session_type, UserRe if (!streq_ptr(session_type, "tty")) return PAM_SUCCESS; - const char *e = pam_getenv(pamh, "TERM"); + const char *e = sym_pam_getenv(pamh, "TERM"); if (!e) e = getenv("TERM"); if (streq_ptr(e, "dumb")) return PAM_SUCCESS; - /* NB: we output directly to stdout, instead of going via pam_info() or so, because that's too + /* NB: we output directly to stdout, instead of going via sym_pam_info() or so, because that's too * high-level for us, as it suffixes the output with a newline, expecting a full blown text message * as prompt string, not just an ANSI sequence. Note that PAM's conv_misc() actually goes to stdout * anyway, hence let's do so here too, but only after careful validation. */ @@ -1657,7 +1655,7 @@ static int open_osc_context(pam_handle_t *pamh, const char *session_type, UserRe sd_id128_t osc_id; r = osc_context_open_session( ur->user_name, - pam_getenv(pamh, "XDG_SESSION_ID"), + sym_pam_getenv(pamh, "XDG_SESSION_ID"), &osc, &osc_id); if (r < 0) @@ -1672,7 +1670,7 @@ static int open_osc_context(pam_handle_t *pamh, const char *session_type, UserRe if (!osc_id_copy) return pam_log_oom(pamh); - r = pam_set_data(pamh, "systemd.osc-context-id", osc_id_copy, pam_cleanup_free); + r = sym_pam_set_data(pamh, "systemd.osc-context-id", osc_id_copy, pam_cleanup_free); if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to set PAM OSC sequence ID data: @PAMERR@"); @@ -1680,7 +1678,7 @@ static int open_osc_context(pam_handle_t *pamh, const char *session_type, UserRe TAKE_PTR(osc_id_copy); if (tty_opath_fd >= 0) { - r = pam_set_data(pamh, "systemd.osc-context-fd", FD_TO_PTR(tty_opath_fd), pam_cleanup_close); + r = sym_pam_set_data(pamh, "systemd.osc-context-fd", FD_TO_PTR(tty_opath_fd), pam_cleanup_close); if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to set PAM OSC sequence fd data: @PAMERR@"); @@ -1698,7 +1696,7 @@ static int close_osc_context(pam_handle_t *pamh, bool debug) { const void *p; int tty_opath_fd = -EBADF; - r = pam_get_data(pamh, "systemd.osc-context-fd", &p); + r = sym_pam_get_data(pamh, "systemd.osc-context-fd", &p); if (r == PAM_SUCCESS) tty_opath_fd = PTR_TO_FD(p); else if (r != PAM_NO_MODULE_DATA) @@ -1707,7 +1705,7 @@ static int close_osc_context(pam_handle_t *pamh, bool debug) { return PAM_SUCCESS; const sd_id128_t *osc_id = NULL; - r = pam_get_data(pamh, "systemd.osc-context-id", (const void**) &osc_id); + r = sym_pam_get_data(pamh, "systemd.osc-context-id", (const void**) &osc_id); if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to get PAM OSC context id data: @PAMERR@"); if (!osc_id) @@ -1721,7 +1719,7 @@ static int close_osc_context(pam_handle_t *pamh, bool debug) { } /* /bin/login calls us with fds 0, 1, 2 closed, which is just weird. Let's step outside of that - * range, just in case pam_syslog() or so logs to stderr */ + * range, just in case sym_pam_syslog() or so logs to stderr */ fd = fd_move_above_stdio(fd); /* Safety check, let's verify this is a valid TTY we just opened */ @@ -1849,6 +1847,10 @@ _public_ PAM_EXTERN int pam_sm_close_session( assert(pamh); + r = dlopen_libpam(LOG_DEBUG); + if (r < 0) + return PAM_SERVICE_ERR; + pam_log_setup(); if (parse_argv(pamh, @@ -1866,7 +1868,7 @@ _public_ PAM_EXTERN int pam_sm_close_session( (void) close_osc_context(pamh, debug); - id = pam_getenv(pamh, "XDG_SESSION_ID"); + id = sym_pam_getenv(pamh, "XDG_SESSION_ID"); if (id) { _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL; bool done = false; diff --git a/src/login/pam_systemd_loadkey.c b/src/login/pam_systemd_loadkey.c index 93ccddc25b6..9d44863b02b 100644 --- a/src/login/pam_systemd_loadkey.c +++ b/src/login/pam_systemd_loadkey.c @@ -41,7 +41,7 @@ _public_ PAM_EXTERN int pam_sm_authenticate( else if (streq(argv[i], "debug")) debug = true; else - pam_syslog(pamh, LOG_WARNING, "Unknown parameter '%s', ignoring.", argv[i]); + sym_pam_syslog(pamh, LOG_WARNING, "Unknown parameter '%s', ignoring.", argv[i]); } pam_debug_syslog(pamh, debug, "pam-systemd-loadkey: initializing..."); @@ -81,7 +81,7 @@ _public_ PAM_EXTERN int pam_sm_authenticate( } else if (passwords_len > 1) pam_debug_syslog(pamh, debug, "Multiple passwords found in the key. Using the last one."); - r = pam_set_item(pamh, PAM_AUTHTOK, passwords[passwords_len - 1]); + r = sym_pam_set_item(pamh, PAM_AUTHTOK, passwords[passwords_len - 1]); if (r != PAM_SUCCESS) return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to set PAM auth token: @PAMERR@"); diff --git a/src/shared/pam-util.c b/src/shared/pam-util.c index 9728c3e00da..37f2caf31e6 100644 --- a/src/shared/pam-util.c +++ b/src/shared/pam-util.c @@ -25,10 +25,15 @@ static void *libpam_dl = NULL; DLSYM_PROTOTYPE(pam_acct_mgmt) = NULL; DLSYM_PROTOTYPE(pam_close_session) = NULL; DLSYM_PROTOTYPE(pam_end) = NULL; +DLSYM_PROTOTYPE(pam_get_authtok_noverify) = NULL; +DLSYM_PROTOTYPE(pam_get_authtok_verify) = NULL; DLSYM_PROTOTYPE(pam_get_data) = NULL; DLSYM_PROTOTYPE(pam_get_item) = NULL; +DLSYM_PROTOTYPE(pam_get_user) = NULL; +DLSYM_PROTOTYPE(pam_getenv) = NULL; DLSYM_PROTOTYPE(pam_getenvlist) = NULL; DLSYM_PROTOTYPE(pam_open_session) = NULL; +DLSYM_PROTOTYPE(pam_prompt) = NULL; DLSYM_PROTOTYPE(pam_putenv) = NULL; DLSYM_PROTOTYPE(pam_set_data) = NULL; DLSYM_PROTOTYPE(pam_set_item) = NULL; @@ -361,6 +366,19 @@ int pam_prompt_graceful(pam_handle_t *pamh, int style, char **ret_response, cons return PAM_SUCCESS; } +int pam_putenv_assign(pam_handle_t *pamh, const char *name, const char *value) { + _cleanup_(erase_and_freep) char *s = NULL; + + assert(pamh); + assert(name); + assert(value); + + if (asprintf(&s, "%s=%s", name, value) < 0) + return PAM_BUF_ERR; + + return sym_pam_putenv(pamh, s); +} + #endif int dlopen_libpam(int log_level) { @@ -378,10 +396,15 @@ int dlopen_libpam(int log_level) { DLSYM_ARG(pam_acct_mgmt), DLSYM_ARG(pam_close_session), DLSYM_ARG(pam_end), + DLSYM_ARG(pam_get_authtok_noverify), + DLSYM_ARG(pam_get_authtok_verify), DLSYM_ARG(pam_get_data), DLSYM_ARG(pam_get_item), + DLSYM_ARG(pam_get_user), + DLSYM_ARG(pam_getenv), DLSYM_ARG(pam_getenvlist), DLSYM_ARG(pam_open_session), + DLSYM_ARG(pam_prompt), DLSYM_ARG(pam_putenv), DLSYM_ARG(pam_set_data), DLSYM_ARG(pam_set_item), diff --git a/src/shared/pam-util.h b/src/shared/pam-util.h index 96d522d8f9b..ca3ed0c7c88 100644 --- a/src/shared/pam-util.h +++ b/src/shared/pam-util.h @@ -6,7 +6,7 @@ #if HAVE_PAM #include #include -#include /* IWYU pragma: export */ +#include /* IWYU pragma: export */ #include #include "dlfcn-util.h" @@ -14,10 +14,15 @@ extern DLSYM_PROTOTYPE(pam_acct_mgmt); extern DLSYM_PROTOTYPE(pam_close_session); extern DLSYM_PROTOTYPE(pam_end); +extern DLSYM_PROTOTYPE(pam_get_authtok_noverify); +extern DLSYM_PROTOTYPE(pam_get_authtok_verify); extern DLSYM_PROTOTYPE(pam_get_data); extern DLSYM_PROTOTYPE(pam_get_item); +extern DLSYM_PROTOTYPE(pam_get_user); +extern DLSYM_PROTOTYPE(pam_getenv); extern DLSYM_PROTOTYPE(pam_getenvlist); extern DLSYM_PROTOTYPE(pam_open_session); +extern DLSYM_PROTOTYPE(pam_prompt); extern DLSYM_PROTOTYPE(pam_putenv); extern DLSYM_PROTOTYPE(pam_set_data); extern DLSYM_PROTOTYPE(pam_set_item); @@ -27,6 +32,11 @@ extern DLSYM_PROTOTYPE(pam_strerror); extern DLSYM_PROTOTYPE(pam_syslog); extern DLSYM_PROTOTYPE(pam_vsyslog); +/* sym_pam_prompt() replacement for the pam_info() macro from , which expands to a direct + * pam_prompt() call. */ +#define sym_pam_info(pamh, fmt, ...) \ + sym_pam_prompt((pamh), PAM_TEXT_INFO, NULL, (fmt), ## __VA_ARGS__) + void pam_log_setup(void); int errno_to_pam_error(int error) _const_; @@ -90,6 +100,10 @@ int pam_get_data_many_internal(pam_handle_t *pamh, ...) _sentinel_; int pam_prompt_graceful(pam_handle_t *pamh, int style, char **ret_response, const char *fmt, ...) _printf_(4,5); +/* Equivalent of pam_misc_setenv(pamh, name, value, 0), without the libpam_misc dep — builds "name=value" + * and hands it to sym_pam_putenv(), then erases the buffer before freeing in case it carried a secret. */ +int pam_putenv_assign(pam_handle_t *pamh, const char *name, const char *value); + #endif int dlopen_libpam(int log_level);