Chromium Code Reviews| 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 @@ |
| ¶ms, 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); |