From cdf1f84a65198df1ac4162f868f35971e5e1a2a1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Sat, 5 Aug 2017 01:45:29 +0200
Subject: [PATCH] ntdll: Add function to create new tokens for elevation
 purposes.

---
 dlls/ntdll/ntdll.spec   |  3 ++
 dlls/ntdll/ntdll_misc.h |  3 ++
 dlls/ntdll/process.c    | 18 +++++++++
 server/protocol.def     |  8 ++++
 server/security.h       |  1 +
 server/token.c          | 84 +++++++++++++++++++++++++++++++++++++++++
 6 files changed, 117 insertions(+)

diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
index f604c8a3c35..850a40412d0 100644
--- a/dlls/ntdll/ntdll.spec
+++ b/dlls/ntdll/ntdll.spec
@@ -1599,6 +1599,9 @@
 # Virtual memory
 @ cdecl __wine_locked_recvmsg(long ptr long)
 
+# Token
+@ cdecl __wine_create_default_token(long)
+
 # Version
 @ cdecl wine_get_version()
 @ cdecl wine_get_build_id()
diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h
index 1f27cd100a7..769d6facc9f 100644
--- a/dlls/ntdll/ntdll_misc.h
+++ b/dlls/ntdll/ntdll_misc.h
@@ -68,6 +68,9 @@ extern void init_locale( HMODULE module ) DECLSPEC_HIDDEN;
 extern void init_user_process_params(void) DECLSPEC_HIDDEN;
 extern NTSTATUS restart_process( RTL_USER_PROCESS_PARAMETERS *params, NTSTATUS status ) DECLSPEC_HIDDEN;
 
+/* token */
+extern HANDLE CDECL __wine_create_default_token(BOOL admin);
+
 /* server support */
 extern BOOL is_wow64 DECLSPEC_HIDDEN;
 
diff --git a/dlls/ntdll/process.c b/dlls/ntdll/process.c
index 77ba5b371e2..3e91a1fa9c4 100644
--- a/dlls/ntdll/process.c
+++ b/dlls/ntdll/process.c
@@ -72,6 +72,24 @@ HANDLE CDECL __wine_make_process_system(void)
     return ret;
 }
 
+/***********************************************************************
+ *           __wine_create_default_token   (NTDLL.@)
+ *
+ * Creates a default limited or admin token.
+ */
+HANDLE CDECL __wine_create_default_token( BOOL admin )
+{
+    HANDLE ret = NULL;
+    SERVER_START_REQ( create_token )
+    {
+        req->admin = admin;
+        if (!wine_server_call( req ))
+            ret = wine_server_ptr_handle( reply->token );
+    }
+    SERVER_END_REQ;
+    return ret;
+}
+
 /***********************************************************************
  *           restart_process
  */
diff --git a/server/protocol.def b/server/protocol.def
index 4d37a0df348..56b52dd2231 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -3581,6 +3581,14 @@ struct handle_info
 @END
 
 
+/* Create a new token */
+@REQ(create_token)
+    unsigned int  admin;        /* admin or limited token */
+@REPLY
+    obj_handle_t  token;        /* handle for new token */
+@END
+
+
 /* Create I/O completion port */
 @REQ(create_completion)
     unsigned int access;          /* desired access to a port */
diff --git a/server/security.h b/server/security.h
index 6c337143c3d..21e90ccf23f 100644
--- a/server/security.h
+++ b/server/security.h
@@ -49,6 +49,7 @@ extern const PSID security_builtin_users_sid;
 extern const PSID security_builtin_admins_sid;
 extern const PSID security_domain_users_sid;
 extern const PSID security_high_label_sid;
+extern const PSID security_medium_label_sid;
 
 
 /* token functions */
diff --git a/server/token.c b/server/token.c
index c4f1cd943c2..970ed1838da 100644
--- a/server/token.c
+++ b/server/token.c
@@ -77,6 +77,7 @@ static const SID anonymous_logon_sid = { SID_REVISION, 1, { SECURITY_NT_AUTHORIT
 static const SID authenticated_user_sid = { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_AUTHENTICATED_USER_RID } };
 static const SID local_system_sid = { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_LOCAL_SYSTEM_RID } };
 static const SID high_label_sid = { SID_REVISION, 1, { SECURITY_MANDATORY_LABEL_AUTHORITY }, { SECURITY_MANDATORY_HIGH_RID } };
+static const SID medium_label_sid = { SID_REVISION, 1, { SECURITY_MANDATORY_LABEL_AUTHORITY }, { SECURITY_MANDATORY_MEDIUM_RID } };
 static const SID_N(5) local_user_sid = { SID_REVISION, 5, { SECURITY_NT_AUTHORITY }, { SECURITY_NT_NON_UNIQUE, 0, 0, 0, 1000 } };
 static const SID_N(2) builtin_admins_sid = { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS } };
 static const SID_N(2) builtin_users_sid = { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS } };
@@ -93,6 +94,7 @@ const PSID security_builtin_admins_sid = (PSID)&builtin_admins_sid;
 const PSID security_builtin_users_sid = (PSID)&builtin_users_sid;
 const PSID security_domain_users_sid = (PSID)&domain_users_sid;
 const PSID security_high_label_sid = (PSID)&high_label_sid;
