292 lines
8.8 KiB
C
292 lines
8.8 KiB
C
/**
|
|
* @file tls13Authenticate.c
|
|
* @version $Format:%h%d$
|
|
*
|
|
* Functions for certificate chain validation in TLS 1.3.
|
|
*/
|
|
/*
|
|
* 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_1_3_AUTHENTICATE
|
|
/* # define DEBUG_TLS_1_3_AUTHENTICATE */
|
|
# endif
|
|
|
|
# ifdef USE_TLS_1_3
|
|
# ifdef USE_CERT_VALIDATE
|
|
|
|
static int32_t matrixSslValidatePeerCerts(ssl_t *ssl,
|
|
void *pkiData);
|
|
static int32_t psCheckValidationResult(ssl_t *ssl,
|
|
psX509Cert_t *leaf);
|
|
static void psCheckSetPathLenFailure(ssl_t *ssl,
|
|
psX509Cert_t *leaf);
|
|
|
|
int32_t tls13ValidateCertChain(ssl_t *ssl)
|
|
{
|
|
matrixSslReorderCertChain(ssl->sec.cert);
|
|
return matrixSslValidatePeerCerts(ssl, NULL);
|
|
}
|
|
|
|
/* Validate the peer certficate chain stored in ssl->sec.cert. */
|
|
static
|
|
int32_t matrixSslValidatePeerCerts(ssl_t *ssl,
|
|
void *pkiData)
|
|
{
|
|
matrixValidateCertsOptions_t *opts;
|
|
psX509Cert_t *foundIssuer;
|
|
int32_t rc;
|
|
|
|
opts = &ssl->validateCertsOpts;
|
|
|
|
/* Perform MatrixSSL internal validation. */
|
|
rc = matrixValidateCertsExt(ssl->hsPool,
|
|
ssl->sec.cert,
|
|
ssl->keys == NULL ? NULL : ssl->keys->CAcerts,
|
|
ssl->expectedName,
|
|
&foundIssuer,
|
|
pkiData,
|
|
ssl->memAllocPtr,
|
|
opts);
|
|
if (rc == PS_MEM_FAIL)
|
|
{
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
psCheckSetPathLenFailure(ssl, ssl->sec.cert);
|
|
rc = psCheckValidationResult(ssl,
|
|
ssl->sec.cert);
|
|
if (rc < 0)
|
|
{
|
|
if (ssl->sec.validateCert == NULL)
|
|
{
|
|
/* Internal validation failed and there is no user cert callback. */
|
|
if (ssl->err == SSL_ALERT_NONE)
|
|
{
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
}
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
|
|
/* Call the user certificate validation callback. */
|
|
rc = matrixUserCertValidator(ssl, ssl->err, ssl->sec.cert,
|
|
ssl->sec.validateCert);
|
|
|
|
return tls13HandleUserCertCbResult(ssl, rc);
|
|
}
|
|
|
|
int32_t tls13HandleUserCertCbResult(ssl_t *ssl, int32 cbRc)
|
|
{
|
|
|
|
/* Test what the user callback returned. */
|
|
ssl->sec.anon = 0;
|
|
if (cbRc == SSL_ALLOW_ANON_CONNECTION)
|
|
{
|
|
ssl->sec.anon = 1;
|
|
}
|
|
else if (cbRc > 0)
|
|
{
|
|
/* User returned an alert. May or may not be the alert that was
|
|
determined above */
|
|
psTraceIntInfo("Certificate authentication alert %d\n", cbRc);
|
|
ssl->err = cbRc;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
else if (cbRc < 0)
|
|
{
|
|
psTraceIntInfo("User certificate callback had an internal error " \
|
|
"(cbRc=%d)\n", cbRc);
|
|
ssl->err = SSL_ALERT_INTERNAL_ERROR;
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
|
|
/* User callback returned 0 (continue on). Did they determine the alert
|
|
was not fatal after all? */
|
|
if (ssl->err != SSL_ALERT_NONE)
|
|
{
|
|
psTraceIntInfo("User certificate callback determined alert %d " \
|
|
"was NOT fatal\n",
|
|
ssl->err);
|
|
ssl->err = SSL_ALERT_NONE;
|
|
}
|
|
|
|
return PS_SUCCESS;
|
|
}
|
|
|
|
static
|
|
int32_t psCheckValidationResult(ssl_t *ssl,
|
|
psX509Cert_t *leaf)
|
|
{
|
|
psX509Cert_t *cert = leaf;
|
|
|
|
while (cert)
|
|
{
|
|
switch (cert->authStatus)
|
|
{
|
|
case PS_CERT_AUTH_FAIL_SIG:
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
break;
|
|
case PS_CERT_AUTH_FAIL_REVOKED:
|
|
ssl->err = SSL_ALERT_CERTIFICATE_REVOKED;
|
|
break;
|
|
case PS_CERT_AUTH_FAIL_AUTHKEY:
|
|
case PS_CERT_AUTH_FAIL_PATH_LEN:
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
break;
|
|
case PS_CERT_AUTH_FAIL_EXTENSION:
|
|
/* The math and basic constraints matched. This case is
|
|
for X.509 extension mayhem */
|
|
if (cert->authFailFlags & PS_CERT_AUTH_FAIL_DATE_FLAG)
|
|
{
|
|
ssl->err = SSL_ALERT_CERTIFICATE_EXPIRED;
|
|
}
|
|
else if (cert->authFailFlags & PS_CERT_AUTH_FAIL_SUBJECT_FLAG)
|
|
{
|
|
/* expectedName was giving to NewSession but couldn't
|
|
match what the peer gave us */
|
|
ssl->err = SSL_ALERT_CERTIFICATE_UNKNOWN;
|
|
}
|
|
else if (cert->next != NULL)
|
|
{
|
|
/* This is an extension problem in the chain.
|
|
Even if it's minor, we are shutting it down */
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
}
|
|
else
|
|
{
|
|
/* This is the case where we did successfully find the
|
|
correct CA to validate the cert and the math passed
|
|
but the extensions had a problem. Give app a
|
|
different message in this case */
|
|
ssl->err = SSL_ALERT_ILLEGAL_PARAMETER;
|
|
}
|
|
break;
|
|
case PS_CERT_AUTH_FAIL_BC:
|
|
case PS_CERT_AUTH_FAIL_DN:
|
|
/* These two are pre-math tests. If this was a problem in the
|
|
middle of the chain it means the chain couldn't even
|
|
validate itself. If it is at the end it means a matching
|
|
CA could not be found */
|
|
if (cert->next != NULL)
|
|
{
|
|
ssl->err = SSL_ALERT_BAD_CERTIFICATE;
|
|
}
|
|
else
|
|
{
|
|
ssl->err = SSL_ALERT_UNKNOWN_CA;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Check if this is the last validated certificate. */
|
|
if (cert->pathEnd == PS_TRUE)
|
|
{
|
|
break;
|
|
}
|
|
|
|
cert = cert->next;
|
|
}
|
|
|
|
if (ssl->err == SSL_ALERT_NONE)
|
|
{
|
|
return PS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
return MATRIXSSL_ERROR;
|
|
}
|
|
}
|
|
|
|
static
|
|
void psCheckSetPathLenFailure(ssl_t *ssl,
|
|
psX509Cert_t *leaf)
|
|
{
|
|
psSize_t pathLen = 0;
|
|
psX509Cert_t *cert = leaf;
|
|
int32_t maxDepth;
|
|
psBool_t exceeded = PS_FALSE;
|
|
|
|
maxDepth = ssl->validateCertsOpts.max_verify_depth;
|
|
|
|
while (cert)
|
|
{
|
|
pathLen++;
|
|
|
|
if (maxDepth > 0)
|
|
{
|
|
exceeded = PS_FALSE;
|
|
psTraceIntInfo("max_verify_depth: %d\n", maxDepth);
|
|
|
|
/*
|
|
A maximum verification depth has been specified in session opts.
|
|
*/
|
|
if (pathLen > maxDepth)
|
|
{
|
|
exceeded = PS_TRUE;
|
|
}
|
|
else if (pathLen == maxDepth)
|
|
{
|
|
/*
|
|
We don't have the root in cert->next. So do the
|
|
following: If the cert is _not_ self-signed, it must
|
|
have a valid root cert as the issuer, since this
|
|
is checked in matrixValidateCerts. Now take that root
|
|
into account when checking the path length.
|
|
*/
|
|
if (memcmpct(&cert->subject, &cert->issuer,
|
|
sizeof(cert->subject)))
|
|
{
|
|
/* Root cert causes depth to be exceeded. */
|
|
exceeded = PS_TRUE;
|
|
}
|
|
}
|
|
if (exceeded)
|
|
{
|
|
/* Max depth exceeded. */
|
|
psTraceErrr("Error: max_verify_depth exceeded\n");
|
|
ssl->err = SSL_ALERT_UNKNOWN_CA;
|
|
cert->authStatus |= PS_CERT_AUTH_FAIL_PATH_LEN;
|
|
cert->authFailFlags |= PS_CERT_AUTH_FAIL_VERIFY_DEPTH_FLAG;
|
|
}
|
|
}
|
|
if (ssl->err != SSL_ALERT_NONE)
|
|
{
|
|
break; /* The first alert is the logical one to send */
|
|
}
|
|
|
|
cert = cert->next;
|
|
}
|
|
}
|
|
# endif /* USE_CERT_VALIDATE */
|
|
# endif /* USE_TLS_1_3 */
|