diff options
author | wtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-30 03:50:57 +0000 |
---|---|---|
committer | wtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-30 03:50:57 +0000 |
commit | 700c89a15f7a691d4189b13352f94d26cd0a729e (patch) | |
tree | f7a627f658e48df9b416cf1e9ef5214b6de668a8 /net/third_party | |
parent | 260e7ffe2d5d5133ebcb90fc0e392a04efe2f1c1 (diff) | |
download | chromium_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.chromium | 4 | ||||
-rwxr-xr-x | net/third_party/nss/patches/applypatches.sh | 2 | ||||
-rw-r--r-- | net/third_party/nss/patches/dtls.patch | 3321 | ||||
-rw-r--r-- | net/third_party/nss/ssl.gyp | 1 | ||||
-rw-r--r-- | net/third_party/nss/ssl/SSLerrs.h | 6 | ||||
-rw-r--r-- | net/third_party/nss/ssl/derive.c | 2 | ||||
-rw-r--r-- | net/third_party/nss/ssl/dtls1con.c | 1163 | ||||
-rw-r--r-- | net/third_party/nss/ssl/manifest.mn | 1 | ||||
-rw-r--r-- | net/third_party/nss/ssl/ssl.h | 14 | ||||
-rw-r--r-- | net/third_party/nss/ssl/ssl3con.c | 584 | ||||
-rw-r--r-- | net/third_party/nss/ssl/ssl3gthr.c | 159 | ||||
-rw-r--r-- | net/third_party/nss/ssl/ssl3prot.h | 4 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslcon.c | 9 | ||||
-rw-r--r-- | net/third_party/nss/ssl/ssldef.c | 5 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslerr.h | 3 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslgathr.c | 3 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslimpl.h | 151 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslproto.h | 5 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslsecur.c | 1 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslsock.c | 121 | ||||
-rw-r--r-- | net/third_party/nss/ssl/sslt.h | 3 |
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 { |