summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-04-04 14:09:27 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2018-04-04 14:09:27 -0700
commita8f8e8ac766ddb8702ef9baf01b7ae4f8d3a940c (patch)
treec1fb03d543dc85acf972e245eaaa39a23cdffcbd /fs
parent2bd99df54f43b659ddaab8922adbaf3bcf3753ed (diff)
parent07108d0e7c7fbbf9c6d76c0af2f1813e4f3f0800 (diff)
downloadlinux-a8f8e8ac766ddb8702ef9baf01b7ae4f8d3a940c.tar.gz
linux-a8f8e8ac766ddb8702ef9baf01b7ae4f8d3a940c.tar.bz2
linux-a8f8e8ac766ddb8702ef9baf01b7ae4f8d3a940c.zip
Merge tag '4.17-SMB3-Fixes' of git://git.samba.org/sfrench/cifs-2.6
Pull cifs updates from Steve French: "Includes SMB3.11 security improvements, as well as various fixes for stable and some debugging improvements" * tag '4.17-SMB3-Fixes' of git://git.samba.org/sfrench/cifs-2.6: cifs: Add minor debug message during negprot smb3: Fix root directory when server returns inode number of zero cifs: fix sparse warning on previous patch in a few printks cifs: add server->vals->header_preamble_size cifs: smbd: disconnect transport on RDMA errors cifs: smbd: avoid reconnect lockup Don't log confusing message on reconnect by default Don't log expected error on DFS referral request fs: cifs: Replace _free_xid call in cifs_root_iget function SMB3.1.1 dialect is no longer experimental Tree connect for SMB3.1.1 must be signed for non-encrypted shares fix smb3-encryption breakage when CONFIG_DEBUG_SG=y CIFS: fix sha512 check in cifs_crypto_secmech_release CIFS: implement v3.11 preauth integrity CIFS: add sha512 secmech CIFS: refactor crypto shash/sdesc allocation&free Update README file for cifs.ko Update TODO list for cifs.ko cifs: fix memory leak in SMB2_open() CIFS: SMBD: fix spelling mistake: "faield" and "legnth"
Diffstat (limited to 'fs')
-rw-r--r--fs/cifs/Kconfig8
-rw-r--r--fs/cifs/cifsencrypt.c85
-rw-r--r--fs/cifs/cifsfs.c1
-rw-r--r--fs/cifs/cifsglob.h9
-rw-r--r--fs/cifs/cifsproto.h5
-rw-r--r--fs/cifs/cifssmb.c6
-rw-r--r--fs/cifs/connect.c18
-rw-r--r--fs/cifs/inode.c38
-rw-r--r--fs/cifs/link.c27
-rw-r--r--fs/cifs/misc.c54
-rw-r--r--fs/cifs/smb1ops.c1
-rw-r--r--fs/cifs/smb2maperror.c2
-rw-r--r--fs/cifs/smb2misc.c89
-rw-r--r--fs/cifs/smb2ops.c76
-rw-r--r--fs/cifs/smb2pdu.c57
-rw-r--r--fs/cifs/smb2pdu.h3
-rw-r--r--fs/cifs/smb2proto.h5
-rw-r--r--fs/cifs/smb2transport.c97
-rw-r--r--fs/cifs/smbdirect.c23
-rw-r--r--fs/cifs/smbencrypt.c27
-rw-r--r--fs/cifs/transport.c20
21 files changed, 408 insertions, 243 deletions
diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig
index 687da62daf4e..741749a98614 100644
--- a/fs/cifs/Kconfig
+++ b/fs/cifs/Kconfig
@@ -187,13 +187,13 @@ config CIFS_NFSD_EXPORT
Allows NFS server to export a CIFS mounted share (nfsd over cifs)
config CIFS_SMB311
- bool "SMB3.1.1 network file system support (Experimental)"
+ bool "SMB3.1.1 network file system support"
depends on CIFS
+ select CRYPTO_SHA512
help
- This enables experimental support for the newest, SMB3.1.1, dialect.
- This dialect includes improved security negotiation features.
- If unsure, say N
+ This enables support for the newest, and most secure dialect, SMB3.11.
+ If unsure, say Y
config CIFS_SMB_DIRECT
bool "SMB Direct support (Experimental)"
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
index f2b0a7f124da..a6ef088e057b 100644
--- a/fs/cifs/cifsencrypt.c
+++ b/fs/cifs/cifsencrypt.c
@@ -36,37 +36,6 @@
#include <crypto/skcipher.h>
#include <crypto/aead.h>
-static int
-cifs_crypto_shash_md5_allocate(struct TCP_Server_Info *server)
-{
- int rc;
- unsigned int size;
-
- if (server->secmech.sdescmd5 != NULL)
- return 0; /* already allocated */
-
- server->secmech.md5 = crypto_alloc_shash("md5", 0, 0);
- if (IS_ERR(server->secmech.md5)) {
- cifs_dbg(VFS, "could not allocate crypto md5\n");
- rc = PTR_ERR(server->secmech.md5);
- server->secmech.md5 = NULL;
- return rc;
- }
-
- size = sizeof(struct shash_desc) +
- crypto_shash_descsize(server->secmech.md5);
- server->secmech.sdescmd5 = kmalloc(size, GFP_KERNEL);
- if (!server->secmech.sdescmd5) {
- crypto_free_shash(server->secmech.md5);
- server->secmech.md5 = NULL;
- return -ENOMEM;
- }
- server->secmech.sdescmd5->shash.tfm = server->secmech.md5;
- server->secmech.sdescmd5->shash.flags = 0x0;
-
- return 0;
-}
-
int __cifs_calc_signature(struct smb_rqst *rqst,
struct TCP_Server_Info *server, char *signature,
struct shash_desc *shash)
@@ -132,13 +101,10 @@ static int cifs_calc_signature(struct smb_rqst *rqst,
if (!rqst->rq_iov || !signature || !server)
return -EINVAL;
- if (!server->secmech.sdescmd5) {
- rc = cifs_crypto_shash_md5_allocate(server);
- if (rc) {
- cifs_dbg(VFS, "%s: Can't alloc md5 crypto\n", __func__);
- return -1;
- }
- }
+ rc = cifs_alloc_hash("md5", &server->secmech.md5,
+ &server->secmech.sdescmd5);
+ if (rc)
+ return -1;
rc = crypto_shash_init(&server->secmech.sdescmd5->shash);
if (rc) {
@@ -663,37 +629,6 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)
return rc;
}
-static int crypto_hmacmd5_alloc(struct TCP_Server_Info *server)
-{
- int rc;
- unsigned int size;
-
- /* check if already allocated */
- if (server->secmech.sdeschmacmd5)
- return 0;
-
- server->secmech.hmacmd5 = crypto_alloc_shash("hmac(md5)", 0, 0);
- if (IS_ERR(server->secmech.hmacmd5)) {
- cifs_dbg(VFS, "could not allocate crypto hmacmd5\n");
- rc = PTR_ERR(server->secmech.hmacmd5);
- server->secmech.hmacmd5 = NULL;
- return rc;
- }
-
- size = sizeof(struct shash_desc) +
- crypto_shash_descsize(server->secmech.hmacmd5);
- server->secmech.sdeschmacmd5 = kmalloc(size, GFP_KERNEL);
- if (!server->secmech.sdeschmacmd5) {
- crypto_free_shash(server->secmech.hmacmd5);
- server->secmech.hmacmd5 = NULL;
- return -ENOMEM;
- }
- server->secmech.sdeschmacmd5->shash.tfm = server->secmech.hmacmd5;
- server->secmech.sdeschmacmd5->shash.flags = 0x0;
-
- return 0;
-}
-
int
setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
{
@@ -757,9 +692,10 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
mutex_lock(&ses->server->srv_mutex);
- rc = crypto_hmacmd5_alloc(ses->server);
+ rc = cifs_alloc_hash("hmac(md5)",
+ &ses->server->secmech.hmacmd5,
+ &ses->server->secmech.sdeschmacmd5);
if (rc) {
- cifs_dbg(VFS, "could not crypto alloc hmacmd5 rc %d\n", rc);
goto unlock;
}
@@ -893,6 +829,11 @@ cifs_crypto_secmech_release(struct TCP_Server_Info *server)
server->secmech.md5 = NULL;
}
+ if (server->secmech.sha512) {
+ crypto_free_shash(server->secmech.sha512);
+ server->secmech.sha512 = NULL;
+ }
+
if (server->secmech.hmacmd5) {
crypto_free_shash(server->secmech.hmacmd5);
server->secmech.hmacmd5 = NULL;
@@ -916,4 +857,6 @@ cifs_crypto_secmech_release(struct TCP_Server_Info *server)
server->secmech.sdeschmacmd5 = NULL;
kfree(server->secmech.sdescmd5);
server->secmech.sdescmd5 = NULL;
+ kfree(server->secmech.sdescsha512);
+ server->secmech.sdescsha512 = NULL;
}
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 32cdea67bbfd..f715609b13f3 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -1486,6 +1486,7 @@ MODULE_SOFTDEP("pre: nls");
MODULE_SOFTDEP("pre: aes");
MODULE_SOFTDEP("pre: cmac");
MODULE_SOFTDEP("pre: sha256");
+MODULE_SOFTDEP("pre: sha512");
MODULE_SOFTDEP("pre: aead2");
MODULE_SOFTDEP("pre: ccm");
module_init(init_cifs)
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 48f7c197cd2d..2282562e78a1 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -130,10 +130,12 @@ struct cifs_secmech {
struct crypto_shash *md5; /* md5 hash function */
struct crypto_shash *hmacsha256; /* hmac-sha256 hash function */
struct crypto_shash *cmacaes; /* block-cipher based MAC function */
+ struct crypto_shash *sha512; /* sha512 hash function */
struct sdesc *sdeschmacmd5; /* ctxt to generate ntlmv2 hash, CR1 */
struct sdesc *sdescmd5; /* ctxt to generate cifs/smb signature */
struct sdesc *sdeschmacsha256; /* ctxt to generate smb2 signature */
struct sdesc *sdesccmacaes; /* ctxt to generate smb3 signature */
+ struct sdesc *sdescsha512; /* ctxt to generate smb3.11 signing key */
struct crypto_aead *ccmaesencrypt; /* smb3 encryption aead */
struct crypto_aead *ccmaesdecrypt; /* smb3 decryption aead */
};
@@ -466,6 +468,7 @@ struct smb_version_values {
__u32 exclusive_lock_type;
__u32 shared_lock_type;
__u32 unlock_lock_type;
+ size_t header_preamble_size;
size_t header_size;
size_t max_header_size;
size_t read_rsp_size;
@@ -673,7 +676,8 @@ struct TCP_Server_Info {
unsigned int max_read;
unsigned int max_write;
#ifdef CONFIG_CIFS_SMB311
- __u8 preauth_sha_hash[64]; /* save initital negprot hash */
+ /* save initital negprot hash */
+ __u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
#endif /* 3.1.1 */
struct delayed_work reconnect; /* reconnect workqueue job */
struct mutex reconnect_mutex; /* prevent simultaneous reconnects */
@@ -862,7 +866,7 @@ struct cifs_ses {
__u8 smb3encryptionkey[SMB3_SIGN_KEY_SIZE];
__u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE];
#ifdef CONFIG_CIFS_SMB311
- __u8 preauth_sha_hash[64];
+ __u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
#endif /* 3.1.1 */
};
@@ -1466,6 +1470,7 @@ struct dfs_info3_param {
#define CIFS_FATTR_NEED_REVAL 0x4
#define CIFS_FATTR_INO_COLLISION 0x8
#define CIFS_FATTR_UNKNOWN_NLINK 0x10
+#define CIFS_FATTR_FAKE_ROOT_INO 0x20
struct cifs_fattr {
u32 cf_flags;
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 93d565186698..365a414a75e9 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -542,4 +542,9 @@ enum securityEnum cifs_select_sectype(struct TCP_Server_Info *,
struct cifs_aio_ctx *cifs_aio_ctx_alloc(void);
void cifs_aio_ctx_release(struct kref *refcount);
int setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw);
+
+int cifs_alloc_hash(const char *name, struct crypto_shash **shash,
+ struct sdesc **sdesc);
+void cifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc);
+
#endif /* _CIFSPROTO_H */
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 9ceebf30eb22..59c09a596c0a 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -1454,7 +1454,8 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
unsigned int data_offset, data_len;
struct cifs_readdata *rdata = mid->callback_data;
char *buf = server->smallbuf;
- unsigned int buflen = get_rfc1002_length(buf) + 4;
+ unsigned int buflen = get_rfc1002_length(buf) +
+ server->vals->header_preamble_size;
bool use_rdma_mr = false;
cifs_dbg(FYI, "%s: mid=%llu offset=%llu bytes=%u\n",
@@ -1504,7 +1505,8 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
return cifs_readv_discard(server, mid);
}
- data_offset = server->ops->read_data_offset(buf) + 4;
+ data_offset = server->ops->read_data_offset(buf) +
+ server->vals->header_preamble_size;
if (data_offset < server->total_read) {
/*
* win2k8 sometimes sends an offset of 0 when the read
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index a726f524fb84..4e0808f40195 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -775,7 +775,8 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
unsigned int pdu_length = get_rfc1002_length(buf);
/* make sure this will fit in a large buffer */
- if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) - 4) {
+ if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) -
+ server->vals->header_preamble_size) {
cifs_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length);
cifs_reconnect(server);
wake_up(&server->response_q);
@@ -791,7 +792,9 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
/* now read the rest */
length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1,
- pdu_length - HEADER_SIZE(server) + 1 + 4);
+ pdu_length - HEADER_SIZE(server) + 1
+ + server->vals->header_preamble_size);
+
if (length < 0)
return length;
server->total_read += length;
@@ -884,7 +887,8 @@ cifs_demultiplex_thread(void *p)
continue;
/* make sure we have enough to get to the MID */
- if (pdu_length < HEADER_SIZE(server) - 1 - 4) {
+ if (pdu_length < HEADER_SIZE(server) - 1 -
+ server->vals->header_preamble_size) {
cifs_dbg(VFS, "SMB response too short (%u bytes)\n",
pdu_length);
cifs_reconnect(server);
@@ -893,8 +897,10 @@ cifs_demultiplex_thread(void *p)
}
/* read down to the MID */
- length = cifs_read_from_socket(server, buf + 4,
- HEADER_SIZE(server) - 1 - 4);
+ length = cifs_read_from_socket(server,
+ buf + server->vals->header_preamble_size,
+ HEADER_SIZE(server) - 1
+ - server->vals->header_preamble_size);
if (length < 0)
continue;
server->total_read += length;
@@ -4306,7 +4312,7 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
server->sec_mode, server->capabilities, server->timeAdj);
if (ses->auth_key.response) {
- cifs_dbg(VFS, "Free previous auth_key.response = %p\n",
+ cifs_dbg(FYI, "Free previous auth_key.response = %p\n",
ses->auth_key.response);
kfree(ses->auth_key.response);
ses->auth_key.response = NULL;
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 8f9a8cc7cc62..f856df4adae3 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -707,6 +707,18 @@ cgfi_exit:
return rc;
}
+/* Simple function to return a 64 bit hash of string. Rarely called */
+static __u64 simple_hashstr(const char *str)
+{
+ const __u64 hash_mult = 1125899906842597L; /* a big enough prime */
+ __u64 hash = 0;
+
+ while (*str)
+ hash = (hash + (__u64) *str++) * hash_mult;
+
+ return hash;
+}
+
int
cifs_get_inode_info(struct inode **inode, const char *full_path,
FILE_ALL_INFO *data, struct super_block *sb, int xid,
@@ -816,6 +828,14 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
tmprc);
fattr.cf_uniqueid = iunique(sb, ROOT_I);
cifs_autodisable_serverino(cifs_sb);
+ } else if ((fattr.cf_uniqueid == 0) &&
+ strlen(full_path) == 0) {
+ /* some servers ret bad root ino ie 0 */
+ cifs_dbg(FYI, "Invalid (0) inodenum\n");
+ fattr.cf_flags |=
+ CIFS_FATTR_FAKE_ROOT_INO;
+ fattr.cf_uniqueid =
+ simple_hashstr(tcon->treeName);
}
}
} else
@@ -832,6 +852,16 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
&fattr.cf_uniqueid, data);
if (tmprc)
fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid;
+ else if ((fattr.cf_uniqueid == 0) &&
+ strlen(full_path) == 0) {
+ /*
+ * Reuse existing root inode num since
+ * inum zero for root causes ls of . and .. to
+ * not be returned
+ */
+ cifs_dbg(FYI, "Srv ret 0 inode num for root\n");
+ fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid;
+ }
} else
fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid;
}
@@ -893,6 +923,9 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
}
cgii_exit:
+ if ((*inode) && ((*inode)->i_ino == 0))
+ cifs_dbg(FYI, "inode number of zero returned\n");
+
kfree(buf);
cifs_put_tlink(tlink);
return rc;
@@ -1066,10 +1099,7 @@ iget_no_retry:
out:
kfree(path);
- /* can not call macro free_xid here since in a void func
- * TODO: This is no longer true
- */
- _free_xid(xid);
+ free_xid(xid);
return inode;
}
diff --git a/fs/cifs/link.c b/fs/cifs/link.c
index 60b5a11ee11b..889a840172eb 100644
--- a/fs/cifs/link.c
+++ b/fs/cifs/link.c
@@ -50,25 +50,12 @@ static int
symlink_hash(unsigned int link_len, const char *link_str, u8 *md5_hash)
{
int rc;
- unsigned int size;
- struct crypto_shash *md5;
- struct sdesc *sdescmd5;
-
- md5 = crypto_alloc_shash("md5", 0, 0);
- if (IS_ERR(md5)) {
- rc = PTR_ERR(md5);
- cifs_dbg(VFS, "%s: Crypto md5 allocation error %d\n",
- __func__, rc);
- return rc;
- }
- size = sizeof(struct shash_desc) + crypto_shash_descsize(md5);
- sdescmd5 = kmalloc(size, GFP_KERNEL);
- if (!sdescmd5) {
- rc = -ENOMEM;
+ struct crypto_shash *md5 = NULL;
+ struct sdesc *sdescmd5 = NULL;
+
+ rc = cifs_alloc_hash("md5", &md5, &sdescmd5);
+ if (rc)
goto symlink_hash_err;
- }
- sdescmd5->shash.tfm = md5;
- sdescmd5->shash.flags = 0x0;
rc = crypto_shash_init(&sdescmd5->shash);
if (rc) {
@@ -85,9 +72,7 @@ symlink_hash(unsigned int link_len, const char *link_str, u8 *md5_hash)
cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);
symlink_hash_err:
- crypto_free_shash(md5);
- kfree(sdescmd5);
-
+ cifs_free_hash(&md5, &sdescmd5);
return rc;
}
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index a0dbced4a45c..460084a8eac5 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -848,3 +848,57 @@ setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw)
iov_iter_bvec(&ctx->iter, ITER_BVEC | rw, ctx->bv, npages, ctx->len);
return 0;
}
+
+/**
+ * cifs_alloc_hash - allocate hash and hash context together
+ *
+ * The caller has to make sure @sdesc is initialized to either NULL or
+ * a valid context. Both can be freed via cifs_free_hash().
+ */
+int
+cifs_alloc_hash(const char *name,
+ struct crypto_shash **shash, struct sdesc **sdesc)
+{
+ int rc = 0;
+ size_t size;
+
+ if (*sdesc != NULL)
+ return 0;
+
+ *shash = crypto_alloc_shash(name, 0, 0);
+ if (IS_ERR(*shash)) {
+ cifs_dbg(VFS, "could not allocate crypto %s\n", name);
+ rc = PTR_ERR(*shash);
+ *shash = NULL;
+ *sdesc = NULL;
+ return rc;
+ }
+
+ size = sizeof(struct shash_desc) + crypto_shash_descsize(*shash);
+ *sdesc = kmalloc(size, GFP_KERNEL);
+ if (*sdesc == NULL) {
+ cifs_dbg(VFS, "no memory left to allocate crypto %s\n", name);
+ crypto_free_shash(*shash);
+ *shash = NULL;
+ return -ENOMEM;
+ }
+
+ (*sdesc)->shash.tfm = *shash;
+ (*sdesc)->shash.flags = 0x0;
+ return 0;
+}
+
+/**
+ * cifs_free_hash - free hash and hash context together
+ *
+ * Freeing a NULL hash or context is safe.
+ */
+void
+cifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc)
+{
+ kfree(*sdesc);
+ *sdesc = NULL;
+ if (*shash)
+ crypto_free_shash(*shash);
+ *shash = NULL;
+}
diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c
index 3d495e440c87..aff8ce8ba34d 100644
--- a/fs/cifs/smb1ops.c
+++ b/fs/cifs/smb1ops.c
@@ -1122,6 +1122,7 @@ struct smb_version_values smb1_values = {
.exclusive_lock_type = 0,
.shared_lock_type = LOCKING_ANDX_SHARED_LOCK,
.unlock_lock_type = 0,
+ .header_preamble_size = 4,
.header_size = sizeof(struct smb_hdr),
.max_header_size = MAX_CIFS_HDR_SIZE,
.read_rsp_size = sizeof(READ_RSP),
diff --git a/fs/cifs/smb2maperror.c b/fs/cifs/smb2maperror.c
index 62c88dfed57b..3bfc9c990724 100644
--- a/fs/cifs/smb2maperror.c
+++ b/fs/cifs/smb2maperror.c
@@ -745,7 +745,7 @@ static const struct status_to_posix_error smb2_error_map_table[] = {
"STATUS_NOLOGON_SERVER_TRUST_ACCOUNT"},
{STATUS_DOMAIN_TRUST_INCONSISTENT, -EIO,
"STATUS_DOMAIN_TRUST_INCONSISTENT"},
- {STATUS_FS_DRIVER_REQUIRED, -EIO, "STATUS_FS_DRIVER_REQUIRED"},
+ {STATUS_FS_DRIVER_REQUIRED, -EOPNOTSUPP, "STATUS_FS_DRIVER_REQUIRED"},
{STATUS_IMAGE_ALREADY_LOADED_AS_DLL, -EIO,
"STATUS_IMAGE_ALREADY_LOADED_AS_DLL"},
{STATUS_NETWORK_OPEN_RESTRICTION, -EIO,
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index 76d03abaa38c..5406e95f5d92 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -150,7 +150,8 @@ smb2_check_message(char *buf, unsigned int length, struct TCP_Server_Info *srvr)
}
return 1;
}
- if (len > CIFSMaxBufSize + MAX_SMB2_HDR_SIZE - 4) {
+ if (len > CIFSMaxBufSize + MAX_SMB2_HDR_SIZE -
+ srvr->vals->header_preamble_size) {
cifs_dbg(VFS, "SMB length greater than maximum, mid=%llu\n",
mid);
return 1;
@@ -189,26 +190,26 @@ smb2_check_message(char *buf, unsigned int length, struct TCP_Server_Info *srvr)
}
}
- if (4 + len != length) {
- cifs_dbg(VFS, "Total length %u RFC1002 length %u mismatch mid %llu\n",
- length, 4 + len, mid);
+ if (srvr->vals->header_preamble_size + len != length) {
+ cifs_dbg(VFS, "Total length %u RFC1002 length %zu mismatch mid %llu\n",
+ length, srvr->vals->header_preamble_size + len, mid);
return 1;
}
clc_len = smb2_calc_size(hdr);
- if (4 + len != clc_len) {
- cifs_dbg(FYI, "Calculated size %u length %u mismatch mid %llu\n",
- clc_len, 4 + len, mid);
+ if (srvr->vals->header_preamble_size + len != clc_len) {
+ cifs_dbg(FYI, "Calculated size %u length %zu mismatch mid %llu\n",
+ clc_len, srvr->vals->header_preamble_size + len, mid);
/* create failed on symlink */
if (command == SMB2_CREATE_HE &&
shdr->Status == STATUS_STOPPED_ON_SYMLINK)
return 0;
/* Windows 7 server returns 24 bytes more */
- if (clc_len + 20 == len && command == SMB2_OPLOCK_BREAK_HE)
+ if (clc_len + 24 - srvr->vals->header_preamble_size == len && command == SMB2_OPLOCK_BREAK_HE)
return 0;
/* server can return one byte more due to implied bcc[0] */
- if (clc_len == 4 + len + 1)
+ if (clc_len == srvr->vals->header_preamble_size + len + 1)
return 0;
/*
@@ -218,10 +219,10 @@ smb2_check_message(char *buf, unsigned int length, struct TCP_Server_Info *srvr)
* Log the server error (once), but allow it and continue
* since the frame is parseable.
*/
- if (clc_len < 4 /* RFC1001 header size */ + len) {
+ if (clc_len < srvr->vals->header_preamble_size /* RFC1001 header size */ + len) {
printk_once(KERN_WARNING
- "SMB2 server sent bad RFC1001 len %d not %d\n",
- len, clc_len - 4);
+ "SMB2 server sent bad RFC1001 len %d not %zu\n",
+ len, clc_len - srvr->vals->header_preamble_size);
return 0;
}
@@ -706,3 +707,67 @@ smb2_handle_cancelled_mid(char *buffer, struct TCP_Server_Info *server)
return 0;
}
+
+#ifdef CONFIG_CIFS_SMB311
+/**
+ * smb311_update_preauth_hash - update @ses hash with the packet data in @iov
+ *
+ * Assumes @iov does not contain the rfc1002 length and iov[0] has the
+ * SMB2 header.
+ */
+int
+smb311_update_preauth_hash(struct cifs_ses *ses, struct kvec *iov, int nvec)
+{
+ int i, rc;
+ struct sdesc *d;
+ struct smb2_sync_hdr *hdr;
+
+ if (ses->server->tcpStatus == CifsGood) {
+ /* skip non smb311 connections */
+ if (ses->server->dialect != SMB311_PROT_ID)
+ return 0;
+
+ /* skip last sess setup response */
+ hdr = (struct smb2_sync_hdr *)iov[0].iov_base;
+ if (hdr->Flags & SMB2_FLAGS_SIGNED)
+ return 0;
+ }
+
+ rc = smb311_crypto_shash_allocate(ses->server);
+ if (rc)
+ return rc;
+
+ d = ses->server->secmech.sdescsha512;
+ rc = crypto_shash_init(&d->shash);
+ if (rc) {
+ cifs_dbg(VFS, "%s: could not init sha512 shash\n", __func__);
+ return rc;
+ }
+
+ rc = crypto_shash_update(&d->shash, ses->preauth_sha_hash,
+ SMB2_PREAUTH_HASH_SIZE);
+ if (rc) {
+ cifs_dbg(VFS, "%s: could not update sha512 shash\n", __func__);
+ return rc;
+ }
+
+ for (i = 0; i < nvec; i++) {
+ rc = crypto_shash_update(&d->shash,
+ iov[i].iov_base, iov[i].iov_len);
+ if (rc) {
+ cifs_dbg(VFS, "%s: could not update sha512 shash\n",
+ __func__);
+ return rc;
+ }
+ }
+
+ rc = crypto_shash_final(&d->shash, ses->preauth_sha_hash);
+ if (rc) {
+ cifs_dbg(VFS, "%s: could not finalize sha512 shash\n",
+ __func__);
+ return rc;
+ }
+
+ return 0;
+}
+#endif
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index eb68e2fcc500..968b1d43a1ea 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -1412,7 +1412,7 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
} while (rc == -EAGAIN);
if (rc) {
- if (rc != -ENOENT)
+ if ((rc != -ENOENT) && (rc != -EOPNOTSUPP))
cifs_dbg(VFS, "ioctl error in smb2_get_dfs_refer rc=%d\n", rc);
goto out;
}
@@ -1457,6 +1457,8 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
unsigned int sub_offset;
unsigned int print_len;
unsigned int print_offset;
+ struct cifs_ses *ses = tcon->ses;
+ struct TCP_Server_Info *server = ses->server;
cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
@@ -1479,7 +1481,7 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
}
if (le32_to_cpu(err_buf->ByteCount) < sizeof(struct smb2_symlink_err_rsp) ||
- get_rfc1002_length(err_buf) + 4 < SMB2_SYMLINK_STRUCT_SIZE) {
+ get_rfc1002_length(err_buf) + server->vals->header_preamble_size < SMB2_SYMLINK_STRUCT_SIZE) {
kfree(utf16_path);
return -ENOENT;
}
@@ -1492,13 +1494,13 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
print_len = le16_to_cpu(symlink->PrintNameLength);
print_offset = le16_to_cpu(symlink->PrintNameOffset);
- if (get_rfc1002_length(err_buf) + 4 <
+ if (get_rfc1002_length(err_buf) + server->vals->header_preamble_size <
SMB2_SYMLINK_STRUCT_SIZE + sub_offset + sub_len) {
kfree(utf16_path);
return -ENOENT;
}
- if (get_rfc1002_length(err_buf) + 4 <
+ if (get_rfc1002_length(err_buf) + server->vals->header_preamble_size <
SMB2_SYMLINK_STRUCT_SIZE + print_offset + print_len) {
kfree(utf16_path);
return -ENOENT;
@@ -2050,7 +2052,8 @@ smb2_dir_needs_close(struct cifsFileInfo *cfile)
}
static void
-fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, struct smb_rqst *old_rq)
+fill_transform_hdr(struct TCP_Server_Info *server,
+ struct smb2_transform_hdr *tr_hdr, struct smb_rqst *old_rq)
{
struct smb2_sync_hdr *shdr =
(struct smb2_sync_hdr *)old_rq->rq_iov[1].iov_base;
@@ -2062,10 +2065,19 @@ fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, struct smb_rqst *old_rq)
tr_hdr->Flags = cpu_to_le16(0x01);
get_random_bytes(&tr_hdr->Nonce, SMB3_AES128CMM_NONCE);
memcpy(&tr_hdr->SessionId, &shdr->SessionId, 8);
- inc_rfc1001_len(tr_hdr, sizeof(struct smb2_transform_hdr) - 4);
+ inc_rfc1001_len(tr_hdr, sizeof(struct smb2_transform_hdr) - server->vals->header_preamble_size);
inc_rfc1001_len(tr_hdr, orig_len);
}
+/* We can not use the normal sg_set_buf() as we will sometimes pass a
+ * stack object as buf.
+ */
+static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf,
+ unsigned int buflen)
+{
+ sg_set_page(sg, virt_to_page(buf), buflen, offset_in_page(buf));
+}
+
static struct scatterlist *
init_sg(struct smb_rqst *rqst, u8 *sign)
{
@@ -2080,16 +2092,16 @@ init_sg(struct smb_rqst *rqst, u8 *sign)
return NULL;
sg_init_table(sg, sg_len);
- sg_set_buf(&sg[0], rqst->rq_iov[0].iov_base + 24, assoc_data_len);
+ smb2_sg_set_buf(&sg[0], rqst->rq_iov[0].iov_base + 24, assoc_data_len);
for (i = 1; i < rqst->rq_nvec; i++)
- sg_set_buf(&sg[i], rqst->rq_iov[i].iov_base,
+ smb2_sg_set_buf(&sg[i], rqst->rq_iov[i].iov_base,
rqst->rq_iov[i].iov_len);
for (j = 0; i < sg_len - 1; i++, j++) {
unsigned int len = (j < rqst->rq_npages - 1) ? rqst->rq_pagesz
: rqst->rq_tailsz;
sg_set_page(&sg[i], rqst->rq_pages[j], len, 0);
}
- sg_set_buf(&sg[sg_len - 1], sign, SMB2_SIGNATURE_SIZE);
+ smb2_sg_set_buf(&sg[sg_len - 1], sign, SMB2_SIGNATURE_SIZE);
return sg;
}
@@ -2125,7 +2137,7 @@ crypt_message(struct TCP_Server_Info *server, struct smb_rqst *rqst, int enc)
{
struct smb2_transform_hdr *tr_hdr =
(struct smb2_transform_hdr *)rqst->rq_iov[0].iov_base;
- unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 24;
+ unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20 - server->vals->header_preamble_size;
int rc = 0;
struct scatterlist *sg;
u8 sign[SMB2_SIGNATURE_SIZE] = {};
@@ -2253,7 +2265,7 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq,
goto err_free_iov;
/* fill the 1st iov with a transform header */
- fill_transform_hdr(tr_hdr, old_rq);
+ fill_transform_hdr(server, tr_hdr, old_rq);
new_rq->rq_iov[0].iov_base = tr_hdr;
new_rq->rq_iov[0].iov_len = sizeof(struct smb2_transform_hdr);
@@ -2335,10 +2347,10 @@ decrypt_raw_data(struct TCP_Server_Info *server, char *buf,
if (rc)
return rc;
- memmove(buf + 4, iov[1].iov_base, buf_data_size);
+ memmove(buf + server->vals->header_preamble_size, iov[1].iov_base, buf_data_size);
hdr = (struct smb2_hdr *)buf;
hdr->smb2_buf_length = cpu_to_be32(buf_data_size + page_data_size);
- server->total_read = buf_data_size + page_data_size + 4;
+ server->total_read = buf_data_size + page_data_size + server->vals->header_preamble_size;
return rc;
}
@@ -2442,7 +2454,7 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
return 0;
}
- data_offset = server->ops->read_data_offset(buf) + 4;
+ data_offset = server->ops->read_data_offset(buf) + server->vals->header_preamble_size;
#ifdef CONFIG_CIFS_SMB_DIRECT
use_rdma_mr = rdata->mr;
#endif
@@ -2538,11 +2550,12 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid)
unsigned int npages;
struct page **pages;
unsigned int len;
- unsigned int buflen = get_rfc1002_length(buf) + 4;
+ unsigned int buflen = get_rfc1002_length(buf) + server->vals->header_preamble_size;
int rc;
int i = 0;
- len = min_t(unsigned int, buflen, server->vals->read_rsp_size - 4 +
+ len = min_t(unsigned int, buflen, server->vals->read_rsp_size -
+ server->vals->header_preamble_size +
sizeof(struct smb2_transform_hdr)) - HEADER_SIZE(server) + 1;
rc = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, len);
@@ -2550,8 +2563,9 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid)
return rc;
server->total_read += rc;
- len = le32_to_cpu(tr_hdr->OriginalMessageSize) + 4 -
- server->vals->read_rsp_size;
+ len = le32_to_cpu(tr_hdr->OriginalMessageSize) +
+ server->vals->header_preamble_size -
+ server->vals->read_rsp_size;
npages = DIV_ROUND_UP(len, PAGE_SIZE);
pages = kmalloc_array(npages, sizeof(struct page *), GFP_KERNEL);
@@ -2577,7 +2591,8 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid)
if (rc)
goto free_pages;
- rc = decrypt_raw_data(server, buf, server->vals->read_rsp_size - 4,
+ rc = decrypt_raw_data(server, buf, server->vals->read_rsp_size -
+ server->vals->header_preamble_size,
pages, npages, len);
if (rc)
goto free_pages;
@@ -2614,7 +2629,7 @@ receive_encrypted_standard(struct TCP_Server_Info *server,
struct mid_q_entry *mid_entry;
/* switch to large buffer if too big for a small one */
- if (pdu_length + 4 > MAX_CIFS_SMALL_BUFFER_SIZE) {
+ if (pdu_length + server->vals->header_preamble_size > MAX_CIFS_SMALL_BUFFER_SIZE) {
server->large_buf = true;
memcpy(server->bigbuf, buf, server->total_read);
buf = server->bigbuf;
@@ -2622,12 +2637,13 @@ receive_encrypted_standard(struct TCP_Server_Info *server,
/* now read the rest */
length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1,
- pdu_length - HEADER_SIZE(server) + 1 + 4);
+ pdu_length - HEADER_SIZE(server) + 1 +
+ server->vals->header_preamble_size);
if (length < 0)
return length;
server->total_read += length;
- buf_size = pdu_length + 4 - sizeof(struct smb2_transform_hdr);
+ buf_size = pdu_length + server->vals->header_preamble_size - sizeof(struct smb2_transform_hdr);
length = decrypt_raw_data(server, buf, buf_size, NULL, 0, 0);
if (length)
return length;
@@ -2656,7 +2672,7 @@ smb3_receive_transform(struct TCP_Server_Info *server, struct mid_q_entry **mid)
struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf;
unsigned int orig_len = le32_to_cpu(tr_hdr->OriginalMessageSize);
- if (pdu_length + 4 < sizeof(struct smb2_transform_hdr) +
+ if (pdu_length + server->vals->header_preamble_size < sizeof(struct smb2_transform_hdr) +
sizeof(struct smb2_sync_hdr)) {
cifs_dbg(VFS, "Transform message is too small (%u)\n",
pdu_length);
@@ -2665,14 +2681,14 @@ smb3_receive_transform(struct TCP_Server_Info *server, struct mid_q_entry **mid)
return -ECONNABORTED;
}
- if (pdu_length + 4 < orig_len + sizeof(struct smb2_transform_hdr)) {
+ if (pdu_length + server->vals->header_preamble_size < orig_len + sizeof(struct smb2_transform_hdr)) {
cifs_dbg(VFS, "Transform message is broken\n");
cifs_reconnect(server);
wake_up(&server->response_q);
return -ECONNABORTED;
}
- if (pdu_length + 4 > CIFSMaxBufSize + MAX_HEADER_SIZE(server))
+ if (pdu_length + server->vals->header_preamble_size > CIFSMaxBufSize + MAX_HEADER_SIZE(server))
return receive_encrypted_read(server, mid);
return receive_encrypted_standard(server, mid);
@@ -2683,7 +2699,8 @@ smb3_handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{
char *buf = server->large_buf ? server->bigbuf : server->smallbuf;
- return handle_read_data(server, mid, buf, get_rfc1002_length(buf) + 4,
+ return handle_read_data(server, mid, buf, get_rfc1002_length(buf) +
+ server->vals->header_preamble_size,
NULL, 0, 0);
}
@@ -3088,6 +3105,7 @@ struct smb_version_values smb20_values = {
.shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
.header_size = sizeof(struct smb2_hdr),
+ .header_preamble_size = 4,
.max_header_size = MAX_SMB2_HDR_SIZE,
.read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
.lock_cmd = SMB2_LOCK,
@@ -3108,6 +3126,7 @@ struct smb_version_values smb21_values = {
.shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
.header_size = sizeof(struct smb2_hdr),
+ .header_preamble_size = 4,
.max_header_size = MAX_SMB2_HDR_SIZE,
.read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
.lock_cmd = SMB2_LOCK,
@@ -3128,6 +3147,7 @@ struct smb_version_values smb3any_values = {
.shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
.header_size = sizeof(struct smb2_hdr),
+ .header_preamble_size = 4,
.max_header_size = MAX_SMB2_HDR_SIZE,
.read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
.lock_cmd = SMB2_LOCK,
@@ -3148,6 +3168,7 @@ struct smb_version_values smbdefault_values = {
.shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
.header_size = sizeof(struct smb2_hdr),
+ .header_preamble_size = 4,
.max_header_size = MAX_SMB2_HDR_SIZE,
.read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
.lock_cmd = SMB2_LOCK,
@@ -3168,6 +3189,7 @@ struct smb_version_values smb30_values = {
.shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
.header_size = sizeof(struct smb2_hdr),
+ .header_preamble_size = 4,
.max_header_size = MAX_SMB2_HDR_SIZE,
.read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
.lock_cmd = SMB2_LOCK,
@@ -3188,6 +3210,7 @@ struct smb_version_values smb302_values = {
.shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
.header_size = sizeof(struct smb2_hdr),
+ .header_preamble_size = 4,
.max_header_size = MAX_SMB2_HDR_SIZE,
.read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
.lock_cmd = SMB2_LOCK,
@@ -3209,6 +3232,7 @@ struct smb_version_values smb311_values = {
.shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
.header_size = sizeof(struct smb2_hdr),
+ .header_preamble_size = 4,
.max_header_size = MAX_SMB2_HDR_SIZE,
.read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
.lock_cmd = SMB2_LOCK,
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 63778ac22fd9..f7741cee2a4c 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -453,6 +453,10 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
return rc;
req->sync_hdr.SessionId = 0;
+#ifdef CONFIG_CIFS_SMB311
+ memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);
+ memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);
+#endif
if (strcmp(ses->server->vals->version_string,
SMB3ANY_VERSION_STRING) == 0) {
@@ -564,6 +568,15 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
/* BB: add check that dialect was valid given dialect(s) we asked for */
+#ifdef CONFIG_CIFS_SMB311
+ /*
+ * Keep a copy of the hash after negprot. This hash will be
+ * the starting hash value for all sessions made from this
+ * server.
+ */
+ memcpy(server->preauth_sha_hash, ses->preauth_sha_hash,
+ SMB2_PREAUTH_HASH_SIZE);
+#endif
/* SMB2 only has an extended negflavor */
server->negflavor = CIFS_NEGFLAVOR_EXTENDED;
/* set it to the maximum buffer size value we can send with 1 credit */
@@ -571,8 +584,10 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
SMB2_MAX_BUFFER_SIZE);
server->max_read = le32_to_cpu(rsp->MaxReadSize);
server->max_write = le32_to_cpu(rsp->MaxWriteSize);
- /* BB Do we need to validate the SecurityMode? */
server->sec_mode = le16_to_cpu(rsp->SecurityMode);
+ if ((server->sec_mode & SMB2_SEC_MODE_FLAGS_ALL) != server->sec_mode)
+ cifs_dbg(FYI, "Server returned unexpected security mode 0x%x\n",
+ server->sec_mode);
server->capabilities = le32_to_cpu(rsp->Capabilities);
/* Internal types */
server->capabilities |= SMB2_NT_FIND | SMB2_LARGE_FILES;
@@ -621,6 +636,10 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
return 0;
#endif
+ /* In SMB3.11 preauth integrity supersedes validate negotiate */
+ if (tcon->ses->server->dialect == SMB311_PROT_ID)
+ return 0;
+
/*
* validation ioctl must be signed, so no point sending this if we
* can not sign it (ie are not known user). Even if signing is not
@@ -1148,6 +1167,14 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
sess_data->buf0_type = CIFS_NO_BUFFER;
sess_data->nls_cp = (struct nls_table *) nls_cp;
+#ifdef CONFIG_CIFS_SMB311
+ /*
+ * Initialize the session hash with the server one.
+ */
+ memcpy(ses->preauth_sha_hash, ses->server->preauth_sha_hash,
+ SMB2_PREAUTH_HASH_SIZE);
+#endif
+
while (sess_data->func)
sess_data->func(sess_data);
@@ -1280,6 +1307,11 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
iov[1].iov_base = unc_path;
iov[1].iov_len = unc_path_len;
+ /* 3.11 tcon req must be signed if not encrypted. See MS-SMB2 3.2.4.1.1 */
+ if ((ses->server->dialect == SMB311_PROT_ID) &&
+ !encryption_required(tcon))
+ req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
+
rc = smb2_send_recv(xid, ses, iov, 2, &resp_buftype, flags, &rsp_iov);
cifs_small_buf_release(req);
rsp = (struct smb2_tree_connect_rsp *)rsp_iov.iov_base;
@@ -1441,7 +1473,7 @@ parse_lease_state(struct TCP_Server_Info *server, struct smb2_create_rsp *rsp,
unsigned int remaining;
char *name;
- data_offset = (char *)rsp + 4 + le32_to_cpu(rsp->CreateContextsOffset);
+ data_offset = (char *)rsp + server->vals->header_preamble_size + le32_to_cpu(rsp->CreateContextsOffset);
remaining = le32_to_cpu(rsp->CreateContextsLength);
cc = (struct create_context *)data_offset;
while (remaining >= sizeof(struct create_context)) {
@@ -1738,8 +1770,10 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
rc = alloc_path_with_tree_prefix(&copy_path, &copy_size,
&name_len,
tcon->treeName, path);
- if (rc)
+ if (rc) {
+ cifs_small_buf_release(req);
return rc;
+ }
req->NameLength = cpu_to_le16(name_len * 2);
uni_path_len = copy_size;
path = copy_path;
@@ -1750,8 +1784,10 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
if (uni_path_len % 8 != 0) {
copy_size = roundup(uni_path_len, 8);
copy_path = kzalloc(copy_size, GFP_KERNEL);
- if (!copy_path)
+ if (!copy_path) {
+ cifs_small_buf_release(req);
return -ENOMEM;
+ }
memcpy((char *)copy_path, (const char *)path,
uni_path_len);
uni_path_len = copy_size;
@@ -3418,6 +3454,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;
int rc;
struct smb2_query_info_req *req;
unsigned int total_len;
@@ -3440,7 +3477,7 @@ build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, int level,
req->InputBufferOffset =
cpu_to_le16(sizeof(struct smb2_query_info_req) - 1);
req->OutputBufferLength = cpu_to_le32(
- outbuf_len + sizeof(struct smb2_query_info_rsp) - 1 - 4);
+ outbuf_len + sizeof(struct smb2_query_info_rsp) - 1 - server->vals->header_preamble_size);
iov->iov_base = (char *)req;
iov->iov_len = total_len;
@@ -3457,6 +3494,7 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
int rc = 0;
int resp_buftype;
struct cifs_ses *ses = tcon->ses;
+ struct TCP_Server_Info *server = ses->server;
struct smb2_fs_full_size_info *info = NULL;
int flags = 0;
@@ -3477,7 +3515,7 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
}
rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
- info = (struct smb2_fs_full_size_info *)(4 /* RFC1001 len */ +
+ 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,
@@ -3500,6 +3538,7 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
int rc = 0;
int resp_buftype, max_len, min_len;
struct cifs_ses *ses = tcon->ses;
+ struct TCP_Server_Info *server = ses->server;
unsigned int rsp_len, offset;
int flags = 0;
@@ -3540,15 +3579,15 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
goto qfsattr_exit;
if (level == FS_ATTRIBUTE_INFORMATION)
- memcpy(&tcon->fsAttrInfo, 4 /* RFC1001 len */ + offset
+ memcpy(&tcon->fsAttrInfo, server->vals->header_preamble_size + offset
+ (char *)&rsp->hdr, min_t(unsigned int,
rsp_len, max_len));
else if (level == FS_DEVICE_INFORMATION)
- memcpy(&tcon->fsDevInfo, 4 /* RFC1001 len */ + offset
+ memcpy(&tcon->fsDevInfo, server->vals->header_preamble_size + offset
+ (char *)&rsp->hdr, sizeof(FILE_SYSTEM_DEVICE_INFO));
else if (level == FS_SECTOR_SIZE_INFORMATION) {
struct smb3_fs_ss_info *ss_info = (struct smb3_fs_ss_info *)
- (4 /* RFC1001 len */ + offset + (char *)&rsp->hdr);
+ (server->vals->header_preamble_size + offset + (char *)&rsp->hdr);
tcon->ss_flags = le32_to_cpu(ss_info->Flags);
tcon->perf_sector_size =
le32_to_cpu(ss_info->PhysicalBytesPerSectorForPerf);
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index 2a2b34ccaf49..253e2c7c952f 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -249,6 +249,8 @@ struct smb2_negotiate_req {
/* SecurityMode flags */
#define SMB2_NEGOTIATE_SIGNING_ENABLED 0x0001
#define SMB2_NEGOTIATE_SIGNING_REQUIRED 0x0002
+#define SMB2_SEC_MODE_FLAGS_ALL 0x0003
+
/* Capabilities flags */
#define SMB2_GLOBAL_CAP_DFS 0x00000001
#define SMB2_GLOBAL_CAP_LEASING 0x00000002 /* Resp only New to SMB2.1 */
@@ -264,6 +266,7 @@ struct smb2_negotiate_req {
#define SMB311_SALT_SIZE 32
/* Hash Algorithm Types */
#define SMB2_PREAUTH_INTEGRITY_SHA512 cpu_to_le16(0x0001)
+#define SMB2_PREAUTH_HASH_SIZE 64
struct smb2_preauth_neg_context {
__le16 ContextType; /* 1 */
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index 05287b01f596..cbcce3f7e86f 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -202,4 +202,9 @@ extern int smb3_validate_negotiate(const unsigned int, struct cifs_tcon *);
extern enum securityEnum smb2_select_sectype(struct TCP_Server_Info *,
enum securityEnum);
+#ifdef CONFIG_CIFS_SMB311
+extern int smb311_crypto_shash_allocate(struct TCP_Server_Info *server);
+extern int smb311_update_preauth_hash(struct cifs_ses *ses,
+ struct kvec *iov, int nvec);
+#endif
#endif /* _SMB2PROTO_H */
diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c
index 99493946e2f9..bf49cb73b9e6 100644
--- a/fs/cifs/smb2transport.c
+++ b/fs/cifs/smb2transport.c
@@ -43,77 +43,62 @@
static int
smb2_crypto_shash_allocate(struct TCP_Server_Info *server)
{
- int rc;
- unsigned int size;
+ return cifs_alloc_hash("hmac(sha256)",
+ &server->secmech.hmacsha256,
+ &server->secmech.sdeschmacsha256);
+}
- if (server->secmech.sdeschmacsha256 != NULL)
- return 0; /* already allocated */
+static int
+smb3_crypto_shash_allocate(struct TCP_Server_Info *server)
+{
+ struct cifs_secmech *p = &server->secmech;
+ int rc;
- server->secmech.hmacsha256 = crypto_alloc_shash("hmac(sha256)", 0, 0);
- if (IS_ERR(server->secmech.hmacsha256)) {
- cifs_dbg(VFS, "could not allocate crypto hmacsha256\n");
- rc = PTR_ERR(server->secmech.hmacsha256);
- server->secmech.hmacsha256 = NULL;
- return rc;
- }
+ rc = cifs_alloc_hash("hmac(sha256)",
+ &p->hmacsha256,
+ &p->sdeschmacsha256);
+ if (rc)
+ goto err;
- size = sizeof(struct shash_desc) +
- crypto_shash_descsize(server->secmech.hmacsha256);
- server->secmech.sdeschmacsha256 = kmalloc(size, GFP_KERNEL);
- if (!server->secmech.sdeschmacsha256) {
- crypto_free_shash(server->secmech.hmacsha256);
- server->secmech.hmacsha256 = NULL;
- return -ENOMEM;
- }
- server->secmech.sdeschmacsha256->shash.tfm = server->secmech.hmacsha256;
- server->secmech.sdeschmacsha256->shash.flags = 0x0;
+ rc = cifs_alloc_hash("cmac(aes)", &p->cmacaes, &p->sdesccmacaes);
+ if (rc)
+ goto err;
return 0;
+err:
+ cifs_free_hash(&p->hmacsha256, &p->sdeschmacsha256);
+ return rc;
}
-static int
-smb3_crypto_shash_allocate(struct TCP_Server_Info *server)
+#ifdef CONFIG_CIFS_SMB311
+int
+smb311_crypto_shash_allocate(struct TCP_Server_Info *server)
{
- unsigned int size;
- int rc;
-
- if (server->secmech.sdesccmacaes != NULL)
- return 0; /* already allocated */
+ struct cifs_secmech *p = &server->secmech;
+ int rc = 0;
- rc = smb2_crypto_shash_allocate(server);
+ rc = cifs_alloc_hash("hmac(sha256)",
+ &p->hmacsha256,
+ &p->sdeschmacsha256);
if (rc)
return rc;
- server->secmech.cmacaes = crypto_alloc_shash("cmac(aes)", 0, 0);
- if (IS_ERR(server->secmech.cmacaes)) {
- cifs_dbg(VFS, "could not allocate crypto cmac-aes");
- kfree(server->secmech.sdeschmacsha256);
- server->secmech.sdeschmacsha256 = NULL;
- crypto_free_shash(server->secmech.hmacsha256);
- server->secmech.hmacsha256 = NULL;
- rc = PTR_ERR(server->secmech.cmacaes);
- server->secmech.cmacaes = NULL;
- return rc;
- }
+ rc = cifs_alloc_hash("cmac(aes)", &p->cmacaes, &p->sdesccmacaes);
+ if (rc)
+ goto err;
- size = sizeof(struct shash_desc) +
- crypto_shash_descsize(server->secmech.cmacaes);
- server->secmech.sdesccmacaes = kmalloc(size, GFP_KERNEL);
- if (!server->secmech.sdesccmacaes) {
- cifs_dbg(VFS, "%s: Can't alloc cmacaes\n", __func__);
- kfree(server->secmech.sdeschmacsha256);
- server->secmech.sdeschmacsha256 = NULL;
- crypto_free_shash(server->secmech.hmacsha256);
- crypto_free_shash(server->secmech.cmacaes);
- server->secmech.hmacsha256 = NULL;
- server->secmech.cmacaes = NULL;
- return -ENOMEM;
- }
- server->secmech.sdesccmacaes->shash.tfm = server->secmech.cmacaes;
- server->secmech.sdesccmacaes->shash.flags = 0x0;
+ rc = cifs_alloc_hash("sha512", &p->sha512, &p->sdescsha512);
+ if (rc)
+ goto err;
return 0;
+
+err:
+ cifs_free_hash(&p->cmacaes, &p->sdesccmacaes);
+ cifs_free_hash(&p->hmacsha256, &p->sdeschmacsha256);
+ return rc;
}
+#endif
static struct cifs_ses *
smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id)
@@ -457,7 +442,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
cifs_dbg(VFS, "%s: Could not init cmac aes\n", __func__);
return rc;
}
-
+
rc = __cifs_calc_signature(rqst, server, sigptr,
&server->secmech.sdesccmacaes->shash);
diff --git a/fs/cifs/smbdirect.c b/fs/cifs/smbdirect.c
index 91710eb571fb..5008af546dd1 100644
--- a/fs/cifs/smbdirect.c
+++ b/fs/cifs/smbdirect.c
@@ -862,6 +862,8 @@ static int smbd_post_send_negotiate_req(struct smbd_connection *info)
ib_dma_unmap_single(info->id->device, request->sge[0].addr,
request->sge[0].length, DMA_TO_DEVICE);
+ smbd_disconnect_rdma_connection(info);
+
dma_mapping_failed:
mempool_free(request, info->request_mempool);
return rc;
@@ -1025,7 +1027,7 @@ static int smbd_post_send(struct smbd_connection *info,
for (i = 0; i < request->num_sge; i++) {
log_rdma_send(INFO,
- "rdma_request sge[%d] addr=%llu legnth=%u\n",
+ "rdma_request sge[%d] addr=%llu length=%u\n",
i, request->sge[0].addr, request->sge[0].length);
ib_dma_sync_single_for_device(
info->id->device,
@@ -1061,6 +1063,7 @@ static int smbd_post_send(struct smbd_connection *info,
if (atomic_dec_and_test(&info->send_pending))
wake_up(&info->wait_send_pending);
}
+ smbd_disconnect_rdma_connection(info);
} else
/* Reset timer for idle connection after packet is sent */
mod_delayed_work(info->workqueue, &info->idle_timer_work,
@@ -1202,7 +1205,7 @@ static int smbd_post_recv(
if (rc) {
ib_dma_unmap_single(info->id->device, response->sge.addr,
response->sge.length, DMA_FROM_DEVICE);
-
+ smbd_disconnect_rdma_connection(info);
log_rdma_recv(ERR, "ib_post_recv failed rc=%d\n", rc);
}
@@ -1498,8 +1501,8 @@ int smbd_reconnect(struct TCP_Server_Info *server)
log_rdma_event(INFO, "reconnecting rdma session\n");
if (!server->smbd_conn) {
- log_rdma_event(ERR, "rdma session already destroyed\n");
- return -EINVAL;
+ log_rdma_event(INFO, "rdma session already destroyed\n");
+ goto create_conn;
}
/*
@@ -1512,15 +1515,19 @@ int smbd_reconnect(struct TCP_Server_Info *server)
}
/* wait until the transport is destroyed */
- wait_event(server->smbd_conn->wait_destroy,
- server->smbd_conn->transport_status == SMBD_DESTROYED);
+ if (!wait_event_timeout(server->smbd_conn->wait_destroy,
+ server->smbd_conn->transport_status == SMBD_DESTROYED, 5*HZ))
+ return -EAGAIN;
destroy_workqueue(server->smbd_conn->workqueue);
kfree(server->smbd_conn);
+create_conn:
log_rdma_event(INFO, "creating rdma session\n");
server->smbd_conn = smbd_get_connection(
server, (struct sockaddr *) &server->dstaddr);
+ log_rdma_event(INFO, "created rdma session info=%p\n",
+ server->smbd_conn);
return server->smbd_conn ? 0 : -ENOENT;
}
@@ -2295,7 +2302,7 @@ static void smbd_mr_recovery_work(struct work_struct *work)
rc = ib_dereg_mr(smbdirect_mr->mr);
if (rc) {
log_rdma_mr(ERR,
- "ib_dereg_mr faield rc=%x\n",
+ "ib_dereg_mr failed rc=%x\n",
rc);
smbd_disconnect_rdma_connection(info);
}
@@ -2542,6 +2549,8 @@ dma_map_error:
if (atomic_dec_and_test(&info->mr_used_count))
wake_up(&info->wait_for_mr_cleanup);
+ smbd_disconnect_rdma_connection(info);
+
return NULL;
}
diff --git a/fs/cifs/smbencrypt.c b/fs/cifs/smbencrypt.c
index c12bffefa3c9..a0b80ac651a6 100644
--- a/fs/cifs/smbencrypt.c
+++ b/fs/cifs/smbencrypt.c
@@ -121,25 +121,12 @@ int
mdfour(unsigned char *md4_hash, unsigned char *link_str, int link_len)
{
int rc;
- unsigned int size;
- struct crypto_shash *md4;
- struct sdesc *sdescmd4;
-
- md4 = crypto_alloc_shash("md4", 0, 0);
- if (IS_ERR(md4)) {
- rc = PTR_ERR(md4);
- cifs_dbg(VFS, "%s: Crypto md4 allocation error %d\n",
- __func__, rc);
- return rc;
- }
- size = sizeof(struct shash_desc) + crypto_shash_descsize(md4);
- sdescmd4 = kmalloc(size, GFP_KERNEL);
- if (!sdescmd4) {
- rc = -ENOMEM;
+ struct crypto_shash *md4 = NULL;
+ struct sdesc *sdescmd4 = NULL;
+
+ rc = cifs_alloc_hash("md4", &md4, &sdescmd4);
+ if (rc)
goto mdfour_err;
- }
- sdescmd4->shash.tfm = md4;
- sdescmd4->shash.flags = 0x0;
rc = crypto_shash_init(&sdescmd4->shash);
if (rc) {
@@ -156,9 +143,7 @@ mdfour(unsigned char *md4_hash, unsigned char *link_str, int link_len)
cifs_dbg(VFS, "%s: Could not generate md4 hash\n", __func__);
mdfour_err:
- crypto_free_shash(md4);
- kfree(sdescmd4);
-
+ cifs_free_hash(&md4, &sdescmd4);
return rc;
}
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index 9779b3292d8e..279718dcb2ed 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -37,6 +37,7 @@
#include "cifsglob.h"
#include "cifsproto.h"
#include "cifs_debug.h"
+#include "smb2proto.h"
#include "smbdirect.h"
/* Max number of iovectors we can use off the stack when sending requests. */
@@ -751,6 +752,12 @@ cifs_send_recv(const unsigned int xid, struct cifs_ses *ses,
if (rc < 0)
goto out;
+#ifdef CONFIG_CIFS_SMB311
+ if (ses->status == CifsNew)
+ smb311_update_preauth_hash(ses, rqst->rq_iov+1,
+ rqst->rq_nvec-1);
+#endif
+
if (timeout == CIFS_ASYNC_OP)
goto out;
@@ -783,12 +790,23 @@ cifs_send_recv(const unsigned int xid, struct cifs_ses *ses,
buf = (char *)midQ->resp_buf;
resp_iov->iov_base = buf;
- resp_iov->iov_len = get_rfc1002_length(buf) + 4;
+ resp_iov->iov_len = get_rfc1002_length(buf) +
+ ses->server->vals->header_preamble_size;
if (midQ->large_buf)
*resp_buf_type = CIFS_LARGE_BUFFER;
else
*resp_buf_type = CIFS_SMALL_BUFFER;
+#ifdef CONFIG_CIFS_SMB311
+ if (ses->status == CifsNew) {
+ struct kvec iov = {
+ .iov_base = buf + 4,
+ .iov_len = get_rfc1002_length(buf)
+ };
+ smb311_update_preauth_hash(ses, &iov, 1);
+ }
+#endif
+
credits = ses->server->ops->get_credits(midQ);
rc = ses->server->ops->check_receive(midQ, ses->server,