From 9c61f6acfa2c43e43f07fae1a5cd447573b9529b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Sun, 6 Aug 2017 02:08:05 +0200
Subject: [PATCH] server: Implement support for creating processes using a
 token.

---
 dlls/kernelbase/process.c | 24 +++++++++++++-----------
 dlls/ntdll/unix/process.c |  1 +
 server/process.c          | 39 +++++++++++++++++++++++++++++++++++----
 server/process.h          |  2 +-
 server/protocol.def       |  1 +
 server/request.c          |  2 +-
 server/security.h         |  2 ++
 server/token.c            | 11 +++++++++++
 8 files changed, 65 insertions(+), 17 deletions(-)

diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c
index a3b168543fc..b5c8b47239d 100644
--- a/dlls/kernelbase/process.c
+++ b/dlls/kernelbase/process.c
@@ -244,7 +244,7 @@ static RTL_USER_PROCESS_PARAMETERS *create_process_params( const WCHAR *filename
 /***********************************************************************
  *           create_nt_process
  */
-static NTSTATUS create_nt_process( SECURITY_ATTRIBUTES *psa, SECURITY_ATTRIBUTES *tsa,
+static NTSTATUS create_nt_process( HANDLE token, SECURITY_ATTRIBUTES *psa, SECURITY_ATTRIBUTES *tsa,
                                    BOOL inherit, DWORD flags, RTL_USER_PROCESS_PARAMETERS *params,
                                    RTL_USER_PROCESS_INFORMATION *info, HANDLE parent )
 {
@@ -259,7 +259,7 @@ static NTSTATUS create_nt_process( SECURITY_ATTRIBUTES *psa, SECURITY_ATTRIBUTES
         status = RtlCreateUserProcess( &nameW, OBJ_CASE_INSENSITIVE, params,
                                        psa ? psa->lpSecurityDescriptor : NULL,
                                        tsa ? tsa->lpSecurityDescriptor : NULL,
-                                       parent, inherit, 0, 0, info );
+                                       parent, inherit, 0, token, info );
         RtlFreeUnicodeString( &nameW );
     }
     return status;
@@ -269,7 +269,7 @@ static NTSTATUS create_nt_process( SECURITY_ATTRIBUTES *psa, SECURITY_ATTRIBUTES
 /***********************************************************************
  *           create_vdm_process
  */
-static NTSTATUS create_vdm_process( SECURITY_ATTRIBUTES *psa, SECURITY_ATTRIBUTES *tsa,
+static NTSTATUS create_vdm_process( HANDLE token, SECURITY_ATTRIBUTES *psa, SECURITY_ATTRIBUTES *tsa,
                                     BOOL inherit, DWORD flags, RTL_USER_PROCESS_PARAMETERS *params,
                                     RTL_USER_PROCESS_INFORMATION *info )
 {
@@ -290,7 +290,7 @@ static NTSTATUS create_vdm_process( SECURITY_ATTRIBUTES *psa, SECURITY_ATTRIBUTE
               winevdm, params->ImagePathName.Buffer, params->CommandLine.Buffer );
     RtlInitUnicodeString( &params->ImagePathName, winevdm );
     RtlInitUnicodeString( &params->CommandLine, newcmdline );
-    status = create_nt_process( psa, tsa, inherit, flags, params, info, NULL );
+    status = create_nt_process( token, psa, tsa, inherit, flags, params, info, NULL );
     HeapFree( GetProcessHeap(), 0, newcmdline );
     return status;
 }
@@ -299,7 +299,7 @@ static NTSTATUS create_vdm_process( SECURITY_ATTRIBUTES *psa, SECURITY_ATTRIBUTE
 /***********************************************************************
  *           create_cmd_process
  */
-static NTSTATUS create_cmd_process( SECURITY_ATTRIBUTES *psa, SECURITY_ATTRIBUTES *tsa,
+static NTSTATUS create_cmd_process( HANDLE token, SECURITY_ATTRIBUTES *psa, SECURITY_ATTRIBUTES *tsa,
                                     BOOL inherit, DWORD flags, RTL_USER_PROCESS_PARAMETERS *params,
                                     RTL_USER_PROCESS_INFORMATION *info )
 {
@@ -318,7 +318,7 @@ static NTSTATUS create_cmd_process( SECURITY_ATTRIBUTES *psa, SECURITY_ATTRIBUTE
     swprintf( newcmdline, len, L"%s /s/c \"%s\"", comspec, params->CommandLine.Buffer );
     RtlInitUnicodeString( &params->ImagePathName, comspec );
     RtlInitUnicodeString( &params->CommandLine, newcmdline );
-    status = create_nt_process( psa, tsa, inherit, flags, params, info, NULL );
+    status = create_nt_process( token, psa, tsa, inherit, flags, params, info, NULL );
     RtlFreeHeap( GetProcessHeap(), 0, newcmdline );
     return status;
 }
@@ -450,7 +450,9 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR
 
     TRACE( "app %s cmdline %s\n", debugstr_w(app_name), debugstr_w(cmd_line) );
 
-    if (token) FIXME( "Creating a process with a token is not yet implemented\n" );
+    /* FIXME: Starting a process which requires admin rights should fail
+     * with ERROR_ELEVATION_REQUIRED when no token is passed. */
+
     if (new_token) FIXME( "No support for returning created process token\n" );
 
     if (app_name)
@@ -523,7 +525,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR
         }
     }
 
-    status = create_nt_process( process_attr, thread_attr, inherit, flags, params, &rtl_info, parent );
+    status = create_nt_process( token, process_attr, thread_attr, inherit, flags, params, &rtl_info, parent );
     switch (status)
     {
     case STATUS_SUCCESS:
@@ -532,7 +534,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR
     case STATUS_INVALID_IMAGE_NE_FORMAT:
     case STATUS_INVALID_IMAGE_PROTECT:
         TRACE( "starting %s as Win16/DOS binary\n", debugstr_w(app_name) );
-        status = create_vdm_process( process_attr, thread_attr, inherit, flags, params, &rtl_info );
+        status = create_vdm_process( token, process_attr, thread_attr, inherit, flags, params, &rtl_info );
         break;
     case STATUS_INVALID_IMAGE_NOT_MZ:
         /* check for .com or .bat extension */
@@ -540,12 +542,12 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR
         if (!wcsicmp( p, L".com" ) || !wcsicmp( p, L".pif" ))
         {
             TRACE( "starting %s as DOS binary\n", debugstr_w(app_name) );
-            status = create_vdm_process( process_attr, thread_attr, inherit, flags, params, &rtl_info );
+            status = create_vdm_process( token, process_attr, thread_attr, inherit, flags, params, &rtl_info );
         }
         else if (!wcsicmp( p, L".bat" ) || !wcsicmp( p, L".cmd" ))
         {
             TRACE( "starting %s as batch binary\n", debugstr_w(app_name) );
-            status = create_cmd_process( process_attr, thread_attr, inherit, flags, params, &rtl_info );
+            status = create_cmd_process( token, process_attr, thread_attr, inherit, flags, params, &rtl_info );
         }
         break;
     }
diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c
index cca6c2747bf..379a0036b63 100644
--- a/dlls/ntdll/unix/process.c
+++ b/dlls/ntdll/unix/process.c
@@ -827,6 +827,7 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_
         req->access         = process_access;
         req->cpu            = pe_info.cpu;
         req->info_size      = startup_info_size;
+        req->token          = wine_server_obj_handle( token );
         wine_server_add_data( req, objattr, attr_len );
         wine_server_add_data( req, startup_info, startup_info_size );
         wine_server_add_data( req, params->Environment, env_size );
diff --git a/server/process.c b/server/process.c
index 52604ec4d61..047916ffd09 100644
--- a/server/process.c
+++ b/server/process.c
@@ -499,7 +499,7 @@ static void start_sigkill_timer( struct process *process )
 /* create a new process */
 /* if the function fails the fd is closed */
 struct process *create_process( int fd, struct process *parent, int inherit_all,
-                                const struct security_descriptor *sd )
+                                const struct security_descriptor *sd, struct token *token )
 {
     struct process *process;
 
@@ -576,7 +576,7 @@ struct process *create_process( int fd, struct process *parent, int inherit_all,
                                        : alloc_handle_table( process, 0 );
         /* Note: for security reasons, starting a new process does not attempt
          * to use the current impersonation token for the new process */
-        process->token = token_duplicate( parent->token, TRUE, 0, NULL, NULL, 0, NULL, 0 );
+        process->token = token_duplicate( token ? token : parent->token, TRUE, 0, NULL, NULL, 0, NULL, 0 );
         process->affinity = parent->affinity;
     }
     if (!process->handles || !process->token) goto error;
@@ -1132,6 +1132,7 @@ DECL_HANDLER(new_process)
     const struct security_descriptor *sd;
     const struct object_attributes *objattr = get_req_object_attributes( &sd, &name, NULL );
     struct process *process = NULL;
+    struct token *token = NULL;
     struct process *parent;
     struct thread *parent_thread = current;
     int socket_fd = thread_get_inflight_fd( current, req->socket_fd );
@@ -1185,10 +1186,39 @@ DECL_HANDLER(new_process)
         return;
     }
 
+    if (req->token)
+    {
+        token = get_token_from_handle( req->token, TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY );
+        if (!token)
+        {
+            close( socket_fd );
+            return;
+        }
+        if (!token_is_primary( token ))
+        {
+            set_error( STATUS_BAD_TOKEN_TYPE );
+            release_object( token );
+            close( socket_fd );
+            return;
+        }
+    }
+
+    if (!req->info_size)  /* create an orphaned process */
+    {
+        if ((process = create_process( socket_fd, NULL, 0, sd, token )))
+        {
+            create_thread( -1, process, NULL );
+            release_object( process );
+        }
+        if (token) release_object( token );
+        return;
+    }
+
     /* build the startup info for a new process */
     if (!(info = alloc_object( &startup_info_ops )))
     {
         close( socket_fd );
+        if (token) release_object( token );
         release_object( parent );
         return;
     }
@@ -1236,7 +1266,7 @@ DECL_HANDLER(new_process)
 #undef FIXUP_LEN
     }
 
-    if (!(process = create_process( socket_fd, parent, req->inherit_all, sd ))) goto done;
+    if (!(process = create_process( socket_fd, parent, req->inherit_all, sd, token ))) goto done;
 
     process->startup_info = (struct startup_info *)grab_object( info );
 
@@ -1297,6 +1327,7 @@ DECL_HANDLER(new_process)
     reply->handle = alloc_handle_no_access_check( current->process, process, req->access, objattr->attributes );
 
  done:
+    if (token) release_object( token );
     if (process) release_object( process );
     release_object( parent );
     release_object( info );
@@ -1330,7 +1361,7 @@ DECL_HANDLER(exec_process)
         close( socket_fd );
         return;
     }
-    if (!(process = create_process( socket_fd, NULL, 0, NULL ))) return;
+    if (!(process = create_process( socket_fd, NULL, 0, NULL, NULL ))) return;
     create_thread( -1, process, NULL );
     release_object( process );
 }
diff --git a/server/process.h b/server/process.h
index dfe5c4e52d8..61b83abf693 100644
--- a/server/process.h
+++ b/server/process.h
@@ -118,7 +118,7 @@ extern unsigned int alloc_ptid( void *ptr );
 extern void free_ptid( unsigned int id );
 extern void *get_ptid_entry( unsigned int id );
 extern struct process *create_process( int fd, struct process *parent, int inherit_all,
-                                       const struct security_descriptor *sd );
+                                       const struct security_descriptor *sd, struct token *token );
 extern data_size_t init_process( struct thread *thread );
 extern struct thread *get_process_first_thread( struct process *process );
 extern struct process *get_process_from_id( process_id_t id );
