Skip to content

Commit 996454b

Browse files
s-zaizensmfrench
authored andcommitted
ksmbd: validate inherited ACE SID length
smb_inherit_dacl() walks the parent directory DACL loaded from the security descriptor xattr. It verifies that each ACE contains the fixed SID header before using it, but does not verify that the variable-length SID described by sid.num_subauth is fully contained in the ACE. A malformed inheritable ACE can advertise more subauthorities than are present in the ACE. compare_sids() may then read past the ACE. smb_set_ace() also clamps the copied destination SID, but used the unchecked source SID count to compute the inherited ACE size. That could advance the temporary inherited ACE buffer pointer and nt_size accounting past the allocated buffer. Fix this by validating the parent ACE SID count and SID length before using the SID during inheritance. Compute the inherited ACE size from the copied SID so the size matches the bounded destination SID. Reject the inherited DACL if size accumulation would overflow smb_acl.size or the security descriptor allocation size. Fixes: e2f3448 ("cifsd: add server-side procedures for SMB3") Signed-off-by: Shota Zaizen <[email protected]> Acked-by: Namjae Jeon <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 6fd7dd4 commit 996454b

1 file changed

Lines changed: 52 additions & 14 deletions

File tree

fs/smb/server/smbacl.c

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,7 +1068,26 @@ static void smb_set_ace(struct smb_ace *ace, const struct smb_sid *sid, u8 type,
10681068
ace->flags = flags;
10691069
ace->access_req = access_req;
10701070
smb_copy_sid(&ace->sid, sid);
1071-
ace->size = cpu_to_le16(1 + 1 + 2 + 4 + 1 + 1 + 6 + (sid->num_subauth * 4));
1071+
ace->size = cpu_to_le16(1 + 1 + 2 + 4 + 1 + 1 + 6 +
1072+
(ace->sid.num_subauth * 4));
1073+
}
1074+
1075+
static int smb_append_inherited_ace(struct smb_ace **ace, int *nt_size,
1076+
u16 *ace_cnt, const struct smb_sid *sid,
1077+
u8 type, u8 flags, __le32 access_req)
1078+
{
1079+
int ace_size;
1080+
1081+
smb_set_ace(*ace, sid, type, flags, access_req);
1082+
ace_size = le16_to_cpu((*ace)->size);
1083+
/* pdacl->size is __le16 and includes struct smb_acl. */
1084+
if (check_add_overflow(*nt_size, ace_size, nt_size) ||
1085+
*nt_size > U16_MAX - (int)sizeof(struct smb_acl))
1086+
return -EINVAL;
1087+
1088+
(*ace_cnt)++;
1089+
*ace = (struct smb_ace *)((char *)*ace + ace_size);
1090+
return 0;
10721091
}
10731092

10741093
int smb_inherit_dacl(struct ksmbd_conn *conn,
@@ -1157,6 +1176,12 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
11571176
CIFS_SID_BASE_SIZE)
11581177
break;
11591178

1179+
if (parent_aces->sid.num_subauth > SID_MAX_SUB_AUTHORITIES ||
1180+
pace_size < offsetof(struct smb_ace, sid) +
1181+
CIFS_SID_BASE_SIZE +
1182+
sizeof(__le32) * parent_aces->sid.num_subauth)
1183+
break;
1184+
11601185
aces_size -= pace_size;
11611186

11621187
flags = parent_aces->flags;
@@ -1186,22 +1211,24 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
11861211
}
11871212

11881213
if (is_dir && creator && flags & CONTAINER_INHERIT_ACE) {
1189-
smb_set_ace(aces, psid, parent_aces->type, inherited_flags,
1190-
parent_aces->access_req);
1191-
nt_size += le16_to_cpu(aces->size);
1192-
ace_cnt++;
1193-
aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size));
1214+
rc = smb_append_inherited_ace(&aces, &nt_size, &ace_cnt,
1215+
psid, parent_aces->type,
1216+
inherited_flags,
1217+
parent_aces->access_req);
1218+
if (rc)
1219+
goto free_aces_base;
11941220
flags |= INHERIT_ONLY_ACE;
11951221
psid = creator;
11961222
} else if (is_dir && !(parent_aces->flags & NO_PROPAGATE_INHERIT_ACE)) {
11971223
psid = &parent_aces->sid;
11981224
}
11991225

1200-
smb_set_ace(aces, psid, parent_aces->type, flags | inherited_flags,
1201-
parent_aces->access_req);
1202-
nt_size += le16_to_cpu(aces->size);
1203-
aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size));
1204-
ace_cnt++;
1226+
rc = smb_append_inherited_ace(&aces, &nt_size, &ace_cnt, psid,
1227+
parent_aces->type,
1228+
flags | inherited_flags,
1229+
parent_aces->access_req);
1230+
if (rc)
1231+
goto free_aces_base;
12051232
pass:
12061233
parent_aces = (struct smb_ace *)((char *)parent_aces + pace_size);
12071234
}
@@ -1211,7 +1238,7 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
12111238
struct smb_acl *pdacl;
12121239
struct smb_sid *powner_sid = NULL, *pgroup_sid = NULL;
12131240
int powner_sid_size = 0, pgroup_sid_size = 0, pntsd_size;
1214-
int pntsd_alloc_size;
1241+
size_t pntsd_alloc_size;
12151242

12161243
if (parent_pntsd->osidoffset) {
12171244
powner_sid = (struct smb_sid *)((char *)parent_pntsd +
@@ -1224,8 +1251,19 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
12241251
pgroup_sid_size = 1 + 1 + 6 + (pgroup_sid->num_subauth * 4);
12251252
}
12261253

1227-
pntsd_alloc_size = sizeof(struct smb_ntsd) + powner_sid_size +
1228-
pgroup_sid_size + sizeof(struct smb_acl) + nt_size;
1254+
if (check_add_overflow(sizeof(struct smb_ntsd),
1255+
(size_t)powner_sid_size,
1256+
&pntsd_alloc_size) ||
1257+
check_add_overflow(pntsd_alloc_size,
1258+
(size_t)pgroup_sid_size,
1259+
&pntsd_alloc_size) ||
1260+
check_add_overflow(pntsd_alloc_size, sizeof(struct smb_acl),
1261+
&pntsd_alloc_size) ||
1262+
check_add_overflow(pntsd_alloc_size, (size_t)nt_size,
1263+
&pntsd_alloc_size)) {
1264+
rc = -EINVAL;
1265+
goto free_aces_base;
1266+
}
12291267

12301268
pntsd = kzalloc(pntsd_alloc_size, KSMBD_DEFAULT_GFP);
12311269
if (!pntsd) {

0 commit comments

Comments
 (0)