1
0
mirror of https://github.com/libssh2/libssh2.git synced 2025-07-31 00:03:08 +03:00

Use modern API in userauth_keyboard_interactive() (#663)

Files: userauth_kbd_packet.c, userauth_kbd_packet.h, test_keyboard_interactive_auth_info_request.c, userauth.c

Notes:
This refactors `SSH_MSG_USERAUTH_INFO_REQUEST` processing in `userauth_keyboard_interactive()` in order to improve robustness, correctness and readability or the code.

* Refactor userauth_keyboard_interactive to use new api for packet parsing
* add unit test for userauth_keyboard_interactive_parse_response()
* add _libssh2_get_boolean() and _libssh2_get_byte() utility functions

Credit:
xalopp
This commit is contained in:
xalopp
2022-02-20 00:46:40 +01:00
committed by GitHub
parent ead7000d28
commit 83853f8aea
11 changed files with 616 additions and 212 deletions

View File

@ -1,5 +1,6 @@
CSOURCES = channel.c comp.c crypt.c hostkey.c kex.c mac.c misc.c \
packet.c publickey.c scp.c session.c sftp.c userauth.c transport.c \
userauth_kbd_packet.c \
version.c knownhost.c agent.c $(CRYPTO_CSOURCES) pem.c keepalive.c global.c \
blowfish.c bcrypt_pbkdf.c agent_win.c

View File

@ -272,8 +272,8 @@ typedef off_t libssh2_struct_stat_size;
typedef struct _LIBSSH2_USERAUTH_KBDINT_PROMPT
{
char *text;
unsigned int length;
unsigned char *text;
size_t length;
unsigned char echo;
} LIBSSH2_USERAUTH_KBDINT_PROMPT;

View File

@ -207,6 +207,8 @@ set(SOURCES
sftp.h
transport.c
transport.h
userauth_kbd_packet.c
userauth_kbd_packet.h
userauth.c
userauth.h
version.c)

View File

@ -760,10 +760,10 @@ struct _LIBSSH2_SESSION
size_t userauth_kybd_data_len;
unsigned char *userauth_kybd_packet;
size_t userauth_kybd_packet_len;
unsigned int userauth_kybd_auth_name_len;
char *userauth_kybd_auth_name;
unsigned userauth_kybd_auth_instruction_len;
char *userauth_kybd_auth_instruction;
size_t userauth_kybd_auth_name_len;
unsigned char *userauth_kybd_auth_name;
size_t userauth_kybd_auth_instruction_len;
unsigned char *userauth_kybd_auth_instruction;
unsigned int userauth_kybd_num_prompts;
int userauth_kybd_auth_failure;
LIBSSH2_USERAUTH_KBDINT_PROMPT *userauth_kybd_prompts;

View File

@ -732,6 +732,29 @@ void _libssh2_string_buf_free(LIBSSH2_SESSION *session, struct string_buf *buf)
buf = NULL;
}
int _libssh2_get_byte(struct string_buf *buf, unsigned char *out)
{
if(!_libssh2_check_length(buf, 1)) {
return -1;
}
*out = buf->dataptr[0];
buf->dataptr += 1;
return 0;
}
int _libssh2_get_boolean(struct string_buf *buf, unsigned char *out)
{
if(!_libssh2_check_length(buf, 1)) {
return -1;
}
*out = buf->dataptr[0] == 0 ? 0 : 1;
buf->dataptr += 1;
return 0;
}
int _libssh2_get_u32(struct string_buf *buf, uint32_t *out)
{
if(!_libssh2_check_length(buf, 4)) {

View File

@ -91,6 +91,8 @@ void _libssh2_explicit_zero(void *buf, size_t size);
struct string_buf* _libssh2_string_buf_new(LIBSSH2_SESSION *session);
void _libssh2_string_buf_free(LIBSSH2_SESSION *session,
struct string_buf *buf);
int _libssh2_get_boolean(struct string_buf *buf, unsigned char *out);
int _libssh2_get_byte(struct string_buf *buf, unsigned char *out);
int _libssh2_get_u32(struct string_buf *buf, uint32_t *out);
int _libssh2_get_u64(struct string_buf *buf, libssh2_uint64_t *out);
int _libssh2_match_string(struct string_buf *buf, const char *match);

View File

@ -52,6 +52,7 @@
#include "transport.h"
#include "session.h"
#include "userauth.h"
#include "userauth_kbd_packet.h"
/* libssh2_userauth_list
*
@ -1878,13 +1879,13 @@ userauth_keyboard_interactive(LIBSSH2_SESSION * session,
((*response_callback)))
{
unsigned char *s;
int rc;
static const unsigned char reply_codes[4] = {
SSH_MSG_USERAUTH_SUCCESS,
SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_INFO_REQUEST, 0
};
unsigned int language_tag_len;
unsigned int i;
if(session->userauth_kybd_state == libssh2_NB_state_idle) {
@ -2007,215 +2008,14 @@ userauth_keyboard_interactive(LIBSSH2_SESSION * session,
}
/* server requested PAM-like conversation */
s = session->userauth_kybd_data + 1;
if(session->userauth_kybd_data_len >= 5) {
/* string name (ISO-10646 UTF-8) */
session->userauth_kybd_auth_name_len = _libssh2_ntohu32(s);
if(session->userauth_kybd_auth_name_len >
session->userauth_kybd_data_len - 5)
return _libssh2_error(session,
LIBSSH2_ERROR_OUT_OF_BOUNDARY,
"Bad keyboard auth name");
s += 4;
}
else {
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
"userauth keyboard data buffer too small"
"to get length");
if(userauth_keyboard_interactive_decode_info_request(session)
< 0) {
goto cleanup;
}
if(session->userauth_kybd_auth_name_len) {
session->userauth_kybd_auth_name =
LIBSSH2_ALLOC(session,
session->userauth_kybd_auth_name_len);
if(!session->userauth_kybd_auth_name) {
_libssh2_error(session, LIBSSH2_ERROR_ALLOC,
"Unable to allocate memory for "
"keyboard-interactive 'name' "
"request field");
goto cleanup;
}
if(s + session->userauth_kybd_auth_name_len <=
session->userauth_kybd_data +
session->userauth_kybd_data_len) {
memcpy(session->userauth_kybd_auth_name, s,
session->userauth_kybd_auth_name_len);
s += session->userauth_kybd_auth_name_len;
}
else {
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
"userauth keyboard data buffer too small"
"for auth name");
goto cleanup;
}
}
if(s + 4 <= session->userauth_kybd_data +
session->userauth_kybd_data_len) {
/* string instruction (ISO-10646 UTF-8) */
session->userauth_kybd_auth_instruction_len =
_libssh2_ntohu32(s);
s += 4;
}
else {
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
"userauth keyboard data buffer too small"
"for auth instruction length");
goto cleanup;
}
if(session->userauth_kybd_auth_instruction_len) {
session->userauth_kybd_auth_instruction =
LIBSSH2_ALLOC(session,
session->userauth_kybd_auth_instruction_len);
if(!session->userauth_kybd_auth_instruction) {
_libssh2_error(session, LIBSSH2_ERROR_ALLOC,
"Unable to allocate memory for "
"keyboard-interactive 'instruction' "
"request field");
goto cleanup;
}
if(s + session->userauth_kybd_auth_instruction_len <=
session->userauth_kybd_data +
session->userauth_kybd_data_len) {
memcpy(session->userauth_kybd_auth_instruction, s,
session->userauth_kybd_auth_instruction_len);
s += session->userauth_kybd_auth_instruction_len;
}
else {
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
"userauth keyboard data buffer too small"
"for auth instruction");
goto cleanup;
}
}
if(s + 4 <= session->userauth_kybd_data +
session->userauth_kybd_data_len) {
/* string language tag (as defined in [RFC-3066]) */
language_tag_len = _libssh2_ntohu32(s);
s += 4;
}
else {
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
"userauth keyboard data buffer too small"
"for auth language tag length");
goto cleanup;
}
if(s + language_tag_len <= session->userauth_kybd_data +
session->userauth_kybd_data_len) {
/* ignoring this field as deprecated */
s += language_tag_len;
}
else {
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
"userauth keyboard data buffer too small"
"for auth language tag");
goto cleanup;
}
if(s + 4 <= session->userauth_kybd_data +
session->userauth_kybd_data_len) {
/* int num-prompts */
session->userauth_kybd_num_prompts = _libssh2_ntohu32(s);
s += 4;
}
else {
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
"userauth keyboard data buffer too small"
"for auth num keyboard prompts");
goto cleanup;
}
if(session->userauth_kybd_num_prompts > 100) {
_libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY,
"Too many replies for "
"keyboard-interactive prompts");
goto cleanup;
}
if(session->userauth_kybd_num_prompts) {
session->userauth_kybd_prompts =
LIBSSH2_CALLOC(session,
sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) *
session->userauth_kybd_num_prompts);
if(!session->userauth_kybd_prompts) {
_libssh2_error(session, LIBSSH2_ERROR_ALLOC,
"Unable to allocate memory for "
"keyboard-interactive prompts array");
goto cleanup;
}
session->userauth_kybd_responses =
LIBSSH2_CALLOC(session,
sizeof(LIBSSH2_USERAUTH_KBDINT_RESPONSE) *
session->userauth_kybd_num_prompts);
if(!session->userauth_kybd_responses) {
_libssh2_error(session, LIBSSH2_ERROR_ALLOC,
"Unable to allocate memory for "
"keyboard-interactive responses array");
goto cleanup;
}
for(i = 0; i < session->userauth_kybd_num_prompts; i++) {
if(s + 4 <= session->userauth_kybd_data +
session->userauth_kybd_data_len) {
/* string prompt[1] (ISO-10646 UTF-8) */
session->userauth_kybd_prompts[i].length =
_libssh2_ntohu32(s);
s += 4;
}
else {
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
"userauth keyboard data buffer too "
"small for auth keyboard "
"prompt length");
goto cleanup;
}
session->userauth_kybd_prompts[i].text =
LIBSSH2_CALLOC(session,
session->userauth_kybd_prompts[i].
length);
if(!session->userauth_kybd_prompts[i].text) {
_libssh2_error(session, LIBSSH2_ERROR_ALLOC,
"Unable to allocate memory for "
"keyboard-interactive prompt message");
goto cleanup;
}
if(s + session->userauth_kybd_prompts[i].length <=
session->userauth_kybd_data +
session->userauth_kybd_data_len) {
memcpy(session->userauth_kybd_prompts[i].text, s,
session->userauth_kybd_prompts[i].length);
s += session->userauth_kybd_prompts[i].length;
}
else {
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
"userauth keyboard data buffer too "
"small for auth keyboard prompt");
goto cleanup;
}
if(s < session->userauth_kybd_data +
session->userauth_kybd_data_len) {
/* boolean echo[1] */
session->userauth_kybd_prompts[i].echo = *s++;
}
else {
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
"userauth keyboard data buffer too "
"small for auth keyboard prompt echo");
goto cleanup;
}
}
}
response_callback(session->userauth_kybd_auth_name,
response_callback((const char *)session->userauth_kybd_auth_name,
session->userauth_kybd_auth_name_len,
(const char *)
session->userauth_kybd_auth_instruction,
session->userauth_kybd_auth_instruction_len,
session->userauth_kybd_num_prompts,

162
src/userauth_kbd_packet.c Normal file
View File

@ -0,0 +1,162 @@
/* Copyright (c) 2022, Xaver Loppenstedt <xaver@loppenstedt.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms,
* with or without modification, are permitted provided
* that the following conditions are met:
*
* Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
*
* Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of the copyright holder nor the names
* of any other contributors may be used to endorse or
* promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*/
#include "libssh2_priv.h"
#include "userauth_kbd_packet.h"
int userauth_keyboard_interactive_decode_info_request(LIBSSH2_SESSION *session)
{
unsigned char *language_tag;
size_t language_tag_len;
unsigned int i;
unsigned char packet_type;
struct string_buf decoded;
decoded.data = session->userauth_kybd_data;
decoded.dataptr = session->userauth_kybd_data;
decoded.len = session->userauth_kybd_data_len;
if(session->userauth_kybd_data_len < 17) {
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
"userauth keyboard data buffer too small "
"to get length");
return -1;
}
/* byte SSH_MSG_USERAUTH_INFO_REQUEST */
_libssh2_get_byte(&decoded, &packet_type);
/* string name (ISO-10646 UTF-8) */
if(_libssh2_copy_string(session, &decoded,
&session->userauth_kybd_auth_name,
&session->userauth_kybd_auth_name_len) == -1) {
_libssh2_error(session, LIBSSH2_ERROR_ALLOC,
"Unable to decode "
"keyboard-interactive 'name' "
"request field");
return -1;
}
/* string instruction (ISO-10646 UTF-8) */
if(_libssh2_copy_string(session, &decoded,
&session->userauth_kybd_auth_instruction,
&session->userauth_kybd_auth_instruction_len)
== -1) {
_libssh2_error(session, LIBSSH2_ERROR_ALLOC,
"Unable to decode "
"keyboard-interactive 'instruction' "
"request field");
return -1;
}
/* string language tag (as defined in [RFC-3066]) */
if(_libssh2_get_string(&decoded, &language_tag,
&language_tag_len) == -1) {
_libssh2_error(session, LIBSSH2_ERROR_ALLOC,
"Unable to decode "
"keyboard-interactive 'language tag' "
"request field");
return -1;
}
/* int num-prompts */
if(_libssh2_get_u32(&decoded, &session->userauth_kybd_num_prompts) == -1) {
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
"Unable to decode "
"keyboard-interactive number of keyboard prompts");
return -1;
}
if(session->userauth_kybd_num_prompts > 100) {
_libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY,
"Too many replies for "
"keyboard-interactive prompts");
return -1;
}
if(session->userauth_kybd_num_prompts == 0) {
return 0;
}
session->userauth_kybd_prompts =
LIBSSH2_CALLOC(session,
sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) *
session->userauth_kybd_num_prompts);
if(!session->userauth_kybd_prompts) {
_libssh2_error(session, LIBSSH2_ERROR_ALLOC,
"Unable to allocate memory for "
"keyboard-interactive prompts array");
return -1;
}
session->userauth_kybd_responses =
LIBSSH2_CALLOC(session,
sizeof(LIBSSH2_USERAUTH_KBDINT_RESPONSE) *
session->userauth_kybd_num_prompts);
if(!session->userauth_kybd_responses) {
_libssh2_error(session, LIBSSH2_ERROR_ALLOC,
"Unable to allocate memory for "
"keyboard-interactive responses array");
return -1;
}
for(i = 0; i < session->userauth_kybd_num_prompts; i++) {
/* string prompt[1] (ISO-10646 UTF-8) */
if(_libssh2_copy_string(session, &decoded,
&session->userauth_kybd_prompts[i].text,
&session->userauth_kybd_prompts[i].length)
== -1) {
_libssh2_error(session, LIBSSH2_ERROR_ALLOC,
"Unable to decode "
"keyboard-interactive prompt message");
return -1;
}
/* boolean echo[1] */
if(_libssh2_get_boolean(&decoded,
&session->userauth_kybd_prompts[i].echo)
== -1) {
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
"Unable to decode "
"user auth keyboard prompt echo");
return -1;
}
}
return 0;
}

