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
;
42 /* Initialize out SD */
43 if (!InitializeSecurityDescriptor (&out_sd
, SECURITY_DESCRIPTOR_REVISION
))
44 log (LOG_TIMESTAMP
) << "InitializeSecurityDescriptor(" << fname
45 << ") failed: " << GetLastError () << endLog
;
46 if (OSMajorVersion () >= 5)
47 out_sd
.Control
|= SE_DACL_PROTECTED
;
49 /* Initialize ACL and fill with almost POSIX-like permissions.
50 Note that the current user always requires write permissions, otherwise
51 creating files in directories with restricted permissions fails. */
52 if (!InitializeAcl (&acl
.acl
, sizeof acl
, ACL_REVISION
))
53 log (LOG_TIMESTAMP
) << "InitializeAcl(" << fname
<< ") failed: "
54 << GetLastError () << endLog
;
56 /* Default user to current user. */
58 owner_sid
= ownerSID
.user
.User
.Sid
;
59 u_attribute
= STANDARD_RIGHTS_ALL
| FILE_GENERIC_READ
| FILE_GENERIC_WRITE
;
60 if (mode
& 0100) // S_IXUSR
61 u_attribute
|= FILE_GENERIC_EXECUTE
;
62 if ((mode
& 0300) == 0300) // S_IWUSR | S_IXUSR
63 u_attribute
|= FILE_DELETE_CHILD
;
64 if (!AddAccessAllowedAce (&acl
.acl
, ACL_REVISION
, u_attribute
, owner_sid
))
65 log (LOG_TIMESTAMP
) << "AddAccessAllowedAce(" << fname
66 << ", owner) failed: " << GetLastError () << endLog
;
70 /* Default group to current primary group. */
73 g_attribute
= STANDARD_RIGHTS_READ
| FILE_READ_ATTRIBUTES
;
74 if (mode
& 0040) // S_IRGRP
75 g_attribute
|= FILE_GENERIC_READ
;
76 if (mode
& 0020) // S_IWGRP
77 g_attribute
|= FILE_GENERIC_WRITE
;
78 if (mode
& 0010) // S_IXGRP
79 g_attribute
|= FILE_GENERIC_EXECUTE
;
80 if ((mode
& 01030) == 00030) // S_IWGRP | S_IXGRP, !S_ISVTX
81 g_attribute
|= FILE_DELETE_CHILD
;
82 if (!AddAccessAllowedAce (&acl
.acl
, ACL_REVISION
, g_attribute
, group_sid
))
83 log (LOG_TIMESTAMP
) << "AddAccessAllowedAce(" << fname
84 << ", group) failed: " << GetLastError () << endLog
;
88 o_attribute
= STANDARD_RIGHTS_READ
| FILE_READ_ATTRIBUTES
;
89 if (mode
& 0004) // S_IROTH
90 o_attribute
|= FILE_GENERIC_READ
;
91 if (mode
& 0002) // S_IWOTH
92 o_attribute
|= FILE_GENERIC_WRITE
;
93 if (mode
& 0001) // S_IXOTH
94 o_attribute
|= FILE_GENERIC_EXECUTE
;
95 if ((mode
& 01003) == 00003) // S_IWOTH | S_IXOTH, !S_ISVTX
96 o_attribute
|= FILE_DELETE_CHILD
;
97 if (!AddAccessAllowedAce (&acl
.acl
, ACL_REVISION
, o_attribute
,
98 everyOneSID
.theSID ()))
99 log (LOG_TIMESTAMP
) << "AddAccessAllowedAce(" << fname
100 << ", everyone) failed: " << GetLastError () << endLog
;
103 if (mode
& 07000) /* At least one of S_ISUID, S_ISGID, S_ISVTX */
106 if (mode
& 04000) // S_ISUID
107 attribute
|= FILE_APPEND_DATA
;
108 if (mode
& 02000) // S_ISGID
109 attribute
|= FILE_WRITE_DATA
;
110 if (mode
& 01000) // S_ISVTX
111 attribute
|= FILE_READ_DATA
;
112 if (!AddAccessAllowedAce (&acl
.acl
, ACL_REVISION
, attribute
,
114 log (LOG_TIMESTAMP
) << "AddAccessAllowedAce(" << fname
115 << ", null) failed: " << GetLastError () << endLog
;
119 /* For directories, we also add inherit-only ACEs for CREATOR OWNER,
120 CREATOR GROUP, and EVERYONE (aka OTHER). */
123 if (!AddAccessAllowedAce (&acl
.acl
, ACL_REVISION
, u_attribute
,
124 cr_ownerSID
.theSID ()))
125 log (LOG_TIMESTAMP
) << "AddAccessAllowedAce(" << fname
126 << ", creator owner) failed: "
127 << GetLastError () << endLog
;
130 ACCESS_ALLOWED_ACE
*ace
;
131 if (GetAce (&acl
.acl
, offset
, (PVOID
*) &ace
))
132 ace
->Header
.AceFlags
|= CONTAINER_INHERIT_ACE
| OBJECT_INHERIT_ACE
136 if (!AddAccessAllowedAce (&acl
.acl
, ACL_REVISION
, g_attribute
,
137 cr_groupSID
.theSID ()))
138 log (LOG_TIMESTAMP
) << "AddAccessAllowedAce(" << fname
139 << ", creator group) 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
, o_attribute
,
150 everyOneSID
.theSID ()))
151 log (LOG_TIMESTAMP
) << "AddAccessAllowedAce(" << fname
152 << ", everyone inherit) 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
164 /* Set SD's DACL to just created ACL. */
165 if (!SetSecurityDescriptorDacl (&out_sd
, TRUE
, &acl
.acl
, FALSE
))
166 log (LOG_TIMESTAMP
) << "SetSecurityDescriptorDacl(" << fname
167 << ") failed: " << GetLastError () << endLog
;
172 NTSecurity::NoteFailedAPI (const std::string
&api
)
174 log (LOG_TIMESTAMP
) << api
<< "() failed: " << GetLastError () << endLog
;
178 NTSecurity::initialiseWellKnownSIDs ()
180 SID_IDENTIFIER_AUTHORITY n_sid_auth
= { SECURITY_NULL_SID_AUTHORITY
};
181 /* Get the SID for "NULL" S-1-0-0 */
182 if (!AllocateAndInitializeSid (&n_sid_auth
, 1, SECURITY_NULL_RID
,
183 0, 0, 0, 0, 0, 0, 0, &nullSID
.theSID ()))
185 NoteFailedAPI ("AllocateAndInitializeSid(null)");
188 SID_IDENTIFIER_AUTHORITY e_sid_auth
= { SECURITY_WORLD_SID_AUTHORITY
};
189 /* Get the SID for "Everyone" S-1-1-0 */
190 if (!AllocateAndInitializeSid (&e_sid_auth
, 1, SECURITY_WORLD_RID
,
191 0, 0, 0, 0, 0, 0, 0, &everyOneSID
.theSID ()))
193 NoteFailedAPI ("AllocateAndInitializeSid(everyone)");
196 SID_IDENTIFIER_AUTHORITY nt_sid_auth
= { SECURITY_NT_AUTHORITY
};
197 /* Get the SID for "Administrators" S-1-5-32-544 */
198 if (!AllocateAndInitializeSid (&nt_sid_auth
, 2, SECURITY_BUILTIN_DOMAIN_RID
,
199 DOMAIN_ALIAS_RID_ADMINS
, 0, 0, 0, 0, 0, 0,
200 &administratorsSID
.theSID ()))
202 NoteFailedAPI ("AllocateAndInitializeSid(admins)");
205 /* Get the SID for "Users" S-1-5-32-545 */
206 if (!AllocateAndInitializeSid (&nt_sid_auth
, 2, SECURITY_BUILTIN_DOMAIN_RID
,
207 DOMAIN_ALIAS_RID_USERS
, 0, 0, 0, 0, 0, 0,
208 &usersSID
.theSID ()))
210 NoteFailedAPI ("AllocateAndInitializeSid(users)");
213 SID_IDENTIFIER_AUTHORITY c_sid_auth
= { SECURITY_CREATOR_SID_AUTHORITY
};
214 /* Get the SID for "CREATOR OWNER" S-1-3-0 */
215 if (!AllocateAndInitializeSid (&c_sid_auth
, 1, SECURITY_CREATOR_OWNER_RID
,
216 0, 0, 0, 0, 0, 0, 0, &cr_ownerSID
.theSID ()))
218 NoteFailedAPI ("AllocateAndInitializeSid(users)");
221 /* Get the SID for "CREATOR GROUP" S-1-3-1 */
222 if (!AllocateAndInitializeSid (&c_sid_auth
, 1, SECURITY_CREATOR_GROUP_RID
,
223 0, 0, 0, 0, 0, 0, 0, &cr_groupSID
.theSID ()))
225 NoteFailedAPI ("AllocateAndInitializeSid(users)");
228 wellKnownSIDsinitialized (true);
232 NTSecurity::setDefaultDACL ()
234 /* To assure that the created files have a useful ACL, the
235 default DACL in the process token is set to full access to
236 everyone. This applies to files and subdirectories created
237 in directories which don't propagate permissions to child
239 To assure that the files group is meaningful, a token primary
240 group of None is changed to Users or Administrators.
241 This is the fallback if real POSIX permissions don't
242 work for some reason. */
244 /* Create a buffer which has enough room to contain the TOKEN_DEFAULT_DACL
245 structure plus an ACL with one ACE. */
246 size_t bufferSize
= sizeof (ACL
) + sizeof (ACCESS_ALLOWED_ACE
)
247 + GetLengthSid (everyOneSID
.theSID ()) - sizeof (DWORD
);
249 std::auto_ptr
<char> buf (new char[bufferSize
]);
251 /* First initialize the TOKEN_DEFAULT_DACL structure. */
252 PACL dacl
= (PACL
) buf
.get ();
254 /* Initialize the ACL for containing one ACE. */
255 if (!InitializeAcl (dacl
, bufferSize
, ACL_REVISION
))
257 NoteFailedAPI ("InitializeAcl");
261 /* Create the ACE which grants full access to "Everyone" and store it
263 if (!AddAccessAllowedAce
264 (dacl
, ACL_REVISION
, GENERIC_ALL
, everyOneSID
.theSID ()))
266 NoteFailedAPI ("AddAccessAllowedAce");
270 /* Set the default DACL to the above computed ACL. */
271 if (!SetTokenInformation (token
.theHANDLE(), TokenDefaultDacl
, &dacl
,
273 NoteFailedAPI ("SetTokenInformation");
277 NTSecurity::setBackupPrivileges ()
279 LUID backup
, restore
;
280 if (!LookupPrivilegeValue (NULL
, SE_BACKUP_NAME
, &backup
))
281 NoteFailedAPI ("LookupPrivilegeValue");
282 else if (!LookupPrivilegeValue (NULL
, SE_RESTORE_NAME
, &restore
))
283 NoteFailedAPI ("LookupPrivilegeValue");
286 PTOKEN_PRIVILEGES new_privs
;
288 new_privs
= (PTOKEN_PRIVILEGES
) alloca (sizeof (TOKEN_PRIVILEGES
)
289 + sizeof (LUID_AND_ATTRIBUTES
));
290 new_privs
->PrivilegeCount
= 2;
291 new_privs
->Privileges
[0].Luid
= backup
;
292 new_privs
->Privileges
[0].Attributes
= SE_PRIVILEGE_ENABLED
;
293 new_privs
->Privileges
[1].Luid
= restore
;
294 new_privs
->Privileges
[1].Attributes
= SE_PRIVILEGE_ENABLED
;
295 if (!AdjustTokenPrivileges (token
.theHANDLE (), FALSE
, new_privs
,
297 NoteFailedAPI ("AdjustTokenPrivileges");
298 else if (GetLastError () == ERROR_NOT_ALL_ASSIGNED
)
299 log (LOG_TIMESTAMP
) << "User has NO backup/restore rights" << endLog
;
301 log (LOG_TIMESTAMP
) << "User has backup/restore rights" << endLog
;
306 NTSecurity::resetPrimaryGroup ()
308 if (primaryGroupSID
.pgrp
.PrimaryGroup
)
310 log (LOG_TIMESTAMP
) << "Changing gid back to original" << endLog
;
311 if (!SetTokenInformation (token
.theHANDLE (), TokenPrimaryGroup
,
312 &primaryGroupSID
, sizeof primaryGroupSID
))
313 NoteFailedAPI ("SetTokenInformation");
318 NTSecurity::setAdminGroup ()
320 TOKEN_PRIMARY_GROUP tpg
;
322 tpg
.PrimaryGroup
= administratorsSID
.theSID ();
323 log (LOG_TIMESTAMP
) << "Changing gid to Administrators" << endLog
;
324 if (!SetTokenInformation (token
.theHANDLE (), TokenPrimaryGroup
,
326 NoteFailedAPI ("SetTokenInformation");
328 groupSID
= administratorsSID
.theSID ();
332 NTSecurity::setDefaultSecurity ()
334 /* First initialize the well known SIDs used later on. */
335 initialiseWellKnownSIDs ();
337 /* Get the processes access token. */
338 if (!OpenProcessToken (GetCurrentProcess (),
339 TOKEN_READ
| TOKEN_ADJUST_DEFAULT
340 | TOKEN_ADJUST_PRIVILEGES
, &token
.theHANDLE ()))
342 NoteFailedAPI ("OpenProcessToken");
346 /* Set backup and restore privileges if available. */
347 setBackupPrivileges ();
349 /* If initializing the well-known SIDs didn't work, we're finished here. */
350 if (!wellKnownSIDsinitialized ())
353 /* Set the default DACL to all permissions for everyone as a fallback. */
357 if (!GetTokenInformation (token
.theHANDLE (), TokenUser
, &ownerSID
,
358 sizeof ownerSID
, &size
))
360 NoteFailedAPI ("GetTokenInformation(user)");
363 /* Make it the owner */
364 TOKEN_OWNER owner
= { ownerSID
.user
.User
.Sid
};
365 if (!SetTokenInformation (token
.theHANDLE (), TokenOwner
, &owner
,
368 NoteFailedAPI ("SetTokenInformation(owner)");
371 /* Get original primary group. The token's primary group will be reset
372 to the original group right before we call the postinstall scripts.
373 This is necessary, otherwise, if the installing user is a domain user,
374 the group information created by the postinstall calls to `mkpasswd -c,
375 mkgroup -c' will be plain wrong. */
376 if (!GetTokenInformation (token
.theHANDLE (), TokenPrimaryGroup
,
377 &primaryGroupSID
, sizeof primaryGroupSID
, &size
))
379 NoteFailedAPI("GetTokenInformation(pgrp)");
380 primaryGroupSID
.pgrp
.PrimaryGroup
= (PSID
) NULL
;
382 groupSID
= primaryGroupSID
.pgrp
.PrimaryGroup
;
383 /* Try to set the primary group to the Administrators group, but only if
384 "Install for all users" has been chosen. If it doesn't work, we're
385 no admin and that's all there's to say about it. */
386 if (root_scope
== IDC_ROOT_SYSTEM
)
390 VersionInfo::VersionInfo ()
392 v
.dwOSVersionInfoSize
= sizeof (OSVERSIONINFO
);
393 if (GetVersionEx (&v
) == 0)
395 log (LOG_PLAIN
) << "GetVersionEx () failed: " << GetLastError ()
398 /* If GetVersionEx fails we really should bail with an error of some kind,
399 but for now just assume we're on NT and continue. */
400 v
.dwPlatformId
= VER_PLATFORM_WIN32_NT
;
404 /* This is the Construct on First Use idiom to avoid static initialization
406 VersionInfo
& GetVer ()
408 static VersionInfo
*vi
= new VersionInfo ();