Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1425 lines
44 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1995.
  5. //
  6. // File: pct1srv.c
  7. //
  8. // Contents:
  9. //
  10. // Classes:
  11. //
  12. // Functions:
  13. //
  14. // History: 09-23-97 jbanes LSA integration stuff.
  15. //
  16. //----------------------------------------------------------------------------
  17. #include <spbase.h>
  18. #include <pct1msg.h>
  19. #include <pct1prot.h>
  20. #include <ssl2msg.h>
  21. SP_STATUS
  22. Pct1SrvHandleUniHello(
  23. PSPContext pContext,
  24. PSPBuffer pCommInput,
  25. PSsl2_Client_Hello pHello,
  26. PSPBuffer pCommOutput);
  27. SP_STATUS WINAPI
  28. Pct1ServerProtocolHandler(PSPContext pContext,
  29. PSPBuffer pCommInput,
  30. PSPBuffer pCommOutput)
  31. {
  32. SP_STATUS pctRet= 0;
  33. DWORD dwStateTransition;
  34. SP_BEGIN("Pct1ServerProtocolHandler");
  35. if(pCommOutput) pCommOutput->cbData = 0;
  36. /* Protocol handling steps should be listed in most common
  37. * to least common in order to improve performance
  38. */
  39. /* We are not connected, so we're doing
  40. * protocol negotiation of some sort. All protocol
  41. * negotiation messages are sent in the clear */
  42. /* There are no branches in the connecting protocol
  43. * state transition diagram, besides connection and error,
  44. * which means that a simple case statement will do */
  45. /* Do we have enough data to determine what kind of message we have */
  46. /* Do we have enough data to determine what kind of message we have, or how much data we need*/
  47. dwStateTransition = (pContext->State & 0xffff);
  48. if(((pContext->State & 0xffff) != SP_STATE_CONNECTED) &&
  49. ((pContext->State & 0xffff) != PCT1_STATE_RENEGOTIATE) &&
  50. ((pContext->State & 0xffff) != SP_STATE_SHUTDOWN) &&
  51. ((pContext->State & 0xffff) != SP_STATE_SHUTDOWN_PENDING))
  52. {
  53. if(pCommInput->cbData < 3)
  54. {
  55. pctRet = PCT_INT_INCOMPLETE_MSG;
  56. }
  57. }
  58. if(pCommInput->cbData >= 3)
  59. {
  60. dwStateTransition |= (((PUCHAR)pCommInput->pvBuffer)[2]<<16);
  61. }
  62. if(pctRet == PCT_ERR_OK)
  63. {
  64. switch(dwStateTransition)
  65. {
  66. case SP_STATE_SHUTDOWN_PENDING:
  67. // There's no CloseNotify in PCT, so just transition to
  68. // the shutdown state and leave the output buffer empty.
  69. pContext->State = SP_STATE_SHUTDOWN;
  70. break;
  71. case SP_STATE_SHUTDOWN:
  72. return PCT_INT_EXPIRED;
  73. case SP_STATE_CONNECTED:
  74. {
  75. //We're connected, and we got called, so we must be doing a REDO
  76. SPBuffer In;
  77. DWORD cbMessage;
  78. // Transfer the write key over from the application process.
  79. if(pContext->hWriteKey == 0)
  80. {
  81. pctRet = SPGetUserKeys(pContext, SCH_FLAG_WRITE_KEY);
  82. if(pctRet != PCT_ERR_OK)
  83. {
  84. SP_RETURN(SP_LOG_RESULT(pctRet));
  85. }
  86. }
  87. // Calculate size of buffer
  88. pCommOutput->cbData = 0;
  89. cbMessage = pContext->pHashInfo->cbCheckSum +
  90. pContext->pCipherInfo->dwBlockSize +
  91. sizeof(PCT1_MESSAGE_HEADER_EX);
  92. /* are we allocating our own memory? */
  93. if(pCommOutput->pvBuffer == NULL)
  94. {
  95. pCommOutput->pvBuffer = SPExternalAlloc(cbMessage);
  96. if (NULL == pCommOutput->pvBuffer)
  97. {
  98. SP_RETURN(SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY));
  99. }
  100. pCommOutput->cbBuffer = cbMessage;
  101. }
  102. if(cbMessage > pCommOutput->cbBuffer)
  103. {
  104. pCommOutput->cbData = cbMessage;
  105. SP_RETURN(PCT_INT_BUFF_TOO_SMALL);
  106. }
  107. In.pvBuffer = ((char *)pCommOutput->pvBuffer)+3;
  108. In.cbBuffer = pCommOutput->cbBuffer-3;
  109. In.cbData = 1;
  110. ((char *)In.pvBuffer)[0] = PCT1_ET_REDO_CONN;
  111. // Build a Redo Request
  112. pctRet = Pct1EncryptRaw(pContext, &In, pCommOutput, PCT1_ENCRYPT_ESCAPE);
  113. break;
  114. }
  115. /* Server receives client hello */
  116. case (SSL2_MT_CLIENT_HELLO << 16) | UNI_STATE_RECVD_UNIHELLO:
  117. {
  118. PSsl2_Client_Hello pSsl2Hello;
  119. // Attempt to recognize and handle various versions of client
  120. // hello, start by trying to unpickle the most recent version, and
  121. // then next most recent, until one unpickles. Then run the handle
  122. // code. We can also put unpickling and handling code in here for
  123. // SSL messages.
  124. pctRet = Ssl2UnpackClientHello(pCommInput, &pSsl2Hello);
  125. if(PCT_ERR_OK == pctRet)
  126. {
  127. // We know we're doing a full handshake, so allocate a cache entry.
  128. if(!SPCacheRetrieveNew(TRUE,
  129. pContext->pszTarget,
  130. &pContext->RipeZombie))
  131. {
  132. pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
  133. }
  134. else
  135. {
  136. pContext->RipeZombie->fProtocol = pContext->dwProtocol;
  137. pContext->RipeZombie->dwCF = pContext->dwRequestedCF;
  138. pContext->RipeZombie->pServerCred = pContext->pCredGroup;
  139. pctRet = Pct1SrvHandleUniHello(
  140. pContext,
  141. pCommInput,
  142. pSsl2Hello,
  143. pCommOutput);
  144. if (PCT_ERR_OK == pctRet)
  145. {
  146. pContext->State = PCT1_STATE_SERVER_HELLO;
  147. }
  148. }
  149. SPExternalFree(pSsl2Hello);
  150. }
  151. if (SP_FATAL(pctRet))
  152. {
  153. pContext->State = PCT1_STATE_ERROR;
  154. }
  155. break;
  156. }
  157. /* Server receives client hello */
  158. case (PCT1_MSG_CLIENT_HELLO << 16) | PCT1_STATE_RENEGOTIATE:
  159. {
  160. PPct1_Client_Hello pPct1Hello;
  161. UCHAR fRealSessId = 0;
  162. int i;
  163. // This is a renegotiate hello, so we do not restart
  164. pctRet = Pct1UnpackClientHello(
  165. pCommInput,
  166. &pPct1Hello);
  167. if(PCT_ERR_OK == pctRet)
  168. {
  169. // Mark context as "unmapped" so that the new keys will get
  170. // passed to the application process once the handshake is
  171. // completed.
  172. pContext->Flags &= ~CONTEXT_FLAG_MAPPED;
  173. // We need to do a full handshake, so lose the cache entry.
  174. SPCacheDereference(pContext->RipeZombie);
  175. pContext->RipeZombie = NULL;
  176. // Get a new cache item, as restarts are not allowed in
  177. // REDO
  178. if(!SPCacheRetrieveNew(TRUE,
  179. pContext->pszTarget,
  180. &pContext->RipeZombie))
  181. {
  182. pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
  183. }
  184. else
  185. {
  186. pContext->RipeZombie->fProtocol = SP_PROT_PCT1_SERVER;
  187. pContext->RipeZombie->dwCF = pContext->dwRequestedCF;
  188. pContext->RipeZombie->pServerCred = pContext->pCredGroup;
  189. pctRet = Pct1SrvHandleClientHello(pContext,
  190. pCommInput,
  191. pPct1Hello,
  192. pCommOutput);
  193. if(PCT_ERR_OK == pctRet)
  194. {
  195. pContext->State = PCT1_STATE_SERVER_HELLO;
  196. }
  197. }
  198. SPExternalFree(pPct1Hello);
  199. }
  200. else if(pctRet != PCT_INT_INCOMPLETE_MSG)
  201. {
  202. pctRet |= PCT_INT_DROP_CONNECTION;
  203. }
  204. if(SP_FATAL(pctRet))
  205. {
  206. pContext->State = PCT1_STATE_ERROR;
  207. }
  208. break;
  209. }
  210. case (PCT1_MSG_CLIENT_HELLO << 16) | SP_STATE_NONE:
  211. {
  212. PPct1_Client_Hello pPct1Hello;
  213. UCHAR fRealSessId = 0;
  214. int i;
  215. /* Attempt to recognize and handle various versions
  216. * of client hello, start by trying to unpickle the
  217. * most recent version, and then next most recent, until
  218. * one unpickles. Then run the handle code. We can also put
  219. * unpickling and handling code in here for SSL messages */
  220. pctRet = Pct1UnpackClientHello(
  221. pCommInput,
  222. &pPct1Hello);
  223. if(PCT_ERR_OK == pctRet)
  224. {
  225. for(i=0;i<(int)pPct1Hello->cbSessionID;i++)
  226. {
  227. fRealSessId |= pPct1Hello->SessionID[i];
  228. }
  229. if (((pContext->Flags & CONTEXT_FLAG_NOCACHE) == 0) &&
  230. (fRealSessId) &&
  231. (SPCacheRetrieveBySession(pContext,
  232. pPct1Hello->SessionID,
  233. pPct1Hello->cbSessionID,
  234. &pContext->RipeZombie)))
  235. {
  236. // We have a good zombie
  237. DebugLog((DEB_TRACE, "Accept client's reconnect request.\n"));
  238. pctRet = Pct1SrvRestart(pContext,
  239. pPct1Hello,
  240. pCommOutput);
  241. if(PCT_ERR_OK == pctRet)
  242. {
  243. pContext->State = SP_STATE_CONNECTED;
  244. pContext->DecryptHandler = Pct1DecryptHandler;
  245. pContext->Encrypt = Pct1EncryptMessage;
  246. pContext->Decrypt = Pct1DecryptMessage;
  247. pContext->GetHeaderSize = Pct1GetHeaderSize;
  248. }
  249. }
  250. else
  251. {
  252. // We're doing a full handshake, so allocate a cache entry.
  253. if(!SPCacheRetrieveNew(TRUE,
  254. pContext->pszTarget,
  255. &pContext->RipeZombie))
  256. {
  257. pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
  258. }
  259. else
  260. {
  261. pContext->RipeZombie->fProtocol = pContext->dwProtocol;
  262. pContext->RipeZombie->dwCF = pContext->dwRequestedCF;
  263. pContext->RipeZombie->pServerCred = pContext->pCredGroup;
  264. pctRet = Pct1SrvHandleClientHello(pContext,
  265. pCommInput,
  266. pPct1Hello,
  267. pCommOutput);
  268. if (PCT_ERR_OK == pctRet)
  269. {
  270. pContext->State = PCT1_STATE_SERVER_HELLO;
  271. }
  272. }
  273. }
  274. SPExternalFree(pPct1Hello);
  275. }
  276. else if(pctRet != PCT_INT_INCOMPLETE_MSG)
  277. {
  278. pctRet |= PCT_INT_DROP_CONNECTION;
  279. }
  280. if(SP_FATAL(pctRet)) {
  281. pContext->State = PCT1_STATE_ERROR;
  282. }
  283. break;
  284. }
  285. case (PCT1_MSG_CLIENT_MASTER_KEY << 16) | PCT1_STATE_SERVER_HELLO:
  286. pctRet = Pct1SrvHandleCMKey(pContext,
  287. pCommInput,
  288. pCommOutput);
  289. if(SP_FATAL(pctRet)) {
  290. pContext->State = PCT1_STATE_ERROR;
  291. } else {
  292. if(PCT_ERR_OK == pctRet) {
  293. pContext->State = SP_STATE_CONNECTED;
  294. pContext->DecryptHandler = Pct1DecryptHandler;
  295. pContext->Encrypt = Pct1EncryptMessage;
  296. pContext->Decrypt = Pct1DecryptMessage;
  297. pContext->GetHeaderSize = Pct1GetHeaderSize;
  298. }
  299. /* We received a non-fatal error, so the state doesn't
  300. * change, giving the app time to deal with this */
  301. }
  302. break;
  303. default:
  304. pContext->State = PCT1_STATE_ERROR;
  305. {
  306. pctRet = PCT_INT_ILLEGAL_MSG;
  307. if(((PUCHAR)pCommInput->pvBuffer)[2] == PCT1_MSG_ERROR)
  308. {
  309. /* we received an error message, process it */
  310. pctRet = Pct1HandleError(pContext,
  311. pCommInput,
  312. pCommOutput);
  313. } else {
  314. /* we received an unknown error, generate a
  315. * PCT_ERR_ILLEGAL_MESSAGE */
  316. pctRet = Pct1GenerateError(pContext,
  317. pCommOutput,
  318. PCT_ERR_ILLEGAL_MESSAGE,
  319. NULL);
  320. }
  321. }
  322. }
  323. }
  324. if(pctRet & PCT_INT_DROP_CONNECTION)
  325. {
  326. pContext->State &= ~SP_STATE_CONNECTED;
  327. }
  328. SP_RETURN(pctRet);
  329. }
  330. SP_STATUS
  331. Pct1SrvHandleUniHello(
  332. PSPContext pContext,
  333. PSPBuffer pCommInput,
  334. PSsl2_Client_Hello pHello,
  335. PSPBuffer pCommOutput)
  336. {
  337. SP_STATUS pctRet = PCT_ERR_ILLEGAL_MESSAGE;
  338. Pct1_Client_Hello ClientHello;
  339. DWORD iCipher;
  340. DWORD dwSpec;
  341. DWORD i;
  342. CipherSpec aCipherSpecs[PCT1_MAX_CIPH_SPECS];
  343. HashSpec aHashSpecs[PCT1_MAX_HASH_SPECS];
  344. CertSpec aCertSpecs[PCT1_MAX_CERT_SPECS];
  345. ExchSpec aExchSpecs[PCT1_MAX_EXCH_SPECS];
  346. SP_BEGIN("Pct1SrvHandlUniHello");
  347. if(NULL == pContext)
  348. {
  349. SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR));
  350. }
  351. ClientHello.pCipherSpecs =aCipherSpecs;
  352. ClientHello.pHashSpecs =aHashSpecs;
  353. ClientHello.pCertSpecs =aCertSpecs;
  354. ClientHello.pExchSpecs =aExchSpecs;
  355. ClientHello.cCipherSpecs =0;
  356. ClientHello.cHashSpecs =0;
  357. ClientHello.cCertSpecs =0;
  358. ClientHello.cExchSpecs =0;
  359. /* validate the buffer configuration */
  360. for (iCipher = 0;
  361. (iCipher < pHello->cCipherSpecs) && (iCipher < PCT1_MAX_CIPH_SPECS) ;
  362. iCipher++ )
  363. {
  364. dwSpec = pHello->CipherSpecs[iCipher] & 0xffff;
  365. switch(pHello->CipherSpecs[iCipher] >> 16)
  366. {
  367. case PCT_SSL_HASH_TYPE:
  368. ClientHello.pHashSpecs[ClientHello.cHashSpecs++] = dwSpec;
  369. break;
  370. case PCT_SSL_EXCH_TYPE:
  371. ClientHello.pExchSpecs[ClientHello.cExchSpecs++] = dwSpec;
  372. break;
  373. case PCT_SSL_CERT_TYPE:
  374. ClientHello.pCertSpecs[ClientHello.cCertSpecs++] = dwSpec;
  375. break;
  376. case PCT_SSL_CIPHER_TYPE_1ST_HALF:
  377. // Do we have enough room for a 2nd half.
  378. if(iCipher+1 >= pHello->cCipherSpecs)
  379. {
  380. break;
  381. }
  382. if((pHello->CipherSpecs[iCipher+1] >> 16) != PCT_SSL_CIPHER_TYPE_2ND_HALF)
  383. {
  384. break;
  385. }
  386. dwSpec = (pHello->CipherSpecs[iCipher+1] & 0xffff) |
  387. (dwSpec<< 16);
  388. ClientHello.pCipherSpecs[ClientHello.cCipherSpecs++] = dwSpec;
  389. break;
  390. }
  391. }
  392. // Restarts are not allowed with Uni Hello's, so we don't need
  393. // The session ID.
  394. ClientHello.cbSessionID = 0;
  395. /* Make the SSL2 challenge into a PCT1 challenge as per the
  396. * compatability doc. */
  397. CopyMemory( ClientHello.Challenge,
  398. pHello->Challenge,
  399. pHello->cbChallenge);
  400. for(i=0; i < pHello->cbChallenge; i++)
  401. {
  402. ClientHello.Challenge[i + pHello->cbChallenge] = ~ClientHello.Challenge[i];
  403. }
  404. ClientHello.cbChallenge = 2*pHello->cbChallenge;
  405. ClientHello.cbKeyArgSize = 0;
  406. pctRet = Pct1SrvHandleClientHello(pContext, pCommInput, &ClientHello, pCommOutput);
  407. SP_RETURN(pctRet);
  408. }
  409. /* Otherwise known as Handle Client Hello */
  410. SP_STATUS
  411. Pct1SrvHandleClientHello(
  412. PSPContext pContext,
  413. PSPBuffer pCommInput,
  414. PPct1_Client_Hello pHello,
  415. PSPBuffer pCommOutput
  416. )
  417. {
  418. SP_STATUS pctRet = PCT_ERR_ILLEGAL_MESSAGE;
  419. PSPCredentialGroup pCred;
  420. Pct1_Server_Hello Reply;
  421. DWORD i, j, k , fMismatch;
  422. BYTE MisData[PCT_NUM_MISMATCHES];
  423. SPBuffer ErrData;
  424. PSessCacheItem pZombie;
  425. BOOL fCert = FALSE;
  426. DWORD aCertSpecs[PCT1_MAX_CERT_SPECS];
  427. DWORD aSigSpecs[PCT1_MAX_SIG_SPECS];
  428. DWORD cCertSpecs;
  429. DWORD cSigSpecs;
  430. BOOL fAllocatedOutput = FALSE;
  431. CertTypeMap LocalCertEncodingPref[5] ;
  432. DWORD cLocalCertEncodingPref = 0;
  433. BOOL fFound;
  434. #if DBG
  435. DWORD di;
  436. #endif
  437. SP_BEGIN("Pct1SrvHandleClientHello");
  438. pCommOutput->cbData = 0;
  439. /* validate the buffer configuration */
  440. ErrData.cbData = 0;
  441. ErrData.pvBuffer = NULL;
  442. ErrData.cbBuffer = 0;
  443. pZombie = pContext->RipeZombie;
  444. pCred = pZombie->pServerCred;
  445. if (!pCred)
  446. {
  447. SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR));
  448. }
  449. do {
  450. #if DBG
  451. DebugLog((DEB_TRACE, "Client Hello at %x\n", pHello));
  452. DebugLog((DEB_TRACE, " CipherSpecs %d\n", pHello->cCipherSpecs));
  453. for (di = 0 ; di < pHello->cCipherSpecs ; di++ )
  454. {
  455. DebugLog((DEB_TRACE, " Cipher[%d] = %06x (%s)\n", di,
  456. pHello->pCipherSpecs[di],
  457. DbgGetNameOfCrypto(pHello->pCipherSpecs[di]) ));
  458. }
  459. #endif
  460. /* store the challenge in the auth block */
  461. CopyMemory( pContext->pChallenge,
  462. pHello->Challenge,
  463. pHello->cbChallenge );
  464. pContext->cbChallenge = pHello->cbChallenge;
  465. // The session id was computed when the cache entry
  466. // was created. We do need to fill in the length, though.
  467. pZombie->cbSessionID = PCT1_SESSION_ID_SIZE;
  468. /* Begin to build the server hello */
  469. FillMemory( &Reply, sizeof( Reply ), 0 );
  470. /* no matter what, we need to make a new connection id */
  471. GenerateRandomBits( Reply.ConnectionID,
  472. PCT1_SESSION_ID_SIZE );
  473. Reply.cbConnectionID = PCT1_SESSION_ID_SIZE;
  474. CopyMemory( pContext->pConnectionID,
  475. Reply.ConnectionID,
  476. PCT1_SESSION_ID_SIZE );
  477. pContext->cbConnectionID = PCT_SESSION_ID_SIZE;
  478. /* no restart case */
  479. /* fill in from properties here... */
  480. Reply.RestartOk = FALSE;
  481. Reply.ClientAuthReq = ((pContext->Flags & CONTEXT_FLAG_MUTUAL_AUTH) != 0);
  482. fMismatch = 0;
  483. pContext->pPendingCipherInfo = NULL;
  484. /* Build a list of cert specs */
  485. /* Hash order of preference:
  486. * Server Preference
  487. * Client Preference
  488. */
  489. for(i=0; i < cPct1CertEncodingPref; i++)
  490. {
  491. for(j=0; j< pHello->cCertSpecs; j++)
  492. {
  493. // Does the client want this cipher type
  494. if(aPct1CertEncodingPref[i].Spec == pHello->pCertSpecs[j])
  495. {
  496. LocalCertEncodingPref[cLocalCertEncodingPref].Spec = aPct1CertEncodingPref[i].Spec;
  497. LocalCertEncodingPref[cLocalCertEncodingPref++].dwCertEncodingType = aPct1CertEncodingPref[i].dwCertEncodingType;
  498. break;
  499. }
  500. }
  501. }
  502. /* Determine Key Exchange to use */
  503. /* Key Exchange order of preference:
  504. * Server Preference
  505. * Client Preference
  506. */
  507. // NOTE: Yes, the following line does do away with any error
  508. // information if we had a previous mismatch. However, the
  509. // setting of pctRet to mismatch in previous lines is for
  510. // logging purposes only. The actual error report occurs later.
  511. pctRet = PCT_ERR_OK;
  512. for(i=0; i < cPct1LocalExchKeyPref; i++)
  513. {
  514. // Do we enable this cipher
  515. if(NULL == KeyExchangeFromSpec(aPct1LocalExchKeyPref[i].Spec, SP_PROT_PCT1_SERVER))
  516. {
  517. continue;
  518. }
  519. for(j=0; j< pHello->cExchSpecs; j++)
  520. {
  521. // Does the client want this cipher type
  522. if(aPct1LocalExchKeyPref[i].Spec != pHello->pExchSpecs[j])
  523. {
  524. continue;
  525. }
  526. // See if we have a cert for this type of
  527. // key exchange.
  528. pctRet = SPPickServerCertificate(pContext,
  529. aPct1LocalExchKeyPref[i].Spec);
  530. if(pctRet != PCT_ERR_OK)
  531. {
  532. continue;
  533. }
  534. // Store the exch id in the cache.
  535. pZombie->SessExchSpec = aPct1LocalExchKeyPref[i].Spec;
  536. pContext->pKeyExchInfo = GetKeyExchangeInfo(pZombie->SessExchSpec);
  537. // load the exch info structure
  538. if(!IsExchAllowed(pContext,
  539. pContext->pKeyExchInfo,
  540. pZombie->fProtocol))
  541. {
  542. pContext->pKeyExchInfo = NULL;
  543. continue;
  544. }
  545. Reply.SrvExchSpec = aPct1LocalExchKeyPref[i].Spec;
  546. break;
  547. }
  548. if(pContext->pKeyExchInfo)
  549. {
  550. break;
  551. }
  552. }
  553. if(PCT_ERR_OK != pctRet)
  554. {
  555. fMismatch |= PCT_IMIS_CERT;
  556. }
  557. if (NULL == pContext->pKeyExchInfo)
  558. {
  559. pctRet = SP_LOG_RESULT(PCT_ERR_SPECS_MISMATCH);
  560. fMismatch |= PCT_IMIS_EXCH;
  561. }
  562. if (fMismatch)
  563. {
  564. pctRet = PCT_ERR_SPECS_MISMATCH;
  565. break;
  566. }
  567. /* Determine Cipher to use */
  568. /* Cipher order of preference:
  569. * Server Preference
  570. * Client Preference
  571. */
  572. fFound = FALSE;
  573. for(i=0; i < Pct1NumCipher; i++)
  574. {
  575. for(j=0; j< pHello->cCipherSpecs; j++)
  576. {
  577. // Does the client want this cipher type
  578. if(Pct1CipherRank[i].Spec == pHello->pCipherSpecs[j])
  579. {
  580. // Store this cipher identifier in the cache
  581. pZombie->aiCipher = Pct1CipherRank[i].aiCipher;
  582. pZombie->dwStrength = Pct1CipherRank[i].dwStrength;
  583. // Load the pending cipher structure.
  584. pContext->pPendingCipherInfo = GetCipherInfo(pZombie->aiCipher,
  585. pZombie->dwStrength);
  586. if(!IsCipherAllowed(pContext,
  587. pContext->pPendingCipherInfo,
  588. pZombie->fProtocol,
  589. pZombie->dwCF))
  590. {
  591. pContext->pPendingCipherInfo = NULL;
  592. continue;
  593. }
  594. // Is cipher supported by CSP?
  595. for(k = 0; k < pZombie->pActiveServerCred->cCapiAlgs; k++)
  596. {
  597. PROV_ENUMALGS_EX *pAlgInfo = &pZombie->pActiveServerCred->pCapiAlgs[k];
  598. if(pAlgInfo->aiAlgid != Pct1CipherRank[i].aiCipher)
  599. {
  600. continue;
  601. }
  602. if(Pct1CipherRank[i].dwStrength > pAlgInfo->dwMaxLen ||
  603. Pct1CipherRank[i].dwStrength < pAlgInfo->dwMinLen)
  604. {
  605. continue;
  606. }
  607. if(!(pAlgInfo->dwProtocols & CRYPT_FLAG_PCT1))
  608. {
  609. continue;
  610. }
  611. fFound = TRUE;
  612. break;
  613. }
  614. if(fFound)
  615. {
  616. break;
  617. }
  618. }
  619. }
  620. if(fFound)
  621. {
  622. break;
  623. }
  624. }
  625. if(fFound)
  626. {
  627. Reply.SrvCipherSpec = Pct1CipherRank[i].Spec;
  628. }
  629. else
  630. {
  631. pctRet = SP_LOG_RESULT(PCT_ERR_SPECS_MISMATCH);
  632. fMismatch |= PCT_IMIS_CIPHER;
  633. }
  634. /* Determine Hash to use */
  635. /* Hash order of preference:
  636. * Server Preference
  637. * Client Preference
  638. */
  639. for(i=0; i < Pct1NumHash; i++)
  640. {
  641. for(j=0; j< pHello->cHashSpecs; j++)
  642. {
  643. // Does the client want this cipher type
  644. if(Pct1HashRank[i].Spec == pHello->pHashSpecs[j])
  645. {
  646. // Store this hash id in the cache
  647. pZombie->aiHash = Pct1HashRank[i].aiHash;
  648. pContext->pPendingHashInfo = GetHashInfo(pZombie->aiHash);
  649. if(!IsHashAllowed(pContext,
  650. pContext->pPendingHashInfo,
  651. pZombie->fProtocol))
  652. {
  653. pContext->pPendingHashInfo = NULL;
  654. continue;
  655. }
  656. Reply.SrvHashSpec = Pct1HashRank[i].Spec;
  657. break;
  658. }
  659. }
  660. if(pContext->pPendingHashInfo)
  661. {
  662. break;
  663. }
  664. }
  665. if (pContext->pPendingHashInfo==NULL)
  666. {
  667. pctRet = SP_LOG_RESULT(PCT_ERR_SPECS_MISMATCH);
  668. fMismatch |= PCT_IMIS_HASH;
  669. }
  670. if (fMismatch)
  671. {
  672. LogCipherMismatchEvent();
  673. pctRet = PCT_ERR_SPECS_MISMATCH;
  674. break;
  675. }
  676. // Pick a certificate to use based on
  677. // the key exchange mechanism selected.
  678. for(i=0; i < cLocalCertEncodingPref; i++)
  679. {
  680. if(LocalCertEncodingPref[i].dwCertEncodingType == pZombie->pActiveServerCred->pCert->dwCertEncodingType)
  681. {
  682. Reply.SrvCertSpec = LocalCertEncodingPref[i].Spec;
  683. break;
  684. }
  685. }
  686. if(Reply.SrvCertSpec == PCT1_CERT_X509_CHAIN)
  687. {
  688. pContext->fCertChainsAllowed = TRUE;
  689. }
  690. Reply.pCertificate = NULL;
  691. Reply.CertificateLen = 0;
  692. // NOTE: SPSerializeCertificate will allocate memory
  693. // for the certificate, which we save in pZombie->pbServerCertificate.
  694. // This must be freed when the zombie dies (can the undead die?)
  695. pctRet = SPSerializeCertificate(SP_PROT_PCT1,
  696. pContext->fCertChainsAllowed,
  697. &pZombie->pbServerCertificate,
  698. &pZombie->cbServerCertificate,
  699. pZombie->pActiveServerCred->pCert,
  700. CERT_CHAIN_CACHE_ONLY_URL_RETRIEVAL);
  701. if(pctRet == PCT_ERR_OK)
  702. {
  703. Reply.pCertificate = pZombie->pbServerCertificate;
  704. Reply.CertificateLen = pZombie->cbServerCertificate;
  705. }
  706. else
  707. {
  708. break;
  709. }
  710. pctRet = ContextInitCiphers(pContext, TRUE, TRUE);
  711. if(PCT_ERR_OK != pctRet)
  712. {
  713. break;
  714. }
  715. /* sig and cert specs are pre-zeroed when Reply is initialized */
  716. if(Reply.ClientAuthReq)
  717. {
  718. PCertSysInfo pCertInfo;
  719. PSigInfo pSigInfo;
  720. cCertSpecs=0;
  721. cSigSpecs = 0;
  722. for(i=0; i < cPct1LocalSigKeyPref; i++)
  723. {
  724. pSigInfo = GetSigInfo(aPct1LocalSigKeyPref[i].Spec);
  725. if(pSigInfo != NULL)
  726. {
  727. if(pSigInfo->fProtocol & SP_PROT_PCT1_SERVER)
  728. {
  729. aSigSpecs[cSigSpecs++] = aPct1LocalSigKeyPref[i].Spec;
  730. }
  731. }
  732. }
  733. Reply.pClientSigSpecs = aSigSpecs;
  734. Reply.cSigSpecs = cSigSpecs;
  735. for(i=0; i < cPct1CertEncodingPref; i++)
  736. {
  737. pCertInfo = GetCertSysInfo(aPct1CertEncodingPref[i].dwCertEncodingType);
  738. if(pCertInfo == NULL)
  739. {
  740. continue;
  741. }
  742. if(0 == (pCertInfo->fProtocol & SP_PROT_PCT1_SERVER))
  743. {
  744. continue;
  745. }
  746. aCertSpecs[cCertSpecs++] = aPct1CertEncodingPref[i].Spec;
  747. }
  748. Reply.pClientCertSpecs = aCertSpecs;
  749. Reply.cCertSpecs = cCertSpecs;
  750. }
  751. #if DBG
  752. DebugLog((DEB_TRACE, "Server picks cipher %06x (%s)\n",
  753. Reply.SrvCipherSpec,
  754. DbgGetNameOfCrypto(Reply.SrvCipherSpec) ));
  755. #endif
  756. Reply.ResponseLen = 0;
  757. if(pCommOutput->pvBuffer == NULL)
  758. {
  759. fAllocatedOutput=TRUE;
  760. }
  761. pctRet = Pct1PackServerHello(&Reply, pCommOutput);
  762. if(PCT_ERR_OK != pctRet)
  763. {
  764. break;
  765. }
  766. /* Regenerate the internal pVerifyPrelude, so we */
  767. /* can match it against the client when we get the */
  768. /* client master key */
  769. pctRet = Pct1BeginVerifyPrelude(pContext,
  770. pCommInput->pvBuffer,
  771. pCommInput->cbData,
  772. pCommOutput->pvBuffer,
  773. pCommOutput->cbData);
  774. if(PCT_ERR_OK != pctRet)
  775. {
  776. if(fAllocatedOutput)
  777. {
  778. SPExternalFree(pCommOutput->pvBuffer);
  779. }
  780. break;
  781. }
  782. SP_RETURN(PCT_ERR_OK);
  783. } while (TRUE); /* end Polish Loop */
  784. if(pctRet == PCT_ERR_SPECS_MISMATCH) {
  785. for(i=0;i<PCT_NUM_MISMATCHES;i++)
  786. {
  787. MisData[i] = (BYTE)(fMismatch & 1);
  788. fMismatch = fMismatch >> 1;
  789. }
  790. ErrData.cbData = ErrData.cbBuffer = PCT_NUM_MISMATCHES;
  791. ErrData.pvBuffer = MisData;
  792. }
  793. pctRet = Pct1GenerateError(pContext,
  794. pCommOutput,
  795. pctRet,
  796. &ErrData);
  797. SP_RETURN(pctRet | PCT_INT_DROP_CONNECTION);
  798. }
  799. //+---------------------------------------------------------------------------
  800. //
  801. // Function: Pct1SrvHandleCMKey
  802. //
  803. // Synopsis: Process the ClientKeyExchange message group.
  804. //
  805. // Arguments: [pContext] -- Schannel context.
  806. // [pCommInput] --
  807. // [pCommOutput] --
  808. //
  809. // History: 10-10-97 jbanes Added CAPI integration.
  810. //
  811. // Notes: This routine is called by the server-side only.
  812. //
  813. //----------------------------------------------------------------------------
  814. SP_STATUS
  815. Pct1SrvHandleCMKey(
  816. PSPContext pContext,
  817. PSPBuffer pCommInput,
  818. PSPBuffer pCommOutput)
  819. {
  820. SP_STATUS pctRet = PCT_ERR_ILLEGAL_MESSAGE;
  821. PPct1_Client_Master_Key pMasterKey = NULL;
  822. DWORD dwKeyLen;
  823. DWORD EncryptedLen;
  824. Pct1_Server_Verify Verify;
  825. UCHAR VerifyPrelude[RESPONSE_SIZE];
  826. DWORD cbVerifyPrelude;
  827. SPBuffer ErrData;
  828. DWORD k;
  829. PSessCacheItem pZombie;
  830. PSigInfo pSigInfo;
  831. SP_BEGIN("Pct1SrvHandleCMKey");
  832. pCommOutput->cbData = 0;
  833. ErrData.cbData = 0;
  834. ErrData.pvBuffer = NULL;
  835. ErrData.cbBuffer = 0;
  836. pZombie = pContext->RipeZombie;
  837. do {
  838. pctRet = Pct1UnpackClientMasterKey(pCommInput, &pMasterKey);
  839. if (PCT_ERR_OK != pctRet)
  840. {
  841. // If it's an incomplete message or something, just return;
  842. if(pctRet == PCT_INT_INCOMPLETE_MSG)
  843. {
  844. SP_RETURN(pctRet);
  845. }
  846. break;
  847. }
  848. /* Validate that the client properly authed */
  849. /* The server requested client auth */
  850. /* NOTE: this deviates from the first pct 1.0 spec,
  851. * Now, we continue with the protocol if client
  852. * auth fails. By the first spec, we should
  853. * drop the connection */
  854. if (pContext->Flags & CONTEXT_FLAG_MUTUAL_AUTH)
  855. {
  856. /* Client auth polish loop */
  857. pctRet = PCT_ERR_OK;
  858. do
  859. {
  860. /* check to see if the client sent no cert */
  861. if(pMasterKey->ClientCertLen == 0)
  862. {
  863. /* No client auth */
  864. break;
  865. }
  866. pctRet = SPLoadCertificate(SP_PROT_PCT1_SERVER,
  867. X509_ASN_ENCODING,
  868. pMasterKey->pClientCert,
  869. pMasterKey->ClientCertLen,
  870. &pZombie->pRemoteCert);
  871. if(PCT_ERR_OK != pctRet)
  872. {
  873. break;
  874. }
  875. if(pContext->RipeZombie->pRemotePublic != NULL)
  876. {
  877. SPExternalFree(pContext->RipeZombie->pRemotePublic);
  878. pContext->RipeZombie->pRemotePublic = NULL;
  879. }
  880. pctRet = SPPublicKeyFromCert(pZombie->pRemoteCert,
  881. &pZombie->pRemotePublic,
  882. NULL);
  883. if(PCT_ERR_OK != pctRet)
  884. {
  885. break;
  886. }
  887. if(pZombie->pRemoteCert == NULL)
  888. {
  889. break;
  890. }
  891. /* verify that we got a sig type that meets PCT spec */
  892. for(k=0; k < cPct1LocalSigKeyPref; k++)
  893. {
  894. if(aPct1LocalSigKeyPref[k].Spec == pMasterKey->ClientSigSpec)
  895. {
  896. break;
  897. }
  898. }
  899. if(k == cPct1LocalSigKeyPref)
  900. {
  901. break;
  902. }
  903. // Get pointer to signature algorithm info and make sure
  904. // we support it.
  905. pSigInfo = GetSigInfo(pMasterKey->ClientSigSpec);
  906. if(pSigInfo == NULL)
  907. {
  908. pctRet = SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE);
  909. break;
  910. }
  911. if(!(pSigInfo->fProtocol & SP_PROT_PCT1_SERVER))
  912. {
  913. pctRet = SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE);
  914. break;
  915. }
  916. // Verify client authentication signature.
  917. DebugLog((DEB_TRACE, "Verify client response signature.\n"));
  918. pctRet = SPVerifySignature(pZombie->hMasterProv,
  919. pZombie->dwCapiFlags,
  920. pZombie->pRemotePublic,
  921. pSigInfo->aiHash,
  922. pMasterKey->VerifyPrelude,
  923. pMasterKey->VerifyPreludeLen,
  924. pMasterKey->pbResponse,
  925. pMasterKey->ResponseLen,
  926. TRUE);
  927. if(pctRet != PCT_ERR_OK)
  928. {
  929. // client auth signature failed to verify, so client auth
  930. // does not happen.
  931. SP_LOG_RESULT(pctRet);
  932. break;
  933. }
  934. DebugLog((DEB_TRACE, "Client response verified successfully.\n"));
  935. pctRet = SPContextDoMapping(pContext);
  936. } while(FALSE); /* end polish loop */
  937. if(PCT_ERR_OK != pctRet)
  938. {
  939. break;
  940. }
  941. }
  942. /* Client auth was successful */
  943. pctRet = PCT_ERR_ILLEGAL_MESSAGE;
  944. /* Copy over the key args */
  945. CopyMemory( pZombie->pKeyArgs,
  946. pMasterKey->KeyArg,
  947. pMasterKey->KeyArgLen );
  948. pZombie->cbKeyArgs = pMasterKey->KeyArgLen;
  949. // Decrypt the encrypted portion of the master key. Because
  950. // we're CAPI integrated, the keys get derived as well.
  951. pctRet = pContext->pKeyExchInfo->System->GenerateServerMasterKey(
  952. pContext,
  953. pMasterKey->ClearKey,
  954. pMasterKey->ClearKeyLen,
  955. pMasterKey->pbEncryptedKey,
  956. pMasterKey->EncryptedKeyLen);
  957. if(PCT_ERR_OK != pctRet)
  958. {
  959. break;
  960. }
  961. // Activate session keys.
  962. Pct1ActivateSessionKeys(pContext);
  963. if (pMasterKey->VerifyPreludeLen != pContext->pHashInfo->cbCheckSum)
  964. {
  965. pctRet = SP_LOG_RESULT(PCT_ERR_INTEGRITY_CHECK_FAILED);
  966. break;
  967. }
  968. /* Check the verify prelude hashes */
  969. /* Hash(CLIENT_MAC_KEY, Hash( "cvp", CLIENT_HELLO, SERVER_HELLO)) */
  970. /* The internal hash should already be in the verify prelude buffer */
  971. /* from the handle client master key. */
  972. cbVerifyPrelude = sizeof(VerifyPrelude);
  973. pctRet = Pct1EndVerifyPrelude(pContext, VerifyPrelude, &cbVerifyPrelude);
  974. if(PCT_ERR_OK != pctRet)
  975. {
  976. break;
  977. }
  978. /* Did the verify prelude hash successfully? */
  979. if(memcmp(VerifyPrelude, pMasterKey->VerifyPrelude, pContext->pHashInfo->cbCheckSum))
  980. {
  981. pctRet = SP_LOG_RESULT(PCT_ERR_INTEGRITY_CHECK_FAILED);
  982. break;
  983. }
  984. /* don't need master key info anymore */
  985. SPExternalFree(pMasterKey);
  986. pMasterKey = NULL;
  987. pContext->WriteCounter = 2;
  988. pContext->ReadCounter = 2;
  989. pZombie->cbSessionID = PCT1_SESSION_ID_SIZE;
  990. CopyMemory( Verify.SessionIdData,
  991. pZombie->SessionID,
  992. pZombie->cbSessionID);
  993. /* compute the response */
  994. Verify.ResponseLen = sizeof(Verify.Response);
  995. pctRet = Pct1ComputeResponse(pContext,
  996. pContext->pChallenge,
  997. pContext->cbChallenge,
  998. pContext->pConnectionID,
  999. pContext->cbConnectionID,
  1000. pZombie->SessionID,
  1001. pZombie->cbSessionID,
  1002. Verify.Response,
  1003. &Verify.ResponseLen);
  1004. if(pctRet != PCT_ERR_OK)
  1005. {
  1006. SP_RETURN(SP_LOG_RESULT(pctRet));
  1007. }
  1008. pctRet = Pct1PackServerVerify(&Verify, pCommOutput);
  1009. if(PCT_ERR_OK != pctRet)
  1010. {
  1011. break;
  1012. }
  1013. /* set up the session in cache */
  1014. SPCacheAdd(pContext);
  1015. SP_RETURN(PCT_ERR_OK);
  1016. } while(TRUE); /* End of polish loop */
  1017. if(pMasterKey) SPExternalFree(pMasterKey);
  1018. pctRet = Pct1GenerateError(pContext,
  1019. pCommOutput,
  1020. pctRet,
  1021. NULL);
  1022. SP_RETURN(pctRet | PCT_INT_DROP_CONNECTION);
  1023. }
  1024. SP_STATUS
  1025. Pct1SrvRestart(
  1026. PSPContext pContext,
  1027. PPct1_Client_Hello pHello,
  1028. PSPBuffer pCommOutput)
  1029. {
  1030. Pct1_Server_Hello Reply;
  1031. SPBuffer ErrData;
  1032. SP_STATUS pctRet = PCT_INT_ILLEGAL_MSG;
  1033. PSessCacheItem pZombie;
  1034. DWORD i;
  1035. SP_BEGIN("Pct1SrvRestart");
  1036. pCommOutput->cbData = 0;
  1037. /* validate the buffer configuration */
  1038. ErrData.cbData = 0;
  1039. ErrData.pvBuffer = NULL;
  1040. ErrData.cbBuffer = 0;
  1041. pZombie = pContext->RipeZombie;
  1042. do {
  1043. /* store the challenge in the auth block */
  1044. CopyMemory( pContext->pChallenge,
  1045. pHello->Challenge,
  1046. pHello->cbChallenge );
  1047. pContext->cbChallenge = pHello->cbChallenge;
  1048. /* Begin to build the server hello */
  1049. FillMemory( &Reply, sizeof( Reply ), 0 );
  1050. /* Generate new connection id */
  1051. GenerateRandomBits( Reply.ConnectionID,
  1052. PCT1_SESSION_ID_SIZE );
  1053. Reply.cbConnectionID = PCT1_SESSION_ID_SIZE;
  1054. CopyMemory( pContext->pConnectionID,
  1055. Reply.ConnectionID,
  1056. Reply.cbConnectionID );
  1057. pContext->cbConnectionID = Reply.cbConnectionID;
  1058. Reply.RestartOk = TRUE;
  1059. /* We don't pass a server cert back during a restart */
  1060. Reply.pCertificate = NULL;
  1061. Reply.CertificateLen = 0;
  1062. /* setup the context */
  1063. for(i=0; i < Pct1NumCipher; i++)
  1064. {
  1065. if((Pct1CipherRank[i].aiCipher == pZombie->aiCipher) &&
  1066. (Pct1CipherRank[i].dwStrength == pZombie->dwStrength))
  1067. {
  1068. Reply.SrvCipherSpec = Pct1CipherRank[i].Spec;
  1069. }
  1070. }
  1071. for(i=0; i < Pct1NumHash; i++)
  1072. {
  1073. if(Pct1HashRank[i].aiHash == pZombie->aiHash)
  1074. {
  1075. Reply.SrvHashSpec = Pct1HashRank[i].Spec;
  1076. }
  1077. }
  1078. Reply.SrvCertSpec = pZombie->pActiveServerCred->pCert->dwCertEncodingType;
  1079. Reply.SrvExchSpec = pZombie->SessExchSpec;
  1080. // We know what our ciphers are, so init the cipher system
  1081. pctRet = ContextInitCiphersFromCache(pContext);
  1082. if(PCT_ERR_OK != pctRet)
  1083. {
  1084. break;
  1085. }
  1086. // We know what our ciphers are, so init the cipher system
  1087. pctRet = ContextInitCiphers(pContext, TRUE, TRUE);
  1088. if(PCT_ERR_OK != pctRet)
  1089. {
  1090. break;
  1091. }
  1092. // Make a new set of session keys.
  1093. pctRet = MakeSessionKeys(pContext,
  1094. pContext->RipeZombie->hMasterProv,
  1095. pContext->RipeZombie->hMasterKey);
  1096. if(PCT_ERR_OK != pctRet)
  1097. {
  1098. break;
  1099. }
  1100. // Activate session keys.
  1101. Pct1ActivateSessionKeys(pContext);
  1102. /* compute the response */
  1103. Reply.ResponseLen = sizeof(Reply.Response);
  1104. pctRet = Pct1ComputeResponse(pContext,
  1105. pContext->pChallenge,
  1106. pContext->cbChallenge,
  1107. pContext->pConnectionID,
  1108. pContext->cbConnectionID,
  1109. pZombie->SessionID,
  1110. pZombie->cbSessionID,
  1111. Reply.Response,
  1112. &Reply.ResponseLen);
  1113. if(pctRet != PCT_ERR_OK)
  1114. {
  1115. break;
  1116. }
  1117. pctRet = Pct1PackServerHello(&Reply, pCommOutput);
  1118. if(PCT_ERR_OK != pctRet)
  1119. {
  1120. break;
  1121. }
  1122. pContext->ReadCounter = 1;
  1123. pContext->WriteCounter = 1;
  1124. SP_RETURN(PCT_ERR_OK);
  1125. } while (TRUE);
  1126. pctRet = Pct1GenerateError(pContext,
  1127. pCommOutput,
  1128. pctRet,
  1129. &ErrData);
  1130. SP_RETURN(pctRet);
  1131. }