Files
mars-matrixssl/matrixssl/matrixsslNet.c
Janne Johansson 69b5f2c6c3 MatrixSSL 4.5.1
2022-07-29 12:30:12 +03:00

842 lines
25 KiB
C

/* matrixsslNet.c
*
* Socket-based networking with MatrixSSL.
*/
/*****************************************************************************
* Copyright (c) 2017 Rambus Inc. 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 "matrixsslNet.h"
#ifdef USE_PS_NETWORKING
# include "osdep_signal.h" /* Defines SIGTERM, etc. */
# include "osdep_sys_types.h"
# include "osdep_sys_socket.h"
# include "osdep_unistd.h"
# include "psUtil.h"
# ifndef MATRIXSSL_INTERACT_READBUF_SIZE
# define MATRIXSSL_INTERACT_READBUF_SIZE (1024 * 18)
# endif
# ifndef MATRIXSSL_INTERACT_MAX_TRANSFER
# define MATRIXSSL_INTERACT_MAX_TRANSFER 64000
# endif
# ifdef USE_MATRIX_NET_DEBUG
# include "osdep_stdio.h"
# define MATRIXSSL_NET_DEBUGF(...) Printf(__VA_ARGS__)
# else
# define MATRIXSSL_NET_DEBUGF(...) do {} while (0)
# endif
/* Defines for constants of TLS record format.
These are needed when readahead mode is turned off. */
#define MSI_TLS_REC_LEN 5 /* Record length of TLS.
1 * type, 2 * version, 2 * content length. */
#define MSI_TLS_REC_CONTENT_LEN_HIGH 3 /* Offset from beginning. */
#define MSI_TLS_REC_CONTENT_LEN_LOW 4 /* Offset from beginning. */
#define MSI_TLS_MAX_CONTENT_LEN (16384 + 2048) /* See RFC 5246. */
void matrixSslInteractBegin(matrixSslInteract_t *i, ssl_t *ssl,
psSocket_t *sock)
{
/* Clear all except the ssl storage. */
Memset(i, 0, sizeof(*i));
i->ssl = ssl;
i->sock = sock;
i->prev_rc = PS_SUCCESS;
i->handshake_complete = PS_FALSE;
i->must_send = 0;
}
/* Adjust amount to read according to record header size or
record size if in no read-ahead mode. */
static
int32
matrixSslInteractBeforeSocketRead(matrixSslInteract_t *i, int32 len)
{
if (!i->no_readahead)
{
/* No adjustment appropriate, just read input as much as possible. */
return len;
}
else
{
/* Adjust amount of input to read to hold either record header or
record content. */
if (i->rechdrlen < MSI_TLS_REC_LEN)
{
/* Reading record header: read at the most missing part of
5 header bytes, less if read buffer is too small
(should not happen). */
i->hdrread = 1;
return PS_MIN(len, MSI_TLS_REC_LEN - i->rechdrlen);
}
if (i->recleft > 0)
{
/* Constrain amount of bytes to process according to the record
length left. */
return PS_MIN(i->recleft, len);
}
}
/* Fallback: The rechdrlen or recleft is not set properly.
We fallback to process the input just as if readahead was not set.
Then core of MatrixSSL will decide how to deal with the packet.
*/
return len;
}
/* Process result of socket read. */
static
void
matrixSslInteractAfterSocketRead(matrixSslInteract_t *i,
const unsigned char *buf, int32 transferred)
{
i->recvretry = 0; /* Typically: do not retry after receive. */
if (transferred <= 0)
{
return; /* No data available. */
}
if (i->no_readahead)
{
if (i->hdrread)
{
/* The request was for reading record header.
Process record header. */
i->hdrread = 0;
/* Keep a copy of the record header inside matrixsslNet.
We use this private copy to determine record length. */
Memcpy(i->rechdr + i->rechdrlen, buf, transferred);
i->rechdrlen += transferred;
if (i->rechdrlen == MSI_TLS_REC_LEN)
{
int32 reclen;
/* Interpret record length (the record type and the TLS version
are ignored here.) */
reclen = i->rechdr[MSI_TLS_REC_CONTENT_LEN_HIGH] << 8;
reclen |= i->rechdr[MSI_TLS_REC_CONTENT_LEN_LOW];
/* Check we do not read beyond TLS maximal encrypted record
length. */
if (reclen <= MSI_TLS_MAX_CONTENT_LEN && reclen != 0)
{
/* We have received record header that is of a valid
size. We constrain amount of data to transfer
with the next transfer to the record size. */
if (i->recleft == 0)
{
i->recleft = reclen;
i->recvretry = 1; /* After providing the record
header bytes to matrixssl,
continue with the read for record
content. */
MATRIXSSL_NET_DEBUGF(
"Parsed record header: need to read %u "
"bytes of record content.\n",
(unsigned) reclen);
return;
}
}
}
else
{
/* Full record header not received yet. */
}
}
if (i->no_readahead && i->recleft)
{
psAssert(transferred <= i->recleft);
i->recleft -= (uint32_t) transferred;
if (i->recleft == 0)
{
/* Entire record received =>
Coming up next: read a record header again.
Set length of record header read to 0. */
i->rechdrlen = 0;
}
}
}
}
/* Check if we should retry receive. */
static
psBool_t
matrixSslInteractSocketReadRetry(matrixSslInteract_t *i, int32 rc)
{
if (i->recvretry)
{
/* We have performed partial read for record header, and
we should retry the reading in case MatrixSSL agrees
there is a partial record. */
if (rc == MATRIXSSL_REQUEST_RECV)
{
/* We have read a record header and passed it to MatrixSSL.
MatrixSSL returned that it needs to receive more bytes.
In this case we will read the record itself,
without returning MATRIXSSL_REQUEST_RECV.
*/
i->recvretry = 0; /* Mark we do not do retry again. */
MATRIXSSL_NET_DEBUGF("Record header read. Repeating "
"receive for record content (%d bytes)"
"\n",
(int) i->recleft);
return true;
}
}
return false; /* Common case: no read retry. */
}
static int32 matrixSslInteractGotData(matrixSslInteract_t *i, int32 rc)
{
/* Cook the received data from behalf of caller.
The data we handle here are alerts. */
MATRIXSSL_NET_DEBUGF("Got Data: rc=%d, bytes left: %d\n",
rc, (int) i->receive_len_left);
if (rc != MATRIXSSL_RECEIVED_ALERT)
{
return rc;
}
i->ch2[0] = 255;
i->ch2[1] = 255;
rc = matrixSslInteractRead(i, i->ch2, 2);
MATRIXSSL_NET_DEBUGF("Alert code read: %d:%d\n", i->ch2[0], i->ch2[1]);
if (rc < 2)
{
MATRIXSSL_NET_DEBUGF("Broken Alert\n");
i->ch2[0] = 255; /* 255, 255 is used as generic code
for alerts not processed correctly. */
return MATRIXSSL_RECEIVED_ALERT;
}
i->last_alert_level = i->ch2[0];
/* Close connection if: */
if (i->ch2[0] == 1 && i->ch2[1] == 0)
{
return MATRIXSSL_REQUEST_CLOSE;
}
return MATRIXSSL_RECEIVED_ALERT;
}
static
int32 matrixSslInteractInt3(matrixSslInteract_t *i,
int can_send, int can_receive,
int can_receive_local)
{
ssize_t transferred;
unsigned char *buf;
int32 rc;
uint32_t transferlen;
# ifdef USE_MATRIX_NET_DEBUG
int block = 1;
# endif /* USE_MATRIX_NET_DEBUG */
/* If there is a write ongoing, resume it. */
if (i->must_send)
{
goto must_send;
}
if (i->receive_buf && i->receive_len_left == 0)
{
/* Continuation of previous receive operation: */
uint32_t len = i->receive_len;
buf = i->receive_buf - len;
again_zero_app_data:
rc = matrixSslProcessedData(i->ssl, &buf, &len);
if (buf != NULL && len != 0)
{
MATRIXSSL_NET_DEBUGF("processed some data, but pending processing:\n"
"rc=%d buf=%p len=%u\n",
(int) rc, (const void *) buf, (unsigned int) len);
if (rc == MATRIXSSL_APP_DATA ||
rc == MATRIXSSL_RECEIVED_ALERT)
{
i->receive_buf = buf;
i->receive_len = len;
i->receive_len_left = len;
rc = matrixSslInteractGotData(i, rc);
if (i->send_close_notify == false)
{
return rc;
}
}
else
{
return PS_FAILURE;
}
}
if (rc == MATRIXSSL_APP_DATA && len == 0)
{
MATRIXSSL_NET_DEBUGF("ignored zero length APP data\n");
goto again_zero_app_data;
}
/* Mark buffer as processed. */
i->receive_buf = NULL;
i->receive_len = 0;
i->receive_len_left = 0;
MATRIXSSL_NET_DEBUGF("Acked processed data, got: rc=%d\n", rc);
if (i->send_close_notify == false && rc != MATRIXSSL_REQUEST_RECV)
{
return rc;
}
}
else if (can_receive_local && i->receive_buf && i->receive_len_left > 0)
{
MATRIXSSL_NET_DEBUGF("Signal more data ready for reading.\n");
/* Maybe there is remaining application data? */
rc = MATRIXSSL_APP_DATA;
if (matrixSslHandshakeIsComplete(i->ssl))
{
i->handshake_complete = PS_TRUE;
}
if (i->send_close_notify == false)
{
return rc;
}
}
if (can_send && i->send_len_left == 0)
{
int32 len;
len = matrixSslGetOutdata(i->ssl, &buf);
if (len > 0)
{
MATRIXSSL_NET_DEBUGF("To be sent: %d bytes\n", (int) len);
i->send_buf = buf;
i->send_len_left = i->send_len = len;
}
# ifdef USE_MATRIX_NET_DEBUG
block = 0;
# endif /* USE_MATRIX_NET_DEBUG */
}
if (can_send && i->send_len_left > 0)
{
int32 len;
must_send:
buf = i->send_buf;
len = i->send_len_left;
transferred = psSocketWriteData(
i->sock, buf,
len < MATRIXSSL_INTERACT_MAX_TRANSFER ? len :
MATRIXSSL_INTERACT_MAX_TRANSFER, 0);
i->must_send = 0;
if (transferred == PS_EAGAIN)
{
i->must_send = 1;
return MATRIXSSL_REQUEST_SEND;
}
if (transferred < 0)
{
return PS_PLATFORM_FAIL;
}
MATRIXSSL_NET_DEBUGF("Sent%s: %d bytes\n", block ? " cont" : "",
(int) transferred);
if (i->send_close_notify)
{
MATRIXSSL_NET_DEBUGF("Successfully sent close_notify\n");
i->send_close_notify = PS_FALSE;
}
i->send_buf += transferred;
i->send_len_left -= transferred;
if (i->send_len_left > 0)
{
i->must_send = 1;
return MATRIXSSL_REQUEST_SEND;
}
rc = matrixSslSentData(i->ssl, (uint32) i->send_len);
if (rc < 0 || rc == MATRIXSSL_REQUEST_CLOSE ||
rc == MATRIXSSL_HANDSHAKE_COMPLETE ||
rc == MATRIXSSL_REQUEST_SEND)
{
if (rc == MATRIXSSL_HANDSHAKE_COMPLETE)
{
#ifdef ENABLE_FALSE_START
/* If false start is enabled, there may be
some data from client received during
handshake. Query for such data. */
unsigned char *buf = NULL;
uint32_t len = 0;
i->handshake_complete = PS_TRUE;
rc = matrixSslReceivedData(i->ssl, 0, &buf, &len);
while(rc == MATRIXSSL_APP_DATA && len == 0)
{
MATRIXSSL_NET_DEBUGF("Ignored zero length false start data record");
rc = matrixSslProcessedData(i->ssl, &buf, &len);
}
if (rc == MATRIXSSL_APP_DATA && len > 0)
{
MATRIXSSL_NET_DEBUGF("Received false start data (%u bytes)",
(unsigned) len);
i->receive_buf = buf;
i->receive_len = len;
i->receive_len_left = len;
return MATRIXSSL_HANDSHAKE_COMPLETE;
}
#endif /* ENABLE_FALSE_START */
return MATRIXSSL_HANDSHAKE_COMPLETE;
}
return rc;
}
}
else
{
rc = PS_SUCCESS;
}
if (can_receive)
{
int32 len;
receive_repeat:
# ifdef MATRIXSSL_INTERACT_READBUF_SIZE
/* Use MatrixsslNet's read buffer size. */
len = matrixSslGetReadbufOfSize(i->ssl,
MATRIXSSL_INTERACT_READBUF_SIZE,
&buf);
# else
/* Use standard buffer size (usually SSL_DEFAULT_IN_BUF_SIZE). */
len = matrixSslGetReadbuf(i->ssl, &buf);
#endif
if (len <= 0)
{
return PS_PLATFORM_FAIL;
}
/* Read from socket, either up-to full read buffer, or
record header or record content size if in no_readahead mode. */
transferlen = matrixSslInteractBeforeSocketRead(i, len);
MATRIXSSL_NET_DEBUGF("Reading input from socket, up-to %u bytes%s\n",
(unsigned) transferlen,
i->hdrread ?
" (the record header)" :
(i->recleft > 0 ?
" (the remaining part of a record)" : ""));
transferred = (int32) psSocketReadData(i->sock, buf, transferlen, 0);
matrixSslInteractAfterSocketRead(i, buf, transferred);
if (transferred >= 0)
{
MATRIXSSL_NET_DEBUGF("Received from peer %d bytes\n",
(int) transferred);
}
if (transferred > 0)
{
rc = matrixSslReceivedData(i->ssl,
(int32) transferred,
&buf, (uint32 *) &len);
/* Check if there are more read operations to perform. */
if (matrixSslInteractSocketReadRetry(i, rc))
{
goto receive_repeat;
}
if (rc == MATRIXSSL_APP_DATA ||
rc == MATRIXSSL_RECEIVED_ALERT)
{
i->receive_buf = buf;
i->receive_len = len;
i->receive_len_left = len;
return matrixSslInteractGotData(i, rc);
}
if (rc == MATRIXSSL_APP_DATA_COMPRESSED)
{
return PS_PLATFORM_FAIL; /* Unsupported. */
}
# ifdef USE_EXT_CLIENT_CERT_KEY_LOADING
if (rc == PS_PENDING && matrixSslNeedClientCert(i->ssl))
{
i->num_last_read_transferred = transferred;
MATRIXSSL_NET_DEBUGF("Client cert needed in response to " \
"CertificateRequest. Returning PS_PENDING\n");
return PS_PENDING;
}
# endif /* USE_EXT_CLIENT_CERT_KEY_LOADING */
}
else if (transferred == 0)
{
/* Connection has closed down unexpectedly. */
MATRIXSSL_NET_DEBUGF("Connection cut off.\n");
return MATRIXSSL_NET_DISCONNECTED;
}
else if (transferred == PS_EAGAIN)
{
return PS_SUCCESS; /* This operation ok, but more data needed. */
}
else if (transferred == PS_MEM_FAIL)
{
return PS_MEM_FAIL;
}
else if (transferred < 0)
{
return PS_PLATFORM_FAIL;
}
}
return rc;
}
static
psBool_t handshake_is_complete(matrixSslInteract_t *i)
{
#ifdef ENABLE_SECURE_REHANDSHAKES
if (matrixSslRehandshaking(i->ssl))
{
return PS_FALSE;
}
#endif
return i->handshake_complete;
}
int32 matrixSslInteract(matrixSslInteract_t *i, int can_send, int can_receive)
{
int32 rc = matrixSslInteractInt3(i, can_send, can_receive, can_receive);
if (rc == PS_SUCCESS && !handshake_is_complete(i))
{
/* If handshaking, guide the caller to wait reading. */
rc = MATRIXSSL_REQUEST_RECV;
}
else if (rc == MATRIXSSL_HANDSHAKE_COMPLETE)
{
i->handshake_complete = PS_TRUE;
}
i->prev_rc = rc;
return rc;
}
int32 matrixSslInteract3(matrixSslInteract_t *i,
int can_send_net, int can_receive_net,
int can_receive_local)
{
int32 rc = matrixSslInteractInt3(i, can_send_net, can_receive_net,
can_receive_local);
if (rc == PS_SUCCESS && !handshake_is_complete(i))
{
/* If handshaking, guide the caller to wait reading. */
rc = MATRIXSSL_REQUEST_RECV;
}
else if (rc == MATRIXSSL_HANDSHAKE_COMPLETE)
{
i->handshake_complete = PS_TRUE;
}
i->prev_rc = rc;
return rc;
}
int32 matrixSslInteractHandshake(matrixSslInteract_t *i,
int can_send, int can_receive)
{
int32 rc = PS_SUCCESS;
while (rc == PS_SUCCESS && !handshake_is_complete(i))
{
rc = matrixSslInteract(i, can_send, can_receive);
if (rc == MATRIXSSL_HANDSHAKE_COMPLETE)
{
rc = PS_SUCCESS;
}
}
return rc;
}
size_t matrixSslInteractReadLeft(matrixSslInteract_t *i)
{
return i->receive_len_left;
}
int32 matrixSslInteractRead(matrixSslInteract_t *i,
unsigned char *target,
size_t max_length)
{
size_t total_read = 0;
size_t real = matrixSslInteractReadLeft(i);
if (real > max_length)
{
real = max_length;
}
if (real > MATRIXSSL_INTERACT_MAX_TRANSFER)
{
real = MATRIXSSL_INTERACT_MAX_TRANSFER;
}
Memcpy(target, i->receive_buf, real);
i->receive_buf += real;
i->receive_len_left -= real;
total_read = real;
if (i->receive_buf && i->receive_len_left == 0)
{
MATRIXSSL_NET_DEBUGF("Read single record (last_part_len=%d).\n",
(int) total_read);
}
else if (i->receive_buf)
{
MATRIXSSL_NET_DEBUGF("Remaining application data: %d bytes "
"(this_part_len=%d)\n",
(int) i->receive_len_left,
(int) total_read);
}
return total_read;
}
int32 matrixSslInteractPeek(matrixSslInteract_t *i,
unsigned char *target,
size_t max_length)
{
size_t real = matrixSslInteractReadLeft(i);
if (real > max_length)
{
real = max_length;
}
if (real > MATRIXSSL_INTERACT_MAX_TRANSFER)
{
real = MATRIXSSL_INTERACT_MAX_TRANSFER;
}
Memcpy(target, i->receive_buf, real);
return real;
}
int32 matrixSslInteractWrite(matrixSslInteract_t *i,
const unsigned char *target,
size_t in_len)
{
unsigned char *buf;
int32 bytesToEncrypt;
int32 rc;
int32 out_len;
rc = matrixSslGetWritebuf(i->ssl, &buf, in_len);
if (rc <= 0)
{
return rc;
}
bytesToEncrypt = rc;
if (bytesToEncrypt > in_len)
{
bytesToEncrypt = in_len;
}
Memcpy(buf, target, bytesToEncrypt);
/* Encrypt. */
rc = matrixSslEncodeWritebuf(i->ssl, bytesToEncrypt);
if (rc < 0)
{
MATRIXSSL_NET_DEBUGF("couldn't encode data %d\n", rc);
return rc;
}
if (i->send_len_left == 0)
{
/* Encode into TLS records. */
out_len = matrixSslGetOutdata(i->ssl, &buf);
if (out_len > 0)
{
MATRIXSSL_NET_DEBUGF("matrixSslInteractWrite: " \
"%d plaintext bytes (from a total of %zu) " \
"encoded into %d bytes\n",
bytesToEncrypt, in_len, out_len);
i->send_buf = buf;
i->send_len_left = i->send_len = out_len;
}
rc = out_len;
}
/* Store how many plaintext bytes we were able to encrypt
and encode. Caller can use this to measure progress. */
i->last_encoded_pt_bytes = bytesToEncrypt;
return rc;
}
int matrixSslInteractRemoveFd(matrixSslInteract_t *i)
{
if (i->sock)
{
int fd = i->sock->internal_fd;
i->sock->internal_fd = -1;
return fd;
}
return -1;
}
void matrixSslInteractClose(matrixSslInteract_t *i)
{
if (i->sock)
{
psSocketShutdown(i->sock, 0);
}
Memset(i, 0, sizeof(*i));
}
void matrixSslInteractCloseErr(matrixSslInteract_t *i, int32 status)
{
if (i->sock)
{
psSocketShutdown(i->sock, 0);
}
Memset(i, 0, sizeof(*i));
}
/**/
int32 matrixSslInteractSendCloseNotify(matrixSslInteract_t *i)
{
ssl_t *ssl;
int32 rc;
MATRIXSSL_NET_DEBUGF("Sending connection closure alert.\n");
ssl = i->ssl;
rc = matrixSslEncodeClosureAlert(ssl);
if (rc >= 0)
{
i->send_close_notify = PS_TRUE;
rc = matrixSslInteract3(i, PS_TRUE, PS_FALSE, PS_TRUE);
if (rc < 0)
{
return rc;
}
}
return rc;
}
int32 matrixSslInteractReceiveCloseNotify(matrixSslInteract_t *i)
{
int32 rc;
rc = matrixSslInteract(i, PS_FALSE, PS_TRUE);
if (rc < 0)
{
return rc;
}
return rc;
}
# ifdef USE_CLIENT_SIDE_SSL
int32 matrixSslInteractBeginConnected(matrixSslInteract_t *msi_p,
const char *hostname, const char *port,
psSocketOptions_t opts,
const psSocketFunctions_t *func,
const sslKeys_t *keys,
sslSessionId_t *sid,
const psCipher16_t cipherSpec[],
uint8_t cSpecLen,
sslCertCb_t certCb,
const char *expectedName,
tlsExtension_t *extensions,
sslExtCb_t extCb,
sslSessOpts_t *options)
{
psSocket_t *sock;
int32 rc;
ssl_t *ssl = NULL;
Memset(msi_p, 0, sizeof(*msi_p));
rc = psSocketConnect(hostname, port, opts,
PS_SOCKET_STREAM, NULL, func, &sock);
if (rc == PS_SUCCESS)
{
/* Got connection, create SSL client session for it. */
rc = matrixSslNewClientSession(&ssl, keys, sid,
cipherSpec, cSpecLen,
certCb, expectedName,
extensions,
extCb, options);
if (rc < 0)
{
psSocketShutdown(sock, 0);
return rc; /* Failure. */
}
matrixSslInteractBegin(msi_p, ssl, sock);
MATRIXSSL_NET_DEBUGF("Connected and has SSL session, rc=%d\n", (int) rc);
return rc;
}
return rc; /* Failure. */
}
# endif /* USE_CLIENT_SIDE_SSL */
# ifdef USE_SERVER_SIDE_SSL
int32 matrixSslInteractBeginAccept(matrixSslInteract_t *msi_p,
psSocket_t *sock,
psSocketOptions_t opts,
const sslKeys_t *keys,
sslCertCb_t certCb,
sslSessOpts_t *options)
{
psSocket_t *new;
int32 rc;
ssl_t *ssl = NULL;
Memset(msi_p, 0, sizeof(*msi_p));
rc = psSocketAccept(sock, 0, &new);
if (rc != PS_SUCCESS)
{
return rc;
}
/* Got connection, create SSL server session for it. */
rc = matrixSslNewServerSession(&ssl, keys, certCb, options);
if (rc < PS_SUCCESS)
{
psSocketShutdown(new, 0);
return rc;
}
matrixSslInteractBegin(msi_p, ssl, new);
MATRIXSSL_NET_DEBUGF("Accepted and has SSL session, rc=%d ssl=%p\n",
(int) rc, ssl);
return rc;
}
# endif /* USE_SERVER_SIDE_SSL */
void matrixSslInteractSetReadahead(matrixSslInteract_t *i,
psBool_t readahead_on)
{
if (i)
{
i->no_readahead = !readahead_on;
}
}
#endif /* USE_PS_NETWORKING */
/* end of file matrixsslNet.c */