--- /dev/null
+/*
+ * 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 <string.h>
+#include <assert.h>
+
+#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)
+ {
+ /* FIXME - use requested or negotiated protocol version here? */
+ 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:
+ if (ctx->negProtocolVersion == SSL_Version_3_0_With_2_0_Hello ||
+ ctx->negProtocolVersion == SSL_Version_Undetermined)
+ if ((err = SSLInitMessageHashes(ctx)) != 0)
+ return err;
+ if ((err = SSL2PrepareAndQueueMessage(SSL2EncodeClientHello, ctx)) != 0)
+ return err;
+ switch (ctx->negProtocolVersion)
+ { case SSL_Version_Undetermined:
+ SSLChangeHdskState(ctx, SSL_HdskStateServerHelloUnknownVersion);
+ break;
+ case SSL_Version_3_0_With_2_0_Hello:
+ assert((ctx->reqProtocolVersion == SSL_Version_3_0) ||
+ (ctx->reqProtocolVersion == TLS_Version_1_0));
+ ctx->negProtocolVersion = ctx->reqProtocolVersion;
+ sslLogNegotiateDebug("===SSL client kickstart: negVersion "
+ "is %d_%d",
+ ctx->negProtocolVersion >> 8, ctx->negProtocolVersion & 0xff);
+ SSLChangeHdskState(ctx, SSL_HdskStateServerHello);
+ break;
+ case SSL_Version_2_0:
+ SSLChangeHdskState(ctx, SSL2_HdskStateServerHello);
+ break;
+ case SSL_Version_3_0_Only:
+ case SSL_Version_3_0:
+ case TLS_Version_1_0_Only:
+ case TLS_Version_1_0:
+ default:
+ assert("Bad protocol version for sending SSL 2 Client Hello");
+ break;
+ }
+ break;
+ case SSL2_MsgClientHello:
+ if ((err = SSL2CompareSessionIDs(ctx)) != 0)
+ return err;
+ if (ctx->ssl2SessionMatch == 0)
+ if ((err = SSL2GenerateSessionID(ctx)) != 0)
+ return err;
+ if ((err = SSL2PrepareAndQueueMessage(SSL2EncodeServerHello, ctx)) != 0)
+ return err;
+ if (ctx->ssl2SessionMatch == 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->ssl2SessionMatch == 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, SSL2_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, SSL2_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;
+ }
+
+ if (ctx->negProtocolVersion == SSL_Version_3_0_With_2_0_Hello ||
+ ctx->negProtocolVersion == SSL_Version_Undetermined)
+ 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->ssl2SessionMatch = 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->ssl2SessionMatch = 1;
+
+ if ((err = SSLFreeBuffer(sessionIdentifier, ctx)) != 0)
+ return err;
+
+ return noErr;
+}
+
+OSStatus
+SSL2InstallSessionKey(SSLContext *ctx)
+{ OSStatus err;
+
+ assert(ctx->ssl2SessionMatch != 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;
+}