Skip to content

Commit c460e78

Browse files
committed
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
2 parents 0f386a6 + 0d994cd commit c460e78

File tree

14 files changed

+306
-153
lines changed

14 files changed

+306
-153
lines changed

fs/ksmbd/auth.c

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -298,8 +298,8 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob,
298298
int blob_len, struct ksmbd_session *sess)
299299
{
300300
char *domain_name;
301-
unsigned int lm_off, nt_off;
302-
unsigned short nt_len;
301+
unsigned int nt_off, dn_off;
302+
unsigned short nt_len, dn_len;
303303
int ret;
304304

305305
if (blob_len < sizeof(struct authenticate_message)) {
@@ -314,15 +314,17 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob,
314314
return -EINVAL;
315315
}
316316

317-
lm_off = le32_to_cpu(authblob->LmChallengeResponse.BufferOffset);
318317
nt_off = le32_to_cpu(authblob->NtChallengeResponse.BufferOffset);
319318
nt_len = le16_to_cpu(authblob->NtChallengeResponse.Length);
319+
dn_off = le32_to_cpu(authblob->DomainName.BufferOffset);
320+
dn_len = le16_to_cpu(authblob->DomainName.Length);
321+
322+
if (blob_len < (u64)dn_off + dn_len || blob_len < (u64)nt_off + nt_len)
323+
return -EINVAL;
320324

321325
/* TODO : use domain name that imported from configuration file */
322-
domain_name = smb_strndup_from_utf16((const char *)authblob +
323-
le32_to_cpu(authblob->DomainName.BufferOffset),
324-
le16_to_cpu(authblob->DomainName.Length), true,
325-
sess->conn->local_nls);
326+
domain_name = smb_strndup_from_utf16((const char *)authblob + dn_off,
327+
dn_len, true, sess->conn->local_nls);
326328
if (IS_ERR(domain_name))
327329
return PTR_ERR(domain_name);
328330

fs/ksmbd/connection.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)
6161
conn->local_nls = load_nls_default();
6262
atomic_set(&conn->req_running, 0);
6363
atomic_set(&conn->r_count, 0);
64+
conn->total_credits = 1;
65+
6466
init_waitqueue_head(&conn->req_running_q);
6567
INIT_LIST_HEAD(&conn->conns_list);
6668
INIT_LIST_HEAD(&conn->sessions);

