]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
cryptsetup: add a configurable token waiting timeout
authorLennart Poettering <lennart@poettering.net>
Fri, 8 Oct 2021 20:11:06 +0000 (22:11 +0200)
committerLennart Poettering <lennart@poettering.net>
Mon, 11 Oct 2021 09:12:29 +0000 (11:12 +0200)
Let's add  configurable timeout how long to wait for FIDO2/PKCS#11
devices to show up. Once the timeout is hit, let's automatically revert
to querying via passphrase.

Fixes: #19739
src/cryptsetup/cryptsetup.c

index 43c6bf529f82586b86cfcc1ed5e79940a7297e50..fc63f8834ef39990ad64c239c230afd0bca05e4e 100644 (file)
@@ -83,6 +83,7 @@ static char *arg_tpm2_device = NULL;
 static bool arg_tpm2_device_auto = false;
 static uint32_t arg_tpm2_pcr_mask = UINT32_MAX;
 static bool arg_headless = false;
+static usec_t arg_token_timeout_usec = 30*USEC_PER_SEC;
 
 STATIC_DESTRUCTOR_REGISTER(arg_cipher, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_hash, freep);
@@ -410,7 +411,15 @@ static int parse_one_option(const char *option) {
         } else if (streq(option, "headless"))
                 arg_headless = true;
 
