mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 00:14:32 +09:00
pid1: when a password is requested during PAMName= processing, query it via the ask-password logic
This commit is contained in:
committed by
Luca Boccassi
parent
f55a6fc1e3
commit
8af1b296cb
@@ -770,6 +770,15 @@
|
||||
changes in the original unit through notification messages. These messages will be considered belonging to the
|
||||
session scope unit and not the original unit. It is hence not recommended to use <varname>PAMName=</varname> in
|
||||
combination with <varname>NotifyAccess=</varname><option>all</option>.</para>
|
||||
|
||||
<para>If a PAM module interactively requests input (a password or suchlike) it will be attempted to
|
||||
be read from a service credential (as configured via <varname>SetCredential=</varname>,
|
||||
<varname>ImportCredential=</varname> and related calls) under the name
|
||||
<varname>pam.authtok.<replaceable>pamservice</replaceable></varname>, where
|
||||
<replaceable>pamservice</replaceable> is replaced by the PAM service name as configured with
|
||||
<varname>PAMName=</varname>. (Note that the credential remains accessible for the runtime of the
|
||||
service!) If no matching credential is set, the user is prompted for it interactively via the <ulink
|
||||
url="https://systemd.io/PASSWORD_AGENTS">Password Agent</ulink> logic.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "apparmor-util.h"
|
||||
#endif
|
||||
#include "argv-util.h"
|
||||
#include "ask-password-api.h"
|
||||
#include "barrier.h"
|
||||
#include "bitfield.h"
|
||||
#include "bpf-dlopen.h"
|
||||
@@ -1048,15 +1049,114 @@ static int enforce_user(
|
||||
|
||||
#if HAVE_PAM
|
||||
|
||||
static int null_conv(
|
||||
static void pam_response_free_array(struct pam_response *responses, size_t n_responses) {
|
||||
assert(responses || n_responses == 0);
|
||||
|
||||
FOREACH_ARRAY(resp, responses, n_responses)
|
||||
erase_and_free(resp->resp);
|
||||
|
||||
free(responses);
|
||||
}
|
||||
|
||||
typedef struct AskPasswordConvData {
|
||||
const ExecContext *context;
|
||||
const ExecParameters *params;
|
||||
} AskPasswordConvData;
|
||||
|
||||
static int ask_password_conv(
|
||||
int num_msg,
|
||||
const struct pam_message **msg,
|
||||
struct pam_response **resp,
|
||||
void *appdata_ptr) {
|
||||
const struct pam_message *msg[],
|
||||
struct pam_response **ret,
|
||||
void *userdata) {
|
||||
|
||||
/* We don't support conversations */
|
||||
AskPasswordConvData *data = ASSERT_PTR(userdata);
|
||||
bool set_credential_env_var = false;
|
||||
int r;
|
||||
|
||||
return PAM_CONV_ERR;
|
||||
assert(num_msg >= 0);
|
||||
assert(msg);
|
||||
assert(data->context);
|
||||
assert(data->params);
|
||||
|
||||
size_t n = num_msg;
|
||||
struct pam_response *responses = new0(struct pam_response, n);
|
||||
if (!responses)
|
||||
return PAM_BUF_ERR;
|
||||
CLEANUP_ARRAY(responses, n, pam_response_free_array);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
const struct pam_message *mi = *msg + i;
|
||||
|
||||
switch (mi->msg_style) {
|
||||
|
||||
case PAM_PROMPT_ECHO_ON:
|
||||
case PAM_PROMPT_ECHO_OFF: {
|
||||
|
||||
/* Locally set the $CREDENTIALS_DIRECTORY to the credentials directory we just populated */
|
||||
if (!set_credential_env_var) {
|
||||
_cleanup_free_ char *creds_dir = NULL;
|
||||
r = exec_context_get_credential_directory(data->context, data->params, data->params->unit_id, &creds_dir);
|
||||
if (r < 0)
|
||||
return log_exec_error_errno(data->context, data->params, r, "Failed to determine credentials directory: %m");
|
||||
|
||||
if (creds_dir) {
|
||||
if (setenv("CREDENTIALS_DIRECTORY", creds_dir, /* overwrite= */ true) < 0)
|
||||
return log_exec_error_errno(data->context, data->params, r, "Failed to set $CREDENTIALS_DIRECTORY: %m");
|
||||
} else
|
||||
(void) unsetenv("CREDENTIALS_DIRECTORY");
|
||||
|
||||
set_credential_env_var = true;
|
||||
}
|
||||
|
||||
_cleanup_free_ char *credential_name = strjoin("pam.authtok.", data->context->pam_name);
|
||||
if (!credential_name)
|
||||
return log_oom();
|
||||
|
||||
AskPasswordRequest req = {
|
||||
.message = mi->msg,
|
||||
.credential = credential_name,
|
||||
.tty_fd = -EBADF,
|
||||
.hup_fd = -EBADF,
|
||||
.until = now(CLOCK_MONOTONIC) + 15 * USEC_PER_SEC,
|
||||
};
|
||||
|
||||
_cleanup_(strv_free_erasep) char **acquired = NULL;
|
||||
r = ask_password_auto(
|
||||
&req,
|
||||
ASK_PASSWORD_ACCEPT_CACHED|
|
||||
ASK_PASSWORD_NO_TTY|
|
||||
(mi->msg_style == PAM_PROMPT_ECHO_ON ? ASK_PASSWORD_ECHO : 0),
|
||||
&acquired);
|
||||
if (r < 0) {
|
||||
log_exec_error_errno(data->context, data->params, r, "Failed to query for password: %m");
|
||||
return PAM_CONV_ERR;
|
||||
}
|
||||
|
||||
responses[i].resp = strdup(ASSERT_PTR(acquired[0]));
|
||||
if (!responses[i].resp) {
|
||||
log_oom();
|
||||
return PAM_BUF_ERR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PAM_ERROR_MSG:
|
||||
log_exec_error(data->context, data->params, "PAM: %s", mi->msg);
|
||||
break;
|
||||
|
||||
case PAM_TEXT_INFO:
|
||||
log_exec_info(data->context, data->params, "PAM: %s", mi->msg);
|
||||
break;
|
||||
|
||||
default:
|
||||
return PAM_CONV_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(responses);
|
||||
n = 0;
|
||||
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
static int pam_close_session_and_delete_credentials(pam_handle_t *handle, int flags) {
|
||||
@@ -1074,24 +1174,27 @@ static int pam_close_session_and_delete_credentials(pam_handle_t *handle, int fl
|
||||
|
||||
return r != PAM_SUCCESS ? r : s;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int setup_pam(
|
||||
const char *name,
|
||||
const ExecContext *context,
|
||||
ExecParameters *params,
|
||||
const char *user,
|
||||
uid_t uid,
|
||||
gid_t gid,
|
||||
const char *tty,
|
||||
char ***env, /* updated on success */
|
||||
const int fds[], size_t n_fds,
|
||||
int exec_fd) {
|
||||
|
||||
#if HAVE_PAM
|
||||
AskPasswordConvData conv_data = {
|
||||
.context = context,
|
||||
.params = params,
|
||||
};
|
||||
|
||||
static const struct pam_conv conv = {
|
||||
.conv = null_conv,
|
||||
.appdata_ptr = NULL
|
||||
const struct pam_conv conv = {
|
||||
.conv = ask_password_conv,
|
||||
.appdata_ptr = &conv_data,
|
||||
};
|
||||
|
||||
_cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
|
||||
@@ -1103,8 +1206,12 @@ static int setup_pam(
|
||||
pid_t parent_pid;
|
||||
int flags = 0;
|
||||
|
||||
assert(name);
|
||||
assert(context);
|
||||
assert(params);
|
||||
assert(user);
|
||||
assert(uid_is_valid(uid));
|
||||
assert(gid_is_valid(gid));
|
||||
assert(fds || n_fds == 0);
|
||||
assert(env);
|
||||
|
||||
/* We set up PAM in the parent process, then fork. The child
|
||||
@@ -1121,12 +1228,13 @@ static int setup_pam(
|
||||
if (log_get_max_level() < LOG_DEBUG)
|
||||
flags |= PAM_SILENT;
|
||||
|
||||
pam_code = pam_start(name, user, &conv, &handle);
|
||||
pam_code = pam_start(context->pam_name, user, &conv, &handle);
|
||||
if (pam_code != PAM_SUCCESS) {
|
||||
handle = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
const char *tty = context->tty_path;
|
||||
if (!tty) {
|
||||
_cleanup_free_ char *q = NULL;
|
||||
|
||||
@@ -4976,7 +5084,7 @@ int exec_invoke(
|
||||
* wins here. (See above.) */
|
||||
|
||||
/* All fds passed in the fds array will be closed in the pam child process. */
|
||||
r = setup_pam(context->pam_name, username, uid, gid, context->tty_path, &accum_env, params->fds, n_fds, params->exec_fd);
|
||||
r = setup_pam(context, params, username, uid, gid, &accum_env, params->fds, n_fds, params->exec_fd);
|
||||
if (r < 0) {
|
||||
*exit_status = EXIT_PAM;
|
||||
return log_exec_error_errno(context, params, r, "Failed to set up PAM session: %m");
|
||||
|
||||
Reference in New Issue
Block a user