Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(227)

Unified Diff: net/third_party/nss/ssl/ssl3con.c

Issue 9764001: Add DTLS support to NSS, contributed by Eric Rescorla. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/third_party/nss/ssl/ssl3con.c
===================================================================
--- net/third_party/nss/ssl/ssl3con.c (revision 127709)
+++ net/third_party/nss/ssl/ssl3con.c (working copy)
@@ -1,4 +1,5 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* TODO(ekr@rtfm.com): Implement HelloVerifyRequest on server side. OK for now. */
/*
* SSL3 Protocol
*
@@ -92,7 +93,9 @@
static SECStatus ssl3_UpdateHandshakeHashes( sslSocket *ss,
const unsigned char *b,
unsigned int l);
+static SECStatus ssl3_FlushHandshakeMessages(sslSocket *ss, PRInt32 flags);
+
static SECStatus Null_Cipher(void *ctx, unsigned char *output, int *outputLen,
int maxOutputLen, const unsigned char *input,
int inputLen);
@@ -221,25 +224,13 @@
#endif /* NSS_ENABLE_ECC */
};
-#ifdef NSS_ENABLE_ZLIB
-/*
- * The DEFLATE algorithm can result in an expansion of 0.1% + 12 bytes. For a
- * maximum TLS record payload of 2**14 bytes, that's 29 bytes.
- */
-#define SSL3_COMPRESSION_MAX_EXPANSION 29
-#else /* !NSS_ENABLE_ZLIB */
-#define SSL3_COMPRESSION_MAX_EXPANSION 0
-#endif
-/*
- * make sure there is room in the write buffer for padding and
- * other compression and cryptographic expansions.
- */
-#define SSL3_BUFFER_FUDGE 100 + SSL3_COMPRESSION_MAX_EXPANSION
-
#define EXPORT_RSA_KEY_LENGTH 64 /* bytes */
+#define RECORD_HEADER_LENGTH(ss_) ((!ss_->ssl3.isDtls) ? \
ekr 2012/03/26 21:49:18 Looks to me like this is no longer used, which is
wtc 2012/03/27 00:28:25 Yes, I noticed (and verified) RECORD_HEADER_LENGTH
+ SSL3_RECORD_HEADER_LENGTH : DTLS_RECORD_HEADER_LENGTH)
+
/* This global item is used only in servers. It is is initialized by
** SSL_ConfigSecureServer(), and is used in ssl3_SendCertificateRequest().
*/
@@ -517,6 +508,7 @@
case hello_request: rv = "hello_request (0)"; break;
case client_hello: rv = "client_hello (1)"; break;
case server_hello: rv = "server_hello (2)"; break;
+ case hello_verify_request: rv = "hello_verify_request (3)"; break;
case certificate: rv = "certificate (11)"; break;
case server_key_exchange: rv = "server_key_exchange (12)"; break;
case certificate_request: rv = "certificate_request (13)"; break;
@@ -582,7 +574,7 @@
/* return pointer to ssl3CipherSuiteDef for suite, or NULL */
/* XXX This does a linear search. A binary search would be better. */
-static const ssl3CipherSuiteDef *
+const ssl3CipherSuiteDef *
ssl_LookupCipherSuiteDef(ssl3CipherSuite suite)
{
int cipher_suite_def_len =
@@ -648,6 +640,7 @@
suite = &ss->cipherSuites[i];
if (suite->enabled) {
++numEnabled;
+
/* We need the cipher defs to see if we have a token that can handle
* this cipher. It isn't part of the static definition.
*/
@@ -656,7 +649,8 @@
suite->isPresent = PR_FALSE;
continue;
}
- cipher_alg=bulk_cipher_defs[cipher_def->bulk_cipher_alg ].calg;
+
+ cipher_alg = bulk_cipher_defs[cipher_def->bulk_cipher_alg ].calg;
PORT_Assert( alg2Mech[cipher_alg].calg == cipher_alg);
cipher_mech = alg2Mech[cipher_alg].cmech;
exchKeyType =
@@ -730,6 +724,7 @@
PORT_Assert(policy != SSL_NOT_ALLOWED && enabled != PR_FALSE);
if (policy == SSL_NOT_ALLOWED || !enabled)
return PR_FALSE;
+
return (PRBool)(suite->enabled &&
suite->isPresent &&
suite->policy != SSL_NOT_ALLOWED &&
@@ -742,13 +737,13 @@
count_cipher_suites(sslSocket *ss, int policy, PRBool enabled)
{
int i, count = 0;
-
if (SSL3_ALL_VERSIONS_DISABLED(&ss->vrange)) {
return 0;
}
for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) {
- if (config_match(&ss->cipherSuites[i], policy, enabled))
+ if (config_match(&ss->cipherSuites[i], policy, enabled)) {
count++;
+ }
}
if (count <= 0) {
PORT_SetError(SSL_ERROR_SSL_DISABLED);
@@ -802,6 +797,7 @@
return SECSuccess;
}
+
static SECStatus
ssl3_GetNewRandom(SSL3Random *random)
{
@@ -1148,7 +1144,7 @@
** ssl3_DestroySSL3Info
** Caller must hold SpecWriteLock.
*/
-static void
+void
ssl3_DestroyCipherSpec(ssl3CipherSpec *spec, PRBool freeSrvName)
{
PRBool freeit = (PRBool)(!spec->bypassCiphers);
@@ -1209,6 +1205,7 @@
pwSpec = ss->ssl3.pwSpec;
PORT_Assert(pwSpec == ss->ssl3.prSpec);
+
/* This hack provides maximal interoperability with SSL 3 servers. */
cwSpec = ss->ssl3.cwSpec;
if (cwSpec->mac_def->mac == mac_null) {
@@ -1217,7 +1214,7 @@
}
pwSpec->version = ss->version;
- isTLS = (PRBool)(pwSpec->version > SSL_LIBRARY_VERSION_3_0);
+ isTLS = (PRBool)((pwSpec->version > SSL_LIBRARY_VERSION_3_0));
SSL_TRC(3, ("%d: SSL3[%d]: Set XXX Pending Cipher Suite to 0x%04x",
SSL_GETPID(), ss->fd, suite));
@@ -1228,6 +1225,12 @@
return SECFailure; /* error code set by ssl_LookupCipherSuiteDef */
}
+ if (IS_DTLS(ss)) {
+ /* Double-check that we did not pick an RC4 suite */
+ PORT_Assert( (suite_def->bulk_cipher_alg != cipher_rc4) &&
+ (suite_def->bulk_cipher_alg != cipher_rc4_40) &&
+ (suite_def->bulk_cipher_alg != cipher_rc4_56));
+ }
cipher = suite_def->bulk_cipher_alg;
kea = suite_def->key_exchange_alg;
@@ -1257,6 +1260,10 @@
pwSpec->compression_method = ss->ssl3.hs.compression;
pwSpec->compressContext = NULL;
pwSpec->decompressContext = NULL;
+
+ /* Note: pwSpec == prSpec here so we're really doing the read side.
+ The epoch is set up in InitPendingCipherSpec */
+ dtls_InitRecvdRecords(&pwSpec->recvdRecords);
ssl_ReleaseSpecWriteLock(ss); /*******************************/
return SECSuccess;
@@ -1754,6 +1761,7 @@
ssl3_InitPendingCipherSpec(sslSocket *ss, PK11SymKey *pms)
{
ssl3CipherSpec * pwSpec;
+ ssl3CipherSpec * cwSpec;
SECStatus rv;
PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
@@ -1762,6 +1770,7 @@
PORT_Assert(ss->ssl3.prSpec == ss->ssl3.pwSpec);
+ cwSpec = ss->ssl3.cwSpec;
pwSpec = ss->ssl3.pwSpec;
if (pms || (!pwSpec->msItem.len && !pwSpec->master_secret)) {
@@ -1774,9 +1783,10 @@
/* Double Bypass succeeded in extracting the master_secret */
const ssl3KEADef * kea_def = ss->ssl3.hs.kea_def;
PRBool isTLS = (PRBool)(kea_def->tls_keygen ||
- (pwSpec->version > SSL_LIBRARY_VERSION_3_0));
+ (pwSpec->version >
+ SSL_LIBRARY_VERSION_3_0));
pwSpec->bypassCiphers = PR_TRUE;
- rv = ssl3_KeyAndMacDeriveBypass( pwSpec,
+ rv = ssl3_KeyAndMacDeriveBypass(pwSpec,
(const unsigned char *)&ss->ssl3.hs.client_random,
(const unsigned char *)&ss->ssl3.hs.server_random,
isTLS,
@@ -1795,6 +1805,32 @@
rv = SECFailure;
}
+ /* Generic behaviors -- common to all crypto methods*/
+ if (!IS_DTLS(ss)) {
+ pwSpec->read_seq_num.high = pwSpec->write_seq_num.high = 0;
+ } else {
+ if (cwSpec->epoch == 65535) {
+ /* The problem here is that we have rehandshaked too many
+ times (you are not allowed to wrap the epoch). The
+ spec says you should be discarding the connection
+ and start over, so not much we can do here. */
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ rv = SECFailure;
+ }
+ /* We only need to modify pwSpec since there is only one
+ "pending" spec object being pointed to by pwSpec and
+ cwSpec at different times.
+
+ The sequence number has the high 16 bits as the epoch.
+ */
+ pwSpec->epoch = cwSpec->epoch + 1;
+
+ pwSpec->read_seq_num.high = pwSpec->write_seq_num.high =
+ pwSpec->epoch << 16;
+ }
+ pwSpec->read_seq_num.low = pwSpec->write_seq_num.low = 0;
+
+
done:
ssl_ReleaseSpecWriteLock(ss); /******************************/
if (rv != SECSuccess)
@@ -1834,6 +1870,7 @@
ssl3_ComputeRecordMAC(
ssl3CipherSpec * spec,
PRBool useServerMacKey,
+ PRBool isDtls,
SSL3ContentType type,
SSL3ProtocolVersion version,
SSL3SequenceNumber seq_num,
@@ -1871,8 +1908,17 @@
isTLS = PR_FALSE;
} else {
/* New TLS hash includes version. */
- temp[9] = MSB(version);
- temp[10] = LSB(version);
+ if (isDtls) {
+ SSL3ProtocolVersion dtls_version;
+
+ dtls_version = dtls_TLSVersionToDTLSVersion(version);
+
+ temp[9] = MSB(dtls_version);
+ temp[10] = LSB(dtls_version);
+ } else {
+ temp[9] = MSB(version);
+ temp[10] = LSB(version);
+ }
temp[11] = MSB(inputLength);
temp[12] = LSB(inputLength);
tempLen = 13;
@@ -2022,9 +2068,10 @@
}
/* Caller must hold the spec read lock. */
-static SECStatus
+SECStatus
ssl3_CompressMACEncryptRecord(ssl3CipherSpec * cwSpec,
PRBool isServer,
+ PRBool isDtls,
SSL3ContentType type,
const SSL3Opaque * pIn,
PRUint32 contentLen,
@@ -2035,7 +2082,11 @@
PRUint32 macLen = 0;
PRUint32 fragLen;
PRUint32 p1Len, p2Len, oddLen = 0;
+ PRUint16 headerLength = isDtls ? DTLS_RECORD_HEADER_LENGTH :
+ SSL3_RECORD_HEADER_LENGTH;
+
int ivLen = 0;
+ int ivBytes = 0;
int cipherBytes = 0;
cipher_def = cwSpec->cipher_def;
@@ -2048,20 +2099,20 @@
* record.
*/
ivLen = cipher_def->iv_size;
- if (ivLen > wrBuf->space - SSL3_RECORD_HEADER_LENGTH) {
+ if (ivLen > wrBuf->space - headerLength) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
- rv = PK11_GenerateRandom(wrBuf->buf + SSL3_RECORD_HEADER_LENGTH, ivLen);
+ rv = PK11_GenerateRandom(wrBuf->buf + headerLength, ivLen);
if (rv != SECSuccess) {
ssl_MapLowLevelError(SSL_ERROR_GENERATE_RANDOM_FAILURE);
return rv;
}
rv = cwSpec->encode( cwSpec->encodeContext,
- wrBuf->buf + SSL3_RECORD_HEADER_LENGTH,
+ wrBuf->buf + headerLength,
&cipherBytes, /* output and actual outLen */
ivLen, /* max outlen */
- wrBuf->buf + SSL3_RECORD_HEADER_LENGTH,
+ wrBuf->buf + headerLength,
ivLen); /* input and inputLen*/
if (rv != SECSuccess || cipherBytes != ivLen) {
PORT_SetError(SSL_ERROR_ENCRYPTION_FAILURE);
@@ -2073,20 +2124,20 @@
int outlen;
rv = cwSpec->compressor(
cwSpec->compressContext,
- wrBuf->buf + SSL3_RECORD_HEADER_LENGTH + ivLen, &outlen,
- wrBuf->space - SSL3_RECORD_HEADER_LENGTH - ivLen, pIn, contentLen);
+ wrBuf->buf + headerLength + ivLen, &outlen,
+ wrBuf->space - headerLength - ivLen, pIn, contentLen);
if (rv != SECSuccess)
return rv;
- pIn = wrBuf->buf + SSL3_RECORD_HEADER_LENGTH + ivLen;
+ pIn = wrBuf->buf + headerLength + ivLen;
contentLen = outlen;
}
/*
* Add the MAC
*/
- rv = ssl3_ComputeRecordMAC( cwSpec, isServer,
+ rv = ssl3_ComputeRecordMAC( cwSpec, isServer, isDtls,
type, cwSpec->version, cwSpec->write_seq_num, pIn, contentLen,
- wrBuf->buf + SSL3_RECORD_HEADER_LENGTH + ivLen + contentLen, &macLen);
+ wrBuf->buf + headerLength + ivLen + contentLen, &macLen);
if (rv != SECSuccess) {
ssl_MapLowLevelError(SSL_ERROR_MAC_COMPUTATION_FAILURE);
return SECFailure;
@@ -2113,7 +2164,7 @@
PORT_Assert((fragLen % cipher_def->block_size) == 0);
/* Pad according to TLS rules (also acceptable to SSL3). */
- pBuf = &wrBuf->buf[SSL3_RECORD_HEADER_LENGTH + ivLen + fragLen - 1];
+ pBuf = &wrBuf->buf[headerLength + ivLen + fragLen - 1];
for (i = padding_length + 1; i > 0; --i) {
*pBuf-- = padding_length;
}
@@ -2130,13 +2181,14 @@
p2Len += oddLen;
PORT_Assert( (cipher_def->block_size < 2) || \
(p2Len % cipher_def->block_size) == 0);
- memmove(wrBuf->buf + SSL3_RECORD_HEADER_LENGTH + ivLen + p1Len,
+ memmove(wrBuf->buf + headerLength + ivLen + p1Len,
pIn + p1Len, oddLen);
}
+
if (p1Len > 0) {
int cipherBytesPart1 = -1;
rv = cwSpec->encode( cwSpec->encodeContext,
- wrBuf->buf + SSL3_RECORD_HEADER_LENGTH + ivLen, /* output */
+ wrBuf->buf + headerLength + ivLen, /* output */
&cipherBytesPart1, /* actual outlen */
p1Len, /* max outlen */
pIn, p1Len); /* input, and inputlen */
@@ -2150,10 +2202,10 @@
if (p2Len > 0) {
int cipherBytesPart2 = -1;
rv = cwSpec->encode( cwSpec->encodeContext,
- wrBuf->buf + SSL3_RECORD_HEADER_LENGTH + ivLen + p1Len,
+ wrBuf->buf + headerLength + ivLen + p1Len,
&cipherBytesPart2, /* output and actual outLen */
p2Len, /* max outlen */
- wrBuf->buf + SSL3_RECORD_HEADER_LENGTH + ivLen + p1Len,
+ wrBuf->buf + headerLength + ivLen + p1Len,
p2Len); /* input and inputLen*/
PORT_Assert(rv == SECSuccess && cipherBytesPart2 == (int) p2Len);
if (rv != SECSuccess || cipherBytesPart2 != (int) p2Len) {
@@ -2162,17 +2214,40 @@
}
cipherBytes += cipherBytesPart2;
}
+
+ /* Add in the IV */
+ cipherBytes += ivBytes;
wtc 2012/03/21 01:22:07 This is a merging error. cipherBytes already incl
ekr 2012/03/21 01:36:40 Oops. This merge got ugly and I thought I caught a
ekr 2012/03/26 21:49:18 I double-checked this and I agree.
+
PORT_Assert(cipherBytes <= MAX_FRAGMENT_LENGTH + 1024);
+ wrBuf->len = cipherBytes + headerLength;
+ wrBuf->buf[0] = type;
+ if (isDtls) {
+ SSL3ProtocolVersion version;
+
+ version = dtls_TLSVersionToDTLSVersion(cwSpec->version);
+
+ wrBuf->buf[1] = MSB(version);
+ wrBuf->buf[2] = LSB(version);
+ wrBuf->buf[3] = (unsigned char)(cwSpec->write_seq_num.high >> 24);
+ wrBuf->buf[4] = (unsigned char)(cwSpec->write_seq_num.high >> 16);
+ wrBuf->buf[5] = (unsigned char)(cwSpec->write_seq_num.high >> 8);
+ wrBuf->buf[6] = (unsigned char)(cwSpec->write_seq_num.high >> 0);
+ wrBuf->buf[7] = (unsigned char)(cwSpec->write_seq_num.low >> 24);
+ wrBuf->buf[8] = (unsigned char)(cwSpec->write_seq_num.low >> 16);
+ wrBuf->buf[9] = (unsigned char)(cwSpec->write_seq_num.low >> 8);
+ wrBuf->buf[10] = (unsigned char)(cwSpec->write_seq_num.low >> 0);
+ wrBuf->buf[11] = MSB(cipherBytes);
+ wrBuf->buf[12] = LSB(cipherBytes);
+ } else {
+ wrBuf->buf[1] = MSB(cwSpec->version);
+ wrBuf->buf[2] = LSB(cwSpec->version);
+ wrBuf->buf[3] = MSB(cipherBytes);
+ wrBuf->buf[4] = LSB(cipherBytes);
+ }
+
ssl3_BumpSequenceNumber(&cwSpec->write_seq_num);
- wrBuf->len = cipherBytes + SSL3_RECORD_HEADER_LENGTH;
- wrBuf->buf[0] = type;
- wrBuf->buf[1] = MSB(cwSpec->version);
- wrBuf->buf[2] = LSB(cwSpec->version);
- wrBuf->buf[3] = MSB(cipherBytes);
- wrBuf->buf[4] = LSB(cipherBytes);
-
return SECSuccess;
}
@@ -2194,10 +2269,13 @@
* ssl_SEND_FLAG_FORCE_INTO_BUFFER
* As above, except this suppresses all write attempts, and forces
* all ciphertext into the pending ciphertext buffer.
+ * ssl_SEND_FLAG_USE_EPOCH (for DTLS)
+ * Forces the use of the provided epoch
*
*/
-static PRInt32
+PRInt32
ssl3_SendRecord( sslSocket * ss,
+ DTLSEpoch epoch, /* DTLS only */
SSL3ContentType type,
const SSL3Opaque * pIn, /* input buffer */
PRInt32 nIn, /* bytes of input */
@@ -2269,7 +2347,9 @@
sslBuffer secondRecord;
rv = ssl3_CompressMACEncryptRecord(ss->ssl3.cwSpec,
- ss->sec.isServer, type, pIn, 1,
+ ss->sec.isServer,
+ IS_DTLS(ss),
+ type, pIn, 1,
wrBuf);
if (rv != SECSuccess)
goto spec_locked_loser;
@@ -2282,7 +2362,9 @@
secondRecord.space = wrBuf->space - wrBuf->len;
rv = ssl3_CompressMACEncryptRecord(ss->ssl3.cwSpec,
- ss->sec.isServer, type, pIn + 1,
+ ss->sec.isServer,
+ IS_DTLS(ss),
+ type, pIn + 1,
contentLen - 1, &secondRecord);
if (rv == SECSuccess) {
PRINT_BUF(50, (ss, "send (encrypted) record data [2/2]:",
@@ -2290,9 +2372,19 @@
wrBuf->len += secondRecord.len;
}
} else {
- rv = ssl3_CompressMACEncryptRecord(ss->ssl3.cwSpec,
- ss->sec.isServer, type, pIn,
- contentLen, wrBuf);
+ if (!IS_DTLS(ss)) {
+ rv = ssl3_CompressMACEncryptRecord(ss->ssl3.cwSpec,
+ ss->sec.isServer,
+ IS_DTLS(ss),
+ type, pIn,
+ contentLen, wrBuf);
+ }
+ else {
+ rv = dtls_CompressMACEncryptRecord( ss, epoch,
+ !!(flags & ssl_SEND_FLAG_USE_EPOCH),
+ type, pIn, contentLen, wrBuf);
+ }
+
if (rv == SECSuccess) {
PRINT_BUF(50, (ss, "send (encrypted) record data:",
wrBuf->buf, wrBuf->len));
@@ -2348,15 +2440,23 @@
/* we got PR_WOULD_BLOCK_ERROR, which means none was sent. */
sent = 0;
}
+
wrBuf->len -= sent;
if (wrBuf->len) {
- /* now take all the remaining unsent new ciphertext and
- * append it to the buffer of previously unsent ciphertext.
- */
- rv = ssl_SaveWriteData(ss, wrBuf->buf + sent, wrBuf->len);
- if (rv != SECSuccess) {
- /* presumably a memory error, SEC_ERROR_NO_MEMORY */
+ if (IS_DTLS(ss)) {
+ /* DTLS just says no in this case. No buffering */
+ PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
return SECFailure;
+ } else {
+ /* now take all the remaining unsent new ciphertext and
+ * append it to the buffer of previously unsent ciphertext.
+ */
+
+ rv = ssl_SaveWriteData(ss, wrBuf->buf + sent, wrBuf->len);
+ if (rv != SECSuccess) {
+ /* presumably a memory error, SEC_ERROR_NO_MEMORY */
+ return SECFailure;
+ }
}
}
}
@@ -2378,6 +2478,9 @@
PRInt32 discarded = 0;
PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss) );
+ /* These flags for internal use only */
+ PORT_Assert( !(flags & (ssl_SEND_FLAG_USE_EPOCH |
+ ssl_SEND_FLAG_NO_RETRANSMIT)));
if (len < 0 || !in) {
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
return SECFailure;
@@ -2415,7 +2518,12 @@
ssl_GetXmitBufLock(ss);
}
toSend = PR_MIN(len - totalSent, MAX_FRAGMENT_LENGTH);
- sent = ssl3_SendRecord(ss, content_application_data,
+ /*
+ * Note that the 0 epoch is OK because flags will never
+ * require its use, as guaranteed by the PORT_Assert
+ * above
+ */
+ sent = ssl3_SendRecord(ss, 0, content_application_data,
in + totalSent, toSend, flags);
if (sent < 0) {
if (totalSent > 0 && PR_GetError() == PR_WOULD_BLOCK_ERROR) {
@@ -2450,10 +2558,16 @@
return totalSent + discarded;
}
-/* Attempt to send the content of sendBuf buffer in an SSL handshake record.
+
+/* Attempt to send buffered handshake messages.
* This function returns SECSuccess or SECFailure, never SECWouldBlock.
* Always set sendBuf.len to 0, even when returning SECFailure.
*
+ * Depending on whether we are doing DTLS or not, this either calls
+ *
+ * - ssl3_FlushHandshake if non-DTLS
+ * - dtls_FlushHandshake if DTLS
+ *
* Called from SSL3_SendAlert(), ssl3_SendChangeCipherSpecs(),
* ssl3_AppendHandshake(), ssl3_SendClientHello(),
* ssl3_SendHelloRequest(), ssl3_SendServerHelloDone(),
@@ -2462,6 +2576,22 @@
static SECStatus
ssl3_FlushHandshake(sslSocket *ss, PRInt32 flags)
{
+ if (IS_DTLS(ss)) {
+ return dtls_FlushHandshakeMessages(ss, flags);
+ } else {
+ return ssl3_FlushHandshakeMessages(ss, flags);
+ }
+}
+
+/* Attempt to send the content of sendBuf buffer in an SSL handshake record.
+ * This function returns SECSuccess or SECFailure, never SECWouldBlock.
+ * Always set sendBuf.len to 0, even when returning SECFailure.
+ *
+ * Called from ssl3_FlushHandshake
+ */
+static SECStatus
+ssl3_FlushHandshakeMessages(sslSocket *ss, PRInt32 flags)
+{
PRInt32 rv = SECSuccess;
PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
@@ -2469,14 +2599,14 @@
if (!ss->sec.ci.sendBuf.buf || !ss->sec.ci.sendBuf.len)
return rv;
-
+
/* only this flag is allowed */
PORT_Assert(!(flags & ~ssl_SEND_FLAG_FORCE_INTO_BUFFER));
if ((flags & ~ssl_SEND_FLAG_FORCE_INTO_BUFFER) != 0) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
} else {
- rv = ssl3_SendRecord(ss, content_handshake, ss->sec.ci.sendBuf.buf,
+ rv = ssl3_SendRecord(ss, 0, content_handshake, ss->sec.ci.sendBuf.buf,
ss->sec.ci.sendBuf.len, flags);
}
if (rv < 0) {
@@ -2593,7 +2723,7 @@
rv = ssl3_FlushHandshake(ss, ssl_SEND_FLAG_FORCE_INTO_BUFFER);
if (rv == SECSuccess) {
PRInt32 sent;
- sent = ssl3_SendRecord(ss, content_alert, bytes, 2,
+ sent = ssl3_SendRecord(ss, 0, content_alert, bytes, 2,
desc == no_certificate
? ssl_SEND_FLAG_FORCE_INTO_BUFFER : 0);
rv = (sent >= 0) ? SECSuccess : (SECStatus)sent;
@@ -2667,7 +2797,7 @@
/*
* Send handshake_Failure alert. Set generic error number.
*/
-static SECStatus
+SECStatus
ssl3_DecodeError(sslSocket *ss)
{
(void)SSL3_SendAlert(ss, alert_fatal,
@@ -2755,7 +2885,8 @@
default: error = SSL_ERROR_RX_UNKNOWN_ALERT; break;
}
if (level == alert_fatal) {
- ss->sec.uncache(ss->sec.ci.sid);
+ if (!ss->opt.noCache)
+ ss->sec.uncache(ss->sec.ci.sid);
wtc 2012/03/21 01:22:07 What is this change for? Did you run into a crash
ekr 2012/03/21 01:36:40 IIRC I was seeing crashes when noCache was set. I
wtc 2012/03/21 02:12:01 Thank you for the confirmation. We have fixed cra
wtc 2012/03/21 02:15:23 I found that this crash was reported in the NSS ne
if ((ss->ssl3.hs.ws == wait_server_hello) &&
(desc == handshake_failure)) {
/* XXX This is a hack. We're assuming that any handshake failure
@@ -2806,17 +2937,21 @@
if (rv != SECSuccess) {
return rv; /* error code set by ssl3_FlushHandshake */
}
- sent = ssl3_SendRecord(ss, content_change_cipher_spec, &change, 1,
- ssl_SEND_FLAG_FORCE_INTO_BUFFER);
- if (sent < 0) {
- return (SECStatus)sent; /* error code set by ssl3_SendRecord */
+ if (!IS_DTLS(ss)) {
+ sent = ssl3_SendRecord(ss, 0, content_change_cipher_spec, &change, 1,
+ ssl_SEND_FLAG_FORCE_INTO_BUFFER);
+ if (sent < 0) {
+ return (SECStatus)sent; /* error code set by ssl3_SendRecord */
+ }
+ } else {
+ rv = dtls_QueueMessage(ss, content_change_cipher_spec, &change, 1);
+ if (rv != SECSuccess)
+ return rv;
}
/* swap the pending and current write specs. */
ssl_GetSpecWriteLock(ss); /**************************************/
pwSpec = ss->ssl3.pwSpec;
- pwSpec->write_seq_num.high = 0;
- pwSpec->write_seq_num.low = 0;
ss->ssl3.pwSpec = ss->ssl3.cwSpec;
ss->ssl3.cwSpec = pwSpec;
@@ -2828,8 +2963,15 @@
/* If we are really through with the old cipher spec
* (Both the read and write sides have changed) destroy it.
*/
- if (ss->ssl3.prSpec == ss->ssl3.pwSpec) {
- ssl3_DestroyCipherSpec(ss->ssl3.pwSpec, PR_FALSE/*freeSrvName*/);
+ if ( ss->ssl3.prSpec == ss->ssl3.pwSpec ) {
+ if (!IS_DTLS(ss)) {
+ ssl3_DestroyCipherSpec(ss->ssl3.pwSpec, PR_FALSE/*freeSrvName*/);
+ } else {
+ /* With DTLS, we need to set a holddown timer in case the final
+ message got lost */
+ ss->ssl3.hs.rtTimeoutMs = DTLS_FINISHED_TIMER;
+ dtls_StartTimer(ss, dtls_FinishedTimerCb);
+ }
}
ssl_ReleaseSpecWriteLock(ss); /**************************************/
@@ -2878,7 +3020,6 @@
/* Swap the pending and current read specs. */
ssl_GetSpecWriteLock(ss); /*************************************/
prSpec = ss->ssl3.prSpec;
- prSpec->read_seq_num.high = prSpec->read_seq_num.low = 0;
ss->ssl3.prSpec = ss->ssl3.crSpec;
ss->ssl3.crSpec = prSpec;
@@ -2918,6 +3059,7 @@
unsigned char * sr = (unsigned char *)&ss->ssl3.hs.server_random;
PRBool isTLS = (PRBool)(kea_def->tls_keygen ||
(pwSpec->version > SSL_LIBRARY_VERSION_3_0));
+
/*
* Whenever isDH is true, we need to use CKM_TLS_MASTER_KEY_DERIVE_DH
* which, unlike CKM_TLS_MASTER_KEY_DERIVE, converts arbitrary size
@@ -2980,7 +3122,13 @@
&params, key_derive, CKA_DERIVE, 0, keyFlags);
if (!isDH && pwSpec->master_secret && ss->opt.detectRollBack) {
SSL3ProtocolVersion client_version;
+
client_version = pms_version.major << 8 | pms_version.minor;
+
+ if (IS_DTLS(ss)) {
+ client_version = dtls_DTLSVersionToTLSVersion(client_version);
+ }
+
if (client_version != ss->clientHelloVersion) {
/* Destroy it. Version roll-back detected. */
PK11_FreeSymKey(pwSpec->master_secret);
@@ -3405,6 +3553,15 @@
{
SECStatus rv;
+ /* If we already have a message in place, we need to enqueue it.
+ This empties the buffer
+ */
+ if (IS_DTLS(ss)) {
+ rv = dtls_StageHandshakeMessage(ss);
+ if (rv != SECSuccess)
+ return rv;
+ }
+
SSL_TRC(30,("%d: SSL3[%d]: append handshake header: type %s",
SSL_GETPID(), ss->fd, ssl3_DecodeHandshakeType(t)));
PRINT_BUF(60, (ss, "MD5 handshake hash:",
@@ -3417,6 +3574,32 @@
return rv; /* error code set by AppendHandshake, if applicable. */
}
rv = ssl3_AppendHandshakeNumber(ss, length, 3);
+ if (rv != SECSuccess) {
+ return rv; /* error code set by AppendHandshake, if applicable. */
+ }
+
+ if (IS_DTLS(ss)) {
+ /* Note that we make an unfragmented message here. We fragment in the
+ transmission code, if necessary */
+ rv = ssl3_AppendHandshakeNumber(ss, ss->ssl3.hs.sendMessageSeq, 2);
+ if (rv != SECSuccess) {
+ return rv; /* error code set by AppendHandshake, if applicable. */
+ }
+ ss->ssl3.hs.sendMessageSeq++;
+
+ /* 0 is the fragment offset, because it's not fragmented yet */
+ rv = ssl3_AppendHandshakeNumber(ss, 0, 3);
+ if (rv != SECSuccess) {
+ return rv; /* error code set by AppendHandshake, if applicable. */
+ }
+
+ /* Fragment length -- set to the packet length because not fragmented */
+ rv = ssl3_AppendHandshakeNumber(ss, length, 3);
+ if (rv != SECSuccess) {
+ return rv; /* error code set by AppendHandshake, if applicable. */
+ }
+ }
+
return rv; /* error code set by AppendHandshake, if applicable. */
}
@@ -3823,9 +4006,11 @@
/* Called from ssl3_HandleHelloRequest(),
* ssl3_RedoHandshake()
* ssl2_BeginClientHandshake (when resuming ssl3 session)
+ * dtls_HandleHelloVerifyRequest(with resending=PR_TRUE)
+ *
*/
SECStatus
-ssl3_SendClientHello(sslSocket *ss)
+ssl3_SendClientHello(sslSocket *ss, PRBool resending)
{
sslSessionID * sid;
ssl3CipherSpec * cwSpec;
@@ -3843,13 +4028,14 @@
PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss) );
-
+
rv = ssl3_InitState(ss);
if (rv != SECSuccess) {
return rv; /* ssl3_InitState has set the error code. */
}
ss->ssl3.hs.sendingSCSV = PR_FALSE; /* Must be reset every handshake */
-
+ PORT_Assert( IS_DTLS(ss) || !resending);
+
/* We might be starting a session renegotiation in which case we should
* clear previous state.
*/
@@ -3924,8 +4110,7 @@
if (sid) {
SSL_AtomicIncrementLong(& ssl3stats.sch_sid_cache_hits );
-
- /* Are we attempting a stateless session resume? */
+ /* Are we attempting a stateless session resume? */
if (sid->version > SSL_LIBRARY_VERSION_3_0 &&
sid->u.ssl3.sessionTicket.ticket.data)
SSL_AtomicIncrementLong(& ssl3stats.sch_sid_stateless_resumes );
@@ -4008,6 +4193,10 @@
}
#endif
+ if (IS_DTLS(ss)) {
+ ssl3_DisableNonDTLSSuites(ss);
+ }
+
/* how many suites are permitted by policy and user preference? */
num_suites = count_cipher_suites(ss, ss->ssl3.policy, PR_TRUE);
if (!num_suites)
@@ -4027,6 +4216,9 @@
1 + ((sid == NULL) ? 0 : sid->u.ssl3.sessionIDLength) +
2 + num_suites*sizeof(ssl3CipherSuite) +
1 + numCompressionMethods + total_exten_len;
+ if (IS_DTLS(ss)) {
+ length += 1 + ss->ssl3.hs.cookieLen;
+ }
rv = ssl3_AppendHandshakeHeader(ss, client_hello, length);
if (rv != SECSuccess) {
@@ -4034,13 +4226,25 @@
}
ss->clientHelloVersion = ss->version;
- rv = ssl3_AppendHandshakeNumber(ss, ss->clientHelloVersion, 2);
+ if (IS_DTLS(ss)) {
+ /* One's complement */
+ PRUint16 version;
+
+ version = dtls_TLSVersionToDTLSVersion(ss->clientHelloVersion);
+ rv = ssl3_AppendHandshakeNumber(ss,(unsigned short)
+ version, 2);
+ } else {
+ rv = ssl3_AppendHandshakeNumber(ss, ss->clientHelloVersion, 2);
+ }
if (rv != SECSuccess) {
return rv; /* err set by ssl3_AppendHandshake* */
}
- rv = ssl3_GetNewRandom(&ss->ssl3.hs.client_random);
- if (rv != SECSuccess) {
- return rv; /* err set by GetNewRandom. */
+
+ if (!resending) { /* Don't re-generate if we are in DTLS re-sending mode */
+ rv = ssl3_GetNewRandom(&ss->ssl3.hs.client_random);
+ if (rv != SECSuccess) {
+ return rv; /* err set by GetNewRandom. */
+ }
}
rv = ssl3_AppendHandshake(ss, &ss->ssl3.hs.client_random,
SSL3_RANDOM_LENGTH);
@@ -4057,6 +4261,14 @@
return rv; /* err set by ssl3_AppendHandshake* */
}
+ if (IS_DTLS(ss)) {
+ rv = ssl3_AppendHandshakeVariable(
+ ss, ss->ssl3.hs.cookie, ss->ssl3.hs.cookieLen, 1);
+ }
+ if (rv != SECSuccess) {
+ return rv; /* err set by ssl3_AppendHandshake* */
+ }
+
rv = ssl3_AppendHandshakeNumber(ss, num_suites*sizeof(ssl3CipherSuite), 2);
if (rv != SECSuccess) {
return rv; /* err set by ssl3_AppendHandshake* */
@@ -4180,8 +4392,12 @@
ss->sec.ci.sid = NULL;
}
+ if (IS_DTLS(ss)) {
+ dtls_RehandshakeCleanup(ss);
+ }
+
ssl_GetXmitBufLock(ss);
- rv = ssl3_SendClientHello(ss);
+ rv = ssl3_SendClientHello(ss, PR_FALSE);
ssl_ReleaseXmitBufLock(ss);
return rv;
@@ -5034,8 +5250,31 @@
if (temp < 0) {
goto loser; /* alert has been sent */
}
- version = (SSL3ProtocolVersion)temp;
+
+ if (!IS_DTLS(ss)) {
+ /* this is appropriate since the negotiation is complete, and we only
+ ** know SSL 3.x.
+ */
+ version = (SSL3ProtocolVersion)temp;
+ if (MSB(version) != MSB(SSL_LIBRARY_VERSION_3_0)) {
+ desc = (version > SSL_LIBRARY_VERSION_3_0) ? protocol_version
+ : handshake_failure;
+ goto alert_loser;
+ }
+ } else {
+ /* RFC 4347 required that you verify that the server versions
+ match (S 4.2.1) in the HelloVerifyRequest and the ServerHello
+ RFC 6347 suggests (SHOULD) that servers always use 1.0
+ in HelloVerifyRequest and allows the versions not to match,
+ esp. when 1.2 is being negotiated.
+
+ Therefore we do not check for matching here.
+ */
+ version = dtls_DTLSVersionToTLSVersion(temp);
+ if (version == 0) /* Insane version number */
+ goto alert_loser;
+ }
rv = ssl3_NegotiateVersion(ss, version, PR_FALSE);
if (rv != SECSuccess) {
desc = (version > SSL_LIBRARY_VERSION_3_0) ? protocol_version
@@ -5043,7 +5282,7 @@
errCode = SSL_ERROR_NO_CYPHER_OVERLAP;
goto alert_loser;
}
- isTLS = (ss->version > SSL_LIBRARY_VERSION_3_0);
+ isTLS = ss->version > SSL_LIBRARY_VERSION_3_0;
rv = ssl3_ConsumeHandshake(
ss, &ss->ssl3.hs.server_random, SSL3_RANDOM_LENGTH, &b, &length);
@@ -5265,7 +5504,6 @@
ssl3_CopyPeerCertsFromSID(ss, sid);
}
-
/* NULL value for PMS signifies re-use of the old MS */
rv = ssl3_InitPendingCipherSpec(ss, NULL);
if (rv != SECSuccess) {
@@ -5910,6 +6148,7 @@
SSL_TRC(3, ("%d: SSL3[%d]: handle server_hello_done handshake",
SSL_GETPID(), ss->fd));
+
PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
@@ -6264,6 +6503,7 @@
SSL3AlertLevel level = alert_fatal;
SSL3ProtocolVersion version;
SECItem sidBytes = {siBuffer, NULL, 0};
+ SECItem cookieBytes = {siBuffer, NULL, 0};
SECItem suites = {siBuffer, NULL, 0};
SECItem comps = {siBuffer, NULL, 0};
PRBool haveSpecWriteLock = PR_FALSE;
@@ -6281,6 +6521,20 @@
return rv; /* error code is set. */
}
+ /* Clearing the handshake pointers so that
+ * ssl_Do1stHandshake won't call ssl2_HandleMessage.
+ *
+ * The issue here is that TLS ordinarily starts out
+ * in ssl2_HandleV3HandshakeRecord() because of the
+ * backward-compatibility code paths. That function
+ * zeroes these next pointers. But with DTLS, we
+ * don't even try to do the v2 ClientHello so we
+ * skip that function and need to reset these
+ * values here.
+ */
+ ss->nextHandshake = NULL;
+ ss->securityHandshake = NULL;
+
/* We might be starting session renegotiation in which case we should
* clear previous state.
*/
@@ -6306,11 +6560,25 @@
goto alert_loser;
}
+ if (IS_DTLS(ss)) {
+ dtls_RehandshakeCleanup(ss);
+ }
+
tmp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
if (tmp < 0)
goto loser; /* malformed, alert already sent */
- ss->clientHelloVersion = version = (SSL3ProtocolVersion)tmp;
+
+ /* Translate the version */
+ if (IS_DTLS(ss)) {
+ ss->clientHelloVersion = version =
+ dtls_DTLSVersionToTLSVersion((SSL3ProtocolVersion)tmp);
+ }
+ else {
+ ss->clientHelloVersion = version = (SSL3ProtocolVersion)tmp;
+ }
+
rv = ssl3_NegotiateVersion(ss, version, PR_TRUE);
+
if (rv != SECSuccess) {
desc = (version > SSL_LIBRARY_VERSION_3_0) ? protocol_version
: handshake_failure;
@@ -6331,6 +6599,14 @@
goto loser; /* malformed */
}
+ /* grab the client's cookie, if present. */
+ if (IS_DTLS(ss)) {
+ rv = ssl3_ConsumeHandshakeVariable(ss, &cookieBytes, 1, &b, &length);
+ if (rv != SECSuccess) {
+ goto loser; /* malformed */
+ }
+ }
+
/* grab the list of cipher suites. */
rv = ssl3_ConsumeHandshakeVariable(ss, &suites, 2, &b, &length);
if (rv != SECSuccess) {
@@ -6479,6 +6755,10 @@
ssl3_FilterECCipherSuitesByServerCerts(ss);
#endif
+ if (IS_DTLS(ss)) {
+ ssl3_DisableNonDTLSSuites(ss);
+ }
+
#ifdef PARANOID
/* Look for a matching cipher suite. */
j = ssl3_config_match_init(ss);
@@ -6562,6 +6842,7 @@
ssl3CipherSuiteCfg *suite = &ss->cipherSuites[j];
if (!config_match(suite, ss->ssl3.policy, PR_TRUE))
continue;
+
for (i = 0; i + 1 < suites.len; i += 2) {
PRUint16 suite_i = (suites.data[i] << 8) | suites.data[i + 1];
if (suite_i == suite->cipher_suite) {
@@ -7166,17 +7447,29 @@
PRUint32 maxBytes = 65535;
PRUint32 length;
PRInt32 extensions_len = 0;
+ SSL3ProtocolVersion version;
SSL_TRC(3, ("%d: SSL3[%d]: send server_hello handshake", SSL_GETPID(),
ss->fd));
PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
- PORT_Assert( MSB(ss->version) == MSB(SSL_LIBRARY_VERSION_3_0));
- if (MSB(ss->version) != MSB(SSL_LIBRARY_VERSION_3_0)) {
- PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
- return SECFailure;
+
+ if (!IS_DTLS(ss)) {
+ PORT_Assert( MSB(ss->version) == MSB(SSL_LIBRARY_VERSION_3_0));
+
+ if (MSB(ss->version) != MSB(SSL_LIBRARY_VERSION_3_0)) {
+ PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
+ return SECFailure;
+ }
+ } else {
+ PORT_Assert(MSB(ss->version) == MSB(SSL_LIBRARY_VERSION_DTLS_1_0));
+
+ if (MSB(ss->version) != MSB(SSL_LIBRARY_VERSION_DTLS_1_0)) {
+ PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP);
+ return SECFailure;
+ }
}
sid = ss->sec.ci.sid;
@@ -7194,7 +7487,14 @@
return rv; /* err set by AppendHandshake. */
}
- rv = ssl3_AppendHandshakeNumber(ss, ss->version, 2);
+ if (IS_DTLS(ss)) {
+ version = dtls_TLSVersionToDTLSVersion(ss->version);
+ }
+ else {
+ version = ss->version;
+ }
+
+ rv = ssl3_AppendHandshakeNumber(ss, version, 2);
if (rv != SECSuccess) {
return rv; /* err set by AppendHandshake. */
}
@@ -7379,11 +7679,8 @@
nnames = ca_list->nnames;
}
- if (!nnames) {
- PORT_SetError(SSL_ERROR_NO_TRUSTED_SSL_CLIENT_CA);
- return SECFailure;
- }
-
+ /* There used to be a test here to require a CA, but there
+ are cases where you want to have no CAs offered. */
wtc 2012/03/21 01:22:07 Are you sure this is good practice?
ekr 2012/03/21 01:36:40 So, it's explicitly permitted by RFC 5246, and it'
for (i = 0, name = names; i < nnames; i++, name++) {
calen += 2 + name->len;
}
@@ -7551,9 +7848,18 @@
}
/* Generate the pre-master secret ... */
- version.major = MSB(ss->clientHelloVersion);
- version.minor = LSB(ss->clientHelloVersion);
+ if (IS_DTLS(ss)) {
+ SSL3ProtocolVersion temp;
+ temp = dtls_TLSVersionToDTLSVersion(ss->clientHelloVersion);
+
+ version.major = MSB(temp);
+ version.minor = LSB(temp);
+ } else {
+ version.major = MSB(ss->clientHelloVersion);
+ version.minor = LSB(ss->clientHelloVersion);
+ }
+
param.data = (unsigned char *)&version;
param.len = sizeof version;
@@ -7633,9 +7939,13 @@
/* triple bypass failed. Let's try for a double bypass. */
goto double_bypass;
} else if (ss->opt.detectRollBack) {
- SSL3ProtocolVersion client_version =
- (rsaPmsBuf[0] << 8) | rsaPmsBuf[1];
- if (client_version != ss->clientHelloVersion) {
+ SSL3ProtocolVersion client_version = (rsaPmsBuf[0] << 8) | rsaPmsBuf[1];
+
+ if (IS_DTLS(ss)) {
+ client_version = dtls_DTLSVersionToTLSVersion(client_version);
+ }
+
+ if (rv != SECSuccess || (client_version != ss->clientHelloVersion)) {
wtc 2012/03/21 01:22:07 I think this rv != SECSuccess || was added i
ekr 2012/03/21 01:36:40 You're right. This was an error introduced when I
ekr 2012/03/26 21:49:18 Done.
ekr 2012/03/26 21:49:18 Done.
/* Version roll-back detected. ensure failure. */
rv = PK11_GenerateRandom(rsaPmsBuf, sizeof rsaPmsBuf);
}
@@ -8851,6 +9161,10 @@
}
}
+ if (IS_DTLS(ss)) {
+ flags |= ssl_SEND_FLAG_NO_RETRANSMIT;
+ }
+
rv = ssl3_SendFinished(ss, flags);
if (rv != SECSuccess) {
goto xmit_loser; /* err is set. */
@@ -8980,13 +9294,14 @@
* hanshake message.
* Caller must hold Handshake and RecvBuf locks.
*/
-static SECStatus
+SECStatus
ssl3_HandleHandshakeMessage(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
{
SECStatus rv = SECSuccess;
SSL3HandshakeType type = ss->ssl3.hs.msg_type;
SSL3Hashes hashes; /* computed hashes are put here. */
PRUint8 hdr[4];
+ PRUint8 dtlsData[8];
PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
@@ -9023,6 +9338,7 @@
hdr[2] = (PRUint8)(length >> 8);
hdr[3] = (PRUint8)(length );
+
/* Start new handshake hashes when we start a new handshake */
if (ss->ssl3.hs.msg_type == client_hello) {
SSL_TRC(30,("%d: SSL3[%d]: reset handshake hashes",
@@ -9033,14 +9349,40 @@
}
}
/* We should not include hello_request messages in the handshake hashes */
- if (ss->ssl3.hs.msg_type != hello_request) {
+ if ((ss->ssl3.hs.msg_type != hello_request) &&
+ (ss->ssl3.hs.msg_type != hello_verify_request)) {
rv = ssl3_UpdateHandshakeHashes(ss, (unsigned char*) hdr, 4);
if (rv != SECSuccess) return rv; /* err code already set. */
+
+ /* Extra data to simulate a complete DTLS handshake fragment */
+ if (IS_DTLS(ss)) {
+ /* Sequence number */
+ dtlsData[0] = MSB(ss->ssl3.hs.recvMessageSeq);
+ dtlsData[1] = LSB(ss->ssl3.hs.recvMessageSeq);
+
+ /* Fragment offset */
+ dtlsData[2] = 0;
+ dtlsData[3] = 0;
+ dtlsData[4] = 0;
+
+ /* Fragment length */
+ dtlsData[5] = (PRUint8)(length >> 16);
+ dtlsData[6] = (PRUint8)(length >> 8);
+ dtlsData[7] = (PRUint8)(length );
+
+ rv = ssl3_UpdateHandshakeHashes(ss, (unsigned char*) dtlsData,
+ sizeof(dtlsData));
+ if (rv != SECSuccess) return rv; /* err code already set. */
+
+ }
+
+ /* The message body */
rv = ssl3_UpdateHandshakeHashes(ss, b, length);
if (rv != SECSuccess) return rv; /* err code already set. */
}
PORT_SetError(0); /* each message starts with no error. */
+
switch (ss->ssl3.hs.msg_type) {
case hello_request:
if (length != 0) {
@@ -9071,6 +9413,14 @@
}
rv = ssl3_HandleServerHello(ss, b, length);
break;
+ case hello_verify_request:
+ if (!IS_DTLS(ss) || ss->sec.isServer) {
+ (void)SSL3_SendAlert(ss, alert_fatal, unexpected_message);
+ PORT_SetError(SSL_ERROR_RX_UNEXPECTED_HELLO_VERIFY_REQUEST);
+ return SECFailure;
+ }
+ rv = dtls_HandleHelloVerifyRequest(ss, b, length);
+ break;
case certificate:
if (ss->ssl3.hs.may_get_cert_status) {
/* If we might get a CertificateStatus then we want to postpone the
@@ -9169,9 +9519,16 @@
PORT_SetError(SSL_ERROR_RX_UNKNOWN_HANDSHAKE);
rv = SECFailure;
}
+
+ if (IS_DTLS(ss) && (rv == SECSuccess)) {
+ /* Increment the expected sequence number */
+ ss->ssl3.hs.recvMessageSeq++;
+ }
+
return rv;
}
+
/* Called only from ssl3_HandleRecord, for each (deciphered) ssl3 record.
* origBuf is the decrypted ssl record content.
* Caller must hold the handshake and RecvBuf locks.
@@ -9331,6 +9688,7 @@
SSL3Opaque hash[MAX_MAC_LENGTH];
sslBuffer *plaintext;
sslBuffer temp_buf;
+ PRUint64 dtls_seq_num;
unsigned int ivLen = 0;
PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
@@ -9366,6 +9724,47 @@
crSpec = ss->ssl3.crSpec;
cipher_def = crSpec->cipher_def;
+ /* If we will be decompressing the buffer we need to decrypt somewhere
+ * other than into databuf */
+ if (crSpec->decompressor) {
+ temp_buf.buf = NULL;
+ temp_buf.space = 0;
+ plaintext = &temp_buf;
+ } else {
+ plaintext = databuf;
+ }
+
+ plaintext->len = 0; /* filled in by decode call below. */
+
+ /*
+ * DTLS relevance checks:
+ * Note that this code currently ignores all out-of-epoch packets,
+ * which means we lose some in the case of rehandshake +
+ * loss/reordering. Since DTLS is explicitly unreliable, this
+ * seems like a good tradeoff for implementation effort and is
+ * consistent with the guidance of RFC 6347 S 4.1 and S 4.2.4.1
+ */
+ if (IS_DTLS(ss)) {
+ DTLSEpoch epoch = (cText->seq_num.high >> 16) & 0xffff;
+
+ if (crSpec->epoch != epoch) {
+ ssl_ReleaseSpecReadLock(ss);
+ SSL_DBG(("%d: SSL3[%d]: HandleRecord, received packet "
+ "from irrelevant epoch %d", SSL_GETPID(), ss->fd, epoch));
+ return SECSuccess;
+ }
+
+ dtls_seq_num = (((PRUint64)(cText->seq_num.high & 0xffff)) << 32) |
+ ((PRUint64)cText->seq_num.low);
+
+ if (dtls_RecordGetRecvd(&crSpec->recvdRecords, dtls_seq_num)) {
+ ssl_ReleaseSpecReadLock(ss);
+ SSL_DBG(("%d: SSL3[%d]: HandleRecord, rejecting "
+ "potentially replayed packet", SSL_GETPID(), ss->fd));
+ return SECSuccess;
+ }
+ }
+
if (cipher_def->type == type_block &&
crSpec->version >= SSL_LIBRARY_VERSION_TLS_1_1) {
/* Consume the per-record explicit IV. RFC 4346 Section 6.2.3.2 states
@@ -9411,17 +9810,6 @@
}
}
- /* If we will be decompressing the buffer we need to decrypt somewhere
- * other than into databuf */
- if (crSpec->decompressor) {
- temp_buf.buf = NULL;
- temp_buf.space = 0;
- plaintext = &temp_buf;
- } else {
- plaintext = databuf;
- }
-
- plaintext->len = 0; /* filled in by decode call below. */
if (plaintext->space < MAX_FRAGMENT_LENGTH) {
rv = sslBuffer_Grow(plaintext, MAX_FRAGMENT_LENGTH + 2048);
if (rv != SECSuccess) {
@@ -9437,6 +9825,7 @@
PRINT_BUF(80, (ss, "ciphertext:", cText->buf->buf + ivLen,
cText->buf->len - ivLen));
+ cipher_def = crSpec->cipher_def;
ekr 2012/03/26 21:49:18 This looks like it was just redundant?
wtc 2012/03/27 00:28:25 Yes, I bet this is also a merging error. Brian Sm
isTLS = (PRBool)(crSpec->version > SSL_LIBRARY_VERSION_3_0);
if (isTLS && cText->buf->len - ivLen > (MAX_FRAGMENT_LENGTH + 2048)) {
@@ -9478,6 +9867,7 @@
}
}
+
/* Remove the MAC. */
if (plaintext->len >= crSpec->mac_size)
plaintext->len -= crSpec->mac_size;
@@ -9487,7 +9877,8 @@
/* compute the MAC */
rType = cText->type;
rv = ssl3_ComputeRecordMAC( crSpec, (PRBool)(!ss->sec.isServer),
- rType, cText->version, crSpec->read_seq_num,
+ IS_DTLS(ss), rType, cText->version,
+ IS_DTLS(ss) ? cText->seq_num : crSpec->read_seq_num,
plaintext->buf, plaintext->len, hash, &hashBytes);
if (rv != SECSuccess) {
padIsBad = PR_TRUE; /* really macIsBad */
@@ -9499,19 +9890,28 @@
crSpec->mac_size) != 0) {
/* must not hold spec lock when calling SSL3_SendAlert. */
ssl_ReleaseSpecReadLock(ss);
- SSL3_SendAlert(ss, alert_fatal, bad_record_mac);
+
/* always log mac error, in case attacker can read server logs. */
PORT_SetError(SSL_ERROR_BAD_MAC_READ);
-
SSL_DBG(("%d: SSL3[%d]: mac check failed", SSL_GETPID(), ss->fd));
-
- return SECFailure;
+
+ if (!IS_DTLS(ss)) {
+ SSL3_SendAlert(ss, alert_fatal, bad_record_mac);
+ return SECFailure;
+ } else {
+ /* Silently drop the packet */
+ plaintext->len = 0; /* Needed to ensure data not left around */
+ return SECSuccess;
+ }
}
+ if (!IS_DTLS(ss)) {
+ ssl3_BumpSequenceNumber(&crSpec->read_seq_num);
+ } else {
+ dtls_RecordSetRecvd(&crSpec->recvdRecords, dtls_seq_num);
+ }
- ssl3_BumpSequenceNumber(&crSpec->read_seq_num);
-
ssl_ReleaseSpecReadLock(ss); /*****************************************/
/*
@@ -9615,7 +10015,11 @@
rv = ssl3_HandleAlert(ss, databuf);
break;
case content_handshake:
- rv = ssl3_HandleHandshake(ss, databuf);
+ if (!IS_DTLS(ss)) {
+ rv = ssl3_HandleHandshake(ss, databuf);
+ } else {
+ rv = dtls_HandleHandshake(ss, databuf);
+ }
break;
/*
case content_application_data is handled before this switch
@@ -9675,6 +10079,9 @@
spec->read_seq_num.high = 0;
spec->read_seq_num.low = 0;
+ spec->epoch = 0;
+ dtls_InitRecvdRecords(&spec->recvdRecords);
+
spec->version = ss->vrange.max;
}
@@ -9716,6 +10123,21 @@
PORT_Memset(&ss->xtnData, 0, sizeof(TLSExtensionData));
+ if (IS_DTLS(ss)) {
+ ss->ssl3.hs.sendMessageSeq = 0;
+ ss->ssl3.hs.recvMessageSeq = 0;
+ ss->ssl3.hs.rtTimeoutMs = INITIAL_DTLS_TIMEOUT_MS;
+ ss->ssl3.hs.rtRetries = 0;
+
+ /* Have to allocate this because ssl_FreeSocket relocates
+ this structure in DEBUG mode */
+ if (!(ss->ssl3.hs.lastMessageFlight = PORT_Alloc(sizeof(PRCList))))
+ return SECFailure;
+ ss->ssl3.hs.recvdHighWater = -1;
+ PR_INIT_CLIST(ss->ssl3.hs.lastMessageFlight);
+ dtls_SetMTU(ss, 0); /* Set the MTU to the highest plateau */
+ }
+
rv = ssl3_NewHandshakeHashes(ss);
if (rv == SECSuccess) {
ss->ssl3.initialized = PR_TRUE;
@@ -9831,7 +10253,7 @@
rv = SECFailure; /* err code was set by Lookup. */
}
*oPolicy = policy;
- return rv;
+ return rv;
}
/* record the user preference for this suite */
@@ -9968,6 +10390,11 @@
PORT_SetError(SSL_ERROR_HANDSHAKE_NOT_COMPLETED);
return SECFailure;
}
+
+ if (IS_DTLS(ss)) {
+ dtls_RehandshakeCleanup(ss);
+ }
+
if (ss->opt.enableRenegotiation == SSL_RENEGOTIATE_NEVER) {
PORT_SetError(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED);
return SECFailure;
@@ -9982,7 +10409,7 @@
/* start off a new handshake. */
rv = (ss->sec.isServer) ? ssl3_SendHelloRequest(ss)
- : ssl3_SendClientHello(ss);
+ : ssl3_SendClientHello(ss, PR_FALSE);
ssl_ReleaseXmitBufLock(ss); /**************************************/
return rv;
@@ -10042,6 +10469,17 @@
ssl3_DestroyCipherSpec(&ss->ssl3.specs[0], PR_TRUE/*freeSrvName*/);
ssl3_DestroyCipherSpec(&ss->ssl3.specs[1], PR_TRUE/*freeSrvName*/);
+ /* Destroy the DTLS data */
+ if (IS_DTLS(ss)) {
+ if (ss->ssl3.hs.lastMessageFlight) {
+ dtls_FreeHandshakeMessages(ss->ssl3.hs.lastMessageFlight);
+ PORT_Free(ss->ssl3.hs.lastMessageFlight);
+ }
+ if (ss->ssl3.hs.recvdFragments.buf) {
+ PORT_Free(ss->ssl3.hs.recvdFragments.buf);
+ }
+ }
+
ss->ssl3.initialized = PR_FALSE;
SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE);

Powered by Google App Engine
This is Rietveld 408576698