From e571cdf9236e083f196c09d0fc96ed7c98fae440 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/kernel32/process.c | 27 +++++++++++++++------------
 server/process.c        | 39 +++++++++++++++++++++++++++++++++++----
 server/process.h        |  2 +-
 server/protocol.def     |  1 +
 server/request.c        |  2 +-
 server/security.h       |  2 ++
 server/token.c          | 11 +++++++++++
 7 files changed, 66 insertions(+), 18 deletions(-)

diff --git a/dlls/kernel32/process.c b/dlls/kernel32/process.c
index bb6cc3e..7b202db 100644
--- a/dlls/kernel32/process.c
+++ b/dlls/kernel32/process.c
@@ -2289,7 +2289,7 @@ static BOOL replace_process( HANDLE handle, const RTL_USER_PROCESS_PARAMETERS *p
  * Create a new process. If hFile is a valid handle we have an exe
  * file, otherwise it is a Winelib app.
  */
-static BOOL create_process( HANDLE hFile, LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES tsa,
+static BOOL create_process( HANDLE token, HANDLE hFile, LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES tsa,
                             BOOL inherit, DWORD flags, const RTL_USER_PROCESS_PARAMETERS *params,
                             LPPROCESS_INFORMATION info, LPCSTR unixdir, const pe_image_info_t *pe_info )
 {
@@ -2356,6 +2356,7 @@ static BOOL create_process( HANDLE hFile, LPSECURITY_ATTRIBUTES psa, LPSECURITY_
         req->access         = PROCESS_ALL_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_end - params->Environment) * sizeof(WCHAR) );
@@ -2526,7 +2527,7 @@ static RTL_USER_PROCESS_PARAMETERS *get_vdm_params( const RTL_USER_PROCESS_PARAM
  *
  * Create a new VDM process for a 16-bit or DOS application.
  */
-static BOOL create_vdm_process( LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES tsa,
+static BOOL create_vdm_process( HANDLE token, LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES tsa,
                                 BOOL inherit, DWORD flags, const RTL_USER_PROCESS_PARAMETERS *params,
                                 LPPROCESS_INFORMATION info, LPCSTR unixdir )
 {
@@ -2536,7 +2537,7 @@ static BOOL create_vdm_process( LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES
 
     if (!(new_params = get_vdm_params( params, &pe_info ))) return FALSE;
 
-    ret = create_process( 0, psa, tsa, inherit, flags, new_params, info, unixdir, &pe_info );
+    ret = create_process( token, 0, psa, tsa, inherit, flags, new_params, info, unixdir, &pe_info );
     RtlDestroyProcessParameters( new_params );
     return ret;
 }
@@ -2547,7 +2548,7 @@ static BOOL create_vdm_process( LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES
  *
  * Create a new cmd shell process for a .BAT file.
  */
-static BOOL create_cmd_process( LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES tsa,
+static BOOL create_cmd_process( HANDLE token, LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES tsa,
                                 BOOL inherit, DWORD flags, LPSTARTUPINFOW startup,
                                 const RTL_USER_PROCESS_PARAMETERS *params,
                                 LPPROCESS_INFORMATION info )
@@ -2577,9 +2578,9 @@ static BOOL create_cmd_process( LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES
     strcatW( newcmdline, params->CommandLine.Buffer );
     strcatW( newcmdline, quotW );
     if (params->CurrentDirectory.DosPath.Length) cur_dir = params->CurrentDirectory.DosPath.Buffer;
-    ret = CreateProcessW( comspec, newcmdline, psa, tsa, inherit,
+    ret = CreateProcessInternalW( token, comspec, newcmdline, psa, tsa, inherit,
                           flags | CREATE_UNICODE_ENVIRONMENT, params->Environment, cur_dir,
-                          startup, info );
+                          startup, info, NULL );
     HeapFree( GetProcessHeap(), 0, newcmdline );
     return ret;
 }
@@ -2692,7 +2693,9 @@ BOOL WINAPI CreateProcessInternalW( HANDLE token, LPCWSTR app_name, LPWSTR cmd_l
 
     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 (!(tidy_cmdline = get_file_name( app_name, cmd_line, name, ARRAY_SIZE( name ), &hFile, &is_64bit )))
@@ -2760,17 +2763,17 @@ BOOL WINAPI CreateProcessInternalW( HANDLE token, LPCWSTR app_name, LPWSTR cmd_l
                debugstr_w(name), is_64bit_arch(pe_info.cpu) ? 64 : 32,
                wine_dbgstr_longlong(pe_info.base), wine_dbgstr_longlong(pe_info.base + pe_info.map_size),
                cpu_names[pe_info.cpu] );
-        retv = create_process( hFile, process_attr, thread_attr,
+        retv = create_process( token, hFile, process_attr, thread_attr,
                                inherit, flags, params, info, unixdir, &pe_info );
         break;
     case BINARY_WIN16:
         TRACE( "starting %s as Win16/DOS binary\n", debugstr_w(name) );
-        retv = create_vdm_process( process_attr, thread_attr, inherit, flags, params, info, unixdir );
+        retv = create_vdm_process( token, process_attr, thread_attr, inherit, flags, params, info, unixdir );
         break;
     case BINARY_UNIX_LIB:
         TRACE( "starting %s as %d-bit Winelib app\n",
                debugstr_w(name), is_64bit_arch(pe_info.cpu) ? 64 : 32 );
-        retv = create_process( hFile, process_attr, thread_attr,
+        retv = create_process( token, hFile, process_attr, thread_attr,
                                inherit, flags, params, info, unixdir, &pe_info );
         break;
     case BINARY_UNKNOWN:
@@ -2780,14 +2783,14 @@ BOOL WINAPI CreateProcessInternalW( HANDLE token, LPCWSTR app_name, LPWSTR cmd_l
             if (!strcmpiW( p, comW ) || !strcmpiW( p, pifW ))
             {
                 TRACE( "starting %s as DOS binary\n", debugstr_w(name) );
-                retv = create_vdm_process( process_attr, thread_attr,
+                retv = create_vdm_process( token, process_attr, thread_attr,
                                            inherit, flags, params, info, unixdir );
                 break;
             }
             if (!strcmpiW( p, batW ) || !strcmpiW( p, cmdW ) )
             {
                 TRACE( "starting %s as batch binary\n", debugstr_w(name) );
-                retv = create_cmd_process( process_attr, thread_attr,
+                retv = create_cmd_process( token, process_attr, thread_attr,
                                            inherit, flags, startup_info, params, info );
                 break;
             }
diff --git a/server/process.c b/server/process.c
index af2ba3d..57bf0ec 100644
--- a/server/process.c
+++ b/server/process.c
@@ -486,7 +486,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;
 
@@ -561,7 +561,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;
@@ -1127,6 +1127,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 = current->process;
     int socket_fd = thread_get_inflight_fd( current, req->socket_fd );
 
@@ -1167,10 +1168,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 );
         return;
     }
     info->process  = NULL;
@@ -1217,7 +1247,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 );
 
@@ -1279,6 +1309,7 @@ DECL_HANDLER(new_process)
     reply->handle = alloc_handle_no_access_check( parent, process, req->access, objattr->attributes );
 
  done:
+    if (token) release_object( token );
     if (process) release_object( process );
     release_object( info );
 }
@@ -1311,7 +1342,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 029b328..ea28091 100644
--- a/server/process.h
+++ b/server/process.h
@@ -116,7 +116,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 607644e..1b5e6de 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -744,6 +744,7 @@ struct rawinput_device
     unsigned int access;         /* access rights for process object */
     cpu_type_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 c10c4e6..86f293f 100644
--- a/server/request.c
+++ b/server/request.c
@@ -577,7 +577,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 21e90cc..32dfe5f 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 1184241..5db97b4 100644
--- a/server/token.c
+++ b/server/token.c
@@ -836,6 +836,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;
@@ -1262,6 +1268,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;
-- 
1.9.1