fs/ksmbd/ksmbd_netlink.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ struct ksmbd_tree_disconnect_request {
211211
*/
212212
struct ksmbd_logout_request {
213213
__s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */
214+
__u32 account_flags;
214215
};
215216

216217
/*
@@ -317,6 +318,7 @@ enum KSMBD_TREE_CONN_STATUS {
317318
#define KSMBD_USER_FLAG_BAD_UID BIT(2)
318319
#define KSMBD_USER_FLAG_BAD_USER BIT(3)
319320
#define KSMBD_USER_FLAG_GUEST_ACCOUNT BIT(4)
321+
#define KSMBD_USER_FLAG_DELAY_SESSION BIT(5)
320322

321323
/*
322324
* Share config flags.

fs/ksmbd/mgmt/user_config.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp)
5555

5656
void ksmbd_free_user(struct ksmbd_user *user)
5757
{
58-
ksmbd_ipc_logout_request(user->name);
58+
ksmbd_ipc_logout_request(user->name, user->flags);
5959
kfree(user->name);
6060
kfree(user->passkey);
6161
kfree(user);

fs/ksmbd/mgmt/user_config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ struct ksmbd_user {
1818

1919
size_t passkey_sz;
2020
char *passkey;
21+
unsigned int failed_login_count;
2122
};
2223

2324
static inline bool user_guest(struct ksmbd_user *user)

fs/ksmbd/smb2misc.c

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -284,11 +284,13 @@ static inline int smb2_ioctl_resp_len(struct smb2_ioctl_req *h)
284284
le32_to_cpu(h->MaxOutputResponse);
285285
}
286286

287-
static int smb2_validate_credit_charge(struct smb2_hdr *hdr)
287+
static int smb2_validate_credit_charge(struct ksmbd_conn *conn,
288+
struct smb2_hdr *hdr)
288289
{
289-
int req_len = 0, expect_resp_len = 0, calc_credit_num, max_len;
290-
int credit_charge = le16_to_cpu(hdr->CreditCharge);
290+
unsigned int req_len = 0, expect_resp_len = 0, calc_credit_num, max_len;
291+
unsigned short credit_charge = le16_to_cpu(hdr->CreditCharge);
291292
void *__hdr = hdr;
293+
int ret;
292294

293295
switch (hdr->Command) {
294296
case SMB2_QUERY_INFO:
@@ -310,21 +312,37 @@ static int smb2_validate_credit_charge(struct smb2_hdr *hdr)
310312
req_len = smb2_ioctl_req_len(__hdr);
311313
expect_resp_len = smb2_ioctl_resp_len(__hdr);
312314
break;
313-
default:
315+
case SMB2_CANCEL:
314316
return 0;
317+
default:
318+
req_len = 1;
319+
break;
315320
}
316321

317-
credit_charge = max(1, credit_charge);
318-
max_len = max(req_len, expect_resp_len);
322+
credit_charge = max_t(unsigned short, credit_charge, 1);
323+
max_len = max_t(unsigned int, req_len, expect_resp_len);
319324
calc_credit_num = DIV_ROUND_UP(max_len, SMB2_MAX_BUFFER_SIZE);
320325

321326
if (credit_charge < calc_credit_num) {
322-
pr_err("Insufficient credit charge, given: %d, needed: %d\n",
323-
credit_charge, calc_credit_num);
327+
ksmbd_debug(SMB, "Insufficient credit charge, given: %d, needed: %d\n",
328+
credit_charge, calc_credit_num);
329+
return 1;
330+
} else if (credit_charge > conn->max_credits) {
331+
ksmbd_debug(SMB, "Too large credit charge: %d\n", credit_charge);
324332
return 1;
325333
}
326334

327-
return 0;
335+
spin_lock(&conn->credits_lock);
336+
if (credit_charge <= conn->total_credits) {
337+
conn->total_credits -= credit_charge;
338+
ret = 0;
339+
} else {
340+
ksmbd_debug(SMB, "Insufficient credits granted, given: %u, granted: %u\n",
341+
credit_charge, conn->total_credits);
342+
ret = 1;
343+
}
344+
spin_unlock(&conn->credits_lock);
345+
return ret;
328346
}
329347

330348
int ksmbd_smb2_check_message(struct ksmbd_work *work)
@@ -382,26 +400,20 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work)
382400
}
383401
}
384402

385-
if ((work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) &&
386-
smb2_validate_credit_charge(hdr)) {
387-
work->conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER);
388-
return 1;
389-
}
390-
391403
if (smb2_calc_size(hdr, &clc_len))
392404
return 1;
393405

394406
if (len != clc_len) {
395407
/* client can return one byte more due to implied bcc[0] */
396408
if (clc_len == len + 1)
397-
return 0;
409+
goto validate_credit;
398410

399411
/*
400412
* Some windows servers (win2016) will pad also the final
401413
* PDU in a compound to 8 bytes.
402414
*/
403415
if (ALIGN(clc_len, 8) == len)
404-
return 0;
416+
goto validate_credit;
405417

406418
/*
407419
* windows client also pad up to 8 bytes when compounding.
@@ -414,7 +426,7 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work)
414426
"cli req padded more than expected. Length %d not %d for cmd:%d mid:%llu\n",
415427
len, clc_len, command,
416428
le64_to_cpu(hdr->MessageId));
417-
return 0;
429+
goto validate_credit;
418430
}
419431

420432
ksmbd_debug(SMB,
@@ -425,6 +437,13 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work)
425437
return 1;
426438
}
427439

440+
validate_credit:
441+
if ((work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) &&
442+
smb2_validate_credit_charge(work->conn, hdr)) {
443+
work->conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER);
444+
return 1;
445+
}
446+
428447
return 0;
429448
}
430449

fs/ksmbd/smb2ops.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ int init_smb3_11_server(struct ksmbd_conn *conn)
284284

285285
void init_smb2_max_read_size(unsigned int sz)
286286
{
287+
sz = clamp_val(sz, SMB3_MIN_IOSIZE, SMB3_MAX_IOSIZE);
287288
smb21_server_values.max_read_size = sz;
288289
smb30_server_values.max_read_size = sz;
289290
smb302_server_values.max_read_size = sz;
@@ -292,6 +293,7 @@ void init_smb2_max_read_size(unsigned int sz)
292293

293294
void init_smb2_max_write_size(unsigned int sz)
294295
{
296+
sz = clamp_val(sz, SMB3_MIN_IOSIZE, SMB3_MAX_IOSIZE);
295297
smb21_server_values.max_write_size = sz;
296298
smb30_server_values.max_write_size = sz;
297299
smb302_server_values.max_write_size = sz;
@@ -300,6 +302,7 @@ void init_smb2_max_write_size(unsigned int sz)
300302

301303
void init_smb2_max_trans_size(unsigned int sz)
302304
{
305+
sz = clamp_val(sz, SMB3_MIN_IOSIZE, SMB3_MAX_IOSIZE);
303306
smb21_server_values.max_trans_size = sz;
304307
smb30_server_values.max_trans_size = sz;
305308
smb302_server_values.max_trans_size = sz;

0 commit comments

Comments
 (0)