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.

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