@@ -724,6 +724,7 @@ static void set_posix_acl_entries_dacl(struct user_namespace *user_ns,
724
724
static void set_ntacl_dacl (struct user_namespace * user_ns ,
725
725
struct smb_acl * pndacl ,
726
726
struct smb_acl * nt_dacl ,
727
+ unsigned int aces_size ,
727
728
const struct smb_sid * pownersid ,
728
729
const struct smb_sid * pgrpsid ,
729
730
struct smb_fattr * fattr )
@@ -737,9 +738,19 @@ static void set_ntacl_dacl(struct user_namespace *user_ns,
737
738
if (nt_num_aces ) {
738
739
ntace = (struct smb_ace * )((char * )nt_dacl + sizeof (struct smb_acl ));
739
740
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 );
743
754
num_aces ++ ;
744
755
}
745
756
}
@@ -912,7 +923,7 @@ int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd,
912
923
/* Convert permission bits from mode to equivalent CIFS ACL */
913
924
int build_sec_desc (struct user_namespace * user_ns ,
914
925
struct smb_ntsd * pntsd , struct smb_ntsd * ppntsd ,
915
- int addition_info , __u32 * secdesclen ,
926
+ int ppntsd_size , int addition_info , __u32 * secdesclen ,
916
927
struct smb_fattr * fattr )
917
928
{
918
929
int rc = 0 ;
@@ -972,15 +983,25 @@ int build_sec_desc(struct user_namespace *user_ns,
972
983
973
984
if (!ppntsd ) {
974
985
set_mode_dacl (user_ns , dacl_ptr , fattr );
975
- } else if (!ppntsd -> dacloffset ) {
976
- goto out ;
977
986
} else {
978
987
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 ;
979
1000
980
- ppdacl_ptr = (struct smb_acl * )((char * )ppntsd +
981
- le32_to_cpu (ppntsd -> dacloffset ));
982
1001
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 );
984
1005
}
985
1006
pntsd -> dacloffset = cpu_to_le32 (offset );
986
1007
offset += le16_to_cpu (dacl_ptr -> size );
@@ -1014,24 +1035,31 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
1014
1035
struct smb_sid owner_sid , group_sid ;
1015
1036
struct dentry * parent = path -> dentry -> d_parent ;
1016
1037
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 ;
1019
1040
char * aces_base ;
1020
1041
bool is_dir = S_ISDIR (d_inode (path -> dentry )-> i_mode );
1021
1042
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 )
1025
1046
return - ENOENT ;
1026
1047
dacloffset = le32_to_cpu (parent_pntsd -> dacloffset );
1027
- if (!dacloffset ) {
1048
+ if (!dacloffset || ( dacloffset + sizeof ( struct smb_acl ) > pntsd_size ) ) {
1028
1049
rc = - EINVAL ;
1029
1050
goto free_parent_pntsd ;
1030
1051
}
1031
1052
1032
1053
parent_pdacl = (struct smb_acl * )((char * )parent_pntsd + dacloffset );
1054
+ acl_len = pntsd_size - dacloffset ;
1033
1055
num_aces = le32_to_cpu (parent_pdacl -> num_aces );
1034
1056
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
+ }
1035
1063
1036
1064
aces_base = kmalloc (sizeof (struct smb_ace ) * num_aces * 2 , GFP_KERNEL );
1037
1065
if (!aces_base ) {
@@ -1042,11 +1070,23 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
1042
1070
aces = (struct smb_ace * )aces_base ;
1043
1071
parent_aces = (struct smb_ace * )((char * )parent_pdacl +
1044
1072
sizeof (struct smb_acl ));
1073
+ aces_size = acl_len - sizeof (struct smb_acl );
1045
1074
1046
1075
if (pntsd_type & DACL_AUTO_INHERITED )
1047
1076
inherited_flags = INHERITED_ACE ;
1048
1077
1049
1078
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
+
1050
1090
flags = parent_aces -> flags ;
1051
1091
if (!smb_inherit_flags (flags , is_dir ))
1052
1092
goto pass ;
@@ -1091,8 +1131,7 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
1091
1131
aces = (struct smb_ace * )((char * )aces + le16_to_cpu (aces -> size ));
1092
1132
ace_cnt ++ ;
1093
1133
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 );
1096
1135
}
1097
1136
1098
1137
if (nt_size > 0 ) {
@@ -1187,7 +1226,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path,
1187
1226
struct smb_ntsd * pntsd = NULL ;
1188
1227
struct smb_acl * pdacl ;
1189
1228
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 ;
1191
1230
struct smb_sid sid ;
1192
1231
int granted = le32_to_cpu (* pdaccess & ~FILE_MAXIMAL_ACCESS_LE );
1193
1232
struct smb_ace * ace ;
@@ -1196,49 +1235,50 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path,
1196
1235
struct smb_ace * others_ace = NULL ;
1197
1236
struct posix_acl_entry * pa_entry ;
1198
1237
unsigned int sid_type = SIDOWNER ;
1199
- char * end_of_acl ;
1238
+ unsigned short ace_size ;
1200
1239
1201
1240
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 ;
1208
1250
1209
1251
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 );
1215
1254
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 ;
1221
1257
1222
1258
if (!pdacl -> num_aces ) {
1223
- if (!(le16_to_cpu ( pdacl -> size ) - sizeof (struct smb_acl )) &&
1259
+ if (!(pdacl_size - sizeof (struct smb_acl )) &&
1224
1260
* pdaccess & ~(FILE_READ_CONTROL_LE | FILE_WRITE_DAC_LE )) {
1225
1261
rc = - EACCES ;
1226
1262
goto err_out ;
1227
1263
}
1228
- kfree (pntsd );
1229
- return 0 ;
1264
+ goto err_out ;
1230
1265
}
1231
1266
1232
1267
if (* pdaccess & FILE_MAXIMAL_ACCESS_LE ) {
1233
1268
granted = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES |
1234
1269
DELETE ;
1235
1270
1236
1271
ace = (struct smb_ace * )((char * )pdacl + sizeof (struct smb_acl ));
1272
+ aces_size = acl_size - sizeof (struct smb_acl );
1237
1273
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 ;
1238
1280
granted |= le32_to_cpu (ace -> access_req );
1239
1281
ace = (struct smb_ace * )((char * )ace + le16_to_cpu (ace -> size ));
1240
- if (end_of_acl < (char * )ace )
1241
- goto err_out ;
1242
1282
}
1243
1283
1244
1284
if (!pdacl -> num_aces )
@@ -1250,7 +1290,15 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path,
1250
1290
id_to_sid (uid , sid_type , & sid );
1251
1291
1252
1292
ace = (struct smb_ace * )((char * )pdacl + sizeof (struct smb_acl ));
1293
+ aces_size = acl_size - sizeof (struct smb_acl );
1253
1294
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
+
1254
1302
if (!compare_sids (& sid , & ace -> sid ) ||
1255
1303
!compare_sids (& sid_unix_NFS_mode , & ace -> sid )) {
1256
1304
found = 1 ;
@@ -1260,8 +1308,6 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path,
1260
1308
others_ace = ace ;
1261
1309
1262
1310
ace = (struct smb_ace * )((char * )ace + le16_to_cpu (ace -> size ));
1263
- if (end_of_acl < (char * )ace )
1264
- goto err_out ;
1265
1311
}
1266
1312
1267
1313
if (* pdaccess & FILE_MAXIMAL_ACCESS_LE && found ) {
0 commit comments