GUAC-1171: Separate key/password management from SSH connection. Maintain connection information within session object.

This commit is contained in:
Michael Jumper 2015-07-10 01:31:15 -07:00
parent 0a015b2843
commit ff287aee52
5 changed files with 281 additions and 148 deletions

View File

@ -29,13 +29,15 @@ libguac_common_ssh_la_SOURCES = \
guac_sftp.c \
guac_ssh.c \
guac_ssh_buffer.c \
guac_ssh_key.c
guac_ssh_key.c \
guac_ssh_user.c
noinst_HEADERS = \
guac_sftp.h \
guac_ssh.h \
guac_ssh_buffer.h \
guac_ssh_key.h
guac_ssh_key.h \
guac_ssh_user.h
libguac_common_ssh_la_CFLAGS = \
-Werror -Wall -pedantic \

View File

@ -137,32 +137,8 @@ void guac_common_ssh_uninit() {
guac_common_ssh_openssl_free_locks(CRYPTO_num_locks());
}
/**
* Connects to the SSH server running at the given hostname and port, but does
* not perform any authentication. Authentication must be immediately performed
* after creation of the session for the session to become usable. If an error
* occurs while connecting, the Guacamole client will automatically and fatally
* abort.
*
* @param client
* The Guacamole client that will be using SSH.
*
* @param hostname
* The hostname of the SSH server to connect to.
*
* @param port
* The port to connect to on the given hostname.
*
* @param socket_fd
* A pointer to an integer in which the newly-allocated file descriptor for
* the SSH socket should be stored.
*
* @return
* A new SSH session if the connection succeeds, or NULL if the connection
* was not successful.
*/
static LIBSSH2_SESSION* guac_common_ssh_connect(guac_client* client,
const char* hostname, const char* port, int* socket_fd) {
guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
const char* hostname, const char* port) {
int retval;
@ -252,30 +228,27 @@ static LIBSSH2_SESSION* guac_common_ssh_connect(guac_client* client,
return NULL;
}
/* Save file descriptor */
if (socket_fd != NULL)
*socket_fd = fd;
/* Allocate new session */
guac_common_ssh_session* common_session =
malloc(sizeof(guac_common_ssh_session));
/* Store basic session data */
common_session->client = client;
common_session->session = session;
common_session->fd = fd;
/* Return created session */
return session;
return common_session;
}
LIBSSH2_SESSION* guac_common_ssh_connect_password(guac_client* client,
const char* hostname, const char* port,
const char* username, const char* password,
int* socket_fd) {
LIBSSH2_SESSION* session = guac_common_ssh_connect(client,
hostname, port, socket_fd);
/* STUB */
return session;
void guac_common_ssh_destroy_session(guac_common_ssh_session* session) {
libssh2_session_disconnect(session->session, "Bye");
libssh2_session_free(session->session);
free(session);
}
static int __sign_callback(LIBSSH2_SESSION* session,
static int guac_common_ssh_sign_callback(LIBSSH2_SESSION* session,
unsigned char** sig, size_t* sig_len,
const unsigned char* data, size_t data_len, void **abstract) {
@ -294,73 +267,33 @@ static int __sign_callback(LIBSSH2_SESSION* session,
return 0;
}
LIBSSH2_SESSION* guac_common_ssh_connect_private_key(guac_client* client,
const char* hostname, const char* port,
const char* username, char* private_key, char* passphrase,
int* socket_fd) {
int guac_common_ssh_authenticate(guac_common_ssh_session* common_session,
guac_common_ssh_user* user) {
LIBSSH2_SESSION* session = guac_common_ssh_connect(client,
hostname, port, socket_fd);
guac_client* client = common_session->client;
LIBSSH2_SESSION* session = common_session->session;
guac_client_log(client, GUAC_LOG_DEBUG,
"Attempting private key import (WITHOUT passphrase)");
/* Get user credentials */
char* username = user->username;
guac_common_ssh_key* key = user->private_key;
/* Attempt to read key without passphrase */
guac_common_ssh_key* key = guac_common_ssh_key_alloc(private_key,
strlen(private_key), "");
#if 0
/* On failure, attempt with passphrase */
if (key == 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);
/* Import key with passphrase */
client_data->key = guac_common_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", guac_common_ssh_key_error());
return NULL;
}
} /* end decrypt key with passphrase */
#endif
/* Success */
guac_client_log(client, GUAC_LOG_INFO, "Auth key successfully imported.");
/* Get list of suported authentication methods */
/* Get list of supported authentication methods */
char* user_authlist = libssh2_userauth_list(session, username,
strlen(username));
guac_client_log(client, GUAC_LOG_DEBUG,
"Supported authentication methods: %s", user_authlist);
/* Check if public key auth is suported on the server */
/* Check if public key auth is supported 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;
"Public key authentication not supported");
return 1;
}
/* Attempt public key auth */
if (libssh2_userauth_publickey(session, username,
(unsigned char*) key->public_key, key->public_key_length,
__sign_callback, (void**) key)) {
guac_common_ssh_sign_callback, (void**) key)) {
/* Abort on failure */
char* error_message;
@ -368,12 +301,12 @@ LIBSSH2_SESSION* guac_common_ssh_connect_private_key(guac_client* client,
guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
"Public key authentication failed: %s", error_message);
return NULL;
return 1;
}
/* Return new session on success */
return session;
/* Authentication succeeded */
return 0;
}

View File

@ -23,9 +23,34 @@
#ifndef GUAC_COMMON_SSH_H
#define GUAC_COMMON_SSH_H
#include "guac_ssh_user.h"
#include <guacamole/client.h>
#include <libssh2.h>
/**
* An SSH session, backed by libssh2 and associated with a particular
* Guacamole client.
*/
typedef struct guac_common_ssh_session {
/**
* The Guacamole client using this SSH session.
*/
guac_client* client;
/**
* The underlying SSH session from libssh2.
*/
LIBSSH2_SESSION* session;
/**
* The file descriptor of the socket being used for the SSH connection.
*/
int fd;
} guac_common_ssh_session;
/**
* Initializes the underlying SSH and encryption libraries used by Guacamole.
* This function must be called before any other guac_common_ssh_*() functions
@ -47,9 +72,9 @@ int guac_common_ssh_init(guac_client* client);
void guac_common_ssh_uninit();
/**
* Connects to the SSH server running at the given hostname and port using the
* given username and password for authentication. If an error occurs while
* connecting, the Guacamole client will automatically and fatally abort.
* Connects to the SSH server running at the given hostname and port but does
* not perform any authentication. If an error occurs while connecting, the
* Guacamole client will automatically and fatally abort.
*
* @param client
* The Guacamole client that will be using SSH.
@ -60,61 +85,39 @@ void guac_common_ssh_uninit();
* @param port
* The port to connect to on the given hostname.
*
* @param username
* The username to authenticate as.
*
* @param password
* The password to provide when authenticating as the given user.
*
* @param socket_fd
* A pointer to an integer in which the newly-allocated file descriptor for
* the SSH socket should be stored.
*
* @return
* A new SSH session if the connection and authentication succeed, or
* NULL if the connection or authentication were not successful.
* A new SSH session if the connection succeeds, or NULL if the connection
* was not successful.
*/
LIBSSH2_SESSION* guac_common_ssh_connect_password(guac_client* client,
const char* hostname, const char* port,
const char* username, const char* password,
int* socket_fd);
guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
const char* hostname, const char* port);
/**
* Connects to the SSH server running at the given hostname and port using the
* given username and private key for authentication. If an error occurs while
* connecting, the Guacamole client will automatically and fatally abort.
* Disconnects and destroys the given SSH session, freeing all associated
* resources.
*
* @param client
* The Guacamole client that will be using SSH.
* @param session
* The SSH session to destroy.
*/
void guac_common_ssh_destroy_session(guac_common_ssh_session* session);
/**
* Authenticates the given user with the given, existing SSH session. If
* authentication fails, the Guacamole client will automatically and fatally
* abort.
*
* @param hostname
* The hostname of the SSH server to connect to.
* @param session
* The SSH session to authenticate with.
*
* @param port
* The port to connect to on the given hostname.
*
* @param username
* The username to authenticate as.
*
* @param private_key
* The base64-encoded private key to use when authenticating.
*
* @param passphrase
* The passphrase to use when importing the private key, if any, or NULL
* if no passphrase should be used.
*
* @param socket_fd
* A pointer to an integer in which the newly-allocated file descriptor for
* the SSH socket should be stored.
* @param user
* The user object describing the current user and their associated
* credentials.
*
* @return
* A new SSH session if the connection and authentication succeed, or
* NULL if the connection or authentication were not successful.
* Zero if authentication succeeds, non-zero if authentication fails.
*/
LIBSSH2_SESSION* guac_common_ssh_connect_private_key(guac_client* client,
const char* hostname, const char* port,
const char* username, char* private_key, char* passphrase,
int* socket_fd);
int guac_common_ssh_authenticate(guac_common_ssh_session* session,
guac_common_ssh_user* user);
#endif

