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