main
cpp 1,044 lines 31.7 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 #include "precomp.h"
4
5 // Exit macros
6 #define AclExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__)
7 #define AclExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__)
8 #define AclExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__)
9 #define AclExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__)
10 #define AclExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__)
11 #define AclExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__)
12 #define AclExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__)
13 #define AclExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__)
14 #define AclExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__)
15 #define AclExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__)
16 #define AclExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ACLUTIL, e, x, s, __VA_ARGS__)
17 #define AclExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ACLUTIL, g, x, s, __VA_ARGS__)
18
19 /********************************************************************
20 AclCheckAccess - determines if token has appropriate privileges
21
22 NOTE: paa->fDenyAccess and paa->dwAccessMask are ignored and must be zero
23 if hToken is NULL, the thread will be checked
24 if hToken is not NULL the token must be an impersonation token
25 ********************************************************************/
26 extern "C" HRESULT DAPI AclCheckAccess(
27 __in HANDLE hToken,
28 __in ACL_ACCESS* paa
29 )
30 {
31 HRESULT hr = S_OK;
32 PSID psid = NULL;
33 BOOL fIsMember = FALSE;
34
35 AclExitOnNull(paa, hr, E_INVALIDARG, "Failed to check ACL access, because no acl access provided to check");
36 Assert(0 == paa->fDenyAccess && 0 == paa->dwAccessMask);
37
38 if (paa->pwzAccountName)
39 {
40 hr = AclGetAccountSid(NULL, paa->pwzAccountName, &psid);
41 AclExitOnFailure(hr, "failed to get SID for account: %ls", paa->pwzAccountName);
42 }
43 else
44 {
45 if (!::AllocateAndInitializeSid(&paa->sia, paa->nSubAuthorityCount, paa->nSubAuthority[0], paa->nSubAuthority[1], paa->nSubAuthority[2], paa->nSubAuthority[3], paa->nSubAuthority[4], paa->nSubAuthority[5], paa->nSubAuthority[6], paa->nSubAuthority[7], &psid))
46 {
47 AclExitWithLastError(hr, "failed to initialize SID");
48 }
49 }
50
51 if (!::CheckTokenMembership(hToken, psid, &fIsMember))
52 {
53 AclExitWithLastError(hr, "failed to check membership");
54 }
55
56 fIsMember ? hr = S_OK : hr = S_FALSE;
57
58 LExit:
59 if (psid)
60 {
61 ::FreeSid(psid); // TODO: does this have bad behavior if SID was allocated by Heap from AclGetAccountSid?
62 }
63
64 return hr;
65 }
66
67
68 /********************************************************************
69 AclCheckAdministratorAccess - determines if token has Administrator privileges
70
71 NOTE: if hToken is NULL, the thread will be checked
72 if hToken is not NULL the token must be an impersonation token
73 ********************************************************************/
74 extern "C" HRESULT DAPI AclCheckAdministratorAccess(
75 __in HANDLE hToken
76 )
77 {
78 ACL_ACCESS aa;
79 SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY;
80
81 memset(&aa, 0, sizeof(aa));
82 aa.sia = siaNt;
83 aa.nSubAuthorityCount = 2;
84 aa.nSubAuthority[0] = SECURITY_BUILTIN_DOMAIN_RID;
85 aa.nSubAuthority[1] = DOMAIN_ALIAS_RID_ADMINS;
86
87 return AclCheckAccess(hToken, &aa);
88 }
89
90
91 /********************************************************************
92 AclCheckLocalSystemAccess - determines if token has LocalSystem privileges
93
94 NOTE: if hToken is NULL, the thread will be checked
95 if hToken is not NULL the token must be an impersonation token
96 ********************************************************************/
97 extern "C" HRESULT DAPI AclCheckLocalSystemAccess(
98 __in HANDLE hToken
99 )
100 {
101 ACL_ACCESS aa;
102 SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY;
103
104 memset(&aa, 0, sizeof(aa));
105 aa.sia = siaNt;
106 aa.nSubAuthorityCount = 1;
107 aa.nSubAuthority[0] = SECURITY_LOCAL_SYSTEM_RID;
108
109 return AclCheckAccess(hToken, &aa);
110 }
111
112
113 /********************************************************************
114 AclGetWellKnownSid - returns a SID for the specified account
115
116 ********************************************************************/
117 extern "C" HRESULT DAPI AclGetWellKnownSid(
118 __in WELL_KNOWN_SID_TYPE wkst,
119 __deref_out PSID* ppsid
120 )
121 {
122 Assert(ppsid);
123
124 HRESULT hr = S_OK;;
125 PSID psid = NULL;
126 DWORD cbSid = SECURITY_MAX_SID_SIZE;
127
128 PSID psidTemp = NULL;
129 #if(_WIN32_WINNT < 0x0501)
130 SID_IDENTIFIER_AUTHORITY siaNT = SECURITY_NT_AUTHORITY;
131 SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_WORLD_SID_AUTHORITY;
132 SID_IDENTIFIER_AUTHORITY siaCreator = SECURITY_CREATOR_SID_AUTHORITY;
133 BOOL fSuccess = FALSE;
134 #endif
135
136 //
137 // allocate memory for the SID and get it
138 //
139 psid = static_cast<PSID>(MemAlloc(cbSid, TRUE));
140 AclExitOnNull(psid, hr, E_OUTOFMEMORY, "failed allocate memory for well known SID");
141
142 #if(_WIN32_WINNT < 0x0501)
143 switch (wkst)
144 {
145 case WinWorldSid: // Everyone
146 fSuccess = ::AllocateAndInitializeSid(&siaWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp);
147 break;
148 case WinAuthenticatedUserSid: // Authenticated Users
149 fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_AUTHENTICATED_USER_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp);
150 break;
151 case WinLocalSystemSid: // LocalSystem
152 fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp);
153 break;
154 case WinLocalServiceSid: // LocalService
155 fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp);
156 break;
157 case WinNetworkServiceSid: // NetworkService
158 fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_NETWORK_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp);
159 break;
160 case WinBuiltinGuestsSid: // Guests
161 fSuccess = ::AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS, 0, 0, 0, 0, 0, 0, &psidTemp);
162 break;
163 case WinBuiltinAdministratorsSid: // Administrators
164 fSuccess = ::AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidTemp);
165 break;
166 case WinBuiltinUsersSid: // Users
167 fSuccess = ::AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0, &psidTemp);
168 break;
169 case WinCreatorOwnerSid: //CREATOR OWNER
170 fSuccess = ::AllocateAndInitializeSid(&siaCreator, 1, SECURITY_CREATOR_OWNER_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp);
171 break;
172 case WinInteractiveSid: // INTERACTIVE
173 fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_INTERACTIVE_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp);
174 break;
175 default:
176 hr = E_INVALIDARG;
177 AclExitOnFailure(hr, "unknown well known SID: %d", wkst);
178 }
179
180 if (!fSuccess)
181 AclExitOnLastError(hr, "failed to allocate well known SID: %d", wkst);
182
183 if (!::CopySid(cbSid, psid, psidTemp))
184 AclExitOnLastError(hr, "failed to create well known SID: %d", wkst);
185 #else
186 Assert(NULL == psidTemp);
187 if (!::CreateWellKnownSid(wkst, NULL, psid, &cbSid))
188 {
189 AclExitWithLastError(hr, "failed to create well known SID: %d", wkst);
190 }
191 #endif
192
193 *ppsid = psid;
194 psid = NULL; // null it here so it won't be released below
195
196 Assert(S_OK == hr && ::IsValidSid(*ppsid));
197 LExit:
198 if (psidTemp)
199 {
200 ::FreeSid(psidTemp);
201 }
202
203 ReleaseMem(psid);
204
205 return hr;
206 }
207
208
209 /********************************************************************
210 AclGetAccountSid - returns a SID for the specified account
211
212 ********************************************************************/
213 extern "C" HRESULT DAPI AclGetAccountSid(
214 __in_opt LPCWSTR wzSystem,
215 __in_z LPCWSTR wzAccount,
216 __deref_out PSID* ppsid
217 )
218 {
219 Assert(wzAccount && *wzAccount && ppsid);
220
221 HRESULT hr = S_OK;
222 UINT er = ERROR_SUCCESS;
223 PSID psid = NULL;
224 DWORD cbSid = SECURITY_MAX_SID_SIZE;
225 LPWSTR pwzDomainName = NULL;
226 DWORD cbDomainName = 255;
227 SID_NAME_USE peUse;
228
229 //
230 // allocate memory for the SID and domain name
231 //
232 psid = static_cast<PSID>(MemAlloc(cbSid, TRUE));
233 AclExitOnNull(psid, hr, E_OUTOFMEMORY, "failed to allocate memory for SID");
234 hr = StrAlloc(&pwzDomainName, cbDomainName);
235 AclExitOnFailure(hr, "failed to allocate string for domain name");
236
237 //
238 // try to lookup the account now
239 //
240 if (!::LookupAccountNameW(wzSystem, wzAccount, psid, &cbSid, pwzDomainName, &cbDomainName, &peUse))
241 {
242 // if one of the buffers wasn't large enough
243 er = ::GetLastError();
244 if (ERROR_INSUFFICIENT_BUFFER == er)
245 {
246 if (SECURITY_MAX_SID_SIZE < cbSid)
247 {
248 PSID psidNew = static_cast<PSID>(MemReAlloc(psid, cbSid, TRUE));
249 AclExitOnNullWithLastError(psidNew, hr, "failed to allocate memory for account: %ls", wzAccount);
250
251 psid = psidNew;
252 }
253 if (255 < cbDomainName)
254 {
255 hr = StrAlloc(&pwzDomainName, cbDomainName);
256 AclExitOnFailure(hr, "failed to allocate string for domain name");
257 }
258
259 if (!::LookupAccountNameW(wzSystem, wzAccount, psid, &cbSid, pwzDomainName, &cbDomainName, &peUse))
260 {
261 AclExitWithLastError(hr, "failed to lookup account: %ls", wzAccount);
262 }
263 }
264 else
265 {
266 AclExitOnWin32Error(er, hr, "failed to lookup account: %ls", wzAccount);
267 }
268 }
269
270 *ppsid = psid;
271 psid = NULL;
272
273 hr = S_OK;
274 LExit:
275 ReleaseStr(pwzDomainName);
276 ReleaseMem(psid);
277
278 return hr;
279 }
280
281
282 /********************************************************************
283 AclGetAccountSidString - gets a string version of the user's SID
284
285 NOTE: ppwzSid should be freed with StrFree()
286 ********************************************************************/
287 extern "C" HRESULT DAPI AclGetAccountSidString(
288 __in_z LPCWSTR wzSystem,
289 __in_z LPCWSTR wzAccount,
290 __deref_out_z LPWSTR* ppwzSid
291 )
292 {
293 Assert(ppwzSid);
294 HRESULT hr = S_OK;
295 PSID psid = NULL;
296 LPWSTR pwz = NULL;
297
298 *ppwzSid = NULL;
299
300 hr = AclGetAccountSid(wzSystem, wzAccount, &psid);
301 AclExitOnFailure(hr, "failed to get SID for account: %ls", wzAccount);
302 Assert(::IsValidSid(psid));
303
304 if (!::ConvertSidToStringSidW(psid, &pwz))
305 {
306 AclExitWithLastError(hr, "failed to convert SID to string for Account: %ls", wzAccount);
307 }
308
309 hr = StrAllocString(ppwzSid, pwz, 0);
310
311 LExit:
312 if (FAILED(hr))
313 {
314 ReleaseNullStr(*ppwzSid);
315 }
316
317 if (pwz)
318 {
319 ::LocalFree(pwz);
320 }
321
322 if (psid)
323 {
324 AclFreeSid(psid);
325 }
326
327 return hr;
328 }
329
330
331 /********************************************************************
332 AclCreateDacl - creates a DACL from ACL_ACE structures
333
334 ********************************************************************/
335 extern "C" HRESULT DAPI AclCreateDacl(
336 __in_ecount(cDeny) ACL_ACE rgaaDeny[],
337 __in DWORD cDeny,
338 __in_ecount(cAllow) ACL_ACE rgaaAllow[],
339 __in DWORD cAllow,
340 __deref_out ACL** ppAcl
341 )
342 {
343 Assert(ppAcl);
344 HRESULT hr = S_OK;
345 ACL* pAcl = NULL;
346 DWORD cbAcl = 0;
347 DWORD i;
348
349 *ppAcl = NULL;
350
351 // initialize the ACL
352 cbAcl = sizeof(ACL);
353 for (i = 0; i < cDeny; ++i)
354 {
355 cbAcl += sizeof(ACCESS_DENIED_ACE) + ::GetLengthSid(rgaaDeny[i].psid) - sizeof(DWORD);
356 }
357
358 for (i = 0; i < cAllow; ++i)
359 {
360 cbAcl += sizeof(ACCESS_ALLOWED_ACE) + ::GetLengthSid(rgaaAllow[i].psid) - sizeof(DWORD);
361 }
362
363 pAcl = static_cast<ACL*>(MemAlloc(cbAcl, TRUE));
364 AclExitOnNull(pAcl, hr, E_OUTOFMEMORY, "failed to allocate ACL");
365
366 #pragma prefast(push)
367 #pragma prefast(disable:25029)
368 if (!::InitializeAcl(pAcl, cbAcl, ACL_REVISION))
369 #pragma prefast(pop)
370 {
371 AclExitWithLastError(hr, "failed to initialize ACL");
372 }
373
374 // add in the ACEs (denied first)
375 for (i = 0; i < cDeny; ++i)
376 {
377 #pragma prefast(push)
378 #pragma prefast(disable:25029)
379 if (!::AddAccessDeniedAceEx(pAcl, ACL_REVISION, rgaaDeny[i].dwFlags, rgaaDeny[i].dwMask, rgaaDeny[i].psid))
380 #pragma prefast(pop)
381 {
382 AclExitWithLastError(hr, "failed to add access denied ACE #%d to ACL", i);
383 }
384 }
385 for (i = 0; i < cAllow; ++i)
386 {
387 #pragma prefast(push)
388 #pragma prefast(disable:25029)
389 if (!::AddAccessAllowedAceEx(pAcl, ACL_REVISION, rgaaAllow[i].dwFlags, rgaaAllow[i].dwMask, rgaaAllow[i].psid))
390 #pragma prefast(pop)
391 {
392 AclExitWithLastError(hr, "failed to add access allowed ACE #%d to ACL", i);
393 }
394 }
395
396 *ppAcl = pAcl;
397 pAcl = NULL;
398 AssertSz(::IsValidAcl(*ppAcl), "AclCreateDacl() - created invalid ACL");
399 Assert(S_OK == hr);
400 LExit:
401 if (pAcl)
402 {
403 AclFreeDacl(pAcl);
404 }
405
406 return hr;
407 }
408
409
410 /********************************************************************
411 AclAddToDacl - creates a new DACL from an ACL plus new ACL_ACE structure
412
413 ********************************************************************/
414 extern "C" HRESULT DAPI AclAddToDacl(
415 __in ACL* pAcl,
416 __in_ecount_opt(cDeny) const ACL_ACE rgaaDeny[],
417 __in DWORD cDeny,
418 __in_ecount_opt(cAllow) const ACL_ACE rgaaAllow[],
419 __in DWORD cAllow,
420 __deref_out ACL** ppAclNew
421 )
422 {
423 Assert(pAcl && ::IsValidAcl(pAcl) && ppAclNew);
424 HRESULT hr = S_OK;
425
426 ACL_SIZE_INFORMATION asi;
427 ACL_ACE* paaNewDeny = NULL;
428 DWORD cNewDeny = 0;
429 ACL_ACE* paaNewAllow = NULL;
430 DWORD cNewAllow = 0;
431
432 ACCESS_ALLOWED_ACE* paaa;
433 ACCESS_DENIED_ACE* pada;
434 DWORD i;
435
436 // allocate memory for all the new ACEs (NOTE: this over calculates the memory necessary, but that's okay)
437 if (!::GetAclInformation(pAcl, &asi, sizeof(asi), AclSizeInformation))
438 {
439 AclExitWithLastError(hr, "failed to get information about original ACL");
440 }
441
442 if ((asi.AceCount + cDeny) < asi.AceCount || // check for overflow
443 (asi.AceCount + cDeny) < cDeny || // check for overflow
444 (asi.AceCount + cDeny) >= MAXSIZE_T / sizeof(ACL_ACE))
445 {
446 hr = E_OUTOFMEMORY;
447 AclExitOnFailure(hr, "Not enough memory to allocate %d ACEs", (asi.AceCount + cDeny));
448 }
449
450 paaNewDeny = static_cast<ACL_ACE*>(MemAlloc(sizeof(ACL_ACE) * (asi.AceCount + cDeny), TRUE));
451 AclExitOnNull(paaNewDeny, hr, E_OUTOFMEMORY, "failed to allocate memory for new deny ACEs");
452
453 if ((asi.AceCount + cAllow) < asi.AceCount || // check for overflow
454 (asi.AceCount + cAllow) < cAllow || // check for overflow
455 (asi.AceCount + cAllow) >= MAXSIZE_T / sizeof(ACL_ACE))
456 {
457 hr = E_OUTOFMEMORY;
458 AclExitOnFailure(hr, "Not enough memory to allocate %d ACEs", (asi.AceCount + cAllow));
459 }
460
461 paaNewAllow = static_cast<ACL_ACE*>(MemAlloc(sizeof(ACL_ACE) * (asi.AceCount + cAllow), TRUE));
462 AclExitOnNull(paaNewAllow, hr, E_OUTOFMEMORY, "failed to allocate memory for new allow ACEs");
463
464 // fill in the new structures with old data then new data (denied first)
465 for (i = 0; i < asi.AceCount; ++i)
466 {
467 if (!::GetAce(pAcl, i, reinterpret_cast<LPVOID*>(&pada)))
468 {
469 AclExitWithLastError(hr, "failed to get ACE #%d from ACL", i);
470 }
471
472 if (ACCESS_DENIED_ACE_TYPE != pada->Header.AceType)
473 {
474 continue; // skip non-denied aces
475 }
476
477 paaNewDeny[i].dwFlags = pada->Header.AceFlags;
478 paaNewDeny[i].dwMask = pada->Mask;
479 paaNewDeny[i].psid = reinterpret_cast<PSID>(&(pada->SidStart));
480 ++cNewDeny;
481 }
482
483 memcpy(paaNewDeny + cNewDeny, rgaaDeny, sizeof(ACL_ACE) * cDeny);
484 cNewDeny += cDeny;
485
486
487 for (i = 0; i < asi.AceCount; ++i)
488 {
489 if (!::GetAce(pAcl, i, reinterpret_cast<LPVOID*>(&paaa)))
490 {
491 AclExitWithLastError(hr, "failed to get ACE #%d from ACL", i);
492 }
493
494 if (ACCESS_ALLOWED_ACE_TYPE != paaa->Header.AceType)
495 {
496 continue; // skip non-allowed aces
497 }
498
499 paaNewAllow[i].dwFlags = paaa->Header.AceFlags;
500 paaNewAllow[i].dwMask = paaa->Mask;
501 paaNewAllow[i].psid = reinterpret_cast<PSID>(&(paaa->SidStart));
502 ++cNewAllow;
503 }
504
505 memcpy(paaNewAllow + cNewAllow, rgaaAllow, sizeof(ACL_ACE) * cAllow);
506 cNewAllow += cAllow;
507
508 // create the dacl with the new
509 hr = AclCreateDacl(paaNewDeny, cNewDeny, paaNewAllow, cNewAllow, ppAclNew);
510 AclExitOnFailure(hr, "failed to create new ACL from existing ACL");
511
512 AssertSz(::IsValidAcl(*ppAclNew), "AclAddToDacl() - created invalid ACL");
513 Assert(S_OK == hr);
514 LExit:
515 ReleaseMem(paaNewAllow);
516 ReleaseMem(paaNewDeny);
517
518 return hr;
519 }
520
521
522 /********************************************************************
523 AclMergeDacls - creates a new DACL from two existing ACLs
524
525 ********************************************************************/
526 extern "C" HRESULT DAPI AclMergeDacls(
527 __in const ACL* pAcl1,
528 __in const ACL* pAcl2,
529 __deref_out ACL** ppAclNew
530 )
531 {
532 HRESULT hr = E_NOTIMPL;
533
534 Assert(pAcl1 && pAcl2 && ppAclNew);
535 UNREFERENCED_PARAMETER(pAcl1);
536 UNREFERENCED_PARAMETER(pAcl2);
537 UNREFERENCED_PARAMETER(ppAclNew);
538
539 //LExit:
540 return hr;
541 }
542
543
544 /********************************************************************
545 AclCreateDaclOld - creates a DACL from an ACL_ACCESS structure
546
547 ********************************************************************/
548 extern "C" HRESULT DAPI AclCreateDaclOld(
549 __in_ecount(cAclAccesses) ACL_ACCESS* paa,
550 __in DWORD cAclAccesses,
551 __deref_out ACL** ppACL
552 )
553 {
554 Assert(ppACL);
555 HRESULT hr = S_OK;
556 DWORD* pdwAccessMask = NULL;
557 PSID* ppsid = NULL;
558
559 DWORD i;
560 int cbAcl;
561
562 *ppACL = NULL;
563
564 //
565 // create the SIDs and calculate the space for the ACL
566 //
567 pdwAccessMask = static_cast<DWORD*>(MemAlloc(sizeof(DWORD) * cAclAccesses, TRUE));
568 AclExitOnNull(pdwAccessMask, hr, E_OUTOFMEMORY, "failed allocate memory for access mask");
569 ppsid = static_cast<PSID*>(MemAlloc(sizeof(PSID) * cAclAccesses, TRUE));
570 AclExitOnNull(ppsid, hr, E_OUTOFMEMORY, "failed allocate memory for sid");
571
572 cbAcl = sizeof (ACL); // start with the size of the header
573 for (i = 0; i < cAclAccesses; ++i)
574 {
575 if (paa[i].pwzAccountName)
576 {
577 hr = AclGetAccountSid(NULL, paa[i].pwzAccountName, ppsid + i);
578 AclExitOnFailure(hr, "failed to get SID for account: %ls", paa[i].pwzAccountName);
579 }
580 else
581 {
582 if ((!::AllocateAndInitializeSid(&paa[i].sia, paa[i].nSubAuthorityCount,
583 paa[i].nSubAuthority[0], paa[i].nSubAuthority[1],
584 paa[i].nSubAuthority[2], paa[i].nSubAuthority[3],
585 paa[i].nSubAuthority[4], paa[i].nSubAuthority[5],
586 paa[i].nSubAuthority[6], paa[i].nSubAuthority[7],
587 (void**)(ppsid + i))))
588 {
589 AclExitWithLastError(hr, "failed to initialize SIDs #%u", i);
590 }
591 }
592
593 // add the newly allocated SID size to the count of bytes for this ACL
594 cbAcl +=::GetLengthSid(*(ppsid + i)) - sizeof(DWORD);
595 if (paa[i].fDenyAccess)
596 {
597 cbAcl += sizeof(ACCESS_DENIED_ACE);
598 }
599 else
600 {
601 cbAcl += sizeof(ACCESS_ALLOWED_ACE);
602 }
603
604 pdwAccessMask[i] = paa[i].dwAccessMask;
605 }
606
607 //
608 // allocate the ACL and set the appropriate ACEs
609 //
610 *ppACL = static_cast<ACL*>(MemAlloc(cbAcl, FALSE));
611 AclExitOnNull(*ppACL, hr, E_OUTOFMEMORY, "failed allocate memory for ACL");
612
613 #pragma prefast(push)
614 #pragma prefast(disable:25029)
615 if (!::InitializeAcl(*ppACL, cbAcl, ACL_REVISION))
616 #pragma prefast(pop)
617 {
618 AclExitWithLastError(hr, "failed to initialize ACLs");
619 }
620
621 // add an access-allowed ACE for each of the SIDs
622 for (i = 0; i < cAclAccesses; ++i)
623 {
624 if (paa[i].fDenyAccess)
625 {
626 #pragma prefast(push)
627 #pragma prefast(disable:25029)
628 if (!::AddAccessDeniedAceEx(*ppACL, ACL_REVISION, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, pdwAccessMask[i], *(ppsid + i)))
629 #pragma prefast(pop)
630 {
631 AclExitWithLastError(hr, "failed to add access denied for ACE");
632 }
633 }
634 else
635 {
636 #pragma prefast(push)
637 #pragma prefast(disable:25029)
638 if (!::AddAccessAllowedAceEx(*ppACL, ACL_REVISION, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, pdwAccessMask[i], *(ppsid + i)))
639 #pragma prefast(pop)
640 {
641 AclExitWithLastError(hr, "failed to add access allowed for ACE");
642 }
643 }
644 }
645
646 LExit:
647 if (FAILED(hr))
648 {
649 ReleaseNullMem(*ppACL);
650 }
651
652 if (ppsid)
653 {
654 for (i = 0; i < cAclAccesses; ++i)
655 {
656 if (ppsid[i])
657 {
658 ::FreeSid(ppsid[i]);
659 }
660 }
661
662 MemFree(ppsid);
663 }
664
665 ReleaseMem(pdwAccessMask);
666
667 return hr;
668 }
669
670
671 /********************************************************************
672 AclCreateSecurityDescriptorFromDacl - creates a self-relative security
673 descriptor from an existing DACL
674
675 ********************************************************************/
676 extern "C" HRESULT DAPI AclCreateSecurityDescriptorFromDacl(
677 __in ACL* pACL,
678 __deref_out SECURITY_DESCRIPTOR** ppsd
679 )
680 {
681 HRESULT hr = S_OK;
682
683 SECURITY_DESCRIPTOR sd;
684 DWORD cbSD;
685
686 AclExitOnNull(pACL, hr, E_INVALIDARG, "Failed to create security descriptor from DACL, because no DACL was provided");
687 AclExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to create security descriptor from DACL, because no output object was provided");
688
689 *ppsd = NULL;
690
691 //
692 // create the absolute security descriptor
693 //
694
695 // initialize our security descriptor, throw the ACL into it, and set the owner
696 #pragma prefast(push)
697 #pragma prefast(disable:25028) // We only call this when pACL isn't NULL, so this call is safe according to the docs
698 #pragma prefast(disable:25029)
699 if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION) ||
700 (!::SetSecurityDescriptorDacl(&sd, TRUE, pACL, FALSE)) ||
701 (!::SetSecurityDescriptorOwner(&sd, NULL, FALSE)))
702 #pragma prefast(pop)
703 {
704 AclExitWithLastError(hr, "failed to initialize security descriptor");
705 }
706
707 //
708 // create the self-relative security descriptor
709 //
710 cbSD = ::GetSecurityDescriptorLength(&sd);
711 *ppsd = static_cast<SECURITY_DESCRIPTOR*>(MemAlloc(cbSD, FALSE));
712 AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor");
713
714 ::MakeSelfRelativeSD(&sd, (BYTE*)*ppsd, &cbSD);
715 Assert(::IsValidSecurityDescriptor(*ppsd));
716
717 LExit:
718 if (FAILED(hr) && NULL != ppsd && NULL != *ppsd)
719 {
720 MemFree(*ppsd);
721 *ppsd = NULL;
722 }
723
724 return hr;
725 }
726
727
728 /********************************************************************
729 AclCreateSecurityDescriptor - creates a self-relative security descriptor from an
730 ACL_ACCESS structure
731
732 NOTE: ppsd should be freed with AclFreeSecurityDescriptor()
733 ********************************************************************/
734 extern "C" HRESULT DAPI AclCreateSecurityDescriptor(
735 __in_ecount(cAclAccesses) ACL_ACCESS* paa,
736 __in DWORD cAclAccesses,
737 __deref_out SECURITY_DESCRIPTOR** ppsd
738 )
739 {
740 Assert(ppsd);
741 HRESULT hr = S_OK;
742
743 ACL* pACL;
744
745 *ppsd = NULL;
746
747 //
748 // create the DACL
749 //
750 hr = AclCreateDaclOld(paa, cAclAccesses, &pACL);
751 AclExitOnFailure(hr, "failed to create DACL for security descriptor");
752
753 //
754 // create self-relative security descriptor
755 //
756 hr = AclCreateSecurityDescriptorFromDacl(pACL, ppsd);
757
758 LExit:
759 return hr;
760 }
761
762
763 /********************************************************************
764 AclCreateSecurityDescriptorFromString - creates a self-relative security
765 descriptor from an SDDL string
766
767 NOTE: ppsd should be freed with AclFreeSecurityDescriptor()
768 ********************************************************************/
769 extern "C" HRESULT DAPI AclCreateSecurityDescriptorFromString(
770 __deref_out SECURITY_DESCRIPTOR** ppsd,
771 __in_z __format_string LPCWSTR wzSddlFormat,
772 ...
773 )
774 {
775 Assert(ppsd);
776 HRESULT hr = S_OK;
777 LPWSTR pwzSddl = NULL;
778 va_list args;
779 PSECURITY_DESCRIPTOR psd = NULL;
780 DWORD cbSD = 0;
781
782 *ppsd = NULL;
783
784 va_start(args, wzSddlFormat);
785 hr = StrAllocFormattedArgs(&pwzSddl, wzSddlFormat, args);
786 va_end(args);
787 AclExitOnFailure(hr, "failed to create SDDL string for format: %ls", wzSddlFormat);
788
789 if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(pwzSddl, SDDL_REVISION_1, &psd, &cbSD))
790 {
791 AclExitWithLastError(hr, "failed to create security descriptor from SDDL: %ls", pwzSddl);
792 }
793
794 *ppsd = static_cast<SECURITY_DESCRIPTOR*>(MemAlloc(cbSD, FALSE));
795 AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed to allocate memory for security descriptor");
796
797 memcpy(*ppsd, psd, cbSD);
798 Assert(::IsValidSecurityDescriptor(*ppsd));
799
800 Assert(S_OK == hr);
801
802 LExit:
803 if (FAILED(hr) && NULL != ppsd && NULL != *ppsd)
804 {
805 MemFree(*ppsd);
806 *ppsd = NULL;
807 }
808
809 if (psd)
810 {
811 ::LocalFree(psd);
812 }
813
814 ReleaseStr(pwzSddl);
815 return hr;
816 }
817
818
819 /********************************************************************
820 AclDuplicateSecurityDescriptor - creates a copy of a self-relative security descriptor
821
822 NOTE: passed in security descriptor must be in self-relative format
823 ********************************************************************/
824 extern "C" HRESULT DAPI AclDuplicateSecurityDescriptor(
825 __in SECURITY_DESCRIPTOR* psd,
826 __deref_out SECURITY_DESCRIPTOR** ppsd
827 )
828 {
829 HRESULT hr = S_OK;
830 DWORD cbSD;
831
832 AclExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to get duplicate ACL security descriptor because no place to output was provided");
833 *ppsd = NULL;
834
835 //
836 // create the self-relative security descriptor
837 //
838 cbSD = ::GetSecurityDescriptorLength(psd);
839 *ppsd = static_cast<SECURITY_DESCRIPTOR*>(MemAlloc(cbSD, 0));
840 AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor");
841
842 memcpy(*ppsd, psd, cbSD);
843 Assert(::IsValidSecurityDescriptor(*ppsd));
844
845 LExit:
846 if (FAILED(hr) && NULL != ppsd && NULL != *ppsd)
847 {
848 MemFree(*ppsd);
849 *ppsd = NULL;
850 }
851
852 return hr;
853 }
854
855
856 /********************************************************************
857 AclGetSecurityDescriptor - returns self-relative security descriptor for named object
858
859 NOTE: free ppsd with AclFreeSecurityDescriptor()
860 ********************************************************************/
861 extern "C" HRESULT DAPI AclGetSecurityDescriptor(
862 __in_z LPCWSTR wzObject,
863 __in SE_OBJECT_TYPE sot,
864 __in SECURITY_INFORMATION securityInformation,
865 __deref_out SECURITY_DESCRIPTOR** ppsd
866 )
867 {
868 HRESULT hr = S_OK;
869 DWORD er;
870 PSECURITY_DESCRIPTOR psd = NULL;
871 DWORD cbSD;
872
873 AclExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to get ACL Security Descriptor because no place to output was provided");
874 *ppsd = NULL;
875
876 // get the security descriptor for the object
877 er = ::GetNamedSecurityInfoW(const_cast<LPWSTR>(wzObject), sot, securityInformation, NULL, NULL, NULL, NULL, &psd);
878 AclExitOnWin32Error(er, hr, "failed to get security info from object: %ls", wzObject);
879 Assert(::IsValidSecurityDescriptor(psd));
880
881 // copy the self-relative security descriptor
882 cbSD = ::GetSecurityDescriptorLength(psd);
883 *ppsd = static_cast<SECURITY_DESCRIPTOR*>(MemAlloc(cbSD, 0));
884 AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor");
885
886 memcpy(*ppsd, psd, cbSD);
887 Assert(::IsValidSecurityDescriptor(*ppsd));
888
889 LExit:
890 if (FAILED(hr) && NULL != ppsd && NULL != *ppsd)
891 {
892 MemFree(*ppsd);
893 *ppsd = NULL;
894 }
895
896 if (psd)
897 {
898 ::LocalFree(psd);
899 }
900
901 return hr;
902 }
903
904
905 extern "C" HRESULT DAPI AclSetSecurityWithRetry(
906 __in_z LPCWSTR wzObject,
907 __in SE_OBJECT_TYPE sot,
908 __in SECURITY_INFORMATION securityInformation,
909 __in_opt PSID psidOwner,
910 __in_opt PSID psidGroup,
911 __in_opt PACL pDacl,
912 __in_opt PACL pSacl,
913 __in DWORD cRetry,
914 __in DWORD dwWaitMilliseconds
915 )
916 {
917 HRESULT hr = S_OK;
918 LPWSTR sczObject = NULL;
919 DWORD i = 0;
920
921 hr = StrAllocString(&sczObject, wzObject, 0);
922 AclExitOnFailure(hr, "Failed to copy object to secure.");
923
924 hr = E_FAIL;
925 for (i = 0; FAILED(hr) && i <= cRetry; ++i)
926 {
927 if (0 < i)
928 {
929 ::Sleep(dwWaitMilliseconds);
930 }
931
932 DWORD er = ::SetNamedSecurityInfoW(sczObject, sot, securityInformation, psidOwner, psidGroup, pDacl, pSacl);
933 hr = HRESULT_FROM_WIN32(er);
934 }
935 AclExitOnRootFailure(hr, "Failed to set security on object '%ls' after %u retries.", wzObject, i);
936
937 LExit:
938 ReleaseStr(sczObject);
939
940 return hr;
941 }
942
943
944 /********************************************************************
945 AclFreeSid - frees a SID created by any Acl* functions
946
947 ********************************************************************/
948 extern "C" HRESULT DAPI AclFreeSid(
949 __in PSID psid
950 )
951 {
952 Assert(psid && ::IsValidSid(psid));
953 HRESULT hr = S_OK;
954
955 hr = MemFree(psid);
956
957 return hr;
958 }
959
960
961 /********************************************************************
962 AclFreeDacl - frees a DACL created by any Acl* functions
963
964 ********************************************************************/
965 extern "C" HRESULT DAPI AclFreeDacl(
966 __in ACL* pACL
967 )
968 {
969 Assert(pACL);
970 HRESULT hr = S_OK;
971
972 hr = MemFree(pACL);
973
974 return hr;
975 }
976
977
978 /********************************************************************
979 AclFreeSecurityDescriptor - frees a security descriptor created by any Acl* functions
980
981 ********************************************************************/
982 extern "C" HRESULT DAPI AclFreeSecurityDescriptor(
983 __in SECURITY_DESCRIPTOR* psd
984 )
985 {
986 Assert(psd && ::IsValidSecurityDescriptor(psd));
987 HRESULT hr = S_OK;
988
989 hr = MemFree(psd);
990
991 return hr;
992 }
993
994
995 /********************************************************************
996 AclAddAdminToSecurityDescriptor - Adds the Administrators group to a security descriptor
997
998 ********************************************************************/
999 extern "C" HRESULT DAPI AclAddAdminToSecurityDescriptor(
1000 __in SECURITY_DESCRIPTOR* pSecurity,
1001 __deref_out SECURITY_DESCRIPTOR** ppSecurityNew
1002 )
1003 {
1004 HRESULT hr = S_OK;
1005 PACL pAcl = NULL;
1006 PACL pAclNew = NULL;
1007 BOOL fValid, fDaclDefaulted;
1008 ACL_ACE ace[1];
1009 SECURITY_DESCRIPTOR* pSecurityNew;
1010
1011 if (!::GetSecurityDescriptorDacl(pSecurity, &fValid, &pAcl, &fDaclDefaulted) || !fValid)
1012 {
1013 AclExitOnLastError(hr, "Failed to get acl from security descriptor");
1014 }
1015
1016 hr = AclGetWellKnownSid(WinBuiltinAdministratorsSid, &ace[0].psid);
1017 AclExitOnFailure(hr, "failed to get sid for Administrators group");
1018
1019 ace[0].dwFlags = NO_PROPAGATE_INHERIT_ACE;
1020 ace[0].dwMask = GENERIC_ALL;
1021
1022 hr = AclAddToDacl(pAcl, NULL, 0, ace, 1, &pAclNew);
1023 AclExitOnFailure(hr, "failed to add Administrators ACE to ACL");
1024
1025 hr = AclCreateSecurityDescriptorFromDacl(pAclNew, &pSecurityNew);
1026 AclExitOnLastError(hr, "Failed to create new security descriptor");
1027
1028 // The DACL is referenced by, not copied into, the security descriptor. Make sure not to free it.
1029 pAclNew = NULL;
1030
1031 *ppSecurityNew = pSecurityNew;
1032
1033 LExit:
1034 if (pAclNew)
1035 {
1036 AclFreeDacl(pAclNew);
1037 }
1038 if (ace[0].psid)
1039 {
1040 AclFreeSid(ace[0].psid);
1041 }
1042
1043 return hr;
1044 }