xref: /reactos/ntoskrnl/se/accesschk.c (revision bcbfcd22)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:         Security access check control implementation
5  * COPYRIGHT:       Copyright 2014 Timo Kreuzer <timo.kreuzer@reactos.org>
6  *                  Copyright 2014 Eric Kohl
7  *                  Copyright 2022 George Bișoc <george.bisoc@reactos.org>
8  */
9 
10 /* INCLUDES *******************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* PRIVATE FUNCTIONS **********************************************************/
17 
18 /**
19  * @brief
20  * Allocates memory for the internal access check rights
21  * data structure and initializes it for use for the kernel.
22  * The purpose of this piece of data is to track down the
23  * remaining, granted and denied access rights whilst we
24  * are doing an access check procedure.
25  *
26  * @return
27  * Returns a pointer to allocated and initialized access
28  * check rights, otherwise NULL is returned.
29  */
30 PACCESS_CHECK_RIGHTS
31 SepInitAccessCheckRights(VOID)
32 {
33     PACCESS_CHECK_RIGHTS AccessRights;
34 
35     PAGED_CODE();
36 
37     /* Allocate some pool for access check rights */
38     AccessRights = ExAllocatePoolWithTag(PagedPool,
39                                          sizeof(ACCESS_CHECK_RIGHTS),
40                                          TAG_ACCESS_CHECK_RIGHT);
41 
42     /* Bail out if we failed */
43     if (!AccessRights)
44     {
45         return NULL;
46     }
47 
48     /* Initialize the structure */
49     AccessRights->RemainingAccessRights = 0;
50     AccessRights->GrantedAccessRights = 0;
51     AccessRights->DeniedAccessRights = 0;
52 
53     return AccessRights;
54 }
55 
56 /**
57  * @brief
58  * Frees an allocated access check rights from
59  * memory space after access check procedures
60  * have finished.
61  *
62  * @param[in] AccessRights
63  * A pointer to access check rights of which is
64  * to be freed from memory.
65  *
66  * @return
67  * Nothing.
68  */
69 VOID
70 SepFreeAccessCheckRights(
71     _In_ PACCESS_CHECK_RIGHTS AccessRights)
72 {
73     PAGED_CODE();
74 
75     if (AccessRights)
76     {
77         ExFreePoolWithTag(AccessRights, TAG_ACCESS_CHECK_RIGHT);
78     }
79 }
80 
81 /**
82  * @brief
83  * Analyzes an access control entry that is present in a discretionary
84  * access control list (DACL) for access right masks of each entry with
85  * the purpose to judge whether the calling thread can be warranted
86  * access check to a certain object or not.
87  *
88  * @param[in] ActionType
89  * The type of analysis to be done against an access entry. This type
90  * influences how access rights are gathered. This can either be AccessCheckMaximum
91  * which means the algorithm will perform analysis against ACEs on behalf of the
92  * requestor that gave us the acknowledgement that he desires MAXIMUM_ALLOWED access
93  * right or AccessCheckRegular if the requestor wants a subset of access rights.
94  *
95  * @param[in] Dacl
96  * The discretionary access control list to be given to this function. This DACL
97  * must have at least one ACE currently present in the list.
98  *
99  * @param[in] AccessToken
100  * A pointer to an access token, where an equality comparison check is performed if
101  * the security identifier (SID) from a ACE of a certain object is present in this
102  * token. This token represents the effective (calling thread) token of the caller.
103  *
104  * @param[in] PrimaryAccessToken
105  * A pointer to an access token, represented as an access token associated with the
106  * primary calling process. This token describes the primary security context of the
107  * main process.
108  *
109  * @param[in] IsTokenRestricted
110  * If this parameter is set to TRUE, the function considers the token pointed by
111  * AccessToken parameter argument as restricted. That is, the token has restricted
112  * SIDs therefore the function will act accordingly against that token by checking
113  * for restricted SIDs only when doing an equaility comparison check between the
114  * two identifiers.
115  *
116  * @param[in] AccessRightsAllocated
117  * If this parameter is set to TRUE, the function will not allocate the access
118  * check rights again. This is typical when we have to do additional analysis
119  * of ACEs because a token has restricted SIDs (see IsTokenRestricted parameter)
120  * of which we already initialized the access check rights pointer before.
121  *
122  * @param[in] PrincipalSelfSid
123  * A pointer to a security identifier that represents a principal. A principal
124  * identifies a user object which is associated with its own security descriptor.
125  *
126  * @param[in] GenericMapping
127  * A pointer to a generic mapping that is associated with the object in question
128  * being checked for access. If certain set of desired access rights have
129  * a generic access right, this parameter is needed to map generic rights.
130  *
131  * @param[in] ObjectTypeList
132  * A pointer to a list array of object types. If such array is provided to the
133  * function, the algorithm will perform a different approach by doing analysis
134  * against ACEs each sub-object of an object of primary level (level 0) or sub-objects
135  * of a sub-object of an object. If this parameter is NULL, the function will normally
136  * analyze the ACEs of a DACL of the target object itself.
137  *
138  * @param[in] ObjectTypeListLength
139  * The length of the object type list array, pointed by ObjectTypeList. This length in
140  * question represents the number of elements in such array. This parameter must be 0
141  * if no array list is provided.
142  *
143  * @param[in] RemainingAccess
144  * The remaining access rights that have yet to be granted to the calling thread
145  * whomst requests access to a certain object. This parameter mustn't be 0 as
146  * the remaining rights are left to be addressed. This is the case if we have
147  * to address the remaining rights on a regular subset basis (the requestor
148  * didn't ask for MAXIMUM_ALLOWED). Otherwise this parameter can be 0.
149  *
150  * @return
151  * Returns a pointer to initialized access check rights after ACE analysis
152  * has finished. This pointer contains the rights that have been acquired
153  * in order to determine if access can be granted to the calling thread.
154  * Typically this pointer contains the remaining, denied and granted rights.
155  *
156  * Otherwise NULL is returned and thus access check procedure can't any longer
157  * continue further. We have prematurely failed this access check operation
158  * at this point.
159  */
160 PACCESS_CHECK_RIGHTS
161 SepAnalyzeAcesFromDacl(
162     _In_ ACCESS_CHECK_RIGHT_TYPE ActionType,
163     _In_ PACL Dacl,
164     _In_ PACCESS_TOKEN AccessToken,
165     _In_ PACCESS_TOKEN PrimaryAccessToken,
166     _In_ BOOLEAN IsTokenRestricted,
167     _In_ BOOLEAN AccessRightsAllocated,
168     _In_opt_ PSID PrincipalSelfSid,
169     _In_ PGENERIC_MAPPING GenericMapping,
170     _In_opt_ POBJECT_TYPE_LIST ObjectTypeList,
171     _In_ ULONG ObjectTypeListLength,
172     _In_ ACCESS_MASK RemainingAccess)
173 {
174     NTSTATUS Status;
175     PACE CurrentAce;
176     ULONG AceIndex;
177     PSID Sid;
178     ACCESS_MASK Access;
179     PACCESS_CHECK_RIGHTS AccessRights;
180 
181     PAGED_CODE();
182 
183     /* These parameters are really needed */
184     ASSERT(Dacl);
185     ASSERT(AccessToken);
186 
187     /* TODO: To be removed once we support object type handling in Se */
188     DBG_UNREFERENCED_PARAMETER(ObjectTypeList);
189     DBG_UNREFERENCED_PARAMETER(ObjectTypeListLength);
190 
191     /* TODO: To be removed once we support compound ACEs handling in Se */
192     DBG_UNREFERENCED_PARAMETER(PrimaryAccessToken);
193 
194     /*
195      * Allocate memory for access check rights if
196      * we have not done it so. Otherwise just use
197      * the already allocated pointer. This is
198      * typically when we have to do additional
199      * ACEs analysis because the token has
200      * restricted SIDs so we have allocated this
201      * pointer before.
202      */
203     if (!AccessRightsAllocated)
204     {
205         AccessRights = SepInitAccessCheckRights();
206         if (!AccessRights)
207         {
208             DPRINT1("SepAnalyzeAcesFromDacl(): Failed to initialize the access check rights!\n");
209             return NULL;
210         }
211     }
212 
213     /* Determine how we should analyze the ACEs */
214     switch (ActionType)
215     {
216         /*
217          * We got the acknowledgement the calling thread desires
218          * maximum rights (as according to MAXIMUM_ALLOWED access
219          * mask). Analyze the ACE of the given DACL.
220          */
221         case AccessCheckMaximum:
222         {
223             /* Loop over the DACL to retrieve ACEs */
224             for (AceIndex = 0; AceIndex < Dacl->AceCount; AceIndex++)
225             {
226                 /* Obtain a ACE now */
227                 Status = RtlGetAce(Dacl, AceIndex, (PVOID*)&CurrentAce);
228 
229                 /* Getting this ACE is important, otherwise something is seriously wrong */
230                 ASSERT(NT_SUCCESS(Status));
231 
232                 /*
233                  * Now it's time to analyze it based upon the
234                  * type of this ACE we're being given.
235                  */
236                 if (!(CurrentAce->Header.AceFlags & INHERIT_ONLY_ACE))
237                 {
238                     if (CurrentAce->Header.AceType == ACCESS_DENIED_ACE_TYPE)
239                     {
240                         /* Get the SID from this ACE */
241                         Sid = SepGetSidFromAce(ACCESS_DENIED_ACE_TYPE, CurrentAce);
242 
243                         if (SepSidInTokenEx(AccessToken, PrincipalSelfSid, Sid, TRUE, IsTokenRestricted))
244                         {
245                             /* Get this access right from the ACE */
246                             Access = CurrentAce->AccessMask;
247 
248                             /* Map this access right if it has a generic mask right */
249                             if ((Access & GENERIC_ACCESS) && GenericMapping)
250                             {
251                                 RtlMapGenericMask(&Access, GenericMapping);
252                             }
253 
254                             /* Deny access rights that have not been granted yet */
255                             AccessRights->DeniedAccessRights |= (Access & ~AccessRights->GrantedAccessRights);
256                             DPRINT("SepAnalyzeAcesFromDacl(): DeniedAccessRights 0x%08lx\n", AccessRights->DeniedAccessRights);
257                         }
258                     }
259                     else if (CurrentAce->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
260                     {
261                         /* Get the SID from this ACE */
262                         Sid = SepGetSidFromAce(ACCESS_ALLOWED_ACE_TYPE, CurrentAce);
263 
264                         if (SepSidInTokenEx(AccessToken, PrincipalSelfSid, Sid, FALSE, IsTokenRestricted))
265                         {
266                             /* Get this access right from the ACE */
267                             Access = CurrentAce->AccessMask;
268 
269                             /* Map this access right if it has a generic mask right */
270                             if ((Access & GENERIC_ACCESS) && GenericMapping)
271                             {
272                                 RtlMapGenericMask(&Access, GenericMapping);
273                             }
274 
275                             /* Grant access rights that have not been denied yet */
276                             AccessRights->GrantedAccessRights |= (Access & ~AccessRights->DeniedAccessRights);
277                             DPRINT("SepAnalyzeAcesFromDacl(): GrantedAccessRights 0x%08lx\n", AccessRights->GrantedAccessRights);
278                         }
279                     }
280                     else
281                     {
282                         DPRINT1("SepAnalyzeAcesFromDacl(): Unsupported ACE type 0x%lx\n", CurrentAce->Header.AceType);
283                     }
284                 }
285             }
286 
287             /* We're done here */
288             break;
289         }
290 
291         /*
292          * We got the acknowledgement the calling thread desires
293          * only a subset of rights therefore we have to act a little
294          * different here.
295          */
296         case AccessCheckRegular:
297         {
298             /* Cache the remaining access rights to be addressed */
299             AccessRights->RemainingAccessRights = RemainingAccess;
300 
301             /* Loop over the DACL to retrieve ACEs */
302             for (AceIndex = 0; AceIndex < Dacl->AceCount; AceIndex++)
303             {
304                 /* Obtain a ACE now */
305                 Status = RtlGetAce(Dacl, AceIndex, (PVOID*)&CurrentAce);
306 
307                 /* Getting this ACE is important, otherwise something is seriously wrong */
308                 ASSERT(NT_SUCCESS(Status));
309 
310                 /*
311                  * Now it's time to analyze it based upon the
312                  * type of this ACE we're being given.
313                  */
314                 if (!(CurrentAce->Header.AceFlags & INHERIT_ONLY_ACE))
315                 {
316                     if (CurrentAce->Header.AceType == ACCESS_DENIED_ACE_TYPE)
317                     {
318                         /* Get the SID from this ACE */
319                         Sid = SepGetSidFromAce(ACCESS_DENIED_ACE_TYPE, CurrentAce);
320 
321                         if (SepSidInTokenEx(AccessToken, PrincipalSelfSid, Sid, TRUE, IsTokenRestricted))
322                         {
323                             /* Get this access right from the ACE */
324                             Access = CurrentAce->AccessMask;
325 
326                             /* Map this access right if it has a generic mask right */
327                             if ((Access & GENERIC_ACCESS) && GenericMapping)
328                             {
329                                 RtlMapGenericMask(&Access, GenericMapping);
330                             }
331 
332                             /*
333                              * The caller requests a right that cannot be
334                              * granted. Access is implicitly denied for
335                              * the calling thread. Track this access right.
336                              */
337                             if (AccessRights->RemainingAccessRights & Access)
338                             {
339                                 DPRINT("SepAnalyzeAcesFromDacl(): Refuted access 0x%08lx\n", Access);
340                                 AccessRights->DeniedAccessRights |= Access;
341                                 break;
342                             }
343                         }
344                     }
345                     else if (CurrentAce->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
346                     {
347                         /* Get the SID from this ACE */
348                         Sid = SepGetSidFromAce(ACCESS_ALLOWED_ACE_TYPE, CurrentAce);
349 
350                         if (SepSidInTokenEx(AccessToken, PrincipalSelfSid, Sid, FALSE, IsTokenRestricted))
351                         {
352                             /* Get this access right from the ACE */
353                             Access = CurrentAce->AccessMask;
354 
355                             /* Map this access right if it has a generic mask right */
356                             if ((Access & GENERIC_ACCESS) && GenericMapping)
357                             {
358                                 RtlMapGenericMask(&Access, GenericMapping);
359                             }
360 
361                             /* Remove granted rights */
362                             DPRINT("SepAnalyzeAcesFromDacl(): RemainingAccessRights 0x%08lx  Access 0x%08lx\n", AccessRights->RemainingAccessRights, Access);
363                             AccessRights->RemainingAccessRights &= ~Access;
364                             DPRINT("SepAnalyzeAcesFromDacl(): RemainingAccessRights 0x%08lx\n", AccessRights->RemainingAccessRights);
365 
366                             /* Track the granted access right */
367                             AccessRights->GrantedAccessRights |= Access;
368                         }
369                     }
370                     else
371                     {
372                         DPRINT1("SepAnalyzeAcesFromDacl(): Unsupported ACE type 0x%lx\n", CurrentAce->Header.AceType);
373                     }
374                 }
375             }
376 
377             /* We're done here */
378             break;
379         }
380 
381         /* We shouldn't reach here */
382         DEFAULT_UNREACHABLE;
383     }
384 
385     /* Return the access rights that we've got */
386     return AccessRights;
387 }
388 
389 /**
390  * @brief
391  * Private function that determines whether security access rights can be given
392  * to the calling thread in order to access an object depending on the security
393  * descriptor and other security context entities, such as an owner. This
394  * function is the heart and brain of the whole access check algorithm in
395  * the kernel.
396  *
397  * @param[in] ClientAccessToken
398  * A pointer to a client (thread) access token that requests access rights
399  * of an object or subset of multiple objects.
400  *
401  * @param[in] PrimaryAccessToken
402  * A pointer to a primary access token that describes the primary security
403  * context of the main calling process.
404  *
405  * @param[in] PrincipalSelfSid
406  * A pointer to a security identifier that represents a security principal,
407  * that is, a user object associated with its security descriptor.
408  *
409  * @param[in] DesiredAccess
410  * The access rights desired by the calling thread to acquire in order to
411  * access an object.
412  *
413  * @param[in] ObjectTypeList
414  * An array list of object types to be checked against for access. The function
415  * will act accordingly in this case by checking each sub-object of an object
416  * of primary level and such. If this parameter is NULL, the function will
417  * perform a normal access check against the target object itself.
418  *
419  * @param[in] ObjectTypeListLength
420  * The length of a object type list. Such length represents the number of
421  * elements in this list.
422  *
423  * @param[in] PreviouslyGrantedAccess
424  * The access rights previously acquired in the past. If this parameter is 0,
425  * it is deemed that the calling thread hasn't acquired any rights. Access checks
426  * are more tighten in this case.
427  *
428  * @param[in] GenericMapping
429  * A pointer to a generic mapping of access rights of the target object.
430  *
431  * @param[in] AccessMode
432  * The processor request level mode.
433  *
434  * @param[in] UseResultList
435  * If set to TRUE, the function will return a list of granted access rights
436  * of each sub-object as well as status code for each. If this parameter is
437  * set to FALSE, then the function will just return only the granted access
438  * rights and status code for single object that's been target for access
439  * checks.
440  *
441  * @param[out] Privileges
442  * A pointer to a definite set of privileges that have been audited
443  * whilst doing access check procedures. Such set of privileges are
444  * optionally returned to the caller. This can be set to NULL if
445  * the caller doesn't want to obtain a set of privileges.
446  *
447  * @param[out] GrantedAccessList
448  * A list of granted access rights returned to the caller. This list
449  * can comprehend multiple elements which represent the sub-objects
450  * that have been checked or a single element which is the target
451  * object itself.
452  *
453  * @param[out] AccessStatusList
454  * A list of access status codes returned to the caller. This list
455  * can comprehend multiple elements which represent the sub-objects
456  * that have been checked or a single element which is the target
457  * object itself.
458  *
459  * @return
460  * Returns TRUE if access onto the specific object is allowed, FALSE
461  * otherwise.
462  */
463 BOOLEAN
464 NTAPI
465 SepAccessCheck(
466     _In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
467     _In_opt_ PACCESS_TOKEN ClientAccessToken,
468     _In_ PACCESS_TOKEN PrimaryAccessToken,
469     _In_opt_ PSID PrincipalSelfSid,
470     _In_ ACCESS_MASK DesiredAccess,
471     _In_opt_ POBJECT_TYPE_LIST ObjectTypeList,
472     _In_ ULONG ObjectTypeListLength,
473     _In_ ACCESS_MASK PreviouslyGrantedAccess,
474     _In_ PGENERIC_MAPPING GenericMapping,
475     _In_ KPROCESSOR_MODE AccessMode,
476     _In_ BOOLEAN UseResultList,
477     _Out_opt_ PPRIVILEGE_SET* Privileges,
478     _Out_ PACCESS_MASK GrantedAccessList,
479     _Out_ PNTSTATUS AccessStatusList)
480 {
481     ACCESS_MASK RemainingAccess;
482     ULONG ResultListLength;
483     ULONG ResultListIndex;
484     PACL Dacl;
485     BOOLEAN Present;
486     BOOLEAN Defaulted;
487     NTSTATUS Status;
488     PACCESS_TOKEN Token = NULL;
489     PACCESS_CHECK_RIGHTS AccessCheckRights = NULL;
490 
491     PAGED_CODE();
492 
493     /* A security descriptor must be expected for access checks */
494     ASSERT(SecurityDescriptor);
495 
496     /* Check for no access desired */
497     if (!DesiredAccess)
498     {
499         /* Check if we had no previous access */
500         if (!PreviouslyGrantedAccess)
501         {
502             /* Then there's nothing to give */
503             DPRINT1("SepAccessCheck(): The caller has no previously granted access gained!\n");
504             Status = STATUS_ACCESS_DENIED;
505             goto ReturnCommonStatus;
506         }
507 
508         /* Return the previous access only */
509         Status = STATUS_SUCCESS;
510         *Privileges = NULL;
511         goto ReturnCommonStatus;
512     }
513 
514     /* Map given accesses */
515     RtlMapGenericMask(&DesiredAccess, GenericMapping);
516     if (PreviouslyGrantedAccess)
517         RtlMapGenericMask(&PreviouslyGrantedAccess, GenericMapping);
518 
519     /* Initialize remaining access rights */
520     RemainingAccess = DesiredAccess;
521 
522     /*
523      * Obtain the token provided by the caller. Client (or also
524      * called impersonation or thread) token takes precedence over
525      * the primary token which is the token associated with the security
526      * context of the main calling process. This is because it is the
527      * client itself that requests access of an object or subset of
528      * multiple objects. Otherwise obtain the security context of the
529      * main process (the actual primary token).
530      */
531     Token = ClientAccessToken ? ClientAccessToken : PrimaryAccessToken;
532 
533     /*
534      * We should at least expect a primary token
535      * to be present if client token is not
536      * available.
537      */
538     ASSERT(Token);
539 
540     /*
541      * Check for ACCESS_SYSTEM_SECURITY and WRITE_OWNER access.
542      * Write down a set of privileges that have been checked
543      * if the caller wants it.
544      */
545     Status = SePrivilegePolicyCheck(&RemainingAccess,
546                                     &PreviouslyGrantedAccess,
547                                     NULL,
548                                     Token,
549                                     Privileges,
550                                     AccessMode);
551     if (!NT_SUCCESS(Status))
552     {
553         goto ReturnCommonStatus;
554     }
555 
556     /* Succeed if there are no more rights to grant */
557     if (RemainingAccess == 0)
558     {
559         Status = STATUS_SUCCESS;
560         goto ReturnCommonStatus;
561     }
562 
563     /* Get the DACL */
564     Status = RtlGetDaclSecurityDescriptor(SecurityDescriptor,
565                                           &Present,
566                                           &Dacl,
567                                           &Defaulted);
568     if (!NT_SUCCESS(Status))
569     {
570         goto ReturnCommonStatus;
571     }
572 
573     /* Grant desired access if the object is unprotected */
574     if (Present == FALSE || Dacl == NULL)
575     {
576         PreviouslyGrantedAccess |= RemainingAccess;
577         if (RemainingAccess & MAXIMUM_ALLOWED)
578         {
579             PreviouslyGrantedAccess &= ~MAXIMUM_ALLOWED;
580             PreviouslyGrantedAccess |= GenericMapping->GenericAll;
581         }
582 
583         Status = STATUS_SUCCESS;
584         goto ReturnCommonStatus;
585     }
586 
587     /* Deny access if the DACL is empty */
588     if (Dacl->AceCount == 0)
589     {
590         if (RemainingAccess == MAXIMUM_ALLOWED && PreviouslyGrantedAccess != 0)
591         {
592             Status = STATUS_SUCCESS;
593         }
594         else
595         {
596             DPRINT1("SepAccessCheck(): The DACL has no ACEs and the caller has no previously granted access!\n");
597             PreviouslyGrantedAccess = 0;
598             Status = STATUS_ACCESS_DENIED;
599         }
600         goto ReturnCommonStatus;
601     }
602 
603     /* Determine the MAXIMUM_ALLOWED access rights according to the DACL */
604     if (DesiredAccess & MAXIMUM_ALLOWED)
605     {
606         /* Perform access checks against ACEs from this DACL */
607         AccessCheckRights = SepAnalyzeAcesFromDacl(AccessCheckMaximum,
608                                                    Dacl,
609                                                    Token,
610                                                    PrimaryAccessToken,
611                                                    FALSE,
612                                                    FALSE,
613                                                    PrincipalSelfSid,
614                                                    GenericMapping,
615                                                    ObjectTypeList,
616                                                    ObjectTypeListLength,
617                                                    0);
618 
619         /*
620          * Getting the access check rights is very
621          * important as we have to do access checks
622          * depending on the kind of rights we get.
623          * Fail prematurely if we can't...
624          */
625         if (!AccessCheckRights)
626         {
627             DPRINT1("SepAccessCheck(): Failed to obtain access check rights!\n");
628             Status = STATUS_INSUFFICIENT_RESOURCES;
629             PreviouslyGrantedAccess = 0;
630             goto ReturnCommonStatus;
631         }
632 
633         /*
634          * Perform further access checks if this token
635          * has restricted SIDs.
636          */
637         if (SeTokenIsRestricted(Token))
638         {
639             AccessCheckRights = SepAnalyzeAcesFromDacl(AccessCheckMaximum,
640                                                        Dacl,
641                                                        Token,
642                                                        PrimaryAccessToken,
643                                                        TRUE,
644                                                        TRUE,
645                                                        PrincipalSelfSid,
646                                                        GenericMapping,
647                                                        ObjectTypeList,
648                                                        ObjectTypeListLength,
649                                                        0);
650         }
651 
652         /* Fail if some rights have not been granted */
653         RemainingAccess &= ~(MAXIMUM_ALLOWED | AccessCheckRights->GrantedAccessRights);
654         if (RemainingAccess != 0)
655         {
656             DPRINT1("SepAccessCheck(): Failed to grant access rights. RemainingAccess = 0x%08lx  DesiredAccess = 0x%08lx\n", RemainingAccess, DesiredAccess);
657             PreviouslyGrantedAccess = 0;
658             Status = STATUS_ACCESS_DENIED;
659             goto ReturnCommonStatus;
660         }
661 
662         /* Set granted access right and access status */
663         PreviouslyGrantedAccess |= AccessCheckRights->GrantedAccessRights;
664         if (PreviouslyGrantedAccess != 0)
665         {
666             Status = STATUS_SUCCESS;
667         }
668         else
669         {
670             DPRINT1("SepAccessCheck(): Failed to grant access rights. PreviouslyGrantedAccess == 0  DesiredAccess = %08lx\n", DesiredAccess);
671             Status = STATUS_ACCESS_DENIED;
672         }
673 
674         /* We have successfully granted all the rights */
675         goto ReturnCommonStatus;
676     }
677 
678     /* Grant rights according to the DACL */
679     AccessCheckRights = SepAnalyzeAcesFromDacl(AccessCheckRegular,
680                                                Dacl,
681                                                Token,
682                                                PrimaryAccessToken,
683                                                FALSE,
684                                                FALSE,
685                                                PrincipalSelfSid,
686                                                GenericMapping,
687                                                ObjectTypeList,
688                                                ObjectTypeListLength,
689                                                RemainingAccess);
690 
691     /*
692      * Getting the access check rights is very
693      * important as we have to do access checks
694      * depending on the kind of rights we get.
695      * Fail prematurely if we can't...
696      */
697     if (!AccessCheckRights)
698     {
699         DPRINT1("SepAccessCheck(): Failed to obtain access check rights!\n");
700         Status = STATUS_INSUFFICIENT_RESOURCES;
701         PreviouslyGrantedAccess = 0;
702         goto ReturnCommonStatus;
703     }
704 
705     /* Fail if some rights have not been granted */
706     if (AccessCheckRights->RemainingAccessRights != 0)
707     {
708         DPRINT1("SepAccessCheck(): Failed to grant access rights. RemainingAccess = 0x%08lx  DesiredAccess = 0x%08lx\n", AccessCheckRights->RemainingAccessRights, DesiredAccess);
709         PreviouslyGrantedAccess = 0;
710         Status = STATUS_ACCESS_DENIED;
711         goto ReturnCommonStatus;
712     }
713 
714     /*
715      * Perform further access checks if this token
716      * has restricted SIDs.
717      */
718     if (SeTokenIsRestricted(Token))
719     {
720         AccessCheckRights = SepAnalyzeAcesFromDacl(AccessCheckRegular,
721                                                    Dacl,
722                                                    Token,
723                                                    PrimaryAccessToken,
724                                                    TRUE,
725                                                    TRUE,
726                                                    PrincipalSelfSid,
727                                                    GenericMapping,
728                                                    ObjectTypeList,
729                                                    ObjectTypeListLength,
730                                                    RemainingAccess);
731 
732         /* Fail if some rights have not been granted */
733         if (AccessCheckRights->RemainingAccessRights != 0)
734         {
735             DPRINT1("SepAccessCheck(): Failed to grant access rights. RemainingAccess = 0x%08lx  DesiredAccess = 0x%08lx\n", AccessCheckRights->RemainingAccessRights, DesiredAccess);
736             PreviouslyGrantedAccess = 0;
737             Status = STATUS_ACCESS_DENIED;
738             goto ReturnCommonStatus;
739         }
740     }
741 
742     /* Set granted access rights */
743     PreviouslyGrantedAccess |= DesiredAccess;
744 
745     /* Fail if no rights have been granted */
746     if (PreviouslyGrantedAccess == 0)
747     {
748         DPRINT1("SepAccessCheck(): Failed to grant access rights. PreviouslyGrantedAccess == 0  DesiredAccess = %08lx\n", DesiredAccess);
749         Status = STATUS_ACCESS_DENIED;
750         goto ReturnCommonStatus;
751     }
752 
753     /*
754      * If we're here then we granted all the desired
755      * access rights the caller wanted.
756      */
757     Status = STATUS_SUCCESS;
758 
759 ReturnCommonStatus:
760     ResultListLength = UseResultList ? ObjectTypeListLength : 1;
761     for (ResultListIndex = 0; ResultListIndex < ResultListLength; ResultListIndex++)
762     {
763         GrantedAccessList[ResultListIndex] = PreviouslyGrantedAccess;
764         AccessStatusList[ResultListIndex] = Status;
765     }
766 
767 #if DBG
768     /* Dump security debug info on access denied case */
769     if (Status == STATUS_ACCESS_DENIED)
770     {
771         SepDumpSdDebugInfo(SecurityDescriptor);
772         SepDumpTokenDebugInfo(Token);
773         SepDumpAccessRightsStats(AccessCheckRights);
774     }
775 #endif
776 
777     /* Free the allocated access check rights */
778     SepFreeAccessCheckRights(AccessCheckRights);
779     AccessCheckRights = NULL;
780 
781     return NT_SUCCESS(Status);
782 }
783 
784 /**
785  * @brief
786  * Retrieves the main user from a security descriptor.
787  *
788  * @param[in] SecurityDescriptor
789  * A valid allocated security descriptor structure where the owner
790  * is to be retrieved.
791  *
792  * @return
793  * Returns a SID that represents the main user (owner).
794  */
795 static PSID
796 SepGetSDOwner(
797     _In_ PSECURITY_DESCRIPTOR _SecurityDescriptor)
798 {
799     PISECURITY_DESCRIPTOR SecurityDescriptor = _SecurityDescriptor;
800     PSID Owner;
801 
802     if (SecurityDescriptor->Control & SE_SELF_RELATIVE)
803         Owner = (PSID)((ULONG_PTR)SecurityDescriptor->Owner +
804                        (ULONG_PTR)SecurityDescriptor);
805     else
806         Owner = (PSID)SecurityDescriptor->Owner;
807 
808     return Owner;
809 }
810 
811 /**
812  * @brief
813  * Retrieves the group from a security descriptor.
814  *
815  * @param[in] SecurityDescriptor
816  * A valid allocated security descriptor structure where the group
817  * is to be retrieved.
818  *
819  * @return
820  * Returns a SID that represents a group.
821  */
822 static PSID
823 SepGetSDGroup(
824     _In_ PSECURITY_DESCRIPTOR _SecurityDescriptor)
825 {
826     PISECURITY_DESCRIPTOR SecurityDescriptor = _SecurityDescriptor;
827     PSID Group;
828 
829     if (SecurityDescriptor->Control & SE_SELF_RELATIVE)
830         Group = (PSID)((ULONG_PTR)SecurityDescriptor->Group +
831                        (ULONG_PTR)SecurityDescriptor);
832     else
833         Group = (PSID)SecurityDescriptor->Group;
834 
835     return Group;
836 }
837 
838 /**
839  * @brief
840  * Retrieves the length size of a set list of privileges structure.
841  *
842  * @param[in] PrivilegeSet
843  * A valid set of privileges.
844  *
845  * @return
846  * Returns the total length of a set of privileges.
847  */
848 static
849 ULONG
850 SepGetPrivilegeSetLength(
851     _In_ PPRIVILEGE_SET PrivilegeSet)
852 {
853     if (PrivilegeSet == NULL)
854         return 0;
855 
856     if (PrivilegeSet->PrivilegeCount == 0)
857         return (ULONG)(sizeof(PRIVILEGE_SET) - sizeof(LUID_AND_ATTRIBUTES));
858 
859     return (ULONG)(sizeof(PRIVILEGE_SET) +
860                    (PrivilegeSet->PrivilegeCount - 1) * sizeof(LUID_AND_ATTRIBUTES));
861 }
862 
863 /* PUBLIC FUNCTIONS ***********************************************************/
864 
865 /**
866  * @brief
867  * Determines whether security access rights can be given to an object
868  * depending on the security descriptor and other security context
869  * entities, such as an owner.
870  *
871  * @param[in] SecurityDescriptor
872  * Security descriptor of the object that is being accessed.
873  *
874  * @param[in] SubjectSecurityContext
875  * The captured subject security context.
876  *
877  * @param[in] SubjectContextLocked
878  * If set to TRUE, the caller acknowledges that the subject context
879  * has already been locked by the caller himself. If set to FALSE,
880  * the function locks the subject context.
881  *
882  * @param[in] DesiredAccess
883  * Access right bitmask that the calling thread wants to acquire.
884  *
885  * @param[in] PreviouslyGrantedAccess
886  * The access rights previously acquired in the past.
887  *
888  * @param[out] Privileges
889  * The returned set of privileges.
890  *
891  * @param[in] GenericMapping
892  * The generic mapping of access rights of an object type.
893  *
894  * @param[in] AccessMode
895  * The processor request level mode.
896  *
897  * @param[out] GrantedAccess
898  * A list of granted access rights.
899  *
900  * @param[out] AccessStatus
901  * The returned status code specifying why access cannot be made
902  * onto an object (if said access is denied in the first place).
903  *
904  * @return
905  * Returns TRUE if access onto the specific object is allowed, FALSE
906  * otherwise.
907  */
908 BOOLEAN
909 NTAPI
910 SeAccessCheck(
911     _In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
912     _In_ PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
913     _In_ BOOLEAN SubjectContextLocked,
914     _In_ ACCESS_MASK DesiredAccess,
915     _In_ ACCESS_MASK PreviouslyGrantedAccess,
916     _Out_ PPRIVILEGE_SET* Privileges,
917     _In_ PGENERIC_MAPPING GenericMapping,
918     _In_ KPROCESSOR_MODE AccessMode,
919     _Out_ PACCESS_MASK GrantedAccess,
920     _Out_ PNTSTATUS AccessStatus)
921 {
922     BOOLEAN ret;
923 
924     PAGED_CODE();
925 
926     /* Check if this is kernel mode */
927     if (AccessMode == KernelMode)
928     {
929         /* Check if kernel wants everything */
930         if (DesiredAccess & MAXIMUM_ALLOWED)
931         {
932             /* Give it */
933             *GrantedAccess = GenericMapping->GenericAll;
934             *GrantedAccess |= (DesiredAccess &~ MAXIMUM_ALLOWED);
935             *GrantedAccess |= PreviouslyGrantedAccess;
936         }
937         else
938         {
939             /* Give the desired and previous access */
940             *GrantedAccess = DesiredAccess | PreviouslyGrantedAccess;
941         }
942 
943         /* Success */
944         *AccessStatus = STATUS_SUCCESS;
945         return TRUE;
946     }
947 
948     /* Check if we didn't get an SD */
949     if (!SecurityDescriptor)
950     {
951         /* Automatic failure */
952         *AccessStatus = STATUS_ACCESS_DENIED;
953         return FALSE;
954     }
955 
956     /* Check for invalid impersonation */
957     if ((SubjectSecurityContext->ClientToken) &&
958         (SubjectSecurityContext->ImpersonationLevel < SecurityImpersonation))
959     {
960         *AccessStatus = STATUS_BAD_IMPERSONATION_LEVEL;
961         return FALSE;
962     }
963 
964     /* Acquire the lock if needed */
965     if (!SubjectContextLocked)
966         SeLockSubjectContext(SubjectSecurityContext);
967 
968     /* Check if the token is the owner and grant WRITE_DAC and READ_CONTROL rights */
969     if (DesiredAccess & (WRITE_DAC | READ_CONTROL | MAXIMUM_ALLOWED))
970     {
971          PACCESS_TOKEN Token = SubjectSecurityContext->ClientToken ?
972              SubjectSecurityContext->ClientToken : SubjectSecurityContext->PrimaryToken;
973 
974         if (SepTokenIsOwner(Token,
975                             SecurityDescriptor,
976                             FALSE))
977         {
978             if (DesiredAccess & MAXIMUM_ALLOWED)
979                 PreviouslyGrantedAccess |= (WRITE_DAC | READ_CONTROL);
980             else
981                 PreviouslyGrantedAccess |= (DesiredAccess & (WRITE_DAC | READ_CONTROL));
982 
983             DesiredAccess &= ~(WRITE_DAC | READ_CONTROL);
984         }
985     }
986 
987     if (DesiredAccess == 0)
988     {
989         *GrantedAccess = PreviouslyGrantedAccess;
990         if (PreviouslyGrantedAccess == 0)
991         {
992             DPRINT1("Request for zero access to an object. Denying.\n");
993             *AccessStatus = STATUS_ACCESS_DENIED;
994             ret = FALSE;
995         }
996         else
997         {
998             *AccessStatus = STATUS_SUCCESS;
999             ret = TRUE;
1000         }
1001     }
1002     else
1003     {
1004         /* Call the internal function */
1005         ret = SepAccessCheck(SecurityDescriptor,
1006                              SubjectSecurityContext->ClientToken,
1007                              SubjectSecurityContext->PrimaryToken,
1008                              NULL,
1009                              DesiredAccess,
1010                              NULL,
1011                              0,
1012                              PreviouslyGrantedAccess,
1013                              GenericMapping,
1014                              AccessMode,
1015                              FALSE,
1016                              Privileges,
1017                              GrantedAccess,
1018                              AccessStatus);
1019     }
1020 
1021     /* Release the lock if needed */
1022     if (!SubjectContextLocked)
1023         SeUnlockSubjectContext(SubjectSecurityContext);
1024 
1025     return ret;
1026 }
1027 
1028 /**
1029  * @brief
1030  * Determines whether security access rights can be given to an object
1031  * depending on the security descriptor. Unlike the regular access check
1032  * procedure in the NT kernel, the fast traverse check is a faster way
1033  * to quickly check if access can be made into an object.
1034  *
1035  * @param[in] SecurityDescriptor
1036  * Security descriptor of the object that is being accessed.
1037  *
1038  * @param[in] AccessState
1039  * An access state to determine if the access token in the current
1040  * security context of the object is an restricted token.
1041  *
1042  * @param[in] DesiredAccess
1043  * The access right bitmask where the calling thread wants to acquire.
1044  *
1045  * @param[in] AccessMode
1046  * Process level request mode.
1047  *
1048  * @return
1049  * Returns TRUE if access onto the specific object is allowed, FALSE
1050  * otherwise.
1051  */
1052 BOOLEAN
1053 NTAPI
1054 SeFastTraverseCheck(
1055     _In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
1056     _In_ PACCESS_STATE AccessState,
1057     _In_ ACCESS_MASK DesiredAccess,
1058     _In_ KPROCESSOR_MODE AccessMode)
1059 {
1060     PACL Dacl;
1061     ULONG AceIndex;
1062     PKNOWN_ACE Ace;
1063 
1064     PAGED_CODE();
1065 
1066     ASSERT(AccessMode != KernelMode);
1067 
1068     if (SecurityDescriptor == NULL)
1069         return FALSE;
1070 
1071     /* Get DACL */
1072     Dacl = SepGetDaclFromDescriptor(SecurityDescriptor);
1073     /* If no DACL, grant access */
1074     if (Dacl == NULL)
1075         return TRUE;
1076 
1077     /* No ACE -> Deny */
1078     if (!Dacl->AceCount)
1079         return FALSE;
1080 
1081     /* Can't perform the check on restricted token */
1082     if (AccessState->Flags & TOKEN_IS_RESTRICTED)
1083         return FALSE;
1084 
1085     /* Browse the ACEs */
1086     for (AceIndex = 0, Ace = (PKNOWN_ACE)((ULONG_PTR)Dacl + sizeof(ACL));
1087          AceIndex < Dacl->AceCount;
1088          AceIndex++, Ace = (PKNOWN_ACE)((ULONG_PTR)Ace + Ace->Header.AceSize))
1089     {
1090         if (Ace->Header.AceFlags & INHERIT_ONLY_ACE)
1091             continue;
1092 
1093         /* If access-allowed ACE */
1094         if (Ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
1095         {
1096             /* Check if all accesses are granted */
1097             if (!(Ace->Mask & DesiredAccess))
1098                 continue;
1099 
1100             /* Check SID and grant access if matching */
1101             if (RtlEqualSid(SeWorldSid, &(Ace->SidStart)))
1102                 return TRUE;
1103         }
1104         /* If access-denied ACE */
1105         else if (Ace->Header.AceType == ACCESS_DENIED_ACE_TYPE)
1106         {
1107             /* Here, only check if it denies any access wanted and deny if so */
1108             if (Ace->Mask & DesiredAccess)
1109                 return FALSE;
1110         }
1111     }
1112 
1113     /* Faulty, deny */
1114     return FALSE;
1115 }
1116 
1117 /* SYSTEM CALLS ***************************************************************/
1118 
1119 /**
1120  * @brief
1121  * Determines whether security access rights can be given to an object
1122  * depending on the security descriptor and a valid handle to an access
1123  * token.
1124  *
1125  * @param[in] SecurityDescriptor
1126  * Security descriptor of the object that is being accessed.
1127  *
1128  * @param[in] TokenHandle
1129  * A handle to a token.
1130  *
1131  * @param[in] DesiredAccess
1132  * The access right bitmask where the calling thread wants to acquire.
1133  *
1134  * @param[in] GenericMapping
1135  * The generic mapping of access rights of an object type.
1136  *
1137  * @param[out] PrivilegeSet
1138  * The returned set of privileges.
1139  *
1140  * @param[in,out] PrivilegeSetLength
1141  * The total length size of a set of privileges.
1142  *
1143  * @param[out] GrantedAccess
1144  * A list of granted access rights.
1145  *
1146  * @param[out] AccessStatus
1147  * The returned status code specifying why access cannot be made
1148  * onto an object (if said access is denied in the first place).
1149  *
1150  * @return
1151  * Returns STATUS_SUCCESS if access check has been done without problems
1152  * and that the object can be accessed. STATUS_GENERIC_NOT_MAPPED is returned
1153  * if no generic access right is mapped. STATUS_NO_IMPERSONATION_TOKEN is returned
1154  * if the token from the handle is not an impersonation token.
1155  * STATUS_BAD_IMPERSONATION_LEVEL is returned if the token cannot be impersonated
1156  * because the current security impersonation level doesn't permit so.
1157  * STATUS_INVALID_SECURITY_DESCR is returned if the security descriptor given
1158  * to the call is not a valid one. STATUS_BUFFER_TOO_SMALL is returned if
1159  * the buffer to the captured privileges has a length that is less than the required
1160  * size of the set of privileges. A failure NTSTATUS code is returned otherwise.
1161  */
1162 NTSTATUS
1163 NTAPI
1164 NtAccessCheck(
1165     _In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
1166     _In_ HANDLE TokenHandle,
1167     _In_ ACCESS_MASK DesiredAccess,
1168     _In_ PGENERIC_MAPPING GenericMapping,
1169     _Out_opt_ PPRIVILEGE_SET PrivilegeSet,
1170     _Inout_ PULONG PrivilegeSetLength,
1171     _Out_ PACCESS_MASK GrantedAccess,
1172     _Out_ PNTSTATUS AccessStatus)
1173 {
1174     PSECURITY_DESCRIPTOR CapturedSecurityDescriptor = NULL;
1175     SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
1176     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1177     ACCESS_MASK PreviouslyGrantedAccess = 0;
1178     PPRIVILEGE_SET Privileges = NULL;
1179     ULONG CapturedPrivilegeSetLength, RequiredPrivilegeSetLength;
1180     PTOKEN Token;
1181     NTSTATUS Status;
1182 
1183     PAGED_CODE();
1184 
1185     /* Check if this is kernel mode */
1186     if (PreviousMode == KernelMode)
1187     {
1188         /* Check if kernel wants everything */
1189         if (DesiredAccess & MAXIMUM_ALLOWED)
1190         {
1191             /* Give it */
1192             *GrantedAccess = GenericMapping->GenericAll;
1193             *GrantedAccess |= (DesiredAccess &~ MAXIMUM_ALLOWED);
1194         }
1195         else
1196         {
1197             /* Just give the desired access */
1198             *GrantedAccess = DesiredAccess;
1199         }
1200 
1201         /* Success */
1202         *AccessStatus = STATUS_SUCCESS;
1203         return STATUS_SUCCESS;
1204     }
1205 
1206     /* Protect probe in SEH */
1207     _SEH2_TRY
1208     {
1209         /* Probe all pointers */
1210         ProbeForRead(GenericMapping, sizeof(GENERIC_MAPPING), sizeof(ULONG));
1211         ProbeForRead(PrivilegeSetLength, sizeof(ULONG), sizeof(ULONG));
1212         ProbeForWrite(PrivilegeSet, *PrivilegeSetLength, sizeof(ULONG));
1213         ProbeForWrite(GrantedAccess, sizeof(ACCESS_MASK), sizeof(ULONG));
1214         ProbeForWrite(AccessStatus, sizeof(NTSTATUS), sizeof(ULONG));
1215 
1216         /* Capture the privilege set length and the mapping */
1217         CapturedPrivilegeSetLength = *PrivilegeSetLength;
1218     }
1219     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1220     {
1221         /* Return the exception code */
1222         _SEH2_YIELD(return _SEH2_GetExceptionCode());
1223     }
1224     _SEH2_END;
1225 
1226     /* Check for unmapped access rights */
1227     if (DesiredAccess & (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL))
1228         return STATUS_GENERIC_NOT_MAPPED;
1229 
1230     /* Reference the token */
1231     Status = ObReferenceObjectByHandle(TokenHandle,
1232                                        TOKEN_QUERY,
1233                                        SeTokenObjectType,
1234                                        PreviousMode,
1235                                        (PVOID*)&Token,
1236                                        NULL);
1237     if (!NT_SUCCESS(Status))
1238     {
1239         DPRINT("Failed to reference token (Status %lx)\n", Status);
1240         return Status;
1241     }
1242 
1243     /* Check token type */
1244     if (Token->TokenType != TokenImpersonation)
1245     {
1246         DPRINT("No impersonation token\n");
1247         ObDereferenceObject(Token);
1248         return STATUS_NO_IMPERSONATION_TOKEN;
1249     }
1250 
1251     /* Check the impersonation level */
1252     if (Token->ImpersonationLevel < SecurityIdentification)
1253     {
1254         DPRINT("Impersonation level < SecurityIdentification\n");
1255         ObDereferenceObject(Token);
1256         return STATUS_BAD_IMPERSONATION_LEVEL;
1257     }
1258 
1259     /* Check for ACCESS_SYSTEM_SECURITY and WRITE_OWNER access */
1260     Status = SePrivilegePolicyCheck(&DesiredAccess,
1261                                     &PreviouslyGrantedAccess,
1262                                     NULL,
1263                                     Token,
1264                                     &Privileges,
1265                                     PreviousMode);
1266     if (!NT_SUCCESS(Status))
1267     {
1268         DPRINT("SePrivilegePolicyCheck failed (Status 0x%08lx)\n", Status);
1269         ObDereferenceObject(Token);
1270         *AccessStatus = Status;
1271         *GrantedAccess = 0;
1272         return STATUS_SUCCESS;
1273     }
1274 
1275     /* Check the size of the privilege set and return the privileges */
1276     if (Privileges != NULL)
1277     {
1278         DPRINT("Privileges != NULL\n");
1279 
1280         /* Calculate the required privilege set buffer size */
1281         RequiredPrivilegeSetLength = SepGetPrivilegeSetLength(Privileges);
1282 
1283         /* Fail if the privilege set buffer is too small */
1284         if (CapturedPrivilegeSetLength < RequiredPrivilegeSetLength)
1285         {
1286             ObDereferenceObject(Token);
1287             SeFreePrivileges(Privileges);
1288             *PrivilegeSetLength = RequiredPrivilegeSetLength;
1289             return STATUS_BUFFER_TOO_SMALL;
1290         }
1291 
1292         /* Copy the privilege set to the caller */
1293         RtlCopyMemory(PrivilegeSet,
1294                       Privileges,
1295                       RequiredPrivilegeSetLength);
1296 
1297         /* Free the local privilege set */
1298         SeFreePrivileges(Privileges);
1299     }
1300     else
1301     {
1302         DPRINT("Privileges == NULL\n");
1303 
1304         /* Fail if the privilege set buffer is too small */
1305         if (CapturedPrivilegeSetLength < sizeof(PRIVILEGE_SET))
1306         {
1307             ObDereferenceObject(Token);
1308             *PrivilegeSetLength = sizeof(PRIVILEGE_SET);
1309             return STATUS_BUFFER_TOO_SMALL;
1310         }
1311 
1312         /* Initialize the privilege set */
1313         PrivilegeSet->PrivilegeCount = 0;
1314         PrivilegeSet->Control = 0;
1315     }
1316 
1317     /* Capture the security descriptor */
1318     Status = SeCaptureSecurityDescriptor(SecurityDescriptor,
1319                                          PreviousMode,
1320                                          PagedPool,
1321                                          FALSE,
1322                                          &CapturedSecurityDescriptor);
1323     if (!NT_SUCCESS(Status))
1324     {
1325         DPRINT("Failed to capture the Security Descriptor\n");
1326         ObDereferenceObject(Token);
1327         return Status;
1328     }
1329 
1330     /* Check the captured security descriptor */
1331     if (CapturedSecurityDescriptor == NULL)
1332     {
1333         DPRINT("Security Descriptor is NULL\n");
1334         ObDereferenceObject(Token);
1335         return STATUS_INVALID_SECURITY_DESCR;
1336     }
1337 
1338     /* Check security descriptor for valid owner and group */
1339     if (SepGetSDOwner(CapturedSecurityDescriptor) == NULL ||
1340         SepGetSDGroup(CapturedSecurityDescriptor) == NULL)
1341     {
1342         DPRINT("Security Descriptor does not have a valid group or owner\n");
1343         SeReleaseSecurityDescriptor(CapturedSecurityDescriptor,
1344                                     PreviousMode,
1345                                     FALSE);
1346         ObDereferenceObject(Token);
1347         return STATUS_INVALID_SECURITY_DESCR;
1348     }
1349 
1350     /* Set up the subject context, and lock it */
1351     SeCaptureSubjectContext(&SubjectSecurityContext);
1352 
1353     /* Lock the token */
1354     SepAcquireTokenLockShared(Token);
1355 
1356     /* Check if the token is the owner and grant WRITE_DAC and READ_CONTROL rights */
1357     if (DesiredAccess & (WRITE_DAC | READ_CONTROL | MAXIMUM_ALLOWED))
1358     {
1359         if (SepTokenIsOwner(Token, CapturedSecurityDescriptor, FALSE))
1360         {
1361             if (DesiredAccess & MAXIMUM_ALLOWED)
1362                 PreviouslyGrantedAccess |= (WRITE_DAC | READ_CONTROL);
1363             else
1364                 PreviouslyGrantedAccess |= (DesiredAccess & (WRITE_DAC | READ_CONTROL));
1365 
1366             DesiredAccess &= ~(WRITE_DAC | READ_CONTROL);
1367         }
1368     }
1369 
1370     if (DesiredAccess == 0)
1371     {
1372         *GrantedAccess = PreviouslyGrantedAccess;
1373         *AccessStatus = STATUS_SUCCESS;
1374     }
1375     else
1376     {
1377         /* Now perform the access check */
1378         SepAccessCheck(CapturedSecurityDescriptor,
1379                        Token,
1380                        &SubjectSecurityContext.PrimaryToken,
1381                        NULL,
1382                        DesiredAccess,
1383                        NULL,
1384                        0,
1385                        PreviouslyGrantedAccess,
1386                        GenericMapping,
1387                        PreviousMode,
1388                        FALSE,
1389                        NULL,
1390                        GrantedAccess,
1391                        AccessStatus);
1392     }
1393 
1394     /* Release subject context and unlock the token */
1395     SeReleaseSubjectContext(&SubjectSecurityContext);
1396     SepReleaseTokenLock(Token);
1397 
1398     /* Release the captured security descriptor */
1399     SeReleaseSecurityDescriptor(CapturedSecurityDescriptor,
1400                                 PreviousMode,
1401                                 FALSE);
1402 
1403     /* Dereference the token */
1404     ObDereferenceObject(Token);
1405 
1406     /* Check succeeded */
1407     return STATUS_SUCCESS;
1408 }
1409 
1410 /**
1411  * @brief
1412  * Determines whether security access could be granted or not on
1413  * an object by the requestor who wants such access through type.
1414  *
1415  * @param[in] SecurityDescriptor
1416  * A security descriptor with information data for auditing.
1417  *
1418  * @param[in] PrincipalSelfSid
1419  * A principal self user SID.
1420  *
1421  * @param[in] ClientToken
1422  * A client access token.
1423  *
1424  * @param[in] DesiredAccess
1425  * The desired access masks rights requested by the caller.
1426  *
1427  * @param[in] ObjectTypeList
1428  * A list of object types.
1429  *
1430  * @param[in] ObjectTypeLength
1431  * The length size of the list.
1432  *
1433  * @param[in] GenericMapping
1434  * The generic mapping list of access masks rights.
1435  *
1436  * @param[in] PrivilegeSet
1437  * An array set of privileges.
1438  *
1439  * @param[in,out] PrivilegeSetLength
1440  * The length size of the array set of privileges.
1441  *
1442  * @param[out] GrantedAccess
1443  * The returned granted access rights.
1444  *
1445  * @param[out] AccessStatus
1446  * The returned NTSTATUS code indicating the final results
1447  * of auditing.
1448  *
1449  * @return
1450  * To be added...
1451  */
1452 NTSTATUS
1453 NTAPI
1454 NtAccessCheckByType(
1455     _In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
1456     _In_ PSID PrincipalSelfSid,
1457     _In_ HANDLE ClientToken,
1458     _In_ ACCESS_MASK DesiredAccess,
1459     _In_ POBJECT_TYPE_LIST ObjectTypeList,
1460     _In_ ULONG ObjectTypeLength,
1461     _In_ PGENERIC_MAPPING GenericMapping,
1462     _In_ PPRIVILEGE_SET PrivilegeSet,
1463     _Inout_ PULONG PrivilegeSetLength,
1464     _Out_ PACCESS_MASK GrantedAccess,
1465     _Out_ PNTSTATUS AccessStatus)
1466 {
1467     UNIMPLEMENTED;
1468     return STATUS_NOT_IMPLEMENTED;
1469 }
1470 
1471 /**
1472  * @brief
1473  * Determines whether security access could be granted or not on
1474  * an object by the requestor who wants such access through
1475  * type list.
1476  *
1477  * @param[in] SecurityDescriptor
1478  * A security descriptor with information data for auditing.
1479  *
1480  * @param[in] PrincipalSelfSid
1481  * A principal self user SID.
1482  *
1483  * @param[in] ClientToken
1484  * A client access token.
1485  *
1486  * @param[in] DesiredAccess
1487  * The desired access masks rights requested by the caller.
1488  *
1489  * @param[in] ObjectTypeList
1490  * A list of object types.
1491  *
1492  * @param[in] ObjectTypeLength
1493  * The length size of the list.
1494  *
1495  * @param[in] GenericMapping
1496  * The generic mapping list of access masks rights.
1497  *
1498  * @param[in] PrivilegeSet
1499  * An array set of privileges.
1500  *
1501  * @param[in,out] PrivilegeSetLength
1502  * The length size of the array set of privileges.
1503  *
1504  * @param[out] GrantedAccess
1505  * The returned granted access rights.
1506  *
1507  * @param[out] AccessStatus
1508  * The returned NTSTATUS code indicating the final results
1509  * of auditing.
1510  *
1511  * @return
1512  * To be added...
1513  */
1514 NTSTATUS
1515 NTAPI
1516 NtAccessCheckByTypeResultList(
1517     _In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
1518     _In_ PSID PrincipalSelfSid,
1519     _In_ HANDLE ClientToken,
1520     _In_ ACCESS_MASK DesiredAccess,
1521     _In_ POBJECT_TYPE_LIST ObjectTypeList,
1522     _In_ ULONG ObjectTypeLength,
1523     _In_ PGENERIC_MAPPING GenericMapping,
1524     _In_ PPRIVILEGE_SET PrivilegeSet,
1525     _Inout_ PULONG PrivilegeSetLength,
1526     _Out_ PACCESS_MASK GrantedAccess,
1527     _Out_ PNTSTATUS AccessStatus)
1528 {
1529     UNIMPLEMENTED;
1530     return STATUS_NOT_IMPLEMENTED;
1531 }
1532 
1533 /* EOF */
1534