netscape-revival
security/lib/ssl/ssl3con.c
/*
* SSL3 stuff.
*
* Copyright © 1995 Netscape Communications Corporation, all rights reserved.
*
* $Id: ssl3con.c,v 1.79.2.8 1997/05/24 00:24:37 jwz Exp $
*/
#include "xp.h"
#include "cert.h"
#include "ssl.h"
#include "crypto.h"
#include "key.h"
#include "secder.h"
#include "sechash.h"
#include "rsa.h"
#include "secrng.h"
#include "secitem.h"
#include "sslimpl.h"
#include "sslproto.h"
#include "sslerr.h"
#include "prtime.h"
#ifdef NSPR20
#include "prinrval.h"
#endif /* NSPR20 */
#include "pk11func.h"
extern int SEC_ERROR_IO;
extern int SEC_ERROR_INVALID_ARGS;
extern int SEC_ERROR_INVALID_KEY;
extern int SEC_ERROR_LIBRARY_FAILURE;
extern int SEC_ERROR_NO_MEMORY;
extern int SEC_ERROR_BAD_KEY;
extern int SSL_ERROR_BAD_CERTIFICATE;
extern int SSL_ERROR_BAD_CLIENT;
extern int SSL_ERROR_BAD_SERVER;
extern int SSL_ERROR_EXPORT_ONLY_SERVER;
extern int SSL_ERROR_NO_CERTIFICATE;
extern int SSL_ERROR_NO_CYPHER_OVERLAP;
extern int SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE;
extern int SSL_ERROR_UNSUPPORTED_VERSION;
extern int SSL_ERROR_US_ONLY_SERVER;
extern int SSL_ERROR_BAD_MAC_READ;
extern int SSL_ERROR_BAD_MAC_ALERT;
extern int SSL_ERROR_BAD_CERT_ALERT;
extern int SSL_ERROR_REVOKED_CERT_ALERT;
extern int SSL_ERROR_EXPIRED_CERT_ALERT;
extern int XP_ERRNO_EIO;
extern int XP_ERRNO_EWOULDBLOCK;
/*
* XXX 'sizeof' is most likely wrong in each of its uses below. It will
* work on a 32 bit architecture (probably)
*/
static SECStatus ssl3_InitState(SSLSocket *ss);
static SECStatus ssl3_SendFinished(SSLSocket *ss);
static SECStatus ssl3_SendCertificate(SSLSocket *ss);
static SECStatus ssl3_SendCertificateRequest(SSLSocket *ss);
static SECStatus ssl3_SendServerKeyExchange(SSLSocket *ss);
static SECStatus ssl3_SendServerHelloDone(SSLSocket *ss);
static SECStatus SSL3_SendServerHello(SSLSocket *ss);
static SSLSessionID *ssl3_NewSessionID(SSLSocket *ss, PRBool is_server);
static SECStatus ssl3_HandshakeFailure(SSLSocket *ss);
static SECStatus ssl3_GenerateSessionKeys(SSLSocket *ss, SSL3CipherSpec *spec,
const SECItem *pms);
static SECStatus Null_Cipher(void *ctx, unsigned char *output,
unsigned int *outputLen, unsigned maxOutputLen,
unsigned char *input, unsigned inputLen);
#define MAX_SEND_BUF_LENGTH 32000 /* watch for 16-bit integer overflow */
#define MIN_SEND_BUF_LENGTH 4000
#define MAX_CIPHER_SUITES 20
/* This list of SSL3 cipher suites is sorted in descending order of
* precedence (desirability). It only includes cipher suites we implement.
*/
static SSL3CipherSuiteCfg cipherSuites[] = {
#ifdef FORTEZZA
{ SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA, SSL_ALLOWED, PR_TRUE },
{ SSL_FORTEZZA_DMS_WITH_RC4_128_SHA, SSL_ALLOWED, PR_TRUE },
#endif /* FORTEZZA */
{ SSL_RSA_WITH_RC4_128_MD5, SSL_ALLOWED, PR_TRUE },
{ SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_ALLOWED, PR_TRUE },
{ SSL_RSA_WITH_DES_CBC_SHA, SSL_ALLOWED, PR_TRUE },
{ SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_ALLOWED, PR_TRUE },
{ SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, SSL_ALLOWED, PR_TRUE },
{ SSL_RSA_WITH_NULL_MD5, SSL_ALLOWED, PR_FALSE },
#ifdef FORTEZZA
{ SSL_FORTEZZA_DMS_WITH_NULL_SHA, SSL_ALLOWED, PR_FALSE },
#endif /* FORTEZZA */
};
static int cipherSuiteCount = sizeof(cipherSuites) / sizeof(cipherSuites[0]);
static /*SSL3CompressionMethod*/ uint8 compressions [] = {
compression_null
};
static int compressionMethodsCount =
sizeof(compressions) / sizeof(compressions[0]);
static /*SSL3ClientCertificateType */ uint8 certificate_types [] = {
ct_RSA_sign,
ct_DSS_sign,
#ifdef FORTEZZA
ct_Fortezza,
#endif
};
#if 0
static int certificateTypeCount =
sizeof(certificate_types) / sizeof(certificate_types[0]);
#endif
#define EXPORT_RSA_KEY_LENGTH 64
/*
* make sure there is room in the write buffer for padding and
* other compression and cryptographic expansions
*/
#define SSL3_BUFFER_FUDGE 100
SECKEYPrivateKey *exportKey;
SECKEYPublicKey *exportPubKey;
static SECKEYPrivateKey *usKey;
#ifdef FORTEZZA
static SECKEYPrivateKey *fortezzaServerKey;
#endif
/* This is a hack to make sure we don't do double handshakes for US policy */
static PRBool policy_some_restricted = PR_FALSE;
CERTCertificateList *ssl3_server_cert_chain = NULL;
CERTDistNames *ssl3_server_ca_list = NULL;
#ifdef FORTEZZA
CERTCertificateList *ssl3_fortezza_server_cert_chain = NULL;
CERTDistNames *ssl3_fortezza_server_ca_list = NULL;
#endif
/* indexed by SSL3BulkCipher */
static const SSL3BulkCipherDef bulk_cipher_defs[] = {
{cipher_null, calg_null, 0, 0, type_stream, 0, 0, kg_null},
{cipher_rc4, calg_rc4, 16, 16, type_stream, 0, 0, kg_strong},
{cipher_rc4_40, calg_rc4, 16, 5, type_stream, 0, 0, kg_export},
{cipher_rc2, calg_rc2, 16, 16, type_block, 8, 8, kg_strong},
{cipher_rc2_40, calg_rc2, 16, 5, type_block, 8, 8, kg_export},
{cipher_des, calg_des, 8, 8, type_block, 8, 8, kg_strong},
{cipher_3des, calg_3des, 24, 24, type_block, 8, 8, kg_strong},
{cipher_des40, calg_des, 8, 5, type_block, 8, 8, kg_export},
{cipher_idea, calg_idea, 16, 16, type_block, 8, 8, kg_strong},
{cipher_fortezza, calg_fortezza, 12, 12, type_block, 24, 8, kg_null},
{cipher_missing, calg_null, 0, 0, type_stream, 0, 0, kg_null},
};
static const SSL3KEADef kea_defs[] = { /* indexed by SSL3KeyExchangeAlgorithm */
{kea_null, kt_null, sign_null, PR_FALSE, 0},
{kea_rsa, kt_rsa, sign_rsa, PR_FALSE, 0},
{kea_rsa_export, kt_rsa, sign_rsa, PR_TRUE, 512},
{kea_dh_dss, kt_dh, sign_dsa, PR_FALSE, 0},
{kea_dh_dss_export, kt_dh, sign_dsa, PR_TRUE, 512},
{kea_dh_rsa, kt_dh, sign_rsa, PR_FALSE, 0},
{kea_dh_rsa_export, kt_dh, sign_rsa, PR_TRUE, 512},
{kea_dhe_dss, kt_dh, sign_dsa, PR_FALSE, 0},
{kea_dhe_dss_export, kt_dh, sign_dsa, PR_TRUE, 512},
{kea_dhe_rsa, kt_dh, sign_rsa, PR_FALSE, 0},
{kea_dhe_rsa_export, kt_dh, sign_rsa, PR_TRUE, 512},
{kea_dh_anon, kt_dh, sign_null, PR_FALSE, 0},
{kea_dh_anon_export, kt_dh, sign_null, PR_TRUE, 512},
{kea_fortezza, kt_fortezza, sign_dsa, PR_FALSE, 0},
};
static const SSL3MACDef mac_defs[] = { /* indexed by MACAlgorithm */
{mac_null, 0},
{mac_md5, 48},
{mac_sha, 40},
};
/* must use ssl_LookupCipherSuiteDef to access */
static const SSL3CipherSuiteDef cipher_suite_defs[] = {
{SSL_NULL_WITH_NULL_NULL,
cipher_null, mac_null, kea_null},
{SSL_RSA_WITH_NULL_MD5,
cipher_null, mac_md5, kea_rsa},
{SSL_RSA_WITH_NULL_SHA,
cipher_null, mac_sha, kea_rsa},
{SSL_RSA_EXPORT_WITH_RC4_40_MD5,
cipher_rc4_40, mac_md5, kea_rsa_export},
{SSL_RSA_WITH_RC4_128_MD5,
cipher_rc4, mac_md5, kea_rsa},
{SSL_RSA_WITH_RC4_128_SHA,
cipher_rc4, mac_sha, kea_rsa},
{SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5,
cipher_rc2_40, mac_md5, kea_rsa_export},
#if 0 /* not implemented */
{SSL_RSA_WITH_IDEA_CBC_SHA,
cipher_idea, mac_sha, kea_rsa},
{SSL_RSA_EXPORT_WITH_DES40_CBC_SHA,
cipher_des40, mac_sha, kea_rsa_export},
#endif
{SSL_RSA_WITH_DES_CBC_SHA,
cipher_des, mac_sha, kea_rsa},
{SSL_RSA_WITH_3DES_EDE_CBC_SHA,
cipher_3des, mac_sha, kea_rsa},
#if 0 /* not implemented */
{SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA,
cipher_des40, mac_sha, kea_dh_dss_export},
{SSL_DH_DSS_DES_CBC_SHA,
cipher_des, mac_sha, kea_dh_dss},
{SSL_DH_DSS_3DES_CBC_SHA,
cipher_3des, mac_sha, kea_dh_dss},
{SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA,
cipher_des40, mac_sha, kea_dh_rsa_export},
{SSL_DH_RSA_DES_CBC_SHA,
cipher_des, mac_sha, kea_dh_rsa},
{SSL_DH_RSA_3DES_CBC_SHA,
cipher_des, mac_sha, kea_dh_rsa},
{SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA,
cipher_des40, mac_sha, kea_dh_dss_export},
{SSL_DHE_DSS_DES_CBC_SHA,
cipher_des, mac_sha, kea_dh_dss},
{SSL_DHE_DSS_3DES_CBC_SHA,
cipher_3des, mac_sha, kea_dh_dss},
{SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA,
cipher_des40, mac_sha, kea_dh_rsa_export},
{SSL_DHE_RSA_DES_CBC_SHA,
cipher_des, mac_sha, kea_dh_rsa},
{SSL_DHE_RSA_3DES_CBC_SHA,
cipher_des, mac_sha, kea_dh_rsa},
{SSL_DH_ANON_EXPORT_RC4_40_MD5,
cipher_rc4_40, mac_md5, kea_dh_anon_export},
{SSL_DH_ANON_EXPORT_RC4_40_MD5,
cipher_rc4, mac_md5, kea_dh_anon_export},
{SSL_DH_ANON_EXPORT_WITH_DES40_CBC_SHA,
cipher_des40, mac_sha, kea_dh_anon_export},
{SSL_DH_ANON_DES_CBC_SHA,
cipher_des, mac_sha, kea_dh_anon},
{SSL_DH_ANON_3DES_CBC_SHA,
cipher_3des, mac_sha, kea_dh_anon},
#endif
#ifdef FORTEZZA
{SSL_FORTEZZA_DMS_WITH_NULL_SHA,
cipher_null, mac_sha, kea_fortezza},
{SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA,
cipher_fortezza, mac_sha, kea_fortezza},
{SSL_FORTEZZA_DMS_WITH_RC4_SHA,
cipher_rc4, mac_sha, kea_fortezza},
#endif
};
char *ssl3_cipherName[] = {
"NULL",
"RC4",
"RC4-40",
"RC2-CBC",
"RC2-CBC-40",
"DES-CBC",
"3DES-EDE-CBC",
"DES-CBC-40",
"IDEA-CBC",
"FORTEZZA",
"missing"
};
static const SSL3CipherSuiteDef *
ssl_LookupCipherSuiteDef(SSL3CipherSuite suite)
{
int cipher_suite_def_len =
sizeof(cipher_suite_defs) / sizeof(cipher_suite_defs[0]);
int i;
for (i = 0; i < cipher_suite_def_len; i++) {
if (cipher_suite_defs[i].cipher_suite == suite)
return &cipher_suite_defs[i];
}
PORT_Assert(PR_FALSE); /* We should never get here. */
return NULL;
}
/* Find the cipher configuration struct associate with suite */
static SSL3CipherSuiteCfg *
ssl_LookupCipherSuiteCfg(SSL3CipherSuite suite)
{
int i;
for (i = 0; i < cipherSuiteCount; i++) {
if (cipherSuites[i].cipher_suite == suite)
return &cipherSuites[i];
}
PORT_Assert(PR_FALSE); /* We should never get here. */
return NULL;
}
/* return PR_TRUE if suite matches policy and enabled state */
static PRBool
config_match(SSL3CipherSuiteCfg *suite, int policy, PRBool enabled)
{
if (policy == SSL_NOT_ALLOWED) {
return ((suite->policy == policy) && (suite->enabled == enabled));
} else {
return ((suite->policy != SSL_NOT_ALLOWED) &&
(suite->policy <= policy) && (suite->enabled == enabled));
}
}
/* return number of cipher suites that match policy and enabled state */
static int
count_cipher_suites(int policy, PRBool enabled)
{
int i, count = 0;
for (i = 0; i < cipherSuiteCount; i++) {
if (config_match(&cipherSuites[i], policy, enabled))
count++;
}
return count;
}
/*
* Null compression, mac and encryption functions
*/
static SECStatus
Null_Cipher(void *ctx, unsigned char *output,
unsigned int *outputLen, unsigned maxOutputLen,
unsigned char *input, unsigned inputLen)
{
*outputLen = inputLen;
if (input != output)
PORT_Memcpy(output, input, inputLen);
return SECSuccess;
}
/*
* SSL3 Utility functions
*/
static SECStatus
ssl3_GetNewRandom(SSL3Random *random)
{
#ifdef NSPR20
PRIntervalTime gmt = PR_IntervalToSeconds(PR_IntervalNow());
#else
int64 now = PR_NowS();
uint32 gmt;
LL_L2I(gmt, now);
#endif /* NSPR20 */
random->rand[0] = (unsigned char)(gmt >> 24);
random->rand[1] = (unsigned char)(gmt >> 16);
random->rand[2] = (unsigned char)(gmt >> 8);
random->rand[3] = (unsigned char)(gmt);
/* first 4 bytes are reserverd for time */
return PK11_GenerateRandom(&random->rand[4], SSL3_RANDOM_LENGTH - 4);
}
static SECStatus
ssl3_SignHashes(SSL3Hashes *hash, SECKEYPrivateKey *key, SECItem *buf)
{
SECStatus rv;
SECItem hashItem;
switch (key->keyType) {
case rsaKey:
hashItem.data = (unsigned char *)hash;
hashItem.len = sizeof(SSL3Hashes);
break;
case dsaKey:
case fortezzaKey:
hashItem.data = (unsigned char *)hash->sha;
hashItem.len = sizeof(hash->sha);
break;
default:
PORT_SetError(SEC_ERROR_INVALID_KEY);
return SECFailure;
}
rv = PK11_Sign(key, buf, &hashItem);
PRINT_BUF(60, (NULL, "signed hashes", (unsigned char*)buf->data, buf->len));
/*if (rv < 0) PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); */
return rv;
}
static SECStatus
ssl3_CheckSignedHashes(SSL3Hashes *hash, CERTCertificate *cert, SECItem *buf,
void *wincx)
{
int rv;
SECKEYPublicKey *key;
SECItem hashItem;
PRINT_BUF(60, (NULL, "check signed hashes", (unsigned char*)buf->data,
buf->len));
key = CERT_ExtractPublicKey(&cert->subjectPublicKeyInfo);
if (key == NULL) return SECFailure;
switch (key->keyType) {
case rsaKey:
hashItem.data = (unsigned char *)hash;
hashItem.len = sizeof(SSL3Hashes);
break;
case dsaKey:
case fortezzaKey:
hashItem.data = (unsigned char *)hash->sha;
hashItem.len = sizeof(hash->sha);
break;
default:
SECKEY_DestroyPublicKey(key);
PORT_SetError(SEC_ERROR_BAD_KEY);
return SECFailure;
}
rv = PK11_Verify(key, buf, &hashItem, wincx);
SECKEY_DestroyPublicKey(key);
if (rv != SECSuccess) return SECFailure;
return SECSuccess;
}
#ifdef notdef
static int
ssl_TmpEnableGroup(unsigned long *suites, int *count,int which,int on) {
int i;
long mask;
SSL3KeyExchangeAlgorithm kea;
const SSL3CipherSuiteDef *suite_def;
PORT_Assert(cipherSuiteCount <= 32);
for(i = 0; i < cipherSuiteCount; i++) {
mask = 1L << i;
spec = ssl_LookupCipherSuiteDef(i);
if (spec == NULL)
return SECFailure;
kea = suite_def->key_exchange_algorithm;
if (((kea == kea_rsa) && (which & SSL_GroupRSA)) ||
((kea == kea_dh) && (which & SSL_GroupDiffieHellman)) ||
((kea == kea_fortezza) && (which & SSL_GroupFortezza))) {
if (on) {
if (((*suites) & mask) == 0) {
(*count)++;
(*suites) |= mask;
}
} else {
if (((*suites) & mask) != 0) {
(*count)--;
(*suites) &= ~mask;
}
}
return SECSuccess;
}
}
return SECFailure;
}
#endif
static SECStatus
ssl3_ComputeExportRSAKeyHash(SECItem modulus, SECItem publicExponent,
SSL3Random *client_rand, SSL3Random *server_rand,
SSL3Hashes *hashes)
{
MD5Context *md5 = NULL;
SHA1Context *sha = NULL;
uint8 modulus_length[2];
uint8 exponent_length[2];
unsigned int outLen;
SECStatus rv = SECSuccess;
/*
* OK, we really should use PKCS #11 for the whole thing, but
* we do screwy things here, like statically allocate the SHA1 and
* MD5 contexts, so we just make sure it's safe before we call
* the sha/md5 stuff....
*/
if (!PK11_HashOK(SEC_OID_MD5)) {
rv = SECFailure;
goto loser;
}
if (!PK11_HashOK(SEC_OID_SHA1)) {
rv = SECFailure;
goto loser;
}
md5 = MD5_NewContext();
if (md5 == NULL) {
rv = SECFailure;
goto loser;
}
sha = SHA1_NewContext();
if (sha == NULL) {
rv = SECFailure;
goto loser;
}
modulus_length[0] = (modulus.len >> 8) & 0xff;
modulus_length[1] = (modulus.len) & 0xff;
exponent_length[0] = (publicExponent.len >> 8) & 0xff;
exponent_length[1] = (publicExponent.len) & 0xff;
MD5_Begin(md5);
SHA1_Begin(sha);
MD5_Update(md5, (unsigned char *)client_rand, SSL3_RANDOM_LENGTH);
SHA1_Update(sha, (unsigned char *)client_rand, SSL3_RANDOM_LENGTH);
MD5_Update(md5, (unsigned char *)server_rand, SSL3_RANDOM_LENGTH);
SHA1_Update(sha, (unsigned char *)server_rand, SSL3_RANDOM_LENGTH);
MD5_Update(md5, modulus_length, 2);
SHA1_Update(sha, modulus_length, 2);
MD5_Update(md5, modulus.data, modulus.len);
SHA1_Update(sha, modulus.data, modulus.len);
MD5_Update(md5, exponent_length, 2);
SHA1_Update(sha, exponent_length, 2);
MD5_Update(md5, publicExponent.data, publicExponent.len);
SHA1_Update(sha, publicExponent.data, publicExponent.len);
MD5_End(md5, hashes->md5, &outLen, MD5_LENGTH);
PORT_Assert(outLen == MD5_LENGTH);
SHA1_End(sha, hashes->sha, &outLen, SHA1_LENGTH);
PORT_Assert(outLen == SHA1_LENGTH);
loser:
if (md5 != NULL) MD5_DestroyContext(md5, PR_TRUE);
if (sha != NULL) SHA1_DestroyContext(sha, PR_TRUE);
return rv;
}
static void
ssl3_BumpSequenceNumber(SSL3SequenceNumber *num)
{
num->low++;
if (num->low == 0)
num->high++;
}
static void
ssl3_DestroyCipherSpec(SSL3CipherSpec *spec) {
if (spec->destroy) {
spec->destroy(spec->encodeContext,PR_TRUE);
spec->destroy(spec->decodeContext,PR_TRUE);
spec->encodeContext = NULL; /* paranoia */
spec->decodeContext = NULL;
}
if (spec->hashContext != NULL) {
spec->hash->destroy(spec->hashContext, PR_TRUE);
spec->hashContext = NULL;
}
spec->destroy=NULL;
}
static SECStatus
ssl3_SetupPendingCipherSpec(SSLSocket *ss, SSL3State *ssl3)
{
SSL3CipherSpec *spec = ssl3->pending_write;
SSL3CipherSuite suite = ssl3->hs.cipher_suite;
SSLSecurityInfo *sec = ss->sec;
MACAlgorithm mac;
SSL3BulkCipher cipher;
SSL3KeyExchangeAlgorithm kea;
const SSL3CipherSuiteDef *suite_def;
PORT_Assert(ssl3->pending_write == ssl3->pending_read);
suite_def = ssl_LookupCipherSuiteDef(suite);
if (suite_def == NULL)
return SECFailure;
cipher = suite_def->bulk_cipher_algorithm;
mac = suite_def->mac_algorithm;
kea = suite_def->key_exchange_algorithm;
ssl3->hs.suite_def = suite_def;
ssl3->hs.kea_def = &kea_defs[kea];
PORT_Assert(ssl3->hs.kea_def->kea == kea);
spec->cipher_def = &bulk_cipher_defs[cipher];
PORT_Assert(spec->cipher_def->cipher == cipher);
spec->mac_def = &mac_defs[mac];
PORT_Assert(spec->mac_def->alg == mac);
sec->keyBits = spec->cipher_def->key_size * 8;
sec->secretKeyBits = spec->cipher_def->secret_key_size * 8;
sec->cipherType = cipher;
/* XXX We should delete the old contexts first */
/* No.. there shouldn't be any old contexts. SSL3spec does not
* get reused. */
spec->encodeContext = NULL;
spec->decodeContext = NULL;
switch (mac) {
case mac_null:
spec->hash = &SECHashObjects[HASH_AlgNULL];
break;
case mac_md5:
spec->hash = &SECHashObjects[HASH_AlgMD5];
break;
case mac_sha:
spec->hash = &SECHashObjects[HASH_AlgSHA1];
break;
default:
PORT_Assert(0); /* impossible mac algorithm */
}
spec->mac_size = spec->hash->length;
return SECSuccess;
}
static SECStatus
InitPendingCipherSpec(SSLSocket *ss, SSL3State *ssl3, const SECItem *pms)
{
SSLSecurityInfo *sec = ss->sec;
SSL3CipherSpec *spec = ssl3->pending_write;
const SSL3BulkCipherDef *cipher_def = spec->cipher_def;
CK_MECHANISM_TYPE mechanism;
PK11SlotInfo *slot = NULL;
SECItem iv,key;
SECItem *param;
PK11Context *serverContext = NULL, *clientContext = NULL;
int rv;
rv = ssl3_GenerateSessionKeys(ss, ss->ssl3->pending_write, pms);
if (rv < 0) return SECFailure;
if (cipher_def->alg == calg_null) {
spec->encode = Null_Cipher;
spec->decode = Null_Cipher;
spec->destroy = NULL;
return SECSuccess;
}
mechanism = (CK_MECHANISM_TYPE) cipher_def->alg;
slot = PK11_GetBestSlot(mechanism,ss->sec->getClientAuthDataArg);
if (slot == NULL) return SECFailure;
/* build the server context */
iv.data = spec->server.write_iv;
iv.len = cipher_def->iv_size;
key.data = spec->server.write_key;
key.len = cipher_def->key_size;
param = PK11_ParamFromIV(mechanism,&iv);
if (param == NULL) goto fail;
serverContext = PK11_CreateContextByRawKey(slot,mechanism,
(sec->isServer ? CKA_ENCRYPT : CKA_DECRYPT),
&key,param,ss->sec->getClientAuthDataArg);
SECITEM_FreeItem(param,PR_TRUE);
if (serverContext == NULL) goto fail;
/* build the client context */
iv.data = spec->client.write_iv;
key.data = spec->client.write_key;;
param = PK11_ParamFromIV(mechanism,&iv);
if (param == NULL) goto fail;
clientContext = PK11_CreateContextByRawKey(slot,mechanism,
(sec->isServer ? CKA_DECRYPT : CKA_ENCRYPT),
&key,param,ss->sec->getClientAuthDataArg);
SECITEM_FreeItem(param,PR_TRUE);
if (clientContext == NULL) goto fail;
PK11_FreeSlot(slot);
spec->encodeContext = (sec->isServer) ? serverContext : clientContext;
spec->decodeContext = (sec->isServer) ? clientContext : serverContext;
spec->encode = (SSLCipher) PK11_CipherOp;
spec->decode = (SSLCipher) PK11_CipherOp;
spec->destroy = (SSLDestroy) PK11_DestroyContext;
spec->hashContext = (*spec->hash->create)();
if (spec->hashContext == NULL) goto fail;
return SECSuccess;
fail:
if (slot != NULL) PK11_FreeSlot(slot);
if (serverContext != NULL) PK11_DestroyContext(serverContext,PR_TRUE);
if (clientContext != NULL) PK11_DestroyContext(clientContext,PR_TRUE);
return SECFailure;
}
/*
* 60 bytes is 3 times the maximum length MAC size that is supported.
*/
static unsigned char mac_pad_1 [60] = {
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36
};
static unsigned char mac_pad_2 [60] = {
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c
};
static void
SSL3_ComputeFragmentHash(
SSL3CipherSpec *spec, SSL3Opaque *mac_secret, SSL3ContentType type,
SSL3SequenceNumber seq_num, SSL3Opaque *input, int inputLength,
unsigned char *outbuf, unsigned int *outLength)
{
unsigned char temp[MAX_MAC_LENGTH];
const SSL3MACDef *mac_def = spec->mac_def;
spec->hash->begin(spec->hashContext);
spec->hash->update(spec->hashContext, mac_secret, spec->mac_size);
spec->hash->update(spec->hashContext, mac_pad_1, mac_def->pad_size);
temp[0] = (unsigned char)(seq_num.high >> 24);
temp[1] = (unsigned char)(seq_num.high >> 16);
temp[2] = (unsigned char)(seq_num.high >> 8);
temp[3] = (unsigned char)(seq_num.high >> 0);
temp[4] = (unsigned char)(seq_num.low >> 24);
temp[5] = (unsigned char)(seq_num.low >> 16);
temp[6] = (unsigned char)(seq_num.low >> 8);
temp[7] = (unsigned char)(seq_num.low >> 0);
temp[8] = type;
temp[9] = (inputLength >> 8) & 0xff;
temp[10] = inputLength & 0xff;
PRINT_BUF(95, (NULL, "COMPUTE FRAG HASH TEMP", temp, 11));
PRINT_BUF(95, (NULL, "COMPUTE FRAG INPUT", input, inputLength));
PRINT_BUF(95, (NULL, "COMPUTE FRAG SECRET", mac_secret, spec->mac_size));
spec->hash->update(spec->hashContext, temp, 11);
spec->hash->update(spec->hashContext, input, inputLength);
spec->hash->end(spec->hashContext, temp, outLength, spec->hash->length);
PORT_Assert(*outLength == spec->hash->length);
PRINT_BUF(95, (NULL, "COMPUTE FRAG HASH TEMP", temp, *outLength));
spec->hash->begin(spec->hashContext);
spec->hash->update(spec->hashContext, mac_secret, spec->mac_size);
spec->hash->update(spec->hashContext, mac_pad_2, mac_def->pad_size);
spec->hash->update(spec->hashContext, temp, *outLength);
spec->hash->end(spec->hashContext, outbuf, outLength, spec->hash->length);
PRINT_BUF(95, (NULL, "COMPUTE FRAG HASH OUT", outbuf, *outLength));
PORT_Assert(*outLength == spec->hash->length);
}
/*
* process the plain text before sending it
*/
static SECStatus
ssl3_SendPlainText(SSLSocket *ss, SSL3ContentType type,
const SSL3Opaque *buf, int bytes, int flags)
{
int rv;
unsigned int hashBytes, cipherBytes, fragLen;
SSL3CipherSpec *spec;
SSLBuffer *write = &ss->sec->writeBuf;
const SSL3BulkCipherDef *cipher_def;
SSL_TRC(3, ("%d: SSL3[%d] SendPlainText type=%d bytes=%d",
SSL_GETPID(), ss->fd, type, bytes));
if (ss->ssl3 == NULL) {
rv = ssl3_InitState(ss);
if (rv == SECFailure) return rv;
}
spec = ss->ssl3->current_write;
cipher_def = spec->cipher_def;
while (bytes > 0) {
if (bytes > MAX_FRAGMENT_LENGTH)
fragLen = MAX_FRAGMENT_LENGTH;
else
fragLen = bytes;
if (write->space < fragLen + SSL3_BUFFER_FUDGE) {
rv = ssl_GrowBuf(write, fragLen + SSL3_BUFFER_FUDGE);
if (rv < 0) {
SSL_DBG(("%d: SSL3[%d]: SendPlainText, tried to get %d bytes",
SSL_GETPID(), ss->fd, fragLen + SSL3_BUFFER_FUDGE));
return SECFailure;
}
}
/*
* null compression is easy to do
*/
PORT_Memcpy(write->buf+SSL3_RECORD_HEADER_LENGTH, buf, fragLen);
buf += fragLen;
bytes -= fragLen;
/*
* Add the MAC
*/
SSL3_ComputeFragmentHash(
spec, (ss->sec->isServer) ?
spec->server.write_mac_secret : spec->client.write_mac_secret,
type, spec->write_seq_num, write->buf + SSL3_RECORD_HEADER_LENGTH,
fragLen, write->buf + fragLen + SSL3_RECORD_HEADER_LENGTH,
&hashBytes);
fragLen += hashBytes; /* needs to be encrypted */
/*
* Encrypt it
*/
if (cipher_def->type == type_block) {
int padding;
/* Assume blockSize is a power of two */
padding = cipher_def->block_size - 1 -
((fragLen) & (cipher_def->block_size - 1));
/* XXX allow other side to see the padding bytes? */
fragLen += padding + 1;
PORT_Assert((fragLen % cipher_def->block_size) == 0);
write->buf[fragLen + SSL3_RECORD_HEADER_LENGTH - 1] = padding;
}
rv = spec->encode(
spec->encodeContext, write->buf + SSL3_RECORD_HEADER_LENGTH,
&cipherBytes, fragLen, write->buf + SSL3_RECORD_HEADER_LENGTH,
fragLen);
if (rv < 0) { return rv; }
/*
* XXX should we zero out our copy of the buffer after compressing
* and decryption
*/
/* PORT_Assert(fragLen == cipherBytes); */
write->len = cipherBytes + SSL3_RECORD_HEADER_LENGTH;
write->buf[0] = type;
write->buf[1] = MSB(SSL_LIBRARY_VERSION_3_0);
write->buf[2] = LSB(SSL_LIBRARY_VERSION_3_0);
write->buf[3] = MSB(cipherBytes);
write->buf[4] = LSB(cipherBytes);
PRINT_BUF(50, (ss, "send record data:", write->buf, write->len));
if (ss->pendingBuf.len > 0)
rv = ssl_SendSavedWriteData(ss, &ss->pendingBuf, ssl_DefSend);
if (ss->pendingBuf.len == 0)
rv = ssl_DefSend(ss, write->buf, write->len, flags);
if(rv < 0) {
if (PORT_GetError() == XP_ERRNO_EWOULDBLOCK) {
rv = ssl_SaveWriteData(ss, &ss->pendingBuf, write->buf,
write->len);
} else {
return rv;
}
} else if (rv < write->len) {
rv = ssl_SaveWriteData(ss, &ss->pendingBuf, write->buf + rv,
write->len - rv);
}
write->len = 0;
ssl3_BumpSequenceNumber(&spec->write_seq_num);
}
return SECSuccess;
}
int
ssl3_SendApplicationData(SSLSocket *ss, const void *in, int len, int flags)
{
SECStatus rv;
rv = ssl3_SendPlainText(ss, content_application_data, in, len, flags);
if (rv < 0)
return rv;
return len;
}
static SECStatus
ssl3_FlushHandshake(SSLSocket *ss)
{
int rv;
SSLConnectInfo *ci;
PORT_Assert(ss->sec != NULL);
ci = ss->sec->ci;
rv = ssl3_SendPlainText(ss, content_handshake, ci->sendBuf.buf,
ci->sendBuf.len, 0);
ci->sendBuf.len = 0;
return rv;
}
/*
* Alerts
*/
SECStatus
SSL3_SendAlert(
SSLSocket *ss, SSL3AlertLevel level, SSL3AlertDescription desc)
{
uint8 bytes[2];
int rv;
SSL_TRC(3, ("%d: SSL3[%d]: send alert level=%d desc=%d",
SSL_GETPID(), ss->fd, level, desc));
bytes[0] = level;
bytes[1] = desc;
if (level == alert_fatal) {
if (ss->sec->ci->sid) {
ss->sec->uncache(ss->sec->ci->sid);
}
}
rv = ssl3_FlushHandshake(ss);
if (rv < 0) return rv;
rv = ssl3_SendPlainText(ss, content_alert, bytes, 2, 0);
return rv;
}
static SECStatus
ssl3_HandshakeFailure(SSLSocket *ss)
{
(void)SSL3_SendAlert(ss, alert_fatal, handshake_failure);
PORT_SetError(
ss->sec->isServer ? SSL_ERROR_BAD_SERVER : SSL_ERROR_BAD_CLIENT);
return SECFailure;
}
SECStatus
SSL3_HandleAlert(SSLSocket *ss, SSLBuffer *buf)
{
SSL3AlertLevel level;
SSL3AlertDescription desc;
int error;
SSL_TRC(3, ("%d: SSL3[%d]: handle alert", SSL_GETPID(), ss->fd));
if (buf->len != 2)
return ssl3_HandshakeFailure(ss);
level = buf->buf[0];
desc = buf->buf[1];
buf->len = 0;
SSL_TRC(5, ("%d: SSL3[%d] received alert, level = %d, description = %d",
SSL_GETPID(), ss->fd, level, desc));
error = SEC_ERROR_IO;
if (desc == bad_record_mac) error = SSL_ERROR_BAD_MAC_ALERT;
else if ((desc == bad_certificate) ||
(desc == unsupported_certificate) ||
(desc == certificate_unknown)) error = SSL_ERROR_BAD_CERT_ALERT;
else if (desc == certificate_revoked) error = SSL_ERROR_REVOKED_CERT_ALERT;
else if (desc == certificate_expired) error = SSL_ERROR_EXPIRED_CERT_ALERT;
if (level == alert_fatal) {
ss->sec->uncache(ss->sec->ci->sid);
if ((ss->ssl3->hs.ws == wait_server_hello) &&
(desc == handshake_failure)) {
/* XXX This is a hack. We're assuming that any handshake failure
* XXX on the client hello is a failure to match
* XXX ciphers.
*/
error = SSL_ERROR_NO_CYPHER_OVERLAP;
}
PORT_SetError(error);
return SECFailure;
}
if ((desc == no_certificate) && (ss->ssl3->hs.ws == wait_client_cert)) {
PORT_Assert(ss->sec->isServer);
if (ss->sec->peerCert != NULL) {
if (ss->sec->peerKey != NULL) {
SECKEY_DestroyPublicKey(ss->sec->peerKey);
ss->sec->peerKey = NULL;
}
CERT_DestroyCertificate(ss->sec->peerCert);
ss->sec->peerCert = NULL;
}
ss->ssl3->hs.ws = wait_client_key;
/* XXX If the server has client auth blindly turned on but doesn't
* XXX actually look at the certificate it won't know that no
* XXX certificate was presented so we close the socket to ensure
* XXX an error. We really shouldn't do this, but there's not a
* XXX better way yet. We only do this if we aren't connected because
* XXX if we're redoing the handshake we know the server is paying
* XXX attention to the certificate.
*/
if (!ss->connected) {
ss->sec->uncache(ss->sec->ci->sid);
PORT_SetError(SSL_ERROR_NO_CERTIFICATE);
SSL3_SendAlert(ss, alert_fatal, bad_certificate);
XP_SOCK_CLOSE(ss->fd);
return SECFailure;
}
}
return SECSuccess;
}
/*
* Change Cipher Specs
*/
static SECStatus
ssl3_SendChangeCipherSpecs(SSLSocket *ss)
{
uint8 change = change_cipher_spec_choice;
int rv;
SSL3State *ssl3 = ss->ssl3;
SSL3CipherSpec *spec;
SSL_TRC(3, ("%d: SSL3[%d]: send change cipher specs",
SSL_GETPID(), ss->fd));
rv = ssl3_FlushHandshake(ss);
if (rv < 0) return rv;
rv = ssl3_SendPlainText(ss, content_change_cipher_spec, &change, 1, 0);
if (rv < 0) return rv;
spec = ss->ssl3->pending_write;
ssl3->pending_write = ssl3->current_write;
ssl3->current_write = spec;
spec->write_seq_num.high = spec->write_seq_num.low = 0;
/* We need to free up the contexts, keys and certs ! */
/* If we are really through with the old cipher spec
* (Both the read and write sides have changed) destroy it.
*/
if (ss->ssl3->pending_read == ss->ssl3->pending_write) {
ssl3_DestroyCipherSpec(ss->ssl3->pending_write);
}
return SECSuccess;
}
static SECStatus
SSL3_HandleChangeCipherSpecs(SSLSocket *ss, SSLBuffer *buf)
{
SSL3CipherSpec *spec;
SSL3WaitState ws = ss->ssl3->hs.ws;
int change;
SSL_TRC(3, ("%d: SSL3[%d]: handle change cipher specs",
SSL_GETPID(), ss->fd));
if (ws != wait_change_cipher && ws != wait_cert_verify) {
(void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
return SECFailure;
}
if(buf->len != 1) {
return ssl3_HandshakeFailure(ss);
}
change = buf->buf[0];
if(change != 1)
return ssl3_HandshakeFailure(ss);
buf->len = 0;
spec = ss->ssl3->pending_read;
ss->ssl3->pending_read = ss->ssl3->current_read;
ss->ssl3->current_read = spec;
spec->read_seq_num.high = spec->read_seq_num.low = 0;
ss->ssl3->hs.ws = wait_finished;
/* If we are really through with the old cipher spec
* (Both the read and write sides have changed) destroy it.
*/
if (ss->ssl3->pending_read == ss->ssl3->pending_write) {
ssl3_DestroyCipherSpec(ss->ssl3->pending_read);
}
return SECSuccess;
}
/*
* Key generation given pre master secret
*/
static char *mixers[] = { "A", "BB", "CCC", "DDDD", "EEEEE", "FFFFFF", "GGGGGGG"};
#define NUM_MIXERS 7
static SECStatus
ssl3_GenerateSessionKeys(SSLSocket *ss, SSL3CipherSpec *spec,
const SECItem *pms)
{
MD5Context *md5 = NULL;
SHA1Context *sha = NULL;
int i;
unsigned int outLen;
SSL3Opaque sha_out[SHA1_LENGTH];
SSL3Opaque key_block[NUM_MIXERS * MD5_LENGTH];
unsigned char * cr = (unsigned char *)&ss->ssl3->hs.client_random;
unsigned char * sr = (unsigned char *)&ss->ssl3->hs.server_random;
const SSL3BulkCipherDef *cipher_def = spec->cipher_def;
/*
* OK, we really should use PKCS #11 for the whole thing, but
* we do screwy things here, like statically allocate the SHA1 and
* MD5 contexts, so we just make sure it's safe before we call
* the sha/md5 stuff....
*/
if (!PK11_HashOK(SEC_OID_MD5)) {
goto loser;
}
if (!PK11_HashOK(SEC_OID_SHA1)) {
goto loser;
}
md5 = MD5_NewContext();
if (md5 == NULL) goto loser;
sha = SHA1_NewContext();
if (sha == NULL) goto loser;
if ((pms != NULL) && (pms->data != NULL)) {
/*
* generate the master secret if we only have the pre master secret
*/
for (i = 0; i < 3; i++) {
SHA1_Begin(sha);
SHA1_Update(sha, (unsigned char*) mixers[i], strlen(mixers[i]));
SHA1_Update(sha, pms->data, pms->len);
SHA1_Update(sha, cr, SSL3_RANDOM_LENGTH);
SHA1_Update(sha, sr, SSL3_RANDOM_LENGTH);
SHA1_End(sha, sha_out, &outLen, SHA1_LENGTH);
PORT_Assert(outLen == SHA1_LENGTH);
MD5_Begin(md5);
MD5_Update(md5, pms->data, pms->len);
MD5_Update(md5, sha_out, outLen);
MD5_End(md5, &spec->master_secret[i*MD5_LENGTH], &outLen,
MD5_LENGTH);
PORT_Assert(outLen == MD5_LENGTH);
}
PRINT_BUF(60, (ss, "master secret:", &spec->master_secret[0], 48));
#ifndef FORTEZZA
/* get rid of the pms info before it gets swapped out */
/* Well not really... we still need to encrypt it on the fortezza
* side!!! */
PORT_Memset(pms->data, 0, pms->len);
#endif
}
/*
* generate the key material
*/
for (i = 0; i < NUM_MIXERS; i++) {
SHA1_Begin(sha);
SHA1_Update(sha, (unsigned char*) mixers[i], strlen(mixers[i]));
SHA1_Update(sha, spec->master_secret, sizeof(SSL3MasterSecret));
SHA1_Update(sha, sr, SSL3_RANDOM_LENGTH);
SHA1_Update(sha, cr, SSL3_RANDOM_LENGTH);
SHA1_End(sha, sha_out, &outLen, SHA1_LENGTH);
PORT_Assert(outLen == SHA1_LENGTH);
MD5_Begin(md5);
MD5_Update(md5, spec->master_secret, sizeof(SSL3MasterSecret));
MD5_Update(md5, sha_out, outLen);
MD5_End(md5, &key_block[i*MD5_LENGTH], &outLen, MD5_LENGTH);
PORT_Assert(outLen == MD5_LENGTH);
}
PRINT_BUF(60, (ss, "key block:", &key_block[0], NUM_MIXERS * MD5_LENGTH));
/*
* put the key material where it goes
*/
i = 0; /* now shows how much consumed */
PORT_Memcpy(spec->client.write_mac_secret, &key_block[i], spec->mac_size);
i += spec->mac_size;
PORT_Memcpy(spec->server.write_mac_secret, &key_block[i], spec->mac_size);
i += spec->mac_size;
#ifdef FORTEZZA
/* we just need the pre-master secret. & mac Fortezza generates it's own
* keys.
*/
if (spec->bulk_cipher_algorithm == cipher_fortezza) {
return SECSuccess;
}
#endif
if (cipher_def->keygen_mode == kg_strong) {
PORT_Memcpy(spec->client.write_key, &key_block[i],
cipher_def->key_size);
i += cipher_def->key_size;
PORT_Memcpy(spec->server.write_key, &key_block[i],
cipher_def->key_size);
i += cipher_def->key_size;
PORT_Memcpy(spec->client.write_iv, &key_block[i], cipher_def->iv_size);
i += cipher_def->iv_size;
PORT_Memcpy(spec->server.write_iv, &key_block[i], cipher_def->iv_size);
i += cipher_def->iv_size;
} else {
MD5_Begin(md5);
MD5_Update(md5, &key_block[i], cipher_def->secret_key_size);
MD5_Update(md5, cr, SSL3_RANDOM_LENGTH);
MD5_Update(md5, sr, SSL3_RANDOM_LENGTH);
MD5_End(md5, spec->client.write_key, &outLen, MD5_LENGTH);
i += cipher_def->secret_key_size;
MD5_Begin(md5);
MD5_Update(md5, &key_block[i], cipher_def->secret_key_size);
MD5_Update(md5, sr, SSL3_RANDOM_LENGTH);
MD5_Update(md5, cr, SSL3_RANDOM_LENGTH);
MD5_End(md5, spec->server.write_key, &outLen, MD5_LENGTH);
i += cipher_def->secret_key_size;
MD5_Begin(md5);
MD5_Update(md5, cr, SSL3_RANDOM_LENGTH);
MD5_Update(md5, sr, SSL3_RANDOM_LENGTH);
MD5_End(md5, spec->client.write_iv, &outLen, MD5_LENGTH);
MD5_Begin(md5);
MD5_Update(md5, sr, SSL3_RANDOM_LENGTH);
MD5_Update(md5, cr, SSL3_RANDOM_LENGTH);
MD5_End(md5, spec->server.write_iv, &outLen, MD5_LENGTH);
}
MD5_DestroyContext(md5, PR_TRUE);
SHA1_DestroyContext(sha, PR_TRUE);
PRINT_BUF(60, (ss, "client write mac secret:",
spec->client.write_mac_secret, spec->mac_size));
PRINT_BUF(60, (ss, "server write mac secret:",
spec->server.write_mac_secret, spec->mac_size));
PRINT_BUF(60, (ss, "client write key:", spec->client.write_key,
cipher_def->key_size));
PRINT_BUF(60, (ss, "server write key:", spec->server.write_key,
cipher_def->key_size));
PRINT_BUF(60, (ss, "client write iv:", spec->client.write_iv,
cipher_def->iv_size));
PRINT_BUF(60, (ss, "server write iv:", spec->server.write_iv,
cipher_def->iv_size));
return SECSuccess;
loser:
if (md5 != NULL) MD5_DestroyContext(md5, PR_TRUE);
if (sha != NULL) SHA1_DestroyContext(sha, PR_TRUE);
return SECFailure;
}
/*
* Handshake messages
*/
static void
SSL3_UpdateHandshakeHashes(SSL3State *ssl3, unsigned char *b, unsigned int l)
{
PRINT_BUF(90, (NULL, "XXX handshake hash input:", b, l));
MD5_Update(ssl3->hs.md5, b, l);
SHA1_Update(ssl3->hs.sha, b, l);
}
static SECStatus
ssl3_AppendHandshake(SSLSocket *ss, const void *void_src, int bytes)
{
SSLSecurityInfo *sec = ss->sec;
SSLConnectInfo *ci = sec->ci;
unsigned char * src = (unsigned char *)void_src;
int room;
SECStatus rv;
room = ci->sendBuf.space - ci->sendBuf.len;
if (ci->sendBuf.space < MAX_SEND_BUF_LENGTH && room < bytes) {
ssl_GrowBuf(
&ci->sendBuf,
#ifndef NSPR20
MAX(MIN_SEND_BUF_LENGTH,
MIN(MAX_SEND_BUF_LENGTH, ci->sendBuf.len + bytes)));
#else
PR_MAX(MIN_SEND_BUF_LENGTH,
PR_MIN(MAX_SEND_BUF_LENGTH, ci->sendBuf.len + bytes)));
#endif /* NSPR20 */
room = ci->sendBuf.space - ci->sendBuf.len;
}
PRINT_BUF(60, (ss, "", (unsigned char*)void_src, bytes));
SSL3_UpdateHandshakeHashes(ss->ssl3, src, bytes);
while (bytes > room) {
if (room > 0)
PORT_Memcpy(ci->sendBuf.buf + ci->sendBuf.len, src, room);
ci->sendBuf.len += room;
rv = ssl3_FlushHandshake(ss);
if (rv < 0) return rv;
bytes -= room;
src += room;
room = ci->sendBuf.space;
PORT_Assert(ci->sendBuf.len == 0);
}
PORT_Memcpy(ci->sendBuf.buf + ci->sendBuf.len, src, bytes);
ci->sendBuf.len += bytes;
return SECSuccess;
}
static SECStatus
ssl3_AppendHandshakeNumber(SSLSocket *ss, long num, int lenSize)
{
uint8 b[4], *p = b;
switch (lenSize) {
case 4:
*p++ = (num >> 24) & 0xff;
case 3:
*p++ = (num >> 16) & 0xff;
case 2:
*p++ = (num >> 8) & 0xff;
case 1:
*p = num & 0xff;
}
SSL_TRC(60, ("%d: number:", SSL_GETPID()));
return ssl3_AppendHandshake(ss, &b[0], lenSize);
}
static SECStatus
ssl3_AppendHandshakeVariable(
SSLSocket *ss, SSL3Opaque *src, long bytes, int lenSize)
{
SECStatus rv;
PORT_Assert((bytes < (1<<8) && lenSize == 1) ||
(bytes < (1L<<16) && lenSize == 2) ||
(bytes < (1L<<24) && lenSize == 3));
SSL_TRC(60,("%d: append variable:", SSL_GETPID()));
if((rv = ssl3_AppendHandshakeNumber(ss, bytes, lenSize)) != SECSuccess) {
return rv;
}
SSL_TRC(60, ("data:"));
return ssl3_AppendHandshake(ss, src, bytes);
}
static SECStatus
ssl3_AppendHandshakeHeader(SSLSocket *ss, SSL3HandshakeType t, uint32 length)
{
int rv;
SSL_TRC(30,("%d: append handshake header: %d", SSL_GETPID(), t));
PRINT_BUF(60, (ss, "md5 handshake hash", (unsigned char*)ss->ssl3->hs.md5, 16));
rv = ssl3_AppendHandshakeNumber(ss, t, 1);
if (rv < 0) return rv;
rv = ssl3_AppendHandshakeNumber(ss, length, 3);
return rv;
}
static SECStatus
ssl3_ConsumeHandshake(
SSLSocket *ss, void *v, int bytes, SSL3Opaque **b, long *length)
{
if (bytes > *length)
return ssl3_HandshakeFailure(ss);
PORT_Memcpy(v, *b, bytes);
PRINT_BUF(60, (ss, "consume bytes:", *b, bytes));
*b += bytes;
*length -= bytes;
return SECSuccess;
}
static long
ssl3_ConsumeHandshakeNumber(
SSLSocket *ss, int bytes, SSL3Opaque **b, long *length)
{
long num = 0;
int i;
SECStatus status;
uint8 buf[4];
status = ssl3_ConsumeHandshake(ss, buf, bytes, b, length);
if (status < 0)
return ssl3_HandshakeFailure(ss);
for (i = 0; i < bytes; i++)
num = (num << 8) + buf[i];
return num;
}
static SECStatus
ssl3_ConsumeHandshakeVariable(
SSLSocket *ss, SECItem *i, int bytes, SSL3Opaque **b, long *length)
{
long rv;
PORT_Assert(bytes <= 3);
i->len = 0;
i->data = NULL;
rv = ssl3_ConsumeHandshakeNumber(ss, bytes, b, length);
if (rv < 0) return rv;
if (rv > 0) {
i->data = PORT_Alloc(rv);
if (i->data == NULL)
return ssl3_HandshakeFailure(ss);
i->len = rv;
rv = ssl3_ConsumeHandshake(ss, i->data, i->len, b, length);
if (rv < 0) {
PORT_Free(i->data);
i->data = NULL;
return ssl3_HandshakeFailure(ss);
}
}
return SECSuccess;
}
/* Extract the hashes of handshake messages to this point */
static void
SSL3_ComputeHandshakeHashes(SSL3State *ssl3, SSL3CipherSpec *spec,
SSL3Hashes *hashes, uint32 sender)
{
MD5Context *md5;
SHA1Context *sha;
SSL3Opaque md5_inner[MAX_MAC_LENGTH];
SSL3Opaque sha_inner[MAX_MAC_LENGTH];
unsigned int outLength;
unsigned char s[4];
/*
* OK, we really should use PKCS #11 for the whole thing, but
* we do screwy things here, like statically allocate the SHA1 and
* MD5 contexts, so we just make sure it's safe before we call
* the sha/md5 stuff....
*/
if (!PK11_HashOK(SEC_OID_MD5)) {
return; /* sigh. well we'll fail soon enough */
}
if (!PK11_HashOK(SEC_OID_SHA1)) {
return; /* sigh. well we'll fail soon enough */
}
s[0] = (unsigned char)(sender >> 24);
s[1] = (unsigned char)(sender >> 16);
s[2] = (unsigned char)(sender >> 8);
s[3] = (unsigned char)sender;
PORT_Assert(mac_defs[mac_md5].alg == mac_md5);
PORT_Assert(mac_defs[mac_sha].alg == mac_sha);
md5 = MD5_CloneContext(ssl3->hs.md5);
if (sender != 0) {
MD5_Update(md5, s, 4);
}
MD5_Update(md5, spec->master_secret, sizeof(SSL3MasterSecret));
MD5_Update(md5, mac_pad_1, mac_defs[mac_md5].pad_size);
MD5_End(md5, md5_inner, &outLength, MD5_LENGTH);
PORT_Assert(outLength == MD5_LENGTH);
sha = SHA1_CloneContext(ssl3->hs.sha);
if (sender != 0) {
SHA1_Update(sha, s, 4);
}
SHA1_Update(sha, spec->master_secret, sizeof(SSL3MasterSecret));
SHA1_Update(sha, mac_pad_1, mac_defs[mac_sha].pad_size);
SHA1_End(sha, sha_inner, &outLength, SHA1_LENGTH);
PORT_Assert(outLength == SHA1_LENGTH);
MD5_Begin(md5);
MD5_Update(md5, spec->master_secret, sizeof(SSL3MasterSecret));
MD5_Update(md5, mac_pad_2, mac_defs[mac_md5].pad_size);
MD5_Update(md5, md5_inner, MD5_LENGTH);
MD5_End(md5, hashes->md5, &outLength, MD5_LENGTH);
PORT_Assert(outLength == MD5_LENGTH);
SHA1_Begin(sha);
SHA1_Update(sha, spec->master_secret, sizeof(SSL3MasterSecret));
SHA1_Update(sha, mac_pad_2, mac_defs[mac_sha].pad_size);
SHA1_Update(sha, sha_inner, SHA1_LENGTH);
SHA1_End(sha, hashes->sha, &outLength, SHA1_LENGTH);
PORT_Assert(outLength == SHA1_LENGTH);
MD5_DestroyContext(md5, PR_TRUE);
SHA1_DestroyContext(sha, PR_TRUE);
PRINT_BUF(60, (NULL, "handshake hashes", (unsigned char*)hashes,
sizeof(SSL3Hashes)));
}
/*
* SSL 2 based implementations pass in the initial outbound buffer
* so that the handshake hash can contain the included information
*/
SECStatus
ssl3_StartHandshakeHash(SSLSocket *ss, unsigned char * buf, int length)
{
SECStatus rv;
rv = ssl3_InitState(ss);
if (rv < 0) return rv;
PORT_Memset(&ss->ssl3->hs.client_random, 0, SSL3_RANDOM_LENGTH);
PORT_Memcpy(
&ss->ssl3->hs.client_random.rand[SSL3_RANDOM_LENGTH - SSL_CHALLENGE_BYTES],
&ss->sec->ci->clientChallenge,
SSL_CHALLENGE_BYTES);
SSL3_UpdateHandshakeHashes(ss->ssl3, buf, length);
/*
* XXX The fragment sequence number went up, but there is no hash
* yet, so we are sort of safe!
*/
return SECSuccess;
}
SECStatus
SSL3_SendClientHello(SSLSocket *ss)
{
SSL3ProtocolVersion version = SSL_LIBRARY_VERSION_3_0;
SSLSecurityInfo *sec = ss->sec;
SSLConnectInfo *ci = sec->ci;
SSLSessionID *sid;
SECStatus rv;
int i, length, num_suites;
#ifdef FORTEZZA
PRBool use_fortezza = PR_FALSE;
#endif
SSL_TRC(3, ("%d: SSL3[%d]: send client hello", SSL_GETPID(), ss->fd));
rv = ssl3_InitState(ss);
if (rv < 0) return rv;
/* Initialize the handshake hashes for the new handshake */
/*
* OK, we really should use PKCS #11 for the whole thing, but
* we do screwy things here, like statically allocate the SHA1 and
* MD5 contexts, so we just make sure it's safe before we call
* the sha/md5 stuff....
*/
if (!PK11_HashOK(SEC_OID_MD5)) {
return SECFailure;
}
if (!PK11_HashOK(SEC_OID_SHA1)) {
return SECFailure;
}
MD5_Begin(ss->ssl3->hs.md5);
SHA1_Begin(ss->ssl3->hs.sha);
PORT_Assert(sec && ci);
#ifdef FORTEZZA
if (SSL_IsEnabledGroup(SSL_GroupFortezza)) {
rv=FortezzaOpen(ss,(FortezzaKEAContext **)&ss->ssl3->kea_context);
switch (rv) {
case SECSuccess:
use_fortezza = PR_TRUE;
break;
case SECFailure:
ssl_TmpEnableGroup(&localCipherSuites,&localCipherSuiteCount,
SSL_GroupFortezza,PR_FALSE);
break;
default:
ss->reStartType = 3;
return (rv); /* SECWouldBlock */
}
}
#endif
if (ss->noCache) {
sid = NULL;
} else {
sid = ssl_LookupSID(ci->peer, ci->port, ss->peerID);
}
#ifdef FORTEZZA
/* We can't resume based on a different card in Fortezza. If the sid
* hasFortezza then it's most likely we will resume with fortezza (if
* the sid doesn't have hasFortezza then the server won't resume fortezza
* even if that's the protocol it chooses.
* So if we try to resume on a different card, toss this sid and
* renegotiate */
if (sid->u.ssl3.hasFortezza && ss->ssl3->kea_context) {
FortezzaKEAContext *kea_context =
(FortezzaKEAContext *)ss->ssl3->kea_context;
if (sid->u.ssl3.fortezzaSocket != kea_context->socketId) {
sid->u.ssl3.resumable = PR_FALSE;
(*ss->sec->uncache)(sid);
ssl_FreeSID(sid);
sid = NULL;
}
}
#endif
if (sid) {
PRINT_BUF(4, (ss, "client, found session-id:", sid->u.ssl3.sessionID,
sid->u.ssl3.sessionIDLength));
ss->version = sid->version;
ss->ssl3->policy = sid->u.ssl3.policy;
} else {
sid = ssl3_NewSessionID(ss, PR_FALSE);
if (!sid) return SECFailure;
}
if (ci->sid != NULL) ssl_FreeSID(ci->sid);
ci->sid = sid;
sec->send = ssl3_SendApplicationData;
num_suites = count_cipher_suites(ss->ssl3->policy, PR_TRUE);
length = sizeof(SSL3ProtocolVersion) + SSL3_RANDOM_LENGTH +
1 + ((sid == NULL) ? 0 : sid->u.ssl3.sessionIDLength) +
2 + num_suites*sizeof(SSL3CipherSuite) +
1 + compressionMethodsCount;
rv = ssl3_AppendHandshakeHeader(ss, client_hello, length);
if (rv < 0) return rv;
rv = ssl3_AppendHandshakeNumber(ss, version, 2);
if (rv < 0) return rv;
rv = ssl3_GetNewRandom(&ss->ssl3->hs.client_random);
if (rv < 0) return rv;
rv = ssl3_AppendHandshake(
ss, &ss->ssl3->hs.client_random, SSL3_RANDOM_LENGTH);
if (rv < 0) return rv;
if (sid)
rv = ssl3_AppendHandshakeVariable(
ss, sid->u.ssl3.sessionID, sid->u.ssl3.sessionIDLength, 1);
else
rv = ssl3_AppendHandshakeVariable(ss, NULL, 0, 1);
if (rv < 0) return rv;
rv = ssl3_AppendHandshakeNumber(
ss, num_suites*sizeof(SSL3CipherSuite), 2);
if (rv < 0) return rv;
for (i = 0; i < cipherSuiteCount; i++) {
if (config_match(&cipherSuites[i], ss->ssl3->policy, PR_TRUE)) {
rv = ssl3_AppendHandshakeNumber(ss, cipherSuites[i].cipher_suite,
sizeof(SSL3CipherSuite));
if (rv < 0) return rv;
}
}
/* packing of compressions in array is unspecified */
rv = ssl3_AppendHandshakeNumber(ss, compressionMethodsCount, 1);
if (rv < 0) return rv;
for (i = 0; i < compressionMethodsCount; i++) {
rv = ssl3_AppendHandshakeNumber(ss, compressions[i], 1);
if (rv < 0) return rv;
}
rv = ssl3_FlushHandshake(ss);
if (rv < 0) return rv;
ss->ssl3->hs.ws = wait_server_hello;
return rv;
}
SECStatus
SSL3_HandleHelloRequest(SSLSocket *ss)
{
SSLSessionID *sid = ss->sec->ci->sid;
SSL_TRC(3, ("%d: SSL3[%d]: handle hello request", SSL_GETPID(), ss->fd));
PORT_Assert(ss->ssl3);
if (ss->ssl3->hs.ws == wait_server_hello)
return SECSuccess;
if (ss->ssl3->hs.ws != idle_handshake || ss->sec->isServer)
return ssl3_HandshakeFailure(ss);
if (sid) {
ss->sec->uncache(sid);
ssl_FreeSID(sid);
ss->sec->ci->sid = NULL;
}
return SSL3_SendClientHello(ss);
}
SECStatus
SSL3_SendClientKeyExchange(SSLSocket *ss)
{
SSL3RSAPreMasterSecret rsa_pms;
#ifdef FORTEZZA
FortezzaKey *tek;
FortezzaKEAContext *kea_context;
FortezzaContext *write_context,*read_context;
FortezzaContext *tmp_write=NULL,*tmp_read=NULL;
SSL3FortezzaKeys fortezza_pms;
int newpers;
SSLSessionID *sid = ss->sec->ci->sid;
#endif
CERTCertificate *cert = 0;
SECKEYPublicKey *serverKey = 0;
SECItem pms;
SECItem enc_pms;
SECStatus rv = SECFailure;
unsigned char buffer[MAX_RSA_MODULUS_LEN];
PK11SymKey *symKey;
PK11SlotInfo *slot;
void *wincx;
SSL_TRC(3, ("%d: SSL3[%d]: send client key exchange",
SSL_GETPID(), ss->fd));
cert = ss->sec->peerCert;
if (ss->sec->peerKey == NULL)
serverKey = CERT_ExtractPublicKey(&cert->subjectPublicKeyInfo);
else
serverKey = ss->sec->peerKey;
if (serverKey == NULL)
return SECFailure;
wincx = ss->sec->getClientAuthDataArg;
/*
* in the future we need to do this whole Key exchange/Gen in the token
* thing here.
*/
switch (ss->ssl3->hs.kea_def->alg) {
case kt_rsa:
rsa_pms.client_version[0] = MSB(SSL_LIBRARY_VERSION_3_0);
rsa_pms.client_version[1] = LSB(SSL_LIBRARY_VERSION_3_0);
rv = PK11_GenerateRandom(&rsa_pms.random[0], sizeof(rsa_pms.random));
if (rv != SECSuccess) break;
rv = SECFailure;
pms.data = (unsigned char *)&rsa_pms;
pms.len = sizeof(rsa_pms);
enc_pms.data = buffer;
enc_pms.len = sizeof(buffer);
slot = PK11_GetBestSlot(CKM_RSA_PKCS,wincx);
if (slot == NULL) break;
symKey = PK11_ImportSymKey(slot,CKM_SSL3_PRE_MASTER_KEY_GEN,
CKA_DERIVE,&pms,wincx);
PK11_FreeSlot(slot);
if (symKey == NULL) break;
rv = PK11_PubWrapSymKey(CKM_RSA_PKCS,serverKey,symKey,&enc_pms);
PK11_FreeSymKey(symKey);
if (rv != SECSuccess) break;
rv = InitPendingCipherSpec(ss, ss->ssl3, &pms);
if (rv != SECSuccess) break;
/*
* PMS must be cleared before we do anything else so that
* it probably will not get swapped to disk.
*/
PORT_Memset(&rsa_pms, 0, sizeof(rsa_pms));
rv = ssl3_AppendHandshakeHeader(ss, client_key_exchange, enc_pms.len);
if (rv != SECSuccess) break;
rv = ssl3_AppendHandshake(ss, enc_pms.data, enc_pms.len);
if (rv != SECSuccess) break;
rv = SECSuccess;
break;
#ifdef FORTEZZA
case kt_fortezza:
if (tek == NULL) goto fortezzaLoser;
if (slot == NULL) break;
/* if we don't have a certificate, we need to read out your public
* key. This changes for a bit when we need to deal with the
* PQG stuff */
PORT_Memset(fortezza_pms.y_signature, 0,
sizeof(fortezza_pms.y_signature));
if (ss->ssl3->clientCertificate == NULL) {
pubkey = CERT_ExtractPublicKey(&ccert->subjectPublicKeyInfo);
if (pubkey == NULL) goto fortezza_oser;
PORT_Assert(pubkey->keyType == fortezzaKeys); /* paranoia */
/* send the protocol */
rv = ssl3_AppendHandshakeHeader(ss, client_key_exchange,
(sizeof(fortezza_pms)-sizeof(fortezza_pms.y_c))
+ 1 + pubkey->fortezzaKeyData.KEAKey.len);
rv |= ssl3_AppendHandshakeVariable(ss,
pubkey->fortezzaKeyData.KEAKey.data,
pubkey->fortezzaKeyData.KEAKey.len,1);
SECKEY_DestroyPublicKey(pubkey);
if (rv < 0) goto fortezza_oser;
} else {
rv = ssl3_AppendHandshakeHeader(ss, client_key_exchange,
(sizeof(fortezza_pms)-sizeof(fortezza_pms.y_c)) + 1);
rv = ssl3_AppendHandshakeVariable(ss,NULL,0,1);
}
/* create a pre-Master secret */
/* We do this now so we can call InitPending Cipher to get our
* context's. That way we can properly load them up...
*/
pms.data = buffer;
pms.len = sizeof(fortezza_pms.encrypted_preMasterSecret);
rv = PK11_GenerateRandom(pms.data,pms.len);
/* encrypt the pms with the TEK */
mechanism.type = CKM_FORTEZZA_SKIPJACK;
mechanism.pParameter = fortezza_pms.master_secret_iv;
mechanism.usParameterLen = sizeof(fortezza_pms.master_secret_iv);
rv = PK11_GETTAB(slot)->C_EncryptInit(slot->session,
&mechanism,tek->objectID);
enc_pms.data = fortezza_pms.encrpyted_preMasterSecret;
enc_pms.len = sizeof(fortezza_pms.encrpyted_preMasterSecret);
rv = InitPendingCipherSpec(ss, ss->ssl3, &pms);
if (rv < 0) return SECFailure;
/* copy the keys and IV's out now */
fortezza_pms.wrapped_client_write_key
fortezza_pms.wrapped_server_write_key;
fortezza_pms.client_write_iv
fortezza_pms.server_write_iv;
if (rv < 0) { goto fortezza_loser; }
/*
* now we initialize out contexts
*/
sid->u.ssl3.hasFortezza = PR_TRUE;
sid->u.ssl3.fortezzaSocket = PK11_ReferenceSlot(slot);
sid->u.ssl3.tek = PK11_ReferenceSymKey(tek);
/* what to do about these??? */
sid->u.ssl3.clientWriteKey = write_context->theKey;
sid->u.ssl3.clientWriteKey->ref_count++;
sid->u.ssl3.serverWriteKey = read_context->theKey;
sid->u.ssl3.serverWriteKey->ref_count++;
PORT_Memcpy(sid->u.ssl3.clientWriteIV,write_context->iv,
sizeof(FortezzaIV));
PORT_Memcpy(sid->u.ssl3.clientWriteSave,write_context->save,
sizeof(FortezzaCryptSave));
PORT_Memcpy(sid->u.ssl3.serverWriteIV,read_context->iv,
sizeof(FortezzaIV));
if (tmp_read) FortezzaDestroyContext(tmp_read,PR_TRUE);
if (tmp_write) FortezzaDestroyContext(tmp_write,PR_TRUE);
return SECSuccess;
fortezza_loser:
if (tmp_read) FortezzaDestroyContext(tmp_read,PR_TRUE);
if (tmp_write) FortezzaDestroyContext(tmp_write,PR_TRUE);
return rv;
#endif
default:
break;
}
return rv;
}
SECStatus
SSL3_SendCertificateVerify(SSLSocket *ss)
{
SSL3Hashes hashes;
SECStatus rv;
SECItem buf;
unsigned char buffer[MAX_RSA_MODULUS_LEN];
SSL3State *ssl3 = ss->ssl3;
buf.data = buffer;
buf.len = MAX_RSA_MODULUS_LEN;
SSL_TRC(3, ("%d: SSL3[%d]: send certificate verify",
SSL_GETPID(), ss->fd));
SSL3_ComputeHandshakeHashes(ss->ssl3, ss->ssl3->pending_write,
&hashes, 0);
rv = ssl3_SignHashes(&hashes, ss->ssl3->clientPrivateKey, &buf);
/*
* diffie-helman & fortezza need the client key for the key
* exchange.
*/
if (ssl3->hs.kea_def->alg == kt_rsa) {
SECKEY_DestroyPrivateKey(ssl3->clientPrivateKey);
ssl3->clientPrivateKey = NULL;
}
if (rv < 0) return SECFailure;
rv = ssl3_AppendHandshakeHeader(ss, certificate_verify, buf.len + 2);
if (rv < 0) return SECFailure;
rv = ssl3_AppendHandshakeVariable(ss, buf.data, buf.len, 2);
if (rv < 0) return SECFailure;
return SECSuccess;
}
SECStatus
SSL3_HandleServerHello(SSLSocket *ss, SSL3Opaque *b, long length)
{
SECStatus rv;
int version, temp; /* allow for consume number failure */
SSLSessionID *sid = ss->sec->ci->sid;
SECItem sidBytes = {siBuffer, NULL, 0};
int i;
PRBool can_resume = PR_TRUE;
SSL_TRC(3, ("%d: SSL3[%d]: handle server hello", SSL_GETPID(), ss->fd));
rv = ssl3_InitState(ss);
if (rv < 0) goto loser;
if (ss->ssl3->hs.ws != wait_server_hello) goto loser;
version = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
if (version < 0) return version;
if (version != SSL_LIBRARY_VERSION_3_0) /* only one supported for now */
goto loser;
rv = ssl3_ConsumeHandshake(
ss, &ss->ssl3->hs.server_random, SSL3_RANDOM_LENGTH, &b, &length);
if (rv < 0) return rv;
rv = ssl3_ConsumeHandshakeVariable(ss, &sidBytes, 1, &b, &length);
if (rv < 0) return rv;
if (sidBytes.len > SSL3_SESSIONID_BYTES) goto loser;
temp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
for (i = 0; i < cipherSuiteCount; i++) {
if ((temp == cipherSuites[i].cipher_suite) &&
(config_match(&cipherSuites[i], ss->ssl3->policy, PR_TRUE)))
break;
if (i == cipherSuiteCount - 1)
goto loser;
}
ss->ssl3->hs.cipher_suite = temp;
ss->ssl3->hs.suite_def = ssl_LookupCipherSuiteDef(temp);
temp = ssl3_ConsumeHandshakeNumber(ss, 1, &b, &length);
for (i = 0; i < compressionMethodsCount; i++) {
if (temp == compressions[i]) break;
if (i == compressionMethodsCount - 1)
goto loser;
}
ss->ssl3->hs.compression = temp;
if (length != 0)
goto loser;
/*
* we may or may not have sent a session id, we may get one back or
* not and if so it may match the one we sent
* (fortezza is not resumable!)
*/
can_resume = PR_TRUE;
#ifdef FORTEZZA
if (ss->ssl3->hs.kea == kea_fortezza) can_resume = sid->u.ssl3.hasFortezza;
#endif
if ((sidBytes.len > 0) && (sidBytes.len == sid->u.ssl3.sessionIDLength) &&
(PORT_Memcmp(sid->u.ssl3.sessionID, sidBytes.data, sidBytes.len) == 0)
&& can_resume) {
/* Got a Match */
ss->ssl3->hs.ws = wait_change_cipher;
ss->ssl3->hs.isResuming = PR_TRUE;
PORT_Memcpy(ss->ssl3->pending_read->master_secret,
sid->u.ssl3.masterSecret,
sizeof(ss->ssl3->pending_read->master_secret));
/* copy the peer cert from the SID */
if (sid->peerCert != NULL) {
ss->sec->peerCert = CERT_DupCertificate(sid->peerCert);
}
} else {
/* throw the old one away, if any */
sid->u.ssl3.resumable = PR_FALSE;
(*ss->sec->uncache)(sid);
ssl_FreeSID(sid);
ss->sec->ci->sid = sid = ssl3_NewSessionID(ss, PR_FALSE);
if (sid == NULL) goto loser;
sid->u.ssl3.sessionIDLength = sidBytes.len;
if (sidBytes.len > SSL3_SESSIONID_BYTES) goto loser;
PORT_Memcpy(sid->u.ssl3.sessionID, sidBytes.data, sidBytes.len);
ss->ssl3->hs.isResuming = PR_FALSE;
#if 0
/* We should really use a table, or something. Diffie-Helman XXXX */
if (MSB(ss->ssl3->hs.cipher_suite) == 0x04) {
if (LSB(ss->ssl3->hs.cipher_suite <= 0x10)) {
ss->ssl3->hs.ws = wait_server_key;
} else {
goto loser;
}
} else {
ss->ssl3->hs.ws = wait_server_cert;
}
#else
ss->ssl3->hs.ws = wait_server_cert;
#endif
}
ssl3_SetupPendingCipherSpec(ss, ss->ssl3);
if (ss->ssl3->hs.isResuming == PR_TRUE) {
rv = InitPendingCipherSpec(ss, ss->ssl3, NULL);
if (rv < 0) goto loser;
#ifdef FORTEZZA
if (ss->ssl3->hs.kea == kea_fortezza) {
FortezzaKEAContext *kea_context =
(FortezzaKEAContext *) ss->ssl3->kea_context;
FortezzaContext *write_context,*read_context;
kea_context->tek = sid->u.ssl3.tek;
kea_context->tek->ref_count++;
if (ss->ssl3->pending_write->bulk_cipher_algorithm ==
cipher_fortezza) {
write_context = ss->ssl3->pending_write->encodeContext;
read_context = ss->ssl3->pending_read->decodeContext;
if (write_context) {
rv = FortezzaSetKey(write_context,
sid->u.ssl3.clientWriteKey);
if (rv < 0) goto loser;
rv = FortezzaLoadIV(write_context,
sid->u.ssl3.clientWriteIV);
if (rv < 0) goto loser;
rv = FortezzaLoadSave(write_context,
sid->u.ssl3.clientWriteSave);
if (rv < 0) goto loser;
}
if (read_context) {
rv = FortezzaSetKey(read_context,
sid->u.ssl3.serverWriteKey);
if (rv < 0) goto loser;
rv = FortezzaLoadIV(read_context,sid->u.ssl3.serverWriteIV);
if (rv < 0) goto loser;
/* we need to know we're doing the 8 byte trash Hack */
rv = FortezzaSetServerInit(read_context);
if (rv < 0) goto loser;
}
}
}
#endif
}
return SECSuccess;
loser:
if (sidBytes.data != NULL)
PORT_ZFree(sidBytes.data, sidBytes.len);
return ssl3_HandshakeFailure(ss);
}
SECStatus
SSL3_HandleServerKeyExchange(SSLSocket *ss, SSL3Opaque *b, long length)
{
SECStatus rv;
SECItem modulus = {siBuffer, NULL, 0};
SECItem exponent = {siBuffer, NULL, 0};
SECItem signature = {siBuffer, NULL, 0};
SECKEYPublicKey *peerKey;
PRArenaPool *arena;
SSL3Hashes hashes;
#ifdef FORTEZZA
FortezzaKEAContext *context;
#endif
SSL_TRC(3, ("%d: SSL3[%d]: handle server key exchange",
SSL_GETPID(), ss->fd));
if (ss->ssl3->hs.ws != wait_server_key &&
ss->ssl3->hs.ws != wait_server_cert) goto loser;
if (ss->sec->peerCert == NULL) goto loser;
switch (ss->ssl3->hs.kea_def->alg) {
case kt_rsa:
rv = ssl3_ConsumeHandshakeVariable(ss, &modulus, 2, &b, &length);
if (rv < 0) goto loser;
rv = ssl3_ConsumeHandshakeVariable(ss, &exponent, 2, &b, &length);
if (rv < 0) goto loser;
rv = ssl3_ConsumeHandshakeVariable(ss, &signature, 2, &b, &length);
if (rv < 0) goto loser;
if (length != 0) goto loser;
/*
* check to make sure the hash is signed by right guy
*/
rv = ssl3_ComputeExportRSAKeyHash(
modulus, exponent, &ss->ssl3->hs.client_random,
&ss->ssl3->hs.server_random, &hashes);
if (rv == SECFailure) goto loser;
rv = ssl3_CheckSignedHashes( &hashes, ss->sec->peerCert, &signature,
ss->sec->getClientAuthDataArg);
if (rv == SECFailure) goto loser;
/*
* we really need to build a new key here because we can no longer
* ignore calling SECKEY_DestroyPublicKey. Using the key may allocate
* pkcs11 slots and ID's.
*/
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (arena == NULL) goto loser;
ss->sec->peerKey = peerKey = (SECKEYPublicKey *)PORT_ArenaZAlloc(
arena, sizeof(SECKEYPublicKey));
if (ss->sec->peerKey == NULL) goto loser;
peerKey->arena = arena;
peerKey->keyType = rsaKey;
peerKey->pkcs11Slot = NULL;
peerKey->pkcs11ID = CK_INVALID_KEY;
peerKey->u.rsa.modulus.data = PORT_ArenaAlloc(arena, modulus.len);
if (peerKey->u.rsa.modulus.data == NULL) goto no_memory;
PORT_Memcpy(peerKey->u.rsa.modulus.data, modulus.data, modulus.len);
peerKey->u.rsa.modulus.len = modulus.len;
peerKey->u.rsa.publicExponent.data =
PORT_ArenaAlloc(arena, exponent.len);
if (peerKey->u.rsa.publicExponent.data == NULL) goto no_memory;
PORT_Memcpy(peerKey->u.rsa.publicExponent.data,
exponent.data, exponent.len);
peerKey->u.rsa.publicExponent.len = exponent.len;
PORT_Free(modulus.data);
PORT_Free(exponent.data);
PORT_Free(signature.data);
ss->ssl3->hs.ws = wait_cert_request;
return SECSuccess;
#ifdef FORTEZZA
case kt_fortezza:
PORT_Assert(ss->ssl3->kea_context);
/* Fortezza need *BOTH* a server cert message and a server key
* exchange message.
*/
if (ss->ssl3->hs.ws == wait_server_cert) goto loser;
context = (FortezzaKEAContext *)ss->ssl3->kea_context;
rv = ssl3_ConsumeHandshake(
ss, &ss->ssl3->R_s, sizeof(context->R_s), &b, &length);
if (rv < 0) goto loser;
ss->ssl3->hs.ws = wait_cert_request;
return SECSuccess;
#endif
default:
goto loser;
}
loser:
if (modulus.data != NULL) PORT_Free(modulus.data);
if (exponent.data != NULL) PORT_Free(exponent.data);
if (signature.data != NULL) PORT_Free(signature.data);
return ssl3_HandshakeFailure(ss);
no_memory:
if (modulus.data != NULL) PORT_Free(modulus.data);
if (exponent.data != NULL) PORT_Free(exponent.data);
if (signature.data != NULL) PORT_Free(signature.data);
return SECFailure;
}
typedef struct dnameNode {
struct dnameNode *next;
SECItem name;
} dnameNode;
SECStatus
SSL3_HandleCertificateRequest(SSLSocket *ss, SSL3Opaque *b, long length)
{
SECStatus rv;
SSL3AlertDescription desc = handshake_failure;
SECItem cert_types;
SSLSecurityInfo *sec = ss->sec;
SSL3State *ssl3 = ss->ssl3;
CERTDistNames ca_list;
PRArenaPool *arena = NULL;
dnameNode *node;
int i, remaining, len, nnames = 0;
unsigned char *data;
SSL_TRC(3, ("%d: SSL3[%d]: handle certificate request",
SSL_GETPID(), ss->fd));
if (ssl3->hs.ws != wait_cert_request) {
desc = unexpected_message;
goto loser;
}
rv = ssl3_ConsumeHandshakeVariable(ss, &cert_types, 1, &b, &length);
if (rv < 0) goto loser;
arena = ca_list.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (arena == NULL) goto no_mem;
remaining = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
if (remaining < 0) goto loser;
desc = unexpected_message;
ca_list.head = node = PORT_ArenaZAlloc(arena, sizeof(dnameNode));
if (node == NULL) goto no_mem;
while (remaining != 0) {
if (remaining < 2) goto loser;
len = node->name.len = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
if (len < 0) goto loser;
remaining -= 2;
if (remaining < len) goto loser;
data = node->name.data = PORT_ArenaAlloc(arena, len);
if (data == NULL) goto no_mem;
rv = ssl3_ConsumeHandshake(ss, data, len, &b, &length);
if (rv < 0) goto loser;
remaining -= len;
nnames++;
if (remaining == 0) break;
node->next = PORT_ArenaZAlloc(arena, sizeof(dnameNode));
node = node->next;
if (node == NULL) goto no_mem;
}
ca_list.nnames = nnames;
ca_list.names = PORT_ArenaAlloc(arena, nnames * sizeof(SECItem));
if (ca_list.names == NULL) goto no_mem;
for(i = 0, node = ca_list.head; i < nnames; i++, node = node->next) {
ca_list.names[i] = node->name;
}
if (length != 0) goto loser;
ssl3->hs.ws = wait_hello_done;
if (sec->getClientAuthData == NULL) {
rv = -1; /* force it to send a no_certificate alert */
} else {
rv = (*sec->getClientAuthData)(sec->getClientAuthDataArg, ss->fd,
&ca_list, &ssl3->clientCertificate,
&ssl3->clientPrivateKey);
}
if (rv < 0) {
if (rv == SECWouldBlock) {
return SECWouldBlock;
} else {
(void)SSL3_SendAlert(ss, alert_warning, no_certificate);
}
} else {
ssl3->clientCertChain = CERT_CertChainFromCert(CERT_GetDefaultCertDB(),
ssl3->clientCertificate);
if (ssl3->clientCertChain == NULL) {
if (ssl3->clientCertificate != NULL) {
CERT_DestroyCertificate(ssl3->clientCertificate);
ssl3->clientCertificate = NULL;
}
if (ssl3->clientPrivateKey != NULL) {
SECKEY_DestroyPrivateKey(ssl3->clientPrivateKey);
ssl3->clientPrivateKey = NULL;
}
(void)SSL3_SendAlert(ss, alert_warning, no_certificate);
}
}
rv = SECSuccess;
goto done;
no_mem:
rv = SECFailure;
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto done;
loser:
rv = SECFailure;
(void)SSL3_SendAlert(ss, alert_fatal, desc);
done:
if (arena != NULL) PORT_FreeArena(arena, PR_FALSE);
return rv;
}
SECStatus
SSL3_HandleServerHelloDone(SSLSocket *ss)
{
SECStatus rv;
SSL3WaitState ws = ss->ssl3->hs.ws;
PRBool send_verify = PR_FALSE;
SSL_TRC(3, ("%d: SSL3[%d]: handle server hello done",
SSL_GETPID(), ss->fd));
if (ws != wait_hello_done && ws != wait_server_cert &&
ws != wait_server_key && ws != wait_cert_request) {
SSL3_SendAlert(ss, alert_fatal, unexpected_message);
return SECFailure;
}
if (ss->ssl3->clientCertChain != NULL &&
ss->ssl3->clientPrivateKey != NULL) {
send_verify = PR_TRUE;
rv = ssl3_SendCertificate(ss);
if (rv < 0) return rv;
}
rv = SSL3_SendClientKeyExchange(ss);
if (rv < 0) return rv;
if (send_verify) {
rv = SSL3_SendCertificateVerify(ss);
if (rv < 0) return rv;
}
rv = ssl3_SendChangeCipherSpecs(ss);
if (rv < 0) return rv;
rv = ssl3_SendFinished(ss);
if (rv < 0) return rv;
ss->ssl3->hs.ws = wait_change_cipher;
return SECSuccess;
}
/*
* Routines used by servers
*/
SECStatus
SSL3_SendHelloRequest(SSLSocket *ss)
{
int rv;
SSL_TRC(3, ("%d: SSL3[%d]: send hello request", SSL_GETPID(), ss->fd));
rv = ssl3_AppendHandshakeHeader(ss, hello_request, 0);
if (rv < 0) return rv;
rv = ssl3_FlushHandshake(ss);
if (rv < 0) return rv;
ss->ssl3->hs.ws = wait_client_hello;
return SECSuccess;
}
static SSLSessionID *
ssl3_NewSessionID(SSLSocket *ss, PRBool is_server)
{
SSLSessionID *sid;
sid = (SSLSessionID*) PORT_ZAlloc(sizeof(SSLSessionID));
if (sid == NULL) return NULL;
sid->addr = ss->sec->ci->peer;
sid->port = ss->sec->ci->port;
if (ss->peerID == NULL) {
sid->peerID = NULL;
} else {
sid->peerID = PORT_Strdup(ss->peerID);
}
sid->u.ssl3.resumable = PR_TRUE;
sid->u.ssl3.policy = SSL_ALLOWED;
sid->references = 1;
sid->cached = never_cached;
sid->version = SSL_LIBRARY_VERSION_3_0;
#ifdef FORTEZZA
sid->u.ssl3.hasFortezza = PR_FALSE;
sid->u.ssl3.clientWriteKey = NULL;
sid->u.ssl3.serverWriteKey = NULL;
sid->u.ssl3.tek = NULL;
#endif
if (is_server) {
int pid = SSL_GETPID();
sid->u.ssl3.sessionIDLength = SSL3_SESSIONID_BYTES;
sid->u.ssl3.sessionID[0] = (pid >> 8) & 0xff;
sid->u.ssl3.sessionID[1] = pid & 0xff;
RNG_GenerateGlobalRandomBytes(
sid->u.ssl3.sessionID + 2, SSL3_SESSIONID_BYTES -2);
}
return sid;
}
static SECStatus
ssl3_SendServerHelloSequence(SSLSocket *ss, SSL3CipherSpec *spec)
{
SECStatus rv;
const SSL3KEADef *kea_def;
SSL_TRC(3, ("%d: SSL3[%d]: begin server hello sequence",
SSL_GETPID(), ss->fd));
rv = SSL3_SendServerHello(ss);
if (rv < 0) return rv;
rv = ssl3_SendCertificate(ss);
if (rv < 0) return rv;
/* We have to do this here, because kea_def is not set up
* until SSL3_SendServerHello(). */
kea_def = ss->ssl3->hs.kea_def;
/* XXX we should really check the key length against the limit */
if (kea_def->is_limited && exportKey != NULL) {
rv = ssl3_SendServerKeyExchange(ss);
if (rv < 0) return rv;
}
else if (kea_def->alg == kt_fortezza) {
rv = ssl3_SendServerKeyExchange(ss);
if (rv < 0) return rv;
}
if (ss->requestCertificate) {
rv = ssl3_SendCertificateRequest(ss);
if (rv < 0) return rv;
}
rv = ssl3_SendServerHelloDone(ss);
if (rv < 0) return rv;
if (ss->requestCertificate) {
ss->ssl3->hs.ws = wait_client_cert;
} else {
ss->ssl3->hs.ws = wait_client_key;
}
return SECSuccess;
}
SECStatus
SSL3_HandleClientHello(SSLSocket *ss, SSL3Opaque *b, long length)
{
SECStatus rv;
SSL3ProtocolVersion version;
SECItem sidBytes = {siBuffer, NULL, 0};
SSLSessionID *sid = NULL;
SSL3State *ssl3 = ss->ssl3;
int i, j;
SECItem suites = {siBuffer, NULL, 0};
SECItem comps = {siBuffer, NULL, 0};
SSLConnectInfo *ci;
SSL3CipherSpec *spec;
#ifdef FORTEZZA
SSL3KeyExchangeAlgorithm kea;
SSL3BulkCipher cipher;
PRBool use_fortezza = PR_FALSE;
#endif
SSL_TRC(3, ("%d: SSL3[%d]: handle client hello", SSL_GETPID(), ss->fd));
/* Get peer name of client */
rv = ssl_GetPeerInfo(ss);
if (rv < 0) {
goto loser;
}
rv = ssl3_InitState(ss);
if (rv < 0) return SECFailure;
if ((ss->ssl3->hs.ws != wait_client_hello) &&
(ss->ssl3->hs.ws != idle_handshake)) {
goto loser;
}
ci = ss->sec->ci;
spec = ssl3->pending_read;
#ifdef FORTEZZA
/* don't even try fortezza if it has been turned off */
if (SSL_IsEnabledGroup(SSL_GroupFortezza)) {
rv = FortezzaOpen(ss,(FortezzaKEAContext **)&ssl3->kea_context);
switch (rv) {
case SECSuccess:
use_fortezza = PR_TRUE;
break;
case SECFailure:
ssl_TmpEnableGroup(&localCipherSuites,&localCipherSuiteCount,
SSL_GroupFortezza,PR_FALSE);
break; /* Failed, don't use the Fortezza card */
default:
return (rv); /* SECWouldBlock */
}
}
#endif
version = (SSL3ProtocolVersion)ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
if (version < SSL_LIBRARY_VERSION_3_0)
goto loser;
if (version > SSL_LIBRARY_VERSION_3_0)
version = SSL_LIBRARY_VERSION_3_0; /* nothing else supported yet */
rv = ssl3_ConsumeHandshake(
ss, &ss->ssl3->hs.client_random, SSL3_RANDOM_LENGTH, &b, &length);
if (rv < 0) goto loser;
rv = ssl3_ConsumeHandshakeVariable(ss, &sidBytes, 1, &b, &length);
if (rv < 0) goto loser;
if (sidBytes.len > 0) {
SSL_TRC(7, ("%d: SSL3[%d]: server, lookup client session-id for 0x%08x",
SSL_GETPID(), ss->fd, ci->peer));
sid = (*ssl_sid_lookup)(ci->peer, sidBytes.data, sidBytes.len);
}
SECITEM_FreeItem(&sidBytes, PR_FALSE);
rv = ssl3_ConsumeHandshakeVariable(ss, &suites, 2, &b, &length);
if (rv < 0) goto loser;
rv = ssl3_ConsumeHandshakeVariable(ss, &comps, 1, &b, &length);
if (rv < 0) goto loser;
if (sid != NULL) {
if ((sid->peerCert == NULL) && ss->requestCertificate) {
ss->sec->uncache(sid);
ssl_FreeSID(sid);
sid = NULL;
}
}
for (i = 0; i < suites.len; i += 2) {
for (j = 0; j < cipherSuiteCount; j++) {
if (config_match(&cipherSuites[j], ss->ssl3->policy, PR_TRUE) &&
(suites.data[i] == (cipherSuites[j].cipher_suite>>8) & 0xff) &&
(suites.data[i + 1] == cipherSuites[j].cipher_suite & 0xff)) {
ss->ssl3->hs.cipher_suite = cipherSuites[j].cipher_suite;
ss->ssl3->hs.suite_def =
ssl_LookupCipherSuiteDef(ss->ssl3->hs.cipher_suite);
goto suite_found;
}
}
}
PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
goto loser;
suite_found:
for (i = 0; i < comps.len; i++) {
for (j = 0; j < compressionMethodsCount; j++) {
if (comps.data[i] == compressions[j]) {
ss->ssl3->hs.compression = compressions[j];
goto compression_found;
}
}
}
PORT_SetError(SSL_ERROR_BAD_CLIENT); /* null compression must be supported */
goto loser;
compression_found:
PORT_Free(suites.data);
suites.data = NULL;
PORT_Free(comps.data);
comps.data = NULL;
ss->sec->send = ssl3_SendApplicationData;
#ifdef FORTEZZA
kea = ss->ssl3->hs.suite_def->key_exchange_algorithm;
cipher = ss->ssl3->hs.suite_def->bulk_cipher_algorithm;
/* can only resume fortezza if we have fortezza info */
if (kea == kea_fortezza) {
if (sid) {
/* this sid does not have enough fortezza info to resume */
if (!sid->u.ssl3.hasFortezza) {
ss->sec->uncache(sid);
ssl_FreeSID(sid);
sid = NULL;
} else {
/* force us to use the same card */
FortezzaKEAContext *kea_context =
(FortezzaKEAContext *) ssl3->kea_context;
if (kea_context->socketId != sid->u.ssl3.fortezzaSocket) {
FortezzaDestroyKEAContext(kea_context,PR_TRUE);
kea_context = FortezzaCreateKEAContextFromSocket
(sid->u.ssl3.fortezzaSocket);
ssl3->kea_context = kea_context;
if (kea_context == NULL) goto loser;
}
}
}
}
#endif
if (sid != NULL ) {
if (ci->sid) {
ss->sec->uncache(ci->sid);
ssl_FreeSID(ci->sid);
}
ci->sid = sid;
PORT_Memcpy(ssl3->pending_read->master_secret, sid->u.ssl3.masterSecret,
sizeof(ssl3->pending_read->master_secret));
if (sid->peerCert != NULL) {
ss->sec->peerCert = CERT_DupCertificate(sid->peerCert);
}
/*
* XXX make sure cipher specs and compression still match
*/
ssl3->hs.isResuming = PR_TRUE;
rv = SSL3_SendServerHello(ss);
if (rv < 0) goto loser;
rv = InitPendingCipherSpec(ss, ss->ssl3, NULL);
if (rv < 0) goto loser;
#ifdef FORTEZZA
if (kea == kea_fortezza) {
FortezzaKEAContext *kea_context =
(FortezzaKEAContext *) ssl3->kea_context;
FortezzaContext *write_context,*read_context;
kea_context->tek = sid->u.ssl3.tek;
kea_context->tek->ref_count++;
if (cipher == cipher_fortezza) {
write_context = ssl3->pending_write->encodeContext;
read_context = ssl3->pending_read->decodeContext;
if (write_context) {
rv = FortezzaSetKey(write_context,
sid->u.ssl3.serverWriteKey);
if (rv < 0) goto loser;
rv = FortezzaLoadIV(write_context,
sid->u.ssl3.serverWriteIV);
if (rv < 0) goto loser;
}
if (read_context) {
rv = FortezzaSetKey(read_context,
sid->u.ssl3.clientWriteKey);
if (rv < 0) goto loser;
rv = FortezzaLoadIV(read_context,sid->u.ssl3.clientWriteIV);
if (rv < 0) goto loser;
}
}
}
#endif
rv = ssl3_SendChangeCipherSpecs(ss);
if (rv < 0) goto loser;
rv = ssl3_SendFinished(ss);
ssl3->hs.ws = wait_change_cipher;
if (rv < 0) goto loser;
} else {
sid = ssl3_NewSessionID(ss, PR_TRUE);
if (sid == NULL) goto loser;
ci->sid = sid;
ssl3->hs.isResuming = PR_FALSE;
rv = ssl3_SendServerHelloSequence(ss, spec);
if (rv < 0) goto loser;
}
if (length != 0) goto loser;
return SECSuccess;
loser:
if (sidBytes.data != NULL) PORT_Free(sidBytes.data);
if (suites.data != NULL) PORT_Free(suites.data);
if (comps.data != NULL) PORT_Free(comps.data);
return ssl3_HandshakeFailure(ss);
}
/*
* SSL3_HandleV2ClientHello is used when a V2 formatted hello comes
* in asking to use the V3 handshake.
*/
SECStatus
SSL3_HandleV2ClientHello(SSLSocket *ss, unsigned char *buffer, int length)
{
SSLSessionID *sid = NULL;
int i, j;
SECStatus rv;
SSL3CipherSpec *spec;
SSLConnectInfo *ci = ss->sec->ci;
SSL3ProtocolVersion version;
int sid_length, suite_length, rand_length;
unsigned char *suites, *random;
SSL_TRC(3, ("%d: SSL3[%d]: handle v2 client hello", SSL_GETPID(), ss->fd));
rv = ssl3_InitState(ss);
if (rv < 0) return SECFailure;
if (ss->ssl3->hs.ws != wait_client_hello) goto loser;
#ifdef FORTEZZA
/* don't even try fortezza if it has been turned off */
if (SSL_IsEnabledGroup(SSL_GroupFortezza)) {
rv = FortezzaOpen(ss,(FortezzaKEAContext **)&ss->ssl3->kea_context);
switch (rv) {
case SECSuccess:
break;
case SECFailure:
ssl_TmpEnableGroup(&localCipherSuites,&localCipherSuiteCount,
SSL_GroupFortezza,PR_FALSE);
break; /* Failed, don't use the Fortezza card */
default:
return (rv); /* SECWouldBlock */
}
}
#endif
version = (buffer[1] << 8) | buffer[2];
suite_length = (buffer[3] << 8) | buffer[4];
sid_length = (buffer[5] << 8) | buffer[6];
rand_length = (buffer[7] << 8) | buffer[8];
if ((sid_length > 0) || /* known session => v3 client */
(length != SSL_HL_CLIENT_HELLO_HBYTES + suite_length + rand_length)) {
SSL_DBG(("%d: SSL3[%d]: bad v2 client hello message, len=%d should=%d",
SSL_GETPID(), ss->fd, length,
SSL_HL_CLIENT_HELLO_HBYTES + suite_length + rand_length));
goto loser;
}
suites = buffer + SSL_HL_CLIENT_HELLO_HBYTES;
random = suites + suite_length;
spec = ss->ssl3->pending_read;
if (rand_length < SSL_MIN_CHALLENGE_BYTES ||
rand_length > SSL_MAX_CHALLENGE_BYTES) goto loser;
PORT_Assert(SSL_MAX_CHALLENGE_BYTES == SSL3_RANDOM_LENGTH);
PORT_Memset(&ss->ssl3->hs.client_random, 0, SSL3_RANDOM_LENGTH);
PORT_Memcpy(
&ss->ssl3->hs.client_random.rand[SSL3_RANDOM_LENGTH - rand_length],
random, rand_length);
PRINT_BUF(60, (ss, "client random:", &ss->ssl3->hs.client_random.rand[0],
SSL3_RANDOM_LENGTH));
/* XXX
* Should we get our favorite, rather than the client's favorite
*/
for (j = 0; j < cipherSuiteCount; j++) {
for (i = 0; i < suite_length; i += 3) {
if (config_match(&cipherSuites[j], ss->ssl3->policy, PR_TRUE) &&
(suites[i] == 0) &&
(suites[i+1] == ((cipherSuites[j].cipher_suite >> 8) & 0xff)) &&
(suites[i + 2] == (cipherSuites[j].cipher_suite & 0xff))) {
ss->ssl3->hs.cipher_suite = cipherSuites[j].cipher_suite;
ss->ssl3->hs.suite_def =
ssl_LookupCipherSuiteDef(ss->ssl3->hs.cipher_suite);
goto suite_found;
}
}
}
SSL3_SendAlert(ss, alert_fatal, handshake_failure);
PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
return SECFailure;
suite_found:
ss->ssl3->hs.compression = compression_null;
ss->sec->send = ssl3_SendApplicationData;
sid = ssl3_NewSessionID(ss, PR_TRUE);
if (sid == NULL) goto loser;
ci->sid = sid;
/* We have to update the handshake hashes before we can send stuff */
SSL3_UpdateHandshakeHashes(ss->ssl3, buffer, length);
/* do not worry about memory leak of sid since it now belongs to ci */
rv = ssl3_SendServerHelloSequence(ss, spec);
if (rv < 0) goto loser;
/* We need to return SECWouldBlock so that the server doesn't block. */
return SECWouldBlock;
loser:
PORT_SetError(SSL_ERROR_BAD_CLIENT);
/* what kind of error should be sent back XXX */
return SECFailure;
}
static SECStatus
SSL3_SendServerHello(SSLSocket *ss)
{
SSL3ProtocolVersion version = SSL_LIBRARY_VERSION_3_0;
SSLSessionID *sid;
SECStatus rv;
long length;
SSL_TRC(3, ("%d: SSL3[%d]: send server hello", SSL_GETPID(), ss->fd));
PORT_Assert(ss->sec && ss->sec->ci);
sid = ss->sec->ci->sid;
length = sizeof(SSL3ProtocolVersion) + SSL3_RANDOM_LENGTH +
1 + ((sid == NULL) ? 0: SSL3_SESSIONID_BYTES) +
sizeof(SSL3CipherSuite) + 1;
rv = ssl3_AppendHandshakeHeader(ss, server_hello, length);
if (rv < 0) return rv;
rv = ssl3_AppendHandshakeNumber(ss, version, 2);
if (rv < 0) return rv;
rv = ssl3_GetNewRandom(&ss->ssl3->hs.server_random);
if (rv < 0) return rv;
rv = ssl3_AppendHandshake(
ss, &ss->ssl3->hs.server_random, SSL3_RANDOM_LENGTH);
if (rv < 0) return rv;
if (sid)
rv = ssl3_AppendHandshakeVariable(
ss, sid->u.ssl3.sessionID, sid->u.ssl3.sessionIDLength, 1);
else
rv = ssl3_AppendHandshakeVariable(ss, NULL, 0, 1);
if (rv < 0) return rv;
rv = ssl3_AppendHandshakeNumber(ss, ss->ssl3->hs.cipher_suite, 2);
if (rv < 0) return rv;
rv = ssl3_AppendHandshakeNumber(ss, ss->ssl3->hs.compression, 1);
if (rv < 0) return rv;
ssl3_SetupPendingCipherSpec(ss, ss->ssl3);
return SECSuccess;
}
static SECStatus
ssl3_SignExportRSAKeyHash(SECKEYPrivateKey *server_key, SSL3Hashes *hashes,
SECItem *signed_hash)
{
int rv;
PORT_Assert(signed_hash != NULL);
PORT_Assert(signed_hash->data == NULL);
signed_hash->data = PORT_Alloc(MAX_RSA_MODULUS_LEN);
if (signed_hash->data == NULL) return SECFailure;
rv = ssl3_SignHashes(hashes, server_key, signed_hash);
if (rv == SECFailure) {
PORT_Free(signed_hash->data);
return SECFailure;
}
return SECSuccess;
}
static SECStatus
ssl3_SendServerKeyExchange(SSLSocket *ss)
{
SECStatus rv = SECFailure;
int length;
SSL3Hashes hashes;
SECItem signed_hash = {siBuffer, NULL, 0};
const SSL3KEADef *kea_def = ss->ssl3->hs.kea_def;
#ifndef FORTEZZA
/* XXX only works for RSA */
#else
FortezzaKEAContext *kea_context;
#endif
SSL_TRC(3, ("%d: SSL3[%d]: send server key exchange",
SSL_GETPID(), ss->fd));
switch (kea_def->alg) {
case kt_rsa:
rv = ssl3_ComputeExportRSAKeyHash(exportPubKey->u.rsa.modulus,
exportPubKey->u.rsa.publicExponent,
&ss->ssl3->hs.client_random,&ss->ssl3->hs.server_random,&hashes);
if (rv == SECFailure) return rv;
rv = ssl3_SignExportRSAKeyHash(usKey, &hashes, &signed_hash);
if (rv == SECFailure) return rv;
if (signed_hash.data == NULL) goto loser;
length = 2 + exportPubKey->u.rsa.modulus.len +
2 + exportPubKey->u.rsa.publicExponent.len +
2 + signed_hash.len;
rv = ssl3_AppendHandshakeHeader(ss, server_key_exchange, length);
if (rv < 0) goto loser;
rv = ssl3_AppendHandshakeVariable(ss, exportPubKey->u.rsa.modulus.data,
exportPubKey->u.rsa.modulus.len, 2);
if (rv < 0) goto loser;
rv = ssl3_AppendHandshakeVariable(ss,
exportPubKey->u.rsa.publicExponent.data,
exportPubKey->u.rsa.publicExponent.len,
2);
if (rv < 0) goto loser;
rv = ssl3_AppendHandshakeVariable(
ss, signed_hash.data, signed_hash.len, 2);
if (rv < 0) goto loser;
PORT_Free(signed_hash.data);
return SECSuccess;
break;
case kt_fortezza:
#ifdef FORTEZZA
rv = PK11_FortezzaGenerateRa(fortezzaServerKey,ssl->ssl3->R_s);
if (rv < 0) return rv;
/* don't waste time signing the random number */
length = sizeof (ssl->ssl3->R_s) /*+ 2 + signed_hash.len*/;
rv = ssl3_AppendHandshakeHeader(ss, server_key_exchange, length);
if (rv < 0) goto loser;
rv = ssl3_AppendHandshake( ss, &ssl->ssl3->R_s,
sizeof(ssl->ssl3->R_s));
if (rv < 0) goto loser;
return SECSuccess;
#endif
case kt_null:
case kt_dh:
break;
}
loser:
if (signed_hash.data != NULL) PORT_Free(signed_hash.data);
return SECFailure;
}
SECStatus
ssl3_SendCertificateRequest(SSLSocket *ss)
{
SECStatus rv;
int length, i, calen = 0;
SECItem *name;
CERTDistNames *ca_list;
SECItem *names = NULL;
int nnames = 0;
SSL_TRC(3, ("%d: SSL3[%d]: send certificate request",
SSL_GETPID(), ss->fd));
if (ss->ssl3->ca_list != NULL) {
ca_list = ss->ssl3->ca_list;
} else {
ca_list = ssl3_server_ca_list;
}
if (ca_list != NULL) {
names = ca_list->names;
nnames = ca_list->nnames;
}
for(i = 0, name = names; i < nnames; i++, name++) {
calen += 2 + name->len;
}
length = 1 + sizeof(certificate_types) + 2 + calen;
rv = ssl3_AppendHandshakeHeader(ss, certificate_request, length);
if (rv < 0) return rv;
rv = ssl3_AppendHandshakeVariable(
ss, certificate_types, sizeof(certificate_types), 1);
if (rv < 0) return rv;
rv = ssl3_AppendHandshakeNumber(ss, calen, 2);
if (rv < 0) return rv;
for (i = 0, name = names; i < nnames; i++, name++) {
rv = ssl3_AppendHandshakeVariable(ss, name->data, name->len, 2);
if (rv < 0) return rv;
}
return SECSuccess;
}
static SECStatus
ssl3_SendServerHelloDone(SSLSocket *ss)
{
SECStatus rv;
SSL_TRC(3, ("%d: SSL3[%d]: send server hello done", SSL_GETPID(), ss->fd));
rv = ssl3_AppendHandshakeHeader(ss, server_hello_done, 0);
if (rv < 0) return rv;
rv = ssl3_FlushHandshake(ss);
if (rv < 0) return rv;
return SECSuccess;
}
static SECStatus
ssl3_HandleCertificateVerify(SSLSocket *ss, SSL3Opaque *b, long length,
SSL3Hashes *hashes)
{
SECItem signed_hash = {siBuffer, NULL, 0};
SECStatus rv;
SSL_TRC(3, ("%d: SSL3[%d]: handle certificate verify",
SSL_GETPID(), ss->fd));
if (ss->ssl3->hs.ws != wait_cert_verify) goto loser;
if (ss->sec->peerCert == NULL) goto loser;
rv = ssl3_ConsumeHandshakeVariable(ss, &signed_hash, 2, &b, &length);
if (rv < 0) goto loser;
/* verify that the key & kea match */
rv = ssl3_CheckSignedHashes(hashes, ss->sec->peerCert, &signed_hash,
ss->sec->getClientAuthDataArg);
if (rv < 0) goto loser;
PORT_Free(signed_hash.data);
signed_hash.data = NULL;
if (length != 0) goto loser;
ss->ssl3->hs.ws = wait_change_cipher;
return SECSuccess;
loser:
if (signed_hash.data != NULL) PORT_Free(signed_hash.data);
return ssl3_HandshakeFailure(ss);
}
SECStatus
SSL3_HandleClientKeyExchange(SSLSocket *ss, SSL3Opaque *b, long length)
{
SECKEYPrivateKey *serverKey = NULL;
SECItem enc_pms;
PK11SymKey *symKey;
int rv;
SSL_TRC(3, ("%d: SSL3[%d]: handle client key exchange",
SSL_GETPID(), ss->fd));
if (ss->ssl3->hs.ws != wait_client_key) {
SSL3_SendAlert(ss, alert_fatal, unexpected_message);
return SECFailure;
}
if (exportKey != NULL && ss->ssl3->hs.kea_def->is_limited) {
serverKey = exportKey;
} else {
serverKey = usKey;
}
switch (ss->ssl3->hs.kea_def->alg) {
case kt_rsa:
/*
* decrypt out of the incoming buffer
*/
enc_pms.data = b;
enc_pms.len= length;
symKey = PK11_PubUnwrapSymKey(serverKey, &enc_pms,
CKM_SSL3_PRE_MASTER_KEY_GEN, 0);
if (symKey == NULL) return SECFailure;
rv = PK11_ExtractKeyValue(symKey);
if (rv != SECSuccess) {
PK11_FreeSymKey(symKey);
return SECFailure;
}
PRINT_BUF(60, (ss, "decrypted premaster secret:",
PK11_GetKeyData(symKey)->data,PK11_GetKeyData(symKey)->len));
rv = InitPendingCipherSpec(ss, ss->ssl3, PK11_GetKeyData(symKey));
PK11_FreeSymKey(symKey);
if (rv < 0) return SECFailure;
break;
#ifdef FORTEZZA
case kt_fort:
/* get the structure */
kea_context = (FortezzaKEAContext *)ss->ssl3->kea_context;
tek = FortezzaTEKAlloc(kea_context);
kea_context->tek = tek; /* Adopt the allocated TEK */
if (tek == NULL) return SECFailure;
rv = ssl3_ConsumeHandshakeVariable(ss,&fortezza_pms.y_c, 1,&b,&length);
if (rv < 0) return rv;
rv = ssl3_ConsumeHandshake( ss, &fortezza_pms.r_c,
sizeof(fortezza_pms)-sizeof(fortezza_pms.y_c), &b, &length);
if (rv < 0) return rv;
/* build a Token Encryption key (tek) TEK's can never be unloaded
* from the card, but given these parameters, and *OUR* fortezza
* card, we can always regenerate the same one on the fly. */
tek->keyRegister = KeyNotLoaded;
tek->keyType = TEK;
/* get the clients public key */
server_cert = FortezzaGetCertificate(
fortezzaServerKey->fortezzaKeyData.socket,
fortezzaServerKey->fortezzaKeyData.certificate,
fortezzaServerKey->fortezzaKeyData.serial);
if (server_cert == NULL) return SECFailure;
if (ss->sec->peerCert != NULL &&
(FortezzaComparePQG(ss->sec->peerCert,server_cert) == 0)) {
CERTCertificate *cert = ss->sec->peerCert;
SECKEYPublicKey *pubkey;
pubkey = CERT_ExtractPublicKey(&cert->subjectPublicKeyInfo);
if (pubkey == NULL) {
CERT_DestroyCertificate(server_cert);
return SECFailure;
}
PORT_Assert(pubkey->keyType == fortezzaKeys); /* paranoia */
PORT_Memcpy(tek->keyData.tek.Y_b,
pubkey->fortezzaKeyData.KEAKey.data,
pubkey->fortezzaKeyData.KEAKey.len);
tek->keyData.tek.Y_bSize = pubkey->fortezzaKeyData.KEAKey.len;
SECKEY_DestroyPublicKey(pubkey);
} else if (fortezza_pms.y_c.len != 0) {
PORT_Memcpy(tek->keyData.tek.Y_b,fortezza_pms.y_c.data,
fortezza_pms.y_c.len);
tek->keyData.tek.Y_bSize = fortezza_pms.y_c.len;
/* if we have client auth on, check the signature */
if (ss->sec->peerCert) {
unsigned char hash[20];
SECKEYPublicKey *pubkey;
pubkey = CERT_ExtractPublicKey(
&ss->sec->peerCert->subjectPublicKeyInfo);
if (pubkey == NULL) {
CERT_DestroyCertificate(server_cert);
return SECFailure;
}
PORT_Assert(pubkey->keyType == fortezzaKeys); /* paranoia */
ssl3_ComputeFortezzaPublicKeyHash(
tek->keyData.tek.Y_b,hash);
if (DSAVerify(hash, fortezza_pms.y_signature, pubkey)
== PR_FALSE) {
/* probably not the correct allert */
SSL3_SendAlert(ss,alert_fatal,illegal_parameter);
CERT_DestroyCertificate(server_cert);
SECKEY_DestroyPublicKey(pubkey);
return SECFailure;
}
SECKEY_DestroyPublicKey(pubkey);
}
} else {
SSL3_SendAlert(ss,alert_fatal,illegal_parameter);
CERT_DestroyCertificate(server_cert);
return SECFailure;
}
CERT_DestroyCertificate(server_cert);
tek->keyData.tek.certificate =
fortezzaServerKey->fortezzaKeyData.certificate;
PORT_Memcpy(tek->keyData.tek.R_b,kea_context->R_s,
sizeof(tek->keyData.tek.R_b));
PORT_Memcpy(tek->keyData.tek.R_a,fortezza_pms.r_c,
sizeof(tek->keyData.tek.R_a));
tek->keyData.tek.flags = CI_RECIPIENT_FLAG;
/* decrypt the pre-Master secret with the TEK */
rv = FortezzaCryptPremaster(Decrypt,kea_context,tek,
fortezza_pms.master_secret_iv,
fortezza_pms.encrypted_preMasterSecret, buffer,
sizeof(fortezza_pms.encrypted_preMasterSecret));
if (rv < 0) { goto fortezza_loser; }
pms.data = buffer;
pms.len = sizeof(fortezza_pms.encrypted_preMasterSecret);
rv = InitPendingCipherSpec(ss, ss->ssl3, &pms);
PORT_Memset(buffer, 0, sizeof(fortezza_pms.encrypted_preMasterSecret));
if (rv < 0) { goto fortezza_loser; }
/* get the context pointers (to save on long code and protect against
* long dereferences in stupid compiliers) */
if (ss->ssl3->pending_write->bulk_cipher_algorithm == cipher_fortezza) {
write_context = ss->ssl3->pending_write->encodeContext;
read_context = ss->ssl3->pending_read->decodeContext;
} else {
/* fortezza NULL_SHA or RC4 don't have fortezza contexts...
* create some temporary ones.
*/
tmp_write = FortezzaCreateContext(kea_context,Encrypt);
write_context = tmp_write;
tmp_read= FortezzaCreateContext(kea_context,Decrypt);
read_context = tmp_read;
}
/* UnwrapKey used the key to unwrap the key into a free storeage,
* then stores a Ks Wrapped MEK in the context so we can load it.
* at will without the TEK.
*/
rv = FortezzaUnwrapKey(write_context,tek,
fortezza_pms.wrapped_server_write_key);
if (rv < 0) { goto fortezza_loser; }
/*
* Load IV it also activates our context (until now we couldn't
* issue an operation on the context. After this call we can).
*/
rv = FortezzaLoadIV(write_context,fortezza_pms.server_write_iv);
if (rv < 0) { goto fortezza_loser; }
/* now get the client write (our read) keys */
rv = FortezzaUnwrapKey(read_context,tek,
fortezza_pms.wrapped_client_write_key);
if (rv < 0) { goto fortezza_loser; }
rv = FortezzaLoadIV(read_context,fortezza_pms.client_write_iv);
if (rv < 0) { goto fortezza_loser; }
sid->u.ssl3.hasFortezza = PR_TRUE;
sid->u.ssl3.fortezzaSocket = kea_context->socketId;
sid->u.ssl3.tek = kea_context->tek;
sid->u.ssl3.tek->ref_count++;
sid->u.ssl3.clientWriteKey = read_context->theKey;
FortezzaKeyReference(sid->u.ssl3.clientWriteKey);
sid->u.ssl3.serverWriteKey = write_context->theKey;
FortezzaKeyReference(sid->u.ssl3.serverWriteKey);
PORT_Memcpy(sid->u.ssl3.clientWriteIV,read_context->iv,
sizeof(FortezzaIV));
PORT_Memcpy(sid->u.ssl3.serverWriteIV,write_context->iv,
sizeof(FortezzaIV));
if (tmp_read) FortezzaDestroyContext(tmp_read,PR_TRUE);
if (tmp_write) FortezzaDestroyContext(tmp_write,PR_TRUE);
break;
fortezza_loser:
if (tmp_read) FortezzaDestroyContext(tmp_read,PR_TRUE);
if (tmp_write) FortezzaDestroyContext(tmp_write,PR_TRUE);
return rv;
#endif
default:
return ssl3_HandshakeFailure(ss);
}
if (ss->sec->peerCert != NULL)
ss->ssl3->hs.ws = wait_cert_verify;
else
ss->ssl3->hs.ws = wait_change_cipher;
return SECSuccess;
}
/*
* used by both
*/
SECStatus
ssl3_SendCertificate(SSLSocket *ss)
{
int rv;
CERTCertificateList *certChain;
int len = 0;
int i;
SSL_TRC(3, ("%d: SSL3[%d]: send certificate", SSL_GETPID(), ss->fd));
#ifdef FORTEZZA
certChain = (ss->sec->isServer) ?
((ss->ssl3->hs.kea == kea_fortezza) ?
ssl3_fortezza_server_cert_chain : ssl3_server_cert_chain) :
ss->ssl3->clientCertChain;
#else
certChain = (ss->sec->isServer) ? ssl3_server_cert_chain :
ss->ssl3->clientCertChain;
#endif
for (i = 0; i < certChain->len; i++) {
len += certChain->certs[i].len + 3;
}
rv = ssl3_AppendHandshakeHeader(ss, certificate, len + 3);
if (rv < 0) return rv;
rv = ssl3_AppendHandshakeNumber(ss, len, 3);
if (rv < 0) return rv;
for (i = 0; i < certChain->len; i++) {
rv = ssl3_AppendHandshakeVariable(ss, certChain->certs[i].data,
certChain->certs[i].len, 3);
if (rv < 0) return rv;
}
return SECSuccess;
}
#ifndef FORTEZZA
typedef struct certNode {
struct certNode *next;
CERTCertificate *cert;
} SSL3CertNode;
#endif
SECStatus
SSL3_HandleCertificate(SSLSocket *ss, SSL3Opaque *b, long length)
{
SECItem certItem;
SSL3CertNode *certs = NULL, *c;
PRArenaPool *arena = NULL;
SSL3AlertDescription desc = bad_certificate;
SECStatus rv;
long remaining;
long size;
SSL3State *ssl3 = ss->ssl3;
SSLSecurityInfo *sec = ss->sec;
PRBool isServer;
CERTCertificate *cert;
SSL_TRC(3, ("%d: SSL3[%d]: handle certificate", SSL_GETPID(), ss->fd));
if (ssl3->hs.ws != wait_server_cert && ssl3->hs.ws != wait_client_cert) {
desc = unexpected_message;
goto loser;
}
PORT_Assert(ssl3->peerCertArena == NULL);
if (sec->peerCert != NULL) {
if (sec->peerKey) {
SECKEY_DestroyPublicKey(sec->peerKey);
sec->peerKey = NULL;
}
CERT_DestroyCertificate(sec->peerCert);
sec->peerCert = NULL;
}
/* XXX need to make sure everything gets freed up when we lose */
remaining = ssl3_ConsumeHandshakeNumber(ss, 3, &b, &length);
if (remaining < 0) goto bad_cert;
ssl3->peerCertArena = arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( arena == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
/* First get the peer cert. */
remaining -= 3;
if (remaining < 0) goto bad_cert;
size = ssl3_ConsumeHandshakeNumber(ss, 3, &b, &length);
if (size < 0) goto bad_cert;
remaining -= size;
if (remaining < 0) goto bad_cert;
certItem.data = PORT_ArenaAlloc(arena, size);
if (certItem.data == NULL) goto loser;
certItem.len = size;
rv = ssl3_ConsumeHandshake(ss, certItem.data, certItem.len, &b,
&length);
if (rv < 0) goto bad_cert;
ss->sec->peerCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
&certItem, NULL, PR_FALSE,
PR_TRUE);
if (ss->sec->peerCert == NULL) goto bad_cert;
/* Now get all of the CA certs. */
while(remaining != 0) {
remaining -= 3;
if (remaining < 0) goto bad_cert;
size = ssl3_ConsumeHandshakeNumber(ss, 3, &b, &length);
if (size < 0) goto bad_cert;
remaining -= size;
if (remaining < 0) goto bad_cert;
certItem.data = PORT_ArenaAlloc(arena, size);
if (certItem.data == NULL) goto loser;
certItem.len = size;
rv = ssl3_ConsumeHandshake(ss, certItem.data, certItem.len, &b,
&length);
if (rv < 0) goto bad_cert;
c = PORT_ArenaAlloc(arena, sizeof(SSL3CertNode));
if (c == NULL) goto loser;
c->cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
&certItem, NULL, PR_FALSE, PR_TRUE);
if (c->cert == NULL) goto loser;
c->next = certs;
certs = c;
}
#ifdef FORTEZZA
if (remaining != 0) goto bad_cert;
FortezzaPQGUpdate(certs,ss->sec->peerCert);
#endif
if ( ss->sec->isServer ) {
isServer = PR_TRUE;
} else {
isServer = PR_FALSE;
}
rv = (int)(* ss->sec->authCertificate)(ss->sec->authCertificateArg,
ss->fd, PR_TRUE, isServer);
if (rv) {
if ( ss->sec->handleBadCert ) {
rv = (* ss->sec->handleBadCert )(ss->sec->badCertArg, ss->fd);
if ( rv ) {
if ( rv == -2 ) {
/* someone will handle this connection asynchronously*/
SSL_DBG(("%d: SSL3[%d]: go to async cert handler",
SSL_GETPID(), ss->fd));
ssl3->peerCertChain = certs;
certs = NULL;
goto cert_block;
}
/* cert is bad */
goto bad_cert;
}
/* cert is good */
} else {
goto bad_cert;
}
}
cert = ss->sec->peerCert;
if (policy_some_restricted && (ssl3->policy == SSL_ALLOWED) &&
(CERT_VerifyCert(cert->dbhandle, cert, PR_FALSE,
certUsageSSLServerWithStepUp,
PR_Now(), ss->sec->authCertificateArg,
NULL) == SECSuccess)) {
ssl3->policy = SSL_RESTRICTED;
ssl3->hs.rehandshake = PR_TRUE;
}
/* We don't need the CA certs now that we've authenticated the peer cert. */
for (; certs; certs = certs->next) {
CERT_DestroyCertificate(certs->cert);
}
PORT_FreeArena(arena, PR_FALSE);
ssl3->peerCertArena = NULL;
ssl3->peerCertChain = NULL;
ss->sec->ci->sid->peerCert = CERT_DupCertificate(ss->sec->peerCert);
cert_block:
if (ss->sec->isServer) {
ssl3->hs.ws = wait_client_key;
} else {
ssl3->hs.ws = wait_cert_request;
if(ssl3->hs.kea_def->is_limited) {
SECKEYPublicKey *key =
CERT_ExtractPublicKey(&ss->sec->peerCert->subjectPublicKeyInfo);
int keyLen = SECKEY_PublicKeyStrength(key);
SECKEY_DestroyPublicKey(key);
if(keyLen > EXPORT_RSA_KEY_LENGTH) {
ssl3->hs.ws = wait_server_key;
}
}
#ifdef FORTEZZA
else if (ss->ssl3->hs.kea == kea_fortezza) {
ss->ssl3->hs.ws = wait_server_key;
}
#endif
}
/* rv must normally be equal to SECSuccess here. If we called
* handleBadCert, it can also be SECWouldBlock.
*/
return rv;
bad_cert:
desc = bad_certificate;
SSL_DBG(("%d: SSL3[%d]: peer certificate is no good: error=%d",
SSL_GETPID(), ss->fd, PORT_GetError()));
loser:
if ( certs != NULL ) {
for (; certs; certs = certs->next) {
CERT_DestroyCertificate(certs->cert);
}
}
if (arena != NULL) {
PORT_FreeArena(arena, PR_FALSE);
ssl3->peerCertArena = NULL;
ssl3->peerCertChain = NULL;
}
if (sec->peerCert != NULL) {
CERT_DestroyCertificate(sec->peerCert);
sec->peerCert = NULL;
}
(void)SSL3_SendAlert(ss, alert_fatal, desc);
return SECFailure;
}
void
SSL3_CleanupPeerCerts(SSL3State *ssl3)
{
PRArenaPool *arena = ssl3->peerCertArena;
SSL3CertNode *certs = (SSL3CertNode *)ssl3->peerCertChain;
for (; certs; certs = certs->next) {
CERT_DestroyCertificate(certs->cert);
}
if (arena) PORT_FreeArena(arena, PR_FALSE);
ssl3->peerCertArena = NULL;
ssl3->peerCertChain = NULL;
}
static SECStatus
ssl3_SendFinished(SSLSocket *ss)
{
SSL3Hashes hashes;
SECStatus rv;
SSL3Sender sender = ss->sec->isServer ? sender_server : sender_client;
SSL_TRC(3, ("%d: SSL3[%d]: send finished", SSL_GETPID(), ss->fd));
SSL3_ComputeHandshakeHashes(ss->ssl3, ss->ssl3->current_write,
&hashes, sender);
rv = ssl3_AppendHandshakeHeader(ss, finished, sizeof(SSL3Hashes));
if (rv < 0) return rv;
rv = ssl3_AppendHandshake(ss, &hashes, sizeof(SSL3Hashes));
rv = ssl3_FlushHandshake(ss);
if (rv < 0) return rv;
return SECSuccess;
}
SECStatus
SSL3_HandleFinished(SSLSocket *ss, SSL3Opaque *b, long length,
const SSL3Hashes *hashes)
{
SSLSecurityInfo *sec = ss->sec;
SSL3State *ssl3 = ss->ssl3;
SSLSessionID *sid = sec->ci->sid;
SSL_TRC(3, ("%d: SSL3[%d]: handle finished", SSL_GETPID(), ss->fd));
if (ssl3->hs.ws != wait_finished)
return ssl3_HandshakeFailure(ss);
if (length != sizeof(SSL3Hashes) || (PORT_Memcmp(hashes, b, length) != 0))
return ssl3_HandshakeFailure(ss);
if ((sec->isServer && !ssl3->hs.isResuming) ||
(!sec->isServer && ssl3->hs.isResuming)) {
SECStatus rv;
rv = ssl3_SendChangeCipherSpecs(ss);
if (rv < 0) return SECFailure;
rv = ssl3_SendFinished(ss);
if (rv < 0) return SECFailure;
if (sec->isServer || !ssl3->hs.rehandshake) {
ss->handshake = NULL;
ss->connected = 1;
ss->gather->writeOffset = 0;
ss->gather->readOffset = 0;
}
}
PORT_Memcpy(sid->u.ssl3.masterSecret, ssl3->current_read->master_secret,
sizeof(sid->u.ssl3.masterSecret));
sid->u.ssl3.cipherSuite = ssl3->hs.cipher_suite;
sid->u.ssl3.compression = ssl3->hs.compression;
sid->u.ssl3.policy = ssl3->policy;
if (!sec->isServer && ssl3->hs.rehandshake) {
ssl_FreeSID(sid);
ss->sec->ci->sid = NULL;
ssl3->hs.rehandshake = PR_FALSE;
return SSL3_SendClientHello(ss);
}
if (!ss->noCache)
(*sec->cache)(sid);
ss->ssl3->hs.ws = idle_handshake;
if (sec->handshakeCallback != NULL) {
(sec->handshakeCallback)(ss->fd, ss->sec->handshakeCallbackData);
}
return SECSuccess;
}
static SECStatus
ssl3_HandleHandshakeMessage(SSLSocket *ss, SSL3Opaque *b, long length)
{
SECStatus rv = SECSuccess;
SSL3HandshakeType type = ss->ssl3->hs.msg_type;
SSL3Hashes hashes;
char hdr[4];
/*
* We have to compute the hashes before we update them with the
* current message.
*/
if((type == finished) || (type == certificate_verify)) {
SSL3Sender sender = 0;
SSL3CipherSpec *spec = ss->ssl3->pending_read;
if (type == finished) {
sender = ss->sec->isServer ? sender_client : sender_server;
spec = ss->ssl3->current_read;
}
SSL3_ComputeHandshakeHashes(ss->ssl3, spec, &hashes, sender);
}
SSL_TRC(30,("%d: handle handshake message: %d", SSL_GETPID(),
ss->ssl3->hs.msg_type));
PRINT_BUF(60, (ss, "md5 handshake hash", (unsigned char*)ss->ssl3->hs.md5, 16));
hdr[0] = ss->ssl3->hs.msg_type;
hdr[1] = (length >> 16) & 0xff;
hdr[2] = (length >> 8) & 0xff;
hdr[3] = (length) & 0xff;
/* Start new handshake hashes when we start a new handshake */
if (ss->ssl3->hs.msg_type == client_hello) {
/*
* OK, we really should use PKCS #11 for the whole thing, but
* we do screwy things here, like statically allocate the SHA1 and
* MD5 contexts, so we just make sure it's safe before we call
* the sha/md5 stuff....
*/
if (!PK11_HashOK(SEC_OID_MD5)) {
return SECFailure;
}
if (!PK11_HashOK(SEC_OID_SHA1)) {
return SECFailure;
}
MD5_Begin(ss->ssl3->hs.md5);
SHA1_Begin(ss->ssl3->hs.sha);
}
/* We should not include hello_request messages in the handshake hashes */
if (ss->ssl3->hs.msg_type != hello_request) {
SSL3_UpdateHandshakeHashes(ss->ssl3, (unsigned char*) hdr, 4);
SSL3_UpdateHandshakeHashes(ss->ssl3, b, length);
}
switch (ss->ssl3->hs.msg_type) {
case hello_request:
if (length != 0 || ss->sec->isServer)
return ssl3_HandshakeFailure(ss);
rv = SSL3_HandleHelloRequest(ss);
break;
case client_hello:
if (!ss->sec->isServer)
return ssl3_HandshakeFailure(ss);
rv = SSL3_HandleClientHello(ss, b, length);
break;
case server_hello:
if (ss->sec->isServer)
return ssl3_HandshakeFailure(ss);
rv = SSL3_HandleServerHello(ss, b, length);
break;
case certificate:
rv = SSL3_HandleCertificate(ss, b, length);
break;
case server_key_exchange:
if (ss->sec->isServer)
return ssl3_HandshakeFailure(ss);
rv = SSL3_HandleServerKeyExchange(ss, b, length);
break;
case certificate_request:
if (ss->sec->isServer)
return ssl3_HandshakeFailure(ss);
rv = SSL3_HandleCertificateRequest(ss, b, length);
break;
case server_hello_done:
if (length != 0 || ss->sec->isServer)
return ssl3_HandshakeFailure(ss);
rv = SSL3_HandleServerHelloDone(ss);
break;
case certificate_verify:
if (!ss->sec->isServer)
return ssl3_HandshakeFailure(ss);
rv = ssl3_HandleCertificateVerify(ss, b, length, &hashes);
break;
case client_key_exchange:
if (!ss->sec->isServer)
return ssl3_HandshakeFailure(ss);
rv = SSL3_HandleClientKeyExchange(ss, b, length);
break;
case finished:
rv = SSL3_HandleFinished(ss, b, length, &hashes);
break;
default:
return ssl3_HandshakeFailure(ss);
}
return rv;
}
SECStatus
SSL3_HandleHandshake(SSLSocket *ss, SSLBuffer *origBuf)
{
/*
* There may be a partial handshake message already in the handshake
* state. The incoming buffer may contain another portion, or a
* complete message or several messages followed by another portion.
*
* Each message is made contiguous before being passed to the actual
* message parser.
*/
SSL3State *ssl3 = ss->ssl3;
SSLBuffer *buf = &ssl3->hs.msgState; /* do not lose the original buffer pointer */
SECStatus rv;
if (buf->buf == NULL) {
*buf = *origBuf;
}
while (buf->len > 0) {
while (ssl3->hs.header_bytes < 4) {
uint8 t;
t = *(buf->buf++);
buf->len--;
if (ssl3->hs.header_bytes++ == 0)
ssl3->hs.msg_type = t;
else
ssl3->hs.msg_len = (ssl3->hs.msg_len << 8) + t;
if (ssl3->hs.header_bytes == 4) {
/*
* XXX For now we do not support anybody who sends a
* handshake message that is more than 32K bytes. It
* should only happen with grotesquely sized certificate
* chains which we should not see in the near future. The
* guard is against 16 bit windows implementations that
* would now have to deal with potentially large blocks.
*/
if (ssl3->hs.msg_len > 0x7fff)
return ssl3_HandshakeFailure(ss);
}
if (buf->len == 0 && ssl3->hs.msg_len > 0) {
buf->buf = NULL;
return SECSuccess;
}
}
/*
* Header has been gathered and there is at least one byte of new
* data available for this message. If it can be done right out
* of the original buffer, then use it from there.
*/
if (ssl3->hs.msg_body.len == 0 && buf->len >= ssl3->hs.msg_len) {
/* handle it from input buffer */
rv = ssl3_HandleHandshakeMessage(ss, buf->buf, ssl3->hs.msg_len);
if (rv == SECFailure) return rv;
buf->buf += ssl3->hs.msg_len;
buf->len -= ssl3->hs.msg_len;
ssl3->hs.msg_len = 0;
ssl3->hs.header_bytes = 0;
if (rv < 0) return rv;
} else {
/* must be copied to msg_body and dealt with from there */
long bytes;
if (buf->len < ssl3->hs.msg_len)
bytes = buf->len;
else
bytes = buf->len;
/* Grow the buffer if needed */
if (bytes > ssl3->hs.msg_body.space - ssl3->hs.msg_body.len) {
rv = ssl_GrowBuf(
&ssl3->hs.msg_body, ssl3->hs.msg_body.len + bytes);
if (rv < 0) return SECFailure;
}
PORT_Memcpy(ssl3->hs.msg_body.buf + ssl3->hs.msg_body.len,
buf->buf, buf->len);
buf->buf += bytes;
buf->len -= bytes;
/* should not be more that a message in msg_body */
PORT_Assert(ssl3->hs.msg_body.len <= ssl3->hs.msg_len);
/* if we have a whole message, do it */
if (ssl3->hs.msg_body.len == ssl3->hs.msg_len) {
rv = ssl3_HandleHandshakeMessage(
ss, ssl3->hs.msg_body.buf, ssl3->hs.msg_len);
if (rv < 0) return rv;
ssl3->hs.msg_body.len = 0;
ssl3->hs.msg_len = 0;
ssl3->hs.header_bytes = 0;
} else {
PORT_Assert(buf->len == 0);
break;
}
}
}
origBuf->len = 0;
buf->buf = NULL;
return SECSuccess;
}
SECStatus
SSL3_HandleRecord(SSLSocket *ss, SSL3Ciphertext *cipher, SSLBuffer *databuf)
{
long rv;
SSL3State *ssl3 = ss->ssl3;
SSL3CipherSpec *spec;
SSL3Opaque hash[MAX_MAC_LENGTH];
unsigned int hashBytes;
unsigned int padding;
SSL3AlertDescription alert = unexpected_message;
const SSL3BulkCipherDef *cipher_def;
if(ssl3 == NULL) {
rv = ssl3_InitState(ss);
if (rv < 0) return rv;
}
ssl3 = ss->ssl3;
spec = ssl3->current_read;
cipher_def = spec->cipher_def;
if (cipher == NULL) {
SSL_DBG(("%d: SSL3[%d]: HandleRecord, resuming handshake",
SSL_GETPID(), ss->fd));
rv = SSL3_HandleHandshake(ss, databuf);
return rv;
}
databuf->len = 0;
if (databuf->space < MAX_FRAGMENT_LENGTH) {
rv = ssl_GrowBuf(databuf, MAX_FRAGMENT_LENGTH + 2048);
if (rv < 0) {
SSL_DBG(("%d: SSL3[%d]: HandleRecord, tried to get %d bytes",
SSL_GETPID(), ss->fd, MAX_FRAGMENT_LENGTH + 2048));
return SECFailure;
}
}
PRINT_BUF(80, (ss, "ciphertext:", cipher->buf->buf, cipher->buf->len));
rv = spec->decode(
spec->decodeContext, databuf->buf, (unsigned *)&databuf->len,
databuf->space, cipher->buf->buf, cipher->buf->len);
PRINT_BUF(80, (ss, "cleartext:", databuf->buf, databuf->len));
if (rv < 0) return SECFailure;
if (cipher_def->type == type_block) {
padding = *(databuf->buf + databuf->len - 1);
if (padding >= cipher_def->block_size) goto bad_msg;
databuf->len -= padding + 1;
if (databuf->len <= 0) goto bad_msg;
}
databuf->len -= spec->mac_size;
SSL3_ComputeFragmentHash(
spec, (ss->sec->isServer) ?
spec->client.write_mac_secret : spec->server.write_mac_secret,
cipher->type, spec->read_seq_num, databuf->buf, databuf->len,
hash, &hashBytes);
if (hashBytes != spec->mac_size ||
PORT_Memcmp(databuf->buf + databuf->len, hash, spec->mac_size) != 0) {
SSL_DBG(("%d: SSL3[%d]: mac check failed",
SSL_GETPID(), ss->fd));
PORT_SetError(SSL_ERROR_BAD_MAC_READ);
SSL3_SendAlert(ss, alert_fatal, bad_record_mac);
return SECFailure;
}
ssl3_BumpSequenceNumber(&spec->read_seq_num);
/*
* the null decompression routine is right here
*/
switch (cipher->type) {
case content_change_cipher_spec:
rv = SSL3_HandleChangeCipherSpecs(ss, databuf);
break;
case content_alert:
rv = SSL3_HandleAlert(ss, databuf);
break;
case content_handshake:
rv = SSL3_HandleHandshake(ss, databuf);
break;
case content_application_data:
break;
default:
SSL_DBG(("%d: SSL3[%d]: bogus content type=%d",
SSL_GETPID(), ss->fd, cipher->type));
return SECFailure;
}
return rv;
bad_msg:
PORT_SetError(XP_ERRNO_EIO);
SSL3_SendAlert(ss, alert_fatal, alert);
return SECFailure;
}
/*
* Initialization functions
*/
static void
ssl3_InitCipherSpec(SSL3CipherSpec *s)
{
s->cipher_def = &bulk_cipher_defs[cipher_null];
PORT_Assert(s->cipher_def->cipher == cipher_null);
s->mac_def = &mac_defs[mac_null];
PORT_Assert(s->mac_def->alg == mac_null);
s->hashContext = NULL;
s->hash = &SECHashObjects[HASH_AlgNULL];
s->encode = Null_Cipher;
s->decode = Null_Cipher;
s->destroy = NULL;
s->mac_size = 0;
s->write_seq_num.high = s->write_seq_num.low = 0;
s->read_seq_num.high = s->read_seq_num.low = 0;
}
static SECStatus
ssl3_InitState(SSLSocket *ss)
{
SSL3State *ssl3 = NULL;
MD5Context *md5 = NULL;
SHA1Context *sha = NULL;
/* reinitialization for renegotiated sessions XXX */
if (ss->ssl3 != NULL) return 0;
ssl3 = (SSL3State*) PORT_ZAlloc(sizeof(SSL3State)); /* zero on purpose */
if (ssl3 == NULL) return SECFailure;
/* note that entire HandshakeState is zero, including the buffer */
ssl3->policy = SSL_ALLOWED;
ssl3->current_read = ssl3->current_write = &ssl3->specs[0];
ssl3->pending_read = ssl3->pending_write = &ssl3->specs[1];
ssl3->hs.rehandshake = PR_FALSE;
ssl3_InitCipherSpec(ssl3->current_read);
ssl3_InitCipherSpec(ssl3->pending_read);
#ifdef FORTEZZA
ssl3->kea_context = NULL; /* paranoia */
#endif
ss->ssl3 = ssl3;
if (ss->sec->isServer)
ssl3->hs.ws = wait_client_hello;
else
ssl3->hs.ws = wait_server_hello;
/*
* OK, we really should use PKCS #11 for the whole thing, but
* we do screwy things here, like statically allocate the SHA1 and
* MD5 contexts, so we just make sure it's safe before we call
* the sha/md5 stuff....
*/
if (!PK11_HashOK(SEC_OID_MD5)) {
goto loser;
}
if (!PK11_HashOK(SEC_OID_SHA1)) {
goto loser;
}
md5 = ssl3->hs.md5 = MD5_NewContext();
if (md5 == NULL) goto loser;
MD5_Begin(ssl3->hs.md5);
sha = ssl3->hs.sha = SHA1_NewContext();
if (sha == NULL) goto loser;
SHA1_Begin(ssl3->hs.sha);
return SECSuccess;
loser:
if (md5 != NULL) MD5_DestroyContext(md5, PR_TRUE);
if (sha != NULL) SHA1_DestroyContext(sha, PR_TRUE);
if (ssl3 != NULL) PORT_Free(ssl3);
return SECFailure;
}
void
SSL3_Init(void)
{
}
void
SSL3_CreateExportRSAKeys(SECKEYPrivateKey *server_key)
{
usKey = server_key;
/* Sigh, should have a get key strength call */
if (PK11_GetPrivateModulusLen(server_key) > EXPORT_RSA_KEY_LENGTH) {
/* need to ask for the key size in bits */
exportKey = SECKEY_CreateRSAPrivateKey(EXPORT_RSA_KEY_LENGTH * 8,
&exportPubKey, NULL);
} else {
exportKey = NULL;
}
}
#ifdef FORTEZZA
void
SSL3_SetFortezzaKeys(SECKEYPrivateKey *server_key)
{
PORT_Assert(server_key->keyType == fortezzaKeys);
fortezzaServerKey = server_key;
}
#endif
/* record the export policy for this cipher suite */
SECStatus
SSL3_SetPolicy(SSL3CipherSuite which, int policy)
{
SSL3CipherSuiteCfg *suite;
if (policy == SSL_RESTRICTED) {
policy_some_restricted = PR_TRUE;
}
suite = ssl_LookupCipherSuiteCfg(which);
if (suite == NULL)
return SECFailure;
suite->policy = policy;
return SECSuccess;
}
/* record the user preference for this suite */
SECStatus
SSL3_EnableCipher(SSL3CipherSuite which, int enabled)
{
SSL3CipherSuiteCfg *suite;
suite = ssl_LookupCipherSuiteCfg(which);
if (suite == NULL)
return SECFailure;
suite->enabled = enabled;
return SECSuccess;
}
SECStatus
SSL3_ConstructV2CipherSpecsHack(SSLSocket *ss, unsigned char *cs, int *size)
{
int i, count = 0;
if (cs == NULL) {
*size = count_cipher_suites(SSL_ALLOWED, PR_TRUE);
return SECSuccess;
}
#ifdef FORTEZZA
if (ss) {
SECStatus rv;
rv = ssl3_InitState(ss);
if (rv < 0) return rv;
if (SSL_IsEnabledGroup(SSL_GroupFortezza)) {
rv=FortezzaOpen(ss,(FortezzaKEAContext **)&ss->ssl3->kea_context);
switch (rv) {
case SECSuccess:
break;
case SECFailure:
ssl_TmpEnableGroup(&localCipherSuites,&localCipherSuiteCount,
SSL_GroupFortezza,PR_FALSE);
break;
default:
ss->reStartType = 2;
return(rv); /* SECWouldBlock.. waiting for user input */
}
}
} else {
/* if we're this far into the SSL2 exchange, we can't do FORTEZZA */
ssl_TmpEnableGroup(&localCipherSuites,&localCipherSuiteCount,
SSL_GroupFortezza,PR_FALSE);
}
#endif
for(i = 0; i < cipherSuiteCount; i++) {
if (config_match(&cipherSuites[i], SSL_ALLOWED, PR_TRUE)) {
if (cs != NULL) {
*cs++ = 0x00;
*cs++ = (cipherSuites[i].cipher_suite >> 8) & 0xFF;
*cs++ = cipherSuites[i].cipher_suite & 0xFF;
}
count++;
}
}
*size = count;
return SECSuccess;
}
SECStatus
SSL3_RedoHandshake(SSLSocket *ss)
{
SSLSecurityInfo *sec = ss->sec;
SSLSessionID *sid = ss->sec->ci->sid;
if (!ss->connected)
return SECFailure;
if (sid) {
sec->uncache(sid);
ssl_FreeSID(sid);
ss->sec->ci->sid = NULL;
}
if (sec->isServer) {
return SSL3_SendHelloRequest(ss);
} else {
return SSL3_SendClientHello(ss);
}
}
void
ssl3_DestroySSL3Info(SSL3State *ssl3)
{
if (ssl3 == NULL)
return;
if (ssl3->clientCertificate != NULL)
CERT_DestroyCertificate(ssl3->clientCertificate);
if (ssl3->clientPrivateKey != NULL)
SECKEY_DestroyPrivateKey(ssl3->clientPrivateKey);
if (ssl3->peerCertArena != NULL)
SSL3_CleanupPeerCerts(ssl3);
#ifdef FORTEZZA
if (ssl3->kea_context) {
FortezzaDestroyKEAContext(ssl3->kea_context,PR_TRUE);
}
#endif
/* clean up handshake */
if (ssl3->hs.md5) {
MD5_DestroyContext(ssl3->hs.md5,PR_TRUE);
}
if (ssl3->hs.sha) {
SHA1_DestroyContext(ssl3->hs.sha,PR_TRUE);
}
/* free the SSL3Buffer (msg_body) */
PORT_FreeBlock(ssl3->hs.msg_body.buf);
/* free up the CipherSpecs */
ssl3_DestroyCipherSpec(&ssl3->specs[0]);
ssl3_DestroyCipherSpec(&ssl3->specs[1]);
PORT_Free(ssl3);
}
#ifdef FORTEZZA
int
SSL_IsEnabledGroup(int which) {
int i;
long mask;
SSL3KeyExchangeAlgorithm kea;
const SSL3CipherSuiteDef *suite_def;
PORT_Assert(cipherSuiteCount <= 32);
for(i = 0; i < cipherSuiteCount; i++) {
mask = 1L << i;
suite_def = ssl_LookupCipherSuiteDef(i);
if (suite_def == NULL) continue;
kea = suite_def->key_exchange_algorithm;
if (((kea == kea_rsa) && (which & SSL_GroupRSA)) ||
((kea == kea_dh) && (which & SSL_GroupDiffieHellman)) ||
((kea == kea_fortezza) && (which & SSL_GroupFortezza))) {
if ((enabledCipherSuites & mask) != 0) {
return PR_TRUE;
}
}
}
return PR_FALSE;
}
int
SSL_EnableGroup(int which,int on) {
int i;
long mask;
SSL3KeyExchangeAlgorithm kea;
const SSL3CipherSuiteDef *suite_def;
PORT_Assert(cipherSuiteCount <= 32);
for(i = 0; i < cipherSuiteCount; i++) {
mask = 1L << i;
suite_def = ssl_LookupCipherSuiteDef(i);
if (suite_def == NULL) continue;
kea = suite_def->key_exchange_algorithm;
if (((kea == kea_rsa) && (which & SSL_GroupRSA)) ||
((kea == kea_dh) && (which & SSL_GroupDiffieHellman)) ||
((kea == kea_fortezza) && (which & SSL_GroupFortezza))) {
if (on) {
if ((enabledCipherSuites & mask) == 0) {
enabledCipherSuiteCount++;
enabledCipherSuites |= mask;
}
} else {
if ((enabledCipherSuites & mask) != 0) {
enabledCipherSuiteCount--;
enabledCipherSuites &= ~mask;
}
}
return SECSuccess;
}
}
return SECFailure;
}
#endif