3745 lines
129 KiB
C
3745 lines
129 KiB
C
/**
|
|
* @file hsDecode.c
|
|
* @version $Format:%h%d$
|
|
*
|
|
* SSL/TLS handshake message parsing
|
|
*/
|
|
/*
|
|
* Copyright (c) 2013-2017 INSIDE Secure Corporation
|
|
* 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 INSIDE at
|
|
* http://www.insidesecure.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_ECC
|
|
# define USE_ECC_EPHEMERAL_KEY_CACHE
|
|
#endif
|
|
|
|
#define COMPRESSION_METHOD_NULL 0x0
|
|
#define COMPRESSION_METHOD_DEFLATE 0x1
|
|
|
|
/* Errors from these routines must either be MATRIXSSL_ERROR or PS_MEM_FAIL */
|
|
|
|
/******************************************************************************/
|
|
|
|
#ifdef USE_SERVER_SIDE_SSL
|
|
int32 parseClientHello(ssl_t *ssl, unsigned char **cp, unsigned char *end)
|
|
{
|
|
unsigned char *suiteStart, *suiteEnd;
|
|
unsigned char compareMin, compareMaj, compLen, serverHighestMinor;
|
|
uint32 suiteLen;
|
|
uint32 resumptionOnTrack, cipher = 0;
|
|
int32 rc, i;
|
|
unsigned char *c;
|
|
|
|
# ifdef USE_ECC_CIPHER_SUITE
|
|
const psEccCurve_t *curve;
|
|
# endif
|
|
# if defined(USE_ECC) || defined(REQUIRE_DH_PARAMS)
|
|
void *pkiData = ssl->userPtr;
|
|
# endif
|
|
|
|
c = *cp;
|
|
|
|
/* First two bytes are the highest supported major and minor SSL versions */
|
|
psTraceHs(">>> Server parsing CLIENT_HELLO\n");
|
|
|
|
# ifdef USE_MATRIXSSL_STATS
|
|
matrixsslUpdateStat(ssl, CH_RECV_STAT, 1);
|
|
# endif
|
|
if (end - c < 2)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid ssl header version length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
ssl->reqMajVer = *c; c++;
|
|
ssl->reqMinVer = *c; c++;
|
|
|
|
psTracePrintProtocolVersion("Parsed ClientHello.client_version",
|
|
ssl->reqMajVer, ssl->reqMinVer, 1);
|
|
|
|
# ifndef USE_SSL_PROTOCOL_VERSIONS_OTHER_THAN_3
|
|
/* RFC 5246 Suggests to accept all RSA minor versions, but only
|
|
major version 0x03 (SSLv3, TLS 1.0, TLS 1.1, TLS 1.2, TLS 1.3 etc) */
|
|
if (ssl->reqMajVer != 0x03
|
|
# ifdef USE_DTLS
|
|
&& ssl->reqMajVer != DTLS_MAJ_VER
|
|
# endif /* USE_DTLS */
|
|
)
|
|
{
|
|
/* Consider invalid major version protocol version error. */
|
|
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
|
|
psTraceInfo("Won't support client's SSL major version\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# endif /* USE_SSL_PROTOCOL_VERSIONS_OTHER_THAN_3 */
|
|
|
|
/* Client should always be sending highest supported protocol. Server
|
|
will reply with a match or a lower version if enabled (or forced). */
|
|
if (ssl->majVer != 0)
|
|
{
|
|
/* If our forced server version is a later protocol than their
|
|
request, we have to exit */
|
|
if (ssl->reqMinVer < ssl->minVer)
|
|
{
|
|
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
|
|
psTraceInfo("Won't support client's SSL version\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# ifdef USE_DTLS
|
|
if (ssl->flags & SSL_FLAGS_DTLS)
|
|
{
|
|
/* DTLS specfication somehow assigned minimum version of DTLS 1.0
|
|
as 255 so there was nowhere to go but down in DTLS 1.1 so
|
|
that is 253 and requires the opposite test from above */
|
|
if (ssl->reqMinVer > ssl->minVer)
|
|
{
|
|
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
|
|
psTraceInfo("Won't support client's DTLS version\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
# endif
|
|
/* Otherwise we just set our forced version to act like it was
|
|
what the client wanted in order to move through the standard
|
|
negotiation. */
|
|
compareMin = ssl->minVer;
|
|
compareMaj = ssl->majVer;
|
|
/* Set the highest version to the version explicitly set */
|
|
serverHighestMinor = ssl->minVer;
|
|
}
|
|
else
|
|
{
|
|
compareMin = ssl->reqMinVer;
|
|
compareMaj = ssl->reqMajVer;
|
|
/* If no explicit version was set for the server, use the highest supported */
|
|
serverHighestMinor = TLS_HIGHEST_MINOR;
|
|
}
|
|
|
|
if (compareMaj >= SSL3_MAJ_VER)
|
|
{
|
|
ssl->majVer = compareMaj;
|
|
# ifdef USE_TLS
|
|
if (compareMin >= TLS_MIN_VER)
|
|
{
|
|
# ifndef DISABLE_TLS_1_0
|
|
/* Allow TLS 1.0, unless specifically disabled. */
|
|
if (!ssl->disable_tls_1_0)
|
|
{
|
|
ssl->minVer = TLS_MIN_VER;
|
|
ssl->flags |= SSL_FLAGS_TLS;
|
|
}
|
|
# endif
|
|
# ifdef USE_TLS_1_1 /* TLS_1_1 */
|
|
if (compareMin >= TLS_1_1_MIN_VER)
|
|
{
|
|
# ifndef DISABLE_TLS_1_1
|
|
ssl->minVer = TLS_1_1_MIN_VER;
|
|
ssl->flags |= SSL_FLAGS_TLS_1_1 | SSL_FLAGS_TLS;
|
|
# endif
|
|
}
|
|
# ifdef USE_TLS_1_2
|
|
# ifdef USE_TLS_1_2_TOGGLE
|
|
/* Prefer TLS 1.2, unless specifically disabled. */
|
|
if (!ssl->disable_tls_1_2)
|
|
{
|
|
# endif /* USE_TLS_1_2_TOGGLE */
|
|
if (compareMin == TLS_1_2_MIN_VER)
|
|
{
|
|
ssl->minVer = TLS_1_2_MIN_VER;
|
|
ssl->flags |= SSL_FLAGS_TLS_1_2 | SSL_FLAGS_TLS_1_1 | SSL_FLAGS_TLS;
|
|
}
|
|
# ifdef USE_TLS_1_2_TOGGLE
|
|
}
|
|
# endif /* USE_TLS_1_2_TOGGLE */
|
|
# ifdef USE_DTLS
|
|
if (ssl->flags & SSL_FLAGS_DTLS)
|
|
{
|
|
if (compareMin == DTLS_1_2_MIN_VER)
|
|
{
|
|
ssl->minVer = DTLS_1_2_MIN_VER;
|
|
}
|
|
}
|
|
# endif
|
|
# endif /* USE_TLS_1_2 */
|
|
# endif /* USE_TLS_1_1 */
|
|
if (ssl->minVer == 0)
|
|
{
|
|
/* TLS versions are disabled. Go SSLv3 if available. */
|
|
# ifdef DISABLE_SSLV3
|
|
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
|
|
psTraceInfo("Can't support client's SSL version\n");
|
|
return MATRIXSSL_ERROR;
|
|
# else
|
|
ssl->minVer = SSL3_MIN_VER;
|
|
# endif
|
|
}
|
|
}
|
|
else if (compareMin == 0)
|
|
{
|
|
# ifdef DISABLE_SSLV3
|
|
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
|
|
psTraceInfo("Client wanted to talk SSLv3 but it's disabled\n");
|
|
return MATRIXSSL_ERROR;
|
|
# else
|
|
ssl->minVer = SSL3_MIN_VER;
|
|
# endif /* DISABLE_SSLV3 */
|
|
}
|
|
# ifdef USE_DTLS
|
|
if (ssl->flags & SSL_FLAGS_DTLS)
|
|
{
|
|
if (compareMin < DTLS_1_2_MIN_VER)
|
|
{
|
|
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
|
|
psTraceInfo("Error: incorrect DTLS required version\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
ssl->minVer = DTLS_MIN_VER;
|
|
# ifdef USE_TLS_1_2
|
|
# ifdef USE_TLS_1_2_TOGGLE
|
|
/* Prefer TLS 1.2, unless specifically disabled. */
|
|
if (!ssl->disable_tls_1_2)
|
|
{
|
|
# endif /* USE_TLS_1_2_TOGGLE */
|
|
if (compareMin == DTLS_1_2_MIN_VER)
|
|
{
|
|
ssl->flags |= SSL_FLAGS_TLS_1_2 | SSL_FLAGS_TLS_1_1 | SSL_FLAGS_TLS;
|
|
ssl->minVer = DTLS_1_2_MIN_VER;
|
|
}
|
|
# ifdef USE_TLS_1_2_TOGGLE
|
|
}
|
|
# endif /* USE_TLS_1_2_TOGGLE */
|
|
# ifdef USE_DTLS
|
|
if (ssl->flags & SSL_FLAGS_DTLS)
|
|
{
|
|
if (compareMin == DTLS_1_2_MIN_VER)
|
|
{
|
|
ssl->minVer = DTLS_1_2_MIN_VER;
|
|
}
|
|
}
|
|
# endif /* USE_DTLS */
|
|
# endif /* USE_TLS_1_2 */
|
|
|
|
}
|
|
# endif /* USE_DTLS */
|
|
# else
|
|
ssl->minVer = SSL3_MIN_VER;
|
|
|
|
# endif /* USE_TLS */
|
|
|
|
}
|
|
else
|
|
{
|
|
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
|
|
psTraceIntInfo("Unsupported ssl version: %d\n", compareMaj);
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
if (ssl->rec.majVer > SSL2_MAJ_VER)
|
|
{
|
|
/* Next is a 32 bytes of random data for key generation
|
|
and a single byte with the session ID length */
|
|
if (end - c < SSL_HS_RANDOM_SIZE + 1)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceIntInfo("Invalid length of random data %d\n",
|
|
(int32) (end - c));
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
memcpy(ssl->sec.clientRandom, c, SSL_HS_RANDOM_SIZE);
|
|
c += SSL_HS_RANDOM_SIZE;
|
|
ssl->sessionIdLen = *c; c++; /* length verified with + 1 above */
|
|
/* If a session length was specified, the client is asking to
|
|
resume a previously established session to speed up the handshake */
|
|
if (ssl->sessionIdLen > 0)
|
|
{
|
|
if (ssl->sessionIdLen > SSL_MAX_SESSION_ID_SIZE ||
|
|
end - c < ssl->sessionIdLen)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
# ifdef USE_MATRIXSSL_STATS
|
|
matrixsslUpdateStat(ssl, FAILED_RESUMPTIONS_STAT, 1);
|
|
# endif
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
memcpy(ssl->sessionId, c, ssl->sessionIdLen);
|
|
c += ssl->sessionIdLen;
|
|
}
|
|
else
|
|
{
|
|
/* Always clear the RESUMED flag if no client session id
|
|
It may be re-enabled if a client session ticket extension is recvd */
|
|
ssl->flags &= ~SSL_FLAGS_RESUMED;
|
|
}
|
|
# ifdef USE_DTLS
|
|
/* If DTLS is enabled, make sure we received a valid cookie in the
|
|
CLIENT_HELLO message. */
|
|
if (ssl->flags & SSL_FLAGS_DTLS)
|
|
{
|
|
psSize_t cookie_len;
|
|
/* Next field is the cookie length */
|
|
if (end - c < 1)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Cookie length not provided\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/** Calculate what we expect the cookie should be by hashing the
|
|
client_hello data up to this point:
|
|
|
|
2 byte version + 1 byte session_id_len +
|
|
session_id + client_random
|
|
|
|
@future The creation of the cookie should ideally take some
|
|
IP Tuple information about the client into account.
|
|
@impl MatrixSSL sends a zero length cookie on re-handshake, but
|
|
other implementations may not, so this allows either
|
|
to be supported.
|
|
*/
|
|
cookie_len = 3 + ssl->sessionIdLen + SSL_HS_RANDOM_SIZE;
|
|
if (dtlsComputeCookie(ssl, c - cookie_len, cookie_len) < 0)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
psTraceInfo("Invalid cookie length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
cookie_len = *c++;
|
|
if (cookie_len > 0)
|
|
{
|
|
if (end - c < cookie_len || cookie_len != DTLS_COOKIE_SIZE)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceInfo("Invalid cookie length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if (memcmpct(c, ssl->srvCookie, DTLS_COOKIE_SIZE) != 0)
|
|
{
|
|
/* Cookie mismatch. Error to avoid possible DOS */
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceInfo("Cookie mismatch\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
c += DTLS_COOKIE_SIZE;
|
|
}
|
|
else
|
|
{
|
|
/* If client sent an empty cookie, and we're not secure
|
|
yet, set the hsState to encode a HELLO_VERIFY message to
|
|
the client, which will provide a new cookie. */
|
|
if (!(ssl->flags & SSL_FLAGS_READ_SECURE))
|
|
{
|
|
ssl->hsState = SSL_HS_CLIENT_HELLO;
|
|
c = end;
|
|
*cp = c;
|
|
/* Clear session so it will be found again when the cookie
|
|
clientHello message comes in next */
|
|
if (ssl->flags & SSL_FLAGS_RESUMED)
|
|
{
|
|
matrixClearSession(ssl, 0);
|
|
}
|
|
/* Will cause HELLO_VERIFY to be encoded */
|
|
return SSL_PROCESS_DATA;
|
|
}
|
|
/** No cookie provided on already secure connection.
|
|
@impl This is a re-handshake case. MatrixSSL lets it slide
|
|
since we're already authenticated. */
|
|
}
|
|
}
|
|
# endif /* USE_DTLS */
|
|
/* Next is the two byte cipher suite list length, network byte order.
|
|
It must not be zero, and must be a multiple of two. */
|
|
if (end - c < 2)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid cipher suite list length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
suiteLen = *c << 8; c++;
|
|
suiteLen += *c; c++;
|
|
/* Save aside. We're going to come back after extensions are
|
|
parsed and choose a cipher suite */
|
|
suiteStart = c;
|
|
|
|
if (suiteLen <= 0 || suiteLen & 1)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceIntInfo("Unable to parse cipher suite list: %d\n",
|
|
suiteLen);
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* Now is 'suiteLen' bytes of the supported cipher suite list,
|
|
listed in order of preference. */
|
|
if (end - c < suiteLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Malformed clientHello message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* We do not choose a ciphersuite yet, as the cipher we choose
|
|
may depend on an extension sent by the client. For example,
|
|
ALPN for HTTP/2 limits which suites we can negotiate, and
|
|
ELLIPTIC_CURVE/ELLIPTIC_POINT extensions may not match with
|
|
what we have available and we would have to fall back to a
|
|
non-ECC cipher.
|
|
Still, make one entire pass of the cipher suites now
|
|
to search for SCSV if secure rehandshakes are on. This is
|
|
the exception because SCSV is not a true ciphersuite, but
|
|
more like an extension that can be "hidden" for pre-TLS1.0
|
|
implementations. */
|
|
suiteEnd = c + suiteLen;
|
|
while (c < suiteEnd)
|
|
{
|
|
cipher = *c << 8; c++;
|
|
cipher += *c; c++;
|
|
# ifdef ENABLE_SECURE_REHANDSHAKES
|
|
if (ssl->myVerifyDataLen == 0)
|
|
{
|
|
if (cipher == TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
|
|
{
|
|
ssl->secureRenegotiationFlag = PS_TRUE;
|
|
}
|
|
}
|
|
# endif
|
|
/** If TLS_FALLBACK_SCSV appears in ClientHello.cipher_suites and the
|
|
highest protocol version supported by the server is higher than
|
|
the version indicated in ClientHello.client_version, the server
|
|
MUST respond with a fatal inappropriate_fallback alert.
|
|
@see https://tools.ietf.org/html/rfc7507#section-3 */
|
|
if (cipher == TLS_FALLBACK_SCSV)
|
|
{
|
|
if (ssl->reqMinVer < serverHighestMinor)
|
|
{
|
|
ssl->err = SSL_ALERT_INAPPROPRIATE_FALLBACK;
|
|
psTraceInfo("Inappropriate version fallback\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Compression parameters */
|
|
if (end - c < 1)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid compression header length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
compLen = *c++;
|
|
if ((uint32) (end - c) < compLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid compression header length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* Per TLS RFCs proposing null compression is MUST. Check the other end
|
|
has proposed null compression (amongst possible other choices). */
|
|
for (i = 0; i < compLen; i++)
|
|
{
|
|
if (c[i] == COMPRESSION_METHOD_NULL)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (i == compLen)
|
|
{
|
|
/* Note, also catches compLen == 0 */
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("No compression.null proposed\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# ifdef USE_ZLIB_COMPRESSION
|
|
while (compLen > 0)
|
|
{
|
|
/* Client wants it and we have it. Enable if we're not already
|
|
in a compression state. FUTURE: Could be re-handshake */
|
|
if (ssl->compression == 0)
|
|
{
|
|
if (*c++ == 0x01)
|
|
{
|
|
ssl->inflate.zalloc = NULL;
|
|
ssl->inflate.zfree = NULL;
|
|
ssl->inflate.opaque = NULL;
|
|
ssl->inflate.avail_in = 0;
|
|
ssl->inflate.next_in = NULL;
|
|
if (inflateInit(&ssl->inflate) != Z_OK)
|
|
{
|
|
psTraceInfo("inflateInit fail. No compression\n");
|
|
}
|
|
else
|
|
{
|
|
ssl->deflate.zalloc = Z_NULL;
|
|
ssl->deflate.zfree = Z_NULL;
|
|
ssl->deflate.opaque = Z_NULL;
|
|
if (deflateInit(&ssl->deflate,
|
|
Z_DEFAULT_COMPRESSION) != Z_OK)
|
|
{
|
|
psTraceInfo("deflateInit fail. No compression\n");
|
|
inflateEnd(&ssl->inflate);
|
|
}
|
|
else
|
|
{
|
|
/* Init good. Let's enable it */
|
|
ssl->compression = 1;
|
|
}
|
|
}
|
|
}
|
|
compLen--;
|
|
}
|
|
else
|
|
{
|
|
c++;
|
|
compLen--;
|
|
}
|
|
}
|
|
# else
|
|
c += compLen;
|
|
# endif
|
|
rc = parseClientHelloExtensions(ssl, &c, end - c);
|
|
if (rc < 0)
|
|
{
|
|
/* Alerts are set by the extension parse */
|
|
return rc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if defined(USE_INTERCEPTOR) || defined(ALLOW_SSLV2_CLIENT_HELLO_PARSE)
|
|
/* Parse a SSLv2 ClientHello message. The same information is
|
|
conveyed but the order and format is different.
|
|
First get the cipher suite length, session id length and challenge
|
|
(client random) length - all two byte values, network byte order */
|
|
uint32_t challengeLen;
|
|
psTraceInfo("Parsing SSLv2 ClientHello\n");
|
|
if (end - c < 6)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceInfo("Can't parse hello message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
suiteLen = *c << 8; c++;
|
|
suiteLen += *c; c++;
|
|
if (suiteLen == 0 || suiteLen % 3 != 0)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceInfo("Can't parse hello message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
ssl->sessionIdLen = *c << 8; c++;
|
|
ssl->sessionIdLen += *c; c++;
|
|
/* A resumed session would use a SSLv3 ClientHello, not SSLv2. */
|
|
if (ssl->sessionIdLen != 0)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceInfo("Bad resumption request\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
challengeLen = *c << 8; c++;
|
|
challengeLen += *c; c++;
|
|
#ifdef ALLOW_SSLV2_CLIENT_HELLO_PARSE
|
|
if (challengeLen != 32) /* Allow only 32-bit, as per RFC 2246, E.2. */
|
|
#else
|
|
if (challengeLen < 16 || challengeLen > 32)
|
|
#endif
|
|
{
|
|
psTraceInfo("Bad challenge length\n");
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* Validate the three lengths that were just sent to us, don't
|
|
want any buffer overflows while parsing the remaining data */
|
|
if ((uint32) (end - c) != suiteLen + ssl->sessionIdLen +
|
|
challengeLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Malformed SSLv2 clientHello\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* Parse the cipher suite list similar to the SSLv3 method, except
|
|
each suite is 3 bytes, instead of two bytes. We define the suite
|
|
as an integer value, so either method works for lookup.
|
|
We don't support session resumption from V2 handshakes, so don't
|
|
need to worry about matching resumed cipher suite. */
|
|
suiteStart = c;
|
|
while (c < (suiteStart + suiteLen))
|
|
{
|
|
cipher = *c << 16; c++;
|
|
cipher += *c << 8; c++;
|
|
cipher += *c; c++;
|
|
/* NOT CHOOSING SUITE HERE ANY LONGER*/
|
|
}
|
|
/* We don't allow session IDs for v2 ClientHellos */
|
|
if (ssl->sessionIdLen > 0)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceInfo("SSLv2 sessions not allowed\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* The client random (between 16 and 32 bytes) fills the least
|
|
significant bytes in the (always) 32 byte SSLv3 client random */
|
|
memset(ssl->sec.clientRandom, 0x0, SSL_HS_RANDOM_SIZE);
|
|
memcpy(ssl->sec.clientRandom + (SSL_HS_RANDOM_SIZE - challengeLen),
|
|
c, challengeLen);
|
|
c += challengeLen;
|
|
# else
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceInfo("SSLV2 CLIENT_HELLO not supported.\n");
|
|
return MATRIXSSL_ERROR;
|
|
# endif
|
|
}
|
|
|
|
/* ClientHello should be the only one in the record. */
|
|
if (c != end)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceInfo("Invalid final client hello length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
/* Look up the session id for ssl session resumption. If found, we
|
|
load the pre-negotiated masterSecret and cipher.
|
|
A resumed request must meet the following restrictions:
|
|
The id must be present in the lookup table
|
|
The requested version must match the original version
|
|
The cipher suite list must contain the original cipher suite
|
|
*/
|
|
if (ssl->sessionIdLen > 0)
|
|
{
|
|
/* Check if we are resuming on a session ticket first. It is
|
|
legal for a client to send both a session ID and a ticket. If
|
|
the ticket is used, the session ID should not be used at all */
|
|
# ifdef USE_STATELESS_SESSION_TICKETS
|
|
if ((ssl->flags & SSL_FLAGS_RESUMED) && (ssl->sid) &&
|
|
(ssl->sid->sessionTicketState == SESS_TICKET_STATE_USING_TICKET))
|
|
{
|
|
goto SKIP_STANDARD_RESUMPTION;
|
|
}
|
|
# endif
|
|
|
|
if (matrixResumeSession(ssl) >= 0)
|
|
{
|
|
ssl->flags &= ~SSL_FLAGS_CLIENT_AUTH;
|
|
ssl->flags |= SSL_FLAGS_RESUMED;
|
|
# ifdef USE_MATRIXSSL_STATS
|
|
matrixsslUpdateStat(ssl, RESUMPTIONS_STAT, 1);
|
|
# endif
|
|
}
|
|
else
|
|
{
|
|
ssl->flags &= ~SSL_FLAGS_RESUMED;
|
|
# ifdef USE_STATELESS_SESSION_TICKETS
|
|
/* Client MAY generate and include a Session ID in the
|
|
TLS ClientHello. If the server accepts the ticket
|
|
and the Session ID is not empty, then it MUST respond
|
|
with the same Session ID present in the ClientHello. Check
|
|
if client is even using the mechanism though */
|
|
if (ssl->sid)
|
|
{
|
|
if (ssl->sid->sessionTicketState == SESS_TICKET_STATE_INIT)
|
|
{
|
|
memset(ssl->sessionId, 0, SSL_MAX_SESSION_ID_SIZE);
|
|
ssl->sessionIdLen = 0;
|
|
}
|
|
else
|
|
{
|
|
/* This flag means we received a session we can't resume
|
|
but we have to send it back if we also get a ticket
|
|
later that we like */
|
|
ssl->extFlags.session_id = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
memset(ssl->sessionId, 0, SSL_MAX_SESSION_ID_SIZE);
|
|
ssl->sessionIdLen = 0;
|
|
}
|
|
# else
|
|
memset(ssl->sessionId, 0, SSL_MAX_SESSION_ID_SIZE);
|
|
ssl->sessionIdLen = 0;
|
|
# ifdef USE_MATRIXSSL_STATS
|
|
matrixsslUpdateStat(ssl, FAILED_RESUMPTIONS_STAT, 1);
|
|
# endif
|
|
# endif
|
|
}
|
|
}
|
|
|
|
# ifdef USE_STATELESS_SESSION_TICKETS
|
|
SKIP_STANDARD_RESUMPTION:
|
|
# endif
|
|
|
|
/* If resumed, confirm the cipher suite was sent. Otherwise, choose
|
|
the cipher suite based on what the user has loaded or what the user
|
|
sends in the pubkey callback */
|
|
if (ssl->flags & SSL_FLAGS_RESUMED)
|
|
{
|
|
/* Have to rewalk ciphers and see if they sent the cipher. Can
|
|
move suiteStart safely since we'll be the last to use it */
|
|
suiteEnd = suiteStart + suiteLen;
|
|
resumptionOnTrack = 0;
|
|
while (suiteStart < suiteEnd)
|
|
{
|
|
if (ssl->rec.majVer > SSL2_MAJ_VER)
|
|
{
|
|
cipher = *suiteStart << 8; suiteStart++;
|
|
cipher += *suiteStart; suiteStart++;
|
|
}
|
|
else
|
|
{
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
psTraceInfo("SSLV2 not supported.\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if (cipher == ssl->cipher->ident)
|
|
{
|
|
resumptionOnTrack = 1;
|
|
}
|
|
}
|
|
if (resumptionOnTrack == 0)
|
|
{
|
|
/* Previous cipher suite wasn't sent for resumption. This is an
|
|
error according to the specs */
|
|
psTraceIntInfo("Client didn't send cipher %d for resumption\n",
|
|
ssl->cipher->ident);
|
|
ssl->cipher = sslGetCipherSpec(ssl, SSL_NULL_WITH_NULL_NULL);
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* User helps pick the cipher based on the key material. Successful
|
|
end result will be assignment of ssl->cipher */
|
|
if (chooseCipherSuite(ssl, suiteStart, suiteLen) < 0)
|
|
{
|
|
psTraceInfo("Server could not support any client cipher suites\n");
|
|
ssl->cipher = sslGetCipherSpec(ssl, SSL_NULL_WITH_NULL_NULL);
|
|
if (ssl->err != SSL_ALERT_UNRECOGNIZED_NAME)
|
|
{
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
}
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if (ssl->cipher->ident == 0)
|
|
{
|
|
psTraceInfo("Client attempting SSL_NULL_WITH_NULL_NULL conn\n");
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
|
|
matrixSslSetKexFlags(ssl);
|
|
|
|
/* If we're resuming a handshake, then the next handshake message we
|
|
expect is the finished message. Otherwise we do the full handshake */
|
|
if (ssl->flags & SSL_FLAGS_RESUMED)
|
|
{
|
|
ssl->hsState = SSL_HS_FINISHED;
|
|
}
|
|
else
|
|
{
|
|
# ifdef USE_DHE_CIPHER_SUITE
|
|
/* If we are DH key exchange we need to generate some keys. The
|
|
FLAGS_DHE_KEY_EXCH will eventually drive the state matchine to
|
|
the ServerKeyExchange path, but ECDH_ suites need the key gen now */
|
|
if (ssl->flags & SSL_FLAGS_DHE_KEY_EXCH)
|
|
{
|
|
|
|
# ifdef USE_ECC_CIPHER_SUITE
|
|
if (ssl->flags & SSL_FLAGS_ECC_CIPHER)
|
|
{
|
|
/* If ecCurveId is zero and we received the extension, then
|
|
we really couldn't match and can't continue. */
|
|
if (ssl->ecInfo.ecCurveId == 0 &&
|
|
(ssl->ecInfo.ecFlags & IS_RECVD_EXT))
|
|
{
|
|
psTraceInfo("Did not share any EC curves with client\n");
|
|
/* Don't see any particular alert for this case */
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* A ecCurveId of zero (with no extension) will return a
|
|
default which is fine according to spec */
|
|
if (getEccParamById(ssl->ecInfo.ecCurveId, &curve) < 0)
|
|
{
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if (psEccNewKey(ssl->hsPool, &ssl->sec.eccKeyPriv, curve) < 0)
|
|
{
|
|
return PS_MEM_FAIL;
|
|
}
|
|
# ifdef USE_ECC_EPHEMERAL_KEY_CACHE
|
|
if ((rc = matrixSslGenEphemeralEcKey(ssl->keys,
|
|
ssl->sec.eccKeyPriv, curve, pkiData)) < 0)
|
|
{
|
|
# else
|
|
if ((rc = psEccGenKey(ssl->hsPool, ssl->sec.eccKeyPriv,
|
|
curve, pkiData)) < 0)
|
|
{
|
|
|
|
# endif
|
|
psEccDeleteKey(&ssl->sec.eccKeyPriv);
|
|
psTraceInfo("GenEphemeralEcc failed\n");
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
return rc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
# endif /* USE_ECC_CIPHER_SUITE */
|
|
# ifdef REQUIRE_DH_PARAMS
|
|
/* Servers using DH suites know DH key sizes when handshake
|
|
pool is created so that has been accounted for here */
|
|
if ((ssl->sec.dhKeyPriv = psMalloc(ssl->hsPool,
|
|
sizeof(psDhKey_t))) == NULL)
|
|
{
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if ((rc = psDhGenKeyInts(ssl->hsPool, ssl->keys->dhParams.size,
|
|
&ssl->keys->dhParams.p, &ssl->keys->dhParams.g,
|
|
ssl->sec.dhKeyPriv, pkiData)) < 0)
|
|
{
|
|
psFree(ssl->sec.dhKeyPriv, ssl->hsPool);
|
|
ssl->sec.dhKeyPriv = NULL;
|
|
psTraceInfo("Error generating DH keys\n");
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# endif
|
|
# ifdef USE_ECC_CIPHER_SUITE
|
|
}
|
|
# endif /* USE_ECC_CIPHER_SUITE */
|
|
}
|
|
# endif /* USE_DHE_CIPHER_SUITE */
|
|
|
|
ssl->hsState = SSL_HS_CLIENT_KEY_EXCHANGE;
|
|
# ifdef USE_CLIENT_AUTH
|
|
/* Next state in client authentication case is to receive the cert */
|
|
if (ssl->flags & SSL_FLAGS_CLIENT_AUTH)
|
|
{
|
|
# ifdef USE_ANON_DH_CIPHER_SUITE
|
|
/* However, what if the server has called for client auth and
|
|
the client is requesting an 'anon' cipher suite?
|
|
|
|
SECURITY: Options are to default to what the
|
|
client wants, what the server wants, or error out. The
|
|
current implementation does what the client wants. */
|
|
if (ssl->flags & SSL_FLAGS_ANON_CIPHER)
|
|
{
|
|
psTraceIntInfo(
|
|
"Anon cipher %d negotiated. Disabling client auth\n",
|
|
ssl->cipher->ident);
|
|
ssl->flags &= ~SSL_FLAGS_CLIENT_AUTH;
|
|
}
|
|
else
|
|
{
|
|
# endif /* USE_ANON_DH_CIPHER_SUITE */
|
|
ssl->hsState = SSL_HS_CERTIFICATE;
|
|
# ifdef USE_ANON_DH_CIPHER_SUITE
|
|
}
|
|
# endif /* USE_ANON_DH_CIPHER_SUITE */
|
|
}
|
|
# endif /* USE_CLIENT_AUTH */
|
|
}
|
|
/* Now that we've parsed the ClientHello, we need to tell the caller that
|
|
we have a handshake response to write out.
|
|
The caller should call sslWrite upon receiving this return code. */
|
|
*cp = c;
|
|
ssl->decState = SSL_HS_CLIENT_HELLO;
|
|
return SSL_PROCESS_DATA;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
int32 parseClientKeyExchange(ssl_t *ssl, int32 hsLen, unsigned char **cp,
|
|
unsigned char *end)
|
|
{
|
|
int32 rc, pubKeyLen;
|
|
unsigned char *c;
|
|
|
|
# ifdef USE_RSA_CIPHER_SUITE
|
|
unsigned char R[SSL_HS_RSA_PREMASTER_SIZE - 2];
|
|
psPool_t *ckepkiPool = NULL;
|
|
# endif
|
|
# ifdef USE_PSK_CIPHER_SUITE
|
|
uint8_t pskLen;
|
|
unsigned char *pskKey = NULL;
|
|
# endif
|
|
void *pkiData = ssl->userPtr;
|
|
|
|
c = *cp;
|
|
|
|
/* RSA: This message contains the premaster secret encrypted with the
|
|
server's public key (from the Certificate). The premaster
|
|
secret is 48 bytes of random data, but the message will be longer
|
|
than that because the 48 bytes are padded before encryption
|
|
according to PKCS#1v1.5. After encryption, we should have the
|
|
correct length. */
|
|
psTraceHs(">>> Server parsing CLIENT_KEY_EXCHANGE\n");
|
|
if ((int32) (end - c) < hsLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid ClientKeyExchange length 1\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
pubKeyLen = hsLen;
|
|
# ifdef USE_TLS
|
|
/* TLS - Two byte length is explicit. */
|
|
if (ssl->majVer >= TLS_MAJ_VER && ssl->minVer >= TLS_MIN_VER)
|
|
{
|
|
if (end - c < 2)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid ClientKeyExchange length 2\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# ifdef USE_ECC_CIPHER_SUITE
|
|
if (ssl->flags & SSL_FLAGS_ECC_CIPHER)
|
|
{
|
|
pubKeyLen = *c; c++;
|
|
}
|
|
else
|
|
{
|
|
# endif /* USE_ECC_CIPHER_SUITE */
|
|
pubKeyLen = *c << 8; c++;
|
|
pubKeyLen += *c; c++;
|
|
# ifdef USE_ECC_CIPHER_SUITE
|
|
}
|
|
# endif /* USE_ECC_CIPHER_SUITE */
|
|
if ((int32) (end - c) < pubKeyLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid ClientKeyExchange length 3\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
# endif /* USE_TLS */
|
|
|
|
# ifdef USE_DHE_CIPHER_SUITE
|
|
if (ssl->flags & SSL_FLAGS_DHE_KEY_EXCH)
|
|
{
|
|
if (ssl->majVer == SSL3_MAJ_VER && ssl->minVer == SSL3_MIN_VER)
|
|
{
|
|
# ifdef USE_ECC_CIPHER_SUITE
|
|
/* Support ECC ciphers in SSLv3. This isn't really a desirable
|
|
combination and it's a fuzzy area in the specs but it works */
|
|
if (!(ssl->flags & SSL_FLAGS_ECC_CIPHER))
|
|
{
|
|
# endif
|
|
/* DH cipher suites use the ClientDiffieHellmanPublic format
|
|
which always includes the explicit key length regardless
|
|
of protocol. If TLS, we already stripped it out above. */
|
|
if (end - c < 2)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid ClientKeyExchange length 4\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
pubKeyLen = *c << 8; c++;
|
|
pubKeyLen += *c; c++;
|
|
if ((int32) (end - c) < pubKeyLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid ClientKeyExchange length 5\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# ifdef USE_ECC_CIPHER_SUITE
|
|
}
|
|
else
|
|
{
|
|
pubKeyLen = *c; c++;
|
|
}
|
|
# endif
|
|
}
|
|
# ifdef USE_PSK_CIPHER_SUITE
|
|
if (ssl->flags & SSL_FLAGS_PSK_CIPHER)
|
|
{
|
|
/* That initial pubKeyLen we read off the top was actually the
|
|
length of the PSK id that we need to find a key for */
|
|
if ((uint32) (end - c) < pubKeyLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid ClientKeyExchange PSK length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
/* If there are PSKs loaded, look at those. Otherwise see if
|
|
there is a callback. */
|
|
if (ssl->keys && ssl->keys->pskKeys)
|
|
{
|
|
matrixSslPskGetKey(ssl, c, pubKeyLen, &pskKey, &pskLen);
|
|
}
|
|
else if (ssl->sec.pskCb)
|
|
{
|
|
(ssl->sec.pskCb)(ssl, c, pubKeyLen, &pskKey, &pskLen);
|
|
}
|
|
if (pskKey == NULL)
|
|
{
|
|
psTraceInfo("Server doesn't not have matching pre-shared key\n");
|
|
ssl->err = SSL_ALERT_UNKNOWN_PSK_IDENTITY;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
c += pubKeyLen;
|
|
/* This is the DH pub key now */
|
|
pubKeyLen = *c << 8; c++;
|
|
pubKeyLen += *c; c++;
|
|
if ((uint32) (end - c) < pubKeyLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid ClientKeyExchange length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
# endif /* USE_PSK_CIPHER_SUITE */
|
|
|
|
# ifdef USE_ECC_CIPHER_SUITE
|
|
if (ssl->flags & SSL_FLAGS_ECC_CIPHER)
|
|
{
|
|
if (psEccNewKey(ssl->hsPool, &ssl->sec.eccKeyPub,
|
|
ssl->sec.eccKeyPriv->curve) < 0)
|
|
{
|
|
return SSL_MEM_ERROR;
|
|
}
|
|
if (psEccX963ImportKey(ssl->hsPool, c, pubKeyLen,
|
|
ssl->sec.eccKeyPub, ssl->sec.eccKeyPriv->curve) < 0)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* BUG FIX after 3.8.1a release. This increment is done later
|
|
in the function. So in cases where multiple handshake messages
|
|
were put in a single record, we are moving pubKeyLen farther
|
|
than we want which could still be in the valid buffer.
|
|
The error would be an "unexpected handshake message" when
|
|
the next message parse was attempted */
|
|
/* c += pubKeyLen; */
|
|
|
|
ssl->sec.premasterSize = ssl->sec.eccKeyPriv->curve->size;
|
|
ssl->sec.premaster = psMalloc(ssl->hsPool,
|
|
ssl->sec.premasterSize);
|
|
if (ssl->sec.premaster == NULL)
|
|
{
|
|
return SSL_MEM_ERROR;
|
|
}
|
|
if ((rc = psEccGenSharedSecret(ssl->hsPool, ssl->sec.eccKeyPriv,
|
|
ssl->sec.eccKeyPub, ssl->sec.premaster,
|
|
&ssl->sec.premasterSize, pkiData)) < 0)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
psFree(ssl->sec.premaster, ssl->hsPool);
|
|
ssl->sec.premaster = NULL;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
psEccDeleteKey(&ssl->sec.eccKeyPub);
|
|
psEccDeleteKey(&ssl->sec.eccKeyPriv);
|
|
}
|
|
else
|
|
{
|
|
# endif /* USE_ECC_CIPHER_SUITE */
|
|
# ifdef REQUIRE_DH_PARAMS
|
|
if ((ssl->sec.dhKeyPub = psMalloc(ssl->hsPool, sizeof(psDhKey_t))) == NULL)
|
|
{
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if (psDhImportPubKey(ssl->hsPool, c, pubKeyLen,
|
|
ssl->sec.dhKeyPub) < 0)
|
|
{
|
|
psFree(ssl->sec.dhKeyPub, ssl->hsPool);
|
|
ssl->sec.dhKeyPub = NULL;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/*
|
|
Now know the premaster details. Create it.
|
|
|
|
A Diffie-Hellman shared secret has, at maximum, the same number of
|
|
bytes as the prime. Use this number as our max buffer size that
|
|
will be into psDhGenSecret.
|
|
*/
|
|
ssl->sec.premasterSize = ssl->sec.dhPLen;
|
|
|
|
# ifdef USE_PSK_CIPHER_SUITE
|
|
if (ssl->flags & SSL_FLAGS_PSK_CIPHER)
|
|
{
|
|
/*
|
|
Premaster is appended with the PSK. Account for that length
|
|
here to avoid a realloc after the standard DH premaster is
|
|
created below.
|
|
*/
|
|
ssl->sec.premasterSize += pskLen + 4; /* psSize_t len heads */
|
|
}
|
|
# endif /* USE_PSK_CIPHER_SUITE */
|
|
|
|
ssl->sec.premaster = psMalloc(ssl->hsPool, ssl->sec.premasterSize);
|
|
if (ssl->sec.premaster == NULL)
|
|
{
|
|
return SSL_MEM_ERROR;
|
|
}
|
|
if ((rc = psDhGenSharedSecret(ssl->hsPool, ssl->sec.dhKeyPriv,
|
|
ssl->sec.dhKeyPub, ssl->sec.dhP, ssl->sec.dhPLen,
|
|
ssl->sec.premaster,
|
|
&ssl->sec.premasterSize, pkiData)) < 0)
|
|
{
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
psFree(ssl->sec.dhP, ssl->hsPool);
|
|
ssl->sec.dhP = NULL; ssl->sec.dhPLen = 0;
|
|
psFree(ssl->sec.dhG, ssl->hsPool);
|
|
ssl->sec.dhG = NULL; ssl->sec.dhGLen = 0;
|
|
psDhClearKey(ssl->sec.dhKeyPub);
|
|
psFree(ssl->sec.dhKeyPub, ssl->hsPool);
|
|
ssl->sec.dhKeyPub = NULL;
|
|
psDhClearKey(ssl->sec.dhKeyPriv);
|
|
psFree(ssl->sec.dhKeyPriv, ssl->hsPool);
|
|
ssl->sec.dhKeyPriv = NULL;
|
|
# ifdef USE_PSK_CIPHER_SUITE
|
|
if (ssl->flags & SSL_FLAGS_PSK_CIPHER)
|
|
{
|
|
/*
|
|
Need to prepend a psSize_t length to the premaster key.
|
|
*/
|
|
memmove(&ssl->sec.premaster[2], ssl->sec.premaster,
|
|
ssl->sec.premasterSize);
|
|
ssl->sec.premaster[0] = (ssl->sec.premasterSize & 0xFF00) >> 8;
|
|
ssl->sec.premaster[1] = (ssl->sec.premasterSize & 0xFF);
|
|
/*
|
|
Next, uint8_t length of PSK and key itself
|
|
*/
|
|
ssl->sec.premaster[ssl->sec.premasterSize + 2] = 0;
|
|
ssl->sec.premaster[ssl->sec.premasterSize + 3] = (pskLen & 0xFF);
|
|
memcpy(&ssl->sec.premaster[ssl->sec.premasterSize + 4], pskKey,
|
|
pskLen);
|
|
/*
|
|
Lastly, adjust the premasterSize
|
|
*/
|
|
ssl->sec.premasterSize += pskLen + 4;
|
|
}
|
|
# endif /* USE_PSK_CIPHER_SUITE */
|
|
# endif /* REQUIRE_DH_PARAMS */
|
|
# ifdef USE_ECC_CIPHER_SUITE
|
|
}
|
|
# endif /* USE_ECC_CIPHER_SUITE */
|
|
}
|
|
else
|
|
{
|
|
# endif /* USE_DHE_CIPHER_SUITE */
|
|
# ifdef USE_PSK_CIPHER_SUITE
|
|
if (ssl->flags & SSL_FLAGS_PSK_CIPHER)
|
|
{
|
|
|
|
if (ssl->majVer == SSL3_MAJ_VER && ssl->minVer == SSL3_MIN_VER)
|
|
{
|
|
/* SSLv3 for basic PSK suites will not have read off
|
|
pubKeyLen at this point */
|
|
pubKeyLen = *c << 8; c++;
|
|
pubKeyLen += *c; c++;
|
|
}
|
|
/* If there are PSKs loaded, look at those. Otherwise see if
|
|
there is a callback. */
|
|
if (ssl->keys && ssl->keys->pskKeys)
|
|
{
|
|
matrixSslPskGetKey(ssl, c, pubKeyLen, &pskKey,
|
|
&pskLen);
|
|
}
|
|
else if (ssl->sec.pskCb)
|
|
{
|
|
if ((ssl->sec.pskCb)(ssl, c, pubKeyLen, &pskKey, &pskLen)
|
|
< 0)
|
|
{
|
|
psTraceInfo("User couldn't find pre-shared key\n");
|
|
ssl->err = SSL_ALERT_UNKNOWN_PSK_IDENTITY;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
if (pskKey == NULL)
|
|
{
|
|
psTraceInfo("Server doesn't have matching pre-shared key\n");
|
|
ssl->err = SSL_ALERT_UNKNOWN_PSK_IDENTITY;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
ssl->sec.premasterSize = (pskLen * 2) + 4;
|
|
ssl->sec.premaster = psMalloc(ssl->hsPool,
|
|
ssl->sec.premasterSize);
|
|
if (ssl->sec.premaster == NULL)
|
|
{
|
|
return SSL_MEM_ERROR;
|
|
}
|
|
memset(ssl->sec.premaster, 0, ssl->sec.premasterSize);
|
|
ssl->sec.premaster[0] = 0;
|
|
ssl->sec.premaster[1] = (pskLen & 0xFF);
|
|
/* memset to 0 handled middle portion */
|
|
ssl->sec.premaster[2 + pskLen] = 0;
|
|
ssl->sec.premaster[3 + pskLen] = (pskLen & 0xFF);
|
|
memcpy(&ssl->sec.premaster[4 + pskLen], pskKey, pskLen);
|
|
}
|
|
else
|
|
{
|
|
# endif
|
|
# ifdef USE_ECC_CIPHER_SUITE
|
|
if (ssl->cipher->type == CS_ECDH_ECDSA ||
|
|
ssl->cipher->type == CS_ECDH_RSA)
|
|
{
|
|
if (ssl->majVer == SSL3_MAJ_VER &&
|
|
ssl->minVer == SSL3_MIN_VER)
|
|
{
|
|
/* Support ECC ciphers in SSLv3. This isn't really a
|
|
desirable combination and it's a fuzzy area in the
|
|
specs but it works */
|
|
pubKeyLen = *c; c++;
|
|
}
|
|
if (ssl->keys == NULL)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if (psEccNewKey(ssl->hsPool, &ssl->sec.eccKeyPub,
|
|
ssl->keys->privKey.key.ecc.curve) < 0)
|
|
{
|
|
return SSL_MEM_ERROR;
|
|
}
|
|
if (psEccX963ImportKey(ssl->hsPool, c, pubKeyLen,
|
|
ssl->sec.eccKeyPub, ssl->keys->privKey.key.ecc.curve)
|
|
< 0)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* BUG FIX after 3.8.1a release. This increment is done
|
|
later in the function. So in cases where multiple
|
|
handshake messages were put in a single record, we are
|
|
moving pubKeyLen farther than we want which could still
|
|
be in the valid buffer. The error would be an
|
|
"unexpected handshake message" when the next message
|
|
parse was attempted */
|
|
/* c += pubKeyLen; */
|
|
|
|
ssl->sec.premasterSize =
|
|
ssl->keys->privKey.key.ecc.curve->size;
|
|
ssl->sec.premaster = psMalloc(ssl->hsPool,
|
|
ssl->sec.premasterSize);
|
|
if (ssl->sec.premaster == NULL)
|
|
{
|
|
return SSL_MEM_ERROR;
|
|
}
|
|
if ((rc = psEccGenSharedSecret(ssl->hsPool,
|
|
&ssl->keys->privKey.key.ecc, ssl->sec.eccKeyPub,
|
|
ssl->sec.premaster, &ssl->sec.premasterSize,
|
|
pkiData)) < 0)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
psFree(ssl->sec.premaster, ssl->hsPool);
|
|
ssl->sec.premaster = NULL;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
psEccDeleteKey(&ssl->sec.eccKeyPub);
|
|
}
|
|
else
|
|
{
|
|
# endif /* USE_ECC_CIPHER_SUITE */
|
|
|
|
# ifdef USE_RSA_CIPHER_SUITE
|
|
if (ssl->keys == NULL)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* Standard RSA suite. Now have a handshake pool to allocate
|
|
the premaster storage */
|
|
ssl->sec.premasterSize = SSL_HS_RSA_PREMASTER_SIZE;
|
|
ssl->sec.premaster = psMalloc(ssl->hsPool,
|
|
SSL_HS_RSA_PREMASTER_SIZE);
|
|
if (ssl->sec.premaster == NULL)
|
|
{
|
|
return SSL_MEM_ERROR;
|
|
}
|
|
|
|
/**
|
|
@security Caution - the results of an RSA private key
|
|
decryption should never have any bearing on timing or response,
|
|
otherwise we can be vulnerable to a side channel attack.
|
|
@see http://web-in-security.blogspot.co.at/2014/08/old-attacks-on-new-tls-implementations.html
|
|
@see https://tools.ietf.org/html/rfc5246#section-7.4.7.1
|
|
"In any case, a TLS server MUST NOT generate an alert if processing an
|
|
RSA-encrypted premaster secret message fails, or the version number
|
|
is not as expected. Instead, it MUST continue the handshake with a
|
|
randomly generated premaster secret. It may be useful to log the
|
|
real cause of failure for troubleshooting purposes; however, care
|
|
must be taken to avoid leaking the information to an attacker
|
|
(through, e.g., timing, log files, or other channels.)"
|
|
*/
|
|
rc = psRsaDecryptPriv(ckepkiPool, &ssl->keys->privKey.key.rsa, c,
|
|
pubKeyLen, ssl->sec.premaster, ssl->sec.premasterSize,
|
|
pkiData);
|
|
/* Step 1 of Bleichenbacher attack mitigation. We do it here
|
|
after the RSA op, but regardless of the result of the op. */
|
|
if (psGetPrngLocked(R, sizeof(R), ssl->userPtr) < 0)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
/* Step 3
|
|
If the PKCS#1 padding is not correct, or the length of message
|
|
M is not exactly 48 bytes:
|
|
pre_master_secret = ClientHello.client_version || R
|
|
else
|
|
pre_master_secret = ClientHello.client_version || M[2..47]
|
|
|
|
Note that explicitly constructing the pre_master_secret with the
|
|
ClientHello.client_version produces an invalid master_secret if the
|
|
client has sent the wrong version in the original pre_master_secret.
|
|
|
|
Note: The version number in the PreMasterSecret is the version
|
|
offered by the client in the ClientHello.client_version, not the
|
|
version negotiated for the connection. This feature is designed to
|
|
prevent rollback attacks. Unfortunately, some old implementations
|
|
use the negotiated version instead, and therefore checking the
|
|
version number may lead to failure to interoperate with such
|
|
incorrect client implementations. This is known in OpenSSL as the
|
|
SSL_OP_TLS_ROLLBACK_BUG. MatrixSSL doesn't support these
|
|
incorrect implementations.
|
|
*/
|
|
ssl->sec.premaster[0] = ssl->reqMajVer;
|
|
ssl->sec.premaster[1] = ssl->reqMinVer;
|
|
if (rc < 0)
|
|
{
|
|
memcpy(ssl->sec.premaster + 2, R, sizeof(R));
|
|
}
|
|
else
|
|
{
|
|
/* Not necessary, but keep timing similar */
|
|
memcpy(R, ssl->sec.premaster + 2, sizeof(R));
|
|
}
|
|
|
|
/* R may contain sensitive data, eg. premaster */
|
|
memzero_s(R, sizeof(R));
|
|
|
|
# else /* RSA is the 'default' so if that didn't get hit there is a problem */
|
|
psTraceInfo("There is no handler for ClientKeyExchange parse. ERROR\n");
|
|
return MATRIXSSL_ERROR;
|
|
# endif /* USE_RSA_CIPHER_SUITE */
|
|
# ifdef USE_ECC_CIPHER_SUITE
|
|
}
|
|
# endif /* USE_ECC_CIPHER_SUITE */
|
|
# ifdef USE_PSK_CIPHER_SUITE
|
|
}
|
|
# endif /* USE_PSK_CIPHER_SUITE */
|
|
# ifdef USE_DHE_CIPHER_SUITE
|
|
}
|
|
# endif /* USE_DHE_CIPHER_SUITE */
|
|
|
|
/* Now that we've got the premaster secret, derive the various
|
|
symmetric keys using it and the client and server random values.
|
|
Update the cached session (if found) with the masterSecret and
|
|
negotiated cipher. */
|
|
if (ssl->extFlags.extended_master_secret == 1)
|
|
{
|
|
if (tlsExtendedDeriveKeys(ssl) < 0)
|
|
{
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (sslCreateKeys(ssl) < 0)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
matrixUpdateSession(ssl);
|
|
|
|
c += pubKeyLen;
|
|
ssl->hsState = SSL_HS_FINISHED;
|
|
|
|
# ifdef USE_DTLS
|
|
/* The freeing of premaster and cert were not done at the normal time
|
|
because of the retransmit scenarios. This is server side */
|
|
if (ssl->sec.premaster)
|
|
{
|
|
psFree(ssl->sec.premaster, ssl->hsPool); ssl->sec.premaster = NULL;
|
|
ssl->sec.premasterSize = 0;
|
|
}
|
|
# endif /* USE_DTLS */
|
|
|
|
# ifdef USE_CLIENT_AUTH
|
|
/* In the non client auth case, we are done with the handshake pool */
|
|
if (!(ssl->flags & SSL_FLAGS_CLIENT_AUTH))
|
|
{
|
|
# ifdef USE_DTLS
|
|
# ifndef USE_ONLY_PSK_CIPHER_SUITE
|
|
if (ssl->sec.cert)
|
|
{
|
|
psFree(ssl->sec.cert, NULL); ssl->sec.cert = NULL;
|
|
}
|
|
# endif
|
|
if (ssl->ckeMsg != NULL)
|
|
{
|
|
psFree(ssl->ckeMsg, ssl->hsPool); ssl->ckeMsg = NULL;
|
|
}
|
|
# endif /* USE_DTLS */
|
|
ssl->hsPool = NULL;
|
|
}
|
|
# else /* CLIENT_AUTH */
|
|
# ifdef USE_DTLS
|
|
if (ssl->ckeMsg != NULL)
|
|
{
|
|
psFree(ssl->ckeMsg, ssl->hsPool); ssl->ckeMsg = NULL;
|
|
}
|
|
# endif /* USE_DTLS */
|
|
ssl->hsPool = NULL;
|
|
# endif
|
|
|
|
|
|
# ifdef USE_CLIENT_AUTH
|
|
/* Tweak the state here for client authentication case */
|
|
if (ssl->flags & SSL_FLAGS_CLIENT_AUTH)
|
|
{
|
|
ssl->hsState = SSL_HS_CERTIFICATE_VERIFY;
|
|
}
|
|
# endif /* USE_CLIENT_AUTH */
|
|
|
|
*cp = c;
|
|
ssl->decState = SSL_HS_CLIENT_KEY_EXCHANGE;
|
|
return PS_SUCCESS;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
# ifndef USE_ONLY_PSK_CIPHER_SUITE
|
|
# ifdef USE_CLIENT_AUTH
|
|
int32 parseCertificateVerify(ssl_t *ssl,
|
|
unsigned char hsMsgHash[SHA512_HASH_SIZE],
|
|
unsigned char **cp, unsigned char *end)
|
|
{
|
|
uint32 certVerifyLen, pubKeyLen;
|
|
int32 rc, i;
|
|
|
|
# ifdef USE_RSA
|
|
unsigned char certVerify[SHA512_HASH_SIZE];
|
|
# endif /* USE_RSA */
|
|
unsigned char *c;
|
|
psPool_t *cvpkiPool = NULL;
|
|
void *pkiData = ssl->userPtr;
|
|
|
|
c = *cp;
|
|
rc = 0;
|
|
PS_VARIABLE_SET_BUT_UNUSED(rc); /* Note: Only used ifdef USE_ECC. */
|
|
psTraceHs(">>> Server parsing CERTIFICATE_VERIFY message\n");
|
|
|
|
# ifdef USE_TLS_1_2
|
|
if (ssl->flags & SSL_FLAGS_TLS_1_2)
|
|
{
|
|
uint32_t hashSigAlg;
|
|
|
|
if ((uint32) (end - c) < 2)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid Certificate Verify message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
hashSigAlg = HASH_SIG_MASK(c[0], c[1]);
|
|
|
|
psTracePrintSigAlgs(hashSigAlg, "Peer CertificateVerify\n");
|
|
|
|
/* The server-sent algorithms has to be one of the ones we sent in
|
|
our ClientHello extension */
|
|
if (!(ssl->hashSigAlg & hashSigAlg))
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid SigHash\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
switch (c[0])
|
|
{
|
|
|
|
case HASH_SIG_SHA256:
|
|
certVerifyLen = SHA256_HASH_SIZE;
|
|
break;
|
|
|
|
# ifdef USE_SHA384
|
|
case HASH_SIG_SHA384:
|
|
/* The one-off grab of SHA-384 handshake hash */
|
|
sslSha384RetrieveHSHash(ssl, hsMsgHash);
|
|
certVerifyLen = SHA384_HASH_SIZE;
|
|
break;
|
|
# endif
|
|
|
|
# ifdef USE_SHA512
|
|
case HASH_SIG_SHA512:
|
|
/* The one-off grab of SHA-512 handshake hash */
|
|
sslSha512RetrieveHSHash(ssl, hsMsgHash);
|
|
certVerifyLen = SHA512_HASH_SIZE;
|
|
break;
|
|
# endif
|
|
|
|
# ifdef USE_SHA1
|
|
case HASH_SIG_SHA1:
|
|
/* The one-off grab of SHA-1 handshake hash */
|
|
sslSha1RetrieveHSHash(ssl, hsMsgHash);
|
|
certVerifyLen = SHA1_HASH_SIZE;
|
|
break;
|
|
# endif
|
|
default:
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid Certificate Verify message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
c += 2;
|
|
}
|
|
else
|
|
{
|
|
certVerifyLen = MD5_HASH_SIZE + SHA1_HASH_SIZE;
|
|
}
|
|
# else
|
|
certVerifyLen = MD5_HASH_SIZE + SHA1_HASH_SIZE;
|
|
# endif /* USE_TLS_1_2 */
|
|
|
|
if ((uint32) (end - c) < 2)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid Certificate Verify message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
pubKeyLen = *c << 8; c++;
|
|
pubKeyLen |= *c; c++;
|
|
if ((uint32) (end - c) < pubKeyLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid Certificate Verify message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* The server side verification of client identity. If we can match
|
|
the signature we know the client has possesion of the private key. */
|
|
# ifdef USE_ECC
|
|
/* Need to read sig algorithm type out of cert itself */
|
|
if (ssl->sec.cert->pubKeyAlgorithm == OID_ECDSA_KEY_ALG)
|
|
{
|
|
rc = 0;
|
|
|
|
# ifdef USE_TLS_1_2
|
|
if (ssl->flags & SSL_FLAGS_TLS_1_2)
|
|
{
|
|
if ((i = psEccDsaVerify(cvpkiPool,
|
|
&ssl->sec.cert->publicKey.key.ecc,
|
|
hsMsgHash, certVerifyLen,
|
|
c, pubKeyLen, &rc, pkiData)) != 0)
|
|
{
|
|
psTraceInfo("ECDSA signature validation failed\n");
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
certVerifyLen = SHA1_HASH_SIZE; /* per spec */
|
|
if ((i = psEccDsaVerify(cvpkiPool,
|
|
&ssl->sec.cert->publicKey.key.ecc,
|
|
hsMsgHash + MD5_HASH_SIZE, certVerifyLen,
|
|
c, pubKeyLen,
|
|
&rc, pkiData)) != 0)
|
|
{
|
|
psTraceInfo("ECDSA signature validation failed\n");
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
# else
|
|
certVerifyLen = SHA1_HASH_SIZE; /* per spec */
|
|
if ((i = psEccDsaVerify(cvpkiPool,
|
|
&ssl->sec.cert->publicKey.key.ecc,
|
|
hsMsgHash + MD5_HASH_SIZE, certVerifyLen,
|
|
c, pubKeyLen,
|
|
&rc, pkiData)) != 0)
|
|
{
|
|
psTraceInfo("ECDSA signature validation failed\n");
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# endif
|
|
if (rc != 1)
|
|
{
|
|
psTraceInfo("Can't verify certVerify sig\n");
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
rc = MATRIXSSL_SUCCESS; /* done using rc as a temp */
|
|
}
|
|
else
|
|
{
|
|
# endif /* USE_ECC */
|
|
# ifdef USE_RSA
|
|
|
|
# ifdef USE_TLS_1_2
|
|
if (ssl->flags & SSL_FLAGS_TLS_1_2)
|
|
{
|
|
if ((i = pubRsaDecryptSignedElement(cvpkiPool,
|
|
&ssl->sec.cert->publicKey.key.rsa, c, pubKeyLen, certVerify,
|
|
certVerifyLen, pkiData)) < 0)
|
|
{
|
|
psTraceInfo("Unable to decrypt CertVerify digital element\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((i = psRsaDecryptPub(cvpkiPool, &ssl->sec.cert->publicKey.key.rsa, c,
|
|
pubKeyLen, certVerify, certVerifyLen, pkiData)) < 0)
|
|
{
|
|
psTraceInfo("Unable to publicly decrypt Certificate Verify message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
# else /* !USE_TLS_1_2 */
|
|
if ((i = psRsaDecryptPub(cvpkiPool, &ssl->sec.cert->publicKey.key.rsa, c,
|
|
pubKeyLen, certVerify, certVerifyLen, pkiData)) < 0)
|
|
{
|
|
psTraceInfo("Unable to publicly decrypt Certificate Verify message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# endif /* USE_TLS_1_2 */
|
|
|
|
if (memcmpct(certVerify, hsMsgHash, certVerifyLen) != 0)
|
|
{
|
|
psTraceInfo("Unable to verify client certificate signature\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# else /* RSA is 'default' so if that didn't get hit there is a problem */
|
|
psTraceInfo("There is no handler for CertificateVerify parse. ERROR\n");
|
|
return MATRIXSSL_ERROR;
|
|
# endif /* USE_RSA */
|
|
# ifdef USE_ECC
|
|
}
|
|
# endif /* USE_ECC*/
|
|
|
|
c += pubKeyLen;
|
|
ssl->hsState = SSL_HS_FINISHED;
|
|
|
|
*cp = c;
|
|
ssl->decState = SSL_HS_CERTIFICATE_VERIFY;
|
|
return PS_SUCCESS;
|
|
}
|
|
# endif /* !USE_ONLY_PSK_CIPHER_SUITE */
|
|
# endif /* USE_ONLY_PSK_CIPHER_SUITE */
|
|
#endif /* USE_SERVER_SIDE_SSL */
|
|
|
|
/******************************************************************************/
|
|
|
|
#ifdef USE_CLIENT_SIDE_SSL
|
|
int32 parseServerHello(ssl_t *ssl, int32 hsLen, unsigned char **cp,
|
|
unsigned char *end)
|
|
{
|
|
uint32 sessionIdLen, cipher = 0;
|
|
int32 rc;
|
|
unsigned char *extData;
|
|
unsigned char *c;
|
|
|
|
c = *cp;
|
|
|
|
psTraceHs(">>> Client parsing SERVER_HELLO message\n");
|
|
# ifdef USE_MATRIXSSL_STATS
|
|
matrixsslUpdateStat(ssl, SH_RECV_STAT, 1);
|
|
# endif
|
|
/* Need to track hsLen because there is no explict way to tell if
|
|
hello extensions are appended so it isn't clear if the record data
|
|
after the compression parameters are a new message or extension data */
|
|
extData = c;
|
|
|
|
# ifdef USE_DTLS
|
|
/* Know now that the allocated members that were helping with the
|
|
HELLO_VERIFY_REQUEST exchange have finished serving their purpose */
|
|
if (ssl->cookie)
|
|
{
|
|
psFree(ssl->cookie, ssl->hsPool); ssl->cookie = NULL;
|
|
ssl->cookieLen = 0; ssl->haveCookie = 0;
|
|
}
|
|
if (ssl->helloExt)
|
|
{
|
|
psFree(ssl->helloExt, ssl->hsPool); ssl->helloExt = NULL;
|
|
ssl->helloExtLen = 0;
|
|
}
|
|
# endif /* USE_DTLS */
|
|
|
|
/* First two bytes are the negotiated SSL version */
|
|
if (end - c < 2)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid ssl header version length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
ssl->reqMajVer = *c; c++;
|
|
ssl->reqMinVer = *c; c++;
|
|
psTracePrintProtocolVersion("Parsed ServerHello.server_version",
|
|
ssl->reqMajVer, ssl->reqMinVer, 1);
|
|
|
|
if (ssl->reqMajVer != ssl->majVer)
|
|
{
|
|
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
|
|
psTraceIntInfo("Unsupported ssl version: %d\n", ssl->reqMajVer);
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
# ifdef USE_TLS
|
|
/* See if the protocol is being downgraded */
|
|
if (ssl->reqMinVer != ssl->minVer)
|
|
{
|
|
if (ssl->clientRejectVersionDowngrade)
|
|
{
|
|
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
|
|
psTraceInfo("Error: version downgrade attempt by server ");
|
|
psTraceInfo(" rejected: ServerHello.server_version <");
|
|
psTraceInfo(" ClientHello.client_version\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
if (ssl->reqMinVer == SSL3_MIN_VER && ssl->minVer >= TLS_MIN_VER)
|
|
{
|
|
# ifdef DISABLE_SSLV3
|
|
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
|
|
psTraceInfo("Server wants to talk SSLv3 but it's disabled\n");
|
|
return MATRIXSSL_ERROR;
|
|
# else
|
|
/* Server minVer now becomes OUR initial requested version.
|
|
This is used during the creation of the premaster where
|
|
this initial requested version is part of the calculation.
|
|
The RFC actually says to use the original requested version
|
|
but no implemenations seem to follow that and just use the
|
|
agreed upon one. */
|
|
ssl->reqMinVer = ssl->minVer;
|
|
ssl->minVer = SSL3_MIN_VER;
|
|
ssl->flags &= ~SSL_FLAGS_TLS;
|
|
# ifdef USE_TLS_1_1
|
|
ssl->flags &= ~SSL_FLAGS_TLS_1_1;
|
|
# endif /* USE_TLS_1_1 */
|
|
# ifdef USE_TLS_1_2
|
|
ssl->flags &= ~SSL_FLAGS_TLS_1_2;
|
|
# endif /* USE_TLS_1_2 */
|
|
# endif /* DISABLE_SSLV3 */
|
|
}
|
|
else
|
|
{
|
|
# ifdef USE_TLS_1_1
|
|
# ifdef USE_TLS_1_2
|
|
/* Step down one at a time */
|
|
if (ssl->reqMinVer < TLS_1_2_MIN_VER &&
|
|
(ssl->flags & SSL_FLAGS_TLS_1_2))
|
|
{
|
|
ssl->flags &= ~SSL_FLAGS_TLS_1_2;
|
|
if (ssl->reqMinVer == TLS_1_1_MIN_VER)
|
|
{
|
|
# ifdef DISABLE_TLS_1_1
|
|
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
|
|
psTraceInfo("Server wants to talk TLS1.1 but it's disabled\n");
|
|
return MATRIXSSL_ERROR;
|
|
# else
|
|
ssl->reqMinVer = ssl->minVer;
|
|
ssl->minVer = TLS_1_1_MIN_VER;
|
|
goto PROTOCOL_DETERMINED;
|
|
# endif
|
|
}
|
|
}
|
|
# endif /* USE_TLS_1_2 */
|
|
if (ssl->reqMinVer == TLS_MIN_VER &&
|
|
ssl->minVer <= TLS_1_2_MIN_VER)
|
|
{
|
|
# ifdef DISABLE_TLS_1_0
|
|
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
|
|
psTraceInfo("Server wants to talk TLS1.0 but it's disabled\n");
|
|
return MATRIXSSL_ERROR;
|
|
# else
|
|
if (ssl->disable_tls_1_0)
|
|
{
|
|
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
|
|
psTraceInfo("Server wants to talk TLS1.0 but it's disabled\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
else
|
|
{
|
|
ssl->reqMinVer = ssl->minVer;
|
|
ssl->minVer = TLS_MIN_VER;
|
|
ssl->flags &= ~SSL_FLAGS_TLS_1_1;
|
|
}
|
|
# endif
|
|
}
|
|
else
|
|
{
|
|
# endif /* USE_TLS_1_1 */
|
|
# ifdef USE_DTLS
|
|
/* Tests for DTLS downgrades */
|
|
if (ssl->flags & SSL_FLAGS_DTLS)
|
|
{
|
|
if (ssl->reqMinVer == DTLS_MIN_VER &&
|
|
ssl->minVer == DTLS_1_2_MIN_VER)
|
|
{
|
|
ssl->reqMinVer = ssl->minVer;
|
|
ssl->minVer = DTLS_MIN_VER;
|
|
ssl->flags &= ~SSL_FLAGS_TLS_1_2;
|
|
goto PROTOCOL_DETERMINED;
|
|
}
|
|
}
|
|
# endif
|
|
/* Wasn't able to settle on a common protocol */
|
|
ssl->err = SSL_ALERT_PROTOCOL_VERSION;
|
|
psTraceIntInfo("Unsupported ssl version: %d\n",
|
|
ssl->reqMajVer);
|
|
return MATRIXSSL_ERROR;
|
|
# ifdef USE_TLS_1_1
|
|
}
|
|
# endif /* USE_TLS_1_1 */
|
|
}
|
|
}
|
|
# endif /* USE_TLS */
|
|
|
|
# if (defined (USE_TLS_1_2) && !defined(DISABLE_TLS_1_1)) || defined (USE_DTLS)
|
|
PROTOCOL_DETERMINED:
|
|
# endif /* (USE_TLS_1_2 && !DISABLE_TLS_1_1) || USE_DTLS */
|
|
|
|
/* Next is a 32 bytes of random data for key generation
|
|
and a single byte with the session ID length */
|
|
if (end - c < SSL_HS_RANDOM_SIZE + 1)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid length of random data\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
memcpy(ssl->sec.serverRandom, c, SSL_HS_RANDOM_SIZE);
|
|
c += SSL_HS_RANDOM_SIZE;
|
|
sessionIdLen = *c; c++;
|
|
if (sessionIdLen > SSL_MAX_SESSION_ID_SIZE ||
|
|
(uint32) (end - c) < sessionIdLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* If a session length was specified, the server has sent us a
|
|
session Id. We may have requested a specific session, and the
|
|
server may or may not agree to use that session. */
|
|
if (sessionIdLen > 0)
|
|
{
|
|
if (ssl->sessionIdLen > 0)
|
|
{
|
|
if (memcmp(ssl->sessionId, c, sessionIdLen) == 0)
|
|
{
|
|
ssl->flags |= SSL_FLAGS_RESUMED;
|
|
}
|
|
else
|
|
{
|
|
ssl->cipher = sslGetCipherSpec(ssl, SSL_NULL_WITH_NULL_NULL);
|
|
memset(ssl->sec.masterSecret, 0x0, SSL_HS_MASTER_SIZE);
|
|
ssl->sessionIdLen = (unsigned char) sessionIdLen;
|
|
memcpy(ssl->sessionId, c, sessionIdLen);
|
|
ssl->flags &= ~SSL_FLAGS_RESUMED;
|
|
# ifdef USE_MATRIXSSL_STATS
|
|
matrixsslUpdateStat(ssl, FAILED_RESUMPTIONS_STAT, 1);
|
|
# endif
|
|
}
|
|
# ifdef USE_EAP_FAST /* TODO Could also do this for any TICKET */
|
|
if (ssl->sid->sessionTicketState == SESS_TICKET_STATE_SENT_TICKET)
|
|
{
|
|
if (ssl->flags & SSL_FLAGS_RESUMED)
|
|
{
|
|
/* The server has accepted our session ticket, and indicated that
|
|
by echoing the random session id we sent. */
|
|
ssl->extFlags.eap_fast_master_secret = 1;
|
|
/* TODO could derive eap keys here */
|
|
}
|
|
else
|
|
{
|
|
/* The server isn't going to use our ticket. But may still
|
|
send a ticket extension and (possibly blank) ticket message */
|
|
ssl->extFlags.eap_fast_master_secret = 0;
|
|
}
|
|
}
|
|
# endif
|
|
}
|
|
else
|
|
{
|
|
ssl->sessionIdLen = (unsigned char) sessionIdLen;
|
|
memcpy(ssl->sessionId, c, sessionIdLen);
|
|
}
|
|
c += sessionIdLen;
|
|
}
|
|
else
|
|
{
|
|
if (ssl->sessionIdLen > 0)
|
|
{
|
|
ssl->cipher = sslGetCipherSpec(ssl, SSL_NULL_WITH_NULL_NULL);
|
|
memset(ssl->sec.masterSecret, 0x0, SSL_HS_MASTER_SIZE);
|
|
ssl->sessionIdLen = 0;
|
|
memset(ssl->sessionId, 0x0, SSL_MAX_SESSION_ID_SIZE);
|
|
ssl->flags &= ~SSL_FLAGS_RESUMED;
|
|
# ifdef USE_MATRIXSSL_STATS
|
|
matrixsslUpdateStat(ssl, FAILED_RESUMPTIONS_STAT, 1);
|
|
# endif
|
|
}
|
|
}
|
|
/* Next is the two byte cipher suite */
|
|
if (end - c < 2)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid cipher suite length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
cipher = *c << 8; c++;
|
|
cipher += *c; c++;
|
|
|
|
/* A resumed session can only match the cipher originally
|
|
negotiated. Otherwise, match the first cipher that we support */
|
|
if (ssl->flags & SSL_FLAGS_RESUMED)
|
|
{
|
|
psAssert(ssl->cipher != NULL);
|
|
if (ssl->cipher->ident != cipher)
|
|
{
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
psTraceInfo("Can't support resumed cipher\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((ssl->cipher = sslGetCipherSpec(ssl, cipher)) == NULL)
|
|
{
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
psTraceIntInfo("Can't support requested cipher: %d\n", cipher);
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
matrixSslSetKexFlags(ssl);
|
|
|
|
/* Decode the compression parameter byte. */
|
|
# define COMPRESSION_METHOD_NULL 0x0
|
|
# define COMPRESSION_METHOD_DEFLATE 0x1
|
|
if (end - c < 1)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceInfo("Expected compression value\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
switch (*c)
|
|
{
|
|
case COMPRESSION_METHOD_NULL:
|
|
/* No compression */
|
|
break;
|
|
# ifdef USE_ZLIB_COMPRESSION
|
|
case COMPRESSION_METHOD_DEFLATE:
|
|
ssl->inflate.zalloc = NULL;
|
|
ssl->inflate.zfree = NULL;
|
|
ssl->inflate.opaque = NULL;
|
|
ssl->inflate.avail_in = 0;
|
|
ssl->inflate.next_in = NULL;
|
|
if (inflateInit(&ssl->inflate) != Z_OK)
|
|
{
|
|
psTraceInfo("inflateInit fail. No compression\n");
|
|
}
|
|
else
|
|
{
|
|
ssl->deflate.zalloc = Z_NULL;
|
|
ssl->deflate.zfree = Z_NULL;
|
|
ssl->deflate.opaque = Z_NULL;
|
|
if (deflateInit(&ssl->deflate, Z_DEFAULT_COMPRESSION) != Z_OK)
|
|
{
|
|
psTraceInfo("deflateInit fail. No compression\n");
|
|
inflateEnd(&ssl->inflate);
|
|
}
|
|
else
|
|
{
|
|
ssl->compression = 1; /* Both contexts initialized */
|
|
}
|
|
}
|
|
break;
|
|
# endif /* USE_ZLIB_COMPRESSION */
|
|
default:
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("zlib compression not enabled.\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* At this point, if we're resumed, we have all the required info
|
|
to derive keys. The next handshake message we expect is
|
|
the Finished message.
|
|
After incrementing c below, we will either be pointing at 'end'
|
|
with no more data in the message, or at the first byte of an optional
|
|
extension. */
|
|
c++;
|
|
|
|
/* If our sent ClientHello had an extension there could be extension data
|
|
to parse here: http://www.faqs.org/rfcs/rfc3546.html
|
|
|
|
The explict test on hsLen is necessary for TLS 1.0 and 1.1 because
|
|
there is no good way to tell if the remaining record data is the
|
|
next handshake message or if it is extension data */
|
|
if (c != end && ((int32) hsLen > (c - extData)))
|
|
{
|
|
rc = parseServerHelloExtensions(ssl, hsLen, extData, &c, end - c);
|
|
if (rc < 0)
|
|
{
|
|
/* Alerts will already have been set inside */
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
# ifdef USE_OCSP_MUST_STAPLE
|
|
/* Will catch cases where a server does not send any extensions at all */
|
|
if (ssl->extFlags.req_status_request == 1)
|
|
{
|
|
if (ssl->extFlags.status_request == 0)
|
|
{
|
|
psTraceInfo("Server doesn't support OCSP stapling\n");
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
# endif
|
|
|
|
if (ssl->maxPtFrag & 0x10000 || ssl->extFlags.req_max_fragment_len)
|
|
{
|
|
/* Server didn't respond to our MAX_FRAG request. Reset default */
|
|
psTraceInfo("Server ignored max fragment length ext request\n");
|
|
ssl->maxPtFrag = SSL_MAX_PLAINTEXT_LEN;
|
|
}
|
|
|
|
if (ssl->extFlags.req_sni)
|
|
{
|
|
psTraceInfo("Server ignored SNI ext request\n");
|
|
}
|
|
|
|
# ifdef USE_STATELESS_SESSION_TICKETS
|
|
if (ssl->sid &&
|
|
ssl->sid->sessionTicketState == SESS_TICKET_STATE_SENT_TICKET)
|
|
{
|
|
/*
|
|
Server did not send an extension reply to our populated ticket.
|
|
|
|
From the updated RFC 5077:
|
|
|
|
"It is also permissible to have an exchange using the
|
|
abbreviated handshake defined in Figure 2 of RFC 4346, where
|
|
the client uses the SessionTicket extension to resume the
|
|
session, but the server does not wish to issue a new ticket,
|
|
and therefore does not send a SessionTicket extension."
|
|
|
|
Lame. We don't get an indication that the server accepted or
|
|
rejected our ticket until we see the next handshake message.
|
|
If they accepted it we'll see a ChangeCipherSpec message and
|
|
if they rejected it we'll see a Certificate message. Let's
|
|
flag this case of a non-response and handle it in the CCS parse.
|
|
|
|
TODO - could also send a sessionId and see if it is returned here.
|
|
Spec requires the same sessionId to be returned if ticket is accepted.
|
|
*/
|
|
ssl->sid->sessionTicketState = SESS_TICKET_STATE_IN_LIMBO;
|
|
}
|
|
# endif /* USE_STATELESS_SESSION_TICKETS */
|
|
|
|
|
|
# if 0 /* TODO moved to extDecode:parseServerHelloExtensions */
|
|
# ifdef ENABLE_SECURE_REHANDSHAKES
|
|
if (renegotiationExt == 0)
|
|
{
|
|
# ifdef REQUIRE_SECURE_REHANDSHAKES
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
psTraceInfo("Srv doesn't support renegotiationInfo\n");
|
|
return MATRIXSSL_ERROR;
|
|
# else
|
|
if (ssl->secureRenegotiationFlag == PS_TRUE)
|
|
{
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
psTraceInfo("Srv didn't send renegotiationInfo on re-hndshk\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# ifndef ENABLE_INSECURE_REHANDSHAKES
|
|
/* This case can only be hit if ENABLE_SECURE is on because otherwise
|
|
we wouldn't even have got this far because both would be off. */
|
|
if (ssl->secureRenegotiationFlag == PS_FALSE &&
|
|
ssl->myVerifyDataLen > 0)
|
|
{
|
|
ssl->err = SSL_ALERT_HANDSHAKE_FAILURE;
|
|
psTraceInfo("Srv attempting insecure renegotiation\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# endif /* !ENABLE_SECURE_REHANDSHAKES */
|
|
# endif /* REQUIRE_SECURE_REHANDSHAKES */
|
|
}
|
|
# endif /* ENABLE_SECURE_REHANDSHAKES */
|
|
# endif
|
|
|
|
if (ssl->flags & SSL_FLAGS_RESUMED)
|
|
{
|
|
if (sslCreateKeys(ssl) < 0)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
ssl->hsState = SSL_HS_FINISHED;
|
|
}
|
|
else
|
|
{
|
|
ssl->hsState = SSL_HS_CERTIFICATE;
|
|
# ifdef USE_ANON_DH_CIPHER_SUITE
|
|
/* Anonymous DH uses SERVER_KEY_EXCHANGE message to send key params */
|
|
if (ssl->flags & SSL_FLAGS_ANON_CIPHER)
|
|
{
|
|
ssl->hsState = SSL_HS_SERVER_KEY_EXCHANGE;
|
|
}
|
|
# endif /* USE_ANON_DH_CIPHER_SUITE */
|
|
# ifdef USE_PSK_CIPHER_SUITE
|
|
/* PSK ciphers never send a CERTIFICATE message. */
|
|
if (ssl->flags & SSL_FLAGS_PSK_CIPHER)
|
|
{
|
|
ssl->hsState = SSL_HS_SERVER_KEY_EXCHANGE;
|
|
}
|
|
# endif /* USE_PSK_CIPHER_SUITE */
|
|
}
|
|
|
|
*cp = c;
|
|
ssl->decState = SSL_HS_SERVER_HELLO;
|
|
return PS_SUCCESS;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
int32 parseServerKeyExchange(ssl_t *ssl,
|
|
unsigned char hsMsgHash[SHA512_HASH_SIZE],
|
|
unsigned char **cp, unsigned char *end)
|
|
{
|
|
unsigned char *c;
|
|
|
|
# ifdef USE_DHE_CIPHER_SUITE
|
|
int32 i;
|
|
uint32 pubDhLen, hashSize;
|
|
psPool_t *skepkiPool = NULL;
|
|
# ifndef USE_ONLY_PSK_CIPHER_SUITE
|
|
psDigestContext_t digestCtx;
|
|
unsigned char *sigStart = NULL, *sigStop = NULL;
|
|
# endif /* USE_ONLY_PSK_CIPHER_SUITE */
|
|
# ifdef USE_TLS_1_2
|
|
uint32 skeHashSigAlg;
|
|
# endif
|
|
# ifdef USE_RSA_CIPHER_SUITE
|
|
unsigned char sigOut[MAX_HASH_SIZE];
|
|
# endif
|
|
# ifdef USE_ECC_CIPHER_SUITE
|
|
uint32 res;
|
|
const psEccCurve_t *curve;
|
|
# endif
|
|
void *pkiData = ssl->userPtr;
|
|
|
|
# endif /* USE_DHE_CIPHER_SUITE */
|
|
|
|
c = *cp;
|
|
|
|
psTraceHs(">>> Client parsing SERVER_KEY_EXCHANGE message\n");
|
|
# ifdef USE_DHE_CIPHER_SUITE
|
|
/* Check the DH status. Could also be a PSK_DHE suite */
|
|
if (ssl->flags & SSL_FLAGS_DHE_KEY_EXCH)
|
|
{
|
|
|
|
# ifdef USE_PSK_CIPHER_SUITE
|
|
if (ssl->flags & SSL_FLAGS_PSK_CIPHER)
|
|
{
|
|
/* Using the value of MAX_HINT_SIZE to know if the user is
|
|
expecting a hint. The PSK specification ONLY allows these
|
|
hints if the "application profile specification" says to
|
|
include them.
|
|
|
|
Contact Support if you require assistance here */
|
|
if (SSL_PSK_MAX_HINT_SIZE > 0)
|
|
{
|
|
if ((end - c) < 2)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid PSK Hint Len\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
ssl->sec.hintLen = *c << 8; c++;
|
|
ssl->sec.hintLen |= *c; c++;
|
|
if (ssl->sec.hintLen > 0)
|
|
{
|
|
if ((unsigned short) (end - c) < ssl->sec.hintLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid PSK Hint\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
ssl->sec.hint = psMalloc(ssl->hsPool, ssl->sec.hintLen);
|
|
if (ssl->sec.hint == NULL)
|
|
{
|
|
return SSL_MEM_ERROR;
|
|
}
|
|
memcpy(ssl->sec.hint, c, ssl->sec.hintLen);
|
|
c += ssl->sec.hintLen;
|
|
}
|
|
}
|
|
}
|
|
# endif /* USE_PSK_CIPHER_SUITE */
|
|
# ifdef USE_ECC_CIPHER_SUITE
|
|
if (ssl->flags & SSL_FLAGS_ECC_CIPHER)
|
|
{
|
|
/* Entry point for ECDHE SKE parsing */
|
|
sigStart = c;
|
|
if ((end - c) < 4) /* ECCurveType, NamedCurve, ECPoint len */
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid ServerKeyExchange message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/*
|
|
Only named curves are currently supported
|
|
|
|
enum { explicit_prime (1), explicit_char2 (2),
|
|
named_curve (3), reserved(248..255) } ECCurveType;
|
|
*/
|
|
if ((int32) * c != 3)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceIntInfo("Unsupported ECCurveType message %d\n",
|
|
(int32) * c);
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
c++;
|
|
|
|
/* Next is curveId */
|
|
i = *c << 8; c++;
|
|
i |= *c; c++;
|
|
|
|
/* Return -1 if this isn't a curve we specified in client hello */
|
|
if (getEccParamById(i, &curve) < 0)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceIntInfo("Error: Could not match EC curve: %d\n", i);
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/*
|
|
struct {
|
|
opaque point <1..2^8-1>;
|
|
} ECPoint;
|
|
|
|
RFC4492
|
|
This is the byte string representation of an elliptic curve
|
|
point following the conversion routine in Section 4.3.6 of ANSI
|
|
X9.62. This byte string may represent an elliptic curve point
|
|
in uncompressed or compressed format; it MUST conform to what
|
|
client has requested through a Supported Point Formats Extension
|
|
if this extension was used.
|
|
*/
|
|
i = *c; c++;
|
|
if ((end - c) < i)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid ServerKeyExchange message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if (psEccNewKey(ssl->hsPool, &ssl->sec.eccKeyPub, curve) < 0)
|
|
{
|
|
return SSL_MEM_ERROR;
|
|
}
|
|
if (psEccX963ImportKey(ssl->hsPool, c, i,
|
|
ssl->sec.eccKeyPub, curve) < 0)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
c += i;
|
|
sigStop = c;
|
|
|
|
}
|
|
else
|
|
{
|
|
# endif /* USE_ECC_CIPHER_SUITE */
|
|
# ifdef REQUIRE_DH_PARAMS
|
|
/* Entry point for standard DH SKE parsing */
|
|
if ((end - c) < 2)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid ServerKeyExchange message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# ifndef USE_ONLY_PSK_CIPHER_SUITE
|
|
sigStart = c;
|
|
# endif
|
|
ssl->sec.dhPLen = *c << 8; c++;
|
|
ssl->sec.dhPLen |= *c; c++;
|
|
if ((uint32) (end - c) < ssl->sec.dhPLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid ServerKeyExchange message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
ssl->sec.dhP = psMalloc(ssl->hsPool, ssl->sec.dhPLen);
|
|
if (ssl->sec.dhP == NULL)
|
|
{
|
|
return SSL_MEM_ERROR;
|
|
}
|
|
memcpy(ssl->sec.dhP, c, ssl->sec.dhPLen);
|
|
c += ssl->sec.dhPLen;
|
|
|
|
ssl->sec.dhGLen = *c << 8; c++;
|
|
ssl->sec.dhGLen |= *c; c++;
|
|
if ((uint32) (end - c) < ssl->sec.dhGLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid ServerKeyExchange message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
ssl->sec.dhG = psMalloc(ssl->hsPool, ssl->sec.dhGLen);
|
|
if (ssl->sec.dhG == NULL)
|
|
{
|
|
return SSL_MEM_ERROR;
|
|
}
|
|
memcpy(ssl->sec.dhG, c, ssl->sec.dhGLen);
|
|
c += ssl->sec.dhGLen;
|
|
|
|
pubDhLen = *c << 8; c++;
|
|
pubDhLen |= *c; c++;
|
|
|
|
if ((uint32) (end - c) < pubDhLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid ServerKeyExchange message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/*
|
|
The next bit on the wire is the public key. Assign to
|
|
the session in structure format
|
|
*/
|
|
if ((ssl->sec.dhKeyPub = psMalloc(ssl->hsPool, sizeof(psDhKey_t))) == NULL)
|
|
{
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if (psDhImportPubKey(ssl->hsPool, c, pubDhLen,
|
|
ssl->sec.dhKeyPub) < 0)
|
|
{
|
|
psFree(ssl->sec.dhKeyPub, ssl->hsPool);
|
|
ssl->sec.dhKeyPub = NULL;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
c += pubDhLen;
|
|
# ifndef USE_ONLY_PSK_CIPHER_SUITE
|
|
sigStop = c;
|
|
# endif
|
|
/*
|
|
Key size is now known for premaster storage. The extra byte
|
|
is to account for the cases where the pubkey length ends
|
|
up being a byte less than the premaster. The premaster size
|
|
is adjusted accordingly when the actual secret is generated.
|
|
*/
|
|
ssl->sec.premasterSize = ssl->sec.dhPLen;
|
|
# ifdef USE_PSK_CIPHER_SUITE
|
|
if (ssl->flags & SSL_FLAGS_PSK_CIPHER)
|
|
{
|
|
/*
|
|
In the PSK case, the true premaster size is still unknown
|
|
but didn't want to change the allocation logic so just
|
|
make sure the size is large enough for the additional
|
|
PSK and length bytes
|
|
*/
|
|
ssl->sec.premasterSize += SSL_PSK_MAX_KEY_SIZE + 4;
|
|
}
|
|
# endif /* USE_PSK_CIPHER_SUITE */
|
|
ssl->sec.premaster = psMalloc(ssl->hsPool, ssl->sec.premasterSize);
|
|
if (ssl->sec.premaster == NULL)
|
|
{
|
|
return SSL_MEM_ERROR;
|
|
}
|
|
# ifdef USE_ANON_DH_CIPHER_SUITE
|
|
if (ssl->flags & SSL_FLAGS_ANON_CIPHER)
|
|
{
|
|
/*
|
|
In the anonymous case, there is no signature to follow
|
|
*/
|
|
ssl->hsState = SSL_HS_SERVER_HELLO_DONE;
|
|
*cp = c;
|
|
ssl->decState = SSL_HS_SERVER_KEY_EXCHANGE;
|
|
return PS_SUCCESS;
|
|
}
|
|
# endif /* USE_ANON_DH_CIPHER_SUITE */
|
|
# endif /* REQUIRE_DH_PARAMS */
|
|
# ifdef USE_ECC_CIPHER_SUITE
|
|
}
|
|
# endif /* USE_ECC_CIPHER_SUITE */
|
|
/*
|
|
This layer of authentation is at the key exchange level.
|
|
The server has sent a signature of the key material that
|
|
the client can validate here.
|
|
*/
|
|
if ((end - c) < 2)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid ServerKeyExchange message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
# ifdef USE_TLS_1_2
|
|
hashSize = 0;
|
|
if (ssl->flags & SSL_FLAGS_TLS_1_2)
|
|
{
|
|
skeHashSigAlg = *c << 8; c++;
|
|
skeHashSigAlg += *c; c++;
|
|
if ((skeHashSigAlg >> 8) == 0x4)
|
|
{
|
|
hashSize = SHA256_HASH_SIZE;
|
|
}
|
|
else if ((skeHashSigAlg >> 8) == 0x5)
|
|
{
|
|
hashSize = SHA384_HASH_SIZE;
|
|
}
|
|
else if ((skeHashSigAlg >> 8) == 0x6)
|
|
{
|
|
hashSize = SHA512_HASH_SIZE;
|
|
}
|
|
else if ((skeHashSigAlg >> 8) == 0x2)
|
|
{
|
|
hashSize = SHA1_HASH_SIZE;
|
|
}
|
|
else
|
|
{
|
|
psTraceIntInfo("Unsupported hashAlg SKE parse: %d\n",
|
|
skeHashSigAlg);
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
# endif /* USE_TLS_1_2 */
|
|
pubDhLen = *c << 8; c++; /* Reusing variable */
|
|
pubDhLen |= *c; c++;
|
|
|
|
if ((uint32) (end - c) < pubDhLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid ServerKeyExchange message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
# ifdef USE_RSA_CIPHER_SUITE
|
|
if (ssl->flags & SSL_FLAGS_DHE_WITH_RSA)
|
|
{
|
|
/*
|
|
We are using the public key provided by the server during the
|
|
CERTIFICATE message. That cert has already been authenticated
|
|
by this point so this signature is to ensure that entity is also
|
|
the one negotiating keys with us.
|
|
*/
|
|
# ifdef USE_TLS_1_2
|
|
/* TLS 1.2 uses single hashes everywhere */
|
|
if (ssl->flags & SSL_FLAGS_TLS_1_2)
|
|
{
|
|
if (hashSize == SHA256_HASH_SIZE)
|
|
{
|
|
psSha256PreInit(&digestCtx.sha256);
|
|
psSha256Init(&digestCtx.sha256);
|
|
psSha256Update(&digestCtx.sha256, ssl->sec.clientRandom,
|
|
SSL_HS_RANDOM_SIZE);
|
|
psSha256Update(&digestCtx.sha256, ssl->sec.serverRandom,
|
|
SSL_HS_RANDOM_SIZE);
|
|
psSha256Update(&digestCtx.sha256, sigStart,
|
|
(uint32) (sigStop - sigStart));
|
|
psSha256Final(&digestCtx.sha256, hsMsgHash);
|
|
# ifdef USE_SHA384
|
|
}
|
|
else if (hashSize == SHA384_HASH_SIZE)
|
|
{
|
|
psSha384PreInit(&digestCtx.sha384);
|
|
psSha384Init(&digestCtx.sha384);
|
|
psSha384Update(&digestCtx.sha384, ssl->sec.clientRandom,
|
|
SSL_HS_RANDOM_SIZE);
|
|
psSha384Update(&digestCtx.sha384, ssl->sec.serverRandom,
|
|
SSL_HS_RANDOM_SIZE);
|
|
psSha384Update(&digestCtx.sha384, sigStart,
|
|
(uint32) (sigStop - sigStart));
|
|
psSha384Final(&digestCtx.sha384, hsMsgHash);
|
|
# endif /* USE_SHA384 */
|
|
# ifdef USE_SHA512
|
|
}
|
|
else if (hashSize == SHA512_HASH_SIZE)
|
|
{
|
|
psSha512PreInit(&digestCtx.sha512);
|
|
psSha512Init(&digestCtx.sha512);
|
|
psSha512Update(&digestCtx.sha512, ssl->sec.clientRandom,
|
|
SSL_HS_RANDOM_SIZE);
|
|
psSha512Update(&digestCtx.sha512, ssl->sec.serverRandom,
|
|
SSL_HS_RANDOM_SIZE);
|
|
psSha512Update(&digestCtx.sha512, sigStart,
|
|
(uint32) (sigStop - sigStart));
|
|
psSha512Final(&digestCtx.sha512, hsMsgHash);
|
|
# endif /* USE_SHA512 */
|
|
# ifdef USE_SHA1
|
|
}
|
|
else if (hashSize == SHA1_HASH_SIZE)
|
|
{
|
|
psSha1PreInit(&digestCtx.sha1);
|
|
psSha1Init(&digestCtx.sha1);
|
|
psSha1Update(&digestCtx.sha1, ssl->sec.clientRandom,
|
|
SSL_HS_RANDOM_SIZE);
|
|
psSha1Update(&digestCtx.sha1, ssl->sec.serverRandom,
|
|
SSL_HS_RANDOM_SIZE);
|
|
psSha1Update(&digestCtx.sha1, sigStart,
|
|
(uint32) (sigStop - sigStart));
|
|
psSha1Final(&digestCtx.sha1, hsMsgHash);
|
|
# endif
|
|
}
|
|
else
|
|
{
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
# ifdef USE_MD5SHA1
|
|
psMd5Sha1PreInit(&digestCtx.md5sha1);
|
|
psMd5Sha1Init(&digestCtx.md5sha1);
|
|
psMd5Sha1Update(&digestCtx.md5sha1, ssl->sec.clientRandom,
|
|
SSL_HS_RANDOM_SIZE);
|
|
psMd5Sha1Update(&digestCtx.md5sha1, ssl->sec.serverRandom,
|
|
SSL_HS_RANDOM_SIZE);
|
|
psMd5Sha1Update(&digestCtx.md5sha1, sigStart,
|
|
(uint32) (sigStop - sigStart));
|
|
psMd5Sha1Final(&digestCtx.md5sha1, hsMsgHash);
|
|
# else /* USE_MD5SHA1 */
|
|
psTraceInfo("USE_MD5SHA1 required\n");
|
|
return MATRIXSSL_ERROR;
|
|
# endif /* USE_MD5SHA1 */
|
|
}
|
|
# else /* USE_TLS_1_2 */
|
|
/*
|
|
The signature portion is an MD5 and SHA1 concat of the randoms
|
|
and the contents of this server key exchange message.
|
|
*/
|
|
# ifdef USE_MD5SHA1
|
|
psMd5Sha1PreInit(&digestCtx.md5sha1);
|
|
psMd5Sha1Init(&digestCtx.md5sha1);
|
|
psMd5Sha1Update(&digestCtx.md5sha1, ssl->sec.clientRandom,
|
|
SSL_HS_RANDOM_SIZE);
|
|
psMd5Sha1Update(&digestCtx.md5sha1, ssl->sec.serverRandom,
|
|
SSL_HS_RANDOM_SIZE);
|
|
psMd5Sha1Update(&digestCtx.md5sha1, sigStart,
|
|
(uint32) (c - sigStart));
|
|
psMd5Sha1Final(&digestCtx.md5sha1, hsMsgHash);
|
|
# else
|
|
psTraceInfo("USE_MD5SHA1 required\n");
|
|
return MATRIXSSL_ERROR;
|
|
# endif /* USE_MD5SHA1 */
|
|
# endif /* USE_TLS_1_2 */
|
|
|
|
|
|
# ifdef USE_TLS_1_2
|
|
if (ssl->flags & SSL_FLAGS_TLS_1_2)
|
|
{
|
|
/* TLS 1.2 doesn't just sign the straight hash so we can't
|
|
pass it through the normal public decryption becuase
|
|
that expects an output length of a known size. These
|
|
signatures are done on elements with some ASN.1
|
|
wrapping so a special decryption with parse is needed */
|
|
if ((i = pubRsaDecryptSignedElement(skepkiPool,
|
|
&ssl->sec.cert->publicKey.key.rsa, c, pubDhLen, sigOut,
|
|
hashSize, pkiData)) < 0)
|
|
{
|
|
|
|
psTraceInfo("Can't decrypt serverKeyExchange sig\n");
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
hashSize = MD5_HASH_SIZE + SHA1_HASH_SIZE;
|
|
|
|
if ((i = psRsaDecryptPub(skepkiPool,
|
|
&ssl->sec.cert->publicKey.key.rsa, c, pubDhLen, sigOut,
|
|
hashSize, pkiData)) < 0)
|
|
{
|
|
psTraceInfo("Can't decrypt server key exchange sig\n");
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
# else /* ! USE_TLS_1_2 */
|
|
hashSize = MD5_HASH_SIZE + SHA1_HASH_SIZE;
|
|
if ((i = psRsaDecryptPub(skepkiPool, &ssl->sec.cert->publicKey.key.rsa,
|
|
c, pubDhLen, sigOut, hashSize, pkiData)) < 0)
|
|
{
|
|
psTraceInfo("Unable to decrypt server key exchange sig\n");
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# endif /* USE_TLS_1_2 */
|
|
|
|
/* Now have hash from the server. Create ours and check match */
|
|
c += pubDhLen;
|
|
|
|
if (memcmpct(sigOut, hsMsgHash, hashSize) != 0)
|
|
{
|
|
psTraceInfo("Fail to verify serverKeyExchange sig\n");
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
# endif /* USE_RSA_CIPHER_SUITE */
|
|
# ifdef USE_ECC_CIPHER_SUITE
|
|
if (ssl->flags & SSL_FLAGS_DHE_WITH_DSA)
|
|
{
|
|
/*
|
|
RFC4492: The default hash function is SHA-1, and sha_size is 20.
|
|
*/
|
|
# ifdef USE_TLS_1_2
|
|
if (ssl->flags & SSL_FLAGS_TLS_1_2 &&
|
|
(hashSize == SHA256_HASH_SIZE))
|
|
{
|
|
psSha256PreInit(&digestCtx.sha256);
|
|
psSha256Init(&digestCtx.sha256);
|
|
psSha256Update(&digestCtx.sha256, ssl->sec.clientRandom,
|
|
SSL_HS_RANDOM_SIZE);
|
|
psSha256Update(&digestCtx.sha256, ssl->sec.serverRandom,
|
|
SSL_HS_RANDOM_SIZE);
|
|
psSha256Update(&digestCtx.sha256, sigStart,
|
|
(int32) (sigStop - sigStart));
|
|
psSha256Final(&digestCtx.sha256, hsMsgHash);
|
|
# ifdef USE_SHA384
|
|
}
|
|
else if (ssl->flags & SSL_FLAGS_TLS_1_2 &&
|
|
(hashSize == SHA384_HASH_SIZE))
|
|
{
|
|
psSha384PreInit(&digestCtx.sha384);
|
|
psSha384Init(&digestCtx.sha384);
|
|
psSha384Update(&digestCtx.sha384, ssl->sec.clientRandom,
|
|
SSL_HS_RANDOM_SIZE);
|
|
psSha384Update(&digestCtx.sha384, ssl->sec.serverRandom,
|
|
SSL_HS_RANDOM_SIZE);
|
|
psSha384Update(&digestCtx.sha384, sigStart,
|
|
(int32) (sigStop - sigStart));
|
|
psSha384Final(&digestCtx.sha384, hsMsgHash);
|
|
# endif
|
|
# ifdef USE_SHA512
|
|
}
|
|
else if (hashSize == SHA512_HASH_SIZE)
|
|
{
|
|
psSha512PreInit(&digestCtx.sha512);
|
|
psSha512Init(&digestCtx.sha512);
|
|
psSha512Update(&digestCtx.sha512, ssl->sec.clientRandom,
|
|
SSL_HS_RANDOM_SIZE);
|
|
psSha512Update(&digestCtx.sha512, ssl->sec.serverRandom,
|
|
SSL_HS_RANDOM_SIZE);
|
|
psSha512Update(&digestCtx.sha512, sigStart,
|
|
(uint32) (sigStop - sigStart));
|
|
psSha512Final(&digestCtx.sha512, hsMsgHash);
|
|
# endif /* USE_SHA512 */
|
|
# ifdef USE_SHA1
|
|
}
|
|
else if (ssl->minVer < TLS_1_2_MIN_VER ||
|
|
# ifdef USE_DTLS
|
|
ssl->minVer == DTLS_MIN_VER ||
|
|
# endif
|
|
((ssl->flags & SSL_FLAGS_TLS_1_2) &&
|
|
(hashSize == SHA1_HASH_SIZE)))
|
|
{
|
|
hashSize = SHA1_HASH_SIZE;
|
|
psSha1PreInit(&digestCtx.sha1);
|
|
psSha1Init(&digestCtx.sha1);
|
|
psSha1Update(&digestCtx.sha1, ssl->sec.clientRandom,
|
|
SSL_HS_RANDOM_SIZE);
|
|
psSha1Update(&digestCtx.sha1, ssl->sec.serverRandom,
|
|
SSL_HS_RANDOM_SIZE);
|
|
psSha1Update(&digestCtx.sha1, sigStart,
|
|
(int32) (sigStop - sigStart));
|
|
psSha1Final(&digestCtx.sha1, hsMsgHash);
|
|
# endif
|
|
}
|
|
else
|
|
{
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# else /* USE_TLS_1_2 */
|
|
hashSize = SHA1_HASH_SIZE;
|
|
psSha1Init(&digestCtx.sha1);
|
|
psSha1Update(&digestCtx.sha1, ssl->sec.clientRandom,
|
|
SSL_HS_RANDOM_SIZE);
|
|
psSha1Update(&digestCtx.sha1, ssl->sec.serverRandom,
|
|
SSL_HS_RANDOM_SIZE);
|
|
psSha1Update(&digestCtx.sha1, sigStart,
|
|
(int32) (sigStop - sigStart));
|
|
psSha1Final(&digestCtx.sha1, hsMsgHash);
|
|
# endif /* USE_TLS_1_2 */
|
|
|
|
i = 0;
|
|
|
|
if ((res = psEccDsaVerify(skepkiPool,
|
|
&ssl->sec.cert->publicKey.key.ecc,
|
|
hsMsgHash, hashSize,
|
|
c, pubDhLen,
|
|
&i, pkiData)) != 0)
|
|
{
|
|
psTraceInfo("ECDSA signature validation failed\n");
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
c += pubDhLen;
|
|
/*
|
|
The validation code comes out of the final parameter
|
|
*/
|
|
if (i != 1)
|
|
{
|
|
psTraceInfo("Can't verify serverKeyExchange sig\n");
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
return MATRIXSSL_ERROR;
|
|
|
|
}
|
|
}
|
|
# endif /* USE_ECC_CIPHER_SUITE */
|
|
|
|
ssl->hsState = SSL_HS_SERVER_HELLO_DONE;
|
|
|
|
}
|
|
# endif /* USE_DHE_CIPHER_SUITE */
|
|
# ifdef USE_PSK_CIPHER_SUITE
|
|
/*
|
|
Entry point for basic PSK ciphers (not DHE or RSA) parsing SKE message
|
|
*/
|
|
if (ssl->flags & SSL_FLAGS_PSK_CIPHER)
|
|
{
|
|
if ((end - c) < 2)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid ServerKeyExchange message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
ssl->sec.hintLen = *c << 8; c++;
|
|
ssl->sec.hintLen |= *c; c++;
|
|
if ((uint32) (end - c) < ssl->sec.hintLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid ServerKeyExchange message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if (ssl->sec.hintLen > 0)
|
|
{
|
|
ssl->sec.hint = psMalloc(ssl->hsPool, ssl->sec.hintLen);
|
|
if (ssl->sec.hint == NULL)
|
|
{
|
|
return SSL_MEM_ERROR;
|
|
}
|
|
memcpy(ssl->sec.hint, c, ssl->sec.hintLen);
|
|
c += ssl->sec.hintLen;
|
|
}
|
|
ssl->hsState = SSL_HS_SERVER_HELLO_DONE;
|
|
}
|
|
# endif /* USE_PSK_CIPHER_SUITE */
|
|
|
|
*cp = c;
|
|
ssl->decState = SSL_HS_SERVER_KEY_EXCHANGE;
|
|
return PS_SUCCESS;
|
|
}
|
|
|
|
# ifdef USE_OCSP_RESPONSE
|
|
int32 parseCertificateStatus(ssl_t *ssl, int32 hsLen, unsigned char **cp,
|
|
unsigned char *end)
|
|
{
|
|
unsigned char *c;
|
|
int32_t responseLen, rc;
|
|
psOcspResponse_t response;
|
|
|
|
/*
|
|
struct {
|
|
CertificateStatusType status_type;
|
|
select (status_type) {
|
|
case ocsp: OCSPResponse;
|
|
} response;
|
|
} CertificateStatus;
|
|
|
|
enum { ocsp(1), (255) } CertificateStatusType;
|
|
opaque OCSPResponse<1..2^24-1>;
|
|
|
|
An "ocsp_response" contains a complete, DER-encoded OCSP response
|
|
(using the ASN.1 type OCSPResponse defined in [RFC6960]). Only one
|
|
OCSP response may be sent.
|
|
*/
|
|
c = *cp;
|
|
if ((end - c) < 4)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid CertificateStatus length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
if (*c != 0x1)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceInfo("Invalid status_type in certificateStatus message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
c++;
|
|
|
|
responseLen = *c << 16; c++;
|
|
responseLen |= *c << 8; c++;
|
|
responseLen |= *c; c++;
|
|
|
|
if (responseLen > (end - c))
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Malformed CertificateStatus message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
memset(&response, 0x0, sizeof(psOcspResponse_t));
|
|
if ((rc = psOcspParseResponse(ssl->hsPool, responseLen, &c, end, &response))
|
|
< 0)
|
|
{
|
|
/* Couldn't parse or no good responses in stream */
|
|
psX509FreeCert(response.OCSPResponseCert);
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE;
|
|
psTraceInfo("Unable to parse OCSPResponse\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
*cp = c;
|
|
|
|
/* Authenticate the parsed response based on the registered CA files
|
|
AND passing through the server chain as well because some real
|
|
world examples we have seen use the intermediate cert as the
|
|
OCSP responder */
|
|
if ((rc = psOcspResponseValidateOld(ssl->hsPool, ssl->keys->CAcerts,
|
|
ssl->sec.cert, &response)) < 0)
|
|
{
|
|
/* Couldn't validate */
|
|
psX509FreeCert(response.OCSPResponseCert);
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE;
|
|
psTraceInfo("Unable to validate OCSPResponse\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
psX509FreeCert(response.OCSPResponseCert);
|
|
|
|
/* Same logic to determine next state as in end of SSL_HS_CERTIFICATE */
|
|
ssl->hsState = SSL_HS_SERVER_HELLO_DONE;
|
|
# ifdef USE_DHE_CIPHER_SUITE
|
|
if (ssl->flags & SSL_FLAGS_DHE_KEY_EXCH)
|
|
{
|
|
ssl->hsState = SSL_HS_SERVER_KEY_EXCHANGE;
|
|
}
|
|
# endif /* USE_DHE_CIPHER_SUITE */
|
|
ssl->decState = SSL_HS_CERTIFICATE_STATUS;
|
|
return PS_SUCCESS;
|
|
}
|
|
# endif /* USE_OCSP_RESPONSE */
|
|
|
|
/******************************************************************************/
|
|
|
|
int32 parseServerHelloDone(ssl_t *ssl, int32 hsLen, unsigned char **cp,
|
|
unsigned char *end)
|
|
{
|
|
unsigned char *c;
|
|
|
|
# if defined(USE_DHE_CIPHER_SUITE) || defined(REQUIRE_DH_PARAMS)
|
|
int32 rc;
|
|
void *pkiData = ssl->userPtr;
|
|
|
|
# endif /* DH */
|
|
|
|
c = *cp;
|
|
|
|
psTraceHs(">>> Client parsing SERVER_HELLO_DONE message\n");
|
|
if (hsLen != 0)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceInfo("Invalid ServerHelloDone message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
# ifdef USE_DHE_CIPHER_SUITE
|
|
if (ssl->flags & SSL_FLAGS_DHE_KEY_EXCH)
|
|
{
|
|
# ifdef USE_ECC_CIPHER_SUITE
|
|
|
|
if (ssl->flags & SSL_FLAGS_ECC_CIPHER)
|
|
{
|
|
/* Set up our private side of the ECC key based on the agreed
|
|
upon curve */
|
|
if (psEccNewKey(ssl->sec.eccDhKeyPool, &ssl->sec.eccKeyPriv,
|
|
ssl->sec.eccKeyPub->curve) < 0)
|
|
{
|
|
return PS_MEM_FAIL;
|
|
}
|
|
if ((rc = matrixSslGenEphemeralEcKey(ssl->keys,
|
|
ssl->sec.eccKeyPriv, ssl->sec.eccKeyPub->curve,
|
|
pkiData)) < 0)
|
|
{
|
|
psEccDeleteKey(&ssl->sec.eccKeyPriv);
|
|
psTraceInfo("GenEphemeralEcc failed\n");
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
# endif
|
|
# ifdef REQUIRE_DH_PARAMS
|
|
/* Can safely set up our ssl->sec.dhKeyPriv with DH keys
|
|
based on the parameters passed over from the server.
|
|
Storing these in a client specific DH pool because at
|
|
handshake pool creation, the size for PKI was not known */
|
|
if ((ssl->sec.dhKeyPriv = psMalloc(ssl->sec.dhKeyPool,
|
|
sizeof(psDhKey_t))) == NULL)
|
|
{
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if ((rc = psDhGenKey(ssl->sec.dhKeyPool, ssl->sec.dhPLen,
|
|
ssl->sec.dhP, ssl->sec.dhPLen, ssl->sec.dhG,
|
|
ssl->sec.dhGLen, ssl->sec.dhKeyPriv, pkiData)) < 0)
|
|
{
|
|
psFree(ssl->sec.dhKeyPriv, ssl->sec.dhKeyPool);
|
|
ssl->sec.dhKeyPriv = NULL;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* Freeing as we go. No more need for G */
|
|
psFree(ssl->sec.dhG, ssl->hsPool); ssl->sec.dhG = NULL;
|
|
# endif /* REQUIRE_DH_PARAMS */
|
|
# ifdef USE_ECC_CIPHER_SUITE
|
|
}
|
|
# endif /* USE_ECC_CIPHER_SUITE */
|
|
}
|
|
# endif /* USE_DHE_CIPHER_SUITE */
|
|
|
|
ssl->hsState = SSL_HS_FINISHED;
|
|
|
|
*cp = c;
|
|
ssl->decState = SSL_HS_SERVER_HELLO_DONE;
|
|
return SSL_PROCESS_DATA;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
# ifndef USE_ONLY_PSK_CIPHER_SUITE
|
|
int32 parseCertificateRequest(ssl_t *ssl, int32 hsLen, unsigned char **cp,
|
|
unsigned char *end)
|
|
{
|
|
# ifdef USE_CLIENT_AUTH
|
|
psX509Cert_t *cert;
|
|
int32 i;
|
|
# endif
|
|
int32 certTypeLen, certChainLen;
|
|
uint32 certLen;
|
|
# ifdef USE_TLS_1_2
|
|
uint32 sigAlgMatch;
|
|
# endif
|
|
unsigned char *c;
|
|
|
|
c = *cp;
|
|
|
|
psTraceHs(">>> Client parsing CERTIFICATE_REQUEST message\n");
|
|
if (hsLen < 4)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceInfo("Invalid Certificate Request message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* Currently ignoring the authentication type request because it was
|
|
underspecified up to TLS 1.1 and TLS 1.2 is now taking care of this
|
|
with the supported_signature_algorithms handling */
|
|
certTypeLen = *c++;
|
|
if (end - c < certTypeLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid Certificate Request message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
c += certTypeLen; /* Skipping (RSA_SIGN etc.) */
|
|
# ifdef USE_TLS_1_2
|
|
sigAlgMatch = 0;
|
|
if (ssl->flags & SSL_FLAGS_TLS_1_2)
|
|
{
|
|
/* supported_signature_algorithms field
|
|
enum {none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5),
|
|
sha512(6), (255) } HashAlgorithm;
|
|
|
|
enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) } SigAlg */
|
|
if (end - c < 2)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid SigHash in Certificate Request message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
certChainLen = *c << 8; c++; /* just borrowing this variable */
|
|
certChainLen |= *c; c++;
|
|
if (end - c < certChainLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid SigHash in Certificate Request message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# ifdef USE_CLIENT_AUTH
|
|
/* Parse supported_signature_algorithms list. */
|
|
ssl->serverSigAlgs = 0;
|
|
while (certChainLen >= 2)
|
|
{
|
|
i = HASH_SIG_MASK(c[0], c[1]);
|
|
ssl->serverSigAlgs |= i;
|
|
c += 2;
|
|
certChainLen -= 2;
|
|
}
|
|
psTracePrintSigAlgs(ssl->serverSigAlgs, "Peer CertificateRequest");
|
|
|
|
/*
|
|
RFC 5246, section 7.4.4:
|
|
"Any certificates provided by the client MUST be signed using a
|
|
hash/signature algorithm pair found in
|
|
supported_signature_algorithms."
|
|
|
|
Check our certificate chain and set sigAlgMatch to 0 if we
|
|
find a cert whose sigAlg is not supported by the server.
|
|
sigAlgMatch==0 will result in us sending an empty Certificate
|
|
message later, as required by RFC 5246.
|
|
*/
|
|
sigAlgMatch = 1;
|
|
if (ssl->keys == NULL || ssl->keys->cert == NULL)
|
|
{
|
|
sigAlgMatch = 0;
|
|
}
|
|
else
|
|
{
|
|
cert = ssl->keys->cert;
|
|
while (cert)
|
|
{
|
|
if (!peerSupportsSigAlg(cert->sigAlgorithm,
|
|
ssl->serverSigAlgs))
|
|
{
|
|
sigAlgMatch = 0;
|
|
}
|
|
cert = cert->next;
|
|
}
|
|
}
|
|
# endif /* USE_CLIENT_AUTH */
|
|
c += certChainLen;
|
|
}
|
|
# endif /* TLS_1_2 */
|
|
|
|
certChainLen = 0;
|
|
if (end - c >= 2)
|
|
{
|
|
certChainLen = *c << 8; c++;
|
|
certChainLen |= *c; c++;
|
|
if (end - c < certChainLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid Certificate Request message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
/* Check the passed in DNs against our cert issuer to see if they match.
|
|
Only supporting a single cert on the client side. */
|
|
ssl->sec.certMatch = 0;
|
|
|
|
# ifdef USE_CLIENT_AUTH
|
|
/* If the user has actually gone to the trouble to load a certificate
|
|
to reply with, we flag that here so there is some flexibility as
|
|
to whether we want to reply with something (even if it doesn't match)
|
|
just in case the server is willing to do a custom test of the cert */
|
|
if (ssl->keys != NULL && ssl->keys->cert)
|
|
{
|
|
ssl->sec.certMatch = SSL_ALLOW_ANON_CONNECTION;
|
|
}
|
|
# endif /* USE_CLIENT_AUTH */
|
|
while (certChainLen > 2)
|
|
{
|
|
certLen = *c << 8; c++;
|
|
certLen |= *c; c++;
|
|
if ((uint32) (end - c) < certLen || certLen <= 0 ||
|
|
(int32) certLen > certChainLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid CertificateRequest message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
certChainLen -= 2;
|
|
# ifdef USE_CLIENT_AUTH
|
|
/* Can parse the message, but will not look for a match. The
|
|
setting of certMatch to 1 will trigger the correct response
|
|
in sslEncode */
|
|
if (ssl->keys != NULL && ssl->keys->cert)
|
|
{
|
|
/* Flag a match if the hash of the DN issuer is identical */
|
|
if (ssl->keys->cert->issuer.dnencLen == certLen)
|
|
{
|
|
if (memcmp(ssl->keys->cert->issuer.dnenc, c, certLen) == 0)
|
|
{
|
|
ssl->sec.certMatch = 1;
|
|
}
|
|
}
|
|
}
|
|
# endif /* USE_CLIENT_AUTH */
|
|
c += certLen;
|
|
certChainLen -= certLen;
|
|
}
|
|
# ifdef USE_TLS_1_2
|
|
if (ssl->flags & SSL_FLAGS_TLS_1_2)
|
|
{
|
|
/* We let the DN parse complete but if we didn't get a sigAlgMatch
|
|
from the previous test we're going to adhere to that for spec
|
|
compliance. So here goes */
|
|
if (sigAlgMatch == 0)
|
|
{
|
|
ssl->sec.certMatch = 0;
|
|
}
|
|
}
|
|
# endif
|
|
ssl->hsState = SSL_HS_SERVER_HELLO_DONE;
|
|
|
|
*cp = c;
|
|
ssl->decState = SSL_HS_CERTIFICATE_REQUEST;
|
|
return PS_SUCCESS;
|
|
}
|
|
# endif /* USE_ONLY_PSK_CIPHER_SUITE */
|
|
#endif /* USE_CLIENT_SIDE_SSL */
|
|
|
|
/******************************************************************************/
|
|
|
|
int32 parseFinished(ssl_t *ssl, int32 hsLen,
|
|
unsigned char hsMsgHash[SHA384_HASH_SIZE],
|
|
unsigned char **cp,
|
|
unsigned char *end)
|
|
{
|
|
int32 rc;
|
|
unsigned char *c;
|
|
|
|
rc = PS_SUCCESS;
|
|
c = *cp;
|
|
|
|
psAssert(hsLen <= SHA384_HASH_SIZE);
|
|
|
|
/* Before the finished handshake message, we should have seen the
|
|
CHANGE_CIPHER_SPEC message come through in the record layer, which
|
|
would have activated the read cipher, and set the READ_SECURE flag.
|
|
This is the first handshake message that was sent securely. */
|
|
psTraceStrHs(">>> %s parsing FINISHED message\n",
|
|
(ssl->flags & SSL_FLAGS_SERVER) ? "Server" : "Client");
|
|
if (!(ssl->flags & SSL_FLAGS_READ_SECURE))
|
|
{
|
|
ssl->err = SSL_ALERT_UNEXPECTED_MESSAGE;
|
|
psTraceInfo("Finished before ChangeCipherSpec\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* The contents of the finished message is a 16 byte MD5 hash followed
|
|
by a 20 byte sha1 hash of all the handshake messages so far, to verify
|
|
that nothing has been tampered with while we were still insecure.
|
|
Compare the message to the value we calculated at the beginning of
|
|
this function. */
|
|
#ifdef USE_TLS
|
|
if (ssl->flags & SSL_FLAGS_TLS)
|
|
{
|
|
if (hsLen != TLS_HS_FINISHED_SIZE)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid Finished length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#endif /* USE_TLS */
|
|
if (hsLen != MD5_HASH_SIZE + SHA1_HASH_SIZE)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid Finished length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
#ifdef USE_TLS
|
|
}
|
|
#endif /* USE_TLS */
|
|
if ((int32) (end - c) < hsLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid Finished length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if (memcmpct(c, hsMsgHash, hsLen) != 0)
|
|
{
|
|
ssl->err = SSL_ALERT_DECRYPT_ERROR;
|
|
psTraceInfo("Invalid handshake msg hash\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
#ifdef ENABLE_SECURE_REHANDSHAKES
|
|
/* Got the peer verify_data for secure renegotiations */
|
|
memcpy(ssl->peerVerifyData, c, hsLen);
|
|
ssl->peerVerifyDataLen = hsLen;
|
|
#endif /* ENABLE_SECURE_REHANDSHAKES */
|
|
c += hsLen;
|
|
ssl->hsState = SSL_HS_DONE;
|
|
/* Now that we've parsed the Finished message, if we're a resumed
|
|
connection, we're done with handshaking, otherwise, we return
|
|
SSL_PROCESS_DATA to get our own cipher spec and finished messages
|
|
sent out by the caller. */
|
|
if (ssl->flags & SSL_FLAGS_SERVER)
|
|
{
|
|
if (!(ssl->flags & SSL_FLAGS_RESUMED))
|
|
{
|
|
rc = SSL_PROCESS_DATA;
|
|
}
|
|
else
|
|
{
|
|
#ifdef ENABLE_SECURE_REHANDSHAKES
|
|
/* We're the server and we are doing a resumed (i.e. abbreviated)
|
|
handshake. The Finished message we just parsed was the final
|
|
handshake message. */
|
|
ssl->secureRenegotiationInProgress = PS_FALSE;
|
|
#endif
|
|
#ifdef USE_SSL_INFORMATIONAL_TRACE
|
|
/* Server side resumed completion */
|
|
matrixSslPrintHSDetails(ssl);
|
|
#endif
|
|
}
|
|
}
|
|
else /* We are the client. */
|
|
{
|
|
#ifdef USE_STATELESS_SESSION_TICKETS
|
|
/* Now that FINISHED is verified, we can mark the ticket as
|
|
valid to conform to section 3.3 of the 5077 RFC */
|
|
if (ssl->sid && ssl->sid->sessionTicketLen > 0)
|
|
{
|
|
ssl->sid->sessionTicketState = SESS_TICKET_STATE_USING_TICKET;
|
|
}
|
|
#endif
|
|
if (ssl->flags & SSL_FLAGS_RESUMED)
|
|
{
|
|
rc = SSL_PROCESS_DATA;
|
|
}
|
|
else
|
|
{
|
|
#ifdef ENABLE_SECURE_REHANDSHAKES
|
|
/* We are the client and were doing a full handshake.
|
|
The Finished message we just parsed was the final
|
|
handshake message. */
|
|
ssl->secureRenegotiationInProgress = PS_FALSE;
|
|
#endif
|
|
#ifdef USE_SSL_INFORMATIONAL_TRACE
|
|
/* Client side standard completion */
|
|
matrixSslPrintHSDetails(ssl);
|
|
#endif
|
|
}
|
|
}
|
|
#ifndef USE_ONLY_PSK_CIPHER_SUITE
|
|
# if defined(USE_CLIENT_SIDE_SSL) || defined(USE_CLIENT_AUTH)
|
|
/* There is also an attempt to free the cert during
|
|
the sending of the finished message to deal with client
|
|
and server and differing handshake types. Both cases are
|
|
attempted keep the lifespan of this allocation as short as possible. */
|
|
if (!(ssl->bFlags & BFLAG_KEEP_PEER_CERTS))
|
|
{
|
|
if (ssl->sec.cert)
|
|
{
|
|
psX509FreeCert(ssl->sec.cert);
|
|
ssl->sec.cert = NULL;
|
|
}
|
|
}
|
|
# endif /* USE_CLIENT_SIDE_SSL || USE_CLIENT_AUTH */
|
|
#endif /* !USE_ONLY_PSK_CIPHER_SUITE */
|
|
|
|
#ifdef USE_DTLS
|
|
if (ssl->flags & SSL_FLAGS_DTLS)
|
|
{
|
|
/* A successful parse of the FINISHED message means the record sequence
|
|
numbers have been reset so we need to clear out our replay detector */
|
|
zeroSixByte(ssl->lastRsn);
|
|
|
|
/* This will just be set between CCS parse and FINISHED parse */
|
|
ssl->parsedCCS = 1;
|
|
|
|
/* Look at the comment in the fragment parsing code to see the
|
|
justification of placing this free here. Bascially, this
|
|
is the best place to do it because we know there can be no
|
|
further fragmented messages. More importantly, the
|
|
hanshake pool is being freed here! */
|
|
if (ssl->fragMessage != NULL)
|
|
{
|
|
psFree(ssl->fragMessage, ssl->hsPool);
|
|
ssl->fragMessage = NULL;
|
|
}
|
|
}
|
|
/* Premaster was not freed at the usual spot becasue of retransmit cases */
|
|
if (ssl->sec.premaster)
|
|
{
|
|
psFree(ssl->sec.premaster, ssl->hsPool); ssl->sec.premaster = NULL;
|
|
}
|
|
if (ssl->ckeMsg)
|
|
{
|
|
psFree(ssl->ckeMsg, ssl->hsPool); ssl->ckeMsg = NULL;
|
|
}
|
|
if (ssl->certVerifyMsg)
|
|
{
|
|
psFree(ssl->certVerifyMsg, ssl->hsPool); ssl->certVerifyMsg = NULL;
|
|
}
|
|
# if defined(USE_PSK_CIPHER_SUITE) && defined(USE_CLIENT_SIDE_SSL)
|
|
if (ssl->sec.hint)
|
|
{
|
|
psFree(ssl->sec.hint, ssl->hsPool); ssl->sec.hint = NULL;
|
|
}
|
|
# endif
|
|
#endif /* USE_DTLS */
|
|
ssl->hsPool = NULL;
|
|
|
|
*cp = c;
|
|
ssl->decState = SSL_HS_FINISHED;
|
|
return rc;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
#ifndef USE_ONLY_PSK_CIPHER_SUITE
|
|
# if defined(USE_CLIENT_SIDE_SSL) || defined(USE_CLIENT_AUTH)
|
|
int32 parseCertificate(ssl_t *ssl, unsigned char **cp, unsigned char *end)
|
|
{
|
|
psX509Cert_t *currentCert, *cert, *foundIssuer;
|
|
unsigned char *c;
|
|
uint32 certLen;
|
|
int32 rc, i, certChainLen, parseLen = 0;
|
|
void *pkiData = ssl->userPtr;
|
|
int32 pathLen;
|
|
|
|
psTraceStrHs(">>> %s parsing CERTIFICATE message\n",
|
|
(ssl->flags & SSL_FLAGS_SERVER) ? "Server" : "Client");
|
|
|
|
c = *cp;
|
|
|
|
# ifdef USE_CERT_CHAIN_PARSING
|
|
if (ssl->rec.partial)
|
|
{
|
|
/* The test for a first pass is against the record header length */
|
|
if (ssl->rec.hsBytesParsed == ssl->recordHeadLen)
|
|
{
|
|
/* Account for the one-time header portion parsed above
|
|
and the 3 byte cert chain length about to be parsed below.
|
|
The minimum length tests have already been performed. */
|
|
ssl->rec.hsBytesParsed += ssl->hshakeHeadLen + 3;
|
|
}
|
|
else
|
|
{
|
|
goto SKIP_CERT_CHAIN_INIT;
|
|
}
|
|
}
|
|
# endif
|
|
if (end - c < 3)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid Certificate message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
certChainLen = *c << 16; c++;
|
|
certChainLen |= *c << 8; c++;
|
|
certChainLen |= *c; c++;
|
|
if (certChainLen < 3)
|
|
{
|
|
# ifdef SERVER_WILL_ACCEPT_EMPTY_CLIENT_CERT_MSG
|
|
if (ssl->flags & SSL_FLAGS_SERVER)
|
|
{
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
ssl->flags &= ~SSL_FLAGS_CLIENT_AUTH;
|
|
goto STRAIGHT_TO_USER_CALLBACK;
|
|
}
|
|
# endif
|
|
if (ssl->majVer == SSL3_MAJ_VER && ssl->minVer == SSL3_MIN_VER)
|
|
{
|
|
ssl->err = SSL_ALERT_NO_CERTIFICATE;
|
|
}
|
|
else
|
|
{
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
}
|
|
psTraceInfo("No certificate sent to verify\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if (end - c < 3)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid Certificate message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
# ifdef USE_CERT_CHAIN_PARSING
|
|
SKIP_CERT_CHAIN_INIT:
|
|
if (ssl->rec.partial)
|
|
{
|
|
/* It is possible to activate the CERT_STREAM_PARSE feature and not
|
|
receive a cert chain in multiple buffers. If we are not flagged
|
|
for 'partial' parsing, we can drop into the standard parse case */
|
|
while (end - c > 0)
|
|
{
|
|
certLen = *c << 16; c++;
|
|
certLen |= *c << 8; c++;
|
|
certLen |= *c; c++;
|
|
if ((parseLen = parseSingleCert(ssl, c, end, certLen)) < 0 )
|
|
{
|
|
return parseLen;
|
|
}
|
|
ssl->rec.hsBytesParsed += parseLen + 3; /* 3 for certLen */
|
|
c += parseLen;
|
|
}
|
|
if (ssl->rec.hsBytesParsed < ssl->rec.trueLen)
|
|
{
|
|
*cp = c;
|
|
return MATRIXSSL_SUCCESS;
|
|
}
|
|
|
|
psAssert(ssl->rec.hsBytesParsed == ssl->rec.trueLen);
|
|
/* Got it all. Disable the stream mechanism. */
|
|
ssl->rec.partial = 0x0;
|
|
ssl->rec.hsBytesParsed = 0;
|
|
ssl->rec.hsBytesHashed = 0;
|
|
}
|
|
else
|
|
{
|
|
psAssert(certChainLen > 0);
|
|
# endif /* USE_CERT_CHAIN_PARSING */
|
|
i = 0;
|
|
currentCert = NULL;
|
|
|
|
# if defined(USE_HARDWARE_CRYPTO_PKA) || defined(USE_EXT_CERTIFICATE_VERIFY_SIGNING)
|
|
/* Skip re-parsing the certs if pending. The above few bytes are fine */
|
|
if (ssl->hwflags & SSL_HWFLAGS_PENDING_PKA_R)
|
|
{
|
|
c += certChainLen;
|
|
ssl->hwflags &= ~SSL_HWFLAGS_PENDING_PKA_R;
|
|
goto RESUME_VALIDATE_CERTS;
|
|
}
|
|
# endif /* USE_HARDWARE_CRYPTO_PKA || USE_EXT_CERTIFICATE_VERIFY_SIGNING */
|
|
/* Chain must be at least 3 b certLen */
|
|
while (certChainLen >= 3)
|
|
{
|
|
int32 certFlags = 0;
|
|
|
|
certLen = *c << 16; c++;
|
|
certLen |= *c << 8; c++;
|
|
certLen |= *c; c++;
|
|
certChainLen -= 3;
|
|
|
|
if ((uint32) (end - c) < certLen || (int32) certLen > certChainLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceInfo("Invalid certificate length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if (ssl->bFlags & BFLAG_KEEP_PEER_CERT_DER)
|
|
{
|
|
certFlags |= CERT_STORE_UNPARSED_BUFFER;
|
|
}
|
|
/*
|
|
Extract the binary cert message into the cert structure
|
|
*/
|
|
if ((parseLen = psX509ParseCert(ssl->hsPool, c, certLen, &cert, certFlags))
|
|
< 0)
|
|
{
|
|
psTraceInfo("Parsing of the peer certificate failed\n");
|
|
psX509FreeCert(cert);
|
|
if (parseLen == PS_MEM_FAIL)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
}
|
|
else
|
|
{
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
}
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# ifdef ALLOW_VERSION_1_ROOT_CERT_PARSE
|
|
/* When ALLOW_VERSION_1_ROOT_CERT_PARSE is defined,
|
|
psX509ParseCert lets version 1 certificates through, in
|
|
order to support loading of locally trusted v1 root
|
|
certs. This means that we need to explicitly reject v1
|
|
certificates sent to us by the peer. They cannot be
|
|
trusted due to missing Basic Constraints, etc. */
|
|
if (cert->version != 2)
|
|
{
|
|
psTraceInfo("Version 1 peer certificates not allowed\n");
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
}
|
|
# endif /* ALLOW_VERSION_1_ROOT_CERT_PARSE */
|
|
c += parseLen;
|
|
|
|
if (i++ == 0)
|
|
{
|
|
ssl->sec.cert = cert;
|
|
currentCert = ssl->sec.cert;
|
|
}
|
|
else
|
|
{
|
|
currentCert->next = cert;
|
|
currentCert = currentCert->next;
|
|
}
|
|
certChainLen -= certLen;
|
|
}
|
|
# ifdef USE_CERT_CHAIN_PARSING
|
|
}
|
|
# endif /* USE_CERT_CHAIN_PARSING */
|
|
|
|
# ifdef USE_CLIENT_SIDE_SSL
|
|
/* Now want to test to see if supplied child-most cert is the appropriate
|
|
pubkey algorithm for the chosen cipher suite. Have seen test
|
|
cases with OpenSSL where an RSA cert will be sent for an ECDHE_ECDSA
|
|
suite, for example. Just testing on the client side because client
|
|
auth is a bit more flexible on the algorithm choices. */
|
|
if (!(ssl->flags & SSL_FLAGS_SERVER))
|
|
{
|
|
if (csCheckCertAgainstCipherSuite(ssl->sec.cert->publicKey.type,
|
|
ssl->cipher->type) == 0)
|
|
{
|
|
psTraceIntInfo("Server sent bad pubkey type for cipher suite %d\n",
|
|
ssl->cipher->type);
|
|
ssl->err = SSL_ALERT_UNSUPPORTED_CERTIFICATE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
# endif
|
|
|
|
/* Time to authenticate the supplied cert against our CAs */
|
|
# if defined(USE_HARDWARE_CRYPTO_PKA) || defined(USE_EXT_CERTIFICATE_VERIFY_SIGNING)
|
|
RESUME_VALIDATE_CERTS:
|
|
# endif /* USE_HARDWARE_CRYPTO_PKA || USE_EXT_CERTIFICATE_VERIFY_SIGNING */
|
|
|
|
rc = matrixValidateCertsExt(ssl->hsPool, ssl->sec.cert,
|
|
ssl->keys == NULL ? NULL : ssl->keys->CAcerts, ssl->expectedName,
|
|
&foundIssuer, pkiData, ssl->memAllocPtr, &ssl->validateCertsOpts);
|
|
|
|
if (rc == PS_MEM_FAIL)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/* Now walk the subject certs and convert any parse or authentication error
|
|
into an SSL alert. The alerts SHOULD be read by the user callback
|
|
to determine whether they are fatal or not. If no user callback,
|
|
the first alert will be considered fatal. */
|
|
cert = ssl->sec.cert;
|
|
pathLen = 0;
|
|
while (cert)
|
|
{
|
|
++pathLen;
|
|
if (ssl->validateCertsOpts.max_verify_depth > 0)
|
|
{
|
|
int exceeded = 0;
|
|
psTraceIntInfo("max_verify_depth: %d\n", ssl->validateCertsOpts.max_verify_depth);
|
|
/*
|
|
A maximum verification depth has been specified in session opts.
|
|
*/
|
|
if (pathLen > (ssl->validateCertsOpts.max_verify_depth))
|
|
{
|
|
exceeded = 1;
|
|
}
|
|
else if (pathLen == (ssl->validateCertsOpts.max_verify_depth))
|
|
{
|
|
/*
|
|
We don't have the root in cert->next. So do the
|
|
following: If the cert is _not_ self-signed, it must
|
|
have a valid root cert as the issuer, since this
|
|
is checked in matrixValidateCerts. Now take that root
|
|
into account when checking the path length.
|
|
*/
|
|
if (memcmpct(&cert->subject, &cert->issuer,
|
|
sizeof(cert->subject)))
|
|
{
|
|
/* Root cert causes depth to be exceeded. */
|
|
exceeded = 1;
|
|
}
|
|
}
|
|
if (exceeded)
|
|
{
|
|
/* Max depth exceeded. */
|
|
psTraceInfo("Error: max_verify_depth exceeded\n");
|
|
ssl->err = SSL_ALERT_UNKNOWN_CA;
|
|
cert->authStatus |= PS_CERT_AUTH_FAIL_PATH_LEN;
|
|
cert->authFailFlags |= PS_CERT_AUTH_FAIL_VERIFY_DEPTH_FLAG;
|
|
}
|
|
}
|
|
if (ssl->err != SSL_ALERT_NONE)
|
|
{
|
|
break; /* The first alert is the logical one to send */
|
|
}
|
|
switch (cert->authStatus)
|
|
{
|
|
case PS_CERT_AUTH_FAIL_SIG:
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
break;
|
|
case PS_CERT_AUTH_FAIL_REVOKED:
|
|
ssl->err = SSL_ALERT_CERTIFICATE_REVOKED;
|
|
break;
|
|
case PS_CERT_AUTH_FAIL_AUTHKEY:
|
|
case PS_CERT_AUTH_FAIL_PATH_LEN:
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
break;
|
|
case PS_CERT_AUTH_FAIL_EXTENSION:
|
|
/* The math and basic constraints matched. This case is
|
|
for X.509 extension mayhem */
|
|
if (cert->authFailFlags & PS_CERT_AUTH_FAIL_DATE_FLAG)
|
|
{
|
|
ssl->err = SSL_ALERT_CERTIFICATE_EXPIRED;
|
|
}
|
|
else if (cert->authFailFlags & PS_CERT_AUTH_FAIL_SUBJECT_FLAG)
|
|
{
|
|
/* expectedName was giving to NewSession but couldn't
|
|
match what the peer gave us */
|
|
ssl->err = SSL_ALERT_CERTIFICATE_UNKNOWN;
|
|
}
|
|
else if (cert->next != NULL)
|
|
{
|
|
/* This is an extension problem in the chain.
|
|
Even if it's minor, we are shutting it down */
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
}
|
|
else
|
|
{
|
|
/* This is the case where we did successfully find the
|
|
correct CA to validate the cert and the math passed
|
|
but the extensions had a problem. Give app a
|
|
different message in this case */
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
}
|
|
break;
|
|
case PS_CERT_AUTH_FAIL_BC:
|
|
case PS_CERT_AUTH_FAIL_DN:
|
|
/* These two are pre-math tests. If this was a problem in the
|
|
middle of the chain it means the chain couldn't even
|
|
validate itself. If it is at the end it means a matching
|
|
CA could not be found */
|
|
if (cert->next != NULL)
|
|
{
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
}
|
|
else
|
|
{
|
|
ssl->err = SSL_ALERT_UNKNOWN_CA;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
cert = cert->next;
|
|
}
|
|
|
|
/* The last thing we want to check before passing the certificates to
|
|
the user callback is the case in which we don't have any
|
|
CA files loaded but we were passed a valid chain that was
|
|
terminated with a self-signed cert. The fact that a CA on this
|
|
peer has not validated the chain should result in an UNKNOWN_CA alert
|
|
|
|
NOTE: This case should only ever get hit if VALIDATE_KEY_MATERIAL
|
|
has been disabled in matrixssllib.h */
|
|
|
|
if (ssl->err == SSL_ALERT_NONE &&
|
|
(ssl->keys == NULL || ssl->keys->CAcerts == NULL))
|
|
{
|
|
ssl->err = SSL_ALERT_UNKNOWN_CA;
|
|
psTraceInfo("WARNING: Valid self-signed cert or cert chain but no local authentication\n");
|
|
rc = -1; /* Force the check on existence of user callback */
|
|
}
|
|
|
|
if (rc < 0)
|
|
{
|
|
psTraceInfo("WARNING: cert did not pass internal validation test\n");
|
|
/* Cert auth failed. If there is no user callback issue fatal alert
|
|
because there will be no intervention to give it a second look. */
|
|
if (ssl->sec.validateCert == NULL)
|
|
{
|
|
/* ssl->err should have been set correctly above but catch
|
|
any missed cases with the generic BAD_CERTIFICATE alert */
|
|
if (ssl->err == SSL_ALERT_NONE)
|
|
{
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
}
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
|
|
# ifdef SERVER_WILL_ACCEPT_EMPTY_CLIENT_CERT_MSG
|
|
STRAIGHT_TO_USER_CALLBACK:
|
|
# endif
|
|
|
|
/* Return from user validation space with knowledge that there is a fatal
|
|
alert or that this is an ANONYMOUS connection. */
|
|
rc = matrixUserCertValidator(ssl, ssl->err, ssl->sec.cert,
|
|
ssl->sec.validateCert);
|
|
/* Test what the user callback returned. */
|
|
ssl->sec.anon = 0;
|
|
if (rc == SSL_ALLOW_ANON_CONNECTION)
|
|
{
|
|
ssl->sec.anon = 1;
|
|
}
|
|
else if (rc > 0)
|
|
{
|
|
/* User returned an alert. May or may not be the alert that was
|
|
determined above */
|
|
psTraceIntInfo("Certificate authentication alert %d\n", rc);
|
|
ssl->err = rc;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
else if (rc < 0)
|
|
{
|
|
psTraceIntInfo("User certificate callback had an internal error (rc=%d)\n", rc);
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
/* User callback returned 0 (continue on). Did they determine the alert
|
|
was not fatal after all? */
|
|
if (ssl->err != SSL_ALERT_NONE)
|
|
{
|
|
psTraceIntInfo("User certificate callback determined alert %d was NOT fatal\n",
|
|
ssl->err);
|
|
ssl->err = SSL_ALERT_NONE;
|
|
}
|
|
|
|
/* Either a client or server could have been processing the cert as part of
|
|
the authentication process. If server, we move to the client key
|
|
exchange state. */
|
|
if (ssl->flags & SSL_FLAGS_SERVER)
|
|
{
|
|
ssl->hsState = SSL_HS_CLIENT_KEY_EXCHANGE;
|
|
}
|
|
else
|
|
{
|
|
ssl->hsState = SSL_HS_SERVER_HELLO_DONE;
|
|
# ifdef USE_DHE_CIPHER_SUITE
|
|
if (ssl->flags & SSL_FLAGS_DHE_KEY_EXCH)
|
|
{
|
|
ssl->hsState = SSL_HS_SERVER_KEY_EXCHANGE;
|
|
}
|
|
# endif /* USE_DHE_CIPHER_SUITE */
|
|
# ifdef USE_OCSP_RESPONSE
|
|
/* State management for OCSP use. Testing if we received a
|
|
status_request from the server to set next expected state */
|
|
if (ssl->extFlags.status_request || ssl->extFlags.status_request_v2)
|
|
{
|
|
/* Why do they allow an ambiguous state here?! From RFC 6066:
|
|
|
|
Note that a server MAY also choose not to send a
|
|
"CertificateStatus" message, even if has received a
|
|
"status_request" extension in the client hello message and has
|
|
sent a "status_request" extension in the server hello message */
|
|
ssl->hsState = SSL_HS_CERTIFICATE_STATUS;
|
|
}
|
|
# endif /* USE_OCSP_RESPONSE */
|
|
}
|
|
*cp = c;
|
|
ssl->decState = SSL_HS_CERTIFICATE;
|
|
return MATRIXSSL_SUCCESS;
|
|
}
|
|
# endif /* USE_CLIENT_SIDE_SSL || USE_CLIENT_AUTH */
|
|
#endif /* !USE_ONLY_PSK_CIPHER_SUITE */
|
|
|
|
/******************************************************************************/
|
|
|