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.

1899 lines
47 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. recovery.c
  5. Abstract:
  6. This module contains code to handle the local key recovery case.
  7. Author:
  8. Pete Skelly (petesk) May 9, 2000
  9. --*/
  10. #include <pch.cpp>
  11. #pragma hdrstop
  12. #include <ntmsv1_0.h>
  13. #include <crypt.h>
  14. #include <userenv.h>
  15. #include <userenvp.h>
  16. #include "debug.h"
  17. #include "passrec.h"
  18. #include "passrecp.h"
  19. #include "pasrec.h"
  20. #include <kerberos.h>
  21. #define RECOVERY_KEY_BASE L"Security\\Recovery\\"
  22. #define RECOVERY_FILENAME L""
  23. #define RECOVERY_STORE_NAME L"Recovery"
  24. DWORD
  25. CPSCreateServerContext(
  26. PCRYPT_SERVER_CONTEXT pServerContext,
  27. handle_t hBinding
  28. );
  29. DWORD
  30. CPSDeleteServerContext(
  31. PCRYPT_SERVER_CONTEXT pServerContext
  32. );
  33. DWORD
  34. DecryptRecoveryPassword(
  35. IN PSID pUserSid,
  36. IN PBYTE pbRecoveryPrivate,
  37. IN DWORD cbRecoveryPrivate,
  38. OUT LPWSTR *ppszPassword);
  39. DWORD
  40. ResetLocalUserPassword(
  41. LPWSTR pszDomain,
  42. LPWSTR pszUsername,
  43. LPWSTR pszOldPassword,
  44. LPWSTR pszNewPassword);
  45. NTSTATUS
  46. PRCreateLocalToken(
  47. PUNICODE_STRING Username,
  48. PUNICODE_STRING Domain,
  49. HANDLE *Token);
  50. DWORD
  51. EncryptRecoveryPassword(
  52. IN HANDLE hUserToken,
  53. IN PCCERT_CONTEXT pCertContext,
  54. IN PUNICODE_STRING pNewPassword)
  55. {
  56. DWORD dwError = ERROR_SUCCESS;
  57. HCRYPTPROV hProv = 0;
  58. HCRYPTKEY hkRecoveryPublic = 0;
  59. PRECOVERY_SUPPLEMENTAL_CREDENTIAL pNewCred = NULL;
  60. DWORD cbNewCred = 0;
  61. BYTE NewPasswordOWF[A_SHA_DIGEST_LEN];
  62. PBYTE pbPasswordBuffer = NULL;
  63. DWORD cbPasswordBuffer = 0;
  64. PBYTE pbSignature = NULL;
  65. DWORD cbSignature = 0;
  66. DWORD cbTemp = 0;
  67. DWORD cbKeySize = 0;
  68. PSID pUserSid = NULL;
  69. D_DebugLog((DEB_TRACE_API, "EncryptRecoveryPassword\n"));
  70. //
  71. // We have a cert with a good signature, so
  72. // go ahead and encrypt to it
  73. //
  74. if(!CryptAcquireContext(&hProv,
  75. NULL,
  76. MS_STRONG_PROV,
  77. PROV_RSA_FULL,
  78. CRYPT_VERIFYCONTEXT))
  79. {
  80. dwError = GetLastError();
  81. goto error;
  82. }
  83. if(!CryptImportPublicKeyInfoEx(hProv,
  84. pCertContext->dwCertEncodingType,
  85. &pCertContext->pCertInfo->SubjectPublicKeyInfo,
  86. CALG_RSA_KEYX,
  87. NULL,
  88. NULL,
  89. &hkRecoveryPublic))
  90. {
  91. dwError = GetLastError();
  92. goto error;
  93. }
  94. cbTemp = sizeof(cbKeySize);
  95. if(!CryptGetKeyParam(hkRecoveryPublic,
  96. KP_BLOCKLEN,
  97. (PBYTE)&cbKeySize,
  98. &cbTemp,
  99. 0))
  100. {
  101. dwError = GetLastError();
  102. goto error;
  103. }
  104. cbKeySize >>= 3; // convert from bits to bytes
  105. if((DWORD)pNewPassword->Length + 20 > cbKeySize)
  106. {
  107. dwError = ERROR_INVALID_DATA;
  108. goto error;
  109. }
  110. FMyPrimitiveSHA(
  111. (PBYTE)pNewPassword->Buffer,
  112. pNewPassword->Length,
  113. NewPasswordOWF);
  114. #ifdef COMPILED_BY_DEVELOPER
  115. D_DebugLog((DEB_TRACE, "Password:%ls\n", pNewPassword->Buffer));
  116. D_DebugLog((DEB_TRACE, "Signature OWF:\n"));
  117. D_DPAPIDumpHexData(DEB_TRACE, " ", NewPasswordOWF, sizeof(NewPasswordOWF));
  118. #endif
  119. dwError = LogonCredGenerateSignature(
  120. hUserToken,
  121. pCertContext->pbCertEncoded,
  122. pCertContext->cbCertEncoded,
  123. NewPasswordOWF,
  124. &pbSignature,
  125. &cbSignature);
  126. if(ERROR_SUCCESS != dwError)
  127. {
  128. goto error;
  129. }
  130. #ifdef COMPILED_BY_DEVELOPER
  131. D_DebugLog((DEB_TRACE, "Signature:\n"));
  132. D_DPAPIDumpHexData(DEB_TRACE, " ", pbSignature, cbSignature);
  133. #endif
  134. cbNewCred = sizeof(RECOVERY_SUPPLEMENTAL_CREDENTIAL) +
  135. A_SHA_DIGEST_LEN +
  136. cbSignature +
  137. cbKeySize;
  138. pNewCred = (PRECOVERY_SUPPLEMENTAL_CREDENTIAL)LocalAlloc(LMEM_FIXED, cbNewCred);
  139. if(NULL == pNewCred)
  140. {
  141. dwError = ERROR_NOT_ENOUGH_MEMORY;
  142. goto error;
  143. }
  144. pNewCred->dwVersion = RECOVERY_SUPPLEMENTAL_CREDENTIAL_VERSION;
  145. pNewCred->cbRecoveryCertHashSize = A_SHA_DIGEST_LEN;
  146. pNewCred->cbRecoveryCertSignatureSize = cbSignature;
  147. pNewCred->cbEncryptedPassword = 0;
  148. CopyMemory((PBYTE)(pNewCred+1)+A_SHA_DIGEST_LEN,
  149. pbSignature,
  150. cbSignature);
  151. if(!CertGetCertificateContextProperty(pCertContext,
  152. CERT_HASH_PROP_ID,
  153. (PBYTE)(pNewCred+1),
  154. &pNewCred->cbRecoveryCertHashSize))
  155. {
  156. dwError = GetLastError();
  157. goto error;
  158. }
  159. CopyMemory((PBYTE)(pNewCred+1) +
  160. pNewCred->cbRecoveryCertHashSize +
  161. pNewCred->cbRecoveryCertSignatureSize,
  162. pNewPassword->Buffer,
  163. pNewPassword->Length);
  164. pNewCred->cbEncryptedPassword = pNewPassword->Length;
  165. if(!CryptEncrypt(hkRecoveryPublic,
  166. 0,
  167. TRUE,
  168. 0, //CRYPT_OAEP,
  169. (PBYTE)(pNewCred+1) +
  170. pNewCred->cbRecoveryCertHashSize +
  171. pNewCred->cbRecoveryCertSignatureSize,
  172. &pNewCred->cbEncryptedPassword,
  173. cbKeySize))
  174. {
  175. dwError = GetLastError();
  176. goto error;
  177. }
  178. //
  179. // Save the recovery data to the registry.
  180. //
  181. if(!GetTokenUserSid(hUserToken, &pUserSid))
  182. {
  183. dwError = GetLastError();
  184. goto error;
  185. }
  186. dwError = RecoverySetSupplementalCredential(
  187. pUserSid,
  188. pNewCred,
  189. cbNewCred);
  190. error:
  191. if(pUserSid)
  192. {
  193. SSFree(pUserSid);
  194. }
  195. RtlSecureZeroMemory(NewPasswordOWF, sizeof(NewPasswordOWF));
  196. if(pbSignature)
  197. {
  198. LocalFree(pbSignature);
  199. }
  200. if(pNewCred)
  201. {
  202. RtlSecureZeroMemory(pNewCred, cbNewCred);
  203. LocalFree(pNewCred);
  204. }
  205. if(hkRecoveryPublic)
  206. {
  207. CryptDestroyKey(hkRecoveryPublic);
  208. }
  209. if(hProv)
  210. {
  211. CryptReleaseContext(hProv,0);
  212. }
  213. D_DebugLog((DEB_TRACE_API, "EncryptRecoveryPassword returned 0x%x\n", dwError));
  214. return dwError;
  215. }
  216. DWORD
  217. RecoverFindRecoveryPublic(
  218. HANDLE hUserToken,
  219. PSID pUserSid,
  220. PCCERT_CONTEXT *ppRecoveryPublic,
  221. PBYTE pbVerifyOWF,
  222. BOOL fVerifySignature)
  223. {
  224. DWORD dwError = ERROR_SUCCESS;
  225. PCCERT_CONTEXT pCertContext = NULL;
  226. PRECOVERY_SUPPLEMENTAL_CREDENTIAL pCred = NULL;
  227. DWORD cbCred = 0;
  228. HCERTSTORE hStore = NULL;
  229. PSID pLocalSid = NULL;
  230. CRYPT_HASH_BLOB HashBlob;
  231. D_DebugLog((DEB_TRACE_API, "RecoverFindRecoveryPublic\n"));
  232. if(NULL == ppRecoveryPublic)
  233. {
  234. dwError = ERROR_INVALID_PARAMETER;
  235. goto error;
  236. }
  237. if(NULL == hUserToken && fVerifySignature)
  238. {
  239. // We need the user token to verify the signature.
  240. dwError = ERROR_INVALID_PARAMETER;
  241. goto error;
  242. }
  243. #ifdef COMPILED_BY_DEVELOPER
  244. D_DebugLog((DEB_TRACE, "Verify OWF:\n"));
  245. D_DPAPIDumpHexData(DEB_TRACE, " ", pbVerifyOWF, A_SHA_DIGEST_LEN);
  246. #endif
  247. if(pUserSid == NULL)
  248. {
  249. if(!GetTokenUserSid(hUserToken, &pLocalSid))
  250. {
  251. dwError = GetLastError();
  252. goto error;
  253. }
  254. pUserSid = pLocalSid;
  255. }
  256. dwError = RecoveryRetrieveSupplementalCredential(pUserSid, &pCred, &cbCred);
  257. if(ERROR_SUCCESS != dwError)
  258. {
  259. goto error;
  260. }
  261. //
  262. // Validate cbCred
  263. //
  264. if((NULL == pCred) ||
  265. (sizeof(RECOVERY_SUPPLEMENTAL_CREDENTIAL) > cbCred) ||
  266. ( sizeof(RECOVERY_SUPPLEMENTAL_CREDENTIAL) +
  267. pCred->cbRecoveryCertHashSize +
  268. pCred->cbRecoveryCertSignatureSize +
  269. pCred->cbEncryptedPassword > cbCred) ||
  270. (pCred->dwVersion != RECOVERY_SUPPLEMENTAL_CREDENTIAL_VERSION))
  271. {
  272. dwError = ERROR_INVALID_DATA;
  273. goto error;
  274. }
  275. //
  276. // Attempt to find the recovery public
  277. //
  278. hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM,
  279. X509_ASN_ENCODING,
  280. NULL,
  281. CERT_STORE_READONLY_FLAG | CERT_SYSTEM_STORE_LOCAL_MACHINE,
  282. RECOVERY_STORE_NAME);
  283. if(NULL == hStore)
  284. {
  285. dwError = GetLastError();
  286. goto error;
  287. }
  288. HashBlob.cbData = pCred->cbRecoveryCertHashSize;
  289. HashBlob.pbData = (PBYTE)(pCred + 1);
  290. while(pCertContext = CertFindCertificateInStore(hStore,
  291. X509_ASN_ENCODING,
  292. 0,
  293. CERT_FIND_HASH,
  294. &HashBlob,
  295. pCertContext))
  296. {
  297. // we found one, verify it's signature
  298. // The signature verification will use the 'old password'
  299. // which will either be the current logon credential, or
  300. // will be in the password history
  301. if(fVerifySignature)
  302. {
  303. dwError = LogonCredVerifySignature( hUserToken,
  304. pCertContext->pbCertEncoded,
  305. pCertContext->cbCertEncoded,
  306. pbVerifyOWF,
  307. (PBYTE)(pCred + 1) + pCred->cbRecoveryCertHashSize,
  308. pCred->cbRecoveryCertSignatureSize);
  309. if(ERROR_SUCCESS == dwError)
  310. {
  311. break;
  312. }
  313. }
  314. else
  315. {
  316. dwError = ERROR_SUCCESS;
  317. break;
  318. }
  319. }
  320. if(NULL == pCertContext)
  321. {
  322. dwError = GetLastError();
  323. goto error;
  324. }
  325. *ppRecoveryPublic = pCertContext;
  326. pCertContext = NULL;
  327. error:
  328. if(pLocalSid)
  329. {
  330. SSFree(pLocalSid);
  331. }
  332. if (pCred)
  333. {
  334. RtlSecureZeroMemory((PBYTE)pCred, cbCred);
  335. LocalFree(pCred);
  336. }
  337. if(pCertContext)
  338. {
  339. CertFreeCertificateContext(pCertContext);
  340. }
  341. if(hStore)
  342. {
  343. CertCloseStore(hStore, 0);
  344. }
  345. D_DebugLog((DEB_TRACE_API, "RecoverFindRecoveryPublic returned 0x%x\n", dwError));
  346. return dwError;
  347. }
  348. DWORD
  349. RecoverChangePasswordNotify(
  350. HANDLE UserToken,
  351. BYTE OldPasswordOWF[A_SHA_DIGEST_LEN],
  352. PUNICODE_STRING NewPassword)
  353. {
  354. DWORD dwError = ERROR_SUCCESS;
  355. PCCERT_CONTEXT pCertContext = NULL;
  356. D_DebugLog((DEB_TRACE_API, "RecoverChangePasswordNotify\n"));
  357. //
  358. // Validate cbCred
  359. //
  360. if(NULL == NewPassword)
  361. {
  362. dwError = ERROR_INVALID_DATA;
  363. goto error;
  364. }
  365. dwError = RecoverFindRecoveryPublic(UserToken,
  366. NULL,
  367. &pCertContext,
  368. OldPasswordOWF,
  369. TRUE); // verify signature
  370. if(ERROR_SUCCESS != dwError)
  371. {
  372. goto error;
  373. }
  374. dwError = EncryptRecoveryPassword(UserToken,
  375. pCertContext,
  376. NewPassword);
  377. if(ERROR_SUCCESS != dwError)
  378. {
  379. goto error;
  380. }
  381. error:
  382. if(pCertContext)
  383. {
  384. CertFreeCertificateContext(pCertContext);
  385. }
  386. D_DebugLog((DEB_TRACE_API, "RecoverChangePasswordNotify returned 0x%x\n", dwError));
  387. return dwError;
  388. }
  389. DWORD
  390. s_SSRecoverQueryStatus(
  391. handle_t h,
  392. PBYTE pbUserName,
  393. DWORD cbUserName,
  394. DWORD *pdwStatus)
  395. {
  396. DWORD dwError;
  397. LPWSTR pszUserName = (LPWSTR)pbUserName;
  398. CRYPT_SERVER_CONTEXT ServerContext;
  399. // Make sure username is zero terminated.
  400. if(pbUserName == NULL || cbUserName < sizeof(WCHAR))
  401. {
  402. return ERROR_INVALID_PARAMETER;
  403. }
  404. if(pszUserName[(cbUserName - 1) / sizeof(WCHAR)] != L'\0')
  405. {
  406. return ERROR_INVALID_PARAMETER;
  407. }
  408. // Check for zero length username.
  409. if(wcslen(pszUserName) == 0)
  410. {
  411. return ERROR_INVALID_PARAMETER;
  412. }
  413. // Validate output parameter.
  414. if(pdwStatus == NULL)
  415. {
  416. return ERROR_INVALID_PARAMETER;
  417. }
  418. dwError = CPSCreateServerContext(&ServerContext, h);
  419. if(dwError != ERROR_SUCCESS)
  420. {
  421. return dwError;
  422. }
  423. dwError = SPRecoverQueryStatus(&ServerContext,
  424. pszUserName,
  425. pdwStatus);
  426. CPSDeleteServerContext( &ServerContext );
  427. return dwError;
  428. }
  429. DWORD
  430. SPRecoverQueryStatus(
  431. PVOID pvContext, // server context
  432. LPWSTR pszUserName, // user name
  433. DWORD *pdwStatus) // recovery status
  434. {
  435. DWORD dwError = ERROR_SUCCESS;
  436. DWORD dwDisp = RECOVERY_STATUS_OK;
  437. PCCERT_CONTEXT pCertContext = NULL;
  438. WCHAR szMachineName[MAX_COMPUTERNAME_LENGTH + 1];
  439. DWORD cchMachineName;
  440. PSID pUserSid = NULL;
  441. DWORD cbSid = 0;
  442. SID_NAME_USE SidType;
  443. PWSTR pszDomainName = NULL;
  444. DWORD cchDomainName = 0;
  445. HANDLE hToken = 0;
  446. UNICODE_STRING UserName;
  447. UNICODE_STRING Domain;
  448. BOOL fImpersonated = FALSE;
  449. D_DebugLog((DEB_TRACE_API, "SPRecoverQueryStatus\n"));
  450. D_DebugLog((DEB_TRACE_API, "User name:%ls\n", pszUserName));
  451. //
  452. // Attempt to obtain the SID for the specified user.
  453. //
  454. // Determine the (local user) domain.
  455. cchMachineName = sizeof(szMachineName) / sizeof(WCHAR);
  456. if(!GetComputerName(szMachineName, &cchMachineName))
  457. {
  458. dwError = GetLastError();
  459. goto cleanup;
  460. }
  461. if(!LookupAccountName(szMachineName,
  462. pszUserName,
  463. NULL,
  464. &cbSid,
  465. NULL,
  466. &cchDomainName,
  467. &SidType))
  468. {
  469. dwError = GetLastError();
  470. if(dwError != ERROR_INSUFFICIENT_BUFFER)
  471. {
  472. dwDisp = RECOVERY_STATUS_USER_NOT_FOUND;
  473. dwError = ERROR_SUCCESS;
  474. goto cleanup;
  475. }
  476. }
  477. pUserSid = (PBYTE)LocalAlloc(LPTR, cbSid);
  478. if(pUserSid == NULL)
  479. {
  480. dwError = ERROR_NOT_ENOUGH_MEMORY;
  481. goto cleanup;
  482. }
  483. pszDomainName = (PWSTR)LocalAlloc(LPTR, cchDomainName * sizeof(WCHAR));
  484. if(pszDomainName == NULL)
  485. {
  486. dwError = ERROR_NOT_ENOUGH_MEMORY;
  487. goto cleanup;
  488. }
  489. if(!LookupAccountName(szMachineName,
  490. pszUserName,
  491. pUserSid,
  492. &cbSid,
  493. pszDomainName,
  494. &cchDomainName,
  495. &SidType))
  496. {
  497. dwDisp = RECOVERY_STATUS_USER_NOT_FOUND;
  498. dwError = ERROR_SUCCESS;
  499. goto cleanup;
  500. }
  501. if(SidType != SidTypeUser)
  502. {
  503. dwDisp = RECOVERY_STATUS_USER_NOT_FOUND;
  504. dwError = ERROR_SUCCESS;
  505. goto cleanup;
  506. }
  507. //
  508. // Attempt to find the recovery public
  509. //
  510. dwError = RecoverFindRecoveryPublic(NULL,
  511. pUserSid,
  512. &pCertContext,
  513. NULL, // current OWF
  514. FALSE); // verify signature
  515. if(ERROR_FILE_NOT_FOUND == dwError)
  516. {
  517. dwDisp = RECOVERY_STATUS_FILE_NOT_FOUND;
  518. dwError = ERROR_SUCCESS;
  519. }
  520. else if(CRYPT_E_NOT_FOUND == dwError)
  521. {
  522. dwDisp = RECOVERY_STATUS_NO_PUBLIC_EXISTS;
  523. dwError = ERROR_SUCCESS;
  524. }
  525. else if(ERROR_INVALID_ACCESS == dwError)
  526. {
  527. dwDisp = RECOVERY_STATUS_PUBLIC_SIGNATURE_INVALID;
  528. dwError = ERROR_SUCCESS;
  529. }
  530. cleanup:
  531. if(ERROR_SUCCESS == dwError)
  532. {
  533. D_DebugLog((DEB_TRACE_API, "Disposition: %d\n", dwDisp));
  534. *pdwStatus = dwDisp;
  535. }
  536. if(hToken)
  537. {
  538. CloseHandle(hToken);
  539. }
  540. if(pCertContext)
  541. {
  542. CertFreeCertificateContext(pCertContext);
  543. }
  544. if(pUserSid) LocalFree(pUserSid);
  545. if(pszDomainName) LocalFree(pszDomainName);
  546. if(fImpersonated)
  547. {
  548. CPSRevertToSelf(pvContext);
  549. }
  550. D_DebugLog((DEB_TRACE_API, "SPRecoverQueryStatus returned 0x%x\n", dwError));
  551. return dwError;
  552. }
  553. NTSTATUS
  554. VerifyCredentials(
  555. IN PWSTR UserName,
  556. IN PWSTR DomainName,
  557. IN PWSTR Password
  558. )
  559. {
  560. PKERB_VERIFY_CREDENTIALS_REQUEST pVerifyRequest;
  561. KERB_VERIFY_CREDENTIALS_REQUEST VerifyRequest;
  562. ULONG cbVerifyRequest;
  563. PVOID pResponse = NULL;
  564. ULONG cbResponse;
  565. USHORT cbUserName;
  566. USHORT cbDomainName;
  567. USHORT cbPassword;
  568. NTSTATUS ProtocolStatus = STATUS_LOGON_FAILURE;
  569. NTSTATUS Status;
  570. UNICODE_STRING AuthenticationPackage;
  571. RtlInitUnicodeString(&AuthenticationPackage, MICROSOFT_KERBEROS_NAME_W);
  572. cbUserName = (USHORT)(lstrlenW(UserName) * sizeof(WCHAR)) ;
  573. cbDomainName = (USHORT)(lstrlenW(DomainName) * sizeof(WCHAR)) ;
  574. cbPassword = (USHORT)(lstrlenW(Password) * sizeof(WCHAR)) ;
  575. cbVerifyRequest = sizeof(VerifyRequest) +
  576. cbUserName +
  577. cbDomainName +
  578. cbPassword ;
  579. pVerifyRequest = &VerifyRequest;
  580. ZeroMemory( &VerifyRequest, sizeof(VerifyRequest) );
  581. pVerifyRequest->MessageType = KerbVerifyCredentialsMessage ;
  582. //
  583. // do the length, buffers, copy, marshall dance.
  584. //
  585. pVerifyRequest->UserName.Length = cbUserName;
  586. pVerifyRequest->UserName.MaximumLength = cbUserName;
  587. pVerifyRequest->UserName.Buffer = UserName;
  588. pVerifyRequest->DomainName.Length = cbDomainName;
  589. pVerifyRequest->DomainName.MaximumLength = cbDomainName;
  590. pVerifyRequest->DomainName.Buffer = DomainName;
  591. pVerifyRequest->Password.Length = cbPassword;
  592. pVerifyRequest->Password.MaximumLength = cbPassword;
  593. pVerifyRequest->Password.Buffer = Password;
  594. pVerifyRequest->VerifyFlags = 0;
  595. Status = LsaICallPackage(
  596. &AuthenticationPackage,
  597. pVerifyRequest,
  598. cbVerifyRequest,
  599. &pResponse,
  600. &cbResponse,
  601. &ProtocolStatus
  602. );
  603. if(!NT_SUCCESS(Status))
  604. {
  605. goto Cleanup;
  606. }
  607. Status = ProtocolStatus;
  608. Cleanup:
  609. return Status;
  610. }
  611. DWORD
  612. s_SSRecoverImportRecoveryKey(
  613. handle_t h,
  614. BYTE* pbUsername,
  615. DWORD cbUsername,
  616. BYTE* pbCurrentPassword,
  617. DWORD cbCurrentPassword,
  618. BYTE* pbRecoveryPublic,
  619. DWORD cbRecoveryPublic)
  620. {
  621. DWORD dwError = ERROR_SUCCESS;
  622. PCCERT_CONTEXT pCertContext = NULL;
  623. HANDLE hUserToken = NULL;
  624. HCERTSTORE hStore = NULL;
  625. WCHAR szMachineName[MAX_COMPUTERNAME_LENGTH + 1];
  626. DWORD cchMachineName;
  627. LPWSTR pszUsername = (LPWSTR)pbUsername;
  628. LPWSTR pszCurrentPassword = (LPWSTR)pbCurrentPassword;
  629. UNICODE_STRING Domain;
  630. UNICODE_STRING Username;
  631. UNICODE_STRING Password;
  632. D_DebugLog((DEB_TRACE_API, "s_SSRecoverImportRecoveryKey\n"));
  633. //
  634. // Validate input parameters.
  635. //
  636. if(pbUsername == NULL || cbUsername < sizeof(WCHAR) ||
  637. pbCurrentPassword == NULL || cbCurrentPassword < sizeof(WCHAR) ||
  638. pbRecoveryPublic == NULL || cbRecoveryPublic == 0)
  639. {
  640. dwError = ERROR_INVALID_PARAMETER;
  641. goto error;
  642. }
  643. // Make sure strings are zero terminated.
  644. if((pszUsername[(cbUsername - 1) / sizeof(WCHAR)] != L'\0') ||
  645. (pszCurrentPassword[(cbCurrentPassword - 1) / sizeof(WCHAR)] != L'\0'))
  646. {
  647. dwError = ERROR_INVALID_PARAMETER;
  648. goto error;
  649. }
  650. // Check for zero length username.
  651. if(wcslen(pszUsername) == 0)
  652. {
  653. return ERROR_INVALID_PARAMETER;
  654. }
  655. // Perform this operation as local system.
  656. RevertToSelf();
  657. //
  658. // Verify the supplied password.
  659. // This ensures that we're being called by the actual user, and
  660. // that we're not being spoofed into creating a non-authorized
  661. // recovery certificate.
  662. //
  663. cchMachineName = MAX_COMPUTERNAME_LENGTH + 1;
  664. if(!GetComputerName(szMachineName, &cchMachineName))
  665. {
  666. dwError = GetLastError();
  667. goto error;
  668. }
  669. dwError = VerifyCredentials(pszUsername,
  670. szMachineName,
  671. pszCurrentPassword);
  672. if(!NT_SUCCESS(dwError))
  673. {
  674. D_DebugLog((DEB_ERROR, "s_SSRecoverImportRecoveryKey: password did not verify (0x%x)\n", dwError));
  675. goto error;
  676. }
  677. //
  678. // Create a token for the user. This will be used when signing the
  679. // recovery file. It almost makes sense to call LogonUser in order to do
  680. // this (since we have the password and everything), but that function
  681. // fails on Whistler when the user has a blank password.
  682. //
  683. RtlInitUnicodeString(&Domain, szMachineName);
  684. RtlInitUnicodeString(&Username, pszUsername);
  685. RtlInitUnicodeString(&Password, pszCurrentPassword);
  686. dwError = PRCreateLocalToken(&Username, &Domain, &hUserToken);
  687. if(!NT_SUCCESS(dwError))
  688. {
  689. D_DebugLog((DEB_ERROR, "s_SSRecoverImportRecoveryKey: could not create local token (0x%x)\n", dwError));
  690. goto error;
  691. }
  692. //
  693. // Add the recovery certificate to the recovery store.
  694. //
  695. pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING,
  696. pbRecoveryPublic,
  697. cbRecoveryPublic);
  698. if(NULL == pCertContext)
  699. {
  700. dwError = GetLastError();
  701. goto error;
  702. }
  703. hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM,
  704. X509_ASN_ENCODING,
  705. NULL,
  706. CERT_SYSTEM_STORE_LOCAL_MACHINE,
  707. RECOVERY_STORE_NAME);
  708. if(NULL == hStore)
  709. {
  710. dwError = GetLastError();
  711. goto error;
  712. }
  713. if(!CertAddCertificateContextToStore(hStore,
  714. pCertContext,
  715. CERT_STORE_ADD_REPLACE_EXISTING,
  716. NULL))
  717. {
  718. dwError = GetLastError();
  719. goto error;
  720. }
  721. dwError = EncryptRecoveryPassword(hUserToken,
  722. pCertContext,
  723. &Password);
  724. if(ERROR_SUCCESS != dwError)
  725. {
  726. goto error;
  727. }
  728. error:
  729. if(hStore)
  730. {
  731. CertCloseStore(hStore, 0);
  732. }
  733. if(pCertContext)
  734. {
  735. CertFreeCertificateContext(pCertContext);
  736. }
  737. if(hUserToken)
  738. {
  739. CloseHandle(hUserToken);
  740. }
  741. D_DebugLog((DEB_TRACE_API, "s_SSRecoverImportRecoveryKey returned 0x%x\n", dwError));
  742. return dwError;
  743. }
  744. NTSTATUS
  745. PRCreateLocalToken(
  746. PUNICODE_STRING Username,
  747. PUNICODE_STRING Domain,
  748. HANDLE *Token)
  749. {
  750. NTSTATUS Status;
  751. NTSTATUS SubStatus;
  752. LUID LogonId;
  753. PUCHAR AuthData;
  754. DWORD AuthDataLen;
  755. SECURITY_STRING PacUserName;
  756. //
  757. // Obtain a PAC for the user.
  758. //
  759. Status = g_pSecpkgTable->GetAuthDataForUser(
  760. Username,
  761. SecNameSamCompatible,
  762. NULL,
  763. &AuthData,
  764. &AuthDataLen,
  765. NULL );
  766. if (!NT_SUCCESS(Status))
  767. {
  768. goto cleanup;
  769. }
  770. //
  771. // Convert the PAC into a user token.
  772. //
  773. PacUserName.Buffer = NULL;
  774. Status = g_pSecpkgTable->ConvertAuthDataToToken(
  775. AuthData,
  776. AuthDataLen,
  777. SecurityImpersonation,
  778. &DPAPITokenSource,
  779. Network,
  780. Domain,
  781. Token,
  782. &LogonId,
  783. &PacUserName,
  784. &SubStatus );
  785. cleanup:
  786. return Status;
  787. }
  788. //+---------------------------------------------------------------------------
  789. //
  790. // Function: PRGetProfilePath
  791. //
  792. // Synopsis: Returns the path for user's application data.
  793. //
  794. //
  795. // Arguments: [hUserToken] -- An access token representing a particular
  796. // user. The token must have TOKEN_IMPERSONATE
  797. // and TOKEN_QUERY priviledge.
  798. //
  799. // [pszPath] -- Pointer to a buffer of length MAX_PATH to
  800. // receive the path. If error occurs then the
  801. // string will be empty.
  802. //
  803. //----------------------------------------------------------------------------
  804. DWORD
  805. PRGetProfilePath(
  806. IN HANDLE hUserToken OPTIONAL,
  807. OUT PWSTR pszPath)
  808. {
  809. DWORD Status;
  810. HANDLE hToken = NULL;
  811. HANDLE hLocalToken = NULL;
  812. //
  813. // Default to empty string.
  814. //
  815. *pszPath = L'\0';
  816. //
  817. // If a user token is explicitly specified, then use it.
  818. // Otherwise, use the current thread token.
  819. //
  820. if(hUserToken)
  821. {
  822. hToken = hUserToken;
  823. }
  824. else
  825. {
  826. if(!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hLocalToken))
  827. {
  828. Status = GetLastError();
  829. goto cleanup;
  830. }
  831. hToken = hLocalToken;
  832. }
  833. //
  834. // Get path to user's profile data. This will typically look something
  835. // like "c:\Documents and Settings\<user>\Application Data".
  836. //
  837. Status = GetUserAppDataPath(hToken, FALSE, pszPath);
  838. if(Status != ERROR_SUCCESS)
  839. {
  840. D_DebugLog((DEB_ERROR, "GetUserAppDataPath returned: %d\n", Status));
  841. goto cleanup;
  842. }
  843. D_DebugLog((DEB_TRACE, "Profile path:%ls\n", pszPath));
  844. cleanup:
  845. if(hLocalToken)
  846. {
  847. CloseHandle(hLocalToken);
  848. }
  849. return Status;
  850. }
  851. DWORD
  852. RecoverySetSupplementalCredential(
  853. IN PSID pUserSid,
  854. IN PRECOVERY_SUPPLEMENTAL_CREDENTIAL pSupplementalCred,
  855. IN DWORD cbSupplementalCred)
  856. {
  857. WCHAR szPath[MAX_PATH + 1];
  858. LPWSTR pszTextualSid;
  859. DWORD cchTextualSid;
  860. HKEY hRecovery = NULL;
  861. DWORD Disp;
  862. DWORD dwError;
  863. HANDLE hOldUser = NULL;
  864. D_DebugLog((DEB_TRACE_API, "RecoverySetSupplementalCredential\n"));
  865. //
  866. // Build path to recovery data.
  867. //
  868. wcscpy(szPath, RECOVERY_KEY_BASE);
  869. pszTextualSid = szPath + wcslen(szPath);
  870. cchTextualSid = (sizeof(szPath) / sizeof(WCHAR)) - wcslen(szPath);
  871. if(!GetTextualSid(pUserSid, pszTextualSid, &cchTextualSid))
  872. {
  873. dwError = ERROR_NOT_ENOUGH_MEMORY;
  874. goto error;
  875. }
  876. //
  877. // Write data to registry as local system.
  878. //
  879. if(!OpenThreadToken(GetCurrentThread(),
  880. TOKEN_IMPERSONATE | TOKEN_READ,
  881. TRUE,
  882. &hOldUser))
  883. {
  884. hOldUser = NULL;
  885. }
  886. RevertToSelf();
  887. //
  888. // Write recovery data to the registry.
  889. //
  890. dwError = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
  891. szPath,
  892. 0,
  893. TEXT(""),
  894. REG_OPTION_NON_VOLATILE,
  895. KEY_WRITE,
  896. NULL,
  897. &hRecovery,
  898. &Disp);
  899. if(dwError != ERROR_SUCCESS)
  900. {
  901. goto error;
  902. }
  903. dwError = RegSetValueEx(hRecovery,
  904. RECOVERY_FILENAME,
  905. NULL,
  906. REG_BINARY,
  907. (PBYTE)pSupplementalCred,
  908. cbSupplementalCred);
  909. if(dwError != ERROR_SUCCESS)
  910. {
  911. goto error;
  912. }
  913. dwError = ERROR_SUCCESS;
  914. error:
  915. if(hRecovery)
  916. {
  917. RegCloseKey(hRecovery);
  918. }
  919. if(hOldUser)
  920. {
  921. //
  922. // We can do nothing about the failure of SetThreadToken here.
  923. //
  924. (void) SetThreadToken(NULL, hOldUser);
  925. CloseHandle(hOldUser);
  926. }
  927. D_DebugLog((DEB_TRACE_API, "RecoverySetSupplementalCredential returned 0x%x\n", dwError));
  928. return dwError;
  929. }
  930. DWORD
  931. RecoveryRetrieveSupplementalCredential(
  932. IN PSID pUserSid,
  933. OUT PRECOVERY_SUPPLEMENTAL_CREDENTIAL *ppSupplementalCred,
  934. OUT DWORD *pcbSupplementalCred)
  935. {
  936. WCHAR szPath[MAX_PATH + 1];
  937. LPWSTR pszTextualSid;
  938. DWORD cchTextualSid;
  939. HKEY hRecovery = NULL;
  940. DWORD Type;
  941. PBYTE pbData = NULL;
  942. DWORD cbData;
  943. DWORD dwError;
  944. HANDLE hOldUser = NULL;
  945. D_DebugLog((DEB_TRACE_API, "RecoveryRetrieveSupplementalCredential\n"));
  946. //
  947. // Build path to recovery data.
  948. //
  949. wcscpy(szPath, RECOVERY_KEY_BASE);
  950. pszTextualSid = szPath + wcslen(szPath);
  951. cchTextualSid = (sizeof(szPath) / sizeof(WCHAR)) - wcslen(szPath);
  952. if(!GetTextualSid(pUserSid, pszTextualSid, &cchTextualSid))
  953. {
  954. dwError = ERROR_NOT_ENOUGH_MEMORY;
  955. goto error;
  956. }
  957. //
  958. // Read data from registry as local system.
  959. //
  960. if(!OpenThreadToken(GetCurrentThread(),
  961. TOKEN_IMPERSONATE | TOKEN_READ,
  962. TRUE,
  963. &hOldUser))
  964. {
  965. hOldUser = NULL;
  966. }
  967. RevertToSelf();
  968. //
  969. // Read recovery data blob out of the registry.
  970. //
  971. dwError = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  972. szPath,
  973. 0,
  974. KEY_READ,
  975. &hRecovery);
  976. if(dwError != ERROR_SUCCESS)
  977. {
  978. goto error;
  979. }
  980. dwError = RegQueryValueEx(hRecovery,
  981. RECOVERY_FILENAME,
  982. NULL,
  983. &Type,
  984. NULL,
  985. &cbData);
  986. if(dwError != ERROR_SUCCESS)
  987. {
  988. if(dwError != ERROR_MORE_DATA)
  989. {
  990. goto error;
  991. }
  992. }
  993. pbData = (PBYTE)LocalAlloc(LPTR, cbData);
  994. if(pbData == NULL)
  995. {
  996. dwError = ERROR_NOT_ENOUGH_MEMORY;
  997. goto error;
  998. }
  999. dwError = RegQueryValueEx(hRecovery,
  1000. RECOVERY_FILENAME,
  1001. NULL,
  1002. &Type,
  1003. pbData,
  1004. &cbData);
  1005. if(dwError != ERROR_SUCCESS)
  1006. {
  1007. if(dwError != ERROR_MORE_DATA)
  1008. {
  1009. goto error;
  1010. }
  1011. }
  1012. if(Type != REG_BINARY)
  1013. {
  1014. dwError = ERROR_INVALID_DATA;
  1015. goto error;
  1016. }
  1017. //
  1018. // Set output parameters.
  1019. //
  1020. *ppSupplementalCred = (PRECOVERY_SUPPLEMENTAL_CREDENTIAL)pbData;
  1021. *pcbSupplementalCred = cbData;
  1022. pbData = NULL;
  1023. dwError = ERROR_SUCCESS;
  1024. error:
  1025. if(hRecovery)
  1026. {
  1027. RegCloseKey(hRecovery);
  1028. }
  1029. if(hOldUser)
  1030. {
  1031. //
  1032. // We can do nothing if SetThreadTOken fails here
  1033. //
  1034. (void)SetThreadToken(NULL, hOldUser);
  1035. CloseHandle(hOldUser);
  1036. }
  1037. if(pbData)
  1038. {
  1039. LocalFree(pbData);
  1040. }
  1041. D_DebugLog((DEB_TRACE_API, "RecoveryRetrieveSupplementalCredential returned 0x%x\n", dwError));
  1042. return dwError;
  1043. }
  1044. DWORD
  1045. s_SSRecoverPassword(
  1046. handle_t h,
  1047. PBYTE pbUsername,
  1048. DWORD cbUsername,
  1049. PBYTE pbRecoveryPrivate,
  1050. DWORD cbRecoveryPrivate,
  1051. PBYTE pbNewPassword,
  1052. DWORD cbNewPassword)
  1053. {
  1054. DWORD dwError = ERROR_SUCCESS;
  1055. PWSTR pszUsername = (PWSTR)pbUsername;
  1056. PWSTR pszNewPassword = (PWSTR)pbNewPassword;
  1057. PWSTR pszOldPassword = NULL;
  1058. CRYPT_SERVER_CONTEXT ServerContext;
  1059. PSID pSid = NULL;
  1060. DWORD cbSid;
  1061. WCHAR szDomain[MAX_COMPUTERNAME_LENGTH + 1];
  1062. DWORD cchDomain;
  1063. SID_NAME_USE AcctType;
  1064. D_DebugLog((DEB_TRACE_API, "s_SSRecoverPassword\n"));
  1065. dwError = CPSCreateServerContext(&ServerContext, h);
  1066. if(dwError != ERROR_SUCCESS)
  1067. {
  1068. return dwError;
  1069. }
  1070. //
  1071. // Validate input parameters.
  1072. //
  1073. if(pbUsername == NULL || cbUsername < sizeof(WCHAR) ||
  1074. pbRecoveryPrivate == NULL || cbRecoveryPrivate == 0 ||
  1075. pbNewPassword == NULL || cbNewPassword < sizeof(WCHAR))
  1076. {
  1077. dwError = ERROR_INVALID_PARAMETER;
  1078. goto error;
  1079. }
  1080. // Make sure strings are zero terminated.
  1081. if((pszUsername[(cbUsername - 1) / sizeof(WCHAR)] != L'\0') ||
  1082. (pszNewPassword[(cbNewPassword - 1) / sizeof(WCHAR)] != L'\0'))
  1083. {
  1084. dwError = ERROR_INVALID_PARAMETER;
  1085. goto error;
  1086. }
  1087. // Check for zero length username.
  1088. if(wcslen(pszUsername) == 0)
  1089. {
  1090. dwError = ERROR_INVALID_PARAMETER;
  1091. goto error;
  1092. }
  1093. //
  1094. // Lookup the user SID.
  1095. //
  1096. cchDomain = MAX_COMPUTERNAME_LENGTH + 1;
  1097. if(!GetComputerNameW(szDomain, &cchDomain))
  1098. {
  1099. dwError = GetLastError();
  1100. goto error;
  1101. }
  1102. if(!LookupAccountNameW(szDomain,
  1103. pszUsername,
  1104. NULL,
  1105. &cbSid,
  1106. NULL,
  1107. &cchDomain,
  1108. &AcctType))
  1109. {
  1110. dwError = GetLastError();
  1111. if(dwError != ERROR_INSUFFICIENT_BUFFER)
  1112. {
  1113. goto error;
  1114. }
  1115. }
  1116. pSid = (PBYTE)LocalAlloc(LPTR, cbSid);
  1117. if(pSid == NULL)
  1118. {
  1119. dwError = ERROR_NOT_ENOUGH_MEMORY;
  1120. goto error;
  1121. }
  1122. if(cchDomain > MAX_COMPUTERNAME_LENGTH + 1)
  1123. {
  1124. dwError = ERROR_NOT_ENOUGH_MEMORY;
  1125. goto error;
  1126. }
  1127. if(!LookupAccountNameW(szDomain,
  1128. pszUsername,
  1129. pSid,
  1130. &cbSid,
  1131. szDomain,
  1132. &cchDomain,
  1133. &AcctType))
  1134. {
  1135. dwError = GetLastError();
  1136. goto error;
  1137. }
  1138. //
  1139. // Import the recovery private
  1140. //
  1141. // The private comes in the form of a version DWORD, followed by a certificate,
  1142. // followed directly by a pvk import blob, so skip past the certificate
  1143. //
  1144. if((cbRecoveryPrivate < 2*sizeof(DWORD)) ||
  1145. ( *((DWORD *)pbRecoveryPrivate) != RECOVERY_BLOB_MAGIC) ||
  1146. ( *((DWORD *)(pbRecoveryPrivate + sizeof(DWORD))) != RECOVERY_BLOB_VERSION))
  1147. {
  1148. dwError = ERROR_INVALID_DATA;
  1149. goto error;
  1150. }
  1151. //
  1152. // Decrypt the recovery file in order to obtain the user's
  1153. // old password. Shazam!
  1154. //
  1155. dwError = DecryptRecoveryPassword(pSid,
  1156. pbRecoveryPrivate,
  1157. cbRecoveryPrivate,
  1158. &pszOldPassword);
  1159. if(dwError != ERROR_SUCCESS)
  1160. {
  1161. CPSImpersonateClient(&ServerContext);
  1162. goto error;
  1163. }
  1164. //
  1165. // Set the user's password to the new value.
  1166. //
  1167. dwError = ResetLocalUserPassword(szDomain,
  1168. pszUsername,
  1169. pszOldPassword,
  1170. pszNewPassword);
  1171. if(dwError != STATUS_SUCCESS)
  1172. {
  1173. D_DebugLog((DEB_ERROR, "Error changing user's password (0x%x)\n", dwError));
  1174. goto error;
  1175. }
  1176. error:
  1177. if(pSid)
  1178. {
  1179. LocalFree(pSid);
  1180. }
  1181. if(pszOldPassword)
  1182. {
  1183. RtlSecureZeroMemory(pszOldPassword, wcslen(pszOldPassword) * sizeof(WCHAR));
  1184. LocalFree(pszOldPassword);
  1185. pszOldPassword = NULL;
  1186. }
  1187. if(pbNewPassword && cbNewPassword)
  1188. {
  1189. RtlSecureZeroMemory(pbNewPassword, cbNewPassword);
  1190. }
  1191. CPSDeleteServerContext(&ServerContext);
  1192. D_DebugLog((DEB_TRACE_API, "s_SSRecoverPassword returned 0x%x\n", dwError));
  1193. return dwError;
  1194. }
  1195. DWORD
  1196. DecryptRecoveryPassword(
  1197. IN PSID pUserSid,
  1198. IN PBYTE pbRecoveryPrivate,
  1199. IN DWORD cbRecoveryPrivate,
  1200. OUT LPWSTR *ppszPassword)
  1201. {
  1202. PRECOVERY_SUPPLEMENTAL_CREDENTIAL pCred = NULL;
  1203. DWORD cbCred = 0;
  1204. HCRYPTPROV hProv = 0;
  1205. HCRYPTKEY hkRecoveryPrivate = 0;
  1206. PBYTE pbPasswordBuffer = NULL;
  1207. DWORD cbPasswordBuffer = 0;
  1208. DWORD dwError;
  1209. D_DebugLog((DEB_TRACE, "DecryptRecoveryPassword\n"));
  1210. //
  1211. // Read the recovery data.
  1212. //
  1213. dwError = RecoveryRetrieveSupplementalCredential(pUserSid,
  1214. &pCred,
  1215. &cbCred);
  1216. if(ERROR_SUCCESS != dwError)
  1217. {
  1218. goto error;
  1219. }
  1220. if((NULL == pCred) ||
  1221. (sizeof(RECOVERY_SUPPLEMENTAL_CREDENTIAL) > cbCred) ||
  1222. ( sizeof(RECOVERY_SUPPLEMENTAL_CREDENTIAL) +
  1223. pCred->cbRecoveryCertHashSize +
  1224. pCred->cbRecoveryCertSignatureSize +
  1225. pCred->cbEncryptedPassword > cbCred) ||
  1226. (pCred->dwVersion != RECOVERY_SUPPLEMENTAL_CREDENTIAL_VERSION))
  1227. {
  1228. dwError = ERROR_INVALID_DATA;
  1229. goto error;
  1230. }
  1231. //
  1232. // Import the recovery private key into CryptoAPI.
  1233. //
  1234. if(!CryptAcquireContext(&hProv, NULL, MS_STRONG_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
  1235. {
  1236. dwError = GetLastError();
  1237. goto error;
  1238. }
  1239. if(!CryptImportKey(hProv,
  1240. pbRecoveryPrivate + 2*sizeof(DWORD),
  1241. cbRecoveryPrivate - 2*sizeof(DWORD),
  1242. 0,
  1243. 0,
  1244. &hkRecoveryPrivate))
  1245. {
  1246. dwError = GetLastError();
  1247. goto error;
  1248. }
  1249. cbPasswordBuffer = cbCred - (sizeof(RECOVERY_SUPPLEMENTAL_CREDENTIAL) +
  1250. pCred->cbRecoveryCertHashSize +
  1251. pCred->cbRecoveryCertSignatureSize);
  1252. pbPasswordBuffer = (PBYTE)LocalAlloc(LPTR, cbPasswordBuffer);
  1253. if(NULL == pbPasswordBuffer)
  1254. {
  1255. dwError = ERROR_NOT_ENOUGH_MEMORY;
  1256. goto error;
  1257. }
  1258. CopyMemory(pbPasswordBuffer,
  1259. (PBYTE)(pCred + 1) +
  1260. pCred->cbRecoveryCertHashSize +
  1261. pCred->cbRecoveryCertSignatureSize,
  1262. cbPasswordBuffer);
  1263. //
  1264. // OAEP padding includes random data, as well as a
  1265. // verification mechanism, so we don't need to worry about
  1266. // salting the password.
  1267. //
  1268. if(!CryptDecrypt(hkRecoveryPrivate,
  1269. 0,
  1270. TRUE,
  1271. 0, //CRYPT_OAEP,
  1272. pbPasswordBuffer,
  1273. &cbPasswordBuffer))
  1274. {
  1275. dwError = GetLastError();
  1276. D_DebugLog((DEB_ERROR, "Error 0x%x decrypting user's password\n", dwError));
  1277. goto error;
  1278. }
  1279. //
  1280. // Zero terminate the password.
  1281. //
  1282. *((LPWSTR)pbPasswordBuffer + cbPasswordBuffer/sizeof(WCHAR)) = L'\0';
  1283. *ppszPassword = (LPWSTR)pbPasswordBuffer;
  1284. pbPasswordBuffer = NULL;
  1285. dwError = ERROR_SUCCESS;
  1286. error:
  1287. if(pbPasswordBuffer)
  1288. {
  1289. LocalFree(pbPasswordBuffer);
  1290. }
  1291. if(pCred)
  1292. {
  1293. RtlSecureZeroMemory((PBYTE)pCred, cbCred);
  1294. LocalFree(pCred);
  1295. }
  1296. if(hkRecoveryPrivate)
  1297. {
  1298. CryptDestroyKey(hkRecoveryPrivate);
  1299. }
  1300. if(hProv)
  1301. {
  1302. CryptReleaseContext(hProv, 0);
  1303. }
  1304. D_DebugLog((DEB_TRACE, "DecryptRecoveryPassword returned 0x%x\n", dwError));
  1305. return dwError;
  1306. }
  1307. DWORD
  1308. ResetLocalUserPassword(
  1309. LPWSTR pszDomain,
  1310. LPWSTR pszUsername,
  1311. LPWSTR pszOldPassword,
  1312. LPWSTR pszNewPassword)
  1313. {
  1314. UNICODE_STRING Domain;
  1315. UNICODE_STRING Username;
  1316. UNICODE_STRING OldPassword;
  1317. UNICODE_STRING NewPassword;
  1318. NTSTATUS Status;
  1319. HANDLE hToken = NULL;
  1320. D_DebugLog((DEB_TRACE, "ResetLocalUserPassword\n"));
  1321. //
  1322. // Cruft up a local token for the user.
  1323. //
  1324. RtlInitUnicodeString(&Domain, pszDomain);
  1325. RtlInitUnicodeString(&Username, pszUsername);
  1326. RtlInitUnicodeString(&OldPassword, pszOldPassword);
  1327. RtlInitUnicodeString(&NewPassword, pszNewPassword);
  1328. Status = PRCreateLocalToken(&Username, &Domain, &hToken);
  1329. if(!NT_SUCCESS(Status))
  1330. {
  1331. goto cleanup;
  1332. }
  1333. //
  1334. // Set the password on the user account to the new value.
  1335. //
  1336. Status = SamIChangePasswordForeignUser2(NULL,
  1337. &Username,
  1338. &NewPassword,
  1339. hToken,
  1340. USER_CHANGE_PASSWORD);
  1341. if(!NT_SUCCESS(Status))
  1342. {
  1343. goto cleanup;
  1344. }
  1345. //
  1346. // Notify DPAPI of the password change. This will cause the CREDHIST
  1347. // and RECOVERY files to be updated, and the master key files to be
  1348. // re-encrypted with the new password.
  1349. //
  1350. LsaINotifyPasswordChanged(&Domain,
  1351. &Username,
  1352. NULL,
  1353. NULL,
  1354. &OldPassword,
  1355. &NewPassword,
  1356. TRUE);
  1357. cleanup:
  1358. if(hToken)
  1359. {
  1360. CloseHandle(hToken);
  1361. }
  1362. D_DebugLog((DEB_TRACE, "ResetLocalUserPassword returned 0x%x\n", Status));
  1363. return Status;
  1364. }
  1365. NTSTATUS
  1366. CreateSystemDirectory(
  1367. LPCWSTR lpPathName)
  1368. {
  1369. NTSTATUS Status;
  1370. OBJECT_ATTRIBUTES Obja;
  1371. HANDLE Handle;
  1372. UNICODE_STRING FileName;
  1373. IO_STATUS_BLOCK IoStatusBlock;
  1374. RTL_RELATIVE_NAME_U RelativeName;
  1375. PVOID FreeBuffer;
  1376. if(!RtlDosPathNameToRelativeNtPathName_U( lpPathName,
  1377. &FileName,
  1378. NULL,
  1379. &RelativeName))
  1380. {
  1381. return STATUS_OBJECT_PATH_NOT_FOUND;
  1382. }
  1383. FreeBuffer = FileName.Buffer;
  1384. if ( RelativeName.RelativeName.Length )
  1385. {
  1386. FileName = RelativeName.RelativeName;
  1387. }
  1388. else
  1389. {
  1390. RelativeName.ContainingDirectory = NULL;
  1391. }
  1392. InitializeObjectAttributes( &Obja,
  1393. &FileName,
  1394. OBJ_CASE_INSENSITIVE,
  1395. RelativeName.ContainingDirectory,
  1396. NULL );
  1397. // Creating the directory with attribute FILE_ATTRIBUTE_SYSTEM to avoid inheriting encryption
  1398. // property from parent directory
  1399. Status = NtCreateFile( &Handle,
  1400. FILE_LIST_DIRECTORY | SYNCHRONIZE,
  1401. &Obja,
  1402. &IoStatusBlock,
  1403. NULL,
  1404. FILE_ATTRIBUTE_SYSTEM,
  1405. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1406. FILE_CREATE,
  1407. FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT,
  1408. NULL,
  1409. 0L );
  1410. RtlReleaseRelativeName(&RelativeName);
  1411. RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
  1412. if(NT_SUCCESS(Status))
  1413. {
  1414. NtClose(Handle);
  1415. return STATUS_SUCCESS;
  1416. }
  1417. else
  1418. {
  1419. return Status;
  1420. }
  1421. }
  1422. /*++
  1423. Create all subdirectories if they do not exists starting at
  1424. szCreationStartPoint.
  1425. szCreationStartPoint must point to a character within the null terminated
  1426. buffer specified by the szFullPath parameter.
  1427. Note that szCreationStartPoint should not point at the first character
  1428. of a drive root, eg:
  1429. d:\foo\bar\bilge\water
  1430. \\server\share\foo\bar
  1431. \\?\d:\big\path\bilge\water
  1432. Instead, szCreationStartPoint should point beyond these components, eg:
  1433. bar\bilge\water
  1434. foo\bar
  1435. big\path\bilge\water
  1436. This function does not implement logic for adjusting to compensate for these
  1437. inputs because the environment it was design to be used in causes the input
  1438. szCreationStartPoint to point well into the szFullPath input buffer.
  1439. --*/
  1440. DWORD
  1441. DPAPICreateNestedDirectories(
  1442. IN LPWSTR szFullPath,
  1443. IN LPWSTR szCreationStartPoint // must point in null-terminated range of szFullPath
  1444. )
  1445. {
  1446. DWORD i;
  1447. DWORD cchRemaining;
  1448. DWORD dwLastError = STATUS_SUCCESS;
  1449. BOOL fSuccess = FALSE;
  1450. if( szCreationStartPoint < szFullPath ||
  1451. szCreationStartPoint > (lstrlenW(szFullPath) + szFullPath)
  1452. )
  1453. {
  1454. return STATUS_INVALID_PARAMETER;
  1455. }
  1456. cchRemaining = lstrlenW( szCreationStartPoint );
  1457. //
  1458. // scan from left to right in the szCreationStartPoint string
  1459. // looking for directory delimiter.
  1460. //
  1461. for ( i = 0 ; i < cchRemaining ; i++ )
  1462. {
  1463. WCHAR charReplaced = szCreationStartPoint[ i ];
  1464. if( charReplaced == L'\\' || charReplaced == L'/' )
  1465. {
  1466. szCreationStartPoint[ i ] = L'\0';
  1467. dwLastError = CreateSystemDirectory(szFullPath);
  1468. szCreationStartPoint[ i ] = charReplaced;
  1469. if(dwLastError == STATUS_OBJECT_NAME_COLLISION)
  1470. {
  1471. dwLastError = STATUS_SUCCESS;
  1472. }
  1473. //
  1474. // Continue onwards regardless of errors, trying to create the
  1475. // specified subdirectories. This is done to address the obscure
  1476. // scenario where the Bypass Traverse Checking Privilege allows
  1477. // the caller to create directories below an existing path where
  1478. // one component denies the user access. We just keep trying
  1479. // and the last CreateSystemDirectory() result is returned to
  1480. // the caller.
  1481. //
  1482. }
  1483. }
  1484. return dwLastError;
  1485. }