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

cifs: Create dedicated keyring for spnego operations

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>
CC: Stable <stable@vger.kernel.org>
Signed-off-by: Steve French <smfrench@gmail.com>

authored by

Sachin Prabhu and committed by
Steve French
b74cb9a8 03b979dd

+71 -2
+67
fs/cifs/cifs_spnego.c
··· 24 24 #include <linux/string.h> 25 25 #include <keys/user-type.h> 26 26 #include <linux/key-type.h> 27 + #include <linux/keyctl.h> 27 28 #include <linux/inet.h> 28 29 #include "cifsglob.h" 29 30 #include "cifs_spnego.h" 30 31 #include "cifs_debug.h" 32 + #include "cifsproto.h" 33 + static const struct cred *spnego_cred; 31 34 32 35 /* create a new cifs key */ 33 36 static int ··· 105 102 size_t desc_len; 106 103 struct key *spnego_key; 107 104 const char *hostname = server->hostname; 105 + const struct cred *saved_cred; 108 106 109 107 /* length of fields (with semicolons): ver=0xyz ip4=ipaddress 110 108 host=hostname sec=mechanism uid=0xFF user=username */ ··· 167 163 sprintf(dp, ";pid=0x%x", current->pid); 168 164 169 165 cifs_dbg(FYI, "key description = %s\n", description); 166 + saved_cred = override_creds(spnego_cred); 170 167 spnego_key = request_key(&cifs_spnego_key_type, description, ""); 168 + revert_creds(saved_cred); 171 169 172 170 #ifdef CONFIG_CIFS_DEBUG2 173 171 if (cifsFYI && !IS_ERR(spnego_key)) { ··· 182 176 out: 183 177 kfree(description); 184 178 return spnego_key; 179 + } 180 + 181 + int 182 + init_cifs_spnego(void) 183 + { 184 + struct cred *cred; 185 + struct key *keyring; 186 + int ret; 187 + 188 + cifs_dbg(FYI, "Registering the %s key type\n", 189 + cifs_spnego_key_type.name); 190 + 191 + /* 192 + * Create an override credential set with special thread keyring for 193 + * spnego upcalls. 194 + */ 195 + 196 + cred = prepare_kernel_cred(NULL); 197 + if (!cred) 198 + return -ENOMEM; 199 + 200 + keyring = keyring_alloc(".cifs_spnego", 201 + GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, 202 + (KEY_POS_ALL & ~KEY_POS_SETATTR) | 203 + KEY_USR_VIEW | KEY_USR_READ, 204 + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); 205 + if (IS_ERR(keyring)) { 206 + ret = PTR_ERR(keyring); 207 + goto failed_put_cred; 208 + } 209 + 210 + ret = register_key_type(&cifs_spnego_key_type); 211 + if (ret < 0) 212 + goto failed_put_key; 213 + 214 + /* 215 + * instruct request_key() to use this special keyring as a cache for 216 + * the results it looks up 217 + */ 218 + set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags); 219 + cred->thread_keyring = keyring; 220 + cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; 221 + spnego_cred = cred; 222 + 223 + cifs_dbg(FYI, "cifs spnego keyring: %d\n", key_serial(keyring)); 224 + return 0; 225 + 226 + failed_put_key: 227 + key_put(keyring); 228 + failed_put_cred: 229 + put_cred(cred); 230 + return ret; 231 + } 232 + 233 + void 234 + exit_cifs_spnego(void) 235 + { 236 + key_revoke(spnego_cred->thread_keyring); 237 + unregister_key_type(&cifs_spnego_key_type); 238 + put_cred(spnego_cred); 239 + cifs_dbg(FYI, "Unregistered %s key type\n", cifs_spnego_key_type.name); 185 240 }
+2 -2
fs/cifs/cifsfs.c
··· 1303 1303 goto out_destroy_mids; 1304 1304 1305 1305 #ifdef CONFIG_CIFS_UPCALL 1306 - rc = register_key_type(&cifs_spnego_key_type); 1306 + rc = init_cifs_spnego(); 1307 1307 if (rc) 1308 1308 goto out_destroy_request_bufs; 1309 1309 #endif /* CONFIG_CIFS_UPCALL */ ··· 1326 1326 out_register_key_type: 1327 1327 #endif 1328 1328 #ifdef CONFIG_CIFS_UPCALL 1329 - unregister_key_type(&cifs_spnego_key_type); 1329 + exit_cifs_spnego(); 1330 1330 out_destroy_request_bufs: 1331 1331 #endif 1332 1332 cifs_destroy_request_bufs();
+2
fs/cifs/cifsproto.h
··· 58 58 } while (0) 59 59 extern int init_cifs_idmap(void); 60 60 extern void exit_cifs_idmap(void); 61 + extern int init_cifs_spnego(void); 62 + extern void exit_cifs_spnego(void); 61 63 extern char *build_path_from_dentry(struct dentry *); 62 64 extern char *cifs_build_path_to_root(struct smb_vol *vol, 63 65 struct cifs_sb_info *cifs_sb,