diff options
Diffstat (limited to 'fs/ksmbd')
-rw-r--r-- | fs/ksmbd/Kconfig | 2 | ||||
-rw-r--r-- | fs/ksmbd/auth.c | 230 | ||||
-rw-r--r-- | fs/ksmbd/connection.c | 23 | ||||
-rw-r--r-- | fs/ksmbd/crypto_ctx.c | 16 | ||||
-rw-r--r-- | fs/ksmbd/crypto_ctx.h | 8 | ||||
-rw-r--r-- | fs/ksmbd/glob.h | 2 | ||||
-rw-r--r-- | fs/ksmbd/ksmbd_netlink.h | 2 | ||||
-rw-r--r-- | fs/ksmbd/ksmbd_work.c | 1 | ||||
-rw-r--r-- | fs/ksmbd/ksmbd_work.h | 4 | ||||
-rw-r--r-- | fs/ksmbd/mgmt/user_config.c | 2 | ||||
-rw-r--r-- | fs/ksmbd/mgmt/user_config.h | 1 | ||||
-rw-r--r-- | fs/ksmbd/misc.c | 21 | ||||
-rw-r--r-- | fs/ksmbd/misc.h | 4 | ||||
-rw-r--r-- | fs/ksmbd/oplock.c | 87 | ||||
-rw-r--r-- | fs/ksmbd/oplock.h | 2 | ||||
-rw-r--r-- | fs/ksmbd/server.c | 5 | ||||
-rw-r--r-- | fs/ksmbd/smb2misc.c | 165 | ||||
-rw-r--r-- | fs/ksmbd/smb2ops.c | 17 | ||||
-rw-r--r-- | fs/ksmbd/smb2pdu.c | 1349 | ||||
-rw-r--r-- | fs/ksmbd/smb2pdu.h | 708 | ||||
-rw-r--r-- | fs/ksmbd/smb_common.c | 79 | ||||
-rw-r--r-- | fs/ksmbd/smb_common.h | 66 | ||||
-rw-r--r-- | fs/ksmbd/smbacl.c | 21 | ||||
-rw-r--r-- | fs/ksmbd/transport_ipc.c | 3 | ||||
-rw-r--r-- | fs/ksmbd/transport_ipc.h | 2 | ||||
-rw-r--r-- | fs/ksmbd/transport_rdma.c | 25 | ||||
-rw-r--r-- | fs/ksmbd/transport_tcp.c | 4 | ||||
-rw-r--r-- | fs/ksmbd/vfs.c | 182 | ||||
-rw-r--r-- | fs/ksmbd/vfs.h | 50 |
29 files changed, 1175 insertions, 1906 deletions
diff --git a/fs/ksmbd/Kconfig b/fs/ksmbd/Kconfig index b83cbd756ae5..e1fe17747ed6 100644 --- a/fs/ksmbd/Kconfig +++ b/fs/ksmbd/Kconfig @@ -6,7 +6,6 @@ config SMB_SERVER select NLS select NLS_UTF8 select CRYPTO - select CRYPTO_MD4 select CRYPTO_MD5 select CRYPTO_HMAC select CRYPTO_ECB @@ -19,6 +18,7 @@ config SMB_SERVER select CRYPTO_GCM select ASN1 select OID_REGISTRY + select CRC32 default n help Choose Y here if you want to allow SMB3 compliant clients diff --git a/fs/ksmbd/auth.c b/fs/ksmbd/auth.c index de36f12070bf..3503b1c48cb4 100644 --- a/fs/ksmbd/auth.c +++ b/fs/ksmbd/auth.c @@ -68,125 +68,6 @@ void ksmbd_copy_gss_neg_header(void *buf) memcpy(buf, NEGOTIATE_GSS_HEADER, AUTH_GSS_LENGTH); } -static void -str_to_key(unsigned char *str, unsigned char *key) -{ - int i; - - key[0] = str[0] >> 1; - key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2); - key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3); - key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4); - key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5); - key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6); - key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7); - key[7] = str[6] & 0x7F; - for (i = 0; i < 8; i++) - key[i] = (key[i] << 1); -} - -static int -smbhash(unsigned char *out, const unsigned char *in, unsigned char *key) -{ - unsigned char key2[8]; - struct des_ctx ctx; - - if (fips_enabled) { - ksmbd_debug(AUTH, "FIPS compliance enabled: DES not permitted\n"); - return -ENOENT; - } - - str_to_key(key, key2); - des_expand_key(&ctx, key2, DES_KEY_SIZE); - des_encrypt(&ctx, out, in); - memzero_explicit(&ctx, sizeof(ctx)); - return 0; -} - -static int ksmbd_enc_p24(unsigned char *p21, const unsigned char *c8, unsigned char *p24) -{ - int rc; - - rc = smbhash(p24, c8, p21); - if (rc) - return rc; - rc = smbhash(p24 + 8, c8, p21 + 7); - if (rc) - return rc; - return smbhash(p24 + 16, c8, p21 + 14); -} - -/* produce a md4 message digest from data of length n bytes */ -static int ksmbd_enc_md4(unsigned char *md4_hash, unsigned char *link_str, - int link_len) -{ - int rc; - struct ksmbd_crypto_ctx *ctx; - - ctx = ksmbd_crypto_ctx_find_md4(); - if (!ctx) { - ksmbd_debug(AUTH, "Crypto md4 allocation error\n"); - return -ENOMEM; - } - - rc = crypto_shash_init(CRYPTO_MD4(ctx)); - if (rc) { - ksmbd_debug(AUTH, "Could not init md4 shash\n"); - goto out; - } - - rc = crypto_shash_update(CRYPTO_MD4(ctx), link_str, link_len); - if (rc) { - ksmbd_debug(AUTH, "Could not update with link_str\n"); - goto out; - } - - rc = crypto_shash_final(CRYPTO_MD4(ctx), md4_hash); - if (rc) - ksmbd_debug(AUTH, "Could not generate md4 hash\n"); -out: - ksmbd_release_crypto_ctx(ctx); - return rc; -} - -static int ksmbd_enc_update_sess_key(unsigned char *md5_hash, char *nonce, - char *server_challenge, int len) -{ - int rc; - struct ksmbd_crypto_ctx *ctx; - - ctx = ksmbd_crypto_ctx_find_md5(); - if (!ctx) { - ksmbd_debug(AUTH, "Crypto md5 allocation error\n"); - return -ENOMEM; - } - - rc = crypto_shash_init(CRYPTO_MD5(ctx)); - if (rc) { - ksmbd_debug(AUTH, "Could not init md5 shash\n"); - goto out; - } - - rc = crypto_shash_update(CRYPTO_MD5(ctx), server_challenge, len); - if (rc) { - ksmbd_debug(AUTH, "Could not update with challenge\n"); - goto out; - } - - rc = crypto_shash_update(CRYPTO_MD5(ctx), nonce, len); - if (rc) { - ksmbd_debug(AUTH, "Could not update with nonce\n"); - goto out; - } - - rc = crypto_shash_final(CRYPTO_MD5(ctx), md5_hash); - if (rc) - ksmbd_debug(AUTH, "Could not generate md5 hash\n"); -out: - ksmbd_release_crypto_ctx(ctx); - return rc; -} - /** * ksmbd_gen_sess_key() - function to generate session key * @sess: session of connection @@ -325,43 +206,6 @@ out: } /** - * ksmbd_auth_ntlm() - NTLM authentication handler - * @sess: session of connection - * @pw_buf: NTLM challenge response - * @passkey: user password - * - * Return: 0 on success, error number on error - */ -int ksmbd_auth_ntlm(struct ksmbd_session *sess, char *pw_buf) -{ - int rc; - unsigned char p21[21]; - char key[CIFS_AUTH_RESP_SIZE]; - - memset(p21, '\0', 21); - memcpy(p21, user_passkey(sess->user), CIFS_NTHASH_SIZE); - rc = ksmbd_enc_p24(p21, sess->ntlmssp.cryptkey, key); - if (rc) { - pr_err("password processing failed\n"); - return rc; - } - - ksmbd_enc_md4(sess->sess_key, user_passkey(sess->user), - CIFS_SMB1_SESSKEY_SIZE); - memcpy(sess->sess_key + CIFS_SMB1_SESSKEY_SIZE, key, - CIFS_AUTH_RESP_SIZE); - sess->sequence_number = 1; - - if (strncmp(pw_buf, key, CIFS_AUTH_RESP_SIZE) != 0) { - ksmbd_debug(AUTH, "ntlmv1 authentication failed\n"); - return -EINVAL; - } - - ksmbd_debug(AUTH, "ntlmv1 authentication pass\n"); - return 0; -} - -/** * ksmbd_auth_ntlmv2() - NTLMv2 authentication handler * @sess: session of connection * @ntlmv2: NTLMv2 challenge response @@ -442,44 +286,6 @@ out: } /** - * __ksmbd_auth_ntlmv2() - NTLM2(extended security) authentication handler - * @sess: session of connection - * @client_nonce: client nonce from LM response. - * @ntlm_resp: ntlm response data from client. - * - * Return: 0 on success, error number on error - */ -static int __ksmbd_auth_ntlmv2(struct ksmbd_session *sess, char *client_nonce, - char *ntlm_resp) -{ - char sess_key[CIFS_SMB1_SESSKEY_SIZE] = {0}; - int rc; - unsigned char p21[21]; - char key[CIFS_AUTH_RESP_SIZE]; - - rc = ksmbd_enc_update_sess_key(sess_key, - client_nonce, - (char *)sess->ntlmssp.cryptkey, 8); - if (rc) { - pr_err("password processing failed\n"); - goto out; - } - - memset(p21, '\0', 21); - memcpy(p21, user_passkey(sess->user), CIFS_NTHASH_SIZE); - rc = ksmbd_enc_p24(p21, sess_key, key); - if (rc) { - pr_err("password processing failed\n"); - goto out; - } - - if (memcmp(ntlm_resp, key, CIFS_AUTH_RESP_SIZE) != 0) - rc = -EINVAL; -out: - return rc; -} - -/** * ksmbd_decode_ntlmssp_auth_blob() - helper function to construct * authenticate blob * @authblob: authenticate blob source pointer @@ -492,8 +298,8 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, int blob_len, struct ksmbd_session *sess) { char *domain_name; - unsigned int lm_off, nt_off; - unsigned short nt_len; + unsigned int nt_off, dn_off; + unsigned short nt_len, dn_len; int ret; if (blob_len < sizeof(struct authenticate_message)) { @@ -508,26 +314,17 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, return -EINVAL; } - lm_off = le32_to_cpu(authblob->LmChallengeResponse.BufferOffset); nt_off = le32_to_cpu(authblob->NtChallengeResponse.BufferOffset); nt_len = le16_to_cpu(authblob->NtChallengeResponse.Length); + dn_off = le32_to_cpu(authblob->DomainName.BufferOffset); + dn_len = le16_to_cpu(authblob->DomainName.Length); - /* process NTLM authentication */ - if (nt_len == CIFS_AUTH_RESP_SIZE) { - if (le32_to_cpu(authblob->NegotiateFlags) & - NTLMSSP_NEGOTIATE_EXTENDED_SEC) - return __ksmbd_auth_ntlmv2(sess, (char *)authblob + - lm_off, (char *)authblob + nt_off); - else - return ksmbd_auth_ntlm(sess, (char *)authblob + - nt_off); - } + if (blob_len < (u64)dn_off + dn_len || blob_len < (u64)nt_off + nt_len) + return -EINVAL; /* TODO : use domain name that imported from configuration file */ - domain_name = smb_strndup_from_utf16((const char *)authblob + - le32_to_cpu(authblob->DomainName.BufferOffset), - le16_to_cpu(authblob->DomainName.Length), true, - sess->conn->local_nls); + domain_name = smb_strndup_from_utf16((const char *)authblob + dn_off, + dn_len, true, sess->conn->local_nls); if (IS_ERR(domain_name)) return PTR_ERR(domain_name); @@ -1076,9 +873,9 @@ int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, __u8 *pi_hash) { int rc; - struct smb2_hdr *rcv_hdr = (struct smb2_hdr *)buf; + struct smb2_hdr *rcv_hdr = smb2_get_msg(buf); char *all_bytes_msg = (char *)&rcv_hdr->ProtocolId; - int msg_size = be32_to_cpu(rcv_hdr->smb2_buf_length); + int msg_size = get_rfc1002_len(buf); struct ksmbd_crypto_ctx *ctx = NULL; if (conn->preauth_info->Preauth_HashId != @@ -1186,7 +983,7 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, u8 *sign) { struct scatterlist *sg; - unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 24; + unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; int i, nr_entries[3] = {0}, total_entries = 0, sg_idx = 0; if (!nvec) @@ -1250,9 +1047,8 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, int ksmbd_crypt_message(struct ksmbd_conn *conn, struct kvec *iov, unsigned int nvec, int enc) { - struct smb2_transform_hdr *tr_hdr = - (struct smb2_transform_hdr *)iov[0].iov_base; - unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 24; + struct smb2_transform_hdr *tr_hdr = smb2_get_msg(iov[0].iov_base); + unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; int rc; struct scatterlist *sg; u8 sign[SMB2_SIGNATURE_SIZE] = {}; diff --git a/fs/ksmbd/connection.c b/fs/ksmbd/connection.c index af086d35398a..83a94d0bb480 100644 --- a/fs/ksmbd/connection.c +++ b/fs/ksmbd/connection.c @@ -61,6 +61,8 @@ struct ksmbd_conn *ksmbd_conn_alloc(void) conn->local_nls = load_nls_default(); atomic_set(&conn->req_running, 0); atomic_set(&conn->r_count, 0); + conn->total_credits = 1; + init_waitqueue_head(&conn->req_running_q); INIT_LIST_HEAD(&conn->conns_list); INIT_LIST_HEAD(&conn->sessions); @@ -156,26 +158,25 @@ void ksmbd_conn_wait_idle(struct ksmbd_conn *conn) int ksmbd_conn_write(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb_hdr *rsp_hdr = work->response_buf; size_t len = 0; int sent; struct kvec iov[3]; int iov_idx = 0; ksmbd_conn_try_dequeue_request(work); - if (!rsp_hdr) { + if (!work->response_buf) { pr_err("NULL response header\n"); return -EINVAL; } if (work->tr_buf) { iov[iov_idx] = (struct kvec) { work->tr_buf, - sizeof(struct smb2_transform_hdr) }; + sizeof(struct smb2_transform_hdr) + 4 }; len += iov[iov_idx++].iov_len; } if (work->aux_payload_sz) { - iov[iov_idx] = (struct kvec) { rsp_hdr, work->resp_hdr_sz }; + iov[iov_idx] = (struct kvec) { work->response_buf, work->resp_hdr_sz }; len += iov[iov_idx++].iov_len; iov[iov_idx] = (struct kvec) { work->aux_payload_buf, work->aux_payload_sz }; len += iov[iov_idx++].iov_len; @@ -183,8 +184,8 @@ int ksmbd_conn_write(struct ksmbd_work *work) if (work->tr_buf) iov[iov_idx].iov_len = work->resp_hdr_sz; else - iov[iov_idx].iov_len = get_rfc1002_len(rsp_hdr) + 4; - iov[iov_idx].iov_base = rsp_hdr; + iov[iov_idx].iov_len = get_rfc1002_len(work->response_buf) + 4; + iov[iov_idx].iov_base = work->response_buf; len += iov[iov_idx++].iov_len; } @@ -296,10 +297,12 @@ int ksmbd_conn_handler_loop(void *p) pdu_size = get_rfc1002_len(hdr_buf); ksmbd_debug(CONN, "RFC1002 header %u bytes\n", pdu_size); - /* make sure we have enough to get to SMB header end */ - if (!ksmbd_pdu_size_has_room(pdu_size)) { - ksmbd_debug(CONN, "SMB request too short (%u bytes)\n", - pdu_size); + /* + * Check if pdu size is valid (min : smb header size, + * max : 0x00FFFFFF). + */ + if (pdu_size < __SMB2_HEADER_STRUCTURE_SIZE || + pdu_size > MAX_STREAM_PROT_LEN) { continue; } diff --git a/fs/ksmbd/crypto_ctx.c b/fs/ksmbd/crypto_ctx.c index 5f4b1008d17e..81488d04199d 100644 --- a/fs/ksmbd/crypto_ctx.c +++ b/fs/ksmbd/crypto_ctx.c @@ -81,12 +81,6 @@ static struct shash_desc *alloc_shash_desc(int id) case CRYPTO_SHASH_SHA512: tfm = crypto_alloc_shash("sha512", 0, 0); break; - case CRYPTO_SHASH_MD4: - tfm = crypto_alloc_shash("md4", 0, 0); - break; - case CRYPTO_SHASH_MD5: - tfm = crypto_alloc_shash("md5", 0, 0); - break; default: return NULL; } @@ -214,16 +208,6 @@ struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void) return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA512); } -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md4(void) -{ - return ____crypto_shash_ctx_find(CRYPTO_SHASH_MD4); -} - -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md5(void) -{ - return ____crypto_shash_ctx_find(CRYPTO_SHASH_MD5); -} - static struct ksmbd_crypto_ctx *____crypto_aead_ctx_find(int id) { struct ksmbd_crypto_ctx *ctx; diff --git a/fs/ksmbd/crypto_ctx.h b/fs/ksmbd/crypto_ctx.h index ef11154b43df..4a367c62f653 100644 --- a/fs/ksmbd/crypto_ctx.h +++ b/fs/ksmbd/crypto_ctx.h @@ -15,8 +15,6 @@ enum { CRYPTO_SHASH_CMACAES, CRYPTO_SHASH_SHA256, CRYPTO_SHASH_SHA512, - CRYPTO_SHASH_MD4, - CRYPTO_SHASH_MD5, CRYPTO_SHASH_MAX, }; @@ -43,8 +41,6 @@ struct ksmbd_crypto_ctx { #define CRYPTO_CMACAES(c) ((c)->desc[CRYPTO_SHASH_CMACAES]) #define CRYPTO_SHA256(c) ((c)->desc[CRYPTO_SHASH_SHA256]) #define CRYPTO_SHA512(c) ((c)->desc[CRYPTO_SHASH_SHA512]) -#define CRYPTO_MD4(c) ((c)->desc[CRYPTO_SHASH_MD4]) -#define CRYPTO_MD5(c) ((c)->desc[CRYPTO_SHASH_MD5]) #define CRYPTO_HMACMD5_TFM(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]->tfm) #define CRYPTO_HMACSHA256_TFM(c)\ @@ -52,8 +48,6 @@ struct ksmbd_crypto_ctx { #define CRYPTO_CMACAES_TFM(c) ((c)->desc[CRYPTO_SHASH_CMACAES]->tfm) #define CRYPTO_SHA256_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA256]->tfm) #define CRYPTO_SHA512_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA512]->tfm) -#define CRYPTO_MD4_TFM(c) ((c)->desc[CRYPTO_SHASH_MD4]->tfm) -#define CRYPTO_MD5_TFM(c) ((c)->desc[CRYPTO_SHASH_MD5]->tfm) #define CRYPTO_GCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_GCM]) #define CRYPTO_CCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_CCM]) @@ -64,8 +58,6 @@ struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void); -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md4(void); -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md5(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void); struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void); void ksmbd_crypto_destroy(void); diff --git a/fs/ksmbd/glob.h b/fs/ksmbd/glob.h index 49a5a3afa118..5b8f3e0ebdb3 100644 --- a/fs/ksmbd/glob.h +++ b/fs/ksmbd/glob.h @@ -12,7 +12,7 @@ #include "unicode.h" #include "vfs_cache.h" -#define KSMBD_VERSION "3.1.9" +#define KSMBD_VERSION "3.4.2" extern int ksmbd_debug_types; diff --git a/fs/ksmbd/ksmbd_netlink.h b/fs/ksmbd/ksmbd_netlink.h index 2fbe2bc1e093..c6718a05d347 100644 --- a/fs/ksmbd/ksmbd_netlink.h +++ b/fs/ksmbd/ksmbd_netlink.h @@ -211,6 +211,7 @@ struct ksmbd_tree_disconnect_request { */ struct ksmbd_logout_request { __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */ + __u32 account_flags; }; /* @@ -317,6 +318,7 @@ enum KSMBD_TREE_CONN_STATUS { #define KSMBD_USER_FLAG_BAD_UID BIT(2) #define KSMBD_USER_FLAG_BAD_USER BIT(3) #define KSMBD_USER_FLAG_GUEST_ACCOUNT BIT(4) +#define KSMBD_USER_FLAG_DELAY_SESSION BIT(5) /* * Share config flags. diff --git a/fs/ksmbd/ksmbd_work.c b/fs/ksmbd/ksmbd_work.c index fd58eb4809f6..14b9caebf7a4 100644 --- a/fs/ksmbd/ksmbd_work.c +++ b/fs/ksmbd/ksmbd_work.c @@ -69,7 +69,6 @@ int ksmbd_workqueue_init(void) void ksmbd_workqueue_destroy(void) { - flush_workqueue(ksmbd_wq); destroy_workqueue(ksmbd_wq); ksmbd_wq = NULL; } diff --git a/fs/ksmbd/ksmbd_work.h b/fs/ksmbd/ksmbd_work.h index f7156bc50049..5ece58e40c97 100644 --- a/fs/ksmbd/ksmbd_work.h +++ b/fs/ksmbd/ksmbd_work.h @@ -92,7 +92,7 @@ struct ksmbd_work { */ static inline void *ksmbd_resp_buf_next(struct ksmbd_work *work) { - return work->response_buf + work->next_smb2_rsp_hdr_off; + return work->response_buf + work->next_smb2_rsp_hdr_off + 4; } /** @@ -101,7 +101,7 @@ static inline void *ksmbd_resp_buf_next(struct ksmbd_work *work) */ static inline void *ksmbd_req_buf_next(struct ksmbd_work *work) { - return work->request_buf + work->next_smb2_rcv_hdr_off; + return work->request_buf + work->next_smb2_rcv_hdr_off + 4; } struct ksmbd_work *ksmbd_alloc_work_struct(void); diff --git a/fs/ksmbd/mgmt/user_config.c b/fs/ksmbd/mgmt/user_config.c index d21629ae5c89..1019d3677d55 100644 --- a/fs/ksmbd/mgmt/user_config.c +++ b/fs/ksmbd/mgmt/user_config.c @@ -55,7 +55,7 @@ struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp) void ksmbd_free_user(struct ksmbd_user *user) { - ksmbd_ipc_logout_request(user->name); + ksmbd_ipc_logout_request(user->name, user->flags); kfree(user->name); kfree(user->passkey); kfree(user); diff --git a/fs/ksmbd/mgmt/user_config.h b/fs/ksmbd/mgmt/user_config.h index b2bb074a0150..aff80b029579 100644 --- a/fs/ksmbd/mgmt/user_config.h +++ b/fs/ksmbd/mgmt/user_config.h @@ -18,6 +18,7 @@ struct ksmbd_user { size_t passkey_sz; char *passkey; + unsigned int failed_login_count; }; static inline bool user_guest(struct ksmbd_user *user) diff --git a/fs/ksmbd/misc.c b/fs/ksmbd/misc.c index 0b307ca28a19..60e7ac62c917 100644 --- a/fs/ksmbd/misc.c +++ b/fs/ksmbd/misc.c @@ -158,25 +158,18 @@ out: * Return : windows path string or error */ -char *convert_to_nt_pathname(char *filename, char *sharepath) +char *convert_to_nt_pathname(char *filename) { char *ab_pathname; - int len, name_len; - name_len = strlen(filename); - ab_pathname = kmalloc(name_len, GFP_KERNEL); + if (strlen(filename) == 0) + filename = "\\"; + + ab_pathname = kstrdup(filename, GFP_KERNEL); if (!ab_pathname) return NULL; - ab_pathname[0] = '\\'; - ab_pathname[1] = '\0'; - - len = strlen(sharepath); - if (!strncmp(filename, sharepath, len) && name_len != len) { - strscpy(ab_pathname, &filename[len], name_len); - ksmbd_conv_path_to_windows(ab_pathname); - } - + ksmbd_conv_path_to_windows(ab_pathname); return ab_pathname; } @@ -240,7 +233,7 @@ char *ksmbd_extract_sharename(char *treename) * * Return: converted name on success, otherwise NULL */ -char *convert_to_unix_name(struct ksmbd_share_config *share, char *name) +char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name) { int no_slash = 0, name_len, path_len; char *new_name; diff --git a/fs/ksmbd/misc.h b/fs/ksmbd/misc.h index af8717d4d85b..253366bd0951 100644 --- a/fs/ksmbd/misc.h +++ b/fs/ksmbd/misc.h @@ -14,13 +14,13 @@ struct ksmbd_file; int match_pattern(const char *str, size_t len, const char *pattern); int ksmbd_validate_filename(char *filename); int parse_stream_name(char *filename, char **stream_name, int *s_type); -char *convert_to_nt_pathname(char *filename, char *sharepath); +char *convert_to_nt_pathname(char *filename); int get_nlink(struct kstat *st); void ksmbd_conv_path_to_unix(char *path); void ksmbd_strip_last_slash(char *path); void ksmbd_conv_path_to_windows(char *path); char *ksmbd_extract_sharename(char *treename); -char *convert_to_unix_name(struct ksmbd_share_config *share, char *name); +char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name); #define KSMBD_DIR_INFO_ALIGNMENT 8 struct ksmbd_dir_info; diff --git a/fs/ksmbd/oplock.c b/fs/ksmbd/oplock.c index 16b6236d1bd2..077b8761d099 100644 --- a/fs/ksmbd/oplock.c +++ b/fs/ksmbd/oplock.c @@ -629,10 +629,10 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) return; } - rsp_hdr = work->response_buf; + rsp_hdr = smb2_get_msg(work->response_buf); memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); - rsp_hdr->smb2_buf_length = - cpu_to_be32(smb2_hdr_size_no_buflen(conn->vals)); + *(__be32 *)work->response_buf = + cpu_to_be32(conn->vals->header_size); rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; rsp_hdr->CreditRequest = cpu_to_le16(0); @@ -645,7 +645,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) rsp_hdr->SessionId = 0; memset(rsp_hdr->Signature, 0, 16); - rsp = work->response_buf; + rsp = smb2_get_msg(work->response_buf); rsp->StructureSize = cpu_to_le16(24); if (!br_info->open_trunc && @@ -659,7 +659,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) rsp->PersistentFid = cpu_to_le64(fp->persistent_id); rsp->VolatileFid = cpu_to_le64(fp->volatile_id); - inc_rfc1001_len(rsp, 24); + inc_rfc1001_len(work->response_buf, 24); ksmbd_debug(OPLOCK, "sending oplock break v_id %llu p_id = %llu lock level = %d\n", @@ -736,10 +736,10 @@ static void __smb2_lease_break_noti(struct work_struct *wk) return; } - rsp_hdr = work->response_buf; + rsp_hdr = smb2_get_msg(work->response_buf); memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); - rsp_hdr->smb2_buf_length = - cpu_to_be32(smb2_hdr_size_no_buflen(conn->vals)); + *(__be32 *)work->response_buf = + cpu_to_be32(conn->vals->header_size); rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; rsp_hdr->CreditRequest = cpu_to_le16(0); @@ -752,7 +752,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk) rsp_hdr->SessionId = 0; memset(rsp_hdr->Signature, 0, 16); - rsp = work->response_buf; + rsp = smb2_get_msg(work->response_buf); rsp->StructureSize = cpu_to_le16(44); rsp->Epoch = br_info->epoch; rsp->Flags = 0; @@ -768,7 +768,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk) rsp->AccessMaskHint = 0; rsp->ShareMaskHint = 0; - inc_rfc1001_len(rsp, 44); + inc_rfc1001_len(work->response_buf, 44); ksmbd_conn_write(work); ksmbd_free_work_struct(work); @@ -1335,19 +1335,16 @@ __u8 smb2_map_lease_to_oplock(__le32 lease_state) */ void create_lease_buf(u8 *rbuf, struct lease *lease) { - char *LeaseKey = (char *)&lease->lease_key; - if (lease->version == 2) { struct create_lease_v2 *buf = (struct create_lease_v2 *)rbuf; - char *ParentLeaseKey = (char *)&lease->parent_lease_key; memset(buf, 0, sizeof(struct create_lease_v2)); - buf->lcontext.LeaseKeyLow = *((__le64 *)LeaseKey); - buf->lcontext.LeaseKeyHigh = *((__le64 *)(LeaseKey + 8)); + memcpy(buf->lcontext.LeaseKey, lease->lease_key, + SMB2_LEASE_KEY_SIZE); buf->lcontext.LeaseFlags = lease->flags; buf->lcontext.LeaseState = lease->state; - buf->lcontext.ParentLeaseKeyLow = *((__le64 *)ParentLeaseKey); - buf->lcontext.ParentLeaseKeyHigh = *((__le64 *)(ParentLeaseKey + 8)); + memcpy(buf->lcontext.ParentLeaseKey, lease->parent_lease_key, + SMB2_LEASE_KEY_SIZE); buf->ccontext.DataOffset = cpu_to_le16(offsetof (struct create_lease_v2, lcontext)); buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context_v2)); @@ -1362,8 +1359,7 @@ void create_lease_buf(u8 *rbuf, struct lease *lease) struct create_lease *buf = (struct create_lease *)rbuf; memset(buf, 0, sizeof(struct create_lease)); - buf->lcontext.LeaseKeyLow = *((__le64 *)LeaseKey); - buf->lcontext.LeaseKeyHigh = *((__le64 *)(LeaseKey + 8)); + memcpy(buf->lcontext.LeaseKey, lease->lease_key, SMB2_LEASE_KEY_SIZE); buf->lcontext.LeaseFlags = lease->flags; buf->lcontext.LeaseState = lease->state; buf->ccontext.DataOffset = cpu_to_le16(offsetof @@ -1398,7 +1394,7 @@ struct lease_ctx_info *parse_lease_state(void *open_req) if (!lreq) return NULL; - data_offset = (char *)req + 4 + le32_to_cpu(req->CreateContextsOffset); + data_offset = (char *)req + le32_to_cpu(req->CreateContextsOffset); cc = (struct create_context *)data_offset; do { cc = (struct create_context *)((char *)cc + next); @@ -1416,19 +1412,17 @@ struct lease_ctx_info *parse_lease_state(void *open_req) if (sizeof(struct lease_context_v2) == le32_to_cpu(cc->DataLength)) { struct create_lease_v2 *lc = (struct create_lease_v2 *)cc; - *((__le64 *)lreq->lease_key) = lc->lcontext.LeaseKeyLow; - *((__le64 *)(lreq->lease_key + 8)) = lc->lcontext.LeaseKeyHigh; + memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); lreq->req_state = lc->lcontext.LeaseState; lreq->flags = lc->lcontext.LeaseFlags; lreq->duration = lc->lcontext.LeaseDuration; - *((__le64 *)lreq->parent_lease_key) = lc->lcontext.ParentLeaseKeyLow; - *((__le64 *)(lreq->parent_lease_key + 8)) = lc->lcontext.ParentLeaseKeyHigh; + memcpy(lreq->parent_lease_key, lc->lcontext.ParentLeaseKey, + SMB2_LEASE_KEY_SIZE); lreq->version = 2; } else { struct create_lease *lc = (struct create_lease *)cc; - *((__le64 *)lreq->lease_key) = lc->lcontext.LeaseKeyLow; - *((__le64 *)(lreq->lease_key + 8)) = lc->lcontext.LeaseKeyHigh; + memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); lreq->req_state = lc->lcontext.LeaseState; lreq->flags = lc->lcontext.LeaseFlags; lreq->duration = lc->lcontext.LeaseDuration; @@ -1451,26 +1445,47 @@ struct lease_ctx_info *parse_lease_state(void *open_req) */ struct create_context *smb2_find_context_vals(void *open_req, const char *tag) { - char *data_offset; struct create_context *cc; unsigned int next = 0; char *name; struct smb2_create_req *req = (struct smb2_create_req *)open_req; + unsigned int remain_len, name_off, name_len, value_off, value_len, + cc_len; - data_offset = (char *)req + 4 + le32_to_cpu(req->CreateContextsOffset); - cc = (struct create_context *)data_offset; + /* + * CreateContextsOffset and CreateContextsLength are guaranteed to + * be valid because of ksmbd_smb2_check_message(). + */ + cc = (struct create_context *)((char *)req + + le32_to_cpu(req->CreateContextsOffset)); + remain_len = le32_to_cpu(req->CreateContextsLength); do { - int val; - cc = (struct create_context *)((char *)cc + next); - name = le16_to_cpu(cc->NameOffset) + (char *)cc; - val = le16_to_cpu(cc->NameLength); - if (val < 4) + if (remain_len < offsetof(struct create_context, Buffer)) return ERR_PTR(-EINVAL); - if (memcmp(name, tag, val) == 0) - return cc; next = le32_to_cpu(cc->Next); + name_off = le16_to_cpu(cc->NameOffset); + name_len = le16_to_cpu(cc->NameLength); + value_off = le16_to_cpu(cc->DataOffset); + value_len = le32_to_cpu(cc->DataLength); + cc_len = next ? next : remain_len; + + if ((next & 0x7) != 0 || + next > remain_len || + name_off != offsetof(struct create_context, Buffer) || + name_len < 4 || + name_off + name_len > cc_len || + (value_off & 0x7) != 0 || + (value_off && (value_off < name_off + name_len)) || + ((u64)value_off + value_len > cc_len)) + return ERR_PTR(-EINVAL); + + name = (char *)cc + name_off; + if (memcmp(name, tag, name_len) == 0) + return cc; + + remain_len -= next; } while (next != 0); return NULL; diff --git a/fs/ksmbd/oplock.h b/fs/ksmbd/oplock.h index 119b8047cfbd..0cf7a2b5bbc0 100644 --- a/fs/ksmbd/oplock.h +++ b/fs/ksmbd/oplock.h @@ -28,8 +28,6 @@ #define OPLOCK_WRITE_TO_NONE 0x04 #define OPLOCK_READ_TO_NONE 0x08 -#define SMB2_LEASE_KEY_SIZE 16 - struct lease_ctx_info { __u8 lease_key[SMB2_LEASE_KEY_SIZE]; __le32 req_state; diff --git a/fs/ksmbd/server.c b/fs/ksmbd/server.c index e6a9f6aa47eb..2e12f6d8483b 100644 --- a/fs/ksmbd/server.c +++ b/fs/ksmbd/server.c @@ -584,6 +584,9 @@ static int __init ksmbd_server_init(void) ret = ksmbd_workqueue_init(); if (ret) goto err_crypto_destroy; + + pr_warn_once("The ksmbd server is experimental, use at your own risk.\n"); + return 0; err_crypto_destroy: @@ -619,7 +622,6 @@ MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER"); MODULE_LICENSE("GPL"); MODULE_SOFTDEP("pre: ecb"); MODULE_SOFTDEP("pre: hmac"); -MODULE_SOFTDEP("pre: md4"); MODULE_SOFTDEP("pre: md5"); MODULE_SOFTDEP("pre: nls"); MODULE_SOFTDEP("pre: aes"); @@ -629,5 +631,6 @@ MODULE_SOFTDEP("pre: sha512"); MODULE_SOFTDEP("pre: aead2"); MODULE_SOFTDEP("pre: ccm"); MODULE_SOFTDEP("pre: gcm"); +MODULE_SOFTDEP("pre: crc32"); module_init(ksmbd_server_init) module_exit(ksmbd_server_exit) diff --git a/fs/ksmbd/smb2misc.c b/fs/ksmbd/smb2misc.c index 9aa46bb3e10d..50d0b1022289 100644 --- a/fs/ksmbd/smb2misc.c +++ b/fs/ksmbd/smb2misc.c @@ -6,7 +6,6 @@ #include "glob.h" #include "nterr.h" -#include "smb2pdu.h" #include "smb_common.h" #include "smbstatus.h" #include "mgmt/user_session.h" @@ -80,18 +79,21 @@ static const bool has_smb2_data_area[NUMBER_OF_SMB2_COMMANDS] = { }; /* - * Returns the pointer to the beginning of the data area. Length of the data - * area and the offset to it (from the beginning of the smb are also returned. + * Set length of the data area and the offset to arguments. + * if they are invalid, return error. */ -static char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr) +static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, + struct smb2_hdr *hdr) { + int ret = 0; + *off = 0; *len = 0; /* error reqeusts do not have data area */ if (hdr->Status && hdr->Status != STATUS_MORE_PROCESSING_REQUIRED && (((struct smb2_err_rsp *)hdr)->StructureSize) == SMB2_ERROR_STRUCTURE_SIZE2_LE) - return NULL; + return ret; /* * Following commands have data areas so we have to get the location @@ -165,69 +167,60 @@ static char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr) case SMB2_IOCTL: *off = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputOffset); *len = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputCount); - break; default: ksmbd_debug(SMB, "no length check for command\n"); break; } - /* - * Invalid length or offset probably means data area is invalid, but - * we have little choice but to ignore the data area in this case. - */ if (*off > 4096) { - ksmbd_debug(SMB, "offset %d too large, data area ignored\n", - *off); - *len = 0; - *off = 0; - } else if (*off < 0) { - ksmbd_debug(SMB, - "negative offset %d to data invalid ignore data area\n", - *off); - *off = 0; - *len = 0; - } else if (*len < 0) { - ksmbd_debug(SMB, - "negative data length %d invalid, data area ignored\n", - *len); - *len = 0; - } else if (*len > 128 * 1024) { - ksmbd_debug(SMB, "data area larger than 128K: %d\n", *len); - *len = 0; + ksmbd_debug(SMB, "offset %d too large\n", *off); + ret = -EINVAL; + } else if ((u64)*off + *len > MAX_STREAM_PROT_LEN) { + ksmbd_debug(SMB, "Request is larger than maximum stream protocol length(%u): %llu\n", + MAX_STREAM_PROT_LEN, (u64)*off + *len); + ret = -EINVAL; } - /* return pointer to beginning of data area, ie offset from SMB start */ - if ((*off != 0) && (*len != 0)) - return (char *)hdr + *off; - else - return NULL; + return ret; } /* * Calculate the size of the SMB message based on the fixed header * portion, the number of word parameters and the data portion of the message. */ -static unsigned int smb2_calc_size(void *buf) +static int smb2_calc_size(void *buf, unsigned int *len) { struct smb2_pdu *pdu = (struct smb2_pdu *)buf; struct smb2_hdr *hdr = &pdu->hdr; - int offset; /* the offset from the beginning of SMB to data area */ - int data_length; /* the length of the variable length data area */ + unsigned int offset; /* the offset from the beginning of SMB to data area */ + unsigned int data_length; /* the length of the variable length data area */ + int ret; + /* Structure Size has already been checked to make sure it is 64 */ - int len = le16_to_cpu(hdr->StructureSize); + *len = le16_to_cpu(hdr->StructureSize); /* * StructureSize2, ie length of fixed parameter area has already * been checked to make sure it is the correct length. */ - len += le16_to_cpu(pdu->StructureSize2); + *len += le16_to_cpu(pdu->StructureSize2); + /* + * StructureSize2 of smb2_lock pdu is set to 48, indicating + * the size of smb2 lock request with single smb2_lock_element + * regardless of number of locks. Subtract single + * smb2_lock_element for correct buffer size check. + */ + if (hdr->Command == SMB2_LOCK) + *len -= sizeof(struct smb2_lock_element); if (has_smb2_data_area[le16_to_cpu(hdr->Command)] == false) goto calc_size_exit; - smb2_get_data_area_len(&offset, &data_length, hdr); - ksmbd_debug(SMB, "SMB2 data length %d offset %d\n", data_length, + ret = smb2_get_data_area_len(&offset, &data_length, hdr); + if (ret) + return ret; + ksmbd_debug(SMB, "SMB2 data length %u offset %u\n", data_length, offset); if (data_length > 0) { @@ -237,16 +230,19 @@ static unsigned int smb2_calc_size(void *buf) * for some commands, typically those with odd StructureSize, * so we must add one to the calculation. */ - if (offset + 1 < len) + if (offset + 1 < *len) { ksmbd_debug(SMB, - "data area offset %d overlaps SMB2 header %d\n", - offset + 1, len); - else - len = offset + data_length; + "data area offset %d overlaps SMB2 header %u\n", + offset + 1, *len); + return -EINVAL; + } + + *len = offset + data_length; } + calc_size_exit: - ksmbd_debug(SMB, "SMB2 len %d\n", len); - return len; + ksmbd_debug(SMB, "SMB2 len %u\n", *len); + return 0; } static inline int smb2_query_info_req_len(struct smb2_query_info_req *h) @@ -287,11 +283,13 @@ static inline int smb2_ioctl_resp_len(struct smb2_ioctl_req *h) le32_to_cpu(h->MaxOutputResponse); } -static int smb2_validate_credit_charge(struct smb2_hdr *hdr) +static int smb2_validate_credit_charge(struct ksmbd_conn *conn, + struct smb2_hdr *hdr) { - int req_len = 0, expect_resp_len = 0, calc_credit_num, max_len; - int credit_charge = le16_to_cpu(hdr->CreditCharge); + unsigned int req_len = 0, expect_resp_len = 0, calc_credit_num, max_len; + unsigned short credit_charge = le16_to_cpu(hdr->CreditCharge); void *__hdr = hdr; + int ret; switch (hdr->Command) { case SMB2_QUERY_INFO: @@ -313,42 +311,51 @@ static int smb2_validate_credit_charge(struct smb2_hdr *hdr) req_len = smb2_ioctl_req_len(__hdr); expect_resp_len = smb2_ioctl_resp_len(__hdr); break; - default: + case SMB2_CANCEL: return 0; + default: + req_len = 1; + break; } - credit_charge = max(1, credit_charge); - max_len = max(req_len, expect_resp_len); + credit_charge = max_t(unsigned short, credit_charge, 1); + max_len = max_t(unsigned int, req_len, expect_resp_len); calc_credit_num = DIV_ROUND_UP(max_len, SMB2_MAX_BUFFER_SIZE); if (credit_charge < calc_credit_num) { - pr_err("Insufficient credit charge, given: %d, needed: %d\n", - credit_charge, calc_credit_num); + ksmbd_debug(SMB, "Insufficient credit charge, given: %d, needed: %d\n", + credit_charge, calc_credit_num); + return 1; + } else if (credit_charge > conn->max_credits) { + ksmbd_debug(SMB, "Too large credit charge: %d\n", credit_charge); return 1; } - return 0; + spin_lock(&conn->credits_lock); + if (credit_charge <= conn->total_credits) { + conn->total_credits -= credit_charge; + ret = 0; + } else { + ksmbd_debug(SMB, "Insufficient credits granted, given: %u, granted: %u\n", + credit_charge, conn->total_credits); + ret = 1; + } + spin_unlock(&conn->credits_lock); + return ret; } int ksmbd_smb2_check_message(struct ksmbd_work *work) { - struct smb2_pdu *pdu = work->request_buf; + struct smb2_pdu *pdu = ksmbd_req_buf_next(work); struct smb2_hdr *hdr = &pdu->hdr; int command; __u32 clc_len; /* calculated length */ - __u32 len = get_rfc1002_len(pdu); - - if (work->next_smb2_rcv_hdr_off) { - pdu = ksmbd_req_buf_next(work); - hdr = &pdu->hdr; - } + __u32 len = get_rfc1002_len(work->request_buf); - if (le32_to_cpu(hdr->NextCommand) > 0) { + if (le32_to_cpu(hdr->NextCommand) > 0) len = le32_to_cpu(hdr->NextCommand); - } else if (work->next_smb2_rcv_hdr_off) { + else if (work->next_smb2_rcv_hdr_off) len -= work->next_smb2_rcv_hdr_off; - len = round_up(len, 8); - } if (check_smb2_hdr(hdr)) return 1; @@ -385,24 +392,20 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) } } - if ((work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) && - smb2_validate_credit_charge(hdr)) { - work->conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); + if (smb2_calc_size(hdr, &clc_len)) return 1; - } - clc_len = smb2_calc_size(hdr); if (len != clc_len) { - /* server can return one byte more due to implied bcc[0] */ + /* client can return one byte more due to implied bcc[0] */ if (clc_len == len + 1) - return 0; + goto validate_credit; /* * Some windows servers (win2016) will pad also the final * PDU in a compound to 8 bytes. */ if (ALIGN(clc_len, 8) == len) - return 0; + goto validate_credit; /* * windows client also pad up to 8 bytes when compounding. @@ -415,12 +418,9 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) "cli req padded more than expected. Length %d not %d for cmd:%d mid:%llu\n", len, clc_len, command, le64_to_cpu(hdr->MessageId)); - return 0; + goto validate_credit; } - if (command == SMB2_LOCK_HE && len == 88) - return 0; - ksmbd_debug(SMB, "cli req too short, len %d not %d. cmd:%d mid:%llu\n", len, clc_len, command, @@ -429,6 +429,13 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) return 1; } +validate_credit: + if ((work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) && + smb2_validate_credit_charge(work->conn, hdr)) { + work->conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); + return 1; + } + return 0; } diff --git a/fs/ksmbd/smb2ops.c b/fs/ksmbd/smb2ops.c index 197473871aa4..0a5d8450e835 100644 --- a/fs/ksmbd/smb2ops.c +++ b/fs/ksmbd/smb2ops.c @@ -6,7 +6,6 @@ #include <linux/slab.h> #include "glob.h" -#include "smb2pdu.h" #include "auth.h" #include "connection.h" @@ -187,11 +186,6 @@ static struct smb_version_cmds smb2_0_server_cmds[NUMBER_OF_SMB2_COMMANDS] = { [SMB2_CHANGE_NOTIFY_HE] = { .proc = smb2_notify}, }; -int init_smb2_0_server(struct ksmbd_conn *conn) -{ - return -EOPNOTSUPP; -} - /** * init_smb2_1_server() - initialize a smb server connection with smb2.1 * command dispatcher @@ -204,7 +198,7 @@ void init_smb2_1_server(struct ksmbd_conn *conn) conn->cmds = smb2_0_server_cmds; conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); conn->max_credits = SMB2_MAX_CREDITS; - conn->signing_algorithm = SIGNING_ALG_HMAC_SHA256; + conn->signing_algorithm = SIGNING_ALG_HMAC_SHA256_LE; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; @@ -222,7 +216,7 @@ void init_smb3_0_server(struct ksmbd_conn *conn) conn->cmds = smb2_0_server_cmds; conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); conn->max_credits = SMB2_MAX_CREDITS; - conn->signing_algorithm = SIGNING_ALG_AES_CMAC; + conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; @@ -247,7 +241,7 @@ void init_smb3_02_server(struct ksmbd_conn *conn) conn->cmds = smb2_0_server_cmds; conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); conn->max_credits = SMB2_MAX_CREDITS; - conn->signing_algorithm = SIGNING_ALG_AES_CMAC; + conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; @@ -272,7 +266,7 @@ int init_smb3_11_server(struct ksmbd_conn *conn) conn->cmds = smb2_0_server_cmds; conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); conn->max_credits = SMB2_MAX_CREDITS; - conn->signing_algorithm = SIGNING_ALG_AES_CMAC; + conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; @@ -289,6 +283,7 @@ int init_smb3_11_server(struct ksmbd_conn *conn) void init_smb2_max_read_size(unsigned int sz) { + sz = clamp_val(sz, SMB3_MIN_IOSIZE, SMB3_MAX_IOSIZE); smb21_server_values.max_read_size = sz; smb30_server_values.max_read_size = sz; smb302_server_values.max_read_size = sz; @@ -297,6 +292,7 @@ void init_smb2_max_read_size(unsigned int sz) void init_smb2_max_write_size(unsigned int sz) { + sz = clamp_val(sz, SMB3_MIN_IOSIZE, SMB3_MAX_IOSIZE); smb21_server_values.max_write_size = sz; smb30_server_values.max_write_size = sz; smb302_server_values.max_write_size = sz; @@ -305,6 +301,7 @@ void init_smb2_max_write_size(unsigned int sz) void init_smb2_max_trans_size(unsigned int sz) { + sz = clamp_val(sz, SMB3_MIN_IOSIZE, SMB3_MAX_IOSIZE); smb21_server_values.max_trans_size = sz; smb30_server_values.max_trans_size = sz; smb302_server_values.max_trans_size = sz; diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index c86164dc70bb..121f8e8c70ac 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -13,7 +13,6 @@ #include <linux/falloc.h> #include "glob.h" -#include "smb2pdu.h" #include "smbfsctl.h" #include "oplock.h" #include "smbacl.h" @@ -44,8 +43,8 @@ static void __wbuf(struct ksmbd_work *work, void **req, void **rsp) *req = ksmbd_req_buf_next(work); *rsp = ksmbd_resp_buf_next(work); } else { - *req = work->request_buf; - *rsp = work->response_buf; + *req = smb2_get_msg(work->request_buf); + *rsp = smb2_get_msg(work->response_buf); } } @@ -93,13 +92,14 @@ struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn */ int smb2_get_ksmbd_tcon(struct ksmbd_work *work) { - struct smb2_hdr *req_hdr = work->request_buf; + struct smb2_hdr *req_hdr = smb2_get_msg(work->request_buf); + unsigned int cmd = le16_to_cpu(req_hdr->Command); int tree_id; work->tcon = NULL; - if (work->conn->ops->get_cmd_val(work) == SMB2_TREE_CONNECT_HE || - work->conn->ops->get_cmd_val(work) == SMB2_CANCEL_HE || - work->conn->ops->get_cmd_val(work) == SMB2_LOGOFF_HE) { + if (cmd == SMB2_TREE_CONNECT_HE || + cmd == SMB2_CANCEL_HE || + cmd == SMB2_LOGOFF_HE) { ksmbd_debug(SMB, "skip to check tree connect request\n"); return 0; } @@ -130,7 +130,7 @@ void smb2_set_err_rsp(struct ksmbd_work *work) if (work->next_smb2_rcv_hdr_off) err_rsp = ksmbd_resp_buf_next(work); else - err_rsp = work->response_buf; + err_rsp = smb2_get_msg(work->response_buf); if (err_rsp->hdr.Status != STATUS_STOPPED_ON_SYMLINK) { err_rsp->StructureSize = SMB2_ERROR_STRUCTURE_SIZE2_LE; @@ -150,7 +150,7 @@ void smb2_set_err_rsp(struct ksmbd_work *work) */ bool is_smb2_neg_cmd(struct ksmbd_work *work) { - struct smb2_hdr *hdr = work->request_buf; + struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); /* is it SMB2 header ? */ if (hdr->ProtocolId != SMB2_PROTO_NUMBER) @@ -174,7 +174,7 @@ bool is_smb2_neg_cmd(struct ksmbd_work *work) */ bool is_smb2_rsp(struct ksmbd_work *work) { - struct smb2_hdr *hdr = work->response_buf; + struct smb2_hdr *hdr = smb2_get_msg(work->response_buf); /* is it SMB2 header ? */ if (hdr->ProtocolId != SMB2_PROTO_NUMBER) @@ -200,7 +200,7 @@ u16 get_smb2_cmd_val(struct ksmbd_work *work) if (work->next_smb2_rcv_hdr_off) rcv_hdr = ksmbd_req_buf_next(work); else - rcv_hdr = work->request_buf; + rcv_hdr = smb2_get_msg(work->request_buf); return le16_to_cpu(rcv_hdr->Command); } @@ -216,7 +216,7 @@ void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err) if (work->next_smb2_rcv_hdr_off) rsp_hdr = ksmbd_resp_buf_next(work); else - rsp_hdr = work->response_buf; + rsp_hdr = smb2_get_msg(work->response_buf); rsp_hdr->Status = err; smb2_set_err_rsp(work); } @@ -236,17 +236,12 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) if (conn->need_neg == false) return -EINVAL; - if (!(conn->dialect >= SMB20_PROT_ID && - conn->dialect <= SMB311_PROT_ID)) - return -EINVAL; - rsp_hdr = work->response_buf; + *(__be32 *)work->response_buf = + cpu_to_be32(conn->vals->header_size); + rsp_hdr = smb2_get_msg(work->response_buf); memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); - - rsp_hdr->smb2_buf_length = - cpu_to_be32(smb2_hdr_size_no_buflen(conn->vals)); - rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; rsp_hdr->CreditRequest = cpu_to_le16(2); @@ -259,7 +254,7 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) rsp_hdr->SessionId = 0; memset(rsp_hdr->Signature, 0, 16); - rsp = work->response_buf; + rsp = smb2_get_msg(work->response_buf); WARN_ON(ksmbd_conn_good(work)); @@ -280,12 +275,12 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) rsp->SecurityBufferOffset = cpu_to_le16(128); rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); - ksmbd_copy_gss_neg_header(((char *)(&rsp->hdr) + - sizeof(rsp->hdr.smb2_buf_length)) + + ksmbd_copy_gss_neg_header((char *)(&rsp->hdr) + le16_to_cpu(rsp->SecurityBufferOffset)); - inc_rfc1001_len(rsp, sizeof(struct smb2_negotiate_rsp) - - sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) + - AUTH_GSS_LENGTH); + inc_rfc1001_len(work->response_buf, + sizeof(struct smb2_negotiate_rsp) - + sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) + + AUTH_GSS_LENGTH); rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE; @@ -295,22 +290,6 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) return 0; } -static int smb2_consume_credit_charge(struct ksmbd_work *work, - unsigned short credit_charge) -{ - struct ksmbd_conn *conn = work->conn; - unsigned int rsp_credits = 1; - - if (!conn->total_credits) - return 0; - - if (credit_charge > 0) - rsp_credits = credit_charge; - - conn->total_credits -= rsp_credits; - return rsp_credits; -} - /** * smb2_set_rsp_credits() - set number of credits in response buffer * @work: smb work containing smb response buffer @@ -320,49 +299,43 @@ int smb2_set_rsp_credits(struct ksmbd_work *work) struct smb2_hdr *req_hdr = ksmbd_req_buf_next(work); struct smb2_hdr *hdr = ksmbd_resp_buf_next(work); struct ksmbd_conn *conn = work->conn; - unsigned short credits_requested = le16_to_cpu(req_hdr->CreditRequest); - unsigned short credit_charge = 1, credits_granted = 0; - unsigned short aux_max, aux_credits, min_credits; - int rsp_credit_charge; + unsigned short credits_requested; + unsigned short credit_charge, credits_granted = 0; + unsigned short aux_max, aux_credits; - if (hdr->Command == SMB2_CANCEL) - goto out; + if (work->send_no_response) + return 0; - /* get default minimum credits by shifting maximum credits by 4 */ - min_credits = conn->max_credits >> 4; + hdr->CreditCharge = req_hdr->CreditCharge; - if (conn->total_credits >= conn->max_credits) { + if (conn->total_credits > conn->max_credits) { + hdr->CreditRequest = 0; pr_err("Total credits overflow: %d\n", conn->total_credits); - conn->total_credits = min_credits; - } - - rsp_credit_charge = - smb2_consume_credit_charge(work, le16_to_cpu(req_hdr->CreditCharge)); - if (rsp_credit_charge < 0) return -EINVAL; + } - hdr->CreditCharge = cpu_to_le16(rsp_credit_charge); + credit_charge = max_t(unsigned short, + le16_to_cpu(req_hdr->CreditCharge), 1); + credits_requested = max_t(unsigned short, + le16_to_cpu(req_hdr->CreditRequest), 1); - if (credits_requested > 0) { - aux_credits = credits_requested - 1; - aux_max = 32; - if (hdr->Command == SMB2_NEGOTIATE) - aux_max = 0; - aux_credits = (aux_credits < aux_max) ? aux_credits : aux_max; - credits_granted = aux_credits + credit_charge; + /* according to smb2.credits smbtorture, Windows server + * 2016 or later grant up to 8192 credits at once. + * + * TODO: Need to adjuct CreditRequest value according to + * current cpu load + */ + aux_credits = credits_requested - 1; + if (hdr->Command == SMB2_NEGOTIATE) + aux_max = 0; + else + aux_max = conn->max_credits - credit_charge; + aux_credits = min_t(unsigned short, aux_credits, aux_max); + credits_granted = credit_charge + aux_credits; - /* if credits granted per client is getting bigger than default - * minimum credits then we should wrap it up within the limits. - */ - if ((conn->total_credits + credits_granted) > min_credits) - credits_granted = min_credits - conn->total_credits; - /* - * TODO: Need to adjuct CreditRequest value according to - * current cpu load - */ - } else if (conn->total_credits == 0) { - credits_granted = 1; - } + if (conn->max_credits - conn->total_credits < credits_granted) + credits_granted = conn->max_credits - + conn->total_credits; conn->total_credits += credits_granted; work->credits_granted += credits_granted; @@ -371,7 +344,6 @@ int smb2_set_rsp_credits(struct ksmbd_work *work) /* Update CreditRequest in last request */ hdr->CreditRequest = cpu_to_le16(work->credits_granted); } -out: ksmbd_debug(SMB, "credits: requested[%d] granted[%d] total_granted[%d]\n", credits_requested, credits_granted, @@ -413,8 +385,8 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work) next_hdr_offset = le32_to_cpu(req->NextCommand); new_len = ALIGN(len, 8); - inc_rfc1001_len(work->response_buf, ((sizeof(struct smb2_hdr) - 4) - + new_len - len)); + inc_rfc1001_len(work->response_buf, + sizeof(struct smb2_hdr) + new_len - len); rsp->NextCommand = cpu_to_le32(new_len); work->next_smb2_rcv_hdr_off += next_hdr_offset; @@ -432,8 +404,8 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work) work->compound_fid = KSMBD_NO_FID; work->compound_pfid = KSMBD_NO_FID; } - memset((char *)rsp_hdr + 4, 0, sizeof(struct smb2_hdr) + 2); - rsp_hdr->ProtocolId = rcv_hdr->ProtocolId; + memset((char *)rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); + rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; rsp_hdr->Command = rcv_hdr->Command; @@ -458,14 +430,29 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work) */ bool is_chained_smb2_message(struct ksmbd_work *work) { - struct smb2_hdr *hdr = work->request_buf; - unsigned int len; + struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); + unsigned int len, next_cmd; if (hdr->ProtocolId != SMB2_PROTO_NUMBER) return false; hdr = ksmbd_req_buf_next(work); - if (le32_to_cpu(hdr->NextCommand) > 0) { + next_cmd = le32_to_cpu(hdr->NextCommand); + if (next_cmd > 0) { + if ((u64)work->next_smb2_rcv_hdr_off + next_cmd + + __SMB2_HEADER_STRUCTURE_SIZE > + get_rfc1002_len(work->request_buf)) { + pr_err("next command(%u) offset exceeds smb msg size\n", + next_cmd); + return false; + } + + if ((u64)get_rfc1002_len(work->response_buf) + MAX_CIFS_SMALL_BUFFER_SIZE > + work->response_sz) { + pr_err("next response offset exceeds response buffer size\n"); + return false; + } + ksmbd_debug(SMB, "got SMB2 chained command\n"); init_chained_smb2_rsp(work); return true; @@ -494,13 +481,13 @@ bool is_chained_smb2_message(struct ksmbd_work *work) */ int init_smb2_rsp_hdr(struct ksmbd_work *work) { - struct smb2_hdr *rsp_hdr = work->response_buf; - struct smb2_hdr *rcv_hdr = work->request_buf; + struct smb2_hdr *rsp_hdr = smb2_get_msg(work->response_buf); + struct smb2_hdr *rcv_hdr = smb2_get_msg(work->request_buf); struct ksmbd_conn *conn = work->conn; memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); - rsp_hdr->smb2_buf_length = - cpu_to_be32(smb2_hdr_size_no_buflen(conn->vals)); + *(__be32 *)work->response_buf = + cpu_to_be32(conn->vals->header_size); rsp_hdr->ProtocolId = rcv_hdr->ProtocolId; rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; rsp_hdr->Command = rcv_hdr->Command; @@ -533,9 +520,9 @@ int init_smb2_rsp_hdr(struct ksmbd_work *work) */ int smb2_allocate_rsp_buf(struct ksmbd_work *work) { - struct smb2_hdr *hdr = work->request_buf; + struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); size_t small_sz = MAX_CIFS_SMALL_BUFFER_SIZE; - size_t large_sz = work->conn->vals->max_trans_size + MAX_SMB2_HDR_SIZE; + size_t large_sz = small_sz + work->conn->vals->max_trans_size; size_t sz = small_sz; int cmd = le16_to_cpu(hdr->Command); @@ -545,7 +532,7 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work) if (cmd == SMB2_QUERY_INFO_HE) { struct smb2_query_info_req *req; - req = work->request_buf; + req = smb2_get_msg(work->request_buf); if (req->InfoType == SMB2_O_INFO_FILE && (req->FileInfoClass == FILE_FULL_EA_INFORMATION || req->FileInfoClass == FILE_ALL_INFORMATION)) @@ -572,7 +559,7 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work) */ int smb2_check_user_session(struct ksmbd_work *work) { - struct smb2_hdr *req_hdr = work->request_buf; + struct smb2_hdr *req_hdr = smb2_get_msg(work->request_buf); struct ksmbd_conn *conn = work->conn; unsigned int cmd = conn->ops->get_cmd_val(work); unsigned long long sess_id; @@ -634,7 +621,7 @@ static char * smb2_get_name(struct ksmbd_share_config *share, const char *src, const int maxlen, struct nls_table *local_nls) { - char *name, *unixname; + char *name; name = smb_strndup_from_utf16(src, maxlen, 1, local_nls); if (IS_ERR(name)) { @@ -642,19 +629,9 @@ smb2_get_name(struct ksmbd_share_config *share, const char *src, return name; } - /* change it to absolute unix name */ ksmbd_conv_path_to_unix(name); ksmbd_strip_last_slash(name); - - unixname = convert_to_unix_name(share, name); - kfree(name); - if (!unixname) { - pr_err("can not convert absolute name\n"); - return ERR_PTR(-ENOMEM); - } - - ksmbd_debug(SMB, "absolute name = %s\n", unixname); - return unixname; + return name; } int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) @@ -663,7 +640,7 @@ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) struct ksmbd_conn *conn = work->conn; int id; - rsp_hdr = work->response_buf; + rsp_hdr = smb2_get_msg(work->response_buf); rsp_hdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND; id = ksmbd_acquire_async_msg_id(&conn->async_ida); @@ -695,7 +672,7 @@ void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status) { struct smb2_hdr *rsp_hdr; - rsp_hdr = work->response_buf; + rsp_hdr = smb2_get_msg(work->response_buf); smb2_set_err_rsp(work); rsp_hdr->Status = status; @@ -736,17 +713,17 @@ static int smb2_get_dos_mode(struct kstat *stat, int attribute) int attr = 0; if (S_ISDIR(stat->mode)) { - attr = ATTR_DIRECTORY | - (attribute & (ATTR_HIDDEN | ATTR_SYSTEM)); + attr = FILE_ATTRIBUTE_DIRECTORY | + (attribute & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)); } else { - attr = (attribute & 0x00005137) | ATTR_ARCHIVE; - attr &= ~(ATTR_DIRECTORY); + attr = (attribute & 0x00005137) | FILE_ATTRIBUTE_ARCHIVE; + attr &= ~(FILE_ATTRIBUTE_DIRECTORY); if (S_ISREG(stat->mode) && (server_conf.share_fake_fscaps & FILE_SUPPORTS_SPARSE_FILES)) - attr |= ATTR_SPARSE; + attr |= FILE_ATTRIBUTE_SPARSE_FILE; if (smb2_get_reparse_tag_special_file(stat->mode)) - attr |= ATTR_REPARSE; + attr |= FILE_ATTRIBUTE_REPARSE_POINT; } return attr; @@ -774,16 +751,16 @@ static void build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt, pneg_ctxt->Ciphers[0] = cipher_type; } -static void build_compression_ctxt(struct smb2_compression_ctx *pneg_ctxt, +static void build_compression_ctxt(struct smb2_compression_capabilities_context *pneg_ctxt, __le16 comp_algo) { pneg_ctxt->ContextType = SMB2_COMPRESSION_CAPABILITIES; pneg_ctxt->DataLength = - cpu_to_le16(sizeof(struct smb2_compression_ctx) + cpu_to_le16(sizeof(struct smb2_compression_capabilities_context) - sizeof(struct smb2_neg_context)); pneg_ctxt->Reserved = cpu_to_le32(0); pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(1); - pneg_ctxt->Reserved1 = cpu_to_le32(0); + pneg_ctxt->Flags = cpu_to_le32(0); pneg_ctxt->CompressionAlgorithms[0] = comp_algo; } @@ -823,11 +800,11 @@ static void build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) } static void assemble_neg_contexts(struct ksmbd_conn *conn, - struct smb2_negotiate_rsp *rsp) + struct smb2_negotiate_rsp *rsp, + void *smb2_buf_len) { - /* +4 is to account for the RFC1001 len field */ char *pneg_ctxt = (char *)rsp + - le32_to_cpu(rsp->NegotiateContextOffset) + 4; + le32_to_cpu(rsp->NegotiateContextOffset); int neg_ctxt_cnt = 1; int ctxt_size; @@ -836,7 +813,7 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt, conn->preauth_info->Preauth_HashId); rsp->NegotiateContextCount = cpu_to_le16(neg_ctxt_cnt); - inc_rfc1001_len(rsp, AUTH_GSS_PADDING); + inc_rfc1001_len(smb2_buf_len, AUTH_GSS_PADDING); ctxt_size = sizeof(struct smb2_preauth_neg_context); /* Round to 8 byte boundary */ pneg_ctxt += round_up(sizeof(struct smb2_preauth_neg_context), 8); @@ -860,12 +837,12 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, ksmbd_debug(SMB, "assemble SMB2_COMPRESSION_CAPABILITIES context\n"); /* Temporarily set to SMB3_COMPRESS_NONE */ - build_compression_ctxt((struct smb2_compression_ctx *)pneg_ctxt, + build_compression_ctxt((struct smb2_compression_capabilities_context *)pneg_ctxt, conn->compress_algorithm); rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); - ctxt_size += sizeof(struct smb2_compression_ctx) + 2; + ctxt_size += sizeof(struct smb2_compression_capabilities_context) + 2; /* Round to 8 byte boundary */ - pneg_ctxt += round_up(sizeof(struct smb2_compression_ctx) + 2, + pneg_ctxt += round_up(sizeof(struct smb2_compression_capabilities_context) + 2, 8); } @@ -890,7 +867,7 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, ctxt_size += sizeof(struct smb2_signing_capabilities) + 2; } - inc_rfc1001_len(rsp, ctxt_size); + inc_rfc1001_len(smb2_buf_len, ctxt_size); } static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn, @@ -939,7 +916,7 @@ static void decode_encrypt_ctxt(struct ksmbd_conn *conn, } static void decode_compress_ctxt(struct ksmbd_conn *conn, - struct smb2_compression_ctx *pneg_ctxt) + struct smb2_compression_capabilities_context *pneg_ctxt) { conn->compress_algorithm = SMB3_COMPRESS_NONE; } @@ -960,8 +937,8 @@ static void decode_sign_cap_ctxt(struct ksmbd_conn *conn, } for (i = 0; i < sign_algo_cnt; i++) { - if (pneg_ctxt->SigningAlgorithms[i] == SIGNING_ALG_HMAC_SHA256 || - pneg_ctxt->SigningAlgorithms[i] == SIGNING_ALG_AES_CMAC) { + if (pneg_ctxt->SigningAlgorithms[i] == SIGNING_ALG_HMAC_SHA256_LE || + pneg_ctxt->SigningAlgorithms[i] == SIGNING_ALG_AES_CMAC_LE) { ksmbd_debug(SMB, "Signing Algorithm ID = 0x%x\n", pneg_ctxt->SigningAlgorithms[i]); conn->signing_negotiated = true; @@ -973,14 +950,14 @@ static void decode_sign_cap_ctxt(struct ksmbd_conn *conn, } static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, - struct smb2_negotiate_req *req) + struct smb2_negotiate_req *req, + int len_of_smb) { /* +4 is to account for the RFC1001 len field */ - struct smb2_neg_context *pctx = (struct smb2_neg_context *)((char *)req + 4); + struct smb2_neg_context *pctx = (struct smb2_neg_context *)req; int i = 0, len_of_ctxts; int offset = le32_to_cpu(req->NegotiateContextOffset); int neg_ctxt_cnt = le16_to_cpu(req->NegotiateContextCount); - int len_of_smb = be32_to_cpu(req->hdr.smb2_buf_length); __le32 status = STATUS_INVALID_PARAMETER; ksmbd_debug(SMB, "decoding %d negotiate contexts\n", neg_ctxt_cnt); @@ -1032,7 +1009,7 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, break; decode_compress_ctxt(conn, - (struct smb2_compression_ctx *)pctx); + (struct smb2_compression_capabilities_context *)pctx); } else if (pctx->ContextType == SMB2_NETNAME_NEGOTIATE_CONTEXT_ID) { ksmbd_debug(SMB, "deassemble SMB2_NETNAME_NEGOTIATE_CONTEXT_ID context\n"); @@ -1065,9 +1042,10 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, int smb2_handle_negotiate(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_negotiate_req *req = work->request_buf; - struct smb2_negotiate_rsp *rsp = work->response_buf; + struct smb2_negotiate_req *req = smb2_get_msg(work->request_buf); + struct smb2_negotiate_rsp *rsp = smb2_get_msg(work->response_buf); int rc = 0; + unsigned int smb2_buf_len, smb2_neg_size; __le32 status; ksmbd_debug(SMB, "Received negotiate request\n"); @@ -1085,6 +1063,44 @@ int smb2_handle_negotiate(struct ksmbd_work *work) goto err_out; } + smb2_buf_len = get_rfc1002_len(work->request_buf); + smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects); + if (smb2_neg_size > smb2_buf_len) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + + if (conn->dialect == SMB311_PROT_ID) { + unsigned int nego_ctxt_off = le32_to_cpu(req->NegotiateContextOffset); + + if (smb2_buf_len < nego_ctxt_off) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + + if (smb2_neg_size > nego_ctxt_off) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + + if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > + nego_ctxt_off) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + } else { + if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > + smb2_buf_len) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + } + conn->cli_cap = le32_to_cpu(req->Capabilities); switch (conn->dialect) { case SMB311_PROT_ID: @@ -1097,7 +1113,8 @@ int smb2_handle_negotiate(struct ksmbd_work *work) goto err_out; } - status = deassemble_neg_contexts(conn, req); + status = deassemble_neg_contexts(conn, req, + get_rfc1002_len(work->request_buf)); if (status != STATUS_SUCCESS) { pr_err("deassemble_neg_contexts error(0x%x)\n", status); @@ -1117,7 +1134,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work) conn->preauth_info->Preauth_HashValue); rsp->NegotiateContextOffset = cpu_to_le32(OFFSET_OF_NEG_CONTEXT); - assemble_neg_contexts(conn, rsp); + assemble_neg_contexts(conn, rsp, work->response_buf); break; case SMB302_PROT_ID: init_smb3_02_server(conn); @@ -1128,13 +1145,6 @@ int smb2_handle_negotiate(struct ksmbd_work *work) case SMB21_PROT_ID: init_smb2_1_server(conn); break; - case SMB20_PROT_ID: - rc = init_smb2_0_server(conn); - if (rc) { - rsp->hdr.Status = STATUS_NOT_SUPPORTED; - goto err_out; - } - break; case SMB2X_PROT_ID: case BAD_PROT_ID: default: @@ -1153,11 +1163,9 @@ int smb2_handle_negotiate(struct ksmbd_work *work) rsp->MaxReadSize = cpu_to_le32(conn->vals->max_read_size); rsp->MaxWriteSize = cpu_to_le32(conn->vals->max_write_size); - if (conn->dialect > SMB20_PROT_ID) { - memcpy(conn->ClientGUID, req->ClientGUID, - SMB2_CLIENT_GUID_SIZE); - conn->cli_sec_mode = le16_to_cpu(req->SecurityMode); - } + memcpy(conn->ClientGUID, req->ClientGUID, + SMB2_CLIENT_GUID_SIZE); + conn->cli_sec_mode = le16_to_cpu(req->SecurityMode); rsp->StructureSize = cpu_to_le16(65); rsp->DialectRevision = cpu_to_le16(conn->dialect); @@ -1174,10 +1182,9 @@ int smb2_handle_negotiate(struct ksmbd_work *work) rsp->SecurityBufferOffset = cpu_to_le16(128); rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); - ksmbd_copy_gss_neg_header(((char *)(&rsp->hdr) + - sizeof(rsp->hdr.smb2_buf_length)) + - le16_to_cpu(rsp->SecurityBufferOffset)); - inc_rfc1001_len(rsp, sizeof(struct smb2_negotiate_rsp) - + ksmbd_copy_gss_neg_header((char *)(&rsp->hdr) + + le16_to_cpu(rsp->SecurityBufferOffset)); + inc_rfc1001_len(work->response_buf, sizeof(struct smb2_negotiate_rsp) - sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) + AUTH_GSS_LENGTH); rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; @@ -1248,19 +1255,13 @@ static int generate_preauth_hash(struct ksmbd_work *work) return 0; } -static int decode_negotiation_token(struct ksmbd_work *work, - struct negotiate_message *negblob) +static int decode_negotiation_token(struct ksmbd_conn *conn, + struct negotiate_message *negblob, + size_t sz) { - struct ksmbd_conn *conn = work->conn; - struct smb2_sess_setup_req *req; - int sz; - if (!conn->use_spnego) return -EINVAL; - req = work->request_buf; - sz = le16_to_cpu(req->SecurityBufferLength); - if (ksmbd_decode_negTokenInit((char *)negblob, sz, conn)) { if (ksmbd_decode_negTokenTarg((char *)negblob, sz, conn)) { conn->auth_mechs |= KSMBD_AUTH_NTLMSSP; @@ -1272,10 +1273,10 @@ static int decode_negotiation_token(struct ksmbd_work *work, } static int ntlm_negotiate(struct ksmbd_work *work, - struct negotiate_message *negblob) + struct negotiate_message *negblob, + size_t negblob_len) { - struct smb2_sess_setup_req *req = work->request_buf; - struct smb2_sess_setup_rsp *rsp = work->response_buf; + struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); struct challenge_message *chgblob; unsigned char *spnego_blob = NULL; u16 spnego_blob_len; @@ -1283,8 +1284,7 @@ static int ntlm_negotiate(struct ksmbd_work *work, int sz, rc; ksmbd_debug(SMB, "negotiate phase\n"); - sz = le16_to_cpu(req->SecurityBufferLength); - rc = ksmbd_decode_ntlmssp_neg_blob(negblob, sz, work->sess); + rc = ksmbd_decode_ntlmssp_neg_blob(negblob, negblob_len, work->sess); if (rc) return rc; @@ -1352,12 +1352,23 @@ static struct ksmbd_user *session_user(struct ksmbd_conn *conn, struct authenticate_message *authblob; struct ksmbd_user *user; char *name; - int sz; + unsigned int auth_msg_len, name_off, name_len, secbuf_len; + secbuf_len = le16_to_cpu(req->SecurityBufferLength); + if (secbuf_len < sizeof(struct authenticate_message)) { + ksmbd_debug(SMB, "blob len %d too small\n", secbuf_len); + return NULL; + } authblob = user_authblob(conn, req); - sz = le32_to_cpu(authblob->UserName.BufferOffset); - name = smb_strndup_from_utf16((const char *)authblob + sz, - le16_to_cpu(authblob->UserName.Length), + name_off = le32_to_cpu(authblob->UserName.BufferOffset); + name_len = le16_to_cpu(authblob->UserName.Length); + auth_msg_len = le16_to_cpu(req->SecurityBufferOffset) + secbuf_len; + + if (auth_msg_len < (u64)name_off + name_len) + return NULL; + + name = smb_strndup_from_utf16((const char *)authblob + name_off, + name_len, true, conn->local_nls); if (IS_ERR(name)) { @@ -1373,8 +1384,8 @@ static struct ksmbd_user *session_user(struct ksmbd_conn *conn, static int ntlm_authenticate(struct ksmbd_work *work) { - struct smb2_sess_setup_req *req = work->request_buf; - struct smb2_sess_setup_rsp *rsp = work->response_buf; + struct smb2_sess_setup_req *req = smb2_get_msg(work->request_buf); + struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess = work->sess; struct channel *chann = NULL; @@ -1397,7 +1408,7 @@ static int ntlm_authenticate(struct ksmbd_work *work) memcpy((char *)&rsp->hdr.ProtocolId + sz, spnego_blob, spnego_blob_len); rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len); kfree(spnego_blob); - inc_rfc1001_len(rsp, spnego_blob_len - 1); + inc_rfc1001_len(work->response_buf, spnego_blob_len - 1); } user = session_user(conn, req); @@ -1499,11 +1510,9 @@ binding_session: } } - if (conn->dialect > SMB20_PROT_ID) { - if (!ksmbd_conn_lookup_dialect(conn)) { - pr_err("fail to verify the dialect\n"); - return -ENOENT; - } + if (!ksmbd_conn_lookup_dialect(conn)) { + pr_err("fail to verify the dialect\n"); + return -ENOENT; } return 0; } @@ -1511,8 +1520,8 @@ binding_session: #ifdef CONFIG_SMB_SERVER_KERBEROS5 static int krb5_authenticate(struct ksmbd_work *work) { - struct smb2_sess_setup_req *req = work->request_buf; - struct smb2_sess_setup_rsp *rsp = work->response_buf; + struct smb2_sess_setup_req *req = smb2_get_msg(work->request_buf); + struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess = work->sess; char *in_blob, *out_blob; @@ -1527,8 +1536,7 @@ static int krb5_authenticate(struct ksmbd_work *work) out_blob = (char *)&rsp->hdr.ProtocolId + le16_to_cpu(rsp->SecurityBufferOffset); out_len = work->response_sz - - offsetof(struct smb2_hdr, smb2_buf_length) - - le16_to_cpu(rsp->SecurityBufferOffset); + (le16_to_cpu(rsp->SecurityBufferOffset) + 4); /* Check previous session */ prev_sess_id = le64_to_cpu(req->PreviousSessionId); @@ -1545,7 +1553,7 @@ static int krb5_authenticate(struct ksmbd_work *work) return -EINVAL; } rsp->SecurityBufferLength = cpu_to_le16(out_len); - inc_rfc1001_len(rsp, out_len - 1); + inc_rfc1001_len(work->response_buf, out_len - 1); if ((conn->sign || server_conf.enforced_signing) || (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) @@ -1585,11 +1593,9 @@ static int krb5_authenticate(struct ksmbd_work *work) } } - if (conn->dialect > SMB20_PROT_ID) { - if (!ksmbd_conn_lookup_dialect(conn)) { - pr_err("fail to verify the dialect\n"); - return -ENOENT; - } + if (!ksmbd_conn_lookup_dialect(conn)) { + pr_err("fail to verify the dialect\n"); + return -ENOENT; } return 0; } @@ -1603,10 +1609,11 @@ static int krb5_authenticate(struct ksmbd_work *work) int smb2_sess_setup(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_sess_setup_req *req = work->request_buf; - struct smb2_sess_setup_rsp *rsp = work->response_buf; + struct smb2_sess_setup_req *req = smb2_get_msg(work->request_buf); + struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); struct ksmbd_session *sess; struct negotiate_message *negblob; + unsigned int negblob_len, negblob_off; int rc = 0; ksmbd_debug(SMB, "Received request for session setup\n"); @@ -1615,7 +1622,7 @@ int smb2_sess_setup(struct ksmbd_work *work) rsp->SessionFlags = 0; rsp->SecurityBufferOffset = cpu_to_le16(72); rsp->SecurityBufferLength = 0; - inc_rfc1001_len(rsp, 9); + inc_rfc1001_len(work->response_buf, 9); if (!req->hdr.SessionId) { sess = ksmbd_smb2_session_create(); @@ -1687,10 +1694,16 @@ int smb2_sess_setup(struct ksmbd_work *work) if (sess->state == SMB2_SESSION_EXPIRED) sess->state = SMB2_SESSION_IN_PROGRESS; + negblob_off = le16_to_cpu(req->SecurityBufferOffset); + negblob_len = le16_to_cpu(req->SecurityBufferLength); + if (negblob_off < offsetof(struct smb2_sess_setup_req, Buffer) || + negblob_len < offsetof(struct negotiate_message, NegotiateFlags)) + return -EINVAL; + negblob = (struct negotiate_message *)((char *)&req->hdr.ProtocolId + - le16_to_cpu(req->SecurityBufferOffset)); + negblob_off); - if (decode_negotiation_token(work, negblob) == 0) { + if (decode_negotiation_token(conn, negblob, negblob_len) == 0) { if (conn->mechToken) negblob = (struct negotiate_message *)conn->mechToken; } @@ -1714,7 +1727,7 @@ int smb2_sess_setup(struct ksmbd_work *work) sess->Preauth_HashValue = NULL; } else if (conn->preferred_auth_mech == KSMBD_AUTH_NTLMSSP) { if (negblob->MessageType == NtLmNegotiate) { - rc = ntlm_negotiate(work, negblob); + rc = ntlm_negotiate(work, negblob, negblob_len); if (rc) goto out_err; rsp->hdr.Status = @@ -1723,7 +1736,8 @@ int smb2_sess_setup(struct ksmbd_work *work) * Note: here total size -1 is done as an * adjustment for 0 size blob */ - inc_rfc1001_len(rsp, le16_to_cpu(rsp->SecurityBufferLength) - 1); + inc_rfc1001_len(work->response_buf, + le16_to_cpu(rsp->SecurityBufferLength) - 1); } else if (negblob->MessageType == NtLmAuthenticate) { rc = ntlm_authenticate(work); @@ -1774,9 +1788,30 @@ out_err: conn->mechToken = NULL; } - if (rc < 0 && sess) { - ksmbd_session_destroy(sess); - work->sess = NULL; + if (rc < 0) { + /* + * SecurityBufferOffset should be set to zero + * in session setup error response. + */ + rsp->SecurityBufferOffset = 0; + + if (sess) { + bool try_delay = false; + + /* + * To avoid dictionary attacks (repeated session setups rapidly sent) to + * connect to server, ksmbd make a delay of a 5 seconds on session setup + * failure to make it harder to send enough random connection requests + * to break into a server. + */ + if (sess->user && sess->user->flags & KSMBD_USER_FLAG_DELAY_SESSION) + try_delay = true; + + ksmbd_session_destroy(sess); + work->sess = NULL; + if (try_delay) + ssleep(5); + } } return rc; @@ -1791,8 +1826,8 @@ out_err: int smb2_tree_connect(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_tree_connect_req *req = work->request_buf; - struct smb2_tree_connect_rsp *rsp = work->response_buf; + struct smb2_tree_connect_req *req = smb2_get_msg(work->request_buf); + struct smb2_tree_connect_rsp *rsp = smb2_get_msg(work->response_buf); struct ksmbd_session *sess = work->sess; char *treename = NULL, *name = NULL; struct ksmbd_tree_conn_status status; @@ -1857,7 +1892,7 @@ out_err1: rsp->Reserved = 0; /* default manual caching */ rsp->ShareFlags = SMB2_SHAREFLAG_MANUAL_CACHING; - inc_rfc1001_len(rsp, 16); + inc_rfc1001_len(work->response_buf, 16); if (!IS_ERR(treename)) kfree(treename); @@ -1962,17 +1997,18 @@ static int smb2_create_open_flags(bool file_present, __le32 access, */ int smb2_tree_disconnect(struct ksmbd_work *work) { - struct smb2_tree_disconnect_rsp *rsp = work->response_buf; + struct smb2_tree_disconnect_rsp *rsp = smb2_get_msg(work->response_buf); struct ksmbd_session *sess = work->sess; struct ksmbd_tree_connect *tcon = work->tcon; rsp->StructureSize = cpu_to_le16(4); - inc_rfc1001_len(rsp, 4); + inc_rfc1001_len(work->response_buf, 4); ksmbd_debug(SMB, "request\n"); if (!tcon) { - struct smb2_tree_disconnect_req *req = work->request_buf; + struct smb2_tree_disconnect_req *req = + smb2_get_msg(work->request_buf); ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; @@ -1994,11 +2030,11 @@ int smb2_tree_disconnect(struct ksmbd_work *work) int smb2_session_logoff(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_logoff_rsp *rsp = work->response_buf; + struct smb2_logoff_rsp *rsp = smb2_get_msg(work->response_buf); struct ksmbd_session *sess = work->sess; rsp->StructureSize = cpu_to_le16(4); - inc_rfc1001_len(rsp, 4); + inc_rfc1001_len(work->response_buf, 4); ksmbd_debug(SMB, "request\n"); @@ -2011,7 +2047,7 @@ int smb2_session_logoff(struct ksmbd_work *work) ksmbd_conn_wait_idle(conn); if (ksmbd_tree_conn_session_logoff(sess)) { - struct smb2_logoff_req *req = work->request_buf; + struct smb2_logoff_req *req = smb2_get_msg(work->request_buf); ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; @@ -2038,8 +2074,8 @@ int smb2_session_logoff(struct ksmbd_work *work) */ static noinline int create_smb2_pipe(struct ksmbd_work *work) { - struct smb2_create_rsp *rsp = work->response_buf; - struct smb2_create_req *req = work->request_buf; + struct smb2_create_rsp *rsp = smb2_get_msg(work->response_buf); + struct smb2_create_req *req = smb2_get_msg(work->request_buf); int id; int err; char *name; @@ -2062,7 +2098,7 @@ static noinline int create_smb2_pipe(struct ksmbd_work *work) rsp->hdr.Status = STATUS_SUCCESS; rsp->StructureSize = cpu_to_le16(89); rsp->OplockLevel = SMB2_OPLOCK_LEVEL_NONE; - rsp->Reserved = 0; + rsp->Flags = 0; rsp->CreateAction = cpu_to_le32(FILE_OPENED); rsp->CreationTime = cpu_to_le64(0); @@ -2070,14 +2106,14 @@ static noinline int create_smb2_pipe(struct ksmbd_work *work) rsp->ChangeTime = cpu_to_le64(0); rsp->AllocationSize = cpu_to_le64(0); rsp->EndofFile = cpu_to_le64(0); - rsp->FileAttributes = ATTR_NORMAL_LE; + rsp->FileAttributes = FILE_ATTRIBUTE_NORMAL_LE; rsp->Reserved2 = 0; rsp->VolatileFileId = cpu_to_le64(id); rsp->PersistentFileId = 0; rsp->CreateContextsOffset = 0; rsp->CreateContextsLength = 0; - inc_rfc1001_len(rsp, 88); /* StructureSize - 1*/ + inc_rfc1001_len(work->response_buf, 88); /* StructureSize - 1*/ kfree(name); return 0; @@ -2103,16 +2139,22 @@ out: * smb2_set_ea() - handler for setting extended attributes using set * info command * @eabuf: set info command buffer + * @buf_len: set info command buffer length * @path: dentry path for get ea * * Return: 0 on success, otherwise error */ -static int smb2_set_ea(struct smb2_ea_info *eabuf, struct path *path) +static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len, + struct path *path) { struct user_namespace *user_ns = mnt_user_ns(path->mnt); char *attr_name = NULL, *value; int rc = 0; - int next = 0; + unsigned int next = 0; + + if (buf_len < sizeof(struct smb2_ea_info) + eabuf->EaNameLength + + le16_to_cpu(eabuf->EaValueLength)) + return -EINVAL; attr_name = kmalloc(XATTR_NAME_MAX + 1, GFP_KERNEL); if (!attr_name) @@ -2177,7 +2219,13 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, struct path *path) next: next = le32_to_cpu(eabuf->NextEntryOffset); + if (next == 0 || buf_len < next) + break; + buf_len -= next; eabuf = (struct smb2_ea_info *)((char *)eabuf + next); + if (next < (u32)eabuf->EaNameLength + le16_to_cpu(eabuf->EaValueLength)) + break; + } while (next != 0); kfree(attr_name); @@ -2304,7 +2352,7 @@ static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon, struct xattr_dos_attrib da; int rc; - fp->f_ci->m_fattr &= ~(ATTR_HIDDEN_LE | ATTR_SYSTEM_LE); + fp->f_ci->m_fattr &= ~(FILE_ATTRIBUTE_HIDDEN_LE | FILE_ATTRIBUTE_SYSTEM_LE); /* get FileAttributes from XATTR_NAME_DOS_ATTRIBUTE */ if (!test_share_config_flag(tcon->share_conf, @@ -2348,7 +2396,7 @@ static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name, return rc; } - rc = ksmbd_vfs_kern_path(name, 0, path, 0); + rc = ksmbd_vfs_kern_path(work, name, 0, path, 0); if (rc) { pr_err("cannot get linux path (%s), err = %d\n", name, rc); @@ -2377,6 +2425,10 @@ static int smb2_create_sd_buffer(struct ksmbd_work *work, ksmbd_debug(SMB, "Set ACLs using SMB2_CREATE_SD_BUFFER context\n"); sd_buf = (struct create_sd_buf_req *)context; + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(struct create_sd_buf_req)) + return -EINVAL; return set_info_sec(work->conn, work->tcon, path, &sd_buf->ntsd, le32_to_cpu(sd_buf->ccontext.DataLength), true); } @@ -2410,7 +2462,7 @@ int smb2_open(struct ksmbd_work *work) struct ksmbd_session *sess = work->sess; struct ksmbd_tree_connect *tcon = work->tcon; struct smb2_create_req *req; - struct smb2_create_rsp *rsp, *rsp_org; + struct smb2_create_rsp *rsp; struct path path; struct ksmbd_share_config *share = tcon->share_conf; struct ksmbd_file *fp = NULL; @@ -2423,7 +2475,7 @@ int smb2_open(struct ksmbd_work *work) struct oplock_info *opinfo; __le32 *next_ptr = NULL; int req_op_level = 0, open_flags = 0, may_flags = 0, file_info = 0; - int rc = 0, len = 0; + int rc = 0; int contxt_cnt = 0, query_disk_id = 0; int maximal_access_ctxt = 0, posix_ctxt = 0; int s_type = 0; @@ -2436,7 +2488,6 @@ int smb2_open(struct ksmbd_work *work) umode_t posix_mode = 0; __le32 daccess, maximal_access = 0; - rsp_org = work->response_buf; WORK_BUFFERS(work, req, rsp); if (req->hdr.NextCommand && !work->next_smb2_rcv_hdr_off && @@ -2495,24 +2546,18 @@ int smb2_open(struct ksmbd_work *work) goto err_out1; } } else { - len = strlen(share->path); - ksmbd_debug(SMB, "share path len %d\n", len); - name = kmalloc(len + 1, GFP_KERNEL); + name = kstrdup("", GFP_KERNEL); if (!name) { - rsp->hdr.Status = STATUS_NO_MEMORY; rc = -ENOMEM; goto err_out1; } - - memcpy(name, share->path, len); - *(name + len) = '\0'; } req_op_level = req->RequestedOplockLevel; if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) lc = parse_lease_state(req); - if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE_LE)) { + if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE)) { pr_err("Invalid impersonationlevel : 0x%x\n", le32_to_cpu(req->ImpersonationLevel)); rc = -EIO; @@ -2520,7 +2565,7 @@ int smb2_open(struct ksmbd_work *work) goto err_out1; } - if (req->CreateOptions && !(req->CreateOptions & CREATE_OPTIONS_MASK)) { + if (req->CreateOptions && !(req->CreateOptions & CREATE_OPTIONS_MASK_LE)) { pr_err("Invalid create options : 0x%x\n", le32_to_cpu(req->CreateOptions)); rc = -EINVAL; @@ -2562,7 +2607,7 @@ int smb2_open(struct ksmbd_work *work) goto err_out1; } - if (req->FileAttributes && !(req->FileAttributes & ATTR_MASK_LE)) { + if (req->FileAttributes && !(req->FileAttributes & FILE_ATTRIBUTE_MASK_LE)) { pr_err("Invalid file attribute : 0x%x\n", le32_to_cpu(req->FileAttributes)); rc = -EINVAL; @@ -2577,6 +2622,12 @@ int smb2_open(struct ksmbd_work *work) goto err_out1; } else if (context) { ea_buf = (struct create_ea_buf_req *)context; + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(struct create_ea_buf_req)) { + rc = -EINVAL; + goto err_out1; + } if (req->CreateOptions & FILE_NO_EA_KNOWLEDGE_LE) { rsp->hdr.Status = STATUS_ACCESS_DENIED; rc = -EACCES; @@ -2615,6 +2666,12 @@ int smb2_open(struct ksmbd_work *work) } else if (context) { struct create_posix *posix = (struct create_posix *)context; + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(struct create_posix)) { + rc = -EINVAL; + goto err_out1; + } ksmbd_debug(SMB, "get posix context\n"); posix_mode = le32_to_cpu(posix->Mode); @@ -2628,13 +2685,9 @@ int smb2_open(struct ksmbd_work *work) goto err_out1; } - if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) { - /* - * On delete request, instead of following up, need to - * look the current entity - */ - rc = ksmbd_vfs_kern_path(name, 0, &path, 1); - if (!rc) { + rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 1); + if (!rc) { + if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) { /* * If file exists with under flags, return access * denied error. @@ -2653,34 +2706,16 @@ int smb2_open(struct ksmbd_work *work) path_put(&path); goto err_out; } - } - } else { - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS)) { - /* - * Use LOOKUP_FOLLOW to follow the path of - * symlink in path buildup - */ - rc = ksmbd_vfs_kern_path(name, LOOKUP_FOLLOW, &path, 1); - if (rc) { /* Case for broken link ?*/ - rc = ksmbd_vfs_kern_path(name, 0, &path, 1); - } - } else { - rc = ksmbd_vfs_kern_path(name, 0, &path, 1); - if (!rc && d_is_symlink(path.dentry)) { - rc = -EACCES; - path_put(&path); - goto err_out; - } + } else if (d_is_symlink(path.dentry)) { + rc = -EACCES; + path_put(&path); + goto err_out; } } if (rc) { - if (rc == -EACCES) { - ksmbd_debug(SMB, - "User does not have right permission\n"); + if (rc != -ENOENT) goto err_out; - } ksmbd_debug(SMB, "can not get linux path for %s, rc = %d\n", name, rc); rc = 0; @@ -2703,7 +2738,7 @@ int smb2_open(struct ksmbd_work *work) } if (req->CreateOptions & FILE_DIRECTORY_FILE_LE && - req->FileAttributes & ATTR_NORMAL_LE) { + req->FileAttributes & FILE_ATTRIBUTE_NORMAL_LE) { rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; rc = -EIO; } @@ -2786,7 +2821,15 @@ int smb2_open(struct ksmbd_work *work) created = true; user_ns = mnt_user_ns(path.mnt); if (ea_buf) { - rc = smb2_set_ea(&ea_buf->ea, &path); + if (le32_to_cpu(ea_buf->ccontext.DataLength) < + sizeof(struct smb2_ea_info)) { + rc = -EINVAL; + goto err_out; + } + + rc = smb2_set_ea(&ea_buf->ea, + le32_to_cpu(ea_buf->ccontext.DataLength), + &path); if (rc == -EOPNOTSUPP) rc = 0; else if (rc) @@ -3019,9 +3062,16 @@ int smb2_open(struct ksmbd_work *work) rc = PTR_ERR(az_req); goto err_out; } else if (az_req) { - loff_t alloc_size = le64_to_cpu(az_req->AllocationSize); + loff_t alloc_size; int err; + if (le16_to_cpu(az_req->ccontext.DataOffset) + + le32_to_cpu(az_req->ccontext.DataLength) < + sizeof(struct create_alloc_size_req)) { + rc = -EINVAL; + goto err_out; + } + alloc_size = le64_to_cpu(az_req->AllocationSize); ksmbd_debug(SMB, "request smb2 create allocate size : %llu\n", alloc_size); @@ -3067,7 +3117,7 @@ int smb2_open(struct ksmbd_work *work) opinfo = rcu_dereference(fp->f_opinfo); rsp->OplockLevel = opinfo != NULL ? opinfo->level : 0; rcu_read_unlock(); - rsp->Reserved = 0; + rsp->Flags = 0; rsp->CreateAction = cpu_to_le32(file_info); rsp->CreationTime = cpu_to_le64(fp->create_time); time = ksmbd_UnixTimeToNT(stat.atime); @@ -3088,7 +3138,7 @@ int smb2_open(struct ksmbd_work *work) rsp->CreateContextsOffset = 0; rsp->CreateContextsLength = 0; - inc_rfc1001_len(rsp_org, 88); /* StructureSize - 1*/ + inc_rfc1001_len(work->response_buf, 88); /* StructureSize - 1*/ /* If lease is request send lease context response */ if (opinfo && opinfo->is_lease) { @@ -3103,7 +3153,8 @@ int smb2_open(struct ksmbd_work *work) create_lease_buf(rsp->Buffer, opinfo->o_lease); le32_add_cpu(&rsp->CreateContextsLength, conn->vals->create_lease_size); - inc_rfc1001_len(rsp_org, conn->vals->create_lease_size); + inc_rfc1001_len(work->response_buf, + conn->vals->create_lease_size); next_ptr = &lease_ccontext->Next; next_off = conn->vals->create_lease_size; } @@ -3123,7 +3174,8 @@ int smb2_open(struct ksmbd_work *work) le32_to_cpu(maximal_access)); le32_add_cpu(&rsp->CreateContextsLength, conn->vals->create_mxac_size); - inc_rfc1001_len(rsp_org, conn->vals->create_mxac_size); + inc_rfc1001_len(work->response_buf, + conn->vals->create_mxac_size); if (next_ptr) *next_ptr = cpu_to_le32(next_off); next_ptr = &mxac_ccontext->Next; @@ -3141,7 +3193,8 @@ int smb2_open(struct ksmbd_work *work) stat.ino, tcon->id); le32_add_cpu(&rsp->CreateContextsLength, conn->vals->create_disk_id_size); - inc_rfc1001_len(rsp_org, conn->vals->create_disk_id_size); + inc_rfc1001_len(work->response_buf, + conn->vals->create_disk_id_size); if (next_ptr) *next_ptr = cpu_to_le32(next_off); next_ptr = &disk_id_ccontext->Next; @@ -3155,15 +3208,15 @@ int smb2_open(struct ksmbd_work *work) fp); le32_add_cpu(&rsp->CreateContextsLength, conn->vals->create_posix_size); - inc_rfc1001_len(rsp_org, conn->vals->create_posix_size); + inc_rfc1001_len(work->response_buf, + conn->vals->create_posix_size); if (next_ptr) *next_ptr = cpu_to_le32(next_off); } if (contxt_cnt > 0) { rsp->CreateContextsOffset = - cpu_to_le32(offsetof(struct smb2_create_rsp, Buffer) - - 4); + cpu_to_le32(offsetof(struct smb2_create_rsp, Buffer)); } err_out: @@ -3176,7 +3229,7 @@ err_out1: rsp->hdr.Status = STATUS_INVALID_PARAMETER; else if (rc == -EOPNOTSUPP) rsp->hdr.Status = STATUS_NOT_SUPPORTED; - else if (rc == -EACCES || rc == -ESTALE) + else if (rc == -EACCES || rc == -ESTALE || rc == -EXDEV) rsp->hdr.Status = STATUS_ACCESS_DENIED; else if (rc == -ENOENT) rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID; @@ -3370,9 +3423,9 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, ffdinfo->EaSize = smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); if (ffdinfo->EaSize) - ffdinfo->ExtFileAttributes = ATTR_REPARSE_POINT_LE; + ffdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; if (d_info->hide_dot_file && d_info->name[0] == '.') - ffdinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + ffdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; memcpy(ffdinfo->FileName, conv_name, conv_len); ffdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); break; @@ -3386,11 +3439,11 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, fbdinfo->EaSize = smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); if (fbdinfo->EaSize) - fbdinfo->ExtFileAttributes = ATTR_REPARSE_POINT_LE; + fbdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; fbdinfo->ShortNameLength = 0; fbdinfo->Reserved = 0; if (d_info->hide_dot_file && d_info->name[0] == '.') - fbdinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + fbdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; memcpy(fbdinfo->FileName, conv_name, conv_len); fbdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); break; @@ -3402,7 +3455,7 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, fdinfo = (struct file_directory_info *)kstat; fdinfo->FileNameLength = cpu_to_le32(conv_len); if (d_info->hide_dot_file && d_info->name[0] == '.') - fdinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + fdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; memcpy(fdinfo->FileName, conv_name, conv_len); fdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); break; @@ -3426,11 +3479,11 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, dinfo->EaSize = smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); if (dinfo->EaSize) - dinfo->ExtFileAttributes = ATTR_REPARSE_POINT_LE; + dinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; dinfo->Reserved = 0; dinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); if (d_info->hide_dot_file && d_info->name[0] == '.') - dinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + dinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; memcpy(dinfo->FileName, conv_name, conv_len); dinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); break; @@ -3444,13 +3497,13 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, fibdinfo->EaSize = smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); if (fibdinfo->EaSize) - fibdinfo->ExtFileAttributes = ATTR_REPARSE_POINT_LE; + fibdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; fibdinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); fibdinfo->ShortNameLength = 0; fibdinfo->Reserved = 0; fibdinfo->Reserved2 = cpu_to_le16(0); if (d_info->hide_dot_file && d_info->name[0] == '.') - fibdinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + fibdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; memcpy(fibdinfo->FileName, conv_name, conv_len); fibdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); break; @@ -3476,9 +3529,10 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, posix_info->Mode = cpu_to_le32(ksmbd_kstat->kstat->mode); posix_info->Inode = cpu_to_le64(ksmbd_kstat->kstat->ino); posix_info->DosAttributes = - S_ISDIR(ksmbd_kstat->kstat->mode) ? ATTR_DIRECTORY_LE : ATTR_ARCHIVE_LE; + S_ISDIR(ksmbd_kstat->kstat->mode) ? + FILE_ATTRIBUTE_DIRECTORY_LE : FILE_ATTRIBUTE_ARCHIVE_LE; if (d_info->hide_dot_file && d_info->name[0] == '.') - posix_info->DosAttributes |= ATTR_HIDDEN_LE; + posix_info->DosAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; id_to_sid(from_kuid_munged(&init_user_ns, ksmbd_kstat->kstat->uid), SIDNFS_USER, (struct smb_sid *)&posix_info->SidBuffer[0]); id_to_sid(from_kgid_munged(&init_user_ns, ksmbd_kstat->kstat->gid), @@ -3742,11 +3796,29 @@ static int verify_info_level(int info_level) return 0; } +static int smb2_calc_max_out_buf_len(struct ksmbd_work *work, + unsigned short hdr2_len, + unsigned int out_buf_len) +{ + int free_len; + + if (out_buf_len > work->conn->vals->max_trans_size) + return -EINVAL; + + free_len = (int)(work->response_sz - + (get_rfc1002_len(work->response_buf) + 4)) - + hdr2_len; + if (free_len < 0) + return -EINVAL; + + return min_t(int, out_buf_len, free_len); +} + int smb2_query_dir(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; struct smb2_query_directory_req *req; - struct smb2_query_directory_rsp *rsp, *rsp_org; + struct smb2_query_directory_rsp *rsp; struct ksmbd_share_config *share = work->tcon->share_conf; struct ksmbd_file *dir_fp = NULL; struct ksmbd_dir_info d_info; @@ -3756,7 +3828,6 @@ int smb2_query_dir(struct ksmbd_work *work) int buffer_sz; struct smb2_query_dir_private query_dir_private = {NULL, }; - rsp_org = work->response_buf; WORK_BUFFERS(work, req, rsp); if (ksmbd_override_fsids(work)) { @@ -3818,9 +3889,13 @@ int smb2_query_dir(struct ksmbd_work *work) memset(&d_info, 0, sizeof(struct ksmbd_dir_info)); d_info.wptr = (char *)rsp->Buffer; d_info.rptr = (char *)rsp->Buffer; - d_info.out_buf_len = (work->response_sz - (get_rfc1002_len(rsp_org) + 4)); - d_info.out_buf_len = min_t(int, d_info.out_buf_len, le32_to_cpu(req->OutputBufferLength)) - - sizeof(struct smb2_query_directory_rsp); + d_info.out_buf_len = + smb2_calc_max_out_buf_len(work, 8, + le32_to_cpu(req->OutputBufferLength)); + if (d_info.out_buf_len < 0) { + rc = -EINVAL; + goto err_out; + } d_info.flags = srch_flag; /* @@ -3873,7 +3948,7 @@ int smb2_query_dir(struct ksmbd_work *work) rsp->OutputBufferOffset = cpu_to_le16(0); rsp->OutputBufferLength = cpu_to_le32(0); rsp->Buffer[0] = 0; - inc_rfc1001_len(rsp_org, 9); + inc_rfc1001_len(work->response_buf, 9); } else { ((struct file_directory_info *) ((char *)rsp->Buffer + d_info.last_entry_offset)) @@ -3882,7 +3957,7 @@ int smb2_query_dir(struct ksmbd_work *work) rsp->StructureSize = cpu_to_le16(9); rsp->OutputBufferOffset = cpu_to_le16(72); rsp->OutputBufferLength = cpu_to_le32(d_info.data_count); - inc_rfc1001_len(rsp_org, 8 + d_info.data_count); + inc_rfc1001_len(work->response_buf, 8 + d_info.data_count); } kfree(srch_ptr); @@ -3925,26 +4000,28 @@ err_out2: * Return: 0 on success, otherwise error */ static int buffer_check_err(int reqOutputBufferLength, - struct smb2_query_info_rsp *rsp, int infoclass_size) + struct smb2_query_info_rsp *rsp, + void *rsp_org, int infoclass_size) { if (reqOutputBufferLength < le32_to_cpu(rsp->OutputBufferLength)) { if (reqOutputBufferLength < infoclass_size) { pr_err("Invalid Buffer Size Requested\n"); rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH; - rsp->hdr.smb2_buf_length = cpu_to_be32(sizeof(struct smb2_hdr) - 4); + *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr)); return -EINVAL; } ksmbd_debug(SMB, "Buffer Overflow\n"); rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; - rsp->hdr.smb2_buf_length = cpu_to_be32(sizeof(struct smb2_hdr) - 4 + + *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr) + reqOutputBufferLength); rsp->OutputBufferLength = cpu_to_le32(reqOutputBufferLength); } return 0; } -static void get_standard_info_pipe(struct smb2_query_info_rsp *rsp) +static void get_standard_info_pipe(struct smb2_query_info_rsp *rsp, + void *rsp_org) { struct smb2_file_standard_info *sinfo; @@ -3957,10 +4034,11 @@ static void get_standard_info_pipe(struct smb2_query_info_rsp *rsp) sinfo->Directory = 0; rsp->OutputBufferLength = cpu_to_le32(sizeof(struct smb2_file_standard_info)); - inc_rfc1001_len(rsp, sizeof(struct smb2_file_standard_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_standard_info)); } -static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num) +static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num, + void *rsp_org) { struct smb2_file_internal_info *file_info; @@ -3970,12 +4048,13 @@ static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num) file_info->IndexNumber = cpu_to_le64(num | (1ULL << 63)); rsp->OutputBufferLength = cpu_to_le32(sizeof(struct smb2_file_internal_info)); - inc_rfc1001_len(rsp, sizeof(struct smb2_file_internal_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_internal_info)); } static int smb2_get_info_file_pipe(struct ksmbd_session *sess, struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp) + struct smb2_query_info_rsp *rsp, + void *rsp_org) { u64 id; int rc; @@ -3993,14 +4072,16 @@ static int smb2_get_info_file_pipe(struct ksmbd_session *sess, switch (req->FileInfoClass) { case FILE_STANDARD_INFORMATION: - get_standard_info_pipe(rsp); + get_standard_info_pipe(rsp, rsp_org); rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), - rsp, FILE_STANDARD_INFORMATION_SIZE); + rsp, rsp_org, + FILE_STANDARD_INFORMATION_SIZE); break; case FILE_INTERNAL_INFORMATION: - get_internal_info_pipe(rsp, id); + get_internal_info_pipe(rsp, id, rsp_org); rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), - rsp, FILE_INTERNAL_INFORMATION_SIZE); + rsp, rsp_org, + FILE_INTERNAL_INFORMATION_SIZE); break; default: ksmbd_debug(SMB, "smb2_info_file_pipe for %u not supported\n", @@ -4041,6 +4122,10 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, path = &fp->filp->f_path; /* single EA entry is requested with given user.* name */ if (req->InputBufferLength) { + if (le32_to_cpu(req->InputBufferLength) < + sizeof(struct smb2_ea_info_req)) + return -EINVAL; + ea_req = (struct smb2_ea_info_req *)req->Buffer; } else { /* need to send all EAs, if no specific EA is requested*/ @@ -4050,12 +4135,11 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, le32_to_cpu(req->Flags)); } - buf_free_len = work->response_sz - - (get_rfc1002_len(rsp_org) + 4) - - sizeof(struct smb2_query_info_rsp); - - if (le32_to_cpu(req->OutputBufferLength) < buf_free_len) - buf_free_len = le32_to_cpu(req->OutputBufferLength); + buf_free_len = + smb2_calc_max_out_buf_len(work, 8, + le32_to_cpu(req->OutputBufferLength)); + if (buf_free_len < 0) + return -EINVAL; rc = ksmbd_vfs_listxattr(path->dentry, &xattr_list); if (rc < 0) { @@ -4186,7 +4270,7 @@ static void get_file_access_info(struct smb2_query_info_rsp *rsp, static int get_file_basic_info(struct smb2_query_info_rsp *rsp, struct ksmbd_file *fp, void *rsp_org) { - struct smb2_file_all_info *basic_info; + struct smb2_file_basic_info *basic_info; struct kstat stat; u64 time; @@ -4196,7 +4280,7 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp, return -EACCES; } - basic_info = (struct smb2_file_all_info *)rsp->Buffer; + basic_info = (struct smb2_file_basic_info *)rsp->Buffer; generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), &stat); basic_info->CreationTime = cpu_to_le64(fp->create_time); @@ -4209,9 +4293,8 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp, basic_info->Attributes = fp->f_ci->m_fattr; basic_info->Pad1 = 0; rsp->OutputBufferLength = - cpu_to_le32(offsetof(struct smb2_file_all_info, AllocationSize)); - inc_rfc1001_len(rsp_org, offsetof(struct smb2_file_all_info, - AllocationSize)); + cpu_to_le32(sizeof(struct smb2_file_basic_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_basic_info)); return 0; } @@ -4288,8 +4371,7 @@ static int get_file_all_info(struct ksmbd_work *work, return -EACCES; } - filename = convert_to_nt_pathname(fp->filename, - work->tcon->share_conf->path); + filename = convert_to_nt_pathname(fp->filename); if (!filename) return -ENOMEM; @@ -4368,6 +4450,8 @@ static void get_file_stream_info(struct ksmbd_work *work, struct path *path = &fp->filp->f_path; ssize_t xattr_list_len; int nbytes = 0, streamlen, stream_name_len, next, idx = 0; + int buf_free_len; + struct smb2_query_info_req *req = ksmbd_req_buf_next(work); generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), &stat); @@ -4381,6 +4465,12 @@ static void get_file_stream_info(struct ksmbd_work *work, goto out; } + buf_free_len = + smb2_calc_max_out_buf_len(work, 8, + le32_to_cpu(req->OutputBufferLength)); + if (buf_free_len < 0) + goto out; + while (idx < xattr_list_len) { stream_name = xattr_list + idx; streamlen = strlen(stream_name); @@ -4405,6 +4495,10 @@ static void get_file_stream_info(struct ksmbd_work *work, streamlen = snprintf(stream_buf, streamlen + 1, ":%s", &stream_name[XATTR_NAME_STREAM_LEN]); + next = sizeof(struct smb2_file_stream_info) + streamlen * 2; + if (next > buf_free_len) + break; + file_info = (struct smb2_file_stream_info *)&rsp->Buffer[nbytes]; streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, stream_buf, streamlen, @@ -4415,22 +4509,21 @@ static void get_file_stream_info(struct ksmbd_work *work, file_info->StreamSize = cpu_to_le64(stream_name_len); file_info->StreamAllocationSize = cpu_to_le64(stream_name_len); - next = sizeof(struct smb2_file_stream_info) + streamlen; nbytes += next; + buf_free_len -= next; file_info->NextEntryOffset = cpu_to_le32(next); } - if (nbytes) { + if (!S_ISDIR(stat.mode) && + buf_free_len >= sizeof(struct smb2_file_stream_info) + 7 * 2) { file_info = (struct smb2_file_stream_info *) &rsp->Buffer[nbytes]; streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, "::$DATA", 7, conn->local_nls, 0); streamlen *= 2; file_info->StreamNameLength = cpu_to_le32(streamlen); - file_info->StreamSize = S_ISDIR(stat.mode) ? 0 : - cpu_to_le64(stat.size); - file_info->StreamAllocationSize = S_ISDIR(stat.mode) ? 0 : - cpu_to_le64(stat.size); + file_info->StreamSize = 0; + file_info->StreamAllocationSize = 0; nbytes += sizeof(struct smb2_file_stream_info) + streamlen; } @@ -4602,7 +4695,7 @@ static int find_file_posix_info(struct smb2_query_info_rsp *rsp, static int smb2_get_info_file(struct ksmbd_work *work, struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp, void *rsp_org) + struct smb2_query_info_rsp *rsp) { struct ksmbd_file *fp; int fileinfoclass = 0; @@ -4613,7 +4706,8 @@ static int smb2_get_info_file(struct ksmbd_work *work, if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) { /* smb2 info file called for pipe */ - return smb2_get_info_file_pipe(work->sess, req, rsp); + return smb2_get_info_file_pipe(work->sess, req, rsp, + work->response_buf); } if (work->next_smb2_rcv_hdr_off) { @@ -4638,77 +4732,77 @@ static int smb2_get_info_file(struct ksmbd_work *work, switch (fileinfoclass) { case FILE_ACCESS_INFORMATION: - get_file_access_info(rsp, fp, rsp_org); + get_file_access_info(rsp, fp, work->response_buf); file_infoclass_size = FILE_ACCESS_INFORMATION_SIZE; break; case FILE_BASIC_INFORMATION: - rc = get_file_basic_info(rsp, fp, rsp_org); + rc = get_file_basic_info(rsp, fp, work->response_buf); file_infoclass_size = FILE_BASIC_INFORMATION_SIZE; break; case FILE_STANDARD_INFORMATION: - get_file_standard_info(rsp, fp, rsp_org); + get_file_standard_info(rsp, fp, work->response_buf); file_infoclass_size = FILE_STANDARD_INFORMATION_SIZE; break; case FILE_ALIGNMENT_INFORMATION: - get_file_alignment_info(rsp, rsp_org); + get_file_alignment_info(rsp, work->response_buf); file_infoclass_size = FILE_ALIGNMENT_INFORMATION_SIZE; break; case FILE_ALL_INFORMATION: - rc = get_file_all_info(work, rsp, fp, rsp_org); + rc = get_file_all_info(work, rsp, fp, work->response_buf); file_infoclass_size = FILE_ALL_INFORMATION_SIZE; break; case FILE_ALTERNATE_NAME_INFORMATION: - get_file_alternate_info(work, rsp, fp, rsp_org); + get_file_alternate_info(work, rsp, fp, work->response_buf); file_infoclass_size = FILE_ALTERNATE_NAME_INFORMATION_SIZE; break; case FILE_STREAM_INFORMATION: - get_file_stream_info(work, rsp, fp, rsp_org); + get_file_stream_info(work, rsp, fp, work->response_buf); file_infoclass_size = FILE_STREAM_INFORMATION_SIZE; break; case FILE_INTERNAL_INFORMATION: - get_file_internal_info(rsp, fp, rsp_org); + get_file_internal_info(rsp, fp, work->response_buf); file_infoclass_size = FILE_INTERNAL_INFORMATION_SIZE; break; case FILE_NETWORK_OPEN_INFORMATION: - rc = get_file_network_open_info(rsp, fp, rsp_org); + rc = get_file_network_open_info(rsp, fp, work->response_buf); file_infoclass_size = FILE_NETWORK_OPEN_INFORMATION_SIZE; break; case FILE_EA_INFORMATION: - get_file_ea_info(rsp, rsp_org); + get_file_ea_info(rsp, work->response_buf); file_infoclass_size = FILE_EA_INFORMATION_SIZE; break; case FILE_FULL_EA_INFORMATION: - rc = smb2_get_ea(work, fp, req, rsp, rsp_org); + rc = smb2_get_ea(work, fp, req, rsp, work->response_buf); file_infoclass_size = FILE_FULL_EA_INFORMATION_SIZE; break; case FILE_POSITION_INFORMATION: - get_file_position_info(rsp, fp, rsp_org); + get_file_position_info(rsp, fp, work->response_buf); file_infoclass_size = FILE_POSITION_INFORMATION_SIZE; break; case FILE_MODE_INFORMATION: - get_file_mode_info(rsp, fp, rsp_org); + get_file_mode_info(rsp, fp, work->response_buf); file_infoclass_size = FILE_MODE_INFORMATION_SIZE; break; case FILE_COMPRESSION_INFORMATION: - get_file_compression_info(rsp, fp, rsp_org); + get_file_compression_info(rsp, fp, work->response_buf); file_infoclass_size = FILE_COMPRESSION_INFORMATION_SIZE; break; case FILE_ATTRIBUTE_TAG_INFORMATION: - rc = get_file_attribute_tag_info(rsp, fp, rsp_org); + rc = get_file_attribute_tag_info(rsp, fp, work->response_buf); file_infoclass_size = FILE_ATTRIBUTE_TAG_INFORMATION_SIZE; break; case SMB_FIND_FILE_POSIX_INFO: @@ -4716,7 +4810,7 @@ static int smb2_get_info_file(struct ksmbd_work *work, pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); rc = -EOPNOTSUPP; } else { - rc = find_file_posix_info(rsp, fp, rsp_org); + rc = find_file_posix_info(rsp, fp, work->response_buf); file_infoclass_size = sizeof(struct smb311_posix_qinfo); } break; @@ -4727,7 +4821,7 @@ static int smb2_get_info_file(struct ksmbd_work *work, } if (!rc) rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), - rsp, + rsp, work->response_buf, file_infoclass_size); ksmbd_fd_put(work, fp); return rc; @@ -4735,7 +4829,7 @@ static int smb2_get_info_file(struct ksmbd_work *work, static int smb2_get_info_filesystem(struct ksmbd_work *work, struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp, void *rsp_org) + struct smb2_query_info_rsp *rsp) { struct ksmbd_session *sess = work->sess; struct ksmbd_conn *conn = sess->conn; @@ -4745,12 +4839,8 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, struct path path; int rc = 0, len; int fs_infoclass_size = 0; - int lookup_flags = 0; - - if (test_share_config_flag(share, KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS)) - lookup_flags = LOOKUP_FOLLOW; - rc = ksmbd_vfs_kern_path(share->path, lookup_flags, &path, 0); + rc = kern_path(share->path, LOOKUP_NO_SYMLINKS, &path); if (rc) { pr_err("cannot create vfs path\n"); return -EIO; @@ -4775,7 +4865,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->DeviceType = cpu_to_le32(stfs.f_type); info->DeviceCharacteristics = cpu_to_le32(0x00000020); rsp->OutputBufferLength = cpu_to_le32(8); - inc_rfc1001_len(rsp_org, 8); + inc_rfc1001_len(work->response_buf, 8); fs_infoclass_size = FS_DEVICE_INFORMATION_SIZE; break; } @@ -4801,7 +4891,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->FileSystemNameLen = cpu_to_le32(len); sz = sizeof(struct filesystem_attribute_info) - 2 + len; rsp->OutputBufferLength = cpu_to_le32(sz); - inc_rfc1001_len(rsp_org, sz); + inc_rfc1001_len(work->response_buf, sz); fs_infoclass_size = FS_ATTRIBUTE_INFORMATION_SIZE; break; } @@ -4809,11 +4899,18 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, { struct filesystem_vol_info *info; size_t sz; + unsigned int serial_crc = 0; info = (struct filesystem_vol_info *)(rsp->Buffer); info->VolumeCreationTime = 0; + serial_crc = crc32_le(serial_crc, share->name, + strlen(share->name)); + serial_crc = crc32_le(serial_crc, share->path, + strlen(share->path)); + serial_crc = crc32_le(serial_crc, ksmbd_netbios_name(), + strlen(ksmbd_netbios_name())); /* Taking dummy value of serial number*/ - info->SerialNumber = cpu_to_le32(0xbc3ac512); + info->SerialNumber = cpu_to_le32(serial_crc); len = smbConvertToUTF16((__le16 *)info->VolumeLabel, share->name, PATH_MAX, conn->local_nls, 0); @@ -4822,7 +4919,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->Reserved = 0; sz = sizeof(struct filesystem_vol_info) - 2 + len; rsp->OutputBufferLength = cpu_to_le32(sz); - inc_rfc1001_len(rsp_org, sz); + inc_rfc1001_len(work->response_buf, sz); fs_infoclass_size = FS_VOLUME_INFORMATION_SIZE; break; } @@ -4836,7 +4933,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->SectorsPerAllocationUnit = cpu_to_le32(1); info->BytesPerSector = cpu_to_le32(stfs.f_bsize); rsp->OutputBufferLength = cpu_to_le32(24); - inc_rfc1001_len(rsp_org, 24); + inc_rfc1001_len(work->response_buf, 24); fs_infoclass_size = FS_SIZE_INFORMATION_SIZE; break; } @@ -4853,7 +4950,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->SectorsPerAllocationUnit = cpu_to_le32(1); info->BytesPerSector = cpu_to_le32(stfs.f_bsize); rsp->OutputBufferLength = cpu_to_le32(32); - inc_rfc1001_len(rsp_org, 32); + inc_rfc1001_len(work->response_buf, 32); fs_infoclass_size = FS_FULL_SIZE_INFORMATION_SIZE; break; } @@ -4874,7 +4971,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->extended_info.rel_date = 0; memcpy(info->extended_info.version_string, "1.1.0", strlen("1.1.0")); rsp->OutputBufferLength = cpu_to_le32(64); - inc_rfc1001_len(rsp_org, 64); + inc_rfc1001_len(work->response_buf, 64); fs_infoclass_size = FS_OBJECT_ID_INFORMATION_SIZE; break; } @@ -4895,7 +4992,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->ByteOffsetForSectorAlignment = 0; info->ByteOffsetForPartitionAlignment = 0; rsp->OutputBufferLength = cpu_to_le32(28); - inc_rfc1001_len(rsp_org, 28); + inc_rfc1001_len(work->response_buf, 28); fs_infoclass_size = FS_SECTOR_SIZE_INFORMATION_SIZE; break; } @@ -4917,7 +5014,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->DefaultQuotaLimit = cpu_to_le64(SMB2_NO_FID); info->Padding = 0; rsp->OutputBufferLength = cpu_to_le32(48); - inc_rfc1001_len(rsp_org, 48); + inc_rfc1001_len(work->response_buf, 48); fs_infoclass_size = FS_CONTROL_INFORMATION_SIZE; break; } @@ -4938,7 +5035,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->TotalFileNodes = cpu_to_le64(stfs.f_files); info->FreeFileNodes = cpu_to_le64(stfs.f_ffree); rsp->OutputBufferLength = cpu_to_le32(56); - inc_rfc1001_len(rsp_org, 56); + inc_rfc1001_len(work->response_buf, 56); fs_infoclass_size = FS_POSIX_INFORMATION_SIZE; } break; @@ -4948,7 +5045,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, return -EOPNOTSUPP; } rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), - rsp, + rsp, work->response_buf, fs_infoclass_size); path_put(&path); return rc; @@ -4956,7 +5053,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, static int smb2_get_info_sec(struct ksmbd_work *work, struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp, void *rsp_org) + struct smb2_query_info_rsp *rsp) { struct ksmbd_file *fp; struct user_namespace *user_ns; @@ -4983,7 +5080,7 @@ static int smb2_get_info_sec(struct ksmbd_work *work, secdesclen = sizeof(struct smb_ntsd); rsp->OutputBufferLength = cpu_to_le32(secdesclen); - inc_rfc1001_len(rsp_org, secdesclen); + inc_rfc1001_len(work->response_buf, secdesclen); return 0; } @@ -5025,7 +5122,7 @@ static int smb2_get_info_sec(struct ksmbd_work *work, return rc; rsp->OutputBufferLength = cpu_to_le32(secdesclen); - inc_rfc1001_len(rsp_org, secdesclen); + inc_rfc1001_len(work->response_buf, secdesclen); return 0; } @@ -5038,10 +5135,9 @@ static int smb2_get_info_sec(struct ksmbd_work *work, int smb2_query_info(struct ksmbd_work *work) { struct smb2_query_info_req *req; - struct smb2_query_info_rsp *rsp, *rsp_org; + struct smb2_query_info_rsp *rsp; int rc = 0; - rsp_org = work->response_buf; WORK_BUFFERS(work, req, rsp); ksmbd_debug(SMB, "GOT query info request\n"); @@ -5049,15 +5145,15 @@ int smb2_query_info(struct ksmbd_work *work) switch (req->InfoType) { case SMB2_O_INFO_FILE: ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n"); - rc = smb2_get_info_file(work, req, rsp, (void *)rsp_org); + rc = smb2_get_info_file(work, req, rsp); break; case SMB2_O_INFO_FILESYSTEM: ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILESYSTEM\n"); - rc = smb2_get_info_filesystem(work, req, rsp, (void *)rsp_org); + rc = smb2_get_info_filesystem(work, req, rsp); break; case SMB2_O_INFO_SECURITY: ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n"); - rc = smb2_get_info_sec(work, req, rsp, (void *)rsp_org); + rc = smb2_get_info_sec(work, req, rsp); break; default: ksmbd_debug(SMB, "InfoType %d not supported yet\n", @@ -5082,7 +5178,7 @@ int smb2_query_info(struct ksmbd_work *work) } rsp->StructureSize = cpu_to_le16(9); rsp->OutputBufferOffset = cpu_to_le16(72); - inc_rfc1001_len(rsp_org, 8); + inc_rfc1001_len(work->response_buf, 8); return 0; } @@ -5095,8 +5191,8 @@ int smb2_query_info(struct ksmbd_work *work) static noinline int smb2_close_pipe(struct ksmbd_work *work) { u64 id; - struct smb2_close_req *req = work->request_buf; - struct smb2_close_rsp *rsp = work->response_buf; + struct smb2_close_req *req = smb2_get_msg(work->request_buf); + struct smb2_close_rsp *rsp = smb2_get_msg(work->response_buf); id = le64_to_cpu(req->VolatileFileId); ksmbd_session_rpc_close(work->sess, id); @@ -5111,7 +5207,7 @@ static noinline int smb2_close_pipe(struct ksmbd_work *work) rsp->AllocationSize = 0; rsp->EndOfFile = 0; rsp->Attributes = 0; - inc_rfc1001_len(rsp, 60); + inc_rfc1001_len(work->response_buf, 60); return 0; } @@ -5127,14 +5223,12 @@ int smb2_close(struct ksmbd_work *work) u64 sess_id; struct smb2_close_req *req; struct smb2_close_rsp *rsp; - struct smb2_close_rsp *rsp_org; struct ksmbd_conn *conn = work->conn; struct ksmbd_file *fp; struct inode *inode; u64 time; int err = 0; - rsp_org = work->response_buf; WORK_BUFFERS(work, req, rsp); if (test_share_config_flag(work->tcon->share_conf, @@ -5224,7 +5318,7 @@ out: rsp->hdr.Status = STATUS_FILE_CLOSED; smb2_set_err_rsp(work); } else { - inc_rfc1001_len(rsp_org, 60); + inc_rfc1001_len(work->response_buf, 60); } return 0; @@ -5238,11 +5332,11 @@ out: */ int smb2_echo(struct ksmbd_work *work) { - struct smb2_echo_rsp *rsp = work->response_buf; + struct smb2_echo_rsp *rsp = smb2_get_msg(work->response_buf); rsp->StructureSize = cpu_to_le16(4); rsp->Reserved = 0; - inc_rfc1001_len(rsp, 4); + inc_rfc1001_len(work->response_buf, 4); return 0; } @@ -5299,7 +5393,7 @@ static int smb2_rename(struct ksmbd_work *work, goto out; len = strlen(new_name); - if (new_name[len - 1] != '/') { + if (len > 0 && new_name[len - 1] != '/') { pr_err("not allow base filename in rename\n"); rc = -ESHARE; goto out; @@ -5327,11 +5421,14 @@ static int smb2_rename(struct ksmbd_work *work, } ksmbd_debug(SMB, "new name %s\n", new_name); - rc = ksmbd_vfs_kern_path(new_name, 0, &path, 1); - if (rc) + rc = ksmbd_vfs_kern_path(work, new_name, LOOKUP_NO_SYMLINKS, &path, 1); + if (rc) { + if (rc != -ENOENT) + goto out; file_present = false; - else + } else { path_put(&path); + } if (ksmbd_share_veto_filename(share, new_name)) { rc = -ENOENT; @@ -5371,7 +5468,7 @@ out: static int smb2_create_link(struct ksmbd_work *work, struct ksmbd_share_config *share, struct smb2_file_link_info *file_info, - struct file *filp, + unsigned int buf_len, struct file *filp, struct nls_table *local_nls) { char *link_name = NULL, *target_name = NULL, *pathname = NULL; @@ -5379,6 +5476,10 @@ static int smb2_create_link(struct ksmbd_work *work, bool file_present = true; int rc; + if (buf_len < (u64)sizeof(struct smb2_file_link_info) + + le32_to_cpu(file_info->FileNameLength)) + return -EINVAL; + ksmbd_debug(SMB, "setting FILE_LINK_INFORMATION\n"); pathname = kmalloc(PATH_MAX, GFP_KERNEL); if (!pathname) @@ -5401,11 +5502,14 @@ static int smb2_create_link(struct ksmbd_work *work, } ksmbd_debug(SMB, "target name is %s\n", target_name); - rc = ksmbd_vfs_kern_path(link_name, 0, &path, 0); - if (rc) + rc = ksmbd_vfs_kern_path(work, link_name, LOOKUP_NO_SYMLINKS, &path, 0); + if (rc) { + if (rc != -ENOENT) + goto out; file_present = false; - else + } else { path_put(&path); + } if (file_info->ReplaceIfExists) { if (file_present) { @@ -5435,12 +5539,11 @@ out: return rc; } -static int set_file_basic_info(struct ksmbd_file *fp, char *buf, +static int set_file_basic_info(struct ksmbd_file *fp, + struct smb2_file_basic_info *file_info, struct ksmbd_share_config *share) { - struct smb2_file_all_info *file_info; struct iattr attrs; - struct timespec64 ctime; struct file *filp; struct inode *inode; struct user_namespace *user_ns; @@ -5449,7 +5552,6 @@ static int set_file_basic_info(struct ksmbd_file *fp, char *buf, if (!(fp->daccess & FILE_WRITE_ATTRIBUTES_LE)) return -EACCES; - file_info = (struct smb2_file_all_info *)buf; attrs.ia_valid = 0; filp = fp->filp; inode = file_inode(filp); @@ -5463,13 +5565,11 @@ static int set_file_basic_info(struct ksmbd_file *fp, char *buf, attrs.ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET); } - if (file_info->ChangeTime) { + attrs.ia_valid |= ATTR_CTIME; + if (file_info->ChangeTime) attrs.ia_ctime = ksmbd_NTtimeToUnix(file_info->ChangeTime); - ctime = attrs.ia_ctime; - attrs.ia_valid |= ATTR_CTIME; - } else { - ctime = inode->i_ctime; - } + else + attrs.ia_ctime = inode->i_ctime; if (file_info->LastWriteTime) { attrs.ia_mtime = ksmbd_NTtimeToUnix(file_info->LastWriteTime); @@ -5478,14 +5578,14 @@ static int set_file_basic_info(struct ksmbd_file *fp, char *buf, if (file_info->Attributes) { if (!S_ISDIR(inode->i_mode) && - file_info->Attributes & ATTR_DIRECTORY_LE) { + file_info->Attributes & FILE_ATTRIBUTE_DIRECTORY_LE) { pr_err("can't change a file to a directory\n"); return -EINVAL; } - if (!(S_ISDIR(inode->i_mode) && file_info->Attributes == ATTR_NORMAL_LE)) + if (!(S_ISDIR(inode->i_mode) && file_info->Attributes == FILE_ATTRIBUTE_NORMAL_LE)) fp->f_ci->m_fattr = file_info->Attributes | - (fp->f_ci->m_fattr & ATTR_DIRECTORY_LE); + (fp->f_ci->m_fattr & FILE_ATTRIBUTE_DIRECTORY_LE); } if (test_share_config_flag(share, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS) && @@ -5515,18 +5615,17 @@ static int set_file_basic_info(struct ksmbd_file *fp, char *buf, return -EACCES; inode_lock(inode); + inode->i_ctime = attrs.ia_ctime; + attrs.ia_valid &= ~ATTR_CTIME; rc = notify_change(user_ns, dentry, &attrs, NULL); - if (!rc) { - inode->i_ctime = ctime; - mark_inode_dirty(inode); - } inode_unlock(inode); } return rc; } static int set_file_allocation_info(struct ksmbd_work *work, - struct ksmbd_file *fp, char *buf) + struct ksmbd_file *fp, + struct smb2_file_alloc_info *file_alloc_info) { /* * TODO : It's working fine only when store dos attributes @@ -5534,7 +5633,6 @@ static int set_file_allocation_info(struct ksmbd_work *work, * properly with any smb.conf option */ - struct smb2_file_alloc_info *file_alloc_info; loff_t alloc_blks; struct inode *inode; int rc; @@ -5542,7 +5640,6 @@ static int set_file_allocation_info(struct ksmbd_work *work, if (!(fp->daccess & FILE_WRITE_DATA_LE)) return -EACCES; - file_alloc_info = (struct smb2_file_alloc_info *)buf; alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9; inode = file_inode(fp->filp); @@ -5565,7 +5662,7 @@ static int set_file_allocation_info(struct ksmbd_work *work, * inode size is retained by backup inode size. */ size = i_size_read(inode); - rc = ksmbd_vfs_truncate(work, NULL, fp, alloc_blks * 512); + rc = ksmbd_vfs_truncate(work, fp, alloc_blks * 512); if (rc) { pr_err("truncate failed! filename : %s, err %d\n", fp->filename, rc); @@ -5578,9 +5675,8 @@ static int set_file_allocation_info(struct ksmbd_work *work, } static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp, - char *buf) + struct smb2_file_eof_info *file_eof_info) { - struct smb2_file_eof_info *file_eof_info; loff_t newsize; struct inode *inode; int rc; @@ -5588,7 +5684,6 @@ static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp, if (!(fp->daccess & FILE_WRITE_DATA_LE)) return -EACCES; - file_eof_info = (struct smb2_file_eof_info *)buf; newsize = le64_to_cpu(file_eof_info->EndOfFile); inode = file_inode(fp->filp); @@ -5602,7 +5697,7 @@ static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp, if (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC) { ksmbd_debug(SMB, "filename : %s truncated to newsize %lld\n", fp->filename, newsize); - rc = ksmbd_vfs_truncate(work, NULL, fp, newsize); + rc = ksmbd_vfs_truncate(work, fp, newsize); if (rc) { ksmbd_debug(SMB, "truncate failed! filename : %s err %d\n", fp->filename, rc); @@ -5615,7 +5710,8 @@ static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp, } static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, - char *buf) + struct smb2_file_rename_info *rename_info, + unsigned int buf_len) { struct user_namespace *user_ns; struct ksmbd_file *parent_fp; @@ -5628,6 +5724,10 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, return -EACCES; } + if (buf_len < (u64)sizeof(struct smb2_file_rename_info) + + le32_to_cpu(rename_info->FileNameLength)) + return -EINVAL; + user_ns = file_mnt_user_ns(fp->filp); if (ksmbd_stream_fd(fp)) goto next; @@ -5650,14 +5750,13 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, } } next: - return smb2_rename(work, fp, user_ns, - (struct smb2_file_rename_info *)buf, + return smb2_rename(work, fp, user_ns, rename_info, work->sess->conn->local_nls); } -static int set_file_disposition_info(struct ksmbd_file *fp, char *buf) +static int set_file_disposition_info(struct ksmbd_file *fp, + struct smb2_file_disposition_info *file_info) { - struct smb2_file_disposition_info *file_info; struct inode *inode; if (!(fp->daccess & FILE_DELETE_LE)) { @@ -5666,7 +5765,6 @@ static int set_file_disposition_info(struct ksmbd_file *fp, char *buf) } inode = file_inode(fp->filp); - file_info = (struct smb2_file_disposition_info *)buf; if (file_info->DeletePending) { if (S_ISDIR(inode->i_mode) && ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY) @@ -5678,15 +5776,14 @@ static int set_file_disposition_info(struct ksmbd_file *fp, char *buf) return 0; } -static int set_file_position_info(struct ksmbd_file *fp, char *buf) +static int set_file_position_info(struct ksmbd_file *fp, + struct smb2_file_pos_info *file_info) { - struct smb2_file_pos_info *file_info; loff_t current_byte_offset; unsigned long sector_size; struct inode *inode; inode = file_inode(fp->filp); - file_info = (struct smb2_file_pos_info *)buf; current_byte_offset = le64_to_cpu(file_info->CurrentByteOffset); sector_size = inode->i_sb->s_blocksize; @@ -5702,17 +5799,14 @@ static int set_file_position_info(struct ksmbd_file *fp, char *buf) return 0; } -static int set_file_mode_info(struct ksmbd_file *fp, char *buf) +static int set_file_mode_info(struct ksmbd_file *fp, + struct smb2_file_mode_info *file_info) { - struct smb2_file_mode_info *file_info; __le32 mode; - file_info = (struct smb2_file_mode_info *)buf; mode = file_info->Mode; - if ((mode & ~FILE_MODE_INFO_MASK) || - (mode & FILE_SYNCHRONOUS_IO_ALERT_LE && - mode & FILE_SYNCHRONOUS_IO_NONALERT_LE)) { + if ((mode & ~FILE_MODE_INFO_MASK)) { pr_err("Mode is not valid : 0x%x\n", le32_to_cpu(mode)); return -EINVAL; } @@ -5737,40 +5831,74 @@ static int set_file_mode_info(struct ksmbd_file *fp, char *buf) * TODO: need to implement an error handling for STATUS_INFO_LENGTH_MISMATCH */ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, - int info_class, char *buf, + struct smb2_set_info_req *req, struct ksmbd_share_config *share) { - switch (info_class) { + unsigned int buf_len = le32_to_cpu(req->BufferLength); + + switch (req->FileInfoClass) { case FILE_BASIC_INFORMATION: - return set_file_basic_info(fp, buf, share); + { + if (buf_len < sizeof(struct smb2_file_basic_info)) + return -EINVAL; + return set_file_basic_info(fp, (struct smb2_file_basic_info *)req->Buffer, share); + } case FILE_ALLOCATION_INFORMATION: - return set_file_allocation_info(work, fp, buf); + { + if (buf_len < sizeof(struct smb2_file_alloc_info)) + return -EINVAL; + return set_file_allocation_info(work, fp, + (struct smb2_file_alloc_info *)req->Buffer); + } case FILE_END_OF_FILE_INFORMATION: - return set_end_of_file_info(work, fp, buf); + { + if (buf_len < sizeof(struct smb2_file_eof_info)) + return -EINVAL; + return set_end_of_file_info(work, fp, + (struct smb2_file_eof_info *)req->Buffer); + } case FILE_RENAME_INFORMATION: + { if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { ksmbd_debug(SMB, "User does not have write permission\n"); return -EACCES; } - return set_rename_info(work, fp, buf); + if (buf_len < sizeof(struct smb2_file_rename_info)) + return -EINVAL; + + return set_rename_info(work, fp, + (struct smb2_file_rename_info *)req->Buffer, + buf_len); + } case FILE_LINK_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_link_info)) + return -EINVAL; + return smb2_create_link(work, work->tcon->share_conf, - (struct smb2_file_link_info *)buf, fp->filp, + (struct smb2_file_link_info *)req->Buffer, + buf_len, fp->filp, work->sess->conn->local_nls); - + } case FILE_DISPOSITION_INFORMATION: + { if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { ksmbd_debug(SMB, "User does not have write permission\n"); return -EACCES; } - return set_file_disposition_info(fp, buf); + if (buf_len < sizeof(struct smb2_file_disposition_info)) + return -EINVAL; + + return set_file_disposition_info(fp, + (struct smb2_file_disposition_info *)req->Buffer); + } case FILE_FULL_EA_INFORMATION: { if (!(fp->daccess & FILE_WRITE_EA_LE)) { @@ -5779,18 +5907,29 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, return -EACCES; } - return smb2_set_ea((struct smb2_ea_info *)buf, - &fp->filp->f_path); - } + if (buf_len < sizeof(struct smb2_ea_info)) + return -EINVAL; + return smb2_set_ea((struct smb2_ea_info *)req->Buffer, + buf_len, &fp->filp->f_path); + } case FILE_POSITION_INFORMATION: - return set_file_position_info(fp, buf); + { + if (buf_len < sizeof(struct smb2_file_pos_info)) + return -EINVAL; + return set_file_position_info(fp, (struct smb2_file_pos_info *)req->Buffer); + } case FILE_MODE_INFORMATION: - return set_file_mode_info(fp, buf); + { + if (buf_len < sizeof(struct smb2_file_mode_info)) + return -EINVAL; + + return set_file_mode_info(fp, (struct smb2_file_mode_info *)req->Buffer); + } } - pr_err("Unimplemented Fileinfoclass :%d\n", info_class); + pr_err("Unimplemented Fileinfoclass :%d\n", req->FileInfoClass); return -EOPNOTSUPP; } @@ -5814,14 +5953,13 @@ static int smb2_set_info_sec(struct ksmbd_file *fp, int addition_info, int smb2_set_info(struct ksmbd_work *work) { struct smb2_set_info_req *req; - struct smb2_set_info_rsp *rsp, *rsp_org; + struct smb2_set_info_rsp *rsp; struct ksmbd_file *fp; int rc = 0; unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; ksmbd_debug(SMB, "Received set info request\n"); - rsp_org = work->response_buf; if (work->next_smb2_rcv_hdr_off) { req = ksmbd_req_buf_next(work); rsp = ksmbd_resp_buf_next(work); @@ -5832,8 +5970,8 @@ int smb2_set_info(struct ksmbd_work *work) pid = work->compound_pfid; } } else { - req = work->request_buf; - rsp = work->response_buf; + req = smb2_get_msg(work->request_buf); + rsp = smb2_get_msg(work->response_buf); } if (!has_file_id(id)) { @@ -5851,8 +5989,7 @@ int smb2_set_info(struct ksmbd_work *work) switch (req->InfoType) { case SMB2_O_INFO_FILE: ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n"); - rc = smb2_set_info_file(work, fp, req->FileInfoClass, - req->Buffer, work->tcon->share_conf); + rc = smb2_set_info_file(work, fp, req, work->tcon->share_conf); break; case SMB2_O_INFO_SECURITY: ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n"); @@ -5874,12 +6011,12 @@ int smb2_set_info(struct ksmbd_work *work) goto err_out; rsp->StructureSize = cpu_to_le16(2); - inc_rfc1001_len(rsp_org, 2); + inc_rfc1001_len(work->response_buf, 2); ksmbd_fd_put(work, fp); return 0; err_out: - if (rc == -EACCES || rc == -EPERM) + if (rc == -EACCES || rc == -EPERM || rc == -EXDEV) rsp->hdr.Status = STATUS_ACCESS_DENIED; else if (rc == -EINVAL) rsp->hdr.Status = STATUS_INVALID_PARAMETER; @@ -5914,12 +6051,12 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work) int nbytes = 0, err; u64 id; struct ksmbd_rpc_command *rpc_resp; - struct smb2_read_req *req = work->request_buf; - struct smb2_read_rsp *rsp = work->response_buf; + struct smb2_read_req *req = smb2_get_msg(work->request_buf); + struct smb2_read_rsp *rsp = smb2_get_msg(work->response_buf); id = le64_to_cpu(req->VolatileFileId); - inc_rfc1001_len(rsp, 16); + inc_rfc1001_len(work->response_buf, 16); rpc_resp = ksmbd_rpc_read(work->sess, id); if (rpc_resp) { if (rpc_resp->flags != KSMBD_RPC_OK) { @@ -5938,7 +6075,7 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work) rpc_resp->payload_sz); nbytes = rpc_resp->payload_sz; - work->resp_hdr_sz = get_rfc1002_len(rsp) + 4; + work->resp_hdr_sz = get_rfc1002_len(work->response_buf) + 4; work->aux_payload_sz = nbytes; kvfree(rpc_resp); } @@ -5948,8 +6085,8 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work) rsp->Reserved = 0; rsp->DataLength = cpu_to_le32(nbytes); rsp->DataRemaining = 0; - rsp->Reserved2 = 0; - inc_rfc1001_len(rsp, nbytes); + rsp->Flags = 0; + inc_rfc1001_len(work->response_buf, nbytes); return 0; out: @@ -5999,14 +6136,13 @@ int smb2_read(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; struct smb2_read_req *req; - struct smb2_read_rsp *rsp, *rsp_org; + struct smb2_read_rsp *rsp; struct ksmbd_file *fp; loff_t offset; size_t length, mincount; ssize_t nbytes = 0, remain_bytes = 0; int err = 0; - rsp_org = work->response_buf; WORK_BUFFERS(work, req, rsp); if (test_share_config_flag(work->tcon->share_conf, @@ -6087,11 +6223,11 @@ int smb2_read(struct ksmbd_work *work) rsp->Reserved = 0; rsp->DataLength = cpu_to_le32(nbytes); rsp->DataRemaining = cpu_to_le32(remain_bytes); - rsp->Reserved2 = 0; - inc_rfc1001_len(rsp_org, 16); - work->resp_hdr_sz = get_rfc1002_len(rsp_org) + 4; + rsp->Flags = 0; + inc_rfc1001_len(work->response_buf, 16); + work->resp_hdr_sz = get_rfc1002_len(work->response_buf) + 4; work->aux_payload_sz = nbytes; - inc_rfc1001_len(rsp_org, nbytes); + inc_rfc1001_len(work->response_buf, nbytes); ksmbd_fd_put(work, fp); return 0; @@ -6126,8 +6262,8 @@ out: */ static noinline int smb2_write_pipe(struct ksmbd_work *work) { - struct smb2_write_req *req = work->request_buf; - struct smb2_write_rsp *rsp = work->response_buf; + struct smb2_write_req *req = smb2_get_msg(work->request_buf); + struct smb2_write_rsp *rsp = smb2_get_msg(work->response_buf); struct ksmbd_rpc_command *rpc_resp; u64 id = 0; int err = 0, ret = 0; @@ -6138,14 +6274,14 @@ static noinline int smb2_write_pipe(struct ksmbd_work *work) id = le64_to_cpu(req->VolatileFileId); if (le16_to_cpu(req->DataOffset) == - (offsetof(struct smb2_write_req, Buffer) - 4)) { + offsetof(struct smb2_write_req, Buffer)) { data_buf = (char *)&req->Buffer[0]; } else { - if ((le16_to_cpu(req->DataOffset) > get_rfc1002_len(req)) || - (le16_to_cpu(req->DataOffset) + length > get_rfc1002_len(req))) { + if ((u64)le16_to_cpu(req->DataOffset) + length > + get_rfc1002_len(work->request_buf)) { pr_err("invalid write data offset %u, smb_len %u\n", le16_to_cpu(req->DataOffset), - get_rfc1002_len(req)); + get_rfc1002_len(work->request_buf)); err = -EINVAL; goto out; } @@ -6177,7 +6313,7 @@ static noinline int smb2_write_pipe(struct ksmbd_work *work) rsp->DataLength = cpu_to_le32(length); rsp->DataRemaining = 0; rsp->Reserved2 = 0; - inc_rfc1001_len(rsp, 16); + inc_rfc1001_len(work->response_buf, 16); return 0; out: if (err) { @@ -6245,7 +6381,7 @@ static ssize_t smb2_write_rdma_channel(struct ksmbd_work *work, int smb2_write(struct ksmbd_work *work) { struct smb2_write_req *req; - struct smb2_write_rsp *rsp, *rsp_org; + struct smb2_write_rsp *rsp; struct ksmbd_file *fp = NULL; loff_t offset; size_t length; @@ -6254,7 +6390,6 @@ int smb2_write(struct ksmbd_work *work) bool writethrough = false; int err = 0; - rsp_org = work->response_buf; WORK_BUFFERS(work, req, rsp); if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) { @@ -6297,14 +6432,14 @@ int smb2_write(struct ksmbd_work *work) if (req->Channel != SMB2_CHANNEL_RDMA_V1 && req->Channel != SMB2_CHANNEL_RDMA_V1_INVALIDATE) { if (le16_to_cpu(req->DataOffset) == - (offsetof(struct smb2_write_req, Buffer) - 4)) { + offsetof(struct smb2_write_req, Buffer)) { data_buf = (char *)&req->Buffer[0]; } else { - if ((le16_to_cpu(req->DataOffset) > get_rfc1002_len(req)) || - (le16_to_cpu(req->DataOffset) + length > get_rfc1002_len(req))) { + if ((u64)le16_to_cpu(req->DataOffset) + length > + get_rfc1002_len(work->request_buf)) { pr_err("invalid write data offset %u, smb_len %u\n", le16_to_cpu(req->DataOffset), - get_rfc1002_len(req)); + get_rfc1002_len(work->request_buf)); err = -EINVAL; goto out; } @@ -6342,7 +6477,7 @@ int smb2_write(struct ksmbd_work *work) rsp->DataLength = cpu_to_le32(nbytes); rsp->DataRemaining = 0; rsp->Reserved2 = 0; - inc_rfc1001_len(rsp_org, 16); + inc_rfc1001_len(work->response_buf, 16); ksmbd_fd_put(work, fp); return 0; @@ -6376,10 +6511,9 @@ out: int smb2_flush(struct ksmbd_work *work) { struct smb2_flush_req *req; - struct smb2_flush_rsp *rsp, *rsp_org; + struct smb2_flush_rsp *rsp; int err; - rsp_org = work->response_buf; WORK_BUFFERS(work, req, rsp); ksmbd_debug(SMB, "SMB2_FLUSH called for fid %llu\n", @@ -6393,7 +6527,7 @@ int smb2_flush(struct ksmbd_work *work) rsp->StructureSize = cpu_to_le16(4); rsp->Reserved = 0; - inc_rfc1001_len(rsp_org, 4); + inc_rfc1001_len(work->response_buf, 4); return 0; out: @@ -6414,7 +6548,7 @@ out: int smb2_cancel(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_hdr *hdr = work->request_buf; + struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); struct smb2_hdr *chdr; struct ksmbd_work *cancel_work = NULL; int canceled = 0; @@ -6429,7 +6563,7 @@ int smb2_cancel(struct ksmbd_work *work) spin_lock(&conn->request_lock); list_for_each_entry(cancel_work, command_list, async_request_entry) { - chdr = cancel_work->request_buf; + chdr = smb2_get_msg(cancel_work->request_buf); if (cancel_work->async_id != le64_to_cpu(hdr->Id.AsyncId)) @@ -6448,7 +6582,7 @@ int smb2_cancel(struct ksmbd_work *work) spin_lock(&conn->request_lock); list_for_each_entry(cancel_work, command_list, request_entry) { - chdr = cancel_work->request_buf; + chdr = smb2_get_msg(cancel_work->request_buf); if (chdr->MessageId != hdr->MessageId || cancel_work == work) @@ -6583,8 +6717,8 @@ static inline bool lock_defer_pending(struct file_lock *fl) */ int smb2_lock(struct ksmbd_work *work) { - struct smb2_lock_req *req = work->request_buf; - struct smb2_lock_rsp *rsp = work->response_buf; + struct smb2_lock_req *req = smb2_get_msg(work->request_buf); + struct smb2_lock_rsp *rsp = smb2_get_msg(work->response_buf); struct smb2_lock_element *lock_ele; struct ksmbd_file *fp = NULL; struct file_lock *flock = NULL; @@ -6891,7 +7025,7 @@ skip: ksmbd_debug(SMB, "successful in taking lock\n"); rsp->hdr.Status = STATUS_SUCCESS; rsp->Reserved = 0; - inc_rfc1001_len(rsp, 4); + inc_rfc1001_len(work->response_buf, 4); ksmbd_fd_put(work, fp); return 0; @@ -6944,24 +7078,26 @@ out2: return err; } -static int fsctl_copychunk(struct ksmbd_work *work, struct smb2_ioctl_req *req, +static int fsctl_copychunk(struct ksmbd_work *work, + struct copychunk_ioctl_req *ci_req, + unsigned int cnt_code, + unsigned int input_count, + unsigned long long volatile_id, + unsigned long long persistent_id, struct smb2_ioctl_rsp *rsp) { - struct copychunk_ioctl_req *ci_req; struct copychunk_ioctl_rsp *ci_rsp; struct ksmbd_file *src_fp = NULL, *dst_fp = NULL; struct srv_copychunk *chunks; unsigned int i, chunk_count, chunk_count_written = 0; unsigned int chunk_size_written = 0; loff_t total_size_written = 0; - int ret, cnt_code; + int ret = 0; - cnt_code = le32_to_cpu(req->CntCode); - ci_req = (struct copychunk_ioctl_req *)&req->Buffer[0]; ci_rsp = (struct copychunk_ioctl_rsp *)&rsp->Buffer[0]; - rsp->VolatileFileId = req->VolatileFileId; - rsp->PersistentFileId = req->PersistentFileId; + rsp->VolatileFileId = cpu_to_le64(volatile_id); + rsp->PersistentFileId = cpu_to_le64(persistent_id); ci_rsp->ChunksWritten = cpu_to_le32(ksmbd_server_side_copy_max_chunk_count()); ci_rsp->ChunkBytesWritten = @@ -6971,12 +7107,13 @@ static int fsctl_copychunk(struct ksmbd_work *work, struct smb2_ioctl_req *req, chunks = (struct srv_copychunk *)&ci_req->Chunks[0]; chunk_count = le32_to_cpu(ci_req->ChunkCount); + if (chunk_count == 0) + goto out; total_size_written = 0; /* verify the SRV_COPYCHUNK_COPY packet */ if (chunk_count > ksmbd_server_side_copy_max_chunk_count() || - le32_to_cpu(req->InputCount) < - offsetof(struct copychunk_ioctl_req, Chunks) + + input_count < offsetof(struct copychunk_ioctl_req, Chunks) + chunk_count * sizeof(struct srv_copychunk)) { rsp->hdr.Status = STATUS_INVALID_PARAMETER; return -EINVAL; @@ -6997,9 +7134,7 @@ static int fsctl_copychunk(struct ksmbd_work *work, struct smb2_ioctl_req *req, src_fp = ksmbd_lookup_foreign_fd(work, le64_to_cpu(ci_req->ResumeKey[0])); - dst_fp = ksmbd_lookup_fd_slow(work, - le64_to_cpu(req->VolatileFileId), - le64_to_cpu(req->PersistentFileId)); + dst_fp = ksmbd_lookup_fd_slow(work, volatile_id, persistent_id); ret = -EINVAL; if (!src_fp || src_fp->persistent_id != le64_to_cpu(ci_req->ResumeKey[1])) { @@ -7074,8 +7209,8 @@ static __be32 idev_ipv4_address(struct in_device *idev) } static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, - struct smb2_ioctl_req *req, - struct smb2_ioctl_rsp *rsp) + struct smb2_ioctl_rsp *rsp, + unsigned int out_buf_len) { struct network_interface_info_ioctl_rsp *nii_rsp = NULL; int nbytes = 0; @@ -7087,6 +7222,12 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, rtnl_lock(); for_each_netdev(&init_net, netdev) { + if (out_buf_len < + nbytes + sizeof(struct network_interface_info_ioctl_rsp)) { + rtnl_unlock(); + return -ENOSPC; + } + if (netdev->type == ARPHRD_LOOPBACK) continue; @@ -7166,11 +7307,6 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, if (nii_rsp) nii_rsp->Next = 0; - if (!nbytes) { - rsp->hdr.Status = STATUS_BUFFER_TOO_SMALL; - return -EINVAL; - } - rsp->PersistentFileId = cpu_to_le64(SMB2_NO_FID); rsp->VolatileFileId = cpu_to_le64(SMB2_NO_FID); return nbytes; @@ -7178,11 +7314,16 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn, struct validate_negotiate_info_req *neg_req, - struct validate_negotiate_info_rsp *neg_rsp) + struct validate_negotiate_info_rsp *neg_rsp, + unsigned int in_buf_len) { int ret = 0; int dialect; + if (in_buf_len < offsetof(struct validate_negotiate_info_req, Dialects) + + le16_to_cpu(neg_req->DialectCount) * sizeof(__le16)) + return -EINVAL; + dialect = ksmbd_lookup_dialect_by_id(neg_req->Dialects, neg_req->DialectCount); if (dialect == BAD_PROT_ID || dialect != conn->dialect) { @@ -7216,7 +7357,7 @@ err_out: static int fsctl_query_allocated_ranges(struct ksmbd_work *work, u64 id, struct file_allocated_range_buffer *qar_req, struct file_allocated_range_buffer *qar_rsp, - int in_count, int *out_count) + unsigned int in_count, unsigned int *out_count) { struct ksmbd_file *fp; loff_t start, length; @@ -7243,7 +7384,8 @@ static int fsctl_query_allocated_ranges(struct ksmbd_work *work, u64 id, } static int fsctl_pipe_transceive(struct ksmbd_work *work, u64 id, - int out_buf_len, struct smb2_ioctl_req *req, + unsigned int out_buf_len, + struct smb2_ioctl_req *req, struct smb2_ioctl_rsp *rsp) { struct ksmbd_rpc_command *rpc_resp; @@ -7301,9 +7443,9 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id, old_fattr = fp->f_ci->m_fattr; if (sparse->SetSparse) - fp->f_ci->m_fattr |= ATTR_SPARSE_FILE_LE; + fp->f_ci->m_fattr |= FILE_ATTRIBUTE_SPARSE_FILE_LE; else - fp->f_ci->m_fattr &= ~ATTR_SPARSE_FILE_LE; + fp->f_ci->m_fattr &= ~FILE_ATTRIBUTE_SPARSE_FILE_LE; if (fp->f_ci->m_fattr != old_fattr && test_share_config_flag(work->tcon->share_conf, @@ -7356,14 +7498,12 @@ static int fsctl_request_resume_key(struct ksmbd_work *work, int smb2_ioctl(struct ksmbd_work *work) { struct smb2_ioctl_req *req; - struct smb2_ioctl_rsp *rsp, *rsp_org; - int cnt_code, nbytes = 0; - int out_buf_len; + struct smb2_ioctl_rsp *rsp; + unsigned int cnt_code, nbytes = 0, out_buf_len, in_buf_len; u64 id = KSMBD_NO_FID; struct ksmbd_conn *conn = work->conn; int ret = 0; - rsp_org = work->response_buf; if (work->next_smb2_rcv_hdr_off) { req = ksmbd_req_buf_next(work); rsp = ksmbd_resp_buf_next(work); @@ -7373,8 +7513,8 @@ int smb2_ioctl(struct ksmbd_work *work) id = work->compound_fid; } } else { - req = work->request_buf; - rsp = work->response_buf; + req = smb2_get_msg(work->request_buf); + rsp = smb2_get_msg(work->response_buf); } if (!has_file_id(id)) @@ -7386,8 +7526,14 @@ int smb2_ioctl(struct ksmbd_work *work) } cnt_code = le32_to_cpu(req->CntCode); - out_buf_len = le32_to_cpu(req->MaxOutputResponse); - out_buf_len = min(KSMBD_IPC_MAX_PAYLOAD, out_buf_len); + ret = smb2_calc_max_out_buf_len(work, 48, + le32_to_cpu(req->MaxOutputResponse)); + if (ret < 0) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + goto out; + } + out_buf_len = (unsigned int)ret; + in_buf_len = le32_to_cpu(req->InputCount); switch (cnt_code) { case FSCTL_DFS_GET_REFERRALS: @@ -7415,6 +7561,7 @@ int smb2_ioctl(struct ksmbd_work *work) break; } case FSCTL_PIPE_TRANSCEIVE: + out_buf_len = min_t(u32, KSMBD_IPC_MAX_PAYLOAD, out_buf_len); nbytes = fsctl_pipe_transceive(work, id, out_buf_len, req, rsp); break; case FSCTL_VALIDATE_NEGOTIATE_INFO: @@ -7423,9 +7570,16 @@ int smb2_ioctl(struct ksmbd_work *work) goto out; } + if (in_buf_len < sizeof(struct validate_negotiate_info_req)) + return -EINVAL; + + if (out_buf_len < sizeof(struct validate_negotiate_info_rsp)) + return -EINVAL; + ret = fsctl_validate_negotiate_info(conn, (struct validate_negotiate_info_req *)&req->Buffer[0], - (struct validate_negotiate_info_rsp *)&rsp->Buffer[0]); + (struct validate_negotiate_info_rsp *)&rsp->Buffer[0], + in_buf_len); if (ret < 0) goto out; @@ -7434,9 +7588,10 @@ int smb2_ioctl(struct ksmbd_work *work) rsp->VolatileFileId = cpu_to_le64(SMB2_NO_FID); break; case FSCTL_QUERY_NETWORK_INTERFACE_INFO: - nbytes = fsctl_query_iface_info_ioctl(conn, req, rsp); - if (nbytes < 0) + ret = fsctl_query_iface_info_ioctl(conn, rsp, out_buf_len); + if (ret < 0) goto out; + nbytes = ret; break; case FSCTL_REQUEST_RESUME_KEY: if (out_buf_len < sizeof(struct resume_key_ioctl_rsp)) { @@ -7461,15 +7616,33 @@ int smb2_ioctl(struct ksmbd_work *work) goto out; } + if (in_buf_len < sizeof(struct copychunk_ioctl_req)) { + ret = -EINVAL; + goto out; + } + if (out_buf_len < sizeof(struct copychunk_ioctl_rsp)) { ret = -EINVAL; goto out; } nbytes = sizeof(struct copychunk_ioctl_rsp); - fsctl_copychunk(work, req, rsp); + rsp->VolatileFileId = req->VolatileFileId; + rsp->PersistentFileId = req->PersistentFileId; + fsctl_copychunk(work, + (struct copychunk_ioctl_req *)&req->Buffer[0], + le32_to_cpu(req->CntCode), + le32_to_cpu(req->InputCount), + le64_to_cpu(req->VolatileFileId), + le64_to_cpu(req->PersistentFileId), + rsp); break; case FSCTL_SET_SPARSE: + if (in_buf_len < sizeof(struct file_sparse)) { + ret = -EINVAL; + goto out; + } + ret = fsctl_set_sparse(work, id, (struct file_sparse *)&req->Buffer[0]); if (ret < 0) @@ -7488,6 +7661,11 @@ int smb2_ioctl(struct ksmbd_work *work) goto out; } + if (in_buf_len < sizeof(struct file_zero_data_information)) { + ret = -EINVAL; + goto out; + } + zero_data = (struct file_zero_data_information *)&req->Buffer[0]; @@ -7507,6 +7685,11 @@ int smb2_ioctl(struct ksmbd_work *work) break; } case FSCTL_QUERY_ALLOCATED_RANGES: + if (in_buf_len < sizeof(struct file_allocated_range_buffer)) { + ret = -EINVAL; + goto out; + } + ret = fsctl_query_allocated_ranges(work, id, (struct file_allocated_range_buffer *)&req->Buffer[0], (struct file_allocated_range_buffer *)&rsp->Buffer[0], @@ -7547,6 +7730,11 @@ int smb2_ioctl(struct ksmbd_work *work) struct duplicate_extents_to_file *dup_ext; loff_t src_off, dst_off, length, cloned; + if (in_buf_len < sizeof(struct duplicate_extents_to_file)) { + ret = -EINVAL; + goto out; + } + dup_ext = (struct duplicate_extents_to_file *)&req->Buffer[0]; fp_in = ksmbd_lookup_fd_slow(work, dup_ext->VolatileFileHandle, @@ -7606,7 +7794,7 @@ dup_ext_out: rsp->Reserved = cpu_to_le16(0); rsp->Flags = cpu_to_le32(0); rsp->Reserved2 = cpu_to_le32(0); - inc_rfc1001_len(rsp_org, 48 + nbytes); + inc_rfc1001_len(work->response_buf, 48 + nbytes); return 0; @@ -7617,6 +7805,8 @@ out: rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; else if (ret == -EOPNOTSUPP) rsp->hdr.Status = STATUS_NOT_SUPPORTED; + else if (ret == -ENOSPC) + rsp->hdr.Status = STATUS_BUFFER_TOO_SMALL; else if (ret < 0 || rsp->hdr.Status == 0) rsp->hdr.Status = STATUS_INVALID_PARAMETER; smb2_set_err_rsp(work); @@ -7631,8 +7821,8 @@ out: */ static void smb20_oplock_break_ack(struct ksmbd_work *work) { - struct smb2_oplock_break *req = work->request_buf; - struct smb2_oplock_break *rsp = work->response_buf; + struct smb2_oplock_break *req = smb2_get_msg(work->request_buf); + struct smb2_oplock_break *rsp = smb2_get_msg(work->response_buf); struct ksmbd_file *fp; struct oplock_info *opinfo = NULL; __le32 err = 0; @@ -7739,7 +7929,7 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work) rsp->Reserved2 = 0; rsp->VolatileFid = cpu_to_le64(volatile_id); rsp->PersistentFid = cpu_to_le64(persistent_id); - inc_rfc1001_len(rsp, 24); + inc_rfc1001_len(work->response_buf, 24); return; err_out: @@ -7775,8 +7965,8 @@ static int check_lease_state(struct lease *lease, __le32 req_state) static void smb21_lease_break_ack(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_lease_ack *req = work->request_buf; - struct smb2_lease_ack *rsp = work->response_buf; + struct smb2_lease_ack *req = smb2_get_msg(work->request_buf); + struct smb2_lease_ack *rsp = smb2_get_msg(work->response_buf); struct oplock_info *opinfo; __le32 err = 0; int ret = 0; @@ -7888,7 +8078,7 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) memcpy(rsp->LeaseKey, req->LeaseKey, 16); rsp->LeaseState = lease_state; rsp->LeaseDuration = 0; - inc_rfc1001_len(rsp, 36); + inc_rfc1001_len(work->response_buf, 36); return; err_out: @@ -7909,8 +8099,8 @@ err_out: */ int smb2_oplock_break(struct ksmbd_work *work) { - struct smb2_oplock_break *req = work->request_buf; - struct smb2_oplock_break *rsp = work->response_buf; + struct smb2_oplock_break *req = smb2_get_msg(work->request_buf); + struct smb2_oplock_break *rsp = smb2_get_msg(work->response_buf); switch (le16_to_cpu(req->StructureSize)) { case OP_BREAK_STRUCT_SIZE_20: @@ -7937,8 +8127,8 @@ int smb2_oplock_break(struct ksmbd_work *work) */ int smb2_notify(struct ksmbd_work *work) { - struct smb2_notify_req *req; - struct smb2_notify_rsp *rsp; + struct smb2_change_notify_req *req; + struct smb2_change_notify_rsp *rsp; WORK_BUFFERS(work, req, rsp); @@ -7962,7 +8152,7 @@ int smb2_notify(struct ksmbd_work *work) */ bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command) { - struct smb2_hdr *rcv_hdr2 = work->request_buf; + struct smb2_hdr *rcv_hdr2 = smb2_get_msg(work->request_buf); if ((rcv_hdr2->Flags & SMB2_FLAGS_SIGNED) && command != SMB2_NEGOTIATE_HE && @@ -7981,22 +8171,22 @@ bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command) */ int smb2_check_sign_req(struct ksmbd_work *work) { - struct smb2_hdr *hdr, *hdr_org; + struct smb2_hdr *hdr; char signature_req[SMB2_SIGNATURE_SIZE]; char signature[SMB2_HMACSHA256_SIZE]; struct kvec iov[1]; size_t len; - hdr_org = hdr = work->request_buf; + hdr = smb2_get_msg(work->request_buf); if (work->next_smb2_rcv_hdr_off) hdr = ksmbd_req_buf_next(work); if (!hdr->NextCommand && !work->next_smb2_rcv_hdr_off) - len = be32_to_cpu(hdr_org->smb2_buf_length); + len = get_rfc1002_len(work->request_buf); else if (hdr->NextCommand) len = le32_to_cpu(hdr->NextCommand); else - len = be32_to_cpu(hdr_org->smb2_buf_length) - + len = get_rfc1002_len(work->request_buf) - work->next_smb2_rcv_hdr_off; memcpy(signature_req, hdr->Signature, SMB2_SIGNATURE_SIZE); @@ -8024,25 +8214,26 @@ int smb2_check_sign_req(struct ksmbd_work *work) */ void smb2_set_sign_rsp(struct ksmbd_work *work) { - struct smb2_hdr *hdr, *hdr_org; + struct smb2_hdr *hdr; struct smb2_hdr *req_hdr; char signature[SMB2_HMACSHA256_SIZE]; struct kvec iov[2]; size_t len; int n_vec = 1; - hdr_org = hdr = work->response_buf; + hdr = smb2_get_msg(work->response_buf); if (work->next_smb2_rsp_hdr_off) hdr = ksmbd_resp_buf_next(work); req_hdr = ksmbd_req_buf_next(work); if (!work->next_smb2_rsp_hdr_off) { - len = get_rfc1002_len(hdr_org); + len = get_rfc1002_len(work->response_buf); if (req_hdr->NextCommand) len = ALIGN(len, 8); } else { - len = get_rfc1002_len(hdr_org) - work->next_smb2_rsp_hdr_off; + len = get_rfc1002_len(work->response_buf) - + work->next_smb2_rsp_hdr_off; len = ALIGN(len, 8); } @@ -8078,23 +8269,23 @@ int smb3_check_sign_req(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; char *signing_key; - struct smb2_hdr *hdr, *hdr_org; + struct smb2_hdr *hdr; struct channel *chann; char signature_req[SMB2_SIGNATURE_SIZE]; char signature[SMB2_CMACAES_SIZE]; struct kvec iov[1]; size_t len; - hdr_org = hdr = work->request_buf; + hdr = smb2_get_msg(work->request_buf); if (work->next_smb2_rcv_hdr_off) hdr = ksmbd_req_buf_next(work); if (!hdr->NextCommand && !work->next_smb2_rcv_hdr_off) - len = be32_to_cpu(hdr_org->smb2_buf_length); + len = get_rfc1002_len(work->request_buf); else if (hdr->NextCommand) len = le32_to_cpu(hdr->NextCommand); else - len = be32_to_cpu(hdr_org->smb2_buf_length) - + len = get_rfc1002_len(work->request_buf) - work->next_smb2_rcv_hdr_off; if (le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { @@ -8135,8 +8326,7 @@ int smb3_check_sign_req(struct ksmbd_work *work) void smb3_set_sign_rsp(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_hdr *req_hdr; - struct smb2_hdr *hdr, *hdr_org; + struct smb2_hdr *req_hdr, *hdr; struct channel *chann; char signature[SMB2_CMACAES_SIZE]; struct kvec iov[2]; @@ -8144,18 +8334,19 @@ void smb3_set_sign_rsp(struct ksmbd_work *work) size_t len; char *signing_key; - hdr_org = hdr = work->response_buf; + hdr = smb2_get_msg(work->response_buf); if (work->next_smb2_rsp_hdr_off) hdr = ksmbd_resp_buf_next(work); req_hdr = ksmbd_req_buf_next(work); if (!work->next_smb2_rsp_hdr_off) { - len = get_rfc1002_len(hdr_org); + len = get_rfc1002_len(work->response_buf); if (req_hdr->NextCommand) len = ALIGN(len, 8); } else { - len = get_rfc1002_len(hdr_org) - work->next_smb2_rsp_hdr_off; + len = get_rfc1002_len(work->response_buf) - + work->next_smb2_rsp_hdr_off; len = ALIGN(len, 8); } @@ -8206,8 +8397,9 @@ void smb3_preauth_hash_rsp(struct ksmbd_work *work) WORK_BUFFERS(work, req, rsp); - if (le16_to_cpu(req->Command) == SMB2_NEGOTIATE_HE) - ksmbd_gen_preauth_integrity_hash(conn, (char *)rsp, + if (le16_to_cpu(req->Command) == SMB2_NEGOTIATE_HE && + conn->preauth_info) + ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, conn->preauth_info->Preauth_HashValue); if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && sess) { @@ -8225,35 +8417,34 @@ void smb3_preauth_hash_rsp(struct ksmbd_work *work) if (!hash_value) return; } - ksmbd_gen_preauth_integrity_hash(conn, (char *)rsp, + ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, hash_value); } } -static void fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, char *old_buf, - __le16 cipher_type) +static void fill_transform_hdr(void *tr_buf, char *old_buf, __le16 cipher_type) { - struct smb2_hdr *hdr = (struct smb2_hdr *)old_buf; + struct smb2_transform_hdr *tr_hdr = tr_buf + 4; + struct smb2_hdr *hdr = smb2_get_msg(old_buf); unsigned int orig_len = get_rfc1002_len(old_buf); - memset(tr_hdr, 0, sizeof(struct smb2_transform_hdr)); + memset(tr_buf, 0, sizeof(struct smb2_transform_hdr) + 4); tr_hdr->ProtocolId = SMB2_TRANSFORM_PROTO_NUM; tr_hdr->OriginalMessageSize = cpu_to_le32(orig_len); - tr_hdr->Flags = cpu_to_le16(0x01); + tr_hdr->Flags = cpu_to_le16(TRANSFORM_FLAG_ENCRYPTED); if (cipher_type == SMB2_ENCRYPTION_AES128_GCM || cipher_type == SMB2_ENCRYPTION_AES256_GCM) get_random_bytes(&tr_hdr->Nonce, SMB3_AES_GCM_NONCE); else get_random_bytes(&tr_hdr->Nonce, SMB3_AES_CCM_NONCE); memcpy(&tr_hdr->SessionId, &hdr->SessionId, 8); - inc_rfc1001_len(tr_hdr, sizeof(struct smb2_transform_hdr) - 4); - inc_rfc1001_len(tr_hdr, orig_len); + inc_rfc1001_len(tr_buf, sizeof(struct smb2_transform_hdr)); + inc_rfc1001_len(tr_buf, orig_len); } int smb3_encrypt_resp(struct ksmbd_work *work) { char *buf = work->response_buf; - struct smb2_transform_hdr *tr_hdr; struct kvec iov[3]; int rc = -ENOMEM; int buf_size = 0, rq_nvec = 2 + (work->aux_payload_sz ? 1 : 0); @@ -8261,15 +8452,15 @@ int smb3_encrypt_resp(struct ksmbd_work *work) if (ARRAY_SIZE(iov) < rq_nvec) return -ENOMEM; - tr_hdr = kzalloc(sizeof(struct smb2_transform_hdr), GFP_KERNEL); - if (!tr_hdr) + work->tr_buf = kzalloc(sizeof(struct smb2_transform_hdr) + 4, GFP_KERNEL); + if (!work->tr_buf) return rc; /* fill transform header */ - fill_transform_hdr(tr_hdr, buf, work->conn->cipher_type); + fill_transform_hdr(work->tr_buf, buf, work->conn->cipher_type); - iov[0].iov_base = tr_hdr; - iov[0].iov_len = sizeof(struct smb2_transform_hdr); + iov[0].iov_base = work->tr_buf; + iov[0].iov_len = sizeof(struct smb2_transform_hdr) + 4; buf_size += iov[0].iov_len - 4; iov[1].iov_base = buf + 4; @@ -8289,15 +8480,14 @@ int smb3_encrypt_resp(struct ksmbd_work *work) return rc; memmove(buf, iov[1].iov_base, iov[1].iov_len); - tr_hdr->smb2_buf_length = cpu_to_be32(buf_size); - work->tr_buf = tr_hdr; + *(__be32 *)work->tr_buf = cpu_to_be32(buf_size); return rc; } bool smb3_is_transform_hdr(void *buf) { - struct smb2_transform_hdr *trhdr = buf; + struct smb2_transform_hdr *trhdr = smb2_get_msg(buf); return trhdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM; } @@ -8307,45 +8497,40 @@ int smb3_decrypt_req(struct ksmbd_work *work) struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess; char *buf = work->request_buf; - struct smb2_hdr *hdr; unsigned int pdu_length = get_rfc1002_len(buf); struct kvec iov[2]; - unsigned int buf_data_size = pdu_length + 4 - - sizeof(struct smb2_transform_hdr); - struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf; - unsigned int orig_len = le32_to_cpu(tr_hdr->OriginalMessageSize); + int buf_data_size = pdu_length - sizeof(struct smb2_transform_hdr); + struct smb2_transform_hdr *tr_hdr = smb2_get_msg(buf); int rc = 0; - sess = ksmbd_session_lookup_all(conn, le64_to_cpu(tr_hdr->SessionId)); - if (!sess) { - pr_err("invalid session id(%llx) in transform header\n", - le64_to_cpu(tr_hdr->SessionId)); - return -ECONNABORTED; - } - - if (pdu_length + 4 < - sizeof(struct smb2_transform_hdr) + sizeof(struct smb2_hdr)) { + if (buf_data_size < sizeof(struct smb2_hdr)) { pr_err("Transform message is too small (%u)\n", pdu_length); return -ECONNABORTED; } - if (pdu_length + 4 < orig_len + sizeof(struct smb2_transform_hdr)) { + if (buf_data_size < le32_to_cpu(tr_hdr->OriginalMessageSize)) { pr_err("Transform message is broken\n"); return -ECONNABORTED; } + sess = ksmbd_session_lookup_all(conn, le64_to_cpu(tr_hdr->SessionId)); + if (!sess) { + pr_err("invalid session id(%llx) in transform header\n", + le64_to_cpu(tr_hdr->SessionId)); + return -ECONNABORTED; + } + iov[0].iov_base = buf; - iov[0].iov_len = sizeof(struct smb2_transform_hdr); - iov[1].iov_base = buf + sizeof(struct smb2_transform_hdr); + iov[0].iov_len = sizeof(struct smb2_transform_hdr) + 4; + iov[1].iov_base = buf + sizeof(struct smb2_transform_hdr) + 4; iov[1].iov_len = buf_data_size; rc = ksmbd_crypt_message(conn, iov, 2, 0); if (rc) return rc; memmove(buf + 4, iov[1].iov_base, buf_data_size); - hdr = (struct smb2_hdr *)buf; - hdr->smb2_buf_length = cpu_to_be32(buf_data_size); + *(__be32 *)buf = cpu_to_be32(buf_data_size); return rc; } @@ -8353,7 +8538,7 @@ int smb3_decrypt_req(struct ksmbd_work *work) bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_hdr *rsp = work->response_buf; + struct smb2_hdr *rsp = smb2_get_msg(work->response_buf); if (conn->dialect < SMB30_PROT_ID) return false; diff --git a/fs/ksmbd/smb2pdu.h b/fs/ksmbd/smb2pdu.h index bcec845b03f3..4a3e4339d4c4 100644 --- a/fs/ksmbd/smb2pdu.h +++ b/fs/ksmbd/smb2pdu.h @@ -10,60 +10,6 @@ #include "ntlmssp.h" #include "smbacl.h" -/* - * Note that, due to trying to use names similar to the protocol specifications, - * there are many mixed case field names in the structures below. Although - * this does not match typical Linux kernel style, it is necessary to be - * able to match against the protocol specfication. - * - * SMB2 commands - * Some commands have minimal (wct=0,bcc=0), or uninteresting, responses - * (ie no useful data other than the SMB error code itself) and are marked such. - * Knowing this helps avoid response buffer allocations and copy in some cases. - */ - -/* List of commands in host endian */ -#define SMB2_NEGOTIATE_HE 0x0000 -#define SMB2_SESSION_SETUP_HE 0x0001 -#define SMB2_LOGOFF_HE 0x0002 /* trivial request/resp */ -#define SMB2_TREE_CONNECT_HE 0x0003 -#define SMB2_TREE_DISCONNECT_HE 0x0004 /* trivial req/resp */ -#define SMB2_CREATE_HE 0x0005 -#define SMB2_CLOSE_HE 0x0006 -#define SMB2_FLUSH_HE 0x0007 /* trivial resp */ -#define SMB2_READ_HE 0x0008 -#define SMB2_WRITE_HE 0x0009 -#define SMB2_LOCK_HE 0x000A -#define SMB2_IOCTL_HE 0x000B -#define SMB2_CANCEL_HE 0x000C -#define SMB2_ECHO_HE 0x000D -#define SMB2_QUERY_DIRECTORY_HE 0x000E -#define SMB2_CHANGE_NOTIFY_HE 0x000F -#define SMB2_QUERY_INFO_HE 0x0010 -#define SMB2_SET_INFO_HE 0x0011 -#define SMB2_OPLOCK_BREAK_HE 0x0012 - -/* The same list in little endian */ -#define SMB2_NEGOTIATE cpu_to_le16(SMB2_NEGOTIATE_HE) -#define SMB2_SESSION_SETUP cpu_to_le16(SMB2_SESSION_SETUP_HE) -#define SMB2_LOGOFF cpu_to_le16(SMB2_LOGOFF_HE) -#define SMB2_TREE_CONNECT cpu_to_le16(SMB2_TREE_CONNECT_HE) -#define SMB2_TREE_DISCONNECT cpu_to_le16(SMB2_TREE_DISCONNECT_HE) -#define SMB2_CREATE cpu_to_le16(SMB2_CREATE_HE) -#define SMB2_CLOSE cpu_to_le16(SMB2_CLOSE_HE) -#define SMB2_FLUSH cpu_to_le16(SMB2_FLUSH_HE) -#define SMB2_READ cpu_to_le16(SMB2_READ_HE) -#define SMB2_WRITE cpu_to_le16(SMB2_WRITE_HE) -#define SMB2_LOCK cpu_to_le16(SMB2_LOCK_HE) -#define SMB2_IOCTL cpu_to_le16(SMB2_IOCTL_HE) -#define SMB2_CANCEL cpu_to_le16(SMB2_CANCEL_HE) -#define SMB2_ECHO cpu_to_le16(SMB2_ECHO_HE) -#define SMB2_QUERY_DIRECTORY cpu_to_le16(SMB2_QUERY_DIRECTORY_HE) -#define SMB2_CHANGE_NOTIFY cpu_to_le16(SMB2_CHANGE_NOTIFY_HE) -#define SMB2_QUERY_INFO cpu_to_le16(SMB2_QUERY_INFO_HE) -#define SMB2_SET_INFO cpu_to_le16(SMB2_SET_INFO_HE) -#define SMB2_OPLOCK_BREAK cpu_to_le16(SMB2_OPLOCK_BREAK_HE) - /*Create Action Flags*/ #define FILE_SUPERSEDED 0x00000000 #define FILE_OPENED 0x00000001 @@ -96,9 +42,6 @@ /* SMB2 Max Credits */ #define SMB2_MAX_CREDITS 8192 -#define SMB2_CLIENT_GUID_SIZE 16 -#define SMB2_CREATE_GUID_SIZE 16 - /* Maximum buffer size value we can send with 1 credit */ #define SMB2_MAX_BUFFER_SIZE 65536 @@ -107,84 +50,11 @@ /* BB FIXME - analyze following length BB */ #define MAX_SMB2_HDR_SIZE 0x78 /* 4 len + 64 hdr + (2*24 wct) + 2 bct + 2 pad */ -#define SMB2_PROTO_NUMBER cpu_to_le32(0x424d53fe) /* 'B''M''S' */ -#define SMB2_TRANSFORM_PROTO_NUM cpu_to_le32(0x424d53fd) - #define SMB21_DEFAULT_IOSIZE (1024 * 1024) #define SMB3_DEFAULT_IOSIZE (4 * 1024 * 1024) #define SMB3_DEFAULT_TRANS_SIZE (1024 * 1024) - -/* - * SMB2 Header Definition - * - * "MBZ" : Must be Zero - * "BB" : BugBug, Something to check/review/analyze later - * "PDU" : "Protocol Data Unit" (ie a network "frame") - * - */ - -#define __SMB2_HEADER_STRUCTURE_SIZE 64 -#define SMB2_HEADER_STRUCTURE_SIZE \ - cpu_to_le16(__SMB2_HEADER_STRUCTURE_SIZE) - -struct smb2_hdr { - __be32 smb2_buf_length; /* big endian on wire */ - /* - * length is only two or three bytes - with - * one or two byte type preceding it that MBZ - */ - __le32 ProtocolId; /* 0xFE 'S' 'M' 'B' */ - __le16 StructureSize; /* 64 */ - __le16 CreditCharge; /* MBZ */ - __le32 Status; /* Error from server */ - __le16 Command; - __le16 CreditRequest; /* CreditResponse */ - __le32 Flags; - __le32 NextCommand; - __le64 MessageId; - union { - struct { - __le32 ProcessId; - __le32 TreeId; - } __packed SyncId; - __le64 AsyncId; - } __packed Id; - __le64 SessionId; - __u8 Signature[16]; -} __packed; - -struct smb2_pdu { - struct smb2_hdr hdr; - __le16 StructureSize2; /* size of wct area (varies, request specific) */ -} __packed; - -#define SMB3_AES_CCM_NONCE 11 -#define SMB3_AES_GCM_NONCE 12 - -struct smb2_transform_hdr { - __be32 smb2_buf_length; /* big endian on wire */ - /* - * length is only two or three bytes - with - * one or two byte type preceding it that MBZ - */ - __le32 ProtocolId; /* 0xFD 'S' 'M' 'B' */ - __u8 Signature[16]; - __u8 Nonce[16]; - __le32 OriginalMessageSize; - __u16 Reserved1; - __le16 Flags; /* EncryptionAlgorithm */ - __le64 SessionId; -} __packed; - -/* - * SMB2 flag definitions - */ -#define SMB2_FLAGS_SERVER_TO_REDIR cpu_to_le32(0x00000001) -#define SMB2_FLAGS_ASYNC_COMMAND cpu_to_le32(0x00000002) -#define SMB2_FLAGS_RELATED_OPERATIONS cpu_to_le32(0x00000004) -#define SMB2_FLAGS_SIGNED cpu_to_le32(0x00000008) -#define SMB2_FLAGS_DFS_OPERATIONS cpu_to_le32(0x10000000) -#define SMB2_FLAGS_REPLAY_OPERATIONS cpu_to_le32(0x20000000) +#define SMB3_MIN_IOSIZE (64 * 1024) +#define SMB3_MAX_IOSIZE (8 * 1024 * 1024) /* * Definitions for SMB2 Protocol Data Units (network frames) @@ -207,425 +77,30 @@ struct smb2_err_rsp { __u8 ErrorData[1]; /* variable length */ } __packed; -struct smb2_negotiate_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 36 */ - __le16 DialectCount; - __le16 SecurityMode; - __le16 Reserved; /* MBZ */ - __le32 Capabilities; - __u8 ClientGUID[SMB2_CLIENT_GUID_SIZE]; - /* In SMB3.02 and earlier next three were MBZ le64 ClientStartTime */ - __le32 NegotiateContextOffset; /* SMB3.1.1 only. MBZ earlier */ - __le16 NegotiateContextCount; /* SMB3.1.1 only. MBZ earlier */ - __le16 Reserved2; - __le16 Dialects[1]; /* One dialect (vers=) at a time for now */ -} __packed; - -/* SecurityMode flags */ -#define SMB2_NEGOTIATE_SIGNING_ENABLED_LE cpu_to_le16(0x0001) -#define SMB2_NEGOTIATE_SIGNING_REQUIRED 0x0002 -#define SMB2_NEGOTIATE_SIGNING_REQUIRED_LE cpu_to_le16(0x0002) -/* Capabilities flags */ -#define SMB2_GLOBAL_CAP_DFS 0x00000001 -#define SMB2_GLOBAL_CAP_LEASING 0x00000002 /* Resp only New to SMB2.1 */ -#define SMB2_GLOBAL_CAP_LARGE_MTU 0X00000004 /* Resp only New to SMB2.1 */ -#define SMB2_GLOBAL_CAP_MULTI_CHANNEL 0x00000008 /* New to SMB3 */ -#define SMB2_GLOBAL_CAP_PERSISTENT_HANDLES 0x00000010 /* New to SMB3 */ -#define SMB2_GLOBAL_CAP_DIRECTORY_LEASING 0x00000020 /* New to SMB3 */ -#define SMB2_GLOBAL_CAP_ENCRYPTION 0x00000040 /* New to SMB3 */ -/* Internal types */ -#define SMB2_NT_FIND 0x00100000 -#define SMB2_LARGE_FILES 0x00200000 - -#define SMB311_SALT_SIZE 32 -/* Hash Algorithm Types */ -#define SMB2_PREAUTH_INTEGRITY_SHA512 cpu_to_le16(0x0001) - -#define PREAUTH_HASHVALUE_SIZE 64 - struct preauth_integrity_info { /* PreAuth integrity Hash ID */ __le16 Preauth_HashId; /* PreAuth integrity Hash Value */ - __u8 Preauth_HashValue[PREAUTH_HASHVALUE_SIZE]; + __u8 Preauth_HashValue[SMB2_PREAUTH_HASH_SIZE]; }; -/* offset is sizeof smb2_negotiate_rsp - 4 but rounded up to 8 bytes. */ +/* offset is sizeof smb2_negotiate_rsp but rounded up to 8 bytes. */ #ifdef CONFIG_SMB_SERVER_KERBEROS5 -/* sizeof(struct smb2_negotiate_rsp) - 4 = +/* sizeof(struct smb2_negotiate_rsp) = * header(64) + response(64) + GSS_LENGTH(96) + GSS_PADDING(0) */ #define OFFSET_OF_NEG_CONTEXT 0xe0 #else -/* sizeof(struct smb2_negotiate_rsp) - 4 = +/* sizeof(struct smb2_negotiate_rsp) = * header(64) + response(64) + GSS_LENGTH(74) + GSS_PADDING(6) */ #define OFFSET_OF_NEG_CONTEXT 0xd0 #endif -#define SMB2_PREAUTH_INTEGRITY_CAPABILITIES cpu_to_le16(1) -#define SMB2_ENCRYPTION_CAPABILITIES cpu_to_le16(2) -#define SMB2_COMPRESSION_CAPABILITIES cpu_to_le16(3) -#define SMB2_NETNAME_NEGOTIATE_CONTEXT_ID cpu_to_le16(5) -#define SMB2_SIGNING_CAPABILITIES cpu_to_le16(8) -#define SMB2_POSIX_EXTENSIONS_AVAILABLE cpu_to_le16(0x100) - -struct smb2_neg_context { - __le16 ContextType; - __le16 DataLength; - __le32 Reserved; - /* Followed by array of data */ -} __packed; - -struct smb2_preauth_neg_context { - __le16 ContextType; /* 1 */ - __le16 DataLength; - __le32 Reserved; - __le16 HashAlgorithmCount; /* 1 */ - __le16 SaltLength; - __le16 HashAlgorithms; /* HashAlgorithms[0] since only one defined */ - __u8 Salt[SMB311_SALT_SIZE]; -} __packed; - -/* Encryption Algorithms Ciphers */ -#define SMB2_ENCRYPTION_AES128_CCM cpu_to_le16(0x0001) -#define SMB2_ENCRYPTION_AES128_GCM cpu_to_le16(0x0002) -#define SMB2_ENCRYPTION_AES256_CCM cpu_to_le16(0x0003) -#define SMB2_ENCRYPTION_AES256_GCM cpu_to_le16(0x0004) - -struct smb2_encryption_neg_context { - __le16 ContextType; /* 2 */ - __le16 DataLength; - __le32 Reserved; - /* CipherCount usally 2, but can be 3 when AES256-GCM enabled */ - __le16 CipherCount; /* AES-128-GCM and AES-128-CCM by default */ - __le16 Ciphers[]; -} __packed; - -#define SMB3_COMPRESS_NONE cpu_to_le16(0x0000) -#define SMB3_COMPRESS_LZNT1 cpu_to_le16(0x0001) -#define SMB3_COMPRESS_LZ77 cpu_to_le16(0x0002) -#define SMB3_COMPRESS_LZ77_HUFF cpu_to_le16(0x0003) - -struct smb2_compression_ctx { - __le16 ContextType; /* 3 */ - __le16 DataLength; - __le32 Reserved; - __le16 CompressionAlgorithmCount; - __u16 Padding; - __le32 Reserved1; - __le16 CompressionAlgorithms[]; -} __packed; - -#define POSIX_CTXT_DATA_LEN 16 -struct smb2_posix_neg_context { - __le16 ContextType; /* 0x100 */ - __le16 DataLength; - __le32 Reserved; - __u8 Name[16]; /* POSIX ctxt GUID 93AD25509CB411E7B42383DE968BCD7C */ -} __packed; - -struct smb2_netname_neg_context { - __le16 ContextType; /* 0x100 */ - __le16 DataLength; - __le32 Reserved; - __le16 NetName[]; /* hostname of target converted to UCS-2 */ -} __packed; - -/* Signing algorithms */ -#define SIGNING_ALG_HMAC_SHA256 cpu_to_le16(0) -#define SIGNING_ALG_AES_CMAC cpu_to_le16(1) -#define SIGNING_ALG_AES_GMAC cpu_to_le16(2) - -struct smb2_signing_capabilities { - __le16 ContextType; /* 8 */ - __le16 DataLength; - __le32 Reserved; - __le16 SigningAlgorithmCount; - __le16 SigningAlgorithms[]; -} __packed; - -struct smb2_negotiate_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 65 */ - __le16 SecurityMode; - __le16 DialectRevision; - __le16 NegotiateContextCount; /* Prior to SMB3.1.1 was Reserved & MBZ */ - __u8 ServerGUID[16]; - __le32 Capabilities; - __le32 MaxTransactSize; - __le32 MaxReadSize; - __le32 MaxWriteSize; - __le64 SystemTime; /* MBZ */ - __le64 ServerStartTime; - __le16 SecurityBufferOffset; - __le16 SecurityBufferLength; - __le32 NegotiateContextOffset; /* Pre:SMB3.1.1 was reserved/ignored */ - __u8 Buffer[1]; /* variable length GSS security buffer */ -} __packed; - -/* Flags */ -#define SMB2_SESSION_REQ_FLAG_BINDING 0x01 -#define SMB2_SESSION_REQ_FLAG_ENCRYPT_DATA 0x04 - #define SMB2_SESSION_EXPIRED (0) #define SMB2_SESSION_IN_PROGRESS BIT(0) #define SMB2_SESSION_VALID BIT(1) -/* Flags */ -#define SMB2_SESSION_REQ_FLAG_BINDING 0x01 -#define SMB2_SESSION_REQ_FLAG_ENCRYPT_DATA 0x04 - -struct smb2_sess_setup_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 25 */ - __u8 Flags; - __u8 SecurityMode; - __le32 Capabilities; - __le32 Channel; - __le16 SecurityBufferOffset; - __le16 SecurityBufferLength; - __le64 PreviousSessionId; - __u8 Buffer[1]; /* variable length GSS security buffer */ -} __packed; - -/* Flags/Reserved for SMB3.1.1 */ -#define SMB2_SHAREFLAG_CLUSTER_RECONNECT 0x0001 - -/* Currently defined SessionFlags */ -#define SMB2_SESSION_FLAG_IS_GUEST_LE cpu_to_le16(0x0001) -#define SMB2_SESSION_FLAG_IS_NULL_LE cpu_to_le16(0x0002) -#define SMB2_SESSION_FLAG_ENCRYPT_DATA_LE cpu_to_le16(0x0004) -struct smb2_sess_setup_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 9 */ - __le16 SessionFlags; - __le16 SecurityBufferOffset; - __le16 SecurityBufferLength; - __u8 Buffer[1]; /* variable length GSS security buffer */ -} __packed; - -struct smb2_logoff_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 4 */ - __le16 Reserved; -} __packed; - -struct smb2_logoff_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 4 */ - __le16 Reserved; -} __packed; - -struct smb2_tree_connect_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 9 */ - __le16 Reserved; /* Flags in SMB3.1.1 */ - __le16 PathOffset; - __le16 PathLength; - __u8 Buffer[1]; /* variable length */ -} __packed; - -struct smb2_tree_connect_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 16 */ - __u8 ShareType; /* see below */ - __u8 Reserved; - __le32 ShareFlags; /* see below */ - __le32 Capabilities; /* see below */ - __le32 MaximalAccess; -} __packed; - -/* Possible ShareType values */ -#define SMB2_SHARE_TYPE_DISK 0x01 -#define SMB2_SHARE_TYPE_PIPE 0x02 -#define SMB2_SHARE_TYPE_PRINT 0x03 - -/* - * Possible ShareFlags - exactly one and only one of the first 4 caching flags - * must be set (any of the remaining, SHI1005, flags may be set individually - * or in combination. - */ -#define SMB2_SHAREFLAG_MANUAL_CACHING 0x00000000 -#define SMB2_SHAREFLAG_AUTO_CACHING 0x00000010 -#define SMB2_SHAREFLAG_VDO_CACHING 0x00000020 -#define SMB2_SHAREFLAG_NO_CACHING 0x00000030 -#define SHI1005_FLAGS_DFS 0x00000001 -#define SHI1005_FLAGS_DFS_ROOT 0x00000002 -#define SHI1005_FLAGS_RESTRICT_EXCLUSIVE_OPENS 0x00000100 -#define SHI1005_FLAGS_FORCE_SHARED_DELETE 0x00000200 -#define SHI1005_FLAGS_ALLOW_NAMESPACE_CACHING 0x00000400 -#define SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM 0x00000800 -#define SHI1005_FLAGS_FORCE_LEVELII_OPLOCK 0x00001000 -#define SHI1005_FLAGS_ENABLE_HASH 0x00002000 - -/* Possible share capabilities */ -#define SMB2_SHARE_CAP_DFS cpu_to_le32(0x00000008) - -struct smb2_tree_disconnect_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 4 */ - __le16 Reserved; -} __packed; - -struct smb2_tree_disconnect_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 4 */ - __le16 Reserved; -} __packed; - -#define ATTR_READONLY_LE cpu_to_le32(ATTR_READONLY) -#define ATTR_HIDDEN_LE cpu_to_le32(ATTR_HIDDEN) -#define ATTR_SYSTEM_LE cpu_to_le32(ATTR_SYSTEM) -#define ATTR_DIRECTORY_LE cpu_to_le32(ATTR_DIRECTORY) -#define ATTR_ARCHIVE_LE cpu_to_le32(ATTR_ARCHIVE) -#define ATTR_NORMAL_LE cpu_to_le32(ATTR_NORMAL) -#define ATTR_TEMPORARY_LE cpu_to_le32(ATTR_TEMPORARY) -#define ATTR_SPARSE_FILE_LE cpu_to_le32(ATTR_SPARSE) -#define ATTR_REPARSE_POINT_LE cpu_to_le32(ATTR_REPARSE) -#define ATTR_COMPRESSED_LE cpu_to_le32(ATTR_COMPRESSED) -#define ATTR_OFFLINE_LE cpu_to_le32(ATTR_OFFLINE) -#define ATTR_NOT_CONTENT_INDEXED_LE cpu_to_le32(ATTR_NOT_CONTENT_INDEXED) -#define ATTR_ENCRYPTED_LE cpu_to_le32(ATTR_ENCRYPTED) -#define ATTR_INTEGRITY_STREAML_LE cpu_to_le32(0x00008000) -#define ATTR_NO_SCRUB_DATA_LE cpu_to_le32(0x00020000) -#define ATTR_MASK_LE cpu_to_le32(0x00007FB7) - -/* Oplock levels */ -#define SMB2_OPLOCK_LEVEL_NONE 0x00 -#define SMB2_OPLOCK_LEVEL_II 0x01 -#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 -#define SMB2_OPLOCK_LEVEL_BATCH 0x09 -#define SMB2_OPLOCK_LEVEL_LEASE 0xFF -/* Non-spec internal type */ -#define SMB2_OPLOCK_LEVEL_NOCHANGE 0x99 - -/* Desired Access Flags */ -#define FILE_READ_DATA_LE cpu_to_le32(0x00000001) -#define FILE_LIST_DIRECTORY_LE cpu_to_le32(0x00000001) -#define FILE_WRITE_DATA_LE cpu_to_le32(0x00000002) -#define FILE_ADD_FILE_LE cpu_to_le32(0x00000002) -#define FILE_APPEND_DATA_LE cpu_to_le32(0x00000004) -#define FILE_ADD_SUBDIRECTORY_LE cpu_to_le32(0x00000004) -#define FILE_READ_EA_LE cpu_to_le32(0x00000008) -#define FILE_WRITE_EA_LE cpu_to_le32(0x00000010) -#define FILE_EXECUTE_LE cpu_to_le32(0x00000020) -#define FILE_TRAVERSE_LE cpu_to_le32(0x00000020) -#define FILE_DELETE_CHILD_LE cpu_to_le32(0x00000040) -#define FILE_READ_ATTRIBUTES_LE cpu_to_le32(0x00000080) -#define FILE_WRITE_ATTRIBUTES_LE cpu_to_le32(0x00000100) -#define FILE_DELETE_LE cpu_to_le32(0x00010000) -#define FILE_READ_CONTROL_LE cpu_to_le32(0x00020000) -#define FILE_WRITE_DAC_LE cpu_to_le32(0x00040000) -#define FILE_WRITE_OWNER_LE cpu_to_le32(0x00080000) -#define FILE_SYNCHRONIZE_LE cpu_to_le32(0x00100000) -#define FILE_ACCESS_SYSTEM_SECURITY_LE cpu_to_le32(0x01000000) -#define FILE_MAXIMAL_ACCESS_LE cpu_to_le32(0x02000000) -#define FILE_GENERIC_ALL_LE cpu_to_le32(0x10000000) -#define FILE_GENERIC_EXECUTE_LE cpu_to_le32(0x20000000) -#define FILE_GENERIC_WRITE_LE cpu_to_le32(0x40000000) -#define FILE_GENERIC_READ_LE cpu_to_le32(0x80000000) -#define DESIRED_ACCESS_MASK cpu_to_le32(0xF21F01FF) - -/* ShareAccess Flags */ -#define FILE_SHARE_READ_LE cpu_to_le32(0x00000001) -#define FILE_SHARE_WRITE_LE cpu_to_le32(0x00000002) -#define FILE_SHARE_DELETE_LE cpu_to_le32(0x00000004) -#define FILE_SHARE_ALL_LE cpu_to_le32(0x00000007) - -/* CreateDisposition Flags */ -#define FILE_SUPERSEDE_LE cpu_to_le32(0x00000000) -#define FILE_OPEN_LE cpu_to_le32(0x00000001) -#define FILE_CREATE_LE cpu_to_le32(0x00000002) -#define FILE_OPEN_IF_LE cpu_to_le32(0x00000003) -#define FILE_OVERWRITE_LE cpu_to_le32(0x00000004) -#define FILE_OVERWRITE_IF_LE cpu_to_le32(0x00000005) -#define FILE_CREATE_MASK_LE cpu_to_le32(0x00000007) - -#define FILE_READ_DESIRED_ACCESS_LE (FILE_READ_DATA_LE | \ - FILE_READ_EA_LE | \ - FILE_GENERIC_READ_LE) -#define FILE_WRITE_DESIRE_ACCESS_LE (FILE_WRITE_DATA_LE | \ - FILE_APPEND_DATA_LE | \ - FILE_WRITE_EA_LE | \ - FILE_WRITE_ATTRIBUTES_LE | \ - FILE_GENERIC_WRITE_LE) - -/* Impersonation Levels */ -#define IL_ANONYMOUS_LE cpu_to_le32(0x00000000) -#define IL_IDENTIFICATION_LE cpu_to_le32(0x00000001) -#define IL_IMPERSONATION_LE cpu_to_le32(0x00000002) -#define IL_DELEGATE_LE cpu_to_le32(0x00000003) - -/* Create Context Values */ -#define SMB2_CREATE_EA_BUFFER "ExtA" /* extended attributes */ -#define SMB2_CREATE_SD_BUFFER "SecD" /* security descriptor */ -#define SMB2_CREATE_DURABLE_HANDLE_REQUEST "DHnQ" -#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT "DHnC" -#define SMB2_CREATE_ALLOCATION_SIZE "AlSi" -#define SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST "MxAc" -#define SMB2_CREATE_TIMEWARP_REQUEST "TWrp" -#define SMB2_CREATE_QUERY_ON_DISK_ID "QFid" -#define SMB2_CREATE_REQUEST_LEASE "RqLs" -#define SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2 "DH2Q" -#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 "DH2C" -#define SMB2_CREATE_APP_INSTANCE_ID "\x45\xBC\xA6\x6A\xEF\xA7\xF7\x4A\x90\x08\xFA\x46\x2E\x14\x4D\x74" - #define SMB2_CREATE_APP_INSTANCE_VERSION "\xB9\x82\xD0\xB7\x3B\x56\x07\x4F\xA0\x7B\x52\x4A\x81\x16\xA0\x10" -#define SVHDX_OPEN_DEVICE_CONTEXT 0x83CE6F1AD851E0986E34401CC9BCFCE9 -#define SMB2_CREATE_TAG_POSIX "\x93\xAD\x25\x50\x9C\xB4\x11\xE7\xB4\x23\x83\xDE\x96\x8B\xCD\x7C" - -struct smb2_create_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 57 */ - __u8 SecurityFlags; - __u8 RequestedOplockLevel; - __le32 ImpersonationLevel; - __le64 SmbCreateFlags; - __le64 Reserved; - __le32 DesiredAccess; - __le32 FileAttributes; - __le32 ShareAccess; - __le32 CreateDisposition; - __le32 CreateOptions; - __le16 NameOffset; - __le16 NameLength; - __le32 CreateContextsOffset; - __le32 CreateContextsLength; - __u8 Buffer[0]; -} __packed; - -struct smb2_create_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 89 */ - __u8 OplockLevel; - __u8 Reserved; - __le32 CreateAction; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 AllocationSize; - __le64 EndofFile; - __le32 FileAttributes; - __le32 Reserved2; - __le64 PersistentFileId; - __le64 VolatileFileId; - __le32 CreateContextsOffset; - __le32 CreateContextsLength; - __u8 Buffer[1]; -} __packed; - -struct create_context { - __le32 Next; - __le16 NameOffset; - __le16 NameLength; - __le16 Reserved; - __le16 DataOffset; - __le32 DataLength; - __u8 Buffer[0]; -} __packed; - struct create_durable_req_v2 { struct create_context ccontext; __u8 Name[8]; @@ -741,22 +216,21 @@ struct create_posix_rsp { #define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE cpu_to_le32(0x02) +#define SMB2_LEASE_KEY_SIZE 16 + struct lease_context { - __le64 LeaseKeyLow; - __le64 LeaseKeyHigh; + __u8 LeaseKey[SMB2_LEASE_KEY_SIZE]; __le32 LeaseState; __le32 LeaseFlags; __le64 LeaseDuration; } __packed; struct lease_context_v2 { - __le64 LeaseKeyLow; - __le64 LeaseKeyHigh; + __u8 LeaseKey[SMB2_LEASE_KEY_SIZE]; __le32 LeaseState; __le32 LeaseFlags; __le64 LeaseDuration; - __le64 ParentLeaseKeyLow; - __le64 ParentLeaseKeyHigh; + __u8 ParentLeaseKey[SMB2_LEASE_KEY_SIZE]; __le16 Epoch; __le16 Reserved; } __packed; @@ -774,114 +248,12 @@ struct create_lease_v2 { __u8 Pad[4]; } __packed; -/* Currently defined values for close flags */ -#define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB cpu_to_le16(0x0001) -struct smb2_close_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 24 */ - __le16 Flags; - __le32 Reserved; - __le64 PersistentFileId; - __le64 VolatileFileId; -} __packed; - -struct smb2_close_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* 60 */ - __le16 Flags; - __le32 Reserved; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 AllocationSize; /* Beginning of FILE_STANDARD_INFO equivalent */ - __le64 EndOfFile; - __le32 Attributes; -} __packed; - -struct smb2_flush_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 24 */ - __le16 Reserved1; - __le32 Reserved2; - __le64 PersistentFileId; - __le64 VolatileFileId; -} __packed; - -struct smb2_flush_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; - __le16 Reserved; -} __packed; - struct smb2_buffer_desc_v1 { __le64 offset; __le32 token; __le32 length; } __packed; -#define SMB2_CHANNEL_NONE cpu_to_le32(0x00000000) -#define SMB2_CHANNEL_RDMA_V1 cpu_to_le32(0x00000001) -#define SMB2_CHANNEL_RDMA_V1_INVALIDATE cpu_to_le32(0x00000002) - -struct smb2_read_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 49 */ - __u8 Padding; /* offset from start of SMB2 header to place read */ - __u8 Reserved; - __le32 Length; - __le64 Offset; - __le64 PersistentFileId; - __le64 VolatileFileId; - __le32 MinimumCount; - __le32 Channel; /* Reserved MBZ */ - __le32 RemainingBytes; - __le16 ReadChannelInfoOffset; /* Reserved MBZ */ - __le16 ReadChannelInfoLength; /* Reserved MBZ */ - __u8 Buffer[1]; -} __packed; - -struct smb2_read_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 17 */ - __u8 DataOffset; - __u8 Reserved; - __le32 DataLength; - __le32 DataRemaining; - __u32 Reserved2; - __u8 Buffer[1]; -} __packed; - -/* For write request Flags field below the following flag is defined: */ -#define SMB2_WRITEFLAG_WRITE_THROUGH 0x00000001 - -struct smb2_write_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 49 */ - __le16 DataOffset; /* offset from start of SMB2 header to write data */ - __le32 Length; - __le64 Offset; - __le64 PersistentFileId; - __le64 VolatileFileId; - __le32 Channel; /* Reserved MBZ */ - __le32 RemainingBytes; - __le16 WriteChannelInfoOffset; /* Reserved MBZ */ - __le16 WriteChannelInfoLength; /* Reserved MBZ */ - __le32 Flags; - __u8 Buffer[1]; -} __packed; - -struct smb2_write_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 17 */ - __u8 DataOffset; - __u8 Reserved; - __le32 DataLength; - __le32 DataRemaining; - __u32 Reserved2; - __u8 Buffer[1]; -} __packed; - #define SMB2_0_IOCTL_IS_FSCTL 0x00000001 struct duplicate_extents_to_file { @@ -1031,43 +403,6 @@ struct reparse_data_buffer { __u8 DataBuffer[]; /* Variable Length */ } __packed; -/* Completion Filter flags for Notify */ -#define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001 -#define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002 -#define FILE_NOTIFY_CHANGE_NAME 0x00000003 -#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004 -#define FILE_NOTIFY_CHANGE_SIZE 0x00000008 -#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 -#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020 -#define FILE_NOTIFY_CHANGE_CREATION 0x00000040 -#define FILE_NOTIFY_CHANGE_EA 0x00000080 -#define FILE_NOTIFY_CHANGE_SECURITY 0x00000100 -#define FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200 -#define FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400 -#define FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800 - -/* Flags */ -#define SMB2_WATCH_TREE 0x0001 - -struct smb2_notify_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 32 */ - __le16 Flags; - __le32 OutputBufferLength; - __le64 PersistentFileId; - __le64 VolatileFileId; - __u32 CompletionFileter; - __u32 Reserved; -} __packed; - -struct smb2_notify_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 9 */ - __le16 OutputBufferOffset; - __le32 OutputBufferLength; - __u8 Buffer[1]; -} __packed; - /* SMB2 Notify Action Flags */ #define FILE_ACTION_ADDED 0x00000001 #define FILE_ACTION_REMOVED 0x00000002 @@ -1464,6 +799,15 @@ struct smb2_file_all_info { /* data block encoding of response to level 18 */ char FileName[1]; } __packed; /* level 18 Query */ +struct smb2_file_basic_info { /* data block encoding of response to level 18 */ + __le64 CreationTime; /* Beginning of FILE_BASIC_INFO equivalent */ + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 Attributes; + __u32 Pad1; /* End of FILE_BASIC_INFO_INFO equivalent */ +} __packed; + struct smb2_file_alt_name_info { __le32 FileNameLength; char FileName[0]; @@ -1517,7 +861,7 @@ struct smb2_file_pos_info { __le64 CurrentByteOffset; } __packed; -#define FILE_MODE_INFO_MASK cpu_to_le32(0x0000103e) +#define FILE_MODE_INFO_MASK cpu_to_le32(0x0000100e) struct smb2_file_mode_info { __le32 Mode; @@ -1628,7 +972,6 @@ struct smb2_posix_info { } __packed; /* functions */ -int init_smb2_0_server(struct ksmbd_conn *conn); void init_smb2_1_server(struct ksmbd_conn *conn); void init_smb3_0_server(struct ksmbd_conn *conn); void init_smb3_02_server(struct ksmbd_conn *conn); @@ -1695,4 +1038,13 @@ int smb2_ioctl(struct ksmbd_work *work); int smb2_oplock_break(struct ksmbd_work *work); int smb2_notify(struct ksmbd_work *ksmbd_work); +/* + * Get the body of the smb2 message excluding the 4 byte rfc1002 headers + * from request/response buffer. + */ +static inline void *smb2_get_msg(void *buf) +{ + return buf + 4; +} + #endif /* _SMB2PDU_H */ diff --git a/fs/ksmbd/smb_common.c b/fs/ksmbd/smb_common.c index 43d3123d8b62..ef7f42b0290a 100644 --- a/fs/ksmbd/smb_common.c +++ b/fs/ksmbd/smb_common.c @@ -21,7 +21,6 @@ static const char basechars[43] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%"; #define MAGIC_CHAR '~' #define PERIOD '.' #define mangle(V) ((char)(basechars[(V) % MANGLE_BASE])) -#define KSMBD_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb2_hdr)) struct smb_protocol { int index; @@ -89,7 +88,7 @@ unsigned int ksmbd_server_side_copy_max_total_size(void) inline int ksmbd_min_protocol(void) { - return SMB2_PROT; + return SMB21_PROT; } inline int ksmbd_max_protocol(void) @@ -129,16 +128,22 @@ int ksmbd_lookup_protocol_idx(char *str) * * check for valid smb signature and packet direction(request/response) * - * Return: 0 on success, otherwise 1 + * Return: 0 on success, otherwise -EINVAL */ int ksmbd_verify_smb_message(struct ksmbd_work *work) { - struct smb2_hdr *smb2_hdr = work->request_buf; + struct smb2_hdr *smb2_hdr = ksmbd_req_buf_next(work); + struct smb_hdr *hdr; if (smb2_hdr->ProtocolId == SMB2_PROTO_NUMBER) return ksmbd_smb2_check_message(work); - return 0; + hdr = work->request_buf; + if (*(__le32 *)hdr->Protocol == SMB1_PROTO_NUMBER && + hdr->Command == SMB_COM_NEGOTIATE) + return 0; + + return -EINVAL; } /** @@ -149,20 +154,7 @@ int ksmbd_verify_smb_message(struct ksmbd_work *work) */ bool ksmbd_smb_request(struct ksmbd_conn *conn) { - int type = *(char *)conn->request_buf; - - switch (type) { - case RFC1002_SESSION_MESSAGE: - /* Regular SMB request */ - return true; - case RFC1002_SESSION_KEEP_ALIVE: - ksmbd_debug(SMB, "RFC 1002 session keep alive\n"); - break; - default: - ksmbd_debug(SMB, "RFC 1002 unknown request type 0x%x\n", type); - } - - return false; + return conn->request_buf[0] == 0; } static bool supported_protocol(int idx) @@ -176,10 +168,12 @@ static bool supported_protocol(int idx) idx <= server_conf.max_protocol); } -static char *next_dialect(char *dialect, int *next_off) +static char *next_dialect(char *dialect, int *next_off, int bcount) { dialect = dialect + *next_off; - *next_off = strlen(dialect); + *next_off = strnlen(dialect, bcount); + if (dialect[*next_off] != '\0') + return NULL; return dialect; } @@ -194,7 +188,9 @@ static int ksmbd_lookup_dialect_by_name(char *cli_dialects, __le16 byte_count) dialect = cli_dialects; bcount = le16_to_cpu(byte_count); do { - dialect = next_dialect(dialect, &next); + dialect = next_dialect(dialect, &next, bcount); + if (!dialect) + break; ksmbd_debug(SMB, "client requested dialect %s\n", dialect); if (!strcmp(dialect, smb1_protos[i].name)) { @@ -242,13 +238,22 @@ int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count) static int ksmbd_negotiate_smb_dialect(void *buf) { - __le32 proto; + int smb_buf_length = get_rfc1002_len(buf); + __le32 proto = ((struct smb2_hdr *)smb2_get_msg(buf))->ProtocolId; - proto = ((struct smb2_hdr *)buf)->ProtocolId; if (proto == SMB2_PROTO_NUMBER) { struct smb2_negotiate_req *req; + int smb2_neg_size = + offsetof(struct smb2_negotiate_req, Dialects); + + req = (struct smb2_negotiate_req *)smb2_get_msg(buf); + if (smb2_neg_size > smb_buf_length) + goto err_out; + + if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > + smb_buf_length) + goto err_out; - req = (struct smb2_negotiate_req *)buf; return ksmbd_lookup_dialect_by_id(req->Dialects, req->DialectCount); } @@ -258,14 +263,22 @@ static int ksmbd_negotiate_smb_dialect(void *buf) struct smb_negotiate_req *req; req = (struct smb_negotiate_req *)buf; + if (le16_to_cpu(req->ByteCount) < 2) + goto err_out; + + if (offsetof(struct smb_negotiate_req, DialectsArray) - 4 + + le16_to_cpu(req->ByteCount) > smb_buf_length) { + goto err_out; + } + return ksmbd_lookup_dialect_by_name(req->DialectsArray, req->ByteCount); } +err_out: return BAD_PROT_ID; } -#define SMB_COM_NEGOTIATE 0x72 int ksmbd_init_smb_server(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; @@ -280,11 +293,6 @@ int ksmbd_init_smb_server(struct ksmbd_work *work) return 0; } -bool ksmbd_pdu_size_has_room(unsigned int pdu) -{ - return (pdu >= KSMBD_MIN_SUPPORTED_HEADER_SIZE - 4); -} - int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level, struct ksmbd_file *dir, struct ksmbd_dir_info *d_info, @@ -419,7 +427,7 @@ int ksmbd_extract_shortname(struct ksmbd_conn *conn, const char *longname, static int __smb2_negotiate(struct ksmbd_conn *conn) { - return (conn->dialect >= SMB20_PROT_ID && + return (conn->dialect >= SMB21_PROT_ID && conn->dialect <= SMB311_PROT_ID); } @@ -437,11 +445,12 @@ int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command) struct ksmbd_conn *conn = work->conn; int ret; - conn->dialect = ksmbd_negotiate_smb_dialect(work->request_buf); + conn->dialect = + ksmbd_negotiate_smb_dialect(work->request_buf); ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); if (command == SMB2_NEGOTIATE_HE) { - struct smb2_hdr *smb2_hdr = work->request_buf; + struct smb2_hdr *smb2_hdr = smb2_get_msg(work->request_buf); if (smb2_hdr->ProtocolId != SMB2_PROTO_NUMBER) { ksmbd_debug(SMB, "Downgrade to SMB1 negotiation\n"); @@ -449,7 +458,7 @@ int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command) } } - if (command == SMB2_NEGOTIATE_HE) { + if (command == SMB2_NEGOTIATE_HE && __smb2_negotiate(conn)) { ret = smb2_handle_negotiate(work); init_smb2_neg_rsp(work); return ret; diff --git a/fs/ksmbd/smb_common.h b/fs/ksmbd/smb_common.h index 57c667c1be06..50590842b651 100644 --- a/fs/ksmbd/smb_common.h +++ b/fs/ksmbd/smb_common.h @@ -10,6 +10,7 @@ #include "glob.h" #include "nterr.h" +#include "../smbfs_common/smb2pdu.h" #include "smb2pdu.h" /* ksmbd's Specific ERRNO */ @@ -32,29 +33,12 @@ #define SMB302_VERSION_STRING "3.02" #define SMB311_VERSION_STRING "3.1.1" -/* Dialects */ -#define SMB10_PROT_ID 0x00 -#define SMB20_PROT_ID 0x0202 -#define SMB21_PROT_ID 0x0210 -/* multi-protocol negotiate request */ -#define SMB2X_PROT_ID 0x02FF -#define SMB30_PROT_ID 0x0300 -#define SMB302_PROT_ID 0x0302 -#define SMB311_PROT_ID 0x0311 -#define BAD_PROT_ID 0xFFFF - #define SMB_ECHO_INTERVAL (60 * HZ) #define CIFS_DEFAULT_IOSIZE (64 * 1024) #define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */ -/* RFC 1002 session packet types */ -#define RFC1002_SESSION_MESSAGE 0x00 -#define RFC1002_SESSION_REQUEST 0x81 -#define RFC1002_POSITIVE_SESSION_RESPONSE 0x82 -#define RFC1002_NEGATIVE_SESSION_RESPONSE 0x83 -#define RFC1002_RETARGET_SESSION_RESPONSE 0x84 -#define RFC1002_SESSION_KEEP_ALIVE 0x85 +#define MAX_STREAM_PROT_LEN 0x00FFFFFF /* Responses when opening a file. */ #define F_SUPERSEDED 0 @@ -65,21 +49,6 @@ /* * File Attribute flags */ -#define ATTR_READONLY 0x0001 -#define ATTR_HIDDEN 0x0002 -#define ATTR_SYSTEM 0x0004 -#define ATTR_VOLUME 0x0008 -#define ATTR_DIRECTORY 0x0010 -#define ATTR_ARCHIVE 0x0020 -#define ATTR_DEVICE 0x0040 -#define ATTR_NORMAL 0x0080 -#define ATTR_TEMPORARY 0x0100 -#define ATTR_SPARSE 0x0200 -#define ATTR_REPARSE 0x0400 -#define ATTR_COMPRESSED 0x0800 -#define ATTR_OFFLINE 0x1000 -#define ATTR_NOT_CONTENT_INDEXED 0x2000 -#define ATTR_ENCRYPTED 0x4000 #define ATTR_POSIX_SEMANTICS 0x01000000 #define ATTR_BACKUP_SEMANTICS 0x02000000 #define ATTR_DELETE_ON_CLOSE 0x04000000 @@ -88,23 +57,6 @@ #define ATTR_NO_BUFFERING 0x20000000 #define ATTR_WRITE_THROUGH 0x80000000 -#define ATTR_READONLY_LE cpu_to_le32(ATTR_READONLY) -#define ATTR_HIDDEN_LE cpu_to_le32(ATTR_HIDDEN) -#define ATTR_SYSTEM_LE cpu_to_le32(ATTR_SYSTEM) -#define ATTR_DIRECTORY_LE cpu_to_le32(ATTR_DIRECTORY) -#define ATTR_ARCHIVE_LE cpu_to_le32(ATTR_ARCHIVE) -#define ATTR_NORMAL_LE cpu_to_le32(ATTR_NORMAL) -#define ATTR_TEMPORARY_LE cpu_to_le32(ATTR_TEMPORARY) -#define ATTR_SPARSE_FILE_LE cpu_to_le32(ATTR_SPARSE) -#define ATTR_REPARSE_POINT_LE cpu_to_le32(ATTR_REPARSE) -#define ATTR_COMPRESSED_LE cpu_to_le32(ATTR_COMPRESSED) -#define ATTR_OFFLINE_LE cpu_to_le32(ATTR_OFFLINE) -#define ATTR_NOT_CONTENT_INDEXED_LE cpu_to_le32(ATTR_NOT_CONTENT_INDEXED) -#define ATTR_ENCRYPTED_LE cpu_to_le32(ATTR_ENCRYPTED) -#define ATTR_INTEGRITY_STREAML_LE cpu_to_le32(0x00008000) -#define ATTR_NO_SCRUB_DATA_LE cpu_to_le32(0x00020000) -#define ATTR_MASK_LE cpu_to_le32(0x00007FB7) - /* List of FileSystemAttributes - see 2.5.1 of MS-FSCC */ #define FILE_SUPPORTS_SPARSE_VDL 0x10000000 /* faster nonsparse extend */ #define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000 /* allow ioctl dup extents */ @@ -166,11 +118,6 @@ /* file_execute, file_read_attributes*/ /* write_dac, and delete. */ -#define FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES) -#define FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ - | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) -#define FILE_EXEC_RIGHTS (FILE_EXECUTE) - #define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA \ | FILE_READ_ATTRIBUTES \ | DELETE | READ_CONTROL | WRITE_DAC \ @@ -210,6 +157,7 @@ FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES) #define SMB1_PROTO_NUMBER cpu_to_le32(0x424d53ff) +#define SMB_COM_NEGOTIATE 0x72 #define SMB1_CLIENT_GUID_SIZE (16) struct smb_hdr { @@ -482,12 +430,6 @@ struct smb_version_cmds { int (*proc)(struct ksmbd_work *swork); }; -static inline size_t -smb2_hdr_size_no_buflen(struct smb_version_values *vals) -{ - return vals->header_size - 4; -} - int ksmbd_min_protocol(void); int ksmbd_max_protocol(void); @@ -500,8 +442,6 @@ int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count); int ksmbd_init_smb_server(struct ksmbd_work *work); -bool ksmbd_pdu_size_has_room(unsigned int pdu); - struct ksmbd_kstat; int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level, diff --git a/fs/ksmbd/smbacl.c b/fs/ksmbd/smbacl.c index 0a95cdec8c80..bd792db32623 100644 --- a/fs/ksmbd/smbacl.c +++ b/fs/ksmbd/smbacl.c @@ -380,7 +380,7 @@ static void parse_dacl(struct user_namespace *user_ns, { int i, ret; int num_aces = 0; - int acl_size; + unsigned int acl_size; char *acl_base; struct smb_ace **ppace; struct posix_acl_entry *cf_pace, *cf_pdace; @@ -392,7 +392,7 @@ static void parse_dacl(struct user_namespace *user_ns, return; /* validate that we do not go past end of acl */ - if (end_of_acl <= (char *)pdacl || + if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl) || end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) { pr_err("ACL too small to parse DACL\n"); return; @@ -431,8 +431,22 @@ static void parse_dacl(struct user_namespace *user_ns, * user/group/other have no permissions */ for (i = 0; i < num_aces; ++i) { + if (end_of_acl - acl_base < acl_size) + break; + ppace[i] = (struct smb_ace *)(acl_base + acl_size); acl_base = (char *)ppace[i]; + acl_size = offsetof(struct smb_ace, sid) + + offsetof(struct smb_sid, sub_auth); + + if (end_of_acl - acl_base < acl_size || + ppace[i]->sid.num_subauth > SID_MAX_SUB_AUTHORITIES || + (end_of_acl - acl_base < + acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth) || + (le16_to_cpu(ppace[i]->size) < + acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth)) + break; + acl_size = le16_to_cpu(ppace[i]->size); ppace[i]->access_req = smb_map_generic_desired_access(ppace[i]->access_req); @@ -807,6 +821,9 @@ int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, if (!pntsd) return -EIO; + if (acl_len < sizeof(struct smb_ntsd)) + return -EINVAL; + owner_sid_ptr = (struct smb_sid *)((char *)pntsd + le32_to_cpu(pntsd->osidoffset)); group_sid_ptr = (struct smb_sid *)((char *)pntsd + diff --git a/fs/ksmbd/transport_ipc.c b/fs/ksmbd/transport_ipc.c index 44aea33a67fa..1acf1892a466 100644 --- a/fs/ksmbd/transport_ipc.c +++ b/fs/ksmbd/transport_ipc.c @@ -601,7 +601,7 @@ int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id, return ret; } -int ksmbd_ipc_logout_request(const char *account) +int ksmbd_ipc_logout_request(const char *account, int flags) { struct ksmbd_ipc_msg *msg; struct ksmbd_logout_request *req; @@ -616,6 +616,7 @@ int ksmbd_ipc_logout_request(const char *account) msg->type = KSMBD_EVENT_LOGOUT_REQUEST; req = (struct ksmbd_logout_request *)msg->payload; + req->account_flags = flags; strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); ret = ipc_msg_send(msg); diff --git a/fs/ksmbd/transport_ipc.h b/fs/ksmbd/transport_ipc.h index 9eacc895ffdb..5e5b90a0c187 100644 --- a/fs/ksmbd/transport_ipc.h +++ b/fs/ksmbd/transport_ipc.h @@ -25,7 +25,7 @@ ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, struct sockaddr *peer_addr); int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id, unsigned long long connect_id); -int ksmbd_ipc_logout_request(const char *account); +int ksmbd_ipc_logout_request(const char *account, int flags); struct ksmbd_share_config_response * ksmbd_ipc_share_config_request(const char *name); struct ksmbd_spnego_authen_response * diff --git a/fs/ksmbd/transport_rdma.c b/fs/ksmbd/transport_rdma.c index 52b2556e76b1..7e57cbb0bb35 100644 --- a/fs/ksmbd/transport_rdma.c +++ b/fs/ksmbd/transport_rdma.c @@ -20,7 +20,6 @@ #define SUBMOD_NAME "smb_direct" #include <linux/kthread.h> -#include <linux/rwlock.h> #include <linux/list.h> #include <linux/mempool.h> #include <linux/highmem.h> @@ -485,7 +484,7 @@ static int smb_direct_check_recvmsg(struct smb_direct_recvmsg *recvmsg) struct smb_direct_data_transfer *req = (struct smb_direct_data_transfer *)recvmsg->packet; struct smb2_hdr *hdr = (struct smb2_hdr *)(recvmsg->packet - + le32_to_cpu(req->data_offset) - 4); + + le32_to_cpu(req->data_offset)); ksmbd_debug(RDMA, "CreditGranted: %u, CreditRequested: %u, DataLength: %u, RemainingDataLength: %u, SMB: %x, Command: %u\n", le16_to_cpu(req->credits_granted), @@ -550,6 +549,10 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) switch (recvmsg->type) { case SMB_DIRECT_MSG_NEGOTIATE_REQ: + if (wc->byte_len < sizeof(struct smb_direct_negotiate_req)) { + put_empty_recvmsg(t, recvmsg); + return; + } t->negotiation_requested = true; t->full_packet_received = true; wake_up_interruptible(&t->wait_status); @@ -557,10 +560,23 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) case SMB_DIRECT_MSG_DATA_TRANSFER: { struct smb_direct_data_transfer *data_transfer = (struct smb_direct_data_transfer *)recvmsg->packet; - int data_length = le32_to_cpu(data_transfer->data_length); + unsigned int data_length; int avail_recvmsg_count, receive_credits; + if (wc->byte_len < + offsetof(struct smb_direct_data_transfer, padding)) { + put_empty_recvmsg(t, recvmsg); + return; + } + + data_length = le32_to_cpu(data_transfer->data_length); if (data_length) { + if (wc->byte_len < sizeof(struct smb_direct_data_transfer) + + (u64)data_length) { + put_empty_recvmsg(t, recvmsg); + return; + } + if (t->full_packet_received) recvmsg->first_segment = true; @@ -569,7 +585,7 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) else t->full_packet_received = true; - enqueue_reassembly(t, recvmsg, data_length); + enqueue_reassembly(t, recvmsg, (int)data_length); wake_up_interruptible(&t->wait_reassembly_queue); spin_lock(&t->receive_credit_lock); @@ -2027,7 +2043,6 @@ int ksmbd_rdma_destroy(void) smb_direct_listener.cm_id = NULL; if (smb_direct_wq) { - flush_workqueue(smb_direct_wq); destroy_workqueue(smb_direct_wq); smb_direct_wq = NULL; } diff --git a/fs/ksmbd/transport_tcp.c b/fs/ksmbd/transport_tcp.c index dc15a5ecd2e0..c14320e03b69 100644 --- a/fs/ksmbd/transport_tcp.c +++ b/fs/ksmbd/transport_tcp.c @@ -215,7 +215,7 @@ out_error: * ksmbd_kthread_fn() - listen to new SMB connections and callback server * @p: arguments to forker thread * - * Return: Returns a task_struct or ERR_PTR + * Return: 0 on success, error number otherwise */ static int ksmbd_kthread_fn(void *p) { @@ -387,7 +387,7 @@ static void tcp_destroy_socket(struct socket *ksmbd_socket) /** * create_socket - create socket for ksmbd/0 * - * Return: Returns a task_struct or ERR_PTR + * Return: 0 on success, error number otherwise */ static int create_socket(struct interface *iface) { diff --git a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c index b047f2980d96..19d36393974c 100644 --- a/fs/ksmbd/vfs.c +++ b/fs/ksmbd/vfs.c @@ -19,6 +19,8 @@ #include <linux/sched/xacct.h> #include <linux/crc32c.h> +#include "../internal.h" /* for vfs_path_lookup */ + #include "glob.h" #include "oplock.h" #include "connection.h" @@ -44,7 +46,6 @@ static char *extract_last_component(char *path) p++; } else { p = NULL; - pr_err("Invalid path %s\n", path); } return p; } @@ -155,7 +156,7 @@ int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, /** * ksmbd_vfs_create() - vfs helper for smb create file * @work: work - * @name: file name + * @name: file name that is relative to share * @mode: file create mode * * Return: 0 on success, otherwise error @@ -166,7 +167,8 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) struct dentry *dentry; int err; - dentry = kern_path_create(AT_FDCWD, name, &path, 0); + dentry = ksmbd_vfs_kern_path_create(work, name, + LOOKUP_NO_SYMLINKS, &path); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); if (err != -ENOENT) @@ -191,7 +193,7 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) /** * ksmbd_vfs_mkdir() - vfs helper for smb create directory * @work: work - * @name: directory name + * @name: directory name that is relative to share * @mode: directory create mode * * Return: 0 on success, otherwise error @@ -203,7 +205,9 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) struct dentry *dentry; int err; - dentry = kern_path_create(AT_FDCWD, name, &path, LOOKUP_DIRECTORY); + dentry = ksmbd_vfs_kern_path_create(work, name, + LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY, + &path); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); if (err != -EEXIST) @@ -578,7 +582,7 @@ int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id) /** * ksmbd_vfs_remove_file() - vfs helper for smb rmdir or unlink - * @name: absolute directory or file name + * @name: directory or file name that is relative to share * * Return: 0 on success, otherwise error */ @@ -588,16 +592,11 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) struct path path; struct dentry *parent; int err; - int flags = 0; if (ksmbd_override_fsids(work)) return -ENOMEM; - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS)) - flags = LOOKUP_FOLLOW; - - err = kern_path(name, flags, &path); + err = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, false); if (err) { ksmbd_debug(VFS, "can't get %s, err %d\n", name, err); ksmbd_revert_fsids(work); @@ -642,7 +641,7 @@ out_err: /** * ksmbd_vfs_link() - vfs helper for creating smb hardlink * @oldname: source file name - * @newname: hardlink name + * @newname: hardlink name that is relative to share * * Return: 0 on success, otherwise error */ @@ -652,24 +651,20 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, struct path oldpath, newpath; struct dentry *dentry; int err; - int flags = 0; if (ksmbd_override_fsids(work)) return -ENOMEM; - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS)) - flags = LOOKUP_FOLLOW; - - err = kern_path(oldname, flags, &oldpath); + err = kern_path(oldname, LOOKUP_NO_SYMLINKS, &oldpath); if (err) { pr_err("cannot get linux path for %s, err = %d\n", oldname, err); goto out1; } - dentry = kern_path_create(AT_FDCWD, newname, &newpath, - flags | LOOKUP_REVAL); + dentry = ksmbd_vfs_kern_path_create(work, newname, + LOOKUP_NO_SYMLINKS | LOOKUP_REVAL, + &newpath); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); pr_err("path create err for %s, err %d\n", newname, err); @@ -788,21 +783,19 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, struct dentry *src_dent, *trap_dent, *src_child; char *dst_name; int err; - int flags; dst_name = extract_last_component(newname); - if (!dst_name) - return -EINVAL; + if (!dst_name) { + dst_name = newname; + newname = ""; + } src_dent_parent = dget_parent(fp->filp->f_path.dentry); src_dent = fp->filp->f_path.dentry; - flags = LOOKUP_DIRECTORY; - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS)) - flags |= LOOKUP_FOLLOW; - - err = kern_path(newname, flags, &dst_path); + err = ksmbd_vfs_kern_path(work, newname, + LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY, + &dst_path, false); if (err) { ksmbd_debug(VFS, "Cannot get path for %s [%d]\n", newname, err); goto out; @@ -848,61 +841,43 @@ out: /** * ksmbd_vfs_truncate() - vfs helper for smb file truncate * @work: work - * @name: old filename * @fid: file id of old file * @size: truncate to given size * * Return: 0 on success, otherwise error */ -int ksmbd_vfs_truncate(struct ksmbd_work *work, const char *name, +int ksmbd_vfs_truncate(struct ksmbd_work *work, struct ksmbd_file *fp, loff_t size) { - struct path path; int err = 0; + struct file *filp; - if (name) { - err = kern_path(name, 0, &path); - if (err) { - pr_err("cannot get linux path for %s, err %d\n", - name, err); - return err; - } - err = vfs_truncate(&path, size); - if (err) - pr_err("truncate failed for %s err %d\n", - name, err); - path_put(&path); - } else { - struct file *filp; - - filp = fp->filp; - - /* Do we need to break any of a levelII oplock? */ - smb_break_all_levII_oplock(work, fp, 1); + filp = fp->filp; - if (!work->tcon->posix_extensions) { - struct inode *inode = file_inode(filp); + /* Do we need to break any of a levelII oplock? */ + smb_break_all_levII_oplock(work, fp, 1); - if (size < inode->i_size) { - err = check_lock_range(filp, size, - inode->i_size - 1, WRITE); - } else { - err = check_lock_range(filp, inode->i_size, - size - 1, WRITE); - } + if (!work->tcon->posix_extensions) { + struct inode *inode = file_inode(filp); - if (err) { - pr_err("failed due to lock\n"); - return -EAGAIN; - } + if (size < inode->i_size) { + err = check_lock_range(filp, size, + inode->i_size - 1, WRITE); + } else { + err = check_lock_range(filp, inode->i_size, + size - 1, WRITE); } - err = vfs_truncate(&filp->f_path, size); - if (err) - pr_err("truncate failed for filename : %s err %d\n", - fp->filename, err); + if (err) { + pr_err("failed due to lock\n"); + return -EAGAIN; + } } + err = vfs_truncate(&filp->f_path, size); + if (err) + pr_err("truncate failed for filename : %s err %d\n", + fp->filename, err); return err; } @@ -1038,7 +1013,7 @@ int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, loff_t off, loff_t len) { smb_break_all_levII_oplock(work, fp, 1); - if (fp->f_ci->m_fattr & ATTR_SPARSE_FILE_LE) + if (fp->f_ci->m_fattr & FILE_ATTRIBUTE_SPARSE_FILE_LE) return vfs_fallocate(fp->filp, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, off, len); @@ -1048,7 +1023,7 @@ int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, struct file_allocated_range_buffer *ranges, - int in_count, int *out_count) + unsigned int in_count, unsigned int *out_count) { struct file *f = fp->filp; struct inode *inode = file_inode(fp->filp); @@ -1220,22 +1195,25 @@ static int ksmbd_vfs_lookup_in_dir(struct path *dir, char *name, size_t namelen) /** * ksmbd_vfs_kern_path() - lookup a file and get path info - * @name: name of file for lookup + * @name: file path that is relative to share * @flags: lookup flags * @path: if lookup succeed, return path info * @caseless: caseless filename lookup * * Return: 0 on success, otherwise error */ -int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path, - bool caseless) +int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name, + unsigned int flags, struct path *path, bool caseless) { + struct ksmbd_share_config *share_conf = work->tcon->share_conf; int err; - if (name[0] != '/') - return -EINVAL; - - err = kern_path(name, flags, path); + flags |= LOOKUP_BENEATH; + err = vfs_path_lookup(share_conf->vfs_path.dentry, + share_conf->vfs_path.mnt, + name, + flags, + path); if (!err) return 0; @@ -1249,11 +1227,10 @@ int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path, return -ENOMEM; path_len = strlen(filepath); - remain_len = path_len - 1; + remain_len = path_len; - err = kern_path("/", flags, &parent); - if (err) - goto out; + parent = share_conf->vfs_path; + path_get(&parent); while (d_can_lookup(parent.dentry)) { char *filename = filepath + path_len - remain_len; @@ -1266,21 +1243,21 @@ int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path, err = ksmbd_vfs_lookup_in_dir(&parent, filename, filename_len); - if (err) { - path_put(&parent); + path_put(&parent); + if (err) goto out; - } - path_put(&parent); next[0] = '\0'; - err = kern_path(filepath, flags, &parent); + err = vfs_path_lookup(share_conf->vfs_path.dentry, + share_conf->vfs_path.mnt, + filepath, + flags, + &parent); if (err) goto out; - - if (is_last) { - path->mnt = parent.mnt; - path->dentry = parent.dentry; + else if (is_last) { + *path = parent; goto out; } @@ -1296,6 +1273,23 @@ out: return err; } +struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, + const char *name, + unsigned int flags, + struct path *path) +{ + char *abs_name; + struct dentry *dent; + + abs_name = convert_to_unix_name(work->tcon->share_conf, name); + if (!abs_name) + return ERR_PTR(-ENOMEM); + + dent = kern_path_create(AT_FDCWD, abs_name, path, flags); + kfree(abs_name); + return dent; +} + int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns, struct dentry *dentry) { @@ -1630,7 +1624,7 @@ void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat) time = ksmbd_UnixTimeToNT(kstat->ctime); info->ChangeTime = cpu_to_le64(time); - if (ksmbd_kstat->file_attributes & ATTR_DIRECTORY_LE) { + if (ksmbd_kstat->file_attributes & FILE_ATTRIBUTE_DIRECTORY_LE) { info->EndOfFile = 0; info->AllocationSize = 0; } else { @@ -1660,9 +1654,9 @@ int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, * or that acl is disable in server's filesystem and the config is yes. */ if (S_ISDIR(ksmbd_kstat->kstat->mode)) - ksmbd_kstat->file_attributes = ATTR_DIRECTORY_LE; + ksmbd_kstat->file_attributes = FILE_ATTRIBUTE_DIRECTORY_LE; else - ksmbd_kstat->file_attributes = ATTR_ARCHIVE_LE; + ksmbd_kstat->file_attributes = FILE_ATTRIBUTE_ARCHIVE_LE; if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { diff --git a/fs/ksmbd/vfs.h b/fs/ksmbd/vfs.h index 85db50abdb24..adf94a4f22fa 100644 --- a/fs/ksmbd/vfs.h +++ b/fs/ksmbd/vfs.h @@ -25,48 +25,9 @@ enum { }; /* CreateOptions */ -/* Flag is set, it must not be a file , valid for directory only */ -#define FILE_DIRECTORY_FILE_LE cpu_to_le32(0x00000001) -#define FILE_WRITE_THROUGH_LE cpu_to_le32(0x00000002) -#define FILE_SEQUENTIAL_ONLY_LE cpu_to_le32(0x00000004) - -/* Should not buffer on server*/ -#define FILE_NO_INTERMEDIATE_BUFFERING_LE cpu_to_le32(0x00000008) -/* MBZ */ -#define FILE_SYNCHRONOUS_IO_ALERT_LE cpu_to_le32(0x00000010) -/* MBZ */ -#define FILE_SYNCHRONOUS_IO_NONALERT_LE cpu_to_le32(0x00000020) - -/* Flaf must not be set for directory */ -#define FILE_NON_DIRECTORY_FILE_LE cpu_to_le32(0x00000040) - -/* Should be zero */ #define CREATE_TREE_CONNECTION cpu_to_le32(0x00000080) -#define FILE_COMPLETE_IF_OPLOCKED_LE cpu_to_le32(0x00000100) -#define FILE_NO_EA_KNOWLEDGE_LE cpu_to_le32(0x00000200) -#define FILE_OPEN_REMOTE_INSTANCE cpu_to_le32(0x00000400) - -/** - * Doc says this is obsolete "open for recovery" flag should be zero - * in any case. - */ -#define CREATE_OPEN_FOR_RECOVERY cpu_to_le32(0x00000400) -#define FILE_RANDOM_ACCESS_LE cpu_to_le32(0x00000800) -#define FILE_DELETE_ON_CLOSE_LE cpu_to_le32(0x00001000) -#define FILE_OPEN_BY_FILE_ID_LE cpu_to_le32(0x00002000) -#define FILE_OPEN_FOR_BACKUP_INTENT_LE cpu_to_le32(0x00004000) -#define FILE_NO_COMPRESSION_LE cpu_to_le32(0x00008000) - -/* Should be zero*/ -#define FILE_OPEN_REQUIRING_OPLOCK cpu_to_le32(0x00010000) -#define FILE_DISALLOW_EXCLUSIVE cpu_to_le32(0x00020000) #define FILE_RESERVE_OPFILTER_LE cpu_to_le32(0x00100000) -#define FILE_OPEN_REPARSE_POINT_LE cpu_to_le32(0x00200000) -#define FILE_OPEN_NO_RECALL_LE cpu_to_le32(0x00400000) -/* Should be zero */ -#define FILE_OPEN_FOR_FREE_SPACE_QUERY_LE cpu_to_le32(0x00800000) -#define CREATE_OPTIONS_MASK cpu_to_le32(0x00FFFFFF) #define CREATE_OPTION_READONLY 0x10000000 /* system. NB not sent over wire */ #define CREATE_OPTION_SPECIAL 0x20000000 @@ -126,7 +87,7 @@ int ksmbd_vfs_link(struct ksmbd_work *work, int ksmbd_vfs_getattr(struct path *path, struct kstat *stat); int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, char *newname); -int ksmbd_vfs_truncate(struct ksmbd_work *work, const char *name, +int ksmbd_vfs_truncate(struct ksmbd_work *work, struct ksmbd_file *fp, loff_t size); struct srv_copychunk; int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, @@ -152,8 +113,13 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, size_t *xattr_stream_name_size, int s_type); int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, struct dentry *dentry, char *attr_name); -int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path, +int ksmbd_vfs_kern_path(struct ksmbd_work *work, + char *name, unsigned int flags, struct path *path, bool caseless); +struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, + const char *name, + unsigned int flags, + struct path *path); int ksmbd_vfs_empty_dir(struct ksmbd_file *fp); void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option); int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, @@ -161,7 +127,7 @@ int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, struct file_allocated_range_buffer; int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, struct file_allocated_range_buffer *ranges, - int in_count, int *out_count); + unsigned int in_count, unsigned int *out_count); int ksmbd_vfs_unlink(struct user_namespace *user_ns, struct dentry *dir, struct dentry *dentry); void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat); |