]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
ask-password: when querying for a password, try to read from credential store first
authorLennart Poettering <lennart@poettering.net>
Thu, 11 Mar 2021 19:17:10 +0000 (20:17 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 26 Mar 2021 11:21:56 +0000 (12:21 +0100)
This adds generic support for the SetCredential=/LoadCredential= logic
to our password querying infrastructure: if a password is requested by a
program that has a credential store configured via
$CREDENTIALS_DIRECTORY we'll look in it for a password.

The "systemd-ask-password" tool is updated with an option to specify the
credential to look for.

14 files changed:
man/systemd-ask-password.xml
src/ask-password/ask-password.c
src/cryptenroll/cryptenroll-password.c
src/cryptenroll/cryptenroll.c
src/cryptsetup/cryptsetup-fido2.c
src/cryptsetup/cryptsetup-pkcs11.c
src/cryptsetup/cryptsetup.c
src/home/homectl.c
src/shared/ask-password-api.c
src/shared/ask-password-api.h
src/shared/dissect-image.c
src/shared/libfido2-util.c
src/shared/pkcs11-util.c
src/shared/pkcs11-util.h

index 433260475dc2d9bf39cc42b316405fbbea829e2c..f241064d8bbefef7a393272193a9b3cb47451fe9 100644 (file)
         directly. Example: <literal>--keyname=cryptsetup</literal></para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--credential=</option></term>
+        <listitem><para>Configure a credential to read the password from – if it exists. This may be used in
+        conjunction with the <varname>LoadCredential=</varname> and <varname>SetCredential=</varname>
+        settings in unit files. See
+        <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+        details. If not specified, defaults to <literal>password</literal>. This option has no effect if no
+        credentials directory is passed to the program (i.e. <varname>$CREDENTIALS_DIRECTORY</varname> is not
+        set) or if the no credential of the specified name exists.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--timeout=</option></term>
 
index 6b89f57e1b71a8ebc9b5a4d71b62a85a71f8e518..09bcefbe66d4e95be0b9e5ab98a8574157266f14 100644 (file)
 #include "main-func.h"
 #include "pretty-print.h"
 #include "strv.h"
+#include "terminal-util.h"
 
 static const char *arg_icon = NULL;
-static const char *arg_id = NULL;
-static const char *arg_keyname = NULL;
+static const char *arg_id = NULL;               /* identifier for 'ask-password' protocol */
+static const char *arg_key_name = NULL;         /* name in kernel keyring */
+static const char *arg_credential_name = NULL;  /* name in $CREDENTIALS_DIRECTORY directory */
 static char *arg_message = NULL;
 static usec_t arg_timeout = DEFAULT_TIMEOUT_USEC;
 static bool arg_multiple = false;
@@ -32,21 +34,26 @@ static int help(void) {
         if (r < 0)
                 return log_oom();
 
-        printf("%s [OPTIONS...] MESSAGE\n\n"
-               "Query the user for a system passphrase, via the TTY or an UI agent.\n\n"
+        printf("%1$s [OPTIONS...] MESSAGE\n\n"
+               "%3$sQuery the user for a system passphrase, via the TTY or an UI agent.%4$s\n\n"
                "  -h --help           Show this help\n"
                "     --icon=NAME      Icon name\n"
                "     --id=ID          Query identifier (e.g. \"cryptsetup:/dev/sda5\")\n"
                "     --keyname=NAME   Kernel key name for caching passwords (e.g. \"cryptsetup\")\n"
+               "     --credential=NAME\n"
+               "                      Credential name for LoadCredential=/SetCredential=\n"
+               "                      credentials\n"
                "     --timeout=SEC    Timeout in seconds\n"
                "     --echo           Do not mask input (useful for usernames)\n"
                "     --no-tty         Ask question via agent even on TTY\n"
                "     --accept-cached  Accept cached passwords\n"
                "     --multiple       List multiple passwords if available\n"
                "     --no-output      Do not print password to standard output\n"
-               "\nSee the %s for details.\n",
+               "\nSee the %2$s for details.\n",
                program_invocation_short_name,
-               link);
+               link,
+               ansi_highlight(),
+               ansi_normal());
 
         return 0;
 }
@@ -64,6 +71,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_KEYNAME,
                 ARG_NO_OUTPUT,
                 ARG_VERSION,
+                ARG_CREDENTIAL,
         };
 
         static const struct option options[] = {
@@ -78,6 +86,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "id",            required_argument, NULL, ARG_ID            },
                 { "keyname",       required_argument, NULL, ARG_KEYNAME       },
                 { "no-output",     no_argument,       NULL, ARG_NO_OUTPUT     },
