GUAC-1171: Separate key/password management from SSH connection. Maintain connection information within session object.
This commit is contained in:
parent
0a015b2843
commit
ff287aee52
@ -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 \
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
84
src/common-ssh/guac_ssh_user.c
Normal file
84
src/common-ssh/guac_ssh_user.c
Normal 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;
|
||||
|
||||
}
|
||||
|
111
src/common-ssh/guac_ssh_user.h
Normal file
111
src/common-ssh/guac_ssh_user.h
Normal 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
|
||||
|
Loading…
Reference in New Issue
Block a user