nelsonb%netscape.com 7e4958958b Add localCert field to sid cache entry so SSL_LocalCertificate can
remember the certs it sent back when it established the SSL session.
Bug 78959.  Also, hold on the certs in the received cert chain until
the SSL connection is complete.  This makes it easier for applications
to look at the entire cert chain after the handshake is over without
having to write their own custom authCert callbacks.  It is backwards
compatible with older NSS SSL applications, but may use more memory.


git-svn-id: svn://10.0.0.236/trunk@107729 18797224-902f-48f8-a5cc-f745e15eee43
2001-11-09 05:39:36 +00:00

3881 lines
110 KiB
C

/*
* SSL v2 handshake functions, and functions common to SSL2 and SSL3.
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Netscape security libraries.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*
* $Id: sslcon.c,v 1.16 2001-11-09 05:39:36 nelsonb%netscape.com Exp $
*/
#include "nssrenam.h"
#include "cert.h"
#include "secitem.h"
#include "sechash.h"
#include "cryptohi.h" /* for SGN_ funcs */
#include "keyhi.h" /* for SECKEY_ high level functions. */
#include "ssl.h"
#include "sslimpl.h"
#include "sslproto.h"
#include "ssl3prot.h"
#include "sslerr.h"
#include "pk11func.h"
#include "prinit.h"
#include "prtime.h" /* for PR_Now() */
#define XXX
static PRBool policyWasSet;
/* This ordered list is indexed by (SSL_CK_xx * 3) */
/* Second and third bytes are MSB and LSB of master key length. */
static const PRUint8 allCipherSuites[] = {
0, 0, 0,
SSL_CK_RC4_128_WITH_MD5, 0x00, 0x80,
SSL_CK_RC4_128_EXPORT40_WITH_MD5, 0x00, 0x80,
SSL_CK_RC2_128_CBC_WITH_MD5, 0x00, 0x80,
SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5, 0x00, 0x80,
SSL_CK_IDEA_128_CBC_WITH_MD5, 0x00, 0x80,
SSL_CK_DES_64_CBC_WITH_MD5, 0x00, 0x40,
SSL_CK_DES_192_EDE3_CBC_WITH_MD5, 0x00, 0xC0,
0, 0, 0
};
#define ssl2_NUM_SUITES_IMPLEMENTED 6
/* This list is sent back to the client when the client-hello message
* contains no overlapping ciphers, so the client can report what ciphers
* are supported by the server. Unlike allCipherSuites (above), this list
* is sorted by descending preference, not by cipherSuite number.
*/
static const PRUint8 implementedCipherSuites[ssl2_NUM_SUITES_IMPLEMENTED * 3] = {
SSL_CK_RC4_128_WITH_MD5, 0x00, 0x80,
SSL_CK_RC2_128_CBC_WITH_MD5, 0x00, 0x80,
SSL_CK_DES_192_EDE3_CBC_WITH_MD5, 0x00, 0xC0,
SSL_CK_DES_64_CBC_WITH_MD5, 0x00, 0x40,
SSL_CK_RC4_128_EXPORT40_WITH_MD5, 0x00, 0x80,
SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5, 0x00, 0x80
};
typedef struct ssl2SpecsStr {
PRUint8 nkm; /* do this many hashes to generate key material. */
PRUint8 nkd; /* size of readKey and writeKey in bytes. */
PRUint8 blockSize;
PRUint8 blockShift;
CK_MECHANISM_TYPE mechanism;
PRUint8 keyLen; /* cipher symkey size in bytes. */
PRUint8 pubLen; /* publicly reveal this many bytes of key. */
PRUint8 ivLen; /* length of IV data at *ca. */
} ssl2Specs;
static const ssl2Specs ssl_Specs[] = {
/* NONE */
{ 0, 0, 0, 0, },
/* SSL_CK_RC4_128_WITH_MD5 */
{ 2, 16, 1, 0, CKM_RC4, 16, 0, 0, },
/* SSL_CK_RC4_128_EXPORT40_WITH_MD5 */
{ 2, 16, 1, 0, CKM_RC4, 16, 11, 0, },
/* SSL_CK_RC2_128_CBC_WITH_MD5 */
{ 2, 16, 8, 3, CKM_RC2_CBC, 16, 0, 8, },
/* SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5 */
{ 2, 16, 8, 3, CKM_RC2_CBC, 16, 11, 8, },
/* SSL_CK_IDEA_128_CBC_WITH_MD5 */
{ 0, 0, 0, 0, },
/* SSL_CK_DES_64_CBC_WITH_MD5 */
{ 1, 8, 8, 3, CKM_DES_CBC, 8, 0, 8, },
/* SSL_CK_DES_192_EDE3_CBC_WITH_MD5 */
{ 3, 24, 8, 3, CKM_DES3_CBC, 24, 0, 8, },
};
#define SET_ERROR_CODE /* reminder */
#define TEST_FOR_FAILURE /* reminder */
/*
** Put a string tag in the library so that we can examine an executable
** and see what kind of security it supports.
*/
const char *ssl_version = "SECURITY_VERSION:"
" +us"
" +export"
#ifdef TRACE
" +trace"
#endif
#ifdef DEBUG
" +debug"
#endif
;
const char * const ssl_cipherName[] = {
"unknown",
"RC4",
"RC4-Export",
"RC2-CBC",
"RC2-CBC-Export",
"IDEA-CBC",
"DES-CBC",
"DES-EDE3-CBC",
"unknown",
"Fortezza",
};
/* bit-masks, showing which SSLv2 suites are allowed.
* lsb corresponds to first cipher suite in allCipherSuites[].
*/
static PRUint16 allowedByPolicy; /* all off by default */
static PRUint16 maybeAllowedByPolicy; /* all off by default */
static PRUint16 chosenPreference = 0xff; /* all on by default */
/* bit values for the above two bit masks */
#define SSL_CB_RC4_128_WITH_MD5 (1 << SSL_CK_RC4_128_WITH_MD5)
#define SSL_CB_RC4_128_EXPORT40_WITH_MD5 (1 << SSL_CK_RC4_128_EXPORT40_WITH_MD5)
#define SSL_CB_RC2_128_CBC_WITH_MD5 (1 << SSL_CK_RC2_128_CBC_WITH_MD5)
#define SSL_CB_RC2_128_CBC_EXPORT40_WITH_MD5 (1 << SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5)
#define SSL_CB_IDEA_128_CBC_WITH_MD5 (1 << SSL_CK_IDEA_128_CBC_WITH_MD5)
#define SSL_CB_DES_64_CBC_WITH_MD5 (1 << SSL_CK_DES_64_CBC_WITH_MD5)
#define SSL_CB_DES_192_EDE3_CBC_WITH_MD5 (1 << SSL_CK_DES_192_EDE3_CBC_WITH_MD5)
#define SSL_CB_IMPLEMENTED \
(SSL_CB_RC4_128_WITH_MD5 | \
SSL_CB_RC4_128_EXPORT40_WITH_MD5 | \
SSL_CB_RC2_128_CBC_WITH_MD5 | \
SSL_CB_RC2_128_CBC_EXPORT40_WITH_MD5 | \
SSL_CB_DES_64_CBC_WITH_MD5 | \
SSL_CB_DES_192_EDE3_CBC_WITH_MD5)
/* Construct a socket's list of cipher specs from the global default values.
*/
static SECStatus
ssl2_ConstructCipherSpecs(sslSocket *ss)
{
PRUint8 * cs = NULL;
unsigned int allowed;
unsigned int count;
int ssl3_count = 0;
int final_count;
int i;
SECStatus rv;
PORT_Assert( ssl_Have1stHandshakeLock(ss) );
count = 0;
PORT_Assert(ss != 0);
allowed = !ss->enableSSL2 ? 0 :
(ss->allowedByPolicy & ss->chosenPreference & SSL_CB_IMPLEMENTED);
while (allowed) {
if (allowed & 1)
++count;
allowed >>= 1;
}
/* Call ssl3_config_match_init() once here,
* instead of inside ssl3_ConstructV2CipherSpecsHack(),
* because the latter gets called twice below,
* and then again in ssl2_BeginClientHandshake().
*/
ssl3_config_match_init(ss);
/* ask SSL3 how many cipher suites it has. */
rv = ssl3_ConstructV2CipherSpecsHack(ss, NULL, &ssl3_count);
if (rv < 0)
return rv;
count += ssl3_count;
/* Allocate memory to hold cipher specs */
if (count > 0)
cs = (PRUint8*) PORT_Alloc(count * 3);
else
PORT_SetError(SSL_ERROR_SSL_DISABLED);
if (cs == NULL)
return SECFailure;
if (ss->cipherSpecs != NULL) {
PORT_Free(ss->cipherSpecs);
}
ss->cipherSpecs = cs;
ss->sizeCipherSpecs = count * 3;
/* fill in cipher specs for SSL2 cipher suites */
allowed = !ss->enableSSL2 ? 0 :
(ss->allowedByPolicy & ss->chosenPreference & SSL_CB_IMPLEMENTED);
for (i = 0; i < ssl2_NUM_SUITES_IMPLEMENTED * 3; i += 3) {
const PRUint8 * hs = implementedCipherSuites + i;
int ok = allowed & (1U << hs[0]);
if (ok) {
cs[0] = hs[0];
cs[1] = hs[1];
cs[2] = hs[2];
cs += 3;
}
}
/* now have SSL3 add its suites onto the end */
rv = ssl3_ConstructV2CipherSpecsHack(ss, cs, &final_count);
/* adjust for any difference between first pass and second pass */
ss->sizeCipherSpecs -= (ssl3_count - final_count) * 3;
return rv;
}
/* This function is called immediately after ssl2_ConstructCipherSpecs()
** at the beginning of a handshake. It detects cases where a protocol
** (e.g. SSL2 or SSL3) is logically enabled, but all its cipher suites
** for that protocol have been disabled. If such cases, it clears the
** enable bit for the protocol. If no protocols remain enabled, or
** if no cipher suites are found, it sets the error code and returns
** SECFailure, otherwise it returns SECSuccess.
*/
static SECStatus
ssl2_CheckConfigSanity(sslSocket *ss)
{
unsigned int allowed;
int ssl3CipherCount = 0;
SECStatus rv;
/* count the SSL2 and SSL3 enabled ciphers.
* if either is zero, clear the socket's enable for that protocol.
*/
if (!ss->cipherSpecs)
goto disabled;
allowed = ss->allowedByPolicy & ss->chosenPreference;
if (! allowed)
ss->enableSSL2 = PR_FALSE; /* not really enabled if no ciphers */
/* ssl3_config_match_init was called in ssl2_ConstructCipherSpecs(). */
/* Ask how many ssl3 CipherSuites were enabled. */
rv = ssl3_ConstructV2CipherSpecsHack(ss, NULL, &ssl3CipherCount);
if (rv != SECSuccess || ssl3CipherCount <= 0) {
ss->enableSSL3 = PR_FALSE; /* not really enabled if no ciphers */
ss->enableTLS = PR_FALSE;
}
if (!ss->enableSSL2 && !ss->enableSSL3 && !ss->enableTLS) {
SSL_DBG(("%d: SSL[%d]: Can't handshake! both v2 and v3 disabled.",
SSL_GETPID(), ss->fd));
disabled:
PORT_SetError(SSL_ERROR_SSL_DISABLED);
return SECFailure;
}
return SECSuccess;
}
/*
* Since this is a global (not per-socket) setting, we cannot use the
* HandshakeLock to protect this. Probably want a global lock.
*/
SECStatus
ssl2_SetPolicy(PRInt32 which, PRInt32 policy)
{
PRUint32 bitMask;
SECStatus rv = SECSuccess;
which &= 0x000f;
bitMask = 1 << which;
if (!(bitMask & SSL_CB_IMPLEMENTED)) {
PORT_SetError(SSL_ERROR_UNKNOWN_CIPHER_SUITE);
return SECFailure;
}
if (policy == SSL_ALLOWED) {
allowedByPolicy |= bitMask;
maybeAllowedByPolicy |= bitMask;
} else if (policy == SSL_RESTRICTED) {
allowedByPolicy &= ~bitMask;
maybeAllowedByPolicy |= bitMask;
} else {
allowedByPolicy &= ~bitMask;
maybeAllowedByPolicy &= ~bitMask;
}
allowedByPolicy &= SSL_CB_IMPLEMENTED;
maybeAllowedByPolicy &= SSL_CB_IMPLEMENTED;
policyWasSet = PR_TRUE;
return rv;
}
SECStatus
ssl2_GetPolicy(PRInt32 which, PRInt32 *oPolicy)
{
PRUint32 bitMask;
PRInt32 policy;
which &= 0x000f;
bitMask = 1 << which;
/* Caller assures oPolicy is not null. */
if (!(bitMask & SSL_CB_IMPLEMENTED)) {
PORT_SetError(SSL_ERROR_UNKNOWN_CIPHER_SUITE);
*oPolicy = SSL_NOT_ALLOWED;
return SECFailure;
}
if (maybeAllowedByPolicy & bitMask) {
policy = (allowedByPolicy & bitMask) ? SSL_ALLOWED : SSL_RESTRICTED;
} else {
policy = SSL_NOT_ALLOWED;
}
*oPolicy = policy;
return SECSuccess;
}
/*
* Since this is a global (not per-socket) setting, we cannot use the
* HandshakeLock to protect this. Probably want a global lock.
* Called from SSL_CipherPrefSetDefault in sslsock.c
* These changes have no effect on any sslSockets already created.
*/
SECStatus
ssl2_CipherPrefSetDefault(PRInt32 which, PRBool enabled)
{
PRUint32 bitMask;
which &= 0x000f;
bitMask = 1 << which;
if (!(bitMask & SSL_CB_IMPLEMENTED)) {
PORT_SetError(SSL_ERROR_UNKNOWN_CIPHER_SUITE);
return SECFailure;
}
if (enabled)
chosenPreference |= bitMask;
else
chosenPreference &= ~bitMask;
chosenPreference &= SSL_CB_IMPLEMENTED;
return SECSuccess;
}
SECStatus
ssl2_CipherPrefGetDefault(PRInt32 which, PRBool *enabled)
{
PRBool rv = PR_FALSE;
PRUint32 bitMask;
which &= 0x000f;
bitMask = 1 << which;
if (!(bitMask & SSL_CB_IMPLEMENTED)) {
PORT_SetError(SSL_ERROR_UNKNOWN_CIPHER_SUITE);
*enabled = PR_FALSE;
return SECFailure;
}
rv = (PRBool)((chosenPreference & bitMask) != 0);
*enabled = rv;
return SECSuccess;
}
SECStatus
ssl2_CipherPrefSet(sslSocket *ss, PRInt32 which, PRBool enabled)
{
PRUint32 bitMask;
which &= 0x000f;
bitMask = 1 << which;
if (!(bitMask & SSL_CB_IMPLEMENTED)) {
PORT_SetError(SSL_ERROR_UNKNOWN_CIPHER_SUITE);
return SECFailure;
}
if (enabled)
ss->chosenPreference |= bitMask;
else
ss->chosenPreference &= ~bitMask;
ss->chosenPreference &= SSL_CB_IMPLEMENTED;
return SECSuccess;
}
SECStatus
ssl2_CipherPrefGet(sslSocket *ss, PRInt32 which, PRBool *enabled)
{
PRBool rv = PR_FALSE;
PRUint32 bitMask;
which &= 0x000f;
bitMask = 1 << which;
if (!(bitMask & SSL_CB_IMPLEMENTED)) {
PORT_SetError(SSL_ERROR_UNKNOWN_CIPHER_SUITE);
*enabled = PR_FALSE;
return SECFailure;
}
rv = (PRBool)((ss->chosenPreference & bitMask) != 0);
*enabled = rv;
return SECSuccess;
}
/* copy global default policy into socket. */
void
ssl2_InitSocketPolicy(sslSocket *ss)
{
ss->allowedByPolicy = allowedByPolicy;
ss->maybeAllowedByPolicy = maybeAllowedByPolicy;
ss->chosenPreference = chosenPreference;
}
/************************************************************************/
/* Called from ssl2_CreateSessionCypher(), which already holds handshake lock.
*/
static SECStatus
ssl2_CreateMAC(sslSecurityInfo *sec, SECItem *readKey, SECItem *writeKey,
int cipherChoice)
{
switch (cipherChoice) {
case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5:
case SSL_CK_RC2_128_CBC_WITH_MD5:
case SSL_CK_RC4_128_EXPORT40_WITH_MD5:
case SSL_CK_RC4_128_WITH_MD5:
case SSL_CK_DES_64_CBC_WITH_MD5:
case SSL_CK_DES_192_EDE3_CBC_WITH_MD5:
sec->hash = HASH_GetHashObject(HASH_AlgMD5);
SECITEM_CopyItem(0, &sec->sendSecret, writeKey);
SECITEM_CopyItem(0, &sec->rcvSecret, readKey);
break;
default:
PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
return SECFailure;
}
sec->hashcx = (*sec->hash->create)();
if (sec->hashcx == NULL)
return SECFailure;
return SECSuccess;
}
/************************************************************************
* All the Send functions below must acquire and release the socket's
* xmitBufLock.
*/
/* Called from all the Send* functions below. */
static SECStatus
ssl2_GetSendBuffer(sslSocket *ss, unsigned int len)
{
sslConnectInfo *ci;
SECStatus rv = SECSuccess;
PORT_Assert((ss->sec != 0));
PORT_Assert(ssl_HaveXmitBufLock(ss));
ci = &ss->sec->ci;
if (len < 128) {
len = 128;
}
if (len > ci->sendBuf.space) {
rv = sslBuffer_Grow(&ci->sendBuf, len);
if (rv != SECSuccess) {
SSL_DBG(("%d: SSL[%d]: ssl2_GetSendBuffer failed, tried to get %d bytes",
SSL_GETPID(), ss->fd, len));
rv = SECFailure;
}
}
return rv;
}
/* Called from:
* ssl2_ClientSetupSessionCypher() <- ssl2_HandleServerHelloMessage()
* ssl2_HandleRequestCertificate() <- ssl2_HandleMessage() <-
ssl_Do1stHandshake()
* ssl2_HandleMessage() <- ssl_Do1stHandshake()
* ssl2_HandleServerHelloMessage() <- ssl_Do1stHandshake()
after ssl2_BeginClientHandshake()
* ssl2_RestartHandshakeAfterCertReq() <- Called from certdlgs.c in nav.
* ssl2_HandleClientHelloMessage() <- ssl_Do1stHandshake()
after ssl2_BeginServerHandshake()
*
* Acquires and releases the socket's xmitBufLock.
*/
int
ssl2_SendErrorMessage(sslSocket *ss, int error)
{
sslSecurityInfo *sec;
int rv;
PRUint8 msg[SSL_HL_ERROR_HBYTES];
PORT_Assert( ssl_Have1stHandshakeLock(ss) );
PORT_Assert((ss->sec != 0));
msg[0] = SSL_MT_ERROR;
msg[1] = MSB(error);
msg[2] = LSB(error);
ssl_GetXmitBufLock(ss); /***************************************/
sec = ss->sec;
SSL_TRC(3, ("%d: SSL[%d]: sending error %d", SSL_GETPID(), ss->fd, error));
ss->handshakeBegun = 1;
rv = (*sec->send)(ss, msg, sizeof(msg), 0);
if (rv >= 0) {
rv = SECSuccess;
}
ssl_ReleaseXmitBufLock(ss); /***************************************/
return rv;
}
/* Called from ssl2_TryToFinish().
* Acquires and releases the socket's xmitBufLock.
*/
static SECStatus
ssl2_SendClientFinishedMessage(sslSocket *ss)
{
sslSecurityInfo *sec;
sslConnectInfo * ci;
SECStatus rv = SECSuccess;
int sent;
PRUint8 msg[1 + SSL_CONNECTIONID_BYTES];
PORT_Assert( ssl_Have1stHandshakeLock(ss) );
PORT_Assert((ss->sec != 0));
ssl_GetXmitBufLock(ss); /***************************************/
sec = ss->sec;
ci = &sec->ci;
if (ci->sentFinished == 0) {
ci->sentFinished = 1;
SSL_TRC(3, ("%d: SSL[%d]: sending client-finished",
SSL_GETPID(), ss->fd));
msg[0] = SSL_MT_CLIENT_FINISHED;
PORT_Memcpy(msg+1, ci->connectionID, sizeof(ci->connectionID));
DUMP_MSG(29, (ss, msg, 1 + sizeof(ci->connectionID)));
sent = (*sec->send)(ss, msg, 1 + sizeof(ci->connectionID), 0);
rv = (sent >= 0) ? SECSuccess : (SECStatus)sent;
}
ssl_ReleaseXmitBufLock(ss); /***************************************/
return rv;
}
/* Called from
* ssl2_HandleClientSessionKeyMessage() <- ssl2_HandleClientHelloMessage()
* ssl2_HandleClientHelloMessage() <- ssl_Do1stHandshake()
after ssl2_BeginServerHandshake()
* Acquires and releases the socket's xmitBufLock.
*/
static SECStatus
ssl2_SendServerVerifyMessage(sslSocket *ss)
{
sslSecurityInfo *sec;
sslConnectInfo * ci;
PRUint8 * msg;
int sendLen;
int sent;
SECStatus rv;
PORT_Assert( ssl_Have1stHandshakeLock(ss) );
PORT_Assert((ss->sec != 0));
ssl_GetXmitBufLock(ss); /***************************************/
sec = ss->sec;
ci = &sec->ci;
sendLen = 1 + SSL_CHALLENGE_BYTES;
rv = ssl2_GetSendBuffer(ss, sendLen);
if (rv != SECSuccess) {
goto done;
}
msg = ci->sendBuf.buf;
msg[0] = SSL_MT_SERVER_VERIFY;
PORT_Memcpy(msg+1, ci->clientChallenge, SSL_CHALLENGE_BYTES);
DUMP_MSG(29, (ss, msg, sendLen));
sent = (*sec->send)(ss, msg, sendLen, 0);
rv = (sent >= 0) ? SECSuccess : (SECStatus)sent;
done:
ssl_ReleaseXmitBufLock(ss); /***************************************/
return rv;
}
/* Called from ssl2_TryToFinish().
* Acquires and releases the socket's xmitBufLock.
*/
static SECStatus
ssl2_SendServerFinishedMessage(sslSocket *ss)
{
sslSecurityInfo *sec;
sslConnectInfo * ci;
sslSessionID * sid;
PRUint8 * msg;
int sendLen, sent;
SECStatus rv = SECSuccess;
PORT_Assert( ssl_Have1stHandshakeLock(ss) );
PORT_Assert((ss->sec != 0));
ssl_GetXmitBufLock(ss); /***************************************/
sec = ss->sec;
ci = &sec->ci;
if (ci->sentFinished == 0) {
ci->sentFinished = 1;
PORT_Assert(ci->sid != 0);
sid = ci->sid;
SSL_TRC(3, ("%d: SSL[%d]: sending server-finished",
SSL_GETPID(), ss->fd));
sendLen = 1 + sizeof(sid->u.ssl2.sessionID);
rv = ssl2_GetSendBuffer(ss, sendLen);
if (rv != SECSuccess) {
goto done;
}
msg = ci->sendBuf.buf;
msg[0] = SSL_MT_SERVER_FINISHED;
PORT_Memcpy(msg+1, sid->u.ssl2.sessionID,
sizeof(sid->u.ssl2.sessionID));
DUMP_MSG(29, (ss, msg, sendLen));
sent = (*sec->send)(ss, msg, sendLen, 0);
if (sent < 0) {
/* If send failed, it is now a bogus session-id */
(*sec->uncache)(sid);
rv = (SECStatus)sent;
} else if (!ss->noCache) {
/* Put the sid in session-id cache, (may already be there) */
(*sec->cache)(sid);
rv = SECSuccess;
}
ssl_FreeSID(sid);
ci->sid = 0;
}
done:
ssl_ReleaseXmitBufLock(ss); /***************************************/
return rv;
}
/* Called from ssl2_ClientSetupSessionCypher() <-
* ssl2_HandleServerHelloMessage()
* after ssl2_BeginClientHandshake()
* Acquires and releases the socket's xmitBufLock.
*/
static SECStatus
ssl2_SendSessionKeyMessage(sslSocket *ss, int cipher, int keySize,
PRUint8 *ca, int caLen,
PRUint8 *ck, int ckLen,
PRUint8 *ek, int ekLen)
{
sslSecurityInfo *sec;
PRUint8 * msg;
int sendLen;
int sent;
SECStatus rv;
PORT_Assert( ssl_Have1stHandshakeLock(ss) );
PORT_Assert((ss->sec != 0));
ssl_GetXmitBufLock(ss); /***************************************/
sec = ss->sec;
sendLen = SSL_HL_CLIENT_MASTER_KEY_HBYTES + ckLen + ekLen + caLen;
rv = ssl2_GetSendBuffer(ss, sendLen);
if (rv != SECSuccess)
goto done;
SSL_TRC(3, ("%d: SSL[%d]: sending client-session-key",
SSL_GETPID(), ss->fd));
msg = sec->ci.sendBuf.buf;
msg[0] = SSL_MT_CLIENT_MASTER_KEY;
msg[1] = cipher;
msg[2] = MSB(keySize);
msg[3] = LSB(keySize);
msg[4] = MSB(ckLen);
msg[5] = LSB(ckLen);
msg[6] = MSB(ekLen);
msg[7] = LSB(ekLen);
msg[8] = MSB(caLen);
msg[9] = LSB(caLen);
PORT_Memcpy(msg+SSL_HL_CLIENT_MASTER_KEY_HBYTES, ck, ckLen);
PORT_Memcpy(msg+SSL_HL_CLIENT_MASTER_KEY_HBYTES+ckLen, ek, ekLen);
PORT_Memcpy(msg+SSL_HL_CLIENT_MASTER_KEY_HBYTES+ckLen+ekLen, ca, caLen);
DUMP_MSG(29, (ss, msg, sendLen));
sent = (*sec->send)(ss, msg, sendLen, 0);
rv = (sent >= 0) ? SECSuccess : (SECStatus)sent;
done:
ssl_ReleaseXmitBufLock(ss); /***************************************/
return rv;
}
/* Called from ssl2_TriggerNextMessage() <- ssl2_HandleMessage()
* Acquires and releases the socket's xmitBufLock.
*/
static SECStatus
ssl2_SendCertificateRequestMessage(sslSocket *ss)
{
sslSecurityInfo *sec;
sslConnectInfo * ci;
PRUint8 * msg;
int sent;
int sendLen;
SECStatus rv;
PORT_Assert( ssl_Have1stHandshakeLock(ss) );
PORT_Assert((ss->sec != 0));
ssl_GetXmitBufLock(ss); /***************************************/
sec = ss->sec;
ci = &sec->ci;
sendLen = SSL_HL_REQUEST_CERTIFICATE_HBYTES + SSL_CHALLENGE_BYTES;
rv = ssl2_GetSendBuffer(ss, sendLen);
if (rv != SECSuccess)
goto done;
SSL_TRC(3, ("%d: SSL[%d]: sending certificate request",
SSL_GETPID(), ss->fd));
/* Generate random challenge for client to encrypt */
PK11_GenerateRandom(ci->serverChallenge, SSL_CHALLENGE_BYTES);
msg = ci->sendBuf.buf;
msg[0] = SSL_MT_REQUEST_CERTIFICATE;
msg[1] = SSL_AT_MD5_WITH_RSA_ENCRYPTION;
PORT_Memcpy(msg + SSL_HL_REQUEST_CERTIFICATE_HBYTES, ci->serverChallenge,
SSL_CHALLENGE_BYTES);
DUMP_MSG(29, (ss, msg, sendLen));
sent = (*sec->send)(ss, msg, sendLen, 0);
rv = (sent >= 0) ? SECSuccess : (SECStatus)sent;
done:
ssl_ReleaseXmitBufLock(ss); /***************************************/
return rv;
}
/* Called from ssl2_HandleRequestCertificate() <- ssl2_HandleMessage()
* ssl2_RestartHandshakeAfterCertReq() <- (application)
* Acquires and releases the socket's xmitBufLock.
*/
static int
ssl2_SendCertificateResponseMessage(sslSocket *ss, SECItem *cert,
SECItem *encCode)
{
sslSecurityInfo *sec;
PRUint8 *msg;
int rv, sendLen;
PORT_Assert( ssl_Have1stHandshakeLock(ss) );
PORT_Assert((ss->sec != 0));
ssl_GetXmitBufLock(ss); /***************************************/
sec = ss->sec;
sendLen = SSL_HL_CLIENT_CERTIFICATE_HBYTES + encCode->len + cert->len;
rv = ssl2_GetSendBuffer(ss, sendLen);
if (rv)
goto done;
SSL_TRC(3, ("%d: SSL[%d]: sending certificate response",
SSL_GETPID(), ss->fd));
msg = sec->ci.sendBuf.buf;
msg[0] = SSL_MT_CLIENT_CERTIFICATE;
msg[1] = SSL_CT_X509_CERTIFICATE;
msg[2] = MSB(cert->len);
msg[3] = LSB(cert->len);
msg[4] = MSB(encCode->len);
msg[5] = LSB(encCode->len);
PORT_Memcpy(msg + SSL_HL_CLIENT_CERTIFICATE_HBYTES, cert->data, cert->len);
PORT_Memcpy(msg + SSL_HL_CLIENT_CERTIFICATE_HBYTES + cert->len,
encCode->data, encCode->len);
DUMP_MSG(29, (ss, msg, sendLen));
rv = (*sec->send)(ss, msg, sendLen, 0);
if (rv >= 0) {
rv = SECSuccess;
}
done:
ssl_ReleaseXmitBufLock(ss); /***************************************/
return rv;
}
/********************************************************************
** Send functions above this line must aquire & release the socket's
** xmitBufLock.
** All the ssl2_Send functions below this line are called vis ss->sec->send
** and require that the caller hold the xmitBufLock.
*/
/*
** Called from ssl2_SendStream, ssl2_SendBlock, but not from ssl2_SendClear.
*/
static SECStatus
ssl2_CalcMAC(PRUint8 * result,
sslSecurityInfo * sec,
const PRUint8 * data,
unsigned int dataLen,
unsigned int paddingLen)
{
const PRUint8 * secret = sec->sendSecret.data;
unsigned int secretLen = sec->sendSecret.len;
unsigned long sequenceNumber = sec->sendSequence;
unsigned int nout;
PRUint8 seq[4];
PRUint8 padding[32];/* XXX max blocksize? */
if (!sec->hash || !sec->hash->length)
return SECSuccess;
if (!sec->hashcx)
return SECFailure;
/* Reset hash function */
(*sec->hash->begin)(sec->hashcx);
/* Feed hash the data */
(*sec->hash->update)(sec->hashcx, secret, secretLen);
(*sec->hash->update)(sec->hashcx, data, dataLen);
PORT_Memset(padding, paddingLen, paddingLen);
(*sec->hash->update)(sec->hashcx, padding, paddingLen);
seq[0] = (PRUint8) (sequenceNumber >> 24);
seq[1] = (PRUint8) (sequenceNumber >> 16);
seq[2] = (PRUint8) (sequenceNumber >> 8);
seq[3] = (PRUint8) (sequenceNumber);
PRINT_BUF(60, (0, "calc-mac secret:", secret, secretLen));
PRINT_BUF(60, (0, "calc-mac data:", data, dataLen));
PRINT_BUF(60, (0, "calc-mac padding:", padding, paddingLen));
PRINT_BUF(60, (0, "calc-mac seq:", seq, 4));
(*sec->hash->update)(sec->hashcx, seq, 4);
/* Get result */
(*sec->hash->end)(sec->hashcx, result, &nout, sec->hash->length);
return SECSuccess;
}
/*
** Maximum transmission amounts. These are tiny bit smaller than they
** need to be (they account for the MAC length plus some padding),
** assuming the MAC is 16 bytes long and the padding is a max of 7 bytes
** long. This gives an additional 9 bytes of slop to work within.
*/
#define MAX_STREAM_CYPHER_LEN 0x7fe0
#define MAX_BLOCK_CYPHER_LEN 0x3fe0
/*
** Send some data in the clear.
** Package up data with the length header and send it.
**
** Return count of bytes succesfully written, or negative number (failure).
*/
static PRInt32
ssl2_SendClear(sslSocket *ss, const PRUint8 *in, PRInt32 len, PRInt32 flags)
{
sslSecurityInfo * sec = ss->sec;
PRUint8 * out;
int rv;
int amount;
int count = 0;
PORT_Assert(sec != 0);
PORT_Assert( ssl_HaveXmitBufLock(ss) );
SSL_TRC(10, ("%d: SSL[%d]: sending %d bytes in the clear",
SSL_GETPID(), ss->fd, len));
PRINT_BUF(50, (ss, "clear data:", (PRUint8*) in, len));
while (len) {
amount = PR_MIN( len, MAX_STREAM_CYPHER_LEN );
if (amount + 2 > sec->writeBuf.space) {
rv = sslBuffer_Grow(&sec->writeBuf, amount + 2);
if (rv != SECSuccess) {
count = rv;
break;
}
}
out = sec->writeBuf.buf;
/*
** Construct message.
*/
out[0] = 0x80 | MSB(amount);
out[1] = LSB(amount);
PORT_Memcpy(&out[2], in, amount);
/* Now send the data */
rv = ssl_DefSend(ss, out, amount + 2, flags & ~ssl_SEND_FLAG_MASK);
if (rv < 0) {
if (PORT_GetError() == PR_WOULD_BLOCK_ERROR) {
rv = 0;
} else {
/* Return short write if some data already went out... */
if (count == 0)
count = rv;
break;
}
}
if ((unsigned)rv < (amount + 2)) {
/* Short write. Save the data and return. */
if (ssl_SaveWriteData(ss, &ss->pendingBuf, out + rv,
amount + 2 - rv) == SECFailure) {
count = SECFailure;
} else {
count += amount;
sec->sendSequence++;
}
break;
}
sec->sendSequence++;
in += amount;
count += amount;
len -= amount;
}
return count;
}
/*
** Send some data, when using a stream cipher. Stream ciphers have a
** block size of 1. Package up the data with the length header
** and send it.
*/
static PRInt32
ssl2_SendStream(sslSocket *ss, const PRUint8 *in, PRInt32 len, PRInt32 flags)
{
sslSecurityInfo *sec = ss->sec;
PRUint8 * out;
int rv;
int count = 0;
int amount;
PRUint8 macLen;
int nout;
int buflen;
PORT_Assert(sec != 0);
PORT_Assert( ssl_HaveXmitBufLock(ss) );
SSL_TRC(10, ("%d: SSL[%d]: sending %d bytes using stream cipher",
SSL_GETPID(), ss->fd, len));
PRINT_BUF(50, (ss, "clear data:", (PRUint8*) in, len));
while (len) {
ssl_GetSpecReadLock(ss); /*************************************/
macLen = sec->hash->length;
amount = PR_MIN( len, MAX_STREAM_CYPHER_LEN );
buflen = amount + 2 + macLen;
if (buflen > sec->writeBuf.space) {
rv = sslBuffer_Grow(&sec->writeBuf, buflen);
if (rv != SECSuccess) {
goto loser;
}
}
out = sec->writeBuf.buf;
nout = amount + macLen;
out[0] = 0x80 | MSB(nout);
out[1] = LSB(nout);
/* Calculate MAC */
rv = ssl2_CalcMAC(out+2, /* put MAC here */
sec,
in, amount, /* input addr & length */
0); /* no padding */
if (rv != SECSuccess)
goto loser;
/* Encrypt MAC */
rv = (*sec->enc)(sec->writecx, out+2, &nout, macLen, out+2, macLen);
if (rv) goto loser;
/* Encrypt data from caller */
rv = (*sec->enc)(sec->writecx, out+2+macLen, &nout, amount, in, amount);
if (rv) goto loser;
ssl_ReleaseSpecReadLock(ss); /*************************************/
PRINT_BUF(50, (ss, "encrypted data:", out, buflen));
rv = ssl_DefSend(ss, out, buflen, flags & ~ssl_SEND_FLAG_MASK);
if (rv < 0) {
if (PORT_GetError() == PR_WOULD_BLOCK_ERROR) {
SSL_TRC(50, ("%d: SSL[%d]: send stream would block, "
"saving data", SSL_GETPID(), ss->fd));
rv = 0;
} else {
SSL_TRC(10, ("%d: SSL[%d]: send stream error %d",
SSL_GETPID(), ss->fd, PORT_GetError()));
/* Return short write if some data already went out... */
if (count == 0)
count = rv;
goto done;
}
}
if ((unsigned)rv < buflen) {
/* Short write. Save the data and return. */
if (ssl_SaveWriteData(ss, &ss->pendingBuf, out + rv,
buflen - rv) == SECFailure) {
count = SECFailure;
} else {
count += amount;
sec->sendSequence++;
}
goto done;
}
sec->sendSequence++;
in += amount;
count += amount;
len -= amount;
}
done:
return count;
loser:
ssl_ReleaseSpecReadLock(ss);
return SECFailure;
}
/*
** Send some data, when using a block cipher. Package up the data with
** the length header and send it.
*/
/* XXX assumes blocksize is > 7 */
static PRInt32
ssl2_SendBlock(sslSocket *ss, const PRUint8 *in, PRInt32 len, PRInt32 flags)
{
sslSecurityInfo *sec = ss->sec;
PRUint8 * out; /* begining of output buffer. */
PRUint8 * op; /* next output byte goes here. */
int rv; /* value from funcs we called. */
int count = 0; /* this function's return value. */
unsigned int hlen; /* output record hdr len, 2 or 3 */
unsigned int macLen; /* MAC is this many bytes long. */
int amount; /* of plaintext to go in record. */
unsigned int padding; /* add this many padding byte. */
int nout; /* ciphertext size after header. */
int buflen; /* size of generated record. */
PORT_Assert(sec != 0);
PORT_Assert( ssl_HaveXmitBufLock(ss) );
SSL_TRC(10, ("%d: SSL[%d]: sending %d bytes using block cipher",
SSL_GETPID(), ss->fd, len));
PRINT_BUF(50, (ss, "clear data:", in, len));
while (len) {
ssl_GetSpecReadLock(ss); /*************************************/
macLen = sec->hash->length;
/* Figure out how much to send, including mac and padding */
amount = PR_MIN( len, MAX_BLOCK_CYPHER_LEN );
nout = amount + macLen;
padding = nout & (sec->blockSize - 1);
if (padding) {
hlen = 3;
padding = sec->blockSize - padding;
nout += padding;
} else {
hlen = 2;
}
buflen = hlen + nout;
if (buflen > sec->writeBuf.space) {
rv = sslBuffer_Grow(&sec->writeBuf, buflen);
if (rv != SECSuccess) {
goto loser;
}
}
out = sec->writeBuf.buf;
/* Construct header */
op = out;
if (padding) {
*op++ = MSB(nout);
*op++ = LSB(nout);
*op++ = padding;
} else {
*op++ = 0x80 | MSB(nout);
*op++ = LSB(nout);
}
/* Calculate MAC */
rv = ssl2_CalcMAC(op, /* MAC goes here. */
sec,
in, amount, /* intput addr, len */
padding);
if (rv != SECSuccess)
goto loser;
op += macLen;
/* Copy in the input data */
/* XXX could eliminate the copy by folding it into the encryption */
PORT_Memcpy(op, in, amount);
op += amount;
if (padding) {
PORT_Memset(op, padding, padding);
op += padding;
}
/* Encrypt result */
rv = (*sec->enc)(sec->writecx, out+hlen, &nout, buflen-hlen,
out+hlen, op - (out + hlen));
if (rv)
goto loser;
ssl_ReleaseSpecReadLock(ss); /*************************************/
PRINT_BUF(50, (ss, "final xmit data:", out, op - out));
rv = ssl_DefSend(ss, out, op - out, flags & ~ssl_SEND_FLAG_MASK);
if (rv < 0) {
if (PORT_GetError() == PR_WOULD_BLOCK_ERROR) {
rv = 0;
} else {
SSL_TRC(10, ("%d: SSL[%d]: send block error %d",
SSL_GETPID(), ss->fd, PORT_GetError()));
/* Return short write if some data already went out... */
if (count == 0)
count = rv;
goto done;
}
}
if (rv < (op - out)) {
/* Short write. Save the data and return. */
if (ssl_SaveWriteData(ss, &ss->pendingBuf, out + rv,
op - out - rv) == SECFailure) {
count = SECFailure;
} else {
count += amount;
sec->sendSequence++;
}
goto done;
}
sec->sendSequence++;
in += amount;
count += amount;
len -= amount;
}
done:
return count;
loser:
ssl_ReleaseSpecReadLock(ss);
return SECFailure;
}
/*
** Called from: ssl2_HandleServerHelloMessage,
** ssl2_HandleClientSessionKeyMessage,
** ssl2_RestartHandshakeAfterServerCert,
** ssl2_HandleClientHelloMessage,
**
*/
static void
ssl2_UseEncryptedSendFunc(sslSocket *ss)
{
sslSecurityInfo *sec;
ssl_GetXmitBufLock(ss);
PORT_Assert(ss->sec != 0);
sec = ss->sec;
PORT_Assert(sec->hashcx != 0);
ss->gather->encrypted = 1;
sec->send = (sec->blockSize > 1) ? ssl2_SendBlock : ssl2_SendStream;
ssl_ReleaseXmitBufLock(ss);
}
/* Called while initializing socket in ssl_CreateSecurityInfo().
** This function allows us to keep the name of ssl2_SendClear static.
*/
void
ssl2_UseClearSendFunc(sslSocket *ss)
{
ss->sec->send = ssl2_SendClear;
}
/************************************************************************
** END of Send functions. *
*************************************************************************/
/***********************************************************************
* For SSL3, this gathers in and handles records/messages until either
* the handshake is complete or application data is available.
*
* For SSL2, this gathers in only the next SSLV2 record.
*
* Called from ssl_Do1stHandshake() via function pointer ss->handshake.
* Caller must hold handshake lock.
* This function acquires and releases the RecvBufLock.
*
* returns SECSuccess for success.
* returns SECWouldBlock when that value is returned by ssl2_GatherRecord() or
* ssl3_GatherCompleteHandshake().
* returns SECFailure on all other errors.
*
* The gather functions called by ssl_GatherRecord1stHandshake are expected
* to return values interpreted as follows:
* 1 : the function completed without error.
* 0 : the function read EOF.
* -1 : read error, or PR_WOULD_BLOCK_ERROR, or handleRecord error.
* -2 : the function wants ssl_GatherRecord1stHandshake to be called again
* immediately, by ssl_Do1stHandshake.
*
* This code is similar to, and easily confused with, DoRecv() in sslsecur.c
*
* This function is called from ssl_Do1stHandshake().
* The following functions put ssl_GatherRecord1stHandshake into ss->handshake:
* ssl2_HandleMessage
* ssl2_HandleVerifyMessage
* ssl2_HandleServerHelloMessage
* ssl2_BeginClientHandshake
* ssl2_HandleClientSessionKeyMessage
* ssl2_RestartHandshakeAfterCertReq
* ssl3_RestartHandshakeAfterCertReq
* ssl2_RestartHandshakeAfterServerCert
* ssl3_RestartHandshakeAfterServerCert
* ssl2_HandleClientHelloMessage
* ssl2_BeginServerHandshake
*/
SECStatus
ssl_GatherRecord1stHandshake(sslSocket *ss)
{
int rv;
PORT_Assert((ss->sec != 0) && (ss->gather != 0));
PORT_Assert( ssl_Have1stHandshakeLock(ss) );
ssl_GetRecvBufLock(ss);
if (ss->version >= SSL_LIBRARY_VERSION_3_0) {
/* Wait for handshake to complete, or application data to arrive. */
rv = ssl3_GatherCompleteHandshake(ss, 0);
} else {
/* See if we have a complete record */
rv = ssl2_GatherRecord(ss, 0);
}
SSL_TRC(10, ("%d: SSL[%d]: handshake gathering, rv=%d",
SSL_GETPID(), ss->fd, rv));
ssl_ReleaseRecvBufLock(ss);
if (rv <= 0) {
if (rv == SECWouldBlock) {
/* Progress is blocked waiting for callback completion. */
SSL_TRC(10, ("%d: SSL[%d]: handshake blocked (need %d)",
SSL_GETPID(), ss->fd, ss->gather->remainder));
return SECWouldBlock;
}
if (rv == 0) {
/* EOF. Loser */
PORT_SetError(PR_END_OF_FILE_ERROR);
}
return SECFailure; /* rv is < 0 here. */
}
SSL_TRC(10, ("%d: SSL[%d]: got handshake record of %d bytes",
SSL_GETPID(), ss->fd, ss->gather->recordLen));
ss->handshake = 0; /* makes ssl_Do1stHandshake call ss->nextHandshake.*/
return SECSuccess;
}
/************************************************************************/
/* Called from ssl2_ServerSetupSessionCypher()
* ssl2_ClientSetupSessionCypher()
*/
static SECStatus
ssl2_FillInSID(sslSessionID * sid,
int cipher,
PRUint8 *keyData,
int keyLen,
PRUint8 *ca,
int caLen,
int keyBits,
int secretKeyBits,
SSLSignType authAlgorithm,
PRUint32 authKeyBits,
SSLKEAType keaType,
PRUint32 keaKeyBits)
{
PORT_Assert(sid->references == 1);
PORT_Assert(sid->cached == never_cached);
PORT_Assert(sid->u.ssl2.masterKey.data == 0);
PORT_Assert(sid->u.ssl2.cipherArg.data == 0);
sid->version = SSL_LIBRARY_VERSION_2;
sid->u.ssl2.cipherType = cipher;
sid->u.ssl2.masterKey.data = (PRUint8*) PORT_Alloc(keyLen);
if (!sid->u.ssl2.masterKey.data) {
return SECFailure;
}
PORT_Memcpy(sid->u.ssl2.masterKey.data, keyData, keyLen);
sid->u.ssl2.masterKey.len = keyLen;
sid->u.ssl2.keyBits = keyBits;
sid->u.ssl2.secretKeyBits = secretKeyBits;
sid->authAlgorithm = authAlgorithm;
sid->authKeyBits = authKeyBits;
sid->keaType = keaType;
sid->keaKeyBits = keaKeyBits;
sid->lastAccessTime = sid->creationTime = ssl_Time();
sid->expirationTime = sid->creationTime + ssl_sid_timeout;
if (caLen) {
sid->u.ssl2.cipherArg.data = (PRUint8*) PORT_Alloc(caLen);
if (!sid->u.ssl2.cipherArg.data) {
return SECFailure;
}
sid->u.ssl2.cipherArg.len = caLen;
PORT_Memcpy(sid->u.ssl2.cipherArg.data, ca, caLen);
}
return SECSuccess;
}
/*
** Construct session keys given the masterKey (tied to the session-id),
** the client's challenge and the server's nonce.
**
** Called from ssl2_CreateSessionCypher() <-
*/
static SECStatus
ssl2_ProduceKeys(sslSocket * ss,
SECItem * readKey,
SECItem * writeKey,
SECItem * masterKey,
PRUint8 * challenge,
PRUint8 * nonce,
int cipherType)
{
PK11Context * cx = 0;
unsigned nkm = 0; /* number of hashes to generate key mat. */
unsigned nkd = 0; /* size of readKey and writeKey. */
unsigned part;
unsigned i;
unsigned off;
SECStatus rv;
PRUint8 countChar;
PRUint8 km[3*16]; /* buffer for key material. */
readKey->data = 0;
writeKey->data = 0;
PORT_Assert( ssl_Have1stHandshakeLock(ss) );
PORT_Assert((ss->sec != 0));
rv = SECSuccess;
cx = PK11_CreateDigestContext(SEC_OID_MD5);
if (cx == NULL) {
ssl_MapLowLevelError(SSL_ERROR_MD5_DIGEST_FAILURE);
return SECFailure;
}
nkm = ssl_Specs[cipherType].nkm;
nkd = ssl_Specs[cipherType].nkd;
readKey->data = (PRUint8*) PORT_Alloc(nkd);
if (!readKey->data)
goto loser;
readKey->len = nkd;
writeKey->data = (PRUint8*) PORT_Alloc(nkd);
if (!writeKey->data)
goto loser;
writeKey->len = nkd;
/* Produce key material */
countChar = '0';
for (i = 0, off = 0; i < nkm; i++, off += 16) {
rv = PK11_DigestBegin(cx);
rv |= PK11_DigestOp(cx, masterKey->data, masterKey->len);
rv |= PK11_DigestOp(cx, &countChar, 1);
rv |= PK11_DigestOp(cx, challenge, SSL_CHALLENGE_BYTES);
rv |= PK11_DigestOp(cx, nonce, SSL_CONNECTIONID_BYTES);
rv |= PK11_DigestFinal(cx, km+off, &part, MD5_LENGTH);
if (rv != SECSuccess) {
ssl_MapLowLevelError(SSL_ERROR_MD5_DIGEST_FAILURE);
rv = SECFailure;
goto loser;
}
countChar++;
}
/* Produce keys */
PORT_Memcpy(readKey->data, km, nkd);
PORT_Memcpy(writeKey->data, km + nkd, nkd);
loser:
PK11_DestroyContext(cx, PR_TRUE);
return rv;
}
/* Called from ssl2_ServerSetupSessionCypher()
** <- ssl2_HandleClientSessionKeyMessage()
** <- ssl2_HandleClientHelloMessage()
** and from ssl2_ClientSetupSessionCypher()
** <- ssl2_HandleServerHelloMessage()
*/
static SECStatus
ssl2_CreateSessionCypher(sslSocket *ss, sslSessionID *sid, PRBool isClient)
{
sslSecurityInfo * sec;
sslConnectInfo * ci;
SECItem * rk;
SECItem * wk;
SECItem * param;
SECStatus rv;
int cipherType = sid->u.ssl2.cipherType;
PK11SlotInfo * slot = NULL;
CK_MECHANISM_TYPE mechanism;
SECItem readKey;
SECItem writeKey;
void *readcx = 0;
void *writecx = 0;
readKey.data = 0;
writeKey.data = 0;
PORT_Assert( ssl_Have1stHandshakeLock(ss) );
PORT_Assert((ss->sec != 0));
if((ss->sec == 0) || (ss->sec->ci.sid == 0))
goto sec_loser; /* don't crash if asserts are off */
/* Trying to cut down on all these switch statements that should be tables.
* So, test cipherType once, here, and then use tables below.
*/
switch (cipherType) {
case SSL_CK_RC4_128_EXPORT40_WITH_MD5:
case SSL_CK_RC4_128_WITH_MD5:
case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5:
case SSL_CK_RC2_128_CBC_WITH_MD5:
case SSL_CK_DES_64_CBC_WITH_MD5:
case SSL_CK_DES_192_EDE3_CBC_WITH_MD5:
break;
default:
SSL_DBG(("%d: SSL[%d]: ssl2_CreateSessionCypher: unknown cipher=%d",
SSL_GETPID(), ss->fd, cipherType));
PORT_SetError(isClient ? SSL_ERROR_BAD_SERVER : SSL_ERROR_BAD_CLIENT);
goto loser;
}
sec = ss->sec;
ci = &sec->ci;
rk = isClient ? &readKey : &writeKey;
wk = isClient ? &writeKey : &readKey;
/* Produce the keys for this session */
rv = ssl2_ProduceKeys(ss, &readKey, &writeKey, &sid->u.ssl2.masterKey,
ci->clientChallenge, ci->connectionID,
cipherType);
if (rv != SECSuccess)
goto loser;
PRINT_BUF(7, (ss, "Session read-key: ", rk->data, rk->len));
PRINT_BUF(7, (ss, "Session write-key: ", wk->data, wk->len));
PORT_Memcpy(ci->readKey, readKey.data, readKey.len);
PORT_Memcpy(ci->writeKey, writeKey.data, writeKey.len);
ci->keySize = readKey.len;
/* Setup the MAC */
rv = ssl2_CreateMAC(sec, rk, wk, cipherType);
if (rv != SECSuccess)
goto loser;
/* First create the session key object */
SSL_TRC(3, ("%d: SSL[%d]: using %s", SSL_GETPID(), ss->fd,
ssl_cipherName[cipherType]));
mechanism = ssl_Specs[cipherType].mechanism;
/* set destructer before we call loser... */
sec->destroy = (void (*)(void*, PRBool)) PK11_DestroyContext;
slot = PK11_GetBestSlot(mechanism, ss->pkcs11PinArg);
if (slot == NULL)
goto loser;
param = PK11_ParamFromIV(mechanism, &sid->u.ssl2.cipherArg);
if (param == NULL)
goto loser;
readcx = PK11_CreateContextByRawKey(slot, mechanism, PK11_OriginUnwrap,
CKA_DECRYPT, rk, param,
ss->pkcs11PinArg);
SECITEM_FreeItem(param, PR_TRUE);
if (readcx == NULL)
goto loser;
/* build the client context */
param = PK11_ParamFromIV(mechanism, &sid->u.ssl2.cipherArg);
if (param == NULL)
goto loser;
writecx = PK11_CreateContextByRawKey(slot, mechanism, PK11_OriginUnwrap,
CKA_ENCRYPT, wk, param,
ss->pkcs11PinArg);
SECITEM_FreeItem(param,PR_TRUE);
if (writecx == NULL)
goto loser;
PK11_FreeSlot(slot);
rv = SECSuccess;
sec->enc = (SSLCipher) PK11_CipherOp;
sec->dec = (SSLCipher) PK11_CipherOp;
sec->readcx = (void *) readcx;
sec->writecx = (void *) writecx;
sec->blockSize = ssl_Specs[cipherType].blockSize;
sec->blockShift = ssl_Specs[cipherType].blockShift;
sec->cipherType = sid->u.ssl2.cipherType;
sec->keyBits = sid->u.ssl2.keyBits;
sec->secretKeyBits = sid->u.ssl2.secretKeyBits;
goto done;
loser:
if (sec->destroy) {
if (readcx) (*sec->destroy)(readcx, PR_TRUE);
if (writecx) (*sec->destroy)(writecx, PR_TRUE);
}
sec->destroy = NULL;
if (slot) PK11_FreeSlot(slot);
sec_loser:
rv = SECFailure;
done:
SECITEM_ZfreeItem(rk, PR_FALSE);
SECITEM_ZfreeItem(wk, PR_FALSE);
return rv;
}
/*
** Setup the server ciphers given information from a CLIENT-MASTER-KEY
** message.
** "ss" pointer to the ssl-socket object
** "cipher" the cipher type to use
** "keyBits" the size of the final cipher key
** "ck" the clear-key data
** "ckLen" the number of bytes of clear-key data
** "ek" the encrypted-key data
** "ekLen" the number of bytes of encrypted-key data
** "ca" the cipher-arg data
** "caLen" the number of bytes of cipher-arg data
**
** The MASTER-KEY is constructed by first decrypting the encrypted-key
** data. This produces the SECRET-KEY-DATA. The MASTER-KEY is composed by
** concatenating the clear-key data with the SECRET-KEY-DATA. This code
** checks to make sure that the client didn't send us an improper amount
** of SECRET-KEY-DATA (it restricts the length of that data to match the
** spec).
**
** Called from ssl2_HandleClientSessionKeyMessage().
*/
static SECStatus
ssl2_ServerSetupSessionCypher(sslSocket *ss, int cipher, unsigned int keyBits,
PRUint8 *ck, unsigned int ckLen,
PRUint8 *ek, unsigned int ekLen,
PRUint8 *ca, unsigned int caLen)
{
PRUint8 *kk;
sslSecurityInfo * sec;
sslSessionID * sid;
PRUint8 * kbuf = 0; /* buffer for RSA decrypted data. */
unsigned int el1; /* length of RSA decrypted data in kbuf */
unsigned int keySize;
unsigned int modulusLen;
SECStatus rv;
PRUint8 mkbuf[SSL_MAX_MASTER_KEY_BYTES];
sslServerCerts * sc = ss->serverCerts + kt_rsa;
PORT_Assert( ssl_Have1stHandshakeLock(ss) );
PORT_Assert( ssl_HaveRecvBufLock(ss) );
PORT_Assert((ss->sec != 0) && (sc->serverKey != 0));
sec = ss->sec;
PORT_Assert((sec->ci.sid != 0));
sid = sec->ci.sid;
keySize = (keyBits + 7) >> 3;
/* Is the message just way too big? */
if (keySize > SSL_MAX_MASTER_KEY_BYTES) {
/* bummer */
SSL_DBG(("%d: SSL[%d]: keySize=%d ckLen=%d max session key size=%d",
SSL_GETPID(), ss->fd, keySize, ckLen,
SSL_MAX_MASTER_KEY_BYTES));
PORT_SetError(SSL_ERROR_BAD_CLIENT);
goto loser;
}
/* Trying to cut down on all these switch statements that should be tables.
* So, test cipherType once, here, and then use tables below.
*/
switch (cipher) {
case SSL_CK_RC4_128_EXPORT40_WITH_MD5:
case SSL_CK_RC4_128_WITH_MD5:
case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5:
case SSL_CK_RC2_128_CBC_WITH_MD5:
case SSL_CK_DES_64_CBC_WITH_MD5:
case SSL_CK_DES_192_EDE3_CBC_WITH_MD5:
break;
default:
SSL_DBG(("%d: SSL[%d]: ssl2_ServerSetupSessionCypher: unknown cipher=%d",
SSL_GETPID(), ss->fd, cipher));
PORT_SetError(SSL_ERROR_BAD_CLIENT);
goto loser;
}
/* For export ciphers, make sure they didn't send too much key data. */
if (ckLen != ssl_Specs[cipher].pubLen) {
SSL_DBG(("%d: SSL[%d]: odd secret key size, keySize=%d ckLen=%d!",
SSL_GETPID(), ss->fd, keySize, ckLen));
/* Somebody tried to sneak by a strange secret key */
PORT_SetError(SSL_ERROR_BAD_CLIENT);
goto loser;
}
/* allocate the buffer to hold the decrypted portion of the key. */
/* XXX Haven't done any range check on ekLen. */
kbuf = (PRUint8*) PORT_Alloc(ekLen);
if (!kbuf) {
goto loser;
}
/*
** Decrypt encrypted half of the key. Note that encrypted half has
** been made to match the modulus size of our public key using
** PKCS#1. keySize is the real size of the data that is interesting.
** NOTE: PK11_PubDecryptRaw will barf on a non-RSA key. This is
** desired behavior here.
*/
rv = PK11_PubDecryptRaw(sc->serverKey, kbuf, &el1, ekLen, ek, ekLen);
if (rv != SECSuccess)
goto hide_loser;
modulusLen = PK11_GetPrivateModulusLen(sc->serverKey);
if (modulusLen == -1) {
/* If the key was really bad, then PK11_pubDecryptRaw
* would have failed, therefore the we must assume that the card
* is just being a pain and not giving us the modulus... but it
* should be the same size as the encrypted key length, so use it
* and keep cranking */
modulusLen = ekLen;
}
/* Is the length of the decrypted data (el1) the expected value? */
if (modulusLen != el1)
goto hide_loser;
/* Cheaply verify that PKCS#1 was used to format the encryption block */
kk = kbuf + modulusLen - (keySize - ckLen);
if ((kbuf[0] != 0x00) || (kbuf[1] != 0x02) || (kk[-1] != 0x00)) {
/* Tsk tsk. */
SSL_DBG(("%d: SSL[%d]: strange encryption block",
SSL_GETPID(), ss->fd));
PORT_SetError(SSL_ERROR_BAD_CLIENT);
goto hide_loser;
}
/* Make sure we're not subject to a version rollback attack. */
if (ss->enableSSL3 || ss->enableTLS) {
PRUint8 threes[8] = { 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03 };
if (PORT_Memcmp(kk - 8 - 1, threes, 8) == 0) {
PORT_SetError(SSL_ERROR_BAD_CLIENT);
goto hide_loser;
}
}
if (0) {
hide_loser:
/* Defense against the Bleichenbacher attack.
* Provide the client with NO CLUES that the decrypted master key
* was erroneous. Don't send any error messages.
* Instead, Generate a completely bogus master key .
*/
PK11_GenerateRandom(kbuf, ekLen);
}
/*
** Construct master key out of the pieces.
*/
if (ckLen) {
PORT_Memcpy(mkbuf, ck, ckLen);
}
PORT_Memcpy(mkbuf+ckLen, kk, keySize-ckLen);
/* Fill in session-id */
rv = ssl2_FillInSID(sid, cipher, mkbuf, keySize, ca, caLen,
keyBits, keyBits - (ckLen<<3),
sec->authAlgorithm, sec->authKeyBits,
sec->keaType, sec->keaKeyBits);
if (rv != SECSuccess) {
goto loser;
}
/* Create session ciphers */
rv = ssl2_CreateSessionCypher(ss, sid, PR_FALSE);
if (rv != SECSuccess) {
goto loser;
}
SSL_TRC(1, ("%d: SSL[%d]: server, using %s cipher, clear=%d total=%d",
SSL_GETPID(), ss->fd, ssl_cipherName[cipher],
ckLen<<3, keySize<<3));
rv = SECSuccess;
goto done;
loser:
rv = SECFailure;
done:
PORT_Free(kbuf);
return rv;
}
/************************************************************************/
/*
** Rewrite the incoming cipher specs, comparing to list of specs we support,
** (ss->cipherSpecs) and eliminating anything we don't support
**
* Note: Our list may contain SSL v3 ciphers.
* We MUST NOT match on any of those.
* Fortunately, this is easy to detect because SSLv3 ciphers have zero
* in the first byte, and none of the SSLv2 ciphers do.
*
* Called from ssl2_HandleClientHelloMessage().
*/
static int
ssl2_QualifyCypherSpecs(sslSocket *ss,
PRUint8 * cs, /* cipher specs in client hello msg. */
int csLen)
{
PRUint8 * ms;
PRUint8 * hs;
PRUint8 * qs;
int mc;
int hc;
PRUint8 qualifiedSpecs[ssl2_NUM_SUITES_IMPLEMENTED * 3];
PORT_Assert( ssl_Have1stHandshakeLock(ss) );
PORT_Assert( ssl_HaveRecvBufLock(ss) );
if (!ss->cipherSpecs) {
ssl2_ConstructCipherSpecs(ss);
}
PRINT_BUF(10, (ss, "specs from client:", cs, csLen));
qs = qualifiedSpecs;
ms = ss->cipherSpecs;
for (mc = ss->sizeCipherSpecs; mc > 0; mc -= 3, ms += 3) {
if (ms[0] == 0)
continue;
for (hs = cs, hc = csLen; hc > 0; hs += 3, hc -= 3) {
if ((hs[0] == ms[0]) &&
(hs[1] == ms[1]) &&
(hs[2] == ms[2])) {
/* Copy this cipher spec into the "keep" section */
qs[0] = hs[0];
qs[1] = hs[1];
qs[2] = hs[2];
qs += 3;
break;
}
}
}
hc = qs - qualifiedSpecs;
PRINT_BUF(10, (ss, "qualified specs from client:", qualifiedSpecs, hc));
PORT_Memcpy(cs, qualifiedSpecs, hc);
return hc;
}
/*
** Pick the best cipher we can find, given the array of server cipher
** specs. Returns cipher number (e.g. SSL_CK_*), or -1 for no overlap.
** If succesful, stores the master key size (bytes) in *pKeyLen.
**
** This is correct only for the client side, but presently
** this function is only called from
** ssl2_ClientSetupSessionCypher() <- ssl2_HandleServerHelloMessage()
**
** Note that most servers only return a single cipher suite in their
** ServerHello messages. So, the code below for finding the "best" cipher
** suite usually has only one choice. The client and server should send
** their cipher suite lists sorted in descending order by preference.
*/
static int
ssl2_ChooseSessionCypher(sslSocket *ss,
int hc, /* number of cs's in hs. */
PRUint8 * hs, /* server hello's cipher suites. */
int * pKeyLen) /* out: sym key size in bytes. */
{
PRUint8 * ms;
unsigned int i;
int bestKeySize;
int bestRealKeySize;
int bestCypher;
int keySize;
int realKeySize;
PRUint8 * ohs = hs;
PORT_Assert( ssl_Have1stHandshakeLock(ss) );
PORT_Assert( ssl_HaveRecvBufLock(ss) );
if (!ss->cipherSpecs) {
ssl2_ConstructCipherSpecs(ss);
}
if (!ss->preferredCipher) {
const PRUint8 * preferred = implementedCipherSuites;
unsigned int allowed = ss->allowedByPolicy & ss->chosenPreference &
SSL_CB_IMPLEMENTED;
if (allowed) {
for (i = ssl2_NUM_SUITES_IMPLEMENTED; i > 0; --i) {
if (0 != (allowed & (1U << preferred[0]))) {
ss->preferredCipher = preferred;
break;
}
preferred += 3;
}
}
}
/*
** Scan list of ciphers recieved from peer and look for a match in
** our list.
* Note: Our list may contain SSL v3 ciphers.
* We MUST NOT match on any of those.
* Fortunately, this is easy to detect because SSLv3 ciphers have zero
* in the first byte, and none of the SSLv2 ciphers do.
*/
bestKeySize = bestRealKeySize = 0;
bestCypher = -1;
while (--hc >= 0) {
for (i = 0, ms = ss->cipherSpecs; i < ss->sizeCipherSpecs; i += 3, ms += 3) {
if ((hs[0] == ss->preferredCipher[0]) &&
(hs[1] == ss->preferredCipher[1]) &&
(hs[2] == ss->preferredCipher[2]) &&
hs[0] != 0) {
/* Pick this cipher immediately! */
*pKeyLen = (((hs[1] << 8) | hs[2]) + 7) >> 3;
return hs[0];
}
if ((hs[0] == ms[0]) && (hs[1] == ms[1]) && (hs[2] == ms[2]) &&
hs[0] != 0) {
/* Found a match */
/* Use secret keySize to determine which cipher is best */
realKeySize = (hs[1] << 8) | hs[2];
switch (hs[0]) {
case SSL_CK_RC4_128_EXPORT40_WITH_MD5:
case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5:
keySize = 40;
break;
default:
keySize = realKeySize;
break;
}
if (keySize > bestKeySize) {
bestCypher = hs[0];
bestKeySize = keySize;
bestRealKeySize = realKeySize;
}
}
}
hs += 3;
}
if (bestCypher < 0) {
/*
** No overlap between server and client. Re-examine server list
** to see what kind of ciphers it does support so that we can set
** the error code appropriately.
*/
if ((ohs[0] == SSL_CK_RC4_128_WITH_MD5) ||
(ohs[0] == SSL_CK_RC2_128_CBC_WITH_MD5)) {
PORT_SetError(SSL_ERROR_US_ONLY_SERVER);
} else if ((ohs[0] == SSL_CK_RC4_128_EXPORT40_WITH_MD5) ||
(ohs[0] == SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5)) {
PORT_SetError(SSL_ERROR_EXPORT_ONLY_SERVER);
} else {
PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
}
SSL_DBG(("%d: SSL[%d]: no cipher overlap", SSL_GETPID(), ss->fd));
goto loser;
}
*pKeyLen = (bestRealKeySize + 7) >> 3;
return bestCypher;
loser:
return -1;
}
static SECStatus
ssl2_ClientHandleServerCert(sslSocket *ss, PRUint8 *certData, int certLen)
{
CERTCertificate *cert = NULL;
SECItem certItem;
PORT_Assert(ss->sec != 0);
certItem.data = certData;
certItem.len = certLen;
/* decode the certificate */
cert = CERT_NewTempCertificate(ss->dbHandle, &certItem, NULL,
PR_FALSE, PR_TRUE);
if (cert == NULL) {
SSL_DBG(("%d: SSL[%d]: decode of server certificate fails",
SSL_GETPID(), ss->fd));
PORT_SetError(SSL_ERROR_BAD_CERTIFICATE);
return SECFailure;
}
#ifdef TRACE
{
if (ssl_trace >= 1) {
char *issuer;
char *subject;
issuer = CERT_NameToAscii(&cert->issuer);
subject = CERT_NameToAscii(&cert->subject);
SSL_TRC(1,("%d: server certificate issuer: '%s'",
SSL_GETPID(), issuer ? issuer : "OOPS"));
SSL_TRC(1,("%d: server name: '%s'",
SSL_GETPID(), subject ? subject : "OOPS"));
PORT_Free(issuer);
PORT_Free(subject);
}
}
#endif
ss->sec->peerCert = cert;
return SECSuccess;
}
/*
* Format one block of data for public/private key encryption using
* the rules defined in PKCS #1. SSL2 does this itself to handle the
* rollback detection.
*/
#define RSA_BLOCK_MIN_PAD_LEN 8
#define RSA_BLOCK_FIRST_OCTET 0x00
#define RSA_BLOCK_AFTER_PAD_OCTET 0x00
#define RSA_BLOCK_PUBLIC_OCTET 0x02
unsigned char *
ssl_FormatSSL2Block(unsigned modulusLen, SECItem *data)
{
unsigned char *block;
unsigned char *bp;
int padLen;
SECStatus rv;
int i;
PORT_Assert (data->len <= (modulusLen - (3 + RSA_BLOCK_MIN_PAD_LEN)));
block = (unsigned char *) PORT_Alloc(modulusLen);
if (block == NULL)
return NULL;
bp = block;
/*
* All RSA blocks start with two octets:
* 0x00 || BlockType
*/
*bp++ = RSA_BLOCK_FIRST_OCTET;
*bp++ = RSA_BLOCK_PUBLIC_OCTET;
/*
* 0x00 || BT || Pad || 0x00 || ActualData
* 1 1 padLen 1 data->len
* Pad is all non-zero random bytes.
*/
padLen = modulusLen - data->len - 3;
PORT_Assert (padLen >= RSA_BLOCK_MIN_PAD_LEN);
rv = PK11_GenerateRandom(bp, padLen);
if (rv == SECFailure) goto loser;
/* replace all the 'zero' bytes */
for (i = 0; i < padLen; i++) {
while (bp[i] == RSA_BLOCK_AFTER_PAD_OCTET) {
rv = PK11_GenerateRandom(bp+i, 1);
if (rv == SECFailure) goto loser;
}
}
bp += padLen;
*bp++ = RSA_BLOCK_AFTER_PAD_OCTET;
PORT_Memcpy (bp, data->data, data->len);
return block;
loser:
if (block) PORT_Free(block);
return NULL;
}
/*
** Given the server's public key and cipher specs, generate a session key
** that is ready to use for encrypting/decrypting the byte stream. At
** the same time, generate the SSL_MT_CLIENT_MASTER_KEY message and
** send it to the server.
**
** Called from ssl2_HandleServerHelloMessage()
*/
static SECStatus
ssl2_ClientSetupSessionCypher(sslSocket *ss, PRUint8 *cs, int csLen)
{
sslSessionID * sid;
sslSecurityInfo * sec;
PRUint8 * ca; /* points to iv data, or NULL if none. */
PRUint8 * ekbuf = 0;
CERTCertificate * cert = 0;
SECKEYPublicKey * serverKey = 0;
unsigned modulusLen = 0;
SECStatus rv;
int cipher;
int keyLen; /* cipher symkey size in bytes. */
int ckLen; /* publicly reveal this many bytes of key. */
int caLen; /* length of IV data at *ca. */
int nc;
unsigned char *eblock; /* holds unencrypted PKCS#1 formatted key. */
SECItem rek; /* holds portion of symkey to be encrypted. */
PRUint8 keyData[SSL_MAX_MASTER_KEY_BYTES];
PRUint8 iv [8];
PORT_Assert( ssl_Have1stHandshakeLock(ss) );
PORT_Assert((ss->sec != 0));
eblock = NULL;
sec = ss->sec;
sid = sec->ci.sid;
PORT_Assert(sid != 0);
cert = sec->peerCert;
serverKey = CERT_ExtractPublicKey(cert);
if (!serverKey) {
SSL_DBG(("%d: SSL[%d]: extract public key failed: error=%d",
SSL_GETPID(), ss->fd, PORT_GetError()));
PORT_SetError(SSL_ERROR_BAD_CERTIFICATE);
rv = SECFailure;
goto loser2;
}
sec->authAlgorithm = ssl_sign_rsa;
sec->keaType = ssl_kea_rsa;
sec->keaKeyBits = \
sec->authKeyBits = SECKEY_PublicKeyStrength(serverKey) * BPB;
/* Choose a compatible cipher with the server */
nc = csLen / 3;
cipher = ssl2_ChooseSessionCypher(ss, nc, cs, &keyLen);
if (cipher < 0) {
/* ssl2_ChooseSessionCypher has set error code. */
ssl2_SendErrorMessage(ss, SSL_PE_NO_CYPHERS);
goto loser;
}
/* Generate the random keys */
PK11_GenerateRandom(keyData, sizeof(keyData));
/*
** Next, carve up the keys into clear and encrypted portions. The
** clear data is taken from the start of keyData and the encrypted
** portion from the remainder. Note that each of these portions is
** carved in half, one half for the read-key and one for the
** write-key.
*/
ca = 0;
/* We know that cipher is a legit value here, because
* ssl2_ChooseSessionCypher doesn't return bogus values.
*/
ckLen = ssl_Specs[cipher].pubLen; /* cleartext key length. */
caLen = ssl_Specs[cipher].ivLen; /* IV length. */
if (caLen) {
PORT_Assert(sizeof iv >= caLen);
PK11_GenerateRandom(iv, caLen);
ca = iv;
}
/* Fill in session-id */
rv = ssl2_FillInSID(sid, cipher, keyData, keyLen,
ca, caLen, keyLen << 3, (keyLen - ckLen) << 3,
sec->authAlgorithm, sec->authKeyBits,
sec->keaType, sec->keaKeyBits);
if (rv != SECSuccess) {
goto loser;
}
SSL_TRC(1, ("%d: SSL[%d]: client, using %s cipher, clear=%d total=%d",
SSL_GETPID(), ss->fd, ssl_cipherName[cipher],
ckLen<<3, keyLen<<3));
/* Now setup read and write ciphers */
rv = ssl2_CreateSessionCypher(ss, sid, PR_TRUE);
if (rv != SECSuccess) {
goto loser;
}
/*
** Fill in the encryption buffer with some random bytes. Then
** copy in the portion of the session key we are encrypting.
*/
modulusLen = SECKEY_PublicKeyStrength(serverKey);
rek.data = keyData + ckLen;
rek.len = keyLen - ckLen;
eblock = ssl_FormatSSL2Block(modulusLen, &rek);
if (eblock == NULL)
goto loser;
/* Set up the padding for version 2 rollback detection. */
/* XXX We should really use defines here */
if (ss->enableSSL3 || ss->enableTLS) {
PORT_Assert((modulusLen - rek.len) > 12);
PORT_Memset(eblock + modulusLen - rek.len - 8 - 1, 0x03, 8);
}
ekbuf = (PRUint8*) PORT_Alloc(modulusLen);
if (!ekbuf)
goto loser;
PRINT_BUF(10, (ss, "master key encryption block:",
eblock, modulusLen));
/* Encrypt ekitem */
rv = PK11_PubEncryptRaw(serverKey, ekbuf, eblock, modulusLen,
ss->pkcs11PinArg);
if (rv)
goto loser;
/* Now we have everything ready to send */
rv = ssl2_SendSessionKeyMessage(ss, cipher, keyLen << 3, ca, caLen,
keyData, ckLen, ekbuf, modulusLen);
if (rv != SECSuccess) {
goto loser;
}
rv = SECSuccess;
goto done;
loser:
rv = SECFailure;
loser2:
done:
PORT_Memset(keyData, 0, sizeof(keyData));
PORT_ZFree(ekbuf, modulusLen);
PORT_ZFree(eblock, modulusLen);
SECKEY_DestroyPublicKey(serverKey);
return rv;
}
/************************************************************************/
/*
* Called from ssl2_HandleMessage in response to SSL_MT_SERVER_FINISHED message.
* Caller holds recvBufLock and handshakeLock
*/
static void
ssl2_ClientRegSessionID(sslSocket *ss, PRUint8 *s)
{
sslSecurityInfo *sec;
sslSessionID *sid;
PORT_Assert((ss->sec != 0));
sec = ss->sec;
sid = sec->ci.sid;
/* Record entry in nonce cache */
if (sid->peerCert == NULL) {
PORT_Memcpy(sid->u.ssl2.sessionID, s, sizeof(sid->u.ssl2.sessionID));
sid->peerCert = CERT_DupCertificate(sec->peerCert);
}
if (!ss->noCache)
(*sec->cache)(sid);
}
/* Called from ssl2_HandleMessage() */
static SECStatus
ssl2_TriggerNextMessage(sslSocket *ss)
{
sslConnectInfo * ci;
SECStatus rv;
PORT_Assert( ssl_Have1stHandshakeLock(ss) );
PORT_Assert((ss->sec != 0));
ci = &ss->sec->ci;
if ((ci->requiredElements & CIS_HAVE_CERTIFICATE) &&
!(ci->sentElements & CIS_HAVE_CERTIFICATE)) {
ci->sentElements |= CIS_HAVE_CERTIFICATE;
rv = ssl2_SendCertificateRequestMessage(ss);
return rv;
}
return SECSuccess;
}
/* See if it's time to send our finished message, or if the handshakes are
** complete. Send finished message if appropriate.
** Returns SECSuccess unless anything goes wrong.
**
** Called from ssl2_HandleMessage,
** ssl2_HandleVerifyMessage
** ssl2_HandleServerHelloMessage
** ssl2_HandleClientSessionKeyMessage
** ssl2_RestartHandshakeAfterCertReq
** ssl2_RestartHandshakeAfterServerCert
*/
static SECStatus
ssl2_TryToFinish(sslSocket *ss)
{
sslSecurityInfo *sec;
sslConnectInfo * ci;
SECStatus rv;
char e, ef;
PORT_Assert( ssl_Have1stHandshakeLock(ss) );
PORT_Assert((ss->sec != 0));
sec = ss->sec;
ci = &sec->ci;
e = ci->elements;
ef = e | CIS_HAVE_FINISHED;
if ((ef & ci->requiredElements) == ci->requiredElements) {
if (sec->isServer) {
/* Send server finished message if we already didn't */
rv = ssl2_SendServerFinishedMessage(ss);
} else {
/* Send client finished message if we already didn't */
rv = ssl2_SendClientFinishedMessage(ss);
}
if (rv != SECSuccess) {
return rv;
}
if ((e & ci->requiredElements) == ci->requiredElements) {
/* Totally finished */
ss->handshake = 0;
return SECSuccess;
}
}
return SECSuccess;
}
/*
** Called from ssl2_HandleRequestCertificate
** ssl2_RestartHandshakeAfterCertReq
*/
static SECStatus
ssl2_SignResponse(sslSocket *ss,
SECKEYPrivateKey *key,
SECItem *response)
{
SGNContext * sgn = NULL;
sslConnectInfo * ci;
sslSecurityInfo *sec;
PRUint8 * challenge;
unsigned int len;
SECStatus rv = SECFailure;
PORT_Assert( ssl_Have1stHandshakeLock(ss) );
sec = ss->sec;
ci = &sec->ci;
challenge = ci->serverChallenge;
len = ci->serverChallengeLen;
/* Sign the expected data... */
sgn = SGN_NewContext(SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION,key);
if (!sgn)
goto done;
rv = SGN_Begin(sgn);
if (rv != SECSuccess)
goto done;
rv = SGN_Update(sgn, ci->readKey, ci->keySize);
if (rv != SECSuccess)
goto done;
rv = SGN_Update(sgn, ci->writeKey, ci->keySize);
if (rv != SECSuccess)
goto done;
rv = SGN_Update(sgn, challenge, len);
if (rv != SECSuccess)
goto done;
rv = SGN_Update(sgn,
sec->peerCert->derCert.data, sec->peerCert->derCert.len);
if (rv != SECSuccess)
goto done;
rv = SGN_End(sgn, response);
if (rv != SECSuccess)
goto done;
done:
SGN_DestroyContext(sgn, PR_TRUE);
return rv == SECSuccess ? SECSuccess : SECFailure;
}
/*
** Try to handle a request-certificate message. Get client's certificate
** and private key and sign a message for the server to see.
** Caller must hold handshakeLock
**
** Called from ssl2_HandleMessage().
*/
static int
ssl2_HandleRequestCertificate(sslSocket *ss)
{
CERTCertificate * cert = NULL; /* app-selected client cert. */
SECKEYPrivateKey *key = NULL; /* priv key for cert. */
SECStatus rv;
SECItem response;
int ret = 0;
PRUint8 authType;
/*
* These things all need to be initialized before we can "goto loser".
*/
response.data = NULL;
PORT_Assert((ss->sec != 0));
if (!ss->sec) {
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
return SECFailure;
}
/* get challenge info from connectionInfo */
authType = ss->sec->ci.authType;
if (authType != SSL_AT_MD5_WITH_RSA_ENCRYPTION) {
SSL_TRC(7, ("%d: SSL[%d]: unsupported auth type 0x%x", SSL_GETPID(),
ss->fd, authType));
goto no_cert_error;
}
/* Get certificate and private-key from client */
if (!ss->getClientAuthData) {
SSL_TRC(7, ("%d: SSL[%d]: client doesn't support client-auth",
SSL_GETPID(), ss->fd));
goto no_cert_error;
}
ret = (*ss->getClientAuthData)(ss->getClientAuthDataArg, ss->fd,
NULL, &cert, &key);
if ( ret == SECWouldBlock ) {
ssl_SetAlwaysBlock(ss);
goto done;
}
if (ret) {
goto no_cert_error;
}
rv = ssl2_SignResponse(ss, key, &response);
if ( rv != SECSuccess ) {
ret = -1;
goto loser;
}
/* Send response message */
ret = ssl2_SendCertificateResponseMessage(ss, &cert->derCert, &response);
/* Now, remember the cert we sent. But first, forget any previous one. */
if (ss->sec->localCert) {
CERT_DestroyCertificate(ss->sec->localCert);
}
ss->sec->localCert = CERT_DupCertificate(cert);
PORT_Assert(!ss->sec->ci.sid->localCert);
if (ss->sec->ci.sid->localCert) {
CERT_DestroyCertificate(ss->sec->ci.sid->localCert);
}
ss->sec->ci.sid->localCert = cert;
cert = NULL;
goto done;
no_cert_error:
SSL_TRC(7, ("%d: SSL[%d]: no certificate (ret=%d)", SSL_GETPID(),
ss->fd, ret));
ret = ssl2_SendErrorMessage(ss, SSL_PE_NO_CERTIFICATE);
loser:
done:
if ( cert ) {
CERT_DestroyCertificate(cert);
}
if ( key ) {
SECKEY_DestroyPrivateKey(key);
}
if ( response.data ) {
PORT_Free(response.data);
}
return ret;
}
/*
** Called from ssl2_HandleMessage for SSL_MT_CLIENT_CERTIFICATE message.
** Caller must hold HandshakeLock and RecvBufLock, since cd and response
** are contained in the gathered input data.
*/
static SECStatus
ssl2_HandleClientCertificate(sslSocket * ss,
PRUint8 certType, /* XXX unused */
PRUint8 * cd,
unsigned int cdLen,
PRUint8 * response,
unsigned int responseLen)
{
sslSecurityInfo *sec = ss->sec;
sslConnectInfo * ci;
CERTCertificate *cert = NULL;
SECKEYPublicKey *pubKey = NULL;
VFYContext * vfy = NULL;
SECItem * derCert;
SECStatus rv = SECFailure;
SECItem certItem;
SECItem rep;
PORT_Assert( ssl_Have1stHandshakeLock(ss) );
PORT_Assert( ssl_HaveRecvBufLock(ss) );
/* Extract the certificate */
certItem.data = cd;
certItem.len = cdLen;
cert = CERT_NewTempCertificate(ss->dbHandle, &certItem, NULL,
PR_FALSE, PR_TRUE);
if (cert == NULL) {
goto loser;
}
/* save the certificate, since the auth routine will need it */
sec->peerCert = cert;
/* Extract the public key */
pubKey = CERT_ExtractPublicKey(cert);
if (!pubKey)
goto loser;
/* Verify the response data... */
rep.data = response;
rep.len = responseLen;
/* SSL 2.0 only supports RSA certs, so we don't have to worry about
* DSA here. */
vfy = VFY_CreateContext(pubKey, &rep, SEC_OID_PKCS1_RSA_ENCRYPTION,
ss->pkcs11PinArg);
if (!vfy)
goto loser;
rv = VFY_Begin(vfy);
if (rv)
goto loser;
ci = &sec->ci;
rv = VFY_Update(vfy, ci->readKey, ci->keySize);
if (rv)
goto loser;
rv = VFY_Update(vfy, ci->writeKey, ci->keySize);
if (rv)
goto loser;
rv = VFY_Update(vfy, ci->serverChallenge, SSL_CHALLENGE_BYTES);
if (rv)
goto loser;
derCert = &ss->serverCerts[kt_rsa].serverCert->derCert;
rv = VFY_Update(vfy, derCert->data, derCert->len);
if (rv)
goto loser;
rv = VFY_End(vfy);
if (rv)
goto loser;
/* Now ask the server application if it likes the certificate... */
rv = (SECStatus) (*ss->authCertificate)(ss->authCertificateArg,
ss->fd, PR_TRUE, PR_TRUE);
/* Hey, it liked it. */
if (SECSuccess == rv)
goto done;
loser:
sec->peerCert = NULL;
CERT_DestroyCertificate(cert);
done:
VFY_DestroyContext(vfy, PR_TRUE);
SECKEY_DestroyPublicKey(pubKey);
return rv;
}
/*
** Handle remaining messages between client/server. Process finished
** messages from either side and any authentication requests.
** This should only be called for SSLv2 handshake messages,
** not for application data records.
** Caller must hold handshake lock.
**
** Called from ssl_Do1stHandshake().
**
*/
static SECStatus
ssl2_HandleMessage(sslSocket *ss)
{
sslSecurityInfo *sec;
sslConnectInfo * ci;
sslGather * gs;
PRUint8 * data;
PRUint8 * cid;
unsigned len, certType, certLen, responseLen;
int rv;
int rv2;
PORT_Assert( ssl_Have1stHandshakeLock(ss) );
PORT_Assert((ss->sec != 0) && (ss->gather != 0));
ssl_GetRecvBufLock(ss);
sec = ss->sec;
gs = ss->gather;
ci = &sec->ci;
data = gs->buf.buf + gs->recordOffset;
if (gs->recordLen < 1) {
goto bad_peer;
}
SSL_TRC(3, ("%d: SSL[%d]: received %d message",
SSL_GETPID(), ss->fd, data[0]));
DUMP_MSG(29, (ss, data, gs->recordLen));
switch (data[0]) {
case SSL_MT_CLIENT_FINISHED:
if (ci->elements & CIS_HAVE_FINISHED) {
SSL_DBG(("%d: SSL[%d]: dup client-finished message",
SSL_GETPID(), ss->fd));
goto bad_peer;
}
/* See if nonce matches */
len = gs->recordLen - 1;
cid = data + 1;
if ((len != sizeof(ci->connectionID)) ||
(PORT_Memcmp(ci->connectionID, cid, len) != 0)) {
SSL_DBG(("%d: SSL[%d]: bad connection-id", SSL_GETPID(), ss->fd));
PRINT_BUF(5, (ss, "sent connection-id",
ci->connectionID, sizeof(ci->connectionID)));
PRINT_BUF(5, (ss, "rcvd connection-id", cid, len));
goto bad_peer;
}
SSL_TRC(5, ("%d: SSL[%d]: got client finished, waiting for 0x%d",
SSL_GETPID(), ss->fd, ci->requiredElements ^ ci->elements));
ci->elements |= CIS_HAVE_FINISHED;
break;
case SSL_MT_SERVER_FINISHED:
if (ci->elements & CIS_HAVE_FINISHED) {
SSL_DBG(("%d: SSL[%d]: dup server-finished message",
SSL_GETPID(), ss->fd));
goto bad_peer;
}
if (gs->recordLen - 1 != SSL2_SESSIONID_BYTES) {
SSL_DBG(("%d: SSL[%d]: bad server-finished message, len=%d",
SSL_GETPID(), ss->fd, gs->recordLen));
goto bad_peer;
}
ssl2_ClientRegSessionID(ss, data+1);
SSL_TRC(5, ("%d: SSL[%d]: got server finished, waiting for 0x%d",
SSL_GETPID(), ss->fd, ci->requiredElements ^ ci->elements));
ci->elements |= CIS_HAVE_FINISHED;
break;
case SSL_MT_REQUEST_CERTIFICATE:
len = gs->recordLen - 2;
if ((len != SSL_MIN_CHALLENGE_BYTES) ||
(len > SSL_MAX_CHALLENGE_BYTES)) {
/* Bad challenge */
SSL_DBG(("%d: SSL[%d]: bad cert request message: code len=%d",
SSL_GETPID(), ss->fd, len));
goto bad_peer;
}
/* save auth request info */
ci->authType = data[1];
ci->serverChallengeLen = len;
PORT_Memcpy(ci->serverChallenge, data + 2, len);
rv = ssl2_HandleRequestCertificate(ss);
if (rv == SECWouldBlock) {
SSL_TRC(3, ("%d: SSL[%d]: async cert request",
SSL_GETPID(), ss->fd));
/* someone is handling this asynchronously */
ssl_ReleaseRecvBufLock(ss);
return SECWouldBlock;
}
if (rv) {
SET_ERROR_CODE
goto loser;
}
break;
case SSL_MT_CLIENT_CERTIFICATE:
if (!ss->authCertificate) {
/* Server asked for authentication and can't handle it */
PORT_SetError(SSL_ERROR_BAD_SERVER);
goto loser;
}
if (gs->recordLen < SSL_HL_CLIENT_CERTIFICATE_HBYTES) {
SET_ERROR_CODE
goto loser;
}
certType = data[1];
certLen = (data[2] << 8) | data[3];
responseLen = (data[4] << 8) | data[5];
if (certType != SSL_CT_X509_CERTIFICATE) {
PORT_SetError(SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE);
goto loser;
}
rv = ssl2_HandleClientCertificate(ss, data[1],
data + SSL_HL_CLIENT_CERTIFICATE_HBYTES,
certLen,
data + SSL_HL_CLIENT_CERTIFICATE_HBYTES + certLen,
responseLen);
if (rv) {
rv2 = ssl2_SendErrorMessage(ss, SSL_PE_BAD_CERTIFICATE);
SET_ERROR_CODE
goto loser;
}
ci->elements |= CIS_HAVE_CERTIFICATE;
break;
case SSL_MT_ERROR:
rv = (data[1] << 8) | data[2];
SSL_TRC(2, ("%d: SSL[%d]: got error message, error=0x%x",
SSL_GETPID(), ss->fd, rv));
/* Convert protocol error number into API error number */
switch (rv) {
case SSL_PE_NO_CYPHERS:
rv = SSL_ERROR_NO_CYPHER_OVERLAP;
break;
case SSL_PE_NO_CERTIFICATE:
rv = SSL_ERROR_NO_CERTIFICATE;
break;
case SSL_PE_BAD_CERTIFICATE:
rv = SSL_ERROR_BAD_CERTIFICATE;
break;
case SSL_PE_UNSUPPORTED_CERTIFICATE_TYPE:
rv = SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE;
break;
default:
goto bad_peer;
}
/* XXX make certificate-request optionally fail... */
PORT_SetError(rv);
goto loser;
default:
SSL_DBG(("%d: SSL[%d]: unknown message %d",
SSL_GETPID(), ss->fd, data[0]));
goto loser;
}
SSL_TRC(3, ("%d: SSL[%d]: handled %d message, required=0x%x got=0x%x",
SSL_GETPID(), ss->fd, data[0],
ci->requiredElements, ci->elements));
rv = ssl2_TryToFinish(ss);
if (rv != SECSuccess)
goto loser;
ss->gather->recordLen = 0;
ssl_ReleaseRecvBufLock(ss);
if (ss->handshake == 0) {
return SECSuccess;
}
ss->handshake = ssl_GatherRecord1stHandshake;
ss->nextHandshake = ssl2_HandleMessage;
return ssl2_TriggerNextMessage(ss);
bad_peer:
PORT_SetError(sec->isServer ? SSL_ERROR_BAD_CLIENT : SSL_ERROR_BAD_SERVER);
/* FALL THROUGH */
loser:
ssl_ReleaseRecvBufLock(ss);
return SECFailure;
}
/************************************************************************/
/* Called from ssl_Do1stHandshake, after ssl2_HandleServerHelloMessage or
** ssl2_RestartHandshakeAfterServerCert.
*/
static SECStatus
ssl2_HandleVerifyMessage(sslSocket *ss)
{
sslConnectInfo * ci;
sslGather * gs;
PRUint8 * data;
SECStatus rv;
PORT_Assert( ssl_Have1stHandshakeLock(ss) );
ssl_GetRecvBufLock(ss);
PORT_Assert((ss->sec != 0) && (ss->gather != 0));
ci = &ss->sec->ci;
gs = ss->gather;
data = gs->buf.buf + gs->recordOffset;
DUMP_MSG(29, (ss, data, gs->recordLen));
if ((gs->recordLen != 1 + SSL_CHALLENGE_BYTES) ||
(data[0] != SSL_MT_SERVER_VERIFY) ||
PORT_Memcmp(data+1, ci->clientChallenge, SSL_CHALLENGE_BYTES)) {
/* Bad server */
PORT_SetError(SSL_ERROR_BAD_SERVER);
goto loser;
}
ci->elements |= CIS_HAVE_VERIFY;
SSL_TRC(5, ("%d: SSL[%d]: got server-verify, required=0x%d got=0x%x",
SSL_GETPID(), ss->fd, ci->requiredElements,
ci->elements));
rv = ssl2_TryToFinish(ss);
if (rv)
goto loser;
ss->gather->recordLen = 0;
ssl_ReleaseRecvBufLock(ss);
if (ss->handshake == 0) {
return SECSuccess;
}
ss->handshake = ssl_GatherRecord1stHandshake;
ss->nextHandshake = ssl2_HandleMessage;
return SECSuccess;
loser:
ssl_ReleaseRecvBufLock(ss);
return SECFailure;
}
/* Not static because ssl2_GatherData() tests ss->nextHandshake for this value.
* ICK!
* Called from ssl_Do1stHandshake after ssl2_BeginClientHandshake()
*/
SECStatus
ssl2_HandleServerHelloMessage(sslSocket *ss)
{
sslSecurityInfo *sec;
sslConnectInfo * ci;
sslGather * gs;
sslSessionID * sid;
PRUint8 * cert;
PRUint8 * cs;
PRUint8 * data;
SECStatus rv;
int needed, sidHit, certLen, csLen, cidLen, certType, err;
PORT_Assert((ss->sec != 0) && (ss->gather != 0));
PORT_Assert( ssl_Have1stHandshakeLock(ss) );
if (!ss->enableSSL2) {
PORT_SetError(SSL_ERROR_SSL2_DISABLED);
return SECFailure;
}
ssl_GetRecvBufLock(ss);
sec = ss->sec;
ci = &sec->ci;
gs = ss->gather;
PORT_Assert(ci->sid != 0);
sid = ci->sid;
data = gs->buf.buf + gs->recordOffset;
DUMP_MSG(29, (ss, data, gs->recordLen));
/* Make sure first message has some data and is the server hello message */
if ((gs->recordLen < SSL_HL_SERVER_HELLO_HBYTES)
|| (data[0] != SSL_MT_SERVER_HELLO)) {
if ((data[0] == SSL_MT_ERROR) && (gs->recordLen == 3)) {
err = (data[1] << 8) | data[2];
if (err == SSL_PE_NO_CYPHERS) {
PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
goto loser;
}
}
goto bad_server;
}
sidHit = data[1];
certType = data[2];
ss->version = (data[3] << 8) | data[4];
certLen = (data[5] << 8) | data[6];
csLen = (data[7] << 8) | data[8];
cidLen = (data[9] << 8) | data[10];
cert = data + SSL_HL_SERVER_HELLO_HBYTES;
cs = cert + certLen;
SSL_TRC(5,
("%d: SSL[%d]: server-hello, hit=%d vers=%x certLen=%d csLen=%d cidLen=%d",
SSL_GETPID(), ss->fd, sidHit, ss->version, certLen,
csLen, cidLen));
if (ss->version != SSL_LIBRARY_VERSION_2) {
if (ss->version < SSL_LIBRARY_VERSION_2) {
SSL_TRC(3, ("%d: SSL[%d]: demoting self (%x) to server version (%x)",
SSL_GETPID(), ss->fd, SSL_LIBRARY_VERSION_2,
ss->version));
} else {
SSL_TRC(1, ("%d: SSL[%d]: server version is %x (we are %x)",
SSL_GETPID(), ss->fd, ss->version, SSL_LIBRARY_VERSION_2));
/* server claims to be newer but does not follow protocol */
PORT_SetError(SSL_ERROR_UNSUPPORTED_VERSION);
goto loser;
}
}
/* Save connection-id for later */
PORT_Memcpy(ci->connectionID, cs + csLen, sizeof(ci->connectionID));
/* See if session-id hit */
needed = CIS_HAVE_MASTER_KEY | CIS_HAVE_FINISHED | CIS_HAVE_VERIFY;
if (sidHit) {
if (certLen || csLen) {
/* Uh oh - bogus server */
SSL_DBG(("%d: SSL[%d]: client, huh? hit=%d certLen=%d csLen=%d",
SSL_GETPID(), ss->fd, sidHit, certLen, csLen));
goto bad_server;
}
/* Total winner. */
SSL_TRC(1, ("%d: SSL[%d]: client, using nonce for peer=0x%08x "
"port=0x%04x",
SSL_GETPID(), ss->fd, ci->peer, ci->port));
sec->peerCert = CERT_DupCertificate(sid->peerCert);
sec->authAlgorithm = sid->authAlgorithm;
sec->authKeyBits = sid->authKeyBits;
sec->keaType = sid->keaType;
sec->keaKeyBits = sid->keaKeyBits;
rv = ssl2_CreateSessionCypher(ss, sid, PR_TRUE);
if (rv != SECSuccess) {
goto loser;
}
} else {
if (certType != SSL_CT_X509_CERTIFICATE) {
PORT_SetError(SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE);
goto loser;
}
if (csLen == 0) {
PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
SSL_DBG(("%d: SSL[%d]: no cipher overlap",
SSL_GETPID(), ss->fd));
goto loser;
}
if (certLen == 0) {
SSL_DBG(("%d: SSL[%d]: client, huh? certLen=%d csLen=%d",
SSL_GETPID(), ss->fd, certLen, csLen));
goto bad_server;
}
if (sid->cached != never_cached) {
/* Forget our session-id - server didn't like it */
SSL_TRC(7, ("%d: SSL[%d]: server forgot me, uncaching session-id",
SSL_GETPID(), ss->fd));
(*sec->uncache)(sid);
ssl_FreeSID(sid);
ci->sid = sid = (sslSessionID*) PORT_ZAlloc(sizeof(sslSessionID));
if (!sid) {
goto loser;
}
sid->references = 1;
sid->addr = ci->peer;
sid->port = ci->port;
}
/* decode the server's certificate */
rv = ssl2_ClientHandleServerCert(ss, cert, certLen);
if (rv != SECSuccess) {
if (PORT_GetError() == SSL_ERROR_BAD_CERTIFICATE) {
(void) ssl2_SendErrorMessage(ss, SSL_PE_BAD_CERTIFICATE);
}
goto loser;
}
/* Setup new session cipher */
rv = ssl2_ClientSetupSessionCypher(ss, cs, csLen);
if (rv != SECSuccess) {
if (PORT_GetError() == SSL_ERROR_BAD_CERTIFICATE) {
(void) ssl2_SendErrorMessage(ss, SSL_PE_BAD_CERTIFICATE);
}
goto loser;
}
}
/* Build up final list of required elements */
ci->elements = CIS_HAVE_MASTER_KEY;
ci->requiredElements = needed;
if (!sidHit) {
/* verify the server's certificate. if sidHit, don't check signatures */
rv = (* ss->authCertificate)(ss->authCertificateArg, ss->fd,
(PRBool)(!sidHit), PR_FALSE);
if (rv) {
if (ss->handleBadCert) {
rv = (*ss->handleBadCert)(ss->badCertArg, ss->fd);
if ( rv ) {
if ( rv == SECWouldBlock ) {
/* someone will handle this connection asynchronously*/
SSL_DBG(("%d: SSL[%d]: go to async cert handler",
SSL_GETPID(), ss->fd));
ssl_ReleaseRecvBufLock(ss);
ssl_SetAlwaysBlock(ss);
return SECWouldBlock;
}
/* cert is bad */
SSL_DBG(("%d: SSL[%d]: server certificate is no good: error=%d",
SSL_GETPID(), ss->fd, PORT_GetError()));
goto loser;
}
/* cert is good */
} else {
SSL_DBG(("%d: SSL[%d]: server certificate is no good: error=%d",
SSL_GETPID(), ss->fd, PORT_GetError()));
goto loser;
}
}
}
/*
** At this point we have a completed session key and our session
** cipher is setup and ready to go. Switch to encrypted write routine
** as all future message data is to be encrypted.
*/
ssl2_UseEncryptedSendFunc(ss);
rv = ssl2_TryToFinish(ss);
if (rv != SECSuccess)
goto loser;
ss->gather->recordLen = 0;
ssl_ReleaseRecvBufLock(ss);
if (ss->handshake == 0) {
return SECSuccess;
}
SSL_TRC(5, ("%d: SSL[%d]: got server-hello, required=0x%d got=0x%x",
SSL_GETPID(), ss->fd, ci->requiredElements, ci->elements));
ss->handshake = ssl_GatherRecord1stHandshake;
ss->nextHandshake = ssl2_HandleVerifyMessage;
return SECSuccess;
bad_server:
PORT_SetError(SSL_ERROR_BAD_SERVER);
/* FALL THROUGH */
loser:
ssl_ReleaseRecvBufLock(ss);
return SECFailure;
}
/* Sends out the initial client Hello message on the connection.
* Acquires and releases the socket's xmitBufLock.
*/
SECStatus
ssl2_BeginClientHandshake(sslSocket *ss)
{
sslSecurityInfo *sec;
sslConnectInfo *ci;
sslSessionID *sid;
PRUint8 *msg;
PRUint8 *cp;
PRUint8 *localCipherSpecs = NULL;
unsigned int localCipherSize;
unsigned int i;
int sendLen, sidLen;
SECStatus rv;
PORT_Assert( ssl_Have1stHandshakeLock(ss) );
PORT_Assert((ss->sec != 0));
sec = ss->sec;
sec->isServer = 0;
sec->sendSequence = 0;
sec->rcvSequence = 0;
ssl_ChooseSessionIDProcs(sec);
ci = &sec->ci;
if (!ss->cipherSpecs) {
rv = ssl2_ConstructCipherSpecs(ss);
if (rv != SECSuccess)
goto loser;
}
/* count the SSL2 and SSL3 enabled ciphers.
* if either is zero, clear the socket's enable for that protocol.
*/
rv = ssl2_CheckConfigSanity(ss);
if (rv != SECSuccess)
goto loser;
/* Get peer name of server */
rv = ssl_GetPeerInfo(ss);
if (rv < 0) {
#ifdef HPUX11
/*
* On some HP-UX B.11.00 systems, getpeername() occasionally
* fails with ENOTCONN after a successful completion of
* non-blocking connect. I found that if we do a write()
* and then retry getpeername(), it will work.
*/
if (PR_GetError() == PR_NOT_CONNECTED_ERROR) {
char dummy;
(void) PR_Write(ss->fd->lower, &dummy, 0);
rv = ssl_GetPeerInfo(ss);
if (rv < 0) {
goto loser;
}
}
#else
goto loser;
#endif
}
SSL_TRC(3, ("%d: SSL[%d]: sending client-hello", SSL_GETPID(), ss->fd));
/* Try to find server in our session-id cache */
if (ss->noCache) {
sid = NULL;
} else {
sid = ssl_LookupSID(&ci->peer, ci->port, ss->peerID, ss->url);
}
while (sid) { /* this isn't really a loop */
/* if we're not doing this SID's protocol any more, drop it. */
if (((sid->version < SSL_LIBRARY_VERSION_3_0) && !ss->enableSSL2) ||
((sid->version == SSL_LIBRARY_VERSION_3_0) && !ss->enableSSL3) ||
((sid->version > SSL_LIBRARY_VERSION_3_0) && !ss->enableTLS)) {
sec->uncache(sid);
ssl_FreeSID(sid);
sid = NULL;
break;
}
if (ss->enableSSL2 && sid->version < SSL_LIBRARY_VERSION_3_0) {
/* If the cipher in this sid is not enabled, drop it. */
for (i = 0; i < ss->sizeCipherSpecs; i += 3) {
if (ss->cipherSpecs[i] == sid->u.ssl2.cipherType)
break;
}
if (i >= ss->sizeCipherSpecs) {
sec->uncache(sid);
ssl_FreeSID(sid);
sid = NULL;
break;
}
}
sidLen = sizeof(sid->u.ssl2.sessionID);
PRINT_BUF(4, (ss, "client, found session-id:", sid->u.ssl2.sessionID,
sidLen));
ss->version = sid->version;
PORT_Assert(!sec->localCert);
if (sec->localCert) {
CERT_DestroyCertificate(sec->localCert);
}
sec->localCert = CERT_DupCertificate(sid->localCert);
break; /* this isn't really a loop */
}
if (!sid) {
sidLen = 0;
sid = (sslSessionID*) PORT_ZAlloc(sizeof(sslSessionID));
if (!sid) {
goto loser;
}
sid->references = 1;
sid->cached = never_cached;
sid->addr = ci->peer;
sid->port = ci->port;
if (ss->peerID != NULL) {
sid->peerID = PORT_Strdup(ss->peerID);
}
if (ss->url != NULL) {
sid->urlSvrName = PORT_Strdup(ss->url);
}
}
ci->sid = sid;
PORT_Assert(sid != NULL);
if ((sid->version >= SSL_LIBRARY_VERSION_3_0 || !ss->v2CompatibleHello) &&
(ss->enableSSL3 || ss->enableTLS)) {
PORT_Assert(ss->gather != NULL);
ss->gather->state = GS_INIT;
ss->handshake = ssl_GatherRecord1stHandshake;
/* ssl3_SendClientHello will override this if it succeeds. */
ss->version = SSL_LIBRARY_VERSION_3_0;
ssl_GetXmitBufLock(ss); /***************************************/
ssl_GetSSL3HandshakeLock(ss);
rv = ssl3_SendClientHello(ss);
ssl_ReleaseSSL3HandshakeLock(ss);
ssl_ReleaseXmitBufLock(ss); /***************************************/
return rv;
}
if (!ss->cipherSpecs) {
rv = ssl2_ConstructCipherSpecs(ss);
if (rv < 0) {
return rv;
}
}
localCipherSpecs = ss->cipherSpecs;
localCipherSize = ss->sizeCipherSpecs;
sendLen = SSL_HL_CLIENT_HELLO_HBYTES + localCipherSize + sidLen +
SSL_CHALLENGE_BYTES;
/* Generate challenge bytes for server */
PK11_GenerateRandom(ci->clientChallenge, SSL_CHALLENGE_BYTES);
ssl_GetXmitBufLock(ss); /***************************************/
rv = ssl2_GetSendBuffer(ss, sendLen);
if (rv)
goto unlock_loser;
/* Construct client-hello message */
cp = msg = ci->sendBuf.buf;
msg[0] = SSL_MT_CLIENT_HELLO;
if ( ss->enableTLS ) {
ss->clientHelloVersion = SSL_LIBRARY_VERSION_3_1_TLS;
} else if ( ss->enableSSL3 ) {
ss->clientHelloVersion = SSL_LIBRARY_VERSION_3_0;
} else {
ss->clientHelloVersion = SSL_LIBRARY_VERSION_2;
}
msg[1] = MSB(ss->clientHelloVersion);
msg[2] = LSB(ss->clientHelloVersion);
msg[3] = MSB(localCipherSize);
msg[4] = LSB(localCipherSize);
msg[5] = MSB(sidLen);
msg[6] = LSB(sidLen);
msg[7] = MSB(SSL_CHALLENGE_BYTES);
msg[8] = LSB(SSL_CHALLENGE_BYTES);
cp += SSL_HL_CLIENT_HELLO_HBYTES;
PORT_Memcpy(cp, localCipherSpecs, localCipherSize);
cp += localCipherSize;
if (sidLen) {
PORT_Memcpy(cp, sid->u.ssl2.sessionID, sidLen);
cp += sidLen;
}
PORT_Memcpy(cp, ci->clientChallenge, SSL_CHALLENGE_BYTES);
/* Send it to the server */
DUMP_MSG(29, (ss, msg, sendLen));
ss->handshakeBegun = 1;
rv = (*sec->send)(ss, msg, sendLen, 0);
ssl_ReleaseXmitBufLock(ss); /***************************************/
if (rv < 0) {
goto loser;
}
rv = ssl3_StartHandshakeHash(ss, msg, sendLen);
if (rv < 0) {
goto loser;
}
/* Setup to receive servers hello message */
ssl_GetRecvBufLock(ss);
ss->gather->recordLen = 0;
ssl_ReleaseRecvBufLock(ss);
ss->handshake = ssl_GatherRecord1stHandshake;
ss->nextHandshake = ssl2_HandleServerHelloMessage;
return SECSuccess;
unlock_loser:
ssl_ReleaseXmitBufLock(ss);
loser:
return SECFailure;
}
/************************************************************************/
/* Handle the CLIENT-MASTER-KEY message.
** Acquires and releases RecvBufLock.
** Called from ssl2_HandleClientHelloMessage().
*/
static SECStatus
ssl2_HandleClientSessionKeyMessage(sslSocket *ss)
{
sslConnectInfo * ci;
PRUint8 * data;
sslGather * gs;
unsigned int caLen;
unsigned int ckLen;
unsigned int ekLen;
unsigned int keySize;
int cipher;
SECStatus rv;
PORT_Assert((ss->sec != 0) && (ss->gather != 0));
ssl_GetRecvBufLock(ss);
gs = ss->gather;
ci = &ss->sec->ci;
data = gs->buf.buf + gs->recordOffset;
DUMP_MSG(29, (ss, data, gs->recordLen));
if ((gs->recordLen < SSL_HL_CLIENT_MASTER_KEY_HBYTES)
|| (data[0] != SSL_MT_CLIENT_MASTER_KEY)) {
goto bad_client;
}
cipher = data[1];
keySize = (data[2] << 8) | data[3];
ckLen = (data[4] << 8) | data[5];
ekLen = (data[6] << 8) | data[7];
caLen = (data[8] << 8) | data[9];
SSL_TRC(5, ("%d: SSL[%d]: session-key, cipher=%d keySize=%d ckLen=%d ekLen=%d caLen=%d",
SSL_GETPID(), ss->fd, cipher, keySize, ckLen, ekLen, caLen));
if (gs->recordLen <
SSL_HL_CLIENT_MASTER_KEY_HBYTES + ckLen + ekLen + caLen) {
SSL_DBG(("%d: SSL[%d]: protocol size mismatch dataLen=%d",
SSL_GETPID(), ss->fd, gs->recordLen));
goto bad_client;
}
/* Use info from client to setup session key */
/* XXX should validate cipher&keySize are in our array */
rv = ssl2_ServerSetupSessionCypher(ss, cipher, keySize,
data + SSL_HL_CLIENT_MASTER_KEY_HBYTES, ckLen,
data + SSL_HL_CLIENT_MASTER_KEY_HBYTES + ckLen, ekLen,
data + SSL_HL_CLIENT_MASTER_KEY_HBYTES + ckLen + ekLen, caLen);
ss->gather->recordLen = 0; /* we're done with this record. */
ssl_ReleaseRecvBufLock(ss);
if (rv != SECSuccess) {
goto loser;
}
ci->elements |= CIS_HAVE_MASTER_KEY;
ssl2_UseEncryptedSendFunc(ss);
/* Send server verify message now that keys are established */
rv = ssl2_SendServerVerifyMessage(ss);
if (rv != SECSuccess)
goto loser;
rv = ssl2_TryToFinish(ss);
if (rv != SECSuccess)
goto loser;
if (ss->handshake == 0) {
return SECSuccess;
}
SSL_TRC(5, ("%d: SSL[%d]: server: waiting for elements=0x%d",
SSL_GETPID(), ss->fd, ci->requiredElements ^ ci->elements));
ss->handshake = ssl_GatherRecord1stHandshake;
ss->nextHandshake = ssl2_HandleMessage;
return ssl2_TriggerNextMessage(ss);
bad_client:
ssl_ReleaseRecvBufLock(ss);
PORT_SetError(SSL_ERROR_BAD_CLIENT);
/* FALLTHROUGH */
loser:
return SECFailure;
}
/*
* attempt to restart the handshake after asynchronously handling
* a request for the client's certificate.
*
* inputs:
* cert Client cert chosen by application.
* key Private key associated with cert.
*
* XXX: need to make ssl2 and ssl3 versions of this function agree on whether
* they take the reference, or bump the ref count!
*
* Return value: XXX
*
* Caller holds 1stHandshakeLock.
*/
int
ssl2_RestartHandshakeAfterCertReq(sslSocket * ss,
CERTCertificate * cert,
SECKEYPrivateKey * key)
{
int ret;
SECStatus rv = SECSuccess;
SECItem response;
if (ss->version >= SSL_LIBRARY_VERSION_3_0)
return SECFailure;
response.data = NULL;
PORT_Assert((ss->sec != 0));
if (ss->sec == NULL)
return SECFailure;
/* generate error if no cert or key */
if ( ( cert == NULL ) || ( key == NULL ) ) {
goto no_cert;
}
/* generate signed response to the challenge */
rv = ssl2_SignResponse(ss, key, &response);
if ( rv != SECSuccess ) {
goto no_cert;
}
/* Send response message */
ret = ssl2_SendCertificateResponseMessage(ss, &cert->derCert, &response);
if (ret) {
goto no_cert;
}
/* try to finish the handshake */
ret = ssl2_TryToFinish(ss);
if (ret) {
goto loser;
}
/* done with handshake */
if (ss->handshake == 0) {
ret = SECSuccess;
goto done;
}
/* continue handshake */
ssl_GetRecvBufLock(ss);
ss->gather->recordLen = 0;
ssl_ReleaseRecvBufLock(ss);
ss->handshake = ssl_GatherRecord1stHandshake;
ss->nextHandshake = ssl2_HandleMessage;
ret = ssl2_TriggerNextMessage(ss);
goto done;
no_cert:
/* no cert - send error */
ret = ssl2_SendErrorMessage(ss, SSL_PE_NO_CERTIFICATE);
goto done;
loser:
ret = SECFailure;
done:
/* free allocated data */
if ( response.data ) {
PORT_Free(response.data);
}
return ret;
}
/* restart an SSL connection that we stopped to run certificate dialogs
** XXX Need to document here how an application marks a cert to show that
** the application has accepted it (overridden CERT_VerifyCert).
*
* Return value: XXX
*
* Caller holds 1stHandshakeLock.
*/
int
ssl2_RestartHandshakeAfterServerCert(sslSocket *ss)
{
int rv = SECSuccess;
if (ss->version >= SSL_LIBRARY_VERSION_3_0)
return SECFailure;
/* SSL 2
** At this point we have a completed session key and our session
** cipher is setup and ready to go. Switch to encrypted write routine
** as all future message data is to be encrypted.
*/
ssl2_UseEncryptedSendFunc(ss);
rv = ssl2_TryToFinish(ss);
if (rv == SECSuccess && ss->handshake != NULL) {
/* handshake is not yet finished. */
SSL_TRC(5, ("%d: SSL[%d]: got server-hello, required=0x%d got=0x%x",
SSL_GETPID(), ss->fd, ss->sec->ci.requiredElements,
ss->sec->ci.elements));
ssl_GetRecvBufLock(ss);
ss->gather->recordLen = 0; /* mark it all used up. */
ssl_ReleaseRecvBufLock(ss);
ss->handshake = ssl_GatherRecord1stHandshake;
ss->nextHandshake = ssl2_HandleVerifyMessage;
}
return rv;
}
/*
** Handle the initial hello message from the client
**
** not static because ssl2_GatherData() tests ss->nextHandshake for this value.
*/
SECStatus
ssl2_HandleClientHelloMessage(sslSocket *ss)
{
sslSecurityInfo *sec;
sslConnectInfo *ci;
sslGather *gs;
sslSessionID *sid;
sslServerCerts * sc;
CERTCertificate *serverCert;
PRUint8 *msg;
PRUint8 *data;
PRUint8 *cs;
PRUint8 *sd;
PRUint8 *cert = NULL;
PRUint8 *challenge;
unsigned int challengeLen;
SECStatus rv;
int csLen;
int sendLen;
int sdLen;
int certLen;
int pid;
int sent;
int gotXmitBufLock = 0;
#if defined(SOLARIS) && defined(i386)
volatile PRUint8 hit;
#else
int hit;
#endif
PRUint8 csImpl[sizeof implementedCipherSuites];
PORT_Assert( ssl_Have1stHandshakeLock(ss) );
PORT_Assert((ss->sec != 0) && (ss->gather != 0));
sec = ss->sec;
ci = &sec->ci;
sc = ss->serverCerts + kt_rsa;
serverCert = sc->serverCert;
ssl_GetRecvBufLock(ss);
gs = ss->gather;
data = gs->buf.buf + gs->recordOffset;
DUMP_MSG(29, (ss, data, gs->recordLen));
/* Make sure first message has some data and is the client hello message */
if ((gs->recordLen < SSL_HL_CLIENT_HELLO_HBYTES)
|| (data[0] != SSL_MT_CLIENT_HELLO)) {
goto bad_client;
}
/* Get peer name of client */
rv = ssl_GetPeerInfo(ss);
if (rv != SECSuccess) {
goto loser;
}
/* Examine version information */
/*
* See if this might be a V2 client hello asking to use the V3 protocol
*/
if ((data[0] == SSL_MT_CLIENT_HELLO) &&
(data[1] >= MSB(SSL_LIBRARY_VERSION_3_0)) &&
(ss->enableSSL3 || ss->enableTLS)) {
rv = ssl3_HandleV2ClientHello(ss, data, gs->recordLen);
if (rv != SECFailure) { /* Success */
ss->handshake = NULL;
ss->nextHandshake = ssl_GatherRecord1stHandshake;
ss->securityHandshake = NULL;
ss->gather->state = GS_INIT;
/* ssl3_HandleV3ClientHello has set ss->version,
** and has gotten us a brand new sid.
*/
ss->sec->ci.sid->version = ss->version;
}
ssl_ReleaseRecvBufLock(ss);
return rv;
}
/* Previously, there was a test here to see if SSL2 was enabled.
** If not, an error code was set, and SECFailure was returned,
** without sending any error code to the other end of the connection.
** That test has been removed. If SSL2 has been disabled, there
** should be no SSL2 ciphers enabled, and consequently, the code
** below should send the ssl2 error message SSL_PE_NO_CYPHERS.
** We now believe this is the correct thing to do, even when SSL2
** has been explicitly disabled by the application.
*/
/* Extract info from message */
ss->version = (data[1] << 8) | data[2];
/* If some client thinks ssl v2 is 2.0 instead of 0.2, we'll allow it. */
if (ss->version >= SSL_LIBRARY_VERSION_3_0) {
ss->version = SSL_LIBRARY_VERSION_2;
}
csLen = (data[3] << 8) | data[4];
sdLen = (data[5] << 8) | data[6];
challengeLen = (data[7] << 8) | data[8];
cs = data + SSL_HL_CLIENT_HELLO_HBYTES;
sd = cs + csLen;
challenge = sd + sdLen;
PRINT_BUF(7, (ss, "server, client session-id value:", sd, sdLen));
if ((unsigned)gs->recordLen !=
SSL_HL_CLIENT_HELLO_HBYTES + csLen + sdLen + challengeLen) {
SSL_DBG(("%d: SSL[%d]: bad client hello message, len=%d should=%d",
SSL_GETPID(), ss->fd, gs->recordLen,
SSL_HL_CLIENT_HELLO_HBYTES+csLen+sdLen+challengeLen));
goto bad_client;
}
SSL_TRC(3, ("%d: SSL[%d]: client version is %x",
SSL_GETPID(), ss->fd, ss->version));
if (ss->version != SSL_LIBRARY_VERSION_2) {
if (ss->version > SSL_LIBRARY_VERSION_2) {
/*
** Newer client than us. Things are ok because new clients
** are required to be backwards compatible with old servers.
** Change version number to our version number so that client
** knows whats up.
*/
ss->version = SSL_LIBRARY_VERSION_2;
} else {
SSL_TRC(1, ("%d: SSL[%d]: client version is %x (we are %x)",
SSL_GETPID(), ss->fd, ss->version, SSL_LIBRARY_VERSION_2));
PORT_SetError(SSL_ERROR_UNSUPPORTED_VERSION);
goto loser;
}
}
/* Qualify cipher specs before returning them to client */
csLen = ssl2_QualifyCypherSpecs(ss, cs, csLen);
if (csLen == 0) {
/* no overlap, send client our list of supported SSL v2 ciphers. */
cs = csImpl;
csLen = sizeof implementedCipherSuites;
PORT_Memcpy(cs, implementedCipherSuites, csLen);
csLen = ssl2_QualifyCypherSpecs(ss, cs, csLen);
if (csLen == 0) {
/* We don't support any SSL v2 ciphers! */
ssl2_SendErrorMessage(ss, SSL_PE_NO_CYPHERS);
PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
goto loser;
}
/* Since this handhsake is going to fail, don't cache it. */
ss->noCache = 1;
}
/* Squirrel away the challenge for later */
PORT_Memcpy(ci->clientChallenge, challenge, challengeLen);
/* Examine message and see if session-id is good */
ci->elements = 0;
if (sdLen > 0 && !ss->noCache) {
SSL_TRC(7, ("%d: SSL[%d]: server, lookup client session-id for 0x%08x%08x%08x%08x",
SSL_GETPID(), ss->fd, ci->peer.pr_s6_addr32[0],
ci->peer.pr_s6_addr32[1], ci->peer.pr_s6_addr32[2],
ci->peer.pr_s6_addr32[3]));
sid = (*ssl_sid_lookup)(&ci->peer, sd, sdLen, ss->dbHandle);
} else {
sid = NULL;
}
if (sid) {
/* Got a good session-id. Short cut! */
SSL_TRC(1, ("%d: SSL[%d]: server, using session-id for 0x%08x (age=%d)",
SSL_GETPID(), ss->fd, ci->peer,
ssl_Time() - sid->creationTime));
PRINT_BUF(1, (ss, "session-id value:", sd, sdLen));
ci->sid = sid;
ci->elements = CIS_HAVE_MASTER_KEY;
hit = 1;
certLen = 0;
csLen = 0;
sec->authAlgorithm = sid->authAlgorithm;
sec->authKeyBits = sid->authKeyBits;
sec->keaType = sid->keaType;
sec->keaKeyBits = sid->keaKeyBits;
rv = ssl2_CreateSessionCypher(ss, sid, PR_FALSE);
if (rv != SECSuccess) {
goto loser;
}
} else {
SECItem * derCert = &serverCert->derCert;
SSL_TRC(7, ("%d: SSL[%d]: server, lookup nonce missed",
SSL_GETPID(), ss->fd));
if (!serverCert) {
SET_ERROR_CODE
goto loser;
}
hit = 0;
sid = (sslSessionID*) PORT_ZAlloc(sizeof(sslSessionID));
if (!sid) {
goto loser;
}
sid->references = 1;
sid->addr = ci->peer;
sid->port = ci->port;
/* Invent a session-id */
ci->sid = sid;
PK11_GenerateRandom(sid->u.ssl2.sessionID+2, SSL2_SESSIONID_BYTES-2);
pid = SSL_GETPID();
sid->u.ssl2.sessionID[0] = MSB(pid);
sid->u.ssl2.sessionID[1] = LSB(pid);
cert = derCert->data;
certLen = derCert->len;
/* pretend that server sids remember the local cert. */
PORT_Assert(!sid->localCert);
if (sid->localCert) {
CERT_DestroyCertificate(sid->localCert);
}
sid->localCert = CERT_DupCertificate(serverCert);
sec->authAlgorithm = ssl_sign_rsa;
sec->keaType = ssl_kea_rsa;
sec->keaKeyBits = \
sec->authKeyBits = ss->serverCerts[kt_rsa].serverKeyBits;
}
/* server sids don't remember the local cert, so whether we found
** a sid or not, just "remember" we used the rsa server cert.
*/
if (sec->localCert) {
CERT_DestroyCertificate(sec->localCert);
}
sec->localCert = CERT_DupCertificate(serverCert);
/* Build up final list of required elements */
ci->requiredElements = CIS_HAVE_MASTER_KEY | CIS_HAVE_FINISHED;
if (ss->requestCertificate) {
ci->requiredElements |= CIS_HAVE_CERTIFICATE;
}
ci->sentElements = 0;
/* Send hello message back to client */
sendLen = SSL_HL_SERVER_HELLO_HBYTES + certLen + csLen
+ SSL_CONNECTIONID_BYTES;
ssl_GetXmitBufLock(ss); gotXmitBufLock = 1;
rv = ssl2_GetSendBuffer(ss, sendLen);
if (rv != SECSuccess) {
goto loser;
}
SSL_TRC(3, ("%d: SSL[%d]: sending server-hello (%d)",
SSL_GETPID(), ss->fd, sendLen));
msg = ci->sendBuf.buf;
msg[0] = SSL_MT_SERVER_HELLO;
msg[1] = hit;
msg[2] = SSL_CT_X509_CERTIFICATE;
msg[3] = MSB(ss->version);
msg[4] = LSB(ss->version);
msg[5] = MSB(certLen);
msg[6] = LSB(certLen);
msg[7] = MSB(csLen);
msg[8] = LSB(csLen);
msg[9] = MSB(SSL_CONNECTIONID_BYTES);
msg[10] = LSB(SSL_CONNECTIONID_BYTES);
if (certLen) {
PORT_Memcpy(msg+SSL_HL_SERVER_HELLO_HBYTES, cert, certLen);
}
if (csLen) {
PORT_Memcpy(msg+SSL_HL_SERVER_HELLO_HBYTES+certLen, cs, csLen);
}
PORT_Memcpy(msg+SSL_HL_SERVER_HELLO_HBYTES+certLen+csLen, ci->connectionID,
SSL_CONNECTIONID_BYTES);
DUMP_MSG(29, (ss, msg, sendLen));
ss->handshakeBegun = 1;
sent = (*sec->send)(ss, msg, sendLen, 0);
if (sent < 0) {
goto loser;
}
ssl_ReleaseXmitBufLock(ss); gotXmitBufLock = 0;
ss->gather->recordLen = 0;
ss->handshake = ssl_GatherRecord1stHandshake;
if (hit) {
/* Old SID Session key is good. Go encrypted */
ssl2_UseEncryptedSendFunc(ss);
/* Send server verify message now that keys are established */
rv = ssl2_SendServerVerifyMessage(ss);
if (rv != SECSuccess)
goto loser;
ss->nextHandshake = ssl2_HandleMessage;
ssl_ReleaseRecvBufLock(ss);
rv = ssl2_TriggerNextMessage(ss);
return rv;
}
ss->nextHandshake = ssl2_HandleClientSessionKeyMessage;
ssl_ReleaseRecvBufLock(ss);
return SECSuccess;
bad_client:
PORT_SetError(SSL_ERROR_BAD_CLIENT);
/* FALLTHROUGH */
loser:
if (gotXmitBufLock) {
ssl_ReleaseXmitBufLock(ss); gotXmitBufLock = 0;
}
SSL_TRC(10, ("%d: SSL[%d]: server, wait for client-hello lossage",
SSL_GETPID(), ss->fd));
ssl_ReleaseRecvBufLock(ss);
return SECFailure;
}
SECStatus
ssl2_BeginServerHandshake(sslSocket *ss)
{
sslSecurityInfo *sec;
sslConnectInfo * ci;
SECStatus rv;
sslServerCerts * rsaAuth = ss->serverCerts + kt_rsa;
PORT_Assert((ss->sec != 0));
sec = ss->sec;
ci = &sec->ci;
sec->isServer = 1;
ssl_ChooseSessionIDProcs(sec);
sec->sendSequence = 0;
sec->rcvSequence = 0;
/* don't turn on SSL2 if we don't have an RSA key and cert */
if (!rsaAuth->serverKey || !rsaAuth->serverCert) {
ss->enableSSL2 = PR_FALSE;
}
if (!ss->cipherSpecs) {
rv = ssl2_ConstructCipherSpecs(ss);
if (rv != SECSuccess)
goto loser;
}
/* count the SSL2 and SSL3 enabled ciphers.
* if either is zero, clear the socket's enable for that protocol.
*/
rv = ssl2_CheckConfigSanity(ss);
if (rv != SECSuccess)
goto loser;
/*
** Generate connection-id. Always do this, even if things fail
** immediately. This way the random number generator is always
** rolling around, every time we get a connection.
*/
PK11_GenerateRandom(ci->connectionID, sizeof(ci->connectionID));
ss->gather->recordLen = 0;
ss->handshake = ssl_GatherRecord1stHandshake;
ss->nextHandshake = ssl2_HandleClientHelloMessage;
return SECSuccess;
loser:
return SECFailure;
}
/* This function doesn't really belong in this file.
** It's here to keep AIX compilers from optimizing it away,
** and not including it in the DSO.
*/
#include "nss.h"
extern const char __nss_ssl_rcsid[];
extern const char __nss_ssl_sccsid[];
PRBool
NSSSSL_VersionCheck(const char *importedVersion)
{
/*
* This is the secret handshake algorithm.
*
* This release has a simple version compatibility
* check algorithm. This release is not backward
* compatible with previous major releases. It is
* not compatible with future major, minor, or
* patch releases.
*/
int vmajor = 0, vminor = 0, vpatch = 0;
const char *ptr = importedVersion;
volatile char c; /* force a reference that won't get optimized away */
c = __nss_ssl_rcsid[0] + __nss_ssl_sccsid[0];
return NSS_VersionCheck(importedVersion);
}