ntdll/server: Implement NtSetSecurityObject. With tests.
diff --git a/dlls/ntdll/sec.c b/dlls/ntdll/sec.c
index b4e5773..7380786 100644
--- a/dlls/ntdll/sec.c
+++ b/dlls/ntdll/sec.c
@@ -1539,15 +1539,82 @@
 
 /******************************************************************************
  *  NtSetSecurityObject		[NTDLL.@]
+ *  ZwSetSecurityObject		[NTDLL.@]
+ *
+ * Sets specified parts of the object's security descriptor.
+ *
+ * PARAMS
+ *  Handle              [I] Handle to the object to change security descriptor of.
+ *  SecurityInformation [I] Specifies which parts of the security descriptor to set.
+ *  SecurityDescriptor  [I] New parts of a security descriptor for the object.
+ *
+ * RETURNS
+ *  NTSTATUS code.
+ *
  */
-NTSTATUS WINAPI
-NtSetSecurityObject(
-        IN HANDLE Handle,
-        IN SECURITY_INFORMATION SecurityInformation,
-        IN PSECURITY_DESCRIPTOR SecurityDescriptor)
+NTSTATUS WINAPI NtSetSecurityObject(HANDLE Handle,
+        SECURITY_INFORMATION SecurityInformation,
+        PSECURITY_DESCRIPTOR SecurityDescriptor)
 {
-	FIXME("%p 0x%08x %p\n", Handle, SecurityInformation, SecurityDescriptor);
-	return STATUS_SUCCESS;
+    NTSTATUS status;
+    struct security_descriptor sd;
+    PACL dacl, sacl;
+    PSID owner, group;
+    BOOLEAN defaulted, present;
+    DWORD revision;
+    SECURITY_DESCRIPTOR_CONTROL control;
+
+    TRACE("%p 0x%08x %p\n", Handle, SecurityInformation, SecurityDescriptor);
+
+    if (!SecurityDescriptor) return STATUS_ACCESS_VIOLATION;
+
+    memset( &sd, 0, sizeof(sd) );
+    RtlGetControlSecurityDescriptor( SecurityDescriptor, &control, &revision );
+    sd.control = control & ~SE_SELF_RELATIVE;
+
+    if (SecurityInformation & OWNER_SECURITY_INFORMATION)
+    {
+        RtlGetOwnerSecurityDescriptor( SecurityDescriptor, &owner, &defaulted );
+        if (!(sd.owner_len = RtlLengthSid( owner )))
+            return STATUS_INVALID_SECURITY_DESCR;
+    }
+
+    if (SecurityInformation & GROUP_SECURITY_INFORMATION)
+    {
+        RtlGetGroupSecurityDescriptor( SecurityDescriptor, &group, &defaulted );
+        if (!(sd.group_len = RtlLengthSid( group )))
+            return STATUS_INVALID_SECURITY_DESCR;
+    }
+
+    if (SecurityInformation & SACL_SECURITY_INFORMATION)
+    {
+        RtlGetSaclSecurityDescriptor( SecurityDescriptor, &present, &sacl, &defaulted );
+        sd.sacl_len = present ? sacl->AclSize : 0;
+        sd.control |= SE_SACL_PRESENT;
+    }
+
+    if (SecurityInformation & DACL_SECURITY_INFORMATION)
+    {
+        RtlGetDaclSecurityDescriptor( SecurityDescriptor, &present, &dacl, &defaulted );
+        sd.dacl_len = present ? dacl->AclSize : 0;
+        sd.control |= SE_DACL_PRESENT;
+    }
+
+    SERVER_START_REQ( set_security_object )
+    {
+        req->handle = Handle;
+        req->security_info = SecurityInformation;
+
+        wine_server_add_data( req, &sd, sizeof(sd) );
+        wine_server_add_data( req, owner, sd.owner_len );
+        wine_server_add_data( req, group, sd.group_len );
+        wine_server_add_data( req, sacl, sd.sacl_len );
+        wine_server_add_data( req, dacl, sd.dacl_len );
+        status = wine_server_call( req );
+    }
+    SERVER_END_REQ;
+
+    return status;
 }
 
 /******************************************************************************
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index be21fc4..4e7cec0 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -3836,6 +3836,18 @@
     /* VARARG(user,token_groups); */
 };
 
