# 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',
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,
'sources' : pam_systemd_home_sources,
'dependencies' : [
libintl,
- libpam_misc,
- libpam,
+ libpam_cflags,
threads,
],
'version-script' : meson.current_source_dir() / 'pam_systemd_home.sym',
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <libintl.h>
-#include <security/pam_misc.h>
#include "sd-bus.h"
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);
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;
* 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. */
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);
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))
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) {
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);
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);
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);
/* 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);
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@",
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);
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);
* 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))
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@");
/* 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@");
(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.");
/* 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);
* 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;
}
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@");
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@");
bool debug = false;
int r;
+ r = dlopen_libpam(LOG_DEBUG);
+ if (r < 0)
+ return PAM_SERVICE_ERR;
+
pam_log_setup();
if (parse_argv(pamh,
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))
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;
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:
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:
/* 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@");
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@");
#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>
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;
}
} 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;
} 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;
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))
_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) {
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);
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;
}
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;
}
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;
}
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;
}
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;
}
* 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;
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;
}
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;
}
* 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);
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);
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);
* 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;
}
}
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;
}
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);
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;
}
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);
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;
}
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);
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 */
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
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. */
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)
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@");
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@");
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)
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)
}
/* /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 */
assert(pamh);
+ r = dlopen_libpam(LOG_DEBUG);
+ if (r < 0)
+ return PAM_SERVICE_ERR;
+
pam_log_setup();
if (parse_argv(pamh,
(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;
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...");
} 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@");
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;
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) {
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),
#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);
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_;
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);