Fix CVE-2024-36031 for openEuler-22.03-LTS-SP1
David Howells (2): keys, dns: Allow key types (eg. DNS) to be reclaimed immediately on expiry keys, dns: Fix size check of V1 server-list header
Edward Adam Davis (1): keys, dns: Fix missing size check of V1 server-list header
Silvio Gissi (1): keys: Fix overwrite of key expiration on instantiation
include/linux/key-type.h | 1 + net/dns_resolver/dns_key.c | 25 ++++++++++++++++--------- security/keys/gc.c | 31 +++++++++++++++++++++---------- security/keys/internal.h | 11 ++++++++++- security/keys/key.c | 16 ++++++---------- security/keys/proc.c | 2 +- 6 files changed, 55 insertions(+), 31 deletions(-)
From: David Howells dhowells@redhat.com
stable inclusion from stable-v5.10.206 commit 97be1e865e70e5a0ad0a5b5f5dca5031ca0b53ac category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I9TM8D CVE: CVE-2024-36031
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=...
--------------------------------
[ Upstream commit 39299bdd2546688d92ed9db4948f6219ca1b9542 ]
If a key has an expiration time, then when that time passes, the key is left around for a certain amount of time before being collected (5 mins by default) so that EKEYEXPIRED can be returned instead of ENOKEY. This is a problem for DNS keys because we want to redo the DNS lookup immediately at that point.
Fix this by allowing key types to be marked such that keys of that type don't have this extra period, but are reclaimed as soon as they expire and turn this on for dns_resolver-type keys. To make this easier to handle, key->expiry is changed to be permanent if TIME64_MAX rather than 0.
Furthermore, give such new-style negative DNS results a 1s default expiry if no other expiry time is set rather than allowing it to stick around indefinitely. This shouldn't be zero as ls will follow a failing stat call immediately with a second with AT_SYMLINK_NOFOLLOW added.
Fixes: 1a4240f4764a ("DNS: Separate out CIFS DNS Resolver code") Signed-off-by: David Howells dhowells@redhat.com Tested-by: Markus Suvanto markus.suvanto@gmail.com cc: Wang Lei wang840925@gmail.com cc: Jeff Layton jlayton@redhat.com cc: Steve French smfrench@gmail.com cc: Marc Dionne marc.dionne@auristor.com cc: Jarkko Sakkinen jarkko@kernel.org cc: "David S. Miller" davem@davemloft.net cc: Eric Dumazet edumazet@google.com cc: Jakub Kicinski kuba@kernel.org cc: Paolo Abeni pabeni@redhat.com cc: linux-afs@lists.infradead.org cc: linux-cifs@vger.kernel.org cc: linux-nfs@vger.kernel.org cc: ceph-devel@vger.kernel.org cc: keyrings@vger.kernel.org cc: netdev@vger.kernel.org Signed-off-by: Sasha Levin sashal@kernel.org Signed-off-by: Yi Yang yiyang13@huawei.com --- include/linux/key-type.h | 1 + net/dns_resolver/dns_key.c | 10 +++++++++- security/keys/gc.c | 31 +++++++++++++++++++++---------- security/keys/internal.h | 11 ++++++++++- security/keys/key.c | 15 +++++---------- security/keys/proc.c | 2 +- 6 files changed, 47 insertions(+), 23 deletions(-)
diff --git a/include/linux/key-type.h b/include/linux/key-type.h index 9a595cc831ea..4421a8ecd799 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -74,6 +74,7 @@ struct key_type {
unsigned int flags; #define KEY_TYPE_NET_DOMAIN 0x00000001 /* Keys of this type have a net namespace domain */ +#define KEY_TYPE_INSTANT_REAP 0x00000002 /* Keys of this type don't have a delay after expiring */
/* vet a description */ int (*vet_description)(const char *description); diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c index 3aced951d5ab..03f8f33dc134 100644 --- a/net/dns_resolver/dns_key.c +++ b/net/dns_resolver/dns_key.c @@ -91,6 +91,7 @@ const struct cred *dns_resolver_cache; static int dns_resolver_preparse(struct key_preparsed_payload *prep) { + const struct dns_server_list_v1_header *v1; const struct dns_payload_header *bin; struct user_key_payload *upayload; unsigned long derrno; @@ -122,6 +123,13 @@ dns_resolver_preparse(struct key_preparsed_payload *prep) return -EINVAL; }
+ v1 = (const struct dns_server_list_v1_header *)bin; + if ((v1->status != DNS_LOOKUP_GOOD && + v1->status != DNS_LOOKUP_GOOD_WITH_BAD)) { + if (prep->expiry == TIME64_MAX) + prep->expiry = ktime_get_real_seconds() + 1; + } + result_len = datalen; goto store_result; } @@ -314,7 +322,7 @@ static long dns_resolver_read(const struct key *key,
struct key_type key_type_dns_resolver = { .name = "dns_resolver", - .flags = KEY_TYPE_NET_DOMAIN, + .flags = KEY_TYPE_NET_DOMAIN | KEY_TYPE_INSTANT_REAP, .preparse = dns_resolver_preparse, .free_preparse = dns_resolver_free_preparse, .instantiate = generic_key_instantiate, diff --git a/security/keys/gc.c b/security/keys/gc.c index 3c90807476eb..eaddaceda14e 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -66,6 +66,19 @@ void key_schedule_gc(time64_t gc_at) } }
+/* + * Set the expiration time on a key. + */ +void key_set_expiry(struct key *key, time64_t expiry) +{ + key->expiry = expiry; + if (expiry != TIME64_MAX) { + if (!(key->type->flags & KEY_TYPE_INSTANT_REAP)) + expiry += key_gc_delay; + key_schedule_gc(expiry); + } +} + /* * Schedule a dead links collection run. */ @@ -176,7 +189,6 @@ static void key_garbage_collector(struct work_struct *work) static u8 gc_state; /* Internal persistent state */ #define KEY_GC_REAP_AGAIN 0x01 /* - Need another cycle */ #define KEY_GC_REAPING_LINKS 0x02 /* - We need to reap links */ -#define KEY_GC_SET_TIMER 0x04 /* - We need to restart the timer */ #define KEY_GC_REAPING_DEAD_1 0x10 /* - We need to mark dead keys */ #define KEY_GC_REAPING_DEAD_2 0x20 /* - We need to reap dead key links */ #define KEY_GC_REAPING_DEAD_3 0x40 /* - We need to reap dead keys */ @@ -184,21 +196,17 @@ static void key_garbage_collector(struct work_struct *work)
struct rb_node *cursor; struct key *key; - time64_t new_timer, limit; + time64_t new_timer, limit, expiry;
kenter("[%lx,%x]", key_gc_flags, gc_state);
limit = ktime_get_real_seconds(); - if (limit > key_gc_delay) - limit -= key_gc_delay; - else - limit = key_gc_delay;
/* Work out what we're going to be doing in this pass */ gc_state &= KEY_GC_REAPING_DEAD_1 | KEY_GC_REAPING_DEAD_2; gc_state <<= 1; if (test_and_clear_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags)) - gc_state |= KEY_GC_REAPING_LINKS | KEY_GC_SET_TIMER; + gc_state |= KEY_GC_REAPING_LINKS;
if (test_and_clear_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags)) gc_state |= KEY_GC_REAPING_DEAD_1; @@ -233,8 +241,11 @@ static void key_garbage_collector(struct work_struct *work) } }
- if (gc_state & KEY_GC_SET_TIMER) { - if (key->expiry > limit && key->expiry < new_timer) { + expiry = key->expiry; + if (expiry != TIME64_MAX) { + if (!(key->type->flags & KEY_TYPE_INSTANT_REAP)) + expiry += key_gc_delay; + if (expiry > limit && expiry < new_timer) { kdebug("will expire %x in %lld", key_serial(key), key->expiry - limit); new_timer = key->expiry; @@ -276,7 +287,7 @@ static void key_garbage_collector(struct work_struct *work) */ kdebug("pass complete");
- if (gc_state & KEY_GC_SET_TIMER && new_timer != (time64_t)TIME64_MAX) { + if (new_timer != TIME64_MAX) { new_timer += key_gc_delay; key_schedule_gc(new_timer); } diff --git a/security/keys/internal.h b/security/keys/internal.h index 9b9cf3b6fcbb..bede6c71ffd9 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -176,6 +176,7 @@ extern unsigned key_gc_delay; extern void keyring_gc(struct key *keyring, time64_t limit); extern void keyring_restriction_gc(struct key *keyring, struct key_type *dead_type); +void key_set_expiry(struct key *key, time64_t expiry); extern void key_schedule_gc(time64_t gc_at); extern void key_schedule_gc_links(void); extern void key_gc_keytype(struct key_type *ktype); @@ -224,10 +225,18 @@ extern struct key *key_get_instantiation_authkey(key_serial_t target_id); */ static inline bool key_is_dead(const struct key *key, time64_t limit) { + time64_t expiry = key->expiry; + + if (expiry != TIME64_MAX) { + if (!(key->type->flags & KEY_TYPE_INSTANT_REAP)) + expiry += key_gc_delay; + if (expiry <= limit) + return true; + } + return key->flags & ((1 << KEY_FLAG_DEAD) | (1 << KEY_FLAG_INVALIDATED)) || - (key->expiry > 0 && key->expiry <= limit) || key->domain_tag->removed; }
diff --git a/security/keys/key.c b/security/keys/key.c index 151ff39b6803..67ad0826e385 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -294,6 +294,7 @@ struct key *key_alloc(struct key_type *type, const char *desc, key->uid = uid; key->gid = gid; key->perm = perm; + key->expiry = TIME64_MAX; key->restrict_link = restrict_link; key->last_used_at = ktime_get_real_seconds();
@@ -463,10 +464,7 @@ static int __key_instantiate_and_link(struct key *key, if (authkey) key_invalidate(authkey);
- if (prep->expiry != TIME64_MAX) { - key->expiry = prep->expiry; - key_schedule_gc(prep->expiry + key_gc_delay); - } + key_set_expiry(key, prep->expiry); } }
@@ -605,8 +603,7 @@ int key_reject_and_link(struct key *key, atomic_inc(&key->user->nikeys); mark_key_instantiated(key, -error); notify_key(key, NOTIFY_KEY_INSTANTIATED, -error); - key->expiry = ktime_get_real_seconds() + timeout; - key_schedule_gc(key->expiry + key_gc_delay); + key_set_expiry(key, ktime_get_real_seconds() + timeout);
if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags)) awaken = 1; @@ -721,16 +718,14 @@ struct key_type *key_type_lookup(const char *type)
void key_set_timeout(struct key *key, unsigned timeout) { - time64_t expiry = 0; + time64_t expiry = TIME64_MAX;
/* make the changes with the locks held to prevent races */ down_write(&key->sem);
if (timeout > 0) expiry = ktime_get_real_seconds() + timeout; - - key->expiry = expiry; - key_schedule_gc(key->expiry + key_gc_delay); + key_set_expiry(key, expiry);
up_write(&key->sem); } diff --git a/security/keys/proc.c b/security/keys/proc.c index d0cde6685627..4f4e2c1824f1 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -198,7 +198,7 @@ static int proc_keys_show(struct seq_file *m, void *v)
/* come up with a suitable timeout value */ expiry = READ_ONCE(key->expiry); - if (expiry == 0) { + if (expiry == TIME64_MAX) { memcpy(xbuf, "perm", 5); } else if (now >= expiry) { memcpy(xbuf, "expd", 5);
From: Edward Adam Davis eadavis@qq.com
stable inclusion from stable-v5.10.208 commit ef1d1d7c10489d91f6ce5dc45e37823e0f599085 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I9TM8D CVE: CVE-2024-36031
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=...
--------------------------------
commit 1997b3cb4217b09e49659b634c94da47f0340409 upstream.
The dns_resolver_preparse() function has a check on the size of the payload for the basic header of the binary-style payload, but is missing a check for the size of the V1 server-list payload header after determining that's what we've been given.
Fix this by getting rid of the the pointer to the basic header and just assuming that we have a V1 server-list payload and moving the V1 server list pointer inside the if-statement. Dealing with other types and versions can be left for when such have been defined.
This can be tested by doing the following with KASAN enabled:
echo -n -e '\x0\x0\x1\x2' | keyctl padd dns_resolver foo @p
and produces an oops like the following:
BUG: KASAN: slab-out-of-bounds in dns_resolver_preparse+0xc9f/0xd60 net/dns_resolver/dns_key.c:127 Read of size 1 at addr ffff888028894084 by task syz-executor265/5069 ... Call Trace: dns_resolver_preparse+0xc9f/0xd60 net/dns_resolver/dns_key.c:127 __key_create_or_update+0x453/0xdf0 security/keys/key.c:842 key_create_or_update+0x42/0x50 security/keys/key.c:1007 __do_sys_add_key+0x29c/0x450 security/keys/keyctl.c:134 do_syscall_x64 arch/x86/entry/common.c:52 [inline] do_syscall_64+0x40/0x110 arch/x86/entry/common.c:83 entry_SYSCALL_64_after_hwframe+0x62/0x6a
This patch was originally by Edward Adam Davis, but was modified by Linus.
Fixes: b946001d3bb1 ("keys, dns: Allow key types (eg. DNS) to be reclaimed immediately on expiry") Reported-and-tested-by: syzbot+94bbb75204a05da3d89f@syzkaller.appspotmail.com Link: https://lore.kernel.org/r/0000000000009b39bc060c73e209@google.com/ Suggested-by: Linus Torvalds torvalds@linux-foundation.org Signed-off-by: Edward Adam Davis eadavis@qq.com Signed-off-by: David Howells dhowells@redhat.com Tested-by: David Howells dhowells@redhat.com Cc: Edward Adam Davis eadavis@qq.com Cc: Jarkko Sakkinen jarkko@kernel.org Cc: Jeffrey E Altman jaltman@auristor.com Cc: Wang Lei wang840925@gmail.com Cc: Jeff Layton jlayton@redhat.com Cc: Steve French sfrench@us.ibm.com Cc: Marc Dionne marc.dionne@auristor.com Cc: "David S. Miller" davem@davemloft.net Cc: Eric Dumazet edumazet@google.com Cc: Jakub Kicinski kuba@kernel.org Cc: Paolo Abeni pabeni@redhat.com Reviewed-by: Simon Horman horms@kernel.org Signed-off-by: Linus Torvalds torvalds@linux-foundation.org Cc: Jeffrey E Altman jaltman@auristor.com Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Yi Yang yiyang13@huawei.com --- net/dns_resolver/dns_key.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c index 03f8f33dc134..8324e9f97066 100644 --- a/net/dns_resolver/dns_key.c +++ b/net/dns_resolver/dns_key.c @@ -91,8 +91,6 @@ const struct cred *dns_resolver_cache; static int dns_resolver_preparse(struct key_preparsed_payload *prep) { - const struct dns_server_list_v1_header *v1; - const struct dns_payload_header *bin; struct user_key_payload *upayload; unsigned long derrno; int ret; @@ -103,27 +101,28 @@ dns_resolver_preparse(struct key_preparsed_payload *prep) return -EINVAL;
if (data[0] == 0) { + const struct dns_server_list_v1_header *v1; + /* It may be a server list. */ - if (datalen <= sizeof(*bin)) + if (datalen <= sizeof(*v1)) return -EINVAL;
- bin = (const struct dns_payload_header *)data; - kenter("[%u,%u],%u", bin->content, bin->version, datalen); - if (bin->content != DNS_PAYLOAD_IS_SERVER_LIST) { + v1 = (const struct dns_server_list_v1_header *)data; + kenter("[%u,%u],%u", v1->hdr.content, v1->hdr.version, datalen); + if (v1->hdr.content != DNS_PAYLOAD_IS_SERVER_LIST) { pr_warn_ratelimited( "dns_resolver: Unsupported content type (%u)\n", - bin->content); + v1->hdr.content); return -EINVAL; }
- if (bin->version != 1) { + if (v1->hdr.version != 1) { pr_warn_ratelimited( "dns_resolver: Unsupported server list version (%u)\n", - bin->version); + v1->hdr.version); return -EINVAL; }
- v1 = (const struct dns_server_list_v1_header *)bin; if ((v1->status != DNS_LOOKUP_GOOD && v1->status != DNS_LOOKUP_GOOD_WITH_BAD)) { if (prep->expiry == TIME64_MAX)
From: David Howells dhowells@redhat.com
stable inclusion from stable-v5.10.209 commit 4533e7623e6d206a6d8e67c9f8d242dd022accbb category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I9TM8D CVE: CVE-2024-36031
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=...
--------------------------------
commit acc657692aed438e9931438f8c923b2b107aebf9 upstream.
Fix the size check added to dns_resolver_preparse() for the V1 server-list header so that it doesn't give EINVAL if the size supplied is the same as the size of the header struct (which should be valid).
This can be tested with:
echo -n -e '\0\0\01\xff\0\0' | keyctl padd dns_resolver desc @p
which will give "add_key: Invalid argument" without this fix.
Fixes: 1997b3cb4217 ("keys, dns: Fix missing size check of V1 server-list header") Reported-by: Pengfei Xu pengfei.xu@intel.com Link: https://lore.kernel.org/r/ZZ4fyY4r3rqgZL+4@xpf.sh.intel.com/ Signed-off-by: David Howells dhowells@redhat.com Signed-off-by: Linus Torvalds torvalds@linux-foundation.org Cc: Petr Vorel pvorel@suse.cz Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Yi Yang yiyang13@huawei.com --- net/dns_resolver/dns_key.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c index 8324e9f97066..26a9d8434c23 100644 --- a/net/dns_resolver/dns_key.c +++ b/net/dns_resolver/dns_key.c @@ -104,7 +104,7 @@ dns_resolver_preparse(struct key_preparsed_payload *prep) const struct dns_server_list_v1_header *v1;
/* It may be a server list. */ - if (datalen <= sizeof(*v1)) + if (datalen < sizeof(*v1)) return -EINVAL;
v1 = (const struct dns_server_list_v1_header *)data;
From: Silvio Gissi sifonsec@amazon.com
stable inclusion from stable-v5.10.217 commit ad2011ea787928b2accb5134f1e423b11fe80a8a category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I9TM8D CVE: CVE-2024-36031
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=...
--------------------------------
commit 9da27fb65a14c18efd4473e2e82b76b53ba60252 upstream.
The expiry time of a key is unconditionally overwritten during instantiation, defaulting to turn it permanent. This causes a problem for DNS resolution as the expiration set by user-space is overwritten to TIME64_MAX, disabling further DNS updates. Fix this by restoring the condition that key_set_expiry is only called when the pre-parser sets a specific expiry.
Fixes: 39299bdd2546 ("keys, dns: Allow key types (eg. DNS) to be reclaimed immediately on expiry") Signed-off-by: Silvio Gissi sifonsec@amazon.com cc: David Howells dhowells@redhat.com cc: Hazem Mohamed Abuelfotoh abuehaze@amazon.com cc: linux-afs@lists.infradead.org cc: linux-cifs@vger.kernel.org cc: keyrings@vger.kernel.org cc: netdev@vger.kernel.org cc: stable@vger.kernel.org Reviewed-by: Jarkko Sakkinen jarkko@kernel.org Signed-off-by: Jarkko Sakkinen jarkko@kernel.org Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Yi Yang yiyang13@huawei.com --- security/keys/key.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/security/keys/key.c b/security/keys/key.c index 67ad0826e385..e5111ce17e25 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -464,7 +464,8 @@ static int __key_instantiate_and_link(struct key *key, if (authkey) key_invalidate(authkey);
- key_set_expiry(key, prep->expiry); + if (prep->expiry != TIME64_MAX) + key_set_expiry(key, prep->expiry); } }
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://gitee.com/openeuler/kernel/pulls/10917 邮件列表地址:https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/4...
FeedBack: The patch(es) which you have sent to kernel@openeuler.org mailing list has been converted to a pull request successfully! Pull request link: https://gitee.com/openeuler/kernel/pulls/10917 Mailing list address: https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/4...