Blame nss/lib/ssl/dtlscon.c

Packit 40b132
/* This Source Code Form is subject to the terms of the Mozilla Public
Packit 40b132
 * License, v. 2.0. If a copy of the MPL was not distributed with this
Packit 40b132
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * DTLS Protocol
Packit 40b132
 */
Packit 40b132
Packit 40b132
#include "ssl.h"
Packit 40b132
#include "sslimpl.h"
Packit 40b132
#include "sslproto.h"
Packit 40b132
Packit 40b132
#ifndef PR_ARRAY_SIZE
Packit 40b132
#define PR_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
Packit 40b132
#endif
Packit 40b132
Packit 40b132
static SECStatus dtls_TransmitMessageFlight(sslSocket *ss);
Packit 40b132
static void dtls_RetransmitTimerExpiredCb(sslSocket *ss);
Packit 40b132
static SECStatus dtls_SendSavedWriteData(sslSocket *ss);
Packit 40b132
Packit 40b132
/* -28 adjusts for the IP/UDP header */
Packit 40b132
static const PRUint16 COMMON_MTU_VALUES[] = {
Packit 40b132
    1500 - 28,  /* Ethernet MTU */
Packit 40b132
    1280 - 28,  /* IPv6 minimum MTU */
Packit 40b132
    576 - 28,   /* Common assumption */
Packit 40b132
    256 - 28    /* We're in serious trouble now */
Packit 40b132
};
Packit 40b132
Packit 40b132
#define DTLS_COOKIE_BYTES 32
Packit 40b132
Packit 40b132
/* List copied from ssl3con.c:cipherSuites */
Packit 40b132
static const ssl3CipherSuite nonDTLSSuites[] = {
Packit 40b132
#ifndef NSS_DISABLE_ECC
Packit 40b132
    TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
Packit 40b132
    TLS_ECDHE_RSA_WITH_RC4_128_SHA,
Packit 40b132
#endif /* NSS_DISABLE_ECC */
Packit 40b132
    TLS_DHE_DSS_WITH_RC4_128_SHA,
Packit 40b132
#ifndef NSS_DISABLE_ECC
Packit 40b132
    TLS_ECDH_RSA_WITH_RC4_128_SHA,
Packit 40b132
    TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
Packit 40b132
#endif /* NSS_DISABLE_ECC */
Packit 40b132
    TLS_RSA_WITH_RC4_128_MD5,
Packit 40b132
    TLS_RSA_WITH_RC4_128_SHA,
Packit 40b132
    TLS_RSA_EXPORT1024_WITH_RC4_56_SHA,
Packit 40b132
    TLS_RSA_EXPORT_WITH_RC4_40_MD5,
Packit 40b132
    0 /* End of list marker */
Packit 40b132
};
Packit 40b132
Packit 40b132
/* Map back and forth between TLS and DTLS versions in wire format.
Packit 40b132
 * Mapping table is:
Packit 40b132
 *
Packit 40b132
 * TLS             DTLS
Packit 40b132
 * 1.1 (0302)      1.0 (feff)
Packit 40b132
 * 1.2 (0303)      1.2 (fefd)
Packit 40b132
 * 1.3 (0304)      1.3 (fefc)
Packit 40b132
 */
