]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
cifs: Create dedicated keyring for spnego operations
authorSachin Prabhu <sprabhu@redhat.com>
Tue, 17 May 2016 23:20:13 +0000 (18:20 -0500)
committerBen Hutchings <ben@decadent.org.uk>
Mon, 22 Aug 2016 21:38:03 +0000 (22:38 +0100)
commit b74cb9a80268be5c80cf4c87c74debf0ff2129ac upstream.

The session key is the default keyring set for request_key operations.
This session key is revoked when the user owning the session logs out.
Any long running daemon processes started by this session ends up with
revoked session keyring which prevents these processes from using the
request_key mechanism from obtaining the krb5 keys.

The problem has been reported by a large number of autofs users. The
problem is also seen with multiuser mounts where the share may be used
by processes run by a user who has since logged out. A reproducer using
automount is available on the Red Hat bz.

The patch creates a new keyring which is used to cache cifs spnego
upcalls.

Red Hat bz: 1267754

Signed-off-by: Sachin Prabhu <sprabhu@redhat.com>
Reported-by: Scott Mayhew <smayhew@redhat.com>
Reviewed-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Signed-off-by: Steve French <smfrench@gmail.com>
[bwh: Backported to 3.16: keyring_alloc() doesn't take a restrict_link param]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
fs/cifs/cifs_spnego.c
fs/cifs/cifsfs.c
fs/cifs/cifsproto.h

index a3e932547617ea016ebb59ff2c9aa738d157153b..2f5a7e0103d0941e9fd128618f57e9f0805260fd 100644 (file)
 #include <linux/string.h>
 #include <keys/user-type.h>
 #include <linux/key-type.h>
+#include <linux/keyctl.h>
 #include <linux/inet.h>
 #include "cifsglob.h"
 #include "cifs_spnego.h"
 #include "cifs_debug.h"
+#include "cifsproto.h"
+static const struct cred *spnego_cred;
 
 /* create a new cifs key */
 static int
@@ -103,6 +106,7 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo)
        size_t desc_len;
        struct key *spnego_key;
        const char *hostname = server->hostname;
+       const struct cred *saved_cred;
 
        /* length of fields (with semicolons): ver=0xyz ip4=ipaddress
           host=hostname sec=mechanism uid=0xFF user=username */
@@ -164,7 +168,9 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo)
        sprintf(dp, ";pid=0x%x", current->pid);
 
        cifs_dbg(FYI, "key description = %s\n", description);
+       saved_cred = override_creds(spnego_cred);
        spnego_key = request_key(&cifs_spnego_key_type, description, "");
+       revert_creds(saved_cred);
 
 #ifdef CONFIG_CIFS_DEBUG2
        if (cifsFYI && !IS_ERR(spnego_key)) {
@@ -178,3 +184,64 @@ out:
        kfree(description);
        return spnego_key;
 }
+
+int
+init_cifs_spnego(void)
+{
+       struct cred *cred;
+       struct key *keyring;
+       int ret;
+
+       cifs_dbg(FYI, "Registering the %s key type\n",
+                cifs_spnego_key_type.name);
+
+       /*
+        * Create an override credential set with special thread keyring for
+        * spnego upcalls.
+        */
+
+       cred = prepare_kernel_cred(NULL);
+       if (!cred)
+               return -ENOMEM;
+
+       keyring = keyring_alloc(".cifs_spnego",
+                               GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
+                               (KEY_POS_ALL & ~KEY_POS_SETATTR) |
+                               KEY_USR_VIEW | KEY_USR_READ,
+                               KEY_ALLOC_NOT_IN_QUOTA, NULL);
+       if (IS_ERR(keyring)) {
+               ret = PTR_ERR(keyring);
+               goto failed_put_cred;
+       }
+
+       ret = register_key_type(&cifs_spnego_key_type);
+       if (ret < 0)
+               goto failed_put_key;
+
+       /*
+        * instruct request_key() to use this special keyring as a cache for
+        * the results it looks up
+        */
+       set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags);
+       cred->thread_keyring = keyring;
+       cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
+       spnego_cred = cred;
+
+       cifs_dbg(FYI, "cifs spnego keyring: %d\n", key_serial(keyring));
+       return 0;
+
+failed_put_key:
+       key_put(keyring);
+failed_put_cred:
+       put_cred(cred);
+       return ret;
+}
+
+void
+exit_cifs_spnego(void)
+{
+       key_revoke(spnego_cred->thread_keyring);
+       unregister_key_type(&cifs_spnego_key_type);
+       put_cred(spnego_cred);
+       cifs_dbg(FYI, "Unregistered %s key type\n", cifs_spnego_key_type.name);
+}
index d678cda1cd817a3144ad0278454be63812fd5c7f..445e1b01191aa1cccefbc91d44a1a56369f2c87a 100644 (file)
@@ -1228,7 +1228,7 @@ init_cifs(void)
                goto out_destroy_mids;
 
 #ifdef CONFIG_CIFS_UPCALL
-       rc = register_key_type(&cifs_spnego_key_type);
+       rc = init_cifs_spnego();
        if (rc)
                goto out_destroy_request_bufs;
 #endif /* CONFIG_CIFS_UPCALL */
@@ -1251,7 +1251,7 @@ out_init_cifs_idmap:
 out_register_key_type:
 #endif
 #ifdef CONFIG_CIFS_UPCALL
-       unregister_key_type(&cifs_spnego_key_type);
+       exit_cifs_spnego();
 out_destroy_request_bufs:
 #endif
        cifs_destroy_request_bufs();
index ca7980a1e303b85d4ef85d2f615645371c1934f7..05ce3072fb095d51a719384a6e8b4ec89aa74061 100644 (file)
@@ -59,6 +59,8 @@ do {                                                          \
 } while (0)
 extern int init_cifs_idmap(void);
 extern void exit_cifs_idmap(void);
+extern int init_cifs_spnego(void);
+extern void exit_cifs_spnego(void);
 extern char *build_path_from_dentry(struct dentry *);
 extern char *cifs_build_path_to_root(struct smb_vol *vol,
                                     struct cifs_sb_info *cifs_sb,