/* * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved. * * The contents of this file constitute Original Code as defined in and are * subject to the Apple Public Source License Version 1.2 (the 'License'). * You may not use this file except in compliance with the License. Please obtain * a copy of the License at http://www.apple.com/publicsource and read it before * using this file. * * This Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the * specific language governing rights and limitations under the License. */ /* File: ssl2Protocol.cpp Contains: Protocol engine for SSL 2 Written by: Doug Mitchell Copyright: (c) 1999 by Apple Computer, Inc., all rights reserved. */ #include "ssl.h" #include "ssl2.h" #include "sslRecord.h" #include "sslMemory.h" #include "sslContext.h" #include "sslHandshake.h" #include "sslSession.h" #include "sslAlertMessage.h" #include "sslDebug.h" #include "appleCdsa.h" #include "sslUtils.h" #include "sslDigests.h" #include #include #ifndef NDEBUG static char *sslHdskMsgToStr(SSL2MessageType msg) { static char badStr[100]; switch(msg) { case SSL2_MsgError: return "SSL2_MsgError"; case SSL2_MsgClientHello: return "SSL2_MsgClientHello"; case SSL2_MsgClientMasterKey: return "SSL2_MsgClientMasterKey"; case SSL2_MsgClientFinished: return "SSL2_MsgClientFinished"; case SSL2_MsgServerHello: return "SSL2_MsgServerHello"; case SSL2_MsgServerVerify: return "SSL2_MsgServerVerify"; case SSL2_MsgServerFinished: return "SSL2_MsgServerFinished"; case SSL2_MsgRequestCert: return "SSL2_MsgRequestCert"; case SSL2_MsgClientCert: return "SSL2_MsgClientCert"; case SSL2_MsgKickstart: return "SSL2_MsgKickstart"; default: sprintf(badStr, "Unknown msg (%d(d)", msg); return badStr; } } static void logSsl2Msg(SSL2MessageType msg, char sent) { char *ms = sslHdskMsgToStr(msg); sslHdskMsgDebug("...msg %s: %s", (sent ? "sent" : "recd"), ms); } #else #define logSsl2Msg(m, s) #endif /* NDEBUG */ OSStatus SSL2ProcessMessage(SSLRecord &rec, SSLContext *ctx) { OSStatus err = 0; SSL2MessageType msg; SSLBuffer contents; if (rec.contents.length < 2) return errSSLProtocol; msg = (SSL2MessageType)rec.contents.data[0]; contents.data = rec.contents.data + 1; contents.length = rec.contents.length - 1; logSsl2Msg(msg, 0); switch (msg) { case SSL2_MsgError: err = errSSLClosedAbort; break; case SSL2_MsgClientHello: if (ctx->state != SSL_HdskStateServerUninit) return errSSLProtocol; err = SSL2ProcessClientHello(contents, ctx); if (err == errSSLNegotiation) SSL2SendError(SSL2_ErrNoCipher, ctx); break; case SSL2_MsgClientMasterKey: if (ctx->state != SSL2_HdskStateClientMasterKey) return errSSLProtocol; err = SSL2ProcessClientMasterKey(contents, ctx); break; case SSL2_MsgClientFinished: if (ctx->state != SSL2_HdskStateClientFinished) return errSSLProtocol; err = SSL2ProcessClientFinished(contents, ctx); break; case SSL2_MsgServerHello: if (ctx->state != SSL2_HdskStateServerHello && ctx->state != SSL_HdskStateServerHelloUnknownVersion) return errSSLProtocol; err = SSL2ProcessServerHello(contents, ctx); if (err == errSSLNegotiation) SSL2SendError(SSL2_ErrNoCipher, ctx); break; case SSL2_MsgServerVerify: if (ctx->state != SSL2_HdskStateServerVerify) return errSSLProtocol; err = SSL2ProcessServerVerify(contents, ctx); break; case SSL2_MsgServerFinished: if (ctx->state != SSL2_HdskStateServerFinished) { /* FIXME - this ifndef should not be necessary */ #ifndef NDEBUG sslHdskStateDebug("SSL2_MsgServerFinished; state %s", hdskStateToStr(ctx->state)); #endif return errSSLProtocol; } err = SSL2ProcessServerFinished(contents, ctx); break; case SSL2_MsgRequestCert: /* Don't process the request; we don't support client certification */ break; case SSL2_MsgClientCert: return errSSLProtocol; break; default: return errSSLProtocol; break; } if (err == 0) { if ((msg == SSL2_MsgClientHello) && (ctx->negProtocolVersion >= SSL_Version_3_0)) { /* Promote this message to SSL 3 protocol */ if ((err = SSL3ReceiveSSL2ClientHello(rec, ctx)) != 0) return err; } else err = SSL2AdvanceHandshake(msg, ctx); } return err; } OSStatus SSL2AdvanceHandshake(SSL2MessageType msg, SSLContext *ctx) { OSStatus err; err = noErr; switch (msg) { case SSL2_MsgKickstart: assert(ctx->negProtocolVersion == SSL_Version_Undetermined); assert(ctx->versionSsl2Enable); if (ctx->versionSsl3Enable || ctx->versionTls1Enable) { /* prepare for possible v3 upgrade */ if ((err = SSLInitMessageHashes(ctx)) != 0) return err; } if ((err = SSL2PrepareAndQueueMessage(SSL2EncodeClientHello, ctx)) != 0) return err; if (ctx->versionSsl3Enable || ctx->versionTls1Enable) { SSLChangeHdskState(ctx, SSL_HdskStateServerHelloUnknownVersion); } else { /* v2 only */ SSLChangeHdskState(ctx, SSL2_HdskStateServerHello); } break; case SSL2_MsgClientHello: if ((err = SSL2CompareSessionIDs(ctx)) != 0) return err; if (ctx->sessionMatch == 0) if ((err = SSL2GenerateSessionID(ctx)) != 0) return err; if ((err = SSL2PrepareAndQueueMessage(SSL2EncodeServerHello, ctx)) != 0) return err; if (ctx->sessionMatch == 0) { SSLChangeHdskState(ctx, SSL2_HdskStateClientMasterKey); break; } sslLogResumSessDebug("===RESUMING SSL2 server-side session"); if ((err = SSL2InstallSessionKey(ctx)) != 0) return err; /* Fall through for matching session; lame, but true */ case SSL2_MsgClientMasterKey: if ((err = SSL2InitCiphers(ctx)) != 0) return err; if ((err = SSL2PrepareAndQueueMessage(SSL2EncodeServerVerify, ctx)) != 0) return err; if ((err = SSL2PrepareAndQueueMessage(SSL2EncodeServerFinished, ctx)) != 0) return err; SSLChangeHdskState(ctx, SSL2_HdskStateClientFinished); break; case SSL2_MsgServerHello: if (ctx->sessionMatch == 0) { if ((err = SSL2PrepareAndQueueMessage(SSL2EncodeClientMasterKey, ctx)) != 0) return err; } else { sslLogResumSessDebug("===RESUMING SSL2 client-side session"); if ((err = SSL2InstallSessionKey(ctx)) != 0) return err; } if ((err = SSL2InitCiphers(ctx)) != 0) return err; if ((err = SSL2PrepareAndQueueMessage(SSL2EncodeClientFinished, ctx)) != 0) return err; SSLChangeHdskState(ctx, SSL2_HdskStateServerVerify); break; case SSL2_MsgClientFinished: /* Handshake is complete; turn ciphers on */ ctx->writeCipher.ready = 1; ctx->readCipher.ready = 1; /* original code never got out of SSL2_MsgClientFinished state */ assert(ctx->protocolSide == SSL_ServerSide); SSLChangeHdskState(ctx, SSL_HdskStateServerReady); if (ctx->peerID.data != 0) SSLAddSessionData(ctx); break; case SSL2_MsgServerVerify: SSLChangeHdskState(ctx, SSL2_HdskStateServerFinished); break; case SSL2_MsgRequestCert: if ((err = SSL2SendError(SSL2_ErrNoCert, ctx)) != 0) return err; break; case SSL2_MsgServerFinished: /* Handshake is complete; turn ciphers on */ ctx->writeCipher.ready = 1; ctx->readCipher.ready = 1; /* original code never got out of SSL2_MsgServerFinished state */ assert(ctx->protocolSide == SSL_ClientSide); SSLChangeHdskState(ctx, SSL_HdskStateClientReady); if (ctx->peerID.data != 0) SSLAddSessionData(ctx); break; case SSL2_MsgError: case SSL2_MsgClientCert: return errSSLProtocol; break; } return noErr; } OSStatus SSL2PrepareAndQueueMessage(EncodeSSL2MessageFunc encodeFunc, SSLContext *ctx) { OSStatus err; SSLRecord rec; rec.contentType = SSL_RecordTypeV2_0; rec.protocolVersion = SSL_Version_2_0; if ((err = encodeFunc(rec.contents, ctx)) != 0) return err; logSsl2Msg((SSL2MessageType)rec.contents.data[0], 1); assert(ctx->sslTslCalls != NULL); if ((err = ctx->sslTslCalls->writeRecord(rec, ctx)) != 0) { SSLFreeBuffer(rec.contents, ctx); return err; } assert((ctx->negProtocolVersion == SSL_Version_Undetermined) || (ctx->negProtocolVersion == SSL_Version_2_0)); if((ctx->negProtocolVersion == SSL_Version_Undetermined) && (ctx->versionSsl3Enable || ctx->versionTls1Enable)) { /* prepare for possible V3/TLS1 upgrade */ if ((err = SSLHashSHA1.update(ctx->shaState, rec.contents)) != 0 || (err = SSLHashMD5.update(ctx->md5State, rec.contents)) != 0) return err; } err = SSLFreeBuffer(rec.contents, ctx); return err; } OSStatus SSL2CompareSessionIDs(SSLContext *ctx) { OSStatus err; SSLBuffer sessionIdentifier; ctx->sessionMatch = 0; if (ctx->resumableSession.data == 0) return noErr; if ((err = SSLRetrieveSessionID(ctx->resumableSession, &sessionIdentifier, ctx)) != 0) return err; if (sessionIdentifier.length == ctx->sessionID.length && memcmp(sessionIdentifier.data, ctx->sessionID.data, sessionIdentifier.length) == 0) ctx->sessionMatch = 1; if ((err = SSLFreeBuffer(sessionIdentifier, ctx)) != 0) return err; return noErr; } OSStatus SSL2InstallSessionKey(SSLContext *ctx) { OSStatus err; assert(ctx->sessionMatch != 0); assert(ctx->resumableSession.data != 0); if ((err = SSLInstallSessionFromData(ctx->resumableSession, ctx)) != 0) return err; return noErr; } OSStatus SSL2GenerateSessionID(SSLContext *ctx) { OSStatus err; if ((err = SSLFreeBuffer(ctx->sessionID, ctx)) != 0) return err; if ((err = SSLAllocBuffer(ctx->sessionID, SSL_SESSION_ID_LEN, ctx)) != 0) return err; if ((err = sslRand(ctx, &ctx->sessionID)) != 0) return err; return noErr; } OSStatus SSL2InitCiphers(SSLContext *ctx) { OSStatus err; int keyMaterialLen; SSLBuffer keyData; uint8 variantChar, *charPtr, *readKey, *writeKey, *iv; SSLBuffer hashDigest, hashContext, masterKey, challenge, connectionID, variantData; keyMaterialLen = 2 * ctx->selectedCipherSpec->cipher->keySize; if ((err = SSLAllocBuffer(keyData, keyMaterialLen, ctx)) != 0) return err; /* Can't have % in assertion string... */ #if SSL_DEBUG { UInt32 keyModDigestSize = keyMaterialLen % SSLHashMD5.digestSize; assert(keyModDigestSize == 0); } #endif masterKey.data = ctx->masterSecret; masterKey.length = ctx->selectedCipherSpec->cipher->keySize; challenge.data = ctx->clientRandom + SSL_CLIENT_SRVR_RAND_SIZE - ctx->ssl2ChallengeLength; challenge.length = ctx->ssl2ChallengeLength; connectionID.data = ctx->serverRandom; connectionID.length = ctx->ssl2ConnectionIDLength; variantData.data = &variantChar; variantData.length = 1; if ((err = SSLAllocBuffer(hashContext, SSLHashMD5.contextSize, ctx)) != 0) { SSLFreeBuffer(keyData, ctx); return err; } variantChar = 0x30; /* '0' */ charPtr = keyData.data; while (keyMaterialLen) { hashDigest.data = charPtr; hashDigest.length = SSLHashMD5.digestSize; if ((err = SSLHashMD5.init(hashContext, ctx)) != 0 || (err = SSLHashMD5.update(hashContext, masterKey)) != 0 || (err = SSLHashMD5.update(hashContext, variantData)) != 0 || (err = SSLHashMD5.update(hashContext, challenge)) != 0 || (err = SSLHashMD5.update(hashContext, connectionID)) != 0 || (err = SSLHashMD5.final(hashContext, hashDigest)) != 0) { SSLFreeBuffer(keyData, ctx); SSLFreeBuffer(hashContext, ctx); return err; } charPtr += hashDigest.length; ++variantChar; keyMaterialLen -= hashDigest.length; } assert(charPtr == keyData.data + keyData.length); if ((err = SSLFreeBuffer(hashContext, ctx)) != 0) { SSLFreeBuffer(keyData, ctx); return err; } ctx->readPending.macRef = ctx->selectedCipherSpec->macAlgorithm; ctx->writePending.macRef = ctx->selectedCipherSpec->macAlgorithm; ctx->readPending.symCipher = ctx->selectedCipherSpec->cipher; ctx->writePending.symCipher = ctx->selectedCipherSpec->cipher; ctx->readPending.sequenceNum = ctx->readCipher.sequenceNum; ctx->writePending.sequenceNum = ctx->writeCipher.sequenceNum; if (ctx->protocolSide == SSL_ServerSide) { writeKey = keyData.data; readKey = keyData.data + ctx->selectedCipherSpec->cipher->keySize; } else { readKey = keyData.data; writeKey = keyData.data + ctx->selectedCipherSpec->cipher->keySize; } iv = ctx->masterSecret + ctx->selectedCipherSpec->cipher->keySize; if ((err = ctx->readPending.symCipher->initialize(readKey, iv, &ctx->readPending, ctx)) != 0 || (err = ctx->writePending.symCipher->initialize(writeKey, iv, &ctx->writePending, ctx)) != 0) { SSLFreeBuffer(keyData, ctx); return err; } /* * HEY! macSecret is only 20 bytes. This blows up when key size * is greater than 20, e.g., 3DES. * I'll increase the size of macSecret to 24, 'cause it appears * from the SSL v23 spec that the macSecret really the same size as * CLIENT-WRITE-KEY and SERVER-READ-KEY (see 1.2 of the spec). */ memcpy(ctx->readPending.macSecret, readKey, ctx->selectedCipherSpec->cipher->keySize); memcpy(ctx->writePending.macSecret, writeKey, ctx->selectedCipherSpec->cipher->keySize); if ((err = SSLFreeBuffer(keyData, ctx)) != 0) return err; ctx->readCipher = ctx->readPending; ctx->writeCipher = ctx->writePending; memset(&ctx->readPending, 0, sizeof(CipherContext)); /* Zero out old data */ memset(&ctx->writePending, 0, sizeof(CipherContext)); /* Zero out old data */ return noErr; }