pam_systemd_home: support login with alias names + user names with realms
This in particular makes sure that we normalize the user name and update it in the PAM session, once we acquire it. This means that if you have a user with name "a" and alias "b", and the user logs in as "b" they end up properly with "a" as user name set, as intended by the PAM gods. Moreover, if you have a user "c" in a ralm "d", they may log in by specifying "c" or "c@d", with equivalent results.
This commit is contained in:
+54
-36
@@ -102,11 +102,6 @@ static int acquire_user_record(
|
||||
UserRecord **ret_record,
|
||||
PamBusData **bus_data) {
|
||||
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
|
||||
_cleanup_free_ char *homed_field = NULL;
|
||||
const char *json = NULL;
|
||||
int r;
|
||||
|
||||
assert(handle);
|
||||
@@ -124,13 +119,19 @@ static int acquire_user_record(
|
||||
if (STR_IN_SET(username, "root", NOBODY_USER_NAME) || !valid_user_group_name(username, 0))
|
||||
return PAM_USER_UNKNOWN;
|
||||
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
|
||||
const char *json = NULL;
|
||||
bool fresh_data;
|
||||
|
||||
/* We cache the user record in the PAM context. We use a field name that includes the username, since
|
||||
* clients might change the user name associated with a PAM context underneath us. Notably, 'sudo'
|
||||
* creates a single PAM context and first authenticates it with the user set to the originating user,
|
||||
* then updates the user for the destination user and issues the session stack with the same PAM
|
||||
* context. We thus must be prepared that the user record changes between calls and we keep any
|
||||
* caching separate. */
|
||||
homed_field = strjoin("systemd-home-user-record-", username);
|
||||
_cleanup_free_ char *homed_field = strjoin("systemd-home-user-record-", username);
|
||||
if (!homed_field)
|
||||
return pam_log_oom(handle);
|
||||
|
||||
@@ -143,9 +144,10 @@ static int acquire_user_record(
|
||||
* negative cache indicator) */
|
||||
if (json == POINTER_MAX)
|
||||
return PAM_USER_UNKNOWN;
|
||||
|
||||
fresh_data = false;
|
||||
} else {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_free_ char *generic_field = NULL, *json_copy = NULL;
|
||||
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
|
||||
|
||||
r = pam_acquire_bus_connection(handle, "pam-systemd-home", debug, &bus, bus_data);
|
||||
@@ -177,37 +179,10 @@ static int acquire_user_record(
|
||||
if (r < 0)
|
||||
return pam_bus_log_parse_error(handle, r);
|
||||
|
||||
/* First copy: for the homed-specific data field, i.e. where we know the user record is from
|
||||
* homed */
|
||||
json_copy = strdup(json);
|
||||
if (!json_copy)
|
||||
return pam_log_oom(handle);
|
||||
|
||||
r = pam_set_data(handle, homed_field, json_copy, pam_cleanup_free);
|
||||
if (r != PAM_SUCCESS)
|
||||
return pam_syslog_pam_error(handle, LOG_ERR, r,
|
||||
"Failed to set PAM user record data '%s': @PAMERR@", homed_field);
|
||||
|
||||
/* Take a second copy: for the generic data field, the one which we share with
|
||||
* pam_systemd. While we insist on only reusing homed records, pam_systemd is fine with homed
|
||||
* and non-homed user records. */
|
||||
json_copy = strdup(json);
|
||||
if (!json_copy)
|
||||
return pam_log_oom(handle);
|
||||
|
||||
generic_field = strjoin("systemd-user-record-", username);
|
||||
if (!generic_field)
|
||||
return pam_log_oom(handle);
|
||||
|
||||
r = pam_set_data(handle, generic_field, json_copy, pam_cleanup_free);
|
||||
if (r != PAM_SUCCESS)
|
||||
return pam_syslog_pam_error(handle, LOG_ERR, r,
|
||||
"Failed to set PAM user record data '%s': @PAMERR@", generic_field);
|
||||
|
||||
TAKE_PTR(json_copy);
|
||||
fresh_data = true;
|
||||
}
|
||||
|
||||
r = sd_json_parse(json, SD_JSON_PARSE_SENSITIVE, &v, NULL, NULL);
|
||||
r = sd_json_parse(json, /* flags= */ 0, &v, NULL, NULL);
|
||||
if (r < 0)
|
||||
return pam_syslog_errno(handle, LOG_ERR, r, "Failed to parse JSON user record: %m");
|
||||
|
||||
@@ -224,6 +199,49 @@ static int acquire_user_record(
|
||||
return pam_syslog_pam_error(handle, 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
|
||||
* 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(handle, PAM_USER, ur->user_name);
|
||||
if (r != PAM_SUCCESS)
|
||||
return pam_syslog_pam_error(handle, LOG_ERR, r,
|
||||
"Failed to update username PAM item to '%s': @PAMERR@", ur->user_name);
|
||||
|
||||
/* Everything seems to be good, let's cache this data now */
|
||||
if (fresh_data) {
|
||||
/* First copy: for the homed-specific data field, i.e. where we know the user record is from
|
||||
* homed */
|
||||
_cleanup_free_ char *json_copy = strdup(json);
|
||||
if (!json_copy)
|
||||
return pam_log_oom(handle);
|
||||
|
||||
r = pam_set_data(handle, homed_field, json_copy, pam_cleanup_free);
|
||||
if (r != PAM_SUCCESS)
|
||||
return pam_syslog_pam_error(handle, LOG_ERR, r,
|
||||
"Failed to set PAM user record data '%s': @PAMERR@", homed_field);
|
||||
|
||||
/* Take a second copy: for the generic data field, the one which we share with
|
||||
* pam_systemd. While we insist on only reusing homed records, pam_systemd is fine with homed
|
||||
* and non-homed user records. */
|
||||
json_copy = strdup(json);
|
||||
if (!json_copy)
|
||||
return pam_log_oom(handle);
|
||||
|
||||
_cleanup_free_ char *generic_field = strjoin("systemd-user-record-", username);
|
||||
if (!generic_field)
|
||||
return pam_log_oom(handle);
|
||||
|
||||
r = pam_set_data(handle, generic_field, json_copy, pam_cleanup_free);
|
||||
if (r != PAM_SUCCESS)
|
||||
return pam_syslog_pam_error(handle, LOG_ERR, r,
|
||||
"Failed to set PAM user record data '%s': @PAMERR@", generic_field);
|
||||
|
||||
TAKE_PTR(json_copy);
|
||||
}
|
||||
|
||||
if (ret_record)
|
||||
*ret_record = TAKE_PTR(ur);
|
||||
|
||||
|
||||
@@ -177,13 +177,11 @@ static int acquire_user_record(
|
||||
pam_handle_t *handle,
|
||||
UserRecord **ret_record) {
|
||||
|
||||
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
|
||||
const char *username = NULL, *json = NULL;
|
||||
_cleanup_free_ char *field = NULL;
|
||||
int r;
|
||||
|
||||
assert(handle);
|
||||
|
||||
const char *username = NULL;
|
||||
r = pam_get_user(handle, &username, NULL);
|
||||
if (r != PAM_SUCCESS)
|
||||
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get user name: @PAMERR@");
|
||||
@@ -192,10 +190,12 @@ static int acquire_user_record(
|
||||
|
||||
/* If pam_systemd_homed (or some other module) already acquired the user record we can reuse it
|
||||
* here. */
|
||||
field = strjoin("systemd-user-record-", username);
|
||||
_cleanup_free_ char *field = strjoin("systemd-user-record-", username);
|
||||
if (!field)
|
||||
return pam_log_oom(handle);
|
||||
|
||||
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
|
||||
const char *json = NULL;
|
||||
r = pam_get_data(handle, field, (const void**) &json);
|
||||
if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA))
|
||||
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM user record data: @PAMERR@");
|
||||
|
||||
Reference in New Issue
Block a user