/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * DTLS Protocol */ #include "ssl.h" #include "sslimpl.h" #include "sslproto.h" #include "dtls13con.h" #ifndef PR_ARRAY_SIZE #define PR_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) #endif static SECStatus dtls_StartRetransmitTimer(sslSocket *ss); static void dtls_RetransmitTimerExpiredCb(sslSocket *ss); static SECStatus dtls_SendSavedWriteData(sslSocket *ss); static void dtls_FinishedTimerCb(sslSocket *ss); static void dtls_CancelAllTimers(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 /* Maximum DTLS expansion = header + IV + max CBC padding + * maximum MAC. */ #define DTLS_MAX_EXPANSION (DTLS_RECORD_HEADER_LENGTH + 16 + 16 + 32) /* List copied from ssl3con.c:cipherSuites */ static const ssl3CipherSuite nonDTLSSuites[] = { TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, TLS_DHE_DSS_WITH_RC4_128_SHA, TLS_ECDH_RSA_WITH_RC4_128_SHA, TLS_ECDH_ECDSA_WITH_RC4_128_SHA, TLS_RSA_WITH_RC4_128_MD5, TLS_RSA_WITH_RC4_128_SHA, 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) * 1.2 (0303) 1.2 (fefd) * 1.3 (0304) 1.3 (0304) */ SSL3ProtocolVersion dtls_TLSVersionToDTLSVersion(SSL3ProtocolVersion tlsv) { if (tlsv == SSL_LIBRARY_VERSION_TLS_1_1) { return SSL_LIBRARY_VERSION_DTLS_1_0_WIRE; } if (tlsv == SSL_LIBRARY_VERSION_TLS_1_2) { return SSL_LIBRARY_VERSION_DTLS_1_2_WIRE; } if (tlsv == SSL_LIBRARY_VERSION_TLS_1_3) { return SSL_LIBRARY_VERSION_DTLS_1_3_WIRE; } /* Anything else is an error, so return * the invalid version 0xffff. */ return 0xffff; } /* 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; } /* Handle the skipped version of DTLS 1.1 by returning * an error. */ if (dtlsv == ((~0x0101) & 0xffff)) { return 0; } if (dtlsv == SSL_LIBRARY_VERSION_DTLS_1_2_WIRE) { return SSL_LIBRARY_VERSION_TLS_1_2; } if (dtlsv == SSL_LIBRARY_VERSION_DTLS_1_3_WIRE) { return SSL_LIBRARY_VERSION_TLS_1_3; } /* Return a fictional higher version than we know of */ return SSL_LIBRARY_VERSION_MAX_SUPPORTED + 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) { PORT_CheckSuccess(ssl3_CipherPrefSet(ss, *suite, PR_FALSE)); } return SECSuccess; } /* Allocate a DTLSQueuedMessage. * * Called from dtls_QueueMessage() */ static DTLSQueuedMessage * dtls_AllocQueuedMessage(ssl3CipherSpec *cwSpec, SSLContentType ct, const unsigned char *data, PRUint32 len) { DTLSQueuedMessage *msg; msg = PORT_ZNew(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->cwSpec = cwSpec; msg->type = ct; /* Safe if we are < 1.3, since the refct is * already very high. */ ssl_CipherSpecAddRef(cwSpec); return msg; } /* * Free a handshake message * * Called from dtls_FreeHandshakeMessages() */ void dtls_FreeHandshakeMessage(DTLSQueuedMessage *msg) { if (!msg) return; /* Safe if we are < 1.3, since the refct is * already very high. */ ssl_CipherSpecRelease(msg->cwSpec); 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 by dtls_HandleHandshake() and dtls_MaybeRetransmitHandshake() if a * handshake message retransmission is detected. */ static SECStatus dtls_RetransmitDetected(sslSocket *ss) { dtlsTimer *timer = ss->ssl3.hs.rtTimer; SECStatus rv = SECSuccess; PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); if (timer->cb == dtls_RetransmitTimerExpiredCb) { /* 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 it should be. */ if ((PR_IntervalNow() - timer->started) > (timer->timeout / 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, ss->ssl3.hs.rtTimer); dtls_RetransmitTimerExpiredCb(ss); } else { SSL_TRC(30, ("%d: SSL3[%d]: Ignoring retransmission: " "last retransmission %dms ago, suppressed for %dms", SSL_GETPID(), ss->fd, PR_IntervalNow() - timer->started, timer->timeout / 4)); } } else if (timer->cb == dtls_FinishedTimerCb) { SSL_TRC(30, ("%d: SSL3[%d]: Retransmit detected in holddown", SSL_GETPID(), ss->fd)); /* 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, ss->ssl3.hs.rtTimer); rv = dtls_TransmitMessageFlight(ss); if (rv == SECSuccess) { rv = dtls_StartHolddownTimer(ss); } } else { PORT_Assert(timer->cb == NULL); /* ... and ignore it. */ } return rv; } static SECStatus dtls_HandleHandshakeMessage(sslSocket *ss, PRUint8 *data, PRBool last) { ss->ssl3.hs.recvdHighWater = -1; return ssl3_HandleHandshakeMessage(ss, data, ss->ssl3.hs.msg_len, last); } /* 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, DTLSEpoch epoch, sslSequenceNumber seqNum, sslBuffer *origBuf) { sslBuffer buf = *origBuf; SECStatus rv = SECSuccess; PRBool discarded = PR_FALSE; ss->ssl3.hs.endOfFlight = PR_FALSE; 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; goto loser; } /* 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_MALFORMED_HANDSHAKE); rv = SECFailure; goto loser; } #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; goto loser; } /* Sanity check the packet contents */ if ((fragment_length + fragment_offset) > message_length) { PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE); rv = SECFailure; goto loser; } /* If we're a server and we receive what appears to be a retried * ClientHello, and we are expecting a ClientHello, move the receive * sequence number forward. This allows for a retried ClientHello if we * send a stateless HelloRetryRequest. */ if (message_seq > ss->ssl3.hs.recvMessageSeq && message_seq == 1 && fragment_offset == 0 && ss->ssl3.hs.ws == wait_client_hello && (SSLHandshakeType)type == ssl_hs_client_hello) { SSL_TRC(5, ("%d: DTLS[%d]: Received apparent 2nd ClientHello", SSL_GETPID(), ss->fd)); ss->ssl3.hs.recvMessageSeq = 1; ss->ssl3.hs.helloRetry = PR_TRUE; } /* 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 = (SSLHandshakeType)type; ss->ssl3.hs.msg_len = message_length; rv = dtls_HandleHandshakeMessage(ss, buf.buf, buf.len == fragment_length); if (rv != SECSuccess) { goto loser; } } else { if (message_seq < ss->ssl3.hs.recvMessageSeq) { /* Case 3: we do an immediate retransmit if we're * in a waiting state. */ rv = dtls_RetransmitDetected(ss); goto loser; } 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? */ SSL_TRC(10, ("%d: SSL3[%d]: dtls_HandleHandshake, discarding handshake message", SSL_GETPID(), ss->fd)); discarded = PR_TRUE; } else { PRInt32 end = fragment_offset + fragment_length; /* 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) goto loser; /* Make room for the fragment map */ rv = sslBuffer_Grow(&ss->ssl3.hs.recvdFragments, map_length); if (rv != SECSuccess) goto loser; /* 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 = (SSLHandshakeType)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; goto loser; } /* Now copy this fragment into the buffer. */ if (end > ss->ssl3.hs.recvdHighWater) { 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 <= (unsigned int)ss->ssl3.hs.recvdHighWater) { /* Either this is the adjacent fragment or an overlapping * fragment */ if (end > ss->ssl3.hs.recvdHighWater) { ss->ssl3.hs.recvdHighWater = end; } } else { for (offset = fragment_offset; offset < end; 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) { rv = dtls_HandleHandshakeMessage(ss, ss->ssl3.hs.msg_body.buf, buf.len == fragment_length); if (rv != SECSuccess) { goto loser; } } } } buf.buf += fragment_length; buf.len -= fragment_length; } // This should never happen, but belt and suspenders. if (rv != SECSuccess) { PORT_Assert(0); goto loser; } /* If we processed all the fragments in this message, then mark it as remembered. * TODO(ekr@rtfm.com): Store out of order messages for DTLS 1.3 so ACKs work * better. Bug 1392620.*/ if (!discarded && tls13_MaybeTls13(ss)) { rv = dtls13_RememberFragment(ss, &ss->ssl3.hs.dtlsRcvdHandshake, 0, 0, 0, epoch, seqNum); } if (rv != SECSuccess) { goto loser; } rv = dtls13_SetupAcks(ss); loser: origBuf->len = 0; /* So ssl3_GatherAppDataRecord will keep looping. */ return rv; } /* Enqueue a message (either handshake or CCS) * * Called from: * dtls_StageHandshakeMessage() * ssl3_SendChangeCipherSpecs() */ SECStatus dtls_QueueMessage(sslSocket *ss, SSLContentType ct, const PRUint8 *pIn, PRInt32 nIn) { SECStatus rv = SECSuccess; DTLSQueuedMessage *msg = NULL; ssl3CipherSpec *spec; PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); spec = ss->ssl3.cwSpec; msg = dtls_AllocQueuedMessage(spec, ct, 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. * 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, ssl_ct_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)) { rv = dtls_StartRetransmitTimer(ss); } else { PORT_Assert(ss->version < SSL_LIBRARY_VERSION_TLS_1_3); } } return rv; } /* The callback for when the retransmit timer expires * * Called from: * dtls_CheckTimer() * dtls_HandleHandshake() */ static void dtls_RetransmitTimerExpiredCb(sslSocket *ss) { SECStatus rv; dtlsTimer *timer = ss->ssl3.hs.rtTimer; 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 */ timer->timeout *= 2; if (timer->timeout > DTLS_RETRANSMIT_MAX_MS) { timer->timeout = DTLS_RETRANSMIT_MAX_MS; } timer->started = PR_IntervalNow(); timer->cb = dtls_RetransmitTimerExpiredCb; SSL_TRC(30, ("%d: SSL3[%d]: Retransmit #%d, next in %d", SSL_GETPID(), ss->fd, ss->ssl3.hs.rtRetries, timer->timeout)); } /* else: OK for now. In future maybe signal the stack that we couldn't * transmit. For now, let the read handle any real network errors */ } #define DTLS_HS_HDR_LEN 12 #define DTLS_MIN_FRAGMENT (DTLS_HS_HDR_LEN + 1 + DTLS_MAX_EXPANSION) /* Encrypt and encode a handshake message fragment. Flush the data out to the * network if there is insufficient space for any fragment. */ static SECStatus dtls_SendFragment(sslSocket *ss, DTLSQueuedMessage *msg, PRUint8 *data, unsigned int len) { PRInt32 sent; SECStatus rv; PRINT_BUF(40, (ss, "dtls_SendFragment", data, len)); sent = ssl3_SendRecord(ss, msg->cwSpec, msg->type, data, len, ssl_SEND_FLAG_FORCE_INTO_BUFFER); if (sent != len) { if (sent != -1) { PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); } return SECFailure; } /* If another fragment won't fit, flush. */ if (ss->ssl3.mtu < ss->pendingBuf.len + DTLS_MIN_FRAGMENT) { SSL_TRC(20, ("%d: DTLS[%d]: dtls_SendFragment: flush", SSL_GETPID(), ss->fd)); rv = dtls_SendSavedWriteData(ss); if (rv != SECSuccess) { return SECFailure; } } return SECSuccess; } /* Fragment a handshake message into multiple records and send them. */ static SECStatus dtls_FragmentHandshake(sslSocket *ss, DTLSQueuedMessage *msg) { PRBool fragmentWritten = PR_FALSE; PRUint16 msgSeq; PRUint8 *fragment; PRUint32 fragmentOffset = 0; PRUint32 fragmentLen; const PRUint8 *content = msg->data + DTLS_HS_HDR_LEN; PRUint32 contentLen = msg->len - DTLS_HS_HDR_LEN; SECStatus rv; /* The headers consume 12 bytes so the smallest possible message (i.e., an * empty one) is 12 bytes. */ PORT_Assert(msg->len >= DTLS_HS_HDR_LEN); /* DTLS only supports fragmenting handshaking messages. */ PORT_Assert(msg->type == ssl_ct_handshake); msgSeq = (msg->data[4] << 8) | msg->data[5]; /* do {} while() so that empty messages are sent at least once. */ do { PRUint8 buf[DTLS_MAX_MTU]; /* >= than largest plausible MTU */ PRBool hasUnackedRange; PRUint32 end; hasUnackedRange = dtls_NextUnackedRange(ss, msgSeq, fragmentOffset, contentLen, &fragmentOffset, &end); if (!hasUnackedRange) { SSL_TRC(20, ("%d: SSL3[%d]: FragmentHandshake %d: all acknowledged", SSL_GETPID(), ss->fd, msgSeq)); break; } SSL_TRC(20, ("%d: SSL3[%d]: FragmentHandshake %d: unacked=%u-%u", SSL_GETPID(), ss->fd, msgSeq, fragmentOffset, end)); /* Cut down to the data we have available. */ PORT_Assert(fragmentOffset <= contentLen); PORT_Assert(fragmentOffset <= end); PORT_Assert(end <= contentLen); fragmentLen = PR_MIN(end, contentLen) - fragmentOffset; /* Limit further by the record size limit. Account for the header. */ fragmentLen = PR_MIN(fragmentLen, msg->cwSpec->recordSizeLimit - DTLS_HS_HDR_LEN); /* Reduce to the space remaining in the MTU. */ fragmentLen = PR_MIN(fragmentLen, ss->ssl3.mtu - /* MTU estimate. */ ss->pendingBuf.len - /* Less any unsent records. */ DTLS_MAX_EXPANSION - /* Allow for expansion. */ DTLS_HS_HDR_LEN); /* And the handshake header. */ PORT_Assert(fragmentLen > 0 || fragmentOffset == 0); /* Make totally sure that we will fit in the buffer. This should be * impossible; DTLS_MAX_MTU should always be more than ss->ssl3.mtu. */ if (fragmentLen >= (DTLS_MAX_MTU - DTLS_HS_HDR_LEN)) { PORT_Assert(0); PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure; } if (fragmentLen == contentLen) { fragment = msg->data; } else { sslBuffer tmp = SSL_BUFFER_FIXED(buf, sizeof(buf)); /* Construct an appropriate-sized fragment */ /* Type, length, sequence */ rv = sslBuffer_Append(&tmp, msg->data, 6); if (rv != SECSuccess) { return SECFailure; } /* Offset. */ rv = sslBuffer_AppendNumber(&tmp, fragmentOffset, 3); if (rv != SECSuccess) { return SECFailure; } /* Length. */ rv = sslBuffer_AppendNumber(&tmp, fragmentLen, 3); if (rv != SECSuccess) { return SECFailure; } /* Data. */ rv = sslBuffer_Append(&tmp, content + fragmentOffset, fragmentLen); if (rv != SECSuccess) { return SECFailure; } fragment = SSL_BUFFER_BASE(&tmp); } /* Record that we are sending first, because encrypting * increments the sequence number. */ rv = dtls13_RememberFragment(ss, &ss->ssl3.hs.dtlsSentHandshake, msgSeq, fragmentOffset, fragmentLen, msg->cwSpec->epoch, msg->cwSpec->nextSeqNum); if (rv != SECSuccess) { return SECFailure; } rv = dtls_SendFragment(ss, msg, fragment, fragmentLen + DTLS_HS_HDR_LEN); if (rv != SECSuccess) { return SECFailure; } fragmentWritten = PR_TRUE; fragmentOffset += fragmentLen; } while (fragmentOffset < contentLen); if (!fragmentWritten) { /* Nothing was written if we got here, so the whole message must have * been acknowledged. Discard it. */ SSL_TRC(10, ("%d: SSL3[%d]: FragmentHandshake %d: removed", SSL_GETPID(), ss->fd, msgSeq)); PR_REMOVE_LINK(&msg->link); dtls_FreeHandshakeMessage(msg); } return SECSuccess; } /* Transmit a flight of handshake messages, stuffing them * into as few records as seems reasonable. * * TODO: Space separate UDP packets out a little. * * Called from: * dtls_FlushHandshake() * dtls_RetransmitTimerExpiredCb() */ SECStatus dtls_TransmitMessageFlight(sslSocket *ss) { SECStatus rv = SECSuccess; PRCList *msg_p; SSL_TRC(10, ("%d: SSL3[%d]: dtls_TransmitMessageFlight", SSL_GETPID(), ss->fd)); 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. This function uses ss->pendingBuf temporarily and it * needs to be empty to start. */ PORT_Assert(!ss->pendingBuf.len); for (msg_p = PR_LIST_HEAD(&ss->ssl3.hs.lastMessageFlight); msg_p != &ss->ssl3.hs.lastMessageFlight;) { DTLSQueuedMessage *msg = (DTLSQueuedMessage *)msg_p; /* Move the pointer forward so that the functions below are free to * remove messages from the list. */ msg_p = PR_NEXT_LINK(msg_p); /* Note: This function fragments messages so that each record is close * to full. This produces fewer records, but it means that messages can * be quite fragmented. Adding an extra flush here would push new * messages into new records and reduce fragmentation. */ if (msg->type == ssl_ct_handshake) { rv = dtls_FragmentHandshake(ss, msg); } else { PORT_Assert(!tls13_MaybeTls13(ss)); rv = dtls_SendFragment(ss, msg, msg->data, msg->len); } if (rv != SECSuccess) { break; } } /* Finally, flush any data that wasn't flushed already. */ 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; } void dtls_InitTimers(sslSocket *ss) { unsigned int i; dtlsTimer **timers[PR_ARRAY_SIZE(ss->ssl3.hs.timers)] = { &ss->ssl3.hs.rtTimer, &ss->ssl3.hs.ackTimer, &ss->ssl3.hs.hdTimer }; static const char *timerLabels[] = { "retransmit", "ack", "holddown" }; PORT_Assert(PR_ARRAY_SIZE(timers) == PR_ARRAY_SIZE(timerLabels)); for (i = 0; i < PR_ARRAY_SIZE(ss->ssl3.hs.timers); ++i) { *timers[i] = &ss->ssl3.hs.timers[i]; ss->ssl3.hs.timers[i].label = timerLabels[i]; } } SECStatus dtls_StartTimer(sslSocket *ss, dtlsTimer *timer, PRUint32 time, DTLSTimerCb cb) { PORT_Assert(timer->cb == NULL); SSL_TRC(10, ("%d: SSL3[%d]: %s dtls_StartTimer %s timeout=%d", SSL_GETPID(), ss->fd, SSL_ROLE(ss), timer->label, time)); timer->started = PR_IntervalNow(); timer->timeout = time; timer->cb = cb; return SECSuccess; } SECStatus dtls_RestartTimer(sslSocket *ss, dtlsTimer *timer) { timer->started = PR_IntervalNow(); return SECSuccess; } PRBool dtls_TimerActive(sslSocket *ss, dtlsTimer *timer) { return timer->cb != NULL; } /* Start a timer for retransmission. */ static SECStatus dtls_StartRetransmitTimer(sslSocket *ss) { ss->ssl3.hs.rtRetries = 0; return dtls_StartTimer(ss, ss->ssl3.hs.rtTimer, DTLS_RETRANSMIT_INITIAL_MS, dtls_RetransmitTimerExpiredCb); } /* Start a timer for holding an old cipher spec. */ SECStatus dtls_StartHolddownTimer(sslSocket *ss) { ss->ssl3.hs.rtRetries = 0; return dtls_StartTimer(ss, ss->ssl3.hs.rtTimer, DTLS_RETRANSMIT_FINISHED_MS, dtls_FinishedTimerCb); } /* Cancel a pending timer * * Called from: * dtls_HandleHandshake() * dtls_CheckTimer() */ void dtls_CancelTimer(sslSocket *ss, dtlsTimer *timer) { SSL_TRC(30, ("%d: SSL3[%d]: %s dtls_CancelTimer %s", SSL_GETPID(), ss->fd, SSL_ROLE(ss), timer->label)); PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); timer->cb = NULL; } static void dtls_CancelAllTimers(sslSocket *ss) { unsigned int i; for (i = 0; i < PR_ARRAY_SIZE(ss->ssl3.hs.timers); ++i) { dtls_CancelTimer(ss, &ss->ssl3.hs.timers[i]); } } /* Check the pending timer and fire the callback if it expired * * Called from ssl3_GatherCompleteHandshake() */ void dtls_CheckTimer(sslSocket *ss) { unsigned int i; SSL_TRC(30, ("%d: SSL3[%d]: dtls_CheckTimer (%s)", SSL_GETPID(), ss->fd, ss->sec.isServer ? "server" : "client")); ssl_GetSSL3HandshakeLock(ss); for (i = 0; i < PR_ARRAY_SIZE(ss->ssl3.hs.timers); ++i) { dtlsTimer *timer = &ss->ssl3.hs.timers[i]; if (!timer->cb) { continue; } if ((PR_IntervalNow() - timer->started) >= PR_MillisecondsToInterval(timer->timeout)) { /* Timer has expired */ DTLSTimerCb cb = timer->cb; SSL_TRC(10, ("%d: SSL3[%d]: %s firing timer %s", SSL_GETPID(), ss->fd, SSL_ROLE(ss), timer->label)); /* Cancel the timer so that we can call the CB safely */ dtls_CancelTimer(ss, timer); /* Now call the CB */ cb(ss); } } ssl_ReleaseSSL3HandshakeLock(ss); } /* The callback to fire when the holddown timer for the Finished * message expires and we can delete it * * Called from dtls_CheckTimer() */ static void dtls_FinishedTimerCb(sslSocket *ss) { dtls_FreeHandshakeMessages(&ss->ssl3.hs.lastMessageFlight); } /* 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) { /* Skip this if we are handling a second ClientHello. */ if (ss->ssl3.hs.helloRetry) { return; } PORT_Assert((ss->version < SSL_LIBRARY_VERSION_TLS_1_3)); dtls_CancelAllTimers(ss); dtls_FreeHandshakeMessages(&ss->ssl3.hs.lastMessageFlight); ss->ssl3.hs.sendMessageSeq = 0; ss->ssl3.hs.recvMessageSeq = 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, PRUint8 *b, PRUint32 length) { int errCode = SSL_ERROR_RX_MALFORMED_HELLO_VERIFY_REQUEST; SECStatus rv; SSL3ProtocolVersion temp; 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; } dtls_ReceivedFirstMessageInFlight(ss); /* The version. * * 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 do anything to enforce a match, just * read and check that this value is sane. */ rv = ssl_ClientReadVersion(ss, &b, &length, &temp); if (rv != SECSuccess) { goto loser; /* alert has been sent */ } /* Read the cookie. * IMPORTANT: The value of ss->ssl3.hs.cookie is only valid while the * HelloVerifyRequest message remains valid. */ rv = ssl3_ConsumeHandshakeVariable(ss, &ss->ssl3.hs.cookie, 1, &b, &length); if (rv != SECSuccess) { goto loser; /* alert has been sent */ } if (ss->ssl3.hs.cookie.len > DTLS_COOKIE_BYTES) { desc = decode_error; goto alert_loser; /* malformed. */ } ssl_GetXmitBufLock(ss); /*******************************/ /* Now re-send the client hello */ rv = ssl3_SendClientHello(ss, client_hello_retransmit); ssl_ReleaseXmitBufLock(ss); /*******************************/ if (rv == SECSuccess) return rv; alert_loser: (void)SSL3_SendAlert(ss, alert_fatal, desc); loser: 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: ssl3_HandleRecord() */ int dtls_RecordGetRecvd(const DTLSRecvdRecords *records, sslSequenceNumber 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, sslSequenceNumber seq) { PRUint64 offset; if (seq < records->left) return; if (seq > records->right) { sslSequenceNumber new_left; sslSequenceNumber new_right; sslSequenceNumber 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; if (new_right > records->right + DTLS_RECVD_RECORDS_WINDOW) { PORT_Memset(records->data, 0, sizeof(records->data)); } else { 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_GetHandshakeTimeout(PRFileDesc *socket, PRIntervalTime *timeout) { sslSocket *ss = NULL; PRBool found = PR_FALSE; PRIntervalTime now = PR_IntervalNow(); PRIntervalTime to; unsigned int i; *timeout = PR_INTERVAL_NO_TIMEOUT; ss = ssl_FindSocket(socket); if (!ss) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } if (!IS_DTLS(ss)) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } for (i = 0; i < PR_ARRAY_SIZE(ss->ssl3.hs.timers); ++i) { PRIntervalTime elapsed; PRIntervalTime desired; dtlsTimer *timer = &ss->ssl3.hs.timers[i]; if (!timer->cb) { continue; } found = PR_TRUE; elapsed = now - timer->started; desired = PR_MillisecondsToInterval(timer->timeout); if (elapsed > desired) { /* Timer expired */ *timeout = PR_INTERVAL_NO_WAIT; return SECSuccess; } else { to = desired - elapsed; } if (*timeout > to) { *timeout = to; } } if (!found) { PORT_SetError(SSL_ERROR_NO_TIMERS_FOUND); return SECFailure; } return SECSuccess; } PRBool dtls_IsLongHeader(SSL3ProtocolVersion version, PRUint8 firstOctet) { #ifndef UNSAFE_FUZZER_MODE return version < SSL_LIBRARY_VERSION_TLS_1_3 || firstOctet == ssl_ct_handshake || firstOctet == ssl_ct_ack || firstOctet == ssl_ct_alert; #else return PR_TRUE; #endif } PRBool dtls_IsDtls13Ciphertext(SSL3ProtocolVersion version, PRUint8 firstOctet) { // Allow no version in case we haven't negotiated one yet. return (version == 0 || version >= SSL_LIBRARY_VERSION_TLS_1_3) && (firstOctet & 0xe0) == 0x20; } DTLSEpoch dtls_ReadEpoch(const ssl3CipherSpec *crSpec, const PRUint8 *hdr) { DTLSEpoch epoch; DTLSEpoch maxEpoch; DTLSEpoch partial; if (dtls_IsLongHeader(crSpec->version, hdr[0])) { return ((DTLSEpoch)hdr[3] << 8) | hdr[4]; } /* A lot of how we recover the epoch here will depend on how we plan to * manage KeyUpdate. In the case that we decide to install a new read spec * as a KeyUpdate is handled, crSpec will always be the highest epoch we can * possibly receive. That makes this easier to manage. */ if (dtls_IsDtls13Ciphertext(crSpec->version, hdr[0])) { /* TODO(ekr@rtfm.com: do something with the two-bit epoch. */ /* Use crSpec->epoch, or crSpec->epoch - 1 if the last bit differs. */ return crSpec->epoch - ((hdr[0] ^ crSpec->epoch) & 0x3); } /* dtls_GatherData should ensure that this works. */ PORT_Assert(hdr[0] == ssl_ct_application_data); /* This uses the same method as is used to recover the sequence number in * dtls_ReadSequenceNumber, except that the maximum value is set to the * current epoch. */ partial = hdr[1] >> 6; maxEpoch = PR_MAX(crSpec->epoch, 3); epoch = (maxEpoch & 0xfffc) | partial; if (partial > (maxEpoch & 0x03)) { epoch -= 4; } return epoch; } static sslSequenceNumber dtls_ReadSequenceNumber(const ssl3CipherSpec *spec, const PRUint8 *hdr) { sslSequenceNumber cap; sslSequenceNumber partial; sslSequenceNumber seqNum; sslSequenceNumber mask; if (dtls_IsLongHeader(spec->version, hdr[0])) { static const unsigned int seqNumOffset = 5; /* type, version, epoch */ static const unsigned int seqNumLength = 6; sslReader r = SSL_READER(hdr + seqNumOffset, seqNumLength); (void)sslRead_ReadNumber(&r, seqNumLength, &seqNum); return seqNum; } /* Only the least significant bits of the sequence number is available here. * This recovers the value based on the next expected sequence number. * * This works by determining the maximum possible sequence number, which is * half the range of possible values above the expected next value (the * expected next value is in |spec->seqNum|). Then, the last part of the * sequence number is replaced. If that causes the value to exceed the * maximum, subtract an entire range. */ if (hdr[0] & 0x08) { cap = spec->nextSeqNum + (1ULL << 15); partial = (((sslSequenceNumber)hdr[1]) << 8) | (sslSequenceNumber)hdr[2]; mask = (1ULL << 16) - 1; } else { cap = spec->nextSeqNum + (1ULL << 7); partial = (sslSequenceNumber)hdr[1]; mask = (1ULL << 8) - 1; } seqNum = (cap & ~mask) | partial; /* The second check prevents the value from underflowing if we get a large * gap at the start of a connection, where this subtraction would cause the * sequence number to wrap to near UINT64_MAX. */ if ((partial > (cap & mask)) && (seqNum > mask)) { seqNum -= mask + 1; } return seqNum; } /* * 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 the packet is not relevant, this function returns PR_FALSE. If the packet * is relevant, this function returns PR_TRUE and sets |*seqNumOut| to the * packet sequence number (removing the epoch). */ PRBool dtls_IsRelevant(sslSocket *ss, const ssl3CipherSpec *spec, const SSL3Ciphertext *cText, sslSequenceNumber *seqNumOut) { sslSequenceNumber seqNum = dtls_ReadSequenceNumber(spec, cText->hdr); if (dtls_RecordGetRecvd(&spec->recvdRecords, seqNum) != 0) { SSL_TRC(10, ("%d: SSL3[%d]: dtls_IsRelevant, rejecting " "potentially replayed packet", SSL_GETPID(), ss->fd)); return PR_FALSE; } *seqNumOut = seqNum; return PR_TRUE; } void dtls_ReceivedFirstMessageInFlight(sslSocket *ss) { if (!IS_DTLS(ss)) return; /* At this point we are advancing our state machine, so we can free our last * flight of messages. */ if (ss->ssl3.hs.ws != idle_handshake || ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) { /* We need to keep our last flight around in DTLS 1.2 and below, * so we can retransmit it in response to other people's * retransmits. */ dtls_FreeHandshakeMessages(&ss->ssl3.hs.lastMessageFlight); /* Reset the timer to the initial value if the retry counter * is 0, per RFC 6347, Sec. 4.2.4.1 */ dtls_CancelTimer(ss, ss->ssl3.hs.rtTimer); if (ss->ssl3.hs.rtRetries == 0) { ss->ssl3.hs.rtTimer->timeout = DTLS_RETRANSMIT_INITIAL_MS; } } /* Empty the ACK queue (TLS 1.3 only). */ ssl_ClearPRCList(&ss->ssl3.hs.dtlsRcvdHandshake, NULL); }