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
139 changes: 135 additions & 4 deletions apps/wolfsshd/auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
#include <wolfssl/wolfcrypt/wc_port.h>
#include <wolfssl/wolfcrypt/error-crypt.h>
#include <wolfssl/wolfcrypt/coding.h>
#include <wolfssl/wolfcrypt/asn_public.h>

#ifdef WOLFSSL_FPKI
#include <wolfssl/wolfcrypt/asn.h>
Expand Down Expand Up @@ -147,20 +148,56 @@ struct WOLFSSHD_AUTH {
#endif

#ifndef MAX_LINE_SZ
/* Sized to hold the largest authorized_keys entry. */
/* sized for the largest authorized_keys entry, composite pubkeys
* included */
#ifndef WOLFSSH_NO_MLDSA
#ifndef WOLFSSH_NO_MLDSA87
#define MAX_LINE_SZ ((WC_MLDSA_87_PUB_KEY_SIZE + 2) / 3 * 4 + 640)
#if defined(WOLFSSH_CERTS)
/* x509v3-ssh-mldsa-87 size (pubkey+CA sig+DER, base64);
* COMPOSITE_MAX_TRAD_PUB_SZ is headroom for a future variant. */
#define MAX_LINE_SZ \
((WC_MLDSA_87_PUB_KEY_SIZE + WC_MLDSA_87_SIG_SIZE + \
COMPOSITE_MAX_TRAD_PUB_SZ + 1024 + 2) / 3 * 4 + 640)
#else
#define MAX_LINE_SZ \
((WC_MLDSA_87_PUB_KEY_SIZE + COMPOSITE_MAX_TRAD_PUB_SZ + \
2) / 3 * 4 + 640)
#endif
#elif !defined(WOLFSSH_NO_MLDSA65)
#define MAX_LINE_SZ ((WC_MLDSA_65_PUB_KEY_SIZE + 2) / 3 * 4 + 640)
#if defined(WOLFSSH_CERTS)
#define MAX_LINE_SZ \
((WC_MLDSA_65_PUB_KEY_SIZE + WC_MLDSA_65_SIG_SIZE + \
COMPOSITE_MAX_TRAD_PUB_SZ + 1024 + 2) / 3 * 4 + 640)
#else
#define MAX_LINE_SZ \
((WC_MLDSA_65_PUB_KEY_SIZE + COMPOSITE_MAX_TRAD_PUB_SZ + \
2) / 3 * 4 + 640)
#endif
#else
#define MAX_LINE_SZ ((WC_MLDSA_44_PUB_KEY_SIZE + 2) / 3 * 4 + 640)
#if defined(WOLFSSH_CERTS)
#define MAX_LINE_SZ \
((WC_MLDSA_44_PUB_KEY_SIZE + WC_MLDSA_44_SIG_SIZE + \
COMPOSITE_MAX_TRAD_PUB_SZ + 1024 + 2) / 3 * 4 + 640)
#else
#define MAX_LINE_SZ \
((WC_MLDSA_44_PUB_KEY_SIZE + COMPOSITE_MAX_TRAD_PUB_SZ + \
2) / 3 * 4 + 640)
#endif
#endif
#else
#define MAX_LINE_SZ 900
#endif
#endif

#ifdef WOLFSSHD_UNIT_TEST
/* Exposes MAX_LINE_SZ so tests can size worst-case lines without
* duplicating the formula above. */
word32 wolfsshd_test_MaxLineSz(void)
{
return (word32)MAX_LINE_SZ;
}
#endif

#if 0
/* this could potentially be useful in a deeply embedded future port */

Expand Down Expand Up @@ -257,6 +294,28 @@ static int CheckAuthKeysLine(char* line, word32 lineSz, const byte* key,
#endif
#endif
#endif
#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256)
"ssh-mldsa44-es256",
#endif
#if !defined(WOLFSSH_NO_MLDSA65) && \
!defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) && !defined(NO_SHA512)
"ssh-mldsa65-es256",
#endif
#if !defined(WOLFSSH_NO_MLDSA87) && \
!defined(WOLFSSH_NO_ECDSA_SHA2_NISTP384) && !defined(NO_SHA512)
"ssh-mldsa87-es384",
#endif
#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ED25519) && \
!defined(NO_SHA512)
"ssh-mldsa44-ed25519@openssh.com",
#endif
#if !defined(WOLFSSH_NO_MLDSA65) && !defined(WOLFSSH_NO_ED25519) && \
!defined(NO_SHA512)
"ssh-mldsa65-ed25519",
#endif
#if !defined(WOLFSSH_NO_MLDSA87) && defined(HAVE_ED448)
"ssh-mldsa87-ed448",
#endif
};
const int NUM_ALLOWED_TYPES =
(int)(sizeof(allowedTypes) / sizeof(allowedTypes[0]));
Expand Down Expand Up @@ -996,6 +1055,78 @@ static int SearchKeysFile(const char* keysFilePath, const byte* key,
return ret;
}

