--- /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.
+ */
+
+#include "ssl.h"
+#include "ssl2.h"
+#include "sslRecord.h"
+#include "sslMemory.h"
+#include "sslContext.h"
+#include "sslAlertMessage.h"
+#include "sslHandshake.h"
+#include "sslSession.h"
+#include "sslDebug.h"
+#include "cipherSpecs.h"
+#include "appleCdsa.h"
+#include "sslUtils.h"
+
+#include <string.h>
+#include <assert.h>
+
+OSStatus
+SSL2ProcessClientHello(SSLBuffer msg, SSLContext *ctx)
+{ OSStatus err;
+ UInt8 *charPtr, *cipherList;
+ unsigned i, j, cipherKindCount, sessionIDLen, challengeLen;
+ SSL2CipherKind cipherKind;
+ SSLCipherSuite matchingCipher, selectedCipher;
+ SSLProtocolVersion version;
+
+ if (msg.length < 27) {
+ sslErrorLog("SSL2ProcessClientHello: msg len error 1\n");
+ return errSSLProtocol;
+ }
+
+ charPtr = msg.data;
+
+ version = (SSLProtocolVersion)SSLDecodeInt(charPtr, 2);
+ if (version > ctx->maxProtocolVersion) {
+ version = ctx->maxProtocolVersion;
+ }
+ /* FIXME - I think this needs work for a SSL_Version_2_0 server, to ensure that
+ * the client isn't establishing a v3 session. */
+ if (ctx->negProtocolVersion == SSL_Version_Undetermined)
+ {
+ /* FIXME - this ifndef should not be necessary */
+ #ifndef NDEBUG
+ sslLogNegotiateDebug("===SSL2 server: negVersion was undetermined; "
+ "is %s", protocolVersStr(version));
+ #endif
+ ctx->negProtocolVersion = version;
+ if(version >= TLS_Version_1_0) {
+ ctx->sslTslCalls = &Tls1Callouts;
+ }
+ else {
+ /* default from context init */
+ assert(ctx->sslTslCalls == &Ssl3Callouts);
+ }
+ }
+ else if (ctx->negProtocolVersion == SSL_Version_3_0_With_2_0_Hello)
+ { if (version < SSL_Version_3_0) {
+ sslErrorLog("SSL2ProcessClientHello: version error\n");
+ return errSSLProtocol;
+ }
+ /* FIXME - I don't think path is ever taken - we NEVER set any
+ * protocol var to SSL_Version_3_0_With_2_0_Hello... */
+ sslLogNegotiateDebug("===SSL2 server: negVersion was "
+ "3_0_With_2_0_Hello; is 3_0");
+ ctx->negProtocolVersion = version;
+ }
+
+ charPtr += 2;
+ cipherKindCount = SSLDecodeInt(charPtr, 2);
+ charPtr += 2;
+ if (cipherKindCount % 3 != 0) {
+ sslErrorLog("SSL2ProcessClientHello: cipherKindCount error\n");
+ return errSSLProtocol;
+ }
+ cipherKindCount /= 3;
+ sessionIDLen = SSLDecodeInt(charPtr, 2);
+ charPtr += 2;
+ challengeLen = SSLDecodeInt(charPtr, 2);
+ charPtr += 2;
+
+ if (msg.length != 8 + 3*cipherKindCount + sessionIDLen + challengeLen ||
+ (sessionIDLen != 0 && sessionIDLen != 16) ||
+ challengeLen < 16 || challengeLen > 32 ) {
+ sslErrorLog("SSL2ProcessClientHello: msg len error 2\n");
+ return errSSLProtocol;
+ }
+ cipherList = charPtr;
+ selectedCipher = SSL_NO_SUCH_CIPHERSUITE;
+
+ if (ctx->negProtocolVersion >= SSL_Version_3_0) {
+ /* If we're negotiating an SSL 3.0 session, use SSL 3.0 suites first */
+ for (i = 0; i < cipherKindCount; i++) {
+ cipherKind = (SSL2CipherKind)SSLDecodeInt(charPtr, 3);
+ charPtr += 3;
+ if (selectedCipher != SSL_NO_SUCH_CIPHERSUITE)
+ continue;
+ if ((((UInt32)cipherKind) & 0xFF0000) != 0)
+ continue; /* Skip SSL 2 suites */
+ matchingCipher = (SSLCipherSuite)((UInt32)cipherKind & 0x00FFFF);
+ for (j = 0; j<ctx->numValidCipherSpecs; j++) {
+ if (ctx->validCipherSpecs[j].cipherSpec == matchingCipher) {
+ selectedCipher = matchingCipher;
+ break;
+ }
+ } /* searching thru all our valid ciphers */
+ } /* for each client cipher */
+ } /* v3 or greater */
+
+ if(selectedCipher == SSL_NO_SUCH_CIPHERSUITE) {
+ /* try again using SSL2 ciphers only */
+ charPtr = cipherList;
+ for (i = 0; i < cipherKindCount; i++) {
+ cipherKind = (SSL2CipherKind)SSLDecodeInt(charPtr, 3);
+ charPtr += 3;
+ if (selectedCipher == SSL_NO_SUCH_CIPHERSUITE) {
+ /* After we find one, just keep advancing ptr past
+ * the unused ones */
+ if ((((UInt32)cipherKind) & 0xFF0000) != 0) {
+ /* If it's a real SSL2 spec, look for it in the list */
+ matchingCipher = SSL_NO_SUCH_CIPHERSUITE;
+ for (j = 0; j < SSL2CipherMapCount; j++) {
+ if (cipherKind == SSL2CipherMap[j].cipherKind) {
+ matchingCipher = SSL2CipherMap[j].cipherSuite;
+ break;
+ }
+ }
+ } /* real 3-byte SSL2 suite */
+ else {
+ /* if the first byte is zero, it's an encoded SSL 3 CipherSuite */
+ matchingCipher = (SSLCipherSuite)((UInt32)cipherKind & 0x00FFFF);
+ /*
+ * One more restriction - if we've negotiated a v2 session,
+ * ignore this matching cipher if it's not in the SSL2 map.
+ */
+ if(ctx->negProtocolVersion < SSL_Version_3_0) {
+ int isInMap = 0;
+ for (j = 0; j < SSL2CipherMapCount; j++) {
+ if (matchingCipher == SSL2CipherMap[j].cipherSuite) {
+ isInMap = 1;
+ break;
+ }
+ }
+ if(!isInMap) {
+ /* Sorry, no can do */
+ matchingCipher = SSL_NO_SUCH_CIPHERSUITE;
+ }
+ } /* SSL2 check */
+ } /* two-byte suite */
+
+ /* now see if we are enabled for this cipher */
+ if (matchingCipher != SSL_NO_SUCH_CIPHERSUITE) {
+ for (j = 0; j < ctx->numValidCipherSpecs; j++) {
+ if (ctx->validCipherSpecs[j].cipherSpec == matchingCipher) {
+ selectedCipher = matchingCipher;
+ break;
+ }
+ }
+ }
+ } /* not ignoring this suite */
+ } /* for each suite in the hello msg */
+ } /* not found in SSL3 ciphersuites */
+
+ if (selectedCipher == SSL_NO_SUCH_CIPHERSUITE)
+ return errSSLNegotiation;
+
+ ctx->selectedCipher = selectedCipher;
+ err = FindCipherSpec(ctx);
+ if(err != 0) {
+ return err;
+ }
+ if (sessionIDLen > 0 && ctx->peerID.data != 0)
+ { /* Don't die on error; just treat it as an uncacheable session */
+ err = SSLAllocBuffer(ctx->sessionID, sessionIDLen, ctx);
+ if (err == 0)
+ memcpy(ctx->sessionID.data, charPtr, sessionIDLen);
+ }
+ charPtr += sessionIDLen;
+
+ ctx->ssl2ChallengeLength = challengeLen;
+ memset(ctx->clientRandom, 0, SSL_CLIENT_SRVR_RAND_SIZE);
+ memcpy(ctx->clientRandom + SSL_CLIENT_SRVR_RAND_SIZE - challengeLen,
+ charPtr, challengeLen);
+ charPtr += challengeLen;
+ assert(charPtr == msg.data + msg.length);
+
+ return noErr;
+}
+
+/*
+ * The SSL v2 spec says that the challenge string sent by the client can be
+ * between 16 and 32 bytes. However all Netscape enterprise servers actually
+ * require a 16 byte challenge. Q.v. cdnow.com, store.apple.com.
+ * Unfortunately this means that when we're trying to do a
+ * SSL_Version_3_0_With_2_0_Hello negotiation, we have to limit ourself to
+ * a 16-byte clientRandom, which we have to concatenate to 16 bytes of
+ * zeroes if we end up with a 3.0 or 3.1 connection. Thus we lose 16 bytes
+ * of entropy.
+ */
+#define SSL2_CHALLENGE_LEN 16
+
+OSStatus
+SSL2EncodeClientHello(SSLBuffer &msg, SSLContext *ctx)
+{ OSStatus err;
+ UInt8 *charPtr;
+ unsigned i, j;
+ int useSSL3Ciphers = 0;
+ int totalCipherCount;
+ int sessionIDLen;
+ UInt16 version;
+ SSLBuffer sessionIdentifier, randomData;
+
+ switch (ctx->negProtocolVersion)
+ { case SSL_Version_Undetermined:
+ case SSL_Version_3_0_With_2_0_Hello:
+ /* go for it, see if server can handle upgrading */
+ useSSL3Ciphers = 1;
+ /* could be SSLv3 or TLSv1 */
+ version = ctx->maxProtocolVersion;
+ break;
+ case SSL_Version_2_0:
+ useSSL3Ciphers = 0;
+ version = SSL_Version_2_0;
+ 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");
+ return errSSLInternal;
+ }
+ /* FIXME - this ifndef should not be necessary */
+ #ifndef NDEBUG
+ sslLogNegotiateDebug("===SSL client: proclaiming %s capable",
+ protocolVersStr((SSLProtocolVersion)version));
+ #endif
+
+ if (useSSL3Ciphers != 0)
+ totalCipherCount = ctx->numValidCipherSpecs;
+ else
+ totalCipherCount = 0;
+
+ for (i = 0; i < SSL2CipherMapCount; i++)
+ for (j = 0; j < ctx->numValidCipherSpecs; j++)
+ if (ctx->validCipherSpecs[j].cipherSpec == SSL2CipherMap[i].cipherSuite)
+ { totalCipherCount++;
+ break;
+ }
+
+ sessionIDLen = 0;
+ sessionIdentifier.data = 0;
+ if (ctx->resumableSession.data != 0)
+ { if ((err = SSLRetrieveSessionID(ctx->resumableSession, &sessionIdentifier, ctx)) != 0)
+ return err;
+ sessionIDLen = sessionIdentifier.length;
+ }
+
+ /* msg length = 9 + 3 * totalCipherCount + sessionIDLen + 16 bytes of challenge
+ * Use exactly 16 bytes of challenge because Netscape products have a bug
+ * that requires this length
+ */
+ if ((err = SSLAllocBuffer(msg, 9 + (3*totalCipherCount) + sessionIDLen +
+ SSL2_CHALLENGE_LEN, ctx)) != 0)
+ { SSLFreeBuffer(sessionIdentifier, ctx);
+ return err;
+ }
+
+ charPtr = msg.data;
+ *charPtr++ = SSL2_MsgClientHello;
+ charPtr = SSLEncodeInt(charPtr, version, 2);
+ charPtr = SSLEncodeInt(charPtr, 3*totalCipherCount, 2);
+ charPtr = SSLEncodeInt(charPtr, sessionIDLen, 2);
+ charPtr = SSLEncodeInt(charPtr, SSL2_CHALLENGE_LEN, 2);
+
+ /* If we can send SSL3 ciphers, encode the two-byte cipher specs into three-byte
+ * CipherKinds which have a leading 0.
+ */
+ if (useSSL3Ciphers != 0)
+ for (i = 0; i < ctx->numValidCipherSpecs; i++)
+ charPtr = SSLEncodeInt(charPtr, ctx->validCipherSpecs[i].cipherSpec, 3);
+
+ /* Now send those SSL2 specs for which we have implementations */
+ for (i = 0; i < SSL2CipherMapCount; i++)
+ for (j = 0; j < ctx->numValidCipherSpecs; j++)
+ if (ctx->validCipherSpecs[j].cipherSpec == SSL2CipherMap[i].cipherSuite)
+ { charPtr = SSLEncodeInt(charPtr, SSL2CipherMap[i].cipherKind, 3);
+ break;
+ }
+
+ if (sessionIDLen > 0)
+ { memcpy(charPtr, sessionIdentifier.data, sessionIDLen);
+ charPtr += sessionIDLen;
+ SSLFreeBuffer(sessionIdentifier, ctx);
+ }
+
+ randomData.data = charPtr;
+ randomData.length = SSL2_CHALLENGE_LEN;
+ if ((err = sslRand(ctx, &randomData)) != 0)
+ { SSLFreeBuffer(msg, ctx);
+ return err;
+ }
+ charPtr += SSL2_CHALLENGE_LEN;
+
+ /* Zero out the first 16 bytes of clientRandom, and store
+ * the challenge in the second 16 bytes */
+ #if (SSL2_CHALLENGE_LEN == SSL_CLIENT_SRVR_RAND_SIZE)
+ /* this path verified to fail with Netscape Enterprise servers 1/16/02 */
+ memcpy(ctx->clientRandom, randomData.data, SSL2_CHALLENGE_LEN);
+ #else
+ memset(ctx->clientRandom, 0, SSL_CLIENT_SRVR_RAND_SIZE - SSL2_CHALLENGE_LEN);
+ memcpy(ctx->clientRandom + SSL_CLIENT_SRVR_RAND_SIZE - SSL2_CHALLENGE_LEN,
+ randomData.data, SSL2_CHALLENGE_LEN);
+ #endif
+ ctx->ssl2ChallengeLength = SSL2_CHALLENGE_LEN;
+
+ assert(charPtr == msg.data + msg.length);
+
+ return noErr;
+}
+
+OSStatus
+SSL2ProcessClientMasterKey(SSLBuffer msg, SSLContext *ctx)
+{ OSStatus err;
+ SSL2CipherKind cipherKind;
+ SSLBuffer secretData;
+ unsigned clearLength, encryptedLength, keyArgLength;
+ UInt32 secretLength, localKeyModulusLen;
+ UInt8 *charPtr;
+ const CSSM_KEY *decryptKey;
+ CSSM_CSP_HANDLE decryptCspHand;
+
+ if (msg.length < 9) {
+ sslErrorLog("SSL2ProcessClientMasterKey: msg.length error 1\n");
+ return errSSLProtocol;
+ }
+ assert(ctx->protocolSide == SSL_ServerSide);
+
+ charPtr = msg.data;
+ cipherKind = (SSL2CipherKind)SSLDecodeInt(charPtr, 3);
+ charPtr += 3;
+ clearLength = SSLDecodeInt(charPtr, 2);
+ charPtr += 2;
+ encryptedLength = SSLDecodeInt(charPtr, 2);
+ charPtr += 2;
+ keyArgLength = SSLDecodeInt(charPtr, 2);
+ charPtr += 2;
+
+ if (msg.length != 9 + clearLength + encryptedLength + keyArgLength) {
+ sslErrorLog("SSL2ProcessClientMasterKey: msg.length error 2\n");
+ return errSSLProtocol;
+ }
+
+ /* Master key == CLEAR_DATA || SECRET_DATA */
+ memcpy(ctx->masterSecret, charPtr, clearLength);
+ charPtr += clearLength;
+
+ /*
+ * Just as in SSL2EncodeServerHello, which key we use depends on the
+ * app's config.
+ */
+ if(ctx->encryptPrivKey) {
+ decryptKey = ctx->encryptPrivKey;
+ assert(ctx->encryptKeyCsp != 0);
+ decryptCspHand = ctx->encryptKeyCsp;
+ }
+ else if(ctx->signingPrivKey) {
+ decryptKey = ctx->signingPrivKey;
+ assert(ctx->signingKeyCsp != 0);
+ decryptCspHand = ctx->signingKeyCsp;
+ }
+ else {
+ /* really should not happen... */
+ sslErrorLog("SSL2ProcessClientMasterKey: No server key!\n");
+ return badReqErr;
+ }
+ localKeyModulusLen = sslKeyLengthInBytes(decryptKey);
+
+ if (encryptedLength != localKeyModulusLen) {
+ sslErrorLog("SSL2ProcessClientMasterKey: encryptedLength error 1\n");
+ return errSSLProtocol;
+ }
+
+ /* Allocate enough room to hold any decrypted value */
+ if ((err = SSLAllocBuffer(secretData, encryptedLength, ctx)) != 0)
+ return err;
+
+ err = sslRsaDecrypt(ctx,
+ decryptKey,
+ decryptCspHand,
+ charPtr,
+ encryptedLength,
+ secretData.data,
+ encryptedLength, // same length for both...?
+ &secretLength);
+ if(err) {
+ SSLFreeBuffer(secretData, ctx);
+ return err;
+ }
+
+ charPtr += encryptedLength;
+
+ if (clearLength + secretLength != ctx->selectedCipherSpec->cipher->keySize) {
+ sslErrorLog("SSL2ProcessClientMasterKey: length error 3\n");
+ return errSSLProtocol;
+ }
+ memcpy(ctx->masterSecret + clearLength, secretData.data, secretLength);
+ if ((err = SSLFreeBuffer(secretData, ctx)) != 0)
+ return err;
+
+ if (keyArgLength != ctx->selectedCipherSpec->cipher->ivSize) {
+ sslErrorLog("SSL2ProcessClientMasterKey: length error 4\n");
+ return errSSLProtocol;
+ }
+
+ /* Stash the IV after the master key in master secret storage */
+ memcpy(ctx->masterSecret + ctx->selectedCipherSpec->cipher->keySize, charPtr, keyArgLength);
+ charPtr += keyArgLength;
+ assert(charPtr = msg.data + msg.length);
+
+ return noErr;
+}
+
+OSStatus
+SSL2EncodeClientMasterKey(SSLBuffer &msg, SSLContext *ctx)
+{ OSStatus err;
+ unsigned length, i, clearLen;
+ UInt32 outputLen, peerKeyModulusLen;
+ SSLBuffer keyData;
+ UInt8 *charPtr;
+
+ peerKeyModulusLen = sslKeyLengthInBytes(ctx->peerPubKey);
+
+ /* Length is 10 + clear key size + encrypted output size + iv size */
+ length = 10;
+ clearLen = ctx->selectedCipherSpec->cipher->keySize - ctx->selectedCipherSpec->cipher->secretKeySize;
+ length += clearLen;
+ length += peerKeyModulusLen;
+ length += ctx->selectedCipherSpec->cipher->ivSize;
+
+ if ((err = SSLAllocBuffer(msg, length, ctx)) != 0)
+ return err;
+ charPtr = msg.data;
+ *charPtr++ = SSL2_MsgClientMasterKey;
+ for (i = 0; i < SSL2CipherMapCount; i++)
+ if (ctx->selectedCipher == SSL2CipherMap[i].cipherSuite)
+ break;
+ assert(i < SSL2CipherMapCount);
+ sslLogNegotiateDebug("===SSL2EncodeClientMasterKey: sending cipherKind 0x%x",
+ SSL2CipherMap[i].cipherKind);
+ charPtr = SSLEncodeInt(charPtr, SSL2CipherMap[i].cipherKind, 3);
+ charPtr = SSLEncodeInt(charPtr, clearLen, 2);
+ charPtr = SSLEncodeInt(charPtr, peerKeyModulusLen, 2);
+ charPtr = SSLEncodeInt(charPtr, ctx->selectedCipherSpec->cipher->ivSize, 2);
+
+ /* Generate the keying material; we need enough data for the key and IV */
+ keyData.data = ctx->masterSecret;
+ keyData.length = ctx->selectedCipherSpec->cipher->keySize + ctx->selectedCipherSpec->cipher->ivSize;
+ assert(keyData.length <= 48); /* Must be able to store it in the masterSecret array */
+ if ((err = sslRand(ctx, &keyData)) != 0)
+ return err;
+
+ memcpy(charPtr, ctx->masterSecret, clearLen);
+ charPtr += clearLen;
+
+ /* Replace this with code to do encryption at lower level & set PKCS1 padding
+ for rollback attack */
+
+ /*
+ * encrypt only the secret key portion of masterSecret, starting at
+ * clearLen bytes
+ */
+ err = sslRsaEncrypt(ctx,
+ ctx->peerPubKey,
+ ctx->peerPubKeyCsp, // XX - maybe cspHand
+ ctx->masterSecret + clearLen,
+ ctx->selectedCipherSpec->cipher->keySize - clearLen,
+ charPtr,
+ peerKeyModulusLen,
+ &outputLen);
+ if(err) {
+ return err;
+ }
+
+ charPtr += outputLen;
+
+ /* copy clear IV to msg buf */
+ memcpy(charPtr, ctx->masterSecret + ctx->selectedCipherSpec->cipher->keySize,
+ ctx->selectedCipherSpec->cipher->ivSize);
+ charPtr += ctx->selectedCipherSpec->cipher->ivSize;
+
+ assert(charPtr == msg.data + msg.length);
+
+ return noErr;
+}
+
+OSStatus
+SSL2ProcessClientFinished(SSLBuffer msg, SSLContext *ctx)
+{ if (msg.length != ctx->sessionID.length) {
+ sslErrorLog("SSL2ProcessClientFinished: length error\n");
+ return errSSLProtocol;
+ }
+ if (memcmp(msg.data, ctx->serverRandom, ctx->ssl2ConnectionIDLength) != 0) {
+ sslErrorLog("SSL2ProcessClientFinished: data compare error\n");
+ return errSSLProtocol;
+ }
+ return noErr;
+}
+
+OSStatus
+SSL2EncodeClientFinished(SSLBuffer &msg, SSLContext *ctx)
+{ OSStatus err;
+
+ if ((err = SSLAllocBuffer(msg, ctx->ssl2ConnectionIDLength+1, ctx)) != 0)
+ return err;
+ msg.data[0] = SSL2_MsgClientFinished;
+ memcpy(msg.data+1, ctx->serverRandom, ctx->ssl2ConnectionIDLength);
+ return noErr;
+}
+
+OSStatus
+SSL2ProcessServerHello(SSLBuffer msg, SSLContext *ctx)
+{ OSStatus err;
+ SSL2CertTypeCode certType;
+ unsigned sessionIDMatch, certLen, cipherSpecsLen, connectionIDLen;
+ unsigned i, j;
+ SSL2CipherKind cipherKind;
+ SSLCertificate *cert;
+ SSLCipherSuite matchingCipher = 0; // avoid compiler warning
+ SSLCipherSuite selectedCipher;
+ UInt8 *charPtr;
+ SSLProtocolVersion version;
+
+ if (msg.length < 10) {
+ sslErrorLog("SSL2ProcessServerHello: length error\n");
+ return errSSLProtocol;
+ }
+ charPtr = msg.data;
+
+ sessionIDMatch = *charPtr++;
+ certType = (SSL2CertTypeCode)*charPtr++;
+ version = (SSLProtocolVersion)SSLDecodeInt(charPtr, 2);
+ charPtr += 2;
+ if (version != SSL_Version_2_0) {
+ sslErrorLog("SSL2ProcessServerHello: version error\n");
+ return errSSLProtocol;
+ }
+ ctx->negProtocolVersion = version;
+ sslLogNegotiateDebug("===SSL2 client: negVersion is 2_0");
+ certLen = SSLDecodeInt(charPtr, 2);
+ charPtr += 2;
+ cipherSpecsLen = SSLDecodeInt(charPtr, 2);
+ charPtr += 2;
+ connectionIDLen = SSLDecodeInt(charPtr, 2);
+ charPtr += 2;
+
+ if (connectionIDLen < 16 || connectionIDLen > 32 || cipherSpecsLen % 3 != 0 ||
+ (msg.length != 10 + certLen + cipherSpecsLen + connectionIDLen) )
+ return errSSLProtocol;
+ if (sessionIDMatch != 0)
+ { if (certLen != 0 || cipherSpecsLen != 0 /* || certType != 0 */ )
+ return errSSLProtocol;
+ ctx->ssl2SessionMatch = 1;
+
+ ctx->ssl2ConnectionIDLength = connectionIDLen;
+ memcpy(ctx->serverRandom, charPtr, connectionIDLen);
+ charPtr += connectionIDLen;
+ }
+ else
+ { if (certType != SSL2_CertTypeX509)
+ return errSSLNegotiation;
+ cipherSpecsLen /= 3;
+
+ cert = (SSLCertificate *)sslMalloc(sizeof(SSLCertificate));
+ if(cert == NULL) {
+ return memFullErr;
+ }
+ cert->next = 0;
+ if ((err = SSLAllocBuffer(cert->derCert, certLen, ctx)) != 0)
+ {
+ sslFree(cert);
+ return err;
+ }
+ memcpy(cert->derCert.data, charPtr, certLen);
+ charPtr += certLen;
+ ctx->peerCert = cert;
+ if((err = sslVerifyCertChain(ctx, *ctx->peerCert)) != 0) {
+ return err;
+ }
+ if((err = sslPubKeyFromCert(ctx,
+ cert->derCert,
+ &ctx->peerPubKey,
+ &ctx->peerPubKeyCsp)) != 0)
+ return err;
+
+ selectedCipher = SSL_NO_SUCH_CIPHERSUITE;
+ for (i = 0; i < cipherSpecsLen; i++)
+ { cipherKind = (SSL2CipherKind)SSLDecodeInt(charPtr, 3);
+ charPtr += 3;
+ if (selectedCipher == SSL_NO_SUCH_CIPHERSUITE) /* After we find one, just keep advancing charPtr past the unused ones */
+ { for (j = 0; j < SSL2CipherMapCount; j++)
+ if (cipherKind == SSL2CipherMap[j].cipherKind)
+ { matchingCipher = SSL2CipherMap[j].cipherSuite;
+ break;
+ }
+ for (j = 0; j < ctx->numValidCipherSpecs; j++)
+ if (ctx->validCipherSpecs[j].cipherSpec == matchingCipher)
+ { selectedCipher = matchingCipher;
+ break;
+ }
+ }
+ }
+ if (selectedCipher == SSL_NO_SUCH_CIPHERSUITE)
+ return errSSLNegotiation;
+ sslLogNegotiateDebug("===SSL2 client: selectedCipher 0x%x",
+ (unsigned)selectedCipher);
+
+ ctx->selectedCipher = selectedCipher;
+ if ((err = FindCipherSpec(ctx)) != 0) {
+ return err;
+ }
+ ctx->ssl2ConnectionIDLength = connectionIDLen;
+ memcpy(ctx->serverRandom, charPtr, connectionIDLen);
+ charPtr += connectionIDLen;
+ }
+
+ assert(charPtr == msg.data + msg.length);
+
+ return noErr;
+}
+
+OSStatus
+SSL2EncodeServerHello(SSLBuffer &msg, SSLContext *ctx)
+{ OSStatus err;
+ SSLCertificate *cert;
+ SSLBuffer randomData;
+ UInt8 *charPtr;
+ unsigned i;
+
+ /* Create the connection ID */
+ ctx->ssl2ConnectionIDLength = SSL2_CONNECTION_ID_LENGTH;
+ randomData.data = ctx->serverRandom;
+ randomData.length = ctx->ssl2ConnectionIDLength;
+ if ((err = sslRand(ctx, &randomData)) != 0)
+ return err;
+
+ if (ctx->ssl2SessionMatch != 0)
+ { if ((err = SSLAllocBuffer(msg, 11 + ctx->sessionID.length, ctx)) != 0)
+ return err;
+ charPtr = msg.data;
+ *charPtr++ = SSL2_MsgServerHello;
+ *charPtr++ = ctx->ssl2SessionMatch;
+ *charPtr++ = 0; /* cert type */
+ charPtr = SSLEncodeInt(charPtr, ctx->negProtocolVersion, 2);
+ charPtr = SSLEncodeInt(charPtr, 0, 2); /* cert len */
+ charPtr = SSLEncodeInt(charPtr, 0, 2); /* cipherspecs len */
+ charPtr = SSLEncodeInt(charPtr, ctx->ssl2ConnectionIDLength, 2);
+ memcpy(charPtr, ctx->serverRandom, ctx->ssl2ConnectionIDLength);
+ charPtr += ctx->ssl2ConnectionIDLength;
+ }
+ else
+ { /* First, find the last cert in the chain; it's the one we'll send */
+
+ /*
+ * Use encryptCert if we have it, but allow for the case of app
+ * specifying one cert which can encrypt and sign.
+ */
+ if(ctx->encryptCert != NULL) {
+ cert = ctx->encryptCert;
+ }
+ else if(ctx->localCert != NULL) {
+ cert = ctx->localCert;
+ }
+ else {
+ /* really should not happen... */
+ sslErrorLog("SSL2EncodeServerHello: No server cert!\n");
+ return badReqErr;
+ }
+
+ while (cert->next != 0)
+ cert = cert->next;
+
+ if ((err = SSLAllocBuffer(msg, 11 + cert->derCert.length + 3 + ctx->sessionID.length, ctx)) != 0)
+ return err;
+ charPtr = msg.data;
+ *charPtr++ = SSL2_MsgServerHello;
+ *charPtr++ = ctx->ssl2SessionMatch;
+ *charPtr++ = SSL2_CertTypeX509; /* cert type */
+
+ /* FIXME - this ifndef should not be necessary */
+ #ifndef NDEBUG
+ sslLogNegotiateDebug("===SSL2 server: sending vers info %s",
+ protocolVersStr((SSLProtocolVersion)ctx->negProtocolVersion));
+ #endif
+
+ charPtr = SSLEncodeInt(charPtr, ctx->negProtocolVersion, 2);
+ charPtr = SSLEncodeInt(charPtr, cert->derCert.length, 2);
+ charPtr = SSLEncodeInt(charPtr, 3, 2); /* cipherspecs len */
+ charPtr = SSLEncodeInt(charPtr, ctx->ssl2ConnectionIDLength, 2);
+ memcpy(charPtr, cert->derCert.data, cert->derCert.length);
+ charPtr += cert->derCert.length;
+ for (i = 0; i < SSL2CipherMapCount; i++)
+ if (ctx->selectedCipher == SSL2CipherMap[i].cipherSuite)
+ break;
+ assert(i < SSL2CipherMapCount);
+ charPtr = SSLEncodeInt(charPtr, SSL2CipherMap[i].cipherKind, 3);
+ sslLogNegotiateDebug("ssl2: server specifying cipherKind 0x%lx",
+ (UInt32)SSL2CipherMap[i].cipherKind);
+ memcpy(charPtr, ctx->serverRandom, ctx->ssl2ConnectionIDLength);
+ charPtr += ctx->ssl2ConnectionIDLength;
+ }
+
+ assert(charPtr == msg.data + msg.length);
+ return noErr;
+}
+
+OSStatus
+SSL2ProcessServerVerify(SSLBuffer msg, SSLContext *ctx)
+{ if (msg.length != ctx->ssl2ChallengeLength)
+ return errSSLProtocol;
+
+ if (memcmp(msg.data, ctx->clientRandom + SSL_CLIENT_SRVR_RAND_SIZE -
+ ctx->ssl2ChallengeLength, ctx->ssl2ChallengeLength) != 0)
+ return errSSLProtocol;
+
+ return noErr;
+}
+
+OSStatus
+SSL2EncodeServerVerify(SSLBuffer &msg, SSLContext *ctx)
+{ OSStatus err;
+
+ if ((err = SSLAllocBuffer(msg, 1 + ctx->ssl2ChallengeLength, ctx)) != 0)
+ return err;
+
+ msg.data[0] = SSL2_MsgServerVerify;
+ memcpy(msg.data+1, ctx->clientRandom + SSL_CLIENT_SRVR_RAND_SIZE -
+ ctx->ssl2ChallengeLength, ctx->ssl2ChallengeLength);
+
+ return noErr;
+}
+
+OSStatus
+SSL2ProcessServerFinished(SSLBuffer msg, SSLContext *ctx)
+{ OSStatus err;
+
+ if ((err = SSLAllocBuffer(ctx->sessionID, msg.length, ctx)) != 0)
+ return err;
+ memcpy(ctx->sessionID.data, msg.data, msg.length);
+ return noErr;
+}
+
+OSStatus
+SSL2EncodeServerFinished(SSLBuffer &msg, SSLContext *ctx)
+{ OSStatus err;
+
+ if ((err = SSLAllocBuffer(msg, 1 + ctx->sessionID.length, ctx)) != 0)
+ return err;
+
+ msg.data[0] = SSL2_MsgServerFinished;
+ memcpy(msg.data+1, ctx->sessionID.data, ctx->sessionID.length);
+
+ return noErr;
+}