Skip to content

Commit 7ea9f8f

Browse files
committed
RFC-4256 Keyboard-Interactive authentication
This implements Keyboard-Interactive authentication. Adds an additional callback set by `wolfSSH_KeyboarAuthPrompts()` which will set a callback in the server to ask the application to provide the prompt details for the client.
1 parent 86499a5 commit 7ea9f8f

File tree

14 files changed

+1672
-22
lines changed

14 files changed

+1672
-22
lines changed

examples/client/common.c

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ static word32 userPrivateKeySz = sizeof(userPrivateKeyBuf);
5757
static word32 userPrivateKeyTypeSz = 0;
5858
static byte isPrivate = 0;
5959

60+
static word32 keyboardResponseCount = 0;
61+
static byte** keyboardResponses;
62+
static word32* keyboardResponseLengths;
63+
6064

6165
#ifdef WOLFSSH_CERTS
6266
#if 0
@@ -445,6 +449,9 @@ int ClientUserAuth(byte authType,
445449
{
446450
const char* defaultPassword = (const char*)ctx;
447451
word32 passwordSz = 0;
452+
#ifdef WOLFSSH_TERM
453+
word32 entry;
454+
#endif
448455
int ret = WOLFSSH_USERAUTH_SUCCESS;
449456

450457
#ifdef DEBUG_WOLFSSH
@@ -456,6 +463,9 @@ int ClientUserAuth(byte authType,
456463
if (authData->type & WOLFSSH_USERAUTH_PUBLICKEY) {
457464
printf(" - publickey\n");
458465
}
466+
if (authData->type & WOLFSSH_USERAUTH_KEYBOARD) {
467+
printf(" - keyboard\n");
468+
}
459469
printf("wolfSSH requesting to use type %d\n", authType);
460470
#endif
461471

@@ -523,7 +533,52 @@ int ClientUserAuth(byte authType,
523533
authData->sf.password.passwordSz = passwordSz;
524534
}
525535
}
526-
536+
#ifdef WOLFSSH_TERM
537+
else if (authType == WOLFSSH_USERAUTH_KEYBOARD) {
538+
if (authData->sf.keyboard.promptName &&
539+
authData->sf.keyboard.promptName[0] != '\0') {
540+
printf("%s\n", authData->sf.keyboard.promptName);
541+
}
542+
if (authData->sf.keyboard.promptInstruction &&
543+
authData->sf.keyboard.promptInstruction[0] != '\0') {
544+
printf("%s\n", authData->sf.keyboard.promptInstruction);
545+
}
546+
keyboardResponseCount = authData->sf.keyboard.promptCount;
547+
keyboardResponses = WMALLOC(sizeof(byte*) * keyboardResponseCount, NULL,
548+
0);
549+
authData->sf.keyboard.responses = (byte**)keyboardResponses;
550+
keyboardResponseLengths = WMALLOC(
551+
sizeof(word32) * keyboardResponseCount, NULL, 0);
552+
authData->sf.keyboard.responseLengths = keyboardResponseLengths;
553+
554+
for (entry = 0; entry < authData->sf.keyboard.promptCount; entry++) {
555+
printf("%s", authData->sf.keyboard.prompts[entry]);
556+
if (!authData->sf.keyboard.promptEcho[entry]) {
557+
ClientSetEcho(0);
558+
}
559+
if (WFGETS((char*)userPassword, sizeof(userPassword), stdin)
560+
== NULL) {
561+
fprintf(stderr, "Getting response failed.\n");
562+
ret = WOLFSSH_USERAUTH_FAILURE;
563+
}
564+
else {
565+
char* c = strpbrk((char*)userPassword, "\r\n");
566+
if (c != NULL)
567+
*c = '\0';
568+
}
569+
passwordSz = (word32)strlen((const char*)userPassword);
570+
ClientSetEcho(1);
571+
#ifdef USE_WINDOWS_API
572+
printf("\r\n");
573+
#endif
574+
WFFLUSH(stdout);
575+
authData->sf.keyboard.responses[entry] =
576+
(byte*) WSTRDUP((char*)userPassword, NULL, 0);
577+
authData->sf.keyboard.responseLengths[entry] = passwordSz;
578+
authData->sf.keyboard.responseCount++;
579+
}
580+
}
581+
#endif
527582
return ret;
528583
}
529584

@@ -797,11 +852,18 @@ int ClientLoadCA(WOLFSSH_CTX* ctx, const char* caCert)
797852
void ClientFreeBuffers(const char* pubKeyName, const char* privKeyName,
798853
void* heap)
799854
{
855+
word32 entry;
800856
if (pubKeyName != NULL && userPublicKey != NULL) {
801857
WFREE(userPublicKey, heap, DYNTYPE_PRIVKEY);
802858
}
803859

804860
if (privKeyName != NULL && userPrivateKey != NULL) {
805861
WFREE(userPrivateKey, heap, DYNTYPE_PRIVKEY);
806862
}
863+
864+
for (entry = 0; entry < keyboardResponseCount; entry++) {
865+
WFREE(keyboardResponses[entry], NULL, 0);
866+
}
867+
WFREE(keyboardResponses, NULL, 0);
868+
WFREE(keyboardResponseLengths, NULL, 0);
807869
}

examples/echoserver/echoserver.c

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,6 @@ static int callbackReqFailure(WOLFSSH *ssh, void *buf, word32 sz, void *ctx)
289289
return WS_SUCCESS;
290290
}
291291