+struct set_security_object_request
+{
+    struct request_header __header;
+    obj_handle_t    handle;
+    unsigned int    security_info;
+    /* VARARG(sd,security_descriptor); */
+};
+struct set_security_object_reply
+{
+    struct reply_header __header;
+};
+
 
 struct create_mailslot_request
 {
@@ -4189,6 +4201,7 @@
     REQ_access_check,
     REQ_get_token_user,
     REQ_get_token_groups,
+    REQ_set_security_object,
     REQ_create_mailslot,
     REQ_open_mailslot,
     REQ_set_mailslot_info,
@@ -4411,6 +4424,7 @@
     struct access_check_request access_check_request;
     struct get_token_user_request get_token_user_request;
     struct get_token_groups_request get_token_groups_request;
+    struct set_security_object_request set_security_object_request;
     struct create_mailslot_request create_mailslot_request;
     struct open_mailslot_request open_mailslot_request;
     struct set_mailslot_info_request set_mailslot_info_request;
@@ -4631,6 +4645,7 @@
     struct access_check_reply access_check_reply;
     struct get_token_user_reply get_token_user_reply;
     struct get_token_groups_reply get_token_groups_reply;
+    struct set_security_object_reply set_security_object_reply;
     struct create_mailslot_reply create_mailslot_reply;
     struct open_mailslot_reply open_mailslot_reply;
     struct set_mailslot_info_reply set_mailslot_info_reply;
@@ -4642,6 +4657,6 @@
     struct get_object_info_reply get_object_info_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 275
+#define SERVER_PROTOCOL_VERSION 276
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/protocol.def b/server/protocol.def
index f49f331..2073a20 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -2759,6 +2759,12 @@
     VARARG(user,token_groups); /* groups the token's user belongs to */
 @END
 
+@REQ(set_security_object)
+    obj_handle_t    handle;       /* handle to the object */
+    unsigned int    security_info; /* which parts of security descriptor to set */
+    VARARG(sd,security_descriptor); /* security descriptor to set */
+@END
+
 /* Create a mailslot */
 @REQ(create_mailslot)
     unsigned int   access;        /* wanted access rights */
diff --git a/server/request.h b/server/request.h
index b52c1aa..9119ec1 100644
--- a/server/request.h
+++ b/server/request.h
@@ -316,6 +316,7 @@
 DECL_HANDLER(access_check);
 DECL_HANDLER(get_token_user);
 DECL_HANDLER(get_token_groups);
+DECL_HANDLER(set_security_object);
 DECL_HANDLER(create_mailslot);
 DECL_HANDLER(open_mailslot);
 DECL_HANDLER(set_mailslot_info);
@@ -537,6 +538,7 @@
     (req_handler)req_access_check,
     (req_handler)req_get_token_user,
     (req_handler)req_get_token_groups,
+    (req_handler)req_set_security_object,
     (req_handler)req_create_mailslot,
     (req_handler)req_open_mailslot,
     (req_handler)req_set_mailslot_info,
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 );
+}
diff --git a/server/trace.c b/server/trace.c
index f9ff323..2909ded 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -3309,6 +3309,14 @@
     dump_varargs_token_groups( cur_size );
 }
 
+static void dump_set_security_object_request( const struct set_security_object_request *req )
+{
+    fprintf( stderr, " handle=%p,", req->handle );
+    fprintf( stderr, " security_info=%08x,", req->security_info );
+    fprintf( stderr, " sd=" );
+    dump_varargs_security_descriptor( cur_size );
+}
+
 static void dump_create_mailslot_request( const struct create_mailslot_request *req )
 {
     fprintf( stderr, " access=%08x,", req->access );
@@ -3642,6 +3650,7 @@
     (dump_func)dump_access_check_request,
     (dump_func)dump_get_token_user_request,
     (dump_func)dump_get_token_groups_request,
+    (dump_func)dump_set_security_object_request,
     (dump_func)dump_create_mailslot_request,
     (dump_func)dump_open_mailslot_request,
     (dump_func)dump_set_mailslot_info_request,
@@ -3860,6 +3869,7 @@
     (dump_func)dump_access_check_reply,
     (dump_func)dump_get_token_user_reply,
     (dump_func)dump_get_token_groups_reply,
+    (dump_func)0,
     (dump_func)dump_create_mailslot_reply,
     (dump_func)dump_open_mailslot_reply,
     (dump_func)dump_set_mailslot_info_reply,
@@ -4078,6 +4088,7 @@
     "access_check",
     "get_token_user",
     "get_token_groups",
+    "set_security_object",
     "create_mailslot",
     "open_mailslot",
     "set_mailslot_info",