Commit 133672ef authored by Steve French's avatar Steve French

[CIFS] Fix buffer overflow if server sends corrupt response to small

request

In SendReceive() function in transport.c - it memcpy's
message payload into a buffer passed via out_buf param. The function
assumes that all buffers are of size (CIFSMaxBufSize +
MAX_CIFS_HDR_SIZE) , unfortunately it is also called with smaller
(MAX_CIFS_SMALL_BUFFER_SIZE) buffers.  There are eight callers
(SMB worker functions) which are primarily affected by this change:

TreeDisconnect, uLogoff, Close, findClose, SetFileSize, SetFileTimes,
Lock and PosixLock

CC: Dave Kleikamp <shaggy@austin.ibm.com>
CC: Przemyslaw Wegrzyn <czajnik@czajsoft.pl>
Acked-by: default avatarJeff Layton <jlayton@redhat.com>
Signed-off-by: default avatarSteve French <sfrench@us.ibm.com>
parent 9418d5dc
......@@ -471,6 +471,17 @@ struct dir_notify_req {
#define CIFS_LARGE_BUFFER 2
#define CIFS_IOVEC 4 /* array of response buffers */
/* Type of Request to SendReceive2 */
#define CIFS_STD_OP 0 /* normal request timeout */
#define CIFS_LONG_OP 1 /* long op (up to 45 sec, oplock time) */
#define CIFS_VLONG_OP 2 /* sloow op - can take up to 180 seconds */
#define CIFS_BLOCKING_OP 4 /* operation can block */
#define CIFS_ASYNC_OP 8 /* do not wait for response */
#define CIFS_TIMEOUT_MASK 0x00F /* only one of 5 above set in req */
#define CIFS_LOG_ERROR 0x010 /* log NT STATUS if non-zero */
#define CIFS_LARGE_BUF_OP 0x020 /* large request buffer */
#define CIFS_NO_RESP 0x040 /* no response buffer required */
/* Security Flags: indicate type of session setup needed */
#define CIFSSEC_MAY_SIGN 0x00001
#define CIFSSEC_MAY_NTLM 0x00002
......
......@@ -48,10 +48,11 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifsSesInfo *,
struct smb_hdr * /* input */ ,
struct smb_hdr * /* out */ ,
int * /* bytes returned */ , const int long_op);
extern int SendReceiveNoRsp(const unsigned int xid, struct cifsSesInfo *ses,
struct smb_hdr *in_buf, int flags);
extern int SendReceive2(const unsigned int /* xid */ , struct cifsSesInfo *,
struct kvec *, int /* nvec to send */,
int * /* type of buf returned */ , const int long_op,
const int logError /* whether to log status code*/ );
int * /* type of buf returned */ , const int flags);
extern int SendReceiveBlockingLock(const unsigned int /* xid */ ,
struct cifsTconInfo *,
struct smb_hdr * /* input */ ,
......
This diff is collapsed.
......@@ -2374,7 +2374,7 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses,
pSMB->req_no_secext.ByteCount = cpu_to_le16(count);
rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response,
&bytes_returned, 1);
&bytes_returned, CIFS_LONG_OP);
if (rc) {
/* rc = map_smb_to_linux_error(smb_buffer_response); now done in SendReceive */
} else if ((smb_buffer_response->WordCount == 3)
......@@ -2678,7 +2678,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
pSMB->req.ByteCount = cpu_to_le16(count);
rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response,
&bytes_returned, 1);
&bytes_returned, CIFS_LONG_OP);
if (smb_buffer_response->Status.CifsError ==
cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))
......@@ -3105,7 +3105,7 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses,
pSMB->req.ByteCount = cpu_to_le16(count);
rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response,
&bytes_returned, 1);
&bytes_returned, CIFS_LONG_OP);
if (rc) {
/* rc = map_smb_to_linux_error(smb_buffer_response) done in SendReceive now */
} else if ((smb_buffer_response->WordCount == 3) ||
......@@ -3381,7 +3381,8 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
pSMB->hdr.smb_buf_length += count;
pSMB->ByteCount = cpu_to_le16(count);
rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length, 0);
rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length,
CIFS_STD_OP);
/* if (rc) rc = map_smb_to_linux_error(smb_buffer_response); */
/* above now done in SendReceive */
......
......@@ -835,9 +835,9 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,
xid = GetXid();
if (*poffset > file->f_path.dentry->d_inode->i_size)
long_op = 2; /* writes past end of file can take a long time */
long_op = CIFS_VLONG_OP; /* writes past EOF take long time */
else
long_op = 1;
long_op = CIFS_LONG_OP;
for (total_written = 0; write_size > total_written;
total_written += bytes_written) {
......@@ -884,7 +884,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,
}
} else
*poffset += bytes_written;
long_op = FALSE; /* subsequent writes fast -
long_op = CIFS_STD_OP; /* subsequent writes fast -
15 seconds is plenty */
}
......@@ -934,9 +934,9 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
xid = GetXid();
if (*poffset > file->f_path.dentry->d_inode->i_size)
long_op = 2; /* writes past end of file can take a long time */
long_op = CIFS_VLONG_OP; /* writes past EOF can be slow */
else
long_op = 1;
long_op = CIFS_LONG_OP;
for (total_written = 0; write_size > total_written;
total_written += bytes_written) {
......@@ -1002,7 +1002,7 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
}
} else
*poffset += bytes_written;
long_op = FALSE; /* subsequent writes fast -
long_op = CIFS_STD_OP; /* subsequent writes fast -
15 seconds is plenty */
}
......@@ -1360,7 +1360,7 @@ retry:
open_file->netfid,
bytes_to_write, offset,
&bytes_written, iov, n_iov,
1);
CIFS_LONG_OP);
atomic_dec(&open_file->wrtPending);
if (rc || bytes_written < bytes_to_write) {
cERROR(1, ("Write2 ret %d, wrote %d",
......
......@@ -514,7 +514,7 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
iov[1].iov_base = str_area;
iov[1].iov_len = count;
rc = SendReceive2(xid, ses, iov, 2 /* num_iovecs */, &resp_buf_type,
0 /* not long op */, 1 /* log NT STATUS if any */ );
CIFS_STD_OP /* not long */ | CIFS_LOG_ERROR);
/* SMB request buf freed in SendReceive2 */
cFYI(1, ("ssetup rc from sendrecv2 is %d", rc));
......
......@@ -308,7 +308,7 @@ smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec,
static int wait_for_free_request(struct cifsSesInfo *ses, const int long_op)
{
if (long_op == -1) {
if (long_op == CIFS_ASYNC_OP) {
/* oplock breaks must not be held up */
atomic_inc(&ses->server->inFlight);
} else {
......@@ -337,7 +337,7 @@ static int wait_for_free_request(struct cifsSesInfo *ses, const int long_op)
as they are allowed to block on server */
/* update # of requests on the wire to server */
if (long_op < 3)
if (long_op != CIFS_BLOCKING_OP)
atomic_inc(&ses->server->inFlight);
spin_unlock(&GlobalMid_Lock);
break;
......@@ -415,17 +415,48 @@ static int wait_for_response(struct cifsSesInfo *ses,
}
}
/*
*
* Send an SMB Request. No response info (other than return code)
* needs to be parsed.
*
* flags indicate the type of request buffer and how long to wait
* and whether to log NT STATUS code (error) before mapping it to POSIX error
*
*/
int
SendReceiveNoRsp(const unsigned int xid, struct cifsSesInfo *ses,
struct smb_hdr *in_buf, int flags)
{
int rc;
struct kvec iov[1];
int resp_buf_type;
iov[0].iov_base = (char *)in_buf;
iov[0].iov_len = in_buf->smb_buf_length + 4;
flags |= CIFS_NO_RESP;
rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags);
#ifdef CONFIG_CIFS_DEBUG2
cFYI(1, ("SendRcvNoR flags %d rc %d", flags, rc));
#endif
return rc;
}
int
SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
struct kvec *iov, int n_vec, int *pRespBufType /* ret */,
const int long_op, const int logError)
const int flags)
{
int rc = 0;
int long_op;
unsigned int receive_len;
unsigned long timeout;
struct mid_q_entry *midQ;
struct smb_hdr *in_buf = iov[0].iov_base;
long_op = flags & CIFS_TIMEOUT_MASK;
*pRespBufType = CIFS_NO_BUFFER; /* no response buf yet */
if ((ses == NULL) || (ses->server == NULL)) {
......@@ -483,15 +514,22 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
if (rc < 0)
goto out;
if (long_op == -1)
goto out;
else if (long_op == 2) /* writes past end of file can take loong time */
if (long_op == CIFS_STD_OP)
timeout = 15 * HZ;
else if (long_op == CIFS_VLONG_OP) /* e.g. slow writes past EOF */
timeout = 180 * HZ;
else if (long_op == 1)
else if (long_op == CIFS_LONG_OP)
timeout = 45 * HZ; /* should be greater than
servers oplock break timeout (about 43 seconds) */
else
timeout = 15 * HZ;
else if (long_op == CIFS_ASYNC_OP)
goto out;
else if (long_op == CIFS_BLOCKING_OP)
timeout = 0x7FFFFFFF; /* large, but not so large as to wrap */
else {
cERROR(1, ("unknown timeout flag %d", long_op));
rc = -EIO;
goto out;
}
/* wait for 15 seconds or until woken up due to response arriving or
due to last connection to this server being unmounted */
......@@ -566,7 +604,8 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
}
/* BB special case reconnect tid and uid here? */
rc = map_smb_to_linux_error(midQ->resp_buf, logError);
rc = map_smb_to_linux_error(midQ->resp_buf,
flags & CIFS_LOG_ERROR);
/* convert ByteCount if necessary */
if (receive_len >= sizeof(struct smb_hdr) - 4
......@@ -574,8 +613,10 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
(2 * midQ->resp_buf->WordCount) + 2 /* bcc */ )
BCC(midQ->resp_buf) =
le16_to_cpu(BCC_LE(midQ->resp_buf));
midQ->resp_buf = NULL; /* mark it so will not be freed
by DeleteMidQEntry */
if ((flags & CIFS_NO_RESP) == 0)
midQ->resp_buf = NULL; /* mark it so buf will
not be freed by
DeleteMidQEntry */
} else {
rc = -EIO;
cFYI(1, ("Bad MID state?"));
......@@ -663,17 +704,25 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
if (rc < 0)
goto out;
if (long_op == -1)
if (long_op == CIFS_STD_OP)
timeout = 15 * HZ;
/* wait for 15 seconds or until woken up due to response arriving or
due to last connection to this server being unmounted */
else if (long_op == CIFS_ASYNC_OP)
goto out;
else if (long_op == 2) /* writes past end of file can take loong time */
else if (long_op == CIFS_VLONG_OP) /* writes past EOF can be slow */
timeout = 180 * HZ;
else if (long_op == 1)
else if (long_op == CIFS_LONG_OP)
timeout = 45 * HZ; /* should be greater than
servers oplock break timeout (about 43 seconds) */
else
timeout = 15 * HZ;
/* wait for 15 seconds or until woken up due to response arriving or
due to last connection to this server being unmounted */
else if (long_op == CIFS_BLOCKING_OP)
timeout = 0x7FFFFFFF; /* large but no so large as to wrap */
else {
cERROR(1, ("unknown timeout flag %d", long_op));
rc = -EIO;
goto out;
}
if (signal_pending(current)) {
/* if signal pending do not hold up user for full smb timeout
but we still give response a chance to complete */
......@@ -812,7 +861,7 @@ send_lock_cancel(const unsigned int xid, struct cifsTconInfo *tcon,
pSMB->hdr.Mid = GetNextMid(ses->server);
return SendReceive(xid, ses, in_buf, out_buf,
&bytes_returned, 0);
&bytes_returned, CIFS_STD_OP);
}
int
......@@ -844,7 +893,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon,
to the same server. We may make this configurable later or
use ses->maxReq */
rc = wait_for_free_request(ses, 3);
rc = wait_for_free_request(ses, CIFS_BLOCKING_OP);
if (rc)
return rc;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment