1
0
mirror of https://git.libssh.org/projects/libssh.git synced 2025-08-08 19:02:06 +03:00

Added the keyboard-interactive authentication method

This commit is contained in:
milo
2011-02-08 00:40:18 +01:00
parent 32e23a25da
commit 3c0a4781e4
9 changed files with 363 additions and 17 deletions

View File

@@ -29,6 +29,27 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_failure);
SSH_PACKET_CALLBACK(ssh_packet_userauth_success); SSH_PACKET_CALLBACK(ssh_packet_userauth_success);
SSH_PACKET_CALLBACK(ssh_packet_userauth_pk_ok); SSH_PACKET_CALLBACK(ssh_packet_userauth_pk_ok);
SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request); SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request);
SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response);
/** @internal
* kdbint structure must be shared with message.c
* and server.c
*/
struct ssh_kbdint_struct {
uint32_t nprompts;
uint32_t nanswers;
char *name;
char *instruction;
char **prompts;
unsigned char *echo; /* bool array */
char **answers;
};
typedef struct ssh_kbdint_struct* ssh_kbdint;
ssh_kbdint kbdint_new(void);
void kbdint_clean(ssh_kbdint kbd);
void kbdint_free(ssh_kbdint kbd);
#ifdef WITH_SSH1 #ifdef WITH_SSH1
void ssh_auth1_handler(ssh_session session, uint8_t type); void ssh_auth1_handler(ssh_session session, uint8_t type);

View File

@@ -446,6 +446,8 @@ LIBSSH_API const char *ssh_userauth_kbdint_getinstruction(ssh_session session);
LIBSSH_API const char *ssh_userauth_kbdint_getname(ssh_session session); LIBSSH_API const char *ssh_userauth_kbdint_getname(ssh_session session);
LIBSSH_API int ssh_userauth_kbdint_getnprompts(ssh_session session); LIBSSH_API int ssh_userauth_kbdint_getnprompts(ssh_session session);
LIBSSH_API const char *ssh_userauth_kbdint_getprompt(ssh_session session, unsigned int i, char *echo); LIBSSH_API const char *ssh_userauth_kbdint_getprompt(ssh_session session, unsigned int i, char *echo);
LIBSSH_API int ssh_userauth_kbdint_getnanswers(ssh_session session);
LIBSSH_API const char *ssh_userauth_kbdint_getanswer(ssh_session session, unsigned int i);
LIBSSH_API int ssh_userauth_kbdint_setanswer(ssh_session session, unsigned int i, LIBSSH_API int ssh_userauth_kbdint_setanswer(ssh_session session, unsigned int i,
const char *answer); const char *answer);
LIBSSH_API int ssh_userauth_list(ssh_session session, const char *username); LIBSSH_API int ssh_userauth_list(ssh_session session, const char *username);

View File

