Use latest Cygwin setup on AppVeyor
[cygwin-setup.git] / win32.cc
bloba403d662bd75691abc8f3be395c21ce335e1227f
1 /*
2 * Copyright (c) 2007 Brian Dessent
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.
9 * A copy of the GNU General Public License can be found at
10 * http://www.gnu.org/
12 * Written by Brian Dessent <brian@dessent.net>
16 #include "win32.h"
17 #include <memory>
18 #include <malloc.h>
19 #include "LogFile.h"
20 #include "resource.h"
21 #include "ini.h"
22 #include <sys/stat.h>
24 NTSecurity nt_sec;
26 #define ALL_INHERIT_ACE (CONTAINER_INHERIT_ACE \
27 | OBJECT_INHERIT_ACE \
28 | INHERIT_ONLY_ACE)
29 #define NO_INHERIT_ACE (0)
31 PSECURITY_DESCRIPTOR
32 NTSecurity::GetPosixPerms (const char *fname, PSID owner_sid, PSID group_sid,
33 mode_t mode, SECURITY_DESCRIPTOR &out_sd, acl_t &acl)
35 DWORD u_attribute, g_attribute, o_attribute;
36 DWORD offset = 0;
38 /* Initialize out SD */
39 if (!InitializeSecurityDescriptor (&out_sd, SECURITY_DESCRIPTOR_REVISION))
40 Log (LOG_TIMESTAMP) << "InitializeSecurityDescriptor(" << fname
41 << ") failed: " << GetLastError () << endLog;
42 out_sd.Control |= SE_DACL_PROTECTED;
44 /* Initialize ACL and fill with almost POSIX-like permissions.
45 Note that the current user always requires write permissions, otherwise
46 creating files in directories with restricted permissions fails. */
47 if (!InitializeAcl (&acl.acl , sizeof acl, ACL_REVISION))
48 Log (LOG_TIMESTAMP) << "InitializeAcl(" << fname << ") failed: "
49 << GetLastError () << endLog;
50 /* USER */
51 /* Default user to current user. */
52 if (!owner_sid)
53 owner_sid = ownerSID.user.User.Sid;
54 u_attribute = STANDARD_RIGHTS_ALL | FILE_GENERIC_READ | FILE_GENERIC_WRITE;
55 if (mode & 0100) // S_IXUSR
56 u_attribute |= FILE_GENERIC_EXECUTE;
57 if ((mode & 0300) == 0300) // S_IWUSR | S_IXUSR
58 u_attribute |= FILE_DELETE_CHILD;
59 if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, NO_INHERIT_ACE,
60 u_attribute, owner_sid))
61 Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname
62 << ", owner) failed: " << GetLastError () << endLog;
63 else
64 offset++;
65 /* GROUP */
66 /* Default group to current primary group. */
67 if (!group_sid)
68 group_sid = groupSID;
69 g_attribute = STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES;
70 if (mode & 0040) // S_IRGRP
71 g_attribute |= FILE_GENERIC_READ;
72 if (mode & 0020) // S_IWGRP
73 g_attribute |= FILE_GENERIC_WRITE;
74 if (mode & 0010) // S_IXGRP
75 g_attribute |= FILE_GENERIC_EXECUTE;
76 if ((mode & 01030) == 00030) // S_IWGRP | S_IXGRP, !S_ISVTX
77 g_attribute |= FILE_DELETE_CHILD;
78 if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, NO_INHERIT_ACE,
79 g_attribute, group_sid))
80 Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname
81 << ", group) failed: " << GetLastError () << endLog;
82 else
83 offset++;
84 /* OTHER */
85 o_attribute = STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES;
86 if (mode & 0004) // S_IROTH
87 o_attribute |= FILE_GENERIC_READ;
88 if (mode & 0002) // S_IWOTH
89 o_attribute |= FILE_GENERIC_WRITE;
90 if (mode & 0001) // S_IXOTH
91 o_attribute |= FILE_GENERIC_EXECUTE;
92 if ((mode & 01003) == 00003) // S_IWOTH | S_IXOTH, !S_ISVTX
93 o_attribute |= FILE_DELETE_CHILD;
94 if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, NO_INHERIT_ACE,
95 o_attribute, everyOneSID.theSID ()))
96 Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname
97 << ", everyone) failed: " << GetLastError () << endLog;
98 else
99 offset++;
100 if (mode & 07000) /* At least one of S_ISUID, S_ISGID, S_ISVTX */
102 DWORD attribute = 0;
103 if (mode & 04000) // S_ISUID
104 attribute |= FILE_APPEND_DATA;
105 if (mode & 02000) // S_ISGID
106 attribute |= FILE_WRITE_DATA;
107 if (mode & 01000) // S_ISVTX
108 attribute |= FILE_READ_DATA;
109 if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, NO_INHERIT_ACE,
110 attribute, nullSID.theSID ()))
111 Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname
112 << ", null) failed: " << GetLastError () << endLog;
113 else
114 offset++;
116 /* For directories, we also add inherit-only ACEs for CREATOR OWNER,
117 CREATOR GROUP, and EVERYONE (aka OTHER). */
118 if (mode & S_IFDIR)
120 if (mode & 01000) // S_ISVTX
122 /* Don't allow default write permissions for group and other
123 in a S_ISVTX dir. */
124 /* GROUP */
125 g_attribute = STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES;
126 if (mode & 0040) // S_IRGRP
127 g_attribute |= FILE_GENERIC_READ;
128 if (mode & 0010) // S_IXGRP
129 g_attribute |= FILE_GENERIC_EXECUTE;
130 /* OTHER */
131 o_attribute = STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES;
132 if (mode & 0004) // S_IROTH
133 o_attribute |= FILE_GENERIC_READ;
134 if (mode & 0001) // S_IXOTH
135 o_attribute |= FILE_GENERIC_EXECUTE;
137 if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, ALL_INHERIT_ACE,
138 u_attribute, cr_ownerSID.theSID ()))
139 Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname
140 << ", creator owner) failed: "
141 << GetLastError () << endLog;
142 else
143 offset++;
144 if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, ALL_INHERIT_ACE,
145 g_attribute, cr_groupSID.theSID ()))
146 Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname
147 << ", creator group) failed: "
148 << GetLastError () << endLog;
149 else
150 offset++;
151 if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, ALL_INHERIT_ACE,
152 o_attribute, everyOneSID.theSID ()))
153 Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname
154 << ", everyone inherit) failed: "
155 << GetLastError () << endLog;
156 else
157 offset++;
160 /* Set SD's DACL to just created ACL. */
161 if (!SetSecurityDescriptorDacl (&out_sd, TRUE, &acl.acl, FALSE))
162 Log (LOG_TIMESTAMP) << "SetSecurityDescriptorDacl(" << fname
163 << ") failed: " << GetLastError () << endLog;
164 return &out_sd;
167 void
168 NTSecurity::NoteFailedAPI (const std::string &api)
170 Log (LOG_TIMESTAMP) << api << "() failed: " << GetLastError () << endLog;
173 void
174 NTSecurity::initialiseWellKnownSIDs ()
176 SID_IDENTIFIER_AUTHORITY n_sid_auth = { SECURITY_NULL_SID_AUTHORITY };
177 /* Get the SID for "NULL" S-1-0-0 */
178 if (!AllocateAndInitializeSid (&n_sid_auth, 1, SECURITY_NULL_RID,
179 0, 0, 0, 0, 0, 0, 0, &nullSID.theSID ()))
180 return;
181 SID_IDENTIFIER_AUTHORITY e_sid_auth = { SECURITY_WORLD_SID_AUTHORITY };
182 /* Get the SID for "Everyone" S-1-1-0 */
183 if (!AllocateAndInitializeSid (&e_sid_auth, 1, SECURITY_WORLD_RID,
184 0, 0, 0, 0, 0, 0, 0, &everyOneSID.theSID ()))
185 return;
186 SID_IDENTIFIER_AUTHORITY nt_sid_auth = { SECURITY_NT_AUTHORITY };
187 /* Get the SID for "Administrators" S-1-5-32-544 */
188 if (!AllocateAndInitializeSid (&nt_sid_auth, 2, SECURITY_BUILTIN_DOMAIN_RID,
189 DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0,
190 &administratorsSID.theSID ()))
191 return;
192 /* Get the SID for "Users" S-1-5-32-545 */
193 if (!AllocateAndInitializeSid (&nt_sid_auth, 2, SECURITY_BUILTIN_DOMAIN_RID,
194 DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0,
195 &usersSID.theSID ()))
196 return;
197 SID_IDENTIFIER_AUTHORITY c_sid_auth = { SECURITY_CREATOR_SID_AUTHORITY };
198 /* Get the SID for "CREATOR OWNER" S-1-3-0 */
199 if (!AllocateAndInitializeSid (&c_sid_auth, 1, SECURITY_CREATOR_OWNER_RID,
200 0, 0, 0, 0, 0, 0, 0, &cr_ownerSID.theSID ()))
201 return;
202 /* Get the SID for "CREATOR GROUP" S-1-3-1 */
203 if (!AllocateAndInitializeSid (&c_sid_auth, 1, SECURITY_CREATOR_GROUP_RID,
204 0, 0, 0, 0, 0, 0, 0, &cr_groupSID.theSID ()))
205 return;
206 wellKnownSIDsinitialized (true);
209 void
210 NTSecurity::setDefaultDACL ()
212 /* To assure that the created files have a useful ACL, the
213 default DACL in the process token is set to full access to
214 everyone. This applies to files and subdirectories created
215 in directories which don't propagate permissions to child
216 objects.
217 To assure that the files group is meaningful, a token primary
218 group of None is changed to Users or Administrators.
219 This is the fallback if real POSIX permissions don't
220 work for some reason. */
222 /* Create a buffer which has enough room to contain the TOKEN_DEFAULT_DACL
223 structure plus an ACL with one ACE. */
224 size_t bufferSize = sizeof (ACL) + sizeof (ACCESS_ALLOWED_ACE)
225 + GetLengthSid (everyOneSID.theSID ()) - sizeof (DWORD);
227 std::unique_ptr<char[]> buf (new char[bufferSize]);
229 /* First initialize the TOKEN_DEFAULT_DACL structure. */
230 PACL dacl = (PACL) buf.get ();
232 /* Initialize the ACL for containing one ACE. */
233 if (!InitializeAcl (dacl, bufferSize, ACL_REVISION))
235 NoteFailedAPI ("InitializeAcl");
236 return;
239 /* Create the ACE which grants full access to "Everyone" and store it
240 in dacl. */
241 if (!AddAccessAllowedAceEx (dacl, ACL_REVISION, NO_INHERIT_ACE,
242 GENERIC_ALL, everyOneSID.theSID ()))
244 NoteFailedAPI ("AddAccessAllowedAceEx");
245 return;
248 /* Set the default DACL to the above computed ACL. */
249 if (!SetTokenInformation (token.theHANDLE(), TokenDefaultDacl, &dacl,
250 bufferSize))
251 NoteFailedAPI ("SetTokenInformation");
254 void
255 NTSecurity::setBackupPrivileges ()
257 LUID backup, restore;
258 if (!LookupPrivilegeValue (NULL, SE_BACKUP_NAME, &backup))
259 NoteFailedAPI ("LookupPrivilegeValue");
260 else if (!LookupPrivilegeValue (NULL, SE_RESTORE_NAME, &restore))
261 NoteFailedAPI ("LookupPrivilegeValue");
262 else
264 PTOKEN_PRIVILEGES new_privs;
266 new_privs = (PTOKEN_PRIVILEGES) alloca (sizeof (TOKEN_PRIVILEGES)
267 + sizeof (LUID_AND_ATTRIBUTES));
268 new_privs->PrivilegeCount = 2;
269 new_privs->Privileges[0].Luid = backup;
270 new_privs->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
271 new_privs->Privileges[1].Luid = restore;
272 new_privs->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
273 if (!AdjustTokenPrivileges (token.theHANDLE (), FALSE, new_privs,
274 0, NULL, NULL))
275 NoteFailedAPI ("AdjustTokenPrivileges");
276 else if (GetLastError () == ERROR_NOT_ALL_ASSIGNED)
277 Log (LOG_TIMESTAMP) << "User has NO backup/restore rights" << endLog;
278 else
279 Log (LOG_TIMESTAMP) << "User has backup/restore rights" << endLog;
283 void
284 NTSecurity::resetPrimaryGroup ()
286 if (primaryGroupSID.pgrp.PrimaryGroup)
288 Log (LOG_TIMESTAMP) << "Changing gid back to original" << endLog;
289 if (!SetTokenInformation (token.theHANDLE (), TokenPrimaryGroup,
290 &primaryGroupSID, sizeof primaryGroupSID))
291 NoteFailedAPI ("SetTokenInformation");
295 void
296 NTSecurity::setAdminGroup ()
298 TOKEN_PRIMARY_GROUP tpg;
300 tpg.PrimaryGroup = administratorsSID.theSID ();
301 Log (LOG_TIMESTAMP) << "Changing gid to Administrators" << endLog;
302 if (!SetTokenInformation (token.theHANDLE (), TokenPrimaryGroup,
303 &tpg, sizeof tpg))
304 NoteFailedAPI ("SetTokenInformation");
305 else
306 groupSID = administratorsSID.theSID ();
309 void
310 NTSecurity::setDefaultSecurity (bool isAdmin)
312 /* Get the processes access token. */
313 if (!OpenProcessToken (GetCurrentProcess (),
314 TOKEN_READ | TOKEN_ADJUST_DEFAULT
315 | TOKEN_ADJUST_PRIVILEGES, &token.theHANDLE ()))
317 NoteFailedAPI ("OpenProcessToken");
318 return;
321 /* Set backup and restore privileges if available. */
322 setBackupPrivileges ();
324 /* Log if symlink creation privilege is available. */
325 if (!hasSymlinkCreationRights())
326 Log (LOG_TIMESTAMP) << "User has NO symlink creation right" << endLog;
327 else
328 Log (LOG_TIMESTAMP) << "User has symlink creation right" << endLog;
330 /* If initializing the well-known SIDs didn't work, we're finished here. */
331 if (!wellKnownSIDsinitialized ())
332 return;
334 /* Set the default DACL to all permissions for everyone as a fallback. */
335 setDefaultDACL ();
337 /* Get the user */
338 if (!GetTokenInformation (token.theHANDLE (), TokenUser, &ownerSID,
339 sizeof ownerSID, &size))
341 NoteFailedAPI ("GetTokenInformation(user)");
342 return;
344 /* Make it the owner */
345 TOKEN_OWNER owner = { ownerSID.user.User.Sid };
346 if (!SetTokenInformation (token.theHANDLE (), TokenOwner, &owner,
347 sizeof owner))
349 NoteFailedAPI ("SetTokenInformation(owner)");
350 return;
352 /* Get original primary group. The token's primary group will be reset
353 to the original group right before we call the postinstall scripts.
354 This is necessary, otherwise, if the installing user is a domain user,
355 the group information created by the postinstall calls to `mkpasswd -c,
356 mkgroup -c' will be plain wrong. */
357 if (!GetTokenInformation (token.theHANDLE (), TokenPrimaryGroup,
358 &primaryGroupSID, sizeof primaryGroupSID, &size))
360 NoteFailedAPI("GetTokenInformation(pgrp)");
361 primaryGroupSID.pgrp.PrimaryGroup = (PSID) NULL;
363 groupSID = primaryGroupSID.pgrp.PrimaryGroup;
364 /* Try to set the primary group to the Administrators group, but only if
365 "Install for all users" has been chosen. If it doesn't work, we're
366 no admin and that's all there's to say about it. */
367 if (isAdmin)
368 setAdminGroup ();
371 bool
372 NTSecurity::isRunAsAdmin ()
374 BOOL is_run_as_admin = FALSE;
375 if (!CheckTokenMembership(NULL, administratorsSID.theSID (), &is_run_as_admin))
376 NoteFailedAPI("CheckTokenMembership(administratorsSID)");
377 return (is_run_as_admin == TRUE);
380 bool
381 NTSecurity::hasSymlinkCreationRights ()
383 LUID symlink;
384 if (!LookupPrivilegeValue (NULL, SE_CREATE_SYMBOLIC_LINK_NAME, &symlink))
386 NoteFailedAPI ("LookupPrivilegeValue");
387 return FALSE;
390 DWORD size;
391 GetTokenInformation (token.theHANDLE (), TokenPrivileges, NULL, 0, &size);
392 /* Will fail ERROR_INSUFFICIENT_BUFFER, but updates size */
394 TOKEN_PRIVILEGES *privileges = (TOKEN_PRIVILEGES *)alloca(size);
395 if (!GetTokenInformation (token.theHANDLE (), TokenPrivileges, privileges,
396 size, &size))
398 NoteFailedAPI ("GetTokenInformation(privileges)");
399 return FALSE;
402 unsigned int i;
403 for (i = 0; i < privileges->PrivilegeCount; i++)
405 if (memcmp(&privileges->Privileges[i].Luid, &symlink, sizeof(LUID)) == 0)
407 return TRUE;
411 return FALSE;
414 VersionInfo::VersionInfo ()
416 v.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
417 if (GetVersionEx (&v) == 0)
419 Log (LOG_PLAIN) << "GetVersionEx () failed: " << GetLastError ()
420 << endLog;
422 /* If GetVersionEx fails we really should bail with an error of some kind,
423 but for now just assume we're on NT and continue. */
424 v.dwPlatformId = VER_PLATFORM_WIN32_NT;
428 /* This is the Construct on First Use idiom to avoid static initialization
429 order problems. */
430 VersionInfo& GetVer ()
432 static VersionInfo *vi = new VersionInfo ();
433 return *vi;
436 /* Identify native machine arch if we are running under WoW */
437 USHORT
438 WowNativeMachine ()
440 typedef BOOL (WINAPI *PFNISWOW64PROCESS2)(HANDLE, USHORT *, USHORT *);
441 PFNISWOW64PROCESS2 pfnIsWow64Process2 = (PFNISWOW64PROCESS2)GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process2");
443 typedef BOOL (WINAPI *PFNISWOW64PROCESS)(HANDLE, PBOOL);
444 PFNISWOW64PROCESS pfnIsWow64Process = (PFNISWOW64PROCESS)GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process");
446 USHORT processMachine, nativeMachine;
447 if ((pfnIsWow64Process2) &&
448 (pfnIsWow64Process2(GetCurrentProcess(), &processMachine, &nativeMachine)))
449 return nativeMachine;
450 else if (pfnIsWow64Process) {
451 #ifdef _X86_
452 BOOL bIsWow64 = FALSE;
453 if (pfnIsWow64Process(GetCurrentProcess(), &bIsWow64))
454 return bIsWow64 ? IMAGE_FILE_MACHINE_AMD64 : IMAGE_FILE_MACHINE_I386;
455 #endif
458 #ifdef __x86_64__
459 return IMAGE_FILE_MACHINE_AMD64;
460 #else
461 return IMAGE_FILE_MACHINE_I386;
462 #endif
465 const std::wstring
466 LoadStringWEx(UINT uID, UINT langId)
468 HINSTANCE hInstance = GetModuleHandle(NULL);
470 // Convert the string ID into a bundle number
471 LPCSTR bundle = MAKEINTRESOURCE(uID / 16 + 1);
472 HRSRC hRes = ::FindResourceEx(hInstance, RT_STRING, bundle, langId);
473 if (hRes)
475 HGLOBAL h = ::LoadResource(hInstance, hRes);
476 if (h)
478 HGLOBAL hGlob = ::LockResource(h);
480 // walk string bundle
481 wchar_t *buf = (wchar_t *)hGlob;
482 for (unsigned int i = 0; i < (uID & 15); i++)
484 buf += 1 + (UINT)*buf;
487 int len = *buf;
488 return std::wstring(buf + 1, len);
491 // N.B.: Due to the way string bundles are encoded, there's no difference
492 // between an absent string resource whose bundle is present, and a string
493 // resource containing the null string.
494 return L"";
497 const std::wstring
498 LoadStringW(unsigned int uID)
500 wchar_t *buf;
502 int len = ::LoadStringW(GetModuleHandle(NULL), uID, (LPWSTR)&buf, 0);
503 if (len > 0)
504 return std::wstring(buf, len);
506 // if empty or absent, fallback to the untranslated string
507 return LoadStringWEx(uID, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US));
510 bool
511 is_developer_mode(void)
513 HKEY hKey;
514 LSTATUS err = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", 0, KEY_READ, &hKey);
515 if (err != ERROR_SUCCESS)
516 return false;
518 DWORD value;
519 DWORD size = sizeof(DWORD);
520 err = RegQueryValueExW(hKey, L"AllowDevelopmentWithoutDevLicense", NULL, NULL, reinterpret_cast<LPBYTE>(&value), &size);
521 RegCloseKey(hKey);
522 if (err != ERROR_SUCCESS)
523 return false;
525 return value != 0;