]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blame - releases/2.6.32.17/cifs-fix-a-malicious-redirect-problem-in-the-dns-lookup-code.patch
4.9-stable patches
[thirdparty/kernel/stable-queue.git] / releases / 2.6.32.17 / cifs-fix-a-malicious-redirect-problem-in-the-dns-lookup-code.patch
CommitLineData
a0402406
GKH
1From 4c0c03ca54f72fdd5912516ad0a23ec5cf01bda7 Mon Sep 17 00:00:00 2001
2From: David Howells <dhowells@redhat.com>
3Date: Thu, 22 Jul 2010 12:53:18 +0100
4Subject: CIFS: Fix a malicious redirect problem in the DNS lookup code
5
6From: David Howells <dhowells@redhat.com>
7
8commit 4c0c03ca54f72fdd5912516ad0a23ec5cf01bda7 upstream.
9
10Fix the security problem in the CIFS filesystem DNS lookup code in which a
11malicious redirect could be installed by a random user by simply adding a
12result record into one of their keyrings with add_key() and then invoking a
13CIFS CFS lookup [CVE-2010-2524].
14
15This is done by creating an internal keyring specifically for the caching of
16DNS lookups. To enforce the use of this keyring, the module init routine
17creates a set of override credentials with the keyring installed as the thread
18keyring and instructs request_key() to only install lookup result keys in that
19keyring.
20
21The override is then applied around the call to request_key().
22
23This has some additional benefits when a kernel service uses this module to
24request a key:
25
26 (1) The result keys are owned by root, not the user that caused the lookup.
27
28 (2) The result keys don't pop up in the user's keyrings.
29
30 (3) The result keys don't come out of the quota of the user that caused the
31 lookup.
32
33The keyring can be viewed as root by doing cat /proc/keys:
34
352a0ca6c3 I----- 1 perm 1f030000 0 0 keyring .dns_resolver: 1/4
36
37It can then be listed with 'keyctl list' by root.
38
39 # keyctl list 0x2a0ca6c3
40 1 key in keyring:
41 726766307: --alswrv 0 0 dns_resolver: foo.bar.com
42
43Signed-off-by: David Howells <dhowells@redhat.com>
44Reviewed-and-Tested-by: Jeff Layton <jlayton@redhat.com>
45Acked-by: Steve French <smfrench@gmail.com>
46Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
47Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
48
49---
50 fs/cifs/cifsfs.c | 6 ++--
51 fs/cifs/dns_resolve.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++
52 fs/cifs/dns_resolve.h | 4 +-
53 3 files changed, 74 insertions(+), 5 deletions(-)
54
55--- a/fs/cifs/cifsfs.c
56+++ b/fs/cifs/cifsfs.c
57@@ -1033,7 +1033,7 @@ init_cifs(void)
58 goto out_unregister_filesystem;
59 #endif
60 #ifdef CONFIG_CIFS_DFS_UPCALL
61- rc = register_key_type(&key_type_dns_resolver);
62+ rc = cifs_init_dns_resolver();
63 if (rc)
64 goto out_unregister_key_type;
65 #endif
66@@ -1045,7 +1045,7 @@ init_cifs(void)
67
68 out_unregister_resolver_key:
69 #ifdef CONFIG_CIFS_DFS_UPCALL
70- unregister_key_type(&key_type_dns_resolver);
71+ cifs_exit_dns_resolver();
72 out_unregister_key_type:
73 #endif
74 #ifdef CONFIG_CIFS_UPCALL
75@@ -1071,7 +1071,7 @@ exit_cifs(void)
76 cifs_proc_clean();
77 #ifdef CONFIG_CIFS_DFS_UPCALL
78 cifs_dfs_release_automount_timer();
79- unregister_key_type(&key_type_dns_resolver);
80+ cifs_exit_dns_resolver();
81 #endif
82 #ifdef CONFIG_CIFS_UPCALL
83 unregister_key_type(&cifs_spnego_key_type);
84--- a/fs/cifs/dns_resolve.c
85+++ b/fs/cifs/dns_resolve.c
86@@ -23,12 +23,16 @@
87 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
88 */
89
90+#include <linux/keyctl.h>
91+#include <linux/key-type.h>
92 #include <keys/user-type.h>
93 #include "dns_resolve.h"
94 #include "cifsglob.h"
95 #include "cifsproto.h"
96 #include "cifs_debug.h"
97
98+static const struct cred *dns_resolver_cache;
99+
100 /* Checks if supplied name is IP address
101 * returns:
102 * 1 - name is IP
103@@ -93,6 +97,7 @@ struct key_type key_type_dns_resolver =
104 int
105 dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
106 {
107+ const struct cred *saved_cred;
108 int rc = -EAGAIN;
109 struct key *rkey = ERR_PTR(-EAGAIN);
110 char *name;
111@@ -132,8 +137,15 @@ dns_resolve_server_name_to_ip(const char
112 goto skip_upcall;
113 }
114
115+ saved_cred = override_creds(dns_resolver_cache);
116 rkey = request_key(&key_type_dns_resolver, name, "");
117+ revert_creds(saved_cred);
118 if (!IS_ERR(rkey)) {
119+ if (!(rkey->perm & KEY_USR_VIEW)) {
120+ down_read(&rkey->sem);
121+ rkey->perm |= KEY_USR_VIEW;
122+ up_read(&rkey->sem);
123+ }
124 len = rkey->type_data.x[0];
125 data = rkey->payload.data;
126 } else {
127@@ -164,4 +176,61 @@ out:
128 return rc;
129 }
130
131+int __init cifs_init_dns_resolver(void)
132+{
133+ struct cred *cred;
134+ struct key *keyring;
135+ int ret;
136+
137+ printk(KERN_NOTICE "Registering the %s key type\n",
138+ key_type_dns_resolver.name);
139+
140+ /* create an override credential set with a special thread keyring in
141+ * which DNS requests are cached
142+ *
143+ * this is used to prevent malicious redirections from being installed
144+ * with add_key().
145+ */
146+ cred = prepare_kernel_cred(NULL);
147+ if (!cred)
148+ return -ENOMEM;
149+
150+ keyring = key_alloc(&key_type_keyring, ".dns_resolver", 0, 0, cred,
151+ (KEY_POS_ALL & ~KEY_POS_SETATTR) |
152+ KEY_USR_VIEW | KEY_USR_READ,
153+ KEY_ALLOC_NOT_IN_QUOTA);
154+ if (IS_ERR(keyring)) {
155+ ret = PTR_ERR(keyring);
156+ goto failed_put_cred;
157+ }
158+
159+ ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL);
160+ if (ret < 0)
161+ goto failed_put_key;
162+
163+ ret = register_key_type(&key_type_dns_resolver);
164+ if (ret < 0)
165+ goto failed_put_key;
166+
167+ /* instruct request_key() to use this special keyring as a cache for
168+ * the results it looks up */
169+ cred->thread_keyring = keyring;
170+ cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
171+ dns_resolver_cache = cred;
172+ return 0;
173+
174+failed_put_key:
175+ key_put(keyring);
176+failed_put_cred:
177+ put_cred(cred);
178+ return ret;
179+}
180
181+void __exit cifs_exit_dns_resolver(void)
182+{
183+ key_revoke(dns_resolver_cache->thread_keyring);
184+ unregister_key_type(&key_type_dns_resolver);
185+ put_cred(dns_resolver_cache);
186+ printk(KERN_NOTICE "Unregistered %s key type\n",
187+ key_type_dns_resolver.name);
188+}
189--- a/fs/cifs/dns_resolve.h
190+++ b/fs/cifs/dns_resolve.h
191@@ -24,8 +24,8 @@
192 #define _DNS_RESOLVE_H
193
194 #ifdef __KERNEL__
195-#include <linux/key-type.h>
196-extern struct key_type key_type_dns_resolver;
197+extern int __init cifs_init_dns_resolver(void);
198+extern void __exit cifs_exit_dns_resolver(void);
199 extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr);
200 #endif /* KERNEL */
201