Skip to content

Commit fda9522

Browse files
manizadasmfrench
authored andcommitted
ksmbd: fix OOB write in QUERY_INFO for compound requests
When a compound request such as READ + QUERY_INFO(Security) is received, and the first command (READ) consumes most of the response buffer, ksmbd could write beyond the allocated buffer while building a security descriptor. The root cause was that smb2_get_info_sec() checked buffer space using ppntsd_size from xattr, while build_sec_desc() often synthesized a significantly larger descriptor from POSIX ACLs. This patch introduces smb_acl_sec_desc_scratch_len() to accurately compute the final descriptor size beforehand, performs proper buffer checking with smb2_calc_max_out_buf_len(), and uses exact-sized allocation + iov pinning. Cc: stable@vger.kernel.org Fixes: e2b76ab ("ksmbd: add support for read compound") Signed-off-by: Asim Viladi Oglu Manizada <manizada@pm.me> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com>
1 parent 7aaa804 commit fda9522

3 files changed

Lines changed: 134 additions & 32 deletions

File tree

fs/smb/server/smb2pdu.c

Lines changed: 89 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3402,20 +3402,24 @@ int smb2_open(struct ksmbd_work *work)
34023402
KSMBD_SHARE_FLAG_ACL_XATTR)) {
34033403
struct smb_fattr fattr;
34043404
struct smb_ntsd *pntsd;
3405-
int pntsd_size, ace_num = 0;
3405+
int pntsd_size;
3406+
size_t scratch_len;
34063407

34073408
ksmbd_acls_fattr(&fattr, idmap, inode);
3408-
if (fattr.cf_acls)
3409-
ace_num = fattr.cf_acls->a_count;
3410-
if (fattr.cf_dacls)
3411-
ace_num += fattr.cf_dacls->a_count;
3412-
3413-
pntsd = kmalloc(sizeof(struct smb_ntsd) +
3414-
sizeof(struct smb_sid) * 3 +
3415-
sizeof(struct smb_acl) +
3416-
sizeof(struct smb_ace) * ace_num * 2,
3417-
KSMBD_DEFAULT_GFP);
3409+
scratch_len = smb_acl_sec_desc_scratch_len(&fattr,
3410+
NULL, 0,
3411+
OWNER_SECINFO | GROUP_SECINFO |
3412+
DACL_SECINFO);
3413+
if (!scratch_len || scratch_len == SIZE_MAX) {
3414+
rc = -EFBIG;
3415+
posix_acl_release(fattr.cf_acls);
3416+
posix_acl_release(fattr.cf_dacls);
3417+
goto err_out;
3418+
}
3419+
3420+
pntsd = kvzalloc(scratch_len, KSMBD_DEFAULT_GFP);
34183421
if (!pntsd) {
3422+
rc = -ENOMEM;
34193423
posix_acl_release(fattr.cf_acls);
34203424
posix_acl_release(fattr.cf_dacls);
34213425
goto err_out;
@@ -3430,7 +3434,7 @@ int smb2_open(struct ksmbd_work *work)
34303434
posix_acl_release(fattr.cf_acls);
34313435
posix_acl_release(fattr.cf_dacls);
34323436
if (rc) {
3433-
kfree(pntsd);
3437+
kvfree(pntsd);
34343438
goto err_out;
34353439
}
34363440

@@ -3440,7 +3444,7 @@ int smb2_open(struct ksmbd_work *work)
34403444
pntsd,
34413445
pntsd_size,
34423446
false);
3443-
kfree(pntsd);
3447+
kvfree(pntsd);
34443448
if (rc)
34453449
pr_err("failed to store ntacl in xattr : %d\n",
34463450
rc);
@@ -5372,8 +5376,9 @@ static int smb2_get_info_file(struct ksmbd_work *work,
53725376
if (test_share_config_flag(work->tcon->share_conf,
53735377
KSMBD_SHARE_FLAG_PIPE)) {
53745378
/* smb2 info file called for pipe */
5375-
return smb2_get_info_file_pipe(work->sess, req, rsp,
5379+
rc = smb2_get_info_file_pipe(work->sess, req, rsp,
53765380
work->response_buf);
5381+
goto iov_pin_out;
53775382
}
53785383

53795384
if (work->next_smb2_rcv_hdr_off) {
@@ -5473,6 +5478,12 @@ static int smb2_get_info_file(struct ksmbd_work *work,
54735478
rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
54745479
rsp, work->response_buf);
54755480
ksmbd_fd_put(work, fp);
5481+
5482+
iov_pin_out:
5483+
if (!rc)
5484+
rc = ksmbd_iov_pin_rsp(work, (void *)rsp,
5485+
offsetof(struct smb2_query_info_rsp, Buffer) +
5486+
le32_to_cpu(rsp->OutputBufferLength));
54765487
return rc;
54775488
}
54785489

@@ -5699,6 +5710,11 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
56995710
rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
57005711
rsp, work->response_buf);
57015712
path_put(&path);
5713+
5714+
if (!rc)
5715+
rc = ksmbd_iov_pin_rsp(work, (void *)rsp,
5716+
offsetof(struct smb2_query_info_rsp, Buffer) +
5717+
le32_to_cpu(rsp->OutputBufferLength));
57025718
return rc;
57035719
}
57045720

@@ -5708,20 +5724,26 @@ static int smb2_get_info_sec(struct ksmbd_work *work,
57085724
{
57095725
struct ksmbd_file *fp;
57105726
struct mnt_idmap *idmap;
5711-
struct smb_ntsd *pntsd = (struct smb_ntsd *)rsp->Buffer, *ppntsd = NULL;
5727+
struct smb_ntsd *pntsd = NULL, *ppntsd = NULL;
57125728
struct smb_fattr fattr = {{0}};
57135729
struct inode *inode;
57145730
__u32 secdesclen = 0;
57155731
unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID;
57165732
int addition_info = le32_to_cpu(req->AdditionalInformation);
5717-
int rc = 0, ppntsd_size = 0;
5733+
int rc = 0, ppntsd_size = 0, max_len;
5734+
size_t scratch_len = 0;
57185735

57195736
if (addition_info & ~(OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO |
57205737
PROTECTED_DACL_SECINFO |
57215738
UNPROTECTED_DACL_SECINFO)) {
57225739
ksmbd_debug(SMB, "Unsupported addition info: 0x%x)\n",
57235740
addition_info);
57245741

5742+
pntsd = kzalloc(ALIGN(sizeof(struct smb_ntsd), 8),
5743+
KSMBD_DEFAULT_GFP);
5744+
if (!pntsd)
5745+
return -ENOMEM;
5746+
57255747
pntsd->revision = cpu_to_le16(1);
57265748
pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PROTECTED);
57275749
pntsd->osidoffset = 0;
@@ -5730,9 +5752,7 @@ static int smb2_get_info_sec(struct ksmbd_work *work,
57305752
pntsd->dacloffset = 0;
57315753

57325754
secdesclen = sizeof(struct smb_ntsd);
5733-
rsp->OutputBufferLength = cpu_to_le32(secdesclen);
5734-
5735-
return 0;
5755+
goto iov_pin;
57365756
}
57375757

57385758
if (work->next_smb2_rcv_hdr_off) {
@@ -5764,18 +5784,58 @@ static int smb2_get_info_sec(struct ksmbd_work *work,
57645784
&ppntsd);
57655785

57665786
/* Check if sd buffer size exceeds response buffer size */
5767-
if (smb2_resp_buf_len(work, 8) > ppntsd_size)
5768-
rc = build_sec_desc(idmap, pntsd, ppntsd, ppntsd_size,
5769-
addition_info, &secdesclen, &fattr);
5787+
max_len = smb2_calc_max_out_buf_len(work,
5788+
offsetof(struct smb2_query_info_rsp, Buffer),
5789+
le32_to_cpu(req->OutputBufferLength));
5790+
if (max_len < 0) {
5791+
rc = -EINVAL;
5792+
goto release_acl;
5793+
}
5794+
5795+
scratch_len = smb_acl_sec_desc_scratch_len(&fattr, ppntsd,
5796+
ppntsd_size, addition_info);
5797+
if (!scratch_len || scratch_len == SIZE_MAX) {
5798+
rc = -EFBIG;
5799+
goto release_acl;
5800+
}
5801+
5802+
pntsd = kvzalloc(scratch_len, KSMBD_DEFAULT_GFP);
5803+
if (!pntsd) {
5804+
rc = -ENOMEM;
5805+
goto release_acl;
5806+
}
5807+
5808+
rc = build_sec_desc(idmap, pntsd, ppntsd, ppntsd_size,
5809+
addition_info, &secdesclen, &fattr);
5810+
5811+
release_acl:
57705812
posix_acl_release(fattr.cf_acls);
57715813
posix_acl_release(fattr.cf_dacls);
57725814
kfree(ppntsd);
57735815
ksmbd_fd_put(work, fp);
5816+
5817+
if (!rc && ALIGN(secdesclen, 8) > scratch_len)
5818+
rc = -EFBIG;
57745819
if (rc)
5775-
return rc;
5820+
goto err_out;
57765821

5822+
iov_pin:
57775823
rsp->OutputBufferLength = cpu_to_le32(secdesclen);
5778-
return 0;
5824+
rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
5825+
rsp, work->response_buf);
5826+
if (rc)
5827+
goto err_out;
5828+
5829+
rc = ksmbd_iov_pin_rsp_read(work, (void *)rsp,
5830+
offsetof(struct smb2_query_info_rsp, Buffer),
5831+
pntsd, secdesclen);
5832+
err_out:
5833+
if (rc) {
5834+
rsp->OutputBufferLength = 0;
5835+
kvfree(pntsd);
5836+
}
5837+
5838+
return rc;
57795839
}
57805840

57815841
/**
@@ -5799,6 +5859,9 @@ int smb2_query_info(struct ksmbd_work *work)
57995859
goto err_out;
58005860
}
58015861

5862+
rsp->StructureSize = cpu_to_le16(9);
5863+
rsp->OutputBufferOffset = cpu_to_le16(72);
5864+
58025865
switch (req->InfoType) {
58035866
case SMB2_O_INFO_FILE:
58045867
ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n");
@@ -5819,14 +5882,6 @@ int smb2_query_info(struct ksmbd_work *work)
58195882
}
58205883
ksmbd_revert_fsids(work);
58215884

5822-
if (!rc) {
5823-
rsp->StructureSize = cpu_to_le16(9);
5824-
rsp->OutputBufferOffset = cpu_to_le16(72);
5825-
rc = ksmbd_iov_pin_rsp(work, (void *)rsp,
5826-
offsetof(struct smb2_query_info_rsp, Buffer) +
5827-
le32_to_cpu(rsp->OutputBufferLength));
5828-
}
5829-
58305885
err_out:
58315886
if (rc < 0) {
58325887
if (rc == -EACCES)
@@ -5837,6 +5892,8 @@ int smb2_query_info(struct ksmbd_work *work)
58375892
rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR;
58385893
else if (rc == -ENOMEM)
58395894
rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
5895+
else if (rc == -EINVAL && rsp->hdr.Status == 0)
5896+
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
58405897
else if (rc == -EOPNOTSUPP || rsp->hdr.Status == 0)
58415898
rsp->hdr.Status = STATUS_INVALID_INFO_CLASS;
58425899
smb2_set_err_rsp(work);

fs/smb/server/smbacl.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -915,6 +915,49 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd,
915915
return 0;
916916
}
917917

918+
size_t smb_acl_sec_desc_scratch_len(struct smb_fattr *fattr,
919+
struct smb_ntsd *ppntsd, int ppntsd_size, int addition_info)
920+
{
921+
size_t len = sizeof(struct smb_ntsd);
922+
size_t tmp;
923+
924+
if (addition_info & OWNER_SECINFO)
925+
len += sizeof(struct smb_sid);
926+
if (addition_info & GROUP_SECINFO)
927+
len += sizeof(struct smb_sid);
928+
if (!(addition_info & DACL_SECINFO))
929+
return len;
930+
931+
len += sizeof(struct smb_acl);
932+
if (ppntsd && ppntsd_size > 0) {
933+
unsigned int dacl_offset = le32_to_cpu(ppntsd->dacloffset);
934+
935+
if (dacl_offset < ppntsd_size &&
936+
check_add_overflow(len, ppntsd_size - dacl_offset, &len))
937+
return 0;
938+
}
939+
940+
if (fattr->cf_acls) {
941+
if (check_mul_overflow((size_t)fattr->cf_acls->a_count,
942+
2 * sizeof(struct smb_ace), &tmp) ||
943+
check_add_overflow(len, tmp, &len))
944+
return 0;
945+
} else {
946+
/* default/minimum DACL */
947+
if (check_add_overflow(len, 5 * sizeof(struct smb_ace), &len))
948+
return 0;
949+
}
950+
951+
if (fattr->cf_dacls) {
952+
if (check_mul_overflow((size_t)fattr->cf_dacls->a_count,
953+
sizeof(struct smb_ace), &tmp) ||
954+
check_add_overflow(len, tmp, &len))
955+
return 0;
956+
}
957+
958+
return len;
959+
}
960+
918961
/* Convert permission bits from mode to equivalent CIFS ACL */
919962
int build_sec_desc(struct mnt_idmap *idmap,
920963
struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd,

fs/smb/server/smbacl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon,
101101
bool type_check, bool get_write);
102102
void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid);
103103
void ksmbd_init_domain(u32 *sub_auth);
104+
size_t smb_acl_sec_desc_scratch_len(struct smb_fattr *fattr,
105+
struct smb_ntsd *ppntsd, int ppntsd_size, int addition_info);
104106

105107
static inline uid_t posix_acl_uid_translate(struct mnt_idmap *idmap,
106108
struct posix_acl_entry *pace)

0 commit comments

Comments
 (0)