@@ -690,6 +690,7 @@ static void set_posix_acl_entries_dacl(struct user_namespace *user_ns,
690
690
static void set_ntacl_dacl (struct user_namespace * user_ns ,
691
691
struct smb_acl * pndacl ,
692
692
struct smb_acl * nt_dacl ,
693
+ unsigned int aces_size ,
693
694
const struct smb_sid * pownersid ,
694
695
const struct smb_sid * pgrpsid ,
695
696
struct smb_fattr * fattr )
@@ -703,9 +704,19 @@ static void set_ntacl_dacl(struct user_namespace *user_ns,
703
704
if (nt_num_aces ) {
704
705
ntace = (struct smb_ace * )((char * )nt_dacl + sizeof (struct smb_acl ));
705
706
for (i = 0 ; i < nt_num_aces ; i ++ ) {
706
- memcpy ((char * )pndace + size , ntace , le16_to_cpu (ntace -> size ));
707
- size += le16_to_cpu (ntace -> size );
708
- ntace = (struct smb_ace * )((char * )ntace + le16_to_cpu (ntace -> size ));
707
+ unsigned short nt_ace_size ;
708
+
709
+ if (offsetof(struct smb_ace , access_req ) > aces_size )
710
+ break ;
711
+
712
+ nt_ace_size = le16_to_cpu (ntace -> size );
713
+ if (nt_ace_size > aces_size )
714
+ break ;
715
+
716
+ memcpy ((char * )pndace + size , ntace , nt_ace_size );
717
+ size += nt_ace_size ;
718
+ aces_size -= nt_ace_size ;
719
+ ntace = (struct smb_ace * )((char * )ntace + nt_ace_size );
709
720
num_aces ++ ;
710
721
}
711
722
}
@@ -878,7 +889,7 @@ int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd,
878
889
/* Convert permission bits from mode to equivalent CIFS ACL */
879
890
int build_sec_desc (struct user_namespace * user_ns ,
880
891
struct smb_ntsd * pntsd , struct smb_ntsd * ppntsd ,
881
- int addition_info , __u32 * secdesclen ,
892
+ int ppntsd_size , int addition_info , __u32 * secdesclen ,
882
893
struct smb_fattr * fattr )
883
894
{
884
895
int rc = 0 ;
@@ -938,15 +949,25 @@ int build_sec_desc(struct user_namespace *user_ns,
938
949
939
950
if (!ppntsd ) {
940
951
set_mode_dacl (user_ns , dacl_ptr , fattr );
941
- } else if (!ppntsd -> dacloffset ) {
942
- goto out ;
943
952
} else {
944
953
struct smb_acl * ppdacl_ptr ;
954
+ unsigned int dacl_offset = le32_to_cpu (ppntsd -> dacloffset );
955
+ int ppdacl_size , ntacl_size = ppntsd_size - dacl_offset ;
956
+
957
+ if (!dacl_offset ||
958
+ (dacl_offset + sizeof (struct smb_acl ) > ppntsd_size ))
959
+ goto out ;
960
+
961
+ ppdacl_ptr = (struct smb_acl * )((char * )ppntsd + dacl_offset );
962
+ ppdacl_size = le16_to_cpu (ppdacl_ptr -> size );
963
+ if (ppdacl_size > ntacl_size ||
964
+ ppdacl_size < sizeof (struct smb_acl ))
965
+ goto out ;
945
966
946
- ppdacl_ptr = (struct smb_acl * )((char * )ppntsd +
947
- le32_to_cpu (ppntsd -> dacloffset ));
948
967
set_ntacl_dacl (user_ns , dacl_ptr , ppdacl_ptr ,
949
- nowner_sid_ptr , ngroup_sid_ptr , fattr );
968
+ ntacl_size - sizeof (struct smb_acl ),
969
+ nowner_sid_ptr , ngroup_sid_ptr ,
970
+ fattr );
950
971
}
951
972
pntsd -> dacloffset = cpu_to_le32 (offset );
952
973
offset += le16_to_cpu (dacl_ptr -> size );
@@ -980,24 +1001,31 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
980
1001
struct smb_sid owner_sid , group_sid ;
981
1002
struct dentry * parent = path -> dentry -> d_parent ;
982
1003
struct user_namespace * user_ns = mnt_user_ns (path -> mnt );
983
- int inherited_flags = 0 , flags = 0 , i , ace_cnt = 0 , nt_size = 0 ;
984
- int rc = 0 , num_aces , dacloffset , pntsd_type , acl_len ;
1004
+ int inherited_flags = 0 , flags = 0 , i , ace_cnt = 0 , nt_size = 0 , pdacl_size ;
1005
+ int rc = 0 , num_aces , dacloffset , pntsd_type , pntsd_size , acl_len , aces_size ;
985
1006
char * aces_base ;
986
1007
bool is_dir = S_ISDIR (d_inode (path -> dentry )-> i_mode );
987
1008
988
- acl_len = ksmbd_vfs_get_sd_xattr (conn , user_ns ,
989
- parent , & parent_pntsd );
990
- if (acl_len <= 0 )
1009
+ pntsd_size = ksmbd_vfs_get_sd_xattr (conn , user_ns ,
1010
+ parent , & parent_pntsd );
1011
+ if (pntsd_size <= 0 )
991
1012
return - ENOENT ;
992
1013
dacloffset = le32_to_cpu (parent_pntsd -> dacloffset );
993
- if (!dacloffset ) {
1014
+ if (!dacloffset || ( dacloffset + sizeof ( struct smb_acl ) > pntsd_size ) ) {
994
1015
rc = - EINVAL ;
995
1016
goto free_parent_pntsd ;
996
1017
}
997
1018
998
1019
parent_pdacl = (struct smb_acl * )((char * )parent_pntsd + dacloffset );
1020
+ acl_len = pntsd_size - dacloffset ;
999
1021
num_aces = le32_to_cpu (parent_pdacl -> num_aces );
1000
1022
pntsd_type = le16_to_cpu (parent_pntsd -> type );
1023
+ pdacl_size = le16_to_cpu (parent_pdacl -> size );
1024
+
1025
+ if (pdacl_size > acl_len || pdacl_size < sizeof (struct smb_acl )) {
1026
+ rc = - EINVAL ;
1027
+ goto free_parent_pntsd ;
1028
+ }
1001
1029
1002
1030
aces_base = kmalloc (sizeof (struct smb_ace ) * num_aces * 2 , GFP_KERNEL );
1003
1031
if (!aces_base ) {
@@ -1008,11 +1036,23 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
1008
1036
aces = (struct smb_ace * )aces_base ;
1009
1037
parent_aces = (struct smb_ace * )((char * )parent_pdacl +
1010
1038
sizeof (struct smb_acl ));
1039
+ aces_size = acl_len - sizeof (struct smb_acl );
1011
1040
1012
1041
if (pntsd_type & DACL_AUTO_INHERITED )
1013
1042
inherited_flags = INHERITED_ACE ;
1014
1043
1015
1044
for (i = 0 ; i < num_aces ; i ++ ) {
1045
+ int pace_size ;
1046
+
1047
+ if (offsetof(struct smb_ace , access_req ) > aces_size )
1048
+ break ;
1049
+
1050
+ pace_size = le16_to_cpu (parent_aces -> size );
1051
+ if (pace_size > aces_size )
1052
+ break ;
1053
+
1054
+ aces_size -= pace_size ;
1055
+
1016
1056
flags = parent_aces -> flags ;
1017
1057
if (!smb_inherit_flags (flags , is_dir ))
1018
1058
goto pass ;
@@ -1057,8 +1097,7 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
1057
1097
aces = (struct smb_ace * )((char * )aces + le16_to_cpu (aces -> size ));
1058
1098
ace_cnt ++ ;
1059
1099
pass :
1060
- parent_aces =
1061
- (struct smb_ace * )((char * )parent_aces + le16_to_cpu (parent_aces -> size ));
1100
+ parent_aces = (struct smb_ace * )((char * )parent_aces + pace_size );
1062
1101
}
1063
1102
1064
1103
if (nt_size > 0 ) {
@@ -1153,7 +1192,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path,
1153
1192
struct smb_ntsd * pntsd = NULL ;
1154
1193
struct smb_acl * pdacl ;
1155
1194
struct posix_acl * posix_acls ;
1156
- int rc = 0 , acl_size ;
1195
+ int rc = 0 , pntsd_size , acl_size , aces_size , pdacl_size , dacl_offset ;
1157
1196
struct smb_sid sid ;
1158
1197
int granted = le32_to_cpu (* pdaccess & ~FILE_MAXIMAL_ACCESS_LE );
1159
1198
struct smb_ace * ace ;
@@ -1162,49 +1201,50 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path,
1162
1201
struct smb_ace * others_ace = NULL ;
1163
1202
struct posix_acl_entry * pa_entry ;
1164
1203
unsigned int sid_type = SIDOWNER ;
1165
- char * end_of_acl ;
1204
+ unsigned short ace_size ;
1166
1205
1167
1206
ksmbd_debug (SMB , "check permission using windows acl\n" );
1168
- acl_size = ksmbd_vfs_get_sd_xattr (conn , user_ns ,
1169
- path -> dentry , & pntsd );
1170
- if (acl_size <= 0 || !pntsd || !pntsd -> dacloffset ) {
1171
- kfree (pntsd );
1172
- return 0 ;
1173
- }
1207
+ pntsd_size = ksmbd_vfs_get_sd_xattr (conn , user_ns ,
1208
+ path -> dentry , & pntsd );
1209
+ if (pntsd_size <= 0 || !pntsd )
1210
+ goto err_out ;
1211
+
1212
+ dacl_offset = le32_to_cpu (pntsd -> dacloffset );
1213
+ if (!dacl_offset ||
1214
+ (dacl_offset + sizeof (struct smb_acl ) > pntsd_size ))
1215
+ goto err_out ;
1174
1216
1175
1217
pdacl = (struct smb_acl * )((char * )pntsd + le32_to_cpu (pntsd -> dacloffset ));
1176
- end_of_acl = ((char * )pntsd ) + acl_size ;
1177
- if (end_of_acl <= (char * )pdacl ) {
1178
- kfree (pntsd );
1179
- return 0 ;
1180
- }
1218
+ acl_size = pntsd_size - dacl_offset ;
1219
+ pdacl_size = le16_to_cpu (pdacl -> size );
1181
1220
1182
- if (end_of_acl < (char * )pdacl + le16_to_cpu (pdacl -> size ) ||
1183
- le16_to_cpu (pdacl -> size ) < sizeof (struct smb_acl )) {
1184
- kfree (pntsd );
1185
- return 0 ;
1186
- }
1221
+ if (pdacl_size > acl_size || pdacl_size < sizeof (struct smb_acl ))
1222
+ goto err_out ;
1187
1223
1188
1224
if (!pdacl -> num_aces ) {
1189
- if (!(le16_to_cpu ( pdacl -> size ) - sizeof (struct smb_acl )) &&
1225
+ if (!(pdacl_size - sizeof (struct smb_acl )) &&
1190
1226
* pdaccess & ~(FILE_READ_CONTROL_LE | FILE_WRITE_DAC_LE )) {
1191
1227
rc = - EACCES ;
1192
1228
goto err_out ;
1193
1229
}
1194
- kfree (pntsd );
1195
- return 0 ;
1230
+ goto err_out ;
1196
1231
}
1197
1232
1198
1233
if (* pdaccess & FILE_MAXIMAL_ACCESS_LE ) {
1199
1234
granted = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES |
1200
1235
DELETE ;
1201
1236
1202
1237
ace = (struct smb_ace * )((char * )pdacl + sizeof (struct smb_acl ));
1238
+ aces_size = acl_size - sizeof (struct smb_acl );
1203
1239
for (i = 0 ; i < le32_to_cpu (pdacl -> num_aces ); i ++ ) {
1240
+ if (offsetof(struct smb_ace , access_req ) > aces_size )
1241
+ break ;
1242
+ ace_size = le16_to_cpu (ace -> size );
1243
+ if (ace_size > aces_size )
1244
+ break ;
1245
+ aces_size -= ace_size ;
1204
1246
granted |= le32_to_cpu (ace -> access_req );
1205
1247
ace = (struct smb_ace * )((char * )ace + le16_to_cpu (ace -> size ));
1206
- if (end_of_acl < (char * )ace )
1207
- goto err_out ;
1208
1248
}
1209
1249
1210
1250
if (!pdacl -> num_aces )
@@ -1216,7 +1256,15 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path,
1216
1256
id_to_sid (uid , sid_type , & sid );
1217
1257
1218
1258
ace = (struct smb_ace * )((char * )pdacl + sizeof (struct smb_acl ));
1259
+ aces_size = acl_size - sizeof (struct smb_acl );
1219
1260
for (i = 0 ; i < le32_to_cpu (pdacl -> num_aces ); i ++ ) {
1261
+ if (offsetof(struct smb_ace , access_req ) > aces_size )
1262
+ break ;
1263
+ ace_size = le16_to_cpu (ace -> size );
1264
+ if (ace_size > aces_size )
1265
+ break ;
1266
+ aces_size -= ace_size ;
1267
+
1220
1268
if (!compare_sids (& sid , & ace -> sid ) ||
1221
1269
!compare_sids (& sid_unix_NFS_mode , & ace -> sid )) {
1222
1270
found = 1 ;
@@ -1226,8 +1274,6 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path,
1226
1274
others_ace = ace ;
1227
1275
1228
1276
ace = (struct smb_ace * )((char * )ace + le16_to_cpu (ace -> size ));
1229
- if (end_of_acl < (char * )ace )
1230
- goto err_out ;
1231
1277
}
1232
1278
1233
1279
if (* pdaccess & FILE_MAXIMAL_ACCESS_LE && found ) {
0 commit comments