43
src/userauth_kbd_packet.h Normal file
View File

@ -0,0 +1,43 @@
/* Copyright (c) 2022, Xaver Loppenstedt <xaver@loppenstedt.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms,
* with or without modification, are permitted provided
* that the following conditions are met:
*
* Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
*
* Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of the copyright holder nor the names
* of any other contributors may be used to endorse or
* promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*/
#ifndef __LIBSSH2_USERAUTH_KBD_PARSE_H
#define __LIBSSH2_USERAUTH_KBD_PARSE_H
int userauth_keyboard_interactive_decode_info_request(LIBSSH2_SESSION *);
#endif /* __LIBSSH2_USERAUTH_KBD_PARSE_H */

View File

@ -76,6 +76,8 @@ if(CRYPTO_BACKEND STREQUAL "OpenSSL" OR NOT CRYPTO_BACKEND)
if(OPENSSL_FOUND)
set(CRYPTO_BACKEND "OpenSSL")
set(CRYPTO_BACKEND_DEFINE "LIBSSH2_OPENSSL")
set(CRYPTO_BACKEND_INCLUDE_DIR ${OPENSSL_INCLUDE_DIR})
endif()
endif()
@ -85,6 +87,8 @@ if(CRYPTO_BACKEND STREQUAL "Libgcrypt" OR NOT CRYPTO_BACKEND)
if(LIBGCRYPT_FOUND)
set(CRYPTO_BACKEND "Libgcrypt")
set(CRYPTO_BACKEND_DEFINE "LIBSSH2_LIBGCRYPT")
set(CRYPTO_BACKEND_INCLUDE_DIR ${LIBGCRYPT_INCLUDE_DIRS})
endif()
endif()
@ -95,6 +99,8 @@ if(CRYPTO_BACKEND STREQUAL "WinCNG" OR NOT CRYPTO_BACKEND)
if(HAVE_BCRYPT_H)
set(CRYPTO_BACKEND "WinCNG")
set(CRYPTO_BACKEND_DEFINE "LIBSSH2_WINCNG")
set(CRYPTO_BACKEND_INCLUDE_DIR "")
endif()
endif()
@ -104,6 +110,8 @@ if(CRYPTO_BACKEND STREQUAL "mbedTLS" OR NOT CRYPTO_BACKEND)
if(MBEDTLS_FOUND)
set(CRYPTO_BACKEND "mbedTLS")
set(CRYPTO_BACKEND_DEFINE "LIBSSH2_MBEDTLS")
set(CRYPTO_BACKEND_INCLUDE_DIR ${MBEDTLS_INCLUDE_DIR})
endif()
endif()
@ -166,6 +174,34 @@ foreach(test ${TESTS})
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
endforeach()
if(WIN32 AND BUILD_SHARED_LIBS)
# Workaround for Visual Studio
add_executable(test_keyboard_interactive_auth_info_request test_keyboard_interactive_auth_info_request.c ../src/userauth_kbd_packet.c ../src/misc.c)
else()
add_executable(test_keyboard_interactive_auth_info_request test_keyboard_interactive_auth_info_request.c ../src/userauth_kbd_packet.c)
endif()
target_compile_definitions(test_keyboard_interactive_auth_info_request PRIVATE "${CRYPTO_BACKEND_DEFINE}")
target_include_directories(test_keyboard_interactive_auth_info_request PRIVATE "${CMAKE_CURRENT_BINARY_DIR}" "../src/" "${CRYPTO_BACKEND_INCLUDE_DIR}")
find_program(GCOV_PATH gcov)
if(CMAKE_COMPILER_IS_GNUCC AND GCOV_PATH)
target_compile_options(test_keyboard_interactive_auth_info_request BEFORE PRIVATE
-g --coverage -fprofile-abs-path)
target_link_libraries(test_keyboard_interactive_auth_info_request ${LIBRARIES} libssh2 gcov)
else()
target_link_libraries(test_keyboard_interactive_auth_info_request ${LIBRARIES} libssh2)
endif()
add_test(
NAME test_keyboard_interactive_auth_info_request COMMAND $<TARGET_FILE:test_keyboard_interactive_auth_info_request>
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
add_custom_target(coverage
COMMAND gcovr -r "${CMAKE_SOURCE_DIR}" --exclude tests/*
COMMAND mkdir -p "${CMAKE_CURRENT_BINARY_DIR}/coverage/"
COMMAND gcovr -r "${CMAKE_SOURCE_DIR}" --exclude tests/* --html-details --output "${CMAKE_CURRENT_BINARY_DIR}/coverage/index.html")
add_custom_target(clean-coverage
COMMAND rm -rf "${CMAKE_CURRENT_BINARY_DIR}/coverage/")
add_target_to_copy_dependencies(
TARGET copy_test_dependencies
DEPENDENCIES ${RUNTIME_DEPENDENCIES}

View File

@ -0,0 +1,335 @@
/* Copyright (C) 2022 Xaver Loppenstedt
* All rights reserved.
*
* Redistribution and use in source and binary forms,
* with or without modification, are permitted provided
* that the following conditions are met:
*
* Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
*
* Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of the copyright holder nor the names
* of any other contributors may be used to endorse or
* promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*/
#include <stdlib.h>
#include "libssh2_priv.h"
#include "userauth_kbd_packet.h"
#define PASS 0
#define FAIL -1
struct expected {
int rc;
int last_error_code;
char *last_error_message;
};
struct test_case {
char *data;
int data_len;
struct expected expected;
};
#define TEST_CASES_LEN 16
struct test_case test_cases[TEST_CASES_LEN] = {
/* to small */
{
NULL, 0,
{FAIL, -38,
"userauth keyboard data buffer too small to get length"}},
/* to small */
{
"1234", 4,
{FAIL, -38,
"userauth keyboard data buffer too small to get length"}},
/* smalest valid packet possible */
{
"<"
"\0\0\0\0"
"\0\0\0\0"
"\0\0\0\0"
"\0\0\0\0", 17,
{PASS, 0, ""}},
/* overrun name */
{
"<"
"\0\0\0\x7f"
"\0\0\0\0"
"\0\0\0\0"
"\0\0\0\0", 17,
{FAIL, -6,
"Unable to decode keyboard-interactive 'name' request field"}},
/* overrun instruction */
{
"<"
"\0\0\0\0"
"\0\0\0\x7f"
"\0\0\0\0"
"\0\0\0\0", 17,
{FAIL, -6,
"Unable to decode keyboard-interactive 'instruction' "
"request field"}},
/* overrun language */
{
"<"
"\0\0\0\0"
"\0\0\0\0"
"\0\0\0\x7f"
"\0\0\0\0", 17,
{FAIL, -6, "Unable to decode keyboard-interactive 'language tag' "
"request field"}},
/* underrun prompt number */
{
"<"
"\0\0\0\x01"
"\0\0\0\0"
"\0\0\0\0"
"\0\0\0\0", 17,
{FAIL, -38,
"Unable to decode keyboard-interactive number of "
"keyboard prompts"}},
/* too many prompts */
{
"<"
"\0\0\0\0"
"\0\0\0\0"
"\0\0\0\0"
"\0\0\0\x7f", 17,
{FAIL, -41, "Too many replies for keyboard-interactive prompts"}},
/* empty prompt */
{
"<"
"\0\0\0\0"
"\0\0\0\0"
"\0\0\0\0"
"\0\0\0\x01"
"\0\0\0\0"
"\0", 22, {PASS, 0, ""}},
/* copied from OpenSSH */
{
"<"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01"
"\0\0\0\x0aPassword: \0", 32, {PASS, 0, ""}},
/* overrun in prompt text */
{
"<"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01"
"\0\0\0\x7bPassword: \0", 32,
{FAIL, -6, "Unable to decode keyboard-interactive "
"prompt message"}},
/* no echo prompt boolean */
{
"<"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01"
"\0\0\0\x0bPassword: \0", 32,
{FAIL, -38, "Unable to decode user auth keyboard prompt echo"}},
/* two prompts */
{
"<"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02"
"\0\0\0\x0aPassword: \0"
"\0\0\0\x07Token: \1", 44,
{PASS, 0, ""}},
/* example from RFC 4256 */
{
"<"
"\0\0\0\x19""CRYPTOCard Authentication"
"\0\0\0\x1b""The challenge is '14315716'"
"\0\0\0\x05""en-US"
"\0\0\0\x01"
"\0\0\0\x0aResponse: "
"\x01"
, 89, {PASS, 0, ""}},
/* three prompts, 3rd missing*/
{
"<"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03"
"\0\0\0\x0aPassword: \0"
"\0\0\0\x07Token: \1", 44,
{FAIL, -6,
"Unable to decode keyboard-interactive prompt message"}},
/* overflow language on 32 bit platform */
{
"<"
"\0\0\0\x19"
"\0\0\0\x01"
"\0\0\0\x05""PWN3D\0\1\2\3\4\5\6\7\1\2\3"
"\x01"
"\0\0\0\x1b""The challenge is '14315716'"
"\xff\xff\xff\xc4""en-US"
"\0\0\0\x01"
"\0\0\0\x0aResponse: "
"\x01",
89,
{FAIL, -6,
"Unable to decode keyboard-interactive 'language tag' "
"request field"}},
};
#define FAILED_MALLOC_TEST_CASES_LEN 2
struct test_case failed_malloc_test_cases[FAILED_MALLOC_TEST_CASES_LEN] = {
/* malloc fail */
{
"<"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01"
"\0\0\0\x0aPassword: \0", 32,
{FAIL, -6,
"Unable to allocate memory for "
"keyboard-interactive prompts array"}},
/* malloc fail */
{
"<"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01"
"\0\0\0\x0aPassword: \0", 32,
{FAIL, -6,
"Unable to allocate memory for "
"keyboard-interactive responses array"
}}
};
static int alloc_count = 0;
static int free_count = 0;
/* libssh2_default_alloc
*/
static
LIBSSH2_ALLOC_FUNC(test_alloc)
{
alloc_count++;
int *threshold_int_ptr = *abstract;
if (*abstract != NULL && *threshold_int_ptr == alloc_count) {
return NULL;
}
return malloc(count);
}
/* libssh2_default_free
*/
static
LIBSSH2_FREE_FUNC(test_free)
{
(void) abstract;
free_count++;
free(ptr);
}
static
int test_case(int num,
char *data, int data_len, void *abstract,
struct expected expected)
{
alloc_count = 0;
free_count = 0;
LIBSSH2_SESSION *session = NULL;
session = libssh2_session_init_ex(test_alloc, test_free, NULL, abstract);
if(session == NULL) {
fprintf(stderr, "libssh2_session_init_ex failed\n");
return 1;
}
session->userauth_kybd_data = LIBSSH2_ALLOC(session, data_len);
session->userauth_kybd_data_len = data_len;
memcpy(session->userauth_kybd_data, data, data_len);
int rc = userauth_keyboard_interactive_decode_info_request(session);
if(rc != expected.rc) {
fprintf(stdout,
"Test case %d: expected return code to be %d got %d\n",
num, expected.rc, rc);
return 1;
}
char *message;
int error_code = libssh2_session_last_error(session, &message, NULL, 0);
if(expected.last_error_code != error_code) {
fprintf(stdout,
"Test case %d: expected last error code to be "
"\"%d\" got \"%d\"\n",
num, expected.last_error_code, error_code);
return 1;
}
if(strcmp(expected.last_error_message, message) != 0) {
fprintf(stdout,
"Test case %d: expected last error message to be "
"\"%s\" got \"%s\"\n",
num, expected.last_error_message, message);
return 1;
}
libssh2_session_free(session);
fprintf(stderr, "Test case %d passed\n", num);
return 0;
}
int main()
{
int i;
for(i = 0; i < TEST_CASES_LEN; i++) {
test_case(i + 1,
test_cases[i].data, test_cases[i].data_len,
NULL,
test_cases[i].expected);
}
for(i = 0; i < FAILED_MALLOC_TEST_CASES_LEN; i++) {
int tc = i + TEST_CASES_LEN + 1;
int malloc_call_num = 5 + i;
test_case(tc,
failed_malloc_test_cases[i].data,
failed_malloc_test_cases[i].data_len,
&malloc_call_num,
failed_malloc_test_cases[i].expected);
}
return 0;
}
/* Workaround for Visual Studio */
#ifdef _MSC_VER
int
bcrypt_pbkdf(const char *pass, size_t passlen, const uint8_t *salt,
size_t saltlen,
uint8_t *key, size_t keylen, unsigned int rounds)
{
(void)pass;
(void)passlen;
(void)salt;
(void)saltlen;
(void)key;
(void)keylen;
(void)rounds;
return -1;
}
#endif