1377 lines
42 KiB
C
1377 lines
42 KiB
C
/**
|
|
* @file extDecode.c
|
|
* @version $Format:%h%d$
|
|
*
|
|
* CLIENT_HELLO and SERVER_HELLO extension parsing
|
|
*/
|
|
/*
|
|
* Copyright (c) 2013-2018 Rambus Inc.
|
|
* Copyright (c) PeerSec Networks, 2002-2011
|
|
* All Rights Reserved
|
|
*
|
|
* The latest version of this code is available at http://www.matrixssl.org
|
|
*
|
|
* This software is open source; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This General Public License does NOT permit incorporating this software
|
|
* into proprietary programs. If you are unable to comply with the GPL, a
|
|
* commercial license for this software may be purchased from Rambus at
|
|
* http://www.rambus.com/
|
|
*
|
|
* This program is distributed in WITHOUT ANY WARRANTY; without even the
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
* See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
*/
|
|
/******************************************************************************/
|
|
|
|
#include "matrixsslImpl.h"
|
|
|
|
/******************************************************************************/
|
|
|
|
#ifdef USE_SERVER_SIDE_SSL
|
|
static int ClientHelloExt(ssl_t *ssl, unsigned short extType,
|
|
unsigned short extLen, const unsigned char *c);
|
|
#endif
|
|
#ifdef USE_CLIENT_SIDE_SSL
|
|
static int ServerHelloExt(ssl_t *ssl, unsigned short extType,
|
|
unsigned short extLen, const unsigned char *c);
|
|
#endif
|
|
#ifdef USE_ALPN
|
|
static int dealWithAlpnExt(ssl_t *ssl, const unsigned char *c,
|
|
unsigned short extLen);
|
|
#endif
|
|
|
|
|
|
#ifdef USE_SERVER_SIDE_SSL
|
|
#ifdef USE_TLS_1_2
|
|
int32_t tlsParseSignatureAlgorithms(ssl_t *ssl,
|
|
const unsigned char *c,
|
|
unsigned short extLen)
|
|
{
|
|
# ifdef USE_ONLY_PSK_CIPHER_SUITE
|
|
/* No need for signatures when using only PSK suites. */
|
|
psTracePrintExtensionParse(ssl, EXT_SIGNATURE_ALGORITHMS);
|
|
return MATRIXSSL_SUCCESS;
|
|
# else
|
|
unsigned short tmpLen;
|
|
uint16_t sigAlg;
|
|
sslKeySelectInfo_t *keySelect;
|
|
|
|
psTracePrintExtensionParse(ssl, EXT_SIGNATURE_ALGORITHMS);
|
|
|
|
/* This extension is responsible for telling the server which
|
|
sig algorithms it accepts so here we are saving them
|
|
aside for when user chooses identity certificate.
|
|
https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */
|
|
|
|
|
|
/* Minimum length of 2 b type, 2 b alg */
|
|
/* Arbitrary Max of 64 suites */
|
|
if (extLen > (2 + 128) || extLen < 4 || (extLen & 1))
|
|
{
|
|
psTraceErrr("Malformed sig_alg len\n");
|
|
goto out_decode_error;
|
|
}
|
|
|
|
tmpLen = *c << 8; c++;
|
|
tmpLen |= *c; c++;
|
|
extLen -= 2;
|
|
|
|
if ((uint32) tmpLen > extLen || tmpLen < 2 || (tmpLen & 1))
|
|
{
|
|
psTraceErrr("Malformed sig_alg extension\n");
|
|
goto out_decode_error;
|
|
}
|
|
keySelect = &ssl->sec.keySelect;
|
|
|
|
/* list of 2 byte pairs in a hash/sig format that
|
|
need to be searched to find match with server
|
|
cert sigAlgorithm
|
|
Test if client will be able to accept our sigs
|
|
based on what our idenity certificate is */
|
|
while (tmpLen >= 2 && extLen >= 2)
|
|
{
|
|
sigAlg = (c[0] << 8) + c[1];
|
|
/* Those algorithms that are not supported by us will be filtered
|
|
out here; ssl->hashSigAlg will contain the shared ones. */
|
|
#if defined(USE_SM2) && defined(USE_SM3)
|
|
/*Fix SM2-SM3 in message*/
|
|
if (sigAlg == 0x0707)
|
|
{
|
|
sigAlg = sigalg_sm2sig_sm3;
|
|
}
|
|
#endif
|
|
if (findFromUint16Array(ssl->supportedSigAlgs,
|
|
ssl->supportedSigAlgsLen,
|
|
sigAlg) != PS_FAILURE)
|
|
{
|
|
/* This client supplied sig_alg is on our supported list */
|
|
ssl->hashSigAlg |= HASH_SIG_MASK(c[0], c[1]);
|
|
}
|
|
if (keySelect->peerSigAlgsLen < TLS_MAX_SIGNATURE_ALGORITHMS)
|
|
{
|
|
keySelect->peerSigAlgs[keySelect->peerSigAlgsLen++] = sigAlg;
|
|
}
|
|
ssl->peerSigAlg |= HASH_SIG_MASK(c[0], c[1]);
|
|
c += 2;
|
|
tmpLen -= 2;
|
|
extLen -= 2;
|
|
}
|
|
|
|
psTracePrintSigAlgs(INDENT_EXTENSION,
|
|
"signature_algorithms",
|
|
ssl->peerSigAlg,
|
|
PS_FALSE);
|
|
|
|
return MATRIXSSL_SUCCESS;
|
|
|
|
out_decode_error:
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
# endif /* USE_ONLY_PSK_CIPHER_SUITE */
|
|
}
|
|
#endif /* USE_TLS_1_2 */
|
|
|
|
# if defined(USE_TLS_1_2) || defined(USE_TLS_1_3)
|
|
# ifdef USE_ECC_CIPHER_SUITE
|
|
int32_t tlsParseSupportedGroups(ssl_t *ssl,
|
|
const unsigned char *c,
|
|
unsigned short extLen)
|
|
{
|
|
unsigned short dataLen;
|
|
uint32 ecFlags;
|
|
|
|
|
|
psAssert(MATRIX_IS_SERVER(ssl));
|
|
|
|
psTracePrintExtensionParse(ssl, EXT_ELLIPTIC_CURVE);
|
|
/* Minimum is 2 b dataLen and 2 b cipher */
|
|
if (extLen < 4)
|
|
{
|
|
psTraceErrr("Invalid ECC Curve len\n");
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
dataLen = *c << 8; c++;
|
|
dataLen += *c; c++;
|
|
extLen -= 2;
|
|
if (dataLen > extLen || dataLen < 2 || (dataLen & 1))
|
|
{
|
|
psTraceErrr("Malformed ECC Curve extension\n");
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* Matching EC curve logic */
|
|
ecFlags = IS_RECVD_EXT; /* Flag if we got it */
|
|
ssl->ecInfo.ecCurveId = 0;
|
|
psTracePrintTls13NamedGroupList(INDENT_EXTENSION,
|
|
"supported_groups/elliptic_curves",
|
|
c, dataLen,
|
|
ssl,
|
|
PS_FALSE);
|
|
while (dataLen >= 2 && extLen >= 2)
|
|
{
|
|
unsigned short curveId;
|
|
|
|
curveId = *c << 8; c++;
|
|
curveId += *c; c++;
|
|
# if defined(USE_SM2) && defined(USE_SM3)
|
|
if (curveId == namedgroup_x448)
|
|
{
|
|
curveId = namedgroup_curveSM2;
|
|
}
|
|
# endif
|
|
dataLen -= 2;
|
|
extLen -= 2;
|
|
# ifdef USE_TLS_1_3
|
|
if (tls13AddPeerSupportedGroup(ssl, curveId) < 0)
|
|
{
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# endif /* USE_TLS_1_3 */
|
|
/*
|
|
Find the curves that are in common between client and server.
|
|
ssl->ecInfo.ecFlags defaults to all curves compiled into the library.
|
|
*/
|
|
if (psTestUserEcID(curveId, ssl->ecInfo.ecFlags) == 0)
|
|
{
|
|
/*
|
|
Client sends curves in priority order, so choose the first
|
|
one we have in common. If ECDHE, it will be used after
|
|
this CLIENT_HELLO parse as the ephemeral key gen curve.
|
|
If ECDH, it will be used to find a matching cert, and for
|
|
key exchange.
|
|
*/
|
|
if (ecFlags == IS_RECVD_EXT)
|
|
{
|
|
ssl->ecInfo.ecCurveId = curveId;
|
|
}
|
|
ecFlags |= curveIdToFlag(curveId);
|
|
}
|
|
}
|
|
|
|
ssl->ecInfo.ecFlags = ecFlags;
|
|
|
|
return MATRIXSSL_SUCCESS;
|
|
}
|
|
# endif /* USE_ECC_CIPHER_SUITE */
|
|
#endif /* USE_TLS_1_2 || USE_TLS_1_3 */
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
Parse the ClientHello extension list.
|
|
*/
|
|
int32 parseClientHelloExtensions(ssl_t *ssl, unsigned char **cp, unsigned short len)
|
|
{
|
|
unsigned short extLen, extType;
|
|
unsigned char *c, *end;
|
|
int rc;
|
|
|
|
# ifdef ENABLE_SECURE_REHANDSHAKES
|
|
unsigned short renegotiationExtSent = 0;
|
|
# endif
|
|
|
|
c = *cp;
|
|
end = c + len;
|
|
|
|
/* Clear extFlags in case of rehandshakes */
|
|
ssl->extFlags.truncated_hmac = 0;
|
|
ssl->extFlags.sni = 0;
|
|
ssl->extFlags.sni_in_last_client_hello = 0;
|
|
ssl->extFlags.session_id = 0;
|
|
ssl->extFlags.session_ticket = 0;
|
|
ssl->extFlags.extended_master_secret = 0;
|
|
ssl->extFlags.status_request = 0;
|
|
|
|
/* There could be extension data to parse here:
|
|
Two byte length and extension info.
|
|
http://www.faqs.org/rfcs/rfc3546.html
|
|
|
|
NOTE: This c != end test is only safe because ClientHello is the
|
|
only record/message in the flight of supported handshake protocols. */
|
|
if (c != end)
|
|
{
|
|
if (end - c < 2)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid extension header len\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
extLen = *c << 8; c++; /* Total length of list, in bytes */
|
|
extLen += *c; c++;
|
|
/* extLen must be minimum 2 b type 2 b len and 0 b value */
|
|
if ((uint32) (end - c) != extLen || extLen < 4)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid extension header len\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
# ifdef USE_TLS_1_2
|
|
ssl->hashSigAlg = 0;
|
|
# endif
|
|
while (c != end)
|
|
{
|
|
if (end - c < 2)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid extension header len\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
extType = *c << 8; c++; /* Individual hello ext */
|
|
extType += *c; c++;
|
|
if (end - c < 2)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid extension header len\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
extLen = *c << 8; c++; /* length of one extension */
|
|
extLen += *c; c++;
|
|
/* Minimum extension value len is 0 bytes */
|
|
if ((uint32) (end - c) < extLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid extension header len\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# ifdef ENABLE_SECURE_REHANDSHAKES
|
|
if (extType == EXT_RENEGOTIATION_INFO)
|
|
{
|
|
renegotiationExtSent = 1;
|
|
}
|
|
# endif
|
|
/* Parse incoming client extensions we support. */
|
|
if ((rc = ClientHelloExt(ssl, extType, extLen, c)) < 0)
|
|
{
|
|
/* On error, ssl->error will have been set */
|
|
return rc;
|
|
}
|
|
c += extLen;
|
|
}
|
|
}
|
|
|
|
/* Handle the extensions that were missing or not what we wanted */
|
|
if (ssl->extFlags.require_extended_master_secret == 1 &&
|
|
ssl->extFlags.extended_master_secret == 0)
|
|
{
|
|
psTraceErrr("Client doesn't support extended master secret\n");
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
# if defined(USE_TLS_1_2) && defined(USE_CERT_PARSE)
|
|
if (NGTD_VER(ssl, v_tls_with_signature_algorithms))
|
|
{
|
|
if (!ssl->hashSigAlg)
|
|
{
|
|
# ifdef USE_SHA1
|
|
/* Client didn't send the extension at all. Unfortunately, spec says we
|
|
have to assume SHA1 in this case, even though both peers are
|
|
already calculating a SHA256 based handshake hash. */
|
|
# ifdef USE_RSA_CIPHER_SUITE
|
|
ssl->hashSigAlg |= HASH_SIG_SHA1_RSA_MASK;
|
|
# endif
|
|
# ifdef USE_ECC_CIPHER_SUITE
|
|
ssl->hashSigAlg |= HASH_SIG_SHA1_ECDSA_MASK;
|
|
# endif
|
|
# else
|
|
if (ssl->flags & SSL_FLAGS_CLIENT_AUTH)
|
|
{
|
|
psTraceErrr("Client didn't provide hashSigAlg and sha1 not supported\n");
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# endif /* USE_SHA1 */
|
|
}
|
|
}
|
|
# endif /* USE_TLS_1_2 */
|
|
|
|
# ifdef USE_STATELESS_SESSION_TICKETS
|
|
/* If session ID was sent that we didn't like AND no ticket was sent
|
|
then we can forget we ever received a sessionID now */
|
|
if (ssl->extFlags.session_id == 1)
|
|
{
|
|
Memset(ssl->sessionId, 0, SSL_MAX_SESSION_ID_SIZE);
|
|
ssl->sessionIdLen = 0;
|
|
}
|
|
ssl->extFlags.session_id = 0;
|
|
ssl->extFlags.session_ticket = 0;
|
|
# endif
|
|
|
|
# ifdef ENABLE_SECURE_REHANDSHAKES
|
|
if (!renegotiationExtSent)
|
|
{
|
|
# ifdef REQUIRE_SECURE_REHANDSHAKES
|
|
/* Check if SCSV was sent instead */
|
|
if (ssl->secureRenegotiationFlag == PS_FALSE &&
|
|
ssl->myVerifyDataLen == 0)
|
|
{
|
|
psTraceErrr("Client doesn't support renegotiation hello\n");
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# endif /* REQUIRE_SECURE_REHANDSHAKES */
|
|
if (ssl->secureRenegotiationFlag == PS_TRUE &&
|
|
ssl->myVerifyDataLen > 0)
|
|
{
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
psTraceErrr("Cln missing renegotiationInfo on re-hndshk\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# ifndef ENABLE_INSECURE_REHANDSHAKES
|
|
if (ssl->secureRenegotiationFlag == PS_FALSE &&
|
|
ssl->myVerifyDataLen > 0)
|
|
{
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
psTraceErrr("Cln attempting insecure handshake\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# endif /* !ENABLE_INSECURE_REHANDSHAKES */
|
|
}
|
|
# endif /* ENABLE_SECURE_REHANDSHAKES */
|
|
|
|
*cp = c;
|
|
return PS_SUCCESS;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
Parse a single client extension
|
|
*/
|
|
static int ClientHelloExt(ssl_t *ssl,
|
|
unsigned short extType,
|
|
unsigned short extLen,
|
|
const unsigned char *c)
|
|
{
|
|
int i;
|
|
int32_t rc;
|
|
# ifdef USE_ECC_CIPHER_SUITE
|
|
unsigned short dataLen;
|
|
# elif defined USE_OCSP_RESPONSE
|
|
unsigned short dataLen;
|
|
# endif /* USE_ECC_CIPHER_SUITE || USE_OCSP_RESPONSE */
|
|
|
|
switch (extType)
|
|
{
|
|
|
|
/**************************************************************************/
|
|
|
|
case EXT_TRUNCATED_HMAC:
|
|
psTracePrintExtensionParse(ssl, EXT_TRUNCATED_HMAC);
|
|
if (extLen != 0)
|
|
{
|
|
psTraceErrr("Bad truncated HMAC extension\n");
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* User could have disabled for this session */
|
|
if (!ssl->extFlags.deny_truncated_hmac)
|
|
{
|
|
ssl->extFlags.truncated_hmac = 1;
|
|
}
|
|
break;
|
|
|
|
case EXT_EXTENDED_MASTER_SECRET:
|
|
psTracePrintExtensionParse(ssl, EXT_EXTENDED_MASTER_SECRET);
|
|
if (extLen != 0)
|
|
{
|
|
psTraceErrr("Bad extended master secret extension\n");
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
ssl->extFlags.extended_master_secret = 1;
|
|
break;
|
|
|
|
/**************************************************************************/
|
|
|
|
case EXT_MAX_FRAGMENT_LEN:
|
|
psTracePrintExtensionParse(ssl, EXT_MAX_FRAGMENT_LEN);
|
|
if (extLen != 1)
|
|
{
|
|
psTraceErrr("Invalid frag len ext len\n");
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* User could have disabled for this session using
|
|
the session options */
|
|
if (!ssl->extFlags.deny_max_fragment_len)
|
|
{
|
|
if (*c == 0x1)
|
|
{
|
|
ssl->maxPtFrag = 0x200;
|
|
}
|
|
else if (*c == 0x2)
|
|
{
|
|
ssl->maxPtFrag = 0x400;
|
|
}
|
|
else if (*c == 0x3)
|
|
{
|
|
ssl->maxPtFrag = 0x800;
|
|
}
|
|
else if (*c == 0x4)
|
|
{
|
|
ssl->maxPtFrag = 0x1000;
|
|
}
|
|
else
|
|
{
|
|
psTraceErrr("Client sent bad frag len value\n");
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
break;
|
|
|
|
/**************************************************************************/
|
|
|
|
case EXT_SNI:
|
|
psTracePrintExtensionParse(ssl, EXT_SNI);
|
|
|
|
/* Must hold (2 b len + 1 b zero) + 2 b len */
|
|
if (extLen < 5)
|
|
{
|
|
psTraceErrr("Invalid server name ext len\n");
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* Two length bytes. May seem odd to ignore but
|
|
the inner length is repeated right below after
|
|
the expected 0x0 bytes */
|
|
i = *c << 8; c++;
|
|
i += *c; c++;
|
|
if (*c++ != 0x0)
|
|
{
|
|
psTraceErrr("Expected host_name in SNI ext\n");
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
(void) i; /* TODO - validate against length determined below */
|
|
extLen -= 3;
|
|
i = *c << 8; c++;
|
|
i += *c; c++;
|
|
extLen -= 2; /* Length check covered above */
|
|
/* Arbitrary length cap between 1 and min(extlen,255) */
|
|
if ((int32) extLen < i || i > 255 || i <= 0)
|
|
{
|
|
psTraceErrr("Invalid host name ext len\n");
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if (ssl->expectedName)
|
|
{
|
|
psFree(ssl->expectedName, ssl->sPool);
|
|
}
|
|
if ((ssl->expectedName =
|
|
psMalloc(ssl->sPool, i + 1)) == NULL)
|
|
{
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
Memcpy(ssl->expectedName, c, i);
|
|
ssl->expectedName[i] = '\0';
|
|
psTracePrintServerName(INDENT_EXTENSION,
|
|
"HostName",
|
|
ssl->expectedName,
|
|
PS_TRUE);
|
|
ssl->extFlags.sni_in_last_client_hello = 1;
|
|
break;
|
|
|
|
# ifdef USE_ALPN
|
|
/**************************************************************************/
|
|
|
|
case EXT_ALPN:
|
|
psTracePrintExtensionParse(ssl, EXT_ALPN);
|
|
|
|
/* Must hold 2 b len 1 b zero 2 b len */
|
|
if (extLen < 2)
|
|
{
|
|
psTraceErrr("Invalid ALPN ext len\n");
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* Skip extension if server didn't register callback */
|
|
if (ssl->srv_alpn_cb)
|
|
{
|
|
int rc;
|
|
if ((rc = dealWithAlpnExt(ssl, c, extLen)) < 0)
|
|
{
|
|
if (rc == PS_PROTOCOL_FAIL)
|
|
{
|
|
/* This is a user space rejection */
|
|
psTraceErrr("User rejects ALPN ext\n");
|
|
ssl->err = SSL_ALERT_NO_APP_PROTOCOL;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
psTraceErrr("Invalid ALPN ext\n");
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
break;
|
|
# endif
|
|
|
|
# ifdef ENABLE_SECURE_REHANDSHAKES
|
|
/**************************************************************************/
|
|
|
|
case EXT_RENEGOTIATION_INFO:
|
|
psTracePrintExtensionParse(ssl, EXT_RENEGOTIATION_INFO);
|
|
|
|
if (ssl->secureRenegotiationFlag == PS_FALSE &&
|
|
ssl->myVerifyDataLen == 0)
|
|
{
|
|
if (extLen == 1 && *c == '\0')
|
|
{
|
|
ssl->secureRenegotiationFlag = PS_TRUE;
|
|
}
|
|
else
|
|
{
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
psTraceErrr("Cln sent bad renegotiationInfo\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
else if ((extLen == ssl->peerVerifyDataLen + 1) &&
|
|
(ssl->secureRenegotiationFlag == PS_TRUE))
|
|
{
|
|
if (*c != ssl->peerVerifyDataLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid renegotiation encoding\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if (memcmpct(c + 1, ssl->peerVerifyData,
|
|
ssl->peerVerifyDataLen) != 0)
|
|
{
|
|
psTraceErrr("Cli verify renegotiation fail\n");
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
ssl->secureRenegotiationInProgress = PS_TRUE;
|
|
}
|
|
else
|
|
{
|
|
psTraceErrr("Bad state/len of renegotiation ext\n");
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
break;
|
|
# endif /* ENABLE_SECURE_REHANDSHAKES */
|
|
|
|
# ifdef USE_TLS_1_2
|
|
/**************************************************************************/
|
|
case EXT_SIGNATURE_ALGORITHMS:
|
|
rc = tlsParseSignatureAlgorithms(ssl, c, extLen);
|
|
if (rc < 0)
|
|
{
|
|
return rc;
|
|
}
|
|
break;
|
|
# endif /* USE_TLS_1_2 */
|
|
|
|
# ifdef USE_STATELESS_SESSION_TICKETS
|
|
/**************************************************************************/
|
|
|
|
case EXT_SESSION_TICKET:
|
|
psTracePrintExtensionParse(ssl, EXT_SESSION_TICKET);
|
|
/* Have a handy place to store this info. Tickets are
|
|
the only way a server will make use of 'sid'.
|
|
Could already exist if rehandshake case here */
|
|
if (ssl->sid == NULL)
|
|
{
|
|
/* Server just needs a spot to manage the state
|
|
of the negotiation. Uses the sessionTicketState
|
|
of this structure */
|
|
ssl->sid = psMalloc(ssl->sPool, sizeof(sslSessionId_t));
|
|
if (ssl->sid == NULL)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
Memset(ssl->sid, 0x0, sizeof(sslSessionId_t));
|
|
ssl->sid->pool = ssl->sPool;
|
|
}
|
|
if (extLen > 0) /* received a ticket */
|
|
{
|
|
ssl->extFlags.session_ticket = 1;
|
|
|
|
if (matrixUnlockSessionTicket(ssl, (unsigned char *) c, extLen) ==
|
|
PS_SUCCESS)
|
|
{
|
|
/* Understood the token */
|
|
ssl->flags |= SSL_FLAGS_RESUMED;
|
|
ssl->sid->sessionTicketState = SESS_TICKET_STATE_USING_TICKET;
|
|
Memcpy(ssl->sec.masterSecret, ssl->sid->masterSecret,
|
|
SSL_HS_MASTER_SIZE);
|
|
# ifdef USE_MATRIXSSL_STATS
|
|
matrixsslUpdateStat(ssl, RESUMPTIONS_STAT, 1);
|
|
# endif
|
|
}
|
|
else
|
|
{
|
|
/* If client sent a sessionId in the hello,
|
|
we can ignore that here now */
|
|
if (ssl->sessionIdLen > 0)
|
|
{
|
|
Memset(ssl->sessionId, 0, SSL_MAX_SESSION_ID_SIZE);
|
|
ssl->sessionIdLen = 0;
|
|
}
|
|
/* Issue another one if we have any keys */
|
|
if (ssl->keys && ssl->keys->sessTickets)
|
|
{
|
|
ssl->sid->sessionTicketState = SESS_TICKET_STATE_RECVD_EXT;
|
|
}
|
|
else
|
|
{
|
|
ssl->sid->sessionTicketState = SESS_TICKET_STATE_INIT;
|
|
}
|
|
# ifdef USE_MATRIXSSL_STATS
|
|
matrixsslUpdateStat(ssl, FAILED_RESUMPTIONS_STAT, 1);
|
|
# endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Request for session ticket. Can we honor? */
|
|
if (ssl->keys && ssl->keys->sessTickets)
|
|
{
|
|
ssl->sid->sessionTicketState = SESS_TICKET_STATE_RECVD_EXT;
|
|
}
|
|
else
|
|
{
|
|
ssl->sid->sessionTicketState = SESS_TICKET_STATE_INIT;
|
|
}
|
|
}
|
|
break;
|
|
# endif /* USE_STATELESS_SESSION_TICKETS */
|
|
|
|
# ifdef USE_TLS_1_3
|
|
case EXT_SUPPORTED_VERSIONS:
|
|
/* This is defined in RFC 8446, but TLS 1.2 implementations
|
|
SHOULD also be able to parse this. */
|
|
rc = tls13ParseSupportedVersions(ssl, &c, extLen);
|
|
if (rc < 0)
|
|
{
|
|
return rc;
|
|
}
|
|
break;
|
|
# endif /* USE_TLS_1_3 */
|
|
|
|
# ifdef USE_ECC_CIPHER_SUITE
|
|
case EXT_ELLIPTIC_CURVE:
|
|
rc = tlsParseSupportedGroups(ssl, c, extLen);
|
|
if (rc < 0)
|
|
{
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
break;
|
|
|
|
case EXT_ELLIPTIC_POINTS:
|
|
psTracePrintExtensionParse(ssl, EXT_ELLIPTIC_POINTS);
|
|
if (extLen < 1)
|
|
{
|
|
psTraceErrr("Invalid ECC Points len\n");
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
dataLen = *c; c++; /* single byte data len */
|
|
extLen -= 1;
|
|
if (dataLen > extLen || dataLen < 1)
|
|
{
|
|
psTraceErrr("Malformed ECC Points extension\n");
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* One of them has to be a zero (uncompressed) and that
|
|
is all we are looking for at the moment */
|
|
if (Memchr(c, '\0', dataLen) == NULL)
|
|
{
|
|
psTraceErrr("ECC Uncompressed Points missing\n");
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
ssl->extFlags.got_elliptic_points = 1;
|
|
break;
|
|
# endif /* USE_ECC_CIPHER_SUITE */
|
|
|
|
# ifdef USE_OCSP_RESPONSE
|
|
case EXT_STATUS_REQUEST:
|
|
psTracePrintExtensionParse(ssl, EXT_STATUS_REQUEST);
|
|
/* Validation of minimum size and status_type of 1 (ocsp) */
|
|
if (extLen < 5 || *c != 0x1)
|
|
{
|
|
psTraceIntInfo("Bad cert_status_request extension: %d\n", extType);
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
c++; extLen--; /* checked status type above */
|
|
/* Skipping the ResponderIDs */
|
|
dataLen = *c << 8; c++; extLen--;
|
|
dataLen += *c; c++; extLen--;
|
|
if (dataLen + 2 > extLen)
|
|
{
|
|
psTraceIntInfo("Bad cert_status_request extension: %d\n", extType);
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
c += dataLen;
|
|
/* Skipping the Extensions */
|
|
dataLen = *c << 8; c++; extLen--;
|
|
dataLen += *c; c++; extLen--;
|
|
if (dataLen > extLen)
|
|
{
|
|
psTraceIntInfo("Bad cert_status_request extension: %d\n", extType);
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* Currently, the OCSPResponse must be loaded into the key material
|
|
so we check if that exists to determine if we will reply with
|
|
the extension and CERTIFICATE_STATUS handshake message */
|
|
if (ssl->keys->OCSPResponseBufLen > 0 &&
|
|
ssl->keys->OCSPResponseBuf != NULL)
|
|
{
|
|
ssl->extFlags.status_request = 1;
|
|
}
|
|
else
|
|
{
|
|
psTraceInfo("Client requesting OCSP but we have no response\n");
|
|
}
|
|
|
|
break;
|
|
# endif /* USE_OCSP_RESPONSE */
|
|
|
|
|
|
/**************************************************************************/
|
|
|
|
default:
|
|
psTraceIntInfo("Ignoring unknown extension: %d\n", extType);
|
|
break;
|
|
}
|
|
return PS_SUCCESS;
|
|
}
|
|
|
|
# ifdef USE_ALPN
|
|
/**************************************************************************/
|
|
/*
|
|
Parse Application Layer Protocol extension and invoke callback. No need
|
|
to advance any buffer pointers, caller is managing.
|
|
*/
|
|
static int dealWithAlpnExt(ssl_t *ssl, const unsigned char *c, unsigned short extLen)
|
|
{
|
|
int32 totLen, i, userChoice;
|
|
unsigned char *end;
|
|
char *proto[MAX_PROTO_EXT];
|
|
int32 protoLen[MAX_PROTO_EXT];
|
|
|
|
end = (unsigned char *) c + extLen;
|
|
|
|
totLen = *c << 8; c++;
|
|
totLen += *c; c++;
|
|
if (totLen != extLen - 2)
|
|
{
|
|
return PS_PARSE_FAIL;
|
|
}
|
|
|
|
for (i = 0; totLen > 0; i++)
|
|
{
|
|
if (i + 1 > MAX_PROTO_EXT)
|
|
{
|
|
i--;
|
|
break;
|
|
}
|
|
protoLen[i] = *c; c++; totLen--;
|
|
if (protoLen[i] > totLen)
|
|
{
|
|
while (i > 0)
|
|
{
|
|
psFree(proto[i - 1], ssl->sPool);
|
|
i--;
|
|
}
|
|
return PS_PARSE_FAIL;
|
|
}
|
|
if ((proto[i] = psMalloc(ssl->sPool, protoLen[i])) == NULL)
|
|
{
|
|
while (i > 0)
|
|
{
|
|
psFree(proto[i - 1], ssl->sPool);
|
|
i--;
|
|
}
|
|
return PS_MEM_FAIL;
|
|
}
|
|
Memcpy(proto[i], c, protoLen[i]);
|
|
totLen -= protoLen[i];
|
|
c += protoLen[i];
|
|
}
|
|
|
|
/* see if they touch it. Using a value outside the range to gauge */
|
|
userChoice = MAX_PROTO_EXT;
|
|
|
|
/* Already tested if callback exists */
|
|
(*ssl->srv_alpn_cb)((void *) ssl, (short) i, proto, protoLen, &userChoice);
|
|
|
|
if (userChoice == MAX_PROTO_EXT)
|
|
{
|
|
/* User chose to completely ignore. No reply extension will be sent */
|
|
while (i > 0)
|
|
{
|
|
psFree(proto[i - 1], ssl->sPool);
|
|
i--;
|
|
}
|
|
return PS_SUCCESS;
|
|
}
|
|
if (userChoice >= i)
|
|
{
|
|
/* User chose something completely unreasonable */
|
|
while (i > 0)
|
|
{
|
|
psFree(proto[i - 1], ssl->sPool);
|
|
i--;
|
|
}
|
|
return PS_LIMIT_FAIL;
|
|
}
|
|
|
|
if (userChoice < 0)
|
|
{
|
|
/* They actually rejected these completely. Returning the
|
|
no-application-protocol alert per the spec */
|
|
while (i > 0)
|
|
{
|
|
psFree(proto[i - 1], ssl->sPool);
|
|
i--;
|
|
}
|
|
return PS_PROTOCOL_FAIL;
|
|
}
|
|
|
|
/* Allocated to session pool so fine to point to it and just skip
|
|
freeing it while cleaning up here */
|
|
ssl->alpn = proto[userChoice];
|
|
ssl->alpnLen = protoLen[userChoice];
|
|
|
|
while (i > 0)
|
|
{
|
|
if (i - 1 != userChoice)
|
|
{
|
|
psFree(proto[i - 1], ssl->sPool);
|
|
}
|
|
i--;
|
|
}
|
|
|
|
/** HTTP2 places some constraints on TLS configuration, so flag
|
|
that here for checking later (in choosing cipher suite, etc).
|
|
@see https://tools.ietf.org/html/rfc7540#section-9.2
|
|
*/
|
|
if ((ssl->alpnLen == 2) && (Memcmp(ssl->alpn, "h2", 2) == 0))
|
|
{
|
|
ssl->flags |= SSL_FLAGS_HTTP2;
|
|
}
|
|
|
|
return PS_SUCCESS;
|
|
}
|
|
# endif /* USE_ALPN */
|
|
#endif /* USE_SERVER_SIDE_SSL */
|
|
|
|
#ifdef USE_CLIENT_SIDE_SSL
|
|
/******************************************************************************/
|
|
/*
|
|
Parse the ServerHello extension list.
|
|
*/
|
|
int32 parseServerHelloExtensions(ssl_t *ssl, int32 hsLen,
|
|
unsigned char *extData, unsigned char **cp,
|
|
unsigned short len)
|
|
{
|
|
unsigned short extLen, extType;
|
|
int32 rc;
|
|
unsigned char *c, *end;
|
|
|
|
# ifdef ENABLE_SECURE_REHANDSHAKES
|
|
unsigned short renegotiationExtSent = 0;
|
|
# endif
|
|
|
|
rc = PS_SUCCESS;
|
|
c = *cp;
|
|
end = c + len;
|
|
|
|
/* Check that we can parse the two length octets. */
|
|
if (end - c < 2)
|
|
{
|
|
goto out_decode_error_length;
|
|
}
|
|
extLen = *c << 8; c++; /* Total length of list */
|
|
extLen += *c; c++;
|
|
if ((uint32) (end - c) < extLen)
|
|
{
|
|
goto out_decode_error_length;
|
|
}
|
|
while ((int32) hsLen > (c - extData))
|
|
{
|
|
if (end - c < 2)
|
|
{
|
|
goto out_decode_error_length;
|
|
}
|
|
extType = *c << 8; c++; /* Individual hello ext */
|
|
extType += *c; c++;
|
|
if (end - c < 2)
|
|
{
|
|
goto out_decode_error_length;
|
|
}
|
|
extLen = *c << 8; c++; /* Length of individual extension */
|
|
extLen += *c; c++;
|
|
if ((uint32) (end - c) < extLen)
|
|
{
|
|
goto out_decode_error_length;
|
|
}
|
|
# ifdef ENABLE_SECURE_REHANDSHAKES
|
|
if (extType == EXT_RENEGOTIATION_INFO)
|
|
{
|
|
renegotiationExtSent = 1;
|
|
}
|
|
# endif
|
|
if ((rc = ServerHelloExt(ssl, extType, extLen, c)) < 0)
|
|
{
|
|
/* On error, ssl->error will have been set */
|
|
return rc;
|
|
}
|
|
c += extLen;
|
|
}
|
|
|
|
/* Enforce the rules for extensions that require the server to send
|
|
something back to us */
|
|
|
|
if (ssl->extFlags.req_extended_master_secret == 1)
|
|
{
|
|
/* The req_extended_master_secret is reset to 0 when the server sends
|
|
one so this case means the server doesn't support it */
|
|
if (ssl->extFlags.require_extended_master_secret == 1)
|
|
{
|
|
psTraceErrr("Server doesn't support extended master secret\n");
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
|
|
# ifdef USE_OCSP_MUST_STAPLE
|
|
if (ssl->extFlags.req_status_request == 1)
|
|
{
|
|
if (ssl->extFlags.status_request == 0)
|
|
{
|
|
psTraceErrr("Server doesn't support OCSP stapling\n");
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
# endif
|
|
|
|
# ifdef ENABLE_SECURE_REHANDSHAKES
|
|
if (!renegotiationExtSent)
|
|
{
|
|
# ifdef REQUIRE_SECURE_REHANDSHAKES
|
|
/* Check if SCSV was sent instead */
|
|
if (ssl->secureRenegotiationFlag == PS_FALSE &&
|
|
ssl->myVerifyDataLen == 0)
|
|
{
|
|
psTraceErrr("Server doesn't support renegotiation hello\n");
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# endif /* REQUIRE_SECURE_REHANDSHAKES */
|
|
if (ssl->secureRenegotiationFlag == PS_TRUE &&
|
|
ssl->myVerifyDataLen > 0)
|
|
{
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
psTraceErrr("Server missing renegotiationInfo on re-hndshk\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# ifndef ENABLE_INSECURE_REHANDSHAKES
|
|
if (ssl->secureRenegotiationFlag == PS_FALSE &&
|
|
ssl->myVerifyDataLen > 0)
|
|
{
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
psTraceErrr("Server attempting insecure handshake\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# endif /* !ENABLE_INSECURE_REHANDSHAKES */
|
|
}
|
|
# endif /* ENABLE_SECURE_REHANDSHAKES */
|
|
|
|
*cp = c;
|
|
return rc;
|
|
|
|
out_decode_error_length:
|
|
psTraceErrr("Invalid ServerHello extension header len\n");
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
Parse the ServerHello extension list.
|
|
*/
|
|
static int ServerHelloExt(ssl_t *ssl, unsigned short extType, unsigned short extLen,
|
|
const unsigned char *c)
|
|
{
|
|
int rc;
|
|
|
|
/* Verify the server only responds with an extension the client requested.
|
|
Zero the value once seen so that it will catch the error if sent twice */
|
|
rc = -1;
|
|
psTracePrintExtensionParse(ssl, extType);
|
|
switch (extType)
|
|
{
|
|
|
|
/**************************************************************************/
|
|
|
|
case EXT_SNI:
|
|
if (ssl->extFlags.req_sni)
|
|
{
|
|
ssl->extFlags.req_sni = 0;
|
|
if (ssl->extCb)
|
|
{
|
|
rc = (*ssl->extCb)(ssl, extType, extLen, (void *) c);
|
|
}
|
|
else
|
|
{
|
|
rc = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
/**************************************************************************/
|
|
|
|
case EXT_MAX_FRAGMENT_LEN:
|
|
if (ssl->extFlags.req_max_fragment_len)
|
|
{
|
|
ssl->extFlags.req_max_fragment_len = 0;
|
|
rc = 0;
|
|
}
|
|
if (!(ssl->maxPtFrag & 0x10000))
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceErrr("Server sent unexpected MAX_FRAG ext\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if (extLen != 1)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceErrr("Server sent bad MAX_FRAG ext\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* Confirm it's the same size we requested and strip
|
|
off 0x10000 from maxPtrFrag */
|
|
if (*c == 0x01 &&
|
|
(ssl->maxPtFrag & 0x200))
|
|
{
|
|
ssl->maxPtFrag = 0x200;
|
|
}
|
|
else if (*c == 0x02 &&
|
|
(ssl->maxPtFrag & 0x400))
|
|
{
|
|
ssl->maxPtFrag = 0x400;
|
|
}
|
|
else if (*c == 0x03 &&
|
|
(ssl->maxPtFrag & 0x800))
|
|
{
|
|
ssl->maxPtFrag = 0x800;
|
|
}
|
|
else if (*c == 0x04 &&
|
|
(ssl->maxPtFrag & 0x1000))
|
|
{
|
|
ssl->maxPtFrag = 0x1000;
|
|
}
|
|
else
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceErrr("Server sent mismatched MAX_FRAG ext\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
c++; extLen--;
|
|
break;
|
|
|
|
/**************************************************************************/
|
|
|
|
case EXT_TRUNCATED_HMAC:
|
|
if (ssl->extFlags.req_truncated_hmac)
|
|
{
|
|
ssl->extFlags.req_truncated_hmac = 0;
|
|
rc = 0;
|
|
}
|
|
if (extLen != 0)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceErrr("Server sent bad truncated hmac ext\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
ssl->extFlags.truncated_hmac = 1;
|
|
break;
|
|
|
|
case EXT_EXTENDED_MASTER_SECRET:
|
|
if (ssl->extFlags.req_extended_master_secret)
|
|
{
|
|
ssl->extFlags.req_extended_master_secret = 0;
|
|
rc = 0;
|
|
}
|
|
if (extLen != 0)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceErrr("Server sent bad extended master secret ext\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
ssl->extFlags.extended_master_secret = 1;
|
|
break;
|
|
|
|
# ifdef USE_ECC_CIPHER_SUITE
|
|
/**************************************************************************/
|
|
|
|
case EXT_ELLIPTIC_CURVE:
|
|
/* elliptic_curves is defined as a ClientHello-only extension, but some
|
|
servers send it in ServerHello as a reply. Like OpenSSL client,
|
|
we allow this, but ignore the extension contents. We will still
|
|
reject the extension if unsolicited, i.e. if we did not send it in
|
|
our ClientHello. */
|
|
if (ssl->extFlags.req_elliptic_curve == 1)
|
|
{
|
|
rc = 0;
|
|
}
|
|
break;
|
|
|
|
/**************************************************************************/
|
|
|
|
case EXT_ELLIPTIC_POINTS:
|
|
if (ssl->extFlags.req_elliptic_points)
|
|
{
|
|
ssl->extFlags.req_elliptic_points = 0;
|
|
rc = 0;
|
|
}
|
|
# ifdef ALLOW_UNSOLICITED_SERVER_HELLO_ELLIPTIC_POINTS
|
|
/* Some servers send the extension regardless of whether we sent
|
|
it in our ClientHello. This is strictly not RFC-compliant.
|
|
Allow it if the above compatibility option is enabled. */
|
|
psTraceInfo("Allowing unsolicited elliptic_point_format extension\n");
|
|
rc = 0;
|
|
# endif
|
|
if (*c++ != (extLen - 1))
|
|
{
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
psTraceErrr("Server sent bad ECPointFormatList\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
extLen--;
|
|
break;
|
|
# endif /* USE_ECC_CIPHER_SUITE */
|
|
|
|
/**************************************************************************/
|
|
|
|
case EXT_SIGNATURE_ALGORITHMS:
|
|
/* @note we do not allow EXT_SIGNATURE_ALGORITHMS from server */
|
|
break;
|
|
|
|
/**************************************************************************/
|
|
|
|
case EXT_ALPN:
|
|
if (ssl->extFlags.req_alpn)
|
|
{
|
|
ssl->extFlags.req_alpn = 0;
|
|
if (ssl->extCb)
|
|
{
|
|
rc = (*ssl->extCb)(ssl, extType, extLen, (void *) c);
|
|
}
|
|
else
|
|
{
|
|
rc = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
# ifdef USE_STATELESS_SESSION_TICKETS
|
|
/**************************************************************************/
|
|
|
|
case EXT_SESSION_TICKET:
|
|
if (ssl->extFlags.req_session_ticket)
|
|
{
|
|
ssl->extFlags.req_session_ticket = 0;
|
|
rc = 0;
|
|
}
|
|
if (ssl->sid && ssl->sid->sessionTicketState ==
|
|
SESS_TICKET_STATE_SENT_EMPTY)
|
|
{
|
|
ssl->sid->sessionTicketState =
|
|
SESS_TICKET_STATE_RECVD_EXT; /* expecting ticket */
|
|
}
|
|
else if (ssl->sid && ssl->sid->sessionTicketState ==
|
|
SESS_TICKET_STATE_SENT_TICKET)
|
|
{
|
|
ssl->sid->sessionTicketState =
|
|
SESS_TICKET_STATE_RECVD_EXT; /* expecting ticket */
|
|
}
|
|
else
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceErrr("Server sent unexpected SESSION_TICKET\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
break;
|
|
# endif /* USE_STATELESS_SESSION_TICKETS */
|
|
|
|
# ifdef ENABLE_SECURE_REHANDSHAKES
|
|
/**************************************************************************/
|
|
|
|
case EXT_RENEGOTIATION_INFO:
|
|
if (ssl->extFlags.req_renegotiation_info)
|
|
{
|
|
ssl->extFlags.req_renegotiation_info = 0;
|
|
rc = 0;
|
|
}
|
|
if (ssl->secureRenegotiationFlag == PS_FALSE &&
|
|
ssl->myVerifyDataLen == 0)
|
|
{
|
|
if (extLen == 1 && *c == '\0')
|
|
{
|
|
ssl->secureRenegotiationFlag = PS_TRUE;
|
|
}
|
|
else
|
|
{
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
psTraceErrr("Server sent bad renegotiationInfo\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
else if (ssl->secureRenegotiationFlag == PS_TRUE &&
|
|
extLen == ((ssl->myVerifyDataLen * 2) + 1))
|
|
{
|
|
c++; extLen--;
|
|
if (memcmpct(c, ssl->myVerifyData,
|
|
ssl->myVerifyDataLen) != 0)
|
|
{
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
psTraceErrr("Srv had bad my renegotiationInfo\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if (memcmpct(c + ssl->myVerifyDataLen, ssl->peerVerifyData,
|
|
ssl->peerVerifyDataLen) != 0)
|
|
{
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
psTraceErrr("Srv had bad peer renegotiationInfo\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
ssl->secureRenegotiationInProgress = PS_TRUE;
|
|
}
|
|
else
|
|
{
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
psTraceErrr("Server sent bad renegotiationInfo\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
break;
|
|
# endif /* ENABLE_SECURE_REHANDSHAKES */
|
|
|
|
case EXT_STATUS_REQUEST:
|
|
if (ssl->extFlags.req_status_request)
|
|
{
|
|
/* Weed out the unsolicited status_request */
|
|
ssl->extFlags.status_request = 1;
|
|
rc = 0;
|
|
}
|
|
break;
|
|
|
|
|
|
/**************************************************************************/
|
|
|
|
default:
|
|
/* We don't recognize the extension, maybe the callback will */
|
|
if (ssl->extCb)
|
|
{
|
|
rc = (*ssl->extCb)(ssl, extType, extLen, (void *) c);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (rc < 0)
|
|
{
|
|
if (ACTV_VER(ssl, v_tls_with_unsupported_extension_alert))
|
|
{
|
|
/* This alert was added only in 1.2 */
|
|
ssl->err = SSL_ALERT_UNSUPPORTED_EXTENSION;
|
|
}
|
|
else
|
|
{
|
|
/* Use a generic alert for older versions */
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
}
|
|
psTraceIntInfo("Server sent unsolicited or duplicate ext %d\n",
|
|
extType);
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
return rc;
|
|
}
|
|
#endif
|
|
|
|
/**************************************************************************/
|