Index: net/third_party/nss/patches/dtls.patch |
=================================================================== |
--- net/third_party/nss/patches/dtls.patch (revision 0) |
+++ net/third_party/nss/patches/dtls.patch (revision 0) |
@@ -0,0 +1,3321 @@ |
+Index: net/third_party/nss/ssl/SSLerrs.h |
+=================================================================== |
+--- net/third_party/nss/ssl/SSLerrs.h (revision 127709) |
++++ net/third_party/nss/ssl/SSLerrs.h (working copy) |
+@@ -423,3 +423,9 @@ |
+ |
+ ER3(SSL_ERROR_RX_UNEXPECTED_CERT_STATUS, (SSL_ERROR_BASE + 121), |
+ "SSL received an unexpected Certificate Status handshake message.") |
++ |
++ER3(SSL_ERROR_RX_MALFORMED_HELLO_VERIFY_REQUEST, (SSL_ERROR_BASE + 122), |
++"SSL received a malformed Hello Verify Request handshake message.") |
++ |
++ER3(SSL_ERROR_RX_UNEXPECTED_HELLO_VERIFY_REQUEST, (SSL_ERROR_BASE + 123), |
++"SSL received an unexpected Hello Verify Request handshake message.") |
+Index: net/third_party/nss/ssl/ssl.h |
+=================================================================== |
+--- net/third_party/nss/ssl/ssl.h (revision 127709) |
++++ net/third_party/nss/ssl/ssl.h (working copy) |
+@@ -80,6 +80,12 @@ |
+ SSL_IMPORT PRFileDesc *SSL_ImportFD(PRFileDesc *model, PRFileDesc *fd); |
+ |
+ /* |
++** Imports fd into DTLS, returning a new socket. Copies DTLS configuration |
++** from model. |
++*/ |
++SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRFileDesc *model, PRFileDesc *fd); |
++ |
++/* |
+ ** Enable/disable an ssl mode |
+ ** |
+ ** SSL_SECURITY: |
+@@ -942,6 +948,14 @@ |
+ PRBool *last_handshake_resumed); |
+ |
+ /* |
++** How long should we wait before retransmitting the next flight of |
++** the DTLS handshake? Returns SECFailure if not DTLS or not in a |
++** handshake. |
++*/ |
++SSL_IMPORT SECStatus DTLS_GetTimeout(PRFileDesc *socket, |
++ PRIntervalTime *timeout); |
++ |
++/* |
+ * Return a boolean that indicates whether the underlying library |
+ * will perform as the caller expects. |
+ * |
+Index: net/third_party/nss/ssl/ssl3gthr.c |
+=================================================================== |
+--- net/third_party/nss/ssl/ssl3gthr.c (revision 127709) |
++++ net/third_party/nss/ssl/ssl3gthr.c (working copy) |
+@@ -50,7 +50,7 @@ |
+ * |
+ * returns 1 if received a complete SSL3 record. |
+ * returns 0 if recv returns EOF |
+- * returns -1 if recv returns <0 |
++ * returns -1 if recv returns < 0 |
+ * (The error value may have already been set to PR_WOULD_BLOCK_ERROR) |
+ * |
+ * Caller must hold the recv buf lock. |
+@@ -59,7 +59,8 @@ |
+ * GS_HEADER: waiting for the 5-byte SSL3 record header to come in. |
+ * GS_DATA: waiting for the body of the SSL3 record to come in. |
+ * |
+- * This loop returns when either (a) an error or EOF occurs, |
++ * This loop returns when either |
++ * (a) an error or EOF occurs, |
+ * (b) PR_WOULD_BLOCK_ERROR, |
+ * (c) data (entire SSL3 record) has been received. |
+ */ |
+@@ -167,6 +168,125 @@ |
+ return rv; |
+ } |
+ |
++/* |
++ * Read in an entire DTLS record. |
++ * |
++ * Blocks here for blocking sockets, otherwise returns -1 with |
++ * PR_WOULD_BLOCK_ERROR when socket would block. |
++ * |
++ * This is simpler than SSL because we are reading on a datagram socket |
++ * and datagrams must contain >=1 complete records. |
++ * |
++ * returns 1 if received a complete DTLS record. |
++ * returns 0 if recv returns EOF |
++ * returns -1 if recv returns < 0 |
++ * (The error value may have already been set to PR_WOULD_BLOCK_ERROR) |
++ * |
++ * Caller must hold the recv buf lock. |
++ * |
++ * This loop returns when either |
++ * (a) an error or EOF occurs, |
++ * (b) PR_WOULD_BLOCK_ERROR, |
++ * (c) data (entire DTLS record) has been received. |
++ */ |
++static int |
++dtls_GatherData(sslSocket *ss, sslGather *gs, int flags) |
++{ |
++ int nb; |
++ int err; |
++ int rv = 1; |
++ |
++ SSL_TRC(30, ("dtls_GatherData")); |
++ |
++ PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) ); |
++ |
++ gs->state = GS_HEADER; |
++ gs->offset = 0; |
++ |
++ if (gs->dtlsPacketOffset == gs->dtlsPacket.len) { /* No data left */ |
++ gs->dtlsPacketOffset = 0; |
++ gs->dtlsPacket.len = 0; |
++ |
++ /* Resize to the maximum possible size so we can fit a full datagram */ |
++ /* This is the max fragment length for an encrypted fragment |
++ ** plus the size of the record header. |
++ ** This magic constant is copied from ssl3_GatherData, with 5 changed |
++ ** to 13 (the size of the record header). |
++ */ |
++ if (gs->dtlsPacket.space < MAX_FRAGMENT_LENGTH + 2048 + 13) { |
++ err = sslBuffer_Grow(&gs->dtlsPacket, |
++ MAX_FRAGMENT_LENGTH + 2048 + 13); |
++ if (err) { /* realloc has set error code to no mem. */ |
++ return err; |
++ } |
++ } |
++ |
++ /* recv() needs to read a full datagram at a time */ |
++ nb = ssl_DefRecv(ss, gs->dtlsPacket.buf, gs->dtlsPacket.space, flags); |
++ |
++ if (nb > 0) { |
++ PRINT_BUF(60, (ss, "raw gather data:", gs->dtlsPacket.buf, nb)); |
++ } else if (nb == 0) { |
++ /* EOF */ |
++ SSL_TRC(30, ("%d: SSL3[%d]: EOF", SSL_GETPID(), ss->fd)); |
++ rv = 0; |
++ return rv; |
++ } else /* if (nb < 0) */ { |
++ SSL_DBG(("%d: SSL3[%d]: recv error %d", SSL_GETPID(), ss->fd, |
++ PR_GetError())); |
++ rv = SECFailure; |
++ return rv; |
++ } |
++ |
++ gs->dtlsPacket.len = nb; |
++ } |
++ |
++ /* At this point we should have >=1 complete records lined up in |
++ * dtlsPacket. Read off the header. |
++ */ |
++ if ((gs->dtlsPacket.len - gs->dtlsPacketOffset) < 13) { |
++ SSL_DBG(("%d: SSL3[%d]: rest of DTLS packet " |
++ "too short to contain header", SSL_GETPID(), ss->fd)); |
++ PR_SetError(PR_WOULD_BLOCK_ERROR, 0); |
++ gs->dtlsPacketOffset = 0; |
++ gs->dtlsPacket.len = 0; |
++ rv = SECFailure; |
++ return rv; |
++ } |
++ memcpy(gs->hdr, gs->dtlsPacket.buf + gs->dtlsPacketOffset, 13); |
++ gs->dtlsPacketOffset += 13; |
++ |
++ /* Have received SSL3 record header in gs->hdr. */ |
++ gs->remainder = (gs->hdr[11] << 8) | gs->hdr[12]; |
++ |
++ if ((gs->dtlsPacket.len - gs->dtlsPacketOffset) < gs->remainder) { |
++ SSL_DBG(("%d: SSL3[%d]: rest of DTLS packet too short " |
++ "to contain rest of body", SSL_GETPID(), ss->fd)); |
++ PR_SetError(PR_WOULD_BLOCK_ERROR, 0); |
++ gs->dtlsPacketOffset = 0; |
++ gs->dtlsPacket.len = 0; |
++ rv = SECFailure; |
++ return rv; |
++ } |
++ |
++ /* OK, we have at least one complete packet, copy into inbuf */ |
++ if (gs->remainder > gs->inbuf.space) { |
++ err = sslBuffer_Grow(&gs->inbuf, gs->remainder); |
++ if (err) { /* realloc has set error code to no mem. */ |
++ return err; |
++ } |
++ } |
++ |
++ memcpy(gs->inbuf.buf, gs->dtlsPacket.buf + gs->dtlsPacketOffset, |
++ gs->remainder); |
++ gs->inbuf.len = gs->remainder; |
++ gs->offset = gs->remainder; |
++ gs->dtlsPacketOffset += gs->remainder; |
++ gs->state = GS_INIT; |
++ |
++ return 1; |
++} |
++ |
+ /* Gather in a record and when complete, Handle that record. |
+ * Repeat this until the handshake is complete, |
+ * or until application data is available. |
+@@ -190,6 +310,8 @@ |
+ int rv; |
+ PRBool canFalseStart = PR_FALSE; |
+ |
++ SSL_TRC(30, ("ssl3_GatherCompleteHandshake")); |
++ |
+ PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) ); |
+ do { |
+ /* Without this, we may end up wrongly reporting |
+@@ -224,7 +346,24 @@ |
+ rv = ssl3_HandleRecord(ss, NULL, &ss->gs.buf); |
+ } else { |
+ /* bring in the next sslv3 record. */ |
+- rv = ssl3_GatherData(ss, &ss->gs, flags); |
++ if (!IS_DTLS(ss)) { |
++ rv = ssl3_GatherData(ss, &ss->gs, flags); |
++ } else { |
++ rv = dtls_GatherData(ss, &ss->gs, flags); |
++ |
++ /* If we got a would block error, that means that no data was |
++ * available, so we check the timer to see if it's time to |
++ * retransmit */ |
++ if (rv == SECFailure && |
++ (PORT_GetError() == PR_WOULD_BLOCK_ERROR)) { |
++ ssl_GetSSL3HandshakeLock(ss); |
++ dtls_CheckTimer(ss); |
++ ssl_ReleaseSSL3HandshakeLock(ss); |
++ /* Restore the error in case something succeeded */ |
++ PORT_SetError(PR_WOULD_BLOCK_ERROR); |
++ } |
++ } |
++ |
+ if (rv <= 0) { |
+ return rv; |
+ } |
+@@ -236,6 +375,20 @@ |
+ */ |
+ cText.type = (SSL3ContentType)ss->gs.hdr[0]; |
+ cText.version = (ss->gs.hdr[1] << 8) | ss->gs.hdr[2]; |
++ |
++ if (IS_DTLS(ss)) { |
++ int i; |
++ |
++ cText.version = dtls_DTLSVersionToTLSVersion(cText.version); |
++ /* DTLS sequence number */ |
++ cText.seq_num.high = 0; cText.seq_num.low = 0; |
++ for (i = 0; i < 4; i++) { |
++ cText.seq_num.high <<= 8; cText.seq_num.low <<= 8; |
++ cText.seq_num.high |= ss->gs.hdr[3 + i]; |
++ cText.seq_num.low |= ss->gs.hdr[7 + i]; |
++ } |
++ } |
++ |
+ cText.buf = &ss->gs.inbuf; |
+ rv = ssl3_HandleRecord(ss, &cText, &ss->gs.buf); |
+ } |
+Index: net/third_party/nss/ssl/derive.c |
+=================================================================== |
+--- net/third_party/nss/ssl/derive.c (revision 127709) |
++++ net/third_party/nss/ssl/derive.c (working copy) |
+@@ -583,6 +583,8 @@ |
+ * arguments were all valid but the slot cannot be bypassed. |
+ */ |
+ |
++/* XXX Add SSL_CBP_TLS1_1 and test it in protocolmask when setting isTLS. */ |
++ |
+ SECStatus |
+ SSL_CanBypass(CERTCertificate *cert, SECKEYPrivateKey *srvPrivkey, |
+ PRUint32 protocolmask, PRUint16 *ciphersuites, int nsuites, |
+Index: net/third_party/nss/ssl/sslerr.h |
+=================================================================== |
+--- net/third_party/nss/ssl/sslerr.h (revision 127709) |
++++ net/third_party/nss/ssl/sslerr.h (working copy) |
+@@ -215,6 +215,9 @@ |
+ |
+ SSL_ERROR_RX_UNEXPECTED_CERT_STATUS = (SSL_ERROR_BASE + 121), |
+ |
++SSL_ERROR_RX_MALFORMED_HELLO_VERIFY_REQUEST = (SSL_ERROR_BASE + 122), |
++SSL_ERROR_RX_UNEXPECTED_HELLO_VERIFY_REQUEST = (SSL_ERROR_BASE + 123), |
++ |
+ SSL_ERROR_END_OF_LIST /* let the c compiler determine the value of this. */ |
+ } SSLErrorCodes; |
+ #endif /* NO_SECURITY_ERROR_ENUM */ |
+Index: net/third_party/nss/ssl/ssldef.c |
+=================================================================== |
+--- net/third_party/nss/ssl/ssldef.c (revision 127709) |
++++ net/third_party/nss/ssl/ssldef.c (working copy) |
+@@ -138,6 +138,11 @@ |
+ return rv; |
+ } |
+ sent += rv; |
++ |
++ if (IS_DTLS(ss) && (len > sent)) { |
++ /* We got a partial write so just return it */ |
++ return sent; |
++ } |
+ } while (len > sent); |
+ ss->lastWriteBlocked = 0; |
+ return sent; |
+Index: net/third_party/nss/ssl/sslimpl.h |
+=================================================================== |
+--- net/third_party/nss/ssl/sslimpl.h (revision 127709) |
++++ net/third_party/nss/ssl/sslimpl.h (working copy) |
+@@ -62,6 +62,7 @@ |
+ #endif |
+ #include "nssrwlk.h" |
+ #include "prthread.h" |
++#include "prclist.h" |
+ |
+ #include "sslt.h" /* for some formerly private types, now public */ |
+ |
+@@ -195,6 +196,10 @@ |
+ |
+ #define EXPORT_RSA_KEY_LENGTH 64 /* bytes */ |
+ |
++#define INITIAL_DTLS_TIMEOUT_MS 1000 /* Default value from RFC 4347 = 1s*/ |
++#define MAX_DTLS_TIMEOUT_MS 60000 /* 1 minute */ |
++#define DTLS_FINISHED_TIMER_MS 120000 /* Time to wait in FINISHED state */ |
++ |
+ typedef struct sslBufferStr sslBuffer; |
+ typedef struct sslConnectInfoStr sslConnectInfo; |
+ typedef struct sslGatherStr sslGather; |
+@@ -287,6 +292,8 @@ |
+ /* Flags interpreted by ssl send functions. */ |
+ #define ssl_SEND_FLAG_FORCE_INTO_BUFFER 0x40000000 |
+ #define ssl_SEND_FLAG_NO_BUFFER 0x20000000 |
++#define ssl_SEND_FLAG_USE_EPOCH 0x10000000 /* DTLS only */ |
++#define ssl_SEND_FLAG_NO_RETRANSMIT 0x08000000 /* DTLS only */ |
+ #define ssl_SEND_FLAG_MASK 0x7f000000 |
+ |
+ /* |
+@@ -448,8 +455,15 @@ |
+ ** The portion of the SSL record header put here always comes off the wire |
+ ** as plaintext, never ciphertext. |
+ ** For SSL2, the plaintext portion is two bytes long. For SSl3 it is 5. |
++ ** For DTLS it is 13. |
+ */ |
+- unsigned char hdr[5]; /* ssl 2 & 3 */ |
++ unsigned char hdr[13]; /* ssl 2 & 3 or dtls */ |
++ |
++ /* Buffer for DTLS data read off the wire as a single datagram */ |
++ sslBuffer dtlsPacket; |
++ |
++ /* the start of the buffered DTLS record in dtlsPacket */ |
++ unsigned int dtlsPacketOffset; |
+ }; |
+ |
+ /* sslGather.state */ |
+@@ -521,6 +535,10 @@ |
+ PRUint32 low; |
+ } SSL3SequenceNumber; |
+ |
++typedef PRUint16 DTLSEpoch; |
++ |
++typedef void (*DTLSTimerCb)(sslSocket *); |
++ |
+ #define MAX_MAC_CONTEXT_BYTES 400 |
+ #define MAX_MAC_CONTEXT_LLONGS (MAX_MAC_CONTEXT_BYTES / 8) |
+ |
+@@ -547,6 +565,20 @@ |
+ PRUint64 cipher_context[MAX_CIPHER_CONTEXT_LLONGS]; |
+ } ssl3KeyMaterial; |
+ |
++/* The DTLS anti-replay window. Defined here because we need it in |
++ * the cipher spec. Note that this is a ring buffer but left and |
++ * right represent the true window, with modular arithmetic used to |
++ * map them onto the buffer. |
++ */ |
++#define DTLS_RECVD_RECORDS_WINDOW 1024 /* Packets; approximate |
++ * Must be divisible by 8 |
++ */ |
++typedef struct DTLSRecvdRecordsStr { |
++ unsigned char data[DTLS_RECVD_RECORDS_WINDOW/8]; |
++ PRUint64 left; |
++ PRUint64 right; |
++} DTLSRecvdRecords; |
++ |
+ /* |
+ ** These are the "specs" in the "ssl3" struct. |
+ ** Access to the pointers to these specs, and all the specs' contents |
+@@ -582,6 +614,8 @@ |
+ SECItem srvVirtName; /* for server: name that was negotiated |
+ * with a client. For client - is |
+ * always set to NULL.*/ |
++ DTLSEpoch epoch; |
++ DTLSRecvdRecords recvdRecords; |
+ } ssl3CipherSpec; |
+ |
+ typedef enum { never_cached, |
+@@ -777,6 +811,17 @@ |
+ typedef SECStatus (*sslRestartTarget)(sslSocket *); |
+ |
+ /* |
++** A DTLS queued message (potentially to be retransmitted) |
++*/ |
++typedef struct DTLSQueuedMessageStr { |
++ PRCList link; /* The linked list link */ |
++ DTLSEpoch epoch; /* The epoch to use */ |
++ SSL3ContentType type; /* The message type */ |
++ unsigned char *data; /* The data */ |
++ PRUint16 len; /* The data length */ |
++} DTLSQueuedMessage; |
++ |
++/* |
+ ** This is the "hs" member of the "ssl3" struct. |
+ ** This entire struct is protected by ssl3HandshakeLock |
+ */ |
+@@ -831,6 +876,30 @@ |
+ sslRestartTarget restartTarget; |
+ /* Shared state between ssl3_HandleFinished and ssl3_FinishHandshake */ |
+ PRBool cacheSID; |
++ |
++ /* This group of values is used for DTLS */ |
++ PRUint16 sendMessageSeq; /* The sending message sequence |
++ * number */ |
++ PRCList * lastMessageFlight; /* The last message flight we sent. |
++ * This is a pointer because |
++ * ssl_FreeSocket relocates the |
++ * structure in DEBUG mode, which |
++ * messes up the list macros */ |
++ PRUint16 maxMessageSent; /* The largest message we sent */ |
++ PRUint16 recvMessageSeq; /* The receiving message sequence |
++ * number */ |
++ sslBuffer recvdFragments; /* The fragments we have received in |
++ * a bitmask */ |
++ PRInt32 recvdHighWater; /* The high water mark for fragments |
++ * received. -1 means no reassembly |
++ * in progress. */ |
++ unsigned char cookie[32]; /* The cookie */ |
++ unsigned char cookieLen; /* The length of the cookie */ |
++ PRIntervalTime rtTimerStarted; /* When the timer was started */ |
++ DTLSTimerCb rtTimerCb; /* The function to call on expiry */ |
++ PRUint32 rtTimeoutMs; /* The length of the current timeout |
++ * used for backoff (in ms) */ |
++ PRUint32 rtRetries; /* The retry counter */ |
+ } SSL3HandshakeState; |
+ |
+ |
+@@ -882,11 +951,18 @@ |
+ */ |
+ SECItem nextProto; |
+ SSLNextProtoState nextProtoState; |
++ |
++ PRUint16 mtu; /* Our estimate of the MTU */ |
+ }; |
+ |
++#define DTLS_MAX_MTU 1500 /* Ethernet MTU but without subtracting the |
++ * headers, so slightly larger than expected */ |
++#define IS_DTLS(ss) (ss->protocolVariant == ssl_variant_datagram) |
++ |
+ typedef struct { |
+ SSL3ContentType type; |
+ SSL3ProtocolVersion version; |
++ SSL3SequenceNumber seq_num; /* DTLS only */ |
+ sslBuffer * buf; |
+ } SSL3Ciphertext; |
+ |
+@@ -1188,6 +1264,9 @@ |
+ /* True when the current session is a stateless resume. */ |
+ PRBool statelessResume; |
+ TLSExtensionData xtnData; |
++ |
++ /* Whether we are doing stream or datagram mode */ |
++ SSLProtocolVariant protocolVariant; |
+ }; |
+ |
+ |
+@@ -1321,7 +1400,35 @@ |
+ extern SECStatus ssl_EnableNagleDelay(sslSocket *ss, PRBool enabled); |
+ |
+ extern PRBool ssl3_CanFalseStart(sslSocket *ss); |
++extern SECStatus |
++ssl3_CompressMACEncryptRecord(ssl3CipherSpec * cwSpec, |
++ PRBool isServer, |
++ PRBool isDTLS, |
++ SSL3ContentType type, |
++ const SSL3Opaque * pIn, |
++ PRUint32 contentLen, |
++ sslBuffer * wrBuf); |
++extern PRInt32 ssl3_SendRecord(sslSocket *ss, DTLSEpoch epoch, |
++ SSL3ContentType type, |
++ const SSL3Opaque* pIn, PRInt32 nIn, |
++ PRInt32 flags); |
+ |
++#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 SSL_LOCK_READER(ss) if (ss->recvLock) PZ_Lock(ss->recvLock) |
+ #define SSL_UNLOCK_READER(ss) if (ss->recvLock) PZ_Unlock(ss->recvLock) |
+ #define SSL_LOCK_WRITER(ss) if (ss->sendLock) PZ_Lock(ss->sendLock) |
+@@ -1417,6 +1524,7 @@ |
+ extern void ssl_FreeSocket(struct sslSocketStr *ssl); |
+ extern SECStatus SSL3_SendAlert(sslSocket *ss, SSL3AlertLevel level, |
+ SSL3AlertDescription desc); |
++extern SECStatus ssl3_DecodeError(sslSocket *ss); |
+ |
+ extern SECStatus ssl3_RestartHandshakeAfterCertReq(sslSocket * ss, |
+ CERTCertificate * cert, |
+@@ -1436,7 +1544,7 @@ |
+ /* |
+ * SSL3 specific routines |
+ */ |
+-SECStatus ssl3_SendClientHello(sslSocket *ss); |
++SECStatus ssl3_SendClientHello(sslSocket *ss, PRBool resending); |
+ |
+ /* |
+ * input into the SSL3 machinery from the actualy network reading code |
+@@ -1531,6 +1639,8 @@ |
+ unsigned char *cs, int *size); |
+ |
+ extern SECStatus ssl3_RedoHandshake(sslSocket *ss, PRBool flushCache); |
++extern SECStatus ssl3_HandleHandshakeMessage(sslSocket *ss, SSL3Opaque *b, |
++ PRUint32 length); |
+ |
+ extern void ssl3_DestroySSL3Info(sslSocket *ss); |
+ |
+@@ -1556,6 +1666,7 @@ |
+ extern SECStatus ssl3_ComputeCommonKeyHash(PRUint8 * hashBuf, |
+ unsigned int bufLen, SSL3Hashes *hashes, |
+ PRBool bypassPKCS11); |
++extern void ssl3_DestroyCipherSpec(ssl3CipherSpec *spec, PRBool freeSrvName); |
+ extern SECStatus ssl3_InitPendingCipherSpec(sslSocket *ss, PK11SymKey *pms); |
+ extern SECStatus ssl3_AppendHandshake(sslSocket *ss, const void *void_src, |
+ PRInt32 bytes); |
+@@ -1724,6 +1835,42 @@ |
+ CERTCertList* list); |
+ #endif /* NSS_PLATFORM_CLIENT_AUTH */ |
+ |
++/**************** DTLS-specific functions **************/ |
++extern void dtls_FreeQueuedMessage(DTLSQueuedMessage *msg); |
++extern void dtls_FreeQueuedMessages(PRCList *lst); |
++extern void dtls_FreeHandshakeMessages(PRCList *lst); |
++ |
++extern SECStatus dtls_HandleHandshake(sslSocket *ss, sslBuffer *origBuf); |
++extern SECStatus dtls_HandleHelloVerifyRequest(sslSocket *ss, |
++ SSL3Opaque *b, PRUint32 length); |
++extern SECStatus dtls_StageHandshakeMessage(sslSocket *ss); |
++extern SECStatus dtls_QueueMessage(sslSocket *ss, SSL3ContentType type, |
++ const SSL3Opaque *pIn, PRInt32 nIn); |
++extern SECStatus dtls_FlushHandshakeMessages(sslSocket *ss, PRInt32 flags); |
++extern SECStatus dtls_CompressMACEncryptRecord(sslSocket *ss, |
++ DTLSEpoch epoch, |
++ PRBool use_epoch, |
++ SSL3ContentType type, |
++ const SSL3Opaque *pIn, |
++ PRUint32 contentLen, |
++ sslBuffer *wrBuf); |
++SECStatus ssl3_DisableNonDTLSSuites(sslSocket * ss); |
++extern SECStatus dtls_StartTimer(sslSocket *ss, DTLSTimerCb cb); |
++extern SECStatus dtls_RestartTimer(sslSocket *ss, PRBool backoff, |
++ DTLSTimerCb cb); |
++extern void dtls_CheckTimer(sslSocket *ss); |
++extern void dtls_CancelTimer(sslSocket *ss); |
++extern void dtls_FinishedTimerCb(sslSocket *ss); |
++extern void dtls_SetMTU(sslSocket *ss, PRUint16 advertised); |
++extern void dtls_InitRecvdRecords(DTLSRecvdRecords *records); |
++extern int dtls_RecordGetRecvd(DTLSRecvdRecords *records, PRUint64 seq); |
++extern void dtls_RecordSetRecvd(DTLSRecvdRecords *records, PRUint64 seq); |
++extern void dtls_RehandshakeCleanup(sslSocket *ss); |
++extern SSL3ProtocolVersion |
++dtls_TLSVersionToDTLSVersion(SSL3ProtocolVersion tlsv); |
++extern SSL3ProtocolVersion |
++dtls_DTLSVersionToTLSVersion(SSL3ProtocolVersion dtlsv); |
++ |
+ /********************** misc calls *********************/ |
+ |
+ extern int ssl_MapLowLevelError(int hiLevelError); |
+Index: net/third_party/nss/ssl/manifest.mn |
+=================================================================== |
+--- net/third_party/nss/ssl/manifest.mn (revision 127709) |
++++ net/third_party/nss/ssl/manifest.mn (working copy) |
+@@ -51,6 +51,7 @@ |
+ |
+ CSRCS = \ |
+ derive.c \ |
++ dtls1con.c \ |
+ prelib.c \ |
+ ssl3con.c \ |
+ ssl3gthr.c \ |
+Index: net/third_party/nss/ssl/ssl3prot.h |
+=================================================================== |
+--- net/third_party/nss/ssl/ssl3prot.h (revision 127709) |
++++ net/third_party/nss/ssl/ssl3prot.h (working copy) |
+@@ -61,6 +61,9 @@ |
+ |
+ #define SSL3_RECORD_HEADER_LENGTH 5 |
+ |
++/* SSL3_RECORD_HEADER_LENGTH + epoch/sequence_number */ |
++#define DTLS_RECORD_HEADER_LENGTH 13 |
++ |
+ #define MAX_FRAGMENT_LENGTH 16384 |
+ |
+ typedef enum { |
+@@ -150,6 +153,7 @@ |
+ hello_request = 0, |
+ client_hello = 1, |
+ server_hello = 2, |
++ hello_verify_request = 3, |
+ new_session_ticket = 4, |
+ certificate = 11, |
+ server_key_exchange = 12, |
+Index: net/third_party/nss/ssl/sslcon.c |
+=================================================================== |
+--- net/third_party/nss/ssl/sslcon.c (revision 127709) |
++++ net/third_party/nss/ssl/sslcon.c (working copy) |
+@@ -1249,7 +1249,12 @@ |
+ |
+ ssl_GetRecvBufLock(ss); |
+ |
+- if (ss->version >= SSL_LIBRARY_VERSION_3_0) { |
++ /* The special case DTLS logic is needed here because the SSL/TLS |
++ * version wants to auto-detect SSL2 vs. SSL3 on the initial handshake |
++ * (ss->version == 0) but with DTLS it gets confused, so we force the |
++ * SSL3 version. |
++ */ |
++ if ((ss->version >= SSL_LIBRARY_VERSION_3_0) || IS_DTLS(ss)) { |
+ /* Wait for handshake to complete, or application data to arrive. */ |
+ rv = ssl3_GatherCompleteHandshake(ss, 0); |
+ } else { |
+@@ -3120,7 +3125,7 @@ |
+ |
+ ssl_GetSSL3HandshakeLock(ss); |
+ ssl_GetXmitBufLock(ss); |
+- rv = ssl3_SendClientHello(ss); |
++ rv = ssl3_SendClientHello(ss, PR_FALSE); |
+ ssl_ReleaseXmitBufLock(ss); |
+ ssl_ReleaseSSL3HandshakeLock(ss); |
+ |
+Index: net/third_party/nss/ssl/sslsecur.c |
+=================================================================== |
+--- net/third_party/nss/ssl/sslsecur.c (revision 127709) |
++++ net/third_party/nss/ssl/sslsecur.c (working copy) |
+@@ -615,6 +615,7 @@ |
+ if (!(flags & PR_MSG_PEEK)) { |
+ ss->gs.readOffset += amount; |
+ } |
++ PORT_Assert(ss->gs.readOffset <= ss->gs.writeOffset); |
+ rv = amount; |
+ |
+ SSL_TRC(30, ("%d: SSL[%d]: amount=%d available=%d", |
+Index: net/third_party/nss/ssl/sslsock.c |
+=================================================================== |
+--- net/third_party/nss/ssl/sslsock.c (revision 127709) |
++++ net/third_party/nss/ssl/sslsock.c (working copy) |
+@@ -194,11 +194,20 @@ |
+ /* |
+ * default range of enabled SSL/TLS protocols |
+ */ |
+-static SSLVersionRange versions_defaults = { |
++static SSLVersionRange versions_defaults_stream = { |
+ SSL_LIBRARY_VERSION_3_0, |
+ SSL_LIBRARY_VERSION_TLS_1_0 |
+ }; |
+ |
++static SSLVersionRange versions_defaults_datagram = { |
++ SSL_LIBRARY_VERSION_TLS_1_1, |
++ SSL_LIBRARY_VERSION_TLS_1_1 |
++}; |
++ |
++#define VERSIONS_DEFAULTS(variant) \ |
++ (variant == ssl_variant_stream ? &versions_defaults_stream : \ |
++ &versions_defaults_datagram) |
++ |
+ sslSessionIDLookupFunc ssl_sid_lookup; |
+ sslSessionIDCacheFunc ssl_sid_cache; |
+ sslSessionIDUncacheFunc ssl_sid_uncache; |
+@@ -217,7 +226,7 @@ |
+ #define LOCKSTATUS_OFFSET 10 /* offset of ENABLED */ |
+ |
+ /* forward declarations. */ |
+-static sslSocket *ssl_NewSocket(PRBool makeLocks); |
++static sslSocket *ssl_NewSocket(PRBool makeLocks, SSLProtocolVariant variant); |
+ static SECStatus ssl_MakeLocks(sslSocket *ss); |
+ static void ssl_SetDefaultsFromEnvironment(void); |
+ static PRStatus ssl_PushIOLayer(sslSocket *ns, PRFileDesc *stack, |
+@@ -281,7 +290,13 @@ |
+ sslSocket *ss; |
+ SECStatus rv; |
+ |
+- ss = ssl_NewSocket((PRBool)(!os->opt.noLocks)); |
++ /* Not implemented for datagram */ |
++ if (IS_DTLS(os)) { |
++ PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); |
++ return NULL; |
++ } |
++ |
++ ss = ssl_NewSocket((PRBool)(!os->opt.noLocks), os->protocolVariant); |
+ if (ss) { |
+ ss->opt = os->opt; |
+ ss->opt.useSocks = PR_FALSE; |
+@@ -698,6 +713,13 @@ |
+ break; |
+ |
+ case SSL_ENABLE_TLS: |
++ if (IS_DTLS(ss)) { |
++ if (on) { |
++ PORT_SetError(SEC_ERROR_INVALID_ARGS); |
++ rv = SECFailure; /* not allowed */ |
++ } |
++ break; |
++ } |
+ ssl_EnableTLS(&ss->vrange, on); |
+ ss->preferredCipher = NULL; |
+ if (ss->cipherSpecs) { |
+@@ -708,6 +730,13 @@ |
+ break; |
+ |
+ case SSL_ENABLE_SSL3: |
++ if (IS_DTLS(ss)) { |
++ if (on) { |
++ PORT_SetError(SEC_ERROR_INVALID_ARGS); |
++ rv = SECFailure; /* not allowed */ |
++ } |
++ break; |
++ } |
+ ssl_EnableSSL3(&ss->vrange, on); |
+ ss->preferredCipher = NULL; |
+ if (ss->cipherSpecs) { |
+@@ -718,6 +747,13 @@ |
+ break; |
+ |
+ case SSL_ENABLE_SSL2: |
++ if (IS_DTLS(ss)) { |
++ if (on) { |
++ PORT_SetError(SEC_ERROR_INVALID_ARGS); |
++ rv = SECFailure; /* not allowed */ |
++ } |
++ break; |
++ } |
+ ss->opt.enableSSL2 = on; |
+ if (on) { |
+ ss->opt.v2CompatibleHello = on; |
+@@ -743,6 +779,13 @@ |
+ break; |
+ |
+ case SSL_V2_COMPATIBLE_HELLO: |
++ if (IS_DTLS(ss)) { |
++ if (on) { |
++ PORT_SetError(SEC_ERROR_INVALID_ARGS); |
++ rv = SECFailure; /* not allowed */ |
++ } |
++ break; |
++ } |
+ ss->opt.v2CompatibleHello = on; |
+ if (!on) { |
+ ss->opt.enableSSL2 = on; |
+@@ -938,10 +981,10 @@ |
+ case SSL_HANDSHAKE_AS_CLIENT: on = ssl_defaults.handshakeAsClient; break; |
+ case SSL_HANDSHAKE_AS_SERVER: on = ssl_defaults.handshakeAsServer; break; |
+ case SSL_ENABLE_TLS: |
+- on = versions_defaults.max >= SSL_LIBRARY_VERSION_TLS_1_0; |
++ on = versions_defaults_stream.max >= SSL_LIBRARY_VERSION_TLS_1_0; |
+ break; |
+ case SSL_ENABLE_SSL3: |
+- on = versions_defaults.min == SSL_LIBRARY_VERSION_3_0; |
++ on = versions_defaults_stream.min == SSL_LIBRARY_VERSION_3_0; |
+ break; |
+ case SSL_ENABLE_SSL2: on = ssl_defaults.enableSSL2; break; |
+ case SSL_NO_CACHE: on = ssl_defaults.noCache; break; |
+@@ -1034,11 +1077,11 @@ |
+ break; |
+ |
+ case SSL_ENABLE_TLS: |
+- ssl_EnableTLS(&versions_defaults, on); |
++ ssl_EnableTLS(&versions_defaults_stream, on); |
+ break; |
+ |
+ case SSL_ENABLE_SSL3: |
+- ssl_EnableSSL3(&versions_defaults, on); |
++ ssl_EnableSSL3(&versions_defaults_stream, on); |
+ break; |
+ |
+ case SSL_ENABLE_SSL2: |
+@@ -1360,8 +1403,8 @@ |
+ |
+ |
+ /* LOCKS ??? XXX */ |
+-PRFileDesc * |
+-SSL_ImportFD(PRFileDesc *model, PRFileDesc *fd) |
++static PRFileDesc * |
++ssl_ImportFD(PRFileDesc *model, PRFileDesc *fd, SSLProtocolVariant variant) |
+ { |
+ sslSocket * ns = NULL; |
+ PRStatus rv; |
+@@ -1374,10 +1417,10 @@ |
+ |
+ if (model == NULL) { |
+ /* Just create a default socket if we're given NULL for the model */ |
+- ns = ssl_NewSocket((PRBool)(!ssl_defaults.noLocks)); |
++ ns = ssl_NewSocket((PRBool)(!ssl_defaults.noLocks), variant); |
+ } else { |
+ sslSocket * ss = ssl_FindSocket(model); |
+- if (ss == NULL) { |
++ if (ss == NULL || ss->protocolVariant != variant) { |
+ SSL_DBG(("%d: SSL[%d]: bad model socket in ssl_ImportFD", |
+ SSL_GETPID(), model)); |
+ return NULL; |
+@@ -1403,6 +1446,18 @@ |
+ return fd; |
+ } |
+ |
++PRFileDesc * |
++SSL_ImportFD(PRFileDesc *model, PRFileDesc *fd) |
++{ |
++ return ssl_ImportFD(model, fd, ssl_variant_stream); |
++} |
++ |
++PRFileDesc * |
++DTLS_ImportFD(PRFileDesc *model, PRFileDesc *fd) |
++{ |
++ return ssl_ImportFD(model, fd, ssl_variant_datagram); |
++} |
++ |
+ SECStatus |
+ SSL_SetNextProtoCallback(PRFileDesc *fd, SSLNextProtoCallback callback, |
+ void *arg) |
+@@ -1667,9 +1722,18 @@ |
+ ssl3_VersionIsSupported(SSLProtocolVariant protocolVariant, |
+ SSL3ProtocolVersion version) |
+ { |
+- return protocolVariant == ssl_variant_stream && |
+- version >= SSL_LIBRARY_VERSION_3_0 && |
+- version <= SSL_LIBRARY_VERSION_MAX_SUPPORTED; |
++ switch (protocolVariant) { |
++ case ssl_variant_stream: |
++ return (version >= SSL_LIBRARY_VERSION_3_0 && |
++ version <= SSL_LIBRARY_VERSION_MAX_SUPPORTED); |
++ case ssl_variant_datagram: |
++ return (version >= SSL_LIBRARY_VERSION_TLS_1_1 && |
++ version <= SSL_LIBRARY_VERSION_MAX_SUPPORTED); |
++ default: |
++ /* Can't get here */ |
++ PORT_Assert(PR_FALSE); |
++ return PR_FALSE; |
++ } |
+ } |
+ |
+ /* Returns PR_TRUE if the given version range is valid and |
+@@ -1689,13 +1753,24 @@ |
+ SSL_VersionRangeGetSupported(SSLProtocolVariant protocolVariant, |
+ SSLVersionRange *vrange) |
+ { |
+- if (protocolVariant != ssl_variant_stream || !vrange) { |
++ if (!vrange) { |
+ PORT_SetError(SEC_ERROR_INVALID_ARGS); |
+ return SECFailure; |
+ } |
+ |
+- vrange->min = SSL_LIBRARY_VERSION_3_0; |
+- vrange->max = SSL_LIBRARY_VERSION_MAX_SUPPORTED; |
++ switch (protocolVariant) { |
++ case ssl_variant_stream: |
++ vrange->min = SSL_LIBRARY_VERSION_3_0; |
++ vrange->max = SSL_LIBRARY_VERSION_MAX_SUPPORTED; |
++ break; |
++ case ssl_variant_datagram: |
++ vrange->min = SSL_LIBRARY_VERSION_TLS_1_1; |
++ vrange->max = SSL_LIBRARY_VERSION_MAX_SUPPORTED; |
++ break; |
++ default: |
++ PORT_SetError(SEC_ERROR_INVALID_ARGS); |
++ return SECFailure; |
++ } |
+ |
+ return SECSuccess; |
+ } |
+@@ -1704,12 +1779,13 @@ |
+ SSL_VersionRangeGetDefault(SSLProtocolVariant protocolVariant, |
+ SSLVersionRange *vrange) |
+ { |
+- if (protocolVariant != ssl_variant_stream || !vrange) { |
++ if ((protocolVariant != ssl_variant_stream && |
++ protocolVariant != ssl_variant_datagram) || !vrange) { |
+ PORT_SetError(SEC_ERROR_INVALID_ARGS); |
+ return SECFailure; |
+ } |
+ |
+- *vrange = versions_defaults; |
++ *vrange = *VERSIONS_DEFAULTS(protocolVariant); |
+ |
+ return SECSuccess; |
+ } |
+@@ -1723,7 +1799,7 @@ |
+ return SECFailure; |
+ } |
+ |
+- versions_defaults = *vrange; |
++ *VERSIONS_DEFAULTS(protocolVariant) = *vrange; |
+ |
+ return SECSuccess; |
+ } |
+@@ -2830,7 +2906,7 @@ |
+ ** Create a newsocket structure for a file descriptor. |
+ */ |
+ static sslSocket * |
+-ssl_NewSocket(PRBool makeLocks) |
++ssl_NewSocket(PRBool makeLocks, SSLProtocolVariant protocolVariant) |
+ { |
+ sslSocket *ss; |
+ |
+@@ -2851,7 +2927,7 @@ |
+ ss->opt = ssl_defaults; |
+ ss->opt.useSocks = PR_FALSE; |
+ ss->opt.noLocks = !makeLocks; |
+- ss->vrange = versions_defaults; |
++ ss->vrange = *VERSIONS_DEFAULTS(protocolVariant); |
+ |
+ ss->peerID = NULL; |
+ ss->rTimeout = PR_INTERVAL_NO_TIMEOUT; |
+@@ -2907,6 +2983,7 @@ |
+ PORT_Free(ss); |
+ ss = NULL; |
+ } |
++ ss->protocolVariant = protocolVariant; |
+ } |
+ return ss; |
+ } |
+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) |
+@@ -42,6 +42,8 @@ |
+ * ***** END LICENSE BLOCK ***** */ |
+ /* $Id: ssl3con.c,v 1.173 2012/03/18 00:31:19 wtc%google.com Exp $ */ |
+ |
++/* TODO(ekr): Implement HelloVerifyRequest on server side. OK for now. */ |
++ |
+ #include "cert.h" |
+ #include "ssl.h" |
+ #include "cryptohi.h" /* for DSAU_ stuff */ |
+@@ -92,6 +94,7 @@ |
+ 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, |
+@@ -221,22 +224,6 @@ |
+ #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 */ |
+ |
+ |
+@@ -517,6 +504,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; |
+@@ -656,7 +644,7 @@ |
+ 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 = |
+@@ -1148,7 +1136,7 @@ |
+ ** ssl3_DestroySSL3Info |
+ ** Caller must hold SpecWriteLock. |
+ */ |
+-static void |
++void |
+ ssl3_DestroyCipherSpec(ssl3CipherSpec *spec, PRBool freeSrvName) |
+ { |
+ PRBool freeit = (PRBool)(!spec->bypassCiphers); |
+@@ -1228,6 +1216,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; |
+@@ -1754,6 +1748,7 @@ |
+ ssl3_InitPendingCipherSpec(sslSocket *ss, PK11SymKey *pms) |
+ { |
+ ssl3CipherSpec * pwSpec; |
++ ssl3CipherSpec * cwSpec; |
+ SECStatus rv; |
+ |
+ PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); |
+@@ -1763,6 +1758,7 @@ |
+ PORT_Assert(ss->ssl3.prSpec == ss->ssl3.pwSpec); |
+ |
+ pwSpec = ss->ssl3.pwSpec; |
++ cwSpec = ss->ssl3.cwSpec; |
+ |
+ if (pms || (!pwSpec->msItem.len && !pwSpec->master_secret)) { |
+ rv = ssl3_DeriveMasterSecret(ss, pms); |
+@@ -1794,7 +1790,32 @@ |
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
+ rv = SECFailure; |
+ } |
++ if (rv != SECSuccess) { |
++ goto done; |
++ } |
+ |
++ /* 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 == PR_UINT16_MAX) { |
++ /* 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; |
++ goto done; |
++ } |
++ /* 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; |
++ |
++ dtls_InitRecvdRecords(&pwSpec->recvdRecords); |
++ } |
++ pwSpec->read_seq_num.low = pwSpec->write_seq_num.low = 0; |
++ |
+ done: |
+ ssl_ReleaseSpecWriteLock(ss); /******************************/ |
+ if (rv != SECSuccess) |
+@@ -1834,6 +1855,7 @@ |
+ ssl3_ComputeRecordMAC( |
+ ssl3CipherSpec * spec, |
+ PRBool useServerMacKey, |
++ PRBool isDTLS, |
+ SSL3ContentType type, |
+ SSL3ProtocolVersion version, |
+ SSL3SequenceNumber seq_num, |
+@@ -1871,8 +1893,16 @@ |
+ 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 +2052,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,10 +2066,12 @@ |
+ PRUint32 macLen = 0; |
+ PRUint32 fragLen; |
+ PRUint32 p1Len, p2Len, oddLen = 0; |
++ PRUint16 headerLen; |
+ int ivLen = 0; |
+ int cipherBytes = 0; |
+ |
+ cipher_def = cwSpec->cipher_def; |
++ headerLen = isDTLS ? DTLS_RECORD_HEADER_LENGTH : SSL3_RECORD_HEADER_LENGTH; |
+ |
+ if (cipher_def->type == type_block && |
+ cwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_1) { |
+@@ -2048,20 +2081,20 @@ |
+ * record. |
+ */ |
+ ivLen = cipher_def->iv_size; |
+- if (ivLen > wrBuf->space - SSL3_RECORD_HEADER_LENGTH) { |
++ if (ivLen > wrBuf->space - headerLen) { |
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
+ return SECFailure; |
+ } |
+- rv = PK11_GenerateRandom(wrBuf->buf + SSL3_RECORD_HEADER_LENGTH, ivLen); |
++ rv = PK11_GenerateRandom(wrBuf->buf + headerLen, 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 + headerLen, |
+ &cipherBytes, /* output and actual outLen */ |
+ ivLen, /* max outlen */ |
+- wrBuf->buf + SSL3_RECORD_HEADER_LENGTH, |
++ wrBuf->buf + headerLen, |
+ ivLen); /* input and inputLen*/ |
+ if (rv != SECSuccess || cipherBytes != ivLen) { |
+ PORT_SetError(SSL_ERROR_ENCRYPTION_FAILURE); |
+@@ -2073,20 +2106,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 + headerLen + ivLen, &outlen, |
++ wrBuf->space - headerLen - ivLen, pIn, contentLen); |
+ if (rv != SECSuccess) |
+ return rv; |
+- pIn = wrBuf->buf + SSL3_RECORD_HEADER_LENGTH + ivLen; |
++ pIn = wrBuf->buf + headerLen + 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 + headerLen + ivLen + contentLen, &macLen); |
+ if (rv != SECSuccess) { |
+ ssl_MapLowLevelError(SSL_ERROR_MAC_COMPUTATION_FAILURE); |
+ return SECFailure; |
+@@ -2113,7 +2146,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[headerLen + ivLen + fragLen - 1]; |
+ for (i = padding_length + 1; i > 0; --i) { |
+ *pBuf-- = padding_length; |
+ } |
+@@ -2130,13 +2163,12 @@ |
+ p2Len += oddLen; |
+ PORT_Assert( (cipher_def->block_size < 2) || \ |
+ (p2Len % cipher_def->block_size) == 0); |
+- memmove(wrBuf->buf + SSL3_RECORD_HEADER_LENGTH + ivLen + p1Len, |
+- pIn + p1Len, oddLen); |
++ memmove(wrBuf->buf + headerLen + 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 + headerLen + ivLen, /* output */ |
+ &cipherBytesPart1, /* actual outlen */ |
+ p1Len, /* max outlen */ |
+ pIn, p1Len); /* input, and inputlen */ |
+@@ -2150,10 +2182,10 @@ |
+ if (p2Len > 0) { |
+ int cipherBytesPart2 = -1; |
+ rv = cwSpec->encode( cwSpec->encodeContext, |
+- wrBuf->buf + SSL3_RECORD_HEADER_LENGTH + ivLen + p1Len, |
++ wrBuf->buf + headerLen + ivLen + p1Len, |
+ &cipherBytesPart2, /* output and actual outLen */ |
+ p2Len, /* max outlen */ |
+- wrBuf->buf + SSL3_RECORD_HEADER_LENGTH + ivLen + p1Len, |
++ wrBuf->buf + headerLen + ivLen + p1Len, |
+ p2Len); /* input and inputLen*/ |
+ PORT_Assert(rv == SECSuccess && cipherBytesPart2 == (int) p2Len); |
+ if (rv != SECSuccess || cipherBytesPart2 != (int) p2Len) { |
+@@ -2164,15 +2196,33 @@ |
+ } |
+ PORT_Assert(cipherBytes <= MAX_FRAGMENT_LENGTH + 1024); |
+ |
++ wrBuf->len = cipherBytes + headerLen; |
++ 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 +2244,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,8 +2322,8 @@ |
+ sslBuffer secondRecord; |
+ |
+ rv = ssl3_CompressMACEncryptRecord(ss->ssl3.cwSpec, |
+- ss->sec.isServer, type, pIn, 1, |
+- wrBuf); |
++ ss->sec.isServer, IS_DTLS(ss), |
++ type, pIn, 1, wrBuf); |
+ if (rv != SECSuccess) |
+ goto spec_locked_loser; |
+ |
+@@ -2282,17 +2335,28 @@ |
+ secondRecord.space = wrBuf->space - wrBuf->len; |
+ |
+ rv = ssl3_CompressMACEncryptRecord(ss->ssl3.cwSpec, |
+- ss->sec.isServer, type, pIn + 1, |
+- contentLen - 1, &secondRecord); |
++ 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]:", |
+ secondRecord.buf, secondRecord.len)); |
+ 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)); |
+@@ -2350,6 +2414,11 @@ |
+ } |
+ wrBuf->len -= sent; |
+ if (wrBuf->len) { |
++ if (IS_DTLS(ss)) { |
++ /* DTLS just says no in this case. No buffering */ |
++ PR_SetError(PR_WOULD_BLOCK_ERROR, 0); |
++ return SECFailure; |
++ } |
+ /* now take all the remaining unsent new ciphertext and |
+ * append it to the buffer of previously unsent ciphertext. |
+ */ |
+@@ -2378,6 +2447,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 +2487,11 @@ |
+ 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 +2526,15 @@ |
+ 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_FlushHandshakeMessages if non-DTLS |
++ * - dtls_FlushHandshakeMessages if DTLS |
++ * |
+ * Called from SSL3_SendAlert(), ssl3_SendChangeCipherSpecs(), |
+ * ssl3_AppendHandshake(), ssl3_SendClientHello(), |
+ * ssl3_SendHelloRequest(), ssl3_SendServerHelloDone(), |
+@@ -2462,6 +2543,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)); |
+@@ -2476,7 +2573,7 @@ |
+ 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 +2690,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 +2764,7 @@ |
+ /* |
+ * Send handshake_Failure alert. Set generic error number. |
+ */ |
+-static SECStatus |
++SECStatus |
+ ssl3_DecodeError(sslSocket *ss) |
+ { |
+ (void)SSL3_SendAlert(ss, alert_fatal, |
+@@ -2755,7 +2852,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); |
+ 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 +2904,22 @@ |
+ 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; |
+@@ -2829,7 +2932,14 @@ |
+ * (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 (!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_MS; |
++ dtls_StartTimer(ss, dtls_FinishedTimerCb); |
++ } |
+ } |
+ ssl_ReleaseSpecWriteLock(ss); /**************************************/ |
+ |
+@@ -2878,7 +2988,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; |
+@@ -2981,6 +3090,11 @@ |
+ 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 +3519,17 @@ |
+ { |
+ SECStatus rv; |
+ |
++ /* If we already have a message in place, we need to enqueue it. |
++ * This empties the buffer. This is a convenient place to call |
++ * dtls_StageHandshakeMessage to mark the message boundary. |
++ */ |
++ 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 +3542,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 +3974,10 @@ |
+ /* 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; |
+@@ -3849,6 +4001,7 @@ |
+ 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. |
+@@ -4008,6 +4161,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 +4184,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 +4194,23 @@ |
+ } |
+ |
+ ss->clientHelloVersion = ss->version; |
+- rv = ssl3_AppendHandshakeNumber(ss, ss->clientHelloVersion, 2); |
++ if (IS_DTLS(ss)) { |
++ PRUint16 version; |
++ |
++ version = dtls_TLSVersionToDTLSVersion(ss->clientHelloVersion); |
++ rv = ssl3_AppendHandshakeNumber(ss, 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 +4227,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 +4358,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; |
+@@ -5036,6 +5218,23 @@ |
+ } |
+ version = (SSL3ProtocolVersion)temp; |
+ |
++ if (IS_DTLS(ss)) { |
++ /* RFC 4347 required that you verify that the server versions |
++ * match (Section 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, |
++ * especially when 1.2 is being negotiated. |
++ * |
++ * Therefore we do not check for matching here. |
++ */ |
++ version = dtls_DTLSVersionToTLSVersion(version); |
++ 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 |
+@@ -6264,6 +6463,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 +6481,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. |
++ */ |
++ if (IS_DTLS(ss)) { |
++ ss->nextHandshake = 0; |
++ ss->securityHandshake = 0; |
++ } |
++ |
+ /* We might be starting session renegotiation in which case we should |
+ * clear previous state. |
+ */ |
+@@ -6306,10 +6520,22 @@ |
+ 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 |
+@@ -6331,6 +6557,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 +6713,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); |
+@@ -7166,17 +7404,28 @@ |
+ 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 +7443,13 @@ |
+ 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 +7634,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. */ |
+ for (i = 0, name = names; i < nnames; i++, name++) { |
+ calen += 2 + name->len; |
+ } |
+@@ -7551,9 +7803,17 @@ |
+ } |
+ |
+ /* 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; |
+ |
+@@ -7635,6 +7895,11 @@ |
+ } else if (ss->opt.detectRollBack) { |
+ SSL3ProtocolVersion client_version = |
+ (rsaPmsBuf[0] << 8) | rsaPmsBuf[1]; |
++ |
++ if (IS_DTLS(ss)) { |
++ client_version = dtls_DTLSVersionToTLSVersion(client_version); |
++ } |
++ |
+ if (client_version != ss->clientHelloVersion) { |
+ /* Version roll-back detected. ensure failure. */ |
+ rv = PK11_GenerateRandom(rsaPmsBuf, sizeof rsaPmsBuf); |
+@@ -8851,6 +9116,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 +9249,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) ); |
+@@ -9032,10 +9302,35 @@ |
+ return rv; |
+ } |
+ } |
+- /* We should not include hello_request messages in the handshake hashes */ |
+- if (ss->ssl3.hs.msg_type != hello_request) { |
++ /* We should not include hello_request and hello_verify_request messages |
++ * in the handshake hashes */ |
++ 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. */ |
+ } |
+@@ -9071,6 +9366,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,6 +9472,12 @@ |
+ 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; |
+ } |
+ |
+@@ -9331,6 +9640,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 +9676,39 @@ |
+ crSpec = ss->ssl3.crSpec; |
+ cipher_def = crSpec->cipher_def; |
+ |
++ /* |
++ * 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 Sections 4.1 and 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)); |
++ /* Silently drop the packet */ |
++ databuf->len = 0; /* Needed to ensure data not left around */ |
++ 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) != 0) { |
++ ssl_ReleaseSpecReadLock(ss); |
++ SSL_DBG(("%d: SSL3[%d]: HandleRecord, rejecting " |
++ "potentially replayed packet", SSL_GETPID(), ss->fd)); |
++ /* Silently drop the packet */ |
++ databuf->len = 0; /* Needed to ensure data not left around */ |
++ 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 |
+@@ -9487,7 +9830,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 +9843,27 @@ |
+ 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); |
++ /* always log mac error, in case attacker can read server logs. */ |
++ PORT_SetError(SSL_ERROR_BAD_MAC_READ); |
++ return SECFailure; |
++ } else { |
++ /* Silently drop the packet */ |
++ databuf->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 +9967,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 +10031,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 +10075,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_New(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; |
+@@ -9968,6 +10342,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 +10361,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 +10421,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); |
+Index: net/third_party/nss/ssl/sslgathr.c |
+=================================================================== |
+--- net/third_party/nss/ssl/sslgathr.c (revision 127709) |
++++ net/third_party/nss/ssl/sslgathr.c (working copy) |
+@@ -434,6 +434,8 @@ |
+ gs->state = GS_INIT; |
+ gs->writeOffset = 0; |
+ gs->readOffset = 0; |
++ gs->dtlsPacketOffset = 0; |
++ gs->dtlsPacket.len = 0; |
+ status = sslBuffer_Grow(&gs->buf, 4096); |
+ return status; |
+ } |
+@@ -445,6 +447,7 @@ |
+ if (gs) { /* the PORT_*Free functions check for NULL pointers. */ |
+ PORT_ZFree(gs->buf.buf, gs->buf.space); |
+ PORT_Free(gs->inbuf.buf); |
++ PORT_Free(gs->dtlsPacket.buf); |
+ } |
+ } |
+ |
+Index: net/third_party/nss/ssl/dtls1con.c |
+=================================================================== |
+--- net/third_party/nss/ssl/dtls1con.c (revision 0) |
++++ net/third_party/nss/ssl/dtls1con.c (revision 0) |
+@@ -0,0 +1,1163 @@ |
++/* |
++ * DTLS Protocol |
++ * |
++ * ***** BEGIN LICENSE BLOCK ***** |
++ * Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
++ * |
++ * The contents of this file are subject to the Mozilla Public License Version |
++ * 1.1 (the "License"); you may not use this file except in compliance with |
++ * the License. You may obtain a copy of the License at |
++ * http://www.mozilla.org/MPL/ |
++ * |
++ * Software distributed under the License is distributed on an "AS IS" basis, |
++ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
++ * for the specific language governing rights and limitations under the |
++ * License. |
++ * |
++ * The Original Code is the Netscape security libraries. |
++ * |
++ * The Initial Developer of the Original Code is |
++ * Netscape Communications Corporation. |
++ * Portions created by the Initial Developer are Copyright (C) 1994-2000 |
++ * the Initial Developer. All Rights Reserved. |
++ * |
++ * Contributor(s): |
++ * Eric Rescorla <ekr@rtfm.com> |
++ * |
++ * Alternatively, the contents of this file may be used under the terms of |
++ * either the GNU General Public License Version 2 or later (the "GPL"), or |
++ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
++ * in which case the provisions of the GPL or the LGPL are applicable instead |
++ * of those above. If you wish to allow use of your version of this file only |
++ * under the terms of either the GPL or the LGPL, and not to allow others to |
++ * use your version of this file under the terms of the MPL, indicate your |
++ * decision by deleting the provisions above and replace them with the notice |
++ * and other provisions required by the GPL or the LGPL. If you do not delete |
++ * the provisions above, a recipient may use your version of this file under |
++ * the terms of any one of the MPL, the GPL or the LGPL. |
++ * |
++ * ***** END LICENSE BLOCK ***** */ |
++/* $Id: $ */ |
++ |
++#include "ssl.h" |
++#include "sslimpl.h" |
++#include "sslproto.h" |
++ |
++#ifndef PR_ARRAY_SIZE |
++#define PR_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) |
++#endif |
++ |
++static SECStatus dtls_TransmitMessageFlight(sslSocket *ss); |
++static void dtls_RetransmitTimerExpiredCb(sslSocket *ss); |
++static SECStatus dtls_SendSavedWriteData(sslSocket *ss); |
++ |
++/* -28 adjusts for the IP/UDP header */ |
++static const PRUint16 COMMON_MTU_VALUES[] = { |
++ 1500 - 28, /* Ethernet MTU */ |
++ 1280 - 28, /* IPv6 minimum MTU */ |
++ 576 - 28, /* Common assumption */ |
++ 256 - 28 /* We're in serious trouble now */ |
++}; |
++ |
++#define DTLS_COOKIE_BYTES 32 |
++ |
++/* List copied from ssl3con.c:cipherSuites */ |
++static const ssl3CipherSuite nonDTLSSuites[] = { |
++#ifdef NSS_ENABLE_ECC |
++ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, |
++ TLS_ECDHE_RSA_WITH_RC4_128_SHA, |
++#endif /* NSS_ENABLE_ECC */ |
++ TLS_DHE_DSS_WITH_RC4_128_SHA, |
++#ifdef NSS_ENABLE_ECC |
++ TLS_ECDH_RSA_WITH_RC4_128_SHA, |
++ TLS_ECDH_ECDSA_WITH_RC4_128_SHA, |
++#endif /* NSS_ENABLE_ECC */ |
++ SSL_RSA_WITH_RC4_128_MD5, |
++ SSL_RSA_WITH_RC4_128_SHA, |
++ TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, |
++ SSL_RSA_EXPORT_WITH_RC4_40_MD5, |
++ 0 /* End of list marker */ |
++}; |
++ |
++/* Map back and forth between TLS and DTLS versions in wire format. |
++ * Mapping table is: |
++ * |
++ * TLS DTLS |
++ * 1.1 (0302) 1.0 (feff) |
++ */ |
++SSL3ProtocolVersion |
++dtls_TLSVersionToDTLSVersion(SSL3ProtocolVersion tlsv) |
++{ |
++ /* Anything other than TLS 1.1 is an error, so return |
++ * the invalid version ffff. */ |
++ if (tlsv != SSL_LIBRARY_VERSION_TLS_1_1) |
++ return 0xffff; |
++ |
++ return SSL_LIBRARY_VERSION_DTLS_1_0_WIRE; |
++} |
++ |
++/* Map known DTLS versions to known TLS versions. |
++ * - Invalid versions (< 1.0) return a version of 0 |
++ * - Versions > known return a version one higher than we know of |
++ * to accomodate a theoretically newer version */ |
++SSL3ProtocolVersion |
++dtls_DTLSVersionToTLSVersion(SSL3ProtocolVersion dtlsv) |
++{ |
++ if (MSB(dtlsv) == 0xff) { |
++ return 0; |
++ } |
++ |
++ if (dtlsv == SSL_LIBRARY_VERSION_DTLS_1_0_WIRE) |
++ return SSL_LIBRARY_VERSION_TLS_1_1; |
++ |
++ /* Return a fictional higher version than we know of */ |
++ return SSL_LIBRARY_VERSION_TLS_1_1 + 1; |
++} |
++ |
++/* On this socket, Disable non-DTLS cipher suites in the argument's list */ |
++SECStatus |
++ssl3_DisableNonDTLSSuites(sslSocket * ss) |
++{ |
++ const ssl3CipherSuite * suite; |
++ |
++ for (suite = nonDTLSSuites; *suite; ++suite) { |
++ SECStatus rv = ssl3_CipherPrefSet(ss, *suite, PR_FALSE); |
++ |
++ PORT_Assert(rv == SECSuccess); /* else is coding error */ |
++ } |
++ return SECSuccess; |
++} |
++ |
++/* Allocate a DTLSQueuedMessage. |
++ * |
++ * Called from dtls_QueueMessage() |
++ */ |
++static DTLSQueuedMessage * |
++dtls_AllocQueuedMessage(PRUint16 epoch, SSL3ContentType type, |
++ const unsigned char *data, PRUint32 len) |
++{ |
++ DTLSQueuedMessage *msg = NULL; |
++ |
++ msg = PORT_ZAlloc(sizeof(DTLSQueuedMessage)); |
++ if (!msg) |
++ return NULL; |
++ |
++ msg->data = PORT_Alloc(len); |
++ if (!msg->data) { |
++ PORT_Free(msg); |
++ return NULL; |
++ } |
++ PORT_Memcpy(msg->data, data, len); |
++ |
++ msg->len = len; |
++ msg->epoch = epoch; |
++ msg->type = type; |
++ |
++ return msg; |
++} |
++ |
++/* |
++ * Free a handshake message |
++ * |
++ * Called from dtls_FreeHandshakeMessages() |
++ */ |
++static void |
++dtls_FreeHandshakeMessage(DTLSQueuedMessage *msg) |
++{ |
++ if (!msg) |
++ return; |
++ |
++ PORT_ZFree(msg->data, msg->len); |
++ PORT_Free(msg); |
++} |
++ |
++/* |
++ * Free a list of handshake messages |
++ * |
++ * Called from: |
++ * dtls_HandleHandshake() |
++ * ssl3_DestroySSL3Info() |
++ */ |
++void |
++dtls_FreeHandshakeMessages(PRCList *list) |
++{ |
++ PRCList *cur_p; |
++ |
++ while (!PR_CLIST_IS_EMPTY(list)) { |
++ cur_p = PR_LIST_TAIL(list); |
++ PR_REMOVE_LINK(cur_p); |
++ dtls_FreeHandshakeMessage((DTLSQueuedMessage *)cur_p); |
++ } |
++} |
++ |
++/* Called only from ssl3_HandleRecord, for each (deciphered) DTLS record. |
++ * origBuf is the decrypted ssl record content and is expected to contain |
++ * complete handshake records |
++ * Caller must hold the handshake and RecvBuf locks. |
++ * |
++ * Note that this code uses msg_len for two purposes: |
++ * |
++ * (1) To pass the length to ssl3_HandleHandshakeMessage() |
++ * (2) To carry the length of a message currently being reassembled |
++ * |
++ * However, unlike ssl3_HandleHandshake(), it is not used to carry |
++ * the state of reassembly (i.e., whether one is in progress). That |
++ * is carried in recvdHighWater and recvdFragments. |
++ */ |
++#define OFFSET_BYTE(o) (o/8) |
++#define OFFSET_MASK(o) (1 << (o%8)) |
++ |
++SECStatus |
++dtls_HandleHandshake(sslSocket *ss, sslBuffer *origBuf) |
++{ |
++ /* XXX OK for now. |
++ * This doesn't work properly with asynchronous certificate validation. |
++ * because that returns a WOULDBLOCK error. The current DTLS |
++ * applications do not need asynchronous validation, but in the |
++ * future we will need to add this. |
++ */ |
++ sslBuffer buf = *origBuf; |
++ SECStatus rv = SECSuccess; |
++ |
++ PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); |
++ PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); |
++ |
++ while (buf.len > 0) { |
++ PRUint8 type; |
++ PRUint32 message_length; |
++ PRUint16 message_seq; |
++ PRUint32 fragment_offset; |
++ PRUint32 fragment_length; |
++ PRUint32 offset; |
++ |
++ if (buf.len < 12) { |
++ PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE); |
++ rv = SECFailure; |
++ break; |
++ } |
++ |
++ /* Parse the header */ |
++ type = buf.buf[0]; |
++ message_length = (buf.buf[1] << 16) | (buf.buf[2] << 8) | buf.buf[3]; |
++ message_seq = (buf.buf[4] << 8) | buf.buf[5]; |
++ fragment_offset = (buf.buf[6] << 16) | (buf.buf[7] << 8) | buf.buf[8]; |
++ fragment_length = (buf.buf[9] << 16) | (buf.buf[10] << 8) | buf.buf[11]; |
++ |
++#define MAX_HANDSHAKE_MSG_LEN 0x1ffff /* 128k - 1 */ |
++ if (message_length > MAX_HANDSHAKE_MSG_LEN) { |
++ (void)ssl3_DecodeError(ss); |
++ PORT_SetError(SSL_ERROR_RX_RECORD_TOO_LONG); |
++ return SECFailure; |
++ } |
++#undef MAX_HANDSHAKE_MSG_LEN |
++ |
++ buf.buf += 12; |
++ buf.len -= 12; |
++ |
++ /* This fragment must be complete */ |
++ if (buf.len < fragment_length) { |
++ PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE); |
++ rv = SECFailure; |
++ break; |
++ } |
++ |
++ /* Sanity check the packet contents */ |
++ if ((fragment_length + fragment_offset) > message_length) { |
++ PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE); |
++ rv = SECFailure; |
++ break; |
++ } |
++ |
++ /* There are three ways we could not be ready for this packet. |
++ * |
++ * 1. It's a partial next message. |
++ * 2. It's a partial or complete message beyond the next |
++ * 3. It's a message we've already seen |
++ * |
++ * If it's the complete next message we accept it right away. |
++ * This is the common case for short messages |
++ */ |
++ if ((message_seq == ss->ssl3.hs.recvMessageSeq) |
++ && (fragment_offset == 0) |
++ && (fragment_length == message_length)) { |
++ /* Complete next message. Process immediately */ |
++ ss->ssl3.hs.msg_type = (SSL3HandshakeType)type; |
++ ss->ssl3.hs.msg_len = message_length; |
++ |
++ /* At this point we are advancing our state machine, so |
++ * we can free our last flight of messages */ |
++ dtls_FreeHandshakeMessages(ss->ssl3.hs.lastMessageFlight); |
++ ss->ssl3.hs.recvdHighWater = -1; |
++ dtls_CancelTimer(ss); |
++ |
++ /* Reset the timer to the initial value if the retry counter |
++ * is 0, per Sec. 4.2.4.1 */ |
++ if (ss->ssl3.hs.rtRetries == 0) { |
++ ss->ssl3.hs.rtTimeoutMs = INITIAL_DTLS_TIMEOUT_MS; |
++ } |
++ |
++ rv = ssl3_HandleHandshakeMessage(ss, buf.buf, ss->ssl3.hs.msg_len); |
++ if (rv == SECFailure) { |
++ /* Do not attempt to process rest of messages in this record */ |
++ break; |
++ } |
++ } else { |
++ if (message_seq < ss->ssl3.hs.recvMessageSeq) { |
++ /* Case 3: we do an immediate retransmit if we're |
++ * in a waiting state*/ |
++ if (ss->ssl3.hs.rtTimerCb == NULL) { |
++ /* Ignore */ |
++ } else if (ss->ssl3.hs.rtTimerCb == |
++ dtls_RetransmitTimerExpiredCb) { |
++ SSL_TRC(30, ("%d: SSL3[%d]: Retransmit detected", |
++ SSL_GETPID(), ss->fd)); |
++ /* Check to see if we retransmitted recently. If so, |
++ * suppress the triggered retransmit. This avoids |
++ * retransmit wars after packet loss. |
++ * This is not in RFC 5346 but should be |
++ */ |
++ if ((PR_IntervalNow() - ss->ssl3.hs.rtTimerStarted) > |
++ (ss->ssl3.hs.rtTimeoutMs / 4)) { |
++ SSL_TRC(30, |
++ ("%d: SSL3[%d]: Shortcutting retransmit timer", |
++ SSL_GETPID(), ss->fd)); |
++ |
++ /* Cancel the timer and call the CB, |
++ * which re-arms the timer */ |
++ dtls_CancelTimer(ss); |
++ dtls_RetransmitTimerExpiredCb(ss); |
++ rv = SECSuccess; |
++ break; |
++ } else { |
++ SSL_TRC(30, |
++ ("%d: SSL3[%d]: We just retransmitted. Ignoring.", |
++ SSL_GETPID(), ss->fd)); |
++ rv = SECSuccess; |
++ break; |
++ } |
++ } else if (ss->ssl3.hs.rtTimerCb == dtls_FinishedTimerCb) { |
++ /* Retransmit the messages and re-arm the timer |
++ * Note that we are not backing off the timer here. |
++ * The spec isn't clear and my reasoning is that this |
++ * may be a re-ordered packet rather than slowness, |
++ * so let's be aggressive. */ |
++ dtls_CancelTimer(ss); |
++ rv = dtls_TransmitMessageFlight(ss); |
++ if (rv == SECSuccess) { |
++ rv = dtls_StartTimer(ss, dtls_FinishedTimerCb); |
++ } |
++ if (rv != SECSuccess) |
++ return rv; |
++ break; |
++ } |
++ } else if (message_seq > ss->ssl3.hs.recvMessageSeq) { |
++ /* Case 2 |
++ * |
++ * Ignore this message. This means we don't handle out of |
++ * order complete messages that well, but we're still |
++ * compliant and this probably does not happen often |
++ * |
++ * XXX OK for now. Maybe do something smarter at some point? |
++ */ |
++ } else { |
++ /* Case 1 |
++ * |
++ * Buffer the fragment for reassembly |
++ */ |
++ /* Make room for the message */ |
++ if (ss->ssl3.hs.recvdHighWater == -1) { |
++ PRUint32 map_length = OFFSET_BYTE(message_length) + 1; |
++ |
++ rv = sslBuffer_Grow(&ss->ssl3.hs.msg_body, message_length); |
++ if (rv != SECSuccess) |
++ break; |
++ /* Make room for the fragment map */ |
++ rv = sslBuffer_Grow(&ss->ssl3.hs.recvdFragments, |
++ map_length); |
++ if (rv != SECSuccess) |
++ break; |
++ |
++ /* Reset the reassembly map */ |
++ ss->ssl3.hs.recvdHighWater = 0; |
++ PORT_Memset(ss->ssl3.hs.recvdFragments.buf, 0, |
++ ss->ssl3.hs.recvdFragments.space); |
++ ss->ssl3.hs.msg_type = (SSL3HandshakeType)type; |
++ ss->ssl3.hs.msg_len = message_length; |
++ } |
++ |
++ /* If we have a message length mismatch, abandon the reassembly |
++ * in progress and hope that the next retransmit will give us |
++ * something sane |
++ */ |
++ if (message_length != ss->ssl3.hs.msg_len) { |
++ ss->ssl3.hs.recvdHighWater = -1; |
++ PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE); |
++ rv = SECFailure; |
++ break; |
++ } |
++ |
++ /* Now copy this fragment into the buffer */ |
++ PORT_Assert((fragment_offset + fragment_length) <= |
++ ss->ssl3.hs.msg_body.space); |
++ PORT_Memcpy(ss->ssl3.hs.msg_body.buf + fragment_offset, |
++ buf.buf, fragment_length); |
++ |
++ /* This logic is a bit tricky. We have two values for |
++ * reassembly state: |
++ * |
++ * - recvdHighWater contains the highest contiguous number of |
++ * bytes received |
++ * - recvdFragments contains a bitmask of packets received |
++ * above recvdHighWater |
++ * |
++ * This avoids having to fill in the bitmask in the common |
++ * case of adjacent fragments received in sequence |
++ */ |
++ if (fragment_offset <= ss->ssl3.hs.recvdHighWater) { |
++ /* Either this is the adjacent fragment or an overlapping |
++ * fragment */ |
++ ss->ssl3.hs.recvdHighWater = fragment_offset + |
++ fragment_length; |
++ } else { |
++ for (offset = fragment_offset; |
++ offset < fragment_offset + fragment_length; |
++ offset++) { |
++ ss->ssl3.hs.recvdFragments.buf[OFFSET_BYTE(offset)] |= |
++ OFFSET_MASK(offset); |
++ } |
++ } |
++ |
++ /* Now figure out the new high water mark if appropriate */ |
++ for (offset = ss->ssl3.hs.recvdHighWater; |
++ offset < ss->ssl3.hs.msg_len; offset++) { |
++ /* Note that this loop is not efficient, since it counts |
++ * bit by bit. If we have a lot of out-of-order packets, |
++ * we should optimize this */ |
++ if (ss->ssl3.hs.recvdFragments.buf[OFFSET_BYTE(offset)] & |
++ OFFSET_MASK(offset)) { |
++ ss->ssl3.hs.recvdHighWater++; |
++ } else { |
++ break; |
++ } |
++ } |
++ |
++ /* If we have all the bytes, then we are good to go */ |
++ if (ss->ssl3.hs.recvdHighWater == ss->ssl3.hs.msg_len) { |
++ ss->ssl3.hs.recvdHighWater = -1; |
++ |
++ rv = ssl3_HandleHandshakeMessage(ss, |
++ ss->ssl3.hs.msg_body.buf, |
++ ss->ssl3.hs.msg_len); |
++ if (rv == SECFailure) |
++ break; /* Skip rest of record */ |
++ |
++ /* At this point we are advancing our state machine, so |
++ * we can free our last flight of messages */ |
++ dtls_FreeHandshakeMessages(ss->ssl3.hs.lastMessageFlight); |
++ dtls_CancelTimer(ss); |
++ |
++ /* If there have been no retries this time, reset the |
++ * timer value to the default per Section 4.2.4.1 */ |
++ if (ss->ssl3.hs.rtRetries == 0) { |
++ ss->ssl3.hs.rtTimeoutMs = INITIAL_DTLS_TIMEOUT_MS; |
++ } |
++ } |
++ } |
++ } |
++ |
++ buf.buf += fragment_length; |
++ buf.len -= fragment_length; |
++ } |
++ |
++ origBuf->len = 0; /* So ssl3_GatherAppDataRecord will keep looping. */ |
++ |
++ /* XXX OK for now. In future handle rv == SECWouldBlock safely in order |
++ * to deal with asynchronous certificate verification */ |
++ return rv; |
++} |
++ |
++/* Enqueue a message (either handshake or CCS) |
++ * |
++ * Called from: |
++ * dtls_StageHandshakeMessage() |
++ * ssl3_SendChangeCipherSpecs() |
++ */ |
++SECStatus dtls_QueueMessage(sslSocket *ss, SSL3ContentType type, |
++ const SSL3Opaque *pIn, PRInt32 nIn) |
++{ |
++ SECStatus rv = SECSuccess; |
++ DTLSQueuedMessage *msg = NULL; |
++ |
++ PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); |
++ PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); |
++ |
++ msg = dtls_AllocQueuedMessage(ss->ssl3.cwSpec->epoch, type, pIn, nIn); |
++ |
++ if (!msg) { |
++ PORT_SetError(SEC_ERROR_NO_MEMORY); |
++ rv = SECFailure; |
++ } else { |
++ PR_APPEND_LINK(&msg->link, ss->ssl3.hs.lastMessageFlight); |
++ } |
++ |
++ return rv; |
++} |
++ |
++/* Add DTLS handshake message to the pending queue |
++ * Empty the sendBuf buffer. |
++ * This function returns SECSuccess or SECFailure, never SECWouldBlock. |
++ * Always set sendBuf.len to 0, even when returning SECFailure. |
++ * |
++ * Called from: |
++ * ssl3_AppendHandshakeHeader() |
++ * dtls_FlushHandshake() |
++ */ |
++SECStatus |
++dtls_StageHandshakeMessage(sslSocket *ss) |
++{ |
++ SECStatus rv = SECSuccess; |
++ |
++ PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); |
++ PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); |
++ |
++ /* This function is sometimes called when no data is actually to |
++ * be staged, so just return SECSuccess. */ |
++ if (!ss->sec.ci.sendBuf.buf || !ss->sec.ci.sendBuf.len) |
++ return rv; |
++ |
++ rv = dtls_QueueMessage(ss, content_handshake, |
++ ss->sec.ci.sendBuf.buf, ss->sec.ci.sendBuf.len); |
++ |
++ /* Whether we succeeded or failed, toss the old handshake data. */ |
++ ss->sec.ci.sendBuf.len = 0; |
++ return rv; |
++} |
++ |
++/* Enqueue the handshake message in sendBuf (if any) and then |
++ * transmit the resulting flight of handshake messages. |
++ * |
++ * Called from: |
++ * ssl3_FlushHandshake() |
++ */ |
++SECStatus |
++dtls_FlushHandshakeMessages(sslSocket *ss, PRInt32 flags) |
++{ |
++ SECStatus rv = SECSuccess; |
++ |
++ PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); |
++ PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); |
++ |
++ rv = dtls_StageHandshakeMessage(ss); |
++ if (rv != SECSuccess) |
++ return rv; |
++ |
++ if (!(flags & ssl_SEND_FLAG_FORCE_INTO_BUFFER)) { |
++ rv = dtls_TransmitMessageFlight(ss); |
++ if (rv != SECSuccess) |
++ return rv; |
++ |
++ if (!(flags & ssl_SEND_FLAG_NO_RETRANSMIT)) { |
++ ss->ssl3.hs.rtRetries = 0; |
++ rv = dtls_StartTimer(ss, dtls_RetransmitTimerExpiredCb); |
++ } |
++ } |
++ |
++ return rv; |
++} |
++ |
++/* The callback for when the retransmit timer expires |
++ * |
++ * Called from: |
++ * dtls_CheckTimer() |
++ * dtls_HandleHandshake() |
++ */ |
++static void |
++dtls_RetransmitTimerExpiredCb(sslSocket *ss) |
++{ |
++ SECStatus rv = SECFailure; |
++ |
++ ss->ssl3.hs.rtRetries++; |
++ |
++ if (!(ss->ssl3.hs.rtRetries % 3)) { |
++ /* If one of the messages was potentially greater than > MTU, |
++ * then downgrade. Do this every time we have retransmitted a |
++ * message twice, per RFC 6347 Sec. 4.1.1 */ |
++ dtls_SetMTU(ss, ss->ssl3.hs.maxMessageSent - 1); |
++ } |
++ |
++ rv = dtls_TransmitMessageFlight(ss); |
++ if (rv == SECSuccess) { |
++ |
++ /* Re-arm the timer */ |
++ rv = dtls_RestartTimer(ss, PR_TRUE, dtls_RetransmitTimerExpiredCb); |
++ } |
++ |
++ if (rv == SECFailure) { |
++ /* XXX OK for now. In future maybe signal the stack that we couldn't |
++ * transmit. For now, let the read handle any real network errors */ |
++ } |
++} |
++ |
++/* Transmit a flight of handshake messages, stuffing them |
++ * into as few records as seems reasonable |
++ * |
++ * Called from: |
++ * dtls_FlushHandshake() |
++ * dtls_RetransmitTimerExpiredCb() |
++ */ |
++static SECStatus |
++dtls_TransmitMessageFlight(sslSocket *ss) |
++{ |
++ SECStatus rv = SECSuccess; |
++ PRCList *msg_p; |
++ PRUint16 room_left = ss->ssl3.mtu; |
++ PRInt32 sent; |
++ |
++ ssl_GetXmitBufLock(ss); |
++ ssl_GetSpecReadLock(ss); |
++ |
++ /* DTLS does not buffer its handshake messages in |
++ * ss->pendingBuf, but rather in the lastMessageFlight |
++ * structure. This is just a sanity check that |
++ * some programming error hasn't inadvertantly |
++ * stuffed something in ss->pendingBuf |
++ */ |
++ PORT_Assert(!ss->pendingBuf.len); |
++ for (msg_p = PR_LIST_HEAD(ss->ssl3.hs.lastMessageFlight); |
++ msg_p != ss->ssl3.hs.lastMessageFlight; |
++ msg_p = PR_NEXT_LINK(msg_p)) { |
++ DTLSQueuedMessage *msg = (DTLSQueuedMessage *)msg_p; |
++ |
++ /* The logic here is: |
++ * |
++ * 1. If this is a message that will not fit into the remaining |
++ * space, then flush. |
++ * 2. If the message will now fit into the remaining space, |
++ * encrypt, buffer, and loop. |
++ * 3. If the message will not fit, then fragment. |
++ * |
++ * At the end of the function, flush. |
++ */ |
++ if ((msg->len + SSL3_BUFFER_FUDGE) > room_left) { |
++ /* The message will not fit into the remaining space, so flush */ |
++ rv = dtls_SendSavedWriteData(ss); |
++ if (rv != SECSuccess) |
++ break; |
++ |
++ room_left = ss->ssl3.mtu; |
++ } |
++ |
++ if ((msg->len + SSL3_BUFFER_FUDGE) <= room_left) { |
++ /* The message will fit, so encrypt and then continue with the |
++ * next packet */ |
++ sent = ssl3_SendRecord(ss, msg->epoch, msg->type, |
++ msg->data, msg->len, |
++ ssl_SEND_FLAG_FORCE_INTO_BUFFER | |
++ ssl_SEND_FLAG_USE_EPOCH); |
++ if (sent != msg->len) { |
++ rv = SECFailure; |
++ if (sent != -1) { |
++ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
++ } |
++ break; |
++ } |
++ |
++ room_left = ss->ssl3.mtu - ss->pendingBuf.len; |
++ } else { |
++ /* The message will not fit, so fragment. |
++ * |
++ * XXX OK for now. Arrange to coalesce the last fragment |
++ * of this message with the next message if possible. |
++ * That would be more efficient. |
++ */ |
++ PRUint32 fragment_offset = 0; |
++ unsigned char fragment[DTLS_MAX_MTU]; /* >= than largest |
++ * plausible MTU */ |
++ |
++ /* Assert that we have already flushed */ |
++ PORT_Assert(room_left == ss->ssl3.mtu); |
++ |
++ /* Case 3: We now need to fragment this message |
++ * DTLS only supports fragmenting handshaking messages */ |
++ PORT_Assert(msg->type == content_handshake); |
++ |
++ /* The headers consume 12 bytes so the smalles possible |
++ * message (i.e., an empty one) is 12 bytes |
++ */ |
++ PORT_Assert(msg->len >= 12); |
++ |
++ while ((fragment_offset + 12) < msg->len) { |
++ PRUint32 fragment_len; |
++ const unsigned char *content = msg->data + 12; |
++ PRUint32 content_len = msg->len - 12; |
++ |
++ /* The reason we use 8 here is that that's the length of |
++ * the new DTLS data that we add to the header */ |
++ fragment_len = PR_MIN(room_left - (SSL3_BUFFER_FUDGE + 8), |
++ content_len - fragment_offset); |
++ PORT_Assert(fragment_len < DTLS_MAX_MTU - 12); |
++ /* Make totally sure that we are within the buffer. |
++ * Note that the only way that fragment len could get |
++ * adjusted here is if |
++ * |
++ * (a) we are in release mode so the PORT_Assert is compiled out |
++ * (b) either the MTU table is inconsistent with DTLS_MAX_MTU |
++ * or ss->ssl3.mtu has become corrupt. |
++ */ |
++ fragment_len = PR_MIN(fragment_len, DTLS_MAX_MTU - 12); |
++ |
++ /* Construct an appropriate-sized fragment */ |
++ /* Type, length, sequence */ |
++ PORT_Memcpy(fragment, msg->data, 6); |
++ |
++ /* Offset */ |
++ fragment[6] = (fragment_offset >> 16) & 0xff; |
++ fragment[7] = (fragment_offset >> 8) & 0xff; |
++ fragment[8] = (fragment_offset) & 0xff; |
++ |
++ /* Fragment length */ |
++ fragment[9] = (fragment_len >> 16) & 0xff; |
++ fragment[10] = (fragment_len >> 8) & 0xff; |
++ fragment[11] = (fragment_len) & 0xff; |
++ |
++ PORT_Memcpy(fragment + 12, content + fragment_offset, |
++ fragment_len); |
++ |
++ /* |
++ * Send the record. We do this in two stages |
++ * 1. Encrypt |
++ */ |
++ sent = ssl3_SendRecord(ss, msg->epoch, msg->type, |
++ fragment, fragment_len + 12, |
++ ssl_SEND_FLAG_FORCE_INTO_BUFFER | |
++ ssl_SEND_FLAG_USE_EPOCH); |
++ if (sent != (fragment_len + 12)) { |
++ rv = SECFailure; |
++ if (sent != -1) { |
++ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
++ } |
++ break; |
++ } |
++ |
++ /* 2. Flush */ |
++ rv = dtls_SendSavedWriteData(ss); |
++ if (rv != SECSuccess) |
++ break; |
++ |
++ fragment_offset += fragment_len; |
++ } |
++ } |
++ } |
++ |
++ /* Finally, we need to flush */ |
++ if (rv == SECSuccess) |
++ rv = dtls_SendSavedWriteData(ss); |
++ |
++ /* Give up the locks */ |
++ ssl_ReleaseSpecReadLock(ss); |
++ ssl_ReleaseXmitBufLock(ss); |
++ |
++ return rv; |
++} |
++ |
++/* Flush the data in the pendingBuf and update the max message sent |
++ * so we can adjust the MTU estimate if we need to. |
++ * Wrapper for ssl_SendSavedWriteData. |
++ * |
++ * Called from dtls_TransmitMessageFlight() |
++ */ |
++static |
++SECStatus dtls_SendSavedWriteData(sslSocket *ss) |
++{ |
++ PRInt32 sent; |
++ |
++ sent = ssl_SendSavedWriteData(ss); |
++ if (sent < 0) |
++ return SECFailure; |
++ |
++ /* We should always have complete writes b/c datagram sockets |
++ * don't really block */ |
++ if (ss->pendingBuf.len > 0) { |
++ ssl_MapLowLevelError(SSL_ERROR_SOCKET_WRITE_FAILURE); |
++ return SECFailure; |
++ } |
++ |
++ /* Update the largest message sent so we can adjust the MTU |
++ * estimate if necessary */ |
++ if (sent > ss->ssl3.hs.maxMessageSent) |
++ ss->ssl3.hs.maxMessageSent = sent; |
++ |
++ return SECSuccess; |
++} |
++ |
++/* Compress, MAC, encrypt a DTLS record. Allows specification of |
++ * the epoch using epoch value. If use_epoch is PR_TRUE then |
++ * we use the provided epoch. If use_epoch is PR_FALSE then |
++ * whatever the current value is in effect is used. |
++ * |
++ * Called from ssl3_SendRecord() |
++ */ |
++SECStatus |
++dtls_CompressMACEncryptRecord(sslSocket * ss, |
++ DTLSEpoch epoch, |
++ PRBool use_epoch, |
++ SSL3ContentType type, |
++ const SSL3Opaque * pIn, |
++ PRUint32 contentLen, |
++ sslBuffer * wrBuf) |
++{ |
++ SECStatus rv = SECFailure; |
++ ssl3CipherSpec * cwSpec; |
++ |
++ ssl_GetSpecReadLock(ss); /********************************/ |
++ |
++ /* The reason for this switch-hitting code is that we might have |
++ * a flight of records spanning an epoch boundary, e.g., |
++ * |
++ * ClientKeyExchange (epoch = 0) |
++ * ChangeCipherSpec (epoch = 0) |
++ * Finished (epoch = 1) |
++ * |
++ * Thus, each record needs a different cipher spec. The information |
++ * about which epoch to use is carried with the record. |
++ */ |
++ if (use_epoch) { |
++ if (ss->ssl3.cwSpec->epoch == epoch) |
++ cwSpec = ss->ssl3.cwSpec; |
++ else if (ss->ssl3.pwSpec->epoch == epoch) |
++ cwSpec = ss->ssl3.pwSpec; |
++ else |
++ cwSpec = NULL; |
++ } else { |
++ cwSpec = ss->ssl3.cwSpec; |
++ } |
++ |
++ if (cwSpec) { |
++ rv = ssl3_CompressMACEncryptRecord(cwSpec, ss->sec.isServer, PR_TRUE, |
++ type, pIn, contentLen, wrBuf); |
++ } else { |
++ PR_NOT_REACHED("Couldn't find a cipher spec matching epoch"); |
++ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
++ } |
++ ssl_ReleaseSpecReadLock(ss); /************************************/ |
++ |
++ return rv; |
++} |
++ |
++/* Start a timer |
++ * |
++ * Called from: |
++ * dtls_HandleHandshake() |
++ * dtls_FlushHAndshake() |
++ * dtls_RestartTimer() |
++ */ |
++SECStatus |
++dtls_StartTimer(sslSocket *ss, DTLSTimerCb cb) |
++{ |
++ PORT_Assert(ss->ssl3.hs.rtTimerCb == NULL); |
++ |
++ ss->ssl3.hs.rtTimerStarted = PR_IntervalNow(); |
++ ss->ssl3.hs.rtTimerCb = cb; |
++ |
++ return SECSuccess; |
++} |
++ |
++/* Restart a timer with optional backoff |
++ * |
++ * Called from dtls_RetransmitTimerExpiredCb() |
++ */ |
++SECStatus |
++dtls_RestartTimer(sslSocket *ss, PRBool backoff, DTLSTimerCb cb) |
++{ |
++ if (backoff) { |
++ ss->ssl3.hs.rtTimeoutMs *= 2; |
++ if (ss->ssl3.hs.rtTimeoutMs > MAX_DTLS_TIMEOUT_MS) |
++ ss->ssl3.hs.rtTimeoutMs = MAX_DTLS_TIMEOUT_MS; |
++ } |
++ |
++ return dtls_StartTimer(ss, cb); |
++} |
++ |
++/* Cancel a pending timer |
++ * |
++ * Called from: |
++ * dtls_HandleHandshake() |
++ * dtls_CheckTimer() |
++ */ |
++void |
++dtls_CancelTimer(sslSocket *ss) |
++{ |
++ PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); |
++ |
++ ss->ssl3.hs.rtTimerCb = NULL; |
++} |
++ |
++/* Check the pending timer and fire the callback if it expired |
++ * |
++ * Called from ssl3_GatherCompleteHandshake() |
++ */ |
++void |
++dtls_CheckTimer(sslSocket *ss) |
++{ |
++ if (!ss->ssl3.hs.rtTimerCb) |
++ return; |
++ |
++ if ((PR_IntervalNow() - ss->ssl3.hs.rtTimerStarted) > |
++ PR_MillisecondsToInterval(ss->ssl3.hs.rtTimeoutMs)) { |
++ /* Timer has expired */ |
++ DTLSTimerCb cb = ss->ssl3.hs.rtTimerCb; |
++ |
++ /* Cancel the timer so that we can call the CB safely */ |
++ dtls_CancelTimer(ss); |
++ |
++ /* Now call the CB */ |
++ cb(ss); |
++ } |
++} |
++ |
++/* The callback to fire when the holddown timer for the Finished |
++ * message expires and we can delete it |
++ * |
++ * Called from dtls_CheckTimer() |
++ */ |
++void |
++dtls_FinishedTimerCb(sslSocket *ss) |
++{ |
++ ssl3_DestroyCipherSpec(ss->ssl3.pwSpec, PR_FALSE); |
++} |
++ |
++/* Cancel the Finished hold-down timer and destroy the |
++ * pending cipher spec. Note that this means that |
++ * successive rehandshakes will fail if the Finished is |
++ * lost. |
++ * |
++ * XXX OK for now. Figure out how to handle the combination |
++ * of Finished lost and rehandshake |
++ */ |
++void |
++dtls_RehandshakeCleanup(sslSocket *ss) |
++{ |
++ dtls_CancelTimer(ss); |
++ ssl3_DestroyCipherSpec(ss->ssl3.pwSpec, PR_FALSE); |
++ ss->ssl3.hs.sendMessageSeq = 0; |
++} |
++ |
++/* Set the MTU to the next step less than or equal to the |
++ * advertised value. Also used to downgrade the MTU by |
++ * doing dtls_SetMTU(ss, biggest packet set). |
++ * |
++ * Passing 0 means set this to the largest MTU known |
++ * (effectively resetting the PMTU backoff value). |
++ * |
++ * Called by: |
++ * ssl3_InitState() |
++ * dtls_RetransmitTimerExpiredCb() |
++ */ |
++void |
++dtls_SetMTU(sslSocket *ss, PRUint16 advertised) |
++{ |
++ int i; |
++ |
++ if (advertised == 0) { |
++ ss->ssl3.mtu = COMMON_MTU_VALUES[0]; |
++ SSL_TRC(30, ("Resetting MTU to %d", ss->ssl3.mtu)); |
++ return; |
++ } |
++ |
++ for (i = 0; i < PR_ARRAY_SIZE(COMMON_MTU_VALUES); i++) { |
++ if (COMMON_MTU_VALUES[i] <= advertised) { |
++ ss->ssl3.mtu = COMMON_MTU_VALUES[i]; |
++ SSL_TRC(30, ("Resetting MTU to %d", ss->ssl3.mtu)); |
++ return; |
++ } |
++ } |
++ |
++ /* Fallback */ |
++ ss->ssl3.mtu = COMMON_MTU_VALUES[PR_ARRAY_SIZE(COMMON_MTU_VALUES)-1]; |
++ SSL_TRC(30, ("Resetting MTU to %d", ss->ssl3.mtu)); |
++} |
++ |
++/* Called from ssl3_HandleHandshakeMessage() when it has deciphered a |
++ * DTLS hello_verify_request |
++ * Caller must hold Handshake and RecvBuf locks. |
++ */ |
++SECStatus |
++dtls_HandleHelloVerifyRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length) |
++{ |
++ int errCode = SSL_ERROR_RX_MALFORMED_HELLO_VERIFY_REQUEST; |
++ SECStatus rv; |
++ PRInt32 temp; |
++ SECItem cookie = {siBuffer, NULL, 0}; |
++ SSL3AlertDescription desc = illegal_parameter; |
++ |
++ SSL_TRC(3, ("%d: SSL3[%d]: handle hello_verify_request handshake", |
++ SSL_GETPID(), ss->fd)); |
++ PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); |
++ PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); |
++ |
++ if (ss->ssl3.hs.ws != wait_server_hello) { |
++ errCode = SSL_ERROR_RX_UNEXPECTED_HELLO_VERIFY_REQUEST; |
++ desc = unexpected_message; |
++ goto alert_loser; |
++ } |
++ |
++ /* The version */ |
++ temp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length); |
++ if (temp < 0) { |
++ goto loser; /* alert has been sent */ |
++ } |
++ |
++ if (temp != SSL_LIBRARY_VERSION_DTLS_1_0_WIRE) { |
++ /* Note: this will need adjustment for DTLS 1.2 per Section 4.2.1 */ |
++ goto alert_loser; |
++ } |
++ |
++ /* The cookie */ |
++ rv = ssl3_ConsumeHandshakeVariable(ss, &cookie, 1, &b, &length); |
++ if (rv != SECSuccess) { |
++ goto loser; /* alert has been sent */ |
++ } |
++ if (cookie.len > DTLS_COOKIE_BYTES) { |
++ desc = decode_error; |
++ goto alert_loser; /* malformed. */ |
++ } |
++ |
++ PORT_Memcpy(ss->ssl3.hs.cookie, cookie.data, cookie.len); |
++ ss->ssl3.hs.cookieLen = cookie.len; |
++ |
++ |
++ ssl_GetXmitBufLock(ss); /*******************************/ |
++ |
++ /* Now re-send the client hello */ |
++ rv = ssl3_SendClientHello(ss, PR_TRUE); |
++ |
++ ssl_ReleaseXmitBufLock(ss); /*******************************/ |
++ |
++ if (rv == SECSuccess) |
++ return rv; |
++ |
++alert_loser: |
++ (void)SSL3_SendAlert(ss, alert_fatal, desc); |
++ |
++loser: |
++ errCode = ssl_MapLowLevelError(errCode); |
++ return SECFailure; |
++} |
++ |
++/* Initialize the DTLS anti-replay window |
++ * |
++ * Called from: |
++ * ssl3_SetupPendingCipherSpec() |
++ * ssl3_InitCipherSpec() |
++ */ |
++void |
++dtls_InitRecvdRecords(DTLSRecvdRecords *records) |
++{ |
++ PORT_Memset(records->data, 0, sizeof(records->data)); |
++ records->left = 0; |
++ records->right = DTLS_RECVD_RECORDS_WINDOW - 1; |
++} |
++ |
++/* |
++ * Has this DTLS record been received? Return values are: |
++ * -1 -- out of range to the left |
++ * 0 -- not received yet |
++ * 1 -- replay |
++ * |
++ * Called from: dtls_HandleRecord() |
++ */ |
++int |
++dtls_RecordGetRecvd(DTLSRecvdRecords *records, PRUint64 seq) |
++{ |
++ PRUint64 offset; |
++ |
++ /* Out of range to the left */ |
++ if (seq < records->left) { |
++ return -1; |
++ } |
++ |
++ /* Out of range to the right; since we advance the window on |
++ * receipt, that means that this packet has not been received |
++ * yet */ |
++ if (seq > records->right) |
++ return 0; |
++ |
++ offset = seq % DTLS_RECVD_RECORDS_WINDOW; |
++ |
++ return !!(records->data[offset / 8] & (1 << (offset % 8))); |
++} |
++ |
++/* Update the DTLS anti-replay window |
++ * |
++ * Called from ssl3_HandleRecord() |
++ */ |
++void |
++dtls_RecordSetRecvd(DTLSRecvdRecords *records, PRUint64 seq) |
++{ |
++ PRUint64 offset; |
++ |
++ if (seq < records->left) |
++ return; |
++ |
++ if (seq > records->right) { |
++ PRUint64 new_left; |
++ PRUint64 new_right; |
++ PRUint64 right; |
++ |
++ /* Slide to the right; this is the tricky part |
++ * |
++ * 1. new_top is set to have room for seq, on the |
++ * next byte boundary by setting the right 8 |
++ * bits of seq |
++ * 2. new_left is set to compensate. |
++ * 3. Zero all bits between top and new_top. Since |
++ * this is a ring, this zeroes everything as-yet |
++ * unseen. Because we always operate on byte |
++ * boundaries, we can zero one byte at a time |
++ */ |
++ new_right = seq | 0x07; |
++ new_left = (new_right - DTLS_RECVD_RECORDS_WINDOW) + 1; |
++ |
++ for (right = records->right + 8; right <= new_right; right += 8) { |
++ offset = right % DTLS_RECVD_RECORDS_WINDOW; |
++ records->data[offset / 8] = 0; |
++ } |
++ |
++ records->right = new_right; |
++ records->left = new_left; |
++ } |
++ |
++ offset = seq % DTLS_RECVD_RECORDS_WINDOW; |
++ |
++ records->data[offset / 8] |= (1 << (offset % 8)); |
++} |
++ |
++SECStatus |
++DTLS_GetTimeout(PRFileDesc *socket, PRIntervalTime *timeout) |
++{ |
++ sslSocket * ss = NULL; |
++ PRIntervalTime elapsed; |
++ PRIntervalTime desired; |
++ |
++ ss = ssl_FindSocket(socket); |
++ |
++ if (!ss) |
++ return SECFailure; |
++ |
++ if (!IS_DTLS(ss)) |
++ return SECFailure; |
++ |
++ if (!ss->ssl3.hs.rtTimerCb) |
++ return SECFailure; |
++ |
++ elapsed = PR_IntervalNow() - ss->ssl3.hs.rtTimerStarted; |
++ desired = PR_MillisecondsToInterval(ss->ssl3.hs.rtTimeoutMs); |
++ if (elapsed > desired) { |
++ /* Timer expired */ |
++ *timeout = PR_INTERVAL_NO_WAIT; |
++ } else { |
++ *timeout = desired - elapsed; |
++ } |
++ |
++ return SECSuccess; |
++} |
+ |
+Property changes on: net/third_party/nss/ssl/dtls1con.c |
+___________________________________________________________________ |
+Added: svn:eol-style |
+ + LF |
+ |
+Index: net/third_party/nss/ssl/sslproto.h |
+=================================================================== |
+--- net/third_party/nss/ssl/sslproto.h (revision 127709) |
++++ net/third_party/nss/ssl/sslproto.h (working copy) |
+@@ -49,10 +49,15 @@ |
+ #define SSL_LIBRARY_VERSION_3_0 0x0300 |
+ #define SSL_LIBRARY_VERSION_TLS_1_0 0x0301 |
+ #define SSL_LIBRARY_VERSION_TLS_1_1 0x0302 |
++/* Note: this is the internal format, not the wire format */ |
++#define SSL_LIBRARY_VERSION_DTLS_1_0 0x0302 |
+ |
+ /* deprecated old name */ |
+ #define SSL_LIBRARY_VERSION_3_1_TLS SSL_LIBRARY_VERSION_TLS_1_0 |
+ |
++/* The DTLS version used in the spec */ |
++#define SSL_LIBRARY_VERSION_DTLS_1_0_WIRE ((~0x0100) & 0xffff) |
++ |
+ /* Header lengths of some of the messages */ |
+ #define SSL_HL_ERROR_HBYTES 3 |
+ #define SSL_HL_CLIENT_HELLO_HBYTES 9 |
+Index: net/third_party/nss/ssl/sslt.h |
+=================================================================== |
+--- net/third_party/nss/ssl/sslt.h (revision 127709) |
++++ net/third_party/nss/ssl/sslt.h (working copy) |
+@@ -190,7 +190,8 @@ |
+ } SSLCipherSuiteInfo; |
+ |
+ typedef enum { |
+- ssl_variant_stream = 0 |
++ ssl_variant_stream = 0, |
++ ssl_variant_datagram = 1 |
+ } SSLProtocolVariant; |
+ |
+ typedef struct SSLVersionRangeStr { |