@@ -2708,6 +2708,216 @@ int wolfSSH_SetHostTpmKey(WOLFSSH_CTX* ctx, byte keyId)
27082708#endif /* WOLFSSH_TPM */
27092709
27102710
2711+ #ifdef WOLFSSH_CERTS
2712+
2713+ /* Finds needle in the first inSz bytes of in. Unlike WSTRNSTR() an embedded
2714+ NUL does not end the search, the buffer being length delimited. */
2715+ static const byte* FindInBuffer(const byte* in, word32 inSz, const char* needle)
2716+ {
2717+ word32 needleSz;
2718+ word32 i;
2719+
2720+ needleSz = (word32)WSTRLEN(needle);
2721+ if (needleSz == 0 || inSz < needleSz) {
2722+ return NULL;
2723+ }
2724+
2725+ for (i = 0; i <= inSz - needleSz; i++) {
2726+ if (WMEMCMP(in + i, needle, needleSz) == 0) {
2727+ return in + i;
2728+ }
2729+ }
2730+
2731+ return NULL;
2732+ }
2733+
2734+
2735+ #ifdef WOLFSSH_HAVE_TRUSTED_CERT_PEM
2736+ /* Reports whether the buffer holds only trusted-certificate PEM. A plain
2737+ certificate anywhere in it wins, the way wc_PemToDer() finds one first. */
2738+ int IsTrustedCertPem(const byte* in, word32 inSz)
2739+ {
2740+ const char* certHeader = NULL;
2741+ const char* trustedHeader = NULL;
2742+
2743+ if (wc_PemGetHeaderFooter(CA_TYPE, &certHeader, NULL) != 0
2744+ || certHeader == NULL
2745+ || wc_PemGetHeaderFooter(TRUSTED_CERT_TYPE, &trustedHeader,
2746+ NULL) != 0
2747+ || trustedHeader == NULL) {
2748+ return 0;
2749+ }
2750+
2751+ return FindInBuffer(in, inSz, certHeader) == NULL
2752+ && FindInBuffer(in, inSz, trustedHeader) != NULL;
2753+ }
2754+ #endif /* WOLFSSH_HAVE_TRUSTED_CERT_PEM */
2755+
2756+
2757+ /* Loads every PEM certificate block in the buffer as a root CA, skipping the
2758+ ones that fail, the way wolfSSL's own chain loader treats a CA file. */
2759+ static int LoadRootCaPemBuffer(WOLFSSH_CTX* ctx, const byte* in, word32 inSz)
2760+ {
2761+ DerBuffer* der = NULL;
2762+ const char* certHeader = NULL;
2763+ const char* certFooter = NULL;
2764+ const char* footer;
2765+ const byte* found;
2766+ const byte* foundEnd;
2767+ const byte* nextHeader;
2768+ const byte* nextCert;
2769+ word32 certHeaderSz;
2770+ word32 certFooterSz;
2771+ word32 headerSz;
2772+ word32 footerSz;
2773+ word32 bodyStart;
2774+ word32 searchSz;
2775+ word32 blockSz;
2776+ word32 used = 0;
2777+ word32 loaded = 0;
2778+ word32 failed = 0;
2779+ int wcType = CA_TYPE;
2780+ int ret;
2781+ #ifdef WOLFSSH_HAVE_TRUSTED_CERT_PEM
2782+ const char* trustedHeader = NULL;
2783+ const char* trustedFooter = NULL;
2784+ const byte* nextTrusted;
2785+ word32 trustedHeaderSz;
2786+ word32 trustedFooterSz;
2787+ #endif
2788+
2789+ if (ctx->certMan == NULL) {
2790+ WLOG(WS_LOG_DEBUG, "Error no cert manager set");
2791+ return WS_MEMORY_E;
2792+ }
2793+
2794+ if (wc_PemGetHeaderFooter(CA_TYPE, &certHeader, &certFooter) != 0
2795+ || certHeader == NULL || certFooter == NULL) {
2796+ return WS_BAD_FILE_E;
2797+ }
2798+ certHeaderSz = (word32)WSTRLEN(certHeader);
2799+ certFooterSz = (word32)WSTRLEN(certFooter);
2800+ #ifdef WOLFSSH_HAVE_TRUSTED_CERT_PEM
2801+ if (wc_PemGetHeaderFooter(TRUSTED_CERT_TYPE, &trustedHeader,
2802+ &trustedFooter) != 0
2803+ || trustedHeader == NULL || trustedFooter == NULL) {
2804+ return WS_BAD_FILE_E;
2805+ }
2806+ trustedHeaderSz = (word32)WSTRLEN(trustedHeader);
2807+ trustedFooterSz = (word32)WSTRLEN(trustedFooter);
2808+ #endif
2809+
2810+ /* Each form's header is re-sought only from behind the last one found. */
2811+ nextCert = FindInBuffer(in, inSz, certHeader);
2812+ #ifdef WOLFSSH_HAVE_TRUSTED_CERT_PEM
2813+ nextTrusted = FindInBuffer(in, inSz, trustedHeader);
2814+ #endif
2815+
2816+ while (used < inSz) {
2817+ found = nextCert;
2818+ footer = certFooter;
2819+ headerSz = certHeaderSz;
2820+ footerSz = certFooterSz;
2821+ wcType = CA_TYPE;
2822+ #ifdef WOLFSSH_HAVE_TRUSTED_CERT_PEM
2823+ /* wc_PemToDer() takes either form, but only finds the one its type
2824+ names first, so whichever header leads picks the type. */
2825+ if (found == NULL || (nextTrusted != NULL && nextTrusted < found)) {
2826+ found = nextTrusted;
2827+ footer = trustedFooter;
2828+ headerSz = trustedHeaderSz;
2829+ footerSz = trustedFooterSz;
2830+ wcType = TRUSTED_CERT_TYPE;
2831+ }
2832+ #endif
2833+
2834+ if (found == NULL) {
2835+ break;
2836+ }
2837+ used = (word32)(found - in);
2838+ bodyStart = used + headerSz;
2839+
2840+ /* Step this form's cursor past the header being read. */
2841+ #ifdef WOLFSSH_HAVE_TRUSTED_CERT_PEM
2842+ if (wcType == TRUSTED_CERT_TYPE) {
2843+ nextTrusted = FindInBuffer(in + bodyStart, inSz - bodyStart,
2844+ trustedHeader);
2845+ }
2846+ else {
2847+ nextCert = FindInBuffer(in + bodyStart, inSz - bodyStart,
2848+ certHeader);
2849+ }
2850+ #else
2851+ nextCert = FindInBuffer(in + bodyStart, inSz - bodyStart, certHeader);
2852+ #endif
2853+
2854+ nextHeader = (nextCert != NULL && nextCert >= in + bodyStart) ?
2855+ nextCert : NULL;
2856+ #ifdef WOLFSSH_HAVE_TRUSTED_CERT_PEM
2857+ if (nextTrusted != NULL && nextTrusted >= in + bodyStart
2858+ && (nextHeader == NULL || nextTrusted < nextHeader)) {
2859+ nextHeader = nextTrusted;
2860+ }
2861+ #endif
2862+
2863+ /* A block runs to its footer, and the next header caps the search
2864+ for one. Bounding the search keeps the walk linear. */
2865+ searchSz = (nextHeader != NULL) ?
2866+ (word32)(nextHeader - (in + bodyStart)) : inSz - bodyStart;
2867+ foundEnd = FindInBuffer(in + bodyStart, searchSz, footer);
2868+
2869+ if (foundEnd == NULL) {
2870+ WLOG(WS_LOG_ERROR, "Skipping CA %u, nothing closes its header",
2871+ loaded + failed);
2872+ failed++;
2873+ used = bodyStart;
2874+ continue;
2875+ }
2876+ blockSz = (word32)(foundEnd - found) + footerSz;
2877+
2878+ if (wc_PemToDer(found, (long)blockSz, wcType, &der, ctx->heap,
2879+ NULL, NULL) != 0) {
2880+ /* A bad body is reported after the buffer is allocated. */
2881+ wc_FreeDer(&der);
2882+ WLOG(WS_LOG_ERROR, "Skipping CA %u, PEM to DER failed",
2883+ loaded + failed);
2884+ failed++;
2885+ }
2886+ else {
2887+ ret = wolfSSH_CERTMAN_LoadRootCA_buffer(ctx->certMan,
2888+ der->buffer, der->length);
2889+ wc_FreeDer(&der);
2890+
2891+ if (ret != WS_SUCCESS) {
2892+ WLOG(WS_LOG_ERROR, "Skipping CA %u, error %d loading it",
2893+ loaded + failed, ret);
2894+ failed++;
2895+ }
2896+ else {
2897+ loaded++;
2898+ }
2899+ }
2900+
2901+ used += blockSz;
2902+ }
2903+
2904+ if (loaded > 0) {
2905+ ret = WS_SUCCESS;
2906+ }
2907+ else if (failed > 0) {
2908+ ret = WS_PARSE_E;
2909+ }
2910+ else {
2911+ WLOG(WS_LOG_ERROR, "No certificate in the CA buffer");
2912+ ret = WS_BAD_FILE_E;
2913+ }
2914+
2915+ return ret;
2916+ }
2917+
2918+ #endif /* WOLFSSH_CERTS */
2919+
2920+
27112921int wolfSSH_ProcessBuffer(WOLFSSH_CTX* ctx,
27122922 const byte* in, word32 inSz,
27132923 int format, int type)
@@ -2784,6 +2994,20 @@ int wolfSSH_ProcessBuffer(WOLFSSH_CTX* ctx,
27842994 }
27852995 }
27862996 else if (format == WOLFSSH_FORMAT_PEM) {
2997+ #ifdef WOLFSSH_CERTS
2998+ if (type == BUFTYPE_CA) {
2999+ /* A CA buffer may hold a bundle, so every block is loaded. */
3000+ return LoadRootCaPemBuffer(ctx, in, inSz);
3001+ }
3002+ #ifdef WOLFSSH_HAVE_TRUSTED_CERT_PEM
3003+ if (type == BUFTYPE_CERT && IsTrustedCertPem(in, inSz)) {
3004+ /* The trust data behind the certificate means nothing to a peer. */
3005+ WLOG(WS_LOG_DEBUG, "Trusted certificate form is for root CAs");
3006+ return WS_BAD_FILETYPE_E;
3007+ }
3008+ #endif /* WOLFSSH_HAVE_TRUSTED_CERT_PEM */
3009+ #endif /* WOLFSSH_CERTS */
3010+
27873011 /* The der size will be smaller than the pem size. */
27883012 der = (byte*)WMALLOC(inSz, heap, dynamicType);
27893013 if (der == NULL)
0 commit comments