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
12 * Written by Brian Dessent <brian@dessent.net>
17 static const char *cvsid
=
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
;
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
;
52 /* Default user to current user. */
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 (!AddAccessAllowedAce (&acl
.acl
, ACL_REVISION
, u_attribute
, owner_sid
))
61 log (LOG_TIMESTAMP
) << "AddAccessAllowedAce(" << fname
62 << ", owner) failed: " << GetLastError () << endLog
;
66 /* Default group to current primary group. */
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 (!AddAccessAllowedAce (&acl
.acl
, ACL_REVISION
, g_attribute
, group_sid
))
79 log (LOG_TIMESTAMP
) << "AddAccessAllowedAce(" << fname
80 << ", group) failed: " << GetLastError () << endLog
;
84 o_attribute
= STANDARD_RIGHTS_READ
| FILE_READ_ATTRIBUTES
;
85 if (mode
& 0004) // S_IROTH
86 o_attribute
|= FILE_GENERIC_READ
;
87 if (mode
& 0002) // S_IWOTH
88 o_attribute
|= FILE_GENERIC_WRITE
;
89 if (mode
& 0001) // S_IXOTH
90 o_attribute
|= FILE_GENERIC_EXECUTE
;
91 if ((mode
& 01003) == 00003) // S_IWOTH | S_IXOTH, !S_ISVTX
92 o_attribute
|= FILE_DELETE_CHILD
;
93 if (!AddAccessAllowedAce (&acl
.acl
, ACL_REVISION
, o_attribute
,
94 everyOneSID
.theSID ()))
95 log (LOG_TIMESTAMP
) << "AddAccessAllowedAce(" << fname
96 << ", everyone) failed: " << GetLastError () << endLog
;
99 if (mode
& 07000) /* At least one of S_ISUID, S_ISGID, S_ISVTX */
102 if (mode
& 04000) // S_ISUID
103 attribute
|= FILE_APPEND_DATA
;
104 if (mode
& 02000) // S_ISGID
105 attribute
|= FILE_WRITE_DATA
;
106 if (mode
& 01000) // S_ISVTX
107 attribute
|= FILE_READ_DATA
;
108 if (!AddAccessAllowedAce (&acl
.acl
, ACL_REVISION
, attribute
,
110 log (LOG_TIMESTAMP
) << "AddAccessAllowedAce(" << fname
111 << ", null) failed: " << GetLastError () << endLog
;
115 /* For directories, we also add inherit-only ACEs for CREATOR OWNER,
116 CREATOR GROUP, and EVERYONE (aka OTHER). */
119 if (mode
& 01000) // S_ISVTX
121 /* Don't allow default write permissions for group and other
124 g_attribute
= STANDARD_RIGHTS_READ
| FILE_READ_ATTRIBUTES
;
125 if (mode
& 0040) // S_IRGRP
126 g_attribute
|= FILE_GENERIC_READ
;
127 if (mode
& 0010) // S_IXGRP
128 g_attribute
|= FILE_GENERIC_EXECUTE
;
130 o_attribute
= STANDARD_RIGHTS_READ
| FILE_READ_ATTRIBUTES
;
131 if (mode
& 0004) // S_IROTH
132 o_attribute
|= FILE_GENERIC_READ
;
133 if (mode
& 0001) // S_IXOTH
134 o_attribute
|= FILE_GENERIC_EXECUTE
;
136 if (!AddAccessAllowedAce (&acl
.acl
, ACL_REVISION
, u_attribute
,
137 cr_ownerSID
.theSID ()))
138 log (LOG_TIMESTAMP
) << "AddAccessAllowedAce(" << fname
139 << ", creator owner) failed: "
140 << GetLastError () << endLog
;
143 ACCESS_ALLOWED_ACE
*ace
;
144 if (GetAce (&acl
.acl
, offset
, (PVOID
*) &ace
))
145 ace
->Header
.AceFlags
|= CONTAINER_INHERIT_ACE
| OBJECT_INHERIT_ACE
149 if (!AddAccessAllowedAce (&acl
.acl
, ACL_REVISION
, g_attribute
,
150 cr_groupSID
.theSID ()))
151 log (LOG_TIMESTAMP
) << "AddAccessAllowedAce(" << fname
152 << ", creator group) failed: "
153 << GetLastError () << endLog
;
156 ACCESS_ALLOWED_ACE
*ace
;
157 if (GetAce (&acl
.acl
, offset
, (PVOID
*) &ace
))
158 ace
->Header
.AceFlags
|= CONTAINER_INHERIT_ACE
| OBJECT_INHERIT_ACE
162 if (!AddAccessAllowedAce (&acl
.acl
, ACL_REVISION
, o_attribute
,
163 everyOneSID
.theSID ()))
164 log (LOG_TIMESTAMP
) << "AddAccessAllowedAce(" << fname
165 << ", everyone inherit) failed: "
166 << GetLastError () << endLog
;
169 ACCESS_ALLOWED_ACE
*ace
;
170 if (GetAce (&acl
.acl
, offset
, (PVOID
*) &ace
))
171 ace
->Header
.AceFlags
|= CONTAINER_INHERIT_ACE
| OBJECT_INHERIT_ACE
177 /* Set SD's DACL to just created ACL. */
178 if (!SetSecurityDescriptorDacl (&out_sd
, TRUE
, &acl
.acl
, FALSE
))
179 log (LOG_TIMESTAMP
) << "SetSecurityDescriptorDacl(" << fname
180 << ") failed: " << GetLastError () << endLog
;
185 NTSecurity::NoteFailedAPI (const std::string
&api
)
187 log (LOG_TIMESTAMP
) << api
<< "() failed: " << GetLastError () << endLog
;
191 NTSecurity::initialiseWellKnownSIDs ()
193 SID_IDENTIFIER_AUTHORITY n_sid_auth
= { SECURITY_NULL_SID_AUTHORITY
};
194 /* Get the SID for "NULL" S-1-0-0 */
195 if (!AllocateAndInitializeSid (&n_sid_auth
, 1, SECURITY_NULL_RID
,
196 0, 0, 0, 0, 0, 0, 0, &nullSID
.theSID ()))
198 SID_IDENTIFIER_AUTHORITY e_sid_auth
= { SECURITY_WORLD_SID_AUTHORITY
};
199 /* Get the SID for "Everyone" S-1-1-0 */
200 if (!AllocateAndInitializeSid (&e_sid_auth
, 1, SECURITY_WORLD_RID
,
201 0, 0, 0, 0, 0, 0, 0, &everyOneSID
.theSID ()))
203 SID_IDENTIFIER_AUTHORITY nt_sid_auth
= { SECURITY_NT_AUTHORITY
};
204 /* Get the SID for "Administrators" S-1-5-32-544 */
205 if (!AllocateAndInitializeSid (&nt_sid_auth
, 2, SECURITY_BUILTIN_DOMAIN_RID
,
206 DOMAIN_ALIAS_RID_ADMINS
, 0, 0, 0, 0, 0, 0,
207 &administratorsSID
.theSID ()))
209 /* Get the SID for "Users" S-1-5-32-545 */
210 if (!AllocateAndInitializeSid (&nt_sid_auth
, 2, SECURITY_BUILTIN_DOMAIN_RID
,
211 DOMAIN_ALIAS_RID_USERS
, 0, 0, 0, 0, 0, 0,
212 &usersSID
.theSID ()))
214 SID_IDENTIFIER_AUTHORITY c_sid_auth
= { SECURITY_CREATOR_SID_AUTHORITY
};
215 /* Get the SID for "CREATOR OWNER" S-1-3-0 */
216 if (!AllocateAndInitializeSid (&c_sid_auth
, 1, SECURITY_CREATOR_OWNER_RID
,
217 0, 0, 0, 0, 0, 0, 0, &cr_ownerSID
.theSID ()))
219 /* Get the SID for "CREATOR GROUP" S-1-3-1 */
220 if (!AllocateAndInitializeSid (&c_sid_auth
, 1, SECURITY_CREATOR_GROUP_RID
,
221 0, 0, 0, 0, 0, 0, 0, &cr_groupSID
.theSID ()))
223 wellKnownSIDsinitialized (true);
227 NTSecurity::setDefaultDACL ()
229 /* To assure that the created files have a useful ACL, the
230 default DACL in the process token is set to full access to
231 everyone. This applies to files and subdirectories created
232 in directories which don't propagate permissions to child
234 To assure that the files group is meaningful, a token primary
235 group of None is changed to Users or Administrators.
236 This is the fallback if real POSIX permissions don't
237 work for some reason. */
239 /* Create a buffer which has enough room to contain the TOKEN_DEFAULT_DACL
240 structure plus an ACL with one ACE. */
241 size_t bufferSize
= sizeof (ACL
) + sizeof (ACCESS_ALLOWED_ACE
)
242 + GetLengthSid (everyOneSID
.theSID ()) - sizeof (DWORD
);
244 std::auto_ptr
<char> buf (new char[bufferSize
]);
246 /* First initialize the TOKEN_DEFAULT_DACL structure. */
247 PACL dacl
= (PACL
) buf
.get ();
249 /* Initialize the ACL for containing one ACE. */
250 if (!InitializeAcl (dacl
, bufferSize
, ACL_REVISION
))
252 NoteFailedAPI ("InitializeAcl");
256 /* Create the ACE which grants full access to "Everyone" and store it
258 if (!AddAccessAllowedAce
259 (dacl
, ACL_REVISION
, GENERIC_ALL
, everyOneSID
.theSID ()))
261 NoteFailedAPI ("AddAccessAllowedAce");
265 /* Set the default DACL to the above computed ACL. */
266 if (!SetTokenInformation (token
.theHANDLE(), TokenDefaultDacl
, &dacl
,
268 NoteFailedAPI ("SetTokenInformation");
272 NTSecurity::setBackupPrivileges ()
274 LUID backup
, restore
;
275 if (!LookupPrivilegeValue (NULL
, SE_BACKUP_NAME
, &backup
))
276 NoteFailedAPI ("LookupPrivilegeValue");
277 else if (!LookupPrivilegeValue (NULL
, SE_RESTORE_NAME
, &restore
))
278 NoteFailedAPI ("LookupPrivilegeValue");
281 PTOKEN_PRIVILEGES new_privs
;
283 new_privs
= (PTOKEN_PRIVILEGES
) alloca (sizeof (TOKEN_PRIVILEGES
)
284 + sizeof (LUID_AND_ATTRIBUTES
));
285 new_privs
->PrivilegeCount
= 2;
286 new_privs
->Privileges
[0].Luid
= backup
;
287 new_privs
->Privileges
[0].Attributes
= SE_PRIVILEGE_ENABLED
;
288 new_privs
->Privileges
[1].Luid
= restore
;
289 new_privs
->Privileges
[1].Attributes
= SE_PRIVILEGE_ENABLED
;
290 if (!AdjustTokenPrivileges (token
.theHANDLE (), FALSE
, new_privs
,
292 NoteFailedAPI ("AdjustTokenPrivileges");
293 else if (GetLastError () == ERROR_NOT_ALL_ASSIGNED
)
294 log (LOG_TIMESTAMP
) << "User has NO backup/restore rights" << endLog
;
296 log (LOG_TIMESTAMP
) << "User has backup/restore rights" << endLog
;
301 NTSecurity::resetPrimaryGroup ()
303 if (primaryGroupSID
.pgrp
.PrimaryGroup
)
305 log (LOG_TIMESTAMP
) << "Changing gid back to original" << endLog
;
306 if (!SetTokenInformation (token
.theHANDLE (), TokenPrimaryGroup
,
307 &primaryGroupSID
, sizeof primaryGroupSID
))
308 NoteFailedAPI ("SetTokenInformation");
313 NTSecurity::setAdminGroup ()
315 TOKEN_PRIMARY_GROUP tpg
;
317 tpg
.PrimaryGroup
= administratorsSID
.theSID ();
318 log (LOG_TIMESTAMP
) << "Changing gid to Administrators" << endLog
;
319 if (!SetTokenInformation (token
.theHANDLE (), TokenPrimaryGroup
,
321 NoteFailedAPI ("SetTokenInformation");
323 groupSID
= administratorsSID
.theSID ();
327 NTSecurity::setDefaultSecurity ()
329 /* Get the processes access token. */
330 if (!OpenProcessToken (GetCurrentProcess (),
331 TOKEN_READ
| TOKEN_ADJUST_DEFAULT
332 | TOKEN_ADJUST_PRIVILEGES
, &token
.theHANDLE ()))
334 NoteFailedAPI ("OpenProcessToken");
338 /* Set backup and restore privileges if available. */
339 setBackupPrivileges ();
341 /* If initializing the well-known SIDs didn't work, we're finished here. */
342 if (!wellKnownSIDsinitialized ())
345 /* Set the default DACL to all permissions for everyone as a fallback. */
349 if (!GetTokenInformation (token
.theHANDLE (), TokenUser
, &ownerSID
,
350 sizeof ownerSID
, &size
))
352 NoteFailedAPI ("GetTokenInformation(user)");
355 /* Make it the owner */
356 TOKEN_OWNER owner
= { ownerSID
.user
.User
.Sid
};
357 if (!SetTokenInformation (token
.theHANDLE (), TokenOwner
, &owner
,
360 NoteFailedAPI ("SetTokenInformation(owner)");
363 /* Get original primary group. The token's primary group will be reset
364 to the original group right before we call the postinstall scripts.
365 This is necessary, otherwise, if the installing user is a domain user,
366 the group information created by the postinstall calls to `mkpasswd -c,
367 mkgroup -c' will be plain wrong. */
368 if (!GetTokenInformation (token
.theHANDLE (), TokenPrimaryGroup
,
369 &primaryGroupSID
, sizeof primaryGroupSID
, &size
))
371 NoteFailedAPI("GetTokenInformation(pgrp)");
372 primaryGroupSID
.pgrp
.PrimaryGroup
= (PSID
) NULL
;
374 groupSID
= primaryGroupSID
.pgrp
.PrimaryGroup
;
375 /* Try to set the primary group to the Administrators group, but only if
376 "Install for all users" has been chosen. If it doesn't work, we're
377 no admin and that's all there's to say about it. */
378 if (root_scope
== IDC_ROOT_SYSTEM
)
383 NTSecurity::isRunAsAdmin ()
385 BOOL is_run_as_admin
= FALSE
;
386 if (!CheckTokenMembership(NULL
, administratorsSID
.theSID (), &is_run_as_admin
))
387 NoteFailedAPI("CheckTokenMembership(administratorsSID)");
388 return (is_run_as_admin
== TRUE
);
392 VersionInfo::VersionInfo ()
394 v
.dwOSVersionInfoSize
= sizeof (OSVERSIONINFO
);
395 if (GetVersionEx (&v
) == 0)
397 log (LOG_PLAIN
) << "GetVersionEx () failed: " << GetLastError ()
400 /* If GetVersionEx fails we really should bail with an error of some kind,
401 but for now just assume we're on NT and continue. */
402 v
.dwPlatformId
= VER_PLATFORM_WIN32_NT
;
406 /* This is the Construct on First Use idiom to avoid static initialization
408 VersionInfo
& GetVer ()
410 static VersionInfo
*vi
= new VersionInfo ();