+                { "credential",    required_argument, NULL, ARG_CREDENTIAL    },
                 {}
         };
 
@@ -128,13 +137,17 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_KEYNAME:
-                        arg_keyname = optarg;
+                        arg_key_name = optarg;
                         break;
 
                 case ARG_NO_OUTPUT:
                         arg_no_output = true;
                         break;
 
+                case ARG_CREDENTIAL:
+                        arg_credential_name = optarg;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -170,7 +183,7 @@ static int run(int argc, char *argv[]) {
         else
                 timeout = 0;
 
-        r = ask_password_auto(arg_message, arg_icon, arg_id, arg_keyname, timeout, arg_flags, &l);
+        r = ask_password_auto(arg_message, arg_icon, arg_id, arg_key_name, arg_credential_name ?: "password", timeout, arg_flags, &l);
         if (r < 0)
                 return log_error_errno(r, "Failed to query password: %m");
 
index e08f564d3faff5c1abb774c17fde40438c1c5c9d..0314831174be1ea6cb996b99d9ef7260f52cc78f 100644 (file)
@@ -57,7 +57,7 @@ int enroll_password(
                         if (!question)
                                 return log_oom();
 
-                        r = ask_password_auto(question, "drive-harddisk", id, "cryptenroll", USEC_INFINITY, 0, &passwords);
+                        r = ask_password_auto(question, "drive-harddisk", id, "cryptenroll", "cryptenroll.new-passphrase", USEC_INFINITY, 0, &passwords);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to query password: %m");
 
@@ -68,7 +68,7 @@ int enroll_password(
                         if (!question)
                                 return log_oom();
 
-                        r = ask_password_auto(question, "drive-harddisk", id, "cryptenroll", USEC_INFINITY, 0, &passwords2);
+                        r = ask_password_auto(question, "drive-harddisk", id, "cryptenroll", "cryptenroll.new-passphrase", USEC_INFINITY, 0, &passwords2);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to query password: %m");
 
index a137a41c9d7cfda6d75696671b48008c886e0b3c..7d12c427b3099f6feaf2e9f2e471b2a7767e627a 100644 (file)
@@ -417,7 +417,7 @@ static int prepare_luks(
                                                        "Too many attempts, giving up:");
 
                         r = ask_password_auto(
-                                        question, "drive-harddisk", id, "cryptenroll", USEC_INFINITY,
+                                        question, "drive-harddisk", id, "cryptenroll", "cryptenroll.passphrase", USEC_INFINITY,
                                         ask_password_flags,
                                         &passwords);
                         if (r < 0)
index dfdb964360a7998e9cecfc527726641dc3c35b6e..82b83ee47705ecbce7f0b7d89c9b1e2999a23817 100644 (file)
@@ -88,7 +88,7 @@ int acquire_fido2_key(
 
                 pins = strv_free_erase(pins);
 
-                r = ask_password_auto("Please enter security token PIN:", "drive-harddisk", NULL, "fido2-pin", until, flags, &pins);
+                r = ask_password_auto("Please enter security token PIN:", "drive-harddisk", NULL, "fido2-pin", "cryptsetup.fido2-pin", until, flags, &pins);
                 if (r < 0)
                         return log_error_errno(r, "Failed to ask for user password: %m");
 
index 8d7d74fb75b1294725db55248dfaa8be9d88bc57..6d7b01176cbe0960706efee68997b16903ed1525 100644 (file)
@@ -70,6 +70,7 @@ static int pkcs11_callback(
                         data->friendly_name,
                         "drive-harddisk",
                         "pkcs11-pin",
+                        "cryptsetup.pkcs11-pin",
                         data->until,
                         NULL);
         if (r < 0)
index dba26a54a3f15faa45c1d82e557bb31f5e52088b..5c55dcd19757e559334f1a89c67e622733410c8d 100644 (file)
@@ -545,7 +545,7 @@ static int get_password(
 
         id = strjoina("cryptsetup:", disk_path);
 
-        r = ask_password_auto(text, "drive-harddisk", id, "cryptsetup", until,
+        r = ask_password_auto(text, "drive-harddisk", id, "cryptsetup", "cryptsetup.passphrase", until,
                               ASK_PASSWORD_PUSH_CACHE | (accept_cached*ASK_PASSWORD_ACCEPT_CACHED),
                               &passwords);
         if (r < 0)
@@ -561,7 +561,7 @@ static int get_password(
 
                 id = strjoina("cryptsetup-verification:", disk_path);
 
-                r = ask_password_auto(text, "drive-harddisk", id, "cryptsetup", until, ASK_PASSWORD_PUSH_CACHE, &passwords2);
+                r = ask_password_auto(text, "drive-harddisk", id, "cryptsetup", "cryptsetup.passphrase", until, ASK_PASSWORD_PUSH_CACHE, &passwords2);
                 if (r < 0)
                         return log_error_errno(r, "Failed to query verification password: %m");
 
index 9d12b9abae528ff5d36d4d26cf6704a83d6e18a2..cf1a2d9f9bb4e2a3477dc89d240b2e654d2f3f31 100644 (file)
@@ -221,7 +221,7 @@ static int acquire_existing_password(const char *user_name, UserRecord *hr, bool
                      user_name) < 0)
                 return log_oom();
 
-        r = ask_password_auto(question, "user-home", NULL, "home-password", USEC_INFINITY, ASK_PASSWORD_ACCEPT_CACHED|ASK_PASSWORD_PUSH_CACHE, &password);
+        r = ask_password_auto(question, "user-home", NULL, "home-password", "home.password", USEC_INFINITY, ASK_PASSWORD_ACCEPT_CACHED|ASK_PASSWORD_PUSH_CACHE, &password);
         if (r < 0)
                 return log_error_errno(r, "Failed to acquire password: %m");
 
@@ -257,7 +257,7 @@ static int acquire_token_pin(const char *user_name, UserRecord *hr) {
                 return log_oom();
 
         /* We never cache or use cached PINs, since usually there are only very few attempts allowed before the PIN is blocked */
-        r = ask_password_auto(question, "user-home", NULL, "token-pin", USEC_INFINITY, 0, &pin);
+        r = ask_password_auto(question, "user-home", NULL, "token-pin", "home.token-pin", USEC_INFINITY, 0, &pin);
         if (r < 0)
                 return log_error_errno(r, "Failed to acquire security token PIN: %m");
 
@@ -1010,7 +1010,7 @@ static int acquire_new_password(
                 if (asprintf(&question, "Please enter new password for user %s:", user_name) < 0)
                         return log_oom();
 
-                r = ask_password_auto(question, "user-home", NULL, "home-password", USEC_INFINITY, 0, &first);
+                r = ask_password_auto(question, "user-home", NULL, "home-password", "home.new-password", USEC_INFINITY, 0, &first);
                 if (r < 0)
                         return log_error_errno(r, "Failed to acquire password: %m");
 
@@ -1018,7 +1018,7 @@ static int acquire_new_password(
                 if (asprintf(&question, "Please enter new password for user %s (repeat):", user_name) < 0)
                         return log_oom();
 
-                r = ask_password_auto(question, "user-home", NULL, "home-password", USEC_INFINITY, 0, &second);
+                r = ask_password_auto(question, "user-home", NULL, "home-password", "home.new-password", USEC_INFINITY, 0, &second);
                 if (r < 0)
                         return log_error_errno(r, "Failed to acquire password: %m");
 
index 729aa1fb00a20ec4ab7c522f87853fa304cd58f2..04c6b5287ed3f1bd9bfe9bf96d8b8e62a31e5d77 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "alloc-util.h"
 #include "ask-password-api.h"
+#include "creds-util.h"
 #include "def.h"
 #include "fd-util.h"
 #include "fileio.h"
@@ -971,11 +972,33 @@ finish:
         return r;
 }
 
+static int ask_password_credential(const char *credential_name, AskPasswordFlags flags, char ***ret) {
+        _cleanup_(erase_and_freep) char *buffer = NULL;
+        size_t size;
+        char **l;
+        int r;
+
+        assert(credential_name);
+        assert(ret);
+
+        r = read_credential(credential_name, (void**) &buffer, &size);
+        if (IN_SET(r, -ENXIO, -ENOENT)) /* No credentials passed or this credential not defined? */
+                return -ENOKEY;
+
+        l = strv_parse_nulstr(buffer, size);
+        if (!l)
+                return -ENOMEM;
+
+        *ret = l;
+        return 0;
+}
+
 int ask_password_auto(
                 const char *message,
                 const char *icon,
-                const char *id,
-                const char *keyname,
+                const char *id,                /* id in "ask-password" protocol */
+                const char *key_name,          /* name in kernel keyring */
+                const char *credential_name,   /* name in $CREDENTIALS_DIRECTORY directory */
                 usec_t until,
                 AskPasswordFlags flags,
                 char ***ret) {
@@ -984,20 +1007,26 @@ int ask_password_auto(
 
         assert(ret);
 
+        if (!(flags & ASK_PASSWORD_NO_CREDENTIAL) && credential_name) {
+                r = ask_password_credential(credential_name, flags, ret);
+                if (r != -ENOKEY)
+                        return r;
+        }
+
         if ((flags & ASK_PASSWORD_ACCEPT_CACHED) &&
-            keyname &&
+            key_name &&
             ((flags & ASK_PASSWORD_NO_TTY) || !isatty(STDIN_FILENO)) &&
             (flags & ASK_PASSWORD_NO_AGENT)) {
-                r = ask_password_keyring(keyname, flags, ret);
+                r = ask_password_keyring(key_name, flags, ret);
                 if (r != -ENOKEY)
                         return r;
         }
 
         if (!(flags & ASK_PASSWORD_NO_TTY) && isatty(STDIN_FILENO))
-                return ask_password_tty(-1, message, keyname, until, flags, NULL, ret);
+                return ask_password_tty(-1, message, key_name, until, flags, NULL, ret);
 
         if (!(flags & ASK_PASSWORD_NO_AGENT))
-                return ask_password_agent(message, icon, id, keyname, until, flags, ret);
+                return ask_password_agent(message, icon, id, key_name, until, flags, ret);
 
         return -EUNATCH;
 }
index 7aac5e597608208bb9923e532125b14c13bedf30..bb507b2e8de0bb8c07dc50b0997b638fad3a468d 100644 (file)
@@ -6,16 +6,17 @@
 #include "time-util.h"
 
 typedef enum AskPasswordFlags {
-        ASK_PASSWORD_ACCEPT_CACHED = 1 << 0,
-        ASK_PASSWORD_PUSH_CACHE    = 1 << 1,
+        ASK_PASSWORD_ACCEPT_CACHED = 1 << 0, /* read from kernel keyring */
+        ASK_PASSWORD_PUSH_CACHE    = 1 << 1, /* write to kernel keyring after getting password from elsewhere */
         ASK_PASSWORD_ECHO          = 1 << 2, /* show the password literally while reading, instead of "*" */
         ASK_PASSWORD_SILENT        = 1 << 3, /* do no show any password at all while reading */
-        ASK_PASSWORD_NO_TTY        = 1 << 4,
-        ASK_PASSWORD_NO_AGENT      = 1 << 5,
+        ASK_PASSWORD_NO_TTY        = 1 << 4, /* never ask for password on tty */
+        ASK_PASSWORD_NO_AGENT      = 1 << 5, /* never ask for password via agent */
         ASK_PASSWORD_CONSOLE_COLOR = 1 << 6, /* Use color if /dev/console points to a console that supports color */
+        ASK_PASSWORD_NO_CREDENTIAL = 1 << 7, /* never use $CREDENTIALS_DIRECTORY data */
 } AskPasswordFlags;
 
-int ask_password_tty(int tty_fd, const char *message, const char *keyname, usec_t until, AskPasswordFlags flags, const char *flag_file, char ***ret);
+int ask_password_tty(int tty_fd, const char *message, const char *key_name, usec_t until, AskPasswordFlags flags, const char *flag_file, char ***ret);
 int ask_password_plymouth(const char *message, usec_t until, AskPasswordFlags flags, const char *flag_file, char ***ret);
-int ask_password_agent(const char *message, const char *icon, const char *id, const char *keyname, usec_t until, AskPasswordFlags flag, char ***ret);
-int ask_password_auto(const char *message, const char *icon, const char *id, const char *keyname, usec_t until, AskPasswordFlags flag, char ***ret);
+int ask_password_agent(const char *message, const char *icon, const char *id, const char *key_name, usec_t until, AskPasswordFlags flag, char ***ret);
+int ask_password_auto(const char *message, const char *icon, const char *id, const char *key_name, const char *credential_name, usec_t until, AskPasswordFlags flag, char ***ret);
index cca92e7fb4e3ab969e7b18c59723e57c57987b39..6eb23db312ab6cad3e7bb032cea26abd860be3a3 100644 (file)
@@ -1993,7 +1993,7 @@ int dissected_image_decrypt_interactively(
 
                 z = strv_free(z);
 
-                r = ask_password_auto("Please enter image passphrase:", NULL, "dissect", "dissect", USEC_INFINITY, 0, &z);
+                r = ask_password_auto("Please enter image passphrase:", NULL, "dissect", "dissect", "dissect.passphrase", USEC_INFINITY, 0, &z);
                 if (r < 0)
                         return log_error_errno(r, "Failed to query for passphrase: %m");
 
index 22b8aba07e5028a30d372272b339ee61cb0acdbf..951ed09899c5dce36e0b68fc18dd7daa52724469 100644 (file)
@@ -566,7 +566,7 @@ int fido2_generate_hmac_hash(
                         if (!has_client_pin)
                                 log_warning("Weird, device asked for client PIN, but does not advertise it as feature. Ignoring.");
 
-                        r = ask_password_auto("Please enter security token PIN:", askpw_icon_name, NULL, "fido2-pin", USEC_INFINITY, 0, &pin);
+                        r = ask_password_auto("Please enter security token PIN:", askpw_icon_name, NULL, "fido2-pin", "fido2-pin", USEC_INFINITY, 0, &pin);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to acquire user PIN: %m");
 
index 27c209cdf8ab6f9afe86f9a8b311da9bdaf5a733..4fa1effb2d3be3878ce56f9fde225757596b1a75 100644 (file)
@@ -181,7 +181,8 @@ int pkcs11_token_login(
                 const CK_TOKEN_INFO *token_info,
                 const char *friendly_name,
                 const char *icon_name,
-                const char *keyname,
+                const char *key_name,
+                const char *credential_name,
                 usec_t until,
                 char **ret_used_pin) {
 
@@ -269,7 +270,7 @@ int pkcs11_token_login(
                                 return log_oom();
 
                         /* 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(text, icon_name, id, keyname, until, 0, &passwords);
+                        r = ask_password_auto(text, icon_name, id, key_name, credential_name, until, 0, &passwords);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to query PIN for security token '%s': %m", token_label);
                 }
@@ -959,7 +960,7 @@ static int pkcs11_acquire_certificate_callback(
 
         /* Called for every token matching our URI */
 
-        r = pkcs11_token_login(m, session, slot_id, token_info, data->askpw_friendly_name, data->askpw_icon_name, "pkcs11-pin", UINT64_MAX, &pin_used);
+        r = pkcs11_token_login(m, session, slot_id, token_info, data->askpw_friendly_name, data->askpw_icon_name, "pkcs11-pin", "pkcs11-pin", UINT64_MAX, &pin_used);
         if (r < 0)
                 return r;
 
index f32ab304299e458a470825ea5a22639217458cfa..c2c852f0ebe356b8fe965bdf2f3141d80325faba 100644 (file)
@@ -30,7 +30,7 @@ char *pkcs11_token_label(const CK_TOKEN_INFO *token_info);
 char *pkcs11_token_manufacturer_id(const CK_TOKEN_INFO *token_info);
 char *pkcs11_token_model(const CK_TOKEN_INFO *token_info);
 
-int pkcs11_token_login(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_SLOT_ID slotid, const CK_TOKEN_INFO *token_info, const char *friendly_name, const char *icon_name, const char *keyname, usec_t until, char **ret_used_pin);
+int pkcs11_token_login(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_SLOT_ID slotid, const CK_TOKEN_INFO *token_info, const char *friendly_name, const char *icon_name, const char *key_name, const char *credential_name, usec_t until, char **ret_used_pin);
 
 int pkcs11_token_find_x509_certificate(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, P11KitUri *search_uri, CK_OBJECT_HANDLE *ret_object);
 #if HAVE_OPENSSL