diff --git a/dlls/advapi32/tests/security.c b/dlls/advapi32/tests/security.c
index d701251c8376ba28838e0bd05f1744142ab40760..dfc0a23452e4101156af36309fdb677c06ab0591 100644
--- a/dlls/advapi32/tests/security.c
+++ b/dlls/advapi32/tests/security.c
@@ -6163,9 +6163,12 @@ static void test_AddMandatoryAce(void)
 {
     static SID low_level = {SID_REVISION, 1, {SECURITY_MANDATORY_LABEL_AUTHORITY},
                             {SECURITY_MANDATORY_LOW_RID}};
+    static SID medium_level = {SID_REVISION, 1, {SECURITY_MANDATORY_LABEL_AUTHORITY},
+                               {SECURITY_MANDATORY_MEDIUM_RID}};
+    static SID_IDENTIFIER_AUTHORITY sia_world = {SECURITY_WORLD_SID_AUTHORITY};
     char buffer_sd[SECURITY_DESCRIPTOR_MIN_LENGTH];
     SECURITY_DESCRIPTOR *sd2, *sd = (SECURITY_DESCRIPTOR *)&buffer_sd;
-    BOOL defaulted, present, ret, found;
+    BOOL defaulted, present, ret, found, found2;
     ACL_SIZE_INFORMATION acl_size_info;
     SYSTEM_MANDATORY_LABEL_ACE *ace;
     char buffer_acl[256];
@@ -6173,6 +6176,7 @@ static void test_AddMandatoryAce(void)
     SECURITY_ATTRIBUTES sa;
     DWORD index, size;
     HANDLE handle;
+    SID *everyone;
     ACL *sacl;
 
     if (!pAddMandatoryAce)
@@ -6267,6 +6271,151 @@ static void test_AddMandatoryAce(void)
     ok(ace->Mask == SYSTEM_MANDATORY_LABEL_NO_WRITE_UP, "Unexpected ACE mask %#x\n", ace->Mask);
     ok(EqualSid(&ace->SidStart, &low_level), "Expected low integrity level\n");
 
+    HeapFree(GetProcessHeap(), 0, sd2);
+
+    ret = pAddMandatoryAce(acl, ACL_REVISION, 0, SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP, &medium_level);
+    ok(ret, "AddMandatoryAce failed with error %u\n", GetLastError());
+
+    ret = SetKernelObjectSecurity(handle, LABEL_SECURITY_INFORMATION, sd);
+    ok(ret, "SetKernelObjectSecurity failed with error %u\n", GetLastError());
+
+    ret = GetKernelObjectSecurity(handle, LABEL_SECURITY_INFORMATION, NULL, 0, &size);
+    ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+       "Unexpected GetKernelObjectSecurity return value %u, error %u\n", ret, GetLastError());
+
+    sd2 = HeapAlloc(GetProcessHeap(), 0, size);
+    ret = GetKernelObjectSecurity(handle, LABEL_SECURITY_INFORMATION, sd2, size, &size);
+    ok(ret, "GetKernelObjectSecurity failed with error %u\n", GetLastError());
+
+    sacl = (void *)0xdeadbeef;
+    present = FALSE;
+    defaulted = TRUE;
+    ret = GetSecurityDescriptorSacl(sd2, &present, &sacl, &defaulted);
+    ok(ret, "GetSecurityDescriptorSacl failed with error %u\n", GetLastError());
+    ok(present, "SACL not present\n");
+    ok(sacl != (void *)0xdeadbeef, "SACL not set\n");
+    ok(sacl->AceCount == 2, "Expected 2 ACEs, got %d\n", sacl->AceCount);
+    ok(!defaulted, "SACL defaulted\n");
+
+    index = 0;
+    found = found2 = FALSE;
+    while (pGetAce(sacl, index++, (void **)&ace))
+    {
+        if (ace->Header.AceType == SYSTEM_MANDATORY_LABEL_ACE_TYPE)
+        {
+            if (EqualSid(&ace->SidStart, &low_level))
+            {
+                found = TRUE;
+                ok(!ace->Header.AceFlags, "Expected 0 as flags, got %#x\n", ace->Header.AceFlags);
+                ok(ace->Mask == SYSTEM_MANDATORY_LABEL_NO_WRITE_UP,
+                   "Expected SYSTEM_MANDATORY_LABEL_NO_WRITE_UP as mask, got %#x\n", ace->Mask);
+            }
+            if (EqualSid(&ace->SidStart, &medium_level))
+            {
+                found2 = TRUE;
+                ok(!ace->Header.AceFlags, "Expected 0 as flags, got %#x\n", ace->Header.AceFlags);
+                ok(ace->Mask == SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP,
+                   "Expected SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP as mask, got %#x\n", ace->Mask);
+            }
+        }
+    }
+    ok(found, "Could not find low mandatory label\n");
+    ok(found2, "Could not find medium mandatory label\n");
+
+    HeapFree(GetProcessHeap(), 0, sd2);
+
+    ret = SetSecurityDescriptorSacl(sd, FALSE, NULL, FALSE);
+    ok(ret, "SetSecurityDescriptorSacl failed with error %u\n", GetLastError());
+
+    ret = SetKernelObjectSecurity(handle, LABEL_SECURITY_INFORMATION, sd);
+    ok(ret, "SetKernelObjectSecurity failed with error %u\n", GetLastError());
+
+    ret = GetKernelObjectSecurity(handle, LABEL_SECURITY_INFORMATION, NULL, 0, &size);
+    ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+       "Unexpected GetKernelObjectSecurity return value %d, error %u\n", ret, GetLastError());
+
+    sd2 = HeapAlloc(GetProcessHeap(), 0, size);
+    ret = GetKernelObjectSecurity(handle, LABEL_SECURITY_INFORMATION, sd2, size, &size);
+    ok(ret, "GetKernelObjectSecurity failed with error %u\n", GetLastError());
+
+    sacl = (void *)0xdeadbeef;
+    present = FALSE;
+    defaulted = TRUE;
+    ret = GetSecurityDescriptorSacl(sd2, &present, &sacl, &defaulted);
+    ok(ret, "GetSecurityDescriptorSacl failed with error %u\n", GetLastError());
+    ok(present, "SACL not present\n");
+    ok(sacl && sacl != (void *)0xdeadbeef, "SACL not set\n");
+    ok(!defaulted, "SACL defaulted\n");
+    ok(!sacl->AceCount, "SACL contains an unexpected ACE count %u\n", sacl->AceCount);
+
+    HeapFree(GetProcessHeap(), 0, sd2);
+
+    ret = InitializeAcl(acl, 256, ACL_REVISION);
+    ok(ret, "InitializeAcl failed with error %u\n", GetLastError());
+
+    ret = pAddMandatoryAce(acl, ACL_REVISION3, 0, SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP, &medium_level);
+    ok(ret, "AddMandatoryAce failed with error %u\n", GetLastError());
+
+    ret = SetSecurityDescriptorSacl(sd, TRUE, acl, FALSE);
+    ok(ret, "SetSecurityDescriptorSacl failed with error %u\n", GetLastError());
+
+    ret = SetKernelObjectSecurity(handle, LABEL_SECURITY_INFORMATION, sd);
+    ok(ret, "SetKernelObjectSecurity failed with error %u\n", GetLastError());
+
+    ret = GetKernelObjectSecurity(handle, LABEL_SECURITY_INFORMATION, NULL, 0, &size);
+    ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+       "Unexpected GetKernelObjectSecurity return value %d, error %u\n", ret, GetLastError());
+
+    sd2 = HeapAlloc(GetProcessHeap(), 0, size);
+    ret = GetKernelObjectSecurity(handle, LABEL_SECURITY_INFORMATION, sd2, size, &size);
+    ok(ret, "GetKernelObjectSecurity failed with error %u\n", GetLastError());
+
+    sacl = (void *)0xdeadbeef;
+    present = FALSE;
+    defaulted = TRUE;
+    ret = GetSecurityDescriptorSacl(sd2, &present, &sacl, &defaulted);
+    ok(ret, "GetSecurityDescriptorSacl failed with error %u\n", GetLastError());
+    ok(present, "SACL not present\n");
+    ok(sacl != (void *)0xdeadbeef, "SACL not set\n");
+    ok(sacl->AclRevision == ACL_REVISION3, "Expected revision 3, got %d\n", sacl->AclRevision);
+    ok(!defaulted, "SACL defaulted\n");
+
+    HeapFree(GetProcessHeap(), 0, sd2);
+
+    ret = InitializeAcl(acl, 256, ACL_REVISION);
+    ok(ret, "InitializeAcl failed with error %u\n", GetLastError());
+
+    ret = AllocateAndInitializeSid(&sia_world, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, (void **)&everyone);
+    ok(ret, "AllocateAndInitializeSid failed with error %u\n", GetLastError());
+
+    ret = AddAccessAllowedAce(acl, ACL_REVISION, KEY_READ, everyone);
+    ok(ret, "AddAccessAllowedAce failed with error %u\n", GetLastError());
+
+    ret = SetSecurityDescriptorSacl(sd, TRUE, acl, FALSE);
+    ok(ret, "SetSecurityDescriptorSacl failed with error %u\n", GetLastError());
+
+    ret = SetKernelObjectSecurity(handle, LABEL_SECURITY_INFORMATION, sd);
+    ok(ret, "SetKernelObjectSecurity failed with error %u\n", GetLastError());
+
+    ret = GetKernelObjectSecurity(handle, LABEL_SECURITY_INFORMATION, NULL, 0, &size);
+    ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+       "Unexpected GetKernelObjectSecurity return value %d, error %u\n", ret, GetLastError());
+
+    sd2 = HeapAlloc(GetProcessHeap(), 0, size);
+    ret = GetKernelObjectSecurity(handle, LABEL_SECURITY_INFORMATION, sd2, size, &size);
+    ok(ret, "GetKernelObjectSecurity failed with error %u\n", GetLastError());
+
+    sacl = (void *)0xdeadbeef;
+    present = FALSE;
+    defaulted = TRUE;
+    ret = GetSecurityDescriptorSacl(sd2, &present, &sacl, &defaulted);
+    ok(ret, "GetSecurityDescriptorSacl failed with error %u\n", GetLastError());
+    ok(present, "SACL not present\n");
+    ok(sacl && sacl != (void *)0xdeadbeef, "SACL not set\n");
+    ok(!defaulted, "SACL defaulted\n");
+    ok(!sacl->AceCount, "SACL contains an unexpected ACE count %u\n", sacl->AceCount);
+
+    FreeSid(everyone);
     HeapFree(GetProcessHeap(), 0, sd2);
     CloseHandle(handle);
 }
