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.

1328 lines
38 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation. All rights reserved.
  3. Module Name:
  4. util.c
  5. Abstract:
  6. This module contains general utility routines used by cfgmgr32 code.
  7. INVALID_DEVINST
  8. CopyFixedUpDeviceId
  9. PnPUnicodeToMultiByte
  10. PnPMultiByteToUnicode
  11. PnPRetrieveMachineName
  12. PnPGetVersion
  13. PnPGetGlobalHandles
  14. PnPEnablePrivileges
  15. PnPRestorePrivileges
  16. IsRemoteServiceRunning
  17. Author:
  18. Paula Tomlinson (paulat) 6-22-1995
  19. Environment:
  20. User mode only.
  21. Revision History:
  22. 22-Jun-1995 paulat
  23. Creation and initial implementation.
  24. --*/
  25. //
  26. // includes
  27. //
  28. #include "precomp.h"
  29. #pragma hdrstop
  30. #include "cfgi.h"
  31. //
  32. // Private prototypes
  33. //
  34. BOOL
  35. EnablePnPPrivileges(
  36. VOID
  37. );
  38. //
  39. // global data
  40. //
  41. extern PVOID hLocalStringTable; // MODIFIED by PnPGetGlobalHandles
  42. extern PVOID hLocalBindingHandle; // MODIFIED by PnPGetGlobalHandles
  43. extern WORD LocalServerVersion; // MODIFIED by PnPGetVersion
  44. extern WCHAR LocalMachineNameNetBIOS[]; // NOT MODIFIED BY THIS FILE
  45. extern CRITICAL_SECTION BindingCriticalSection; // NOT MODIFIED IN THIS FILE
  46. extern CRITICAL_SECTION StringTableCriticalSection; // NOT MODIFIED IN THIS FILE
  47. BOOL
  48. INVALID_DEVINST(
  49. PWSTR pDeviceID
  50. )
  51. /*++
  52. Routine Description:
  53. This routine attempts a simple check whether the pDeviceID string
  54. returned from StringTableStringFromID is valid or not. It does
  55. this simply by dereferencing the pointer and comparing the first
  56. character in the string against the range of characters for a valid
  57. device id. If the string is valid but it's not an existing device id
  58. then this error will be caught later.
  59. Arguments:
  60. pDeviceID Supplies a pointer to the string to be validated.
  61. Return Value:
  62. If it's invalid it returns TRUE, otherwise it returns FALSE.
  63. --*/
  64. {
  65. BOOL Status = FALSE;
  66. try {
  67. if ((!ARGUMENT_PRESENT(pDeviceID)) ||
  68. (*pDeviceID <= L' ') ||
  69. (*pDeviceID > (WCHAR)0x7F) ||
  70. (*pDeviceID == L',')) {
  71. Status = TRUE;
  72. }
  73. } except(EXCEPTION_EXECUTE_HANDLER) {
  74. Status = TRUE;
  75. }
  76. return Status;
  77. } // INVALID_DEVINST
  78. VOID
  79. CopyFixedUpDeviceId(
  80. OUT LPWSTR DestinationString,
  81. IN LPCWSTR SourceString,
  82. IN DWORD SourceStringLen
  83. )
  84. /*++
  85. Routine Description:
  86. This routine copies a device id, fixing it up as it does the copy.
  87. 'Fixing up' means that the string is made upper-case, and that the
  88. following character ranges are turned into underscores (_):
  89. c <= 0x20 (' ')
  90. c > 0x7F
  91. c == 0x2C (',')
  92. (NOTE: This algorithm is also implemented in the Config Manager APIs,
  93. and must be kept in sync with that routine. To maintain device identifier
  94. compatibility, these routines must work the same as Win95.)
  95. Arguments:
  96. DestinationString - Supplies a pointer to the destination string buffer
  97. where the fixed-up device id is to be copied. This buffer must
  98. be large enough to hold a copy of the source string (including
  99. terminating NULL).
  100. SourceString - Supplies a pointer to the (null-terminated) source
  101. string to be fixed up.
  102. SourceStringLen - Supplies the length, in characters, of the source
  103. string (not including terminating NULL).
  104. Return Value:
  105. None.
  106. --*/
  107. {
  108. PWCHAR p;
  109. try {
  110. CopyMemory(DestinationString,
  111. SourceString,
  112. ((SourceStringLen + 1) * sizeof(WCHAR)));
  113. CharUpperBuff(DestinationString, SourceStringLen);
  114. for(p = DestinationString; *p; p++) {
  115. if((*p <= L' ') ||
  116. (*p > (WCHAR)0x7F) ||
  117. (*p == L',')) {
  118. *p = L'_';
  119. }
  120. }
  121. } except(EXCEPTION_EXECUTE_HANDLER) {
  122. NOTHING;
  123. }
  124. } // CopyFixedUpDeviceId
  125. CONFIGRET
  126. PnPUnicodeToMultiByte(
  127. IN PWSTR UnicodeString,
  128. IN ULONG UnicodeStringLen,
  129. OUT PSTR AnsiString OPTIONAL,
  130. IN OUT PULONG AnsiStringLen
  131. )
  132. /*++
  133. Routine Description:
  134. Convert a string from unicode to ansi.
  135. Arguments:
  136. UnicodeString - Supplies string to be converted.
  137. UnicodeStringLen - Specifies the size, in bytes, of the string to be
  138. converted.
  139. AnsiString - Optionally, supplies a buffer to receive the ANSI
  140. string.
  141. AnsiStringLen - Supplies the address of a variable that contains the
  142. size, in bytes, of the buffer pointed to by AnsiString.
  143. This API replaces the initial size with the number of
  144. bytes of data copied to the buffer. If the variable is
  145. initially zero, the API replaces it with the buffer size
  146. needed to receive all the registry data. In this case,
  147. the AnsiString parameter is ignored.
  148. Return Value:
  149. Returns a CONFIGRET code.
  150. --*/
  151. {
  152. CONFIGRET Status = CR_SUCCESS;
  153. NTSTATUS ntStatus;
  154. ULONG ulAnsiStringLen = 0;
  155. try {
  156. //
  157. // Validate parameters
  158. //
  159. if ((!ARGUMENT_PRESENT(AnsiStringLen)) ||
  160. (!ARGUMENT_PRESENT(AnsiString)) && (*AnsiStringLen != 0)) {
  161. Status = CR_INVALID_POINTER;
  162. goto Clean0;
  163. }
  164. //
  165. // Determine the size required for the ANSI string representation.
  166. //
  167. ntStatus = RtlUnicodeToMultiByteSize(&ulAnsiStringLen,
  168. UnicodeString,
  169. UnicodeStringLen);
  170. if (!NT_SUCCESS(ntStatus)) {
  171. Status = CR_FAILURE;
  172. goto Clean0;
  173. }
  174. if ((!ARGUMENT_PRESENT(AnsiString)) ||
  175. (*AnsiStringLen < ulAnsiStringLen)) {
  176. *AnsiStringLen = ulAnsiStringLen;
  177. Status = CR_BUFFER_SMALL;
  178. goto Clean0;
  179. }
  180. //
  181. // Perform the conversion.
  182. //
  183. ntStatus = RtlUnicodeToMultiByteN(AnsiString,
  184. *AnsiStringLen,
  185. &ulAnsiStringLen,
  186. UnicodeString,
  187. UnicodeStringLen);
  188. ASSERT(NT_SUCCESS(ntStatus));
  189. ASSERT(ulAnsiStringLen <= *AnsiStringLen);
  190. if (!NT_SUCCESS(ntStatus)) {
  191. Status = CR_FAILURE;
  192. }
  193. *AnsiStringLen = ulAnsiStringLen;
  194. Clean0:
  195. NOTHING;
  196. } except(EXCEPTION_EXECUTE_HANDLER) {
  197. Status = CR_FAILURE;
  198. }
  199. return Status;
  200. } // PnPUnicodeToMultiByte
  201. CONFIGRET
  202. PnPMultiByteToUnicode(
  203. IN PSTR AnsiString,
  204. IN ULONG AnsiStringLen,
  205. OUT PWSTR UnicodeString OPTIONAL,
  206. IN OUT PULONG UnicodeStringLen
  207. )
  208. /*++
  209. Routine Description:
  210. Convert a string from ansi to unicode.
  211. Arguments:
  212. AnsiString - Supplies string to be converted.
  213. AnsiStringLen - Specifies the size, in bytes, of the string to be
  214. converted.
  215. UnicodeString - Optionally, supplies a buffer to receive the Unicode
  216. string.
  217. UnicodeStringLen - Supplies the address of a variable that contains the
  218. size, in bytes, of the buffer pointed to by UnicodeString.
  219. This API replaces the initial size with the number of
  220. bytes of data copied to the buffer. If the variable is
  221. initially zero, the API replaces it with the buffer size
  222. needed to receive all the registry data. In this case,
  223. the UnicodeString parameter is ignored.
  224. Return Value:
  225. Returns a CONFIGRET code.
  226. --*/
  227. {
  228. CONFIGRET Status = CR_SUCCESS;
  229. NTSTATUS ntStatus;
  230. ULONG ulUnicodeStringLen = 0;
  231. try {
  232. //
  233. // Validate parameters
  234. //
  235. if ((!ARGUMENT_PRESENT(UnicodeStringLen)) ||
  236. (!ARGUMENT_PRESENT(UnicodeString)) && (*UnicodeStringLen != 0)) {
  237. Status = CR_INVALID_POINTER;
  238. goto Clean0;
  239. }
  240. //
  241. // Determine the size required for the ANSI string representation.
  242. //
  243. ntStatus = RtlMultiByteToUnicodeSize(&ulUnicodeStringLen,
  244. AnsiString,
  245. AnsiStringLen);
  246. if (!NT_SUCCESS(ntStatus)) {
  247. Status = CR_FAILURE;
  248. goto Clean0;
  249. }
  250. if ((!ARGUMENT_PRESENT(UnicodeString)) ||
  251. (*UnicodeStringLen < ulUnicodeStringLen)) {
  252. *UnicodeStringLen = ulUnicodeStringLen;
  253. Status = CR_BUFFER_SMALL;
  254. goto Clean0;
  255. }
  256. //
  257. // Perform the conversion.
  258. //
  259. ntStatus = RtlMultiByteToUnicodeN(UnicodeString,
  260. *UnicodeStringLen,
  261. &ulUnicodeStringLen,
  262. AnsiString,
  263. AnsiStringLen);
  264. ASSERT(NT_SUCCESS(ntStatus));
  265. ASSERT(ulUnicodeStringLen <= *UnicodeStringLen);
  266. if (!NT_SUCCESS(ntStatus)) {
  267. Status = CR_FAILURE;
  268. }
  269. *UnicodeStringLen = ulUnicodeStringLen;
  270. Clean0:
  271. NOTHING;
  272. } except(EXCEPTION_EXECUTE_HANDLER) {
  273. Status = CR_FAILURE;
  274. }
  275. return Status;
  276. } // PnPMultiByteToUnicode
  277. BOOL
  278. PnPRetrieveMachineName(
  279. IN HMACHINE hMachine,
  280. OUT LPWSTR pszMachineName
  281. )
  282. /*++
  283. Routine Description:
  284. Optimized version of PnPConnect, only returns the machine name
  285. associated with this connection.
  286. Arguments:
  287. hMachine - Information about this connection
  288. pszMachineName - Returns machine name specified when CM_Connect_Machine
  289. was called.
  290. ** THIS BUFFER MUST BE AT LEAST (MAX_PATH + 3) CHARACTERS LONG. **
  291. Return Value:
  292. Return TRUE if the function succeeds and FALSE if it fails.
  293. --*/
  294. {
  295. BOOL Status = TRUE;
  296. try {
  297. if (hMachine == NULL) {
  298. //
  299. // local machine scenario
  300. //
  301. // use the global local machine name string that was filled
  302. // when the DLL initialized.
  303. //
  304. if (FAILED(StringCchCopy(
  305. pszMachineName,
  306. MAX_PATH + 3,
  307. LocalMachineNameNetBIOS))) {
  308. Status = FALSE;
  309. goto Clean0;
  310. }
  311. } else {
  312. //
  313. // remote machine scenario
  314. //
  315. // validate the machine handle.
  316. //
  317. if (((PPNP_MACHINE)hMachine)->ulSignature != (ULONG)MACHINE_HANDLE_SIGNATURE) {
  318. Status = FALSE;
  319. goto Clean0;
  320. }
  321. //
  322. // use information within the hMachine handle to fill in the
  323. // machine name. The hMachine info was set on a previous call
  324. // to CM_Connect_Machine.
  325. //
  326. if (FAILED(StringCchCopy(
  327. pszMachineName,
  328. MAX_PATH + 3,
  329. ((PPNP_MACHINE)hMachine)->szMachineName))) {
  330. Status = FALSE;
  331. goto Clean0;
  332. }
  333. }
  334. Clean0:
  335. NOTHING;
  336. } except(EXCEPTION_EXECUTE_HANDLER) {
  337. Status = FALSE;
  338. }
  339. return Status;
  340. } // PnPRetrieveMachineName
  341. BOOL
  342. PnPGetVersion(
  343. IN HMACHINE hMachine,
  344. IN WORD * pwVersion
  345. )
  346. /*++
  347. Routine Description:
  348. This routine returns the internal server version for the specified machine
  349. connection, as returned by the RPC server interface routine
  350. PNP_GetVersionInternal. If the PNP_GetVersionInternal interface does not
  351. exist on the specified machine, this routine returns the version as reported
  352. by PNP_GetVersion.
  353. Arguments:
  354. hMachine - Information about this connection
  355. pwVersion - Receives the internal server version.
  356. Return Value:
  357. Return TRUE if the function succeeds and FALSE if it fails.
  358. Notes:
  359. The version reported by PNP_GetVersion is defined to be constant, at 0x0400.
  360. The version returned by PNP_GetVersionInternal may change with each release
  361. of the product, starting with 0x0501 for Windows NT 5.1.
  362. --*/
  363. {
  364. BOOL Status = TRUE;
  365. handle_t hBinding = NULL;
  366. CONFIGRET crStatus;
  367. WORD wVersionInternal;
  368. try {
  369. if (pwVersion == NULL) {
  370. Status = FALSE;
  371. goto Clean0;
  372. }
  373. if (hMachine == NULL) {
  374. //
  375. // local machine scenario
  376. //
  377. if (LocalServerVersion != 0) {
  378. //
  379. // local server version has already been retrieved.
  380. //
  381. *pwVersion = LocalServerVersion;
  382. } else {
  383. //
  384. // retrieve binding handle for the local machine.
  385. //
  386. if (!PnPGetGlobalHandles(hMachine, NULL, &hBinding)) {
  387. Status = FALSE;
  388. goto Clean0;
  389. }
  390. ASSERT(hBinding);
  391. //
  392. // initialize the version supplied to the internal client
  393. // version, in case the server wants to adjust the response
  394. // based on the client version.
  395. //
  396. wVersionInternal = (WORD)CFGMGR32_VERSION_INTERNAL;
  397. //
  398. // No special privileges are required by the server
  399. //
  400. RpcTryExcept {
  401. //
  402. // call rpc service entry point
  403. //
  404. crStatus = PNP_GetVersionInternal(
  405. hBinding, // rpc binding
  406. &wVersionInternal); // internal server version
  407. }
  408. RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
  409. KdPrintEx((DPFLTR_PNPMGR_ID,
  410. DBGF_WARNINGS,
  411. "PNP_GetVersionInternal caused an exception (%d)\n",
  412. RpcExceptionCode()));
  413. crStatus = MapRpcExceptionToCR(RpcExceptionCode());
  414. }
  415. RpcEndExcept
  416. if (crStatus == CR_SUCCESS) {
  417. //
  418. // PNP_GetVersionInternal exists on NT 5.1 and later.
  419. //
  420. ASSERT(wVersionInternal >= (WORD)0x0501);
  421. //
  422. // initialize the global local server version.
  423. //
  424. LocalServerVersion = *pwVersion = wVersionInternal;
  425. } else {
  426. //
  427. // we successfully retrieved a local binding handle, but
  428. // PNP_GetVersionInternal failed for some reason other than
  429. // the server not being available.
  430. //
  431. ASSERT(0);
  432. //
  433. // although we know this version of the client should match
  434. // a version of the server where PNP_GetVersionInternal is
  435. // available, it's technically possible (though unsupported)
  436. // that this client is communicating with a downlevel server
  437. // on the local machine, so we'll have to resort to calling
  438. // PNP_GetVersion.
  439. //
  440. //
  441. // No special privileges are required by the server
  442. //
  443. RpcTryExcept {
  444. //
  445. // call rpc service entry point
  446. //
  447. crStatus = PNP_GetVersion(
  448. hBinding, // rpc binding
  449. &wVersionInternal); // server version
  450. }
  451. RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
  452. KdPrintEx((DPFLTR_PNPMGR_ID,
  453. DBGF_WARNINGS,
  454. "PNP_GetVersion caused an exception (%d)\n",
  455. RpcExceptionCode()));
  456. crStatus = MapRpcExceptionToCR(RpcExceptionCode());
  457. }
  458. RpcEndExcept
  459. if (crStatus == CR_SUCCESS) {
  460. //
  461. // PNP_GetVersion should always return 0x0400 on all servers.
  462. //
  463. ASSERT(wVersionInternal == (WORD)0x0400);
  464. //
  465. // initialize the global local server version.
  466. //
  467. LocalServerVersion = *pwVersion = wVersionInternal;
  468. } else {
  469. //
  470. // nothing more we can do here but fail.
  471. //
  472. ASSERT(0);
  473. Status = FALSE;
  474. }
  475. }
  476. }
  477. } else {
  478. //
  479. // remote machine scenario
  480. //
  481. // validate the machine handle.
  482. //
  483. if (((PPNP_MACHINE)hMachine)->ulSignature != (ULONG)MACHINE_HANDLE_SIGNATURE) {
  484. Status = FALSE;
  485. goto Clean0;
  486. }
  487. //
  488. // use information within the hMachine handle to fill in the
  489. // version. The hMachine info was set on a previous call to
  490. // CM_Connect_Machine.
  491. //
  492. *pwVersion = ((PPNP_MACHINE)hMachine)->wVersion;
  493. }
  494. Clean0:
  495. NOTHING;
  496. } except(EXCEPTION_EXECUTE_HANDLER) {
  497. Status = FALSE;
  498. }
  499. return Status;
  500. } // PnPGetVersion
  501. BOOL
  502. PnPGetGlobalHandles(
  503. IN HMACHINE hMachine,
  504. OUT PVOID *phStringTable, OPTIONAL
  505. OUT PVOID *phBindingHandle OPTIONAL
  506. )
  507. /*++
  508. Routine Description:
  509. This routine retrieves a handle to the string table and/or the rpc binding
  510. handle for the specified server machine connection.
  511. Arguments:
  512. hMachine - Specifies a server machine connection handle, as returned
  513. by CM_Connect_Machine.
  514. phStringTable - Optionally, specifies an address to receive a handle to
  515. the string table for the specified server machine
  516. connection.
  517. phBindingHandle - Optionally, specifies an address to receive the RPC
  518. binding handle for the specifies server machine
  519. connection.
  520. Return value:
  521. Returns TRUE if successful, FALSE otherwise.
  522. --*/
  523. {
  524. BOOL bStatus = TRUE;
  525. try {
  526. if (ARGUMENT_PRESENT(phStringTable)) {
  527. if (hMachine == NULL) {
  528. //------------------------------------------------------
  529. // Retrieve String Table Handle for the local machine
  530. //-------------------------------------------------------
  531. EnterCriticalSection(&StringTableCriticalSection);
  532. if (hLocalStringTable != NULL) {
  533. //
  534. // local string table has already been created
  535. //
  536. *phStringTable = hLocalStringTable;
  537. } else {
  538. //
  539. // first time, initialize the local string table
  540. //
  541. hLocalStringTable = pSetupStringTableInitialize();
  542. if (hLocalStringTable != NULL) {
  543. //
  544. // No matter how the string table is implemented, I never
  545. // want to have a string id of zero - this would generate
  546. // an invalid devinst. So, add a small priming string just
  547. // to be safe.
  548. //
  549. pSetupStringTableAddString(hLocalStringTable,
  550. PRIMING_STRING,
  551. STRTAB_CASE_SENSITIVE);
  552. *phStringTable = hLocalStringTable;
  553. } else {
  554. KdPrintEx((DPFLTR_PNPMGR_ID,
  555. DBGF_ERRORS,
  556. "CFGMGR32: failed to initialize local string table\n"));
  557. *phStringTable = NULL;
  558. }
  559. }
  560. LeaveCriticalSection(&StringTableCriticalSection);
  561. if (*phStringTable == NULL) {
  562. bStatus = FALSE;
  563. goto Clean0;
  564. }
  565. } else {
  566. //-------------------------------------------------------
  567. // Retrieve String Table Handle for the remote machine
  568. //-------------------------------------------------------
  569. //
  570. // validate the machine handle.
  571. //
  572. if (((PPNP_MACHINE)hMachine)->ulSignature != (ULONG)MACHINE_HANDLE_SIGNATURE) {
  573. bStatus = FALSE;
  574. goto Clean0;
  575. }
  576. //
  577. // use information within the hMachine handle to set the string
  578. // table handle. The hMachine info was set on a previous call
  579. // to CM_Connect_Machine.
  580. //
  581. *phStringTable = ((PPNP_MACHINE)hMachine)->hStringTable;
  582. }
  583. }
  584. if (ARGUMENT_PRESENT(phBindingHandle)) {
  585. if (hMachine == NULL) {
  586. //-------------------------------------------------------
  587. // Retrieve Binding Handle for the local machine
  588. //-------------------------------------------------------
  589. EnterCriticalSection(&BindingCriticalSection);
  590. if (hLocalBindingHandle != NULL) {
  591. //
  592. // local binding handle has already been set
  593. //
  594. *phBindingHandle = hLocalBindingHandle;
  595. } else {
  596. //
  597. // first time, explicitly force binding to local machine
  598. //
  599. pnp_handle = PNP_HANDLE_bind(NULL); // set rpc global
  600. if (pnp_handle != NULL) {
  601. *phBindingHandle = hLocalBindingHandle = (PVOID)pnp_handle;
  602. } else {
  603. KdPrintEx((DPFLTR_PNPMGR_ID,
  604. DBGF_ERRORS,
  605. "CFGMGR32: failed to initialize local binding handle\n"));
  606. *phBindingHandle = NULL;
  607. }
  608. }
  609. LeaveCriticalSection(&BindingCriticalSection);
  610. if (*phBindingHandle == NULL) {
  611. bStatus = FALSE;
  612. goto Clean0;
  613. }
  614. } else {
  615. //-------------------------------------------------------
  616. // Retrieve Binding Handle for the remote machine
  617. //-------------------------------------------------------
  618. //
  619. // validate the machine handle.
  620. //
  621. if (((PPNP_MACHINE)hMachine)->ulSignature != (ULONG)MACHINE_HANDLE_SIGNATURE) {
  622. bStatus = FALSE;
  623. goto Clean0;
  624. }
  625. //
  626. // use information within the hMachine handle to set the
  627. // binding handle. The hMachine info was set on a previous call
  628. // to CM_Connect_Machine.
  629. //
  630. *phBindingHandle = ((PPNP_MACHINE)hMachine)->hBindingHandle;
  631. }
  632. }
  633. Clean0:
  634. NOTHING;
  635. } except(EXCEPTION_EXECUTE_HANDLER) {
  636. bStatus = FALSE;
  637. }
  638. return bStatus;
  639. } // PnpGetGlobalHandles
  640. HANDLE
  641. PnPEnablePrivileges(
  642. IN PULONG Privileges,
  643. IN ULONG PrivilegeCount
  644. )
  645. /*++
  646. Routine Description:
  647. This routine enables the specified privileges in the thread token for the
  648. calling thread. If no thread exists (not impersonating), the process token
  649. is used.
  650. Arguments:
  651. Privileges - Specifies a list of privileges to enable.
  652. PrivilegeCount - Specifies the number of privileges in the list.
  653. Return value:
  654. If successful, returns a handle to the previous thread token (if it exists)
  655. or NULL, to indicate that the thread did not previously have a token. If
  656. successful, ReleasePrivileges should be called to ensure that the previous
  657. thread token (if exists) is replaced on the calling thread and that the
  658. handle is closed.
  659. If unsuccessful, INVALID_HANDLE_VALUE is returned.
  660. Notes:
  661. This routine is intended to operate on well-known privileges only; no lookup
  662. of privilege names is done by this routine; it assumes that the privilege
  663. LUID value for well-known privileges can be constructed from it's
  664. corresponding ULONG privilege value, via RtlConvertUlongToLuid.
  665. This is true for SE_LOAD_DRIVER_PRIVILEGE and SE_UNDOCK_PRIVILEGE, which are
  666. the only privilege values CFGMGR32 uses this routine to enable. If
  667. additional pricileges are used where that is not the case, this routine may
  668. be changed to receive an array of privilege names - with the corresponding
  669. privilege LUID value lookup performed for each.
  670. --*/
  671. {
  672. BOOL bResult;
  673. HANDLE hToken, hNewToken;
  674. HANDLE hOriginalThreadToken;
  675. PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL;
  676. SECURITY_ATTRIBUTES sa;
  677. PTOKEN_PRIVILEGES pTokenPrivileges;
  678. ULONG nBufferSize, i;
  679. //
  680. // Validate parameters
  681. //
  682. if ((!ARGUMENT_PRESENT(Privileges)) || (PrivilegeCount == 0)) {
  683. return INVALID_HANDLE_VALUE;
  684. }
  685. //
  686. // Note that TOKEN_PRIVILEGES includes a single LUID_AND_ATTRIBUTES
  687. //
  688. nBufferSize =
  689. sizeof(TOKEN_PRIVILEGES) +
  690. ((PrivilegeCount - 1) * sizeof(LUID_AND_ATTRIBUTES));
  691. pTokenPrivileges = (PTOKEN_PRIVILEGES)
  692. pSetupMalloc(nBufferSize);
  693. if (pTokenPrivileges == NULL) {
  694. return INVALID_HANDLE_VALUE;
  695. }
  696. //
  697. // Initialize the Privileges Structure
  698. //
  699. pTokenPrivileges->PrivilegeCount = PrivilegeCount;
  700. for (i = 0; i < PrivilegeCount; i++) {
  701. pTokenPrivileges->Privileges[i].Luid = RtlConvertUlongToLuid(Privileges[i]);
  702. pTokenPrivileges->Privileges[i].Attributes = SE_PRIVILEGE_ENABLED;
  703. }
  704. //
  705. // Open the thread token for TOKEN_DUPLICATE access. We also required
  706. // READ_CONTROL access to read the security descriptor information.
  707. //
  708. hToken = hOriginalThreadToken = INVALID_HANDLE_VALUE;
  709. bResult =
  710. OpenThreadToken(
  711. GetCurrentThread(),
  712. TOKEN_DUPLICATE | READ_CONTROL,
  713. FALSE,
  714. &hToken);
  715. if (bResult) {
  716. //
  717. // Remember the previous thread token
  718. //
  719. hOriginalThreadToken = hToken;
  720. } else if (GetLastError() == ERROR_NO_TOKEN) {
  721. //
  722. // No thread token - open the process token.
  723. //
  724. //
  725. // Note that if we failed to open the thread token for any other reason,
  726. // we don't want to open the process token instead. The caller is
  727. // impersonating, and opening the process token would defeat that.
  728. // We'll simply not enable any privileges, and the caller will have to
  729. // pass any required privilege checks on the merit of their existing
  730. // thread token.
  731. //
  732. bResult =
  733. OpenProcessToken(
  734. GetCurrentProcess(),
  735. TOKEN_DUPLICATE | READ_CONTROL,
  736. &hToken);
  737. }
  738. if (bResult) {
  739. ASSERT((hToken != NULL) && (hToken != INVALID_HANDLE_VALUE));
  740. //
  741. // Copy the security descriptor from whichever token we were able to
  742. // retrieve so that we can apply it to the duplicated token.
  743. //
  744. // Note that if we cannot retrieve the security descriptor for the
  745. // token, we will not continue on below to duplicate it with the default
  746. // security descriptor, since it may be more restrictive than that of
  747. // the original token, and may prevent the client from removing the
  748. // impersonation token from the thread when restoring privileges.
  749. //
  750. bResult =
  751. GetKernelObjectSecurity(
  752. hToken,
  753. DACL_SECURITY_INFORMATION,
  754. NULL,
  755. 0,
  756. &nBufferSize);
  757. if ((!bResult) &&
  758. (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
  759. ASSERT(nBufferSize > 0);
  760. pSecurityDescriptor = (PSECURITY_DESCRIPTOR)
  761. pSetupMalloc(nBufferSize);
  762. if (pSecurityDescriptor != NULL) {
  763. bResult =
  764. GetKernelObjectSecurity(
  765. hToken,
  766. DACL_SECURITY_INFORMATION,
  767. pSecurityDescriptor,
  768. nBufferSize,
  769. &nBufferSize);
  770. }
  771. } else {
  772. bResult = FALSE;
  773. }
  774. }
  775. if (bResult) {
  776. ASSERT(pSecurityDescriptor != NULL);
  777. //
  778. // Duplicate whichever token we were able to retrieve, using the
  779. // token's security descriptor.
  780. //
  781. ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
  782. sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  783. sa.lpSecurityDescriptor = pSecurityDescriptor;
  784. sa.bInheritHandle = FALSE;
  785. bResult =
  786. DuplicateTokenEx(
  787. hToken,
  788. TOKEN_IMPERSONATE | TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
  789. &sa, // PSECURITY_ATTRIBUTES
  790. SecurityImpersonation, // SECURITY_IMPERSONATION_LEVEL
  791. TokenImpersonation, // TokenType
  792. &hNewToken); // Duplicate token
  793. if (bResult) {
  794. ASSERT((hNewToken != NULL) && (hNewToken != INVALID_HANDLE_VALUE));
  795. //
  796. // Adjust the privileges of the duplicated token. We don't care
  797. // about its previous state because we still have the original
  798. // token.
  799. //
  800. bResult =
  801. AdjustTokenPrivileges(
  802. hNewToken, // TokenHandle
  803. FALSE, // DisableAllPrivileges
  804. pTokenPrivileges, // NewState
  805. 0, // BufferLength
  806. NULL, // PreviousState
  807. NULL); // ReturnLength
  808. if (bResult) {
  809. //
  810. // Begin impersonating with the new token
  811. //
  812. bResult =
  813. SetThreadToken(
  814. NULL,
  815. hNewToken);
  816. }
  817. CloseHandle(hNewToken);
  818. }
  819. }
  820. //
  821. // If something failed, don't return a token
  822. //
  823. if (!bResult) {
  824. hOriginalThreadToken = INVALID_HANDLE_VALUE;
  825. }
  826. //
  827. // Close the original token if we aren't returning it
  828. //
  829. if ((hOriginalThreadToken == INVALID_HANDLE_VALUE) &&
  830. (hToken != INVALID_HANDLE_VALUE)) {
  831. CloseHandle(hToken);
  832. }
  833. //
  834. // If we succeeded, but there was no original thread token, return NULL.
  835. // PnPRestorePrivileges will simply remove the current thread token.
  836. //
  837. if (bResult && (hOriginalThreadToken == INVALID_HANDLE_VALUE)) {
  838. hOriginalThreadToken = NULL;
  839. }
  840. if (pSecurityDescriptor != NULL) {
  841. pSetupFree(pSecurityDescriptor);
  842. }
  843. pSetupFree(pTokenPrivileges);
  844. return hOriginalThreadToken;
  845. } // PnPEnablePrivileges
  846. VOID
  847. PnPRestorePrivileges(
  848. IN HANDLE hToken
  849. )
  850. /*++
  851. Routine Description:
  852. This routine restores the privileges of the calling thread to their state
  853. prior to a corresponding call to PnPEnablePrivileges.
  854. Arguments:
  855. hToken - Return value from corresponding call to PnPEnablePrivileges.
  856. Return value:
  857. None.
  858. Notes:
  859. If the corresponding call to PnPEnablePrivileges returned a handle to the
  860. previous thread token, this routine will restore it, and close the handle.
  861. If PnPEnablePrivileges returned NULL, no thread token previously existed.
  862. This routine will remove any existing token from the thread.
  863. If PnPEnablePrivileges returned INVALID_HANDLE_VALUE, the attempt to enable
  864. the specified privileges failed, but the previous state of the thread was
  865. not modified. This routine does nothing.
  866. --*/
  867. {
  868. BOOL bResult;
  869. //
  870. // First, check if we actually need to do anything for this thread.
  871. //
  872. if (hToken != INVALID_HANDLE_VALUE) {
  873. //
  874. // Call SetThreadToken for the current thread with the specified hToken.
  875. // If the handle value is NULL, SetThreadToken will remove the current
  876. // thread token from the thread. Ignore the return, there's nothing we
  877. // can do about it.
  878. //
  879. bResult = SetThreadToken(NULL, hToken);
  880. if (hToken != NULL) {
  881. //
  882. // Close the handle to the token.
  883. //
  884. CloseHandle(hToken);
  885. }
  886. }
  887. return;
  888. } // PnPRestorePrivileges
  889. CONFIGRET
  890. IsRemoteServiceRunning(
  891. IN LPCWSTR UNCServerName,
  892. IN LPCWSTR ServiceName
  893. )
  894. /*++
  895. Routine Description:
  896. This routine connects to the active service database of the Service Control
  897. Manager (SCM) on the machine specified and returns whether or not the
  898. specified service is running.
  899. Arguments:
  900. UNCServerName - Specifies the name of the remote machine.
  901. ServiceName - Specifies the name of the service whose status is to be
  902. queried.
  903. Return value:
  904. Returns TRUE if the specified service is installed on the remote machine and
  905. is currently in the SERVICE_RUNNING state, FALSE otherwise.
  906. --*/
  907. {
  908. CONFIGRET Status = CR_SUCCESS;
  909. DWORD Err;
  910. SC_HANDLE hSCManager = NULL, hService = NULL;
  911. SERVICE_STATUS ServiceStatus;
  912. //
  913. // Open the Service Control Manager
  914. //
  915. hSCManager = OpenSCManager(
  916. UNCServerName, // computer name
  917. SERVICES_ACTIVE_DATABASE, // SCM database name
  918. SC_MANAGER_CONNECT // access type
  919. );
  920. if (hSCManager == NULL) {
  921. Err = GetLastError();
  922. KdPrintEx((DPFLTR_PNPMGR_ID,
  923. DBGF_WARNINGS,
  924. "CFGMGR32: OpenSCManager failed, error = %d\n",
  925. Err));
  926. if (Err == ERROR_ACCESS_DENIED) {
  927. Status = CR_ACCESS_DENIED;
  928. } else {
  929. Status = CR_MACHINE_UNAVAILABLE;
  930. }
  931. goto Clean0;
  932. }
  933. //
  934. // Open the service
  935. //
  936. hService = OpenService(
  937. hSCManager, // handle to SCM database
  938. ServiceName, // service name
  939. SERVICE_QUERY_STATUS // access type
  940. );
  941. if (hService == NULL) {
  942. Err = GetLastError();
  943. KdPrintEx((DPFLTR_PNPMGR_ID,
  944. DBGF_WARNINGS,
  945. "CFGMGR32: OpenService failed, error = %d\n",
  946. Err));
  947. if (Err == ERROR_ACCESS_DENIED) {
  948. Status = CR_ACCESS_DENIED;
  949. } else {
  950. Status = CR_NO_CM_SERVICES;
  951. }
  952. goto Clean0;
  953. }
  954. //
  955. // Query the service status
  956. //
  957. if (!QueryServiceStatus(hService,
  958. &ServiceStatus)) {
  959. Err = GetLastError();
  960. KdPrintEx((DPFLTR_PNPMGR_ID,
  961. DBGF_WARNINGS,
  962. "CFGMGR32: QueryServiceStatus failed, error = %d\n",
  963. Err));
  964. if (Err == ERROR_ACCESS_DENIED) {
  965. Status = CR_ACCESS_DENIED;
  966. } else {
  967. Status = CR_NO_CM_SERVICES;
  968. }
  969. goto Clean0;
  970. }
  971. //
  972. // Check if the service is running.
  973. //
  974. if (ServiceStatus.dwCurrentState != SERVICE_RUNNING) {
  975. Status = CR_NO_CM_SERVICES;
  976. goto Clean0;
  977. }
  978. Clean0:
  979. if (hService) {
  980. CloseServiceHandle(hService);
  981. }
  982. if (hSCManager) {
  983. CloseServiceHandle(hSCManager);
  984. }
  985. return Status;
  986. } // IsRemoteServiceRunning