@@ -30,6 +30,7 @@ struct ssh_auth_request {
char *password; char *password;
struct ssh_public_key_struct *public_key; struct ssh_public_key_struct *public_key;
char signature_state; char signature_state;
char kbdint_response;
}; };
struct ssh_channel_request_open { struct ssh_channel_request_open {

View File

@@ -173,6 +173,7 @@ LIBSSH_API int ssh_message_reply_default(ssh_message msg);
LIBSSH_API char *ssh_message_auth_user(ssh_message msg); LIBSSH_API char *ssh_message_auth_user(ssh_message msg);
LIBSSH_API char *ssh_message_auth_password(ssh_message msg); LIBSSH_API char *ssh_message_auth_password(ssh_message msg);
LIBSSH_API ssh_public_key ssh_message_auth_publickey(ssh_message msg); LIBSSH_API ssh_public_key ssh_message_auth_publickey(ssh_message msg);
LIBSSH_API int ssh_message_auth_kbdint_is_response(ssh_message msg);
LIBSSH_API enum ssh_publickey_state_e ssh_message_auth_publickey_state(ssh_message msg); LIBSSH_API enum ssh_publickey_state_e ssh_message_auth_publickey_state(ssh_message msg);
LIBSSH_API int ssh_message_auth_reply_success(ssh_message msg,int partial); LIBSSH_API int ssh_message_auth_reply_success(ssh_message msg,int partial);
LIBSSH_API int ssh_message_auth_reply_pk_ok(ssh_message msg, ssh_string algo, ssh_string pubkey); LIBSSH_API int ssh_message_auth_reply_pk_ok(ssh_message msg, ssh_string algo, ssh_string pubkey);
@@ -180,6 +181,10 @@ LIBSSH_API int ssh_message_auth_reply_pk_ok_simple(ssh_message msg);
LIBSSH_API int ssh_message_auth_set_methods(ssh_message msg, int methods); LIBSSH_API int ssh_message_auth_set_methods(ssh_message msg, int methods);
LIBSSH_API int ssh_message_auth_interactive_request(ssh_message msg,
const char *name, const char *instruction,
unsigned int num_prompts, const char **prompts, char *echo);
LIBSSH_API int ssh_message_service_reply_success(ssh_message msg); LIBSSH_API int ssh_message_service_reply_success(ssh_message msg);
LIBSSH_API char *ssh_message_service_service(ssh_message msg); LIBSSH_API char *ssh_message_service_service(ssh_message msg);

View File

@@ -27,7 +27,6 @@
#include "libssh/auth.h" #include "libssh/auth.h"
#include "libssh/channels.h" #include "libssh/channels.h"
#include "libssh/poll.h" #include "libssh/poll.h"
typedef struct ssh_kbdint_struct* ssh_kbdint;
/* These are the different states a SSH session can be into its life */ /* These are the different states a SSH session can be into its life */
enum ssh_session_state_e { enum ssh_session_state_e {

View File

@@ -1275,16 +1275,7 @@ int ssh_userauth_autopubkey(ssh_session session, const char *passphrase) {
return SSH_AUTH_DENIED; return SSH_AUTH_DENIED;
} }
struct ssh_kbdint_struct { ssh_kbdint kbdint_new(void) {
uint32_t nprompts;
char *name;
char *instruction;
char **prompts;
unsigned char *echo; /* bool array */
char **answers;
};
static ssh_kbdint kbdint_new(void) {
ssh_kbdint kbd; ssh_kbdint kbd;
kbd = malloc(sizeof (struct ssh_kbdint_struct)); kbd = malloc(sizeof (struct ssh_kbdint_struct));
@@ -1297,19 +1288,19 @@ static ssh_kbdint kbdint_new(void) {
} }
static void kbdint_free(ssh_kbdint kbd) { void kbdint_free(ssh_kbdint kbd) {
int i, n; int i, n;
if (kbd == NULL) { if (kbd == NULL) {
return; return;
} }
n = kbd->nprompts;
SAFE_FREE(kbd->name); SAFE_FREE(kbd->name);
SAFE_FREE(kbd->instruction); SAFE_FREE(kbd->instruction);
SAFE_FREE(kbd->echo); SAFE_FREE(kbd->echo);
n = kbd->nprompts;
if (kbd->prompts) { if (kbd->prompts) {
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
BURN_STRING(kbd->prompts[i]); BURN_STRING(kbd->prompts[i]);
@@ -1317,6 +1308,8 @@ static void kbdint_free(ssh_kbdint kbd) {
} }
SAFE_FREE(kbd->prompts); SAFE_FREE(kbd->prompts);
} }
n = kbd->nanswers;
if (kbd->answers) { if (kbd->answers) {
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
BURN_STRING(kbd->answers[i]); BURN_STRING(kbd->answers[i]);
@@ -1328,19 +1321,18 @@ static void kbdint_free(ssh_kbdint kbd) {
SAFE_FREE(kbd); SAFE_FREE(kbd);
} }
static void kbdint_clean(ssh_kbdint kbd) { void kbdint_clean(ssh_kbdint kbd) {
int i, n; int i, n;
if (kbd == NULL) { if (kbd == NULL) {
return; return;
} }
n = kbd->nprompts;
SAFE_FREE(kbd->name); SAFE_FREE(kbd->name);
SAFE_FREE(kbd->instruction); SAFE_FREE(kbd->instruction);
SAFE_FREE(kbd->echo); SAFE_FREE(kbd->echo);
n = kbd->nprompts;
if (kbd->prompts) { if (kbd->prompts) {
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
BURN_STRING(kbd->prompts[i]); BURN_STRING(kbd->prompts[i]);
@@ -1349,6 +1341,8 @@ static void kbdint_clean(ssh_kbdint kbd) {
SAFE_FREE(kbd->prompts); SAFE_FREE(kbd->prompts);
} }
n = kbd->nanswers;
if (kbd->answers) { if (kbd->answers) {
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
BURN_STRING(kbd->answers[i]); BURN_STRING(kbd->answers[i]);
@@ -1358,6 +1352,7 @@ static void kbdint_clean(ssh_kbdint kbd) {
} }
kbd->nprompts = 0; kbd->nprompts = 0;
kbd->nanswers = 0;
} }
/* this function sends the first packet as explained in section 3.1 /* this function sends the first packet as explained in section 3.1
@@ -1535,6 +1530,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request) {
ssh_string_free(tmp); ssh_string_free(tmp);
if (session->kbdint->prompts[i] == NULL) { if (session->kbdint->prompts[i] == NULL) {
ssh_set_error_oom(session); ssh_set_error_oom(session);
session->kbdint->nprompts = i;
kbdint_free(session->kbdint); kbdint_free(session->kbdint);
session->kbdint = NULL; session->kbdint = NULL;
leave_function(); leave_function();
@@ -1757,6 +1753,40 @@ const char *ssh_userauth_kbdint_getprompt(ssh_session session, unsigned int i,
return session->kbdint->prompts[i]; return session->kbdint->prompts[i];
} }
#ifdef WITH_SERVER
/**
* @brief Get the number of answers the client has given.
*
* @param[in] session The ssh session to use.
*
* @returns The number of answers.
*/
int ssh_userauth_kbdint_getnanswers(ssh_session session) {
if(session==NULL || session->kbdint == NULL)
return SSH_ERROR;
return session->kbdint->nanswers;
}
/**
* @brief Get the answer for a question from a message block.
*
* @param[in] session The ssh session to use.
*
* @param[in] i index The number of the ith answer.
*
* @return 0 on success, < 0 on error.
*/
const char *ssh_userauth_kbdint_getanswer(ssh_session session, unsigned int i) {
if(session==NULL || session->kbdint == NULL)
return NULL;
if (i > session->kbdint->nanswers) {
return NULL;
}
return session->kbdint->answers[i];
}
#endif
/** /**
* @brief Set the answer for a question from a message block. * @brief Set the answer for a question from a message block.
* *

View File

@@ -336,6 +336,38 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){
goto end; goto end;
} }
if (strncmp(method_c, "keyboard-interactive", method_size) == 0) {
ssh_string lang = NULL;
ssh_string submethods = NULL;
msg->auth_request.method = SSH_AUTH_METHOD_INTERACTIVE;
SAFE_FREE(service_c);
SAFE_FREE(method_c);
lang = buffer_get_ssh_string(packet);
if (lang == NULL) {
goto error;
}
/* from the RFC 4256
* 3.1. Initial Exchange
* "The language tag is deprecated and SHOULD be the empty string."
*/
ssh_string_free(lang);
submethods = buffer_get_ssh_string(packet);
if (submethods == NULL) {
goto error;
}
/* from the RFC 4256
* 3.1. Initial Exchange
* "One possible implementation strategy of the submethods field on the
* server is that, unless the user may use multiple different
* submethods, the server ignores this field."
*/
ssh_string_free(submethods);
goto end;
}
if (strncmp(method_c, "publickey", method_size) == 0) { if (strncmp(method_c, "publickey", method_size) == 0) {
ssh_string algo = NULL; ssh_string algo = NULL;
ssh_string publickey = NULL; ssh_string publickey = NULL;
@@ -432,6 +464,129 @@ end:
return SSH_PACKET_USED; return SSH_PACKET_USED;
} }
/**
* @internal
*
* @brief Handle a SSH_MSG_MSG_USERAUTH_INFO_RESPONSE packet and queue a
* SSH Message
*/
#ifndef WITH_SERVER
SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response){
(void)session;
(void)type;
(void)packet;
(void)user;
return SSH_PACKET_USED;
}
#else
SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response){
uint32_t nanswers;
uint32_t i;
ssh_string tmp;
ssh_message msg = NULL;
enter_function();
(void)user;
(void)type;
msg = ssh_message_new(session);
if (msg == NULL) {
ssh_set_error_oom(session);
goto error;
}
/* HACK: we forge a message to be able to handle it in the
* same switch() as other auth methods */
msg->type = SSH_REQUEST_AUTH;
msg->auth_request.method = SSH_AUTH_METHOD_INTERACTIVE;
msg->auth_request.kbdint_response = 1;
#if 0 // should we wipe the username ?
msg->auth_request.username = NULL;
#endif
buffer_get_u32(packet, &nanswers);
if (session->kbdint == NULL) {
ssh_log(session, SSH_LOG_PROTOCOL, "Warning: Got a keyboard-interactive "
"response but it seems we didn't send the request.");
session->kbdint = kbdint_new();
if (session->kbdint == NULL) {
ssh_set_error_oom(session);
leave_function();
return SSH_PACKET_USED;
}
}
nanswers = ntohl(nanswers);
ssh_log(session,SSH_LOG_PACKET,"kbdint: %d answers",nanswers);
if (nanswers > KBDINT_MAX_PROMPT) {
ssh_set_error(session, SSH_FATAL,
"Too much answers received from client: %u (0x%.4x)",
nanswers, nanswers);
kbdint_free(session->kbdint);
session->kbdint = NULL;
leave_function();
return SSH_PACKET_USED;
}
if(nanswers != session->kbdint->nprompts) {
/* warn but let the application handle this case */
ssh_log(session, SSH_LOG_PROTOCOL, "Warning: Number of prompts and answers"
" mismatch: p=%u a=%u", session->kbdint->nprompts, nanswers);
}
session->kbdint->nanswers = nanswers;
session->kbdint->answers = malloc(nanswers * sizeof(char *));
if (session->kbdint->answers == NULL) {
session->kbdint->nanswers = 0;
ssh_set_error_oom(session);
kbdint_free(session->kbdint);
session->kbdint = NULL;
leave_function();
return SSH_PACKET_USED;
}
memset(session->kbdint->answers, 0, nanswers * sizeof(char *));
for (i = 0; i < nanswers; i++) {
tmp = buffer_get_ssh_string(packet);
if (tmp == NULL) {
ssh_set_error(session, SSH_FATAL, "Short INFO_RESPONSE packet");
session->kbdint->nanswers = i;
kbdint_free(session->kbdint);
session->kbdint = NULL;
leave_function();
return SSH_PACKET_USED;
}
session->kbdint->answers[i] = ssh_string_to_char(tmp);
ssh_string_free(tmp);
if (session->kbdint->answers[i] == NULL) {
ssh_set_error_oom(session);
session->kbdint->nanswers = i;
kbdint_free(session->kbdint);
session->kbdint = NULL;
leave_function();
return SSH_PACKET_USED;
}
}
goto end;
error:
ssh_message_free(msg);
leave_function();
return SSH_PACKET_USED;
end:
ssh_message_queue(session,msg);
leave_function();
return SSH_PACKET_USED;
}
#endif
SSH_PACKET_CALLBACK(ssh_packet_channel_open){ SSH_PACKET_CALLBACK(ssh_packet_channel_open){
ssh_message msg = NULL; ssh_message msg = NULL;
ssh_string type_s = NULL, originator = NULL, destination = NULL; ssh_string type_s = NULL, originator = NULL, destination = NULL;

View File

@@ -81,7 +81,7 @@ ssh_packet_callback default_packet_handlers[]= {
ssh_packet_userauth_pk_ok, // SSH2_MSG_USERAUTH_PK_OK 60 ssh_packet_userauth_pk_ok, // SSH2_MSG_USERAUTH_PK_OK 60
// SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60 // SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60
// SSH2_MSG_USERAUTH_INFO_REQUEST 60 // SSH2_MSG_USERAUTH_INFO_REQUEST 60
NULL, // SSH2_MSG_USERAUTH_INFO_RESPONSE 61 ssh_packet_userauth_info_response, // SSH2_MSG_USERAUTH_INFO_RESPONSE 61
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, // 62-79 NULL, NULL, NULL, NULL, // 62-79

View File

@@ -747,6 +747,14 @@ enum ssh_publickey_state_e ssh_message_auth_publickey_state(ssh_message msg){
return msg->auth_request.signature_state; return msg->auth_request.signature_state;
} }
int ssh_message_auth_kbdint_is_response(ssh_message msg) {
if (msg == NULL) {
return -1;
}
return msg->auth_request.kbdint_response != 0;
}
int ssh_message_auth_set_methods(ssh_message msg, int methods) { int ssh_message_auth_set_methods(ssh_message msg, int methods) {
if (msg == NULL || msg->session == NULL) { if (msg == NULL || msg->session == NULL) {
return -1; return -1;
@@ -757,6 +765,131 @@ int ssh_message_auth_set_methods(ssh_message msg, int methods) {
return 0; return 0;
} }
int ssh_message_auth_interactive_request(ssh_message msg, const char *name,
const char *instruction, unsigned int num_prompts,
const char **prompts, char *echo) {
int r;
unsigned int i = 0;
ssh_string tmp = NULL;
if(name == NULL || instruction == NULL) {
return SSH_ERROR;
}
if(num_prompts > 0 && (prompts == NULL || echo == NULL)) {
return SSH_ERROR;
}
if (buffer_add_u8(msg->session->out_buffer, SSH2_MSG_USERAUTH_INFO_REQUEST) < 0) {
return SSH_ERROR;
}
/* name */
tmp = ssh_string_from_char(name);
if (buffer_add_ssh_string(msg->session->out_buffer, tmp) < 0) {
return SSH_ERROR;
}
ssh_string_free(tmp); tmp = NULL;
/* instruction */
tmp = ssh_string_from_char(instruction);
if (buffer_add_ssh_string(msg->session->out_buffer, tmp) < 0) {
return SSH_ERROR;
}
ssh_string_free(tmp); tmp = NULL;
/* language tag */
tmp = ssh_string_from_char("");
if (buffer_add_ssh_string(msg->session->out_buffer, tmp) < 0) {
return SSH_ERROR;
}
ssh_string_free(tmp); tmp = NULL;
/* num prompts */
if (buffer_add_u32(msg->session->out_buffer, ntohl(num_prompts)) < 0) {
return SSH_ERROR;
}
for(i = 0; i < num_prompts; i++) {
/* prompt[i] */
tmp = ssh_string_from_char(prompts[i]);
if (buffer_add_ssh_string(msg->session->out_buffer, tmp) < 0) {
goto error;
}
ssh_string_free(tmp); tmp = NULL;
/* echo[i] */
if (buffer_add_u8(msg->session->out_buffer, echo[i]) < 0) {
return SSH_ERROR;
}
}
r = packet_send(msg->session);
/* fill in the kbdint structure */
if (msg->session->kbdint == NULL) {
ssh_log(msg->session, SSH_LOG_PROTOCOL, "Warning: Got a "
"keyboard-interactive response but it "
"seems we didn't send the request.");
msg->session->kbdint = kbdint_new();
if (msg->session->kbdint == NULL) {
ssh_set_error_oom(msg->session);
return SSH_ERROR;
}
} else {
kbdint_clean(msg->session->kbdint);
}
msg->session->kbdint->name = strdup(name);
if(msg->session->kbdint->name == NULL) {
ssh_set_error_oom(msg->session);
kbdint_free(msg->session->kbdint);
msg->session->kbdint = NULL;
return SSH_PACKET_USED;
}
msg->session->kbdint->instruction = strdup(instruction);
if(msg->session->kbdint->instruction == NULL) {
ssh_set_error_oom(msg->session);
kbdint_free(msg->session->kbdint);
msg->session->kbdint = NULL;
return SSH_PACKET_USED;
}
msg->session->kbdint->nprompts = num_prompts;
msg->session->kbdint->prompts = malloc(num_prompts * sizeof(char *));
if (msg->session->kbdint->prompts == NULL) {
msg->session->kbdint->nprompts = 0;
ssh_set_error_oom(msg->session);
kbdint_free(msg->session->kbdint);
msg->session->kbdint = NULL;
return SSH_ERROR;
}
msg->session->kbdint->echo = malloc(num_prompts * sizeof(char));
if (msg->session->kbdint->echo == NULL) {
ssh_set_error_oom(msg->session);
kbdint_free(msg->session->kbdint);
msg->session->kbdint = NULL;
return SSH_ERROR;
}
for (i = 0; i < num_prompts; i++) {
msg->session->kbdint->echo[i] = echo[i];
msg->session->kbdint->prompts[i] = strdup(prompts[i]);
if (msg->session->kbdint->prompts[i] == NULL) {
ssh_set_error_oom(msg->session);
msg->session->kbdint->nprompts = i;
kbdint_free(msg->session->kbdint);
msg->session->kbdint = NULL;
return SSH_PACKET_USED;
}
}
return r;
error:
if(tmp) ssh_string_free(tmp);
return SSH_ERROR;
}
int ssh_message_auth_reply_success(ssh_message msg, int partial) { int ssh_message_auth_reply_success(ssh_message msg, int partial) {
int r; int r;