292-
293292
static void *global_req(void *ctx)
294293
{
295294
int ret;
@@ -1997,6 +1996,34 @@ static int LoadPasswdList(StrList* strList, PwMapList* mapList)
19971996
return count;
19981997
}
19991998

1999+
static int LoadKeyboardList(StrList* strList, PwMapList* mapList)
2000+
{
2001+
char names[256];
2002+
char* passwd;
2003+
int count = 0;
2004+
2005+
while (strList) {
2006+
WSTRNCPY(names, strList->str, sizeof names - 1);
2007+
passwd = WSTRCHR(names, ':');
2008+
if (passwd != NULL) {
2009+
*passwd = 0;
2010+
passwd++;
2011+
2012+
PwMapNew(mapList, WOLFSSH_USERAUTH_KEYBOARD,
2013+
(byte*)names, (word32)WSTRLEN(names),
2014+
(byte*)passwd, (word32)WSTRLEN(passwd));
2015+
}
2016+
else {
2017+
fprintf(stderr, "Ignoring password: %s\n", names);
2018+
}
2019+
2020+
strList = strList->next;
2021+
count++;
2022+
}
2023+
2024+
return count;
2025+
}
2026+
20002027
#ifndef NO_FILESYSTEM
20012028
static int LoadPubKeyList(StrList* strList, int format, PwMapList* mapList)
20022029
{
@@ -2103,7 +2130,8 @@ static int wsUserAuth(byte authType,
21032130
#ifdef WOLFSSH_ALLOW_USERAUTH_NONE
21042131
authType != WOLFSSH_USERAUTH_NONE &&
21052132
#endif
2106-
authType != WOLFSSH_USERAUTH_PUBLICKEY) {
2133+
authType != WOLFSSH_USERAUTH_PUBLICKEY &&
2134+
authType != WOLFSSH_USERAUTH_KEYBOARD) {
21072135

21082136
return WOLFSSH_USERAUTH_FAILURE;
21092137
}
@@ -2113,6 +2141,14 @@ static int wsUserAuth(byte authType,
21132141
authData->sf.password.passwordSz,
21142142
authHash);
21152143
}
2144+
else if (authType == WOLFSSH_USERAUTH_KEYBOARD) {
2145+
if (authData->sf.keyboard.responseCount != 1) {
2146+
return WOLFSSH_USERAUTH_FAILURE;
2147+
}
2148+
wc_Sha256Hash(authData->sf.keyboard.responses[0],
2149+
authData->sf.keyboard.responseLengths[0],
2150+
authHash);
2151+
}
21162152
else if (authType == WOLFSSH_USERAUTH_PUBLICKEY) {
21172153
wc_Sha256Hash(authData->sf.publicKey.publicKey,
21182154
authData->sf.publicKey.publicKeySz,
@@ -2213,6 +2249,14 @@ static int wsUserAuth(byte authType,
22132249
WOLFSSH_USERAUTH_REJECTED;
22142250
}
22152251
}
2252+
else if (authData->type == WOLFSSH_USERAUTH_KEYBOARD) {
2253+
if (WMEMCMP(map->p, authHash, WC_SHA256_DIGEST_SIZE) == 0) {
2254+
return WOLFSSH_USERAUTH_SUCCESS;
2255+
}
2256+
else {
2257+
return WOLFSSH_USERAUTH_INVALID_PASSWORD;
2258+
}
2259+
}
22162260
#ifdef WOLFSSH_ALLOW_USERAUTH_NONE
22172261
else if (authData->type == WOLFSSH_USERAUTH_NONE) {
22182262
return WOLFSSH_USERAUTH_SUCCESS;
@@ -2228,6 +2272,13 @@ static int wsUserAuth(byte authType,
22282272
return WOLFSSH_USERAUTH_INVALID_USER;
22292273
}
22302274

2275+
static int keyboardCallback(WS_UserAuthData_Keyboard *kbAuth, void *ctx)
2276+
{
2277+
WS_UserAuthData_Keyboard *kbAuthData = (WS_UserAuthData_Keyboard*) ctx;
2278+
WMEMCPY(kbAuth, kbAuthData, sizeof(WS_UserAuthData_Keyboard));
2279+
2280+
return WS_SUCCESS;
2281+
}
22312282

22322283
#ifdef WOLFSSH_SFTP
22332284
/*
@@ -2312,6 +2363,9 @@ static void ShowUsage(void)
23122363
" load in an X.509 DER cert to accept from peer\n");
23132364
printf(" -P <name>:<password>\n"
23142365
" add password to accept from peer\n");
2366+
printf(" -i <name>:<password>\n"
2367+
" add passowrd to accept via keyboard-interactive "
2368+
"from peer\n");
23152369
#ifdef WOLFSSH_CERTS
23162370
printf(" -a <file> load in a root CA certificate file\n");
23172371
#endif
@@ -2352,6 +2406,8 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
23522406
StrList* derPubKeyList = NULL;
23532407
#endif
23542408
StrList* passwdList = NULL;
2409+
StrList* keyboardList = NULL;
2410+
WS_UserAuthData_Keyboard kbAuthData;
23552411
WS_SOCKET_T listenFd = WOLFSSH_SOCKET_INVALID;
23562412
word32 defaultHighwater = EXAMPLE_HIGHWATER_MARK;
23572413
word32 threadCount = 0;
@@ -2376,9 +2432,10 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
23762432
int argc = serverArgs->argc;
23772433
char** argv = serverArgs->argv;
23782434
serverArgs->return_code = EXIT_SUCCESS;
2435+
kbAuthData.promptCount = 0;
23792436

23802437
if (argc > 0) {
2381-
const char* optlist = "?1a:d:efEp:R:Ni:j:I:J:K:P:k:b:";
2438+
const char* optlist = "?1a:d:efEp:R:Ni:j:i:I:J:K:P:k:b:";
23822439
myoptind = 0;
23832440
while ((ch = mygetopt(argc, argv, optlist)) != -1) {
23842441
switch (ch) {
@@ -2462,6 +2519,10 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
24622519
passwdList = StrListAdd(passwdList, myoptarg);
24632520
break;
24642521

2522+
case 'i':
2523+
keyboardList = StrListAdd(keyboardList, myoptarg);
2524+
break;
2525+
24652526
case 'b':
24662527
userAuthWouldBlock = atoi(myoptarg);
24672528
break;
@@ -2529,6 +2590,7 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
25292590
wolfSSH_SetUserAuth(ctx, wsUserAuth);
25302591
else
25312592
wolfSSH_SetUserAuth(ctx, ((func_args*)args)->user_auth);
2593+
25322594
wolfSSH_SetUserAuthResult(ctx, wsUserAuthResult);
25332595
wolfSSH_CTX_SetBanner(ctx, echoserverBanner);
25342596
#ifdef WOLFSSH_AGENT
@@ -2561,6 +2623,26 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
25612623
passwdList = NULL;
25622624
}
25632625

2626+
if (keyboardList) {
2627+
LoadKeyboardList(keyboardList, &pwMapList);
2628+
StrListFree(keyboardList);
2629+
keyboardList = NULL;
2630+
kbAuthData.promptCount = 1;
2631+
kbAuthData.promptName = NULL;
2632+
kbAuthData.promptNameSz = 0;
2633+
kbAuthData.promptInstruction = NULL;
2634+
kbAuthData.promptInstructionSz = 0;
2635+
kbAuthData.promptLanguage = NULL;
2636+
kbAuthData.promptLanguageSz = 0;
2637+
kbAuthData.prompts = WMALLOC(sizeof(char*), NULL, 0);
2638+
kbAuthData.prompts[0] = (byte*)"KB Auth Password: ";
2639+
kbAuthData.promptLengths = WMALLOC(sizeof(word32), NULL, 0);
2640+
kbAuthData.promptLengths[0] = 18;
2641+
kbAuthData.promptEcho = WMALLOC(sizeof(byte), NULL, 0);
2642+
kbAuthData.promptEcho[0] = 0;
2643+
wolfSSH_SetKeyboardAuthPrompts(ctx, keyboardCallback);
2644+
}
2645+
25642646
{
25652647
const char* bufName = NULL;
25662648
#ifndef WOLFSSH_SMALL_STACK
@@ -2762,6 +2844,7 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
27622844
#endif
27632845
wolfSSH_SetUserAuthCtx(ssh, &pwMapList);
27642846
wolfSSH_SetKeyingCompletionCbCtx(ssh, (void*)ssh);
2847+
wolfSSH_SetKeyboardAuthCtx(ssh, &kbAuthData);
27652848
/* Use the session object for its own highwater callback ctx */
27662849
if (defaultHighwater > 0) {
27672850
wolfSSH_SetHighwaterCtx(ssh, (void*)ssh);
@@ -2834,6 +2917,11 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
28342917
if (listenFd != WOLFSSH_SOCKET_INVALID) {
28352918
WCLOSESOCKET(listenFd);
28362919
}
2920+
if (kbAuthData.promptCount > 0) {
2921+
WFREE(kbAuthData.promptLengths, NULL, 0);
2922+
WFREE(kbAuthData.prompts, NULL, 0);
2923+
WFREE(kbAuthData.promptEcho, NULL, 0);
2924+
}
28372925
wc_FreeMutex(&doneLock);
28382926
PwMapListDelete(&pwMapList);
28392927
wolfSSH_CTX_free(ctx);

0 commit comments

Comments
 (0)