goto out_unregister_filesystem;
 #endif
 #ifdef CONFIG_CIFS_DFS_UPCALL
-       rc = register_key_type(&key_type_dns_resolver);
+       rc = cifs_init_dns_resolver();
        if (rc)
                goto out_unregister_key_type;
 #endif
 
  out_unregister_resolver_key:
 #ifdef CONFIG_CIFS_DFS_UPCALL
-       unregister_key_type(&key_type_dns_resolver);
+       cifs_exit_dns_resolver();
  out_unregister_key_type:
 #endif
 #ifdef CONFIG_CIFS_UPCALL
        cifs_proc_clean();
 #ifdef CONFIG_CIFS_DFS_UPCALL
        cifs_dfs_release_automount_timer();
-       unregister_key_type(&key_type_dns_resolver);
+       cifs_exit_dns_resolver();
 #endif
 #ifdef CONFIG_CIFS_UPCALL
        unregister_key_type(&cifs_spnego_key_type);
 
  */
 
 #include <linux/slab.h>
+#include <linux/keyctl.h>
+#include <linux/key-type.h>
 #include <keys/user-type.h>
 #include "dns_resolve.h"
 #include "cifsglob.h"
 #include "cifsproto.h"
 #include "cifs_debug.h"
 
+static const struct cred *dns_resolver_cache;
+
 /* Checks if supplied name is IP address
  * returns:
  *             1 - name is IP
 int
 dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
 {
+       const struct cred *saved_cred;
        int rc = -EAGAIN;
        struct key *rkey = ERR_PTR(-EAGAIN);
        char *name;
                goto skip_upcall;
        }
 
+       saved_cred = override_creds(dns_resolver_cache);
        rkey = request_key(&key_type_dns_resolver, name, "");
+       revert_creds(saved_cred);
        if (!IS_ERR(rkey)) {
+               if (!(rkey->perm & KEY_USR_VIEW)) {
+                       down_read(&rkey->sem);
+                       rkey->perm |= KEY_USR_VIEW;
+                       up_read(&rkey->sem);
+               }
                len = rkey->type_data.x[0];
                data = rkey->payload.data;
        } else {
        return rc;
 }
 
+int __init cifs_init_dns_resolver(void)
+{
+       struct cred *cred;
+       struct key *keyring;
+       int ret;
+
+       printk(KERN_NOTICE "Registering the %s key type\n",
+              key_type_dns_resolver.name);
+
+       /* create an override credential set with a special thread keyring in
+        * which DNS requests are cached
+        *
+        * this is used to prevent malicious redirections from being installed
+        * with add_key().
+        */
+       cred = prepare_kernel_cred(NULL);
+       if (!cred)
+               return -ENOMEM;
+
+       keyring = key_alloc(&key_type_keyring, ".dns_resolver", 0, 0, cred,
+                           (KEY_POS_ALL & ~KEY_POS_SETATTR) |
+                           KEY_USR_VIEW | KEY_USR_READ,
+                           KEY_ALLOC_NOT_IN_QUOTA);
+       if (IS_ERR(keyring)) {
+               ret = PTR_ERR(keyring);
+               goto failed_put_cred;
+       }
+
+       ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL);
+       if (ret < 0)
+               goto failed_put_key;
+
+       ret = register_key_type(&key_type_dns_resolver);
+       if (ret < 0)
+               goto failed_put_key;
+
+       /* instruct request_key() to use this special keyring as a cache for
+        * the results it looks up */
+       cred->thread_keyring = keyring;
+       cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
+       dns_resolver_cache = cred;
+       return 0;
+
+failed_put_key:
+       key_put(keyring);
+failed_put_cred:
+       put_cred(cred);
+       return ret;
+}
 
+void __exit cifs_exit_dns_resolver(void)
+{
+       key_revoke(dns_resolver_cache->thread_keyring);
+       unregister_key_type(&key_type_dns_resolver);
+       put_cred(dns_resolver_cache);
+       printk(KERN_NOTICE "Unregistered %s key type\n",
+              key_type_dns_resolver.name);
+}