]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
tree-wide: dlopen libpam in pam plugins
authorDaan De Meyer <daan@amutable.com>
Fri, 15 May 2026 14:54:04 +0000 (14:54 +0000)
committerDaan De Meyer <daan@amutable.com>
Mon, 18 May 2026 21:17:38 +0000 (21:17 +0000)
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.

meson.build
src/home/meson.build
src/home/pam_systemd_home.c
src/login/pam_systemd.c
src/login/pam_systemd_loadkey.c
src/shared/pam-util.c
src/shared/pam-util.h

index c69b96e1c6d5067039d3a206d07389cb9b8022ce..92129bee7d77bc4fb456279fc1e56dc7d85a3a91 100644 (file)
@@ -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,
index 600f00b4ac9978253baf55da65c63056c30a1d01..631eeb13aa879890aba329cf3458a7de0fa10150 100644 (file)
@@ -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',
index 1de66b4c0325ccc9fc22119580a5d63249a39892..5fe6dcbec20fc24c4711aeff68bf1e0b759bf758 100644 (file)
@@ -1,7 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include <libintl.h>
-#include <security/pam_misc.h>
 
 #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@");
 
index 4e571e705655d0a586926de82cce23e024e441a3..1b0adfae9ea823d6de3094994ebd8d39c25cea2f 100644 (file)
@@ -3,7 +3,6 @@
 #include <endian.h>
 #include <fcntl.h>
 #include <pwd.h>
-#include <security/pam_misc.h>
 #include <sys/file.h>
 #include <sys/stat.h>
 #include <sys/sysmacros.h>
@@ -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;
index 93ccddc25b6a74462653456c34898e413af33d2a..9d44863b02bd1eccf325eaed43897a677943df35 100644 (file)
@@ -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@");
 
index 9728c3e00da071ee86b35e1f249bd5c6854b59e7..37f2caf31e6f3c79ce0e38ce3c9f0c86f725c13e 100644 (file)
@@ -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),
index 96d522d8f9bfb1a3541dd0c175c5b2ff1abc4582..ca3ed0c7c88a3a319c49adf8eb9a0afbdfd236ca 100644 (file)
@@ -6,7 +6,7 @@
 #if HAVE_PAM
 #include <security/pam_appl.h>
 #include <security/pam_ext.h>
-#include <security/pam_modules.h> /* IWYU pragma: export */
+#include <security/pam_modules.h>       /* IWYU pragma: export */
 #include <syslog.h>
 
 #include "dlfcn-util.h"
 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 <security/pam_ext.h>, 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);