Commit 09c9b0ea authored by Linus Torvalds's avatar Linus Torvalds

Merge tag '4.17-rc1SMB3-Fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull cifs fixes from Steve French:
 "SMB3 fixes, a few for stable, and some important cleanup work from
  Ronnie of the smb3 transport code"

* tag '4.17-rc1SMB3-Fixes' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: change validate_buf to validate_iov
  cifs: remove rfc1002 hardcoded constants from cifs_discard_remaining_data()
  cifs: Change SMB2_open to return an iov for the error parameter
  cifs: add resp_buf_size to the mid_q_entry structure
  smb3.11: replace a 4 with server->vals->header_preamble_size
  cifs: replace a 4 with server->vals->header_preamble_size
  cifs: add pdu_size to the TCP_Server_Info structure
  SMB311: Improve checking of negotiate security contexts
  SMB3: Fix length checking of SMB3.11 negotiate request
  CIFS: add ONCE flag for cifs_dbg type
  cifs: Use ULL suffix for 64-bit constant
  SMB3: Log at least once if tree connect fails during reconnect
  cifs: smb2pdu: Fix potential NULL pointer dereference
parents f0d98d85 c1596ff5
...@@ -42,23 +42,6 @@ cifs_dump_mem(char *label, void *data, int length) ...@@ -42,23 +42,6 @@ cifs_dump_mem(char *label, void *data, int length)
data, length, true); data, length, true);
} }
#ifdef CONFIG_CIFS_DEBUG
void cifs_vfs_err(const char *fmt, ...)
{
struct va_format vaf;
va_list args;
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
pr_err_ratelimited("CIFS VFS: %pV", &vaf);
va_end(args);
}
#endif
void cifs_dump_detail(void *buf) void cifs_dump_detail(void *buf)
{ {
#ifdef CONFIG_CIFS_DEBUG2 #ifdef CONFIG_CIFS_DEBUG2
......
...@@ -39,6 +39,7 @@ extern int cifsFYI; ...@@ -39,6 +39,7 @@ extern int cifsFYI;
#else #else
#define NOISY 0 #define NOISY 0
#endif #endif
#define ONCE 8
/* /*
* debug ON * debug ON
...@@ -46,19 +47,28 @@ extern int cifsFYI; ...@@ -46,19 +47,28 @@ extern int cifsFYI;
*/ */
#ifdef CONFIG_CIFS_DEBUG #ifdef CONFIG_CIFS_DEBUG
__printf(1, 2) void cifs_vfs_err(const char *fmt, ...);
/* information message: e.g., configuration, major event */ /* information message: e.g., configuration, major event */
#define cifs_dbg(type, fmt, ...) \ #define cifs_dbg_func(ratefunc, type, fmt, ...) \
do { \ do { \
if (type == FYI && cifsFYI & CIFS_INFO) { \ if ((type) & FYI && cifsFYI & CIFS_INFO) { \
pr_debug_ratelimited("%s: " \ pr_debug_ ## ratefunc("%s: " \
fmt, __FILE__, ##__VA_ARGS__); \ fmt, __FILE__, ##__VA_ARGS__); \
} else if (type == VFS) { \ } else if ((type) & VFS) { \
cifs_vfs_err(fmt, ##__VA_ARGS__); \ pr_err_ ## ratefunc("CuIFS VFS: " \
} else if (type == NOISY && type != 0) { \ fmt, ##__VA_ARGS__); \
pr_debug_ratelimited(fmt, ##__VA_ARGS__); \ } else if ((type) & NOISY && (NOISY != 0)) { \
} \ pr_debug_ ## ratefunc(fmt, ##__VA_ARGS__); \
} \
} while (0)
#define cifs_dbg(type, fmt, ...) \
do { \
if ((type) & ONCE) \
cifs_dbg_func(once, \
type, fmt, ##__VA_ARGS__); \
else \
cifs_dbg_func(ratelimited, \
type, fmt, ##__VA_ARGS__); \
} while (0) } while (0)
/* /*
......
...@@ -665,6 +665,8 @@ struct TCP_Server_Info { ...@@ -665,6 +665,8 @@ struct TCP_Server_Info {
struct delayed_work echo; /* echo ping workqueue job */ struct delayed_work echo; /* echo ping workqueue job */
char *smallbuf; /* pointer to current "small" buffer */ char *smallbuf; /* pointer to current "small" buffer */
char *bigbuf; /* pointer to current "big" buffer */ char *bigbuf; /* pointer to current "big" buffer */
/* Total size of this PDU. Only valid from cifs_demultiplex_thread */
unsigned int pdu_size;
unsigned int total_read; /* total amount of data read in this pass */ unsigned int total_read; /* total amount of data read in this pass */
#ifdef CONFIG_CIFS_FSCACHE #ifdef CONFIG_CIFS_FSCACHE
struct fscache_cookie *fscache; /* client index cache cookie */ struct fscache_cookie *fscache; /* client index cache cookie */
...@@ -676,6 +678,7 @@ struct TCP_Server_Info { ...@@ -676,6 +678,7 @@ struct TCP_Server_Info {
unsigned int max_read; unsigned int max_read;
unsigned int max_write; unsigned int max_write;
#ifdef CONFIG_CIFS_SMB311 #ifdef CONFIG_CIFS_SMB311
__le16 cipher_type;
/* save initital negprot hash */ /* save initital negprot hash */
__u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE]; __u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
#endif /* 3.1.1 */ #endif /* 3.1.1 */
...@@ -1373,6 +1376,7 @@ struct mid_q_entry { ...@@ -1373,6 +1376,7 @@ struct mid_q_entry {
mid_handle_t *handle; /* call handle mid callback */ mid_handle_t *handle; /* call handle mid callback */
void *callback_data; /* general purpose pointer for callback */ void *callback_data; /* general purpose pointer for callback */
void *resp_buf; /* pointer to received SMB header */ void *resp_buf; /* pointer to received SMB header */
unsigned int resp_buf_size;
int mid_state; /* wish this were enum but can not pass to wait_event */ int mid_state; /* wish this were enum but can not pass to wait_event */
unsigned int mid_flags; unsigned int mid_flags;
__le16 command; /* smb command code */ __le16 command; /* smb command code */
......
...@@ -206,8 +206,10 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) ...@@ -206,8 +206,10 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
mutex_unlock(&ses->session_mutex); mutex_unlock(&ses->session_mutex);
cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
if (rc) if (rc) {
printk_once(KERN_WARNING "reconnect tcon failed rc = %d\n", rc);
goto out; goto out;
}
atomic_inc(&tconInfoReconnectCount); atomic_inc(&tconInfoReconnectCount);
...@@ -1416,8 +1418,9 @@ CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, int *oplock, ...@@ -1416,8 +1418,9 @@ CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, int *oplock,
int int
cifs_discard_remaining_data(struct TCP_Server_Info *server) cifs_discard_remaining_data(struct TCP_Server_Info *server)
{ {
unsigned int rfclen = get_rfc1002_length(server->smallbuf); unsigned int rfclen = server->pdu_size;
int remaining = rfclen + 4 - server->total_read; int remaining = rfclen + server->vals->header_preamble_size -
server->total_read;
while (remaining > 0) { while (remaining > 0) {
int length; int length;
...@@ -1454,7 +1457,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -1454,7 +1457,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
unsigned int data_offset, data_len; unsigned int data_offset, data_len;
struct cifs_readdata *rdata = mid->callback_data; struct cifs_readdata *rdata = mid->callback_data;
char *buf = server->smallbuf; char *buf = server->smallbuf;
unsigned int buflen = get_rfc1002_length(buf) + unsigned int buflen = server->pdu_size +
server->vals->header_preamble_size; server->vals->header_preamble_size;
bool use_rdma_mr = false; bool use_rdma_mr = false;
......
...@@ -772,7 +772,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -772,7 +772,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{ {
int length; int length;
char *buf = server->smallbuf; char *buf = server->smallbuf;
unsigned int pdu_length = get_rfc1002_length(buf); unsigned int pdu_length = server->pdu_size;
/* make sure this will fit in a large buffer */ /* make sure this will fit in a large buffer */
if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) - if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) -
...@@ -881,6 +881,7 @@ cifs_demultiplex_thread(void *p) ...@@ -881,6 +881,7 @@ cifs_demultiplex_thread(void *p)
* so we can now interpret the length field. * so we can now interpret the length field.
*/ */
pdu_length = get_rfc1002_length(buf); pdu_length = get_rfc1002_length(buf);
server->pdu_size = pdu_length;
cifs_dbg(FYI, "RFC1002 header 0x%x\n", pdu_length); cifs_dbg(FYI, "RFC1002 header 0x%x\n", pdu_length);
if (!is_smb_response(server, buf[0])) if (!is_smb_response(server, buf[0]))
...@@ -927,6 +928,7 @@ cifs_demultiplex_thread(void *p) ...@@ -927,6 +928,7 @@ cifs_demultiplex_thread(void *p)
server->lstrp = jiffies; server->lstrp = jiffies;
if (mid_entry != NULL) { if (mid_entry != NULL) {
mid_entry->resp_buf_size = server->pdu_size;
if ((mid_entry->mid_flags & MID_WAIT_CANCELLED) && if ((mid_entry->mid_flags & MID_WAIT_CANCELLED) &&
mid_entry->mid_state == MID_RESPONSE_RECEIVED && mid_entry->mid_state == MID_RESPONSE_RECEIVED &&
server->ops->handle_cancelled_mid) server->ops->handle_cancelled_mid)
......
...@@ -710,7 +710,7 @@ cifs_get_file_info(struct file *filp) ...@@ -710,7 +710,7 @@ cifs_get_file_info(struct file *filp)
/* Simple function to return a 64 bit hash of string. Rarely called */ /* Simple function to return a 64 bit hash of string. Rarely called */
static __u64 simple_hashstr(const char *str) static __u64 simple_hashstr(const char *str)
{ {
const __u64 hash_mult = 1125899906842597L; /* a big enough prime */ const __u64 hash_mult = 1125899906842597ULL; /* a big enough prime */
__u64 hash = 0; __u64 hash = 0;
while (*str) while (*str)
......
...@@ -93,6 +93,43 @@ static const __le16 smb2_rsp_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { ...@@ -93,6 +93,43 @@ static const __le16 smb2_rsp_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = {
/* SMB2_OPLOCK_BREAK */ cpu_to_le16(24) /* SMB2_OPLOCK_BREAK */ cpu_to_le16(24)
}; };
#ifdef CONFIG_CIFS_SMB311
static __u32 get_neg_ctxt_len(struct smb2_hdr *hdr, __u32 len, __u32 non_ctxlen,
size_t hdr_preamble_size)
{
__u16 neg_count;
__u32 nc_offset, size_of_pad_before_neg_ctxts;
struct smb2_negotiate_rsp *pneg_rsp = (struct smb2_negotiate_rsp *)hdr;
/* Negotiate contexts are only valid for latest dialect SMB3.11 */
neg_count = le16_to_cpu(pneg_rsp->NegotiateContextCount);
if ((neg_count == 0) ||
(pneg_rsp->DialectRevision != cpu_to_le16(SMB311_PROT_ID)))
return 0;
/* Make sure that negotiate contexts start after gss security blob */
nc_offset = le32_to_cpu(pneg_rsp->NegotiateContextOffset);
if (nc_offset < non_ctxlen - hdr_preamble_size /* RFC1001 len */) {
printk_once(KERN_WARNING "invalid negotiate context offset\n");
return 0;
}
size_of_pad_before_neg_ctxts = nc_offset -
(non_ctxlen - hdr_preamble_size);
/* Verify that at least minimal negotiate contexts fit within frame */
if (len < nc_offset + (neg_count * sizeof(struct smb2_neg_context))) {
printk_once(KERN_WARNING "negotiate context goes beyond end\n");
return 0;
}
cifs_dbg(FYI, "length of negcontexts %d pad %d\n",
len - nc_offset, size_of_pad_before_neg_ctxts);
/* length of negcontexts including pad from end of sec blob to them */
return (len - nc_offset) + size_of_pad_before_neg_ctxts;
}
#endif /* CIFS_SMB311 */
int int
smb2_check_message(char *buf, unsigned int length, struct TCP_Server_Info *srvr) smb2_check_message(char *buf, unsigned int length, struct TCP_Server_Info *srvr)
{ {
...@@ -198,6 +235,11 @@ smb2_check_message(char *buf, unsigned int length, struct TCP_Server_Info *srvr) ...@@ -198,6 +235,11 @@ smb2_check_message(char *buf, unsigned int length, struct TCP_Server_Info *srvr)
clc_len = smb2_calc_size(hdr); clc_len = smb2_calc_size(hdr);
#ifdef CONFIG_CIFS_SMB311
if (shdr->Command == SMB2_NEGOTIATE)
clc_len += get_neg_ctxt_len(hdr, len, clc_len,
srvr->vals->header_preamble_size);
#endif /* SMB311 */
if (srvr->vals->header_preamble_size + len != clc_len) { if (srvr->vals->header_preamble_size + len != clc_len) {
cifs_dbg(FYI, "Calculated size %u length %zu mismatch mid %llu\n", cifs_dbg(FYI, "Calculated size %u length %zu mismatch mid %llu\n",
clc_len, srvr->vals->header_preamble_size + len, mid); clc_len, srvr->vals->header_preamble_size + len, mid);
......
...@@ -1451,6 +1451,7 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -1451,6 +1451,7 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE; __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
struct cifs_open_parms oparms; struct cifs_open_parms oparms;
struct cifs_fid fid; struct cifs_fid fid;
struct kvec err_iov = {NULL, 0};
struct smb2_err_rsp *err_buf = NULL; struct smb2_err_rsp *err_buf = NULL;
struct smb2_symlink_err_rsp *symlink; struct smb2_symlink_err_rsp *symlink;
unsigned int sub_len; unsigned int sub_len;
...@@ -1473,15 +1474,16 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -1473,15 +1474,16 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
oparms.fid = &fid; oparms.fid = &fid;
oparms.reconnect = false; oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, &err_buf); rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, &err_iov);
if (!rc || !err_buf) { if (!rc || !err_buf) {
kfree(utf16_path); kfree(utf16_path);
return -ENOENT; return -ENOENT;
} }
err_buf = err_iov.iov_base;
if (le32_to_cpu(err_buf->ByteCount) < sizeof(struct smb2_symlink_err_rsp) || if (le32_to_cpu(err_buf->ByteCount) < sizeof(struct smb2_symlink_err_rsp) ||
get_rfc1002_length(err_buf) + server->vals->header_preamble_size < SMB2_SYMLINK_STRUCT_SIZE) { err_iov.iov_len + server->vals->header_preamble_size < SMB2_SYMLINK_STRUCT_SIZE) {
kfree(utf16_path); kfree(utf16_path);
return -ENOENT; return -ENOENT;
} }
...@@ -1494,13 +1496,13 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -1494,13 +1496,13 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
print_len = le16_to_cpu(symlink->PrintNameLength); print_len = le16_to_cpu(symlink->PrintNameLength);
print_offset = le16_to_cpu(symlink->PrintNameOffset); print_offset = le16_to_cpu(symlink->PrintNameOffset);
if (get_rfc1002_length(err_buf) + server->vals->header_preamble_size < if (err_iov.iov_len + server->vals->header_preamble_size <
SMB2_SYMLINK_STRUCT_SIZE + sub_offset + sub_len) { SMB2_SYMLINK_STRUCT_SIZE + sub_offset + sub_len) {
kfree(utf16_path); kfree(utf16_path);
return -ENOENT; return -ENOENT;
} }
if (get_rfc1002_length(err_buf) + server->vals->header_preamble_size < if (err_iov.iov_len + server->vals->header_preamble_size <
SMB2_SYMLINK_STRUCT_SIZE + print_offset + print_len) { SMB2_SYMLINK_STRUCT_SIZE + print_offset + print_len) {
kfree(utf16_path); kfree(utf16_path);
return -ENOENT; return -ENOENT;
...@@ -2550,7 +2552,7 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid) ...@@ -2550,7 +2552,7 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid)
unsigned int npages; unsigned int npages;
struct page **pages; struct page **pages;
unsigned int len; unsigned int len;
unsigned int buflen = get_rfc1002_length(buf) + server->vals->header_preamble_size; unsigned int buflen = server->pdu_size + server->vals->header_preamble_size;
int rc; int rc;
int i = 0; int i = 0;
...@@ -2624,7 +2626,7 @@ receive_encrypted_standard(struct TCP_Server_Info *server, ...@@ -2624,7 +2626,7 @@ receive_encrypted_standard(struct TCP_Server_Info *server,
{ {
int length; int length;
char *buf = server->smallbuf; char *buf = server->smallbuf;
unsigned int pdu_length = get_rfc1002_length(buf); unsigned int pdu_length = server->pdu_size;
unsigned int buf_size; unsigned int buf_size;
struct mid_q_entry *mid_entry; struct mid_q_entry *mid_entry;
...@@ -2668,7 +2670,7 @@ static int ...@@ -2668,7 +2670,7 @@ static int
smb3_receive_transform(struct TCP_Server_Info *server, struct mid_q_entry **mid) smb3_receive_transform(struct TCP_Server_Info *server, struct mid_q_entry **mid)
{ {
char *buf = server->smallbuf; char *buf = server->smallbuf;
unsigned int pdu_length = get_rfc1002_length(buf); unsigned int pdu_length = server->pdu_size;
struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf; struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf;
unsigned int orig_len = le32_to_cpu(tr_hdr->OriginalMessageSize); unsigned int orig_len = le32_to_cpu(tr_hdr->OriginalMessageSize);
...@@ -2699,7 +2701,7 @@ smb3_handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -2699,7 +2701,7 @@ smb3_handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{ {
char *buf = server->large_buf ? server->bigbuf : server->smallbuf; char *buf = server->large_buf ? server->bigbuf : server->smallbuf;
return handle_read_data(server, mid, buf, get_rfc1002_length(buf) + return handle_read_data(server, mid, buf, server->pdu_size +
server->vals->header_preamble_size, server->vals->header_preamble_size,
NULL, 0, 0); NULL, 0, 0);
} }
......
...@@ -268,8 +268,11 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) ...@@ -268,8 +268,11 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
mutex_unlock(&tcon->ses->session_mutex); mutex_unlock(&tcon->ses->session_mutex);
cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
if (rc) if (rc) {
/* If sess reconnected but tcon didn't, something strange ... */
printk_once(KERN_WARNING "reconnect tcon failed rc = %d\n", rc);
goto out; goto out;
}
if (smb2_command != SMB2_INTERNAL_CMD) if (smb2_command != SMB2_INTERNAL_CMD)
queue_delayed_work(cifsiod_wq, &server->reconnect, 0); queue_delayed_work(cifsiod_wq, &server->reconnect, 0);
...@@ -403,6 +406,100 @@ assemble_neg_contexts(struct smb2_negotiate_req *req, ...@@ -403,6 +406,100 @@ assemble_neg_contexts(struct smb2_negotiate_req *req,
*total_len += 4 + sizeof(struct smb2_preauth_neg_context) *total_len += 4 + sizeof(struct smb2_preauth_neg_context)
+ sizeof(struct smb2_encryption_neg_context); + sizeof(struct smb2_encryption_neg_context);
} }
static void decode_preauth_context(struct smb2_preauth_neg_context *ctxt)
{
unsigned int len = le16_to_cpu(ctxt->DataLength);
/* If invalid preauth context warn but use what we requested, SHA-512 */
if (len < MIN_PREAUTH_CTXT_DATA_LEN) {
printk_once(KERN_WARNING "server sent bad preauth context\n");
return;
}
if (le16_to_cpu(ctxt->HashAlgorithmCount) != 1)
printk_once(KERN_WARNING "illegal SMB3 hash algorithm count\n");
if (ctxt->HashAlgorithms != SMB2_PREAUTH_INTEGRITY_SHA512)
printk_once(KERN_WARNING "unknown SMB3 hash algorithm\n");
}
static int decode_encrypt_ctx(struct TCP_Server_Info *server,
struct smb2_encryption_neg_context *ctxt)
{
unsigned int len = le16_to_cpu(ctxt->DataLength);
cifs_dbg(FYI, "decode SMB3.11 encryption neg context of len %d\n", len);
if (len < MIN_ENCRYPT_CTXT_DATA_LEN) {
printk_once(KERN_WARNING "server sent bad crypto ctxt len\n");
return -EINVAL;
}
if (le16_to_cpu(ctxt->CipherCount) != 1) {
printk_once(KERN_WARNING "illegal SMB3.11 cipher count\n");
return -EINVAL;
}
cifs_dbg(FYI, "SMB311 cipher type:%d\n", le16_to_cpu(ctxt->Ciphers[0]));
if ((ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES128_CCM) &&
(ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES128_GCM)) {
printk_once(KERN_WARNING "invalid SMB3.11 cipher returned\n");
return -EINVAL;
}
server->cipher_type = ctxt->Ciphers[0];
return 0;
}
static int smb311_decode_neg_context(struct smb2_negotiate_rsp *rsp,
struct TCP_Server_Info *server)
{
struct smb2_neg_context *pctx;
unsigned int offset = le32_to_cpu(rsp->NegotiateContextOffset);
unsigned int ctxt_cnt = le16_to_cpu(rsp->NegotiateContextCount);
unsigned int len_of_smb = be32_to_cpu(rsp->hdr.smb2_buf_length);
unsigned int len_of_ctxts, i;
int rc = 0;
cifs_dbg(FYI, "decoding %d negotiate contexts\n", ctxt_cnt);
if (len_of_smb <= offset) {
cifs_dbg(VFS, "Invalid response: negotiate context offset\n");
return -EINVAL;
}
len_of_ctxts = len_of_smb - offset;
for (i = 0; i < ctxt_cnt; i++) {
int clen;
/* check that offset is not beyond end of SMB */
if (len_of_ctxts == 0)
break;
if (len_of_ctxts < sizeof(struct smb2_neg_context))
break;
pctx = (struct smb2_neg_context *)(offset +
server->vals->header_preamble_size + (char *)rsp);
clen = le16_to_cpu(pctx->DataLength);
if (clen > len_of_ctxts)
break;
if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES)
decode_preauth_context(
(struct smb2_preauth_neg_context *)pctx);
else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES)
rc = decode_encrypt_ctx(server,
(struct smb2_encryption_neg_context *)pctx);
else
cifs_dbg(VFS, "unknown negcontext of type %d ignored\n",
le16_to_cpu(pctx->ContextType));
if (rc)
break;
/* offsets must be 8 byte aligned */
clen = (clen + 7) & ~0x7;
offset += clen + sizeof(struct smb2_neg_context);
len_of_ctxts -= clen;
}
return rc;
}
#else #else
static void assemble_neg_contexts(struct smb2_negotiate_req *req, static void assemble_neg_contexts(struct smb2_negotiate_req *req,
unsigned int *total_len) unsigned int *total_len)
...@@ -616,6 +713,15 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) ...@@ -616,6 +713,15 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
else if (rc == 0) else if (rc == 0)
rc = -EIO; rc = -EIO;
} }
#ifdef CONFIG_CIFS_SMB311
if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) {
if (rsp->NegotiateContextCount)
rc = smb311_decode_neg_context(rsp, server);
else
cifs_dbg(VFS, "Missing expected negotiate contexts\n");
}
#endif /* CONFIG_CIFS_SMB311 */
neg_exit: neg_exit:
free_rsp_buf(resp_buftype, rsp); free_rsp_buf(resp_buftype, rsp);
return rc; return rc;
...@@ -1026,7 +1132,7 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data) ...@@ -1026,7 +1132,7 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
if (rc) if (rc)
goto out; goto out;
if (offsetof(struct smb2_sess_setup_rsp, Buffer) - 4 != if (offsetof(struct smb2_sess_setup_rsp, Buffer) - ses->server->vals->header_preamble_size !=
le16_to_cpu(rsp->SecurityBufferOffset)) { le16_to_cpu(rsp->SecurityBufferOffset)) {
cifs_dbg(VFS, "Invalid security buffer offset %d\n", cifs_dbg(VFS, "Invalid security buffer offset %d\n",
le16_to_cpu(rsp->SecurityBufferOffset)); le16_to_cpu(rsp->SecurityBufferOffset));
...@@ -1701,7 +1807,7 @@ alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len, ...@@ -1701,7 +1807,7 @@ alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len,
int int
SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
__u8 *oplock, struct smb2_file_all_info *buf, __u8 *oplock, struct smb2_file_all_info *buf,
struct smb2_err_rsp **err_buf) struct kvec *err_iov)
{ {
struct smb2_create_req *req; struct smb2_create_req *req;
struct smb2_create_rsp *rsp; struct smb2_create_rsp *rsp;
...@@ -1841,9 +1947,11 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, ...@@ -1841,9 +1947,11 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
if (rc != 0) { if (rc != 0) {
cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); cifs_stats_fail_inc(tcon, SMB2_CREATE_HE);
if (err_buf && rsp) if (err_iov && rsp) {
*err_buf = kmemdup(rsp, get_rfc1002_length(rsp) + 4, *err_iov = rsp_iov;
GFP_KERNEL); resp_buftype = CIFS_NO_BUFFER;
rsp = NULL;
}
goto creat_exit; goto creat_exit;
} }
...@@ -2098,13 +2206,13 @@ SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -2098,13 +2206,13 @@ SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
} }
static int static int
validate_buf(unsigned int offset, unsigned int buffer_length, validate_iov(struct TCP_Server_Info *server,
struct smb2_hdr *hdr, unsigned int min_buf_size) unsigned int offset, unsigned int buffer_length,
struct kvec *iov, unsigned int min_buf_size)
{ {
unsigned int smb_len = be32_to_cpu(hdr->smb2_buf_length); unsigned int smb_len = iov->iov_len;
char *end_of_smb = smb_len + 4 /* RFC1001 length field */ + (char *)hdr; char *end_of_smb = smb_len + server->vals->header_preamble_size + (char *)iov->iov_base;
char *begin_of_buf = 4 /* RFC1001 len field */ + offset + (char *)hdr; char *begin_of_buf = server->vals->header_preamble_size + offset + (char *)iov->iov_base;
char *end_of_buf = begin_of_buf + buffer_length; char *end_of_buf = begin_of_buf + buffer_length;
...@@ -2134,18 +2242,18 @@ validate_buf(unsigned int offset, unsigned int buffer_length, ...@@ -2134,18 +2242,18 @@ validate_buf(unsigned int offset, unsigned int buffer_length,
* Caller must free buffer. * Caller must free buffer.
*/ */
static int static int
validate_and_copy_buf(unsigned int offset, unsigned int buffer_length, validate_and_copy_iov(struct TCP_Server_Info *server,
struct smb2_hdr *hdr, unsigned int minbufsize, unsigned int offset, unsigned int buffer_length,
struct kvec *iov, unsigned int minbufsize,
char *data) char *data)
{ {
char *begin_of_buf = 4 /* RFC1001 len field */ + offset + (char *)hdr; char *begin_of_buf = server->vals->header_preamble_size + offset + (char *)(iov->iov_base);
int rc; int rc;
if (!data) if (!data)
return -EINVAL; return -EINVAL;
rc = validate_buf(offset, buffer_length, hdr, minbufsize); rc = validate_iov(server, offset, buffer_length, iov, minbufsize);
if (rc) if (rc)
return rc; return rc;
...@@ -2223,9 +2331,10 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -2223,9 +2331,10 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
} }
} }
rc = validate_and_copy_buf(le16_to_cpu(rsp->OutputBufferOffset), rc = validate_and_copy_iov(ses->server,
le16_to_cpu(rsp->OutputBufferOffset),
le32_to_cpu(rsp->OutputBufferLength), le32_to_cpu(rsp->OutputBufferLength),
&rsp->hdr, min_len, *data); &rsp_iov, min_len, *data);
qinf_exit: qinf_exit:
free_rsp_buf(resp_buftype, rsp); free_rsp_buf(resp_buftype, rsp);
...@@ -3146,8 +3255,9 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -3146,8 +3255,9 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
goto qdir_exit; goto qdir_exit;
} }
rc = validate_buf(le16_to_cpu(rsp->OutputBufferOffset), rc = validate_iov(server,
le32_to_cpu(rsp->OutputBufferLength), &rsp->hdr, le16_to_cpu(rsp->OutputBufferOffset),
le32_to_cpu(rsp->OutputBufferLength), &rsp_iov,
info_buf_size); info_buf_size);
if (rc) if (rc)
goto qdir_exit; goto qdir_exit;
...@@ -3454,7 +3564,7 @@ static int ...@@ -3454,7 +3564,7 @@ static int
build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, int level, build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, int level,
int outbuf_len, u64 persistent_fid, u64 volatile_fid) int outbuf_len, u64 persistent_fid, u64 volatile_fid)
{ {
struct TCP_Server_Info *server = tcon->ses->server; struct TCP_Server_Info *server;
int rc; int rc;
struct smb2_query_info_req *req; struct smb2_query_info_req *req;
unsigned int total_len; unsigned int total_len;
...@@ -3464,6 +3574,8 @@ build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, int level, ...@@ -3464,6 +3574,8 @@ build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, int level,
if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
return -EIO; return -EIO;
server = tcon->ses->server;
rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, (void **) &req, rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, (void **) &req,
&total_len); &total_len);
if (rc) if (rc)
...@@ -3517,8 +3629,9 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -3517,8 +3629,9 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
info = (struct smb2_fs_full_size_info *)(server->vals->header_preamble_size + info = (struct smb2_fs_full_size_info *)(server->vals->header_preamble_size +
le16_to_cpu(rsp->OutputBufferOffset) + (char *)&rsp->hdr); le16_to_cpu(rsp->OutputBufferOffset) + (char *)&rsp->hdr);
rc = validate_buf(le16_to_cpu(rsp->OutputBufferOffset), rc = validate_iov(server,
le32_to_cpu(rsp->OutputBufferLength), &rsp->hdr, le16_to_cpu(rsp->OutputBufferOffset),
le32_to_cpu(rsp->OutputBufferLength), &rsp_iov,
sizeof(struct smb2_fs_full_size_info)); sizeof(struct smb2_fs_full_size_info));
if (!rc) if (!rc)
copy_fs_info_to_kstatfs(info, fsdata); copy_fs_info_to_kstatfs(info, fsdata);
...@@ -3574,7 +3687,7 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -3574,7 +3687,7 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
rsp_len = le32_to_cpu(rsp->OutputBufferLength); rsp_len = le32_to_cpu(rsp->OutputBufferLength);
offset = le16_to_cpu(rsp->OutputBufferOffset); offset = le16_to_cpu(rsp->OutputBufferOffset);
rc = validate_buf(offset, rsp_len, &rsp->hdr, min_len); rc = validate_iov(server, offset, rsp_len, &rsp_iov, min_len);
if (rc) if (rc)
goto qfsattr_exit; goto qfsattr_exit;
......
...@@ -263,11 +263,19 @@ struct smb2_negotiate_req { ...@@ -263,11 +263,19 @@ struct smb2_negotiate_req {
#define SMB2_NT_FIND 0x00100000 #define SMB2_NT_FIND 0x00100000
#define SMB2_LARGE_FILES 0x00200000 #define SMB2_LARGE_FILES 0x00200000
struct smb2_neg_context {
__le16 ContextType;
__le16 DataLength;
__le32 Reserved;
/* Followed by array of data */
} __packed;
#define SMB311_SALT_SIZE 32 #define SMB311_SALT_SIZE 32
/* Hash Algorithm Types */ /* Hash Algorithm Types */
#define SMB2_PREAUTH_INTEGRITY_SHA512 cpu_to_le16(0x0001) #define SMB2_PREAUTH_INTEGRITY_SHA512 cpu_to_le16(0x0001)
#define SMB2_PREAUTH_HASH_SIZE 64 #define SMB2_PREAUTH_HASH_SIZE 64
#define MIN_PREAUTH_CTXT_DATA_LEN (SMB311_SALT_SIZE + 6)
struct smb2_preauth_neg_context { struct smb2_preauth_neg_context {
__le16 ContextType; /* 1 */ __le16 ContextType; /* 1 */
__le16 DataLength; __le16 DataLength;
...@@ -282,6 +290,8 @@ struct smb2_preauth_neg_context { ...@@ -282,6 +290,8 @@ struct smb2_preauth_neg_context {
#define SMB2_ENCRYPTION_AES128_CCM cpu_to_le16(0x0001) #define SMB2_ENCRYPTION_AES128_CCM cpu_to_le16(0x0001)
#define SMB2_ENCRYPTION_AES128_GCM cpu_to_le16(0x0002) #define SMB2_ENCRYPTION_AES128_GCM cpu_to_le16(0x0002)
/* Min encrypt context data is one cipher so 2 bytes + 2 byte count field */
#define MIN_ENCRYPT_CTXT_DATA_LEN 4
struct smb2_encryption_neg_context { struct smb2_encryption_neg_context {
__le16 ContextType; /* 2 */ __le16 ContextType; /* 2 */
__le16 DataLength; __le16 DataLength;
......
...@@ -122,7 +122,7 @@ extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon); ...@@ -122,7 +122,7 @@ extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon);
extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms,
__le16 *path, __u8 *oplock, __le16 *path, __u8 *oplock,
struct smb2_file_all_info *buf, struct smb2_file_all_info *buf,
struct smb2_err_rsp **err_buf); struct kvec *err_iov);
extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, u32 opcode, u64 persistent_fid, u64 volatile_fid, u32 opcode,
bool is_fsctl, char *in_data, u32 indatalen, bool is_fsctl, char *in_data, u32 indatalen,
......
...@@ -604,7 +604,7 @@ int ...@@ -604,7 +604,7 @@ int
smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
bool log_error) bool log_error)
{ {
unsigned int len = get_rfc1002_length(mid->resp_buf); unsigned int len = mid->resp_buf_size;
struct kvec iov[2]; struct kvec iov[2];
struct smb_rqst rqst = { .rq_iov = iov, struct smb_rqst rqst = { .rq_iov = iov,
.rq_nvec = 2 }; .rq_nvec = 2 };
......
...@@ -790,7 +790,7 @@ cifs_send_recv(const unsigned int xid, struct cifs_ses *ses, ...@@ -790,7 +790,7 @@ cifs_send_recv(const unsigned int xid, struct cifs_ses *ses,
buf = (char *)midQ->resp_buf; buf = (char *)midQ->resp_buf;
resp_iov->iov_base = buf; resp_iov->iov_base = buf;
resp_iov->iov_len = get_rfc1002_length(buf) + resp_iov->iov_len = midQ->resp_buf_size +
ses->server->vals->header_preamble_size; ses->server->vals->header_preamble_size;
if (midQ->large_buf) if (midQ->large_buf)
*resp_buf_type = CIFS_LARGE_BUFFER; *resp_buf_type = CIFS_LARGE_BUFFER;
......
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