Skip to content

Commit

Permalink
Merge tag '5.15-rc6-ksmbd-fixes' of git://git.samba.org/ksmbd
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Linus Torvalds committed Oct 24, 2021
2 parents 0f386a6 + 0d994cd commit c460e78
Show file tree
Hide file tree
Showing 14 changed files with 306 additions and 153 deletions.
16 changes: 9 additions & 7 deletions fs/ksmbd/auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand All @@ -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);

Expand Down
2 changes: 2 additions & 0 deletions fs/ksmbd/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions fs/ksmbd/ksmbd_netlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

/*
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion fs/ksmbd/mgmt/user_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions fs/ksmbd/mgmt/user_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
55 changes: 37 additions & 18 deletions fs/ksmbd/smb2misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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)
Expand Down Expand Up @@ -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.
Expand All @@ -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,
Expand All @@ -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;
}

Expand Down
3 changes: 3 additions & 0 deletions fs/ksmbd/smb2ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down
Loading

0 comments on commit c460e78

Please sign in to comment.