Files
mars-matrixssl/matrixssl/sslDecode.c
2016-05-04 16:22:06 -07:00

2475 lines
79 KiB
C

/**
* @file sslDecode.c
* @version $Format:%h%d$
*
* Secure Sockets Layer protocol message decoding portion of MatrixSSL.
*/
/*
* Copyright (c) 2013-2016 INSIDE Secure Corporation
* Copyright (c) PeerSec Networks, 2002-2011
* All Rights Reserved
*
* The latest version of this code is available at http://www.matrixssl.org
*
* This software is open source; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This General Public License does NOT permit incorporating this software
* into proprietary programs. If you are unable to comply with the GPL, a
* commercial license for this software may be purchased from INSIDE at
* http://www.insidesecure.com/
*
* This program is distributed in WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* http://www.gnu.org/copyleft/gpl.html
*/
/******************************************************************************/
#include "matrixsslApi.h"
/******************************************************************************/
#define LUCKY13
#define SSL_MAX_IGNORED_MESSAGE_COUNT 1024
static int32 parseSSLHandshake(ssl_t *ssl, char *inbuf, uint32 len);
#ifdef USE_CERT_CHAIN_PARSING
static int32 parseSingleCert(ssl_t *ssl, unsigned char *c, unsigned char *end,
int32 certLen);
#endif /* USE_CERT_CHAIN_PARSING */
#ifdef LUCKY13
static int32 addCompressCount(ssl_t *ssl, int32 padLen);
#endif
#ifdef USE_ZLIB_COMPRESSION
/* Does not need to be a large value because we're only inflating the 16
byte FINISHED message. In fact, compression will grow 16 bytes but
this is a good reminder that FUTURE support will need to account for
likely data growth here */
#define MATRIX_INFLATE_FINISHED_OH 128
#endif
/******************************************************************************/
/*
Parse incoming data per http://wp.netscape.com/eng/ssl3
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)
{
unsigned char *c, *p, *end, *pend, *ctStart, *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 */
#ifdef USE_ZLIB_COMPRESSION
int32 preInflateLen, postInflateLen, currLen;
int zret;
#endif
/*
If we've had a protocol error, don't allow further use of the session
*/
*error = PS_SUCCESS;
if (ssl->flags & SSL_FLAGS_ERROR || ssl->flags & SSL_FLAGS_CLOSED) {
psTraceInfo("Can't use matrixSslDecode on closed/error-flagged sess\n");
*error = PS_PROTOCOL_FAIL;
return MATRIXSSL_ERROR;
}
origbuf = *buf; /* Save the original buffer location */
p = pend = mac = ctStart = NULL;
padLen = 0;
/*
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 */
if (ssl->majVer != 0 || (*c & 0x80) == 0) {
if (end - c < ssl->recordHeadLen) {
*requiredLen = ssl->recordHeadLen;
return SSL_PARTIAL;
}
ssl->rec.type = *c; c++;
ssl->rec.majVer = *c; c++;
ssl->rec.minVer = *c; c++;
#ifdef USE_DTLS
if (ssl->flags & SSL_FLAGS_DTLS) {
if (ssl->rec.majVer == DTLS_MAJ_VER &&
ssl->rec.minVer >= DTLS_1_2_MIN_VER) {
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++;
} else {
psTraceIntDtls("Expecting DTLS record version. Got %d\n",
ssl->rec.majVer);
*error = PS_PROTOCOL_FAIL;
return MATRIXSSL_ERROR;
}
}
#endif /* USE_DTLS */
ssl->rec.len = *c << 8; c++;
ssl->rec.len += *c; c++;
} else {
/* OpenSSL 0.9.8 will send a SSLv2 CLIENT_HELLO. Use the -no_ssl2
option when running a 0.9.8 client to prevent this */
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
psTraceInfo("SSLv2 records not supported\n");
goto encodeResponse;
}
/*
Validate the various record headers. 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
*/
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;
psTraceIntInfo("Record header type not valid: %d\n", ssl->rec.type);
goto encodeResponse;
}
/*
Verify the record version numbers unless this is the first record we're
reading.
*/
if (ssl->hsState != SSL_HS_SERVER_HELLO &&
ssl->hsState != SSL_HS_CLIENT_HELLO) {
if (ssl->rec.majVer != ssl->majVer || ssl->rec.minVer != ssl->minVer) {
#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) {
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
psTraceInfo("Record header version not valid\n");
goto encodeResponse;
}
#else
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
psTraceInfo("Record header version not valid\n");
goto encodeResponse;
#endif
}
}
/*
Verify max and min record lengths
*/
if (ssl->rec.len > SSL_MAX_RECORD_LEN || ssl->rec.len == 0) {
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
psTraceIntInfo("Record header length not valid: %d\n", ssl->rec.len);
goto encodeResponse;
}
/*
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 (ssl->flags & SSL_FLAGS_DTLS) {
psTraceInfo("DTLS error: Received PARTIAL record from peer.\n");
psTraceInfo("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 (ssl->flags & SSL_FLAGS_DTLS) {
/* 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("on a record type of %d\n", ssl->rec.type);
/* 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) {
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
*/
ctStart = 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 (ssl->flags & SSL_FLAGS_TLS_1_1) {
if (ssl->rec.len < (ssl->deMacSize + 1 + ssl->deBlockSize)) {
ssl->err = SSL_ALERT_BAD_RECORD_MAC;
psTraceInfo("Ciphertext length failed sanity\n");
goto encodeResponse;
}
} else {
if (ssl->rec.len < (ssl->deMacSize + 1)) {
ssl->err = SSL_ALERT_BAD_RECORD_MAC;
psTraceInfo("Ciphertext length failed sanity\n");
goto encodeResponse;
}
}
#else
if (ssl->rec.len < (ssl->deMacSize + 1)) {
ssl->err = SSL_ALERT_BAD_RECORD_MAC;
psTraceInfo("Ciphertext length failed sanity\n");
goto encodeResponse;
}
#endif /* USE_TLS_1_1 */
}
/* CT to PT */
if (ssl->decrypt(ssl, c, ctStart, ssl->rec.len) < 0) {
ssl->err = SSL_ALERT_DECRYPT_ERROR;
psTraceInfo("Couldn't decrypt record data 2\n");
goto encodeResponse;
}
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 = ctStart + 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 = ctStart + 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 (ssl->majVer == SSL3_MAJ_VER && ssl->minVer == SSL3_MIN_VER &&
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 (ssl->flags & SSL_FLAGS_TLS_1_1) {
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 && ssl->majVer == TLS_MAJ_VER &&
ssl->minVer >= TLS_MIN_VER) {
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 ((ssl->flags & SSL_FLAGS_TLS_1_1) && (ssl->deBlockSize > 1)) {
ctStart += 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) {
/* Run this helper regardless of error status thus far */
rc = addCompressCount(ssl, padLen);
if (macError == 0) {
#ifndef USE_CL_DIGESTS
psDigestContext_t md;
unsigned char tmp[128];
switch (ssl->deMacSize) {
#ifdef USE_SHA256
case SHA256_HASH_SIZE:
psSha256Init(&md.sha256);
while (rc > 0) {
psSha256Update(&md.sha256, tmp, 64);
rc--;
}
psSha256Final(&md.sha256, tmp);
break;
#endif
#ifdef USE_SHA384
case SHA384_HASH_SIZE:
psSha384Init(&md.sha384);
while (rc > 0) {
psSha384Update(&md.sha384, tmp, 128);
rc--;
}
psSha384Final(&md.sha384, tmp);
break;
#endif
#ifdef USE_SHA1
case SHA1_HASH_SIZE:
psSha1Init(&md.sha1);
while (rc > 0) {
psSha1Update(&md.sha1, tmp, 64);
rc--;
}
psSha1Final(&md.sha1, tmp);
break;
#endif
default:
psAssert(0);
break;
}
#else
/* With FIPS implementation,
psHmacSha*Tls functions shall be used to
mitigate Lucky13. */
#endif /* USE_CL_DIGESTS */
}
}
#endif /* LUCKY13 */
if (ssl->verifyMac(ssl, ssl->rec.type, ctStart,
(uint32)(mac - ctStart), mac) < 0 || macError) {
ssl->err = SSL_ALERT_BAD_RECORD_MAC;
psTraceInfo("Couldn't verify MAC or pad of record data\n");
goto encodeResponse;
}
memset(mac, 0x0, ssl->deMacSize);
/* Record data starts at ctStart and ends at mac */
p = ctStart;
pend = mac;
} else {
/*
The record data is the entire record as there is no MAC or padding
*/
p = ctStart;
pend = mac = ctStart + ssl->rec.len;
}
#ifdef USE_ZLIB_COMPRESSION
/* Currently only supporting compression of FINISHED message.
Compressed application data is handled outside MatrixSSL.
Re-handshakes are not allowed with compression and we've
incremented ssl->compression if we've already been through here
so we'll know */
if (ssl->compression == 2 && ssl->flags & SSL_FLAGS_READ_SECURE &&
ssl->rec.type == SSL_RECORD_TYPE_HANDSHAKE) {
ssl->err = SSL_ALERT_INTERNAL_ERROR;
psTraceInfo("Re-handshakes not supported on compressed sessions\n");
goto encodeResponse;
}
if (ssl->compression && ssl->flags & SSL_FLAGS_READ_SECURE &&
ssl->rec.type == SSL_RECORD_TYPE_HANDSHAKE) {
/* TODO - handle the cases below where the buffer has to grow */
currLen = ssl->inflate.total_out;
preInflateLen = (int32)(pend - p);
ssl->zlibBuffer = psMalloc(ssl->bufferPool, preInflateLen +
MATRIX_INFLATE_FINISHED_OH);
if (ssl->zlibBuffer == NULL) {
ssl->err = SSL_ALERT_INTERNAL_ERROR;
psTraceInfo("Couldn't allocate compressed scratch pad\n");
goto encodeResponse;
}
memset(ssl->zlibBuffer, 0, preInflateLen + MATRIX_INFLATE_FINISHED_OH);
if (preInflateLen > 0) { /* zero length record possible */
/* psTraceBytes("pre inflate", ctStart, preInflateLen); */
ssl->inflate.next_in = ctStart;
ssl->inflate.avail_in = preInflateLen;
ssl->inflate.next_out = ssl->zlibBuffer;
ssl->inflate.avail_out = SSL_MAX_PLAINTEXT_LEN;
if ((zret = inflate(&ssl->inflate, Z_SYNC_FLUSH)) != Z_OK) {
ssl->err = SSL_ALERT_INTERNAL_ERROR;
psFree(ssl->zlibBuffer, ssl->bufferPool); ssl->zlibBuffer =NULL;
inflateEnd(&ssl->inflate);
psTraceIntInfo("ZLIB inflate failed %d\n", zret);
goto encodeResponse;
}
if (ssl->inflate.avail_in != 0) {
ssl->err = SSL_ALERT_INTERNAL_ERROR;
psFree(ssl->zlibBuffer, ssl->bufferPool); ssl->zlibBuffer =NULL;
inflateEnd(&ssl->inflate);
psTraceInfo("ZLIB inflate didn't work in one pass\n");
goto encodeResponse;
}
postInflateLen = ssl->inflate.total_out - currLen;
/* psTraceBytes("post inflate", ssl->zlibBuffer,
postInflateLen); */
if (postInflateLen <= preInflateLen) {
/* Easy case where compressed data was actually larger.
Don't need to update c or inlen because the next
good data is already correctly being pointed to */
memcpy(p, ssl->zlibBuffer, postInflateLen);
mac = p + postInflateLen;
pend = mac;
} else {
/* Data expanded. Fit it in the buffer and update all
the associated lengths and pointers
Add back in the MAC and pad to preInflate so we're
looking at the useful boundaries of the buffers */
preInflateLen += (int32)(c - mac);
/* reusing currLen var. Now the difference in lengths */
currLen = postInflateLen - preInflateLen;
if ((int32)(c - ssl->inbuf) == ssl->inlen) {
/* Good, this was the only data in the buffer. Just
check there is room to append */
if ((ssl->insize - ssl->inlen) >= postInflateLen) {
memcpy(p, ssl->zlibBuffer, postInflateLen);
c += currLen;
mac = p + postInflateLen;
pend = mac;
} else {
/* Only one here but not enough room to store it */
ssl->err = SSL_ALERT_INTERNAL_ERROR;
psFree(ssl->zlibBuffer, ssl->bufferPool);
ssl->zlibBuffer = NULL;
inflateEnd(&ssl->inflate);
psTraceInfo("ZLIB buffer management needed\n");
goto encodeResponse;
}
} else {
/* Push any existing data further back in the buffer to
make room for this uncompressed length. c pointing
to start of next record that needs to be pushed
back. currLen is how far to push back.
p pointing to where zlibBuffer should copy
to. postInflateLen is amount to copy there. */
if (currLen < (ssl->insize - ssl->inlen)) {
/* Good, fits in current buffer. Move all valid
data back currLen */
memmove(c + currLen, c,
ssl->inlen - (int32)(c - ssl->inbuf));
c += currLen;
memcpy(p, ssl->zlibBuffer, postInflateLen);
mac = p + postInflateLen;
pend = mac;
} else {
/* Need to realloc more space AND push the records
back */
ssl->err = SSL_ALERT_INTERNAL_ERROR;
psFree(ssl->zlibBuffer, ssl->bufferPool);
ssl->zlibBuffer = NULL;
inflateEnd(&ssl->inflate);
psTraceInfo("ZLIB buffer management needed\n");
goto encodeResponse;
}
}
/* Finally increase inlen and *len to account for it now */
ssl->inlen += currLen;
*len += currLen;
ssl->rec.len += currLen;
}
}
psFree(ssl->zlibBuffer, ssl->bufferPool); ssl->zlibBuffer = NULL;
/* Will not need the context any longer since FINISHED is the only
supported message */
inflateEnd(&ssl->inflate);
ssl->compression = 2;
}
#endif /* USE_ZLIB_COMPRESSION */
/* 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;
psTraceInfo("Record overflow\n");
goto encodeResponse;
}
} else {
if ((int32)(pend - p) > ssl->maxPtFrag) {
ssl->err = SSL_ALERT_RECORD_OVERFLOW;
psTraceInfo("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:
psTraceStrHs(">>> %s parsing CHANGE_CIPHER_SPEC message\n",
(ssl->flags & SSL_FLAGS_SERVER) ? "Server" : "Client");
/*
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;
psTraceInfo("Invalid length for CipherSpec\n");
goto encodeResponse;
}
if (*p == 1) {
p++;
} else {
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
psTraceInfo("Invalid value for CipherSpec\n");
goto encodeResponse;
}
#ifdef USE_DTLS
if (ssl->flags & SSL_FLAGS_DTLS) {
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 */
*remaining = *len - (c - origbuf);
*buf = c;
/*
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;
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;
psTraceInfo("Error in length of alert record\n");
goto encodeResponse;
}
*alertLevel = *p; p++;
*alertDescription = *p; p++;
*len = 2;
#ifdef USE_SSL_HANDSHAKE_MSG_TRACE
if (ssl->flags & SSL_FLAGS_SERVER) {
psTraceHs(">>> Server");
} else {
psTraceHs(">>> Client");
}
if (*alertDescription == SSL_ALERT_CLOSE_NOTIFY) {
psTraceHs(" parsing ALERT (CLOSE_NOTIFY) message\n");
} else {
psTraceHs(" parsing ALERT message\n");
}
#endif
psTraceIntInfo("Received alert %d\n", (int32)(*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_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;
}
#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.
*/
#ifdef 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)) {
psTraceHs(">>> 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;
}
#ifdef 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:
psTraceIntInfo("Unknown return %d from parseSSLHandshake!\n", rc);
if (ssl->err == SSL_ALERT_NONE) {
ssl->err = SSL_ALERT_INTERNAL_ERROR;
}
goto encodeResponse;
}
break;
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 (ctStart == 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;
/*
SECURITY - Clear the decoded incoming record from outbuf before encoding
the response into outbuf.
*/
memset(tmpout.buf, 0x0, tmpout.size);
#ifdef ENABLE_FALSE_START
}
#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
*/
rc = matrixSslEncodeClientHello(ssl, &tmpout, 0, 0, requiredLen, NULL,
&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) {
psTraceInfo("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;
#ifdef ENABLE_FALSE_START
}
#endif
return SSL_SEND_RESPONSE;
}
if (rc == SSL_FULL) {
#ifdef ENABLE_FALSE_START
/* We don't support growing outbuf in the false start 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 (ssl->flags & SSL_FLAGS_TLS_1_1) {
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 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 (!(ssl->flags & SSL_FLAGS_DTLS)) {
#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;
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;
psTraceInfo("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) {
psTraceInfo("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) {
psTraceInfo("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 (ssl->flags & SSL_FLAGS_DTLS) {
if (end - c < 5) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("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_REQEST 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;
psTraceInfo("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 (ssl->flags & SSL_FLAGS_DTLS) {
if (end - c < 8) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("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;
psTraceInfo("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
/* 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 */
psTraceInfo("Expecting CERTIFICATE_STATUS message\n");
ssl->err = SSL_ALERT_UNEXPECTED_MESSAGE;
return MATRIXSSL_ERROR;
#endif
#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 */
#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 (ssl->flags & SSL_FLAGS_DTLS) {
#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;
psTraceInfo("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,
(ssl->flags & SSL_FLAGS_SERVER) ? 0 : SSL_FLAGS_SERVER) <= 0) {
psTraceInfo("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, -1) <= 0) {
psTraceInfo("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) {
if (end - c < 3) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("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 (ssl->flags & SSL_FLAGS_DTLS) {
if (end - c < 8) {
ssl->err = SSL_ALERT_DECODE_ERROR;
psTraceInfo("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);
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;
}
/*
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;
psTraceInfo("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;
psTraceInfo("Invalid handshake length\n");
return MATRIXSSL_ERROR;
}
}
#ifdef USE_DTLS
if (ssl->flags & SSL_FLAGS_DTLS) {
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 */
rc = parseClientHello(ssl, &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;
/******************************************************************************/
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 */
psTraceHs(">>> Client parsing HELLO_REQUEST message\n");
if (end - c != 0) {
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
psTraceInfo("Invalid hello request message\n");
return MATRIXSSL_ERROR;
}
#ifdef SSL_REHANDSHAKES_ENABLED
if (ssl->rehandshakeCount <= 0) {
ssl->err = SSL_ALERT_NO_RENEGOTIATION;
psTraceInfo("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;
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
case SSL_HS_CERTIFICATE_STATUS:
psTraceHs(">>> Client parsing CERTIFICATE_STATUS message\n");
rc = parseCertificateStatus(ssl, hsLen, &c, end);
if (rc < 0) {
return rc;
}
break;
#endif /* USE_OCSP */
/******************************************************************************/
#ifdef USE_STATELESS_SESSION_TICKETS
case SSL_HS_NEW_SESSION_TICKET:
psTraceHs(">>> Client parsing NEW_SESSION_TICKET message\n");
if (hsLen < 6) {
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
psTraceInfo("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;
psTraceInfo("Invalid NewSessionTicket message\n");
return MATRIXSSL_ERROR;
}
if (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;
}
break;
/******************************************************************************/
#ifndef 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;
}
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:
psTraceHs(">>> Client parsing HELLO_VERIFY_REQUEST message\n");
/*
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;
psTraceInfo("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;
psTraceInfo("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;
psTraceInfo("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 (ssl->flags & SSL_FLAGS_DTLS) {
ssl->lastMsn = msn; /* MSN of last message sucessfully parsed */
}
#endif /* USE_DTLS */
/*
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;
}
return rc;
}
/******************************************************************************/
#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;
psX509Cert_t *cert, *p;
/*
Extract the binary cert message into the cert structure
*/
if ((parseLen = psX509ParseCert(ssl->hsPool, c, certLen, &cert, 0)) < 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 */
/******************************************************************************/