From 0f4bc4008d15d2a1ca2ec9bee16c5bc5cee23658 Mon Sep 17 00:00:00 2001 From: Aleksey Bragin Date: Sat, 15 Dec 2007 20:51:40 +0000 Subject: [PATCH] - Fix typo in KCB flag. - CmDeleteKey takes a Key Body, not a KCB. - Simplify some code since ValueCache.RealKCB is a KCB now, not a hacked PKEY_OBJECT, so a bunch of typecasts can be removed. - Implement CmpCleanUpSubKeyInfo to remove cached subkey information when a KCB is deleted (subkeys are not cached yet, however). - Add checks in various code paths to make sure we don't touch a read-only KCB. - Don't double-dereference a key object during a delete. - Add checks for invalid key information classes. - For KeyNameInformation, the caller must have some handle access to succeed the call. - Validate alignment of key value names. - Don't accept NULLs at the end of key value names. - Validate key value name lengths. - Validate data lengths. - Remove a couple of forced lazy flushes that aren't required anymore. - Use CmpLockRegistry() in ntapi.c instead of the manual lock release. svn path=/trunk/; revision=31257 --- reactos/ntoskrnl/config/cmapi.c | 3 +- reactos/ntoskrnl/config/cmkcbncb.c | 64 ++++++- reactos/ntoskrnl/config/ntapi.c | 239 +++++++++++++++++-------- reactos/ntoskrnl/include/internal/cm.h | 26 ++- 4 files changed, 246 insertions(+), 86 deletions(-) diff --git a/reactos/ntoskrnl/config/cmapi.c b/reactos/ntoskrnl/config/cmapi.c index 82c8cdff5b8..6116455a5ee 100644 --- a/reactos/ntoskrnl/config/cmapi.c +++ b/reactos/ntoskrnl/config/cmapi.c @@ -1035,12 +1035,13 @@ Quickie: NTSTATUS NTAPI -CmDeleteKey(IN PCM_KEY_CONTROL_BLOCK Kcb) +CmDeleteKey(IN PCM_KEY_BODY KeyBody) { NTSTATUS Status; PHHIVE Hive; PCM_KEY_NODE Node, Parent; HCELL_INDEX Cell, ParentCell; + PCM_KEY_CONTROL_BLOCK Kcb = KeyBody->KeyControlBlock; /* Acquire hive lock */ KeEnterCriticalRegion(); diff --git a/reactos/ntoskrnl/config/cmkcbncb.c b/reactos/ntoskrnl/config/cmkcbncb.c index d741dabd047..18090f876c7 100644 --- a/reactos/ntoskrnl/config/cmkcbncb.c +++ b/reactos/ntoskrnl/config/cmkcbncb.c @@ -449,15 +449,15 @@ CmpCleanUpKcbValueCache(IN PCM_KEY_CONTROL_BLOCK Kcb) else if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND) { /* This is a sym link, check if there's only one reference left */ - if ((((PCM_KEY_CONTROL_BLOCK)Kcb->ValueCache.RealKcb)->RefCount == 1) && - !(((PCM_KEY_CONTROL_BLOCK)Kcb->ValueCache.RealKcb)->Delete)) + if ((Kcb->ValueCache.RealKcb->RefCount == 1) && + !(Kcb->ValueCache.RealKcb->Delete)) { /* Disable delay close for the KCB */ - ((PCM_KEY_CONTROL_BLOCK)Kcb->ValueCache.RealKcb)->ExtFlags |= CM_KCB_NO_DELAY_CLOSE; + Kcb->ValueCache.RealKcb->ExtFlags |= CM_KCB_NO_DELAY_CLOSE; } /* Dereference the KCB */ - CmpDelayDerefKeyControlBlock((PCM_KEY_CONTROL_BLOCK)Kcb->ValueCache.RealKcb); + CmpDelayDerefKeyControlBlock(Kcb->ValueCache.RealKcb); Kcb->ExtFlags &= ~CM_KCB_SYM_LINK_FOUND; } } @@ -501,6 +501,61 @@ CmpCleanUpKcbCacheWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb, } } +VOID +NTAPI +CmpCleanUpSubKeyInfo(IN PCM_KEY_CONTROL_BLOCK Kcb) +{ + PCM_KEY_NODE KeyNode; + + /* Sanity check */ + ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) || + (CmpTestRegistryLockExclusive() == TRUE)); + + /* Check if there's any cached subkey */ + if (Kcb->ExtFlags & (CM_KCB_NO_SUBKEY | CM_KCB_SUBKEY_ONE | CM_KCB_SUBKEY_HINT)) + { + /* Check if there's a hint */ + if (Kcb->ExtFlags & (CM_KCB_SUBKEY_HINT)) + { + /* Kill it */ + ExFreePool(Kcb->IndexHint); + } + + /* Remove subkey flags */ + Kcb->ExtFlags &= ~(CM_KCB_NO_SUBKEY | CM_KCB_SUBKEY_ONE | CM_KCB_SUBKEY_HINT); + } + + /* Check if there's no linked cell */ + if (Kcb->KeyCell == HCELL_NIL) + { + /* Make sure it's a delete */ + ASSERT(Kcb->Delete); + KeyNode = NULL; + } + else + { + /* Get the key node */ + KeyNode = (PCM_KEY_NODE)HvGetCell(Kcb->KeyHive, Kcb->KeyCell); + } + + /* Check if we got the node */ + if (!KeyNode) + { + /* We didn't, mark the cached data invalid */ + Kcb->ExtFlags |= CM_KCB_INVALID_CACHED_INFO; + } + else + { + /* We have a keynode, update subkey counts */ + Kcb->ExtFlags &= ~CM_KCB_INVALID_CACHED_INFO; + Kcb->SubKeyCount = KeyNode->SubKeyCounts[Stable] + + KeyNode->SubKeyCounts[Volatile]; + + /* Release the cell */ + HvReleaseCell(Kcb->KeyHive, Kcb->KeyCell); + } +} + VOID NTAPI CmpDereferenceKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb) @@ -821,4 +876,3 @@ EnlistKeyBodyWithKCB(IN PCM_KEY_BODY KeyBody, /* FIXME: Implement once we don't link parents to children anymore */ } - diff --git a/reactos/ntoskrnl/config/ntapi.c b/reactos/ntoskrnl/config/ntapi.c index 17642bbe321..57e27f21a1c 100644 --- a/reactos/ntoskrnl/config/ntapi.c +++ b/reactos/ntoskrnl/config/ntapi.c @@ -33,7 +33,7 @@ NtCreateKey(OUT PHANDLE KeyHandle, HANDLE Handle; PAGED_CODE(); DPRINT("NtCreateKey(OB 0x%wZ)\n", ObjectAttributes->ObjectName); - + /* Setup the parse context */ ParseContext.CreateOperation = TRUE; ParseContext.CreateOptions = CreateOptions; @@ -65,7 +65,7 @@ NtOpenKey(OUT PHANDLE KeyHandle, NTSTATUS Status; PAGED_CODE(); DPRINT("NtOpenKey(OB 0x%wZ)\n", ObjectAttributes->ObjectName); - + /* Just let the object manager handle this */ Status = ObOpenObjectByName(ObjectAttributes, CmpKeyObjectType, @@ -90,7 +90,6 @@ NtDeleteKey(IN HANDLE KeyHandle) REG_DELETE_KEY_INFORMATION DeleteKeyInfo; REG_POST_OPERATION_INFORMATION PostOperationInfo; PAGED_CODE(); - DPRINT("NtDeleteKey(KH 0x%p)\n", KeyHandle); /* Verify that the handle is valid and is a registry key */ @@ -98,13 +97,9 @@ NtDeleteKey(IN HANDLE KeyHandle) DELETE, CmpKeyObjectType, ExGetPreviousMode(), - (PVOID *)&KeyObject, + (PVOID*)&KeyObject, NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT("ObReferenceObjectByHandle() failed with Status = 0x%08X\n"); - return Status; - } + if (!NT_SUCCESS(Status)) return Status; /* Setup the callback */ PostOperationInfo.Object = (PVOID)KeyObject; @@ -112,16 +107,17 @@ NtDeleteKey(IN HANDLE KeyHandle) Status = CmiCallRegisteredCallbacks(RegNtPreDeleteKey, &DeleteKeyInfo); if (NT_SUCCESS(Status)) { - /* Call the internal API */ - Status = CmDeleteKey(KeyObject->KeyControlBlock); - - /* Remove the keep-alive reference */ - ObDereferenceObject(KeyObject); - if (KeyObject->KeyControlBlock->KeyHive != - KeyObject->KeyControlBlock->ParentKcb->KeyHive) + /* Check if we are read-only */ + if ((KeyObject->KeyControlBlock->ExtFlags & CM_KCB_READ_ONLY_KEY) || + (KeyObject->KeyControlBlock->ParentKcb->ExtFlags & CM_KCB_READ_ONLY_KEY)) { - /* Dereference again */ - ObDereferenceObject(KeyObject); + /* Fail */ + Status = STATUS_ACCESS_DENIED; + } + else + { + /* Call the internal API */ + Status = CmDeleteKey(KeyObject); } /* Do post callback */ @@ -148,22 +144,26 @@ NtEnumerateKey(IN HANDLE KeyHandle, REG_ENUMERATE_KEY_INFORMATION EnumerateKeyInfo; REG_POST_OPERATION_INFORMATION PostOperationInfo; PAGED_CODE(); - DPRINT("NtEnumerateKey() KH 0x%x, Index 0x%x, KIC %d, Length %d\n", - KeyHandle, Index, KeyInformationClass, Length); + KeyHandle, Index, KeyInformationClass, Length); + + /* Reject classes we don't know about */ + if ((KeyInformationClass != KeyBasicInformation) && + (KeyInformationClass != KeyNodeInformation) && + (KeyInformationClass != KeyFullInformation)) + { + /* Fail */ + return STATUS_INVALID_PARAMETER; + } /* Verify that the handle is valid and is a registry key */ Status = ObReferenceObjectByHandle(KeyHandle, KEY_ENUMERATE_SUB_KEYS, CmpKeyObjectType, ExGetPreviousMode(), - (PVOID *)&KeyObject, + (PVOID*)&KeyObject, NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT("ObReferenceObjectByHandle() failed with Status = 0x%08X\n"); - return Status; - } + if (!NT_SUCCESS(Status)) return Status; /* Setup the callback */ PostOperationInfo.Object = (PVOID)KeyObject; @@ -209,22 +209,26 @@ NtEnumerateValueKey(IN HANDLE KeyHandle, REG_ENUMERATE_VALUE_KEY_INFORMATION EnumerateValueKeyInfo; REG_POST_OPERATION_INFORMATION PostOperationInfo; PAGED_CODE(); - DPRINT("NtEnumerateValueKey() KH 0x%x, Index 0x%x, KVIC %d, Length %d\n", - KeyHandle, Index, KeyValueInformationClass, Length); + KeyHandle, Index, KeyValueInformationClass, Length); + + /* Reject classes we don't know about */ + if ((KeyValueInformationClass != KeyValueBasicInformation) && + (KeyValueInformationClass != KeyValueFullInformation) && + (KeyValueInformationClass != KeyValuePartialInformation)) + { + /* Fail */ + return STATUS_INVALID_PARAMETER; + } /* Verify that the handle is valid and is a registry key */ Status = ObReferenceObjectByHandle(KeyHandle, KEY_QUERY_VALUE, CmpKeyObjectType, ExGetPreviousMode(), - (PVOID *)&KeyObject, + (PVOID*)&KeyObject, NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT("ObReferenceObjectByHandle() failed with Status = 0x%08X\n"); - return Status; - } + if (!NT_SUCCESS(Status)) return Status; /* Setup the callback */ PostOperationInfo.Object = (PVOID)KeyObject; @@ -269,25 +273,57 @@ NtQueryKey(IN HANDLE KeyHandle, PCM_KEY_BODY KeyObject; REG_QUERY_KEY_INFORMATION QueryKeyInfo; REG_POST_OPERATION_INFORMATION PostOperationInfo; + OBJECT_HANDLE_INFORMATION HandleInfo; PAGED_CODE(); - DPRINT("NtQueryKey() KH 0x%x, KIC %d, Length %d\n", - KeyHandle, KeyInformationClass, Length); + KeyHandle, KeyInformationClass, Length); - /* Verify that the handle is valid and is a registry key */ - Status = ObReferenceObjectByHandle(KeyHandle, - (KeyInformationClass != - KeyNameInformation) ? - KEY_QUERY_VALUE : 0, - CmpKeyObjectType, - ExGetPreviousMode(), - (PVOID *)&KeyObject, - NULL); - if (!NT_SUCCESS(Status)) + /* Reject invalid classes */ + if ((KeyInformationClass != KeyBasicInformation) && + (KeyInformationClass != KeyNodeInformation) && + (KeyInformationClass != KeyFullInformation) && + (KeyInformationClass != KeyNameInformation) && + (KeyInformationClass != KeyCachedInformation) && + (KeyInformationClass != KeyFlagsInformation)) { - DPRINT("ObReferenceObjectByHandle() failed with Status = 0x%08X\n"); - return Status; + /* Fail */ + return STATUS_INVALID_PARAMETER; } + + /* Check if just the name is required */ + if (KeyInformationClass == KeyNameInformation) + { + /* Ignore access level */ + Status = ObReferenceObjectByHandle(KeyHandle, + 0, + CmpKeyObjectType, + ExGetPreviousMode(), + (PVOID*)&KeyObject, + &HandleInfo); + if (NT_SUCCESS(Status)) + { + /* At least a single bit of access is required */ + if (!HandleInfo.GrantedAccess) + { + /* No such luck */ + ObDereferenceObject(KeyObject); + Status = STATUS_ACCESS_DENIED; + } + } + } + else + { + /* Get a reference */ + Status = ObReferenceObjectByHandle(KeyHandle, + KEY_QUERY_VALUE, + CmpKeyObjectType, + ExGetPreviousMode(), + (PVOID*)&KeyObject, + NULL); + } + + /* Quit on failure */ + if (!NT_SUCCESS(Status)) return Status; /* Setup the callback */ PostOperationInfo.Object = (PVOID)KeyObject; @@ -331,8 +367,8 @@ NtQueryValueKey(IN HANDLE KeyHandle, PCM_KEY_BODY KeyObject; REG_QUERY_VALUE_KEY_INFORMATION QueryValueKeyInfo; REG_POST_OPERATION_INFORMATION PostOperationInfo; + UNICODE_STRING ValueNameCopy = *ValueName; PAGED_CODE(); - DPRINT("NtQueryValueKey() KH 0x%x, VN '%wZ', KVIC %d, Length %d\n", KeyHandle, ValueName, KeyValueInformationClass, Length); @@ -341,18 +377,32 @@ NtQueryValueKey(IN HANDLE KeyHandle, KEY_QUERY_VALUE, CmpKeyObjectType, ExGetPreviousMode(), - (PVOID *)&KeyObject, + (PVOID*)&KeyObject, NULL); - if (!NT_SUCCESS(Status)) + if (!NT_SUCCESS(Status)) return Status; + + /* Make sure the name is aligned properly */ + if ((ValueNameCopy.Length & (sizeof(WCHAR) - 1))) { - DPRINT("ObReferenceObjectByHandle() failed with Status = 0x%08X\n"); - return Status; + /* It isn't, so we'll fail */ + ObDereferenceObject(KeyObject); + return STATUS_INVALID_PARAMETER; + } + else + { + /* Ignore any null characters at the end */ + while ((ValueNameCopy.Length) && + !(ValueNameCopy.Buffer[ValueNameCopy.Length / sizeof(WCHAR) - 1])) + { + /* Skip it */ + ValueNameCopy.Length -= sizeof(WCHAR); + } } /* Setup the callback */ PostOperationInfo.Object = (PVOID)KeyObject; QueryValueKeyInfo.Object = (PVOID)KeyObject; - QueryValueKeyInfo.ValueName = ValueName; + QueryValueKeyInfo.ValueName = &ValueNameCopy; QueryValueKeyInfo.KeyValueInformationClass = KeyValueInformationClass; QueryValueKeyInfo.Length = Length; QueryValueKeyInfo.ResultLength = ResultLength; @@ -363,7 +413,7 @@ NtQueryValueKey(IN HANDLE KeyHandle, { /* Call the internal API */ Status = CmQueryValueKey(KeyObject->KeyControlBlock, - *ValueName, + ValueNameCopy, KeyValueInformationClass, KeyValueInformation, Length, @@ -374,8 +424,6 @@ NtQueryValueKey(IN HANDLE KeyHandle, CmiCallRegisteredCallbacks(RegNtPostQueryValueKey, &PostOperationInfo); } - DPRINT("NtQueryValueKey() returning 0x%08X\n", Status); - /* Dereference and return status */ ObDereferenceObject(KeyObject); return Status; @@ -394,8 +442,8 @@ NtSetValueKey(IN HANDLE KeyHandle, PCM_KEY_BODY KeyObject; REG_SET_VALUE_KEY_INFORMATION SetValueKeyInfo; REG_POST_OPERATION_INFORMATION PostOperationInfo; + UNICODE_STRING ValueNameCopy = *ValueName; PAGED_CODE(); - DPRINT("NtSetValueKey() KH 0x%x, VN '%wZ', TI %x, T %d, DS %d\n", KeyHandle, ValueName, TitleIndex, Type, DataSize); @@ -404,12 +452,34 @@ NtSetValueKey(IN HANDLE KeyHandle, KEY_SET_VALUE, CmpKeyObjectType, ExGetPreviousMode(), - (PVOID *)&KeyObject, + (PVOID*)&KeyObject, NULL); - if (!NT_SUCCESS(Status)) + if (!NT_SUCCESS(Status)) return Status; + + /* Make sure the name is aligned, not too long, and the data under 4GB */ + if ( (ValueNameCopy.Length > 32767) || + ((ValueNameCopy.Length & (sizeof(WCHAR) - 1))) || + (DataSize > 0x80000000)) { - DPRINT1("ObReferenceObjectByHandle() failed with Status = 0x%08X\n", Status); - return Status; + /* Fail */ + ObDereferenceObject(KeyObject); + return STATUS_INVALID_PARAMETER; + } + + /* Ignore any null characters at the end */ + while ((ValueNameCopy.Length) && + !(ValueNameCopy.Buffer[ValueNameCopy.Length / sizeof(WCHAR) - 1])) + { + /* Skip it */ + ValueNameCopy.Length -= sizeof(WCHAR); + } + + /* Don't touch read-only keys */ + if (KeyObject->KeyControlBlock->ExtFlags & CM_KCB_READ_ONLY_KEY) + { + /* Fail */ + ObDereferenceObject(KeyObject); + return STATUS_ACCESS_DENIED; } /* Setup callback */ @@ -427,19 +497,18 @@ NtSetValueKey(IN HANDLE KeyHandle, { /* Call the internal API */ Status = CmSetValueKey(KeyObject->KeyControlBlock, - ValueName, + &ValueNameCopy, Type, Data, DataSize); } - /* Do the post-callback and de-reference the key object */ + /* Do the post-callback */ PostOperationInfo.Status = Status; CmiCallRegisteredCallbacks(RegNtPostSetValueKey, &PostOperationInfo); - ObDereferenceObject(KeyObject); - /* Synchronize the hives and return */ - CmpLazyFlush(); + /* Dereference and return status */ + ObDereferenceObject(KeyObject); return Status; } @@ -453,6 +522,7 @@ NtDeleteValueKey(IN HANDLE KeyHandle, REG_DELETE_VALUE_KEY_INFORMATION DeleteValueKeyInfo; REG_POST_OPERATION_INFORMATION PostOperationInfo; KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); + UNICODE_STRING ValueNameCopy = *ValueName; PAGED_CODE(); /* Verify that the handle is valid and is a registry key */ @@ -462,10 +532,22 @@ NtDeleteValueKey(IN HANDLE KeyHandle, PreviousMode, (PVOID *)&KeyObject, NULL); - if (!NT_SUCCESS(Status)) + if (!NT_SUCCESS(Status)) return Status; + + /* Don't touch read-only keys */ + if (KeyObject->KeyControlBlock->ExtFlags & CM_KCB_READ_ONLY_KEY) { - DPRINT("ObReferenceObjectByHandle() failed with Status = 0x%08X\n"); - return Status; + /* Fail */ + ObDereferenceObject(KeyObject); + return STATUS_ACCESS_DENIED; + } + + /* Make sure the name is aligned properly */ + if ((ValueNameCopy.Length & (sizeof(WCHAR) - 1))) + { + /* It isn't, so we'll fail */ + ObDereferenceObject(KeyObject); + return STATUS_INVALID_PARAMETER; } /* Do the callback */ @@ -476,7 +558,7 @@ NtDeleteValueKey(IN HANDLE KeyHandle, if (NT_SUCCESS(Status)) { /* Call the internal API */ - Status = CmDeleteValueKey(KeyObject->KeyControlBlock, *ValueName); + Status = CmDeleteValueKey(KeyObject->KeyControlBlock, ValueNameCopy); /* Do the post callback */ PostOperationInfo.Object = (PVOID)KeyObject; @@ -485,9 +567,8 @@ NtDeleteValueKey(IN HANDLE KeyHandle, &PostOperationInfo); } - /* Dereference the key body and synchronize the hives */ + /* Dereference the key body */ ObDereferenceObject(KeyObject); - CmpLazyFlush(); return Status; } @@ -509,8 +590,10 @@ NtFlushKey(IN HANDLE KeyHandle) if (!NT_SUCCESS(Status)) return Status; /* Lock the registry */ - KeEnterCriticalRegion(); - ExAcquireResourceExclusiveLite(&CmpRegistryLock, TRUE); + CmpLockRegistry(); + + /* Lock the KCB */ + CmpAcquireKcbLockShared(KeyObject->KeyControlBlock); /* Make sure KCB isn't deleted */ if (KeyObject->KeyControlBlock->Delete) @@ -524,9 +607,9 @@ NtFlushKey(IN HANDLE KeyHandle) Status = CmFlushKey(KeyObject->KeyControlBlock, FALSE); } - /* Release the lock */ - ExReleaseResourceLite(&CmpRegistryLock); - KeLeaveCriticalRegion(); + /* Release the locks */ + CmpReleaseKcbLock(KeyObject->KeyControlBlock); + CmpUnlockRegistry(); /* Dereference the object and return status */ ObDereferenceObject(KeyObject); diff --git a/reactos/ntoskrnl/include/internal/cm.h b/reactos/ntoskrnl/include/internal/cm.h index 87425d2d8b8..e0de92d9a2e 100644 --- a/reactos/ntoskrnl/include/internal/cm.h +++ b/reactos/ntoskrnl/include/internal/cm.h @@ -53,7 +53,7 @@ #define CM_KCB_KEY_NON_EXIST 0x10 #define CM_KCB_NO_DELAY_CLOSE 0x20 #define CM_KCB_INVALID_CACHED_INFO 0x40 -#define CM_KEY_READ_ONLY_KEY 0x80 +#define CM_KCB_READ_ONLY_KEY 0x80 // // CM_KEY_VALUE Types @@ -577,6 +577,16 @@ CmpGetValueKeyFromCache( OUT PHCELL_INDEX CellToRelease ); +VALUE_SEARCH_RETURN_TYPE +NTAPI +CmpCompareNewValueDataAgainstKCBCache( + IN PCM_KEY_CONTROL_BLOCK Kcb, + IN PUNICODE_STRING ValueName, + IN ULONG Type, + IN PVOID Data, + IN ULONG DataSize +); + // // Registry Validation Functions // @@ -858,6 +868,12 @@ CmpRemoveKeyControlBlock( IN PCM_KEY_CONTROL_BLOCK Kcb ); +VOID +NTAPI +CmpCleanUpKcbValueCache( + IN PCM_KEY_CONTROL_BLOCK Kcb +); + VOID NTAPI CmpCleanUpKcbCacheWithLock( @@ -865,6 +881,12 @@ CmpCleanUpKcbCacheWithLock( IN BOOLEAN LockHeldExclusively ); +VOID +NTAPI +CmpCleanUpSubKeyInfo( + IN PCM_KEY_CONTROL_BLOCK Kcb +); + VOID NTAPI CmpDereferenceKeyControlBlockWithLock( @@ -1324,7 +1346,7 @@ CmEnumerateKey(IN PCM_KEY_CONTROL_BLOCK Kcb, NTSTATUS NTAPI CmDeleteKey( - IN PCM_KEY_CONTROL_BLOCK Kcb + IN PCM_KEY_BODY KeyBody ); NTSTATUS