//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1995. // // File: context.c // // Contents: // // Classes: // // Functions: // // History: 8-08-95 RichardW Created // 8-13-95 TerenceS PCTized // //---------------------------------------------------------------------------- // TBD: finish client authentication. // TBD: include another exchange system? // TBD: add redo capability? #include "pctsspi.h" #define VALID_REQUEST_FLAGS ( ISC_REQ_MUTUAL_AUTH | \ ISC_REQ_REPLAY_DETECT | \ ISC_REQ_SEQUENCE_DETECT | \ ISC_REQ_CONFIDENTIALITY | \ ISC_REQ_ALLOCATE_MEMORY | \ ISC_REQ_CONNECTION ) #include "md5.h" // supported cipher type arrays CipherSelect PctAvailableCiphers[] = { { PCT_CIPHER_RC4 | PCT_ENC_BITS_128 | PCT_MAC_BITS_128, &csRC4 }, { PCT_CIPHER_RC4 | PCT_ENC_BITS_40 | PCT_MAC_BITS_128, &csRC4 }, }; // available ciphers, in order of preference CipherSpec PctCipherRank[] = { PCT_CIPHER_RC4 | PCT_ENC_BITS_128 | PCT_MAC_BITS_128, PCT_CIPHER_RC4 | PCT_ENC_BITS_40 | PCT_MAC_BITS_128 }; DWORD PctNumAvailableCiphers = 2; HashSelect PctAvailableHashes[] = { { PCT_HASH_MD5, &ckMD5 } }; // available hashes, in order of preference HashSpec PctHashRank[] = { PCT_HASH_MD5 }; DWORD PctNumAvailableHashes = 1; CertSpec PctAvailableCerts[] = { PCT_CERT_X509 }; DWORD PctNumAvailableCerts = 1; SigSpec PctAvailableSigs[] = { PCT_SIG_NONE }; DWORD PctNumAvailableSigs = 1; ExchSpec PctAvailableExch[] = { PCT_EXCH_RSA_PKCS1 }; DWORD PctNumAvailableExch = 1; #if DBG typedef struct _DbgMapCrypto { DWORD C; PSTR psz; } DbgMapCrypto; DbgMapCrypto DbgCryptoNames[] = { {PCT_CIPHER_RC4, "RC4 "}, }; CHAR DbgNameSpace[100]; PSTR DbgAlgNames[] = { "Basic RSA", "RSA with MD2", "RSA with MD5", "RC4 stream"}; #define AlgName(x) ((x < sizeof(DbgAlgNames) / sizeof(PSTR)) ? DbgAlgNames[x] : "Unknown") PSTR DbgGetNameOfCrypto(DWORD x) { int i; for (i = 0; i < sizeof(DbgCryptoNames) / sizeof(DbgMapCrypto) ; i++ ) { if ((x & PCT_CIPHER_ALG) == DbgCryptoNames[i].C) { sprintf(DbgNameSpace, "%s %d / %d MACbits", (DbgCryptoNames[i].psz), (x & PCT_CIPHER_STRENGTH) >> PCT_CSTR_POS, (x & PCT_CIPHER_MAC)); return DbgNameSpace; } } return("Unknown"); } #endif // allow quick initialization of hashes void InitHashBuf(HashBuf Buf, PPctContext pContext ) { CloneHashBuf(Buf, pContext->InitMACState, pContext->pCheck); return; } // session key computation BOOL PctComputeKey(PCheckSumBuffer *ppHash, PPctContext pContext, UCHAR *Buffer, UCHAR *pConst, DWORD dwCLen, DWORD fFlags) { DWORD BufferLen; PPctAuthContext pAuth; #if DBG DWORD di; CHAR BufDispBuf[16 * 3 + 5]; #endif pAuth = pContext->pAuthData; BufferLen = 0; Buffer[BufferLen] = 1; BufferLen += 1; if (!(fFlags & PCT_MAKE_MAC)) { // add the first constant CopyMemory(Buffer+BufferLen, pConst, dwCLen); BufferLen += dwCLen; } // add the master key CopyMemory(Buffer+BufferLen, pAuth->MasterKey, MASTER_KEY_SIZE ); BufferLen += MASTER_KEY_SIZE; // repeat the constant CopyMemory(Buffer+BufferLen, pConst, dwCLen); BufferLen += dwCLen; // add the connection id CopyMemory(Buffer+BufferLen, pAuth->ConnectionId.bSessionId, PCT_SESSION_ID_SIZE); BufferLen += PCT_SESSION_ID_SIZE; // repeat the constant CopyMemory(Buffer+BufferLen, pConst, dwCLen); BufferLen += dwCLen; if (fFlags & PCT_USE_CERT) { // add in the certificate CopyMemory(Buffer+BufferLen, (UCHAR *)pAuth->pRawCert, pAuth->CertLen); BufferLen += pAuth->CertLen; // repeat the constant CopyMemory(Buffer+BufferLen, pConst, dwCLen); BufferLen += dwCLen; } // add the challenge CopyMemory(Buffer+BufferLen, (UCHAR *)pAuth->Challenge, PCT_CHALLENGE_SIZE); BufferLen += PCT_CHALLENGE_SIZE; // again, repeat the constant CopyMemory(Buffer+BufferLen, pConst, dwCLen); BufferLen += dwCLen; #if DBG #if DBG_WATCH_KEYS DebugLog((DEB_TRACE, "Buffer:\n")); BufDispBuf[0] = 0; for(di=0;dipCheck->Sum( *ppHash, BufferLen, Buffer ); return TRUE; } BOOL PctMakeSessionKeys(PPctContext pContext, UCHAR *pClearKey, DWORD dwClearLen) { PCheckSumBuffer CWriteHash, SWriteHash, CMacHash, SMacHash; PCheckSumBuffer CExportKey, SExportKey; PPctAuthContext pAuth; UCHAR pWriteKey[MASTER_KEY_SIZE], pReadKey[MASTER_KEY_SIZE]; UCHAR Buffer[DERIVATION_BUFFER_SIZE]; DWORD BufferLen, MaxBufferLen, dwKeyLen; HashBuf CWriteHB, SWriteHB, CMACHB, SMACHB, CExpHB, SExpHB; #if DBG DWORD i; CHAR KeyDispBuf[MASTER_KEY_SIZE*2+1]; #endif if (!pContext->InitMACState) return FALSE; InitHashBuf(CWriteHB, pContext); InitHashBuf(SWriteHB, pContext); InitHashBuf(CMACHB, pContext); InitHashBuf(SMACHB, pContext); InitHashBuf(CExpHB, pContext); InitHashBuf(SExpHB, pContext); CWriteHash = (PCheckSumBuffer)CWriteHB; SWriteHash = (PCheckSumBuffer)SWriteHB; CMacHash = (PCheckSumBuffer)CMACHB; SMacHash = (PCheckSumBuffer)SMACHB; CExportKey = (PCheckSumBuffer)CExpHB; SExportKey = (PCheckSumBuffer)SExpHB; pAuth = pContext->pAuthData; #if DBG DebugLog((DEB_TRACE, "Making session keys\n", KeyDispBuf)); for(i=0;iConnectionId.bSessionId[i]); DebugLog((DEB_TRACE, " ConnId\t%s\n", KeyDispBuf)); for(i=0;ipRawCert))[i]); DebugLog((DEB_TRACE, " Cert\t%s\n", KeyDispBuf)); for(i=0;iChallenge[i]); DebugLog((DEB_TRACE, " Challenge \t%s\n", KeyDispBuf)); #endif MaxBufferLen = 1 + // initial number PCT_MAX_NUM_SEP * PCT_MAX_SEP_LEN + MASTER_KEY_SIZE + PCT_SESSION_ID_SIZE + PCT_SESSION_ID_SIZE + pAuth->CertLen; if (MaxBufferLen > DERIVATION_BUFFER_SIZE) return FALSE; // compute the ClientWriteKey PctComputeKey( &CWriteHash, pContext, Buffer, PCT_CONST_CWK, PCT_CONST_CWK_LEN, PCT_USE_CERT); // compute the ServerWriteKey PctComputeKey( &SWriteHash, pContext, Buffer, PCT_CONST_SWK, PCT_CONST_SWK_LEN, 0); // compute the ClientMacKey PctComputeKey( &CMacHash, pContext, Buffer, PCT_CONST_CMK, PCT_CONST_CMK_LEN, PCT_USE_CERT | PCT_MAKE_MAC); // compute the ServerMacKey PctComputeKey( &SMacHash, pContext, Buffer, PCT_CONST_SMK, PCT_CONST_SMK_LEN, PCT_MAKE_MAC); // find bit strength of cipher dwKeyLen = (pAuth->CipherType & PCT_CIPHER_STRENGTH) >> PCT_CSTR_POS; // convert to bytes dwKeyLen = dwKeyLen / 8; if (dwKeyLen < (pContext->pCheck->cbCheckSum)) { // chop the encryption keys down to selected length #if DBG DebugLog((DEB_TRACE, "Chopping down client write keys\n", KeyDispBuf)); #endif // compute the standard length clientwritekey BufferLen = 0; Buffer[BufferLen] = 1; BufferLen += 1; CopyMemory(Buffer+BufferLen, "sl", 2); BufferLen += 2; pContext->pCheck->Finalize( CWriteHash, Buffer+BufferLen ); BufferLen += dwKeyLen; CopyMemory(Buffer+BufferLen, "sl", 2); BufferLen += 2; CopyMemory(Buffer+BufferLen, pClearKey, dwClearLen); BufferLen += dwClearLen; InitHashBuf(CWriteHB, pContext); pContext->pCheck->Sum( CWriteHash, BufferLen, Buffer ); // compute the standard length serverwritekey #if DBG DebugLog((DEB_TRACE, "Chopping down server write keys\n", KeyDispBuf)); #endif BufferLen = 0; Buffer[BufferLen] = 1; BufferLen += 1; CopyMemory(Buffer+BufferLen, PCT_CONST_SLK, PCT_CONST_SLK_LEN); BufferLen += PCT_CONST_SLK_LEN; pContext->pCheck->Finalize( SWriteHash, Buffer+BufferLen ); BufferLen += dwKeyLen; CopyMemory(Buffer+BufferLen, PCT_CONST_SLK, PCT_CONST_SLK_LEN); BufferLen += PCT_CONST_SLK_LEN; CopyMemory(Buffer+BufferLen, pClearKey, dwClearLen); BufferLen += dwClearLen; InitHashBuf(SWriteHB, pContext); pContext->pCheck->Sum( SWriteHash, BufferLen, Buffer ); } pContext->KeySize = MASTER_KEY_SIZE; if (pContext->Flags & CONTEXT_FLAG_OUTBOUND) { pContext->pCheck->Finalize( SWriteHash, pReadKey ); pContext->pCheck->Finalize( CWriteHash, pWriteKey ); pContext->pCheck->Finalize( SMacHash, pContext->ReadMACKey ); pContext->pCheck->Finalize( CMacHash, pContext->WriteMACKey ); } else { pContext->pCheck->Finalize( SWriteHash, pWriteKey ); pContext->pCheck->Finalize( CWriteHash, pReadKey ); pContext->pCheck->Finalize( SMacHash, pContext->WriteMACKey ); pContext->pCheck->Finalize( CMacHash, pContext->ReadMACKey ); } pContext->pCheck->Initialize(0, &(pContext->ReadMACState)); pContext->pCheck->Sum( pContext->ReadMACState, pContext->KeySize, pContext->ReadMACKey); pContext->pCheck->Initialize(0, &(pContext->WriteMACState)); pContext->pCheck->Sum( pContext->WriteMACState, pContext->KeySize, pContext->WriteMACKey); #if DBG for(i=0;iMasterKey[i]); DebugLog((DEB_TRACE, " MasterKey \t%s\n", KeyDispBuf)); for(i=0;iReadMACKey[i]); DebugLog((DEB_TRACE, " MACKey\t%s\n", KeyDispBuf)); for(i=0;iWriteMACKey[i]); DebugLog((DEB_TRACE, " MACKey\t%s\n", KeyDispBuf)); #endif if (pContext->pSystem->Initialize( pReadKey, CONTEXT_KEY_SIZE, &pContext->pReadState ) ) { if (pContext->pSystem->Initialize( pWriteKey, CONTEXT_KEY_SIZE, &pContext->pWriteState) ) { return (TRUE); } pContext->pSystem->Discard( &pContext->pReadState ); } return( FALSE ); } PSTR CopyString( PSTR pszString) { PSTR pszNewString; DWORD cchString; cchString = strlen(pszString) + 1; pszNewString = LocalAlloc(LMEM_FIXED, cchString); if (pszNewString) { CopyMemory(pszNewString, pszString, cchString); } return(pszNewString); } PPctContext PctCreateContext( VOID) { PPctContext pContext; pContext = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeof(PctContext)); if (pContext) { if ((pContext->pAuthData = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeof(PctAuthContext))) == NULL) { LocalFree(pContext); return NULL; } } pContext->Magic = PCT_CONTEXT_MAGIC; pContext->Flags = 0; return(pContext); } VOID PctDeleteAuthContext( PPctAuthContext pAuth) { if (pAuth) { if (pAuth->pCred) PctDereferenceCredential( pAuth->pCred ); if (pAuth->pCertificate) LocalFree( pAuth->pCertificate ); if (pAuth->pClHello) LocalFree( pAuth->pClHello ); ZeroMemory( pAuth, sizeof(PctAuthContext) ); LocalFree(pAuth); } } VOID PctDeleteContext(PPctContext pContext) { if (pContext->pszName) LocalFree(pContext->pszName); if (pContext->ReadMACState) pContext->pCheck->Finish( &(pContext->ReadMACState) ); if (pContext->WriteMACState) pContext->pCheck->Finish( &(pContext->WriteMACState) ); if (pContext->InitMACState) pContext->pCheck->Finish( &(pContext->InitMACState) ); if (pContext->pReadState) pContext->pSystem->Discard( &pContext->pReadState ); if (pContext->pWriteState) pContext->pSystem->Discard( &pContext->pWriteState ); PctDeleteAuthContext( pContext->pAuthData ); ZeroMemory( pContext, sizeof( PctContext ) ); LocalFree( pContext ); } PPctContext PctpValidateContextHandle(PCtxtHandle phContext) { PPctContext pContext = NULL; BOOL fReturn; fReturn = FALSE; if (phContext) { try { pContext = (PPctContext) phContext->dwUpper; if (pContext->Magic == PCT_CONTEXT_MAGIC) { fReturn = TRUE; } } except (EXCEPTION_EXECUTE_HANDLER) { pContext = NULL; } } if (fReturn) { return(pContext); } return(NULL); } SECURITY_STATUS PctHandleServerHello( PPctContext pContext, PSecBufferDesc pInput, PCtxtHandle phNewContext, PSecBufferDesc pOutput, unsigned long SEC_FAR * pfContextAttr, PTimeStamp ptsExpiry ) { SECURITY_STATUS scRet; PPctAuthContext pAuth; PServer_Hello pHello; PCheckSumBuffer pSum, pSubSum; Client_Master_Key Key; PctError XmitError; ExportKeyInfo *pExportKey; DWORD i, dwKeyLen, ExchOK, dwError; BOOL fPack; PSecBuffer pBuffer; PX509Certificate pCertificate; PUCHAR pPortionToEncrypt; DWORD BytesToEncrypt; UCHAR Temp[16]; UCHAR TmpDigest[RESPONSE_SIZE]; HashBuf SumBuf, SubSumBuf; // validate the buffer configuration if ((!pOutput) || (pOutput->cBuffers < 1) || (pInput->pBuffers[0].cbBuffer < sizeof(Pct_Record_Header))) { return(SEC_E_INVALID_TOKEN); } // make sure we have the needed authentication data area if (pContext->pAuthData == NULL) { return (SEC_E_INVALID_TOKEN); } pAuth = pContext->pAuthData; pBuffer = pOutput->pBuffers; if (pContext->ContextAttr & ISC_REQ_ALLOCATE_MEMORY) { pBuffer->pvBuffer = NULL; pBuffer->cbBuffer = 0; *pfContextAttr = ISC_RET_ALLOCATED_MEMORY; } if (!UnpackServerHello(TRUE, &dwError, (PPct_Server_Hello) pInput->pBuffers[0].pvBuffer, pInput->pBuffers[0].cbBuffer, &pHello)) { if (dwError) { // process error from client here.... } return(SEC_E_INVALID_TOKEN); } #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%x\n", pHello->CertificateLen)); #endif pAuth->CipherType = pHello->SrvCipherSpec; pAuth->HashType = pHello->SrvHashSpec; pAuth->ExchType = pHello->SrvExchSpec; pAuth->ConnectionId = pHello->Connection; ExchOK = 0; for(i=0;iSrvExchSpec) { // init the system ExchOK = 1; } if (!ExchOK) { PctExternalFree(pHello); return (SEC_E_INVALID_TOKEN); } pContext->pSystem = NULL; for(i=0;iSrvCipherSpec) pContext->pSystem = PctAvailableCiphers[i].System; if (pContext->pSystem == NULL) { PctExternalFree(pHello); return (SEC_E_INVALID_TOKEN); } pContext->pCheck = NULL; for(i=0;iSrvHashSpec) { pContext->pCheck = PctAvailableHashes[i].System; pContext->pCheck->Initialize(0, &(pContext->InitMACState)); } if (pContext->pCheck == NULL) { PctExternalFree(pHello); return (SEC_E_INVALID_TOKEN); } // let's resurrect the zombie session if (pHello->RestartOk) { // if there's no zombie, the message is wrong. We can't restart. if ((pAuth->ZombieJuju) == PCT_DEAD_ZOMBIE) { PctExternalFree(pHello); return (SEC_E_INVALID_TOKEN); } // get all the needed goo from the zombie data pAuth->SessionId = pAuth->ZombieSession.Session; CopyMemory(pAuth->MasterKey, pAuth->ZombieSession.MasterKey, MASTER_KEY_SIZE); memcpy(pAuth->pRawCert, pAuth->ZombieSession.CertData, pAuth->ZombieSession.SessCertLen); pAuth->CertLen = pAuth->ZombieSession.SessCertLen; if ((pAuth->ZombieSession.SessCiphSpec & PCT_CIPHER_STRENGTH) < PCT_ENC_BITS_128) { pExportKey = pAuth->ZombieSession.ClearData; scRet = PctMakeSessionKeys(pContext, pExportKey->ClearKey, pExportKey->dwClearLen); } else { scRet = PctMakeSessionKeys(pContext, NULL, 0); } // let's check the response in the message // check the length if (pHello->ResponseLen != pContext->pCheck->cbCheckSum) return (SEC_E_INVALID_TOKEN); // calculate the correct response // set up the two hashes InitHashBuf(SubSumBuf, pContext); CloneHashBuf(SumBuf, pContext->ReadMACState, pContext->pCheck); pSum = (PCheckSumBuffer)SumBuf; pSubSum = (PCheckSumBuffer)SubSumBuf; // create the server response pContext->pCheck->Sum(pSubSum, PCT_CONST_RESP_LEN, PCT_CONST_RESP); pContext->pCheck->Sum(pSubSum, PCT_CHALLENGE_SIZE, pAuth->Challenge); pContext->pCheck->Sum(pSubSum, PCT_SESSION_ID_SIZE, pAuth->ConnectionId.bSessionId); pContext->pCheck->Sum(pSubSum, PCT_SESSION_ID_SIZE, pAuth->SessionId.bSessionId); pContext->pCheck->Finalize(pSubSum, TmpDigest); pContext->pCheck->Sum(pSum, pContext->pCheck->cbCheckSum, TmpDigest); pContext->pCheck->Finalize(pSum, TmpDigest); // check it against the response in the message if (memcmp(TmpDigest, pHello->Response, pHello->ResponseLen)) { PctExternalFree(pHello); XmitError.Error = PCT_ERR_SERVER_AUTH_FAILED; XmitError.ErrInfoLen = 0; fPack = PackPctError(&XmitError, (PPct_Error *) &pBuffer->pvBuffer, &pBuffer->cbBuffer); if (!fPack) { return (SEC_E_INVALID_TOKEN); } else { pContext->Flags |= CONTEXT_FLAG_ERRMODE; pContext->Error = SEC_E_INVALID_TOKEN; return (SEC_I_CONTINUE_NEEDED); } } // do we need to authenticate ourselves? if (pHello->ClientAuthReq) { // add the verify prelude InitHashBuf(SumBuf, pContext); pSum = (PCheckSumBuffer)SumBuf; pContext->pCheck->Sum(pSum, MASTER_KEY_SIZE, pContext->WriteMACKey); pContext->pCheck->Finalize(pAuth->pVerifyPrelude, Key.VerifyPrelude); pContext->pCheck->Finish(&(pAuth->pVerifyPrelude)); pContext->pCheck->Sum(pSum, pContext->pCheck->cbCheckSum, Key.VerifyPrelude); pContext->pCheck->Finalize(pSum, Key.VerifyPrelude); Key.VerifyPreludeLen = pContext->pCheck->cbCheckSum; Key.ResponseLen = PCT_SIGNATURE_SIZE; PctExternalFree(pHello); if (!PctSign(pContext, Key.VerifyPrelude, Key.VerifyPreludeLen, Key.Response, &Key.ResponseLen)) { return (SEC_E_INVALID_TOKEN); } return (SEC_I_CONTINUE_NEEDED); } // ok, we're done, so let's jettison the auth data pContext->pAuthData = NULL; PctDeleteAuthContext(pAuth); PctExternalFree(pHello); pContext->ReadCounter = 1; pContext->WriteCounter = 1; // fini. return (SEC_E_OK); } // we aren't restarting, so let's continue with the protocol. CopyMemory(pAuth->pRawCert, pHello->pCertificate, pHello->CertificateLen); pAuth->CertLen = pHello->CertificateLen; switch(pHello->SrvCertSpec) { case PCT_CERT_X509: if (!CrackCertificate(pHello->pCertificate, pHello->CertificateLen, &pCertificate)) { XmitError.Error = PCT_ERR_BAD_CERTIFICATE; XmitError.ErrInfoLen = 0; fPack = PackPctError(&XmitError, (PPct_Error *) &pBuffer->pvBuffer, &pBuffer->cbBuffer); PctExternalFree(pHello); if (!fPack) { return (SEC_E_INVALID_TOKEN); } else { pContext->Flags |= CONTEXT_FLAG_ERRMODE; pContext->Error = SEC_E_INVALID_TOKEN; return (SEC_I_CONTINUE_NEEDED); } } break; default: XmitError.Error = PCT_ERR_BAD_CERTIFICATE; XmitError.ErrInfoLen = 0; fPack = PackPctError(&XmitError, (PPct_Error *) &pBuffer->pvBuffer, &pBuffer->cbBuffer); PctExternalFree(pHello); if (!fPack) { return (SEC_E_INVALID_TOKEN); } else { pContext->Flags |= CONTEXT_FLAG_ERRMODE; pContext->Error = SEC_E_INVALID_TOKEN; return (SEC_I_CONTINUE_NEEDED); } } pAuth->pCertificate = pCertificate; #if DBG DebugLog((DEB_TRACE, "Certificate = %x\n", pCertificate)); DebugLog((DEB_TRACE, " Version = %d\n", pCertificate->Version)); DebugLog((DEB_TRACE, " Serial Number = %d : %d\n", pCertificate->SerialNumber[0], pCertificate->SerialNumber[1])); DebugLog((DEB_TRACE, " AlgId = %d, %s\n", pCertificate->SignatureAlgorithm, AlgName(pCertificate->SignatureAlgorithm))); DebugLog((DEB_TRACE, " Issuer = %s\n", pCertificate->pszIssuer)); DebugLog((DEB_TRACE, " Subject = %s\n", pCertificate->pszSubject)); DebugLog((DEB_TRACE, " Public Key\n")); DebugLog((DEB_TRACE, " keylen = %d\n", pCertificate->pPublicKey->keylen)); DebugLog((DEB_TRACE, " bitlen = %d\n", pCertificate->pPublicKey->bitlen)); DebugLog((DEB_TRACE, " datalen = %d\n", pCertificate->pPublicKey->datalen)); DebugLog((DEB_TRACE, " exponent = %d\n", pCertificate->pPublicKey->pubexp)); #endif GenerateRandomBits(pAuth->MasterKey, MASTER_KEY_SIZE); // take care of generating the needed clearkey if we are not // using a full strength cipher. dwKeyLen = ((pAuth->CipherType & PCT_CIPHER_STRENGTH) >> PCT_CSTR_POS) / 8; if (dwKeyLen < MASTER_KEY_SIZE) { pAuth->dwClearLen = MASTER_KEY_SIZE - dwKeyLen; GenerateRandomBits(pAuth->ClearKey, pAuth->dwClearLen); } else { pAuth->dwClearLen = 0; } pContext->pCheck->Initialize(0, &(pAuth->pVerifyPrelude)); pContext->pCheck->Sum(pAuth->pVerifyPrelude, PCT_CONST_VP_LEN, PCT_CONST_VP); pContext->pCheck->Sum(pAuth->pVerifyPrelude, pAuth->cbClHello, pAuth->pClHello); pContext->pCheck->Sum(pAuth->pVerifyPrelude, pInput->pBuffers[0].cbBuffer, pInput->pBuffers[0].pvBuffer); // don't need the verify goo anymore LocalFree(pAuth->pClHello); pAuth->pClHello = NULL; // don't need the server hello data anymore PctExternalFree(pHello); scRet = PctMakeSessionKeys(pContext, pAuth->ClearKey, pAuth->dwClearLen); // prepare the clientmasterkey message Key.ClientCertLen = 0; // add the verify prelude InitHashBuf(SumBuf, pContext); pSum = (PCheckSumBuffer)SumBuf; pContext->pCheck->Sum(pSum, MASTER_KEY_SIZE, pContext->WriteMACKey); pContext->pCheck->Finalize(pAuth->pVerifyPrelude, Key.VerifyPrelude); pContext->pCheck->Finish(&pAuth->pVerifyPrelude); pContext->pCheck->Sum(pSum, pContext->pCheck->cbCheckSum, Key.VerifyPrelude); pContext->pCheck->Finalize(pSum, Key.VerifyPrelude); Key.VerifyPreludeLen = pContext->pCheck->cbCheckSum; Key.ClearKeyLen = pAuth->dwClearLen; if (pAuth->dwClearLen) CopyMemory(Key.ClearKey, pAuth->MasterKey, pAuth->dwClearLen); Key.KeyArgLen = 0; // // ok, we have the master key. Now, encrypt it with the // public key we got from our friends on the net... // Key.EncryptedKeyLen = ENCRYPTED_KEY_SIZE; if (!PkcsPublicEncrypt( pAuth->MasterKey, MASTER_KEY_SIZE, pCertificate->pPublicKey, NETWORK_ORDER, Key.EncryptedKey, &Key.EncryptedKeyLen)) { // // Clean Up // XmitError.Error = PCT_ERR_BAD_CERTIFICATE; XmitError.ErrInfoLen = 0; fPack = PackPctError(&XmitError, (PPct_Error *) &pBuffer->pvBuffer, &pBuffer->cbBuffer); if (!fPack) { return (SEC_E_NO_AUTHENTICATING_AUTHORITY); } else { pContext->Flags |= CONTEXT_FLAG_ERRMODE; pContext->Error = SEC_E_NO_AUTHENTICATING_AUTHORITY; return (SEC_I_CONTINUE_NEEDED); } } Key.ResponseLen = 0; fPack = PackClientMasterKey(&Key, (PPct_Client_Master_Key *) &pBuffer->pvBuffer, &pBuffer->cbBuffer); pContext->WriteCounter++; pBuffer->BufferType = SECBUFFER_TOKEN; if (fPack) { pContext->Flags |= CONTEXT_FLAG_KEY; return(SEC_I_CONTINUE_NEEDED); } return(SEC_E_INVALID_TOKEN); } SECURITY_STATUS PctHandleServerVerify( PPctContext pContext, PSecBufferDesc pInput, PCtxtHandle phNewContext, PSecBufferDesc pOutput, unsigned long SEC_FAR * pfContextAttr, PTimeStamp ptsExpiry ) { SECURITY_STATUS scRet; PPctAuthContext pAuth; PCheckSumBuffer pSum, pSubSum; PServer_Verify pVerify; DWORD HeaderSize, dwError; PSecBuffer pBuffer; SEC_CHAR *Target; SessCacheItem CachedSession; HashBuf SumBuf, SubSumBuf; pContext->ReadCounter = 2; pContext->WriteCounter = 2; // add to the verify prelude context, skipping the header. if (pInput->pBuffers[0].cbBuffer < sizeof(Pct_Record_Header)) { return (SEC_E_INVALID_TOKEN); } // insure that we have the authentication data around if (pContext->pAuthData == NULL) { return (SEC_E_INVALID_TOKEN); } pAuth = pContext->pAuthData; // unpack the message if (!UnpackServerVerify(TRUE, &dwError, (PPct_Server_Verify) pInput->pBuffers[0].pvBuffer, pInput->pBuffers[0].cbBuffer, &pVerify)) { if (dwError) { // process client error return here. } return(SEC_E_INVALID_TOKEN); } // construct the response CloneHashBuf(SumBuf, pContext->ReadMACState, pContext->pCheck); InitHashBuf(SubSumBuf, pContext); pSum = (PCheckSumBuffer)SumBuf; pSubSum = (PCheckSumBuffer)SubSumBuf; pContext->pCheck->Sum(pSubSum, PCT_CONST_RESP_LEN, PCT_CONST_RESP); pContext->pCheck->Sum(pSubSum, PCT_CHALLENGE_SIZE, pAuth->Challenge); pContext->pCheck->Sum(pSubSum, PCT_SESSION_ID_SIZE, pAuth->ConnectionId.bSessionId); pContext->pCheck->Sum(pSubSum, PCT_SESSION_ID_SIZE, pVerify->SessionIdData); // we don't need the challenge anymore, so finish the compare hash // out into the challenge data memory. pContext->pCheck->Finalize(pSubSum, pAuth->Challenge); pContext->pCheck->Sum(pSum, pContext->pCheck->cbCheckSum, pAuth->Challenge); pContext->pCheck->Finalize(pSum, pAuth->Challenge); if ((pVerify->ResponseLen != pContext->pCheck->cbCheckSum) || (memcmp(pVerify->Response, pAuth->Challenge, pVerify->ResponseLen))) { PctExternalFree(pVerify); return( SEC_E_INVALID_TOKEN ); } CopyMemory(pAuth->SessionId.bSessionId, pVerify->SessionIdData, PCT_SESSION_ID_SIZE); // done with the verify data PctExternalFree(pVerify); if ((pContext->pszName) && ((Target = PctExternalAlloc(strlen(pContext->pszName)+1)) == NULL)) { // we can't cache, but we can still function.... return (SEC_E_OK); } strcpy(Target, pContext->pszName); CachedSession.TargetName = Target; CachedSession.Session = pAuth->SessionId; CachedSession.SessCiphSpec = pAuth->CipherType; CachedSession.SessHashSpec = pAuth->HashType; CachedSession.SessExchSpec = pAuth->ExchType; CachedSession.SessCertLen = pAuth->CertLen; CopyMemory(CachedSession.MasterKey, pAuth->MasterKey, MASTER_KEY_SIZE); CopyMemory(CachedSession.CertData, pAuth->pRawCert, pAuth->CertLen); if (!PctAddToCache(&CachedSession)) { // again, no error return because we can still work with cache // failures... PctExternalFree(Target); } return( SEC_E_OK ); } SECURITY_STATUS SEC_ENTRY PctInitializeSecurityContextW( PCredHandle phCredential, // Cred to base context PCtxtHandle phContext, // Existing context (OPT) SEC_WCHAR SEC_FAR * pszTargetName, // Name of target unsigned long fContextReq, // Context Requirements unsigned long Reserved1, // Reserved, MBZ unsigned long TargetDataRep, // Data rep of target PSecBufferDesc pInput, // Input Buffers unsigned long Reserved2, // Reserved, MBZ PCtxtHandle phNewContext, // (out) New Context handle PSecBufferDesc pOutput, // (inout) Output Buffers unsigned long SEC_FAR * pfContextAttr, // (out) Context attrs PTimeStamp ptsExpiry // (out) Life span (OPT) ) { PCHAR pszAnsiTarget; DWORD cchTarget; SECURITY_STATUS scRet; if (pszTargetName) { cchTarget = wcslen(pszTargetName) + 1; pszAnsiTarget = LocalAlloc(LMEM_FIXED, cchTarget * 2); if (!pszAnsiTarget) { return(SEC_E_INSUFFICIENT_MEMORY); } WideCharToMultiByte(CP_ACP, 0, pszTargetName, cchTarget, pszAnsiTarget, cchTarget * 2, NULL, NULL ); } else { pszAnsiTarget = NULL; } scRet = PctInitializeSecurityContextA(phCredential, phContext, pszAnsiTarget, fContextReq, Reserved1, TargetDataRep, pInput, Reserved2, phNewContext, pOutput, pfContextAttr, ptsExpiry ); if (pszAnsiTarget) { LocalFree(pszAnsiTarget); } } SECURITY_STATUS SEC_ENTRY PctInitializeSecurityContextA( PCredHandle phCredential, // Cred to base context PCtxtHandle phContext, // Existing context (OPT) SEC_CHAR SEC_FAR * pszTargetName, // Name of target unsigned long fContextReq, // Context Requirements unsigned long Reserved1, // Reserved, MBZ unsigned long TargetDataRep, // Data rep of target PSecBufferDesc pInput, // Input Buffers unsigned long Reserved2, // Reserved, MBZ PCtxtHandle phNewContext, // (out) New Context handle PSecBufferDesc pOutput, // (inout) Output Buffers unsigned long SEC_FAR * pfContextAttr, // (out) Context attrs PTimeStamp ptsExpiry // (out) Life span (OPT) ) { PPctContext pContext; PPctAuthContext pAuth; PPctCredential pCred; Client_Hello HelloMessage; PSecBuffer pBuffer; BOOL fPack; SessCacheItem CachedSession; if (Reserved2) { return(SEC_E_UNSUPPORTED_FUNCTION); } pContext = PctpValidateContextHandle(phContext); if (pContext) { if (pContext->Flags & CONTEXT_FLAG_ERRMODE) return pContext->Error; if (pContext->Flags & CONTEXT_FLAG_KEY) { return PctHandleServerVerify( pContext, pInput, phNewContext, pOutput, pfContextAttr, ptsExpiry); } return PctHandleServerHello( pContext, pInput, phNewContext, pOutput, pfContextAttr, ptsExpiry); } pCred = PctpValidateCredentialHandle(phCredential); if (!pCred) { return(SEC_E_INVALID_HANDLE); } if ((!pOutput) || (pOutput->cBuffers < 1)) { return(SEC_E_INVALID_TOKEN); } fContextReq &= VALID_REQUEST_FLAGS; pBuffer = pOutput->pBuffers; if (fContextReq & ISC_REQ_ALLOCATE_MEMORY) { pBuffer->pvBuffer = NULL; pBuffer->cbBuffer = 0; *pfContextAttr = ISC_RET_ALLOCATED_MEMORY; } pContext = PctCreateContext(); if (!pContext) { return(SEC_E_INSUFFICIENT_MEMORY); } if (pszTargetName) { pContext->pszName = CopyString(pszTargetName); } pContext->ContextAttr = fContextReq; pAuth = pContext->pAuthData; pAuth->pCred = pCred; pContext->Flags = CONTEXT_FLAG_OUTBOUND; PctReferenceCredential(pCred); GenerateRandomBits( pAuth->Challenge, PCT_CHALLENGE_SIZE ); // Build the hello message. HelloMessage.pKeyArg = NULL; HelloMessage.cbKeyArgSize = 0; HelloMessage.pCipherSpecs = PctCipherRank; HelloMessage.cCipherSpecs = PctNumAvailableCiphers; HelloMessage.pHashSpecs = PctHashRank; HelloMessage.cHashSpecs = PctNumAvailableHashes; HelloMessage.pCertSpecs = PctAvailableCerts; HelloMessage.cCertSpecs = PctNumAvailableCerts; HelloMessage.pExchSpecs = PctAvailableExch; HelloMessage.cExchSpecs = PctNumAvailableExch; // check if we can find the target in the cache. // (defeat the cache if reserved1 is set to PCT_TEST_NOCACHE) if ((!(Reserved1 & PCT_TEST_NOCACHE)) && (PctFindTargetInCache(pContext->pszName, &CachedSession))) { #if DBG DebugLog((DEB_TRACE, "Attempting to restart session.\n")); #endif HelloMessage.SessionId = CachedSession.Session; pAuth->ZombieSession = CachedSession; pAuth->ZombieJuju = PCT_LIVE_ZOMBIE; } else { pAuth->ZombieJuju = PCT_DEAD_ZOMBIE; RtlZeroMemory(HelloMessage.SessionId.bSessionId, PCT_SESSION_ID_SIZE); } RtlCopyMemory( HelloMessage.Challenge.bChallenge, pAuth->Challenge, PCT_CHALLENGE_SIZE ); fPack = PackClientHello(&HelloMessage, (PPct_Client_Hello *) &pBuffer->pvBuffer, &pBuffer->cbBuffer); pBuffer->BufferType = SECBUFFER_TOKEN; if (fPack) { // we need to keep the actual message around, since we need to // hash it later, and we don't know the hash algorithm to use // yet. if ((pAuth->pClHello = LocalAlloc(LMEM_FIXED, pBuffer->cbBuffer)) == NULL) { return (SEC_E_INSUFFICIENT_MEMORY); } CopyMemory(pAuth->pClHello, pBuffer->pvBuffer, pBuffer->cbBuffer); pAuth->cbClHello = pBuffer->cbBuffer; phNewContext->dwUpper = (DWORD) pContext; return(SEC_I_CONTINUE_NEEDED); } PctDeleteContext( pContext ); return(SEC_E_INSUFFICIENT_MEMORY); } SECURITY_STATUS SEC_ENTRY PctHandleClientMasterKey( PPctContext pContext, PSecBufferDesc pInput, PCtxtHandle phNewContext, PSecBufferDesc pOutput, unsigned long SEC_FAR * pfContextAttr, PTimeStamp ptsExpiry ) { SECURITY_STATUS scRet; PPctAuthContext pAuth; PClient_Master_Key pMasterKey; PctError XmitError; PCryptoSystem pSystem; DWORD i, dwKeyLen, dwError; DWORD EncryptedLen; PCheckSumBuffer pSum, pSubSum; HashBuf SumBuf, SubSumBuf; BOOL fPack; PSecBuffer pBuffer; CtxtHandle hContext; SecBuffer Buffers[3]; SecBufferDesc Desc; DWORD HeaderSize; Server_Verify Verify; SessCacheItem CachedSession; if ((!pOutput) || (pOutput->cBuffers < 1) || (pInput->pBuffers[0].cbBuffer < sizeof(Pct_Record_Header))) { return(SEC_E_INVALID_TOKEN); } // make sure we have the needed authentication data area if (pContext->pAuthData == NULL) { return (SEC_E_INVALID_TOKEN); } pAuth = pContext->pAuthData; pBuffer = pOutput->pBuffers; if (pContext->ContextAttr & ISC_REQ_ALLOCATE_MEMORY) { pBuffer->pvBuffer = NULL; pBuffer->cbBuffer = 0; *pfContextAttr = ISC_RET_ALLOCATED_MEMORY; } if (!UnpackClientMasterKey(&dwError, (PPct_Client_Master_Key) pInput->pBuffers[0].pvBuffer, pInput->pBuffers[0].cbBuffer, &pMasterKey)) { if (dwError) { // process client error here.... } return (SEC_E_INVALID_TOKEN); } // // Validate Client Response // EncryptedLen = MASTER_KEY_SIZE; if ((!PkcsPrivateDecrypt(pMasterKey->EncryptedKey, pMasterKey->EncryptedKeyLen, pAuth->pCred->pPrivateKey, NETWORK_ORDER, pAuth->MasterKey, &EncryptedLen )) || (EncryptedLen != MASTER_KEY_SIZE)) { XmitError.Error = PCT_ERR_ILLEGAL_MESSAGE; XmitError.ErrInfoLen = 0; fPack = PackPctError(&XmitError, (PPct_Error *) &pBuffer->pvBuffer, &pBuffer->cbBuffer); // clean up. PctExternalFree(pMasterKey); if (!fPack) { return (SEC_E_INVALID_TOKEN); } else { pContext->Flags |= CONTEXT_FLAG_ERRMODE; pContext->Error = SEC_E_INVALID_TOKEN; return (SEC_I_CONTINUE_NEEDED); } } dwKeyLen = ((pAuth->CipherType & PCT_CIPHER_STRENGTH) >> PCT_CSTR_POS) / 8; if ((dwKeyLen == MASTER_KEY_SIZE) && (pMasterKey->ClearKeyLen)) { XmitError.Error = PCT_ERR_ILLEGAL_MESSAGE; XmitError.ErrInfoLen = 0; fPack = PackPctError(&XmitError, (PPct_Error *) &pBuffer->pvBuffer, &pBuffer->cbBuffer); PctExternalFree(pMasterKey); if (!fPack) { return (SEC_E_INVALID_TOKEN); } else { pContext->Flags |= CONTEXT_FLAG_ERRMODE; pContext->Error = SEC_E_INVALID_TOKEN; return (SEC_I_CONTINUE_NEEDED); } } pAuth->dwClearLen = pMasterKey->ClearKeyLen; memcpy(pAuth->ClearKey, pMasterKey->ClearKey, pAuth->dwClearLen); PctMakeSessionKeys( pContext, pMasterKey->ClearKey, pMasterKey->ClearKeyLen ); // don't need master key info anymore PctExternalFree(pMasterKey); pContext->WriteCounter = 2; pContext->ReadCounter = 2; GenerateRandomBits( pAuth->SessionId.bSessionId, PCT_SESSION_ID_SIZE ); CopyMemory( Verify.SessionIdData, pAuth->SessionId.bSessionId, PCT_SESSION_ID_SIZE); // compute the response CloneHashBuf(SumBuf, pContext->WriteMACState, pContext->pCheck); InitHashBuf(SubSumBuf, pContext); pSum = (PCheckSumBuffer)SumBuf; pSubSum = (PCheckSumBuffer)SubSumBuf; pContext->pCheck->Sum(pSubSum, PCT_CONST_RESP_LEN, PCT_CONST_RESP); pContext->pCheck->Sum(pSubSum, PCT_CHALLENGE_SIZE, pAuth->Challenge); pContext->pCheck->Sum(pSubSum, PCT_SESSION_ID_SIZE, pAuth->ConnectionId.bSessionId); pContext->pCheck->Sum(pSubSum, PCT_SESSION_ID_SIZE, pAuth->SessionId.bSessionId); // we don't need the challenge anymore, so finish the compare hash // out into the challenge data memory. pContext->pCheck->Finalize(pSubSum, pAuth->Challenge); pContext->pCheck->Sum(pSum, pContext->pCheck->cbCheckSum, pAuth->Challenge); pContext->pCheck->Finalize(pSum, pAuth->Challenge); CopyMemory( Verify.Response, pAuth->Challenge, pContext->pCheck->cbCheckSum ); Verify.ResponseLen = pContext->pCheck->cbCheckSum; fPack = PackServerVerify(&Verify, (PPct_Server_Verify *) &pBuffer->pvBuffer, &pBuffer->cbBuffer); pBuffer->BufferType = SECBUFFER_TOKEN; if (fPack) { // let's add this to the cache. // try to allocate the clear key data, if needed if ((pAuth->CipherType & PCT_CIPHER_STRENGTH) < PCT_ENC_BITS_128) { if ((CachedSession.ClearData = LocalAlloc(LMEM_FIXED, sizeof(ExportKeyInfo))) == NULL) { pContext->pAuthData = NULL; PctDeleteAuthContext(pAuth); // can't cache, but continue anyway. return (SEC_E_OK); } memcpy(CachedSession.ClearData->ClearKey, pAuth->ClearKey, pAuth->dwClearLen); CachedSession.ClearData->dwClearLen = pAuth->dwClearLen; } else CachedSession.ClearData = NULL; CachedSession.Session = pAuth->SessionId; CachedSession.SessCiphSpec = pAuth->CipherType; CachedSession.SessHashSpec = pAuth->HashType; CachedSession.SessExchSpec = pAuth->ExchType; CachedSession.SessCertLen = pAuth->CertLen; CachedSession.TargetName = NULL; memcpy(CachedSession.CertData, pAuth->pRawCert, pAuth->CertLen); memcpy(CachedSession.MasterKey, pAuth->MasterKey, MASTER_KEY_SIZE); PctAddToCache(&CachedSession); pContext->pAuthData = NULL; PctDeleteAuthContext(pAuth); return(SEC_E_OK); } PctDeleteContext( pContext ); return(SEC_E_INSUFFICIENT_MEMORY); } SECURITY_STATUS SEC_ENTRY PctAcceptSecurityContext( PCredHandle phCredential, // Cred to base context PCtxtHandle phContext, // Existing context (OPT) PSecBufferDesc pInput, // Input buffer unsigned long fContextReq, // Context Requirements unsigned long TargetDataRep, // Target Data Rep PCtxtHandle phNewContext, // (out) New context handle PSecBufferDesc pOutput, // (inout) Output buffers unsigned long SEC_FAR * pfContextAttr, // (out) Context attributes PTimeStamp ptsExpiry // (out) Life span (OPT) ) { PPctContext pContext; PPctAuthContext pAuth; PPctCredential pCred; PSecBuffer pBuffer; PctError XmitError; ExportKeyInfo *pExportKey; Server_Hello Reply; PClient_Hello pHello; BOOL fPack, fRealSessId; PCheckSumBuffer pSum, pSubSum; HashBuf SumBuf, SubSumBuf; SessCacheItem CachedSession; DWORD i, j, ExchOK, fMismatch, ErrorDetail; BYTE MisData[PCT_NUM_MISMATCHES]; #if DBG DWORD di; #endif pContext = PctpValidateContextHandle(phContext); if (pContext) { if (pContext->Flags & CONTEXT_FLAG_ERRMODE) { PctDeleteContext(pContext); return pContext->Error; } if (pContext->Flags & CONTEXT_FLAG_HELLO) { return PctHandleClientMasterKey(pContext, pInput, phNewContext, pOutput, pfContextAttr, ptsExpiry ); } return( SEC_E_UNSUPPORTED_FUNCTION ); } pCred = PctpValidateCredentialHandle(phCredential); if (!pCred) { return( SEC_E_INVALID_HANDLE ); } if ((!pOutput || !pOutput) || (pOutput->cBuffers < 1) || (pInput->cBuffers < 1)) { return( SEC_E_INVALID_TOKEN ); } fContextReq &= VALID_REQUEST_FLAGS; pBuffer = &pInput->pBuffers[0]; // BUGBUG: Find DATA if (!UnpackClientHello( TRUE, &ErrorDetail, (PPct_Client_Hello) pBuffer->pvBuffer, pBuffer->cbBuffer, &pHello ) ) { if (ErrorDetail == PCT_ERR_SSL_STYLE_MSG) return (SEC_E_INVALID_TOKEN); XmitError.Error = PCT_ERR_ILLEGAL_MESSAGE; XmitError.ErrInfoLen = 0; // build a fake context... pBuffer = pOutput->pBuffers; if (fContextReq & ISC_REQ_ALLOCATE_MEMORY) { pBuffer->pvBuffer = NULL; pBuffer->cbBuffer = 0; *pfContextAttr = ISC_RET_ALLOCATED_MEMORY; } pContext = PctCreateContext(); if (!pContext) { return(SEC_E_INSUFFICIENT_MEMORY); } pAuth = pContext->pAuthData; pContext->ContextAttr = fContextReq; PctReferenceCredential(pCred); pAuth->pCred = pCred; pContext->Flags = 0; phNewContext->dwUpper = (DWORD) pContext; // now build the error. fPack = PackPctError(&XmitError, (PPct_Error *) &pBuffer->pvBuffer, &pBuffer->cbBuffer); if (!fPack) { return (SEC_E_INVALID_TOKEN); } else { pContext->Flags |= CONTEXT_FLAG_ERRMODE; pContext->Error = SEC_E_INVALID_TOKEN; return (SEC_I_CONTINUE_NEEDED); } return( SEC_E_INVALID_TOKEN ); } #if DBG DebugLog((DEB_TRACE, "Client Hello at %x\n", pHello)); DebugLog((DEB_TRACE, " CipherSpecs %d\n", pHello->cCipherSpecs)); for (di = 0 ; di < pHello->cCipherSpecs ; di++ ) { DebugLog((DEB_TRACE, " Cipher[%di] = %06x (%s)\n", di, pHello->pCipherSpecs[di], DbgGetNameOfCrypto(pHello->pCipherSpecs[di]) )); } #endif // // Build server side context: // pBuffer = pOutput->pBuffers; if (fContextReq & ISC_REQ_ALLOCATE_MEMORY) { pBuffer->pvBuffer = NULL; pBuffer->cbBuffer = 0; *pfContextAttr = ISC_RET_ALLOCATED_MEMORY; } pContext = PctCreateContext(); if (!pContext) { return(SEC_E_INSUFFICIENT_MEMORY); } pAuth = pContext->pAuthData; pContext->ContextAttr = fContextReq; PctReferenceCredential(pCred); pAuth->pCred = pCred; pContext->Flags = 0; CopyMemory(pAuth->pRawCert, pCred->pCertificate, pCred->cbCertificate); pAuth->CertLen = pCred->cbCertificate; CopyMemory( pAuth->Challenge, pHello->Challenge.bChallenge, PCT_CHALLENGE_SIZE ); pAuth->SessionId = pHello->SessionId; ZeroMemory( &Reply, sizeof( Reply ) ); // no matter what, we need to make a new connection id GenerateRandomBits( Reply.Connection.bSessionId, PCT_SESSION_ID_SIZE ); pAuth->ConnectionId = Reply.Connection; // check if the client wants a restart. fRealSessId = FALSE; for(i=0;iSessionId.bSessionId[i]) { fRealSessId = TRUE; break; } if ((fRealSessId) && (PctFindSessIdInCache(&(pHello->SessionId), &CachedSession))) { Reply.RestartOk = TRUE; pAuth->CipherType = Reply.SrvCipherSpec = CachedSession.SessCiphSpec; pAuth->HashType = Reply.SrvHashSpec = CachedSession.SessHashSpec; pAuth->ExchType = Reply.SrvExchSpec = CachedSession.SessExchSpec; pAuth->CertLen = CachedSession.SessCertLen; memcpy(pAuth->pRawCert, CachedSession.CertData, pAuth->CertLen); // setup the context memcpy(pAuth->MasterKey, CachedSession.MasterKey, MASTER_KEY_SIZE); memcpy(pAuth->Challenge, pHello->Challenge.bChallenge, PCT_CHALLENGE_SIZE); // now, we don't need the message anymore PctExternalFree(pHello); // BUGBUG - handle exchange selection pContext->pSystem = NULL; for(i=0;iCipherType) pContext->pSystem = PctAvailableCiphers[i].System; if (pContext->pSystem == NULL) return (SEC_E_INVALID_TOKEN); pContext->pCheck = NULL; for(i=0;iHashType) { pContext->pCheck = PctAvailableHashes[i].System; pContext->pCheck->Initialize(0, &pContext->InitMACState); } if (pContext->pCheck == NULL) { return (SEC_E_INVALID_TOKEN); } if ((pAuth->CipherType & PCT_CIPHER_STRENGTH) < PCT_ENC_BITS_128) { pExportKey = CachedSession.ClearData; PctMakeSessionKeys(pContext, pExportKey->ClearKey, pExportKey->dwClearLen); } else { PctMakeSessionKeys(pContext, NULL, 0); } // calculate the response CloneHashBuf(SumBuf, pContext->WriteMACState, pContext->pCheck); InitHashBuf(SubSumBuf, pContext); pSum = (PCheckSumBuffer)SumBuf; pSubSum = (PCheckSumBuffer)SubSumBuf; pContext->pCheck->Sum(pSubSum, PCT_CONST_RESP_LEN, PCT_CONST_RESP); pContext->pCheck->Sum(pSubSum, PCT_CHALLENGE_SIZE, pAuth->Challenge); pContext->pCheck->Sum(pSubSum, PCT_SESSION_ID_SIZE, pAuth->ConnectionId.bSessionId); pContext->pCheck->Sum(pSubSum, PCT_SESSION_ID_SIZE, pAuth->SessionId.bSessionId); pContext->pCheck->Finalize(pSubSum, Reply.Response); pContext->pCheck->Sum(pSum, pContext->pCheck->cbCheckSum, Reply.Response); pContext->pCheck->Finalize(pSum, Reply.Response); Reply.ResponseLen = pContext->pCheck->cbCheckSum; pContext->ReadCounter = 1; pContext->WriteCounter = 1; // delete the unneeded authentication data pContext->pAuthData = NULL; PctDeleteAuthContext(pAuth); } else { // no restart case Reply.RestartOk = FALSE; Reply.SrvCertSpec = PCT_CERT_X509; Reply.CertificateLen = pCred->cbCertificate; Reply.pCertificate = pCred->pCertificate; fMismatch = 0; pContext->pSystem = NULL; for(j=0;jcCipherSpecs;j++) { for(i=0;ipCipherSpecs[j] == PctAvailableCiphers[i].Spec) { pAuth->CipherType = PctAvailableCiphers[i].Spec; Reply.SrvCipherSpec = PctAvailableCiphers[i].Spec; pContext->pSystem = PctAvailableCiphers[i].System; break; } if (pContext->pSystem != NULL) break; } if (pContext->pSystem == NULL) fMismatch |= PCT_IMIS_CIPHER; pContext->pCheck = NULL; for(j=0;jcHashSpecs;j++) { for(i=0;ipHashSpecs[j] == PctAvailableHashes[i].Spec) { pAuth->HashType = PctAvailableHashes[i].Spec; Reply.SrvHashSpec = PctAvailableHashes[i].Spec; pContext->pCheck = PctAvailableHashes[i].System; pContext->pCheck->Initialize(0, &pContext->InitMACState); break; } if (pContext->pCheck != NULL) break; } if (pContext->pCheck == NULL) fMismatch |= PCT_IMIS_HASH; ExchOK = 0; for(j=0;jcExchSpecs;j++) { for(i=0;ipExchSpecs[j] == PctAvailableExch[i]) { Reply.SrvExchSpec = PctAvailableExch[i]; ExchOK = 1; break; } if (ExchOK) break; } if (ExchOK == 0) fMismatch |= PCT_IMIS_EXCH; else pAuth->ExchType = Reply.SrvExchSpec = pHello->pExchSpecs[0]; Reply.SrvCertSpec = pHello->pCertSpecs[0]; if (fMismatch) { #if DBG DebugLog((DEB_TRACE, "Server sending spec mismatch\n")); #endif for(i=0;i> 1; } XmitError.Error = PCT_ERR_SPECS_MISMATCH; XmitError.ErrInfoLen = PCT_NUM_MISMATCHES; XmitError.ErrInfo = MisData; fPack = PackPctError(&XmitError, (PPct_Error *) &pBuffer->pvBuffer, &pBuffer->cbBuffer); PctExternalFree(pHello); pBuffer->BufferType = SECBUFFER_TOKEN; if (!fPack) { return (SEC_E_INVALID_TOKEN); } else { phNewContext->dwUpper = (DWORD) pContext; pContext->Flags |= CONTEXT_FLAG_ERRMODE; pContext->Error = SEC_E_INVALID_TOKEN; return (SEC_I_CONTINUE_NEEDED); } return( SEC_E_INVALID_TOKEN ); } #if DBG DebugLog((DEB_TRACE, "Server picks cipher %06x (%s)\n", Reply.SrvCipherSpec, DbgGetNameOfCrypto(Reply.SrvCipherSpec) )); #endif PctExternalFree(pHello); Reply.ResponseLen = 0; } fPack = PackServerHello(&Reply, (PPct_Server_Hello *) &pBuffer->pvBuffer, &pBuffer->cbBuffer); pBuffer->BufferType = SECBUFFER_TOKEN; if (fPack) { phNewContext->dwUpper = (DWORD) pContext; pContext->Flags |= CONTEXT_FLAG_HELLO; if (Reply.ResponseLen == 0) return(SEC_I_CONTINUE_NEEDED); else return(SEC_E_OK); } PctDeleteContext( pContext ); return(SEC_E_INSUFFICIENT_MEMORY); } SECURITY_STATUS SEC_ENTRY PctDeleteSecurityContext( PCtxtHandle phContext // Context to delete ) { PPctContext pContext; pContext = PctpValidateContextHandle(phContext); if (!pContext) { return( SEC_E_INVALID_HANDLE ); } PctDeleteContext( pContext ); } SECURITY_STATUS SEC_ENTRY PctQueryContextAttributesA( PCtxtHandle phContext, // Context to query unsigned long ulAttribute, // Attribute to query void SEC_FAR * pBuffer // Buffer for attributes ) { PPctContext pContext; PSecPkgContext_Sizes pSizes; PSecPkgContext_StreamSizes pStrSizes; pContext = PctpValidateContextHandle( phContext ); if ( !pContext ) { return( SEC_E_INVALID_HANDLE ); } switch ( ulAttribute ) { case SECPKG_ATTR_SIZES: if (!pContext->pSystem) { return( SEC_E_INVALID_HANDLE ); } pSizes = (PSecPkgContext_Sizes) pBuffer; pSizes->cbMaxToken = 3 * 1024; pSizes->cbMaxSignature = (pContext->Flags & CONTEXT_FLAG_BLOCK) ? sizeof( Pct_Record_Header_Ex ) : sizeof( Pct_Record_Header ); pSizes->cbBlockSize = pContext->pSystem->BlockSize; pSizes->cbSecurityTrailer = pSizes->cbMaxSignature; return( SEC_E_OK ); case SECPKG_ATTR_STREAM_SIZES: if (pContext->pSystem) { return SEC_E_INVALID_HANDLE; } pStrSizes = (PSecPkgContext_StreamSizes)pBuffer; pStrSizes->cbHeader = 3; pStrSizes->cbTrailer = pContext->pCheck->cbCheckSum; pStrSizes->cbMaximumMessage = 32768; pStrSizes->cBuffers = 4; return( SEC_E_OK ); case SECPKG_ATTR_NAMES: case SECPKG_ATTR_LIFESPAN: case SECPKG_ATTR_DCE_INFO: return( SEC_E_UNSUPPORTED_FUNCTION ); default: return( SEC_E_INVALID_TOKEN ); } return( SEC_E_INVALID_TOKEN ); } SECURITY_STATUS SEC_ENTRY PctQueryContextAttributesW( PCtxtHandle phContext, // Context to query unsigned long ulAttribute, // Attribute to query void SEC_FAR * pBuffer // Buffer for attributes ) { return( PctQueryContextAttributesA( phContext, ulAttribute, pBuffer ) ); } SECURITY_STATUS SEC_ENTRY PctImpersonateSecurityContext( PCtxtHandle phContext // Context to impersonate ) { PPctContext pContext; pContext = PctpValidateContextHandle( phContext ); if ( pContext ) { return( SEC_E_NO_IMPERSONATION ); } return( SEC_E_INVALID_HANDLE ); } SECURITY_STATUS SEC_ENTRY PctRevertSecurityContext( PCtxtHandle phContext // Context from which to re ) { PPctContext pContext; pContext = PctpValidateContextHandle( phContext ); if ( pContext ) { return( SEC_E_NO_IMPERSONATION ); } return( SEC_E_INVALID_HANDLE ); }