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