Skip to content

Commit cf42a00

Browse files
manizadanamjaejeon
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: e2b76ab8b5c9 ("ksmbd: add support for read compound") Signed-off-by: Asim Viladi Oglu Manizada <manizada@pm.me> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
1 parent 55bc056 commit cf42a00

3 files changed

Lines changed: 127 additions & 33 deletions

File tree

smb2pdu.c

Lines changed: 82 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3539,23 +3539,20 @@ int smb2_open(struct ksmbd_work *work)
35393539
KSMBD_SHARE_FLAG_ACL_XATTR)) {
35403540
struct smb_fattr fattr;
35413541
struct smb_ntsd *pntsd;
3542-
int pntsd_size, ace_num = 0;
3542+
int pntsd_size;
3543+
size_t scratch_len;
35433544

35443545
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
35453546
ksmbd_acls_fattr(&fattr, idmap, inode);
35463547
#else
35473548
ksmbd_acls_fattr(&fattr, user_ns, inode);
35483549
#endif
3549-
if (fattr.cf_acls)
3550-
ace_num = fattr.cf_acls->a_count;
3551-
if (fattr.cf_dacls)
3552-
ace_num += fattr.cf_dacls->a_count;
3553-
3554-
pntsd = kmalloc(sizeof(struct smb_ntsd) +
3555-
sizeof(struct smb_sid) * 3 +
3556-
sizeof(struct smb_acl) +
3557-
sizeof(struct smb_ace) * ace_num * 2,
3558-
KSMBD_DEFAULT_GFP);
3550+
scratch_len = smb_acl_sec_desc_scratch_len(&fattr,
3551+
NULL, 0,
3552+
OWNER_SECINFO | GROUP_SECINFO |
3553+
DACL_SECINFO);
3554+
3555+
pntsd = kvmalloc(scratch_len, KSMBD_DEFAULT_GFP);
35593556
if (!pntsd) {
35603557
posix_acl_release(fattr.cf_acls);
35613558
posix_acl_release(fattr.cf_dacls);
@@ -3575,7 +3572,7 @@ int smb2_open(struct ksmbd_work *work)
35753572
posix_acl_release(fattr.cf_acls);
35763573
posix_acl_release(fattr.cf_dacls);
35773574
if (rc) {
3578-
kfree(pntsd);
3575+
kvfree(pntsd);
35793576
goto err_out;
35803577
}
35813578

@@ -3589,7 +3586,7 @@ int smb2_open(struct ksmbd_work *work)
35893586
pntsd,
35903587
pntsd_size,
35913588
false);
3592-
kfree(pntsd);
3589+
kvfree(pntsd);
35933590
if (rc)
35943591
pr_err("failed to store ntacl in xattr : %d\n",
35953592
rc);
@@ -5638,8 +5635,9 @@ static int smb2_get_info_file(struct ksmbd_work *work,
56385635
if (test_share_config_flag(work->tcon->share_conf,
56395636
KSMBD_SHARE_FLAG_PIPE)) {
56405637
/* smb2 info file called for pipe */
5641-
return smb2_get_info_file_pipe(work->sess, req, rsp,
5638+
rc = smb2_get_info_file_pipe(work->sess, req, rsp,
56425639
work->response_buf);
5640+
goto iov_pin_out;
56435641
}
56445642

56455643
if (work->next_smb2_rcv_hdr_off) {
@@ -5739,6 +5737,12 @@ static int smb2_get_info_file(struct ksmbd_work *work,
57395737
rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
57405738
rsp, work->response_buf);
57415739
ksmbd_fd_put(work, fp);
5740+
5741+
iov_pin_out:
5742+
if (!rc)
5743+
rc = ksmbd_iov_pin_rsp(work, (void *)rsp,
5744+
offsetof(struct smb2_query_info_rsp, Buffer) +
5745+
le32_to_cpu(rsp->OutputBufferLength));
57425746
return rc;
57435747
}
57445748

@@ -5958,6 +5962,11 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
59585962
rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
59595963
rsp, work->response_buf);
59605964
path_put(&path);
5965+
5966+
if (!rc)
5967+
rc = ksmbd_iov_pin_rsp(work, (void *)rsp,
5968+
offsetof(struct smb2_query_info_rsp, Buffer) +
5969+
le32_to_cpu(rsp->OutputBufferLength));
59615970
return rc;
59625971
}
59635972

@@ -5971,20 +5980,29 @@ static int smb2_get_info_sec(struct ksmbd_work *work,
59715980
#else
59725981
struct user_namespace *user_ns;
59735982
#endif
5974-
struct smb_ntsd *pntsd = (struct smb_ntsd *)rsp->Buffer, *ppntsd = NULL;
5983+
struct smb_ntsd *pntsd = NULL, *ppntsd = NULL;
59755984
struct smb_fattr fattr = {{0}};
59765985
struct inode *inode;
59775986
__u32 secdesclen = 0;
59785987
unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID;
59795988
int addition_info = le32_to_cpu(req->AdditionalInformation);
5980-
int rc = 0, ppntsd_size = 0;
5989+
int rc = 0, ppntsd_size = 0, max_len;
5990+
size_t scratch_len;
59815991

59825992
if (addition_info & ~(OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO |
59835993
PROTECTED_DACL_SECINFO |
59845994
UNPROTECTED_DACL_SECINFO)) {
59855995
ksmbd_debug(SMB, "Unsupported addition info: 0x%x)\n",
59865996
addition_info);
59875997

5998+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
5999+
pntsd = kmalloc_obj(struct smb_ntsd, KSMBD_DEFAULT_GFP);
6000+
#else
6001+
pntsd = kmalloc(sizeof(struct smb_ntsd), KSMBD_DEFAULT_GFP);
6002+
#endif
6003+
if (!pntsd)
6004+
return -ENOMEM;
6005+
59886006
pntsd->revision = cpu_to_le16(1);
59896007
pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PROTECTED);
59906008
pntsd->osidoffset = 0;
@@ -5993,9 +6011,7 @@ static int smb2_get_info_sec(struct ksmbd_work *work,
59936011
pntsd->dacloffset = 0;
59946012

59956013
secdesclen = sizeof(struct smb_ntsd);
5996-
rsp->OutputBufferLength = cpu_to_le32(secdesclen);
5997-
5998-
return 0;
6014+
goto iov_pin;
59996015
}
60006016

60016017
if (work->next_smb2_rcv_hdr_off) {
@@ -6038,23 +6054,59 @@ static int smb2_get_info_sec(struct ksmbd_work *work,
60386054
fp->filp->f_path.dentry,
60396055
&ppntsd);
60406056

6057+
max_len = smb2_calc_max_out_buf_len(work,
6058+
offsetof(struct smb2_query_info_rsp, Buffer),
6059+
le32_to_cpu(req->OutputBufferLength));
6060+
if (max_len < 0) {
6061+
rc = -EINVAL;
6062+
goto out;
6063+
}
6064+
6065+
scratch_len = smb_acl_sec_desc_scratch_len(&fattr, ppntsd,
6066+
ppntsd_size, addition_info);
6067+
pntsd = kvzalloc(scratch_len, KSMBD_DEFAULT_GFP);
6068+
if (!pntsd) {
6069+
rc = -ENOMEM;
6070+
goto out;
6071+
}
6072+
60416073
/* Check if sd buffer size exceeds response buffer size */
6042-
if (smb2_resp_buf_len(work, 8) > ppntsd_size)
60436074
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
6044-
rc = build_sec_desc(idmap, pntsd, ppntsd, ppntsd_size,
6075+
rc = build_sec_desc(idmap, pntsd, ppntsd, ppntsd_size,
6076+
addition_info, &secdesclen, &fattr);
60456077
#else
6046-
rc = build_sec_desc(user_ns, pntsd, ppntsd, ppntsd_size,
6078+
rc = build_sec_desc(user_ns, pntsd, ppntsd, ppntsd_size,
6079+
addition_info, &secdesclen, &fattr);
60476080
#endif
6048-
addition_info, &secdesclen, &fattr);
6081+
6082+
out:
60496083
posix_acl_release(fattr.cf_acls);
60506084
posix_acl_release(fattr.cf_dacls);
60516085
kfree(ppntsd);
60526086
ksmbd_fd_put(work, fp);
6053-
if (rc)
6087+
if (rc) {
6088+
kvfree(pntsd);
60546089
return rc;
6090+
}
60556091

6092+
iov_pin:
60566093
rsp->OutputBufferLength = cpu_to_le32(secdesclen);
6057-
return 0;
6094+
rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
6095+
rsp, work->response_buf);
6096+
if (rc) {
6097+
kvfree(pntsd);
6098+
return rc;
6099+
}
6100+
6101+
rc = ksmbd_iov_pin_rsp_read(work, (void *)rsp,
6102+
offsetof(struct smb2_query_info_rsp, Buffer),
6103+
pntsd, secdesclen);
6104+
if (rc) {
6105+
rsp->OutputBufferLength = 0;
6106+
kvfree(pntsd);
6107+
}
6108+
6109+
return rc;
60586110
}
60596111

60606112
/**
@@ -6078,6 +6130,9 @@ int smb2_query_info(struct ksmbd_work *work)
60786130
goto err_out;
60796131
}
60806132

6133+
rsp->StructureSize = cpu_to_le16(9);
6134+
rsp->OutputBufferOffset = cpu_to_le16(72);
6135+
60816136
switch (req->InfoType) {
60826137
case SMB2_O_INFO_FILE:
60836138
ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n");
@@ -6098,14 +6153,6 @@ int smb2_query_info(struct ksmbd_work *work)
60986153
}
60996154
ksmbd_revert_fsids(work);
61006155

6101-
if (!rc) {
6102-
rsp->StructureSize = cpu_to_le16(9);
6103-
rsp->OutputBufferOffset = cpu_to_le16(72);
6104-
rc = ksmbd_iov_pin_rsp(work, (void *)rsp,
6105-
offsetof(struct smb2_query_info_rsp, Buffer) +
6106-
le32_to_cpu(rsp->OutputBufferLength));
6107-
}
6108-
61096156
err_out:
61106157
if (rc < 0) {
61116158
if (rc == -EACCES)
@@ -6116,6 +6163,8 @@ int smb2_query_info(struct ksmbd_work *work)
61166163
rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR;
61176164
else if (rc == -ENOMEM)
61186165
rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
6166+
else if (rc == -EINVAL && rsp->hdr.Status == 0)
6167+
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
61196168
else if (rc == -EOPNOTSUPP || rsp->hdr.Status == 0)
61206169
rsp->hdr.Status = STATUS_INVALID_INFO_CLASS;
61216170
smb2_set_err_rsp(work);

smbacl.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,6 +1029,49 @@ int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd,
10291029
return 0;
10301030
}
10311031

1032+
size_t smb_acl_sec_desc_scratch_len(struct smb_fattr *fattr,
1033+
struct smb_ntsd *ppntsd, int ppntsd_size, int addition_info)
1034+
{
1035+
size_t len = sizeof(struct smb_ntsd);
1036+
size_t tmp;
1037+
1038+
if (addition_info & OWNER_SECINFO)
1039+
len += sizeof(struct smb_sid);
1040+
if (addition_info & GROUP_SECINFO)
1041+
len += sizeof(struct smb_sid);
1042+
if (!(addition_info & DACL_SECINFO))
1043+
return len;
1044+
1045+
len += sizeof(struct smb_acl);
1046+
if (ppntsd && ppntsd_size > 0) {
1047+
unsigned int dacl_offset = le32_to_cpu(ppntsd->dacloffset);
1048+
1049+
if (dacl_offset < ppntsd_size &&
1050+
check_add_overflow(len, ppntsd_size - dacl_offset, &len))
1051+
return -EFBIG;
1052+
}
1053+
1054+
if (fattr->cf_acls) {
1055+
if (check_mul_overflow((size_t)fattr->cf_acls->a_count,
1056+
2 * sizeof(struct smb_ace), &tmp) ||
1057+
check_add_overflow(len, tmp, &len))
1058+
return -EFBIG;
1059+
} else {
1060+
/* default/minimum DACL */
1061+
if (check_add_overflow(len, 5 * sizeof(struct smb_ace), &len))
1062+
return -EFBIG;
1063+
}
1064+
1065+
if (fattr->cf_dacls) {
1066+
if (check_mul_overflow((size_t)fattr->cf_dacls->a_count,
1067+
sizeof(struct smb_ace), &tmp) ||
1068+
check_add_overflow(len, tmp, &len))
1069+
return -EFBIG;
1070+
}
1071+
1072+
return len;
1073+
}
1074+
10321075
/* Convert permission bits from mode to equivalent CIFS ACL */
10331076
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
10341077
int build_sec_desc(struct mnt_idmap *idmap,

smbacl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,8 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon,
222222
bool type_check, bool get_write);
223223
void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid);
224224
void ksmbd_init_domain(u32 *sub_auth);
225+
size_t smb_acl_sec_desc_scratch_len(struct smb_fattr *fattr,
226+
struct smb_ntsd *ppntsd, int ppntsd_size, int addition_info);
225227

226228
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
227229
static inline uid_t posix_acl_uid_translate(struct mnt_idmap *idmap,

0 commit comments

Comments
 (0)