|
@@ -268,8 +268,11 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
|
|
|
mutex_unlock(&tcon->ses->session_mutex);
|
|
|
|
|
|
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;
|
|
|
+ }
|
|
|
|
|
|
if (smb2_command != SMB2_INTERNAL_CMD)
|
|
|
queue_delayed_work(cifsiod_wq, &server->reconnect, 0);
|
|
@@ -403,6 +406,100 @@ assemble_neg_contexts(struct smb2_negotiate_req *req,
|
|
|
*total_len += 4 + sizeof(struct smb2_preauth_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
|
|
|
static void assemble_neg_contexts(struct smb2_negotiate_req *req,
|
|
|
unsigned int *total_len)
|
|
@@ -616,6 +713,15 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
|
|
|
else if (rc == 0)
|
|
|
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:
|
|
|
free_rsp_buf(resp_buftype, rsp);
|
|
|
return rc;
|
|
@@ -1026,7 +1132,7 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
|
|
|
if (rc)
|
|
|
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)) {
|
|
|
cifs_dbg(VFS, "Invalid security buffer offset %d\n",
|
|
|
le16_to_cpu(rsp->SecurityBufferOffset));
|
|
@@ -1701,7 +1807,7 @@ alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len,
|
|
|
int
|
|
|
SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
|
|
|
__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_rsp *rsp;
|
|
@@ -1841,9 +1947,11 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
|
|
|
|
|
|
if (rc != 0) {
|
|
|
cifs_stats_fail_inc(tcon, SMB2_CREATE_HE);
|
|
|
- if (err_buf && rsp)
|
|
|
- *err_buf = kmemdup(rsp, get_rfc1002_length(rsp) + 4,
|
|
|
- GFP_KERNEL);
|
|
|
+ if (err_iov && rsp) {
|
|
|
+ *err_iov = rsp_iov;
|
|
|
+ resp_buftype = CIFS_NO_BUFFER;
|
|
|
+ rsp = NULL;
|
|
|
+ }
|
|
|
goto creat_exit;
|
|
|
}
|
|
|
|
|
@@ -2098,13 +2206,13 @@ close_exit:
|
|
|
}
|
|
|
|
|
|
static int
|
|
|
-validate_buf(unsigned int offset, unsigned int buffer_length,
|
|
|
- struct smb2_hdr *hdr, unsigned int min_buf_size)
|
|
|
-
|
|
|
+validate_iov(struct TCP_Server_Info *server,
|
|
|
+ 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);
|
|
|
- char *end_of_smb = smb_len + 4 /* RFC1001 length field */ + (char *)hdr;
|
|
|
- char *begin_of_buf = 4 /* RFC1001 len field */ + offset + (char *)hdr;
|
|
|
+ unsigned int smb_len = iov->iov_len;
|
|
|
+ char *end_of_smb = smb_len + server->vals->header_preamble_size + (char *)iov->iov_base;
|
|
|
+ char *begin_of_buf = server->vals->header_preamble_size + offset + (char *)iov->iov_base;
|
|
|
char *end_of_buf = begin_of_buf + buffer_length;
|
|
|
|
|
|
|
|
@@ -2134,18 +2242,18 @@ validate_buf(unsigned int offset, unsigned int buffer_length,
|
|
|
* Caller must free buffer.
|
|
|
*/
|
|
|
static int
|
|
|
-validate_and_copy_buf(unsigned int offset, unsigned int buffer_length,
|
|
|
- struct smb2_hdr *hdr, unsigned int minbufsize,
|
|
|
+validate_and_copy_iov(struct TCP_Server_Info *server,
|
|
|
+ unsigned int offset, unsigned int buffer_length,
|
|
|
+ struct kvec *iov, unsigned int minbufsize,
|
|
|
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;
|
|
|
|
|
|
if (!data)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- rc = validate_buf(offset, buffer_length, hdr, minbufsize);
|
|
|
+ rc = validate_iov(server, offset, buffer_length, iov, minbufsize);
|
|
|
if (rc)
|
|
|
return rc;
|
|
|
|
|
@@ -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),
|
|
|
- &rsp->hdr, min_len, *data);
|
|
|
+ &rsp_iov, min_len, *data);
|
|
|
|
|
|
qinf_exit:
|
|
|
free_rsp_buf(resp_buftype, rsp);
|
|
@@ -3146,8 +3255,9 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
|
|
|
goto qdir_exit;
|
|
|
}
|
|
|
|
|
|
- rc = validate_buf(le16_to_cpu(rsp->OutputBufferOffset),
|
|
|
- le32_to_cpu(rsp->OutputBufferLength), &rsp->hdr,
|
|
|
+ rc = validate_iov(server,
|
|
|
+ le16_to_cpu(rsp->OutputBufferOffset),
|
|
|
+ le32_to_cpu(rsp->OutputBufferLength), &rsp_iov,
|
|
|
info_buf_size);
|
|
|
if (rc)
|
|
|
goto qdir_exit;
|
|
@@ -3454,7 +3564,7 @@ static int
|
|
|
build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, int level,
|
|
|
int outbuf_len, u64 persistent_fid, u64 volatile_fid)
|
|
|
{
|
|
|
- struct TCP_Server_Info *server = tcon->ses->server;
|
|
|
+ struct TCP_Server_Info *server;
|
|
|
int rc;
|
|
|
struct smb2_query_info_req *req;
|
|
|
unsigned int total_len;
|
|
@@ -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))
|
|
|
return -EIO;
|
|
|
|
|
|
+ server = tcon->ses->server;
|
|
|
+
|
|
|
rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, (void **) &req,
|
|
|
&total_len);
|
|
|
if (rc)
|
|
@@ -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 +
|
|
|
le16_to_cpu(rsp->OutputBufferOffset) + (char *)&rsp->hdr);
|
|
|
- rc = validate_buf(le16_to_cpu(rsp->OutputBufferOffset),
|
|
|
- le32_to_cpu(rsp->OutputBufferLength), &rsp->hdr,
|
|
|
+ rc = validate_iov(server,
|
|
|
+ le16_to_cpu(rsp->OutputBufferOffset),
|
|
|
+ le32_to_cpu(rsp->OutputBufferLength), &rsp_iov,
|
|
|
sizeof(struct smb2_fs_full_size_info));
|
|
|
if (!rc)
|
|
|
copy_fs_info_to_kstatfs(info, fsdata);
|
|
@@ -3574,7 +3687,7 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
|
|
|
|
|
|
rsp_len = le32_to_cpu(rsp->OutputBufferLength);
|
|
|
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)
|
|
|
goto qfsattr_exit;
|
|
|
|