-        else if (!streq(option, "x-initrd.attach"))
+        else if ((val = startswith(option, "token-timeout="))) {
+
+                r = parse_sec_fix_0(val, &arg_token_timeout_usec);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
+                        return 0;
+                }
+
+        } else if (!streq(option, "x-initrd.attach"))
                 log_warning("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
 
         return 0;
@@ -711,11 +720,25 @@ static char *make_bindname(const char *volume) {
         return s;
 }
 
-static int make_security_device_monitor(sd_event *event, sd_device_monitor **ret) {
+static int make_security_device_monitor(
+                sd_event **ret_event,
+                sd_device_monitor **ret_monitor) {
         _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
+        _cleanup_(sd_event_unrefp) sd_event *event = NULL;
         int r;
 
-        assert(ret);
+        assert(ret_event);
+        assert(ret_monitor);
+
+        /* Waits for a device with "security-device" tag to show up in udev */
+
+        r = sd_event_default(&event);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate event loop: %m");
+
+        r = sd_event_add_time_relative(event, NULL, CLOCK_MONOTONIC, arg_token_timeout_usec, USEC_PER_SEC, NULL, INT_TO_PTR(-ETIMEDOUT));
+        if (r < 0)
+                return log_error_errno(r, "Failed to install timeout event source: %m");
 
         r = sd_device_monitor_new(&monitor);
         if (r < 0)
@@ -733,10 +756,49 @@ static int make_security_device_monitor(sd_event *event, sd_device_monitor **ret
         if (r < 0)
                 return log_error_errno(r, "Failed to start device monitor: %m");
 
-        *ret = TAKE_PTR(monitor);
+        *ret_event = TAKE_PTR(event);
+        *ret_monitor = TAKE_PTR(monitor);
         return 0;
 }
 
+static int run_security_device_monitor(
+                sd_event *event,
+                sd_device_monitor *monitor) {
+        bool processed = false;
+        int r;
+
+        assert(event);
+        assert(monitor);
+
+        /* Runs the event loop for the device monitor until either something happens, or the time-out is
+         * hit. */
+
+        for (;;) {
+                int x;
+
+                r = sd_event_get_exit_code(event, &x);
+                if (r < 0) {
+                        if (r != -ENODATA)
+                                return log_error_errno(r, "Failed to query exit code from event loop: %m");
+
+                        /* On ENODATA we aren't told to exit yet. */
+                } else {
+                        assert(x == -ETIMEDOUT);
+                        return log_notice_errno(SYNTHETIC_ERRNO(EAGAIN),
+                                                "Timed out waiting for security device, aborting security device based authentication attempt.");
+                }
+
+                /* Wait for one event, and then eat all subsequent events until there are no further ones */
+                r = sd_event_run(event, processed ? 0 : UINT64_MAX);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to run event loop: %m");
+                if (r == 0) /* no events queued anymore */
+                        return 0;
+
+                processed = true;
+        }
+}
+
 static bool libcryptsetup_plugins_support(void) {
 #if HAVE_LIBCRYPTSETUP_PLUGINS
         int r;
@@ -906,8 +968,6 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
                 return log_oom();
 
         for (;;) {
-                bool processed = false;
-
                 if (use_libcryptsetup_plugin && !arg_fido2_cid) {
                         r = attach_luks2_by_fido2(cd, name, until, arg_headless, arg_fido2_device, flags);
                         if (IN_SET(r, -ENOTUNIQ, -ENXIO, -ENOENT))
@@ -941,11 +1001,7 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
 
                         assert(!event);
 
-                        r = sd_event_default(&event);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to allocate event loop: %m");
-
-                        r = make_security_device_monitor(event, &monitor);
+                        r = make_security_device_monitor(&event, &monitor);
                         if (r < 0)
                                 return r;
 
@@ -956,17 +1012,9 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
                         continue;
                 }
 
-                for (;;) {
-                        /* Wait for one event, and then eat all subsequent events until there are no
-                         * further ones */
-                        r = sd_event_run(event, processed ? 0 : UINT64_MAX);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to run event loop: %m");
-                        if (r == 0)
-                                break;
-
-                        processed = true;
-                }
+                r = run_security_device_monitor(event, monitor);
+                if (r < 0)
+                        return r;
 
                 log_debug("Got one or more potentially relevant udev events, rescanning FIDO2...");
         }
@@ -1069,8 +1117,6 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
                 return log_oom();
 
         for (;;) {
-                bool processed = false;
-
                 if (use_libcryptsetup_plugin && arg_pkcs11_uri_auto)
                         r = attach_luks2_by_pkcs11(cd, name, friendly, until, arg_headless, flags);
                 else {
@@ -1096,11 +1142,7 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
 
                         assert(!event);
 
-                        r = sd_event_default(&event);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to allocate event loop: %m");
-
-                        r = make_security_device_monitor(event, &monitor);
+                        r = make_security_device_monitor(&event, &monitor);
                         if (r < 0)
                                 return r;
 
@@ -1112,17 +1154,9 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
                         continue;
                 }
 
-                for (;;) {
-                        /* Wait for one event, and then eat all subsequent events until there are no
-                         * further ones */
-                        r = sd_event_run(event, processed ? 0 : UINT64_MAX);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to run event loop: %m");
-                        if (r == 0)
-                                break;
-
-                        processed = true;
-                }
+                r = run_security_device_monitor(event, monitor);
+                if (r < 0)
+                        return r;
 
                 log_debug("Got one or more potentially relevant udev events, rescanning PKCS#11...");
         }
@@ -1157,11 +1191,24 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
         return 0;
 }
 
-static int make_tpm2_device_monitor(sd_event *event, sd_device_monitor **ret) {
+static int make_tpm2_device_monitor(
+                sd_event **ret_event,
+                sd_device_monitor **ret_monitor) {
+
         _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
+        _cleanup_(sd_event_unrefp) sd_event *event = NULL;
         int r;
 
-        assert(ret);
+        assert(ret_event);
+        assert(ret_monitor);
+
+        r = sd_event_default(&event);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate event loop: %m");
+
+        r = sd_event_add_time_relative(event, NULL, CLOCK_MONOTONIC, arg_token_timeout_usec, USEC_PER_SEC, NULL, INT_TO_PTR(-ETIMEDOUT));
+        if (r < 0)
+                return log_error_errno(r, "Failed to install timeout event source: %m");
 
         r = sd_device_monitor_new(&monitor);
         if (r < 0)
@@ -1179,7 +1226,8 @@ static int make_tpm2_device_monitor(sd_event *event, sd_device_monitor **ret) {
         if (r < 0)
                 return log_error_errno(r, "Failed to start device monitor: %m");
 
-        *ret = TAKE_PTR(monitor);
+        *ret_event = TAKE_PTR(event);
+        *ret_monitor = TAKE_PTR(monitor);
         return 0;
 }
 
@@ -1236,8 +1284,6 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
                 return log_oom();
 
         for (;;) {
-                bool processed = false;
-
                 if (key_file || key_data) {
                         /* If key data is specified, use that */
 
@@ -1341,11 +1387,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
                                 return log_notice_errno(SYNTHETIC_ERRNO(EAGAIN),
                                                         "No TPM2 hardware discovered and EFI bios indicates no support for it either, assuming TPM2-less system, falling back to traditional unocking.");
 
-                        r = sd_event_default(&event);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to allocate event loop: %m");
-
-                        r = make_tpm2_device_monitor(event, &monitor);
+                        r = make_tpm2_device_monitor(&event, &monitor);
                         if (r < 0)
                                 return r;
 
@@ -1356,17 +1398,9 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
                         continue;
                 }
 
-                for (;;) {
-                        /* Wait for one event, and then eat all subsequent events until there are no
-                         * further ones */
-                        r = sd_event_run(event, processed ? 0 : UINT64_MAX);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to run event loop: %m");
-                        if (r == 0)
-                                break;
-
-                        processed = true;
-                }
+                r = run_security_device_monitor(event, monitor);
+                if (r < 0)
+                        return r;
 
                 log_debug("Got one or more potentially relevant udev events, rescanning for TPM2...");
         }