From f2b9aaf3e7d82389fb9e37e241caa8f6333eb671 Mon Sep 17 00:00:00 2001 From: Tejaswi Kandula Date: Wed, 28 Jun 2023 14:39:22 -0700 Subject: [PATCH 1/2] GUACAMOLE-1290: Add support for ssh certificate authentication --- src/common-ssh/common-ssh/user.h | 23 +++++++++++++++++++++++ src/common-ssh/ssh.c | 6 +++++- src/common-ssh/user.c | 14 ++++++++++++++ src/protocols/ssh/settings.c | 11 +++++++++++ src/protocols/ssh/settings.h | 6 ++++++ src/protocols/ssh/ssh.c | 26 ++++++++++++++++++++++++++ 6 files changed, 85 insertions(+), 1 deletion(-) diff --git a/src/common-ssh/common-ssh/user.h b/src/common-ssh/common-ssh/user.h index 745728f1..f94a2300 100644 --- a/src/common-ssh/common-ssh/user.h +++ b/src/common-ssh/common-ssh/user.h @@ -44,6 +44,12 @@ typedef struct guac_common_ssh_user { */ guac_common_ssh_key* private_key; + /** + * The public key which should be used to authenticate this user, if any, + * or NULL if a password or just a private key will be used instead. + */ + char* public_key; + } guac_common_ssh_user; /** @@ -104,5 +110,22 @@ void guac_common_ssh_user_set_password(guac_common_ssh_user* user, int guac_common_ssh_user_import_key(guac_common_ssh_user* user, char* private_key, char* passphrase); +/** + * Imports the given public key, associating that key with the given user. + * If the public key is imported successfully, it will be used for + * future authentication attempts. + * + * @param user + * The user to associate with the given private key. + * + * @param public_key + * + * @return + * Zero if public key is successfully imported, or non-zero if the + * public key could not be imported due to an error. + */ +int guac_common_ssh_user_import_public_key(guac_common_ssh_user* user, + char* public_key); + #endif diff --git a/src/common-ssh/ssh.c b/src/common-ssh/ssh.c index 1cbd4834..6ad5fd90 100644 --- a/src/common-ssh/ssh.c +++ b/src/common-ssh/ssh.c @@ -284,6 +284,8 @@ static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session) /* Get user credentials */ guac_common_ssh_key* key = user->private_key; + char* public_key = user->public_key; + /* Validate username provided */ if (user->username == NULL) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED, @@ -317,9 +319,11 @@ static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session) return 1; } + int public_key_length = public_key == NULL ? 0 : strlen(public_key); + /* Attempt public key auth */ if (libssh2_userauth_publickey_frommemory(session, user->username, - username_len, NULL, 0, key->private_key, + username_len, public_key, public_key_length, key->private_key, key->private_key_length, key->passphrase)) { /* Abort on failure */ diff --git a/src/common-ssh/user.c b/src/common-ssh/user.c index 92c8d963..79c8590a 100644 --- a/src/common-ssh/user.c +++ b/src/common-ssh/user.c @@ -31,6 +31,7 @@ guac_common_ssh_user* guac_common_ssh_create_user(const char* username) { user->username = strdup(username); user->password = NULL; user->private_key = NULL; + user->public_key = NULL; return user; @@ -45,6 +46,7 @@ void guac_common_ssh_destroy_user(guac_common_ssh_user* user) { /* Free all other data */ free(user->password); free(user->username); + free(user->public_key); free(user); } @@ -80,3 +82,15 @@ int guac_common_ssh_user_import_key(guac_common_ssh_user* user, } +int guac_common_ssh_user_import_public_key(guac_common_ssh_user* user, + char* public_key) { + + /* Free existing public key, if present */ + free(user->public_key); + user->public_key = strdup(public_key); + + /* Fail if key could not be read */ + return user->public_key == NULL; + +} + diff --git a/src/protocols/ssh/settings.c b/src/protocols/ssh/settings.c index 686ef637..dd2eba7e 100644 --- a/src/protocols/ssh/settings.c +++ b/src/protocols/ssh/settings.c @@ -47,6 +47,7 @@ const char* GUAC_SSH_CLIENT_ARGS[] = { "sftp-disable-upload", "private-key", "passphrase", + "public-key", #ifdef ENABLE_SSH_AGENT "enable-agent", #endif @@ -148,6 +149,11 @@ enum SSH_ARGS_IDX { */ IDX_PASSPHRASE, + /** + * The public key to use for authentication, if any. + */ + IDX_PUBLIC_KEY, + #ifdef ENABLE_SSH_AGENT /** * Whether SSH agent forwarding support should be enabled. @@ -373,6 +379,10 @@ guac_ssh_settings* guac_ssh_parse_args(guac_user* user, guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv, IDX_PASSPHRASE, NULL); + settings->public_key_base64 = + guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv, + IDX_PUBLIC_KEY, NULL); + /* Read maximum scrollback size */ settings->max_scrollback = guac_user_parse_args_int(user, GUAC_SSH_CLIENT_ARGS, argv, @@ -567,6 +577,7 @@ void guac_ssh_settings_free(guac_ssh_settings* settings) { free(settings->password); free(settings->key_base64); free(settings->key_passphrase); + free(settings->public_key_base64); /* Free display preferences */ free(settings->font_name); diff --git a/src/protocols/ssh/settings.h b/src/protocols/ssh/settings.h index 103ff029..008512ef 100644 --- a/src/protocols/ssh/settings.h +++ b/src/protocols/ssh/settings.h @@ -93,6 +93,12 @@ typedef struct guac_ssh_settings { */ char* key_passphrase; + /** + * The public key, if any. If no public key is + * specified, this will be NULL. + */ + char* public_key_base64; + /** * Whether this connection is read-only, and user input should be dropped. */ diff --git a/src/protocols/ssh/ssh.c b/src/protocols/ssh/ssh.c index 2ebe0687..64a667c6 100644 --- a/src/protocols/ssh/ssh.c +++ b/src/protocols/ssh/ssh.c @@ -134,6 +134,32 @@ static guac_common_ssh_user* guac_ssh_get_user(guac_client* client) { } /* end if key given */ + if (settings->public_key_base64 != NULL) { + + guac_client_log(client, GUAC_LOG_DEBUG, + "Attempting public key import"); + + /* Attempt to read public key */ + if (guac_common_ssh_user_import_public_key(user, + settings->public_key_base64)) { + + /* If failing*/ + guac_client_abort(client, + GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED, + "Auth public key import failed: %s", + guac_common_ssh_key_error()); + + guac_common_ssh_destroy_user(user); + return NULL; + + } + + /* Success */ + guac_client_log(client, GUAC_LOG_INFO, + "Auth public key successfully imported."); + + } + /* If available, get password from settings */ else if (settings->password != NULL) { guac_common_ssh_user_set_password(user, settings->password); From 766b9f68dd63529fa39fa86aa4c67bf150e57560 Mon Sep 17 00:00:00 2001 From: Tejaswi Kandula Date: Tue, 11 Jul 2023 14:05:37 -0700 Subject: [PATCH 2/2] GUACAMOLE-1290: Minor changes to comments --- src/common-ssh/common-ssh/user.h | 1 + src/protocols/ssh/settings.h | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/common-ssh/common-ssh/user.h b/src/common-ssh/common-ssh/user.h index f94a2300..f3065f93 100644 --- a/src/common-ssh/common-ssh/user.h +++ b/src/common-ssh/common-ssh/user.h @@ -119,6 +119,7 @@ int guac_common_ssh_user_import_key(guac_common_ssh_user* user, * The user to associate with the given private key. * * @param public_key + * The base64-encoded public key to import. * * @return * Zero if public key is successfully imported, or non-zero if the diff --git a/src/protocols/ssh/settings.h b/src/protocols/ssh/settings.h index 008512ef..e5d20750 100644 --- a/src/protocols/ssh/settings.h +++ b/src/protocols/ssh/settings.h @@ -94,8 +94,8 @@ typedef struct guac_ssh_settings { char* key_passphrase; /** - * The public key, if any. If no public key is - * specified, this will be NULL. + * The public key, encoded as base64, if any. If no public key is specified, + * this will be NULL. */ char* public_key_base64;