2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/se/accesschk.c
5 * PURPOSE: Security manager
7 * PROGRAMMERS: No programmer listed.
10 /* INCLUDES *******************************************************************/
16 /* GLOBALS ********************************************************************/
19 /* PRIVATE FUNCTIONS **********************************************************/
21 #define OLD_ACCESS_CHECK
24 SepAccessCheckEx(IN PSECURITY_DESCRIPTOR SecurityDescriptor
,
25 IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext
,
26 IN ACCESS_MASK DesiredAccess
,
27 IN POBJECT_TYPE_LIST ObjectTypeList
,
28 IN ULONG ObjectTypeListLength
,
29 IN ACCESS_MASK PreviouslyGrantedAccess
,
30 OUT PPRIVILEGE_SET
* Privileges
,
31 IN PGENERIC_MAPPING GenericMapping
,
32 IN KPROCESSOR_MODE AccessMode
,
33 OUT PACCESS_MASK GrantedAccessList
,
34 OUT PNTSTATUS AccessStatusList
,
35 IN BOOLEAN UseResultList
)
37 ACCESS_MASK RemainingAccess
;
38 ACCESS_MASK TempAccess
;
39 ACCESS_MASK TempGrantedAccess
= 0;
40 ACCESS_MASK TempDeniedAccess
= 0;
42 ULONG i
, ResultListLength
;
51 /* Check for no access desired */
54 /* Check if we had no previous access */
55 if (!PreviouslyGrantedAccess
)
57 /* Then there's nothing to give */
58 Status
= STATUS_ACCESS_DENIED
;
59 goto ReturnCommonStatus
;
62 /* Return the previous access only */
63 Status
= STATUS_SUCCESS
;
65 goto ReturnCommonStatus
;
68 /* Map given accesses */
69 RtlMapGenericMask(&DesiredAccess
, GenericMapping
);
70 if (PreviouslyGrantedAccess
)
71 RtlMapGenericMask(&PreviouslyGrantedAccess
, GenericMapping
);
73 /* Initialize remaining access rights */
74 RemainingAccess
= DesiredAccess
;
76 Token
= SubjectSecurityContext
->ClientToken
?
77 SubjectSecurityContext
->ClientToken
: SubjectSecurityContext
->PrimaryToken
;
79 /* Check for ACCESS_SYSTEM_SECURITY and WRITE_OWNER access */
80 Status
= SePrivilegePolicyCheck(&RemainingAccess
,
81 &PreviouslyGrantedAccess
,
86 if (!NT_SUCCESS(Status
))
88 goto ReturnCommonStatus
;
91 /* Succeed if there are no more rights to grant */
92 if (RemainingAccess
== 0)
94 Status
= STATUS_SUCCESS
;
95 goto ReturnCommonStatus
;
99 Status
= RtlGetDaclSecurityDescriptor(SecurityDescriptor
,
103 if (!NT_SUCCESS(Status
))
105 goto ReturnCommonStatus
;
108 /* RULE 1: Grant desired access if the object is unprotected */
109 if (Present
== FALSE
|| Dacl
== NULL
)
111 PreviouslyGrantedAccess
|= RemainingAccess
;
112 if (RemainingAccess
& MAXIMUM_ALLOWED
)
114 PreviouslyGrantedAccess
&= ~MAXIMUM_ALLOWED
;
115 PreviouslyGrantedAccess
|= GenericMapping
->GenericAll
;
118 Status
= STATUS_SUCCESS
;
119 goto ReturnCommonStatus
;
122 /* Deny access if the DACL is empty */
123 if (Dacl
->AceCount
== 0)
125 if (RemainingAccess
== MAXIMUM_ALLOWED
&& PreviouslyGrantedAccess
!= 0)
127 Status
= STATUS_SUCCESS
;
131 PreviouslyGrantedAccess
= 0;
132 Status
= STATUS_ACCESS_DENIED
;
134 goto ReturnCommonStatus
;
137 /* Determine the MAXIMUM_ALLOWED access rights according to the DACL */
138 if (DesiredAccess
& MAXIMUM_ALLOWED
)
140 CurrentAce
= (PACE
)(Dacl
+ 1);
141 for (i
= 0; i
< Dacl
->AceCount
; i
++)
143 if (!(CurrentAce
->Header
.AceFlags
& INHERIT_ONLY_ACE
))
145 Sid
= (PSID
)(CurrentAce
+ 1);
146 if (CurrentAce
->Header
.AceType
== ACCESS_DENIED_ACE_TYPE
)
148 if (SepSidInToken(Token
, Sid
))
150 /* Map access rights from the ACE */
151 TempAccess
= CurrentAce
->AccessMask
;
152 RtlMapGenericMask(&TempAccess
, GenericMapping
);
154 /* Deny access rights that have not been granted yet */
155 TempDeniedAccess
|= (TempAccess
& ~TempGrantedAccess
);
158 else if (CurrentAce
->Header
.AceType
== ACCESS_ALLOWED_ACE_TYPE
)
160 if (SepSidInToken(Token
, Sid
))
162 /* Map access rights from the ACE */
163 TempAccess
= CurrentAce
->AccessMask
;
164 RtlMapGenericMask(&TempAccess
, GenericMapping
);
166 /* Grant access rights that have not been denied yet */
167 TempGrantedAccess
|= (TempAccess
& ~TempDeniedAccess
);
172 DPRINT1("Unsupported ACE type 0x%lx\n", CurrentAce
->Header
.AceType
);
176 /* Get the next ACE */
177 CurrentAce
= (PACE
)((ULONG_PTR
)CurrentAce
+ CurrentAce
->Header
.AceSize
);
180 /* Fail if some rights have not been granted */
181 RemainingAccess
&= ~(MAXIMUM_ALLOWED
| TempGrantedAccess
);
182 if (RemainingAccess
!= 0)
184 PreviouslyGrantedAccess
= 0;
185 Status
= STATUS_ACCESS_DENIED
;
186 goto ReturnCommonStatus
;
189 /* Set granted access right and access status */
190 PreviouslyGrantedAccess
|= TempGrantedAccess
;
191 if (PreviouslyGrantedAccess
!= 0)
193 Status
= STATUS_SUCCESS
;
197 Status
= STATUS_ACCESS_DENIED
;
199 goto ReturnCommonStatus
;
202 /* RULE 4: Grant rights according to the DACL */
203 CurrentAce
= (PACE
)(Dacl
+ 1);
204 for (i
= 0; i
< Dacl
->AceCount
; i
++)
206 if (!(CurrentAce
->Header
.AceFlags
& INHERIT_ONLY_ACE
))
208 Sid
= (PSID
)(CurrentAce
+ 1);
209 if (CurrentAce
->Header
.AceType
== ACCESS_DENIED_ACE_TYPE
)
211 if (SepSidInToken(Token
, Sid
))
213 #ifdef OLD_ACCESS_CHECK
214 PreviouslyGrantedAccess
= 0;
215 Status
= STATUS_ACCESS_DENIED
;
216 goto ReturnCommonStatus
;
218 /* Map access rights from the ACE */
219 TempAccess
= CurrentAce
->AccessMask
;
220 RtlMapGenericMask(&TempAccess
, GenericMapping
);
222 /* Leave if a remaining right must be denied */
223 if (RemainingAccess
& TempAccess
)
228 else if (CurrentAce
->Header
.AceType
== ACCESS_ALLOWED_ACE_TYPE
)
230 if (SepSidInToken(Token
, Sid
))
232 #ifdef OLD_ACCESS_CHECK
233 TempAccess
= CurrentAce
->AccessMask
;
234 RtlMapGenericMask(&TempAccess
, GenericMapping
);
235 PreviouslyGrantedAccess
|= TempAccess
;
237 /* Map access rights from the ACE */
238 TempAccess
= CurrentAce
->AccessMask
;
239 RtlMapGenericMask(&TempAccess
, GenericMapping
);
241 /* Remove granted rights */
242 RemainingAccess
&= ~TempAccess
;
248 DPRINT1("Unsupported ACE type 0x%lx\n", CurrentAce
->Header
.AceType
);
252 /* Get the next ACE */
253 CurrentAce
= (PACE
)((ULONG_PTR
)CurrentAce
+ CurrentAce
->Header
.AceSize
);
256 #ifdef OLD_ACCESS_CHECK
257 DPRINT("PreviouslyGrantedAccess %08lx\n DesiredAccess %08lx\n",
258 PreviouslyGrantedAccess
, DesiredAccess
);
260 PreviouslyGrantedAccess
&= DesiredAccess
;
262 if ((PreviouslyGrantedAccess
& ~VALID_INHERIT_FLAGS
) ==
263 (DesiredAccess
& ~VALID_INHERIT_FLAGS
))
265 Status
= STATUS_SUCCESS
;
266 goto ReturnCommonStatus
;
270 DPRINT1("HACK: Should deny access for caller: granted 0x%lx, desired 0x%lx (generic mapping %p).\n",
271 PreviouslyGrantedAccess
, DesiredAccess
, GenericMapping
);
272 //*AccessStatus = STATUS_ACCESS_DENIED;
274 PreviouslyGrantedAccess
= DesiredAccess
;
275 Status
= STATUS_SUCCESS
;
276 goto ReturnCommonStatus
;
279 DPRINT("DesiredAccess %08lx\nPreviouslyGrantedAccess %08lx\nRemainingAccess %08lx\n",
280 DesiredAccess
, PreviouslyGrantedAccess
, RemainingAccess
);
282 /* Fail if some rights have not been granted */
283 if (RemainingAccess
!= 0)
286 Status
= STATUS_ACCESS_DENIED
;
287 goto ReturnCommonStatus
;
290 /* Set granted access rights */
291 PreviouslyGrantedAccess
|= DesiredAccess
;
293 DPRINT("GrantedAccess %08lx\n", *GrantedAccess
);
295 /* Fail if no rights have been granted */
296 if (PreviouslyGrantedAccess
== 0)
298 Status
= STATUS_ACCESS_DENIED
;
299 goto ReturnCommonStatus
;
302 Status
= STATUS_SUCCESS
;
303 goto ReturnCommonStatus
;
308 ResultListLength
= UseResultList
? ObjectTypeListLength
: 1;
309 for (i
= 0; i
< ResultListLength
; i
++)
311 GrantedAccessList
[i
] = PreviouslyGrantedAccess
;
312 AccessStatusList
[i
] = Status
;
315 return NT_SUCCESS(Status
);
319 SepAccessCheck(IN PSECURITY_DESCRIPTOR SecurityDescriptor
,
320 IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext
,
321 IN ACCESS_MASK DesiredAccess
,
322 IN ACCESS_MASK PreviouslyGrantedAccess
,
323 OUT PPRIVILEGE_SET
* Privileges
,
324 IN PGENERIC_MAPPING GenericMapping
,
325 IN KPROCESSOR_MODE AccessMode
,
326 OUT PACCESS_MASK GrantedAccess
,
327 OUT PNTSTATUS AccessStatus
)
329 return SepAccessCheckEx(SecurityDescriptor
,
330 SubjectSecurityContext
,
334 PreviouslyGrantedAccess
,
344 SepGetSDOwner(IN PSECURITY_DESCRIPTOR _SecurityDescriptor
)
346 PISECURITY_DESCRIPTOR SecurityDescriptor
= _SecurityDescriptor
;
349 if (SecurityDescriptor
->Control
& SE_SELF_RELATIVE
)
350 Owner
= (PSID
)((ULONG_PTR
)SecurityDescriptor
->Owner
+
351 (ULONG_PTR
)SecurityDescriptor
);
353 Owner
= (PSID
)SecurityDescriptor
->Owner
;
359 SepGetSDGroup(IN PSECURITY_DESCRIPTOR _SecurityDescriptor
)
361 PISECURITY_DESCRIPTOR SecurityDescriptor
= _SecurityDescriptor
;
364 if (SecurityDescriptor
->Control
& SE_SELF_RELATIVE
)
365 Group
= (PSID
)((ULONG_PTR
)SecurityDescriptor
->Group
+
366 (ULONG_PTR
)SecurityDescriptor
);
368 Group
= (PSID
)SecurityDescriptor
->Group
;
374 /* PUBLIC FUNCTIONS ***********************************************************/
381 SeAccessCheck(IN PSECURITY_DESCRIPTOR SecurityDescriptor
,
382 IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext
,
383 IN BOOLEAN SubjectContextLocked
,
384 IN ACCESS_MASK DesiredAccess
,
385 IN ACCESS_MASK PreviouslyGrantedAccess
,
386 OUT PPRIVILEGE_SET
* Privileges
,
387 IN PGENERIC_MAPPING GenericMapping
,
388 IN KPROCESSOR_MODE AccessMode
,
389 OUT PACCESS_MASK GrantedAccess
,
390 OUT PNTSTATUS AccessStatus
)
396 /* Check if this is kernel mode */
397 if (AccessMode
== KernelMode
)
399 /* Check if kernel wants everything */
400 if (DesiredAccess
& MAXIMUM_ALLOWED
)
403 *GrantedAccess
= GenericMapping
->GenericAll
;
404 *GrantedAccess
|= (DesiredAccess
&~ MAXIMUM_ALLOWED
);
405 *GrantedAccess
|= PreviouslyGrantedAccess
;
409 /* Give the desired and previous access */
410 *GrantedAccess
= DesiredAccess
| PreviouslyGrantedAccess
;
414 *AccessStatus
= STATUS_SUCCESS
;
418 /* Check if we didn't get an SD */
419 if (!SecurityDescriptor
)
421 /* Automatic failure */
422 *AccessStatus
= STATUS_ACCESS_DENIED
;
426 /* Check for invalid impersonation */
427 if ((SubjectSecurityContext
->ClientToken
) &&
428 (SubjectSecurityContext
->ImpersonationLevel
< SecurityImpersonation
))
430 *AccessStatus
= STATUS_BAD_IMPERSONATION_LEVEL
;
434 /* Acquire the lock if needed */
435 if (!SubjectContextLocked
)
436 SeLockSubjectContext(SubjectSecurityContext
);
438 /* Check if the token is the owner and grant WRITE_DAC and READ_CONTROL rights */
439 if (DesiredAccess
& (WRITE_DAC
| READ_CONTROL
| MAXIMUM_ALLOWED
))
441 PACCESS_TOKEN Token
= SubjectSecurityContext
->ClientToken
?
442 SubjectSecurityContext
->ClientToken
: SubjectSecurityContext
->PrimaryToken
;
444 if (SepTokenIsOwner(Token
,
448 if (DesiredAccess
& MAXIMUM_ALLOWED
)
449 PreviouslyGrantedAccess
|= (WRITE_DAC
| READ_CONTROL
);
451 PreviouslyGrantedAccess
|= (DesiredAccess
& (WRITE_DAC
| READ_CONTROL
));
453 DesiredAccess
&= ~(WRITE_DAC
| READ_CONTROL
);
457 if (DesiredAccess
== 0)
459 *GrantedAccess
= PreviouslyGrantedAccess
;
460 *AccessStatus
= STATUS_SUCCESS
;
465 /* Call the internal function */
466 ret
= SepAccessCheck(SecurityDescriptor
,
467 SubjectSecurityContext
,
469 PreviouslyGrantedAccess
,
477 /* Release the lock if needed */
478 if (!SubjectContextLocked
)
479 SeUnlockSubjectContext(SubjectSecurityContext
);
489 SeFastTraverseCheck(IN PSECURITY_DESCRIPTOR SecurityDescriptor
,
490 IN PACCESS_STATE AccessState
,
491 IN ACCESS_MASK DesiredAccess
,
492 IN KPROCESSOR_MODE AccessMode
)
500 NT_ASSERT(AccessMode
!= KernelMode
);
502 if (SecurityDescriptor
== NULL
)
506 Dacl
= SepGetDaclFromDescriptor(SecurityDescriptor
);
507 /* If no DACL, grant access */
515 /* Can't perform the check on restricted token */
516 if (AccessState
->Flags
& TOKEN_IS_RESTRICTED
)
519 /* Browse the ACEs */
520 for (AceIndex
= 0, Ace
= (PKNOWN_ACE
)((ULONG_PTR
)Dacl
+ sizeof(ACL
));
521 AceIndex
< Dacl
->AceCount
;
522 AceIndex
++, Ace
= (PKNOWN_ACE
)((ULONG_PTR
)Ace
+ Ace
->Header
.AceSize
))
524 if (Ace
->Header
.AceFlags
& INHERIT_ONLY_ACE
)
527 /* If access-allowed ACE */
528 if (Ace
->Header
.AceType
& ACCESS_ALLOWED_ACE_TYPE
)
530 /* Check if all accesses are granted */
531 if (!(Ace
->Mask
& DesiredAccess
))
534 /* Check SID and grant access if matching */
535 if (RtlEqualSid(SeWorldSid
, &(Ace
->SidStart
)))
538 /* If access-denied ACE */
539 else if (Ace
->Header
.AceType
& ACCESS_DENIED_ACE_TYPE
)
541 /* Here, only check if it denies all the access wanted and deny if so */
542 if (Ace
->Mask
& DesiredAccess
)
551 /* SYSTEM CALLS ***************************************************************/
558 NtAccessCheck(IN PSECURITY_DESCRIPTOR SecurityDescriptor
,
559 IN HANDLE TokenHandle
,
560 IN ACCESS_MASK DesiredAccess
,
561 IN PGENERIC_MAPPING GenericMapping
,
562 OUT PPRIVILEGE_SET PrivilegeSet OPTIONAL
,
563 IN OUT PULONG PrivilegeSetLength
,
564 OUT PACCESS_MASK GrantedAccess
,
565 OUT PNTSTATUS AccessStatus
)
567 PSECURITY_DESCRIPTOR CapturedSecurityDescriptor
= NULL
;
568 SECURITY_SUBJECT_CONTEXT SubjectSecurityContext
;
569 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
570 ACCESS_MASK PreviouslyGrantedAccess
= 0;
575 /* Check if this is kernel mode */
576 if (PreviousMode
== KernelMode
)
578 /* Check if kernel wants everything */
579 if (DesiredAccess
& MAXIMUM_ALLOWED
)
582 *GrantedAccess
= GenericMapping
->GenericAll
;
583 *GrantedAccess
|= (DesiredAccess
&~ MAXIMUM_ALLOWED
);
587 /* Just give the desired access */
588 *GrantedAccess
= DesiredAccess
;
592 *AccessStatus
= STATUS_SUCCESS
;
593 return STATUS_SUCCESS
;
596 /* Protect probe in SEH */
599 /* Probe all pointers */
600 ProbeForRead(GenericMapping
, sizeof(GENERIC_MAPPING
), sizeof(ULONG
));
601 ProbeForRead(PrivilegeSetLength
, sizeof(ULONG
), sizeof(ULONG
));
602 ProbeForWrite(PrivilegeSet
, *PrivilegeSetLength
, sizeof(ULONG
));
603 ProbeForWrite(GrantedAccess
, sizeof(ACCESS_MASK
), sizeof(ULONG
));
604 ProbeForWrite(AccessStatus
, sizeof(NTSTATUS
), sizeof(ULONG
));
606 /* Initialize the privilege set */
607 PrivilegeSet
->PrivilegeCount
= 0;
609 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
611 /* Return the exception code */
612 _SEH2_YIELD(return _SEH2_GetExceptionCode());
616 /* Check for unmapped access rights */
617 if (DesiredAccess
& (GENERIC_READ
| GENERIC_WRITE
| GENERIC_EXECUTE
| GENERIC_ALL
))
618 return STATUS_GENERIC_NOT_MAPPED
;
620 /* Reference the token */
621 Status
= ObReferenceObjectByHandle(TokenHandle
,
627 if (!NT_SUCCESS(Status
))
629 DPRINT("Failed to reference token (Status %lx)\n", Status
);
633 /* Check token type */
634 if (Token
->TokenType
!= TokenImpersonation
)
636 DPRINT("No impersonation token\n");
637 ObDereferenceObject(Token
);
638 return STATUS_NO_IMPERSONATION_TOKEN
;
641 /* Check the impersonation level */
642 if (Token
->ImpersonationLevel
< SecurityIdentification
)
644 DPRINT("Impersonation level < SecurityIdentification\n");
645 ObDereferenceObject(Token
);
646 return STATUS_BAD_IMPERSONATION_LEVEL
;
649 /* Capture the security descriptor */
650 Status
= SeCaptureSecurityDescriptor(SecurityDescriptor
,
654 &CapturedSecurityDescriptor
);
655 if (!NT_SUCCESS(Status
))
657 DPRINT("Failed to capture the Security Descriptor\n");
658 ObDereferenceObject(Token
);
662 /* Check the captured security descriptor */
663 if (CapturedSecurityDescriptor
== NULL
)
665 DPRINT("Security Descriptor is NULL\n");
666 ObDereferenceObject(Token
);
667 return STATUS_INVALID_SECURITY_DESCR
;
670 /* Check security descriptor for valid owner and group */
671 if (SepGetSDOwner(SecurityDescriptor
) == NULL
|| // FIXME: use CapturedSecurityDescriptor
672 SepGetSDGroup(SecurityDescriptor
) == NULL
) // FIXME: use CapturedSecurityDescriptor
674 DPRINT("Security Descriptor does not have a valid group or owner\n");
675 SeReleaseSecurityDescriptor(CapturedSecurityDescriptor
,
678 ObDereferenceObject(Token
);
679 return STATUS_INVALID_SECURITY_DESCR
;
682 /* Set up the subject context, and lock it */
683 SeCaptureSubjectContext(&SubjectSecurityContext
);
686 SepAcquireTokenLockShared(Token
);
688 /* Check if the token is the owner and grant WRITE_DAC and READ_CONTROL rights */
689 if (DesiredAccess
& (WRITE_DAC
| READ_CONTROL
| MAXIMUM_ALLOWED
))
691 if (SepTokenIsOwner(Token
, SecurityDescriptor
, FALSE
)) // FIXME: use CapturedSecurityDescriptor
693 if (DesiredAccess
& MAXIMUM_ALLOWED
)
694 PreviouslyGrantedAccess
|= (WRITE_DAC
| READ_CONTROL
);
696 PreviouslyGrantedAccess
|= (DesiredAccess
& (WRITE_DAC
| READ_CONTROL
));
698 DesiredAccess
&= ~(WRITE_DAC
| READ_CONTROL
);
702 if (DesiredAccess
== 0)
704 *GrantedAccess
= PreviouslyGrantedAccess
;
705 *AccessStatus
= STATUS_SUCCESS
;
709 /* Now perform the access check */
710 SepAccessCheck(SecurityDescriptor
, // FIXME: use CapturedSecurityDescriptor
711 &SubjectSecurityContext
,
713 PreviouslyGrantedAccess
,
714 &PrivilegeSet
, //FIXME
721 /* Release subject context and unlock the token */
722 SeReleaseSubjectContext(&SubjectSecurityContext
);
723 SepReleaseTokenLock(Token
);
725 /* Release the captured security descriptor */
726 SeReleaseSecurityDescriptor(CapturedSecurityDescriptor
,
730 /* Dereference the token */
731 ObDereferenceObject(Token
);
733 /* Check succeeded */
734 return STATUS_SUCCESS
;
740 NtAccessCheckByType(IN PSECURITY_DESCRIPTOR SecurityDescriptor
,
741 IN PSID PrincipalSelfSid
,
742 IN HANDLE ClientToken
,
743 IN ACCESS_MASK DesiredAccess
,
744 IN POBJECT_TYPE_LIST ObjectTypeList
,
745 IN ULONG ObjectTypeLength
,
746 IN PGENERIC_MAPPING GenericMapping
,
747 IN PPRIVILEGE_SET PrivilegeSet
,
748 IN OUT PULONG PrivilegeSetLength
,
749 OUT PACCESS_MASK GrantedAccess
,
750 OUT PNTSTATUS AccessStatus
)
753 return STATUS_NOT_IMPLEMENTED
;
758 NtAccessCheckByTypeResultList(IN PSECURITY_DESCRIPTOR SecurityDescriptor
,
759 IN PSID PrincipalSelfSid
,
760 IN HANDLE ClientToken
,
761 IN ACCESS_MASK DesiredAccess
,
762 IN POBJECT_TYPE_LIST ObjectTypeList
,
763 IN ULONG ObjectTypeLength
,
764 IN PGENERIC_MAPPING GenericMapping
,
765 IN PPRIVILEGE_SET PrivilegeSet
,
766 IN OUT PULONG PrivilegeSetLength
,
767 OUT PACCESS_MASK GrantedAccess
,
768 OUT PNTSTATUS AccessStatus
)
771 return STATUS_NOT_IMPLEMENTED
;