diff --git a/server/protocol.def b/server/protocol.def
index 901c380b721..8c86967609f 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -801,6 +801,7 @@ struct rawinput_device
     unsigned int access;         /* access rights for process object */
     client_cpu_t cpu;            /* CPU that the new process will use */
     data_size_t  info_size;      /* size of startup info */
+    obj_handle_t token;          /* token for the new process */
     VARARG(objattr,object_attributes);   /* object attributes */
     VARARG(info,startup_info,info_size); /* startup information */
     VARARG(env,unicode_str);     /* environment for new process */
diff --git a/server/request.c b/server/request.c
index 4c1f30a5fe7..321bb6cfa81 100644
--- a/server/request.c
+++ b/server/request.c
@@ -582,7 +582,7 @@ static void master_socket_poll_event( struct fd *fd, int event )
         int client = accept( get_unix_fd( master_socket->fd ), (struct sockaddr *) &dummy, &len );
         if (client == -1) return;
         fcntl( client, F_SETFL, O_NONBLOCK );
-        if ((process = create_process( client, NULL, 0, NULL )))
+        if ((process = create_process( client, NULL, 0, NULL, NULL )))
         {
             create_thread( -1, process, NULL );
             release_object( process );
diff --git a/server/security.h b/server/security.h
index 21e90ccf23f..32dfe5f8db9 100644
--- a/server/security.h
+++ b/server/security.h
@@ -67,6 +67,8 @@ extern const ACL *token_get_default_dacl( struct token *token );
 extern const SID *token_get_user( struct token *token );
 extern const SID *token_get_primary_group( struct token *token );
 extern int token_sid_present( struct token *token, const SID *sid, int deny);
+extern struct token *get_token_from_handle( obj_handle_t handle, unsigned int access );
+extern int token_is_primary( struct token *token );
 
 static inline const ACE_HEADER *ace_next( const ACE_HEADER *ace )
 {
diff --git a/server/token.c b/server/token.c
index 1c1d49989b3..2f466aa1b25 100644
--- a/server/token.c
+++ b/server/token.c
@@ -843,6 +843,12 @@ int token_assign_label( struct token *token, PSID label )
     return ret;
 }
 
+struct token *get_token_from_handle( obj_handle_t handle, unsigned int access )
+{
+    return (struct token *)get_handle_obj( current->process, handle,
+                                           access, &token_ops );
+}
+
 struct token *token_create_admin( void )
 {
     struct token *token = NULL;
@@ -1269,6 +1275,11 @@ const SID *token_get_primary_group( struct token *token )
     return token->primary_group;
 }
 
+int token_is_primary( struct token *token )
+{
+    return token->primary;
+}
+
 int check_object_access(struct object *obj, unsigned int *access)
 {
     GENERIC_MAPPING mapping;
-- 
2.27.0