View File

@ -0,0 +1,84 @@
/*
* Copyright (C) 2015 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 "guac_ssh_key.h"
#include "guac_ssh_user.h"
#include <stdlib.h>
#include <string.h>
guac_common_ssh_user* guac_common_ssh_create_user(const char* username) {
guac_common_ssh_user* user = malloc(sizeof(guac_common_ssh_user));
/* Init user */
user->username = strdup(username);
user->password = NULL;
user->private_key = NULL;
return user;
}
void guac_common_ssh_destroy_user(guac_common_ssh_user* user) {
/* Free private key, if present */
if (user->private_key != NULL)
guac_common_ssh_key_free(user->private_key);
/* Free all other data */
free(user->password);
free(user->username);
}
void guac_common_ssh_user_set_password(guac_common_ssh_user* user,
const char* password) {
/* Replace current password with given value */
free(user->password);
user->password = strdup(password);
}
int guac_common_ssh_user_import_key(guac_common_ssh_user* user,
char* private_key, char* passphrase) {
/* Free existing private key, if present */
if (user->private_key != NULL)
guac_common_ssh_key_free(user->private_key);
/* Attempt to read key without passphrase if none given */
if (passphrase == NULL)
user->private_key = guac_common_ssh_key_alloc(private_key,
strlen(private_key), "");
/* Otherwise, use provided passphrase */
else
user->private_key = guac_common_ssh_key_alloc(private_key,
strlen(private_key), passphrase);
/* Fail if key could not be read */
return user->private_key == NULL;
}

