Added the keyboard-interactive authentication method
Этот коммит содержится в:
родитель
32e23a25da
Коммит
3c0a4781e4
@ -29,6 +29,27 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_failure);
|
||||
SSH_PACKET_CALLBACK(ssh_packet_userauth_success);
|
||||
SSH_PACKET_CALLBACK(ssh_packet_userauth_pk_ok);
|
||||
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
|
||||
void ssh_auth1_handler(ssh_session session, uint8_t type);
|
||||
|
@ -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 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 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,
|
||||
const char *answer);
|
||||
LIBSSH_API int ssh_userauth_list(ssh_session session, const char *username);
|
||||
|
@ -30,6 +30,7 @@ struct ssh_auth_request {
|
||||
char *password;
|
||||
struct ssh_public_key_struct *public_key;
|
||||
char signature_state;
|
||||
char kbdint_response;
|
||||
};
|
||||
|
||||
struct ssh_channel_request_open {
|
||||
|
@ -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_password(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 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);
|
||||
@ -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_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 char *ssh_message_service_service(ssh_message msg);
|
||||
|
||||
|
@ -27,7 +27,6 @@
|
||||
#include "libssh/auth.h"
|
||||
#include "libssh/channels.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 */
|
||||
enum ssh_session_state_e {
|
||||
|
60
src/auth.c
60
src/auth.c
@ -1275,16 +1275,7 @@ int ssh_userauth_autopubkey(ssh_session session, const char *passphrase) {
|
||||
return SSH_AUTH_DENIED;
|
||||
}
|
||||
|
||||
struct ssh_kbdint_struct {
|
||||
uint32_t nprompts;
|
||||
char *name;
|
||||
char *instruction;
|
||||
char **prompts;
|
||||
unsigned char *echo; /* bool array */
|
||||
char **answers;
|
||||
};
|
||||
|
||||
static ssh_kbdint kbdint_new(void) {
|
||||
ssh_kbdint kbdint_new(void) {
|
||||
ssh_kbdint kbd;
|
||||
|
||||
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;
|
||||
|
||||
if (kbd == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
n = kbd->nprompts;
|
||||
|
||||
SAFE_FREE(kbd->name);
|
||||
SAFE_FREE(kbd->instruction);
|
||||
SAFE_FREE(kbd->echo);
|
||||
|
||||
n = kbd->nprompts;
|
||||
if (kbd->prompts) {
|
||||
for (i = 0; i < n; i++) {
|
||||
BURN_STRING(kbd->prompts[i]);
|
||||
@ -1317,6 +1308,8 @@ static void kbdint_free(ssh_kbdint kbd) {
|
||||
}
|
||||
SAFE_FREE(kbd->prompts);
|
||||
}
|
||||
|
||||
n = kbd->nanswers;
|
||||
if (kbd->answers) {
|
||||
for (i = 0; i < n; i++) {
|
||||
BURN_STRING(kbd->answers[i]);
|
||||
@ -1328,19 +1321,18 @@ static void kbdint_free(ssh_kbdint kbd) {
|
||||
SAFE_FREE(kbd);
|
||||
}
|
||||
|
||||
static void kbdint_clean(ssh_kbdint kbd) {
|
||||
void kbdint_clean(ssh_kbdint kbd) {
|
||||
int i, n;
|
||||
|
||||
if (kbd == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
n = kbd->nprompts;
|
||||
|
||||
SAFE_FREE(kbd->name);
|
||||
SAFE_FREE(kbd->instruction);
|
||||
SAFE_FREE(kbd->echo);
|
||||
|
||||
n = kbd->nprompts;
|
||||
if (kbd->prompts) {
|
||||
for (i = 0; i < n; i++) {
|
||||
BURN_STRING(kbd->prompts[i]);
|
||||
@ -1349,6 +1341,8 @@ static void kbdint_clean(ssh_kbdint kbd) {
|
||||
SAFE_FREE(kbd->prompts);
|
||||
}
|
||||
|
||||
n = kbd->nanswers;
|
||||
|
||||
if (kbd->answers) {
|
||||
for (i = 0; i < n; i++) {
|
||||
BURN_STRING(kbd->answers[i]);
|
||||
@ -1358,6 +1352,7 @@ static void kbdint_clean(ssh_kbdint kbd) {
|
||||
}
|
||||
|
||||
kbd->nprompts = 0;
|
||||
kbd->nanswers = 0;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
if (session->kbdint->prompts[i] == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
session->kbdint->nprompts = i;
|
||||
kbdint_free(session->kbdint);
|
||||
session->kbdint = NULL;
|
||||
leave_function();
|
||||
@ -1757,6 +1753,40 @@ const char *ssh_userauth_kbdint_getprompt(ssh_session session, unsigned int 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.
|
||||
*
|
||||
|
155
src/messages.c
155
src/messages.c
@ -336,6 +336,38 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){
|
||||
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) {
|
||||
ssh_string algo = NULL;
|
||||
ssh_string publickey = NULL;
|
||||
@ -432,6 +464,129 @@ end:
|
||||
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_message msg = NULL;
|
||||
ssh_string type_s = NULL, originator = NULL, destination = NULL;
|
||||
|
@ -81,7 +81,7 @@ ssh_packet_callback default_packet_handlers[]= {
|
||||
ssh_packet_userauth_pk_ok, // SSH2_MSG_USERAUTH_PK_OK 60
|
||||
// SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 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, // 62-79
|
||||
|
133
src/server.c
133
src/server.c
@ -747,6 +747,14 @@ enum ssh_publickey_state_e ssh_message_auth_publickey_state(ssh_message msg){
|
||||
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) {
|
||||
if (msg == NULL || msg->session == NULL) {
|
||||
return -1;
|
||||
@ -757,6 +765,131 @@ int ssh_message_auth_set_methods(ssh_message msg, int methods) {
|
||||
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 r;
|
||||
|
||||
|
Загрузка…
x
Ссылка в новой задаче
Block a user