mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-02-04 04:10:39 +09:00
Add Keyboard Interactive
Signed-off-by: anshul agrawal <anshulagrawal2902@gmail.com> Reviewed-by: Jakub Jelen <jjelen@redhat.com>
This commit is contained in:
@@ -631,6 +631,58 @@ auth_publickey(ssh_session session,
|
||||
return SSH_AUTH_DENIED;
|
||||
}
|
||||
|
||||
static int kbdint_check_response(ssh_session session)
|
||||
{
|
||||
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("omnitrix", answer);
|
||||
if (cmp != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
answer = ssh_userauth_kbdint_getanswer(session, 1);
|
||||
cmp = strcmp("000", answer);
|
||||
if (cmp != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
auth_kbdint(ssh_message message, ssh_session session, void *userdata)
|
||||
{
|
||||
struct session_data_struct *sdata = (struct session_data_struct *)userdata;
|
||||
const char *name = "\n\nKeyboard-Interactive Fancy Authentication\n";
|
||||
const char *instruction = "Most powerful weapon in the galaxy";
|
||||
const char *prompts[2] = {"Name of the weapon: ", "Destruct Code: "};
|
||||
char echo[] = {1, 0};
|
||||
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->authenticated = 1;
|
||||
return SSH_AUTH_SUCCESS;
|
||||
}
|
||||
return SSH_AUTH_DENIED;
|
||||
}
|
||||
}
|
||||
|
||||
static ssh_channel
|
||||
channel_open(ssh_session session, void *userdata)
|
||||
{
|
||||
@@ -720,14 +772,15 @@ handle_session(ssh_event event, ssh_session session)
|
||||
struct ssh_server_callbacks_struct server_cb = {
|
||||
.userdata = &sdata,
|
||||
.auth_password_function = auth_password,
|
||||
.auth_kbdint_function = auth_kbdint,
|
||||
.channel_open_request_session_function = channel_open,
|
||||
};
|
||||
|
||||
if (authorizedkeys[0]) {
|
||||
server_cb.auth_pubkey_function = auth_publickey;
|
||||
ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_PUBLICKEY);
|
||||
ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_PUBLICKEY | SSH_AUTH_METHOD_INTERACTIVE);
|
||||
} else
|
||||
ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD);
|
||||
ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_INTERACTIVE);
|
||||
|
||||
ssh_callbacks_init(&server_cb);
|
||||
ssh_callbacks_init(&channel_cb);
|
||||
|
||||
@@ -276,6 +276,18 @@ typedef int (*ssh_auth_gssapi_mic_callback) (ssh_session session, const char *us
|
||||
typedef int (*ssh_auth_pubkey_callback) (ssh_session session, const char *user, struct ssh_key_struct *pubkey,
|
||||
char signature_state, void *userdata);
|
||||
|
||||
/**
|
||||
* @brief SSH authentication callback. Tries to authenticates user with the "keyboard-interactive" method
|
||||
* @param message Current message
|
||||
* @param session Current session handler
|
||||
* @param userdata Userdata to be passed to the callback function.
|
||||
* @returns SSH_AUTH_SUCCESS Authentication is accepted.
|
||||
* @returns SSH_AUTH_INFO More info required for authentication.
|
||||
* @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed.
|
||||
* @returns SSH_AUTH_DENIED Authentication failed.
|
||||
*/
|
||||
typedef int (*ssh_auth_kbdint_callback) (ssh_message message, ssh_session session, void *userdata);
|
||||
|
||||
/**
|
||||
* @brief Handles an SSH service request
|
||||
* @param session current session handler
|
||||
@@ -418,6 +430,12 @@ struct ssh_server_callbacks_struct {
|
||||
*/
|
||||
ssh_channel_open_request_direct_tcpip_callback
|
||||
channel_open_request_direct_tcpip_function;
|
||||
|
||||
/** This function gets called when a client tries to authenticate through
|
||||
* keyboard interactive method.
|
||||
*/
|
||||
ssh_auth_kbdint_callback auth_kbdint_function;
|
||||
|
||||
};
|
||||
typedef struct ssh_server_callbacks_struct *ssh_server_callbacks;
|
||||
|
||||
|
||||
@@ -184,6 +184,19 @@ static int ssh_execute_server_request(ssh_session session, ssh_message msg)
|
||||
ssh_message_reply_default(msg);
|
||||
}
|
||||
|
||||
return SSH_OK;
|
||||
} else if (msg->auth_request.method == SSH_AUTH_METHOD_INTERACTIVE &&
|
||||
ssh_callbacks_exists(session->server_callbacks, auth_kbdint_function)) {
|
||||
rc = session->server_callbacks->auth_kbdint_function(msg,
|
||||
session,
|
||||
session->server_callbacks->userdata);
|
||||
if (rc == SSH_AUTH_SUCCESS || rc == SSH_AUTH_PARTIAL) {
|
||||
ssh_message_auth_reply_success(msg, rc == SSH_AUTH_PARTIAL);
|
||||
} else if (rc == SSH_AUTH_INFO) {
|
||||
return SSH_OK;
|
||||
} else {
|
||||
ssh_message_reply_default(msg);
|
||||
}
|
||||
return SSH_OK;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user