1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS kernel 4 * FILE: ntoskrnl/ob/sdcache.c 5 * PURPOSE: No purpose listed. 6 * 7 * PROGRAMMERS: David Welch (welch@cwcom.net) 8 */ 9 10 /* INCLUDES *******************************************************************/ 11 12 #include <ntoskrnl.h> 13 #define NDEBUG 14 #include <debug.h> 15 16 /* GLOBALS ********************************************************************/ 17 18 #define SD_CACHE_ENTRIES 0x100 19 OB_SD_CACHE_LIST ObsSecurityDescriptorCache[SD_CACHE_ENTRIES]; 20 21 /* PRIVATE FUNCTIONS **********************************************************/ 22 23 VOID 24 FORCEINLINE 25 ObpSdAcquireLock(IN POB_SD_CACHE_LIST CacheEntry) 26 { 27 /* Acquire the lock */ 28 KeEnterCriticalRegion(); 29 ExAcquirePushLockExclusive(&CacheEntry->PushLock); 30 } 31 32 VOID 33 FORCEINLINE 34 ObpSdReleaseLock(IN POB_SD_CACHE_LIST CacheEntry) 35 { 36 /* Release the lock */ 37 ExReleasePushLockExclusive(&CacheEntry->PushLock); 38 KeLeaveCriticalRegion(); 39 } 40 41 VOID 42 FORCEINLINE 43 ObpSdAcquireLockShared(IN POB_SD_CACHE_LIST CacheEntry) 44 { 45 /* Acquire the lock */ 46 KeEnterCriticalRegion(); 47 ExAcquirePushLockShared(&CacheEntry->PushLock); 48 } 49 50 VOID 51 FORCEINLINE 52 ObpSdReleaseLockShared(IN POB_SD_CACHE_LIST CacheEntry) 53 { 54 /* Release the lock */ 55 ExReleasePushLock(&CacheEntry->PushLock); 56 KeLeaveCriticalRegion(); 57 } 58 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((PULONG_PTR)&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 ppb = (PUCHAR)((ULONG_PTR)Buffer + Length); 89 pp = (PULONG)ALIGN_DOWN(p + 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, TAG('O', 'b', 'S', 'c')); 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 185 /* Get the SD */ 186 SecurityDescriptor = ObjectHeader->SecurityDescriptor; 187 if (!SecurityDescriptor) 188 { 189 /* No SD, nothing to do */ 190 return NULL; 191 } 192 193 /* Lock the object */ 194 ObpAcquireObjectLockShared(ObjectHeader); 195 196 /* Get the object header */ 197 SdHeader = ObpGetHeaderForSd(SecurityDescriptor); 198 199 /* Do the reference */ 200 InterlockedIncrement((PLONG)&SdHeader->RefCount); 201 202 /* Release the lock and return */ 203 ObpReleaseObjectLock(ObjectHeader); 204 return SecurityDescriptor; 205 } 206 207 /* PUBLIC FUNCTIONS ***********************************************************/ 208 209 /*++ 210 * @name ObReferenceSecurityDescriptor 211 * @implemented NT5.2 212 * 213 * The ObReferenceSecurityDescriptor routine <FILLMEIN> 214 * 215 * @param SecurityDescriptor 216 * <FILLMEIN> 217 * 218 * @param Count 219 * <FILLMEIN> 220 * 221 * @return STATUS_SUCCESS or appropriate error value. 222 * 223 * @remarks None. 224 * 225 *--*/ 226 VOID 227 NTAPI 228 ObReferenceSecurityDescriptor(IN PSECURITY_DESCRIPTOR SecurityDescriptor, 229 IN ULONG Count) 230 { 231 PSECURITY_DESCRIPTOR_HEADER SdHeader; 232 233 /* Get the header */ 234 SdHeader = ObpGetHeaderForSd(SecurityDescriptor); 235 236 /* Do the references */ 237 InterlockedExchangeAdd((PLONG)&SdHeader->RefCount, Count); 238 } 239 240 /*++ 241 * @name ObDereferenceSecurityDescriptor 242 * @implemented NT5.2 243 * 244 * The ObDereferenceSecurityDescriptor routine <FILLMEIN> 245 * 246 * @param SecurityDescriptor 247 * <FILLMEIN> 248 * 249 * @param Count 250 * <FILLMEIN> 251 * 252 * @return STATUS_SUCCESS or appropriate error value. 253 * 254 * @remarks None. 255 * 256 *--*/ 257 VOID 258 NTAPI 259 ObDereferenceSecurityDescriptor(IN PSECURITY_DESCRIPTOR SecurityDescriptor, 260 IN ULONG Count) 261 { 262 PSECURITY_DESCRIPTOR_HEADER SdHeader; 263 LONG OldValue, NewValue; 264 ULONG Index; 265 POB_SD_CACHE_LIST CacheEntry; 266 267 /* Get the header */ 268 SdHeader = ObpGetHeaderForSd(SecurityDescriptor); 269 270 /* Get the current reference count */ 271 OldValue = SdHeader->RefCount; 272 273 /* Check if the caller is destroying this SD -- we need the lock for that */ 274 while (OldValue != Count) 275 { 276 /* He isn't, we can just try to derefeference atomically */ 277 NewValue = InterlockedCompareExchange((PLONG)&SdHeader->RefCount, 278 OldValue - Count, 279 OldValue); 280 if (NewValue == OldValue) return; 281 282 /* Try again */ 283 OldValue = NewValue; 284 } 285 286 /* At this point, we need the lock, so choose an entry */ 287 Index = SdHeader->FullHash % SD_CACHE_ENTRIES; 288 CacheEntry = &ObsSecurityDescriptorCache[Index]; 289 290 /* Acquire the lock for it */ 291 ObpSdAcquireLock(CacheEntry); 292 ASSERT(SdHeader->RefCount != 0); 293 294 /* Now do the dereference */ 295 if (InterlockedExchangeAdd((PLONG)&SdHeader->RefCount, -(LONG)Count) == Count) 296 { 297 /* We're down to zero -- destroy the header */ 298 SdHeader = ObpDestroySecurityDescriptorHeader(SdHeader); 299 300 /* Release the lock */ 301 ObpSdReleaseLock(CacheEntry); 302 303 /* Free the header */ 304 ExFreePool(SdHeader); 305 } 306 else 307 { 308 /* Just release the lock */ 309 ObpSdReleaseLock(CacheEntry); 310 } 311 312 } 313 314 /*++ 315 * @name ObLogSecurityDescriptor 316 * @implemented NT5.2 317 * 318 * The ObLogSecurityDescriptor routine <FILLMEIN> 319 * 320 * @param InputSecurityDescriptor 321 * <FILLMEIN> 322 * 323 * @param OutputSecurityDescriptor 324 * <FILLMEIN> 325 * 326 * @param RefBias 327 * <FILLMEIN> 328 * 329 * @return STATUS_SUCCESS or appropriate error value. 330 * 331 * @remarks None. 332 * 333 *--*/ 334 NTSTATUS 335 NTAPI 336 ObLogSecurityDescriptor(IN PSECURITY_DESCRIPTOR InputSecurityDescriptor, 337 OUT PSECURITY_DESCRIPTOR *OutputSecurityDescriptor, 338 IN ULONG RefBias) 339 { 340 PSECURITY_DESCRIPTOR_HEADER SdHeader = NULL, NewHeader = NULL; 341 ULONG Length, Hash, Index; 342 POB_SD_CACHE_LIST CacheEntry; 343 BOOLEAN Result; 344 PLIST_ENTRY NextEntry; 345 346 /* Get the length */ 347 Length = RtlLengthSecurityDescriptor(InputSecurityDescriptor); 348 349 /* Get the hash */ 350 Hash = ObpHashSecurityDescriptor(InputSecurityDescriptor, Length); 351 352 /* Now select the appropriate cache entry */ 353 Index = Hash % SD_CACHE_ENTRIES; 354 CacheEntry = &ObsSecurityDescriptorCache[Index]; 355 356 /* Lock it shared */ 357 ObpSdAcquireLockShared(CacheEntry); 358 359 /* Start our search */ 360 while (TRUE) 361 { 362 /* Reset result found */ 363 Result = FALSE; 364 365 /* Loop the hash list */ 366 NextEntry = CacheEntry->Head.Flink; 367 while (NextEntry != &CacheEntry->Head) 368 { 369 /* Get the header */ 370 SdHeader = ObpGetHeaderForEntry(NextEntry); 371 372 /* Our hashes are ordered, so quickly check if we should stop now */ 373 if (SdHeader->FullHash > Hash) break; 374 375 /* We survived the quick hash check, now check for equalness */ 376 if (SdHeader->FullHash == Hash) 377 { 378 /* Hashes match, now compare descriptors */ 379 Result = ObpCompareSecurityDescriptors(InputSecurityDescriptor, 380 Length, 381 &SdHeader->SecurityDescriptor); 382 if (Result) break; 383 } 384 385 /* Go to the next entry */ 386 NextEntry = NextEntry->Flink; 387 } 388 389 /* Check if we found anything */ 390 if (Result) 391 { 392 /* Increment its reference count */ 393 InterlockedExchangeAdd((PLONG)&SdHeader->RefCount, RefBias); 394 395 /* Release the lock */ 396 ObpSdReleaseLockShared(CacheEntry); 397 398 /* Return the descriptor */ 399 *OutputSecurityDescriptor = &SdHeader->SecurityDescriptor; 400 401 /* Free anything that we may have had to create */ 402 if (NewHeader) ExFreePool(NewHeader); 403 return STATUS_SUCCESS; 404 } 405 406 /* Check if we got here, and didn't create a descriptor yet */ 407 if (!NewHeader) 408 { 409 /* Release the lock */ 410 ObpSdReleaseLockShared(CacheEntry); 411 412 /* This should be our first time in the loop, create it */ 413 NewHeader = ObpCreateCacheEntry(InputSecurityDescriptor, 414 Length, 415 Hash, 416 RefBias); 417 if (!NewHeader) return STATUS_INSUFFICIENT_RESOURCES; 418 419 /* Now acquire the exclusive lock and we should hit the right path */ 420 ObpSdAcquireLock(CacheEntry); 421 } 422 else 423 { 424 /* We have inserted the SD, we're fine now */ 425 break; 426 } 427 } 428 429 /* Okay, now let's do the insert, we should have the exclusive lock */ 430 InsertTailList(NextEntry, &NewHeader->Link); 431 432 /* Release the lock */ 433 ObpSdReleaseLock(CacheEntry); 434 435 /* Return the SD*/ 436 *OutputSecurityDescriptor = &NewHeader->SecurityDescriptor; 437 return STATUS_SUCCESS; 438 } 439 440 /* EOF */ 441