Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | /* |
michael@0 | 6 | * DTLS Protocol |
michael@0 | 7 | */ |
michael@0 | 8 | |
michael@0 | 9 | #include "ssl.h" |
michael@0 | 10 | #include "sslimpl.h" |
michael@0 | 11 | #include "sslproto.h" |
michael@0 | 12 | |
michael@0 | 13 | #ifndef PR_ARRAY_SIZE |
michael@0 | 14 | #define PR_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) |
michael@0 | 15 | #endif |
michael@0 | 16 | |
michael@0 | 17 | static SECStatus dtls_TransmitMessageFlight(sslSocket *ss); |
michael@0 | 18 | static void dtls_RetransmitTimerExpiredCb(sslSocket *ss); |
michael@0 | 19 | static SECStatus dtls_SendSavedWriteData(sslSocket *ss); |
michael@0 | 20 | |
michael@0 | 21 | /* -28 adjusts for the IP/UDP header */ |
michael@0 | 22 | static const PRUint16 COMMON_MTU_VALUES[] = { |
michael@0 | 23 | 1500 - 28, /* Ethernet MTU */ |
michael@0 | 24 | 1280 - 28, /* IPv6 minimum MTU */ |
michael@0 | 25 | 576 - 28, /* Common assumption */ |
michael@0 | 26 | 256 - 28 /* We're in serious trouble now */ |
michael@0 | 27 | }; |
michael@0 | 28 | |
michael@0 | 29 | #define DTLS_COOKIE_BYTES 32 |
michael@0 | 30 | |
michael@0 | 31 | /* List copied from ssl3con.c:cipherSuites */ |
michael@0 | 32 | static const ssl3CipherSuite nonDTLSSuites[] = { |
michael@0 | 33 | #ifndef NSS_DISABLE_ECC |
michael@0 | 34 | TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, |
michael@0 | 35 | TLS_ECDHE_RSA_WITH_RC4_128_SHA, |
michael@0 | 36 | #endif /* NSS_DISABLE_ECC */ |
michael@0 | 37 | TLS_DHE_DSS_WITH_RC4_128_SHA, |
michael@0 | 38 | #ifndef NSS_DISABLE_ECC |
michael@0 | 39 | TLS_ECDH_RSA_WITH_RC4_128_SHA, |
michael@0 | 40 | TLS_ECDH_ECDSA_WITH_RC4_128_SHA, |
michael@0 | 41 | #endif /* NSS_DISABLE_ECC */ |
michael@0 | 42 | TLS_RSA_WITH_RC4_128_MD5, |
michael@0 | 43 | TLS_RSA_WITH_RC4_128_SHA, |
michael@0 | 44 | TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, |
michael@0 | 45 | TLS_RSA_EXPORT_WITH_RC4_40_MD5, |
michael@0 | 46 | 0 /* End of list marker */ |
michael@0 | 47 | }; |
michael@0 | 48 | |
michael@0 | 49 | /* Map back and forth between TLS and DTLS versions in wire format. |
michael@0 | 50 | * Mapping table is: |
michael@0 | 51 | * |
michael@0 | 52 | * TLS DTLS |
michael@0 | 53 | * 1.1 (0302) 1.0 (feff) |
michael@0 | 54 | * 1.2 (0303) 1.2 (fefd) |
michael@0 | 55 | */ |
michael@0 | 56 | SSL3ProtocolVersion |
michael@0 | 57 | dtls_TLSVersionToDTLSVersion(SSL3ProtocolVersion tlsv) |
michael@0 | 58 | { |
michael@0 | 59 | if (tlsv == SSL_LIBRARY_VERSION_TLS_1_1) { |
michael@0 | 60 | return SSL_LIBRARY_VERSION_DTLS_1_0_WIRE; |
michael@0 | 61 | } |
michael@0 | 62 | if (tlsv == SSL_LIBRARY_VERSION_TLS_1_2) { |
michael@0 | 63 | return SSL_LIBRARY_VERSION_DTLS_1_2_WIRE; |
michael@0 | 64 | } |
michael@0 | 65 | |
michael@0 | 66 | /* Anything other than TLS 1.1 or 1.2 is an error, so return |
michael@0 | 67 | * the invalid version 0xffff. */ |
michael@0 | 68 | return 0xffff; |
michael@0 | 69 | } |
michael@0 | 70 | |
michael@0 | 71 | /* Map known DTLS versions to known TLS versions. |
michael@0 | 72 | * - Invalid versions (< 1.0) return a version of 0 |
michael@0 | 73 | * - Versions > known return a version one higher than we know of |
michael@0 | 74 | * to accomodate a theoretically newer version */ |
michael@0 | 75 | SSL3ProtocolVersion |
michael@0 | 76 | dtls_DTLSVersionToTLSVersion(SSL3ProtocolVersion dtlsv) |
michael@0 | 77 | { |
michael@0 | 78 | if (MSB(dtlsv) == 0xff) { |
michael@0 | 79 | return 0; |
michael@0 | 80 | } |
michael@0 | 81 | |
michael@0 | 82 | if (dtlsv == SSL_LIBRARY_VERSION_DTLS_1_0_WIRE) { |
michael@0 | 83 | return SSL_LIBRARY_VERSION_TLS_1_1; |
michael@0 | 84 | } |
michael@0 | 85 | if (dtlsv == SSL_LIBRARY_VERSION_DTLS_1_2_WIRE) { |
michael@0 | 86 | return SSL_LIBRARY_VERSION_TLS_1_2; |
michael@0 | 87 | } |
michael@0 | 88 | |
michael@0 | 89 | /* Return a fictional higher version than we know of */ |
michael@0 | 90 | return SSL_LIBRARY_VERSION_TLS_1_2 + 1; |
michael@0 | 91 | } |
michael@0 | 92 | |
michael@0 | 93 | /* On this socket, Disable non-DTLS cipher suites in the argument's list */ |
michael@0 | 94 | SECStatus |
michael@0 | 95 | ssl3_DisableNonDTLSSuites(sslSocket * ss) |
michael@0 | 96 | { |
michael@0 | 97 | const ssl3CipherSuite * suite; |
michael@0 | 98 | |
michael@0 | 99 | for (suite = nonDTLSSuites; *suite; ++suite) { |
michael@0 | 100 | SECStatus rv = ssl3_CipherPrefSet(ss, *suite, PR_FALSE); |
michael@0 | 101 | |
michael@0 | 102 | PORT_Assert(rv == SECSuccess); /* else is coding error */ |
michael@0 | 103 | } |
michael@0 | 104 | return SECSuccess; |
michael@0 | 105 | } |
michael@0 | 106 | |
michael@0 | 107 | /* Allocate a DTLSQueuedMessage. |
michael@0 | 108 | * |
michael@0 | 109 | * Called from dtls_QueueMessage() |
michael@0 | 110 | */ |
michael@0 | 111 | static DTLSQueuedMessage * |
michael@0 | 112 | dtls_AllocQueuedMessage(PRUint16 epoch, SSL3ContentType type, |
michael@0 | 113 | const unsigned char *data, PRUint32 len) |
michael@0 | 114 | { |
michael@0 | 115 | DTLSQueuedMessage *msg = NULL; |
michael@0 | 116 | |
michael@0 | 117 | msg = PORT_ZAlloc(sizeof(DTLSQueuedMessage)); |
michael@0 | 118 | if (!msg) |
michael@0 | 119 | return NULL; |
michael@0 | 120 | |
michael@0 | 121 | msg->data = PORT_Alloc(len); |
michael@0 | 122 | if (!msg->data) { |
michael@0 | 123 | PORT_Free(msg); |
michael@0 | 124 | return NULL; |
michael@0 | 125 | } |
michael@0 | 126 | PORT_Memcpy(msg->data, data, len); |
michael@0 | 127 | |
michael@0 | 128 | msg->len = len; |
michael@0 | 129 | msg->epoch = epoch; |
michael@0 | 130 | msg->type = type; |
michael@0 | 131 | |
michael@0 | 132 | return msg; |
michael@0 | 133 | } |
michael@0 | 134 | |
michael@0 | 135 | /* |
michael@0 | 136 | * Free a handshake message |
michael@0 | 137 | * |
michael@0 | 138 | * Called from dtls_FreeHandshakeMessages() |
michael@0 | 139 | */ |
michael@0 | 140 | static void |
michael@0 | 141 | dtls_FreeHandshakeMessage(DTLSQueuedMessage *msg) |
michael@0 | 142 | { |
michael@0 | 143 | if (!msg) |
michael@0 | 144 | return; |
michael@0 | 145 | |
michael@0 | 146 | PORT_ZFree(msg->data, msg->len); |
michael@0 | 147 | PORT_Free(msg); |
michael@0 | 148 | } |
michael@0 | 149 | |
michael@0 | 150 | /* |
michael@0 | 151 | * Free a list of handshake messages |
michael@0 | 152 | * |
michael@0 | 153 | * Called from: |
michael@0 | 154 | * dtls_HandleHandshake() |
michael@0 | 155 | * ssl3_DestroySSL3Info() |
michael@0 | 156 | */ |
michael@0 | 157 | void |
michael@0 | 158 | dtls_FreeHandshakeMessages(PRCList *list) |
michael@0 | 159 | { |
michael@0 | 160 | PRCList *cur_p; |
michael@0 | 161 | |
michael@0 | 162 | while (!PR_CLIST_IS_EMPTY(list)) { |
michael@0 | 163 | cur_p = PR_LIST_TAIL(list); |
michael@0 | 164 | PR_REMOVE_LINK(cur_p); |
michael@0 | 165 | dtls_FreeHandshakeMessage((DTLSQueuedMessage *)cur_p); |
michael@0 | 166 | } |
michael@0 | 167 | } |
michael@0 | 168 | |
michael@0 | 169 | /* Called only from ssl3_HandleRecord, for each (deciphered) DTLS record. |
michael@0 | 170 | * origBuf is the decrypted ssl record content and is expected to contain |
michael@0 | 171 | * complete handshake records |
michael@0 | 172 | * Caller must hold the handshake and RecvBuf locks. |
michael@0 | 173 | * |
michael@0 | 174 | * Note that this code uses msg_len for two purposes: |
michael@0 | 175 | * |
michael@0 | 176 | * (1) To pass the length to ssl3_HandleHandshakeMessage() |
michael@0 | 177 | * (2) To carry the length of a message currently being reassembled |
michael@0 | 178 | * |
michael@0 | 179 | * However, unlike ssl3_HandleHandshake(), it is not used to carry |
michael@0 | 180 | * the state of reassembly (i.e., whether one is in progress). That |
michael@0 | 181 | * is carried in recvdHighWater and recvdFragments. |
michael@0 | 182 | */ |
michael@0 | 183 | #define OFFSET_BYTE(o) (o/8) |
michael@0 | 184 | #define OFFSET_MASK(o) (1 << (o%8)) |
michael@0 | 185 | |
michael@0 | 186 | SECStatus |
michael@0 | 187 | dtls_HandleHandshake(sslSocket *ss, sslBuffer *origBuf) |
michael@0 | 188 | { |
michael@0 | 189 | /* XXX OK for now. |
michael@0 | 190 | * This doesn't work properly with asynchronous certificate validation. |
michael@0 | 191 | * because that returns a WOULDBLOCK error. The current DTLS |
michael@0 | 192 | * applications do not need asynchronous validation, but in the |
michael@0 | 193 | * future we will need to add this. |
michael@0 | 194 | */ |
michael@0 | 195 | sslBuffer buf = *origBuf; |
michael@0 | 196 | SECStatus rv = SECSuccess; |
michael@0 | 197 | |
michael@0 | 198 | PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); |
michael@0 | 199 | PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); |
michael@0 | 200 | |
michael@0 | 201 | while (buf.len > 0) { |
michael@0 | 202 | PRUint8 type; |
michael@0 | 203 | PRUint32 message_length; |
michael@0 | 204 | PRUint16 message_seq; |
michael@0 | 205 | PRUint32 fragment_offset; |
michael@0 | 206 | PRUint32 fragment_length; |
michael@0 | 207 | PRUint32 offset; |
michael@0 | 208 | |
michael@0 | 209 | if (buf.len < 12) { |
michael@0 | 210 | PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE); |
michael@0 | 211 | rv = SECFailure; |
michael@0 | 212 | break; |
michael@0 | 213 | } |
michael@0 | 214 | |
michael@0 | 215 | /* Parse the header */ |
michael@0 | 216 | type = buf.buf[0]; |
michael@0 | 217 | message_length = (buf.buf[1] << 16) | (buf.buf[2] << 8) | buf.buf[3]; |
michael@0 | 218 | message_seq = (buf.buf[4] << 8) | buf.buf[5]; |
michael@0 | 219 | fragment_offset = (buf.buf[6] << 16) | (buf.buf[7] << 8) | buf.buf[8]; |
michael@0 | 220 | fragment_length = (buf.buf[9] << 16) | (buf.buf[10] << 8) | buf.buf[11]; |
michael@0 | 221 | |
michael@0 | 222 | #define MAX_HANDSHAKE_MSG_LEN 0x1ffff /* 128k - 1 */ |
michael@0 | 223 | if (message_length > MAX_HANDSHAKE_MSG_LEN) { |
michael@0 | 224 | (void)ssl3_DecodeError(ss); |
michael@0 | 225 | PORT_SetError(SSL_ERROR_RX_RECORD_TOO_LONG); |
michael@0 | 226 | return SECFailure; |
michael@0 | 227 | } |
michael@0 | 228 | #undef MAX_HANDSHAKE_MSG_LEN |
michael@0 | 229 | |
michael@0 | 230 | buf.buf += 12; |
michael@0 | 231 | buf.len -= 12; |
michael@0 | 232 | |
michael@0 | 233 | /* This fragment must be complete */ |
michael@0 | 234 | if (buf.len < fragment_length) { |
michael@0 | 235 | PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE); |
michael@0 | 236 | rv = SECFailure; |
michael@0 | 237 | break; |
michael@0 | 238 | } |
michael@0 | 239 | |
michael@0 | 240 | /* Sanity check the packet contents */ |
michael@0 | 241 | if ((fragment_length + fragment_offset) > message_length) { |
michael@0 | 242 | PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE); |
michael@0 | 243 | rv = SECFailure; |
michael@0 | 244 | break; |
michael@0 | 245 | } |
michael@0 | 246 | |
michael@0 | 247 | /* There are three ways we could not be ready for this packet. |
michael@0 | 248 | * |
michael@0 | 249 | * 1. It's a partial next message. |
michael@0 | 250 | * 2. It's a partial or complete message beyond the next |
michael@0 | 251 | * 3. It's a message we've already seen |
michael@0 | 252 | * |
michael@0 | 253 | * If it's the complete next message we accept it right away. |
michael@0 | 254 | * This is the common case for short messages |
michael@0 | 255 | */ |
michael@0 | 256 | if ((message_seq == ss->ssl3.hs.recvMessageSeq) |
michael@0 | 257 | && (fragment_offset == 0) |
michael@0 | 258 | && (fragment_length == message_length)) { |
michael@0 | 259 | /* Complete next message. Process immediately */ |
michael@0 | 260 | ss->ssl3.hs.msg_type = (SSL3HandshakeType)type; |
michael@0 | 261 | ss->ssl3.hs.msg_len = message_length; |
michael@0 | 262 | |
michael@0 | 263 | /* At this point we are advancing our state machine, so |
michael@0 | 264 | * we can free our last flight of messages */ |
michael@0 | 265 | dtls_FreeHandshakeMessages(&ss->ssl3.hs.lastMessageFlight); |
michael@0 | 266 | ss->ssl3.hs.recvdHighWater = -1; |
michael@0 | 267 | dtls_CancelTimer(ss); |
michael@0 | 268 | |
michael@0 | 269 | /* Reset the timer to the initial value if the retry counter |
michael@0 | 270 | * is 0, per Sec. 4.2.4.1 */ |
michael@0 | 271 | if (ss->ssl3.hs.rtRetries == 0) { |
michael@0 | 272 | ss->ssl3.hs.rtTimeoutMs = INITIAL_DTLS_TIMEOUT_MS; |
michael@0 | 273 | } |
michael@0 | 274 | |
michael@0 | 275 | rv = ssl3_HandleHandshakeMessage(ss, buf.buf, ss->ssl3.hs.msg_len); |
michael@0 | 276 | if (rv == SECFailure) { |
michael@0 | 277 | /* Do not attempt to process rest of messages in this record */ |
michael@0 | 278 | break; |
michael@0 | 279 | } |
michael@0 | 280 | } else { |
michael@0 | 281 | if (message_seq < ss->ssl3.hs.recvMessageSeq) { |
michael@0 | 282 | /* Case 3: we do an immediate retransmit if we're |
michael@0 | 283 | * in a waiting state*/ |
michael@0 | 284 | if (ss->ssl3.hs.rtTimerCb == NULL) { |
michael@0 | 285 | /* Ignore */ |
michael@0 | 286 | } else if (ss->ssl3.hs.rtTimerCb == |
michael@0 | 287 | dtls_RetransmitTimerExpiredCb) { |
michael@0 | 288 | SSL_TRC(30, ("%d: SSL3[%d]: Retransmit detected", |
michael@0 | 289 | SSL_GETPID(), ss->fd)); |
michael@0 | 290 | /* Check to see if we retransmitted recently. If so, |
michael@0 | 291 | * suppress the triggered retransmit. This avoids |
michael@0 | 292 | * retransmit wars after packet loss. |
michael@0 | 293 | * This is not in RFC 5346 but should be |
michael@0 | 294 | */ |
michael@0 | 295 | if ((PR_IntervalNow() - ss->ssl3.hs.rtTimerStarted) > |
michael@0 | 296 | (ss->ssl3.hs.rtTimeoutMs / 4)) { |
michael@0 | 297 | SSL_TRC(30, |
michael@0 | 298 | ("%d: SSL3[%d]: Shortcutting retransmit timer", |
michael@0 | 299 | SSL_GETPID(), ss->fd)); |
michael@0 | 300 | |
michael@0 | 301 | /* Cancel the timer and call the CB, |
michael@0 | 302 | * which re-arms the timer */ |
michael@0 | 303 | dtls_CancelTimer(ss); |
michael@0 | 304 | dtls_RetransmitTimerExpiredCb(ss); |
michael@0 | 305 | rv = SECSuccess; |
michael@0 | 306 | break; |
michael@0 | 307 | } else { |
michael@0 | 308 | SSL_TRC(30, |
michael@0 | 309 | ("%d: SSL3[%d]: We just retransmitted. Ignoring.", |
michael@0 | 310 | SSL_GETPID(), ss->fd)); |
michael@0 | 311 | rv = SECSuccess; |
michael@0 | 312 | break; |
michael@0 | 313 | } |
michael@0 | 314 | } else if (ss->ssl3.hs.rtTimerCb == dtls_FinishedTimerCb) { |
michael@0 | 315 | /* Retransmit the messages and re-arm the timer |
michael@0 | 316 | * Note that we are not backing off the timer here. |
michael@0 | 317 | * The spec isn't clear and my reasoning is that this |
michael@0 | 318 | * may be a re-ordered packet rather than slowness, |
michael@0 | 319 | * so let's be aggressive. */ |
michael@0 | 320 | dtls_CancelTimer(ss); |
michael@0 | 321 | rv = dtls_TransmitMessageFlight(ss); |
michael@0 | 322 | if (rv == SECSuccess) { |
michael@0 | 323 | rv = dtls_StartTimer(ss, dtls_FinishedTimerCb); |
michael@0 | 324 | } |
michael@0 | 325 | if (rv != SECSuccess) |
michael@0 | 326 | return rv; |
michael@0 | 327 | break; |
michael@0 | 328 | } |
michael@0 | 329 | } else if (message_seq > ss->ssl3.hs.recvMessageSeq) { |
michael@0 | 330 | /* Case 2 |
michael@0 | 331 | * |
michael@0 | 332 | * Ignore this message. This means we don't handle out of |
michael@0 | 333 | * order complete messages that well, but we're still |
michael@0 | 334 | * compliant and this probably does not happen often |
michael@0 | 335 | * |
michael@0 | 336 | * XXX OK for now. Maybe do something smarter at some point? |
michael@0 | 337 | */ |
michael@0 | 338 | } else { |
michael@0 | 339 | /* Case 1 |
michael@0 | 340 | * |
michael@0 | 341 | * Buffer the fragment for reassembly |
michael@0 | 342 | */ |
michael@0 | 343 | /* Make room for the message */ |
michael@0 | 344 | if (ss->ssl3.hs.recvdHighWater == -1) { |
michael@0 | 345 | PRUint32 map_length = OFFSET_BYTE(message_length) + 1; |
michael@0 | 346 | |
michael@0 | 347 | rv = sslBuffer_Grow(&ss->ssl3.hs.msg_body, message_length); |
michael@0 | 348 | if (rv != SECSuccess) |
michael@0 | 349 | break; |
michael@0 | 350 | /* Make room for the fragment map */ |
michael@0 | 351 | rv = sslBuffer_Grow(&ss->ssl3.hs.recvdFragments, |
michael@0 | 352 | map_length); |
michael@0 | 353 | if (rv != SECSuccess) |
michael@0 | 354 | break; |
michael@0 | 355 | |
michael@0 | 356 | /* Reset the reassembly map */ |
michael@0 | 357 | ss->ssl3.hs.recvdHighWater = 0; |
michael@0 | 358 | PORT_Memset(ss->ssl3.hs.recvdFragments.buf, 0, |
michael@0 | 359 | ss->ssl3.hs.recvdFragments.space); |
michael@0 | 360 | ss->ssl3.hs.msg_type = (SSL3HandshakeType)type; |
michael@0 | 361 | ss->ssl3.hs.msg_len = message_length; |
michael@0 | 362 | } |
michael@0 | 363 | |
michael@0 | 364 | /* If we have a message length mismatch, abandon the reassembly |
michael@0 | 365 | * in progress and hope that the next retransmit will give us |
michael@0 | 366 | * something sane |
michael@0 | 367 | */ |
michael@0 | 368 | if (message_length != ss->ssl3.hs.msg_len) { |
michael@0 | 369 | ss->ssl3.hs.recvdHighWater = -1; |
michael@0 | 370 | PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE); |
michael@0 | 371 | rv = SECFailure; |
michael@0 | 372 | break; |
michael@0 | 373 | } |
michael@0 | 374 | |
michael@0 | 375 | /* Now copy this fragment into the buffer */ |
michael@0 | 376 | PORT_Assert((fragment_offset + fragment_length) <= |
michael@0 | 377 | ss->ssl3.hs.msg_body.space); |
michael@0 | 378 | PORT_Memcpy(ss->ssl3.hs.msg_body.buf + fragment_offset, |
michael@0 | 379 | buf.buf, fragment_length); |
michael@0 | 380 | |
michael@0 | 381 | /* This logic is a bit tricky. We have two values for |
michael@0 | 382 | * reassembly state: |
michael@0 | 383 | * |
michael@0 | 384 | * - recvdHighWater contains the highest contiguous number of |
michael@0 | 385 | * bytes received |
michael@0 | 386 | * - recvdFragments contains a bitmask of packets received |
michael@0 | 387 | * above recvdHighWater |
michael@0 | 388 | * |
michael@0 | 389 | * This avoids having to fill in the bitmask in the common |
michael@0 | 390 | * case of adjacent fragments received in sequence |
michael@0 | 391 | */ |
michael@0 | 392 | if (fragment_offset <= ss->ssl3.hs.recvdHighWater) { |
michael@0 | 393 | /* Either this is the adjacent fragment or an overlapping |
michael@0 | 394 | * fragment */ |
michael@0 | 395 | ss->ssl3.hs.recvdHighWater = fragment_offset + |
michael@0 | 396 | fragment_length; |
michael@0 | 397 | } else { |
michael@0 | 398 | for (offset = fragment_offset; |
michael@0 | 399 | offset < fragment_offset + fragment_length; |
michael@0 | 400 | offset++) { |
michael@0 | 401 | ss->ssl3.hs.recvdFragments.buf[OFFSET_BYTE(offset)] |= |
michael@0 | 402 | OFFSET_MASK(offset); |
michael@0 | 403 | } |
michael@0 | 404 | } |
michael@0 | 405 | |
michael@0 | 406 | /* Now figure out the new high water mark if appropriate */ |
michael@0 | 407 | for (offset = ss->ssl3.hs.recvdHighWater; |
michael@0 | 408 | offset < ss->ssl3.hs.msg_len; offset++) { |
michael@0 | 409 | /* Note that this loop is not efficient, since it counts |
michael@0 | 410 | * bit by bit. If we have a lot of out-of-order packets, |
michael@0 | 411 | * we should optimize this */ |
michael@0 | 412 | if (ss->ssl3.hs.recvdFragments.buf[OFFSET_BYTE(offset)] & |
michael@0 | 413 | OFFSET_MASK(offset)) { |
michael@0 | 414 | ss->ssl3.hs.recvdHighWater++; |
michael@0 | 415 | } else { |
michael@0 | 416 | break; |
michael@0 | 417 | } |
michael@0 | 418 | } |
michael@0 | 419 | |
michael@0 | 420 | /* If we have all the bytes, then we are good to go */ |
michael@0 | 421 | if (ss->ssl3.hs.recvdHighWater == ss->ssl3.hs.msg_len) { |
michael@0 | 422 | ss->ssl3.hs.recvdHighWater = -1; |
michael@0 | 423 | |
michael@0 | 424 | rv = ssl3_HandleHandshakeMessage(ss, |
michael@0 | 425 | ss->ssl3.hs.msg_body.buf, |
michael@0 | 426 | ss->ssl3.hs.msg_len); |
michael@0 | 427 | if (rv == SECFailure) |
michael@0 | 428 | break; /* Skip rest of record */ |
michael@0 | 429 | |
michael@0 | 430 | /* At this point we are advancing our state machine, so |
michael@0 | 431 | * we can free our last flight of messages */ |
michael@0 | 432 | dtls_FreeHandshakeMessages(&ss->ssl3.hs.lastMessageFlight); |
michael@0 | 433 | dtls_CancelTimer(ss); |
michael@0 | 434 | |
michael@0 | 435 | /* If there have been no retries this time, reset the |
michael@0 | 436 | * timer value to the default per Section 4.2.4.1 */ |
michael@0 | 437 | if (ss->ssl3.hs.rtRetries == 0) { |
michael@0 | 438 | ss->ssl3.hs.rtTimeoutMs = INITIAL_DTLS_TIMEOUT_MS; |
michael@0 | 439 | } |
michael@0 | 440 | } |
michael@0 | 441 | } |
michael@0 | 442 | } |
michael@0 | 443 | |
michael@0 | 444 | buf.buf += fragment_length; |
michael@0 | 445 | buf.len -= fragment_length; |
michael@0 | 446 | } |
michael@0 | 447 | |
michael@0 | 448 | origBuf->len = 0; /* So ssl3_GatherAppDataRecord will keep looping. */ |
michael@0 | 449 | |
michael@0 | 450 | /* XXX OK for now. In future handle rv == SECWouldBlock safely in order |
michael@0 | 451 | * to deal with asynchronous certificate verification */ |
michael@0 | 452 | return rv; |
michael@0 | 453 | } |
michael@0 | 454 | |
michael@0 | 455 | /* Enqueue a message (either handshake or CCS) |
michael@0 | 456 | * |
michael@0 | 457 | * Called from: |
michael@0 | 458 | * dtls_StageHandshakeMessage() |
michael@0 | 459 | * ssl3_SendChangeCipherSpecs() |
michael@0 | 460 | */ |
michael@0 | 461 | SECStatus dtls_QueueMessage(sslSocket *ss, SSL3ContentType type, |
michael@0 | 462 | const SSL3Opaque *pIn, PRInt32 nIn) |
michael@0 | 463 | { |
michael@0 | 464 | SECStatus rv = SECSuccess; |
michael@0 | 465 | DTLSQueuedMessage *msg = NULL; |
michael@0 | 466 | |
michael@0 | 467 | PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); |
michael@0 | 468 | PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); |
michael@0 | 469 | |
michael@0 | 470 | msg = dtls_AllocQueuedMessage(ss->ssl3.cwSpec->epoch, type, pIn, nIn); |
michael@0 | 471 | |
michael@0 | 472 | if (!msg) { |
michael@0 | 473 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
michael@0 | 474 | rv = SECFailure; |
michael@0 | 475 | } else { |
michael@0 | 476 | PR_APPEND_LINK(&msg->link, &ss->ssl3.hs.lastMessageFlight); |
michael@0 | 477 | } |
michael@0 | 478 | |
michael@0 | 479 | return rv; |
michael@0 | 480 | } |
michael@0 | 481 | |
michael@0 | 482 | /* Add DTLS handshake message to the pending queue |
michael@0 | 483 | * Empty the sendBuf buffer. |
michael@0 | 484 | * This function returns SECSuccess or SECFailure, never SECWouldBlock. |
michael@0 | 485 | * Always set sendBuf.len to 0, even when returning SECFailure. |
michael@0 | 486 | * |
michael@0 | 487 | * Called from: |
michael@0 | 488 | * ssl3_AppendHandshakeHeader() |
michael@0 | 489 | * dtls_FlushHandshake() |
michael@0 | 490 | */ |
michael@0 | 491 | SECStatus |
michael@0 | 492 | dtls_StageHandshakeMessage(sslSocket *ss) |
michael@0 | 493 | { |
michael@0 | 494 | SECStatus rv = SECSuccess; |
michael@0 | 495 | |
michael@0 | 496 | PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); |
michael@0 | 497 | PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); |
michael@0 | 498 | |
michael@0 | 499 | /* This function is sometimes called when no data is actually to |
michael@0 | 500 | * be staged, so just return SECSuccess. */ |
michael@0 | 501 | if (!ss->sec.ci.sendBuf.buf || !ss->sec.ci.sendBuf.len) |
michael@0 | 502 | return rv; |
michael@0 | 503 | |
michael@0 | 504 | rv = dtls_QueueMessage(ss, content_handshake, |
michael@0 | 505 | ss->sec.ci.sendBuf.buf, ss->sec.ci.sendBuf.len); |
michael@0 | 506 | |
michael@0 | 507 | /* Whether we succeeded or failed, toss the old handshake data. */ |
michael@0 | 508 | ss->sec.ci.sendBuf.len = 0; |
michael@0 | 509 | return rv; |
michael@0 | 510 | } |
michael@0 | 511 | |
michael@0 | 512 | /* Enqueue the handshake message in sendBuf (if any) and then |
michael@0 | 513 | * transmit the resulting flight of handshake messages. |
michael@0 | 514 | * |
michael@0 | 515 | * Called from: |
michael@0 | 516 | * ssl3_FlushHandshake() |
michael@0 | 517 | */ |
michael@0 | 518 | SECStatus |
michael@0 | 519 | dtls_FlushHandshakeMessages(sslSocket *ss, PRInt32 flags) |
michael@0 | 520 | { |
michael@0 | 521 | SECStatus rv = SECSuccess; |
michael@0 | 522 | |
michael@0 | 523 | PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); |
michael@0 | 524 | PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); |
michael@0 | 525 | |
michael@0 | 526 | rv = dtls_StageHandshakeMessage(ss); |
michael@0 | 527 | if (rv != SECSuccess) |
michael@0 | 528 | return rv; |
michael@0 | 529 | |
michael@0 | 530 | if (!(flags & ssl_SEND_FLAG_FORCE_INTO_BUFFER)) { |
michael@0 | 531 | rv = dtls_TransmitMessageFlight(ss); |
michael@0 | 532 | if (rv != SECSuccess) |
michael@0 | 533 | return rv; |
michael@0 | 534 | |
michael@0 | 535 | if (!(flags & ssl_SEND_FLAG_NO_RETRANSMIT)) { |
michael@0 | 536 | ss->ssl3.hs.rtRetries = 0; |
michael@0 | 537 | rv = dtls_StartTimer(ss, dtls_RetransmitTimerExpiredCb); |
michael@0 | 538 | } |
michael@0 | 539 | } |
michael@0 | 540 | |
michael@0 | 541 | return rv; |
michael@0 | 542 | } |
michael@0 | 543 | |
michael@0 | 544 | /* The callback for when the retransmit timer expires |
michael@0 | 545 | * |
michael@0 | 546 | * Called from: |
michael@0 | 547 | * dtls_CheckTimer() |
michael@0 | 548 | * dtls_HandleHandshake() |
michael@0 | 549 | */ |
michael@0 | 550 | static void |
michael@0 | 551 | dtls_RetransmitTimerExpiredCb(sslSocket *ss) |
michael@0 | 552 | { |
michael@0 | 553 | SECStatus rv = SECFailure; |
michael@0 | 554 | |
michael@0 | 555 | ss->ssl3.hs.rtRetries++; |
michael@0 | 556 | |
michael@0 | 557 | if (!(ss->ssl3.hs.rtRetries % 3)) { |
michael@0 | 558 | /* If one of the messages was potentially greater than > MTU, |
michael@0 | 559 | * then downgrade. Do this every time we have retransmitted a |
michael@0 | 560 | * message twice, per RFC 6347 Sec. 4.1.1 */ |
michael@0 | 561 | dtls_SetMTU(ss, ss->ssl3.hs.maxMessageSent - 1); |
michael@0 | 562 | } |
michael@0 | 563 | |
michael@0 | 564 | rv = dtls_TransmitMessageFlight(ss); |
michael@0 | 565 | if (rv == SECSuccess) { |
michael@0 | 566 | |
michael@0 | 567 | /* Re-arm the timer */ |
michael@0 | 568 | rv = dtls_RestartTimer(ss, PR_TRUE, dtls_RetransmitTimerExpiredCb); |
michael@0 | 569 | } |
michael@0 | 570 | |
michael@0 | 571 | if (rv == SECFailure) { |
michael@0 | 572 | /* XXX OK for now. In future maybe signal the stack that we couldn't |
michael@0 | 573 | * transmit. For now, let the read handle any real network errors */ |
michael@0 | 574 | } |
michael@0 | 575 | } |
michael@0 | 576 | |
michael@0 | 577 | /* Transmit a flight of handshake messages, stuffing them |
michael@0 | 578 | * into as few records as seems reasonable |
michael@0 | 579 | * |
michael@0 | 580 | * Called from: |
michael@0 | 581 | * dtls_FlushHandshake() |
michael@0 | 582 | * dtls_RetransmitTimerExpiredCb() |
michael@0 | 583 | */ |
michael@0 | 584 | static SECStatus |
michael@0 | 585 | dtls_TransmitMessageFlight(sslSocket *ss) |
michael@0 | 586 | { |
michael@0 | 587 | SECStatus rv = SECSuccess; |
michael@0 | 588 | PRCList *msg_p; |
michael@0 | 589 | PRUint16 room_left = ss->ssl3.mtu; |
michael@0 | 590 | PRInt32 sent; |
michael@0 | 591 | |
michael@0 | 592 | ssl_GetXmitBufLock(ss); |
michael@0 | 593 | ssl_GetSpecReadLock(ss); |
michael@0 | 594 | |
michael@0 | 595 | /* DTLS does not buffer its handshake messages in |
michael@0 | 596 | * ss->pendingBuf, but rather in the lastMessageFlight |
michael@0 | 597 | * structure. This is just a sanity check that |
michael@0 | 598 | * some programming error hasn't inadvertantly |
michael@0 | 599 | * stuffed something in ss->pendingBuf |
michael@0 | 600 | */ |
michael@0 | 601 | PORT_Assert(!ss->pendingBuf.len); |
michael@0 | 602 | for (msg_p = PR_LIST_HEAD(&ss->ssl3.hs.lastMessageFlight); |
michael@0 | 603 | msg_p != &ss->ssl3.hs.lastMessageFlight; |
michael@0 | 604 | msg_p = PR_NEXT_LINK(msg_p)) { |
michael@0 | 605 | DTLSQueuedMessage *msg = (DTLSQueuedMessage *)msg_p; |
michael@0 | 606 | |
michael@0 | 607 | /* The logic here is: |
michael@0 | 608 | * |
michael@0 | 609 | * 1. If this is a message that will not fit into the remaining |
michael@0 | 610 | * space, then flush. |
michael@0 | 611 | * 2. If the message will now fit into the remaining space, |
michael@0 | 612 | * encrypt, buffer, and loop. |
michael@0 | 613 | * 3. If the message will not fit, then fragment. |
michael@0 | 614 | * |
michael@0 | 615 | * At the end of the function, flush. |
michael@0 | 616 | */ |
michael@0 | 617 | if ((msg->len + SSL3_BUFFER_FUDGE) > room_left) { |
michael@0 | 618 | /* The message will not fit into the remaining space, so flush */ |
michael@0 | 619 | rv = dtls_SendSavedWriteData(ss); |
michael@0 | 620 | if (rv != SECSuccess) |
michael@0 | 621 | break; |
michael@0 | 622 | |
michael@0 | 623 | room_left = ss->ssl3.mtu; |
michael@0 | 624 | } |
michael@0 | 625 | |
michael@0 | 626 | if ((msg->len + SSL3_BUFFER_FUDGE) <= room_left) { |
michael@0 | 627 | /* The message will fit, so encrypt and then continue with the |
michael@0 | 628 | * next packet */ |
michael@0 | 629 | sent = ssl3_SendRecord(ss, msg->epoch, msg->type, |
michael@0 | 630 | msg->data, msg->len, |
michael@0 | 631 | ssl_SEND_FLAG_FORCE_INTO_BUFFER | |
michael@0 | 632 | ssl_SEND_FLAG_USE_EPOCH); |
michael@0 | 633 | if (sent != msg->len) { |
michael@0 | 634 | rv = SECFailure; |
michael@0 | 635 | if (sent != -1) { |
michael@0 | 636 | PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
michael@0 | 637 | } |
michael@0 | 638 | break; |
michael@0 | 639 | } |
michael@0 | 640 | |
michael@0 | 641 | room_left = ss->ssl3.mtu - ss->pendingBuf.len; |
michael@0 | 642 | } else { |
michael@0 | 643 | /* The message will not fit, so fragment. |
michael@0 | 644 | * |
michael@0 | 645 | * XXX OK for now. Arrange to coalesce the last fragment |
michael@0 | 646 | * of this message with the next message if possible. |
michael@0 | 647 | * That would be more efficient. |
michael@0 | 648 | */ |
michael@0 | 649 | PRUint32 fragment_offset = 0; |
michael@0 | 650 | unsigned char fragment[DTLS_MAX_MTU]; /* >= than largest |
michael@0 | 651 | * plausible MTU */ |
michael@0 | 652 | |
michael@0 | 653 | /* Assert that we have already flushed */ |
michael@0 | 654 | PORT_Assert(room_left == ss->ssl3.mtu); |
michael@0 | 655 | |
michael@0 | 656 | /* Case 3: We now need to fragment this message |
michael@0 | 657 | * DTLS only supports fragmenting handshaking messages */ |
michael@0 | 658 | PORT_Assert(msg->type == content_handshake); |
michael@0 | 659 | |
michael@0 | 660 | /* The headers consume 12 bytes so the smalles possible |
michael@0 | 661 | * message (i.e., an empty one) is 12 bytes |
michael@0 | 662 | */ |
michael@0 | 663 | PORT_Assert(msg->len >= 12); |
michael@0 | 664 | |
michael@0 | 665 | while ((fragment_offset + 12) < msg->len) { |
michael@0 | 666 | PRUint32 fragment_len; |
michael@0 | 667 | const unsigned char *content = msg->data + 12; |
michael@0 | 668 | PRUint32 content_len = msg->len - 12; |
michael@0 | 669 | |
michael@0 | 670 | /* The reason we use 8 here is that that's the length of |
michael@0 | 671 | * the new DTLS data that we add to the header */ |
michael@0 | 672 | fragment_len = PR_MIN(room_left - (SSL3_BUFFER_FUDGE + 8), |
michael@0 | 673 | content_len - fragment_offset); |
michael@0 | 674 | PORT_Assert(fragment_len < DTLS_MAX_MTU - 12); |
michael@0 | 675 | /* Make totally sure that we are within the buffer. |
michael@0 | 676 | * Note that the only way that fragment len could get |
michael@0 | 677 | * adjusted here is if |
michael@0 | 678 | * |
michael@0 | 679 | * (a) we are in release mode so the PORT_Assert is compiled out |
michael@0 | 680 | * (b) either the MTU table is inconsistent with DTLS_MAX_MTU |
michael@0 | 681 | * or ss->ssl3.mtu has become corrupt. |
michael@0 | 682 | */ |
michael@0 | 683 | fragment_len = PR_MIN(fragment_len, DTLS_MAX_MTU - 12); |
michael@0 | 684 | |
michael@0 | 685 | /* Construct an appropriate-sized fragment */ |
michael@0 | 686 | /* Type, length, sequence */ |
michael@0 | 687 | PORT_Memcpy(fragment, msg->data, 6); |
michael@0 | 688 | |
michael@0 | 689 | /* Offset */ |
michael@0 | 690 | fragment[6] = (fragment_offset >> 16) & 0xff; |
michael@0 | 691 | fragment[7] = (fragment_offset >> 8) & 0xff; |
michael@0 | 692 | fragment[8] = (fragment_offset) & 0xff; |
michael@0 | 693 | |
michael@0 | 694 | /* Fragment length */ |
michael@0 | 695 | fragment[9] = (fragment_len >> 16) & 0xff; |
michael@0 | 696 | fragment[10] = (fragment_len >> 8) & 0xff; |
michael@0 | 697 | fragment[11] = (fragment_len) & 0xff; |
michael@0 | 698 | |
michael@0 | 699 | PORT_Memcpy(fragment + 12, content + fragment_offset, |
michael@0 | 700 | fragment_len); |
michael@0 | 701 | |
michael@0 | 702 | /* |
michael@0 | 703 | * Send the record. We do this in two stages |
michael@0 | 704 | * 1. Encrypt |
michael@0 | 705 | */ |
michael@0 | 706 | sent = ssl3_SendRecord(ss, msg->epoch, msg->type, |
michael@0 | 707 | fragment, fragment_len + 12, |
michael@0 | 708 | ssl_SEND_FLAG_FORCE_INTO_BUFFER | |
michael@0 | 709 | ssl_SEND_FLAG_USE_EPOCH); |
michael@0 | 710 | if (sent != (fragment_len + 12)) { |
michael@0 | 711 | rv = SECFailure; |
michael@0 | 712 | if (sent != -1) { |
michael@0 | 713 | PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
michael@0 | 714 | } |
michael@0 | 715 | break; |
michael@0 | 716 | } |
michael@0 | 717 | |
michael@0 | 718 | /* 2. Flush */ |
michael@0 | 719 | rv = dtls_SendSavedWriteData(ss); |
michael@0 | 720 | if (rv != SECSuccess) |
michael@0 | 721 | break; |
michael@0 | 722 | |
michael@0 | 723 | fragment_offset += fragment_len; |
michael@0 | 724 | } |
michael@0 | 725 | } |
michael@0 | 726 | } |
michael@0 | 727 | |
michael@0 | 728 | /* Finally, we need to flush */ |
michael@0 | 729 | if (rv == SECSuccess) |
michael@0 | 730 | rv = dtls_SendSavedWriteData(ss); |
michael@0 | 731 | |
michael@0 | 732 | /* Give up the locks */ |
michael@0 | 733 | ssl_ReleaseSpecReadLock(ss); |
michael@0 | 734 | ssl_ReleaseXmitBufLock(ss); |
michael@0 | 735 | |
michael@0 | 736 | return rv; |
michael@0 | 737 | } |
michael@0 | 738 | |
michael@0 | 739 | /* Flush the data in the pendingBuf and update the max message sent |
michael@0 | 740 | * so we can adjust the MTU estimate if we need to. |
michael@0 | 741 | * Wrapper for ssl_SendSavedWriteData. |
michael@0 | 742 | * |
michael@0 | 743 | * Called from dtls_TransmitMessageFlight() |
michael@0 | 744 | */ |
michael@0 | 745 | static |
michael@0 | 746 | SECStatus dtls_SendSavedWriteData(sslSocket *ss) |
michael@0 | 747 | { |
michael@0 | 748 | PRInt32 sent; |
michael@0 | 749 | |
michael@0 | 750 | sent = ssl_SendSavedWriteData(ss); |
michael@0 | 751 | if (sent < 0) |
michael@0 | 752 | return SECFailure; |
michael@0 | 753 | |
michael@0 | 754 | /* We should always have complete writes b/c datagram sockets |
michael@0 | 755 | * don't really block */ |
michael@0 | 756 | if (ss->pendingBuf.len > 0) { |
michael@0 | 757 | ssl_MapLowLevelError(SSL_ERROR_SOCKET_WRITE_FAILURE); |
michael@0 | 758 | return SECFailure; |
michael@0 | 759 | } |
michael@0 | 760 | |
michael@0 | 761 | /* Update the largest message sent so we can adjust the MTU |
michael@0 | 762 | * estimate if necessary */ |
michael@0 | 763 | if (sent > ss->ssl3.hs.maxMessageSent) |
michael@0 | 764 | ss->ssl3.hs.maxMessageSent = sent; |
michael@0 | 765 | |
michael@0 | 766 | return SECSuccess; |
michael@0 | 767 | } |
michael@0 | 768 | |
michael@0 | 769 | /* Compress, MAC, encrypt a DTLS record. Allows specification of |
michael@0 | 770 | * the epoch using epoch value. If use_epoch is PR_TRUE then |
michael@0 | 771 | * we use the provided epoch. If use_epoch is PR_FALSE then |
michael@0 | 772 | * whatever the current value is in effect is used. |
michael@0 | 773 | * |
michael@0 | 774 | * Called from ssl3_SendRecord() |
michael@0 | 775 | */ |
michael@0 | 776 | SECStatus |
michael@0 | 777 | dtls_CompressMACEncryptRecord(sslSocket * ss, |
michael@0 | 778 | DTLSEpoch epoch, |
michael@0 | 779 | PRBool use_epoch, |
michael@0 | 780 | SSL3ContentType type, |
michael@0 | 781 | const SSL3Opaque * pIn, |
michael@0 | 782 | PRUint32 contentLen, |
michael@0 | 783 | sslBuffer * wrBuf) |
michael@0 | 784 | { |
michael@0 | 785 | SECStatus rv = SECFailure; |
michael@0 | 786 | ssl3CipherSpec * cwSpec; |
michael@0 | 787 | |
michael@0 | 788 | ssl_GetSpecReadLock(ss); /********************************/ |
michael@0 | 789 | |
michael@0 | 790 | /* The reason for this switch-hitting code is that we might have |
michael@0 | 791 | * a flight of records spanning an epoch boundary, e.g., |
michael@0 | 792 | * |
michael@0 | 793 | * ClientKeyExchange (epoch = 0) |
michael@0 | 794 | * ChangeCipherSpec (epoch = 0) |
michael@0 | 795 | * Finished (epoch = 1) |
michael@0 | 796 | * |
michael@0 | 797 | * Thus, each record needs a different cipher spec. The information |
michael@0 | 798 | * about which epoch to use is carried with the record. |
michael@0 | 799 | */ |
michael@0 | 800 | if (use_epoch) { |
michael@0 | 801 | if (ss->ssl3.cwSpec->epoch == epoch) |
michael@0 | 802 | cwSpec = ss->ssl3.cwSpec; |
michael@0 | 803 | else if (ss->ssl3.pwSpec->epoch == epoch) |
michael@0 | 804 | cwSpec = ss->ssl3.pwSpec; |
michael@0 | 805 | else |
michael@0 | 806 | cwSpec = NULL; |
michael@0 | 807 | } else { |
michael@0 | 808 | cwSpec = ss->ssl3.cwSpec; |
michael@0 | 809 | } |
michael@0 | 810 | |
michael@0 | 811 | if (cwSpec) { |
michael@0 | 812 | rv = ssl3_CompressMACEncryptRecord(cwSpec, ss->sec.isServer, PR_TRUE, |
michael@0 | 813 | PR_FALSE, type, pIn, contentLen, |
michael@0 | 814 | wrBuf); |
michael@0 | 815 | } else { |
michael@0 | 816 | PR_NOT_REACHED("Couldn't find a cipher spec matching epoch"); |
michael@0 | 817 | PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
michael@0 | 818 | } |
michael@0 | 819 | ssl_ReleaseSpecReadLock(ss); /************************************/ |
michael@0 | 820 | |
michael@0 | 821 | return rv; |
michael@0 | 822 | } |
michael@0 | 823 | |
michael@0 | 824 | /* Start a timer |
michael@0 | 825 | * |
michael@0 | 826 | * Called from: |
michael@0 | 827 | * dtls_HandleHandshake() |
michael@0 | 828 | * dtls_FlushHAndshake() |
michael@0 | 829 | * dtls_RestartTimer() |
michael@0 | 830 | */ |
michael@0 | 831 | SECStatus |
michael@0 | 832 | dtls_StartTimer(sslSocket *ss, DTLSTimerCb cb) |
michael@0 | 833 | { |
michael@0 | 834 | PORT_Assert(ss->ssl3.hs.rtTimerCb == NULL); |
michael@0 | 835 | |
michael@0 | 836 | ss->ssl3.hs.rtTimerStarted = PR_IntervalNow(); |
michael@0 | 837 | ss->ssl3.hs.rtTimerCb = cb; |
michael@0 | 838 | |
michael@0 | 839 | return SECSuccess; |
michael@0 | 840 | } |
michael@0 | 841 | |
michael@0 | 842 | /* Restart a timer with optional backoff |
michael@0 | 843 | * |
michael@0 | 844 | * Called from dtls_RetransmitTimerExpiredCb() |
michael@0 | 845 | */ |
michael@0 | 846 | SECStatus |
michael@0 | 847 | dtls_RestartTimer(sslSocket *ss, PRBool backoff, DTLSTimerCb cb) |
michael@0 | 848 | { |
michael@0 | 849 | if (backoff) { |
michael@0 | 850 | ss->ssl3.hs.rtTimeoutMs *= 2; |
michael@0 | 851 | if (ss->ssl3.hs.rtTimeoutMs > MAX_DTLS_TIMEOUT_MS) |
michael@0 | 852 | ss->ssl3.hs.rtTimeoutMs = MAX_DTLS_TIMEOUT_MS; |
michael@0 | 853 | } |
michael@0 | 854 | |
michael@0 | 855 | return dtls_StartTimer(ss, cb); |
michael@0 | 856 | } |
michael@0 | 857 | |
michael@0 | 858 | /* Cancel a pending timer |
michael@0 | 859 | * |
michael@0 | 860 | * Called from: |
michael@0 | 861 | * dtls_HandleHandshake() |
michael@0 | 862 | * dtls_CheckTimer() |
michael@0 | 863 | */ |
michael@0 | 864 | void |
michael@0 | 865 | dtls_CancelTimer(sslSocket *ss) |
michael@0 | 866 | { |
michael@0 | 867 | PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); |
michael@0 | 868 | |
michael@0 | 869 | ss->ssl3.hs.rtTimerCb = NULL; |
michael@0 | 870 | } |
michael@0 | 871 | |
michael@0 | 872 | /* Check the pending timer and fire the callback if it expired |
michael@0 | 873 | * |
michael@0 | 874 | * Called from ssl3_GatherCompleteHandshake() |
michael@0 | 875 | */ |
michael@0 | 876 | void |
michael@0 | 877 | dtls_CheckTimer(sslSocket *ss) |
michael@0 | 878 | { |
michael@0 | 879 | if (!ss->ssl3.hs.rtTimerCb) |
michael@0 | 880 | return; |
michael@0 | 881 | |
michael@0 | 882 | if ((PR_IntervalNow() - ss->ssl3.hs.rtTimerStarted) > |
michael@0 | 883 | PR_MillisecondsToInterval(ss->ssl3.hs.rtTimeoutMs)) { |
michael@0 | 884 | /* Timer has expired */ |
michael@0 | 885 | DTLSTimerCb cb = ss->ssl3.hs.rtTimerCb; |
michael@0 | 886 | |
michael@0 | 887 | /* Cancel the timer so that we can call the CB safely */ |
michael@0 | 888 | dtls_CancelTimer(ss); |
michael@0 | 889 | |
michael@0 | 890 | /* Now call the CB */ |
michael@0 | 891 | cb(ss); |
michael@0 | 892 | } |
michael@0 | 893 | } |
michael@0 | 894 | |
michael@0 | 895 | /* The callback to fire when the holddown timer for the Finished |
michael@0 | 896 | * message expires and we can delete it |
michael@0 | 897 | * |
michael@0 | 898 | * Called from dtls_CheckTimer() |
michael@0 | 899 | */ |
michael@0 | 900 | void |
michael@0 | 901 | dtls_FinishedTimerCb(sslSocket *ss) |
michael@0 | 902 | { |
michael@0 | 903 | ssl3_DestroyCipherSpec(ss->ssl3.pwSpec, PR_FALSE); |
michael@0 | 904 | } |
michael@0 | 905 | |
michael@0 | 906 | /* Cancel the Finished hold-down timer and destroy the |
michael@0 | 907 | * pending cipher spec. Note that this means that |
michael@0 | 908 | * successive rehandshakes will fail if the Finished is |
michael@0 | 909 | * lost. |
michael@0 | 910 | * |
michael@0 | 911 | * XXX OK for now. Figure out how to handle the combination |
michael@0 | 912 | * of Finished lost and rehandshake |
michael@0 | 913 | */ |
michael@0 | 914 | void |
michael@0 | 915 | dtls_RehandshakeCleanup(sslSocket *ss) |
michael@0 | 916 | { |
michael@0 | 917 | dtls_CancelTimer(ss); |
michael@0 | 918 | ssl3_DestroyCipherSpec(ss->ssl3.pwSpec, PR_FALSE); |
michael@0 | 919 | ss->ssl3.hs.sendMessageSeq = 0; |
michael@0 | 920 | ss->ssl3.hs.recvMessageSeq = 0; |
michael@0 | 921 | } |
michael@0 | 922 | |
michael@0 | 923 | /* Set the MTU to the next step less than or equal to the |
michael@0 | 924 | * advertised value. Also used to downgrade the MTU by |
michael@0 | 925 | * doing dtls_SetMTU(ss, biggest packet set). |
michael@0 | 926 | * |
michael@0 | 927 | * Passing 0 means set this to the largest MTU known |
michael@0 | 928 | * (effectively resetting the PMTU backoff value). |
michael@0 | 929 | * |
michael@0 | 930 | * Called by: |
michael@0 | 931 | * ssl3_InitState() |
michael@0 | 932 | * dtls_RetransmitTimerExpiredCb() |
michael@0 | 933 | */ |
michael@0 | 934 | void |
michael@0 | 935 | dtls_SetMTU(sslSocket *ss, PRUint16 advertised) |
michael@0 | 936 | { |
michael@0 | 937 | int i; |
michael@0 | 938 | |
michael@0 | 939 | if (advertised == 0) { |
michael@0 | 940 | ss->ssl3.mtu = COMMON_MTU_VALUES[0]; |
michael@0 | 941 | SSL_TRC(30, ("Resetting MTU to %d", ss->ssl3.mtu)); |
michael@0 | 942 | return; |
michael@0 | 943 | } |
michael@0 | 944 | |
michael@0 | 945 | for (i = 0; i < PR_ARRAY_SIZE(COMMON_MTU_VALUES); i++) { |
michael@0 | 946 | if (COMMON_MTU_VALUES[i] <= advertised) { |
michael@0 | 947 | ss->ssl3.mtu = COMMON_MTU_VALUES[i]; |
michael@0 | 948 | SSL_TRC(30, ("Resetting MTU to %d", ss->ssl3.mtu)); |
michael@0 | 949 | return; |
michael@0 | 950 | } |
michael@0 | 951 | } |
michael@0 | 952 | |
michael@0 | 953 | /* Fallback */ |
michael@0 | 954 | ss->ssl3.mtu = COMMON_MTU_VALUES[PR_ARRAY_SIZE(COMMON_MTU_VALUES)-1]; |
michael@0 | 955 | SSL_TRC(30, ("Resetting MTU to %d", ss->ssl3.mtu)); |
michael@0 | 956 | } |
michael@0 | 957 | |
michael@0 | 958 | /* Called from ssl3_HandleHandshakeMessage() when it has deciphered a |
michael@0 | 959 | * DTLS hello_verify_request |
michael@0 | 960 | * Caller must hold Handshake and RecvBuf locks. |
michael@0 | 961 | */ |
michael@0 | 962 | SECStatus |
michael@0 | 963 | dtls_HandleHelloVerifyRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length) |
michael@0 | 964 | { |
michael@0 | 965 | int errCode = SSL_ERROR_RX_MALFORMED_HELLO_VERIFY_REQUEST; |
michael@0 | 966 | SECStatus rv; |
michael@0 | 967 | PRInt32 temp; |
michael@0 | 968 | SECItem cookie = {siBuffer, NULL, 0}; |
michael@0 | 969 | SSL3AlertDescription desc = illegal_parameter; |
michael@0 | 970 | |
michael@0 | 971 | SSL_TRC(3, ("%d: SSL3[%d]: handle hello_verify_request handshake", |
michael@0 | 972 | SSL_GETPID(), ss->fd)); |
michael@0 | 973 | PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); |
michael@0 | 974 | PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); |
michael@0 | 975 | |
michael@0 | 976 | if (ss->ssl3.hs.ws != wait_server_hello) { |
michael@0 | 977 | errCode = SSL_ERROR_RX_UNEXPECTED_HELLO_VERIFY_REQUEST; |
michael@0 | 978 | desc = unexpected_message; |
michael@0 | 979 | goto alert_loser; |
michael@0 | 980 | } |
michael@0 | 981 | |
michael@0 | 982 | /* The version */ |
michael@0 | 983 | temp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length); |
michael@0 | 984 | if (temp < 0) { |
michael@0 | 985 | goto loser; /* alert has been sent */ |
michael@0 | 986 | } |
michael@0 | 987 | |
michael@0 | 988 | if (temp != SSL_LIBRARY_VERSION_DTLS_1_0_WIRE && |
michael@0 | 989 | temp != SSL_LIBRARY_VERSION_DTLS_1_2_WIRE) { |
michael@0 | 990 | goto alert_loser; |
michael@0 | 991 | } |
michael@0 | 992 | |
michael@0 | 993 | /* The cookie */ |
michael@0 | 994 | rv = ssl3_ConsumeHandshakeVariable(ss, &cookie, 1, &b, &length); |
michael@0 | 995 | if (rv != SECSuccess) { |
michael@0 | 996 | goto loser; /* alert has been sent */ |
michael@0 | 997 | } |
michael@0 | 998 | if (cookie.len > DTLS_COOKIE_BYTES) { |
michael@0 | 999 | desc = decode_error; |
michael@0 | 1000 | goto alert_loser; /* malformed. */ |
michael@0 | 1001 | } |
michael@0 | 1002 | |
michael@0 | 1003 | PORT_Memcpy(ss->ssl3.hs.cookie, cookie.data, cookie.len); |
michael@0 | 1004 | ss->ssl3.hs.cookieLen = cookie.len; |
michael@0 | 1005 | |
michael@0 | 1006 | |
michael@0 | 1007 | ssl_GetXmitBufLock(ss); /*******************************/ |
michael@0 | 1008 | |
michael@0 | 1009 | /* Now re-send the client hello */ |
michael@0 | 1010 | rv = ssl3_SendClientHello(ss, PR_TRUE); |
michael@0 | 1011 | |
michael@0 | 1012 | ssl_ReleaseXmitBufLock(ss); /*******************************/ |
michael@0 | 1013 | |
michael@0 | 1014 | if (rv == SECSuccess) |
michael@0 | 1015 | return rv; |
michael@0 | 1016 | |
michael@0 | 1017 | alert_loser: |
michael@0 | 1018 | (void)SSL3_SendAlert(ss, alert_fatal, desc); |
michael@0 | 1019 | |
michael@0 | 1020 | loser: |
michael@0 | 1021 | errCode = ssl_MapLowLevelError(errCode); |
michael@0 | 1022 | return SECFailure; |
michael@0 | 1023 | } |
michael@0 | 1024 | |
michael@0 | 1025 | /* Initialize the DTLS anti-replay window |
michael@0 | 1026 | * |
michael@0 | 1027 | * Called from: |
michael@0 | 1028 | * ssl3_SetupPendingCipherSpec() |
michael@0 | 1029 | * ssl3_InitCipherSpec() |
michael@0 | 1030 | */ |
michael@0 | 1031 | void |
michael@0 | 1032 | dtls_InitRecvdRecords(DTLSRecvdRecords *records) |
michael@0 | 1033 | { |
michael@0 | 1034 | PORT_Memset(records->data, 0, sizeof(records->data)); |
michael@0 | 1035 | records->left = 0; |
michael@0 | 1036 | records->right = DTLS_RECVD_RECORDS_WINDOW - 1; |
michael@0 | 1037 | } |
michael@0 | 1038 | |
michael@0 | 1039 | /* |
michael@0 | 1040 | * Has this DTLS record been received? Return values are: |
michael@0 | 1041 | * -1 -- out of range to the left |
michael@0 | 1042 | * 0 -- not received yet |
michael@0 | 1043 | * 1 -- replay |
michael@0 | 1044 | * |
michael@0 | 1045 | * Called from: dtls_HandleRecord() |
michael@0 | 1046 | */ |
michael@0 | 1047 | int |
michael@0 | 1048 | dtls_RecordGetRecvd(DTLSRecvdRecords *records, PRUint64 seq) |
michael@0 | 1049 | { |
michael@0 | 1050 | PRUint64 offset; |
michael@0 | 1051 | |
michael@0 | 1052 | /* Out of range to the left */ |
michael@0 | 1053 | if (seq < records->left) { |
michael@0 | 1054 | return -1; |
michael@0 | 1055 | } |
michael@0 | 1056 | |
michael@0 | 1057 | /* Out of range to the right; since we advance the window on |
michael@0 | 1058 | * receipt, that means that this packet has not been received |
michael@0 | 1059 | * yet */ |
michael@0 | 1060 | if (seq > records->right) |
michael@0 | 1061 | return 0; |
michael@0 | 1062 | |
michael@0 | 1063 | offset = seq % DTLS_RECVD_RECORDS_WINDOW; |
michael@0 | 1064 | |
michael@0 | 1065 | return !!(records->data[offset / 8] & (1 << (offset % 8))); |
michael@0 | 1066 | } |
michael@0 | 1067 | |
michael@0 | 1068 | /* Update the DTLS anti-replay window |
michael@0 | 1069 | * |
michael@0 | 1070 | * Called from ssl3_HandleRecord() |
michael@0 | 1071 | */ |
michael@0 | 1072 | void |
michael@0 | 1073 | dtls_RecordSetRecvd(DTLSRecvdRecords *records, PRUint64 seq) |
michael@0 | 1074 | { |
michael@0 | 1075 | PRUint64 offset; |
michael@0 | 1076 | |
michael@0 | 1077 | if (seq < records->left) |
michael@0 | 1078 | return; |
michael@0 | 1079 | |
michael@0 | 1080 | if (seq > records->right) { |
michael@0 | 1081 | PRUint64 new_left; |
michael@0 | 1082 | PRUint64 new_right; |
michael@0 | 1083 | PRUint64 right; |
michael@0 | 1084 | |
michael@0 | 1085 | /* Slide to the right; this is the tricky part |
michael@0 | 1086 | * |
michael@0 | 1087 | * 1. new_top is set to have room for seq, on the |
michael@0 | 1088 | * next byte boundary by setting the right 8 |
michael@0 | 1089 | * bits of seq |
michael@0 | 1090 | * 2. new_left is set to compensate. |
michael@0 | 1091 | * 3. Zero all bits between top and new_top. Since |
michael@0 | 1092 | * this is a ring, this zeroes everything as-yet |
michael@0 | 1093 | * unseen. Because we always operate on byte |
michael@0 | 1094 | * boundaries, we can zero one byte at a time |
michael@0 | 1095 | */ |
michael@0 | 1096 | new_right = seq | 0x07; |
michael@0 | 1097 | new_left = (new_right - DTLS_RECVD_RECORDS_WINDOW) + 1; |
michael@0 | 1098 | |
michael@0 | 1099 | for (right = records->right + 8; right <= new_right; right += 8) { |
michael@0 | 1100 | offset = right % DTLS_RECVD_RECORDS_WINDOW; |
michael@0 | 1101 | records->data[offset / 8] = 0; |
michael@0 | 1102 | } |
michael@0 | 1103 | |
michael@0 | 1104 | records->right = new_right; |
michael@0 | 1105 | records->left = new_left; |
michael@0 | 1106 | } |
michael@0 | 1107 | |
michael@0 | 1108 | offset = seq % DTLS_RECVD_RECORDS_WINDOW; |
michael@0 | 1109 | |
michael@0 | 1110 | records->data[offset / 8] |= (1 << (offset % 8)); |
michael@0 | 1111 | } |
michael@0 | 1112 | |
michael@0 | 1113 | SECStatus |
michael@0 | 1114 | DTLS_GetHandshakeTimeout(PRFileDesc *socket, PRIntervalTime *timeout) |
michael@0 | 1115 | { |
michael@0 | 1116 | sslSocket * ss = NULL; |
michael@0 | 1117 | PRIntervalTime elapsed; |
michael@0 | 1118 | PRIntervalTime desired; |
michael@0 | 1119 | |
michael@0 | 1120 | ss = ssl_FindSocket(socket); |
michael@0 | 1121 | |
michael@0 | 1122 | if (!ss) |
michael@0 | 1123 | return SECFailure; |
michael@0 | 1124 | |
michael@0 | 1125 | if (!IS_DTLS(ss)) |
michael@0 | 1126 | return SECFailure; |
michael@0 | 1127 | |
michael@0 | 1128 | if (!ss->ssl3.hs.rtTimerCb) |
michael@0 | 1129 | return SECFailure; |
michael@0 | 1130 | |
michael@0 | 1131 | elapsed = PR_IntervalNow() - ss->ssl3.hs.rtTimerStarted; |
michael@0 | 1132 | desired = PR_MillisecondsToInterval(ss->ssl3.hs.rtTimeoutMs); |
michael@0 | 1133 | if (elapsed > desired) { |
michael@0 | 1134 | /* Timer expired */ |
michael@0 | 1135 | *timeout = PR_INTERVAL_NO_WAIT; |
michael@0 | 1136 | } else { |
michael@0 | 1137 | *timeout = desired - elapsed; |
michael@0 | 1138 | } |
michael@0 | 1139 | |
michael@0 | 1140 | return SECSuccess; |
michael@0 | 1141 | } |