Packit 40b132
SSL3ProtocolVersion
Packit 40b132
dtls_TLSVersionToDTLSVersion(SSL3ProtocolVersion tlsv)
Packit 40b132
{
Packit 40b132
    if (tlsv == SSL_LIBRARY_VERSION_TLS_1_1) {
Packit 40b132
        return SSL_LIBRARY_VERSION_DTLS_1_0_WIRE;
Packit 40b132
    }
Packit 40b132
    if (tlsv == SSL_LIBRARY_VERSION_TLS_1_2) {
Packit 40b132
        return SSL_LIBRARY_VERSION_DTLS_1_2_WIRE;
Packit 40b132
    }
Packit 40b132
    if (tlsv == SSL_LIBRARY_VERSION_TLS_1_3) {
Packit 40b132
        return SSL_LIBRARY_VERSION_DTLS_1_3_WIRE;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* Anything other than TLS 1.1 or 1.2 is an error, so return
Packit 40b132
     * the invalid version 0xffff. */
Packit 40b132
    return 0xffff;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Map known DTLS versions to known TLS versions.
Packit 40b132
 * - Invalid versions (< 1.0) return a version of 0
Packit 40b132
 * - Versions > known return a version one higher than we know of
Packit 40b132
 * to accomodate a theoretically newer version */
Packit 40b132
SSL3ProtocolVersion
Packit 40b132
dtls_DTLSVersionToTLSVersion(SSL3ProtocolVersion dtlsv)
Packit 40b132
{
Packit 40b132
    if (MSB(dtlsv) == 0xff) {
Packit 40b132
        return 0;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (dtlsv == SSL_LIBRARY_VERSION_DTLS_1_0_WIRE) {
Packit 40b132
        return SSL_LIBRARY_VERSION_TLS_1_1;
Packit 40b132
    }
Packit 40b132
    if (dtlsv == SSL_LIBRARY_VERSION_DTLS_1_2_WIRE) {
Packit 40b132
        return SSL_LIBRARY_VERSION_TLS_1_2;
Packit 40b132
    }
Packit 40b132
    if (dtlsv == SSL_LIBRARY_VERSION_DTLS_1_3_WIRE) {
Packit 40b132
        return SSL_LIBRARY_VERSION_TLS_1_3;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* Return a fictional higher version than we know of */
Packit 40b132
    return SSL_LIBRARY_VERSION_TLS_1_2 + 1;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* On this socket, Disable non-DTLS cipher suites in the argument's list */
Packit 40b132
SECStatus
Packit 40b132
ssl3_DisableNonDTLSSuites(sslSocket * ss)
Packit 40b132
{
Packit 40b132
    const ssl3CipherSuite * suite;
Packit 40b132
Packit 40b132
    for (suite = nonDTLSSuites; *suite; ++suite) {
Packit 40b132
        SECStatus rv = ssl3_CipherPrefSet(ss, *suite, PR_FALSE);
Packit 40b132
Packit 40b132
        PORT_Assert(rv == SECSuccess); /* else is coding error */
Packit 40b132
    }
Packit 40b132
    return SECSuccess;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Allocate a DTLSQueuedMessage.
Packit 40b132
 *
Packit 40b132
 * Called from dtls_QueueMessage()
Packit 40b132
 */
Packit 40b132
static DTLSQueuedMessage *
Packit 40b132
dtls_AllocQueuedMessage(PRUint16 epoch, SSL3ContentType type,
Packit 40b132
                        const unsigned char *data, PRUint32 len)
Packit 40b132
{
Packit 40b132
    DTLSQueuedMessage *msg = NULL;
Packit 40b132
Packit 40b132
    msg = PORT_ZAlloc(sizeof(DTLSQueuedMessage));
Packit 40b132
    if (!msg)
Packit 40b132
        return NULL;
Packit 40b132
Packit 40b132
    msg->data = PORT_Alloc(len);
Packit 40b132
    if (!msg->data) {
Packit 40b132
        PORT_Free(msg);
Packit 40b132
        return NULL;
Packit 40b132
    }
Packit 40b132
    PORT_Memcpy(msg->data, data, len);
Packit 40b132
Packit 40b132
    msg->len = len;
Packit 40b132
    msg->epoch = epoch;
Packit 40b132
    msg->type = type;
Packit 40b132
Packit 40b132
    return msg;
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * Free a handshake message
Packit 40b132
 *
Packit 40b132
 * Called from dtls_FreeHandshakeMessages()
Packit 40b132
 */
Packit 40b132
static void
Packit 40b132
dtls_FreeHandshakeMessage(DTLSQueuedMessage *msg)
Packit 40b132
{
Packit 40b132
    if (!msg)
Packit 40b132
        return;
Packit 40b132
Packit 40b132
    PORT_ZFree(msg->data, msg->len);
Packit 40b132
    PORT_Free(msg);
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * Free a list of handshake messages
Packit 40b132
 *
Packit 40b132
 * Called from:
Packit 40b132
 *              dtls_HandleHandshake()
Packit 40b132
 *              ssl3_DestroySSL3Info()
Packit 40b132
 */
Packit 40b132
void
Packit 40b132
dtls_FreeHandshakeMessages(PRCList *list)
Packit 40b132
{
Packit 40b132
    PRCList *cur_p;
Packit 40b132
Packit 40b132
    while (!PR_CLIST_IS_EMPTY(list)) {
Packit 40b132
        cur_p = PR_LIST_TAIL(list);
Packit 40b132
        PR_REMOVE_LINK(cur_p);
Packit 40b132
        dtls_FreeHandshakeMessage((DTLSQueuedMessage *)cur_p);
Packit 40b132
    }
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Called only from ssl3_HandleRecord, for each (deciphered) DTLS record.
Packit 40b132
 * origBuf is the decrypted ssl record content and is expected to contain
Packit 40b132
 * complete handshake records
Packit 40b132
 * Caller must hold the handshake and RecvBuf locks.
Packit 40b132
 *
Packit 40b132
 * Note that this code uses msg_len for two purposes:
Packit 40b132
 *
Packit 40b132
 * (1) To pass the length to ssl3_HandleHandshakeMessage()
Packit 40b132
 * (2) To carry the length of a message currently being reassembled
Packit 40b132
 *
Packit 40b132
 * However, unlike ssl3_HandleHandshake(), it is not used to carry
Packit 40b132
 * the state of reassembly (i.e., whether one is in progress). That
Packit 40b132
 * is carried in recvdHighWater and recvdFragments.
Packit 40b132
 */
Packit 40b132
#define OFFSET_BYTE(o) (o/8)
Packit 40b132
#define OFFSET_MASK(o) (1 << (o%8))
Packit 40b132
Packit 40b132
SECStatus
Packit 40b132
dtls_HandleHandshake(sslSocket *ss, sslBuffer *origBuf)
Packit 40b132
{
Packit 40b132
    /* XXX OK for now.
Packit 40b132
     * This doesn't work properly with asynchronous certificate validation.
Packit 40b132
     * because that returns a WOULDBLOCK error. The current DTLS
Packit 40b132
     * applications do not need asynchronous validation, but in the
Packit 40b132
     * future we will need to add this.
Packit 40b132
     */
Packit 40b132
    sslBuffer buf = *origBuf;
Packit 40b132
    SECStatus rv = SECSuccess;
Packit 40b132
Packit 40b132
    PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
Packit 40b132
    PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
Packit 40b132
Packit 40b132
    while (buf.len > 0) {
Packit 40b132
        PRUint8 type;
Packit 40b132
        PRUint32 message_length;
Packit 40b132
        PRUint16 message_seq;
Packit 40b132
        PRUint32 fragment_offset;
Packit 40b132
        PRUint32 fragment_length;
Packit 40b132
        PRUint32 offset;
Packit 40b132
Packit 40b132
        if (buf.len < 12) {
Packit 40b132
            PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
Packit 40b132
            rv = SECFailure;
Packit 40b132
            break;
Packit 40b132
        }
Packit 40b132
Packit 40b132
        /* Parse the header */
Packit 40b132
        type = buf.buf[0];
Packit 40b132
        message_length = (buf.buf[1] << 16) | (buf.buf[2] << 8) | buf.buf[3];
Packit 40b132
        message_seq = (buf.buf[4] << 8) | buf.buf[5];
Packit 40b132
        fragment_offset = (buf.buf[6] << 16) | (buf.buf[7] << 8) | buf.buf[8];
Packit 40b132
        fragment_length = (buf.buf[9] << 16) | (buf.buf[10] << 8) | buf.buf[11];
Packit 40b132
Packit 40b132
#define MAX_HANDSHAKE_MSG_LEN 0x1ffff   /* 128k - 1 */
Packit 40b132
        if (message_length > MAX_HANDSHAKE_MSG_LEN) {
Packit 40b132
            (void)ssl3_DecodeError(ss);
Packit 40b132
            PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
Packit 40b132
            return SECFailure;
Packit 40b132
        }
Packit 40b132
#undef MAX_HANDSHAKE_MSG_LEN
Packit 40b132
Packit 40b132
        buf.buf += 12;
Packit 40b132
        buf.len -= 12;
Packit 40b132
Packit 40b132
        /* This fragment must be complete */
Packit 40b132
        if (buf.len < fragment_length) {
Packit 40b132
            PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
Packit 40b132
            rv = SECFailure;
Packit 40b132
            break;
Packit 40b132
        }
Packit 40b132
Packit 40b132
        /* Sanity check the packet contents */
Packit 40b132
        if ((fragment_length + fragment_offset) > message_length) {
Packit 40b132
            PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
Packit 40b132
            rv = SECFailure;
Packit 40b132
            break;
Packit 40b132
        }
Packit 40b132
Packit 40b132
        /* There are three ways we could not be ready for this packet.
Packit 40b132
         *
Packit 40b132
         * 1. It's a partial next message.
Packit 40b132
         * 2. It's a partial or complete message beyond the next
Packit 40b132
         * 3. It's a message we've already seen
Packit 40b132
         *
Packit 40b132
         * If it's the complete next message we accept it right away.
Packit 40b132
         * This is the common case for short messages
Packit 40b132
         */
Packit 40b132
        if ((message_seq == ss->ssl3.hs.recvMessageSeq)
Packit 40b132
            && (fragment_offset == 0)
Packit 40b132
            && (fragment_length == message_length)) {
Packit 40b132
            /* Complete next message. Process immediately */
Packit 40b132
            ss->ssl3.hs.msg_type = (SSL3HandshakeType)type;
Packit 40b132
            ss->ssl3.hs.msg_len = message_length;
Packit 40b132
Packit 40b132
            /* At this point we are advancing our state machine, so
Packit 40b132
             * we can free our last flight of messages */
Packit 40b132
            dtls_FreeHandshakeMessages(&ss->ssl3.hs.lastMessageFlight);
Packit 40b132
            ss->ssl3.hs.recvdHighWater = -1;
Packit 40b132
            dtls_CancelTimer(ss);
Packit 40b132
Packit 40b132
            /* Reset the timer to the initial value if the retry counter
Packit 40b132
             * is 0, per Sec. 4.2.4.1 */
Packit 40b132
            if (ss->ssl3.hs.rtRetries == 0) {
Packit 40b132
                ss->ssl3.hs.rtTimeoutMs = INITIAL_DTLS_TIMEOUT_MS;
Packit 40b132
            }
Packit 40b132
Packit 40b132
            rv = ssl3_HandleHandshakeMessage(ss, buf.buf, ss->ssl3.hs.msg_len);
Packit 40b132
            if (rv == SECFailure) {
Packit 40b132
                /* Do not attempt to process rest of messages in this record */
Packit 40b132
                break;
Packit 40b132
            }
Packit 40b132
        } else {
Packit 40b132
            if (message_seq < ss->ssl3.hs.recvMessageSeq) {
Packit 40b132
                /* Case 3: we do an immediate retransmit if we're
Packit 40b132
                 * in a waiting state*/
Packit 40b132
                if (ss->ssl3.hs.rtTimerCb == NULL) {
Packit 40b132
                    /* Ignore */
Packit 40b132
                } else if (ss->ssl3.hs.rtTimerCb ==
Packit 40b132
                         dtls_RetransmitTimerExpiredCb) {
Packit 40b132
                    SSL_TRC(30, ("%d: SSL3[%d]: Retransmit detected",
Packit 40b132
                                 SSL_GETPID(), ss->fd));
Packit 40b132
                    /* Check to see if we retransmitted recently. If so,
Packit 40b132
                     * suppress the triggered retransmit. This avoids
Packit 40b132
                     * retransmit wars after packet loss.
Packit 40b132
                     * This is not in RFC 5346 but should be
Packit 40b132
                     */
Packit 40b132
                    if ((PR_IntervalNow() - ss->ssl3.hs.rtTimerStarted) >
Packit 40b132
                        (ss->ssl3.hs.rtTimeoutMs / 4)) {
Packit 40b132
                            SSL_TRC(30,
Packit 40b132
                            ("%d: SSL3[%d]: Shortcutting retransmit timer",
Packit 40b132
                            SSL_GETPID(), ss->fd));
Packit 40b132
Packit 40b132
                            /* Cancel the timer and call the CB,
Packit 40b132
                             * which re-arms the timer */
Packit 40b132
                            dtls_CancelTimer(ss);
Packit 40b132
                            dtls_RetransmitTimerExpiredCb(ss);
Packit 40b132
                            rv = SECSuccess;
Packit 40b132
                            break;
Packit 40b132
                        } else {
Packit 40b132
                            SSL_TRC(30,
Packit 40b132
                            ("%d: SSL3[%d]: We just retransmitted. Ignoring.",
Packit 40b132
                            SSL_GETPID(), ss->fd));
Packit 40b132
                            rv = SECSuccess;
Packit 40b132
                            break;
Packit 40b132
                        }
Packit 40b132
                } else if (ss->ssl3.hs.rtTimerCb == dtls_FinishedTimerCb) {
Packit 40b132
                    /* Retransmit the messages and re-arm the timer
Packit 40b132
                     * Note that we are not backing off the timer here.
Packit 40b132
                     * The spec isn't clear and my reasoning is that this
Packit 40b132
                     * may be a re-ordered packet rather than slowness,
Packit 40b132
                     * so let's be aggressive. */
Packit 40b132
                    dtls_CancelTimer(ss);
Packit 40b132
                    rv = dtls_TransmitMessageFlight(ss);
Packit 40b132
                    if (rv == SECSuccess) {
Packit 40b132
                        rv = dtls_StartTimer(ss, dtls_FinishedTimerCb);
Packit 40b132
                    }
Packit 40b132
                    if (rv != SECSuccess)
Packit 40b132
                        return rv;
Packit 40b132
                    break;
Packit 40b132
                }
Packit 40b132
            } else if (message_seq > ss->ssl3.hs.recvMessageSeq) {
Packit 40b132
                /* Case 2
Packit 40b132
                 *
Packit 40b132
                 * Ignore this message. This means we don't handle out of
Packit 40b132
                 * order complete messages that well, but we're still
Packit 40b132
                 * compliant and this probably does not happen often
Packit 40b132
                 *
Packit 40b132
                 * XXX OK for now. Maybe do something smarter at some point?
Packit 40b132
                 */
Packit 40b132
            } else {
Packit 40b132
                /* Case 1
Packit 40b132
                 *
Packit 40b132
                 * Buffer the fragment for reassembly
Packit 40b132
                 */
Packit 40b132
                /* Make room for the message */
Packit 40b132
                if (ss->ssl3.hs.recvdHighWater == -1) {
Packit 40b132
                    PRUint32 map_length = OFFSET_BYTE(message_length) + 1;
Packit 40b132
Packit 40b132
                    rv = sslBuffer_Grow(&ss->ssl3.hs.msg_body, message_length);
Packit 40b132
                    if (rv != SECSuccess)
Packit 40b132
                        break;
Packit 40b132
                    /* Make room for the fragment map */
Packit 40b132
                    rv = sslBuffer_Grow(&ss->ssl3.hs.recvdFragments,
Packit 40b132
                                        map_length);
Packit 40b132
                    if (rv != SECSuccess)
Packit 40b132
                        break;
Packit 40b132
Packit 40b132
                    /* Reset the reassembly map */
Packit 40b132
                    ss->ssl3.hs.recvdHighWater = 0;
Packit 40b132
                    PORT_Memset(ss->ssl3.hs.recvdFragments.buf, 0,
Packit 40b132
                                ss->ssl3.hs.recvdFragments.space);
Packit 40b132
                    ss->ssl3.hs.msg_type = (SSL3HandshakeType)type;
Packit 40b132
                    ss->ssl3.hs.msg_len = message_length;
Packit 40b132
                }
Packit 40b132
Packit 40b132
                /* If we have a message length mismatch, abandon the reassembly
Packit 40b132
                 * in progress and hope that the next retransmit will give us
Packit 40b132
                 * something sane
Packit 40b132
                 */
Packit 40b132
                if (message_length != ss->ssl3.hs.msg_len) {
Packit 40b132
                    ss->ssl3.hs.recvdHighWater = -1;
Packit 40b132
                    PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
Packit 40b132
                    rv = SECFailure;
Packit 40b132
                    break;
Packit 40b132
                }
Packit 40b132
Packit 40b132
                /* Now copy this fragment into the buffer */
Packit 40b132
                PORT_Assert((fragment_offset + fragment_length) <=
Packit 40b132
                            ss->ssl3.hs.msg_body.space);
Packit 40b132
                PORT_Memcpy(ss->ssl3.hs.msg_body.buf + fragment_offset,
Packit 40b132
                            buf.buf, fragment_length);
Packit 40b132
Packit 40b132
                /* This logic is a bit tricky. We have two values for
Packit 40b132
                 * reassembly state:
Packit 40b132
                 *
Packit 40b132
                 * - recvdHighWater contains the highest contiguous number of
Packit 40b132
                 *   bytes received
Packit 40b132
                 * - recvdFragments contains a bitmask of packets received
Packit 40b132
                 *   above recvdHighWater
Packit 40b132
                 *
Packit 40b132
                 * This avoids having to fill in the bitmask in the common
Packit 40b132
                 * case of adjacent fragments received in sequence
Packit 40b132
                 */
Packit 40b132
                if (fragment_offset <= ss->ssl3.hs.recvdHighWater) {
Packit 40b132
                    /* Either this is the adjacent fragment or an overlapping
Packit 40b132
                     * fragment */
Packit 40b132
                    ss->ssl3.hs.recvdHighWater = fragment_offset +
Packit 40b132
                                                 fragment_length;
Packit 40b132
                } else {
Packit 40b132
                    for (offset = fragment_offset;
Packit 40b132
                         offset < fragment_offset + fragment_length;
Packit 40b132
                         offset++) {
Packit 40b132
                        ss->ssl3.hs.recvdFragments.buf[OFFSET_BYTE(offset)] |=
Packit 40b132
                            OFFSET_MASK(offset);
Packit 40b132
                    }
Packit 40b132
                }
Packit 40b132
Packit 40b132
                /* Now figure out the new high water mark if appropriate */
Packit 40b132
                for (offset = ss->ssl3.hs.recvdHighWater;
Packit 40b132
                     offset < ss->ssl3.hs.msg_len; offset++) {
Packit 40b132
                    /* Note that this loop is not efficient, since it counts
Packit 40b132
                     * bit by bit. If we have a lot of out-of-order packets,
Packit 40b132
                     * we should optimize this */
Packit 40b132
                    if (ss->ssl3.hs.recvdFragments.buf[OFFSET_BYTE(offset)] &
Packit 40b132
                        OFFSET_MASK(offset)) {
Packit 40b132
                        ss->ssl3.hs.recvdHighWater++;
Packit 40b132
                    } else {
Packit 40b132
                        break;
Packit 40b132
                    }
Packit 40b132
                }
Packit 40b132
Packit 40b132
                /* If we have all the bytes, then we are good to go */
Packit 40b132
                if (ss->ssl3.hs.recvdHighWater == ss->ssl3.hs.msg_len) {
Packit 40b132
                    ss->ssl3.hs.recvdHighWater = -1;
Packit 40b132
Packit 40b132
                    rv = ssl3_HandleHandshakeMessage(ss,
Packit 40b132
                                                     ss->ssl3.hs.msg_body.buf,
Packit 40b132
                                                     ss->ssl3.hs.msg_len);
Packit 40b132
                    if (rv == SECFailure)
Packit 40b132
                        break; /* Skip rest of record */
Packit 40b132
Packit 40b132
                    /* At this point we are advancing our state machine, so
Packit 40b132
                     * we can free our last flight of messages */
Packit 40b132
                    dtls_FreeHandshakeMessages(&ss->ssl3.hs.lastMessageFlight);
Packit 40b132
                    dtls_CancelTimer(ss);
Packit 40b132
Packit 40b132
                    /* If there have been no retries this time, reset the
Packit 40b132
                     * timer value to the default per Section 4.2.4.1 */
Packit 40b132
                    if (ss->ssl3.hs.rtRetries == 0) {
Packit 40b132
                        ss->ssl3.hs.rtTimeoutMs = INITIAL_DTLS_TIMEOUT_MS;
Packit 40b132
                    }
Packit 40b132
                }
Packit 40b132
            }
Packit 40b132
        }
Packit 40b132
Packit 40b132
        buf.buf += fragment_length;
Packit 40b132
        buf.len -= fragment_length;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    origBuf->len = 0;   /* So ssl3_GatherAppDataRecord will keep looping. */
Packit 40b132
Packit 40b132
    /* XXX OK for now. In future handle rv == SECWouldBlock safely in order
Packit 40b132
     * to deal with asynchronous certificate verification */
Packit 40b132
    return rv;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Enqueue a message (either handshake or CCS)
Packit 40b132
 *
Packit 40b132
 * Called from:
Packit 40b132
 *              dtls_StageHandshakeMessage()
Packit 40b132
 *              ssl3_SendChangeCipherSpecs()
Packit 40b132
 */
Packit 40b132
SECStatus dtls_QueueMessage(sslSocket *ss, SSL3ContentType type,
Packit 40b132
    const SSL3Opaque *pIn, PRInt32 nIn)
Packit 40b132
{
Packit 40b132
    SECStatus rv = SECSuccess;
Packit 40b132
    DTLSQueuedMessage *msg = NULL;
Packit 40b132
Packit 40b132
    PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
Packit 40b132
    PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
Packit 40b132
Packit 40b132
    msg = dtls_AllocQueuedMessage(ss->ssl3.cwSpec->epoch, type, pIn, nIn);
Packit 40b132
Packit 40b132
    if (!msg) {
Packit 40b132
        PORT_SetError(SEC_ERROR_NO_MEMORY);
Packit 40b132
        rv = SECFailure;
Packit 40b132
    } else {
Packit 40b132
        PR_APPEND_LINK(&msg->link, &ss->ssl3.hs.lastMessageFlight);
Packit 40b132
    }
Packit 40b132
Packit 40b132
    return rv;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Add DTLS handshake message to the pending queue
Packit 40b132
 * Empty the sendBuf buffer.
Packit 40b132
 * This function returns SECSuccess or SECFailure, never SECWouldBlock.
Packit 40b132
 * Always set sendBuf.len to 0, even when returning SECFailure.
Packit 40b132
 *
Packit 40b132
 * Called from:
Packit 40b132
 *              ssl3_AppendHandshakeHeader()
Packit 40b132
 *              dtls_FlushHandshake()
Packit 40b132
 */
Packit 40b132
SECStatus
Packit 40b132
dtls_StageHandshakeMessage(sslSocket *ss)
Packit 40b132
{
Packit 40b132
    SECStatus rv = SECSuccess;
Packit 40b132
Packit 40b132
    PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
Packit 40b132
    PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
Packit 40b132
Packit 40b132
    /* This function is sometimes called when no data is actually to
Packit 40b132
     * be staged, so just return SECSuccess. */
Packit 40b132
    if (!ss->sec.ci.sendBuf.buf || !ss->sec.ci.sendBuf.len)
Packit 40b132
        return rv;
Packit 40b132
Packit 40b132
    rv = dtls_QueueMessage(ss, content_handshake,
Packit 40b132
                           ss->sec.ci.sendBuf.buf, ss->sec.ci.sendBuf.len);
Packit 40b132
Packit 40b132
    /* Whether we succeeded or failed, toss the old handshake data. */
Packit 40b132
    ss->sec.ci.sendBuf.len = 0;
Packit 40b132
    return rv;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Enqueue the handshake message in sendBuf (if any) and then
Packit 40b132
 * transmit the resulting flight of handshake messages.
Packit 40b132
 *
Packit 40b132
 * Called from:
Packit 40b132
 *              ssl3_FlushHandshake()
Packit 40b132
 */
Packit 40b132
SECStatus
Packit 40b132
dtls_FlushHandshakeMessages(sslSocket *ss, PRInt32 flags)
Packit 40b132
{
Packit 40b132
    SECStatus rv = SECSuccess;
Packit 40b132
Packit 40b132
    PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
Packit 40b132
    PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
Packit 40b132
Packit 40b132
    rv = dtls_StageHandshakeMessage(ss);
Packit 40b132
    if (rv != SECSuccess)
Packit 40b132
        return rv;
Packit 40b132
Packit 40b132
    if (!(flags & ssl_SEND_FLAG_FORCE_INTO_BUFFER)) {
Packit 40b132
        rv = dtls_TransmitMessageFlight(ss);
Packit 40b132
        if (rv != SECSuccess)
Packit 40b132
            return rv;
Packit 40b132
Packit 40b132
        if (!(flags & ssl_SEND_FLAG_NO_RETRANSMIT)) {
Packit 40b132
            ss->ssl3.hs.rtRetries = 0;
Packit 40b132
            rv = dtls_StartTimer(ss, dtls_RetransmitTimerExpiredCb);
Packit 40b132
        }
Packit 40b132
    }
Packit 40b132
Packit 40b132
    return rv;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* The callback for when the retransmit timer expires
Packit 40b132
 *
Packit 40b132
 * Called from:
Packit 40b132
 *              dtls_CheckTimer()
Packit 40b132
 *              dtls_HandleHandshake()
Packit 40b132
 */
Packit 40b132
static void
Packit 40b132
dtls_RetransmitTimerExpiredCb(sslSocket *ss)
Packit 40b132
{
Packit 40b132
    SECStatus rv = SECFailure;
Packit 40b132
Packit 40b132
    ss->ssl3.hs.rtRetries++;
Packit 40b132
Packit 40b132
    if (!(ss->ssl3.hs.rtRetries % 3)) {
Packit 40b132
        /* If one of the messages was potentially greater than > MTU,
Packit 40b132
         * then downgrade. Do this every time we have retransmitted a
Packit 40b132
         * message twice, per RFC 6347 Sec. 4.1.1 */
Packit 40b132
        dtls_SetMTU(ss, ss->ssl3.hs.maxMessageSent - 1);
Packit 40b132
    }
Packit 40b132
Packit 40b132
    rv = dtls_TransmitMessageFlight(ss);
Packit 40b132
    if (rv == SECSuccess) {
Packit 40b132
Packit 40b132
        /* Re-arm the timer */
Packit 40b132
        rv = dtls_RestartTimer(ss, PR_TRUE, dtls_RetransmitTimerExpiredCb);
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (rv == SECFailure) {
Packit 40b132
        /* XXX OK for now. In future maybe signal the stack that we couldn't
Packit 40b132
         * transmit. For now, let the read handle any real network errors */
Packit 40b132
    }
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Transmit a flight of handshake messages, stuffing them
Packit 40b132
 * into as few records as seems reasonable
Packit 40b132
 *
Packit 40b132
 * Called from:
Packit 40b132
 *             dtls_FlushHandshake()
Packit 40b132
 *             dtls_RetransmitTimerExpiredCb()
Packit 40b132
 */
Packit 40b132
static SECStatus
Packit 40b132
dtls_TransmitMessageFlight(sslSocket *ss)
Packit 40b132
{
Packit 40b132
    SECStatus rv = SECSuccess;
Packit 40b132
    PRCList *msg_p;
Packit 40b132
    PRUint16 room_left = ss->ssl3.mtu;
Packit 40b132
    PRInt32 sent;
Packit 40b132
Packit 40b132
    ssl_GetXmitBufLock(ss);
Packit 40b132
    ssl_GetSpecReadLock(ss);
Packit 40b132
Packit 40b132
    /* DTLS does not buffer its handshake messages in
Packit 40b132
     * ss->pendingBuf, but rather in the lastMessageFlight
Packit 40b132
     * structure. This is just a sanity check that
Packit 40b132
     * some programming error hasn't inadvertantly
Packit 40b132
     * stuffed something in ss->pendingBuf
Packit 40b132
     */
Packit 40b132
    PORT_Assert(!ss->pendingBuf.len);
Packit 40b132
    for (msg_p = PR_LIST_HEAD(&ss->ssl3.hs.lastMessageFlight);
Packit 40b132
         msg_p != &ss->ssl3.hs.lastMessageFlight;
Packit 40b132
         msg_p = PR_NEXT_LINK(msg_p)) {
Packit 40b132
        DTLSQueuedMessage *msg = (DTLSQueuedMessage *)msg_p;
Packit 40b132
Packit 40b132
        /* The logic here is:
Packit 40b132
         *
Packit 40b132
         * 1. If this is a message that will not fit into the remaining
Packit 40b132
         *    space, then flush.
Packit 40b132
         * 2. If the message will now fit into the remaining space,
Packit 40b132
         *    encrypt, buffer, and loop.
Packit 40b132
         * 3. If the message will not fit, then fragment.
Packit 40b132
         *
Packit 40b132
         * At the end of the function, flush.
Packit 40b132
         */
Packit 40b132
        if ((msg->len + SSL3_BUFFER_FUDGE) > room_left) {
Packit 40b132
            /* The message will not fit into the remaining space, so flush */
Packit 40b132
            rv = dtls_SendSavedWriteData(ss);
Packit 40b132
            if (rv != SECSuccess)
Packit 40b132
                break;
Packit 40b132
Packit 40b132
            room_left = ss->ssl3.mtu;
Packit 40b132
        }
Packit 40b132
Packit 40b132
        if ((msg->len + SSL3_BUFFER_FUDGE) <= room_left) {
Packit 40b132
            /* The message will fit, so encrypt and then continue with the
Packit 40b132
             * next packet */
Packit 40b132
            sent = ssl3_SendRecord(ss, msg->epoch, msg->type,
Packit 40b132
                                   msg->data, msg->len,
Packit 40b132
                                   ssl_SEND_FLAG_FORCE_INTO_BUFFER |
Packit 40b132
                                   ssl_SEND_FLAG_USE_EPOCH);
Packit 40b132
            if (sent != msg->len) {
Packit 40b132
                rv = SECFailure;
Packit 40b132
                if (sent != -1) {
Packit 40b132
                    PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
Packit 40b132
                }
Packit 40b132
                break;
Packit 40b132
            }
Packit 40b132
Packit 40b132
            room_left = ss->ssl3.mtu - ss->pendingBuf.len;
Packit 40b132
        } else {
Packit 40b132
            /* The message will not fit, so fragment.
Packit 40b132
             *
Packit 40b132
             * XXX OK for now. Arrange to coalesce the last fragment
Packit 40b132
             * of this message with the next message if possible.
Packit 40b132
             * That would be more efficient.
Packit 40b132
             */
Packit 40b132
            PRUint32 fragment_offset = 0;
Packit 40b132
            unsigned char fragment[DTLS_MAX_MTU]; /* >= than largest
Packit 40b132
                                                   * plausible MTU */
Packit 40b132
Packit 40b132
            /* Assert that we have already flushed */
Packit 40b132
            PORT_Assert(room_left == ss->ssl3.mtu);
Packit 40b132
Packit 40b132
            /* Case 3: We now need to fragment this message
Packit 40b132
             * DTLS only supports fragmenting handshaking messages */
Packit 40b132
            PORT_Assert(msg->type == content_handshake);
Packit 40b132
Packit 40b132
            /* The headers consume 12 bytes so the smalles possible
Packit 40b132
             *  message (i.e., an empty one) is 12 bytes
Packit 40b132
             */
Packit 40b132
            PORT_Assert(msg->len >= 12);
Packit 40b132
Packit 40b132
            while ((fragment_offset + 12) < msg->len) {
Packit 40b132
                PRUint32 fragment_len;
Packit 40b132
                const unsigned char *content = msg->data + 12;
Packit 40b132
                PRUint32 content_len = msg->len - 12;
Packit 40b132
Packit 40b132
                /* The reason we use 8 here is that that's the length of
Packit 40b132
                 * the new DTLS data that we add to the header */
Packit 40b132
                fragment_len = PR_MIN(room_left - (SSL3_BUFFER_FUDGE + 8),
Packit 40b132
                                      content_len - fragment_offset);
Packit 40b132
                PORT_Assert(fragment_len < DTLS_MAX_MTU - 12);
Packit 40b132
                /* Make totally sure that we are within the buffer.
Packit 40b132
                 * Note that the only way that fragment len could get
Packit 40b132
                 * adjusted here is if
Packit 40b132
                 *
Packit 40b132
                 * (a) we are in release mode so the PORT_Assert is compiled out
Packit 40b132
                 * (b) either the MTU table is inconsistent with DTLS_MAX_MTU
Packit 40b132
                 * or ss->ssl3.mtu has become corrupt.
Packit 40b132
                 */
Packit 40b132
                fragment_len = PR_MIN(fragment_len, DTLS_MAX_MTU - 12);
Packit 40b132
Packit 40b132
                /* Construct an appropriate-sized fragment */
Packit 40b132
                /* Type, length, sequence */
Packit 40b132
                PORT_Memcpy(fragment, msg->data, 6);
Packit 40b132
Packit 40b132
                /* Offset */
Packit 40b132
                fragment[6] = (fragment_offset >> 16) & 0xff;
Packit 40b132
                fragment[7] = (fragment_offset >> 8) & 0xff;
Packit 40b132
                fragment[8] = (fragment_offset) & 0xff;
Packit 40b132
Packit 40b132
                /* Fragment length */
Packit 40b132
                fragment[9] = (fragment_len >> 16) & 0xff;
Packit 40b132
                fragment[10] = (fragment_len >> 8) & 0xff;
Packit 40b132
                fragment[11] = (fragment_len) & 0xff;
Packit 40b132
Packit 40b132
                PORT_Memcpy(fragment + 12, content + fragment_offset,
Packit 40b132
                            fragment_len);
Packit 40b132
Packit 40b132
                /*
Packit 40b132
                 *  Send the record. We do this in two stages
Packit 40b132
                 * 1. Encrypt
Packit 40b132
                 */
Packit 40b132
                sent = ssl3_SendRecord(ss, msg->epoch, msg->type,
Packit 40b132
                                       fragment, fragment_len + 12,
Packit 40b132
                                       ssl_SEND_FLAG_FORCE_INTO_BUFFER |
Packit 40b132
                                       ssl_SEND_FLAG_USE_EPOCH);
Packit 40b132
                if (sent != (fragment_len + 12)) {
Packit 40b132
                    rv = SECFailure;
Packit 40b132
                    if (sent != -1) {
Packit 40b132
                        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
Packit 40b132
                    }
Packit 40b132
                    break;
Packit 40b132
                }
Packit 40b132
Packit 40b132
                /* 2. Flush */
Packit 40b132
                rv = dtls_SendSavedWriteData(ss);
Packit 40b132
                if (rv != SECSuccess)
Packit 40b132
                    break;
Packit 40b132
Packit 40b132
                fragment_offset += fragment_len;
Packit 40b132
            }
Packit 40b132
        }
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* Finally, we need to flush */
Packit 40b132
    if (rv == SECSuccess)
Packit 40b132
        rv = dtls_SendSavedWriteData(ss);
Packit 40b132
Packit 40b132
    /* Give up the locks */
Packit 40b132
    ssl_ReleaseSpecReadLock(ss);
Packit 40b132
    ssl_ReleaseXmitBufLock(ss);
Packit 40b132
Packit 40b132
    return rv;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Flush the data in the pendingBuf and update the max message sent
Packit 40b132
 * so we can adjust the MTU estimate if we need to.
Packit 40b132
 * Wrapper for ssl_SendSavedWriteData.
Packit 40b132
 *
Packit 40b132
 * Called from dtls_TransmitMessageFlight()
Packit 40b132
 */
Packit 40b132
static
Packit 40b132
SECStatus dtls_SendSavedWriteData(sslSocket *ss)
Packit 40b132
{
Packit 40b132
    PRInt32 sent;
Packit 40b132
Packit 40b132
    sent = ssl_SendSavedWriteData(ss);
Packit 40b132
    if (sent < 0)
Packit 40b132
        return SECFailure;
Packit 40b132
Packit 40b132
    /* We should always have complete writes b/c datagram sockets
Packit 40b132
     * don't really block */
Packit 40b132
    if (ss->pendingBuf.len > 0) {
Packit 40b132
        ssl_MapLowLevelError(SSL_ERROR_SOCKET_WRITE_FAILURE);
Packit 40b132
        return SECFailure;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* Update the largest message sent so we can adjust the MTU
Packit 40b132
     * estimate if necessary */
Packit 40b132
    if (sent > ss->ssl3.hs.maxMessageSent)
Packit 40b132
        ss->ssl3.hs.maxMessageSent = sent;
Packit 40b132
Packit 40b132
    return SECSuccess;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Compress, MAC, encrypt a DTLS record. Allows specification of
Packit 40b132
 * the epoch using epoch value. If use_epoch is PR_TRUE then
Packit 40b132
 * we use the provided epoch. If use_epoch is PR_FALSE then
Packit 40b132
 * whatever the current value is in effect is used.
Packit 40b132
 *
Packit 40b132
 * Called from ssl3_SendRecord()
Packit 40b132
 */
Packit 40b132
SECStatus
Packit 40b132
dtls_CompressMACEncryptRecord(sslSocket *        ss,
Packit 40b132
                              DTLSEpoch          epoch,
Packit 40b132
                              PRBool             use_epoch,
Packit 40b132
                              SSL3ContentType    type,
Packit 40b132
                              const SSL3Opaque * pIn,
Packit 40b132
                              PRUint32           contentLen,
Packit 40b132
                              sslBuffer        * wrBuf)
Packit 40b132
{
Packit 40b132
    SECStatus rv = SECFailure;
Packit 40b132
    ssl3CipherSpec *          cwSpec;
Packit 40b132
Packit 40b132
    ssl_GetSpecReadLock(ss);    /********************************/
Packit 40b132
Packit 40b132
    /* The reason for this switch-hitting code is that we might have
Packit 40b132
     * a flight of records spanning an epoch boundary, e.g.,
Packit 40b132
     *
Packit 40b132
     * ClientKeyExchange (epoch = 0)
Packit 40b132
     * ChangeCipherSpec (epoch = 0)
Packit 40b132
     * Finished (epoch = 1)
Packit 40b132
     *
Packit 40b132
     * Thus, each record needs a different cipher spec. The information
Packit 40b132
     * about which epoch to use is carried with the record.
Packit 40b132
     */
Packit 40b132
    if (use_epoch) {
Packit 40b132
        if (ss->ssl3.cwSpec->epoch == epoch)
Packit 40b132
            cwSpec = ss->ssl3.cwSpec;
Packit 40b132
        else if (ss->ssl3.pwSpec->epoch == epoch)
Packit 40b132
            cwSpec = ss->ssl3.pwSpec;
Packit 40b132
        else
Packit 40b132
            cwSpec = NULL;
Packit 40b132
    } else {
Packit 40b132
        cwSpec = ss->ssl3.cwSpec;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (cwSpec) {
Packit 40b132
        rv = ssl3_CompressMACEncryptRecord(cwSpec, ss->sec.isServer, PR_TRUE,
Packit 40b132
                                           PR_FALSE, type, pIn, contentLen,
Packit 40b132
                                           wrBuf);
Packit 40b132
    } else {
Packit 40b132
        PR_NOT_REACHED("Couldn't find a cipher spec matching epoch");
Packit 40b132
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
Packit 40b132
    }
Packit 40b132
    ssl_ReleaseSpecReadLock(ss); /************************************/
Packit 40b132
Packit 40b132
    return rv;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Start a timer
Packit 40b132
 *
Packit 40b132
 * Called from:
Packit 40b132
 *             dtls_HandleHandshake()
Packit 40b132
 *             dtls_FlushHAndshake()
Packit 40b132
 *             dtls_RestartTimer()
Packit 40b132
 */
Packit 40b132
SECStatus
Packit 40b132
dtls_StartTimer(sslSocket *ss, DTLSTimerCb cb)
Packit 40b132
{
Packit 40b132
    PORT_Assert(ss->ssl3.hs.rtTimerCb == NULL);
Packit 40b132
Packit 40b132
    ss->ssl3.hs.rtTimerStarted = PR_IntervalNow();
Packit 40b132
    ss->ssl3.hs.rtTimerCb = cb;
Packit 40b132
Packit 40b132
    return SECSuccess;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Restart a timer with optional backoff
Packit 40b132
 *
Packit 40b132
 * Called from dtls_RetransmitTimerExpiredCb()
Packit 40b132
 */
Packit 40b132
SECStatus
Packit 40b132
dtls_RestartTimer(sslSocket *ss, PRBool backoff, DTLSTimerCb cb)
Packit 40b132
{
Packit 40b132
    if (backoff) {
Packit 40b132
        ss->ssl3.hs.rtTimeoutMs *= 2;
Packit 40b132
        if (ss->ssl3.hs.rtTimeoutMs > MAX_DTLS_TIMEOUT_MS)
Packit 40b132
            ss->ssl3.hs.rtTimeoutMs = MAX_DTLS_TIMEOUT_MS;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    return dtls_StartTimer(ss, cb);
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Cancel a pending timer
Packit 40b132
 *
Packit 40b132
 * Called from:
Packit 40b132
 *              dtls_HandleHandshake()
Packit 40b132
 *              dtls_CheckTimer()
Packit 40b132
 */
Packit 40b132
void
Packit 40b132
dtls_CancelTimer(sslSocket *ss)
Packit 40b132
{
Packit 40b132
    PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
Packit 40b132
Packit 40b132
    ss->ssl3.hs.rtTimerCb = NULL;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Check the pending timer and fire the callback if it expired
Packit 40b132
 *
Packit 40b132
 * Called from ssl3_GatherCompleteHandshake()
Packit 40b132
 */
Packit 40b132
void
Packit 40b132
dtls_CheckTimer(sslSocket *ss)
Packit 40b132
{
Packit 40b132
    if (!ss->ssl3.hs.rtTimerCb)
Packit 40b132
        return;
Packit 40b132
Packit 40b132
    if ((PR_IntervalNow() - ss->ssl3.hs.rtTimerStarted) >
Packit 40b132
        PR_MillisecondsToInterval(ss->ssl3.hs.rtTimeoutMs)) {
Packit 40b132
        /* Timer has expired */
Packit 40b132
        DTLSTimerCb cb = ss->ssl3.hs.rtTimerCb;
Packit 40b132
Packit 40b132
        /* Cancel the timer so that we can call the CB safely */
Packit 40b132
        dtls_CancelTimer(ss);
Packit 40b132
Packit 40b132
        /* Now call the CB */
Packit 40b132
        cb(ss);
Packit 40b132
    }
Packit 40b132
}
Packit 40b132
Packit 40b132
/* The callback to fire when the holddown timer for the Finished
Packit 40b132
 * message expires and we can delete it
Packit 40b132
 *
Packit 40b132
 * Called from dtls_CheckTimer()
Packit 40b132
 */
Packit 40b132
void
Packit 40b132
dtls_FinishedTimerCb(sslSocket *ss)
Packit 40b132
{
Packit 40b132
    ssl3_DestroyCipherSpec(ss->ssl3.pwSpec, PR_FALSE);
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Cancel the Finished hold-down timer and destroy the
Packit 40b132
 * pending cipher spec. Note that this means that
Packit 40b132
 * successive rehandshakes will fail if the Finished is
Packit 40b132
 * lost.
Packit 40b132
 *
Packit 40b132
 * XXX OK for now. Figure out how to handle the combination
Packit 40b132
 * of Finished lost and rehandshake
Packit 40b132
 */
Packit 40b132
void
Packit 40b132
dtls_RehandshakeCleanup(sslSocket *ss)
Packit 40b132
{
Packit 40b132
    dtls_CancelTimer(ss);
Packit 40b132
    ssl3_DestroyCipherSpec(ss->ssl3.pwSpec, PR_FALSE);
Packit 40b132
    ss->ssl3.hs.sendMessageSeq = 0;
Packit 40b132
    ss->ssl3.hs.recvMessageSeq = 0;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Set the MTU to the next step less than or equal to the
Packit 40b132
 * advertised value. Also used to downgrade the MTU by
Packit 40b132
 * doing dtls_SetMTU(ss, biggest packet set).
Packit 40b132
 *
Packit 40b132
 * Passing 0 means set this to the largest MTU known
Packit 40b132
 * (effectively resetting the PMTU backoff value).
Packit 40b132
 *
Packit 40b132
 * Called by:
Packit 40b132
 *            ssl3_InitState()
Packit 40b132
 *            dtls_RetransmitTimerExpiredCb()
Packit 40b132
 */
Packit 40b132
void
Packit 40b132
dtls_SetMTU(sslSocket *ss, PRUint16 advertised)
Packit 40b132
{
Packit 40b132
    int i;
Packit 40b132
Packit 40b132
    if (advertised == 0) {
Packit 40b132
        ss->ssl3.mtu = COMMON_MTU_VALUES[0];
Packit 40b132
        SSL_TRC(30, ("Resetting MTU to %d", ss->ssl3.mtu));
Packit 40b132
        return;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    for (i = 0; i < PR_ARRAY_SIZE(COMMON_MTU_VALUES); i++) {
Packit 40b132
        if (COMMON_MTU_VALUES[i] <= advertised) {
Packit 40b132
            ss->ssl3.mtu = COMMON_MTU_VALUES[i];
Packit 40b132
            SSL_TRC(30, ("Resetting MTU to %d", ss->ssl3.mtu));
Packit 40b132
            return;
Packit 40b132
        }
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* Fallback */
Packit 40b132
    ss->ssl3.mtu = COMMON_MTU_VALUES[PR_ARRAY_SIZE(COMMON_MTU_VALUES)-1];
Packit 40b132
    SSL_TRC(30, ("Resetting MTU to %d", ss->ssl3.mtu));
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Called from ssl3_HandleHandshakeMessage() when it has deciphered a
Packit 40b132
 * DTLS hello_verify_request
Packit 40b132
 * Caller must hold Handshake and RecvBuf locks.
Packit 40b132
 */
Packit 40b132
SECStatus
Packit 40b132
dtls_HandleHelloVerifyRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
Packit 40b132
{
Packit 40b132
    int                 errCode = SSL_ERROR_RX_MALFORMED_HELLO_VERIFY_REQUEST;
Packit 40b132
    SECStatus           rv;
Packit 40b132
    PRInt32             temp;
Packit 40b132
    SECItem             cookie = {siBuffer, NULL, 0};
Packit 40b132
    SSL3AlertDescription desc   = illegal_parameter;
Packit 40b132
Packit 40b132
    SSL_TRC(3, ("%d: SSL3[%d]: handle hello_verify_request handshake",
Packit 40b132
        SSL_GETPID(), ss->fd));
Packit 40b132
    PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
Packit 40b132
    PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
Packit 40b132
Packit 40b132
    if (ss->ssl3.hs.ws != wait_server_hello) {
Packit 40b132
        errCode = SSL_ERROR_RX_UNEXPECTED_HELLO_VERIFY_REQUEST;
Packit 40b132
        desc    = unexpected_message;
Packit 40b132
        goto alert_loser;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* The version */
Packit 40b132
    temp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
Packit 40b132
    if (temp < 0) {
Packit 40b132
        goto loser;     /* alert has been sent */
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (temp != SSL_LIBRARY_VERSION_DTLS_1_0_WIRE &&
Packit 40b132
        temp != SSL_LIBRARY_VERSION_DTLS_1_2_WIRE) {
Packit 40b132
        goto alert_loser;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* The cookie */
Packit 40b132
    rv = ssl3_ConsumeHandshakeVariable(ss, &cookie, 1, &b, &length);
Packit 40b132
    if (rv != SECSuccess) {
Packit 40b132
        goto loser;     /* alert has been sent */
Packit 40b132
    }
Packit 40b132
    if (cookie.len > DTLS_COOKIE_BYTES) {
Packit 40b132
        desc = decode_error;
Packit 40b132
        goto alert_loser;       /* malformed. */
Packit 40b132
    }
Packit 40b132
Packit 40b132
    PORT_Memcpy(ss->ssl3.hs.cookie, cookie.data, cookie.len);
Packit 40b132
    ss->ssl3.hs.cookieLen = cookie.len;
Packit 40b132
Packit 40b132
Packit 40b132
    ssl_GetXmitBufLock(ss);             /*******************************/
Packit 40b132
Packit 40b132
    /* Now re-send the client hello */
Packit 40b132
    rv = ssl3_SendClientHello(ss, PR_TRUE);
Packit 40b132
Packit 40b132
    ssl_ReleaseXmitBufLock(ss);         /*******************************/
Packit 40b132
Packit 40b132
    if (rv == SECSuccess)
Packit 40b132
        return rv;
Packit 40b132
Packit 40b132
alert_loser:
Packit 40b132
    (void)SSL3_SendAlert(ss, alert_fatal, desc);
Packit 40b132
Packit 40b132
loser:
Packit 40b132
    errCode = ssl_MapLowLevelError(errCode);
Packit 40b132
    return SECFailure;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Initialize the DTLS anti-replay window
Packit 40b132
 *
Packit 40b132
 * Called from:
Packit 40b132
 *              ssl3_SetupPendingCipherSpec()
Packit 40b132
 *              ssl3_InitCipherSpec()
Packit 40b132
 */
Packit 40b132
void
Packit 40b132
dtls_InitRecvdRecords(DTLSRecvdRecords *records)
Packit 40b132
{
Packit 40b132
    PORT_Memset(records->data, 0, sizeof(records->data));
Packit 40b132
    records->left = 0;
Packit 40b132
    records->right = DTLS_RECVD_RECORDS_WINDOW - 1;
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * Has this DTLS record been received? Return values are:
Packit 40b132
 * -1 -- out of range to the left
Packit 40b132
 *  0 -- not received yet
Packit 40b132
 *  1 -- replay
Packit 40b132
 *
Packit 40b132
 *  Called from: dtls_HandleRecord()
Packit 40b132
 */
Packit 40b132
int
Packit 40b132
dtls_RecordGetRecvd(DTLSRecvdRecords *records, PRUint64 seq)
Packit 40b132
{
Packit 40b132
    PRUint64 offset;
Packit 40b132
Packit 40b132
    /* Out of range to the left */
Packit 40b132
    if (seq < records->left) {
Packit 40b132
        return -1;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* Out of range to the right; since we advance the window on
Packit 40b132
     * receipt, that means that this packet has not been received
Packit 40b132
     * yet */
Packit 40b132
    if (seq > records->right)
Packit 40b132
        return 0;
Packit 40b132
Packit 40b132
    offset = seq % DTLS_RECVD_RECORDS_WINDOW;
Packit 40b132
Packit 40b132
    return !!(records->data[offset / 8] & (1 << (offset % 8)));
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Update the DTLS anti-replay window
Packit 40b132
 *
Packit 40b132
 * Called from ssl3_HandleRecord()
Packit 40b132
 */
Packit 40b132
void
Packit 40b132
dtls_RecordSetRecvd(DTLSRecvdRecords *records, PRUint64 seq)
Packit 40b132
{
Packit 40b132
    PRUint64 offset;
Packit 40b132
Packit 40b132
    if (seq < records->left)
Packit 40b132
        return;
Packit 40b132
Packit 40b132
    if (seq > records->right) {
Packit 40b132
        PRUint64 new_left;
Packit 40b132
        PRUint64 new_right;
Packit 40b132
        PRUint64 right;
Packit 40b132
Packit 40b132
        /* Slide to the right; this is the tricky part
Packit 40b132
         *
Packit 40b132
         * 1. new_top is set to have room for seq, on the
Packit 40b132
         *    next byte boundary by setting the right 8
Packit 40b132
         *    bits of seq
Packit 40b132
         * 2. new_left is set to compensate.
Packit 40b132
         * 3. Zero all bits between top and new_top. Since
Packit 40b132
         *    this is a ring, this zeroes everything as-yet
Packit 40b132
         *    unseen. Because we always operate on byte
Packit 40b132
         *    boundaries, we can zero one byte at a time
Packit 40b132
         */
Packit 40b132
        new_right = seq | 0x07;
Packit 40b132
        new_left = (new_right - DTLS_RECVD_RECORDS_WINDOW) + 1;
Packit 40b132
Packit 40b132
        for (right = records->right + 8; right <= new_right; right += 8) {
Packit 40b132
            offset = right % DTLS_RECVD_RECORDS_WINDOW;
Packit 40b132
            records->data[offset / 8] = 0;
Packit 40b132
        }
Packit 40b132
Packit 40b132
        records->right = new_right;
Packit 40b132
        records->left = new_left;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    offset = seq % DTLS_RECVD_RECORDS_WINDOW;
Packit 40b132
Packit 40b132
    records->data[offset / 8] |= (1 << (offset % 8));
Packit 40b132
}
Packit 40b132
Packit 40b132
SECStatus
Packit 40b132
DTLS_GetHandshakeTimeout(PRFileDesc *socket, PRIntervalTime *timeout)
Packit 40b132
{
Packit 40b132
    sslSocket * ss = NULL;
Packit 40b132
    PRIntervalTime elapsed;
Packit 40b132
    PRIntervalTime desired;
Packit 40b132
Packit 40b132
    ss = ssl_FindSocket(socket);
Packit 40b132
Packit 40b132
    if (!ss)
Packit 40b132
        return SECFailure;
Packit 40b132
Packit 40b132
    if (!IS_DTLS(ss))
Packit 40b132
        return SECFailure;
Packit 40b132
Packit 40b132
    if (!ss->ssl3.hs.rtTimerCb)
Packit 40b132
        return SECFailure;
Packit 40b132
Packit 40b132
    elapsed = PR_IntervalNow() - ss->ssl3.hs.rtTimerStarted;
Packit 40b132
    desired = PR_MillisecondsToInterval(ss->ssl3.hs.rtTimeoutMs);
Packit 40b132
    if (elapsed > desired) {
Packit 40b132
        /* Timer expired */
Packit 40b132
        *timeout = PR_INTERVAL_NO_WAIT;
Packit 40b132
    } else {
Packit 40b132
        *timeout = desired - elapsed;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    return SECSuccess;
Packit 40b132
}