diff --git a/dlls/ntdll/sec.c b/dlls/ntdll/sec.c
index 3f7aa79323668427108c25a78a05609ba89a3282..5c858b5bcbbdbea137b61322e5a70a2024679cb5 100644
--- a/dlls/ntdll/sec.c
+++ b/dlls/ntdll/sec.c
@@ -1775,7 +1775,8 @@ NTSTATUS WINAPI NtSetSecurityObject(HANDLE Handle,
             return STATUS_INVALID_SECURITY_DESCR;
     }
 
-    if (SecurityInformation & SACL_SECURITY_INFORMATION)
+    if (SecurityInformation & SACL_SECURITY_INFORMATION ||
+        SecurityInformation & LABEL_SECURITY_INFORMATION)
     {
         status = RtlGetSaclSecurityDescriptor( SecurityDescriptor, &present, &sacl, &defaulted );
         if (status != STATUS_SUCCESS) return status;
diff --git a/server/handle.c b/server/handle.c
index 7d4512b183e4f506a68fc27e786f27f59a531b46..faa3a9f13678baebd13d060fb001ae7a58b051c9 100644
--- a/server/handle.c
+++ b/server/handle.c
@@ -687,7 +687,8 @@ DECL_HANDLER(set_security_object)
     }
 
     if (req->security_info & OWNER_SECURITY_INFORMATION ||
-        req->security_info & GROUP_SECURITY_INFORMATION)
+        req->security_info & GROUP_SECURITY_INFORMATION ||
+        req->security_info & LABEL_SECURITY_INFORMATION)
         access |= WRITE_OWNER;
     if (req->security_info & SACL_SECURITY_INFORMATION)
         access |= ACCESS_SYSTEM_SECURITY;
