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.

879 lines
24 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation. All rights reserved.
  3. Module Name:
  4. pnpsec.c
  5. Abstract:
  6. This module implements the security checks required to access the Plug and
  7. Play manager APIs.
  8. VerifyClientPrivilege
  9. VerifyClientAccess
  10. VerifyKernelInitiatedEjectPermissions
  11. Author:
  12. James G. Cavalaris (jamesca) 05-Apr-2002
  13. Environment:
  14. User-mode only.
  15. Revision History:
  16. 05-Apr-2002 Jim Cavalaris (jamesca)
  17. Creation and initial implementation.
  18. --*/
  19. //
  20. // includes
  21. //
  22. #include "precomp.h"
  23. #pragma hdrstop
  24. #include "umpnpi.h"
  25. #include "umpnpdat.h"
  26. #include <svcsp.h>
  27. #pragma warning(disable:4204)
  28. #pragma warning(disable:4221)
  29. //
  30. // Global data provided by the service controller.
  31. // Specifies well-known account and group SIDs for us to use.
  32. //
  33. extern PSVCS_GLOBAL_DATA PnPGlobalData;
  34. //
  35. // Security descriptor of the Plug and Play Manager security object, used to
  36. // control access to the Plug and Play Manager APIs.
  37. //
  38. PSECURITY_DESCRIPTOR PlugPlaySecurityObject = NULL;
  39. //
  40. // Generic security mapping for the Plug and Play Manager security object.
  41. //
  42. GENERIC_MAPPING PlugPlaySecurityObjectMapping = PLUGPLAY_GENERIC_MAPPING;
  43. //
  44. // Function prototypes.
  45. //
  46. BOOL
  47. VerifyTokenPrivilege(
  48. IN HANDLE hToken,
  49. IN ULONG Privilege,
  50. IN LPCWSTR ServiceName
  51. );
  52. //
  53. // Access and privilege check routines.
  54. //
  55. BOOL
  56. VerifyClientPrivilege(
  57. IN handle_t hBinding,
  58. IN ULONG Privilege,
  59. IN LPCWSTR ServiceName
  60. )
  61. /*++
  62. Routine Description:
  63. This routine impersonates the client associated with hBinding and checks
  64. if the client possesses the specified privilege.
  65. Arguments:
  66. hBinding RPC Binding handle
  67. Privilege Specifies the privilege to be checked.
  68. Return value:
  69. The return value is TRUE if the client possesses the privilege, FALSE if not
  70. or if an error occurs.
  71. --*/
  72. {
  73. RPC_STATUS rpcStatus;
  74. BOOL bResult;
  75. HANDLE hToken;
  76. //
  77. // If the specified RPC binding handle is NULL, this is an internal call so
  78. // we assume that the privilege has already been checked.
  79. //
  80. if (hBinding == NULL) {
  81. return TRUE;
  82. }
  83. //
  84. // Impersonate the client to retrieve the impersonation token.
  85. //
  86. rpcStatus = RpcImpersonateClient(hBinding);
  87. if (rpcStatus != RPC_S_OK) {
  88. //
  89. // Since we can't impersonate the client we better not do the security
  90. // checks as ourself (they would always succeed).
  91. //
  92. return FALSE;
  93. }
  94. if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hToken)) {
  95. bResult =
  96. VerifyTokenPrivilege(
  97. hToken, Privilege, ServiceName);
  98. CloseHandle(hToken);
  99. } else {
  100. KdPrintEx((DPFLTR_PNPMGR_ID,
  101. DBGF_ERRORS,
  102. "UMPNPMGR: OpenThreadToken failed, error = %d\n",
  103. GetLastError()));
  104. bResult = FALSE;
  105. }
  106. rpcStatus = RpcRevertToSelf();
  107. if (rpcStatus != RPC_S_OK) {
  108. KdPrintEx((DPFLTR_PNPMGR_ID,
  109. DBGF_ERRORS,
  110. "UMPNPMGR: RpcRevertToSelf failed, error = %d\n",
  111. rpcStatus));
  112. ASSERT(rpcStatus == RPC_S_OK);
  113. }
  114. return bResult;
  115. } // VerifyClientPrivilege
  116. BOOL
  117. VerifyTokenPrivilege(
  118. IN HANDLE hToken,
  119. IN ULONG Privilege,
  120. IN LPCWSTR ServiceName
  121. )
  122. /*++
  123. Routine Description:
  124. This routine checks if the specified token possesses the specified
  125. privilege.
  126. Arguments:
  127. hToken Specifies a handle to the token whose privileges are to be
  128. checked
  129. Privilege Specifies the privilege to be checked.
  130. ServiceName Specifies the privileged subsystem service (operation
  131. requiring the privilege).
  132. Return value:
  133. The return value is TRUE if the client possesses the privilege, FALSE if not
  134. or if an error occurs.
  135. --*/
  136. {
  137. PRIVILEGE_SET privilegeSet;
  138. BOOL bResult = FALSE;
  139. //
  140. // Specify the privilege to be checked.
  141. //
  142. ZeroMemory(&privilegeSet, sizeof(PRIVILEGE_SET));
  143. privilegeSet.PrivilegeCount = 1;
  144. privilegeSet.Control = 0;
  145. privilegeSet.Privilege[0].Luid = RtlConvertUlongToLuid(Privilege);
  146. privilegeSet.Privilege[0].Attributes = 0;
  147. //
  148. // Perform the actual privilege check.
  149. //
  150. if (!PrivilegeCheck(hToken, &privilegeSet, &bResult)) {
  151. KdPrintEx((DPFLTR_PNPMGR_ID,
  152. DBGF_ERRORS,
  153. "UMPNPMGR: PrivilegeCheck failed, error = %d\n",
  154. GetLastError()));
  155. bResult = FALSE;
  156. }
  157. //
  158. // Generate an audit of the attempted privilege use, using the result of the
  159. // previous check.
  160. //
  161. if (!PrivilegedServiceAuditAlarm(
  162. PLUGPLAY_SUBSYSTEM_NAME,
  163. ServiceName,
  164. hToken,
  165. &privilegeSet,
  166. bResult)) {
  167. KdPrintEx((DPFLTR_PNPMGR_ID,
  168. DBGF_ERRORS,
  169. "UMPNPMGR: PrivilegedServiceAuditAlarm failed, error = %d\n",
  170. GetLastError()));
  171. }
  172. return bResult;
  173. } // VerifyTokenPrivilege
  174. BOOL
  175. VerifyKernelInitiatedEjectPermissions(
  176. IN HANDLE UserToken OPTIONAL,
  177. IN BOOL DockDevice
  178. )
  179. /*++
  180. Routine Description:
  181. Checks that the user has eject permissions for the specified type of
  182. hardware.
  183. Arguments:
  184. UserToken - Token of the logged in console user, NULL if no console user
  185. is logged in.
  186. DockDevice - TRUE if a dock is being ejected, FALSE if an ordinary device
  187. was specified.
  188. Return Value:
  189. TRUE if the eject should procceed, FALSE otherwise.
  190. --*/
  191. {
  192. LONG Result = ERROR_SUCCESS;
  193. BOOL AllowUndock;
  194. WCHAR RegStr[MAX_CM_PATH];
  195. HKEY hKey = NULL;
  196. DWORD dwSize, dwValue, dwType;
  197. TOKEN_PRIVILEGES NewPrivs, OldPrivs;
  198. //
  199. // Only enforce eject permissions for dock devices. We do not specify per
  200. // device ejection security for other types of devices, since most devices
  201. // are in no way secure from removal.
  202. //
  203. if (!DockDevice) {
  204. return TRUE;
  205. }
  206. //
  207. // Unless the policy says otherwise, we do NOT allow undock without
  208. // privilege check.
  209. //
  210. AllowUndock = FALSE;
  211. //
  212. // First, check the "Allow undock without having to log on" policy. If the
  213. // policy does NOT allow undock without logon, we require that there is an
  214. // interactive user logged on to the physical Console session, and that user
  215. // has the SE_UNDOCK_PRIVILEGE.
  216. //
  217. //
  218. // Open the System policies key.
  219. //
  220. if (SUCCEEDED(
  221. StringCchPrintf(
  222. RegStr,
  223. SIZECHARS(RegStr),
  224. L"%s\\%s",
  225. pszRegPathPolicies,
  226. pszRegKeySystem))) {
  227. if (RegOpenKeyEx(
  228. HKEY_LOCAL_MACHINE,
  229. RegStr,
  230. 0,
  231. KEY_READ,
  232. &hKey) == ERROR_SUCCESS) {
  233. //
  234. // Retrieve the "UndockWithoutLogon" value.
  235. //
  236. dwType = 0;
  237. dwValue = 0;
  238. dwSize = sizeof(dwValue);
  239. Result =
  240. RegQueryValueEx(
  241. hKey,
  242. pszRegValueUndockWithoutLogon,
  243. NULL,
  244. &dwType,
  245. (LPBYTE)&dwValue,
  246. &dwSize);
  247. if ((Result == ERROR_SUCCESS) && (dwType == REG_DWORD)) {
  248. //
  249. // If the value exists and is non-zero, we allow undock without
  250. // privilege check. If the value id zero, the policy requires
  251. // us to check the privileges of the supplied user token.
  252. //
  253. AllowUndock = (dwValue != 0);
  254. } else if (Result == ERROR_FILE_NOT_FOUND) {
  255. //
  256. // No value means allow any undock.
  257. //
  258. AllowUndock = TRUE;
  259. } else {
  260. //
  261. // For all remaining cases, the policy check either failed, or
  262. // an error was encountered reading the policy. We have
  263. // insufficient information to determine whether the eject
  264. // should be allowed based on policy alone, so we defer any
  265. // decision and check the privileges of the supplied user token.
  266. //
  267. AllowUndock = FALSE;
  268. }
  269. //
  270. // Close the policy key.
  271. //
  272. RegCloseKey(hKey);
  273. }
  274. }
  275. //
  276. // If the policy allowed undock without logon, there is no need to check
  277. // token privileges.
  278. //
  279. if (AllowUndock) {
  280. return TRUE;
  281. }
  282. //
  283. // If the policy requires privileges to be checked, but no user token was
  284. // supplied, deny the request.
  285. //
  286. if (UserToken == NULL) {
  287. return FALSE;
  288. }
  289. //
  290. // Enable the required SE_UNDOCK_PRIVILEGE token privilege.
  291. // The TOKEN_PRIVILEGES structure contains 1 LUID_AND_ATTRIBUTES, which is
  292. // all we need for now.
  293. //
  294. ZeroMemory(&NewPrivs, sizeof(TOKEN_PRIVILEGES));
  295. ZeroMemory(&OldPrivs, sizeof(TOKEN_PRIVILEGES));
  296. NewPrivs.PrivilegeCount = 1;
  297. NewPrivs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  298. NewPrivs.Privileges[0].Luid = RtlConvertUlongToLuid(SE_UNDOCK_PRIVILEGE);
  299. dwSize = sizeof(TOKEN_PRIVILEGES);
  300. if (!AdjustTokenPrivileges(
  301. UserToken,
  302. FALSE,
  303. &NewPrivs,
  304. sizeof(TOKEN_PRIVILEGES),
  305. &OldPrivs,
  306. &dwSize)) {
  307. return FALSE;
  308. }
  309. //
  310. // Check if the required SE_UNDOCK_PRIVILEGE privilege is enabled. Note
  311. // that this routine also audits the use of this privilege.
  312. //
  313. AllowUndock =
  314. VerifyTokenPrivilege(
  315. UserToken,
  316. SE_UNDOCK_PRIVILEGE,
  317. L"UNDOCK: EJECT DOCK DEVICE");
  318. //
  319. // Adjust the privilege back to its previous state.
  320. //
  321. AdjustTokenPrivileges(
  322. UserToken,
  323. FALSE,
  324. &OldPrivs,
  325. sizeof(TOKEN_PRIVILEGES),
  326. (PTOKEN_PRIVILEGES)NULL,
  327. (PDWORD)NULL);
  328. return AllowUndock;
  329. } // VerifyKernelInitiatedEjectPermissions
  330. BOOL
  331. CreatePlugPlaySecurityObject(
  332. VOID
  333. )
  334. /*++
  335. Routine Description:
  336. This function creates the self-relative security descriptor which
  337. represents the Plug and Play security object.
  338. Arguments:
  339. None.
  340. Return Value:
  341. TRUE if the object was successfully created, FALSE otherwise.
  342. --*/
  343. {
  344. BOOL Status = TRUE;
  345. NTSTATUS NtStatus;
  346. BOOLEAN WasEnabled;
  347. PSECURITY_DESCRIPTOR AbsoluteSd = NULL;
  348. HANDLE TokenHandle = NULL;
  349. //
  350. // This routine is called from our service start routine, so we must have
  351. // been provided with the global data block by now.
  352. //
  353. ASSERT(PnPGlobalData != NULL);
  354. //
  355. // SE_AUDIT_PRIVILEGE privilege is required to perform object access and
  356. // privilege auditing, and must be enabled in the process token. Leave it
  357. // enabled since we will be auditing frequently. Note that we share this
  358. // process (services.exe) with the SCM, which also performs auditing, and
  359. // most likely has already enabled this privilege for the process.
  360. //
  361. RtlAdjustPrivilege(SE_AUDIT_PRIVILEGE,
  362. TRUE,
  363. FALSE,
  364. &WasEnabled);
  365. //
  366. // SE_SECURITY_PRIVILEGE privilege is required to create a security
  367. // descriptor with a SACL. Impersonate ourselves to safely enable the
  368. // privilege on our thread only.
  369. //
  370. NtStatus =
  371. RtlImpersonateSelf(
  372. SecurityImpersonation);
  373. ASSERT(NT_SUCCESS(NtStatus));
  374. if (!NT_SUCCESS(NtStatus)) {
  375. return FALSE;
  376. }
  377. NtStatus =
  378. RtlAdjustPrivilege(
  379. SE_SECURITY_PRIVILEGE,
  380. TRUE,
  381. TRUE,
  382. &WasEnabled);
  383. ASSERT(NT_SUCCESS(NtStatus));
  384. if (NT_SUCCESS(NtStatus)) {
  385. //
  386. // Specify the ACEs for the security object.
  387. // Order matters! These ACEs are inserted into the DACL in the
  388. // following order. Security access is granted or denied based on
  389. // the order of the ACEs in the DACL.
  390. //
  391. // ISSUE-2002/06/25-jamesca: Description of PlugPlaySecurityObject ACEs.
  392. // For consistent read/write access between the server-side PlugPlay
  393. // APIs and direct registry access by the client, we apply a DACL on
  394. // the PlugPlaySecurityObject that similar to what is applied by
  395. // default to the client accesible parts of the Plug and Play registry
  396. // -- specifically, SYSTEM\CCS\Control\Class. Note however that
  397. // "Power Users" are NOT special-cased here as they are in the
  398. // registry ACLs; they must be authenticated as a group listed below
  399. // for any access to be granted. If the access requirements for Plug
  400. // and Play objects such as the registry ever change, you must
  401. // re-evaluate the ACEs below to make sure they are still
  402. // appropriate!!
  403. //
  404. RTL_ACE_DATA PlugPlayAceData[] = {
  405. //
  406. // Local System Account is granted all access.
  407. //
  408. { ACCESS_ALLOWED_ACE_TYPE,
  409. 0,
  410. 0,
  411. GENERIC_ALL,
  412. &(PnPGlobalData->LocalSystemSid) },
  413. //
  414. // Local Administrators Group is granted all access.
  415. //
  416. { ACCESS_ALLOWED_ACE_TYPE,
  417. 0,
  418. 0,
  419. GENERIC_ALL,
  420. &(PnPGlobalData->AliasAdminsSid) },
  421. //
  422. // Network Group is denied any access.
  423. // (unless granted by Local Administrators ACE, above)
  424. //
  425. { ACCESS_DENIED_ACE_TYPE,
  426. 0,
  427. 0,
  428. GENERIC_ALL,
  429. &(PnPGlobalData->NetworkSid) },
  430. //
  431. // Users are granted read and execute access.
  432. // (unless denied by Network ACE, above)
  433. //
  434. { ACCESS_ALLOWED_ACE_TYPE,
  435. 0,
  436. 0,
  437. GENERIC_READ | GENERIC_EXECUTE,
  438. &(PnPGlobalData->AliasUsersSid) },
  439. //
  440. // Any access request not explicitly granted above is denied.
  441. //
  442. //
  443. // Audit object-specific write access requests for everyone, for
  444. // failure or success. Audit all access request failures.
  445. //
  446. // We don't audit successful read access requests, because they
  447. // occur too frequently to be useful. We don't audit successful
  448. // execute requests because they result in privilege checks, which
  449. // are audited separately.
  450. //
  451. // Also note that we only audit the PLUGPLAY_WRITE object-specific
  452. // right. Since the GENERIC_WRITE mapping shares standard rights
  453. // with GENERIC_READ and GENERIC_EXECUTE, auditing successful access
  454. // grants for any of the GENERIC_WRITE bits would also result in
  455. // auditing any sucessful request for GENERIC_READ (and/or
  456. // GENERIC_EXECUTE access) - which as mentioned, are too frequent.
  457. //
  458. { SYSTEM_AUDIT_ACE_TYPE,
  459. 0,
  460. FAILED_ACCESS_ACE_FLAG | SUCCESSFUL_ACCESS_ACE_FLAG,
  461. PLUGPLAY_WRITE,
  462. &(PnPGlobalData->WorldSid) },
  463. //
  464. // Audit all access failures for everyone.
  465. //
  466. // ISSUE-2002/06/25-jamesca: Everyone vs. Anonymous group SIDs:
  467. // Note that for the purposes of auditing, the Everyone SID also
  468. // includes Anonymous, though in all other cases the two groups
  469. // are now disjoint (Windows XP and later). The named pipe used
  470. // for our RPC endpoint only grants access to everyone however,
  471. // so technically we will never receive RPC calls from an
  472. // Anonymous caller.
  473. //
  474. { SYSTEM_AUDIT_ACE_TYPE,
  475. 0,
  476. FAILED_ACCESS_ACE_FLAG,
  477. GENERIC_ALL,
  478. &(PnPGlobalData->WorldSid) },
  479. };
  480. //
  481. // Create a new Absolute Security Descriptor, specifying the LocalSystem
  482. // account SID for both Owner and Group.
  483. //
  484. NtStatus =
  485. RtlCreateAndSetSD(
  486. PlugPlayAceData,
  487. RTL_NUMBER_OF(PlugPlayAceData),
  488. PnPGlobalData->LocalSystemSid,
  489. PnPGlobalData->LocalSystemSid,
  490. &AbsoluteSd);
  491. ASSERT(NT_SUCCESS(NtStatus));
  492. if (NT_SUCCESS(NtStatus)) {
  493. NtStatus =
  494. NtOpenThreadToken(
  495. NtCurrentThread(),
  496. TOKEN_QUERY,
  497. FALSE,
  498. &TokenHandle);
  499. ASSERT(NT_SUCCESS(NtStatus));
  500. if (NT_SUCCESS(NtStatus)) {
  501. //
  502. // Create the security object (a user-mode object is really a pseudo-
  503. // object represented by a security descriptor that have relative
  504. // pointers to SIDs and ACLs). This routine allocates the memory to
  505. // hold the relative security descriptor so the memory allocated for the
  506. // DACL, ACEs, and the absolute descriptor can be freed.
  507. //
  508. NtStatus =
  509. RtlNewSecurityObject(
  510. NULL,
  511. AbsoluteSd,
  512. &PlugPlaySecurityObject,
  513. FALSE,
  514. TokenHandle,
  515. &PlugPlaySecurityObjectMapping);
  516. ASSERT(NT_SUCCESS(NtStatus));
  517. NtClose(TokenHandle);
  518. }
  519. //
  520. // Free the Absolute Security Descriptor allocated by
  521. // RtlCreateAndSetSD in the process heap. If sucessful, we should
  522. // have a self-relative one in PlugPlaySecurityObject.
  523. //
  524. RtlFreeHeap(RtlProcessHeap(), 0, AbsoluteSd);
  525. }
  526. }
  527. ASSERT(IsValidSecurityDescriptor(PlugPlaySecurityObject));
  528. //
  529. // If not successful, we could not create the security object.
  530. //
  531. if (!NT_SUCCESS(NtStatus)) {
  532. ASSERT(PlugPlaySecurityObject == NULL);
  533. PlugPlaySecurityObject = NULL;
  534. Status = FALSE;
  535. }
  536. //
  537. // Stop impersonating.
  538. //
  539. TokenHandle = NULL;
  540. NtStatus =
  541. NtSetInformationThread(
  542. NtCurrentThread(),
  543. ThreadImpersonationToken,
  544. (PVOID)&TokenHandle,
  545. sizeof(TokenHandle));
  546. ASSERT(NT_SUCCESS(NtStatus));
  547. return Status;
  548. } // CreatePlugPlaySecurityObject
  549. VOID
  550. DestroyPlugPlaySecurityObject(
  551. VOID
  552. )
  553. /*++
  554. Routine Description:
  555. This function deletes the self-relative security descriptor which
  556. represents the Plug and Play security object.
  557. Arguments:
  558. None.
  559. Return Value:
  560. None.
  561. --*/
  562. {
  563. if (PlugPlaySecurityObject != NULL) {
  564. RtlDeleteSecurityObject(&PlugPlaySecurityObject);
  565. PlugPlaySecurityObject = NULL;
  566. }
  567. return;
  568. } // DestroyPlugPlaySecutityObject
  569. BOOL
  570. VerifyClientAccess(
  571. IN handle_t hBinding,
  572. IN ACCESS_MASK DesiredAccess
  573. )
  574. /*++
  575. Routine Description:
  576. This routine determines if the client associated with hBinding is granted
  577. trhe desired access.
  578. Arguments:
  579. hBinding RPC Binding handle
  580. DesiredAccess Access desired
  581. Return value:
  582. The return value is TRUE if the client is granted access, FALSE if not or if
  583. an error occurs.
  584. --*/
  585. {
  586. RPC_STATUS rpcStatus;
  587. BOOL AccessStatus = FALSE;
  588. BOOL GenerateOnClose;
  589. ACCESS_MASK GrantedAccess;
  590. //
  591. // If the specified RPC binding handle is NULL, this is an internal call so
  592. // we assume that the access has already been checked.
  593. //
  594. if (hBinding == NULL) {
  595. return TRUE;
  596. }
  597. //
  598. // If we have no security object, we cannot perform access checks, and no
  599. // access is granted.
  600. //
  601. ASSERT(PlugPlaySecurityObject != NULL);
  602. if (PlugPlaySecurityObject == NULL) {
  603. return FALSE;
  604. }
  605. //
  606. // If any generic access rights were specified, map them to the specific and
  607. // standard rights specified in the object's generic access map.
  608. //
  609. MapGenericMask(
  610. (PDWORD)&DesiredAccess,
  611. &PlugPlaySecurityObjectMapping);
  612. //
  613. // Impersonate the client.
  614. //
  615. rpcStatus = RpcImpersonateClient(hBinding);
  616. if (rpcStatus != RPC_S_OK) {
  617. KdPrintEx((DPFLTR_PNPMGR_ID,
  618. DBGF_ERRORS,
  619. "UMPNPMGR: RpcImpersonateClient failed, error = %d\n",
  620. rpcStatus));
  621. return FALSE;
  622. }
  623. //
  624. // Perform the access check while impersonating the client - the
  625. // impersonation token is automatically used. Generate audit and alarm,
  626. // as specified by the security object.
  627. //
  628. // Note that auditing requires the SE_AUDIT_PRIVILEGE to be enabled in the
  629. // *process* token. Most likely, the SCM would have enabled this privilege
  630. // for the services.exe process at startup (since it also performs
  631. // auditing). If not, we would have attempted to enable it during our
  632. // initialization, when we created the PlugPlaySecurityObject.
  633. //
  634. if (!AccessCheckAndAuditAlarm(
  635. PLUGPLAY_SUBSYSTEM_NAME, // subsystem name
  636. NULL, // handle to object
  637. PLUGPLAY_SECURITY_OBJECT_TYPE, // type of object
  638. PLUGPLAY_SECURITY_OBJECT_NAME, // name of object
  639. PlugPlaySecurityObject, // SD
  640. DesiredAccess, // requested access rights
  641. &PlugPlaySecurityObjectMapping, // mapping
  642. FALSE, // creation status
  643. &GrantedAccess, // granted access rights
  644. &AccessStatus, // result of access check
  645. &GenerateOnClose // audit generation option
  646. )) {
  647. KdPrintEx((DPFLTR_PNPMGR_ID,
  648. DBGF_ERRORS,
  649. "UMPNPMGR: AccessCheckAndAuditAlarm failed, error = %d\n",
  650. GetLastError()));
  651. AccessStatus = FALSE;
  652. }
  653. //
  654. // Stop impersonating.
  655. //
  656. rpcStatus = RpcRevertToSelf();
  657. if (rpcStatus != RPC_S_OK) {
  658. KdPrintEx((DPFLTR_PNPMGR_ID,
  659. DBGF_ERRORS,
  660. "UMPNPMGR: RpcRevertToSelf failed, error = %d\n",
  661. rpcStatus));
  662. ASSERT(rpcStatus == RPC_S_OK);
  663. }
  664. return AccessStatus;
  665. } // VerifyClientAccess
  666.