diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-04-04 14:09:27 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-04-04 14:09:27 -0700 |
commit | a8f8e8ac766ddb8702ef9baf01b7ae4f8d3a940c (patch) | |
tree | c1fb03d543dc85acf972e245eaaa39a23cdffcbd /fs | |
parent | 2bd99df54f43b659ddaab8922adbaf3bcf3753ed (diff) | |
parent | 07108d0e7c7fbbf9c6d76c0af2f1813e4f3f0800 (diff) |
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/Kconfig | 8 | ||||
-rw-r--r-- | fs/cifs/cifsencrypt.c | 85 | ||||
-rw-r--r-- | fs/cifs/cifsfs.c | 1 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 9 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 5 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 6 | ||||
-rw-r--r-- | fs/cifs/connect.c | 18 | ||||
-rw-r--r-- | fs/cifs/inode.c | 38 | ||||
-rw-r--r-- | fs/cifs/link.c | 27 | ||||
-rw-r--r-- | fs/cifs/misc.c | 54 | ||||
-rw-r--r-- | fs/cifs/smb1ops.c | 1 | ||||
-rw-r--r-- | fs/cifs/smb2maperror.c | 2 | ||||
-rw-r--r-- | fs/cifs/smb2misc.c | 89 | ||||
-rw-r--r-- | fs/cifs/smb2ops.c | 76 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 57 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.h | 3 | ||||
-rw-r--r-- | fs/cifs/smb2proto.h | 5 | ||||
-rw-r--r-- | fs/cifs/smb2transport.c | 97 | ||||
-rw-r--r-- | fs/cifs/smbdirect.c | 23 | ||||
-rw-r--r-- | fs/cifs/smbencrypt.c | 27 | ||||
-rw-r--r-- | fs/cifs/transport.c | 20 |
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); leng |