diff options
author | James Morris <james.l.morris@oracle.com> | 2017-10-18 21:28:38 -0400 |
---|---|---|
committer | James Morris <james.l.morris@oracle.com> | 2017-10-18 21:28:38 -0400 |
commit | 494b9ae7abb84e6d88d7587906aff29dd26cf9d0 (patch) | |
tree | aeb24854a715777aaa9d433d57f5e45d05017f73 | |
parent | 73d3393ada4f70fa3df5639c8d438f2f034c0ecb (diff) | |
parent | 68a1fdbbf8bd3378325e45c19e167a165f9ffc3a (diff) |
Merge commit 'tags/keys-fixes-20171018' into fixes-v4.14-rc5
-rw-r--r-- | crypto/asymmetric_keys/asymmetric_type.c | 4 | ||||
-rw-r--r-- | crypto/asymmetric_keys/pkcs7_parser.c | 3 | ||||
-rw-r--r-- | fs/crypto/keyinfo.c | 5 | ||||
-rw-r--r-- | fs/ecryptfs/ecryptfs_kernel.h | 24 | ||||
-rw-r--r-- | fs/ecryptfs/keystore.c | 9 | ||||
-rw-r--r-- | fs/fscache/object-list.c | 7 | ||||
-rw-r--r-- | include/linux/key.h | 47 | ||||
-rw-r--r-- | lib/digsig.c | 6 | ||||
-rw-r--r-- | net/dns_resolver/dns_key.c | 2 | ||||
-rw-r--r-- | security/keys/Kconfig | 1 | ||||
-rw-r--r-- | security/keys/big_key.c | 4 | ||||
-rw-r--r-- | security/keys/encrypted-keys/encrypted.c | 9 | ||||
-rw-r--r-- | security/keys/gc.c | 8 | ||||
-rw-r--r-- | security/keys/key.c | 41 | ||||
-rw-r--r-- | security/keys/keyctl.c | 9 | ||||
-rw-r--r-- | security/keys/keyring.c | 14 | ||||
-rw-r--r-- | security/keys/permission.c | 7 | ||||
-rw-r--r-- | security/keys/proc.c | 31 | ||||
-rw-r--r-- | security/keys/process_keys.c | 2 | ||||
-rw-r--r-- | security/keys/request_key.c | 7 | ||||
-rw-r--r-- | security/keys/request_key_auth.c | 2 | ||||
-rw-r--r-- | security/keys/trusted.c | 2 | ||||
-rw-r--r-- | security/keys/user_defined.c | 4 |
23 files changed, 168 insertions, 80 deletions
diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index e4b0ed386bc8..39aecad286fe 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c | |||
@@ -57,6 +57,8 @@ struct key *find_asymmetric_key(struct key *keyring, | |||
57 | char *req, *p; | 57 | char *req, *p; |
58 | int len; | 58 | int len; |
59 | 59 | ||
60 | BUG_ON(!id_0 && !id_1); | ||
61 | |||
60 | if (id_0) { | 62 | if (id_0) { |
61 | lookup = id_0->data; | 63 | lookup = id_0->data; |
62 | len = id_0->len; | 64 | len = id_0->len; |
@@ -105,7 +107,7 @@ struct key *find_asymmetric_key(struct key *keyring, | |||
105 | if (id_0 && id_1) { | 107 | if (id_0 && id_1) { |
106 | const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); | 108 | const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); |
107 | 109 | ||
108 | if (!kids->id[0]) { | 110 | if (!kids->id[1]) { |
109 | pr_debug("First ID matches, but second is missing\n"); | 111 | pr_debug("First ID matches, but second is missing\n"); |
110 | goto reject; | 112 | goto reject; |
111 | } | 113 | } |
diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c index af4cd8649117..d140d8bb2c96 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.c +++ b/crypto/asymmetric_keys/pkcs7_parser.c | |||
@@ -88,6 +88,9 @@ static int pkcs7_check_authattrs(struct pkcs7_message *msg) | |||
88 | bool want = false; | 88 | bool want = false; |
89 | 89 | ||
90 | sinfo = msg->signed_infos; | 90 | sinfo = msg->signed_infos; |
91 | if (!sinfo) | ||
92 | goto inconsistent; | ||
93 | |||
91 | if (sinfo->authattrs) { | 94 | if (sinfo->authattrs) { |
92 | want = true; | 95 | want = true; |
93 | msg->have_authattrs = true; | 96 | msg->have_authattrs = true; |
diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index 018c588c7ac3..8e704d12a1cf 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c | |||
@@ -109,6 +109,11 @@ static int validate_user_key(struct fscrypt_info *crypt_info, | |||
109 | goto out; | 109 | goto out; |
110 | } | 110 | } |
111 | ukp = user_key_payload_locked(keyring_key); | 111 | ukp = user_key_payload_locked(keyring_key); |
112 | if (!ukp) { | ||
113 | /* key was revoked before we acquired its semaphore */ | ||
114 | res = -EKEYREVOKED; | ||
115 | goto out; | ||
116 | } | ||
112 | if (ukp->datalen != sizeof(struct fscrypt_key)) { | 117 | if (ukp->datalen != sizeof(struct fscrypt_key)) { |
113 | res = -EINVAL; | 118 | res = -EINVAL; |
114 | goto out; | 119 | goto out; |
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h index 9c351bf757b2..3fbc0ff79699 100644 --- a/fs/ecryptfs/ecryptfs_kernel.h +++ b/fs/ecryptfs/ecryptfs_kernel.h | |||
@@ -84,11 +84,16 @@ struct ecryptfs_page_crypt_context { | |||
84 | static inline struct ecryptfs_auth_tok * | 84 | static inline struct ecryptfs_auth_tok * |
85 | ecryptfs_get_encrypted_key_payload_data(struct key *key) | 85 | ecryptfs_get_encrypted_key_payload_data(struct key *key) |
86 | { | 86 | { |
87 | if (key->type == &key_type_encrypted) | 87 | struct encrypted_key_payload *payload; |
88 | return (struct ecryptfs_auth_tok *) | 88 | |
89 | (&((struct encrypted_key_payload *)key->payload.data[0])->payload_data); | 89 | if (key->type != &key_type_encrypted) |
90 | else | ||
91 | return NULL; | 90 | return NULL; |
91 | |||
92 | payload = key->payload.data[0]; | ||
93 | if (!payload) | ||
94 | return ERR_PTR(-EKEYREVOKED); | ||
95 | |||
96 | return (struct ecryptfs_auth_tok *)payload->payload_data; | ||
92 | } | 97 | } |
93 | 98 | ||
94 | static inline struct key *ecryptfs_get_encrypted_key(char *sig) | 99 | static inline struct key *ecryptfs_get_encrypted_key(char *sig) |
@@ -114,12 +119,17 @@ static inline struct ecryptfs_auth_tok * | |||
114 | ecryptfs_get_key_payload_data(struct key *key) | 119 | ecryptfs_get_key_payload_data(struct key *key) |
115 | { | 120 | { |
116 | struct ecryptfs_auth_tok *auth_tok; | 121 | struct ecryptfs_auth_tok *auth_tok; |
122 | struct user_key_payload *ukp; | ||
117 | 123 | ||
118 | auth_tok = ecryptfs_get_encrypted_key_payload_data(key); | 124 | auth_tok = ecryptfs_get_encrypted_key_payload_data(key); |
119 | if (!auth_tok) | 125 | if (auth_tok) |
120 | return (struct ecryptfs_auth_tok *)user_key_payload_locked(key)->data; | ||
121 | else | ||
122 | return auth_tok; | 126 | return auth_tok; |
127 | |||
128 | ukp = user_key_payload_locked(key); | ||
129 | if (!ukp) | ||
130 | return ERR_PTR(-EKEYREVOKED); | ||
131 | |||
132 | return (struct ecryptfs_auth_tok *)ukp->data; | ||
123 | } | 133 | } |
124 | 134 | ||
125 | #define ECRYPTFS_MAX_KEYSET_SIZE 1024 | 135 | #define ECRYPTFS_MAX_KEYSET_SIZE 1024 |
diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c index 3cf1546dca82..fa218cd64f74 100644 --- a/fs/ecryptfs/keystore.c +++ b/fs/ecryptfs/keystore.c | |||
@@ -459,7 +459,8 @@ out: | |||
459 | * @auth_tok_key: key containing the authentication token | 459 | * @auth_tok_key: key containing the authentication token |
460 | * @auth_tok: authentication token | 460 | * @auth_tok: authentication token |
461 | * | 461 | * |
462 | * Returns zero on valid auth tok; -EINVAL otherwise | 462 | * Returns zero on valid auth tok; -EINVAL if the payload is invalid; or |
463 | * -EKEYREVOKED if the key was revoked before we acquired its semaphore. | ||
463 | */ | 464 | */ |
464 | static int | 465 | static int |
465 | ecryptfs_verify_auth_tok_from_key(struct key *auth_tok_key, | 466 | ecryptfs_verify_auth_tok_from_key(struct key *auth_tok_key, |
@@ -468,6 +469,12 @@ ecryptfs_verify_auth_tok_from_key(struct key *auth_tok_key, | |||
468 | int rc = 0; | 469 | int rc = 0; |
469 | 470 | ||
470 | (*auth_tok) = ecryptfs_get_key_payload_data(auth_tok_key); | 471 | (*auth_tok) = ecryptfs_get_key_payload_data(auth_tok_key); |
472 | if (IS_ERR(*auth_tok)) { | ||
473 | rc = PTR_ERR(*auth_tok); | ||
474 | *auth_tok = NULL; | ||
475 | goto out; | ||
476 | } | ||
477 | |||
471 | if (ecryptfs_verify_version((*auth_tok)->version)) { | 478 | if (ecryptfs_verify_version((*auth_tok)->version)) { |
472 | printk(KERN_ERR "Data structure version mismatch. Userspace " | 479 | printk(KERN_ERR "Data structure version mismatch. Userspace " |
473 | "tools must match eCryptfs kernel module with major " | 480 | "tools must match eCryptfs kernel module with major " |
diff --git a/fs/fscache/object-list.c b/fs/fscache/object-list.c index b5ab06fabc60..0438d4cd91ef 100644 --- a/fs/fscache/object-list.c +++ b/fs/fscache/object-list.c | |||
@@ -331,6 +331,13 @@ static void fscache_objlist_config(struct fscache_objlist_data *data) | |||
331 | rcu_read_lock(); | 331 | rcu_read_lock(); |
332 | 332 | ||
333 | confkey = user_key_payload_rcu(key); | 333 | confkey = user_key_payload_rcu(key); |
334 | if (!confkey) { | ||
335 | /* key was revoked */ | ||
336 | rcu_read_unlock(); | ||
337 | key_put(key); | ||
338 | goto no_config; | ||
339 | } | ||
340 | |||
334 | buf = confkey->data; | 341 | buf = confkey->data; |
335 | 342 | ||
336 | for (len = confkey->datalen - 1; len >= 0; len--) { | 343 | for (len = confkey->datalen - 1; len >= 0; len--) { |
diff --git a/include/linux/key.h b/include/linux/key.h index e315e16b6ff8..8a15cabe928d 100644 --- a/include/linux/key.h +++ b/include/linux/key.h | |||
@@ -138,6 +138,11 @@ struct key_restriction { | |||
138 | struct key_type *keytype; | 138 | struct key_type *keytype; |
139 | }; | 139 | }; |
140 | 140 | ||
141 | enum key_state { | ||
142 | KEY_IS_UNINSTANTIATED, | ||
143 | KEY_IS_POSITIVE, /* Positively instantiated */ | ||
144 | }; | ||
145 | |||
141 | /*****************************************************************************/ | 146 | /*****************************************************************************/ |
142 | /* | 147 | /* |
143 | * authentication token / access credential / keyring | 148 | * authentication token / access credential / keyring |
@@ -169,6 +174,7 @@ struct key { | |||
169 | * - may not match RCU dereferenced payload | 174 | * - may not match RCU dereferenced payload |
170 | * - payload should contain own length | 175 | * - payload should contain own length |
171 | */ | 176 | */ |
177 | short state; /* Key state (+) or rejection error (-) */ | ||
172 | 178 | ||
173 | #ifdef KEY_DEBUGGING | 179 | #ifdef KEY_DEBUGGING |
174 | unsigned magic; | 180 | unsigned magic; |
@@ -176,18 +182,16 @@ struct key { | |||
176 | #endif | 182 | #endif |
177 | 183 | ||
178 | unsigned long flags; /* status flags (change with bitops) */ | 184 | unsigned long flags; /* status flags (change with bitops) */ |
179 | #define KEY_FLAG_INSTANTIATED 0 /* set if key has been instantiated */ | 185 | #define KEY_FLAG_DEAD 0 /* set if key type has been deleted */ |
180 | #define KEY_FLAG_DEAD 1 /* set if key type has been deleted */ | 186 | #define KEY_FLAG_REVOKED 1 /* set if key had been revoked */ |
181 | #define KEY_FLAG_REVOKED 2 /* set if key had been revoked */ | 187 | #define KEY_FLAG_IN_QUOTA 2 /* set if key consumes quota */ |
182 | #define KEY_FLAG_IN_QUOTA 3 /* set if key consumes quota */ | 188 | #define KEY_FLAG_USER_CONSTRUCT 3 /* set if key is being constructed in userspace */ |
183 | #define KEY_FLAG_USER_CONSTRUCT 4 /* set if key is being constructed in userspace */ | 189 | #define KEY_FLAG_ROOT_CAN_CLEAR 4 /* set if key can be cleared by root without permission */ |
184 | #define KEY_FLAG_NEGATIVE 5 /* set if key is negative */ | 190 | #define KEY_FLAG_INVALIDATED 5 /* set if key has been invalidated */ |
185 | #define KEY_FLAG_ROOT_CAN_CLEAR 6 /* set if key can be cleared by root without permission */ | 191 | #define KEY_FLAG_BUILTIN 6 /* set if key is built in to the kernel */ |
186 | #define KEY_FLAG_INVALIDATED 7 /* set if key has been invalidated */ | 192 | #define KEY_FLAG_ROOT_CAN_INVAL 7 /* set if key can be invalidated by root without permission */ |
187 | #define KEY_FLAG_BUILTIN 8 /* set if key is built in to the kernel */ | 193 | #define KEY_FLAG_KEEP 8 /* set if key should not be removed */ |
188 | #define KEY_FLAG_ROOT_CAN_INVAL 9 /* set if key can be invalidated by root without permission */ | 194 | #define KEY_FLAG_UID_KEYRING 9 /* set if key is a user or user session keyring */ |
189 | #define KEY_FLAG_KEEP 10 /* set if key should not be removed */ | ||
190 | #define KEY_FLAG_UID_KEYRING 11 /* set if key is a user or user session keyring */ | ||
191 | 195 | ||
192 | /* the key type and key description string | 196 | /* the key type and key description string |
193 | * - the desc is used to match a key against search criteria | 197 | * - the desc is used to match a key against search criteria |
@@ -213,7 +217,6 @@ struct key { | |||
213 | struct list_head name_link; | 217 | struct list_head name_link; |
214 | struct assoc_array keys; | 218 | struct assoc_array keys; |
215 | }; | 219 | }; |
216 | int reject_error; | ||
217 | }; | 220 | }; |
218 | 221 | ||
219 | /* This is set on a keyring to restrict the addition of a link to a key | 222 | /* This is set on a keyring to restrict the addition of a link to a key |
@@ -353,17 +356,27 @@ extern void key_set_timeout(struct key *, unsigned); | |||
353 | #define KEY_NEED_SETATTR 0x20 /* Require permission to change attributes */ | 356 | #define KEY_NEED_SETATTR 0x20 /* Require permission to change attributes */ |
354 | #define KEY_NEED_ALL 0x3f /* All the above permissions */ | 357 | #define KEY_NEED_ALL 0x3f /* All the above permissions */ |
355 | 358 | ||
359 | static inline short key_read_state(const struct key *key) | ||
360 | { | ||
361 | /* Barrier versus mark_key_instantiated(). */ | ||
362 | return smp_load_acquire(&key->state); | ||
363 | } | ||
364 | |||
356 | /** | 365 | /** |
357 | * key_is_instantiated - Determine if a key has been positively instantiated | 366 | * key_is_positive - Determine if a key has been positively instantiated |
358 | * @key: The key to check. | 367 | * @key: The key to check. |
359 | * | 368 | * |
360 | * Return true if the specified key has been positively instantiated, false | 369 | * Return true if the specified key has been positively instantiated, false |
361 | * otherwise. | 370 | * otherwise. |
362 | */ | 371 | */ |
363 | static inline bool key_is_instantiated(const struct key *key) | 372 | static inline bool key_is_positive(const struct key *key) |
373 | { | ||
374 | return key_read_state(key) == KEY_IS_POSITIVE; | ||
375 | } | ||
376 | |||
377 | static inline bool key_is_negative(const struct key *key) | ||
364 | { | 378 | { |
365 | return test_bit(KEY_FLAG_INSTANTIATED, &key->flags) && | 379 | return key_read_state(key) < 0; |
366 | !test_bit(KEY_FLAG_NEGATIVE, &key->flags); | ||
367 | } | 380 | } |
368 | 381 | ||
369 | #define dereference_key_rcu(KEY) \ | 382 | #define dereference_key_rcu(KEY) \ |
diff --git a/lib/digsig.c b/lib/digsig.c index 03d7c63837ae..6ba6fcd92dd1 100644 --- a/lib/digsig.c +++ b/lib/digsig.c | |||
@@ -87,6 +87,12 @@ static int digsig_verify_rsa(struct key *key, | |||
87 | down_read(&key->sem); | 87 | down_read(&key->sem); |
88 | ukp = user_key_payload_locked(key); | 88 | ukp = user_key_payload_locked(key); |
89 | 89 | ||
90 | if (!ukp) { | ||
91 | /* key was revoked before we acquired its semaphore */ | ||
92 | err = -EKEYREVOKED; | ||
93 | goto err1; | ||
94 | } | ||
95 | |||
90 | if (ukp->datalen < sizeof(*pkh)) | 96 | if (ukp->datalen < sizeof(*pkh)) |
91 | goto err1; | 97 | goto err1; |
92 | 98 | ||
diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c index 8737412c7b27..e1d4d898a007 100644 --- a/net/dns_resolver/dns_key.c +++ b/net/dns_resolver/dns_key.c | |||
@@ -224,7 +224,7 @@ static int dns_resolver_match_preparse(struct key_match_data *match_data) | |||
224 | static void dns_resolver_describe(const struct key *key, struct seq_file *m) | 224 | static void dns_resolver_describe(const struct key *key, struct seq_file *m) |
225 | { | 225 | { |
226 | seq_puts(m, key->description); | 226 | seq_puts(m, key->description); |
227 | if (key_is_instantiated(key)) { | 227 | if (key_is_positive(key)) { |
228 | int err = PTR_ERR(key->payload.data[dns_key_error]); | 228 | int err = PTR_ERR(key->payload.data[dns_key_error]); |
229 | 229 | ||
230 | if (err) | 230 | if (err) |
diff --git a/security/keys/Kconfig b/security/keys/Kconfig index 91eafada3164..6462e6654ccf 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig | |||
@@ -45,6 +45,7 @@ config BIG_KEYS | |||
45 | bool "Large payload keys" | 45 | bool "Large payload keys" |
46 | depends on KEYS | 46 | depends on KEYS |
47 | depends on TMPFS | 47 | depends on TMPFS |
48 | select CRYPTO | ||
48 | select CRYPTO_AES | 49 | select CRYPTO_AES |
49 | select CRYPTO_GCM | 50 | select CRYPTO_GCM |
50 | help | 51 | help |
diff --git a/security/keys/big_key.c b/security/keys/big_key.c index e607830b6154..929e14978c42 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c | |||
@@ -247,7 +247,7 @@ void big_key_revoke(struct key *key) | |||
247 | 247 | ||
248 | /* clear the quota */ | 248 | /* clear the quota */ |
249 | key_payload_reserve(key, 0); | 249 | key_payload_reserve(key, 0); |
250 | if (key_is_instantiated(key) && | 250 | if (key_is_positive(key) && |
251 | (size_t)key->payload.data[big_key_len] > BIG_KEY_FILE_THRESHOLD) | 251 | (size_t)key->payload.data[big_key_len] > BIG_KEY_FILE_THRESHOLD) |
252 | vfs_truncate(path, 0); | 252 | vfs_truncate(path, 0); |
253 | } | 253 | } |
@@ -279,7 +279,7 @@ void big_key_describe(const struct key *key, struct seq_file *m) | |||
279 | 279 | ||
280 | seq_puts(m, key->description); | 280 | seq_puts(m, key->description); |
281 | 281 | ||
282 | if (key_is_instantiated(key)) | 282 | if (key_is_positive(key)) |
283 | seq_printf(m, ": %zu [%s]", | 283 | seq_printf(m, ": %zu [%s]", |
284 | datalen, | 284 | datalen, |
285 | datalen > BIG_KEY_FILE_THRESHOLD ? "file" : "buff"); | 285 | datalen > BIG_KEY_FILE_THRESHOLD ? "file" : "buff"); |
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 69855ba0d3b3..d92cbf9687c3 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c | |||
@@ -309,6 +309,13 @@ static struct key *request_user_key(const char *master_desc, const u8 **master_k | |||
309 | 309 | ||
310 | down_read(&ukey->sem); | 310 | down_read(&ukey->sem); |
311 | upayload = user_key_payload_locked(ukey); | 311 | upayload = user_key_payload_locked(ukey); |
312 | if (!upayload) { | ||
313 | /* key was revoked before we acquired its semaphore */ | ||
314 | up_read(&ukey->sem); | ||
315 | key_put(ukey); | ||
316 | ukey = ERR_PTR(-EKEYREVOKED); | ||
317 | goto error; | ||
318 | } | ||
312 | *master_key = upayload->data; | 319 | *master_key = upayload->data; |
313 | *master_keylen = upayload->datalen; | 320 | *master_keylen = upayload->datalen; |
314 | error: | 321 | error: |
@@ -847,7 +854,7 @@ static int encrypted_update(struct key *key, struct key_preparsed_payload *prep) | |||
847 | size_t datalen = prep->datalen; | 854 | size_t datalen = prep->datalen; |
848 | int ret = 0; | 855 | int ret = 0; |
849 | 856 | ||
850 | if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) | 857 | if (key_is_negative(key)) |
851 | return -ENOKEY; | 858 | return -ENOKEY; |
852 | if (datalen <= 0 || datalen > 32767 || !prep->data) | 859 | if (datalen <= 0 || datalen > 32767 || !prep->data) |
853 | return -EINVAL; | 860 | return -EINVAL; |
diff --git a/security/keys/gc.c b/security/keys/gc.c index 87cb260e4890..f01d48cb3de1 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c | |||
@@ -129,15 +129,15 @@ static noinline void key_gc_unused_keys(struct list_head *keys) | |||
129 | while (!list_empty(keys)) { | 129 | while (!list_empty(keys)) { |
130 | struct key *key = | 130 | struct key *key = |
131 | list_entry(keys->next, struct key, graveyard_link); | 131 | list_entry(keys->next, struct key, graveyard_link); |
132 | short state = key->state; | ||
133 | |||
132 | list_del(&key->graveyard_link); | 134 | list_del(&key->graveyard_link); |
133 | 135 | ||
134 | kdebug("- %u", key->serial); | 136 | kdebug("- %u", key->serial); |
135 | key_check(key); | 137 | key_check(key); |
136 | 138 | ||
137 | /* Throw away the key data if the key is instantiated */ | 139 | /* Throw away the key data if the key is instantiated */ |
138 | if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags) && | 140 | if (state == KEY_IS_POSITIVE && key->type->destroy) |
139 | !test_bit(KEY_FLAG_NEGATIVE, &key->flags) && | ||
140 | key->type->destroy) | ||
141 | key->type->destroy(key); | 141 | key->type->destroy(key); |
142 | 142 | ||
143 | security_key_free(key); | 143 | security_key_free(key); |
@@ -151,7 +151,7 @@ static noinline void key_gc_unused_keys(struct list_head *keys) | |||
151 | } | 151 | } |
152 | 152 | ||
153 | atomic_dec(&key->user->nkeys); | 153 | atomic_dec(&key->user->nkeys); |
154 | if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) | 154 | if (state != KEY_IS_UNINSTANTIATED) |
155 | atomic_dec(&key->user->nikeys); | 155 | atomic_dec(&key->user->nikeys); |
156 | 156 | ||
157 | key_user_put(key->user); | 157 | key_user_put(key->user); |
diff --git a/security/keys/key.c b/security/keys/key.c index eb914a838840..83bf4b4afd49 100644 --- a/security/keys/key.c +++ b/security/keys/key.c | |||
@@ -402,6 +402,18 @@ int key_payload_reserve(struct key *key, size_t datalen) | |||
402 | EXPORT_SYMBOL(key_payload_reserve); | 402 | EXPORT_SYMBOL(key_payload_reserve); |
403 | 403 | ||
404 | /* | 404 | /* |
405 | * Change the key state to being instantiated. | ||
406 | */ | ||
407 | static void mark_key_instantiated(struct key *key, int reject_error) | ||
408 | { | ||
409 | /* Commit the payload before setting the state; barrier versus | ||
410 | * key_read_state(). | ||
411 | */ | ||
412 | smp_store_release(&key->state, | ||
413 | (reject_error < 0) ? reject_error : KEY_IS_POSITIVE); | ||
414 | } | ||
415 | |||
416 | /* | ||
405 | * Instantiate a key and link it into the target keyring atomically. Must be | 417 | * Instantiate a key and link it into the target keyring atomically. Must be |
406 | * called with the target keyring's semaphore writelocked. The target key's | 418 | * called with the target keyring's semaphore writelocked. The target key's |
407 | * semaphore need not be locked as instantiation is serialised by | 419 | * semaphore need not be locked as instantiation is serialised by |
@@ -424,14 +436,14 @@ static int __key_instantiate_and_link(struct key *key, | |||
424 | mutex_lock(&key_construction_mutex); | 436 | mutex_lock(&key_construction_mutex); |
425 | 437 | ||
426 | /* can't instantiate twice */ | 438 | /* can't instantiate twice */ |
427 | if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) { | 439 | if (key->state == KEY_IS_UNINSTANTIATED) { |
428 | /* instantiate the key */ | 440 | /* instantiate the key */ |
429 | ret = key->type->instantiate(key, prep); | 441 | ret = key->type->instantiate(key, prep); |
430 | 442 | ||
431 | if (ret == 0) { | 443 | if (ret == 0) { |
432 | /* mark the key as being instantiated */ | 444 | /* mark the key as being instantiated */ |
433 | atomic_inc(&key->user->nikeys); | 445 | atomic_inc(&key->user->nikeys); |
434 | set_bit(KEY_FLAG_INSTANTIATED, &key->flags); | 446 | mark_key_instantiated(key, 0); |
435 | 447 | ||
436 | if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags)) | 448 | if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags)) |
437 | awaken = 1; | 449 | awaken = 1; |
@@ -577,13 +589,10 @@ int key_reject_and_link(struct key *key, | |||
577 | mutex_lock(&key_construction_mutex); | 589 | mutex_lock(&key_construction_mutex); |
578 | 590 | ||
579 | /* can't instantiate twice */ | 591 | /* can't instantiate twice */ |
580 | if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) { | 592 | if (key->state == KEY_IS_UNINSTANTIATED) { |
581 | /* mark the key as being negatively instantiated */ | 593 | /* mark the key as being negatively instantiated */ |
582 | atomic_inc(&key->user->nikeys); | 594 | atomic_inc(&key->user->nikeys); |
583 | key->reject_error = -error; | 595 | mark_key_instantiated(key, -error); |
584 | smp_wmb(); | ||
585 | set_bit(KEY_FLAG_NEGATIVE, &key->flags); | ||
586 | set_bit(KEY_FLAG_INSTANTIATED, &key->flags); | ||
587 | now = current_kernel_time(); | 596 | now = current_kernel_time(); |
588 | key->expiry = now.tv_sec + timeout; | 597 | key->expiry = now.tv_sec + timeout; |
589 | key_schedule_gc(key->expiry + key_gc_delay); | 598 | key_schedule_gc(key->expiry + key_gc_delay); |
@@ -752,8 +761,8 @@ static inline key_ref_t __key_update(key_ref_t key_ref, | |||
752 | 761 | ||
753 | ret = key->type->update(key, prep); | 762 | ret = key->type->update(key, prep); |
754 | if (ret == 0) | 763 | if (ret == 0) |
755 | /* updating a negative key instantiates it */ | 764 | /* Updating a negative key positively instantiates it */ |
756 | clear_bit(KEY_FLAG_NEGATIVE, &key->flags); | 765 | mark_key_instantiated(key, 0); |
757 | 766 | ||
758 | up_write(&key->sem); | 767 | up_write(&key->sem); |
759 | 768 | ||
@@ -936,6 +945,16 @@ error: | |||
936 | */ | 945 | */ |
937 | __key_link_end(keyring, &index_key, edit); | 946 | __key_link_end(keyring, &index_key, edit); |
938 | 947 | ||
948 | key = key_ref_to_ptr(key_ref); | ||
949 | if (test_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags)) { | ||
950 | ret = wait_for_key_construction(key, true); | ||
951 | if (ret < 0) { | ||
952 | key_ref_put(key_ref); | ||
953 | key_ref = ERR_PTR(ret); | ||
954 | goto error_free_prep; | ||
955 | } | ||
956 | } | ||
957 | |||
939 | key_ref = __key_update(key_ref, &prep); | 958 | key_ref = __key_update(key_ref, &prep); |
940 | goto error_free_prep; | 959 | goto error_free_prep; |
941 | } | 960 | } |
@@ -986,8 +1005,8 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen) | |||
986 | 1005 | ||
987 | ret = key->type->update(key, &prep); | 1006 | ret = key->type->update(key, &prep); |
988 | if (ret == 0) | 1007 | if (ret == 0) |
989 | /* updating a negative key instantiates it */ | 1008 | /* Updating a negative key positively instantiates it */ |
990 | clear_bit(KEY_FLAG_NEGATIVE, &key->flags); | 1009 | mark_key_instantiated(key, 0); |
991 | 1010 | ||
992 | up_write(&key->sem); | 1011 | up_write(&key->sem); |
993 | 1012 | ||
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 365ff85d7e27..76d22f726ae4 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c | |||
@@ -766,10 +766,9 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen) | |||
766 | 766 | ||
767 | key = key_ref_to_ptr(key_ref); | 767 | key = key_ref_to_ptr(key_ref); |
768 | 768 | ||
769 | if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) { | 769 | ret = key_read_state(key); |
770 | ret = -ENOKEY; | 770 | if (ret < 0) |
771 | goto error2; | 771 | goto error2; /* Negatively instantiated */ |
772 | } | ||
773 | 772 | ||
774 | /* see if we can read it directly */ | 773 | /* see if we can read it directly */ |
775 | ret = key_permission(key_ref, KEY_NEED_READ); | 774 | ret = key_permission(key_ref, KEY_NEED_READ); |
@@ -901,7 +900,7 @@ long keyctl_chown_key(key_serial_t id, uid_t user, gid_t group) | |||
901 | atomic_dec(&key->user->nkeys); | 900 | atomic_dec(&key->user->nkeys); |
902 | atomic_inc(&newowner->nkeys); | 901 | atomic_inc(&newowner->nkeys); |
903 | 902 | ||
904 | if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) { | 903 | if (key->state != KEY_IS_UNINSTANTIATED) { |
905 | atomic_dec(&key->user->nikeys); | 904 | atomic_dec(&key->user->nikeys); |
906 | atomic_inc(&newowner->nikeys); | 905 | atomic_inc(&newowner->nikeys); |
907 | } | 906 | } |
diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 4fa82a8a9c0e..a7e51f793867 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c | |||
@@ -414,7 +414,7 @@ static void keyring_describe(const struct key *keyring, struct seq_file *m) | |||
414 | else | 414 | else |
415 | seq_puts(m, "[anon]"); | 415 | seq_puts(m, "[anon]"); |
416 | 416 | ||
417 | if (key_is_instantiated(keyring)) { | 417 | if (key_is_positive(keyring)) { |
418 | if (keyring->keys.nr_leaves_on_tree != 0) | 418 | if (keyring->keys.nr_leaves_on_tree != 0) |
419 | seq_printf(m, ": %lu", keyring->keys.nr_leaves_on_tree); | 419 | seq_printf(m, ": %lu", keyring->keys.nr_leaves_on_tree); |
420 | else | 420 | else |
@@ -553,7 +553,8 @@ static int keyring_search_iterator(const void *object, void *iterator_data) | |||
553 | { | 553 | { |
554 | struct keyring_search_context *ctx = iterator_data; | 554 | struct keyring_search_context *ctx = iterator_data; |
555 | const struct key *key = keyring_ptr_to_key(object); | 555 | const struct key *key = keyring_ptr_to_key(object); |
556 | unsigned long kflags = key->flags; | 556 | unsigned long kflags = READ_ONCE(key->flags); |
557 | short state = READ_ONCE(key->state); | ||
557 | 558 | ||
558 | kenter("{%d}", key->serial); | 559 | kenter("{%d}", key->serial); |
559 | 560 | ||
@@ -565,6 +566,8 @@ static int keyring_search_iterator(const void *object, void *iterator_data) | |||
565 | 566 | ||
566 | /* skip invalidated, revoked and expired keys */ | 567 | /* skip invalidated, revoked and expired keys */ |
567 | if (ctx->flags & KEYRING_SEARCH_DO_STATE_CHECK) { | 568 | if (ctx->flags & KEYRING_SEARCH_DO_STATE_CHECK) { |
569 | time_t expiry = READ_ONCE(key->expiry); | ||
570 | |||
568 | if (kflags & ((1 << KEY_FLAG_INVALIDATED) | | 571 | if (kflags & ((1 << KEY_FLAG_INVALIDATED) | |
569 | (1 << KEY_FLAG_REVOKED))) { | 572 | (1 << KEY_FLAG_REVOKED))) { |
570 | ctx->result = ERR_PTR(-EKEYREVOKED); | 573 | ctx->result = ERR_PTR(-EKEYREVOKED); |
@@ -572,7 +575,7 @@ static int keyring_search_iterator(const void *object, void *iterator_data) | |||
572 | goto skipped; | 575 | goto skipped; |
573 | } | 576 | } |
574 | 577 | ||
575 | if (key->expiry && ctx->now.tv_sec >= key->expiry) { | 578 | if (expiry && ctx->now.tv_sec >= expiry) { |
576 | if (!(ctx->flags & KEYRING_SEARCH_SKIP_EXPIRED)) | 579 | if (!(ctx->flags & KEYRING_SEARCH_SKIP_EXPIRED)) |
577 | ctx->result = ERR_PTR(-EKEYEXPIRED); | 580 | ctx->result = ERR_PTR(-EKEYEXPIRED); |
578 | kleave(" = %d [expire]", ctx->skipped_ret); | 581 | kleave(" = %d [expire]", ctx->skipped_ret); |
@@ -597,9 +600,8 @@ static int keyring_search_iterator(const void *object, void *iterator_data) | |||
597 | 600 | ||
598 | if (ctx->flags & KEYRING_SEARCH_DO_STATE_CHECK) { | 601 | if (ctx->flags & KEYRING_SEARCH_DO_STATE_CHECK) { |
599 | /* we set a different error code if we pass a negative key */ | 602 | /* we set a different error code if we pass a negative key */ |
600 | if (kflags & (1 << KEY_FLAG_NEGATIVE)) { | 603 | if (state < 0) { |
601 | smp_rmb(); | 604 | ctx->result = ERR_PTR(state); |
602 | ctx->result = ERR_PTR(key->reject_error); | ||
603 | kleave(" = %d [neg]", ctx->skipped_ret); | 605 | kleave(" = %d [neg]", ctx->skipped_ret); |
604 | goto skipped; | 606 | goto skipped; |
605 | } | 607 | } |
diff --git a/security/keys/permission.c b/security/keys/permission.c index 732cc0beffdf..a72b4dd70c8a 100644 --- a/security/keys/permission.c +++ b/security/keys/permission.c | |||
@@ -88,7 +88,8 @@ EXPORT_SYMBOL(key_task_permission); | |||
88 | */ | 88 | */ |
89 | int key_validate(const struct key *key) | 89 | int key_validate(const struct key *key) |
90 | { | 90 | { |
91 | unsigned long flags = key->flags; | 91 | unsigned long flags = READ_ONCE(key->flags); |
92 | time_t expiry = READ_ONCE(key->expiry); | ||
92 | 93 | ||
93 | if (flags & (1 << KEY_FLAG_INVALIDATED)) | 94 | if (flags & (1 << KEY_FLAG_INVALIDATED)) |
94 | return -ENOKEY; | 95 | return -ENOKEY; |
@@ -99,9 +100,9 @@ int key_validate(const struct key *key) | |||
99 | return -EKEYREVOKED; | 100 | return -EKEYREVOKED; |
100 | 101 | ||
101 | /* check it hasn't expired */ | 102 | /* check it hasn't expired */ |
102 | if (key->expiry) { | 103 | if (expiry) { |
103 | struct timespec now = current_kernel_time(); | 104 | struct timespec now = current_kernel_time(); |
104 | if (now.tv_sec >= key->expiry) | 105 | if (now.tv_sec >= expiry) |
105 | return -EKEYEXPIRED; | 106 | return -EKEYEXPIRED; |
106 | } | 107 | } |
107 | 108 | ||
diff --git a/security/keys/proc.c b/security/keys/proc.c index de834309d100..6d1fcbba1e09 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c | |||
@@ -179,9 +179,12 @@ static int proc_keys_show(struct seq_file *m, void *v) | |||
179 | struct rb_node *_p = v; | 179 | struct rb_node *_p = v; |
180 | struct key *key = rb_entry(_p, struct key, serial_node); | 180 | struct key *key = rb_entry(_p, struct key, serial_node); |
181 | struct timespec now; | 181 | struct timespec now; |
182 | time_t expiry; | ||
182 | unsigned long timo; | 183 | unsigned long timo; |
184 | unsigned long flags; | ||
183 | key_ref_t key_ref, skey_ref; | 185 | key_ref_t key_ref, skey_ref; |
184 | char xbuf[16]; | 186 | char xbuf[16]; |
187 | short state; | ||
185 | int rc; | 188 | int rc; |
186 | 189 | ||
187 | struct keyring_search_context ctx = { | 190 | struct keyring_search_context ctx = { |
@@ -217,12 +220,13 @@ static int proc_keys_show(struct seq_file *m, void *v) | |||
217 | rcu_read_lock(); | 220 | rcu_read_lock(); |
218 | 221 | ||
219 | /* come up with a suitable timeout value */ | 222 | /* come up with a suitable timeout value */ |
220 | if (key->expiry == 0) { | 223 | expiry = READ_ONCE(key->expiry); |
224 | if (expiry == 0) { | ||
221 | memcpy(xbuf, "perm", 5); | 225 | memcpy(xbuf, "perm", 5); |
222 | } else if (now.tv_sec >= key->expiry) { | 226 | } else if (now.tv_sec >= expiry) { |
223 | memcpy(xbuf, "expd", 5); | 227 | memcpy(xbuf, "expd", 5); |
224 | } else { | 228 | } else { |
225 | timo = key->expiry - now.tv_sec; | 229 | timo = expiry - now.tv_sec; |
226 | 230 | ||
227 | if (timo < 60) | 231 | if (timo < 60) |
228 | sprintf(xbuf, "%lus", timo); | 232 | sprintf(xbuf, "%lus", timo); |
@@ -236,18 +240,21 @@ static int proc_keys_show(struct seq_file *m, void *v) | |||
236 | sprintf(xbuf, "%luw", timo / (60*60*24*7)); | 240 | sprintf(xbuf, "%luw", timo / (60*60*24*7)); |
237 | } | 241 | } |
238 | 242 | ||
239 | #define showflag(KEY, LETTER, FLAG) \ | 243 | state = key_read_state(key); |
240 | (test_bit(FLAG, &(KEY)->flags) ? LETTER : '-') | ||
241 | 244 | ||
245 | #define showflag(FLAGS, LETTER, FLAG) \ | ||
246 | ((FLAGS & (1 << FLAG)) ? LETTER : '-') | ||
247 | |||
248 | flags = READ_ONCE(key->flags); | ||
242 | seq_printf(m, "%08x %c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ", | 249 | seq_printf(m, "%08x %c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ", |
243 | key->serial, | 250 | key->serial, |
244 | showflag(key, 'I', KEY_FLAG_INSTANTIATED), | 251 | state != KEY_IS_UNINSTANTIATED ? 'I' : '-', |
245 | showflag(key, 'R', KEY_FLAG_REVOKED), | 252 | showflag(flags, 'R', KEY_FLAG_REVOKED), |
246 | showflag(key, 'D', KEY_FLAG_DEAD), | 253 | showflag(flags, 'D', KEY_FLAG_DEAD), |
247 | showflag(key, 'Q', KEY_FLAG_IN_QUOTA), | 254 | showflag(flags, 'Q', KEY_FLAG_IN_QUOTA), |
248 | showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT), | 255 | showflag(flags, 'U', KEY_FLAG_USER_CONSTRUCT), |
249 | showflag(key, 'N', KEY_FLAG_NEGATIVE), | 256 | state < 0 ? 'N' : '-', |
250 | showflag(key, 'i', KEY_FLAG_INVALIDATED), | 257 | showflag(flags, 'i', KEY_FLAG_INVALIDATED), |
251 | refcount_read(&key->usage), | 258 | refcount_read(&key->usage), |
252 | xbuf, | 259 | xbuf, |
253 | key->perm, | 260 | key->perm, |
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 293d3598153b..740affd65ee9 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c | |||
@@ -730,7 +730,7 @@ try_again: | |||
730 | 730 | ||
731 | ret = -EIO; | 731 | ret = -EIO; |
732 | if (!(lflags & KEY_LOOKUP_PARTIAL) && | 732 | if (!(lflags & KEY_LOOKUP_PARTIAL) && |
733 | !test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) | 733 | key_read_state(key) == KEY_IS_UNINSTANTIATED) |
734 | goto invalid_key; | 734 | goto invalid_key; |
735 | 735 | ||
736 | /* check the permissions */ | 736 | /* check the permissions */ |
diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 63e63a42db3c..e8036cd0ad54 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c | |||
@@ -595,10 +595,9 @@ int wait_for_key_construction(struct key *key, bool intr) | |||
595 | intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); | 595 | intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); |
596 | if (ret) | 596 | if (ret) |
597 | return -ERESTARTSYS; | 597 | return -ERESTARTSYS; |
598 | if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) { | 598 | ret = key_read_state(key); |
599 | smp_rmb(); | 599 | if (ret < 0) |
600 | return key->reject_error; | 600 | return ret; |
601 | } | ||
602 | return key_validate(key); | 601 | return key_validate(key); |
603 | } | 602 | } |
604 | EXPORT_SYMBOL(wait_for_key_construction); | 603 | EXPORT_SYMBOL(wait_for_key_construction); |
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 6ebf1af8fce9..424e1d90412e 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c | |||
@@ -73,7 +73,7 @@ static void request_key_auth_describe(const struct key *key, | |||
73 | 73 | ||
74 | seq_puts(m, "key:"); | 74 | seq_puts(m, "key:"); |
75 | seq_puts(m, key->description); | 75 | seq_puts(m, key->description); |
76 | if (key_is_instantiated(key)) | 76 | if (key_is_positive(key)) |
77 | seq_printf(m, " pid:%d ci:%zu", rka->pid, rka->callout_len); | 77 | seq_printf(m, " pid:%d ci:%zu", rka->pid, rka->callout_len); |
78 | } | 78 | } |
79 | 79 | ||
diff --git a/security/keys/trusted.c b/security/keys/trusted.c index ddfaebf60fc8..bd85315cbfeb 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c | |||
@@ -1066,7 +1066,7 @@ static int trusted_update(struct key *key, struct key_preparsed_payload *prep) | |||
1066 | char *datablob; | 1066 | char *datablob; |
1067 | int ret = 0; | 1067 | int ret = 0; |
1068 | 1068 | ||
1069 | if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) | 1069 | if (key_is_negative(key)) |
1070 | return -ENOKEY; | 1070 | return -ENOKEY; |
1071 | p = key->payload.data[0]; | 1071 | p = key->payload.data[0]; |
1072 | if (!p->migratable) | 1072 | if (!p->migratable) |
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index 3d8c68eba516..9f558bedba23 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c | |||
@@ -114,7 +114,7 @@ int user_update(struct key *key, struct key_preparsed_payload *prep) | |||
114 | 114 | ||
115 | /* attach the new data, displacing the old */ | 115 | /* attach the new data, displacing the old */ |
116 | key->expiry = prep->expiry; | 116 | key->expiry = prep->expiry; |
117 | if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags)) | 117 | if (key_is_positive(key)) |
118 | zap = dereference_key_locked(key); | 118 | zap = dereference_key_locked(key); |
119 | rcu_assign_keypointer(key, prep->payload.data[0]); | 119 | rcu_assign_keypointer(key, prep->payload.data[0]); |
120 | prep->payload.data[0] = NULL; | 120 | prep->payload.data[0] = NULL; |
@@ -162,7 +162,7 @@ EXPORT_SYMBOL_GPL(user_destroy); | |||
162 | void user_describe(const struct key *key, struct seq_file *m) | 162 | void user_describe(const struct key *key, struct seq_file *m) |
163 | { | 163 | { |
164 | seq_puts(m, key->description); | 164 | seq_puts(m, key->description); |
165 | if (key_is_instantiated(key)) | 165 | if (key_is_positive(key)) |
166 | seq_printf(m, ": %u", key->datalen); | 166 | seq_printf(m, ": %u", key->datalen); |
167 | } | 167 | } |
168 | 168 | ||