ntdll/server: Implement NtSetSecurityObject. With tests.
diff --git a/server/token.c b/server/token.c
index 9a59188..88558e6 100644
--- a/server/token.c
+++ b/server/token.c
@@ -886,6 +886,93 @@
     return token->default_dacl;
 }
 
+static void set_object_sd( struct object *obj, const struct security_descriptor *sd,
+                           unsigned int set_info )
+{
+    struct security_descriptor new_sd, *pnew_sd;
+    int present;
+    const SID *owner, *group;
+    const ACL *sacl, *dacl;
+    char *ptr;
+
+    if (!set_info) return;
+
+    new_sd.control = sd->control & ~SE_SELF_RELATIVE;
+
+    owner = sd_get_owner( sd );
+    if (set_info & OWNER_SECURITY_INFORMATION && owner)
+        new_sd.owner_len = sd->owner_len;
+    else
+    {
+        owner = current->process->token->user;
+        new_sd.owner_len = FIELD_OFFSET(SID, SubAuthority[owner->SubAuthorityCount]);
+        new_sd.control |= SE_OWNER_DEFAULTED;
+    }
+
+    group = sd_get_group( sd );
+    if (set_info & GROUP_SECURITY_INFORMATION && group)
+        new_sd.group_len = sd->group_len;
+    else
+    {
+        group = current->process->token->primary_group;
+        new_sd.group_len = FIELD_OFFSET(SID, SubAuthority[group->SubAuthorityCount]);
+        new_sd.control |= SE_GROUP_DEFAULTED;
+    }
+
+    new_sd.control |= SE_SACL_PRESENT;
+    sacl = sd_get_sacl( sd, &present );
+    if (set_info & SACL_SECURITY_INFORMATION && present)
+        new_sd.sacl_len = sd->sacl_len;
+    else
+    {
+        if (obj->sd) sacl = sd_get_sacl( obj->sd, &present );
+
+        if (obj->sd && present)
+            new_sd.sacl_len = obj->sd->sacl_len;
+        else
+        {
+            new_sd.sacl_len = 0;
+            new_sd.control |= SE_SACL_DEFAULTED;
+        }
+    }
+
+    new_sd.control |= SE_DACL_PRESENT;
+    dacl = sd_get_dacl( sd, &present );
+    if (set_info & DACL_SECURITY_INFORMATION && present)
+        new_sd.dacl_len = sd->dacl_len;
+    else
+    {
+        if (obj->sd) dacl = sd_get_dacl( obj->sd, &present );
+
+        if (obj->sd && present)
+            new_sd.dacl_len = obj->sd->dacl_len;
+        else
+        {
+            dacl = token_get_default_dacl( current->process->token );
+            new_sd.dacl_len = dacl->AclSize;
+            new_sd.control |= SE_DACL_DEFAULTED;
+        }
+    }
+
+    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;
+    pnew_sd = (struct security_descriptor*)ptr;
+
+    memcpy( ptr, &new_sd, sizeof(new_sd) );
+    ptr += sizeof(new_sd);
+    memcpy( ptr, owner, new_sd.owner_len );
+    ptr += new_sd.owner_len;
+    memcpy( ptr, group, new_sd.group_len );
+    ptr += new_sd.group_len;
+    memcpy( ptr, sacl, new_sd.sacl_len );
+    ptr += new_sd.sacl_len;
+    memcpy( ptr, dacl, new_sd.dacl_len );
+
+    free( obj->sd );
+    obj->sd = pnew_sd;
+}
+
 /* open a security token */
 DECL_HANDLER(open_token)
 {
@@ -1206,3 +1293,30 @@
         release_object( token );
     }
 }
+
+DECL_HANDLER(set_security_object)
+{
+    data_size_t sd_size = get_req_data_size();
+    const struct security_descriptor *sd = get_req_data();
+    struct object *obj;
+    unsigned int access = 0;
+
+    if (!sd_is_valid( sd, sd_size ))
+    {
+        set_error( STATUS_ACCESS_VIOLATION );
+        return;
+    }
+
+    if (req->security_info & OWNER_SECURITY_INFORMATION ||
+        req->security_info & GROUP_SECURITY_INFORMATION)
+        access |= WRITE_OWNER;
+    if (req->security_info & SACL_SECURITY_INFORMATION)
+        access |= ACCESS_SYSTEM_SECURITY;
+    if (req->security_info & DACL_SECURITY_INFORMATION)
+        access |= WRITE_DAC;
+
+    if (!(obj = get_handle_obj( current->process, req->handle, access, NULL ))) return;
+
+    set_object_sd( obj, sd, req->security_info );
+    release_object( obj );
+}