+const PSID security_medium_label_sid = (PSID)&medium_label_sid;
 
 static luid_t prev_luid_value = { 1000, 0 };
 
@@ -915,6 +917,64 @@ struct token *token_create_admin( void )
     return token;
 }
 
+static struct token *token_create_limited( void )
+{
+    struct token *token = NULL;
+    static const SID_IDENTIFIER_AUTHORITY nt_authority = { SECURITY_NT_AUTHORITY };
+    static const unsigned int alias_admins_subauth[] = { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS };
+    static const unsigned int alias_users_subauth[] = { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS };
+    /* on Windows, this value changes every time the user logs on */
+    static const unsigned int logon_subauth[] = { SECURITY_LOGON_IDS_RID, 0, 1 /* FIXME: should be randomly generated when tokens are inherited by new processes */ };
+    PSID alias_admins_sid;
+    PSID alias_users_sid;
+    PSID logon_sid;
+    const SID *user_sid = security_unix_uid_to_sid( getuid() );
+    ACL *default_dacl = create_default_dacl( user_sid );
+
+    alias_admins_sid = security_sid_alloc( &nt_authority, sizeof(alias_admins_subauth)/sizeof(alias_admins_subauth[0]),
+                                           alias_admins_subauth );
+    alias_users_sid = security_sid_alloc( &nt_authority, sizeof(alias_users_subauth)/sizeof(alias_users_subauth[0]),
+                                          alias_users_subauth );
+    logon_sid = security_sid_alloc( &nt_authority, sizeof(logon_subauth)/sizeof(logon_subauth[0]),
+                                    logon_subauth );
+
+    if (alias_admins_sid && alias_users_sid && logon_sid && default_dacl)
+    {
+        const LUID_AND_ATTRIBUTES user_privs[] =
+        {
+            { SeChangeNotifyPrivilege        , SE_PRIVILEGE_ENABLED },
+            { SeShutdownPrivilege            , 0                    },
+            { SeUndockPrivilege              , 0                    },
+        };
+        /* note: we don't include non-builtin groups here for the user -
+         * telling us these is the job of a client-side program */
+        const SID_AND_ATTRIBUTES user_groups[] =
+        {
+            { security_world_sid, SE_GROUP_ENABLED|SE_GROUP_ENABLED_BY_DEFAULT|SE_GROUP_MANDATORY },
+            { security_local_sid, SE_GROUP_ENABLED|SE_GROUP_ENABLED_BY_DEFAULT|SE_GROUP_MANDATORY },
+            { security_interactive_sid, SE_GROUP_ENABLED|SE_GROUP_ENABLED_BY_DEFAULT|SE_GROUP_MANDATORY },
+            { security_authenticated_user_sid, SE_GROUP_ENABLED|SE_GROUP_ENABLED_BY_DEFAULT|SE_GROUP_MANDATORY },
+            { security_domain_users_sid, SE_GROUP_ENABLED|SE_GROUP_ENABLED_BY_DEFAULT|SE_GROUP_MANDATORY|SE_GROUP_OWNER },
+            { alias_admins_sid, SE_GROUP_USE_FOR_DENY_ONLY },
+            { alias_users_sid, SE_GROUP_ENABLED|SE_GROUP_ENABLED_BY_DEFAULT|SE_GROUP_MANDATORY },
+            { logon_sid, SE_GROUP_ENABLED|SE_GROUP_ENABLED_BY_DEFAULT|SE_GROUP_MANDATORY|SE_GROUP_LOGON_ID },
+        };
+        static const TOKEN_SOURCE admin_source = {"SeMgr", {0, 0}};
+        token = create_token( TRUE, user_sid, user_groups, sizeof(user_groups)/sizeof(user_groups[0]),
+                              user_privs, sizeof(user_privs)/sizeof(user_privs[0]), default_dacl,
+                              admin_source, NULL, -1, TokenElevationTypeLimited, &medium_label_sid );
+        /* we really need a primary group */
+        assert( token->primary_group );
+    }
+
+    free( logon_sid );
+    free( alias_admins_sid );
+    free( alias_users_sid );
+    free( default_dacl );
+
+    return token;
+}
+
 static struct privilege *token_find_privilege( struct token *token, const LUID *luid, int enabled_only )
 {
     struct privilege *privilege;
@@ -1720,3 +1780,27 @@ DECL_HANDLER(set_token_default_dacl)
         release_object( token );
     }
 }
+
+DECL_HANDLER(create_token)
+{
+    struct token *token;
+    PSID label;
+
+    if (req->admin)
+    {
+        token = token_create_admin();
+        label = security_high_label_sid;
+    }
+    else
+    {
+        token = token_create_limited();
+        label = security_medium_label_sid;
+    }
+
+    if (token)
+    {
+        if (token_assign_label( token, label ))
+            reply->token = alloc_handle( current->process, token, TOKEN_ALL_ACCESS, 0 );
+        release_object( token );
+    }
+}
-- 
2.27.0

