Basic implementation of token integrity levels and UAC handling Accepted

Revisions: 

Revision 1

user image Michael Müller Author
07 Aug. 17

This patchset is a basic implementation of the UAC features introduced with Windows Vista. In contrast to Windows, we still start processes with a full admin token by default, but one can use runas /trustlevel:0x20000 program.exe to lower the rights. This makes it possible to use applications, which explicitly refuse to work when the current token has admin rights. The code also implements the necessary logic to elevate a process again in case the manifest file of the application requests admin rights. Unlike Windows we don't show any annoying confirmation dialogs though ;-)

user image Sebastian Lackner
07 Aug. 17

I've added an additional patch for a LinkedToken semi-stub implementation. Otherwise it looks good to me.

Single files Merged diff Tar archive
You have unsaved changes. Press CTRL + ENTER in a text field to submit your comments.

0001-advapi32-tests-Extend-security-label-token-integrity.patch

From 899d8330d88e31de4f344e92b0cfc347004fd796 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Mon, 7 Aug 2017 01:25:02 +0200
Subject: advapi32/tests: Extend security label / token integrity tests.
---
dlls/advapi32/tests/Makefile.in | 2 +-
dlls/advapi32/tests/security.c | 389 +++++++++++++++++++++++++++++++++++++++-
2 files changed, 387 insertions(+), 4 deletions(-)
diff --git a/dlls/advapi32/tests/Makefile.in b/dlls/advapi32/tests/Makefile.in
index 36ce031ef62..4437e6e5de7 100644
--- a/dlls/advapi32/tests/Makefile.in
+++ b/dlls/advapi32/tests/Makefile.in
@@ -1,5 +1,5 @@
TESTDLL = advapi32.dll
-IMPORTS = ole32 advapi32
+IMPORTS = ole32 user32 advapi32
C_SRCS = \
cred.c \
diff --git a/dlls/advapi32/tests/security.c b/dlls/advapi32/tests/security.c
index 0fd41fe82fa..4a03db27e69 100644
--- a/dlls/advapi32/tests/security.c
+++ b/dlls/advapi32/tests/security.c
@@ -7191,13 +7191,19 @@ static void test_token_security_descriptor(void)
{
static SID low_level = {SID_REVISION, 1, {SECURITY_MANDATORY_LABEL_AUTHORITY},
{SECURITY_MANDATORY_LOW_RID}};
+ static SID medium_level = {SID_REVISION, 1, {SECURITY_MANDATORY_LABEL_AUTHORITY},
+ {SECURITY_MANDATORY_MEDIUM_RID}};
+ static SID high_level = {SID_REVISION, 1, {SECURITY_MANDATORY_LABEL_AUTHORITY},
+ {SECURITY_MANDATORY_HIGH_RID}};
char buffer_sd[SECURITY_DESCRIPTOR_MIN_LENGTH];
- SECURITY_DESCRIPTOR *sd = (SECURITY_DESCRIPTOR *)&buffer_sd, *sd2;
+ SECURITY_DESCRIPTOR *sd = (SECURITY_DESCRIPTOR *)&buffer_sd, *sd2, *sd3;
char buffer_acl[256], buffer[MAX_PATH];
- ACL *acl = (ACL *)&buffer_acl, *acl2, *acl_child;
+ ACL *acl = (ACL *)&buffer_acl, *acl2, *acl_child, *sacl;
BOOL defaulted, present, ret, found;
- HANDLE token, token2, token3;
+ HANDLE token, token2, token3, token4, token5, token6;
EXPLICIT_ACCESSW exp_access;
+ TOKEN_MANDATORY_LABEL *tml;
+ BYTE buffer_integrity[64];
PROCESS_INFORMATION info;
DWORD size, index, retd;
ACCESS_ALLOWED_ACE *ace;
@@ -7347,6 +7353,185 @@ static void test_token_security_descriptor(void)
/* The security label is also not inherited */
if (pAddMandatoryAce)
{
+ memset(buffer_integrity, 0, sizeof(buffer_integrity));
+ ret = GetTokenInformation(token, TokenIntegrityLevel, buffer_integrity, sizeof(buffer_integrity), &size);
+ ok(ret, "GetTokenInformation failed with error %u\n", GetLastError());
+ tml = (TOKEN_MANDATORY_LABEL *)buffer_integrity;
+ ok(EqualSid(tml->Label.Sid, &medium_level) || EqualSid(tml->Label.Sid, &high_level),
+ "Expected medium or high integrity level\n");
+
+ if (EqualSid(tml->Label.Sid, &high_level))
+ {
+ DWORD process_id;
+ HANDLE process;
+ HWND shell;
+
+ /* This test tries to get a medium token and then impersonates this token. The
+ * idea is to check whether the sd label of a newly created token depends on the
+ * current active token or the integrity level of the newly created token. */
+
+ /* Steal process token of the explorer.exe process */
+ shell = GetShellWindow();
+ todo_wine ok(shell != NULL, "Failed to get shell window\n");
+ if (!shell) shell = GetDesktopWindow(); /* FIXME: Workaround for Wine */
+ ok(GetWindowThreadProcessId(shell, &process_id),
+ "Failed to get process id of shell window: %u\n", GetLastError());
+ process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, process_id);
+ ok(process != NULL, "Failed to open process: %u\n", GetLastError());
+ ok(OpenProcessToken(process, TOKEN_ALL_ACCESS, &token4),
+ "Failed to open process token: %u\n", GetLastError());
+ CloseHandle(process);
+
+ /* Check TokenIntegrityLevel and LABEL_SECURITY_INFORMATION of explorer.exe token */
+ memset(buffer_integrity, 0, sizeof(buffer_integrity));
+ ret = GetTokenInformation(token4, TokenIntegrityLevel, buffer_integrity, sizeof(buffer_integrity), &size);
+ ok(ret, "GetTokenInformation failed with error %u\n", GetLastError());
+ tml = (TOKEN_MANDATORY_LABEL *)buffer_integrity;
+ todo_wine ok(EqualSid(tml->Label.Sid, &medium_level), "Expected medium integrity level\n");
+
+ size = 0;
+ ret = GetKernelObjectSecurity(token4, LABEL_SECURITY_INFORMATION, NULL, 0, &size);
+ ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+ "Unexpected GetKernelObjectSecurity return value %u, error %u\n", ret, GetLastError());
+
+ sd3 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
+ ret = GetKernelObjectSecurity(token4, LABEL_SECURITY_INFORMATION, sd3, size, &size);
+ ok(ret, "GetKernelObjectSecurity failed with error %u\n", GetLastError());
+
+ sacl = NULL;
+ ret = GetSecurityDescriptorSacl(sd3, &present, &sacl, &defaulted);
+ ok(ret, "GetSecurityDescriptorSacl failed with error %u\n", GetLastError());
+ ok(present, "No SACL in the security descriptor\n");
+ ok(sacl != NULL, "NULL SACL in the security descriptor\n");
+
+ if (sacl)
+ {
+ ret = pGetAce(sacl, 0, (void **)&ace);
+ ok(ret, "GetAce failed with error %u\n", GetLastError());
+ ok(ace->Header.AceType == SYSTEM_MANDATORY_LABEL_ACE_TYPE,
+ "Unexpected ACE type %#x\n", ace->Header.AceType);
+ todo_wine ok(EqualSid(&ace->SidStart, &medium_level),
+ "Expected medium integrity level\n");
+ }
+
+ HeapFree(GetProcessHeap(), 0, sd3);
+
+ /* Start child process with the explorer.exe token */
+ memset(&startup, 0, sizeof(startup));
+ startup.cb = sizeof(startup);
+ startup.dwFlags = STARTF_USESHOWWINDOW;
+ startup.wShowWindow = SW_SHOWNORMAL;
+
+ sprintf(buffer, "%s tests/security.c test_token_sd_medium", myARGV[0]);
+ ret = CreateProcessAsUserA(token4, NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info);
+ ok(ret || GetLastError() == ERROR_PRIVILEGE_NOT_HELD,
+ "CreateProcess failed with error %u\n", GetLastError());
+ if (ret)
+ {
+ winetest_wait_child_process(info.hProcess);
+ CloseHandle(info.hProcess);
+ CloseHandle(info.hThread);
+ }
+ else
+ win_skip("Skipping test for creating process with medium level token\n");
+
+ ret = DuplicateTokenEx(token4, 0, NULL, SecurityImpersonation, TokenImpersonation, &token5);
+ ok(ret, "DuplicateTokenEx failed with error %u\n", GetLastError());
+ ret = SetThreadToken(NULL, token5);
+ todo_wine ok(ret, "SetThreadToken failed with error %u\n", GetLastError());
+ CloseHandle(token4);
+
+ /* Restrict current process token while impersonating a medium integrity token */
+ ret = CreateRestrictedToken(token, 0, 0, NULL, 0, NULL, 0, NULL, &token6);
+ ok(ret, "CreateRestrictedToken failed with error %u\n", GetLastError());
+
+ memset(buffer_integrity, 0, sizeof(buffer_integrity));
+ ret = GetTokenInformation(token6, TokenIntegrityLevel, buffer_integrity, sizeof(buffer_integrity), &size);
+ ok(ret, "GetTokenInformation failed with error %u\n", GetLastError());
+ tml = (TOKEN_MANDATORY_LABEL *)buffer_integrity;
+ ok(EqualSid(tml->Label.Sid, &high_level), "Expected high integrity level\n");
+
+ size = 0;
+ ret = GetKernelObjectSecurity(token6, LABEL_SECURITY_INFORMATION, NULL, 0, &size);
+ ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+ "Unexpected GetKernelObjectSecurity return value %u, error %u\n", ret, GetLastError());
+
+ sd3 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
+ ret = GetKernelObjectSecurity(token6, LABEL_SECURITY_INFORMATION, sd3, size, &size);
+ ok(ret, "GetKernelObjectSecurity failed with error %u\n", GetLastError());
+
+ sacl = NULL;
+ ret = GetSecurityDescriptorSacl(sd3, &present, &sacl, &defaulted);
+ ok(ret, "GetSecurityDescriptorSacl failed with error %u\n", GetLastError());
+ todo_wine ok(present, "No SACL in the security descriptor\n");
+ todo_wine ok(sacl != NULL, "NULL SACL in the security descriptor\n");
+
+ if (sacl)
+ {
+ ret = pGetAce(sacl, 0, (void **)&ace);
+ ok(ret, "GetAce failed with error %u\n", GetLastError());
+ ok(ace->Header.AceType == SYSTEM_MANDATORY_LABEL_ACE_TYPE,
+ "Unexpected ACE type %#x\n", ace->Header.AceType);
+ ok(EqualSid(&ace->SidStart, &medium_level),
+ "Expected medium integrity level\n");
+ }
+
+ HeapFree(GetProcessHeap(), 0, sd3);
+ RevertToSelf();
+ CloseHandle(token5);
+
+ /* Start child process with the restricted token */
+ sprintf(buffer, "%s tests/security.c test_token_sd_restricted", myARGV[0]);
+ ret = CreateProcessAsUserA(token6, NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info);
+ ok(ret, "CreateProcess failed with error %u\n", GetLastError());
+ winetest_wait_child_process(info.hProcess);
+ CloseHandle(info.hProcess);
+ CloseHandle(info.hThread);
+ CloseHandle(token6);
+
+ /* DuplicateTokenEx should assign security label even when SA points to empty SD */
+ memset(sd, 0, sizeof(buffer_sd));
+ ret = InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION);
+ ok(ret, "InitializeSecurityDescriptor failed with error %u\n", GetLastError());
+
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.lpSecurityDescriptor = sd;
+ sa.bInheritHandle = FALSE;
+
+ ret = DuplicateTokenEx(token, 0, &sa, 0, TokenPrimary, &token6);
+ ok(ret, "DuplicateTokenEx failed with error %u\n", GetLastError());
+
+ size = 0;
+ ret = GetKernelObjectSecurity(token6, LABEL_SECURITY_INFORMATION, NULL, 0, &size);
+ todo_wine ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+ "Unexpected GetKernelObjectSecurity return value %u, error %u\n", ret, GetLastError());
+
+ sd3 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
+ ret = GetKernelObjectSecurity(token6, LABEL_SECURITY_INFORMATION, sd3, size, &size);
+ todo_wine ok(ret, "GetKernelObjectSecurity failed with error %u\n", GetLastError());
+
+ sacl = NULL;
+ ret = GetSecurityDescriptorSacl(sd3, &present, &sacl, &defaulted);
+ todo_wine ok(ret, "GetSecurityDescriptorSacl failed with error %u\n", GetLastError());
+ todo_wine ok(present, "No SACL in the security descriptor\n");
+ todo_wine ok(sacl != NULL, "NULL SACL in the security descriptor\n");
+
+ if (sacl)
+ {
+ ret = pGetAce(sacl, 0, (void **)&ace);
+ ok(ret, "GetAce failed with error %u\n", GetLastError());
+ ok(ace->Header.AceType == SYSTEM_MANDATORY_LABEL_ACE_TYPE,
+ "Unexpected ACE type %#x\n", ace->Header.AceType);
+ ok(EqualSid(&ace->SidStart, &high_level),
+ "Expected high integrity level\n");
+ }
+
+ HeapFree(GetProcessHeap(), 0, sd3);
+ CloseHandle(token6);
+ }
+ else
+ skip("Skipping test, running without admin rights\n");
+
ret = InitializeAcl(acl, 256, ACL_REVISION);
ok(ret, "InitializeAcl failed with error %u\n", GetLastError());
@@ -7362,6 +7547,90 @@ static void test_token_security_descriptor(void)
ret = SetKernelObjectSecurity(token, LABEL_SECURITY_INFORMATION, sd);
ok(ret, "SetKernelObjectSecurity failed with error %u\n", GetLastError());
+
+ /* changing the label of the security descriptor does not change the integrity level of the token itself */
+ memset(buffer_integrity, 0, sizeof(buffer_integrity));
+ ret = GetTokenInformation(token, TokenIntegrityLevel, buffer_integrity, sizeof(buffer_integrity), &size);
+ ok(ret, "GetTokenInformation failed with error %u\n", GetLastError());
+ tml = (TOKEN_MANDATORY_LABEL *)buffer_integrity;
+ ok(EqualSid(tml->Label.Sid, &medium_level) || EqualSid(tml->Label.Sid, &high_level),
+ "Expected medium or high integrity level\n");
+
+ /* restricting / duplicating a token resets the mandatory sd label */
+ ret = CreateRestrictedToken(token, 0, 0, NULL, 0, NULL, 0, NULL, &token4);
+ ok(ret, "CreateRestrictedToken failed with error %u\n", GetLastError());
+
+ memset(buffer_integrity, 0, sizeof(buffer_integrity));
+ ret = GetTokenInformation(token4, TokenIntegrityLevel, buffer_integrity, sizeof(buffer_integrity), &size);
+ ok(ret, "GetTokenInformation failed with error %u\n", GetLastError());
+ tml = (TOKEN_MANDATORY_LABEL *)buffer_integrity;
+ ok(EqualSid(tml->Label.Sid, &medium_level) || EqualSid(tml->Label.Sid, &high_level),
+ "Expected medium or high integrity level\n");
+
+ size = 0;
+ ret = GetKernelObjectSecurity(token4, LABEL_SECURITY_INFORMATION, NULL, 0, &size);
+ ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+ "Unexpected GetKernelObjectSecurity return value %u, error %u\n", ret, GetLastError());
+
+ sd3 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
+ ret = GetKernelObjectSecurity(token4, LABEL_SECURITY_INFORMATION, sd3, size, &size);
+ ok(ret, "GetKernelObjectSecurity failed with error %u\n", GetLastError());
+
+ ret = GetSecurityDescriptorSacl(sd3, &present, &sacl, &defaulted);
+ ok(ret, "GetSecurityDescriptorSacl failed with error %u\n", GetLastError());
+ todo_wine ok(present, "No SACL in the security descriptor\n");
+ todo_wine ok(sacl != NULL, "NULL SACL in the security descriptor\n");
+
+ if (sacl)
+ {
+ ret = pGetAce(sacl, 0, (void **)&ace);
+ ok(ret, "GetAce failed with error %u\n", GetLastError());
+ ok(ace->Header.AceType == SYSTEM_MANDATORY_LABEL_ACE_TYPE,
+ "Unexpected ACE type %#x\n", ace->Header.AceType);
+ ok(EqualSid(&ace->SidStart, &medium_level) || EqualSid(&ace->SidStart, &high_level),
+ "Low integrity level should not have been inherited\n");
+ }
+
+ HeapFree(GetProcessHeap(), 0, sd3);
+ CloseHandle(token4);
+
+ ret = DuplicateTokenEx(token, 0, NULL, 0, TokenPrimary, &token4);
+ ok(ret, "DuplicateTokenEx failed with error %u\n", GetLastError());
+
+ memset(buffer_integrity, 0, sizeof(buffer_integrity));
+ ret = GetTokenInformation(token4, TokenIntegrityLevel, buffer_integrity, sizeof(buffer_integrity), &size);
+ ok(ret, "GetTokenInformation failed with error %u\n", GetLastError());
+ tml = (TOKEN_MANDATORY_LABEL*) buffer_integrity;
+ ok(EqualSid(tml->Label.Sid, &medium_level) || EqualSid(tml->Label.Sid, &high_level),
+ "Expected medium or high integrity level\n");
+
+ size = 0;
+ ret = GetKernelObjectSecurity(token4, LABEL_SECURITY_INFORMATION, NULL, 0, &size);
+ todo_wine ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+ "Unexpected GetKernelObjectSecurity return value %u, error %u\n", ret, GetLastError());
+
+ sd3 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
+ ret = GetKernelObjectSecurity(token4, LABEL_SECURITY_INFORMATION, sd3, size, &size);
+ todo_wine ok(ret, "GetKernelObjectSecurity failed with error %u\n", GetLastError());
+
+ sacl = NULL;
+ ret = GetSecurityDescriptorSacl(sd3, &present, &sacl, &defaulted);
+ todo_wine ok(ret, "GetSecurityDescriptorSacl failed with error %u\n", GetLastError());
+ todo_wine ok(present, "No SACL in the security descriptor\n");
+ todo_wine ok(sacl != NULL, "NULL SACL in the security descriptor\n");
+
+ if (sacl)
+ {
+ ret = pGetAce(sacl, 0, (void **)&ace);
+ ok(ret, "GetAce failed with error %u\n", GetLastError());
+ ok(ace->Header.AceType == SYSTEM_MANDATORY_LABEL_ACE_TYPE,
+ "Unexpected ACE type %#x\n", ace->Header.AceType);
+ ok(EqualSid(&ace->SidStart, &medium_level) || EqualSid(&ace->SidStart, &high_level),
+ "Low integrity level should not have been inherited\n");
+ }
+
+ HeapFree(GetProcessHeap(), 0, sd3);
+ CloseHandle(token4);
}
else
win_skip("SYSTEM_MANDATORY_LABEL not supported\n");
@@ -7467,6 +7736,116 @@ static void test_child_token_sd(void)
HeapFree(GetProcessHeap(), 0, sd);
}
+static void test_child_token_sd_restricted(void)
+{
+ static SID high_level = {SID_REVISION, 1, {SECURITY_MANDATORY_LABEL_AUTHORITY},
+ {SECURITY_MANDATORY_HIGH_RID}};
+ SYSTEM_MANDATORY_LABEL_ACE *ace_label;
+ BOOL ret, present, defaulted;
+ TOKEN_MANDATORY_LABEL *tml;
+ BYTE buffer_integrity[64];
+ SECURITY_DESCRIPTOR *sd;
+ HANDLE token;
+ DWORD size;
+ ACL *acl;
+
+ if (!pAddMandatoryAce)
+ {
+ win_skip("SYSTEM_MANDATORY_LABEL not supported\n");
+ return;
+ }
+
+ ret = OpenProcessToken(GetCurrentProcess(), MAXIMUM_ALLOWED, &token);
+ ok(ret, "OpenProcessToken failed with error %u\n", GetLastError());
+
+ ret = GetKernelObjectSecurity(token, LABEL_SECURITY_INFORMATION, NULL, 0, &size);
+ ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+ "Unexpected GetKernelObjectSecurity return value %d, error %u\n", ret, GetLastError());
+
+ sd = HeapAlloc(GetProcessHeap(), 0, size);
+ ret = GetKernelObjectSecurity(token, LABEL_SECURITY_INFORMATION, sd, size, &size);
+ ok(ret, "GetKernelObjectSecurity failed with error %u\n", GetLastError());
+
+ acl = NULL;
+ present = FALSE;
+ defaulted = TRUE;
+ ret = GetSecurityDescriptorSacl(sd, &present, &acl, &defaulted);
+ ok(ret, "GetSecurityDescriptorSacl failed with error %u\n", GetLastError());
+ ok(present, "SACL not present\n");
+ ok(acl && acl != (void *)0xdeadbeef, "Got invalid SACL\n");
+ ok(!defaulted, "SACL defaulted\n");
+ ok(acl->AceCount == 1, "Expected exactly one ACE\n");
+ ret = pGetAce(acl, 0, (void **)&ace_label);
+ ok(ret, "GetAce failed with error %u\n", GetLastError());
+ ok(ace_label->Header.AceType == SYSTEM_MANDATORY_LABEL_ACE_TYPE,
+ "Unexpected ACE type %#x\n", ace_label->Header.AceType);
+ ok(EqualSid(&ace_label->SidStart, &high_level),
+ "Expected high integrity level\n");
+
+ memset(buffer_integrity, 0, sizeof(buffer_integrity));
+ ret = GetTokenInformation(token, TokenIntegrityLevel, buffer_integrity, sizeof(buffer_integrity), &size);
+ ok(ret, "GetTokenInformation failed with error %u\n", GetLastError());
+ tml = (TOKEN_MANDATORY_LABEL *)buffer_integrity;
+ ok(EqualSid(tml->Label.Sid, &high_level), "Expected high integrity level\n");
+
+ HeapFree(GetProcessHeap(), 0, sd);
+}
+
+static void test_child_token_sd_medium(void)
+{
+ static SID medium_level = {SID_REVISION, 1, {SECURITY_MANDATORY_LABEL_AUTHORITY},
+ {SECURITY_MANDATORY_MEDIUM_RID}};
+ SYSTEM_MANDATORY_LABEL_ACE *ace_label;
+ BOOL ret, present, defaulted;
+ TOKEN_MANDATORY_LABEL *tml;
+ BYTE buffer_integrity[64];
+ SECURITY_DESCRIPTOR *sd;
+ HANDLE token;
+ DWORD size;
+ ACL *acl;
+
+ if (!pAddMandatoryAce)
+ {
+ win_skip("SYSTEM_MANDATORY_LABEL not supported\n");
+ return;
+ }
+
+ ret = OpenProcessToken(GetCurrentProcess(), MAXIMUM_ALLOWED, &token);
+ ok(ret, "OpenProcessToken failed with error %u\n", GetLastError());
+
+ ret = GetKernelObjectSecurity(token, LABEL_SECURITY_INFORMATION, NULL, 0, &size);
+ ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+ "Unexpected GetKernelObjectSecurity return value %d, error %u\n", ret, GetLastError());
+
+ sd = HeapAlloc(GetProcessHeap(), 0, size);
+ ret = GetKernelObjectSecurity(token, LABEL_SECURITY_INFORMATION, sd, size, &size);
+ ok(ret, "GetKernelObjectSecurity failed with error %u\n", GetLastError());
+
+ acl = NULL;
+ present = FALSE;
+ defaulted = TRUE;
+ ret = GetSecurityDescriptorSacl(sd, &present, &acl, &defaulted);
+ ok(ret, "GetSecurityDescriptorSacl failed with error %u\n", GetLastError());
+ ok(present, "SACL not present\n");
+ ok(acl && acl != (void *)0xdeadbeef, "Got invalid SACL\n");
+ ok(!defaulted, "SACL defaulted\n");
+ ok(acl->AceCount == 1, "Expected exactly one ACE\n");
+ ret = pGetAce(acl, 0, (void **)&ace_label);
+ ok(ret, "GetAce failed with error %u\n", GetLastError());
+ ok(ace_label->Header.AceType == SYSTEM_MANDATORY_LABEL_ACE_TYPE,
+ "Unexpected ACE type %#x\n", ace_label->Header.AceType);
+ todo_wine ok(EqualSid(&ace_label->SidStart, &medium_level),
+ "Expected medium integrity level\n");
+
+ memset(buffer_integrity, 0, sizeof(buffer_integrity));
+ ret = GetTokenInformation(token, TokenIntegrityLevel, buffer_integrity, sizeof(buffer_integrity), &size);
+ ok(ret, "GetTokenInformation failed with error %u\n", GetLastError());
+ tml = (TOKEN_MANDATORY_LABEL *)buffer_integrity;
+ todo_wine ok(EqualSid(tml->Label.Sid, &medium_level), "Expected medium integrity level\n");
+
+ HeapFree(GetProcessHeap(), 0, sd);
+}
+
static void test_GetExplicitEntriesFromAclW(void)
{
static const WCHAR wszCurrentUser[] = { 'C','U','R','R','E','N','T','_','U','S','E','R','\0'};
@@ -7653,6 +8032,10 @@ START_TEST(security)
{
if (!strcmp(myARGV[2], "test_token_sd"))
test_child_token_sd();
+ else if (!strcmp(myARGV[2], "test_token_sd_restricted"))
+ test_child_token_sd_restricted();
+ else if (!strcmp(myARGV[2], "test_token_sd_medium"))
+ test_child_token_sd_medium();
else
test_process_security_child();
return;
--
2.13.1

0002-server-Implement-token-elevation-information.patch

From 01b21b60bbffc605c0c6c645ad33a3e2359cf4b7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Sat, 5 Aug 2017 00:26:03 +0200
Subject: server: Implement token elevation information.
---
dlls/ntdll/nt.c | 16 ++++++++++++----
server/protocol.def | 8 ++++++++
server/token.c | 22 +++++++++++++++++++---
3 files changed, 39 insertions(+), 7 deletions(-)
diff --git a/dlls/ntdll/nt.c b/dlls/ntdll/nt.c
index 5822dec9b15..dda6cabe1cf 100644
--- a/dlls/ntdll/nt.c
+++ b/dlls/ntdll/nt.c
@@ -597,18 +597,26 @@ NTSTATUS WINAPI NtQueryInformationToken(
SERVER_END_REQ;
break;
case TokenElevationType:
+ SERVER_START_REQ( get_token_elevation_type )
{
TOKEN_ELEVATION_TYPE *elevation_type = tokeninfo;
- FIXME("QueryInformationToken( ..., TokenElevationType, ...) semi-stub\n");
- *elevation_type = TokenElevationTypeFull;
+ req->handle = wine_server_obj_handle( token );
+ status = wine_server_call( req );
+ if (status == STATUS_SUCCESS)
+ *elevation_type = reply->elevation;
}
+ SERVER_END_REQ;
break;
case TokenElevation:
+ SERVER_START_REQ( get_token_elevation_type )
{
TOKEN_ELEVATION *elevation = tokeninfo;
- FIXME("QueryInformationToken( ..., TokenElevation, ...) semi-stub\n");
- elevation->TokenIsElevated = TRUE;
+ req->handle = wine_server_obj_handle( token );
+ status = wine_server_call( req );
+ if (status == STATUS_SUCCESS)
+ elevation->TokenIsElevated = (reply->elevation == TokenElevationTypeFull);
}
+ SERVER_END_REQ;
break;
case TokenSessionId:
{
diff --git a/server/protocol.def b/server/protocol.def
index b3dce66eb9c..33f1d5f0ab8 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -3673,6 +3673,14 @@ struct handle_info
@END
+/* Get elevation level of token */
+@REQ(get_token_elevation_type)
+ obj_handle_t handle; /* handle to the object */
+@REPLY
+ unsigned int elevation; /* elevation level */
+@END
+
+
/* Create I/O completion port */
@REQ(create_completion)
unsigned int access; /* desired access to a port */
diff --git a/server/token.c b/server/token.c
index 7ab0f634c05..6a1085bae12 100644
--- a/server/token.c
+++ b/server/token.c
@@ -126,6 +126,7 @@ struct token
ACL *default_dacl; /* the default DACL to assign to objects created by this user */
TOKEN_SOURCE source; /* source of the token */
int impersonation_level; /* impersonation level this token is capable of if non-primary token */
+ TOKEN_ELEVATION_TYPE elevation; /* elevation level */
};
struct privilege
@@ -566,7 +567,7 @@ static struct token *create_token( unsigned primary, const SID *user,
const LUID_AND_ATTRIBUTES *privs, unsigned int priv_count,
const ACL *default_dacl, TOKEN_SOURCE source,
const luid_t *modified_id,
- int impersonation_level )
+ int impersonation_level, TOKEN_ELEVATION_TYPE elevation )
{
struct token *token = alloc_object( &token_ops );
if (token)
@@ -588,6 +589,7 @@ static struct token *create_token( unsigned primary, const SID *user,
token->impersonation_level = impersonation_level;
token->default_dacl = NULL;
token->primary_group = NULL;
+ token->elevation = elevation;
/* copy user */
token->user = memdup( user, security_sid_len( user ));
@@ -700,7 +702,8 @@ struct token *token_duplicate( struct token *src_token, unsigned primary,
token = create_token( primary, src_token->user, NULL, 0,
NULL, 0, src_token->default_dacl,
src_token->source, modified_id,
- impersonation_level );
+ impersonation_level,
+ src_token->elevation );
if (!token) return token;
/* copy groups */
@@ -904,7 +907,7 @@ struct token *token_create_admin( void )
static const TOKEN_SOURCE admin_source = {"SeMgr", {0, 0}};
token = create_token( TRUE, user_sid, admin_groups, sizeof(admin_groups)/sizeof(admin_groups[0]),
admin_privs, sizeof(admin_privs)/sizeof(admin_privs[0]), default_dacl,
- admin_source, NULL, -1 );
+ admin_source, NULL, -1, TokenElevationTypeFull );
/* we really need a primary group */
assert( token->primary_group );
}
@@ -1652,6 +1655,19 @@ DECL_HANDLER(get_token_statistics)
}
}
+DECL_HANDLER(get_token_elevation_type)
+{
+ struct token *token;
+
+ if ((token = (struct token *)get_handle_obj( current->process, req->handle,
+ TOKEN_QUERY,
+ &token_ops )))
+ {
+ reply->elevation = token->elevation;
+ release_object( token );
+ }
+}
+
DECL_HANDLER(get_token_default_dacl)
{
struct token *token;
--
2.13.1

0003-server-Correctly-treat-zero-access-mask-in-duplicate.patch

From 6ae2d648a96f7df1bb854d86b2eb009c76ea083e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Mon, 7 Aug 2017 02:22:11 +0200
Subject: server: Correctly treat zero access mask in duplicate_token
wineserver call.
---
dlls/advapi32/tests/security.c | 14 +++++++-------
server/token.c | 3 ++-
2 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/dlls/advapi32/tests/security.c b/dlls/advapi32/tests/security.c
index 4a03db27e69..f1a64e29dea 100644
--- a/dlls/advapi32/tests/security.c
+++ b/dlls/advapi32/tests/security.c
@@ -7438,7 +7438,7 @@ static void test_token_security_descriptor(void)
ret = DuplicateTokenEx(token4, 0, NULL, SecurityImpersonation, TokenImpersonation, &token5);
ok(ret, "DuplicateTokenEx failed with error %u\n", GetLastError());
ret = SetThreadToken(NULL, token5);
- todo_wine ok(ret, "SetThreadToken failed with error %u\n", GetLastError());
+ ok(ret, "SetThreadToken failed with error %u\n", GetLastError());
CloseHandle(token4);
/* Restrict current process token while impersonating a medium integrity token */
@@ -7503,16 +7503,16 @@ static void test_token_security_descriptor(void)
size = 0;
ret = GetKernelObjectSecurity(token6, LABEL_SECURITY_INFORMATION, NULL, 0, &size);
- todo_wine ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+ ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
"Unexpected GetKernelObjectSecurity return value %u, error %u\n", ret, GetLastError());
sd3 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
ret = GetKernelObjectSecurity(token6, LABEL_SECURITY_INFORMATION, sd3, size, &size);
- todo_wine ok(ret, "GetKernelObjectSecurity failed with error %u\n", GetLastError());
+ ok(ret, "GetKernelObjectSecurity failed with error %u\n", GetLastError());
sacl = NULL;
ret = GetSecurityDescriptorSacl(sd3, &present, &sacl, &defaulted);
- todo_wine ok(ret, "GetSecurityDescriptorSacl failed with error %u\n", GetLastError());
+ ok(ret, "GetSecurityDescriptorSacl failed with error %u\n", GetLastError());
todo_wine ok(present, "No SACL in the security descriptor\n");
todo_wine ok(sacl != NULL, "NULL SACL in the security descriptor\n");
@@ -7606,16 +7606,16 @@ static void test_token_security_descriptor(void)
size = 0;
ret = GetKernelObjectSecurity(token4, LABEL_SECURITY_INFORMATION, NULL, 0, &size);
- todo_wine ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+ ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
"Unexpected GetKernelObjectSecurity return value %u, error %u\n", ret, GetLastError());
sd3 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
ret = GetKernelObjectSecurity(token4, LABEL_SECURITY_INFORMATION, sd3, size, &size);
- todo_wine ok(ret, "GetKernelObjectSecurity failed with error %u\n", GetLastError());
+ ok(ret, "GetKernelObjectSecurity failed with error %u\n", GetLastError());
sacl = NULL;
ret = GetSecurityDescriptorSacl(sd3, &present, &sacl, &defaulted);
- todo_wine ok(ret, "GetSecurityDescriptorSacl failed with error %u\n", GetLastError());
+ ok(ret, "GetSecurityDescriptorSacl failed with error %u\n", GetLastError());
todo_wine ok(present, "No SACL in the security descriptor\n");
todo_wine ok(sacl != NULL, "NULL SACL in the security descriptor\n");
diff --git a/server/token.c b/server/token.c
index 6a1085bae12..292e1df80fd 100644
--- a/server/token.c
+++ b/server/token.c
@@ -1376,7 +1376,8 @@ DECL_HANDLER(duplicate_token)
struct token *token = token_duplicate( src_token, req->primary, req->impersonation_level, sd, NULL, 0, NULL, 0 );
if (token)
{
- reply->new_handle = alloc_handle_no_access_check( current->process, token, req->access, objattr->attributes );
+ unsigned int access = req->access ? req->access : get_handle_access( current->process, req->handle );
+ reply->new_handle = alloc_handle_no_access_check( current->process, token, access, objattr->attributes );
release_object( token );
}
release_object( src_token );
--
2.13.1

