297 lines
9.7 KiB
C
297 lines
9.7 KiB
C
/**
|
|
* @file tlsSelectKeys.c
|
|
* @version $Format:%h%d$
|
|
*
|
|
* Client-side key and certificate selection.
|
|
*/
|
|
/*
|
|
* Copyright (c) 2013-2018 Rambus Inc.
|
|
* Copyright (c) PeerSec Networks, 2002-2011
|
|
* All Rights Reserved
|
|
*
|
|
* The latest version of this code is available at http://www.matrixssl.org
|
|
*
|
|
* This software is open source; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This General Public License does NOT permit incorporating this software
|
|
* into proprietary programs. If you are unable to comply with the GPL, a
|
|
* commercial license for this software may be purchased from Rambus at
|
|
* http://www.rambus.com/
|
|
*
|
|
* This program is distributed in WITHOUT ANY WARRANTY; without even the
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
* See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
*/
|
|
/******************************************************************************/
|
|
|
|
#include "matrixsslImpl.h"
|
|
|
|
#ifndef DEBUG_TLS_SELECT_KEYS
|
|
/* # define DEBUG_TLS_SELECT_KEYS */
|
|
#endif
|
|
|
|
#ifdef USE_IDENTITY_CERTIFICATES
|
|
|
|
psBool_t matrixSslSetClientIdentity(ssl_t *ssl,
|
|
const sslKeys_t *keys)
|
|
{
|
|
/* No keys, or no identities -> fail. In case of multiple, use the first. */
|
|
if (keys == NULL || keys->identity == NULL)
|
|
return PS_FALSE;
|
|
|
|
# ifdef USE_CLIENT_SIDE_SSL
|
|
ssl->sec.certMatch = 1;
|
|
# endif
|
|
ssl->chosenIdentity = keys->identity;
|
|
# ifdef USE_EXT_CLIENT_CERT_KEY_LOADING
|
|
/* The client has now set the keys to use - foward the state
|
|
machine accordingly, so that resumed certificateRequest packet
|
|
gets processed. */
|
|
(void) matrixSslClientCertUpdated(ssl);
|
|
(void) matrixSslClientPrivKeyUpdated(ssl);
|
|
#endif
|
|
return PS_TRUE;
|
|
}
|
|
|
|
const sslKeySelectInfo_t *matrixSslGetClientKeySelectInfo(ssl_t *ssl)
|
|
{
|
|
return &ssl->sec.keySelect;
|
|
}
|
|
|
|
static
|
|
int32_t checkSigAlg(ssl_t *ssl,
|
|
sslKeySelectInfo_t *keySelect,
|
|
uint32_t certSigAlg)
|
|
{
|
|
uint32_t check = 0;
|
|
|
|
if (USING_TLS_1_3(ssl))
|
|
{
|
|
check = keySelect->peerCertSigAlgMask;
|
|
}
|
|
else
|
|
{
|
|
check = keySelect->peerSigAlgMask;
|
|
}
|
|
|
|
if (check != 0 && !peerSupportsSigAlg(certSigAlg, check))
|
|
{
|
|
# ifdef DEBUG_TLS_SELECT_KEYS
|
|
psTraceIntInfo("identityCb: invalid cert sig algorithm %x ",
|
|
cert->sigAlgorithm);
|
|
psTraceIntInfo("Not found in list: %x\n", check);
|
|
# endif /* DEBUG_TLS_SELECT_KEYS */
|
|
return PS_FAILURE;
|
|
}
|
|
|
|
return PS_SUCCESS;
|
|
}
|
|
|
|
/* Choose a client cert and key from the ssl->keys->identity list. */
|
|
static
|
|
int32_t chooseFromLoadedKeys(ssl_t *ssl, sslKeySelectInfo_t *keySelect)
|
|
{
|
|
psBool_t found = PS_FALSE;
|
|
int i;
|
|
int32_t rc;
|
|
sslIdentity_t *id;
|
|
|
|
/* RFC 5246, section 7.4.4: "Any certificates provided by the
|
|
client MUST be signed using a hash/signature algorithm pair
|
|
found in supported_signature_algorithms.". Check our
|
|
certificate chain, and set certMatch = 1 if a suitable
|
|
cert is found. */
|
|
if (keySelect->nCas == 0)
|
|
{
|
|
/*
|
|
Server did not specify CA's. Use the first key that can
|
|
is compatible with the server's supported_signature_algorithms.
|
|
*/
|
|
id = ssl->keys->identity;
|
|
do
|
|
{
|
|
rc = checkSigAlg(ssl,
|
|
keySelect,
|
|
id->cert->sigAlgorithm);
|
|
if (rc == PS_SUCCESS)
|
|
{
|
|
ssl->chosenIdentity = id;
|
|
# ifdef USE_CLIENT_SIDE_SSL
|
|
ssl->sec.certMatch = 1;
|
|
# endif
|
|
found = PS_TRUE;
|
|
break;
|
|
}
|
|
id = id->next;
|
|
}
|
|
while (id != NULL);
|
|
}
|
|
else
|
|
{
|
|
for (found = PS_FALSE, i = 0; !found && i < keySelect->nCas; i++)
|
|
{
|
|
# ifdef USE_CERT_PARSE
|
|
sslIdentity_t *has;
|
|
psX509Cert_t *cert;
|
|
|
|
for (has = ssl->keys->identity; !found && has; has = has->next)
|
|
{
|
|
/* traverse up chains to find accepted issuer */
|
|
for (cert = has->cert; cert; cert = cert->next)
|
|
{
|
|
if (cert->issuer.dnencLen == keySelect->caNameLens[i]
|
|
&& Memcmp(cert->issuer.dnenc,
|
|
keySelect->caNames[i],
|
|
keySelect->caNameLens[i]) == 0)
|
|
{
|
|
rc = checkSigAlg(ssl,
|
|
keySelect,
|
|
cert->sigAlgorithm);
|
|
if (rc != PS_SUCCESS)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* This chain was found to be fruitful -
|
|
issuer is withing the path, and algorithms
|
|
are fine. */
|
|
ssl->chosenIdentity = has;
|
|
# ifdef USE_CLIENT_SIDE_SSL
|
|
ssl->sec.certMatch = 1;
|
|
# endif
|
|
found = PS_TRUE;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
# ifdef DEBUG_TLS_SELECT_KEYS
|
|
psTraceInfo("identityCb: issuer name mismatch\n");
|
|
psTraceBytes("has: ",
|
|
(const unsigned char*)cert->issuer.dnenc,
|
|
cert->issuer.dnencLen);
|
|
psTraceBytes("req: ",
|
|
keySelect->caNames[i],
|
|
keySelect->caNameLens[i]);
|
|
# endif /* DEBUG_TLS_SELECT_KEYS */
|
|
}
|
|
}
|
|
}
|
|
#endif /* USE_CERT_PARSE */
|
|
}
|
|
}
|
|
|
|
if (ssl->chosenIdentity == NULL)
|
|
{
|
|
psTraceInfo("identityCb: no keys matched\n");
|
|
# ifndef USE_CERT_PARSE
|
|
/* XXX: Fallback to the first identity we have in case
|
|
suitable wasn't found due to limited functionality.... Or
|
|
should we send an empty certificate message in this case,
|
|
or maybe fallback to anonymous connection? */
|
|
ssl->chosenIdentity = ssl->keys->identity;
|
|
# endif
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
psAssert(ssl->chosenIdentity != NULL);
|
|
psTraceInfo("Chosen client auth key:\n ");
|
|
psTracePrintPubKeyTypeAndSize(ssl, &ssl->chosenIdentity->privKey);
|
|
return PS_SUCCESS;
|
|
}
|
|
return PS_FAILURE;
|
|
}
|
|
|
|
/** Choose the client key and cert to use in client authentication
|
|
in the current handshake.
|
|
|
|
There are three ways we can get client keys from the application.
|
|
The list here is in order of priority. For example, key loaded
|
|
via the identity callback will override keys loaded during session
|
|
initialization.
|
|
|
|
1. The application has registered an identity callback that will
|
|
load the keys on demand.
|
|
2. The application has loaded some keys with matrix*LoadKeys* during
|
|
session initialization. In this case, the keys are already in
|
|
ssl->keys->identity.
|
|
3. The application uses external client cert and key loading
|
|
(the defined USE_EXT_CLIENT_CERT_KEY_LOADING) is needed for this.
|
|
In this approach, the client will get the PS_PENDING return value
|
|
from matrixSslReceivedData and query matrixSslNeedClientCert
|
|
and matrixSslNeedPrivKey. If they return PS_TRUE, the client
|
|
will load the new keys into ssl->keys and call
|
|
matrixSslClientCertUpdated or matrixSslClientPrivKeyUpdated.
|
|
*/
|
|
int32_t matrixSslChooseClientKeys(ssl_t *ssl, sslKeySelectInfo_t *keySelect)
|
|
{
|
|
int32_t rc;
|
|
psBool_t found = PS_FALSE;
|
|
|
|
# ifdef USE_CLIENT_SIDE_SSL
|
|
ssl->sec.certMatch = 0;
|
|
# endif
|
|
|
|
/* Approach 1: the identity callback. */
|
|
if (ssl->sec.identityCb)
|
|
{
|
|
#if defined USE_SSL_HANDSHAKE_MSG_TRACE || defined USE_SSL_INFORMATIONAL_TRACE
|
|
static int num_called = 0;
|
|
|
|
psTraceIntInfo("matrixSslChooseClientKeys: trying callback (%d)\n",
|
|
num_called++);
|
|
#endif
|
|
rc = ssl->sec.identityCb(ssl, keySelect);
|
|
if (rc == PS_SUCCESS && ssl->chosenIdentity != NULL)
|
|
{
|
|
found = PS_TRUE;
|
|
}
|
|
}
|
|
|
|
/* Approach 2: pre-loaded keys. */
|
|
if (!found && ssl->keys->identity)
|
|
{
|
|
psTraceInfo("matrixSslChooseClientKeys: trying pre-loaded\n");
|
|
rc = chooseFromLoadedKeys(ssl, keySelect);
|
|
if (rc == PS_SUCCESS && ssl->chosenIdentity != NULL)
|
|
{
|
|
found = PS_TRUE;
|
|
}
|
|
}
|
|
|
|
/* Approach 3: external loading (if enabled). */
|
|
if (!found)
|
|
{
|
|
# ifdef USE_EXT_CLIENT_CERT_KEY_LOADING
|
|
/*
|
|
This should result in PS_PENDING to be sent to the
|
|
application after ServerHelloDone has been parsed,
|
|
to try "external" key and cert loading.
|
|
*/
|
|
ssl->extClientCertKeyStateFlags =
|
|
EXT_CLIENT_CERT_KEY_STATE_WAIT_FOR_CERT_KEY_UPDATE;
|
|
psTraceInfo("matrixSslChooseClientKeys: trying external load\n");
|
|
return PS_SUCCESS;
|
|
# endif
|
|
psTraceInfo("matrixSslChooseClientKeys: no pre-loaded keys, "\
|
|
"nor callback. Going anon.\n");
|
|
/* no keys material given - try anonymous (or fall back to
|
|
external key selection). */
|
|
return PS_SUCCESS;
|
|
}
|
|
|
|
psAssert(ssl->chosenIdentity != NULL);
|
|
return PS_SUCCESS;
|
|
}
|
|
#endif /* !USE_IDENTITY_CERTIFICATES */
|
|
|
|
/******************************************************************************/
|