[CIFS] Fix multiuser mounts so server does not invalidate earlier security contexts

When two different users mount the same Windows 2003 Server share using CIFS,
the first session mounted can be invalidated. Some servers invalidate the first
smb session when a second similar user (e.g. two users who get mapped by server to "guest")
authenticates an smb session from the same client.

By making sure that we set the 2nd and subsequent vc numbers to nonzero values,
this ensures that we will not have this problem.

Fixes Samba bug 6004, problem description follows:
How to reproduce:

- configure an "open share" (full permissions to Guest user) on Windows 2003
Server (I couldn't reproduce the problem with Samba server or Windows older
than 2003)
- mount the share twice with different users who will be authenticated as guest.

noacl,noperm,user=john,dir_mode=0700,domain=DOMAIN,rw
noacl,noperm,user=jeff,dir_mode=0700,domain=DOMAIN,rw

Result:

- just the mount point mounted last is accessible:

Signed-off-by: Steve French <sfrench@us.ibm.com>

+105 -7
+10
fs/cifs/CHANGES
··· 1 Version 1.56 2 ------------ 3 Add "forcemandatorylock" mount option to allow user to use mandatory
··· 1 + Version 1.57 2 + ------------ 3 + Improve support for multiple security contexts to the same server. We 4 + used to use the same "vcnumber" for all connections which could cause 5 + the server to treat subsequent connections, especially those that 6 + are authenticated as guest, as reconnections, invalidating the earlier 7 + user's smb session. This fix allows cifs to mount multiple times to the 8 + same server with different userids without risking invalidating earlier 9 + established security contexts. 10 + 11 Version 1.56 12 ------------ 13 Add "forcemandatorylock" mount option to allow user to use mandatory
+1 -1
fs/cifs/cifsfs.h
··· 100 extern const struct export_operations cifs_export_ops; 101 #endif /* EXPERIMENTAL */ 102 103 - #define CIFS_VERSION "1.56" 104 #endif /* _CIFSFS_H */
··· 100 extern const struct export_operations cifs_export_ops; 101 #endif /* EXPERIMENTAL */ 102 103 + #define CIFS_VERSION "1.57" 104 #endif /* _CIFSFS_H */
+5 -1
fs/cifs/cifsglob.h
··· 164 /* multiplexed reads or writes */ 165 unsigned int maxBuf; /* maxBuf specifies the maximum */ 166 /* message size the server can send or receive for non-raw SMBs */ 167 - unsigned int maxRw; /* maxRw specifies the maximum */ 168 /* message size the server can send or receive for */ 169 /* SMB_COM_WRITE_RAW or SMB_COM_READ_RAW. */ 170 char sessid[4]; /* unique token id for this session */ 171 /* (returned on Negotiate */ 172 int capabilities; /* allow selective disabling of caps by smb sess */ ··· 213 unsigned overrideSecFlg; /* if non-zero override global sec flags */ 214 __u16 ipc_tid; /* special tid for connection to IPC share */ 215 __u16 flags; 216 char *serverOS; /* name of operating system underlying server */ 217 char *serverNOS; /* name of network operating system of server */ 218 char *serverDomain; /* security realm of server */
··· 164 /* multiplexed reads or writes */ 165 unsigned int maxBuf; /* maxBuf specifies the maximum */ 166 /* message size the server can send or receive for non-raw SMBs */ 167 + unsigned int max_rw; /* maxRw specifies the maximum */ 168 /* message size the server can send or receive for */ 169 /* SMB_COM_WRITE_RAW or SMB_COM_READ_RAW. */ 170 + unsigned int max_vcs; /* maximum number of smb sessions, at least 171 + those that can be specified uniquely with 172 + vcnumbers */ 173 char sessid[4]; /* unique token id for this session */ 174 /* (returned on Negotiate */ 175 int capabilities; /* allow selective disabling of caps by smb sess */ ··· 210 unsigned overrideSecFlg; /* if non-zero override global sec flags */ 211 __u16 ipc_tid; /* special tid for connection to IPC share */ 212 __u16 flags; 213 + __u16 vcnum; 214 char *serverOS; /* name of operating system underlying server */ 215 char *serverNOS; /* name of network operating system of server */ 216 char *serverDomain; /* security realm of server */
+4 -3
fs/cifs/cifssmb.c
··· 528 server->maxReq = le16_to_cpu(rsp->MaxMpxCount); 529 server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize), 530 (__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE); 531 GETU32(server->sessid) = le32_to_cpu(rsp->SessionKey); 532 /* even though we do not use raw we might as well set this 533 accurately, in case we ever find a need for it */ 534 if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) { 535 - server->maxRw = 0xFF00; 536 server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE; 537 } else { 538 - server->maxRw = 0;/* we do not need to use raw anyway */ 539 server->capabilities = CAP_MPX_MODE; 540 } 541 tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone); ··· 639 /* probably no need to store and check maxvcs */ 640 server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize), 641 (__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE); 642 - server->maxRw = le32_to_cpu(pSMBr->MaxRawSize); 643 cFYI(DBG2, ("Max buf = %d", ses->server->maxBuf)); 644 GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey); 645 server->capabilities = le32_to_cpu(pSMBr->Capabilities);
··· 528 server->maxReq = le16_to_cpu(rsp->MaxMpxCount); 529 server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize), 530 (__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE); 531 + server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs); 532 GETU32(server->sessid) = le32_to_cpu(rsp->SessionKey); 533 /* even though we do not use raw we might as well set this 534 accurately, in case we ever find a need for it */ 535 if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) { 536 + server->max_rw = 0xFF00; 537 server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE; 538 } else { 539 + server->max_rw = 0;/* do not need to use raw anyway */ 540 server->capabilities = CAP_MPX_MODE; 541 } 542 tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone); ··· 638 /* probably no need to store and check maxvcs */ 639 server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize), 640 (__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE); 641 + server->max_rw = le32_to_cpu(pSMBr->MaxRawSize); 642 cFYI(DBG2, ("Max buf = %d", ses->server->maxBuf)); 643 GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey); 644 server->capabilities = le32_to_cpu(pSMBr->Capabilities);
+85 -2
fs/cifs/sess.c
··· 34 extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, 35 unsigned char *p24); 36 37 static __u32 cifs_ssetup_hdr(struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB) 38 { 39 __u32 capabilities = 0; 40 41 /* init fields common to all four types of SessSetup */ 42 - /* note that header is initialized to zero in header_assemble */ 43 pSMB->req.AndXCommand = 0xFF; 44 pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf); 45 pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq); 46 47 /* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */ 48 ··· 155 if (ses->capabilities & CAP_UNIX) 156 capabilities |= CAP_UNIX; 157 158 - /* BB check whether to init vcnum BB */ 159 return capabilities; 160 } 161
··· 34 extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, 35 unsigned char *p24); 36 37 + /* Checks if this is the first smb session to be reconnected after 38 + the socket has been reestablished (so we know whether to use vc 0). 39 + Called while holding the cifs_tcp_ses_lock, so do not block */ 40 + static bool is_first_ses_reconnect(struct cifsSesInfo *ses) 41 + { 42 + struct list_head *tmp; 43 + struct cifsSesInfo *tmp_ses; 44 + 45 + list_for_each(tmp, &ses->server->smb_ses_list) { 46 + tmp_ses = list_entry(tmp, struct cifsSesInfo, 47 + smb_ses_list); 48 + if (tmp_ses->need_reconnect == false) 49 + return false; 50 + } 51 + /* could not find a session that was already connected, 52 + this must be the first one we are reconnecting */ 53 + return true; 54 + } 55 + 56 + /* 57 + * vc number 0 is treated specially by some servers, and should be the 58 + * first one we request. After that we can use vcnumbers up to maxvcs, 59 + * one for each smb session (some Windows versions set maxvcs incorrectly 60 + * so maxvc=1 can be ignored). If we have too many vcs, we can reuse 61 + * any vc but zero (some servers reset the connection on vcnum zero) 62 + * 63 + */ 64 + static __le16 get_next_vcnum(struct cifsSesInfo *ses) 65 + { 66 + __u16 vcnum = 0; 67 + struct list_head *tmp; 68 + struct cifsSesInfo *tmp_ses; 69 + __u16 max_vcs = ses->server->max_vcs; 70 + __u16 i; 71 + int free_vc_found = 0; 72 + 73 + /* Quoting the MS-SMB specification: "Windows-based SMB servers set this 74 + field to one but do not enforce this limit, which allows an SMB client 75 + to establish more virtual circuits than allowed by this value ... but 76 + other server implementations can enforce this limit." */ 77 + if (max_vcs < 2) 78 + max_vcs = 0xFFFF; 79 + 80 + write_lock(&cifs_tcp_ses_lock); 81 + if ((ses->need_reconnect) && is_first_ses_reconnect(ses)) 82 + goto get_vc_num_exit; /* vcnum will be zero */ 83 + for (i = ses->server->srv_count - 1; i < max_vcs; i++) { 84 + if (i == 0) /* this is the only connection, use vc 0 */ 85 + break; 86 + 87 + free_vc_found = 1; 88 + 89 + list_for_each(tmp, &ses->server->smb_ses_list) { 90 + tmp_ses = list_entry(tmp, struct cifsSesInfo, 91 + smb_ses_list); 92 + if (tmp_ses->vcnum == i) { 93 + free_vc_found = 0; 94 + break; /* found duplicate, try next vcnum */ 95 + } 96 + } 97 + if (free_vc_found) 98 + break; /* we found a vcnumber that will work - use it */ 99 + } 100 + 101 + if (i == 0) 102 + vcnum = 0; /* for most common case, ie if one smb session, use 103 + vc zero. Also for case when no free vcnum, zero 104 + is safest to send (some clients only send zero) */ 105 + else if (free_vc_found == 0) 106 + vcnum = 1; /* we can not reuse vc=0 safely, since some servers 107 + reset all uids on that, but 1 is ok. */ 108 + else 109 + vcnum = i; 110 + ses->vcnum = vcnum; 111 + get_vc_num_exit: 112 + write_unlock(&cifs_tcp_ses_lock); 113 + 114 + return le16_to_cpu(vcnum); 115 + } 116 + 117 static __u32 cifs_ssetup_hdr(struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB) 118 { 119 __u32 capabilities = 0; 120 121 /* init fields common to all four types of SessSetup */ 122 + /* Note that offsets for first seven fields in req struct are same */ 123 + /* in CIFS Specs so does not matter which of 3 forms of struct */ 124 + /* that we use in next few lines */ 125 + /* Note that header is initialized to zero in header_assemble */ 126 pSMB->req.AndXCommand = 0xFF; 127 pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf); 128 pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq); 129 + pSMB->req.VcNumber = get_next_vcnum(ses); 130 131 /* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */ 132 ··· 71 if (ses->capabilities & CAP_UNIX) 72 capabilities |= CAP_UNIX; 73 74 return capabilities; 75 } 76