Skip to content

Commit f7109f7

Browse files
wolfsshd: implement PubkeyAuthentication config directive
1 parent 2d0ef5a commit f7109f7

8 files changed

Lines changed: 322 additions & 13 deletions

File tree

apps/wolfsshd/auth.c

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,6 +1253,14 @@ static int RequestAuthentication(WS_UserAuthData* authData,
12531253
}
12541254

12551255

1256+
if (ret == WOLFSSH_USERAUTH_SUCCESS &&
1257+
authData->type == WOLFSSH_USERAUTH_PUBLICKEY &&
1258+
wolfSSHD_ConfigGetPubKeyAuth(usrConf) != 1) {
1259+
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Public key authentication not "
1260+
"allowed by configuration!");
1261+
ret = WOLFSSH_USERAUTH_REJECTED;
1262+
}
1263+
12561264
#ifdef WOLFSSL_FPKI
12571265
if (ret == WOLFSSH_USERAUTH_SUCCESS &&
12581266
authData->type == WOLFSSH_USERAUTH_PUBLICKEY) {
@@ -1434,6 +1442,26 @@ int DefaultUserAuth(byte authType, WS_UserAuthData* authData, void* ctx)
14341442
}
14351443

14361444

1445+
/* Builds the bit mask of authentication methods advertised to a peer based on
1446+
* the resolved per-user configuration. A method is only offered when its
1447+
* corresponding config option is enabled, so PasswordAuthentication no and
1448+
* PubkeyAuthentication no remove the method from the advertisement. Returns 0
1449+
* when both are disabled (no methods advertised). */
1450+
WOLFSSHD_STATIC int wolfSSHD_GetUserAuthTypes(const WOLFSSHD_CONFIG* usrConf)
1451+
{
1452+
int ret = 0;
1453+
1454+
if (wolfSSHD_ConfigGetPwAuth(usrConf) == 1) {
1455+
ret |= WOLFSSH_USERAUTH_PASSWORD;
1456+
}
1457+
if (wolfSSHD_ConfigGetPubKeyAuth(usrConf) == 1) {
1458+
ret |= WOLFSSH_USERAUTH_PUBLICKEY;
1459+
}
1460+
1461+
return ret;
1462+
}
1463+
1464+
14371465
int DefaultUserAuthTypes(WOLFSSH* ssh, void* ctx)
14381466
{
14391467
WOLFSSHD_CONFIG* usrConf;
@@ -1453,10 +1481,7 @@ int DefaultUserAuthTypes(WOLFSSH* ssh, void* ctx)
14531481
ret = WS_BAD_ARGUMENT;
14541482
}
14551483
else {
1456-
if (wolfSSHD_ConfigGetPwAuth(usrConf) == 1) {
1457-
ret |= WOLFSSH_USERAUTH_PASSWORD;
1458-
}
1459-
ret |= WOLFSSH_USERAUTH_PUBLICKEY;
1484+
ret = wolfSSHD_GetUserAuthTypes(usrConf);
14601485
}
14611486

14621487
return ret;

apps/wolfsshd/auth.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,5 +91,6 @@ int CheckPasswordHashUnix(const char* input, char* stored);
9191
int CheckAuthKeysLine(char* line, word32 lineSz, const byte* key,
9292
word32 keySz);
9393
int CAKeysFileDiffers(const char* a, const char* b);
94+
int wolfSSHD_GetUserAuthTypes(const WOLFSSHD_CONFIG* usrConf);
9495
#endif
9596
#endif /* WOLFAUTH_H */

apps/wolfsshd/configuration.c

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,8 @@
3232
/* functions for parsing out options from a config file and for handling loading
3333
* key/certs using the env. filesystem */
3434

35-
#ifdef WOLFSSHD_UNIT_TEST
36-
#define WOLFSSHD_STATIC
37-
#else
38-
#define WOLFSSHD_STATIC static
39-
#endif
35+
/* WOLFSSHD_STATIC is defined in configuration.h so configuration.c and auth.c
36+
* share the same test-visibility convention. */
4037

