Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 189 additions & 0 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -2708,6 +2708,181 @@ int wolfSSH_SetHostTpmKey(WOLFSSH_CTX* ctx, byte keyId)
#endif /* WOLFSSH_TPM */


#ifdef WOLFSSH_CERTS

/* Finds needle in the first inSz bytes of in. Unlike WSTRNSTR() an embedded
NUL does not end the search, the buffer being length delimited. */
static const byte* FindInBuffer(const byte* in, word32 inSz, const char* needle)
{
word32 needleSz;
word32 i;

needleSz = (word32)WSTRLEN(needle);
if (needleSz == 0 || inSz < needleSz) {
return NULL;
}

for (i = 0; i <= inSz - needleSz; i++) {
if (WMEMCMP(in + i, needle, needleSz) == 0) {
return in + i;
}
}

return NULL;
}


#ifdef WOLFSSH_HAVE_TRUSTED_CERT_PEM
/* Reports whether the buffer holds only trusted-certificate PEM. A plain
certificate anywhere in it wins, the way wc_PemToDer() finds one first. */
int IsTrustedCertPem(const byte* in, word32 inSz)
{
const char* certHeader = NULL;
const char* trustedHeader = NULL;

if (wc_PemGetHeaderFooter(CA_TYPE, &certHeader, NULL) != 0
|| certHeader == NULL
|| wc_PemGetHeaderFooter(TRUSTED_CERT_TYPE, &trustedHeader,
NULL) != 0
|| trustedHeader == NULL) {
return 0;
}

return FindInBuffer(in, inSz, certHeader) == NULL
&& FindInBuffer(in, inSz, trustedHeader) != NULL;
}
#endif /* WOLFSSH_HAVE_TRUSTED_CERT_PEM */


/* Loads every PEM certificate block in the buffer as a root CA, skipping the
ones that fail, the way wolfSSL's own chain loader treats a CA file. */
static int LoadRootCaPemBuffer(WOLFSSH_CTX* ctx, const byte* in, word32 inSz)
{
EncryptedInfo* info;
#ifndef WOLFSSH_SMALL_STACK
EncryptedInfo info_s;
#endif
DerBuffer* der = NULL;
const char* certHeader = NULL;
const char* header;
const byte* found;
word32 used = 0;
word32 loaded = 0;
word32 failed = 0;
word32 headerSz;
int wcType = CA_TYPE;
int ret;
#ifdef WOLFSSH_HAVE_TRUSTED_CERT_PEM
const char* trustedHeader = NULL;
const byte* foundTrusted;
#endif

if (ctx->certMan == NULL) {
WLOG(WS_LOG_DEBUG, "Error no cert manager set");
return WS_MEMORY_E;
}
Comment thread
yosuke-wolfssl marked this conversation as resolved.

if (wc_PemGetHeaderFooter(CA_TYPE, &certHeader, NULL) != 0
|| certHeader == NULL) {
return WS_BAD_FILE_E;
}
#ifdef WOLFSSH_HAVE_TRUSTED_CERT_PEM
if (wc_PemGetHeaderFooter(TRUSTED_CERT_TYPE, &trustedHeader, NULL) != 0
Comment thread
yosuke-wolfssl marked this conversation as resolved.
|| trustedHeader == NULL) {
return WS_BAD_FILE_E;
}

/* The trusted form is rare, so find it once instead of every block. */
foundTrusted = FindInBuffer(in, inSz, trustedHeader);
#endif

#ifndef WOLFSSH_SMALL_STACK
info = &info_s;
#else
info = (EncryptedInfo*)WMALLOC(sizeof(EncryptedInfo), ctx->heap,
DYNTYPE_TEMP);
if (info == NULL) {
return WS_MEMORY_E;
}
#endif

while (used < inSz) {
/* Text may sit between blocks, so seek the next header. */
found = FindInBuffer(in + used, inSz - used, certHeader);
Comment thread
yosuke-wolfssl marked this conversation as resolved.
header = certHeader;
#ifdef WOLFSSH_HAVE_TRUSTED_CERT_PEM
if (foundTrusted != NULL && foundTrusted < in + used) {
foundTrusted = FindInBuffer(in + used, inSz - used, trustedHeader);
}

/* wc_PemToDer() finds its own type first, so the lead header wins. */
if (found == NULL || (foundTrusted != NULL && foundTrusted < found)) {
found = foundTrusted;
header = trustedHeader;
wcType = TRUSTED_CERT_TYPE;
Comment thread
yosuke-wolfssl marked this conversation as resolved.
}
else {
wcType = CA_TYPE;
}
#endif

if (found == NULL) {
break;
}
used = (word32)(found - in);
headerSz = (word32)WSTRLEN(header);

WMEMSET(info, 0, sizeof(EncryptedInfo));
if (wc_PemToDer(in + used, (long)(inSz - used), wcType, &der,
ctx->heap, info, NULL) != 0) {
/* A bad body is reported after the buffer is allocated. */
wc_FreeDer(&der);
WLOG(WS_LOG_ERROR, "Skipping CA %u, PEM to DER failed",
loaded + failed);
failed++;
/* The block length is unknown, so resume behind its header. */
used += headerSz;
continue;
}

ret = wolfSSH_CERTMAN_LoadRootCA_buffer(ctx->certMan,
der->buffer, der->length);
wc_FreeDer(&der);

if (ret != WS_SUCCESS) {
WLOG(WS_LOG_ERROR, "Skipping CA %u, error %d loading it",
loaded + failed, ret);
failed++;
}
else {
loaded++;
}

/* A block that consumes nothing would stall the walk. */
used += (info->consumed > 0) ? (word32)info->consumed : headerSz;
}

#ifdef WOLFSSH_SMALL_STACK
WFREE(info, ctx->heap, DYNTYPE_TEMP);
#endif

if (loaded > 0) {
ret = WS_SUCCESS;
}
else if (failed > 0) {
ret = WS_PARSE_E;
}
else {
WLOG(WS_LOG_ERROR, "No certificate in the CA buffer");
ret = WS_BAD_FILE_E;
}

return ret;
}

