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.

2266 lines
58 KiB

  1. /*++
  2. Copyright (c) 1996-2000 Microsoft Corporation
  3. Module Name:
  4. session.c
  5. Abstract:
  6. This module contains routines to support communication with the LSA
  7. (Local Security Authority) to permit querying of active sessions.
  8. This module also supports calling into the LSA to retrieve a credential
  9. derived from a logged on user specified by Logon Identifier.
  10. Author:
  11. Scott Field (sfield) 02-Mar-97
  12. Pete Skelly (petesk) 09-May-00 - added credential history and signature code
  13. --*/
  14. #include <pch.cpp>
  15. #pragma hdrstop
  16. #include <ntmsv1_0.h>
  17. #include <crypt.h>
  18. #include <userenv.h>
  19. #include <userenvp.h>
  20. #include "debug.h"
  21. #include "passrec.h"
  22. #define HMAC_K_PADSIZE (64)
  23. #define CREDENTIAL_HISTORY_VERSION 1
  24. #define CREDENTIAL_HISTORY_SALT_SIZE 16 // 128 bits
  25. #define DEFAULT_KEY_GEN_ALG CALG_HMAC
  26. #define DEFAULT_ENCRYPTION_ALG CALG_3DES
  27. typedef struct _CREDENTIAL_HISTORY_HEADER
  28. {
  29. DWORD dwVersion;
  30. GUID CredentialID;
  31. DWORD dwPreviousCredOffset;
  32. } CREDENTIAL_HISTORY_HEADER, *PCREDENTIAL_HISTORY_HEADER;
  33. typedef struct _CREDENTIAL_HISTORY
  34. {
  35. CREDENTIAL_HISTORY_HEADER Header;
  36. DWORD dwFlags;
  37. DWORD KeyGenAlg;
  38. DWORD cIterationCount; // pbkdf2 iteration count
  39. DWORD cbSid; // sid is used as mixing bytes
  40. DWORD KeyEncrAlg;
  41. DWORD cbShaOwf;
  42. DWORD cbNtOwf;
  43. BYTE Salt[CREDENTIAL_HISTORY_SALT_SIZE];
  44. } CREDENTIAL_HISTORY, *PCREDENTIAL_HISTORY;
  45. typedef struct _CREDENTIAL_HISTORY_MAP
  46. {
  47. PSID pUserSid;
  48. WCHAR wszFilePath[MAX_PATH+1];
  49. HANDLE hHistoryFile;
  50. HANDLE hMapping;
  51. DWORD dwMapSize;
  52. PBYTE pMapping;
  53. struct _CREDENTIAL_HISTORY_MAP *pNext;
  54. } CREDENTIAL_HISTORY_MAP, *PCREDENTIAL_HISTORY_MAP;
  55. RTL_CRITICAL_SECTION g_csCredHistoryCache;
  56. DWORD
  57. OpenCredentialHistoryMap(
  58. HANDLE hUserToken,
  59. LPWSTR pszProfilePath,
  60. PCREDENTIAL_HISTORY_MAP *ppMap,
  61. PCREDENTIAL_HISTORY *ppCurrent);
  62. PCREDENTIAL_HISTORY
  63. GetPreviousCredentialHistory(
  64. PCREDENTIAL_HISTORY_MAP pMap,
  65. PCREDENTIAL_HISTORY pCurrent
  66. );
  67. DWORD
  68. CloseCredentialHistoryMap(PCREDENTIAL_HISTORY_MAP pMap,
  69. BOOL fReader);
  70. DWORD
  71. DestroyCredentialHistoryMap(PCREDENTIAL_HISTORY_MAP pMap);
  72. VOID
  73. DeriveWithHMAC_SHA1(
  74. IN PBYTE pbKeyMaterial, // input key material
  75. IN DWORD cbKeyMaterial,
  76. IN PBYTE pbData, // input mixing data
  77. IN DWORD cbData,
  78. IN OUT BYTE rgbHMAC[A_SHA_DIGEST_LEN] // output buffer
  79. );
  80. DWORD
  81. DecryptCredentialHistory(PCREDENTIAL_HISTORY pCredential,
  82. BYTE rgbDecryptingCredential[A_SHA_DIGEST_LEN],
  83. BYTE rgbShaOwf[A_SHA_DIGEST_LEN],
  84. BYTE rgbNTOwf[A_SHA_DIGEST_LEN]);
  85. DWORD
  86. RetrieveCurrentDerivedCredential(
  87. IN LUID *pLogonId,
  88. IN BOOL fDPOWF,
  89. IN PBYTE pbMixingBytes,
  90. IN DWORD cbMixingBytes,
  91. IN OUT BYTE rgbDerivedCredential[A_SHA_DIGEST_LEN]
  92. )
  93. {
  94. NTSTATUS ntstatus;
  95. DWORD rc = ERROR_SUCCESS;
  96. NTSTATUS AuthPackageStatus;
  97. PMSV1_0_DERIVECRED_REQUEST pDeriveCredentialRequest;
  98. DWORD cbDeriveCredentialRequest;
  99. PMSV1_0_DERIVECRED_RESPONSE pDeriveCredentialResponse;
  100. ULONG DeriveCredentialResponseLength;
  101. UNICODE_STRING PackageName;
  102. HANDLE hToken = NULL;
  103. RtlInitUnicodeString(&PackageName, L"MICROSOFT_AUTHENTICATION_PACKAGE_V1_0");
  104. //
  105. // must specify mixing bytes.
  106. //
  107. if( cbMixingBytes == 0 || pbMixingBytes == NULL )
  108. {
  109. return ERROR_INVALID_PARAMETER;
  110. }
  111. //
  112. // Ask authentication package to provide derived credential associated
  113. // with the specified logon identifier.
  114. // note: submit buffer must be a single contiguous block.
  115. //
  116. cbDeriveCredentialRequest = sizeof(MSV1_0_DERIVECRED_REQUEST) + cbMixingBytes;
  117. pDeriveCredentialRequest = (MSV1_0_DERIVECRED_REQUEST *)SSAlloc( cbDeriveCredentialRequest );
  118. if( pDeriveCredentialRequest == NULL )
  119. {
  120. return ERROR_INVALID_PARAMETER;
  121. }
  122. pDeriveCredentialRequest->MessageType = MsV1_0DeriveCredential;
  123. CopyMemory( &(pDeriveCredentialRequest->LogonId), pLogonId, sizeof(LUID) );
  124. pDeriveCredentialRequest->DeriveCredType = fDPOWF?MSV1_0_DERIVECRED_TYPE_SHA1_V2:MSV1_0_DERIVECRED_TYPE_SHA1;
  125. pDeriveCredentialRequest->DeriveCredInfoLength = cbMixingBytes;
  126. CopyMemory(pDeriveCredentialRequest->DeriveCredSubmitBuffer, pbMixingBytes, cbMixingBytes);
  127. if(!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &hToken))
  128. {
  129. return GetLastError();
  130. }
  131. RevertToSelf();
  132. // Make this call as local system
  133. ntstatus = LsaICallPackage(
  134. &PackageName,
  135. pDeriveCredentialRequest,
  136. cbDeriveCredentialRequest,
  137. (PVOID *)&pDeriveCredentialResponse,
  138. &DeriveCredentialResponseLength,
  139. &AuthPackageStatus
  140. );
  141. SSFree( pDeriveCredentialRequest );
  142. if (!SetThreadToken(NULL, hToken))
  143. {
  144. rc = GetLastError();
  145. }
  146. CloseHandle(hToken);
  147. if(!NT_SUCCESS(ntstatus))
  148. {
  149. return RtlNtStatusToDosError(ntstatus);
  150. }
  151. if (ERROR_SUCCESS == rc)
  152. {
  153. CopyMemory( rgbDerivedCredential,
  154. pDeriveCredentialResponse->DeriveCredReturnBuffer,
  155. pDeriveCredentialResponse->DeriveCredInfoLength
  156. );
  157. }
  158. RtlSecureZeroMemory( pDeriveCredentialResponse->DeriveCredReturnBuffer,
  159. pDeriveCredentialResponse->DeriveCredInfoLength
  160. );
  161. LsaIFreeReturnBuffer( pDeriveCredentialResponse );
  162. return rc;
  163. }
  164. DWORD
  165. QueryDerivedCredential(
  166. IN OUT GUID *CredentialID,
  167. IN LUID *pLogonId,
  168. IN DWORD dwFlags,
  169. IN PBYTE pbMixingBytes,
  170. IN DWORD cbMixingBytes,
  171. IN OUT BYTE rgbDerivedCredential[A_SHA_DIGEST_LEN]
  172. )
  173. {
  174. DWORD dwLastError = ERROR_SUCCESS;
  175. PCREDENTIAL_HISTORY pCurrent = NULL;
  176. PCREDENTIAL_HISTORY pNext = NULL;
  177. BYTE rgbCurrentDerivedCredential[A_SHA_DIGEST_LEN];
  178. BYTE rgbCurrentShaOWF[A_SHA_DIGEST_LEN];
  179. BYTE rgbCurrentNTOWF[A_SHA_DIGEST_LEN];
  180. PCREDENTIAL_HISTORY_MAP pHistoryMap= NULL;
  181. WCHAR wszTextualSid[MAX_PATH + 1];
  182. WCHAR szProfilePath[MAX_PATH + 1];
  183. DWORD cchTextualSid = 0;
  184. BOOL fIsRoot = TRUE;
  185. //
  186. // Get path to user's profile data. This will typically look something
  187. // like "c:\Documents and Settings\<user>\Application Data".
  188. //
  189. dwLastError = PRGetProfilePath(NULL,
  190. szProfilePath);
  191. if( dwLastError != ERROR_SUCCESS )
  192. {
  193. D_DebugLog((DEB_TRACE_API, "DPAPIChangePassword returned 0x%x\n", dwLastError));
  194. return dwLastError;
  195. }
  196. //
  197. // work-around the fact that much of this (new) code is not thread-safe.
  198. //
  199. RtlEnterCriticalSection(&g_csCredHistoryCache);
  200. //
  201. // Open history file
  202. //
  203. dwLastError = OpenCredentialHistoryMap(NULL, szProfilePath, &pHistoryMap, &pCurrent);
  204. while((ERROR_SUCCESS == dwLastError) &&
  205. (pCurrent) &&
  206. (0 == (dwFlags & USE_ROOT_CREDENTIAL)))
  207. {
  208. //
  209. // We're looking for a specific credential ID
  210. //
  211. if((NULL != CredentialID) &&
  212. (0 == memcmp(&pCurrent->Header.CredentialID, CredentialID, sizeof(GUID))))
  213. {
  214. // found it,
  215. break;
  216. }
  217. pNext = GetPreviousCredentialHistory(pHistoryMap, pCurrent);
  218. if(NULL == pNext)
  219. {
  220. if(NULL != CredentialID)
  221. {
  222. // If we're looking for a specific credential, but
  223. // couldn't find it, then return an error
  224. dwLastError = NTE_BAD_KEY;
  225. }
  226. else
  227. {
  228. // no credential id was specified, so default to the oldest one.
  229. dwLastError = ERROR_SUCCESS;
  230. }
  231. break;
  232. }
  233. //
  234. // get the textual sid
  235. //
  236. cchTextualSid = MAX_PATH;
  237. if(!GetTextualSid((PBYTE)(pNext + 1), wszTextualSid, &cchTextualSid))
  238. {
  239. dwLastError = ERROR_INVALID_PARAMETER;
  240. break;
  241. }
  242. if(fIsRoot)
  243. {
  244. //
  245. // crack the next credential using the current
  246. // credentials
  247. //
  248. dwLastError = RetrieveCurrentDerivedCredential(pLogonId,
  249. (0 != (pNext->dwFlags & USE_DPAPI_OWF)), // always use
  250. (PBYTE)wszTextualSid,
  251. cchTextualSid*sizeof(WCHAR),
  252. rgbCurrentDerivedCredential);
  253. fIsRoot = FALSE;
  254. }
  255. else
  256. {
  257. //
  258. // calculate the current derived credential used to decrypt the
  259. // next credential history structure by using the decrypted OWF
  260. // from the previous pass
  261. DeriveWithHMAC_SHA1((0 != (pNext->dwFlags & USE_DPAPI_OWF))?rgbCurrentShaOWF:rgbCurrentNTOWF,
  262. A_SHA_DIGEST_LEN,
  263. (PBYTE)wszTextualSid,
  264. cchTextualSid*sizeof(WCHAR),
  265. rgbCurrentDerivedCredential);
  266. //
  267. // we don't need the OWF anymore, so zap it.
  268. //
  269. RtlSecureZeroMemory(rgbCurrentShaOWF, A_SHA_DIGEST_LEN);
  270. RtlSecureZeroMemory(rgbCurrentNTOWF, A_SHA_DIGEST_LEN);
  271. }
  272. if(ERROR_SUCCESS != dwLastError)
  273. {
  274. break;
  275. }
  276. //
  277. // used the derived credential to decrypt
  278. // the data blob of pNext
  279. //
  280. dwLastError = DecryptCredentialHistory(pNext,
  281. rgbCurrentDerivedCredential,
  282. rgbCurrentShaOWF,
  283. rgbCurrentNTOWF);
  284. if(ERROR_SUCCESS != dwLastError)
  285. {
  286. break;
  287. }
  288. pCurrent = pNext;
  289. pNext = NULL;
  290. }
  291. if(ERROR_SUCCESS == dwLastError)
  292. {
  293. if(fIsRoot)
  294. {
  295. //
  296. // crack the next credential using the current
  297. // credentials
  298. //
  299. dwLastError = RetrieveCurrentDerivedCredential(pLogonId,
  300. (0 != (dwFlags & USE_DPAPI_OWF)),
  301. pbMixingBytes,
  302. cbMixingBytes,
  303. rgbDerivedCredential);
  304. if(ERROR_SUCCESS == dwLastError)
  305. {
  306. if((CredentialID != NULL) &&
  307. (0 != (dwFlags & USE_ROOT_CREDENTIAL)))
  308. {
  309. CopyMemory(CredentialID, &pCurrent->Header.CredentialID, sizeof(GUID));
  310. }
  311. }
  312. }
  313. else
  314. {
  315. //
  316. // calculate the current derived credential used to decrypt the
  317. // next credential history structure by using the decrypted OWF
  318. // from the previous pass
  319. DeriveWithHMAC_SHA1((0 != (dwFlags & USE_DPAPI_OWF))?rgbCurrentShaOWF:rgbCurrentNTOWF,
  320. A_SHA_DIGEST_LEN,
  321. pbMixingBytes,
  322. cbMixingBytes,
  323. rgbDerivedCredential);
  324. }
  325. }
  326. //
  327. // Clear out any owf's we may have lying around
  328. //
  329. RtlSecureZeroMemory(rgbCurrentShaOWF, A_SHA_DIGEST_LEN);
  330. RtlSecureZeroMemory(rgbCurrentNTOWF, A_SHA_DIGEST_LEN);
  331. if(pHistoryMap)
  332. {
  333. CloseCredentialHistoryMap(pHistoryMap, TRUE);
  334. }
  335. RtlLeaveCriticalSection(&g_csCredHistoryCache);
  336. return dwLastError;
  337. }
  338. VOID
  339. DeriveWithHMAC_SHA1(
  340. IN PBYTE pbKeyMaterial, // input key material
  341. IN DWORD cbKeyMaterial,
  342. IN PBYTE pbData, // input mixing data
  343. IN DWORD cbData,
  344. IN OUT BYTE rgbHMAC[A_SHA_DIGEST_LEN] // output buffer
  345. )
  346. {
  347. unsigned __int64 rgbKipad[ HMAC_K_PADSIZE/sizeof(unsigned __int64) ];
  348. unsigned __int64 rgbKopad[ HMAC_K_PADSIZE/sizeof(unsigned __int64) ];
  349. A_SHA_CTX sSHAHash;
  350. DWORD dwBlock;
  351. // truncate
  352. if( cbKeyMaterial > HMAC_K_PADSIZE )
  353. {
  354. cbKeyMaterial = HMAC_K_PADSIZE;
  355. }
  356. ZeroMemory(rgbKipad, sizeof(rgbKipad));
  357. ZeroMemory(rgbKopad, sizeof(rgbKopad));
  358. CopyMemory(rgbKipad, pbKeyMaterial, cbKeyMaterial);
  359. CopyMemory(rgbKopad, pbKeyMaterial, cbKeyMaterial);
  360. // Kipad, Kopad are padded sMacKey. Now XOR across...
  361. for( dwBlock = 0; dwBlock < (HMAC_K_PADSIZE/sizeof(unsigned __int64)) ; dwBlock++ )
  362. {
  363. rgbKipad[dwBlock] ^= 0x3636363636363636;
  364. rgbKopad[dwBlock] ^= 0x5C5C5C5C5C5C5C5C;
  365. }
  366. // prepend Kipad to data, Hash to get H1
  367. A_SHAInit(&sSHAHash);
  368. A_SHAUpdate(&sSHAHash, (PBYTE)rgbKipad, sizeof(rgbKipad));
  369. A_SHAUpdate(&sSHAHash, pbData, cbData);
  370. // Finish off the hash
  371. A_SHAFinal(&sSHAHash, rgbHMAC);
  372. // prepend Kopad to H1, hash to get HMAC
  373. // note: done in place to avoid buffer copies
  374. // final hash: output value into passed-in buffer
  375. A_SHAInit(&sSHAHash);
  376. A_SHAUpdate(&sSHAHash, (PBYTE)rgbKopad, sizeof(rgbKopad));
  377. A_SHAUpdate(&sSHAHash, rgbHMAC, A_SHA_DIGEST_LEN);
  378. A_SHAFinal(&sSHAHash, rgbHMAC);
  379. RtlSecureZeroMemory( rgbKipad, sizeof(rgbKipad) );
  380. RtlSecureZeroMemory( rgbKopad, sizeof(rgbKopad) );
  381. RtlSecureZeroMemory( &sSHAHash, sizeof(sSHAHash) );
  382. return;
  383. }
  384. DWORD
  385. DecryptCredentialHistory(PCREDENTIAL_HISTORY pCredential,
  386. BYTE rgbDecryptingCredential[A_SHA_DIGEST_LEN],
  387. BYTE rgbShaOwf[A_SHA_DIGEST_LEN],
  388. BYTE rgbNTOwf[A_SHA_DIGEST_LEN])
  389. {
  390. DWORD dwLastError = ERROR_SUCCESS;
  391. DWORD j;
  392. BYTE rgbSymKey[A_SHA_DIGEST_LEN*2]; // big enough to handle 3des keys
  393. //
  394. // Derive the protection key
  395. //
  396. for(j=0; j < 2; j++)
  397. {
  398. if(!PKCS5DervivePBKDF2( rgbDecryptingCredential,
  399. A_SHA_DIGEST_LEN,
  400. pCredential->Salt,
  401. CREDENTIAL_HISTORY_SALT_SIZE,
  402. pCredential->KeyGenAlg,
  403. pCredential->cIterationCount,
  404. j+1,
  405. rgbSymKey + j*A_SHA_DIGEST_LEN))
  406. {
  407. dwLastError = ERROR_INVALID_DATA;
  408. goto cleanup;
  409. }
  410. }
  411. if (CALG_3DES == pCredential->KeyEncrAlg)
  412. {
  413. DES3TABLE s3DESKey;
  414. DWORD iBlock;
  415. BYTE ResultBlock[2*A_SHA_DIGEST_LEN+DES_BLOCKLEN ];
  416. //
  417. // Round up blocks. it's assumed that the total block size was verified
  418. // earlier
  419. //
  420. DWORD cBlocks = (pCredential->cbShaOwf + pCredential->cbNtOwf + DES_BLOCKLEN - 1)/DES_BLOCKLEN;
  421. BYTE feedback[ DES_BLOCKLEN ];
  422. // initialize 3des key
  423. //
  424. if((pCredential->cbShaOwf != A_SHA_DIGEST_LEN) ||
  425. (pCredential->cbNtOwf != A_SHA_DIGEST_LEN))
  426. {
  427. return ERROR_INVALID_PARAMETER;
  428. }
  429. tripledes3key(&s3DESKey, rgbSymKey);
  430. //
  431. // IV is derived from the DES_BLOCKLEN bytes of the calculated
  432. // rgbSymKey, after the 3des key
  433. CopyMemory(feedback, rgbSymKey + DES3_KEYSIZE, DES_BLOCKLEN);
  434. for(iBlock=0; iBlock < cBlocks; iBlock++)
  435. {
  436. CBC(tripledes,
  437. DES_BLOCKLEN,
  438. ResultBlock+iBlock*DES_BLOCKLEN,
  439. ((PBYTE)(pCredential + 1) + pCredential->cbSid)+iBlock*DES_BLOCKLEN,
  440. &s3DESKey,
  441. DECRYPT,
  442. feedback);
  443. }
  444. CopyMemory(rgbShaOwf, ResultBlock, A_SHA_DIGEST_LEN);
  445. CopyMemory(rgbNTOwf, ResultBlock + A_SHA_DIGEST_LEN, A_SHA_DIGEST_LEN);
  446. RtlSecureZeroMemory(ResultBlock, sizeof(ResultBlock));
  447. }
  448. else
  449. {
  450. dwLastError = ERROR_INVALID_DATA;
  451. goto cleanup;
  452. }
  453. cleanup:
  454. return dwLastError;
  455. }
  456. DWORD
  457. EncryptCredentialHistory(BYTE rgbEncryptingCredential[A_SHA_DIGEST_LEN],
  458. DWORD dwFlags,
  459. BYTE SHAOwfToEncrypt[A_SHA_DIGEST_LEN],
  460. BYTE NTOwfToEncrypt[A_SHA_DIGEST_LEN],
  461. PCREDENTIAL_HISTORY *ppCredential,
  462. DWORD *pcbCredential)
  463. {
  464. DWORD dwLastError = ERROR_SUCCESS;
  465. DWORD j;
  466. BYTE rgbSymKey[A_SHA_DIGEST_LEN*2]; // big enough to handle 3des keys
  467. PCREDENTIAL_HISTORY pCred = NULL;
  468. DWORD cbCred = 0;
  469. HANDLE hToken = NULL;
  470. PSID pSidUser = NULL;
  471. BYTE ResultBuffer[A_SHA_DIGEST_LEN * 2]; // 2 * A_SHA_DIGEST_LEN
  472. DWORD cBlocks = 0;
  473. DWORD cbBlock = 0;
  474. cbBlock = DES_BLOCKLEN;
  475. if(!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken))
  476. {
  477. dwLastError = GetLastError();
  478. goto error;
  479. }
  480. if(!GetTokenUserSid(hToken, &pSidUser))
  481. {
  482. dwLastError = GetLastError();
  483. goto error;
  484. }
  485. cBlocks = sizeof(ResultBuffer)/DES_BLOCKLEN; // this should be 5
  486. cbCred= sizeof(CREDENTIAL_HISTORY) +
  487. GetLengthSid(pSidUser) +
  488. cBlocks*cbBlock;
  489. pCred = (PCREDENTIAL_HISTORY)LocalAlloc(LMEM_ZEROINIT, cbCred);
  490. if(NULL == pCred)
  491. {
  492. dwLastError = ERROR_NOT_ENOUGH_MEMORY;
  493. goto error;
  494. }
  495. pCred->dwFlags = dwFlags;
  496. pCred->KeyGenAlg = DEFAULT_KEY_GEN_ALG;
  497. pCred->cIterationCount = GetIterationCount();
  498. pCred->KeyEncrAlg = DEFAULT_ENCRYPTION_ALG;
  499. pCred->cbShaOwf = A_SHA_DIGEST_LEN;
  500. pCred->cbNtOwf = A_SHA_DIGEST_LEN;
  501. pCred->cbSid = GetLengthSid(pSidUser);
  502. CopyMemory((PBYTE)(pCred+1), (PBYTE)pSidUser, pCred->cbSid);
  503. if(!RtlGenRandom(pCred->Salt, CREDENTIAL_HISTORY_SALT_SIZE))
  504. {
  505. dwLastError = GetLastError();
  506. goto error;
  507. }
  508. for(j=0; j < 2; j++)
  509. {
  510. if(!PKCS5DervivePBKDF2( rgbEncryptingCredential,
  511. A_SHA_DIGEST_LEN,
  512. pCred->Salt,
  513. CREDENTIAL_HISTORY_SALT_SIZE,
  514. pCred->KeyGenAlg,
  515. pCred->cIterationCount,
  516. j+1,
  517. rgbSymKey + j*A_SHA_DIGEST_LEN))
  518. {
  519. dwLastError = ERROR_INVALID_DATA;
  520. goto error;
  521. }
  522. }
  523. if (CALG_3DES == pCred->KeyEncrAlg)
  524. {
  525. DES3TABLE s3DESKey;
  526. DWORD iBlock;
  527. BYTE feedback[ DES_BLOCKLEN ];
  528. // initialize 3des key
  529. tripledes3key(&s3DESKey, rgbSymKey);
  530. //
  531. // IV is derived from the DES_BLOCKLEN bytes of the calculated
  532. // rgbSymKey, after the 3des key
  533. CopyMemory(feedback, rgbSymKey + DES3_KEYSIZE, DES_BLOCKLEN);
  534. CopyMemory(ResultBuffer, SHAOwfToEncrypt, A_SHA_DIGEST_LEN);
  535. CopyMemory(ResultBuffer+A_SHA_DIGEST_LEN, NTOwfToEncrypt, A_SHA_DIGEST_LEN);
  536. for(iBlock=0; iBlock < cBlocks; iBlock++)
  537. {
  538. CBC(tripledes,
  539. DES_BLOCKLEN,
  540. ((PBYTE)(pCred + 1) + pCred->cbSid)+iBlock*DES_BLOCKLEN,
  541. ResultBuffer + iBlock*DES_BLOCKLEN,
  542. &s3DESKey,
  543. ENCRYPT,
  544. feedback);
  545. }
  546. RtlSecureZeroMemory(ResultBuffer, sizeof(ResultBuffer));
  547. }
  548. else
  549. {
  550. dwLastError = ERROR_INVALID_DATA;
  551. goto error;
  552. }
  553. *ppCredential = pCred;
  554. *pcbCredential = cbCred;
  555. pCred = NULL;
  556. error:
  557. if(pCred)
  558. {
  559. LocalFree(pCred);
  560. }
  561. if(hToken)
  562. {
  563. CloseHandle(hToken);
  564. }
  565. if(pSidUser)
  566. {
  567. SSFree(pSidUser);
  568. }
  569. return dwLastError;
  570. }
  571. #define PRODUCT_ROOT_STRING L"\\Microsoft\\Protect\\"
  572. #define HISTORY_FILENAME L"CREDHIST"
  573. DWORD
  574. CreateCredentialHistoryMap(
  575. HANDLE hUserToken,
  576. LPWSTR pszProfilePath,
  577. PCREDENTIAL_HISTORY_MAP *ppMap,
  578. BOOL fRead)
  579. {
  580. PCREDENTIAL_HISTORY_MAP pMap = NULL;
  581. PCREDENTIAL_HISTORY_MAP pCached = NULL;
  582. DWORD dwError = ERROR_SUCCESS;
  583. DWORD dwHighFileSize = 0;
  584. DWORD cbUserStorageRoot;
  585. HANDLE hTemporaryMapping = NULL;
  586. WCHAR szFilePath[MAX_PATH + 1];
  587. PWSTR pszCreationStartPoint;
  588. NTSTATUS Status;
  589. if(NULL == ppMap)
  590. {
  591. return ERROR_INVALID_PARAMETER;
  592. }
  593. pMap = (PCREDENTIAL_HISTORY_MAP)LocalAlloc(LPTR, sizeof(CREDENTIAL_HISTORY_MAP));
  594. if(NULL == pMap)
  595. {
  596. return ERROR_NOT_ENOUGH_MEMORY;
  597. }
  598. //
  599. // Obtain the user's SID.
  600. //
  601. if(hUserToken)
  602. {
  603. if(!GetTokenUserSid(hUserToken, &pMap->pUserSid))
  604. {
  605. dwError = GetLastError();
  606. goto error;
  607. }
  608. }
  609. else
  610. {
  611. HANDLE hToken;
  612. if(!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken))
  613. {
  614. dwError = ERROR_NO_TOKEN;
  615. goto error;
  616. }
  617. if(!GetTokenUserSid(hToken, &pMap->pUserSid))
  618. {
  619. dwError = GetLastError();
  620. CloseHandle(hToken);
  621. goto error;
  622. }
  623. CloseHandle(hToken);
  624. }
  625. //
  626. // Open map file
  627. //
  628. if(wcslen(pszProfilePath) + wcslen(PRODUCT_ROOT_STRING) + wcslen(HISTORY_FILENAME) > MAX_PATH)
  629. {
  630. dwError = ERROR_INVALID_PARAMETER;
  631. goto error;
  632. }
  633. wcscpy(szFilePath, pszProfilePath);
  634. // Build the path in a separate buffer just in case we have to create
  635. // the directory.
  636. pszCreationStartPoint = szFilePath + wcslen(szFilePath) + sizeof(WCHAR);
  637. wcscat(szFilePath, PRODUCT_ROOT_STRING);
  638. // Copy the path plus the filename over to the map structure.
  639. wcscpy(pMap->wszFilePath, szFilePath);
  640. wcscat(pMap->wszFilePath, HISTORY_FILENAME);
  641. //
  642. // Create the history file.
  643. //
  644. dwError = ERROR_SUCCESS;
  645. while(TRUE)
  646. {
  647. pMap->hHistoryFile = CreateFileWithRetries(
  648. pMap->wszFilePath,
  649. GENERIC_READ | GENERIC_WRITE,
  650. 0, // cannot share this file when open
  651. NULL,
  652. OPEN_ALWAYS,
  653. FILE_ATTRIBUTE_HIDDEN |
  654. FILE_ATTRIBUTE_SYSTEM |
  655. FILE_FLAG_RANDOM_ACCESS,
  656. NULL
  657. );
  658. if(INVALID_HANDLE_VALUE == pMap->hHistoryFile)
  659. {
  660. dwError = GetLastError();
  661. if(dwError == ERROR_PATH_NOT_FOUND)
  662. {
  663. // Create the DPAPI directory, and then try to create the file
  664. // again.
  665. Status = DPAPICreateNestedDirectories(szFilePath,
  666. pszCreationStartPoint);
  667. if(!NT_SUCCESS(Status))
  668. {
  669. goto error;
  670. }
  671. dwError = ERROR_SUCCESS;
  672. continue;
  673. }
  674. else
  675. {
  676. goto error;
  677. }
  678. }
  679. break;
  680. }
  681. pMap->dwMapSize = GetFileSize(pMap->hHistoryFile, &dwHighFileSize);
  682. if((-1 == pMap->dwMapSize) ||
  683. (dwHighFileSize != 0))
  684. {
  685. dwError = ERROR_INVALID_PARAMETER;
  686. goto error;
  687. }
  688. //
  689. // If this map is too small, we need to create a new header
  690. //
  691. if(pMap->dwMapSize < sizeof(CREDENTIAL_HISTORY_HEADER))
  692. {
  693. PCREDENTIAL_HISTORY_HEADER pHeader = NULL;
  694. pMap->dwMapSize = sizeof(CREDENTIAL_HISTORY_HEADER);
  695. hTemporaryMapping = CreateFileMapping(pMap->hHistoryFile,
  696. NULL,
  697. PAGE_READWRITE,
  698. 0,
  699. pMap->dwMapSize,
  700. NULL);
  701. if(NULL == hTemporaryMapping)
  702. {
  703. dwError = GetLastError();
  704. goto error;
  705. }
  706. pHeader = (PCREDENTIAL_HISTORY_HEADER)(PBYTE)MapViewOfFile(hTemporaryMapping,
  707. FILE_MAP_WRITE,
  708. 0,
  709. 0,
  710. 0);
  711. if(NULL == pHeader)
  712. {
  713. dwError = GetLastError();
  714. goto error;
  715. }
  716. //
  717. // Write a fresh header into the cred history file
  718. //
  719. pHeader->dwPreviousCredOffset = 0;
  720. pHeader->dwVersion = CREDENTIAL_HISTORY_VERSION;
  721. dwError = UuidCreate( &pHeader->CredentialID );
  722. FlushViewOfFile(pHeader, pMap->dwMapSize);
  723. UnmapViewOfFile(pHeader);
  724. if(ERROR_SUCCESS != dwError)
  725. {
  726. goto error;
  727. }
  728. }
  729. *ppMap = pMap;
  730. pMap = NULL;
  731. error:
  732. if(pMap)
  733. {
  734. DestroyCredentialHistoryMap(pMap);
  735. }
  736. if(hTemporaryMapping)
  737. {
  738. CloseHandle(hTemporaryMapping);
  739. }
  740. return dwError;
  741. }
  742. DWORD
  743. OpenCredentialHistoryMap(
  744. HANDLE hUserToken,
  745. LPWSTR pszProfilePath,
  746. PCREDENTIAL_HISTORY_MAP *ppMap,
  747. PCREDENTIAL_HISTORY *ppCurrent)
  748. {
  749. PCREDENTIAL_HISTORY_MAP pMap = NULL;
  750. PCREDENTIAL_HISTORY pCurrent = NULL;
  751. DWORD dwError = ERROR_SUCCESS;
  752. DWORD dwHighFileSize = 0;
  753. WCHAR szFilePath[MAX_PATH+1];
  754. DWORD cbUserStorageRoot;
  755. HANDLE hTemporaryMapping = NULL;
  756. if((NULL == ppMap) ||
  757. (NULL == ppCurrent))
  758. {
  759. return ERROR_INVALID_PARAMETER;
  760. }
  761. dwError = CreateCredentialHistoryMap(
  762. hUserToken,
  763. pszProfilePath,
  764. &pMap,
  765. TRUE);
  766. if(ERROR_SUCCESS != dwError)
  767. {
  768. goto error;
  769. }
  770. if(NULL == pMap->hMapping)
  771. {
  772. //
  773. // Open a read-only mapping of the file
  774. //
  775. pMap->hMapping = CreateFileMapping(pMap->hHistoryFile,
  776. NULL,
  777. PAGE_READONLY,
  778. dwHighFileSize,
  779. pMap->dwMapSize,
  780. NULL);
  781. if(NULL == pMap->hMapping)
  782. {
  783. dwError = GetLastError();
  784. goto error;
  785. }
  786. }
  787. if(NULL == pMap->pMapping)
  788. {
  789. pMap->pMapping = (PBYTE)MapViewOfFile(pMap->hMapping,
  790. FILE_MAP_READ,
  791. 0,
  792. 0,
  793. 0);
  794. if(NULL == pMap->pMapping)
  795. {
  796. dwError = GetLastError();
  797. goto error;
  798. }
  799. }
  800. pCurrent = GetPreviousCredentialHistory(pMap, NULL);
  801. if(NULL == pCurrent)
  802. {
  803. dwError = ERROR_INVALID_PARAMETER;
  804. goto error;
  805. }
  806. *ppMap = pMap;
  807. pMap = NULL;
  808. *ppCurrent = pCurrent;
  809. error:
  810. if(pMap)
  811. {
  812. CloseCredentialHistoryMap(pMap, TRUE);
  813. }
  814. return dwError;
  815. }
  816. PCREDENTIAL_HISTORY
  817. GetPreviousCredentialHistory(
  818. PCREDENTIAL_HISTORY_MAP pMap,
  819. PCREDENTIAL_HISTORY pCurrent
  820. )
  821. {
  822. PCREDENTIAL_HISTORY pPrevious = NULL;
  823. DWORD cbSize = 0;
  824. if((NULL == pMap) ||
  825. (NULL == pMap->pMapping))
  826. {
  827. return NULL;
  828. }
  829. if(pMap->dwMapSize < sizeof(CREDENTIAL_HISTORY_HEADER))
  830. {
  831. return NULL;
  832. }
  833. if(NULL == pCurrent)
  834. {
  835. pPrevious = (PCREDENTIAL_HISTORY)((PBYTE)pMap->pMapping +
  836. pMap->dwMapSize - sizeof(CREDENTIAL_HISTORY_HEADER));
  837. }
  838. else
  839. {
  840. if(((PBYTE)pCurrent < pMap->pMapping) ||
  841. ((PBYTE)pCurrent - pMap->pMapping >= (__int64)pMap->dwMapSize) ||
  842. ((PBYTE)pCurrent - pMap->pMapping < (__int64)pCurrent->Header.dwPreviousCredOffset))
  843. {
  844. return NULL;
  845. }
  846. pPrevious = (PCREDENTIAL_HISTORY)((PBYTE)pCurrent - pCurrent->Header.dwPreviousCredOffset);
  847. cbSize = sizeof(CREDENTIAL_HISTORY) + pPrevious->cbSid;
  848. if(cbSize > pCurrent->Header.dwPreviousCredOffset)
  849. {
  850. return NULL;
  851. }
  852. cbSize = pCurrent->Header.dwPreviousCredOffset - cbSize;
  853. if(cbSize < pPrevious->cbShaOwf + pPrevious->cbNtOwf)
  854. {
  855. return NULL;
  856. }
  857. if(cbSize % DES_BLOCKLEN)
  858. {
  859. return NULL;
  860. }
  861. if(!IsValidSid((PSID)(pPrevious+1)))
  862. {
  863. return NULL;
  864. }
  865. if(GetLengthSid((PSID)(pPrevious+1)) != pPrevious->cbSid)
  866. {
  867. return NULL;
  868. }
  869. }
  870. // validate found credential history
  871. if(pPrevious->Header.dwVersion != CREDENTIAL_HISTORY_VERSION)
  872. {
  873. return NULL;
  874. }
  875. return pPrevious;
  876. }
  877. DWORD
  878. DestroyCredentialHistoryMap(PCREDENTIAL_HISTORY_MAP pMap)
  879. {
  880. if(NULL == pMap)
  881. {
  882. return ERROR_SUCCESS;
  883. }
  884. if (pMap->pUserSid)
  885. {
  886. SSFree(pMap->pUserSid);
  887. pMap->pUserSid = NULL;
  888. }
  889. if(pMap->pMapping)
  890. {
  891. UnmapViewOfFile(pMap->pMapping);
  892. pMap->pMapping = NULL;
  893. }
  894. if(pMap->hMapping)
  895. {
  896. CloseHandle(pMap->hMapping);
  897. pMap->hMapping = NULL;
  898. }
  899. if(pMap->hHistoryFile)
  900. {
  901. CloseHandle(pMap->hHistoryFile);
  902. pMap->hHistoryFile = NULL;
  903. }
  904. LocalFree(pMap);
  905. return ERROR_SUCCESS;
  906. }
  907. DWORD
  908. CloseCredentialHistoryMap(PCREDENTIAL_HISTORY_MAP pMap, BOOL fReader)
  909. {
  910. if(NULL == pMap)
  911. {
  912. return ERROR_INVALID_PARAMETER;
  913. }
  914. if(pMap->pMapping)
  915. {
  916. UnmapViewOfFile(pMap->pMapping);
  917. pMap->pMapping = NULL;
  918. }
  919. if(pMap->hMapping)
  920. {
  921. CloseHandle(pMap->hMapping);
  922. pMap->hMapping = NULL;
  923. }
  924. if(pMap->hHistoryFile)
  925. {
  926. CloseHandle(pMap->hHistoryFile);
  927. pMap->hHistoryFile = NULL;
  928. }
  929. return DestroyCredentialHistoryMap(pMap);
  930. }
  931. DWORD
  932. AppendCredentialHistoryMap(
  933. PCREDENTIAL_HISTORY_MAP pMap,
  934. PCREDENTIAL_HISTORY pCredHistory,
  935. DWORD cbCredHistory
  936. )
  937. {
  938. DWORD dwLastError = ERROR_SUCCESS;
  939. DWORD dwHighFileSize = 0;
  940. DWORD dwLowFileSize = 0;
  941. HANDLE hWriteMapping = NULL;
  942. LPVOID pWriteMapping = NULL;
  943. PCREDENTIAL_HISTORY_HEADER pHeader = NULL;
  944. if((NULL == pCredHistory) ||
  945. (NULL == pMap))
  946. {
  947. return ERROR_INVALID_PARAMETER;
  948. }
  949. dwLowFileSize = pMap->dwMapSize + cbCredHistory;
  950. if(dwLowFileSize < pMap->dwMapSize)
  951. {
  952. //we wrapped, so fail.
  953. dwLastError = ERROR_INVALID_DATA;
  954. goto error;
  955. }
  956. // Create a new mapping
  957. hWriteMapping = CreateFileMapping(pMap->hHistoryFile,
  958. NULL,
  959. PAGE_READWRITE,
  960. 0,
  961. dwLowFileSize,
  962. NULL);
  963. if(NULL == hWriteMapping)
  964. {
  965. dwLastError = GetLastError();
  966. goto error;
  967. }
  968. pWriteMapping = (PCREDENTIAL_HISTORY_HEADER)(PBYTE)MapViewOfFile(hWriteMapping,
  969. FILE_MAP_WRITE,
  970. 0,
  971. 0,
  972. dwLowFileSize);
  973. if(NULL == pWriteMapping)
  974. {
  975. dwLastError = GetLastError();
  976. goto error;
  977. }
  978. //
  979. // Append the rest of the current entry
  980. //
  981. CopyMemory((PBYTE)pWriteMapping + pMap->dwMapSize,
  982. (PBYTE)pCredHistory + sizeof(CREDENTIAL_HISTORY_HEADER),
  983. cbCredHistory - sizeof(CREDENTIAL_HISTORY_HEADER));
  984. pHeader = (PCREDENTIAL_HISTORY_HEADER)((PBYTE)pWriteMapping +
  985. pMap->dwMapSize +
  986. cbCredHistory -
  987. sizeof(CREDENTIAL_HISTORY_HEADER));
  988. //
  989. // Write a fresh header into the cred history file
  990. //
  991. pHeader->dwPreviousCredOffset = cbCredHistory;
  992. pHeader->dwVersion = CREDENTIAL_HISTORY_VERSION;
  993. dwLastError = UuidCreate( &pHeader->CredentialID );
  994. pMap->dwMapSize = dwLowFileSize;
  995. //
  996. // Flush and close write mapping
  997. //
  998. if(ERROR_SUCCESS == dwLastError)
  999. {
  1000. if(!FlushViewOfFile(pWriteMapping, pMap->dwMapSize))
  1001. {
  1002. dwLastError = GetLastError();
  1003. }
  1004. }
  1005. UnmapViewOfFile(pWriteMapping);
  1006. if(ERROR_SUCCESS != dwLastError)
  1007. {
  1008. goto error;
  1009. }
  1010. CloseHandle(hWriteMapping);
  1011. hWriteMapping = NULL;
  1012. // Remap the read mapping to bump up the size
  1013. if(pMap->pMapping)
  1014. {
  1015. UnmapViewOfFile(pMap->pMapping);
  1016. pMap->pMapping = NULL;
  1017. }
  1018. if(pMap->hMapping)
  1019. {
  1020. CloseHandle(pMap->hMapping);
  1021. pMap->hMapping = NULL;
  1022. }
  1023. pMap->hMapping = CreateFileMapping(pMap->hHistoryFile,
  1024. NULL,
  1025. PAGE_READONLY,
  1026. dwHighFileSize,
  1027. pMap->dwMapSize,
  1028. NULL);
  1029. if(NULL == pMap->hMapping)
  1030. {
  1031. dwLastError = GetLastError();
  1032. goto error;
  1033. }
  1034. pMap->pMapping = (PBYTE)MapViewOfFile(pMap->hMapping,
  1035. FILE_MAP_READ,
  1036. 0,
  1037. 0,
  1038. 0);
  1039. if(NULL == pMap->pMapping)
  1040. {
  1041. dwLastError = GetLastError();
  1042. goto error;
  1043. }
  1044. error:
  1045. if(hWriteMapping)
  1046. {
  1047. CloseHandle(hWriteMapping);
  1048. }
  1049. return dwLastError;
  1050. }
  1051. DWORD
  1052. DPAPIChangePassword(
  1053. HANDLE hUserToken,
  1054. BYTE OldPasswordShaOWF[A_SHA_DIGEST_LEN],
  1055. BYTE OldPasswordNTOWF[A_SHA_DIGEST_LEN],
  1056. BYTE NewPasswordOWF[A_SHA_DIGEST_LEN])
  1057. {
  1058. DWORD dwLastError = ERROR_SUCCESS;
  1059. PCREDENTIAL_HISTORY_MAP pMap = NULL;
  1060. PCREDENTIAL_HISTORY pHistory = NULL;
  1061. DWORD cbHistory = 0;
  1062. WCHAR wszUserSid[MAX_PATH+1];
  1063. DWORD cchUserSid = 0;
  1064. BYTE NewEncryptingCred[A_SHA_DIGEST_LEN];
  1065. WCHAR szProfilePath[MAX_PATH + 1];
  1066. HANDLE hOldUser = NULL;
  1067. D_DebugLog((DEB_TRACE_API, "DPAPIChangePassword\n"));
  1068. //
  1069. // Get path to user's profile data. This will typically look something
  1070. // like "c:\Documents and Settings\<user>\Application Data".
  1071. //
  1072. dwLastError = PRGetProfilePath(hUserToken,
  1073. szProfilePath);
  1074. if( dwLastError != ERROR_SUCCESS )
  1075. {
  1076. D_DebugLog((DEB_TRACE_API, "DPAPIChangePassword returned 0x%x\n", dwLastError));
  1077. return dwLastError;
  1078. }
  1079. //
  1080. // work-around the fact that much of this (new) code is not thread-safe.
  1081. //
  1082. RtlEnterCriticalSection(&g_csCredHistoryCache);
  1083. dwLastError = CreateCredentialHistoryMap(
  1084. hUserToken,
  1085. szProfilePath,
  1086. &pMap,
  1087. FALSE);
  1088. if(ERROR_SUCCESS != dwLastError)
  1089. {
  1090. goto error;
  1091. }
  1092. //
  1093. // Get textual SID of user.
  1094. //
  1095. cchUserSid = MAX_PATH;
  1096. if(!GetUserTextualSid(hUserToken,
  1097. wszUserSid,
  1098. &cchUserSid))
  1099. {
  1100. dwLastError = ERROR_INVALID_DATA;
  1101. goto error;
  1102. }
  1103. //
  1104. // Encrypt the credential history goo.
  1105. //
  1106. if(hUserToken)
  1107. {
  1108. if(!OpenThreadToken(GetCurrentThread(),
  1109. TOKEN_IMPERSONATE | TOKEN_READ,
  1110. TRUE,
  1111. &hOldUser))
  1112. {
  1113. hOldUser = NULL;
  1114. }
  1115. if(!ImpersonateLoggedOnUser(hUserToken))
  1116. {
  1117. dwLastError = GetLastError();
  1118. goto error;
  1119. }
  1120. }
  1121. DeriveWithHMAC_SHA1(NewPasswordOWF,
  1122. A_SHA_DIGEST_LEN,
  1123. (PBYTE)wszUserSid,
  1124. cchUserSid*sizeof(WCHAR),
  1125. NewEncryptingCred);
  1126. dwLastError = EncryptCredentialHistory(NewEncryptingCred,
  1127. USE_DPAPI_OWF,
  1128. OldPasswordShaOWF,
  1129. OldPasswordNTOWF,
  1130. &pHistory,
  1131. &cbHistory);
  1132. if(hOldUser)
  1133. {
  1134. //
  1135. // This code probably never gets executed. hOldUser should be NULL, as this routine
  1136. // looks like called in the SYSTEM context.
  1137. // Even if the hOldUser is not NULL, the failure of SetThreadToken should no prevent
  1138. // continue execution of this routine. Let's ignore the return value of SetThreadToken
  1139. // here.
  1140. //
  1141. (void) SetThreadToken(NULL, hOldUser);
  1142. }
  1143. if(ERROR_SUCCESS != dwLastError)
  1144. {
  1145. goto error;
  1146. }
  1147. //
  1148. // Update the CREDHIST file.
  1149. //
  1150. dwLastError = AppendCredentialHistoryMap(pMap, pHistory, cbHistory);
  1151. if(ERROR_SUCCESS != dwLastError)
  1152. {
  1153. goto error;
  1154. }
  1155. error:
  1156. if(hOldUser)
  1157. {
  1158. CloseHandle(hOldUser);
  1159. hOldUser = NULL;
  1160. }
  1161. if(pMap)
  1162. {
  1163. CloseCredentialHistoryMap(pMap, FALSE);
  1164. }
  1165. RtlLeaveCriticalSection(&g_csCredHistoryCache);
  1166. if(pHistory)
  1167. {
  1168. RtlSecureZeroMemory(pHistory, cbHistory);
  1169. LocalFree(pHistory);
  1170. }
  1171. RtlSecureZeroMemory(NewEncryptingCred, A_SHA_DIGEST_LEN);
  1172. D_DebugLog((DEB_TRACE_API, "DPAPIChangePassword returned 0x%x\n", dwLastError));
  1173. return dwLastError;
  1174. }
  1175. #define SIGNATURE_SALT_SIZE (16)
  1176. #define CRED_SIGNATURE_VERSION 1
  1177. typedef struct _CRED_SIGNATURE
  1178. {
  1179. DWORD dwVersion;
  1180. GUID CredentialID;
  1181. DWORD cIterations;
  1182. BYTE Salt[SIGNATURE_SALT_SIZE];
  1183. DWORD cbSid;
  1184. DWORD cbSignature;
  1185. } CRED_SIGNATURE, *PCRED_SIGNATURE;
  1186. DWORD
  1187. LogonCredGenerateSignatureKey(
  1188. IN LUID *pLogonId,
  1189. IN DWORD dwFlags,
  1190. IN PBYTE pbCurrentOWF,
  1191. IN PCRED_SIGNATURE pSignature,
  1192. OUT BYTE rgbSignatureKey[A_SHA_DIGEST_LEN])
  1193. {
  1194. DWORD dwLastError = ERROR_SUCCESS;
  1195. BYTE rgbDerivedCredential[A_SHA_DIGEST_LEN];
  1196. D_DebugLog((DEB_TRACE_API, "LogonCredGenerateSignatureKey\n"));
  1197. if(NULL == pbCurrentOWF)
  1198. {
  1199. dwLastError = QueryDerivedCredential(&pSignature->CredentialID,
  1200. pLogonId,
  1201. dwFlags,
  1202. (PBYTE)(pSignature+1),
  1203. pSignature->cbSid,
  1204. rgbDerivedCredential);
  1205. if(ERROR_SUCCESS != dwLastError)
  1206. {
  1207. goto error;
  1208. }
  1209. }
  1210. else
  1211. {
  1212. // D_DebugLog((DEB_TRACE_BUFFERS, "Input CurrentOWF:\n"));
  1213. // D_DPAPIDumpHexData(DEB_TRACE_BUFFERS, " ", pbCurrentOWF, A_SHA_DIGEST_LEN);
  1214. DeriveWithHMAC_SHA1(pbCurrentOWF,
  1215. A_SHA_DIGEST_LEN,
  1216. (PBYTE)(pSignature+1),
  1217. pSignature->cbSid,
  1218. rgbDerivedCredential);
  1219. if(dwFlags & USE_ROOT_CREDENTIAL)
  1220. {
  1221. ZeroMemory(&pSignature->CredentialID, sizeof(GUID));
  1222. }
  1223. }
  1224. // D_DebugLog((DEB_TRACE_BUFFERS, "Computed DerivedCredential:\n"));
  1225. // D_DPAPIDumpHexData(DEB_TRACE_BUFFERS, " ", rgbDerivedCredential, sizeof(rgbDerivedCredential));
  1226. if(!PKCS5DervivePBKDF2( rgbDerivedCredential,
  1227. A_SHA_DIGEST_LEN,
  1228. pSignature->Salt,
  1229. SIGNATURE_SALT_SIZE,
  1230. CALG_HMAC,
  1231. pSignature->cIterations,
  1232. 1,
  1233. rgbSignatureKey))
  1234. {
  1235. dwLastError = ERROR_INVALID_DATA;
  1236. goto error;
  1237. }
  1238. // D_DebugLog((DEB_TRACE_BUFFERS, "Computed SignatureKey:\n"));
  1239. // D_DPAPIDumpHexData(DEB_TRACE_BUFFERS, " ", rgbSignatureKey, A_SHA_DIGEST_LEN);
  1240. error:
  1241. RtlSecureZeroMemory(rgbDerivedCredential, A_SHA_DIGEST_LEN);
  1242. D_DebugLog((DEB_TRACE_API, "LogonCredGenerateSignatureKey returned 0x%x\n", dwLastError));
  1243. return dwLastError;
  1244. }
  1245. DWORD
  1246. LogonCredGenerateSignature(
  1247. IN HANDLE hUserToken,
  1248. IN PBYTE pbData,
  1249. IN DWORD cbData,
  1250. IN PBYTE pbCurrentOWF,
  1251. OUT PBYTE *ppbSignature,
  1252. OUT DWORD *pcbSignature)
  1253. {
  1254. DWORD dwLastError = ERROR_SUCCESS;
  1255. LUID LogonId;
  1256. PCRED_SIGNATURE pSignature = NULL;
  1257. DWORD cbSignature = 0;
  1258. BYTE rgbSignatureKey[A_SHA_DIGEST_LEN];
  1259. PSID pUserSid = NULL;
  1260. D_DebugLog((DEB_TRACE_API, "LogonCredGenerateSignature\n"));
  1261. cbSignature = sizeof(CRED_SIGNATURE);
  1262. if(!GetTokenAuthenticationId(hUserToken, &LogonId))
  1263. {
  1264. dwLastError = GetLastError();
  1265. goto error;
  1266. }
  1267. if(!GetTokenUserSid(hUserToken, &pUserSid))
  1268. {
  1269. dwLastError = GetLastError();
  1270. goto error;
  1271. }
  1272. D_DebugLog((DEB_TRACE_BUFFERS, "User SID:\n"));
  1273. D_DPAPIDumpHexData(DEB_TRACE_BUFFERS, " ", (PBYTE)pUserSid, GetLengthSid(pUserSid));
  1274. cbSignature += GetLengthSid(pUserSid);
  1275. cbSignature += A_SHA_DIGEST_LEN;
  1276. pSignature = (PCRED_SIGNATURE)LocalAlloc(LMEM_ZEROINIT, cbSignature);
  1277. if(NULL == pSignature)
  1278. {
  1279. dwLastError = ERROR_NOT_ENOUGH_MEMORY;
  1280. goto error;
  1281. }
  1282. pSignature->cIterations = GetIterationCount();
  1283. pSignature->dwVersion = CRED_SIGNATURE_VERSION;
  1284. pSignature->cbSid = GetLengthSid(pUserSid);
  1285. pSignature->cbSignature = A_SHA_DIGEST_LEN;
  1286. CopyMemory((PBYTE)(pSignature+1),
  1287. pUserSid,
  1288. pSignature->cbSid);
  1289. if(!RtlGenRandom(pSignature->Salt, SIGNATURE_SALT_SIZE))
  1290. {
  1291. dwLastError = GetLastError();
  1292. goto error;
  1293. }
  1294. // D_DebugLog((DEB_TRACE_BUFFERS, "Generated Salt:\n"));
  1295. // D_DPAPIDumpHexData(DEB_TRACE_BUFFERS, " ", pSignature->Salt, SIGNATURE_SALT_SIZE);
  1296. dwLastError = LogonCredGenerateSignatureKey(&LogonId,
  1297. USE_ROOT_CREDENTIAL | USE_DPAPI_OWF,
  1298. pbCurrentOWF,
  1299. pSignature,
  1300. rgbSignatureKey);
  1301. if(ERROR_SUCCESS != dwLastError)
  1302. {
  1303. goto error;
  1304. }
  1305. if(!FMyPrimitiveHMACParam(
  1306. rgbSignatureKey,
  1307. A_SHA_DIGEST_LEN,
  1308. pbData,
  1309. cbData,
  1310. (PBYTE)(pSignature+1) + pSignature->cbSid))
  1311. {
  1312. dwLastError = ERROR_INVALID_DATA;
  1313. }
  1314. else
  1315. {
  1316. D_DebugLog((DEB_TRACE_BUFFERS, "Computed Signature:\n"));
  1317. D_DPAPIDumpHexData(DEB_TRACE_BUFFERS, " ", (PBYTE)(pSignature+1) + pSignature->cbSid, A_SHA_DIGEST_LEN);
  1318. *ppbSignature = (PBYTE)pSignature;
  1319. *pcbSignature = cbSignature;
  1320. pSignature = NULL;
  1321. }
  1322. error:
  1323. if(pSignature)
  1324. {
  1325. RtlSecureZeroMemory(pSignature, cbSignature);
  1326. LocalFree(pSignature);
  1327. }
  1328. if(pUserSid)
  1329. {
  1330. SSFree(pUserSid);
  1331. }
  1332. D_DebugLog((DEB_TRACE_API, "LogonCredGenerateSignature returned 0x%x\n", dwLastError));
  1333. return dwLastError;
  1334. }
  1335. DWORD
  1336. LogonCredVerifySignature(
  1337. IN HANDLE hUserToken, // optional
  1338. IN PBYTE pbData,
  1339. IN DWORD cbData,
  1340. IN PBYTE pbCurrentOWF,
  1341. IN PBYTE pbSignature,
  1342. IN DWORD cbSignature)
  1343. {
  1344. DWORD dwLastError = ERROR_SUCCESS;
  1345. LUID LogonId;
  1346. HANDLE hOldUser = NULL;
  1347. BOOL fIsMember = FALSE;
  1348. PCRED_SIGNATURE pSignature = (PCRED_SIGNATURE)pbSignature;
  1349. BYTE rgbSignatureKey[A_SHA_DIGEST_LEN];
  1350. BYTE rgbSignatureHash[A_SHA_DIGEST_LEN];
  1351. PSID pUserSid = NULL;
  1352. D_DebugLog((DEB_TRACE_API, "LogonCredVerifySignature\n"));
  1353. if(hUserToken)
  1354. {
  1355. if(!OpenThreadToken(GetCurrentThread(),
  1356. TOKEN_IMPERSONATE | TOKEN_READ,
  1357. TRUE,
  1358. &hOldUser))
  1359. {
  1360. hOldUser = NULL;
  1361. }
  1362. if(!ImpersonateLoggedOnUser(hUserToken))
  1363. {
  1364. dwLastError = GetLastError();
  1365. goto error;
  1366. }
  1367. }
  1368. if(!GetThreadAuthenticationId(GetCurrentThread(), &LogonId))
  1369. {
  1370. dwLastError = GetLastError();
  1371. goto error;
  1372. }
  1373. //
  1374. // Verify passed in credential
  1375. //
  1376. if((NULL == pSignature) ||
  1377. (sizeof(CRED_SIGNATURE) > cbSignature) ||
  1378. (pSignature->dwVersion != CRED_SIGNATURE_VERSION) ||
  1379. (pSignature->cbSid + pSignature->cbSignature + sizeof(CRED_SIGNATURE) > cbSignature) ||
  1380. (pSignature->cbSignature != A_SHA_DIGEST_LEN))
  1381. {
  1382. dwLastError = ERROR_INVALID_DATA;
  1383. goto error;
  1384. }
  1385. if(!IsValidSid((PSID)(pSignature+1)))
  1386. {
  1387. dwLastError = ERROR_INVALID_DATA;
  1388. goto error;
  1389. }
  1390. if(pSignature->cbSid != GetLengthSid((PSID)(pSignature+1)))
  1391. {
  1392. dwLastError = ERROR_INVALID_DATA;
  1393. goto error;
  1394. }
  1395. D_DebugLog((DEB_TRACE_BUFFERS, "User SID:\n"));
  1396. D_DPAPIDumpHexData(DEB_TRACE_BUFFERS, " ", (PBYTE)(pSignature+1), pSignature->cbSid);
  1397. if(!CheckTokenMembership( NULL,
  1398. (PSID)(pSignature+1),
  1399. &fIsMember ))
  1400. {
  1401. dwLastError = GetLastError();
  1402. goto error;
  1403. }
  1404. if(!fIsMember)
  1405. {
  1406. dwLastError = ERROR_INVALID_ACCESS;
  1407. goto error;
  1408. }
  1409. dwLastError = LogonCredGenerateSignatureKey(&LogonId,
  1410. USE_DPAPI_OWF,
  1411. pbCurrentOWF,
  1412. pSignature,
  1413. rgbSignatureKey);
  1414. if(ERROR_SUCCESS != dwLastError)
  1415. {
  1416. goto error;
  1417. }
  1418. if(!FMyPrimitiveHMACParam(
  1419. rgbSignatureKey,
  1420. A_SHA_DIGEST_LEN,
  1421. pbData,
  1422. cbData,
  1423. rgbSignatureHash))
  1424. {
  1425. dwLastError = ERROR_INVALID_DATA;
  1426. goto error;
  1427. }
  1428. D_DebugLog((DEB_TRACE_BUFFERS, "Computed Signature:\n"));
  1429. D_DPAPIDumpHexData(DEB_TRACE_BUFFERS, " ", rgbSignatureHash, A_SHA_DIGEST_LEN);
  1430. D_DebugLog((DEB_TRACE_BUFFERS, "Input Signature:\n"));
  1431. D_DPAPIDumpHexData(DEB_TRACE_BUFFERS, " ", (PBYTE)(pSignature+1) + pSignature->cbSid, pSignature->cbSignature);
  1432. if(0 != memcmp(rgbSignatureHash, (PBYTE)(pSignature+1) + pSignature->cbSid, pSignature->cbSignature))
  1433. {
  1434. D_DebugLog((DEB_ERROR, "LogonCredVerifySignature: signature did not verify!\n"));
  1435. dwLastError = ERROR_INVALID_ACCESS;
  1436. goto error;
  1437. }
  1438. error:
  1439. if(hOldUser)
  1440. {
  1441. //
  1442. // We have already done our job. SetThreadToken failure should be ignored here.
  1443. // Further, this code might never been executed. LogonCredVerifySignature() is called
  1444. // from two places. If hUserToken==NULL, -> hOldUser = NULL (the thread was impersonating)
  1445. // If hUserToken != NULL, the caller is in SYSTEM, OpenThreadToken would fail -> hOldUser == NULL.
  1446. // Either way, hOldUser should be NULL.
  1447. //
  1448. (void) SetThreadToken(NULL, hOldUser);
  1449. CloseHandle(hOldUser);
  1450. }
  1451. D_DebugLog((DEB_TRACE_API, "LogonCredVerifySignature returned 0x%x\n", dwLastError));
  1452. return dwLastError;
  1453. }
  1454. DWORD
  1455. DPAPINotifyPasswordChange(
  1456. IN PUNICODE_STRING NetbiosDomainName,
  1457. IN PUNICODE_STRING UserName,
  1458. IN PUNICODE_STRING OldPassword,
  1459. IN PUNICODE_STRING NewPassword)
  1460. {
  1461. DWORD Status = ERROR_SUCCESS;
  1462. BYTE OldPasswordShaOWF[A_SHA_DIGEST_LEN];
  1463. BYTE OldPasswordNTOWF[A_SHA_DIGEST_LEN];
  1464. BYTE NewPasswordOWF[A_SHA_DIGEST_LEN];
  1465. HANDLE hThreadToken = NULL;
  1466. HANDLE hUserToken = NULL;
  1467. PWSTR pszTargetName = NULL;
  1468. PWSTR pszCurrentName = NULL;
  1469. DWORD cchCurrentName;
  1470. PSID pUserSid = NULL;
  1471. PSID pCurrentSid = NULL;
  1472. DWORD cbSid;
  1473. SID_NAME_USE SidType;
  1474. PWSTR pszDomainName = NULL;
  1475. DWORD cchDomainName;
  1476. BOOL fSameUser = FALSE;
  1477. BOOL fLocalAccount = FALSE;
  1478. PROFILEINFOW ProfileInfo;
  1479. BOOL fProfileLoaded = FALSE;
  1480. D_DebugLog((DEB_TRACE_API, "DPAPINotifyPasswordChange\n"));
  1481. //
  1482. // Validate input parameters.
  1483. //
  1484. if((NetbiosDomainName == NULL) ||
  1485. (UserName == NULL) ||
  1486. (NewPassword == NULL))
  1487. {
  1488. Status = ERROR_INVALID_PARAMETER;
  1489. goto cleanup;
  1490. }
  1491. if(NetbiosDomainName->Buffer)
  1492. {
  1493. D_DebugLog((DEB_TRACE_API, " Domain:%ls\n", NetbiosDomainName->Buffer));
  1494. }
  1495. if(UserName->Buffer)
  1496. {
  1497. D_DebugLog((DEB_TRACE_API, " Username:%ls\n", UserName->Buffer));
  1498. }
  1499. #ifdef COMPILED_BY_DEVELOPER
  1500. if(OldPassword)
  1501. {
  1502. D_DebugLog((DEB_TRACE_API, " Old password:%ls\n", OldPassword->Buffer));
  1503. }
  1504. D_DebugLog((DEB_TRACE_API, " New password:%ls\n", NewPassword->Buffer));
  1505. #endif
  1506. //
  1507. // Save off the token for the current thread, and revert back to the
  1508. // local system account.
  1509. //
  1510. if(!OpenThreadToken(GetCurrentThread(),
  1511. TOKEN_QUERY | TOKEN_IMPERSONATE,
  1512. FALSE,
  1513. &hThreadToken))
  1514. {
  1515. hThreadToken = NULL;
  1516. }
  1517. RevertToSelf();
  1518. //
  1519. // Get SID of user whose password is being changed.
  1520. //
  1521. cbSid = 0;
  1522. if(!LookupAccountName(NetbiosDomainName->Buffer,
  1523. UserName->Buffer,
  1524. NULL,
  1525. &cbSid,
  1526. NULL,
  1527. &cchDomainName,
  1528. &SidType))
  1529. {
  1530. Status = GetLastError();
  1531. if(Status != ERROR_INSUFFICIENT_BUFFER)
  1532. {
  1533. goto cleanup;
  1534. }
  1535. }
  1536. pUserSid = LocalAlloc(LPTR, cbSid);
  1537. if(pUserSid == NULL)
  1538. {
  1539. Status = ERROR_NOT_ENOUGH_MEMORY;
  1540. goto cleanup;
  1541. }
  1542. pszDomainName = (PWSTR)LocalAlloc(LPTR, cchDomainName * sizeof(WCHAR));
  1543. if(pszDomainName == NULL)
  1544. {
  1545. Status = ERROR_NOT_ENOUGH_MEMORY;
  1546. goto cleanup;
  1547. }
  1548. if(!LookupAccountName(NetbiosDomainName->Buffer,
  1549. UserName->Buffer,
  1550. pUserSid,
  1551. &cbSid,
  1552. pszDomainName,
  1553. &cchDomainName,
  1554. &SidType))
  1555. {
  1556. Status = GetLastError();
  1557. if(Status != ERROR_INSUFFICIENT_BUFFER)
  1558. {
  1559. goto cleanup;
  1560. }
  1561. }
  1562. //
  1563. // Determine if we're already logged on as the user whose password
  1564. // is being changed. If we are, then we can skip loading the user
  1565. // profile, etc.
  1566. //
  1567. if(hThreadToken != NULL)
  1568. {
  1569. if(!GetTokenUserSid(hThreadToken, &pCurrentSid))
  1570. {
  1571. Status = GetLastError();
  1572. goto cleanup;
  1573. }
  1574. if(EqualSid(pCurrentSid, pUserSid))
  1575. {
  1576. fSameUser = TRUE;
  1577. }
  1578. }
  1579. //
  1580. // Create logon token for user whose password is being changed.
  1581. //
  1582. if(fSameUser)
  1583. {
  1584. hUserToken = hThreadToken;
  1585. }
  1586. else
  1587. {
  1588. D_DebugLog((DEB_TRACE, "Logging on as user whose password is being changed.\n"));
  1589. if(!LogonUser(UserName->Buffer,
  1590. NetbiosDomainName->Buffer,
  1591. NewPassword->Buffer,
  1592. LOGON32_LOGON_INTERACTIVE,
  1593. LOGON32_PROVIDER_DEFAULT,
  1594. &hUserToken))
  1595. {
  1596. Status = GetLastError();
  1597. D_DebugLog((DEB_ERROR, "Unable to log on as user whose password is being changed (0x%x).\n", Status));
  1598. goto cleanup;
  1599. }
  1600. memset(&ProfileInfo, 0, sizeof(ProfileInfo));
  1601. ProfileInfo.dwSize = sizeof(ProfileInfo);
  1602. ProfileInfo.dwFlags = PI_NOUI;
  1603. ProfileInfo.lpUserName = UserName->Buffer;
  1604. if(!LoadUserProfileW(hUserToken, &ProfileInfo))
  1605. {
  1606. Status = GetLastError();
  1607. D_DebugLog((DEB_ERROR, "Error loading user profile (0x%x).\n", Status));
  1608. goto cleanup;
  1609. }
  1610. fProfileLoaded = TRUE;
  1611. }
  1612. //
  1613. // Is this a local account?
  1614. //
  1615. if(NetbiosDomainName->Buffer)
  1616. {
  1617. WCHAR szMachineName[MAX_COMPUTERNAME_LENGTH + 1];
  1618. DWORD cchMachineName;
  1619. cchMachineName = MAX_COMPUTERNAME_LENGTH + 1;
  1620. if(!GetComputerName(szMachineName, &cchMachineName))
  1621. {
  1622. Status = GetLastError();
  1623. goto cleanup;
  1624. }
  1625. if (CSTR_EQUAL == CompareString(
  1626. LOCALE_SYSTEM_DEFAULT,
  1627. NORM_IGNORECASE,
  1628. NetbiosDomainName->Buffer,
  1629. -1, // cchCount1
  1630. szMachineName,
  1631. -1 // cchCount2
  1632. ))
  1633. {
  1634. fLocalAccount = TRUE;
  1635. }
  1636. }
  1637. if(fLocalAccount)
  1638. {
  1639. D_DebugLog((DEB_TRACE, "Local account\n"));
  1640. if(OldPassword == NULL)
  1641. {
  1642. Status = ERROR_INVALID_PARAMETER;
  1643. goto cleanup;
  1644. }
  1645. //
  1646. // Compute hashes of old and new passwords.
  1647. //
  1648. ZeroMemory(OldPasswordShaOWF, A_SHA_DIGEST_LEN);
  1649. ZeroMemory(OldPasswordNTOWF, A_SHA_DIGEST_LEN);
  1650. ZeroMemory(NewPasswordOWF, A_SHA_DIGEST_LEN);
  1651. FMyPrimitiveSHA(
  1652. (PBYTE)OldPassword->Buffer,
  1653. OldPassword->Length,
  1654. OldPasswordShaOWF);
  1655. Status = RtlCalculateNtOwfPassword(
  1656. OldPassword,
  1657. (PLM_OWF_PASSWORD)OldPasswordNTOWF);
  1658. if(Status != ERROR_SUCCESS)
  1659. {
  1660. goto cleanup;
  1661. }
  1662. FMyPrimitiveSHA(
  1663. (PBYTE)NewPassword->Buffer,
  1664. NewPassword->Length,
  1665. NewPasswordOWF);
  1666. #ifdef COMPILED_BY_DEVELOPER
  1667. D_DebugLog((DEB_TRACE, " Old password SHA OWF:\n"));
  1668. D_DPAPIDumpHexData(DEB_TRACE, " ", OldPasswordShaOWF, A_SHA_DIGEST_LEN);
  1669. D_DebugLog((DEB_TRACE, " Old password NT OWF:\n"));
  1670. D_DPAPIDumpHexData(DEB_TRACE, " ", OldPasswordNTOWF, A_SHA_DIGEST_LEN);
  1671. D_DebugLog((DEB_TRACE, " New password SHA OWF:\n"));
  1672. D_DPAPIDumpHexData(DEB_TRACE, " ", NewPasswordOWF, A_SHA_DIGEST_LEN);
  1673. #endif
  1674. //
  1675. // Encrypt the CREDHIST file with the new password and append the new password to
  1676. // the end of the file.
  1677. //
  1678. Status = DPAPIChangePassword(hUserToken,
  1679. OldPasswordShaOWF,
  1680. OldPasswordNTOWF,
  1681. NewPasswordOWF);
  1682. if(Status != ERROR_SUCCESS)
  1683. {
  1684. goto cleanup;
  1685. }
  1686. //
  1687. // Re-synchronize the master keys.
  1688. //
  1689. DPAPISynchronizeMasterKeys(hUserToken);
  1690. //
  1691. // Encrypt the new password with the recovery public key, and store it
  1692. // in the RECOVERY file. This will allow us to recover the password using the
  1693. // recovery floppy, should the user forget it.
  1694. //
  1695. Status = RecoverChangePasswordNotify(hUserToken,
  1696. OldPasswordShaOWF,
  1697. NewPassword);
  1698. if(Status != ERROR_SUCCESS)
  1699. {
  1700. goto cleanup;
  1701. }
  1702. }
  1703. else
  1704. {
  1705. D_DebugLog((DEB_TRACE, "Domain account\n"));
  1706. //
  1707. // Re-synchronize the master keys.
  1708. //
  1709. DPAPISynchronizeMasterKeys(hUserToken);
  1710. }
  1711. cleanup:
  1712. if(pUserSid) LocalFree(pUserSid);
  1713. if(pCurrentSid) LocalFree(pCurrentSid);
  1714. if(pszDomainName) LocalFree(pszDomainName);
  1715. if(pszTargetName) LocalFree(pszTargetName);
  1716. if(pszCurrentName) LocalFree(pszCurrentName);
  1717. if(hUserToken && fProfileLoaded)
  1718. {
  1719. UnloadUserProfile(hUserToken, ProfileInfo.hProfile);
  1720. }
  1721. if(hUserToken && (hUserToken != hThreadToken))
  1722. {
  1723. CloseHandle(hUserToken);
  1724. }
  1725. if(hThreadToken)
  1726. {
  1727. if(!ImpersonateLoggedOnUser(hThreadToken))
  1728. {
  1729. // Unable to impersonate user.
  1730. if(Status == ERROR_SUCCESS)
  1731. {
  1732. Status = GetLastError();
  1733. }
  1734. }
  1735. CloseHandle(hThreadToken);
  1736. }
  1737. D_DebugLog((DEB_TRACE_API, "DPAPINotifyPasswordChange returned 0x%x\n", Status));
  1738. return Status;
  1739. }