cert_store refreshes its keyring by unregistering and then registering again key_type_cert_store_key. Once keys pin to the kernel module of their key type, this is no longer necessary. Register key_type_cert_store_key once during init before creating sysfs attributes, and track subsystem-created keys explicitly so that the refresh operationunlinks and invalidates previous entries directly. Signed-off-by: Jarkko Sakkinen --- arch/s390/kernel/cert_store.c | 161 ++++++++++++++-------------------- 1 file changed, 67 insertions(+), 94 deletions(-) diff --git a/arch/s390/kernel/cert_store.c b/arch/s390/kernel/cert_store.c index 998f2da56175..ab1a78cdfd4c 100644 --- a/arch/s390/kernel/cert_store.c +++ b/arch/s390/kernel/cert_store.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -296,6 +297,18 @@ static int check_certificate_valid(const struct vce *vce) return check_certificate_hash(vce); } +struct cert_store_key { + struct list_head list; + struct key *key; +}; + +static DEFINE_MUTEX(cs_refresh_lock); +/* Owned references, protected by cs_refresh_lock. */ +static LIST_HEAD(cs_keys); +static struct key *active_cs_keyring; +static struct key *cs_keyring_parent; + +/* Return an owned reference to the user session keyring. */ static struct key *get_user_session_keyring(void) { key_ref_t us_keyring_ref; @@ -305,118 +318,66 @@ static struct key *get_user_session_keyring(void) if (IS_ERR(us_keyring_ref)) { pr_dbf_msg("Couldn't get user session keyring: %ld", PTR_ERR(us_keyring_ref)); - return ERR_PTR(-ENOKEY); + return ERR_CAST(us_keyring_ref); } - key_ref_put(us_keyring_ref); return key_ref_to_ptr(us_keyring_ref); } -/* Invalidate all keys from cert_store keyring. */ -static int invalidate_keyring_keys(struct key *keyring) -{ - unsigned long num_keys, key_index; - size_t keyring_payload_len; - key_serial_t *key_array; - struct key *current_key; - int rc; - - keyring_payload_len = key_type_keyring.read(keyring, NULL, 0); - num_keys = keyring_payload_len / sizeof(key_serial_t); - key_array = kzalloc_objs(key_serial_t, num_keys); - if (!key_array) - return -ENOMEM; - - rc = key_type_keyring.read(keyring, (char *)key_array, keyring_payload_len); - if (rc != keyring_payload_len) { - pr_dbf_msg("Couldn't read keyring payload"); - goto out; - } - - for (key_index = 0; key_index < num_keys; key_index++) { - current_key = key_lookup(key_array[key_index]); - pr_dbf_msg("Invalidating key %08x", current_key->serial); - - key_invalidate(current_key); - key_put(current_key); - rc = key_unlink(keyring, current_key); - if (rc) { - pr_dbf_msg("Couldn't unlink key %08x: %d", current_key->serial, rc); - break; - } - } -out: - kfree(key_array); - return rc; -} - -static struct key *find_cs_keyring(void) -{ - key_ref_t cs_keyring_ref; - struct key *cs_keyring; - - cs_keyring_ref = keyring_search(make_key_ref(get_user_session_keyring(), true), - &key_type_keyring, CERT_STORE_KEYRING_NAME, - false); - if (!IS_ERR(cs_keyring_ref)) { - cs_keyring = key_ref_to_ptr(cs_keyring_ref); - key_ref_put(cs_keyring_ref); - goto found; - } - /* Search default locations: thread, process, session keyrings */ - cs_keyring = request_key(&key_type_keyring, CERT_STORE_KEYRING_NAME, NULL); - if (IS_ERR(cs_keyring)) - return NULL; - key_put(cs_keyring); -found: - return cs_keyring; -} - static void cleanup_cs_keys(void) { - struct key *cs_keyring; + struct cert_store_key *entry, *tmp; + int rc; - cs_keyring = find_cs_keyring(); - if (!cs_keyring) + if (!active_cs_keyring) return; - pr_dbf_msg("Found cert_store keyring. Purging..."); - /* - * Remove cert_store_key_type in case invalidation - * of old cert_store keys failed (= severe error). - */ - if (invalidate_keyring_keys(cs_keyring)) - unregister_key_type(&key_type_cert_store_key); + /* Invalidate even certificates unlinked or linked into other keyrings. */ + list_for_each_entry_safe(entry, tmp, &cs_keys, list) { + key_invalidate(entry->key); + key_put(entry->key); + list_del(&entry->list); + kfree(entry); + } - keyring_clear(cs_keyring); - key_invalidate(cs_keyring); - key_put(cs_keyring); - key_unlink(get_user_session_keyring(), cs_keyring); + rc = keyring_clear(active_cs_keyring); + if (rc) + pr_dbf_msg("Couldn't clear cert_store keyring: %d", rc); + key_invalidate(active_cs_keyring); + rc = key_unlink(cs_keyring_parent, active_cs_keyring); + if (rc) + pr_dbf_msg("Couldn't unlink cert_store keyring: %d", rc); + key_put(active_cs_keyring); + key_put(cs_keyring_parent); + active_cs_keyring = NULL; + cs_keyring_parent = NULL; } +/* Return a borrowed reference; cleanup_cs_keys() owns the keyring. */ static struct key *create_cs_keyring(void) { - static struct key *cs_keyring; + struct key *cs_keyring, *parent; /* Cleanup previous cs_keyring and all associated keys if any. */ cleanup_cs_keys(); + parent = get_user_session_keyring(); + if (IS_ERR(parent)) + return parent; + cs_keyring = keyring_alloc(CERT_STORE_KEYRING_NAME, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(), (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ, KEY_ALLOC_NOT_IN_QUOTA | KEY_ALLOC_SET_KEEP, - NULL, get_user_session_keyring()); + NULL, parent); if (IS_ERR(cs_keyring)) { pr_dbf_msg("Can't allocate cert_store keyring"); - return NULL; + key_put(parent); + return cs_keyring; } + active_cs_keyring = cs_keyring; + cs_keyring_parent = parent; pr_dbf_msg("Successfully allocated cert_store keyring: %08x", cs_keyring->serial); - /* - * In case a previous clean-up ran into an - * error and unregistered key type. - */ - register_key_type(&key_type_cert_store_key); - return cs_keyring; } @@ -454,6 +415,7 @@ static char *get_key_description(struct vcssb *vcssb, const struct vce *vce) static int create_key_from_vce(struct vcssb *vcssb, struct vce *vce, struct key *keyring) { + struct cert_store_key *entry; key_ref_t newkey; char *desc; int rc; @@ -462,6 +424,13 @@ static int create_key_from_vce(struct vcssb *vcssb, struct vce *vce, if (!desc) return -ENOMEM; + /* Reserve cleanup tracking before the key can be linked elsewhere. */ + entry = kmalloc_obj(*entry); + if (!entry) { + rc = -ENOMEM; + goto out; + } + newkey = key_create_or_update( make_key_ref(keyring, true), CERT_STORE_KEY_TYPE_NAME, desc, (u8 *)vce + vce->vce_hdr.vc_offset, @@ -473,10 +442,12 @@ static int create_key_from_vce(struct vcssb *vcssb, struct vce *vce, if (rc) { pr_dbf_msg("Couldn't create a key from Certificate Entry (%d)", rc); rc = -ENOKEY; + kfree(entry); goto out; } - key_ref_put(newkey); + entry->key = key_ref_to_ptr(newkey); + list_add_tail(&entry->list, &cs_keys); out: kfree(desc); return rc; @@ -704,19 +675,18 @@ static int fill_cs_keyring(void) if (rc) goto cleanup_keys; - rc = -ENOMEM; cs_keyring = create_cs_keyring(); - if (!cs_keyring) + if (IS_ERR(cs_keyring)) { + rc = PTR_ERR(cs_keyring); goto cleanup_keys; + } rc = add_certificates_to_keyring(vcssb, cs_keyring); if (rc) - goto cleanup_cs_keyring; + goto cleanup_keys; goto out; -cleanup_cs_keyring: - key_put(cs_keyring); cleanup_keys: cleanup_cs_keys(); out: @@ -724,7 +694,6 @@ static int fill_cs_keyring(void) return rc; } -static DEFINE_MUTEX(cs_refresh_lock); static int cs_status_val = -1; static ssize_t cs_status_show(struct kobject *kobj, @@ -794,14 +763,18 @@ static int __init cert_store_init(void) if (!cert_store_kobj) goto cleanup_dbf; - rc = sysfs_create_files(cert_store_kobj, cert_store_attrs); + rc = register_key_type(&key_type_cert_store_key); if (rc) goto cleanup_kobj; - register_key_type(&key_type_cert_store_key); + rc = sysfs_create_files(cert_store_kobj, cert_store_attrs); + if (rc) + goto cleanup_key_type; return rc; +cleanup_key_type: + unregister_key_type(&key_type_cert_store_key); cleanup_kobj: kobject_put(cert_store_kobj); cleanup_dbf: -- 2.47.3