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 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 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 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 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 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 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