Commit c460e789 authored by Linus Torvalds's avatar Linus Torvalds

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
parents 0f386a60 0d994cd4
...@@ -298,8 +298,8 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, ...@@ -298,8 +298,8 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob,
int blob_len, struct ksmbd_session *sess) int blob_len, struct ksmbd_session *sess)
{ {
char *domain_name; char *domain_name;
unsigned int lm_off, nt_off; unsigned int nt_off, dn_off;
unsigned short nt_len; unsigned short nt_len, dn_len;
int ret; int ret;
if (blob_len < sizeof(struct authenticate_message)) { if (blob_len < sizeof(struct authenticate_message)) {
...@@ -314,15 +314,17 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, ...@@ -314,15 +314,17 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob,
return -EINVAL; return -EINVAL;
} }
lm_off = le32_to_cpu(authblob->LmChallengeResponse.BufferOffset);
nt_off = le32_to_cpu(authblob->NtChallengeResponse.BufferOffset); nt_off = le32_to_cpu(authblob->NtChallengeResponse.BufferOffset);
nt_len = le16_to_cpu(authblob->NtChallengeResponse.Length); 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 */ /* TODO : use domain name that imported from configuration file */
domain_name = smb_strndup_from_utf16((const char *)authblob + domain_name = smb_strndup_from_utf16((const char *)authblob + dn_off,
le32_to_cpu(authblob->DomainName.BufferOffset), dn_len, true, sess->conn->local_nls);
le16_to_cpu(authblob->DomainName.Length), true,
sess->conn->local_nls);
if (IS_ERR(domain_name)) if (IS_ERR(domain_name))
return PTR_ERR(domain_name); return PTR_ERR(domain_name);
......
...@@ -61,6 +61,8 @@ struct ksmbd_conn *ksmbd_conn_alloc(void) ...@@ -61,6 +61,8 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)
conn->local_nls = load_nls_default(); conn->local_nls = load_nls_default();
atomic_set(&conn->req_running, 0); atomic_set(&conn->req_running, 0);
atomic_set(&conn->r_count, 0); atomic_set(&conn->r_count, 0);
conn->total_credits = 1;
init_waitqueue_head(&conn->req_running_q); init_waitqueue_head(&conn->req_running_q);
INIT_LIST_HEAD(&conn->conns_list); INIT_LIST_HEAD(&conn->conns_list);
INIT_LIST_HEAD(&conn->sessions); INIT_LIST_HEAD(&conn->sessions);
......
...@@ -211,6 +211,7 @@ struct ksmbd_tree_disconnect_request { ...@@ -211,6 +211,7 @@ struct ksmbd_tree_disconnect_request {
*/ */
struct ksmbd_logout_request { struct ksmbd_logout_request {
__s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */ __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */
__u32 account_flags;
}; };
/* /*
...@@ -317,6 +318,7 @@ enum KSMBD_TREE_CONN_STATUS { ...@@ -317,6 +318,7 @@ enum KSMBD_TREE_CONN_STATUS {
#define KSMBD_USER_FLAG_BAD_UID BIT(2) #define KSMBD_USER_FLAG_BAD_UID BIT(2)
#define KSMBD_USER_FLAG_BAD_USER BIT(3) #define KSMBD_USER_FLAG_BAD_USER BIT(3)
#define KSMBD_USER_FLAG_GUEST_ACCOUNT BIT(4) #define KSMBD_USER_FLAG_GUEST_ACCOUNT BIT(4)
#define KSMBD_USER_FLAG_DELAY_SESSION BIT(5)
/* /*
* Share config flags. * Share config flags.
......
...@@ -55,7 +55,7 @@ struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp) ...@@ -55,7 +55,7 @@ struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp)
void ksmbd_free_user(struct ksmbd_user *user) 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->name);
kfree(user->passkey); kfree(user->passkey);
kfree(user); kfree(user);
......
...@@ -18,6 +18,7 @@ struct ksmbd_user { ...@@ -18,6 +18,7 @@ struct ksmbd_user {
size_t passkey_sz; size_t passkey_sz;
char *passkey; char *passkey;
unsigned int failed_login_count;
}; };
static inline bool user_guest(struct ksmbd_user *user) 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) ...@@ -284,11 +284,13 @@ static inline int smb2_ioctl_resp_len(struct smb2_ioctl_req *h)
le32_to_cpu(h->MaxOutputResponse); 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; unsigned int req_len = 0, expect_resp_len = 0, calc_credit_num, max_len;
int credit_charge = le16_to_cpu(hdr->CreditCharge); unsigned short credit_charge = le16_to_cpu(hdr->CreditCharge);
void *__hdr = hdr; void *__hdr = hdr;
int ret;
switch (hdr->Command) { switch (hdr->Command) {
case SMB2_QUERY_INFO: case SMB2_QUERY_INFO:
...@@ -310,21 +312,37 @@ static int smb2_validate_credit_charge(struct smb2_hdr *hdr) ...@@ -310,21 +312,37 @@ static int smb2_validate_credit_charge(struct smb2_hdr *hdr)
req_len = smb2_ioctl_req_len(__hdr); req_len = smb2_ioctl_req_len(__hdr);
expect_resp_len = smb2_ioctl_resp_len(__hdr); expect_resp_len = smb2_ioctl_resp_len(__hdr);
break; break;
default: case SMB2_CANCEL:
return 0; return 0;
default:
req_len = 1;
break;
} }
credit_charge = max(1, credit_charge); credit_charge = max_t(unsigned short, credit_charge, 1);
max_len = max(req_len, expect_resp_len); max_len = max_t(unsigned int, req_len, expect_resp_len);
calc_credit_num = DIV_ROUND_UP(max_len, SMB2_MAX_BUFFER_SIZE); calc_credit_num = DIV_ROUND_UP(max_len, SMB2_MAX_BUFFER_SIZE);
if (credit_charge < calc_credit_num) { if (credit_charge < calc_credit_num) {
pr_err("Insufficient credit charge, given: %d, needed: %d\n", ksmbd_debug(SMB, "Insufficient credit charge, given: %d, needed: %d\n",
credit_charge, calc_credit_num); 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 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) int ksmbd_smb2_check_message(struct ksmbd_work *work)
...@@ -382,26 +400,20 @@ 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)) if (smb2_calc_size(hdr, &clc_len))
return 1; return 1;
if (len != clc_len) { if (len != clc_len) {
/* client 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) if (clc_len == len + 1)
return 0; goto validate_credit;
/* /*
* Some windows servers (win2016) will pad also the final * Some windows servers (win2016) will pad also the final
* PDU in a compound to 8 bytes. * PDU in a compound to 8 bytes.
*/ */
if (ALIGN(clc_len, 8) == len) if (ALIGN(clc_len, 8) == len)
return 0; goto validate_credit;
/* /*
* windows client also pad up to 8 bytes when compounding. * windows client also pad up to 8 bytes when compounding.
...@@ -414,7 +426,7 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) ...@@ -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", "cli req padded more than expected. Length %d not %d for cmd:%d mid:%llu\n",
len, clc_len, command, len, clc_len, command,
le64_to_cpu(hdr->MessageId)); le64_to_cpu(hdr->MessageId));
return 0; goto validate_credit;
} }
ksmbd_debug(SMB, ksmbd_debug(SMB,
...@@ -425,6 +437,13 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) ...@@ -425,6 +437,13 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work)
return 1; 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; return 0;
} }
......
...@@ -284,6 +284,7 @@ int init_smb3_11_server(struct ksmbd_conn *conn) ...@@ -284,6 +284,7 @@ int init_smb3_11_server(struct ksmbd_conn *conn)
void init_smb2_max_read_size(unsigned int sz) 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; smb21_server_values.max_read_size = sz;
smb30_server_values.max_read_size = sz; smb30_server_values.max_read_size = sz;
smb302_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) ...@@ -292,6 +293,7 @@ void init_smb2_max_read_size(unsigned int sz)
void init_smb2_max_write_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; smb21_server_values.max_write_size = sz;
smb30_server_values.max_write_size = sz; smb30_server_values.max_write_size = sz;
smb302_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) ...@@ -300,6 +302,7 @@ void init_smb2_max_write_size(unsigned int sz)
void init_smb2_max_trans_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; smb21_server_values.max_trans_size = sz;
smb30_server_values.max_trans_size = sz; smb30_server_values.max_trans_size = sz;
smb302_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) ...@@ -292,22 +292,6 @@ int init_smb2_neg_rsp(struct ksmbd_work *work)
return 0; 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 * smb2_set_rsp_credits() - set number of credits in response buffer
* @work: smb work containing smb response buffer * @work: smb work containing smb response buffer
...@@ -317,49 +301,43 @@ int smb2_set_rsp_credits(struct ksmbd_work *work) ...@@ -317,49 +301,43 @@ int smb2_set_rsp_credits(struct ksmbd_work *work)
struct smb2_hdr *req_hdr = ksmbd_req_buf_next(work); struct smb2_hdr *req_hdr = ksmbd_req_buf_next(work);
struct smb2_hdr *hdr = ksmbd_resp_buf_next(work); struct smb2_hdr *hdr = ksmbd_resp_buf_next(work);
struct ksmbd_conn *conn = work->conn; struct ksmbd_conn *conn = work->conn;
unsigned short credits_requested = le16_to_cpu(req_hdr->CreditRequest); unsigned short credits_requested;
unsigned short credit_charge = 1, credits_granted = 0; unsigned short credit_charge, credits_granted = 0;
unsigned short aux_max, aux_credits, min_credits; unsigned short aux_max, aux_credits;
int rsp_credit_charge;
if (hdr->Command == SMB2_CANCEL) if (work->send_no_response)
goto out; return 0;
/* get default minimum credits by shifting maximum credits by 4 */ hdr->CreditCharge = req_hdr->CreditCharge;
min_credits = conn->max_credits >> 4;
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); 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; 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) { /* according to smb2.credits smbtorture, Windows server
aux_credits = credits_requested - 1; * 2016 or later grant up to 8192 credits at once.
aux_max = 32; *
if (hdr->Command == SMB2_NEGOTIATE) * TODO: Need to adjuct CreditRequest value according to
aux_max = 0; * current cpu load
aux_credits = (aux_credits < aux_max) ? aux_credits : aux_max; */
credits_granted = aux_credits + credit_charge; 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 if (conn->max_credits - conn->total_credits < credits_granted)
* minimum credits then we should wrap it up within the limits. credits_granted = conn->max_credits -
*/ conn->total_credits;
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;
}
conn->total_credits += credits_granted; conn->total_credits += credits_granted;
work->credits_granted += credits_granted; work->credits_granted += credits_granted;
...@@ -368,7 +346,6 @@ int smb2_set_rsp_credits(struct ksmbd_work *work) ...@@ -368,7 +346,6 @@ int smb2_set_rsp_credits(struct ksmbd_work *work)
/* Update CreditRequest in last request */ /* Update CreditRequest in last request */
hdr->CreditRequest = cpu_to_le16(work->credits_granted); hdr->CreditRequest = cpu_to_le16(work->credits_granted);
} }
out:
ksmbd_debug(SMB, ksmbd_debug(SMB,
"credits: requested[%d] granted[%d] total_granted[%d]\n", "credits: requested[%d] granted[%d] total_granted[%d]\n",
credits_requested, credits_granted, credits_requested, credits_granted,
...@@ -472,6 +449,12 @@ bool is_chained_smb2_message(struct ksmbd_work *work) ...@@ -472,6 +449,12 @@ bool is_chained_smb2_message(struct ksmbd_work *work)
return false; 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"); ksmbd_debug(SMB, "got SMB2 chained command\n");
init_chained_smb2_rsp(work); init_chained_smb2_rsp(work);
return true; return true;
...@@ -541,7 +524,7 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work) ...@@ -541,7 +524,7 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work)
{ {
struct smb2_hdr *hdr = work->request_buf; struct smb2_hdr *hdr = work->request_buf;
size_t small_sz = MAX_CIFS_SMALL_BUFFER_SIZE; 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; size_t sz = small_sz;
int cmd = le16_to_cpu(hdr->Command); int cmd = le16_to_cpu(hdr->Command);
...@@ -1274,19 +1257,13 @@ static int generate_preauth_hash(struct ksmbd_work *work) ...@@ -1274,19 +1257,13 @@ static int generate_preauth_hash(struct ksmbd_work *work)
return 0; return 0;
} }
static int decode_negotiation_token(struct ksmbd_work *work, static int decode_negotiation_token(struct ksmbd_conn *conn,
struct negotiate_message *negblob) 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) if (!conn->use_spnego)
return -EINVAL; return -EINVAL;
req = work->request_buf;
sz = le16_to_cpu(req->SecurityBufferLength);
if (ksmbd_decode_negTokenInit((char *)negblob, sz, conn)) { if (ksmbd_decode_negTokenInit((char *)negblob, sz, conn)) {
if (ksmbd_decode_negTokenTarg((char *)negblob, sz, conn)) { if (ksmbd_decode_negTokenTarg((char *)negblob, sz, conn)) {
conn->auth_mechs |= KSMBD_AUTH_NTLMSSP; conn->auth_mechs |= KSMBD_AUTH_NTLMSSP;
...@@ -1298,9 +1275,9 @@ static int decode_negotiation_token(struct ksmbd_work *work, ...@@ -1298,9 +1275,9 @@ static int decode_negotiation_token(struct ksmbd_work *work,
} }
static int ntlm_negotiate(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 = work->response_buf;
struct challenge_message *chgblob; struct challenge_message *chgblob;
unsigned char *spnego_blob = NULL; unsigned char *spnego_blob = NULL;
...@@ -1309,8 +1286,7 @@ static int ntlm_negotiate(struct ksmbd_work *work, ...@@ -1309,8 +1286,7 @@ static int ntlm_negotiate(struct ksmbd_work *work,
int sz, rc; int sz, rc;
ksmbd_debug(SMB, "negotiate phase\n"); ksmbd_debug(SMB, "negotiate phase\n");
sz = le16_to_cpu(req->SecurityBufferLength); rc = ksmbd_decode_ntlmssp_neg_blob(negblob, negblob_len, work->sess);
rc = ksmbd_decode_ntlmssp_neg_blob(negblob, sz, work->sess);
if (rc) if (rc)
return rc; return rc;
...@@ -1378,12 +1354,23 @@ static struct ksmbd_user *session_user(struct ksmbd_conn *conn, ...@@ -1378,12 +1354,23 @@ static struct ksmbd_user *session_user(struct ksmbd_conn *conn,
struct authenticate_message *authblob; struct authenticate_message *authblob;
struct ksmbd_user *user; struct ksmbd_user *user;
char *name; 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); authblob = user_authblob(conn, req);
sz = le32_to_cpu(authblob->UserName.BufferOffset); name_off = le32_to_cpu(authblob->UserName.BufferOffset);
name = smb_strndup_from_utf16((const char *)authblob + sz, name_len = le16_to_cpu(authblob->UserName.Length);
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, true,
conn->local_nls); conn->local_nls);
if (IS_ERR(name)) { if (IS_ERR(name)) {
...@@ -1629,6 +1616,7 @@ int smb2_sess_setup(struct ksmbd_work *work) ...@@ -1629,6 +1616,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
struct smb2_sess_setup_rsp *rsp = work->response_buf; struct smb2_sess_setup_rsp *rsp = work->response_buf;
struct ksmbd_session *sess; struct ksmbd_session *sess;
struct negotiate_message *negblob; struct negotiate_message *negblob;
unsigned int negblob_len, negblob_off;
int rc = 0; int rc = 0;
ksmbd_debug(SMB, "Received request for session setup\n"); ksmbd_debug(SMB, "Received request for session setup\n");
...@@ -1709,10 +1697,16 @@ int smb2_sess_setup(struct ksmbd_work *work) ...@@ -1709,10 +1697,16 @@ int smb2_sess_setup(struct ksmbd_work *work)
if (sess->state == SMB2_SESSION_EXPIRED) if (sess->state == SMB2_SESSION_EXPIRED)
sess->state = SMB2_SESSION_IN_PROGRESS; 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) - 4) ||
negblob_len < offsetof(struct negotiate_message, NegotiateFlags))
return -EINVAL;
negblob = (struct negotiate_message *)((char *)&req->hdr.ProtocolId + 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) if (conn->mechToken)
negblob = (struct negotiate_message *)conn->mechToken; negblob = (struct negotiate_message *)conn->mechToken;
} }
...@@ -1736,7 +1730,7 @@ int smb2_sess_setup(struct ksmbd_work *work) ...@@ -1736,7 +1730,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
sess->Preauth_HashValue = NULL; sess->Preauth_HashValue = NULL;
} else if (conn->preferred_auth_mech == KSMBD_AUTH_NTLMSSP) { } else if (conn->preferred_auth_mech == KSMBD_AUTH_NTLMSSP) {
if (negblob->MessageType == NtLmNegotiate) { if (negblob->MessageType == NtLmNegotiate) {
rc = ntlm_negotiate(work, negblob); rc = ntlm_negotiate(work, negblob, negblob_len);
if (rc) if (rc)
goto out_err; goto out_err;
rsp->hdr.Status = rsp->hdr.Status =
...@@ -1796,9 +1790,30 @@ int smb2_sess_setup(struct ksmbd_work *work) ...@@ -1796,9 +1790,30 @@ int smb2_sess_setup(struct ksmbd_work *work)
conn->mechToken = NULL; conn->mechToken = NULL;
} }
if (rc < 0 && sess) { if (rc < 0) {
ksmbd_session_destroy(sess); /*
work->sess = NULL; * 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; return rc;
...@@ -3779,6 +3794,24 @@ static int verify_info_level(int info_level) ...@@ -3779,6 +3794,24 @@ static int verify_info_level(int info_level)
return 0; 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) int smb2_query_dir(struct ksmbd_work *work)
{ {
struct ksmbd_conn *conn = work->conn; struct ksmbd_conn *conn = work->conn;
...@@ -3855,9 +3888,13 @@ int smb2_query_dir(struct ksmbd_work *work) ...@@ -3855,9 +3888,13 @@ int smb2_query_dir(struct ksmbd_work *work)
memset(&d_info, 0, sizeof(struct ksmbd_dir_info)); memset(&d_info, 0, sizeof(struct ksmbd_dir_info));
d_info.wptr = (char *)rsp->Buffer; d_info.wptr = (char *)rsp->Buffer;
d_info.rptr = (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 =
d_info.out_buf_len = min_t(int, d_info.out_buf_len, le32_to_cpu(req->OutputBufferLength)) - smb2_calc_max_out_buf_len(work, 8,
sizeof(struct smb2_query_directory_rsp); le32_to_cpu(req->OutputBufferLength));
if (d_info.out_buf_len < 0) {
rc = -EINVAL;
goto err_out;
}
d_info.flags = srch_flag; d_info.flags = srch_flag;
/* /*
...@@ -4091,12 +4128,11 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -4091,12 +4128,11 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp,
le32_to_cpu(req->Flags)); le32_to_cpu(req->Flags));
} }
buf_free_len = work->response_sz - buf_free_len =
(get_rfc1002_len(rsp_org) + 4) - smb2_calc_max_out_buf_len(work, 8,
sizeof(struct smb2_query_info_rsp); le32_to_cpu(req->OutputBufferLength));
if (buf_free_len < 0)
if (le32_to_cpu(req->OutputBufferLength) < buf_free_len) return -EINVAL;
buf_free_len = le32_to_cpu(req->OutputBufferLength);
rc = ksmbd_vfs_listxattr(path->dentry, &xattr_list); rc = ksmbd_vfs_listxattr(path->dentry, &xattr_list);
if (rc < 0) { if (rc < 0) {
...@@ -4407,6 +4443,8 @@ static void get_file_stream_info(struct ksmbd_work *work, ...@@ -4407,6 +4443,8 @@ static void get_file_stream_info(struct ksmbd_work *work,
struct path *path = &fp->filp->f_path; struct path *path = &fp->filp->f_path;
ssize_t xattr_list_len; ssize_t xattr_list_len;
int nbytes = 0, streamlen, stream_name_len, next, idx = 0; 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), generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp),
&stat); &stat);
...@@ -4420,6 +4458,12 @@ static void get_file_stream_info(struct ksmbd_work *work, ...@@ -4420,6 +4458,12 @@ static void get_file_stream_info(struct ksmbd_work *work,
goto out; 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) { while (idx < xattr_list_len) {
stream_name = xattr_list + idx; stream_name = xattr_list + idx;
streamlen = strlen(stream_name); streamlen = strlen(stream_name);
...@@ -4444,6 +4488,10 @@ static void get_file_stream_info(struct ksmbd_work *work, ...@@ -4444,6 +4488,10 @@ static void get_file_stream_info(struct ksmbd_work *work,
streamlen = snprintf(stream_buf, streamlen + 1, streamlen = snprintf(stream_buf, streamlen + 1,
":%s", &stream_name[XATTR_NAME_STREAM_LEN]); ":%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]; file_info = (struct smb2_file_stream_info *)&rsp->Buffer[nbytes];
streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName,
stream_buf, streamlen, stream_buf, streamlen,
...@@ -4454,12 +4502,13 @@ static void get_file_stream_info(struct ksmbd_work *work, ...@@ -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->StreamSize = cpu_to_le64(stream_name_len);
file_info->StreamAllocationSize = 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; nbytes += next;
buf_free_len -= next;
file_info->NextEntryOffset = cpu_to_le32(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 *) file_info = (struct smb2_file_stream_info *)
&rsp->Buffer[nbytes]; &rsp->Buffer[nbytes];
streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName,
...@@ -6220,8 +6269,7 @@ static noinline int smb2_write_pipe(struct ksmbd_work *work) ...@@ -6220,8 +6269,7 @@ static noinline int smb2_write_pipe(struct ksmbd_work *work)
(offsetof(struct smb2_write_req, Buffer) - 4)) { (offsetof(struct smb2_write_req, Buffer) - 4)) {
data_buf = (char *)&req->Buffer[0]; data_buf = (char *)&req->Buffer[0];
} else { } else {
if ((le16_to_cpu(req->DataOffset) > get_rfc1002_len(req)) || if ((u64)le16_to_cpu(req->DataOffset) + length > get_rfc1002_len(req)) {
(le16_to_cpu(req->DataOffset) + length > get_rfc1002_len(req))) {
pr_err("invalid write data offset %u, smb_len %u\n", pr_err("invalid write data offset %u, smb_len %u\n",
le16_to_cpu(req->DataOffset), le16_to_cpu(req->DataOffset),
get_rfc1002_len(req)); get_rfc1002_len(req));
...@@ -6379,8 +6427,7 @@ int smb2_write(struct ksmbd_work *work) ...@@ -6379,8 +6427,7 @@ int smb2_write(struct ksmbd_work *work)
(offsetof(struct smb2_write_req, Buffer) - 4)) { (offsetof(struct smb2_write_req, Buffer) - 4)) {
data_buf = (char *)&req->Buffer[0]; data_buf = (char *)&req->Buffer[0];
} else { } else {
if ((le16_to_cpu(req->DataOffset) > get_rfc1002_len(req)) || if ((u64)le16_to_cpu(req->DataOffset) + length > get_rfc1002_len(req)) {
(le16_to_cpu(req->DataOffset) + length > get_rfc1002_len(req))) {
pr_err("invalid write data offset %u, smb_len %u\n", pr_err("invalid write data offset %u, smb_len %u\n",
le16_to_cpu(req->DataOffset), le16_to_cpu(req->DataOffset),
get_rfc1002_len(req)); get_rfc1002_len(req));
...@@ -7023,24 +7070,26 @@ int smb2_lock(struct ksmbd_work *work) ...@@ -7023,24 +7070,26 @@ int smb2_lock(struct ksmbd_work *work)
return err; 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 smb2_ioctl_rsp *rsp)
{ {
struct copychunk_ioctl_req *ci_req;
struct copychunk_ioctl_rsp *ci_rsp; struct copychunk_ioctl_rsp *ci_rsp;
struct ksmbd_file *src_fp = NULL, *dst_fp = NULL; struct ksmbd_file *src_fp = NULL, *dst_fp = NULL;
struct srv_copychunk *chunks; struct srv_copychunk *chunks;
unsigned int i, chunk_count, chunk_count_written = 0; unsigned int i, chunk_count, chunk_count_written = 0;
unsigned int chunk_size_written = 0; unsigned int chunk_size_written = 0;
loff_t total_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]; ci_rsp = (struct copychunk_ioctl_rsp *)&rsp->Buffer[0];
rsp->VolatileFileId = req->VolatileFileId; rsp->VolatileFileId = cpu_to_le64(volatile_id);
rsp->PersistentFileId = req->PersistentFileId; rsp->PersistentFileId = cpu_to_le64(persistent_id);
ci_rsp->ChunksWritten = ci_rsp->ChunksWritten =
cpu_to_le32(ksmbd_server_side_copy_max_chunk_count()); cpu_to_le32(ksmbd_server_side_copy_max_chunk_count());
ci_rsp->ChunkBytesWritten = ci_rsp->ChunkBytesWritten =
...@@ -7050,12 +7099,13 @@ static int fsctl_copychunk(struct ksmbd_work *work, struct smb2_ioctl_req *req, ...@@ -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]; chunks = (struct srv_copychunk *)&ci_req->Chunks[0];
chunk_count = le32_to_cpu(ci_req->ChunkCount); chunk_count = le32_to_cpu(ci_req->ChunkCount);
if (chunk_count == 0)
goto out;
total_size_written = 0; total_size_written = 0;
/* verify the SRV_COPYCHUNK_COPY packet */ /* verify the SRV_COPYCHUNK_COPY packet */
if (chunk_count > ksmbd_server_side_copy_max_chunk_count() || if (chunk_count > ksmbd_server_side_copy_max_chunk_count() ||
le32_to_cpu(req->InputCount) < input_count < offsetof(struct copychunk_ioctl_req, Chunks) +
offsetof(struct copychunk_ioctl_req, Chunks) +
chunk_count * sizeof(struct srv_copychunk)) { chunk_count * sizeof(struct srv_copychunk)) {
rsp->hdr.Status = STATUS_INVALID_PARAMETER; rsp->hdr.Status = STATUS_INVALID_PARAMETER;
return -EINVAL; return -EINVAL;
...@@ -7076,9 +7126,7 @@ static int fsctl_copychunk(struct ksmbd_work *work, struct smb2_ioctl_req *req, ...@@ -7076,9 +7126,7 @@ static int fsctl_copychunk(struct ksmbd_work *work, struct smb2_ioctl_req *req,
src_fp = ksmbd_lookup_foreign_fd(work, src_fp = ksmbd_lookup_foreign_fd(work,
le64_to_cpu(ci_req->ResumeKey[0])); le64_to_cpu(ci_req->ResumeKey[0]));
dst_fp = ksmbd_lookup_fd_slow(work, dst_fp = ksmbd_lookup_fd_slow(work, volatile_id, persistent_id);
le64_to_cpu(req->VolatileFileId),
le64_to_cpu(req->PersistentFileId));
ret = -EINVAL; ret = -EINVAL;
if (!src_fp || if (!src_fp ||
src_fp->persistent_id != le64_to_cpu(ci_req->ResumeKey[1])) { src_fp->persistent_id != le64_to_cpu(ci_req->ResumeKey[1])) {
...@@ -7153,8 +7201,8 @@ static __be32 idev_ipv4_address(struct in_device *idev) ...@@ -7153,8 +7201,8 @@ static __be32 idev_ipv4_address(struct in_device *idev)
} }
static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, 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; struct network_interface_info_ioctl_rsp *nii_rsp = NULL;
int nbytes = 0; int nbytes = 0;
...@@ -7166,6 +7214,12 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, ...@@ -7166,6 +7214,12 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn,
rtnl_lock(); rtnl_lock();
for_each_netdev(&init_net, netdev) { 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) if (netdev->type == ARPHRD_LOOPBACK)
continue; continue;
...@@ -7245,11 +7299,6 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, ...@@ -7245,11 +7299,6 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn,
if (nii_rsp) if (nii_rsp)
nii_rsp->Next = 0; nii_rsp->Next = 0;
if (!nbytes) {
rsp->hdr.Status = STATUS_BUFFER_TOO_SMALL;
return -EINVAL;
}
rsp->PersistentFileId = cpu_to_le64(SMB2_NO_FID); rsp->PersistentFileId = cpu_to_le64(SMB2_NO_FID);
rsp->VolatileFileId = cpu_to_le64(SMB2_NO_FID); rsp->VolatileFileId = cpu_to_le64(SMB2_NO_FID);
return nbytes; return nbytes;
...@@ -7257,11 +7306,16 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, ...@@ -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, static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn,
struct validate_negotiate_info_req *neg_req, 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 ret = 0;
int dialect; 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, dialect = ksmbd_lookup_dialect_by_id(neg_req->Dialects,
neg_req->DialectCount); neg_req->DialectCount);
if (dialect == BAD_PROT_ID || dialect != conn->dialect) { if (dialect == BAD_PROT_ID || dialect != conn->dialect) {
...@@ -7295,7 +7349,7 @@ static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn, ...@@ -7295,7 +7349,7 @@ static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn,
static int fsctl_query_allocated_ranges(struct ksmbd_work *work, u64 id, 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_req,
struct file_allocated_range_buffer *qar_rsp, 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; struct ksmbd_file *fp;
loff_t start, length; loff_t start, length;
...@@ -7322,7 +7376,8 @@ static int fsctl_query_allocated_ranges(struct ksmbd_work *work, u64 id, ...@@ -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, 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 smb2_ioctl_rsp *rsp)
{ {
struct ksmbd_rpc_command *rpc_resp; struct ksmbd_rpc_command *rpc_resp;
...@@ -7436,8 +7491,7 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -7436,8 +7491,7 @@ int smb2_ioctl(struct ksmbd_work *work)
{ {
struct smb2_ioctl_req *req; struct smb2_ioctl_req *req;
struct smb2_ioctl_rsp *rsp, *rsp_org; struct smb2_ioctl_rsp *rsp, *rsp_org;
int cnt_code, nbytes = 0; unsigned int cnt_code, nbytes = 0, out_buf_len, in_buf_len;
int out_buf_len;
u64 id = KSMBD_NO_FID; u64 id = KSMBD_NO_FID;
struct ksmbd_conn *conn = work->conn; struct ksmbd_conn *conn = work->conn;
int ret = 0; int ret = 0;
...@@ -7465,8 +7519,14 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -7465,8 +7519,14 @@ int smb2_ioctl(struct ksmbd_work *work)
} }
cnt_code = le32_to_cpu(req->CntCode); cnt_code = le32_to_cpu(req->CntCode);
out_buf_len = le32_to_cpu(req->MaxOutputResponse); ret = smb2_calc_max_out_buf_len(work, 48,
out_buf_len = min(KSMBD_IPC_MAX_PAYLOAD, out_buf_len); 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) { switch (cnt_code) {
case FSCTL_DFS_GET_REFERRALS: case FSCTL_DFS_GET_REFERRALS:
...@@ -7494,6 +7554,7 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -7494,6 +7554,7 @@ int smb2_ioctl(struct ksmbd_work *work)
break; break;
} }
case FSCTL_PIPE_TRANSCEIVE: 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); nbytes = fsctl_pipe_transceive(work, id, out_buf_len, req, rsp);
break; break;
case FSCTL_VALIDATE_NEGOTIATE_INFO: case FSCTL_VALIDATE_NEGOTIATE_INFO:
...@@ -7502,9 +7563,16 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -7502,9 +7563,16 @@ int smb2_ioctl(struct ksmbd_work *work)
goto out; 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, ret = fsctl_validate_negotiate_info(conn,
(struct validate_negotiate_info_req *)&req->Buffer[0], (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) if (ret < 0)
goto out; goto out;
...@@ -7513,9 +7581,10 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -7513,9 +7581,10 @@ int smb2_ioctl(struct ksmbd_work *work)
rsp->VolatileFileId = cpu_to_le64(SMB2_NO_FID); rsp->VolatileFileId = cpu_to_le64(SMB2_NO_FID);
break; break;
case FSCTL_QUERY_NETWORK_INTERFACE_INFO: case FSCTL_QUERY_NETWORK_INTERFACE_INFO:
nbytes = fsctl_query_iface_info_ioctl(conn, req, rsp); ret = fsctl_query_iface_info_ioctl(conn, rsp, out_buf_len);
if (nbytes < 0) if (ret < 0)
goto out; goto out;
nbytes = ret;
break; break;
case FSCTL_REQUEST_RESUME_KEY: case FSCTL_REQUEST_RESUME_KEY:
if (out_buf_len < sizeof(struct resume_key_ioctl_rsp)) { if (out_buf_len < sizeof(struct resume_key_ioctl_rsp)) {
...@@ -7540,15 +7609,33 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -7540,15 +7609,33 @@ int smb2_ioctl(struct ksmbd_work *work)
goto out; goto out;
} }
if (in_buf_len < sizeof(struct copychunk_ioctl_req)) {
ret = -EINVAL;
goto out;
}
if (out_buf_len < sizeof(struct copychunk_ioctl_rsp)) { if (out_buf_len < sizeof(struct copychunk_ioctl_rsp)) {
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
nbytes = sizeof(struct copychunk_ioctl_rsp); 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; break;
case FSCTL_SET_SPARSE: case FSCTL_SET_SPARSE:
if (in_buf_len < sizeof(struct file_sparse)) {
ret = -EINVAL;
goto out;
}
ret = fsctl_set_sparse(work, id, ret = fsctl_set_sparse(work, id,
(struct file_sparse *)&req->Buffer[0]); (struct file_sparse *)&req->Buffer[0]);
if (ret < 0) if (ret < 0)
...@@ -7567,6 +7654,11 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -7567,6 +7654,11 @@ int smb2_ioctl(struct ksmbd_work *work)
goto out; goto out;
} }
if (in_buf_len < sizeof(struct file_zero_data_information)) {
ret = -EINVAL;
goto out;
}
zero_data = zero_data =
(struct file_zero_data_information *)&req->Buffer[0]; (struct file_zero_data_information *)&req->Buffer[0];
...@@ -7586,6 +7678,11 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -7586,6 +7678,11 @@ int smb2_ioctl(struct ksmbd_work *work)
break; break;
} }
case FSCTL_QUERY_ALLOCATED_RANGES: 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, ret = fsctl_query_allocated_ranges(work, id,
(struct file_allocated_range_buffer *)&req->Buffer[0], (struct file_allocated_range_buffer *)&req->Buffer[0],
(struct file_allocated_range_buffer *)&rsp->Buffer[0], (struct file_allocated_range_buffer *)&rsp->Buffer[0],
...@@ -7626,6 +7723,11 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -7626,6 +7723,11 @@ int smb2_ioctl(struct ksmbd_work *work)
struct duplicate_extents_to_file *dup_ext; struct duplicate_extents_to_file *dup_ext;
loff_t src_off, dst_off, length, cloned; 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]; dup_ext = (struct duplicate_extents_to_file *)&req->Buffer[0];
fp_in = ksmbd_lookup_fd_slow(work, dup_ext->VolatileFileHandle, fp_in = ksmbd_lookup_fd_slow(work, dup_ext->VolatileFileHandle,
...@@ -7696,6 +7798,8 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -7696,6 +7798,8 @@ int smb2_ioctl(struct ksmbd_work *work)
rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND;
else if (ret == -EOPNOTSUPP) else if (ret == -EOPNOTSUPP)
rsp->hdr.Status = STATUS_NOT_SUPPORTED; 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) else if (ret < 0 || rsp->hdr.Status == 0)
rsp->hdr.Status = STATUS_INVALID_PARAMETER; rsp->hdr.Status = STATUS_INVALID_PARAMETER;
smb2_set_err_rsp(work); smb2_set_err_rsp(work);
......
...@@ -113,6 +113,8 @@ ...@@ -113,6 +113,8 @@
#define SMB21_DEFAULT_IOSIZE (1024 * 1024) #define SMB21_DEFAULT_IOSIZE (1024 * 1024)
#define SMB3_DEFAULT_IOSIZE (4 * 1024 * 1024) #define SMB3_DEFAULT_IOSIZE (4 * 1024 * 1024)
#define SMB3_DEFAULT_TRANS_SIZE (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 * SMB2 Header Definition
......
...@@ -601,7 +601,7 @@ int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id, ...@@ -601,7 +601,7 @@ int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id,
return ret; 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_ipc_msg *msg;
struct ksmbd_logout_request *req; struct ksmbd_logout_request *req;
...@@ -616,6 +616,7 @@ int ksmbd_ipc_logout_request(const char *account) ...@@ -616,6 +616,7 @@ int ksmbd_ipc_logout_request(const char *account)
msg->type = KSMBD_EVENT_LOGOUT_REQUEST; msg->type = KSMBD_EVENT_LOGOUT_REQUEST;
req = (struct ksmbd_logout_request *)msg->payload; req = (struct ksmbd_logout_request *)msg->payload;
req->account_flags = flags;
strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ);
ret = ipc_msg_send(msg); ret = ipc_msg_send(msg);
......
...@@ -25,7 +25,7 @@ ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, ...@@ -25,7 +25,7 @@ ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess,
struct sockaddr *peer_addr); struct sockaddr *peer_addr);
int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id, int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id,
unsigned long long connect_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 * struct ksmbd_share_config_response *
ksmbd_ipc_share_config_request(const char *name); ksmbd_ipc_share_config_request(const char *name);
struct ksmbd_spnego_authen_response * struct ksmbd_spnego_authen_response *
......
...@@ -549,6 +549,10 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) ...@@ -549,6 +549,10 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
switch (recvmsg->type) { switch (recvmsg->type) {
case SMB_DIRECT_MSG_NEGOTIATE_REQ: 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->negotiation_requested = true;
t->full_packet_received = true; t->full_packet_received = true;
wake_up_interruptible(&t->wait_status); wake_up_interruptible(&t->wait_status);
...@@ -556,10 +560,23 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) ...@@ -556,10 +560,23 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
case SMB_DIRECT_MSG_DATA_TRANSFER: { case SMB_DIRECT_MSG_DATA_TRANSFER: {
struct smb_direct_data_transfer *data_transfer = struct smb_direct_data_transfer *data_transfer =
(struct smb_direct_data_transfer *)recvmsg->packet; (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; 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 (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) if (t->full_packet_received)
recvmsg->first_segment = true; recvmsg->first_segment = true;
...@@ -568,7 +585,7 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) ...@@ -568,7 +585,7 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
else else
t->full_packet_received = true; 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); wake_up_interruptible(&t->wait_reassembly_queue);
spin_lock(&t->receive_credit_lock); spin_lock(&t->receive_credit_lock);
......
...@@ -1023,7 +1023,7 @@ int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -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, int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length,
struct file_allocated_range_buffer *ranges, 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 file *f = fp->filp;
struct inode *inode = file_inode(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, ...@@ -166,7 +166,7 @@ int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp,
struct file_allocated_range_buffer; struct file_allocated_range_buffer;
int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length,
struct file_allocated_range_buffer *ranges, 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, int ksmbd_vfs_unlink(struct user_namespace *user_ns,
struct dentry *dir, struct dentry *dentry); struct dentry *dir, struct dentry *dentry);
void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat); void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment