xref: /reactos/ntoskrnl/se/objtype.c (revision e38f4c2b)
1 /*
2  * PROJECT:     ReactOS Kernel
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Security object type list support routines
5  * COPYRIGHT:   Copyright Timo Kreuzer <timo.kreuzer@reactos.org>
6  *              Copyright 2023 George Bișoc <george.bisoc@reactos.org>
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 /* PRIVATE FUNCTIONS **********************************************************/
16 
17 /**
18  * @brief
19  * Validates a list of object types passed from user mode,
20  * ensuring the following conditions are met for a valid
21  * list:
22  *
23  * - The list must not be too big and it can be read
24  * - Each object must have a valid level
25  * - The level hierarchy between objects has to be consistent
26  *   (e.g. a root cannot have a level 2 subordinate object)
27  * - The list must have only one root and it must be in the
28  *   first position
29  * - Each object type GUID can be read and captured
30  *
31  * @param[in] ObjectTypeList
32  * A pointer to an object type list of which the elements
33  * are being validated.
34  *
35  * @param[in] ObjectTypeListLength
36  * The length of the list, representing the number of object
37  * elements in that list.
38  *
39  * @return
40  * Returns STATUS_SUCCESS if the list has been validated and it
41  * contains valid objects. STATUS_INVALID_PARAMETER is returned
42  * if the list is not valid. Otherwise a NTSTATUS code is returned.
43  */
44 static
45 NTSTATUS
SepValidateObjectTypeList(_In_reads_ (ObjectTypeListLength)POBJECT_TYPE_LIST ObjectTypeList,_In_ ULONG ObjectTypeListLength)46 SepValidateObjectTypeList(
47     _In_reads_(ObjectTypeListLength) POBJECT_TYPE_LIST ObjectTypeList,
48     _In_ ULONG ObjectTypeListLength)
49 {
50     PGUID ObjectTypeGuid;
51     ULONG ObjectTypeIndex;
52     USHORT Level, PrevLevel;
53     SIZE_T Size;
54 
55     /* Ensure we do not hit an integer overflow */
56     Size = ObjectTypeListLength * sizeof(OBJECT_TYPE_LIST);
57     if (Size == 0)
58     {
59         DPRINT1("The object type list is too big, integer overflow alert!\n");
60         return STATUS_INVALID_PARAMETER;
61     }
62 
63     _SEH2_TRY
64     {
65         /* Ensure we can actually read from that list */
66         ProbeForRead(ObjectTypeList, Size, sizeof(ULONG));
67 
68         /* Begin looping for each object from the list */
69         for (ObjectTypeIndex = 0;
70              ObjectTypeIndex < ObjectTypeListLength;
71              ObjectTypeIndex++)
72         {
73             /* Get the level of this object and check for validity */
74             Level = ObjectTypeList[ObjectTypeIndex].Level;
75             if (Level > ACCESS_MAX_LEVEL)
76             {
77                 DPRINT1("Invalid object level found (level %u, at index %lu)\n", Level, ObjectTypeIndex);
78                 _SEH2_YIELD(return STATUS_INVALID_PARAMETER);
79             }
80 
81             /* Are we past the first position in the list? */
82             if (ObjectTypeIndex != 0)
83             {
84                 /* Ensure that we do not have two object roots */
85                 if (Level == ACCESS_OBJECT_GUID)
86                 {
87                     DPRINT1("This list has two roots (at index %lu)\n", ObjectTypeIndex);
88                     _SEH2_YIELD(return STATUS_INVALID_PARAMETER);
89                 }
90 
91                 /*
92                  * Ensure the current level is consistent with the prior level.
93                  * That means, if the previous object is the root (denoted by the
94                  * level as ACCESS_OBJECT_GUID aka 0) the current object must
95                  * be a child of the parent which is the root (also called a
96                  * property set). Whereas a property is a sibling of the
97                  * child, the property set.
98                  */
99                 if (Level > PrevLevel + 1)
100                 {
101                     DPRINT1("The object levels are not consistent (current level %u, previous level %u, at index %lu)\n",
102                             Level, PrevLevel, ObjectTypeIndex);
103                     _SEH2_YIELD(return STATUS_INVALID_PARAMETER);
104                 }
105             }
106             else
107             {
108                 /* This is the first position so the object must be the root */
109                 if (Level != ACCESS_OBJECT_GUID)
110                 {
111                     DPRINT1("The object is not the root at first index!\n");
112                     _SEH2_YIELD(return STATUS_INVALID_PARAMETER);
113                 }
114             }
115 
116             /* Get the object type and check that we can read from it */
117             ObjectTypeGuid = ObjectTypeList[ObjectTypeIndex].ObjectType;
118             ProbeForRead(ObjectTypeGuid, sizeof(GUID), sizeof(ULONG));
119 
120             /*
121              * Cache the level, we need it to ensure the levels between
122              * the previous and the next object are consistent with each other.
123              */
124             PrevLevel = Level;
125         }
126     }
127     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
128     {
129         _SEH2_YIELD(return _SEH2_GetExceptionCode());
130     }
131     _SEH2_END;
132 
133     return STATUS_SUCCESS;
134 }
135 
136 /**
137  * @brief
138  * Compares two object type GUIDs for equality.
139  *
140  * @param[in] Guid1
141  * A pointer to the first object type GUID.
142  *
143  * @param[in] Guid2
144  * A pointer to the second object type GUID.
145  *
146  * @return
147  * Returns TRUE if both GUIDs are equal, FALSE otherwise.
148  */
149 static
150 BOOLEAN
SepIsEqualObjectTypeGuid(_In_ CONST GUID * Guid1,_In_ CONST GUID * Guid2)151 SepIsEqualObjectTypeGuid(
152     _In_ CONST GUID *Guid1,
153     _In_ CONST GUID *Guid2)
154 {
155     return RtlCompareMemory(Guid1, Guid2, sizeof(GUID)) == sizeof(GUID);
156 }
157 
158 /* PUBLIC FUNCTIONS ************************************************************/
159 
160 /**
161  * @brief
162  * Captures an object type GUID from an object
163  * access control entry (ACE).
164  *
165  * @param[in] Ace
166  * A pointer to an access control entry, of which
167  * the object type GUID is to be captured from.
168  *
169  * @param[in] IsAceDenied
170  * If set to TRUE, the function will capture the
171  * GUID from a denied object ACE, otherwise from
172  * the allowed object ACE.
173  *
174  * @return
175  * Returns a pointer to an object type GUID, otherwise
176  * NULL is returned if the target ACE does not have
177  * an object type GUID.
178  */
179 PGUID
SepGetObjectTypeGuidFromAce(_In_ PACE Ace,_In_ BOOLEAN IsAceDenied)180 SepGetObjectTypeGuidFromAce(
181     _In_ PACE Ace,
182     _In_ BOOLEAN IsAceDenied)
183 {
184     PGUID ObjectTypeGuid = NULL;
185 
186     PAGED_CODE();
187 
188     /* This Ace must not be NULL */
189     ASSERT(Ace);
190 
191     /* Grab the GUID based on the object type ACE header */
192     ObjectTypeGuid = IsAceDenied ? (PGUID)&((PACCESS_DENIED_OBJECT_ACE)Ace)->ObjectType :
193         (PGUID)&((PACCESS_ALLOWED_OBJECT_ACE)Ace)->ObjectType;
194     return ObjectTypeGuid;
195 }
196 
197 /**
198  * @brief
199  * Searches for an object type GUID if it exists
200  * on an object type list.
201  *
202  * @param[in] ObjectTypeList
203  * A pointer to an object type list.
204  *
205  * @param[in] ObjectTypeListLength
206  * The length of the list, representing the number
207  * of object elements in that list.
208  *
209  * @param[in] ObjectTypeGuid
210  * A pointer to an object type GUID to search in the
211  * list of interest.
212  *
213  * @param[out] ObjectIndex
214  * If the function found the target GUID, the function
215  * returns a pointer to the object index location to
216  * this parameter.
217  *
218  * @return
219  * Returns TRUE if the object type GUID of interest exists
220  * in the target list, FALSE otherwise.
221  */
222 BOOLEAN
SepObjectTypeGuidInList(_In_reads_ (ObjectTypeListLength)POBJECT_TYPE_LIST_INTERNAL ObjectTypeList,_In_ ULONG ObjectTypeListLength,_In_ PGUID ObjectTypeGuid,_Out_ PULONG ObjectIndex)223 SepObjectTypeGuidInList(
224     _In_reads_(ObjectTypeListLength) POBJECT_TYPE_LIST_INTERNAL ObjectTypeList,
225     _In_ ULONG ObjectTypeListLength,
226     _In_ PGUID ObjectTypeGuid,
227     _Out_ PULONG ObjectIndex)
228 {
229     ULONG ObjectTypeIndex;
230     GUID ObjectTypeGuidToSearch;
231 
232     PAGED_CODE();
233 
234     /* Loop over the object elements */
235     for (ObjectTypeIndex = 0;
236          ObjectTypeIndex < ObjectTypeListLength;
237          ObjectTypeIndex++)
238     {
239         /* Is this the object we are searching for? */
240         ObjectTypeGuidToSearch = ObjectTypeList[ObjectTypeIndex].ObjectTypeGuid;
241         if (SepIsEqualObjectTypeGuid(ObjectTypeGuid, &ObjectTypeGuidToSearch))
242         {
243             /* Return the indext of that object to caller */
244             *ObjectIndex = ObjectTypeIndex;
245             return TRUE;
246         }
247     }
248 
249     return FALSE;
250 }
251 
252 /**
253  * @brief
254  * Captures a list of object types and converts it to
255  * an internal form for use by the kernel. The list
256  * is validated before its data is copied.
257  *
258  * @param[in] ObjectTypeList
259  * A pointer to a list of object types passed from
260  * UM to be captured.
261  *
262  * @param[in] ObjectTypeListLength
263  * The length size of the list. This length represents
264  * the number of object elements in that list.
265  *
266  * @param[in] PreviousMode
267  * Processor access level mode. This has to be set to
268  * UserMode as object type access check is not supported
269  * in the kernel.
270  *
271  * @param[out] CapturedObjectTypeList
272  * A pointer to a returned captured list of object types.
273  *
274  * @return
275  * Returns STATUS_SUCCESS if the list of object types has been captured
276  * successfully. STATUS_INVALID_PARAMETER is returned if the caller hasn't
277  * supplied a buffer list of object types or the list is invalid.
278  * STATUS_INSUFFICIENT_RESOURCES is returned if pool memory allocation
279  * for the captured list has failed.
280  */
281 NTSTATUS
SeCaptureObjectTypeList(_In_reads_opt_ (ObjectTypeListLength)POBJECT_TYPE_LIST ObjectTypeList,_In_ ULONG ObjectTypeListLength,_In_ KPROCESSOR_MODE PreviousMode,_Out_ POBJECT_TYPE_LIST_INTERNAL * CapturedObjectTypeList)282 SeCaptureObjectTypeList(
283     _In_reads_opt_(ObjectTypeListLength) POBJECT_TYPE_LIST ObjectTypeList,
284     _In_ ULONG ObjectTypeListLength,
285     _In_ KPROCESSOR_MODE PreviousMode,
286     _Out_ POBJECT_TYPE_LIST_INTERNAL *CapturedObjectTypeList)
287 {
288     NTSTATUS Status;
289     ULONG ObjectTypeIndex;
290     SIZE_T Size;
291     PGUID ObjectTypeGuid;
292     POBJECT_TYPE_LIST_INTERNAL InternalTypeList;
293 
294     PAGED_CODE();
295 
296     /* We do not support that in the kernel */
297     if (PreviousMode == KernelMode)
298     {
299         return STATUS_NOT_IMPLEMENTED;
300     }
301 
302     /* No count elements of objects means no captured list for you */
303     if (ObjectTypeListLength == 0)
304     {
305         *CapturedObjectTypeList = NULL;
306         return STATUS_SUCCESS;
307     }
308 
309     /* Check if the caller supplied a list since we have the count of elements */
310     if (ObjectTypeList == NULL)
311     {
312         DPRINT1("The caller did not provide a list of object types!\n");
313         return STATUS_INVALID_PARAMETER;
314     }
315 
316     /* Validate that list before we copy contents from it */
317     Status = SepValidateObjectTypeList(ObjectTypeList, ObjectTypeListLength);
318     if (!NT_SUCCESS(Status))
319     {
320         DPRINT1("SepValidateObjectTypeList failed (Status 0x%08lx)\n", Status);
321         return Status;
322     }
323 
324     /* Allocate a new list */
325     Size = ObjectTypeListLength * sizeof(OBJECT_TYPE_LIST_INTERNAL);
326     InternalTypeList = ExAllocatePoolWithTag(PagedPool, Size, TAG_SEPA);
327     if (InternalTypeList == NULL)
328     {
329         DPRINT1("Failed to allocate pool memory for the object type list!\n");
330         return STATUS_INSUFFICIENT_RESOURCES;
331     }
332 
333     _SEH2_TRY
334     {
335         /* Loop for every object element, data was already probed */
336         for (ObjectTypeIndex = 0;
337              ObjectTypeIndex < ObjectTypeListLength;
338              ObjectTypeIndex++)
339         {
340             /* Copy the object type GUID */
341             ObjectTypeGuid = ObjectTypeList[ObjectTypeIndex].ObjectType;
342             InternalTypeList[ObjectTypeIndex].ObjectTypeGuid = *ObjectTypeGuid;
343 
344             /* Copy the object hierarchy level */
345             InternalTypeList[ObjectTypeIndex].Level = ObjectTypeList[ObjectTypeIndex].Level;
346 
347             /* Initialize the access check rights */
348             InternalTypeList[ObjectTypeIndex].ObjectAccessRights.RemainingAccessRights = 0;
349             InternalTypeList[ObjectTypeIndex].ObjectAccessRights.GrantedAccessRights = 0;
350             InternalTypeList[ObjectTypeIndex].ObjectAccessRights.DeniedAccessRights = 0;
351         }
352 
353         /* Give the captured list to caller */
354         *CapturedObjectTypeList = InternalTypeList;
355     }
356     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
357     {
358         ExFreePoolWithTag(InternalTypeList, TAG_SEPA);
359         InternalTypeList = NULL;
360         _SEH2_YIELD(return _SEH2_GetExceptionCode());
361     }
362     _SEH2_END;
363 
364     return STATUS_SUCCESS;
365 }
366 
367 /**
368  * @brief
369  * Releases a buffer list of object types.
370  *
371  * @param[in] CapturedObjectTypeList
372  * A list of object types to free.
373  *
374  * @param[in] PreviousMode
375  * Processor access level mode.
376  */
377 VOID
SeReleaseObjectTypeList(_In_ _Post_invalid_ POBJECT_TYPE_LIST_INTERNAL CapturedObjectTypeList,_In_ KPROCESSOR_MODE PreviousMode)378 SeReleaseObjectTypeList(
379     _In_  _Post_invalid_ POBJECT_TYPE_LIST_INTERNAL CapturedObjectTypeList,
380     _In_ KPROCESSOR_MODE PreviousMode)
381 {
382     PAGED_CODE();
383 
384     if ((PreviousMode != KernelMode) && (CapturedObjectTypeList != NULL))
385     {
386         ExFreePoolWithTag(CapturedObjectTypeList, TAG_SEPA);
387     }
388 }
389 
390 /* EOF */
391