[MS] Add messages 2200-2249 and 3502-3507 to netmsg.dll.
[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 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 */