From 1f2dac34d0c5b33cb533edc907b235c5d9a05d36 Mon Sep 17 00:00:00 2001 From: Daniel Zatovic Date: Thu, 9 Mar 2023 14:32:15 +0100 Subject: [PATCH] Support specifying keyring and key using keyctl syntax. When using the --link-vk-to-keyring option, allow specifying the keyring using the same syntax as keyctl (see "man keyctl"). E.g. "@u" for user keyring and "%:testring" for a user-created keyring. --- lib/libcryptsetup.h | 6 +- lib/setup.c | 23 ++++- lib/utils_keyring.c | 195 ++++++++++++++++++++++++++++++++++++-- lib/utils_keyring.h | 2 + src/cryptsetup.c | 12 ++- src/cryptsetup_arg_list.h | 2 +- 6 files changed, 222 insertions(+), 18 deletions(-) diff --git a/lib/libcryptsetup.h b/lib/libcryptsetup.h index a7bd1338..54074a5a 100644 --- a/lib/libcryptsetup.h +++ b/lib/libcryptsetup.h @@ -3032,9 +3032,11 @@ void crypt_safe_memzero(void *data, size_t size); * Link the volume key to the specified keyring. * * @param cd crypt device handle - * @param keyring_to_link_vk the ID of the keyring in which volume key should be linked + * @param keyring_to_link_vk the ID of the keyring in which volume key should + * be linked, if @e NULL is specified, linking will be disabled (the key will + * be linked just to the thread keyring, which is destroyed on process exit) */ -void crypt_set_keyring_to_link(struct crypt_device *cd, int keyring_to_link_vk); +int crypt_set_keyring_to_link(struct crypt_device *cd, const char *keyring_to_link_vk); /** * Set the type of volume key stored in keyring. diff --git a/lib/setup.c b/lib/setup.c index 91fdf728..f2acb2bc 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -61,7 +61,7 @@ struct crypt_device { unsigned key_in_keyring:1; bool link_vk_to_keyring; - int keyring_to_link_vk; + int32_t keyring_to_link_vk; key_type_t keyring_key_type; uint64_t data_offset; @@ -7254,12 +7254,25 @@ void crypt_drop_keyring_key_by_description(struct crypt_device *cd, const char * crypt_set_key_in_keyring(cd, 0); } -void crypt_set_keyring_to_link(struct crypt_device *cd, int keyring_to_link_vk) +int crypt_set_keyring_to_link(struct crypt_device *cd, const char *keyring_to_link_vk) { - if (cd) { - cd->link_vk_to_keyring = true; - cd->keyring_to_link_vk = keyring_to_link_vk; + int32_t id = 0; + + if (!cd) + return -EINVAL; + + if (keyring_to_link_vk) { + id = keyring_by_name(keyring_to_link_vk); + if (id == 0) { + log_err(cd, _("Invalid keyring format \"%s\"."), keyring_to_link_vk); + return -EINVAL; + } } + + cd->keyring_to_link_vk = id; + cd->link_vk_to_keyring = id != 0; + + return 0; } int crypt_set_vk_keyring_type(struct crypt_device *cd, const char *key_type_desc) diff --git a/lib/utils_keyring.c b/lib/utils_keyring.c index 72704006..d470be3c 100644 --- a/lib/utils_keyring.c +++ b/lib/utils_keyring.c @@ -19,7 +19,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #include +#include #include #include #include @@ -69,12 +71,28 @@ static key_serial_t add_key(const char *type, return syscall(__NR_add_key, type, description, payload, plen, keyring); } +/* keyctl_describe */ +static long keyctl_describe(key_serial_t id, char *buffer, size_t buflen) +{ + return syscall(__NR_keyctl, KEYCTL_DESCRIBE, id, buffer, buflen); +} + /* keyctl_read */ static long keyctl_read(key_serial_t key, char *buffer, size_t buflen) { return syscall(__NR_keyctl, KEYCTL_READ, key, buffer, buflen); } +/* key handle permissions mask */ +typedef uint32_t key_perm_t; +#define KEY_POS_ALL 0x3f000000 +#define KEY_USR_ALL 0x003f0000 + +static long keyctl_setperm(key_serial_t id, key_perm_t perm) +{ + return syscall(__NR_keyctl, KEYCTL_SETPERM, id, perm); +} + /* keyctl_link */ static long keyctl_link(key_serial_t key, key_serial_t keyring) { @@ -86,6 +104,107 @@ static long keyctl_unlink(key_serial_t key, key_serial_t keyring) { return syscall(__NR_keyctl, KEYCTL_UNLINK, key, keyring); } + +/* inspired by keyutils written by David Howells (dhowells@redhat.com) */ +static key_serial_t keyring_process_proc_keys_line(char *line, const char *type, const char *desc, + key_serial_t destringid) +{ + char typebuf[40], rdesc[1024], *kdesc, *cp; + int ndesc, n; + key_serial_t id; + int dlen; + + assert(desc); + dlen = strlen(desc); + cp = line + strlen(line); + + ndesc = 0; + n = sscanf(line, "%x %*s %*u %*s %*x %*d %*d %s %n", + &id, typebuf, &ndesc); + if (n == 2 && ndesc > 0 && ndesc <= cp - line) { + if (strcmp(typebuf, type) != 0) + return 0; + kdesc = line + ndesc; + if (memcmp(kdesc, desc, dlen) != 0) + return 0; + if (kdesc[dlen] != ':' && + kdesc[dlen] != '\0' && + kdesc[dlen] != ' ') + return 0; + kdesc[dlen] = '\0'; + + /* The key type appends extra stuff to the end of the + * description after a colon in /proc/keys. Colons, + * however, are allowed in descriptions, so we need to + * make a further check. */ + n = keyctl_describe(id, rdesc, sizeof(rdesc) - 1); + if (n < 0) + return 0; + if ((size_t)n >= sizeof(rdesc) - 1) + return 0; + rdesc[n] = '\0'; + + cp = strrchr(rdesc, ';'); + if (!cp) + return 0; + cp++; + if (strcmp(cp, desc) != 0) + return 0; + + + if (destringid && keyctl_link(id, destringid) == -1) + return 0; + + return id; + } + + return 0; +} + +/* inspired by keyutils written by David Howells (dhowells@redhat.com), returns 0 ID on failure */ +static key_serial_t find_key_by_type_and_desc(const char *type, const char *desc, key_serial_t destringid) +{ + key_serial_t id; + int f; + char buf[1024]; + char *newline; + size_t buffer_len = 0; + + int n; + + do { + id = request_key(type, desc, NULL, 0); + } while (id < 0 && errno == EINTR); + if (id >= 0 || errno == ENOMEM) + return id; + + f = open("/proc/keys", O_RDONLY); + if (f < 0) + return 0; + + while ((n = read(f, buf + buffer_len, sizeof(buf) - buffer_len - 1)) > 0) { + buffer_len += n; + buf[buffer_len] = '\0'; + newline = strchr(buf, '\n'); + while (newline != NULL && buffer_len != 0) { + *newline = '\0'; + + if ((id = keyring_process_proc_keys_line(buf, type, desc, destringid))) { + close(f); + return id; + } + + buffer_len -= newline - buf + 1; + assert(buffer_len <= sizeof(buf) - 1); + memmove(buf, newline + 1, buffer_len); + buf[buffer_len] = '\0'; + newline = strchr(buf, '\n'); + } + } + + close(f); + return 0; +} #endif int keyring_check(void) @@ -156,9 +275,9 @@ int keyring_get_passphrase(const char *key_desc, char *buf = NULL; size_t len = 0; - do + do { kid = request_key(key_type_name(USER_KEY), key_desc, NULL, 0); - while (kid < 0 && errno == EINTR); + } while (kid < 0 && errno == EINTR); if (kid < 0) return -errno; @@ -200,13 +319,16 @@ static int keyring_link_key_to_keyring_key_type(const char *type_name, const cha if (!type_name || !key_desc) return -EINVAL; - do + do { kid = request_key(type_name, key_desc, NULL, 0); - while (kid < 0 && errno == EINTR); + } while (kid < 0 && errno == EINTR); if (kid < 0) return 0; + /* see https://mjg59.dreamwidth.org/37333.html */ + if (keyring_to_link == KEY_SPEC_USER_KEYRING || keyring_to_link == KEY_SPEC_USER_SESSION_KEYRING) + keyctl_setperm(kid, KEY_POS_ALL | KEY_USR_ALL); r = keyctl_link(kid, keyring_to_link); if (r < 0) return -errno; @@ -225,9 +347,9 @@ static int keyring_revoke_and_unlink_key_type(const char *type_name, const char if (!type_name || !key_desc) return -EINVAL; - do + do { kid = request_key(type_name, key_desc, NULL, 0); - while (kid < 0 && errno == EINTR); + } while (kid < 0 && errno == EINTR); if (kid < 0) return 0; @@ -253,6 +375,67 @@ const char *key_type_name(key_type_t type) return NULL; } +int32_t keyring_by_name(const char *name) +{ + int32_t id = 0; +#ifdef KERNEL_KEYRING + char *end; + char *name_copy, *name_copy_p; + + if (name[0] == '@') { + if (strcmp(name, "@t" ) == 0) return KEY_SPEC_THREAD_KEYRING; + if (strcmp(name, "@p" ) == 0) return KEY_SPEC_PROCESS_KEYRING; + if (strcmp(name, "@s" ) == 0) return KEY_SPEC_SESSION_KEYRING; + if (strcmp(name, "@u" ) == 0) return KEY_SPEC_USER_KEYRING; + if (strcmp(name, "@us") == 0) return KEY_SPEC_USER_SESSION_KEYRING; + if (strcmp(name, "@g" ) == 0) return KEY_SPEC_GROUP_KEYRING; + if (strcmp(name, "@a" ) == 0) return KEY_SPEC_REQKEY_AUTH_KEY; + + return 0; + } + + /* handle a lookup-by-name request "%:", eg: "%keyring:_ses" */ + name_copy = strdup(name); + if (!name_copy) + goto out; + name_copy_p = name_copy; + + if (name_copy_p[0] == '%') { + const char *type; + + name_copy_p++; + if (!*name_copy_p) + goto out; + + if (*name_copy_p == ':') { + type = "keyring"; + name_copy_p++; + } else { + type = name_copy_p; + name_copy_p = strchr(name_copy_p, ':'); + if (!name_copy_p) + goto out; + *(name_copy_p++) = '\0'; + } + + if (!*name_copy_p) + goto out; + + id = find_key_by_type_and_desc(type, name_copy_p, 0); + goto out; + } + + id = strtoul(name, &end, 0); + if (*end) + id = 0; + +out: + if (name_copy) + free(name_copy); +#endif + return id; +} + key_type_t key_type_by_name(const char *name) { #ifdef KERNEL_KEYRING diff --git a/lib/utils_keyring.h b/lib/utils_keyring.h index 91465626..9c5db0bc 100644 --- a/lib/utils_keyring.h +++ b/lib/utils_keyring.h @@ -23,11 +23,13 @@ #define _UTILS_KEYRING #include +#include typedef enum { LOGON_KEY = 0, USER_KEY, BIG_KEY, TRUSTED_KEY, ENCRYPTED_KEY, INVALID_KEY } key_type_t; const char *key_type_name(key_type_t ktype); key_type_t key_type_by_name(const char *name); +int32_t keyring_by_name(const char *name); int keyring_check(void); diff --git a/src/cryptsetup.c b/src/cryptsetup.c index 68a1f4a6..1acb5c6b 100644 --- a/src/cryptsetup.c +++ b/src/cryptsetup.c @@ -1664,8 +1664,10 @@ static int action_open_luks(void) } } - if (ARG_SET(OPT_LINK_VK_TO_KEYRING_ID)) - crypt_set_keyring_to_link(cd, ARG_INT32(OPT_LINK_VK_TO_KEYRING_ID)); + if (ARG_SET(OPT_LINK_VK_TO_KEYRING_ID)) { + if ((r = crypt_set_keyring_to_link(cd, ARG_STR(OPT_LINK_VK_TO_KEYRING_ID)))) + return r; + } if (ARG_SET(OPT_VOLUME_KEY_FILE_ID)) { keysize = crypt_get_volume_key_size(cd); @@ -2532,8 +2534,10 @@ static int action_luksResume(void) } } - if (ARG_SET(OPT_LINK_VK_TO_KEYRING_ID)) - crypt_set_keyring_to_link(cd, ARG_INT32(OPT_LINK_VK_TO_KEYRING_ID)); + if (ARG_SET(OPT_LINK_VK_TO_KEYRING_ID)) { + if ((r = crypt_set_keyring_to_link(cd, ARG_STR(OPT_LINK_VK_TO_KEYRING_ID)))) + return r; + } if (!isLUKS(crypt_get_type(cd))) { log_err(_("%s is not active LUKS device name or header is missing."), action_argv[0]); diff --git a/src/cryptsetup_arg_list.h b/src/cryptsetup_arg_list.h index b4826890..b74cdb93 100644 --- a/src/cryptsetup_arg_list.h +++ b/src/cryptsetup_arg_list.h @@ -111,7 +111,7 @@ ARG(OPT_KEYSLOT_KEY_SIZE, '\0', POPT_ARG_STRING, N_("LUKS2 keyslot: The size of ARG(OPT_LABEL, '\0', POPT_ARG_STRING, N_("Set label for the LUKS2 device"), NULL, CRYPT_ARG_STRING, {}, OPT_LABEL_ACTIONS) -ARG(OPT_LINK_VK_TO_KEYRING, '\0', POPT_ARG_STRING, N_("Set keyring where to link volume key"), NULL, CRYPT_ARG_INT32, {}, OPT_LINK_VK_TO_KEYRING_ACTIONS) +ARG(OPT_LINK_VK_TO_KEYRING, '\0', POPT_ARG_STRING, N_("Set keyring where to link volume key"), NULL, CRYPT_ARG_STRING, {}, OPT_LINK_VK_TO_KEYRING_ACTIONS) ARG(OPT_LUKS2_KEYSLOTS_SIZE, '\0', POPT_ARG_STRING, N_("LUKS2 header keyslots area size"), N_("bytes"), CRYPT_ARG_UINT64, {}, OPT_LUKS2_KEYSLOTS_SIZE_ACTIONS)