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
68 changes: 54 additions & 14 deletions apps/wolfsshd/auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -835,7 +835,6 @@ static int SearchForPubKey(const char* path, const char* authKeysFile,
if (f != WBADFILE) {
WFCLOSE(NULL, f);
}

if (lineBuf != NULL) {
WFREE(lineBuf, NULL, DYNTYPE_BUFFER);
}
Expand Down Expand Up @@ -882,19 +881,23 @@ static int CheckPublicKeyUnix(const char* name,
if (pubKeyCtx->isOsshCert) {
int rc;
byte* caKey = NULL;
word32 caKeySz;
word32 caKeySz = 0;
const byte* caKeyType = NULL;
word32 caKeyTypeSz;
word32 caKeyTypeSz = 0;
byte fingerprint[WC_SHA256_DIGEST_SIZE];

if (pubKeyCtx->caKey == NULL ||
pubKeyCtx->caKeySz != WC_SHA256_DIGEST_SIZE) {
WFILE* f = WBADFILE;
char* lineBuf = NULL;
char* current = NULL;
word32 currentSz = 0;
int foundKey = 0;

if (pubKeyCtx->caKeyHash == NULL ||
pubKeyCtx->caKeyHashSz != WC_SHA256_DIGEST_SIZE) {
ret = WS_FATAL_ERROR;
}

if (ret == WSSHD_AUTH_SUCCESS) {
f = XFOPEN(usrCaKeysFile, "rb");
if (f == XBADFILE) {
if (WFOPEN(NULL, &f, usrCaKeysFile, "rb") != 0) {
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Unable to open %s",
usrCaKeysFile);
ret = WS_BAD_FILE_E;
Expand All @@ -907,8 +910,8 @@ static int CheckPublicKeyUnix(const char* name,
}
}
while (ret == WSSHD_AUTH_SUCCESS &&
(current = XFGETS(lineBuf, MAX_LINE_SZ, f)) != NULL) {
currentSz = (word32)XSTRLEN(current);
(current = WFGETS(lineBuf, MAX_LINE_SZ, f)) != NULL) {
currentSz = (word32)WSTRLEN(current);

/* remove leading spaces */
while (currentSz > 0 && current[0] == ' ') {
Expand All @@ -924,19 +927,41 @@ static int CheckPublicKeyUnix(const char* name,
continue; /* commented out line */
}

if (caKey != NULL) {
WFREE(caKey, NULL, DYNTYPE_PRIVKEY);
caKey = NULL;
caKeySz = 0;
caKeyType = NULL;
caKeyTypeSz = 0;
}

rc = wolfSSH_ReadKey_buffer((const byte*)current, currentSz,
WOLFSSH_FORMAT_SSH, &caKey, &caKeySz,
&caKeyType, &caKeyTypeSz, NULL);
if (rc == WS_SUCCESS) {
rc = wc_Hash(WC_HASH_TYPE_SHA256, caKey, caKeySz, fingerprint,
WC_SHA256_DIGEST_SIZE);
if (rc == 0 && ConstantCompare(fingerprint, pubKeyCtx->caKey,
if (rc == 0 && ConstantCompare(fingerprint, pubKeyCtx->caKeyHash,
WC_SHA256_DIGEST_SIZE) == 0) {
foundKey = 1;
break;
}
}
}

if (caKey != NULL) {
WFREE(caKey, NULL, DYNTYPE_PRIVKEY);
}
if (f != WBADFILE) {
WFCLOSE(NULL, f);
}
if (lineBuf != NULL) {
WFREE(lineBuf, NULL, DYNTYPE_BUFFER);
}

if (ret == WSSHD_AUTH_SUCCESS && !foundKey) {
ret = WSSHD_AUTH_FAILURE;
}
}
else
#endif /* WOLFSSH_OSSH_CERTS */
Expand Down Expand Up @@ -1626,9 +1651,9 @@ static int RequestAuthentication(WS_UserAuthData* authData,
* closed: require AuthorizedKeysFile (per-user key/cert mapping)
* or a wolfSSL build with FPKI. */
wolfSSH_Log(WS_LOG_ERROR,
"[SSHD] Certificate authentication cannot bind the requested "
"user without FPKI or AuthorizedKeysFile; rejecting "
"(user=%s)", usr);
"[SSHD] Certificate authentication cannot bind "
"the requested user without FPKI or "
"AuthorizedKeysFile; rejecting (user=%s)", usr);
ret = WOLFSSH_USERAUTH_REJECTED;
#endif
}
Expand Down Expand Up @@ -2249,4 +2274,19 @@ WOLFSSHD_CONFIG* wolfSSHD_AuthGetUserConf(const WOLFSSHD_AUTH* auth,
}
return ret;
}
#if defined(WOLFSSHD_UNIT_TEST) && defined(WOLFSSH_OSSH_CERTS)
/* Expose the OSSH CA-fingerprint matching path of CheckPublicKeyUnix for
* unit testing. caKeyHash must be a SHA-256 digest of the raw public key. */
int wolfSSHD_TestCheckOsshCertCa(const byte* caKeyHash,
word32 caKeyHashSz,
const char* caKeysFile)
{
WS_UserAuthData_PublicKey pk;
WMEMSET(&pk, 0, sizeof(pk));
pk.isOsshCert = 1;
pk.caKeyHash = caKeyHash;
pk.caKeyHashSz = caKeyHashSz;
return CheckPublicKeyUnix(NULL, &pk, caKeysFile, NULL, NULL);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] Test helper references Unix-only CheckPublicKeyUnix without !_WIN32 guard · API contract violations

wolfSSHD_TestCheckOsshCertCa (and its auth.h:104 prototype) is guarded only by WOLFSSHD_UNIT_TEST && WOLFSSH_OSSH_CERTS, but calls CheckPublicKeyUnix, which exists only under #ifndef _WIN32. A Windows unit-test build with OSSH certs fails to compile/link; sibling helpers in auth.h are correctly !_WIN32 guarded.

Fix: Wrap the helper's declaration and definition in #ifndef _WIN32, matching the other Unix-only unit-test helpers.

}
#endif /* WOLFSSHD_UNIT_TEST && WOLFSSH_OSSH_CERTS */
#endif /* WOLFSSH_SSHD */
5 changes: 5 additions & 0 deletions apps/wolfsshd/auth.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,5 +101,10 @@ int CheckAuthKeysLine(char* line, word32 lineSz, const byte* key,
word32 keySz);
int CAKeysFileDiffers(const char* a, const char* b);
int wolfSSHD_GetUserAuthTypes(const WOLFSSHD_CONFIG* usrConf);
#if defined(WOLFSSHD_UNIT_TEST) && defined(WOLFSSH_OSSH_CERTS)
int wolfSSHD_TestCheckOsshCertCa(const byte* caKeyHash,
word32 caKeyHashSz,
const char* caKeysFile);
#endif
#endif
#endif /* WOLFAUTH_H */
158 changes: 158 additions & 0 deletions apps/wolfsshd/test/test_configuration.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#endif

#include <wolfssh/ssh.h>
#include <wolfssh/internal.h>
#include <wolfssl/wolfcrypt/coding.h>
#include <configuration.h>
#include <auth.h>
Expand Down Expand Up @@ -1900,6 +1901,159 @@ static int test_OpenSecureFile(void)
}
#endif /* !_WIN32 */

#if defined(WOLFSSH_OSSH_CERTS) && !defined(NO_SHA256)
#include <wolfssl/wolfcrypt/sha256.h>
static int test_CheckOsshCertCa(void)
{
int ret = WS_SUCCESS;
int rc;
/* ECC-P256 public key in OpenSSH format used as a stand-in CA key. */
static const char caKeyStr[] =
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAA"
"BBBNkI5JTP6D0lF42tbxX19cE87hztUS6FSDoGvPfiU0CgeNSbI+aFdKIzTP5CQEJSvm25"
"qUzgDtH7oyaQROUnNvk= hansel";
static const char caKeyFile[] = "./test_ossh_ca_keys.txt";
byte fingerprint[WC_SHA256_DIGEST_SIZE];
byte wrongHash[WC_SHA256_DIGEST_SIZE];
byte* caKeyRaw = NULL;
word32 caKeyRawSz = 0;
const byte* caKeyType = NULL;
word32 caKeyTypeSz = 0;
WFILE* f = WBADFILE;

Log("test_CheckOsshCertCa\n");

rc = wolfSSH_ReadKey_buffer((const byte*)caKeyStr,
(word32)WSTRLEN(caKeyStr),
WOLFSSH_FORMAT_SSH, &caKeyRaw, &caKeyRawSz,
&caKeyType, &caKeyTypeSz, NULL);
if (rc != WS_SUCCESS) {
Log(" Failed to parse CA key: %d\n", rc);
return WS_FATAL_ERROR;
}
rc = wc_Sha256Hash(caKeyRaw, caKeyRawSz, fingerprint);
WFREE(caKeyRaw, NULL, DYNTYPE_PRIVKEY);
if (rc != 0) {
Log(" Failed to hash CA key: %d\n", rc);
return WS_FATAL_ERROR;
}

/* Write a one-line CA keys file. */
if (WFOPEN(NULL, &f, caKeyFile, "w") != 0) {
Log(" Failed to create CA keys file\n");
return WS_FATAL_ERROR;
}
WFWRITE(NULL, caKeyStr, sizeof(char), WSTRLEN(caKeyStr), f);
WFWRITE(NULL, "\n", sizeof(char), 1, f);
WFCLOSE(NULL, f);

Log(" Testing: matching CA fingerprint.");
rc = wolfSSHD_TestCheckOsshCertCa(fingerprint, WC_SHA256_DIGEST_SIZE,
caKeyFile);
if (rc == WSSHD_AUTH_SUCCESS) {
Log(" PASSED.\n");
}
else {
Log(" FAILED (rc=%d).\n", rc);
ret = WS_FATAL_ERROR;
}

if (ret == WS_SUCCESS) {
WMEMSET(wrongHash, 0xBB, WC_SHA256_DIGEST_SIZE);
Log(" Testing: non-matching CA fingerprint.");
rc = wolfSSHD_TestCheckOsshCertCa(wrongHash, WC_SHA256_DIGEST_SIZE,
caKeyFile);
if (rc == WSSHD_AUTH_FAILURE) {
Log(" PASSED.\n");
}
else {
Log(" FAILED (rc=%d).\n", rc);
ret = WS_FATAL_ERROR;
}
}

/* Empty file: no matching key fails with WSSHD_AUTH_FAILURE */
if (ret == WS_SUCCESS && WFOPEN(NULL, &f, caKeyFile, "w") == 0) {
WFCLOSE(NULL, f);
Log(" Testing: empty CA keys file.");
rc = wolfSSHD_TestCheckOsshCertCa(fingerprint, WC_SHA256_DIGEST_SIZE,
caKeyFile);
if (rc == WSSHD_AUTH_FAILURE) {
Log(" PASSED.\n");
}
else {
Log(" FAILED (rc=%d).\n", rc);
ret = WS_FATAL_ERROR;
}
}

/* Comment-only file: no matching key fails with WSSHD_AUTH_FAILURE */
if (ret == WS_SUCCESS && WFOPEN(NULL, &f, caKeyFile, "w") == 0) {
static const char comment[] = "# trusted CAs\n";
WFWRITE(NULL, comment, sizeof(char), WSTRLEN(comment), f);
WFCLOSE(NULL, f);
Log(" Testing: comment-only CA keys file.");
rc = wolfSSHD_TestCheckOsshCertCa(fingerprint, WC_SHA256_DIGEST_SIZE,
caKeyFile);
if (rc == WSSHD_AUTH_FAILURE) {
Log(" PASSED.\n");
}
else {
Log(" FAILED (rc=%d).\n", rc);
ret = WS_FATAL_ERROR;
}
}

WREMOVE(0, caKeyFile);
return ret;
}
static int test_CheckOsshCertCa_Malformed(void) {
int ret = WS_SUCCESS;
int rc;
static const char caKeyStr[] =
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAA"
"BBBNkI5JTP6D0lF42tbxX19cE87hztUS6FSDoGvPfiU0CgeNSbI+aFdKIzTP5CQEJSvm25"
"qUzgDtH7oyaQROUnNvk= hansel";
static const char caKeyFile[] = "./test_ossh_ca_keys_malformed.txt";
static const char badLine[] = "not-a-valid-key\n";
byte fingerprint[WC_SHA256_DIGEST_SIZE];
byte* caKeyRaw = NULL;
word32 caKeyRawSz = 0;
const byte* caKeyType = NULL;
word32 caKeyTypeSz = 0;
WFILE* f = WBADFILE;

rc = wolfSSH_ReadKey_buffer((const byte*)caKeyStr,
(word32)WSTRLEN(caKeyStr),
WOLFSSH_FORMAT_SSH, &caKeyRaw, &caKeyRawSz,
&caKeyType, &caKeyTypeSz, NULL);
if (rc != WS_SUCCESS) { return WS_FATAL_ERROR; }
rc = wc_Sha256Hash(caKeyRaw, caKeyRawSz, fingerprint);
WFREE(caKeyRaw, NULL, DYNTYPE_PRIVKEY);
if (rc != 0) { return WS_FATAL_ERROR; }

/* write malformed line */
if (WFOPEN(NULL, &f, caKeyFile, "w") != 0) {
return WS_FATAL_ERROR;
}
WFWRITE(NULL, badLine, sizeof(char), WSTRLEN(badLine), f);
WFCLOSE(NULL, f);

Log(" Testing: malformed key line CA keys file.");
rc = wolfSSHD_TestCheckOsshCertCa(fingerprint, WC_SHA256_DIGEST_SIZE,
caKeyFile);
if (rc == WSSHD_AUTH_FAILURE) {
Log(" PASSED.\n");
} else {
Log(" FAILED (rc=%d).\n", rc);
ret = WS_FATAL_ERROR;
}

WREMOVE(0, caKeyFile);
return ret;
}
#endif /* WOLFSSH_OSSH_CERTS && !NO_SHA256 */

const TEST_CASE testCases[] = {
TEST_DECL(test_ConfigDefaults),
TEST_DECL(test_ParseConfigLine),
Expand Down Expand Up @@ -1933,6 +2087,10 @@ const TEST_CASE testCases[] = {
#if defined(WOLFSSH_HAVE_LIBCRYPT) || defined(WOLFSSH_HAVE_LIBLOGIN)
TEST_DECL(test_CheckPasswordHashUnix),
#endif
#if defined(WOLFSSH_OSSH_CERTS) && !defined(NO_SHA256)
TEST_DECL(test_CheckOsshCertCa),
TEST_DECL(test_CheckOsshCertCa_Malformed),
#endif
};

int main(int argc, char** argv)
Expand Down
5 changes: 5 additions & 0 deletions wolfssh/ssh.h
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,11 @@ typedef struct WS_UserAuthData_PublicKey {
const byte* signature;
word32 signatureSz;
byte isCert:1;
#ifdef WOLFSSH_OSSH_CERTS
byte isOsshCert:1;
const byte* caKeyHash;
word32 caKeyHashSz;
#endif
} WS_UserAuthData_PublicKey;

typedef struct WS_UserAuthData {
Expand Down
Loading