0004-server-Implement-token-integrity-level.patch

From e7eab0c83b5f67371e08e4ef9168e4e7082a3965 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Mon, 7 Aug 2017 02:28:35 +0200
Subject: server: Implement token integrity level.
---
dlls/ntdll/nt.c | 23 ++++++++++++++---------
server/protocol.def | 7 +++++++
server/token.c | 30 +++++++++++++++++++++++++++---
3 files changed, 48 insertions(+), 12 deletions(-)
diff --git a/dlls/ntdll/nt.c b/dlls/ntdll/nt.c
index dda6cabe1cf..6f2b24e6ba4 100644
--- a/dlls/ntdll/nt.c
+++ b/dlls/ntdll/nt.c
@@ -372,7 +372,7 @@ NTSTATUS WINAPI NtQueryInformationToken(
0, /* TokenAccessInformation */
0, /* TokenVirtualizationAllowed */
0, /* TokenVirtualizationEnabled */
- sizeof(TOKEN_MANDATORY_LABEL) + sizeof(SID), /* TokenIntegrityLevel [sizeof(SID) includes one SubAuthority] */
+ 0, /* TokenIntegrityLevel */
0, /* TokenUIAccess */
0, /* TokenMandatoryPolicy */
sizeof(TOKEN_GROUPS) + sizeof(logon_sid), /* TokenLogonSid */
@@ -625,18 +625,23 @@ NTSTATUS WINAPI NtQueryInformationToken(
}
break;
case TokenIntegrityLevel:
+ SERVER_START_REQ( get_token_integrity )
{
- /* report always "S-1-16-12288" (high mandatory level) for now */
- static const SID high_level = {SID_REVISION, 1, {SECURITY_MANDATORY_LABEL_AUTHORITY},
- {SECURITY_MANDATORY_HIGH_RID}};
-
TOKEN_MANDATORY_LABEL *tml = tokeninfo;
- PSID psid = tml + 1;
+ PSID sid = tml + 1;
+ DWORD sid_len = tokeninfolength < sizeof(*tml) ? 0 : tokeninfolength - sizeof(*tml);
- tml->Label.Sid = psid;
- tml->Label.Attributes = SE_GROUP_INTEGRITY | SE_GROUP_INTEGRITY_ENABLED;
- memcpy(psid, &high_level, sizeof(SID));
+ req->handle = wine_server_obj_handle( token );
+ wine_server_set_reply( req, sid, sid_len );
+ status = wine_server_call( req );
+ if (retlen) *retlen = reply->sid_len + sizeof(*tml);
+ if (status == STATUS_SUCCESS)
+ {
+ tml->Label.Sid = sid;
+ tml->Label.Attributes = SE_GROUP_INTEGRITY | SE_GROUP_INTEGRITY_ENABLED;
+ }
}
+ SERVER_END_REQ;
break;
case TokenAppContainerSid:
{
diff --git a/server/protocol.def b/server/protocol.def
index 33f1d5f0ab8..ac2e2242511 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -3424,6 +3424,13 @@ enum caret_state
VARARG(sid,SID); /* the sid specified by which_sid from the token */
@END
+@REQ(get_token_integrity)
+ obj_handle_t handle; /* handle to the token */
+@REPLY
+ data_size_t sid_len; /* length needed to store sid */
+ VARARG(sid,SID); /* the integrity sid */
+@END
+
@REQ(get_token_groups)
obj_handle_t handle; /* handle to the token */
@REPLY
diff --git a/server/token.c b/server/token.c
index 292e1df80fd..8d2de6ab58e 100644
--- a/server/token.c
+++ b/server/token.c
@@ -127,6 +127,7 @@ struct token
TOKEN_SOURCE source; /* source of the token */
int impersonation_level; /* impersonation level this token is capable of if non-primary token */
TOKEN_ELEVATION_TYPE elevation; /* elevation level */
+ const SID *integrity; /* token integrity */
};
struct privilege
@@ -567,7 +568,8 @@ static struct token *create_token( unsigned primary, const SID *user,
const LUID_AND_ATTRIBUTES *privs, unsigned int priv_count,
const ACL *default_dacl, TOKEN_SOURCE source,
const luid_t *modified_id,
- int impersonation_level, TOKEN_ELEVATION_TYPE elevation )
+ int impersonation_level, TOKEN_ELEVATION_TYPE elevation,
+ const SID *integrity )
{
struct token *token = alloc_object( &token_ops );
if (token)
@@ -648,6 +650,7 @@ static struct token *create_token( unsigned primary, const SID *user,
}
token->source = source;
+ token->integrity = integrity;
}
return token;
}
@@ -703,7 +706,8 @@ struct token *token_duplicate( struct token *src_token, unsigned primary,
NULL, 0, src_token->default_dacl,
src_token->source, modified_id,
impersonation_level,
- src_token->elevation );
+ src_token->elevation,
+ src_token->integrity );
if (!token) return token;
/* copy groups */
@@ -907,7 +911,7 @@ struct token *token_create_admin( void )
static const TOKEN_SOURCE admin_source = {"SeMgr", {0, 0}};
token = create_token( TRUE, user_sid, admin_groups, sizeof(admin_groups)/sizeof(admin_groups[0]),
admin_privs, sizeof(admin_privs)/sizeof(admin_privs[0]), default_dacl,
- admin_source, NULL, -1, TokenElevationTypeFull );
+ admin_source, NULL, -1, TokenElevationTypeFull, &high_label_sid );
/* we really need a primary group */
assert( token->primary_group );
}
@@ -1550,6 +1554,26 @@ DECL_HANDLER(get_token_sid)
}
}
+/* retrieves the integrity sid */
+DECL_HANDLER(get_token_integrity)
+{
+ struct token *token;
+
+ reply->sid_len = 0;
+
+ if ((token = (struct token *)get_handle_obj( current->process, req->handle,
+ TOKEN_QUERY,
+ &token_ops )))
+ {
+ reply->sid_len = security_sid_len( token->integrity );
+ if (reply->sid_len <= get_reply_max_size())
+ set_reply_data( token->integrity, reply->sid_len );
+ else
+ set_error( STATUS_BUFFER_TOO_SMALL );
+ release_object( token );
+ }
+}
+
/* retrieves the groups that the user represented by the token belongs to */
DECL_HANDLER(get_token_groups)
{
--
2.13.1

0005-server-Use-all-group-attributes-in-create_token.patch

From 78bb8a3ff43e542119dd3ba26354fe770636e58d Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Sun, 6 Aug 2017 15:16:33 +0200
Subject: server: Use all group attributes in create_token.
---
server/token.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/server/token.c b/server/token.c
index 8d2de6ab58e..e61fe97bfa0 100644
--- a/server/token.c
+++ b/server/token.c
@@ -613,13 +613,13 @@ static struct token *create_token( unsigned primary, const SID *user,
return NULL;
}
memcpy( &group->sid, groups[i].Sid, security_sid_len( groups[i].Sid ));
- group->enabled = TRUE;
- group->def = TRUE;
- group->logon = (groups[i].Attributes & SE_GROUP_LOGON_ID) != 0;
group->mandatory = (groups[i].Attributes & SE_GROUP_MANDATORY) != 0;
- group->owner = (groups[i].Attributes & SE_GROUP_OWNER) != 0;
- group->resource = FALSE;
- group->deny_only = FALSE;
+ group->def = (groups[i].Attributes & SE_GROUP_ENABLED_BY_DEFAULT) != 0;
+ group->enabled = (groups[i].Attributes & SE_GROUP_ENABLED) != 0;
+ group->owner = (groups[i].Attributes & SE_GROUP_OWNER) != 0;
+ group->deny_only = (groups[i].Attributes & SE_GROUP_USE_FOR_DENY_ONLY) != 0;
+ group->logon = (groups[i].Attributes & SE_GROUP_LOGON_ID) != 0;
+ group->resource = (groups[i].Attributes & SE_GROUP_RESOURCE) != 0;
list_add_tail( &token->groups, &group->entry );
/* Use first owner capable group as an owner */
if (!token->primary_group && group->owner)
@@ -1628,8 +1628,8 @@ DECL_HANDLER(get_token_groups)
if (group->enabled) *attr_ptr |= SE_GROUP_ENABLED;
if (group->owner) *attr_ptr |= SE_GROUP_OWNER;
if (group->deny_only) *attr_ptr |= SE_GROUP_USE_FOR_DENY_ONLY;
- if (group->resource) *attr_ptr |= SE_GROUP_RESOURCE;
if (group->logon) *attr_ptr |= SE_GROUP_LOGON_ID;
+ if (group->resource) *attr_ptr |= SE_GROUP_RESOURCE;
memcpy(sid_ptr, &group->sid, security_sid_len( &group->sid ));
--
2.13.1

0006-ntdll-Add-function-to-create-new-tokens-for-elevatio.patch

From 90dfc764926fe77163822b627c39f9d3351f25cf 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: 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 275fda57970..8f5357b944c 100644
--- a/dlls/ntdll/ntdll.spec
+++ b/dlls/ntdll/ntdll.spec
@@ -1487,6 +1487,9 @@
@ cdecl wine_server_send_fd(long)
@ cdecl __wine_make_process_system()
+# Token
+@ cdecl __wine_create_default_token(long)
+
# Version
@ cdecl wine_get_version() NTDLL_wine_get_version
@ cdecl wine_get_patches() NTDLL_wine_get_patches
diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h
index c6c60090d10..e64cb9e75a3 100644
--- a/dlls/ntdll/ntdll_misc.h
+++ b/dlls/ntdll/ntdll_misc.h
@@ -82,6 +82,9 @@ extern void fill_cpu_info(void) DECLSPEC_HIDDEN;
extern void *grow_virtual_heap( HANDLE handle, SIZE_T *size ) DECLSPEC_HIDDEN;
extern void heap_set_debug_flags( HANDLE handle ) DECLSPEC_HIDDEN;
+/* token */
+extern HANDLE CDECL __wine_create_default_token(BOOL admin);
+
/* server support */
extern timeout_t server_start_time DECLSPEC_HIDDEN;
extern unsigned int server_cpus DECLSPEC_HIDDEN;
diff --git a/dlls/ntdll/process.c b/dlls/ntdll/process.c
index f2eb09c142f..ba4613e87b3 100644
--- a/dlls/ntdll/process.c
+++ b/dlls/ntdll/process.c
@@ -99,6 +99,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;
+}
+
static UINT process_error_mode;
#define UNIMPLEMENTED_INFO_CLASS(c) \
diff --git a/server/protocol.def b/server/protocol.def
index ac2e2242511..300f23fb9b6 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -3688,6 +3688,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 e61fe97bfa0..c9d36a5b4f3 100644
--- a/server/token.c
+++ b/server/token.c
@@ -71,6 +71,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 struct /* same fields as struct SID */
{
BYTE Revision;
@@ -110,6 +111,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 };
@@ -924,6 +926,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;
@@ -1738,3 +1798,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.13.1

