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
10 changes: 10 additions & 0 deletions apps/wolfsshd/auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -3088,7 +3088,17 @@ int DefaultUserAuthTypes(WOLFSSH* ssh, void* ctx)
usr = wolfSSH_GetUsername(ssh);
usrConf = wolfSSHD_AuthGetUserConf(authCtx, usr, NULL, NULL,
NULL, NULL, NULL);
if (usrConf == NULL && usr != NULL && authCtx->checkUserCb != NULL &&
authCtx->checkUserCb(usr) == WSSHD_AUTH_FAILURE) {
/* No such account: compose on the name alone, so Match User
* applies and Match Group cannot. Other failures stay closed. */
usrConf = wolfSSHD_GetUserConf(authCtx->conf, usr, NULL, 0,
NULL, NULL, NULL, NULL, NULL);
}

if (usrConf == NULL) {
wolfSSH_Log(WS_LOG_ERROR,
"[SSHD] No user configuration; advertising no methods");
ret = 0;
}
else {
Expand Down
164 changes: 164 additions & 0 deletions apps/wolfsshd/test/test_configuration.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#ifndef _WIN32
#include <unistd.h>
#include <pwd.h>
#include <errno.h>
#endif
#if !defined(_WIN32) && !(defined(__OSX__) || defined(__APPLE__))
#include <shadow.h>
Expand Down Expand Up @@ -5705,6 +5706,168 @@ static int test_DefaultUserAuthTypesNullArgs(void)
return ret;
}

/* Probed name; also the Match User target below. */
#define UNKNOWN_USER "nonexistent_test_user_xyz"

/* Stubbed account lookup for the probed name, independent of the host */
enum {
ADV_NO_ACCOUNT, /* getpwnam: no such account */
ADV_LOOKUP_ERROR, /* getpwnam: lookup error */
ADV_GROUPS_FAIL, /* account exists, its group list cannot be read */
ADV_RESOLVED /* account exists with a readable group list */
};

#ifndef _WIN32
static int s_adv_mode;
static struct passwd s_adv_pw;

static struct passwd* stub_getpwnam_adv(const char* name)
{
(void)name;
switch (s_adv_mode) {
case ADV_NO_ACCOUNT:
return NULL;
case ADV_LOOKUP_ERROR:
errno = EIO;
return NULL;
default:
WMEMSET(&s_adv_pw, 0, sizeof(s_adv_pw));
s_adv_pw.pw_name = (char*)UNKNOWN_USER;
s_adv_pw.pw_uid = 1002;
s_adv_pw.pw_gid = 1002;
s_adv_pw.pw_passwd = (char*)"x";
return &s_adv_pw;
}
}
#endif

