Skip to content

Commit ac478bb

Browse files
committed
ksmbd: fix heap-based overflow in set_ntacl_dacl()
The testcase use SMB2_SET_INFO_HE command to set a malformed file attribute under the label `security.NTACL`. SMB2_QUERY_INFO_HE command in testcase trigger the following overflow. [ 4712.003781] ================================================================== [ 4712.003790] BUG: KASAN: slab-out-of-bounds in build_sec_desc+0x842/0x1dd0 [ksmbd] [ 4712.003807] Write of size 1060 at addr ffff88801e34c068 by task kworker/0:0/4190 [ 4712.003813] CPU: 0 PID: 4190 Comm: kworker/0:0 Not tainted 5.19.0-rc5 #1 [ 4712.003850] Workqueue: ksmbd-io handle_ksmbd_work [ksmbd] [ 4712.003867] Call Trace: [ 4712.003870] <TASK> [ 4712.003873] dump_stack_lvl+0x49/0x5f [ 4712.003935] print_report.cold+0x5e/0x5cf [ 4712.003972] ? ksmbd_vfs_get_sd_xattr+0x16d/0x500 [ksmbd] [ 4712.003984] ? cmp_map_id+0x200/0x200 [ 4712.003988] ? build_sec_desc+0x842/0x1dd0 [ksmbd] [ 4712.004000] kasan_report+0xaa/0x120 [ 4712.004045] ? build_sec_desc+0x842/0x1dd0 [ksmbd] [ 4712.004056] kasan_check_range+0x100/0x1e0 [ 4712.004060] memcpy+0x3c/0x60 [ 4712.004064] build_sec_desc+0x842/0x1dd0 [ksmbd] [ 4712.004076] ? parse_sec_desc+0x580/0x580 [ksmbd] [ 4712.004088] ? ksmbd_acls_fattr+0x281/0x410 [ksmbd] [ 4712.004099] smb2_query_info+0xa8f/0x6110 [ksmbd] [ 4712.004111] ? psi_group_change+0x856/0xd70 [ 4712.004148] ? update_load_avg+0x1c3/0x1af0 [ 4712.004152] ? asym_cpu_capacity_scan+0x5d0/0x5d0 [ 4712.004157] ? xas_load+0x23/0x300 [ 4712.004162] ? smb2_query_dir+0x1530/0x1530 [ksmbd] [ 4712.004173] ? _raw_spin_lock_bh+0xe0/0xe0 [ 4712.004179] handle_ksmbd_work+0x30e/0x1020 [ksmbd] [ 4712.004192] process_one_work+0x778/0x11c0 [ 4712.004227] ? _raw_spin_lock_irq+0x8e/0xe0 [ 4712.004231] worker_thread+0x544/0x1180 [ 4712.004234] ? __cpuidle_text_end+0x4/0x4 [ 4712.004239] kthread+0x282/0x320 [ 4712.004243] ? process_one_work+0x11c0/0x11c0 [ 4712.004246] ? kthread_complete_and_exit+0x30/0x30 [ 4712.004282] ret_from_fork+0x1f/0x30 This patch add the buffer validation for security descriptor that is stored by malformed SMB2_SET_INFO_HE command. and allocate large response buffer about SMB2_O_INFO_SECURITY file info class. Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-17771 Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
1 parent 64ccf4b commit ac478bb

File tree

4 files changed

+119
-57
lines changed

4 files changed

+119
-57
lines changed

smb2pdu.c

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -540,9 +540,10 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work)
540540
struct smb2_query_info_req *req;
541541

542542
req = smb2_get_msg(work->request_buf);
543-
if (req->InfoType == SMB2_O_INFO_FILE &&
544-
(req->FileInfoClass == FILE_FULL_EA_INFORMATION ||
545-
req->FileInfoClass == FILE_ALL_INFORMATION))
543+
if ((req->InfoType == SMB2_O_INFO_FILE &&
544+
(req->FileInfoClass == FILE_FULL_EA_INFORMATION ||
545+
req->FileInfoClass == FILE_ALL_INFORMATION)) ||
546+
req->InfoType == SMB2_O_INFO_SECURITY)
546547
sz = large_sz;
547548
}
548549

