xref: /reactos/ntoskrnl/config/cmparse.c (revision 76f1da56)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/config/cmparse.c
5  * PURPOSE:         Configuration Manager - Object Manager Parse Interface
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include "ntoskrnl.h"
12 #define NDEBUG
13 #include "debug.h"
14 
15 /* GLOBALS *******************************************************************/
16 
17 /* FUNCTIONS *****************************************************************/
18 
19 BOOLEAN
20 NTAPI
CmpGetNextName(IN OUT PUNICODE_STRING RemainingName,OUT PUNICODE_STRING NextName,OUT PBOOLEAN LastName)21 CmpGetNextName(IN OUT PUNICODE_STRING RemainingName,
22                OUT PUNICODE_STRING NextName,
23                OUT PBOOLEAN LastName)
24 {
25     BOOLEAN NameValid = TRUE;
26 
27     ASSERT(RemainingName->Length % sizeof(WCHAR) == 0);
28 
29     /* Check if there's nothing left in the name */
30     if (!(RemainingName->Buffer) ||
31         (!RemainingName->Length) ||
32         !(*RemainingName->Buffer))
33     {
34         /* Clear the next name and set this as last */
35         *LastName = TRUE;
36         NextName->Buffer = NULL;
37         NextName->Length = 0;
38         return TRUE;
39     }
40 
41     /* Check if we have a path separator */
42     while (RemainingName->Length &&
43            (*RemainingName->Buffer == OBJ_NAME_PATH_SEPARATOR))
44     {
45         /* Skip it */
46         RemainingName->Buffer++;
47         RemainingName->Length -= sizeof(WCHAR);
48         RemainingName->MaximumLength -= sizeof(WCHAR);
49     }
50 
51     /* Start loop at where the current buffer is */
52     NextName->Buffer = RemainingName->Buffer;
53     while (RemainingName->Length &&
54            (*RemainingName->Buffer != OBJ_NAME_PATH_SEPARATOR))
55     {
56         /* Move to the next character */
57         RemainingName->Buffer++;
58         RemainingName->Length -= sizeof(WCHAR);
59         RemainingName->MaximumLength -= sizeof(WCHAR);
60     }
61 
62     /* See how many chars we parsed and validate the length */
63     NextName->Length = (USHORT)((ULONG_PTR)RemainingName->Buffer -
64                                 (ULONG_PTR)NextName->Buffer);
65     if (NextName->Length > 512) NameValid = FALSE;
66     NextName->MaximumLength = NextName->Length;
67 
68     /* If there's nothing left, we're last */
69     *LastName = !RemainingName->Length;
70     return NameValid;
71 }
72 
73 BOOLEAN
74 NTAPI
CmpGetSymbolicLink(IN PHHIVE Hive,IN OUT PUNICODE_STRING ObjectName,IN OUT PCM_KEY_CONTROL_BLOCK SymbolicKcb,IN PUNICODE_STRING RemainingName OPTIONAL)75 CmpGetSymbolicLink(IN PHHIVE Hive,
76                    IN OUT PUNICODE_STRING ObjectName,
77                    IN OUT PCM_KEY_CONTROL_BLOCK SymbolicKcb,
78                    IN PUNICODE_STRING RemainingName OPTIONAL)
79 {
80     HCELL_INDEX LinkCell = HCELL_NIL;
81     PCM_KEY_VALUE LinkValue = NULL;
82     PWSTR LinkName = NULL;
83     BOOLEAN LinkNameAllocated = FALSE;
84     PWSTR NewBuffer;
85     ULONG Length = 0;
86     ULONG ValueLength = 0;
87     BOOLEAN Result = FALSE;
88     HCELL_INDEX CellToRelease = HCELL_NIL;
89     PCM_KEY_NODE Node;
90     UNICODE_STRING NewObjectName;
91 
92     /* Make sure we're not being deleted */
93     if (SymbolicKcb->Delete) return FALSE;
94 
95     /* Get the key node */
96     Node = (PCM_KEY_NODE)HvGetCell(SymbolicKcb->KeyHive, SymbolicKcb->KeyCell);
97     if (!Node) goto Exit;
98 
99     /* Find the symbolic link key */
100     LinkCell = CmpFindValueByName(Hive, Node, &CmSymbolicLinkValueName);
101     HvReleaseCell(SymbolicKcb->KeyHive, SymbolicKcb->KeyCell);
102     if (LinkCell == HCELL_NIL) goto Exit;
103 
104     /* Get the value cell */
105     LinkValue = (PCM_KEY_VALUE)HvGetCell(Hive, LinkCell);
106     if (!LinkValue) goto Exit;
107 
108     /* Make sure it's a registry link */
109     if (LinkValue->Type != REG_LINK) goto Exit;
110 
111     /* Now read the value data */
112     if (!CmpGetValueData(Hive,
113                          LinkValue,
114                          &ValueLength,
115                          (PVOID*)&LinkName,
116                          &LinkNameAllocated,
117                          &CellToRelease))
118     {
119         /* Fail */
120         goto Exit;
121     }
122 
123     /* Get the length */
124     Length = ValueLength + sizeof(WCHAR);
125 
126     /* Make sure we start with a slash */
127     if (*LinkName != OBJ_NAME_PATH_SEPARATOR) goto Exit;
128 
129     /* Add the remaining name if needed */
130     if (RemainingName) Length += RemainingName->Length + sizeof(WCHAR);
131 
132     /* Check for overflow */
133     if (Length > 0xFFFF) goto Exit;
134 
135     /* Check if we need a new buffer */
136     if (Length > ObjectName->MaximumLength)
137     {
138         /* We do -- allocate one */
139         NewBuffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_CM);
140         if (!NewBuffer) goto Exit;
141 
142         /* Setup the new string and copy the symbolic target */
143         NewObjectName.Buffer = NewBuffer;
144         NewObjectName.MaximumLength = (USHORT)Length;
145         NewObjectName.Length = (USHORT)ValueLength;
146         RtlCopyMemory(NewBuffer, LinkName, ValueLength);
147 
148         /* Check if we need to add anything else */
149         if (RemainingName)
150         {
151             /* Add the remaining name */
152             NewBuffer[ValueLength / sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
153             NewObjectName.Length += sizeof(WCHAR);
154             RtlAppendUnicodeStringToString(&NewObjectName, RemainingName);
155         }
156 
157         /* Free the old buffer */
158         ExFreePool(ObjectName->Buffer);
159         *ObjectName = NewObjectName;
160     }
161     else
162     {
163         /* The old name is large enough -- update the length */
164         ObjectName->Length = (USHORT)ValueLength;
165         if (RemainingName)
166         {
167             /* Copy the remaining name inside */
168             RtlMoveMemory(&ObjectName->Buffer[(ValueLength / sizeof(WCHAR)) + 1],
169                           RemainingName->Buffer,
170                           RemainingName->Length);
171 
172             /* Add the slash and update the length */
173             ObjectName->Buffer[ValueLength / sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
174             ObjectName->Length += RemainingName->Length + sizeof(WCHAR);
175         }
176 
177         /* Copy the symbolic link target name */
178         RtlCopyMemory(ObjectName->Buffer, LinkName, ValueLength);
179     }
180 
181     /* Null-terminate the whole thing */
182     ObjectName->Buffer[ObjectName->Length / sizeof(WCHAR)] = UNICODE_NULL;
183     Result = TRUE;
184 
185 Exit:
186     /* Free the link name */
187     if (LinkNameAllocated) ExFreePool(LinkName);
188 
189     /* Check if we had a value cell */
190     if (LinkValue)
191     {
192         /* Release it */
193         ASSERT(LinkCell != HCELL_NIL);
194         HvReleaseCell(Hive, LinkCell);
195     }
196 
197     /* Check if we had an active cell and release it, then return the result */
198     if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);
199     return Result;
200 }
201 
202 NTSTATUS
203 NTAPI
CmpDoCreateChild(IN PHHIVE Hive,IN HCELL_INDEX ParentCell,IN PSECURITY_DESCRIPTOR ParentDescriptor OPTIONAL,IN PACCESS_STATE AccessState,IN PUNICODE_STRING Name,IN KPROCESSOR_MODE AccessMode,IN PCM_PARSE_CONTEXT ParseContext,IN PCM_KEY_CONTROL_BLOCK ParentKcb,IN ULONG Flags,OUT PHCELL_INDEX KeyCell,OUT PVOID * Object)204 CmpDoCreateChild(IN PHHIVE Hive,
205                  IN HCELL_INDEX ParentCell,
206                  IN PSECURITY_DESCRIPTOR ParentDescriptor OPTIONAL,
207                  IN PACCESS_STATE AccessState,
208                  IN PUNICODE_STRING Name,
209                  IN KPROCESSOR_MODE AccessMode,
210                  IN PCM_PARSE_CONTEXT ParseContext,
211                  IN PCM_KEY_CONTROL_BLOCK ParentKcb,
212                  IN ULONG Flags,
213                  OUT PHCELL_INDEX KeyCell,
214                  OUT PVOID *Object)
215 {
216     NTSTATUS Status = STATUS_SUCCESS;
217     PCM_KEY_BODY KeyBody;
218     HCELL_INDEX ClassCell = HCELL_NIL;
219     PCM_KEY_NODE KeyNode;
220     PCELL_DATA CellData;
221     ULONG StorageType;
222     PCM_KEY_CONTROL_BLOCK Kcb;
223     PSECURITY_DESCRIPTOR NewDescriptor;
224 
225     /* Get the storage type */
226     StorageType = Stable;
227     if (ParseContext->CreateOptions & REG_OPTION_VOLATILE) StorageType = Volatile;
228 
229     /* Allocate the child */
230     *KeyCell = HvAllocateCell(Hive,
231                               FIELD_OFFSET(CM_KEY_NODE, Name) +
232                               CmpNameSize(Hive, Name),
233                               StorageType,
234                               HCELL_NIL);
235     if (*KeyCell == HCELL_NIL)
236     {
237         /* Fail */
238         Status = STATUS_INSUFFICIENT_RESOURCES;
239         goto Quickie;
240     }
241 
242     /* Get the key node */
243     KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, *KeyCell);
244     if (!KeyNode)
245     {
246         /* Fail, this should never happen */
247         ASSERT(FALSE);
248         Status = STATUS_INSUFFICIENT_RESOURCES;
249         goto Quickie;
250     }
251 
252     /* Release the cell */
253     HvReleaseCell(Hive, *KeyCell);
254 
255     /* Check if we have a class name */
256     if (ParseContext->Class.Length > 0)
257     {
258         /* Allocate a class cell */
259         ClassCell = HvAllocateCell(Hive,
260                                    ParseContext->Class.Length,
261                                    StorageType,
262                                    HCELL_NIL);
263         if (ClassCell == HCELL_NIL)
264         {
265             /* Fail */
266             Status = STATUS_INSUFFICIENT_RESOURCES;
267             goto Quickie;
268         }
269     }
270 
271     /* Allocate the Cm Object */
272     Status = ObCreateObject(AccessMode,
273                             CmpKeyObjectType,
274                             NULL,
275                             AccessMode,
276                             NULL,
277                             sizeof(CM_KEY_BODY),
278                             0,
279                             0,
280                             Object);
281     if (!NT_SUCCESS(Status)) goto Quickie;
282 
283     /* Setup the key body */
284     KeyBody = (PCM_KEY_BODY)(*Object);
285     KeyBody->Type = CM_KEY_BODY_TYPE;
286     KeyBody->KeyControlBlock = NULL;
287     KeyBody->KcbLocked = FALSE;
288 
289     /* Check if we had a class */
290     if (ParseContext->Class.Length > 0)
291     {
292         /* Get the class cell */
293         CellData = HvGetCell(Hive, ClassCell);
294         if (!CellData)
295         {
296             /* Fail, this should never happen */
297             ASSERT(FALSE);
298             Status = STATUS_INSUFFICIENT_RESOURCES;
299             ObDereferenceObject(*Object);
300             goto Quickie;
301         }
302 
303         /* Release the cell */
304         HvReleaseCell(Hive, ClassCell);
305 
306         /* Copy the class data */
307         RtlCopyMemory(&CellData->u.KeyString[0],
308                       ParseContext->Class.Buffer,
309                       ParseContext->Class.Length);
310     }
311 
312     /* Fill out the key node */
313     KeyNode->Signature = CM_KEY_NODE_SIGNATURE;
314     KeyNode->Flags = Flags;
315     KeQuerySystemTime(&KeyNode->LastWriteTime);
316     KeyNode->Spare = 0;
317     KeyNode->Parent = ParentCell;
318     KeyNode->SubKeyCounts[Stable] = 0;
319     KeyNode->SubKeyCounts[Volatile] = 0;
320     KeyNode->SubKeyLists[Stable] = HCELL_NIL;
321     KeyNode->SubKeyLists[Volatile] = HCELL_NIL;
322     KeyNode->ValueList.Count = 0;
323     KeyNode->ValueList.List = HCELL_NIL;
324     KeyNode->Security = HCELL_NIL;
325     KeyNode->Class = ClassCell;
326     KeyNode->ClassLength = ParseContext->Class.Length;
327     KeyNode->MaxValueDataLen = 0;
328     KeyNode->MaxNameLen = 0;
329     KeyNode->MaxValueNameLen = 0;
330     KeyNode->MaxClassLen = 0;
331     KeyNode->NameLength = CmpCopyName(Hive, KeyNode->Name, Name);
332     if (KeyNode->NameLength < Name->Length) KeyNode->Flags |= KEY_COMP_NAME;
333 
334     /* Create the KCB */
335     Kcb = CmpCreateKeyControlBlock(Hive,
336                                    *KeyCell,
337                                    KeyNode,
338                                    ParentKcb,
339                                    CMP_LOCK_HASHES_FOR_KCB,
340                                    Name);
341     if (!Kcb)
342     {
343         /* Fail */
344         ObDereferenceObjectDeferDelete(*Object);
345         Status = STATUS_INSUFFICIENT_RESOURCES;
346         goto Quickie;
347     }
348 
349     /* Sanity check */
350     ASSERT(Kcb->RefCount == 1);
351 
352     /* Now fill out the Cm object */
353     KeyBody->NotifyBlock = NULL;
354     KeyBody->ProcessID = PsGetCurrentProcessId();
355     KeyBody->KeyControlBlock = Kcb;
356 
357     /* Link it with the KCB */
358     EnlistKeyBodyWithKCB(KeyBody, CMP_ENLIST_KCB_LOCKED_EXCLUSIVE);
359 
360     /* Assign security */
361     Status = SeAssignSecurity(ParentDescriptor,
362                               AccessState->SecurityDescriptor,
363                               &NewDescriptor,
364                               TRUE,
365                               &AccessState->SubjectSecurityContext,
366                               &CmpKeyObjectType->TypeInfo.GenericMapping,
367                               CmpKeyObjectType->TypeInfo.PoolType);
368     if (NT_SUCCESS(Status))
369     {
370         /*
371          * FIXME: We must acquire a security lock when assigning
372          * a security descriptor to this hive but since the
373          * CmpAssignSecurityDescriptor function does nothing
374          * (we lack the necessary security management implementations
375          * anyway), do not do anything for now.
376          */
377         Status = CmpAssignSecurityDescriptor(Kcb, NewDescriptor);
378     }
379 
380     /* Now that the security descriptor is copied in the hive, we can free the original */
381     SeDeassignSecurity(&NewDescriptor);
382 
383     if (NT_SUCCESS(Status))
384     {
385         /* Send notification to registered callbacks */
386         CmpReportNotify(Kcb, Hive, Kcb->KeyCell, REG_NOTIFY_CHANGE_NAME);
387     }
388 
389 Quickie:
390     /* Check if we got here because of failure */
391     if (!NT_SUCCESS(Status))
392     {
393         /* Free any cells we might've allocated */
394         if (ParseContext->Class.Length > 0) HvFreeCell(Hive, ClassCell);
395         HvFreeCell(Hive, *KeyCell);
396     }
397 
398     /* Return status */
399     return Status;
400 }
401 
402 NTSTATUS
403 NTAPI
CmpDoCreate(IN PHHIVE Hive,IN HCELL_INDEX Cell,IN PACCESS_STATE AccessState,IN PUNICODE_STRING Name,IN KPROCESSOR_MODE AccessMode,IN PCM_PARSE_CONTEXT ParseContext,IN PCM_KEY_CONTROL_BLOCK ParentKcb,OUT PVOID * Object)404 CmpDoCreate(IN PHHIVE Hive,
405             IN HCELL_INDEX Cell,
406             IN PACCESS_STATE AccessState,
407             IN PUNICODE_STRING Name,
408             IN KPROCESSOR_MODE AccessMode,
409             IN PCM_PARSE_CONTEXT ParseContext,
410             IN PCM_KEY_CONTROL_BLOCK ParentKcb,
411             OUT PVOID *Object)
412 {
413     NTSTATUS Status;
414     PCELL_DATA CellData;
415     HCELL_INDEX KeyCell;
416     ULONG ParentType;
417     PCM_KEY_BODY KeyBody;
418     PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
419     LARGE_INTEGER TimeStamp;
420     PCM_KEY_NODE KeyNode;
421 
422     /* Make sure the KCB is locked and lock the flusher */
423     CMP_ASSERT_KCB_LOCK(ParentKcb);
424     CmpLockHiveFlusherShared((PCMHIVE)Hive);
425 
426     /* Bail out on read-only KCBs */
427     if (ParentKcb->ExtFlags & CM_KCB_READ_ONLY_KEY)
428     {
429         Status = STATUS_ACCESS_DENIED;
430         goto Exit;
431     }
432 
433     /* Check if the parent is being deleted */
434     if (ParentKcb->Delete)
435     {
436         /* It has, quit */
437         ASSERT(FALSE);
438         Status = STATUS_OBJECT_NAME_NOT_FOUND;
439         goto Exit;
440     }
441 
442     /* Get the parent node */
443     KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
444     if (!KeyNode)
445     {
446         /* Fail */
447         ASSERT(FALSE);
448         Status = STATUS_INSUFFICIENT_RESOURCES;
449         goto Exit;
450     }
451 
452     /* Make sure nobody added us yet */
453     if (CmpFindSubKeyByName(Hive, KeyNode, Name) != HCELL_NIL)
454     {
455         /* Fail */
456         ASSERT(FALSE);
457         Status = STATUS_REPARSE;
458         goto Exit;
459     }
460 
461     /* Sanity check */
462     ASSERT(Cell == ParentKcb->KeyCell);
463 
464     /* Get the parent type */
465     ParentType = HvGetCellType(Cell);
466     if ((ParentType == Volatile) &&
467         !(ParseContext->CreateOptions & REG_OPTION_VOLATILE))
468     {
469         /* Children of volatile parents must also be volatile */
470         //ASSERT(FALSE);
471         Status = STATUS_CHILD_MUST_BE_VOLATILE;
472         goto Exit;
473     }
474 
475     /* Don't allow children under symlinks */
476     if (ParentKcb->Flags & KEY_SYM_LINK)
477     {
478         /* Fail */
479         ASSERT(FALSE);
480         Status = STATUS_ACCESS_DENIED;
481         goto Exit;
482     }
483 
484     /* Make the cell dirty for now */
485     HvMarkCellDirty(Hive, Cell, FALSE);
486 
487     /* Do the actual create operation */
488     Status = CmpDoCreateChild(Hive,
489                               Cell,
490                               SecurityDescriptor,
491                               AccessState,
492                               Name,
493                               AccessMode,
494                               ParseContext,
495                               ParentKcb,
496                               0,
497                               &KeyCell,
498                               Object);
499     if (NT_SUCCESS(Status))
500     {
501         /* Get the key body */
502         KeyBody = (PCM_KEY_BODY)(*Object);
503 
504         /* Now add the subkey */
505         if (!CmpAddSubKey(Hive, Cell, KeyCell))
506         {
507             /* Free the created child */
508             CmpFreeKeyByCell(Hive, KeyCell, FALSE);
509 
510             /* Purge out this KCB */
511             KeyBody->KeyControlBlock->Delete = TRUE;
512             CmpRemoveKeyControlBlock(KeyBody->KeyControlBlock);
513 
514             /* And cleanup the key body object */
515             ObDereferenceObjectDeferDelete(*Object);
516             Status = STATUS_INSUFFICIENT_RESOURCES;
517             goto Exit;
518         }
519 
520         /* Get the key node */
521         KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
522         if (!KeyNode)
523         {
524             /* Fail, this shouldn't happen */
525             CmpFreeKeyByCell(Hive, KeyCell, TRUE); // Subkey linked above
526 
527             /* Purge out this KCB */
528             KeyBody->KeyControlBlock->Delete = TRUE;
529             CmpRemoveKeyControlBlock(KeyBody->KeyControlBlock);
530 
531             /* And cleanup the key body object */
532             ObDereferenceObjectDeferDelete(*Object);
533             Status = STATUS_INSUFFICIENT_RESOURCES;
534             goto Exit;
535         }
536 
537         /* Clean up information on this subkey */
538         CmpCleanUpSubKeyInfo(KeyBody->KeyControlBlock->ParentKcb);
539 
540         /* Sanity checks */
541         ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyCell == Cell);
542         ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyHive == Hive);
543         ASSERT(KeyBody->KeyControlBlock->ParentKcb == ParentKcb);
544         ASSERT(KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen == KeyNode->MaxNameLen);
545 
546         /* Update the timestamp */
547         KeQuerySystemTime(&TimeStamp);
548         KeyNode->LastWriteTime = TimeStamp;
549         KeyBody->KeyControlBlock->ParentKcb->KcbLastWriteTime = TimeStamp;
550 
551         /* Check if we need to update name maximum */
552         if (KeyNode->MaxNameLen < Name->Length)
553         {
554             /* Do it */
555             KeyNode->MaxNameLen = Name->Length;
556             KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen = Name->Length;
557         }
558 
559         /* Check if we need to update class length maximum */
560         if (KeyNode->MaxClassLen < ParseContext->Class.Length)
561         {
562             /* Update it */
563             KeyNode->MaxClassLen = ParseContext->Class.Length;
564         }
565 
566         /* Check if we're creating a symbolic link */
567         if (ParseContext->CreateOptions & REG_OPTION_CREATE_LINK)
568         {
569             /* Get the cell data */
570             CellData = HvGetCell(Hive, KeyCell);
571             if (!CellData)
572             {
573                 /* This shouldn't happen */
574                 CmpFreeKeyByCell(Hive, KeyCell, TRUE); // Subkey linked above
575 
576                 /* Purge out this KCB */
577                 KeyBody->KeyControlBlock->Delete = TRUE;
578                 CmpRemoveKeyControlBlock(KeyBody->KeyControlBlock);
579 
580                 /* And cleanup the key body object */
581                 ObDereferenceObjectDeferDelete(*Object);
582                 Status = STATUS_INSUFFICIENT_RESOURCES;
583                 goto Exit;
584             }
585 
586             /* Update the flags */
587             CellData->u.KeyNode.Flags |= KEY_SYM_LINK;
588             KeyBody->KeyControlBlock->Flags = CellData->u.KeyNode.Flags;
589             HvReleaseCell(Hive, KeyCell);
590         }
591     }
592 
593 Exit:
594     /* Release the flusher lock and return status */
595     CmpUnlockHiveFlusher((PCMHIVE)Hive);
596     return Status;
597 }
598 
599 NTSTATUS
600 NTAPI
CmpDoOpen(IN PHHIVE Hive,IN HCELL_INDEX Cell,IN PCM_KEY_NODE Node,IN PACCESS_STATE AccessState,IN KPROCESSOR_MODE AccessMode,IN ULONG Attributes,IN PCM_PARSE_CONTEXT Context OPTIONAL,IN ULONG ControlFlags,IN OUT PCM_KEY_CONTROL_BLOCK * CachedKcb,IN PULONG KcbsLocked,IN PUNICODE_STRING KeyName,OUT PVOID * Object)601 CmpDoOpen(IN PHHIVE Hive,
602           IN HCELL_INDEX Cell,
603           IN PCM_KEY_NODE Node,
604           IN PACCESS_STATE AccessState,
605           IN KPROCESSOR_MODE AccessMode,
606           IN ULONG Attributes,
607           IN PCM_PARSE_CONTEXT Context OPTIONAL,
608           IN ULONG ControlFlags,
609           IN OUT PCM_KEY_CONTROL_BLOCK *CachedKcb,
610           IN PULONG KcbsLocked,
611           IN PUNICODE_STRING KeyName,
612           OUT PVOID *Object)
613 {
614     NTSTATUS Status;
615     BOOLEAN LockKcb = FALSE;
616     BOOLEAN IsLockShared = FALSE;
617     PCM_KEY_BODY KeyBody = NULL;
618     PCM_KEY_CONTROL_BLOCK Kcb = NULL;
619 
620     /* Make sure the hive isn't locked */
621     if ((Hive->HiveFlags & HIVE_IS_UNLOADING) &&
622         (((PCMHIVE)Hive)->CreatorOwner != KeGetCurrentThread()))
623     {
624         /* It is, don't touch it */
625         return STATUS_OBJECT_NAME_NOT_FOUND;
626     }
627 
628     /* Check if we have a context */
629     if (Context)
630     {
631         /* Check if this is a link create (which shouldn't be an open) */
632         if (Context->CreateLink)
633         {
634             return STATUS_ACCESS_DENIED;
635         }
636 
637         /* Check if this is symlink create attempt */
638         if (Context->CreateOptions & REG_OPTION_CREATE_LINK)
639         {
640             /* Key already exists */
641             return STATUS_OBJECT_NAME_COLLISION;
642         }
643 
644         /* Set the disposition */
645         Context->Disposition = REG_OPENED_EXISTING_KEY;
646     }
647 
648     /* Lock the KCB on creation if asked */
649     if (ControlFlags & CMP_CREATE_KCB_KCB_LOCKED)
650     {
651         LockKcb = TRUE;
652     }
653 
654     /* Check if caller doesn't want to create a KCB */
655     if (ControlFlags & CMP_OPEN_KCB_NO_CREATE)
656     {
657         /*
658          * The caller doesn't want to create a KCB. This means the KCB
659          * is already in cache and other threads may take use of it
660          * so it has to be locked in share mode.
661          */
662         IsLockShared = TRUE;
663 
664         /* Check if this is a symlink */
665         if (((*CachedKcb)->Flags & KEY_SYM_LINK) && !(Attributes & OBJ_OPENLINK))
666         {
667             /* Is this symlink found? */
668             if ((*CachedKcb)->ExtFlags & CM_KCB_SYM_LINK_FOUND)
669             {
670                 /* Get the real KCB, is this deleted? */
671                 Kcb = (*CachedKcb)->ValueCache.RealKcb;
672                 if (Kcb->Delete)
673                 {
674                     /*
675                      * The real KCB is gone, do a reparse. We used to lock the KCB in
676                      * shared mode as others may have taken use of it but since we
677                      * must do a reparse of the key the only thing that matter is us.
678                      * Lock the KCB exclusively so nobody is going to mess with the KCB.
679                      */
680                     DPRINT1("The real KCB is deleted, attempt a reparse\n");
681                     CmpUnLockKcbArray(KcbsLocked);
682                     CmpAcquireKcbLockExclusiveByIndex(GET_HASH_INDEX((*CachedKcb)->ConvKey));
683                     CmpCleanUpKcbValueCache(*CachedKcb);
684                     KcbsLocked[0] = 1;
685                     KcbsLocked[1] = GET_HASH_INDEX((*CachedKcb)->ConvKey);
686                     return STATUS_REPARSE;
687                 }
688 
689                 /*
690                  * The symlink has been found. As in the similar case above,
691                  * the KCB of the symlink exclusively, we don't want anybody
692                  * to mess it up.
693                  */
694                 CmpUnLockKcbArray(KcbsLocked);
695                 CmpAcquireKcbLockExclusiveByIndex(GET_HASH_INDEX((*CachedKcb)->ConvKey));
696                 KcbsLocked[0] = 1;
697                 KcbsLocked[1] = GET_HASH_INDEX((*CachedKcb)->ConvKey);
698             }
699             else
700             {
701                 /* We must do a reparse */
702                 DPRINT("The symlink is not found, attempt a reparse\n");
703                 return STATUS_REPARSE;
704             }
705         }
706         else
707         {
708             /* This is not a symlink, just give the cached KCB already */
709             Kcb = *CachedKcb;
710         }
711 
712         /* The caller wants to open a cached KCB */
713         if (!CmpReferenceKeyControlBlock(Kcb))
714         {
715             /* Return failure code */
716             return STATUS_INSUFFICIENT_RESOURCES;
717         }
718     }
719     else
720     {
721         /*
722          * The caller wants to create a new KCB. Unlike the code path above, here
723          * we must check if the lock is exclusively held because in the scenario
724          * where the caller doesn't want to create a KCB is because it is already
725          * in the cache and it must have a shared lock instead.
726          */
727         ASSERT(CmpIsKcbLockedExclusive(*CachedKcb));
728 
729         /* Check if this is a symlink */
730         if ((Node->Flags & KEY_SYM_LINK) && !(Attributes & OBJ_OPENLINK))
731         {
732             /* Create the KCB for the symlink */
733             Kcb = CmpCreateKeyControlBlock(Hive,
734                                            Cell,
735                                            Node,
736                                            *CachedKcb,
737                                            LockKcb ? CMP_LOCK_HASHES_FOR_KCB : 0,
738                                            KeyName);
739             if (!Kcb)
740             {
741                 /* Return failure */
742                 return STATUS_INSUFFICIENT_RESOURCES;
743             }
744 
745             /* Make sure it's also locked, and set the pointer */
746             ASSERT(CmpIsKcbLockedExclusive(Kcb));
747             *CachedKcb = Kcb;
748 
749             /* Return reparse required */
750             return STATUS_REPARSE;
751         }
752 
753         /* Create the KCB */
754         Kcb = CmpCreateKeyControlBlock(Hive,
755                                        Cell,
756                                        Node,
757                                        *CachedKcb,
758                                        LockKcb ? CMP_LOCK_HASHES_FOR_KCB : 0,
759                                        KeyName);
760         if (!Kcb)
761         {
762             /* Return failure */
763             return STATUS_INSUFFICIENT_RESOURCES;
764         }
765 
766         /* Make sure it's also locked, and set the pointer */
767         ASSERT(CmpIsKcbLockedExclusive(Kcb));
768         *CachedKcb = Kcb;
769     }
770 
771     /* Allocate the key object */
772     Status = ObCreateObject(AccessMode,
773                             CmpKeyObjectType,
774                             NULL,
775                             AccessMode,
776                             NULL,
777                             sizeof(CM_KEY_BODY),
778                             0,
779                             0,
780                             Object);
781     if (NT_SUCCESS(Status))
782     {
783         /* Get the key body and fill it out */
784         KeyBody = (PCM_KEY_BODY)(*Object);
785         KeyBody->KeyControlBlock = Kcb;
786         KeyBody->Type = CM_KEY_BODY_TYPE;
787         KeyBody->ProcessID = PsGetCurrentProcessId();
788         KeyBody->NotifyBlock = NULL;
789 
790         /* Link to the KCB */
791         EnlistKeyBodyWithKCB(KeyBody, IsLockShared ? CMP_ENLIST_KCB_LOCKED_SHARED : CMP_ENLIST_KCB_LOCKED_EXCLUSIVE);
792 
793         /*
794          * We are already holding a lock against the KCB that is assigned
795          * to this key body. This is to prevent a potential deadlock on
796          * CmpSecurityMethod as ObCheckObjectAccess will invoke the Object
797          * Manager to call that method, of which CmpSecurityMethod would
798          * attempt to acquire a lock again.
799          */
800         KeyBody->KcbLocked = TRUE;
801 
802         if (!ObCheckObjectAccess(*Object,
803                                  AccessState,
804                                  FALSE,
805                                  AccessMode,
806                                  &Status))
807         {
808             /* Access check failed */
809             ObDereferenceObject(*Object);
810         }
811 
812         /*
813          * We are done, the lock we are holding will be released
814          * once the registry parsing is done.
815          */
816         KeyBody->KcbLocked = FALSE;
817     }
818     else
819     {
820         /* Failed, dereference the KCB */
821         CmpDereferenceKeyControlBlockWithLock(Kcb, FALSE);
822     }
823 
824     /* Return status */
825     return Status;
826 }
827 
828 NTSTATUS
829 NTAPI
CmpCreateLinkNode(IN PHHIVE Hive,IN HCELL_INDEX Cell,IN PACCESS_STATE AccessState,IN UNICODE_STRING Name,IN KPROCESSOR_MODE AccessMode,IN ULONG CreateOptions,IN PCM_PARSE_CONTEXT Context,IN PCM_KEY_CONTROL_BLOCK ParentKcb,IN PULONG KcbsLocked,OUT PVOID * Object)830 CmpCreateLinkNode(IN PHHIVE Hive,
831                   IN HCELL_INDEX Cell,
832                   IN PACCESS_STATE AccessState,
833                   IN UNICODE_STRING Name,
834                   IN KPROCESSOR_MODE AccessMode,
835                   IN ULONG CreateOptions,
836                   IN PCM_PARSE_CONTEXT Context,
837                   IN PCM_KEY_CONTROL_BLOCK ParentKcb,
838                   IN PULONG KcbsLocked,
839                   OUT PVOID *Object)
840 {
841     NTSTATUS Status;
842     HCELL_INDEX KeyCell, LinkCell, ChildCell;
843     PCM_KEY_BODY KeyBody;
844     LARGE_INTEGER TimeStamp;
845     PCM_KEY_NODE KeyNode;
846     PCM_KEY_CONTROL_BLOCK Kcb = ParentKcb;
847 
848     /* Link nodes only allowed on the master */
849     if (Hive != &CmiVolatileHive->Hive)
850     {
851         /* Fail */
852         DPRINT1("Invalid link node attempt\n");
853         return STATUS_ACCESS_DENIED;
854     }
855 
856     /* Make sure the KCB is locked and lock the flusher */
857     CMP_ASSERT_KCB_LOCK(ParentKcb);
858     CmpLockHiveFlusherShared((PCMHIVE)Hive);
859     CmpLockHiveFlusherShared((PCMHIVE)Context->ChildHive.KeyHive);
860 
861     /* Bail out on read-only KCBs */
862     if (ParentKcb->ExtFlags & CM_KCB_READ_ONLY_KEY)
863     {
864         Status = STATUS_ACCESS_DENIED;
865         goto Exit;
866     }
867 
868     /* Check if the parent is being deleted */
869     if (ParentKcb->Delete)
870     {
871         /* It is, quit */
872         ASSERT(FALSE);
873         Status = STATUS_OBJECT_NAME_NOT_FOUND;
874         goto Exit;
875     }
876 
877     /* Allocate a link node */
878     LinkCell = HvAllocateCell(Hive,
879                               FIELD_OFFSET(CM_KEY_NODE, Name) +
880                               CmpNameSize(Hive, &Name),
881                               Stable,
882                               HCELL_NIL);
883     if (LinkCell == HCELL_NIL)
884     {
885         /* Fail */
886         Status = STATUS_INSUFFICIENT_RESOURCES;
887         goto Exit;
888     }
889 
890     /* Get the key cell */
891     KeyCell = Context->ChildHive.KeyCell;
892     if (KeyCell != HCELL_NIL)
893     {
894         /* Hive exists! */
895         ChildCell = KeyCell;
896 
897         /* Get the node data */
898         KeyNode = (PCM_KEY_NODE)HvGetCell(Context->ChildHive.KeyHive, ChildCell);
899         if (!KeyNode)
900         {
901             /* Fail */
902             ASSERT(FALSE);
903             Status = STATUS_INSUFFICIENT_RESOURCES;
904             goto Exit;
905         }
906 
907         /* Fill out the data */
908         KeyNode->Parent = LinkCell;
909         KeyNode->Flags |= KEY_HIVE_ENTRY | KEY_NO_DELETE;
910         HvReleaseCell(Context->ChildHive.KeyHive, ChildCell);
911 
912         /* Now open the key cell */
913         KeyNode = (PCM_KEY_NODE)HvGetCell(Context->ChildHive.KeyHive, KeyCell);
914         if (!KeyNode)
915         {
916             /* Fail */
917             ASSERT(FALSE);
918             Status = STATUS_INSUFFICIENT_RESOURCES;
919             goto Exit;
920         }
921 
922         /* Open the parent */
923         Status = CmpDoOpen(Context->ChildHive.KeyHive,
924                            KeyCell,
925                            KeyNode,
926                            AccessState,
927                            AccessMode,
928                            CreateOptions,
929                            NULL,
930                            CMP_CREATE_KCB_KCB_LOCKED,
931                            &Kcb,
932                            KcbsLocked,
933                            &Name,
934                            Object);
935         HvReleaseCell(Context->ChildHive.KeyHive, KeyCell);
936     }
937     else
938     {
939         /* Do the actual create operation */
940         Status = CmpDoCreateChild(Context->ChildHive.KeyHive,
941                                   Cell,
942                                   NULL,
943                                   AccessState,
944                                   &Name,
945                                   AccessMode,
946                                   Context,
947                                   ParentKcb,
948                                   KEY_HIVE_ENTRY | KEY_NO_DELETE,
949                                   &ChildCell,
950                                   Object);
951         if (NT_SUCCESS(Status))
952         {
953             /* Setup root pointer */
954             Context->ChildHive.KeyHive->BaseBlock->RootCell = ChildCell;
955         }
956     }
957 
958     /* Check if open or create suceeded */
959     if (NT_SUCCESS(Status))
960     {
961         /* Mark the cell dirty */
962         HvMarkCellDirty(Context->ChildHive.KeyHive, ChildCell, FALSE);
963 
964         /* Get the key node */
965         KeyNode = (PCM_KEY_NODE)HvGetCell(Context->ChildHive.KeyHive, ChildCell);
966         if (!KeyNode)
967         {
968             /* Fail */
969             ASSERT(FALSE);
970             Status = STATUS_INSUFFICIENT_RESOURCES;
971             goto Exit;
972         }
973 
974         /* Release it */
975         HvReleaseCell(Context->ChildHive.KeyHive, ChildCell);
976 
977         /* Set the parent and flags */
978         KeyNode->Parent = LinkCell;
979         KeyNode->Flags |= KEY_HIVE_ENTRY | KEY_NO_DELETE;
980 
981         /* Get the link node */
982         KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, LinkCell);
983         if (!KeyNode)
984         {
985             /* Fail */
986             ASSERT(FALSE);
987             Status = STATUS_INSUFFICIENT_RESOURCES;
988             goto Exit;
989         }
990 
991         /* Set it up */
992         KeyNode->Signature = CM_LINK_NODE_SIGNATURE;
993         KeyNode->Flags = KEY_HIVE_EXIT | KEY_NO_DELETE;
994         KeyNode->Parent = Cell;
995         KeyNode->NameLength = CmpCopyName(Hive, KeyNode->Name, &Name);
996         if (KeyNode->NameLength < Name.Length) KeyNode->Flags |= KEY_COMP_NAME;
997         KeQuerySystemTime(&TimeStamp);
998         KeyNode->LastWriteTime = TimeStamp;
999 
1000         /* Clear out the rest */
1001         KeyNode->SubKeyCounts[Stable] = 0;
1002         KeyNode->SubKeyCounts[Volatile] = 0;
1003         KeyNode->SubKeyLists[Stable] = HCELL_NIL;
1004         KeyNode->SubKeyLists[Volatile] = HCELL_NIL;
1005         KeyNode->ValueList.Count = 0;
1006         KeyNode->ValueList.List = HCELL_NIL;
1007         KeyNode->ClassLength = 0;
1008 
1009         /* Reference the root node */
1010         KeyNode->ChildHiveReference.KeyHive = Context->ChildHive.KeyHive;
1011         KeyNode->ChildHiveReference.KeyCell = ChildCell;
1012         HvReleaseCell(Hive, LinkCell);
1013 
1014         /* Get the parent node */
1015         KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
1016         if (!KeyNode)
1017         {
1018             /* Fail */
1019             ASSERT(FALSE);
1020             Status = STATUS_INSUFFICIENT_RESOURCES;
1021             goto Exit;
1022         }
1023 
1024         /* Now add the subkey */
1025         if (!CmpAddSubKey(Hive, Cell, LinkCell))
1026         {
1027             /* Failure! We don't handle this yet! */
1028             ASSERT(FALSE);
1029         }
1030 
1031         /* Get the key body */
1032         KeyBody = (PCM_KEY_BODY)*Object;
1033 
1034         /* Clean up information on this subkey */
1035         CmpCleanUpSubKeyInfo(KeyBody->KeyControlBlock->ParentKcb);
1036 
1037         /* Sanity checks */
1038         ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyCell == Cell);
1039         ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyHive == Hive);
1040         ASSERT(KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen == KeyNode->MaxNameLen);
1041 
1042         /* Update the timestamp */
1043         KeQuerySystemTime(&TimeStamp);
1044         KeyNode->LastWriteTime = TimeStamp;
1045         KeyBody->KeyControlBlock->ParentKcb->KcbLastWriteTime = TimeStamp;
1046 
1047         /* Check if we need to update name maximum */
1048         if (KeyNode->MaxNameLen < Name.Length)
1049         {
1050             /* Do it */
1051             KeyNode->MaxNameLen = Name.Length;
1052             KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen = Name.Length;
1053         }
1054 
1055         /* Check if we need to update class length maximum */
1056         if (KeyNode->MaxClassLen < Context->Class.Length)
1057         {
1058             /* Update it */
1059             KeyNode->MaxClassLen = Context->Class.Length;
1060         }
1061 
1062         /* Release the cell */
1063         HvReleaseCell(Hive, Cell);
1064     }
1065     else
1066     {
1067         /* Release the link cell */
1068         HvReleaseCell(Hive, LinkCell);
1069     }
1070 
1071 Exit:
1072     /* Release the flusher locks and return status */
1073     CmpUnlockHiveFlusher((PCMHIVE)Context->ChildHive.KeyHive);
1074     CmpUnlockHiveFlusher((PCMHIVE)Hive);
1075     return Status;
1076 }
1077 
1078 VOID
1079 NTAPI
CmpHandleExitNode(IN OUT PHHIVE * Hive,IN OUT HCELL_INDEX * Cell,IN OUT PCM_KEY_NODE * KeyNode,IN OUT PHHIVE * ReleaseHive,IN OUT HCELL_INDEX * ReleaseCell)1080 CmpHandleExitNode(IN OUT PHHIVE *Hive,
1081                   IN OUT HCELL_INDEX *Cell,
1082                   IN OUT PCM_KEY_NODE *KeyNode,
1083                   IN OUT PHHIVE *ReleaseHive,
1084                   IN OUT HCELL_INDEX *ReleaseCell)
1085 {
1086     /* Check if we have anything to release */
1087     if (*ReleaseCell != HCELL_NIL)
1088     {
1089         /* Release it */
1090         ASSERT(*ReleaseHive != NULL);
1091         HvReleaseCell(*ReleaseHive, *ReleaseCell);
1092     }
1093 
1094     /* Get the link references */
1095     *Hive = (*KeyNode)->ChildHiveReference.KeyHive;
1096     *Cell = (*KeyNode)->ChildHiveReference.KeyCell;
1097 
1098     /* Get the new node */
1099     *KeyNode = (PCM_KEY_NODE)HvGetCell(*Hive, *Cell);
1100     if (*KeyNode)
1101     {
1102         /* Set the new release values */
1103         *ReleaseCell = *Cell;
1104         *ReleaseHive = *Hive;
1105     }
1106     else
1107     {
1108         /* Nothing to release */
1109         *ReleaseCell = HCELL_NIL;
1110         *ReleaseHive = NULL;
1111     }
1112 }
1113 
1114 /**
1115  * @brief
1116  * Computes the hashes of each subkey in key path name
1117  * and stores them in a hash stack for cache lookup.
1118  *
1119  * @param[in] RemainingName
1120  * A Unicode string structure consisting of the remaining
1121  * registry key path name.
1122  *
1123  * @param[in] ConvKey
1124  * The hash convkey of the current KCB to be supplied.
1125  *
1126  * @param[in,out] HashCacheStack
1127  * An array stack. This function uses this array to store
1128  * all the computed hashes of a key pathname.
1129  *
1130  * @param[out] TotalSubKeys
1131  * The number of total subkeys that have been found, returned
1132  * by this function to the caller. If no subkey levels are found
1133  * the function returns 0.
1134  *
1135  * @return
1136  * Returns the number of remaining subkey levels to caller.
1137  * If no subkey levels are found then this function returns 0.
1138  */
1139 static
1140 ULONG
CmpComputeHashValue(_In_ PUNICODE_STRING RemainingName,_In_ ULONG ConvKey,_Inout_ PCM_HASH_CACHE_STACK HashCacheStack,_Out_ PULONG TotalSubKeys)1141 CmpComputeHashValue(
1142     _In_ PUNICODE_STRING RemainingName,
1143     _In_ ULONG ConvKey,
1144     _Inout_ PCM_HASH_CACHE_STACK HashCacheStack,
1145     _Out_ PULONG TotalSubKeys)
1146 {
1147     ULONG CopyConvKey;
1148     ULONG SubkeysInTotal;
1149     ULONG RemainingSubkeysInTotal;
1150     PWCHAR RemainingNameBuffer;
1151     USHORT RemainingNameLength;
1152     USHORT KeyNameLength;
1153 
1154     /* Don't compute the hashes on a NULL remaining name */
1155     RemainingNameBuffer = RemainingName->Buffer;
1156     RemainingNameLength = RemainingName->Length;
1157     if (RemainingNameLength == 0)
1158     {
1159         *TotalSubKeys = 0;
1160         return 0;
1161     }
1162 
1163     /* Skip any leading separator */
1164     while (RemainingNameLength >= sizeof(WCHAR) &&
1165            *RemainingNameBuffer == OBJ_NAME_PATH_SEPARATOR)
1166     {
1167         RemainingNameBuffer++;
1168         RemainingNameLength -= sizeof(WCHAR);
1169     }
1170 
1171     /* Now set up the hash stack entries and compute the hashes */
1172     SubkeysInTotal = 0;
1173     RemainingSubkeysInTotal = 0;
1174     KeyNameLength = 0;
1175     CopyConvKey = ConvKey;
1176     HashCacheStack[RemainingSubkeysInTotal].NameOfKey.Buffer = RemainingNameBuffer;
1177     while (RemainingNameLength > 0)
1178     {
1179         /* Is this character a separator? */
1180         if (*RemainingNameBuffer != OBJ_NAME_PATH_SEPARATOR)
1181         {
1182             /* It's not, add it to the hash */
1183             CopyConvKey = COMPUTE_HASH_CHAR(CopyConvKey, *RemainingNameBuffer);
1184 
1185             /* Go to the next character (add up the length of the character as well) */
1186             RemainingNameBuffer++;
1187             KeyNameLength += sizeof(WCHAR);
1188             RemainingNameLength -= sizeof(WCHAR);
1189 
1190             /*
1191              * We are at the end of the key name path. Take into account
1192              * the last character and if we still have space in the hash
1193              * stack, add it up in the remaining list.
1194              */
1195             if (RemainingNameLength == 0)
1196             {
1197                 if (RemainingSubkeysInTotal < CMP_SUBKEY_LEVELS_DEPTH_LIMIT)
1198                 {
1199                     HashCacheStack[RemainingSubkeysInTotal].NameOfKey.Length = KeyNameLength;
1200                     HashCacheStack[RemainingSubkeysInTotal].NameOfKey.MaximumLength = KeyNameLength;
1201                     HashCacheStack[RemainingSubkeysInTotal].ConvKey = CopyConvKey;
1202                     RemainingSubkeysInTotal++;
1203                 }
1204 
1205                 SubkeysInTotal++;
1206             }
1207         }
1208         else
1209         {
1210             /* Skip any leading separator */
1211             while (RemainingNameLength >= sizeof(WCHAR) &&
1212                    *RemainingNameBuffer == OBJ_NAME_PATH_SEPARATOR)
1213             {
1214                 RemainingNameBuffer++;
1215                 RemainingNameLength -= sizeof(WCHAR);
1216             }
1217 
1218             /*
1219              * It would be possible that a malformed key pathname may be passed
1220              * to the registry parser such as a path with only separators like
1221              * "\\\\" for example. This would trick the function into believing
1222              * the key path has subkeys albeit that is not the case.
1223              */
1224             ASSERT(RemainingNameLength != 0);
1225 
1226             /* Take into account this subkey */
1227             SubkeysInTotal++;
1228 
1229             /* And add it up to the hash stack */
1230             if (RemainingSubkeysInTotal < CMP_SUBKEY_LEVELS_DEPTH_LIMIT)
1231             {
1232                 HashCacheStack[RemainingSubkeysInTotal].NameOfKey.Length = KeyNameLength;
1233                 HashCacheStack[RemainingSubkeysInTotal].NameOfKey.MaximumLength = KeyNameLength;
1234                 HashCacheStack[RemainingSubkeysInTotal].ConvKey = CopyConvKey;
1235 
1236                 RemainingSubkeysInTotal++;
1237                 KeyNameLength = 0;
1238 
1239                 /*
1240                  * Precaution check -- we have added up a remaining
1241                  * subkey above but we must ensure we still have space
1242                  * to hold up the new subkey for which we will compute
1243                  * the hashes, so that we don't blow up the hash stack.
1244                  */
1245                 if (RemainingSubkeysInTotal < CMP_SUBKEY_LEVELS_DEPTH_LIMIT)
1246                 {
1247                     HashCacheStack[RemainingSubkeysInTotal].NameOfKey.Buffer = RemainingNameBuffer;
1248                 }
1249             }
1250         }
1251     }
1252 
1253     *TotalSubKeys = SubkeysInTotal;
1254     return RemainingSubkeysInTotal;
1255 }
1256 
1257 /**
1258  * @brief
1259  * Compares each subkey's hash and name with those
1260  * captured in the hash cache stack.
1261  *
1262  * @param[in] HashCacheStack
1263  * A pointer to a hash cache stack array filled with
1264  * subkey hashes and names for comparison.
1265  *
1266  * @param[in] CurrentKcb
1267  * A pointer to the currently given KCB.
1268  *
1269  * @param[in] RemainingSubkeys
1270  * The remaining subkey levels to be supplied.
1271  *
1272  * @param[out] ParentKcb
1273  * A pointer to the parent KCB returned to the caller.
1274  * This parameter points to the parent of the current
1275  * KCB if all the subkeys match, otherwise it points
1276  * to the actual current KCB.
1277  *
1278  * @return
1279  * Returns TRUE if all the subkey levels match, otherwise
1280  * FALSE is returned.
1281  */
1282 static
1283 BOOLEAN
CmpCompareSubkeys(_In_ PCM_HASH_CACHE_STACK HashCacheStack,_In_ PCM_KEY_CONTROL_BLOCK CurrentKcb,_In_ ULONG RemainingSubkeys,_Out_ PCM_KEY_CONTROL_BLOCK * ParentKcb)1284 CmpCompareSubkeys(
1285     _In_ PCM_HASH_CACHE_STACK HashCacheStack,
1286     _In_ PCM_KEY_CONTROL_BLOCK CurrentKcb,
1287     _In_ ULONG RemainingSubkeys,
1288     _Out_ PCM_KEY_CONTROL_BLOCK *ParentKcb)
1289 {
1290     LONG HashStackIndex;
1291     LONG Result;
1292     PCM_NAME_CONTROL_BLOCK NameBlock;
1293     UNICODE_STRING CurrentNameBlock;
1294 
1295     ASSERT(CurrentKcb != NULL);
1296 
1297     /* Loop each hash and check that they match */
1298     HashStackIndex = RemainingSubkeys;
1299     while (HashStackIndex >= 0)
1300     {
1301         /* Does the subkey hash match? */
1302         if (CurrentKcb->ConvKey != HashCacheStack[HashStackIndex].ConvKey)
1303         {
1304             *ParentKcb = CurrentKcb;
1305             return FALSE;
1306         }
1307 
1308         /* Compare the subkey string, is the name compressed? */
1309         NameBlock = CurrentKcb->NameBlock;
1310         if (NameBlock->Compressed)
1311         {
1312             Result = CmpCompareCompressedName(&HashCacheStack[HashStackIndex].NameOfKey,
1313                                               NameBlock->Name,
1314                                               NameBlock->NameLength);
1315         }
1316         else
1317         {
1318             CurrentNameBlock.Buffer = NameBlock->Name;
1319             CurrentNameBlock.Length = NameBlock->NameLength;
1320             CurrentNameBlock.MaximumLength = NameBlock->NameLength;
1321 
1322             Result = RtlCompareUnicodeString(&HashCacheStack[HashStackIndex].NameOfKey,
1323                                              &CurrentNameBlock,
1324                                              TRUE);
1325         }
1326 
1327         /* Do the subkey names match? */
1328         if (Result)
1329         {
1330             *ParentKcb = CurrentKcb;
1331             return FALSE;
1332         }
1333 
1334         /* Go to the next subkey hash */
1335         HashStackIndex--;
1336     }
1337 
1338     /* All the subkeys match */
1339     *ParentKcb = CurrentKcb->ParentKcb;
1340     return TRUE;
1341 }
1342 
1343 /**
1344  * @brief
1345  * Removes the subkeys on a remaining key pathname.
1346  *
1347  * @param[in] HashCacheStack
1348  * A pointer to a hash cache stack array filled with
1349  * subkey hashes and names.
1350  *
1351  * @param[in] RemainingSubkeys
1352  * The remaining subkey levels to be supplied.
1353  *
1354  * @param[in,out] RemainingName
1355  * A Unicode string structure consisting of the remaining
1356  * registry key path name, where the subkeys of such path
1357  * are to be removed.
1358  */
1359 static
1360 VOID
CmpRemoveSubkeysInRemainingName(_In_ PCM_HASH_CACHE_STACK HashCacheStack,_In_ ULONG RemainingSubkeys,_Inout_ PUNICODE_STRING RemainingName)1361 CmpRemoveSubkeysInRemainingName(
1362     _In_ PCM_HASH_CACHE_STACK HashCacheStack,
1363     _In_ ULONG RemainingSubkeys,
1364     _Inout_ PUNICODE_STRING RemainingName)
1365 {
1366     ULONG HashStackIndex = 0;
1367 
1368     /* Skip any leading separator on matching name */
1369     while (RemainingName->Length >= sizeof(WCHAR) &&
1370            RemainingName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
1371     {
1372         RemainingName->Buffer++;
1373         RemainingName->Length -= sizeof(WCHAR);
1374     }
1375 
1376     /* Skip the subkeys as well */
1377     while (HashStackIndex <= RemainingSubkeys)
1378     {
1379         RemainingName->Buffer += HashCacheStack[HashStackIndex].NameOfKey.Length / sizeof(WCHAR);
1380         RemainingName->Length -= HashCacheStack[HashStackIndex].NameOfKey.Length;
1381 
1382         /* Skip any leading separator */
1383         while (RemainingName->Length >= sizeof(WCHAR) &&
1384                RemainingName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
1385         {
1386             RemainingName->Buffer++;
1387             RemainingName->Length -= sizeof(WCHAR);
1388         }
1389 
1390         /* Go to the next hash */
1391         HashStackIndex++;
1392     }
1393 }
1394 
1395 /**
1396  * @brief
1397  * Looks up in the pool cache for key pathname that matches
1398  * with one in the said cache and returns a KCB pointing
1399  * to that name. This function performs locking of KCBs
1400  * during cache lookup.
1401  *
1402  * @param[in] HashCacheStack
1403  * A pointer to a hash cache stack array filled with
1404  * subkey hashes and names.
1405  *
1406  * @param[in] LockKcbsExclusive
1407  * If set to TRUE, the KCBs are locked exclusively by the
1408  * calling thread, otherwise they are locked in shared mode.
1409  * See Remarks for further information.
1410  *
1411  * @param[in] TotalRemainingSubkeys
1412  * The total remaining subkey levels to be supplied.
1413  *
1414  * @param[in,out] RemainingName
1415  * A Unicode string structure consisting of the remaining
1416  * registry key path name. The remaining name is updated
1417  * by the function if a key pathname is found in cache.
1418  *
1419  * @param[in,out] OuterStackArray
1420  * A pointer to an array that lives on the caller's stack.
1421  * The expected size of the array is up to 32 elements,
1422  * which is the imposed limit by CMP_HASH_STACK_LIMIT.
1423  * This limit also corresponds to the maximum depth of
1424  * subkey levels.
1425  *
1426  * @param[in,out] Kcb
1427  * A pointer to a KCB, this KCB is changed if the key pathname
1428  * is found in cache.
1429  *
1430  * @param[out] Hive
1431  * A pointer to a hive, this hive is changed if the key pathname
1432  * is found in cache.
1433  *
1434  * @param[out] Cell
1435  * A pointer to a cell, this cell is changed if the key pathname
1436  * is found in cache.
1437  *
1438  * @param[out] MatchRemainSubkeyLevel
1439  * A pointer to match subkey levels returned by the function.
1440  * If no match levels are found, this is 0.
1441  *
1442  * @return
1443  * Returns STATUS_SUCCESS if cache lookup has completed successfully.
1444  * STATUS_OBJECT_NAME_NOT_FOUND is returned if the current KCB of
1445  * the key pathname has been deleted. STATUS_RETRY is returned if
1446  * at least the current KCB or its parent have been deleted
1447  * and a cache lookup must be retried again. STATUS_UNSUCCESSFUL is
1448  * returned if a KCB is referenced too many times.
1449  *
1450  * @remarks
1451  * The function attempts to do a cache lookup with a shared lock
1452  * on KCBs so that other threads can simultaneously get access
1453  * to these KCBs. When the captured KCB is being deleted on us
1454  * we have to retry a lookup with exclusive look so that no other
1455  * threads will mess with the KCBs and perform appropriate actions
1456  * if a KCB is deleted.
1457  */
1458 static
1459 NTSTATUS
CmpLookInCache(_In_ PCM_HASH_CACHE_STACK HashCacheStack,_In_ BOOLEAN LockKcbsExclusive,_In_ ULONG TotalRemainingSubkeys,_Inout_ PUNICODE_STRING RemainingName,_Inout_ PULONG OuterStackArray,_Inout_ PCM_KEY_CONTROL_BLOCK * Kcb,_Out_ PHHIVE * Hive,_Out_ PHCELL_INDEX Cell,_Out_ PULONG MatchRemainSubkeyLevel)1460 CmpLookInCache(
1461     _In_ PCM_HASH_CACHE_STACK HashCacheStack,
1462     _In_ BOOLEAN LockKcbsExclusive,
1463     _In_ ULONG TotalRemainingSubkeys,
1464     _Inout_ PUNICODE_STRING RemainingName,
1465     _Inout_ PULONG OuterStackArray,
1466     _Inout_ PCM_KEY_CONTROL_BLOCK *Kcb,
1467     _Out_ PHHIVE *Hive,
1468     _Out_ PHCELL_INDEX Cell,
1469     _Out_ PULONG MatchRemainSubkeyLevel)
1470 {
1471     LONG RemainingSubkeys;
1472     ULONG TotalLevels;
1473     BOOLEAN SubkeysMatch;
1474     PCM_KEY_CONTROL_BLOCK CurrentKcb, ParentKcb;
1475     PCM_KEY_HASH HashEntry = NULL;
1476     BOOLEAN KeyFoundInCache = FALSE;
1477     PULONG LockedKcbs = NULL;
1478 
1479     /* Reference the KCB */
1480     if (!CmpReferenceKeyControlBlock(*Kcb))
1481     {
1482         /* This key is opened too many times, bail out */
1483         DPRINT1("Could not reference the KCB, too many references (KCB 0x%p)\n", Kcb);
1484         return STATUS_UNSUCCESSFUL;
1485     }
1486 
1487     /* Prepare to lock the KCBs */
1488     LockedKcbs = CmpBuildAndLockKcbArray(HashCacheStack,
1489                                          LockKcbsExclusive ? CMP_LOCK_KCB_ARRAY_EXCLUSIVE : CMP_LOCK_KCB_ARRAY_SHARED,
1490                                          *Kcb,
1491                                          OuterStackArray,
1492                                          TotalRemainingSubkeys,
1493                                          0);
1494     NT_ASSERT(LockedKcbs);
1495 
1496     /* Lookup in the cache */
1497     RemainingSubkeys = TotalRemainingSubkeys - 1;
1498     TotalLevels = TotalRemainingSubkeys + (*Kcb)->TotalLevels + 1;
1499     while (RemainingSubkeys >= 0)
1500     {
1501         /* Get the hash entry from the cache */
1502         HashEntry = GET_HASH_ENTRY(CmpCacheTable, HashCacheStack[RemainingSubkeys].ConvKey)->Entry;
1503 
1504         /* Take one level down as we are processing this hash entry */
1505         TotalLevels--;
1506 
1507         while (HashEntry != NULL)
1508         {
1509             /* Validate this hash and obtain the current KCB */
1510             ASSERT_VALID_HASH(HashEntry);
1511             CurrentKcb = CONTAINING_RECORD(HashEntry, CM_KEY_CONTROL_BLOCK, KeyHash);
1512 
1513             /* Does this KCB have matching levels? */
1514             if (TotalLevels == CurrentKcb->TotalLevels)
1515             {
1516                 /*
1517                  * We have matching subkey levels but don't directly assume we have
1518                  * a matching key path in cache. Start comparing each subkey.
1519                  */
1520                 SubkeysMatch = CmpCompareSubkeys(HashCacheStack,
1521                                                  CurrentKcb,
1522                                                  RemainingSubkeys,
1523                                                  &ParentKcb);
1524                 if (SubkeysMatch)
1525                 {
1526                     /* All subkeys match, now check if the base KCB matches with parent */
1527                     if (*Kcb == ParentKcb)
1528                     {
1529                         /* Is the KCB marked as deleted? */
1530                         if (CurrentKcb->Delete ||
1531                             CurrentKcb->ParentKcb->Delete)
1532                         {
1533                             /*
1534                              * Either the current or its parent KCB is marked
1535                              * but we had a shared lock so probably a naughty
1536                              * thread was deleting it. Retry doing a cache
1537                              * lookup again with exclusive lock.
1538                              */
1539                             if (!LockKcbsExclusive)
1540                             {
1541                                 CmpUnLockKcbArray(LockedKcbs);
1542                                 CmpDereferenceKeyControlBlock(*Kcb);
1543                                 DPRINT1("The current KCB or its parent is deleted, retrying looking in the cache\n");
1544                                 return STATUS_RETRY;
1545                             }
1546 
1547                             /* We're under an exclusive lock, is the KCB deleted yet? */
1548                             if (CurrentKcb->Delete)
1549                             {
1550                                 /* The KCB is gone, the key should no longer belong in the cache */
1551                                 CmpRemoveKeyControlBlock(CurrentKcb);
1552                                 CmpUnLockKcbArray(LockedKcbs);
1553                                 CmpDereferenceKeyControlBlock(*Kcb);
1554                                 DPRINT1("The current KCB is deleted (KCB 0x%p)\n", CurrentKcb);
1555                                 return STATUS_OBJECT_NAME_NOT_FOUND;
1556                             }
1557 
1558                             /*
1559                              * The parent is deleted so it must be that somebody created
1560                              * a fake key. Assert ourselves that is the case.
1561                              */
1562                             ASSERT(CurrentKcb->ExtFlags & CM_KCB_KEY_NON_EXIST);
1563 
1564                             /* Remove this KCB out of cache if someone still uses it */
1565                             if (CurrentKcb->RefCount != 0)
1566                             {
1567                                 CurrentKcb->Delete = TRUE;
1568                                 CmpRemoveKeyControlBlock(CurrentKcb);
1569                             }
1570                             else
1571                             {
1572                                 /* Otherwise expunge it */
1573                                 CmpRemoveFromDelayedClose(CurrentKcb);
1574                                 CmpCleanUpKcbCacheWithLock(CurrentKcb, FALSE);
1575                             }
1576 
1577                             /* Stop looking for next hashes as the KCB is kaput */
1578                             break;
1579                         }
1580 
1581                         /* We finally found the key in cache, acknowledge it */
1582                         KeyFoundInCache = TRUE;
1583 
1584                         /* Remove the subkeys in the remaining name and stop looking in the cache */
1585                         CmpRemoveSubkeysInRemainingName(HashCacheStack, RemainingSubkeys, RemainingName);
1586                         break;
1587                     }
1588                 }
1589             }
1590 
1591             /* Go to the next hash */
1592             HashEntry = HashEntry->NextHash;
1593         }
1594 
1595         /* Stop looking in cache if we found the matching key */
1596         if (KeyFoundInCache)
1597         {
1598             DPRINT("Key found in cache, stop looking\n");
1599             break;
1600         }
1601 
1602         /* Keep looking in the cache until we run out of remaining subkeys */
1603         RemainingSubkeys--;
1604     }
1605 
1606     /* Return the matching subkey levels */
1607     *MatchRemainSubkeyLevel = RemainingSubkeys + 1;
1608 
1609     /* We have to update the KCB if the key was found in cache */
1610     if (KeyFoundInCache)
1611     {
1612         /*
1613          * Before we change the KCB we must dereference the prior
1614          * KCB that we no longer need it.
1615          */
1616         CmpDereferenceKeyControlBlock(*Kcb);
1617         *Kcb = CurrentKcb;
1618 
1619         /* Reference the new KCB now */
1620         if (!CmpReferenceKeyControlBlock(*Kcb))
1621         {
1622             /* This key is opened too many times, bail out */
1623             DPRINT1("Could not reference the KCB, too many references (KCB 0x%p)\n", Kcb);
1624             return STATUS_UNSUCCESSFUL;
1625         }
1626 
1627         /* Update hive and cell data from current KCB */
1628         *Hive = CurrentKcb->KeyHive;
1629         *Cell = CurrentKcb->KeyCell;
1630     }
1631 
1632     /* Unlock the KCBs */
1633     CmpUnLockKcbArray(LockedKcbs);
1634     return STATUS_SUCCESS;
1635 }
1636 
1637 /**
1638  * @brief
1639  * Builds a hash stack cache and looks up in the
1640  * pool cache for a matching key pathname.
1641  *
1642  * @param[in] ParseObject
1643  * A pointer to a parse object, acting as a key
1644  * body. This parameter is unused.
1645  *
1646  * @param[in,out] Kcb
1647  * A pointer to a KCB. This KCB is used by the
1648  * registry parser after hash stack and cache
1649  * lookup are done. This KCB might change if the
1650  * key is found to be cached in the cache pool.
1651  *
1652  * @param[in] Current
1653  * The current remaining key pathname.
1654  *
1655  * @param[out] Hive
1656  * A pointer to a registry hive, returned by the caller.
1657  *
1658  * @param[out] Cell
1659  * A pointer to a hive cell, returned by the caller.
1660  *
1661  * @param[out] TotalRemainingSubkeys
1662  * A pointer to a number of total remaining subkey levels,
1663  * returned by the caller. This can be 0 if no subkey levels
1664  * have been found.
1665  *
1666  * @param[out] MatchRemainSubkeyLevel
1667  * A pointer to a number of remaining subkey levels that match,
1668  * returned by the caller. This can be 0 if no matching levels
1669  * are found.
1670  *
1671  * @param[out] TotalSubkeys
1672  * A pointer to a number of total subkeys. This can be 0 if no
1673  * subkey levels are found. By definition, both MatchRemainSubkeyLevel
1674  * and TotalRemainingSubkeys are 0 as well.
1675  *
1676  * @param[in,out] OuterStackArray
1677  * A pointer to an array that lives on the caller's stack.
1678  * The expected size of the array is up to 32 elements,
1679  * which is the imposed limit by CMP_HASH_STACK_LIMIT.
1680  * This limit also corresponds to the maximum depth of
1681  * subkey levels.
1682  *
1683  * @param[out] LockedKcbs
1684  * A pointer to an array of locked KCBs, returned by the caller.
1685  *
1686  * @return
1687  * Returns STATUS_SUCCESS if all the operations have succeeded without
1688  * problems. STATUS_NAME_TOO_LONG is returned if the key pathname has
1689  * too many subkey levels (more than 32 levels deep). A failure NTSTATUS
1690  * code is returned otherwise. Refer to CmpLookInCache documentation
1691  * for more information about other returned status codes.
1692  * STATUS_UNSUCCESSFUL is returned if a KCB is referenced too many times.
1693  */
1694 NTSTATUS
1695 NTAPI
CmpBuildHashStackAndLookupCache(_In_ PCM_KEY_BODY ParseObject,_Inout_ PCM_KEY_CONTROL_BLOCK * Kcb,_In_ PUNICODE_STRING Current,_Out_ PHHIVE * Hive,_Out_ PHCELL_INDEX Cell,_Out_ PULONG TotalRemainingSubkeys,_Out_ PULONG MatchRemainSubkeyLevel,_Out_ PULONG TotalSubkeys,_Inout_ PULONG OuterStackArray,_Out_ PULONG * LockedKcbs)1696 CmpBuildHashStackAndLookupCache(
1697     _In_ PCM_KEY_BODY ParseObject,
1698     _Inout_ PCM_KEY_CONTROL_BLOCK *Kcb,
1699     _In_ PUNICODE_STRING Current,
1700     _Out_ PHHIVE *Hive,
1701     _Out_ PHCELL_INDEX Cell,
1702     _Out_ PULONG TotalRemainingSubkeys,
1703     _Out_ PULONG MatchRemainSubkeyLevel,
1704     _Out_ PULONG TotalSubkeys,
1705     _Inout_ PULONG OuterStackArray,
1706     _Out_ PULONG *LockedKcbs)
1707 {
1708     NTSTATUS Status;
1709     ULONG ConvKey;
1710     ULONG SubkeysInTotal, RemainingSubkeysInTotal, MatchRemainingSubkeys;
1711     CM_HASH_CACHE_STACK HashCacheStack[CMP_SUBKEY_LEVELS_DEPTH_LIMIT];
1712 
1713     /* Make sure it's not a dead KCB */
1714     ASSERT((*Kcb)->RefCount > 0);
1715 
1716     /* Lock the registry */
1717     CmpLockRegistry();
1718 
1719     /* Calculate hash values for every subkey this key path has */
1720     ConvKey = (*Kcb)->ConvKey;
1721     RemainingSubkeysInTotal = CmpComputeHashValue(Current,
1722                                                   ConvKey,
1723                                                   HashCacheStack,
1724                                                   &SubkeysInTotal);
1725 
1726     /* This key path has too many subkeys */
1727     if (SubkeysInTotal > CMP_SUBKEY_LEVELS_DEPTH_LIMIT)
1728     {
1729         DPRINT1("The key path has too many subkeys - %lu\n", SubkeysInTotal);
1730         *LockedKcbs = NULL;
1731         return STATUS_NAME_TOO_LONG;
1732     }
1733 
1734     /* Return hive and cell data */
1735     *Hive = (*Kcb)->KeyHive;
1736     *Cell = (*Kcb)->KeyCell;
1737 
1738     /* Do we have any subkeys? */
1739     if (!RemainingSubkeysInTotal && !SubkeysInTotal)
1740     {
1741         /*
1742          * We don't have any subkeys nor remaining levels, the
1743          * KCB points to the actual key. Lock it.
1744          */
1745         if (!CmpReferenceKeyControlBlock(*Kcb))
1746         {
1747             /* This key is opened too many times, bail out */
1748             DPRINT1("Could not reference the KCB, too many references (KCB 0x%p)\n", Kcb);
1749             return STATUS_UNSUCCESSFUL;
1750         }
1751 
1752         CmpAcquireKcbLockSharedByIndex(GET_HASH_INDEX(ConvKey));
1753 
1754         /* Add this KCB in the array of locked KCBs */
1755         OuterStackArray[0] = 1;
1756         OuterStackArray[1] = GET_HASH_INDEX(ConvKey);
1757         *LockedKcbs = OuterStackArray;
1758 
1759         /* And return all the subkey level counters */
1760         *TotalRemainingSubkeys = RemainingSubkeysInTotal;
1761         *MatchRemainSubkeyLevel = 0;
1762         *TotalSubkeys = SubkeysInTotal;
1763         return STATUS_SUCCESS;
1764     }
1765 
1766     /* Lookup in the cache */
1767     Status = CmpLookInCache(HashCacheStack,
1768                             FALSE,
1769                             RemainingSubkeysInTotal,
1770                             Current,
1771                             OuterStackArray,
1772                             Kcb,
1773                             Hive,
1774                             Cell,
1775                             &MatchRemainingSubkeys);
1776     if (!NT_SUCCESS(Status))
1777     {
1778         /* Bail out if cache lookup failed for other reasons */
1779         if (Status != STATUS_RETRY)
1780         {
1781             DPRINT1("CmpLookInCache() failed (Status 0x%lx)\n", Status);
1782             *LockedKcbs = NULL;
1783             return Status;
1784         }
1785 
1786         /* Retry looking in the cache but with KCBs locked exclusively */
1787         Status = CmpLookInCache(HashCacheStack,
1788                                 TRUE,
1789                                 RemainingSubkeysInTotal,
1790                                 Current,
1791                                 OuterStackArray,
1792                                 Kcb,
1793                                 Hive,
1794                                 Cell,
1795                                 &MatchRemainingSubkeys);
1796         if (!NT_SUCCESS(Status))
1797         {
1798             DPRINT1("CmpLookInCache() failed after retry (Status 0x%lx)\n", Status);
1799             *LockedKcbs = NULL;
1800             return Status;
1801         }
1802     }
1803 
1804     /*
1805      * Check if we have a full match of remaining levels.
1806      *
1807      * FIXME: It is possible we can catch a fake key from the cache
1808      * when we did the lookup, in such case we should not do any
1809      * locking as such KCB does not point to any real information.
1810      * Currently ReactOS doesn't create fake KCBs so we are good
1811      * for now.
1812      */
1813     if (RemainingSubkeysInTotal == MatchRemainingSubkeys)
1814     {
1815         /*
1816          * Just simply lock this KCB as it points to the full
1817          * subkey levels in cache.
1818          */
1819         CmpAcquireKcbLockSharedByIndex(GET_HASH_INDEX((*Kcb)->ConvKey));
1820         OuterStackArray[0] = 1;
1821         OuterStackArray[1] = GET_HASH_INDEX((*Kcb)->ConvKey);
1822         *LockedKcbs = OuterStackArray;
1823     }
1824     else
1825     {
1826         /*
1827          * We only have a partial match so other subkey levels
1828          * have each KCB. Simply just lock them.
1829          */
1830         *LockedKcbs = CmpBuildAndLockKcbArray(HashCacheStack,
1831                                               CMP_LOCK_KCB_ARRAY_EXCLUSIVE,
1832                                               *Kcb,
1833                                               OuterStackArray,
1834                                               RemainingSubkeysInTotal,
1835                                               MatchRemainingSubkeys);
1836         NT_ASSERT(*LockedKcbs);
1837     }
1838 
1839     /* Return all the subkey level counters */
1840     *TotalRemainingSubkeys = RemainingSubkeysInTotal;
1841     *MatchRemainSubkeyLevel = MatchRemainingSubkeys;
1842     *TotalSubkeys = SubkeysInTotal;
1843     return Status;
1844 }
1845 
1846 NTSTATUS
1847 NTAPI
CmpParseKey(IN PVOID ParseObject,IN PVOID ObjectType,IN OUT PACCESS_STATE AccessState,IN KPROCESSOR_MODE AccessMode,IN ULONG Attributes,IN OUT PUNICODE_STRING CompleteName,IN OUT PUNICODE_STRING RemainingName,IN OUT PVOID Context OPTIONAL,IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,OUT PVOID * Object)1848 CmpParseKey(IN PVOID ParseObject,
1849             IN PVOID ObjectType,
1850             IN OUT PACCESS_STATE AccessState,
1851             IN KPROCESSOR_MODE AccessMode,
1852             IN ULONG Attributes,
1853             IN OUT PUNICODE_STRING CompleteName,
1854             IN OUT PUNICODE_STRING RemainingName,
1855             IN OUT PVOID Context OPTIONAL,
1856             IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
1857             OUT PVOID *Object)
1858 {
1859     NTSTATUS Status;
1860     PCM_KEY_CONTROL_BLOCK Kcb, ParentKcb;
1861     PHHIVE Hive = NULL;
1862     PCM_KEY_NODE Node = NULL;
1863     HCELL_INDEX Cell = HCELL_NIL, NextCell;
1864     PHHIVE HiveToRelease = NULL;
1865     HCELL_INDEX CellToRelease = HCELL_NIL;
1866     UNICODE_STRING Current, NextName;
1867     PCM_PARSE_CONTEXT ParseContext = Context;
1868     ULONG TotalRemainingSubkeys = 0, MatchRemainSubkeyLevel = 0, TotalSubkeys = 0;
1869     ULONG LockedKcbArray[CMP_KCBS_IN_ARRAY_LIMIT];
1870     PULONG LockedKcbs;
1871     BOOLEAN IsKeyCached = FALSE;
1872     BOOLEAN Result, Last;
1873     PAGED_CODE();
1874 
1875     /* Loop path separators at the end */
1876     while (RemainingName->Length &&
1877            (RemainingName->Buffer[(RemainingName->Length / sizeof(WCHAR)) - 1] ==
1878             OBJ_NAME_PATH_SEPARATOR))
1879     {
1880         /* Remove path separator */
1881         RemainingName->Length -= sizeof(WCHAR);
1882     }
1883 
1884     /* Fail if this isn't a key object */
1885     if (ObjectType != CmpKeyObjectType) return STATUS_OBJECT_TYPE_MISMATCH;
1886 
1887     /* Copy the remaining name */
1888     Current = *RemainingName;
1889 
1890     /* Check if this is a create */
1891     if (!ParseContext || !ParseContext->CreateOperation)
1892     {
1893         /* It isn't, so no context */
1894         ParseContext = NULL;
1895     }
1896 
1897     /* Grab the KCB */
1898     Kcb = ((PCM_KEY_BODY)ParseObject)->KeyControlBlock;
1899 
1900     /* Sanity check */
1901     ASSERT(Kcb != NULL);
1902 
1903     /* Fail if the key was marked as deleted */
1904     if (Kcb->Delete)
1905         return STATUS_KEY_DELETED;
1906 
1907     /* Lookup in the cache */
1908     Status = CmpBuildHashStackAndLookupCache(ParseObject,
1909                                              &Kcb,
1910                                              &Current,
1911                                              &Hive,
1912                                              &Cell,
1913                                              &TotalRemainingSubkeys,
1914                                              &MatchRemainSubkeyLevel,
1915                                              &TotalSubkeys,
1916                                              LockedKcbArray,
1917                                              &LockedKcbs);
1918     CMP_ASSERT_REGISTRY_LOCK();
1919     if (!NT_SUCCESS(Status))
1920     {
1921         DPRINT1("Failed to look in cache, stop parsing (Status 0x%lx)\n", Status);
1922         ParentKcb = NULL;
1923         goto Quickie;
1924     }
1925 
1926     /* This is now the parent */
1927     ParentKcb = Kcb;
1928 
1929     /* Sanity check */
1930     ASSERT(ParentKcb != NULL);
1931 
1932     /* Don't do anything if we're being deleted */
1933     if (Kcb->Delete)
1934     {
1935         Status = STATUS_OBJECT_NAME_NOT_FOUND;
1936         goto Quickie;
1937     }
1938 
1939     /* Check if everything was found cached */
1940     if (!TotalRemainingSubkeys)
1941     {
1942         /*
1943          * We don't have any remaining subkey levels so we're good
1944          * that we have an already perfect candidate for a KCB, just
1945          * do the open directly.
1946          */
1947         DPRINT("No remaining subkeys, the KCB points to the actual key\n");
1948         IsKeyCached = TRUE;
1949         goto KeyCachedOpenNow;
1950     }
1951 
1952     /* Check if we have a matching level */
1953     if (MatchRemainSubkeyLevel)
1954     {
1955         /*
1956          * We have a matching level, check if that matches
1957          * with the total levels of subkeys. Do the open directly
1958          * if that is the case, because the whole subkeys levels
1959          * is cached.
1960          */
1961         if (MatchRemainSubkeyLevel == TotalSubkeys)
1962         {
1963             DPRINT("We have a full matching level, open the key now\n");
1964             IsKeyCached = TRUE;
1965             goto KeyCachedOpenNow;
1966         }
1967 
1968         /*
1969          * We only have a partial match, make sure we did not
1970          * get mismatched hive data.
1971          */
1972         ASSERT(Hive == Kcb->KeyHive);
1973         ASSERT(Cell == Kcb->KeyCell);
1974     }
1975 
1976     /*
1977      * FIXME: Currently the registry parser doesn't check for fake
1978      * KCBs. CmpCreateKeyControlBlock does have the necessary implementation
1979      * to create such fake keys but we don't create these fake keys anywhere.
1980      * When we will do, we must improve the registry parser routine to handle
1981      * fake keys a bit differently here.
1982      */
1983 
1984     /* Check if this is a symlink */
1985     if (Kcb->Flags & KEY_SYM_LINK)
1986     {
1987         /* Get the next name */
1988         Result = CmpGetNextName(&Current, &NextName, &Last);
1989         Current.Buffer = NextName.Buffer;
1990 
1991         /* Validate the current name string length */
1992         if (Current.Length + NextName.Length > MAXUSHORT)
1993         {
1994             /* too long */
1995             Status = STATUS_NAME_TOO_LONG;
1996             goto Quickie;
1997         }
1998         Current.Length += NextName.Length;
1999 
2000         /* Validate the current name string maximum length */
2001         if (Current.MaximumLength + NextName.MaximumLength > MAXUSHORT)
2002         {
2003             /* too long */
2004             Status = STATUS_NAME_TOO_LONG;
2005             goto Quickie;
2006         }
2007         Current.MaximumLength += NextName.MaximumLength;
2008 
2009         /* CmpGetSymbolicLink doesn't want a lock */
2010         CmpUnLockKcbArray(LockedKcbs);
2011         LockedKcbs = NULL;
2012 
2013         /* Parse the symlink */
2014         if (CmpGetSymbolicLink(Hive,
2015                                CompleteName,
2016                                Kcb,
2017                                &Current))
2018         {
2019             /* Symlink parse succeeded */
2020             Status = STATUS_REPARSE;
2021         }
2022         else
2023         {
2024             /* Couldn't find symlink */
2025             Status = STATUS_OBJECT_NAME_NOT_FOUND;
2026         }
2027 
2028         /* We're done */
2029         goto Quickie;
2030     }
2031 
2032     /* Get the key node */
2033     Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
2034     if (!Node)
2035     {
2036         Status = STATUS_INSUFFICIENT_RESOURCES;
2037         goto Quickie;
2038     }
2039 
2040     /* Start parsing */
2041     Status = STATUS_NOT_IMPLEMENTED;
2042     while (TRUE)
2043     {
2044         /* Get the next component */
2045         Result = CmpGetNextName(&Current, &NextName, &Last);
2046         if (Result && NextName.Length)
2047         {
2048             /* See if this is a sym link */
2049             if (!(Kcb->Flags & KEY_SYM_LINK))
2050             {
2051                 /* Find the subkey */
2052                 NextCell = CmpFindSubKeyByName(Hive, Node, &NextName);
2053                 if (NextCell != HCELL_NIL)
2054                 {
2055                     /* Get the new node */
2056                     Cell = NextCell;
2057                     Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
2058                     ASSERT(Node);
2059 
2060                     /* Check if this was the last key */
2061                     if (Last)
2062                     {
2063                         /* Is this an exit node */
2064                         if (Node->Flags & KEY_HIVE_EXIT)
2065                         {
2066                             /* Handle it */
2067                             CmpHandleExitNode(&Hive,
2068                                               &Cell,
2069                                               &Node,
2070                                               &HiveToRelease,
2071                                               &CellToRelease);
2072                             if (!Node)
2073                             {
2074                                 /* Fail */
2075                                 Status = STATUS_INSUFFICIENT_RESOURCES;
2076                                 break;
2077                             }
2078                         }
2079 
2080 KeyCachedOpenNow:
2081                         /* Do the open */
2082                         Status = CmpDoOpen(Hive,
2083                                            Cell,
2084                                            Node,
2085                                            AccessState,
2086                                            AccessMode,
2087                                            Attributes,
2088                                            ParseContext,
2089                                            IsKeyCached ? CMP_OPEN_KCB_NO_CREATE : CMP_CREATE_KCB_KCB_LOCKED,
2090                                            &Kcb,
2091                                            LockedKcbs,
2092                                            &NextName,
2093                                            Object);
2094                         if (Status == STATUS_REPARSE)
2095                         {
2096                             /* CmpGetSymbolicLink doesn't want a lock */
2097                             CmpUnLockKcbArray(LockedKcbs);
2098                             LockedKcbs = NULL;
2099 
2100                             /* Parse the symlink */
2101                             if (!CmpGetSymbolicLink(Hive,
2102                                                     CompleteName,
2103                                                     Kcb,
2104                                                     NULL))
2105                             {
2106                                 /* Symlink parse failed */
2107                                 Status = STATUS_OBJECT_NAME_NOT_FOUND;
2108                             }
2109                         }
2110 
2111                         /* We are done */
2112                         break;
2113                     }
2114 
2115                     /* Is this an exit node */
2116                     if (Node->Flags & KEY_HIVE_EXIT)
2117                     {
2118                         /* Handle it */
2119                         CmpHandleExitNode(&Hive,
2120                                           &Cell,
2121                                           &Node,
2122                                           &HiveToRelease,
2123                                           &CellToRelease);
2124                         if (!Node)
2125                         {
2126                             /* Fail */
2127                             Status = STATUS_INSUFFICIENT_RESOURCES;
2128                             break;
2129                         }
2130                     }
2131 
2132                     /* Create a KCB for this key */
2133                     Kcb = CmpCreateKeyControlBlock(Hive,
2134                                                    Cell,
2135                                                    Node,
2136                                                    ParentKcb,
2137                                                    CMP_LOCK_HASHES_FOR_KCB,
2138                                                    &NextName);
2139                     if (!Kcb)
2140                     {
2141                         /* Fail */
2142                         Status = STATUS_INSUFFICIENT_RESOURCES;
2143                         break;
2144                     }
2145 
2146                     /* Dereference the parent and set the new one */
2147                     CmpDereferenceKeyControlBlockWithLock(ParentKcb, FALSE);
2148                     ParentKcb = Kcb;
2149                 }
2150                 else
2151                 {
2152                     /* Check if this was the last key for a create */
2153                     if (Last && ParseContext)
2154                     {
2155                         /* Check if we're doing a link node */
2156                         if (ParseContext->CreateLink)
2157                         {
2158                             /* The only thing we should see */
2159                             Status = CmpCreateLinkNode(Hive,
2160                                                        Cell,
2161                                                        AccessState,
2162                                                        NextName,
2163                                                        AccessMode,
2164                                                        Attributes,
2165                                                        ParseContext,
2166                                                        ParentKcb,
2167                                                        LockedKcbs,
2168                                                        Object);
2169                         }
2170                         else if (Hive == &CmiVolatileHive->Hive && CmpNoVolatileCreates)
2171                         {
2172                             /* Creating keys in the master hive is not allowed */
2173                             Status = STATUS_INVALID_PARAMETER;
2174                         }
2175                         else
2176                         {
2177                             /* Do the create */
2178                             Status = CmpDoCreate(Hive,
2179                                                  Cell,
2180                                                  AccessState,
2181                                                  &NextName,
2182                                                  AccessMode,
2183                                                  ParseContext,
2184                                                  ParentKcb,
2185                                                  Object);
2186                         }
2187 
2188                         /* Check for reparse (in this case, someone beat us) */
2189                         if (Status == STATUS_REPARSE) break;
2190 
2191                         /* Update disposition */
2192                         ParseContext->Disposition = REG_CREATED_NEW_KEY;
2193                         break;
2194                     }
2195                     else
2196                     {
2197                         /* Key not found */
2198                         Status = STATUS_OBJECT_NAME_NOT_FOUND;
2199                         break;
2200                     }
2201                 }
2202             }
2203             else
2204             {
2205                 /* Save the next name */
2206                 Current.Buffer = NextName.Buffer;
2207 
2208                 /* Validate the current name string length */
2209                 if (Current.Length + NextName.Length > MAXUSHORT)
2210                 {
2211                     /* too long */
2212                     Status = STATUS_NAME_TOO_LONG;
2213                     break;
2214                 }
2215                 Current.Length += NextName.Length;
2216 
2217                 /* Validate the current name string maximum length */
2218                 if (Current.MaximumLength + NextName.MaximumLength > MAXUSHORT)
2219                 {
2220                     /* too long */
2221                     Status = STATUS_NAME_TOO_LONG;
2222                     break;
2223                 }
2224                 Current.MaximumLength += NextName.MaximumLength;
2225 
2226                 /* CmpGetSymbolicLink doesn't want a lock */
2227                 CmpUnLockKcbArray(LockedKcbs);
2228                 LockedKcbs = NULL;
2229 
2230                 /* Parse the symlink */
2231                 if (CmpGetSymbolicLink(Hive,
2232                                        CompleteName,
2233                                        Kcb,
2234                                        &Current))
2235                 {
2236                     /* Symlink parse succeeded */
2237                     Status = STATUS_REPARSE;
2238                 }
2239                 else
2240                 {
2241                     /* Couldn't find symlink */
2242                     Status = STATUS_OBJECT_NAME_NOT_FOUND;
2243                 }
2244 
2245                 /* We're done */
2246                 break;
2247             }
2248         }
2249         else if (Result && Last)
2250         {
2251             /* Opening the root. Is this an exit node? */
2252             if (Node->Flags & KEY_HIVE_EXIT)
2253             {
2254                 /* Handle it */
2255                 CmpHandleExitNode(&Hive,
2256                                   &Cell,
2257                                   &Node,
2258                                   &HiveToRelease,
2259                                   &CellToRelease);
2260                 if (!Node)
2261                 {
2262                     /* Fail */
2263                     Status = STATUS_INSUFFICIENT_RESOURCES;
2264                     break;
2265                 }
2266             }
2267 
2268             /* Do the open */
2269             Status = CmpDoOpen(Hive,
2270                                Cell,
2271                                Node,
2272                                AccessState,
2273                                AccessMode,
2274                                Attributes,
2275                                ParseContext,
2276                                CMP_OPEN_KCB_NO_CREATE,
2277                                &Kcb,
2278                                LockedKcbs,
2279                                &NextName,
2280                                Object);
2281             if (Status == STATUS_REPARSE)
2282             {
2283                 /* Nothing to do */
2284             }
2285 
2286             /* We're done */
2287             break;
2288         }
2289         else
2290         {
2291             /* Bogus */
2292             Status = STATUS_INVALID_PARAMETER;
2293             break;
2294         }
2295     }
2296 
2297 Quickie:
2298     /* Unlock all the KCBs */
2299     if (LockedKcbs != NULL)
2300     {
2301         CmpUnLockKcbArray(LockedKcbs);
2302     }
2303 
2304     /* Dereference the parent if it exists */
2305     if (ParentKcb)
2306         CmpDereferenceKeyControlBlock(ParentKcb);
2307 
2308     /* Unlock the registry */
2309     CmpUnlockRegistry();
2310     return Status;
2311 }
2312