pam_systemd_home: also store acquirement fd per user

We might pin a home through authentication and a different one through a
session, all from the same PAM context, like sudo does. Hence also store
the referencing fd keyed by the user name.
This commit is contained in:
Lennart Poettering 2020-05-07 11:01:42 +02:00
parent dbe7fff476
commit 6c8428bb8b
1 changed files with 68 additions and 27 deletions

View File

@ -62,27 +62,30 @@ static int parse_argv(
static int acquire_user_record(
pam_handle_t *handle,
const char *username,
UserRecord **ret_record) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
const char *username = NULL, *json = NULL;
_cleanup_free_ char *homed_field = NULL;
const char *json = NULL;
int r;
assert(handle);
r = pam_get_user(handle, &username, NULL);
if (r != PAM_SUCCESS) {
pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r));
return r;
}
if (!username) {
r = pam_get_user(handle, &username, NULL);
if (r != PAM_SUCCESS) {
pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r));
return r;
}
if (isempty(username)) {
pam_syslog(handle, LOG_ERR, "User name not set.");
return PAM_SERVICE_ERR;
if (isempty(username)) {
pam_syslog(handle, LOG_ERR, "User name not set.");
return PAM_SERVICE_ERR;
}
}
/* Let's bypass all IPC complexity for the two user names we know for sure we don't manage, and for
@ -418,7 +421,9 @@ static int acquire_home(
bool do_auth = please_authenticate, home_not_active = false, home_locked = false;
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
_cleanup_close_ int acquired_fd = -1;
_cleanup_free_ char *fd_field = NULL;
const void *home_fd_ptr = NULL;
const char *username = NULL;
unsigned n_attempts = 0;
int r;
@ -432,8 +437,27 @@ static int acquire_home(
* authenticates, while the other PAM hooks unset it so that they can a ref of their own without
* authentication if possible, but with authentication if necessary. */
r = pam_get_user(handle, &username, NULL);
if (r != PAM_SUCCESS) {
pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r));
return r;
}
if (isempty(username)) {
pam_syslog(handle, LOG_ERR, "User name not set.");
return PAM_SERVICE_ERR;
}
/* If we already have acquired the fd, let's shortcut this */
r = pam_get_data(handle, "systemd-home-fd", &home_fd_ptr);
fd_field = strjoin("systemd-home-fd-", username);
if (!fd_field)
return pam_log_oom(handle);
r = pam_get_data(handle, fd_field, &home_fd_ptr);
if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
pam_syslog(handle, LOG_ERR, "Failed to retrieve PAM home reference fd: %s", pam_strerror(handle, r));
return r;
}
if (r == PAM_SUCCESS && PTR_TO_FD(home_fd_ptr) >= 0)
return PAM_SUCCESS;
@ -441,7 +465,7 @@ static int acquire_home(
if (r != PAM_SUCCESS)
return r;
r = acquire_user_record(handle, &ur);
r = acquire_user_record(handle, username, &ur);
if (r != PAM_SUCCESS)
return r;
@ -557,7 +581,7 @@ static int acquire_home(
do_auth = true;
}
r = pam_set_data(handle, "systemd-home-fd", FD_TO_PTR(acquired_fd), cleanup_home_fd);
r = pam_set_data(handle, fd_field, FD_TO_PTR(acquired_fd), cleanup_home_fd);
if (r < 0) {
pam_syslog(handle, LOG_ERR, "Failed to set PAM bus data: %s", pam_strerror(handle, r));
return r;
@ -578,15 +602,27 @@ static int acquire_home(
return PAM_SUCCESS;
}
static int release_home_fd(pam_handle_t *handle) {
static int release_home_fd(pam_handle_t *handle, const char *username) {
_cleanup_free_ char *fd_field = NULL;
const void *home_fd_ptr = NULL;
int r;
r = pam_get_data(handle, "systemd-home-fd", &home_fd_ptr);
if (r == PAM_NO_MODULE_DATA || PTR_TO_FD(home_fd_ptr) < 0)
return PAM_NO_MODULE_DATA;
assert(handle);
assert(username);
r = pam_set_data(handle, "systemd-home-fd", NULL, NULL);
fd_field = strjoin("systemd-home-fd-", username);
if (!fd_field)
return pam_log_oom(handle);
r = pam_get_data(handle, 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) {
pam_syslog(handle, LOG_ERR, "Failed to retrieve PAM home reference fd: %s", pam_strerror(handle, r));
return r;
}
r = pam_set_data(handle, fd_field, NULL, NULL);
if (r != PAM_SUCCESS)
pam_syslog(handle, LOG_ERR, "Failed to release PAM home reference fd: %s", pam_strerror(handle, r));
@ -673,20 +709,25 @@ _public_ PAM_EXTERN int pam_sm_close_session(
if (debug)
pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed session end");
/* Let's explicitly drop the reference to the homed session, so that the subsequent ReleaseHome()
* call will be able to do its thing. */
r = release_home_fd(handle);
if (r == PAM_NO_MODULE_DATA) /* Nothing to do, we never acquired an fd */
return PAM_SUCCESS;
if (r != PAM_SUCCESS)
return r;
r = pam_get_user(handle, &username, NULL);
if (r != PAM_SUCCESS) {
pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r));
return r;
}
if (isempty(username)) {
pam_syslog(handle, LOG_ERR, "User name not set.");
return PAM_SERVICE_ERR;
}
/* Let's explicitly drop the reference to the homed session, so that the subsequent ReleaseHome()
* call will be able to do its thing. */
r = release_home_fd(handle, username);
if (r == PAM_NO_MODULE_DATA) /* Nothing to do, we never acquired an fd */
return PAM_SUCCESS;
if (r != PAM_SUCCESS)
return r;
r = pam_acquire_bus_connection(handle, &bus);
if (r != PAM_SUCCESS)
return r;
@ -738,7 +779,7 @@ _public_ PAM_EXTERN int pam_sm_acct_mgmt(
if (r != PAM_SUCCESS)
return r;
r = acquire_user_record(handle, &ur);
r = acquire_user_record(handle, NULL, &ur);
if (r != PAM_SUCCESS)
return r;
@ -846,7 +887,7 @@ _public_ PAM_EXTERN int pam_sm_chauthtok(
if (r != PAM_SUCCESS)
return r;
r = acquire_user_record(handle, &ur);
r = acquire_user_record(handle, NULL, &ur);
if (r != PAM_SUCCESS)
return r;