//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1995. // // File: pct1cli.c // // Contents: // // Classes: // // Functions: // // History: 09-23-97 jbanes LSA integration stuff. // //---------------------------------------------------------------------------- #include #include #include VOID Pct1ActivateSessionKeys(PSPContext pContext) { if(pContext->hReadKey) { if(!CryptDestroyKey(pContext->hReadKey)) { SP_LOG_RESULT(GetLastError()); } } pContext->hReadKey = pContext->hPendingReadKey; if(pContext->hReadMAC) { if(!CryptDestroyKey(pContext->hReadMAC)) { SP_LOG_RESULT(GetLastError()); } } pContext->hReadMAC = pContext->hPendingReadMAC; if(pContext->hWriteKey) { if(!CryptDestroyKey(pContext->hWriteKey)) { SP_LOG_RESULT(GetLastError()); } } pContext->hWriteKey = pContext->hPendingWriteKey; if(pContext->hWriteMAC) { if(!CryptDestroyKey(pContext->hWriteMAC)) { SP_LOG_RESULT(GetLastError()); } } pContext->hWriteMAC = pContext->hPendingWriteMAC; pContext->hPendingReadKey = 0; pContext->hPendingReadMAC = 0; pContext->hPendingWriteKey = 0; pContext->hPendingWriteMAC = 0; } SP_STATUS WINAPI Pct1ClientProtocolHandler(PSPContext pContext, PSPBuffer pCommInput, PSPBuffer pCommOutput) { SP_STATUS pctRet= PCT_ERR_OK; DWORD dwStateTransition; SP_BEGIN("Pct1ClientProtocolHandler"); if(pCommOutput) pCommOutput->cbData = 0; /* Protocol handling steps should be listed in most common * to least common in order to improve performance */ /* We are not connected, so we're doing * protocol negotiation of some sort. All protocol * negotiation messages are sent in the clear */ /* There are no branches in the connecting protocol * state transition diagram, besides connection and error, * which means that a simple case statement will do */ /* Do we have enough data to determine what kind of message we have */ /* Do we have enough data to determine what kind of message we have, or how much data we need*/ dwStateTransition = (pContext->State & 0xffff); if(pCommInput->cbData < 3) { if(!(dwStateTransition == PCT1_STATE_RENEGOTIATE || dwStateTransition == SP_STATE_SHUTDOWN || dwStateTransition == SP_STATE_SHUTDOWN_PENDING)) { pctRet = PCT_INT_INCOMPLETE_MSG; } } else { dwStateTransition = (((PUCHAR)pCommInput->pvBuffer)[2]<<16) | (pContext->State & 0xffff); } if(pctRet == PCT_ERR_OK) { switch(dwStateTransition) { case SP_STATE_SHUTDOWN_PENDING: // There's no CloseNotify in PCT, so just transition to // the shutdown state and leave the output buffer empty. pContext->State = SP_STATE_SHUTDOWN; break; case SP_STATE_SHUTDOWN: return PCT_INT_EXPIRED; case PCT1_STATE_RENEGOTIATE: { SPBuffer In; SPBuffer Out; DWORD cbMessage; BOOL fAllocated = FALSE; cbMessage = pContext->pHashInfo->cbCheckSum + pContext->pCipherInfo->dwBlockSize + sizeof(PCT1_MESSAGE_HEADER_EX) + PCT1_MAX_CLIENT_HELLO; /* are we allocating our own memory? */ if(pCommOutput->pvBuffer == NULL) { pCommOutput->pvBuffer = SPExternalAlloc(cbMessage); if (NULL == pCommOutput->pvBuffer) { SP_RETURN(SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY)); } fAllocated = TRUE; pCommOutput->cbBuffer = cbMessage; } if(cbMessage > pCommOutput->cbBuffer) { if(fAllocated) { SPExternalFree(pCommOutput->pvBuffer); pCommOutput->pvBuffer = NULL; SP_RETURN(PCT_INT_INTERNAL_ERROR); } pCommOutput->cbData = cbMessage; SP_RETURN(PCT_INT_BUFF_TOO_SMALL); } In.pvBuffer = ((char *)pCommOutput->pvBuffer)+3; In.cbBuffer = pCommOutput->cbBuffer-3; In.cbData = 1; ((char *)In.pvBuffer)[0] = PCT1_ET_REDO_CONN; // Build a Redo Request pctRet = Pct1EncryptRaw(pContext, &In, pCommOutput, PCT1_ENCRYPT_ESCAPE); if(pctRet != PCT_ERR_OK) { if(fAllocated) { SPExternalFree(pCommOutput->pvBuffer); pCommOutput->pvBuffer = NULL; } break; } Out.pvBuffer = (char *)pCommOutput->pvBuffer + pCommOutput->cbData; Out.cbBuffer = pCommOutput->cbBuffer - pCommOutput->cbData; // Mark context as "unmapped" so that the new keys will get // passed to the application process once the handshake is // completed. pContext->Flags &= ~CONTEXT_FLAG_MAPPED; if(!SPCacheClone(&pContext->RipeZombie)) { if(fAllocated) { SPExternalFree(pCommOutput->pvBuffer); pCommOutput->pvBuffer = NULL; } SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR)); } pctRet = GeneratePct1StyleHello(pContext, &Out); pCommOutput->cbData += Out.cbData; break; } /* Client receives Server hello */ case (PCT1_MSG_SERVER_HELLO << 16) | UNI_STATE_CLIENT_HELLO: case (PCT1_MSG_SERVER_HELLO << 16) | PCT1_STATE_CLIENT_HELLO: { PPct1_Server_Hello pHello; /* Attempt to recognize and handle various versions * of Server hello, start by trying to unpickle the * oldest, and the next version, until * one unpickles. Then run the handle code. We can also put * unpickling and handling code in here for SSL messages */ if(PCT_ERR_OK == (pctRet = Pct1UnpackServerHello( pCommInput, &pHello))) { /* let's resurrect the zombie session */ if (pHello->RestartOk) { pctRet = Pct1CliRestart(pContext, pHello, pCommOutput); if(PCT_ERR_OK == pctRet) { pContext->State = SP_STATE_CONNECTED; pContext->DecryptHandler = Pct1DecryptHandler; pContext->Encrypt = Pct1EncryptMessage; pContext->Decrypt = Pct1DecryptMessage; pContext->GetHeaderSize = Pct1GetHeaderSize; } } else { pContext->RipeZombie->fProtocol = SP_PROT_PCT1_CLIENT; if(pContext->RipeZombie->hMasterKey != 0) { // We've attempted to do a reconnect and the server has // blown us off. In this case we must use a new and different // cache entry. pContext->RipeZombie->ZombieJuju = FALSE; if(!SPCacheClone(&pContext->RipeZombie)) { pctRet = SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); } } if(pctRet == PCT_ERR_OK) { pctRet = Pct1CliHandleServerHello(pContext, pCommInput, pHello, pCommOutput); } if(PCT_ERR_OK == pctRet) { pContext->State = PCT1_STATE_CLIENT_MASTER_KEY; pContext->DecryptHandler = Pct1DecryptHandler; pContext->Encrypt = Pct1EncryptMessage; /* ?DCB? */ pContext->Decrypt = Pct1DecryptMessage; /* ?DCB? */ pContext->GetHeaderSize = Pct1GetHeaderSize; } } SPExternalFree(pHello); } else if(pctRet != PCT_INT_INCOMPLETE_MSG) { pctRet |= PCT_INT_DROP_CONNECTION; } if(SP_FATAL(pctRet)) { pContext->State = PCT1_STATE_ERROR; } break; } case (PCT1_MSG_SERVER_VERIFY << 16) | PCT1_STATE_CLIENT_MASTER_KEY: pctRet = Pct1CliHandleServerVerify(pContext, pCommInput, pCommOutput); if(SP_FATAL(pctRet)) { pContext->State = PCT1_STATE_ERROR; } else { if(PCT_ERR_OK == pctRet) { pContext->State = SP_STATE_CONNECTED; pContext->DecryptHandler = Pct1DecryptHandler; pContext->Encrypt = Pct1EncryptMessage; pContext->Decrypt = Pct1DecryptMessage; pContext->GetHeaderSize = Pct1GetHeaderSize; } /* We received a non-fatal error, so the state doesn't * change, giving the app time to deal with this */ } break; default: pContext->State = PCT1_STATE_ERROR; { pctRet = PCT_INT_ILLEGAL_MSG; if(((PUCHAR)pCommInput->pvBuffer)[2] == PCT1_MSG_ERROR) { /* we received an error message, process it */ pctRet = Pct1HandleError(pContext, pCommInput, pCommOutput); } else { /* we received an unknown error, generate a * PCT_ERR_ILLEGAL_MESSAGE */ pctRet = Pct1GenerateError(pContext, pCommOutput, PCT_ERR_ILLEGAL_MESSAGE, NULL); } } } } if(pctRet & PCT_INT_DROP_CONNECTION) { pContext->State &= ~SP_STATE_CONNECTED; } SP_RETURN(pctRet); } //+--------------------------------------------------------------------------- // // Function: Pct1CheckForExistingCred // // Synopsis: Choose client certificate. Use one of the certificates // attached to the credential handle if possible. If the // credential handle is anonymous, then attempt to create // a default credential. // // Notes: This routine is called by the client-side only. // // Returns: PCT_ERR_OK // The function completed successfully. The // pContext->pActiveClientCred field has been updated to // point at a suitable client credential. // // SEC_E_INCOMPLETE_CREDENTIALS // No suitable certificate has been found. Notify the // application. // // SEC_I_INCOMPLETE_CREDENTIALS // No suitable certificate has been found. Attempt an // anonymous connection. // // // Fatal error. // //---------------------------------------------------------------------------- SP_STATUS Pct1CheckForExistingCred( PSPContext pContext) { SP_STATUS pctRet; // // Examine the certificates attached to the credential group and see // if any of them are suitable. // if(pContext->pCredGroup->CredCount != 0) { pctRet = SPPickClientCertificate(pContext, SP_EXCH_RSA_PKCS1); if(pctRet == PCT_ERR_OK) { // We found one. DebugLog((DEB_TRACE, "Application provided suitable client certificate.\n")); return PCT_ERR_OK; } // The credential group contained one or more certificates, // but none were suitable. Don't even try to find a default // certificate in this situation. goto error; } // // Attempt to acquire a default credential. // if(pContext->pCredGroup->dwFlags & CRED_FLAG_NO_DEFAULT_CREDS) { // Look in credential manager only. pctRet = AcquireDefaultClientCredential(pContext, TRUE); } else { // Look in both credential manager and MY certificate store. pctRet = AcquireDefaultClientCredential(pContext, FALSE); } if(pctRet == PCT_ERR_OK) { DebugLog((DEB_TRACE, "Default client certificate acquired.\n")); return PCT_ERR_OK; } error: if(pContext->Flags & CONTEXT_FLAG_NO_INCOMPLETE_CRED_MSG) { return SP_LOG_RESULT(SEC_I_INCOMPLETE_CREDENTIALS); } else { return SP_LOG_RESULT(SEC_E_INCOMPLETE_CREDENTIALS); } } SP_STATUS Pct1CliHandleServerHello(PSPContext pContext, PSPBuffer pCommInput, PPct1_Server_Hello pHello, PSPBuffer pCommOutput) { /* error to return to peer */ SP_STATUS pctRet=PCT_ERR_ILLEGAL_MESSAGE; PSessCacheItem pZombie; PPct1_Client_Master_Key pCMKey = NULL; SPBuffer ErrData; DWORD i, j; DWORD fMismatch = 0; DWORD cbClientCert = 0; PBYTE pbClientCert = NULL; BYTE MisData[PCT_NUM_MISMATCHES]; CertTypeMap LocalCertEncodingPref[5] ; DWORD cLocalCertEncodingPref = 0; BOOL fClientAuth; PSigInfo pSigInfo = NULL; DWORD ClientCertSpec = 0; SP_BEGIN("Pct1CliHandleServerHello"); pCommOutput->cbData = 0; /* validate the buffer configuration */ ErrData.cbData = 0; ErrData.pvBuffer = NULL; ErrData.cbBuffer = 0; pZombie = pContext->RipeZombie; #if DBG DebugLog((DEB_TRACE, "Hello = %x\n", pHello)); DebugLog((DEB_TRACE, " Restart\t%s\n", pHello->RestartOk ? "Yes":"No")); DebugLog((DEB_TRACE, " ClientAuth\t%s\n", pHello->ClientAuthReq ? "Yes":"No")); DebugLog((DEB_TRACE, " Certificate Type\t%x\n", pHello->SrvCertSpec)); DebugLog((DEB_TRACE, " Hash Type\t%x\n", pHello->SrvHashSpec)); DebugLog((DEB_TRACE, " Cipher Type\t%x (%s)\n", pHello->SrvCipherSpec, DbgGetNameOfCrypto(pHello->SrvCipherSpec))); DebugLog((DEB_TRACE, " Certificate Len\t%ld\n", pHello->CertificateLen)); #endif CopyMemory(pContext->pConnectionID, pHello->ConnectionID, pHello->cbConnectionID); pContext->cbConnectionID = pHello->cbConnectionID; fClientAuth = pHello->ClientAuthReq; if(fClientAuth) { // If we're doing client auth, check to see if we have // proper credentials. /* Build a list of cert specs */ for(i=0; i < cPct1CertEncodingPref; i++) { for(j=0; j< pHello->cCertSpecs; j++) { // Does the client want this cipher type if(aPct1CertEncodingPref[i].Spec == pHello->pClientCertSpecs[j]) { LocalCertEncodingPref[cLocalCertEncodingPref].Spec = aPct1CertEncodingPref[i].Spec; LocalCertEncodingPref[cLocalCertEncodingPref++].dwCertEncodingType = aPct1CertEncodingPref[i].dwCertEncodingType; break; } } } // Decide on a signature algorithm. for(i = 0; i < cPct1LocalSigKeyPref; i++) { for(j = 0; j < pHello->cSigSpecs; j++) { if(pHello->pClientSigSpecs[j] != aPct1LocalSigKeyPref[i].Spec) { continue; } pSigInfo = GetSigInfo(pHello->pClientSigSpecs[j]); if(pSigInfo == NULL) continue; if((pSigInfo->fProtocol & SP_PROT_PCT1_CLIENT) == 0) { continue; } break; } if(pSigInfo) { break; } } // Our PCT implementation only supports RSA client authentication. pContext->Ssl3ClientCertTypes[0] = SSL3_CERTTYPE_RSA_SIGN; pContext->cSsl3ClientCertTypes = 1; pctRet = Pct1CheckForExistingCred(pContext); if(pctRet == SEC_E_INCOMPLETE_CREDENTIALS) { // It's okay to return here as we haven't done anything // yet. We just need to return this error as a warning. SP_RETURN(SEC_I_INCOMPLETE_CREDENTIALS); } else if(pctRet != PCT_ERR_OK) { // Attempt to carry on without a certificate, and hope // the server doesn't shut us down. fClientAuth = FALSE; pSigInfo = NULL; LogNoClientCertFoundEvent(); } else { // We are doing client auth with a certificate. // Check to see if we're doing CHAIN based certificates // by finding the first shared encoding type that matches // our certificate type. for(i=0; i < cLocalCertEncodingPref; i++) { if(LocalCertEncodingPref[i].dwCertEncodingType == pContext->pActiveClientCred->pCert->dwCertEncodingType) { ClientCertSpec = LocalCertEncodingPref[i].Spec; if(LocalCertEncodingPref[i].Spec == PCT1_CERT_X509_CHAIN) { pContext->fCertChainsAllowed = TRUE; } break; } } // Get the client certificate chain. pctRet = SPSerializeCertificate(SP_PROT_PCT1, pContext->fCertChainsAllowed, &pbClientCert, &cbClientCert, pContext->pActiveClientCred->pCert, CERT_CHAIN_CACHE_ONLY_URL_RETRIEVAL); if(pctRet != PCT_ERR_OK) { SP_RETURN(pctRet); } } } for(i=0; i < Pct1NumCipher; i++) { if(Pct1CipherRank[i].Spec == pHello->SrvCipherSpec) { // Store this cipher identifier in the cache pZombie->aiCipher = Pct1CipherRank[i].aiCipher; pZombie->dwStrength = Pct1CipherRank[i].dwStrength; // Load the pending cipher structure. pContext->pPendingCipherInfo = GetCipherInfo(pZombie->aiCipher, pZombie->dwStrength); if(!IsCipherAllowed(pContext, pContext->pPendingCipherInfo, pZombie->fProtocol, pZombie->dwCF)) { pContext->pPendingCipherInfo = NULL; continue; } break; } } for(i=0; i < Pct1NumHash; i++) { if(Pct1HashRank[i].Spec == pHello->SrvHashSpec) { // Store this hash id in the cache pZombie->aiHash = Pct1HashRank[i].aiHash; // Load the pending hash sturcture pContext->pPendingHashInfo = GetHashInfo(pZombie->aiHash); if(!IsHashAllowed(pContext, pContext->pPendingHashInfo, pZombie->fProtocol)) { pContext->pPendingHashInfo = NULL; continue; } break; } } for(i=0; i < cPct1LocalExchKeyPref; i++) { if(aPct1LocalExchKeyPref[i].Spec == pHello->SrvExchSpec) { // Store the exch id in the cache. pZombie->SessExchSpec = aPct1LocalExchKeyPref[i].Spec; // load the exch info structure pContext->pKeyExchInfo = GetKeyExchangeInfo(pZombie->SessExchSpec); if(!IsExchAllowed(pContext, pContext->pKeyExchInfo, pZombie->fProtocol)) { pContext->pKeyExchInfo = NULL; continue; } break; } } if (pContext->pPendingCipherInfo == NULL) { fMismatch |= PCT_IMIS_CIPHER; pctRet = SP_LOG_RESULT(PCT_ERR_SPECS_MISMATCH); goto cleanup; } if (pContext->pPendingHashInfo == NULL) { fMismatch |= PCT_IMIS_HASH; pctRet = SP_LOG_RESULT(PCT_ERR_SPECS_MISMATCH); goto cleanup; } if (pContext->pKeyExchInfo == NULL) { fMismatch |= PCT_IMIS_EXCH; pctRet = SP_LOG_RESULT(PCT_ERR_SPECS_MISMATCH); goto cleanup; } // Determine the CSP to use, based on the key exchange algorithm. if(pContext->pKeyExchInfo->Spec != SP_EXCH_RSA_PKCS1) { pctRet = SP_LOG_RESULT(PCT_ERR_SPECS_MISMATCH); goto cleanup; } pContext->RipeZombie->hMasterProv = g_hRsaSchannel; // Go ahead and move the pending ciphers to active, and init them. pctRet = ContextInitCiphers(pContext, TRUE, TRUE); if(PCT_ERR_OK != pctRet) { goto cleanup; } /* we aren't restarting, so let's continue with the protocol. */ /* Crack the server certificate */ pctRet = SPLoadCertificate(pZombie->fProtocol, X509_ASN_ENCODING, pHello->pCertificate, pHello->CertificateLen, &pZombie->pRemoteCert); if(pctRet != PCT_ERR_OK) { goto cleanup; } if(pContext->RipeZombie->pRemotePublic != NULL) { SPExternalFree(pContext->RipeZombie->pRemotePublic); pContext->RipeZombie->pRemotePublic = NULL; } pctRet = SPPublicKeyFromCert(pZombie->pRemoteCert, &pZombie->pRemotePublic, NULL); if(pctRet != PCT_ERR_OK) { goto cleanup; } // Automatically validate server certificate if appropriate // context flag is set. pctRet = AutoVerifyServerCertificate(pContext); if(pctRet != PCT_ERR_OK) { SP_LOG_RESULT(pctRet); goto cleanup; } pZombie->pbServerCertificate = SPExternalAlloc(pHello->CertificateLen); pZombie->cbServerCertificate = pHello->CertificateLen; if(pZombie->pbServerCertificate == NULL) { pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); goto cleanup; } CopyMemory(pZombie->pbServerCertificate, pHello->pCertificate, pHello->CertificateLen); /* Create the verify prelude hashes */ /* Which should look like */ /* Hash(CLIENT_MAC_KEY, Hash( "cvp", CLIENT_HELLO, SERVER_HELLO)) */ /* Here we just do the inner hash */ if(pContext->pClientHello == NULL) { pctRet = SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR); goto cleanup; } pCMKey = (PPct1_Client_Master_Key)SPExternalAlloc(sizeof(Pct1_Client_Master_Key) + cbClientCert); if (NULL == pCMKey) { pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); goto cleanup; } // Generate Key Args if(pContext->pCipherInfo->dwBlockSize > 1) { pctRet = GenerateRandomBits(pZombie->pKeyArgs, pContext->pCipherInfo->dwBlockSize); if(!NT_SUCCESS(pctRet)) { goto cleanup; } pZombie->cbKeyArgs = pCMKey->KeyArgLen = pContext->pCipherInfo->dwBlockSize; /* Copy over the key args */ CopyMemory(pCMKey->KeyArg, pZombie->pKeyArgs, pZombie->cbKeyArgs ); } else { pCMKey->KeyArgLen = 0; } pctRet = pContext->pKeyExchInfo->System->GenerateClientExchangeValue( pContext, pHello->Response, pHello->ResponseLen, pCMKey->ClearKey, &pCMKey->ClearKeyLen, NULL, &pCMKey->EncryptedKeyLen); if(PCT_ERR_OK != pctRet) { goto cleanup; } pCMKey->pbEncryptedKey = SPExternalAlloc(pCMKey->EncryptedKeyLen); if(pCMKey->pbEncryptedKey == NULL) { pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); goto cleanup; } pctRet = pContext->pKeyExchInfo->System->GenerateClientExchangeValue( pContext, pHello->Response, pHello->ResponseLen, pCMKey->ClearKey, &pCMKey->ClearKeyLen, pCMKey->pbEncryptedKey, &pCMKey->EncryptedKeyLen); if(PCT_ERR_OK != pctRet) { goto cleanup; } pctRet = Pct1BeginVerifyPrelude(pContext, pContext->pClientHello, pContext->cbClientHello, pCommInput->pvBuffer, pCommInput->cbData); if(PCT_ERR_OK != pctRet) { goto cleanup; } // Activate session keys. Pct1ActivateSessionKeys(pContext); pCMKey->VerifyPreludeLen = sizeof(pCMKey->VerifyPrelude); pctRet = Pct1EndVerifyPrelude(pContext, pCMKey->VerifyPrelude, &pCMKey->VerifyPreludeLen); if(PCT_ERR_OK != pctRet) { goto cleanup; } /* Choose a client cert */ /* For each Cert the server understands, check to see if we */ /* have that type of cert */ pCMKey->ClientCertLen = 0; pCMKey->ClientCertSpec = 0; pCMKey->ClientSigSpec = 0; pCMKey->ResponseLen = 0; if(fClientAuth && pSigInfo != NULL) { // The client cert spec was already chosen // Also, pContext->fCertChainsAllowed will be // previously set if we're doing chains. pCMKey->ClientCertSpec = ClientCertSpec; pCMKey->ClientSigSpec = pSigInfo->Spec; pCMKey->pClientCert = (PUCHAR)(pCMKey+1); pCMKey->ClientCertLen = cbClientCert; memcpy(pCMKey->pClientCert, pbClientCert, cbClientCert); // Allocate memory for signature. pCMKey->ResponseLen = pContext->pActiveClientCred->pPublicKey->cbPublic; pCMKey->pbResponse = SPExternalAlloc(pCMKey->ResponseLen); if(pCMKey->pbResponse == NULL) { pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); goto cleanup; } DebugLog((DEB_TRACE, "Sign client response.\n")); // Sign hash via a call to the application process. pctRet = SignHashUsingCallback(pContext->pActiveClientCred->hRemoteProv, pContext->pActiveClientCred->dwKeySpec, pSigInfo->aiHash, pCMKey->VerifyPrelude, pCMKey->VerifyPreludeLen, pCMKey->pbResponse, &pCMKey->ResponseLen, TRUE); if(pctRet != PCT_ERR_OK) { goto cleanup; } DebugLog((DEB_TRACE, "Client response signed successfully.\n")); // Convert signature to big endian. ReverseInPlace(pCMKey->pbResponse, pCMKey->ResponseLen); } pctRet = PCT_ERR_ILLEGAL_MESSAGE; if(PCT_ERR_OK != (pctRet = Pct1PackClientMasterKey(pCMKey, pCommOutput))) { pctRet = SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); goto cleanup; } pContext->WriteCounter++; pctRet = PCT_ERR_OK; cleanup: if(pCMKey) { if(pCMKey->pbEncryptedKey) { SPExternalFree(pCMKey->pbEncryptedKey); } if(pCMKey->pbResponse) { SPExternalFree(pCMKey->pbResponse); } SPExternalFree(pCMKey); } if(pbClientCert) { SPExternalFree(pbClientCert); } if(pctRet != PCT_ERR_OK) { if(pctRet == PCT_ERR_SPECS_MISMATCH) { for(i=0;i> 1; } ErrData.cbData = ErrData.cbBuffer = PCT_NUM_MISMATCHES; ErrData.pvBuffer = MisData; } pctRet = Pct1GenerateError(pContext, pCommOutput, pctRet, &ErrData); pctRet |= PCT_INT_DROP_CONNECTION; } SP_RETURN(pctRet); } SP_STATUS Pct1CliRestart(PSPContext pContext, PPct1_Server_Hello pHello, PSPBuffer pCommOutput) { SP_STATUS pctRet = PCT_ERR_ILLEGAL_MESSAGE; UCHAR Response[RESPONSE_SIZE]; DWORD cbResponse; PPct1_Server_Hello pLocalHello = pHello; PSessCacheItem pZombie; SP_BEGIN("Pct1CliRestart"); pZombie = pContext->RipeZombie; /* if there's no zombie, the message is wrong. We can't restart. */ if(pZombie == NULL) { pctRet = SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); goto error; } if(!pZombie->hMasterKey) { pctRet = SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); goto error; } if(!pZombie->ZombieJuju) { DebugLog((DEB_WARN, "Session expired on client machine, but not on server.\n")); } CopyMemory(pContext->pConnectionID, pHello->ConnectionID, pHello->cbConnectionID); pContext->cbConnectionID = pHello->cbConnectionID; //Init pending ciphers pctRet = ContextInitCiphersFromCache(pContext); if(PCT_ERR_OK != pctRet) { goto error; } // We know what our ciphers are, so init the cipher system pctRet = ContextInitCiphers(pContext, TRUE, TRUE); if(PCT_ERR_OK != pctRet) { goto error; } // Make a new set of session keys. pctRet = MakeSessionKeys(pContext, pContext->RipeZombie->hMasterProv, pContext->RipeZombie->hMasterKey); if(PCT_ERR_OK != pctRet) { goto error; } // Activate session keys. Pct1ActivateSessionKeys(pContext); pctRet = PCT_ERR_ILLEGAL_MESSAGE; DebugLog((DEB_TRACE, "Session Keys Made\n")); /* let's check the response in the message */ /* check the length */ if (pLocalHello->ResponseLen != pContext->pHashInfo->cbCheckSum) { pctRet = SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); goto error; } /* calculate the correct response */ cbResponse = sizeof(Response); pctRet = Pct1ComputeResponse(pContext, pContext->pChallenge, pContext->cbChallenge, pContext->pConnectionID, pContext->cbConnectionID, pZombie->SessionID, pZombie->cbSessionID, Response, &cbResponse); if(pctRet != PCT_ERR_OK) { goto error; } /* check it against the response in the message */ if (memcmp(Response, pLocalHello->Response, pLocalHello->ResponseLen)) { pctRet = SP_LOG_RESULT(PCT_ERR_SERVER_AUTH_FAILED); goto error; } /* ok, we're done, so let's jettison the auth data */ pContext->ReadCounter = 1; pContext->WriteCounter = 1; /* fini. */ SP_RETURN(PCT_ERR_OK); error: pctRet = Pct1GenerateError(pContext, pCommOutput, pctRet, NULL); SP_RETURN(pctRet | PCT_INT_DROP_CONNECTION); } SP_STATUS Pct1CliHandleServerVerify( PSPContext pContext, PSPBuffer pCommInput, PSPBuffer pCommOutput) { SP_STATUS pctRet; PPct1_Server_Verify pVerify = NULL; SPBuffer ErrData; PSessCacheItem pZombie; UCHAR Response[RESPONSE_SIZE]; DWORD cbResponse; SP_BEGIN("Pct1CliHandleServerVerify"); pZombie = pContext->RipeZombie; pContext->ReadCounter = 2; pContext->WriteCounter = 2; pCommOutput->cbData = 0; ErrData.cbData = 0; ErrData.pvBuffer = NULL; ErrData.cbBuffer = 0; /* unpack the message */ pctRet = Pct1UnpackServerVerify(pCommInput, &pVerify); if (PCT_ERR_OK != pctRet) { // If it's an incomplete message or something, just return; if(!SP_FATAL(pctRet)) { SP_RETURN(pctRet); } goto error; } // compute the correct response cbResponse = sizeof(Response); pctRet = Pct1ComputeResponse(pContext, pContext->pChallenge, pContext->cbChallenge, pContext->pConnectionID, pContext->cbConnectionID, pVerify->SessionIdData, PCT_SESSION_ID_SIZE, Response, &cbResponse); if(pctRet != PCT_ERR_OK) { SP_LOG_RESULT(pctRet); goto error; } if(pVerify->ResponseLen != cbResponse || memcmp(pVerify->Response, Response, pVerify->ResponseLen)) { pctRet = SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE); goto error; } CopyMemory(pZombie->SessionID, pVerify->SessionIdData, PCT_SESSION_ID_SIZE); pZombie->cbSessionID = PCT_SESSION_ID_SIZE; /* done with the verify data */ SPExternalFree(pVerify); pVerify = NULL; /* set up the session in cache */ SPCacheAdd(pContext); SP_RETURN( PCT_ERR_OK ); error: if(pVerify) SPExternalFree(pVerify); pctRet = Pct1GenerateError(pContext, pCommOutput, pctRet, NULL); SP_RETURN(pctRet | PCT_INT_DROP_CONNECTION); } SP_STATUS WINAPI GeneratePct1StyleHello( PSPContext pContext, PSPBuffer pOutput) { Pct1_Client_Hello HelloMessage; PSessCacheItem pZombie; CipherSpec aCipherSpecs[10]; HashSpec aHashSpecs[10]; CertSpec aCertSpecs[10]; ExchSpec aExchSpecs[10]; DWORD i; SP_STATUS pctRet = PCT_INT_INTERNAL_ERROR; SP_BEGIN("Pct1CliInstigateHello"); HelloMessage.pCipherSpecs = aCipherSpecs; HelloMessage.pHashSpecs = aHashSpecs; HelloMessage.pCertSpecs = aCertSpecs; HelloMessage.pExchSpecs = aExchSpecs; if(pContext == NULL) { SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR)); } if (!pOutput) { SP_RETURN(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR)); } pZombie = pContext->RipeZombie; pContext->Flags |= CONTEXT_FLAG_CLIENT; pctRet = GenerateRandomBits(pContext->pChallenge, PCT1_CHALLENGE_SIZE); if(!NT_SUCCESS(pctRet)) { SP_RETURN(SP_LOG_RESULT(pctRet)); } pContext->cbChallenge = PCT1_CHALLENGE_SIZE; /* Build the hello message. */ HelloMessage.cbChallenge = PCT1_CHALLENGE_SIZE; HelloMessage.pKeyArg = NULL; HelloMessage.cbKeyArgSize = 0; HelloMessage.cCipherSpecs = 0; for(i=0; i < Pct1NumCipher; i++) { PCipherInfo pCipherInfo; pCipherInfo = GetCipherInfo(Pct1CipherRank[i].aiCipher, Pct1CipherRank[i].dwStrength); if(IsCipherAllowed(pContext, pCipherInfo, pContext->dwProtocol, pContext->dwRequestedCF)) { HelloMessage.pCipherSpecs[HelloMessage.cCipherSpecs++] = Pct1CipherRank[i].Spec; } } HelloMessage.cHashSpecs = 0; for(i=0; i < Pct1NumHash; i++) { PHashInfo pHashInfo; pHashInfo = GetHashInfo(Pct1HashRank[i].aiHash); if(IsHashAllowed(pContext, pHashInfo, pContext->dwProtocol)) { HelloMessage.pHashSpecs[HelloMessage.cHashSpecs++] = Pct1HashRank[i].Spec; } } HelloMessage.cCertSpecs = 0; for(i=0; i < cPct1CertEncodingPref; i++) { PCertSysInfo pCertInfo = GetCertSysInfo(aPct1CertEncodingPref[i].dwCertEncodingType); if(pCertInfo == NULL) { continue; } // Is this cert type enabled? if(0 == (pCertInfo->fProtocol & SP_PROT_PCT1_CLIENT)) { continue; } HelloMessage.pCertSpecs[HelloMessage.cCertSpecs++] = aPct1CertEncodingPref[i].Spec; } HelloMessage.cExchSpecs = 0; for(i=0; i < cPct1LocalExchKeyPref; i++) { PKeyExchangeInfo pExchInfo; pExchInfo = GetKeyExchangeInfo(aPct1LocalExchKeyPref[i].Spec); if(IsExchAllowed(pContext, pExchInfo, pContext->dwProtocol)) { HelloMessage.pExchSpecs[HelloMessage.cExchSpecs++] = aPct1LocalExchKeyPref[i].Spec; } } if (pZombie->cbSessionID) { CopyMemory(HelloMessage.SessionID, pZombie->SessionID, pZombie->cbSessionID); HelloMessage.cbSessionID = pZombie->cbSessionID; } else { FillMemory(HelloMessage.SessionID, PCT_SESSION_ID_SIZE, 0); HelloMessage.cbSessionID = PCT_SESSION_ID_SIZE; } CopyMemory( HelloMessage.Challenge, pContext->pChallenge, HelloMessage.cbChallenge ); HelloMessage.cbChallenge = pContext->cbChallenge; pctRet = Pct1PackClientHello(&HelloMessage, pOutput); if(PCT_ERR_OK != pctRet) { SP_RETURN(pctRet); } // Save the ClientHello message so we can hash it later, once // we know what algorithm and CSP we're using. if(pContext->pClientHello) { SPExternalFree(pContext->pClientHello); } pContext->pClientHello = SPExternalAlloc(pOutput->cbData); if(pContext->pClientHello == NULL) { SP_RETURN(SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY)); } CopyMemory(pContext->pClientHello, pOutput->pvBuffer, pOutput->cbData); pContext->cbClientHello = pOutput->cbData; pContext->dwClientHelloProtocol = SP_PROT_PCT1_CLIENT; /* We set this here to tell the protocol engine that we just send a client * hello, and we're expecting a pct server hello */ pContext->State = PCT1_STATE_CLIENT_HELLO; SP_RETURN(PCT_ERR_OK); }