Allow to run on Windows lacking CreateSymbolicLinkW()
[cygwin-setup.git] / win32.cc
blobea3d53af8337977b1272fac867b17118eb2dafd4
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>
23 #include "String++.h"
25 NTSecurity nt_sec;
27 #define ALL_INHERIT_ACE (CONTAINER_INHERIT_ACE \
28 | OBJECT_INHERIT_ACE \
29 | INHERIT_ONLY_ACE)
30 #define NO_INHERIT_ACE (0)
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)
36 DWORD u_attribute, g_attribute, o_attribute;
37 DWORD offset = 0;
39 /* Initialize out SD */
40 if (!InitializeSecurityDescriptor (&out_sd, SECURITY_DESCRIPTOR_REVISION))
41 Log (LOG_TIMESTAMP) << "InitializeSecurityDescriptor(" << fname
42 << ") failed: " << GetLastError () << endLog;
43 out_sd.Control |= SE_DACL_PROTECTED;
45 /* Initialize ACL and fill with almost POSIX-like permissions.
46 Note that the current user always requires write permissions, otherwise
47 creating files in directories with restricted permissions fails. */
48 if (!InitializeAcl (&acl.acl , sizeof acl, ACL_REVISION))
49 Log (LOG_TIMESTAMP) << "InitializeAcl(" << fname << ") failed: "
50 << GetLastError () << endLog;
51 /* USER */
52 /* Default user to current user. */
53 if (!owner_sid)
54 owner_sid = ownerSID.user.User.Sid;
55 u_attribute = STANDARD_RIGHTS_ALL | FILE_GENERIC_READ | FILE_GENERIC_WRITE;
56 if (mode & 0100) // S_IXUSR
57 u_attribute |= FILE_GENERIC_EXECUTE;
58 if ((mode & 0300) == 0300) // S_IWUSR | S_IXUSR
59 u_attribute |= FILE_DELETE_CHILD;
60 if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, NO_INHERIT_ACE,
61 u_attribute, owner_sid))
62 Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname
63 << ", owner) failed: " << GetLastError () << endLog;
64 else
65 offset++;
66 /* GROUP */
67 /* Default group to current primary group. */
68 if (!group_sid)
69 group_sid = groupSID;
70 g_attribute = STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES;
71 if (mode & 0040) // S_IRGRP
72 g_attribute |= FILE_GENERIC_READ;
73 if (mode & 0020) // S_IWGRP
74 g_attribute |= FILE_GENERIC_WRITE;
75 if (mode & 0010) // S_IXGRP
76 g_attribute |= FILE_GENERIC_EXECUTE;
77 if ((mode & 01030) == 00030) // S_IWGRP | S_IXGRP, !S_ISVTX
78 g_attribute |= FILE_DELETE_CHILD;
79 if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, NO_INHERIT_ACE,
80 g_attribute, group_sid))
81 Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname
82 << ", group) failed: " << GetLastError () << endLog;
83 else
84 offset++;
85 /* OTHER */
86 o_attribute = STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES;
87 if (mode & 0004) // S_IROTH
88 o_attribute |= FILE_GENERIC_READ;
89 if (mode & 0002) // S_IWOTH
90 o_attribute |= FILE_GENERIC_WRITE;
91 if (mode & 0001) // S_IXOTH
92 o_attribute |= FILE_GENERIC_EXECUTE;
93 if ((mode & 01003) == 00003) // S_IWOTH | S_IXOTH, !S_ISVTX
94 o_attribute |= FILE_DELETE_CHILD;
95 if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, NO_INHERIT_ACE,
96 o_attribute, everyOneSID.theSID ()))
97 Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname
98 << ", everyone) failed: " << GetLastError () << endLog;
99 else
100 offset++;
101 if (mode & 07000) /* At least one of S_ISUID, S_ISGID, S_ISVTX */
103 DWORD attribute = 0;
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;
110 if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, NO_INHERIT_ACE,
111 attribute, nullSID.theSID ()))
112 Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname
113 << ", null) failed: " << GetLastError () << endLog;
114 else
115 offset++;
117 /* For directories, we also add inherit-only ACEs for CREATOR OWNER,
118 CREATOR GROUP, and EVERYONE (aka OTHER). */
119 if (mode & S_IFDIR)
121 if (mode & 01000) // S_ISVTX
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;
138 if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, ALL_INHERIT_ACE,
139 u_attribute, cr_ownerSID.theSID ()))
140 Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname
141 << ", creator owner) failed: "
142 << GetLastError () << endLog;
143 else
144 offset++;
145 if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, ALL_INHERIT_ACE,
146 g_attribute, cr_groupSID.theSID ()))
147 Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname
148 << ", creator group) failed: "
149 << GetLastError () << endLog;
150 else
151 offset++;
152 if (!AddAccessAllowedAceEx (&acl.acl, ACL_REVISION, ALL_INHERIT_ACE,
153 o_attribute, everyOneSID.theSID ()))
154 Log (LOG_TIMESTAMP) << "AddAccessAllowedAceEx(" << fname
155 << ", everyone inherit) failed: "
156 << GetLastError () << endLog;
157 else
158 offset++;
161 /* Set SD's DACL to just created ACL. */
162 if (!SetSecurityDescriptorDacl (&out_sd, TRUE, &acl.acl, FALSE))
163 Log (LOG_TIMESTAMP) << "SetSecurityDescriptorDacl(" << fname
164 << ") failed: " << GetLastError () << endLog;
165 return &out_sd;
168 void
169 NTSecurity::NoteFailedAPI (const std::string &api)
171 Log (LOG_TIMESTAMP) << api << "() failed: " << GetLastError () << endLog;
174 void
175 NTSecurity::initialiseWellKnownSIDs ()
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 ()))
181 return;
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 ()))
186 return;
187 SID_IDENTIFIER_AUTHORITY nt_sid_auth = { SECURITY_NT_AUTHORITY };
188 /* Get the SID for "Administrators" S-1-5-32-544 */
189 if (!AllocateAndInitializeSid (&nt_sid_auth, 2, SECURITY_BUILTIN_DOMAIN_RID,
190 DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0,
191 &administratorsSID.theSID ()))
192 return;
193 /* Get the SID for "Users" S-1-5-32-545 */
194 if (!AllocateAndInitializeSid (&nt_sid_auth, 2, SECURITY_BUILTIN_DOMAIN_RID,
195 DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0,
196 &usersSID.theSID ()))
197 return;
198 SID_IDENTIFIER_AUTHORITY c_sid_auth = { SECURITY_CREATOR_SID_AUTHORITY };
199 /* Get the SID for "CREATOR OWNER" S-1-3-0 */
200 if (!AllocateAndInitializeSid (&c_sid_auth, 1, SECURITY_CREATOR_OWNER_RID,
201 0, 0, 0, 0, 0, 0, 0, &cr_ownerSID.theSID ()))
202 return;
203 /* Get the SID for "CREATOR GROUP" S-1-3-1 */
204 if (!AllocateAndInitializeSid (&c_sid_auth, 1, SECURITY_CREATOR_GROUP_RID,
205 0, 0, 0, 0, 0, 0, 0, &cr_groupSID.theSID ()))
206 return;
207 wellKnownSIDsinitialized (true);
210 void
211 NTSecurity::setDefaultDACL ()
213 /* To assure that the created files have a useful ACL, the
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
217 objects.
218 To assure that the files group is meaningful, a token primary
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. */
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);
228 std::unique_ptr<char[]> buf (new char[bufferSize]);
230 /* First initialize the TOKEN_DEFAULT_DACL structure. */
231 PACL dacl = (PACL) buf.get ();
233 /* Initialize the ACL for containing one ACE. */
234 if (!InitializeAcl (dacl, bufferSize, ACL_REVISION))
236 NoteFailedAPI ("InitializeAcl");
237 return;
240 /* Create the ACE which grants full access to "Everyone" and store it
241 in dacl. */
242 if (!AddAccessAllowedAceEx (dacl, ACL_REVISION, NO_INHERIT_ACE,
243 GENERIC_ALL, everyOneSID.theSID ()))
245 NoteFailedAPI ("AddAccessAllowedAceEx");
246 return;
249 /* Set the default DACL to the above computed ACL. */
250 if (!SetTokenInformation (token.theHANDLE(), TokenDefaultDacl, &dacl,
251 bufferSize))
252 NoteFailedAPI ("SetTokenInformation");
255 void
256 NTSecurity::setBackupPrivileges ()
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
265 PTOKEN_PRIVILEGES new_privs;
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)
278 Log (LOG_TIMESTAMP) << "User has NO backup/restore rights" << endLog;
279 else
280 Log (LOG_TIMESTAMP) << "User has backup/restore rights" << endLog;
284 void
285 NTSecurity::resetPrimaryGroup ()
287 if (primaryGroupSID.pgrp.PrimaryGroup)
289 Log (LOG_TIMESTAMP) << "Changing gid back to original" << endLog;
290 if (!SetTokenInformation (token.theHANDLE (), TokenPrimaryGroup,
291 &primaryGroupSID, sizeof primaryGroupSID))
292 NoteFailedAPI ("SetTokenInformation");
296 void
297 NTSecurity::setAdminGroup ()
299 TOKEN_PRIMARY_GROUP tpg;
301 tpg.PrimaryGroup = administratorsSID.theSID ();
302 Log (LOG_TIMESTAMP) << "Changing gid to Administrators" << endLog;
303 if (!SetTokenInformation (token.theHANDLE (), TokenPrimaryGroup,
304 &tpg, sizeof tpg))
305 NoteFailedAPI ("SetTokenInformation");
306 else
307 groupSID = administratorsSID.theSID ();
310 void
311 NTSecurity::setDefaultSecurity ()
313 /* Get the processes access token. */
314 if (!OpenProcessToken (GetCurrentProcess (),
315 TOKEN_READ | TOKEN_ADJUST_DEFAULT
316 | TOKEN_ADJUST_PRIVILEGES, &token.theHANDLE ()))
318 NoteFailedAPI ("OpenProcessToken");
319 return;
322 /* Set backup and restore privileges if available. */
323 setBackupPrivileges ();
325 /* Log if symlink creation privilege is available. */
326 if (!hasSymlinkCreationRights())
327 Log (LOG_TIMESTAMP) << "User has NO symlink creation right" << endLog;
328 else
329 Log (LOG_TIMESTAMP) << "User has symlink creation right" << endLog;
331 /* If initializing the well-known SIDs didn't work, we're finished here. */
332 if (!wellKnownSIDsinitialized ())
333 return;
335 /* Set the default DACL to all permissions for everyone as a fallback. */
336 setDefaultDACL ();
338 /* Get the user */
339 if (!GetTokenInformation (token.theHANDLE (), TokenUser, &ownerSID,
340 sizeof ownerSID, &size))
342 NoteFailedAPI ("GetTokenInformation(user)");
343 return;
345 /* Make it the owner */
346 TOKEN_OWNER owner = { ownerSID.user.User.Sid };
347 if (!SetTokenInformation (token.theHANDLE (), TokenOwner, &owner,
348 sizeof owner))
350 NoteFailedAPI ("SetTokenInformation(owner)");
351 return;
353 /* Get original primary group */
354 if (!GetTokenInformation (token.theHANDLE (), TokenPrimaryGroup,
355 &primaryGroupSID, sizeof primaryGroupSID, &size))
357 NoteFailedAPI("GetTokenInformation(pgrp)");
358 primaryGroupSID.pgrp.PrimaryGroup = (PSID) NULL;
360 groupSID = primaryGroupSID.pgrp.PrimaryGroup;
363 bool
364 NTSecurity::isRunAsAdmin ()
366 BOOL is_run_as_admin = FALSE;
367 if (!CheckTokenMembership(NULL, administratorsSID.theSID (), &is_run_as_admin))
368 NoteFailedAPI("CheckTokenMembership(administratorsSID)");
369 return (is_run_as_admin == TRUE);
372 bool
373 NTSecurity::hasSymlinkCreationRights ()
375 LUID symlink;
376 if (!LookupPrivilegeValue (NULL, SE_CREATE_SYMBOLIC_LINK_NAME, &symlink))
378 NoteFailedAPI ("LookupPrivilegeValue");
379 return FALSE;
382 DWORD size;
383 GetTokenInformation (token.theHANDLE (), TokenPrivileges, NULL, 0, &size);
384 /* Will fail ERROR_INSUFFICIENT_BUFFER, but updates size */
386 TOKEN_PRIVILEGES *privileges = (TOKEN_PRIVILEGES *)alloca(size);
387 if (!GetTokenInformation (token.theHANDLE (), TokenPrivileges, privileges,
388 size, &size))
390 NoteFailedAPI ("GetTokenInformation(privileges)");
391 return FALSE;
394 unsigned int i;
395 for (i = 0; i < privileges->PrivilegeCount; i++)
397 if (memcmp(&privileges->Privileges[i].Luid, &symlink, sizeof(LUID)) == 0)
399 return TRUE;
403 return FALSE;
406 VersionInfo::VersionInfo ()
408 v.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
409 if (GetVersionEx (&v) == 0)
411 Log (LOG_PLAIN) << "GetVersionEx () failed: " << GetLastError ()
412 << endLog;
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;
420 /* This is the Construct on First Use idiom to avoid static initialization
421 order problems. */
422 VersionInfo& GetVer ()
424 static VersionInfo *vi = new VersionInfo ();
425 return *vi;
428 /* Identify native machine arch if we are running under WoW */
429 USHORT
430 WowNativeMachine ()
432 typedef BOOL (WINAPI *PFNISWOW64PROCESS2)(HANDLE, USHORT *, USHORT *);
433 PFNISWOW64PROCESS2 pfnIsWow64Process2 = (PFNISWOW64PROCESS2)GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process2");
435 typedef BOOL (WINAPI *PFNISWOW64PROCESS)(HANDLE, PBOOL);
436 PFNISWOW64PROCESS pfnIsWow64Process = (PFNISWOW64PROCESS)GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process");
438 USHORT processMachine, nativeMachine;
439 if ((pfnIsWow64Process2) &&
440 (pfnIsWow64Process2(GetCurrentProcess(), &processMachine, &nativeMachine)))
441 return nativeMachine;
442 else if (pfnIsWow64Process) {
443 #ifdef _X86_
444 BOOL bIsWow64 = FALSE;
445 if (pfnIsWow64Process(GetCurrentProcess(), &bIsWow64))
446 return bIsWow64 ? IMAGE_FILE_MACHINE_AMD64 : IMAGE_FILE_MACHINE_I386;
447 #endif
450 #ifdef __x86_64__
451 return IMAGE_FILE_MACHINE_AMD64;
452 #else
453 return IMAGE_FILE_MACHINE_I386;
454 #endif
457 const std::wstring
458 LoadStringWEx(UINT uID, UINT langId)
460 HINSTANCE hInstance = GetModuleHandle(NULL);
462 // Convert the string ID into a bundle number
463 LPCSTR bundle = MAKEINTRESOURCE(uID / 16 + 1);
464 HRSRC hRes = ::FindResourceEx(hInstance, RT_STRING, bundle, langId);
465 if (hRes)
467 HGLOBAL h = ::LoadResource(hInstance, hRes);
468 if (h)
470 HGLOBAL hGlob = ::LockResource(h);
472 // walk string bundle
473 wchar_t *buf = (wchar_t *)hGlob;
474 for (unsigned int i = 0; i < (uID & 15); i++)
476 buf += 1 + (UINT)*buf;
479 int len = *buf;
480 return std::wstring(buf + 1, len);
483 // N.B.: Due to the way string bundles are encoded, there's no difference
484 // between an absent string resource whose bundle is present, and a string
485 // resource containing the null string.
486 return L"";
489 const std::wstring
490 LoadStringW(unsigned int uID)
492 wchar_t *buf;
494 int len = ::LoadStringW(GetModuleHandle(NULL), uID, (LPWSTR)&buf, 0);
495 if (len > 0)
496 return std::wstring(buf, len);
498 // if empty or absent, fallback to the untranslated string
499 return LoadStringWEx(uID, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US));
502 const std::string
503 LoadStringUtf8(unsigned int uID)
505 return wstring_to_string(LoadStringW(uID));
508 bool
509 is_developer_mode(void)
511 HKEY hKey;
512 LSTATUS err = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", 0, KEY_READ, &hKey);
513 if (err != ERROR_SUCCESS)
514 return false;
516 DWORD value;
517 DWORD size = sizeof(DWORD);
518 err = RegQueryValueExW(hKey, L"AllowDevelopmentWithoutDevLicense", NULL, NULL, reinterpret_cast<LPBYTE>(&value), &size);
519 RegCloseKey(hKey);
520 if (err != ERROR_SUCCESS)
521 return false;
523 return value != 0;