Ten fixes for the ksmbd kernel server, for improved security and additional buffer overflow checks
-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmFzb/4ACgkQiiy9cAdy T1HhOQv/bTsGX85V5QFBQyW7F7ymYaSuDpvb/nYFzzs4fMz8/lbCJ6GQHMK7G+uR ZYFdZw5c5A+KsfjRoRAKOc6HJbGERnKWvIlRkNWnmcpsXcR3PFhhOxXKXxCTBjga fHxAJBENbsyCtGz25pUtuOKVG9zVaz31lyXgnz53b0ATrb8CxIll7zdKTp7aGdK0 UIWUEYeUwctIhvyBXyms8ni+15keop6/7Y1KhUvodL/VhU4YCkKerFFQojsqLEBk 4N6ZrgEEvzZPSjMt3KkbAapMNvf8Jgy6hKrB10MWB2sJG3bG1A4MuWYzVz7hlLbx KXztSjbEiExCw7Y8ZXeA0pT/P51TVB9uxSLaDJhGVrXIxkZ4exUz2pS0Tp2E8eMH 4BGylAV9vrZqjmF3HQHJu8c/+f833dwbmMzgDFlFgR01U8NQYQvLf6ZoxmnQxdJC 5CVzO2rGV1bAVEbCJAN/wnKSCpEzjcN0ruz9bcje8MFF9mXXiJxATIgNahuaj17t AvOnAbwd =FhM8 -----END PGP SIGNATURE----- Merge tag '5.15-rc6-ksmbd-fixes' of git://git.samba.org/ksmbd Pull ksmbd fixes from Steve French: "Ten fixes for the ksmbd kernel server, for improved security and additional buffer overflow checks: - a security improvement to session establishment to reduce the possibility of dictionary attacks - fix to ensure that maximum i/o size negotiated in the protocol is not less than 64K and not more than 8MB to better match expected behavior - fix for crediting (flow control) important to properly verify that sufficient credits are available for the requested operation - seven additional buffer overflow, buffer validation checks" * tag '5.15-rc6-ksmbd-fixes' of git://git.samba.org/ksmbd: ksmbd: add buffer validation in session setup ksmbd: throttle session setup failures to avoid dictionary attacks ksmbd: validate OutputBufferLength of QUERY_DIR, QUERY_INFO, IOCTL requests ksmbd: validate credit charge after validating SMB2 PDU body size ksmbd: add buffer validation for smb direct ksmbd: limit read/write/trans buffer size not to exceed 8MB ksmbd: validate compound response buffer ksmbd: fix potencial 32bit overflow from data area check in smb2_write ksmbd: improve credits management ksmbd: add validation in smb2_ioctl
This commit is contained in:
commit
c460e7896e
@ -298,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)) {
|
||||
@ -314,15 +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);
|
||||
|
||||
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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -284,11 +284,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:
|
||||
@ -310,21 +312,37 @@ 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)
|
||||
@ -382,26 +400,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);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (smb2_calc_size(hdr, &clc_len))
|
||||
return 1;
|
||||
|
||||
if (len != clc_len) {
|
||||
/* 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.
|
||||
@ -414,7 +426,7 @@ 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;
|
||||
}
|
||||
|
||||
ksmbd_debug(SMB,
|
||||
@ -425,6 +437,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;
|
||||
}
|
||||
|
||||
|
@ -284,6 +284,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;
|
||||
@ -292,6 +293,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;
|
||||
@ -300,6 +302,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;
|
||||
|
@ -292,22 +292,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
|
||||
@ -317,50 +301,44 @@ 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);
|
||||
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/* 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 (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;
|
||||
|
||||
@ -368,7 +346,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,
|
||||
@ -472,6 +449,12 @@ bool is_chained_smb2_message(struct ksmbd_work *work)
|
||||
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;
|
||||
@ -541,7 +524,7 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work)
|
||||
{
|
||||
struct smb2_hdr *hdr = 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);
|
||||
|
||||
@ -1274,19 +1257,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;
|
||||
@ -1298,9 +1275,9 @@ 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 challenge_message *chgblob;
|
||||
unsigned char *spnego_blob = NULL;
|
||||
@ -1309,8 +1286,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;
|
||||
|
||||
@ -1378,12 +1354,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)) {
|
||||
@ -1629,6 +1616,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
|
||||
struct smb2_sess_setup_rsp *rsp = 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");
|
||||
@ -1709,10 +1697,16 @@ int smb2_sess_setup(struct ksmbd_work *work)
|
||||
if (sess->state == SMB2_SESSION_EXPIRED)
|
||||
sess->state = SMB2_SESSION_IN_PROGRESS;
|
||||
|
||||
negblob = (struct negotiate_message *)((char *)&req->hdr.ProtocolId +
|
||||
le16_to_cpu(req->SecurityBufferOffset));
|
||||
negblob_off = le16_to_cpu(req->SecurityBufferOffset);
|
||||
negblob_len = le16_to_cpu(req->SecurityBufferLength);
|
||||
if (negblob_off < (offsetof(struct smb2_sess_setup_req, Buffer) - 4) ||
|
||||
negblob_len < offsetof(struct negotiate_message, NegotiateFlags))
|
||||
return -EINVAL;
|
||||
|
||||
if (decode_negotiation_token(work, negblob) == 0) {
|
||||
negblob = (struct negotiate_message *)((char *)&req->hdr.ProtocolId +
|
||||
negblob_off);
|
||||
|
||||
if (decode_negotiation_token(conn, negblob, negblob_len) == 0) {
|
||||
if (conn->mechToken)
|
||||
negblob = (struct negotiate_message *)conn->mechToken;
|
||||
}
|
||||
@ -1736,7 +1730,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 =
|
||||
@ -1796,9 +1790,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;
|
||||
@ -3779,6 +3794,24 @@ 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;
|
||||
@ -3855,9 +3888,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;
|
||||
|
||||
/*
|
||||
@ -4091,12 +4128,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) {
|
||||
@ -4407,6 +4443,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);
|
||||
@ -4420,6 +4458,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);
|
||||
@ -4444,6 +4488,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,
|
||||
@ -4454,12 +4502,13 @@ 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 (!S_ISDIR(stat.mode)) {
|
||||
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,
|
||||
@ -6220,8 +6269,7 @@ static noinline int smb2_write_pipe(struct ksmbd_work *work)
|
||||
(offsetof(struct smb2_write_req, Buffer) - 4)) {
|
||||
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(req)) {
|
||||
pr_err("invalid write data offset %u, smb_len %u\n",
|
||||
le16_to_cpu(req->DataOffset),
|
||||
get_rfc1002_len(req));
|
||||
@ -6379,8 +6427,7 @@ int smb2_write(struct ksmbd_work *work)
|
||||
(offsetof(struct smb2_write_req, Buffer) - 4)) {
|
||||
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(req)) {
|
||||
pr_err("invalid write data offset %u, smb_len %u\n",
|
||||
le16_to_cpu(req->DataOffset),
|
||||
get_rfc1002_len(req));
|
||||
@ -7023,24 +7070,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 =
|
||||
@ -7050,12 +7099,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;
|
||||
@ -7076,9 +7126,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])) {
|
||||
@ -7153,8 +7201,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;
|
||||
@ -7166,6 +7214,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;
|
||||
|
||||
@ -7245,11 +7299,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;
|
||||
@ -7257,11 +7306,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 < sizeof(struct validate_negotiate_info_req) +
|
||||
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) {
|
||||
@ -7295,7 +7349,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;
|
||||
@ -7322,7 +7376,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;
|
||||
@ -7436,8 +7491,7 @@ 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;
|
||||
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;
|
||||
@ -7465,8 +7519,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:
|
||||
@ -7494,6 +7554,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:
|
||||
@ -7502,9 +7563,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;
|
||||
|
||||
@ -7513,9 +7581,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)) {
|
||||
@ -7540,15 +7609,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)
|
||||
@ -7567,6 +7654,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];
|
||||
|
||||
@ -7586,6 +7678,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],
|
||||
@ -7626,6 +7723,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,
|
||||
@ -7696,6 +7798,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);
|
||||
|
@ -113,6 +113,8 @@
|
||||
#define SMB21_DEFAULT_IOSIZE (1024 * 1024)
|
||||
#define SMB3_DEFAULT_IOSIZE (4 * 1024 * 1024)
|
||||
#define SMB3_DEFAULT_TRANS_SIZE (1024 * 1024)
|
||||
#define SMB3_MIN_IOSIZE (64 * 1024)
|
||||
#define SMB3_MAX_IOSIZE (8 * 1024 * 1024)
|
||||
|
||||
/*
|
||||
* SMB2 Header Definition
|
||||
|
@ -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);
|
||||
|
@ -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 *
|
||||
|
@ -549,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);
|
||||
@ -556,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;
|
||||
|
||||
@ -568,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);
|
||||
|
@ -1023,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);
|
||||
|
@ -166,7 +166,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);
|
||||
|
Loading…
Reference in New Issue
Block a user