1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/ob/obsdcach.c 5 * PURPOSE: Security Descriptor Caching 6 * PROGRAMMERS: Aleksey Bragin (aleksey@reactos.org) 7 */ 8 9 /* INCLUDES *******************************************************************/ 10 11 #include <ntoskrnl.h> 12 #define NDEBUG 13 #include <debug.h> 14 15 /* GLOBALS ********************************************************************/ 16 17 #define SD_CACHE_ENTRIES 0x100 18 OB_SD_CACHE_LIST ObsSecurityDescriptorCache[SD_CACHE_ENTRIES]; 19 20 /* PRIVATE FUNCTIONS **********************************************************/ 21 22 FORCEINLINE 23 VOID 24 ObpSdAcquireLock(IN POB_SD_CACHE_LIST CacheEntry) 25 { 26 /* Acquire the lock */ 27 KeEnterCriticalRegion(); 28 ExAcquirePushLockExclusive(&CacheEntry->PushLock); 29 } 30 31 FORCEINLINE 32 VOID 33 ObpSdReleaseLock(IN POB_SD_CACHE_LIST CacheEntry) 34 { 35 /* Release the lock */ 36 ExReleasePushLockExclusive(&CacheEntry->PushLock); 37 KeLeaveCriticalRegion(); 38 } 39 40 FORCEINLINE 41 VOID 42 ObpSdAcquireLockShared(IN POB_SD_CACHE_LIST CacheEntry) 43 { 44 /* Acquire the lock */ 45 KeEnterCriticalRegion(); 46 ExAcquirePushLockShared(&CacheEntry->PushLock); 47 } 48 49 FORCEINLINE 50 VOID 51 ObpSdReleaseLockShared(IN POB_SD_CACHE_LIST CacheEntry) 52 { 53 /* Release the lock */ 54 ExReleasePushLock(&CacheEntry->PushLock); 55 KeLeaveCriticalRegion(); 56 } 57 58 CODE_SEG("INIT") 59 NTSTATUS 60 NTAPI 61 ObpInitSdCache(VOID) 62 { 63 ULONG i; 64 65 /* Loop each cache entry */ 66 for (i = 0; i < SD_CACHE_ENTRIES; i++) 67 { 68 /* Initialize the lock and the list */ 69 InitializeListHead(&ObsSecurityDescriptorCache[i].Head); 70 ExInitializePushLock(&ObsSecurityDescriptorCache[i].PushLock); 71 } 72 73 /* Return success */ 74 return STATUS_SUCCESS; 75 } 76 77 ULONG 78 NTAPI 79 ObpHash(IN PVOID Buffer, 80 IN ULONG Length) 81 { 82 PULONG p, pp; 83 PUCHAR pb, ppb; 84 ULONG Hash = 0; 85 86 /* Setup aligned and byte buffers */ 87 p = Buffer; 88 pb = (PUCHAR)p; 89 ppb = (PUCHAR)((ULONG_PTR)Buffer + Length); 90 pp = (PULONG)ALIGN_DOWN(pb + Length, ULONG); 91 92 /* Loop aligned data */ 93 while (p < pp) 94 { 95 /* XOR-rotate */ 96 Hash ^= *p++; 97 Hash = _rotl(Hash, 3); 98 } 99 100 /* Loop non-aligned data */ 101 pb = (PUCHAR)p; 102 while (pb < ppb) 103 { 104 /* XOR-rotate */ 105 Hash ^= *pb++; 106 Hash = _rotl(Hash, 3); 107 } 108 109 /* Return the hash */ 110 return Hash; 111 } 112 113 ULONG 114 NTAPI 115 ObpHashSecurityDescriptor(IN PSECURITY_DESCRIPTOR SecurityDescriptor, 116 IN ULONG Length) 117 { 118 /* Just hash the entire SD */ 119 return ObpHash(SecurityDescriptor, Length); 120 } 121 122 PSECURITY_DESCRIPTOR_HEADER 123 NTAPI 124 ObpCreateCacheEntry(IN PSECURITY_DESCRIPTOR SecurityDescriptor, 125 IN ULONG Length, 126 IN ULONG FullHash, 127 IN ULONG RefCount) 128 { 129 ULONG CacheSize; 130 PSECURITY_DESCRIPTOR_HEADER SdHeader; 131 ASSERT(Length == RtlLengthSecurityDescriptor(SecurityDescriptor)); 132 133 /* Calculate the memory we'll need to allocate and allocate it */ 134 CacheSize = Length + (sizeof(SECURITY_DESCRIPTOR_HEADER) - sizeof(QUAD)); 135 SdHeader = ExAllocatePoolWithTag(PagedPool, CacheSize, TAG_OB_SD_CACHE); 136 if (!SdHeader) return NULL; 137 138 /* Setup the header */ 139 SdHeader->RefCount = RefCount; 140 SdHeader->FullHash = FullHash; 141 142 /* Copy the descriptor */ 143 RtlCopyMemory(&SdHeader->SecurityDescriptor, SecurityDescriptor, Length); 144 145 /* Return it */ 146 return SdHeader; 147 } 148 149 BOOLEAN 150 NTAPI 151 ObpCompareSecurityDescriptors(IN PSECURITY_DESCRIPTOR Sd1, 152 IN ULONG Length1, 153 IN PSECURITY_DESCRIPTOR Sd2) 154 { 155 ULONG Length2; 156 ASSERT(Length1 == RtlLengthSecurityDescriptor(Sd1)); 157 158 /* Get the length of the second SD */ 159 Length2 = RtlLengthSecurityDescriptor(Sd2); 160 161 /* Compare lengths */ 162 if (Length1 != Length2) return FALSE; 163 164 /* Compare contents */ 165 return RtlEqualMemory(Sd1, Sd2, Length1); 166 } 167 168 PVOID 169 NTAPI 170 ObpDestroySecurityDescriptorHeader(IN PSECURITY_DESCRIPTOR_HEADER SdHeader) 171 { 172 ASSERT(SdHeader->RefCount == 0); 173 174 /* Just unlink the SD and return it back to the caller */ 175 RemoveEntryList(&SdHeader->Link); 176 return SdHeader; 177 } 178 179 PSECURITY_DESCRIPTOR 180 NTAPI 181 ObpReferenceSecurityDescriptor(IN POBJECT_HEADER ObjectHeader) 182 { 183 PSECURITY_DESCRIPTOR SecurityDescriptor; 184 PSECURITY_DESCRIPTOR_HEADER SdHeader; 185 PEX_FAST_REF FastRef; 186 EX_FAST_REF OldValue; 187 ULONG_PTR Count; 188 189 /* Acquire a reference to the security descriptor */ 190 FastRef = (PEX_FAST_REF)&ObjectHeader->SecurityDescriptor; 191 OldValue = ExAcquireFastReference(FastRef); 192 193 /* Get the descriptor and reference count */ 194 SecurityDescriptor = ExGetObjectFastReference(OldValue); 195 Count = ExGetCountFastReference(OldValue); 196 197 /* Check if there's no descriptor or if there's still cached references */ 198 if ((Count >= 1) || !(SecurityDescriptor)) 199 { 200 /* Check if this is the last reference */ 201 if (Count == 1) 202 { 203 /* Add the extra references that we'll take */ 204 SdHeader = ObpGetHeaderForSd(SecurityDescriptor); 205 InterlockedExchangeAdd((PLONG)&SdHeader->RefCount, MAX_FAST_REFS); 206 207 /* Now insert them */ 208 if (!ExInsertFastReference(FastRef, SecurityDescriptor)) 209 { 210 /* Undo the references since we failed */ 211 InterlockedExchangeAdd((PLONG)&SdHeader->RefCount, 212 -MAX_FAST_REFS); 213 } 214 } 215 216 /* Return the SD */ 217 return SecurityDescriptor; 218 } 219 220 /* Lock the object */ 221 ObpAcquireObjectLockShared(ObjectHeader); 222 223 /* Get the object header */ 224 SecurityDescriptor = ExGetObjectFastReference(*FastRef); 225 SdHeader = ObpGetHeaderForSd(SecurityDescriptor); 226 227 /* Do the reference */ 228 InterlockedIncrement((PLONG)&SdHeader->RefCount); 229 230 /* Release the lock and return */ 231 ObpReleaseObjectLock(ObjectHeader); 232 return SecurityDescriptor; 233 } 234 235 /* PUBLIC FUNCTIONS ***********************************************************/ 236 237 /*++ 238 * @name ObReferenceSecurityDescriptor 239 * @implemented NT5.2 240 * 241 * The ObReferenceSecurityDescriptor routine <FILLMEIN> 242 * 243 * @param SecurityDescriptor 244 * <FILLMEIN> 245 * 246 * @param Count 247 * <FILLMEIN> 248 * 249 * @return STATUS_SUCCESS or appropriate error value. 250 * 251 * @remarks None. 252 * 253 *--*/ 254 VOID 255 NTAPI 256 ObReferenceSecurityDescriptor(IN PSECURITY_DESCRIPTOR SecurityDescriptor, 257 IN ULONG Count) 258 { 259 PSECURITY_DESCRIPTOR_HEADER SdHeader; 260 261 /* Get the header */ 262 SdHeader = ObpGetHeaderForSd(SecurityDescriptor); 263 264 /* Do the references */ 265 InterlockedExchangeAdd((PLONG)&SdHeader->RefCount, Count); 266 } 267 268 /*++ 269 * @name ObDereferenceSecurityDescriptor 270 * @implemented NT5.2 271 * 272 * The ObDereferenceSecurityDescriptor routine <FILLMEIN> 273 * 274 * @param SecurityDescriptor 275 * <FILLMEIN> 276 * 277 * @param Count 278 * <FILLMEIN> 279 * 280 * @return STATUS_SUCCESS or appropriate error value. 281 * 282 * @remarks None. 283 * 284 *--*/ 285 VOID 286 NTAPI 287 ObDereferenceSecurityDescriptor(IN PSECURITY_DESCRIPTOR SecurityDescriptor, 288 IN ULONG Count) 289 { 290 PSECURITY_DESCRIPTOR_HEADER SdHeader; 291 LONG OldValue, NewValue; 292 ULONG Index; 293 POB_SD_CACHE_LIST CacheEntry; 294 295 /* Get the header */ 296 SdHeader = ObpGetHeaderForSd(SecurityDescriptor); 297 298 /* Get the current reference count */ 299 OldValue = SdHeader->RefCount; 300 301 /* Check if the caller is destroying this SD -- we need the lock for that */ 302 while (OldValue != Count) 303 { 304 /* He isn't, we can just try to derefeference atomically */ 305 NewValue = InterlockedCompareExchange((PLONG)&SdHeader->RefCount, 306 OldValue - Count, 307 OldValue); 308 if (NewValue == OldValue) return; 309 310 /* Try again */ 311 OldValue = NewValue; 312 } 313 314 /* At this point, we need the lock, so choose an entry */ 315 Index = SdHeader->FullHash % SD_CACHE_ENTRIES; 316 CacheEntry = &ObsSecurityDescriptorCache[Index]; 317 318 /* Acquire the lock for it */ 319 ObpSdAcquireLock(CacheEntry); 320 ASSERT(SdHeader->RefCount != 0); 321 322 /* Now do the dereference */ 323 if (InterlockedExchangeAdd((PLONG)&SdHeader->RefCount, -(LONG)Count) == Count) 324 { 325 /* We're down to zero -- destroy the header */ 326 SdHeader = ObpDestroySecurityDescriptorHeader(SdHeader); 327 328 /* Release the lock */ 329 ObpSdReleaseLock(CacheEntry); 330 331 /* Free the header */ 332 ExFreePool(SdHeader); 333 } 334 else 335 { 336 /* Just release the lock */ 337 ObpSdReleaseLock(CacheEntry); 338 } 339 340 } 341 342 /*++ 343 * @name ObLogSecurityDescriptor 344 * @implemented NT5.2 345 * 346 * The ObLogSecurityDescriptor routine <FILLMEIN> 347 * 348 * @param InputSecurityDescriptor 349 * <FILLMEIN> 350 * 351 * @param OutputSecurityDescriptor 352 * <FILLMEIN> 353 * 354 * @param RefBias 355 * <FILLMEIN> 356 * 357 * @return STATUS_SUCCESS or appropriate error value. 358 * 359 * @remarks None. 360 * 361 *--*/ 362 NTSTATUS 363 NTAPI 364 ObLogSecurityDescriptor(IN PSECURITY_DESCRIPTOR InputSecurityDescriptor, 365 OUT PSECURITY_DESCRIPTOR *OutputSecurityDescriptor, 366 IN ULONG RefBias) 367 { 368 PSECURITY_DESCRIPTOR_HEADER SdHeader = NULL, NewHeader = NULL; 369 ULONG Length, Hash, Index; 370 POB_SD_CACHE_LIST CacheEntry; 371 BOOLEAN Result; 372 PLIST_ENTRY NextEntry; 373 374 /* Get the length */ 375 Length = RtlLengthSecurityDescriptor(InputSecurityDescriptor); 376 377 /* Get the hash */ 378 Hash = ObpHashSecurityDescriptor(InputSecurityDescriptor, Length); 379 380 /* Now select the appropriate cache entry */ 381 Index = Hash % SD_CACHE_ENTRIES; 382 CacheEntry = &ObsSecurityDescriptorCache[Index]; 383 384 /* Lock it shared */ 385 ObpSdAcquireLockShared(CacheEntry); 386 387 /* Start our search */ 388 while (TRUE) 389 { 390 /* Reset result found */ 391 Result = FALSE; 392 393 /* Loop the hash list */ 394 NextEntry = CacheEntry->Head.Flink; 395 while (NextEntry != &CacheEntry->Head) 396 { 397 /* Get the header */ 398 SdHeader = ObpGetHeaderForEntry(NextEntry); 399 400 /* Our hashes are ordered, so quickly check if we should stop now */ 401 if (SdHeader->FullHash > Hash) break; 402 403 /* We survived the quick hash check, now check for equalness */ 404 if (SdHeader->FullHash == Hash) 405 { 406 /* Hashes match, now compare descriptors */ 407 Result = ObpCompareSecurityDescriptors(InputSecurityDescriptor, 408 Length, 409 &SdHeader->SecurityDescriptor); 410 if (Result) break; 411 } 412 413 /* Go to the next entry */ 414 NextEntry = NextEntry->Flink; 415 } 416 417 /* Check if we found anything */ 418 if (Result) 419 { 420 /* Increment its reference count */ 421 InterlockedExchangeAdd((PLONG)&SdHeader->RefCount, RefBias); 422 423 /* Release the lock */ 424 ObpSdReleaseLockShared(CacheEntry); 425 426 /* Return the descriptor */ 427 *OutputSecurityDescriptor = &SdHeader->SecurityDescriptor; 428 429 /* Free anything that we may have had to create */ 430 if (NewHeader) ExFreePoolWithTag(NewHeader, TAG_OB_SD_CACHE); 431 return STATUS_SUCCESS; 432 } 433 434 /* Check if we got here, and didn't create a descriptor yet */ 435 if (!NewHeader) 436 { 437 /* Release the lock */ 438 ObpSdReleaseLockShared(CacheEntry); 439 440 /* This should be our first time in the loop, create it */ 441 NewHeader = ObpCreateCacheEntry(InputSecurityDescriptor, 442 Length, 443 Hash, 444 RefBias); 445 if (!NewHeader) return STATUS_INSUFFICIENT_RESOURCES; 446 447 /* Now acquire the exclusive lock and we should hit the right path */ 448 ObpSdAcquireLock(CacheEntry); 449 } 450 else 451 { 452 /* We have inserted the SD, we're fine now */ 453 break; 454 } 455 } 456 457 /* Okay, now let's do the insert, we should have the exclusive lock */ 458 InsertTailList(NextEntry, &NewHeader->Link); 459 460 /* Release the lock */ 461 ObpSdReleaseLock(CacheEntry); 462 463 /* Return the SD*/ 464 *OutputSecurityDescriptor = &NewHeader->SecurityDescriptor; 465 return STATUS_SUCCESS; 466 } 467 468 /* EOF */ 469