diff --git a/examples/echoserver/echoserver.c b/examples/echoserver/echoserver.c index a9ef4af5..58819e0b 100644 --- a/examples/echoserver/echoserver.c +++ b/examples/echoserver/echoserver.c @@ -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; diff --git a/src/certman.c b/src/certman.c index 89032d82..5ca24de9 100644 --- a/src/certman.c +++ b/src/certman.c @@ -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 */ diff --git a/src/internal.c b/src/internal.c index 9d6c1a2d..37027ea4 100644 --- a/src/internal.c +++ b/src/internal.c @@ -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; } diff --git a/src/ssh.c b/src/ssh.c index f299ebad..7b2a6c73 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -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); diff --git a/wolfssh/certman.h b/wolfssh/certman.h index 3d2218d7..09859aad 100644 --- a/wolfssh/certman.h +++ b/wolfssh/certman.h @@ -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