View File

@ -0,0 +1,111 @@
/*
* Copyright (C) 2015 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_COMMON_SSH_USER_H
#define GUAC_COMMON_SSH_USER_H
#include "guac_ssh_key.h"
/**
* Data describing an SSH user, including their credentials.
*/
typedef struct guac_common_ssh_user {
/**
* The username of this user.
*/
char* username;
/**
* The password which should be used to authenticate this user, if any, or
* NULL if a private key will be used instead.
*/
char* password;
/**
* The private key which should be used to authenticate this user, if any,
* or NULL if a password will be used instead.
*/
guac_common_ssh_key* private_key;
} guac_common_ssh_user;
/**
* Creates a new SSH user with the given username. When additionally populated
* with a password or private key, this user can then be used for
* authentication.
*
* @param username
* The username of the user being created.
*
* @return
* A new SSH user having the given username, but no associated password
* or private key.
*/
guac_common_ssh_user* guac_common_ssh_create_user(const char* username);
/**
* Destroys the given user object, releasing all associated resources.
*
* @param user
* The user to destroy.
*/
void guac_common_ssh_destroy_user(guac_common_ssh_user* user);
/**
* Associates the given user with the given password, such that that password
* is used for future authentication attempts.
*
* @param user
* The user to associate with the given password.
*
* @param password
* The password to associate with the given user.
*/
void guac_common_ssh_user_set_password(guac_common_ssh_user* user,
const char* password);
/**
* Imports the given private key, associating that key with the given user. If
* necessary to decrypt the key, a passphrase may be specified. The private key
* must be provided in base64 form. If the private key is imported
* successfully, it will be used for future authentication attempts.
*
* @param user
* The user to associate with the given private key.
*
* @param private_key
* The base64-encoded private key to import.
*
* @param passphrase
* The passphrase to use to decrypt the given private key, or NULL if no
* passphrase should be used.
*
* @return
* Zero if the private key is successfully imported, or non-zero if the
* private key could not be imported due to an error.
*/
int guac_common_ssh_user_import_key(guac_common_ssh_user* user,
char* private_key, char* passphrase);
#endif