GUAC-1171: Migrate to common SSH within client.
This commit is contained in:
parent
98503a9fb7
commit
0fcea2738b
@ -30,18 +30,14 @@ libguac_client_ssh_la_SOURCES = \
|
|||||||
clipboard.c \
|
clipboard.c \
|
||||||
guac_handlers.c \
|
guac_handlers.c \
|
||||||
sftp.c \
|
sftp.c \
|
||||||
ssh_buffer.c \
|
ssh_client.c
|
||||||
ssh_client.c \
|
|
||||||
ssh_key.c
|
|
||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
client.h \
|
client.h \
|
||||||
clipboard.h \
|
clipboard.h \
|
||||||
guac_handlers.h \
|
guac_handlers.h \
|
||||||
sftp.h \
|
sftp.h \
|
||||||
ssh_buffer.h \
|
ssh_client.h
|
||||||
ssh_client.h \
|
|
||||||
ssh_key.h
|
|
||||||
|
|
||||||
# Add agent sources if enabled
|
# Add agent sources if enabled
|
||||||
if ENABLE_SSH_AGENT
|
if ENABLE_SSH_AGENT
|
||||||
|
@ -142,7 +142,6 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
|
|||||||
strcpy(client_data->password, argv[IDX_PASSWORD]);
|
strcpy(client_data->password, argv[IDX_PASSWORD]);
|
||||||
|
|
||||||
/* Init public key auth information */
|
/* Init public key auth information */
|
||||||
client_data->key = NULL;
|
|
||||||
strcpy(client_data->key_base64, argv[IDX_PRIVATE_KEY]);
|
strcpy(client_data->key_base64, argv[IDX_PRIVATE_KEY]);
|
||||||
strcpy(client_data->key_passphrase, argv[IDX_PASSPHRASE]);
|
strcpy(client_data->key_passphrase, argv[IDX_PASSPHRASE]);
|
||||||
|
|
||||||
|
@ -26,8 +26,8 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "guac_ssh.h"
|
||||||
#include "sftp.h"
|
#include "sftp.h"
|
||||||
#include "ssh_key.h"
|
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
|
|
||||||
#include <libssh2.h>
|
#include <libssh2.h>
|
||||||
@ -77,11 +77,6 @@ typedef struct ssh_guac_client_data {
|
|||||||
*/
|
*/
|
||||||
char key_passphrase[1024];
|
char key_passphrase[1024];
|
||||||
|
|
||||||
/**
|
|
||||||
* The private key to use for authentication, if any.
|
|
||||||
*/
|
|
||||||
ssh_key* key;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of the font to use for display rendering.
|
* The name of the font to use for display rendering.
|
||||||
*/
|
*/
|
||||||
@ -117,12 +112,12 @@ typedef struct ssh_guac_client_data {
|
|||||||
/**
|
/**
|
||||||
* SSH session, used by the SSH client thread.
|
* SSH session, used by the SSH client thread.
|
||||||
*/
|
*/
|
||||||
LIBSSH2_SESSION* session;
|
guac_common_ssh_session* session;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The distinct SSH session used for SFTP.
|
* The distinct SSH session used for SFTP.
|
||||||
*/
|
*/
|
||||||
LIBSSH2_SESSION* sftp_ssh_session;
|
guac_common_ssh_session* sftp_ssh_session;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SFTP session, used for file transfers.
|
* SFTP session, used for file transfers.
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "guac_handlers.h"
|
#include "guac_handlers.h"
|
||||||
#include "ssh_key.h"
|
#include "guac_ssh.h"
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
|
|
||||||
#include <cairo/cairo.h>
|
#include <cairo/cairo.h>
|
||||||
@ -106,10 +106,8 @@ int ssh_guac_client_free_handler(guac_client* client) {
|
|||||||
libssh2_sftp_shutdown(guac_client_data->sftp_session);
|
libssh2_sftp_shutdown(guac_client_data->sftp_session);
|
||||||
|
|
||||||
/* Disconnect SSH session corresponding to the SFTP session */
|
/* Disconnect SSH session corresponding to the SFTP session */
|
||||||
if (guac_client_data->sftp_ssh_session) {
|
if (guac_client_data->sftp_ssh_session)
|
||||||
libssh2_session_disconnect(guac_client_data->sftp_ssh_session, "Bye");
|
guac_common_ssh_destroy_session(guac_client_data->sftp_ssh_session);
|
||||||
libssh2_session_free(guac_client_data->sftp_ssh_session);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clean up the SFTP filesystem object */
|
/* Clean up the SFTP filesystem object */
|
||||||
if (guac_client_data->sftp_filesystem)
|
if (guac_client_data->sftp_filesystem)
|
||||||
@ -117,15 +115,12 @@ int ssh_guac_client_free_handler(guac_client* client) {
|
|||||||
|
|
||||||
/* Free session */
|
/* Free session */
|
||||||
if (guac_client_data->session != NULL)
|
if (guac_client_data->session != NULL)
|
||||||
libssh2_session_free(guac_client_data->session);
|
guac_common_ssh_destroy_session(guac_client_data->session);
|
||||||
|
|
||||||
/* Free auth key */
|
|
||||||
if (guac_client_data->key != NULL)
|
|
||||||
ssh_key_free(guac_client_data->key);
|
|
||||||
|
|
||||||
/* Free generic data struct */
|
/* Free generic data struct */
|
||||||
free(client->data);
|
free(client->data);
|
||||||
|
|
||||||
|
guac_common_ssh_uninit();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,136 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013 Glyptodon LLC
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <openssl/bn.h>
|
|
||||||
#include <openssl/ossl_typ.h>
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
void buffer_write_byte(char** buffer, uint8_t value) {
|
|
||||||
|
|
||||||
uint8_t* data = (uint8_t*) *buffer;
|
|
||||||
*data = value;
|
|
||||||
|
|
||||||
(*buffer)++;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void buffer_write_uint32(char** buffer, uint32_t value) {
|
|
||||||
|
|
||||||
uint8_t* data = (uint8_t*) *buffer;
|
|
||||||
|
|
||||||
data[0] = (value & 0xFF000000) >> 24;
|
|
||||||
data[1] = (value & 0x00FF0000) >> 16;
|
|
||||||
data[2] = (value & 0x0000FF00) >> 8;
|
|
||||||
data[3] = value & 0x000000FF;
|
|
||||||
|
|
||||||
*buffer += 4;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void buffer_write_data(char** buffer, const char* data, int length) {
|
|
||||||
memcpy(*buffer, data, length);
|
|
||||||
*buffer += length;
|
|
||||||
}
|
|
||||||
|
|
||||||
void buffer_write_bignum(char** buffer, BIGNUM* value) {
|
|
||||||
|
|
||||||
unsigned char* bn_buffer;
|
|
||||||
int length;
|
|
||||||
|
|
||||||
/* If zero, just write zero length */
|
|
||||||
if (BN_is_zero(value)) {
|
|
||||||
buffer_write_uint32(buffer, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate output buffer, add padding byte */
|
|
||||||
length = BN_num_bytes(value);
|
|
||||||
bn_buffer = malloc(length);
|
|
||||||
|
|
||||||
/* Convert BIGNUM */
|
|
||||||
BN_bn2bin(value, bn_buffer);
|
|
||||||
|
|
||||||
/* If first byte has high bit set, write padding byte */
|
|
||||||
if (bn_buffer[0] & 0x80) {
|
|
||||||
buffer_write_uint32(buffer, length+1);
|
|
||||||
buffer_write_byte(buffer, 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
buffer_write_uint32(buffer, length);
|
|
||||||
|
|
||||||
/* Write data */
|
|
||||||
memcpy(*buffer, bn_buffer, length);
|
|
||||||
*buffer += length;
|
|
||||||
|
|
||||||
free(bn_buffer);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void buffer_write_string(char** buffer, const char* string, int length) {
|
|
||||||
buffer_write_uint32(buffer, length);
|
|
||||||
buffer_write_data(buffer, string, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t buffer_read_byte(char** buffer) {
|
|
||||||
|
|
||||||
uint8_t* data = (uint8_t*) *buffer;
|
|
||||||
uint8_t value = *data;
|
|
||||||
|
|
||||||
(*buffer)++;
|
|
||||||
|
|
||||||
return value;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t buffer_read_uint32(char** buffer) {
|
|
||||||
|
|
||||||
uint8_t* data = (uint8_t*) *buffer;
|
|
||||||
uint32_t value =
|
|
||||||
(data[0] << 24)
|
|
||||||
| (data[1] << 16)
|
|
||||||
| (data[2] << 8)
|
|
||||||
| data[3];
|
|
||||||
|
|
||||||
*buffer += 4;
|
|
||||||
|
|
||||||
return value;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
char* buffer_read_string(char** buffer, int* length) {
|
|
||||||
|
|
||||||
char* value;
|
|
||||||
|
|
||||||
*length = buffer_read_uint32(buffer);
|
|
||||||
value = *buffer;
|
|
||||||
|
|
||||||
*buffer += *length;
|
|
||||||
|
|
||||||
return value;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013 Glyptodon LLC
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef _GUAC_SSH_BUFFER_H
|
|
||||||
#define _GUAC_SSH_BUFFER_H
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <openssl/bn.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the given byte to the given buffer, advancing the buffer pointer by
|
|
||||||
* one byte.
|
|
||||||
*/
|
|
||||||
void buffer_write_byte(char** buffer, uint8_t value);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the given integer to the given buffer, advancing the buffer pointer
|
|
||||||
* four bytes.
|
|
||||||
*/
|
|
||||||
void buffer_write_uint32(char** buffer, uint32_t value);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the given string and its length to the given buffer, advancing the
|
|
||||||
* buffer pointer by the size of the length (four bytes) and the size of the
|
|
||||||
* string.
|
|
||||||
*/
|
|
||||||
void buffer_write_string(char** buffer, const char* string, int length);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the given BIGNUM the given buffer, advancing the buffer pointer by
|
|
||||||
* the size of the length (four bytes) and the size of the BIGNUM.
|
|
||||||
*/
|
|
||||||
void buffer_write_bignum(char** buffer, BIGNUM* value);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the given data the given buffer, advancing the buffer pointer by the
|
|
||||||
* given length.
|
|
||||||
*/
|
|
||||||
void buffer_write_data(char** buffer, const char* data, int length);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads a single byte from the given buffer, advancing the buffer by one byte.
|
|
||||||
*/
|
|
||||||
uint8_t buffer_read_byte(char** buffer);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads an integer from the given buffer, advancing the buffer by four bytes.
|
|
||||||
*/
|
|
||||||
uint32_t buffer_read_uint32(char** buffer);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads a string and its length from the given buffer, advancing the buffer
|
|
||||||
* by the size of the length (four bytes) and the size of the string, and
|
|
||||||
* returning a pointer to the buffer. The length of the string is stored in
|
|
||||||
* the given int.
|
|
||||||
*/
|
|
||||||
char* buffer_read_string(char** buffer, int* length);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -25,7 +25,6 @@
|
|||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "guac_ssh.h"
|
#include "guac_ssh.h"
|
||||||
#include "sftp.h"
|
#include "sftp.h"
|
||||||
#include "ssh_key.h"
|
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
|
|
||||||
#ifdef ENABLE_SSH_AGENT
|
#ifdef ENABLE_SSH_AGENT
|
||||||
@ -57,6 +56,96 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produces a new user object containing a username and password or private
|
||||||
|
* key, prompting the user as necessary to obtain that information.
|
||||||
|
*
|
||||||
|
* @param client
|
||||||
|
* The Guacamole client containing any existing user data, as well as the
|
||||||
|
* terminal to use when prompting the user.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A new user object containing the user's username and other credentials.
|
||||||
|
*/
|
||||||
|
static guac_common_ssh_user* guac_ssh_get_user(guac_client* client) {
|
||||||
|
|
||||||
|
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
|
||||||
|
|
||||||
|
guac_common_ssh_user* user;
|
||||||
|
|
||||||
|
/* Get username */
|
||||||
|
if (client_data->username[0] == 0)
|
||||||
|
guac_terminal_prompt(client_data->term, "Login as: ",
|
||||||
|
client_data->username, sizeof(client_data->username), true);
|
||||||
|
|
||||||
|
/* Create user object from username */
|
||||||
|
user = guac_common_ssh_create_user(client_data->username);
|
||||||
|
|
||||||
|
/* If key specified, import */
|
||||||
|
if (client_data->key_base64[0] != 0) {
|
||||||
|
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||||
|
"Attempting private key import (WITHOUT passphrase)");
|
||||||
|
|
||||||
|
/* Attempt to read key without passphrase */
|
||||||
|
if (guac_common_ssh_user_import_key(user,
|
||||||
|
client_data->key_base64, NULL)) {
|
||||||
|
|
||||||
|
/* Log failure of initial attempt */
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||||
|
"Initial import failed: %s",
|
||||||
|
guac_common_ssh_key_error());
|
||||||
|
|
||||||
|
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||||
|
"Re-attempting private key import (WITH passphrase)");
|
||||||
|
|
||||||
|
/* Prompt for passphrase if missing */
|
||||||
|
if (client_data->key_passphrase[0] == 0)
|
||||||
|
guac_terminal_prompt(client_data->term, "Key passphrase: ",
|
||||||
|
client_data->key_passphrase,
|
||||||
|
sizeof(client_data->key_passphrase), false);
|
||||||
|
|
||||||
|
/* Reattempt import with passphrase */
|
||||||
|
if (guac_common_ssh_user_import_key(user,
|
||||||
|
client_data->key_base64,
|
||||||
|
client_data->key_passphrase)) {
|
||||||
|
|
||||||
|
/* If still failing, give up */
|
||||||
|
guac_client_abort(client,
|
||||||
|
GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
|
||||||
|
"Auth key import failed: %s",
|
||||||
|
guac_common_ssh_key_error());
|
||||||
|
|
||||||
|
guac_common_ssh_destroy_user(user);
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end decrypt key with passphrase */
|
||||||
|
|
||||||
|
/* Success */
|
||||||
|
guac_client_log(client, GUAC_LOG_INFO,
|
||||||
|
"Auth key successfully imported.");
|
||||||
|
|
||||||
|
} /* end if key given */
|
||||||
|
|
||||||
|
/* Otherwise, get password if not provided */
|
||||||
|
else if (client_data->password[0] == 0) {
|
||||||
|
|
||||||
|
guac_terminal_prompt(client_data->term, "Password: ",
|
||||||
|
client_data->password, sizeof(client_data->password), false);
|
||||||
|
|
||||||
|
guac_common_ssh_user_set_password(user, client_data->password);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear screen of any prompts */
|
||||||
|
guac_terminal_printf(client_data->term, "\x1B[H\x1B[J");
|
||||||
|
|
||||||
|
return user;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void* ssh_input_thread(void* data) {
|
void* ssh_input_thread(void* data) {
|
||||||
|
|
||||||
guac_client* client = (guac_client*) data;
|
guac_client* client = (guac_client*) data;
|
||||||
@ -76,213 +165,13 @@ void* ssh_input_thread(void* data) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __sign_callback(LIBSSH2_SESSION* session,
|
|
||||||
unsigned char** sig, size_t* sig_len,
|
|
||||||
const unsigned char* data, size_t data_len, void **abstract) {
|
|
||||||
|
|
||||||
ssh_key* key = (ssh_key*) abstract;
|
|
||||||
int length;
|
|
||||||
|
|
||||||
/* Allocate space for signature */
|
|
||||||
*sig = malloc(4096);
|
|
||||||
|
|
||||||
/* Sign with key */
|
|
||||||
length = ssh_key_sign(key, (const char*) data, data_len, *sig);
|
|
||||||
if (length < 0)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
*sig_len = length;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback for the keyboard-interactive authentication method. Currently
|
|
||||||
* suports just one prompt for the password.
|
|
||||||
*/
|
|
||||||
static void __kbd_callback(const char *name, int name_len,
|
|
||||||
const char *instruction, int instruction_len,
|
|
||||||
int num_prompts,
|
|
||||||
const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts,
|
|
||||||
LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses,
|
|
||||||
void **abstract) {
|
|
||||||
|
|
||||||
guac_client* client = (guac_client*) *abstract;
|
|
||||||
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
|
|
||||||
|
|
||||||
if (num_prompts == 1) {
|
|
||||||
responses[0].text = strdup(client_data->password);
|
|
||||||
responses[0].length = strlen(client_data->password);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
guac_client_log(client, GUAC_LOG_WARNING,
|
|
||||||
"Unsupported number of keyboard-interactive prompts: %i",
|
|
||||||
num_prompts);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static LIBSSH2_SESSION* __guac_ssh_create_session(guac_client* client,
|
|
||||||
int* socket_fd) {
|
|
||||||
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
int fd;
|
|
||||||
struct addrinfo* addresses;
|
|
||||||
struct addrinfo* current_address;
|
|
||||||
|
|
||||||
char connected_address[1024];
|
|
||||||
char connected_port[64];
|
|
||||||
char *user_authlist;
|
|
||||||
|
|
||||||
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
|
|
||||||
|
|
||||||
struct addrinfo hints = {
|
|
||||||
.ai_family = AF_UNSPEC,
|
|
||||||
.ai_socktype = SOCK_STREAM,
|
|
||||||
.ai_protocol = IPPROTO_TCP
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Get socket */
|
|
||||||
fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
|
|
||||||
/* Get addresses connection */
|
|
||||||
if ((retval = getaddrinfo(client_data->hostname, client_data->port,
|
|
||||||
&hints, &addresses))) {
|
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Error parsing given address or port: %s",
|
|
||||||
gai_strerror(retval));
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Attempt connection to each address until success */
|
|
||||||
current_address = addresses;
|
|
||||||
while (current_address != NULL) {
|
|
||||||
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
/* Resolve hostname */
|
|
||||||
if ((retval = getnameinfo(current_address->ai_addr,
|
|
||||||
current_address->ai_addrlen,
|
|
||||||
connected_address, sizeof(connected_address),
|
|
||||||
connected_port, sizeof(connected_port),
|
|
||||||
NI_NUMERICHOST | NI_NUMERICSERV)))
|
|
||||||
guac_client_log(client, GUAC_LOG_DEBUG, "Unable to resolve host: %s", gai_strerror(retval));
|
|
||||||
|
|
||||||
/* Connect */
|
|
||||||
if (connect(fd, current_address->ai_addr,
|
|
||||||
current_address->ai_addrlen) == 0) {
|
|
||||||
|
|
||||||
guac_client_log(client, GUAC_LOG_DEBUG, "Successfully connected to "
|
|
||||||
"host %s, port %s", connected_address, connected_port);
|
|
||||||
|
|
||||||
/* Done if successful connect */
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Otherwise log information regarding bind failure */
|
|
||||||
else
|
|
||||||
guac_client_log(client, GUAC_LOG_DEBUG, "Unable to connect to "
|
|
||||||
"host %s, port %s: %s",
|
|
||||||
connected_address, connected_port, strerror(errno));
|
|
||||||
|
|
||||||
current_address = current_address->ai_next;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If unable to connect to anything, fail */
|
|
||||||
if (current_address == NULL) {
|
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Unable to connect to any addresses.");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Free addrinfo */
|
|
||||||
freeaddrinfo(addresses);
|
|
||||||
|
|
||||||
/* Open SSH session */
|
|
||||||
LIBSSH2_SESSION* session = libssh2_session_init_ex(NULL, NULL,
|
|
||||||
NULL, client);
|
|
||||||
if (session == NULL) {
|
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Session allocation failed.");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Perform handshake */
|
|
||||||
if (libssh2_session_handshake(session, fd)) {
|
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "SSH handshake failed.");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Save file descriptor */
|
|
||||||
if (socket_fd != NULL)
|
|
||||||
*socket_fd = fd;
|
|
||||||
|
|
||||||
/* Get list of suported authentication methods */
|
|
||||||
user_authlist = libssh2_userauth_list(session, client_data->username, strlen(client_data->username));
|
|
||||||
guac_client_log(client, GUAC_LOG_DEBUG, "Supported authentication methods: %s", user_authlist);
|
|
||||||
|
|
||||||
/* Authenticate with key if available */
|
|
||||||
if (client_data->key != NULL) {
|
|
||||||
|
|
||||||
/* Check if public key auth is suported on the server */
|
|
||||||
if (strstr(user_authlist, "publickey") == NULL) {
|
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
|
|
||||||
"Public key authentication not suported");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!libssh2_userauth_publickey(session, client_data->username,
|
|
||||||
(unsigned char*) client_data->key->public_key,
|
|
||||||
client_data->key->public_key_length,
|
|
||||||
__sign_callback, (void**) client_data->key))
|
|
||||||
return session;
|
|
||||||
else {
|
|
||||||
char* error_message;
|
|
||||||
libssh2_session_last_error(session, &error_message, NULL, 0);
|
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
|
|
||||||
"Public key authentication failed: %s", error_message);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Authenticate with password */
|
|
||||||
if (strstr(user_authlist, "password") != NULL) {
|
|
||||||
guac_client_log(client, GUAC_LOG_DEBUG, "Using password authentication method");
|
|
||||||
retval = libssh2_userauth_password(session, client_data->username, client_data->password);
|
|
||||||
}
|
|
||||||
else if (strstr(user_authlist, "keyboard-interactive") != NULL) {
|
|
||||||
guac_client_log(client, GUAC_LOG_DEBUG, "Using keyboard-interactive authentication method");
|
|
||||||
retval = libssh2_userauth_keyboard_interactive(session, client_data->username, &__kbd_callback);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_BAD_TYPE, "No known authentication methods");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (retval == 0)
|
|
||||||
return session;
|
|
||||||
|
|
||||||
else {
|
|
||||||
char* error_message;
|
|
||||||
libssh2_session_last_error(session, &error_message, NULL, 0);
|
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
|
|
||||||
"Password authentication failed: %s", error_message);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void* ssh_client_thread(void* data) {
|
void* ssh_client_thread(void* data) {
|
||||||
|
|
||||||
guac_client* client = (guac_client*) data;
|
guac_client* client = (guac_client*) data;
|
||||||
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
|
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
|
||||||
|
|
||||||
char name[1024];
|
|
||||||
|
|
||||||
guac_socket* socket = client->socket;
|
guac_socket* socket = client->socket;
|
||||||
char buffer[8192];
|
char buffer[8192];
|
||||||
int bytes_read = -1234;
|
|
||||||
|
|
||||||
int socket_fd;
|
|
||||||
|
|
||||||
pthread_t input_thread;
|
pthread_t input_thread;
|
||||||
|
|
||||||
@ -290,81 +179,31 @@ void* ssh_client_thread(void* data) {
|
|||||||
if (guac_common_ssh_init(client))
|
if (guac_common_ssh_init(client))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* Get username */
|
/* Get user and credentials */
|
||||||
if (client_data->username[0] == 0)
|
guac_common_ssh_user* user = guac_ssh_get_user(client);
|
||||||
guac_terminal_prompt(client_data->term, "Login as: ",
|
|
||||||
client_data->username, sizeof(client_data->username), true);
|
|
||||||
|
|
||||||
/* Send new name */
|
/* Send new name */
|
||||||
snprintf(name, sizeof(name)-1, "%s@%s", client_data->username, client_data->hostname);
|
char name[1024];
|
||||||
|
snprintf(name, sizeof(name)-1, "%s@%s",
|
||||||
|
client_data->username, client_data->hostname);
|
||||||
guac_protocol_send_name(socket, name);
|
guac_protocol_send_name(socket, name);
|
||||||
|
|
||||||
/* If key specified, import */
|
|
||||||
if (client_data->key_base64[0] != 0) {
|
|
||||||
|
|
||||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
|
||||||
"Attempting private key import (WITHOUT passphrase)");
|
|
||||||
|
|
||||||
/* Attempt to read key without passphrase */
|
|
||||||
client_data->key = ssh_key_alloc(client_data->key_base64,
|
|
||||||
strlen(client_data->key_base64), "");
|
|
||||||
|
|
||||||
/* On failure, attempt with passphrase */
|
|
||||||
if (client_data->key == NULL) {
|
|
||||||
|
|
||||||
/* Log failure of initial attempt */
|
|
||||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
|
||||||
"Initial import failed: %s", ssh_key_error());
|
|
||||||
|
|
||||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
|
||||||
"Re-attempting private key import (WITH passphrase)");
|
|
||||||
|
|
||||||
/* Prompt for passphrase if missing */
|
|
||||||
if (client_data->key_passphrase[0] == 0)
|
|
||||||
guac_terminal_prompt(client_data->term, "Key passphrase: ",
|
|
||||||
client_data->key_passphrase, sizeof(client_data->key_passphrase), false);
|
|
||||||
|
|
||||||
/* Import key with passphrase */
|
|
||||||
client_data->key = ssh_key_alloc(client_data->key_base64,
|
|
||||||
strlen(client_data->key_base64),
|
|
||||||
client_data->key_passphrase);
|
|
||||||
|
|
||||||
/* If still failing, give up */
|
|
||||||
if (client_data->key == NULL) {
|
|
||||||
guac_client_abort(client,
|
|
||||||
GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
|
|
||||||
"Auth key import failed: %s", ssh_key_error());
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
} /* end decrypt key with passphrase */
|
|
||||||
|
|
||||||
/* Success */
|
|
||||||
guac_client_log(client, GUAC_LOG_INFO, "Auth key successfully imported.");
|
|
||||||
|
|
||||||
} /* end if key given */
|
|
||||||
|
|
||||||
/* Otherwise, get password if not provided */
|
|
||||||
else if (client_data->password[0] == 0)
|
|
||||||
guac_terminal_prompt(client_data->term, "Password: ",
|
|
||||||
client_data->password, sizeof(client_data->password), false);
|
|
||||||
|
|
||||||
/* Clear screen */
|
|
||||||
guac_terminal_printf(client_data->term, "\x1B[H\x1B[J");
|
|
||||||
|
|
||||||
/* Open SSH session */
|
/* Open SSH session */
|
||||||
client_data->session = __guac_ssh_create_session(client, &socket_fd);
|
client_data->session = guac_common_ssh_create_session(client,
|
||||||
|
client_data->hostname, client_data->port, user);
|
||||||
if (client_data->session == NULL) {
|
if (client_data->session == NULL) {
|
||||||
/* Already aborted within __guac_ssh_create_session() */
|
/* Already aborted within guac_common_ssh_create_session() */
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_init(&client_data->term_channel_lock, NULL);
|
pthread_mutex_init(&client_data->term_channel_lock, NULL);
|
||||||
|
|
||||||
/* Open channel for terminal */
|
/* Open channel for terminal */
|
||||||
client_data->term_channel = libssh2_channel_open_session(client_data->session);
|
client_data->term_channel =
|
||||||
|
libssh2_channel_open_session(client_data->session->session);
|
||||||
if (client_data->term_channel == NULL) {
|
if (client_data->term_channel == NULL) {
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Unable to open terminal channel.");
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
|
||||||
|
"Unable to open terminal channel.");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -393,14 +232,15 @@ void* ssh_client_thread(void* data) {
|
|||||||
|
|
||||||
/* Create SSH session specific for SFTP */
|
/* Create SSH session specific for SFTP */
|
||||||
guac_client_log(client, GUAC_LOG_DEBUG, "Reconnecting for SFTP...");
|
guac_client_log(client, GUAC_LOG_DEBUG, "Reconnecting for SFTP...");
|
||||||
client_data->sftp_ssh_session = __guac_ssh_create_session(client, NULL);
|
client_data->sftp_ssh_session = guac_common_ssh_create_session(client,
|
||||||
|
client_data->hostname, client_data->port, user);
|
||||||
if (client_data->sftp_ssh_session == NULL) {
|
if (client_data->sftp_ssh_session == NULL) {
|
||||||
/* Already aborted within __guac_ssh_create_session() */
|
/* Already aborted within guac_common_ssh_create_session() */
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Request SFTP */
|
/* Request SFTP */
|
||||||
client_data->sftp_session = libssh2_sftp_init(client_data->sftp_ssh_session);
|
client_data->sftp_session = libssh2_sftp_init(client_data->sftp_ssh_session->session);
|
||||||
if (client_data->sftp_session == NULL) {
|
if (client_data->sftp_session == NULL) {
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Unable to start SFTP session.");
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Unable to start SFTP session.");
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -439,10 +279,10 @@ void* ssh_client_thread(void* data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Set non-blocking */
|
/* Set non-blocking */
|
||||||
libssh2_session_set_blocking(client_data->session, 0);
|
libssh2_session_set_blocking(client_data->session->session, 0);
|
||||||
|
|
||||||
/* While data available, write to terminal */
|
/* While data available, write to terminal */
|
||||||
bytes_read = 0;
|
int bytes_read = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
||||||
/* Track total amount of data read */
|
/* Track total amount of data read */
|
||||||
@ -491,13 +331,14 @@ void* ssh_client_thread(void* data) {
|
|||||||
struct timeval timeout;
|
struct timeval timeout;
|
||||||
|
|
||||||
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
||||||
FD_SET(socket_fd, &fds);
|
FD_SET(client_data->session->fd, &fds);
|
||||||
|
|
||||||
/* Wait for one second */
|
/* Wait for one second */
|
||||||
timeout.tv_sec = 1;
|
timeout.tv_sec = 1;
|
||||||
timeout.tv_usec = 0;
|
timeout.tv_usec = 0;
|
||||||
|
|
||||||
if (select(socket_fd+1, &fds, NULL, NULL, &timeout) < 0)
|
if (select(client_data->session->fd + 1, &fds,
|
||||||
|
NULL, NULL, &timeout) < 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -508,7 +349,6 @@ void* ssh_client_thread(void* data) {
|
|||||||
pthread_join(input_thread, NULL);
|
pthread_join(input_thread, NULL);
|
||||||
|
|
||||||
pthread_mutex_destroy(&client_data->term_channel_lock);
|
pthread_mutex_destroy(&client_data->term_channel_lock);
|
||||||
guac_common_ssh_uninit();
|
|
||||||
|
|
||||||
guac_client_log(client, GUAC_LOG_INFO, "SSH connection ended.");
|
guac_client_log(client, GUAC_LOG_INFO, "SSH connection ended.");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -1,216 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013 Glyptodon LLC
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "ssh_buffer.h"
|
|
||||||
#include "ssh_key.h"
|
|
||||||
|
|
||||||
#include <openssl/bio.h>
|
|
||||||
#include <openssl/bn.h>
|
|
||||||
#include <openssl/dsa.h>
|
|
||||||
#include <openssl/err.h>
|
|
||||||
#include <openssl/evp.h>
|
|
||||||
#include <openssl/obj_mac.h>
|
|
||||||
#include <openssl/pem.h>
|
|
||||||
#include <openssl/rsa.h>
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
ssh_key* ssh_key_alloc(char* data, int length, char* passphrase) {
|
|
||||||
|
|
||||||
ssh_key* key;
|
|
||||||
BIO* key_bio;
|
|
||||||
|
|
||||||
char* public_key;
|
|
||||||
char* pos;
|
|
||||||
|
|
||||||
/* Create BIO for reading key from memory */
|
|
||||||
key_bio = BIO_new_mem_buf(data, length);
|
|
||||||
|
|
||||||
/* If RSA key, load RSA */
|
|
||||||
if (length > sizeof(SSH_RSA_KEY_HEADER)-1
|
|
||||||
&& memcmp(SSH_RSA_KEY_HEADER, data,
|
|
||||||
sizeof(SSH_RSA_KEY_HEADER)-1) == 0) {
|
|
||||||
|
|
||||||
RSA* rsa_key;
|
|
||||||
|
|
||||||
/* Read key */
|
|
||||||
rsa_key = PEM_read_bio_RSAPrivateKey(key_bio, NULL, NULL, passphrase);
|
|
||||||
if (rsa_key == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* Allocate key */
|
|
||||||
key = malloc(sizeof(ssh_key));
|
|
||||||
key->rsa = rsa_key;
|
|
||||||
|
|
||||||
/* Set type */
|
|
||||||
key->type = SSH_KEY_RSA;
|
|
||||||
|
|
||||||
/* Allocate space for public key */
|
|
||||||
public_key = malloc(4096);
|
|
||||||
pos = public_key;
|
|
||||||
|
|
||||||
/* Derive public key */
|
|
||||||
buffer_write_string(&pos, "ssh-rsa", sizeof("ssh-rsa")-1);
|
|
||||||
buffer_write_bignum(&pos, rsa_key->e);
|
|
||||||
buffer_write_bignum(&pos, rsa_key->n);
|
|
||||||
|
|
||||||
/* Save public key to structure */
|
|
||||||
key->public_key = public_key;
|
|
||||||
key->public_key_length = pos - public_key;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If DSA key, load DSA */
|
|
||||||
else if (length > sizeof(SSH_DSA_KEY_HEADER)-1
|
|
||||||
&& memcmp(SSH_DSA_KEY_HEADER, data,
|
|
||||||
sizeof(SSH_DSA_KEY_HEADER)-1) == 0) {
|
|
||||||
|
|
||||||
DSA* dsa_key;
|
|
||||||
|
|
||||||
/* Read key */
|
|
||||||
dsa_key = PEM_read_bio_DSAPrivateKey(key_bio, NULL, NULL, passphrase);
|
|
||||||
if (dsa_key == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* Allocate key */
|
|
||||||
key = malloc(sizeof(ssh_key));
|
|
||||||
key->dsa = dsa_key;
|
|
||||||
|
|
||||||
/* Set type */
|
|
||||||
key->type = SSH_KEY_DSA;
|
|
||||||
|
|
||||||
/* Allocate space for public key */
|
|
||||||
public_key = malloc(4096);
|
|
||||||
pos = public_key;
|
|
||||||
|
|
||||||
/* Derive public key */
|
|
||||||
buffer_write_string(&pos, "ssh-dss", sizeof("ssh-dss")-1);
|
|
||||||
buffer_write_bignum(&pos, dsa_key->p);
|
|
||||||
buffer_write_bignum(&pos, dsa_key->q);
|
|
||||||
buffer_write_bignum(&pos, dsa_key->g);
|
|
||||||
buffer_write_bignum(&pos, dsa_key->pub_key);
|
|
||||||
|
|
||||||
/* Save public key to structure */
|
|
||||||
key->public_key = public_key;
|
|
||||||
key->public_key_length = pos - public_key;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Otherwise, unsupported type */
|
|
||||||
else {
|
|
||||||
BIO_free(key_bio);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy private key to structure */
|
|
||||||
key->private_key_length = length;
|
|
||||||
key->private_key = malloc(length);
|
|
||||||
memcpy(key->private_key, data, length);
|
|
||||||
|
|
||||||
BIO_free(key_bio);
|
|
||||||
return key;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* ssh_key_error() {
|
|
||||||
|
|
||||||
/* Return static error string */
|
|
||||||
return ERR_reason_error_string(ERR_get_error());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void ssh_key_free(ssh_key* key) {
|
|
||||||
|
|
||||||
/* Free key-specific data */
|
|
||||||
if (key->type == SSH_KEY_RSA)
|
|
||||||
RSA_free(key->rsa);
|
|
||||||
else if (key->type == SSH_KEY_DSA)
|
|
||||||
DSA_free(key->dsa);
|
|
||||||
|
|
||||||
free(key->public_key);
|
|
||||||
free(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
int ssh_key_sign(ssh_key* key, const char* data, int length, unsigned char* sig) {
|
|
||||||
|
|
||||||
const EVP_MD* md;
|
|
||||||
EVP_MD_CTX md_ctx;
|
|
||||||
|
|
||||||
unsigned char digest[EVP_MAX_MD_SIZE];
|
|
||||||
unsigned int dlen, len;
|
|
||||||
|
|
||||||
/* Get SHA1 digest */
|
|
||||||
if ((md = EVP_get_digestbynid(NID_sha1)) == NULL)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* Digest data */
|
|
||||||
EVP_DigestInit(&md_ctx, md);
|
|
||||||
EVP_DigestUpdate(&md_ctx, data, length);
|
|
||||||
EVP_DigestFinal(&md_ctx, digest, &dlen);
|
|
||||||
|
|
||||||
/* Sign with key */
|
|
||||||
switch (key->type) {
|
|
||||||
|
|
||||||
case SSH_KEY_RSA:
|
|
||||||
if (RSA_sign(NID_sha1, digest, dlen, sig, &len, key->rsa) == 1)
|
|
||||||
return len;
|
|
||||||
|
|
||||||
case SSH_KEY_DSA: {
|
|
||||||
|
|
||||||
DSA_SIG* dsa_sig = DSA_do_sign(digest, dlen, key->dsa);
|
|
||||||
if (dsa_sig != NULL) {
|
|
||||||
|
|
||||||
/* Compute size of each half of signature */
|
|
||||||
int rlen = BN_num_bytes(dsa_sig->r);
|
|
||||||
int slen = BN_num_bytes(dsa_sig->s);
|
|
||||||
|
|
||||||
/* Ensure each number is within the required size */
|
|
||||||
if (rlen > DSA_SIG_NUMBER_SIZE || slen > DSA_SIG_NUMBER_SIZE)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* Init to all zeroes */
|
|
||||||
memset(sig, 0, DSA_SIG_SIZE);
|
|
||||||
|
|
||||||
/* Add R at the end of the first block of the signature */
|
|
||||||
BN_bn2bin(dsa_sig->r, sig + DSA_SIG_SIZE
|
|
||||||
- DSA_SIG_NUMBER_SIZE - rlen);
|
|
||||||
|
|
||||||
/* Add S at the end of the second block of the signature */
|
|
||||||
BN_bn2bin(dsa_sig->s, sig + DSA_SIG_SIZE - slen);
|
|
||||||
|
|
||||||
/* Done */
|
|
||||||
DSA_SIG_free(dsa_sig);
|
|
||||||
return DSA_SIG_SIZE;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,137 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013 Glyptodon LLC
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef _GUAC_SSH_KEY_H
|
|
||||||
#define _GUAC_SSH_KEY_H
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <openssl/ossl_typ.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The expected header of RSA private keys.
|
|
||||||
*/
|
|
||||||
#define SSH_RSA_KEY_HEADER "-----BEGIN RSA PRIVATE KEY-----"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The expected header of DSA private keys.
|
|
||||||
*/
|
|
||||||
#define SSH_DSA_KEY_HEADER "-----BEGIN DSA PRIVATE KEY-----"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The size of single number within a DSA signature, in bytes.
|
|
||||||
*/
|
|
||||||
#define DSA_SIG_NUMBER_SIZE 20
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The size of a DSA signature, in bytes.
|
|
||||||
*/
|
|
||||||
#define DSA_SIG_SIZE DSA_SIG_NUMBER_SIZE*2
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of an SSH key.
|
|
||||||
*/
|
|
||||||
typedef enum ssh_key_type {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RSA key.
|
|
||||||
*/
|
|
||||||
SSH_KEY_RSA,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DSA key.
|
|
||||||
*/
|
|
||||||
SSH_KEY_DSA
|
|
||||||
|
|
||||||
} ssh_key_type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstraction of a key used for SSH authentication.
|
|
||||||
*/
|
|
||||||
typedef struct ssh_key {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of this key.
|
|
||||||
*/
|
|
||||||
ssh_key_type type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Underlying RSA private key, if any.
|
|
||||||
*/
|
|
||||||
RSA* rsa;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Underlying DSA private key, if any.
|
|
||||||
*/
|
|
||||||
DSA* dsa;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The associated public key, encoded as necessary for SSH.
|
|
||||||
*/
|
|
||||||
char* public_key;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The length of the public key, in bytes.
|
|
||||||
*/
|
|
||||||
int public_key_length;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The private key, encoded as necessary for SSH.
|
|
||||||
*/
|
|
||||||
char* private_key;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The length of the private key, in bytes.
|
|
||||||
*/
|
|
||||||
int private_key_length;
|
|
||||||
|
|
||||||
} ssh_key;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allocates a new key containing the given private key data and specified
|
|
||||||
* passphrase. If unable to read the key, NULL is returned.
|
|
||||||
*/
|
|
||||||
ssh_key* ssh_key_alloc(char* data, int length, char* passphrase);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a statically-allocated string describing the most recent SSH key
|
|
||||||
* error.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* A statically-allocated string describing the most recent SSH key error.
|
|
||||||
*/
|
|
||||||
const char* ssh_key_error();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Frees all memory associated with the given key.
|
|
||||||
*/
|
|
||||||
void ssh_key_free(ssh_key* key);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signs the given data using the given key, returning the length of the
|
|
||||||
* signature in bytes, or a value less than zero on error.
|
|
||||||
*/
|
|
||||||
int ssh_key_sign(ssh_key* key, const char* data, int length, unsigned char* sig);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user