mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-02-04 20:30:38 +09:00
extensions: Host-bound public key authentication
Signed-off-by: Abdallah Alhadad <abdallahselhdad@gmail.com> Reviewed-by: Jakub Jelen <jjelen@redhat.com>
This commit is contained in:
@@ -28,6 +28,7 @@ struct ssh_auth_request {
|
||||
int method;
|
||||
char *password;
|
||||
struct ssh_key_struct *pubkey;
|
||||
struct ssh_key_struct *server_pubkey;
|
||||
char *sigtype;
|
||||
enum ssh_publickey_state_e signature_state;
|
||||
char kbdint_response;
|
||||
|
||||
@@ -120,6 +120,8 @@ enum ssh_pending_call_e {
|
||||
/* server-sig-algs extension */
|
||||
#define SSH_EXT_SIG_RSA_SHA256 0x02
|
||||
#define SSH_EXT_SIG_RSA_SHA512 0x04
|
||||
/* Host-bound public key authentication extension */
|
||||
#define SSH_EXT_PUBLICKEY_HOSTBOUND 0x08
|
||||
|
||||
/* members that are common to ssh_session and ssh_bind */
|
||||
struct ssh_common_struct {
|
||||
|
||||
59
src/auth.c
59
src/auth.c
@@ -462,13 +462,56 @@ fail:
|
||||
return SSH_AUTH_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Adds the server's public key to the authentication request.
|
||||
*
|
||||
* This function is used internally when the hostbound public key authentication
|
||||
* extension is enabled. It export the server's public key and adds it to the
|
||||
* authentication buffer.
|
||||
*
|
||||
* @param[in] session The SSH session.
|
||||
*
|
||||
* @returns SSH_OK on success, SSH_ERROR if an error occurred.
|
||||
*/
|
||||
static int add_hostbound_pubkey(ssh_session session)
|
||||
{
|
||||
int rc;
|
||||
ssh_string server_pubkey_s = NULL;
|
||||
|
||||
if (session == NULL || session->current_crypto == NULL ||
|
||||
session->current_crypto->server_pubkey == NULL) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Invalid session or server public key");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = ssh_pki_export_pubkey_blob(session->current_crypto->server_pubkey,
|
||||
&server_pubkey_s);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_add_ssh_string(session->out_buffer, server_pubkey_s);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
error:
|
||||
SSH_STRING_FREE(server_pubkey_s);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Build a public key authentication request.
|
||||
*
|
||||
* This helper function creates a SSH2_MSG_USERAUTH_REQUEST message for public
|
||||
* key authentication.
|
||||
* key authentication and adds the server's public key if the hostbound
|
||||
* extension is enabled.
|
||||
*
|
||||
* @param[in] session The SSH session.
|
||||
* @param[in] username The username, may be NULL.
|
||||
@@ -486,6 +529,11 @@ static int build_pubkey_auth_request(ssh_session session,
|
||||
ssh_string pubkey_s)
|
||||
{
|
||||
int rc;
|
||||
const char *auth_method = "publickey";
|
||||
|
||||
if (session->extensions & SSH_EXT_PUBLICKEY_HOSTBOUND) {
|
||||
auth_method = "publickey-hostbound-v00@openssh.com";
|
||||
}
|
||||
|
||||
/* request */
|
||||
rc = ssh_buffer_pack(session->out_buffer,
|
||||
@@ -493,7 +541,7 @@ static int build_pubkey_auth_request(ssh_session session,
|
||||
SSH2_MSG_USERAUTH_REQUEST,
|
||||
username ? username : session->opts.username,
|
||||
"ssh-connection",
|
||||
"publickey",
|
||||
auth_method,
|
||||
has_signature, /* private key? */
|
||||
sig_type_c, /* algo */
|
||||
pubkey_s /* public key */
|
||||
@@ -502,6 +550,13 @@ static int build_pubkey_auth_request(ssh_session session,
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
if (session->extensions & SSH_EXT_PUBLICKEY_HOSTBOUND) {
|
||||
rc = add_hostbound_pubkey(session);
|
||||
if (rc < 0) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -642,6 +642,7 @@ void ssh_message_free(ssh_message msg){
|
||||
SAFE_FREE(msg->auth_request.password);
|
||||
}
|
||||
ssh_key_free(msg->auth_request.pubkey);
|
||||
ssh_key_free(msg->auth_request.server_pubkey);
|
||||
break;
|
||||
case SSH_REQUEST_CHANNEL_OPEN:
|
||||
SAFE_FREE(msg->channel_request_open.originator);
|
||||
@@ -733,7 +734,8 @@ error:
|
||||
static ssh_buffer ssh_msg_userauth_build_digest(ssh_session session,
|
||||
ssh_message msg,
|
||||
const char *service,
|
||||
ssh_string algo)
|
||||
ssh_string algo,
|
||||
const char *method)
|
||||
{
|
||||
struct ssh_crypto_struct *crypto = NULL;
|
||||
ssh_buffer buffer;
|
||||
@@ -763,8 +765,8 @@ static ssh_buffer ssh_msg_userauth_build_digest(ssh_session session,
|
||||
SSH2_MSG_USERAUTH_REQUEST, /* type */
|
||||
msg->auth_request.username,
|
||||
service,
|
||||
"publickey", /* method */
|
||||
1, /* has to be signed (true) */
|
||||
method,
|
||||
1, /* has to be signed (true) */
|
||||
ssh_string_get_char(algo), /* pubkey algorithm */
|
||||
str); /* public key as a blob */
|
||||
|
||||
@@ -775,6 +777,25 @@ static ssh_buffer ssh_msg_userauth_build_digest(ssh_session session,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Add server public key for hostbound extension */
|
||||
if (strcmp(method, "publickey-hostbound-v00@openssh.com") == 0 &&
|
||||
msg->auth_request.server_pubkey != NULL) {
|
||||
|
||||
rc = ssh_pki_export_pubkey_blob(msg->auth_request.server_pubkey, &str);
|
||||
if (rc < 0) {
|
||||
SSH_BUFFER_FREE(buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_add_ssh_string(buffer, str);
|
||||
SSH_STRING_FREE(str);
|
||||
if (rc < 0) {
|
||||
ssh_set_error_oom(session);
|
||||
SSH_BUFFER_FREE(buffer);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@@ -870,19 +891,41 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request)
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (strcmp(method, "publickey") == 0) {
|
||||
if (strcmp(method, "publickey") == 0 ||
|
||||
strcmp(method, "publickey-hostbound-v00@openssh.com") == 0) {
|
||||
ssh_string algo = NULL;
|
||||
ssh_string pubkey_blob = NULL;
|
||||
ssh_string server_pubkey_blob = NULL;
|
||||
uint8_t has_sign;
|
||||
|
||||
msg->auth_request.method = SSH_AUTH_METHOD_PUBLICKEY;
|
||||
SAFE_FREE(method);
|
||||
|
||||
rc = ssh_buffer_unpack(packet, "bSS", &has_sign, &algo, &pubkey_blob);
|
||||
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
cmp = strcmp(method, "publickey-hostbound-v00@openssh.com");
|
||||
if (cmp == 0) {
|
||||
server_pubkey_blob = ssh_buffer_get_ssh_string(packet);
|
||||
if (server_pubkey_blob == NULL) {
|
||||
SSH_STRING_FREE(pubkey_blob);
|
||||
SSH_STRING_FREE(algo);
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = ssh_pki_import_pubkey_blob(server_pubkey_blob,
|
||||
&msg->auth_request.server_pubkey);
|
||||
SSH_STRING_FREE(server_pubkey_blob);
|
||||
|
||||
if (rc < 0) {
|
||||
SSH_STRING_FREE(pubkey_blob);
|
||||
SSH_STRING_FREE(algo);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
rc = ssh_pki_import_pubkey_blob(pubkey_blob, &msg->auth_request.pubkey);
|
||||
SSH_STRING_FREE(pubkey_blob);
|
||||
pubkey_blob = NULL;
|
||||
@@ -914,7 +957,11 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request)
|
||||
goto error;
|
||||
}
|
||||
|
||||
digest = ssh_msg_userauth_build_digest(session, msg, service, algo);
|
||||
digest = ssh_msg_userauth_build_digest(session,
|
||||
msg,
|
||||
service,
|
||||
algo,
|
||||
method);
|
||||
SSH_STRING_FREE(algo);
|
||||
algo = NULL;
|
||||
if (digest == NULL) {
|
||||
@@ -965,8 +1012,47 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request)
|
||||
|
||||
SSH_LOG(SSH_LOG_PACKET, "Valid signature received");
|
||||
|
||||
cmp = strcmp(method, "publickey-hostbound-v00@openssh.com");
|
||||
if (cmp == 0) {
|
||||
ssh_key server_key = NULL;
|
||||
|
||||
if (msg->auth_request.server_pubkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_PACKET,
|
||||
"Server public key not provided by client");
|
||||
msg->auth_request.signature_state =
|
||||
SSH_PUBLICKEY_STATE_WRONG;
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = ssh_get_server_publickey(session, &server_key);
|
||||
if (rc != SSH_OK) {
|
||||
SSH_LOG(SSH_LOG_PACKET,
|
||||
"Failed to get server public key for hostbound "
|
||||
"verification");
|
||||
msg->auth_request.signature_state =
|
||||
SSH_PUBLICKEY_STATE_ERROR;
|
||||
ssh_key_free(server_key);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (ssh_key_cmp(server_key,
|
||||
msg->auth_request.server_pubkey,
|
||||
SSH_KEY_CMP_PUBLIC) != 0) {
|
||||
SSH_LOG(SSH_LOG_PACKET,
|
||||
"Server public key doesn't match the one provided "
|
||||
"by client");
|
||||
msg->auth_request.signature_state =
|
||||
SSH_PUBLICKEY_STATE_WRONG;
|
||||
ssh_key_free(server_key);
|
||||
goto error;
|
||||
}
|
||||
ssh_key_free(server_key);
|
||||
}
|
||||
|
||||
msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_VALID;
|
||||
}
|
||||
|
||||
SAFE_FREE(method);
|
||||
SSH_STRING_FREE(algo);
|
||||
goto end;
|
||||
}
|
||||
|
||||
@@ -291,7 +291,6 @@ SSH_PACKET_CALLBACK(ssh_packet_ext_info)
|
||||
for (i = 0; i < nr_extensions; i++) {
|
||||
char *name = NULL;
|
||||
char *value = NULL;
|
||||
int cmp;
|
||||
|
||||
rc = ssh_buffer_unpack(packet, "ss", &name, &value);
|
||||
if (rc != SSH_OK) {
|
||||
@@ -299,8 +298,7 @@ SSH_PACKET_CALLBACK(ssh_packet_ext_info)
|
||||
return SSH_PACKET_USED;
|
||||
}
|
||||
|
||||
cmp = strcmp(name, "server-sig-algs");
|
||||
if (cmp == 0) {
|
||||
if (strcmp(name, "server-sig-algs") == 0) {
|
||||
/* TODO check for NULL bytes */
|
||||
SSH_LOG(SSH_LOG_PACKET, "Extension: %s=<%s>", name, value);
|
||||
|
||||
@@ -313,6 +311,9 @@ SSH_PACKET_CALLBACK(ssh_packet_ext_info)
|
||||
if (rc == 1) {
|
||||
session->extensions |= SSH_EXT_SIG_RSA_SHA256;
|
||||
}
|
||||
} else if (strcmp(name, "publickey-hostbound@openssh.com") == 0) {
|
||||
SSH_LOG(SSH_LOG_PACKET, "Extension: %s=<%s>", name, value);
|
||||
session->extensions |= SSH_EXT_PUBLICKEY_HOSTBOUND;
|
||||
} else {
|
||||
SSH_LOG(SSH_LOG_PACKET, "Unknown extension: %s", name);
|
||||
}
|
||||
|
||||
@@ -229,11 +229,13 @@ static int ssh_server_send_extensions(ssh_session session) {
|
||||
}
|
||||
|
||||
rc = ssh_buffer_pack(session->out_buffer,
|
||||
"bdss",
|
||||
"bdssss",
|
||||
SSH2_MSG_EXT_INFO,
|
||||
1, /* nr. of extensions */
|
||||
2, /* nr. of extensions */
|
||||
"server-sig-algs",
|
||||
hostkey_algorithms);
|
||||
hostkey_algorithms,
|
||||
"publickey-hostbound@openssh.com",
|
||||
"0");
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user