| /* -*- 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_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_tls13_ech_is_inner_xtn, &tls13_ServerHandleEchIsInnerXtn }, |
| { ssl_record_size_limit_xtn, &ssl_HandleRecordSizeLimitXtn }, |
| { 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 }, |
| { 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 }, |
| { 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[] = |
| { |
| { 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 }, |
| /* 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 }, |
| { 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_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 }, |
| }; |
| |
| 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) |
| { |
| 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); |
| } |
| |
| /* 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) && |
| !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 && |
| !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; |
| } |
| } |
| |
| /* 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; |
| } |
| |
| static 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_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; |
| |
| 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 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; |
| } |
| |
| 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_APPEND_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->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); |
| 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); |
| } |