-
Notifications
You must be signed in to change notification settings - Fork 103
Fix SendUserAuthKeyboardResponse() and add regress tests #910
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15419,55 +15419,80 @@ static int BuildUserAuthRequestPublicKey(WOLFSSH* ssh, | |
| int SendUserAuthKeyboardResponse(WOLFSSH* ssh) | ||
| { | ||
| byte* output; | ||
| int authRet = WOLFSSH_USERAUTH_FAILURE; | ||
| int ret = WS_SUCCESS; | ||
| word32 idx; | ||
| word32 payloadSz = 0; | ||
| word32 prompt; | ||
| WS_UserAuthData authData; | ||
|
|
||
| WLOG(WS_LOG_DEBUG, "Entering SendUserAuthKeyboardResponse()"); | ||
| WMEMSET(&authData, 0, sizeof(authData)); | ||
|
|
||
| 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 = ssh->kbAuth.promptName ? | ||
| (word32)WSTRLEN((char*)ssh->kbAuth.promptName) : 0; | ||
| authData.sf.keyboard.promptInstruction = ssh->kbAuth.promptInstruction; | ||
| authData.sf.keyboard.promptInstructionSz = ssh->kbAuth.promptInstruction ? | ||
| (word32)WSTRLEN((char*)ssh->kbAuth.promptInstruction) : 0; | ||
| authData.sf.keyboard.promptLanguage = ssh->kbAuth.promptLanguage; | ||
| authData.sf.keyboard.promptLanguageSz = ssh->kbAuth.promptLanguage ? | ||
| (word32)WSTRLEN((char*)ssh->kbAuth.promptLanguage) : 0; | ||
| 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); | ||
| WLOG(WS_LOG_DEBUG, "Entering SendUserAuthKeyboardResponse()"); | ||
|
|
||
| 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); | ||
| if (ssh == NULL || ssh->ctx == NULL) { | ||
| ret = WS_BAD_ARGUMENT; | ||
| } | ||
|
Comment on lines
+15433
to
15435
|
||
| 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; | ||
| if (ret == WS_SUCCESS && ssh->ctx->userAuthCb == NULL) { | ||
| ret = WS_INVALID_STATE_E; | ||
| } | ||
yosuke-wolfssl marked this conversation as resolved.
Show resolved
Hide resolved
yosuke-wolfssl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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; | ||
|
|
||
| if (ret == WS_SUCCESS) { | ||
| 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 = ssh->kbAuth.promptName ? | ||
| (word32)WSTRLEN((char*)ssh->kbAuth.promptName) : 0; | ||
| authData.sf.keyboard.promptInstruction = ssh->kbAuth.promptInstruction; | ||
| authData.sf.keyboard.promptInstructionSz = ssh->kbAuth.promptInstruction ? | ||
| (word32)WSTRLEN((char*)ssh->kbAuth.promptInstruction) : 0; | ||
| authData.sf.keyboard.promptLanguage = ssh->kbAuth.promptLanguage; | ||
| authData.sf.keyboard.promptLanguageSz = ssh->kbAuth.promptLanguage ? | ||
| (word32)WSTRLEN((char*)ssh->kbAuth.promptLanguage) : 0; | ||
| 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"); | ||
| authRet = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_KEYBOARD, &authData, | ||
| ssh->userAuthCtx); | ||
| } | ||
|
|
||
| if (ret == WS_SUCCESS) { | ||
| if (authRet != 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"); | ||
| } | ||
| } | ||
| else { | ||
| WLOG(WS_LOG_DEBUG, "SUAR: Callback successful keyboard"); | ||
|
|
||
| if (ssh != NULL && ssh->ctx != NULL) { | ||
| 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); | ||
| if (ssh->kbAuth.prompts != NULL) { | ||
| 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); | ||
|
|
||
| ssh->kbAuth.promptName = NULL; | ||
| ssh->kbAuth.promptInstruction = NULL; | ||
| ssh->kbAuth.promptLanguage = NULL; | ||
| ssh->kbAuth.promptEcho = NULL; | ||
| ssh->kbAuth.prompts = NULL; | ||
| ssh->kbAuth.promptCount = 0; | ||
| } | ||
|
|
||
| payloadSz = MSG_ID_SZ; | ||
|
|
@@ -15479,13 +15504,13 @@ int SendUserAuthKeyboardResponse(WOLFSSH* ssh) | |
| ret = PreparePacket(ssh, payloadSz); | ||
| } | ||
|
|
||
| output = ssh->outputBuffer.buffer; | ||
| idx = ssh->outputBuffer.length; | ||
|
|
||
| output[idx++] = MSGID_USERAUTH_INFO_RESPONSE; | ||
| if (ret == WS_SUCCESS) { | ||
| output = ssh->outputBuffer.buffer; | ||
| idx = ssh->outputBuffer.length; | ||
|
|
||
| if (ret == WS_SUCCESS) | ||
| output[idx++] = MSGID_USERAUTH_INFO_RESPONSE; | ||
| ret = BuildUserAuthResponseKeyboard(ssh, output, &idx, &authData); | ||
| } | ||
|
|
||
| if (ret == WS_SUCCESS) { | ||
| ssh->outputBuffer.length = idx; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -557,6 +557,114 @@ static void TestSftpBufferSendPendingOutput(void) | |
| #endif /* WOLFSSH_SFTP */ | ||
|
|
||
|
|
||
| #ifdef WOLFSSH_KEYBOARD_INTERACTIVE | ||
| static int KbPreparePacketFailUserAuth(byte authType, WS_UserAuthData* authData, | ||
| void* ctx) | ||
| { | ||
| static byte* responses[1]; | ||
| static word32 responseLens[1]; | ||
| static byte response[] = "regress"; | ||
|
|
||
| (void)ctx; | ||
|
|
||
| if (authType != WOLFSSH_USERAUTH_KEYBOARD || authData == NULL) { | ||
| return WOLFSSH_USERAUTH_INVALID_AUTHTYPE; | ||
| } | ||
|
|
||
| if (authData->sf.keyboard.promptCount != 1 || | ||
| authData->sf.keyboard.prompts == NULL) { | ||
| return WOLFSSH_USERAUTH_INVALID_PASSWORD; | ||
| } | ||
|
|
||
| responses[0] = response; | ||
| responseLens[0] = (word32)sizeof(response) - 1; | ||
| authData->sf.keyboard.responseCount = 1; | ||
| authData->sf.keyboard.responseLengths = responseLens; | ||
| authData->sf.keyboard.responses = responses; | ||
|
|
||
| return WOLFSSH_USERAUTH_SUCCESS; | ||
| } | ||
|
|
||
| static void TestKeyboardResponsePreparePacketFailure(WOLFSSH* ssh, | ||
| WOLFSSH_CTX* ctx) | ||
| { | ||
| byte* prompt; | ||
| byte** prompts; | ||
| byte* promptEcho; | ||
| int ret; | ||
| byte* savedBuffer; | ||
|
|
||
| AssertNotNull(ssh); | ||
| AssertNotNull(ctx); | ||
|
|
||
| ResetSession(ssh); | ||
| wolfSSH_SetUserAuth(ctx, KbPreparePacketFailUserAuth); | ||
|
|
||
| prompt = (byte*)WMALLOC(9, ctx->heap, DYNTYPE_STRING); /* "Password" */ | ||
| prompts = (byte**)WMALLOC(sizeof(byte*), ctx->heap, DYNTYPE_STRING); | ||
| promptEcho = (byte*)WMALLOC(1, ctx->heap, DYNTYPE_STRING); | ||
| AssertNotNull(prompt); | ||
| AssertNotNull(prompts); | ||
| AssertNotNull(promptEcho); | ||
|
|
||
| WMEMCPY(prompt, "Password", 8); | ||
| prompt[8] = '\0'; | ||
| prompts[0] = prompt; | ||
| promptEcho[0] = 0; | ||
|
|
||
| ssh->kbAuth.promptCount = 1; | ||
| ssh->kbAuth.prompts = prompts; | ||
| ssh->kbAuth.promptEcho = promptEcho; | ||
| ssh->kbAuth.promptName = NULL; | ||
| ssh->kbAuth.promptInstruction = NULL; | ||
| ssh->kbAuth.promptLanguage = NULL; | ||
|
|
||
| /* Force PreparePacket() to fail with WS_OVERFLOW_E. */ | ||
| ssh->outputBuffer.length = 0; | ||
| ssh->outputBuffer.idx = 1; | ||
|
|
||
| savedBuffer = ssh->outputBuffer.buffer; | ||
| ssh->outputBuffer.buffer = NULL; | ||
|
Comment on lines
+622
to
+627
|
||
|
|
||
| ret = SendUserAuthKeyboardResponse(ssh); | ||
| AssertIntEQ(ret, WS_OVERFLOW_E); | ||
|
|
||
| /* Ensure packet purge/reset happened cleanly. */ | ||
| AssertIntEQ(ssh->outputBuffer.idx, 0); | ||
| AssertIntEQ(ssh->outputBuffer.length, 0); | ||
|
|
||
| /* Restore known-good buffer pointer for subsequent tests. */ | ||
| if (ssh->outputBuffer.buffer == NULL) { | ||
| ssh->outputBuffer.buffer = savedBuffer; | ||
| } | ||
|
|
||
| /* Verify SendUserAuthKeyboardResponse() cleaned up kbAuth state. */ | ||
| AssertIntEQ(ssh->kbAuth.promptCount, 0); | ||
| AssertTrue(ssh->kbAuth.prompts == NULL); | ||
| AssertTrue(ssh->kbAuth.promptEcho == NULL); | ||
| } | ||
|
|
||
| static void TestKeyboardResponseNoUserAuthCallback(WOLFSSH* ssh, | ||
| WOLFSSH_CTX* ctx) | ||
| { | ||
| int ret; | ||
|
|
||
| AssertNotNull(ssh); | ||
| AssertNotNull(ctx); | ||
|
|
||
| ResetSession(ssh); | ||
| wolfSSH_SetUserAuth(ctx, NULL); | ||
|
|
||
| ret = SendUserAuthKeyboardResponse(ssh); | ||
| AssertIntEQ(ret, WS_INVALID_STATE_E); | ||
|
|
||
| /* No packet should have been started. */ | ||
| AssertIntEQ(ssh->outputBuffer.length, 0); | ||
| AssertIntEQ(ssh->outputBuffer.idx, 0); | ||
| } | ||
| #endif /* WOLFSSH_KEYBOARD_INTERACTIVE */ | ||
|
|
||
|
|
||
| int main(int argc, char** argv) | ||
| { | ||
| WOLFSSH_CTX* ctx; | ||
|
|
@@ -594,6 +702,11 @@ int main(int argc, char** argv) | |
| TestSftpBufferSendPendingOutput(); | ||
| #endif | ||
|
|
||
| #ifdef WOLFSSH_KEYBOARD_INTERACTIVE | ||
| TestKeyboardResponsePreparePacketFailure(ssh, ctx); | ||
| TestKeyboardResponseNoUserAuthCallback(ssh, ctx); | ||
| #endif | ||
|
|
||
| /* TODO: add app-level regressions that simulate stdin EOF/password | ||
| * prompts and mid-session socket closes once the test harness can | ||
| * drive the wolfssh client without real sockets/tty. */ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔵 [Low]
authDatanot zero-initialized unlike peer functionSendUserAuthRequest💡 SUGGEST
conventionThe stack-local
WS_UserAuthData authData;is declared but not zero-initialized. On early-error paths (e.g.,WS_BAD_ARGUMENTwhenssh == NULL), the struct contains indeterminate stack values untilForceZeroat line 15525. While currently safe (no code readsauthDataon those paths), the peer functionSendUserAuthRequestat line 15561 unconditionally callsWMEMSET(&authData, 0, sizeof(authData));right after declaration. Adding the same pattern here would be more defensive and consistent.Suggestion:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed