Synchronize up to trunk's revision r57784.
[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 /* Always unlock the primary one */
342 SepReleaseTokenLock(PrimaryToken);
343
344 /* Unlock the impersonation one if it's there */
345 if (!ClientToken) return;
346 SepReleaseTokenLock(ClientToken);
347 }
348
349 /*
350 * @implemented
351 */
352 VOID
353 NTAPI
354 SeReleaseSubjectContext(IN PSECURITY_SUBJECT_CONTEXT SubjectContext)
355 {
356 PAGED_CODE();
357
358 /* Drop reference on the primary */
359 ObFastDereferenceObject(&PsGetCurrentProcess()->Token, SubjectContext->PrimaryToken);
360 SubjectContext->PrimaryToken = NULL;
361
362 /* Drop reference on the impersonation, if there was one */
363 PsDereferenceImpersonationToken(SubjectContext->ClientToken);
364 SubjectContext->ClientToken = NULL;
365 }
366
367 /*
368 * @implemented
369 */
370 NTSTATUS
371 NTAPI
372 SeCreateAccessStateEx(IN PETHREAD Thread,
373 IN PEPROCESS Process,
374 IN OUT PACCESS_STATE AccessState,
375 IN PAUX_ACCESS_DATA AuxData,
376 IN ACCESS_MASK Access,
377 IN PGENERIC_MAPPING GenericMapping)
378 {
379 ACCESS_MASK AccessMask = Access;
380 PTOKEN Token;
381 PAGED_CODE();
382
383 /* Map the Generic Acess to Specific Access if we have a Mapping */
384 if ((Access & GENERIC_ACCESS) && (GenericMapping))
385 {
386 RtlMapGenericMask(&AccessMask, GenericMapping);
387 }
388
389 /* Initialize the Access State */
390 RtlZeroMemory(AccessState, sizeof(ACCESS_STATE));
391 ASSERT(AccessState->SecurityDescriptor == NULL);
392 ASSERT(AccessState->PrivilegesAllocated == FALSE);
393
394 /* Initialize and save aux data */
395 RtlZeroMemory(AuxData, sizeof(AUX_ACCESS_DATA));
396 AccessState->AuxData = AuxData;
397
398 /* Capture the Subject Context */
399 SeCaptureSubjectContextEx(Thread,
400 Process,
401 &AccessState->SubjectSecurityContext);
402
403 /* Set Access State Data */
404 AccessState->RemainingDesiredAccess = AccessMask;
405 AccessState->OriginalDesiredAccess = AccessMask;
406 ExpAllocateLocallyUniqueId(&AccessState->OperationID);
407
408 /* Get the Token to use */
409 Token = SeQuerySubjectContextToken(&AccessState->SubjectSecurityContext);
410
411 /* Check for Travers Privilege */
412 if (Token->TokenFlags & TOKEN_HAS_TRAVERSE_PRIVILEGE)
413 {
414 /* Preserve the Traverse Privilege */
415 AccessState->Flags = TOKEN_HAS_TRAVERSE_PRIVILEGE;
416 }
417
418 /* Set the Auxiliary Data */
419 AuxData->PrivilegeSet = (PPRIVILEGE_SET)((ULONG_PTR)AccessState +
420 FIELD_OFFSET(ACCESS_STATE,
421 Privileges));
422 if (GenericMapping) AuxData->GenericMapping = *GenericMapping;
423
424 /* Return Sucess */
425 return STATUS_SUCCESS;
426 }
427
428 /*
429 * @implemented
430 */
431 NTSTATUS
432 NTAPI
433 SeCreateAccessState(IN OUT PACCESS_STATE AccessState,
434 IN PAUX_ACCESS_DATA AuxData,
435 IN ACCESS_MASK Access,
436 IN PGENERIC_MAPPING GenericMapping)
437 {
438 PAGED_CODE();
439
440 /* Call the extended API */
441 return SeCreateAccessStateEx(PsGetCurrentThread(),
442 PsGetCurrentProcess(),
443 AccessState,
444 AuxData,
445 Access,
446 GenericMapping);
447 }
448
449 /*
450 * @implemented
451 */
452 VOID
453 NTAPI
454 SeDeleteAccessState(IN PACCESS_STATE AccessState)
455 {
456 PAUX_ACCESS_DATA AuxData;
457 PAGED_CODE();
458
459 /* Get the Auxiliary Data */
460 AuxData = AccessState->AuxData;
461
462 /* Deallocate Privileges */
463 if (AccessState->PrivilegesAllocated) ExFreePool(AuxData->PrivilegeSet);
464
465 /* Deallocate Name and Type Name */
466 if (AccessState->ObjectName.Buffer)
467 {
468 ExFreePool(AccessState->ObjectName.Buffer);
469 }
470
471 if (AccessState->ObjectTypeName.Buffer)
472 {
473 ExFreePool(AccessState->ObjectTypeName.Buffer);
474 }
475
476 /* Release the Subject Context */
477 SeReleaseSubjectContext(&AccessState->SubjectSecurityContext);
478 }
479
480 /*
481 * @implemented
482 */
483 VOID
484 NTAPI
485 SeSetAccessStateGenericMapping(IN PACCESS_STATE AccessState,
486 IN PGENERIC_MAPPING GenericMapping)
487 {
488 PAGED_CODE();
489
490 /* Set the Generic Mapping */
491 ((PAUX_ACCESS_DATA)AccessState->AuxData)->GenericMapping = *GenericMapping;
492 }
493
494 /*
495 * @implemented
496 */
497 NTSTATUS
498 NTAPI
499 SeCreateClientSecurity(IN PETHREAD Thread,
500 IN PSECURITY_QUALITY_OF_SERVICE Qos,
501 IN BOOLEAN RemoteClient,
502 OUT PSECURITY_CLIENT_CONTEXT ClientContext)
503 {
504 TOKEN_TYPE TokenType;
505 BOOLEAN ThreadEffectiveOnly;
506 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
507 PACCESS_TOKEN Token;
508 NTSTATUS Status;
509 PAGED_CODE();
510
511 /* Reference the correct token */
512 Token = PsReferenceEffectiveToken(Thread,
513 &TokenType,
514 &ThreadEffectiveOnly,
515 &ImpersonationLevel);
516
517 /* Create client security from it */
518 Status = SepCreateClientSecurity(Token,
519 Qos,
520 RemoteClient,
521 TokenType,
522 ThreadEffectiveOnly,
523 ImpersonationLevel,
524 ClientContext);
525
526 /* Check if we failed or static tracking was used */
527 if (!(NT_SUCCESS(Status)) || (Qos->ContextTrackingMode == SECURITY_STATIC_TRACKING))
528 {
529 /* Dereference our copy since it's not being used */
530 ObDereferenceObject(Token);
531 }
532
533 /* Return status */
534 return Status;
535 }
536
537 /*
538 * @implemented
539 */
540 NTSTATUS
541 NTAPI
542 SeCreateClientSecurityFromSubjectContext(IN PSECURITY_SUBJECT_CONTEXT SubjectContext,
543 IN PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos,
544 IN BOOLEAN ServerIsRemote,
545 OUT PSECURITY_CLIENT_CONTEXT ClientContext)
546 {
547 PACCESS_TOKEN Token;
548 NTSTATUS Status;
549 PAGED_CODE();
550
551 /* Get the right token and reference it */
552 Token = SeQuerySubjectContextToken(SubjectContext);
553 ObReferenceObject(Token);
554
555 /* Create the context */
556 Status = SepCreateClientSecurity(Token,
557 ClientSecurityQos,
558 ServerIsRemote,
559 SubjectContext->ClientToken ?
560 TokenImpersonation : TokenPrimary,
561 FALSE,
562 SubjectContext->ImpersonationLevel,
563 ClientContext);
564
565 /* Check if we failed or static tracking was used */
566 if (!(NT_SUCCESS(Status)) ||
567 (ClientSecurityQos->ContextTrackingMode == SECURITY_STATIC_TRACKING))
568 {
569 /* Dereference our copy since it's not being used */
570 ObDereferenceObject(Token);
571 }
572
573 /* Return status */
574 return Status;
575 }
576
577 /*
578 * @implemented
579 */
580 NTSTATUS
581 NTAPI
582 SeImpersonateClientEx(IN PSECURITY_CLIENT_CONTEXT ClientContext,
583 IN PETHREAD ServerThread OPTIONAL)
584 {
585 BOOLEAN EffectiveOnly;
586 PAGED_CODE();
587
588 /* Check if direct access is requested */
589 if (!ClientContext->DirectlyAccessClientToken)
590 {
591 /* No, so get the flag from QOS */
592 EffectiveOnly = ClientContext->SecurityQos.EffectiveOnly;
593 }
594 else
595 {
596 /* Yes, so see if direct access should be effective only */
597 EffectiveOnly = ClientContext->DirectAccessEffectiveOnly;
598 }
599
600 /* Use the current thread if one was not passed */
601 if (!ServerThread) ServerThread = PsGetCurrentThread();
602
603 /* Call the lower layer routine */
604 return PsImpersonateClient(ServerThread,
605 ClientContext->ClientToken,
606 TRUE,
607 EffectiveOnly,
608 ClientContext->SecurityQos.ImpersonationLevel);
609 }
610
611 /*
612 * @implemented
613 */
614 VOID
615 NTAPI
616 SeImpersonateClient(IN PSECURITY_CLIENT_CONTEXT ClientContext,
617 IN PETHREAD ServerThread OPTIONAL)
618 {
619 PAGED_CODE();
620
621 /* Call the new API */
622 SeImpersonateClientEx(ClientContext, ServerThread);
623 }
624
625 /* EOF */