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.
This commit is contained in:
Daan De Meyer
2026-05-18 21:17:38 +00:00
parent 94d745c42c
commit 62053ee289
7 changed files with 132 additions and 110 deletions
+2 -21
View 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,
+1 -2
View 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',
+38 -35
View 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@");
+51 -49
View 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;
+2 -2
View 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@");
+23
View 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),
+15 -1
View 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"
@@ -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 <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);