Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

David Howells and committed by
Linus Torvalds
4c0c03ca cd5b8f87

+74 -5
+3 -3
fs/cifs/cifsfs.c
··· 923 923 goto out_unregister_filesystem; 924 924 #endif 925 925 #ifdef CONFIG_CIFS_DFS_UPCALL 926 - rc = register_key_type(&key_type_dns_resolver); 926 + rc = cifs_init_dns_resolver(); 927 927 if (rc) 928 928 goto out_unregister_key_type; 929 929 #endif ··· 935 935 936 936 out_unregister_resolver_key: 937 937 #ifdef CONFIG_CIFS_DFS_UPCALL 938 - unregister_key_type(&key_type_dns_resolver); 938 + cifs_exit_dns_resolver(); 939 939 out_unregister_key_type: 940 940 #endif 941 941 #ifdef CONFIG_CIFS_UPCALL ··· 961 961 cifs_proc_clean(); 962 962 #ifdef CONFIG_CIFS_DFS_UPCALL 963 963 cifs_dfs_release_automount_timer(); 964 - unregister_key_type(&key_type_dns_resolver); 964 + cifs_exit_dns_resolver(); 965 965 #endif 966 966 #ifdef CONFIG_CIFS_UPCALL 967 967 unregister_key_type(&cifs_spnego_key_type);
+69
fs/cifs/dns_resolve.c
··· 24 24 */ 25 25 26 26 #include <linux/slab.h> 27 + #include <linux/keyctl.h> 28 + #include <linux/key-type.h> 27 29 #include <keys/user-type.h> 28 30 #include "dns_resolve.h" 29 31 #include "cifsglob.h" 30 32 #include "cifsproto.h" 31 33 #include "cifs_debug.h" 34 + 35 + static const struct cred *dns_resolver_cache; 32 36 33 37 /* Checks if supplied name is IP address 34 38 * returns: ··· 98 94 int 99 95 dns_resolve_server_name_to_ip(const char *unc, char **ip_addr) 100 96 { 97 + const struct cred *saved_cred; 101 98 int rc = -EAGAIN; 102 99 struct key *rkey = ERR_PTR(-EAGAIN); 103 100 char *name; ··· 138 133 goto skip_upcall; 139 134 } 140 135 136 + saved_cred = override_creds(dns_resolver_cache); 141 137 rkey = request_key(&key_type_dns_resolver, name, ""); 138 + revert_creds(saved_cred); 142 139 if (!IS_ERR(rkey)) { 140 + if (!(rkey->perm & KEY_USR_VIEW)) { 141 + down_read(&rkey->sem); 142 + rkey->perm |= KEY_USR_VIEW; 143 + up_read(&rkey->sem); 144 + } 143 145 len = rkey->type_data.x[0]; 144 146 data = rkey->payload.data; 145 147 } else { ··· 177 165 return rc; 178 166 } 179 167 168 + int __init cifs_init_dns_resolver(void) 169 + { 170 + struct cred *cred; 171 + struct key *keyring; 172 + int ret; 180 173 174 + printk(KERN_NOTICE "Registering the %s key type\n", 175 + key_type_dns_resolver.name); 176 + 177 + /* create an override credential set with a special thread keyring in 178 + * which DNS requests are cached 179 + * 180 + * this is used to prevent malicious redirections from being installed 181 + * with add_key(). 182 + */ 183 + cred = prepare_kernel_cred(NULL); 184 + if (!cred) 185 + return -ENOMEM; 186 + 187 + keyring = key_alloc(&key_type_keyring, ".dns_resolver", 0, 0, cred, 188 + (KEY_POS_ALL & ~KEY_POS_SETATTR) | 189 + KEY_USR_VIEW | KEY_USR_READ, 190 + KEY_ALLOC_NOT_IN_QUOTA); 191 + if (IS_ERR(keyring)) { 192 + ret = PTR_ERR(keyring); 193 + goto failed_put_cred; 194 + } 195 + 196 + ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL); 197 + if (ret < 0) 198 + goto failed_put_key; 199 + 200 + ret = register_key_type(&key_type_dns_resolver); 201 + if (ret < 0) 202 + goto failed_put_key; 203 + 204 + /* instruct request_key() to use this special keyring as a cache for 205 + * the results it looks up */ 206 + cred->thread_keyring = keyring; 207 + cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; 208 + dns_resolver_cache = cred; 209 + return 0; 210 + 211 + failed_put_key: 212 + key_put(keyring); 213 + failed_put_cred: 214 + put_cred(cred); 215 + return ret; 216 + } 217 + 218 + void __exit cifs_exit_dns_resolver(void) 219 + { 220 + key_revoke(dns_resolver_cache->thread_keyring); 221 + unregister_key_type(&key_type_dns_resolver); 222 + put_cred(dns_resolver_cache); 223 + printk(KERN_NOTICE "Unregistered %s key type\n", 224 + key_type_dns_resolver.name); 225 + }
+2 -2
fs/cifs/dns_resolve.h
··· 24 24 #define _DNS_RESOLVE_H 25 25 26 26 #ifdef __KERNEL__ 27 - #include <linux/key-type.h> 28 - extern struct key_type key_type_dns_resolver; 27 + extern int __init cifs_init_dns_resolver(void); 28 + extern void __exit cifs_exit_dns_resolver(void); 29 29 extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr); 30 30 #endif /* KERNEL */ 31 31