]>
Commit | Line | Data |
---|---|---|
5072c0bb BD |
1 | /* |
2 | * Copyright (c) 2007 Brian Dessent | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * A copy of the GNU General Public License can be found at | |
10 | * http://www.gnu.org/ | |
11 | * | |
12 | * Written by Brian Dessent <brian@dessent.net> | |
13 | * | |
14 | */ | |
15 | ||
5072c0bb | 16 | #include "win32.h" |
2e0aaec9 | 17 | #include <memory> |
b41c2908 | 18 | #include <malloc.h> |
5072c0bb | 19 | #include "LogFile.h" |
d29a864d | 20 | #include "resource.h" |
2801c09c | 21 | #include "ini.h" |
89cc2408 | 22 | #include <sys/stat.h> |
20f237b4 | 23 | #include "String++.h" |
5072c0bb | 24 | |
be617b59 CV |
25 | NTSecurity nt_sec; |
26 | ||
7d0ffe17 CV |
27 | #define ALL_INHERIT_ACE (CONTAINER_INHERIT_ACE \ |
28 | | OBJECT_INHERIT_ACE \ | |
29 | | INHERIT_ONLY_ACE) | |
30 | #define NO_INHERIT_ACE (0) | |
31 | ||
26922cd2 CV |
32 | PSECURITY_DESCRIPTOR |
33 | NTSecurity::GetPosixPerms (const char *fname, PSID owner_sid, PSID group_sid, | |
34 | mode_t mode, SECURITY_DESCRIPTOR &out_sd, acl_t &acl) | |
be617b59 | 35 | { |
26922cd2 | 36 | DWORD u_attribute, g_attribute, o_attribute; |
89cc2408 | 37 | DWORD offset = 0; |
b41c2908 | 38 | |
be617b59 CV |
39 | /* Initialize out SD */ |
40 | if (!InitializeSecurityDescriptor (&out_sd, SECURITY_DESCRIPTOR_REVISION)) | |
157dc2b8 | 41 | Log (LOG_TIMESTAMP) << "InitializeSecurityDescriptor(" << fname |
b41c2908 | 42 | << ") failed: " << GetLastError () << endLog; |
1e029da2 | 43 | out_sd.Control |= SE_DACL_PROTECTED; |
5072c0bb | 44 | |
be617b59 | 45 | /* Initialize ACL and fill with almost POSIX-like permissions. |
b41c2908 CV |
46 | Note that the current user always requires write permissions, otherwise |
47 | creating files in directories with restricted permissions fails. */ | |
be617b59 | 48 | if (!InitializeAcl (&acl.acl , sizeof acl, ACL_REVISION)) |
157dc2b8 | 49 | Log (LOG_TIMESTAMP) << "InitializeAcl(" << fname << ") failed: " |
be617b59 | 50 | << GetLastError () << endLog; |
89cc2408 | 51 | /* USER */ |
26922cd2 CV |
52 | /* Default user to current user. */ |
53 | if (!owner_sid) | |
54 | owner_sid = ownerSID.user.User.Sid; | |
89cc2408 | 55 | u_attribute = STANDARD_RIGHTS_ALL | FILE_GENERIC_READ | FILE_GENERIC_WRITE; |
b41c2908 | 56 | if (mode & 0100) // S_IXUSR |
89cc2408 | 57 | u_attribute |= FILE_GENERIC_EXECUTE; |
b41c2908 | 58 | if ((mode & 0300) == 0300) // S_IWUSR | S_IXUSR |
89cc2408 | 59 | u_attribute |= FILE_DELETE_CHILD; |
7d0ffe17 CV |
60 | if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, NO_INHERIT_ACE, |
61 | u_attribute, owner_sid)) | |
62 | Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname | |
b41c2908 | 63 | << ", owner) failed: " << GetLastError () << endLog; |
89cc2408 CV |
64 | else |
65 | offset++; | |
66 | /* GROUP */ | |
26922cd2 CV |
67 | /* Default group to current primary group. */ |
68 | if (!group_sid) | |
69 | group_sid = groupSID; | |
89cc2408 | 70 | g_attribute = STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES; |
b41c2908 | 71 | if (mode & 0040) // S_IRGRP |
89cc2408 | 72 | g_attribute |= FILE_GENERIC_READ; |
b41c2908 | 73 | if (mode & 0020) // S_IWGRP |
89cc2408 | 74 | g_attribute |= FILE_GENERIC_WRITE; |
b41c2908 | 75 | if (mode & 0010) // S_IXGRP |
89cc2408 | 76 | g_attribute |= FILE_GENERIC_EXECUTE; |
b41c2908 | 77 | if ((mode & 01030) == 00030) // S_IWGRP | S_IXGRP, !S_ISVTX |
89cc2408 | 78 | g_attribute |= FILE_DELETE_CHILD; |
7d0ffe17 CV |
79 | if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, NO_INHERIT_ACE, |
80 | g_attribute, group_sid)) | |
81 | Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname | |
b41c2908 | 82 | << ", group) failed: " << GetLastError () << endLog; |
89cc2408 CV |
83 | else |
84 | offset++; | |
85 | /* OTHER */ | |
86 | o_attribute = STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES; | |
b41c2908 | 87 | if (mode & 0004) // S_IROTH |
89cc2408 | 88 | o_attribute |= FILE_GENERIC_READ; |
b41c2908 | 89 | if (mode & 0002) // S_IWOTH |
89cc2408 | 90 | o_attribute |= FILE_GENERIC_WRITE; |
b41c2908 | 91 | if (mode & 0001) // S_IXOTH |
89cc2408 | 92 | o_attribute |= FILE_GENERIC_EXECUTE; |
b41c2908 | 93 | if ((mode & 01003) == 00003) // S_IWOTH | S_IXOTH, !S_ISVTX |
89cc2408 | 94 | o_attribute |= FILE_DELETE_CHILD; |
7d0ffe17 CV |
95 | if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, NO_INHERIT_ACE, |
96 | o_attribute, everyOneSID.theSID ())) | |
97 | Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname | |
b41c2908 | 98 | << ", everyone) failed: " << GetLastError () << endLog; |
89cc2408 CV |
99 | else |
100 | offset++; | |
be617b59 CV |
101 | if (mode & 07000) /* At least one of S_ISUID, S_ISGID, S_ISVTX */ |
102 | { | |
89cc2408 | 103 | DWORD attribute = 0; |
be617b59 CV |
104 | if (mode & 04000) // S_ISUID |
105 | attribute |= FILE_APPEND_DATA; | |
106 | if (mode & 02000) // S_ISGID | |
107 | attribute |= FILE_WRITE_DATA; | |
108 | if (mode & 01000) // S_ISVTX | |
109 | attribute |= FILE_READ_DATA; | |
7d0ffe17 CV |
110 | if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, NO_INHERIT_ACE, |
111 | attribute, nullSID.theSID ())) | |
112 | Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname | |
be617b59 | 113 | << ", null) failed: " << GetLastError () << endLog; |
89cc2408 CV |
114 | else |
115 | offset++; | |
116 | } | |
117 | /* For directories, we also add inherit-only ACEs for CREATOR OWNER, | |
118 | CREATOR GROUP, and EVERYONE (aka OTHER). */ | |
119 | if (mode & S_IFDIR) | |
120 | { | |
d285d991 CV |
121 | if (mode & 01000) // S_ISVTX |
122 | { | |
123 | /* Don't allow default write permissions for group and other | |
124 | in a S_ISVTX dir. */ | |
125 | /* GROUP */ | |
126 | g_attribute = STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES; | |
127 | if (mode & 0040) // S_IRGRP | |
128 | g_attribute |= FILE_GENERIC_READ; | |
129 | if (mode & 0010) // S_IXGRP | |
130 | g_attribute |= FILE_GENERIC_EXECUTE; | |
131 | /* OTHER */ | |
132 | o_attribute = STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES; | |
133 | if (mode & 0004) // S_IROTH | |
134 | o_attribute |= FILE_GENERIC_READ; | |
135 | if (mode & 0001) // S_IXOTH | |
136 | o_attribute |= FILE_GENERIC_EXECUTE; | |
137 | } | |
7d0ffe17 CV |
138 | if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, ALL_INHERIT_ACE, |
139 | u_attribute, cr_ownerSID.theSID ())) | |
140 | Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname | |
89cc2408 CV |
141 | << ", creator owner) failed: " |
142 | << GetLastError () << endLog; | |
143 | else | |
7d0ffe17 CV |
144 | offset++; |
145 | if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, ALL_INHERIT_ACE, | |
146 | g_attribute, cr_groupSID.theSID ())) | |
147 | Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname | |
89cc2408 CV |
148 | << ", creator group) failed: " |
149 | << GetLastError () << endLog; | |
150 | else | |
7d0ffe17 CV |
151 | offset++; |
152 | if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, ALL_INHERIT_ACE, | |
153 | o_attribute, everyOneSID.theSID ())) | |
154 | Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname | |
89cc2408 CV |
155 | << ", everyone inherit) failed: " |
156 | << GetLastError () << endLog; | |
157 | else | |
7d0ffe17 | 158 | offset++; |
be617b59 | 159 | } |
b41c2908 CV |
160 | |
161 | /* Set SD's DACL to just created ACL. */ | |
be617b59 | 162 | if (!SetSecurityDescriptorDacl (&out_sd, TRUE, &acl.acl, FALSE)) |
157dc2b8 | 163 | Log (LOG_TIMESTAMP) << "SetSecurityDescriptorDacl(" << fname |
b41c2908 | 164 | << ") failed: " << GetLastError () << endLog; |
26922cd2 CV |
165 | return &out_sd; |
166 | } | |
167 | ||
5072c0bb BD |
168 | void |
169 | NTSecurity::NoteFailedAPI (const std::string &api) | |
170 | { | |
157dc2b8 | 171 | Log (LOG_TIMESTAMP) << api << "() failed: " << GetLastError () << endLog; |
5072c0bb BD |
172 | } |
173 | ||
174 | void | |
be617b59 | 175 | NTSecurity::initialiseWellKnownSIDs () |
5072c0bb | 176 | { |
be617b59 CV |
177 | SID_IDENTIFIER_AUTHORITY n_sid_auth = { SECURITY_NULL_SID_AUTHORITY }; |
178 | /* Get the SID for "NULL" S-1-0-0 */ | |
179 | if (!AllocateAndInitializeSid (&n_sid_auth, 1, SECURITY_NULL_RID, | |
180 | 0, 0, 0, 0, 0, 0, 0, &nullSID.theSID ())) | |
be617b59 | 181 | return; |
be617b59 CV |
182 | SID_IDENTIFIER_AUTHORITY e_sid_auth = { SECURITY_WORLD_SID_AUTHORITY }; |
183 | /* Get the SID for "Everyone" S-1-1-0 */ | |
184 | if (!AllocateAndInitializeSid (&e_sid_auth, 1, SECURITY_WORLD_RID, | |
185 | 0, 0, 0, 0, 0, 0, 0, &everyOneSID.theSID ())) | |
be617b59 | 186 | return; |
be617b59 CV |
187 | SID_IDENTIFIER_AUTHORITY nt_sid_auth = { SECURITY_NT_AUTHORITY }; |
188 | /* Get the SID for "Administrators" S-1-5-32-544 */ | |
157dc2b8 | 189 | if (!AllocateAndInitializeSid (&nt_sid_auth, 2, SECURITY_BUILTIN_DOMAIN_RID, |
be617b59 CV |
190 | DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, |
191 | &administratorsSID.theSID ())) | |
be617b59 | 192 | return; |
be617b59 | 193 | /* Get the SID for "Users" S-1-5-32-545 */ |
157dc2b8 | 194 | if (!AllocateAndInitializeSid (&nt_sid_auth, 2, SECURITY_BUILTIN_DOMAIN_RID, |
be617b59 CV |
195 | DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0, |
196 | &usersSID.theSID ())) | |
be617b59 | 197 | return; |
89cc2408 CV |
198 | SID_IDENTIFIER_AUTHORITY c_sid_auth = { SECURITY_CREATOR_SID_AUTHORITY }; |
199 | /* Get the SID for "CREATOR OWNER" S-1-3-0 */ | |
157dc2b8 | 200 | if (!AllocateAndInitializeSid (&c_sid_auth, 1, SECURITY_CREATOR_OWNER_RID, |
26922cd2 | 201 | 0, 0, 0, 0, 0, 0, 0, &cr_ownerSID.theSID ())) |
89cc2408 | 202 | return; |
89cc2408 | 203 | /* Get the SID for "CREATOR GROUP" S-1-3-1 */ |
157dc2b8 | 204 | if (!AllocateAndInitializeSid (&c_sid_auth, 1, SECURITY_CREATOR_GROUP_RID, |
26922cd2 | 205 | 0, 0, 0, 0, 0, 0, 0, &cr_groupSID.theSID ())) |
89cc2408 | 206 | return; |
be617b59 | 207 | wellKnownSIDsinitialized (true); |
5072c0bb BD |
208 | } |
209 | ||
210 | void | |
211 | NTSecurity::setDefaultDACL () | |
212 | { | |
157dc2b8 | 213 | /* To assure that the created files have a useful ACL, the |
5072c0bb BD |
214 | default DACL in the process token is set to full access to |
215 | everyone. This applies to files and subdirectories created | |
216 | in directories which don't propagate permissions to child | |
157dc2b8 | 217 | objects. |
5072c0bb | 218 | To assure that the files group is meaningful, a token primary |
be617b59 CV |
219 | group of None is changed to Users or Administrators. |
220 | This is the fallback if real POSIX permissions don't | |
221 | work for some reason. */ | |
5072c0bb BD |
222 | |
223 | /* Create a buffer which has enough room to contain the TOKEN_DEFAULT_DACL | |
224 | structure plus an ACL with one ACE. */ | |
225 | size_t bufferSize = sizeof (ACL) + sizeof (ACCESS_ALLOWED_ACE) | |
226 | + GetLengthSid (everyOneSID.theSID ()) - sizeof (DWORD); | |
227 | ||
1c6add37 | 228 | std::unique_ptr<char[]> buf (new char[bufferSize]); |
5072c0bb BD |
229 | |
230 | /* First initialize the TOKEN_DEFAULT_DACL structure. */ | |
231 | PACL dacl = (PACL) buf.get (); | |
232 | ||
233 | /* Initialize the ACL for containing one ACE. */ | |
234 | if (!InitializeAcl (dacl, bufferSize, ACL_REVISION)) | |
235 | { | |
236 | NoteFailedAPI ("InitializeAcl"); | |
5072c0bb BD |
237 | return; |
238 | } | |
239 | ||
240 | /* Create the ACE which grants full access to "Everyone" and store it | |
241 | in dacl. */ | |
7d0ffe17 CV |
242 | if (!AddAccessAllowedAceEx (dacl, ACL_REVISION, NO_INHERIT_ACE, |
243 | GENERIC_ALL, everyOneSID.theSID ())) | |
5072c0bb | 244 | { |
7d0ffe17 | 245 | NoteFailedAPI ("AddAccessAllowedAceEx"); |
5072c0bb BD |
246 | return; |
247 | } | |
248 | ||
249 | /* Set the default DACL to the above computed ACL. */ | |
157dc2b8 | 250 | if (!SetTokenInformation (token.theHANDLE(), TokenDefaultDacl, &dacl, |
5072c0bb | 251 | bufferSize)) |
be617b59 | 252 | NoteFailedAPI ("SetTokenInformation"); |
5072c0bb BD |
253 | } |
254 | ||
255 | void | |
be617b59 | 256 | NTSecurity::setBackupPrivileges () |
5072c0bb | 257 | { |
b41c2908 CV |
258 | LUID backup, restore; |
259 | if (!LookupPrivilegeValue (NULL, SE_BACKUP_NAME, &backup)) | |
260 | NoteFailedAPI ("LookupPrivilegeValue"); | |
261 | else if (!LookupPrivilegeValue (NULL, SE_RESTORE_NAME, &restore)) | |
262 | NoteFailedAPI ("LookupPrivilegeValue"); | |
263 | else | |
264 | { | |
265 | PTOKEN_PRIVILEGES new_privs; | |
266 | ||
267 | new_privs = (PTOKEN_PRIVILEGES) alloca (sizeof (TOKEN_PRIVILEGES) | |
268 | + sizeof (LUID_AND_ATTRIBUTES)); | |
269 | new_privs->PrivilegeCount = 2; | |
270 | new_privs->Privileges[0].Luid = backup; | |
271 | new_privs->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; | |
272 | new_privs->Privileges[1].Luid = restore; | |
273 | new_privs->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED; | |
274 | if (!AdjustTokenPrivileges (token.theHANDLE (), FALSE, new_privs, | |
275 | 0, NULL, NULL)) | |
276 | NoteFailedAPI ("AdjustTokenPrivileges"); | |
277 | else if (GetLastError () == ERROR_NOT_ALL_ASSIGNED) | |
157dc2b8 AG |
278 | Log (LOG_TIMESTAMP) << "User has NO backup/restore rights" << endLog; |
279 | else | |
280 | Log (LOG_TIMESTAMP) << "User has backup/restore rights" << endLog; | |
b41c2908 | 281 | } |
be617b59 CV |
282 | } |
283 | ||
770e3aed CV |
284 | void |
285 | NTSecurity::resetPrimaryGroup () | |
d29a864d CV |
286 | { |
287 | if (primaryGroupSID.pgrp.PrimaryGroup) | |
288 | { | |
157dc2b8 | 289 | Log (LOG_TIMESTAMP) << "Changing gid back to original" << endLog; |
d29a864d CV |
290 | if (!SetTokenInformation (token.theHANDLE (), TokenPrimaryGroup, |
291 | &primaryGroupSID, sizeof primaryGroupSID)) | |
292 | NoteFailedAPI ("SetTokenInformation"); | |
293 | } | |
294 | } | |
295 | ||
296 | void | |
297 | NTSecurity::setAdminGroup () | |
770e3aed CV |
298 | { |
299 | TOKEN_PRIMARY_GROUP tpg; | |
300 | ||
d29a864d | 301 | tpg.PrimaryGroup = administratorsSID.theSID (); |
157dc2b8 | 302 | Log (LOG_TIMESTAMP) << "Changing gid to Administrators" << endLog; |
d29a864d CV |
303 | if (!SetTokenInformation (token.theHANDLE (), TokenPrimaryGroup, |
304 | &tpg, sizeof tpg)) | |
770e3aed | 305 | NoteFailedAPI ("SetTokenInformation"); |
26922cd2 CV |
306 | else |
307 | groupSID = administratorsSID.theSID (); | |
770e3aed CV |
308 | } |
309 | ||
be617b59 | 310 | void |
78e83185 | 311 | NTSecurity::setDefaultSecurity () |
be617b59 | 312 | { |
be617b59 CV |
313 | /* Get the processes access token. */ |
314 | if (!OpenProcessToken (GetCurrentProcess (), | |
315 | TOKEN_READ | TOKEN_ADJUST_DEFAULT | |
316 | | TOKEN_ADJUST_PRIVILEGES, &token.theHANDLE ())) | |
317 | { | |
318 | NoteFailedAPI ("OpenProcessToken"); | |
319 | return; | |
320 | } | |
321 | ||
322 | /* Set backup and restore privileges if available. */ | |
323 | setBackupPrivileges (); | |
b41c2908 | 324 | |
449640c4 | 325 | /* Log if symlink creation privilege is available. */ |
7850e958 | 326 | if (!hasSymlinkCreationRights()) |
449640c4 JT |
327 | Log (LOG_TIMESTAMP) << "User has NO symlink creation right" << endLog; |
328 | else | |
329 | Log (LOG_TIMESTAMP) << "User has symlink creation right" << endLog; | |
330 | ||
be617b59 CV |
331 | /* If initializing the well-known SIDs didn't work, we're finished here. */ |
332 | if (!wellKnownSIDsinitialized ()) | |
5072c0bb BD |
333 | return; |
334 | ||
be617b59 CV |
335 | /* Set the default DACL to all permissions for everyone as a fallback. */ |
336 | setDefaultDACL (); | |
337 | ||
5072c0bb | 338 | /* Get the user */ |
157dc2b8 | 339 | if (!GetTokenInformation (token.theHANDLE (), TokenUser, &ownerSID, |
26922cd2 | 340 | sizeof ownerSID, &size)) |
5072c0bb | 341 | { |
d29a864d | 342 | NoteFailedAPI ("GetTokenInformation(user)"); |
5072c0bb BD |
343 | return; |
344 | } | |
345 | /* Make it the owner */ | |
26922cd2 | 346 | TOKEN_OWNER owner = { ownerSID.user.User.Sid }; |
157dc2b8 | 347 | if (!SetTokenInformation (token.theHANDLE (), TokenOwner, &owner, |
d29a864d | 348 | sizeof owner)) |
5072c0bb | 349 | { |
d29a864d | 350 | NoteFailedAPI ("SetTokenInformation(owner)"); |
5072c0bb BD |
351 | return; |
352 | } | |
3d427dc9 | 353 | /* Get original primary group */ |
770e3aed | 354 | if (!GetTokenInformation (token.theHANDLE (), TokenPrimaryGroup, |
d29a864d | 355 | &primaryGroupSID, sizeof primaryGroupSID, &size)) |
770e3aed | 356 | { |
d29a864d CV |
357 | NoteFailedAPI("GetTokenInformation(pgrp)"); |
358 | primaryGroupSID.pgrp.PrimaryGroup = (PSID) NULL; | |
770e3aed | 359 | } |
26922cd2 | 360 | groupSID = primaryGroupSID.pgrp.PrimaryGroup; |
5072c0bb BD |
361 | } |
362 | ||
aa09dcbb CV |
363 | bool |
364 | NTSecurity::isRunAsAdmin () | |
365 | { | |
366 | BOOL is_run_as_admin = FALSE; | |
367 | if (!CheckTokenMembership(NULL, administratorsSID.theSID (), &is_run_as_admin)) | |
507e5b52 | 368 | NoteFailedAPI("CheckTokenMembership(administratorsSID)"); |
aa09dcbb CV |
369 | return (is_run_as_admin == TRUE); |
370 | } | |
371 | ||
449640c4 JT |
372 | bool |
373 | NTSecurity::hasSymlinkCreationRights () | |
374 | { | |
375 | LUID symlink; | |
376 | if (!LookupPrivilegeValue (NULL, SE_CREATE_SYMBOLIC_LINK_NAME, &symlink)) | |
377 | { | |
378 | NoteFailedAPI ("LookupPrivilegeValue"); | |
379 | return FALSE; | |
380 | } | |
381 | ||
382 | DWORD size; | |
383 | GetTokenInformation (token.theHANDLE (), TokenPrivileges, NULL, 0, &size); | |
384 | /* Will fail ERROR_INSUFFICIENT_BUFFER, but updates size */ | |
385 | ||
386 | TOKEN_PRIVILEGES *privileges = (TOKEN_PRIVILEGES *)alloca(size); | |
387 | if (!GetTokenInformation (token.theHANDLE (), TokenPrivileges, privileges, | |
388 | size, &size)) | |
389 | { | |
390 | NoteFailedAPI ("GetTokenInformation(privileges)"); | |
391 | return FALSE; | |
392 | } | |
393 | ||
394 | unsigned int i; | |
395 | for (i = 0; i < privileges->PrivilegeCount; i++) | |
396 | { | |
397 | if (memcmp(&privileges->Privileges[i].Luid, &symlink, sizeof(LUID)) == 0) | |
398 | { | |
beaffbbd | 399 | return TRUE; |
449640c4 JT |
400 | } |
401 | } | |
402 | ||
403 | return FALSE; | |
404 | } | |
aa09dcbb | 405 | |
5072c0bb BD |
406 | VersionInfo::VersionInfo () |
407 | { | |
408 | v.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); | |
409 | if (GetVersionEx (&v) == 0) | |
410 | { | |
157dc2b8 | 411 | Log (LOG_PLAIN) << "GetVersionEx () failed: " << GetLastError () |
5072c0bb | 412 | << endLog; |
157dc2b8 | 413 | |
5072c0bb BD |
414 | /* If GetVersionEx fails we really should bail with an error of some kind, |
415 | but for now just assume we're on NT and continue. */ | |
416 | v.dwPlatformId = VER_PLATFORM_WIN32_NT; | |
417 | } | |
418 | } | |
419 | ||
420 | /* This is the Construct on First Use idiom to avoid static initialization | |
421 | order problems. */ | |
422 | VersionInfo& GetVer () | |
423 | { | |
424 | static VersionInfo *vi = new VersionInfo (); | |
425 | return *vi; | |
426 | } | |
e73fb694 JT |
427 | |
428 | /* Identify native machine arch if we are running under WoW */ | |
429 | USHORT | |
430 | WowNativeMachine () | |
431 | { | |
e73fb694 JT |
432 | typedef BOOL (WINAPI *PFNISWOW64PROCESS2)(HANDLE, USHORT *, USHORT *); |
433 | PFNISWOW64PROCESS2 pfnIsWow64Process2 = (PFNISWOW64PROCESS2)GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process2"); | |
434 | ||
435 | typedef BOOL (WINAPI *PFNISWOW64PROCESS)(HANDLE, PBOOL); | |
436 | PFNISWOW64PROCESS pfnIsWow64Process = (PFNISWOW64PROCESS)GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process"); | |
437 | ||
438 | USHORT processMachine, nativeMachine; | |
439 | if ((pfnIsWow64Process2) && | |
440 | (pfnIsWow64Process2(GetCurrentProcess(), &processMachine, &nativeMachine))) | |
441 | return nativeMachine; | |
442 | else if (pfnIsWow64Process) { | |
70fc13d3 | 443 | #ifdef _X86_ |
e73fb694 JT |
444 | BOOL bIsWow64 = FALSE; |
445 | if (pfnIsWow64Process(GetCurrentProcess(), &bIsWow64)) | |
446 | return bIsWow64 ? IMAGE_FILE_MACHINE_AMD64 : IMAGE_FILE_MACHINE_I386; | |
70fc13d3 | 447 | #endif |
e73fb694 | 448 | } |
70fc13d3 | 449 | |
4936dbca JT |
450 | #ifdef __x86_64__ |
451 | return IMAGE_FILE_MACHINE_AMD64; | |
452 | #else | |
e73fb694 JT |
453 | return IMAGE_FILE_MACHINE_I386; |
454 | #endif | |
455 | } | |
a2362bc5 | 456 | |
fd571d50 JT |
457 | /* Identify machine arch for the current process */ |
458 | USHORT | |
459 | WindowsProcessMachine () | |
460 | { | |
461 | #if defined(__x86_64__) | |
462 | USHORT processMachine = IMAGE_FILE_MACHINE_AMD64; | |
463 | #elif defined(__i386__) | |
464 | USHORT processMachine = IMAGE_FILE_MACHINE_I386; | |
465 | #elif defined(__aarch64__) | |
466 | USHORT processMachine = IMAGE_FILE_MACHINE_ARM64; | |
467 | #else | |
468 | #error "Unknown architecture" | |
469 | #endif | |
470 | return processMachine; | |
471 | } | |
472 | ||
473 | /* Convert a machine arch identifier to a string */ | |
474 | const std::string | |
475 | machine_name(USHORT machine) | |
476 | { | |
477 | switch (machine) | |
478 | { | |
479 | case IMAGE_FILE_MACHINE_I386: | |
480 | return "x86"; | |
481 | break; | |
482 | case IMAGE_FILE_MACHINE_AMD64: | |
483 | return "x86_64"; | |
484 | break; | |
485 | case IMAGE_FILE_MACHINE_ARM64: | |
486 | return "ARM64"; | |
487 | break; | |
488 | default: | |
489 | std::stringstream machine_desc; | |
490 | machine_desc << std::hex << machine; | |
491 | return machine_desc.str(); | |
492 | } | |
493 | } | |
494 | ||
477a6ff5 JT |
495 | const std::wstring |
496 | LoadStringWEx(UINT uID, UINT langId) | |
497 | { | |
498 | HINSTANCE hInstance = GetModuleHandle(NULL); | |
499 | ||
500 | // Convert the string ID into a bundle number | |
501 | LPCSTR bundle = MAKEINTRESOURCE(uID / 16 + 1); | |
502 | HRSRC hRes = ::FindResourceEx(hInstance, RT_STRING, bundle, langId); | |
503 | if (hRes) | |
504 | { | |
505 | HGLOBAL h = ::LoadResource(hInstance, hRes); | |
506 | if (h) | |
507 | { | |
508 | HGLOBAL hGlob = ::LockResource(h); | |
509 | ||
510 | // walk string bundle | |
511 | wchar_t *buf = (wchar_t *)hGlob; | |
512 | for (unsigned int i = 0; i < (uID & 15); i++) | |
513 | { | |
514 | buf += 1 + (UINT)*buf; | |
515 | } | |
516 | ||
517 | int len = *buf; | |
518 | return std::wstring(buf + 1, len); | |
519 | } | |
520 | } | |
521 | // N.B.: Due to the way string bundles are encoded, there's no difference | |
522 | // between an absent string resource whose bundle is present, and a string | |
523 | // resource containing the null string. | |
524 | return L""; | |
525 | } | |
526 | ||
a2362bc5 JT |
527 | const std::wstring |
528 | LoadStringW(unsigned int uID) | |
529 | { | |
530 | wchar_t *buf; | |
531 | ||
532 | int len = ::LoadStringW(GetModuleHandle(NULL), uID, (LPWSTR)&buf, 0); | |
533 | if (len > 0) | |
534 | return std::wstring(buf, len); | |
535 | ||
7409282c JT |
536 | // if empty or absent, fallback to the untranslated string |
537 | return LoadStringWEx(uID, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)); | |
a2362bc5 | 538 | } |
b60d80c6 | 539 | |
20f237b4 JT |
540 | const std::string |
541 | LoadStringUtf8(unsigned int uID) | |
542 | { | |
543 | return wstring_to_string(LoadStringW(uID)); | |
544 | } | |
545 | ||
b60d80c6 JT |
546 | bool |
547 | is_developer_mode(void) | |
548 | { | |
549 | HKEY hKey; | |
550 | LSTATUS err = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", 0, KEY_READ, &hKey); | |
551 | if (err != ERROR_SUCCESS) | |
552 | return false; | |
553 | ||
554 | DWORD value; | |
555 | DWORD size = sizeof(DWORD); | |
556 | err = RegQueryValueExW(hKey, L"AllowDevelopmentWithoutDevLicense", NULL, NULL, reinterpret_cast<LPBYTE>(&value), &size); | |
557 | RegCloseKey(hKey); | |
558 | if (err != ERROR_SUCCESS) | |
559 | return false; | |
560 | ||
561 | return value != 0; | |
562 | } |