master
cpp 201 lines 7.94 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 WslSecurity.cpp
8
9 Abstract:
10
11 This file contains WSL Core security function definitions.
12
13 --*/
14
15 #include "precomp.h"
16 #include "WslSecurity.h"
17
18 std::unique_ptr<wsl::windows::common::security::privilege_context> wsl::windows::common::security::AcquirePrivilege(_In_ LPCWSTR privilegeName)
19 {
20 // Open the token of the current process.
21 wil::unique_handle token;
22 THROW_IF_WIN32_BOOL_FALSE(::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token));
23
24 auto luid = EnableTokenPrivilege(token.get(), privilegeName);
25 return std::make_unique<wsl::windows::common::security::privilege_context>(std::move(token), luid);
26 }
27
28 std::vector<std::unique_ptr<wsl::windows::common::security::privilege_context>> wsl::windows::common::security::AcquirePrivileges(
29 _In_ const std::vector<LPCWSTR>& privilegeNames)
30 {
31 std::vector<std::unique_ptr<wsl::windows::common::security::privilege_context>> context;
32 for (const auto* name : privilegeNames)
33 {
34 context.emplace_back(AcquirePrivilege(name));
35 }
36
37 return context;
38 }
39
40 void wsl::windows::common::security::ApplyProcessMitigationPolicies()
41 {
42 PROCESS_MITIGATION_DYNAMIC_CODE_POLICY codePolicy{};
43 codePolicy.AllowRemoteDowngrade = false;
44 codePolicy.AllowThreadOptOut = false;
45 codePolicy.ProhibitDynamicCode = true;
46 LOG_IF_WIN32_BOOL_FALSE(SetProcessMitigationPolicy(ProcessDynamicCodePolicy, &codePolicy, sizeof(codePolicy)));
47
48 // Note: Enabling PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY::DisallowWin32kSystemCalls
49 // breaks the service initialization logic (CoInitializeSecurity fails).
50
51 PROCESS_MITIGATION_FONT_DISABLE_POLICY fontPolicy{};
52 fontPolicy.DisableNonSystemFonts = true;
53 LOG_IF_WIN32_BOOL_FALSE(SetProcessMitigationPolicy(ProcessFontDisablePolicy, &fontPolicy, sizeof(fontPolicy)));
54
55 PROCESS_MITIGATION_IMAGE_LOAD_POLICY loadPolicy{};
56 loadPolicy.PreferSystem32Images = true;
57 LOG_IF_WIN32_BOOL_FALSE(SetProcessMitigationPolicy(ProcessImageLoadPolicy, &loadPolicy, sizeof(loadPolicy)));
58 }
59
60 SECURITY_DESCRIPTOR wsl::windows::common::security::CreateSecurityDescriptor(_In_ PSID userSid)
61 {
62 SECURITY_DESCRIPTOR sd{};
63 THROW_IF_WIN32_BOOL_FALSE(InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION));
64 THROW_IF_WIN32_BOOL_FALSE(SetSecurityDescriptorOwner(&sd, userSid, false));
65 return sd;
66 }
67
68 wil::unique_handle wsl::windows::common::security::CreateRestrictedToken(_In_ HANDLE token)
69 {
70 // N.B. These operations must be done while impersonating the user to avoid
71 // accidentally raising the integrity level.
72 auto runAsUser = wil::impersonate_token(token);
73
74 // Get the thread token with appropriate access rights.
75 wil::unique_handle newToken{};
76 THROW_IF_WIN32_BOOL_FALSE(::OpenThreadToken(
77 ::GetCurrentThread(), (TOKEN_DUPLICATE | TOKEN_QUERY | TOKEN_ADJUST_DEFAULT | TOKEN_ASSIGN_PRIMARY), TRUE, &newToken));
78
79 // Create a restricted token with only the SeChangeNotifyPrivilege privilege.
80 wil::unique_handle restrictedToken{};
81 THROW_IF_WIN32_BOOL_FALSE(::CreateRestrictedToken(newToken.get(), DISABLE_MAX_PRIVILEGE, 0, NULL, 0, NULL, 0, NULL, &restrictedToken));
82
83 // Drop the token down to medium integrity level.
84 auto [sid, sidBuffer] = wsl::windows::common::security::CreateSid(SECURITY_MANDATORY_LABEL_AUTHORITY, SECURITY_MANDATORY_MEDIUM_RID);
85 TOKEN_MANDATORY_LABEL tokenLabel{};
86 tokenLabel.Label.Attributes = SE_GROUP_INTEGRITY;
87 tokenLabel.Label.Sid = sid;
88 THROW_IF_WIN32_BOOL_FALSE(::SetTokenInformation(restrictedToken.get(), TokenIntegrityLevel, &tokenLabel, sizeof(tokenLabel)));
89 return restrictedToken;
90 }
91
92 void wsl::windows::common::security::ConfigureForCOMImpersonation(IUnknown* Instance)
93 {
94 wil::com_ptr_nothrow<IClientSecurity> clientSecurity;
95 THROW_IF_FAILED(Instance->QueryInterface(IID_PPV_ARGS(&clientSecurity)));
96
97 // Get the current proxy blanket settings.
98 DWORD authnSvc, authzSvc, authnLvl, capabilites;
99 THROW_IF_FAILED(clientSecurity->QueryBlanket(Instance, &authnSvc, &authzSvc, NULL, &authnLvl, NULL, NULL, &capabilites));
100
101 // Make sure that dynamic cloaking is used.
102 WI_ClearFlag(capabilites, EOAC_STATIC_CLOAKING);
103 WI_SetFlag(capabilites, EOAC_DYNAMIC_CLOAKING);
104 THROW_IF_FAILED(clientSecurity->SetBlanket(Instance, authnSvc, authzSvc, NULL, authnLvl, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, capabilites));
105 }
106
107 LUID wsl::windows::common::security::EnableTokenPrivilege(_Inout_ HANDLE token, _In_ LPCWSTR privilegeName)
108 {
109 // Convert privilege name to an LUID.
110 LUID luid{};
111 THROW_IF_WIN32_BOOL_FALSE(::LookupPrivilegeValueW(nullptr, privilegeName, &luid));
112
113 TOKEN_PRIVILEGES newState{};
114 newState.PrivilegeCount = 1;
115 newState.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
116 newState.Privileges[0].Luid = luid;
117 THROW_IF_WIN32_BOOL_FALSE(::AdjustTokenPrivileges(token, FALSE, &newState, 0, nullptr, nullptr));
118
119 return luid;
120 }
121
122 DWORD wsl::windows::common::security::GetUserBasicIntegrityLevel(_In_ HANDLE token)
123 {
124 // Get the integrity level.
125 const auto label = wil::get_token_information<TOKEN_MANDATORY_LABEL>(token);
126 DWORD BasicIntegrityLevel =
127 *GetSidSubAuthority(label->Label.Sid, static_cast<UCHAR>(*::GetSidSubAuthorityCount(label->Label.Sid) - 1));
128
129 // Convert the range of medium integrity level to a single level.
130 if ((BasicIntegrityLevel >= SECURITY_MANDATORY_MEDIUM_RID) && (BasicIntegrityLevel < SECURITY_MANDATORY_HIGH_RID))
131 {
132 BasicIntegrityLevel = SECURITY_MANDATORY_MEDIUM_RID;
133 }
134
135 return BasicIntegrityLevel;
136 }
137
138 bool wsl::windows::common::security::IsTokenElevated(_In_ HANDLE token)
139 {
140 return (GetUserBasicIntegrityLevel(token) == SECURITY_MANDATORY_HIGH_RID);
141 }
142
143 wil::unique_handle wsl::windows::common::security::GetUserToken(_In_ TOKEN_TYPE tokenType, _In_ RPC_BINDING_HANDLE handle)
144 {
145 wil::unique_handle contextToken;
146
147 // Start by impersonating the caller and getting their token-user data out.
148 {
149 std::variant<int, wil::unique_coreverttoself_call, unique_revert_to_self> runAsClient;
150
151 if (handle == nullptr)
152 {
153 runAsClient = wil::CoImpersonateClient();
154 }
155 else
156 {
157 runAsClient = RpcImpersonateCaller(handle);
158 }
159
160 THROW_IF_WIN32_BOOL_FALSE(::OpenThreadToken(GetCurrentThread(), TOKEN_DUPLICATE | TOKEN_READ, TRUE, &contextToken));
161 }
162
163 wil::unique_handle newToken;
164 THROW_IF_WIN32_BOOL_FALSE(::DuplicateTokenEx(
165 contextToken.get(),
166 TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_DEFAULT,
167 nullptr,
168 SecurityImpersonation,
169 tokenType,
170 &newToken));
171
172 // If the token integrity level is system, reduce it to high integrity level. The VM worker process runs at
173 // high integrity level and objects created with a higher integrity level token may be inaccessible.
174 if (GetUserBasicIntegrityLevel(newToken.get()) == SECURITY_MANDATORY_SYSTEM_RID)
175 {
176 auto [sid, sidBuffer] = wsl::windows::common::security::CreateSid(SECURITY_MANDATORY_LABEL_AUTHORITY, SECURITY_MANDATORY_HIGH_RID);
177 TOKEN_MANDATORY_LABEL tokenLabel{};
178 tokenLabel.Label.Attributes = SE_GROUP_INTEGRITY;
179 tokenLabel.Label.Sid = sid;
180 THROW_IF_WIN32_BOOL_FALSE(::SetTokenInformation(newToken.get(), TokenIntegrityLevel, &tokenLabel, sizeof(tokenLabel)));
181 }
182
183 return newToken;
184 }
185
186 bool wsl::windows::common::security::IsTokenLocalSystem(_In_opt_ HANDLE token)
187 {
188 auto [sid, sidBuffer] = wsl::windows::common::security::CreateSid(SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SYSTEM_RID);
189
190 BOOL member{};
191 THROW_IF_WIN32_BOOL_FALSE(::CheckTokenMembership(token, sid, &member));
192
193 return member ? true : false;
194 }
195
196 wsl::windows::common::security::unique_revert_to_self wsl::windows::common::security::RpcImpersonateCaller(_In_ RPC_BINDING_HANDLE handle)
197 {
198 THROW_IF_WIN32_ERROR(static_cast<DWORD>(RpcImpersonateClient(handle)));
199
200 return {};
201 }