/* Detects OpenSSH vs ASN1/DER format of a raw host private key buffer.
*
* Uses wc_KeyPemToDer(), not wc_PemToDer(..., PRIVATEKEY_TYPE, ...): the
* latter also unwraps PKCS#8 via ToTraditional(), which mangles key types
* with no traditional DER form (e.g. ML-DSA).
*
* On a PEM buffer, *keyDer is a WMALLOC'd (heap, DYNTYPE_SSHD) buffer the
* caller must WS_FORCEZERO + WFREE; NULL if data was passed through as-is
* (raw DER or OpenSSH). privBuf/privBufSz are set to the buffer to actually
* load. Returns WOLFSSH_FORMAT_ASN1/WOLFSSH_FORMAT_OPENSSH, or negative on
* error. */
int wolfSSHD_DetectPrivKeyFormat(byte* data, word32 dataSz, void* heap,
byte** keyDer, byte** privBuf, word32* privBufSz)
{
int keyFormat = WOLFSSH_FORMAT_ASN1;
byte* der;
int derSz;

if (keyDer != NULL) {
*keyDer = NULL;
}
if (privBuf != NULL) {
*privBuf = NULL;
}
if (privBufSz != NULL) {
*privBufSz = 0;
}

if (data == NULL || dataSz == 0 || keyDer == NULL || privBuf == NULL ||
privBufSz == NULL) {
return WS_BAD_ARGUMENT;
}

der = (byte*)WMALLOC(dataSz, heap, DYNTYPE_SSHD);
if (der == NULL) {
return WS_MEMORY_E;
}

derSz = wc_KeyPemToDer(data, (int)dataSz, der, (int)dataSz, NULL);
if (derSz <= 0) {
WFREE(der, heap, DYNTYPE_SSHD);

*privBuf = data;
*privBufSz = dataSz;

/* wstrnstr() stops at the first NUL, so binary buffers fall
* through to the WMEMCMP magic check below. */
if (WSTRNSTR((const char*)*privBuf,
"-----BEGIN OPENSSH PRIVATE KEY-----", *privBufSz) != NULL) {
keyFormat = WOLFSSH_FORMAT_OPENSSH;
}
else if (*privBufSz >= sizeof("openssh-key-v1") &&
WMEMCMP(*privBuf, "openssh-key-v1",
sizeof("openssh-key-v1")) == 0) {
/* sizeof() includes the magic's trailing NUL */
keyFormat = WOLFSSH_FORMAT_OPENSSH;
}
}
else {
*keyDer = der;
*privBuf = der;
*privBufSz = (word32)derSz;
/* PEM-decoded result may still be an OpenSSH binary blob */
if (*privBufSz >= sizeof("openssh-key-v1") &&
WMEMCMP(*privBuf, "openssh-key-v1",
sizeof("openssh-key-v1")) == 0) {
keyFormat = WOLFSSH_FORMAT_OPENSSH;
}
}

return keyFormat;
}

WOLFSSHD_STATIC int SearchForPubKey(const char* path,
const char* authKeysFile, const char* user,
Expand Down
9 changes: 9 additions & 0 deletions apps/wolfsshd/auth.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
#define WOLFSSH_USER_GET_STRING(x) #x
#define WOLFSSH_USER_STRING(x) WOLFSSH_USER_GET_STRING(x)

#include <wolfssl/wolfcrypt/asn_public.h> /* for wc_KeyPemToDer */

#if 0

typedef struct USER_NODE USER_NODE;
Expand Down Expand Up @@ -101,6 +103,12 @@ int wolfSSHD_GetHomeDirectory(WOLFSSHD_AUTH* auth, WOLFSSH* ssh, WCHAR* out, int
int wolfSSHD_OpenSecureFile(const char* path, WUID_T ownerUid,
int rejectReadable, void* heap, WFILE** out);

/* classifies a loaded host private key buffer as OpenSSH or ASN1/DER.
* *keyDer is a WMALLOC'd (heap, DYNTYPE_SSHD) buffer to WS_FORCEZERO +
* WFREE on a PEM decode, else NULL. */
int wolfSSHD_DetectPrivKeyFormat(byte* data, word32 dataSz, void* heap,
byte** keyDer, byte** privBuf, word32* privBufSz);

#ifdef WOLFSSHD_UNIT_TEST
#ifndef _WIN32
extern int (*wsshd_setregid_cb)(WGID_T, WGID_T);
Expand All @@ -117,6 +125,7 @@ int SearchForPubKey(const char* path, const char* authKeysFile,
const char* user,
const WS_UserAuthData_PublicKey* pubKeyCtx,
WUID_T uid, int strictModes);
word32 wolfsshd_test_MaxLineSz(void);
#endif
#if defined(WOLFSSH_HAVE_LIBCRYPT) || defined(WOLFSSH_HAVE_LIBLOGIN)
int CheckPasswordHashUnix(const char* input, char* stored);
Expand Down
Loading
Loading