3124 lines
106 KiB
C
3124 lines
106 KiB
C
/**
|
|
* @file sslDecode.c
|
|
* @version $Format:%h%d$
|
|
*
|
|
* SSL/TLS protocol message decoding portion of MatrixSSL.
|
|
*/
|
|
/*
|
|
* Copyright (c) 2013-2018 Rambus Inc.
|
|
* Copyright (c) PeerSec Networks, 2002-2011
|
|
* All Rights Reserved
|
|
*
|
|
* The latest version of this code is available at http://www.matrixssl.org
|
|
*
|
|
* This software is open source; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This General Public License does NOT permit incorporating this software
|
|
* into proprietary programs. If you are unable to comply with the GPL, a
|
|
* commercial license for this software may be purchased from Rambus at
|
|
* http://www.rambus.com/
|
|
*
|
|
* This program is distributed in WITHOUT ANY WARRANTY; without even the
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
* See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
*/
|
|
/******************************************************************************/
|
|
|
|
#include "matrixsslImpl.h"
|
|
|
|
/******************************************************************************/
|
|
|
|
# define LUCKY13
|
|
|
|
#define SSL_MAX_IGNORED_MESSAGE_COUNT 1024
|
|
|
|
#ifndef USE_TLS_1_3_ONLY
|
|
static int32 parseSSLHandshake(ssl_t *ssl, char *inbuf, uint32 len);
|
|
static int32_t matrixSslDecodeTls12AndBelow(ssl_t *ssl,
|
|
unsigned char **buf, uint32 *len,
|
|
uint32 size,
|
|
uint32 *remaining,
|
|
uint32 *requiredLen,
|
|
int32 *error,
|
|
unsigned char *alertLevel,
|
|
unsigned char *alertDescription);
|
|
# ifdef LUCKY13
|
|
static int32 addCompressCount(ssl_t *ssl, int32 padLen);
|
|
# endif
|
|
#endif
|
|
|
|
#ifdef USE_CERT_CHAIN_PARSING
|
|
static int32 parseSingleCert(ssl_t *ssl, unsigned char *c, unsigned char *end,
|
|
int32 certLen);
|
|
#endif /* USE_CERT_CHAIN_PARSING */
|
|
|
|
static inline
|
|
psRes_t validateRecordHdrType(ssl_t *ssl)
|
|
{
|
|
switch (ssl->rec.type)
|
|
{
|
|
case SSL_RECORD_TYPE_CHANGE_CIPHER_SPEC:
|
|
case SSL_RECORD_TYPE_ALERT:
|
|
case SSL_RECORD_TYPE_HANDSHAKE:
|
|
case SSL_RECORD_TYPE_APPLICATION_DATA:
|
|
break;
|
|
/* Any other case is unrecognized */
|
|
default:
|
|
ssl->err = SSL_ALERT_UNEXPECTED_MESSAGE;
|
|
psTraceErrr("Invalid record header type\n");
|
|
psTraceIntInfo("Record header type not valid: %d\n", ssl->rec.type);
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
return MATRIXSSL_SUCCESS;
|
|
}
|
|
|
|
static inline
|
|
psRes_t validateRecordHdrVersion(ssl_t *ssl)
|
|
{
|
|
psProtocolVersion_t recordVer;
|
|
psBool_t ok = PS_TRUE;
|
|
|
|
recordVer = psVerFromEncodingMajMin(ssl->rec.majVer, ssl->rec.minVer);
|
|
if (recordVer == v_undefined)
|
|
{
|
|
psTraceErrr("Unrecognized record header version\n");
|
|
goto out_fail;
|
|
}
|
|
|
|
/* If we have negotiated a protocol version, check that the
|
|
record header version matches the version we have negotiated.
|
|
However, if we are using TLS 1.3, the record header version
|
|
number MUST be ignored. Also do not perform the check for
|
|
ClientHellos, as the these could be renegotation ClientHellos
|
|
or DTLS' 2nd ClientHellos, both of which require version
|
|
negotiation to be performed anew. */
|
|
if (ssl->hsState != SSL_HS_CLIENT_HELLO &&
|
|
VersionNegotiationComplete(ssl))
|
|
{
|
|
if (!NGTD_VER(ssl, v_tls_1_3_any) &&
|
|
!NGTD_VER(ssl, recordVer))
|
|
{
|
|
ok = PS_FALSE;
|
|
}
|
|
}
|
|
# ifdef USE_DTLS
|
|
else
|
|
{
|
|
if (recordVer & v_dtls_any)
|
|
{
|
|
if (!SUPP_VER(ssl, v_dtls_any))
|
|
{
|
|
psTraceErrr("Received a DTLS record, but DTLS not enabled\n");
|
|
goto out_fail;
|
|
}
|
|
/* Set this as the active version now, so that we are able to
|
|
decode using the correct format. Support for this version
|
|
will be checked later. */
|
|
if (!ACTV_VER(ssl, v_dtls_any))
|
|
{
|
|
SET_ACTV_VER(ssl, recordVer);
|
|
}
|
|
}
|
|
}
|
|
# endif
|
|
|
|
# ifdef USE_LENIENT_TLS_RECORD_VERSION_MATCHING
|
|
/* If using TLS, allow the record header to have any TLS version
|
|
for compatility. There have been e.g. some real world servers
|
|
that always encode TLS 1.1 in the record header, even after
|
|
TLS 1.2 has been chosen or negotiated. */
|
|
if (NGTD_VER(ssl, v_tls_any) && (recordVer & v_tls_any))
|
|
{
|
|
ok = PS_TRUE;
|
|
}
|
|
# endif
|
|
|
|
if (ok)
|
|
{
|
|
return MATRIXSSL_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
#ifdef SSL_REHANDSHAKES_ENABLED
|
|
/*
|
|
If in DONE state and this version doesn't match the previously
|
|
negotiated one that can be OK because a CLIENT_HELLO for a
|
|
rehandshake might be acting like a first time send and using
|
|
a lower version to get to the parsing phase. Unsupported
|
|
versions will be weeded out at CLIENT_HELLO parse time.
|
|
*/
|
|
if (ssl->hsState != SSL_HS_DONE ||
|
|
ssl->rec.type != SSL_RECORD_TYPE_HANDSHAKE)
|
|
{
|
|
goto out_fail_mismatch;
|
|
}
|
|
#else
|
|
goto out_fail_mismatch;
|
|
#endif
|
|
}
|
|
|
|
out_fail_mismatch:
|
|
psTraceErrr("Record header version does not match negotiated\n");
|
|
|
|
out_fail:
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTracePrintProtocolVersionNew(INDENT_ERROR,
|
|
"Unexpected version",
|
|
recordVer,
|
|
PS_TRUE);
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
static inline
|
|
psRes_t validateRecordHdrLen(ssl_t *ssl)
|
|
{
|
|
/*
|
|
Verify max and min record lengths
|
|
*/
|
|
if (ssl->rec.len > SSL_MAX_RECORD_LEN || ssl->rec.len == 0)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceErrr("Invalid record header length\n");
|
|
psTraceIntInfo("Record header length not valid: %d\n", ssl->rec.len);
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
return MATRIXSSL_SUCCESS;
|
|
}
|
|
|
|
# ifdef ALLOW_SSLV2_CLIENT_HELLO_PARSE
|
|
static inline
|
|
psBool_t isSslv2ClientHelloRecord(ssl_t *ssl,
|
|
unsigned char *c,
|
|
unsigned char *end)
|
|
{
|
|
/*
|
|
Conditions for accepting an SSL 2.0 record:
|
|
- A. It must be an SSL 2.0 ClientHello record.
|
|
- B. It must conform to E.2. of RFC 5246.
|
|
- C. We must be in the initial handshake state with no protocol
|
|
negotiated yet.
|
|
|
|
Note that (*c & 0x80) will never be true for TLS 1.0 or later,
|
|
because none of the valid record types has the high bit set.
|
|
*/
|
|
if ((end - c >= 3)
|
|
/* Conditions A & B: */
|
|
&& (*c & 0x80) && (*(c+2) == 1)
|
|
/* Condition C: */
|
|
&& ssl->hsState == SSL_HS_CLIENT_HELLO
|
|
&& !VersionNegotiationComplete(ssl))
|
|
{
|
|
return PS_TRUE;
|
|
}
|
|
|
|
return PS_FALSE;
|
|
}
|
|
|
|
static inline
|
|
psResSize_t handleSslv2Record(ssl_t *ssl,
|
|
unsigned char *c)
|
|
{
|
|
ssl->rec.type = SSL_RECORD_TYPE_HANDSHAKE;
|
|
ssl->rec.majVer = SSL2_MAJ_VER;
|
|
ssl->rec.minVer = 0;
|
|
ssl->rec.len = (*c & 0x7f) << 8; c++;
|
|
ssl->rec.len += *c;
|
|
|
|
return 2;
|
|
}
|
|
# endif /* ALLOW_SSLV2_CLIENT_HELLO_PARSE */
|
|
|
|
/** Parse and validate a record header.
|
|
Returns the number of bytes parsed or < 0 on error. */
|
|
static inline
|
|
psResSize_t handleRecordHdr(ssl_t *ssl,
|
|
unsigned char *c,
|
|
unsigned char *end,
|
|
uint32_t *requiredLen,
|
|
int32 *error)
|
|
{
|
|
unsigned char *orig_c = c;
|
|
psRes_t res;
|
|
|
|
# ifdef ALLOW_SSLV2_CLIENT_HELLO_PARSE
|
|
if (isSslv2ClientHelloRecord(ssl, c, end))
|
|
{
|
|
return handleSslv2Record(ssl, c);
|
|
}
|
|
# endif
|
|
|
|
psAssert(ssl->recordHeadLen == SSL3_HEADER_LEN ||
|
|
ssl->recordHeadLen == DTLS_HEADER_LEN);
|
|
|
|
if (end - c < ssl->recordHeadLen)
|
|
{
|
|
*requiredLen = ssl->recordHeadLen;
|
|
return SSL_PARTIAL;
|
|
}
|
|
|
|
/*
|
|
Parse and validate the record header. The type must be valid,
|
|
the major and minor versions must match the negotiated versions
|
|
(if we're past ClientHello) and the length must be < 16K and > 0
|
|
*/
|
|
ssl->rec.type = *c; c++;
|
|
res = validateRecordHdrType(ssl);
|
|
if (res != MATRIXSSL_SUCCESS)
|
|
{
|
|
return res;
|
|
}
|
|
|
|
ssl->rec.majVer = *c; c++;
|
|
ssl->rec.minVer = *c; c++;
|
|
res = validateRecordHdrVersion(ssl);
|
|
if (res != MATRIXSSL_SUCCESS)
|
|
{
|
|
return res;
|
|
}
|
|
|
|
#ifdef USE_DTLS
|
|
if (ACTV_VER(ssl, v_dtls_any))
|
|
{
|
|
ssl->rec.epoch[0] = *c++;
|
|
ssl->rec.epoch[1] = *c++;
|
|
ssl->rec.rsn[0] = *c++;
|
|
ssl->rec.rsn[1] = *c++;
|
|
ssl->rec.rsn[2] = *c++;
|
|
ssl->rec.rsn[3] = *c++;
|
|
ssl->rec.rsn[4] = *c++;
|
|
ssl->rec.rsn[5] = *c++;
|
|
}
|
|
#endif
|
|
|
|
ssl->rec.len = *c << 8; c++;
|
|
ssl->rec.len += *c++;
|
|
res = validateRecordHdrLen(ssl);
|
|
if (res != MATRIXSSL_SUCCESS)
|
|
{
|
|
return res;
|
|
}
|
|
|
|
return (c - orig_c);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
Parse incoming records.
|
|
|
|
Input parameters to decode:
|
|
. buf points to the start of data to decode
|
|
. len points to the length in bytes of data to decode
|
|
. size is the number of allocated bytes that follow buf
|
|
|
|
|
|
|
|
Meaningful parameters after the call to decode:
|
|
MATRIXSSL_SUCCESS
|
|
. buf will point to the first undecoded byte (could==inbuf or inbuf+inlen)
|
|
. remaining will indicate how many more bytes of undecoded data remain
|
|
* call again if more to decode or return if handshake is complete
|
|
|
|
SSL_PARTIAL
|
|
. buf will not have moved (because partials start parse over)
|
|
. reqLen will indicate how many bytes the entire full record is
|
|
* get more data from peer and call again
|
|
|
|
SSL_FULL (implies decode completed fully but couldn't fit response)
|
|
. buf will not have moved (it is reset to the front of final record)
|
|
. len will be 0 to indicate no remaining unprocessed data
|
|
. reqLen will inform how large buf should be grown before re-invoking
|
|
* realloc the buf to the reqLen size and call again
|
|
|
|
SSL_SEND_RESPONSE
|
|
. buf will point to the encoded handshake data to send
|
|
. len will be length of data to send (from start offset)
|
|
* pass the buf to the transport layer for sending to peer
|
|
|
|
SSL_ALERT
|
|
. buf will point to start of received alert (2 bytes alert level and desc)
|
|
. len will be length of alert data (should be 2)
|
|
. alertLevel will be 1 (warning) or 2 (fatal)
|
|
. alertDesc will be SSL specified alert code
|
|
|
|
MATRIXSSL_ERROR (unrecoverable failure)
|
|
. decodeErr is internal parse err code
|
|
|
|
SSL_PROCESS_DATA (ONLY CASE WITH DECRYPTED DATA AND POSSIBLE UNENCRYPTED)
|
|
. unencrypted user data ready for processing is at prevBuf
|
|
. buf points to start of any remaining unencrypted data
|
|
. remaining is length of remaining encrypted data yet to decode
|
|
. len is length of unencrypted data ready for user processing
|
|
* pass unencypted data to application level
|
|
* call decode again if more encrypted data remaining
|
|
|
|
*/
|
|
int32 matrixSslDecode(ssl_t *ssl,
|
|
unsigned char **buf,
|
|
uint32 *len,
|
|
uint32 size,
|
|
uint32 *remaining,
|
|
uint32 *requiredLen,
|
|
int32 *error,
|
|
unsigned char *alertLevel,
|
|
unsigned char *alertDescription)
|
|
{
|
|
*error = PS_SUCCESS;
|
|
|
|
/* If we've had a protocol error, don't allow further use of the session */
|
|
if (ssl->flags & SSL_FLAGS_ERROR || ssl->flags & SSL_FLAGS_CLOSED)
|
|
{
|
|
psTraceErrr("Can't use matrixSslDecode on closed/error-flagged sess\n");
|
|
*error = PS_PROTOCOL_FAIL;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
# ifdef USE_TLS_1_3
|
|
if (ACTV_VER(ssl, v_tls_1_3_any))
|
|
{
|
|
int32_t rc = PS_FAILURE;
|
|
rc = matrixSslDecodeTls13(ssl,
|
|
buf,
|
|
len,
|
|
size,
|
|
remaining,
|
|
requiredLen,
|
|
error,
|
|
alertLevel,
|
|
alertDescription);
|
|
if (rc != SSL_NO_TLS_1_3)
|
|
{
|
|
return rc;
|
|
}
|
|
psTraceInfo("TLS 1.3 not supported, falling back to legacy path\n");
|
|
}
|
|
# endif
|
|
|
|
# ifndef USE_TLS_1_3_ONLY
|
|
return matrixSslDecodeTls12AndBelow(ssl,
|
|
buf,
|
|
len,
|
|
size,
|
|
remaining,
|
|
requiredLen,
|
|
error,
|
|
alertLevel,
|
|
alertDescription);
|
|
# endif
|
|
|
|
return MATRIXSSL_SUCCESS;
|
|
}
|
|
|
|
# ifndef USE_TLS_1_3_ONLY
|
|
static
|
|
int32_t matrixSslDecodeTls12AndBelow(ssl_t *ssl,
|
|
unsigned char **buf, uint32 *len,
|
|
uint32 size,
|
|
uint32 *remaining,
|
|
uint32 *requiredLen,
|
|
int32 *error,
|
|
unsigned char *alertLevel,
|
|
unsigned char *alertDescription)
|
|
{
|
|
|
|
unsigned char *c, *p, *end, *pend, *decryptedStart, *origbuf;
|
|
unsigned char *mac;
|
|
unsigned char macError;
|
|
int32 rc;
|
|
unsigned char padLen;
|
|
|
|
# ifdef USE_CLIENT_SIDE_SSL
|
|
sslSessOpts_t options;
|
|
# endif
|
|
psBuf_t tmpout;
|
|
#ifdef USE_CERT_CHAIN_PARSING
|
|
int32 certlen, i, nextCertLen;
|
|
#endif /* USE_CERT_CHAIN_PARSING */
|
|
|
|
origbuf = *buf; /* Save the original buffer location */
|
|
|
|
p = pend = mac = decryptedStart = NULL;
|
|
padLen = 0;
|
|
|
|
#ifdef USE_EXT_CLIENT_CERT_KEY_LOADING
|
|
if (ssl->extClientCertKeyStateFlags ==
|
|
EXT_CLIENT_CERT_KEY_STATE_GOT_CERT_KEY_UPDATE)
|
|
{
|
|
/* Client program has loaded new client cert and keys based on
|
|
the server's CertificateRequest message. We have already parsed
|
|
the server's last flight entirely. Now skip directly to writing
|
|
the response. Reset extClientCertKey state. */
|
|
ssl->extClientCertKeyStateFlags = EXT_CLIENT_CERT_KEY_STATE_INIT;
|
|
goto encodeResponse;
|
|
}
|
|
#endif /* USE_EXT_CLIENT_CERT_KEY_LOADING */
|
|
|
|
# ifdef USE_EXT_CERTIFICATE_VERIFY_SIGNING
|
|
if (ssl->hwflags & SSL_HWFLAGS_PENDING_PKA_W ||
|
|
ssl->hwflags & SSL_HWFLAGS_PENDING_FLIGHT_W)
|
|
{
|
|
goto encodeResponse;
|
|
}
|
|
# endif /* USE_EXT_CERTIFICATE_VERIFY_SIGNING */
|
|
|
|
/*
|
|
This flag is set if the previous call to this routine returned an SSL_FULL
|
|
error from encodeResponse, indicating that there is data to be encoded,
|
|
but the out buffer was not big enough to handle it. If we fall in this
|
|
case, the user has increased the out buffer size and is re-calling this
|
|
routine
|
|
*/
|
|
if (ssl->flags & SSL_FLAGS_NEED_ENCODE)
|
|
{
|
|
ssl->flags &= ~SSL_FLAGS_NEED_ENCODE;
|
|
goto encodeResponse;
|
|
}
|
|
*requiredLen = 0;
|
|
c = *buf; /* c is record parse pointer */
|
|
end = *buf + *len;
|
|
|
|
/*
|
|
Processing the SSL Record header.
|
|
If the high bit of the first byte is set and this is the first
|
|
message we've seen, we parse the request as an SSLv2 request
|
|
@see http://wp.netscape.com/eng/security/SSL_2.html
|
|
SSLv2 also supports a 3 byte header when padding is used, but this should
|
|
not be required for the initial plaintext message, so we don't support it.
|
|
|
|
@security SSLV2 ClientHello is deprecated and no longer supported.
|
|
|
|
v2 Header:
|
|
2 bytes length (ignore high bit)
|
|
v3 Header:
|
|
1 byte type
|
|
1 byte major version
|
|
1 byte minor version
|
|
2 bytes length
|
|
*/
|
|
#ifdef USE_DTLS
|
|
decodeMore:
|
|
#endif
|
|
if (end - c == 0)
|
|
{
|
|
/*
|
|
This case could happen if change cipher spec was last
|
|
message in the buffer or if there is a zero-length record
|
|
at the end of a multi-record application data buffer.
|
|
*/
|
|
return MATRIXSSL_SUCCESS;
|
|
}
|
|
/* Even for SSLv2, we want at least 5 bytes in the record to continue */
|
|
if (end - c < SSL3_HEADER_LEN)
|
|
{
|
|
*requiredLen = SSL3_HEADER_LEN;
|
|
return SSL_PARTIAL;
|
|
}
|
|
#ifdef USE_CERT_CHAIN_PARSING
|
|
/*
|
|
If we're in process of parsing a partial record, then skip the
|
|
usual record header parse. Currently we're only supporting
|
|
partial parsing for the certificate messages since they are the
|
|
largest in size.
|
|
*/
|
|
if (ssl->rec.partial != 0x0)
|
|
{
|
|
psAssert(ssl->rec.type == SSL_RECORD_TYPE_HANDSHAKE);
|
|
psAssert(ssl->hsState == SSL_HS_CERTIFICATE);
|
|
/*
|
|
Get this next record length based on the certificate size,
|
|
which will always be the first three bytes of a partial here
|
|
*/
|
|
ssl->rec.len = c[0] << 16;
|
|
ssl->rec.len |= c[1] << 8;
|
|
ssl->rec.len |= c[2];
|
|
ssl->rec.len += 3;
|
|
goto SKIP_RECORD_PARSE;
|
|
}
|
|
#endif /* USE_CERT_CHAIN_PARSING */
|
|
|
|
/* Parse and validate the record header. */
|
|
rc = handleRecordHdr(ssl, c, end, requiredLen, error);
|
|
if (rc < 0)
|
|
{
|
|
if (ssl->err != SSL_ALERT_NONE)
|
|
{
|
|
goto encodeResponse;
|
|
}
|
|
return rc;
|
|
}
|
|
c += rc; /* handleRecordHdr returns number of bytes parsed. */
|
|
|
|
/*
|
|
This implementation requires the entire SSL record to be in the 'in' buffer
|
|
before we parse it. This is because we need to MAC the entire record before
|
|
allowing it to be used by the caller.
|
|
*/
|
|
#ifdef USE_CERT_CHAIN_PARSING
|
|
SKIP_RECORD_PARSE:
|
|
if ((end - c < ssl->rec.len) || ssl->rec.partial)
|
|
{
|
|
/*
|
|
This feature will only work if the CERTIFICATE message is sent in a
|
|
different record from the SERVER_HELLO message.
|
|
*/
|
|
if (ssl->hsState != SSL_HS_CERTIFICATE)
|
|
{
|
|
ssl->rec.partial = 0x0;
|
|
*requiredLen = ssl->rec.len + ssl->recordHeadLen;
|
|
return SSL_PARTIAL;
|
|
}
|
|
/*
|
|
Not supporting cert stream parsing for re-handshake. This is
|
|
important because the block cipher assumes a single pass is a record
|
|
and will use explicit IV each pass
|
|
*/
|
|
if (ssl->flags & SSL_FLAGS_READ_SECURE)
|
|
{
|
|
ssl->rec.partial = 0x0;
|
|
*requiredLen = ssl->rec.len + ssl->recordHeadLen;
|
|
return SSL_PARTIAL;
|
|
}
|
|
/*
|
|
Manipulate the rec.len for partial handling
|
|
*/
|
|
i = 0;
|
|
if (ssl->rec.partial == 0x0)
|
|
{
|
|
/*
|
|
Initialization for partial parse counters
|
|
*/
|
|
ssl->rec.hsBytesHashed = 0;
|
|
ssl->rec.hsBytesParsed = 0;
|
|
ssl->rec.partial = 0x1;
|
|
ssl->rec.trueLen = ssl->rec.len + ssl->recordHeadLen;
|
|
ssl->rec.len = 0;
|
|
/*
|
|
Best to identify and isolate full certificate boundaries
|
|
ASAP to keep parsing logic as high level as possible.
|
|
|
|
Current state of record buffer: pointer at start of HS record
|
|
which begins with 4 bytes of hsType(1) and hsLen(3). After
|
|
the header are 3 bytes of certchainlen and 3 bytes of first
|
|
cert len. Make sure we have at least one full cert here before
|
|
allowing the partial parse.
|
|
*/
|
|
if (end - c < (ssl->hshakeHeadLen + 6)) /* 3*2 cert chain len */
|
|
{
|
|
ssl->rec.partial = 0x0; /* Unusable. Reset */
|
|
*requiredLen = ssl->hshakeHeadLen + 6;
|
|
return SSL_PARTIAL;
|
|
}
|
|
ssl->rec.len += (ssl->hshakeHeadLen + 3);
|
|
i = ssl->hshakeHeadLen;
|
|
certlen = c[i] << 16; i++;
|
|
certlen |= c[i] << 8; i++;
|
|
certlen |= c[i]; i++;
|
|
/*
|
|
This feature only works if the CERTIFICATE message is the only
|
|
message in the record. Test this by seeing that trueLen doesn't
|
|
claim there is more to follow
|
|
*/
|
|
if (ssl->rec.trueLen != (certlen + 3 + ssl->hshakeHeadLen +
|
|
ssl->recordHeadLen))
|
|
{
|
|
ssl->rec.partial = 0x0; /* Unusable. Reset */
|
|
*requiredLen = ssl->rec.trueLen;
|
|
return SSL_PARTIAL;
|
|
}
|
|
/* First cert length */
|
|
ssl->rec.len += 3;
|
|
certlen = c[i] << 16; i++;
|
|
certlen |= c[i] << 8; i++;
|
|
certlen |= c[i];
|
|
ssl->rec.len += certlen;
|
|
}
|
|
|
|
/* One complete cert? */
|
|
if (end - c < ssl->rec.len)
|
|
{
|
|
/*
|
|
If there isn't a full cert in the first partial, we reset and
|
|
handle as the standard SSL_PARTIAL case.
|
|
*/
|
|
if (ssl->rec.hsBytesParsed == 0)
|
|
{
|
|
ssl->rec.partial = 0x0; /* Unusable. Reset */
|
|
*requiredLen = ssl->rec.len + ssl->recordHeadLen;
|
|
}
|
|
else
|
|
{
|
|
/* Record header has already been parsed */
|
|
*requiredLen = ssl->rec.len;
|
|
}
|
|
return SSL_PARTIAL; /* Standard partial case */
|
|
}
|
|
|
|
/* More than one complete cert? */
|
|
while (end - c > ssl->rec.len)
|
|
{
|
|
if (ssl->rec.len + ssl->rec.hsBytesParsed == ssl->rec.trueLen)
|
|
{
|
|
/*
|
|
Don't try to read another cert if the total of already parsed
|
|
record and the length of the current record match the 'trueLen'.
|
|
If they are equal, we know we are on the final cert and don't
|
|
need to look for more
|
|
*/
|
|
break;
|
|
}
|
|
psAssert(ssl->rec.len + ssl->rec.hsBytesParsed <= ssl->rec.trueLen);
|
|
nextCertLen = c[ssl->rec.len] << 16;
|
|
nextCertLen |= c[ssl->rec.len + 1] << 8;
|
|
nextCertLen |= c[ssl->rec.len + 2];
|
|
if (end - c > (ssl->rec.len + nextCertLen + 3))
|
|
{
|
|
ssl->rec.len += (nextCertLen + 3);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
if (end - c < ssl->rec.len)
|
|
{
|
|
# ifdef USE_DTLS
|
|
if (ACTV_VER(ssl, v_dtls_any))
|
|
{
|
|
psTraceErrr("DTLS error: Received PARTIAL record from peer.\n");
|
|
psTraceErrr("This indicates a PMTU mismatch\n");
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
goto encodeResponse;
|
|
}
|
|
# endif /* USE_DTLS */
|
|
*requiredLen = ssl->rec.len + ssl->recordHeadLen;
|
|
return SSL_PARTIAL;
|
|
|
|
}
|
|
#endif
|
|
|
|
#ifdef USE_DTLS
|
|
if (ACTV_VER(ssl, v_dtls_any))
|
|
{
|
|
|
|
/* Epoch and RSN validation. Silently ignore most mismatches (SUCCESS) */
|
|
rc = dtlsCompareEpoch(ssl->rec.epoch, ssl->expectedEpoch);
|
|
/* These cases have become pretty complex due to a code change in which
|
|
the epoch is always incremented when CCS is sent. We used to
|
|
reset the FINISHED epoch back to +1 of the current epoch when
|
|
resending the FINISHED flight but a customer had a problem with this
|
|
because they thought every single message must be unique for epoch
|
|
and sequence number. They were probably correct but now it's a
|
|
real mess trying to keep the expectedEpoch up-to-date when we can't
|
|
possibly know how many epoch increments the peer has made before we
|
|
receive a FINISHED message or an APPLICATION DATA record */
|
|
if (rc == 1
|
|
&& ssl->rec.type == SSL_RECORD_TYPE_HANDSHAKE
|
|
&& ssl->hsState == SSL_HS_FINISHED)
|
|
{
|
|
/* Special handlers for these CCS/Finished cases because epoch
|
|
could be larger for a good reason */
|
|
|
|
/* This is the case where we are getting a finished without having
|
|
seen a CCS. Preumably they will be trying again since this is
|
|
an indication that they are aware they are the senders */
|
|
if (ssl->parsedCCS == 0)
|
|
{
|
|
c += ssl->rec.len;
|
|
*buf = c;
|
|
return MATRIXSSL_SUCCESS;
|
|
}
|
|
|
|
/* This is the case where we didn't receive a combo CCS/FINISHED
|
|
flight from the peer and they have resent with a larger epoch
|
|
for the resent FINISHED message (so as not to send a epoch/seqNo
|
|
duplicate on this resend). Make the expected epoch the new one
|
|
to reset the test for any future rehandshakes */
|
|
ssl->expectedEpoch[0] = ssl->rec.epoch[0];
|
|
ssl->expectedEpoch[1] = ssl->rec.epoch[1];
|
|
}
|
|
else if (rc != 0)
|
|
{
|
|
psTraceIntDtls("Epoch mismatch %d ", ssl->rec.epoch[1]);
|
|
psTraceIntDtls("expected %d ", ssl->expectedEpoch[1]);
|
|
psTraceIntDtls("on a record type of %d", ssl->rec.type);
|
|
psTraceIntDtls("at state %d\n", ssl->hsState);
|
|
|
|
/* Another corner case where the peer has sent repeat FINISHED
|
|
messages when we are in the state where we are finished.
|
|
Need to keep the expectedEpoch up-to-date then because when
|
|
the peer finally gets around to sending application data it
|
|
will be sending it on the last epoch it sent for the final
|
|
FINISHED. */
|
|
if (rc == 1 && ssl->rec.type == SSL_RECORD_TYPE_HANDSHAKE &&
|
|
ssl->hsState == SSL_HS_DONE)
|
|
{
|
|
ssl->expectedEpoch[0] = ssl->rec.epoch[0];
|
|
ssl->expectedEpoch[1] = ssl->rec.epoch[1];
|
|
}
|
|
|
|
/* Yet another corner case where we are receiving application data
|
|
that has an epoch larger than we were expecting. This could
|
|
happen if the peer has been sending "duplicate" FINISHED
|
|
messages in which we have already parsed an earlier one and we
|
|
are in the done state. If we didn't receive those duplicate
|
|
FINISHED messages and are now getting an APPLICATION record,
|
|
let's just try to decrypt it and get this communication going */
|
|
if (rc == 1 && ssl->rec.type == SSL_RECORD_TYPE_APPLICATION_DATA &&
|
|
ssl->hsState == SSL_HS_DONE)
|
|
{
|
|
ssl->expectedEpoch[0] = ssl->rec.epoch[0];
|
|
ssl->expectedEpoch[1] = ssl->rec.epoch[1];
|
|
goto ADVANCE_TO_APP_DATA;
|
|
}
|
|
|
|
/* Now just skip the record as a duplicate */
|
|
|
|
c += ssl->rec.len;
|
|
*buf = c;
|
|
/* If this is a ChangeCipherSpec message from the peer
|
|
and we have never received encypted application data this is
|
|
probably the 'endgame' problem in which the peer never received
|
|
our final handshake flight. Trigger a resend in this specific
|
|
case */
|
|
if ((ssl->rec.type == SSL_RECORD_TYPE_CHANGE_CIPHER_SPEC) &&
|
|
(ssl->appDataExch == 0))
|
|
{
|
|
/* Need to make sure we mark the rest of this buffer as read.
|
|
The CCS message can be passed in here with the FINISHED tacked
|
|
on. OpenSSL sends them separately but most wouldn't */
|
|
if (end != c)
|
|
{
|
|
if (*c != SSL_RECORD_TYPE_HANDSHAKE)
|
|
{
|
|
/* Silently ignore packet. */
|
|
return MATRIXSSL_SUCCESS;
|
|
}
|
|
psAssert(*c == SSL_RECORD_TYPE_HANDSHAKE); /* Finished */
|
|
c += 11; /* Skip type, version, epoch to get to length */
|
|
/* borrow rc since we will be leaving here anyway */
|
|
rc = *c << 8; c++;
|
|
rc += *c; c++;
|
|
c += rc; /* Skip FINISHED message we've already accepted */
|
|
*buf = c;
|
|
}
|
|
return DTLS_RETRANSMIT;
|
|
}
|
|
if (end - c > 0)
|
|
{
|
|
goto decodeMore;
|
|
}
|
|
|
|
/* Next, check if this is a record on a session that the server
|
|
has already closed. Server timed out this client completely and then
|
|
the client decides to send a new encoded client hello or app data
|
|
on an epoch that it thinks is fine. If we are getting an epoch
|
|
greater than ours and we don't even have a state for this client,
|
|
an error should be returned so the ssl session can be deleted */
|
|
if (rc == 1 && ssl->flags & SSL_FLAGS_SERVER &&
|
|
ssl->hsState == SSL_HS_CLIENT_HELLO)
|
|
{
|
|
*buf = origbuf;
|
|
ssl->err = SSL_ALERT_UNEXPECTED_MESSAGE;
|
|
psTraceDtls("Client sending record on closed session\n");
|
|
goto encodeResponse;
|
|
}
|
|
|
|
/* If getting epoch that is less than expected, we'll resend */
|
|
if (rc == -1)
|
|
{
|
|
return DTLS_RETRANSMIT;
|
|
}
|
|
/* Got FINISHED message without ever getting a change cipher spec */
|
|
return MATRIXSSL_SUCCESS;
|
|
}
|
|
|
|
if (dtlsChkReplayWindow(ssl, ssl->rec.rsn) != 1)
|
|
{
|
|
psTraceIntDtls("Seen this record before %d\n", ssl->rec.rsn[5]);
|
|
c += ssl->rec.len;
|
|
*buf = c;
|
|
if (end - c > 0)
|
|
{
|
|
goto decodeMore;
|
|
}
|
|
return MATRIXSSL_SUCCESS;
|
|
}
|
|
}
|
|
ADVANCE_TO_APP_DATA:
|
|
#endif /* USE_DTLS */
|
|
|
|
#ifdef USE_MATRIXSSL_STATS
|
|
if (ssl->rec.type == SSL_RECORD_TYPE_APPLICATION_DATA)
|
|
{
|
|
matrixsslUpdateStat(ssl, APP_DATA_RECV_STAT, ssl->rec.len +
|
|
ssl->recordHeadLen);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
Decrypt the entire record contents. The record length should be
|
|
a multiple of block size, or decrypt will return an error
|
|
If we're still handshaking and sending plaintext, the decryption
|
|
callback will point to a null provider that passes the data unchanged
|
|
*/
|
|
|
|
decryptedStart = origbuf; /* Clear-text start. Decrypt to the front */
|
|
|
|
/* Sanity check ct len. Step 1 of Lucky 13 MEE-TLS-CBC decryption.
|
|
max{b, t + 1} is always "t + 1" because largest possible blocksize
|
|
is 16 and smallest possible tag len is 16. Multiple of block size test
|
|
is done in decrypt. We return the identical error as if the mac failed,
|
|
since this is a sanity check for pad and mac verification. */
|
|
if ((ssl->flags & SSL_FLAGS_READ_SECURE) && (ssl->deBlockSize > 1) &&
|
|
!(ssl->flags & SSL_FLAGS_AEAD_R))
|
|
{
|
|
#ifdef USE_TLS_1_1
|
|
if (ACTV_VER(ssl, v_tls_explicit_iv))
|
|
{
|
|
if (ssl->rec.len < (ssl->deMacSize + 1 + ssl->deBlockSize))
|
|
{
|
|
ssl->err = SSL_ALERT_BAD_RECORD_MAC;
|
|
psTraceErrr("Ciphertext length failed sanity\n");
|
|
goto encodeResponse;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ssl->rec.len < (ssl->deMacSize + 1))
|
|
{
|
|
ssl->err = SSL_ALERT_BAD_RECORD_MAC;
|
|
psTraceErrr("Ciphertext length failed sanity\n");
|
|
goto encodeResponse;
|
|
}
|
|
}
|
|
#else
|
|
if (ssl->rec.len < (ssl->deMacSize + 1))
|
|
{
|
|
ssl->err = SSL_ALERT_BAD_RECORD_MAC;
|
|
psTraceErrr("Ciphertext length failed sanity\n");
|
|
goto encodeResponse;
|
|
}
|
|
#endif /* USE_TLS_1_1 */
|
|
}
|
|
|
|
/*
|
|
Decrypt the record contents using the current cipher (may be NULL).
|
|
The caller of this function expects to find the decrypted data
|
|
at the start of the input buffer, where we currently have the
|
|
record header (5 bytes if TLS, 13 if DTLS).
|
|
|
|
For most cipher implementations, overlapping input and output
|
|
buffers are not a problem, but our current ChaCha20 implementation
|
|
requires decryption to be exactly in-situ in that case.
|
|
*/
|
|
# ifdef USE_CHACHA20_POLY1305_IETF_CIPHER_SUITE
|
|
if (DECRYPTING_WITH_CHACHA20(ssl))
|
|
{
|
|
decryptedStart = c;
|
|
}
|
|
# endif /* USE_CHACHA20_POLY1305_IETF_CIPHER_SUITE */
|
|
rc = ssl->decrypt(ssl, c, decryptedStart, ssl->rec.len);
|
|
if (rc < 0)
|
|
{
|
|
ssl->err = SSL_ALERT_DECRYPT_ERROR;
|
|
psTraceErrr("Couldn't decrypt record data 2\n");
|
|
goto encodeResponse;
|
|
}
|
|
# ifdef USE_CHACHA20_POLY1305_IETF_CIPHER_SUITE
|
|
if (DECRYPTING_WITH_CHACHA20(ssl) && decryptedStart > origbuf)
|
|
{
|
|
Memmove(origbuf, decryptedStart, ssl->rec.len);
|
|
decryptedStart = origbuf;
|
|
}
|
|
# endif /* USE_CHACHA20_POLY1305_IETF_CIPHER_SUITE */
|
|
|
|
c += ssl->rec.len;
|
|
|
|
if (ssl->flags & SSL_FLAGS_AEAD_R)
|
|
{
|
|
/* AEAD needs a bit of manual length manipulation for buffer mgmnt */
|
|
ssl->rec.len -= AEAD_TAG_LEN(ssl);
|
|
if (ssl->flags & SSL_FLAGS_NONCE_R)
|
|
{
|
|
ssl->rec.len -= TLS_EXPLICIT_NONCE_LEN;
|
|
}
|
|
}
|
|
/*
|
|
If we're reading a secure message, we need to validate the MAC and
|
|
padding (if using a block cipher). Insecure messages do not have
|
|
a trailing MAC or any padding.
|
|
|
|
SECURITY - There are several vulnerabilities in block cipher padding
|
|
that we handle in the below code. For more information see:
|
|
http://www.openssl.org/~bodo/tls-cbc.txt
|
|
*/
|
|
if (ssl->flags & SSL_FLAGS_READ_SECURE && !(ssl->flags & SSL_FLAGS_AEAD_R))
|
|
{
|
|
/*
|
|
Start tracking MAC errors, rather then immediately catching them to
|
|
stop timing and alert description attacks that differentiate between
|
|
a padding error and a MAC error.
|
|
*/
|
|
macError = 0;
|
|
/*
|
|
Decode padding only if blocksize is > 0 (we're using a block cipher),
|
|
otherwise no padding will be present, and the mac is the last
|
|
macSize bytes of the record.
|
|
*/
|
|
if (ssl->deBlockSize <= 1)
|
|
{
|
|
mac = decryptedStart + ssl->rec.len - ssl->deMacSize;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
The goal from here through completion of ssl->verifyMac call is a
|
|
constant processing time for a given record length. Going to
|
|
follow the suggestions of the Lucky 13 research paper section
|
|
"Careful implementation of MEE-TLS-CBC decryption".
|
|
http://www.isg.rhul.ac.uk/tls/TLStiming.pdf
|
|
|
|
Consistent timing is still a "goal" here. This implementation
|
|
accounts for the largest timing discrepencies but is not a
|
|
strict "clock cycles" equalizer. The complexity of the attack
|
|
circumstances and plaintext recovery possibilities using these
|
|
techniques is almost entirely in the academic realm. Improvements
|
|
to this code will be an ongoing process as research uncovers
|
|
more practical plaintext recovery threats.
|
|
|
|
Verify the pad data for block ciphers
|
|
c points within the cipher text, p points within the plaintext
|
|
The last byte of the record is the pad length
|
|
*/
|
|
p = decryptedStart + ssl->rec.len;
|
|
padLen = *(p - 1);
|
|
/*
|
|
SSL3.0 requires the pad length to be less than blockSize
|
|
TLS can have a pad length up to 255 for obfuscating the data len
|
|
*/
|
|
if (ACTV_VER(ssl, v_ssl_3_0) && padLen >= ssl->deBlockSize)
|
|
{
|
|
macError = 1;
|
|
}
|
|
/*
|
|
The minimum record length is the size of the mac, plus pad bytes
|
|
plus one length byte, plus explicit IV if TLS 1.1 or above
|
|
*/
|
|
if (ACTV_VER(ssl, v_tls_explicit_iv))
|
|
{
|
|
if (ssl->rec.len < ssl->deMacSize + padLen + 1 + ssl->deBlockSize)
|
|
{
|
|
macError = 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ssl->rec.len < ssl->deMacSize + padLen + 1)
|
|
{
|
|
macError = 3;
|
|
}
|
|
}
|
|
if (macError)
|
|
{
|
|
/* Step 3 of Lucky 13 MEE-TLS-CBC decryption: Run a loop as
|
|
if there were 256 bytes of padding, with a dummy check
|
|
in each iteration*/
|
|
for (rc = 255; rc >= 0; rc--)
|
|
{
|
|
/* make the test a moving target so it doesn't get
|
|
optimized out at compile. The loop is written
|
|
this way so the macError assignment will be done
|
|
only once */
|
|
if ((unsigned char) rc == padLen)
|
|
{
|
|
macError = 1; /* No incr to avoid any wraps */
|
|
}
|
|
}
|
|
}
|
|
#ifdef USE_TLS
|
|
/*
|
|
TLS specifies that all pad bytes must have the same value
|
|
as the final pad length byte. Some SSL3 implementations also
|
|
do this by convention, but some just fill with random bytes.
|
|
(We're just overloading the 'mac' ptr here, this has nothing to
|
|
do with real MAC.)
|
|
*/
|
|
if (!macError && !ACTV_VER(ssl, v_ssl_3_0))
|
|
{
|
|
for (mac = p - padLen - 1; mac < p; mac++)
|
|
{
|
|
if (*mac != padLen)
|
|
{
|
|
macError = 1;
|
|
}
|
|
}
|
|
/* Lucky 13 step 4. If this fails, then run a loop as if there
|
|
were 256 - padlen - 1 bytes of padding, with a dummy
|
|
check in each iteration */
|
|
if (macError)
|
|
{
|
|
for (rc = 256 - padLen - 1; rc > 0; rc--)
|
|
{
|
|
/* make the test a moving target so it doesn't get
|
|
optimized out at compile. Again, make it so
|
|
the loop condition doesn't get hit more than
|
|
once. */
|
|
if ((unsigned char) rc == padLen)
|
|
{
|
|
macError = 2; /* change value for smart compilers */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif /* USE_TLS */
|
|
/*
|
|
The mac starts macSize bytes before the padding and length byte.
|
|
If we have a macError, just fake the mac as the last macSize bytes
|
|
of the record, so we are sure to have enough bytes to verify
|
|
against, we'll fail anyway, so the actual contents don't matter.
|
|
*/
|
|
if (!macError)
|
|
{
|
|
/* No padding errors */
|
|
mac = p - padLen - 1 - ssl->deMacSize;
|
|
/* Lucky 13 step 5: Otherwise (the padding is now correctly
|
|
formatted) run a loop as if there were 256 - padlen - 1
|
|
bytes of padding, doing a dummy check in each iteration */
|
|
for (rc = (256 - padLen) - 1; rc > 0; rc--)
|
|
{
|
|
/* make this test look like the others */
|
|
if ((unsigned char) rc == padLen)
|
|
{
|
|
/* coverity[assigned_value] */
|
|
macError = 1; /* not really an error. reset below */
|
|
}
|
|
}
|
|
(void) macError; /* Suppress static analysis warnings */
|
|
macError = 0;
|
|
}
|
|
else
|
|
{
|
|
/* Lucky 13 step 3 and 4 condition: Then let P' denote the first
|
|
plen - t bytes of P, compute a MAC on SQN||HDR||P' and do a
|
|
constant-time comparison of the computed MAC with the
|
|
last t bytes of P. Return fatal error. */
|
|
mac = origbuf + ssl->rec.len - ssl->deMacSize;
|
|
}
|
|
}
|
|
/*
|
|
Verify the MAC of the message by calculating our own MAC of the message
|
|
and comparing it to the one in the message. We do this step regardless
|
|
of whether or not we've already set macError to stop timing attacks.
|
|
Clear the mac in the callers buffer if we're successful
|
|
*/
|
|
#ifdef USE_TLS_1_1
|
|
if (ACTV_VER(ssl, v_tls_explicit_iv) && (ssl->deBlockSize > 1))
|
|
{
|
|
decryptedStart += ssl->deBlockSize; /* skip explicit IV */
|
|
}
|
|
#endif
|
|
|
|
#ifdef LUCKY13
|
|
/*
|
|
Lucky 13 Step 5. If using a block cipher, blind the mac operation.
|
|
Doing this extra MAC compression here rather
|
|
than inside the real verify to keep this code patch at the
|
|
protocol level.
|
|
The Sha Update calls are with an exact state size for the
|
|
hash, so the compress function will be called 1:1 with the Update.
|
|
*/
|
|
if (ssl->deBlockSize > 1)
|
|
{
|
|
unsigned char tmp[128];
|
|
psDigestContext_t md;
|
|
|
|
/* set up the hash independent of the padding status */
|
|
switch (ssl->deMacSize)
|
|
{
|
|
# ifdef USE_SHA256
|
|
case SHA256_HASH_SIZE:
|
|
psSha256PreInit(&md.u.sha256);
|
|
psSha256Init(&md.u.sha256);
|
|
break;
|
|
# endif
|
|
# ifdef USE_SHA384
|
|
case SHA384_HASH_SIZE:
|
|
psSha384PreInit(&md.u.sha384);
|
|
psSha384Init(&md.u.sha384);
|
|
break;
|
|
# endif
|
|
# ifdef USE_SHA1
|
|
case SHA1_HASH_SIZE:
|
|
psSha1PreInit(&md.u.sha1);
|
|
psSha1Init(&md.u.sha1);
|
|
break;
|
|
# endif
|
|
default:
|
|
psAssert(0);
|
|
break;
|
|
}
|
|
|
|
rc = addCompressCount(ssl, padLen);
|
|
/* Perform a an update on the padding that is not cut off in case of a padding error */
|
|
if (macError == 0)
|
|
{
|
|
switch (ssl->deMacSize)
|
|
{
|
|
# ifdef USE_SHA256
|
|
case SHA256_HASH_SIZE:
|
|
while (rc > 0)
|
|
{
|
|
psSha256Update(&md.u.sha256, tmp, 64);
|
|
rc--;
|
|
}
|
|
break;
|
|
# endif
|
|
# ifdef USE_SHA384
|
|
case SHA384_HASH_SIZE:
|
|
while (rc > 0)
|
|
{
|
|
psSha384Update(&md.u.sha384, tmp, 128);
|
|
rc--;
|
|
}
|
|
break;
|
|
# endif
|
|
# ifdef USE_SHA1
|
|
case SHA1_HASH_SIZE:
|
|
while (rc > 0)
|
|
{
|
|
psSha1Update(&md.u.sha1, tmp, 64);
|
|
rc--;
|
|
}
|
|
break;
|
|
# endif
|
|
default:
|
|
psAssert(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Finish the hash independent of the padding status. Not necessary to thwart the timing side channel
|
|
but it could free the resources if necessary */
|
|
switch (ssl->deMacSize)
|
|
{
|
|
# ifdef USE_SHA256
|
|
case SHA256_HASH_SIZE:
|
|
psSha256Final(&md.u.sha256, tmp);
|
|
break;
|
|
# endif
|
|
# ifdef USE_SHA384
|
|
case SHA384_HASH_SIZE:
|
|
psSha384Final(&md.u.sha384, tmp);
|
|
break;
|
|
# endif
|
|
# ifdef USE_SHA1
|
|
case SHA1_HASH_SIZE:
|
|
psSha1Final(&md.u.sha1, tmp);
|
|
break;
|
|
# endif
|
|
default:
|
|
psAssert(0);
|
|
break;
|
|
}
|
|
|
|
}
|
|
#endif /* LUCKY13 */
|
|
|
|
if (ssl->verifyMac(ssl, ssl->rec.type, decryptedStart,
|
|
(uint32) (mac - decryptedStart), mac) < 0 || macError)
|
|
{
|
|
ssl->err = SSL_ALERT_BAD_RECORD_MAC;
|
|
psTraceErrr("Couldn't verify MAC or pad of record data\n");
|
|
goto encodeResponse;
|
|
}
|
|
|
|
Memset(mac, 0x0, ssl->deMacSize);
|
|
|
|
/* Record data starts at decryptedStart and ends at mac */
|
|
p = decryptedStart;
|
|
pend = mac;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
The record data is the entire record as there is no MAC or padding
|
|
*/
|
|
p = decryptedStart;
|
|
pend = mac = decryptedStart + ssl->rec.len;
|
|
}
|
|
|
|
/* Check now for maximum plaintext length of 16kb. */
|
|
if (ssl->maxPtFrag == 0xFF) /* Still negotiating size */
|
|
{
|
|
if ((int32) (pend - p) > SSL_MAX_PLAINTEXT_LEN)
|
|
{
|
|
ssl->err = SSL_ALERT_RECORD_OVERFLOW;
|
|
psTraceErrr("Record overflow\n");
|
|
goto encodeResponse;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((int32) (pend - p) > ssl->maxPtFrag)
|
|
{
|
|
ssl->err = SSL_ALERT_RECORD_OVERFLOW;
|
|
psTraceErrr("Record overflow\n");
|
|
goto encodeResponse;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Take action based on the actual record type we're dealing with
|
|
'p' points to the start of the data, and 'pend' points to the end
|
|
*/
|
|
switch (ssl->rec.type)
|
|
{
|
|
case SSL_RECORD_TYPE_CHANGE_CIPHER_SPEC:
|
|
psTracePrintChangeCipherSpecParse(ssl);
|
|
/*
|
|
Body is single byte with value 1 to indicate that the next message
|
|
will be encrypted using the negotiated cipher suite
|
|
*/
|
|
if (pend - p < 1)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceErrr("Invalid length for CipherSpec\n");
|
|
goto encodeResponse;
|
|
}
|
|
if (*p == 1)
|
|
{
|
|
p++;
|
|
}
|
|
else
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceErrr("Invalid value for CipherSpec\n");
|
|
goto encodeResponse;
|
|
}
|
|
|
|
#ifdef USE_DTLS
|
|
if (ACTV_VER(ssl, v_dtls_any))
|
|
{
|
|
if (ssl->hsState != SSL_HS_FINISHED)
|
|
{
|
|
/* Possible to get the changeCipherSpec message out of order */
|
|
psTraceIntInfo("Got out of order CCS: state %d\n", ssl->hsState);
|
|
*buf = c;
|
|
goto decodeMore;
|
|
}
|
|
/* The epoch corner cases surrounding the CHANGE_CIPHER_SPEC
|
|
message are complex. Let's just finally create a clear signal
|
|
that the CCS was parsed. The general problem is that our
|
|
state machine is FINISHED when expecting either the CCS or
|
|
the FINISHED message (probably goes back to CCS having some
|
|
special record type in the specs). This will just be set
|
|
between CCS parse and FINISHED parse */
|
|
ssl->parsedCCS = 1;
|
|
/*
|
|
Expect epoch to increment after successful CCS parse
|
|
*/
|
|
incrTwoByte(ssl, ssl->expectedEpoch, 0);
|
|
}
|
|
#endif /* USE_DTLS */
|
|
|
|
/*
|
|
If we're expecting finished, then this is the right place to get
|
|
this record. It is really part of the handshake but it has its
|
|
own record type.
|
|
Activate the read cipher callbacks, so we will decrypt incoming
|
|
data from now on.
|
|
*/
|
|
if (ssl->hsState == SSL_HS_FINISHED)
|
|
{
|
|
if (sslActivateReadCipher(ssl) < 0)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
goto encodeResponse;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef USE_STATELESS_SESSION_TICKETS
|
|
/* RFC 5077 allows the server to not acknowlege whether or not it
|
|
accepted our session ticket in the SERVER_HELLO extension so
|
|
there was no place prior to recieving this CCS to find out.
|
|
Different cipher suites types will be in different states */
|
|
if (ssl->hsState == SSL_HS_CERTIFICATE && ssl->sid &&
|
|
ssl->sid->sessionTicketState == SESS_TICKET_STATE_IN_LIMBO)
|
|
{
|
|
/* Do all the things that should have been done earlier */
|
|
ssl->flags |= SSL_FLAGS_RESUMED;
|
|
# ifdef USE_MATRIXSSL_STATS
|
|
matrixsslUpdateStat(ssl, RESUMPTIONS_STAT, 1);
|
|
# endif
|
|
if (sslCreateKeys(ssl) < 0)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
goto encodeResponse;
|
|
}
|
|
ssl->hsState = SSL_HS_FINISHED;
|
|
if (sslActivateReadCipher(ssl) < 0)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
goto encodeResponse;
|
|
}
|
|
ssl->sid->sessionTicketState = SESS_TICKET_STATE_INIT;
|
|
# ifdef USE_ANON_DH_CIPHER_SUITE
|
|
/* Anon DH could be in SERVER_KEY_EXCHANGE state */
|
|
}
|
|
else if ((ssl->flags & SSL_FLAGS_ANON_CIPHER) &&
|
|
(ssl->hsState == SSL_HS_SERVER_KEY_EXCHANGE) && ssl->sid &&
|
|
ssl->sid->sessionTicketState == SESS_TICKET_STATE_IN_LIMBO)
|
|
{
|
|
/* Do all the things that should have been done earlier */
|
|
ssl->flags |= SSL_FLAGS_RESUMED;
|
|
# ifdef USE_MATRIXSSL_STATS
|
|
matrixsslUpdateStat(ssl, RESUMPTIONS_STAT, 1);
|
|
# endif
|
|
if (sslCreateKeys(ssl) < 0)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
goto encodeResponse;
|
|
}
|
|
ssl->hsState = SSL_HS_FINISHED;
|
|
if (sslActivateReadCipher(ssl) < 0)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
goto encodeResponse;
|
|
}
|
|
ssl->sid->sessionTicketState = SESS_TICKET_STATE_INIT;
|
|
# endif /* USE_ANON_DH_CIPHER_SUITE */
|
|
# ifdef USE_PSK_CIPHER_SUITE
|
|
/* PSK could be in SERVER_KEY_EXCHANGE state */
|
|
}
|
|
else if ((ssl->flags & SSL_FLAGS_PSK_CIPHER) &&
|
|
(ssl->hsState == SSL_HS_SERVER_KEY_EXCHANGE) && ssl->sid &&
|
|
ssl->sid->sessionTicketState == SESS_TICKET_STATE_IN_LIMBO)
|
|
{
|
|
/* Do all the things that should have been done earlier */
|
|
ssl->flags |= SSL_FLAGS_RESUMED;
|
|
# ifdef USE_MATRIXSSL_STATS
|
|
matrixsslUpdateStat(ssl, RESUMPTIONS_STAT, 1);
|
|
# endif
|
|
if (sslCreateKeys(ssl) < 0)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
goto encodeResponse;
|
|
}
|
|
ssl->hsState = SSL_HS_FINISHED;
|
|
if (sslActivateReadCipher(ssl) < 0)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
goto encodeResponse;
|
|
}
|
|
ssl->sid->sessionTicketState = SESS_TICKET_STATE_INIT;
|
|
# endif /* USE_PSK_CIPHER_SUITE */
|
|
}
|
|
else
|
|
{
|
|
ssl->err = SSL_ALERT_UNEXPECTED_MESSAGE;
|
|
psTraceIntInfo("Invalid CipherSpec order: %d\n", ssl->hsState);
|
|
goto encodeResponse;
|
|
}
|
|
#else
|
|
ssl->err = SSL_ALERT_UNEXPECTED_MESSAGE;
|
|
psTraceIntInfo("Invalid CipherSpec order: %d\n", ssl->hsState);
|
|
goto encodeResponse;
|
|
#endif
|
|
}
|
|
ssl->decState = SSL_HS_CCC;
|
|
*remaining = *len - (c - origbuf);
|
|
*buf = c;
|
|
return MATRIXSSL_SUCCESS;
|
|
|
|
case SSL_RECORD_TYPE_ALERT:
|
|
/*
|
|
Decoded an alert
|
|
1 byte alert level (warning or fatal)
|
|
1 byte alert description corresponding to SSL_ALERT_*
|
|
*/
|
|
if (pend - p < 2)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Error in length of alert record\n");
|
|
goto encodeResponse;
|
|
}
|
|
*alertLevel = *p; p++;
|
|
*alertDescription = *p; p++;
|
|
*len = 2;
|
|
psTracePrintAlertReceiveInfo(ssl, *alertDescription);
|
|
|
|
/*
|
|
If the alert is fatal, or is a close message (usually a warning),
|
|
flag the session with ERROR so it cannot be used anymore.
|
|
Caller can decide whether or not to close on other warnings.
|
|
*/
|
|
if (*alertLevel == SSL_ALERT_LEVEL_FATAL)
|
|
{
|
|
ssl->flags |= SSL_FLAGS_ERROR;
|
|
}
|
|
if (*alertDescription == SSL_ALERT_CLOSE_NOTIFY)
|
|
{
|
|
ssl->flags |= SSL_FLAGS_CLOSED;
|
|
}
|
|
*buf = c;
|
|
ssl->decState = SSL_HS_ALERT;
|
|
return SSL_ALERT;
|
|
|
|
case SSL_RECORD_TYPE_HANDSHAKE:
|
|
/*
|
|
We've got one or more handshake messages in the record data.
|
|
The handshake parsing function will take care of all messages
|
|
and return an error if there is any problem.
|
|
If there is a response to be sent (either a return handshake
|
|
or an error alert, send it). If the message was parsed, but no
|
|
response is needed, loop up and try to parse another message
|
|
*/
|
|
#ifdef USE_CERT_CHAIN_PARSING
|
|
if (ssl->rec.partial)
|
|
{
|
|
if (ssl->rec.hsBytesParsed == 0)
|
|
{
|
|
/*
|
|
Account for the SSL record header for first pass
|
|
*/
|
|
ssl->rec.hsBytesParsed = ssl->recordHeadLen;
|
|
}
|
|
}
|
|
#endif
|
|
rc = parseSSLHandshake(ssl, (char *) p, (uint32) (pend - p));
|
|
/* If the entire fragment is present, the parse has occured */
|
|
if (ssl->fragMessage != NULL)
|
|
{
|
|
if (ssl->fragIndex == ssl->fragTotal)
|
|
{
|
|
psFree(ssl->fragMessage, ssl->hsPool);
|
|
ssl->fragMessage = NULL;
|
|
ssl->fragIndex = ssl->fragTotal = 0;
|
|
}
|
|
}
|
|
switch (rc)
|
|
{
|
|
case MATRIXSSL_SUCCESS:
|
|
*remaining = *len - (c - origbuf);
|
|
*buf = c;
|
|
return MATRIXSSL_SUCCESS;
|
|
#ifdef USE_EXT_CLIENT_CERT_KEY_LOADING
|
|
case PS_PENDING:
|
|
if (matrixSslNeedClientCert(ssl))
|
|
{
|
|
/*
|
|
Do not create the response flight just yet. Instead,
|
|
return to the client application to give it a chance
|
|
to load a new client cert and key if desired.
|
|
*/
|
|
psTraceInfo("matrixSslDecode returning PS_PENDING\n");
|
|
return PS_PENDING;
|
|
}
|
|
break;
|
|
#endif /* USE_EXT_CLIENT_CERT_KEY_LOADING */
|
|
#ifdef USE_DTLS
|
|
case DTLS_RETRANSMIT:
|
|
/* The idea here is to only return retransmit if
|
|
we are seeing the final message in the inbuf as repeat. Otherwise
|
|
the next msg right in this flight might be able to move our state
|
|
forward without a resend. */
|
|
*remaining = *len - (c - origbuf);
|
|
*buf = c;
|
|
if (*remaining == 0)
|
|
{
|
|
return DTLS_RETRANSMIT;
|
|
}
|
|
else
|
|
{
|
|
return MATRIXSSL_SUCCESS;
|
|
}
|
|
break;
|
|
#endif /* USE_DTLS */
|
|
|
|
case SSL_PROCESS_DATA:
|
|
/*
|
|
We're here when we've processed an SSL header that requires
|
|
a response. In all cases (except FALSE START), we would not
|
|
expect to have any data remaining in the incoming buffer, since
|
|
the peer would be waiting for our response.
|
|
*/
|
|
#if defined(ENABLE_FALSE_START)
|
|
if (c < origbuf + *len)
|
|
{
|
|
/*
|
|
If there's still incoming data in the buffer, it could be
|
|
FALSE START app data immediately after the FINISHED message,
|
|
and before we've had a chance to encode and send our
|
|
CHANGE_CIPHER_SPEC and FINISHED message. We hack around
|
|
some values to support this case.
|
|
http://tools.ietf.org/html/draft-bmoeller-tls-falsestart-00
|
|
*/
|
|
if (*c == SSL_RECORD_TYPE_APPLICATION_DATA &&
|
|
ssl->hsState == SSL_HS_DONE &&
|
|
(ssl->flags & SSL_FLAGS_SERVER))
|
|
{
|
|
psTraceInfo(">>> Server buffering FALSE START APPLICATION_DATA\n");
|
|
ssl->flags |= SSL_FLAGS_FALSE_START;
|
|
*remaining = *len - (c - origbuf);
|
|
*buf = c;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
Implies successful parse of supposed last message in
|
|
flight so check for the corner cases and reset the
|
|
buffer to start to write response
|
|
*/
|
|
#endif
|
|
if (*c == SSL_RECORD_TYPE_APPLICATION_DATA &&
|
|
ssl->hsState == SSL_HS_DONE &&
|
|
(ssl->flags & SSL_FLAGS_SERVER))
|
|
{
|
|
/* If this asserts, try defining ENABLE_FALSE_START */
|
|
psAssert(origbuf + *len == c);
|
|
*buf = origbuf;
|
|
}
|
|
else if (*c == SSL_RECORD_TYPE_APPLICATION_DATA &&
|
|
ssl->hsState == SSL_HS_HELLO_REQUEST &&
|
|
(c < (origbuf + *len)))
|
|
{
|
|
/* message tacked on to end of HELLO_REQUEST. Very
|
|
complicated scenario for the state machine and
|
|
API so we're going to ignore the HELLO_REQUEST
|
|
(fine by the specification) and give precedence to
|
|
the app data. This backup flag data was set aside
|
|
in sslResetContext when the HELLO_REQUEST was
|
|
received */
|
|
*buf = c;
|
|
# ifdef USE_CLIENT_SIDE_SSL
|
|
ssl->sec.anon = ssl->anonBk;
|
|
ssl->flags = ssl->flagsBk;
|
|
ssl->bFlags = ssl->bFlagsBk;
|
|
# endif
|
|
ssl->hsState = SSL_HS_DONE;
|
|
return MATRIXSSL_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
/* If this asserts, please report the values of the
|
|
* c byte and ssl->hsState to support */
|
|
psAssert(origbuf + *len == c);
|
|
*buf = origbuf;
|
|
}
|
|
#if defined(ENABLE_FALSE_START)
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*buf = origbuf;
|
|
}
|
|
#endif
|
|
goto encodeResponse;
|
|
|
|
case MATRIXSSL_ERROR:
|
|
case SSL_MEM_ERROR:
|
|
if (ssl->err == SSL_ALERT_NONE)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
}
|
|
goto encodeResponse;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
psTraceIntInfo("Unknown return %d from parseSSLHandshake!\n", rc);
|
|
if (ssl->err == SSL_ALERT_NONE)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
}
|
|
goto encodeResponse;
|
|
|
|
case SSL_RECORD_TYPE_APPLICATION_DATA:
|
|
|
|
/*
|
|
Data is in the out buffer, let user handle it
|
|
Don't allow application data until handshake is complete, and we are
|
|
secure. It is ok to let application data through on the client
|
|
if we are in the SERVER_HELLO state because this could mean that
|
|
the client has sent a CLIENT_HELLO message for a rehandshake
|
|
and is awaiting reply.
|
|
*/
|
|
if ((ssl->hsState != SSL_HS_DONE && ssl->hsState != SSL_HS_SERVER_HELLO)
|
|
|| !(ssl->flags & SSL_FLAGS_READ_SECURE))
|
|
{
|
|
ssl->err = SSL_ALERT_UNEXPECTED_MESSAGE;
|
|
psTraceIntInfo("Incomplete handshake: %d\n", ssl->hsState);
|
|
goto encodeResponse;
|
|
}
|
|
/*
|
|
Insitu for application data is more tricky than it is for SSL handshake
|
|
messages. This is because there is never going to be any 'out' data
|
|
for handshake messages until the final record of a flight is parsed.
|
|
Whereas application data necessarily has an 'out' for every 'in'
|
|
record because it is the decrypted data of the 'in'. So, the managed
|
|
cases result anytime there is more than 1 app record in the 'in' buffer
|
|
where the insitu must hold BOTH a decrypted buffer and the next
|
|
encrypted record.
|
|
|
|
Create so that:
|
|
. buf points to start of any remaining unencrypted data
|
|
. start is length of remaining encrypted data yet to decode
|
|
. len is length of unencrypted data ready for user processing
|
|
|
|
*/
|
|
*buf = c;
|
|
*remaining = *len - (c - origbuf);
|
|
*len = mac - origbuf;
|
|
/*
|
|
SECURITY - If the mac is at the current out->end, then there is no data
|
|
in the record. These records are valid, but are usually not sent by
|
|
the application layer protocol. Rather, they are initiated within the
|
|
remote SSL protocol implementation to avoid some types of attacks when
|
|
using block ciphers. For more information see:
|
|
http://www.openssl.org/~bodo/tls-cbc.txt
|
|
|
|
SECURITY - Returning blank messages has the potential
|
|
for denial of service, because we are not changing the state of the
|
|
system in any way when processing these messages, (although the upper
|
|
level protocol may). To counteract this, we maintain a counter
|
|
that we share with other types of ignored messages. If too many in a
|
|
row occur, an alert will be sent and the connection closed.
|
|
We implement this as a leaky bucket, so if a non-blank message comes
|
|
in, the ignored message count is decremented, ensuring that we only
|
|
error on a large number of consecutive blanks.
|
|
*/
|
|
if (decryptedStart == mac)
|
|
{
|
|
if (ssl->ignoredMessageCount++ >= SSL_MAX_IGNORED_MESSAGE_COUNT)
|
|
{
|
|
ssl->err = SSL_ALERT_UNEXPECTED_MESSAGE;
|
|
psTraceIntInfo("Exceeded limit on ignored messages: %d\n",
|
|
SSL_MAX_IGNORED_MESSAGE_COUNT);
|
|
goto encodeResponse;
|
|
}
|
|
}
|
|
else if (ssl->ignoredMessageCount > 0)
|
|
{
|
|
ssl->ignoredMessageCount--;
|
|
}
|
|
#ifdef USE_MATRIXSSL_STATS
|
|
matrixsslUpdateStat(ssl, STAT_PT_DATA_RECV, *len);
|
|
#endif
|
|
ssl->decState = SSL_HS_DONE;
|
|
return SSL_PROCESS_DATA;
|
|
|
|
default:
|
|
/* Falls to error below */
|
|
break;
|
|
}
|
|
/*
|
|
Should not get here under normal operation
|
|
*/
|
|
psTraceIntInfo("Invalid record type in matrixSslDecode: %d\n",
|
|
ssl->rec.type);
|
|
*error = PS_PROTOCOL_FAIL;
|
|
return MATRIXSSL_ERROR;
|
|
|
|
encodeResponse:
|
|
/*
|
|
We decoded a record that needs a response, either a handshake response
|
|
or an alert if we've detected an error.
|
|
*/
|
|
# ifdef ENABLE_FALSE_START
|
|
if ((ssl->flags & SSL_FLAGS_FALSE_START) && *buf != origbuf)
|
|
{
|
|
/*
|
|
Encode the output into ssl->outbuf in this case, rather than back
|
|
into origbuf, since there is still valid data in origbuf that
|
|
needs to be decoded later.
|
|
Other places in this function we do not reference the ssl inbuf
|
|
or outbuf directly, but this was the cleanest way for this hack.
|
|
Caller must test to see if *buf has been modified if
|
|
ssl->flags & SSL_FLAGS_FALSE_START
|
|
*/
|
|
tmpout.buf = tmpout.start = tmpout.end = ssl->outbuf + ssl->outlen;
|
|
tmpout.size = ssl->outsize - ssl->outlen;
|
|
Memset(origbuf, 0x0, (*buf - origbuf)); /* SECURITY (see below) */
|
|
}
|
|
else
|
|
# endif
|
|
{
|
|
psAssert(origbuf == *buf);
|
|
tmpout.buf = tmpout.end = tmpout.start = origbuf;
|
|
tmpout.size = size;
|
|
|
|
# if defined(USE_HARDWARE_CRYPTO_RECORD) || defined(USE_HARDWARE_CRYPTO_PKA) || defined(USE_EXT_CERTIFICATE_VERIFY_SIGNING)
|
|
if (!(ssl->hwflags & SSL_HWFLAGS_PENDING_PKA_W) &&
|
|
!(ssl->hwflags & SSL_HWFLAGS_PENDING_FLIGHT_W))
|
|
{
|
|
/* If we are coming back through on a pending, this data is GOLD */
|
|
Memset(tmpout.buf, 0x0, tmpout.size);
|
|
}
|
|
# else
|
|
/*
|
|
SECURITY - Clear the decoded incoming record from outbuf before encoding
|
|
the response into outbuf.
|
|
*/
|
|
Memset(tmpout.buf, 0x0, tmpout.size);
|
|
# endif
|
|
}
|
|
|
|
# ifdef USE_CLIENT_SIDE_SSL
|
|
if (ssl->hsState == SSL_HS_HELLO_REQUEST)
|
|
{
|
|
Memset(&options, 0x0, sizeof(sslSessOpts_t));
|
|
/*
|
|
Don't clear the session info. If receiving a HELLO_REQUEST from a
|
|
MatrixSSL enabled server the determination on whether to reuse the
|
|
session is made on that side, so always send the current session
|
|
Re-send the backed up user extensions (if any). TODO: test this.
|
|
*/
|
|
rc = matrixSslEncodeClientHello(ssl,
|
|
&tmpout,
|
|
# ifdef ENABLE_SECURE_REHANDSHAKES
|
|
ssl->tlsClientCipherSuites,
|
|
ssl->tlsClientCipherSuitesLen,
|
|
# else
|
|
0,
|
|
0,
|
|
# endif
|
|
requiredLen,
|
|
ssl->userExt,
|
|
&options);
|
|
}
|
|
else
|
|
{
|
|
# endif /* USE_CLIENT_SIDE_SSL */
|
|
rc = sslEncodeResponse(ssl, &tmpout, requiredLen);
|
|
# ifdef USE_CLIENT_SIDE_SSL
|
|
}
|
|
# endif /* USE_CLIENT_SIDE_SSL */
|
|
*alertDescription = SSL_ALERT_NONE;
|
|
if (rc == MATRIXSSL_SUCCESS)
|
|
{
|
|
if (ssl->err != SSL_ALERT_NONE)
|
|
{
|
|
/* We know this is always a fatal alert due to an error in
|
|
message parsing or creation so flag this session as error */
|
|
ssl->flags |= SSL_FLAGS_ERROR;
|
|
/*
|
|
If tmpbuf has data, it is an alert that needs to be sent so let
|
|
it fall through. Not sure how we would ever not have data in tmpout
|
|
*/
|
|
if (tmpout.buf == tmpout.end)
|
|
{
|
|
psTraceErrr("Unexpected data\n");
|
|
*error = PS_PROTOCOL_FAIL;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
*alertDescription = (unsigned char) ssl->err;
|
|
*alertLevel = SSL_ALERT_LEVEL_FATAL;
|
|
}
|
|
# ifdef ENABLE_FALSE_START
|
|
if ((ssl->flags & SSL_FLAGS_FALSE_START) && *buf != origbuf)
|
|
{
|
|
/* Update outlen with the data we added */
|
|
ssl->outlen += tmpout.end - tmpout.buf;
|
|
}
|
|
else
|
|
# endif
|
|
{
|
|
*remaining = 0;
|
|
*len = tmpout.end - tmpout.buf;
|
|
}
|
|
return SSL_SEND_RESPONSE;
|
|
}
|
|
if (rc == SSL_FULL)
|
|
{
|
|
# if defined(ENABLE_FALSE_START)
|
|
/* We don't support growing outbuf in the false start or early data case */
|
|
if (*buf != origbuf)
|
|
{
|
|
psAssert(rc != SSL_FULL);
|
|
*error = rc;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# endif
|
|
ssl->flags |= SSL_FLAGS_NEED_ENCODE;
|
|
*len = 0; /* No data left to decode */
|
|
/* requiredLen is set by sslEncode Response or ClientHello above */
|
|
return SSL_FULL;
|
|
}
|
|
psAssert(rc < 0);
|
|
*error = rc;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
#ifdef LUCKY13
|
|
/* Return the number of additional MAC compressions that are needed to blind
|
|
the padding/hmac logic for thwarting Lucky 13 style attacks
|
|
*/
|
|
static int32 addCompressCount(ssl_t *ssl, int32 padLen)
|
|
{
|
|
int32 l1, l2, c1, c2, len;
|
|
|
|
c1 = c2 = 0;
|
|
len = ssl->rec.len;
|
|
|
|
# ifdef USE_TLS_1_1
|
|
if (ACTV_VER(ssl, v_tls_explicit_iv))
|
|
{
|
|
len -= ssl->deBlockSize; /* skip explicit IV */
|
|
}
|
|
# endif
|
|
l1 = 13 + len - ssl->deMacSize;
|
|
l2 = 13 + len - padLen - 1 - ssl->deMacSize;
|
|
|
|
if (ssl->deMacSize == SHA1_HASH_SIZE || ssl->deMacSize == SHA256_HASH_SIZE)
|
|
{
|
|
while (l1 > 64)
|
|
{
|
|
c1++; l1 -= 64;
|
|
}
|
|
if (l1 > 56)
|
|
{
|
|
c1++;
|
|
}
|
|
while (l2 > 64)
|
|
{
|
|
c2++; l2 -= 64;
|
|
}
|
|
if (l2 > 56)
|
|
{
|
|
c2++;
|
|
}
|
|
# ifdef USE_SHA384
|
|
}
|
|
else if (ssl->deMacSize == SHA384_HASH_SIZE)
|
|
{
|
|
while (l1 > 128)
|
|
{
|
|
c1++; l1 -= 128;
|
|
}
|
|
if (l1 > 112)
|
|
{
|
|
c1++;
|
|
}
|
|
while (l2 > 128)
|
|
{
|
|
c2++; l2 -= 128;
|
|
}
|
|
if (l2 > 112)
|
|
{
|
|
c2++;
|
|
}
|
|
|
|
# endif
|
|
}
|
|
|
|
return c1 - c2;
|
|
}
|
|
#endif /* LUCKY13 */
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
The workhorse for parsing handshake messages. Also enforces the state
|
|
machine for proper ordering of handshake messages.
|
|
Parameters:
|
|
ssl - ssl context
|
|
inbuf - buffer to read handshake message from
|
|
len - data length for the current ssl record. The ssl record
|
|
can contain multiple handshake messages, so we may need to parse
|
|
them all here.
|
|
Return:
|
|
MATRIXSSL_SUCCESS
|
|
SSL_PROCESS_DATA
|
|
MATRIXSSL_ERROR - see ssl->err for details
|
|
MEM_FAIL
|
|
-MATRIXSSL_ERROR and MEM_FAIL will be caught and an alert sent. If you
|
|
want to specifiy the alert the set ss->err. Otherwise it will
|
|
be an INTERNAL_ERROR
|
|
*/
|
|
static int32 parseSSLHandshake(ssl_t *ssl, char *inbuf, uint32 len)
|
|
{
|
|
unsigned char *c, *end;
|
|
unsigned char *saved_c = NULL;
|
|
unsigned char hsType;
|
|
int32 rc;
|
|
uint32 hsLen;
|
|
unsigned char hsMsgHash[SHA512_HASH_SIZE];
|
|
|
|
#ifdef USE_DTLS
|
|
uint32 fragLen;
|
|
int32 msn, fragOffset, j;
|
|
# ifdef USE_CLIENT_SIDE_SSL
|
|
int32 hvreqMinVer, hvreqMajVer;
|
|
# endif
|
|
#endif /* USE_DTLS */
|
|
|
|
|
|
rc = MATRIXSSL_SUCCESS;
|
|
c = (unsigned char *) inbuf;
|
|
end = (unsigned char *) (inbuf + len);
|
|
|
|
/* Immediately check if we are working with a fragmented message. */
|
|
#ifdef USE_DTLS
|
|
msn = 0;
|
|
/* This is the non-DTLS fragmentation handler */
|
|
if (!(ACTV_VER(ssl, v_dtls_any)))
|
|
{
|
|
#endif
|
|
if (ssl->fragMessage != NULL)
|
|
{
|
|
/* Just borrowing hsLen variable. Is the rest here or do we still
|
|
need more? */
|
|
hsLen = min((uint32) (end - c), ssl->fragTotal - ssl->fragIndex);
|
|
Memcpy(ssl->fragMessage + ssl->fragIndex, c, hsLen);
|
|
ssl->fragIndex += hsLen;
|
|
c += hsLen;
|
|
/* Save the read pointer so that we can return parsing the buffer
|
|
after the fragment has been handled */
|
|
saved_c = c;
|
|
if (ssl->fragIndex == ssl->fragTotal)
|
|
{
|
|
c = ssl->fragMessage + ssl->hshakeHeadLen;
|
|
end = ssl->fragMessage + ssl->fragTotal;
|
|
hsLen = ssl->fragTotal - ssl->hshakeHeadLen;
|
|
goto SKIP_HSHEADER_PARSE;
|
|
}
|
|
else
|
|
{
|
|
return MATRIXSSL_SUCCESS;
|
|
}
|
|
}
|
|
#ifdef USE_DTLS
|
|
}
|
|
#endif
|
|
|
|
#ifdef USE_CERT_CHAIN_PARSING
|
|
if (ssl->rec.partial && (ssl->rec.hsBytesParsed > ssl->recordHeadLen))
|
|
{
|
|
goto SKIP_HSHEADER_PARSE;
|
|
}
|
|
#endif /* USE_CERT_CHAIN_PARSING */
|
|
|
|
parseHandshake:
|
|
if (end - c < 1)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid length of handshake message 1\n");
|
|
psTraceIntInfo("%d\n", (int32) (end - c));
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
hsType = *c; c++;
|
|
|
|
#ifndef SSL_REHANDSHAKES_ENABLED
|
|
/*
|
|
If all rehandshaking is disabled, just catch that here and alert.
|
|
*/
|
|
if (ssl->flags & SSL_FLAGS_SERVER)
|
|
{
|
|
if (hsType == SSL_HS_CLIENT_HELLO && ssl->hsState == SSL_HS_DONE)
|
|
{
|
|
psTraceErrr("Closing conn with client. Rehandshake is disabled\n");
|
|
ssl->err = SSL_ALERT_NO_RENEGOTIATION;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (hsType == SSL_HS_HELLO_REQUEST && ssl->hsState == SSL_HS_DONE)
|
|
{
|
|
psTraceErrr("Closing conn with server. Rehandshake is disabled\n");
|
|
ssl->err = SSL_ALERT_NO_RENEGOTIATION;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
#endif /* SSL_REHANDSHAKES_ENABLED */
|
|
|
|
#ifdef USE_DTLS
|
|
/*
|
|
The MSN helpes keep the state machine sane prior to passing through to
|
|
the hsType exceptions because if they are received out-of-order it could
|
|
choose the wrong handshake type (client auth, rehandshake, or standard)
|
|
|
|
It is mostly important to deal with future messages here because those
|
|
are the ones that may bypass us to the wrong handshake type. Duplicates
|
|
are handled below.
|
|
*/
|
|
if (ACTV_VER(ssl, v_dtls_any))
|
|
{
|
|
if (end - c < 5)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid length of handshake message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
msn = c[3] << 8;
|
|
msn += c[4];
|
|
if (msn > (ssl->lastMsn + 1))
|
|
{
|
|
psTraceIntDtls("Ignoring future handshake msg %d\n", hsType);
|
|
return MATRIXSSL_SUCCESS;
|
|
}
|
|
else if (msn != 0 && ssl->lastMsn >= msn)
|
|
{
|
|
psTraceIntDtls("Ignoring already seen handshake msg %d\n", hsType);
|
|
return DTLS_RETRANSMIT;
|
|
}
|
|
}
|
|
#endif /* USE_DTLS */
|
|
|
|
/*
|
|
hsType is the received handshake type and ssl->hsState is the expected
|
|
handshake type. If it doesn't match, there are some possible cases
|
|
that are not errors. These are checked here.
|
|
*/
|
|
if (hsType != ssl->hsState &&
|
|
(hsType != SSL_HS_CLIENT_HELLO || ssl->hsState != SSL_HS_DONE))
|
|
{
|
|
|
|
/*
|
|
A mismatch is possible in the client authentication case.
|
|
The optional CERTIFICATE_REQUEST may be appearing instead of
|
|
SERVER_HELLO_DONE.
|
|
*/
|
|
if ((hsType == SSL_HS_CERTIFICATE_REQUEST) &&
|
|
(ssl->hsState == SSL_HS_SERVER_HELLO_DONE))
|
|
{
|
|
/*
|
|
This is where the client is first aware of requested client
|
|
authentication so we set the flag here.
|
|
|
|
*/
|
|
ssl->flags |= SSL_FLAGS_CLIENT_AUTH;
|
|
ssl->hsState = SSL_HS_CERTIFICATE_REQUEST;
|
|
goto hsStateDetermined;
|
|
}
|
|
/*
|
|
Another possible mismatch allowed is for a HELLO_REQUEST message.
|
|
Indicates a rehandshake initiated from the server.
|
|
*/
|
|
if ((hsType == SSL_HS_HELLO_REQUEST) &&
|
|
(ssl->hsState == SSL_HS_DONE) &&
|
|
!(ssl->flags & SSL_FLAGS_SERVER))
|
|
{
|
|
sslResetContext(ssl);
|
|
ssl->hsState = hsType;
|
|
goto hsStateDetermined;
|
|
}
|
|
|
|
/* Another possible mismatch is HELLO_REQUEST right after we sent
|
|
a re-handshake CLIENT_HELLO. Will ignore the request and
|
|
assume this was a timing issue and that the server will reply
|
|
to our CLIENT_HELLO when it is received */
|
|
if ((hsType == SSL_HS_HELLO_REQUEST) &&
|
|
(ssl->hsState == SSL_HS_SERVER_HELLO) &&
|
|
(ssl->flags & SSL_FLAGS_READ_SECURE) &&
|
|
(ssl->flags & SSL_FLAGS_WRITE_SECURE) &&
|
|
!(ssl->flags & SSL_FLAGS_SERVER))
|
|
{
|
|
/* There is no body to the message. Confirm this and exit happily
|
|
without changing state */
|
|
if (end - c < 3)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid length of handshake message 2\n");
|
|
psTraceIntInfo("%d\n", (int32) (end - c));
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
hsLen = *c << 16; c++;
|
|
hsLen += *c << 8; c++;
|
|
hsLen += *c; c++;
|
|
#ifdef USE_DTLS
|
|
if (ACTV_VER(ssl, v_dtls_any))
|
|
{
|
|
if (end - c < 8)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid length of handshake message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
c += 8;
|
|
}
|
|
#endif
|
|
#ifdef SSL_REHANDSHAKES_ENABLED
|
|
if (ssl->rehandshakeCount <= 0)
|
|
{
|
|
ssl->err = SSL_ALERT_NO_RENEGOTIATION;
|
|
psTraceErrr("Server re-handshaking denied. Out of credits.\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
ssl->rehandshakeCount--;
|
|
#endif
|
|
if (hsLen == 0)
|
|
{
|
|
return MATRIXSSL_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
|
|
#ifdef USE_STATELESS_SESSION_TICKETS
|
|
/* Another possible mismatch allowed is for a
|
|
SSL_HS_NEW_SESSION_TICKET message. */
|
|
if ((hsType == SSL_HS_NEW_SESSION_TICKET) &&
|
|
(ssl->hsState == SSL_HS_FINISHED) && ssl->sid &&
|
|
(ssl->sid->sessionTicketState == SESS_TICKET_STATE_RECVD_EXT) &&
|
|
!(ssl->flags & SSL_FLAGS_SERVER))
|
|
{
|
|
ssl->hsState = hsType;
|
|
goto hsStateDetermined;
|
|
}
|
|
|
|
#endif /* USE_STATELESS_SESSION_TICKETS */
|
|
|
|
#ifdef USE_OCSP_RESPONSE
|
|
/* Another possible mismatch is server didn't send the optional
|
|
CERTIFICATE_STATUS message. Unfortunate this was not specified
|
|
to be strictly handled in the status_request extensions */
|
|
if (ssl->hsState == SSL_HS_CERTIFICATE_STATUS)
|
|
{
|
|
/* The two valid states from here are identical
|
|
checking of the next state calculation at the end of the
|
|
SSL_HS_CERTIFICATE message handling.
|
|
(But in reverse order due to the precedence of DHE mode.)
|
|
*/
|
|
# ifdef USE_OCSP_MUST_STAPLE
|
|
/* This is the case where the server sent a reply to our
|
|
status_request extension but didn't actually send the
|
|
handshake message. If we are in a MUST state, time to fail */
|
|
psTraceErrr("Expecting CERTIFICATE_STATUS message\n");
|
|
ssl->err = SSL_ALERT_UNEXPECTED_MESSAGE;
|
|
return MATRIXSSL_ERROR;
|
|
# else
|
|
# ifdef USE_DHE_CIPHER_SUITE
|
|
if (ssl->flags & SSL_FLAGS_DHE_KEY_EXCH &&
|
|
hsType == SSL_HS_SERVER_KEY_EXCHANGE)
|
|
{
|
|
ssl->hsState = hsType;
|
|
goto hsStateDetermined;
|
|
}
|
|
# endif /* USE_DHE_CIPHER_SUITE */
|
|
if (hsType == SSL_HS_SERVER_HELLO_DONE)
|
|
{
|
|
ssl->hsState = hsType;
|
|
goto hsStateDetermined;
|
|
}
|
|
# endif /* USE_OCSP_MUST_STAPLE */
|
|
}
|
|
#endif /* USE_OCSP_RESPONSE */
|
|
|
|
#ifdef USE_PSK_CIPHER_SUITE
|
|
/*
|
|
PSK suites are probably not including SERVER_KEY_EXCHANGE message
|
|
*/
|
|
if (ssl->flags & SSL_FLAGS_PSK_CIPHER)
|
|
{
|
|
if ((hsType == SSL_HS_SERVER_HELLO_DONE) &&
|
|
(ssl->hsState == SSL_HS_SERVER_KEY_EXCHANGE))
|
|
{
|
|
# ifdef USE_DHE_CIPHER_SUITE
|
|
/*
|
|
DH kex suites must be sending a SERVER_KEY_EXCHANGE message
|
|
*/
|
|
if (ssl->flags & SSL_FLAGS_DHE_KEY_EXCH)
|
|
{
|
|
psTraceIntInfo("Expecting SKE message: %d\n", hsType);
|
|
ssl->err = SSL_ALERT_UNEXPECTED_MESSAGE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# endif /* USE_DHE_CIPHER_SUITE */
|
|
ssl->hsState = hsType;
|
|
goto hsStateDetermined;
|
|
}
|
|
}
|
|
#endif /* USE_PSK_CIPHER_SUITE */
|
|
|
|
#ifdef USE_DTLS
|
|
/*
|
|
DTLS inserts an optional VERIFY_REQUEST back to clients
|
|
*/
|
|
if (ACTV_VER(ssl, v_dtls_any))
|
|
{
|
|
# ifdef USE_CLIENT_SIDE_SSL
|
|
if ((hsType == SSL_HS_HELLO_VERIFY_REQUEST) &&
|
|
(ssl->hsState == SSL_HS_SERVER_HELLO))
|
|
{
|
|
/* However, if this is a retransmit and we've already parsed
|
|
the HELLO_VERIFY_REQUEST we can safely skip it */
|
|
if (ssl->haveCookie == 0)
|
|
{
|
|
ssl->hsState = hsType;
|
|
goto hsStateDetermined;
|
|
}
|
|
}
|
|
# endif
|
|
/*
|
|
A final MSN sanity test and handling of duplicate hello messages
|
|
*/
|
|
if ((ssl->lastMsn + 1) == msn)
|
|
{
|
|
ssl->err = SSL_ALERT_UNEXPECTED_MESSAGE;
|
|
psTraceIntDtls("Correct MSN %d on unexpected HS msg ", msn);
|
|
psTraceIntDtls(" %d\n", hsType);
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
else if (ssl->lastMsn >= msn)
|
|
{
|
|
psTraceDtls("IGNORING ALREADY SEEN HELLO HANDSHAKE MSG\n");
|
|
return DTLS_RETRANSMIT;
|
|
}
|
|
}
|
|
#endif /* USE_DTLS */
|
|
|
|
ssl->err = SSL_ALERT_UNEXPECTED_MESSAGE;
|
|
psTraceIntInfo("Out-of-order handshake message: %d\n", hsType);
|
|
psTraceIntInfo("Wanted: %d\n", ssl->hsState);
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
hsStateDetermined:
|
|
if (hsType == SSL_HS_CLIENT_HELLO)
|
|
{
|
|
sslInitHSHash(ssl);
|
|
if (ssl->hsState == SSL_HS_DONE)
|
|
{
|
|
# ifdef SSL_REHANDSHAKES_ENABLED
|
|
/* This is a 'leaky bucket' mechanism where each X bytes of data transfer gains
|
|
you a re-handshake credit. Prevents the DOS attack of repeat
|
|
re-handshake requests */
|
|
if (ssl->rehandshakeCount <= 0)
|
|
{
|
|
ssl->err = SSL_ALERT_NO_RENEGOTIATION;
|
|
psTraceErrr("Client re-handshaking denied\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
ssl->rehandshakeBytes = 0; /* reset */
|
|
ssl->rehandshakeCount--;
|
|
# endif /* SSL_REHANDSHAKES_ENABLED */
|
|
/* Rehandshake. Server receiving client hello on existing connection */
|
|
sslResetContext(ssl);
|
|
ssl->hsState = hsType;
|
|
}
|
|
}
|
|
|
|
/*
|
|
We need to get a copy of the message hashes to compare to those sent
|
|
in the finished message (which does not include a hash of itself)
|
|
before we update the handshake hashes
|
|
*/
|
|
if (ssl->hsState == SSL_HS_FINISHED)
|
|
{
|
|
if (sslSnapshotHSHash(ssl, hsMsgHash, PS_FALSE, PS_TRUE) <= 0)
|
|
{
|
|
psTraceErrr("Error snapshotting HS hash\n");
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
#ifdef USE_CLIENT_AUTH
|
|
if (ssl->hsState == SSL_HS_CERTIFICATE_VERIFY)
|
|
{
|
|
/* Same issue as above for client auth. Need a handshake snapshot
|
|
that doesn't include this message we are about to process */
|
|
if (sslSnapshotHSHash(ssl, hsMsgHash, PS_FALSE, PS_FALSE) <= 0)
|
|
{
|
|
psTraceErrr("Error snapshotting HS hash\n");
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
#endif /* USE_CLIENT_AUTH */
|
|
|
|
/*
|
|
Process the handshake header and update the ongoing handshake hash
|
|
SSLv3:
|
|
1 byte type
|
|
3 bytes length
|
|
SSLv2:
|
|
1 byte type
|
|
*/
|
|
if (ssl->rec.majVer >= SSL3_MAJ_VER)
|
|
{
|
|
uint32 hsLenMax;
|
|
if (end - c < 3)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid length of handshake message 2\n");
|
|
psTraceIntInfo("%d\n", (int32) (end - c));
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
hsLen = *c << 16; c++;
|
|
hsLen += *c << 8; c++;
|
|
hsLen += *c; c++;
|
|
|
|
if (ssl->hsState == SSL_HS_CLIENT_HELLO)
|
|
{
|
|
/* This is for Client Hello.
|
|
Note: *Client Hello* is determined according to
|
|
expected state of server, rather than examining of the
|
|
message. Therefore, this limit applies to any first
|
|
protocol handshake message received. */
|
|
#ifdef SSL_DEFAULT_IN_HS_SIZE_CLIENT_HELLO
|
|
hsLenMax = SSL_DEFAULT_IN_HS_SIZE_CLIENT_HELLO;
|
|
#else
|
|
hsLenMax = 1024; /* Built-in default, in case MatrixSSL
|
|
configuration does not override the size. */
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
/* This is for other messages. Other messages can be
|
|
larger, due to possibility that they can include certificates.
|
|
Certificates can be (in theory) arbitrarily large,
|
|
but we need to provide a limit for certificate chain, because
|
|
otherwise arbitrary amount of memory could be allocated
|
|
. */
|
|
#ifdef SSL_DEFAULT_IN_HS_SIZE
|
|
hsLenMax = SSL_DEFAULT_IN_HS_SIZE;
|
|
#else
|
|
hsLenMax = 65536; /* Built-in default, in case MatrixSSL
|
|
configuration does not override the size. */
|
|
#endif
|
|
}
|
|
if (hsLen > hsLenMax)
|
|
{
|
|
/* The (fragmented) packet is considered overly large and dropped.
|
|
*/
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Maximum length exceeded.\n");
|
|
psTraceIntInfo("%d\n", (int) hsLen);
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
#ifdef USE_DTLS
|
|
if (ACTV_VER(ssl, v_dtls_any))
|
|
{
|
|
if (end - c < 8)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid length of handshake message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
msn = *c << 8; c++;
|
|
msn += *c; c++;
|
|
fragOffset = *c << 16; c++;
|
|
fragOffset += *c << 8; c++;
|
|
fragOffset += *c; c++;
|
|
fragLen = *c << 16; c++;
|
|
fragLen += *c << 8; c++;
|
|
fragLen += *c; c++;
|
|
if (fragLen != hsLen)
|
|
{
|
|
/*
|
|
Have a fragmented message here. Allocate if first time
|
|
seen and assign msn. Can only deal with single fragmented
|
|
message at a time.
|
|
*/
|
|
if (ssl->fragTotal == 0)
|
|
{
|
|
/*
|
|
When all the fragments are received, this allocated pointer
|
|
becomes the 'c' parsing pointer. With all the potential
|
|
exit points in the parse code from all the different
|
|
messages it is not easy to free this on the fly. So, what
|
|
happens here is that each first message fragment that is
|
|
encountered will free the previous message if it exists
|
|
(not NULL).
|
|
|
|
The final test for freeing this pointer will be in the
|
|
encoding (client) and decoding (server) of the Finished
|
|
message. At this point we know we can't possibly be
|
|
recieving any more fragments since the CCS and Finished
|
|
messages will never be so large that they would require
|
|
fragmenting. Also, the handshake pool is freed during
|
|
the encoding of Finished.
|
|
*/
|
|
if (ssl->fragMessage != NULL )
|
|
{
|
|
psFree(ssl->fragMessage, ssl->hsPool);
|
|
ssl->fragMessage = NULL;
|
|
}
|
|
ssl->fragMessage = psMalloc(ssl->hsPool, hsLen);
|
|
ssl->fragLenStored = hsLen;
|
|
if (ssl->fragMessage == NULL)
|
|
{
|
|
return SSL_MEM_ERROR;
|
|
}
|
|
ssl->fragMsn = msn;
|
|
}
|
|
|
|
if (ssl->fragMsn != msn)
|
|
{
|
|
/*
|
|
Got a fragment from a different msg. Ignore
|
|
*/
|
|
return MATRIXSSL_SUCCESS;
|
|
}
|
|
/*
|
|
Still could be a duplicate fragment. Make sure we haven't
|
|
seen it before. If we haven't this routine also returns
|
|
the next open fragment header index for use below.
|
|
*/
|
|
if ((rc = dtlsSeenFrag(ssl, fragOffset, &j)) == 1)
|
|
{
|
|
return MATRIXSSL_SUCCESS;
|
|
}
|
|
else if (rc == -1) /* MAX_FRAGMENTS exceeded */
|
|
{
|
|
dtlsInitFrag(ssl); /* init will free memory */
|
|
if (ssl->fragMessage != NULL)
|
|
{
|
|
psFree(ssl->fragMessage, ssl->hsPool);
|
|
ssl->fragMessage = NULL;
|
|
}
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceIntDtls("Max fragment limit exceeded: %d\n",
|
|
MAX_FRAGMENTS);
|
|
return PS_LIMIT_FAIL;
|
|
}
|
|
|
|
/*
|
|
Verify the fragment belongs within fragMessage.
|
|
*/
|
|
if (fragOffset + fragLen > hsLen ||
|
|
fragOffset + fragLen > ssl->fragLenStored)
|
|
{
|
|
/* Fragment outside proper area. */
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceIntDtls("Fragment outside range [0...%d]: ignored\n",
|
|
(int) hsLen);
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
/*
|
|
Need to save the hs header info aside as well so that we may
|
|
pass the fragments through the handshake hash mechanism in
|
|
the correct order. This list also keeps track of the fragment
|
|
offsets and lengths for the same reason.
|
|
*/
|
|
ssl->fragHeaders[j].hsHeader = psMalloc(ssl->hsPool,
|
|
ssl->hshakeHeadLen);
|
|
if (ssl->fragHeaders[j].hsHeader == NULL)
|
|
{
|
|
dtlsInitFrag(ssl); /* init to free */
|
|
return SSL_MEM_ERROR;
|
|
}
|
|
Memcpy(ssl->fragHeaders[j].hsHeader, c - ssl->hshakeHeadLen,
|
|
ssl->hshakeHeadLen);
|
|
ssl->fragHeaders[j].offset = fragOffset;
|
|
ssl->fragHeaders[j].fragLen = fragLen;
|
|
|
|
ssl->fragTotal += fragLen;
|
|
Memcpy(ssl->fragMessage + fragOffset, c, fragLen);
|
|
if (ssl->fragTotal != hsLen)
|
|
{
|
|
|
|
/* Don't have all the fragments yet */
|
|
return MATRIXSSL_SUCCESS;
|
|
}
|
|
c = ssl->fragMessage;
|
|
end = ssl->fragMessage + hsLen;
|
|
}
|
|
}
|
|
#endif /* USE_DTLS */
|
|
#ifdef USE_CERT_CHAIN_PARSING
|
|
if (((uint32) (end - c) < hsLen) && !ssl->rec.partial)
|
|
{
|
|
#else
|
|
if ((uint32) (end - c) < hsLen)
|
|
{
|
|
#endif
|
|
/* Support for fragmented handshake messages - non-DTLS */
|
|
if (ssl->fragMessage == NULL)
|
|
{
|
|
/* Initial indication there is a fragmented message */
|
|
ssl->fragTotal = hsLen + ssl->hshakeHeadLen;
|
|
ssl->fragMessage = psMalloc(ssl->hsPool, ssl->fragTotal);
|
|
if (ssl->fragMessage == NULL)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
psTraceErrr("Memory allocation error\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
ssl->fragIndex = (uint32) (end - c) + ssl->hshakeHeadLen;
|
|
Memcpy(ssl->fragMessage, c - ssl->hshakeHeadLen,
|
|
ssl->fragIndex);
|
|
return MATRIXSSL_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid handshake length\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
#ifdef USE_DTLS
|
|
if (ACTV_VER(ssl, v_dtls_any))
|
|
{
|
|
if (ssl->fragTotal > 0)
|
|
{
|
|
/* Run the UpdateHash over the fragmented message */
|
|
dtlsHsHashFragMsg(ssl);
|
|
dtlsInitFrag(ssl);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
The DTLS case in which the message was not fragmented.
|
|
Not at all unusual to hit this
|
|
*/
|
|
sslUpdateHSHash(ssl, c - ssl->hshakeHeadLen,
|
|
hsLen + ssl->hshakeHeadLen);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
#endif /* USE_DTLS */
|
|
SKIP_HSHEADER_PARSE:
|
|
|
|
#ifdef USE_CERT_CHAIN_PARSING
|
|
if (ssl->rec.partial)
|
|
{
|
|
/*
|
|
Length of partial certificate records are being managed
|
|
manually with ssl->rec.len. The first pass will need to
|
|
include the record header in the hash.
|
|
*/
|
|
if (ssl->rec.hsBytesHashed == 0)
|
|
{
|
|
sslUpdateHSHash(ssl, c - ssl->hshakeHeadLen, ssl->rec.len);
|
|
}
|
|
else
|
|
{
|
|
sslUpdateHSHash(ssl, c, ssl->rec.len);
|
|
}
|
|
ssl->rec.hsBytesHashed += ssl->rec.len;
|
|
}
|
|
else
|
|
{
|
|
sslUpdateHSHash(ssl, c - ssl->hshakeHeadLen,
|
|
hsLen + ssl->hshakeHeadLen);
|
|
}
|
|
#else
|
|
sslUpdateHSHash(ssl, c - ssl->hshakeHeadLen,
|
|
hsLen + ssl->hshakeHeadLen);
|
|
|
|
#endif
|
|
#ifdef USE_DTLS
|
|
}
|
|
#endif /* USE_DTLS */
|
|
|
|
}
|
|
else if (ssl->rec.majVer == SSL2_MAJ_VER)
|
|
{
|
|
/*
|
|
Assume that the handshake len is the same as the incoming ssl record
|
|
length minus 1 byte (type), this is verified in SSL_HS_CLIENT_HELLO
|
|
*/
|
|
hsLen = len - 1;
|
|
sslUpdateHSHash(ssl, (unsigned char *) inbuf, len);
|
|
}
|
|
else
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceIntInfo("Invalid record version: %d\n", ssl->rec.majVer);
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
Finished with header. Process each type of handshake message.
|
|
*/
|
|
switch (ssl->hsState)
|
|
{
|
|
|
|
/******************************************************************************/
|
|
|
|
#ifdef USE_SERVER_SIDE_SSL
|
|
case SSL_HS_CLIENT_HELLO:
|
|
psAssert(rc == 0); /* checking to see if this is the correct default */
|
|
if (c + hsLen != end)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid length for Client Hello.\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
rc = parseClientHello(ssl, &c, end);
|
|
#ifdef USE_TLS_1_3
|
|
if (USING_TLS_1_3(ssl))
|
|
{
|
|
/* Tr-Hash already up-to-date if binders were parsed. If not,
|
|
we have delayed updating until now. */
|
|
if (!ssl->sec.tls13UsingPsk || ssl->sec.tls13BindersLen == 0)
|
|
{
|
|
tls13TranscriptHashUpdate(ssl,
|
|
ssl->sec.tls13CHStart,
|
|
ssl->sec.tls13CHLen);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* SSL_PROCESS_DATA is a valid code to indicate the end of a flight */
|
|
if (rc < 0 && rc != SSL_PROCESS_DATA)
|
|
{
|
|
return rc;
|
|
}
|
|
|
|
break;
|
|
|
|
/******************************************************************************/
|
|
|
|
case SSL_HS_CLIENT_KEY_EXCHANGE:
|
|
psAssert(rc == 0); /* checking to see if this is the correct default */
|
|
rc = parseClientKeyExchange(ssl, hsLen, &c, end);
|
|
if (rc < 0)
|
|
{
|
|
return rc;
|
|
}
|
|
break;
|
|
#endif /* USE_SERVER_SIDE_SSL */
|
|
|
|
/******************************************************************************/
|
|
|
|
case SSL_HS_FINISHED:
|
|
psAssert(rc == 0); /* checking to see if this is the correct default */
|
|
rc = parseFinished(ssl, hsLen, hsMsgHash, &c, end);
|
|
/* SSL_PROCESS_DATA is a valid code to indicate the end of a flight */
|
|
if (rc < 0 && rc != SSL_PROCESS_DATA)
|
|
{
|
|
return rc;
|
|
}
|
|
break;
|
|
|
|
/******************************************************************************/
|
|
#ifdef USE_CLIENT_SIDE_SSL
|
|
case SSL_HS_HELLO_REQUEST:
|
|
/* No body message and the only one in record flight */
|
|
psTracePrintHsMessageParse(ssl, SSL_HS_HELLO_REQUEST);
|
|
if (end - c != 0)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceErrr("Invalid hello request message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# ifdef SSL_REHANDSHAKES_ENABLED
|
|
if (ssl->rehandshakeCount <= 0)
|
|
{
|
|
ssl->err = SSL_ALERT_NO_RENEGOTIATION;
|
|
psTraceErrr("Server re-handshaking denied\n");
|
|
/* Reset the state to done */
|
|
ssl->hsState = SSL_HS_DONE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
ssl->rehandshakeCount--;
|
|
# endif
|
|
/* Intentionally not changing state here to SERVER_HELLO. The
|
|
encodeResponse case this will fall into needs to distinguish
|
|
between calling the normal sslEncodeResponse or encodeClientHello.
|
|
The HELLO_REQUEST state is used to make that determination and the
|
|
writing of CLIENT_HELLO will properly move the state along itself */
|
|
ssl->decState = SSL_HS_HELLO_REQUEST;
|
|
rc = SSL_PROCESS_DATA;
|
|
# ifdef USE_EXT_CLIENT_CERT_KEY_LOADING
|
|
/* Reinitialize the state of the on-demand client cert and key loading
|
|
feature for the re-handshake. */
|
|
ssl->extClientCertKeyStateFlags = EXT_CLIENT_CERT_KEY_STATE_INIT;
|
|
# endif /* USE_EXT_CLIENT_CERT_KEY_LOADING */
|
|
|
|
#ifdef USE_DTLS
|
|
if (ACTV_VER(ssl, v_dtls_any))
|
|
{
|
|
/* Server initiated rehandshake - brign resend epoch up to
|
|
date ... shouldn't this be done when entering
|
|
done-state? */
|
|
ssl->resendEpoch[0] = ssl->epoch[0];
|
|
ssl->resendEpoch[1] = ssl->epoch[1];
|
|
ssl->appDataExch = 0;
|
|
ssl->msn = ssl->resendMsn = 0;
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
/******************************************************************************/
|
|
|
|
case SSL_HS_SERVER_HELLO:
|
|
|
|
psAssert(rc == 0); /* checking to see if this is the correct default */
|
|
rc = parseServerHello(ssl, hsLen, &c, end);
|
|
if (rc < 0)
|
|
{
|
|
return rc;
|
|
}
|
|
break;
|
|
|
|
#endif /* USE_CLIENT_SIDE_SSL */
|
|
|
|
/******************************************************************************/
|
|
|
|
#ifndef USE_ONLY_PSK_CIPHER_SUITE
|
|
# if defined(USE_CLIENT_SIDE_SSL) || defined(USE_CLIENT_AUTH)
|
|
|
|
case SSL_HS_CERTIFICATE:
|
|
psAssert(rc == 0); /* checking to see if this is the correct default */
|
|
rc = parseCertificate(ssl, &c, end);
|
|
if (rc < 0)
|
|
{
|
|
return rc;
|
|
}
|
|
break;
|
|
|
|
# endif /* USE_CLIENT_SIDE_SSL || USE_CLIENT_AUTH */
|
|
#endif /* !USE_ONLY_PSK_CIPHER_SUITE */
|
|
|
|
#ifdef USE_CLIENT_SIDE_SSL
|
|
/******************************************************************************/
|
|
# ifdef USE_OCSP_RESPONSE
|
|
case SSL_HS_CERTIFICATE_STATUS:
|
|
rc = parseCertificateStatus(ssl, hsLen, &c, end);
|
|
if (rc < 0)
|
|
{
|
|
return rc;
|
|
}
|
|
break;
|
|
# endif /* USE_OCSP_RESPONSE */
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
# ifdef USE_STATELESS_SESSION_TICKETS
|
|
case SSL_HS_NEW_SESSION_TICKET:
|
|
psTracePrintHsMessageParse(ssl, SSL_HS_NEW_SESSION_TICKET);
|
|
# ifdef USE_EAP_FAST
|
|
if (ssl->flags & SSL_FLAGS_EAP_FAST)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceErrr("NEW_SESSION_TICKET unsupported in EAP-FAST\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
# endif
|
|
if (hsLen < 6)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceErrr("Invalid NewSessionTicket message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
ssl->sid->sessionTicketLifetimeHint = *c << 24; c++;
|
|
ssl->sid->sessionTicketLifetimeHint |= *c << 16; c++;
|
|
ssl->sid->sessionTicketLifetimeHint |= *c << 8; c++;
|
|
ssl->sid->sessionTicketLifetimeHint |= *c; c++;
|
|
/* Reusing hsLen here */
|
|
hsLen = *c << 8; c++;
|
|
hsLen |= *c; c++;
|
|
|
|
if ((uint32) (end - c) < hsLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid NewSessionTicket message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if (ssl->sid->sessionTicket == NULL || ssl->sid->sessionTicketLen == 0)
|
|
{
|
|
/* First time receiving a session ticket */
|
|
ssl->sid->sessionTicketLen = hsLen;
|
|
/* This client has a dedicated SessionId pool to draw from. */
|
|
if ((ssl->sid->sessionTicket = psMalloc(ssl->sid->pool,
|
|
ssl->sid->sessionTicketLen)) != NULL)
|
|
{
|
|
Memcpy(ssl->sid->sessionTicket, c, ssl->sid->sessionTicketLen);
|
|
c += ssl->sid->sessionTicketLen;
|
|
}
|
|
else
|
|
{
|
|
/* Don't fail on alloc error. Just won't have the ticket for
|
|
next time */
|
|
c += ssl->sid->sessionTicketLen;
|
|
ssl->sid->sessionTicketLen = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Updated (or duplicate) ticket */
|
|
psAssert(ssl->sid->sessionTicket); /* exists from previous hs */
|
|
if (hsLen == ssl->sid->sessionTicketLen &&
|
|
(Memcmp(c, ssl->sid->sessionTicket, hsLen) == 0))
|
|
{
|
|
/* server not updating the ticket */
|
|
c += ssl->sid->sessionTicketLen;
|
|
}
|
|
else
|
|
{
|
|
ssl->sid->sessionTicketLen = hsLen;
|
|
psFree(ssl->sid->sessionTicket, ssl->sid->pool);
|
|
if ((ssl->sid->sessionTicket = psMalloc(ssl->sid->pool,
|
|
ssl->sid->sessionTicketLen)) != NULL)
|
|
{
|
|
Memcpy(ssl->sid->sessionTicket, c,
|
|
ssl->sid->sessionTicketLen);
|
|
c += ssl->sid->sessionTicketLen;
|
|
}
|
|
else
|
|
{
|
|
/* Don't fail on alloc error. Just won't have the ticket
|
|
for next time */
|
|
c += ssl->sid->sessionTicketLen;
|
|
ssl->sid->sessionTicketLen = 0;
|
|
}
|
|
}
|
|
}
|
|
ssl->sid->sessionTicketState = SESS_TICKET_STATE_INIT;
|
|
ssl->hsState = SSL_HS_FINISHED;
|
|
ssl->decState = SSL_HS_NEW_SESSION_TICKET;
|
|
break;
|
|
# endif /* USE_STATELESS_SESSION_TICKETS */
|
|
|
|
/******************************************************************************/
|
|
|
|
case SSL_HS_SERVER_HELLO_DONE:
|
|
|
|
psAssert(rc == 0); /* checking to see if this is the correct default */
|
|
rc = parseServerHelloDone(ssl, hsLen, &c, end);
|
|
if (rc < 0 && rc != SSL_PROCESS_DATA)
|
|
{
|
|
return rc;
|
|
}
|
|
# ifdef USE_EXT_CLIENT_CERT_KEY_LOADING
|
|
/* Note: we must have parsed both CertificateRequest and ServerHelloDone
|
|
before proceeding to new client cert and key loading state. */
|
|
ssl->extClientCertKeyStateFlags |=
|
|
EXT_CLIENT_CERT_KEY_STATE_GOT_SERVER_HELLO_DONE;
|
|
# endif /* USE_EXT_CLIENT_CERT_KEY_LOADING */
|
|
break;
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
# if defined(USE_CLIENT_AUTH) && !defined(USE_ONLY_PSK_CIPHER_SUITE)
|
|
case SSL_HS_CERTIFICATE_REQUEST:
|
|
|
|
psAssert(rc == 0); /* checking to see if this is the correct default */
|
|
rc = parseCertificateRequest(ssl, hsLen, &c, end);
|
|
if (rc < 0)
|
|
{
|
|
return rc;
|
|
}
|
|
# ifdef USE_EXT_CLIENT_CERT_KEY_LOADING
|
|
/* Note: we must have parsed both CertificateRequest and ServerHelloDone
|
|
before proceeding to new client cert and key loading state. */
|
|
ssl->extClientCertKeyStateFlags |=
|
|
EXT_CLIENT_CERT_KEY_STATE_GOT_CERTIFICATE_REQUEST;
|
|
# endif /* USE_EXT_CLIENT_CERT_KEY_LOADING */
|
|
|
|
break;
|
|
# endif /* !USE_ONLY_PSK_CIPHER_SUITE */
|
|
#endif /* USE_CLIENT_SIDE_SSL */
|
|
|
|
/******************************************************************************/
|
|
|
|
#ifndef USE_ONLY_PSK_CIPHER_SUITE
|
|
# if defined(USE_CLIENT_AUTH) && defined(USE_SERVER_SIDE_SSL)
|
|
case SSL_HS_CERTIFICATE_VERIFY:
|
|
|
|
psAssert(rc == 0); /* checking to see if this is the correct default */
|
|
rc = parseCertificateVerify(ssl, hsMsgHash, &c, end);
|
|
if (rc < 0)
|
|
{
|
|
return rc;
|
|
}
|
|
|
|
break;
|
|
# endif /* USE_SERVER_SIDE_SSL && USE_CLIENT_AUTH */
|
|
#endif /* !USE_ONLY_PSK_CIPHER_SUITE */
|
|
|
|
/******************************************************************************/
|
|
|
|
case SSL_HS_SERVER_KEY_EXCHANGE:
|
|
#ifdef USE_CLIENT_SIDE_SSL
|
|
psAssert(rc == 0); /* checking to see if this is the correct default */
|
|
rc = parseServerKeyExchange(ssl, hsMsgHash, &c, end);
|
|
if (rc < 0)
|
|
{
|
|
return rc;
|
|
}
|
|
#else /* USE_CLIENT_SIDE_SSL */
|
|
ssl->err = SSL_ALERT_UNEXPECTED_MESSAGE;
|
|
return MATRIXSSL_ERROR;
|
|
#endif /* USE_CLIENT_SIDE_SSL */
|
|
break;
|
|
|
|
/******************************************************************************/
|
|
|
|
#ifdef USE_DTLS
|
|
# ifdef USE_CLIENT_SIDE_SSL
|
|
case SSL_HS_HELLO_VERIFY_REQUEST:
|
|
psTracePrintHsMessageParse(ssl, SSL_HS_HELLO_VERIFY_REQUEST);
|
|
/*
|
|
Format for message is two byte version specifier, 1 byte length, and
|
|
the cookie itself
|
|
|
|
*/
|
|
if ((end - c) < 3)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid HelloVerifyRequest message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
hvreqMajVer = *c; c++;
|
|
hvreqMinVer = *c; c++;
|
|
(void) hvreqMajVer; /* Silence a 'set but not used' warning. */
|
|
(void) hvreqMinVer;
|
|
ssl->cookieLen = *c; c++;
|
|
if (ssl->cookieLen > 0)
|
|
{
|
|
if ((end - c) < ssl->cookieLen)
|
|
{
|
|
ssl->err = SSL_ALERT_DECODE_ERROR;
|
|
psTraceErrr("Invalid HelloVerifyRequest message\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
/*
|
|
The handshake pool does exists at this point. For DTLS handshakes
|
|
the client created the pool during the ClientHello write in order
|
|
to store the initial message in case the Server asks for cookie
|
|
(which is exactly what is happening right here).
|
|
*/
|
|
if (ssl->haveCookie)
|
|
{
|
|
/* retransmit. should match what we already have */
|
|
if (memcmpct(ssl->cookie, c, ssl->cookieLen) != 0)
|
|
{
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
psTraceErrr("Cookie has changed on retransmit\n");
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
c += ssl->cookieLen;
|
|
}
|
|
else
|
|
{
|
|
ssl->cookie = psMalloc(ssl->hsPool, ssl->cookieLen);
|
|
if (ssl->cookie == NULL)
|
|
{
|
|
return SSL_MEM_ERROR;
|
|
}
|
|
Memcpy(ssl->cookie, c, ssl->cookieLen);
|
|
c += ssl->cookieLen;
|
|
}
|
|
}
|
|
ssl->haveCookie++;
|
|
ssl->hsState = SSL_HS_SERVER_HELLO;
|
|
ssl->decState = SSL_HS_HELLO_VERIFY_REQUEST;
|
|
rc = SSL_PROCESS_DATA;
|
|
break;
|
|
# endif /* USE_CLIENT_SIDE_SSL */
|
|
#endif /* USE_DTLS */
|
|
|
|
/******************************************************************************/
|
|
|
|
default:
|
|
ssl->err = SSL_ALERT_UNEXPECTED_MESSAGE;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
#ifdef USE_DTLS
|
|
if (ACTV_VER(ssl, v_dtls_any))
|
|
{
|
|
ssl->lastMsn = msn; /* MSN of last message sucessfully parsed */
|
|
}
|
|
#endif /* USE_DTLS */
|
|
|
|
/* In case fragmented message was assembled and parsed from
|
|
ssl->fragMessage we have to return to the original buffer here */
|
|
if (saved_c != NULL)
|
|
{
|
|
c = saved_c;
|
|
end = (unsigned char *) (inbuf + len);
|
|
saved_c = NULL;
|
|
}
|
|
/*
|
|
if we've got more data in the record, the sender has packed
|
|
multiple handshake messages in one record. Parse the next one.
|
|
*/
|
|
if (c < end)
|
|
{
|
|
goto parseHandshake;
|
|
}
|
|
|
|
# ifdef USE_EXT_CLIENT_CERT_KEY_LOADING
|
|
if ((ssl->extClientCertKeyStateFlags &
|
|
EXT_CLIENT_CERT_KEY_STATE_GOT_CERTIFICATE_REQUEST) &&
|
|
(ssl->extClientCertKeyStateFlags &
|
|
EXT_CLIENT_CERT_KEY_STATE_GOT_SERVER_HELLO_DONE))
|
|
{
|
|
psTraceInfo("Received CertificateRequest flight\n");
|
|
psTraceInfo("Now returning PS_PENDING to get client cert and key\n");
|
|
ssl->extClientCertKeyStateFlags =
|
|
EXT_CLIENT_CERT_KEY_STATE_WAIT_FOR_CERT_KEY_UPDATE;
|
|
return PS_PENDING;
|
|
}
|
|
# endif /* USE_EXT_CLIENT_CERT_KEY_LOADING */
|
|
|
|
return rc;
|
|
}
|
|
# endif /* USE_TLS_1_3_ONLY */
|
|
|
|
/******************************************************************************/
|
|
#if defined(USE_CLIENT_SIDE_SSL) || defined(USE_CLIENT_AUTH)
|
|
# ifdef USE_CERT_CHAIN_PARSING
|
|
static int32 parseSingleCert(ssl_t *ssl, unsigned char *c, unsigned char *end,
|
|
int32 certLen)
|
|
{
|
|
int32 parseLen, certFlags;
|
|
psX509Cert_t *cert, *p;
|
|
|
|
/*
|
|
Extract the binary cert message into the cert structure
|
|
*/
|
|
if (ssl->bflags & BFLAG_KEEP_PEER_CERT_DER)
|
|
{
|
|
certFlags |= CERT_STORE_UNPARSED_BUFFER;
|
|
}
|
|
|
|
if ((parseLen = psX509ParseCert(ssl->hsPool, c, certLen, &cert, certFlags)) < 0)
|
|
{
|
|
psX509FreeCert(cert);
|
|
if (parseLen == PS_MEM_FAIL)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
}
|
|
else
|
|
{
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
}
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
if (ssl->sec.cert == NULL)
|
|
{
|
|
ssl->sec.cert = cert;
|
|
}
|
|
else
|
|
{
|
|
p = ssl->sec.cert;
|
|
while (p->next != NULL)
|
|
{
|
|
p = p->next;
|
|
}
|
|
p->next = cert;
|
|
}
|
|
return parseLen;
|
|
}
|
|
# endif /* USE_CERT_CHAIN_PARSING */
|
|
#endif /* USE_CLIENT_SIDE_SSL || USE_CLIENT_AUTH */
|
|
|
|
/******************************************************************************/
|