aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs/dns_resolve.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2010-07-22 07:53:18 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2010-07-22 12:42:40 -0400
commit4c0c03ca54f72fdd5912516ad0a23ec5cf01bda7 (patch)
treea05cfa65c298edb1f034923ff6eb76839b5bc0dd /fs/cifs/dns_resolve.c
parentcd5b8f8755a89a57fc8c408d284b8b613f090345 (diff)
CIFS: Fix a malicious redirect problem in the DNS lookup code
Fix the security problem in the CIFS filesystem DNS lookup code in which a malicious redirect could be installed by a random user by simply adding a result record into one of their keyrings with add_key() and then invoking a CIFS CFS lookup [CVE-2010-2524]. This is done by creating an internal keyring specifically for the caching of DNS lookups. To enforce the use of this keyring, the module init routine creates a set of override credentials with the keyring installed as the thread keyring and instructs request_key() to only install lookup result keys in that keyring. The override is then applied around the call to request_key(). This has some additional benefits when a kernel service uses this module to request a key: (1) The result keys are owned by root, not the user that caused the lookup. (2) The result keys don't pop up in the user's keyrings. (3) The result keys don't come out of the quota of the user that caused the lookup. The keyring can be viewed as root by doing cat /proc/keys: 2a0ca6c3 I----- 1 perm 1f030000 0 0 keyring .dns_resolver: 1/4 It can then be listed with 'keyctl list' by root. # keyctl list 0x2a0ca6c3 1 key in keyring: 726766307: --alswrv 0 0 dns_resolver: foo.bar.com Signed-off-by: David Howells <dhowells@redhat.com> Reviewed-and-Tested-by: Jeff Layton <jlayton@redhat.com> Acked-by: Steve French <smfrench@gmail.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/cifs/dns_resolve.c')
-rw-r--r--fs/cifs/dns_resolve.c69
1 files changed, 69 insertions, 0 deletions
diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c
index 4db2c5e7283f..49315cbf742d 100644
--- a/fs/cifs/dns_resolve.c
+++ b/fs/cifs/dns_resolve.c
@@ -24,12 +24,16 @@
24 */ 24 */
25 25
26#include <linux/slab.h> 26#include <linux/slab.h>
27#include <linux/keyctl.h>
28#include <linux/key-type.h>
27#include <keys/user-type.h> 29#include <keys/user-type.h>
28#include "dns_resolve.h" 30#include "dns_resolve.h"
29#include "cifsglob.h" 31#include "cifsglob.h"
30#include "cifsproto.h" 32#include "cifsproto.h"
31#include "cifs_debug.h" 33#include "cifs_debug.h"
32 34
35static const struct cred *dns_resolver_cache;
36
33/* Checks if supplied name is IP address 37/* Checks if supplied name is IP address
34 * returns: 38 * returns:
35 * 1 - name is IP 39 * 1 - name is IP
@@ -94,6 +98,7 @@ struct key_type key_type_dns_resolver = {
94int 98int
95dns_resolve_server_name_to_ip(const char *unc, char **ip_addr) 99dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
96{ 100{
101 const struct cred *saved_cred;
97 int rc = -EAGAIN; 102 int rc = -EAGAIN;
98 struct key *rkey = ERR_PTR(-EAGAIN); 103 struct key *rkey = ERR_PTR(-EAGAIN);
99 char *name; 104 char *name;
@@ -133,8 +138,15 @@ dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
133 goto skip_upcall; 138 goto skip_upcall;
134 } 139 }
135 140
141 saved_cred = override_creds(dns_resolver_cache);
136 rkey = request_key(&key_type_dns_resolver, name, ""); 142 rkey = request_key(&key_type_dns_resolver, name, "");
143 revert_creds(saved_cred);
137 if (!IS_ERR(rkey)) { 144 if (!IS_ERR(rkey)) {
145 if (!(rkey->perm & KEY_USR_VIEW)) {
146 down_read(&rkey->sem);
147 rkey->perm |= KEY_USR_VIEW;
148 up_read(&rkey->sem);
149 }
138 len = rkey->type_data.x[0]; 150 len = rkey->type_data.x[0];
139 data = rkey->payload.data; 151 data = rkey->payload.data;
140 } else { 152 } else {
@@ -165,4 +177,61 @@ out:
165 return rc; 177 return rc;
166} 178}
167 179
180int __init cifs_init_dns_resolver(void)
181{
182 struct cred *cred;
183 struct key *keyring;
184 int ret;
185
186 printk(KERN_NOTICE "Registering the %s key type\n",
187 key_type_dns_resolver.name);
188
189 /* create an override credential set with a special thread keyring in
190 * which DNS requests are cached
191 *
192 * this is used to prevent malicious redirections from being installed
193 * with add_key().
194 */
195 cred = prepare_kernel_cred(NULL);
196 if (!cred)
197 return -ENOMEM;
198
199 keyring = key_alloc(&key_type_keyring, ".dns_resolver", 0, 0, cred,
200 (KEY_POS_ALL & ~KEY_POS_SETATTR) |
201 KEY_USR_VIEW | KEY_USR_READ,
202 KEY_ALLOC_NOT_IN_QUOTA);
203 if (IS_ERR(keyring)) {
204 ret = PTR_ERR(keyring);
205 goto failed_put_cred;
206 }
207
208 ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL);
209 if (ret < 0)
210 goto failed_put_key;
211
212 ret = register_key_type(&key_type_dns_resolver);
213 if (ret < 0)
214 goto failed_put_key;
215
216 /* instruct request_key() to use this special keyring as a cache for
217 * the results it looks up */
218 cred->thread_keyring = keyring;
219 cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
220 dns_resolver_cache = cred;
221 return 0;
222
223failed_put_key:
224 key_put(keyring);
225failed_put_cred:
226 put_cred(cred);
227 return ret;
228}
168 229
230void __exit cifs_exit_dns_resolver(void)
231{
232 key_revoke(dns_resolver_cache->thread_keyring);
233 unregister_key_type(&key_type_dns_resolver);
234 put_cred(dns_resolver_cache);
235 printk(KERN_NOTICE "Unregistered %s key type\n",
236 key_type_dns_resolver.name);
237}