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.
pull/763/head
Andrew Hutchings 2025-01-10 17:06:32 +00:00
parent 34c3794396
commit a4733ecd95
15 changed files with 1714 additions and 27 deletions

View File

@ -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

View File

@ -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
@ -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
@ -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
@ -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;
}
@ -797,6 +863,7 @@ 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);
}
@ -804,4 +871,10 @@ void ClientFreeBuffers(const char* pubKeyName, const char* privKeyName,
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);
}

View File

@ -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;
@ -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;
@ -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)
{
@ -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;
}
@ -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,
@ -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;
@ -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
/*
@ -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
@ -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;
@ -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) {
@ -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;
@ -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
@ -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
@ -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);
@ -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);
@ -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

View File

@ -598,7 +598,8 @@ INLINE static int IsMessageAllowedServer(WOLFSSH *ssh, byte msg)
}
/* Explicitly check for the user authentication messages that
* only the server sends, it shouldn't receive them. */
if (msg > MSGID_USERAUTH_RESTRICT) {
if ((msg > MSGID_USERAUTH_RESTRICT) &&
(msg != MSGID_USERAUTH_INFO_RESPONSE)) {
WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by server "
"during user authentication", msg);
return 0;
@ -1035,7 +1036,8 @@ WOLFSSH* SshInit(WOLFSSH* ssh, WOLFSSH_CTX* ctx)
ssh->authId = ID_USERAUTH_PUBLICKEY;
ssh->supportedAuth[0] = ID_USERAUTH_PUBLICKEY;
ssh->supportedAuth[1] = ID_USERAUTH_PASSWORD;
ssh->supportedAuth[2] = ID_NONE; /* ID_NONE is treated as empty slot */
ssh->supportedAuth[2] = ID_USERAUTH_KEYBOARD;
ssh->supportedAuth[3] = ID_NONE; /* ID_NONE is treated as empty slot */
ssh->nextChannel = DEFAULT_NEXT_CHANNEL;
ssh->blockSz = MIN_BLOCK_SZ;
ssh->encryptId = ID_NONE;
@ -2520,6 +2522,7 @@ static const NameIdPair NameIdMap[] = {
/* UserAuth IDs */
{ ID_USERAUTH_PASSWORD, TYPE_OTHER, "password" },
{ ID_USERAUTH_PUBLICKEY, TYPE_OTHER, "publickey" },
{ ID_USERAUTH_KEYBOARD, TYPE_OTHER, "keyboard-interactive" },
/* Channel Type IDs */
{ ID_CHANTYPE_SESSION, TYPE_OTHER, "session" },
@ -3438,7 +3441,8 @@ static int GetNameListRaw(byte* idList, word32* idListSz,
{
const char* displayName = IdToName(id);
if (displayName) {
WLOG(WS_LOG_DEBUG, "GNL: name ID = %s", displayName);
WLOG(WS_LOG_DEBUG, "GNL: name ID %.*s matches %s", nameSz,
name, displayName);
}
}
if (id != ID_UNKNOWN || idListIdx == 0) {
@ -6207,6 +6211,151 @@ static int DoUserAuthRequestNone(WOLFSSH* ssh, WS_UserAuthData* authData,
#endif
static int DoUserAuthInfoResponse(WOLFSSH* ssh,
byte* buf, word32 len, word32* idx)
{
word32 begin;
WS_UserAuthData authData;
WS_UserAuthData_Keyboard* kb = NULL;
int ret = WS_SUCCESS;
int authFailure = 0;
byte partialSuccess = 0;
word32 entry;
word32 allocatedCount = 0;
WLOG(WS_LOG_DEBUG, "Entering DoUserAuthInfoResponse()");
if (ssh == NULL || buf == NULL || len == 0 || idx == NULL) {
ret = WS_BAD_ARGUMENT;
}
if (ret == WS_SUCCESS) {
begin = *idx;
kb = &authData.sf.keyboard;
authData.type = WOLFSSH_USERAUTH_KEYBOARD;
authData.username = (byte*)ssh->userName;
authData.usernameSz = ssh->userNameSz;
ret = GetUint32(&kb->responseCount, buf, len, &begin);
}
if ((ret == WS_SUCCESS) &&
(ssh->kbAuth.promptCount != kb->responseCount)) {
WLOG(WS_LOG_DEBUG, "DUARKB: Invalid number of responses received");
ret = WS_USER_AUTH_E;
}
if (ret == WS_SUCCESS && kb->responseCount > WOLFSSH_MAX_PROMPTS) {
WLOG(WS_LOG_DEBUG, "DUARKB: Received too many responses (%d), max: %d",
kb->responseCount, WOLFSSH_MAX_PROMPTS);
ret = WS_USER_AUTH_E;
}
if (ret == WS_SUCCESS) {
allocatedCount = kb->responseCount;
if (kb->responseCount) {
kb->responseLengths =
(word32*)WMALLOC(sizeof(word32) * kb->responseCount, NULL, 0);
if (!kb->responseLengths) {
ret = WS_MEMORY_E;
}
}
else {
kb->responseLengths = NULL;
}
}
if (ret == WS_SUCCESS) {
if (kb->responseCount) {
kb->responses = (byte**) WMALLOC(sizeof(byte*) * kb->responseCount,
NULL, 0);
if (!kb->responses) {
ret = WS_MEMORY_E;
}
}
else {
kb->responses = NULL;
}
}
if (ret == WS_SUCCESS) {
for (entry = 0; entry < kb->responseCount; entry++) {
if (ret == WS_SUCCESS) {
ret = GetStringRef(&kb->responseLengths[entry],
(const byte**)&kb->responses[entry], buf, len, &begin);
}
}
}
if (ret == WS_SUCCESS) {
if (ssh->ctx->userAuthCb != NULL) {
WLOG(WS_LOG_DEBUG, "DUARKB: Calling the userauth callback");
ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_KEYBOARD,
&authData, ssh->userAuthCtx);
if (ret == WOLFSSH_USERAUTH_SUCCESS) {
WLOG(WS_LOG_DEBUG, "DUARKB: keyboard check success");
ret = WS_SUCCESS;
}
else if (ret == WOLFSSH_USERAUTH_SUCCESS_ANOTHER) {
WLOG(WS_LOG_DEBUG, "DUARKB: keyboard check success, another wanted");
}
else if (ret == WOLFSSH_USERAUTH_PARTIAL_SUCCESS) {
WLOG(WS_LOG_DEBUG, "DUARKB: keyboard check partial success");
partialSuccess = 1;
ret = WS_SUCCESS;
}
else if (ret == WOLFSSH_USERAUTH_REJECTED) {
WLOG(WS_LOG_DEBUG, "DUARKB: keyboard rejected");
#ifndef NO_FAILURE_ON_REJECTED
authFailure = 1;
#endif
ret = WS_USER_AUTH_E;
}
else if (ret == WOLFSSH_USERAUTH_WOULD_BLOCK) {
WLOG(WS_LOG_DEBUG, "DUARKB: keyboard callback would block");
ret = WS_AUTH_PENDING;
}
else {
WLOG(WS_LOG_DEBUG, "DUARKB: keyboard check failed, retry");
authFailure = 1;
ret = WS_SUCCESS;
}
}
else {
WLOG(WS_LOG_DEBUG, "DUARKB: No user auth callback");
authFailure = 1;
}
}
if (allocatedCount) {
WFREE(kb->responseLengths, NULL, 0);
WFREE(kb->responses, NULL, 0);
}
if (ret == WS_SUCCESS || ret == WOLFSSH_USERAUTH_SUCCESS_ANOTHER) {
*idx = begin;
}
if (authFailure || partialSuccess) {
ret = SendUserAuthFailure(ssh, partialSuccess);
}
else if (ret == WOLFSSH_USERAUTH_SUCCESS_ANOTHER) {
ret = SendUserAuthKeyboardRequest(ssh, &authData);
}
else if (ret == WS_SUCCESS) {
ssh->clientState = CLIENT_USERAUTH_DONE;
}
WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthInfoResponse(), ret = %d", ret);
return ret;
}
/* Utility for DoUserAuthRequest() */
static int DoUserAuthRequestPassword(WOLFSSH* ssh, WS_UserAuthData* authData,
byte* buf, word32 len, word32* idx)
@ -7416,6 +7565,10 @@ static int DoUserAuthRequest(WOLFSSH* ssh,
ret = GetUint32(&authData.serviceNameSz, buf, len, &begin);
}
if (ret == WS_SUCCESS) {
ret = wolfSSH_SetUsernameRaw(ssh, authData.username, authData.usernameSz);
}
if (ret == WS_SUCCESS) {
if (authData.serviceNameSz > len - begin) {
ret = WS_BUFFER_E;
@ -7436,6 +7589,9 @@ static int DoUserAuthRequest(WOLFSSH* ssh,
if (authNameId == ID_USERAUTH_PASSWORD)
ret = DoUserAuthRequestPassword(ssh, &authData, buf, len, &begin);
else if (authNameId == ID_USERAUTH_KEYBOARD) {
ret = SendUserAuthKeyboardRequest(ssh, &authData);
}
#if !defined(WOLFSSH_NO_RSA) || !defined(WOLFSSH_NO_ECDSA)
else if (authNameId == ID_USERAUTH_PUBLICKEY) {
authData.sf.publicKey.dataToSign = buf + *idx;
@ -7469,8 +7625,9 @@ static int DoUserAuthRequest(WOLFSSH* ssh,
static int DoUserAuthFailure(WOLFSSH* ssh,
byte* buf, word32 len, word32* idx)
{
byte authList[3]; /* Should only ever be password, publickey, hostname */
word32 authListSz = 3;
byte authList[4]; /* Should only ever be password, publickey, hostname,
keyboard */
word32 authListSz = 4;
byte partialSuccess;
byte authType = 0;
int ret = WS_SUCCESS;
@ -7498,6 +7655,9 @@ static int DoUserAuthFailure(WOLFSSH* ssh,
case ID_USERAUTH_PASSWORD:
authType |= WOLFSSH_USERAUTH_PASSWORD;
break;
case ID_USERAUTH_KEYBOARD:
authType |= WOLFSSH_USERAUTH_KEYBOARD;
break;
#if !defined(WOLFSSH_NO_RSA) || !defined(WOLFSSH_NO_ECDSA)
case ID_USERAUTH_PUBLICKEY:
authType |= WOLFSSH_USERAUTH_PUBLICKEY;
@ -7540,6 +7700,9 @@ static int DoUserAuthSuccess(WOLFSSH* ssh,
return ret;
}
if (ssh->serverState == SERVER_USERAUTH_ACCEPT_KEYBOARD)
ssh->serverState = SERVER_USERAUTH_ACCEPT_KEYBOARD_DONE;
else
ssh->serverState = SERVER_USERAUTH_ACCEPT_DONE;
WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthSuccess(), ret = %d", ret);
@ -7575,6 +7738,102 @@ static int DoUserAuthBanner(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
}
static int DoUserAuthInfoRequest(WOLFSSH* ssh, byte* buf, word32 len,
word32* idx)
{
int ret = WS_SUCCESS;
word32 begin;
word32 promptSz = 0;
word32 entry;
byte *authName = NULL;
byte *authInstruction = NULL;
byte *language = NULL;
byte *echo = NULL;
byte **prompts = NULL;
WLOG(WS_LOG_DEBUG, "Entering DoUserAuthInfoRequest()");
if (ssh == NULL || buf == NULL || len == 0 || idx == NULL)
ret = WS_BAD_ARGUMENT;
if (ret == WS_SUCCESS) {
begin = *idx;
ret = GetStringAlloc(ssh->ctx->heap, (char**)&authName, buf, len,
&begin);
}
if (ret == WS_SUCCESS)
ret = GetStringAlloc(ssh->ctx->heap, (char**)&authInstruction, buf, len,
&begin);
if (ret == WS_SUCCESS)
ret = GetStringAlloc(ssh->ctx->heap, (char**)&language, buf, len,
&begin);
if (ret == WS_SUCCESS)
ret = GetUint32(&promptSz, buf, len, &begin);
if (ret == WS_SUCCESS && promptSz > WOLFSSH_MAX_PROMPTS) {
WLOG(WS_LOG_DEBUG, "Received too many prompts (%d), max: %d", promptSz,
WOLFSSH_MAX_PROMPTS);
ret = WS_USER_AUTH_E;
}
if (ret == WS_SUCCESS && promptSz) {
prompts = (byte**)WMALLOC(sizeof(byte*) * promptSz, ssh->ctx->heap,
DYNTYPE_BUFFER);
if (!prompts) {
ret = WS_MEMORY_E;
} else {
echo = (byte*)WMALLOC(sizeof(byte) * promptSz, ssh->ctx->heap,
DYNTYPE_BUFFER);
}
if (!echo) {
ret = WS_MEMORY_E;
} else {
WMEMSET(prompts, '\0', sizeof(char*) * promptSz);
for (entry = 0; entry < promptSz; entry++) {
ret = GetStringAlloc(ssh->ctx->heap, (char**)&prompts[entry],
buf, len, &begin);
if (ret != WS_SUCCESS)
break;
ret = GetBoolean(&echo[entry], buf, len, &begin);
if (ret != WS_SUCCESS)
break;
}
}
}
if (ret == WS_SUCCESS) {
*idx += len;
ssh->kbAuth.promptCount = promptSz;
ssh->kbAuth.prompts = (byte**)prompts;
ssh->kbAuth.promptInstruction = authInstruction;
ssh->kbAuth.promptName = authName;
ssh->kbAuth.promptLanguage = language;
ssh->kbAuth.promptEcho = echo;
WLOG(WS_LOG_DEBUG, "Got keyboard-interactive prompt '%s'", authName);
} else {
if (prompts) {
for (entry = 0; entry < promptSz; entry++) {
WFREE((void*)prompts[entry], ssh->ctx->heap, DYNTYPE_BUFFER);
}
}
WFREE(prompts, ssh->ctx->heap, DYNTYPE_BUFFER);
WFREE(echo, ssh->ctx->heap, DYNTYPE_BUFFER);
}
if (ret == WS_SUCCESS)
ssh->serverState = SERVER_USERAUTH_ACCEPT_KEYBOARD;
WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthInfoRequest(), ret = %d", ret);
return ret;
}
#ifdef WOLFSSH_FWD
static int DoGlobalRequestFwd(WOLFSSH* ssh,
byte* buf, word32 len, word32* idx, int wantReply, int isCancel)
@ -8925,6 +9184,16 @@ static int DoPacket(WOLFSSH* ssh, byte* bufferConsumed)
ret = DoUserAuthRequest(ssh, buf + idx, payloadSz, &payloadIdx);
break;
case MSGID_USERAUTH_INFO_RESPONSE:
WLOG(WS_LOG_DEBUG, "Decoding MSG_USERAUTH_INFO_RESPONSE");
ret = DoUserAuthInfoResponse(ssh, buf + idx, payloadSz, &payloadIdx);
break;
case MSGID_USERAUTH_INFO_REQUEST:
WLOG(WS_LOG_DEBUG, "Decoding MSGID_USERAUTH_INFO_REQUEST");
ret = DoUserAuthInfoRequest(ssh, buf + idx, payloadSz, &payloadIdx);
break;
case MSGID_USERAUTH_FAILURE:
WLOG(WS_LOG_DEBUG, "Decoding MSGID_USERAUTH_FAILURE");
ret = DoUserAuthFailure(ssh, buf + idx, payloadSz, &payloadIdx);
@ -12674,6 +12943,225 @@ static int BuildUserAuthRequestPassword(WOLFSSH* ssh,
return ret;
}
static int PrepareUserAuthRequestKeyboard(WOLFSSH* ssh, word32* payloadSz,
const WS_UserAuthData* authData)
{
int ret = WS_SUCCESS;
word32 entry;
if (ssh == NULL || payloadSz == NULL || authData == NULL)
ret = WS_BAD_ARGUMENT;
if (ret == WS_SUCCESS)
{
*payloadSz += LENGTH_SZ * 3;
if (authData->sf.keyboard.promptName) {
*payloadSz += authData->sf.keyboard.promptNameSz;
}
if (authData->sf.keyboard.promptInstruction) {
*payloadSz += authData->sf.keyboard.promptInstructionSz;
}
if (authData->sf.keyboard.promptLanguage) {
*payloadSz += authData->sf.keyboard.promptLanguageSz;
}
*payloadSz += LENGTH_SZ;
for (entry = 0; entry < authData->sf.keyboard.promptCount; entry++) {
*payloadSz += LENGTH_SZ +
authData->sf.keyboard.promptLengths[entry];
*payloadSz += BOOLEAN_SZ;
}
}
return ret;
}
static int BuildUserAuthRequestKeyboard(WOLFSSH* ssh, byte* output, word32* idx,
const WS_UserAuthData* authData)
{
int ret = WS_SUCCESS;
word32 begin;
word32 entry;
if (ssh == NULL || output == NULL || idx == NULL || authData == NULL)
ret = WS_BAD_ARGUMENT;
if (ret == WS_SUCCESS) {
begin = *idx;
if (authData->sf.keyboard.promptName) {
word32 slen = authData->sf.keyboard.promptNameSz;
c32toa(slen, output + begin);
begin += LENGTH_SZ;
WMEMCPY(output + begin, authData->sf.keyboard.promptName, slen);
begin += slen;
} else {
c32toa(0, output + begin);
begin += LENGTH_SZ;
}
if (authData->sf.keyboard.promptInstruction) {
word32 slen = authData->sf.keyboard.promptInstructionSz;
c32toa(slen, output + begin);
begin += LENGTH_SZ;
WMEMCPY(output + begin, authData->sf.keyboard.promptInstruction, slen);
begin += slen;
} else {
c32toa(0, output + begin);
begin += LENGTH_SZ;
}
if (authData->sf.keyboard.promptLanguage) {
word32 slen = authData->sf.keyboard.promptLanguageSz;
c32toa(slen, output + begin);
begin += LENGTH_SZ;
WMEMCPY(output + begin, authData->sf.keyboard.promptLanguage, slen);
begin += slen;
} else {
c32toa(0, output + begin);
begin += LENGTH_SZ;
}
c32toa(authData->sf.keyboard.promptCount, output + begin);
begin += LENGTH_SZ;
for (entry = 0; entry < authData->sf.keyboard.promptCount;
entry++) {
c32toa(authData->sf.keyboard.promptLengths[entry], output + begin);
begin += LENGTH_SZ;
WMEMCPY(output + begin, authData->sf.keyboard.prompts[entry],
authData->sf.keyboard.promptLengths[entry]);
begin += authData->sf.keyboard.promptLengths[entry];
output[begin] = authData->sf.keyboard.promptEcho[entry];
begin++;
}
*idx = begin;
}
return ret;
}
int SendUserAuthKeyboardRequest(WOLFSSH* ssh, WS_UserAuthData* authData)
{
byte* output;
word32 idx;
word32 payloadSz = 0;
int ret = WS_SUCCESS;
WLOG(WS_LOG_DEBUG, "Entering SendUserAuthKeyboardRequest()");
if (ssh == NULL || authData == NULL) {
ret = WS_BAD_ARGUMENT;
}
if (ret == WS_SUCCESS) {
ret = ssh->ctx->keyboardAuthCb(&authData->sf.keyboard,
ssh->keyboardAuthCtx);
}
if (authData->sf.keyboard.promptCount > 0 &&
(authData->sf.keyboard.prompts == NULL ||
authData->sf.keyboard.promptLengths == NULL ||
authData->sf.keyboard.promptEcho == NULL)) {
ret = WS_BAD_USAGE;
}
if (authData->sf.keyboard.promptCount > WOLFSSH_MAX_PROMPTS) {
ret = WS_BAD_USAGE;
}
ssh->kbAuth.promptCount = authData->sf.keyboard.promptCount;
payloadSz = MSG_ID_SZ;
if (ret == WS_SUCCESS) {
ret = PrepareUserAuthRequestKeyboard(ssh, &payloadSz, authData);
}
if (ret == WS_SUCCESS) {
ret = PreparePacket(ssh, payloadSz);
}
output = ssh->outputBuffer.buffer;
idx = ssh->outputBuffer.length;
output[idx++] = MSGID_USERAUTH_INFO_REQUEST;
if (ret == WS_SUCCESS) {
ret = BuildUserAuthRequestKeyboard(ssh, output, &idx, authData);
}
if (ret == WS_SUCCESS) {
ssh->outputBuffer.length = idx;
ret = BundlePacket(ssh);
}
if (ret == WS_SUCCESS) {
ret = wolfSSH_SendPacket(ssh);
}
if ((ret != WS_WANT_WRITE) && (ret != WS_SUCCESS)) {
PurgePacket(ssh);
}
WLOG(WS_LOG_DEBUG, "Leaving SendUserAuthKeyboardRequest(), ret = %d", ret);
return ret;
}
static int PrepareUserAuthResponseKeyboard(WOLFSSH* ssh, word32* payloadSz,
const WS_UserAuthData* authData)
{
int ret = WS_SUCCESS;
word32 entry;
if (ssh == NULL || payloadSz == NULL || authData == NULL)
ret = WS_BAD_ARGUMENT;
if (ret == WS_SUCCESS)
{
*payloadSz += LENGTH_SZ;
for (entry = 0; entry < authData->sf.keyboard.responseCount; entry++) {
*payloadSz += LENGTH_SZ +
authData->sf.keyboard.responseLengths[entry];
}
}
return ret;
}
static int BuildUserAuthResponseKeyboard(WOLFSSH* ssh, byte* output, word32* idx,
const WS_UserAuthData* authData)
{
int ret = WS_SUCCESS;
word32 begin;
word32 entry;
if (ssh == NULL || output == NULL || idx == NULL || authData == NULL)
ret = WS_BAD_ARGUMENT;
if (ret == WS_SUCCESS &&
authData->sf.keyboard.promptCount !=
authData->sf.keyboard.responseCount) {
ret = WS_USER_AUTH_E;
WLOG(WS_LOG_DEBUG, "Not enough answers provided for prompts");
}
if (ret == WS_SUCCESS) {
begin = *idx;
c32toa(authData->sf.keyboard.responseCount, output + begin);
begin += LENGTH_SZ;
for (entry = 0; entry < authData->sf.keyboard.responseCount; entry++) {
c32toa(authData->sf.keyboard.responseLengths[entry], output + begin);
begin += LENGTH_SZ;
WMEMCPY(output + begin, authData->sf.keyboard.responses[entry],
authData->sf.keyboard.responseLengths[entry]);
begin += authData->sf.keyboard.responseLengths[entry];
}
*idx = begin;
}
return ret;
}
#ifndef WOLFSSH_NO_RSA
static int PrepareUserAuthRequestRsa(WOLFSSH* ssh, word32* payloadSz,
@ -13971,6 +14459,95 @@ static int BuildUserAuthRequestPublicKey(WOLFSSH* ssh,
#endif
int SendUserAuthKeyboardResponse(WOLFSSH* ssh)
{
byte* output;
int ret = WS_SUCCESS;
word32 idx;
word32 payloadSz = 0;
word32 prompt;
WS_UserAuthData authData;
WLOG(WS_LOG_DEBUG, "Entering SendUserAuthKeyboardResponse()");
authData.type = WOLFSSH_USERAUTH_KEYBOARD;
authData.username = (const byte*)ssh->userName;
authData.usernameSz = ssh->userNameSz;
authData.sf.keyboard.promptCount = ssh->kbAuth.promptCount;
authData.sf.keyboard.promptName = ssh->kbAuth.promptName;
authData.sf.keyboard.promptNameSz =
(word32)WSTRLEN((char*)ssh->kbAuth.promptName);
authData.sf.keyboard.promptInstruction = ssh->kbAuth.promptInstruction;
authData.sf.keyboard.promptInstructionSz =
(word32)WSTRLEN((char*)ssh->kbAuth.promptInstruction);
authData.sf.keyboard.promptLanguage = ssh->kbAuth.promptLanguage;
authData.sf.keyboard.promptLanguageSz =
(word32)WSTRLEN((char*)ssh->kbAuth.promptLanguage);
authData.sf.keyboard.prompts = ssh->kbAuth.prompts;
authData.sf.keyboard.promptEcho = ssh->kbAuth.promptEcho;
authData.sf.keyboard.responseCount = 0;
WLOG(WS_LOG_DEBUG, "SUAR: Calling the userauth callback");
ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_KEYBOARD, &authData,
ssh->userAuthCtx);
WFREE(ssh->kbAuth.promptName, ssh->ctx->heap, 0);
WFREE(ssh->kbAuth.promptInstruction, ssh->ctx->heap, 0);
WFREE(ssh->kbAuth.promptLanguage, ssh->ctx->heap, 0);
WFREE(ssh->kbAuth.promptEcho, ssh->ctx->heap, 0);
for (prompt = 0; prompt < ssh->kbAuth.promptCount; prompt++) {
WFREE((void*)ssh->kbAuth.prompts[prompt], ssh->ctx->heap, 0);
}
WFREE(ssh->kbAuth.prompts, ssh->ctx->heap, 0);
if (ret != WOLFSSH_USERAUTH_SUCCESS) {
WLOG(WS_LOG_DEBUG, "SUAR: Couldn't get keyboard auth");
ret = WS_FATAL_ERROR;
}
else if (ssh->kbAuth.promptCount != authData.sf.keyboard.responseCount) {
WLOG(WS_LOG_DEBUG,
"SUAR: Keyboard auth response count does not match request count");
ret = WS_USER_AUTH_E;
}
else {
WLOG(WS_LOG_DEBUG, "SUAR: Callback successful keyboard");
}
payloadSz = MSG_ID_SZ;
if (ret == WS_SUCCESS) {
ret = PrepareUserAuthResponseKeyboard(ssh, &payloadSz, &authData);
}
if (ret == WS_SUCCESS) {
ret = PreparePacket(ssh, payloadSz);
}
output = ssh->outputBuffer.buffer;
idx = ssh->outputBuffer.length;
output[idx++] = MSGID_USERAUTH_INFO_RESPONSE;
if (ret == WS_SUCCESS)
ret = BuildUserAuthResponseKeyboard(ssh, output, &idx, &authData);
if (ret == WS_SUCCESS) {
ssh->outputBuffer.length = idx;
ret = BundlePacket(ssh);
}
if (ret == WS_SUCCESS) {
ret = wolfSSH_SendPacket(ssh);
}
if (ret != WS_WANT_WRITE && ret != WS_SUCCESS)
PurgePacket(ssh);
ForceZero(&authData, sizeof(WS_UserAuthData));
WLOG(WS_LOG_DEBUG, "Leaving SendUserAuthKeyboardResponse(), ret = %d", ret);
return ret;
}
int SendUserAuthRequest(WOLFSSH* ssh, byte authType, int addSig)
{
@ -14004,8 +14581,11 @@ int SendUserAuthRequest(WOLFSSH* ssh, byte authType, int addSig)
WMEMSET(keySig_ptr, 0, sizeof(WS_KeySignature));
keySig_ptr->keySigId = ID_NONE;
keySig_ptr->heap = ssh->ctx->heap;
if (ssh->ctx->userAuthCb != NULL) {
/* Callback happens later for keyboard auth */
if (authType & WOLFSSH_USERAUTH_KEYBOARD) {
authId = ID_USERAUTH_KEYBOARD;
}
else if (ssh->ctx->userAuthCb != NULL) {
WLOG(WS_LOG_DEBUG, "SUAR: Calling the userauth callback");
WMEMSET(&authData, 0, sizeof(authData));
@ -14028,9 +14608,11 @@ int SendUserAuthRequest(WOLFSSH* ssh, byte authType, int addSig)
authData.type = authType;
}
}
/* fall into public key case if password case was not successful */
/* fall into public key case if keyboard or password case was not
* successful */
if ((ret == WS_FATAL_ERROR ||
!(authType & WOLFSSH_USERAUTH_PASSWORD)) &&
(!(authType & WOLFSSH_USERAUTH_PASSWORD) &&
!(authType & WOLFSSH_USERAUTH_KEYBOARD))) &&
(authType & WOLFSSH_USERAUTH_PUBLICKEY)) {
ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_PUBLICKEY,
&authData, ssh->userAuthCtx);
@ -14068,7 +14650,8 @@ int SendUserAuthRequest(WOLFSSH* ssh, byte authType, int addSig)
ret = PrepareUserAuthRequestPublicKey(ssh, &payloadSz, &authData,
keySig_ptr);
}
else if (authId != ID_NONE && !ssh->userAuthPkDone)
else if (authId != ID_NONE && authId != ID_USERAUTH_KEYBOARD &&
!ssh->userAuthPkDone)
ret = WS_INVALID_ALGO_ID;
}
@ -14107,6 +14690,14 @@ int SendUserAuthRequest(WOLFSSH* ssh, byte authType, int addSig)
ret = BuildUserAuthRequestPassword(ssh, output, &idx, &authData);
}
else if (authId == ID_USERAUTH_KEYBOARD) {
/* language tag, deprecated, should be empty */
c32toa(0, output + idx);
idx += LENGTH_SZ;
/* submethods */
c32toa(0, output + idx);
idx += LENGTH_SZ;
}
else if (authId == ID_USERAUTH_PUBLICKEY)
ret = BuildUserAuthRequestPublicKey(ssh, output, &idx, &authData,
sigStart, sigStartIdx, keySig_ptr);
@ -14143,14 +14734,17 @@ static int GetAllowedAuth(WOLFSSH* ssh, char* authStr)
{
int typeAllowed = 0;
if (ssh == NULL || authStr == NULL)
return WS_BAD_ARGUMENT;
typeAllowed |= WOLFSSH_USERAUTH_PASSWORD;
if (ssh->ctx->keyboardAuthCb != NULL) {
typeAllowed |= WOLFSSH_USERAUTH_KEYBOARD;
}
#if !defined(WOLFSSH_NO_RSA) || !defined(WOLFSSH_NO_ECDSA)
typeAllowed |= WOLFSSH_USERAUTH_PUBLICKEY;
#endif
if (ssh == NULL || authStr == NULL)
return WS_BAD_ARGUMENT;
authStr[0] = '\0';
if (ssh->ctx && ssh->ctx->userAuthTypesCb) {
typeAllowed = ssh->ctx->userAuthTypesCb(ssh, ssh->userAuthCtx);
@ -14163,6 +14757,10 @@ static int GetAllowedAuth(WOLFSSH* ssh, char* authStr)
WSTRNCAT(authStr, "password,", MAX_AUTH_STRING-1);
}
if (typeAllowed & WOLFSSH_USERAUTH_KEYBOARD) {
WSTRNCAT(authStr, "keyboard-interactive,", MAX_AUTH_STRING-1);
}
/* remove last comma from the list */
return (int)XSTRLEN(authStr) - 1;
}

View File

@ -840,6 +840,28 @@ int wolfSSH_connect(WOLFSSH* ssh)
return WS_FATAL_ERROR;
}
}
while (ssh->serverState == SERVER_USERAUTH_ACCEPT_KEYBOARD) {
if ( (ssh->error = SendUserAuthKeyboardResponse(ssh)) <
WS_SUCCESS) {
WLOG(WS_LOG_DEBUG, connectError, "CLIENT_USERAUTH_SENT",
ssh->error);
return WS_FATAL_ERROR;
}
ssh->serverState = SERVER_USERAUTH_ACCEPT_KEYBOARD_NEXT;
while (
(ssh->serverState < SERVER_USERAUTH_ACCEPT_KEYBOARD_DONE) &&
(ssh->serverState != SERVER_USERAUTH_ACCEPT_KEYBOARD) &&
(ssh->serverState != SERVER_USERAUTH_ACCEPT_DONE)) {
if (DoReceive(ssh) < WS_SUCCESS) {
WLOG(WS_LOG_DEBUG, connectError,
"CLIENT_USERAUTH_SENT", ssh->error);
return WS_FATAL_ERROR;
}
}
}
ssh->connectState = CONNECT_SERVER_USERAUTH_ACCEPT_DONE;
WLOG(WS_LOG_DEBUG, connectState, "SERVER_USERAUTH_ACCEPT_DONE");
NO_BREAK;
@ -1288,6 +1310,20 @@ int wolfSSH_SendDisconnect(WOLFSSH *ssh, word32 reason)
return SendDisconnect(ssh, reason);
}
void wolfSSH_SetKeyboardAuthPrompts(WOLFSSH_CTX* ctx,
WS_CallbackKeyboardAuthPrompts cb)
{
if (ctx != NULL) {
ctx->keyboardAuthCb = cb;
}
}
void wolfSSH_SetKeyboardAuthCtx(WOLFSSH* ssh, void* keyboardAuthCtx)
{
if (ssh != NULL) {
ssh->keyboardAuthCtx = keyboardAuthCtx;
}
}
void wolfSSH_SetUserAuth(WOLFSSH_CTX* ctx, WS_CallbackUserAuth cb)
{

View File

@ -1555,6 +1555,150 @@ static void test_wolfSSH_QueryAlgoList(void)
AssertIntEQ(WS_INVALID_ALGO_ID, k);
}
#if defined(WOLFSSH_SFTP) && !defined(NO_WOLFSSH_CLIENT) && \
!defined(SINGLE_THREADED)
static byte* kbResponse = (byte*)"test";
static word32 kbResponseLength = 4;
static int keyboardUserAuth(byte authType, WS_UserAuthData* authData, void* ctx)
{
(void) ctx;
int ret = WOLFSSH_USERAUTH_INVALID_AUTHTYPE;
if (authType == WOLFSSH_USERAUTH_KEYBOARD) {
AssertIntEQ(1, authData->sf.keyboard.promptCount);
AssertStrEQ("KB Auth Password: ", authData->sf.keyboard.prompts[0]);
authData->sf.keyboard.responseCount = 1;
authData->sf.keyboard.responseLengths = &kbResponseLength;
authData->sf.keyboard.responses = (byte**)&kbResponse;
ret = WS_SUCCESS;
}
return ret;
}
static void keyboard_client_connect(WOLFSSH_CTX** ctx, WOLFSSH** ssh, int port)
{
SOCKET_T sockFd = WOLFSSH_SOCKET_INVALID;
SOCKADDR_IN_T clientAddr;
socklen_t clientAddrSz = sizeof(clientAddr);
int ret;
char* host = (char*)wolfSshIp;
const char* username = "test";
if (ctx == NULL || ssh == NULL) {
return;
}
*ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
if (*ctx == NULL) {
return;
}
wolfSSH_SetUserAuth(*ctx, keyboardUserAuth);
*ssh = wolfSSH_new(*ctx);
if (*ssh == NULL) {
wolfSSH_CTX_free(*ctx);
*ctx = NULL;
return;
}
build_addr(&clientAddr, host, port);
tcp_socket(&sockFd, ((struct sockaddr_in *)&clientAddr)->sin_family);
ret = connect(sockFd, (const struct sockaddr *)&clientAddr, clientAddrSz);
if (ret != 0){
wolfSSH_free(*ssh);
wolfSSH_CTX_free(*ctx);
*ctx = NULL;
*ssh = NULL;
return;
}
ret = wolfSSH_SetUsername(*ssh, username);
if (ret == WS_SUCCESS)
ret = wolfSSH_set_fd(*ssh, (int)sockFd);
if (ret == WS_SUCCESS)
ret = wolfSSH_connect(*ssh);
if (ret != WS_SUCCESS){
wolfSSH_free(*ssh);
wolfSSH_CTX_free(*ctx);
*ctx = NULL;
*ssh = NULL;
return;
}
}
static void test_wolfSSH_KeyboardInteractive(void)
{
func_args ser;
tcp_ready ready;
int argsCount;
WS_SOCKET_T clientFd;
const char* args[10];
WOLFSSH_CTX* ctx = NULL;
WOLFSSH* ssh = NULL;
THREAD_TYPE serThread;
WMEMSET(&ser, 0, sizeof(func_args));
argsCount = 0;
args[argsCount++] = ".";
args[argsCount++] = "-1";
args[argsCount++] = "-i";
args[argsCount++] = "test:test";
#ifndef USE_WINDOWS_API
args[argsCount++] = "-p";
args[argsCount++] = "0";
#endif
ser.argv = (char**)args;
ser.argc = argsCount;
ser.signal = &ready;
InitTcpReady(ser.signal);
ThreadStart(echoserver_test, (void*)&ser, &serThread);
WaitTcpReady(&ready);
keyboard_client_connect(&ctx, &ssh, ready.port);
AssertNotNull(ctx);
AssertNotNull(ssh);
argsCount = wolfSSH_shutdown(ssh);
if (argsCount == WS_SOCKET_ERROR_E) {
/* If the socket is closed on shutdown, peer is gone, this is OK. */
argsCount = WS_SUCCESS;
}
#if DEFAULT_HIGHWATER_MARK < 8000
if (argsCount == WS_REKEYING) {
/* in cases where highwater mark is really small a re-key could happen */
argsCount = WS_SUCCESS;
}
#endif
AssertIntEQ(argsCount, WS_SUCCESS);
/* close client socket down */
clientFd = wolfSSH_get_fd(ssh);
WCLOSESOCKET(clientFd);
wolfSSH_free(ssh);
wolfSSH_CTX_free(ctx);
#ifdef WOLFSSH_ZEPHYR
/* Weird deadlock without this sleep */
k_sleep(Z_TIMEOUT_TICKS(100));
#endif
ThreadJoin(serThread);
}
#else /* WOLFSSH_SFTP && !NO_WOLFSSH_CLIENT && !SINGLE_THREADED */
static void test_wolfSSH_KeyboardInteractive(void) { ; }
#endif /* WOLFSSH_SFTP && !NO_WOLFSSH_CLIENT && !SINGLE_THREADED */
#endif /* WOLFSSH_TEST_BLOCK */
@ -1591,6 +1735,7 @@ int wolfSSH_ApiTest(int argc, char** argv)
test_wolfSSH_ReadKey();
test_wolfSSH_QueryAlgoList();
test_wolfSSH_SetAlgoList();
test_wolfSSH_KeyboardInteractive();
/* SCP tests */
test_wolfSSH_SCP_CB();

622
tests/auth.c 100644
View File

@ -0,0 +1,622 @@
/* auth.c
*
* Copyright (C) 2025 wolfSSL Inc.
*
* This file is part of wolfSSH.
*
* wolfSSH is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfSSH is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with wolfSSH. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WOLFSSL_USER_SETTINGS
#include <wolfssl/wolfcrypt/settings.h>
#else
#include <wolfssl/options.h>
#endif
#include <wolfssl/wolfcrypt/wc_port.h>
#include <wolfssh/port.h>
#include <stdio.h>
#include <wolfssh/ssh.h>
#include <wolfssh/internal.h>
#define WOLFSSH_TEST_CLIENT
#define WOLFSSH_TEST_SERVER
#define WOLFSSH_TEST_LOCKING
#ifndef SINGLE_THREADED
#define WOLFSSH_TEST_THREADING
#endif
#include <wolfssh/test.h>
#include "tests/auth.h"
#ifndef WOLFSSH_NO_ABORT
#define WABORT() abort()
#else
#define WABORT()
#endif
#define PrintError(description, result) do { \
printf("\nERROR - %s line %d failed with:", __FILE__, __LINE__); \
printf("\n expected: "); printf description; \
printf("\n result: "); printf result; printf("\n\n"); \
} while(0)
#ifdef WOLFSSH_ZEPHYR
#define Fail(description, result) do { \
PrintError(description, result); \
WABORT(); \
} while(0)
#else
#define Fail(description, result) do { \
PrintError(description, result); \
WFFLUSH(stdout); \
WABORT(); \
} while(0)
#endif
#define Assert(test, description, result) if (!(test)) Fail(description, result)
#define AssertTrue(x) Assert( (x), ("%s is true", #x), (#x " => FALSE"))
#define AssertFalse(x) Assert(!(x), ("%s is false", #x), (#x " => TRUE"))
#define AssertNotNull(x) Assert( (x), ("%s is not null", #x), (#x " => NULL"))
#define AssertNull(x) do { \
PEDANTIC_EXTENSION void* _x = (void*)(x); \
\
Assert(!_x, ("%s is null", #x), (#x " => %p", _x)); \
} while(0)
#define AssertInt(x, y, op, er) do { \
int _x = (int)(x); \
int _y = (int)(y); \
Assert(_x op _y, ("%s " #op " %s", #x, #y), ("%d " #er " %d", _x, _y)); \
} while(0)
#define AssertIntEQ(x, y) AssertInt(x, y, ==, !=)
#define AssertIntNE(x, y) AssertInt(x, y, !=, ==)
#define AssertIntGT(x, y) AssertInt(x, y, >, <=)
#define AssertIntLT(x, y) AssertInt(x, y, <, >=)
#define AssertIntGE(x, y) AssertInt(x, y, >=, <)
#define AssertIntLE(x, y) AssertInt(x, y, <=, >)
#define AssertStr(x, y, op, er) do { \
const char* _x = (const char*)(x); \
const char* _y = (const char*)(y); \
int _z = (_x && _y) ? strcmp(_x, _y) : -1; \
Assert(_z op 0, ("%s " #op " %s", #x, #y), \
("\"%s\" " #er " \"%s\"", _x, _y));\
} while(0)
#define AssertStrEQ(x, y) AssertStr(x, y, ==, !=)
#define AssertStrNE(x, y) AssertStr(x, y, !=, ==)
#define AssertStrGT(x, y) AssertStr(x, y, >, <=)
#define AssertStrLT(x, y) AssertStr(x, y, <, >=)
#define AssertStrGE(x, y) AssertStr(x, y, >=, <)
#define AssertStrLE(x, y) AssertStr(x, y, <=, >)
#define AssertPtr(x, y, op, er) do { \
PRAGMA_GCC_DIAG_PUSH \
/* remarkably, without this inhibition, */ \
/* the _Pragma()s make the declarations warn. */ \
PRAGMA_GCC("GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
/* inhibit "ISO C forbids conversion of function pointer */ \
/* to object pointer type [-Werror=pedantic]" */ \
PRAGMA_GCC("GCC diagnostic ignored \"-Wpedantic\"") \
void* _x = (void*)(x); \
void* _y = (void*)(y); \
Assert(_x op _y, ("%s " #op " %s", #x, #y), ("%p " #er " %p", _x, _y)); \
PRAGMA_GCC_DIAG_POP; \
} while(0)
#define AssertPtrEq(x, y) AssertPtr(x, y, ==, !=)
#define AssertPtrNE(x, y) AssertPtr(x, y, !=, ==)
#define AssertPtrGT(x, y) AssertPtr(x, y, >, <=)
#define AssertPtrLT(x, y) AssertPtr(x, y, <, >=)
#define AssertPtrGE(x, y) AssertPtr(x, y, >=, <)
#define AssertPtrLE(x, y) AssertPtr(x, y, <=, >)
#define ES_ERROR(...) do { \
fprintf(stderr, __VA_ARGS__); \
serverArgs->return_code = ret; \
WOLFSSL_RETURN_FROM_THREAD(0); \
} while(0)
#define EXAMPLE_KEYLOAD_BUFFER_SZ 1200
#ifdef WOLFSSH_NO_ECDSA_SHA2_NISTP256
#define ECC_PATH "./keys/server-key-ecc-521.der"
#else
#define ECC_PATH "./keys/server-key-ecc.der"
#endif
#if !defined(NO_WOLFSSH_SERVER) && !defined(NO_WOLFSSH_CLIENT) && \
!defined(SINGLE_THREADED) && !defined(WOLFSSH_TEST_BLOCK) && \
!defined(NO_FILESYSTEM)
const char *testText1 = "test";
const char *testText2 = "password";
byte *kbResponses[4];
word32 kbResponseLengths[4];
word32 kbResponseCount;
byte kbMultiRound = 0;
byte currentRound = 0;
byte unbalanced = 0;
WS_UserAuthData_Keyboard promptData;
static int load_file(const char* fileName, byte* buf, word32* bufSz)
{
WFILE* file;
word32 fileSz;
word32 readSz;
if (fileName == NULL) return 0;
if (WFOPEN(NULL, &file, fileName, "rb") != 0)
return 0;
WFSEEK(NULL, file, 0, WSEEK_END);
fileSz = (word32)WFTELL(NULL, file);
WREWIND(NULL, file);
if (buf == NULL || fileSz > *bufSz) {
*bufSz = fileSz;
WFCLOSE(NULL, file);
return 0;
}
readSz = (word32)WFREAD(NULL, buf, 1, fileSz, file);
WFCLOSE(NULL, file);
if (readSz < fileSz) {
fileSz = 0;
}
return fileSz;
}
static int load_key(byte isEcc, byte* buf, word32 bufSz)
{
word32 sz = 0;
#ifndef NO_FILESYSTEM
const char* bufName;
bufName = isEcc ? ECC_PATH : "./keys/server-key-rsa.der" ;
sz = load_file(bufName, buf, &bufSz);
#else
/* using buffers instead */
if (isEcc) {
if ((word32)sizeof_ecc_key_der_256 > bufSz) {
return 0;
}
WMEMCPY(buf, ecc_key_der_256, sizeof_ecc_key_der_256);
sz = sizeof_ecc_key_der_256;
}
else {
if ((word32)sizeof_rsa_key_der_2048 > bufSz) {
return 0;
}
WMEMCPY(buf, (byte*)rsa_key_der_2048, sizeof_rsa_key_der_2048);
sz = sizeof_rsa_key_der_2048;
}
#endif
return sz;
}
static int serverUserAuth(byte authType, WS_UserAuthData* authData, void* ctx)
{
(void) ctx;
if (authType != WOLFSSH_USERAUTH_KEYBOARD) {
return WOLFSSH_USERAUTH_FAILURE;
}
if (authData->sf.keyboard.responseCount != kbResponseCount) {
return WOLFSSH_USERAUTH_FAILURE;
}
for (word32 resp = 0; resp < kbResponseCount; resp++) {
if (authData->sf.keyboard.responseLengths[resp] !=
kbResponseLengths[resp]) {
return WOLFSSH_USERAUTH_FAILURE;
}
if (WSTRCMP((const char*)authData->sf.keyboard.responses[resp],
(const char*)kbResponses[resp]) != 0) {
return WOLFSSH_USERAUTH_FAILURE;
}
}
if (kbMultiRound && currentRound == 0) {
currentRound++;
kbResponses[0] = (byte*)testText2;
kbResponseLengths[0] = 8;
return WOLFSSH_USERAUTH_SUCCESS_ANOTHER;
}
return WOLFSSH_USERAUTH_SUCCESS;
}
static int serverKeyboardCallback(WS_UserAuthData_Keyboard *kbAuth, void *ctx)
{
(void) ctx;
WMEMCPY(kbAuth, &promptData, sizeof(WS_UserAuthData_Keyboard));
return WS_SUCCESS;
}
static INLINE void SignalTcpReady(tcp_ready* ready, word16 port)
{
pthread_mutex_lock(&ready->mutex);
ready->ready = 1;
ready->port = port;
pthread_cond_signal(&ready->cond);
pthread_mutex_unlock(&ready->mutex);
}
static THREAD_RETURN WOLFSSH_THREAD server_thread(void* args)
{
thread_args* serverArgs;
int ret = WS_SUCCESS;
word16 port = 0;
WOLFSSH_CTX* ctx = NULL;
WOLFSSH* ssh = NULL;
byte buf[EXAMPLE_KEYLOAD_BUFFER_SZ];
byte* keyLoadBuf;
int peerEcc = 1;
word32 bufSz;
WS_SOCKET_T listenFd = WOLFSSH_SOCKET_INVALID;
WS_SOCKET_T clientFd = WOLFSSH_SOCKET_INVALID;
SOCKADDR_IN_T clientAddr;
socklen_t clientAddrSz = sizeof(clientAddr);
serverArgs = (thread_args*) args;
serverArgs->return_code = EXIT_SUCCESS;
promptData.promptCount = kbResponseCount;
promptData.promptName = NULL;
promptData.promptNameSz = 0;
promptData.promptInstruction = NULL;
promptData.promptInstructionSz = 0;
promptData.promptLanguage = NULL;
promptData.promptLanguageSz = 0;
if (kbResponseCount) {
promptData.prompts =
(byte**)WMALLOC(sizeof(byte*) * kbResponseCount, NULL, 0);
if (promptData.prompts == NULL) {
ES_ERROR("Could not allocate prompts");
}
promptData.promptLengths =
(word32*)WMALLOC(sizeof(word32) * kbResponseCount, NULL, 0);
if (promptData.promptLengths == NULL) {
ES_ERROR("Could not allocate promptLengths");
}
promptData.promptEcho =
(byte*)WMALLOC(sizeof(byte) * kbResponseCount, NULL, 0);
if (promptData.promptEcho == NULL) {
ES_ERROR("Could not allocate promptEcho");
}
for (word32 prompt = 0; prompt < kbResponseCount; prompt++) {
promptData.prompts[prompt] = (byte*)"Password: ";
promptData.promptLengths[prompt] = 10;
promptData.promptEcho[prompt] = 0;
}
}
else {
promptData.prompts = NULL;
promptData.promptLengths = NULL;
promptData.promptEcho = NULL;
}
tcp_listen(&listenFd, &port, 1);
SignalTcpReady(serverArgs->signal, port);
ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL);
if (ctx == NULL) {
ES_ERROR("Couldn't allocate SSH CTX data.\n");
}
wolfSSH_SetUserAuth(ctx, serverUserAuth);
wolfSSH_SetKeyboardAuthPrompts(ctx, serverKeyboardCallback);
ssh = wolfSSH_new(ctx);
if (ssh == NULL) {
ES_ERROR("Couldn't allocate SSH data.\n");
}
keyLoadBuf = buf;
bufSz = EXAMPLE_KEYLOAD_BUFFER_SZ;
bufSz = load_key(peerEcc, keyLoadBuf, bufSz);
if (bufSz == 0) {
ES_ERROR("Couldn't load first key file.\n");
}
if (wolfSSH_CTX_UsePrivateKey_buffer(ctx, keyLoadBuf, bufSz,
WOLFSSH_FORMAT_ASN1) < 0) {
ES_ERROR("Couldn't use first key buffer.\n");
}
clientFd = accept(listenFd, (struct sockaddr*)&clientAddr, &clientAddrSz);
if (clientFd == -1) {
ES_ERROR("tcp accept failed");
}
wolfSSH_set_fd(ssh, (int)clientFd);
ret = wolfSSH_accept(ssh);
if (ret && !unbalanced) {
ES_ERROR("wolfSSH Accept Error");
}
ret = wolfSSH_shutdown(ssh);
if (ret == WS_SOCKET_ERROR_E) {
/* fine on shutdown */
ret = WS_SUCCESS;
#if DEFAULT_HIGHWATER_MARK < 8000
if (ret == WS_REKEYING) {
ret = WS_SUCCESS;
}
#endif
}
if (promptData.promptCount > 0) {
WFREE(promptData.promptLengths, NULL, 0);
WFREE(promptData.prompts, NULL, 0);
WFREE(promptData.promptEcho, NULL, 0);
}
wolfSSH_free(ssh);
wolfSSH_CTX_free(ctx);
if (ret) {
ES_ERROR("wolfSSH Shutdown Error");
}
WOLFSSL_RETURN_FROM_THREAD(0);
}
static int keyboardUserAuth(byte authType, WS_UserAuthData* authData, void* ctx)
{
(void) ctx;
int ret = WOLFSSH_USERAUTH_INVALID_AUTHTYPE;
if (authType == WOLFSSH_USERAUTH_KEYBOARD) {
AssertIntEQ(kbResponseCount, authData->sf.keyboard.promptCount);
for (word32 prompt = 0; prompt < kbResponseCount; prompt++) {
AssertStrEQ("Password: ", authData->sf.keyboard.prompts[prompt]);
}
authData->sf.keyboard.responseCount = kbResponseCount;
if (unbalanced) {
authData->sf.keyboard.responseCount++;
}
authData->sf.keyboard.responseLengths = kbResponseLengths;
authData->sf.keyboard.responses = (byte**)kbResponses;
ret = WS_SUCCESS;
}
return ret;
}
static int basic_client_connect(WOLFSSH_CTX** ctx, WOLFSSH** ssh, int port)
{
SOCKET_T sockFd = WOLFSSH_SOCKET_INVALID;
SOCKADDR_IN_T clientAddr;
socklen_t clientAddrSz = sizeof(clientAddr);
int ret = WS_SUCCESS;
char* host = (char*)wolfSshIp;
const char* username = "test";
if (ctx == NULL || ssh == NULL) {
return WS_BAD_ARGUMENT;
}
*ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
if (*ctx == NULL) {
return WS_BAD_ARGUMENT;
}
wolfSSH_SetUserAuth(*ctx, keyboardUserAuth);
*ssh = wolfSSH_new(*ctx);
if (*ssh == NULL) {
wolfSSH_CTX_free(*ctx);
*ctx = NULL;
return WS_MEMORY_E;
}
build_addr(&clientAddr, host, port);
tcp_socket(&sockFd, ((struct sockaddr_in *)&clientAddr)->sin_family);
ret = connect(sockFd, (const struct sockaddr *)&clientAddr, clientAddrSz);
if (ret != 0){
wolfSSH_free(*ssh);
wolfSSH_CTX_free(*ctx);
*ctx = NULL;
*ssh = NULL;
return ret;
}
ret = wolfSSH_SetUsername(*ssh, username);
if (ret == WS_SUCCESS)
ret = wolfSSH_set_fd(*ssh, (int)sockFd);
if (ret == WS_SUCCESS)
ret = wolfSSH_connect(*ssh);
return ret;
}
static void test_client(void)
{
int ret;
thread_args serverArgs;
tcp_ready ready;
WOLFSSH_CTX* ctx = NULL;
WOLFSSH* ssh = NULL;
THREAD_TYPE serThread;
WS_SOCKET_T clientFd;
serverArgs.signal = &ready;
InitTcpReady(serverArgs.signal);
ThreadStart(server_thread, (void*)&serverArgs, &serThread);
WaitTcpReady(&ready);
ret = basic_client_connect(&ctx, &ssh, ready.port);
/* for the unbalanced auth test */
if (unbalanced) {
AssertIntEQ(ret, WS_FATAL_ERROR);
}
else {
AssertIntEQ(ret, WS_SUCCESS);
}
AssertNotNull(ctx);
AssertNotNull(ssh);
ret = wolfSSH_shutdown(ssh);
if (ret == WS_SOCKET_ERROR_E) {
/* fine on shutdown */
ret = WS_SUCCESS;
}
#if DEFAULT_HIGHWATER_MARK < 8000
if (ret == WS_REKEYING) {
ret = WS_SUCCESS;
}
#endif
if (!unbalanced) {
AssertIntEQ(ret, WS_SUCCESS);
}
/* close client socket down */
clientFd = wolfSSH_get_fd(ssh);
WCLOSESOCKET(clientFd);
wolfSSH_free(ssh);
wolfSSH_CTX_free(ctx);
ThreadJoin(serThread);
#if DEFAULT_HIGHWATER_MARK < 8000
if (serverArgs.return_code == WS_REKEYING) {
serverArgs.return_code = WS_SUCCESS;
}
#endif
if (!unbalanced) {
AssertIntEQ(serverArgs.return_code, WS_SUCCESS);
}
}
static void test_basic_KeyboardInteractive(void)
{
printf("Testing single prompt / response\n");
kbResponses[0] = (byte*)testText1;
kbResponseLengths[0] = 4;
kbResponseCount = 1;
test_client();
}
static void test_empty_KeyboardInteractive(void)
{
printf("Testing empty prompt / no response\n");
kbResponses[0] = NULL;
kbResponseLengths[0] = 0;
kbResponseCount = 0;
test_client();
}
static void test_multi_prompt_KeyboardInteractive(void)
{
printf("Testing multiple prompts\n");
kbResponses[0] = (byte*)testText1;
kbResponses[1] = (byte*)testText2;
kbResponseLengths[0] = 4;
kbResponseLengths[1] = 8;
kbResponseCount = 2;
test_client();
}
static void test_multi_round_KeyboardInteractive(void)
{
printf("Testing mutliple prompt rounds\n");
kbResponses[0] = (byte*)testText1;
kbResponseLengths[0] = 4;
kbResponseCount = 1;
kbMultiRound = 1;
test_client();
AssertIntEQ(currentRound, 1);
currentRound = 0;
kbMultiRound = 0;
}
static void test_unbalanced_client_KeyboardInteractive(void)
{
printf("Testing too many responses\n");
kbResponses[0] = (byte*)testText1;
kbResponseLengths[0] = 4;
kbResponseCount = 1;
unbalanced = 1;
test_client();
unbalanced = 0;
}
#endif /* WOLFSSH_TEST_BLOCK */
int wolfSSH_AuthTest(int argc, char** argv)
{
(void) argc;
(void) argv;
#if defined(NO_WOLFSSH_SERVER) || defined(NO_WOLFSSH_CLIENT) || \
defined(SINGLE_THREADED) || defined(WOLFSSH_TEST_BLOCK) || \
defined(NO_FILESYSTEM)
return 77;
#else
AssertIntEQ(wolfSSH_Init(), WS_SUCCESS);
#if defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,2)
{
int i;
for (i = 0; i < FIPS_CAST_COUNT; i++) {
AssertIntEQ(wc_RunCast_fips(i), WS_SUCCESS);
}
}
#endif /* HAVE_FIPS */
/* Add test calls here */
test_basic_KeyboardInteractive();
test_empty_KeyboardInteractive();
test_multi_prompt_KeyboardInteractive();
test_multi_round_KeyboardInteractive();
test_unbalanced_client_KeyboardInteractive();
AssertIntEQ(wolfSSH_Cleanup(), WS_SUCCESS);
return 0;
#endif
}
#ifndef NO_AUTHTEST_MAIN_DRIVER
int main(int argc, char** argv)
{
return wolfSSH_AuthTest(argc, argv);
}
#endif

33
tests/auth.h 100644
View File

@ -0,0 +1,33 @@
/* auth.h
*
* Copyright (C) 2025 wolfSSL Inc.
*
* This file is part of wolfSSH.
*
* wolfSSH is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfSSH is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with wolfSSH. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _WOLFSSH_TESTS_AUTH_H_
#define _WOLFSSH_TESTS_AUTH_H_
#include <wolfssh/test.h>
int wolfSSH_AuthTest(int argc, char** argv);
typedef struct thread_args {
int return_code;
tcp_ready* signal;
} thread_args;
#endif /* _WOLFSSH_TESTS_AUTH_H_ */

View File

@ -3,9 +3,9 @@
# All paths should be given relative to the root
check_PROGRAMS += tests/unit.test tests/api.test \
tests/testsuite.test
tests/testsuite.test tests/auth.test
noinst_PROGRAMS += tests/unit.test tests/api.test \
tests/testsuite.test
tests/testsuite.test tests/auth.test
tests_unit_test_SOURCES = tests/unit.c tests/unit.h
tests_unit_test_CPPFLAGS = -DNO_MAIN_DRIVER
@ -100,3 +100,34 @@ tests_testsuite_test_CPPFLAGS += -DWOLFSSH_CERTS
endif
tests_testsuite_test_LDADD = src/libwolfssh.la
tests_testsuite_test_DEPENDENCIES = src/libwolfssh.la
tests_auth_test_SOURCES = tests/auth.c tests/auth.h
tests_auth_test_CPPFLAGS = -DNO_MAIN_DRIVER
if BUILD_KEYGEN
tests_auth_test_CPPFLAGS += -DWOLFSSH_KEYGEN
endif
if BUILD_SCP
tests_auth_test_CPPFLAGS += -DWOLFSSH_SCP
endif
if BUILD_SFTP
tests_auth_test_CPPFLAGS += -DWOLFSSH_SFTP
endif
if BUILD_TERM
tests_auth_test_CPPFLAGS += -DWOLFSSH_TERM
endif
if BUILD_SHELL
tests_auth_test_CPPFLAGS += -DWOLFSSH_SHELL
endif
if BUILD_AGENT
tests_auth_test_CPPFLAGS += -DWOLFSSH_AGENT
endif
if BUILD_FWD
tests_auth_test_CPPFLAGS += -DWOLFSSH_FWD
endif
if BUILD_CERTS
tests_auth_test_CPPFLAGS += -DWOLFSSH_CERTS
endif
tests_auth_test_LDADD = src/libwolfssh.la
tests_auth_test_DEPENDENCIES = src/libwolfssh.la

View File

@ -353,6 +353,7 @@ enum {
/* UserAuth IDs */
ID_USERAUTH_PASSWORD,
ID_USERAUTH_PUBLICKEY,
ID_USERAUTH_KEYBOARD,
/* Channel Type IDs */
ID_CHANTYPE_SESSION,
@ -509,6 +510,7 @@ struct WOLFSSH_CTX {
WS_CallbackUserAuth userAuthCb; /* User Authentication Callback */
WS_CallbackUserAuthTypes userAuthTypesCb; /* Authentication Types Allowed */
WS_CallbackUserAuthResult userAuthResultCb; /* User Authentication Result */
WS_CallbackKeyboardAuthPrompts keyboardAuthCb; /* Keyboard auth prompts */
WS_CallbackHighwater highwaterCb; /* Data Highwater Mark Callback */
WS_CallbackGlobalReq globalReqCb; /* Global Request Callback */
WS_CallbackReqSuccess reqSuccessCb; /* Global Request Success Callback */
@ -712,7 +714,7 @@ struct WOLFSSH {
byte processReplyState;
byte isKeying;
byte authId; /* if using public key or password */
byte supportedAuth[3]; /* supported auth IDs public key , password */
byte supportedAuth[4]; /* supported auth IDs public key , password */
#ifdef WOLFSSH_SCP
byte scpState;
@ -802,6 +804,7 @@ struct WOLFSSH {
void* userAuthCtx;
void* userAuthResultCtx;
void* keyboardAuthCtx;
char* userName;
word32 userNameSz;
char* password;
@ -890,6 +893,7 @@ struct WOLFSSH {
word32 exitStatus;
#endif
void* keyingCompletionCtx;
WS_UserAuthData_Keyboard kbAuth;
};
@ -998,6 +1002,8 @@ WOLFSSH_LOCAL int SendServiceRequest(WOLFSSH*, byte);
WOLFSSH_LOCAL int SendServiceAccept(WOLFSSH*, byte);
WOLFSSH_LOCAL int SendExtInfo(WOLFSSH* ssh);
WOLFSSH_LOCAL int SendUserAuthRequest(WOLFSSH*, byte, int);
WOLFSSH_LOCAL int SendUserAuthKeyboardResponse(WOLFSSH*);
WOLFSSH_LOCAL int SendUserAuthKeyboardRequest(WOLFSSH*, WS_UserAuthData*);
WOLFSSH_LOCAL int SendUserAuthSuccess(WOLFSSH*);
WOLFSSH_LOCAL int SendUserAuthFailure(WOLFSSH*, byte);
WOLFSSH_LOCAL int SendUserAuthBanner(WOLFSSH*);
@ -1098,6 +1104,9 @@ enum ServerStates {
SERVER_KEXINIT_DONE,
SERVER_USERAUTH_REQUEST_DONE,
SERVER_USERAUTH_ACCEPT_DONE,
SERVER_USERAUTH_ACCEPT_KEYBOARD,
SERVER_USERAUTH_ACCEPT_KEYBOARD_NEXT,
SERVER_USERAUTH_ACCEPT_KEYBOARD_DONE,
SERVER_CHANNEL_OPEN_DONE,
SERVER_DONE
};
@ -1146,6 +1155,8 @@ enum WS_MessageIds {
MSGID_USERAUTH_BANNER = 53,
MSGID_USERAUTH_PK_OK = 60, /* Public Key OK */
MSGID_USERAUTH_PW_CHRQ = 60, /* Password Change Request */
MSGID_USERAUTH_INFO_REQUEST = 60,
MSGID_USERAUTH_INFO_RESPONSE = 61,
MSGID_GLOBAL_REQUEST = 80,
MSGID_REQUEST_SUCCESS = 81,

View File

@ -88,6 +88,13 @@ extern "C" {
#define HAVE_WC_ECC_SET_RNG
#endif
/* Maximum number of prompts in one request / response transaction for
* Keyboard-Interactive authentication.
*/
#if !defined(WOLFSSH_MAX_PROMPTS)
#define WOLFSSH_MAX_PROMPTS 64
#endif
#ifdef __cplusplus
}
#endif

View File

@ -292,6 +292,22 @@ typedef struct WS_UserAuthData_Password {
word32 newPasswordSz;
} WS_UserAuthData_Password;
typedef struct WS_UserAuthData_Keyboard {
word32 promptCount;
word32 responseCount;
word32 promptNameSz;
word32 promptInstructionSz;
word32 promptLanguageSz;
byte* promptName;
byte* promptInstruction;
byte* promptLanguage;
word32* promptLengths;
word32* responseLengths;
byte* promptEcho;
byte** responses;
byte** prompts;
} WS_UserAuthData_Keyboard;
typedef struct WS_UserAuthData_PublicKey {
const byte* dataToSign;
const byte* publicKeyType;
@ -317,6 +333,7 @@ typedef struct WS_UserAuthData {
union {
WS_UserAuthData_Password password;
WS_UserAuthData_PublicKey publicKey;
WS_UserAuthData_Keyboard keyboard;
} sf;
} WS_UserAuthData;
@ -328,6 +345,11 @@ WOLFSSH_API void wolfSSH_SetUserAuthTypes(WOLFSSH_CTX*,
WOLFSSH_API void wolfSSH_SetUserAuthCtx(WOLFSSH*, void*);
WOLFSSH_API void* wolfSSH_GetUserAuthCtx(WOLFSSH*);
typedef int (*WS_CallbackKeyboardAuthPrompts)(WS_UserAuthData_Keyboard*, void*);
WOLFSSH_API void wolfSSH_SetKeyboardAuthPrompts(WOLFSSH_CTX*,
WS_CallbackKeyboardAuthPrompts);
WOLFSSH_API void wolfSSH_SetKeyboardAuthCtx(WOLFSSH*, void*);
typedef int (*WS_CallbackUserAuthResult)(byte result,
WS_UserAuthData* authData, void* userAuthResultCtx);
WOLFSSH_API void wolfSSH_SetUserAuthResult(WOLFSSH_CTX* ctx,
@ -425,7 +447,8 @@ enum WS_FormatTypes {
/* bit map */
#define WOLFSSH_USERAUTH_PASSWORD 0x01
#define WOLFSSH_USERAUTH_PUBLICKEY 0x02
#define WOLFSSH_USERAUTH_NONE 0x04
#define WOLFSSH_USERAUTH_KEYBOARD 0x04
#define WOLFSSH_USERAUTH_NONE 0x08
enum WS_UserAuthResults
{
@ -437,6 +460,7 @@ enum WS_UserAuthResults
WOLFSSH_USERAUTH_REJECTED,
WOLFSSH_USERAUTH_INVALID_PUBLICKEY,
WOLFSSH_USERAUTH_PARTIAL_SUCCESS,
WOLFSSH_USERAUTH_SUCCESS_ANOTHER,
WOLFSSH_USERAUTH_WOULD_BLOCK
};

View File

@ -34,6 +34,9 @@ extern "C" {
#undef WOLFSSH_SCP
#define WOLFSSH_SCP
#undef NO_AUTHTEST_MAIN_DRIVER
#define NO_AUTHTEST_MAIN_DRIVER
#undef NO_APITEST_MAIN_DRIVER
#define NO_APITEST_MAIN_DRIVER

View File

@ -31,6 +31,9 @@ extern "C" {
#undef WOLFSSH_SCP
#define WOLFSSH_SCP
#undef NO_AUTHTEST_MAIN_DRIVER
#define NO_AUTHTEST_MAIN_DRIVER
#undef NO_APITEST_MAIN_DRIVER
#define NO_APITEST_MAIN_DRIVER