Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC-4256 Keyboard-Interactive authentication #763

Merged
merged 1 commit into from
Feb 11, 2025
Merged
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
3 changes: 2 additions & 1 deletion .github/workflows/sshd-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ jobs:
- name: Test memory after close down
working-directory: ./wolfssh/
run: |
sudo apt-get install valgrind
sudo apt-get -y update
sudo apt-get -y install valgrind
touch sshd_config.txt
./configure --enable-all LDFLAGS="-L${{ github.workspace }}/build-dir/lib" CPPFLAGS="-I${{ github.workspace }}/build-dir/include -DWOLFSSH_NO_FPKI -DWOLFSSH_NO_SFTP_TIMEOUT -DWOLFSSH_MAX_SFTP_RW=4000000 -DMAX_PATH_SZ=120" --enable-static --disable-shared && make
sudo timeout --preserve-status -s 2 5 valgrind --error-exitcode=1 --leak-check=full ./apps/wolfsshd/wolfsshd -D -f sshd_config -h ./keys/server-key.pem -d -p 22222
Expand Down
73 changes: 73 additions & 0 deletions examples/client/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ static word32 userPrivateKeySz = sizeof(userPrivateKeyBuf);
static word32 userPrivateKeyTypeSz = 0;
static byte isPrivate = 0;

static word32 keyboardResponseCount = 0;
static byte** keyboardResponses;
static word32* keyboardResponseLengths;


#ifdef WOLFSSH_CERTS
#if 0
Expand Down Expand Up @@ -445,6 +449,9 @@ int ClientUserAuth(byte authType,
{
const char* defaultPassword = (const char*)ctx;
word32 passwordSz = 0;
#ifdef WOLFSSH_TERM
word32 entry;
#endif
int ret = WOLFSSH_USERAUTH_SUCCESS;

#ifdef DEBUG_WOLFSSH
Expand All @@ -456,6 +463,9 @@ int ClientUserAuth(byte authType,
if (authData->type & WOLFSSH_USERAUTH_PUBLICKEY) {
printf(" - publickey\n");
}
if (authData->type & WOLFSSH_USERAUTH_KEYBOARD) {
printf(" - keyboard\n");
}
printf("wolfSSH requesting to use type %d\n", authType);
#endif

Expand Down Expand Up @@ -523,7 +533,63 @@ int ClientUserAuth(byte authType,
authData->sf.password.passwordSz = passwordSz;
}
}
#ifdef WOLFSSH_TERM
else if (authType == WOLFSSH_USERAUTH_KEYBOARD) {
if (authData->sf.keyboard.promptName &&
authData->sf.keyboard.promptName[0] != '\0') {
printf("%s\n", authData->sf.keyboard.promptName);
}
if (authData->sf.keyboard.promptInstruction &&
authData->sf.keyboard.promptInstruction[0] != '\0') {
printf("%s\n", authData->sf.keyboard.promptInstruction);
}
keyboardResponseCount = authData->sf.keyboard.promptCount;
keyboardResponses =
(byte**)WMALLOC(sizeof(byte*) * keyboardResponseCount, NULL, 0);
if (keyboardResponses == NULL) {
ret = WS_MEMORY_E;
}
if (ret == WS_SUCCESS) {
authData->sf.keyboard.responses = (byte**)keyboardResponses;
keyboardResponseLengths = (word32*)WMALLOC(
sizeof(word32) * keyboardResponseCount, NULL, 0);
authData->sf.keyboard.responseLengths = keyboardResponseLengths;
}

if (keyboardResponseLengths == NULL) {
ret = WS_MEMORY_E;
}

for (entry = 0; entry < authData->sf.keyboard.promptCount; entry++) {
if (ret == WS_SUCCESS) {
printf("%s", authData->sf.keyboard.prompts[entry]);
if (!authData->sf.keyboard.promptEcho[entry]) {
ClientSetEcho(0);
}
if (WFGETS((char*)userPassword, sizeof(userPassword), stdin)
== NULL) {
fprintf(stderr, "Getting response failed.\n");
ret = WOLFSSH_USERAUTH_FAILURE;
}
else {
char* c = strpbrk((char*)userPassword, "\r\n");
if (c != NULL)
*c = '\0';
}
passwordSz = (word32)strlen((const char*)userPassword);
ClientSetEcho(1);
#ifdef USE_WINDOWS_API
printf("\r\n");
#endif
WFFLUSH(stdout);
authData->sf.keyboard.responses[entry] =
(byte*) WSTRDUP((char*)userPassword, NULL, 0);
authData->sf.keyboard.responseLengths[entry] = passwordSz;
authData->sf.keyboard.responseCount++;
}
}
}
#endif
return ret;
}

Expand Down Expand Up @@ -797,11 +863,18 @@ int ClientLoadCA(WOLFSSH_CTX* ctx, const char* caCert)
void ClientFreeBuffers(const char* pubKeyName, const char* privKeyName,
void* heap)
{
word32 entry;
if (pubKeyName != NULL && userPublicKey != NULL) {
WFREE(userPublicKey, heap, DYNTYPE_PRIVKEY);
}

if (privKeyName != NULL && userPrivateKey != NULL) {
WFREE(userPrivateKey, heap, DYNTYPE_PRIVKEY);
}

for (entry = 0; entry < keyboardResponseCount; entry++) {
WFREE(keyboardResponses[entry], NULL, 0);
}
WFREE(keyboardResponses, NULL, 0);
WFREE(keyboardResponseLengths, NULL, 0);
}
110 changes: 105 additions & 5 deletions examples/echoserver/echoserver.c
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,6 @@ static int callbackReqFailure(WOLFSSH *ssh, void *buf, word32 sz, void *ctx)
return WS_SUCCESS;
}


