Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

440 lines
12 KiB

  1. /*++
  2. Copyright (c) 20001 Microsoft Corporation
  3. Module Name:
  4. cmhook.c
  5. Abstract:
  6. Provides routines for implementing callbacks into the registry code.
  7. Callbacks are to be used by the virus filter drivers and cluster
  8. replication engine.
  9. Author:
  10. Dragos C. Sambotin (DragosS) 20-Mar-2001
  11. Revision History:
  12. --*/
  13. #include "cmp.h"
  14. #define CM_MAX_CALLBACKS 100 //TBD
  15. typedef struct _CM_CALLBACK_CONTEXT_BLOCK {
  16. LARGE_INTEGER Cookie; // to identify a specific callback for deregistration purposes
  17. LIST_ENTRY ThreadListHead; // Active threads inside this callback
  18. FAST_MUTEX ThreadListLock; // syncronize access to the above
  19. PVOID CallerContext;
  20. } CM_CALLBACK_CONTEXT_BLOCK, *PCM_CALLBACK_CONTEXT_BLOCK;
  21. typedef struct _CM_ACTIVE_NOTIFY_THREAD {
  22. LIST_ENTRY ThreadList;
  23. PETHREAD Thread;
  24. } CM_ACTIVE_NOTIFY_THREAD, *PCM_ACTIVE_NOTIFY_THREAD;
  25. #ifdef ALLOC_DATA_PRAGMA
  26. #pragma data_seg("PAGEDATA")
  27. #endif
  28. ULONG CmpCallBackCount = 0;
  29. EX_CALLBACK CmpCallBackVector[CM_MAX_CALLBACKS] = {0};
  30. #ifdef ALLOC_DATA_PRAGMA
  31. #pragma data_seg()
  32. #endif
  33. VOID
  34. CmpInitCallback(VOID);
  35. BOOLEAN
  36. CmpCheckRecursionAndRecordThreadInfo(
  37. PCM_CALLBACK_CONTEXT_BLOCK CallbackBlock,
  38. PCM_ACTIVE_NOTIFY_THREAD ActiveThreadInfo
  39. );
  40. #ifdef ALLOC_PRAGMA
  41. #pragma alloc_text(PAGE,CmRegisterCallback)
  42. #pragma alloc_text(PAGE,CmUnRegisterCallback)
  43. #pragma alloc_text(PAGE,CmpInitCallback)
  44. #pragma alloc_text(PAGE,CmpCallCallBacks)
  45. #pragma alloc_text(PAGE,CmpCheckRecursionAndRecordThreadInfo)
  46. #endif
  47. NTSTATUS
  48. CmRegisterCallback(IN PEX_CALLBACK_FUNCTION Function,
  49. IN PVOID Context,
  50. IN OUT PLARGE_INTEGER Cookie
  51. )
  52. /*++
  53. Routine Description:
  54. Registers a new callback.
  55. Arguments:
  56. Return Value:
  57. --*/
  58. {
  59. PEX_CALLBACK_ROUTINE_BLOCK RoutineBlock;
  60. ULONG i;
  61. PCM_CALLBACK_CONTEXT_BLOCK CmCallbackContext;
  62. PAGED_CODE();
  63. CmCallbackContext = (PCM_CALLBACK_CONTEXT_BLOCK)ExAllocatePoolWithTag (PagedPool,
  64. sizeof (CM_CALLBACK_CONTEXT_BLOCK),
  65. 'bcMC');
  66. if( CmCallbackContext == NULL ) {
  67. return STATUS_INSUFFICIENT_RESOURCES;
  68. }
  69. RoutineBlock = ExAllocateCallBack (Function,CmCallbackContext);
  70. if( RoutineBlock == NULL ) {
  71. ExFreePool(CmCallbackContext);
  72. return STATUS_INSUFFICIENT_RESOURCES;
  73. }
  74. //
  75. // init the context
  76. //
  77. KeQuerySystemTime(&(CmCallbackContext->Cookie));
  78. *Cookie = CmCallbackContext->Cookie;
  79. InitializeListHead(&(CmCallbackContext->ThreadListHead));
  80. ExInitializeFastMutex(&(CmCallbackContext->ThreadListLock));
  81. CmCallbackContext->CallerContext = Context;
  82. //
  83. // find a spot where we could add this callback
  84. //
  85. for( i=0;i<CM_MAX_CALLBACKS;i++) {
  86. if( ExCompareExchangeCallBack (&CmpCallBackVector[i],RoutineBlock,NULL) ) {
  87. InterlockedExchangeAdd ((PLONG) &CmpCallBackCount, 1);
  88. return STATUS_SUCCESS;
  89. }
  90. }
  91. //
  92. // no more callbacks
  93. //
  94. ExFreePool(CmCallbackContext);
  95. ExFreeCallBack(RoutineBlock);
  96. return STATUS_INSUFFICIENT_RESOURCES;
  97. }
  98. NTSTATUS
  99. CmUnRegisterCallback(IN LARGE_INTEGER Cookie)
  100. /*++
  101. Routine Description:
  102. Unregisters a callback.
  103. Arguments:
  104. Return Value:
  105. --*/
  106. {
  107. ULONG i;
  108. PCM_CALLBACK_CONTEXT_BLOCK CmCallbackContext;
  109. PEX_CALLBACK_ROUTINE_BLOCK RoutineBlock;
  110. PAGED_CODE();
  111. //
  112. // Search for this cookie
  113. //
  114. for( i=0;i<CM_MAX_CALLBACKS;i++) {
  115. RoutineBlock = ExReferenceCallBackBlock(&(CmpCallBackVector[i]) );
  116. if( RoutineBlock ) {
  117. CmCallbackContext = (PCM_CALLBACK_CONTEXT_BLOCK)ExGetCallBackBlockContext(RoutineBlock);
  118. if( CmCallbackContext && (CmCallbackContext->Cookie.QuadPart == Cookie.QuadPart) ) {
  119. //
  120. // found it
  121. //
  122. if( ExCompareExchangeCallBack (&CmpCallBackVector[i],NULL,RoutineBlock) ) {
  123. InterlockedExchangeAdd ((PLONG) &CmpCallBackCount, -1);
  124. ExDereferenceCallBackBlock (&(CmpCallBackVector[i]),RoutineBlock);
  125. //
  126. // wait for others to release their reference, then tear down the structure
  127. //
  128. ExWaitForCallBacks (RoutineBlock);
  129. ExFreePool(CmCallbackContext);
  130. ExFreeCallBack(RoutineBlock);
  131. return STATUS_SUCCESS;
  132. }
  133. } else {
  134. ExDereferenceCallBackBlock (&(CmpCallBackVector[i]),RoutineBlock);
  135. }
  136. }
  137. }
  138. return STATUS_INVALID_PARAMETER;
  139. }
  140. NTSTATUS CmpTestCallback(
  141. IN PVOID CallbackContext,
  142. IN PVOID Argument1,
  143. IN PVOID Argument2
  144. );
  145. //
  146. // Cm internals
  147. //
  148. NTSTATUS
  149. CmpCallCallBacks (
  150. IN REG_NOTIFY_CLASS Type,
  151. IN PVOID Argument
  152. )
  153. /*++
  154. Routine Description:
  155. This function calls the callback thats inside a callback structure
  156. Arguments:
  157. Type - Nt call selector
  158. Argument - Caller provided argument to pass on (one of the REG_*_INFORMATION )
  159. Return Value:
  160. NTSTATUS - STATUS_SUCCESS or error status returned by the first callback
  161. --*/
  162. {
  163. NTSTATUS Status = STATUS_SUCCESS;
  164. ULONG i;
  165. PEX_CALLBACK_ROUTINE_BLOCK RoutineBlock;
  166. PCM_CALLBACK_CONTEXT_BLOCK CmCallbackContext;
  167. PAGED_CODE();
  168. for(i=0;i<CM_MAX_CALLBACKS;i++) {
  169. RoutineBlock = ExReferenceCallBackBlock(&(CmpCallBackVector[i]) );
  170. if( RoutineBlock != NULL ) {
  171. //
  172. // we have a safe reference on this block.
  173. //
  174. //
  175. // record thread on a stack struct, so we don't need to allocate pool for it. We unlink
  176. // it from our lists prior to this function exit, so we are on the safe side.
  177. //
  178. CM_ACTIVE_NOTIFY_THREAD ActiveThreadInfo;
  179. //
  180. // get context info
  181. //
  182. CmCallbackContext = (PCM_CALLBACK_CONTEXT_BLOCK)ExGetCallBackBlockContext(RoutineBlock);
  183. ASSERT( CmCallbackContext != NULL );
  184. ActiveThreadInfo.Thread = PsGetCurrentThread();
  185. #if DBG
  186. InitializeListHead(&(ActiveThreadInfo.ThreadList));
  187. #endif //DBG
  188. if( CmpCheckRecursionAndRecordThreadInfo(CmCallbackContext,&ActiveThreadInfo) ) {
  189. Status = ExGetCallBackBlockRoutine(RoutineBlock)(CmCallbackContext->CallerContext,(PVOID)(ULONG_PTR)Type,Argument);
  190. //
  191. // now that we're down, remove ourselves from the thread list
  192. //
  193. ExAcquireFastMutex(&(CmCallbackContext->ThreadListLock));
  194. RemoveEntryList(&(ActiveThreadInfo.ThreadList));
  195. ExReleaseFastMutex(&(CmCallbackContext->ThreadListLock));
  196. } else {
  197. ASSERT( IsListEmpty(&(ActiveThreadInfo.ThreadList)) );
  198. }
  199. ExDereferenceCallBackBlock (&(CmpCallBackVector[i]),RoutineBlock);
  200. if( !NT_SUCCESS(Status) ) {
  201. //
  202. // don't bother calling other callbacks if this one vetoed.
  203. //
  204. return Status;
  205. }
  206. }
  207. }
  208. return STATUS_SUCCESS;
  209. }
  210. VOID
  211. CmpInitCallback(VOID)
  212. /*++
  213. Routine Description:
  214. Init the callback module
  215. Arguments:
  216. Return Value:
  217. --*/
  218. {
  219. ULONG i;
  220. PAGED_CODE();
  221. CmpCallBackCount = 0;
  222. for( i=0;i<CM_MAX_CALLBACKS;i++) {
  223. ExInitializeCallBack (&(CmpCallBackVector[i]));
  224. }
  225. /*
  226. {
  227. LARGE_INTEGER Cookie;
  228. if( NT_SUCCESS(CmRegisterCallback(CmpTestCallback,NULL,&Cookie) ) ) {
  229. DbgPrint("Test Hooks installed\n");
  230. }
  231. }
  232. */
  233. }
  234. BOOLEAN
  235. CmpCheckRecursionAndRecordThreadInfo(
  236. PCM_CALLBACK_CONTEXT_BLOCK CallbackBlock,
  237. PCM_ACTIVE_NOTIFY_THREAD ActiveThreadInfo
  238. )
  239. /*++
  240. Routine Description:
  241. Checks if current thread is already inside the callback (recursion avoidance)
  242. Arguments:
  243. Return Value:
  244. --*/
  245. {
  246. PLIST_ENTRY AnchorAddr;
  247. PCM_ACTIVE_NOTIFY_THREAD CurrentThreadInfo;
  248. PAGED_CODE();
  249. ExAcquireFastMutex(&(CallbackBlock->ThreadListLock));
  250. //
  251. // walk the ActiveThreadList and see if we are already active
  252. //
  253. AnchorAddr = &(CallbackBlock->ThreadListHead);
  254. CurrentThreadInfo = (PCM_ACTIVE_NOTIFY_THREAD)(CallbackBlock->ThreadListHead.Flink);
  255. while ( CurrentThreadInfo != (PCM_ACTIVE_NOTIFY_THREAD)AnchorAddr ) {
  256. CurrentThreadInfo = CONTAINING_RECORD(
  257. CurrentThreadInfo,
  258. CM_ACTIVE_NOTIFY_THREAD,
  259. ThreadList
  260. );
  261. if( CurrentThreadInfo->Thread == ActiveThreadInfo->Thread ) {
  262. //
  263. // already there!
  264. //
  265. ExReleaseFastMutex(&(CallbackBlock->ThreadListLock));
  266. return FALSE;
  267. }
  268. //
  269. // skip to the next element
  270. //
  271. CurrentThreadInfo = (PCM_ACTIVE_NOTIFY_THREAD)(CurrentThreadInfo->ThreadList.Flink);
  272. }
  273. //
  274. // add this thread
  275. //
  276. InsertTailList(&(CallbackBlock->ThreadListHead), &(ActiveThreadInfo->ThreadList));
  277. ExReleaseFastMutex(&(CallbackBlock->ThreadListLock));
  278. return TRUE;
  279. }
  280. //
  281. // test hook procedure
  282. //
  283. BOOLEAN CmpCallbackSpew = FALSE;
  284. NTSTATUS CmpTestCallback(
  285. IN PVOID CallbackContext,
  286. IN PVOID Argument1,
  287. IN PVOID Argument2
  288. )
  289. {
  290. REG_NOTIFY_CLASS Type;
  291. PAGED_CODE();
  292. UNREFERENCED_PARAMETER (CallbackContext);
  293. if( !CmpCallbackSpew ) return STATUS_SUCCESS;
  294. Type = (REG_NOTIFY_CLASS)(ULONG_PTR)Argument1;
  295. switch( Type ) {
  296. case RegNtPreDeleteKey:
  297. {
  298. PREG_DELETE_KEY_INFORMATION pDelete = (PREG_DELETE_KEY_INFORMATION)Argument2;
  299. //
  300. // Code to handle NtDeleteKey
  301. //
  302. DbgPrint("Callback(NtDeleteKey) called, arg = %p\n",pDelete);
  303. }
  304. break;
  305. case RegNtPreSetValueKey:
  306. {
  307. PREG_SET_VALUE_KEY_INFORMATION pSetValue = (PREG_SET_VALUE_KEY_INFORMATION)Argument2;
  308. //
  309. // Code to handle NtSetValueKey
  310. //
  311. DbgPrint("Callback(NtSetValueKey) called, arg = %p\n",pSetValue);
  312. }
  313. break;
  314. case RegNtPreDeleteValueKey:
  315. {
  316. PREG_DELETE_VALUE_KEY_INFORMATION pDeteteValue = (PREG_DELETE_VALUE_KEY_INFORMATION)Argument2;
  317. //
  318. // Code to handle NtDeleteValueKey
  319. //
  320. DbgPrint("Callback(NtDeleteValueKey) called, arg = %p\n",pDeteteValue);
  321. }
  322. break;
  323. case RegNtPreSetInformationKey:
  324. {
  325. PREG_SET_INFORMATION_KEY_INFORMATION pSetInfo = (PREG_SET_INFORMATION_KEY_INFORMATION)Argument2;
  326. //
  327. // Code to handle NtSetInformationKey
  328. //
  329. DbgPrint("Callback(NtSetInformationKey) called, arg = %p\n",pSetInfo);
  330. }
  331. break;
  332. default:
  333. DbgPrint("Callback(%lx) called, arg = %p - We don't handle this call\n",(ULONG)Type,Argument2);
  334. break;
  335. }
  336. return STATUS_SUCCESS;
  337. }