xref: /reactos/ntoskrnl/se/accesschk.c (revision 5cadc268)
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     PACCESS_CHECK_RIGHTS AccessCheckRights;
483     PACCESS_TOKEN Token;
484     ULONG ResultListLength;
485     ULONG ResultListIndex;
486     PACL Dacl;
487     BOOLEAN Present;
488     BOOLEAN Defaulted;
489     NTSTATUS Status;
490 
491     PAGED_CODE();
492 
493     /* A security descriptor must be expected for access checks */
494     ASSERT(SecurityDescriptor);
495 
496     /* Assume no access check rights first */
497     AccessCheckRights = NULL;
498 
499     /* Check for no access desired */
500     if (!DesiredAccess)
501     {
502         /* Check if we had no previous access */
503         if (!PreviouslyGrantedAccess)
504         {
505             /* Then there's nothing to give */
506             DPRINT1("SepAccessCheck(): The caller has no previously granted access gained!\n");
507             Status = STATUS_ACCESS_DENIED;
508             goto ReturnCommonStatus;
509         }
510 
511         /* Return the previous access only */
512         Status = STATUS_SUCCESS;
513         *Privileges = NULL;
514         goto ReturnCommonStatus;
515     }
516 
517     /* Map given accesses */
518     RtlMapGenericMask(&DesiredAccess, GenericMapping);
519     if (PreviouslyGrantedAccess)
520         RtlMapGenericMask(&PreviouslyGrantedAccess, GenericMapping);
521 
522     /* Initialize remaining access rights */
523     RemainingAccess = DesiredAccess;
524 
525     /*
526      * Obtain the token provided by the caller. Client (or also
527      * called impersonation or thread) token takes precedence over
528      * the primary token which is the token associated with the security
529      * context of the main calling process. This is because it is the
530      * client itself that requests access of an object or subset of
531      * multiple objects. Otherwise obtain the security context of the
532      * main process (the actual primary token).
533      */
534     Token = ClientAccessToken ? ClientAccessToken : PrimaryAccessToken;
535 
536     /*
537      * We should at least expect a primary token
538      * to be present if client token is not
539      * available.
540      */
541     ASSERT(Token);
542 
543     /*
544      * Check for ACCESS_SYSTEM_SECURITY and WRITE_OWNER access.
545      * Write down a set of privileges that have been checked
546      * if the caller wants it.
547      */
548     Status = SePrivilegePolicyCheck(&RemainingAccess,
549                                     &PreviouslyGrantedAccess,
550                                     NULL,
551                                     Token,
552                                     Privileges,
553                                     AccessMode);
554     if (!NT_SUCCESS(Status))
555     {
556         goto ReturnCommonStatus;
557     }
558 
559     /* Succeed if there are no more rights to grant */
560     if (RemainingAccess == 0)
561     {
562         Status = STATUS_SUCCESS;
563         goto ReturnCommonStatus;
564     }
565 
566     /* Get the DACL */
567     Status = RtlGetDaclSecurityDescriptor(SecurityDescriptor,
568                                           &Present,
569                                           &Dacl,
570                                           &Defaulted);
571     if (!NT_SUCCESS(Status))
572     {
573         goto ReturnCommonStatus;
574     }
575 
576     /* Grant desired access if the object is unprotected */
577     if (Present == FALSE || Dacl == NULL)
578     {
579         PreviouslyGrantedAccess |= RemainingAccess;
580         if (RemainingAccess & MAXIMUM_ALLOWED)
581         {
582             PreviouslyGrantedAccess &= ~MAXIMUM_ALLOWED;
583             PreviouslyGrantedAccess |= GenericMapping->GenericAll;
584         }
585 
586         Status = STATUS_SUCCESS;
587         goto ReturnCommonStatus;
588     }
589 
590     /* Deny access if the DACL is empty */
591     if (Dacl->AceCount == 0)
592     {
593         if (RemainingAccess == MAXIMUM_ALLOWED && PreviouslyGrantedAccess != 0)
594         {
595             Status = STATUS_SUCCESS;
596         }
597         else
598         {
599             DPRINT1("SepAccessCheck(): The DACL has no ACEs and the caller has no previously granted access!\n");
600             PreviouslyGrantedAccess = 0;
601             Status = STATUS_ACCESS_DENIED;
602         }
603         goto ReturnCommonStatus;
604     }
605 
606     /* Determine the MAXIMUM_ALLOWED access rights according to the DACL */
607     if (DesiredAccess & MAXIMUM_ALLOWED)
608     {
609         /* Perform access checks against ACEs from this DACL */
610         AccessCheckRights = SepAnalyzeAcesFromDacl(AccessCheckMaximum,
611                                                    Dacl,
612                                                    Token,
613                                                    PrimaryAccessToken,
614                                                    FALSE,
615                                                    FALSE,
616                                                    PrincipalSelfSid,
617                                                    GenericMapping,
618                                                    ObjectTypeList,
619                                                    ObjectTypeListLength,
620                                                    0);
621 
622         /*
623          * Getting the access check rights is very
624          * important as we have to do access checks
625          * depending on the kind of rights we get.
626          * Fail prematurely if we can't...
627          */
628         if (!AccessCheckRights)
629         {
630             DPRINT1("SepAccessCheck(): Failed to obtain access check rights!\n");
631             Status = STATUS_INSUFFICIENT_RESOURCES;
632             PreviouslyGrantedAccess = 0;
633             goto ReturnCommonStatus;
634         }
635 
636         /*
637          * Perform further access checks if this token
638          * has restricted SIDs.
639          */
640         if (SeTokenIsRestricted(Token))
641         {
642             AccessCheckRights = SepAnalyzeAcesFromDacl(AccessCheckMaximum,
643                                                        Dacl,
644                                                        Token,
645                                                        PrimaryAccessToken,
646                                                        TRUE,
647                                                        TRUE,
648                                                        PrincipalSelfSid,
649                                                        GenericMapping,
650                                                        ObjectTypeList,
651                                                        ObjectTypeListLength,
652                                                        0);
653         }
654 
655         /* Fail if some rights have not been granted */
656         RemainingAccess &= ~(MAXIMUM_ALLOWED | AccessCheckRights->GrantedAccessRights);
657         if (RemainingAccess != 0)
658         {
659             DPRINT1("SepAccessCheck(): Failed to grant access rights. RemainingAccess = 0x%08lx  DesiredAccess = 0x%08lx\n", RemainingAccess, DesiredAccess);
660             PreviouslyGrantedAccess = 0;
661             Status = STATUS_ACCESS_DENIED;
662             goto ReturnCommonStatus;
663         }
664 
665         /* Set granted access right and access status */
666         PreviouslyGrantedAccess |= AccessCheckRights->GrantedAccessRights;
667         if (PreviouslyGrantedAccess != 0)
668         {
669             Status = STATUS_SUCCESS;
670         }
671         else
672         {
673             DPRINT1("SepAccessCheck(): Failed to grant access rights. PreviouslyGrantedAccess == 0  DesiredAccess = %08lx\n", DesiredAccess);
674             Status = STATUS_ACCESS_DENIED;
675         }
676 
677         /* We have successfully granted all the rights */
678         goto ReturnCommonStatus;
679     }
680 
681     /* Grant rights according to the DACL */
682     AccessCheckRights = SepAnalyzeAcesFromDacl(AccessCheckRegular,
683                                                Dacl,
684                                                Token,
685                                                PrimaryAccessToken,
686                                                FALSE,
687                                                FALSE,
688                                                PrincipalSelfSid,
689                                                GenericMapping,
690                                                ObjectTypeList,
691                                                ObjectTypeListLength,
692                                                RemainingAccess);
693 
694     /*
695      * Getting the access check rights is very
696      * important as we have to do access checks
697      * depending on the kind of rights we get.
698      * Fail prematurely if we can't...
699      */
700     if (!AccessCheckRights)
701     {
702         DPRINT1("SepAccessCheck(): Failed to obtain access check rights!\n");
703         Status = STATUS_INSUFFICIENT_RESOURCES;
704         PreviouslyGrantedAccess = 0;
705         goto ReturnCommonStatus;
706     }
707 
708     /* Fail if some rights have not been granted */
709     if (AccessCheckRights->RemainingAccessRights != 0)
710     {
711         DPRINT1("SepAccessCheck(): Failed to grant access rights. RemainingAccess = 0x%08lx  DesiredAccess = 0x%08lx\n", AccessCheckRights->RemainingAccessRights, DesiredAccess);
712         PreviouslyGrantedAccess = 0;
713         Status = STATUS_ACCESS_DENIED;
714         goto ReturnCommonStatus;
715     }
716 
717     /*
718      * Perform further access checks if this token
719      * has restricted SIDs.
720      */
721     if (SeTokenIsRestricted(Token))
722     {
723         AccessCheckRights = SepAnalyzeAcesFromDacl(AccessCheckRegular,
724                                                    Dacl,
725                                                    Token,
726                                                    PrimaryAccessToken,
727                                                    TRUE,
728                                                    TRUE,
729                                                    PrincipalSelfSid,
730                                                    GenericMapping,
731                                                    ObjectTypeList,
732                                                    ObjectTypeListLength,
733                                                    RemainingAccess);
734 
735         /* Fail if some rights have not been granted */
736         if (AccessCheckRights->RemainingAccessRights != 0)
737         {
738             DPRINT1("SepAccessCheck(): Failed to grant access rights. RemainingAccess = 0x%08lx  DesiredAccess = 0x%08lx\n", AccessCheckRights->RemainingAccessRights, DesiredAccess);
739             PreviouslyGrantedAccess = 0;
740             Status = STATUS_ACCESS_DENIED;
741             goto ReturnCommonStatus;
742         }
743     }
744 
745     /* Set granted access rights */
746     PreviouslyGrantedAccess |= DesiredAccess;
747 
748     /* Fail if no rights have been granted */
749     if (PreviouslyGrantedAccess == 0)
750     {
751         DPRINT1("SepAccessCheck(): Failed to grant access rights. PreviouslyGrantedAccess == 0  DesiredAccess = %08lx\n", DesiredAccess);
752         Status = STATUS_ACCESS_DENIED;
753         goto ReturnCommonStatus;
754     }
755 
756     /*
757      * If we're here then we granted all the desired
758      * access rights the caller wanted.
759      */
760     Status = STATUS_SUCCESS;
761 
762 ReturnCommonStatus:
763     ResultListLength = UseResultList ? ObjectTypeListLength : 1;
764     for (ResultListIndex = 0; ResultListIndex < ResultListLength; ResultListIndex++)
765     {
766         GrantedAccessList[ResultListIndex] = PreviouslyGrantedAccess;
767         AccessStatusList[ResultListIndex] = Status;
768     }
769 
770     /* Free the allocated access check rights */
771     SepFreeAccessCheckRights(AccessCheckRights);
772     AccessCheckRights = NULL;
773 
774     return NT_SUCCESS(Status);
775 }
776 
777 /**
778  * @brief
779  * Retrieves the main user from a security descriptor.
780  *
781  * @param[in] SecurityDescriptor
782  * A valid allocated security descriptor structure where the owner
783  * is to be retrieved.
784  *
785  * @return
786  * Returns a SID that represents the main user (owner).
787  */
788 static PSID
789 SepGetSDOwner(
790     _In_ PSECURITY_DESCRIPTOR _SecurityDescriptor)
791 {
792     PISECURITY_DESCRIPTOR SecurityDescriptor = _SecurityDescriptor;
793     PSID Owner;
794 
795     if (SecurityDescriptor->Control & SE_SELF_RELATIVE)
796         Owner = (PSID)((ULONG_PTR)SecurityDescriptor->Owner +
797                        (ULONG_PTR)SecurityDescriptor);
798     else
799         Owner = (PSID)SecurityDescriptor->Owner;
800 
801     return Owner;
802 }
803 
804 /**
805  * @brief
806  * Retrieves the group from a security descriptor.
807  *
808  * @param[in] SecurityDescriptor
809  * A valid allocated security descriptor structure where the group
810  * is to be retrieved.
811  *
812  * @return
813  * Returns a SID that represents a group.
814  */
815 static PSID
816 SepGetSDGroup(
817     _In_ PSECURITY_DESCRIPTOR _SecurityDescriptor)
818 {
819     PISECURITY_DESCRIPTOR SecurityDescriptor = _SecurityDescriptor;
820     PSID Group;
821 
822     if (SecurityDescriptor->Control & SE_SELF_RELATIVE)
823         Group = (PSID)((ULONG_PTR)SecurityDescriptor->Group +
824                        (ULONG_PTR)SecurityDescriptor);
825     else
826         Group = (PSID)SecurityDescriptor->Group;
827 
828     return Group;
829 }
830 
831 /**
832  * @brief
833  * Retrieves the length size of a set list of privileges structure.
834  *
835  * @param[in] PrivilegeSet
836  * A valid set of privileges.
837  *
838  * @return
839  * Returns the total length of a set of privileges.
840  */
841 static
842 ULONG
843 SepGetPrivilegeSetLength(
844     _In_ PPRIVILEGE_SET PrivilegeSet)
845 {
846     if (PrivilegeSet == NULL)
847         return 0;
848 
849     if (PrivilegeSet->PrivilegeCount == 0)
850         return (ULONG)(sizeof(PRIVILEGE_SET) - sizeof(LUID_AND_ATTRIBUTES));
851 
852     return (ULONG)(sizeof(PRIVILEGE_SET) +
853                    (PrivilegeSet->PrivilegeCount - 1) * sizeof(LUID_AND_ATTRIBUTES));
854 }
855 
856 /* PUBLIC FUNCTIONS ***********************************************************/
857 
858 /**
859  * @brief
860  * Determines whether security access rights can be given to an object
861  * depending on the security descriptor and other security context
862  * entities, such as an owner.
863  *
864  * @param[in] SecurityDescriptor
865  * Security descriptor of the object that is being accessed.
866  *
867  * @param[in] SubjectSecurityContext
868  * The captured subject security context.
869  *
870  * @param[in] SubjectContextLocked
871  * If set to TRUE, the caller acknowledges that the subject context
872  * has already been locked by the caller himself. If set to FALSE,
873  * the function locks the subject context.
874  *
875  * @param[in] DesiredAccess
876  * Access right bitmask that the calling thread wants to acquire.
877  *
878  * @param[in] PreviouslyGrantedAccess
879  * The access rights previously acquired in the past.
880  *
881  * @param[out] Privileges
882  * The returned set of privileges.
883  *
884  * @param[in] GenericMapping
885  * The generic mapping of access rights of an object type.
886  *
887  * @param[in] AccessMode
888  * The processor request level mode.
889  *
890  * @param[out] GrantedAccess
891  * A list of granted access rights.
892  *
893  * @param[out] AccessStatus
894  * The returned status code specifying why access cannot be made
895  * onto an object (if said access is denied in the first place).
896  *
897  * @return
898  * Returns TRUE if access onto the specific object is allowed, FALSE
899  * otherwise.
900  */
901 BOOLEAN
902 NTAPI
903 SeAccessCheck(
904     _In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
905     _In_ PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
906     _In_ BOOLEAN SubjectContextLocked,
907     _In_ ACCESS_MASK DesiredAccess,
908     _In_ ACCESS_MASK PreviouslyGrantedAccess,
909     _Out_ PPRIVILEGE_SET* Privileges,
910     _In_ PGENERIC_MAPPING GenericMapping,
911     _In_ KPROCESSOR_MODE AccessMode,
912     _Out_ PACCESS_MASK GrantedAccess,
913     _Out_ PNTSTATUS AccessStatus)
914 {
915     BOOLEAN ret;
916 
917     PAGED_CODE();
918 
919     /* Check if this is kernel mode */
920     if (AccessMode == KernelMode)
921     {
922         /* Check if kernel wants everything */
923         if (DesiredAccess & MAXIMUM_ALLOWED)
924         {
925             /* Give it */
926             *GrantedAccess = GenericMapping->GenericAll;
927             *GrantedAccess |= (DesiredAccess &~ MAXIMUM_ALLOWED);
928             *GrantedAccess |= PreviouslyGrantedAccess;
929         }
930         else
931         {
932             /* Give the desired and previous access */
933             *GrantedAccess = DesiredAccess | PreviouslyGrantedAccess;
934         }
935 
936         /* Success */
937         *AccessStatus = STATUS_SUCCESS;
938         return TRUE;
939     }
940 
941     /* Check if we didn't get an SD */
942     if (!SecurityDescriptor)
943     {
944         /* Automatic failure */
945         *AccessStatus = STATUS_ACCESS_DENIED;
946         return FALSE;
947     }
948 
949     /* Check for invalid impersonation */
950     if ((SubjectSecurityContext->ClientToken) &&
951         (SubjectSecurityContext->ImpersonationLevel < SecurityImpersonation))
952     {
953         *AccessStatus = STATUS_BAD_IMPERSONATION_LEVEL;
954         return FALSE;
955     }
956 
957     /* Acquire the lock if needed */
958     if (!SubjectContextLocked)
959         SeLockSubjectContext(SubjectSecurityContext);
960 
961     /* Check if the token is the owner and grant WRITE_DAC and READ_CONTROL rights */
962     if (DesiredAccess & (WRITE_DAC | READ_CONTROL | MAXIMUM_ALLOWED))
963     {
964          PACCESS_TOKEN Token = SubjectSecurityContext->ClientToken ?
965              SubjectSecurityContext->ClientToken : SubjectSecurityContext->PrimaryToken;
966 
967         if (SepTokenIsOwner(Token,
968                             SecurityDescriptor,
969                             FALSE))
970         {
971             if (DesiredAccess & MAXIMUM_ALLOWED)
972                 PreviouslyGrantedAccess |= (WRITE_DAC | READ_CONTROL);
973             else
974                 PreviouslyGrantedAccess |= (DesiredAccess & (WRITE_DAC | READ_CONTROL));
975 
976             DesiredAccess &= ~(WRITE_DAC | READ_CONTROL);
977         }
978     }
979 
980     if (DesiredAccess == 0)
981     {
982         *GrantedAccess = PreviouslyGrantedAccess;
983         if (PreviouslyGrantedAccess == 0)
984         {
985             DPRINT1("Request for zero access to an object. Denying.\n");
986             *AccessStatus = STATUS_ACCESS_DENIED;
987             ret = FALSE;
988         }
989         else
990         {
991             *AccessStatus = STATUS_SUCCESS;
992             ret = TRUE;
993         }
994     }
995     else
996     {
997         /* Call the internal function */
998         ret = SepAccessCheck(SecurityDescriptor,
999                              SubjectSecurityContext->ClientToken,
1000                              SubjectSecurityContext->PrimaryToken,
1001                              NULL,
1002                              DesiredAccess,
1003                              NULL,
1004                              0,
1005                              PreviouslyGrantedAccess,
1006                              GenericMapping,
1007                              AccessMode,
1008                              FALSE,
1009                              Privileges,
1010                              GrantedAccess,
1011                              AccessStatus);
1012     }
1013 
1014     /* Release the lock if needed */
1015     if (!SubjectContextLocked)
1016         SeUnlockSubjectContext(SubjectSecurityContext);
1017 
1018     return ret;
1019 }
1020 
1021 /**
1022  * @brief
1023  * Determines whether security access rights can be given to an object
1024  * depending on the security descriptor. Unlike the regular access check
1025  * procedure in the NT kernel, the fast traverse check is a faster way
1026  * to quickly check if access can be made into an object.
1027  *
1028  * @param[in] SecurityDescriptor
1029  * Security descriptor of the object that is being accessed.
1030  *
1031  * @param[in] AccessState
1032  * An access state to determine if the access token in the current
1033  * security context of the object is an restricted token.
1034  *
1035  * @param[in] DesiredAccess
1036  * The access right bitmask where the calling thread wants to acquire.
1037  *
1038  * @param[in] AccessMode
1039  * Process level request mode.
1040  *
1041  * @return
1042  * Returns TRUE if access onto the specific object is allowed, FALSE
1043  * otherwise.
1044  */
1045 BOOLEAN
1046 NTAPI
1047 SeFastTraverseCheck(
1048     _In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
1049     _In_ PACCESS_STATE AccessState,
1050     _In_ ACCESS_MASK DesiredAccess,
1051     _In_ KPROCESSOR_MODE AccessMode)
1052 {
1053     PACL Dacl;
1054     ULONG AceIndex;
1055     PKNOWN_ACE Ace;
1056 
1057     PAGED_CODE();
1058 
1059     ASSERT(AccessMode != KernelMode);
1060 
1061     if (SecurityDescriptor == NULL)
1062         return FALSE;
1063 
1064     /* Get DACL */
1065     Dacl = SepGetDaclFromDescriptor(SecurityDescriptor);
1066     /* If no DACL, grant access */
1067     if (Dacl == NULL)
1068         return TRUE;
1069 
1070     /* No ACE -> Deny */
1071     if (!Dacl->AceCount)
1072         return FALSE;
1073 
1074     /* Can't perform the check on restricted token */
1075     if (AccessState->Flags & TOKEN_IS_RESTRICTED)
1076         return FALSE;
1077 
1078     /* Browse the ACEs */
1079     for (AceIndex = 0, Ace = (PKNOWN_ACE)((ULONG_PTR)Dacl + sizeof(ACL));
1080          AceIndex < Dacl->AceCount;
1081          AceIndex++, Ace = (PKNOWN_ACE)((ULONG_PTR)Ace + Ace->Header.AceSize))
1082     {
1083         if (Ace->Header.AceFlags & INHERIT_ONLY_ACE)
1084             continue;
1085 
1086         /* If access-allowed ACE */
1087         if (Ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
1088         {
1089             /* Check if all accesses are granted */
1090             if (!(Ace->Mask & DesiredAccess))
1091                 continue;
1092 
1093             /* Check SID and grant access if matching */
1094             if (RtlEqualSid(SeWorldSid, &(Ace->SidStart)))
1095                 return TRUE;
1096         }
1097         /* If access-denied ACE */
1098         else if (Ace->Header.AceType == ACCESS_DENIED_ACE_TYPE)
1099         {
1100             /* Here, only check if it denies any access wanted and deny if so */
1101             if (Ace->Mask & DesiredAccess)
1102                 return FALSE;
1103         }
1104     }
1105 
1106     /* Faulty, deny */
1107     return FALSE;
1108 }
1109 
1110 /* SYSTEM CALLS ***************************************************************/
1111 
1112 /**
1113  * @brief
1114  * Determines whether security access rights can be given to an object
1115  * depending on the security descriptor and a valid handle to an access
1116  * token.
1117  *
1118  * @param[in] SecurityDescriptor
1119  * Security descriptor of the object that is being accessed.
1120  *
1121  * @param[in] TokenHandle
1122  * A handle to a token.
1123  *
1124  * @param[in] DesiredAccess
1125  * The access right bitmask where the calling thread wants to acquire.
1126  *
1127  * @param[in] GenericMapping
1128  * The generic mapping of access rights of an object type.
1129  *
1130  * @param[out] PrivilegeSet
1131  * The returned set of privileges.
1132  *
1133  * @param[in,out] PrivilegeSetLength
1134  * The total length size of a set of privileges.
1135  *
1136  * @param[out] GrantedAccess
1137  * A list of granted access rights.
1138  *
1139  * @param[out] AccessStatus
1140  * The returned status code specifying why access cannot be made
1141  * onto an object (if said access is denied in the first place).
1142  *
1143  * @return
1144  * Returns STATUS_SUCCESS if access check has been done without problems
1145  * and that the object can be accessed. STATUS_GENERIC_NOT_MAPPED is returned
1146  * if no generic access right is mapped. STATUS_NO_IMPERSONATION_TOKEN is returned
1147  * if the token from the handle is not an impersonation token.
1148  * STATUS_BAD_IMPERSONATION_LEVEL is returned if the token cannot be impersonated
1149  * because the current security impersonation level doesn't permit so.
1150  * STATUS_INVALID_SECURITY_DESCR is returned if the security descriptor given
1151  * to the call is not a valid one. STATUS_BUFFER_TOO_SMALL is returned if
1152  * the buffer to the captured privileges has a length that is less than the required
1153  * size of the set of privileges. A failure NTSTATUS code is returned otherwise.
1154  */
1155 NTSTATUS
1156 NTAPI
1157 NtAccessCheck(
1158     _In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
1159     _In_ HANDLE TokenHandle,
1160     _In_ ACCESS_MASK DesiredAccess,
1161     _In_ PGENERIC_MAPPING GenericMapping,
1162     _Out_opt_ PPRIVILEGE_SET PrivilegeSet,
1163     _Inout_ PULONG PrivilegeSetLength,
1164     _Out_ PACCESS_MASK GrantedAccess,
1165     _Out_ PNTSTATUS AccessStatus)
1166 {
1167     PSECURITY_DESCRIPTOR CapturedSecurityDescriptor = NULL;
1168     SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
1169     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1170     ACCESS_MASK PreviouslyGrantedAccess = 0;
1171     PPRIVILEGE_SET Privileges = NULL;
1172     ULONG CapturedPrivilegeSetLength, RequiredPrivilegeSetLength;
1173     PTOKEN Token;
1174     NTSTATUS Status;
1175 
1176     PAGED_CODE();
1177 
1178     /* Check if this is kernel mode */
1179     if (PreviousMode == KernelMode)
1180     {
1181         /* Check if kernel wants everything */
1182         if (DesiredAccess & MAXIMUM_ALLOWED)
1183         {
1184             /* Give it */
1185             *GrantedAccess = GenericMapping->GenericAll;
1186             *GrantedAccess |= (DesiredAccess &~ MAXIMUM_ALLOWED);
1187         }
1188         else
1189         {
1190             /* Just give the desired access */
1191             *GrantedAccess = DesiredAccess;
1192         }
1193 
1194         /* Success */
1195         *AccessStatus = STATUS_SUCCESS;
1196         return STATUS_SUCCESS;
1197     }
1198 
1199     /* Protect probe in SEH */
1200     _SEH2_TRY
1201     {
1202         /* Probe all pointers */
1203         ProbeForRead(GenericMapping, sizeof(GENERIC_MAPPING), sizeof(ULONG));
1204         ProbeForRead(PrivilegeSetLength, sizeof(ULONG), sizeof(ULONG));
1205         ProbeForWrite(PrivilegeSet, *PrivilegeSetLength, sizeof(ULONG));
1206         ProbeForWrite(GrantedAccess, sizeof(ACCESS_MASK), sizeof(ULONG));
1207         ProbeForWrite(AccessStatus, sizeof(NTSTATUS), sizeof(ULONG));
1208 
1209         /* Capture the privilege set length and the mapping */
1210         CapturedPrivilegeSetLength = *PrivilegeSetLength;
1211     }
1212     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1213     {
1214         /* Return the exception code */
1215         _SEH2_YIELD(return _SEH2_GetExceptionCode());
1216     }
1217     _SEH2_END;
1218 
1219     /* Check for unmapped access rights */
1220     if (DesiredAccess & (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL))
1221         return STATUS_GENERIC_NOT_MAPPED;
1222 
1223     /* Reference the token */
1224     Status = ObReferenceObjectByHandle(TokenHandle,
1225                                        TOKEN_QUERY,
1226                                        SeTokenObjectType,
1227                                        PreviousMode,
1228                                        (PVOID*)&Token,
1229                                        NULL);
1230     if (!NT_SUCCESS(Status))
1231     {
1232         DPRINT("Failed to reference token (Status %lx)\n", Status);
1233         return Status;
1234     }
1235 
1236     /* Check token type */
1237     if (Token->TokenType != TokenImpersonation)
1238     {
1239         DPRINT("No impersonation token\n");
1240         ObDereferenceObject(Token);
1241         return STATUS_NO_IMPERSONATION_TOKEN;
1242     }
1243 
1244     /* Check the impersonation level */
1245     if (Token->ImpersonationLevel < SecurityIdentification)
1246     {
1247         DPRINT("Impersonation level < SecurityIdentification\n");
1248         ObDereferenceObject(Token);
1249         return STATUS_BAD_IMPERSONATION_LEVEL;
1250     }
1251 
1252     /* Check for ACCESS_SYSTEM_SECURITY and WRITE_OWNER access */
1253     Status = SePrivilegePolicyCheck(&DesiredAccess,
1254                                     &PreviouslyGrantedAccess,
1255                                     NULL,
1256                                     Token,
1257                                     &Privileges,
1258                                     PreviousMode);
1259     if (!NT_SUCCESS(Status))
1260     {
1261         DPRINT("SePrivilegePolicyCheck failed (Status 0x%08lx)\n", Status);
1262         ObDereferenceObject(Token);
1263         *AccessStatus = Status;
1264         *GrantedAccess = 0;
1265         return STATUS_SUCCESS;
1266     }
1267 
1268     /* Check the size of the privilege set and return the privileges */
1269     if (Privileges != NULL)
1270     {
1271         DPRINT("Privileges != NULL\n");
1272 
1273         /* Calculate the required privilege set buffer size */
1274         RequiredPrivilegeSetLength = SepGetPrivilegeSetLength(Privileges);
1275 
1276         /* Fail if the privilege set buffer is too small */
1277         if (CapturedPrivilegeSetLength < RequiredPrivilegeSetLength)
1278         {
1279             ObDereferenceObject(Token);
1280             SeFreePrivileges(Privileges);
1281             *PrivilegeSetLength = RequiredPrivilegeSetLength;
1282             return STATUS_BUFFER_TOO_SMALL;
1283         }
1284 
1285         /* Copy the privilege set to the caller */
1286         RtlCopyMemory(PrivilegeSet,
1287                       Privileges,
1288                       RequiredPrivilegeSetLength);
1289 
1290         /* Free the local privilege set */
1291         SeFreePrivileges(Privileges);
1292     }
1293     else
1294     {
1295         DPRINT("Privileges == NULL\n");
1296 
1297         /* Fail if the privilege set buffer is too small */
1298         if (CapturedPrivilegeSetLength < sizeof(PRIVILEGE_SET))
1299         {
1300             ObDereferenceObject(Token);
1301             *PrivilegeSetLength = sizeof(PRIVILEGE_SET);
1302             return STATUS_BUFFER_TOO_SMALL;
1303         }
1304 
1305         /* Initialize the privilege set */
1306         PrivilegeSet->PrivilegeCount = 0;
1307         PrivilegeSet->Control = 0;
1308     }
1309 
1310     /* Capture the security descriptor */
1311     Status = SeCaptureSecurityDescriptor(SecurityDescriptor,
1312                                          PreviousMode,
1313                                          PagedPool,
1314                                          FALSE,
1315                                          &CapturedSecurityDescriptor);
1316     if (!NT_SUCCESS(Status))
1317     {
1318         DPRINT("Failed to capture the Security Descriptor\n");
1319         ObDereferenceObject(Token);
1320         return Status;
1321     }
1322 
1323     /* Check the captured security descriptor */
1324     if (CapturedSecurityDescriptor == NULL)
1325     {
1326         DPRINT("Security Descriptor is NULL\n");
1327         ObDereferenceObject(Token);
1328         return STATUS_INVALID_SECURITY_DESCR;
1329     }
1330 
1331     /* Check security descriptor for valid owner and group */
1332     if (SepGetSDOwner(CapturedSecurityDescriptor) == NULL ||
1333         SepGetSDGroup(CapturedSecurityDescriptor) == NULL)
1334     {
1335         DPRINT("Security Descriptor does not have a valid group or owner\n");
1336         SeReleaseSecurityDescriptor(CapturedSecurityDescriptor,
1337                                     PreviousMode,
1338                                     FALSE);
1339         ObDereferenceObject(Token);
1340         return STATUS_INVALID_SECURITY_DESCR;
1341     }
1342 
1343     /* Set up the subject context, and lock it */
1344     SeCaptureSubjectContext(&SubjectSecurityContext);
1345 
1346     /* Lock the token */
1347     SepAcquireTokenLockShared(Token);
1348 
1349     /* Check if the token is the owner and grant WRITE_DAC and READ_CONTROL rights */
1350     if (DesiredAccess & (WRITE_DAC | READ_CONTROL | MAXIMUM_ALLOWED))
1351     {
1352         if (SepTokenIsOwner(Token, CapturedSecurityDescriptor, FALSE))
1353         {
1354             if (DesiredAccess & MAXIMUM_ALLOWED)
1355                 PreviouslyGrantedAccess |= (WRITE_DAC | READ_CONTROL);
1356             else
1357                 PreviouslyGrantedAccess |= (DesiredAccess & (WRITE_DAC | READ_CONTROL));
1358 
1359             DesiredAccess &= ~(WRITE_DAC | READ_CONTROL);
1360         }
1361     }
1362 
1363     if (DesiredAccess == 0)
1364     {
1365         *GrantedAccess = PreviouslyGrantedAccess;
1366         *AccessStatus = STATUS_SUCCESS;
1367     }
1368     else
1369     {
1370         /* Now perform the access check */
1371         SepAccessCheck(CapturedSecurityDescriptor,
1372                        Token,
1373                        &SubjectSecurityContext.PrimaryToken,
1374                        NULL,
1375                        DesiredAccess,
1376                        NULL,
1377                        0,
1378                        PreviouslyGrantedAccess,
1379                        GenericMapping,
1380                        PreviousMode,
1381                        FALSE,
1382                        NULL,
1383                        GrantedAccess,
1384                        AccessStatus);
1385     }
1386 
1387     /* Release subject context and unlock the token */
1388     SeReleaseSubjectContext(&SubjectSecurityContext);
1389     SepReleaseTokenLock(Token);
1390 
1391     /* Release the captured security descriptor */
1392     SeReleaseSecurityDescriptor(CapturedSecurityDescriptor,
1393                                 PreviousMode,
1394                                 FALSE);
1395 
1396     /* Dereference the token */
1397     ObDereferenceObject(Token);
1398 
1399     /* Check succeeded */
1400     return STATUS_SUCCESS;
1401 }
1402 
1403 /**
1404  * @brief
1405  * Determines whether security access could be granted or not on
1406  * an object by the requestor who wants such access through type.
1407  *
1408  * @param[in] SecurityDescriptor
1409  * A security descriptor with information data for auditing.
1410  *
1411  * @param[in] PrincipalSelfSid
1412  * A principal self user SID.
1413  *
1414  * @param[in] ClientToken
1415  * A client access token.
1416  *
1417  * @param[in] DesiredAccess
1418  * The desired access masks rights requested by the caller.
1419  *
1420  * @param[in] ObjectTypeList
1421  * A list of object types.
1422  *
1423  * @param[in] ObjectTypeLength
1424  * The length size of the list.
1425  *
1426  * @param[in] GenericMapping
1427  * The generic mapping list of access masks rights.
1428  *
1429  * @param[in] PrivilegeSet
1430  * An array set of privileges.
1431  *
1432  * @param[in,out] PrivilegeSetLength
1433  * The length size of the array set of privileges.
1434  *
1435  * @param[out] GrantedAccess
1436  * The returned granted access rights.
1437  *
1438  * @param[out] AccessStatus
1439  * The returned NTSTATUS code indicating the final results
1440  * of auditing.
1441  *
1442  * @return
1443  * To be added...
1444  */
1445 NTSTATUS
1446 NTAPI
1447 NtAccessCheckByType(
1448     _In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
1449     _In_ PSID PrincipalSelfSid,
1450     _In_ HANDLE ClientToken,
1451     _In_ ACCESS_MASK DesiredAccess,
1452     _In_ POBJECT_TYPE_LIST ObjectTypeList,
1453     _In_ ULONG ObjectTypeLength,
1454     _In_ PGENERIC_MAPPING GenericMapping,
1455     _In_ PPRIVILEGE_SET PrivilegeSet,
1456     _Inout_ PULONG PrivilegeSetLength,
1457     _Out_ PACCESS_MASK GrantedAccess,
1458     _Out_ PNTSTATUS AccessStatus)
1459 {
1460     UNIMPLEMENTED;
1461     return STATUS_NOT_IMPLEMENTED;
1462 }
1463 
1464 /**
1465  * @brief
1466  * Determines whether security access could be granted or not on
1467  * an object by the requestor who wants such access through
1468  * type list.
1469  *
1470  * @param[in] SecurityDescriptor
1471  * A security descriptor with information data for auditing.
1472  *
1473  * @param[in] PrincipalSelfSid
1474  * A principal self user SID.
1475  *
1476  * @param[in] ClientToken
1477  * A client access token.
1478  *
1479  * @param[in] DesiredAccess
1480  * The desired access masks rights requested by the caller.
1481  *
1482  * @param[in] ObjectTypeList
1483  * A list of object types.
1484  *
1485  * @param[in] ObjectTypeLength
1486  * The length size of the list.
1487  *
1488  * @param[in] GenericMapping
1489  * The generic mapping list of access masks rights.
1490  *
1491  * @param[in] PrivilegeSet
1492  * An array set of privileges.
1493  *
1494  * @param[in,out] PrivilegeSetLength
1495  * The length size of the array set of privileges.
1496  *
1497  * @param[out] GrantedAccess
1498  * The returned granted access rights.
1499  *
1500  * @param[out] AccessStatus
1501  * The returned NTSTATUS code indicating the final results
1502  * of auditing.
1503  *
1504  * @return
1505  * To be added...
1506  */
1507 NTSTATUS
1508 NTAPI
1509 NtAccessCheckByTypeResultList(
1510     _In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
1511     _In_ PSID PrincipalSelfSid,
1512     _In_ HANDLE ClientToken,
1513     _In_ ACCESS_MASK DesiredAccess,
1514     _In_ POBJECT_TYPE_LIST ObjectTypeList,
1515     _In_ ULONG ObjectTypeLength,
1516     _In_ PGENERIC_MAPPING GenericMapping,
1517     _In_ PPRIVILEGE_SET PrivilegeSet,
1518     _Inout_ PULONG PrivilegeSetLength,
1519     _Out_ PACCESS_MASK GrantedAccess,
1520     _Out_ PNTSTATUS AccessStatus)
1521 {
1522     UNIMPLEMENTED;
1523     return STATUS_NOT_IMPLEMENTED;
1524 }
1525 
1526 /* EOF */
1527