Add Keyboard Interactive

Signed-off-by: anshul agrawal <anshulagrawal2902@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
This commit is contained in:
anshul agrawal
2026-01-06 22:56:44 +05:30
parent 06186279a8
commit 3f0007895c
7 changed files with 238 additions and 3 deletions

View File

@@ -172,6 +172,65 @@ null_userdata:
return SSH_AUTH_DENIED;
}
static int kbdint_check_response(ssh_session session, struct session_data_st *sdata)
{
int count, cmp;
const char *answer = NULL;
count = ssh_userauth_kbdint_getnanswers(session);
if (count != 2) {
return 0;
}
answer = ssh_userauth_kbdint_getanswer(session, 0);
cmp = strcasecmp(sdata->username, answer);
if (cmp != 0) {
return 0;
}
answer = ssh_userauth_kbdint_getanswer(session, 1);
cmp = strcmp(sdata->password, answer);
if (cmp != 0) {
return 0;
}
return 1;
}
static int
auth_kbdint_cb(ssh_message message, ssh_session session, void *userdata)
{
struct session_data_st *sdata = (struct session_data_st *)userdata;
const char *name = "\n\nKeyboard-Interactive Fancy Authentication\n";
const char *instruction = "Get yourself authenticated";
const char *prompts[2] = {"Username: ", "Password: "};
char echo[] = {1, 0};
if (sdata == NULL) {
fprintf(stderr, "Error: NULL userdata\n");
return SSH_AUTH_DENIED;
}
if (!ssh_message_auth_kbdint_is_response(message)) {
printf("User %s wants to auth with kbdint\n",
ssh_message_auth_user(message));
ssh_message_auth_interactive_request(message,
name,
instruction,
2,
prompts,
echo);
return SSH_AUTH_INFO;
} else {
if (kbdint_check_response(session, sdata)) {
sdata->authenticated = 1;
return SSH_AUTH_SUCCESS;
}
}
return SSH_AUTH_DENIED;
}
#if WITH_GSSAPI
int auth_gssapi_mic_cb(ssh_session session,
UNUSED_PARAM(const char *user),
@@ -783,6 +842,7 @@ struct ssh_server_callbacks_struct *get_default_server_cb(void)
cb->auth_password_function = auth_password_cb;
cb->auth_pubkey_function = auth_pubkey_cb;
cb->channel_open_request_session_function = channel_new_session_cb;
cb->auth_kbdint_function = auth_kbdint_cb;
#if WITH_GSSAPI
cb->auth_gssapi_mic_function = auth_gssapi_mic_cb;
#endif
@@ -912,7 +972,8 @@ void default_handle_session_cb(ssh_event event,
} else {
ssh_set_auth_methods(session,
SSH_AUTH_METHOD_PASSWORD |
SSH_AUTH_METHOD_PUBLICKEY|
SSH_AUTH_METHOD_PUBLICKEY |
SSH_AUTH_METHOD_INTERACTIVE |
SSH_AUTH_METHOD_GSSAPI_MIC);
}

View File

@@ -276,6 +276,7 @@ static int init_server_state(struct server_state_st *state,
} else {
state->auth_methods = SSH_AUTH_METHOD_PASSWORD |
SSH_AUTH_METHOD_PUBLICKEY |
SSH_AUTH_METHOD_INTERACTIVE |
SSH_AUTH_METHOD_GSSAPI_MIC;
}

View File

@@ -375,6 +375,22 @@ static void handle_kbdint_session_cb(ssh_event event,
goto end;
}
/*
* This test was written prior to adding the kbdint callback
* for the server. Hence, here the server uses the
* ssh_message_callback for kbdint authentication,
* instead of the kbdint callback.
*
* Setting the kbdint callback as NULL ensures that the
* default kbdint callback for test_server doesn't get used
* for kbdint authentication.
*
* The test for kbdint callback based authentication has
* been added in torture_server.c, libssh keeps this test to
* test the old way of doing kbdint authentication using
* ssh_message_callback.
*/
server_cb->auth_kbdint_function = NULL;
server_cb->userdata = &sdata;
/* This is a macro, it does not return a value */

View File

@@ -287,6 +287,76 @@ static void torture_server_auth_pubkey(void **state)
assert_int_equal(rc, SSH_AUTH_SUCCESS);
}
static void torture_server_auth_kbdint(void **state)
{
struct test_server_st *tss = *state;
struct torture_state *s = NULL;
ssh_session session = NULL;
int rc;
assert_non_null(tss);
s = tss->state;
assert_non_null(s);
session = s->ssh.session;
assert_non_null(session);
rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_BOB);
assert_ssh_return_code(session, rc);
rc = ssh_connect(session);
assert_ssh_return_code(session, rc);
rc = ssh_userauth_none(session, NULL);
assert_int_equal(rc, SSH_AUTH_DENIED);
rc = ssh_userauth_list(session, NULL);
assert_true(rc & SSH_AUTH_METHOD_INTERACTIVE);
rc = ssh_userauth_kbdint(session, NULL, NULL);
assert_int_equal(rc, SSH_AUTH_INFO);
assert_int_equal(ssh_userauth_kbdint_getnprompts(session), 2);
/* Passing a wrong password */
rc = ssh_userauth_kbdint_setanswer(session, 0, SSHD_DEFAULT_USER);
assert_int_equal(rc, 0);
rc = ssh_userauth_kbdint_setanswer(session, 1, "wrongpassword");
assert_int_equal(rc, 0);
rc = ssh_userauth_kbdint(session, NULL, NULL);
assert_int_equal(rc, SSH_AUTH_DENIED);
rc = ssh_userauth_kbdint(session, NULL, NULL);
assert_int_equal(rc, SSH_AUTH_INFO);
assert_int_equal(ssh_userauth_kbdint_getnprompts(session), 2);
/* Passing a wrong username */
rc = ssh_userauth_kbdint_setanswer(session, 0, "wrongusername");
assert_int_equal(rc, 0);
rc = ssh_userauth_kbdint_setanswer(session, 1, SSHD_DEFAULT_PASSWORD);
assert_int_equal(rc, 0);
rc = ssh_userauth_kbdint(session, NULL, NULL);
assert_int_equal(rc, SSH_AUTH_DENIED);
rc = ssh_userauth_kbdint(session, NULL, NULL);
assert_int_equal(rc, SSH_AUTH_INFO);
assert_int_equal(ssh_userauth_kbdint_getnprompts(session), 2);
/* Passing the right password */
rc = ssh_userauth_kbdint_setanswer(session, 0, SSHD_DEFAULT_USER);
assert_int_equal(rc, 0);
rc = ssh_userauth_kbdint_setanswer(session, 1, SSHD_DEFAULT_PASSWORD);
assert_int_equal(rc, 0);
rc = ssh_userauth_kbdint(session, NULL, NULL);
assert_int_equal(rc, SSH_AUTH_SUCCESS);
}
static void torture_server_hostkey_mismatch(void **state)
{
struct test_server_st *tss = *state;
@@ -551,6 +621,9 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_server_auth_pubkey,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_server_auth_kbdint,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_server_hostkey_mismatch,
session_setup,
session_teardown),