Merge 25584, 25588.
[reactos.git] / reactos / ntoskrnl / ob / obsecure.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ob/obsecure.c
5 * PURPOSE: SRM Interface of the Object Manager
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
7 * Eric Kohl
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <internal/debug.h>
15
16 /* PRIVATE FUNCTIONS *********************************************************/
17
18 /*++
19 * @name ObCheckObjectAccess
20 *
21 * The ObAssignSecurity routine <FILLMEIN>
22 *
23 * @param Object
24 * <FILLMEIN>
25 *
26 * @param AccessState
27 * <FILLMEIN>
28 *
29 * @param Unknown
30 * <FILLMEIN>
31 *
32 * @param AccessMode
33 * <FILLMEIN>
34 *
35 * @param ReturnedStatus
36 * <FILLMEIN>
37 *
38 * @return TRUE if access was granted, FALSE otherwise.
39 *
40 * @remarks None.
41 *
42 *--*/
43 BOOLEAN
44 NTAPI
45 ObCheckObjectAccess(IN PVOID Object,
46 IN OUT PACCESS_STATE AccessState,
47 IN BOOLEAN Unknown,
48 IN KPROCESSOR_MODE AccessMode,
49 OUT PNTSTATUS ReturnedStatus)
50 {
51 POBJECT_HEADER ObjectHeader;
52 POBJECT_TYPE ObjectType;
53 PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
54 BOOLEAN SdAllocated;
55 NTSTATUS Status;
56 BOOLEAN Result;
57 ACCESS_MASK GrantedAccess;
58 PPRIVILEGE_SET Privileges = NULL;
59 PAGED_CODE();
60
61 /* Get the object header and type */
62 ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
63 ObjectType = ObjectHeader->Type;
64
65 /* Get security information */
66 Status = ObGetObjectSecurity(Object, &SecurityDescriptor, &SdAllocated);
67 if (!NT_SUCCESS(Status))
68 {
69 /* Return failure */
70 *ReturnedStatus = Status;
71 return FALSE;
72 }
73 else if (!SecurityDescriptor)
74 {
75 /* Otherwise, if we don't actually have an SD, return success */
76 *ReturnedStatus = Status;
77 return TRUE;
78 }
79
80 /* Lock the security context */
81 SeLockSubjectContext(&AccessState->SubjectSecurityContext);
82
83 /* Now do the entire access check */
84 Result = SeAccessCheck(SecurityDescriptor,
85 &AccessState->SubjectSecurityContext,
86 TRUE,
87 AccessState->RemainingDesiredAccess,
88 AccessState->PreviouslyGrantedAccess,
89 &Privileges,
90 &ObjectType->TypeInfo.GenericMapping,
91 AccessMode,
92 &GrantedAccess,
93 ReturnedStatus);
94 if (Privileges)
95 {
96 /* We got privileges, append them to teh access state and free them */
97 Status = SeAppendPrivileges(AccessState, Privileges);
98 SeFreePrivileges(Privileges);
99 }
100
101 /* Check if access was granted */
102 if (Result)
103 {
104 /* Update the access state */
105 AccessState->RemainingDesiredAccess &= ~(GrantedAccess |
106 MAXIMUM_ALLOWED);
107 AccessState->PreviouslyGrantedAccess |= GrantedAccess;
108 }
109
110 /* Do audit alarm */
111 SeOpenObjectAuditAlarm(&ObjectType->Name,
112 Object,
113 NULL,
114 SecurityDescriptor,
115 AccessState,
116 FALSE,
117 Result,
118 AccessMode,
119 &AccessState->GenerateOnClose);
120
121 /* We're done, unlock the context and release security */
122 SeUnlockSubjectContext(&AccessState->SubjectSecurityContext);
123 ObReleaseObjectSecurity(SecurityDescriptor, SdAllocated);
124 return Result;
125 }
126
127 /* PUBLIC FUNCTIONS **********************************************************/
128
129 /*++
130 * @name ObAssignSecurity
131 * @implemented NT4
132 *
133 * The ObAssignSecurity routine <FILLMEIN>
134 *
135 * @param AccessState
136 * <FILLMEIN>
137 *
138 * @param SecurityDescriptor
139 * <FILLMEIN>
140 *
141 * @param Object
142 * <FILLMEIN>
143 *
144 * @param Type
145 * <FILLMEIN>
146 *
147 * @return STATUS_SUCCESS or appropriate error value.
148 *
149 * @remarks None.
150 *
151 *--*/
152 NTSTATUS
153 NTAPI
154 ObAssignSecurity(IN PACCESS_STATE AccessState,
155 IN PSECURITY_DESCRIPTOR SecurityDescriptor,
156 IN PVOID Object,
157 IN POBJECT_TYPE Type)
158 {
159 PSECURITY_DESCRIPTOR NewDescriptor;
160 NTSTATUS Status;
161 KIRQL CalloutIrql;
162 PAGED_CODE();
163
164 /* Build the new security descriptor */
165 Status = SeAssignSecurity(SecurityDescriptor,
166 AccessState->SecurityDescriptor,
167 &NewDescriptor,
168 (Type == ObDirectoryType),
169 &AccessState->SubjectSecurityContext,
170 &Type->TypeInfo.GenericMapping,
171 PagedPool);
172 if (!NT_SUCCESS(Status)) return Status;
173
174 /* Call the security method */
175 ObpCalloutStart(&CalloutIrql);
176 Status = Type->TypeInfo.SecurityProcedure(Object,
177 AssignSecurityDescriptor,
178 NULL,
179 NewDescriptor,
180 NULL,
181 NULL,
182 PagedPool,
183 &Type->TypeInfo.GenericMapping);
184 ObpCalloutEnd(CalloutIrql, "Security", Type, Object);
185
186 /* Check for failure and deassign security if so */
187 if (!NT_SUCCESS(Status)) SeDeassignSecurity(&NewDescriptor);
188
189 /* Return to caller */
190 return Status;
191 }
192
193 /*++
194 * @name ObGetObjectSecurity
195 * @implemented NT4
196 *
197 * The ObGetObjectSecurity routine <FILLMEIN>
198 *
199 * @param Object
200 * <FILLMEIN>
201 *
202 * @param SecurityDescriptor
203 * <FILLMEIN>
204 *
205 * @param MemoryAllocated
206 * <FILLMEIN>
207 *
208 * @return STATUS_SUCCESS or appropriate error value.
209 *
210 * @remarks None.
211 *
212 *--*/
213 NTSTATUS
214 NTAPI
215 ObGetObjectSecurity(IN PVOID Object,
216 OUT PSECURITY_DESCRIPTOR *SecurityDescriptor,
217 OUT PBOOLEAN MemoryAllocated)
218 {
219 POBJECT_HEADER Header;
220 POBJECT_TYPE Type;
221 ULONG Length = 0;
222 NTSTATUS Status;
223 SECURITY_INFORMATION SecurityInformation;
224 KIRQL CalloutIrql;
225 PAGED_CODE();
226
227 /* Get the object header and type */
228 Header = OBJECT_TO_OBJECT_HEADER(Object);
229 Type = Header->Type;
230
231 /* Tell the caller that we didn't have to allocate anything yet */
232 *MemoryAllocated = FALSE;
233
234 /* Check if the object uses default security */
235 if (Type->TypeInfo.SecurityProcedure == SeDefaultObjectMethod)
236 {
237 /* Reference the descriptor */
238 *SecurityDescriptor =
239 ObpReferenceCachedSecurityDescriptor(Header->SecurityDescriptor);
240 return STATUS_SUCCESS;
241 }
242
243 /* Set mask to query */
244 SecurityInformation = OWNER_SECURITY_INFORMATION |
245 GROUP_SECURITY_INFORMATION |
246 DACL_SECURITY_INFORMATION |
247 SACL_SECURITY_INFORMATION;
248
249 /* Get the security descriptor size */
250 ObpCalloutStart(&CalloutIrql);
251 Status = Type->TypeInfo.SecurityProcedure(Object,
252 QuerySecurityDescriptor,
253 &SecurityInformation,
254 *SecurityDescriptor,
255 &Length,
256 &Header->SecurityDescriptor,
257 Type->TypeInfo.PoolType,
258 &Type->TypeInfo.GenericMapping);
259 ObpCalloutEnd(CalloutIrql, "Security", Type, Object);
260
261 /* Check for failure */
262 if (Status != STATUS_BUFFER_TOO_SMALL) return Status;
263
264 /* Allocate security descriptor */
265 *SecurityDescriptor = ExAllocatePoolWithTag(PagedPool,
266 Length,
267 TAG_SEC_QUERY);
268 if (!(*SecurityDescriptor)) return STATUS_INSUFFICIENT_RESOURCES;
269 *MemoryAllocated = TRUE;
270
271 /* Query security descriptor */
272 ObpCalloutStart(&CalloutIrql);
273 Status = Type->TypeInfo.SecurityProcedure(Object,
274 QuerySecurityDescriptor,
275 &SecurityInformation,
276 *SecurityDescriptor,
277 &Length,
278 &Header->SecurityDescriptor,
279 Type->TypeInfo.PoolType,
280 &Type->TypeInfo.GenericMapping);
281 ObpCalloutEnd(CalloutIrql, "Security", Type, Object);
282
283 /* Check for failure */
284 if (!NT_SUCCESS(Status))
285 {
286 /* Free the descriptor and tell the caller we failed */
287 ExFreePool(*SecurityDescriptor);
288 *MemoryAllocated = FALSE;
289 }
290
291 /* Return status */
292 return Status;
293 }
294
295 /*++
296 * @name ObReleaseObjectSecurity
297 * @implemented NT4
298 *
299 * The ObReleaseObjectSecurity routine <FILLMEIN>
300 *
301 * @param SecurityDescriptor
302 * <FILLMEIN>
303 *
304 * @param MemoryAllocated
305 * <FILLMEIN>
306 *
307 * @return STATUS_SUCCESS or appropriate error value.
308 *
309 * @remarks None.
310 *
311 *--*/
312 VOID
313 NTAPI
314 ObReleaseObjectSecurity(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
315 IN BOOLEAN MemoryAllocated)
316 {
317 PAGED_CODE();
318
319 /* Nothing to do in this case */
320 if (!SecurityDescriptor) return;
321
322 /* Check if we had allocated it from memory */
323 if (MemoryAllocated)
324 {
325 /* Free it */
326 ExFreePool(SecurityDescriptor);
327 }
328 else
329 {
330 /* Otherwise this means we used an internal descriptor */
331 ObpDereferenceCachedSecurityDescriptor(SecurityDescriptor);
332 }
333 }
334
335 /*++
336 * @name ObSetSecurityObjectByPointer
337 * @implemented NT5.1
338 *
339 * The ObSetSecurityObjectByPointer routine <FILLMEIN>
340 *
341 * @param SecurityDescriptor
342 * <FILLMEIN>
343 *
344 * @param MemoryAllocated
345 * <FILLMEIN>
346 *
347 * @return STATUS_SUCCESS or appropriate error value.
348 *
349 * @remarks None.
350 *
351 *--*/
352 NTSTATUS
353 NTAPI
354 ObSetSecurityObjectByPointer(IN PVOID Object,
355 IN SECURITY_INFORMATION SecurityInformation,
356 IN PSECURITY_DESCRIPTOR SecurityDescriptor)
357 {
358 POBJECT_TYPE Type;
359 POBJECT_HEADER Header;
360 PAGED_CODE();
361
362 /* Get the header and type */
363 Header = OBJECT_TO_OBJECT_HEADER(Object);
364 Type = Header->Type;
365
366 /* Sanity check */
367 ASSERT(SecurityDescriptor);
368
369 /* Call the security procedure */
370 return Type->TypeInfo.SecurityProcedure(Object,
371 SetSecurityDescriptor,
372 &SecurityInformation,
373 SecurityDescriptor,
374 NULL,
375 &Header->SecurityDescriptor,
376 Type->TypeInfo.PoolType,
377 &Type->TypeInfo.GenericMapping);
378 }
379
380 /*++
381 * @name NtQuerySecurityObject
382 * @implemented NT4
383 *
384 * The NtQuerySecurityObject routine <FILLMEIN>
385 *
386 * @param Handle
387 * <FILLMEIN>
388 *
389 * @param SecurityInformation
390 * <FILLMEIN>
391 *
392 * @param SecurityDescriptor
393 * <FILLMEIN>
394 *
395 * @param Length
396 * <FILLMEIN>
397 *
398 * @param ResultLength
399 * <FILLMEIN>
400 *
401 * @return STATUS_SUCCESS or appropriate error value.
402 *
403 * @remarks None.
404 *
405 *--*/
406 NTSTATUS
407 NTAPI
408 NtQuerySecurityObject(IN HANDLE Handle,
409 IN SECURITY_INFORMATION SecurityInformation,
410 OUT PSECURITY_DESCRIPTOR SecurityDescriptor,
411 IN ULONG Length,
412 OUT PULONG ResultLength)
413 {
414 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
415 PVOID Object;
416 POBJECT_HEADER Header;
417 POBJECT_TYPE Type;
418 ACCESS_MASK DesiredAccess;
419 NTSTATUS Status = STATUS_SUCCESS;
420 PAGED_CODE();
421
422 /* Check if we came from user mode */
423 if (PreviousMode != KernelMode)
424 {
425 /* Enter SEH */
426 _SEH_TRY
427 {
428 /* Probe the SD and the length pointer */
429 ProbeForWrite(SecurityDescriptor, Length, sizeof(ULONG));
430 ProbeForWriteUlong(ResultLength);
431 }
432 _SEH_HANDLE
433 {
434 /* Get the exception code */
435 Status = _SEH_GetExceptionCode();
436 }
437 _SEH_END;
438
439 /* Fail if we got an access violation */
440 if (!NT_SUCCESS(Status)) return Status;
441 }
442
443 /* Get the required access rights for the operation */
444 SeQuerySecurityAccessMask(SecurityInformation, &DesiredAccess);
445
446 /* Reference the object */
447 Status = ObReferenceObjectByHandle(Handle,
448 DesiredAccess,
449 NULL,
450 PreviousMode,
451 &Object,
452 NULL);
453 if (!NT_SUCCESS(Status)) return Status;
454
455 /* Get the Object Header and Type */
456 Header = OBJECT_TO_OBJECT_HEADER(Object);
457 Type = Header->Type;
458
459 /* Call the security procedure's query function */
460 Status = Type->TypeInfo.SecurityProcedure(Object,
461 QuerySecurityDescriptor,
462 &SecurityInformation,
463 SecurityDescriptor,
464 &Length,
465 &Header->SecurityDescriptor,
466 Type->TypeInfo.PoolType,
467 &Type->TypeInfo.GenericMapping);
468
469 /* Dereference the object */
470 ObDereferenceObject(Object);
471
472 /* Protect write with SEH */
473 _SEH_TRY
474 {
475 /* Return the needed length */
476 *ResultLength = Length;
477 }
478 _SEH_EXCEPT(_SEH_ExSystemExceptionFilter)
479 {
480 /* Get the exception code */
481 Status = _SEH_GetExceptionCode();
482 }
483 _SEH_END;
484
485 /* Return status */
486 return Status;
487 }
488
489 /*++
490 * @name NtSetSecurityObject
491 * @implemented NT4
492 *
493 * The NtSetSecurityObject routine <FILLMEIN>
494 *
495 * @param Handle
496 * <FILLMEIN>
497 *
498 * @param SecurityInformation
499 * <FILLMEIN>
500 *
501 * @param SecurityDescriptor
502 * <FILLMEIN>
503 *
504 * @return STATUS_SUCCESS or appropriate error value.
505 *
506 * @remarks None.
507 *
508 *--*/
509 NTSTATUS
510 NTAPI
511 NtSetSecurityObject(IN HANDLE Handle,
512 IN SECURITY_INFORMATION SecurityInformation,
513 IN PSECURITY_DESCRIPTOR SecurityDescriptor)
514 {
515 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
516 PVOID Object;
517 SECURITY_DESCRIPTOR_RELATIVE *CapturedDescriptor;
518 ACCESS_MASK DesiredAccess;
519 NTSTATUS Status;
520 PAGED_CODE();
521
522 /* Make sure the caller doesn't pass a NULL security descriptor! */
523 if (!SecurityDescriptor) return STATUS_ACCESS_VIOLATION;
524
525 /* Set the required access rights for the operation */
526 SeSetSecurityAccessMask(SecurityInformation, &DesiredAccess);
527
528 /* Reference the object */
529 Status = ObReferenceObjectByHandle(Handle,
530 DesiredAccess,
531 NULL,
532 PreviousMode,
533 &Object,
534 NULL);
535 if (NT_SUCCESS(Status))
536 {
537 /* Capture and make a copy of the security descriptor */
538 Status = SeCaptureSecurityDescriptor(SecurityDescriptor,
539 PreviousMode,
540 PagedPool,
541 TRUE,
542 (PSECURITY_DESCRIPTOR*)
543 &CapturedDescriptor);
544 if (!NT_SUCCESS(Status))
545 {
546 /* Fail */
547 ObDereferenceObject(Object);
548 return Status;
549 }
550
551 /* Sanity check */
552 ASSERT(CapturedDescriptor->Control & SE_SELF_RELATIVE);
553
554 /*
555 * Make sure the security descriptor passed by the caller
556 * is valid for the operation we're about to perform
557 */
558 if (((SecurityInformation & OWNER_SECURITY_INFORMATION) &&
559 !(CapturedDescriptor->Owner)) ||
560 ((SecurityInformation & GROUP_SECURITY_INFORMATION) &&
561 !(CapturedDescriptor->Group)))
562 {
563 /* Set the failure status */
564 Status = STATUS_INVALID_SECURITY_DESCR;
565 }
566 else
567 {
568 /* Set security */
569 Status = ObSetSecurityObjectByPointer(Object,
570 SecurityInformation,
571 CapturedDescriptor);
572 }
573
574 /* Release the descriptor and return status */
575 SeReleaseSecurityDescriptor((PSECURITY_DESCRIPTOR)CapturedDescriptor,
576 PreviousMode,
577 TRUE);
578 }
579
580 /* Now we can dereference the object */
581 ObDereferenceObject(Object);
582 return Status;
583 }
584
585 /*++
586 * @name ObQueryObjectAuditingByHandle
587 * @implemented NT5
588 *
589 * The ObDereferenceSecurityDescriptor routine <FILLMEIN>
590 *
591 * @param SecurityDescriptor
592 * <FILLMEIN>
593 *
594 * @param Count
595 * <FILLMEIN>
596 *
597 * @return STATUS_SUCCESS or appropriate error value.
598 *
599 * @remarks None.
600 *
601 *--*/
602 NTSTATUS
603 NTAPI
604 ObQueryObjectAuditingByHandle(IN HANDLE Handle,
605 OUT PBOOLEAN GenerateOnClose)
606 {
607 PHANDLE_TABLE_ENTRY HandleEntry;
608 PVOID HandleTable;
609 NTSTATUS Status = STATUS_SUCCESS;
610 PAGED_CODE();
611
612 /* Check if we're dealing with a kernel handle */
613 if (ObIsKernelHandle(Handle, ExGetPreviousMode()))
614 {
615 /* Use the kernel table and convert the handle */
616 HandleTable = ObpKernelHandleTable;
617 Handle = ObKernelHandleToHandle(Handle);
618 }
619 else
620 {
621 /* Use the process's handle table */
622 HandleTable = PsGetCurrentProcess()->ObjectTable;
623 }
624
625 /* Enter a critical region while we touch the handle table */
626 KeEnterCriticalRegion();
627
628 /* Map the handle */
629 HandleEntry = ExMapHandleToPointer(HandleTable, Handle);
630 if(HandleEntry)
631 {
632 /* Check if the flag is set */
633 *GenerateOnClose = (HandleEntry->ObAttributes &
634 EX_HANDLE_ENTRY_AUDITONCLOSE) != 0;
635
636 /* Unlock the entry */
637 ExUnlockHandleTableEntry(HandleTable, HandleEntry);
638 }
639 else
640 {
641 /* Otherwise, fail */
642 Status = STATUS_INVALID_HANDLE;
643 }
644
645 /* Leave the critical region and return the status */
646 KeLeaveCriticalRegion();
647 return Status;
648 }
649
650 /* EOF */