@@ -3312,6 +3312,7 @@ typedef BOOL(WINAPI *is_well_known_sid_fn)(PSID, WELL_KNOWN_SID_TYPE);
33123312typedef BOOL (WINAPI * is_valid_acl_fn )(PACL );
33133313typedef BOOL (WINAPI * initialize_acl_fn )(PACL , DWORD , DWORD );
33143314typedef BOOL (WINAPI * add_access_allowed_ace_fn )(PACL , DWORD , DWORD , PSID );
3315+ typedef BOOL (WINAPI * add_access_allowed_ace_ex_fn )(PACL , DWORD , DWORD , DWORD , PSID );
33153316typedef BOOL (WINAPI * initialize_security_descriptor_fn )(PSECURITY_DESCRIPTOR , DWORD );
33163317typedef BOOL (WINAPI * set_security_descriptor_dacl_fn )(PSECURITY_DESCRIPTOR , BOOL , PACL , BOOL );
33173318typedef BOOL (WINAPI * set_security_descriptor_owner_fn )(PSECURITY_DESCRIPTOR , PSID , BOOL );
@@ -3339,6 +3340,7 @@ typedef struct {
33393340 is_valid_acl_fn is_valid_acl ;
33403341 initialize_acl_fn initialize_acl ;
33413342 add_access_allowed_ace_fn add_access_allowed_ace ;
3343+ add_access_allowed_ace_ex_fn add_access_allowed_ace_ex ;
33423344 initialize_security_descriptor_fn initialize_security_descriptor ;
33433345 set_security_descriptor_dacl_fn set_security_descriptor_dacl ;
33443346 set_security_descriptor_owner_fn set_security_descriptor_owner ;
@@ -3352,6 +3354,9 @@ typedef struct {
33523354 PACL acl ;
33533355 PSECURITY_DESCRIPTOR descriptor ;
33543356 SECURITY_ATTRIBUTES attributes ;
3357+ PACL directory_acl ;
3358+ PSECURITY_DESCRIPTOR directory_descriptor ;
3359+ SECURITY_ATTRIBUTES directory_attributes ;
33553360} win_security_t ;
33563361
33573362typedef struct win_generation_address {
@@ -3560,6 +3565,8 @@ static void win_security_destroy(win_security_t *security) {
35603565 }
35613566 free (security -> descriptor );
35623567 free (security -> acl );
3568+ free (security -> directory_descriptor );
3569+ free (security -> directory_acl );
35633570 free (security -> user_sid );
35643571 if (security -> advapi ) {
35653572 (void )FreeLibrary (security -> advapi );
@@ -3614,6 +3621,8 @@ static bool win_security_init(win_security_t *security) {
36143621 RESOLVE_ADVAPI_MEMBER (security , initialize_acl , initialize_acl_fn , "InitializeAcl" );
36153622 RESOLVE_ADVAPI_MEMBER (security , add_access_allowed_ace , add_access_allowed_ace_fn ,
36163623 "AddAccessAllowedAce" );
3624+ RESOLVE_ADVAPI_MEMBER (security , add_access_allowed_ace_ex , add_access_allowed_ace_ex_fn ,
3625+ "AddAccessAllowedAceEx" );
36173626 RESOLVE_ADVAPI_MEMBER (security , initialize_security_descriptor ,
36183627 initialize_security_descriptor_fn , "InitializeSecurityDescriptor" );
36193628 RESOLVE_ADVAPI_MEMBER (security , set_security_descriptor_dacl , set_security_descriptor_dacl_fn ,
@@ -3679,6 +3688,37 @@ static bool win_security_init(win_security_t *security) {
36793688 security -> attributes .nLength = sizeof (security -> attributes );
36803689 security -> attributes .lpSecurityDescriptor = security -> descriptor ;
36813690 security -> attributes .bInheritHandle = FALSE;
3691+
3692+ /* Containers need a second, inheritable ACL. AddAccessAllowedAce above
3693+ * cannot express inheritance flags, and a flagless ACE applied to a
3694+ * directory together with PROTECTED_DACL_SECURITY_INFORMATION yields
3695+ * D:PAI(A;;FA;;;<user>): the protection severs the inherited ACEs while
3696+ * the new one propagates nothing, so every child is created with an empty
3697+ * DACL. Files and kernel objects are leaves and keep the flagless ACL.
3698+ *
3699+ * The rights here must be specific (FILE_ALL_ACCESS) rather than
3700+ * GENERIC_ALL. Windows splits an inheritable generic-rights ACE into an
3701+ * effective mapped ACE plus an INHERIT_ONLY one carrying the generic bits,
3702+ * and the owner-only DACL validators require exactly one ACE. */
3703+ security -> directory_acl = malloc (acl_size );
3704+ security -> directory_descriptor = malloc (SECURITY_DESCRIPTOR_MIN_LENGTH );
3705+ if (!security -> directory_acl || !security -> directory_descriptor ||
3706+ !security -> initialize_acl (security -> directory_acl , (DWORD )acl_size , ACL_REVISION ) ||
3707+ !security -> add_access_allowed_ace_ex (security -> directory_acl , ACL_REVISION ,
3708+ CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE ,
3709+ FILE_ALL_ACCESS , security -> user_sid ) ||
3710+ !security -> initialize_security_descriptor (security -> directory_descriptor ,
3711+ SECURITY_DESCRIPTOR_REVISION ) ||
3712+ !security -> set_security_descriptor_dacl (security -> directory_descriptor , TRUE,
3713+ security -> directory_acl , FALSE) ||
3714+ !security -> set_security_descriptor_owner (security -> directory_descriptor , security -> user_sid ,
3715+ FALSE)) {
3716+ win_security_destroy (security );
3717+ return false;
3718+ }
3719+ security -> directory_attributes .nLength = sizeof (security -> directory_attributes );
3720+ security -> directory_attributes .lpSecurityDescriptor = security -> directory_descriptor ;
3721+ security -> directory_attributes .bInheritHandle = FALSE;
36823722 return true;
36833723}
36843724
@@ -4047,7 +4087,7 @@ static bool win_runtime_directory_secure(const wchar_t *runtime_dir) {
40474087 if (!win_security_init (& security )) {
40484088 return false;
40494089 }
4050- bool created = CreateDirectoryW (runtime_dir , & security .attributes ) != 0 ;
4090+ bool created = CreateDirectoryW (runtime_dir , & security .directory_attributes ) != 0 ;
40514091 if (!created && GetLastError () != ERROR_ALREADY_EXISTS ) {
40524092 win_security_destroy (& security );
40534093 return false;
@@ -4094,7 +4134,7 @@ static bool win_runtime_directory_secure(const wchar_t *runtime_dir) {
40944134 directory , SE_FILE_OBJECT ,
40954135 (owner_exact ? 0U : (DWORD )OWNER_SECURITY_INFORMATION ) | DACL_SECURITY_INFORMATION |
40964136 PROTECTED_DACL_SECURITY_INFORMATION ,
4097- owner_exact ? NULL : security .user_sid , NULL , security .acl , NULL );
4137+ owner_exact ? NULL : security .user_sid , NULL , security .directory_acl , NULL );
40984138 }
40994139 if (valid_handle && owner_ok && secure_result != ERROR_SUCCESS ) {
41004140 ipc_validation_detail_set ("owner/DACL repair failed (status %lu%s)" ,
@@ -4181,7 +4221,7 @@ static bool win_private_directory_tree_secure(const wchar_t *directory_path) {
41814221 if (attributes == INVALID_FILE_ATTRIBUTES ) {
41824222 DWORD error = GetLastError ();
41834223 ok = (error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND ) &&
4184- CreateDirectoryW (path , & security .attributes ) != 0 ;
4224+ CreateDirectoryW (path , & security .directory_attributes ) != 0 ;
41854225 }
41864226 /* Ancestors are observe-only and must already be secure. The
41874227 * final current-user directory is intentionally handled below by
0 commit comments