[NTOS:MM] Assert MmLocateMemoryAreaByAddress return value to satisfy Coverity. CID...
[reactos.git] / ntoskrnl / se / access.c
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 it */
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 = ((ThreadEffectiveOnly) ||
219 (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, ImpersonationLevel, 0, &NewToken);
228 if (!NT_SUCCESS(Status)) return Status;
229 }
230 else
231 {
232 /* Use direct access and check if this is local */
233 ClientContext->DirectlyAccessClientToken = TRUE;
234 if (ServerIsRemote)
235 {
236 /* We are doing delegation, so make a copy of the control data */
237 SeGetTokenControlInformation(Token,
238 &ClientContext->ClientTokenControl);
239 }
240
241 /* Keep the same token */
242 NewToken = Token;
243 }
244
245 /* Fill out the context and return success */
246 ClientContext->SecurityQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
247 ClientContext->SecurityQos.ImpersonationLevel = ClientSecurityQos->ImpersonationLevel;
248 ClientContext->SecurityQos.ContextTrackingMode = ClientSecurityQos->ContextTrackingMode;
249 ClientContext->SecurityQos.EffectiveOnly = ClientSecurityQos->EffectiveOnly;
250 ClientContext->ServerIsRemote = ServerIsRemote;
251 ClientContext->ClientToken = NewToken;
252 return STATUS_SUCCESS;
253 }
254
255 /* PUBLIC FUNCTIONS ***********************************************************/
256
257 /*
258 * @implemented
259 */
260 VOID
261 NTAPI
262 SeCaptureSubjectContextEx(IN PETHREAD Thread,
263 IN PEPROCESS Process,
264 OUT PSECURITY_SUBJECT_CONTEXT SubjectContext)
265 {
266 BOOLEAN CopyOnOpen, EffectiveOnly;
267
268 PAGED_CODE();
269
270 /* Save the unique ID */
271 SubjectContext->ProcessAuditId = Process->UniqueProcessId;
272
273 /* Check if we have a thread */
274 if (!Thread)
275 {
276 /* We don't, so no token */
277 SubjectContext->ClientToken = NULL;
278 }
279 else
280 {
281 /* Get the impersonation token */
282 SubjectContext->ClientToken = PsReferenceImpersonationToken(Thread,
283 &CopyOnOpen,
284 &EffectiveOnly,
285 &SubjectContext->ImpersonationLevel);
286 }
287
288 /* Get the primary token */
289 SubjectContext->PrimaryToken = PsReferencePrimaryToken(Process);
290 }
291
292 /*
293 * @implemented
294 */
295 VOID
296 NTAPI
297 SeCaptureSubjectContext(OUT PSECURITY_SUBJECT_CONTEXT SubjectContext)
298 {
299 /* Call the extended API */
300 SeCaptureSubjectContextEx(PsGetCurrentThread(),
301 PsGetCurrentProcess(),
302 SubjectContext);
303 }
304
305 /*
306 * @implemented
307 */
308 VOID
309 NTAPI
310 SeLockSubjectContext(IN PSECURITY_SUBJECT_CONTEXT SubjectContext)
311 {
312 PTOKEN PrimaryToken, ClientToken;
313 PAGED_CODE();
314
315 /* Read both tokens */
316 PrimaryToken = SubjectContext->PrimaryToken;
317 ClientToken = SubjectContext->ClientToken;
318
319 /* Always lock the primary */
320 SepAcquireTokenLockShared(PrimaryToken);
321
322 /* Lock the impersonation one if it's there */
323 if (!ClientToken) return;
324 SepAcquireTokenLockShared(ClientToken);
325 }
326
327 /*
328 * @implemented
329 */
330 VOID
331 NTAPI
332 SeUnlockSubjectContext(IN PSECURITY_SUBJECT_CONTEXT SubjectContext)
333 {
334 PTOKEN PrimaryToken, ClientToken;
335 PAGED_CODE();
336
337 /* Read both tokens */
338 PrimaryToken = SubjectContext->PrimaryToken;
339 ClientToken = SubjectContext->ClientToken;
340
341 /* Unlock the impersonation one if it's there */
342 if (ClientToken)
343 {
344 SepReleaseTokenLock(ClientToken);
345 }
346
347 /* Always unlock the primary one */
348 SepReleaseTokenLock(PrimaryToken);
349 }
350
351 /*
352 * @implemented
353 */
354 VOID
355 NTAPI
356 SeReleaseSubjectContext(IN PSECURITY_SUBJECT_CONTEXT SubjectContext)
357 {
358 PAGED_CODE();
359
360 /* Drop reference on the primary */
361 ObFastDereferenceObject(&PsGetCurrentProcess()->Token, SubjectContext->PrimaryToken);
362 SubjectContext->PrimaryToken = NULL;
363
364 /* Drop reference on the impersonation, if there was one */
365 PsDereferenceImpersonationToken(SubjectContext->ClientToken);
366 SubjectContext->ClientToken = NULL;
367 }
368
369 /*
370 * @implemented
371 */
372 NTSTATUS
373 NTAPI
374 SeCreateAccessStateEx(IN PETHREAD Thread,
375 IN PEPROCESS Process,
376 IN OUT PACCESS_STATE AccessState,
377 IN PAUX_ACCESS_DATA AuxData,
378 IN ACCESS_MASK Access,
379 IN PGENERIC_MAPPING GenericMapping)
380 {
381 ACCESS_MASK AccessMask = Access;
382 PTOKEN Token;
383 PAGED_CODE();
384
385 /* Map the Generic Acess to Specific Access if we have a Mapping */
386 if ((Access & GENERIC_ACCESS) && (GenericMapping))
387 {
388 RtlMapGenericMask(&AccessMask, GenericMapping);
389 }
390
391 /* Initialize the Access State */
392 RtlZeroMemory(AccessState, sizeof(ACCESS_STATE));
393 ASSERT(AccessState->SecurityDescriptor == NULL);
394 ASSERT(AccessState->PrivilegesAllocated == FALSE);
395
396 /* Initialize and save aux data */
397 RtlZeroMemory(AuxData, sizeof(AUX_ACCESS_DATA));
398 AccessState->AuxData = AuxData;
399
400 /* Capture the Subject Context */
401 SeCaptureSubjectContextEx(Thread,
402 Process,
403 &AccessState->SubjectSecurityContext);
404
405 /* Set Access State Data */
406 AccessState->RemainingDesiredAccess = AccessMask;
407 AccessState->OriginalDesiredAccess = AccessMask;
408 ExAllocateLocallyUniqueId(&AccessState->OperationID);
409
410 /* Get the Token to use */
411 Token = SeQuerySubjectContextToken(&AccessState->SubjectSecurityContext);
412
413 /* Check for Travers Privilege */
414 if (Token->TokenFlags & TOKEN_HAS_TRAVERSE_PRIVILEGE)
415 {
416 /* Preserve the Traverse Privilege */
417 AccessState->Flags = TOKEN_HAS_TRAVERSE_PRIVILEGE;
418 }
419
420 /* Set the Auxiliary Data */
421 AuxData->PrivilegeSet = (PPRIVILEGE_SET)((ULONG_PTR)AccessState +
422 FIELD_OFFSET(ACCESS_STATE,
423 Privileges));
424 if (GenericMapping) AuxData->GenericMapping = *GenericMapping;
425
426 /* Return Sucess */
427 return STATUS_SUCCESS;
428 }
429
430 /*
431 * @implemented
432 */
433 NTSTATUS
434 NTAPI
435 SeCreateAccessState(IN OUT PACCESS_STATE AccessState,
436 IN PAUX_ACCESS_DATA AuxData,
437 IN ACCESS_MASK Access,
438 IN PGENERIC_MAPPING GenericMapping)
439 {
440 PAGED_CODE();
441
442 /* Call the extended API */
443 return SeCreateAccessStateEx(PsGetCurrentThread(),
444 PsGetCurrentProcess(),
445 AccessState,
446 AuxData,
447 Access,
448 GenericMapping);
449 }
450
451 /*
452 * @implemented
453 */
454 VOID
455 NTAPI
456 SeDeleteAccessState(IN PACCESS_STATE AccessState)
457 {
458 PAUX_ACCESS_DATA AuxData;
459 PAGED_CODE();
460
461 /* Get the Auxiliary Data */
462 AuxData = AccessState->AuxData;
463
464 /* Deallocate Privileges */
465 if (AccessState->PrivilegesAllocated)
466 ExFreePoolWithTag(AuxData->PrivilegeSet, TAG_PRIVILEGE_SET);
467
468 /* Deallocate Name and Type Name */
469 if (AccessState->ObjectName.Buffer)
470 {
471 ExFreePool(AccessState->ObjectName.Buffer);
472 }
473
474 if (AccessState->ObjectTypeName.Buffer)
475 {
476 ExFreePool(AccessState->ObjectTypeName.Buffer);
477 }
478
479 /* Release the Subject Context */
480 SeReleaseSubjectContext(&AccessState->SubjectSecurityContext);
481 }
482
483 /*
484 * @implemented
485 */
486 VOID
487 NTAPI
488 SeSetAccessStateGenericMapping(IN PACCESS_STATE AccessState,
489 IN PGENERIC_MAPPING GenericMapping)
490 {
491 PAGED_CODE();
492
493 /* Set the Generic Mapping */
494 ((PAUX_ACCESS_DATA)AccessState->AuxData)->GenericMapping = *GenericMapping;
495 }
496
497 /*
498 * @implemented
499 */
500 NTSTATUS
501 NTAPI
502 SeCreateClientSecurity(IN PETHREAD Thread,
503 IN PSECURITY_QUALITY_OF_SERVICE Qos,
504 IN BOOLEAN RemoteClient,
505 OUT PSECURITY_CLIENT_CONTEXT ClientContext)
506 {
507 TOKEN_TYPE TokenType;
508 BOOLEAN ThreadEffectiveOnly;
509 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
510 PACCESS_TOKEN Token;
511 NTSTATUS Status;
512 PAGED_CODE();
513
514 /* Reference the correct token */
515 Token = PsReferenceEffectiveToken(Thread,
516 &TokenType,
517 &ThreadEffectiveOnly,
518 &ImpersonationLevel);
519
520 /* Create client security from it */
521 Status = SepCreateClientSecurity(Token,
522 Qos,
523 RemoteClient,
524 TokenType,
525 ThreadEffectiveOnly,
526 ImpersonationLevel,
527 ClientContext);
528
529 /* Check if we failed or static tracking was used */
530 if (!(NT_SUCCESS(Status)) || (Qos->ContextTrackingMode == SECURITY_STATIC_TRACKING))
531 {
532 /* Dereference our copy since it's not being used */
533 ObDereferenceObject(Token);
534 }
535
536 /* Return status */
537 return Status;
538 }
539
540 /*
541 * @implemented
542 */
543 NTSTATUS
544 NTAPI
545 SeCreateClientSecurityFromSubjectContext(IN PSECURITY_SUBJECT_CONTEXT SubjectContext,
546 IN PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos,
547 IN BOOLEAN ServerIsRemote,
548 OUT PSECURITY_CLIENT_CONTEXT ClientContext)
549 {
550 PACCESS_TOKEN Token;
551 NTSTATUS Status;
552 PAGED_CODE();
553
554 /* Get the right token and reference it */
555 Token = SeQuerySubjectContextToken(SubjectContext);
556 ObReferenceObject(Token);
557
558 /* Create the context */
559 Status = SepCreateClientSecurity(Token,
560 ClientSecurityQos,
561 ServerIsRemote,
562 SubjectContext->ClientToken ?
563 TokenImpersonation : TokenPrimary,
564 FALSE,
565 SubjectContext->ImpersonationLevel,
566 ClientContext);
567
568 /* Check if we failed or static tracking was used */
569 if (!(NT_SUCCESS(Status)) ||
570 (ClientSecurityQos->ContextTrackingMode == SECURITY_STATIC_TRACKING))
571 {
572 /* Dereference our copy since it's not being used */
573 ObDereferenceObject(Token);
574 }
575
576 /* Return status */
577 return Status;
578 }
579
580 /*
581 * @implemented
582 */
583 NTSTATUS
584 NTAPI
585 SeImpersonateClientEx(IN PSECURITY_CLIENT_CONTEXT ClientContext,
586 IN PETHREAD ServerThread OPTIONAL)
587 {
588 BOOLEAN EffectiveOnly;
589 PAGED_CODE();
590
591 /* Check if direct access is requested */
592 if (!ClientContext->DirectlyAccessClientToken)
593 {
594 /* No, so get the flag from QOS */
595 EffectiveOnly = ClientContext->SecurityQos.EffectiveOnly;
596 }
597 else
598 {
599 /* Yes, so see if direct access should be effective only */
600 EffectiveOnly = ClientContext->DirectAccessEffectiveOnly;
601 }
602
603 /* Use the current thread if one was not passed */
604 if (!ServerThread) ServerThread = PsGetCurrentThread();
605
606 /* Call the lower layer routine */
607 return PsImpersonateClient(ServerThread,
608 ClientContext->ClientToken,
609 TRUE,
610 EffectiveOnly,
611 ClientContext->SecurityQos.ImpersonationLevel);
612 }
613
614 /*
615 * @implemented
616 */
617 VOID
618 NTAPI
619 SeImpersonateClient(IN PSECURITY_CLIENT_CONTEXT ClientContext,
620 IN PETHREAD ServerThread OPTIONAL)
621 {
622 PAGED_CODE();
623
624 /* Call the new API */
625 SeImpersonateClientEx(ClientContext, ServerThread);
626 }
627
628 /* EOF */