diff --git a/server/object.c b/server/object.c
index e43fe3f217306f33deb68b9befa9ea209e8f2a06..70872e8011cf913a2120e9fc521f3fd2ea0c5d36 100644
--- a/server/object.c
+++ b/server/object.c
@@ -542,6 +542,7 @@ int set_sd_defaults_from_token( struct object *obj, const struct security_descri
     int present;
     const SID *owner = NULL, *group = NULL;
     const ACL *sacl, *dacl;
+    ACL *replaced_sacl = NULL;
     char *ptr;
 
     if (!set_info) return 1;
@@ -586,6 +587,14 @@ int set_sd_defaults_from_token( struct object *obj, const struct security_descri
     sacl = sd_get_sacl( sd, &present );
     if (set_info & SACL_SECURITY_INFORMATION && present)
         new_sd.sacl_len = sd->sacl_len;
+    else if (set_info & LABEL_SECURITY_INFORMATION && present)
+    {
+        const ACL *old_sacl = NULL;
+        if (obj->sd && obj->sd->control & SE_SACL_PRESENT) old_sacl = sd_get_sacl( obj->sd, &present );
+        if (!(replaced_sacl = replace_security_labels( old_sacl, sacl ))) return 0;
+        new_sd.sacl_len = replaced_sacl->AclSize;
+        sacl = replaced_sacl;
+    }
     else
     {
         if (obj->sd) sacl = sd_get_sacl( obj->sd, &present );
@@ -616,7 +625,11 @@ int set_sd_defaults_from_token( struct object *obj, const struct security_descri
 
     ptr = mem_alloc( sizeof(new_sd) + new_sd.owner_len + new_sd.group_len +
                      new_sd.sacl_len + new_sd.dacl_len );
-    if (!ptr) return 0;
+    if (!ptr)
+    {
+        free( replaced_sacl );
+        return 0;
+    }
     new_sd_ptr = (struct security_descriptor*)ptr;
 
     memcpy( ptr, &new_sd, sizeof(new_sd) );
@@ -629,6 +642,7 @@ int set_sd_defaults_from_token( struct object *obj, const struct security_descri
     ptr += new_sd.sacl_len;
     memcpy( ptr, dacl, new_sd.dacl_len );
 
+    free( replaced_sacl );
     free( obj->sd );
     obj->sd = new_sd_ptr;
     return 1;
diff --git a/server/security.h b/server/security.h
index 0c6f1815854ae0b4fd283cccbc466aa58d848be8..5c76821803236494c8f1b4c1fbedca770b3f7b53 100644
--- a/server/security.h
+++ b/server/security.h
@@ -97,6 +97,7 @@ static inline int thread_single_check_privilege( struct thread *thread, const LU
 
 extern int sd_is_valid( const struct security_descriptor *sd, data_size_t size );
 extern ACL *extract_security_labels( const ACL *sacl );
+extern ACL *replace_security_labels( const ACL *old_sacl, const ACL *new_sacl );
 
 /* gets the discretionary access control list from a security descriptor */
 static inline const ACL *sd_get_dacl( const struct security_descriptor *sd, int *present )
diff --git a/server/token.c b/server/token.c
index 56529f2ff4468dab617cb1f2d7fa9316e511a2fb..918359503669dba83fcb7bec2b9d7b55fde30396 100644
--- a/server/token.c
+++ b/server/token.c
@@ -377,6 +377,75 @@ ACL *extract_security_labels( const ACL *sacl )
     return label_acl;
 }
 
+/* replace security labels in an existing SACL */
+ACL *replace_security_labels( const ACL *old_sacl, const ACL *new_sacl )
+{
+    const ACE_HEADER *ace;
+    ACE_HEADER *replaced_ace;
+    size_t size = sizeof(ACL);
+    unsigned int i, count = 0;
+    BYTE revision = ACL_REVISION;
+    ACL *replaced_acl;
+
+    if (old_sacl)
+    {
+        revision = max( revision, old_sacl->AclRevision );
+        ace = (const ACE_HEADER *)(old_sacl + 1);
+        for (i = 0; i < old_sacl->AceCount; i++, ace = ace_next( ace ))
+        {
+            if (ace->AceType == SYSTEM_MANDATORY_LABEL_ACE_TYPE) continue;
+            size += ace->AceSize;
+            count++;
+        }
+    }
+
+    if (new_sacl)
+    {
+        revision = max( revision, new_sacl->AclRevision );
+        ace = (const ACE_HEADER *)(new_sacl + 1);
+        for (i = 0; i < new_sacl->AceCount; i++, ace = ace_next( ace ))
+        {
+            if (ace->AceType != SYSTEM_MANDATORY_LABEL_ACE_TYPE) continue;
+            size += ace->AceSize;
+            count++;
+        }
+    }
+
+    replaced_acl = mem_alloc( size );
+    if (!replaced_acl) return NULL;
+
+    replaced_acl->AclRevision = revision;
+    replaced_acl->Sbz1 = 0;
+    replaced_acl->AclSize = size;
+    replaced_acl->AceCount = count;
+    replaced_acl->Sbz2 = 0;
+    replaced_ace = (ACE_HEADER *)(replaced_acl + 1);
+
+    if (old_sacl)
+    {
+        ace = (const ACE_HEADER *)(old_sacl + 1);
+        for (i = 0; i < old_sacl->AceCount; i++, ace = ace_next( ace ))
+        {
+            if (ace->AceType == SYSTEM_MANDATORY_LABEL_ACE_TYPE) continue;
+            memcpy( replaced_ace, ace, ace->AceSize );
+            replaced_ace = (ACE_HEADER *)ace_next( replaced_ace );
+        }
+    }
+
+    if (new_sacl)
+    {
+        ace = (const ACE_HEADER *)(new_sacl + 1);
+        for (i = 0; i < new_sacl->AceCount; i++, ace = ace_next( ace ))
+        {
+            if (ace->AceType != SYSTEM_MANDATORY_LABEL_ACE_TYPE) continue;
+            memcpy( replaced_ace, ace, ace->AceSize );
+            replaced_ace = (ACE_HEADER *)ace_next( replaced_ace );
+        }
+    }
+
+    return replaced_acl;
+}
+
 /* maps from generic rights to specific rights as given by a mapping */
 static inline void map_generic_mask(unsigned int *mask, const GENERIC_MAPPING *mapping)
 {