@@ -3024,7 +3025,7 @@ int smb2_open(struct ksmbd_work *work)
30243025
goto err_out;
30253026

30263027
rc = build_sec_desc(user_ns,
3027-
pntsd, NULL,
3028+
pntsd, NULL, 0,
30283029
OWNER_SECINFO |
30293030
GROUP_SECINFO |
30303031
DACL_SECINFO,
@@ -3881,6 +3882,15 @@ static int verify_info_level(int info_level)
38813882
return 0;
38823883
}
38833884

3885+
static int smb2_resp_buf_len(struct ksmbd_work *work, unsigned short hdr2_len)
3886+
{
3887+
int free_len;
3888+
3889+
free_len = (int)(work->response_sz -
3890+
(get_rfc1002_len(work->response_buf) + 4)) - hdr2_len;
3891+
return free_len;
3892+
}
3893+
38843894
static int smb2_calc_max_out_buf_len(struct ksmbd_work *work,
38853895
unsigned short hdr2_len,
38863896
unsigned int out_buf_len)
@@ -3890,9 +3900,7 @@ static int smb2_calc_max_out_buf_len(struct ksmbd_work *work,
38903900
if (out_buf_len > work->conn->vals->max_trans_size)
38913901
return -EINVAL;
38923902

3893-
free_len = (int)(work->response_sz -
3894-
(get_rfc1002_len(work->response_buf) + 4)) -
3895-
hdr2_len;
3903+
free_len = smb2_resp_buf_len(work, hdr2_len);
38963904
if (free_len < 0)
38973905
return -EINVAL;
38983906

@@ -5184,10 +5192,10 @@ static int smb2_get_info_sec(struct ksmbd_work *work,
51845192
struct smb_ntsd *pntsd = (struct smb_ntsd *)rsp->Buffer, *ppntsd = NULL;
51855193
struct smb_fattr fattr = {{0}};
51865194
struct inode *inode;
5187-
__u32 secdesclen;
5195+
__u32 secdesclen = 0;
51885196
unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID;
51895197
int addition_info = le32_to_cpu(req->AdditionalInformation);
5190-
int rc;
5198+
int rc = 0, ppntsd_size = 0;
51915199

51925200
if (addition_info & ~(OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO |
51935201
PROTECTED_DACL_SECINFO |
@@ -5233,11 +5241,14 @@ static int smb2_get_info_sec(struct ksmbd_work *work,
52335241

52345242
if (test_share_config_flag(work->tcon->share_conf,
52355243
KSMBD_SHARE_FLAG_ACL_XATTR))
5236-
ksmbd_vfs_get_sd_xattr(work->conn, user_ns,
5237-
fp->filp->f_path.dentry, &ppntsd);
5238-
5239-
rc = build_sec_desc(user_ns, pntsd, ppntsd, addition_info,
5240-
&secdesclen, &fattr);
5244+
ppntsd_size = ksmbd_vfs_get_sd_xattr(work->conn, user_ns,
5245+
fp->filp->f_path.dentry,
5246+
&ppntsd);
5247+
5248+
/* Check if sd buffer size exceeds response buffer size */
5249+
if (smb2_resp_buf_len(work, 8) > ppntsd_size)
5250+
rc = build_sec_desc(user_ns, pntsd, ppntsd, ppntsd_size,
5251+
addition_info, &secdesclen, &fattr);
52415252
posix_acl_release(fattr.cf_acls);
52425253
posix_acl_release(fattr.cf_dacls);
52435254
kfree(ppntsd);

smbacl.c

Lines changed: 88 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,7 @@ static void set_posix_acl_entries_dacl(struct user_namespace *user_ns,
724724
static void set_ntacl_dacl(struct user_namespace *user_ns,
725725
struct smb_acl *pndacl,
726726
struct smb_acl *nt_dacl,
727+
unsigned int aces_size,
727728
const struct smb_sid *pownersid,
728729
const struct smb_sid *pgrpsid,
729730
struct smb_fattr *fattr)
@@ -737,9 +738,19 @@ static void set_ntacl_dacl(struct user_namespace *user_ns,
737738
if (nt_num_aces) {
738739
ntace = (struct smb_ace *)((char *)nt_dacl + sizeof(struct smb_acl));
739740
for (i = 0; i < nt_num_aces; i++) {
740-
memcpy((char *)pndace + size, ntace, le16_to_cpu(ntace->size));
741-
size += le16_to_cpu(ntace->size);
742-
ntace = (struct smb_ace *)((char *)ntace + le16_to_cpu(ntace->size));
741+
unsigned short nt_ace_size;
742+
743+
if (offsetof(struct smb_ace, access_req) > aces_size)
744+
break;
745+
746+
nt_ace_size = le16_to_cpu(ntace->size);
747+
if (nt_ace_size > aces_size)
748+
break;
749+
750+
memcpy((char *)pndace + size, ntace, nt_ace_size);
751+
size += nt_ace_size;
752+
aces_size -= nt_ace_size;
753+
ntace = (struct smb_ace *)((char *)ntace + nt_ace_size);
743754
num_aces++;
744755
}
745756
}
@@ -912,7 +923,7 @@ int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd,
912923
/* Convert permission bits from mode to equivalent CIFS ACL */
913924
int build_sec_desc(struct user_namespace *user_ns,
914925
struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd,
915-
int addition_info, __u32 *secdesclen,
926+
int ppntsd_size, int addition_info, __u32 *secdesclen,
916927
struct smb_fattr *fattr)
917928
{
918929
int rc = 0;
@@ -972,15 +983,25 @@ int build_sec_desc(struct user_namespace *user_ns,
972983

973984
if (!ppntsd) {
974985
set_mode_dacl(user_ns, dacl_ptr, fattr);
975-
} else if (!ppntsd->dacloffset) {
976-
goto out;
977986
} else {
978987
struct smb_acl *ppdacl_ptr;
988+
unsigned int dacl_offset = le32_to_cpu(ppntsd->dacloffset);
989+
int ppdacl_size, ntacl_size = ppntsd_size - dacl_offset;
990+
991+
if (!dacl_offset ||
992+
(dacl_offset + sizeof(struct smb_acl) > ppntsd_size))
993+
goto out;
994+
995+
ppdacl_ptr = (struct smb_acl *)((char *)ppntsd + dacl_offset);
996+
ppdacl_size = le16_to_cpu(ppdacl_ptr->size);
997+
if (ppdacl_size > ntacl_size ||
998+
ppdacl_size < sizeof(struct smb_acl))
999+
goto out;
9791000

980-
ppdacl_ptr = (struct smb_acl *)((char *)ppntsd +
981-
le32_to_cpu(ppntsd->dacloffset));
9821001
set_ntacl_dacl(user_ns, dacl_ptr, ppdacl_ptr,
983-
nowner_sid_ptr, ngroup_sid_ptr, fattr);
1002+
ntacl_size - sizeof(struct smb_acl),
1003+
nowner_sid_ptr, ngroup_sid_ptr,
1004+
fattr);
9841005
}
9851006
pntsd->dacloffset = cpu_to_le32(offset);
9861007
offset += le16_to_cpu(dacl_ptr->size);
@@ -1014,24 +1035,31 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
10141035
struct smb_sid owner_sid, group_sid;
10151036
struct dentry *parent = path->dentry->d_parent;
10161037
struct user_namespace *user_ns = mnt_user_ns(path->mnt);
1017-
int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0;
1018-
int rc = 0, num_aces, dacloffset, pntsd_type, acl_len;
1038+
int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0, pdacl_size;
1039+
int rc = 0, num_aces, dacloffset, pntsd_type, pntsd_size, acl_len, aces_size;
10191040
char *aces_base;
10201041
bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode);
10211042

1022-
acl_len = ksmbd_vfs_get_sd_xattr(conn, user_ns,
1023-
parent, &parent_pntsd);
1024-
if (acl_len <= 0)
1043+
pntsd_size = ksmbd_vfs_get_sd_xattr(conn, user_ns,
1044+
parent, &parent_pntsd);
1045+
if (pntsd_size <= 0)
10251046
return -ENOENT;
10261047
dacloffset = le32_to_cpu(parent_pntsd->dacloffset);
1027-
if (!dacloffset) {
1048+
if (!dacloffset || (dacloffset + sizeof(struct smb_acl) > pntsd_size)) {
10281049
rc = -EINVAL;
10291050
goto free_parent_pntsd;
10301051
}
10311052

10321053
parent_pdacl = (struct smb_acl *)((char *)parent_pntsd + dacloffset);
1054+
acl_len = pntsd_size - dacloffset;
10331055
num_aces = le32_to_cpu(parent_pdacl->num_aces);
10341056
pntsd_type = le16_to_cpu(parent_pntsd->type);
1057+
pdacl_size = le16_to_cpu(parent_pdacl->size);
1058+
1059+
if (pdacl_size > acl_len || pdacl_size < sizeof(struct smb_acl)) {
1060+
rc = -EINVAL;
1061+
goto free_parent_pntsd;
1062+
}
10351063

10361064
aces_base = kmalloc(sizeof(struct smb_ace) * num_aces * 2, GFP_KERNEL);
10371065
if (!aces_base) {
@@ -1042,11 +1070,23 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
10421070
aces = (struct smb_ace *)aces_base;
10431071
parent_aces = (struct smb_ace *)((char *)parent_pdacl +
10441072
sizeof(struct smb_acl));
1073+
aces_size = acl_len - sizeof(struct smb_acl);
10451074

10461075
if (pntsd_type & DACL_AUTO_INHERITED)
10471076
inherited_flags = INHERITED_ACE;
10481077

10491078
for (i = 0; i < num_aces; i++) {
1079+
int pace_size;
1080+
1081+
if (offsetof(struct smb_ace, access_req) > aces_size)
1082+
break;
1083+
1084+
pace_size = le16_to_cpu(parent_aces->size);
1085+
if (pace_size > aces_size)
1086+
break;
1087+
1088+
aces_size -= pace_size;
1089+
10501090
flags = parent_aces->flags;
10511091
if (!smb_inherit_flags(flags, is_dir))
10521092
goto pass;
@@ -1091,8 +1131,7 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
10911131
aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size));
10921132
ace_cnt++;
10931133
pass:
1094-
parent_aces =
1095-
(struct smb_ace *)((char *)parent_aces + le16_to_cpu(parent_aces->size));
1134+
parent_aces = (struct smb_ace *)((char *)parent_aces + pace_size);
10961135
}
10971136

10981137
if (nt_size > 0) {
@@ -1187,7 +1226,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path,
11871226
struct smb_ntsd *pntsd = NULL;
11881227
struct smb_acl *pdacl;
11891228
struct posix_acl *posix_acls;
1190-
int rc = 0, acl_size;
1229+
int rc = 0, pntsd_size, acl_size, aces_size, pdacl_size, dacl_offset;
11911230
struct smb_sid sid;
11921231
int granted = le32_to_cpu(*pdaccess & ~FILE_MAXIMAL_ACCESS_LE);
11931232
struct smb_ace *ace;
@@ -1196,49 +1235,50 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path,
11961235
struct smb_ace *others_ace = NULL;
11971236
struct posix_acl_entry *pa_entry;
11981237
unsigned int sid_type = SIDOWNER;
1199-
char *end_of_acl;
1238+
unsigned short ace_size;
12001239

12011240
ksmbd_debug(SMB, "check permission using windows acl\n");
1202-
acl_size = ksmbd_vfs_get_sd_xattr(conn, user_ns,
1203-
path->dentry, &pntsd);
1204-
if (acl_size <= 0 || !pntsd || !pntsd->dacloffset) {
1205-
kfree(pntsd);
1206-
return 0;
1207-
}
1241+
pntsd_size = ksmbd_vfs_get_sd_xattr(conn, user_ns,
1242+
path->dentry, &pntsd);
1243+
if (pntsd_size <= 0 || !pntsd)
1244+
goto err_out;
1245+
1246+
dacl_offset = le32_to_cpu(pntsd->dacloffset);
1247+
if (!dacl_offset ||
1248+
(dacl_offset + sizeof(struct smb_acl) > pntsd_size))
1249+
goto err_out;
12081250

12091251
pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset));
1210-
end_of_acl = ((char *)pntsd) + acl_size;
1211-
if (end_of_acl <= (char *)pdacl) {
1212-
kfree(pntsd);
1213-
return 0;
1214-
}
1252+
acl_size = pntsd_size - dacl_offset;
1253+
pdacl_size = le16_to_cpu(pdacl->size);
12151254

1216-
if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size) ||
1217-
le16_to_cpu(pdacl->size) < sizeof(struct smb_acl)) {
1218-
kfree(pntsd);
1219-
return 0;
1220-
}
1255+
if (pdacl_size > acl_size || pdacl_size < sizeof(struct smb_acl))
1256+
goto err_out;
12211257

12221258
if (!pdacl->num_aces) {
1223-
if (!(le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) &&
1259+
if (!(pdacl_size - sizeof(struct smb_acl)) &&
12241260
*pdaccess & ~(FILE_READ_CONTROL_LE | FILE_WRITE_DAC_LE)) {
12251261
rc = -EACCES;
12261262
goto err_out;
12271263
}
1228-
kfree(pntsd);
1229-
return 0;
1264+
goto err_out;
12301265
}
12311266

12321267
if (*pdaccess & FILE_MAXIMAL_ACCESS_LE) {
12331268
granted = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES |
12341269
DELETE;
12351270

12361271
ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl));
1272+
aces_size = acl_size - sizeof(struct smb_acl);
12371273
for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) {
1274+
if (offsetof(struct smb_ace, access_req) > aces_size)
1275+
break;
1276+
ace_size = le16_to_cpu(ace->size);
1277+
if (ace_size > aces_size)
1278+
break;
1279+
aces_size -= ace_size;
12381280
granted |= le32_to_cpu(ace->access_req);
12391281
ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size));
1240-
if (end_of_acl < (char *)ace)
1241-
goto err_out;
12421282
}
12431283

12441284
if (!pdacl->num_aces)
@@ -1250,7 +1290,15 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path,
12501290
id_to_sid(uid, sid_type, &sid);
12511291

12521292
ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl));
1293+
aces_size = acl_size - sizeof(struct smb_acl);
12531294
for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) {
1295+
if (offsetof(struct smb_ace, access_req) > aces_size)
1296+
break;
1297+
ace_size = le16_to_cpu(ace->size);
1298+
if (ace_size > aces_size)
1299+
break;
1300+
aces_size -= ace_size;
1301+
12541302
if (!compare_sids(&sid, &ace->sid) ||
12551303
!compare_sids(&sid_unix_NFS_mode, &ace->sid)) {
12561304
found = 1;
@@ -1260,8 +1308,6 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path,
12601308
others_ace = ace;
12611309

12621310
ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size));
1263-
if (end_of_acl < (char *)ace)
1264-
goto err_out;
12651311
}
12661312

12671313
if (*pdaccess & FILE_MAXIMAL_ACCESS_LE && found) {

smbacl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ struct posix_acl_state {
196196
int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd,
197197
int acl_len, struct smb_fattr *fattr);
198198
int build_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd,
199-
struct smb_ntsd *ppntsd, int addition_info,
199+
struct smb_ntsd *ppntsd, int ppntsd_size, int addition_info,
200200
__u32 *secdesclen, struct smb_fattr *fattr);
201201
int init_acl_state(struct posix_acl_state *state, int cnt);
202202
void free_acl_state(struct posix_acl_state *state);

vfs.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1707,6 +1707,11 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn,
17071707
}
17081708

17091709
*pntsd = acl.sd_buf;
1710+
if (acl.sd_size < sizeof(struct smb_ntsd)) {
1711+
pr_err("sd size is invalid\n");
1712+
goto out_free;
1713+
}
1714+
17101715
(*pntsd)->osidoffset = cpu_to_le32(le32_to_cpu((*pntsd)->osidoffset) -
17111716
NDR_NTSD_OFFSETOF);
17121717
(*pntsd)->gsidoffset = cpu_to_le32(le32_to_cpu((*pntsd)->gsidoffset) -

0 commit comments

Comments
 (0)