summaryrefslogtreecommitdiffstats
path: root/net/third_party
diff options
context:
space:
mode:
authorwtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-30 03:50:57 +0000
committerwtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-30 03:50:57 +0000
commit700c89a15f7a691d4189b13352f94d26cd0a729e (patch)
treef7a627f658e48df9b416cf1e9ef5214b6de668a8 /net/third_party
parent260e7ffe2d5d5133ebcb90fc0e392a04efe2f1c1 (diff)
downloadchromium_src-700c89a15f7a691d4189b13352f94d26cd0a729e.zip
chromium_src-700c89a15f7a691d4189b13352f94d26cd0a729e.tar.gz
chromium_src-700c89a15f7a691d4189b13352f94d26cd0a729e.tar.bz2
Add DTLS support to NSS, contributed by Eric Rescorla.
R=rsleevi@chromium.org BUG=120938 TEST=none Review URL: http://codereview.chromium.org/9764001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@129778 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/third_party')
-rw-r--r--net/third_party/nss/README.chromium4
-rwxr-xr-xnet/third_party/nss/patches/applypatches.sh2
-rw-r--r--net/third_party/nss/patches/dtls.patch3321
-rw-r--r--net/third_party/nss/ssl.gyp1
-rw-r--r--net/third_party/nss/ssl/SSLerrs.h6
-rw-r--r--net/third_party/nss/ssl/derive.c2
-rw-r--r--net/third_party/nss/ssl/dtls1con.c1163
-rw-r--r--net/third_party/nss/ssl/manifest.mn1
-rw-r--r--net/third_party/nss/ssl/ssl.h14
-rw-r--r--net/third_party/nss/ssl/ssl3con.c584
-rw-r--r--net/third_party/nss/ssl/ssl3gthr.c159
-rw-r--r--net/third_party/nss/ssl/ssl3prot.h4
-rw-r--r--net/third_party/nss/ssl/sslcon.c9
-rw-r--r--net/third_party/nss/ssl/ssldef.c5
-rw-r--r--net/third_party/nss/ssl/sslerr.h3
-rw-r--r--net/third_party/nss/ssl/sslgathr.c3
-rw-r--r--net/third_party/nss/ssl/sslimpl.h151
-rw-r--r--net/third_party/nss/ssl/sslproto.h5
-rw-r--r--net/third_party/nss/ssl/sslsecur.c1
-rw-r--r--net/third_party/nss/ssl/sslsock.c121
-rw-r--r--net/third_party/nss/ssl/sslt.h3
21 files changed, 5435 insertions, 127 deletions
diff --git a/net/third_party/nss/README.chromium b/net/third_party/nss/README.chromium
index 864784e..6e2ca29 100644
--- a/net/third_party/nss/README.chromium
+++ b/net/third_party/nss/README.chromium
@@ -61,6 +61,10 @@ Patches:
https://bugzilla.mozilla.org/show_bug.cgi?id=51413
patches/getrequestedclientcerttypes.patch
+ * Add DTLS support.
+ https://bugzilla.mozilla.org/show_bug.cgi?id=681065
+ patches/dtls.patch
+
Apply the patches to NSS by running the patches/applypatches.sh script. Read
the comments at the top of patches/applypatches.sh for instructions.
diff --git a/net/third_party/nss/patches/applypatches.sh b/net/third_party/nss/patches/applypatches.sh
index d826b29..dbb603e 100755
--- a/net/third_party/nss/patches/applypatches.sh
+++ b/net/third_party/nss/patches/applypatches.sh
@@ -32,3 +32,5 @@ patch -p6 < $patches_dir/getrequestedclientcerttypes.patch
patch -p6 < $patches_dir/restartclientauth.patch
patch -p6 < $patches_dir/encryptedclientcerts.patch
+
+patch -p4 < $patches_dir/dtls.patch
diff --git a/net/third_party/nss/patches/dtls.patch b/net/third_party/nss/patches/dtls.patch
new file mode 100644
index 0000000..1dc2325
--- /dev/null
+++ b/net/third_party/nss/patches/dtls.patch
@@ -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 {
diff --git a/net/third_party/nss/ssl.gyp b/net/third_party/nss/ssl.gyp
index b8f75d6..0380c0e 100644
--- a/net/third_party/nss/ssl.gyp
+++ b/net/third_party/nss/ssl.gyp
@@ -27,6 +27,7 @@
'ssl/authcert.c',
'ssl/cmpcert.c',
'ssl/derive.c',
+ 'ssl/dtls1con.c',
'ssl/nsskea.c',
'ssl/os2_err.c',
'ssl/os2_err.h',
diff --git a/net/third_party/nss/ssl/SSLerrs.h b/net/third_party/nss/ssl/SSLerrs.h
index 29aa512..e3f9a1c 100644
--- a/net/third_party/nss/ssl/SSLerrs.h
+++ b/net/third_party/nss/ssl/SSLerrs.h
@@ -423,3 +423,9 @@ ER3(SSL_ERROR_INVALID_VERSION_RANGE, (SSL_ERROR_BASE + 120),
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.")
diff --git a/net/third_party/nss/ssl/derive.c b/net/third_party/nss/ssl/derive.c
index d4a1d14..ea22dde 100644
--- a/net/third_party/nss/ssl/derive.c
+++ b/net/third_party/nss/ssl/derive.c
@@ -583,6 +583,8 @@ ssl_canExtractMS(PK11SymKey *pms, PRBool isTLS, PRBool isDH, PRBool *pcbp)
* 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,
diff --git a/net/third_party/nss/ssl/dtls1con.c b/net/third_party/nss/ssl/dtls1con.c
new file mode 100644
index 0000000..5843651
--- /dev/null
+++ b/net/third_party/nss/ssl/dtls1con.c
@@ -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;
+}
diff --git a/net/third_party/nss/ssl/manifest.mn b/net/third_party/nss/ssl/manifest.mn
index 0c0370c..6a20fd3 100644
--- a/net/third_party/nss/ssl/manifest.mn
+++ b/net/third_party/nss/ssl/manifest.mn
@@ -51,6 +51,7 @@ MAPFILE = $(OBJDIR)/ssl.def
CSRCS = \
derive.c \
+ dtls1con.c \
prelib.c \
ssl3con.c \
ssl3gthr.c \
diff --git a/net/third_party/nss/ssl/ssl.h b/net/third_party/nss/ssl/ssl.h
index 92e823a..8885575 100644
--- a/net/third_party/nss/ssl/ssl.h
+++ b/net/third_party/nss/ssl/ssl.h
@@ -80,6 +80,12 @@ SSL_IMPORT PRUint16 SSL_GetNumImplementedCiphers(void);
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 @@ SSL_IMPORT SECStatus SSL_HandshakeResumedSession(PRFileDesc *fd,
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.
*
diff --git a/net/third_party/nss/ssl/ssl3con.c b/net/third_party/nss/ssl/ssl3con.c
index 322e502..e8a7f017 100644
--- a/net/third_party/nss/ssl/ssl3con.c
+++ b/net/third_party/nss/ssl/ssl3con.c
@@ -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_NewHandshakeHashes( sslSocket *ss);
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 @@ static const /*SSL3ClientCertificateType */ uint8 certificate_types [] = {
#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 @@ ssl3_DecodeHandshakeType(int msgType)
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 @@ ssl3_config_match_init(sslSocket *ss)
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_CleanupKeyMaterial(ssl3KeyMaterial *mat)
** ssl3_DestroySSL3Info
** Caller must hold SpecWriteLock.
*/
-static void
+void
ssl3_DestroyCipherSpec(ssl3CipherSpec *spec, PRBool freeSrvName)
{
PRBool freeit = (PRBool)(!spec->bypassCiphers);
@@ -1228,6 +1216,12 @@ ssl3_SetupPendingCipherSpec(sslSocket *ss)
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 @@ SECStatus
ssl3_InitPendingCipherSpec(sslSocket *ss, PK11SymKey *pms)
{
ssl3CipherSpec * pwSpec;
+ ssl3CipherSpec * cwSpec;
SECStatus rv;
PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
@@ -1763,6 +1758,7 @@ ssl3_InitPendingCipherSpec(sslSocket *ss, PK11SymKey *pms)
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,6 +1790,31 @@ ssl3_InitPendingCipherSpec(sslSocket *ss, PK11SymKey *pms)
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); /******************************/
@@ -1834,6 +1855,7 @@ static SECStatus
ssl3_ComputeRecordMAC(
ssl3CipherSpec * spec,
PRBool useServerMacKey,
+ PRBool isDTLS,
SSL3ContentType type,
SSL3ProtocolVersion version,
SSL3SequenceNumber seq_num,
@@ -1871,8 +1893,16 @@ ssl3_ComputeRecordMAC(
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 @@ ssl3_ClientAuthTokenPresent(sslSessionID *sid) {
}
/* 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 @@ ssl3_CompressMACEncryptRecord(ssl3CipherSpec * cwSpec,
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 @@ ssl3_CompressMACEncryptRecord(ssl3CipherSpec * cwSpec,
* 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 @@ ssl3_CompressMACEncryptRecord(ssl3CipherSpec * cwSpec,
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 @@ ssl3_CompressMACEncryptRecord(ssl3CipherSpec * cwSpec,
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 @@ ssl3_CompressMACEncryptRecord(ssl3CipherSpec * cwSpec,
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 @@ ssl3_CompressMACEncryptRecord(ssl3CipherSpec * cwSpec,
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,14 +2196,32 @@ ssl3_CompressMACEncryptRecord(ssl3CipherSpec * cwSpec,
}
PORT_Assert(cipherBytes <= MAX_FRAGMENT_LENGTH + 1024);
- ssl3_BumpSequenceNumber(&cwSpec->write_seq_num);
-
- wrBuf->len = cipherBytes + SSL3_RECORD_HEADER_LENGTH;
+ wrBuf->len = cipherBytes + headerLen;
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);
+ 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);
return SECSuccess;
}
@@ -2194,10 +2244,13 @@ ssl3_CompressMACEncryptRecord(ssl3CipherSpec * cwSpec,
* 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 @@ ssl3_SendRecord( sslSocket * ss,
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 @@ ssl3_SendRecord( sslSocket * ss,
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 @@ spec_locked_loser:
}
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 @@ ssl3_SendApplicationData(sslSocket *ss, const unsigned char *in,
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 @@ ssl3_SendApplicationData(sslSocket *ss, const unsigned char *in,
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 @@ ssl3_SendApplicationData(sslSocket *ss, const unsigned char *in,
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 @@ ssl3_SendApplicationData(sslSocket *ss, const unsigned char *in,
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 @@ ssl3_FlushHandshake(sslSocket *ss, PRInt32 flags)
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 @@ SSL3_SendAlert(sslSocket *ss, SSL3AlertLevel level, SSL3AlertDescription desc)
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 @@ ssl3_SendAlertForCertError(sslSocket * ss, PRErrorCode errCode)
/*
* 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 @@ ssl3_HandleAlert(sslSocket *ss, sslBuffer *buf)
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 @@ ssl3_SendChangeCipherSpecs(sslSocket *ss)
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 @@ ssl3_SendChangeCipherSpecs(sslSocket *ss)
* (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 @@ ssl3_HandleChangeCipherSpecs(sslSocket *ss, sslBuffer *buf)
/* 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 @@ ssl3_DeriveMasterSecret(sslSocket *ss, PK11SymKey *pms)
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 @@ ssl3_AppendHandshakeHeader(sslSocket *ss, SSL3HandshakeType t, PRUint32 length)
{
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 @@ ssl3_AppendHandshakeHeader(sslSocket *ss, SSL3HandshakeType t, PRUint32 length)
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 @@ done:
/* 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 @@ ssl3_SendClientHello(sslSocket *ss)
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 @@ ssl3_SendClientHello(sslSocket *ss)
}
#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 @@ ssl3_SendClientHello(sslSocket *ss)
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 @@ ssl3_SendClientHello(sslSocket *ss)
}
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 @@ ssl3_SendClientHello(sslSocket *ss)
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 @@ ssl3_HandleHelloRequest(sslSocket *ss)
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 @@ ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
}
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 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
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 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
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 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
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 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
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_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
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 @@ ssl3_SendServerHello(sslSocket *ss)
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 @@ ssl3_SendServerHello(sslSocket *ss)
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 @@ ssl3_SendCertificateRequest(sslSocket *ss)
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,8 +7803,16 @@ ssl3_GenerateRSAPMS(sslSocket *ss, ssl3CipherSpec *spec,
}
/* 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 @@ ssl3_HandleRSAClientKeyExchange(sslSocket *ss,
} 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 @@ ssl3_HandleFinished(sslSocket *ss, SSL3Opaque *b, PRUint32 length,
}
}
+ 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 @@ ssl3_MaybeHandlePendingCertificateMessage(sslSocket *ss)
* 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 @@ ssl3_HandleHandshakeMessage(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
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 @@ ssl3_HandleHandshakeMessage(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
}
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 @@ ssl3_HandleHandshakeMessage(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
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 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
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 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
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 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
/* 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,18 +9843,26 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText, sslBuffer *databuf)
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;
+ }
}
-
-
- ssl3_BumpSequenceNumber(&crSpec->read_seq_num);
+ if (!IS_DTLS(ss)) {
+ ssl3_BumpSequenceNumber(&crSpec->read_seq_num);
+ } else {
+ dtls_RecordSetRecvd(&crSpec->recvdRecords, dtls_seq_num);
+ }
ssl_ReleaseSpecReadLock(ss); /*****************************************/
@@ -9615,7 +9967,11 @@ process_it:
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 @@ ssl3_InitCipherSpec(sslSocket *ss, ssl3CipherSpec *spec)
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 @@ ssl3_InitState(sslSocket *ss)
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 @@ ssl3_RedoHandshake(sslSocket *ss, PRBool flushCache)
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 @@ ssl3_RedoHandshake(sslSocket *ss, PRBool flushCache)
/* 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_DestroySSL3Info(sslSocket *ss)
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);
diff --git a/net/third_party/nss/ssl/ssl3gthr.c b/net/third_party/nss/ssl/ssl3gthr.c
index c6b09c7..4e25124 100644
--- a/net/third_party/nss/ssl/ssl3gthr.c
+++ b/net/third_party/nss/ssl/ssl3gthr.c
@@ -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 @@ ssl3_GatherData(sslSocket *ss, sslGather *gs, int flags)
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 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
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 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
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 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
*/
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);
}
diff --git a/net/third_party/nss/ssl/ssl3prot.h b/net/third_party/nss/ssl/ssl3prot.h
index a350a3d..550c341 100644
--- a/net/third_party/nss/ssl/ssl3prot.h
+++ b/net/third_party/nss/ssl/ssl3prot.h
@@ -61,6 +61,9 @@ typedef uint16 ssl3CipherSuite;
#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 @@ typedef enum {
hello_request = 0,
client_hello = 1,
server_hello = 2,
+ hello_verify_request = 3,
new_session_ticket = 4,
certificate = 11,
server_key_exchange = 12,
diff --git a/net/third_party/nss/ssl/sslcon.c b/net/third_party/nss/ssl/sslcon.c
index 71030d7..7a469e5 100644
--- a/net/third_party/nss/ssl/sslcon.c
+++ b/net/third_party/nss/ssl/sslcon.c
@@ -1249,7 +1249,12 @@ ssl_GatherRecord1stHandshake(sslSocket *ss)
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 @@ ssl2_BeginClientHandshake(sslSocket *ss)
ssl_GetSSL3HandshakeLock(ss);
ssl_GetXmitBufLock(ss);
- rv = ssl3_SendClientHello(ss);
+ rv = ssl3_SendClientHello(ss, PR_FALSE);
ssl_ReleaseXmitBufLock(ss);
ssl_ReleaseSSL3HandshakeLock(ss);
diff --git a/net/third_party/nss/ssl/ssldef.c b/net/third_party/nss/ssl/ssldef.c
index 2e6a05f..bc8b5d8 100644
--- a/net/third_party/nss/ssl/ssldef.c
+++ b/net/third_party/nss/ssl/ssldef.c
@@ -138,6 +138,11 @@ int ssl_DefSend(sslSocket *ss, const unsigned char *buf, int len, int flags)
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;
diff --git a/net/third_party/nss/ssl/sslerr.h b/net/third_party/nss/ssl/sslerr.h
index 33c809e..9d3bebc 100644
--- a/net/third_party/nss/ssl/sslerr.h
+++ b/net/third_party/nss/ssl/sslerr.h
@@ -215,6 +215,9 @@ SSL_ERROR_INVALID_VERSION_RANGE = (SSL_ERROR_BASE + 120),
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 */
diff --git a/net/third_party/nss/ssl/sslgathr.c b/net/third_party/nss/ssl/sslgathr.c
index 3b864f6..d39c968 100644
--- a/net/third_party/nss/ssl/sslgathr.c
+++ b/net/third_party/nss/ssl/sslgathr.c
@@ -434,6 +434,8 @@ ssl_InitGather(sslGather *gs)
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 @@ ssl_DestroyGather(sslGather *gs)
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);
}
}
diff --git a/net/third_party/nss/ssl/sslimpl.h b/net/third_party/nss/ssl/sslimpl.h
index 5f5ddbc..4991aca 100644
--- a/net/third_party/nss/ssl/sslimpl.h
+++ b/net/third_party/nss/ssl/sslimpl.h
@@ -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 @@ typedef enum { SSLAppOpRead = 0,
#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 @@ struct sslSocketOpsStr {
/* 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 @@ struct sslGatherStr {
** 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 @@ typedef struct {
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 @@ typedef struct {
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 @@ typedef struct {
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 @@ struct TLSExtensionDataStr {
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 @@ const ssl3CipherSuiteDef *suite_def;
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 @@ struct ssl3StateStr {
*/
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 @@ const unsigned char * preferredCipher;
/* True when the current session is a stateless resume. */
PRBool statelessResume;
TLSExtensionData xtnData;
+
+ /* Whether we are doing stream or datagram mode */
+ SSLProtocolVariant protocolVariant;
};
@@ -1321,6 +1400,34 @@ extern void ssl3_SetAlwaysBlock(sslSocket *ss);
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)
@@ -1417,6 +1524,7 @@ extern sslSocket *ssl_FindSocket(PRFileDesc *fd);
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 @@ extern SECStatus ssl3_StartHandshakeHash(
/*
* 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 @@ extern SECStatus ssl3_ConstructV2CipherSpecsHack(sslSocket *ss,
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_SendECDHServerKeyExchange(sslSocket *ss);
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 @@ extern CERTCertificateList* hack_NewCertificateListFromCertList(
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);
diff --git a/net/third_party/nss/ssl/sslproto.h b/net/third_party/nss/ssl/sslproto.h
index 985b097..434d062 100644
--- a/net/third_party/nss/ssl/sslproto.h
+++ b/net/third_party/nss/ssl/sslproto.h
@@ -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
diff --git a/net/third_party/nss/ssl/sslsecur.c b/net/third_party/nss/ssl/sslsecur.c
index a0e410b..e4804d0 100644
--- a/net/third_party/nss/ssl/sslsecur.c
+++ b/net/third_party/nss/ssl/sslsecur.c
@@ -615,6 +615,7 @@ DoRecv(sslSocket *ss, unsigned char *out, int len, int flags)
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",
diff --git a/net/third_party/nss/ssl/sslsock.c b/net/third_party/nss/ssl/sslsock.c
index a91412b..3364902 100644
--- a/net/third_party/nss/ssl/sslsock.c
+++ b/net/third_party/nss/ssl/sslsock.c
@@ -194,11 +194,20 @@ static sslOptions ssl_defaults = {
/*
* 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 @@ char lockStatus[] = "Locks are ENABLED. ";
#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 @@ ssl_DupSocket(sslSocket *os)
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 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRBool on)
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 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRBool on)
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 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRBool on)
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 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRBool on)
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 @@ SSL_OptionGetDefault(PRInt32 which, PRBool *pOn)
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 @@ SSL_OptionSetDefault(PRInt32 which, PRBool on)
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 @@ NSS_SetFrancePolicy(void)
/* 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 @@ SSL_ImportFD(PRFileDesc *model, PRFileDesc *fd)
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 @@ SSL_ImportFD(PRFileDesc *model, PRFileDesc *fd)
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 @@ PRBool
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 @@ SECStatus
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 @@ SECStatus
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 @@ SSL_VersionRangeSetDefault(SSLProtocolVariant protocolVariant,
return SECFailure;
}
- versions_defaults = *vrange;
+ *VERSIONS_DEFAULTS(protocolVariant) = *vrange;
return SECSuccess;
}
@@ -2830,7 +2906,7 @@ ssl_SetDefaultsFromEnvironment(void)
** 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 @@ ssl_NewSocket(PRBool makeLocks)
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 @@ loser:
PORT_Free(ss);
ss = NULL;
}
+ ss->protocolVariant = protocolVariant;
}
return ss;
}
diff --git a/net/third_party/nss/ssl/sslt.h b/net/third_party/nss/ssl/sslt.h
index af15414..eddfffd 100644
--- a/net/third_party/nss/ssl/sslt.h
+++ b/net/third_party/nss/ssl/sslt.h
@@ -190,7 +190,8 @@ typedef struct SSLCipherSuiteInfoStr {
} SSLCipherSuiteInfo;
typedef enum {
- ssl_variant_stream = 0
+ ssl_variant_stream = 0,
+ ssl_variant_datagram = 1
} SSLProtocolVariant;
typedef struct SSLVersionRangeStr {