/* An unknown name advertises what the global directives and Match User
* blocks offer it, so the reply does not reveal which accounts exist. A
* lookup error or unreadable group list advertises nothing. */
static int test_DefaultUserAuthTypesUnknownUser(void)
{
int ret = WS_SUCCESS;
int i, j;

static const struct {
const char* desc;
int mode;
const char* lines[3]; /* directives, NULL terminated */
int expected;
} vectors[] = {
{"stock config advertises both methods", ADV_NO_ACCOUNT, {NULL},
WOLFSSH_USERAUTH_PASSWORD | WOLFSSH_USERAUTH_PUBLICKEY},
{"global PasswordAuthentication no advertises pubkey only",
ADV_NO_ACCOUNT, {"PasswordAuthentication no", NULL},
WOLFSSH_USERAUTH_PUBLICKEY},
{"Match User on the probed name applies", ADV_NO_ACCOUNT,
{"Match User " UNKNOWN_USER, "PasswordAuthentication no", NULL},
WOLFSSH_USERAUTH_PUBLICKEY},
{"Match User on another name does not apply", ADV_NO_ACCOUNT,
{"Match User someone_else", "PasswordAuthentication no", NULL},
WOLFSSH_USERAUTH_PASSWORD | WOLFSSH_USERAUTH_PUBLICKEY},
{"Match Group does not apply to a name with no account",
ADV_NO_ACCOUNT,
{"Match Group somegroup", "PasswordAuthentication no", NULL},
WOLFSSH_USERAUTH_PASSWORD | WOLFSSH_USERAUTH_PUBLICKEY},
#ifndef _WIN32
{"account lookup error advertises nothing", ADV_LOOKUP_ERROR,
{NULL}, 0},
{"unreadable group list advertises nothing", ADV_GROUPS_FAIL,
{NULL}, 0},
{"resolved account takes its Match User block", ADV_RESOLVED,
{"Match User " UNKNOWN_USER, "PasswordAuthentication no", NULL},
WOLFSSH_USERAUTH_PUBLICKEY},
#endif
};
const int numVectors = (int)(sizeof(vectors) / sizeof(*vectors));
const int maxLines = (int)(sizeof(vectors[0].lines) /
sizeof(*vectors[0].lines));
WOLFSSHD_CONFIG* head;
WOLFSSHD_CONFIG* conf;
WOLFSSHD_AUTH* authCtx;
WOLFSSH_CTX* ctx;
WOLFSSH* ssh;
#ifndef _WIN32
struct passwd* (*savedGetpwnam)(const char*);
int (*savedGrouplist)(const char*, WGID_T, WGID_T*, int*);
int (*savedSetgroups)(int, const WGID_T*);
#endif
/* keeps the permission drop in wolfSSHD_AuthCreateUser a no-op; see
* test_DefaultUserAuth_OOBRead */
static const char privSep[] = "UsePrivilegeSeparation no";

for (i = 0; i < numVectors && ret == WS_SUCCESS; ++i) {
Log(" Testing scenario: %s.", vectors[i].desc);

head = wolfSSHD_ConfigNew(NULL);
if (head == NULL) {
Log(" FAILED.\n");
return WS_MEMORY_E;
}
/* a Match line moves 'conf' to the new block; keep the list head */
conf = head;

/* goes in before any Match line so it lands in the global section */
ret = ParseConfigLine(&conf, privSep, (int)WSTRLEN(privSep), 0);
for (j = 0; ret == WS_SUCCESS && j < maxLines &&
vectors[i].lines[j] != NULL; ++j) {
ret = ParseConfigLine(&conf, vectors[i].lines[j],
(int)WSTRLEN(vectors[i].lines[j]), 0);
}
if (ret != WS_SUCCESS) {
Log(" FAILED.\n");
wolfSSHD_ConfigFree(head);
break;
}

authCtx = wolfSSHD_AuthCreateUser(NULL, head);
if (authCtx == NULL) {
Log(" FAILED.\n");
wolfSSHD_ConfigFree(head);
ret = WS_FATAL_ERROR;
break;
}

#ifndef _WIN32
s_adv_mode = vectors[i].mode;
savedGetpwnam = wsshd_getpwnam_cb;
wsshd_getpwnam_cb = stub_getpwnam_adv;
InstallGroupStubs(0, &savedGrouplist, &savedSetgroups);
s_grouplist_always_fail = (vectors[i].mode == ADV_GROUPS_FAIL);
#endif

ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL);
ssh = (ctx != NULL) ? wolfSSH_new(ctx) : NULL;
if (ssh == NULL ||
wolfSSH_SetUsername(ssh, UNKNOWN_USER) != WS_SUCCESS) {
Log(" FAILED.\n");
ret = WS_FATAL_ERROR;
}
else if (DefaultUserAuthTypes(ssh, authCtx) != vectors[i].expected) {
Log(" FAILED.\n");
ret = WS_FATAL_ERROR;
}
else {
Log(" PASSED.\n");
}

#ifndef _WIN32
wsshd_getpwnam_cb = savedGetpwnam;
wsshd_getgrouplist_cb = savedGrouplist;
wsshd_setgroups_cb = savedSetgroups;
#endif
wolfSSH_free(ssh);
wolfSSH_CTX_free(ctx);
wolfSSHD_AuthFreeUser(authCtx);
wolfSSHD_ConfigFree(head);
}

return ret;
}

#undef UNKNOWN_USER

#ifndef _WIN32
/* report a single secure-open scenario; returns WS_SUCCESS when the observed
* result matches expectation (wantOk != 0 means expect acceptance) */
Expand Down Expand Up @@ -7316,6 +7479,7 @@ const TEST_CASE testCases[] = {
TEST_DECL(test_ConfigIncludeMatchChain),
TEST_DECL(test_GetUserAuthTypes),
TEST_DECL(test_DefaultUserAuthTypesNullArgs),
TEST_DECL(test_DefaultUserAuthTypesUnknownUser),
TEST_DECL(test_ConfigSetAuthKeysFile),
TEST_DECL(test_ResolveAuthKeysPath),
TEST_DECL(test_ConfigFree),
Expand Down
Loading