diff options
Diffstat (limited to 'security/nss/lib/ssl/ssl3ext.c')
-rw-r--r-- | security/nss/lib/ssl/ssl3ext.c | 711 |
1 files changed, 587 insertions, 124 deletions
diff --git a/security/nss/lib/ssl/ssl3ext.c b/security/nss/lib/ssl/ssl3ext.c index 271084cf7..5a5077998 100644 --- a/security/nss/lib/ssl/ssl3ext.c +++ b/security/nss/lib/ssl/ssl3ext.c @@ -14,8 +14,20 @@ #include "sslimpl.h" #include "sslproto.h" #include "ssl3exthandle.h" +#include "tls13err.h" #include "tls13exthandle.h" +/* Callback function that handles a received extension. */ +typedef SECStatus (*ssl3ExtensionHandlerFunc)(const sslSocket *ss, + TLSExtensionData *xtnData, + SECItem *data); + +/* Row in a table of hello extension handlers. */ +typedef struct { + SSLExtensionType ex_type; + ssl3ExtensionHandlerFunc ex_handler; +} ssl3ExtensionHandler; + /* Table of handlers for received TLS hello extensions, one per extension. * In the second generation, this table will be dynamic, and functions * will be registered here. @@ -31,16 +43,15 @@ static const ssl3ExtensionHandler clientHelloHandlers[] = { { ssl_app_layer_protocol_xtn, &ssl3_ServerHandleAppProtoXtn }, { ssl_use_srtp_xtn, &ssl3_ServerHandleUseSRTPXtn }, { ssl_cert_status_xtn, &ssl3_ServerHandleStatusRequestXtn }, - { ssl_signature_algorithms_xtn, &ssl3_ServerHandleSigAlgsXtn }, + { ssl_signature_algorithms_xtn, &ssl3_HandleSigAlgsXtn }, { ssl_extended_master_secret_xtn, &ssl3_HandleExtendedMasterSecretXtn }, { ssl_signed_cert_timestamp_xtn, &ssl3_ServerHandleSignedCertTimestampXtn }, { ssl_tls13_key_share_xtn, &tls13_ServerHandleKeyShareXtn }, { ssl_tls13_pre_shared_key_xtn, &tls13_ServerHandlePreSharedKeyXtn }, { ssl_tls13_early_data_xtn, &tls13_ServerHandleEarlyDataXtn }, - { ssl_tls13_psk_key_exchange_modes_xtn, - &tls13_ServerHandlePskKeyExchangeModesXtn }, - { ssl_tls13_short_header_xtn, &tls13_HandleShortHeaderXtn }, - { -1, NULL } + { ssl_tls13_psk_key_exchange_modes_xtn, &tls13_ServerHandlePskModesXtn }, + { ssl_tls13_cookie_xtn, &tls13_ServerHandleCookieXtn }, + { 0, NULL } }; /* These two tables are used by the client, to handle server hello @@ -59,36 +70,38 @@ static const ssl3ExtensionHandler serverHelloHandlersTLS[] = { { ssl_tls13_key_share_xtn, &tls13_ClientHandleKeyShareXtn }, { ssl_tls13_pre_shared_key_xtn, &tls13_ClientHandlePreSharedKeyXtn }, { ssl_tls13_early_data_xtn, &tls13_ClientHandleEarlyDataXtn }, - { ssl_tls13_short_header_xtn, &tls13_HandleShortHeaderXtn }, - { -1, NULL } + { 0, NULL } }; static const ssl3ExtensionHandler helloRetryRequestHandlers[] = { { ssl_tls13_key_share_xtn, tls13_ClientHandleKeyShareXtnHrr }, { ssl_tls13_cookie_xtn, tls13_ClientHandleHrrCookie }, - { -1, NULL } + { 0, NULL } }; static const ssl3ExtensionHandler serverHelloHandlersSSL3[] = { { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn }, - { -1, NULL } + { 0, NULL } }; static const ssl3ExtensionHandler newSessionTicketHandlers[] = { - { ssl_tls13_ticket_early_data_info_xtn, - &tls13_ClientHandleTicketEarlyDataInfoXtn }, - { -1, NULL } + { ssl_tls13_early_data_xtn, + &tls13_ClientHandleTicketEarlyDataXtn }, + { 0, NULL } }; /* This table is used by the client to handle server certificates in TLS 1.3 */ static const ssl3ExtensionHandler serverCertificateHandlers[] = { { ssl_signed_cert_timestamp_xtn, &ssl3_ClientHandleSignedCertTimestampXtn }, { ssl_cert_status_xtn, &ssl3_ClientHandleStatusRequestXtn }, - { -1, NULL } + { 0, NULL } }; static const ssl3ExtensionHandler certificateRequestHandlers[] = { - { -1, NULL } + { ssl_signature_algorithms_xtn, &ssl3_HandleSigAlgsXtn }, + { ssl_tls13_certificate_authorities_xtn, + &tls13_ClientHandleCertAuthoritiesXtn }, + { 0, NULL } }; /* Tables of functions to format TLS hello extensions, one function per @@ -101,14 +114,14 @@ static const ssl3ExtensionHandler certificateRequestHandlers[] = { * the client hello is empty (for example, the extended master secret * extension, if it were listed last). See bug 1243641. */ -static const ssl3HelloExtensionSender clientHelloSendersTLS[SSL_MAX_EXTENSIONS] = +static const sslExtensionBuilder clientHelloSendersTLS[] = { - { ssl_server_name_xtn, &ssl3_SendServerNameXtn }, + { ssl_server_name_xtn, &ssl3_ClientSendServerNameXtn }, { ssl_extended_master_secret_xtn, &ssl3_SendExtendedMasterSecretXtn }, { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn }, { ssl_supported_groups_xtn, &ssl_SendSupportedGroupsXtn }, { ssl_ec_point_formats_xtn, &ssl3_SendSupportedPointFormatsXtn }, - { ssl_session_ticket_xtn, &ssl3_SendSessionTicketXtn }, + { ssl_session_ticket_xtn, &ssl3_ClientSendSessionTicketXtn }, { ssl_next_proto_nego_xtn, &ssl3_ClientSendNextProtoNegoXtn }, { ssl_app_layer_protocol_xtn, &ssl3_ClientSendAppProtoXtn }, { ssl_use_srtp_xtn, &ssl3_ClientSendUseSRTPXtn }, @@ -121,22 +134,155 @@ static const ssl3HelloExtensionSender clientHelloSendersTLS[SSL_MAX_EXTENSIONS] * client hello is empty. They are not intolerant of TLS 1.2, so list * signature_algorithms at the end. See bug 1243641. */ { ssl_tls13_supported_versions_xtn, &tls13_ClientSendSupportedVersionsXtn }, - { ssl_tls13_short_header_xtn, &tls13_SendShortHeaderXtn }, - { ssl_signature_algorithms_xtn, &ssl3_ClientSendSigAlgsXtn }, + { ssl_signature_algorithms_xtn, &ssl3_SendSigAlgsXtn }, { ssl_tls13_cookie_xtn, &tls13_ClientSendHrrCookieXtn }, - { ssl_tls13_psk_key_exchange_modes_xtn, - &tls13_ClientSendPskKeyExchangeModesXtn }, - { ssl_padding_xtn, &ssl3_ClientSendPaddingExtension }, + { ssl_tls13_psk_key_exchange_modes_xtn, &tls13_ClientSendPskModesXtn }, /* The pre_shared_key extension MUST be last. */ { ssl_tls13_pre_shared_key_xtn, &tls13_ClientSendPreSharedKeyXtn }, - /* any extra entries will appear as { 0, NULL } */ + { 0, NULL } }; -static const ssl3HelloExtensionSender clientHelloSendersSSL3[SSL_MAX_EXTENSIONS] = { - { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn } - /* any extra entries will appear as { 0, NULL } */ +static const sslExtensionBuilder clientHelloSendersSSL3[] = { + { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn }, + { 0, NULL } +}; + +static const sslExtensionBuilder tls13_cert_req_senders[] = { + { ssl_signature_algorithms_xtn, &ssl3_SendSigAlgsXtn }, + { ssl_tls13_certificate_authorities_xtn, &tls13_SendCertAuthoritiesXtn }, + { 0, NULL } +}; + +static const sslExtensionBuilder tls13_hrr_senders[] = { + { ssl_tls13_key_share_xtn, &tls13_ServerSendHrrKeyShareXtn }, + { ssl_tls13_cookie_xtn, &tls13_ServerSendHrrCookieXtn }, + { ssl_tls13_supported_versions_xtn, &tls13_ServerSendSupportedVersionsXtn }, + { 0, NULL } +}; + +static const struct { + SSLExtensionType type; + SSLExtensionSupport support; +} ssl_supported_extensions[] = { + { ssl_server_name_xtn, ssl_ext_native_only }, + { ssl_cert_status_xtn, ssl_ext_native }, + { ssl_supported_groups_xtn, ssl_ext_native_only }, + { ssl_ec_point_formats_xtn, ssl_ext_native }, + { ssl_signature_algorithms_xtn, ssl_ext_native_only }, + { ssl_use_srtp_xtn, ssl_ext_native }, + { ssl_app_layer_protocol_xtn, ssl_ext_native_only }, + { ssl_signed_cert_timestamp_xtn, ssl_ext_native }, + { ssl_padding_xtn, ssl_ext_native }, + { ssl_extended_master_secret_xtn, ssl_ext_native_only }, + { ssl_session_ticket_xtn, ssl_ext_native_only }, + { ssl_tls13_key_share_xtn, ssl_ext_native_only }, + { ssl_tls13_pre_shared_key_xtn, ssl_ext_native_only }, + { ssl_tls13_early_data_xtn, ssl_ext_native_only }, + { ssl_tls13_supported_versions_xtn, ssl_ext_native_only }, + { ssl_tls13_cookie_xtn, ssl_ext_native_only }, + { ssl_tls13_psk_key_exchange_modes_xtn, ssl_ext_native_only }, + { ssl_tls13_ticket_early_data_info_xtn, ssl_ext_native_only }, + { ssl_tls13_certificate_authorities_xtn, ssl_ext_native }, + { ssl_next_proto_nego_xtn, ssl_ext_none }, + { ssl_renegotiation_info_xtn, ssl_ext_native } }; +static SSLExtensionSupport +ssl_GetExtensionSupport(PRUint16 type) +{ + unsigned int i; + for (i = 0; i < PR_ARRAY_SIZE(ssl_supported_extensions); ++i) { + if (type == ssl_supported_extensions[i].type) { + return ssl_supported_extensions[i].support; + } + } + return ssl_ext_none; +} + +SECStatus +SSLExp_GetExtensionSupport(PRUint16 type, SSLExtensionSupport *support) +{ + *support = ssl_GetExtensionSupport(type); + return SECSuccess; +} + +SECStatus +SSLExp_InstallExtensionHooks(PRFileDesc *fd, PRUint16 extension, + SSLExtensionWriter writer, void *writerArg, + SSLExtensionHandler handler, void *handlerArg) +{ + sslSocket *ss = ssl_FindSocket(fd); + PRCList *cursor; + sslCustomExtensionHooks *hook; + + if (!ss) { + return SECFailure; /* Code already set. */ + } + + /* Need to specify both or neither, but not just one. */ + if ((writer && !handler) || (!writer && handler)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + if (ssl_GetExtensionSupport(extension) == ssl_ext_native_only) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + if (ss->firstHsDone || ((ss->ssl3.hs.ws != idle_handshake) && + (ss->ssl3.hs.ws != wait_client_hello))) { + PORT_SetError(PR_INVALID_STATE_ERROR); + return SECFailure; + } + + /* Remove any old handler. */ + for (cursor = PR_NEXT_LINK(&ss->extensionHooks); + cursor != &ss->extensionHooks; + cursor = PR_NEXT_LINK(cursor)) { + hook = (sslCustomExtensionHooks *)cursor; + if (hook->type == extension) { + PR_REMOVE_LINK(&hook->link); + PORT_Free(hook); + break; + } + } + + if (!writer && !handler) { + return SECSuccess; + } + + hook = PORT_ZNew(sslCustomExtensionHooks); + if (!hook) { + return SECFailure; /* This removed the old one, oh well. */ + } + + hook->type = extension; + hook->writer = writer; + hook->writerArg = writerArg; + hook->handler = handler; + hook->handlerArg = handlerArg; + PR_APPEND_LINK(&hook->link, &ss->extensionHooks); + return SECSuccess; +} + +static sslCustomExtensionHooks * +ssl_FindCustomExtensionHooks(sslSocket *ss, PRUint16 extension) +{ + PRCList *cursor; + + for (cursor = PR_NEXT_LINK(&ss->extensionHooks); + cursor != &ss->extensionHooks; + cursor = PR_NEXT_LINK(cursor)) { + sslCustomExtensionHooks *hook = (sslCustomExtensionHooks *)cursor; + if (hook->type == extension) { + return hook; + } + } + + return NULL; +} + static PRBool arrayContainsExtension(const PRUint16 *array, PRUint32 len, PRUint16 ex_type) { @@ -156,8 +302,11 @@ ssl3_ExtensionNegotiated(const sslSocket *ss, PRUint16 ex_type) xtnData->numNegotiated, ex_type); } +/* This checks for whether an extension was advertised. On the client, this + * covers extensions that are sent in ClientHello; on the server, extensions + * sent in CertificateRequest (TLS 1.3 only). */ PRBool -ssl3_ClientExtensionAdvertised(const sslSocket *ss, PRUint16 ex_type) +ssl3_ExtensionAdvertised(const sslSocket *ss, PRUint16 ex_type) { const TLSExtensionData *xtnData = &ss->xtnData; return arrayContainsExtension(xtnData->advertised, @@ -240,6 +389,44 @@ ssl3_FindExtension(sslSocket *ss, SSLExtensionType extension_type) return NULL; } +static SECStatus +ssl_CallExtensionHandler(sslSocket *ss, SSLHandshakeType handshakeMessage, + TLSExtension *extension, + const ssl3ExtensionHandler *handler) +{ + SECStatus rv = SECSuccess; + SSLAlertDescription alert = handshake_failure; + sslCustomExtensionHooks *customHooks; + + customHooks = ssl_FindCustomExtensionHooks(ss, extension->type); + if (customHooks) { + if (customHooks->handler) { + rv = customHooks->handler(ss->fd, handshakeMessage, + extension->data.data, + extension->data.len, + &alert, customHooks->handlerArg); + } + } else { + /* Find extension_type in table of Hello Extension Handlers. */ + for (; handler->ex_handler != NULL; ++handler) { + if (handler->ex_type == extension->type) { + rv = (*handler->ex_handler)(ss, &ss->xtnData, &extension->data); + break; + } + } + } + + if (rv != SECSuccess) { + if (!ss->ssl3.fatalAlertSent) { + /* Send an alert if the handler didn't already. */ + (void)SSL3_SendAlert(ss, alert_fatal, alert); + } + return SECFailure; + } + + return SECSuccess; +} + /* Go through the hello extensions in |ss->ssl3.hs.remoteExtensions|. * For each one, find the extension handler in the table, and * if present, invoke that handler. @@ -250,42 +437,46 @@ ssl3_FindExtension(sslSocket *ss, SSLExtensionType extension_type) * right phase. */ SECStatus -ssl3_HandleParsedExtensions(sslSocket *ss, - SSL3HandshakeType handshakeMessage) +ssl3_HandleParsedExtensions(sslSocket *ss, SSLHandshakeType message) { const ssl3ExtensionHandler *handlers; /* HelloRetryRequest doesn't set ss->version. It might be safe to * do so, but we weren't entirely sure. TODO(ekr@rtfm.com). */ PRBool isTLS13 = (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) || - (handshakeMessage == hello_retry_request); + (message == ssl_hs_hello_retry_request); + /* The following messages can include extensions that were not included in + * the original ClientHello. */ + PRBool allowNotOffered = (message == ssl_hs_client_hello) || + (message == ssl_hs_certificate_request) || + (message == ssl_hs_new_session_ticket); PRCList *cursor; - switch (handshakeMessage) { - case client_hello: + switch (message) { + case ssl_hs_client_hello: handlers = clientHelloHandlers; break; - case new_session_ticket: + case ssl_hs_new_session_ticket: PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3); handlers = newSessionTicketHandlers; break; - case hello_retry_request: + case ssl_hs_hello_retry_request: handlers = helloRetryRequestHandlers; break; - case encrypted_extensions: + case ssl_hs_encrypted_extensions: PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3); /* fall through */ - case server_hello: + case ssl_hs_server_hello: if (ss->version > SSL_LIBRARY_VERSION_3_0) { handlers = serverHelloHandlersTLS; } else { handlers = serverHelloHandlersSSL3; } break; - case certificate: + case ssl_hs_certificate: PORT_Assert(!ss->sec.isServer); handlers = serverCertificateHandlers; break; - case certificate_request: + case ssl_hs_certificate_request: PORT_Assert(!ss->sec.isServer); handlers = certificateRequestHandlers; break; @@ -299,28 +490,41 @@ ssl3_HandleParsedExtensions(sslSocket *ss, cursor != &ss->ssl3.hs.remoteExtensions; cursor = PR_NEXT_LINK(cursor)) { TLSExtension *extension = (TLSExtension *)cursor; - const ssl3ExtensionHandler *handler; + SECStatus rv; /* Check whether the server sent an extension which was not advertised - * in the ClientHello */ - if (!ss->sec.isServer && - !ssl3_ClientExtensionAdvertised(ss, extension->type) && - (handshakeMessage != new_session_ticket) && - (extension->type != ssl_tls13_cookie_xtn)) { + * in the ClientHello. + * + * Note that a TLS 1.3 server should check if CertificateRequest + * extensions were sent. But the extensions used for CertificateRequest + * do not have any response, so we rely on + * ssl3_ExtensionAdvertised to return false on the server. That + * results in the server only rejecting any extension. */ + if (!allowNotOffered && (extension->type != ssl_tls13_cookie_xtn) && + !ssl3_ExtensionAdvertised(ss, extension->type)) { (void)SSL3_SendAlert(ss, alert_fatal, unsupported_extension); PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION); return SECFailure; } /* Check that this is a legal extension in TLS 1.3 */ - if (isTLS13 && !tls13_ExtensionAllowed(extension->type, handshakeMessage)) { - if (handshakeMessage == client_hello) { - /* Skip extensions not used in TLS 1.3 */ - continue; + if (isTLS13 && + !ssl_FindCustomExtensionHooks(ss, extension->type)) { + switch (tls13_ExtensionStatus(extension->type, message)) { + case tls13_extension_allowed: + break; + case tls13_extension_unknown: + if (allowNotOffered) { + continue; /* Skip over unknown extensions. */ + } + /* Fall through. */ + case tls13_extension_disallowed: + SSL_TRC(3, ("%d: TLS13: unexpected extension %d in message %d", + SSL_GETPID(), extension, message)); + tls13_FatalError(ss, SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION, + unsupported_extension); + return SECFailure; } - tls13_FatalError(ss, SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION, - unsupported_extension); - return SECFailure; } /* Special check for this being the last extension if it's @@ -334,23 +538,9 @@ ssl3_HandleParsedExtensions(sslSocket *ss, return SECFailure; } - /* find extension_type in table of Hello Extension Handlers */ - for (handler = handlers; handler->ex_type >= 0; handler++) { - /* if found, call this handler */ - if (handler->ex_type == extension->type) { - SECStatus rv; - - rv = (*handler->ex_handler)(ss, &ss->xtnData, - (PRUint16)extension->type, - &extension->data); - if (rv != SECSuccess) { - if (!ss->ssl3.fatalAlertSent) { - /* send a generic alert if the handler didn't already */ - (void)SSL3_SendAlert(ss, alert_fatal, handshake_failure); - } - return SECFailure; - } - } + rv = ssl_CallExtensionHandler(ss, message, extension, handlers); + if (rv != SECSuccess) { + return SECFailure; } } return SECSuccess; @@ -361,7 +551,7 @@ ssl3_HandleParsedExtensions(sslSocket *ss, SECStatus ssl3_HandleExtensions(sslSocket *ss, PRUint8 **b, PRUint32 *length, - SSL3HandshakeType handshakeMessage) + SSLHandshakeType handshakeMessage) { SECStatus rv; @@ -383,21 +573,30 @@ SECStatus ssl3_RegisterExtensionSender(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, - ssl3HelloExtensionSenderFunc cb) + sslExtensionBuilderFunc cb) { int i; - ssl3HelloExtensionSender *sender; + sslExtensionBuilder *sender; if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { sender = &xtnData->serverHelloSenders[0]; } else { - if (tls13_ExtensionAllowed(ex_type, server_hello)) { - PORT_Assert(!tls13_ExtensionAllowed(ex_type, encrypted_extensions)); + if (tls13_ExtensionStatus(ex_type, ssl_hs_server_hello) == + tls13_extension_allowed) { + PORT_Assert(tls13_ExtensionStatus(ex_type, + ssl_hs_encrypted_extensions) == + tls13_extension_disallowed); sender = &xtnData->serverHelloSenders[0]; - } else if (tls13_ExtensionAllowed(ex_type, certificate)) { + } else if (tls13_ExtensionStatus(ex_type, + ssl_hs_encrypted_extensions) == + tls13_extension_allowed) { + sender = &xtnData->encryptedExtensionsSenders[0]; + } else if (tls13_ExtensionStatus(ex_type, ssl_hs_certificate) == + tls13_extension_allowed) { sender = &xtnData->certificateSenders[0]; } else { - PORT_Assert(tls13_ExtensionAllowed(ex_type, encrypted_extensions)); - sender = &xtnData->encryptedExtensionsSenders[0]; + PORT_Assert(0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; } } for (i = 0; i < SSL_MAX_EXTENSIONS; ++i, ++sender) { @@ -418,32 +617,289 @@ ssl3_RegisterExtensionSender(const sslSocket *ss, return SECFailure; } -/* call each of the extension senders and return the accumulated length */ -PRInt32 -ssl3_CallHelloExtensionSenders(sslSocket *ss, PRBool append, PRUint32 maxBytes, - const ssl3HelloExtensionSender *sender) +static SECStatus +ssl_CallCustomExtensionSenders(sslSocket *ss, sslBuffer *buf, + SSLHandshakeType message) { - PRInt32 total_exten_len = 0; - int i; + sslBuffer tail = SSL_BUFFER_EMPTY; + SECStatus rv; + PRCList *cursor; - if (!sender) { - if (ss->vrange.max > SSL_LIBRARY_VERSION_3_0) { - sender = &clientHelloSendersTLS[0]; - } else { - sender = &clientHelloSendersSSL3[0]; + /* Save any extensions that want to be last. */ + if (ss->xtnData.lastXtnOffset) { + rv = sslBuffer_Append(&tail, buf->buf + ss->xtnData.lastXtnOffset, + buf->len - ss->xtnData.lastXtnOffset); + if (rv != SECSuccess) { + return SECFailure; } + buf->len = ss->xtnData.lastXtnOffset; } - for (i = 0; i < SSL_MAX_EXTENSIONS; ++i, ++sender) { - if (sender->ex_sender) { - PRInt32 extLen = (*sender->ex_sender)(ss, &ss->xtnData, append, maxBytes); - if (extLen < 0) - return -1; - maxBytes -= extLen; - total_exten_len += extLen; + /* Reserve the maximum amount of space possible. */ + rv = sslBuffer_Grow(buf, 65535); + if (rv != SECSuccess) { + return SECFailure; + } + + for (cursor = PR_NEXT_LINK(&ss->extensionHooks); + cursor != &ss->extensionHooks; + cursor = PR_NEXT_LINK(cursor)) { + sslCustomExtensionHooks *hook = + (sslCustomExtensionHooks *)cursor; + PRBool append = PR_FALSE; + unsigned int len = 0; + + if (hook->writer) { + /* The writer writes directly into |buf|. Provide space that allows + * for the existing extensions, any tail, plus type and length. */ + unsigned int space = buf->space - (buf->len + tail.len + 4); + append = (*hook->writer)(ss->fd, message, + buf->buf + buf->len + 4, &len, space, + hook->writerArg); + if (len > space) { + PORT_SetError(SEC_ERROR_APPLICATION_CALLBACK_ERROR); + goto loser; + } + } + if (!append) { + continue; + } + + rv = sslBuffer_AppendNumber(buf, hook->type, 2); + if (rv != SECSuccess) { + goto loser; /* Code already set. */ + } + rv = sslBuffer_AppendNumber(buf, len, 2); + if (rv != SECSuccess) { + goto loser; /* Code already set. */ + } + buf->len += len; + + if (message == ssl_hs_client_hello || + message == ssl_hs_certificate_request) { + ss->xtnData.advertised[ss->xtnData.numAdvertised++] = hook->type; } } - return total_exten_len; + + sslBuffer_Append(buf, tail.buf, tail.len); + sslBuffer_Clear(&tail); + return SECSuccess; + +loser: + sslBuffer_Clear(&tail); + return SECFailure; +} + +/* Call extension handlers for the given message. */ +SECStatus +ssl_ConstructExtensions(sslSocket *ss, sslBuffer *buf, SSLHandshakeType message) +{ + const sslExtensionBuilder *sender; + SECStatus rv; + + PORT_Assert(buf->len == 0); + + switch (message) { + case ssl_hs_client_hello: + if (ss->vrange.max > SSL_LIBRARY_VERSION_3_0) { + sender = clientHelloSendersTLS; + } else { + sender = clientHelloSendersSSL3; + } + break; + + case ssl_hs_server_hello: + sender = ss->xtnData.serverHelloSenders; + break; + + case ssl_hs_certificate_request: + PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3); + sender = tls13_cert_req_senders; + break; + + case ssl_hs_certificate: + PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3); + sender = ss->xtnData.certificateSenders; + break; + + case ssl_hs_encrypted_extensions: + PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3); + sender = ss->xtnData.encryptedExtensionsSenders; + break; + + case ssl_hs_hello_retry_request: + PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3); + sender = tls13_hrr_senders; + break; + + default: + PORT_Assert(0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + for (; sender->ex_sender != NULL; ++sender) { + PRBool append = PR_FALSE; + unsigned int start = buf->len; + unsigned int length; + + if (ssl_FindCustomExtensionHooks(ss, sender->ex_type)) { + continue; + } + + /* Save space for the extension type and length. Note that we don't grow + * the buffer now; rely on sslBuffer_Append* to do that. */ + buf->len += 4; + rv = (*sender->ex_sender)(ss, &ss->xtnData, buf, &append); + if (rv != SECSuccess) { + goto loser; + } + + /* Save the length and go back to the start. */ + length = buf->len - start - 4; + buf->len = start; + if (!append) { + continue; + } + + buf->len = start; + rv = sslBuffer_AppendNumber(buf, sender->ex_type, 2); + if (rv != SECSuccess) { + goto loser; /* Code already set. */ + } + rv = sslBuffer_AppendNumber(buf, length, 2); + if (rv != SECSuccess) { + goto loser; /* Code already set. */ + } + /* Skip over the extension body. */ + buf->len += length; + + if (message == ssl_hs_client_hello || + message == ssl_hs_certificate_request) { + ss->xtnData.advertised[ss->xtnData.numAdvertised++] = + sender->ex_type; + } + } + + if (!PR_CLIST_IS_EMPTY(&ss->extensionHooks)) { + rv = ssl_CallCustomExtensionSenders(ss, buf, message); + if (rv != SECSuccess) { + goto loser; + } + } + + if (buf->len > 0xffff) { + PORT_SetError(SSL_ERROR_TX_RECORD_TOO_LONG); + goto loser; + } + + return SECSuccess; + +loser: + sslBuffer_Clear(buf); + return SECFailure; +} + +/* This extension sender can be used anywhere that an always empty extension is + * needed. Mostly that is for ServerHello where sender registration is dynamic; + * ClientHello senders are usually conditional in some way. */ +SECStatus +ssl_SendEmptyExtension(const sslSocket *ss, TLSExtensionData *xtnData, + sslBuffer *buf, PRBool *append) +{ + *append = PR_TRUE; + return SECSuccess; +} + +/* Takes the size of the ClientHello, less the record header, and determines how + * much padding is required. */ +static unsigned int +ssl_CalculatePaddingExtLen(const sslSocket *ss, unsigned int clientHelloLength) +{ + unsigned int recordLength = 1 /* handshake message type */ + + 3 /* handshake message length */ + + clientHelloLength; + unsigned int extensionLen; + + /* Don't pad for DTLS, for SSLv3, or for renegotiation. */ + if (IS_DTLS(ss) || + ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_0 || + ss->firstHsDone) { + return 0; + } + + /* A padding extension may be included to ensure that the record containing + * the ClientHello doesn't have a length between 256 and 511 bytes + * (inclusive). Initial ClientHello records with such lengths trigger bugs + * in F5 devices. */ + if (recordLength < 256 || recordLength >= 512) { + return 0; + } + + extensionLen = 512 - recordLength; + /* Extensions take at least four bytes to encode. Always include at least + * one byte of data if we are padding. Some servers will time out or + * terminate the connection if the last ClientHello extension is empty. */ + if (extensionLen < 5) { + extensionLen = 5; + } + + return extensionLen - 4; +} + +/* ssl3_SendPaddingExtension possibly adds an extension which ensures that a + * ClientHello record is either < 256 bytes or is >= 512 bytes. This ensures + * that we don't trigger bugs in F5 products. + * + * This takes an existing extension buffer, |buf|, and the length of the + * remainder of the ClientHello, |prefixLen|. It modifies the extension buffer + * to insert padding at the right place. + */ +SECStatus +ssl_InsertPaddingExtension(const sslSocket *ss, unsigned int prefixLen, + sslBuffer *buf) +{ + static unsigned char padding[252] = { 0 }; + unsigned int paddingLen; + unsigned int tailLen; + SECStatus rv; + + /* Account for the size of the header, the length field of the extensions + * block and the size of the existing extensions. */ + paddingLen = ssl_CalculatePaddingExtLen(ss, prefixLen + 2 + buf->len); + if (!paddingLen) { + return SECSuccess; + } + + /* Move the tail if there is one. This only happens if we are sending the + * TLS 1.3 PSK extension, which needs to be at the end. */ + if (ss->xtnData.lastXtnOffset) { + PORT_Assert(buf->len > ss->xtnData.lastXtnOffset); + tailLen = buf->len - ss->xtnData.lastXtnOffset; + rv = sslBuffer_Grow(buf, buf->len + 4 + paddingLen); + if (rv != SECSuccess) { + return SECFailure; + } + PORT_Memmove(buf->buf + ss->xtnData.lastXtnOffset + 4 + paddingLen, + buf->buf + ss->xtnData.lastXtnOffset, + tailLen); + buf->len = ss->xtnData.lastXtnOffset; + } else { + tailLen = 0; + } + + rv = sslBuffer_AppendNumber(buf, ssl_padding_xtn, 2); + if (rv != SECSuccess) { + return SECFailure; /* Code already set. */ + } + rv = sslBuffer_AppendVariable(buf, padding, paddingLen, 2); + if (rv != SECSuccess) { + return SECFailure; /* Code already set. */ + } + + buf->len += tailLen; + + return SECSuccess; } void @@ -460,52 +916,59 @@ ssl3_DestroyRemoteExtensions(PRCList *list) /* Initialize the extension data block. */ void -ssl3_InitExtensionData(TLSExtensionData *xtnData) +ssl3_InitExtensionData(TLSExtensionData *xtnData, const sslSocket *ss) { + unsigned int advertisedMax; + PRCList *cursor; + /* Set things up to the right starting state. */ PORT_Memset(xtnData, 0, sizeof(*xtnData)); xtnData->peerSupportsFfdheGroups = PR_FALSE; PR_INIT_CLIST(&xtnData->remoteKeyShares); + + /* Allocate enough to allow for native extensions, plus any custom ones. */ + if (ss->sec.isServer) { + advertisedMax = PR_MAX(PR_ARRAY_SIZE(certificateRequestHandlers), + PR_ARRAY_SIZE(tls13_cert_req_senders)); + } else { + advertisedMax = PR_MAX(PR_ARRAY_SIZE(clientHelloHandlers), + PR_ARRAY_SIZE(clientHelloSendersTLS)); + ++advertisedMax; /* For the RI SCSV, which we also track. */ + } + for (cursor = PR_NEXT_LINK(&ss->extensionHooks); + cursor != &ss->extensionHooks; + cursor = PR_NEXT_LINK(cursor)) { + ++advertisedMax; + } + xtnData->advertised = PORT_ZNewArray(PRUint16, advertisedMax); } -/* Free everything that has been allocated and then reset back to - * the starting state. */ void -ssl3_ResetExtensionData(TLSExtensionData *xtnData) +ssl3_DestroyExtensionData(TLSExtensionData *xtnData) { - /* Clean up. */ ssl3_FreeSniNameArray(xtnData); - PORT_Free(xtnData->clientSigSchemes); + PORT_Free(xtnData->sigSchemes); SECITEM_FreeItem(&xtnData->nextProto, PR_FALSE); tls13_DestroyKeyShares(&xtnData->remoteKeyShares); - - /* Now reinit. */ - ssl3_InitExtensionData(xtnData); -} - -/* Thunks to let extension handlers operate on const sslSocket* objects. */ -SECStatus -ssl3_ExtAppendHandshake(const sslSocket *ss, const void *void_src, - PRInt32 bytes) -{ - return ssl3_AppendHandshake((sslSocket *)ss, void_src, bytes); -} - -SECStatus -ssl3_ExtAppendHandshakeNumber(const sslSocket *ss, PRInt32 num, - PRInt32 lenSize) -{ - return ssl3_AppendHandshakeNumber((sslSocket *)ss, num, lenSize); + SECITEM_FreeItem(&xtnData->certReqContext, PR_FALSE); + SECITEM_FreeItem(&xtnData->applicationToken, PR_FALSE); + if (xtnData->certReqAuthorities.arena) { + PORT_FreeArena(xtnData->certReqAuthorities.arena, PR_FALSE); + xtnData->certReqAuthorities.arena = NULL; + } + PORT_Free(xtnData->advertised); } -SECStatus -ssl3_ExtAppendHandshakeVariable(const sslSocket *ss, - const PRUint8 *src, PRInt32 bytes, - PRInt32 lenSize) +/* Free everything that has been allocated and then reset back to + * the starting state. */ +void +ssl3_ResetExtensionData(TLSExtensionData *xtnData, const sslSocket *ss) { - return ssl3_AppendHandshakeVariable((sslSocket *)ss, src, bytes, lenSize); + ssl3_DestroyExtensionData(xtnData); + ssl3_InitExtensionData(xtnData, ss); } +/* Thunks to let extension handlers operate on const sslSocket* objects. */ void ssl3_ExtSendAlert(const sslSocket *ss, SSL3AlertLevel level, SSL3AlertDescription desc) |