xref: /reactos/ntoskrnl/se/access.c (revision 8a978a17)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS kernel
4  * FILE:            ntoskrnl/se/access.c
5  * PURPOSE:         Access state functions
6  *
7  * PROGRAMMERS:     Alex Ionescu (alex@relsoft.net) -
8  *                               Based on patch by Javier M. Mellid
9  */
10 
11 /* INCLUDES *******************************************************************/
12 
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16 
17 /* GLOBALS ********************************************************************/
18 
19 ERESOURCE SepSubjectContextLock;
20 
21 /* PRIVATE FUNCTIONS **********************************************************/
22 
23 BOOLEAN
24 NTAPI
25 SepSidInTokenEx(IN PACCESS_TOKEN _Token,
26                 IN PSID PrincipalSelfSid,
27                 IN PSID _Sid,
28                 IN BOOLEAN Deny,
29                 IN BOOLEAN Restricted)
30 {
31     ULONG i;
32     PTOKEN Token = (PTOKEN)_Token;
33     PISID TokenSid, Sid = (PISID)_Sid;
34     PSID_AND_ATTRIBUTES SidAndAttributes;
35     ULONG SidCount, SidLength;
36     USHORT SidMetadata;
37     PAGED_CODE();
38 
39     /* Not yet supported */
40     ASSERT(PrincipalSelfSid == NULL);
41     ASSERT(Restricted == FALSE);
42 
43     /* Check if a principal SID was given, and this is our current SID already */
44     if ((PrincipalSelfSid) && (RtlEqualSid(SePrincipalSelfSid, Sid)))
45     {
46         /* Just use the principal SID in this case */
47         Sid = PrincipalSelfSid;
48     }
49 
50     /* Check if this is a restricted token or not */
51     if (Restricted)
52     {
53         /* Use the restricted SIDs and count */
54         SidAndAttributes = Token->RestrictedSids;
55         SidCount = Token->RestrictedSidCount;
56     }
57     else
58     {
59         /* Use the normal SIDs and count */
60         SidAndAttributes = Token->UserAndGroups;
61         SidCount = Token->UserAndGroupCount;
62     }
63 
64     /* Do checks here by hand instead of the usual 4 function calls */
65     SidLength = FIELD_OFFSET(SID,
66                              SubAuthority[Sid->SubAuthorityCount]);
67     SidMetadata = *(PUSHORT)&Sid->Revision;
68 
69     /* Loop every SID */
70     for (i = 0; i < SidCount; i++)
71     {
72         TokenSid = (PISID)SidAndAttributes->Sid;
73 #if SE_SID_DEBUG
74         UNICODE_STRING sidString;
75         RtlConvertSidToUnicodeString(&sidString, TokenSid, TRUE);
76         DPRINT1("SID in Token: %wZ\n", &sidString);
77         RtlFreeUnicodeString(&sidString);
78 #endif
79         /* Check if the SID metadata matches */
80         if (*(PUSHORT)&TokenSid->Revision == SidMetadata)
81         {
82             /* Check if the SID data matches */
83             if (RtlEqualMemory(Sid, TokenSid, SidLength))
84             {
85                 /* Check if the group is enabled, or used for deny only */
86                 if ((!(i) && !(SidAndAttributes->Attributes & SE_GROUP_USE_FOR_DENY_ONLY)) ||
87                     (SidAndAttributes->Attributes & SE_GROUP_ENABLED) ||
88                     ((Deny) && (SidAndAttributes->Attributes & SE_GROUP_USE_FOR_DENY_ONLY)))
89                 {
90                     /* SID is present */
91                     return TRUE;
92                 }
93                 else
94                 {
95                     /* SID is not present */
96                     return FALSE;
97                 }
98             }
99         }
100 
101         /* Move to the next SID */
102         SidAndAttributes++;
103     }
104 
105     /* SID is not present */
106     return FALSE;
107 }
108 
109 BOOLEAN
110 NTAPI
111 SepSidInToken(IN PACCESS_TOKEN _Token,
112               IN PSID Sid)
113 {
114     /* Call extended API */
115     return SepSidInTokenEx(_Token, NULL, Sid, FALSE, FALSE);
116 }
117 
118 BOOLEAN
119 NTAPI
120 SepTokenIsOwner(IN PACCESS_TOKEN _Token,
121                 IN PSECURITY_DESCRIPTOR SecurityDescriptor,
122                 IN BOOLEAN TokenLocked)
123 {
124     PSID Sid;
125     BOOLEAN Result;
126     PTOKEN Token = _Token;
127 
128     /* Get the owner SID */
129     Sid = SepGetOwnerFromDescriptor(SecurityDescriptor);
130     ASSERT(Sid != NULL);
131 
132     /* Lock the token if needed */
133     if (!TokenLocked) SepAcquireTokenLockShared(Token);
134 
135     /* Check if the owner SID is found, handling restricted case as well */
136     Result = SepSidInToken(Token, Sid);
137     if ((Result) && (Token->TokenFlags & TOKEN_IS_RESTRICTED))
138     {
139         Result = SepSidInTokenEx(Token, NULL, Sid, FALSE, TRUE);
140     }
141 
142     /* Release the lock if we had acquired it */
143     if (!TokenLocked) SepReleaseTokenLock(Token);
144 
145     /* Return the result */
146     return Result;
147 }
148 
149 VOID
150 NTAPI
151 SeGetTokenControlInformation(IN PACCESS_TOKEN _Token,
152                              OUT PTOKEN_CONTROL TokenControl)
153 {
154     PTOKEN Token = _Token;
155     PAGED_CODE();
156 
157     /* Capture the main fields */
158     TokenControl->AuthenticationId = Token->AuthenticationId;
159     TokenControl->TokenId = Token->TokenId;
160     TokenControl->TokenSource = Token->TokenSource;
161 
162     /* Lock the token */
163     SepAcquireTokenLockShared(Token);
164 
165     /* Capture the modified ID */
166     TokenControl->ModifiedId = Token->ModifiedId;
167 
168     /* Unlock it */
169     SepReleaseTokenLock(Token);
170 }
171 
172 NTSTATUS
173 NTAPI
174 SepCreateClientSecurity(IN PACCESS_TOKEN Token,
175                         IN PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos,
176                         IN BOOLEAN ServerIsRemote,
177                         IN TOKEN_TYPE TokenType,
178                         IN BOOLEAN ThreadEffectiveOnly,
179                         IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
180                         OUT PSECURITY_CLIENT_CONTEXT ClientContext)
181 {
182     NTSTATUS Status;
183     PACCESS_TOKEN NewToken;
184     PAGED_CODE();
185 
186     /* Check for bogus impersonation level */
187     if (!VALID_IMPERSONATION_LEVEL(ClientSecurityQos->ImpersonationLevel))
188     {
189         /* Fail the call */
190         return STATUS_INVALID_PARAMETER;
191     }
192 
193     /* Check what kind of token this is */
194     if (TokenType != TokenImpersonation)
195     {
196         /* On a primary token, if we do direct access, copy the flag from the QOS */
197         ClientContext->DirectAccessEffectiveOnly = ClientSecurityQos->EffectiveOnly;
198     }
199     else
200     {
201         /* This is an impersonation token, is the level ok? */
202         if (ClientSecurityQos->ImpersonationLevel > ImpersonationLevel)
203         {
204             /* Nope, fail */
205             return STATUS_BAD_IMPERSONATION_LEVEL;
206         }
207 
208         /* Is the level too low, or are we doing something other than delegation remotely */
209         if ((ImpersonationLevel == SecurityAnonymous) ||
210             (ImpersonationLevel == SecurityIdentification) ||
211             ((ServerIsRemote) && (ImpersonationLevel != SecurityDelegation)))
212         {
213             /* Fail the call */
214             return STATUS_BAD_IMPERSONATION_LEVEL;
215         }
216 
217         /* Pick either the thread setting or the QOS setting */
218         ClientContext->DirectAccessEffectiveOnly =
219             ((ThreadEffectiveOnly) || (ClientSecurityQos->EffectiveOnly)) ? TRUE : FALSE;
220     }
221 
222     /* Is this static tracking */
223     if (ClientSecurityQos->ContextTrackingMode == SECURITY_STATIC_TRACKING)
224     {
225         /* Do not use direct access and make a copy */
226         ClientContext->DirectlyAccessClientToken = FALSE;
227         Status = SeCopyClientToken(Token,
228                                    ClientSecurityQos->ImpersonationLevel,
229                                    KernelMode,
230                                    &NewToken);
231         if (!NT_SUCCESS(Status))
232             return Status;
233     }
234     else
235     {
236         /* Use direct access and check if this is local */
237         ClientContext->DirectlyAccessClientToken = TRUE;
238         if (ServerIsRemote)
239         {
240             /* We are doing delegation, so make a copy of the control data */
241             SeGetTokenControlInformation(Token,
242                                          &ClientContext->ClientTokenControl);
243         }
244 
245         /* Keep the same token */
246         NewToken = Token;
247     }
248 
249     /* Fill out the context and return success */
250     ClientContext->SecurityQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
251     ClientContext->SecurityQos.ImpersonationLevel = ClientSecurityQos->ImpersonationLevel;
252     ClientContext->SecurityQos.ContextTrackingMode = ClientSecurityQos->ContextTrackingMode;
253     ClientContext->SecurityQos.EffectiveOnly = ClientSecurityQos->EffectiveOnly;
254     ClientContext->ServerIsRemote = ServerIsRemote;
255     ClientContext->ClientToken = NewToken;
256     return STATUS_SUCCESS;
257 }
258 
259 /* PUBLIC FUNCTIONS ***********************************************************/
260 
261 /*
262  * @implemented
263  */
264 VOID
265 NTAPI
266 SeCaptureSubjectContextEx(IN PETHREAD Thread,
267                           IN PEPROCESS Process,
268                           OUT PSECURITY_SUBJECT_CONTEXT SubjectContext)
269 {
270     BOOLEAN CopyOnOpen, EffectiveOnly;
271 
272     PAGED_CODE();
273 
274     /* Save the unique ID */
275     SubjectContext->ProcessAuditId = Process->UniqueProcessId;
276 
277     /* Check if we have a thread */
278     if (!Thread)
279     {
280         /* We don't, so no token */
281         SubjectContext->ClientToken = NULL;
282     }
283     else
284     {
285         /* Get the impersonation token */
286         SubjectContext->ClientToken = PsReferenceImpersonationToken(Thread,
287                                                                     &CopyOnOpen,
288                                                                     &EffectiveOnly,
289                                                                     &SubjectContext->ImpersonationLevel);
290     }
291 
292     /* Get the primary token */
293     SubjectContext->PrimaryToken = PsReferencePrimaryToken(Process);
294 }
295 
296 /*
297  * @implemented
298  */
299 VOID
300 NTAPI
301 SeCaptureSubjectContext(OUT PSECURITY_SUBJECT_CONTEXT SubjectContext)
302 {
303     /* Call the extended API */
304     SeCaptureSubjectContextEx(PsGetCurrentThread(),
305                               PsGetCurrentProcess(),
306                               SubjectContext);
307 }
308 
309 /*
310  * @implemented
311  */
312 VOID
313 NTAPI
314 SeLockSubjectContext(IN PSECURITY_SUBJECT_CONTEXT SubjectContext)
315 {
316     PTOKEN PrimaryToken, ClientToken;
317     PAGED_CODE();
318 
319     /* Read both tokens */
320     PrimaryToken = SubjectContext->PrimaryToken;
321     ClientToken = SubjectContext->ClientToken;
322 
323     /* Always lock the primary */
324     SepAcquireTokenLockShared(PrimaryToken);
325 
326     /* Lock the impersonation one if it's there */
327     if (!ClientToken) return;
328     SepAcquireTokenLockShared(ClientToken);
329 }
330 
331 /*
332  * @implemented
333  */
334 VOID
335 NTAPI
336 SeUnlockSubjectContext(IN PSECURITY_SUBJECT_CONTEXT SubjectContext)
337 {
338     PTOKEN PrimaryToken, ClientToken;
339     PAGED_CODE();
340 
341     /* Read both tokens */
342     PrimaryToken = SubjectContext->PrimaryToken;
343     ClientToken = SubjectContext->ClientToken;
344 
345     /* Unlock the impersonation one if it's there */
346     if (ClientToken)
347     {
348         SepReleaseTokenLock(ClientToken);
349     }
350 
351     /* Always unlock the primary one */
352     SepReleaseTokenLock(PrimaryToken);
353 }
354 
355 /*
356  * @implemented
357  */
358 VOID
359 NTAPI
360 SeReleaseSubjectContext(IN PSECURITY_SUBJECT_CONTEXT SubjectContext)
361 {
362     PAGED_CODE();
363 
364     /* Drop reference on the primary */
365     ObFastDereferenceObject(&PsGetCurrentProcess()->Token, SubjectContext->PrimaryToken);
366     SubjectContext->PrimaryToken = NULL;
367 
368     /* Drop reference on the impersonation, if there was one */
369     PsDereferenceImpersonationToken(SubjectContext->ClientToken);
370     SubjectContext->ClientToken = NULL;
371 }
372 
373 /*
374  * @implemented
375  */
376 NTSTATUS
377 NTAPI
378 SeCreateAccessStateEx(IN PETHREAD Thread,
379                       IN PEPROCESS Process,
380                       IN OUT PACCESS_STATE AccessState,
381                       IN PAUX_ACCESS_DATA AuxData,
382                       IN ACCESS_MASK Access,
383                       IN PGENERIC_MAPPING GenericMapping)
384 {
385     ACCESS_MASK AccessMask = Access;
386     PTOKEN Token;
387     PAGED_CODE();
388 
389     /* Map the Generic Acess to Specific Access if we have a Mapping */
390     if ((Access & GENERIC_ACCESS) && (GenericMapping))
391     {
392         RtlMapGenericMask(&AccessMask, GenericMapping);
393     }
394 
395     /* Initialize the Access State */
396     RtlZeroMemory(AccessState, sizeof(ACCESS_STATE));
397     ASSERT(AccessState->SecurityDescriptor == NULL);
398     ASSERT(AccessState->PrivilegesAllocated == FALSE);
399 
400     /* Initialize and save aux data */
401     RtlZeroMemory(AuxData, sizeof(AUX_ACCESS_DATA));
402     AccessState->AuxData = AuxData;
403 
404     /* Capture the Subject Context */
405     SeCaptureSubjectContextEx(Thread,
406                               Process,
407                               &AccessState->SubjectSecurityContext);
408 
409     /* Set Access State Data */
410     AccessState->RemainingDesiredAccess = AccessMask;
411     AccessState->OriginalDesiredAccess = AccessMask;
412     ExAllocateLocallyUniqueId(&AccessState->OperationID);
413 
414     /* Get the Token to use */
415     Token = SeQuerySubjectContextToken(&AccessState->SubjectSecurityContext);
416 
417     /* Check for Travers Privilege */
418     if (Token->TokenFlags & TOKEN_HAS_TRAVERSE_PRIVILEGE)
419     {
420         /* Preserve the Traverse Privilege */
421         AccessState->Flags = TOKEN_HAS_TRAVERSE_PRIVILEGE;
422     }
423 
424     /* Set the Auxiliary Data */
425     AuxData->PrivilegeSet = (PPRIVILEGE_SET)((ULONG_PTR)AccessState +
426                                              FIELD_OFFSET(ACCESS_STATE,
427                                                           Privileges));
428     if (GenericMapping) AuxData->GenericMapping = *GenericMapping;
429 
430     /* Return Sucess */
431     return STATUS_SUCCESS;
432 }
433 
434 /*
435  * @implemented
436  */
437 NTSTATUS
438 NTAPI
439 SeCreateAccessState(IN OUT PACCESS_STATE AccessState,
440                     IN PAUX_ACCESS_DATA AuxData,
441                     IN ACCESS_MASK Access,
442                     IN PGENERIC_MAPPING GenericMapping)
443 {
444     PAGED_CODE();
445 
446     /* Call the extended API */
447     return SeCreateAccessStateEx(PsGetCurrentThread(),
448                                  PsGetCurrentProcess(),
449                                  AccessState,
450                                  AuxData,
451                                  Access,
452                                  GenericMapping);
453 }
454 
455 /*
456  * @implemented
457  */
458 VOID
459 NTAPI
460 SeDeleteAccessState(IN PACCESS_STATE AccessState)
461 {
462     PAUX_ACCESS_DATA AuxData;
463     PAGED_CODE();
464 
465     /* Get the Auxiliary Data */
466     AuxData = AccessState->AuxData;
467 
468     /* Deallocate Privileges */
469     if (AccessState->PrivilegesAllocated)
470         ExFreePoolWithTag(AuxData->PrivilegeSet, TAG_PRIVILEGE_SET);
471 
472     /* Deallocate Name and Type Name */
473     if (AccessState->ObjectName.Buffer)
474     {
475         ExFreePool(AccessState->ObjectName.Buffer);
476     }
477 
478     if (AccessState->ObjectTypeName.Buffer)
479     {
480         ExFreePool(AccessState->ObjectTypeName.Buffer);
481     }
482 
483     /* Release the Subject Context */
484     SeReleaseSubjectContext(&AccessState->SubjectSecurityContext);
485 }
486 
487 /*
488  * @implemented
489  */
490 VOID
491 NTAPI
492 SeSetAccessStateGenericMapping(IN PACCESS_STATE AccessState,
493                                IN PGENERIC_MAPPING GenericMapping)
494 {
495     PAGED_CODE();
496 
497     /* Set the Generic Mapping */
498     ((PAUX_ACCESS_DATA)AccessState->AuxData)->GenericMapping = *GenericMapping;
499 }
500 
501 /*
502  * @implemented
503  */
504 NTSTATUS
505 NTAPI
506 SeCreateClientSecurity(IN PETHREAD Thread,
507                        IN PSECURITY_QUALITY_OF_SERVICE Qos,
508                        IN BOOLEAN RemoteClient,
509                        OUT PSECURITY_CLIENT_CONTEXT ClientContext)
510 {
511     TOKEN_TYPE TokenType;
512     BOOLEAN ThreadEffectiveOnly;
513     SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
514     PACCESS_TOKEN Token;
515     NTSTATUS Status;
516     PAGED_CODE();
517 
518     /* Reference the correct token */
519     Token = PsReferenceEffectiveToken(Thread,
520                                       &TokenType,
521                                       &ThreadEffectiveOnly,
522                                       &ImpersonationLevel);
523 
524     /* Create client security from it */
525     Status = SepCreateClientSecurity(Token,
526                                      Qos,
527                                      RemoteClient,
528                                      TokenType,
529                                      ThreadEffectiveOnly,
530                                      ImpersonationLevel,
531                                      ClientContext);
532 
533     /* Check if we failed or static tracking was used */
534     if (!(NT_SUCCESS(Status)) || (Qos->ContextTrackingMode == SECURITY_STATIC_TRACKING))
535     {
536         /* Dereference our copy since it's not being used */
537         ObDereferenceObject(Token);
538     }
539 
540     /* Return status */
541     return Status;
542 }
543 
544 /*
545  * @implemented
546  */
547 NTSTATUS
548 NTAPI
549 SeCreateClientSecurityFromSubjectContext(IN PSECURITY_SUBJECT_CONTEXT SubjectContext,
550                                          IN PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos,
551                                          IN BOOLEAN ServerIsRemote,
552                                          OUT PSECURITY_CLIENT_CONTEXT ClientContext)
553 {
554     PACCESS_TOKEN Token;
555     NTSTATUS Status;
556     PAGED_CODE();
557 
558     /* Get the right token and reference it */
559     Token = SeQuerySubjectContextToken(SubjectContext);
560     ObReferenceObject(Token);
561 
562     /* Create the context */
563     Status = SepCreateClientSecurity(Token,
564                                      ClientSecurityQos,
565                                      ServerIsRemote,
566                                      SubjectContext->ClientToken ?
567                                      TokenImpersonation : TokenPrimary,
568                                      FALSE,
569                                      SubjectContext->ImpersonationLevel,
570                                      ClientContext);
571 
572     /* Check if we failed or static tracking was used */
573     if (!(NT_SUCCESS(Status)) ||
574         (ClientSecurityQos->ContextTrackingMode == SECURITY_STATIC_TRACKING))
575     {
576         /* Dereference our copy since it's not being used */
577         ObDereferenceObject(Token);
578     }
579 
580     /* Return status */
581     return Status;
582 }
583 
584 /*
585  * @implemented
586  */
587 NTSTATUS
588 NTAPI
589 SeImpersonateClientEx(IN PSECURITY_CLIENT_CONTEXT ClientContext,
590                       IN PETHREAD ServerThread OPTIONAL)
591 {
592     BOOLEAN EffectiveOnly;
593     PAGED_CODE();
594 
595     /* Check if direct access is requested */
596     if (!ClientContext->DirectlyAccessClientToken)
597     {
598         /* No, so get the flag from QOS */
599         EffectiveOnly = ClientContext->SecurityQos.EffectiveOnly;
600     }
601     else
602     {
603         /* Yes, so see if direct access should be effective only */
604         EffectiveOnly = ClientContext->DirectAccessEffectiveOnly;
605     }
606 
607     /* Use the current thread if one was not passed */
608     if (!ServerThread) ServerThread = PsGetCurrentThread();
609 
610     /* Call the lower layer routine */
611     return PsImpersonateClient(ServerThread,
612                                ClientContext->ClientToken,
613                                TRUE,
614                                EffectiveOnly,
615                                ClientContext->SecurityQos.ImpersonationLevel);
616 }
617 
618 /*
619  * @implemented
620  */
621 VOID
622 NTAPI
623 SeImpersonateClient(IN PSECURITY_CLIENT_CONTEXT ClientContext,
624                     IN PETHREAD ServerThread OPTIONAL)
625 {
626     PAGED_CODE();
627 
628     /* Call the new API */
629     SeImpersonateClientEx(ClientContext, ServerThread);
630 }
631 
632 /* EOF */
633