Test SearchForPubKey authorized_keys no-match rejection

pull/1113/head
Yosuke Shimizu 2026-07-16 09:00:17 +09:00 committed by John Safranek
parent 0b819b0b48
commit 37e852b95e
3 changed files with 132 additions and 0 deletions

View File

@ -894,10 +894,16 @@ int wolfSSHD_OpenSecureFile(const char* path, WUID_T ownerUid,
#endif
}
#if defined(WOLFSSHD_UNIT_TEST) && !defined(_WIN32)
int SearchForPubKey(const char* path, const char* authKeysFile,
const WS_UserAuthData_PublicKey* pubKeyCtx,
WUID_T uid, int strictModes)
#else
static int SearchForPubKey(const char* path, const char* authKeysFile,
const char* user,
const WS_UserAuthData_PublicKey* pubKeyCtx,
WUID_T uid, int strictModes)
#endif
{
int ret = WSSHD_AUTH_SUCCESS;
char authKeysPath[MAX_PATH_SZ];

View File

@ -105,6 +105,9 @@ extern int (*wsshd_getgrouplist_cb)(const char*, WGID_T, WGID_T*, int*);
int wolfSSHD_GetUserGroupNames(void* heap, const char* usr, WGID_T primaryGid,
char*** outNames, word32* outCount);
void wolfSSHD_FreeUserGroupNames(void* heap, char** names, word32 count);
int SearchForPubKey(const char* path, const char* authKeysFile,
const WS_UserAuthData_PublicKey* pubKeyCtx,
WUID_T uid, int strictModes);
#endif
#if defined(WOLFSSH_HAVE_LIBCRYPT) || defined(WOLFSSH_HAVE_LIBLOGIN)
int CheckPasswordHashUnix(const char* input, char* stored);

View File

@ -1421,6 +1421,126 @@ static int test_CheckAuthKeysLine(void)
return ret;
}
#ifndef _WIN32
/* Drive SearchForPubKey against a temp authorized_keys file to cover the
* "no line matched -> WSSHD_AUTH_FAILURE" gate: the authorized key kills a
* condition inversion, the unauthorized key kills a deletion of the gate. */
static int test_SearchForPubKey(void)
{
int ret = WS_SUCCESS;
static const char keyAStr[] = "wolfssh-auth-key-test-A-AAAAAAA";
static const char keyBStr[] = "wolfssh-auth-key-test-B-BBBBBBB";
const byte* keyA = (const byte*)keyAStr;
const byte* keyB = (const byte*)keyBStr;
const word32 keySz = (word32)(sizeof(keyAStr) - 1);
char base[] = "/tmp/wolfsshd_pkXXXXXX";
char keysPath[64] = "";
char missPath[64] = "";
char line[256];
WS_UserAuthData_PublicKey pubKeyCtx;
WUID_T uid = getuid();
FILE* f = NULL;
int rc;
if (mkdtemp(base) == NULL) {
Log(" mkdtemp failed.\n");
ret = WS_FATAL_ERROR;
}
if (ret == WS_SUCCESS) {
snprintf(keysPath, sizeof(keysPath), "%s/authorized_keys", base);
snprintf(missPath, sizeof(missPath), "%s/absent_keys", base);
ret = BuildAuthKeysLine(keyA, keySz, line, sizeof(line));
}
if (ret == WS_SUCCESS) {
f = fopen(keysPath, "w");
if (f == NULL) {
Log(" fopen of authorized_keys failed.\n");
ret = WS_FATAL_ERROR;
}
else {
fputs(line, f);
fputs("\n", f);
fclose(f);
}
}
/* Force 0600 so the StrictModes secure-open is not tripped by a permissive
* umask leaving the file group or world writable. */
if (ret == WS_SUCCESS && chmod(keysPath, S_IRUSR | S_IWUSR) != 0) {
Log(" chmod of authorized_keys failed.\n");
ret = WS_FATAL_ERROR;
}
WMEMSET(&pubKeyCtx, 0, sizeof(pubKeyCtx));
pubKeyCtx.publicKeySz = keySz;
/* StrictModes disabled so the check stays hermetic (no ownership gate). */
if (ret == WS_SUCCESS) {
Log(" Testing scenario: authorized key is accepted.");
pubKeyCtx.publicKey = keyA;
rc = SearchForPubKey(base, keysPath, &pubKeyCtx, uid, 0);
if (rc == WSSHD_AUTH_SUCCESS) {
Log(" PASSED.\n");
}
else {
Log(" FAILED (rc=%d).\n", rc);
ret = WS_FATAL_ERROR;
}
}
/* The temp file is user-owned with safe perms, so the StrictModes
* secure-open branch must also accept the authorized key. */
if (ret == WS_SUCCESS) {
Log(" Testing scenario: authorized key accepted under StrictModes.");
pubKeyCtx.publicKey = keyA;
rc = SearchForPubKey(base, keysPath, &pubKeyCtx, uid, 1);
if (rc == WSSHD_AUTH_SUCCESS) {
Log(" PASSED.\n");
}
else {
Log(" FAILED (rc=%d).\n", rc);
ret = WS_FATAL_ERROR;
}
}
if (ret == WS_SUCCESS) {
Log(" Testing scenario: unauthorized key is rejected.");
pubKeyCtx.publicKey = keyB;
rc = SearchForPubKey(base, keysPath, &pubKeyCtx, uid, 0);
if (rc == WSSHD_AUTH_FAILURE) {
Log(" PASSED.\n");
}
else {
Log(" FAILED (rc=%d).\n", rc);
ret = WS_FATAL_ERROR;
}
}
/* A missing authorized_keys file is an error, not a silent accept. */
if (ret == WS_SUCCESS) {
Log(" Testing scenario: missing keys file returns an error.");
pubKeyCtx.publicKey = keyA;
rc = SearchForPubKey(base, missPath, &pubKeyCtx, uid, 0);
if (rc < 0) {
Log(" PASSED.\n");
}
else {
Log(" FAILED (rc=%d).\n", rc);
ret = WS_FATAL_ERROR;
}
}
if (keysPath[0] != '\0') {
unlink(keysPath);
}
rmdir(base);
return ret;
}
#endif /* !_WIN32 */
#endif /* WOLFSSL_BASE64_ENCODE */
#ifndef _WIN32
@ -2998,6 +3118,9 @@ const TEST_CASE testCases[] = {
#ifdef WOLFSSL_BASE64_ENCODE
TEST_DECL(test_CheckAuthKeysLine),
#endif
#if defined(WOLFSSL_BASE64_ENCODE) && !defined(_WIN32)
TEST_DECL(test_SearchForPubKey),
#endif
#ifndef _WIN32
TEST_DECL(test_GetUserGroupNames),
TEST_DECL(test_AuthReducePermissionsUser_ok),