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.

718 lines
16 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. passrec.c
  5. Abstract:
  6. This module contains client side code to handle the local key recovery case.
  7. Author:
  8. Pete Skelly (petesk) May 9, 2000
  9. --*/
  10. #include <nt.h>
  11. #include <ntrtl.h>
  12. #include <nturtl.h>
  13. #include <windows.h>
  14. #include <lm.h>
  15. #include <rpc.h>
  16. #include <shlobj.h>
  17. #include <userenv.h>
  18. #include <wincrypt.h>
  19. #include "passrecp.h"
  20. #include "dpapiprv.h"
  21. #include "pasrec.h"
  22. #include "passrec.h"
  23. #define FILETIME_TICKS_PER_SECOND 10000000
  24. #define RECOVERYKEY_LIFETIME 60*60*24*365*5 // 5 Years
  25. DWORD
  26. PRRecoverPassword(
  27. IN LPWSTR pszUsername,
  28. IN PBYTE pbRecoveryPrivate,
  29. IN DWORD cbRecoveryPrivate,
  30. IN LPWSTR pszNewPassword)
  31. {
  32. DWORD dwError = ERROR_SUCCESS;
  33. RPC_BINDING_HANDLE h;
  34. unsigned short *pszBinding;
  35. if((NULL == pszUsername) ||
  36. (NULL == pbRecoveryPrivate) ||
  37. (NULL == pszNewPassword))
  38. {
  39. return ERROR_INVALID_PARAMETER;
  40. }
  41. dwError = RpcStringBindingComposeW(
  42. NULL,
  43. (unsigned short*)DPAPI_LOCAL_PROT_SEQ,
  44. NULL,
  45. (unsigned short*)DPAPI_LOCAL_ENDPOINT,
  46. NULL,
  47. &pszBinding
  48. );
  49. if (RPC_S_OK != dwError)
  50. {
  51. return(dwError);
  52. }
  53. dwError = RpcBindingFromStringBindingW(pszBinding, &h);
  54. if (RPC_S_OK != dwError)
  55. {
  56. goto error;
  57. }
  58. dwError = RpcEpResolveBinding(
  59. h,
  60. PasswordRecovery_v1_0_c_ifspec);
  61. if (RPC_S_OK != dwError)
  62. {
  63. goto error;
  64. }
  65. __try
  66. {
  67. dwError = SSRecoverPassword(h,
  68. (PBYTE)pszUsername,
  69. (wcslen(pszUsername) + 1) * sizeof(WCHAR),
  70. pbRecoveryPrivate,
  71. cbRecoveryPrivate,
  72. (PBYTE)pszNewPassword,
  73. (wcslen(pszNewPassword) + 1) * sizeof(WCHAR));
  74. }
  75. __except ( EXCEPTION_EXECUTE_HANDLER )
  76. {
  77. dwError = _exception_code();
  78. }
  79. error:
  80. if(pszBinding)
  81. {
  82. RpcStringFreeW(&pszBinding);
  83. }
  84. if(h)
  85. {
  86. RpcBindingFree(&h);
  87. }
  88. return dwError;
  89. }
  90. DWORD
  91. PRQueryStatus(
  92. IN OPTIONAL LPWSTR pszDomain,
  93. IN OPTIONAL LPWSTR pszUserName,
  94. OUT DWORD *pdwStatus)
  95. {
  96. DWORD dwError = ERROR_SUCCESS;
  97. RPC_BINDING_HANDLE h;
  98. WCHAR *pszBinding;
  99. WCHAR szUserName[UNLEN + 1];
  100. DWORD cchUserName;
  101. if(NULL == pdwStatus)
  102. {
  103. return ERROR_INVALID_PARAMETER;
  104. }
  105. //
  106. // If the caller didn't specify a username, then use the
  107. // username of the calling thread.
  108. //
  109. if(pszUserName == NULL)
  110. {
  111. pszUserName = szUserName;
  112. cchUserName = sizeof(szUserName) / sizeof(WCHAR);
  113. if(!GetUserNameW(szUserName, &cchUserName))
  114. {
  115. return GetLastError();
  116. }
  117. }
  118. dwError = RpcStringBindingComposeW(
  119. NULL,
  120. (unsigned short*)DPAPI_LOCAL_PROT_SEQ,
  121. NULL,
  122. (unsigned short*)DPAPI_LOCAL_ENDPOINT,
  123. NULL,
  124. &pszBinding
  125. );
  126. if (RPC_S_OK != dwError)
  127. {
  128. return(dwError);
  129. }
  130. dwError = RpcBindingFromStringBindingW(pszBinding, &h);
  131. if (RPC_S_OK != dwError)
  132. {
  133. goto error;
  134. }
  135. dwError = RpcEpResolveBinding(
  136. h,
  137. PasswordRecovery_v1_0_c_ifspec);
  138. if (RPC_S_OK != dwError)
  139. {
  140. goto error;
  141. }
  142. __try
  143. {
  144. dwError = SSRecoverQueryStatus(
  145. h,
  146. (PBYTE)pszUserName,
  147. (wcslen(pszUserName) + 1) * sizeof(WCHAR),
  148. pdwStatus);
  149. }
  150. __except ( EXCEPTION_EXECUTE_HANDLER )
  151. {
  152. dwError = _exception_code();
  153. }
  154. error:
  155. if(pszBinding)
  156. {
  157. RpcStringFreeW(&pszBinding);
  158. }
  159. if(h)
  160. {
  161. RpcBindingFree(&h);
  162. }
  163. return dwError;
  164. }
  165. DWORD
  166. PRImportRecoveryKey(
  167. IN LPWSTR pszUsername,
  168. IN LPWSTR pszCurrentPassword,
  169. IN BYTE* pbRecoveryPublic,
  170. IN DWORD cbRecoveryPublic)
  171. {
  172. DWORD dwError = ERROR_SUCCESS;
  173. RPC_BINDING_HANDLE h;
  174. unsigned short *pszBinding;
  175. HANDLE hToken = NULL;
  176. //
  177. // on WinNT5, go to the shared services.exe RPC server
  178. //
  179. if((NULL == pbRecoveryPublic) ||
  180. (0 == cbRecoveryPublic) ||
  181. (NULL == pszUsername) ||
  182. (NULL == pszCurrentPassword))
  183. {
  184. return ERROR_INVALID_PARAMETER;
  185. }
  186. dwError = RpcStringBindingComposeW(
  187. NULL,
  188. (unsigned short*)DPAPI_LOCAL_PROT_SEQ,
  189. NULL,
  190. (unsigned short*)DPAPI_LOCAL_ENDPOINT,
  191. NULL,
  192. &pszBinding
  193. );
  194. if (RPC_S_OK != dwError)
  195. {
  196. return(dwError);
  197. }
  198. dwError = RpcBindingFromStringBindingW(pszBinding, &h);
  199. if (RPC_S_OK != dwError)
  200. {
  201. goto error;
  202. }
  203. dwError = RpcEpResolveBinding(
  204. h,
  205. PasswordRecovery_v1_0_c_ifspec);
  206. if (RPC_S_OK != dwError)
  207. {
  208. goto error;
  209. }
  210. __try
  211. {
  212. dwError = SSRecoverImportRecoveryKey(
  213. h,
  214. (PBYTE)pszUsername,
  215. (wcslen(pszUsername) + 1) * sizeof(WCHAR),
  216. (PBYTE)pszCurrentPassword,
  217. (wcslen(pszCurrentPassword) + 1) * sizeof(WCHAR),
  218. pbRecoveryPublic,
  219. cbRecoveryPublic);
  220. }
  221. __except ( EXCEPTION_EXECUTE_HANDLER )
  222. {
  223. dwError = _exception_code();
  224. }
  225. error:
  226. if(hToken)
  227. {
  228. //
  229. // Impersonate back, if we were impersonating
  230. //
  231. SetThreadToken(NULL, hToken);
  232. }
  233. if(pszBinding)
  234. {
  235. RpcStringFreeW(&pszBinding);
  236. }
  237. if(h)
  238. {
  239. RpcBindingFree(&h);
  240. }
  241. return dwError;
  242. }
  243. DWORD GenerateRecoveryCert(HCRYPTPROV hCryptProv,
  244. HCRYPTKEY hCryptKey,
  245. LPWSTR pszUsername,
  246. PSID pSid,
  247. PBYTE *ppbPublicExportData,
  248. DWORD *pcbPublicExportLength)
  249. {
  250. DWORD dwError = ERROR_SUCCESS;
  251. CERT_INFO CertInfo;
  252. CERT_PUBLIC_KEY_INFO *pKeyInfo = NULL;
  253. DWORD cbKeyInfo = 0;
  254. CERT_NAME_BLOB CertName;
  255. CERT_RDN_ATTR RDNAttributes[1];
  256. CERT_RDN CertRDN[] = {1, RDNAttributes} ;
  257. CERT_NAME_INFO NameInfo = {1, CertRDN};
  258. GUID GuidKey;
  259. CertName.pbData = NULL;
  260. CertName.cbData = 0;
  261. RDNAttributes[0].Value.pbData = NULL;
  262. RDNAttributes[0].Value.cbData = 0;
  263. DWORD cbCertSize = 0;
  264. PBYTE pbCert = NULL;
  265. DWORD cSize = 0;
  266. dwError = UuidCreate( &GuidKey );
  267. if(ERROR_SUCCESS != dwError)
  268. {
  269. goto error;
  270. }
  271. // Generate a self-signed cert structure
  272. RDNAttributes[0].dwValueType = CERT_RDN_PRINTABLE_STRING;
  273. RDNAttributes[0].pszObjId = szOID_COMMON_NAME;
  274. RDNAttributes[0].Value.cbData = wcslen(pszUsername) * sizeof(WCHAR);
  275. RDNAttributes[0].Value.pbData = (PBYTE)pszUsername;
  276. //
  277. // Get the actual public key info from the key
  278. //
  279. if(!CryptExportPublicKeyInfo(hCryptProv,
  280. AT_KEYEXCHANGE,
  281. X509_ASN_ENCODING,
  282. NULL,
  283. &cbKeyInfo))
  284. {
  285. dwError = GetLastError();
  286. goto error;
  287. }
  288. pKeyInfo = (CERT_PUBLIC_KEY_INFO *)midl_user_allocate(cbKeyInfo);
  289. if(NULL == pKeyInfo)
  290. {
  291. dwError = ERROR_NOT_ENOUGH_MEMORY;
  292. goto error;
  293. }
  294. if(!CryptExportPublicKeyInfo(hCryptProv,
  295. AT_KEYEXCHANGE,
  296. X509_ASN_ENCODING,
  297. pKeyInfo,
  298. &cbKeyInfo))
  299. {
  300. dwError = GetLastError();
  301. goto error;
  302. }
  303. //
  304. // Generate the certificate name
  305. //
  306. if(!CryptEncodeObject(X509_ASN_ENCODING,
  307. X509_NAME,
  308. &NameInfo,
  309. NULL,
  310. &CertName.cbData))
  311. {
  312. dwError = GetLastError();
  313. goto error;
  314. }
  315. CertName.pbData = (PBYTE)midl_user_allocate(CertName.cbData);
  316. if(NULL == CertName.pbData)
  317. {
  318. dwError = ERROR_NOT_ENOUGH_MEMORY;
  319. goto error;
  320. }
  321. if(!CryptEncodeObject(X509_ASN_ENCODING,
  322. X509_NAME,
  323. &NameInfo,
  324. CertName.pbData,
  325. &CertName.cbData))
  326. {
  327. dwError = GetLastError();
  328. goto error;
  329. }
  330. CertInfo.dwVersion = CERT_V3;
  331. CertInfo.SerialNumber.pbData = (PBYTE)&GuidKey;
  332. CertInfo.SerialNumber.cbData = sizeof(GUID);
  333. CertInfo.SignatureAlgorithm.pszObjId = szOID_OIWSEC_sha1RSASign;
  334. CertInfo.SignatureAlgorithm.Parameters.cbData = 0;
  335. CertInfo.SignatureAlgorithm.Parameters.pbData = NULL;
  336. CertInfo.Issuer.pbData = CertName.pbData;
  337. CertInfo.Issuer.cbData = CertName.cbData;
  338. GetSystemTimeAsFileTime(&CertInfo.NotBefore);
  339. CertInfo.NotAfter = CertInfo.NotBefore;
  340. ((LARGE_INTEGER * )&CertInfo.NotAfter)->QuadPart +=
  341. Int32x32To64(FILETIME_TICKS_PER_SECOND, RECOVERYKEY_LIFETIME);
  342. CertInfo.Subject.pbData = CertName.pbData;
  343. CertInfo.Subject.cbData = CertName.cbData;
  344. CertInfo.SubjectPublicKeyInfo = *pKeyInfo;
  345. CertInfo.SubjectUniqueId.pbData = (PBYTE)pSid;
  346. CertInfo.SubjectUniqueId.cbData = GetLengthSid(pSid);
  347. CertInfo.SubjectUniqueId.cUnusedBits = 0;
  348. CertInfo.IssuerUniqueId.pbData = (PBYTE)pSid;
  349. CertInfo.IssuerUniqueId.cbData = GetLengthSid(pSid);
  350. CertInfo.IssuerUniqueId.cUnusedBits = 0;
  351. CertInfo.cExtension = 0;
  352. CertInfo.rgExtension = NULL;
  353. if(!CryptSignAndEncodeCertificate(hCryptProv,
  354. AT_KEYEXCHANGE,
  355. X509_ASN_ENCODING,
  356. X509_CERT_TO_BE_SIGNED,
  357. &CertInfo,
  358. &CertInfo.SignatureAlgorithm,
  359. NULL,
  360. NULL,
  361. &cbCertSize))
  362. {
  363. dwError = GetLastError();
  364. goto error;
  365. }
  366. pbCert = (PBYTE)midl_user_allocate(cbCertSize);
  367. if(NULL == pbCert)
  368. {
  369. dwError = ERROR_NOT_ENOUGH_MEMORY;
  370. goto error;
  371. }
  372. if(!CryptSignAndEncodeCertificate(hCryptProv,
  373. AT_KEYEXCHANGE,
  374. X509_ASN_ENCODING,
  375. X509_CERT_TO_BE_SIGNED,
  376. &CertInfo,
  377. &CertInfo.SignatureAlgorithm,
  378. NULL,
  379. pbCert,
  380. &cbCertSize))
  381. {
  382. dwError = GetLastError();
  383. goto error;
  384. }
  385. *pcbPublicExportLength = cbCertSize;
  386. *ppbPublicExportData = pbCert;
  387. if(!CertCreateCertificateContext(X509_ASN_ENCODING, pbCert, cbCertSize))
  388. {
  389. dwError = GetLastError();
  390. goto error;
  391. }
  392. pbCert = NULL;
  393. error:
  394. if(pbCert)
  395. {
  396. midl_user_free(pbCert);
  397. }
  398. if(pKeyInfo)
  399. {
  400. midl_user_free(pKeyInfo);
  401. }
  402. if(CertName.pbData)
  403. {
  404. midl_user_free(CertName.pbData);
  405. }
  406. return dwError;
  407. }
  408. DWORD
  409. PRGenerateRecoveryKey(
  410. IN LPWSTR pszUsername,
  411. IN LPWSTR pszCurrentPassword,
  412. OUT PBYTE *ppbRecoveryPrivate,
  413. OUT DWORD *pcbRecoveryPrivate)
  414. {
  415. DWORD dwError = 0;
  416. HCRYPTPROV hProv = 0;
  417. HCRYPTKEY hKey = 0;
  418. DWORD dwDefaultKeySize = 2048;
  419. DWORD cbPrivateExportLength = 0;
  420. DWORD cbPublic = 0;
  421. PBYTE pbPublic = NULL;
  422. PBYTE pbRecoveryPrivate = NULL;
  423. DWORD cbRecoveryPrivate = 0;
  424. PSID pSid = NULL;
  425. DWORD cbSid;
  426. WCHAR szDomain[MAX_COMPUTERNAME_LENGTH + 1];
  427. DWORD cchDomain;
  428. SID_NAME_USE AcctType;
  429. //
  430. // Obtain SID of current user.
  431. //
  432. cchDomain = MAX_COMPUTERNAME_LENGTH + 1;
  433. if(!GetComputerNameW(szDomain, &cchDomain))
  434. {
  435. dwError = GetLastError();
  436. goto error;
  437. }
  438. if(!LookupAccountNameW(szDomain,
  439. pszUsername,
  440. NULL,
  441. &cbSid,
  442. NULL,
  443. &cchDomain,
  444. &AcctType))
  445. {
  446. dwError = GetLastError();
  447. if(dwError != ERROR_INSUFFICIENT_BUFFER)
  448. {
  449. goto error;
  450. }
  451. }
  452. pSid = (PBYTE)LocalAlloc(LPTR, cbSid);
  453. if(pSid == NULL)
  454. {
  455. dwError = ERROR_NOT_ENOUGH_MEMORY;
  456. goto error;
  457. }
  458. if(cchDomain > MAX_COMPUTERNAME_LENGTH + 1)
  459. {
  460. dwError = ERROR_NOT_ENOUGH_MEMORY;
  461. goto error;
  462. }
  463. if(!LookupAccountNameW(szDomain,
  464. pszUsername,
  465. pSid,
  466. &cbSid,
  467. szDomain,
  468. &cchDomain,
  469. &AcctType))
  470. {
  471. dwError = GetLastError();
  472. goto error;
  473. }
  474. //
  475. // Create recovery private key.
  476. //
  477. if(!CryptAcquireContext(&hProv,
  478. NULL,
  479. MS_STRONG_PROV,
  480. PROV_RSA_FULL,
  481. CRYPT_VERIFYCONTEXT))
  482. {
  483. dwError = GetLastError();
  484. goto error;
  485. }
  486. if(!CryptGenKey(hProv,
  487. AT_KEYEXCHANGE,
  488. CRYPT_EXPORTABLE | dwDefaultKeySize << 16,
  489. &hKey))
  490. {
  491. dwError = GetLastError();
  492. goto error;
  493. }
  494. dwError = GenerateRecoveryCert(hProv,
  495. hKey,
  496. pszUsername,
  497. pSid,
  498. &pbPublic,
  499. &cbPublic);
  500. if(ERROR_SUCCESS != dwError)
  501. {
  502. goto error;
  503. }
  504. //
  505. // Get the private key size
  506. //
  507. if(!CryptExportKey(hKey,
  508. NULL,
  509. PRIVATEKEYBLOB,
  510. 0,
  511. NULL,
  512. &cbPrivateExportLength))
  513. {
  514. dwError = GetLastError();
  515. goto error;
  516. }
  517. cbRecoveryPrivate = 2*sizeof(DWORD) + cbPrivateExportLength;
  518. pbRecoveryPrivate = (PBYTE)LocalAlloc(LMEM_FIXED, cbRecoveryPrivate);
  519. if(NULL == pbRecoveryPrivate)
  520. {
  521. dwError = ERROR_NOT_ENOUGH_MEMORY;
  522. goto error;
  523. }
  524. *(DWORD *)pbRecoveryPrivate = RECOVERY_BLOB_MAGIC;
  525. *(DWORD *)(pbRecoveryPrivate + sizeof(DWORD)) = RECOVERY_BLOB_VERSION;
  526. CopyMemory(pbRecoveryPrivate + 2*sizeof(DWORD),
  527. pbPublic,
  528. cbPublic);
  529. //
  530. // Export the private key
  531. //
  532. if(!CryptExportKey(hKey,
  533. NULL,
  534. PRIVATEKEYBLOB,
  535. 0,
  536. pbRecoveryPrivate + 2*sizeof(DWORD),
  537. &cbPrivateExportLength))
  538. {
  539. dwError = GetLastError();
  540. goto error;
  541. }
  542. dwError = PRImportRecoveryKey(
  543. pszUsername,
  544. pszCurrentPassword,
  545. pbPublic,
  546. cbPublic);
  547. if(ERROR_SUCCESS != dwError)
  548. {
  549. goto error;
  550. }
  551. *ppbRecoveryPrivate = pbRecoveryPrivate;
  552. *pcbRecoveryPrivate = cbRecoveryPrivate;
  553. pbRecoveryPrivate = NULL;
  554. error:
  555. if(pbRecoveryPrivate)
  556. {
  557. ZeroMemory(pbRecoveryPrivate, cbRecoveryPrivate);
  558. LocalFree(pbRecoveryPrivate);
  559. }
  560. if(pbPublic)
  561. {
  562. LocalFree(pbPublic);
  563. }
  564. if(hKey)
  565. {
  566. CryptDestroyKey(hKey);
  567. }
  568. if(hProv)
  569. {
  570. CryptReleaseContext(hProv, 0);
  571. }
  572. if(pSid)
  573. {
  574. LocalFree(pSid);
  575. }
  576. return dwError;
  577. }