[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>
This commit is contained in:
Steve French 2009-02-20 05:43:09 +00:00
parent c3b2a0c640
commit eca6acf915
5 changed files with 105 additions and 7 deletions

View file

@ -1,3 +1,13 @@
Version 1.57
------------
Improve support for multiple security contexts to the same server. We
used to use the same "vcnumber" for all connections which could cause
the server to treat subsequent connections, especially those that
are authenticated as guest, as reconnections, invalidating the earlier
user's smb session. This fix allows cifs to mount multiple times to the
same server with different userids without risking invalidating earlier
established security contexts.
Version 1.56
------------
Add "forcemandatorylock" mount option to allow user to use mandatory

View file

@ -100,5 +100,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
extern const struct export_operations cifs_export_ops;
#endif /* EXPERIMENTAL */
#define CIFS_VERSION "1.56"
#define CIFS_VERSION "1.57"
#endif /* _CIFSFS_H */

View file

@ -164,9 +164,12 @@ struct TCP_Server_Info {
/* multiplexed reads or writes */
unsigned int maxBuf; /* maxBuf specifies the maximum */
/* message size the server can send or receive for non-raw SMBs */
unsigned int maxRw; /* maxRw specifies the maximum */
unsigned int max_rw; /* maxRw specifies the maximum */
/* message size the server can send or receive for */
/* SMB_COM_WRITE_RAW or SMB_COM_READ_RAW. */
unsigned int max_vcs; /* maximum number of smb sessions, at least
those that can be specified uniquely with
vcnumbers */
char sessid[4]; /* unique token id for this session */
/* (returned on Negotiate */
int capabilities; /* allow selective disabling of caps by smb sess */
@ -210,6 +213,7 @@ struct cifsSesInfo {
unsigned overrideSecFlg; /* if non-zero override global sec flags */
__u16 ipc_tid; /* special tid for connection to IPC share */
__u16 flags;
__u16 vcnum;
char *serverOS; /* name of operating system underlying server */
char *serverNOS; /* name of network operating system of server */
char *serverDomain; /* security realm of server */

View file

@ -528,14 +528,15 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
server->maxReq = le16_to_cpu(rsp->MaxMpxCount);
server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize),
(__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs);
GETU32(server->sessid) = le32_to_cpu(rsp->SessionKey);
/* even though we do not use raw we might as well set this
accurately, in case we ever find a need for it */
if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) {
server->maxRw = 0xFF00;
server->max_rw = 0xFF00;
server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE;
} else {
server->maxRw = 0;/* we do not need to use raw anyway */
server->max_rw = 0;/* do not need to use raw anyway */
server->capabilities = CAP_MPX_MODE;
}
tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone);
@ -638,7 +639,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
/* probably no need to store and check maxvcs */
server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize),
(__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
server->maxRw = le32_to_cpu(pSMBr->MaxRawSize);
server->max_rw = le32_to_cpu(pSMBr->MaxRawSize);
cFYI(DBG2, ("Max buf = %d", ses->server->maxBuf));
GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey);
server->capabilities = le32_to_cpu(pSMBr->Capabilities);

View file

@ -34,15 +34,99 @@
extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8,
unsigned char *p24);
/* Checks if this is the first smb session to be reconnected after
the socket has been reestablished (so we know whether to use vc 0).
Called while holding the cifs_tcp_ses_lock, so do not block */
static bool is_first_ses_reconnect(struct cifsSesInfo *ses)
{
struct list_head *tmp;
struct cifsSesInfo *tmp_ses;
list_for_each(tmp, &ses->server->smb_ses_list) {
tmp_ses = list_entry(tmp, struct cifsSesInfo,
smb_ses_list);
if (tmp_ses->need_reconnect == false)
return false;
}
/* could not find a session that was already connected,
this must be the first one we are reconnecting */
return true;
}
/*
* vc number 0 is treated specially by some servers, and should be the
* first one we request. After that we can use vcnumbers up to maxvcs,
* one for each smb session (some Windows versions set maxvcs incorrectly
* so maxvc=1 can be ignored). If we have too many vcs, we can reuse
* any vc but zero (some servers reset the connection on vcnum zero)
*
*/
static __le16 get_next_vcnum(struct cifsSesInfo *ses)
{
__u16 vcnum = 0;
struct list_head *tmp;
struct cifsSesInfo *tmp_ses;
__u16 max_vcs = ses->server->max_vcs;
__u16 i;
int free_vc_found = 0;
/* Quoting the MS-SMB specification: "Windows-based SMB servers set this
field to one but do not enforce this limit, which allows an SMB client
to establish more virtual circuits than allowed by this value ... but
other server implementations can enforce this limit." */
if (max_vcs < 2)
max_vcs = 0xFFFF;
write_lock(&cifs_tcp_ses_lock);
if ((ses->need_reconnect) && is_first_ses_reconnect(ses))
goto get_vc_num_exit; /* vcnum will be zero */
for (i = ses->server->srv_count - 1; i < max_vcs; i++) {
if (i == 0) /* this is the only connection, use vc 0 */
break;
free_vc_found = 1;
list_for_each(tmp, &ses->server->smb_ses_list) {
tmp_ses = list_entry(tmp, struct cifsSesInfo,
smb_ses_list);
if (tmp_ses->vcnum == i) {
free_vc_found = 0;
break; /* found duplicate, try next vcnum */
}
}
if (free_vc_found)
break; /* we found a vcnumber that will work - use it */
}
if (i == 0)
vcnum = 0; /* for most common case, ie if one smb session, use
vc zero. Also for case when no free vcnum, zero
is safest to send (some clients only send zero) */
else if (free_vc_found == 0)
vcnum = 1; /* we can not reuse vc=0 safely, since some servers
reset all uids on that, but 1 is ok. */
else
vcnum = i;
ses->vcnum = vcnum;
get_vc_num_exit:
write_unlock(&cifs_tcp_ses_lock);
return le16_to_cpu(vcnum);
}
static __u32 cifs_ssetup_hdr(struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB)
{
__u32 capabilities = 0;
/* init fields common to all four types of SessSetup */
/* note that header is initialized to zero in header_assemble */
/* Note that offsets for first seven fields in req struct are same */
/* in CIFS Specs so does not matter which of 3 forms of struct */
/* that we use in next few lines */
/* Note that header is initialized to zero in header_assemble */
pSMB->req.AndXCommand = 0xFF;
pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
pSMB->req.VcNumber = get_next_vcnum(ses);
/* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */
@ -71,7 +155,6 @@ static __u32 cifs_ssetup_hdr(struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB)
if (ses->capabilities & CAP_UNIX)
capabilities |= CAP_UNIX;
/* BB check whether to init vcnum BB */
return capabilities;
}