0007-shell32-Implement-process-elevation-using-runas-verb.patch

From 4ff67fcf2edf94d646db5a27a5731b5d962a4781 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Sat, 5 Aug 2017 02:03:20 +0200
Subject: shell32: Implement process elevation using runas verb.
---
dlls/shell32/shlexec.c | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/dlls/shell32/shlexec.c b/dlls/shell32/shlexec.c
index 0cf112b6373..af50078dbca 100644
--- a/dlls/shell32/shlexec.c
+++ b/dlls/shell32/shlexec.c
@@ -50,6 +50,8 @@
WINE_DEFAULT_DEBUG_CHANNEL(exec);
+extern HANDLE CDECL __wine_create_default_token(BOOL admin);
+
static const WCHAR wszOpen[] = {'o','p','e','n',0};
static const WCHAR wszExe[] = {'.','e','x','e',0};
static const WCHAR wszILPtr[] = {':','%','p',0};
@@ -312,6 +314,8 @@ static HRESULT SHELL_GetPathFromIDListForExecuteW(LPCITEMIDLIST pidl, LPWSTR psz
static UINT_PTR SHELL_ExecuteW(const WCHAR *lpCmd, WCHAR *env, BOOL shWait,
const SHELLEXECUTEINFOW *psei, LPSHELLEXECUTEINFOW psei_out)
{
+ static WCHAR runasW[] = {'r','u','n','a','s',0};
+ HANDLE token = NULL;
STARTUPINFOW startup;
PROCESS_INFORMATION info;
UINT_PTR retval = SE_ERR_NOASSOC;
@@ -344,8 +348,20 @@ static UINT_PTR SHELL_ExecuteW(const WCHAR *lpCmd, WCHAR *env, BOOL shWait,
dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
if (!(psei->fMask & SEE_MASK_NO_CONSOLE))
dwCreationFlags |= CREATE_NEW_CONSOLE;
- if (CreateProcessW(NULL, (LPWSTR)lpCmd, NULL, NULL, FALSE, dwCreationFlags, env,
- lpDirectory, &startup, &info))
+
+ /* Spawning a process with runas verb means that the process should be
+ * executed with admin rights. This function ignores the manifest data,
+ * and allows programs to elevate rights on-demand. On Windows a complex
+ * RPC menchanism is used, using CreateProcessAsUser would fail because
+ * it can only be used to drop rights. */
+ if (psei->lpVerb && !strcmpiW(psei->lpVerb, runasW))
+ {
+ if (!(token = __wine_create_default_token(TRUE)))
+ ERR("Failed to create admin token\n");
+ }
+
+ if (CreateProcessAsUserW(token, NULL, (LPWSTR)lpCmd, NULL, NULL, FALSE,
+ dwCreationFlags, env, lpDirectory, &startup, &info))
{
/* Give 30 seconds to the app to come up, if desired. Probably only needed
when starting app immediately before making a DDE connection. */
@@ -365,6 +381,8 @@ static UINT_PTR SHELL_ExecuteW(const WCHAR *lpCmd, WCHAR *env, BOOL shWait,
retval = ERROR_BAD_FORMAT;
}
+ if (token) CloseHandle(token);
+
TRACE("returning %lu\n", retval);
psei_out->hInstApp = (HINSTANCE)retval;
--
2.13.1

0008-ntdll-Implement-process-token-elevation-through-mani.patch

From 7c48930afa2467f4de1a46056fe5cb7be2c4d293 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Sat, 5 Aug 2017 03:39:55 +0200
Subject: ntdll: Implement process token elevation through manifests.
---
dlls/ntdll/loader.c | 37 +++++++++++++++++++++++++++++++++++++
server/process.c | 8 ++++++++
server/process.h | 1 +
server/protocol.def | 7 +++++++
server/token.c | 14 ++++++++++++++
5 files changed, 67 insertions(+)
diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c
index a09eca9db75..2f6bcbde8fe 100644
--- a/dlls/ntdll/loader.c
+++ b/dlls/ntdll/loader.c
@@ -3632,6 +3632,32 @@ static void load_global_options(void)
/***********************************************************************
+ * elevate_process
+ */
+static void elevate_process( void )
+{
+ NTSTATUS status;
+ HANDLE token;
+
+ if (!(token = __wine_create_default_token( TRUE )))
+ {
+ ERR( "Failed to create admin token\n" );
+ return;
+ }
+
+ SERVER_START_REQ( replace_process_token )
+ {
+ req->token = wine_server_obj_handle( token );
+ if ((status = wine_server_call( req )))
+ ERR( "Failed to replace process token: %08x\n", status );
+ }
+ SERVER_END_REQ;
+
+ NtClose( token );
+}
+
+
+/***********************************************************************
* start_process
*/
static void start_process( void *arg )
@@ -3679,6 +3705,7 @@ void WINAPI LdrInitializeThunk( void *kernel_start, ULONG_PTR unknown2,
ULONG_PTR unknown3, ULONG_PTR unknown4 )
{
static const WCHAR globalflagW[] = {'G','l','o','b','a','l','F','l','a','g',0};
+ ACTIVATION_CONTEXT_RUN_LEVEL_INFORMATION runlevel;
LARGE_INTEGER timeout;
NTSTATUS status;
WINE_MODREF *wm;
@@ -3725,6 +3752,16 @@ void WINAPI LdrInitializeThunk( void *kernel_start, ULONG_PTR unknown2,
if ((status = fixup_imports( wm, load_path )) != STATUS_SUCCESS) goto error;
heap_set_debug_flags( GetProcessHeap() );
+ /* elevate process if necessary */
+ status = RtlQueryInformationActivationContext( 0, NULL, 0, RunlevelInformationInActivationContext,
+ &runlevel, sizeof(runlevel), NULL );
+ if (!status && (runlevel.RunLevel == ACTCTX_RUN_LEVEL_HIGHEST_AVAILABLE ||
+ runlevel.RunLevel == ACTCTX_RUN_LEVEL_REQUIRE_ADMIN))
+ {
+ TRACE( "Application requested admin rights (run level %d)\n", runlevel.RunLevel );
+ elevate_process(); /* FIXME: the process exists with a wrong token for a short time */
+ }
+
/* Store original entrypoint (in case it gets corrupted) */
start_params.kernel_start = kernel_start;
start_params.entry = wm->ldr.EntryPoint;
diff --git a/server/process.c b/server/process.c
index f0f60edcd3f..74675d343b4 100644
--- a/server/process.c
+++ b/server/process.c
@@ -1146,6 +1146,14 @@ struct process_snapshot *process_snap( int *count )
return snapshot;
}
+/* replace the token of a process */
+void replace_process_token( struct process *process, struct token *new_token )
+{
+ release_object(current->process->token);
+ current->process->token = new_token;
+ grab_object(new_token);
+}
+
/* create a new process */
DECL_HANDLER(new_process)
{
diff --git a/server/process.h b/server/process.h
index 548796f9c22..262eb59627b 100644
--- a/server/process.h
+++ b/server/process.h
@@ -137,6 +137,7 @@ extern void break_process( struct process *process );
extern void detach_debugged_processes( struct thread *debugger );
extern struct process_snapshot *process_snap( int *count );
extern void enum_processes( int (*cb)(struct process*, void*), void *user);
+extern void replace_process_token( struct process *process, struct token *token );
/* console functions */
extern void inherit_console(struct thread *parent_thread, struct process *process, obj_handle_t hconin);
diff --git a/server/protocol.def b/server/protocol.def
index 300f23fb9b6..e5b598259f7 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -3696,6 +3696,13 @@ struct handle_info
@END
+/* Create a new token */
+@REQ(replace_process_token)
+ obj_handle_t token; /* new process token */
+@REPLY
+@END
+
+
/* Create I/O completion port */
@REQ(create_completion)
unsigned int access; /* desired access to a port */
diff --git a/server/token.c b/server/token.c
index c9d36a5b4f3..385ea3bbfda 100644
--- a/server/token.c
+++ b/server/token.c
@@ -1822,3 +1822,17 @@ DECL_HANDLER(create_token)
release_object( token );
}
}
+
+/* Replaces the token of the current process */
+DECL_HANDLER(replace_process_token)
+{
+ struct token *token;
+
+ if ((token = (struct token *)get_handle_obj( current->process, req->token,
+ TOKEN_ASSIGN_PRIMARY,
+ &token_ops )))
+ {
+ replace_process_token( current->process, token );
+ release_object( token );
+ }
+}
--
2.13.1

0009-kernel32-Implement-CreateProcessInternalW.patch

From 231adfae6a4928816a15ba63fd1cdf984b2f9ea1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Sat, 5 Aug 2017 04:02:16 +0200
Subject: kernel32: Implement CreateProcessInternalW.
---
dlls/kernel32/kernel32.spec | 2 +-
dlls/kernel32/process.c | 24 ++++++++++++++----------
include/winbase.h | 1 +
3 files changed, 16 insertions(+), 11 deletions(-)
diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec
index 608a89da3bd..0b2879ddd33 100644
--- a/dlls/kernel32/kernel32.spec
+++ b/dlls/kernel32/kernel32.spec
@@ -315,7 +315,7 @@
@ stdcall CreateProcessA(str str ptr ptr long long ptr str ptr ptr)
# @ stub CreateProcessAsUserW
# @ stub CreateProcessInternalA
-# @ stub CreateProcessInternalW
+@ stdcall CreateProcessInternalW(long wstr wstr ptr ptr long long ptr wstr ptr ptr ptr)
# @ stub CreateProcessInternalWSecure
@ stdcall CreateProcessW(wstr wstr ptr ptr long long ptr wstr ptr ptr)
@ stdcall CreateRemoteThread(long ptr long ptr long long ptr)
diff --git a/dlls/kernel32/process.c b/dlls/kernel32/process.c
index 00b93348855..cacdcb3b621 100644
--- a/dlls/kernel32/process.c
+++ b/dlls/kernel32/process.c
@@ -2463,12 +2463,13 @@ static LPWSTR get_file_name( LPCWSTR appname, LPWSTR cmdline, LPWSTR buffer,
return ret;
}
-
-/* Steam hotpatches CreateProcessA and W, so to prevent it from crashing use an internal function */
-static BOOL create_process_impl( LPCWSTR app_name, LPWSTR cmd_line, LPSECURITY_ATTRIBUTES process_attr,
- LPSECURITY_ATTRIBUTES thread_attr, BOOL inherit, DWORD flags,
- LPVOID env, LPCWSTR cur_dir, LPSTARTUPINFOW startup_info,
- LPPROCESS_INFORMATION info )
+/**********************************************************************
+ * CreateProcessInternalW (KERNEL32.@)
+ */
+BOOL WINAPI CreateProcessInternalW( HANDLE token, LPCWSTR app_name, LPWSTR cmd_line, LPSECURITY_ATTRIBUTES process_attr,
+ LPSECURITY_ATTRIBUTES thread_attr, BOOL inherit, DWORD flags,
+ LPVOID env, LPCWSTR cur_dir, LPSTARTUPINFOW startup_info,
+ LPPROCESS_INFORMATION info, HANDLE *new_token )
{
BOOL retv = FALSE;
HANDLE hFile = 0;
@@ -2481,6 +2482,9 @@ static BOOL create_process_impl( LPCWSTR app_name, LPWSTR cmd_line, LPSECURITY_A
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");
+ if (new_token) FIXME("No support for returning created process token\n");
+
if (!(tidy_cmdline = get_file_name( app_name, cmd_line, name, sizeof(name)/sizeof(WCHAR),
&hFile, &binary_info )))
return FALSE;
@@ -2632,8 +2636,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessA( LPCSTR app_name, LPSTR cmd_line, L
FIXME("StartupInfo.lpReserved is used, please report (%s)\n",
debugstr_a(startup_info->lpReserved));
- ret = create_process_impl( app_nameW, cmd_lineW, process_attr, thread_attr,
- inherit, flags, env, cur_dirW, &infoW, info );
+ ret = CreateProcessInternalW( NULL, app_nameW, cmd_lineW, process_attr, thread_attr,
+ inherit, flags, env, cur_dirW, &infoW, info, NULL );
done:
HeapFree( GetProcessHeap(), 0, app_nameW );
HeapFree( GetProcessHeap(), 0, cmd_lineW );
@@ -2652,8 +2656,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessW( LPCWSTR app_name, LPWSTR cmd_line,
LPVOID env, LPCWSTR cur_dir, LPSTARTUPINFOW startup_info,
LPPROCESS_INFORMATION info )
{
- return create_process_impl( app_name, cmd_line, process_attr, thread_attr,
- inherit, flags, env, cur_dir, startup_info, info);
+ return CreateProcessInternalW( NULL, app_name, cmd_line, process_attr, thread_attr,
+ inherit, flags, env, cur_dir, startup_info, info, NULL);
}
diff --git a/include/winbase.h b/include/winbase.h
index 24efbd865ab..7982fa4a070 100644
--- a/include/winbase.h
+++ b/include/winbase.h
@@ -1847,6 +1847,7 @@ WINBASEAPI BOOL WINAPI CreateProcessW(LPCWSTR,LPWSTR,LPSECURITY_ATTRIBUTE
WINADVAPI BOOL WINAPI CreateProcessAsUserA(HANDLE,LPCSTR,LPSTR,LPSECURITY_ATTRIBUTES,LPSECURITY_ATTRIBUTES,BOOL,DWORD,LPVOID,LPCSTR,LPSTARTUPINFOA,LPPROCESS_INFORMATION);
WINADVAPI BOOL WINAPI CreateProcessAsUserW(HANDLE,LPCWSTR,LPWSTR,LPSECURITY_ATTRIBUTES,LPSECURITY_ATTRIBUTES,BOOL,DWORD,LPVOID,LPCWSTR,LPSTARTUPINFOW,LPPROCESS_INFORMATION);
#define CreateProcessAsUser WINELIB_NAME_AW(CreateProcessAsUser)
+WINBASEAPI BOOL WINAPI CreateProcessInternalW(HANDLE,LPCWSTR,LPWSTR,LPSECURITY_ATTRIBUTES,LPSECURITY_ATTRIBUTES,BOOL,DWORD,LPVOID,LPCWSTR,LPSTARTUPINFOW,LPPROCESS_INFORMATION,HANDLE*);
WINADVAPI BOOL WINAPI CreateProcessWithLogonW(LPCWSTR,LPCWSTR,LPCWSTR,DWORD,LPCWSTR,LPWSTR,DWORD,LPVOID,LPCWSTR,LPSTARTUPINFOW,LPPROCESS_INFORMATION);
WINBASEAPI HANDLE WINAPI CreateRemoteThread(HANDLE,LPSECURITY_ATTRIBUTES,SIZE_T,LPTHREAD_START_ROUTINE,LPVOID,DWORD,LPDWORD);
WINBASEAPI HANDLE WINAPI CreateRemoteThreadEx(HANDLE,LPSECURITY_ATTRIBUTES,SIZE_T,LPTHREAD_START_ROUTINE,LPVOID,DWORD,LPPROC_THREAD_ATTRIBUTE_LIST,LPDWORD);
--
2.13.1

0010-server-Implement-support-for-creating-processes-usin.patch

From 92bbd900f160d9b105b8f5acbf1dcda86e277e12 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: server: Implement support for creating processes using a token.
---
dlls/kernel32/process.c | 33 ++++++++++++++++++---------------
server/process.c | 29 +++++++++++++++++++++++++----
server/process.h | 2 +-
server/protocol.def | 1 +
server/request.c | 2 +-
server/security.h | 2 ++
server/token.c | 11 +++++++++++
7 files changed, 59 insertions(+), 21 deletions(-)
diff --git a/dlls/kernel32/process.c b/dlls/kernel32/process.c
index cacdcb3b621..3ae9d175a79 100644
--- a/dlls/kernel32/process.c
+++ b/dlls/kernel32/process.c
@@ -2064,7 +2064,7 @@ static NTSTATUS create_struct_sd(PSECURITY_DESCRIPTOR nt_sd, struct security_des
* 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, LPCWSTR filename, LPWSTR cmd_line, LPWSTR env,
+static BOOL create_process( HANDLE token, HANDLE hFile, LPCWSTR filename, LPWSTR cmd_line, LPWSTR env,
LPCWSTR cur_dir, LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES tsa,
BOOL inherit, DWORD flags, LPSTARTUPINFOW startup,
LPPROCESS_INFORMATION info, LPCSTR unixdir,
@@ -2210,6 +2210,7 @@ static BOOL create_process( HANDLE hFile, LPCWSTR filename, LPWSTR cmd_line, LPW
req->info_size = startup_info_size;
req->env_size = (env_end - env) * sizeof(WCHAR);
req->process_sd_size = process_sd_size;
+ req->token = wine_server_obj_handle( token );
wine_server_add_data( req, startup_info, startup_info_size );
wine_server_add_data( req, env, (env_end - env) * sizeof(WCHAR) );
@@ -2310,7 +2311,7 @@ error:
*
* Create a new VDM process for a 16-bit or DOS application.
*/
-static BOOL create_vdm_process( LPCWSTR filename, LPWSTR cmd_line, LPWSTR env, LPCWSTR cur_dir,
+static BOOL create_vdm_process( HANDLE token, LPCWSTR filename, LPWSTR cmd_line, LPWSTR env, LPCWSTR cur_dir,
LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES tsa,
BOOL inherit, DWORD flags, LPSTARTUPINFOW startup,
LPPROCESS_INFORMATION info, LPCSTR unixdir,
@@ -2334,7 +2335,7 @@ static BOOL create_vdm_process( LPCWSTR filename, LPWSTR cmd_line, LPWSTR env, L
return FALSE;
}
sprintfW(new_cmd_line, argsW, winevdmW, buffer, cmd_line);
- ret = create_process( 0, winevdmW, new_cmd_line, env, cur_dir, psa, tsa, inherit,
+ ret = create_process( token, 0, winevdmW, new_cmd_line, env, cur_dir, psa, tsa, inherit,
flags, startup, info, unixdir, binary_info, exec_only );
HeapFree( GetProcessHeap(), 0, new_cmd_line );
return ret;
@@ -2346,7 +2347,7 @@ static BOOL create_vdm_process( LPCWSTR filename, LPWSTR cmd_line, LPWSTR env, L
*
* Create a new cmd shell process for a .BAT file.
*/
-static BOOL create_cmd_process( LPCWSTR filename, LPWSTR cmd_line, LPVOID env, LPCWSTR cur_dir,
+static BOOL create_cmd_process( HANDLE token, LPCWSTR filename, LPWSTR cmd_line, LPVOID env, LPCWSTR cur_dir,
LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES tsa,
BOOL inherit, DWORD flags, LPSTARTUPINFOW startup,
LPPROCESS_INFORMATION info )
@@ -2371,8 +2372,8 @@ static BOOL create_cmd_process( LPCWSTR filename, LPWSTR cmd_line, LPVOID env, L
strcpyW( newcmdline, comspec );
strcatW( newcmdline, slashcW );
strcatW( newcmdline, cmd_line );
- ret = CreateProcessW( comspec, newcmdline, psa, tsa, inherit,
- flags, env, cur_dir, startup, info );
+ ret = CreateProcessInternalW( token, comspec, newcmdline, psa, tsa, inherit,
+ flags, env, cur_dir, startup, info, NULL );
HeapFree( GetProcessHeap(), 0, newcmdline );
return ret;
}
@@ -2482,7 +2483,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, sizeof(name)/sizeof(WCHAR),
@@ -2540,20 +2543,20 @@ BOOL WINAPI CreateProcessInternalW( HANDLE token, LPCWSTR app_name, LPWSTR cmd_l
debugstr_w(name), (binary_info.flags & BINARY_FLAG_64BIT) ? 64 : 32,
binary_info.res_start, binary_info.res_end, binary_info.arch,
(binary_info.flags & BINARY_FLAG_FAKEDLL) ? ", fakedll" : "" );
- retv = create_process( hFile, name, tidy_cmdline, envW, cur_dir, process_attr, thread_attr,
+ retv = create_process( token, hFile, name, tidy_cmdline, envW, cur_dir, process_attr, thread_attr,
inherit, flags, startup_info, info, unixdir, &binary_info, FALSE );
break;
case BINARY_OS216:
case BINARY_WIN16:
case BINARY_DOS:
TRACE( "starting %s as Win16/DOS binary\n", debugstr_w(name) );
- retv = create_vdm_process( name, tidy_cmdline, envW, cur_dir, process_attr, thread_attr,
+ retv = create_vdm_process( token, name, tidy_cmdline, envW, cur_dir, process_attr, thread_attr,
inherit, flags, startup_info, info, unixdir, &binary_info, FALSE );
break;
case BINARY_UNIX_LIB:
TRACE( "starting %s as %d-bit Winelib app\n",
debugstr_w(name), (binary_info.flags & BINARY_FLAG_64BIT) ? 64 : 32 );
- retv = create_process( hFile, name, tidy_cmdline, envW, cur_dir, process_attr, thread_attr,
+ retv = create_process( token, hFile, name, tidy_cmdline, envW, cur_dir, process_attr, thread_attr,
inherit, flags, startup_info, info, unixdir, &binary_info, FALSE );
break;
case BINARY_UNKNOWN:
@@ -2565,7 +2568,7 @@ BOOL WINAPI CreateProcessInternalW( HANDLE token, LPCWSTR app_name, LPWSTR cmd_l
TRACE( "starting %s as DOS binary\n", debugstr_w(name) );
binary_info.type = BINARY_DOS;
binary_info.arch = IMAGE_FILE_MACHINE_I386;
- retv = create_vdm_process( name, tidy_cmdline, envW, cur_dir, process_attr, thread_attr,
+ retv = create_vdm_process( token, name, tidy_cmdline, envW, cur_dir, process_attr, thread_attr,
inherit, flags, startup_info, info, unixdir,
&binary_info, FALSE );
break;
@@ -2573,7 +2576,7 @@ BOOL WINAPI CreateProcessInternalW( HANDLE token, LPCWSTR app_name, LPWSTR cmd_l
if (!strcmpiW( p, batW ) || !strcmpiW( p, cmdW ) )
{
TRACE( "starting %s as batch binary\n", debugstr_w(name) );
- retv = create_cmd_process( name, tidy_cmdline, envW, cur_dir, process_attr, thread_attr,
+ retv = create_cmd_process( token, name, tidy_cmdline, envW, cur_dir, process_attr, thread_attr,
inherit, flags, startup_info, info );
break;
}
@@ -2692,12 +2695,12 @@ static void exec_process( LPCWSTR name )
TRACE( "starting %s as Win%d binary (%p-%p, arch %04x)\n",
debugstr_w(name), (binary_info.flags & BINARY_FLAG_64BIT) ? 64 : 32,
binary_info.res_start, binary_info.res_end, binary_info.arch );
- create_process( hFile, name, GetCommandLineW(), NULL, NULL, NULL, NULL,
+ create_process( NULL, hFile, name, GetCommandLineW(), NULL, NULL, NULL, NULL,
FALSE, 0, &startup_info, &info, NULL, &binary_info, TRUE );
break;
case BINARY_UNIX_LIB:
TRACE( "%s is a Unix library, starting as Winelib app\n", debugstr_w(name) );
- create_process( hFile, name, GetCommandLineW(), NULL, NULL, NULL, NULL,
+ create_process( NULL, hFile, name, GetCommandLineW(), NULL, NULL, NULL, NULL,
FALSE, 0, &startup_info, &info, NULL, &binary_info, TRUE );
break;
case BINARY_UNKNOWN:
@@ -2711,7 +2714,7 @@ static void exec_process( LPCWSTR name )
case BINARY_WIN16:
case BINARY_DOS:
TRACE( "starting %s as Win16/DOS binary\n", debugstr_w(name) );
- create_vdm_process( name, GetCommandLineW(), NULL, NULL, NULL, NULL,
+ create_vdm_process( NULL, name, GetCommandLineW(), NULL, NULL, NULL, NULL,
FALSE, 0, &startup_info, &info, NULL, &binary_info, TRUE );
break;
default:
diff --git a/server/process.c b/server/process.c
index 74675d343b4..ef2452fb8fb 100644
--- a/server/process.c
+++ b/server/process.c
@@ -501,7 +501,7 @@ static void start_sigkill_timer( struct process *process )
/* create a new process and its main thread */
/* if the function fails the fd is closed */
-struct thread *create_process( int fd, struct thread *parent_thread, int inherit_all )
+struct thread *create_process( int fd, struct thread *parent_thread, int inherit_all, struct token *token )
{
struct process *process;
struct thread *thread = NULL;
@@ -571,7 +571,7 @@ struct thread *create_process( int fd, struct thread *parent_thread, int inherit
: 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;
@@ -1160,6 +1160,7 @@ DECL_HANDLER(new_process)
struct startup_info *info;
struct thread *thread;
struct process *process;
+ struct token *token = NULL;
struct process *parent = current->process;
int socket_fd = thread_get_inflight_fd( current, req->socket_fd );
const struct security_descriptor *process_sd = NULL, *thread_sd = NULL;
@@ -1195,9 +1196,27 @@ 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 */
{
- create_process( socket_fd, NULL, 0 );
+ create_process( socket_fd, NULL, 0, token );
+ if (token) release_object( token );
return;
}
@@ -1205,6 +1224,7 @@ DECL_HANDLER(new_process)
if (!(info = alloc_object( &startup_info_ops )))
{
close( socket_fd );
+ if (token) release_object( token );
return;
}
info->exe_file = NULL;
@@ -1287,7 +1307,7 @@ DECL_HANDLER(new_process)
}
}
- if (!(thread = create_process( socket_fd, current, req->inherit_all ))) goto done;
+ if (!(thread = create_process( socket_fd, current, req->inherit_all, token ))) goto done;
process = thread->process;
process->startup_info = (struct startup_info *)grab_object( info );
@@ -1369,6 +1389,7 @@ DECL_HANDLER(new_process)
}
done:
+ if (token) release_object( token );
release_object( info );
}
diff --git a/server/process.h b/server/process.h
index 262eb59627b..fcb45d8d676 100644
--- a/server/process.h
+++ b/server/process.h
@@ -115,7 +115,7 @@ struct process_snapshot
extern unsigned int alloc_ptid( void *ptr );
extern void free_ptid( unsigned int id );
extern void *get_ptid_entry( unsigned int id );
-extern struct thread *create_process( int fd, struct thread *parent_thread, int inherit_all );
+extern struct thread *create_process( int fd, struct thread *parent_thread, int inherit_all, 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 e5b598259f7..7106cdb6f04 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -767,6 +767,7 @@ struct rawinput_device
data_size_t info_size; /* size of startup info */
data_size_t env_size; /* size of the environment */
data_size_t process_sd_size;/* size of the process security descriptor */
+ obj_handle_t token; /* token for the new process */
VARARG(info,startup_info,info_size); /* startup information */
VARARG(env,unicode_str,env_size); /* environment for new process */
VARARG(process_sd,security_descriptor,process_sd_size); /* security descriptor to set on the process */
diff --git a/server/request.c b/server/request.c
index 83e608917f8..23760cdeb98 100644
--- a/server/request.c
+++ b/server/request.c
@@ -571,7 +571,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 );
- create_process( client, NULL, 0 );
+ create_process( client, NULL, 0, NULL );
}
}
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 385ea3bbfda..c507294b49d 100644
--- a/server/token.c
+++ b/server/token.c
@@ -851,6 +851,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;
@@ -1278,6 +1284,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.13.1

0011-advapi32-Use-token-in-CreateProcessAsUserW-and-Creat.patch

From fecf001b60985a443bf41ae4b45911fc1f500b23 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:11:15 +0200
Subject: advapi32: Use token in CreateProcessAsUserW and
CreateProcessWithTokenW.
---
dlls/advapi32/security.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/dlls/advapi32/security.c b/dlls/advapi32/security.c
index c531e45c9a0..4fc27ef82f9 100644
--- a/dlls/advapi32/security.c
+++ b/dlls/advapi32/security.c
@@ -5700,13 +5700,14 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessAsUserW(
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation )
{
- FIXME("%p %s %s %p %p %d 0x%08x %p %s %p %p - semi-stub\n", hToken,
+ TRACE("%p %s %s %p %p %d 0x%08x %p %s %p %p\n", hToken,
debugstr_w(lpApplicationName), debugstr_w(lpCommandLine), lpProcessAttributes,
lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment,
debugstr_w(lpCurrentDirectory), lpStartupInfo, lpProcessInformation);
/* We should create the process with a suspended main thread */
- if (!CreateProcessW (lpApplicationName,
+ if (!CreateProcessInternalW(hToken,
+ lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
@@ -5715,7 +5716,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessAsUserW(
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
- lpProcessInformation))
+ lpProcessInformation,
+ NULL))
{
return FALSE;
}
@@ -5742,14 +5744,14 @@ BOOL WINAPI CreateProcessWithTokenW(HANDLE token, DWORD logon_flags, LPCWSTR app
DWORD creation_flags, void *environment, LPCWSTR current_directory, STARTUPINFOW *startup_info,
PROCESS_INFORMATION *process_information )
{
- FIXME("%p 0x%08x %s %s 0x%08x %p %s %p %p - semi-stub\n", token,
+ TRACE("%p 0x%08x %s %s 0x%08x %p %s %p %p\n", token,
logon_flags, debugstr_w(application_name), debugstr_w(command_line),
creation_flags, environment, debugstr_w(current_directory),
startup_info, process_information);
/* FIXME: check if handles should be inherited */
- return CreateProcessW( application_name, command_line, NULL, NULL, FALSE, creation_flags, environment,
- current_directory, startup_info, process_information );
+ return CreateProcessInternalW( token, application_name, command_line, NULL, NULL, FALSE, creation_flags, environment,
+ current_directory, startup_info, process_information, NULL );
}
/******************************************************************************
--
2.13.1

0012-user32-Start-explorer.exe-using-limited-rights.patch

From a00dfa2a7700d9faff8f2bcda3fbd3d7c807177c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Mon, 7 Aug 2017 02:53:06 +0200
Subject: user32: Start explorer.exe using limited rights.
---
dlls/advapi32/tests/security.c | 4 ++--
dlls/user32/win.c | 12 ++++++++++--
2 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/dlls/advapi32/tests/security.c b/dlls/advapi32/tests/security.c
index f1a64e29dea..52524ee6fe2 100644
--- a/dlls/advapi32/tests/security.c
+++ b/dlls/advapi32/tests/security.c
@@ -7387,7 +7387,7 @@ static void test_token_security_descriptor(void)
ret = GetTokenInformation(token4, TokenIntegrityLevel, buffer_integrity, sizeof(buffer_integrity), &size);
ok(ret, "GetTokenInformation failed with error %u\n", GetLastError());
tml = (TOKEN_MANDATORY_LABEL *)buffer_integrity;
- todo_wine ok(EqualSid(tml->Label.Sid, &medium_level), "Expected medium integrity level\n");
+ ok(EqualSid(tml->Label.Sid, &medium_level), "Expected medium integrity level\n");
size = 0;
ret = GetKernelObjectSecurity(token4, LABEL_SECURITY_INFORMATION, NULL, 0, &size);
@@ -7841,7 +7841,7 @@ static void test_child_token_sd_medium(void)
ret = GetTokenInformation(token, TokenIntegrityLevel, buffer_integrity, sizeof(buffer_integrity), &size);
ok(ret, "GetTokenInformation failed with error %u\n", GetLastError());
tml = (TOKEN_MANDATORY_LABEL *)buffer_integrity;
- todo_wine ok(EqualSid(tml->Label.Sid, &medium_level), "Expected medium integrity level\n");
+ ok(EqualSid(tml->Label.Sid, &medium_level), "Expected medium integrity level\n");
HeapFree(GetProcessHeap(), 0, sd);
}
diff --git a/dlls/user32/win.c b/dlls/user32/win.c
index cbf22374374..ea116b9d139 100644
--- a/dlls/user32/win.c
+++ b/dlls/user32/win.c
@@ -43,6 +43,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(win);
#define NB_USER_HANDLES ((LAST_USER_HANDLE - FIRST_USER_HANDLE + 1) >> 1)
#define USER_HANDLE_TO_INDEX(hwnd) ((LOWORD(hwnd) - FIRST_USER_HANDLE) >> 1)
+extern HANDLE CDECL __wine_create_default_token(BOOL admin);
+
static DWORD process_layout = ~0u;
static struct list window_surfaces = LIST_INIT( window_surfaces );
@@ -2067,6 +2069,7 @@ HWND WINAPI GetDesktopWindow(void)
WCHAR app[MAX_PATH + sizeof(explorer)/sizeof(WCHAR)];
WCHAR cmdline[MAX_PATH + (sizeof(explorer) + sizeof(args))/sizeof(WCHAR)];
WCHAR desktop[MAX_PATH];
+ HANDLE token;
void *redir;
SERVER_START_REQ( set_user_object_info )
@@ -2099,9 +2102,12 @@ HWND WINAPI GetDesktopWindow(void)
strcpyW( cmdline, app );
strcatW( cmdline, args );
+ if (!(token = __wine_create_default_token( FALSE )))
+ ERR( "Failed to create limited token\n" );
+
Wow64DisableWow64FsRedirection( &redir );
- if (CreateProcessW( app, cmdline, NULL, NULL, FALSE, DETACHED_PROCESS,
- NULL, windir, &si, &pi ))
+ if (CreateProcessAsUserW( token, app, cmdline, NULL, NULL, FALSE, DETACHED_PROCESS,
+ NULL, windir, &si, &pi ))
{
TRACE( "started explorer pid %04x tid %04x\n", pi.dwProcessId, pi.dwThreadId );
WaitForInputIdle( pi.hProcess, 10000 );
@@ -2111,6 +2117,8 @@ HWND WINAPI GetDesktopWindow(void)
else WARN( "failed to start explorer, err %d\n", GetLastError() );
Wow64RevertWow64FsRedirection( redir );
+ if (token) CloseHandle( token );
+
SERVER_START_REQ( get_desktop_window )
{
req->force = 1;
--
2.13.1

0013-server-Correctly-assign-security-labels-for-tokens.patch

From 05b008651a894dd0656b9cd08c7db32e525d546f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Mon, 7 Aug 2017 03:33:26 +0200
Subject: server: Correctly assign security labels for tokens.
---
dlls/advapi32/tests/security.c | 21 ++++++++++-----------
server/process.c | 8 +-------
server/security.h | 2 +-
server/token.c | 41 ++++++++++++++++++++++++-----------------
4 files changed, 36 insertions(+), 36 deletions(-)
diff --git a/dlls/advapi32/tests/security.c b/dlls/advapi32/tests/security.c
index 52524ee6fe2..a35baab0e25 100644
--- a/dlls/advapi32/tests/security.c
+++ b/dlls/advapi32/tests/security.c
@@ -7289,7 +7289,6 @@ static void test_token_security_descriptor(void)
defaulted = TRUE;
ret = GetSecurityDescriptorDacl(sd2, &present, &acl2, &defaulted);
ok(ret, "GetSecurityDescriptorDacl failed with error %u\n", GetLastError());
- todo_wine
ok(present, "DACL not present\n");
if (present)
@@ -7410,7 +7409,7 @@ static void test_token_security_descriptor(void)
ok(ret, "GetAce failed with error %u\n", GetLastError());
ok(ace->Header.AceType == SYSTEM_MANDATORY_LABEL_ACE_TYPE,
"Unexpected ACE type %#x\n", ace->Header.AceType);
- todo_wine ok(EqualSid(&ace->SidStart, &medium_level),
+ ok(EqualSid(&ace->SidStart, &medium_level),
"Expected medium integrity level\n");
}
@@ -7463,8 +7462,8 @@ static void test_token_security_descriptor(void)
sacl = NULL;
ret = GetSecurityDescriptorSacl(sd3, &present, &sacl, &defaulted);
ok(ret, "GetSecurityDescriptorSacl failed with error %u\n", GetLastError());
- todo_wine ok(present, "No SACL in the security descriptor\n");
- todo_wine ok(sacl != NULL, "NULL SACL in the security descriptor\n");
+ ok(present, "No SACL in the security descriptor\n");
+ ok(sacl != NULL, "NULL SACL in the security descriptor\n");
if (sacl)
{
@@ -7513,8 +7512,8 @@ static void test_token_security_descriptor(void)
sacl = NULL;
ret = GetSecurityDescriptorSacl(sd3, &present, &sacl, &defaulted);
ok(ret, "GetSecurityDescriptorSacl failed with error %u\n", GetLastError());
- todo_wine ok(present, "No SACL in the security descriptor\n");
- todo_wine ok(sacl != NULL, "NULL SACL in the security descriptor\n");
+ ok(present, "No SACL in the security descriptor\n");
+ ok(sacl != NULL, "NULL SACL in the security descriptor\n");
if (sacl)
{
@@ -7578,8 +7577,8 @@ static void test_token_security_descriptor(void)
ret = GetSecurityDescriptorSacl(sd3, &present, &sacl, &defaulted);
ok(ret, "GetSecurityDescriptorSacl failed with error %u\n", GetLastError());
- todo_wine ok(present, "No SACL in the security descriptor\n");
- todo_wine ok(sacl != NULL, "NULL SACL in the security descriptor\n");
+ ok(present, "No SACL in the security descriptor\n");
+ ok(sacl != NULL, "NULL SACL in the security descriptor\n");
if (sacl)
{
@@ -7616,8 +7615,8 @@ static void test_token_security_descriptor(void)
sacl = NULL;
ret = GetSecurityDescriptorSacl(sd3, &present, &sacl, &defaulted);
ok(ret, "GetSecurityDescriptorSacl failed with error %u\n", GetLastError());
- todo_wine ok(present, "No SACL in the security descriptor\n");
- todo_wine ok(sacl != NULL, "NULL SACL in the security descriptor\n");
+ ok(present, "No SACL in the security descriptor\n");
+ ok(sacl != NULL, "NULL SACL in the security descriptor\n");
if (sacl)
{
@@ -7834,7 +7833,7 @@ static void test_child_token_sd_medium(void)
ok(ret, "GetAce failed with error %u\n", GetLastError());
ok(ace_label->Header.AceType == SYSTEM_MANDATORY_LABEL_ACE_TYPE,
"Unexpected ACE type %#x\n", ace_label->Header.AceType);
- todo_wine ok(EqualSid(&ace_label->SidStart, &medium_level),
+ ok(EqualSid(&ace_label->SidStart, &medium_level),
"Expected medium integrity level\n");
memset(buffer_integrity, 0, sizeof(buffer_integrity));
diff --git a/server/process.c b/server/process.c
index ef2452fb8fb..ae998ab80b9 100644
--- a/server/process.c
+++ b/server/process.c
@@ -571,17 +571,11 @@ struct thread *create_process( int fd, struct thread *parent_thread, int inherit
: 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( token ? token : parent->token, TRUE, 0, NULL, NULL, 0, NULL, 0 );
+ process->token = token_duplicate( token ? token : parent->token, TRUE, 0, NULL, NULL, 0, NULL, 0, NULL );
process->affinity = parent->affinity;
}
if (!process->handles || !process->token) goto error;
- /* Assign a high security label to the token. The default would be medium
- * but Wine provides admin access to all applications right now so high
- * makes more sense for the time being. */
- if (!token_assign_label( process->token, security_high_label_sid ))
- goto error;
-
/* create the main thread */
if (pipe( request_pipe ) == -1)
{
diff --git a/server/security.h b/server/security.h
index 32dfe5f8db9..87377ccd673 100644
--- a/server/security.h
+++ b/server/security.h
@@ -59,7 +59,7 @@ extern int token_assign_label( struct token *token, PSID label );
extern struct token *token_duplicate( struct token *src_token, unsigned primary,
int impersonation_level, const struct security_descriptor *sd,
const LUID_AND_ATTRIBUTES *filter_privileges, unsigned int priv_count,
- const SID *filter_groups, unsigned int group_count );
+ const SID *filter_groups, unsigned int group_count, struct token *impersonation );
extern int token_check_privileges( struct token *token, int all_required,
const LUID_AND_ATTRIBUTES *reqprivs,
unsigned int count, LUID_AND_ATTRIBUTES *usedprivs);
diff --git a/server/token.c b/server/token.c
index c507294b49d..c6b0f0d39d3 100644
--- a/server/token.c
+++ b/server/token.c
@@ -686,7 +686,7 @@ static int filter_privilege( struct privilege *privilege, const LUID_AND_ATTRIBU
struct token *token_duplicate( struct token *src_token, unsigned primary,
int impersonation_level, const struct security_descriptor *sd,
const LUID_AND_ATTRIBUTES *filter_privileges, unsigned int priv_count,
- const SID *filter_groups, unsigned int group_count)
+ const SID *filter_groups, unsigned int group_count, struct token *impersonation)
{
const luid_t *modified_id =
primary || (impersonation_level == src_token->impersonation_level) ?
@@ -750,6 +750,12 @@ struct token *token_duplicate( struct token *src_token, unsigned primary,
if (sd) default_set_sd( &token->obj, sd, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION );
+ if (!token_assign_label( token, impersonation ? (PSID)impersonation->integrity : (PSID)token->integrity ))
+ {
+ release_object( token );
+ return NULL;
+ }
+
return token;
}
@@ -922,6 +928,12 @@ struct token *token_create_admin( void )
admin_source, NULL, -1, TokenElevationTypeFull, &high_label_sid );
/* we really need a primary group */
assert( token->primary_group );
+
+ if (!token_assign_label( token, (PSID)token->integrity ))
+ {
+ release_object( token );
+ token = NULL;
+ }
}
free( logon_sid );
@@ -980,6 +992,12 @@ static struct token *token_create_limited( void )
admin_source, NULL, -1, TokenElevationTypeLimited, &medium_label_sid );
/* we really need a primary group */
assert( token->primary_group );
+
+ if (!token_assign_label( token, (PSID)token->integrity ))
+ {
+ release_object( token );
+ token = NULL;
+ }
}
free( logon_sid );
@@ -1448,7 +1466,8 @@ DECL_HANDLER(duplicate_token)
TOKEN_DUPLICATE,
&token_ops )))
{
- struct token *token = token_duplicate( src_token, req->primary, req->impersonation_level, sd, NULL, 0, NULL, 0 );
+ struct token *token = token_duplicate( src_token, req->primary, req->impersonation_level, sd, NULL, 0,
+ NULL, 0, thread_get_impersonation_token( current ) );
if (token)
{
unsigned int access = req->access ? req->access : get_handle_access( current->process, req->handle );
@@ -1478,7 +1497,7 @@ DECL_HANDLER(filter_token)
group_count = get_sid_count( filter_groups, get_req_data_size() - priv_count * sizeof(LUID_AND_ATTRIBUTES) );
token = token_duplicate( src_token, src_token->primary, src_token->impersonation_level, NULL,
- filter_privileges, priv_count, filter_groups, group_count );
+ filter_privileges, priv_count, filter_groups, group_count, thread_get_impersonation_token( current ) );
if (token)
{
unsigned int access = get_handle_access( current->process, req->handle );
@@ -1813,23 +1832,11 @@ DECL_HANDLER(set_token_default_dacl)
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;
- }
+ token = req->admin ? token_create_admin() : token_create_limited();
if (token)
{
- if (token_assign_label( token, label ))
- reply->token = alloc_handle( current->process, token, TOKEN_ALL_ACCESS, 0 );
+ reply->token = alloc_handle( current->process, token, TOKEN_ALL_ACCESS, 0 );
release_object( token );
}
}
--
2.13.1

0014-programs-runas-Basic-implementation-for-starting-pro.patch

From 276b7ee0a235f5f15340d2327a2ab870b05eb161 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Sun, 6 Aug 2017 03:15:34 +0200
Subject: programs/runas: Basic implementation for starting processes with a
different trustlevel.
---
configure.ac | 1 +
programs/runas/Makefile.in | 8 ++
programs/runas/runas.c | 214 +++++++++++++++++++++++++++++++++++++++++++++
programs/runas/runas.h | 26 ++++++
programs/runas/runas.rc | 39 +++++++++
5 files changed, 288 insertions(+)
create mode 100644 programs/runas/Makefile.in
create mode 100644 programs/runas/runas.c
create mode 100644 programs/runas/runas.h
create mode 100644 programs/runas/runas.rc
diff --git a/configure.ac b/configure.ac
index edc100f4637..b9c648bce43 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3861,6 +3861,7 @@ WINE_CONFIG_TEST(programs/regedit/tests)
WINE_CONFIG_PROGRAM(regsvcs,,[install])
WINE_CONFIG_PROGRAM(regsvr32,,[clean,install,installbin,manpage])
WINE_CONFIG_PROGRAM(rpcss,,[clean,install])
+WINE_CONFIG_PROGRAM(runas,,[install])
WINE_CONFIG_PROGRAM(rundll.exe16,enable_win16,[install])
WINE_CONFIG_PROGRAM(rundll32,,[install])
WINE_CONFIG_PROGRAM(sc,,[install])
diff --git a/programs/runas/Makefile.in b/programs/runas/Makefile.in
new file mode 100644
index 00000000000..be9434b214a
--- /dev/null
+++ b/programs/runas/Makefile.in
@@ -0,0 +1,8 @@
+MODULE = runas.exe
+APPMODE = -mconsole -municode
+IMPORTS = advapi32 user32
+
+C_SRCS = \
+ runas.c
+
+RC_SRCS = runas.rc
diff --git a/programs/runas/runas.c b/programs/runas/runas.c
new file mode 100644
index 00000000000..cfd1c73ac56
--- /dev/null
+++ b/programs/runas/runas.c
@@ -0,0 +1,214 @@
+/*
+ * runas.exe implementation
+ *
+ * Copyright 2017 Michael Müller
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <windows.h>
+#include <wine/unicode.h>
+#include <wine/debug.h>
+
+#include "runas.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(runas);
+
+extern HANDLE CDECL __wine_create_default_token(BOOL admin);
+
+struct cmdinfo
+{
+ WCHAR *program;
+ DWORD trustlevel;
+};
+
+static void output_writeconsole(const WCHAR *str, DWORD wlen)
+{
+ DWORD count, ret;
+
+ ret = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), str, wlen, &count, NULL);
+ if (!ret)
+ {
+ DWORD len;
+ char *msgA;
+
+ /* On Windows WriteConsoleW() fails if the output is redirected. So fall
+ * back to WriteFile(), assuming the console encoding is still the right
+ * one in that case.
+ */
+ len = WideCharToMultiByte(GetConsoleOutputCP(), 0, str, wlen, NULL, 0, NULL, NULL);
+ msgA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
+ if (!msgA) return;
+
+ WideCharToMultiByte(GetConsoleOutputCP(), 0, str, wlen, msgA, len, NULL, NULL);
+ WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), msgA, len, &count, FALSE);
+ HeapFree(GetProcessHeap(), 0, msgA);
+ }
+}
+
+static void output_formatstring(const WCHAR *fmt, __ms_va_list va_args)
+{
+ WCHAR *str;
+ DWORD len;
+
+ SetLastError(NO_ERROR);
+ len = FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ fmt, 0, 0, (WCHAR *)&str, 0, &va_args);
+ if (len == 0 && GetLastError() != NO_ERROR)
+ {
+ WINE_FIXME("Could not format string: le=%u, fmt=%s\n", GetLastError(), wine_dbgstr_w(fmt));
+ return;
+ }
+ output_writeconsole(str, len);
+ LocalFree(str);
+}
+
+static void __cdecl output_message(unsigned int id, ...)
+{
+ WCHAR fmt[1024];
+ __ms_va_list va_args;
+
+ if (!LoadStringW(GetModuleHandleW(NULL), id, fmt, sizeof(fmt)/sizeof(WCHAR)))
+ {
+ WINE_FIXME("LoadString failed with %d\n", GetLastError());
+ return;
+ }
+ __ms_va_start(va_args, id);
+ output_formatstring(fmt, va_args);
+ __ms_va_end(va_args);
+}
+
+static void show_usage(void)
+{
+ output_message(STRING_USAGE);
+}
+
+static void show_trustlevels(void)
+{
+ output_message(STRING_TRUSTLEVELS);
+ ExitProcess(0);
+}
+
+static WCHAR *starts_with(WCHAR *str, const WCHAR *start)
+{
+ DWORD start_len = strlenW(start);
+ if (strlenW(str) < start_len)
+ return NULL;
+ if (strncmpW(str, start, start_len))
+ return NULL;
+ return str + start_len;
+}
+
+static BOOL parse_command_line(int argc, WCHAR *argv[], struct cmdinfo *cmd)
+{
+ static const WCHAR showtrustlevelsW[] = {'/','s','h','o','w','t','r','u','s','t','l','e','v','e','l','s',0};
+ static const WCHAR trustlevelW[] = {'/','t','r','u','s','t','l','e','v','e','l',':',0};
+ int i;
+
+ memset(cmd, 0, sizeof(*cmd));
+
+ for (i = 1; i < argc; i++)
+ {
+ if (argv[i][0] == '/')
+ {
+ WCHAR *arg;
+
+ if ((arg = starts_with(argv[i], trustlevelW)))
+ cmd->trustlevel = strtoulW(arg, NULL, 0);
+ else if (!strcmpW(argv[i], showtrustlevelsW))
+ show_trustlevels();
+ else
+ WINE_FIXME("Ignoring parameter %s\n", wine_dbgstr_w(argv[i]));
+ }
+ else if (argv[i][0])
+ {
+ if (cmd->program) return FALSE;
+ cmd->program = argv[i];
+ }
+ }
+
+ return TRUE;
+}
+
+static BOOL start_process(struct cmdinfo *cmd)
+{
+ PROCESS_INFORMATION info;
+ STARTUPINFOW startup;
+ HANDLE token = NULL;
+ BOOL ret;
+
+ if (cmd->trustlevel == 0x20000)
+ {
+ if (!(token = __wine_create_default_token(FALSE)))
+ ERR("Failed to create limited token\n");
+ }
+
+ memset(&startup, 0, sizeof(startup));
+ startup.cb = sizeof(startup);
+ ret = CreateProcessAsUserW(token, NULL, cmd->program, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info);
+ if (ret)
+ {
+ CloseHandle(info.hProcess);
+ CloseHandle(info.hThread);
+ }
+ else
+ {
+ DWORD error = GetLastError();
+ WCHAR *str;
+
+ if (FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, 0, (WCHAR *)&str, 0, NULL))
+ {