#endif /* WOLFSSH_CERTS */


int wolfSSH_ProcessBuffer(WOLFSSH_CTX* ctx,
const byte* in, word32 inSz,
int format, int type)
Expand Down Expand Up @@ -2784,6 +2959,20 @@ int wolfSSH_ProcessBuffer(WOLFSSH_CTX* ctx,
}
}
else if (format == WOLFSSH_FORMAT_PEM) {
#ifdef WOLFSSH_CERTS
if (type == BUFTYPE_CA) {
/* A CA buffer may hold a bundle, so every block is loaded. */
return LoadRootCaPemBuffer(ctx, in, inSz);
}
#ifdef WOLFSSH_HAVE_TRUSTED_CERT_PEM
if (type == BUFTYPE_CERT && IsTrustedCertPem(in, inSz)) {
/* The trust data behind the certificate means nothing to a peer. */
WLOG(WS_LOG_DEBUG, "Trusted certificate form is for root CAs");
return WS_BAD_FILETYPE_E;
}
#endif /* WOLFSSH_HAVE_TRUSTED_CERT_PEM */
#endif /* WOLFSSH_CERTS */

/* The der size will be smaller than the pem size. */
der = (byte*)WMALLOC(inSz, heap, dynamicType);
if (der == NULL)
Expand Down
21 changes: 20 additions & 1 deletion src/ssh.c
Original file line number Diff line number Diff line change
Expand Up @@ -2350,12 +2350,17 @@ int wolfSSH_ReadPublicKey_buffer(const byte* in, word32 inSz, int format,
#ifdef WOLFSSH_CERTS
static const char* CertBeginPrefix = "-----BEGIN CERTIFICATE-----";
#endif
#if defined(WOLFSSH_CERTS) && defined(WOLFSSH_HAVE_TRUSTED_CERT_PEM)
static const char* TrustedCertBeginPrefix =
"-----BEGIN TRUSTED CERTIFICATE-----";
#endif

/* Longest algorithm name is ecdsa-sha2-nistp521-cert-v01@openssh.com. */
#define WOLFSSH_MAX_CERT_ALGO_NAME_SZ 48

/* Identifies a certificate from its content, without decoding or allocating.
An x509v3-* line holds a wire chain, not a certificate, so it is declined. */
An x509v3-* line holds a wire chain, not a certificate, so it is declined.
The trusted form is PEM too; the decoders decide who takes it. */
static int SniffCertForm(const byte* in, word32 inSz, byte* flavor,
byte* certId)
{
Expand All @@ -2375,6 +2380,12 @@ static int SniffCertForm(const byte* in, word32 inSz, byte* flavor,
*flavor = WOLFSSH_CERT_FLAVOR_X509;
ret = WOLFSSH_FORMAT_PEM;
}
#ifdef WOLFSSH_HAVE_TRUSTED_CERT_PEM
else if (WSTRNSTR((const char*)in, TrustedCertBeginPrefix, inSz) != NULL) {
*flavor = WOLFSSH_CERT_FLAVOR_X509;
ret = WOLFSSH_FORMAT_PEM;
}
#endif /* WOLFSSH_HAVE_TRUSTED_CERT_PEM */
#endif /* WOLFSSH_CERTS */

#ifdef WOLFSSH_OSSH_CERTS
Expand Down Expand Up @@ -2421,6 +2432,14 @@ static int DoPemCert(const byte* in, word32 inSz, byte** out, word32* outSz,
word32 derSz = 0;
int ret;

#ifdef WOLFSSH_HAVE_TRUSTED_CERT_PEM
if (IsTrustedCertPem(in, inSz)) {
/* The trust data behind the certificate means nothing to a peer. */
WLOG(WS_LOG_DEBUG, "Trusted certificate form is for root CAs");
return WS_BAD_FILETYPE_E;
}
#endif

der = (byte*)WMALLOC(inSz, heap, DYNTYPE_CERT);
if (der == NULL) {
return WS_MEMORY_E;
Expand Down
Loading
Loading