From 107986a33e8fc356ddeda256d730921836f4b521 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 7 Nov 2024 11:49:33 +0100 Subject: [PATCH 01/11] sd-varlink: expose api to get input/output fd separately --- src/libsystemd/libsystemd.sym | 5 ++++- src/libsystemd/sd-varlink/sd-varlink.c | 24 ++++++++++++++++++++++++ src/systemd/sd-varlink.h | 2 ++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index d2747537e6..ccb9398bcd 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -1062,9 +1062,12 @@ global: LIBSYSTEMD_258 { global: + sd_device_enumerator_add_all_parents; + sd_device_enumerator_add_all_parents; sd_json_variant_type_from_string; sd_json_variant_type_to_string; + sd_varlink_get_input_fd; + sd_varlink_get_output_fd; sd_varlink_reset_fds; sd_varlink_server_listen_name; - sd_device_enumerator_add_all_parents; } LIBSYSTEMD_257; diff --git a/src/libsystemd/sd-varlink/sd-varlink.c b/src/libsystemd/sd-varlink/sd-varlink.c index aa5aaf8df8..59228cc08b 100644 --- a/src/libsystemd/sd-varlink/sd-varlink.c +++ b/src/libsystemd/sd-varlink/sd-varlink.c @@ -1673,6 +1673,30 @@ _public_ int sd_varlink_get_fd(sd_varlink *v) { 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) { int ret = 0; diff --git a/src/systemd/sd-varlink.h b/src/systemd/sd-varlink.h index 083be6598d..223d02f4d7 100644 --- a/src/systemd/sd-varlink.h +++ b/src/systemd/sd-varlink.h @@ -96,6 +96,8 @@ sd_varlink* sd_varlink_ref(sd_varlink *link); sd_varlink* sd_varlink_unref(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_timeout(sd_varlink *v, uint64_t *ret); From 1a2e4b614e2f286db4fd8e440059c97ac049d836 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 6 Nov 2024 11:58:56 +0100 Subject: [PATCH 02/11] pcrextend: split out varlink server setup into its own function --- src/pcrextend/pcrextend.c | 48 ++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/src/pcrextend/pcrextend.c b/src/pcrextend/pcrextend.c index ef4d1a1f17..54c31c3a82 100644 --- a/src/pcrextend/pcrextend.c +++ b/src/pcrextend/pcrextend.c @@ -297,6 +297,29 @@ static int vl_method_extend(sd_varlink *link, sd_json_variant *parameters, sd_va return sd_varlink_reply(link, NULL); } +static int vl_server(void) { + _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL; + int r; + + r = varlink_server_new(&varlink_server, SD_VARLINK_SERVER_ROOT_ONLY, /* userdata= */ NULL); + 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_PCRExtend); + if (r < 0) + return log_error_errno(r, "Failed to add Varlink interface: %m"); + + r = sd_varlink_server_bind_method(varlink_server, "io.systemd.PCRExtend.Extend", vl_method_extend); + 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[]) { _cleanup_free_ char *word = NULL; Tpm2UserspaceEventType event; @@ -308,29 +331,8 @@ static int run(int argc, char *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) - return log_error_errno(r, "Failed to allocate Varlink server: %m"); - - r = sd_varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_PCRExtend); - if (r < 0) - return log_error_errno(r, "Failed to add Varlink interface: %m"); - - r = sd_varlink_server_bind_method(varlink_server, "io.systemd.PCRExtend.Extend", vl_method_extend); - 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 EXIT_SUCCESS; - } + if (arg_varlink) + return vl_server(); /* Invocation as Varlink service */ if (arg_file_system) { if (optind != argc) From 49187cfc3499b826064d02457182d1f552482f90 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 6 Nov 2024 23:38:01 +0100 Subject: [PATCH 03/11] ask-password-agent: optionally read pw to reply from env var --- .../tty-ask-password-agent.c | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index 8b83212755..4544ee8fa7 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -243,20 +243,28 @@ static int process_one_password_file(const char *filename, FILE *f) { SET_FLAG(flags, ASK_PASSWORD_ECHO, echo); SET_FLAG(flags, ASK_PASSWORD_SILENT, silent); - if (arg_plymouth) { - AskPasswordRequest req = { - .message = message, - }; + /* 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) { + AskPasswordRequest req = { + .message = message, + }; - r = ask_password_plymouth(&req, not_after, flags, filename, &passwords); - } else - r = agent_ask_password_tty(message, not_after, flags, filename, &passwords); - if (r < 0) { - /* If the query went away, that's OK */ - if (IN_SET(r, -ETIME, -ENOENT)) - return 0; + r = ask_password_plymouth(&req, not_after, flags, filename, &passwords); + } else + r = agent_ask_password_tty(message, not_after, flags, filename, &passwords); + if (r < 0) { + /* If the query went away, that's OK */ + if (IN_SET(r, -ETIME, -ENOENT)) + return 0; - return log_error_errno(r, "Failed to query password: %m"); + return log_error_errno(r, "Failed to query password: %m"); + } } assert(!strv_isempty(passwords)); From 254649d5762540ade590909c70c27af86c7dfdac Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 6 Nov 2024 23:38:17 +0100 Subject: [PATCH 04/11] ask-password-agent: send READY=1 when we established inotify watch --- src/tty-ask-password-agent/tty-ask-password-agent.c | 4 ++++ units/systemd-ask-password-console.service | 1 + units/systemd-ask-password-wall.service | 1 + 3 files changed, 6 insertions(+) diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index 4544ee8fa7..214687673f 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -21,6 +21,7 @@ #include "build.h" #include "conf-parser.h" #include "constants.h" +#include "daemon-util.h" #include "devnum-util.h" #include "dirent-util.h" #include "exit-status.h" @@ -393,6 +394,9 @@ static int process_and_watch_password_files(bool watch) { 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 (;;) { usec_t timeout = USEC_INFINITY; diff --git a/units/systemd-ask-password-console.service b/units/systemd-ask-password-console.service index afd0f0baad..b8066da851 100644 --- a/units/systemd-ask-password-console.service +++ b/units/systemd-ask-password-console.service @@ -22,4 +22,5 @@ Before=shutdown.target initrd-switch-root.target [Service] ExecStart=systemd-tty-ask-password-agent --watch --console +Type=notify SystemCallArchitectures=native diff --git a/units/systemd-ask-password-wall.service b/units/systemd-ask-password-wall.service index 18b59d99a7..c785614e1e 100644 --- a/units/systemd-ask-password-wall.service +++ b/units/systemd-ask-password-wall.service @@ -15,4 +15,5 @@ After=systemd-user-sessions.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 ExecStart=systemd-tty-ask-password-agent --wall +Type=notify SystemCallArchitectures=native From 4ff3689ad2ea7aad4aa5e5428238b447baea807e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 7 Nov 2024 10:00:53 +0100 Subject: [PATCH 05/11] ask-password-api: move 'flag_file' from function parameter into AskPasswordRequest structure --- src/firstboot/firstboot.c | 4 +-- src/shared/ask-password-api.c | 25 ++++++++++--------- src/shared/ask-password-api.h | 5 ++-- src/test/test-ask-password-api.c | 2 +- .../tty-ask-password-agent.c | 6 +++-- 5 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 9be62b8df3..bf9b840a23 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -738,7 +738,7 @@ static int prompt_root_password(int rfd) { .message = msg1, }; - r = ask_password_tty(-EBADF, &req, /* until= */ 0, /* flags= */ 0, /* flag_file= */ NULL, &a); + r = ask_password_tty(-EBADF, &req, /* until= */ 0, /* flags= */ 0, &a); if (r < 0) return log_error_errno(r, "Failed to query root password: %m"); if (strv_length(a) != 1) @@ -760,7 +760,7 @@ static int prompt_root_password(int rfd) { req.message = msg2; - r = ask_password_tty(-EBADF, &req, /* until= */ 0, /* flags= */ 0, /* flag_file= */ NULL, &b); + r = ask_password_tty(-EBADF, &req, /* until= */ 0, /* flags= */ 0, &b); if (r < 0) return log_error_errno(r, "Failed to query root password: %m"); if (strv_length(b) != 1) diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index 2e49096f82..e8ba9a2c36 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -311,7 +311,6 @@ int ask_password_plymouth( const AskPasswordRequest *req, usec_t until, AskPasswordFlags flags, - const char *flag_file, char ***ret) { _cleanup_close_ int fd = -EBADF, inotify_fd = -EBADF; @@ -328,12 +327,12 @@ int ask_password_plymouth( const char *message = req && req->message ? req->message : "Password:"; - if (flag_file) { + if (req->flag_file) { inotify_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK); if (inotify_fd < 0) 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; } @@ -375,7 +374,7 @@ int ask_password_plymouth( else 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; r = ppoll_usec(pollfd, n_pollfd, timeout); @@ -468,7 +467,6 @@ int ask_password_tty( const AskPasswordRequest *req, usec_t until, AskPasswordFlags flags, - const char *flag_file, char ***ret) { bool reset_tty = false, dirty = false, use_color = false, press_tab_visible = false; @@ -493,15 +491,14 @@ int ask_password_tty( if (!FLAGS_SET(flags, ASK_PASSWORD_HIDE_EMOJI) && emoji_enabled()) 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); if (inotify_fd < 0) return -errno; } - if (flag_file) { - if (inotify_add_watch(inotify_fd, flag_file, IN_ATTRIB /* for the link count */) < 0) + if (req->flag_file) + if (inotify_add_watch(inotify_fd, req->flag_file, IN_ATTRIB /* for the link count */) < 0) return -errno; - } if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && req && keyring) { r = ask_password_keyring(req, flags, ret); if (r >= 0) @@ -590,8 +587,8 @@ int ask_password_tty( else timeout = USEC_INFINITY; - if (flag_file) { - r = RET_NERRNO(access(flag_file, F_OK)); + if (req->flag_file) { + r = RET_NERRNO(access(req->flag_file, F_OK)); if (r < 0) goto finish; } @@ -820,6 +817,10 @@ int ask_password_agent( if (FLAGS_SET(flags, ASK_PASSWORD_NO_AGENT)) 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(sigset_add_many(&mask, SIGINT, SIGTERM) >= 0); assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) >= 0); @@ -1127,7 +1128,7 @@ int ask_password_auto( } 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(-EBADF, req, until, flags, ret); if (!FLAGS_SET(flags, ASK_PASSWORD_NO_AGENT)) return ask_password_agent(req, until, flags, ret); diff --git a/src/shared/ask-password-api.h b/src/shared/ask-password-api.h index b3cb407e51..c8763700c8 100644 --- a/src/shared/ask-password-api.h +++ b/src/shared/ask-password-api.h @@ -26,10 +26,11 @@ typedef struct AskPasswordRequest { const char *icon; /* freedesktop icon spec name */ const char *id; /* some identifier used for this prompt for the "ask-password" protocol */ const char *credential; /* $CREDENTIALS_DIRECTORY credential name */ + const char *flag_file; /* Once this flag file disappears abort the query */ } 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_plymouth(const AskPasswordRequest *req, usec_t until, AskPasswordFlags flags, const char *flag_file, char ***ret); +int ask_password_tty(int tty_fd, const AskPasswordRequest *req, usec_t until, AskPasswordFlags flags, char ***ret); +int ask_password_plymouth(const AskPasswordRequest *req, usec_t until, AskPasswordFlags flags, char ***ret); int ask_password_agent(const AskPasswordRequest *req, usec_t until, AskPasswordFlags flag, char ***ret); int ask_password_auto(const AskPasswordRequest *req, usec_t until, AskPasswordFlags flag, char ***ret); diff --git a/src/test/test-ask-password-api.c b/src/test/test-ask-password-api.c index e58e86870b..efd19696c8 100644 --- a/src/test/test-ask-password-api.c +++ b/src/test/test-ask-password-api.c @@ -13,7 +13,7 @@ TEST(ask_password) { .keyring = "da key", }; - r = ask_password_tty(-EBADF, &req, /* until= */ 0, /* flags= */ ASK_PASSWORD_CONSOLE_COLOR, /* flag_file= */ NULL, &ret); + r = ask_password_tty(-EBADF, &req, /* until= */ 0, /* flags= */ ASK_PASSWORD_CONSOLE_COLOR, &ret); if (r == -ECANCELED) ASSERT_NULL(ret); else { diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index 214687673f..3c38fbbd41 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -149,9 +149,10 @@ static int agent_ask_password_tty( AskPasswordRequest req = { .message = message, + .flag_file = flag_file, }; - r = ask_password_tty(tty_fd, &req, until, flags, flag_file, ret); + r = ask_password_tty(tty_fd, &req, until, flags, ret); if (arg_console) { assert(tty_fd >= 0); @@ -254,9 +255,10 @@ static int process_one_password_file(const char *filename, FILE *f) { if (arg_plymouth) { AskPasswordRequest req = { .message = message, + .flag_file = filename, }; - r = ask_password_plymouth(&req, not_after, flags, filename, &passwords); + r = ask_password_plymouth(&req, not_after, flags, &passwords); } else r = agent_ask_password_tty(message, not_after, flags, filename, &passwords); if (r < 0) { From 72068d9d2500e0ea127994f26a5dcadf5666b65e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 7 Nov 2024 10:30:34 +0100 Subject: [PATCH 06/11] ask-password-api: move tty_fd into AskPasswordRequest structure, too --- src/ask-password/ask-password.c | 1 + src/bootctl/bootctl-install.c | 1 + src/cryptenroll/cryptenroll-password.c | 2 ++ src/cryptenroll/cryptenroll-tpm2.c | 1 + src/cryptsetup/cryptsetup.c | 2 ++ src/firstboot/firstboot.c | 5 +++-- src/home/homectl.c | 4 ++++ src/keyutil/keyutil.c | 2 ++ src/measure/measure.c | 1 + src/pcrlock/pcrlock.c | 1 + src/repart/repart.c | 1 + src/sbsign/sbsign.c | 1 + src/shared/ask-password-api.c | 8 +++++--- src/shared/ask-password-api.h | 3 ++- src/shared/cryptsetup-fido2.c | 1 + src/shared/cryptsetup-tpm2.c | 1 + src/shared/dissect-image.c | 1 + src/shared/libfido2-util.c | 1 + src/shared/pkcs11-util.c | 1 + src/test/test-ask-password-api.c | 3 ++- src/tty-ask-password-agent/tty-ask-password-agent.c | 4 +++- 21 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/ask-password/ask-password.c b/src/ask-password/ask-password.c index 59eb7acddd..17e5fa4dc1 100644 --- a/src/ask-password/ask-password.c +++ b/src/ask-password/ask-password.c @@ -252,6 +252,7 @@ static int run(int argc, char *argv[]) { timeout = arg_timeout > 0 ? usec_add(now(CLOCK_MONOTONIC), arg_timeout) : 0; AskPasswordRequest req = { + .tty_fd = -EBADF, .message = arg_message, .icon = arg_icon, .id = arg_id, diff --git a/src/bootctl/bootctl-install.c b/src/bootctl/bootctl-install.c index 7ad264d882..0a5b59a503 100644 --- a/src/bootctl/bootctl-install.c +++ b/src/bootctl/bootctl-install.c @@ -984,6 +984,7 @@ int verb_install(int argc, char *argv[], void *userdata) { arg_private_key_source, arg_private_key, &(AskPasswordRequest) { + .tty_fd = -EBADF, .id = "bootctl-private-key-pin", .keyring = arg_private_key, .credential = "bootctl.private-key-pin", diff --git a/src/cryptenroll/cryptenroll-password.c b/src/cryptenroll/cryptenroll-password.c index f6b53d4a76..0560eddb66 100644 --- a/src/cryptenroll/cryptenroll-password.c +++ b/src/cryptenroll/cryptenroll-password.c @@ -55,6 +55,7 @@ int load_volume_key_password( return log_oom(); AskPasswordRequest req = { + .tty_fd = -EBADF, .message = question, .icon = "drive-harddisk", .id = id, @@ -130,6 +131,7 @@ int enroll_password( return log_oom(); AskPasswordRequest req = { + .tty_fd = -EBADF, .icon = "drive-harddisk", .id = id, .keyring = "cryptenroll", diff --git a/src/cryptenroll/cryptenroll-tpm2.c b/src/cryptenroll/cryptenroll-tpm2.c index ca163ef3c2..a5750427dc 100644 --- a/src/cryptenroll/cryptenroll-tpm2.c +++ b/src/cryptenroll/cryptenroll-tpm2.c @@ -119,6 +119,7 @@ static int get_pin(char **ret_pin_str, TPM2Flags *ret_flags) { SYNTHETIC_ERRNO(ENOKEY), "Too many attempts, giving up."); AskPasswordRequest req = { + .tty_fd = -EBADF, .message = "Please enter TPM2 PIN:", .icon = "drive-harddisk", .keyring = "tpm2-pin", diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 1da9e21d8e..97acb03baf 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -906,6 +906,7 @@ static int get_password( return log_oom(); AskPasswordRequest req = { + .tty_fd = -EBADF, .message = text, .icon = "drive-harddisk", .id = id, @@ -1422,6 +1423,7 @@ static int crypt_activate_by_token_pin_ask_password( pins = strv_free_erase(pins); AskPasswordRequest req = { + .tty_fd = -EBADF, .message = message, .icon = "drive-harddisk", .keyring = keyring, diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index bf9b840a23..08c80f539a 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -735,10 +735,11 @@ static int prompt_root_password(int rfd) { _cleanup_free_ char *error = NULL; AskPasswordRequest req = { + .tty_fd = -EBADF, .message = msg1, }; - r = ask_password_tty(-EBADF, &req, /* until= */ 0, /* flags= */ 0, &a); + r = ask_password_tty(&req, /* until= */ 0, /* flags= */ 0, &a); if (r < 0) return log_error_errno(r, "Failed to query root password: %m"); if (strv_length(a) != 1) @@ -760,7 +761,7 @@ static int prompt_root_password(int rfd) { req.message = msg2; - r = ask_password_tty(-EBADF, &req, /* until= */ 0, /* flags= */ 0, &b); + r = ask_password_tty(&req, /* until= */ 0, /* flags= */ 0, &b); if (r < 0) return log_error_errno(r, "Failed to query root password: %m"); if (strv_length(b) != 1) diff --git a/src/home/homectl.c b/src/home/homectl.c index b3aacbcbcf..fbb38b64cf 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -263,6 +263,7 @@ static int acquire_existing_password( return log_oom(); AskPasswordRequest req = { + .tty_fd = -EBADF, .message = question, .icon = "user-home", .keyring = "home-password", @@ -321,6 +322,7 @@ static int acquire_recovery_key( return log_oom(); AskPasswordRequest req = { + .tty_fd = -EBADF, .message = question, .icon = "user-home", .keyring = "home-recovery-key", @@ -375,6 +377,7 @@ static int acquire_token_pin( return log_oom(); AskPasswordRequest req = { + .tty_fd = -EBADF, .message = question, .icon = "user-home", .keyring = "token-pin", @@ -1229,6 +1232,7 @@ static int acquire_new_password( return log_oom(); AskPasswordRequest req = { + .tty_fd = -EBADF, .message = question, .icon = "user-home", .keyring = "home-password", diff --git a/src/keyutil/keyutil.c b/src/keyutil/keyutil.c index 70176c76c7..b034c1c40f 100644 --- a/src/keyutil/keyutil.c +++ b/src/keyutil/keyutil.c @@ -182,6 +182,7 @@ static int verb_validate(int argc, char *argv[], void *userdata) { arg_private_key_source, arg_private_key, &(AskPasswordRequest) { + .tty_fd = -EBADF, .id = "keyutil-private-key-pin", .keyring = arg_private_key, .credential = "keyutil.private-key-pin", @@ -238,6 +239,7 @@ static int verb_public(int argc, char *argv[], void *userdata) { arg_private_key_source, arg_private_key, &(AskPasswordRequest) { + .tty_fd = -EBADF, .id = "keyutil-private-key-pin", .keyring = arg_private_key, .credential = "keyutil.private-key-pin", diff --git a/src/measure/measure.c b/src/measure/measure.c index 22dd183125..c4ca9dc8da 100644 --- a/src/measure/measure.c +++ b/src/measure/measure.c @@ -888,6 +888,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) { arg_private_key_source, arg_private_key, &(AskPasswordRequest) { + .tty_fd = -EBADF, .id = "measure-private-key-pin", .keyring = arg_private_key, .credential = "measure.private-key-pin", diff --git a/src/pcrlock/pcrlock.c b/src/pcrlock/pcrlock.c index 9562bf2a89..bbf2ae856c 100644 --- a/src/pcrlock/pcrlock.c +++ b/src/pcrlock/pcrlock.c @@ -4551,6 +4551,7 @@ static int make_policy(bool force, RecoveryPinMode recovery_pin_mode) { _cleanup_(strv_free_erasep) char **l = NULL; AskPasswordRequest req = { + .tty_fd = -EBADF, .message = "Recovery PIN", .id = "pcrlock-recovery-pin", .credential = "pcrlock.recovery-pin", diff --git a/src/repart/repart.c b/src/repart/repart.c index 1a88185c47..a89810fbf5 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -8572,6 +8572,7 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY * arg_private_key_source, arg_private_key, &(AskPasswordRequest) { + .tty_fd = -EBADF, .id = "repart-private-key-pin", .keyring = arg_private_key, .credential = "repart.private-key-pin", diff --git a/src/sbsign/sbsign.c b/src/sbsign/sbsign.c index d17fdfea5e..538c2ebe63 100644 --- a/src/sbsign/sbsign.c +++ b/src/sbsign/sbsign.c @@ -203,6 +203,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) { arg_private_key_source, arg_private_key, &(AskPasswordRequest) { + .tty_fd = -EBADF, .id = "sbsign-private-key-pin", .keyring = arg_private_key, .credential = "sbsign.private-key-pin", diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index e8ba9a2c36..f1e2b06c3d 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -463,7 +463,6 @@ int ask_password_plymouth( #define SKIPPED "(skipped)" int ask_password_tty( - int ttyfd, const AskPasswordRequest *req, usec_t until, AskPasswordFlags flags, @@ -526,8 +525,11 @@ int ask_password_tty( CLEANUP_ERASE(passphrase); /* 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); + else + ttyfd = req->tty_fd; if (ttyfd >= 0) { if (tcgetattr(ttyfd, &old_termios) < 0) @@ -1128,7 +1130,7 @@ int ask_password_auto( } if (!FLAGS_SET(flags, ASK_PASSWORD_NO_TTY) && isatty_safe(STDIN_FILENO)) - return ask_password_tty(-EBADF, req, until, flags, ret); + return ask_password_tty(req, until, flags, ret); if (!FLAGS_SET(flags, ASK_PASSWORD_NO_AGENT)) return ask_password_agent(req, until, flags, ret); diff --git a/src/shared/ask-password-api.h b/src/shared/ask-password-api.h index c8763700c8..1abaad2134 100644 --- a/src/shared/ask-password-api.h +++ b/src/shared/ask-password-api.h @@ -27,9 +27,10 @@ typedef struct AskPasswordRequest { const char *id; /* some identifier used for this prompt for the "ask-password" protocol */ 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) */ } AskPasswordRequest; -int ask_password_tty(int tty_fd, const AskPasswordRequest *req, usec_t until, AskPasswordFlags flags, char ***ret); +int ask_password_tty(const AskPasswordRequest *req, usec_t until, AskPasswordFlags flags, char ***ret); int ask_password_plymouth(const AskPasswordRequest *req, usec_t until, AskPasswordFlags flags, char ***ret); int ask_password_agent(const AskPasswordRequest *req, usec_t until, AskPasswordFlags flag, char ***ret); int ask_password_auto(const AskPasswordRequest *req, usec_t until, AskPasswordFlags flag, char ***ret); diff --git a/src/shared/cryptsetup-fido2.c b/src/shared/cryptsetup-fido2.c index 1e1ef6dec0..6e4b120ae6 100644 --- a/src/shared/cryptsetup-fido2.c +++ b/src/shared/cryptsetup-fido2.c @@ -112,6 +112,7 @@ int acquire_fido2_key( return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "PIN querying disabled via 'headless' option. Use the '$PIN' environment variable."); static const AskPasswordRequest req = { + .tty_fd = -EBADF, .message = "Please enter security token PIN:", .icon = "drive-harddisk", .keyring = "fido2-pin", diff --git a/src/shared/cryptsetup-tpm2.c b/src/shared/cryptsetup-tpm2.c index bc4fb100bb..05f0fa2bfa 100644 --- a/src/shared/cryptsetup-tpm2.c +++ b/src/shared/cryptsetup-tpm2.c @@ -35,6 +35,7 @@ static int get_pin( "Use the '$PIN' environment variable."); AskPasswordRequest req = { + .tty_fd = -EBADF, .message = "Please enter TPM2 PIN:", .icon = "drive-harddisk", .keyring = "tpm2-pin", diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 39c5c8a21a..5a65f55fca 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -3077,6 +3077,7 @@ int dissected_image_decrypt_interactively( z = strv_free_erase(z); static const AskPasswordRequest req = { + .tty_fd = -EBADF, .message = "Please enter image passphrase:", .id = "dissect", .keyring = "dissect", diff --git a/src/shared/libfido2-util.c b/src/shared/libfido2-util.c index b518dec7fa..8e981f4c84 100644 --- a/src/shared/libfido2-util.c +++ b/src/shared/libfido2-util.c @@ -857,6 +857,7 @@ int fido2_generate_hmac_hash( for (;;) { _cleanup_strv_free_erase_ char **pin = NULL; AskPasswordRequest req = { + .tty_fd = -EBADF, .message = "Please enter security token PIN:", .icon = askpw_icon, .keyring = "fido2-pin", diff --git a/src/shared/pkcs11-util.c b/src/shared/pkcs11-util.c index c7fa3eccb2..caec1606ed 100644 --- a/src/shared/pkcs11-util.c +++ b/src/shared/pkcs11-util.c @@ -380,6 +380,7 @@ int pkcs11_token_login( return log_oom(); AskPasswordRequest req = { + .tty_fd = -EBADF, .message = text, .icon = askpw_icon, .id = id, diff --git a/src/test/test-ask-password-api.c b/src/test/test-ask-password-api.c index efd19696c8..ccf3cee9f3 100644 --- a/src/test/test-ask-password-api.c +++ b/src/test/test-ask-password-api.c @@ -9,11 +9,12 @@ TEST(ask_password) { int r; static const AskPasswordRequest req = { + .tty_fd = -EBADF, .message = "hello?", .keyring = "da key", }; - r = ask_password_tty(-EBADF, &req, /* until= */ 0, /* flags= */ ASK_PASSWORD_CONSOLE_COLOR, &ret); + r = ask_password_tty(&req, /* until= */ 0, /* flags= */ ASK_PASSWORD_CONSOLE_COLOR, &ret); if (r == -ECANCELED) ASSERT_NULL(ret); else { diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index 3c38fbbd41..fdc819754b 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -148,11 +148,12 @@ static int agent_ask_password_tty( } AskPasswordRequest req = { + .tty_fd = tty_fd, .message = message, .flag_file = flag_file, }; - r = ask_password_tty(tty_fd, &req, until, flags, ret); + r = ask_password_tty(&req, until, flags, ret); if (arg_console) { assert(tty_fd >= 0); @@ -254,6 +255,7 @@ static int process_one_password_file(const char *filename, FILE *f) { } else { if (arg_plymouth) { AskPasswordRequest req = { + .tty_fd = -EBADF, .message = message, .flag_file = filename, }; From c4a02a526d46151905906808aef80e0b53a3853e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 7 Nov 2024 10:41:56 +0100 Subject: [PATCH 07/11] ask-password-api: move timeout into AskPasswordRequest structure too --- src/ask-password/ask-password.c | 3 ++- src/bootctl/bootctl-install.c | 1 + src/cryptenroll/cryptenroll-password.c | 8 ++++--- src/cryptenroll/cryptenroll-tpm2.c | 3 +-- src/cryptsetup/cryptsetup.c | 8 ++++--- src/firstboot/firstboot.c | 5 +++-- src/home/homectl.c | 12 +++++----- src/keyutil/keyutil.c | 2 ++ src/measure/measure.c | 1 + src/pcrlock/pcrlock.c | 2 +- src/repart/repart.c | 1 + src/sbsign/sbsign.c | 1 + src/shared/ask-password-api.c | 22 ++++++++----------- src/shared/ask-password-api.h | 9 ++++---- src/shared/cryptsetup-fido2.c | 5 +++-- src/shared/cryptsetup-tpm2.c | 3 ++- src/shared/dissect-image.c | 3 ++- src/shared/libfido2-util.c | 3 ++- src/shared/openssl-util.c | 2 +- src/shared/pkcs11-util.c | 3 ++- src/test/test-ask-password-api.c | 3 ++- .../tty-ask-password-agent.c | 6 +++-- 22 files changed, 62 insertions(+), 44 deletions(-) diff --git a/src/ask-password/ask-password.c b/src/ask-password/ask-password.c index 17e5fa4dc1..d3afc37781 100644 --- a/src/ask-password/ask-password.c +++ b/src/ask-password/ask-password.c @@ -258,9 +258,10 @@ static int run(int argc, char *argv[]) { .id = arg_id, .keyring = arg_key_name, .credential = arg_credential_name ?: "password", + .until = timeout, }; - r = ask_password_auto(&req, timeout, arg_flags, &l); + r = ask_password_auto(&req, arg_flags, &l); if (r < 0) return log_error_errno(r, "Failed to query password: %m"); diff --git a/src/bootctl/bootctl-install.c b/src/bootctl/bootctl-install.c index 0a5b59a503..76deba56e7 100644 --- a/src/bootctl/bootctl-install.c +++ b/src/bootctl/bootctl-install.c @@ -988,6 +988,7 @@ int verb_install(int argc, char *argv[], void *userdata) { .id = "bootctl-private-key-pin", .keyring = arg_private_key, .credential = "bootctl.private-key-pin", + .until = USEC_INFINITY, }, &private_key, &ui); diff --git a/src/cryptenroll/cryptenroll-password.c b/src/cryptenroll/cryptenroll-password.c index 0560eddb66..56a3bb4456 100644 --- a/src/cryptenroll/cryptenroll-password.c +++ b/src/cryptenroll/cryptenroll-password.c @@ -61,6 +61,7 @@ int load_volume_key_password( .id = id, .keyring = "cryptenroll", .credential = "cryptenroll.passphrase", + .until = USEC_INFINITY, }; for (;;) { @@ -70,7 +71,7 @@ int load_volume_key_password( return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), "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) return log_error_errno(r, "Failed to query password: %m"); @@ -136,6 +137,7 @@ int enroll_password( .id = id, .keyring = "cryptenroll", .credential = "cryptenroll.new-passphrase", + .until = USEC_INFINITY, }; for (;;) { @@ -152,7 +154,7 @@ int enroll_password( req.message = question; - r = ask_password_auto(&req, USEC_INFINITY, /* flags= */ 0, &passwords); + r = ask_password_auto(&req, /* flags= */ 0, &passwords); if (r < 0) return log_error_errno(r, "Failed to query password: %m"); @@ -165,7 +167,7 @@ int enroll_password( req.message = question; - r = ask_password_auto(&req, USEC_INFINITY, /* flags= */ 0, &passwords2); + r = ask_password_auto(&req, /* flags= */ 0, &passwords2); if (r < 0) return log_error_errno(r, "Failed to query password: %m"); diff --git a/src/cryptenroll/cryptenroll-tpm2.c b/src/cryptenroll/cryptenroll-tpm2.c index a5750427dc..42d5a9cd03 100644 --- a/src/cryptenroll/cryptenroll-tpm2.c +++ b/src/cryptenroll/cryptenroll-tpm2.c @@ -124,12 +124,12 @@ static int get_pin(char **ret_pin_str, TPM2Flags *ret_flags) { .icon = "drive-harddisk", .keyring = "tpm2-pin", .credential = "cryptenroll.new-tpm2-pin", + .until = USEC_INFINITY, }; pin = strv_free_erase(pin); r = ask_password_auto( &req, - /* until= */ USEC_INFINITY, /* flags= */ 0, &pin); if (r < 0) @@ -140,7 +140,6 @@ static int get_pin(char **ret_pin_str, TPM2Flags *ret_flags) { r = ask_password_auto( &req, - USEC_INFINITY, /* flags= */ 0, &pin2); if (r < 0) diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 97acb03baf..cb3b591186 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -912,12 +912,13 @@ static int get_password( .id = id, .keyring = "cryptsetup", .credential = "cryptsetup.passphrase", + .until = until, }; if (ignore_cached) flags &= ~ASK_PASSWORD_ACCEPT_CACHED; - r = ask_password_auto(&req, until, flags, &passwords); + r = ask_password_auto(&req, flags, &passwords); if (r < 0) return log_error_errno(r, "Failed to query password: %m"); @@ -938,7 +939,7 @@ static int get_password( req.message = text; req.id = id; - r = ask_password_auto(&req, until, flags, &passwords2); + r = ask_password_auto(&req, flags, &passwords2); if (r < 0) return log_error_errno(r, "Failed to query verification password: %m"); @@ -1428,9 +1429,10 @@ static int crypt_activate_by_token_pin_ask_password( .icon = "drive-harddisk", .keyring = keyring, .credential = credential, + .until = until, }; - r = ask_password_auto(&req, until, flags, &pins); + r = ask_password_auto(&req, flags, &pins); if (r < 0) return r; diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 08c80f539a..5658255358 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -737,9 +737,10 @@ static int prompt_root_password(int rfd) { AskPasswordRequest req = { .tty_fd = -EBADF, .message = msg1, + .until = USEC_INFINITY, }; - r = ask_password_tty(&req, /* until= */ 0, /* flags= */ 0, &a); + r = ask_password_tty(&req, /* flags= */ 0, &a); if (r < 0) return log_error_errno(r, "Failed to query root password: %m"); if (strv_length(a) != 1) @@ -761,7 +762,7 @@ static int prompt_root_password(int rfd) { req.message = msg2; - r = ask_password_tty(&req, /* until= */ 0, /* flags= */ 0, &b); + r = ask_password_tty(&req, /* flags= */ 0, &b); if (r < 0) return log_error_errno(r, "Failed to query root password: %m"); if (strv_length(b) != 1) diff --git a/src/home/homectl.c b/src/home/homectl.c index fbb38b64cf..611fb966fe 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -268,9 +268,10 @@ static int acquire_existing_password( .icon = "user-home", .keyring = "home-password", .credential = "home.password", + .until = USEC_INFINITY, }; - 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 * disabled via the flags. Not an error for us. */ log_debug_errno(r, "No passwords acquired."); @@ -327,9 +328,10 @@ static int acquire_recovery_key( .icon = "user-home", .keyring = "home-recovery-key", .credential = "home.recovery-key", + .until = USEC_INFINITY, }; - 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 * disabled via the flags. Not an error for us. */ log_debug_errno(r, "No recovery keys acquired."); @@ -382,9 +384,10 @@ static int acquire_token_pin( .icon = "user-home", .keyring = "token-pin", .credential = "home.token-pin", + .until = USEC_INFINITY, }; - 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 * via the flags. Not an error for us. */ log_debug_errno(r, "No security token PINs acquired."); @@ -1237,11 +1240,11 @@ static int acquire_new_password( .icon = "user-home", .keyring = "home-password", .credential = "home.new-password", + .until = USEC_INFINITY, }; r = ask_password_auto( &req, - USEC_INFINITY, /* flags= */ 0, /* no caching, we want to collect a new password here after all */ &first); if (r < 0) @@ -1257,7 +1260,6 @@ static int acquire_new_password( r = ask_password_auto( &req, - USEC_INFINITY, /* flags= */ 0, /* no caching */ &second); if (r < 0) diff --git a/src/keyutil/keyutil.c b/src/keyutil/keyutil.c index b034c1c40f..bae7421572 100644 --- a/src/keyutil/keyutil.c +++ b/src/keyutil/keyutil.c @@ -186,6 +186,7 @@ static int verb_validate(int argc, char *argv[], void *userdata) { .id = "keyutil-private-key-pin", .keyring = arg_private_key, .credential = "keyutil.private-key-pin", + .until = USEC_INFINITY, }, &private_key, &ui); @@ -243,6 +244,7 @@ static int verb_public(int argc, char *argv[], void *userdata) { .id = "keyutil-private-key-pin", .keyring = arg_private_key, .credential = "keyutil.private-key-pin", + .until = USEC_INFINITY, }, &private_key, &ui); diff --git a/src/measure/measure.c b/src/measure/measure.c index c4ca9dc8da..fbd039da79 100644 --- a/src/measure/measure.c +++ b/src/measure/measure.c @@ -892,6 +892,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) { .id = "measure-private-key-pin", .keyring = arg_private_key, .credential = "measure.private-key-pin", + .until = USEC_INFINITY, }, &privkey, &ui); diff --git a/src/pcrlock/pcrlock.c b/src/pcrlock/pcrlock.c index bbf2ae856c..b22ee57788 100644 --- a/src/pcrlock/pcrlock.c +++ b/src/pcrlock/pcrlock.c @@ -4555,11 +4555,11 @@ static int make_policy(bool force, RecoveryPinMode recovery_pin_mode) { .message = "Recovery PIN", .id = "pcrlock-recovery-pin", .credential = "pcrlock.recovery-pin", + .until = USEC_INFINITY, }; r = ask_password_auto( &req, - /* until= */ 0, /* flags= */ 0, &l); if (r < 0) diff --git a/src/repart/repart.c b/src/repart/repart.c index a89810fbf5..b381d4808a 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -8576,6 +8576,7 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY * .id = "repart-private-key-pin", .keyring = arg_private_key, .credential = "repart.private-key-pin", + .until = USEC_INFINITY, }, &private_key, &ui); diff --git a/src/sbsign/sbsign.c b/src/sbsign/sbsign.c index 538c2ebe63..5c5c884c98 100644 --- a/src/sbsign/sbsign.c +++ b/src/sbsign/sbsign.c @@ -207,6 +207,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) { .id = "sbsign-private-key-pin", .keyring = arg_private_key, .credential = "sbsign.private-key-pin", + .until = USEC_INFINITY, }, &private_key, &ui); diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index f1e2b06c3d..39e6277b37 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -309,7 +309,6 @@ static int backspace_string(int ttyfd, const char *str) { int ask_password_plymouth( const AskPasswordRequest *req, - usec_t until, AskPasswordFlags flags, char ***ret) { @@ -369,8 +368,8 @@ int ask_password_plymouth( for (;;) { usec_t timeout; - if (until > 0) - timeout = usec_sub_unsigned(until, now(CLOCK_MONOTONIC)); + if (req->until > 0) + timeout = usec_sub_unsigned(req->until, now(CLOCK_MONOTONIC)); else timeout = USEC_INFINITY; @@ -464,7 +463,6 @@ int ask_password_plymouth( int ask_password_tty( const AskPasswordRequest *req, - usec_t until, AskPasswordFlags flags, char ***ret) { @@ -584,8 +582,8 @@ int ask_password_tty( usec_t timeout; ssize_t n; - if (until > 0) - timeout = usec_sub_unsigned(until, now(CLOCK_MONOTONIC)); + if (req->until > 0) + timeout = usec_sub_unsigned(req->until, now(CLOCK_MONOTONIC)); else timeout = USEC_INFINITY; @@ -799,7 +797,6 @@ static int create_socket(const char *askpwdir, char **ret) { int ask_password_agent( const AskPasswordRequest *req, - usec_t until, AskPasswordFlags flags, char ***ret) { @@ -894,7 +891,7 @@ int ask_password_agent( socket_name, FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED), FLAGS_SET(flags, ASK_PASSWORD_ECHO), - until, + req->until, FLAGS_SET(flags, ASK_PASSWORD_SILENT)); if (req) { @@ -946,8 +943,8 @@ int ask_password_agent( usec_t timeout; ssize_t n; - if (until > 0) - timeout = usec_sub_unsigned(until, now(CLOCK_MONOTONIC)); + if (req->until > 0) + timeout = usec_sub_unsigned(req->until, now(CLOCK_MONOTONIC)); else timeout = USEC_INFINITY; @@ -1106,7 +1103,6 @@ static int ask_password_credential(const AskPasswordRequest *req, AskPasswordFla int ask_password_auto( const AskPasswordRequest *req, - usec_t until, AskPasswordFlags flags, char ***ret) { @@ -1130,10 +1126,10 @@ int ask_password_auto( } if (!FLAGS_SET(flags, ASK_PASSWORD_NO_TTY) && isatty_safe(STDIN_FILENO)) - return ask_password_tty(req, until, flags, ret); + return ask_password_tty(req, flags, ret); 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; } diff --git a/src/shared/ask-password-api.h b/src/shared/ask-password-api.h index 1abaad2134..fe453522fc 100644 --- a/src/shared/ask-password-api.h +++ b/src/shared/ask-password-api.h @@ -28,11 +28,12 @@ typedef struct AskPasswordRequest { 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 (if zero: forever) */ } AskPasswordRequest; -int ask_password_tty(const AskPasswordRequest *req, usec_t until, AskPasswordFlags flags, char ***ret); -int ask_password_plymouth(const AskPasswordRequest *req, usec_t until, AskPasswordFlags flags, char ***ret); -int ask_password_agent(const AskPasswordRequest *req, usec_t until, AskPasswordFlags flag, char ***ret); -int ask_password_auto(const AskPasswordRequest *req, usec_t until, AskPasswordFlags flag, char ***ret); +int ask_password_tty(const AskPasswordRequest *req, AskPasswordFlags flags, char ***ret); +int ask_password_plymouth(const AskPasswordRequest *req, AskPasswordFlags flags, char ***ret); +int ask_password_agent(const AskPasswordRequest *req, AskPasswordFlags flag, char ***ret); +int ask_password_auto(const AskPasswordRequest *req, AskPasswordFlags flag, char ***ret); int acquire_user_ask_password_directory(char **ret); diff --git a/src/shared/cryptsetup-fido2.c b/src/shared/cryptsetup-fido2.c index 6e4b120ae6..070908d525 100644 --- a/src/shared/cryptsetup-fido2.c +++ b/src/shared/cryptsetup-fido2.c @@ -111,16 +111,17 @@ int acquire_fido2_key( 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."); - static const AskPasswordRequest req = { + AskPasswordRequest req = { .tty_fd = -EBADF, .message = "Please enter security token PIN:", .icon = "drive-harddisk", .keyring = "fido2-pin", .credential = "cryptsetup.fido2-pin", + .until = until, }; 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) return log_error_errno(r, "Failed to ask for user password: %m"); diff --git a/src/shared/cryptsetup-tpm2.c b/src/shared/cryptsetup-tpm2.c index 05f0fa2bfa..7a1275eb38 100644 --- a/src/shared/cryptsetup-tpm2.c +++ b/src/shared/cryptsetup-tpm2.c @@ -40,10 +40,11 @@ static int get_pin( .icon = "drive-harddisk", .keyring = "tpm2-pin", .credential = askpw_credential, + .until = until, }; 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) return log_error_errno(r, "Failed to ask for user pin: %m"); assert(strv_length(pin) == 1); diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 5a65f55fca..3feda770af 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -3082,9 +3082,10 @@ int dissected_image_decrypt_interactively( .id = "dissect", .keyring = "dissect", .credential = "dissect.passphrase", + .until = USEC_INFINITY, }; - r = ask_password_auto(&req, USEC_INFINITY, /* flags= */ 0, &z); + r = ask_password_auto(&req, /* flags= */ 0, &z); if (r < 0) return log_error_errno(r, "Failed to query for passphrase: %m"); diff --git a/src/shared/libfido2-util.c b/src/shared/libfido2-util.c index 8e981f4c84..200068dce4 100644 --- a/src/shared/libfido2-util.c +++ b/src/shared/libfido2-util.c @@ -862,9 +862,10 @@ int fido2_generate_hmac_hash( .icon = askpw_icon, .keyring = "fido2-pin", .credential = askpw_credential, + .until = USEC_INFINITY, }; - r = ask_password_auto(&req, USEC_INFINITY, /* flags= */ 0, &pin); + r = ask_password_auto(&req, /* flags= */ 0, &pin); if (r < 0) return log_error_errno(r, "Failed to acquire user PIN: %m"); diff --git a/src/shared/openssl-util.c b/src/shared/openssl-util.c index 417f842546..b60fc67b26 100644 --- a/src/shared/openssl-util.c +++ b/src/shared/openssl-util.c @@ -1399,7 +1399,7 @@ static int openssl_ask_password_ui_read(UI *ui, UI_STRING *uis) { req->message = UI_get0_output_string(uis); _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) { log_error_errno(r, "Failed to query for PIN: %m"); return 0; diff --git a/src/shared/pkcs11-util.c b/src/shared/pkcs11-util.c index caec1606ed..8d14b2f8f7 100644 --- a/src/shared/pkcs11-util.c +++ b/src/shared/pkcs11-util.c @@ -386,10 +386,11 @@ int pkcs11_token_login( .id = id, .keyring = askpw_keyring, .credential = askpw_credential, + .until = until, }; /* 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) return log_error_errno(r, "Failed to query PIN for security token '%s': %m", token_label); } diff --git a/src/test/test-ask-password-api.c b/src/test/test-ask-password-api.c index ccf3cee9f3..38afe0c6ef 100644 --- a/src/test/test-ask-password-api.c +++ b/src/test/test-ask-password-api.c @@ -12,9 +12,10 @@ TEST(ask_password) { .tty_fd = -EBADF, .message = "hello?", .keyring = "da key", + .until = USEC_INFINITY, }; - r = ask_password_tty(&req, /* until= */ 0, /* flags= */ ASK_PASSWORD_CONSOLE_COLOR, &ret); + r = ask_password_tty(&req, /* flags= */ ASK_PASSWORD_CONSOLE_COLOR, &ret); if (r == -ECANCELED) ASSERT_NULL(ret); else { diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index fdc819754b..31292d5acf 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -151,9 +151,10 @@ static int agent_ask_password_tty( .tty_fd = tty_fd, .message = message, .flag_file = flag_file, + .until = until, }; - r = ask_password_tty(&req, until, flags, ret); + r = ask_password_tty(&req, flags, ret); if (arg_console) { assert(tty_fd >= 0); @@ -258,9 +259,10 @@ static int process_one_password_file(const char *filename, FILE *f) { .tty_fd = -EBADF, .message = message, .flag_file = filename, + .until = not_after, }; - r = ask_password_plymouth(&req, not_after, flags, &passwords); + r = ask_password_plymouth(&req, flags, &passwords); } else r = agent_ask_password_tty(message, not_after, flags, filename, &passwords); if (r < 0) { From d66894a7a53f0eb26228074ef1b8dcca2a316f87 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 7 Nov 2024 11:38:23 +0100 Subject: [PATCH 08/11] ask-password-api: add new "hup_fd" field to AskPasswordReq This new field allows specification of an fd on which the password prompt logic will look for POLLHUP events for, and if seen will abort the query. The usecase for this is that when we query for a pw on behalf of a Varlink client we can abort the query automatically if the client dies. --- src/ask-password/ask-password.c | 1 + src/bootctl/bootctl-install.c | 1 + src/cryptenroll/cryptenroll-password.c | 2 + src/cryptenroll/cryptenroll-tpm2.c | 1 + src/cryptsetup/cryptsetup.c | 2 + src/firstboot/firstboot.c | 1 + src/home/homectl.c | 4 + src/keyutil/keyutil.c | 2 + src/measure/measure.c | 1 + src/pcrlock/pcrlock.c | 1 + src/repart/repart.c | 1 + src/sbsign/sbsign.c | 1 + src/shared/ask-password-api.c | 83 +++++++++++++++---- src/shared/ask-password-api.h | 1 + src/shared/cryptsetup-fido2.c | 1 + src/shared/cryptsetup-tpm2.c | 1 + src/shared/dissect-image.c | 1 + src/shared/libfido2-util.c | 1 + src/shared/pkcs11-util.c | 1 + src/test/test-ask-password-api.c | 1 + .../tty-ask-password-agent.c | 2 + 21 files changed, 96 insertions(+), 14 deletions(-) diff --git a/src/ask-password/ask-password.c b/src/ask-password/ask-password.c index d3afc37781..154aaa030e 100644 --- a/src/ask-password/ask-password.c +++ b/src/ask-password/ask-password.c @@ -259,6 +259,7 @@ static int run(int argc, char *argv[]) { .keyring = arg_key_name, .credential = arg_credential_name ?: "password", .until = timeout, + .hup_fd = -EBADF, }; r = ask_password_auto(&req, arg_flags, &l); diff --git a/src/bootctl/bootctl-install.c b/src/bootctl/bootctl-install.c index 76deba56e7..884521bcfa 100644 --- a/src/bootctl/bootctl-install.c +++ b/src/bootctl/bootctl-install.c @@ -989,6 +989,7 @@ int verb_install(int argc, char *argv[], void *userdata) { .keyring = arg_private_key, .credential = "bootctl.private-key-pin", .until = USEC_INFINITY, + .hup_fd = -EBADF, }, &private_key, &ui); diff --git a/src/cryptenroll/cryptenroll-password.c b/src/cryptenroll/cryptenroll-password.c index 56a3bb4456..c6888cc83b 100644 --- a/src/cryptenroll/cryptenroll-password.c +++ b/src/cryptenroll/cryptenroll-password.c @@ -62,6 +62,7 @@ int load_volume_key_password( .keyring = "cryptenroll", .credential = "cryptenroll.passphrase", .until = USEC_INFINITY, + .hup_fd = -EBADF, }; for (;;) { @@ -138,6 +139,7 @@ int enroll_password( .keyring = "cryptenroll", .credential = "cryptenroll.new-passphrase", .until = USEC_INFINITY, + .hup_fd = -EBADF, }; for (;;) { diff --git a/src/cryptenroll/cryptenroll-tpm2.c b/src/cryptenroll/cryptenroll-tpm2.c index 42d5a9cd03..fd03fecc62 100644 --- a/src/cryptenroll/cryptenroll-tpm2.c +++ b/src/cryptenroll/cryptenroll-tpm2.c @@ -125,6 +125,7 @@ static int get_pin(char **ret_pin_str, TPM2Flags *ret_flags) { .keyring = "tpm2-pin", .credential = "cryptenroll.new-tpm2-pin", .until = USEC_INFINITY, + .hup_fd = -EBADF, }; pin = strv_free_erase(pin); diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index cb3b591186..e5f21bbce7 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -913,6 +913,7 @@ static int get_password( .keyring = "cryptsetup", .credential = "cryptsetup.passphrase", .until = until, + .hup_fd = -EBADF, }; if (ignore_cached) @@ -1430,6 +1431,7 @@ static int crypt_activate_by_token_pin_ask_password( .keyring = keyring, .credential = credential, .until = until, + .hup_fd = -EBADF, }; r = ask_password_auto(&req, flags, &pins); diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 5658255358..15abb53be8 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -738,6 +738,7 @@ static int prompt_root_password(int rfd) { .tty_fd = -EBADF, .message = msg1, .until = USEC_INFINITY, + .hup_fd = -EBADF, }; r = ask_password_tty(&req, /* flags= */ 0, &a); diff --git a/src/home/homectl.c b/src/home/homectl.c index 611fb966fe..104c35454b 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -269,6 +269,7 @@ static int acquire_existing_password( .keyring = "home-password", .credential = "home.password", .until = USEC_INFINITY, + .hup_fd = -EBADF, }; r = ask_password_auto(&req, flags, &password); @@ -329,6 +330,7 @@ static int acquire_recovery_key( .keyring = "home-recovery-key", .credential = "home.recovery-key", .until = USEC_INFINITY, + .hup_fd = -EBADF, }; r = ask_password_auto(&req, flags, &recovery_key); @@ -385,6 +387,7 @@ static int acquire_token_pin( .keyring = "token-pin", .credential = "home.token-pin", .until = USEC_INFINITY, + .hup_fd = -EBADF, }; r = ask_password_auto(&req, flags, &pin); @@ -1241,6 +1244,7 @@ static int acquire_new_password( .keyring = "home-password", .credential = "home.new-password", .until = USEC_INFINITY, + .hup_fd = -EBADF, }; r = ask_password_auto( diff --git a/src/keyutil/keyutil.c b/src/keyutil/keyutil.c index bae7421572..23459e48f5 100644 --- a/src/keyutil/keyutil.c +++ b/src/keyutil/keyutil.c @@ -187,6 +187,7 @@ static int verb_validate(int argc, char *argv[], void *userdata) { .keyring = arg_private_key, .credential = "keyutil.private-key-pin", .until = USEC_INFINITY, + .hup_fd = -EBADF, }, &private_key, &ui); @@ -245,6 +246,7 @@ static int verb_public(int argc, char *argv[], void *userdata) { .keyring = arg_private_key, .credential = "keyutil.private-key-pin", .until = USEC_INFINITY, + .hup_fd = -EBADF, }, &private_key, &ui); diff --git a/src/measure/measure.c b/src/measure/measure.c index fbd039da79..18461797b7 100644 --- a/src/measure/measure.c +++ b/src/measure/measure.c @@ -893,6 +893,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) { .keyring = arg_private_key, .credential = "measure.private-key-pin", .until = USEC_INFINITY, + .hup_fd = -EBADF, }, &privkey, &ui); diff --git a/src/pcrlock/pcrlock.c b/src/pcrlock/pcrlock.c index b22ee57788..72b13f8fac 100644 --- a/src/pcrlock/pcrlock.c +++ b/src/pcrlock/pcrlock.c @@ -4556,6 +4556,7 @@ static int make_policy(bool force, RecoveryPinMode recovery_pin_mode) { .id = "pcrlock-recovery-pin", .credential = "pcrlock.recovery-pin", .until = USEC_INFINITY, + .hup_fd = -EBADF, }; r = ask_password_auto( diff --git a/src/repart/repart.c b/src/repart/repart.c index b381d4808a..7934a97435 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -8577,6 +8577,7 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY * .keyring = arg_private_key, .credential = "repart.private-key-pin", .until = USEC_INFINITY, + .hup_fd = -EBADF, }, &private_key, &ui); diff --git a/src/sbsign/sbsign.c b/src/sbsign/sbsign.c index 5c5c884c98..92b8284219 100644 --- a/src/sbsign/sbsign.c +++ b/src/sbsign/sbsign.c @@ -208,6 +208,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) { .keyring = arg_private_key, .credential = "sbsign.private-key-pin", .until = USEC_INFINITY, + .hup_fd = -EBADF, }, &private_key, &ui); diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index 39e6277b37..502b22ee89 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -355,15 +355,30 @@ int ask_password_plymouth( enum { POLL_SOCKET, - POLL_INOTIFY, /* Must be last, because optional */ + POLL_TWO, + POLL_THREE, _POLL_MAX, }; struct pollfd pollfd[_POLL_MAX] = { - [POLL_SOCKET] = { .fd = fd, .events = POLLIN }, - [POLL_INOTIFY] = { .fd = inotify_fd, .events = POLLIN }, + [POLL_SOCKET] = { + .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 (;;) { usec_t timeout; @@ -384,7 +399,10 @@ int ask_password_plymouth( if (r == 0) 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); if (pollfd[POLL_SOCKET].revents == 0) @@ -567,15 +585,31 @@ int ask_password_tty( enum { POLL_TTY, - POLL_INOTIFY, /* Must be last, because optional */ + POLL_TWO, + POLL_THREE, _POLL_MAX, }; struct pollfd pollfd[_POLL_MAX] = { - [POLL_TTY] = { .fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO, .events = POLLIN }, - [POLL_INOTIFY] = { .fd = inotify_fd, .events = POLLIN }, + [POLL_TTY] = { + .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 (;;) { _cleanup_(erase_char) char c; @@ -603,7 +637,12 @@ int ask_password_tty( 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); r = ask_password_keyring(req, flags, ret); @@ -924,16 +963,29 @@ int ask_password_agent( enum { POLL_SOCKET, POLL_SIGNAL, - POLL_INOTIFY, /* Must be last, because optional */ + POLL_THREE, + POLL_FOUR, _POLL_MAX }; struct pollfd pollfd[_POLL_MAX] = { [POLL_SOCKET] = { .fd = socket_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 (;;) { CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control; @@ -963,7 +1015,10 @@ int ask_password_agent( 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); if (req && req->keyring) { diff --git a/src/shared/ask-password-api.h b/src/shared/ask-password-api.h index fe453522fc..03c6d1abdb 100644 --- a/src/shared/ask-password-api.h +++ b/src/shared/ask-password-api.h @@ -28,6 +28,7 @@ typedef struct AskPasswordRequest { 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) */ + int hup_fd; /* An extra fd to watch for POLLHUP, in which case to abort the query */ usec_t until; /* CLOCK_MONOTONIC time until which to show the prompt (if zero: forever) */ } AskPasswordRequest; diff --git a/src/shared/cryptsetup-fido2.c b/src/shared/cryptsetup-fido2.c index 070908d525..8d4c179183 100644 --- a/src/shared/cryptsetup-fido2.c +++ b/src/shared/cryptsetup-fido2.c @@ -118,6 +118,7 @@ int acquire_fido2_key( .keyring = "fido2-pin", .credential = "cryptsetup.fido2-pin", .until = until, + .hup_fd = -EBADF, }; pins = strv_free_erase(pins); diff --git a/src/shared/cryptsetup-tpm2.c b/src/shared/cryptsetup-tpm2.c index 7a1275eb38..c1cd67bacd 100644 --- a/src/shared/cryptsetup-tpm2.c +++ b/src/shared/cryptsetup-tpm2.c @@ -41,6 +41,7 @@ static int get_pin( .keyring = "tpm2-pin", .credential = askpw_credential, .until = until, + .hup_fd = -EBADF, }; pin = strv_free_erase(pin); diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 3feda770af..86a549477c 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -3083,6 +3083,7 @@ int dissected_image_decrypt_interactively( .keyring = "dissect", .credential = "dissect.passphrase", .until = USEC_INFINITY, + .hup_fd = -EBADF, }; r = ask_password_auto(&req, /* flags= */ 0, &z); diff --git a/src/shared/libfido2-util.c b/src/shared/libfido2-util.c index 200068dce4..101641e7fe 100644 --- a/src/shared/libfido2-util.c +++ b/src/shared/libfido2-util.c @@ -863,6 +863,7 @@ int fido2_generate_hmac_hash( .keyring = "fido2-pin", .credential = askpw_credential, .until = USEC_INFINITY, + .hup_fd = -EBADF, }; r = ask_password_auto(&req, /* flags= */ 0, &pin); diff --git a/src/shared/pkcs11-util.c b/src/shared/pkcs11-util.c index 8d14b2f8f7..a38a91279d 100644 --- a/src/shared/pkcs11-util.c +++ b/src/shared/pkcs11-util.c @@ -387,6 +387,7 @@ int pkcs11_token_login( .keyring = askpw_keyring, .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 */ diff --git a/src/test/test-ask-password-api.c b/src/test/test-ask-password-api.c index 38afe0c6ef..d79c476489 100644 --- a/src/test/test-ask-password-api.c +++ b/src/test/test-ask-password-api.c @@ -13,6 +13,7 @@ TEST(ask_password) { .message = "hello?", .keyring = "da key", .until = USEC_INFINITY, + .hup_fd = -EBADF, }; r = ask_password_tty(&req, /* flags= */ ASK_PASSWORD_CONSOLE_COLOR, &ret); diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index 31292d5acf..094810dcb3 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -152,6 +152,7 @@ static int agent_ask_password_tty( .message = message, .flag_file = flag_file, .until = until, + .hup_fd = -EBADF, }; r = ask_password_tty(&req, flags, ret); @@ -260,6 +261,7 @@ static int process_one_password_file(const char *filename, FILE *f) { .message = message, .flag_file = filename, .until = not_after, + .hup_fd = -EBADF, }; r = ask_password_plymouth(&req, flags, &passwords); From 066f6bfb6278962e288cce2ba522a2e400980e7c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 6 Nov 2024 11:56:46 +0100 Subject: [PATCH 09/11] ask-password: add Varlink API for querying passwords from the user This turns systemd-ask-password into a small Varlink service, so that there's an standard IPC way to ask for a password. It mostly directly exposes the functionality of the Varlink service. --- src/ask-password/ask-password.c | 168 +++++++++++++++++- .../io.systemd.ask-password.policy | 31 ++++ src/shared/meson.build | 1 + src/shared/varlink-io.systemd.AskPassword.c | 54 ++++++ src/shared/varlink-io.systemd.AskPassword.h | 6 + src/test/test-varlink-idl.c | 3 + units/meson.build | 5 + units/systemd-ask-password.socket | 21 +++ units/systemd-ask-password@.service | 16 ++ units/user/meson.build | 84 ++++++--- units/user/systemd-ask-password.socket | 20 +++ units/user/systemd-ask-password@.service | 16 ++ 12 files changed, 397 insertions(+), 28 deletions(-) create mode 100644 src/ask-password/io.systemd.ask-password.policy create mode 100644 src/shared/varlink-io.systemd.AskPassword.c create mode 100644 src/shared/varlink-io.systemd.AskPassword.h create mode 100644 units/systemd-ask-password.socket create mode 100644 units/systemd-ask-password@.service create mode 100644 units/user/systemd-ask-password.socket create mode 100644 units/user/systemd-ask-password@.service diff --git a/src/ask-password/ask-password.c b/src/ask-password/ask-password.c index 154aaa030e..551dcb8b27 100644 --- a/src/ask-password/ask-password.c +++ b/src/ask-password/ask-password.c @@ -5,16 +5,23 @@ #include #include +#include "sd-varlink.h" + #include "ask-password-api.h" #include "build.h" +#include "bus-polkit.h" #include "constants.h" +#include "json-util.h" #include "log.h" #include "macro.h" #include "main-func.h" #include "parse-argument.h" #include "pretty-print.h" +#include "string-table.h" #include "strv.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_id = NULL; /* identifier for 'ask-password' protocol */ @@ -26,6 +33,7 @@ static bool arg_multiple = false; static bool arg_no_output = false; static AskPasswordFlags arg_flags = ASK_PASSWORD_PUSH_CACHE; static bool arg_newline = true; +static bool arg_varlink = false; STATIC_DESTRUCTOR_REGISTER(arg_message, freep); @@ -213,7 +221,7 @@ static int parse_argv(int argc, char *argv[]) { else { r = parse_boolean_argument("--emoji=", emoji, NULL); if (r < 0) - return r; + return r; SET_FLAG(arg_flags, ASK_PASSWORD_HIDE_EMOJI, !r); } @@ -232,9 +240,164 @@ static int parse_argv(int argc, char *argv[]) { 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; } +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), + }; + + /* Specifying the absolute or relative timeout as zero means: do not ask interactively, only check + * cache, hence leave the field at zero in that case. Otherwise we take the minimum of both times. */ + 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[]) { _cleanup_strv_free_erase_ char **l = NULL; usec_t timeout; @@ -249,6 +412,9 @@ static int run(int argc, char *argv[]) { if (r <= 0) return r; + if (arg_varlink) + return vl_server(); /* Invocation as Varlink service */ + timeout = arg_timeout > 0 ? usec_add(now(CLOCK_MONOTONIC), arg_timeout) : 0; AskPasswordRequest req = { diff --git a/src/ask-password/io.systemd.ask-password.policy b/src/ask-password/io.systemd.ask-password.policy new file mode 100644 index 0000000000..8fddf5c07e --- /dev/null +++ b/src/ask-password/io.systemd.ask-password.policy @@ -0,0 +1,31 @@ + + + + + + + + The systemd Project + https://systemd.io + + + Allow to query the user interactively for a password + Authentication is required for an application to ask the user interactively for a password. + + auth_admin_keep + auth_admin_keep + yes + + + + diff --git a/src/shared/meson.build b/src/shared/meson.build index af9ef74b32..213d12d7a8 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -177,6 +177,7 @@ shared_sources = files( 'userdb-dropin.c', 'userdb.c', 'varlink-idl-common.c', + 'varlink-io.systemd.AskPassword.c', 'varlink-io.systemd.BootControl.c', 'varlink-io.systemd.Credentials.c', 'varlink-io.systemd.Hostname.c', diff --git a/src/shared/varlink-io.systemd.AskPassword.c b/src/shared/varlink-io.systemd.AskPassword.c new file mode 100644 index 0000000000..86e0a803c8 --- /dev/null +++ b/src/shared/varlink-io.systemd.AskPassword.c @@ -0,0 +1,54 @@ +/* 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; set to zero to only check cache and not query interactively; set to UINT64_MAX to disable relative timeout; if not set defaults to 90s)"), + 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; " + "set to zero to only check cache and not query interactively; leave unset or set to UINT64_NAX to disable absolute timeout)"), + 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); diff --git a/src/shared/varlink-io.systemd.AskPassword.h b/src/shared/varlink-io.systemd.AskPassword.h new file mode 100644 index 0000000000..1be83f4c90 --- /dev/null +++ b/src/shared/varlink-io.systemd.AskPassword.h @@ -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; diff --git a/src/test/test-varlink-idl.c b/src/test/test-varlink-idl.c index 182d59bd20..74c64d392a 100644 --- a/src/test/test-varlink-idl.c +++ b/src/test/test-varlink-idl.c @@ -11,6 +11,7 @@ #include "varlink-idl-util.h" #include "varlink-io.systemd.h" #include "varlink-io.systemd.BootControl.h" +#include "varlink-io.systemd.AskPassword.h" #include "varlink-io.systemd.Credentials.h" #include "varlink-io.systemd.Import.h" #include "varlink-io.systemd.Journal.h" @@ -194,6 +195,8 @@ TEST(parse_format) { print_separator(); test_parse_format_one(&vl_interface_io_systemd_MachineImage); print_separator(); + test_parse_format_one(&vl_interface_io_systemd_AskPassword); + print_separator(); test_parse_format_one(&vl_interface_xyz_test); } diff --git a/units/meson.build b/units/meson.build index 00978a18b0..f6d661da97 100644 --- a/units/meson.build +++ b/units/meson.build @@ -245,6 +245,11 @@ units = [ { 'file' : 'system-update-cleanup.service' }, { 'file' : 'system-update-pre.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', 'symlinks' : ['sysinit.target.wants/'], diff --git a/units/systemd-ask-password.socket b/units/systemd-ask-password.socket new file mode 100644 index 0000000000..c8d203b077 --- /dev/null +++ b/units/systemd-ask-password.socket @@ -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 diff --git a/units/systemd-ask-password@.service b/units/systemd-ask-password@.service new file mode 100644 index 0000000000..07bbd45a7b --- /dev/null +++ b/units/systemd-ask-password@.service @@ -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 diff --git a/units/user/meson.build b/units/user/meson.build index 21070f7ef8..290f268ef1 100644 --- a/units/user/meson.build +++ b/units/user/meson.build @@ -1,34 +1,64 @@ # SPDX-License-Identifier: LGPL-2.1-or-later units = [ - 'app.slice', - 'background.slice', - 'basic.target', - 'bluetooth.target', - 'capsule@.target', - 'default.target', - 'exit.target', - 'graphical-session-pre.target', - 'graphical-session.target', - 'paths.target', - 'printer.target', - 'session.slice', - 'shutdown.target', - 'smartcard.target', - 'sockets.target', - 'sound.target', - 'systemd-exit.service', - 'systemd-tmpfiles-clean.service', - 'systemd-tmpfiles-clean.timer', - 'systemd-tmpfiles-setup.service', - 'timers.target', + { 'file' : 'app.slice' }, + { 'file' : 'background.slice' }, + { 'file' : 'basic.target' }, + { 'file' : 'bluetooth.target' }, + { 'file' : 'capsule@.target' }, + { 'file' : 'default.target' }, + { 'file' : 'exit.target' }, + { 'file' : 'graphical-session-pre.target' }, + { 'file' : 'graphical-session.target' }, + { 'file' : 'paths.target' }, + { 'file' : 'printer.target' }, + { 'file' : 'session.slice' }, + { 'file' : 'shutdown.target' }, + { 'file' : 'smartcard.target' }, + { 'file' : 'sockets.target' }, + { 'file' : 'sound.target' }, + { + 'file' : 'systemd-ask-password.socket', + 'symlinks' : ['sockets.target.wants/'] + }, + { '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 - units += 'xdg-desktop-autostart.target' -endif +foreach unit : units + file = unit.get('file') -foreach file : units - install_data(file, - install_dir : userunitdir) + install = true + foreach cond : unit.get('conditions', []) + if conf.get(cond) != 1 + install = false + break + endif + endforeach + + if install + install_data(file, + 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 diff --git a/units/user/systemd-ask-password.socket b/units/user/systemd-ask-password.socket new file mode 100644 index 0000000000..ce9ccd9e55 --- /dev/null +++ b/units/user/systemd-ask-password.socket @@ -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 diff --git a/units/user/systemd-ask-password@.service b/units/user/systemd-ask-password@.service new file mode 100644 index 0000000000..e5d72ff343 --- /dev/null +++ b/units/user/systemd-ask-password@.service @@ -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 From 7fd0ea6cf6b3f1ed407bba9b94cd9c1400e736b9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 7 Nov 2024 15:07:18 +0100 Subject: [PATCH 10/11] ask-password: add comment with well-known errors from ask_password_auto() --- src/shared/ask-password-api.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index 502b22ee89..df8915082b 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -1165,6 +1165,17 @@ int ask_password_auto( 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) { r = ask_password_credential(req, flags, ret); if (r != -ENOKEY) From 3fbca0e23a6c8ac3e5547957ec7a690420a463f2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 6 Nov 2024 23:47:37 +0100 Subject: [PATCH 11/11] test: add CI test validating the new varlink interface and more --- test/units/TEST-74-AUX-UTILS.ask-password.sh | 24 ++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100755 test/units/TEST-74-AUX-UTILS.ask-password.sh diff --git a/test/units/TEST-74-AUX-UTILS.ask-password.sh b/test/units/TEST-74-AUX-UTILS.ask-password.sh new file mode 100755 index 0000000000..1ae1df1142 --- /dev/null +++ b/test/units/TEST-74-AUX-UTILS.ask-password.sh @@ -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\""