diff options
Diffstat (limited to '')
-rw-r--r-- | security/nss/lib/ssl/ssl3ext.c | 1186 |
1 files changed, 1186 insertions, 0 deletions
diff --git a/security/nss/lib/ssl/ssl3ext.c b/security/nss/lib/ssl/ssl3ext.c new file mode 100644 index 0000000000..f349e70327 --- /dev/null +++ b/security/nss/lib/ssl/ssl3ext.c @@ -0,0 +1,1186 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * SSL3 Protocol + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* TLS extension code moved here from ssl3ecc.c */ + +#include "nssrenam.h" +#include "nss.h" +#include "pk11pub.h" +#include "ssl.h" +#include "sslimpl.h" +#include "sslproto.h" +#include "ssl3exthandle.h" +#include "tls13ech.h" +#include "tls13err.h" +#include "tls13exthandle.h" +#include "tls13subcerts.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. + */ +/* This table is used by the server, to handle client hello extensions. */ +static const ssl3ExtensionHandler clientHelloHandlers[] = { + { ssl_server_name_xtn, &ssl3_HandleServerNameXtn }, + { ssl_supported_groups_xtn, &ssl_HandleSupportedGroupsXtn }, + { ssl_ec_point_formats_xtn, &ssl3_HandleSupportedPointFormatsXtn }, + { ssl_session_ticket_xtn, &ssl3_ServerHandleSessionTicketXtn }, + { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn }, + { ssl_app_layer_protocol_xtn, &ssl3_ServerHandleAppProtoXtn }, + { ssl_use_srtp_xtn, &ssl3_ServerHandleUseSRTPXtn }, + { ssl_cert_status_xtn, &ssl3_ServerHandleStatusRequestXtn }, + { ssl_tls13_certificate_authorities_xtn, &tls13_ServerHandleCertAuthoritiesXtn }, + { ssl_signature_algorithms_xtn, &ssl3_HandleSigAlgsXtn }, + { ssl_extended_master_secret_xtn, &ssl3_HandleExtendedMasterSecretXtn }, + { ssl_signed_cert_timestamp_xtn, &ssl3_ServerHandleSignedCertTimestampXtn }, + { ssl_delegated_credentials_xtn, &tls13_ServerHandleDelegatedCredentialsXtn }, + { 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_ServerHandlePskModesXtn }, + { ssl_tls13_cookie_xtn, &tls13_ServerHandleCookieXtn }, + { ssl_tls13_post_handshake_auth_xtn, &tls13_ServerHandlePostHandshakeAuthXtn }, + { ssl_record_size_limit_xtn, &ssl_HandleRecordSizeLimitXtn }, + { ssl_certificate_compression_xtn, &ssl3_HandleCertificateCompressionXtn }, + { 0, NULL } +}; + +/* These two tables are used by the client, to handle server hello + * extensions. */ +static const ssl3ExtensionHandler serverHelloHandlersTLS[] = { + { ssl_server_name_xtn, &ssl3_HandleServerNameXtn }, + /* TODO: add a handler for ssl_ec_point_formats_xtn */ + { ssl_session_ticket_xtn, &ssl3_ClientHandleSessionTicketXtn }, + { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn }, + { ssl_app_layer_protocol_xtn, &ssl3_ClientHandleAppProtoXtn }, + { ssl_use_srtp_xtn, &ssl3_ClientHandleUseSRTPXtn }, + { ssl_cert_status_xtn, &ssl3_ClientHandleStatusRequestXtn }, + { ssl_extended_master_secret_xtn, &ssl3_HandleExtendedMasterSecretXtn }, + { ssl_signed_cert_timestamp_xtn, &ssl3_ClientHandleSignedCertTimestampXtn }, + { ssl_tls13_key_share_xtn, &tls13_ClientHandleKeyShareXtn }, + { ssl_tls13_pre_shared_key_xtn, &tls13_ClientHandlePreSharedKeyXtn }, + { ssl_tls13_early_data_xtn, &tls13_ClientHandleEarlyDataXtn }, + { ssl_tls13_encrypted_client_hello_xtn, &tls13_ClientHandleEchXtn }, + { ssl_record_size_limit_xtn, &ssl_HandleRecordSizeLimitXtn }, + { 0, NULL } +}; + +static const ssl3ExtensionHandler helloRetryRequestHandlers[] = { + { ssl_tls13_key_share_xtn, tls13_ClientHandleKeyShareXtnHrr }, + { ssl_tls13_cookie_xtn, tls13_ClientHandleHrrCookie }, + { ssl_tls13_encrypted_client_hello_xtn, tls13_ClientHandleHrrEchXtn }, + { 0, NULL } +}; + +static const ssl3ExtensionHandler serverHelloHandlersSSL3[] = { + { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn }, + { 0, NULL } +}; + +static const ssl3ExtensionHandler newSessionTicketHandlers[] = { + { 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 }, + { ssl_delegated_credentials_xtn, &tls13_ClientHandleDelegatedCredentialsXtn }, + { 0, NULL } +}; + +static const ssl3ExtensionHandler certificateRequestHandlers[] = { + { ssl_signature_algorithms_xtn, &ssl3_HandleSigAlgsXtn }, + { ssl_tls13_certificate_authorities_xtn, + &tls13_ClientHandleCertAuthoritiesXtn }, + { ssl_certificate_compression_xtn, &ssl3_HandleCertificateCompressionXtn }, + { 0, NULL } +}; + +/* Tables of functions to format TLS hello extensions, one function per + * extension. + * These static tables are for the formatting of client hello extensions. + * The server's table of hello senders is dynamic, in the socket struct, + * and sender functions are registered there. + * NB: the order of these extensions can have an impact on compatibility. Some + * servers (e.g. Tomcat) will terminate the connection if the last extension in + * the client hello is empty (for example, the extended master secret + * extension, if it were listed last). See bug 1243641. + */ +static const sslExtensionBuilder clientHelloSendersTLS[] = { + /* TLS 1.3 GREASE extensions - empty. */ + { ssl_tls13_grease_xtn, &tls13_SendEmptyGreaseXtn }, + { 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_ClientSendSessionTicketXtn }, + { ssl_app_layer_protocol_xtn, &ssl3_ClientSendAppProtoXtn }, + { ssl_use_srtp_xtn, &ssl3_ClientSendUseSRTPXtn }, + { ssl_cert_status_xtn, &ssl3_ClientSendStatusRequestXtn }, + { ssl_delegated_credentials_xtn, &tls13_ClientSendDelegatedCredentialsXtn }, + { ssl_signed_cert_timestamp_xtn, &ssl3_ClientSendSignedCertTimestampXtn }, + { ssl_tls13_key_share_xtn, &tls13_ClientSendKeyShareXtn }, + { ssl_tls13_early_data_xtn, &tls13_ClientSendEarlyDataXtn }, + /* Some servers (e.g. WebSphere Application Server 7.0 and Tomcat) will + * time out or terminate the connection if the last extension in the + * 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_signature_algorithms_xtn, &ssl3_SendSigAlgsXtn }, + { ssl_tls13_cookie_xtn, &tls13_ClientSendHrrCookieXtn }, + { ssl_tls13_psk_key_exchange_modes_xtn, &tls13_ClientSendPskModesXtn }, + { ssl_tls13_post_handshake_auth_xtn, &tls13_ClientSendPostHandshakeAuthXtn }, + { ssl_record_size_limit_xtn, &ssl_SendRecordSizeLimitXtn }, + { ssl_certificate_compression_xtn, &ssl3_SendCertificateCompressionXtn }, + /* TLS 1.3 GREASE extensions - 1 zero byte. */ + { ssl_tls13_grease_xtn, &tls13_SendGreaseXtn }, + /* The pre_shared_key extension MUST be last. */ + { ssl_tls13_pre_shared_key_xtn, &tls13_ClientSendPreSharedKeyXtn }, + { 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 }, + /* TLS 1.3 GREASE extension. */ + { ssl_tls13_grease_xtn, &tls13_SendEmptyGreaseXtn }, + { ssl_certificate_compression_xtn, &ssl3_SendCertificateCompressionXtn }, + { 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 }, + { ssl_tls13_encrypted_client_hello_xtn, &tls13_ServerSendHrrEchXtn }, + { 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_delegated_credentials_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_renegotiation_info_xtn, ssl_ext_native }, + { ssl_tls13_encrypted_client_hello_xtn, ssl_ext_native_only }, + { ssl_certificate_compression_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; +} + +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) +{ + unsigned int i; + for (i = 0; i < len; i++) { + if (ex_type == array[i]) + return PR_TRUE; + } + return PR_FALSE; +} + +PRBool +ssl3_ExtensionNegotiated(const sslSocket *ss, PRUint16 ex_type) +{ + const TLSExtensionData *xtnData = &ss->xtnData; + return arrayContainsExtension(xtnData->negotiated, + 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_ExtensionAdvertised(const sslSocket *ss, PRUint16 ex_type) +{ + const TLSExtensionData *xtnData = &ss->xtnData; + return arrayContainsExtension(xtnData->advertised, + xtnData->numAdvertised, ex_type); +} + +PRBool +ssl3_ExtensionAdvertisedClientHelloInner(const sslSocket *ss, PRUint16 ex_type) +{ + const TLSExtensionData *xtnData = &ss->xtnData; + return arrayContainsExtension(xtnData->echAdvertised, + xtnData->echNumAdvertised, ex_type); +} + +/* Go through hello extensions in |b| and deserialize + * them into the list in |ss->ssl3.hs.remoteExtensions|. + * The only checking we do in this point is for duplicates. + * + * IMPORTANT: This list just contains pointers to the incoming + * buffer so they can only be used during ClientHello processing. + */ +SECStatus +ssl3_ParseExtensions(sslSocket *ss, PRUint8 **b, PRUint32 *length) +{ + /* Clean out the extensions list. */ + ssl3_DestroyRemoteExtensions(&ss->ssl3.hs.remoteExtensions); + + while (*length) { + SECStatus rv; + PRUint32 extension_type; + SECItem extension_data = { siBuffer, NULL, 0 }; + TLSExtension *extension; + PRCList *cursor; + + /* Get the extension's type field */ + rv = ssl3_ConsumeHandshakeNumber(ss, &extension_type, 2, b, length); + if (rv != SECSuccess) { + return SECFailure; /* alert already sent */ + } + + /* Check whether an extension has been sent multiple times. */ + for (cursor = PR_NEXT_LINK(&ss->ssl3.hs.remoteExtensions); + cursor != &ss->ssl3.hs.remoteExtensions; + cursor = PR_NEXT_LINK(cursor)) { + if (((TLSExtension *)cursor)->type == extension_type) { + (void)SSL3_SendAlert(ss, alert_fatal, illegal_parameter); + PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION); + return SECFailure; + } + } + + /* Get the data for this extension, so we can pass it or skip it. */ + rv = ssl3_ConsumeHandshakeVariable(ss, &extension_data, 2, b, length); + if (rv != SECSuccess) { + return rv; /* alert already sent */ + } + + SSL_TRC(10, ("%d: SSL3[%d]: parsed extension %d len=%u", + SSL_GETPID(), ss->fd, extension_type, extension_data.len)); + + extension = PORT_ZNew(TLSExtension); + if (!extension) { + return SECFailure; + } + + extension->type = (PRUint16)extension_type; + extension->data = extension_data; + PR_APPEND_LINK(&extension->link, &ss->ssl3.hs.remoteExtensions); + } + + return SECSuccess; +} + +TLSExtension * +ssl3_FindExtension(sslSocket *ss, SSLExtensionType extension_type) +{ + PRCList *cursor; + + for (cursor = PR_NEXT_LINK(&ss->ssl3.hs.remoteExtensions); + cursor != &ss->ssl3.hs.remoteExtensions; + cursor = PR_NEXT_LINK(cursor)) { + TLSExtension *extension = (TLSExtension *)cursor; + + if (extension->type == extension_type) { + return extension; + } + } + + 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) { + SECItem tmp = extension->data; + + rv = (*handler->ex_handler)(ss, &ss->xtnData, &tmp); + 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. + * Servers ignore any extensions with unknown extension types. + * Clients reject any extensions with unadvertised extension types + * + * In TLS >= 1.3, the client checks that extensions appear in the + * right phase. + */ +SECStatus +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) || + (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 (message) { + case ssl_hs_client_hello: + handlers = clientHelloHandlers; + break; + case ssl_hs_new_session_ticket: + PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3); + handlers = newSessionTicketHandlers; + break; + case ssl_hs_hello_retry_request: + handlers = helloRetryRequestHandlers; + break; + case ssl_hs_encrypted_extensions: + PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3); + /* fall through */ + case ssl_hs_server_hello: + if (ss->version > SSL_LIBRARY_VERSION_3_0) { + handlers = serverHelloHandlersTLS; + } else { + handlers = serverHelloHandlersSSL3; + } + break; + case ssl_hs_certificate: + PORT_Assert(!ss->sec.isServer); + handlers = serverCertificateHandlers; + break; + case ssl_hs_certificate_request: + PORT_Assert(!ss->sec.isServer); + handlers = certificateRequestHandlers; + break; + default: + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + PORT_Assert(0); + return SECFailure; + } + + for (cursor = PR_NEXT_LINK(&ss->ssl3.hs.remoteExtensions); + cursor != &ss->ssl3.hs.remoteExtensions; + cursor = PR_NEXT_LINK(cursor)) { + TLSExtension *extension = (TLSExtension *)cursor; + SECStatus rv; + + /* Check whether the server sent an extension which was not advertised + * 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)) { + if (!ssl3_ExtensionAdvertised(ss, extension->type)) { + SSL_TRC(10, ("Server sent xtn type=%d which is invalid for the CHO", extension->type)); + (void)SSL3_SendAlert(ss, alert_fatal, unsupported_extension); + PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION); + return SECFailure; + } + /* If we offered ECH, we also check whether the extension is compatible with + * the Client Hello Inner. We don't yet know whether the server accepted ECH, + * so we only store this for now. If we later accept, we check this boolean + * and reject with an unsupported_extension alert if it is set. */ + if (ss->ssl3.hs.echHpkeCtx && !ssl3_ExtensionAdvertisedClientHelloInner(ss, extension->type)) { + SSL_TRC(10, ("Server sent xtn type=%d which is invalid for the CHI", extension->type)); + ss->ssl3.hs.echInvalidExtension = PR_TRUE; + } + } + + /* Check that this is a legal extension in TLS 1.3 */ + 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. */ + } + /* RFC8446 Section 4.2 - Implementations MUST NOT send extension responses if + * the remote endpoint did not send the corresponding extension request ... + * Upon receiving such an extension, an endpoint MUST abort the handshake with + * an "unsupported_extension" alert. */ + SSL_TRC(3, ("%d: TLS13: unknown extension %d in message %d", + SSL_GETPID(), extension, message)); + tls13_FatalError(ss, SSL_ERROR_RX_UNEXPECTED_EXTENSION, + unsupported_extension); + return SECFailure; + case tls13_extension_disallowed: + /* RFC8446 Section 4.2 - If an implementation receives an extension which it + * recognizes and which is not specified for the message in which it appears, + * it MUST abort the handshake with an "illegal_parameter" alert. */ + SSL_TRC(3, ("%d: TLS13: disallowed extension %d in message %d", + SSL_GETPID(), extension, message)); + tls13_FatalError(ss, SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION, + illegal_parameter); + return SECFailure; + } + } + + /* Special check for this being the last extension if it's + * PreSharedKey */ + if (ss->sec.isServer && isTLS13 && + (extension->type == ssl_tls13_pre_shared_key_xtn) && + (PR_NEXT_LINK(cursor) != &ss->ssl3.hs.remoteExtensions)) { + tls13_FatalError(ss, + SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, + illegal_parameter); + return SECFailure; + } + + rv = ssl_CallExtensionHandler(ss, message, extension, handlers); + if (rv != SECSuccess) { + return SECFailure; + } + } + return SECSuccess; +} + +/* Syntactic sugar around ssl3_ParseExtensions and + * ssl3_HandleParsedExtensions. */ +SECStatus +ssl3_HandleExtensions(sslSocket *ss, + PRUint8 **b, PRUint32 *length, + SSLHandshakeType handshakeMessage) +{ + SECStatus rv; + + rv = ssl3_ParseExtensions(ss, b, length); + if (rv != SECSuccess) + return rv; + + rv = ssl3_HandleParsedExtensions(ss, handshakeMessage); + if (rv != SECSuccess) + return rv; + + ssl3_DestroyRemoteExtensions(&ss->ssl3.hs.remoteExtensions); + return SECSuccess; +} + +/* Add a callback function to the table of senders of server hello extensions. + */ +SECStatus +ssl3_RegisterExtensionSender(const sslSocket *ss, + TLSExtensionData *xtnData, + PRUint16 ex_type, + sslExtensionBuilderFunc cb) +{ + int i; + sslExtensionBuilder *sender; + if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { + sender = &xtnData->serverHelloSenders[0]; + } else { + 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_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(0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + } + for (i = 0; i < SSL_MAX_EXTENSIONS; ++i, ++sender) { + if (!sender->ex_sender) { + sender->ex_type = ex_type; + sender->ex_sender = cb; + return SECSuccess; + } + /* detect duplicate senders */ + PORT_Assert(sender->ex_type != ex_type); + if (sender->ex_type == ex_type) { + /* duplicate */ + break; + } + } + PORT_Assert(i < SSL_MAX_EXTENSIONS); /* table needs to grow */ + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; +} + +SECStatus +ssl_CallCustomExtensionSenders(sslSocket *ss, sslBuffer *buf, + SSLHandshakeType message) +{ + sslBuffer tail = SSL_BUFFER_EMPTY; + SECStatus rv; + PRCList *cursor; + + /* 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; + } + + /* 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_ech_outer_client_hello || + message == ssl_hs_certificate_request) { + ss->xtnData.advertised[ss->xtnData.numAdvertised++] = hook->type; + } + } + + rv = sslBuffer_Append(buf, tail.buf, tail.len); + if (rv != SECSuccess) { + goto loser; /* Code already set. */ + } + + 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); + + /* Clear out any extensions previously advertised */ + ss->xtnData.numAdvertised = 0; + ss->xtnData.echNumAdvertised = 0; + + switch (message) { + case ssl_hs_client_hello: + if (ss->vrange.max > SSL_LIBRARY_VERSION_3_0) { + /* Use TLS ClientHello Extension Permutation? */ + if (ss->opt.enableChXtnPermutation) { + sender = ss->ssl3.hs.chExtensionPermutation; + } else { + 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) { + PRUint16 ex_type = sender->ex_type; + 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; + } + + /* If TLS 1.3 GREASE is enabled, replace ssl_tls13_grease_xtn dummy + * GREASE extension types with randomly generated GREASE value. */ + rv = tls13_MaybeGreaseExtensionType(ss, message, &ex_type); + if (rv != SECSuccess) { + goto loser; /* Code already set. */ + } + + rv = sslBuffer_AppendNumber(buf, 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++] = + ex_type; + } + } + + if (!PR_CLIST_IS_EMPTY(&ss->extensionHooks)) { + if (message == ssl_hs_client_hello && ss->opt.callExtensionWriterOnEchInner) { + message = ssl_hs_ech_outer_client_hello; + } + 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 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 (clientHelloLength < 256 || clientHelloLength >= 512) { + return 0; + } + + extensionLen = 512 - clientHelloLength; + /* 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; +} + +/* Manually insert an extension, retaining the position of the PSK + * extension, if present. */ +SECStatus +ssl3_EmplaceExtension(sslSocket *ss, sslBuffer *buf, PRUint16 exType, + const PRUint8 *data, unsigned int len, PRBool advertise) +{ + SECStatus rv; + unsigned int tailLen; + + /* 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 + len); + if (rv != SECSuccess) { + return SECFailure; + } + PORT_Memmove(buf->buf + ss->xtnData.lastXtnOffset + 4 + len, + buf->buf + ss->xtnData.lastXtnOffset, + tailLen); + buf->len = ss->xtnData.lastXtnOffset; + } else { + tailLen = 0; + } + if (exType == ssl_tls13_encrypted_client_hello_xtn) { + ss->xtnData.echXtnOffset = buf->len; + } + rv = sslBuffer_AppendNumber(buf, exType, 2); + if (rv != SECSuccess) { + return SECFailure; /* Code already set. */ + } + rv = sslBuffer_AppendVariable(buf, data, len, 2); + if (rv != SECSuccess) { + return SECFailure; /* Code already set. */ + } + + if (ss->xtnData.lastXtnOffset) { + ss->xtnData.lastXtnOffset += 4 + len; + } + + buf->len += tailLen; + + /* False only to retain behavior with padding_xtn. Maybe + * we can just mark that advertised as well? TODO */ + if (advertise) { + ss->xtnData.advertised[ss->xtnData.numAdvertised++] = exType; + } + + return SECSuccess; +} + +/* 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(sslSocket *ss, unsigned int prefixLen, + sslBuffer *buf) +{ + static unsigned char padding[252] = { 0 }; + unsigned int paddingLen; + /* Exit early if an application-provided extension hook + * already added padding. */ + if (ssl3_ExtensionAdvertised(ss, ssl_padding_xtn)) { + return SECSuccess; + } + + /* 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; + } + + return ssl3_EmplaceExtension(ss, buf, ssl_padding_xtn, padding, paddingLen, PR_FALSE); +} + +void +ssl3_MoveRemoteExtensions(PRCList *dst, PRCList *src) +{ + PRCList *cur_p; + while (!PR_CLIST_IS_EMPTY(src)) { + cur_p = PR_LIST_TAIL(src); + PR_REMOVE_LINK(cur_p); + PR_INSERT_LINK(cur_p, dst); + } +} + +void +ssl3_DestroyRemoteExtensions(PRCList *list) +{ + PRCList *cur_p; + + while (!PR_CLIST_IS_EMPTY(list)) { + cur_p = PR_LIST_TAIL(list); + PR_REMOVE_LINK(cur_p); + PORT_Free(cur_p); + } +} + +/* Initialize the extension data block. */ +void +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); + xtnData->echAdvertised = PORT_ZNewArray(PRUint16, advertisedMax); + + xtnData->peerDelegCred = NULL; + xtnData->peerRequestedDelegCred = PR_FALSE; + xtnData->sendingDelegCredToPeer = PR_FALSE; + xtnData->selectedPsk = NULL; +} + +void +ssl3_DestroyExtensionData(TLSExtensionData *xtnData) +{ + ssl3_FreeSniNameArray(xtnData); + PORT_Free(xtnData->sigSchemes); + PORT_Free(xtnData->delegCredSigSchemes); + PORT_Free(xtnData->delegCredSigSchemesAdvertised); + SECITEM_FreeItem(&xtnData->nextProto, PR_FALSE); + tls13_DestroyKeyShares(&xtnData->remoteKeyShares); + 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); + PORT_Free(xtnData->echAdvertised); + tls13_DestroyDelegatedCredential(xtnData->peerDelegCred); + + tls13_DestroyEchXtnState(xtnData->ech); + xtnData->ech = NULL; +} + +/* Free everything that has been allocated and then reset back to + * the starting state. */ +void +ssl3_ResetExtensionData(TLSExtensionData *xtnData, const sslSocket *ss) +{ + 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) +{ + (void)SSL3_SendAlert((sslSocket *)ss, level, desc); +} + +void +ssl3_ExtDecodeError(const sslSocket *ss) +{ + (void)ssl3_DecodeError((sslSocket *)ss); +} + +SECStatus +ssl3_ExtConsumeHandshake(const sslSocket *ss, void *v, PRUint32 bytes, + PRUint8 **b, PRUint32 *length) +{ + return ssl3_ConsumeHandshake((sslSocket *)ss, v, bytes, b, length); +} + +SECStatus +ssl3_ExtConsumeHandshakeNumber(const sslSocket *ss, PRUint32 *num, + PRUint32 bytes, PRUint8 **b, PRUint32 *length) +{ + return ssl3_ConsumeHandshakeNumber((sslSocket *)ss, num, bytes, b, length); +} + +SECStatus +ssl3_ExtConsumeHandshakeVariable(const sslSocket *ss, SECItem *i, + PRUint32 bytes, PRUint8 **b, + PRUint32 *length) +{ + return ssl3_ConsumeHandshakeVariable((sslSocket *)ss, i, bytes, b, length); +} + +SECStatus +tls_ClientHelloExtensionPermutationSetup(sslSocket *ss) +{ + size_t buildersLen = PR_ARRAY_SIZE(clientHelloSendersTLS); + const size_t buildersSize = (sizeof(sslExtensionBuilder) * buildersLen); + /* Psk Extension and then NULL entry MUST be last. */ + const size_t permutationLen = buildersLen - 2; + + /* There shouldn't already be a stored permutation. */ + PR_ASSERT(!ss->ssl3.hs.chExtensionPermutation); + + /* This shuffle handles up to 256 extensions. */ + PR_ASSERT(buildersLen < 256); + uint8_t permutation[256] = { 0 }; + + sslExtensionBuilder *builders = PORT_ZAlloc(buildersSize); + if (!builders) { + return SECFailure; + } + + /* Get a working copy of default builders. */ + PORT_Memcpy(builders, clientHelloSendersTLS, buildersSize); + + /* Get permutation randoms. */ + if (PK11_GenerateRandom(permutation, permutationLen) != SECSuccess) { + PORT_Free(builders); + return SECFailure; + } + + /* Fisher-Yates Shuffle */ + for (size_t i = permutationLen - 1; i > 0; i--) { + size_t idx = permutation[i - 1] % (i + 1); + sslExtensionBuilder tmp = builders[i]; + builders[i] = builders[idx]; + builders[idx] = tmp; + } + + /* Make sure that Psk extension is penultimate (before NULL entry). */ + PR_ASSERT(builders[buildersLen - 2].ex_type == ssl_tls13_pre_shared_key_xtn); + PR_ASSERT(builders[buildersLen - 2].ex_sender == clientHelloSendersTLS[buildersLen - 2].ex_sender); + + ss->ssl3.hs.chExtensionPermutation = builders; + return SECSuccess; +} + +void +tls_ClientHelloExtensionPermutationDestroy(sslSocket *ss) +{ + if (ss->ssl3.hs.chExtensionPermutation) { + PORT_Free(ss->ssl3.hs.chExtensionPermutation); + ss->ssl3.hs.chExtensionPermutation = NULL; + } +} |