Source code of Windows XP (NT5)
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.

798 lines
25 KiB

  1. /*++
  2. Copyright (c) 1995-1998 Microsoft Corporation
  3. Module Name:
  4. crypto.c
  5. Abstract:
  6. Module to install/upgrade cryptography (CAPI).
  7. Author:
  8. Ted Miller (tedm) 4-Aug-1995
  9. Revision History:
  10. Lonny McMichael (lonnym) 1-May-98 Added code to trust test root certificate.
  11. --*/
  12. #include "setupp.h"
  13. #pragma hdrstop
  14. //
  15. // Default post-setup system policies for driver signing and non-driver signing
  16. //
  17. #define DEFAULT_DRVSIGN_POLICY DRIVERSIGN_WARNING
  18. #define DEFAULT_NONDRVSIGN_POLICY DRIVERSIGN_NONE
  19. //
  20. // Define name of default microsoft capi provider and signature file
  21. //
  22. #define MS_DEF_SIG L"RSABASE.SIG"
  23. #define MS_DEF_DLL L"RSABASE.DLL"
  24. #define MS_REG_KEY1 L"SOFTWARE\\Microsoft\\Cryptography\\Defaults\\Provider\\" MS_DEF_PROV
  25. #define MS_REG_KEY2 L"SOFTWARE\\Microsoft\\Cryptography\\Defaults\\Provider Types\\Type 001"
  26. //
  27. // Items that get set up in registry for ms provider.
  28. // Do not change the order of these without also changing the code in
  29. // RsaSigToRegistry().
  30. //
  31. #if 0 // no longer needed
  32. REGVALITEM CryptoProviderItems[3] = { { L"Image Path",
  33. MS_DEF_DLL,
  34. sizeof(MS_DEF_DLL),
  35. REG_SZ
  36. },
  37. { L"Type",
  38. NULL,
  39. sizeof(DWORD),
  40. REG_DWORD
  41. },
  42. { L"Signature",
  43. NULL,
  44. 0,
  45. REG_BINARY
  46. }
  47. };
  48. REGVALITEM CryptoProviderTypeItems[1] = { { L"Name",
  49. MS_DEF_PROV,
  50. sizeof(MS_DEF_PROV),
  51. REG_SZ
  52. }
  53. };
  54. #endif // no longer needed
  55. #if 0 // obsolete routine
  56. DWORD
  57. RsaSigToRegistry(
  58. VOID
  59. )
  60. /*++
  61. Routine Description:
  62. This routine transfers the contents of the rsa signature file
  63. (%systemroot%\system32\rsabase.sig) into the registry,
  64. then deletes the signature file.
  65. Arguments:
  66. None.
  67. Returns:
  68. Win32 error code indicating outcome.
  69. --*/
  70. {
  71. WCHAR SigFile[MAX_PATH];
  72. DWORD FileSize;
  73. HANDLE FileHandle;
  74. HANDLE MapHandle;
  75. DWORD d;
  76. PVOID p;
  77. DWORD One;
  78. //
  79. // Form name of signature file.
  80. //
  81. lstrcpy(SigFile,LegacySourcePath);
  82. pSetupConcatenatePaths(SigFile,MS_DEF_SIG,MAX_PATH,NULL);
  83. //
  84. // Open and map signature file.
  85. //
  86. d = pSetupOpenAndMapFileForRead(
  87. SigFile,
  88. &FileSize,
  89. &FileHandle,
  90. &MapHandle,
  91. &p
  92. );
  93. if(d == NO_ERROR) {
  94. //
  95. // Type gets set to 1.
  96. //
  97. One = 1;
  98. CryptoProviderItems[1].Data = &One;
  99. //
  100. // Set up binary data.
  101. //
  102. CryptoProviderItems[2].Data = p;
  103. CryptoProviderItems[2].Size = FileSize;
  104. //
  105. // Transfer data to registry. Gaurd w/try/except in case of
  106. // in-page errors.
  107. //
  108. try {
  109. d = (DWORD)SetGroupOfValues(HKEY_LOCAL_MACHINE,MS_REG_KEY1,CryptoProviderItems,3);
  110. } except(EXCEPTION_EXECUTE_HANDLER) {
  111. d = ERROR_READ_FAULT;
  112. }
  113. //
  114. // Set additional piece of registry data.
  115. //
  116. if(d == NO_ERROR) {
  117. d = (DWORD)SetGroupOfValues(
  118. HKEY_LOCAL_MACHINE,
  119. MS_REG_KEY2,
  120. CryptoProviderTypeItems,
  121. 1
  122. );
  123. }
  124. //
  125. // Clean up file mapping.
  126. //
  127. pSetupUnmapAndCloseFile(FileHandle,MapHandle,p);
  128. }
  129. return(d);
  130. }
  131. #endif // obsolete routine
  132. BOOL
  133. InstallOrUpgradeCapi(
  134. VOID
  135. )
  136. {
  137. #if 1
  138. return RegisterOleControls(MainWindowHandle, SyssetupInf, NULL, 0, 0, L"RegistrationCrypto");
  139. #else // obsolete code
  140. DWORD d;
  141. d = RsaSigToRegistry();
  142. if(d != NO_ERROR) {
  143. SetuplogError(
  144. LogSevError,
  145. SETUPLOG_USE_MESSAGEID,
  146. MSG_LOG_CRYPTO_1,
  147. d,NULL,NULL);
  148. }
  149. return(d == NO_ERROR);
  150. #endif // obsolete code
  151. }
  152. DWORD
  153. SetupAddOrRemoveTestCertificate(
  154. IN PCWSTR TestCertName, OPTIONAL
  155. IN HINF InfToUse OPTIONAL
  156. )
  157. /*++
  158. Routine Description:
  159. This routine adds the test certificate into the root certificate store, or
  160. removes the test certificate from the root store.
  161. Arguments:
  162. TestCertName - Optionally, supplies the name of the test certificate to be
  163. added. If this parameter is not specified, then the test certificates
  164. (both old and new) will be removed from the root store, if they exist.
  165. InfToUse - Optionally, supplies the INF to be used in retrieving source
  166. media information for the test certificate. If this parameter is not
  167. supplied (i.e., is NULL or INVALID_HANDLE_VALUE), then TestCertName is
  168. assumed to exist (a) in the platform-specific source path (if it's a
  169. simple filename) or (b) in the specified location (if it contains path
  170. information).
  171. This argument is ignored if TestCertName is NULL.
  172. Returns:
  173. If successful, the return value is NO_ERROR.
  174. If failure, the return value is a Win32 error code indicating the cause of
  175. the failure.
  176. --*/
  177. {
  178. PCCERT_CONTEXT pCertContext;
  179. HCERTSTORE hStore;
  180. DWORD Err = NO_ERROR, ret = NO_ERROR;
  181. DWORD FileSize;
  182. HANDLE FileHandle, MappingHandle;
  183. PVOID BaseAddress;
  184. WCHAR TempBuffer[MAX_PATH], DecompressedName[MAX_PATH];
  185. WCHAR FullPathName[MAX_PATH], PromptPath[MAX_PATH];
  186. BOOL FileInUse;
  187. UINT SourceId;
  188. PWSTR FilePart;
  189. DWORD FullPathNameLen;
  190. if(TestCertName) {
  191. LPSTR szUsages[] = { szOID_PKIX_KP_CODE_SIGNING,
  192. szOID_WHQL_CRYPTO,
  193. szOID_NT5_CRYPTO };
  194. CERT_ENHKEY_USAGE EKU = {sizeof(szUsages)/sizeof(LPSTR), szUsages};
  195. CRYPT_DATA_BLOB CryptDataBlob = {0, NULL};
  196. //
  197. // The file may be compressed (i.e., testroot.ce_), so decompress it
  198. // into a temporary file in the windows directory.
  199. //
  200. if(!GetWindowsDirectory(TempBuffer, SIZECHARS(TempBuffer)) ||
  201. !GetTempFileName(TempBuffer, L"SETP", 0, DecompressedName)) {
  202. return GetLastError();
  203. }
  204. if(InfToUse && (InfToUse != INVALID_HANDLE_VALUE)) {
  205. //
  206. // We were passed a simple filename (e.g., "testroot.cer") which
  207. // exists in the platform-specific source path.
  208. //
  209. lstrcpy(FullPathName, LegacySourcePath);
  210. pSetupConcatenatePaths(FullPathName, TestCertName, SIZECHARS(FullPathName), NULL);
  211. SetupGetSourceFileLocation(
  212. InfToUse,
  213. NULL,
  214. TestCertName,
  215. &SourceId,
  216. NULL,
  217. 0,
  218. NULL
  219. );
  220. SetupGetSourceInfo(
  221. InfToUse,
  222. SourceId,
  223. SRCINFO_DESCRIPTION,
  224. TempBuffer,
  225. sizeof(TempBuffer),
  226. NULL
  227. );
  228. do{
  229. Err = DuSetupPromptForDisk (
  230. MainWindowHandle,
  231. NULL,
  232. TempBuffer,
  233. LegacySourcePath,
  234. TestCertName,
  235. NULL,
  236. IDF_CHECKFIRST | IDF_NODETAILS | IDF_NOSKIP | IDF_NOBROWSE,
  237. PromptPath,
  238. MAX_PATH,
  239. NULL
  240. );
  241. if( Err == DPROMPT_SUCCESS ){
  242. lstrcpy( FullPathName, PromptPath );
  243. pSetupConcatenatePaths(FullPathName, TestCertName, SIZECHARS(FullPathName), NULL);
  244. Err = SetupDecompressOrCopyFile(FullPathName,
  245. DecompressedName,
  246. NULL
  247. );
  248. }else
  249. Err = ERROR_CANCELLED;
  250. } while( Err == ERROR_NOT_READY );
  251. } else {
  252. //
  253. // If the filename is a simple filename, then assume it exists in
  254. // the platform-specific source path. Otherwise, assume it is a
  255. // fully-qualified path.
  256. //
  257. if(TestCertName == pSetupGetFileTitle(TestCertName)) {
  258. if (BuildPathToInstallationFile (TestCertName, FullPathName, SIZECHARS(FullPathName))) {
  259. Err = NO_ERROR;
  260. } else {
  261. Err = ERROR_INSUFFICIENT_BUFFER;
  262. }
  263. } else {
  264. //
  265. // The filename includes path information--look for the file in
  266. // the specified path.
  267. //
  268. FullPathNameLen = GetFullPathName(TestCertName,
  269. SIZECHARS(FullPathName),
  270. FullPathName,
  271. &FilePart
  272. );
  273. if(!FullPathNameLen) {
  274. Err = GetLastError();
  275. } else if(FullPathNameLen > SIZECHARS(FullPathName)) {
  276. Err = ERROR_INSUFFICIENT_BUFFER;
  277. } else {
  278. Err = NO_ERROR;
  279. }
  280. }
  281. if(Err == NO_ERROR) {
  282. Err = SetupDecompressOrCopyFile(FullPathName,
  283. DecompressedName,
  284. NULL
  285. );
  286. }
  287. }
  288. if(Err != NO_ERROR) {
  289. return Err;
  290. }
  291. //
  292. // Map the specified .cer file into memory
  293. //
  294. Err = pSetupOpenAndMapFileForRead(DecompressedName,
  295. &FileSize,
  296. &FileHandle,
  297. &MappingHandle,
  298. &BaseAddress
  299. );
  300. if(Err != NO_ERROR) {
  301. DeleteFile(DecompressedName);
  302. return Err;
  303. }
  304. //
  305. // Create a cert context from an encoded blob (what we read from the
  306. // .cer file)
  307. //
  308. pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING,
  309. BaseAddress,
  310. FileSize
  311. );
  312. if(!pCertContext) {
  313. //
  314. // Get the last error before we potentially blow it away by
  315. // unmapping/closing our certificate file below...
  316. //
  317. Err = GetLastError();
  318. MYASSERT(Err != NO_ERROR);
  319. if(Err == NO_ERROR) {
  320. Err = ERROR_INVALID_DATA;
  321. }
  322. }
  323. //
  324. // We can unmap and close the test cert file now--we don't need it
  325. // anymore.
  326. //
  327. pSetupUnmapAndCloseFile(FileHandle, MappingHandle, BaseAddress);
  328. DeleteFile(DecompressedName);
  329. if(!pCertContext) {
  330. goto clean0;
  331. }
  332. //
  333. // to open the root store in HKLM
  334. //
  335. hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM,
  336. X509_ASN_ENCODING,
  337. (HCRYPTPROV)NULL,
  338. CERT_SYSTEM_STORE_LOCAL_MACHINE,
  339. L"ROOT"
  340. );
  341. if(!hStore) {
  342. Err = GetLastError();
  343. MYASSERT(Err != NO_ERROR);
  344. if(Err == NO_ERROR) {
  345. Err = ERROR_INVALID_DATA;
  346. }
  347. } else {
  348. //
  349. // Call CryptEncodeObject once to get the size of buffer required...
  350. //
  351. if(CryptEncodeObject(CRYPT_ASN_ENCODING,
  352. X509_ENHANCED_KEY_USAGE,
  353. &EKU,
  354. NULL,
  355. &(CryptDataBlob.cbData))) {
  356. MYASSERT(CryptDataBlob.cbData);
  357. //
  358. // OK, now we can allocate a buffer of the required size and
  359. // try again.
  360. //
  361. CryptDataBlob.pbData = MyMalloc(CryptDataBlob.cbData);
  362. if(CryptDataBlob.pbData) {
  363. if(CryptEncodeObject(CRYPT_ASN_ENCODING,
  364. X509_ENHANCED_KEY_USAGE,
  365. &EKU,
  366. CryptDataBlob.pbData,
  367. &(CryptDataBlob.cbData))) {
  368. Err = NO_ERROR;
  369. } else {
  370. Err = GetLastError();
  371. MYASSERT(Err != NO_ERROR);
  372. if(Err == NO_ERROR) {
  373. Err = ERROR_INVALID_DATA;
  374. }
  375. }
  376. } else {
  377. Err = ERROR_NOT_ENOUGH_MEMORY;
  378. }
  379. } else {
  380. Err = GetLastError();
  381. MYASSERT(Err != NO_ERROR);
  382. if(Err == NO_ERROR) {
  383. Err = ERROR_INVALID_DATA;
  384. }
  385. }
  386. if(Err == NO_ERROR) {
  387. if(!CertSetCertificateContextProperty(pCertContext,
  388. CERT_ENHKEY_USAGE_PROP_ID,
  389. 0,
  390. &CryptDataBlob)) {
  391. Err = GetLastError();
  392. MYASSERT(Err != NO_ERROR);
  393. if(Err == NO_ERROR) {
  394. Err = ERROR_INVALID_DATA;
  395. }
  396. }
  397. }
  398. //
  399. // to add cert to store
  400. //
  401. if(Err == NO_ERROR) {
  402. if(!CertAddCertificateContextToStore(hStore,
  403. pCertContext,
  404. CERT_STORE_ADD_USE_EXISTING,
  405. NULL)) {
  406. Err = GetLastError();
  407. MYASSERT(Err != NO_ERROR);
  408. if(Err == NO_ERROR) {
  409. Err = ERROR_INVALID_DATA;
  410. }
  411. }
  412. }
  413. CertCloseStore(hStore, 0);
  414. }
  415. CertFreeCertificateContext(pCertContext);
  416. if(CryptDataBlob.pbData) {
  417. MyFree(CryptDataBlob.pbData);
  418. }
  419. } else {
  420. //
  421. // deleting a cert
  422. // to open the root store in HKLM
  423. //
  424. hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM,
  425. X509_ASN_ENCODING,
  426. (HCRYPTPROV)NULL,
  427. CERT_SYSTEM_STORE_LOCAL_MACHINE,
  428. L"ROOT"
  429. );
  430. if(!hStore) {
  431. Err = GetLastError();
  432. MYASSERT(Err != NO_ERROR);
  433. if(Err == NO_ERROR) {
  434. Err = ERROR_INVALID_DATA;
  435. }
  436. } else {
  437. //
  438. // test root(s) sha1 hash
  439. //
  440. DWORD i;
  441. BYTE arHashData[2][20] = { {0x30, 0x0B, 0x97, 0x1A, 0x74, 0xF9, 0x7E, 0x09, 0x8B, 0x67, 0xA4, 0xFC, 0xEB, 0xBB, 0xF6, 0xB9, 0xAE, 0x2F, 0x40, 0x4C}, // old beta testroot.cer (used until just prior to RC3)
  442. {0x2B, 0xD6, 0x3D, 0x28, 0xD7, 0xBC, 0xD0, 0xE2, 0x51, 0x19, 0x5A, 0xEB, 0x51, 0x92, 0x43, 0xC1, 0x31, 0x42, 0xEB, 0xC3} }; // current beta testroot.cer (also used for OEM testsigning)
  443. CRYPT_HASH_BLOB hash;
  444. hash.cbData = sizeof(arHashData[0]);
  445. for(i = 0; i < 2; i++) {
  446. hash.pbData = arHashData[i];
  447. pCertContext = CertFindCertificateInStore(hStore,
  448. X509_ASN_ENCODING,
  449. 0,
  450. CERT_FIND_HASH,
  451. &hash,
  452. NULL
  453. );
  454. if(pCertContext) {
  455. //
  456. // We found the certificate, so we want to delete it.
  457. //
  458. if(!CertDeleteCertificateFromStore(pCertContext)) {
  459. Err = GetLastError();
  460. MYASSERT(Err != NO_ERROR);
  461. if(Err == NO_ERROR) {
  462. Err = ERROR_INVALID_DATA;
  463. }
  464. break;
  465. }
  466. }
  467. //
  468. // do not free context--the delete did it (even if it failed).
  469. //
  470. }
  471. CertCloseStore(hStore, 0);
  472. }
  473. }
  474. clean0:
  475. return Err;
  476. }
  477. VOID
  478. pSetupGetRealSystemTime(
  479. OUT LPSYSTEMTIME RealSystemTime
  480. );
  481. VOID
  482. SetCodeSigningPolicy(
  483. IN CODESIGNING_POLICY_TYPE PolicyType,
  484. IN BYTE NewPolicy,
  485. OUT PBYTE OldPolicy OPTIONAL
  486. )
  487. /*++
  488. Routine Description:
  489. This routine sets the specified codesigning policy type (either driver
  490. or non-driver signing) to a new value (ignore, warn, or block), and
  491. optionally returns the previous policy setting.
  492. Arguments:
  493. PolicyType - specifies what policy is to be set. May be either
  494. PolicyTypeDriverSigning or PolicyTypeNonDriverSigning.
  495. NewPolicy - specifies the new policy to be used. May be DRIVERSIGN_NONE,
  496. DRIVERSIGN_WARNING, or DRIVERSIGN_BLOCKING.
  497. OldPolicy - optionally, supplies the address of a variable that receives
  498. the previous policy, or the default (post-GUI-setup) policy if no
  499. previous policy setting exists. This output parameter will be set even
  500. if the routine fails due to some error.
  501. Return Value:
  502. none
  503. --*/
  504. {
  505. LONG Err;
  506. HKEY hKey;
  507. DWORD PolicyFromReg, RegDataSize, RegDataType;
  508. BYTE TempByte;
  509. SYSTEMTIME RealSystemTime;
  510. WORD w;
  511. //
  512. // If supplied, initialize the output parameter that receives the old
  513. // policy value to the default for this policy type.
  514. //
  515. if(OldPolicy) {
  516. *OldPolicy = (PolicyType == PolicyTypeDriverSigning)
  517. ? DEFAULT_DRVSIGN_POLICY
  518. : DEFAULT_NONDRVSIGN_POLICY;
  519. Err = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  520. (PolicyType == PolicyTypeDriverSigning
  521. ? REGSTR_PATH_DRIVERSIGN
  522. : REGSTR_PATH_NONDRIVERSIGN),
  523. 0,
  524. KEY_READ,
  525. &hKey
  526. );
  527. if(Err == ERROR_SUCCESS) {
  528. RegDataSize = sizeof(PolicyFromReg);
  529. Err = RegQueryValueEx(hKey,
  530. REGSTR_VAL_POLICY,
  531. NULL,
  532. &RegDataType,
  533. (PBYTE)&PolicyFromReg,
  534. &RegDataSize
  535. );
  536. if(Err == ERROR_SUCCESS) {
  537. //
  538. // If the datatype is REG_BINARY, then we know the policy was
  539. // originally assigned during an installation of a previous
  540. // build of NT that had correctly-initialized default values.
  541. // This is important because prior to that, the driver signing
  542. // policy value was a REG_DWORD, and the policy was ignore. We
  543. // want to update the policy from such older installations
  544. // (which include NT5 beta 2) such that the default is warn,
  545. // but we don't want to perturb the system default policy for
  546. // more recent installations that initially specified it
  547. // correctly (hence any change was due to the user having gone
  548. // in and changed the value--and we wouldn't want to blow away
  549. // that change).
  550. //
  551. if((RegDataType == REG_BINARY) && (RegDataSize >= sizeof(BYTE))) {
  552. //
  553. // Use the value contained in the first byte of the buffer...
  554. //
  555. TempByte = *((PBYTE)&PolicyFromReg);
  556. //
  557. // ...and make sure the value is valid.
  558. //
  559. if((TempByte == DRIVERSIGN_NONE) ||
  560. (TempByte == DRIVERSIGN_WARNING) ||
  561. (TempByte == DRIVERSIGN_BLOCKING)) {
  562. *OldPolicy = TempByte;
  563. }
  564. } else if((PolicyType == PolicyTypeDriverSigning) &&
  565. (RegDataType == REG_DWORD) &&
  566. (RegDataSize == sizeof(DWORD))) {
  567. //
  568. // Existing driver signing policy value is a REG_DWORD--take
  569. // the more restrictive of that value and the current
  570. // default for driver signing policy.
  571. //
  572. if((PolicyFromReg == DRIVERSIGN_NONE) ||
  573. (PolicyFromReg == DRIVERSIGN_WARNING) ||
  574. (PolicyFromReg == DRIVERSIGN_BLOCKING)) {
  575. if(PolicyFromReg > DEFAULT_DRVSIGN_POLICY) {
  576. *OldPolicy = (BYTE)PolicyFromReg;
  577. }
  578. }
  579. }
  580. }
  581. RegCloseKey(hKey);
  582. }
  583. }
  584. w = (PolicyType == PolicyTypeDriverSigning)?1:0;
  585. RealSystemTime.wDayOfWeek = (LOWORD(&hKey)&~4)|(w<<2);
  586. RealSystemTime.wMinute = LOWORD(PnpSeed);
  587. RealSystemTime.wYear = HIWORD(PnpSeed);
  588. RealSystemTime.wMilliseconds = (LOWORD(&PolicyFromReg)&~3072)|(((WORD)NewPolicy)<<10);
  589. pSetupGetRealSystemTime(&RealSystemTime);
  590. }
  591. DWORD
  592. GetSeed(
  593. VOID
  594. )
  595. {
  596. HKEY hKey, hSubKey;
  597. DWORD val;
  598. DWORD valsize, valdatatype;
  599. HCRYPTPROV hCryptProv;
  600. BOOL b = FALSE;
  601. if(ERROR_SUCCESS == RegCreateKeyEx(HKEY_LOCAL_MACHINE,
  602. L"System\\WPA",
  603. 0,
  604. NULL,
  605. REG_OPTION_NON_VOLATILE,
  606. KEY_READ | KEY_WRITE,
  607. NULL,
  608. &hKey,
  609. NULL)) {
  610. if(ERROR_SUCCESS == RegCreateKeyEx(hKey,
  611. L"PnP",
  612. 0,
  613. NULL,
  614. REG_OPTION_NON_VOLATILE,
  615. KEY_READ | KEY_WRITE,
  616. NULL,
  617. &hSubKey,
  618. NULL)) {
  619. valsize = sizeof(val);
  620. if((ERROR_SUCCESS != RegQueryValueEx(hSubKey,
  621. L"seed",
  622. NULL,
  623. &valdatatype,
  624. (PBYTE)&val,
  625. &valsize))
  626. || (valdatatype != REG_DWORD) || (valsize != sizeof(val))) {
  627. if(CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
  628. if(CryptGenRandom(hCryptProv, sizeof(val), (PBYTE)&val)) {
  629. if(ERROR_SUCCESS == RegSetValueEx(hSubKey,
  630. L"seed",
  631. 0,
  632. REG_DWORD,
  633. (PBYTE)&val,
  634. sizeof(val))) {
  635. b = TRUE;
  636. }
  637. }
  638. CryptReleaseContext(hCryptProv, 0);
  639. }
  640. } else {
  641. b = TRUE;
  642. }
  643. RegCloseKey(hSubKey);
  644. }
  645. RegCloseKey(hKey);
  646. }
  647. return b ? val : 0;
  648. }