summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/ssl/ssl3ext.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/ssl/ssl3ext.c')
-rw-r--r--security/nss/lib/ssl/ssl3ext.c709
1 files changed, 585 insertions, 124 deletions
diff --git a/security/nss/lib/ssl/ssl3ext.c b/security/nss/lib/ssl/ssl3ext.c
index 271084cf7..ade280903 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,39 @@ 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:
+ 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 +536,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 +549,7 @@ ssl3_HandleParsedExtensions(sslSocket *ss,
SECStatus
ssl3_HandleExtensions(sslSocket *ss,
PRUint8 **b, PRUint32 *length,
- SSL3HandshakeType handshakeMessage)
+ SSLHandshakeType handshakeMessage)
{
SECStatus rv;
@@ -383,21 +571,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 +615,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 +914,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)