static void *global_req(void *ctx)
{
int ret;
Expand Down Expand Up @@ -869,7 +868,7 @@ static int ssh_worker(thread_ctx_t* threadCtx)
#if defined(HAVE_SYS_IOCTL_H)
wolfSSH_DoModes(ssh->modes, ssh->modesSz, childFd);
{
struct winsize s = {0};
struct winsize s = {0,0,0,0};

s.ws_col = ssh->widthChar;
s.ws_row = ssh->heightRows;
Expand Down Expand Up @@ -1997,6 +1996,34 @@ static int LoadPasswdList(StrList* strList, PwMapList* mapList)
return count;
}

static int LoadKeyboardList(StrList* strList, PwMapList* mapList)
{
char names[256];
char* passwd;
int count = 0;

while (strList) {
WSTRNCPY(names, strList->str, sizeof names - 1);
passwd = WSTRCHR(names, ':');
if (passwd != NULL) {
*passwd = 0;
passwd++;

PwMapNew(mapList, WOLFSSH_USERAUTH_KEYBOARD,
(byte*)names, (word32)WSTRLEN(names),
(byte*)passwd, (word32)WSTRLEN(passwd));
}
else {
fprintf(stderr, "Ignoring password: %s\n", names);
}

strList = strList->next;
count++;
}

return count;
}

#ifndef NO_FILESYSTEM
static int LoadPubKeyList(StrList* strList, int format, PwMapList* mapList)
{
Expand Down Expand Up @@ -2103,7 +2130,8 @@ static int wsUserAuth(byte authType,
#ifdef WOLFSSH_ALLOW_USERAUTH_NONE
authType != WOLFSSH_USERAUTH_NONE &&
#endif
authType != WOLFSSH_USERAUTH_PUBLICKEY) {
authType != WOLFSSH_USERAUTH_PUBLICKEY &&
authType != WOLFSSH_USERAUTH_KEYBOARD) {

return WOLFSSH_USERAUTH_FAILURE;
}
Expand All @@ -2113,6 +2141,14 @@ static int wsUserAuth(byte authType,
authData->sf.password.passwordSz,
authHash);
}
else if (authType == WOLFSSH_USERAUTH_KEYBOARD) {
if (authData->sf.keyboard.responseCount != 1) {
return WOLFSSH_USERAUTH_FAILURE;
}
wc_Sha256Hash(authData->sf.keyboard.responses[0],
authData->sf.keyboard.responseLengths[0],
authHash);
}
else if (authType == WOLFSSH_USERAUTH_PUBLICKEY) {
wc_Sha256Hash(authData->sf.publicKey.publicKey,
authData->sf.publicKey.publicKeySz,
Expand Down Expand Up @@ -2213,6 +2249,14 @@ static int wsUserAuth(byte authType,
WOLFSSH_USERAUTH_REJECTED;
}
}
else if (authData->type == WOLFSSH_USERAUTH_KEYBOARD) {
if (WMEMCMP(map->p, authHash, WC_SHA256_DIGEST_SIZE) == 0) {
return WOLFSSH_USERAUTH_SUCCESS;
}
else {
return WOLFSSH_USERAUTH_INVALID_PASSWORD;
}
}
#ifdef WOLFSSH_ALLOW_USERAUTH_NONE
else if (authData->type == WOLFSSH_USERAUTH_NONE) {
return WOLFSSH_USERAUTH_SUCCESS;
Expand All @@ -2228,6 +2272,13 @@ static int wsUserAuth(byte authType,
return WOLFSSH_USERAUTH_INVALID_USER;
}

static int keyboardCallback(WS_UserAuthData_Keyboard *kbAuth, void *ctx)
{
WS_UserAuthData_Keyboard *kbAuthData = (WS_UserAuthData_Keyboard*) ctx;
WMEMCPY(kbAuth, kbAuthData, sizeof(WS_UserAuthData_Keyboard));

return WS_SUCCESS;
}

#ifdef WOLFSSH_SFTP
/*
Expand Down Expand Up @@ -2312,6 +2363,9 @@ static void ShowUsage(void)
" load in an X.509 DER cert to accept from peer\n");
printf(" -P <name>:<password>\n"
" add password to accept from peer\n");
printf(" -i <name>:<password>\n"
" add passowrd to accept via keyboard-interactive "
"from peer\n");
#ifdef WOLFSSH_CERTS
printf(" -a <file> load in a root CA certificate file\n");
#endif
Expand Down Expand Up @@ -2352,6 +2406,8 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
StrList* derPubKeyList = NULL;
#endif
StrList* passwdList = NULL;
StrList* keyboardList = NULL;
WS_UserAuthData_Keyboard kbAuthData;
WS_SOCKET_T listenFd = WOLFSSH_SOCKET_INVALID;
word32 defaultHighwater = EXAMPLE_HIGHWATER_MARK;
word32 threadCount = 0;
Expand All @@ -2376,9 +2432,10 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
int argc = serverArgs->argc;
char** argv = serverArgs->argv;
serverArgs->return_code = EXIT_SUCCESS;
kbAuthData.promptCount = 0;

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

case 'i':
keyboardList = StrListAdd(keyboardList, myoptarg);
break;

case 'b':
userAuthWouldBlock = atoi(myoptarg);
break;
Expand Down Expand Up @@ -2529,6 +2590,7 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
wolfSSH_SetUserAuth(ctx, wsUserAuth);
else
wolfSSH_SetUserAuth(ctx, ((func_args*)args)->user_auth);

wolfSSH_SetUserAuthResult(ctx, wsUserAuthResult);
wolfSSH_CTX_SetBanner(ctx, echoserverBanner);
#ifdef WOLFSSH_AGENT
Expand Down Expand Up @@ -2561,6 +2623,35 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
passwdList = NULL;
}

if (keyboardList) {
LoadKeyboardList(keyboardList, &pwMapList);
StrListFree(keyboardList);
keyboardList = NULL;
kbAuthData.promptCount = 1;
kbAuthData.promptName = NULL;
kbAuthData.promptNameSz = 0;
kbAuthData.promptInstruction = NULL;
kbAuthData.promptInstructionSz = 0;
kbAuthData.promptLanguage = NULL;
kbAuthData.promptLanguageSz = 0;
kbAuthData.prompts = (byte**)WMALLOC(sizeof(byte*), NULL, 0);
if (kbAuthData.prompts == NULL) {
ES_ERROR("Error allocating prompts");
}
kbAuthData.prompts[0] = (byte*)"KB Auth Password: ";
kbAuthData.promptLengths = (word32*)WMALLOC(sizeof(word32), NULL, 0);
if (kbAuthData.prompts == NULL) {
ES_ERROR("Error allocating promptLengths");
}
kbAuthData.promptLengths[0] = 18;
kbAuthData.promptEcho = (byte*)WMALLOC(sizeof(byte), NULL, 0);
if (kbAuthData.prompts == NULL) {
ES_ERROR("Error allocating promptEcho");
}
kbAuthData.promptEcho[0] = 0;
wolfSSH_SetKeyboardAuthPrompts(ctx, keyboardCallback);
}

{
const char* bufName = NULL;
#ifndef WOLFSSH_SMALL_STACK
Expand Down Expand Up @@ -2762,6 +2853,7 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
#endif
wolfSSH_SetUserAuthCtx(ssh, &pwMapList);
wolfSSH_SetKeyingCompletionCbCtx(ssh, (void*)ssh);
wolfSSH_SetKeyboardAuthCtx(ssh, &kbAuthData);
/* Use the session object for its own highwater callback ctx */
if (defaultHighwater > 0) {
wolfSSH_SetHighwaterCtx(ssh, (void*)ssh);
Expand Down Expand Up @@ -2834,6 +2926,11 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
if (listenFd != WOLFSSH_SOCKET_INVALID) {
WCLOSESOCKET(listenFd);
}
if (kbAuthData.promptCount > 0) {
WFREE(kbAuthData.promptLengths, NULL, 0);
WFREE(kbAuthData.prompts, NULL, 0);
WFREE(kbAuthData.promptEcho, NULL, 0);
}
wc_FreeMutex(&doneLock);
PwMapListDelete(&pwMapList);
wolfSSH_CTX_free(ctx);
Expand Down Expand Up @@ -2867,8 +2964,11 @@ int wolfSSH_Echoserver(int argc, char** argv)

WSTARTTCP();

#ifdef DEBUG_WOLFSSH

#ifdef DEBUG_WOLFSSL
wolfSSL_Debugging_ON();
#endif
#ifdef DEBUG_WOLFSSH
wolfSSH_Debugging_ON();
#endif

Expand Down
Loading