1
0
mirror of https://github.com/systemd/systemd.git synced 2024-12-22 17:35:35 +03:00
This commit is contained in:
Lennart Poettering 2024-12-20 22:25:20 +00:00 committed by GitHub
commit aa579d5b9a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
40 changed files with 712 additions and 134 deletions

View File

@ -5,16 +5,23 @@
#include <stddef.h> #include <stddef.h>
#include <unistd.h> #include <unistd.h>
#include "sd-varlink.h"
#include "ask-password-api.h" #include "ask-password-api.h"
#include "build.h" #include "build.h"
#include "bus-polkit.h"
#include "constants.h" #include "constants.h"
#include "json-util.h"
#include "log.h" #include "log.h"
#include "macro.h" #include "macro.h"
#include "main-func.h" #include "main-func.h"
#include "parse-argument.h" #include "parse-argument.h"
#include "pretty-print.h" #include "pretty-print.h"
#include "string-table.h"
#include "strv.h" #include "strv.h"
#include "terminal-util.h" #include "terminal-util.h"
#include "varlink-io.systemd.AskPassword.h"
#include "varlink-util.h"
static const char *arg_icon = NULL; static const char *arg_icon = NULL;
static const char *arg_id = NULL; /* identifier for 'ask-password' protocol */ static const char *arg_id = NULL; /* identifier for 'ask-password' protocol */
@ -26,6 +33,7 @@ static bool arg_multiple = false;
static bool arg_no_output = false; static bool arg_no_output = false;
static AskPasswordFlags arg_flags = ASK_PASSWORD_PUSH_CACHE; static AskPasswordFlags arg_flags = ASK_PASSWORD_PUSH_CACHE;
static bool arg_newline = true; static bool arg_newline = true;
static bool arg_varlink = false;
STATIC_DESTRUCTOR_REGISTER(arg_message, freep); STATIC_DESTRUCTOR_REGISTER(arg_message, freep);
@ -232,9 +240,162 @@ static int parse_argv(int argc, char *argv[]) {
return log_oom(); return log_oom();
} }
r = sd_varlink_invocation(SD_VARLINK_ALLOW_ACCEPT);
if (r < 0)
return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
if (r > 0)
arg_varlink = true;
return 1; return 1;
} }
typedef enum EchoMode {
ECHO_OFF,
ECHO_ON,
ECHO_MASKED,
_ECHO_MODE_MAX,
_ECHO_MODE_INVALID = -EINVAL,
} EchoMode;
static const char* echo_mode_table[_ECHO_MODE_MAX] = {
[ECHO_OFF] = "off",
[ECHO_ON] = "on",
[ECHO_MASKED] = "masked",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(echo_mode, EchoMode, ECHO_ON);
static JSON_DISPATCH_ENUM_DEFINE(dispatch_echo_mode, EchoMode, echo_mode_from_string);
typedef struct MethodAskParameters {
const char *message;
const char *keyring;
const char *icon;
const char *id;
uint64_t timeout_usec;
uint64_t until_usec;
int accept_cached;
int push_cache;
EchoMode echo_mode;
} MethodAskParameters;
static int vl_method_ask(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
static const sd_json_dispatch_field dispatch_table[] = {
{ "message", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MethodAskParameters, message), 0 },
{ "keyname", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MethodAskParameters, keyring), 0 },
{ "icon", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MethodAskParameters, icon), 0 },
{ "id", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MethodAskParameters, id), 0 },
{ "timeoutUSec", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(MethodAskParameters, timeout_usec), 0 },
{ "untilUSec", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(MethodAskParameters, until_usec), 0 },
{ "acceptCached", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(MethodAskParameters, accept_cached), 0 },
{ "pushCache", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(MethodAskParameters, push_cache) , 0 },
{ "echo", SD_JSON_VARIANT_STRING, dispatch_echo_mode, offsetof(MethodAskParameters, echo_mode), 0 },
VARLINK_DISPATCH_POLKIT_FIELD,
{}
};
Hashmap **polkit_registry = ASSERT_PTR(userdata);
MethodAskParameters p = {
.timeout_usec = DEFAULT_TIMEOUT_USEC,
.until_usec = UINT64_MAX,
.accept_cached = -1,
.push_cache = -1,
.echo_mode = _ECHO_MODE_INVALID,
};
int r;
assert(link);
r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
if (r != 0)
return r;
r = varlink_verify_polkit_async_full(
link,
/* bus= */ NULL,
"io.systemd.ask-password.ask",
/* details= */ NULL,
/* good_user= */ FLAGS_SET(arg_flags, ASK_PASSWORD_USER) ? getuid() : UID_INVALID,
/* flags= */ 0,
polkit_registry);
if (r <= 0)
return r;
AskPasswordRequest req = {
.tty_fd = -EBADF,
.message = p.message ?: arg_message,
.icon = p.icon ?: arg_icon,
.id = p.id ?: arg_id,
.keyring = p.keyring ?: arg_key_name,
.credential = arg_credential_name,
.hup_fd = sd_varlink_get_input_fd(link),
};
if (p.timeout_usec != 0 && p.until_usec != 0)
req.until = MIN(usec_add(now(CLOCK_MONOTONIC), p.timeout_usec), p.until_usec);
/* If the timeout is set to zero, don't ask agents, just stick to cache */
SET_FLAG(arg_flags, ASK_PASSWORD_NO_AGENT, req.until == 0);
if (p.accept_cached >= 0)
SET_FLAG(arg_flags, ASK_PASSWORD_ACCEPT_CACHED, p.accept_cached);
if (p.push_cache >= 0)
SET_FLAG(arg_flags, ASK_PASSWORD_PUSH_CACHE, p.push_cache);
if (p.echo_mode >= 0) {
SET_FLAG(arg_flags, ASK_PASSWORD_ECHO, p.echo_mode == ECHO_ON);
SET_FLAG(arg_flags, ASK_PASSWORD_SILENT, p.echo_mode == ECHO_OFF);
}
_cleanup_strv_free_erase_ char **l = NULL;
r = ask_password_auto(&req, arg_flags, &l);
if (r == -EUNATCH)
return sd_varlink_error(link, "io.systemd.AskPassword.NoPasswordAvailable", NULL);
if (r == -ETIME)
return sd_varlink_error(link, "io.systemd.AskPassword.TimeoutReached", NULL);
if (r == -ECONNRESET) { /* POLLHUP on the varlink fd we passed in via .hup_fd */
sd_varlink_close(link);
return 1;
}
if (r < 0)
return r;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *vl = NULL;
r = sd_json_variant_new_array_strv(&vl, l);
if (r < 0)
return r;
sd_json_variant_sensitive(vl);
return sd_varlink_replybo(link, SD_JSON_BUILD_PAIR("passwords", SD_JSON_BUILD_VARIANT(vl)));
}
static int vl_server(void) {
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL;
_cleanup_(hashmap_freep) Hashmap *polkit_registry = NULL;
int r;
r = varlink_server_new(&varlink_server, SD_VARLINK_SERVER_INHERIT_USERDATA, /* userdata= */ &polkit_registry);
if (r < 0)
return log_error_errno(r, "Failed to allocate Varlink server: %m");
r = sd_varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_AskPassword);
if (r < 0)
return log_error_errno(r, "Failed to add Varlink interface: %m");
r = sd_varlink_server_bind_method(varlink_server, "io.systemd.AskPassword.Ask", vl_method_ask);
if (r < 0)
return log_error_errno(r, "Failed to bind Varlink method: %m");
r = sd_varlink_server_loop_auto(varlink_server);
if (r < 0)
return log_error_errno(r, "Failed to run Varlink event loop: %m");
return 0;
}
static int run(int argc, char *argv[]) { static int run(int argc, char *argv[]) {
_cleanup_strv_free_erase_ char **l = NULL; _cleanup_strv_free_erase_ char **l = NULL;
usec_t timeout; usec_t timeout;
@ -249,17 +410,23 @@ static int run(int argc, char *argv[]) {
if (r <= 0) if (r <= 0)
return r; return r;
if (arg_varlink)
return vl_server(); /* Invocation as Varlink service */
timeout = arg_timeout > 0 ? usec_add(now(CLOCK_MONOTONIC), arg_timeout) : 0; timeout = arg_timeout > 0 ? usec_add(now(CLOCK_MONOTONIC), arg_timeout) : 0;
AskPasswordRequest req = { AskPasswordRequest req = {
.tty_fd = -EBADF,
.message = arg_message, .message = arg_message,
.icon = arg_icon, .icon = arg_icon,
.id = arg_id, .id = arg_id,
.keyring = arg_key_name, .keyring = arg_key_name,
.credential = arg_credential_name ?: "password", .credential = arg_credential_name ?: "password",
.until = timeout,
.hup_fd = -EBADF,
}; };
r = ask_password_auto(&req, timeout, arg_flags, &l); r = ask_password_auto(&req, arg_flags, &l);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to query password: %m"); return log_error_errno(r, "Failed to query password: %m");

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?> <!--*-nxml-*-->
<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
"https://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
<!--
SPDX-License-Identifier: LGPL-2.1-or-later
This file is part of systemd.
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
-->
<policyconfig>
<vendor>The systemd Project</vendor>
<vendor_url>https://systemd.io</vendor_url>
<action id="io.systemd.ask-password.ask">
<description gettext-domain="systemd">Allow to query the user interactively for a password</description>
<message gettext-domain="systemd">Authentication is required for an application to ask the user interactively for a password.</message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<allow_active>yes</allow_active>
</defaults>
</action>
</policyconfig>

View File

@ -984,9 +984,12 @@ int verb_install(int argc, char *argv[], void *userdata) {
arg_private_key_source, arg_private_key_source,
arg_private_key, arg_private_key,
&(AskPasswordRequest) { &(AskPasswordRequest) {
.tty_fd = -EBADF,
.id = "bootctl-private-key-pin", .id = "bootctl-private-key-pin",
.keyring = arg_private_key, .keyring = arg_private_key,
.credential = "bootctl.private-key-pin", .credential = "bootctl.private-key-pin",
.until = USEC_INFINITY,
.hup_fd = -EBADF,
}, },
&private_key, &private_key,
&ui); &ui);

View File

@ -55,11 +55,14 @@ int load_volume_key_password(
return log_oom(); return log_oom();
AskPasswordRequest req = { AskPasswordRequest req = {
.tty_fd = -EBADF,
.message = question, .message = question,
.icon = "drive-harddisk", .icon = "drive-harddisk",
.id = id, .id = id,
.keyring = "cryptenroll", .keyring = "cryptenroll",
.credential = "cryptenroll.passphrase", .credential = "cryptenroll.passphrase",
.until = USEC_INFINITY,
.hup_fd = -EBADF,
}; };
for (;;) { for (;;) {
@ -69,7 +72,7 @@ int load_volume_key_password(
return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), return log_error_errno(SYNTHETIC_ERRNO(ENOKEY),
"Too many attempts, giving up."); "Too many attempts, giving up.");
r = ask_password_auto(&req, USEC_INFINITY, ask_password_flags, &passwords); r = ask_password_auto(&req, ask_password_flags, &passwords);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to query password: %m"); return log_error_errno(r, "Failed to query password: %m");
@ -130,10 +133,13 @@ int enroll_password(
return log_oom(); return log_oom();
AskPasswordRequest req = { AskPasswordRequest req = {
.tty_fd = -EBADF,
.icon = "drive-harddisk", .icon = "drive-harddisk",
.id = id, .id = id,
.keyring = "cryptenroll", .keyring = "cryptenroll",
.credential = "cryptenroll.new-passphrase", .credential = "cryptenroll.new-passphrase",
.until = USEC_INFINITY,
.hup_fd = -EBADF,
}; };
for (;;) { for (;;) {
@ -150,7 +156,7 @@ int enroll_password(
req.message = question; req.message = question;
r = ask_password_auto(&req, USEC_INFINITY, /* flags= */ 0, &passwords); r = ask_password_auto(&req, /* flags= */ 0, &passwords);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to query password: %m"); return log_error_errno(r, "Failed to query password: %m");
@ -163,7 +169,7 @@ int enroll_password(
req.message = question; req.message = question;
r = ask_password_auto(&req, USEC_INFINITY, /* flags= */ 0, &passwords2); r = ask_password_auto(&req, /* flags= */ 0, &passwords2);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to query password: %m"); return log_error_errno(r, "Failed to query password: %m");

View File

@ -119,16 +119,18 @@ static int get_pin(char **ret_pin_str, TPM2Flags *ret_flags) {
SYNTHETIC_ERRNO(ENOKEY), "Too many attempts, giving up."); SYNTHETIC_ERRNO(ENOKEY), "Too many attempts, giving up.");
AskPasswordRequest req = { AskPasswordRequest req = {
.tty_fd = -EBADF,
.message = "Please enter TPM2 PIN:", .message = "Please enter TPM2 PIN:",
.icon = "drive-harddisk", .icon = "drive-harddisk",
.keyring = "tpm2-pin", .keyring = "tpm2-pin",
.credential = "cryptenroll.new-tpm2-pin", .credential = "cryptenroll.new-tpm2-pin",
.until = USEC_INFINITY,
.hup_fd = -EBADF,
}; };
pin = strv_free_erase(pin); pin = strv_free_erase(pin);
r = ask_password_auto( r = ask_password_auto(
&req, &req,
/* until= */ USEC_INFINITY,
/* flags= */ 0, /* flags= */ 0,
&pin); &pin);
if (r < 0) if (r < 0)
@ -139,7 +141,6 @@ static int get_pin(char **ret_pin_str, TPM2Flags *ret_flags) {
r = ask_password_auto( r = ask_password_auto(
&req, &req,
USEC_INFINITY,
/* flags= */ 0, /* flags= */ 0,
&pin2); &pin2);
if (r < 0) if (r < 0)

View File

@ -906,17 +906,20 @@ static int get_password(
return log_oom(); return log_oom();
AskPasswordRequest req = { AskPasswordRequest req = {
.tty_fd = -EBADF,
.message = text, .message = text,
.icon = "drive-harddisk", .icon = "drive-harddisk",
.id = id, .id = id,
.keyring = "cryptsetup", .keyring = "cryptsetup",
.credential = "cryptsetup.passphrase", .credential = "cryptsetup.passphrase",
.until = until,
.hup_fd = -EBADF,
}; };
if (ignore_cached) if (ignore_cached)
flags &= ~ASK_PASSWORD_ACCEPT_CACHED; flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
r = ask_password_auto(&req, until, flags, &passwords); r = ask_password_auto(&req, flags, &passwords);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to query password: %m"); return log_error_errno(r, "Failed to query password: %m");
@ -937,7 +940,7 @@ static int get_password(
req.message = text; req.message = text;
req.id = id; req.id = id;
r = ask_password_auto(&req, until, flags, &passwords2); r = ask_password_auto(&req, flags, &passwords2);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to query verification password: %m"); return log_error_errno(r, "Failed to query verification password: %m");
@ -1422,13 +1425,16 @@ static int crypt_activate_by_token_pin_ask_password(
pins = strv_free_erase(pins); pins = strv_free_erase(pins);
AskPasswordRequest req = { AskPasswordRequest req = {
.tty_fd = -EBADF,
.message = message, .message = message,
.icon = "drive-harddisk", .icon = "drive-harddisk",
.keyring = keyring, .keyring = keyring,
.credential = credential, .credential = credential,
.until = until,
.hup_fd = -EBADF,
}; };
r = ask_password_auto(&req, until, flags, &pins); r = ask_password_auto(&req, flags, &pins);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -735,10 +735,13 @@ static int prompt_root_password(int rfd) {
_cleanup_free_ char *error = NULL; _cleanup_free_ char *error = NULL;
AskPasswordRequest req = { AskPasswordRequest req = {
.tty_fd = -EBADF,
.message = msg1, .message = msg1,
.until = USEC_INFINITY,
.hup_fd = -EBADF,
}; };
r = ask_password_tty(-EBADF, &req, /* until= */ 0, /* flags= */ 0, /* flag_file= */ NULL, &a); r = ask_password_tty(&req, /* flags= */ 0, &a);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to query root password: %m"); return log_error_errno(r, "Failed to query root password: %m");
if (strv_length(a) != 1) if (strv_length(a) != 1)
@ -760,7 +763,7 @@ static int prompt_root_password(int rfd) {
req.message = msg2; req.message = msg2;
r = ask_password_tty(-EBADF, &req, /* until= */ 0, /* flags= */ 0, /* flag_file= */ NULL, &b); r = ask_password_tty(&req, /* flags= */ 0, &b);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to query root password: %m"); return log_error_errno(r, "Failed to query root password: %m");
if (strv_length(b) != 1) if (strv_length(b) != 1)

View File

@ -263,13 +263,16 @@ static int acquire_existing_password(
return log_oom(); return log_oom();
AskPasswordRequest req = { AskPasswordRequest req = {
.tty_fd = -EBADF,
.message = question, .message = question,
.icon = "user-home", .icon = "user-home",
.keyring = "home-password", .keyring = "home-password",
.credential = "home.password", .credential = "home.password",
.until = USEC_INFINITY,
.hup_fd = -EBADF,
}; };
r = ask_password_auto(&req, USEC_INFINITY, flags, &password); r = ask_password_auto(&req, flags, &password);
if (r == -EUNATCH) { /* EUNATCH is returned if no password was found and asking interactively was if (r == -EUNATCH) { /* EUNATCH is returned if no password was found and asking interactively was
* disabled via the flags. Not an error for us. */ * disabled via the flags. Not an error for us. */
log_debug_errno(r, "No passwords acquired."); log_debug_errno(r, "No passwords acquired.");
@ -321,13 +324,16 @@ static int acquire_recovery_key(
return log_oom(); return log_oom();
AskPasswordRequest req = { AskPasswordRequest req = {
.tty_fd = -EBADF,
.message = question, .message = question,
.icon = "user-home", .icon = "user-home",
.keyring = "home-recovery-key", .keyring = "home-recovery-key",
.credential = "home.recovery-key", .credential = "home.recovery-key",
.until = USEC_INFINITY,
.hup_fd = -EBADF,
}; };
r = ask_password_auto(&req, USEC_INFINITY, flags, &recovery_key); r = ask_password_auto(&req, flags, &recovery_key);
if (r == -EUNATCH) { /* EUNATCH is returned if no recovery key was found and asking interactively was if (r == -EUNATCH) { /* EUNATCH is returned if no recovery key was found and asking interactively was
* disabled via the flags. Not an error for us. */ * disabled via the flags. Not an error for us. */
log_debug_errno(r, "No recovery keys acquired."); log_debug_errno(r, "No recovery keys acquired.");
@ -375,13 +381,16 @@ static int acquire_token_pin(
return log_oom(); return log_oom();
AskPasswordRequest req = { AskPasswordRequest req = {
.tty_fd = -EBADF,
.message = question, .message = question,
.icon = "user-home", .icon = "user-home",
.keyring = "token-pin", .keyring = "token-pin",
.credential = "home.token-pin", .credential = "home.token-pin",
.until = USEC_INFINITY,
.hup_fd = -EBADF,
}; };
r = ask_password_auto(&req, USEC_INFINITY, flags, &pin); r = ask_password_auto(&req, flags, &pin);
if (r == -EUNATCH) { /* EUNATCH is returned if no PIN was found and asking interactively was disabled if (r == -EUNATCH) { /* EUNATCH is returned if no PIN was found and asking interactively was disabled
* via the flags. Not an error for us. */ * via the flags. Not an error for us. */
log_debug_errno(r, "No security token PINs acquired."); log_debug_errno(r, "No security token PINs acquired.");
@ -1229,15 +1238,17 @@ static int acquire_new_password(
return log_oom(); return log_oom();
AskPasswordRequest req = { AskPasswordRequest req = {
.tty_fd = -EBADF,
.message = question, .message = question,
.icon = "user-home", .icon = "user-home",
.keyring = "home-password", .keyring = "home-password",
.credential = "home.new-password", .credential = "home.new-password",
.until = USEC_INFINITY,
.hup_fd = -EBADF,
}; };
r = ask_password_auto( r = ask_password_auto(
&req, &req,
USEC_INFINITY,
/* flags= */ 0, /* no caching, we want to collect a new password here after all */ /* flags= */ 0, /* no caching, we want to collect a new password here after all */
&first); &first);
if (r < 0) if (r < 0)
@ -1253,7 +1264,6 @@ static int acquire_new_password(
r = ask_password_auto( r = ask_password_auto(
&req, &req,
USEC_INFINITY,
/* flags= */ 0, /* no caching */ /* flags= */ 0, /* no caching */
&second); &second);
if (r < 0) if (r < 0)

View File

@ -182,9 +182,12 @@ static int verb_validate(int argc, char *argv[], void *userdata) {
arg_private_key_source, arg_private_key_source,
arg_private_key, arg_private_key,
&(AskPasswordRequest) { &(AskPasswordRequest) {
.tty_fd = -EBADF,
.id = "keyutil-private-key-pin", .id = "keyutil-private-key-pin",
.keyring = arg_private_key, .keyring = arg_private_key,
.credential = "keyutil.private-key-pin", .credential = "keyutil.private-key-pin",
.until = USEC_INFINITY,
.hup_fd = -EBADF,
}, },
&private_key, &private_key,
&ui); &ui);
@ -238,9 +241,12 @@ static int verb_public(int argc, char *argv[], void *userdata) {
arg_private_key_source, arg_private_key_source,
arg_private_key, arg_private_key,
&(AskPasswordRequest) { &(AskPasswordRequest) {
.tty_fd = -EBADF,
.id = "keyutil-private-key-pin", .id = "keyutil-private-key-pin",
.keyring = arg_private_key, .keyring = arg_private_key,
.credential = "keyutil.private-key-pin", .credential = "keyutil.private-key-pin",
.until = USEC_INFINITY,
.hup_fd = -EBADF,
}, },
&private_key, &private_key,
&ui); &ui);

View File

@ -1062,8 +1062,10 @@ global:
LIBSYSTEMD_258 { LIBSYSTEMD_258 {
global: global:
sd_device_enumerator_add_all_parents;
sd_json_variant_type_from_string; sd_json_variant_type_from_string;
sd_json_variant_type_to_string; sd_json_variant_type_to_string;
sd_varlink_get_input_fd;
sd_varlink_get_output_fd;
sd_varlink_reset_fds; sd_varlink_reset_fds;
sd_device_enumerator_add_all_parents;
} LIBSYSTEMD_257; } LIBSYSTEMD_257;

View File

@ -1673,6 +1673,30 @@ _public_ int sd_varlink_get_fd(sd_varlink *v) {
return v->input_fd; return v->input_fd;
} }
_public_ int sd_varlink_get_input_fd(sd_varlink *v) {
assert_return(v, -EINVAL);
if (v->state == VARLINK_DISCONNECTED)
return varlink_log_errno(v, SYNTHETIC_ERRNO(ENOTCONN), "Not connected.");
if (v->input_fd < 0)
return varlink_log_errno(v, SYNTHETIC_ERRNO(EBADF), "No valid input fd.");
return v->input_fd;
}
_public_ int sd_varlink_get_output_fd(sd_varlink *v) {
assert_return(v, -EINVAL);
if (v->state == VARLINK_DISCONNECTED)
return varlink_log_errno(v, SYNTHETIC_ERRNO(ENOTCONN), "Not connected.");
if (v->output_fd < 0)
return varlink_log_errno(v, SYNTHETIC_ERRNO(EBADF), "No valid output fd.");
return v->output_fd;
}
_public_ int sd_varlink_get_events(sd_varlink *v) { _public_ int sd_varlink_get_events(sd_varlink *v) {
int ret = 0; int ret = 0;

View File

@ -888,9 +888,12 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
arg_private_key_source, arg_private_key_source,
arg_private_key, arg_private_key,
&(AskPasswordRequest) { &(AskPasswordRequest) {
.tty_fd = -EBADF,
.id = "measure-private-key-pin", .id = "measure-private-key-pin",
.keyring = arg_private_key, .keyring = arg_private_key,
.credential = "measure.private-key-pin", .credential = "measure.private-key-pin",
.until = USEC_INFINITY,
.hup_fd = -EBADF,
}, },
&privkey, &privkey,
&ui); &ui);

View File

@ -297,23 +297,11 @@ static int vl_method_extend(sd_varlink *link, sd_json_variant *parameters, sd_va
return sd_varlink_reply(link, NULL); return sd_varlink_reply(link, NULL);
} }
static int run(int argc, char *argv[]) { static int vl_server(void) {
_cleanup_free_ char *word = NULL; _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL;
Tpm2UserspaceEventType event;
int r; int r;
log_setup(); r = varlink_server_new(&varlink_server, SD_VARLINK_SERVER_ROOT_ONLY, /* userdata= */ NULL);
r = parse_argv(argc, argv);
if (r <= 0)
return r;
if (arg_varlink) {
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL;
/* Invocation as Varlink service */
r = varlink_server_new(&varlink_server, SD_VARLINK_SERVER_ROOT_ONLY, NULL);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to allocate Varlink server: %m"); return log_error_errno(r, "Failed to allocate Varlink server: %m");
@ -329,8 +317,22 @@ static int run(int argc, char *argv[]) {
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to run Varlink event loop: %m"); return log_error_errno(r, "Failed to run Varlink event loop: %m");
return EXIT_SUCCESS; return 0;
} }
static int run(int argc, char *argv[]) {
_cleanup_free_ char *word = NULL;
Tpm2UserspaceEventType event;
int r;
log_setup();
r = parse_argv(argc, argv);
if (r <= 0)
return r;
if (arg_varlink)
return vl_server(); /* Invocation as Varlink service */
if (arg_file_system) { if (arg_file_system) {
if (optind != argc) if (optind != argc)

View File

@ -4550,14 +4550,16 @@ static int make_policy(bool force, RecoveryPinMode recovery_pin_mode) {
_cleanup_(strv_free_erasep) char **l = NULL; _cleanup_(strv_free_erasep) char **l = NULL;
AskPasswordRequest req = { AskPasswordRequest req = {
.tty_fd = -EBADF,
.message = "Recovery PIN", .message = "Recovery PIN",
.id = "pcrlock-recovery-pin", .id = "pcrlock-recovery-pin",
.credential = "pcrlock.recovery-pin", .credential = "pcrlock.recovery-pin",
.until = USEC_INFINITY,
.hup_fd = -EBADF,
}; };
r = ask_password_auto( r = ask_password_auto(
&req, &req,
/* until= */ 0,
/* flags= */ 0, /* flags= */ 0,
&l); &l);
if (r < 0) if (r < 0)

View File

@ -8572,9 +8572,12 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY *
arg_private_key_source, arg_private_key_source,
arg_private_key, arg_private_key,
&(AskPasswordRequest) { &(AskPasswordRequest) {
.tty_fd = -EBADF,
.id = "repart-private-key-pin", .id = "repart-private-key-pin",
.keyring = arg_private_key, .keyring = arg_private_key,
.credential = "repart.private-key-pin", .credential = "repart.private-key-pin",
.until = USEC_INFINITY,
.hup_fd = -EBADF,
}, },
&private_key, &private_key,
&ui); &ui);

View File

@ -203,9 +203,12 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
arg_private_key_source, arg_private_key_source,
arg_private_key, arg_private_key,
&(AskPasswordRequest) { &(AskPasswordRequest) {
.tty_fd = -EBADF,
.id = "sbsign-private-key-pin", .id = "sbsign-private-key-pin",
.keyring = arg_private_key, .keyring = arg_private_key,
.credential = "sbsign.private-key-pin", .credential = "sbsign.private-key-pin",
.until = USEC_INFINITY,
.hup_fd = -EBADF,
}, },
&private_key, &private_key,
&ui); &ui);

View File

@ -309,9 +309,7 @@ static int backspace_string(int ttyfd, const char *str) {
int ask_password_plymouth( int ask_password_plymouth(
const AskPasswordRequest *req, const AskPasswordRequest *req,
usec_t until,
AskPasswordFlags flags, AskPasswordFlags flags,
const char *flag_file,
char ***ret) { char ***ret) {
_cleanup_close_ int fd = -EBADF, inotify_fd = -EBADF; _cleanup_close_ int fd = -EBADF, inotify_fd = -EBADF;
@ -328,12 +326,12 @@ int ask_password_plymouth(
const char *message = req && req->message ? req->message : "Password:"; const char *message = req && req->message ? req->message : "Password:";
if (flag_file) { if (req->flag_file) {
inotify_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK); inotify_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
if (inotify_fd < 0) if (inotify_fd < 0)
return -errno; return -errno;
if (inotify_add_watch(inotify_fd, flag_file, IN_ATTRIB) < 0) /* for the link count */ if (inotify_add_watch(inotify_fd, req->flag_file, IN_ATTRIB) < 0) /* for the link count */
return -errno; return -errno;
} }
@ -357,25 +355,40 @@ int ask_password_plymouth(
enum { enum {
POLL_SOCKET, POLL_SOCKET,
POLL_INOTIFY, /* Must be last, because optional */ POLL_TWO,
POLL_THREE,
_POLL_MAX, _POLL_MAX,
}; };
struct pollfd pollfd[_POLL_MAX] = { struct pollfd pollfd[_POLL_MAX] = {
[POLL_SOCKET] = { .fd = fd, .events = POLLIN }, [POLL_SOCKET] = {
[POLL_INOTIFY] = { .fd = inotify_fd, .events = POLLIN }, .fd = fd,
.events = POLLIN,
},
}; };
size_t n_pollfd = inotify_fd >= 0 ? _POLL_MAX : _POLL_MAX-1; size_t n_pollfd = POLL_SOCKET + 1, inotify_idx = SIZE_MAX, hup_fd_idx = SIZE_MAX;
if (inotify_fd >= 0)
pollfd[inotify_idx = n_pollfd++] = (struct pollfd) {
.fd = inotify_fd,
.events = POLLIN,
};
if (req->hup_fd >= 0)
pollfd[hup_fd_idx = n_pollfd++] = (struct pollfd) {
.fd = req->hup_fd,
.events = POLLHUP,
};
assert(n_pollfd <= _POLL_MAX);
for (;;) { for (;;) {
usec_t timeout; usec_t timeout;
if (until > 0) if (req->until > 0)
timeout = usec_sub_unsigned(until, now(CLOCK_MONOTONIC)); timeout = usec_sub_unsigned(req->until, now(CLOCK_MONOTONIC));
else else
timeout = USEC_INFINITY; timeout = USEC_INFINITY;
if (flag_file && access(flag_file, F_OK) < 0) if (req->flag_file && access(req->flag_file, F_OK) < 0)
return -errno; return -errno;
r = ppoll_usec(pollfd, n_pollfd, timeout); r = ppoll_usec(pollfd, n_pollfd, timeout);
@ -386,7 +399,10 @@ int ask_password_plymouth(
if (r == 0) if (r == 0)
return -ETIME; return -ETIME;
if (inotify_fd >= 0 && pollfd[POLL_INOTIFY].revents != 0) if (req->hup_fd >= 0 && pollfd[hup_fd_idx].revents & POLLHUP)
return -ECONNRESET;
if (inotify_fd >= 0 && pollfd[inotify_idx].revents != 0)
(void) flush_fd(inotify_fd); (void) flush_fd(inotify_fd);
if (pollfd[POLL_SOCKET].revents == 0) if (pollfd[POLL_SOCKET].revents == 0)
@ -464,11 +480,8 @@ int ask_password_plymouth(
#define SKIPPED "(skipped)" #define SKIPPED "(skipped)"
int ask_password_tty( int ask_password_tty(
int ttyfd,
const AskPasswordRequest *req, const AskPasswordRequest *req,
usec_t until,
AskPasswordFlags flags, AskPasswordFlags flags,
const char *flag_file,
char ***ret) { char ***ret) {
bool reset_tty = false, dirty = false, use_color = false, press_tab_visible = false; bool reset_tty = false, dirty = false, use_color = false, press_tab_visible = false;
@ -493,15 +506,14 @@ int ask_password_tty(
if (!FLAGS_SET(flags, ASK_PASSWORD_HIDE_EMOJI) && emoji_enabled()) if (!FLAGS_SET(flags, ASK_PASSWORD_HIDE_EMOJI) && emoji_enabled())
message = strjoina(special_glyph(SPECIAL_GLYPH_LOCK_AND_KEY), " ", message); message = strjoina(special_glyph(SPECIAL_GLYPH_LOCK_AND_KEY), " ", message);
if (flag_file || (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && keyring)) { if (req->flag_file || (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && keyring)) {
inotify_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK); inotify_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
if (inotify_fd < 0) if (inotify_fd < 0)
return -errno; return -errno;
} }
if (flag_file) { if (req->flag_file)
if (inotify_add_watch(inotify_fd, flag_file, IN_ATTRIB /* for the link count */) < 0) if (inotify_add_watch(inotify_fd, req->flag_file, IN_ATTRIB /* for the link count */) < 0)
return -errno; return -errno;
}
if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && req && keyring) { if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && req && keyring) {
r = ask_password_keyring(req, flags, ret); r = ask_password_keyring(req, flags, ret);
if (r >= 0) if (r >= 0)
@ -529,8 +541,11 @@ int ask_password_tty(
CLEANUP_ERASE(passphrase); CLEANUP_ERASE(passphrase);
/* If the caller didn't specify a TTY, then use the controlling tty, if we can. */ /* If the caller didn't specify a TTY, then use the controlling tty, if we can. */
if (ttyfd < 0) int ttyfd;
if (req->tty_fd < 0)
ttyfd = cttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC); ttyfd = cttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC);
else
ttyfd = req->tty_fd;
if (ttyfd >= 0) { if (ttyfd >= 0) {
if (tcgetattr(ttyfd, &old_termios) < 0) if (tcgetattr(ttyfd, &old_termios) < 0)
@ -570,28 +585,44 @@ int ask_password_tty(
enum { enum {
POLL_TTY, POLL_TTY,
POLL_INOTIFY, /* Must be last, because optional */ POLL_TWO,
POLL_THREE,
_POLL_MAX, _POLL_MAX,
}; };
struct pollfd pollfd[_POLL_MAX] = { struct pollfd pollfd[_POLL_MAX] = {
[POLL_TTY] = { .fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO, .events = POLLIN }, [POLL_TTY] = {
[POLL_INOTIFY] = { .fd = inotify_fd, .events = POLLIN }, .fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO,
.events = POLLIN,
},
}; };
size_t n_pollfd = inotify_fd >= 0 ? _POLL_MAX : _POLL_MAX-1; size_t n_pollfd = POLL_TTY + 1, inotify_idx = SIZE_MAX, hup_fd_idx = SIZE_MAX;
if (inotify_fd >= 0)
pollfd[inotify_idx = n_pollfd++] = (struct pollfd) {
.fd = inotify_fd,
.events = POLLIN,
};
if (req->hup_fd >= 0)
pollfd[hup_fd_idx = n_pollfd++] = (struct pollfd) {
.fd = req->hup_fd,
.events = POLLHUP,
};
assert(n_pollfd <= _POLL_MAX);
for (;;) { for (;;) {
_cleanup_(erase_char) char c; _cleanup_(erase_char) char c;
usec_t timeout; usec_t timeout;
ssize_t n; ssize_t n;
if (until > 0) if (req->until > 0)
timeout = usec_sub_unsigned(until, now(CLOCK_MONOTONIC)); timeout = usec_sub_unsigned(req->until, now(CLOCK_MONOTONIC));
else else
timeout = USEC_INFINITY; timeout = USEC_INFINITY;
if (flag_file) { if (req->flag_file) {
r = RET_NERRNO(access(flag_file, F_OK)); r = RET_NERRNO(access(req->flag_file, F_OK));
if (r < 0) if (r < 0)
goto finish; goto finish;
} }
@ -606,7 +637,12 @@ int ask_password_tty(
goto finish; goto finish;
} }
if (inotify_fd >= 0 && pollfd[POLL_INOTIFY].revents != 0 && keyring) { if (req->hup_fd >= 0 && pollfd[hup_fd_idx].revents & POLLHUP) {
r = -ECONNRESET;
goto finish;
}
if (inotify_fd >= 0 && pollfd[inotify_idx].revents != 0 && keyring) {
(void) flush_fd(inotify_fd); (void) flush_fd(inotify_fd);
r = ask_password_keyring(req, flags, ret); r = ask_password_keyring(req, flags, ret);
@ -800,7 +836,6 @@ static int create_socket(const char *askpwdir, char **ret) {
int ask_password_agent( int ask_password_agent(
const AskPasswordRequest *req, const AskPasswordRequest *req,
usec_t until,
AskPasswordFlags flags, AskPasswordFlags flags,
char ***ret) { char ***ret) {
@ -820,6 +855,10 @@ int ask_password_agent(
if (FLAGS_SET(flags, ASK_PASSWORD_NO_AGENT)) if (FLAGS_SET(flags, ASK_PASSWORD_NO_AGENT))
return -EUNATCH; return -EUNATCH;
/* We don't support the flag file concept for now when querying via the agent logic */
if (req->flag_file)
return -EOPNOTSUPP;
assert_se(sigemptyset(&mask) >= 0); assert_se(sigemptyset(&mask) >= 0);
assert_se(sigset_add_many(&mask, SIGINT, SIGTERM) >= 0); assert_se(sigset_add_many(&mask, SIGINT, SIGTERM) >= 0);
assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) >= 0); assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) >= 0);
@ -891,7 +930,7 @@ int ask_password_agent(
socket_name, socket_name,
FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED), FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED),
FLAGS_SET(flags, ASK_PASSWORD_ECHO), FLAGS_SET(flags, ASK_PASSWORD_ECHO),
until, req->until,
FLAGS_SET(flags, ASK_PASSWORD_SILENT)); FLAGS_SET(flags, ASK_PASSWORD_SILENT));
if (req) { if (req) {
@ -924,16 +963,29 @@ int ask_password_agent(
enum { enum {
POLL_SOCKET, POLL_SOCKET,
POLL_SIGNAL, POLL_SIGNAL,
POLL_INOTIFY, /* Must be last, because optional */ POLL_THREE,
POLL_FOUR,
_POLL_MAX _POLL_MAX
}; };
struct pollfd pollfd[_POLL_MAX] = { struct pollfd pollfd[_POLL_MAX] = {
[POLL_SOCKET] = { .fd = socket_fd, .events = POLLIN }, [POLL_SOCKET] = { .fd = socket_fd, .events = POLLIN },
[POLL_SIGNAL] = { .fd = signal_fd, .events = POLLIN }, [POLL_SIGNAL] = { .fd = signal_fd, .events = POLLIN },
[POLL_INOTIFY] = { .fd = inotify_fd, .events = POLLIN },
}; };
size_t n_pollfd = inotify_fd >= 0 ? _POLL_MAX : _POLL_MAX - 1; size_t n_pollfd = POLL_SIGNAL + 1, inotify_idx = SIZE_MAX, hup_fd_idx = SIZE_MAX;
if (inotify_fd >= 0)
pollfd[inotify_idx = n_pollfd++] = (struct pollfd) {
.fd = inotify_fd,
.events = POLLIN,
};
if (req->hup_fd >= 0)
pollfd[hup_fd_idx = n_pollfd ++] = (struct pollfd) {
.fd = req->hup_fd,
.events = POLLHUP,
};
assert(n_pollfd <= _POLL_MAX);
for (;;) { for (;;) {
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control; CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
@ -943,8 +995,8 @@ int ask_password_agent(
usec_t timeout; usec_t timeout;
ssize_t n; ssize_t n;
if (until > 0) if (req->until > 0)
timeout = usec_sub_unsigned(until, now(CLOCK_MONOTONIC)); timeout = usec_sub_unsigned(req->until, now(CLOCK_MONOTONIC));
else else
timeout = USEC_INFINITY; timeout = USEC_INFINITY;
@ -963,7 +1015,10 @@ int ask_password_agent(
goto finish; goto finish;
} }
if (inotify_fd >= 0 && pollfd[POLL_INOTIFY].revents != 0) { if (req->hup_fd >= 0 && pollfd[hup_fd_idx].revents & POLLHUP)
return -ECONNRESET;
if (inotify_fd >= 0 && pollfd[inotify_idx].revents != 0) {
(void) flush_fd(inotify_fd); (void) flush_fd(inotify_fd);
if (req && req->keyring) { if (req && req->keyring) {
@ -1103,7 +1158,6 @@ static int ask_password_credential(const AskPasswordRequest *req, AskPasswordFla
int ask_password_auto( int ask_password_auto(
const AskPasswordRequest *req, const AskPasswordRequest *req,
usec_t until,
AskPasswordFlags flags, AskPasswordFlags flags,
char ***ret) { char ***ret) {
@ -1111,6 +1165,17 @@ int ask_password_auto(
assert(ret); assert(ret);
/* Returns the following well-known errors:
*
* -ETIME a timeout was specified and hit
* -EUNATCH couldn't ask interactively and no cached password available either
* -ENOENT the specified flag file disappeared
* -ECANCELED the user explicitly cancelled the request
* -EINTR SIGINT/SIGTERM where received during the query
* -ENOEXEC headless mode was requested but no password could be acquired non-interactively
* -ECONNRESET a POLLHUP has been seen on the specified hup_fd
*/
if (!FLAGS_SET(flags, ASK_PASSWORD_NO_CREDENTIAL) && req && req->credential) { if (!FLAGS_SET(flags, ASK_PASSWORD_NO_CREDENTIAL) && req && req->credential) {
r = ask_password_credential(req, flags, ret); r = ask_password_credential(req, flags, ret);
if (r != -ENOKEY) if (r != -ENOKEY)
@ -1127,10 +1192,10 @@ int ask_password_auto(
} }
if (!FLAGS_SET(flags, ASK_PASSWORD_NO_TTY) && isatty_safe(STDIN_FILENO)) if (!FLAGS_SET(flags, ASK_PASSWORD_NO_TTY) && isatty_safe(STDIN_FILENO))
return ask_password_tty(-EBADF, req, until, flags, NULL, ret); return ask_password_tty(req, flags, ret);
if (!FLAGS_SET(flags, ASK_PASSWORD_NO_AGENT)) if (!FLAGS_SET(flags, ASK_PASSWORD_NO_AGENT))
return ask_password_agent(req, until, flags, ret); return ask_password_agent(req, flags, ret);
return -EUNATCH; return -EUNATCH;
} }

View File

@ -26,11 +26,15 @@ typedef struct AskPasswordRequest {
const char *icon; /* freedesktop icon spec name */ const char *icon; /* freedesktop icon spec name */
const char *id; /* some identifier used for this prompt for the "ask-password" protocol */ const char *id; /* some identifier used for this prompt for the "ask-password" protocol */
const char *credential; /* $CREDENTIALS_DIRECTORY credential name */ const char *credential; /* $CREDENTIALS_DIRECTORY credential name */
const char *flag_file; /* Once this flag file disappears abort the query */
int tty_fd; /* If querying on a TTY, the TTY to query on (or -EBADF) */
usec_t until; /* CLOCK_MONOTONIC time until which to show the prompt */
int hup_fd; /* An extra fd to watch for POLLHUP, in which case to abort the query */
} AskPasswordRequest; } AskPasswordRequest;
int ask_password_tty(int tty_fd, const AskPasswordRequest *req, usec_t until, AskPasswordFlags flags, const char *flag_file, char ***ret); int ask_password_tty(const AskPasswordRequest *req, AskPasswordFlags flags, char ***ret);
int ask_password_plymouth(const AskPasswordRequest *req, usec_t until, AskPasswordFlags flags, const char *flag_file, char ***ret); int ask_password_plymouth(const AskPasswordRequest *req, AskPasswordFlags flags, char ***ret);
int ask_password_agent(const AskPasswordRequest *req, usec_t until, AskPasswordFlags flag, char ***ret); int ask_password_agent(const AskPasswordRequest *req, AskPasswordFlags flag, char ***ret);
int ask_password_auto(const AskPasswordRequest *req, usec_t until, AskPasswordFlags flag, char ***ret); int ask_password_auto(const AskPasswordRequest *req, AskPasswordFlags flag, char ***ret);
int acquire_user_ask_password_directory(char **ret); int acquire_user_ask_password_directory(char **ret);

View File

@ -111,15 +111,18 @@ int acquire_fido2_key(
if (FLAGS_SET(askpw_flags, ASK_PASSWORD_HEADLESS)) if (FLAGS_SET(askpw_flags, ASK_PASSWORD_HEADLESS))
return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "PIN querying disabled via 'headless' option. Use the '$PIN' environment variable."); return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "PIN querying disabled via 'headless' option. Use the '$PIN' environment variable.");
static const AskPasswordRequest req = { AskPasswordRequest req = {
.tty_fd = -EBADF,
.message = "Please enter security token PIN:", .message = "Please enter security token PIN:",
.icon = "drive-harddisk", .icon = "drive-harddisk",
.keyring = "fido2-pin", .keyring = "fido2-pin",
.credential = "cryptsetup.fido2-pin", .credential = "cryptsetup.fido2-pin",
.until = until,
.hup_fd = -EBADF,
}; };
pins = strv_free_erase(pins); pins = strv_free_erase(pins);
r = ask_password_auto(&req, until, askpw_flags, &pins); r = ask_password_auto(&req, askpw_flags, &pins);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to ask for user password: %m"); return log_error_errno(r, "Failed to ask for user password: %m");

View File

@ -35,14 +35,17 @@ static int get_pin(
"Use the '$PIN' environment variable."); "Use the '$PIN' environment variable.");
AskPasswordRequest req = { AskPasswordRequest req = {
.tty_fd = -EBADF,
.message = "Please enter TPM2 PIN:", .message = "Please enter TPM2 PIN:",
.icon = "drive-harddisk", .icon = "drive-harddisk",
.keyring = "tpm2-pin", .keyring = "tpm2-pin",
.credential = askpw_credential, .credential = askpw_credential,
.until = until,
.hup_fd = -EBADF,
}; };
pin = strv_free_erase(pin); pin = strv_free_erase(pin);
r = ask_password_auto(&req, until, askpw_flags, &pin); r = ask_password_auto(&req, askpw_flags, &pin);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to ask for user pin: %m"); return log_error_errno(r, "Failed to ask for user pin: %m");
assert(strv_length(pin) == 1); assert(strv_length(pin) == 1);

View File

@ -3077,13 +3077,16 @@ int dissected_image_decrypt_interactively(
z = strv_free_erase(z); z = strv_free_erase(z);
static const AskPasswordRequest req = { static const AskPasswordRequest req = {
.tty_fd = -EBADF,
.message = "Please enter image passphrase:", .message = "Please enter image passphrase:",
.id = "dissect", .id = "dissect",
.keyring = "dissect", .keyring = "dissect",
.credential = "dissect.passphrase", .credential = "dissect.passphrase",
.until = USEC_INFINITY,
.hup_fd = -EBADF,
}; };
r = ask_password_auto(&req, USEC_INFINITY, /* flags= */ 0, &z); r = ask_password_auto(&req, /* flags= */ 0, &z);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to query for passphrase: %m"); return log_error_errno(r, "Failed to query for passphrase: %m");

View File

@ -857,13 +857,16 @@ int fido2_generate_hmac_hash(
for (;;) { for (;;) {
_cleanup_strv_free_erase_ char **pin = NULL; _cleanup_strv_free_erase_ char **pin = NULL;
AskPasswordRequest req = { AskPasswordRequest req = {
.tty_fd = -EBADF,
.message = "Please enter security token PIN:", .message = "Please enter security token PIN:",
.icon = askpw_icon, .icon = askpw_icon,
.keyring = "fido2-pin", .keyring = "fido2-pin",
.credential = askpw_credential, .credential = askpw_credential,
.until = USEC_INFINITY,
.hup_fd = -EBADF,
}; };
r = ask_password_auto(&req, USEC_INFINITY, /* flags= */ 0, &pin); r = ask_password_auto(&req, /* flags= */ 0, &pin);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to acquire user PIN: %m"); return log_error_errno(r, "Failed to acquire user PIN: %m");

View File

@ -177,6 +177,7 @@ shared_sources = files(
'userdb-dropin.c', 'userdb-dropin.c',
'userdb.c', 'userdb.c',
'varlink-idl-common.c', 'varlink-idl-common.c',
'varlink-io.systemd.AskPassword.c',
'varlink-io.systemd.BootControl.c', 'varlink-io.systemd.BootControl.c',
'varlink-io.systemd.Credentials.c', 'varlink-io.systemd.Credentials.c',
'varlink-io.systemd.Hostname.c', 'varlink-io.systemd.Hostname.c',

View File

@ -1399,7 +1399,7 @@ static int openssl_ask_password_ui_read(UI *ui, UI_STRING *uis) {
req->message = UI_get0_output_string(uis); req->message = UI_get0_output_string(uis);
_cleanup_(strv_freep) char **l = NULL; _cleanup_(strv_freep) char **l = NULL;
r = ask_password_auto(req, /*until=*/ 0, ASK_PASSWORD_ACCEPT_CACHED|ASK_PASSWORD_PUSH_CACHE, &l); r = ask_password_auto(req, ASK_PASSWORD_ACCEPT_CACHED|ASK_PASSWORD_PUSH_CACHE, &l);
if (r < 0) { if (r < 0) {
log_error_errno(r, "Failed to query for PIN: %m"); log_error_errno(r, "Failed to query for PIN: %m");
return 0; return 0;

View File

@ -380,15 +380,18 @@ int pkcs11_token_login(
return log_oom(); return log_oom();
AskPasswordRequest req = { AskPasswordRequest req = {
.tty_fd = -EBADF,
.message = text, .message = text,
.icon = askpw_icon, .icon = askpw_icon,
.id = id, .id = id,
.keyring = askpw_keyring, .keyring = askpw_keyring,
.credential = askpw_credential, .credential = askpw_credential,
.until = until,
.hup_fd = -EBADF,
}; };
/* We never cache PINs, simply because it's fatal if we use wrong PINs, since usually there are only 3 tries */ /* We never cache PINs, simply because it's fatal if we use wrong PINs, since usually there are only 3 tries */
r = ask_password_auto(&req, until, askpw_flags, &passwords); r = ask_password_auto(&req, askpw_flags, &passwords);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to query PIN for security token '%s': %m", token_label); return log_error_errno(r, "Failed to query PIN for security token '%s': %m", token_label);
} }

View File

@ -0,0 +1,53 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "bus-polkit.h"
#include "varlink-io.systemd.AskPassword.h"
static SD_VARLINK_DEFINE_ENUM_TYPE(
EchoMode,
SD_VARLINK_FIELD_COMMENT("Request that the password is prompted for without any visual feedback"),
SD_VARLINK_DEFINE_ENUM_VALUE(off),
SD_VARLINK_FIELD_COMMENT("Show the password in plaintext as it is typed in"),
SD_VARLINK_DEFINE_ENUM_VALUE(on),
SD_VARLINK_FIELD_COMMENT("Provide visual feedback as the password is typed, but mask the password plaintext"),
SD_VARLINK_DEFINE_ENUM_VALUE(masked));
static SD_VARLINK_DEFINE_METHOD(
Ask,
SD_VARLINK_FIELD_COMMENT("The message to show when prompting for the password"),
SD_VARLINK_DEFINE_INPUT(message, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The name for the kernel keyring entry used for caching"),
SD_VARLINK_DEFINE_INPUT(keyname, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The icon name to display, following the freedesktop.org icon naming specification"),
SD_VARLINK_DEFINE_INPUT(icon, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("An recognizable id for the password prompt"),
SD_VARLINK_DEFINE_INPUT(id, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Timeout in µs (relative, CLOCK_MONOTONIC)"),
SD_VARLINK_DEFINE_INPUT(timeoutUSec, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Timeout in µs (absolute, CLOCK_MONOTONIC; if both timeoutUSec and untilUSec are specified the earlier of the two is used)"),
SD_VARLINK_DEFINE_INPUT(untilUSec, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Whether to accept cached passwords from the kernel keyring"),
SD_VARLINK_DEFINE_INPUT(acceptCached, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Whether to push acquired passwords into the kernel keyring"),
SD_VARLINK_DEFINE_INPUT(pushCache, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Whether to give visual feedback when typing in the password"),
SD_VARLINK_DEFINE_INPUT_BY_TYPE(echo, EchoMode, SD_VARLINK_NULLABLE),
VARLINK_DEFINE_POLKIT_INPUT,
SD_VARLINK_FIELD_COMMENT("List of acquired passwords. This typically contains one entry, but might contain more in case multiple passwords were previously cached."),
SD_VARLINK_DEFINE_OUTPUT(passwords, SD_VARLINK_STRING, SD_VARLINK_ARRAY));
static SD_VARLINK_DEFINE_ERROR(NoPasswordAvailable);
static SD_VARLINK_DEFINE_ERROR(TimeoutReached);
SD_VARLINK_DEFINE_INTERFACE(
io_systemd_AskPassword,
"io.systemd.AskPassword",
SD_VARLINK_INTERFACE_COMMENT("An interface for interactively asking the user for a password"),
SD_VARLINK_SYMBOL_COMMENT("Encodes whether to provide visual feedback as the password is typed in"),
&vl_type_EchoMode,
SD_VARLINK_SYMBOL_COMMENT("Interactively ask the user for a password, or answer from a previously cached entry"),
&vl_method_Ask,
SD_VARLINK_SYMBOL_COMMENT("No password available, because none was provided in the cache, and no agent was asked"),
&vl_error_NoPasswordAvailable,
SD_VARLINK_SYMBOL_COMMENT("Query timeout reached, user did not provide a password in time"),
&vl_error_TimeoutReached);

View File

@ -0,0 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "sd-varlink-idl.h"
extern const sd_varlink_interface vl_interface_io_systemd_AskPassword;

View File

@ -96,6 +96,8 @@ sd_varlink* sd_varlink_ref(sd_varlink *link);
sd_varlink* sd_varlink_unref(sd_varlink *v); sd_varlink* sd_varlink_unref(sd_varlink *v);
int sd_varlink_get_fd(sd_varlink *v); int sd_varlink_get_fd(sd_varlink *v);
int sd_varlink_get_input_fd(sd_varlink *v);
int sd_varlink_get_output_fd(sd_varlink *v);
int sd_varlink_get_events(sd_varlink *v); int sd_varlink_get_events(sd_varlink *v);
int sd_varlink_get_timeout(sd_varlink *v, uint64_t *ret); int sd_varlink_get_timeout(sd_varlink *v, uint64_t *ret);

View File

@ -9,11 +9,14 @@ TEST(ask_password) {
int r; int r;
static const AskPasswordRequest req = { static const AskPasswordRequest req = {
.tty_fd = -EBADF,
.message = "hello?", .message = "hello?",
.keyring = "da key", .keyring = "da key",
.until = USEC_INFINITY,
.hup_fd = -EBADF,
}; };
r = ask_password_tty(-EBADF, &req, /* until= */ 0, /* flags= */ ASK_PASSWORD_CONSOLE_COLOR, /* flag_file= */ NULL, &ret); r = ask_password_tty(&req, /* flags= */ ASK_PASSWORD_CONSOLE_COLOR, &ret);
if (r == -ECANCELED) if (r == -ECANCELED)
ASSERT_NULL(ret); ASSERT_NULL(ret);
else { else {

View File

@ -11,6 +11,7 @@
#include "varlink-idl-util.h" #include "varlink-idl-util.h"
#include "varlink-io.systemd.h" #include "varlink-io.systemd.h"
#include "varlink-io.systemd.BootControl.h" #include "varlink-io.systemd.BootControl.h"
#include "varlink-io.systemd.AskPassword.h"
#include "varlink-io.systemd.Credentials.h" #include "varlink-io.systemd.Credentials.h"
#include "varlink-io.systemd.Import.h" #include "varlink-io.systemd.Import.h"
#include "varlink-io.systemd.Journal.h" #include "varlink-io.systemd.Journal.h"
@ -194,6 +195,8 @@ TEST(parse_format) {
print_separator(); print_separator();
test_parse_format_one(&vl_interface_io_systemd_MachineImage); test_parse_format_one(&vl_interface_io_systemd_MachineImage);
print_separator(); print_separator();
test_parse_format_one(&vl_interface_io_systemd_AskPassword);
print_separator();
test_parse_format_one(&vl_interface_xyz_test); test_parse_format_one(&vl_interface_xyz_test);
} }

View File

@ -21,6 +21,7 @@
#include "build.h" #include "build.h"
#include "conf-parser.h" #include "conf-parser.h"
#include "constants.h" #include "constants.h"
#include "daemon-util.h"
#include "devnum-util.h" #include "devnum-util.h"
#include "dirent-util.h" #include "dirent-util.h"
#include "exit-status.h" #include "exit-status.h"
@ -147,10 +148,14 @@ static int agent_ask_password_tty(
} }
AskPasswordRequest req = { AskPasswordRequest req = {
.tty_fd = tty_fd,
.message = message, .message = message,
.flag_file = flag_file,
.until = until,
.hup_fd = -EBADF,
}; };
r = ask_password_tty(tty_fd, &req, until, flags, flag_file, ret); r = ask_password_tty(&req, flags, ret);
if (arg_console) { if (arg_console) {
assert(tty_fd >= 0); assert(tty_fd >= 0);
@ -243,12 +248,23 @@ static int process_one_password_file(const char *filename, FILE *f) {
SET_FLAG(flags, ASK_PASSWORD_ECHO, echo); SET_FLAG(flags, ASK_PASSWORD_ECHO, echo);
SET_FLAG(flags, ASK_PASSWORD_SILENT, silent); SET_FLAG(flags, ASK_PASSWORD_SILENT, silent);
/* Allow providing a password via env var, for debugging purposes */
const char *e = secure_getenv("SYSTEMD_ASK_PASSWORD_AGENT_PASSWORD");
if (e) {
passwords = strv_new(e);
if (!passwords)
return log_oom();
} else {
if (arg_plymouth) { if (arg_plymouth) {
AskPasswordRequest req = { AskPasswordRequest req = {
.tty_fd = -EBADF,
.message = message, .message = message,
.flag_file = filename,
.until = not_after,
.hup_fd = -EBADF,
}; };
r = ask_password_plymouth(&req, not_after, flags, filename, &passwords); r = ask_password_plymouth(&req, flags, &passwords);
} else } else
r = agent_ask_password_tty(message, not_after, flags, filename, &passwords); r = agent_ask_password_tty(message, not_after, flags, filename, &passwords);
if (r < 0) { if (r < 0) {
@ -258,6 +274,7 @@ static int process_one_password_file(const char *filename, FILE *f) {
return log_error_errno(r, "Failed to query password: %m"); return log_error_errno(r, "Failed to query password: %m");
} }
}
assert(!strv_isempty(passwords)); assert(!strv_isempty(passwords));
r = send_passwords(socket_name, passwords); r = send_passwords(socket_name, passwords);
@ -385,6 +402,9 @@ static int process_and_watch_password_files(bool watch) {
pollfd[FD_INOTIFY] = (struct pollfd) { .fd = notify, .events = POLLIN }; pollfd[FD_INOTIFY] = (struct pollfd) { .fd = notify, .events = POLLIN };
} }
_unused_ _cleanup_(notify_on_cleanup) const char *notify_stop =
notify_start(NOTIFY_READY, NOTIFY_STOPPING);
for (;;) { for (;;) {
usec_t timeout = USEC_INFINITY; usec_t timeout = USEC_INFINITY;

View File

@ -0,0 +1,24 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
# shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh
at_exit() {
set +e
systemctl stop waldo-ask-pw-agent.service
}
trap at_exit EXIT
systemd-ask-password --help
systemd-tty-ask-password-agent --list
varlinkctl introspect /run/systemd/io.systemd.AskPassword
# Spawn an agent that always replies all ask password requests with "waldo"
systemd-run -u waldo-ask-pw-agent.service -p Environment=SYSTEMD_ASK_PASSWORD_AGENT_PASSWORD=waldo -p Type=notify /usr/bin/systemd-tty-ask-password-agent --watch --console=/dev/console
assert_eq "$(systemd-ask-password --no-tty)" "waldo"
assert_eq "$(varlinkctl call /usr/bin/systemd-ask-password io.systemd.AskPassword.Ask '{"message":"foobar"}' | jq '.passwords[0]')" "\"waldo\""

View File

@ -245,6 +245,11 @@ units = [
{ 'file' : 'system-update-cleanup.service' }, { 'file' : 'system-update-cleanup.service' },
{ 'file' : 'system-update-pre.target' }, { 'file' : 'system-update-pre.target' },
{ 'file' : 'system-update.target' }, { 'file' : 'system-update.target' },
{
'file' : 'systemd-ask-password.socket',
'symlinks' : ['sockets.target.wants/']
},
{ 'file' : 'systemd-ask-password@.service' },
{ {
'file' : 'systemd-ask-password-console.path', 'file' : 'systemd-ask-password-console.path',
'symlinks' : ['sysinit.target.wants/'], 'symlinks' : ['sysinit.target.wants/'],

View File

@ -22,4 +22,5 @@ Before=shutdown.target initrd-switch-root.target
[Service] [Service]
ExecStart=systemd-tty-ask-password-agent --watch --console ExecStart=systemd-tty-ask-password-agent --watch --console
Type=notify
SystemCallArchitectures=native SystemCallArchitectures=native

View File

@ -15,4 +15,5 @@ After=systemd-user-sessions.service
[Service] [Service]
ExecStartPre=-systemctl stop systemd-ask-password-console.path systemd-ask-password-console.service systemd-ask-password-plymouth.path systemd-ask-password-plymouth.service ExecStartPre=-systemctl stop systemd-ask-password-console.path systemd-ask-password-console.service systemd-ask-password-plymouth.path systemd-ask-password-plymouth.service
ExecStart=systemd-tty-ask-password-agent --wall ExecStart=systemd-tty-ask-password-agent --wall
Type=notify
SystemCallArchitectures=native SystemCallArchitectures=native

View File

@ -0,0 +1,21 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Query the User Interactively for a Password
Documentation=man:systemd-ask-password(1)
DefaultDependencies=no
Before=sockets.target
[Socket]
ListenStream=/run/systemd/io.systemd.AskPassword
FileDescriptorName=varlink
SocketMode=0666
Accept=yes
MaxConnectionsPerSource=16

View File

@ -0,0 +1,16 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Query the User Interactively for a Password
Documentation=man:systemd-ask-password(1)
DefaultDependencies=no
[Service]
ExecStart=-systemd-ask-password --system --no-tty

View File

@ -1,34 +1,64 @@
# SPDX-License-Identifier: LGPL-2.1-or-later # SPDX-License-Identifier: LGPL-2.1-or-later
units = [ units = [
'app.slice', { 'file' : 'app.slice' },
'background.slice', { 'file' : 'background.slice' },
'basic.target', { 'file' : 'basic.target' },
'bluetooth.target', { 'file' : 'bluetooth.target' },
'capsule@.target', { 'file' : 'capsule@.target' },
'default.target', { 'file' : 'default.target' },
'exit.target', { 'file' : 'exit.target' },
'graphical-session-pre.target', { 'file' : 'graphical-session-pre.target' },
'graphical-session.target', { 'file' : 'graphical-session.target' },
'paths.target', { 'file' : 'paths.target' },
'printer.target', { 'file' : 'printer.target' },
'session.slice', { 'file' : 'session.slice' },
'shutdown.target', { 'file' : 'shutdown.target' },
'smartcard.target', { 'file' : 'smartcard.target' },
'sockets.target', { 'file' : 'sockets.target' },
'sound.target', { 'file' : 'sound.target' },
'systemd-exit.service', {
'systemd-tmpfiles-clean.service', 'file' : 'systemd-ask-password.socket',
'systemd-tmpfiles-clean.timer', 'symlinks' : ['sockets.target.wants/']
'systemd-tmpfiles-setup.service', },
'timers.target', { 'file' : 'systemd-ask-password@.service' },
{ 'file' : 'systemd-exit.service' },
{ 'file' : 'systemd-tmpfiles-clean.service' },
{ 'file' : 'systemd-tmpfiles-clean.timer' },
{ 'file' : 'systemd-tmpfiles-setup.service' },
{ 'file' : 'timers.target' },
{
'file' : 'xdg-desktop-autostart.target',
'conditions': ['ENABLE_XDG_AUTOSTART'],
}
] ]
if conf.get('ENABLE_XDG_AUTOSTART') == 1 foreach unit : units
units += 'xdg-desktop-autostart.target' file = unit.get('file')
endif
foreach file : units install = true
foreach cond : unit.get('conditions', [])
if conf.get(cond) != 1
install = false
break
endif
endforeach
if install
install_data(file, install_data(file,
install_dir : userunitdir) install_dir : userunitdir)
foreach target : unit.get('symlinks', [])
if target.endswith('/')
install_emptydir(userunitdir / target)
meson.add_install_script(sh, '-c',
ln_s.format(userunitdir / file,
userunitdir / target / file))
else
meson.add_install_script(sh, '-c',
ln_s.format(userunitdir / file,
userunitdir / target))
endif
endforeach
endif
endforeach endforeach

View File

@ -0,0 +1,20 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Query the User Interactively for a Password
Documentation=man:systemd-ask-password(1)
DefaultDependencies=no
Before=sockets.target
[Socket]
ListenStream=%t/systemd/io.systemd.AskPassword
FileDescriptorName=varlink
SocketMode=0600
Accept=yes

View File

@ -0,0 +1,16 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Query the User Interactively for a Password
Documentation=man:systemd-ask-password(1)
DefaultDependencies=no
[Service]
ExecStart=-systemd-ask-password --user --no-tty