4138
#include <wolfssh/ssh.h>
4239
#include <wolfssh/internal.h>
@@ -217,6 +214,7 @@ WOLFSSHD_CONFIG* wolfSSHD_ConfigNew(void* heap)
217214
/* default values */
218215
ret->port = 22;
219216
ret->passwordAuth = 1;
217+
ret->pubKeyAuth = 1;
220218
ret->loginTimer = 120;
221219
}
222220
return ret;
@@ -388,9 +386,10 @@ enum {
388386
OPT_TRUSTED_USER_CA_KEYS = 21,
389387
OPT_PIDFILE = 22,
390388
OPT_BANNER = 23,
389+
OPT_PUBKEY_AUTH = 24,
391390
};
392391
enum {
393-
NUM_OPTIONS = 24
392+
NUM_OPTIONS = 25
394393
};
395394

396395
static const CONFIG_OPTION options[NUM_OPTIONS] = {
@@ -407,6 +406,7 @@ static const CONFIG_OPTION options[NUM_OPTIONS] = {
407406
{OPT_LOGIN_GRACE_TIME, "LoginGraceTime"},
408407
{OPT_HOST_KEY, "HostKey"},
409408
{OPT_PASSWORD_AUTH, "PasswordAuthentication"},
409+
{OPT_PUBKEY_AUTH, "PubkeyAuthentication"},
410410
{OPT_PORT, "Port"},
411411
{OPT_PERMIT_ROOT, "PermitRootLogin"},
412412
{OPT_USE_DNS, "UseDNS"},
@@ -553,6 +553,32 @@ static int HandlePwAuth(WOLFSSHD_CONFIG* conf, const char* value)
553553
return ret;
554554
}
555555

556+
/* returns WS_SUCCESS on success */
557+
static int HandlePubKeyAuth(WOLFSSHD_CONFIG* conf, const char* value)
558+
{
559+
int ret = WS_SUCCESS;
560+
561+
if (conf == NULL || value == NULL) {
562+
ret = WS_BAD_ARGUMENT;
563+
}
564+
565+
if (ret == WS_SUCCESS) {
566+
if (WSTRCMP(value, "no") == 0) {
567+
wolfSSH_Log(WS_LOG_INFO,
568+
"[SSHD] public key authentication disabled");
569+
conf->pubKeyAuth = 0;
570+
}
571+
else if (WSTRCMP(value, "yes") == 0) {
572+
conf->pubKeyAuth = 1;
573+
}
574+
else {
575+
ret = WS_BAD_ARGUMENT;
576+
}
577+
}
578+
579+
return ret;
580+
}
581+
556582
#define WOLFSSH_PROTOCOL_VERSION 2
557583

558584
/* returns WS_SUCCESS on success */
@@ -1032,6 +1058,9 @@ static int HandleConfigOption(WOLFSSHD_CONFIG** conf, int opt,
10321058
case OPT_PASSWORD_AUTH:
10331059
ret = HandlePwAuth(*conf, value);
10341060
break;
1061+
case OPT_PUBKEY_AUTH:
1062+
ret = HandlePubKeyAuth(*conf, value);
1063+
break;
10351064
case OPT_PORT:
10361065
ret = HandlePort(*conf, value);
10371066
break;
@@ -1467,6 +1496,17 @@ byte wolfSSHD_ConfigGetPwAuth(const WOLFSSHD_CONFIG* conf)
14671496
return ret;
14681497
}
14691498

1499+
byte wolfSSHD_ConfigGetPubKeyAuth(const WOLFSSHD_CONFIG* conf)
1500+
{
1501+
byte ret = 0;
1502+
1503+
if (conf != NULL) {
1504+
ret = conf->pubKeyAuth;
1505+
}
1506+
1507+
return ret;
1508+
}
1509+
14701510
byte wolfSSHD_ConfigGetPermitRoot(const WOLFSSHD_CONFIG* conf)
14711511
{
14721512
byte ret = 0;

apps/wolfsshd/configuration.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@
2323

2424
typedef struct WOLFSSHD_CONFIG WOLFSSHD_CONFIG;
2525

26+
/* Expose otherwise-static wolfsshd internals to the unit tests while keeping
27+
* them file-local in normal builds. Shared so configuration.c and auth.c use
28+
* the same convention. */
29+
#ifdef WOLFSSHD_UNIT_TEST
30+
#define WOLFSSHD_STATIC
31+
#else
32+
#define WOLFSSHD_STATIC static
33+
#endif
34+
2635
#include "auth.h"
2736

2837
/* 0 so that privilege separation is default on after struct memset'd on init */
@@ -52,6 +61,7 @@ byte wolfSSHD_ConfigGetPermitRoot(const WOLFSSHD_CONFIG* conf);
5261
byte wolfSSHD_ConfigGetPrivilegeSeparation(const WOLFSSHD_CONFIG* conf);
5362
long wolfSSHD_ConfigGetGraceTime(const WOLFSSHD_CONFIG* conf);
5463
byte wolfSSHD_ConfigGetPwAuth(const WOLFSSHD_CONFIG* conf);
64+
byte wolfSSHD_ConfigGetPubKeyAuth(const WOLFSSHD_CONFIG* conf);
5565
WOLFSSHD_CONFIG* wolfSSHD_GetUserConf(const WOLFSSHD_CONFIG* conf,
5666
const char* usr, const char* grp, const char* host,
5767
const char* localAdr, word16* localPort, const char* RDomain,

apps/wolfsshd/test/test_configuration.c

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,10 @@ static int test_ConfigDefaults(void)
194194
if (wolfSSHD_ConfigGetPwAuth(conf) == 0)
195195
ret = WS_FATAL_ERROR;
196196
}
197+
if (ret == WS_SUCCESS) {
198+
if (wolfSSHD_ConfigGetPubKeyAuth(conf) == 0)
199+
ret = WS_FATAL_ERROR;
200+
}
197201

198202
wolfSSHD_ConfigFree(conf);
199203
return ret;
@@ -242,6 +246,11 @@ static int test_ParseConfigLine(void)
242246
{"Password auth yes", "PasswordAuthentication yes", 0},
243247
{"Password auth invalid", "PasswordAuthentication wolfsshd", 1},
244248

249+
/* Public key auth tests. */
250+
{"Pubkey auth no", "PubkeyAuthentication no", 0},
251+
{"Pubkey auth yes", "PubkeyAuthentication yes", 0},
252+
{"Pubkey auth invalid", "PubkeyAuthentication wolfsshd", 1},
253+
245254
/* Include files tests. */
246255
{"Include file bad", "Include sshd_config.d/test.bad", 1},
247256
{"Include file exists", "Include sshd_config.d/01-test.conf", 0},
@@ -312,6 +321,9 @@ static int test_ConfigCopy(void)
312321
if (ret == WS_SUCCESS) ret = PCL("Port 2222");
313322
if (ret == WS_SUCCESS) ret = PCL("LoginGraceTime 30");
314323
if (ret == WS_SUCCESS) ret = PCL("PasswordAuthentication yes");
324+
/* set to the non-default value so a dropped copy (which would leave the
325+
* wolfSSHD_ConfigNew default of 1) is caught */
326+
if (ret == WS_SUCCESS) ret = PCL("PubkeyAuthentication no");
315327
if (ret == WS_SUCCESS) ret = PCL("PermitEmptyPasswords yes");
316328
if (ret == WS_SUCCESS) ret = PCL("PermitRootLogin yes");
317329
if (ret == WS_SUCCESS) ret = PCL("UsePrivilegeSeparation sandbox");
@@ -389,6 +401,12 @@ static int test_ConfigCopy(void)
389401
if (wolfSSHD_ConfigGetPwAuth(match) == 0)
390402
ret = WS_FATAL_ERROR;
391403
}
404+
/* pubKeyAuth was set to the non-default 'no' (0) on the head, so the copy
405+
* must carry 0; a dropped copy would surface as the default 1 here */
406+
if (ret == WS_SUCCESS) {
407+
if (wolfSSHD_ConfigGetPubKeyAuth(match) != 0)
408+
ret = WS_FATAL_ERROR;
409+
}
392410
if (ret == WS_SUCCESS) {
393411
if (wolfSSHD_ConfigGetPermitEmptyPw(match) == 0)
394412
ret = WS_FATAL_ERROR;
@@ -448,14 +466,17 @@ static int test_GetUserConfMatchOverride(void)
448466
* global head node unchanged. */
449467
if (ret == WS_SUCCESS) ret = PCL("Match User testuser");
450468
if (ret == WS_SUCCESS) ret = PCL("PasswordAuthentication no");
469+
if (ret == WS_SUCCESS) ret = PCL("PubkeyAuthentication no");
451470
if (ret == WS_SUCCESS) ret = PCL("PermitEmptyPasswords no");
452471
if (ret == WS_SUCCESS) ret = PCL("PermitRootLogin no");
453472
if (ret == WS_SUCCESS) ret = PCL("AuthorizedKeysFile .ssh/match_keys");
454473
#undef PCL
455474

456-
/* the global head node must keep the permissive values */
475+
/* the global head node must keep the permissive values (pubKeyAuth keeps
476+
* its default of 1, proving the Match override did not leak to the head) */
457477
if (ret == WS_SUCCESS) {
458478
if (wolfSSHD_ConfigGetPwAuth(head) != 1 ||
479+
wolfSSHD_ConfigGetPubKeyAuth(head) != 1 ||
459480
wolfSSHD_ConfigGetPermitEmptyPw(head) != 1 ||
460481
wolfSSHD_ConfigGetPermitRoot(head) != 1)
461482
ret = WS_FATAL_ERROR;
@@ -473,6 +494,7 @@ static int test_GetUserConfMatchOverride(void)
473494
* ones RequestAuthentication and DoCheckUser will now enforce */
474495
if (ret == WS_SUCCESS) {
475496
if (wolfSSHD_ConfigGetPwAuth(match) != 0 ||
497+
wolfSSHD_ConfigGetPubKeyAuth(match) != 0 ||
476498
wolfSSHD_ConfigGetPermitEmptyPw(match) != 0 ||
477499
wolfSSHD_ConfigGetPermitRoot(match) != 0)
478500
ret = WS_FATAL_ERROR;
@@ -855,12 +877,80 @@ static int test_CAKeysFileDiffers(void)
855877
return ret;
856878
}
857879

880+
/* Exercises the auth-method advertisement logic used by DefaultUserAuthTypes:
881+
* a method is only offered when its config option is enabled. Covers all four
882+
* permutations of PasswordAuthentication and PubkeyAuthentication, including the
883+
* security-relevant cases where pubkey is disabled and where both are disabled
884+
* (no methods advertised, mask == 0). */
885+
static int test_GetUserAuthTypes(void)
886+
{
887+
int ret = WS_SUCCESS;
888+
int i;
889+
890+
static const struct {
891+
const char* desc;
892+
int pwAuth; /* 1 = leave enabled, 0 = PasswordAuthentication no */
893+
int pubKeyAuth; /* 1 = leave enabled, 0 = PubkeyAuthentication no */
894+
int expected;
895+
} vectors[] = {
896+
{"both enabled advertises both", 1, 1,
897+
WOLFSSH_USERAUTH_PASSWORD | WOLFSSH_USERAUTH_PUBLICKEY},
898+
{"pubkey disabled advertises password only", 1, 0,
899+
WOLFSSH_USERAUTH_PASSWORD},
900+
{"password disabled advertises pubkey only", 0, 1,
901+
WOLFSSH_USERAUTH_PUBLICKEY},
902+
{"both disabled advertises nothing", 0, 0, 0},
903+
};
904+
const int numVectors = (int)(sizeof(vectors) / sizeof(*vectors));
905+
WOLFSSHD_CONFIG* conf;
906+
907+
for (i = 0; i < numVectors && ret == WS_SUCCESS; ++i) {
908+
Log(" Testing scenario: %s.", vectors[i].desc);
909+
910+
conf = wolfSSHD_ConfigNew(NULL);
911+
if (conf == NULL) {
912+
Log(" FAILED.\n");
913+
ret = WS_MEMORY_E;
914+
break;
915+
}
916+
917+
/* both options default to enabled in wolfSSHD_ConfigNew, so only the
918+
* disabled cases need an explicit directive */
919+
if (vectors[i].pwAuth == 0) {
920+
ret = ParseConfigLine(&conf, "PasswordAuthentication no",
921+
(int)WSTRLEN("PasswordAuthentication no"));
922+
}
923+
if (ret == WS_SUCCESS && vectors[i].pubKeyAuth == 0) {
924+
ret = ParseConfigLine(&conf, "PubkeyAuthentication no",
925+
(int)WSTRLEN("PubkeyAuthentication no"));
926+
}
927+
928+
if (ret == WS_SUCCESS) {
929+
if (wolfSSHD_GetUserAuthTypes(conf) != vectors[i].expected) {
930+
Log(" FAILED.\n");
931+
ret = WS_FATAL_ERROR;
932+
}
933+
else {
934+
Log(" PASSED.\n");
935+
}
936+
}
937+
else {
938+
Log(" FAILED.\n");
939+
}
940+
941+
wolfSSHD_ConfigFree(conf);
942+
}
943+
944+
return ret;
945+
}
946+
858947
const TEST_CASE testCases[] = {
859948
TEST_DECL(test_ConfigDefaults),
860949
TEST_DECL(test_ParseConfigLine),
861950
TEST_DECL(test_ConfigCopy),
862951
TEST_DECL(test_GetUserConfMatchOverride),
863952
TEST_DECL(test_CAKeysFileDiffers),
953+
TEST_DECL(test_GetUserAuthTypes),
864954
TEST_DECL(test_ConfigFree),
865955
#ifdef WOLFSSL_BASE64_ENCODE
866956
TEST_DECL(test_CheckAuthKeysLine),

src/internal.c

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16255,6 +16255,7 @@ int SendUserAuthRequest(WOLFSSH* ssh, byte authType, int addSig)
1625516255
static int GetAllowedAuth(WOLFSSH* ssh, char* authStr)
1625616256
{
1625716257
int typeAllowed = 0;
16258+
int authStrSz;
1625816259

1625916260
if (ssh == NULL || authStr == NULL)
1626016261
return WS_BAD_ARGUMENT;
@@ -16285,8 +16286,16 @@ static int GetAllowedAuth(WOLFSSH* ssh, char* authStr)
1628516286
}
1628616287
#endif
1628716288

16288-
/* remove last comma from the list */
16289-
return (int)XSTRLEN(authStr) - 1;
16289+
/* Remove the trailing comma from the list. An empty list (no auth methods
16290+
* allowed, e.g. a server callback that returns 0) has length 0 and must not
16291+
* underflow to a negative size; return 0 so SendUserAuthFailure emits a
16292+
* valid USERAUTH_FAILURE with an empty name-list per RFC 4252. */
16293+
authStrSz = (int)XSTRLEN(authStr);
16294+
if (authStrSz > 0) {
16295+
authStrSz--;
16296+
}
16297+
16298+
return authStrSz;
1629016299
}
1629116300

1629216301
int SendUserAuthFailure(WOLFSSH* ssh, byte partialSuccess)
@@ -18248,6 +18257,11 @@ int wolfSSH_TestDoUserAuthRequest(WOLFSSH* ssh, byte* buf, word32 len,
1824818257
return DoUserAuthRequest(ssh, buf, len, idx);
1824918258
}
1825018259

18260+
int wolfSSH_TestSendUserAuthFailure(WOLFSSH* ssh, byte partialSuccess)
18261+
{
18262+
return SendUserAuthFailure(ssh, partialSuccess);
18263+
}
18264+
1825118265
int wolfSSH_TestHighwaterCheck(WOLFSSH* ssh, byte side)
1825218266
{
1825318267
return HighwaterCheck(ssh, side);

0 commit comments

Comments
 (0)