handle certificate chains

pull/415/head
JacobBarthelmeh 2022-07-27 10:39:34 -07:00
parent 69e1cde58f
commit e40e852bc2
5 changed files with 221 additions and 93 deletions

View File

@ -1898,11 +1898,14 @@ static int wsUserAuth(byte authType,
wc_Sha256Hash(authData->sf.publicKey.publicKey,
authData->sf.publicKey.publicKeySz,
authHash);
#if defined(WOLFSSH_CERTS) && !defined(WOLFSSH_NO_FPKI)
/* Display FPKI info UUID and FASC-N */
#if defined(WOLFSSH_CERTS) && !defined(WOLFSSH_NO_FPKI) && \
defined(WOLFSSL_FPKI)
/* Display FPKI info UUID and FASC-N, getter function for FASC-N and
* UUID are dependent on wolfSSL version newer than 5.3.0 so gatting
* on the macro WOLFSSL_FPKI here too */
if (authData->sf.publicKey.isCert) {
DecodedCert cert;
byte* uuid;
byte* uuid = NULL;
word32 fascnSz;
word32 uuidSz;
word32 i;

View File

@ -86,33 +86,36 @@ struct WOLFSSH_CERTMAN {
static WOLFSSH_CERTMAN* _CertMan_init(WOLFSSH_CERTMAN* cm, void* heap)
{
WOLFSSH_CERTMAN* ret = NULL;
WLOG_ENTER();
if (cm != NULL) {
WMEMSET(cm, 0, sizeof *cm);
cm->cm = wolfSSL_CertManagerNew_ex(heap);
if (cm->cm != NULL) {
int ret;
ret = cm;
if (ret != NULL) {
WMEMSET(ret, 0, sizeof(WOLFSSH_CERTMAN));
ret->cm = wolfSSL_CertManagerNew_ex(heap);
if (ret->cm == NULL) {
ret = NULL;
}
#ifdef HAVE_OCSP
else {
int err;
ret = wolfSSL_CertManagerEnableOCSP(cm->cm,
err = wolfSSL_CertManagerEnableOCSP(ret->cm,
WOLFSSL_OCSP_CHECKALL);
if (ret == WOLFSSL_SUCCESS) {
if (err == WOLFSSL_SUCCESS) {
WLOG(WS_LOG_CERTMAN, "Enabled OCSP");
}
else {
WLOG(WS_LOG_CERTMAN, "Couldn't enable OCSP");
wolfSSL_CertManagerFree(cm->cm);
cm = NULL;
wolfSSL_CertManagerFree(ret->cm);
ret = NULL;
}
}
else {
cm = NULL;
}
#endif
}
WLOG_LEAVE_PTR(cm);
return cm;
WLOG_LEAVE_PTR(ret);
return ret;
}
@ -190,59 +193,148 @@ enum {
};
#endif /* WOLFSSH_NO_FPKI */
int wolfSSH_CERTMAN_VerifyCert_buffer(WOLFSSH_CERTMAN* cm,
const unsigned char* cert, word32 certSz)
/* if handling a chain it is expected to be the leaf cert first followed by
* intermediates and CA last (CA may be ommited) */
int wolfSSH_CERTMAN_VerifyCerts_buffer(WOLFSSH_CERTMAN* cm,
const unsigned char* certs, word32 certSz, word32 certsCount)
{
int ret = WS_SUCCESS;
int idx;
unsigned char **certLoc; /* locations of certificate start */
word32 *certLen; /* size of certificate, in sync with certLoc */
unsigned char *currentPt;
word32 currentSz;
WLOG_ENTER();
if (ret == WS_SUCCESS) {
ret = wolfSSL_CertManagerVerifyBuffer(cm->cm, cert, certSz,
WOLFSSL_FILETYPE_ASN1);
certLoc = (unsigned char**)WMALLOC(certsCount * sizeof(unsigned char*),
cm->heap, DYNTYPE_CERT);
certLen = (word32*)WMALLOC(certsCount * sizeof(word32), cm->heap,
DYNTYPE_CERT);
if (ret == WOLFSSL_SUCCESS) {
ret = WS_SUCCESS;
}
else if (ret == ASN_NO_SIGNER_E) {
WLOG(WS_LOG_CERTMAN, "cert verify: no signer");
ret = WS_CERT_NO_SIGNER_E;
}
else if (ret == ASN_AFTER_DATE_E) {
WLOG(WS_LOG_CERTMAN, "cert verify: expired");
ret = WS_CERT_EXPIRED_E;
}
else if (ret == ASN_SIG_CONFIRM_E) {
WLOG(WS_LOG_CERTMAN, "cert verify: bad sig");
ret = WS_CERT_SIG_CONFIRM_E;
currentPt = (unsigned char*)certs; /* set initial certificate pointer */
currentSz = 0;
for (idx = 0; idx < (int)certsCount; idx++) {
word32 sz = 0;
certLoc[idx] = currentPt;
/* get the size of the certificate from first sequence */
if (currentSz + MAX_SEQ_SZ >= certSz) {
ret = WS_BUFFER_E;
break;
}
else {
WLOG(WS_LOG_CERTMAN, "cert verify: other error (%d)", ret);
ret = WS_CERT_OTHER_E;
/* at this point there is at least 5 bytes in currentPt */
if (currentPt[sz] != (ASN_SEQUENCE | ASN_CONSTRUCTED)) {
WLOG(WS_LOG_CERTMAN, "no cert sequence to get length from");
ret = ASN_PARSE_E;
break;
}
sz++;
if (ret == WS_SUCCESS) {
if (currentPt[sz] >= ASN_LONG_LENGTH) {
word32 bytes = currentPt[sz++] & 0x7F;
if (bytes > MAX_LENGTH_SZ) {
WLOG(WS_LOG_CERTMAN, "length found is too large!");
ret = ASN_PARSE_E;
break;
}
else {
byte b;
certLen[idx] = 0;
for (; bytes > 0; bytes--) {
b = currentPt[sz++];
certLen[idx] = (certLen[idx] << 8) | b;
}
}
}
else {
certLen[idx] = (word32)currentPt[sz++];
}
sz += certLen[idx];
certLen[idx] = sz; /* update size to contain first sequence */
}
}
/* advance current pointer and update current total size */
if (ret == WS_SUCCESS) {
if (currentSz + sz > certSz) {
WLOG(WS_LOG_CERTMAN, "cert found is too large!");
ret = ASN_PARSE_E;
break;
}
currentSz += sz;
currentPt += sz;
}
}
if (ret == WS_SUCCESS) {
ret = wolfSSL_CertManagerCheckOCSP(cm->cm, (byte*)cert, certSz);
for (idx = certsCount - 1; idx >= 0; idx--) {
WLOG(WS_LOG_CERTMAN, "verifying cert at index %d", idx);
ret = wolfSSL_CertManagerVerifyBuffer(cm->cm, certLoc[idx],
certLen[idx], WOLFSSL_FILETYPE_ASN1);
if (ret == WOLFSSL_SUCCESS) {
ret = WS_SUCCESS;
}
else if (ret == ASN_NO_SIGNER_E) {
WLOG(WS_LOG_CERTMAN, "cert verify: no signer");
ret = WS_CERT_NO_SIGNER_E;
}
else if (ret == ASN_AFTER_DATE_E) {
WLOG(WS_LOG_CERTMAN, "cert verify: expired");
ret = WS_CERT_EXPIRED_E;
}
else if (ret == ASN_SIG_CONFIRM_E) {
WLOG(WS_LOG_CERTMAN, "cert verify: bad sig");
ret = WS_CERT_SIG_CONFIRM_E;
}
else {
WLOG(WS_LOG_CERTMAN, "cert verify: other error (%d)", ret);
ret = WS_CERT_OTHER_E;
}
if (ret == WOLFSSL_SUCCESS) {
ret = WS_SUCCESS;
}
else if (ret == OCSP_CERT_REVOKED) {
WLOG(WS_LOG_CERTMAN, "ocsp lookup: ocsp revoked");
ret = WS_CERT_REVOKED_E;
}
else {
WLOG(WS_LOG_CERTMAN, "ocsp lookup: other error (%d)", ret);
ret = WS_CERT_OTHER_E;
#ifdef HAVE_OCSP
if (ret == WS_SUCCESS) {
ret = wolfSSL_CertManagerCheckOCSP(cm->cm, (byte*)certLoc[idx],
certLen[idx]);
if (ret == WOLFSSL_SUCCESS) {
ret = WS_SUCCESS;
}
else if (ret == OCSP_CERT_REVOKED) {
WLOG(WS_LOG_CERTMAN, "ocsp lookup: ocsp revoked");
ret = WS_CERT_REVOKED_E;
}
else {
WLOG(WS_LOG_CERTMAN, "ocsp lookup: other error (%d)", ret);
ret = WS_CERT_OTHER_E;
}
}
#endif /* HAVE_OCSP */
/* verified successfully, add intermideate as trusted */
if (ret == WS_SUCCESS && idx > 0) {
WLOG(WS_LOG_CERTMAN, "adding intermidiate cert as trusted");
ret = wolfSSH_CERTMAN_LoadRootCA_buffer(cm, certLoc[idx],
certLen[idx]);
}
if (ret != WS_SUCCESS) {
break;
}
}
}
#ifndef WOLFSSH_NO_FPKI
/* FPKI checking on the leaf certificate */
if (ret == WS_SUCCESS) {
DecodedCert decoded;
wc_InitDecodedCert(&decoded, cert, certSz, cm->cm);
wc_InitDecodedCert(&decoded, certLoc[0], certLen[0], cm->cm);
ret = wc_ParseCert(&decoded, WOLFSSL_FILETYPE_ASN1, 0, cm->cm);
if (ret == 0) {
@ -387,16 +479,20 @@ static int CheckProfile(DecodedCert* cert, int profile)
/* cycle through alt names to check for needed types */
current = cert->altNames;
while (current != NULL) {
#ifdef WOLFSSL_FPKI
if (current->oidSum == FASCN_OID) {
hasFascN = 1;
}
#endif /* WOLFSSL_FPKI */
current = current->next;
}
#ifdef WOLFSSL_FPKI
if (wc_GetUUIDFromCert(cert, uuid, &uuidSz) == 0) {
hasUUID = 1;
}
#endif /* WOLFSSL_FPKI */
/* all must have UUID and worksheet 6 must have FASC-N in addition to
* UUID */

View File

@ -546,6 +546,8 @@ WOLFSSH_CTX* CtxInit(WOLFSSH_CTX* ctx, byte side, void* heap)
#endif /* DEBUG_WOLFSSH */
#ifdef WOLFSSH_CERTS
ctx->certMan = wolfSSH_CERTMAN_new(ctx->heap);
if (ctx->certMan == NULL)
return NULL;
#endif /* WOLFSSH_CERTS */
ctx->windowSz = DEFAULT_WINDOW_SZ;
ctx->maxPacketSz = DEFAULT_MAX_PACKET_SZ;
@ -767,7 +769,6 @@ int wolfSSH_ProcessBuffer(WOLFSSH_CTX* ctx,
word32 derSz, scratch = 0;
union wolfSSH_key *key_ptr = NULL;
(void)wcType;
(void)dynamicType;
(void)heap;
@ -846,7 +847,15 @@ int wolfSSH_ProcessBuffer(WOLFSSH_CTX* ctx,
if (ctx->certMan != NULL) {
ret = wolfSSH_CERTMAN_LoadRootCA_buffer(ctx->certMan, der, derSz);
}
else {
WLOG(WS_LOG_DEBUG, "Error no cert manager set");
ret = WS_MEMORY_E;
}
WFREE(der, heap, dynamicType);
if (ret < 0) {
WLOG(WS_LOG_DEBUG, "Error %d loading in CA buffer", ret);
goto end;
}
}
#endif /* WOLFSSH_CERTS */
else {
@ -911,6 +920,8 @@ int wolfSSH_ProcessBuffer(WOLFSSH_CTX* ctx,
end:
if (key_ptr)
WFREE(key_ptr, heap, dynamicType);
(void)wcType;
return ret;
}
@ -4546,11 +4557,6 @@ static int DoUserAuthRequestRsaCert(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk,
}
}
if (ret == WS_SUCCESS) {
ret = wolfSSH_CERTMAN_VerifyCert_buffer(ssh->ctx->certMan,
pk->publicKey, pk->publicKeySz);
}
if (ret == WS_SUCCESS) {
byte* pub = NULL;
word32 pubSz;
@ -4895,13 +4901,6 @@ static int DoUserAuthRequestEccCert(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk,
}
}
#ifdef WOLFSSH_CERTS
if (ret == WS_SUCCESS) {
ret = wolfSSH_CERTMAN_VerifyCert_buffer(ssh->ctx->certMan,
pk->publicKey, pk->publicKeySz);
}
#endif /* WOLFSSH_CERTS */
if (ret == WS_SUCCESS) {
byte* pub = NULL;
word32 pubSz;
@ -5037,6 +5036,9 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData,
int ret = WS_SUCCESS;
int authFailure = 0;
byte pkTypeId;
#ifdef WOLFSSH_CERTS
word32 certCount = 0;
#endif
WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequestPublicKey()");
@ -5063,14 +5065,15 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData,
pkTypeId = NameToId((char*)pk->publicKeyType, pk->publicKeyTypeSz);
if (pkTypeId == ID_UNKNOWN) {
WLOG(WS_LOG_DEBUG, "DUARPK: Unknown / Unsupported key type");
ret = WS_INVALID_ALGO_ID;
ret = SendUserAuthFailure(ssh, 0);
authFailure = 1;
}
}
if (ret == WS_SUCCESS)
if (ret == WS_SUCCESS && !authFailure)
ret = GetSize(&pk->publicKeySz, buf, len, &begin);
if (ret == WS_SUCCESS) {
if (ret == WS_SUCCESS && !authFailure) {
pk->publicKey = buf + begin;
begin += pk->publicKeySz;
#ifdef WOLFSSH_CERTS
@ -5079,21 +5082,47 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData,
pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP384 ||
pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP521) {
word32 l = 0, m = 0;
word32 ocspCount = 0;
byte* ocspBuf = NULL;
word32 ocspBufSz = 0;
/* Skip the name */
ret = GetSize(&l, pk->publicKey, pk->publicKeySz, &m);
m += l;
/* Get the cert count */
ret = GetUint32(&l, pk->publicKey, pk->publicKeySz, &m);
if (ret == WS_SUCCESS) {
ret = GetSize(&l, pk->publicKey, pk->publicKeySz, &m);
ret = GetUint32(&certCount, pk->publicKey, pk->publicKeySz, &m);
if (ret == WS_SUCCESS) {
WLOG(WS_LOG_INFO, "Peer sent certificate count of %d",
certCount);
ret = GetSize(&l, pk->publicKey, pk->publicKeySz, &m);
}
}
if (ret == WS_SUCCESS) {
ocspBuf = (byte*)pk->publicKey + m + l;
ocspBufSz = pk->publicKeySz - l;
pk->publicKeySz = l;
pk->publicKey = pk->publicKey + m;
pk->isCert = 1;
}
/* get OCSP count */
if (ret == WS_SUCCESS) {
m = 0;
ret = GetUint32(&ocspCount, ocspBuf, ocspBufSz, &m);
}
if (ret == WS_SUCCESS) {
WLOG(WS_LOG_INFO, "Peer sent OCSP count of %d", ocspCount);
}
/* @TODO handle OCSP's */
if (ocspCount > 0) {
WLOG(WS_LOG_INFO, "Peer sent OCSP's, not yet handled");
ret = GetSize(&l, ocspBuf, ocspBufSz, &m);
}
}
#endif /* WOLFSSH_CERTS */
@ -5177,26 +5206,13 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData,
}
/* The rest of the fields in the signature are already
* in the buffer. Just need to account for the sizes. */
* in the buffer. Just need to account for the sizes, which total
* the length of the buffer minus the signature and uint32 size of
* signature. */
if (ret == 0) {
word32 dataToSignSz;
dataToSignSz = authData->usernameSz +
authData->serviceNameSz +
authData->authNameSz + BOOLEAN_SZ +
pk->publicKeyTypeSz + pk->publicKeySz +
(UINT32_SZ * 5);
#ifdef WOLFSSH_CERTS
if (pkTypeId == ID_X509V3_SSH_RSA ||
pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP256 ||
pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP384 ||
pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP521) {
dataToSignSz += pk->publicKeyTypeSz + (UINT32_SZ * 4);
}
#endif /* WOLFSSH_CERTS */
dataToSignSz = len - pk->signatureSz - UINT32_SZ;
ret = wc_HashUpdate(&hash, hashId,
pk->dataToSign, dataToSignSz);
}
@ -5210,6 +5226,15 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData,
}
wc_HashFree(&hash, hashId);
#ifdef WOLFSSH_CERTS
/* verify certificates sent after the auth cb has allowed the
* connection */
if (ret == WS_SUCCESS && pk->isCert == 1 && certCount > 0) {
ret = wolfSSH_CERTMAN_VerifyCerts_buffer(ssh->ctx->certMan,
pk->publicKey, pk->publicKeySz, certCount);
}
#endif
if (ret == WS_SUCCESS) {
switch (pkTypeId) {
#ifndef WOLFSSH_NO_RSA
@ -9917,6 +9942,7 @@ static int BuildUserAuthRequestEccCert(WOLFSSH* ssh,
byte* r;
byte* s;
byte sig[139]; /* wc_ecc_sig_size() for a prime521 key. */
byte rs[139]; /* wc_ecc_sig_size() for a prime521 key. */
word32 sigSz = sizeof(sig), rSz, sSz;
byte* checkData = NULL;
word32 checkDataSz = 0;
@ -9985,9 +10011,9 @@ static int BuildUserAuthRequestEccCert(WOLFSSH* ssh,
}
if (ret == WS_SUCCESS) {
rSz = sSz = sizeof(sig) / 2;
r = sig;
s = sig + rSz;
rSz = sSz = sizeof(rs) / 2;
r = rs;
s = rs + rSz;
ret = wc_ecc_sig_to_rs(sig, sigSz, r, &rSz, s, &sSz);
}
@ -10354,7 +10380,7 @@ int SendUserAuthRequest(WOLFSSH* ssh, byte authId, int addSig)
ret = PrepareUserAuthRequestPublicKey(ssh, &payloadSz, &authData,
keySig_ptr);
}
else if (authId != ID_NONE)
else if (authId != ID_NONE && !ssh->userAuthPkDone)
ret = WS_INVALID_ALGO_ID;
}

View File

@ -103,7 +103,10 @@ WOLFSSH_CTX* wolfSSH_CTX_new(byte side, void* heap)
}
ctx = (WOLFSSH_CTX*)WMALLOC(sizeof(WOLFSSH_CTX), heap, DYNTYPE_CTX);
ctx = CtxInit(ctx, side, heap);
if (CtxInit(ctx, side, heap) == NULL) {
WFREE(ctx, heap, DYNTYPE_CTX);
ctx = NULL;
}
WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_CTX_new(), ctx = %p", ctx);

View File

@ -51,8 +51,8 @@ int wolfSSH_CERTMAN_LoadRootCA_buffer(WOLFSSH_CERTMAN* cm,
const unsigned char* rootCa, word32 rootCaSz);
WOLFSSH_API
int wolfSSH_CERTMAN_VerifyCert_buffer(WOLFSSH_CERTMAN* cm,
const unsigned char* cert, word32 certSz);
int wolfSSH_CERTMAN_VerifyCerts_buffer(WOLFSSH_CERTMAN* cm,
const unsigned char* cert, word32 certSz, word32 certCount);
#ifdef __cplusplus