Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

ntapi.c

Go to the documentation of this file.
00001 /*++ 00002 00003 Copyright (c) 1991 Microsoft Corporation 00004 00005 Module Name: 00006 00007 ntapi.c 00008 00009 Abstract: 00010 00011 This module contains the NT level entry points for the registry. 00012 00013 Author: 00014 00015 Bryan M. Willman (bryanwi) 26-Aug-1991 00016 00017 Revision History: 00018 00019 Elliot Shmukler (t-ellios) 24-Aug-1998 00020 00021 Modified NtInitializeRegistry to handle the LKG work that needs 00022 to be done when a boot is accepted by SC. 00023 00024 --*/ 00025 00026 #include "cmp.h" 00027 #include "safeboot.h" 00028 #include <evntrace.h> 00029 00030 extern POBJECT_TYPE ExEventObjectType; 00031 00032 extern POBJECT_TYPE CmpKeyObjectType; 00033 00034 extern BOOLEAN CmFirstTime; 00035 extern BOOLEAN CmBootAcceptFirstTime; 00036 00037 extern BOOLEAN CmpTraceFlag; 00038 00039 // 00040 // Nt API helper routines 00041 // 00042 NTSTATUS 00043 CmpNameFromAttributes( 00044 IN POBJECT_ATTRIBUTES Attributes, 00045 KPROCESSOR_MODE PreviousMode, 00046 OUT PUNICODE_STRING FullName 00047 ); 00048 00049 #ifdef POOL_TAGGING 00050 00051 #define ALLOCATE_WITH_QUOTA(a,b,c) ExAllocatePoolWithQuotaTag((a)|POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,b,c) 00052 00053 #else 00054 00055 #define ALLOCATE_WITH_QUOTA(a,b,c) ExAllocatePoolWithQuota((a)|POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,b) 00056 00057 #endif 00058 00059 #if DBG 00060 00061 ULONG 00062 CmpExceptionFilter( 00063 IN PEXCEPTION_POINTERS ExceptionPointers 00064 ); 00065 00066 #ifdef ALLOC_PRAGMA 00067 #pragma alloc_text(PAGE,CmpExceptionFilter) 00068 #endif 00069 #else 00070 00071 #define CmpExceptionFilter(x) EXCEPTION_EXECUTE_HANDLER 00072 00073 #endif 00074 00075 #ifdef REGISTRY_LOCK_CHECKING 00076 #ifdef ALLOC_PRAGMA 00077 #pragma alloc_text(PAGE,CmpCheckLockExceptionFilter) 00078 #endif 00079 00080 ULONG 00081 CmpCheckLockExceptionFilter( 00082 IN PEXCEPTION_POINTERS ExceptionPointers 00083 ) 00084 { 00085 KdPrint(("CM: Registry exception %lx, ExceptionPointers = %lx\n", 00086 ExceptionPointers->ExceptionRecord->ExceptionCode, 00087 ExceptionPointers)); 00088 00089 KeBugCheckEx(REGISTRY_ERROR,12, 00090 (ULONG_PTR)ExceptionPointers->ExceptionRecord->ExceptionCode, 00091 (ULONG_PTR)ExceptionPointers->ExceptionRecord, 00092 (ULONG_PTR)ExceptionPointers->ContextRecord); 00093 00094 return EXCEPTION_EXECUTE_HANDLER; 00095 } 00096 #endif //REGISTRY_LOCK_CHECKING 00097 00098 #ifdef KCB_TO_KEYBODY_LINK 00099 VOID 00100 CmpFlushNotifiesOnKeyBodyList( 00101 IN PCM_KEY_CONTROL_BLOCK kcb 00102 ); 00103 #else 00104 BOOLEAN 00105 CmpEnumKeyObjectCallback( 00106 IN PVOID Object, 00107 IN PUNICODE_STRING ObjectName, 00108 IN ULONG HandleCount, 00109 IN ULONG PointerCount, 00110 IN PVOID Context 00111 ); 00112 00113 #endif 00114 00115 VOID 00116 CmpDummyApc( 00117 struct _KAPC *Apc, 00118 PVOID *SystemArgument1, 00119 PVOID *SystemArgument2 00120 ); 00121 00122 #ifdef ALLOC_PRAGMA 00123 #pragma alloc_text(PAGE,NtCreateKey) 00124 #pragma alloc_text(PAGE,NtDeleteKey) 00125 #pragma alloc_text(PAGE,NtDeleteValueKey) 00126 #pragma alloc_text(PAGE,NtEnumerateKey) 00127 #pragma alloc_text(PAGE,NtEnumerateValueKey) 00128 #pragma alloc_text(PAGE,NtFlushKey) 00129 #pragma alloc_text(PAGE,NtInitializeRegistry) 00130 #pragma alloc_text(PAGE,NtNotifyChangeKey) 00131 #pragma alloc_text(PAGE,NtNotifyChangeMultipleKeys) 00132 #pragma alloc_text(PAGE,NtOpenKey) 00133 #pragma alloc_text(PAGE,NtQueryKey) 00134 #pragma alloc_text(PAGE,NtQueryValueKey) 00135 #pragma alloc_text(PAGE,NtQueryMultipleValueKey) 00136 #pragma alloc_text(PAGE,NtRestoreKey) 00137 #pragma alloc_text(PAGE,NtSaveKey) 00138 #pragma alloc_text(PAGE,NtSaveMergedKeys) 00139 #pragma alloc_text(PAGE,NtSetValueKey) 00140 #pragma alloc_text(PAGE,NtLoadKey) 00141 #pragma alloc_text(PAGE,NtUnloadKey) 00142 #pragma alloc_text(PAGE,NtSetInformationKey) 00143 #pragma alloc_text(PAGE,NtReplaceKey) 00144 #pragma alloc_text(PAGE,NtQueryOpenSubKeys) 00145 #pragma alloc_text(PAGE,CmpNameFromAttributes) 00146 #pragma alloc_text(PAGE,CmpAllocatePostBlock) 00147 #pragma alloc_text(PAGE,CmpFreePostBlock) 00148 00149 #ifndef KCB_TO_KEYBODY_LINK 00150 #pragma alloc_text(PAGE,CmpEnumKeyObjectCallback) 00151 #endif 00152 00153 #endif 00154 00155 // #define LOG_NT_API 1 00156 00157 #ifdef LOG_NT_API 00158 BOOLEAN CmpLogApi = FALSE; 00159 #endif 00160 00161 // 00162 // Nt level registry API calls 00163 // 00164 00165 NTSTATUS 00166 NtCreateKey( 00167 OUT PHANDLE KeyHandle, 00168 IN ACCESS_MASK DesiredAccess, 00169 IN POBJECT_ATTRIBUTES ObjectAttributes, 00170 IN ULONG TitleIndex, 00171 IN PUNICODE_STRING Class OPTIONAL, 00172 IN ULONG CreateOptions, 00173 OUT PULONG Disposition OPTIONAL 00174 ) 00175 /*++ 00176 00177 Routine Description: 00178 00179 An existing registry key may be opened, or a new one created, 00180 with NtCreateKey. 00181 00182 If the specified key does not exist, an attempt is made to create it. 00183 For the create attempt to succeed, the new node must be a direct 00184 child of the node referred to by KeyHandle. If the node exists, 00185 it is opened. Its value is not affected in any way. 00186 00187 Share access is computed from desired access. 00188 00189 NOTE: 00190 00191 If CreateOptions has REG_OPTION_BACKUP_RESTORE set, then 00192 DesiredAccess will be ignored. If the caller has the 00193 privilege SeBackupPrivilege asserted, a handle with 00194 KEY_READ | ACCESS_SYSTEM_SECURITY will be returned. 00195 If SeRestorePrivilege, then same but KEY_WRITE rather 00196 than KEY_READ. If both, then both access sets. If neither 00197 privilege is asserted, then the call will fail. 00198 00199 Arguments: 00200 00201 KeyHandle - Receives a Handle which is used to access the 00202 specified key in the Registration Database. 00203 00204 DesiredAccess - Specifies the access rights desired. 00205 00206 ObjectAttributes - Specifies the attributes of the key being opened. 00207 Note that a key name must be specified. If a Root Directory is 00208 specified, the name is relative to the root. The name of the 00209 object must be within the name space allocated to the Registry, 00210 that is, all names beginning "\Registry". RootHandle, if 00211 present, must be a handle to "\", or "\Registry", or a key 00212 under "\Registry". 00213 00214 RootHandle must have been opened for KEY_CREATE_SUB_KEY access 00215 if a new node is to be created. 00216 00217 NOTE: Object manager will capture and probe this argument. 00218 00219 TitleIndex - Specifies the index of the localized alias for 00220 the name of the key. The title index specifies the index of 00221 the localized alias for the name. Ignored if the key 00222 already exists. 00223 00224 Class - Specifies the object class of the key. (To the registry 00225 this is just a string.) Ignored if NULL. 00226 00227 CreateOptions - Optional control values: 00228 00229 REG_OPTION_VOLATILE - Object is not to be stored across boots. 00230 00231 Disposition - This optional parameter is a pointer to a variable 00232 that will receive a value indicating whether a new Registry 00233 key was created or an existing one opened: 00234 00235 REG_CREATED_NEW_KEY - A new Registry Key was created 00236 REG_OPENED_EXISTING_KEY - An existing Registry Key was opened 00237 00238 Return Value: 00239 00240 NTSTATUS - Result code from call, among the following: 00241 00242 <TBS> 00243 00244 --*/ 00245 { 00246 NTSTATUS status; 00247 KPROCESSOR_MODE mode; 00248 CM_PARSE_CONTEXT ParseContext; 00249 PCM_KEY_BODY KeyBody; 00250 HANDLE Handle = 0; 00251 UNICODE_STRING CapturedObjectName = {0}; 00252 00253 // Start registry call tracing 00254 StartWmiCmTrace(); 00255 00256 PAGED_CODE(); 00257 00258 BEGIN_LOCK_CHECKPOINT; 00259 00260 CMLOG(CML_API, CMS_NTAPI) KdPrint(("NtCreateKey\n")); 00261 CMLOG(CML_API_ARGS, CMS_NTAPI) { 00262 KdPrint(("\tDesiredAccess=%08lx ", DesiredAccess)); 00263 KdPrint(("\tCreateOptions=%08lx\n", CreateOptions)); 00264 KdPrint(("\tRootHandle=%08lx\n", ObjectAttributes->RootDirectory)); 00265 KdPrint(("\tName='%wZ'\n", ObjectAttributes->ObjectName)); 00266 } 00267 00268 mode = KeGetPreviousMode(); 00269 CmpLockRegistryExclusive(); 00270 00271 #ifdef LOG_NT_API 00272 if (CmpLogApi) { 00273 KdPrint(("NtCreateKey %wZ relative to %d...", 00274 ObjectAttributes->ObjectName, 00275 ObjectAttributes->RootDirectory)); 00276 } 00277 #endif 00278 00279 try { 00280 00281 ParseContext.Class.Length = 0; 00282 ParseContext.Class.Buffer = NULL; 00283 00284 if (mode == UserMode) { 00285 00286 if (ARGUMENT_PRESENT(Class)) { 00287 ParseContext.Class = ProbeAndReadUnicodeString(Class); 00288 ProbeForRead( 00289 ParseContext.Class.Buffer, 00290 ParseContext.Class.Length, 00291 sizeof(WCHAR) 00292 ); 00293 } 00294 ProbeAndZeroHandle(KeyHandle); 00295 00296 if (ARGUMENT_PRESENT(Disposition)) { 00297 ProbeForWriteUlong(Disposition); 00298 } 00299 00300 // 00301 // probe the ObjectAttributes as we shall use it for tracing 00302 // 00303 ProbeForRead( ObjectAttributes, 00304 sizeof(OBJECT_ATTRIBUTES), 00305 PROBE_ALIGNMENT(OBJECT_ATTRIBUTES) ); 00306 CapturedObjectName = ProbeAndReadUnicodeString(ObjectAttributes->ObjectName); 00307 ProbeForRead( 00308 CapturedObjectName.Buffer, 00309 CapturedObjectName.Length, 00310 sizeof(WCHAR) 00311 ); 00312 00313 } else { 00314 if (ARGUMENT_PRESENT(Class)) { 00315 ParseContext.Class = *Class; 00316 } 00317 CapturedObjectName = *(ObjectAttributes->ObjectName); 00318 } 00319 00320 if ((CreateOptions & REG_LEGAL_OPTION) != CreateOptions) { 00321 CmpUnlockRegistry(); 00322 00323 // End registry call tracing 00324 EndWmiCmTrace(STATUS_INVALID_PARAMETER,0,&CapturedObjectName,EVENT_TRACE_TYPE_REGCREATE); 00325 00326 return STATUS_INVALID_PARAMETER; 00327 } 00328 00329 ParseContext.TitleIndex = 0; 00330 ParseContext.CreateOptions = CreateOptions; 00331 ParseContext.Disposition = 0L; 00332 ParseContext.CreateLink = FALSE; 00333 ParseContext.PredefinedHandle = NULL; 00334 00335 status = ObOpenObjectByName( 00336 ObjectAttributes, 00337 CmpKeyObjectType, 00338 mode, 00339 NULL, 00340 DesiredAccess, 00341 (PVOID)&ParseContext, 00342 &Handle 00343 ); 00344 00345 if (status==STATUS_PREDEFINED_HANDLE) { 00346 status = ObReferenceObjectByHandle(Handle, 00347 0, 00348 CmpKeyObjectType, 00349 KernelMode, 00350 (PVOID *)(&KeyBody), 00351 NULL); 00352 if (NT_SUCCESS(status)) { 00353 HANDLE TempHandle; 00354 00355 // 00356 // Make sure we do the dereference and close before accessing 00357 // user space as the reference might fault. 00358 // 00359 TempHandle = (HANDLE)LongToHandle(KeyBody->Type); 00360 ObDereferenceObject((PVOID)KeyBody); 00361 NtClose(Handle); 00362 Handle = *KeyHandle = TempHandle; 00363 status = STATUS_SUCCESS; 00364 } 00365 } else 00366 if (NT_SUCCESS(status)) { 00367 *KeyHandle = Handle; 00368 } 00369 00370 if (ARGUMENT_PRESENT(Disposition)) { 00371 *Disposition = ParseContext.Disposition; 00372 } 00373 00374 } except (CmpExceptionFilter(GetExceptionInformation())) { 00375 CMLOG(CML_API, CMS_EXCEPTION) { 00376 KdPrint(("!!NtCreateKey: code:%08lx\n", GetExceptionCode())); 00377 } 00378 status = GetExceptionCode(); 00379 } 00380 #ifdef LOG_NT_API 00381 if (CmpLogApi) { 00382 if (NT_SUCCESS(status)) { 00383 KdPrint(("succeeded %d\n",Handle)); 00384 } else { 00385 KdPrint(("failed %08lx\n",status)); 00386 } 00387 } 00388 #endif 00389 00390 00391 CmpUnlockRegistry(); 00392 00393 END_LOCK_CHECKPOINT; 00394 00395 // End registry call tracing 00396 EndWmiCmTrace(status,Handle,&CapturedObjectName,EVENT_TRACE_TYPE_REGCREATE); 00397 00398 return status; 00399 } 00400 00401 extern PCM_KEY_BODY ExpControlKey[2]; 00402 00403 // 00404 // WARNING: This should be the same as the one defined in obp.h 00405 // Remove this one when object manager guys will export 00406 // this via ob.h 00407 // 00408 #define OBJ_AUDIT_OBJECT_CLOSE 0x00000004L 00409 00410 NTSTATUS 00411 NtDeleteKey( 00412 IN HANDLE KeyHandle 00413 ) 00414 /*++ 00415 00416 Routine Description: 00417 00418 A registry key may be marked for delete, causing it to be removed 00419 from the system. It will remain in the name space until the last 00420 handle to it is closed. 00421 00422 Arguments: 00423 00424 KeyHandle - Specifies the handle of the Key to delete, must have 00425 been opened for DELETE access. 00426 00427 Return Value: 00428 00429 NTSTATUS - Result code from call, among the following: 00430 00431 <TBS> 00432 00433 --*/ 00434 { 00435 PCM_KEY_BODY KeyBody; 00436 NTSTATUS status; 00437 OBJECT_HANDLE_INFORMATION HandleInfo; 00438 00439 // Start registry call tracing 00440 StartWmiCmTrace(); 00441 00442 PAGED_CODE(); 00443 00444 BEGIN_LOCK_CHECKPOINT; 00445 00446 CMLOG(CML_API, CMS_NTAPI) KdPrint(("NtDeleteKey\n")); 00447 CMLOG(CML_API_ARGS, CMS_NTAPI) { 00448 KdPrint(("\tKeyHandle=%08lx\n", KeyHandle)); 00449 } 00450 #ifdef LOG_NT_API 00451 if (CmpLogApi) { 00452 KdPrint(("NtDeleteKey %d\n",KeyHandle)); 00453 } 00454 #endif 00455 00456 status = ObReferenceObjectByHandle(KeyHandle, 00457 DELETE, 00458 CmpKeyObjectType, 00459 KeGetPreviousMode(), 00460 (PVOID *)(&KeyBody), 00461 &HandleInfo); 00462 00463 if (NT_SUCCESS(status)) { 00464 00465 // 00466 // Silently fail deletes of setup key and productoptions key 00467 // 00468 00469 if ( (ExpControlKey[0] && KeyBody->KeyControlBlock == ExpControlKey[0]->KeyControlBlock) || 00470 (ExpControlKey[1] && KeyBody->KeyControlBlock == ExpControlKey[1]->KeyControlBlock) ) { 00471 ObDereferenceObject((PVOID)KeyBody); 00472 00473 // End registry call tracing 00474 EndWmiCmTrace(STATUS_SUCCESS,KeyHandle,NULL,EVENT_TRACE_TYPE_REGDELETE); 00475 00476 return STATUS_SUCCESS; 00477 } 00478 00479 status = CmDeleteKey(KeyBody); 00480 00481 if (NT_SUCCESS(status)) { 00482 // 00483 // Audit the deletion 00484 // 00485 00486 if ( HandleInfo.HandleAttributes & OBJ_AUDIT_OBJECT_CLOSE ) { 00487 SeDeleteObjectAuditAlarm(KeyBody, 00488 KeyHandle ); 00489 } 00490 } 00491 ObDereferenceObject((PVOID)KeyBody); 00492 } 00493 00494 END_LOCK_CHECKPOINT; 00495 00496 // End registry call tracing 00497 EndWmiCmTrace(status,KeyHandle,NULL,EVENT_TRACE_TYPE_REGDELETE); 00498 00499 return status; 00500 } 00501 00502 00503 NTSTATUS 00504 NtDeleteValueKey( 00505 IN HANDLE KeyHandle, 00506 IN PUNICODE_STRING ValueName 00507 ) 00508 /*++ 00509 00510 Routine Description: 00511 00512 One of the value entries of a registry key may be removed with this 00513 call. To remove the entire key, call NtDeleteKey. 00514 00515 The value entry with ValueName matching ValueName is removed from the key. 00516 If no such entry exists, an error is returned. 00517 00518 Arguments: 00519 00520 KeyHandle - Specifies the handle of the key containing the value 00521 entry of interest. Must have been opend for KEY_SET_VALUE access. 00522 00523 ValueName - The name of the value to be deleted. NULL is a legal name. 00524 00525 Return Value: 00526 00527 NTSTATUS - Result code from call, among the following: 00528 00529 <TBS> 00530 00531 --*/ 00532 { 00533 NTSTATUS status; 00534 PCM_KEY_BODY KeyBody; 00535 KPROCESSOR_MODE mode; 00536 UNICODE_STRING LocalValueName; 00537 00538 // Start registry call tracing 00539 StartWmiCmTrace(); 00540 00541 PAGED_CODE(); 00542 00543 BEGIN_LOCK_CHECKPOINT; 00544 00545 CMLOG(CML_API, CMS_NTAPI) KdPrint(("NtDeleteValueKey\n")); 00546 CMLOG(CML_API_ARGS, CMS_NTAPI) { 00547 KdPrint(("\tKeyHandle=%08lx\n", KeyHandle)); 00548 KdPrint(("\tValueName='%wZ'\n", ValueName)); 00549 } 00550 #ifdef LOG_NT_API 00551 if (CmpLogApi) { 00552 KdPrint(("NtDeleteValueKey %d %wZ\n",KeyHandle,ValueName)); 00553 } 00554 #endif 00555 mode = KeGetPreviousMode(); 00556 00557 status = ObReferenceObjectByHandle( 00558 KeyHandle, 00559 KEY_SET_VALUE, 00560 CmpKeyObjectType, 00561 mode, 00562 (PVOID *)(&KeyBody), 00563 NULL 00564 ); 00565 00566 if (NT_SUCCESS(status)) { 00567 00568 00569 try { 00570 if (mode == UserMode) { 00571 LocalValueName = ProbeAndReadUnicodeString(ValueName); 00572 ProbeForRead( 00573 LocalValueName.Buffer, 00574 LocalValueName.Length, 00575 sizeof(WCHAR) 00576 ); 00577 } else { 00578 LocalValueName = *ValueName; 00579 } 00580 00581 status = CmDeleteValueKey( 00582 KeyBody->KeyControlBlock, 00583 LocalValueName 00584 ); 00585 00586 } except (EXCEPTION_EXECUTE_HANDLER) { 00587 CMLOG(CML_API, CMS_EXCEPTION) { 00588 KdPrint(("!!NtDeleteValueKey: code:%08lx\n", GetExceptionCode())); 00589 } 00590 status = GetExceptionCode(); 00591 } 00592 00593 00594 ObDereferenceObject((PVOID)KeyBody); 00595 } else { 00596 LocalValueName.Buffer = NULL; 00597 LocalValueName.Length = 0; 00598 } 00599 00600 END_LOCK_CHECKPOINT; 00601 00602 // End registry call tracing 00603 EndWmiCmTrace(status,KeyHandle,&LocalValueName,EVENT_TRACE_TYPE_REGDELETEVALUE); 00604 00605 return status; 00606 } 00607 00608 00609 NTSTATUS 00610 NtEnumerateKey( 00611 IN HANDLE KeyHandle, 00612 IN ULONG Index, 00613 IN KEY_INFORMATION_CLASS KeyInformationClass, 00614 IN PVOID KeyInformation, 00615 IN ULONG Length, 00616 IN PULONG ResultLength 00617 ) 00618 /*++ 00619 00620 Routine Description: 00621 00622 The sub keys of an open key may be enumerated with NtEnumerateKey. 00623 00624 NtEnumerateKey returns the name of the Index'th sub key of the open 00625 key specified by KeyHandle. The value STATUS_NO_MORE_ENTRIES will be 00626 returned if value of Index is larger than the number of sub keys. 00627 00628 Note that Index is simply a way to select among child keys. Two calls 00629 to NtEnumerateKey with the same Index are NOT guaranteed to return 00630 the same results. 00631 00632 If KeyInformation is not long enough to hold all requested data, 00633 STATUS_BUFFER_OVERFLOW will be returned, and ResultLength will be 00634 set to the number of bytes actually required. 00635 00636 Arguments: 00637 00638 KeyHandle - Handle of the key whose sub keys are to be enumerated. Must 00639 be open for KEY_ENUMERATE_SUB_KEY access. 00640 00641 Index - Specifies the (0-based) number of the sub key to be returned. 00642 00643 KeyInformationClass - Specifies the type of information returned in 00644 Buffer. One of the following types: 00645 00646 KeyBasicInformation - return last write time, title index, and name. 00647 (see KEY_BASIC_INFORMATION structure) 00648 00649 KeyNodeInformation - return last write time, title index, name, class. 00650 (see KEY_NODE_INFORMATION structure) 00651 00652 KeyInformation -Supplies pointer to buffer to receive the data. 00653 00654 Length - Length of KeyInformation in bytes. 00655 00656 ResultLength - Number of bytes actually written into KeyInformation. 00657 00658 Return Value: 00659 00660 NTSTATUS - Result code from call, among the following: 00661 00662 <TBS> 00663 00664 --*/ 00665 { 00666 NTSTATUS status; 00667 PCM_KEY_BODY KeyBody; 00668 KPROCESSOR_MODE mode; 00669 00670 // Start registry call tracing 00671 StartWmiCmTrace(); 00672 00673 PAGED_CODE(); 00674 00675 BEGIN_LOCK_CHECKPOINT; 00676 00677 CMLOG(CML_API, CMS_NTAPI) KdPrint(("NtEnumerateKey\n")); 00678 CMLOG(CML_API_ARGS, CMS_NTAPI) { 00679 KdPrint(("\tKeyHandle=%08lx Index=%08lx\n", KeyHandle, Index)); 00680 } 00681 00682 #ifdef LOG_NT_API 00683 if (CmpLogApi) { 00684 KdPrint(("NtEnumerateKey %d %d %d\n",KeyHandle,Index,KeyInformationClass)); 00685 } 00686 #endif 00687 00688 if ((KeyInformationClass != KeyBasicInformation) && 00689 (KeyInformationClass != KeyNodeInformation) && 00690 (KeyInformationClass != KeyFullInformation)) 00691 { 00692 // End registry call tracing 00693 EndWmiCmTrace(STATUS_INVALID_PARAMETER,KeyHandle,NULL,EVENT_TRACE_TYPE_REGENUMERATEKEY); 00694 00695 return STATUS_INVALID_PARAMETER; 00696 } 00697 00698 mode = KeGetPreviousMode(); 00699 00700 status = ObReferenceObjectByHandle( 00701 KeyHandle, 00702 KEY_ENUMERATE_SUB_KEYS, 00703 CmpKeyObjectType, 00704 mode, 00705 (PVOID *)(&KeyBody), 00706 NULL 00707 ); 00708 00709 if (NT_SUCCESS(status)) { 00710 00711 try { 00712 if (mode == UserMode) { 00713 ProbeForWrite( 00714 KeyInformation, 00715 Length, 00716 sizeof(ULONG) 00717 ); 00718 ProbeForWriteUlong(ResultLength); 00719 } 00720 00721 } except (EXCEPTION_EXECUTE_HANDLER) { 00722 CMLOG(CML_API, CMS_EXCEPTION) { 00723 KdPrint(("!!NtEnumerateKey: code:%08lx\n", GetExceptionCode())); 00724 } 00725 status = GetExceptionCode(); 00726 } 00727 00728 if( NT_SUCCESS(status)) { 00729 // 00730 // CmEnumerateKey is protected to user mode buffer exceptions 00731 // all other exceptions are cm internals and should result in a bugcheck 00732 // 00733 status = CmEnumerateKey( 00734 KeyBody->KeyControlBlock, 00735 Index, 00736 KeyInformationClass, 00737 KeyInformation, 00738 Length, 00739 ResultLength 00740 ); 00741 } 00742 00743 ObDereferenceObject((PVOID)KeyBody); 00744 } 00745 00746 END_LOCK_CHECKPOINT; 00747 00748 // End registry call tracing 00749 EndWmiCmTrace(status,KeyHandle,NULL,EVENT_TRACE_TYPE_REGENUMERATEKEY); 00750 00751 return status; 00752 } 00753 00754 00755 NTSTATUS 00756 NtEnumerateValueKey( 00757 IN HANDLE KeyHandle, 00758 IN ULONG Index, 00759 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, 00760 IN PVOID KeyValueInformation, 00761 IN ULONG Length, 00762 IN PULONG ResultLength 00763 ) 00764 /*++ 00765 00766 Routine Description: 00767 00768 The value entries of an open key may be enumerated 00769 with NtEnumerateValueKey. 00770 00771 NtEnumerateValueKey returns the name of the Index'th value 00772 entry of the open key specified by KeyHandle. The value 00773 STATUS_NO_MORE_ENTRIES will be returned if value of Index is 00774 larger than the number of sub keys. 00775 00776 Note that Index is simply a way to select among value 00777 entries. Two calls to NtEnumerateValueKey with the same Index 00778 are NOT guaranteed to return the same results. 00779 00780 If KeyValueInformation is not long enough to hold all requested data, 00781 STATUS_BUFFER_OVERFLOW will be returned, and ResultLength will be 00782 set to the number of bytes actually required. 00783 00784 Arguments: 00785 00786 KeyHandle - Handle of the key whose value entries are to be enumerated. 00787 Must have been opened with KEY_QUERY_VALUE access. 00788 00789 Index - Specifies the (0-based) number of the sub key to be returned. 00790 00791 KeyValueInformationClass - Specifies the type of information returned 00792 in Buffer. One of the following types: 00793 00794 KeyValueBasicInformation - return time of last write, 00795 title index, and name. (See KEY_VALUE_BASIC_INFORMATION) 00796 00797 KeyValueFullInformation - return time of last write, 00798 title index, name, class. (See KEY_VALUE_FULL_INFORMATION) 00799 00800 KeyValueInformation -Supplies pointer to buffer to receive the data. 00801 00802 Length - Length of KeyValueInformation in bytes. 00803 00804 ResultLength - Number of bytes actually written into KeyValueInformation. 00805 00806 Return Value: 00807 00808 NTSTATUS - Result code from call, among the following: 00809 00810 <TBS> 00811 00812 --*/ 00813 { 00814 NTSTATUS status; 00815 PCM_KEY_BODY KeyBody; 00816 KPROCESSOR_MODE mode; 00817 00818 // Start registry call tracing 00819 StartWmiCmTrace(); 00820 00821 PAGED_CODE(); 00822 00823 BEGIN_LOCK_CHECKPOINT; 00824 00825 CMLOG(CML_API, CMS_NTAPI) KdPrint(("NtEnumerateValueKey\n")); 00826 CMLOG(CML_API_ARGS, CMS_NTAPI) { 00827 KdPrint(("\tKeyHandle=%08lx Index=%08lx\n", KeyHandle, Index)); 00828 } 00829 00830 #ifdef LOG_NT_API 00831 if (CmpLogApi) { 00832 KdPrint(("NtEnumerateValueKey %d %d %d\n",KeyHandle,Index,KeyValueInformationClass)); 00833 } 00834 #endif 00835 00836 if ((KeyValueInformationClass != KeyValueBasicInformation) && 00837 (KeyValueInformationClass != KeyValueFullInformation) && 00838 (KeyValueInformationClass != KeyValuePartialInformation)) 00839 { 00840 // End registry call tracing 00841 EndWmiCmTrace(STATUS_INVALID_PARAMETER,KeyHandle,NULL,EVENT_TRACE_TYPE_REGENUMERATEVALUEKEY); 00842 00843 return STATUS_INVALID_PARAMETER; 00844 } 00845 00846 mode = KeGetPreviousMode(); 00847 00848 status = ObReferenceObjectByHandle( 00849 KeyHandle, 00850 KEY_QUERY_VALUE, 00851 CmpKeyObjectType, 00852 mode, 00853 (PVOID *)(&KeyBody), 00854 NULL 00855 ); 00856 00857 if (NT_SUCCESS(status)) { 00858 00859 try { 00860 if (mode == UserMode) { 00861 ProbeForWrite( 00862 KeyValueInformation, 00863 Length, 00864 sizeof(ULONG) 00865 ); 00866 ProbeForWriteUlong(ResultLength); 00867 } 00868 00869 } except (EXCEPTION_EXECUTE_HANDLER) { 00870 CMLOG(CML_API, CMS_EXCEPTION) { 00871 KdPrint(("!!NtEnumerateValueKey: code:%08lx\n", GetExceptionCode())); 00872 } 00873 status = GetExceptionCode(); 00874 } 00875 00876 if( NT_SUCCESS(status)) { 00877 // 00878 // CmEnumerateValueKey is protected to user mode buffer exceptions 00879 // all other exceptions are cm internals and should result in a bugcheck 00880 // 00881 status = CmEnumerateValueKey( 00882 KeyBody->KeyControlBlock, 00883 Index, 00884 KeyValueInformationClass, 00885 KeyValueInformation, 00886 Length, 00887 ResultLength 00888 ); 00889 } 00890 00891 ObDereferenceObject((PVOID)KeyBody); 00892 } 00893 00894 END_LOCK_CHECKPOINT; 00895 00896 // End registry call tracing 00897 EndWmiCmTrace(status,KeyHandle,NULL,EVENT_TRACE_TYPE_REGENUMERATEVALUEKEY); 00898 00899 return status; 00900 } 00901 00902 00903 NTSTATUS 00904 NtFlushKey( 00905 IN HANDLE KeyHandle 00906 ) 00907 /*++ 00908 00909 Routine Description: 00910 00911 Changes made by NtCreateKey or NtSetKey may be flushed to disk with 00912 NtFlushKey. 00913 00914 NtFlushKey will not return to its caller until any changed data 00915 associated with KeyHandle has been written to permanent store. 00916 00917 WARNING: NtFlushKey will flush the entire registry tree, and thus will 00918 burn cycles and I/O. 00919 00920 Arguments: 00921 00922 KeyHandle - Handle of open key to be flushed. 00923 00924 Return Value: 00925 00926 NTSTATUS - Result code from call, among the following: 00927 00928 <TBS> 00929 00930 --*/ 00931 { 00932 PCM_KEY_BODY KeyBody; 00933 NTSTATUS status; 00934 REGISTRY_COMMAND Command; 00935 00936 // Start registry call tracing 00937 StartWmiCmTrace(); 00938 00939 PAGED_CODE(); 00940 00941 BEGIN_LOCK_CHECKPOINT; 00942 00943 CMLOG(CML_API, CMS_NTAPI) KdPrint(("NtFlushKey\n")); 00944 00945 #ifdef LOG_NT_API 00946 if (CmpLogApi) { 00947 KdPrint(("NtFlushKey(%d)\n",KeyHandle)); 00948 } 00949 #endif 00950 00951 status = ObReferenceObjectByHandle( 00952 KeyHandle, 00953 0, 00954 CmpKeyObjectType, 00955 KeGetPreviousMode(), 00956 (PVOID *)(&KeyBody), 00957 NULL 00958 ); 00959 00960 if (NT_SUCCESS(status)) { 00961 00962 // 00963 // Need the exclusive lock so we can call the registry worker thread. 00964 // 00965 CmpLockRegistry(); 00966 00967 if (KeyBody->KeyControlBlock->Delete) { 00968 status = STATUS_KEY_DELETED; 00969 } else { 00970 // 00971 // Wake up worker thread to do real work for us 00972 // 00973 00974 Command.Command = REG_CMD_FLUSH_KEY; 00975 Command.Hive = KeyBody->KeyControlBlock->KeyHive; 00976 Command.Cell = KeyBody->KeyControlBlock->KeyCell; 00977 00978 CmpWorker(&Command); 00979 status = Command.Status; 00980 } 00981 00982 ObDereferenceObject((PVOID)KeyBody); 00983 00984 CmpUnlockRegistry(); 00985 } 00986 00987 END_LOCK_CHECKPOINT; 00988 00989 // End registry call tracing 00990 EndWmiCmTrace(status,KeyHandle,NULL,EVENT_TRACE_TYPE_REGFLUSH); 00991 00992 return status; 00993 } 00994 00995 00996 NTSTATUS 00997 NtInitializeRegistry( 00998 IN USHORT BootCondition 00999 ) 01000 /*++ 01001 01002 Routine Description: 01003 01004 This routine is called in 2 situations: 01005 01006 1) It is called from SM after autocheck (chkdsk) has 01007 run and the paging files have been opened. It's function is 01008 to bind in memory hives to their files, and to open any other 01009 files yet to be used. 01010 01011 2) It is called from SC after the current boot has been accepted 01012 and the control set used for the boot process should be saved 01013 as the LKG control set. 01014 01015 After this routine accomplishes the work of situation #1 and 01016 #2, further requests for such work will not be carried out. 01017 01018 Arguments: 01019 01020 BootCondition - 01021 01022 REG_INIT_BOOT_SM - The routine has been called from SM 01023 in situation #1. 01024 01025 REG_INIT_BOOT_SETUP - The routine has been called to perform 01026 situation #1 work but has been called 01027 from setup and needs to do some special 01028 work. 01029 01030 REG_INIT_BOOT_ACCEPTED_BASE + Num 01031 (where 0 < Num < 1000) - The routine has been called 01032 in situation #2. "Num" is the 01033 number of the control set 01034 to which the boot control set 01035 should be saved. 01036 01037 Return Value: 01038 01039 NTSTATUS - Result code from call, among the following: 01040 01041 STATUS_SUCCESS - it worked 01042 STATUS_ACCESS_DENIED - the routine has already done the work 01043 requested and will not do it again. 01044 01045 --*/ 01046 { 01047 REGISTRY_COMMAND Command; 01048 BOOLEAN SetupBoot; 01049 01050 PAGED_CODE(); 01051 #ifdef LOG_NT_API 01052 if (CmpLogApi) { 01053 KdPrint(("NtInitializeRegistry()\n")); 01054 } 01055 #endif 01056 01057 // 01058 // Force previous mode to be KernelMode 01059 // 01060 if (KeGetPreviousMode() == UserMode) { 01061 return ZwInitializeRegistry(BootCondition); 01062 } else { 01063 01064 // 01065 // Check for a valid BootCondition value 01066 // 01067 01068 if(BootCondition > REG_INIT_MAX_VALID_CONDITION) 01069 return STATUS_INVALID_PARAMETER; 01070 01071 // 01072 // Check for a Boot acceptance 01073 // 01074 01075 if((BootCondition >= REG_INIT_BOOT_ACCEPTED_BASE) && 01076 (BootCondition <= REG_INIT_BOOT_ACCEPTED_MAX)) 01077 { 01078 // 01079 // Make sure the Boot can be accepted only once 01080 // 01081 01082 if(!CmBootAcceptFirstTime) 01083 return STATUS_ACCESS_DENIED; 01084 01085 CmBootAcceptFirstTime = FALSE; 01086 01087 // 01088 // Calculate the control set we want to save 01089 // the boot control set to 01090 // 01091 01092 BootCondition -= REG_INIT_BOOT_ACCEPTED_BASE; 01093 01094 if(BootCondition) 01095 { 01096 // 01097 // Valid control set number passed in. 01098 // Do the save. 01099 // 01100 01101 return CmpSaveBootControlSet(BootCondition); 01102 } 01103 else 01104 { 01105 // 01106 // 0 passed in as a control set number. 01107 // That is not valid, fail. 01108 // 01109 01110 return STATUS_INVALID_PARAMETER; 01111 } 01112 } 01113 01114 // called from setup? 01115 01116 SetupBoot = (BootCondition == REG_INIT_BOOT_SETUP ? TRUE : FALSE); 01117 01118 // 01119 // Fail if not first time called for situation #1 work. 01120 // 01121 01122 if (CmFirstTime != TRUE) { 01123 return STATUS_ACCESS_DENIED; 01124 } 01125 CmFirstTime = FALSE; 01126 01127 // 01128 // Wake up worker thread to do real work for us 01129 // 01130 01131 Command.Command = REG_CMD_INIT; 01132 Command.SetupBoot = SetupBoot; 01133 01134 CmpLockRegistryExclusive(); 01135 01136 CmpWorker(&Command); 01137 CmpSetVersionData(); 01138 01139 CmpUnlockRegistry(); 01140 01141 // 01142 // Notify PO that the volumes are usabled 01143 // 01144 PoInitHiberServices(SetupBoot); 01145 01146 if (!SetupBoot) { 01147 IopCopyBootLogRegistryToFile(); 01148 } 01149 01150 return STATUS_SUCCESS; 01151 } 01152 } 01153 01154 01155 NTSTATUS 01156 NtNotifyChangeKey( 01157 IN HANDLE KeyHandle, 01158 IN HANDLE Event OPTIONAL, 01159 IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, 01160 IN PVOID ApcContext OPTIONAL, 01161 OUT PIO_STATUS_BLOCK IoStatusBlock, 01162 IN ULONG CompletionFilter, 01163 IN BOOLEAN WatchTree, 01164 OUT PVOID Buffer, 01165 IN ULONG BufferSize, 01166 IN BOOLEAN Asynchronous 01167 ) 01168 /*++ 01169 01170 Routine Description: 01171 01172 Notification of key creation, deletion, and modification may be 01173 obtained by calling NtNotifyChangeKey. 01174 01175 NtNotifyChangeKey monitors changes to a key - if the key or 01176 subtree specified by KeyHandle are modified, the service notifies 01177 its caller. It also returns the name(s) of the key(s) that changed. 01178 All names are specified relative to the key that the handle represents 01179 (therefore a NULL name represents that key). The service completes 01180 once the key or subtree has been modified based on the supplied 01181 CompletionFilter. The service is a "single shot" and therefore 01182 needs to be reinvoked to watch the key for further changes. 01183 01184 The operation of this service begins by opening a key for KEY_NOTIFY 01185 access. Once the handle is returned, the NtNotifyChangeKey service 01186 may be invoked to begin watching the values and subkeys of the 01187 specified key for changes. The first time the service is invoked, 01188 the BufferSize parameter supplies not only the size of the user's 01189 Buffer, but also the size of the buffer that will be used by the 01190 Registry to store names of keys that have changed. Likewise, the 01191 CompletionFilter and WatchTree parameters on the first call indicate 01192 how notification should operate for all calls using the supplied 01193 KeyHandle. These two parameters are ignored on subsequent calls 01194 to the API with the same instance of KeyHandle. 01195 01196 Once a modification is made that should be reported, the Registry will 01197 complete the service. The names of the files that have changed since 01198 the last time the service was called will be placed into the caller's 01199 output Buffer. The Information field of IoStatusBlock will contain 01200 the number of bytes placed in Buffer, or zero if too many keys have 01201 changed since the last time the service was called, in which case 01202 the application must Query and Enumerate the key and sub keys to 01203 discover changes. The Status field of IoStatusBlock will contain 01204 the actual status of the call. 01205 01206 If Asynchronous is TRUE, then Event, if specified, will be set to 01207 the Signaled state. If no Event parameter was specified, then 01208 KeyHandle will be set to the Signaled state. If an ApcRoutine 01209 was specified, it is invoked with the ApcContext and the address of the 01210 IoStatusBlock as its arguments. If Asynchronous is FALSE, Event, 01211 ApcRoutine, and ApcContext are ignored. 01212 01213 This service requires KEY_NOTIFY access to the key that was 01214 actually modified 01215 01216 The notify "session" is terminated by closing KeyHandle. 01217 01218 Arguments: 01219 01220 KeyHandle-- Supplies a handle to an open key. This handle is 01221 effectively the notify handle, because only one set of 01222 notify parameters may be set against it. 01223 01224 Event - An optional handle to an event to be set to the 01225 Signaled state when the operation completes. 01226 01227 ApcRoutine - An optional procedure to be invoked once the 01228 operation completes. For more information about this 01229 parameter see the NtReadFile system service description. 01230 01231 If PreviousMode == Kernel, this parameter is an optional 01232 pointer to a WORK_QUEUE_ITEM to be queued when the notify 01233 is signaled. 01234 01235 ApcContext - A pointer to pass as an argument to the ApcRoutine, 01236 if one was specified, when the operation completes. This 01237 argument is required if an ApcRoutine was specified. 01238 01239 If PreviousMode == Kernel, this parameter is an optional 01240 WORK_QUEUE_TYPE describing the queue to be used. This argument 01241 is required if an ApcRoutine was specified. 01242 01243 IoStatusBlock - A variable to receive the final completion status. 01244 For more information about this parameter see the NtCreateFile 01245 system service description. 01246 01247 CompletionFilter -- Specifies a set of flags that indicate the 01248 types of operations on the key or its value that cause the 01249 call to complete. The following are valid flags for this parameter: 01250 01251 REG_NOTIFY_CHANGE_NAME -- Specifies that the call should be 01252 completed if a subkey is added or deleted. 01253 01254 REG_NOTIFY_CHANGE_ATTRIBUTES -- Specifies that the call should 01255 be completed if the attributes (e.g.: ACL) of the key or 01256 any subkey are changed. 01257 01258 REG_NOTIFY_CHANGE_LAST_SET -- Specifies that the call should be 01259 completed if the lastWriteTime of the key or any of its 01260 subkeys is changed. (Ie. if the value of the key or any 01261 subkey is changed). 01262 01263 REG_NOTIFY_CHANGE_SECURITY -- Specifies that the call should be 01264 completed if the security information (e.g. ACL) on the key 01265 or any subkey is changed. 01266 01267 WatchTree -- A BOOLEAN value that, if TRUE, specifies that all 01268 changes in the subtree of this key should also be reported. 01269 If FALSE, only changes to this key, its value, and its immediate 01270 subkeys (but not their values nor their subkeys) are reported. 01271 01272 Buffer -- A variable to receive the name(s) of the key(s) that 01273 changed. See REG_NOTIFY_INFORMATION. 01274 01275 BufferSize -- Specifies the length of Buffer. 01276 01277 Asynchronous -- If FALSE, call will not return until 01278 complete (synchronous) if TRUE, call may return STATUS_PENDING. 01279 01280 Obs: 01281 Since NtNotifyChangeMultipleKeys, this routine is kept only for bacwards compatibility 01282 01283 Return Value: 01284 01285 NTSTATUS - Result code from call, among the following: 01286 01287 <TBS> 01288 01289 --*/ 01290 { 01291 PAGED_CODE(); 01292 01293 CMLOG(CML_API, CMS_NTAPI) KdPrint(("NtNotifyChangeKey\n")); 01294 #ifdef LOG_NT_API 01295 if (CmpLogApi) { 01296 KdPrint(("NtNotifyChangeKey(%d)\n",KeyHandle)); 01297 } 01298 #endif 01299 01300 // Just call the wiser routine 01301 return NtNotifyChangeMultipleKeys( 01302 KeyHandle, 01303 0, 01304 NULL, 01305 Event, 01306 ApcRoutine, 01307 ApcContext, 01308 IoStatusBlock, 01309 CompletionFilter, 01310 WatchTree, 01311 Buffer, 01312 BufferSize, 01313 Asynchronous 01314 ); 01315 01316 } 01317 01318 NTSTATUS 01319 NtNotifyChangeMultipleKeys( 01320 IN HANDLE MasterKeyHandle, 01321 IN ULONG Count, 01322 IN OBJECT_ATTRIBUTES SlaveObjects[], 01323 IN HANDLE Event OPTIONAL, 01324 IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, 01325 IN PVOID ApcContext OPTIONAL, 01326 OUT PIO_STATUS_BLOCK IoStatusBlock, 01327 IN ULONG CompletionFilter, 01328 IN BOOLEAN WatchTree, 01329 OUT PVOID Buffer, 01330 IN ULONG BufferSize, 01331 IN BOOLEAN Asynchronous 01332 ) 01333 /*++ 01334 01335 Routine Description: 01336 01337 Notificaion of creation, deletion and modification on multiple keys 01338 may be obtained with NtNotifyChangeMultipleKeys. 01339 01340 NtNotifyMultipleKeys monitors changes to any of the MasterKeyHandle 01341 or one of SlaveObjects and/or their subtrees, whichever occurs first. 01342 When an event on these keys is triggered, the notification is considered 01343 fulfilled, and has to be "armed" again, in order to watch for further 01344 changes. 01345 01346 The mechanism is similar to the one described in NtNotifyChangeKey. 01347 01348 The MasterKeyHandle key, give the caller control over the lifetime 01349 of the notification. The notification will live as long as the caller 01350 keeps the MasterKeyHandle open, or an event is triggered. 01351 01352 The caller doesn't have to open the SlaveKeys. He will provide the 01353 routine with an array of OBJECT_ATTRIBUTES, describing the slave objects. 01354 The routine will open the objects, and ensure keep a reference on them 01355 untill the back-end side will close them. 01356 01357 The notify "session" is terminated by closing MasterKeyHandle. 01358 01359 Obs: 01360 For the time being, the routine supports only one slave object. When more 01361 than one slave object is provided, the routine will signal an error of 01362 STATUS_INVALID_PARAMETER. 01363 However, the interface is designed for future enhancements (taking an 01364 array of slave objects), that may be provided with future versions(w2001). 01365 01366 When no slave object is supplied (i.e. Count == 0) we have the identical 01367 behavior as for NtNotifyChangeKey. 01368 01369 Arguments: 01370 01371 MasterKeyHandle - Supplies a handle to an open key. This handle is 01372 the "master handle". It has control overthe lifetime of the 01373 notification. 01374 01375 Count - Number of slave objects. For the time being, this should be 1 01376 01377 SlaveObjects - Array of slave objects. Only the attributes of the 01378 objects are provided, so the caller doesn't have to take care 01379 of them. 01380 01381 Event,ApcRoutine,ApcContext,IoStatusBlock,CompletionFilter,WatchTree, 01382 Buffer,BufferSize,Asynchronous - same as for NtNotifyChangeKey 01383 01384 Return Value: 01385 01386 NTSTATUS - Result code from call, among the following: 01387 01388 <TBS> 01389 01390 --*/ 01391 { 01392 NTSTATUS status; 01393 NTSTATUS WaitStatus; 01394 KPROCESSOR_MODE PreviousMode; 01395 PCM_KEY_BODY MasterKeyBody; 01396 PCM_KEY_BODY SlaveKeyBody; 01397 PKEVENT UserEvent=NULL; 01398 PCM_POST_BLOCK MasterPostBlock; 01399 PCM_POST_BLOCK SlavePostBlock; 01400 KIRQL OldIrql; 01401 HANDLE SlaveKeyHandle; 01402 POST_BLOCK_TYPE PostType = PostSynchronous; 01403 BOOLEAN SlavePresent = FALSE; // assume that we are in the NtNotifyChangeKey case 01404 #if defined(_WIN64) 01405 BOOLEAN UseIosb32=FALSE; // If the caller is a 32bit process on sundown and previous mode 01406 // is user mode, use a 32bit IoSb. 01407 #endif 01408 01409 PAGED_CODE(); 01410 01411 BEGIN_LOCK_CHECKPOINT; 01412 01413 CMLOG(CML_API, CMS_NTAPI) KdPrint(("NtNotifyChangeMultipleKeys\n")); 01414 01415 if(Count > 1) { 01416 // 01417 // This version supports only one slave object 01418 // 01419 return STATUS_INVALID_PARAMETER; 01420 } 01421 01422 if(Count == 1) { 01423 // 01424 // We have one slave, so we are in the NtNotifyChangeMultipleKeys case 01425 // 01426 SlavePresent = TRUE; 01427 } 01428 01429 #ifdef LOG_NT_API 01430 if (CmpLogApi && SlavePresent) { 01431 KdPrint(("NtNotifyChangeMultipleKeys(%d,%wZ relative to %d, Asynchronous = %d)\n",MasterKeyHandle,SlaveObjects->ObjectName,SlaveObjects->RootDirectory,(int)Asynchronous)); 01432 } 01433 #endif 01434 01435 // 01436 // Threads that are attached give us real grief, so disallow it. 01437 // 01438 if (KeIsAttachedProcess()) { 01439 KeBugCheckEx(REGISTRY_ERROR,8,1,0,0); 01440 } 01441 01442 // 01443 // Probe user buffer parameters. 01444 // 01445 PreviousMode = KeGetPreviousMode(); 01446 if (PreviousMode != KernelMode) { 01447 01448 #if defined(_WIN64) 01449 // Process is 32bit if Wow64 is not NULL. 01450 UseIosb32 = (PsGetCurrentProcess()->Wow64Process != NULL); 01451 #endif 01452 01453 try { 01454 01455 ProbeForWrite( 01456 IoStatusBlock, 01457 #if defined(_WIN64) 01458 UseIosb32 ? sizeof(IO_STATUS_BLOCK32) : sizeof(IO_STATUS_BLOCK), 01459 #else 01460 sizeof(IO_STATUS_BLOCK), 01461 #endif 01462 sizeof(ULONG) 01463 ); 01464 01465 01466 ProbeForWrite(Buffer, BufferSize, sizeof(ULONG)); 01467 01468 // 01469 // Initialize IOSB 01470 // 01471 01472 CmpSetIoStatus(IoStatusBlock, STATUS_PENDING, 0, UseIosb32); 01473 } except(EXCEPTION_EXECUTE_HANDLER) { 01474 CMLOG(CML_API, CMS_EXCEPTION) { 01475 KdPrint(("!!NtChangeNotifyMultipleKeys: code:%08lx\n", GetExceptionCode())); 01476 } 01477 return GetExceptionCode(); 01478 } 01479 if (Asynchronous) { 01480 PostType = PostAsyncUser; 01481 } 01482 } else { 01483 if (Asynchronous) { 01484 PostType = PostAsyncKernel; 01485 if( Count > 0 ) { 01486 // 01487 // we don't allow multiple asyncronous kernel notifications 01488 // 01489 return STATUS_INVALID_PARAMETER; 01490 } 01491 } 01492 } 01493 01494 // 01495 // Check filter 01496 // 01497 if (CompletionFilter != (CompletionFilter & REG_LEGAL_CHANGE_FILTER)) { 01498 return STATUS_INVALID_PARAMETER; 01499 } 01500 01501 // 01502 // Reference the Master Key handle 01503 // 01504 status = ObReferenceObjectByHandle( 01505 MasterKeyHandle, 01506 KEY_NOTIFY, 01507 CmpKeyObjectType, 01508 PreviousMode, 01509 (PVOID *)(&MasterKeyBody), 01510 NULL 01511 ); 01512 if (!NT_SUCCESS(status)) { 01513 return status; 01514 } 01515 01516 if(SlavePresent) { 01517 CmpLockRegistry(); 01518 // 01519 // Open the slave object and add a reference to it. 01520 // 01521 try { 01522 01523 status = ObOpenObjectByName(SlaveObjects, 01524 CmpKeyObjectType, 01525 PreviousMode, 01526 NULL, 01527 KEY_NOTIFY, 01528 NULL, 01529 &SlaveKeyHandle); 01530 if (NT_SUCCESS(status)) { 01531 status = ObReferenceObjectByHandle(SlaveKeyHandle, 01532 KEY_NOTIFY, 01533 CmpKeyObjectType, 01534 PreviousMode, 01535 (PVOID *)&SlaveKeyBody, 01536 NULL); 01537 NtClose(SlaveKeyHandle); 01538 01539 } 01540 01541 } except (CmpExceptionFilter(GetExceptionInformation())) { 01542 CMLOG(CML_API, CMS_EXCEPTION) { 01543 KdPrint(("!!NtNotifyChangeMultipleKeys: code:%08lx\n", GetExceptionCode())); 01544 } 01545 status = GetExceptionCode(); 01546 } 01547 01548 CmpUnlockRegistry(); 01549 01550 if (!NT_SUCCESS(status)) { 01551 ObDereferenceObject(MasterKeyBody); 01552 return status; 01553 } 01554 01555 // 01556 // Reject calls setting with keys on the same hive as they could lead to obscure deadlocks 01557 // 01558 if( MasterKeyBody->KeyControlBlock->KeyHive == SlaveKeyBody->KeyControlBlock->KeyHive ) { 01559 ObDereferenceObject(SlaveKeyBody); 01560 ObDereferenceObject(MasterKeyBody); 01561 return STATUS_INVALID_PARAMETER; 01562 } 01563 } 01564 01565 01566 // 01567 // Allocate master and slave post blocks, and init it. Do NOT put it on the chain, 01568 // CmNotifyChangeKey will do that while holding a mutex. 01569 // 01570 // WARNING: PostBlocks MUST BE ALLOCATED from Pool, since back side 01571 // of Notify will free it! 01572 // 01573 // 01574 // Exclusively lock the registry; We want nobody to mess with it while we are doing the 01575 // post/notify list manipulation; what else could be safer than that :-) 01576 // 01577 CmpLockRegistryExclusive(); 01578 MasterPostBlock = CmpAllocateMasterPostBlock(PostType); 01579 if (MasterPostBlock == NULL) { 01580 if(SlavePresent) { 01581 ObDereferenceObject(SlaveKeyBody); 01582 } 01583 ObDereferenceObject(MasterKeyBody); 01584 CmpUnlockRegistry(); 01585 return STATUS_INSUFFICIENT_RESOURCES; 01586 } 01587 01588 #if DBG 01589 MasterPostBlock->TraceIntoDebugger = TRUE; 01590 #endif 01591 01592 if(SlavePresent) { 01593 SlavePostBlock = CmpAllocateSlavePostBlock(PostType,SlaveKeyBody,MasterPostBlock); 01594 if (SlavePostBlock == NULL) { 01595 ObDereferenceObject(SlaveKeyBody); 01596 ObDereferenceObject(MasterKeyBody); 01597 CmpFreePostBlock(MasterPostBlock); 01598 CmpUnlockRegistry(); 01599 return STATUS_INSUFFICIENT_RESOURCES; 01600 } 01601 01602 #if DBG 01603 SlavePostBlock->TraceIntoDebugger = TRUE; 01604 #endif 01605 } 01606 01607 if ((PostType == PostAsyncUser) || 01608 (PostType == PostAsyncKernel)) { 01609 01610 // 01611 // If event is present, reference it, save its address, and set 01612 // it to the not signaled state. 01613 // 01614 if (ARGUMENT_PRESENT(Event)) { 01615 status = ObReferenceObjectByHandle( 01616 Event, 01617 EVENT_MODIFY_STATE, 01618 ExEventObjectType, 01619 PreviousMode, 01620 (PVOID *)(&UserEvent), 01621 NULL 01622 ); 01623 if (!NT_SUCCESS(status)) { 01624 if(SlavePresent) { 01625 CmpFreePostBlock(SlavePostBlock); 01626 // SlaveKeyBody is dereferenced in CmpFreePostBlock(SlavePostBlock) 01627 } 01628 CmpFreePostBlock(MasterPostBlock); 01629 ObDereferenceObject(MasterKeyBody); 01630 CmpUnlockRegistry(); 01631 return status; 01632 } else { 01633 KeClearEvent(UserEvent); 01634 } 01635 } 01636 01637 if (PostType == PostAsyncUser) { 01638 KPROCESSOR_MODE ApcMode; 01639 01640 MasterPostBlock->u->AsyncUser.IoStatusBlock = IoStatusBlock; 01641 MasterPostBlock->u->AsyncUser.UserEvent = UserEvent; 01642 // 01643 // Initialize APC. May or may not be a user apc, will always 01644 // be a kernel apc. 01645 // 01646 ApcMode = PreviousMode; 01647 if( ApcRoutine == NULL ) { 01648 ApcRoutine = (PIO_APC_ROUTINE)CmpDummyApc; 01649 ApcMode = KernelMode; 01650 } 01651 KeInitializeApc(MasterPostBlock->u->AsyncUser.Apc, 01652 KeGetCurrentThread(), 01653 CurrentApcEnvironment, 01654 (PKKERNEL_ROUTINE)CmpPostApc, 01655 (PKRUNDOWN_ROUTINE)CmpPostApcRunDown, 01656 (PKNORMAL_ROUTINE)ApcRoutine, 01657 ApcMode, 01658 ApcContext); 01659 01660 } else { 01661 MasterPostBlock->u->AsyncKernel.Event = UserEvent; 01662 MasterPostBlock->u->AsyncKernel.WorkItem = (PWORK_QUEUE_ITEM)ApcRoutine; 01663 MasterPostBlock->u->AsyncKernel.QueueType = (WORK_QUEUE_TYPE)((ULONG_PTR)ApcContext); 01664 } 01665 } 01666 01667 01668 // 01669 // Call worker for master 01670 // 01671 status = CmpNotifyChangeKey( 01672 MasterKeyBody, 01673 MasterPostBlock, 01674 CompletionFilter, 01675 WatchTree, 01676 Buffer, 01677 BufferSize, 01678 MasterPostBlock 01679 ); 01680 if( !NT_SUCCESS(status)) { 01681 // 01682 // it didn't work, clean up for error path 01683 // 01684 if (UserEvent != NULL) { 01685 ObDereferenceObject(UserEvent); 01686 } 01687 01688 if(SlavePresent) { 01689 CmpFreePostBlock(SlavePostBlock); 01690 // SlaveKeyBody is dereferenced in CmpFreePostBlock(SlavePostBlock) 01691 } 01692 // MasterPostBlock if freed by CmpNotifyChangeKey !!! 01693 ObDereferenceObject(MasterKeyBody); 01694 CmpUnlockRegistry(); 01695 return status; 01696 01697 } 01698 01699 ASSERT(status == STATUS_PENDING || status == STATUS_SUCCESS); 01700 01701 if(SlavePresent) { 01702 if( status == STATUS_SUCCESS ) { 01703 // 01704 // The notify has already been triggered for the master, there is no point to set one for the slave too 01705 // Clean up the mess we made for the slave object and signal as there is no slave present 01706 // 01707 CmpFreePostBlock(SlavePostBlock); 01708 SlavePresent = FALSE; 01709 } else { 01710 // 01711 // Call worker for slave 01712 // 01713 status = CmpNotifyChangeKey( 01714 SlaveKeyBody, 01715 SlavePostBlock, 01716 CompletionFilter, 01717 WatchTree, 01718 Buffer, 01719 BufferSize, 01720 MasterPostBlock 01721 ); 01722 if(!NT_SUCCESS(status)) { 01723 // 01724 // if we are here, the slave key has been deleted in between or there was no memory available to allocate 01725 // a notify block for the slave key. We do the cleanup here since we already hold the registry lock 01726 // exclusively and we don't want to give a anybody else a chance to trigger the notification on master post 01727 // (otherwise we could end up freeing it twice). The master post block and the user event are cleaned later, 01728 // covering both single and multiple notifications cases 01729 // 01730 01731 // Use Cmp variant to protect for multiple deletion of the same object 01732 CmpRemoveEntryList(&(MasterPostBlock->NotifyList)); 01733 01734 KeRaiseIrql(APC_LEVEL, &OldIrql); 01735 // Use Cmp variant to protect for multiple deletion of the same object 01736 CmpRemoveEntryList(&(MasterPostBlock->ThreadList)); 01737 KeLowerIrql(OldIrql); 01738 } 01739 } 01740 } 01741 01742 // 01743 // postblocks are now on various lists, so we can die without losing them 01744 // 01745 CmpUnlockRegistry(); 01746 01747 if (NT_SUCCESS(status)) { 01748 // 01749 // success. wait for event if sync. 01750 // do NOT deref User event, back side of notify will do that. 01751 // 01752 ASSERT(status == STATUS_PENDING || status == STATUS_SUCCESS); 01753 01754 if (PostType == PostSynchronous) { 01755 WaitStatus = KeWaitForSingleObject(MasterPostBlock->u->Sync.SystemEvent, 01756 Executive, 01757 PreviousMode, 01758 TRUE, 01759 NULL); 01760 01761 01762 if ((WaitStatus==STATUS_ALERTED) || (WaitStatus == STATUS_USER_APC)) { 01763 01764 // 01765 // The wait was aborted, clean up and return. 01766 // 01767 // 1. Remove the PostBlocks from the notify list. This 01768 // is normally done by the back end of notify, but 01769 // we have to do it here since the back end is not 01770 // involved. 01771 // 2. Delist and free the post blocks 01772 // 01773 CmpLockRegistryExclusive(); 01774 01775 KeRaiseIrql(APC_LEVEL, &OldIrql); 01776 if(SlavePresent) { 01777 if (SlavePostBlock->NotifyList.Flink != NULL) { 01778 // Use Cmp variant to protect for multiple deletion of the same object 01779 CmpRemoveEntryList(&(SlavePostBlock->NotifyList)); 01780 } 01781 // Use Cmp variant to protect for multiple deletion of the same object 01782 CmpRemoveEntryList(&(SlavePostBlock->ThreadList)); 01783 } 01784 01785 if (MasterPostBlock->NotifyList.Flink != NULL) { 01786 // Use Cmp variant to protect for multiple deletion of the same object 01787 CmpRemoveEntryList(&(MasterPostBlock->NotifyList)); 01788 } 01789 // Use Cmp variant to protect for multiple deletion of the same object 01790 CmpRemoveEntryList(&(MasterPostBlock->ThreadList)); 01791 KeLowerIrql(OldIrql); 01792 01793 CmpUnlockRegistry(); 01794 01795 if(SlavePresent) { 01796 CmpFreePostBlock(SlavePostBlock); 01797 } 01798 CmpFreePostBlock(MasterPostBlock); 01799 01800 status = WaitStatus; 01801 01802 } else { 01803 // 01804 // The wait was satisfied, which means the back end has 01805 // already removed the postblock from the notify list. 01806 // We just have to delist and free the post block. 01807 // 01808 01809 // 01810 // Aquire the registry lock exclusive to enter the post block rule prerequisites 01811 // 01812 CmpLockRegistryExclusive(); 01813 01814 KeRaiseIrql(APC_LEVEL, &OldIrql); 01815 if(SlavePresent) { 01816 if (SlavePostBlock->NotifyList.Flink != NULL) { 01817 // Use Cmp variant to protect for multiple deletion of the same object 01818 CmpRemoveEntryList(&(SlavePostBlock->NotifyList)); 01819 } 01820 // Use Cmp variant to protect for multiple deletion of the same object 01821 CmpRemoveEntryList(&(SlavePostBlock->ThreadList)); 01822 } 01823 01824 if (MasterPostBlock->NotifyList.Flink != NULL) { 01825 // Use Cmp variant to protect for multiple deletion of the same object 01826 CmpRemoveEntryList(&(MasterPostBlock->NotifyList)); 01827 } 01828 // Use Cmp variant to protect for multiple deletion of the same object 01829 CmpRemoveEntryList(&(MasterPostBlock->ThreadList)); 01830 KeLowerIrql(OldIrql); 01831 01832 CmpUnlockRegistry(); 01833 01834 status = MasterPostBlock->u->Sync.Status; 01835 01836 try { 01837 CmpSetIoStatus(IoStatusBlock, status, 0, UseIosb32); 01838 } except (EXCEPTION_EXECUTE_HANDLER) { 01839 status = GetExceptionCode(); 01840 } 01841 01842 if(SlavePresent) { 01843 CmpFreePostBlock(SlavePostBlock); 01844 } 01845 CmpFreePostBlock(MasterPostBlock); 01846 } 01847 } 01848 01849 } else { 01850 CmpFreePostBlock(MasterPostBlock); 01851 // 01852 // it didn't work, clean up for error path 01853 // 01854 if (UserEvent != NULL) { 01855 ObDereferenceObject(UserEvent); 01856 } 01857 } 01858 01859 ObDereferenceObject(MasterKeyBody); 01860 // 01861 // Don't dereference SlaveKeyBody!!! => Back-end routine will do that !!! 01862 // 01863 01864 END_LOCK_CHECKPOINT; 01865 01866 return status; 01867 } 01868 01869 NTSTATUS 01870 NtOpenKey( 01871 OUT PHANDLE KeyHandle, 01872 IN ACCESS_MASK DesiredAccess, 01873 IN POBJECT_ATTRIBUTES ObjectAttributes 01874 ) 01875 /*++ 01876 01877 Routine Description: 01878 01879 A registry key which already exists may be opened with NtOpenKey. 01880 01881 Share access is computed from desired access. 01882 01883 Arguments: 01884 01885 KeyHandle - Receives a Handle which is used to access the 01886 specified key in the Registration Database. 01887 01888 DesiredAccess - Specifies the access rights desired. 01889 01890 ObjectAttributes - Specifies the attributes of the key being opened. 01891 Note that a key name must be specified. If a Root Directory 01892 is specified, the name is relative to the root. The name of 01893 the object must be within the name space allocated to the 01894 Registry, that is, all names beginning "\Registry". RootHandle, 01895 if present, must be a handle to "\", or "\Registry", or a 01896 key under "\Registry". If the specified key does not exist, or 01897 access requested is not allowed, the operation will fail. 01898 01899 NOTE: Object manager will capture and probe this argument. 01900 01901 Return Value: 01902 01903 NTSTATUS - Result code from call, among the following: 01904 01905 <TBS> 01906 01907 --*/ 01908 { 01909 NTSTATUS status; 01910 KPROCESSOR_MODE mode; 01911 PCM_KEY_BODY KeyBody; 01912 HANDLE Handle =0; 01913 UNICODE_STRING CapturedObjectName = {0}; 01914 01915 // Start registry call tracing 01916 StartWmiCmTrace(); 01917 01918 PAGED_CODE(); 01919 01920 BEGIN_LOCK_CHECKPOINT; 01921 01922 CMLOG(CML_API, CMS_NTAPI) KdPrint(("NtOpenKey\n")); 01923 CMLOG(CML_API_ARGS, CMS_NTAPI) { 01924 KdPrint(("\tDesiredAccess=%08lx ", DesiredAccess)); 01925 KdPrint(("\tRootHandle=%08lx\n", ObjectAttributes->RootDirectory)); 01926 KdPrint(("\tName='%wZ'\n", ObjectAttributes->ObjectName)); 01927 } 01928 01929 mode = KeGetPreviousMode(); 01930 01931 CmpLockRegistry(); 01932 01933 #ifdef LOG_NT_API 01934 if (CmpLogApi) { 01935 KdPrint(("NtOpenKey %wZ relative to %d...", 01936 ObjectAttributes->ObjectName, 01937 ObjectAttributes->RootDirectory)); 01938 } 01939 #endif 01940 01941 try { 01942 01943 if (mode == UserMode) { 01944 ProbeAndZeroHandle(KeyHandle); 01945 // 01946 // probe the ObjectAttributes as we shall use it for tracing 01947 // 01948 ProbeForRead( ObjectAttributes, 01949 sizeof(OBJECT_ATTRIBUTES), 01950 PROBE_ALIGNMENT(OBJECT_ATTRIBUTES) ); 01951 CapturedObjectName = ProbeAndReadUnicodeString(ObjectAttributes->ObjectName); 01952 ProbeForRead( 01953 CapturedObjectName.Buffer, 01954 CapturedObjectName.Length, 01955 sizeof(WCHAR) 01956 ); 01957 01958 } else { 01959 CapturedObjectName = *(ObjectAttributes->ObjectName); 01960 } 01961 01962 status = ObOpenObjectByName( 01963 ObjectAttributes, 01964 CmpKeyObjectType, 01965 mode, 01966 NULL, 01967 DesiredAccess, 01968 NULL, 01969 &Handle 01970 ); 01971 if (status==STATUS_PREDEFINED_HANDLE) { 01972 status = ObReferenceObjectByHandle(Handle, 01973 0, 01974 CmpKeyObjectType, 01975 KernelMode, 01976 (PVOID *)(&KeyBody), 01977 NULL); 01978 if (NT_SUCCESS(status)) { 01979 *KeyHandle = (HANDLE)LongToHandle(KeyBody->Type); 01980 ObDereferenceObject((PVOID)KeyBody); 01981 status = STATUS_SUCCESS; 01982 } 01983 NtClose(Handle); 01984 Handle = *KeyHandle; 01985 } else 01986 if (NT_SUCCESS(status)) { 01987 *KeyHandle = Handle; 01988 } 01989 01990 } except (CmpExceptionFilter(GetExceptionInformation())) { 01991 CMLOG(CML_API, CMS_EXCEPTION) { 01992 KdPrint(("!!NtOpenKey: code:%08lx\n", GetExceptionCode())); 01993 } 01994 status = GetExceptionCode(); 01995 } 01996 #ifdef LOG_NT_API 01997 if (CmpLogApi) { 01998 if (NT_SUCCESS(status)) { 01999 KdPrint(("succeeded %d\n",Handle)); 02000 } else { 02001 KdPrint(("failed %08lx\n",status)); 02002 } 02003 } 02004 #endif 02005 02006 CmpUnlockRegistry(); 02007 02008 END_LOCK_CHECKPOINT; 02009 02010 // End registry call tracing 02011 EndWmiCmTrace(status,Handle,&CapturedObjectName,EVENT_TRACE_TYPE_REGOPEN); 02012 02013 return status; 02014 } 02015 02016 02017 NTSTATUS 02018 NtQueryKey( 02019 IN HANDLE KeyHandle, 02020 IN KEY_INFORMATION_CLASS KeyInformationClass, 02021 IN PVOID KeyInformation, 02022 IN ULONG Length, 02023 IN PULONG ResultLength 02024 ) 02025 /*++ 02026 02027 Routine Description: 02028 02029 Data about the class of a key, and the numbers and sizes of its 02030 children and value entries may be queried with NtQueryKey. 02031 02032 If KeyValueInformation is not long enough to hold all requested data, 02033 STATUS_BUFFER_OVERFLOW will be returned, and ResultLength will be 02034 set to the number of bytes actually required. 02035 02036 NOTE: The returned lengths are guaranteed to be at least as 02037 long as the described values, but may be longer in 02038 some circumstances. 02039 02040 Arguments: 02041 02042 KeyHandle - Handle of the key to query data for. Must have been 02043 opened for KEY_QUERY_KEY access. 02044 02045 KeyInformationClass - Specifies the type of information 02046 returned in Buffer. One of the following types: 02047 02048 KeyBasicInformation - return last write time, title index, and name. 02049 (See KEY_BASIC_INFORMATION) 02050 02051 KeyNodeInformation - return last write time, title index, name, class. 02052 (See KEY_NODE_INFORMATION) 02053 02054 KeyFullInformation - return all data except for name and security. 02055 (See KEY_FULL_INFORMATION) 02056 02057 KeyInformation -Supplies pointer to buffer to receive the data. 02058 02059 Length - Length of KeyInformation in bytes. 02060 02061 ResultLength - Number of bytes actually written into KeyInformation. 02062 02063 Return Value: 02064 02065 NTSTATUS - Result code from call, among the following: 02066 02067 <TBS> 02068 02069 --*/ 02070 { 02071 NTSTATUS status; 02072 PCM_KEY_BODY KeyBody; 02073 KPROCESSOR_MODE mode; 02074 02075 // Start registry call tracing 02076 StartWmiCmTrace(); 02077 02078 PAGED_CODE(); 02079 02080 BEGIN_LOCK_CHECKPOINT; 02081 02082 CMLOG(CML_API, CMS_NTAPI) KdPrint(("NtQueryKey\n")); 02083 #ifdef LOG_NT_API 02084 if (CmpLogApi) { 02085 KdPrint(("NtQueryKey %d %d\n",KeyHandle,KeyInformationClass)); 02086 } 02087 #endif 02088 02089 if ((KeyInformationClass != KeyBasicInformation) && 02090 (KeyInformationClass != KeyNodeInformation) && 02091 (KeyInformationClass != KeyFullInformation) && 02092 (KeyInformationClass != KeyNameInformation) ) 02093 { 02094 02095 // End registry call tracing 02096 EndWmiCmTrace(STATUS_INVALID_PARAMETER,KeyHandle,NULL,EVENT_TRACE_TYPE_REGQUERY); 02097 02098 return STATUS_INVALID_PARAMETER; 02099 } 02100 02101 mode = KeGetPreviousMode(); 02102 02103 if( KeyInformationClass == KeyNameInformation ){ 02104 // 02105 // special case: name information is available regardless of the access level 02106 // you have on the key (provided that you have some ...) 02107 // 02108 02109 OBJECT_HANDLE_INFORMATION HandleInfo; 02110 02111 // reference with "no access required" 02112 status = ObReferenceObjectByHandle( 02113 KeyHandle, 02114 0, 02115 CmpKeyObjectType, 02116 mode, 02117 (PVOID *)(&KeyBody), 02118 &HandleInfo 02119 ); 02120 if( NT_SUCCESS(status) ) { 02121 if( HandleInfo.GrantedAccess == 0 ) { 02122 // 02123 // no access is granted on the handle; bad luck! 02124 // 02125 ObDereferenceObject((PVOID)KeyBody); 02126 02127 status = STATUS_ACCESS_DENIED; 02128 } 02129 } 02130 } else { 02131 status = ObReferenceObjectByHandle( 02132 KeyHandle, 02133 KEY_QUERY_VALUE, 02134 CmpKeyObjectType, 02135 mode, 02136 (PVOID *)(&KeyBody), 02137 NULL 02138 ); 02139 } 02140 02141 if (NT_SUCCESS(status)) { 02142 02143 try { 02144 if (mode == UserMode) { 02145 ProbeForWrite( 02146 KeyInformation, 02147 Length, 02148 sizeof(ULONG) 02149 ); 02150 ProbeForWriteUlong(ResultLength); 02151 } 02152 02153 } except (EXCEPTION_EXECUTE_HANDLER) { 02154 CMLOG(CML_API, CMS_EXCEPTION) { 02155 KdPrint(("!!NtQueryKey: code:%08lx\n", GetExceptionCode())); 02156 } 02157 status = GetExceptionCode(); 02158 } 02159 02160 if( NT_SUCCESS(status)) { 02161 // 02162 // CmQueryKey is protected to user mode buffer exceptions 02163 // all other exceptions are cm internals and should result in a bugcheck 02164 // 02165 status = CmQueryKey( 02166 KeyBody->KeyControlBlock, 02167 KeyInformationClass, 02168 KeyInformation, 02169 Length, 02170 ResultLength 02171 ); 02172 } 02173 02174 ObDereferenceObject((PVOID)KeyBody); 02175 } 02176 02177 END_LOCK_CHECKPOINT; 02178 02179 // End registry call tracing 02180 EndWmiCmTrace(status,KeyHandle,NULL,EVENT_TRACE_TYPE_REGQUERY); 02181 02182 return status; 02183 } 02184 02185 02186 NTSTATUS 02187 NtQueryValueKey( 02188 IN HANDLE KeyHandle, 02189 IN PUNICODE_STRING ValueName, 02190 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, 02191 IN PVOID KeyValueInformation, 02192 IN ULONG Length, 02193 IN PULONG ResultLength 02194 ) 02195 /*++ 02196 02197 Routine Description: 02198 02199 The ValueName, TitleIndex, Type, and Data for any one of a key's 02200 value entries may be queried with NtQueryValueKey. 02201 02202 If KeyValueInformation is not long enough to hold all requested data, 02203 STATUS_BUFFER_OVERFLOW will be returned, and ResultLength will be 02204 set to the number of bytes actually required. 02205 02206 Arguments: 02207 02208 KeyHandle - Handle of the key whose value entries are to be 02209 enumerated. Must be open for KEY_QUERY_VALUE access. 02210 02211 Index - Specifies the (0-based) number of the sub key to be returned. 02212 02213 ValueName - The name of the value entry to return data for. 02214 02215 KeyValueInformationClass - Specifies the type of information 02216 returned in KeyValueInformation. One of the following types: 02217 02218 KeyValueBasicInformation - return time of last write, title 02219 index, and name. (See KEY_VALUE_BASIC_INFORMATION) 02220 02221 KeyValueFullInformation - return time of last write, title 02222 index, name, class. (See KEY_VALUE_FULL_INFORMATION) 02223 02224 KeyValueInformation -Supplies pointer to buffer to receive the data. 02225 02226 Length - Length of KeyValueInformation in bytes. 02227 02228 ResultLength - Number of bytes actually written into KeyValueInformation. 02229 02230 Return Value: 02231 02232 NTSTATUS - Result code from call, among the following: 02233 02234 <TBS> 02235 02236 TMP: The IopQueryRegsitryValues() routine in the IO system assumes 02237 STATUS_OBJECT_NAME_NOT_FOUND is returned if the value being queried 02238 for does not exist. 02239 02240 --*/ 02241 { 02242 NTSTATUS status; 02243 PCM_KEY_BODY KeyBody; 02244 KPROCESSOR_MODE mode; 02245 UNICODE_STRING LocalValueName; 02246 02247 // Start registry call tracing 02248 StartWmiCmTrace(); 02249 02250 PAGED_CODE(); 02251 02252 BEGIN_LOCK_CHECKPOINT; 02253 02254 CMLOG(CML_API, CMS_NTAPI) KdPrint(("NtQueryValueKey\n")); 02255 CMLOG(CML_API_ARGS, CMS_NTAPI) { 02256 KdPrint(("\tKeyHandle=%08lx\n", KeyHandle)); 02257 KdPrint(("\tValueName='%wZ'\n", ValueName)); 02258 } 02259 #ifdef LOG_NT_API 02260 if (CmpLogApi) { 02261 KdPrint(("NtQueryValueKey %d %wZ %d\n",KeyHandle,ValueName,KeyValueInformationClass)); 02262 } 02263 #endif 02264 02265 if ((KeyValueInformationClass != KeyValueBasicInformation) && 02266 (KeyValueInformationClass != KeyValueFullInformation) && 02267 (KeyValueInformationClass != KeyValueFullInformationAlign64) && 02268 (KeyValueInformationClass != KeyValuePartialInformationAlign64) && 02269 (KeyValueInformationClass != KeyValuePartialInformation)) 02270 { 02271 // End registry call tracing 02272 EndWmiCmTrace(STATUS_INVALID_PARAMETER,KeyHandle,NULL,EVENT_TRACE_TYPE_REGQUERYVALUE); 02273 02274 return STATUS_INVALID_PARAMETER; 02275 } 02276 02277 mode = KeGetPreviousMode(); 02278 02279 status = ObReferenceObjectByHandle( 02280 KeyHandle, 02281 KEY_QUERY_VALUE, 02282 CmpKeyObjectType, 02283 mode, 02284 (PVOID *)(&KeyBody), 02285 NULL 02286 ); 02287 02288 if (NT_SUCCESS(status)) { 02289 02290 try { 02291 if (mode == UserMode) { 02292 LocalValueName = ProbeAndReadUnicodeString(ValueName); 02293 ProbeForRead(LocalValueName.Buffer, 02294 LocalValueName.Length, 02295 sizeof(WCHAR)); 02296 02297 // 02298 // We only probe the output buffer for Read to avoid touching 02299 // all the pages. Some people like to pass in gigantic buffers 02300 // Just In Case. The actual copy into the buffer is done under 02301 // an exception handler. 02302 // 02303 02304 ProbeForRead(KeyValueInformation, 02305 Length, 02306 sizeof(ULONG)); 02307 ProbeForWriteUlong(ResultLength); 02308 } else { 02309 LocalValueName = *ValueName; 02310 } 02311 02312 } except (EXCEPTION_EXECUTE_HANDLER) { 02313 CMLOG(CML_API, CMS_EXCEPTION) { 02314 KdPrint(("!!NtQueryValueKey: code:%08lx\n", GetExceptionCode())); 02315 } 02316 status = GetExceptionCode(); 02317 } 02318 02319 if( NT_SUCCESS(status)) { 02320 // 02321 // CmQueryValueKey is protected to user mode buffer exceptions 02322 // all other exceptions are cm internals and should result in a bugcheck 02323 // 02324 status = CmQueryValueKey(KeyBody->KeyControlBlock, 02325 LocalValueName, 02326 KeyValueInformationClass, 02327 KeyValueInformation, 02328 Length, 02329 ResultLength); 02330 } 02331 02332 ObDereferenceObject((PVOID)KeyBody); 02333 } else { 02334 LocalValueName.Buffer = NULL; 02335 LocalValueName.Length = 0; 02336 } 02337 02338 END_LOCK_CHECKPOINT; 02339 02340 // End registry call tracing 02341 EndWmiCmTrace(status,KeyHandle,&LocalValueName,EVENT_TRACE_TYPE_REGQUERYVALUE); 02342 02343 return status; 02344 } 02345 02346 02347 NTSTATUS 02348 NtRestoreKey( 02349 IN HANDLE KeyHandle, 02350 IN HANDLE FileHandle, 02351 IN ULONG Flags 02352 ) 02353 /*++ 02354 02355 Routine Description: 02356 02357 A file in the format created by NtSaveKey may be loaded into 02358 the system's active registry with NtRestoreKey. An entire subtree 02359 is created in the active registry as a result. All of the 02360 data for the new sub-tree, including such things as security 02361 descriptors, will be read from the source file. The data will 02362 not be interpreted in any way. 02363 02364 This call (unlike NtLoadKey, see below) copies the data. The 02365 system will NOT be using the source file after the call returns. 02366 02367 If the flag REG_WHOLE_HIVE_VOLATILE is specified, a new hive 02368 can be created. It will be a memory only copy. The restore 02369 must be done to the root of a hive (e.g. \registry\user<name>) 02370 02371 If the flag is NOT set, then the target of the restore must 02372 be an existing hive. The restore can be done to an arbitrary 02373 location within an existing hive. 02374 02375 Caller must have SeRestorePrivilege privilege. 02376 02377 If the flag REG_REFRESH_HIVE is set (must be only flag) then the 02378 the Hive will be restored to its state as of the last flush. 02379 02380 The hive must be marked NOLAZY_FLUSH, and the caller must have 02381 TCB privilege, and the handle must point to the root of the hive. 02382 If the refresh fails, the hive will be corrupt, and the system 02383 will bugcheck. Notifies are flushed. The hive file will be resized, 02384 the log will not. If there is any volatile space in the hive 02385 being refreshed, STATUS_UNSUCCESSFUL will be returned. (It's much 02386 too obscure a failure to warrant a new error code.) 02387 02388 If the flag REG_FORCE_RESTORE is set, the restore operation is done 02389 even if the KeyHandle has open subkeys by other applications 02390 02391 Arguments: 02392 02393 KeyHandle - refers to the Key in the registry which is to be the 02394 root of the new tree read from the disk. This key 02395 will be replaced. 02396 02397 FileHandle - refers to file to restore from, must have read access. 02398 02399 Flags - If REG_WHOLE_HIVE_VOLATILE is set, then the copy will 02400 exist only in memory, and disappear when the machine 02401 is rebooted. No hive file will be created on disk. 02402 02403 Normally, a hive file will be created on disk. 02404 02405 Return Value: 02406 02407 NTSTATUS - values TBS. 02408 02409 02410 --*/ 02411 { 02412 NTSTATUS status; 02413 PCM_KEY_BODY KeyBody; 02414 KPROCESSOR_MODE mode; 02415 02416 PAGED_CODE(); 02417 02418 BEGIN_LOCK_CHECKPOINT; 02419 02420 CMLOG(CML_API, CMS_NTAPI) KdPrint(("NtRestoreKey\n")); 02421 #ifdef LOG_NT_API 02422 if (CmpLogApi) { 02423 KdPrint(("NtRestoreKey\n")); 02424 } 02425 #endif 02426 02427 mode = KeGetPreviousMode(); 02428 // 02429 // Check to see if the caller has the privilege to make this call. 02430 // 02431 if (!SeSinglePrivilegeCheck(SeRestorePrivilege, mode)) { 02432 return(STATUS_PRIVILEGE_NOT_HELD); 02433 } 02434 02435 // 02436 // Force previous mode to be KernelMode so we can call filesystems 02437 // 02438 02439 if (mode == UserMode) { 02440 return ZwRestoreKey(KeyHandle, FileHandle, Flags); 02441 } else { 02442 02443 status = ObReferenceObjectByHandle( 02444 KeyHandle, 02445 0, 02446 CmpKeyObjectType, 02447 mode, 02448 (PVOID *)(&KeyBody), 02449 NULL 02450 ); 02451 02452 if (NT_SUCCESS(status)) { 02453 02454 status = CmRestoreKey( 02455 KeyBody->KeyControlBlock, 02456 FileHandle, 02457 Flags 02458 ); 02459 02460 ObDereferenceObject((PVOID)KeyBody); 02461 } 02462 } 02463 02464 END_LOCK_CHECKPOINT; 02465 02466 return status; 02467 } 02468 02469 02470 NTSTATUS 02471 NtSaveKey( 02472 IN HANDLE KeyHandle, 02473 IN HANDLE FileHandle 02474 ) 02475 /*++ 02476 02477 Routine Description: 02478 02479 A subtree of the active registry may be written to a file in a 02480 format suitable for use with NtRestoreKey. All of the data in the 02481 subtree, including such things as security descriptors will be written 02482 out. 02483 02484 Caller must have SeBackupPrivilege privilege. 02485 02486 Arguments: 02487 02488 KeyHandle - refers to the Key in the registry which is the 02489 root of the tree to be written to disk. The specified 02490 node will be included in the data written out. 02491 02492 FileHandle - a file handle with write access to the target file 02493 of interest. 02494 02495 Return Value: 02496 02497 NTSTATUS - values TBS 02498 02499 --*/ 02500 { 02501 NTSTATUS status; 02502 PCM_KEY_BODY KeyBody; 02503 KPROCESSOR_MODE mode; 02504 02505 PAGED_CODE(); 02506 02507 BEGIN_LOCK_CHECKPOINT; 02508 02509 CMLOG(CML_API, CMS_NTAPI) KdPrint(("NtSaveKey\n")); 02510 #ifdef LOG_NT_API 02511 if (CmpLogApi) { 02512 KdPrint(("NtSaveKey\n")); 02513 } 02514 #endif 02515 02516 mode = KeGetPreviousMode(); 02517 02518 // 02519 // Check to see if the caller has the privilege to make this call. 02520 // 02521 if (!SeSinglePrivilegeCheck(SeBackupPrivilege, mode)) { 02522 return(STATUS_PRIVILEGE_NOT_HELD); 02523 } 02524 02525 // 02526 // Force previous mode to be KernelMode 02527 // 02528 02529 if (mode == UserMode) { 02530 return ZwSaveKey(KeyHandle, FileHandle); 02531 } else { 02532 02533 status = ObReferenceObjectByHandle( 02534 KeyHandle, 02535 0, 02536 CmpKeyObjectType, 02537 mode, 02538 (PVOID *)(&KeyBody), 02539 NULL 02540 ); 02541 02542 if (NT_SUCCESS(status)) { 02543 02544 status = CmSaveKey( 02545 KeyBody->KeyControlBlock, 02546 FileHandle 02547 ); 02548 02549 ObDereferenceObject((PVOID)KeyBody); 02550 } 02551 } 02552 02553 END_LOCK_CHECKPOINT; 02554 02555 return status; 02556 } 02557 02558 NTSTATUS 02559 NtSaveMergedKeys( 02560 IN HANDLE HighPrecedenceKeyHandle, 02561 IN HANDLE LowPrecedenceKeyHandle, 02562 IN HANDLE FileHandle 02563 ) 02564 /*++ 02565 02566 Routine Description: 02567 02568 Two subtrees of the registry can be merged. The resulting subtree may 02569 be written to a file in a format suitable for use with NtRestoreKey. 02570 All of the data in the subtree, including such things as security 02571 descriptors will be written out. 02572 02573 Caller must have SeBackupPrivilege privilege. 02574 02575 Arguments: 02576 02577 HighPrecedenceKeyHandle - refers to the key in the registry which is the 02578 root of the HighPrecedence tree. I.e., when a key is present in 02579 both trees headded by the two keys, the key underneath HighPrecedence 02580 tree will always prevail. The specified 02581 node will be included in the data written out. 02582 02583 LowPrecedenceKeyHandle - referrs to the key in the registry which is the 02584 root of the "second choice" tree. Keys from this trees get saved 02585 when there is no equivalent key in the tree headded by HighPrecedenceKey 02586 02587 FileHandle - a file handle with write access to the target file 02588 of interest. 02589 02590 Return Value: 02591 02592 NTSTATUS - values TBS 02593 02594 --*/ 02595 { 02596 NTSTATUS status; 02597 PCM_KEY_BODY HighKeyBody; 02598 PCM_KEY_BODY LowKeyBody; 02599 KPROCESSOR_MODE mode; 02600 02601 PAGED_CODE(); 02602 02603 BEGIN_LOCK_CHECKPOINT; 02604 02605 CMLOG(CML_API, CMS_NTAPI) KdPrint(("NtSaveMergedKeys\n")); 02606 #ifdef LOG_NT_API 02607 if (CmpLogApi) { 02608 KdPrint(("NtSaveMergedKeys\n")); 02609 } 02610 #endif 02611 02612 mode = KeGetPreviousMode(); 02613 02614 // 02615 // Check to see if the caller has the privilege to make this call. 02616 // 02617 if (!SeSinglePrivilegeCheck(SeBackupPrivilege, mode)) { 02618 return(STATUS_PRIVILEGE_NOT_HELD); 02619 } 02620 02621 // 02622 // Force previous mode to be KernelMode 02623 // 02624 02625 if (mode == UserMode) { 02626 return ZwSaveMergedKeys(HighPrecedenceKeyHandle, LowPrecedenceKeyHandle, FileHandle); 02627 } else { 02628 02629 status = ObReferenceObjectByHandle( 02630 HighPrecedenceKeyHandle, 02631 0, 02632 CmpKeyObjectType, 02633 mode, 02634 (PVOID *)(&HighKeyBody), 02635 NULL 02636 ); 02637 02638 if (NT_SUCCESS(status)) { 02639 02640 status = ObReferenceObjectByHandle( 02641 LowPrecedenceKeyHandle, 02642 0, 02643 CmpKeyObjectType, 02644 mode, 02645 (PVOID *)(&LowKeyBody), 02646 NULL 02647 ); 02648 02649 if (NT_SUCCESS(status)) { 02650 02651 status = CmSaveMergedKeys( 02652 HighKeyBody->KeyControlBlock, 02653 LowKeyBody->KeyControlBlock, 02654 FileHandle 02655 ); 02656 02657 ObDereferenceObject((PVOID)LowKeyBody); 02658 } 02659 02660 ObDereferenceObject((PVOID)HighKeyBody); 02661 } 02662 02663 } 02664 02665 END_LOCK_CHECKPOINT; 02666 02667 return status; 02668 } 02669 02670 02671 NTSTATUS 02672 NtSetValueKey( 02673 IN HANDLE KeyHandle, 02674 IN PUNICODE_STRING ValueName, 02675 IN ULONG TitleIndex OPTIONAL, 02676 IN ULONG Type, 02677 IN PVOID Data, 02678 IN ULONG DataSize 02679 ) 02680 /*++ 02681 02682 Routine Description: 02683 02684 A value entry may be created or replaced with NtSetValueKey. 02685 02686 If a value entry with a Value ID (i.e. name) matching the 02687 one specified by ValueName exists, it is deleted and replaced 02688 with the one specified. If no such value entry exists, a new 02689 one is created. NULL is a legal Value ID. While Value IDs must 02690 be unique within any given key, the same Value ID may appear 02691 in many different keys. 02692 02693 Arguments: 02694 02695 KeyHandle - Handle of the key whose for which a value entry is 02696 to be set. Must be opened for KEY_SET_VALUE access. 02697 02698 ValueName - The unique (relative to the containing key) name 02699 of the value entry. May be NULL. 02700 02701 TitleIndex - Supplies the title index for ValueName. The title 02702 index specifies the index of the localized alias for the ValueName. 02703 02704 Type - The integer type number of the value entry. 02705 02706 Data - Pointer to buffer with actual data for the value entry. 02707 02708 DataSize - Size of Data buffer. 02709 02710 02711 Return Value: 02712 02713 NTSTATUS - Result code from call, among the following: 02714 02715 <TBS> 02716 02717 --*/ 02718 { 02719 NTSTATUS status; 02720 PCM_KEY_BODY KeyBody; 02721 KPROCESSOR_MODE mode; 02722 UNICODE_STRING LocalValueName; 02723 PWSTR CapturedName=NULL; 02724 02725 // Start registry call tracing 02726 StartWmiCmTrace(); 02727 02728 PAGED_CODE(); 02729 02730 BEGIN_LOCK_CHECKPOINT; 02731 02732 CMLOG(CML_API, CMS_NTAPI) KdPrint(("NtSetValueKey\n")); 02733 CMLOG(CML_API_ARGS, CMS_NTAPI) { 02734 KdPrint(("\tKeyHandle=%08lx\n", KeyHandle)); 02735 KdPrint(("\tValueName='%wZ'n", ValueName)); 02736 } 02737 #ifdef LOG_NT_API 02738 if (CmpLogApi) { 02739 KdPrint(("NtSetValueKey %d %wZ\n",KeyHandle,ValueName)); 02740 } 02741 #endif 02742 02743 mode = KeGetPreviousMode(); 02744 02745 status = ObReferenceObjectByHandle( 02746 KeyHandle, 02747 KEY_SET_VALUE, 02748 CmpKeyObjectType, 02749 mode, 02750 (PVOID *)(&KeyBody), 02751 NULL 02752 ); 02753 02754 if (NT_SUCCESS(status)) { 02755 02756 if (mode == UserMode) { 02757 try { 02758 LocalValueName = ProbeAndReadUnicodeString(ValueName); 02759 ProbeForRead(Data, 02760 DataSize, 02761 sizeof(UCHAR)); 02762 02763 // 02764 // Sanity check for ValueName length 02765 // 02766 if(LocalValueName.Length > MAX_KEY_VALUE_NAME_LENGTH) { 02767 status = STATUS_INVALID_PARAMETER; 02768 goto Exit; 02769 } 02770 02771 // 02772 // Capture the name buffer. Note that a zero-length name is valid, that is the 02773 // "Default" value. 02774 // 02775 if (LocalValueName.Length > 0) { 02776 ProbeForRead(LocalValueName.Buffer, 02777 LocalValueName.Length, 02778 sizeof(WCHAR)); 02779 CapturedName = ExAllocatePoolWithQuotaTag(PagedPool, LocalValueName.Length, 'nVmC'); 02780 if (CapturedName == NULL) { 02781 status = STATUS_INSUFFICIENT_RESOURCES; 02782 goto Exit; 02783 } 02784 RtlCopyMemory(CapturedName, LocalValueName.Buffer, LocalValueName.Length); 02785 } else { 02786 CapturedName = NULL; 02787 } 02788 LocalValueName.Buffer = CapturedName; 02789 02790 } except (CmpExceptionFilter(GetExceptionInformation())) { 02791 CMLOG(CML_API, CMS_EXCEPTION) { 02792 KdPrint(("!!NtSetValueKey: code:%08lx\n", GetExceptionCode())); 02793 } 02794 status = GetExceptionCode(); 02795 goto Exit; 02796 } 02797 } else { 02798 LocalValueName = *ValueName; 02799 CapturedName = NULL; 02800 02801 // 02802 // Sanity check for ValueName length 02803 // 02804 if(LocalValueName.Length > MAX_KEY_VALUE_NAME_LENGTH) { 02805 status = STATUS_INVALID_PARAMETER; 02806 goto Exit; 02807 } 02808 } 02809 02810 status = CmSetValueKey(KeyBody->KeyControlBlock, 02811 &LocalValueName, 02812 Type, 02813 Data, 02814 DataSize); 02815 02816 Exit: 02817 // End registry call tracing 02818 EndWmiCmTrace(status,KeyHandle,&LocalValueName,EVENT_TRACE_TYPE_REGSETVALUE); 02819 02820 if (CapturedName != NULL) { 02821 ExFreePool(CapturedName); 02822 } 02823 ObDereferenceObject((PVOID)KeyBody); 02824 } 02825 02826 END_LOCK_CHECKPOINT; 02827 02828 return status; 02829 } 02830 02831 NTSTATUS 02832 NtLoadKey( 02833 IN POBJECT_ATTRIBUTES TargetKey, 02834 IN POBJECT_ATTRIBUTES SourceFile 02835 ) 02836 02837 /*++ 02838 02839 Routine Description: 02840 02841 A hive (file in the format created by NtSaveKey) may be linked 02842 into the active registry with this call. UNLIKE NtRestoreKey, 02843 the file specified to NtLoadKey will become the actual backing 02844 store of part of the registry (that is, it will NOT be copied.) 02845 02846 The file may have an associated .log file. 02847 02848 If the hive file is marked as needing a .log file, and one is 02849 not present, the call will fail. 02850 02851 The name specified by SourceFile must be such that ".log" can 02852 be appended to it to generate the name of the log file. Thus, 02853 on FAT file systems, the hive file may not have an extension. 02854 02855 Caller must have SeRestorePrivilege privilege. 02856 02857 This call is used by logon to make the user's profile available 02858 in the registry. It is not intended for use doing backup, 02859 restore, etc. Use NtRestoreKey for that. 02860 02861 Arguments: 02862 02863 TargetKey - specifies the path to a key to link the hive to. 02864 path must be of the form "\registry\user<username>" 02865 02866 SourceFile - specifies a file. while file could be remote, 02867 that is strongly discouraged. 02868 02869 Return Value: 02870 02871 NTSTATUS - values TBS. 02872 02873 --*/ 02874 02875 { 02876 return(NtLoadKey2(TargetKey, SourceFile, 0)); 02877 } 02878 02879 02880 NTSTATUS 02881 NtLoadKey2( 02882 IN POBJECT_ATTRIBUTES TargetKey, 02883 IN POBJECT_ATTRIBUTES SourceFile, 02884 IN ULONG Flags 02885 ) 02886 02887 /*++ 02888 02889 Routine Description: 02890 02891 A hive (file in the format created by NtSaveKey) may be linked 02892 into the active registry with this call. UNLIKE NtRestoreKey, 02893 the file specified to NtLoadKey will become the actual backing 02894 store of part of the registry (that is, it will NOT be copied.) 02895 02896 The file may have an associated .log file. 02897 02898 If the hive file is marked as needing a .log file, and one is 02899 not present, the call will fail. 02900 02901 The name specified by SourceFile must be such that ".log" can 02902 be appended to it to generate the name of the log file. Thus, 02903 on FAT file systems, the hive file may not have an extension. 02904 02905 Caller must have SeRestorePrivilege privilege. 02906 02907 This call is used by logon to make the user's profile available 02908 in the registry. It is not intended for use doing backup, 02909 restore, etc. Use NtRestoreKey for that. 02910 02911 Arguments: 02912 02913 TargetKey - specifies the path to a key to link the hive to. 02914 path must be of the form "\registry\user<username>" 02915 02916 SourceFile - specifies a file. while file could be remote, 02917 that is strongly discouraged. 02918 02919 Flags - specifies any flags that should be used for the load operation. 02920 The only valid flag is REG_NO_LAZY_FLUSH. 02921 02922 02923 Return Value: 02924 02925 NTSTATUS - values TBS. 02926 02927 --*/ 02928 02929 { 02930 OBJECT_ATTRIBUTES File; 02931 OBJECT_ATTRIBUTES Key; 02932 KPROCESSOR_MODE PreviousMode; 02933 UNICODE_STRING CapturedKeyName; 02934 UNICODE_STRING FileName; 02935 USHORT Maximum; 02936 NTSTATUS Status; 02937 PWSTR KeyBuffer; 02938 PSECURITY_DESCRIPTOR CapturedDescriptor; 02939 02940 PAGED_CODE(); 02941 02942 BEGIN_LOCK_CHECKPOINT; 02943 02944 CMLOG(CML_API, CMS_NTAPI) KdPrint(("NtLoadKey\n")); 02945 CMLOG(CML_API_ARGS, CMS_NTAPI) { 02946 KdPrint(("\tTargetKey ='%wZ'\n", TargetKey->ObjectName)); 02947 KdPrint(("\tSourceFile='%wZ'\n", SourceFile->ObjectName)); 02948 } 02949 #ifdef LOG_NT_API 02950 if (CmpLogApi) { 02951 KdPrint(("NtLoadKey\n")); 02952 } 02953 #endif 02954 // 02955 // Check for illegal flags 02956 // 02957 if (Flags & ~REG_NO_LAZY_FLUSH) { 02958 return(STATUS_INVALID_PARAMETER); 02959 } 02960 02961 FileName.Buffer = NULL; 02962 KeyBuffer = NULL; 02963 02964 // 02965 // The way we do this is a cronk, but at least it's the same cronk we 02966 // use for all the registry I/O. 02967 // 02968 // The file needs to be opened in the worker thread's context, since 02969 // the resulting handle must be valid when we poke him to go read/write 02970 // from. So we just capture the object attributes for the hive file 02971 // here, then poke the worker thread to go do the rest of the work. 02972 // 02973 02974 PreviousMode = KeGetPreviousMode(); 02975 02976 // 02977 // Check to see if the caller has the privilege to make this call. 02978 // 02979 if (!SeSinglePrivilegeCheck(SeRestorePrivilege, PreviousMode)) { 02980 return(STATUS_PRIVILEGE_NOT_HELD); 02981 } 02982 02983 // 02984 // CmpNameFromAttributes will probe and capture as necessary. 02985 // 02986 KeEnterCriticalRegion(); 02987 Status = CmpNameFromAttributes(SourceFile, 02988 PreviousMode, 02989 &FileName); 02990 if (!NT_SUCCESS(Status)) { 02991 KeLeaveCriticalRegion(); 02992 return(Status); 02993 } 02994 02995 try { 02996 02997 // 02998 // Probe the object attributes if necessary. 02999 // 03000 if (PreviousMode == UserMode) { 03001 ProbeForRead(TargetKey, 03002 sizeof(OBJECT_ATTRIBUTES), 03003 PROBE_ALIGNMENT(OBJECT_ATTRIBUTES) ); 03004 } 03005 03006 // 03007 // Capture the object attributes. 03008 // 03009 Key = *TargetKey; 03010 03011 // 03012 // Capture the object name. 03013 // 03014 03015 if (PreviousMode == UserMode) { 03016 CapturedKeyName = ProbeAndReadUnicodeString(Key.ObjectName); 03017 ProbeForRead(CapturedKeyName.Buffer, 03018 CapturedKeyName.Length, 03019 sizeof(WCHAR)); 03020 } else { 03021 CapturedKeyName = *(TargetKey->ObjectName); 03022 } 03023 03024 File.ObjectName = &FileName; 03025 File.SecurityDescriptor = SourceFile->SecurityDescriptor; 03026 03027 Maximum = (USHORT)(CapturedKeyName.Length); 03028 03029 KeyBuffer = ALLOCATE_WITH_QUOTA(PagedPool, Maximum, CM_POOL_TAG); 03030 03031 if (KeyBuffer == NULL) { 03032 ExFreePool(FileName.Buffer); 03033 KeLeaveCriticalRegion(); 03034 return(STATUS_INSUFFICIENT_RESOURCES); 03035 } 03036 03037 RtlMoveMemory(KeyBuffer, CapturedKeyName.Buffer, Maximum); 03038 CapturedKeyName.Length = Maximum; 03039 CapturedKeyName.Buffer = KeyBuffer; 03040 03041 Key.ObjectName = &CapturedKeyName; 03042 Key.SecurityDescriptor = NULL; 03043 03044 // 03045 // Capture the security descriptor. 03046 // 03047 Status = SeCaptureSecurityDescriptor(File.SecurityDescriptor, 03048 PreviousMode, 03049 PagedPool, 03050 TRUE, 03051 &CapturedDescriptor); 03052 File.SecurityDescriptor = CapturedDescriptor; 03053 03054 } except (EXCEPTION_EXECUTE_HANDLER) { 03055 CMLOG(CML_API, CMS_EXCEPTION) { 03056 KdPrint(("!!NtLoadKey: code:%08lx\n", GetExceptionCode())); 03057 } 03058 Status = GetExceptionCode(); 03059 03060 } 03061 03062 // 03063 // Clean up if there was an exception or SeCaptureSD failed. 03064 // 03065 if (!NT_SUCCESS(Status)) { 03066 if (FileName.Buffer != NULL) { 03067 ExFreePool(FileName.Buffer); 03068 } 03069 if (KeyBuffer != NULL) { 03070 ExFreePool(KeyBuffer); 03071 } 03072 KeLeaveCriticalRegion(); 03073 return(Status); 03074 } 03075 03076 Status = CmLoadKey(&Key, &File, Flags); 03077 03078 ExFreePool(FileName.Buffer); 03079 ExFreePool(KeyBuffer); 03080 if (CapturedDescriptor != NULL) { 03081 ExFreePool(CapturedDescriptor); 03082 } 03083 03084 KeLeaveCriticalRegion(); 03085 03086 END_LOCK_CHECKPOINT; 03087 03088 return(Status); 03089 } 03090 03091 03092 NTSTATUS 03093 NtUnloadKey( 03094 IN POBJECT_ATTRIBUTES TargetKey 03095 ) 03096 /*++ 03097 03098 Routine Description: 03099 03100 Drop a subtree (hive) out of the registry. 03101 03102 Will fail if applied to anything other than the root of a hive. 03103 03104 Cannot be applied to core system hives (HARDWARE, SYSTEM, etc.) 03105 03106 Can be applied to user hives loaded via NtRestoreKey or NtLoadKey. 03107 03108 If there are handles open to the hive being dropped, this call 03109 will fail. Terminate relevent processes so that handles are 03110 closed. 03111 03112 This call will flush the hive being dropped. 03113 03114 Caller must have SeRestorePrivilege privilege. 03115 03116 Arguments: 03117 03118 TargetKey - specifies the path to a key to link the hive to. 03119 path must be of the form "\registry\user<username>" 03120 03121 Return Value: 03122 03123 NTSTATUS - values TBS. 03124 03125 --*/ 03126 { 03127 HANDLE KeyHandle; 03128 NTSTATUS Status; 03129 PCM_KEY_BODY KeyBody; 03130 PHHIVE Hive; 03131 HCELL_INDEX Cell; 03132 KPROCESSOR_MODE PreviousMode; 03133 CM_PARSE_CONTEXT ParseContext; 03134 03135 PAGED_CODE(); 03136 03137 BEGIN_LOCK_CHECKPOINT; 03138 03139 CMLOG(CML_API, CMS_NTAPI) KdPrint(("NtUnloadKey\n")); 03140 CMLOG(CML_API_ARGS, CMS_NTAPI) { 03141 KdPrint(("\tTargetKey ='%wZ'\n", TargetKey->ObjectName)); 03142 } 03143 #ifdef LOG_NT_API 03144 if (CmpLogApi) { 03145 KdPrint(("NtUnloadKey\n")); 03146 } 03147 #endif 03148 03149 PreviousMode = KeGetPreviousMode(); 03150 03151 if (!SeSinglePrivilegeCheck(SeRestorePrivilege, PreviousMode)) { 03152 return(STATUS_PRIVILEGE_NOT_HELD); 03153 } 03154 03155 CmpLockRegistryExclusive(); 03156 03157 try { 03158 03159 ParseContext.TitleIndex = 0; 03160 ParseContext.Class.Length = 0; 03161 ParseContext.Class.Buffer = NULL; 03162 ParseContext.CreateOptions = REG_OPTION_BACKUP_RESTORE; 03163 ParseContext.Disposition = 0L; 03164 ParseContext.CreateLink = FALSE; 03165 ParseContext.PredefinedHandle = NULL; 03166 03167 Status = ObOpenObjectByName(TargetKey, 03168 CmpKeyObjectType, 03169 PreviousMode, 03170 NULL, 03171 KEY_WRITE, 03172 &ParseContext, 03173 &KeyHandle); 03174 if (NT_SUCCESS(Status)) { 03175 Status = ObReferenceObjectByHandle(KeyHandle, 03176 KEY_WRITE, 03177 CmpKeyObjectType, 03178 PreviousMode, 03179 (PVOID *)&KeyBody, 03180 NULL); 03181 NtClose(KeyHandle); 03182 } 03183 03184 } except (EXCEPTION_EXECUTE_HANDLER) { 03185 Status = GetExceptionCode(); 03186 CMLOG(CML_API, CMS_EXCEPTION) { 03187 KdPrint(("!!NtUnloadKey: code:%08lx\n", Status)); 03188 } 03189 } 03190 03191 if (NT_SUCCESS(Status)) { 03192 Hive = KeyBody->KeyControlBlock->KeyHive; 03193 Cell = KeyBody->KeyControlBlock->KeyCell; 03194 03195 // 03196 // Report the notify here, because the KCB won't be around later. 03197 // 03198 03199 CmpReportNotify(KeyBody->KeyControlBlock, 03200 Hive, 03201 Cell, 03202 REG_NOTIFY_CHANGE_LAST_SET); 03203 03204 03205 // 03206 // post any waiting notifies 03207 // 03208 CmpFlushNotify(KeyBody); 03209 03210 Status = CmUnloadKey(Hive, Cell, KeyBody->KeyControlBlock); 03211 if (NT_SUCCESS(Status)) { 03212 // 03213 // Mark this kcb as deleted so that it won't get put on the delayed close list. 03214 // 03215 KeyBody->KeyControlBlock->Delete = TRUE; 03216 // 03217 // If the parent has the subkey info or hint cached, free it. 03218 // 03219 ASSERT_CM_LOCK_OWNED_EXCLUSIVE(); 03220 CmpCleanUpSubKeyInfo(KeyBody->KeyControlBlock->ParentKcb); 03221 CmpRemoveKeyControlBlock(KeyBody->KeyControlBlock); 03222 } 03223 03224 ObDereferenceObject((PVOID)KeyBody); 03225 } 03226 03227 CmpUnlockRegistry(); 03228 03229 END_LOCK_CHECKPOINT; 03230 03231 return(Status); 03232 } 03233 03234 NTSTATUS 03235 NtSetInformationKey( 03236 IN HANDLE KeyHandle, 03237 IN KEY_SET_INFORMATION_CLASS KeySetInformationClass, 03238 IN PVOID KeySetInformation, 03239 IN ULONG KeySetInformationLength 03240 ) 03241 { 03242 NTSTATUS status; 03243 PCM_KEY_BODY KeyBody; 03244 KPROCESSOR_MODE mode; 03245 LARGE_INTEGER LocalWriteTime; 03246 03247 // Start registry call tracing 03248 StartWmiCmTrace(); 03249 03250 PAGED_CODE(); 03251 03252 BEGIN_LOCK_CHECKPOINT; 03253 03254 CMLOG(CML_API, CMS_NTAPI) KdPrint(("NtSetInformationKey\n")); 03255 CMLOG(CML_API_ARGS, CMS_NTAPI) { 03256 KdPrint(("\tKeyHandle=%08lx\n", KeyHandle)); 03257 KdPrint(("\tInfoClass=%08x\n", KeySetInformationClass)); 03258 } 03259 #ifdef LOG_NT_API 03260 if (CmpLogApi) { 03261 KdPrint(("NtSetInformationKey %d %d\n",KeyHandle,KeySetInformationClass)); 03262 } 03263 #endif 03264 03265 switch (KeySetInformationClass) { 03266 case KeyWriteTimeInformation: 03267 if (KeySetInformationLength != sizeof( KEY_WRITE_TIME_INFORMATION )) { 03268 03269 // End registry call tracing 03270 EndWmiCmTrace(STATUS_INFO_LENGTH_MISMATCH,KeyHandle,NULL,EVENT_TRACE_TYPE_REGSETINFORMATION); 03271 03272 return STATUS_INFO_LENGTH_MISMATCH; 03273 } 03274 break; 03275 03276 default: 03277 03278 // End registry call tracing 03279 EndWmiCmTrace(STATUS_INVALID_INFO_CLASS,KeyHandle,NULL,EVENT_TRACE_TYPE_REGSETINFORMATION); 03280 03281 return STATUS_INVALID_INFO_CLASS; 03282 } 03283 03284 mode = KeGetPreviousMode(); 03285 03286 status = ObReferenceObjectByHandle( 03287 KeyHandle, 03288 KEY_SET_VALUE, 03289 CmpKeyObjectType, 03290 mode, 03291 (PVOID *)(&KeyBody), 03292 NULL 03293 ); 03294 03295 if (NT_SUCCESS(status)) { 03296 03297 try { 03298 03299 if (mode == UserMode) { 03300 LocalWriteTime = ProbeAndReadLargeInteger( 03301 (PLARGE_INTEGER) KeySetInformation ); 03302 } else { 03303 LocalWriteTime = *(PLARGE_INTEGER)KeySetInformation; 03304 } 03305 03306 } except (EXCEPTION_EXECUTE_HANDLER) { 03307 CMLOG(CML_API, CMS_EXCEPTION) { 03308 KdPrint(("!!NtSetInformationKey: code:%08lx\n", GetExceptionCode())); 03309 } 03310 status = GetExceptionCode(); 03311 } 03312 03313 if( NT_SUCCESS(status)) { 03314 // 03315 // not in try ... except! we want bugcheck here if something wrong in the registry 03316 // 03317 status = CmSetLastWriteTimeKey( 03318 KeyBody->KeyControlBlock, 03319 &LocalWriteTime 03320 ); 03321 } 03322 03323 ObDereferenceObject((PVOID)KeyBody); 03324 } 03325 03326 END_LOCK_CHECKPOINT; 03327 03328 // End registry call tracing 03329 EndWmiCmTrace(status,KeyHandle,NULL,EVENT_TRACE_TYPE_REGSETINFORMATION); 03330 03331 return status; 03332 } 03333 03334 03335 NTSTATUS 03336 NtReplaceKey( 03337 IN POBJECT_ATTRIBUTES NewFile, 03338 IN HANDLE TargetHandle, 03339 IN POBJECT_ATTRIBUTES OldFile 03340 ) 03341 /*++ 03342 03343 Routine Description: 03344 03345 A hive file may be "replaced" under a running system, such 03346 that the new file will be the one actually used at next 03347 boot, with this call. 03348 03349 This routine will: 03350 03351 Open newfile, and verify that it is a valid Hive file. 03352 03353 Rename the Hive file backing TargetHandle to OldFile. 03354 All handles will remain open, and the system will continue 03355 to use the file until rebooted. 03356 03357 Rename newfile to match the name of the hive file 03358 backing TargetHandle. 03359 03360 .log and .alt files are ignored 03361 03362 The system must be rebooted for any useful effect to be seen. 03363 03364 Caller must have SeRestorePrivilege. 03365 03366 Arguments: 03367 03368 NewFile - specifies the new file to use. must not be just 03369 a handle, since NtReplaceKey will insist on 03370 opening the file for exclusive access (which it 03371 will hold until the system is rebooted.) 03372 03373 TargetHandle - handle to a registry hive root 03374 03375 OldFile - name of file to apply to current hive, which will 03376 become old hive 03377 03378 Return Value: 03379 03380 NTSTATUS - values TBS. 03381 03382 --*/ 03383 { 03384 KPROCESSOR_MODE PreviousMode; 03385 UNICODE_STRING NewHiveName; 03386 UNICODE_STRING OldFileName; 03387 NTSTATUS Status; 03388 PCM_KEY_BODY KeyBody; 03389 03390 PAGED_CODE(); 03391 03392 BEGIN_LOCK_CHECKPOINT; 03393 03394 CMLOG(CML_API, CMS_NTAPI) KdPrint(("NtReplaceKey\n")); 03395 CMLOG(CML_API_ARGS, CMS_NTAPI) { 03396 KdPrint(("\tNewFile ='%wZ'\n", NewFile->ObjectName)); 03397 KdPrint(("\tOldFile ='%wZ'\n", OldFile->ObjectName)); 03398 } 03399 #ifdef LOG_NT_API 03400 if (CmpLogApi) { 03401 KdPrint(("NtReplaceKey\n")); 03402 } 03403 #endif 03404 03405 PreviousMode = KeGetPreviousMode(); 03406 03407 // 03408 // Check to see if the caller has the privilege to make this call. 03409 // 03410 if (!SeSinglePrivilegeCheck(SeRestorePrivilege, PreviousMode)) { 03411 return(STATUS_PRIVILEGE_NOT_HELD); 03412 } 03413 03414 KeEnterCriticalRegion(); 03415 Status = CmpNameFromAttributes(NewFile, 03416 PreviousMode, 03417 &NewHiveName); 03418 if (!NT_SUCCESS(Status)) { 03419 KeLeaveCriticalRegion(); 03420 return(Status); 03421 } 03422 03423 Status = CmpNameFromAttributes(OldFile, 03424 PreviousMode, 03425 &OldFileName); 03426 if (!NT_SUCCESS(Status)) { 03427 ExFreePool(NewHiveName.Buffer); 03428 KeLeaveCriticalRegion(); 03429 return(Status); 03430 } 03431 03432 Status = ObReferenceObjectByHandle(TargetHandle, 03433 0, 03434 CmpKeyObjectType, 03435 PreviousMode, 03436 (PVOID *)&KeyBody, 03437 NULL); 03438 if (NT_SUCCESS(Status)) { 03439 03440 Status = CmReplaceKey(KeyBody->KeyControlBlock->KeyHive, 03441 KeyBody->KeyControlBlock->KeyCell, 03442 &NewHiveName, 03443 &OldFileName); 03444 03445 ObDereferenceObject((PVOID)KeyBody); 03446 } 03447 03448 ExFreePool(OldFileName.Buffer); 03449 ExFreePool(NewHiveName.Buffer); 03450 KeLeaveCriticalRegion(); 03451 03452 END_LOCK_CHECKPOINT; 03453 03454 return(Status); 03455 } 03456 03457 03458 NTSYSAPI 03459 NTSTATUS 03460 NTAPI 03461 NtQueryMultipleValueKey( 03462 IN HANDLE KeyHandle, 03463 IN PKEY_VALUE_ENTRY ValueEntries, 03464 IN ULONG EntryCount, 03465 OUT PVOID ValueBuffer, 03466 IN OUT PULONG BufferLength, 03467 OUT OPTIONAL PULONG RequiredBufferLength 03468 ) 03469 /*++ 03470 03471 Routine Description: 03472 03473 Multiple values of any key may be queried atomically with 03474 this api. 03475 03476 Arguments: 03477 03478 KeyHandle - Supplies the key to be queried. 03479 03480 ValueNames - Supplies an array of value names to be queried 03481 03482 ValueEntries - Returns an array of KEY_VALUE_ENTRY structures, one for each value. 03483 03484 EntryCount - Supplies the number of entries in the ValueNames and ValueEntries arrays 03485 03486 ValueBuffer - Returns the value data for each value. 03487 03488 BufferLength - Supplies the length of the ValueBuffer array in bytes. 03489 Returns the length of the ValueBuffer array that was filled in. 03490 03491 RequiredBufferLength - if present, Returns the length in bytes of the ValueBuffer 03492 array required to return all the values of this key. 03493 03494 Return Value: 03495 03496 NTSTATUS 03497 03498 --*/ 03499 03500 { 03501 KPROCESSOR_MODE PreviousMode; 03502 NTSTATUS Status; 03503 PCM_KEY_BODY KeyBody; 03504 ULONG i; 03505 ULONG LocalBufferLength; 03506 03507 // Start registry call tracing 03508 StartWmiCmTrace(); 03509 03510 PAGED_CODE(); 03511 03512 BEGIN_LOCK_CHECKPOINT; 03513 03514 CMLOG(CML_API, CMS_NTAPI) KdPrint(("NtQueryMultipleValueKey\n")); 03515 CMLOG(CML_API_ARGS, CMS_NTAPI) { 03516 KdPrint(("\tKeyHandle=%08lx\n", KeyHandle)); 03517 } 03518 #ifdef LOG_NT_API 03519 if (CmpLogApi) { 03520 KdPrint(("NtQueryMultipleValueKey\n")); 03521 } 03522 #endif 03523 03524 PreviousMode = KeGetPreviousMode(); 03525 Status = ObReferenceObjectByHandle(KeyHandle, 03526 KEY_QUERY_VALUE, 03527 CmpKeyObjectType, 03528 PreviousMode, 03529 (PVOID *)(&KeyBody), 03530 NULL); 03531 if (NT_SUCCESS(Status)) { 03532 try { 03533 if (PreviousMode == UserMode) { 03534 LocalBufferLength = ProbeAndReadUlong(BufferLength); 03535 03536 // 03537 // Probe the output buffers 03538 // 03539 // Put an arbitrary 64K limit on the number of entries to 03540 // prevent bogus apps from passing an EntryCount large enough 03541 // to overflow the EntryCount * sizeof(KEY_VALUE_ENTRY) calculation. 03542 // 03543 if (EntryCount > 0x10000) { 03544 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES); 03545 } 03546 ProbeForWrite(ValueEntries, 03547 EntryCount * sizeof(KEY_VALUE_ENTRY), 03548 sizeof(ULONG)); 03549 if (ARGUMENT_PRESENT(RequiredBufferLength)) { 03550 ProbeForWriteUlong(RequiredBufferLength); 03551 } 03552 03553 ProbeForWrite(ValueBuffer, 03554 LocalBufferLength, 03555 sizeof(ULONG)); 03556 03557 } else { 03558 LocalBufferLength = *BufferLength; 03559 } 03560 03561 } except(EXCEPTION_EXECUTE_HANDLER) { 03562 CMLOG(CML_API, CMS_EXCEPTION) { 03563 KdPrint(("!!NtQueryMultipleValueKey: code:%08lx\n",GetExceptionCode())); 03564 } 03565 Status = GetExceptionCode(); 03566 } 03567 03568 if( NT_SUCCESS(Status)) { 03569 // 03570 // CmQueryMultipleValueKey is protected to user mode buffer exceptions 03571 // all other exceptions are cm internals and should result in a bugcheck 03572 // 03573 Status = CmQueryMultipleValueKey(KeyBody->KeyControlBlock, 03574 ValueEntries, 03575 EntryCount, 03576 ValueBuffer, 03577 &LocalBufferLength, 03578 RequiredBufferLength); 03579 try { 03580 // anybody messed with BufferLength in between? 03581 *BufferLength = LocalBufferLength; 03582 } except(EXCEPTION_EXECUTE_HANDLER) { 03583 CMLOG(CML_API, CMS_EXCEPTION) { 03584 KdPrint(("!!NtQueryMultipleValueKey: code:%08lx\n",GetExceptionCode())); 03585 } 03586 Status = GetExceptionCode(); 03587 } 03588 } 03589 ObDereferenceObject((PVOID)KeyBody); 03590 } 03591 03592 END_LOCK_CHECKPOINT; 03593 03594 // End registry call tracing 03595 EndWmiCmTrace(Status,KeyHandle,NULL,EVENT_TRACE_TYPE_REGQUERYMULTIPLEVALUE); 03596 03597 return(Status); 03598 03599 } 03600 03601 03602 NTSTATUS 03603 CmpNameFromAttributes( 03604 IN POBJECT_ATTRIBUTES Attributes, 03605 KPROCESSOR_MODE PreviousMode, 03606 OUT PUNICODE_STRING FullName 03607 ) 03608 03609 /*++ 03610 03611 Routine Description: 03612 03613 This is a helper routine that converts OBJECT_ATTRIBUTES into a 03614 full object pathname. This is needed because we cannot pass handles 03615 to the worker thread, since it runs in a different process. 03616 03617 This routine will also probe and capture the attributes based on 03618 PreviousMode. 03619 03620 Storage for the string buffer is allocated from paged pool, and should 03621 be freed by the caller. 03622 03623 Arguments: 03624 03625 Attributes - Supplies the object attributes to be converted to a pathname 03626 03627 PreviousMode - Supplies the previous mode. 03628 03629 Name - Returns the object pathname. 03630 03631 Return Value: 03632 03633 NTSTATUS 03634 03635 --*/ 03636 03637 { 03638 OBJECT_ATTRIBUTES CapturedAttributes; 03639 UNICODE_STRING FileName; 03640 UNICODE_STRING RootName; 03641 NTSTATUS Status; 03642 ULONG ObjectNameLength; 03643 UCHAR ObjectNameInfo[512]; 03644 POBJECT_NAME_INFORMATION ObjectName; 03645 PWSTR End; 03646 PUNICODE_STRING CapturedObjectName; 03647 ULONG Length; 03648 03649 PAGED_CODE(); 03650 FullName->Buffer = NULL; // so we know whether to free it in our exception handler 03651 try { 03652 03653 // 03654 // Probe the object attributes if necessary. 03655 // 03656 if (PreviousMode == UserMode) { 03657 ProbeForRead(Attributes, 03658 sizeof(OBJECT_ATTRIBUTES), 03659 PROBE_ALIGNMENT(OBJECT_ATTRIBUTES) ); 03660 CapturedObjectName = Attributes->ObjectName; 03661 FileName = ProbeAndReadUnicodeString(CapturedObjectName); 03662 ProbeForRead(FileName.Buffer, 03663 FileName.Length, 03664 sizeof(WCHAR)); 03665 } else { 03666 FileName = *(Attributes->ObjectName); 03667 } 03668 03669 CapturedAttributes = *Attributes; 03670 03671 if (CapturedAttributes.RootDirectory != NULL) { 03672 03673 if ((FileName.Buffer != NULL) && 03674 (FileName.Length >= sizeof(WCHAR)) && 03675 (*(FileName.Buffer) == OBJ_NAME_PATH_SEPARATOR)) { 03676 return(STATUS_OBJECT_PATH_SYNTAX_BAD); 03677 } 03678 03679 // 03680 // Find the name of the root directory and append the 03681 // name of the relative object to it. 03682 // 03683 03684 Status = ZwQueryObject(CapturedAttributes.RootDirectory, 03685 ObjectNameInformation, 03686 &ObjectNameInfo, 03687 sizeof(ObjectNameInfo), 03688 &ObjectNameLength); 03689 03690 ObjectName = (POBJECT_NAME_INFORMATION)ObjectNameInfo; 03691 if (!NT_SUCCESS(Status)) { 03692 return(Status); 03693 } 03694 RootName = ObjectName->Name; 03695 03696 FullName->Length = 0; 03697 Length = RootName.Length+FileName.Length+sizeof(WCHAR); 03698 // 03699 // Overflow test: If Length overflows the USHRT_MAX value 03700 // cleanup and return STATUS_OBJECT_PATH_INVALID 03701 // 03702 if( Length>0xFFFF ) { 03703 return STATUS_OBJECT_PATH_INVALID; 03704 } 03705 03706 FullName->MaximumLength = (USHORT)Length; 03707 03708 FullName->Buffer = ALLOCATE_WITH_QUOTA(PagedPool, FullName->MaximumLength, CM_POOL_TAG); 03709 if (FullName->Buffer == NULL) { 03710 return STATUS_INSUFFICIENT_RESOURCES; 03711 } 03712 03713 Status = RtlAppendUnicodeStringToString(FullName, &RootName); 03714 ASSERT(NT_SUCCESS(Status)); 03715 03716 // 03717 // Append a trailing separator if necessary. 03718 // 03719 if( FullName->Length != 0 ) { 03720 End = (PWSTR)((PUCHAR)FullName->Buffer + FullName->Length) - 1; 03721 if (*End != OBJ_NAME_PATH_SEPARATOR) { 03722 ++End; 03723 *End = OBJ_NAME_PATH_SEPARATOR; 03724 FullName->Length += sizeof(WCHAR); 03725 } 03726 } 03727 03728 Status = RtlAppendUnicodeStringToString(FullName, &FileName); 03729 ASSERT(NT_SUCCESS(Status)); 03730 03731 } else { 03732 03733 // 03734 // RootDirectory is NULL, so just use the name. 03735 // 03736 FullName->Length = FileName.Length; 03737 FullName->MaximumLength = FileName.Length; 03738 FullName->Buffer = ALLOCATE_WITH_QUOTA(PagedPool, FileName.Length, CM_POOL_TAG); 03739 if (FullName->Buffer == NULL) { 03740 Status = STATUS_INSUFFICIENT_RESOURCES; 03741 } else { 03742 RtlMoveMemory(FullName->Buffer, 03743 FileName.Buffer, 03744 FileName.Length); 03745 Status = STATUS_SUCCESS; 03746 } 03747 } 03748 03749 03750 } except (EXCEPTION_EXECUTE_HANDLER) { 03751 Status = GetExceptionCode(); 03752 CMLOG(CML_API, CMS_EXCEPTION) { 03753 KdPrint(("!!CmpNameFromAttributes: code %08lx\n", Status)); 03754 } 03755 if (FullName->Buffer != NULL) { 03756 ExFreePool(FullName->Buffer); 03757 } 03758 } 03759 03760 return(Status); 03761 } 03762 03763 VOID 03764 CmpFreePostBlock( 03765 IN PCM_POST_BLOCK PostBlock 03766 ) 03767 03768 /*++ 03769 03770 Routine Description: 03771 03772 Frees the various bits of pool that were allocated for a postblock 03773 03774 Arguments: 03775 03776 None 03777 03778 Return Value: 03779 03780 None. 03781 03782 --*/ 03783 03784 { 03785 03786 CMLOG(CML_API, CMS_NTAPI) { 03787 #if DBG 03788 if(PostBlock->TraceIntoDebugger) { 03789 KdPrint(("[CM]CmpFreePostBlock: PostBlock:%08lx\t", PostBlock)); 03790 if( PostBlock->NotifyType&REG_NOTIFY_MASTER_POST) { 03791 KdPrint(("--MasterBlock\n")); 03792 } else { 03793 KdPrint(("--SlaveBlock\n")); 03794 } 03795 } 03796 #endif 03797 } 03798 03799 #ifdef _CM_ENTRYLIST_MANIPULATION 03800 // check if the post block has been removed from the notify and thread list(s) 03801 if((PostBlock->NotifyList.Flink != NULL) || (PostBlock->NotifyList.Blink != NULL)) { 03802 DbgPrint("CmpFreePostBlock: Attempt to free post block %08lx not removed from notify list\n",PostBlock); 03803 DbgBreakPoint(); 03804 } 03805 if((PostBlock->ThreadList.Flink != NULL) || (PostBlock->ThreadList.Blink != NULL)) { 03806 DbgPrint("CmpFreePostBlock: Attempt to free post block %08lx not removed from thread list\n",PostBlock); 03807 DbgBreakPoint(); 03808 } 03809 03810 #endif 03811 03812 // Protect for multiple deletion of the same object 03813 CmpClearListEntry(&(PostBlock->CancelPostList)); 03814 03815 // 03816 // Cleanup for objects referenced by NtNotifyMultipleKeys 03817 // 03818 if( PostBlock->PostKeyBody) { 03819 03820 // 03821 // If we have a PostKeyBody, the attached key body must not be NULL 03822 // 03823 ASSERT(PostBlock->PostKeyBody->KeyBody); 03824 03825 CMLOG(CML_MAJOR, CMS_NOTIFY) { 03826 #if DBG 03827 WCHAR *NameBuffer = NULL; 03828 UNICODE_STRING KeyName; 03829 03830 KdPrint(("[CM]\tCmpFreePostBlock: Dereferencing KeyBody:%08lx\n", PostBlock->PostKeyBody->KeyBody)); 03831 NameBuffer = ExAllocatePool(PagedPool, MAX_KEY_NAME_LENGTH); 03832 if(NameBuffer) { 03833 CmpInitializeKeyNameString(PostBlock->PostKeyBody->KeyBody->KeyControlBlock->KeyNode,&KeyName,NameBuffer); 03834 KdPrint(("[CM]\tCmpFreePostBlock: KeyName:tKey = %.*S\n",KeyName.Length / sizeof(WCHAR),KeyName.Buffer)); 03835 ExFreePool(NameBuffer); 03836 } 03837 #endif 03838 } 03839 03840 // 03841 // KeyBodyList must be used only in CmpPostBlock implementation for the delayed dereferencing mechanism. 03842 // 03843 ASSERT(IsListEmpty(&(PostBlock->PostKeyBody->KeyBodyList))); 03844 03845 // 03846 // dereference the actual keybody 03847 // 03848 ObDereferenceObject(PostBlock->PostKeyBody->KeyBody); 03849 03850 // 03851 // Free the PostKeyBody structure 03852 // 03853 ExFreePool(PostBlock->PostKeyBody); 03854 } 03855 03856 if( IsMasterPostBlock(PostBlock) ) { 03857 // 03858 // this members are allocated only for master post blocks 03859 // 03860 switch (PostBlockType(PostBlock)) { 03861 case PostSynchronous: 03862 ExFreePool(PostBlock->u->Sync.SystemEvent); 03863 break; 03864 case PostAsyncUser: 03865 ExFreePool(PostBlock->u->AsyncUser.Apc); 03866 break; 03867 case PostAsyncKernel: 03868 break; 03869 } 03870 ExFreePool(PostBlock->u); 03871 } 03872 03873 #ifdef _CM_ENTRYLIST_MANIPULATION 03874 RtlZeroMemory((PVOID)PostBlock, sizeof(CM_POST_BLOCK)); 03875 #endif 03876 03877 // and the storage for the Post object 03878 ExFreePool(PostBlock); 03879 } 03880 03881 03882 PCM_POST_BLOCK 03883 CmpAllocatePostBlock( 03884 IN POST_BLOCK_TYPE BlockType, 03885 IN ULONG PostFlags, 03886 IN PCM_KEY_BODY KeyBody, 03887 IN PCM_POST_BLOCK MasterBlock 03888 ) 03889 03890 /*++ 03891 03892 Routine Description: 03893 03894 Allocates a post block from pool. The non-pagable stuff comes from 03895 NonPagedPool, the pagable stuff from paged pool. Quota will be 03896 charged. 03897 03898 Arguments: 03899 03900 BlockType - specifies the type of the post block to be allocated 03901 i.e. : PostSyncrhronous, PostAsyncUser, PostAsyncKernel 03902 03903 PostFlags - specifies the flags to be set on the allocated post block 03904 vallid flags: 03905 - REG_NOTIFY_MASTER_POST - the post block to be allocated 03906 is a master post block. 03907 KeyBody - The Key object to whom this post block is attached. On master blocks 03908 this is NULL. When the post object is freed, the KeyBody object is 03909 dereferenced (if not NULL - i.e. for slave blocks). This allow us to 03910 perform back-end cleanup for "fake-slave" keys opened by NtNotifyMultipleKeys 03911 MasterBlock - the post block to be allocated is a slave of this master block. 03912 valid only when PostFlags == REG_NOTIFY_MASTER_POST 03913 03914 03915 Obs: The Sync.SystemEvent and AsyncUser.Apc members are allocated only for master post blocks 03916 03917 Return Value: 03918 03919 Pointer to the CM_POST_BLOCK if successful 03920 03921 NULL if there were not enough resources available. 03922 03923 --*/ 03924 03925 { 03926 PCM_POST_BLOCK PostBlock; 03927 03928 // protection against outrageous calls 03929 ASSERT( !PostFlags || (!MasterBlock && !KeyBody) ); 03930 03931 PostBlock = ALLOCATE_WITH_QUOTA(PagedPool, sizeof(CM_POST_BLOCK),CM_POSTBLOCK_TAG); 03932 if (PostBlock==NULL) { 03933 return(NULL); 03934 } 03935 03936 #ifdef _CM_ENTRYLIST_MANIPULATION 03937 RtlZeroMemory((PVOID)PostBlock, sizeof(CM_POST_BLOCK)); 03938 #endif 03939 03940 #if DBG 03941 PostBlock->TraceIntoDebugger = FALSE; 03942 #endif 03943 03944 PostBlock->NotifyType = (ULONG)BlockType; 03945 PostBlock->NotifyType |= PostFlags; 03946 03947 if(IsMasterPostBlock(PostBlock)) { 03948 PostBlock->PostKeyBody = NULL; 03949 // 03950 // master post block ==> allocate the storage 03951 // 03952 PostBlock->u = ALLOCATE_WITH_QUOTA(NonPagedPool, 03953 sizeof(CM_POST_BLOCK_UNION), 03954 CM_POSTBLOCK_TAG); 03955 if (PostBlock->u == NULL) { 03956 ExFreePool(PostBlock); 03957 return(NULL); 03958 } 03959 03960 switch (BlockType) { 03961 case PostSynchronous: 03962 PostBlock->u->Sync.SystemEvent = ALLOCATE_WITH_QUOTA(NonPagedPool, 03963 sizeof(KEVENT), 03964 CM_POSTEVENT_TAG); 03965 if (PostBlock->u->Sync.SystemEvent == NULL) { 03966 ExFreePool(PostBlock->u); 03967 ExFreePool(PostBlock); 03968 return(NULL); 03969 } 03970 KeInitializeEvent(PostBlock->u->Sync.SystemEvent, 03971 SynchronizationEvent, 03972 FALSE); 03973 break; 03974 case PostAsyncUser: 03975 PostBlock->u->AsyncUser.Apc = ALLOCATE_WITH_QUOTA(NonPagedPool, 03976 sizeof(KAPC), 03977 CM_POSTAPC_TAG); 03978 if (PostBlock->u->AsyncUser.Apc==NULL) { 03979 ExFreePool(PostBlock->u); 03980 ExFreePool(PostBlock); 03981 return(NULL); 03982 } 03983 break; 03984 case PostAsyncKernel: 03985 RtlZeroMemory(&PostBlock->u->AsyncKernel, sizeof(CM_ASYNC_KERNEL_POST_BLOCK)); 03986 break; 03987 } 03988 } else { 03989 // 03990 // Slave post block ==> copy storage allocated for the master post block 03991 // 03992 PostBlock->u = MasterBlock->u; 03993 03994 // 03995 // allocate a PostKeyBody which will hold this KeyBody, and initialize the head of its KeyBodyList 03996 // 03997 PostBlock->PostKeyBody = ALLOCATE_WITH_QUOTA(PagedPool, sizeof(CM_POST_KEY_BODY),CM_POSTBLOCK_TAG); 03998 if (PostBlock->PostKeyBody == NULL) { 03999 ExFreePool(PostBlock); 04000 return(NULL); 04001 } 04002 PostBlock->PostKeyBody->KeyBody = KeyBody; 04003 InitializeListHead(&(PostBlock->PostKeyBody->KeyBodyList)); 04004 } 04005 04006 return(PostBlock); 04007 } 04008 04009 #if DBG 04010 04011 LOGICAL CmpExceptionBreak = FALSE; 04012 04013 04014 ULONG 04015 CmpExceptionFilter( 04016 IN PEXCEPTION_POINTERS ExceptionPointers 04017 ) 04018 04019 /*++ 04020 04021 Routine Description: 04022 04023 Debug code to find registry exceptions that are being swallowed 04024 04025 Return Value: 04026 04027 EXCEPTION_EXECUTE_HANDLER 04028 04029 --*/ 04030 04031 { 04032 KdPrint(("CM: Registry exception %lx, ExceptionPointers = %p\n", 04033 ExceptionPointers->ExceptionRecord->ExceptionCode, 04034 ExceptionPointers)); 04035 04036 if (CmpExceptionBreak == TRUE) { 04037 04038 try { 04039 DbgBreakPoint(); 04040 } except (EXCEPTION_EXECUTE_HANDLER) { 04041 04042 // 04043 // no debugger enabled, just keep going 04044 // 04045 04046 } 04047 } 04048 04049 return(EXCEPTION_EXECUTE_HANDLER); 04050 } 04051 04052 #endif 04053 04054 ULONG CmpOpenSubKeys; 04055 04056 #ifndef KCB_TO_KEYBODY_LINK 04057 04058 BOOLEAN 04059 CmpEnumKeyObjectCallback( 04060 IN PVOID Object, 04061 IN PUNICODE_STRING ObjectName, 04062 IN ULONG HandleCount, 04063 IN ULONG PointerCount, 04064 IN PVOID Context 04065 ) 04066 { 04067 PCM_KEY_BODY KeyBody; 04068 PHHIVE Hive; 04069 04070 KeyBody = (PCM_KEY_BODY)Object; 04071 Hive = (PHHIVE)Context; 04072 04073 if( KeyBody->KeyControlBlock->KeyHive == Hive ) { 04074 // 04075 // that's and open subkey inside of the hive 04076 // 04077 DbgPrint("Key %wZ (HandleCount = %lu PointerCount = %lu) is opened by process %lx\n", 04078 ObjectName,HandleCount,PointerCount,KeyBody->Process); 04079 04080 // count it 04081 CmpOpenSubKeys++; 04082 } 04083 04084 return TRUE; 04085 } 04086 04087 #endif 04088 04089 NTSTATUS 04090 NtQueryOpenSubKeys( 04091 IN POBJECT_ATTRIBUTES TargetKey, 04092 OUT PULONG HandleCount 04093 ) 04094 /*++ 04095 04096 Routine Description: 04097 04098 Dumps all the subkeys of the target key that are kept open by some other 04099 process; Returns the number of open subkeys 04100 04101 04102 Arguments: 04103 04104 TargetKey - specifies the path to a key to link the hive to. 04105 path must be of the form "\registry\user<username>" 04106 04107 Return Value: 04108 04109 NTSTATUS - values TBS. 04110 04111 --*/ 04112 { 04113 HANDLE KeyHandle; 04114 NTSTATUS Status; 04115 PCM_KEY_BODY KeyBody; 04116 PHHIVE Hive; 04117 HCELL_INDEX Cell; 04118 KPROCESSOR_MODE PreviousMode; 04119 UNICODE_STRING HiveName; 04120 04121 PAGED_CODE(); 04122 04123 BEGIN_LOCK_CHECKPOINT; 04124 04125 CMLOG(CML_API, CMS_NTAPI) KdPrint(("NtQueryOpenSubKeys\n")); 04126 CMLOG(CML_API_ARGS, CMS_NTAPI) { 04127 KdPrint(("\tTargetKey ='%wZ'\n", TargetKey->ObjectName)); 04128 } 04129 #ifdef LOG_NT_API 04130 if (CmpLogApi) { 04131 KdPrint(("NtQueryOpenSubKeys\n")); 04132 } 04133 #endif 04134 04135 PreviousMode = KeGetPreviousMode(); 04136 04137 // 04138 // lock registry exclusive so nobody messes with it while we're around 04139 // 04140 CmpLockRegistryExclusive(); 04141 04142 try { 04143 04144 if (PreviousMode == UserMode) { 04145 ProbeForWriteUlong(HandleCount); 04146 } 04147 04148 Status = ObOpenObjectByName(TargetKey, 04149 CmpKeyObjectType, 04150 PreviousMode, 04151 NULL, 04152 KEY_READ, 04153 NULL, 04154 &KeyHandle); 04155 if (NT_SUCCESS(Status)) { 04156 Status = ObReferenceObjectByHandle(KeyHandle, 04157 KEY_READ, 04158 CmpKeyObjectType, 04159 PreviousMode, 04160 (PVOID *)&KeyBody, 04161 NULL); 04162 NtClose(KeyHandle); 04163 } 04164 04165 } except (EXCEPTION_EXECUTE_HANDLER) { 04166 Status = GetExceptionCode(); 04167 CMLOG(CML_API, CMS_EXCEPTION) { 04168 KdPrint(("!!NtQueryOpenSubKeys: code:%08lx\n", Status)); 04169 } 04170 } 04171 04172 if (NT_SUCCESS(Status)) { 04173 Hive = KeyBody->KeyControlBlock->KeyHive; 04174 Cell = KeyBody->KeyControlBlock->KeyCell; 04175 04176 04177 // 04178 // Make sure the cell passed in is the root cell of the hive. 04179 // 04180 if (Cell != Hive->BaseBlock->RootCell) { 04181 ObDereferenceObject((PVOID)KeyBody); 04182 CmpUnlockRegistry(); 04183 return(STATUS_INVALID_PARAMETER); 04184 } 04185 04186 // 04187 // Dump the hive name and hive address 04188 // 04189 RtlInitUnicodeString(&HiveName, (PCWSTR)Hive->BaseBlock->FileName); 04190 DbgPrint("\n Subkeys open inside the hive (%lx) (%.*S) :\n\n",Hive,HiveName.Length / sizeof(WCHAR),HiveName.Buffer); 04191 04192 // 04193 // dump open subkeys (if any) 04194 // 04195 #ifdef KCB_TO_KEYBODY_LINK 04196 CmpOpenSubKeys = CmpSearchForOpenSubKeys(KeyBody->KeyControlBlock,SearchAndCount); 04197 #else 04198 // 04199 // use a global var to count the number of subkeys, as this is the only 04200 // way interfere with the Enum callback; It is safe to use as this will 04201 // be the only thread working on this global var (registry is locked exclusively) 04202 // 04203 CmpOpenSubKeys = 0; 04204 ObEnumerateObjectsByType( 04205 CmpKeyObjectType, 04206 CmpEnumKeyObjectCallback, 04207 Hive 04208 ); 04209 #endif 04210 ObDereferenceObject((PVOID)KeyBody); 04211 try { 04212 // 04213 // protect user mode memory 04214 // 04215 *HandleCount = CmpOpenSubKeys; 04216 } except (EXCEPTION_EXECUTE_HANDLER) { 04217 Status = GetExceptionCode(); 04218 } 04219 } 04220 04221 CmpUnlockRegistry(); 04222 04223 END_LOCK_CHECKPOINT; 04224 04225 return(Status); 04226 } 04227 04228

Generated on Sat May 15 19:40:58 2004 for test by doxygen 1.3.7