feat: add gssapi key exchange

feat: add generic functions for importing name and initializing ctx

feat: add suffix to gsskex algs dynamically

feat: move gssapi key exchange to another file

feat: add gssapi key exchange for server

refactor: remove unnecessary fields in gssapi struct

refactor: add some documentation and improve logging

fix: remove gss_dh callbacks

feat: add a check to see if GSSAPI is configured correctly

fix: memory leaks

feat: add client side "gssapi-keyex" auth

feat: add gssapi_key_exchange_algs for server

fix: some memory issues

feat: add gssapi kex options to config

feat: add check to see if GSSAPI key exchange was performed

feat: add more tests for gssapi key exchange

fix: add valgrind supp

Signed-off-by: Gauravsingh Sisodia <xaerru@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
This commit is contained in:
Gauravsingh Sisodia
2024-07-17 05:49:24 +00:00
committed by Jakub Jelen
parent 701a2155a7
commit bc5211d055
39 changed files with 2100 additions and 154 deletions

View File

@@ -52,6 +52,7 @@ typedef struct ssh_kbdint_struct* ssh_kbdint;
ssh_kbdint ssh_kbdint_new(void); ssh_kbdint ssh_kbdint_new(void);
void ssh_kbdint_clean(ssh_kbdint kbd); void ssh_kbdint_clean(ssh_kbdint kbd);
void ssh_kbdint_free(ssh_kbdint kbd); void ssh_kbdint_free(ssh_kbdint kbd);
int ssh_userauth_gssapi_keyex(ssh_session session);
/** @internal /** @internal
* States of authentication in the client-side. They describe * States of authentication in the client-side. They describe
@@ -88,6 +89,8 @@ enum ssh_auth_state_e {
SSH_AUTH_STATE_PASSWORD_AUTH_SENT, SSH_AUTH_STATE_PASSWORD_AUTH_SENT,
/** We have sent a request without auth information (method 'none') */ /** We have sent a request without auth information (method 'none') */
SSH_AUTH_STATE_AUTH_NONE_SENT, SSH_AUTH_STATE_AUTH_NONE_SENT,
/** We have sent the MIC and expecting to be authenticated */
SSH_AUTH_STATE_GSSAPI_KEYEX_MIC_SENT,
}; };
/** @internal /** @internal

View File

@@ -54,6 +54,8 @@ struct ssh_bind_struct {
char *pubkey_accepted_key_types; char *pubkey_accepted_key_types;
char* moduli_file; char* moduli_file;
int rsa_min_size; int rsa_min_size;
bool gssapi_key_exchange;
char *gssapi_key_exchange_algs;
}; };
struct ssh_poll_handle_struct *ssh_bind_get_poll(struct ssh_bind_struct struct ssh_poll_handle_struct *ssh_bind_get_poll(struct ssh_bind_struct

View File

@@ -69,6 +69,8 @@ enum ssh_config_opcode_e {
SOC_CERTIFICATE, SOC_CERTIFICATE,
SOC_REQUIRED_RSA_SIZE, SOC_REQUIRED_RSA_SIZE,
SOC_ADDRESSFAMILY, SOC_ADDRESSFAMILY,
SOC_GSSAPIKEYEXCHANGE,
SOC_GSSAPIKEXALGORITHMS,
SOC_MAX /* Keep this one last in the list */ SOC_MAX /* Keep this one last in the list */
}; };

View File

@@ -95,6 +95,10 @@ enum ssh_key_exchange_e {
/* mlkem1024nistp384-sha384 */ /* mlkem1024nistp384-sha384 */
SSH_KEX_MLKEM1024NISTP384_SHA384, SSH_KEX_MLKEM1024NISTP384_SHA384,
#endif /* HAVE_MLKEM */ #endif /* HAVE_MLKEM */
/* gss-group14-sha256-* */
SSH_GSS_KEX_DH_GROUP14_SHA256,
/* gss-group16-sha512-* */
SSH_GSS_KEX_DH_GROUP16_SHA512,
}; };
enum ssh_cipher_e { enum ssh_cipher_e {

35
include/libssh/dh-gss.h Normal file
View File

@@ -0,0 +1,35 @@
/*
* dh-gss.h - diffie-hellman GSSAPI key exchange
*
* This file is part of the SSH Library
*
* Copyright (c) 2024 by Gauravsingh Sisodia <xaerru@gmail.com>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#ifndef DH_GSS_H_
#define DH_GSS_H_
#include "config.h"
#ifdef WITH_GSSAPI
int ssh_client_gss_dh_init(ssh_session session);
void ssh_server_gss_dh_init(ssh_session session);
int ssh_server_gss_dh_process_init(ssh_session session, ssh_buffer packet);
void ssh_client_gss_dh_remove_callbacks(ssh_session session);
#endif /* WITH_GSSAPI */
#endif /* DH_GSS_H_ */

View File

@@ -29,6 +29,9 @@
/* all OID begin with the tag identifier + length */ /* all OID begin with the tag identifier + length */
#define SSH_OID_TAG 06 #define SSH_OID_TAG 06
#define GSSAPI_KEY_EXCHANGE_SUPPORTED \
"gss-group14-sha256-,gss-group16-sha512-,"
typedef struct ssh_gssapi_struct *ssh_gssapi; typedef struct ssh_gssapi_struct *ssh_gssapi;
#ifdef __cplusplus #ifdef __cplusplus
@@ -44,14 +47,12 @@ enum ssh_gssapi_state_e {
struct ssh_gssapi_struct{ struct ssh_gssapi_struct{
enum ssh_gssapi_state_e state; /* current state */ enum ssh_gssapi_state_e state; /* current state */
struct gss_OID_desc_struct mech; /* mechanism being elected for auth */
gss_cred_id_t server_creds; /* credentials of server */ gss_cred_id_t server_creds; /* credentials of server */
gss_cred_id_t client_creds; /* creds delegated by the client */ gss_cred_id_t client_creds; /* creds delegated by the client */
gss_ctx_id_t ctx; /* the authentication context */ gss_ctx_id_t ctx; /* the authentication context */
gss_name_t client_name; /* Identity of the client */ gss_name_t client_name; /* Identity of the client */
char *user; /* username of client */ char *user; /* username of client */
char *canonic_user; /* canonic form of the client's username */ char *canonic_user; /* canonic form of the client's username */
char *service; /* name of the service */
struct { struct {
gss_name_t server_name; /* identity of server */ gss_name_t server_name; /* identity of server */
OM_uint32 flags; /* flags used for init context */ OM_uint32 flags; /* flags used for init context */
@@ -65,6 +66,7 @@ struct ssh_gssapi_struct{
int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n_oid, ssh_string *oids); int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n_oid, ssh_string *oids);
SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server); SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server);
SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_mic); SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_mic);
int ssh_gssapi_server_oids(gss_OID_set *selected);
#endif /* WITH_SERVER */ #endif /* WITH_SERVER */
SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token); SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token);
@@ -76,7 +78,17 @@ int ssh_gssapi_init(ssh_session session);
void ssh_gssapi_log_error(int verb, const char *msg_a, int maj_stat, int min_stat); void ssh_gssapi_log_error(int verb, const char *msg_a, int maj_stat, int min_stat);
int ssh_gssapi_auth_mic(ssh_session session); int ssh_gssapi_auth_mic(ssh_session session);
void ssh_gssapi_free(ssh_session session); void ssh_gssapi_free(ssh_session session);
int ssh_gssapi_client_identity(ssh_session session, gss_OID_set *valid_oids);
char *ssh_gssapi_name_to_char(gss_name_t name); char *ssh_gssapi_name_to_char(gss_name_t name);
int ssh_gssapi_import_name(struct ssh_gssapi_struct *gssapi, const char *host);
OM_uint32 ssh_gssapi_init_ctx(struct ssh_gssapi_struct *gssapi,
gss_buffer_desc *input_token,
gss_buffer_desc *output_token,
OM_uint32 *ret_flags);
char *ssh_gssapi_oid_hash(ssh_string oid);
char *ssh_gssapi_kex_mechs(ssh_session session, const char *gss_algs);
int ssh_gssapi_check_client_config(ssh_session session);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@@ -152,13 +152,14 @@ enum ssh_auth_e {
}; };
/* auth flags */ /* auth flags */
#define SSH_AUTH_METHOD_UNKNOWN 0x0000u #define SSH_AUTH_METHOD_UNKNOWN 0x0000u
#define SSH_AUTH_METHOD_NONE 0x0001u #define SSH_AUTH_METHOD_NONE 0x0001u
#define SSH_AUTH_METHOD_PASSWORD 0x0002u #define SSH_AUTH_METHOD_PASSWORD 0x0002u
#define SSH_AUTH_METHOD_PUBLICKEY 0x0004u #define SSH_AUTH_METHOD_PUBLICKEY 0x0004u
#define SSH_AUTH_METHOD_HOSTBASED 0x0008u #define SSH_AUTH_METHOD_HOSTBASED 0x0008u
#define SSH_AUTH_METHOD_INTERACTIVE 0x0010u #define SSH_AUTH_METHOD_INTERACTIVE 0x0010u
#define SSH_AUTH_METHOD_GSSAPI_MIC 0x0020u #define SSH_AUTH_METHOD_GSSAPI_MIC 0x0020u
#define SSH_AUTH_METHOD_GSSAPI_KEYEX 0x0040u
/* messages */ /* messages */
enum ssh_requests_e { enum ssh_requests_e {
@@ -429,6 +430,8 @@ enum ssh_options_e {
SSH_OPTIONS_PROXYJUMP_CB_LIST_APPEND, SSH_OPTIONS_PROXYJUMP_CB_LIST_APPEND,
SSH_OPTIONS_PKI_CONTEXT, SSH_OPTIONS_PKI_CONTEXT,
SSH_OPTIONS_ADDRESS_FAMILY, SSH_OPTIONS_ADDRESS_FAMILY,
SSH_OPTIONS_GSSAPI_KEY_EXCHANGE,
SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS,
}; };
enum { enum {

View File

@@ -59,6 +59,8 @@ enum ssh_bind_options_e {
SSH_BIND_OPTIONS_MODULI, SSH_BIND_OPTIONS_MODULI,
SSH_BIND_OPTIONS_RSA_MIN_SIZE, SSH_BIND_OPTIONS_RSA_MIN_SIZE,
SSH_BIND_OPTIONS_IMPORT_KEY_STR, SSH_BIND_OPTIONS_IMPORT_KEY_STR,
SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE,
SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS,
}; };
typedef struct ssh_bind_struct* ssh_bind; typedef struct ssh_bind_struct* ssh_bind;

View File

@@ -67,7 +67,8 @@ enum ssh_pending_call_e {
SSH_PENDING_CALL_AUTH_AGENT, SSH_PENDING_CALL_AUTH_AGENT,
SSH_PENDING_CALL_AUTH_KBDINT_INIT, SSH_PENDING_CALL_AUTH_KBDINT_INIT,
SSH_PENDING_CALL_AUTH_KBDINT_SEND, SSH_PENDING_CALL_AUTH_KBDINT_SEND,
SSH_PENDING_CALL_AUTH_GSSAPI_MIC SSH_PENDING_CALL_AUTH_GSSAPI_MIC,
SSH_PENDING_CALL_AUTH_GSSAPI_KEYEX
}; };
/* libssh calls may block an undefined amount of time */ /* libssh calls may block an undefined amount of time */
@@ -201,6 +202,8 @@ struct ssh_session_struct {
*/ */
bool first_kex_follows_guess_wrong; bool first_kex_follows_guess_wrong;
ssh_string gssapi_key_exchange_mic;
ssh_buffer in_hashbuf; ssh_buffer in_hashbuf;
ssh_buffer out_hashbuf; ssh_buffer out_hashbuf;
struct ssh_crypto_struct *current_crypto; struct ssh_crypto_struct *current_crypto;
@@ -265,6 +268,8 @@ struct ssh_session_struct {
char compressionlevel; char compressionlevel;
char *gss_server_identity; char *gss_server_identity;
char *gss_client_identity; char *gss_client_identity;
bool gssapi_key_exchange;
char *gssapi_key_exchange_algs;
int gss_delegate_creds; int gss_delegate_creds;
int flags; int flags;
int exp_flags; int exp_flags;

View File

@@ -39,6 +39,14 @@
#define SSH2_MSG_USERAUTH_GSSAPI_ERRTOK 65 #define SSH2_MSG_USERAUTH_GSSAPI_ERRTOK 65
#define SSH2_MSG_USERAUTH_GSSAPI_MIC 66 #define SSH2_MSG_USERAUTH_GSSAPI_MIC 66
#define SSH2_MSG_KEXGSS_INIT 30
#define SSH2_MSG_KEXGSS_CONTINUE 31
#define SSH2_MSG_KEXGSS_COMPLETE 32
#define SSH2_MSG_KEXGSS_HOSTKEY 33
#define SSH2_MSG_KEXGSS_ERROR 34
#define SSH2_MSG_KEXGSS_GROUPREQ 40
#define SSH2_MSG_KEXGSS_GROUP 41
#define SSH2_MSG_GLOBAL_REQUEST 80 #define SSH2_MSG_GLOBAL_REQUEST 80
#define SSH2_MSG_REQUEST_SUCCESS 81 #define SSH2_MSG_REQUEST_SUCCESS 81
#define SSH2_MSG_REQUEST_FAILURE 82 #define SSH2_MSG_REQUEST_FAILURE 82

View File

@@ -286,6 +286,7 @@ if (WITH_GSSAPI AND GSSAPI_FOUND)
set(libssh_SRCS set(libssh_SRCS
${libssh_SRCS} ${libssh_SRCS}
gssapi.c gssapi.c
dh-gss.c
) )
endif (WITH_GSSAPI AND GSSAPI_FOUND) endif (WITH_GSSAPI AND GSSAPI_FOUND)

View File

@@ -45,6 +45,7 @@
#include "libssh/pki.h" #include "libssh/pki.h"
#include "libssh/gssapi.h" #include "libssh/gssapi.h"
#include "libssh/legacy.h" #include "libssh/legacy.h"
#include "libssh/gssapi.h"
/** /**
* @defgroup libssh_auth The SSH authentication functions * @defgroup libssh_auth The SSH authentication functions
@@ -88,6 +89,7 @@ static int ssh_auth_response_termination(void *user)
case SSH_AUTH_STATE_GSSAPI_REQUEST_SENT: case SSH_AUTH_STATE_GSSAPI_REQUEST_SENT:
case SSH_AUTH_STATE_GSSAPI_TOKEN: case SSH_AUTH_STATE_GSSAPI_TOKEN:
case SSH_AUTH_STATE_GSSAPI_MIC_SENT: case SSH_AUTH_STATE_GSSAPI_MIC_SENT:
case SSH_AUTH_STATE_GSSAPI_KEYEX_MIC_SENT:
case SSH_AUTH_STATE_PUBKEY_AUTH_SENT: case SSH_AUTH_STATE_PUBKEY_AUTH_SENT:
case SSH_AUTH_STATE_PUBKEY_OFFER_SENT: case SSH_AUTH_STATE_PUBKEY_OFFER_SENT:
case SSH_AUTH_STATE_PASSWORD_AUTH_SENT: case SSH_AUTH_STATE_PASSWORD_AUTH_SENT:
@@ -121,6 +123,9 @@ static const char *ssh_auth_get_current_method(ssh_session session)
case SSH_AUTH_METHOD_GSSAPI_MIC: case SSH_AUTH_METHOD_GSSAPI_MIC:
method = "gssapi"; method = "gssapi";
break; break;
case SSH_AUTH_METHOD_GSSAPI_KEYEX:
method = "gssapi-keyex";
break;
default: default:
break; break;
} }
@@ -175,6 +180,7 @@ static int ssh_userauth_get_response(ssh_session session)
case SSH_AUTH_STATE_GSSAPI_REQUEST_SENT: case SSH_AUTH_STATE_GSSAPI_REQUEST_SENT:
case SSH_AUTH_STATE_GSSAPI_TOKEN: case SSH_AUTH_STATE_GSSAPI_TOKEN:
case SSH_AUTH_STATE_GSSAPI_MIC_SENT: case SSH_AUTH_STATE_GSSAPI_MIC_SENT:
case SSH_AUTH_STATE_GSSAPI_KEYEX_MIC_SENT:
case SSH_AUTH_STATE_PUBKEY_OFFER_SENT: case SSH_AUTH_STATE_PUBKEY_OFFER_SENT:
case SSH_AUTH_STATE_PUBKEY_AUTH_SENT: case SSH_AUTH_STATE_PUBKEY_AUTH_SENT:
case SSH_AUTH_STATE_PASSWORD_AUTH_SENT: case SSH_AUTH_STATE_PASSWORD_AUTH_SENT:
@@ -272,6 +278,9 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_failure) {
if (strstr(auth_methods, "gssapi-with-mic") != NULL) { if (strstr(auth_methods, "gssapi-with-mic") != NULL) {
session->auth.supported_methods |= SSH_AUTH_METHOD_GSSAPI_MIC; session->auth.supported_methods |= SSH_AUTH_METHOD_GSSAPI_MIC;
} }
if (strstr(auth_methods, "gssapi-keyex") != NULL) {
session->auth.supported_methods |= SSH_AUTH_METHOD_GSSAPI_KEYEX;
}
end: end:
session->auth.current_method = SSH_AUTH_METHOD_UNKNOWN; session->auth.current_method = SSH_AUTH_METHOD_UNKNOWN;
@@ -2446,4 +2455,130 @@ pending:
return rc; return rc;
} }
/**
* @brief Try to authenticate through the "gssapi-with-keyex" method.
*
* @param[in] session The ssh session to use.
*
* @returns
* - `SSH_AUTH_ERROR`: A serious error happened.
* - `SSH_AUTH_DENIED`: Authentication failed : use another method.
* - `SSH_AUTH_PARTIAL`: You've been partially authenticated, you still
* have to use another method.
* - `SSH_AUTH_SUCCESS`: Authentication success.
* - `SSH_AUTH_AGAIN`: In nonblocking mode, you've got to call this again
* later.
*/
int ssh_userauth_gssapi_keyex(ssh_session session)
{
int rc = SSH_AUTH_DENIED;
#ifdef WITH_GSSAPI
ssh_buffer buf = ssh_buffer_new();
gss_buffer_desc mic_buf = GSS_C_EMPTY_BUFFER;
OM_uint32 maj_stat, min_stat;
gss_buffer_desc mic_token_buf = GSS_C_EMPTY_BUFFER;
switch(session->pending_call_state) {
case SSH_PENDING_CALL_NONE:
break;
case SSH_PENDING_CALL_AUTH_GSSAPI_KEYEX:
goto pending;
default:
ssh_set_error(session,
SSH_FATAL,
"Wrong state (%d) during pending SSH call",
session->pending_call_state);
return SSH_ERROR;
}
/* Check if GSSAPI Key exchange was performed */
switch (session->current_crypto->kex_type) {
case SSH_GSS_KEX_DH_GROUP14_SHA256:
case SSH_GSS_KEX_DH_GROUP16_SHA512:
break;
default:
ssh_set_error(session,
SSH_FATAL,
"Attempt to authenticate with \"gssapi-keyex\" without doing GSSAPI Key exchange.");
return SSH_ERROR;
}
rc = ssh_userauth_request_service(session);
if (rc == SSH_AGAIN) {
return SSH_AUTH_AGAIN;
} else if (rc == SSH_ERROR) {
return SSH_AUTH_ERROR;
}
SSH_LOG(SSH_LOG_DEBUG, "Authenticating with gssapi-with-keyex");
session->auth.current_method = SSH_AUTH_METHOD_GSSAPI_KEYEX;
session->auth.state = SSH_AUTH_STATE_NONE;
session->pending_call_state = SSH_PENDING_CALL_AUTH_GSSAPI_KEYEX;
rc = ssh_buffer_pack(buf,
"dPbsss",
session->current_crypto->session_id_len,
session->current_crypto->session_id_len,
session->current_crypto->session_id,
SSH2_MSG_USERAUTH_REQUEST,
session->opts.username,
"ssh-connection",
"gssapi-keyex");
if (rc != SSH_OK) {
ssh_set_error_oom(session);
session->auth.state = SSH_AUTH_STATE_NONE;
session->pending_call_state = SSH_PENDING_CALL_NONE;
return rc;
}
mic_buf.length = ssh_buffer_get_len(buf);
mic_buf.value = ssh_buffer_get(buf);
maj_stat = gss_get_mic(&min_stat,session->gssapi->ctx, GSS_C_QOP_DEFAULT,
&mic_buf, &mic_token_buf);
if (GSS_ERROR(maj_stat)) {
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"generating MIC",
maj_stat,
min_stat);
session->auth.state = SSH_AUTH_STATE_NONE;
session->pending_call_state = SSH_PENDING_CALL_NONE;
return SSH_ERROR;
}
SSH_BUFFER_FREE(buf);
rc = ssh_buffer_pack(session->out_buffer,
"bsssdP",
SSH2_MSG_USERAUTH_REQUEST,
session->opts.username,
"ssh-connection",
"gssapi-keyex",
mic_token_buf.length,
(size_t)mic_token_buf.length,
mic_token_buf.value);
if (rc != SSH_OK) {
ssh_set_error_oom(session);
session->auth.state = SSH_AUTH_STATE_NONE;
session->pending_call_state = SSH_PENDING_CALL_NONE;
gss_release_buffer(&min_stat, &mic_token_buf);
return rc;
}
gss_release_buffer(&min_stat, &mic_token_buf);
session->auth.state = SSH_AUTH_STATE_GSSAPI_KEYEX_MIC_SENT;
ssh_packet_send(session);
pending:
rc = ssh_userauth_get_response(session);
if (rc != SSH_AUTH_AGAIN) {
session->pending_call_state = SSH_PENDING_CALL_NONE;
}
#else
(void) session; /* unused */
#endif
return rc;
}
/** @} */ /** @} */

View File

@@ -386,6 +386,7 @@ void ssh_bind_free(ssh_bind sshbind){
SAFE_FREE(sshbind->rsakey); SAFE_FREE(sshbind->rsakey);
SAFE_FREE(sshbind->ecdsakey); SAFE_FREE(sshbind->ecdsakey);
SAFE_FREE(sshbind->ed25519key); SAFE_FREE(sshbind->ed25519key);
SAFE_FREE(sshbind->gssapi_key_exchange_algs);
ssh_key_free(sshbind->rsa); ssh_key_free(sshbind->rsa);
sshbind->rsa = NULL; sshbind->rsa = NULL;
@@ -463,6 +464,15 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd)
} }
session->common.log_verbosity = sshbind->common.log_verbosity; session->common.log_verbosity = sshbind->common.log_verbosity;
session->opts.gssapi_key_exchange = sshbind->gssapi_key_exchange;
if (sshbind->gssapi_key_exchange_algs != NULL) {
session->opts.gssapi_key_exchange_algs = strdup(sshbind->gssapi_key_exchange_algs);
if (session->opts.gssapi_key_exchange_algs == NULL) {
ssh_set_error_oom(sshbind);
return SSH_ERROR;
}
}
if (sshbind->banner != NULL) { if (sshbind->banner != NULL) {
session->server_opts.custombanner = strdup(sshbind->banner); session->server_opts.custombanner = strdup(sshbind->banner);

View File

@@ -38,6 +38,7 @@
#include "libssh/socket.h" #include "libssh/socket.h"
#include "libssh/session.h" #include "libssh/session.h"
#include "libssh/dh.h" #include "libssh/dh.h"
#include "libssh/dh-gss.h"
#ifdef WITH_GEX #ifdef WITH_GEX
#include "libssh/dh-gex.h" #include "libssh/dh-gex.h"
#endif /* WITH_GEX */ #endif /* WITH_GEX */
@@ -266,7 +267,13 @@ int dh_handshake(ssh_session session)
switch (session->dh_handshake_state) { switch (session->dh_handshake_state) {
case DH_STATE_INIT: case DH_STATE_INIT:
switch (session->next_crypto->kex_type) { switch(session->next_crypto->kex_type){
#ifdef WITH_GSSAPI
case SSH_GSS_KEX_DH_GROUP14_SHA256:
case SSH_GSS_KEX_DH_GROUP16_SHA512:
rc = ssh_client_gss_dh_init(session);
break;
#endif
case SSH_KEX_DH_GROUP1_SHA1: case SSH_KEX_DH_GROUP1_SHA1:
case SSH_KEX_DH_GROUP14_SHA1: case SSH_KEX_DH_GROUP14_SHA1:
case SSH_KEX_DH_GROUP14_SHA256: case SSH_KEX_DH_GROUP14_SHA256:

View File

@@ -155,6 +155,8 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
{"xauthlocation", SOC_NA, true}, {"xauthlocation", SOC_NA, true},
{"pubkeyacceptedkeytypes", SOC_PUBKEYACCEPTEDKEYTYPES, true}, {"pubkeyacceptedkeytypes", SOC_PUBKEYACCEPTEDKEYTYPES, true},
{"requiredrsasize", SOC_REQUIRED_RSA_SIZE, true}, {"requiredrsasize", SOC_REQUIRED_RSA_SIZE, true},
{"gssapikeyexchange", SOC_GSSAPIKEYEXCHANGE, true},
{"gssapikexalgorithms", SOC_GSSAPIKEXALGORITHMS, true},
{NULL, SOC_UNKNOWN, false}, {NULL, SOC_UNKNOWN, false},
}; };
@@ -1557,6 +1559,23 @@ static int ssh_config_parse_line_internal(ssh_session session,
ssh_options_set(session, SSH_OPTIONS_CERTIFICATE, p); ssh_options_set(session, SSH_OPTIONS_CERTIFICATE, p);
} }
break; break;
case SOC_GSSAPIKEYEXCHANGE: {
bool b = false;
i = ssh_config_get_yesno(&s, -1);
CHECK_COND_OR_FAIL(i < 0, "Invalid argument");
if (*parsing) {
bool b = (i == 1) ? true : false;
ssh_options_set(session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE, &b);
}
break;
}
case SOC_GSSAPIKEXALGORITHMS:
p = ssh_config_get_str_tok(&s, NULL);
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS, p);
}
break;
case SOC_REQUIRED_RSA_SIZE: case SOC_REQUIRED_RSA_SIZE:
l = ssh_config_get_long(&s, -1); l = ssh_config_get_long(&s, -1);
CHECK_COND_OR_FAIL(l < 0, "Invalid argument"); CHECK_COND_OR_FAIL(l < 0, "Invalid argument");

439
src/dh-gss.c Normal file
View File

@@ -0,0 +1,439 @@
/*
* dh-gss.c - diffie-hellman GSSAPI key exchange
*
* This file is part of the SSH Library
*
* Copyright (c) 2024 by Gauravsingh Sisodia <xaerru@gmail.com>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include <stdio.h>
#include <gssapi/gssapi.h>
#include <errno.h>
#include "libssh/gssapi.h"
#include "libssh/priv.h"
#include "libssh/crypto.h"
#include "libssh/buffer.h"
#include "libssh/session.h"
#include "libssh/dh.h"
#include "libssh/ssh2.h"
#include "libssh/dh-gss.h"
static SSH_PACKET_CALLBACK(ssh_packet_client_gss_dh_reply);
static ssh_packet_callback gss_dh_client_callbacks[]= {
ssh_packet_client_gss_dh_reply
};
static struct ssh_packet_callbacks_struct ssh_gss_dh_client_callbacks = {
.start = SSH2_MSG_KEXGSS_COMPLETE,
.n_callbacks = 1,
.callbacks = gss_dh_client_callbacks,
.user = NULL
};
/** @internal
* @brief Starts gssapi key exchange
*/
int ssh_client_gss_dh_init(ssh_session session){
struct ssh_crypto_struct *crypto = session->next_crypto;
#if !defined(HAVE_LIBCRYPTO) || OPENSSL_VERSION_NUMBER < 0x30000000L
const_bignum pubkey;
#else
bignum pubkey = NULL;
#endif /* OPENSSL_VERSION_NUMBER */
int rc;
gss_OID_set selected = GSS_C_NO_OID_SET; /* oid selected for authentication */
OM_uint32 maj_stat, min_stat;
const char *gss_host = session->opts.host;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
OM_uint32 oflags;
rc = ssh_dh_init_common(crypto);
if (rc == SSH_ERROR) {
goto error;
}
rc = ssh_dh_keypair_gen_keys(crypto->dh_ctx, DH_CLIENT_KEYPAIR);
if (rc == SSH_ERROR) {
goto error;
}
rc = ssh_dh_keypair_get_keys(crypto->dh_ctx, DH_CLIENT_KEYPAIR, NULL, &pubkey);
if (rc != SSH_OK) {
goto error;
}
rc = ssh_gssapi_init(session);
if (rc == SSH_ERROR) {
goto error;
}
if (session->opts.gss_server_identity != NULL) {
gss_host = session->opts.gss_server_identity;
}
rc = ssh_gssapi_import_name(session->gssapi, gss_host);
if (rc != SSH_OK) {
goto error;
}
rc = ssh_gssapi_client_identity(session, &selected);
if (rc == SSH_ERROR) {
goto error;
}
session->gssapi->client.flags = GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG;
maj_stat = ssh_gssapi_init_ctx(session->gssapi, &input_token, &output_token, &oflags);
gss_release_oid_set(&min_stat, &selected);
if (GSS_ERROR(maj_stat)) {
ssh_gssapi_log_error(SSH_LOG_WARN,
"Initializing gssapi context",
maj_stat,
min_stat);
goto error;
}
if (!(oflags & GSS_C_INTEG_FLAG) || !(oflags & GSS_C_MUTUAL_FLAG)) {
SSH_LOG(SSH_LOG_WARN, "GSSAPI(init) integrity and mutual flags were not set");
goto error;
}
rc = ssh_buffer_pack(session->out_buffer, "bdPB",
SSH2_MSG_KEXGSS_INIT,
output_token.length,
(size_t)output_token.length,
output_token.value,
pubkey);
if (rc != SSH_OK) {
goto error;
}
gss_release_buffer(&min_stat, &output_token);
#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
bignum_safe_free(pubkey);
#endif
/* register the packet callbacks */
ssh_packet_set_callbacks(session, &ssh_gss_dh_client_callbacks);
session->dh_handshake_state = DH_STATE_INIT_SENT;
rc = ssh_packet_send(session);
return rc;
error:
#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
bignum_safe_free(pubkey);
#endif
ssh_dh_cleanup(crypto);
return SSH_ERROR;
}
void ssh_client_gss_dh_remove_callbacks(ssh_session session)
{
ssh_packet_remove_callbacks(session, &ssh_gss_dh_client_callbacks);
}
SSH_PACKET_CALLBACK(ssh_packet_client_gss_dh_reply){
struct ssh_crypto_struct *crypto=session->next_crypto;
ssh_string pubkey_blob = NULL, mic = NULL, otoken = NULL;
uint8_t b;
bignum server_pubkey;
int rc;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
OM_uint32 oflags;
OM_uint32 maj_stat;
(void)type;
(void)user;
ssh_client_gss_dh_remove_callbacks(session);
rc = ssh_buffer_unpack(packet,
"BSbS",
&server_pubkey,
&mic,
&b,
&otoken);
if (rc == SSH_ERROR) {
goto error;
}
session->gssapi_key_exchange_mic = mic;
input_token.length = ssh_string_len(otoken);
input_token.value = ssh_string_data(otoken);
maj_stat = ssh_gssapi_init_ctx(session->gssapi, &input_token, &output_token, &oflags);
if (maj_stat != GSS_S_COMPLETE) {
goto error;
}
SSH_STRING_FREE(otoken);
rc = ssh_dh_keypair_set_keys(crypto->dh_ctx, DH_SERVER_KEYPAIR,
NULL, server_pubkey);
if (rc != SSH_OK) {
SSH_STRING_FREE(pubkey_blob);
bignum_safe_free(server_pubkey);
goto error;
}
rc = ssh_dh_compute_shared_secret(session->next_crypto->dh_ctx,
DH_CLIENT_KEYPAIR, DH_SERVER_KEYPAIR,
&session->next_crypto->shared_secret);
ssh_dh_debug_crypto(session->next_crypto);
if (rc == SSH_ERROR){
ssh_set_error(session, SSH_FATAL, "Could not generate shared secret");
goto error;
}
/* Send the MSG_NEWKEYS */
rc = ssh_packet_send_newkeys(session);
if (rc == SSH_ERROR) {
goto error;
}
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
return SSH_PACKET_USED;
error:
ssh_dh_cleanup(session->next_crypto);
session->session_state=SSH_SESSION_STATE_ERROR;
return SSH_PACKET_USED;
}
#ifdef WITH_SERVER
static SSH_PACKET_CALLBACK(ssh_packet_server_gss_dh_init);
static ssh_packet_callback gss_dh_server_callbacks[] = {
ssh_packet_server_gss_dh_init,
};
static struct ssh_packet_callbacks_struct ssh_gss_dh_server_callbacks = {
.start = SSH2_MSG_KEXGSS_INIT,
.n_callbacks = 1,
.callbacks = gss_dh_server_callbacks,
.user = NULL
};
/** @internal
* @brief sets up the gssapi kex callbacks
*/
void ssh_server_gss_dh_init(ssh_session session){
/* register the packet callbacks */
ssh_packet_set_callbacks(session, &ssh_gss_dh_server_callbacks);
ssh_dh_init_common(session->next_crypto);
}
/** @internal
* @brief processes a SSH_MSG_KEXGSS_INIT and sends
* the appropriate SSH_MSG_KEXGSS_COMPLETE
*/
int ssh_server_gss_dh_process_init(ssh_session session, ssh_buffer packet)
{
struct ssh_crypto_struct *crypto = session->next_crypto;
ssh_key privkey = NULL;
enum ssh_digest_e digest = SSH_DIGEST_AUTO;
bignum client_pubkey;
#if !defined(HAVE_LIBCRYPTO) || OPENSSL_VERSION_NUMBER < 0x30000000L
const_bignum server_pubkey;
#else
bignum server_pubkey = NULL;
#endif /* OPENSSL_VERSION_NUMBER */
int rc;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
ssh_string otoken = NULL;
OM_uint32 maj_stat, min_stat;
gss_name_t client_name = GSS_C_NO_NAME;
OM_uint32 ret_flags=0;
gss_buffer_desc mic = GSS_C_EMPTY_BUFFER, msg = GSS_C_EMPTY_BUFFER;
char hostname[NI_MAXHOST] = {0};
char err_msg[SSH_ERRNO_MSG_MAX] = {0};
rc = ssh_buffer_unpack(packet, "S", &otoken);
if (rc == SSH_ERROR) {
ssh_set_error(session, SSH_FATAL, "No token in client request");
goto error;
}
input_token.length = ssh_string_len(otoken);
input_token.value = ssh_string_data(otoken);
rc = ssh_buffer_unpack(packet, "B", &client_pubkey);
if (rc == SSH_ERROR) {
ssh_set_error(session, SSH_FATAL, "No e number in client request");
goto error;
}
rc = ssh_dh_keypair_set_keys(crypto->dh_ctx, DH_CLIENT_KEYPAIR,
NULL, client_pubkey);
if (rc != SSH_OK) {
bignum_safe_free(client_pubkey);
goto error;
}
rc = ssh_dh_keypair_gen_keys(crypto->dh_ctx, DH_SERVER_KEYPAIR);
if (rc == SSH_ERROR) {
goto error;
}
rc = ssh_get_key_params(session, &privkey, &digest);
if (rc != SSH_OK) {
goto error;
}
rc = ssh_dh_compute_shared_secret(crypto->dh_ctx,
DH_SERVER_KEYPAIR, DH_CLIENT_KEYPAIR,
&crypto->shared_secret);
ssh_dh_debug_crypto(crypto);
if (rc == SSH_ERROR) {
ssh_set_error(session, SSH_FATAL, "Could not generate shared secret");
goto error;
}
rc = ssh_make_sessionid(session);
if (rc != SSH_OK) {
ssh_set_error(session, SSH_FATAL, "Could not create a session id");
goto error;
}
rc = ssh_dh_keypair_get_keys(crypto->dh_ctx, DH_SERVER_KEYPAIR,
NULL, &server_pubkey);
if (rc != SSH_OK){
goto error;
}
rc = ssh_gssapi_init(session);
if (rc == SSH_ERROR) {
goto error;
}
rc = gethostname(hostname, 64);
if (rc != 0) {
SSH_LOG(SSH_LOG_TRACE,
"Error getting hostname: %s",
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
goto error;
}
rc = ssh_gssapi_import_name(session->gssapi, hostname);
if (rc != SSH_OK) {
goto error;
}
maj_stat = gss_acquire_cred(&min_stat, session->gssapi->client.server_name, 0,
GSS_C_NO_OID_SET, GSS_C_ACCEPT,
&session->gssapi->server_creds, NULL, NULL);
if (maj_stat != GSS_S_COMPLETE) {
ssh_gssapi_log_error(SSH_LOG_TRACE,
"acquiring credentials",
maj_stat,
min_stat);
goto error;
}
maj_stat = gss_accept_sec_context(&min_stat, &session->gssapi->ctx, session->gssapi->server_creds,
&input_token, GSS_C_NO_CHANNEL_BINDINGS, &client_name, NULL /*mech_oid*/, &output_token, &ret_flags,
NULL /*time*/, &session->gssapi->client_creds);
if (GSS_ERROR(maj_stat)) {
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"accepting token failed",
maj_stat,
min_stat);
goto error;
}
SSH_STRING_FREE(otoken);
gss_release_name(&min_stat, &client_name);
if (!(ret_flags & GSS_C_INTEG_FLAG) || !(ret_flags & GSS_C_MUTUAL_FLAG)) {
SSH_LOG(SSH_LOG_WARN, "GSSAPI(accept) integrity and mutual flags were not set");
goto error;
}
SSH_LOG(SSH_LOG_DEBUG, "token accepted");
msg.length = session->next_crypto->digest_len;
msg.value = session->next_crypto->secret_hash;
maj_stat = gss_get_mic(&min_stat,
session->gssapi->ctx,
GSS_C_QOP_DEFAULT,
&msg,
&mic);
if (GSS_ERROR(maj_stat)) {
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"creating mic failed",
maj_stat,
min_stat);
goto error;
}
rc = ssh_buffer_pack(session->out_buffer,
"bBdPbdP",
SSH2_MSG_KEXGSS_COMPLETE,
server_pubkey,
mic.length,
(size_t)mic.length,
mic.value,
1,
output_token.length,
(size_t)output_token.length,
output_token.value);
#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
bignum_safe_free(server_pubkey);
#endif
if(rc != SSH_OK) {
ssh_set_error_oom(session);
ssh_buffer_reinit(session->out_buffer);
goto error;
}
gss_release_buffer(&min_stat, &output_token);
gss_release_buffer(&min_stat, &mic);
rc = ssh_packet_send(session);
if (rc == SSH_ERROR) {
goto error;
}
SSH_LOG(SSH_LOG_DEBUG, "Sent SSH2_MSG_KEXGSS_COMPLETE");
session->dh_handshake_state=DH_STATE_NEWKEYS_SENT;
/* Send the MSG_NEWKEYS */
rc = ssh_packet_send_newkeys(session);
if (rc == SSH_ERROR) {
goto error;
}
return SSH_OK;
error:
#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
bignum_safe_free(server_pubkey);
#endif
session->session_state = SSH_SESSION_STATE_ERROR;
ssh_dh_cleanup(session->next_crypto);
return SSH_ERROR;
}
/** @internal
* @brief parse an incoming SSH_MSG_KEXGSS_INIT packet and complete
* Diffie-Hellman key exchange
**/
static SSH_PACKET_CALLBACK(ssh_packet_server_gss_dh_init){
(void)type;
(void)user;
SSH_LOG(SSH_LOG_DEBUG, "Received SSH_MSG_KEXGSS_INIT");
ssh_packet_remove_callbacks(session, &ssh_gss_dh_server_callbacks);
ssh_server_gss_dh_process_init(session, packet);
return SSH_PACKET_USED;
}
#endif /* WITH_SERVER */

View File

@@ -26,6 +26,10 @@
#include "config.h" #include "config.h"
#include <stdio.h> #include <stdio.h>
#ifdef WITH_GSSAPI
#include <gssapi/gssapi.h>
#include "libssh/gssapi.h"
#endif
#include "libssh/priv.h" #include "libssh/priv.h"
#include "libssh/crypto.h" #include "libssh/crypto.h"
@@ -36,6 +40,7 @@
#include "libssh/ssh2.h" #include "libssh/ssh2.h"
#include "libssh/pki.h" #include "libssh/pki.h"
#include "libssh/bignum.h" #include "libssh/bignum.h"
#include "libssh/string.h"
static unsigned char p_group1_value[] = { static unsigned char p_group1_value[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,

View File

@@ -424,9 +424,11 @@ int ssh_dh_init_common(struct ssh_crypto_struct *crypto)
break; break;
case SSH_KEX_DH_GROUP14_SHA1: case SSH_KEX_DH_GROUP14_SHA1:
case SSH_KEX_DH_GROUP14_SHA256: case SSH_KEX_DH_GROUP14_SHA256:
case SSH_GSS_KEX_DH_GROUP14_SHA256:
rc = ssh_dh_set_parameters(ctx, ssh_dh_group14, ssh_dh_generator); rc = ssh_dh_set_parameters(ctx, ssh_dh_group14, ssh_dh_generator);
break; break;
case SSH_KEX_DH_GROUP16_SHA512: case SSH_KEX_DH_GROUP16_SHA512:
case SSH_GSS_KEX_DH_GROUP16_SHA512:
rc = ssh_dh_set_parameters(ctx, ssh_dh_group16, ssh_dh_generator); rc = ssh_dh_set_parameters(ctx, ssh_dh_group16, ssh_dh_generator);
break; break;
case SSH_KEX_DH_GROUP18_SHA512: case SSH_KEX_DH_GROUP18_SHA512:

View File

@@ -253,9 +253,11 @@ int ssh_dh_init_common(struct ssh_crypto_struct *crypto)
break; break;
case SSH_KEX_DH_GROUP14_SHA1: case SSH_KEX_DH_GROUP14_SHA1:
case SSH_KEX_DH_GROUP14_SHA256: case SSH_KEX_DH_GROUP14_SHA256:
case SSH_GSS_KEX_DH_GROUP14_SHA256:
rc = ssh_dh_set_parameters(ctx, ssh_dh_group14, ssh_dh_generator); rc = ssh_dh_set_parameters(ctx, ssh_dh_group14, ssh_dh_generator);
break; break;
case SSH_KEX_DH_GROUP16_SHA512: case SSH_KEX_DH_GROUP16_SHA512:
case SSH_GSS_KEX_DH_GROUP16_SHA512:
rc = ssh_dh_set_parameters(ctx, ssh_dh_group16, ssh_dh_generator); rc = ssh_dh_set_parameters(ctx, ssh_dh_group16, ssh_dh_generator);
break; break;
case SSH_KEX_DH_GROUP18_SHA512: case SSH_KEX_DH_GROUP18_SHA512:

View File

@@ -23,6 +23,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <errno.h>
#ifdef HAVE_UNISTD_H #ifdef HAVE_UNISTD_H
#include <unistd.h> #include <unistd.h>
#endif #endif
@@ -37,6 +38,9 @@
#include <libssh/callbacks.h> #include <libssh/callbacks.h>
#include <libssh/string.h> #include <libssh/string.h>
#include <libssh/server.h> #include <libssh/server.h>
#include <libssh/token.h>
static gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"};
/** @internal /** @internal
* @initializes a gssapi context for authentication * @initializes a gssapi context for authentication
@@ -110,7 +114,6 @@ ssh_gssapi_free(ssh_session session)
gss_release_oid(&min, &session->gssapi->client.oid); gss_release_oid(&min, &session->gssapi->client.oid);
gss_delete_sec_context(&min, &session->gssapi->ctx, GSS_C_NO_BUFFER); gss_delete_sec_context(&min, &session->gssapi->ctx, GSS_C_NO_BUFFER);
SAFE_FREE(session->gssapi->mech.elements);
SAFE_FREE(session->gssapi->canonic_user); SAFE_FREE(session->gssapi->canonic_user);
SAFE_FREE(session->gssapi); SAFE_FREE(session->gssapi);
} }
@@ -147,6 +150,45 @@ static int ssh_gssapi_send_response(ssh_session session, ssh_string oid)
#ifdef WITH_SERVER #ifdef WITH_SERVER
/** @internal
* @brief get all the oids server supports
* @param[out] selected OID set of supported oids
* @returns SSH_OK if successful, SSH_ERROR otherwise
*/
int
ssh_gssapi_server_oids(gss_OID_set *selected)
{
OM_uint32 maj_stat, min_stat;
size_t i;
char *ptr = NULL;
gss_OID_set supported; /* oids supported by server */
maj_stat = gss_indicate_mechs(&min_stat, &supported);
if (maj_stat != GSS_S_COMPLETE) {
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"indicate mechs",
maj_stat,
min_stat);
return SSH_ERROR;
}
for (i=0; i < supported->count; ++i){
ptr = ssh_get_hexa(supported->elements[i].elements, supported->elements[i].length);
/* According to RFC 4462 we MUST NOT use SPNEGO */
if (supported->elements[i].length == spnego_oid.length &&
memcmp(supported->elements[i].elements, spnego_oid.elements, supported->elements[i].length) == 0) {
SAFE_FREE(ptr);
continue;
}
SSH_LOG(SSH_LOG_DEBUG, "Supported mech %zu: %s", i, ptr);
SAFE_FREE(ptr);
}
*selected = supported;
return SSH_OK;
}
/** @internal /** @internal
* @brief handles an user authentication using GSSAPI * @brief handles an user authentication using GSSAPI
*/ */
@@ -154,12 +196,9 @@ int
ssh_gssapi_handle_userauth(ssh_session session, const char *user, ssh_gssapi_handle_userauth(ssh_session session, const char *user,
uint32_t n_oid, ssh_string *oids) uint32_t n_oid, ssh_string *oids)
{ {
char service_name[] = "host"; char hostname[NI_MAXHOST] = {0};
gss_buffer_desc name_buf;
gss_name_t server_name; /* local server fqdn */
OM_uint32 maj_stat, min_stat; OM_uint32 maj_stat, min_stat;
size_t i; size_t i;
char *ptr = NULL;
gss_OID_set supported; /* oids supported by server */ gss_OID_set supported; /* oids supported by server */
gss_OID_set both_supported; /* oids supported by both client and server */ gss_OID_set both_supported; /* oids supported by both client and server */
gss_OID_set selected; /* oid selected for authentication */ gss_OID_set selected; /* oid selected for authentication */
@@ -167,12 +206,22 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
size_t oid_count=0; size_t oid_count=0;
struct gss_OID_desc_struct oid; struct gss_OID_desc_struct oid;
int rc; int rc;
char err_msg[SSH_ERRNO_MSG_MAX] = {0};
rc = gethostname(hostname, 64);
if (rc != 0) {
SSH_LOG(SSH_LOG_TRACE,
"Error getting hostname: %s",
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_ERROR;
}
/* Destroy earlier GSSAPI context if any */ /* Destroy earlier GSSAPI context if any */
ssh_gssapi_free(session); ssh_gssapi_free(session);
rc = ssh_gssapi_init(session); rc = ssh_gssapi_init(session);
if (rc == SSH_ERROR) if (rc == SSH_ERROR) {
return rc; return rc;
}
/* Callback should select oid and acquire credential */ /* Callback should select oid and acquire credential */
if (ssh_callbacks_exists(session->server_callbacks, if (ssh_callbacks_exists(session->server_callbacks,
@@ -197,23 +246,13 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
/* Default implementation for selecting oid and acquiring credential */ /* Default implementation for selecting oid and acquiring credential */
gss_create_empty_oid_set(&min_stat, &both_supported); gss_create_empty_oid_set(&min_stat, &both_supported);
maj_stat = gss_indicate_mechs(&min_stat, &supported); /* Get the server supported oids */
if (maj_stat != GSS_S_COMPLETE) { rc = ssh_gssapi_server_oids(&supported);
SSH_LOG(SSH_LOG_DEBUG, "indicate mechs %d, %d", maj_stat, min_stat); if (rc != SSH_OK) {
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"indicate mechs",
maj_stat,
min_stat);
gss_release_oid_set(&min_stat, &both_supported);
return SSH_ERROR; return SSH_ERROR;
} }
for (i=0; i < supported->count; ++i){ /* Loop through client supported oids */
ptr = ssh_get_hexa(supported->elements[i].elements, supported->elements[i].length);
SSH_LOG(SSH_LOG_DEBUG, "Supported mech %zu: %s", i, ptr);
free(ptr);
}
for (i=0 ; i< n_oid ; ++i){ for (i=0 ; i< n_oid ; ++i){
unsigned char *oid_s = (unsigned char *) ssh_string_data(oids[i]); unsigned char *oid_s = (unsigned char *) ssh_string_data(oids[i]);
size_t len = ssh_string_len(oids[i]); size_t len = ssh_string_len(oids[i]);
@@ -225,8 +264,10 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
SSH_LOG(SSH_LOG_TRACE,"GSSAPI: received invalid OID"); SSH_LOG(SSH_LOG_TRACE,"GSSAPI: received invalid OID");
continue; continue;
} }
/* Convert oid from string to gssapi format */
oid.elements = &oid_s[2]; oid.elements = &oid_s[2];
oid.length = len - 2; oid.length = len - 2;
/* Check if this client oid is supported by server */
gss_test_oid_set_member(&min_stat,&oid,supported,&present); gss_test_oid_set_member(&min_stat,&oid,supported,&present);
if(present){ if(present){
gss_add_oid_set_member(&min_stat,&oid,&both_supported); gss_add_oid_set_member(&min_stat,&oid,&both_supported);
@@ -241,28 +282,18 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
return SSH_OK; return SSH_OK;
} }
name_buf.value = service_name; rc = ssh_gssapi_import_name(session->gssapi, hostname);
name_buf.length = strlen(name_buf.value) + 1; if (rc != SSH_OK) {
maj_stat = gss_import_name(&min_stat, &name_buf, ssh_auth_reply_default(session, 0);
(gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &server_name);
if (maj_stat != GSS_S_COMPLETE) {
SSH_LOG(SSH_LOG_DEBUG, "importing name %d, %d", maj_stat, min_stat);
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"importing name",
maj_stat,
min_stat);
gss_release_oid_set(&min_stat, &both_supported); gss_release_oid_set(&min_stat, &both_supported);
return -1; return SSH_ERROR;
} }
maj_stat = gss_acquire_cred(&min_stat, server_name, 0, maj_stat = gss_acquire_cred(&min_stat, session->gssapi->client.server_name, 0,
both_supported, GSS_C_ACCEPT, both_supported, GSS_C_ACCEPT,
&session->gssapi->server_creds, &selected, NULL); &session->gssapi->server_creds, &selected, NULL);
gss_release_name(&min_stat, &server_name);
gss_release_oid_set(&min_stat, &both_supported); gss_release_oid_set(&min_stat, &both_supported);
if (maj_stat != GSS_S_COMPLETE) { if (maj_stat != GSS_S_COMPLETE) {
SSH_LOG(SSH_LOG_TRACE, "error acquiring credentials %d, %d", maj_stat, min_stat);
ssh_gssapi_log_error(SSH_LOG_TRACE, ssh_gssapi_log_error(SSH_LOG_TRACE,
"acquiring creds", "acquiring creds",
maj_stat, maj_stat,
@@ -270,8 +301,7 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
ssh_auth_reply_default(session,0); ssh_auth_reply_default(session,0);
return SSH_ERROR; return SSH_ERROR;
} }
SSH_LOG(SSH_LOG_DEBUG, "acquired credentials");
SSH_LOG(SSH_LOG_DEBUG, "acquiring credentials %d, %d", maj_stat, min_stat);
/* finding which OID from client we selected */ /* finding which OID from client we selected */
for (i=0 ; i< n_oid ; ++i){ for (i=0 ; i< n_oid ; ++i){
@@ -293,17 +323,8 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
break; break;
} }
} }
session->gssapi->mech.length = oid.length;
session->gssapi->mech.elements = malloc(oid.length);
if (session->gssapi->mech.elements == NULL){
ssh_set_error_oom(session);
gss_release_oid_set(&min_stat, &selected);
return SSH_ERROR;
}
memcpy(session->gssapi->mech.elements, oid.elements, oid.length);
gss_release_oid_set(&min_stat, &selected); gss_release_oid_set(&min_stat, &selected);
session->gssapi->user = strdup(user); session->gssapi->user = strdup(user);
session->gssapi->service = service_name;
session->gssapi->state = SSH_GSSAPI_STATE_RCV_TOKEN; session->gssapi->state = SSH_GSSAPI_STATE_RCV_TOKEN;
return ssh_gssapi_send_response(session, oids[i]); return ssh_gssapi_send_response(session, oids[i]);
} }
@@ -381,7 +402,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server)
return SSH_PACKET_USED; return SSH_PACKET_USED;
} }
hexa = ssh_get_hexa(ssh_string_data(token),ssh_string_len(token)); hexa = ssh_get_hexa(ssh_string_data(token),ssh_string_len(token));
SSH_LOG(SSH_LOG_PACKET, "GSSAPI Token : %s",hexa); SSH_LOG(SSH_LOG_PACKET, "GSSAPI Token : %s", hexa);
SAFE_FREE(hexa); SAFE_FREE(hexa);
input_token.length = ssh_string_len(token); input_token.length = ssh_string_len(token);
input_token.value = ssh_string_data(token); input_token.value = ssh_string_data(token);
@@ -400,7 +421,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server)
} }
if (GSS_ERROR(maj_stat)){ if (GSS_ERROR(maj_stat)){
ssh_gssapi_log_error(SSH_LOG_DEBUG, ssh_gssapi_log_error(SSH_LOG_DEBUG,
"Gssapi error", "accepting token failed",
maj_stat, maj_stat,
min_stat); min_stat);
gss_release_buffer(&min_stat, &output_token); gss_release_buffer(&min_stat, &output_token);
@@ -626,9 +647,185 @@ fail:
return SSH_ERROR; return SSH_ERROR;
} }
/** @brief returns the OIDs of the mechs that have usable credentials /** @internal
* @brief Get the base64 encoding of md5 of the oid to add as suffix to GSSAPI
* key exchange algorithms.
*
* @param[in] oid The OID as a ssh_string
*
* @returns the hash or NULL on error
*/ */
static int ssh_gssapi_match(ssh_session session, gss_OID_set *valid_oids) char *
ssh_gssapi_oid_hash(ssh_string oid)
{
MD5CTX ctx = NULL;
unsigned char *h = NULL;
int rc;
char *base64 = NULL;
h = calloc(MD5_DIGEST_LEN, sizeof(unsigned char));
if (h == NULL) {
return NULL;
}
ctx = md5_init();
if (ctx == NULL) {
SAFE_FREE(h);
return NULL;
}
rc = md5_update(ctx,
ssh_string_data(oid),
ssh_string_len(oid));
if (rc != SSH_OK) {
SAFE_FREE(h);
md5_ctx_free(ctx);
return NULL;
}
rc = md5_final(h, ctx);
if (rc != SSH_OK) {
SAFE_FREE(h);
return NULL;
}
base64 = (char *)bin_to_base64(h, 16);
SAFE_FREE(h);
return base64;
}
/** @internal
* @brief Check if client has GSSAPI mechanisms configured
*
* @param[in] session The SSH session
*
* @returns SSH_OK if any one of the mechanisms is configured or NULL
*/
int
ssh_gssapi_check_client_config(ssh_session session)
{
OM_uint32 maj_stat, min_stat;
size_t i;
char *ptr = NULL;
gss_OID_set supported = GSS_C_NO_OID_SET;
gss_name_t client_id = GSS_C_NO_NAME;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc namebuf = GSS_C_EMPTY_BUFFER;
OM_uint32 oflags;
struct ssh_gssapi_struct *gssapi = NULL;
int ret = SSH_ERROR;
gss_OID_set one_oidset = GSS_C_NO_OID_SET;
maj_stat = gss_indicate_mechs(&min_stat, &supported);
if (maj_stat != GSS_S_COMPLETE) {
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"indicate mechs",
maj_stat,
min_stat);
return SSH_ERROR;
}
for (i = 0; i < supported->count; ++i){
gssapi = calloc(1, sizeof(struct ssh_gssapi_struct));
gssapi->server_creds = GSS_C_NO_CREDENTIAL;
gssapi->client_creds = GSS_C_NO_CREDENTIAL;
gssapi->ctx = GSS_C_NO_CONTEXT;
gssapi->state = SSH_GSSAPI_STATE_NONE;
/* According to RFC 4462 we MUST NOT use SPNEGO */
if (supported->elements[i].length == spnego_oid.length &&
memcmp(supported->elements[i].elements, spnego_oid.elements, supported->elements[i].length) == 0) {
ret = SSH_ERROR;
goto end;
}
gss_create_empty_oid_set(&min_stat, &one_oidset);
gss_add_oid_set_member(&min_stat, &supported->elements[i], &one_oidset);
if (session->opts.gss_client_identity != NULL) {
namebuf.value = (void *)session->opts.gss_client_identity;
namebuf.length = strlen(session->opts.gss_client_identity);
maj_stat = gss_import_name(&min_stat, &namebuf,
GSS_C_NT_USER_NAME, &client_id);
if (GSS_ERROR(maj_stat)) {
ret = SSH_ERROR;
goto end;
}
}
maj_stat = gss_acquire_cred(&min_stat, client_id, GSS_C_INDEFINITE,
one_oidset, GSS_C_INITIATE,
&gssapi->client.creds,
NULL, NULL);
if (GSS_ERROR(maj_stat)) {
ssh_gssapi_log_error(SSH_LOG_WARN,
"acquiring credential",
maj_stat,
min_stat);
ret = SSH_ERROR;
goto end;
}
ret = ssh_gssapi_import_name(gssapi, session->opts.host);
if (ret != SSH_OK) {
goto end;
}
maj_stat = ssh_gssapi_init_ctx(gssapi, &input_token, &output_token, &oflags);
if (GSS_ERROR(maj_stat)) {
ssh_gssapi_log_error(SSH_LOG_WARN,
"initializing context",
maj_stat,
min_stat);
ret = SSH_ERROR;
goto end;
}
ptr = ssh_get_hexa(supported->elements[i].elements, supported->elements[i].length);
SSH_LOG(SSH_LOG_DEBUG, "Supported mech %zu: %s", i, ptr);
free(ptr);
/* If any one is configured then return successfully */
ret = SSH_OK;
end:
if (ret == SSH_ERROR) {
SSH_LOG(SSH_LOG_WARN, "GSSAPI not configured correctly");
}
SAFE_FREE(gssapi->user);
gss_release_oid_set(&min_stat, &one_oidset);
gss_release_name(&min_stat, &gssapi->client.server_name);
gss_release_cred(&min_stat,&gssapi->server_creds);
gss_release_cred(&min_stat,&gssapi->client.creds);
gss_release_oid(&min_stat, &gssapi->client.oid);
gss_release_buffer(&min_stat, &output_token);
gss_delete_sec_context(&min_stat, &gssapi->ctx, GSS_C_NO_BUFFER);
SAFE_FREE(gssapi->canonic_user);
SAFE_FREE(gssapi);
if (ret == SSH_OK) {
break;
}
}
gss_release_oid_set(&min_stat, &supported);
return ret;
}
/** @internal
* @brief acquires a credential and returns a set of mechanisms for which it is
* valid
*
* @param[in] session The SSH session
* @param[out] valid_oids The set of OIDs for which the credential is valid
*
* @returns SSH_OK if successful, SSH_ERROR otherwise
*/
int ssh_gssapi_client_identity(ssh_session session, gss_OID_set *valid_oids)
{ {
OM_uint32 maj_stat, min_stat, lifetime; OM_uint32 maj_stat, min_stat, lifetime;
gss_OID_set actual_mechs = GSS_C_NO_OID_SET; gss_OID_set actual_mechs = GSS_C_NO_OID_SET;
@@ -675,6 +872,7 @@ static int ssh_gssapi_match(ssh_session session, gss_OID_set *valid_oids)
goto end; goto end;
} }
} }
SSH_LOG(SSH_LOG_DEBUG, "acquired credentials");
gss_create_empty_oid_set(&min_stat, valid_oids); gss_create_empty_oid_set(&min_stat, valid_oids);
@@ -702,6 +900,187 @@ end:
return ret; return ret;
} }
/** @internal
* @brief Add suffixes of oid hash to each GSSAPI key exchange algorithm
* @param[in] session current session handler
* @returns string suffixed kex algorithms or NULL on error
*/
char *
ssh_gssapi_kex_mechs(ssh_session session, const char *gss_algs)
{
size_t i,j;
gss_OID_set selected = GSS_C_NO_OID_SET; /* oid selected for authentication */
ssh_string *oids = NULL;
int rc;
size_t n_oids = 0;
struct ssh_tokens_st *algs = NULL;
char *oid_hash = NULL;
char *new_gss_algs = NULL;
char gss_kex_algs[8000] = {0};
OM_uint32 min_stat;
size_t offset = 0;
/* Get supported oids */
if (session->server) {
#ifdef WITH_SERVER
rc = ssh_gssapi_server_oids(&selected);
if (rc == SSH_ERROR) {
return NULL;
}
#endif
} else {
rc = ssh_gssapi_client_identity(session, &selected);
if (rc == SSH_ERROR) {
return NULL;
}
}
ssh_gssapi_free(session);
n_oids = selected->count;
SSH_LOG(SSH_LOG_DEBUG, "Sending %zu oids", n_oids);
oids = calloc(n_oids, sizeof(ssh_string));
if (oids == NULL) {
ssh_set_error_oom(session);
return NULL;
}
/* Check if algorithms are valid */
new_gss_algs = ssh_find_all_matching(GSSAPI_KEY_EXCHANGE_SUPPORTED, gss_algs);
if (gss_algs == NULL) {
ssh_set_error(session,
SSH_FATAL,
"GSSAPI key exchange algorithms not supported or invalid");
rc = SSH_ERROR;
goto out;
}
algs = ssh_tokenize(new_gss_algs, ',');
if (algs == NULL) {
ssh_set_error(session,
SSH_FATAL,
"Couldn't tokenize GSSAPI key exchange algs");
rc = SSH_ERROR;
goto out;
}
for (i=0; i<n_oids; ++i){
oids[i] = ssh_string_new(selected->elements[i].length + 2);
if (oids[i] == NULL) {
ssh_set_error_oom(session);
rc = SSH_ERROR;
goto out;
}
((unsigned char *)oids[i]->data)[0] = SSH_OID_TAG;
((unsigned char *)oids[i]->data)[1] = selected->elements[i].length;
memcpy((unsigned char *)oids[i]->data + 2, selected->elements[i].elements,
selected->elements[i].length);
/* Get the algorithm suffix */
oid_hash = ssh_gssapi_oid_hash(oids[i]);
if (oid_hash == NULL) {
ssh_set_error_oom(session);
rc = SSH_ERROR;
goto out;
}
/* For each oid loop through the algorithms, append the oid and append
* the algorithms to a string */
for (j = 0; algs->tokens[j]; j++) {
if (sizeof(gss_kex_algs) < offset) {
ssh_set_error(session,
SSH_FATAL,
"snprintf failed");
rc = SSH_ERROR;
goto out;
}
rc = snprintf(&gss_kex_algs[offset], sizeof(gss_kex_algs)-offset, "%s%s,", algs->tokens[j], oid_hash);
if (rc < 0 || rc >= (ssize_t)sizeof(gss_kex_algs)) {
ssh_set_error(session,
SSH_FATAL,
"snprintf failed");
rc = SSH_ERROR;
goto out;
}
/* + 1 for ',' */
offset += strlen(algs->tokens[j]) + strlen(oid_hash) + 1;
}
SAFE_FREE(oid_hash);
SSH_STRING_FREE(oids[i]);
}
rc = SSH_OK;
out:
SAFE_FREE(oid_hash);
SAFE_FREE(oids);
SAFE_FREE(new_gss_algs);
gss_release_oid_set(&min_stat, &selected);
ssh_tokens_free(algs);
if (rc != SSH_OK) {
return NULL;
}
return strdup(gss_kex_algs);
}
int
ssh_gssapi_import_name(struct ssh_gssapi_struct *gssapi, const char *host)
{
gss_buffer_desc hostname;
char name_buf[256] = {0};
OM_uint32 maj_stat, min_stat;
/* import target host name */
snprintf(name_buf, sizeof(name_buf), "host@%s", host);
hostname.value = name_buf;
hostname.length = strlen(name_buf) + 1;
maj_stat = gss_import_name(&min_stat,
&hostname,
(gss_OID)GSS_C_NT_HOSTBASED_SERVICE,
&gssapi->client.server_name);
SSH_LOG(SSH_LOG_DEBUG, "importing name: %s", name_buf);
if (maj_stat != GSS_S_COMPLETE) {
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"error importing name",
maj_stat,
min_stat);
}
return maj_stat;
}
OM_uint32
ssh_gssapi_init_ctx(struct ssh_gssapi_struct *gssapi,
gss_buffer_desc *input_token,
gss_buffer_desc *output_token,
OM_uint32 *ret_flags)
{
OM_uint32 maj_stat, min_stat;
maj_stat = gss_init_sec_context(&min_stat,
gssapi->client.creds,
&gssapi->ctx,
gssapi->client.server_name,
gssapi->client.oid,
gssapi->client.flags,
0,
NULL,
input_token,
NULL,
output_token,
ret_flags,
NULL);
if (GSS_ERROR(maj_stat)) {
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"initializing gssapi context",
maj_stat,
min_stat);
}
return maj_stat;
}
/** /**
* @brief launches a gssapi-with-mic auth request * @brief launches a gssapi-with-mic auth request
* @returns SSH_AUTH_ERROR: A serious error happened\n * @returns SSH_AUTH_ERROR: A serious error happened\n
@@ -716,9 +1095,7 @@ int ssh_gssapi_auth_mic(ssh_session session)
ssh_string *oids = NULL; ssh_string *oids = NULL;
int rc; int rc;
size_t n_oids = 0; size_t n_oids = 0;
OM_uint32 maj_stat, min_stat; OM_uint32 min_stat;
char name_buf[256] = {0};
gss_buffer_desc hostname;
const char *gss_host = session->opts.host; const char *gss_host = session->opts.host;
/* Destroy earlier GSSAPI context if any */ /* Destroy earlier GSSAPI context if any */
@@ -731,20 +1108,9 @@ int ssh_gssapi_auth_mic(ssh_session session)
if (session->opts.gss_server_identity != NULL) { if (session->opts.gss_server_identity != NULL) {
gss_host = session->opts.gss_server_identity; gss_host = session->opts.gss_server_identity;
} }
/* import target host name */
snprintf(name_buf, sizeof(name_buf), "host@%s", gss_host);
hostname.value = name_buf; rc = ssh_gssapi_import_name(session->gssapi, gss_host);
hostname.length = strlen(name_buf) + 1; if (rc != SSH_OK) {
maj_stat = gss_import_name(&min_stat, &hostname,
(gss_OID)GSS_C_NT_HOSTBASED_SERVICE,
&session->gssapi->client.server_name);
if (maj_stat != GSS_S_COMPLETE) {
SSH_LOG(SSH_LOG_DEBUG, "importing name %d, %d", maj_stat, min_stat);
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"importing name",
maj_stat,
min_stat);
return SSH_AUTH_DENIED; return SSH_AUTH_DENIED;
} }
@@ -757,7 +1123,7 @@ int ssh_gssapi_auth_mic(ssh_session session)
SSH_LOG(SSH_LOG_DEBUG, "Authenticating with gssapi to host %s with user %s", SSH_LOG(SSH_LOG_DEBUG, "Authenticating with gssapi to host %s with user %s",
session->opts.host, session->gssapi->user); session->opts.host, session->gssapi->user);
rc = ssh_gssapi_match(session, &selected); rc = ssh_gssapi_client_identity(session, &selected);
if (rc == SSH_ERROR) { if (rc == SSH_ERROR) {
return SSH_AUTH_DENIED; return SSH_AUTH_DENIED;
} }
@@ -869,22 +1235,11 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_response){
session->gssapi->client.flags |= GSS_C_DELEG_FLAG; session->gssapi->client.flags |= GSS_C_DELEG_FLAG;
} }
/* prepare the first TOKEN response */ maj_stat = ssh_gssapi_init_ctx(session->gssapi, &input_token, &output_token, NULL);
maj_stat = gss_init_sec_context(&min_stat, if (GSS_ERROR(maj_stat)){
session->gssapi->client.creds,
&session->gssapi->ctx,
session->gssapi->client.server_name,
session->gssapi->client.oid,
session->gssapi->client.flags,
0, NULL, &input_token, NULL,
&output_token, NULL, NULL);
if(GSS_ERROR(maj_stat)){
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"Initializing gssapi context",
maj_stat,
min_stat);
goto error; goto error;
} }
if (output_token.length != 0){ if (output_token.length != 0){
hexa = ssh_get_hexa(output_token.value, output_token.length); hexa = ssh_get_hexa(output_token.value, output_token.length);
SSH_LOG(SSH_LOG_PACKET, "GSSAPI: sending token %s", hexa); SSH_LOG(SSH_LOG_PACKET, "GSSAPI: sending token %s", hexa);
@@ -984,29 +1339,14 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_client)
hexa = ssh_get_hexa(ssh_string_data(token),ssh_string_len(token)); hexa = ssh_get_hexa(ssh_string_data(token),ssh_string_len(token));
SSH_LOG(SSH_LOG_PACKET, "GSSAPI Token : %s",hexa); SSH_LOG(SSH_LOG_PACKET, "GSSAPI Token : %s",hexa);
SAFE_FREE(hexa); SAFE_FREE(hexa);
input_token.length = ssh_string_len(token); input_token.length = ssh_string_len(token);
input_token.value = ssh_string_data(token); input_token.value = ssh_string_data(token);
maj_stat = gss_init_sec_context(&min_stat, maj_stat = ssh_gssapi_init_ctx(session->gssapi, &input_token, &output_token, NULL);
session->gssapi->client.creds, if (GSS_ERROR(maj_stat)) {
&session->gssapi->ctx,
session->gssapi->client.server_name,
session->gssapi->client.oid,
session->gssapi->client.flags,
0, NULL, &input_token, NULL,
&output_token, NULL, NULL);
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"accepting token",
maj_stat,
min_stat);
SSH_STRING_FREE(token);
if (GSS_ERROR(maj_stat)){
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"Gssapi error",
maj_stat,
min_stat);
goto error; goto error;
} }
SSH_STRING_FREE(token);
if (output_token.length != 0) { if (output_token.length != 0) {
hexa = ssh_get_hexa(output_token.value, output_token.length); hexa = ssh_get_hexa(output_token.value, output_token.length);

View File

@@ -49,6 +49,8 @@
#include "libssh/pki.h" #include "libssh/pki.h"
#include "libssh/bignum.h" #include "libssh/bignum.h"
#include "libssh/token.h" #include "libssh/token.h"
#include "libssh/gssapi.h"
#include "libssh/dh-gss.h"
#ifdef HAVE_BLOWFISH #ifdef HAVE_BLOWFISH
# define BLOWFISH ",blowfish-cbc" # define BLOWFISH ",blowfish-cbc"
@@ -806,6 +808,32 @@ int ssh_set_client_kex(ssh_session session)
ssh_set_error(session, SSH_FATAL, "PRNG error"); ssh_set_error(session, SSH_FATAL, "PRNG error");
return SSH_ERROR; return SSH_ERROR;
} }
#ifdef WITH_GSSAPI
if (session->opts.gssapi_key_exchange) {
char *gssapi_algs = NULL;
ok = ssh_gssapi_init(session);
if (ok != SSH_OK) {
ssh_set_error_oom(session);
return SSH_ERROR;
}
ok = ssh_gssapi_import_name(session->gssapi, session->opts.host);
if (ok != SSH_OK) {
return SSH_ERROR;
}
gssapi_algs = ssh_gssapi_kex_mechs(session, session->opts.gssapi_key_exchange_algs ? session->opts.gssapi_key_exchange_algs : GSSAPI_KEY_EXCHANGE_SUPPORTED);
if (gssapi_algs == NULL) {
return SSH_ERROR;
}
/* Prefix the default algorithms with gsskex algs */
session->opts.wanted_methods[SSH_KEX] =
ssh_prefix_without_duplicates(default_methods[SSH_KEX], gssapi_algs);
SAFE_FREE(gssapi_algs);
}
#endif
/* Set the list of allowed algorithms in order of preference, if it hadn't /* Set the list of allowed algorithms in order of preference, if it hadn't
* been set yet. */ * been set yet. */
@@ -910,6 +938,10 @@ kex_select_kex_type(const char *kex)
{ {
if (strcmp(kex, "diffie-hellman-group1-sha1") == 0) { if (strcmp(kex, "diffie-hellman-group1-sha1") == 0) {
return SSH_KEX_DH_GROUP1_SHA1; return SSH_KEX_DH_GROUP1_SHA1;
} else if (strncmp(kex, "gss-group14-sha256-", 19) == 0) {
return SSH_GSS_KEX_DH_GROUP14_SHA256;
} else if (strncmp(kex, "gss-group16-sha512-", 19) == 0) {
return SSH_GSS_KEX_DH_GROUP16_SHA512;
} else if (strcmp(kex, "diffie-hellman-group14-sha1") == 0) { } else if (strcmp(kex, "diffie-hellman-group14-sha1") == 0) {
return SSH_KEX_DH_GROUP14_SHA1; return SSH_KEX_DH_GROUP14_SHA1;
} else if (strcmp(kex, "diffie-hellman-group14-sha256") == 0) { } else if (strcmp(kex, "diffie-hellman-group14-sha256") == 0) {
@@ -967,6 +999,12 @@ static void revert_kex_callbacks(ssh_session session)
case SSH_KEX_DH_GROUP18_SHA512: case SSH_KEX_DH_GROUP18_SHA512:
ssh_client_dh_remove_callbacks(session); ssh_client_dh_remove_callbacks(session);
break; break;
case SSH_GSS_KEX_DH_GROUP14_SHA256:
case SSH_GSS_KEX_DH_GROUP16_SHA512:
#ifdef WITH_GSSAPI
ssh_client_gss_dh_remove_callbacks(session);
#endif /* WITH_GSSAPI */
break;
#ifdef WITH_GEX #ifdef WITH_GEX
case SSH_KEX_DH_GEX_SHA1: case SSH_KEX_DH_GEX_SHA1:
case SSH_KEX_DH_GEX_SHA256: case SSH_KEX_DH_GEX_SHA256:
@@ -1436,6 +1474,23 @@ int ssh_make_sessionid(ssh_session session)
goto error; goto error;
} }
if (server_pubkey_blob == NULL && session->opts.gssapi_key_exchange) {
ssh_string_free(server_pubkey_blob);
server_pubkey_blob = ssh_string_new(0);
}
if (session->server) {
switch (session->next_crypto->kex_type) {
case SSH_GSS_KEX_DH_GROUP14_SHA256:
case SSH_GSS_KEX_DH_GROUP16_SHA512:
ssh_string_free(server_pubkey_blob);
server_pubkey_blob = ssh_string_new(0);
break;
default:
break;
}
}
rc = ssh_buffer_pack(buf, rc = ssh_buffer_pack(buf,
"dPdPS", "dPdPS",
ssh_buffer_get_len(client_hash), ssh_buffer_get_len(client_hash),
@@ -1457,7 +1512,9 @@ int ssh_make_sessionid(ssh_session session)
case SSH_KEX_DH_GROUP1_SHA1: case SSH_KEX_DH_GROUP1_SHA1:
case SSH_KEX_DH_GROUP14_SHA1: case SSH_KEX_DH_GROUP14_SHA1:
case SSH_KEX_DH_GROUP14_SHA256: case SSH_KEX_DH_GROUP14_SHA256:
case SSH_GSS_KEX_DH_GROUP14_SHA256:
case SSH_KEX_DH_GROUP16_SHA512: case SSH_KEX_DH_GROUP16_SHA512:
case SSH_GSS_KEX_DH_GROUP16_SHA512:
case SSH_KEX_DH_GROUP18_SHA512: case SSH_KEX_DH_GROUP18_SHA512:
rc = ssh_dh_keypair_get_keys(session->next_crypto->dh_ctx, rc = ssh_dh_keypair_get_keys(session->next_crypto->dh_ctx,
DH_CLIENT_KEYPAIR, NULL, &client_pubkey); DH_CLIENT_KEYPAIR, NULL, &client_pubkey);
@@ -1651,6 +1708,7 @@ int ssh_make_sessionid(ssh_session session)
session->next_crypto->secret_hash); session->next_crypto->secret_hash);
break; break;
case SSH_KEX_DH_GROUP14_SHA256: case SSH_KEX_DH_GROUP14_SHA256:
case SSH_GSS_KEX_DH_GROUP14_SHA256:
case SSH_KEX_ECDH_SHA2_NISTP256: case SSH_KEX_ECDH_SHA2_NISTP256:
case SSH_KEX_CURVE25519_SHA256: case SSH_KEX_CURVE25519_SHA256:
case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG: case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
@@ -1686,6 +1744,7 @@ int ssh_make_sessionid(ssh_session session)
session->next_crypto->secret_hash); session->next_crypto->secret_hash);
break; break;
case SSH_KEX_DH_GROUP16_SHA512: case SSH_KEX_DH_GROUP16_SHA512:
case SSH_GSS_KEX_DH_GROUP16_SHA512:
case SSH_KEX_DH_GROUP18_SHA512: case SSH_KEX_DH_GROUP18_SHA512:
case SSH_KEX_ECDH_SHA2_NISTP521: case SSH_KEX_ECDH_SHA2_NISTP521:
case SSH_KEX_SNTRUP761X25519_SHA512: case SSH_KEX_SNTRUP761X25519_SHA512:

View File

@@ -41,6 +41,11 @@
#include "libssh/priv.h" #include "libssh/priv.h"
#include "libssh/session.h" #include "libssh/session.h"
#include <sys/types.h> #include <sys/types.h>
#include "libssh/misc.h"
#include "libssh/options.h"
#include "libssh/config_parser.h"
#include "libssh/gssapi.h"
#include "libssh/token.h"
#ifdef WITH_SERVER #ifdef WITH_SERVER
#include "libssh/server.h" #include "libssh/server.h"
#include "libssh/bind.h" #include "libssh/bind.h"
@@ -560,6 +565,16 @@ int ssh_options_set_algo(ssh_session session,
* Set it to specify that GSSAPI should delegate credentials * Set it to specify that GSSAPI should delegate credentials
* to the server (int, 0 = false). * to the server (int, 0 = false).
* *
* - SSH_OPTIONS_GSSAPI_KEY_EXCHANGE
* Set to true to do GSSAPI key exchange (bool).
*
* - SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS
* Set the GSSAPI key exchange method to be used (const char *,
* comma-separated list). ex:
* "gss-group14-sha256-,gss-group16-sha512-"
* These will prefix the default algorithms if
* SSH_OPTIONS_GSSAPI_KEY_EXCHANGE is true.
*
* - SSH_OPTIONS_PASSWORD_AUTH * - SSH_OPTIONS_PASSWORD_AUTH
* Set it if password authentication should be used * Set it if password authentication should be used
* in ssh_userauth_auto_pubkey(). (int, 0=false). * in ssh_userauth_auto_pubkey(). (int, 0=false).
@@ -1240,6 +1255,40 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
session->opts.gss_delegate_creds = (x & 0xff); session->opts.gss_delegate_creds = (x & 0xff);
} }
break; break;
#ifdef WITH_GSSAPI
case SSH_OPTIONS_GSSAPI_KEY_EXCHANGE:
if (value == NULL) {
ssh_set_error_invalid(session);
return -1;
} else {
bool *x = (bool *)value;
rc = ssh_gssapi_check_client_config(session);
if (rc == SSH_OK) {
session->opts.gssapi_key_exchange = *x;
} else {
SSH_LOG(SSH_LOG_WARN, "Disabled GSSAPI key exchange");
session->opts.gssapi_key_exchange = false;
}
}
break;
case SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS:
v = value;
if (v == NULL || v[0] == '\0') {
ssh_set_error_invalid(session);
return -1;
} else {
/* Check if algorithms are supported */
char *ret = ssh_find_all_matching(GSSAPI_KEY_EXCHANGE_SUPPORTED, v);
if (ret == NULL) {
ssh_set_error(session,
SSH_FATAL,
"GSSAPI key exchange algorithms not supported or invalid");
return -1;
}
session->opts.gssapi_key_exchange_algs = ret;
}
break;
#endif
case SSH_OPTIONS_PASSWORD_AUTH: case SSH_OPTIONS_PASSWORD_AUTH:
case SSH_OPTIONS_PUBKEY_AUTH: case SSH_OPTIONS_PUBKEY_AUTH:
case SSH_OPTIONS_KBDINT_AUTH: case SSH_OPTIONS_KBDINT_AUTH:
@@ -2277,6 +2326,14 @@ static int ssh_bind_set_algo(ssh_bind sshbind,
* Default is 3072 bits or 2048 bits in FIPS mode. * Default is 3072 bits or 2048 bits in FIPS mode.
* (int) * (int)
* *
* - SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE
* Set true to enable GSSAPI key exchange,
* false to disable GSSAPI key exchange. (bool)
*
* - SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS
* Set the GSSAPI key exchange method to be used (const char *,
* comma-separated list). ex:
* "gss-group14-sha256-,gss-group16-sha512-"
* *
* @param value The value to set. This is a generic pointer and the * @param value The value to set. This is a generic pointer and the
* datatype which should be used is described at the * datatype which should be used is described at the
@@ -2674,6 +2731,34 @@ ssh_bind_options_set(ssh_bind sshbind,
sshbind->rsa_min_size = *x; sshbind->rsa_min_size = *x;
} }
break; break;
#ifdef WITH_GSSAPI
case SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE:
if (value == NULL) {
ssh_set_error_invalid(sshbind);
return -1;
} else {
bool *x = (bool *)value;
sshbind->gssapi_key_exchange = *x;
}
break;
case SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS:
if (value == NULL) {
ssh_set_error_invalid(sshbind);
return -1;
} else {
char *ret = NULL;
SAFE_FREE(sshbind->gssapi_key_exchange_algs);
ret = ssh_find_all_matching(GSSAPI_KEY_EXCHANGE_SUPPORTED, value);
if (ret == NULL) {
ssh_set_error(sshbind,
SSH_REQUEST_DENIED,
"GSSAPI key exchange algorithms not supported or invalid");
return -1;
}
sshbind->gssapi_key_exchange_algs = ret;
}
break;
#endif /* WITH_GSSAPI */
default: default:
ssh_set_error(sshbind, ssh_set_error(sshbind,
SSH_REQUEST_DENIED, SSH_REQUEST_DENIED,

View File

@@ -594,6 +594,7 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
* or session->auth.state == SSH_AUTH_STATE_PUBKEY_AUTH_SENT * or session->auth.state == SSH_AUTH_STATE_PUBKEY_AUTH_SENT
* or session->auth.state == SSH_AUTH_STATE_PASSWORD_AUTH_SENT * or session->auth.state == SSH_AUTH_STATE_PASSWORD_AUTH_SENT
* or session->auth.state == SSH_AUTH_STATE_GSSAPI_MIC_SENT * or session->auth.state == SSH_AUTH_STATE_GSSAPI_MIC_SENT
* or session->auth.state == SSH_AUTH_STATE_GSSAPI_KEYEX_MIC_SENT
* or session->auth.state == SSH_AUTH_STATE_AUTH_NONE_SENT * or session->auth.state == SSH_AUTH_STATE_AUTH_NONE_SENT
* *
* Transitions: * Transitions:
@@ -623,6 +624,7 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
(session->auth.state != SSH_AUTH_STATE_PUBKEY_AUTH_SENT) && (session->auth.state != SSH_AUTH_STATE_PUBKEY_AUTH_SENT) &&
(session->auth.state != SSH_AUTH_STATE_PASSWORD_AUTH_SENT) && (session->auth.state != SSH_AUTH_STATE_PASSWORD_AUTH_SENT) &&
(session->auth.state != SSH_AUTH_STATE_GSSAPI_MIC_SENT) && (session->auth.state != SSH_AUTH_STATE_GSSAPI_MIC_SENT) &&
(session->auth.state != SSH_AUTH_STATE_GSSAPI_KEYEX_MIC_SENT) &&
(session->auth.state != SSH_AUTH_STATE_AUTH_NONE_SENT)) (session->auth.state != SSH_AUTH_STATE_AUTH_NONE_SENT))
{ {
rc = SSH_PACKET_DENIED; rc = SSH_PACKET_DENIED;

View File

@@ -27,6 +27,10 @@
#ifdef HAVE_ARPA_INET_H #ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h> #include <arpa/inet.h>
#endif #endif
#ifdef WITH_GSSAPI
#include <gssapi/gssapi.h>
#include "libssh/gssapi.h"
#endif
#include "libssh/priv.h" #include "libssh/priv.h"
#include "libssh/buffer.h" #include "libssh/buffer.h"
@@ -173,53 +177,79 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys)
/* server things are done in server.c */ /* server things are done in server.c */
session->dh_handshake_state=DH_STATE_FINISHED; session->dh_handshake_state=DH_STATE_FINISHED;
} else { } else {
ssh_key server_key = NULL; if (session->opts.gssapi_key_exchange) {
#ifdef WITH_GSSAPI
OM_uint32 maj_stat, min_stat;
gss_buffer_desc mic = GSS_C_EMPTY_BUFFER, msg = GSS_C_EMPTY_BUFFER;
/* client */ mic.length = ssh_string_len(session->gssapi_key_exchange_mic);
mic.value = ssh_string_data(session->gssapi_key_exchange_mic);
/* Verify the host's signature. FIXME do it sooner */ msg.length = session->next_crypto->digest_len;
sig_blob = session->next_crypto->dh_server_signature; msg.value = session->next_crypto->secret_hash;
session->next_crypto->dh_server_signature = NULL;
/* get the server public key */ maj_stat = gss_verify_mic(&min_stat,
server_key = ssh_dh_get_next_server_publickey(session); session->gssapi->ctx,
if (server_key == NULL) { &msg,
goto error; &mic,
} NULL);
if (maj_stat != GSS_S_COMPLETE) {
rc = ssh_pki_import_signature_blob(sig_blob, server_key, &sig);
ssh_string_burn(sig_blob);
SSH_STRING_FREE(sig_blob);
if (rc != SSH_OK) {
goto error;
}
/* Check if signature from server matches user preferences */
if (session->opts.wanted_methods[SSH_HOSTKEYS]) {
rc = match_group(session->opts.wanted_methods[SSH_HOSTKEYS],
sig->type_c);
if (rc == 0) {
ssh_set_error(session, ssh_set_error(session,
SSH_FATAL, SSH_FATAL,
"Public key from server (%s) doesn't match user " "Failed to verify mic after GSSAPI Key Exchange");
"preference (%s)",
sig->type_c,
session->opts.wanted_methods[SSH_HOSTKEYS]);
goto error; goto error;
} }
} SSH_STRING_FREE(session->gssapi_key_exchange_mic);
#endif
} else {
ssh_key server_key = NULL;
rc = ssh_pki_signature_verify(session, /* client */
sig,
server_key, /* Verify the host's signature. FIXME do it sooner */
session->next_crypto->secret_hash, sig_blob = session->next_crypto->dh_server_signature;
session->next_crypto->digest_len); session->next_crypto->dh_server_signature = NULL;
SSH_SIGNATURE_FREE(sig);
if (rc == SSH_ERROR) { /* get the server public key */
ssh_set_error(session, server_key = ssh_dh_get_next_server_publickey(session);
SSH_FATAL, if (server_key == NULL) {
"Failed to verify server hostkey signature"); goto error;
goto error; }
rc = ssh_pki_import_signature_blob(sig_blob, server_key, &sig);
ssh_string_burn(sig_blob);
SSH_STRING_FREE(sig_blob);
if (rc != SSH_OK) {
goto error;
}
/* Check if signature from server matches user preferences */
if (session->opts.wanted_methods[SSH_HOSTKEYS]) {
rc = match_group(session->opts.wanted_methods[SSH_HOSTKEYS],
sig->type_c);
if (rc == 0) {
ssh_set_error(session,
SSH_FATAL,
"Public key from server (%s) doesn't match user "
"preference (%s)",
sig->type_c,
session->opts.wanted_methods[SSH_HOSTKEYS]);
goto error;
}
}
rc = ssh_pki_signature_verify(session,
sig,
server_key,
session->next_crypto->secret_hash,
session->next_crypto->digest_len);
SSH_SIGNATURE_FREE(sig);
if (rc == SSH_ERROR) {
ssh_set_error(session,
SSH_FATAL,
"Failed to verify server hostkey signature");
goto error;
}
} }
SSH_LOG(SSH_LOG_DEBUG, "Signature verified and valid"); SSH_LOG(SSH_LOG_DEBUG, "Signature verified and valid");

View File

@@ -60,6 +60,7 @@
#include "libssh/options.h" #include "libssh/options.h"
#include "libssh/curve25519.h" #include "libssh/curve25519.h"
#include "libssh/token.h" #include "libssh/token.h"
#include "libssh/gssapi.h"
#define set_status(session, status) do {\ #define set_status(session, status) do {\
if (session->common.callbacks && session->common.callbacks->connect_status_function) \ if (session->common.callbacks && session->common.callbacks->connect_status_function) \
@@ -98,6 +99,9 @@ int server_set_kex(ssh_session session)
enum ssh_keytypes_e keytype; enum ssh_keytypes_e keytype;
size_t len; size_t len;
int ok; int ok;
#ifdef WITH_GSSAPI
char *gssapi_algs = NULL;
#endif /* WITH_GSSAPI */
/* Skip if already set, for example for the rekey or when we do the guessing /* Skip if already set, for example for the rekey or when we do the guessing
* it could have been already used to make some protocol decisions. */ * it could have been already used to make some protocol decisions. */
@@ -169,6 +173,28 @@ int server_set_kex(ssh_session session)
return -1; return -1;
} }
#ifdef WITH_GSSAPI
if (session->opts.gssapi_key_exchange) {
ok = ssh_gssapi_init(session);
if (ok != SSH_OK) {
ssh_set_error_oom(session);
return SSH_ERROR;
}
gssapi_algs = ssh_gssapi_kex_mechs(session, session->opts.gssapi_key_exchange_algs ? session->opts.gssapi_key_exchange_algs : GSSAPI_KEY_EXCHANGE_SUPPORTED);
if (gssapi_algs == NULL) {
return SSH_ERROR;
}
ssh_gssapi_free(session);
/* Prefix the default algorithms with gsskex algs */
session->opts.wanted_methods[SSH_KEX] =
ssh_prefix_without_duplicates(ssh_kex_get_default_methods(SSH_KEX), gssapi_algs);
SAFE_FREE(gssapi_algs);
}
#endif /* WITH_GSSAPI */
for (i = 0; i < SSH_KEX_METHODS; i++) { for (i = 0; i < SSH_KEX_METHODS; i++) {
wanted = session->opts.wanted_methods[i]; wanted = session->opts.wanted_methods[i];
if (wanted == NULL) { if (wanted == NULL) {

View File

@@ -325,6 +325,7 @@ void ssh_free(ssh_session session)
#ifdef WITH_GSSAPI #ifdef WITH_GSSAPI
ssh_gssapi_free(session); ssh_gssapi_free(session);
SAFE_FREE(session->opts.gssapi_key_exchange_algs);
#endif #endif
/* options */ /* options */
@@ -470,8 +471,12 @@ const char* ssh_get_kex_algo(ssh_session session) {
return "diffie-hellman-group14-sha1"; return "diffie-hellman-group14-sha1";
case SSH_KEX_DH_GROUP14_SHA256: case SSH_KEX_DH_GROUP14_SHA256:
return "diffie-hellman-group14-sha256"; return "diffie-hellman-group14-sha256";
case SSH_GSS_KEX_DH_GROUP14_SHA256:
return "gss-group14-sha256-";
case SSH_KEX_DH_GROUP16_SHA512: case SSH_KEX_DH_GROUP16_SHA512:
return "diffie-hellman-group16-sha512"; return "diffie-hellman-group16-sha512";
case SSH_GSS_KEX_DH_GROUP16_SHA512:
return "gss-group16-sha512-";
case SSH_KEX_DH_GROUP18_SHA512: case SSH_KEX_DH_GROUP18_SHA512:
return "diffie-hellman-group18-sha512"; return "diffie-hellman-group18-sha512";
case SSH_KEX_ECDH_SHA2_NISTP256: case SSH_KEX_ECDH_SHA2_NISTP256:

View File

@@ -54,6 +54,7 @@
#ifdef HAVE_MLKEM #ifdef HAVE_MLKEM
#include "libssh/hybrid_mlkem.h" #include "libssh/hybrid_mlkem.h"
#endif #endif
#include "libssh/dh-gss.h"
static struct ssh_hmac_struct ssh_hmac_tab[] = { static struct ssh_hmac_struct ssh_hmac_tab[] = {
{ "hmac-sha1", SSH_HMAC_SHA1, false }, { "hmac-sha1", SSH_HMAC_SHA1, false },
@@ -587,6 +588,12 @@ int crypt_set_algorithms_server(ssh_session session){
case SSH_KEX_DH_GROUP18_SHA512: case SSH_KEX_DH_GROUP18_SHA512:
ssh_server_dh_init(session); ssh_server_dh_init(session);
break; break;
#ifdef WITH_GSSAPI
case SSH_GSS_KEX_DH_GROUP14_SHA256:
case SSH_GSS_KEX_DH_GROUP16_SHA512:
ssh_server_gss_dh_init(session);
break;
#endif /* WITH_GSSAPI */
#ifdef WITH_GEX #ifdef WITH_GEX
case SSH_KEX_DH_GEX_SHA1: case SSH_KEX_DH_GEX_SHA1:
case SSH_KEX_DH_GEX_SHA256: case SSH_KEX_DH_GEX_SHA256:

View File

@@ -45,7 +45,8 @@ endif()
if (WITH_GSSAPI AND GSSAPI_FOUND AND GSSAPI_TESTING) if (WITH_GSSAPI AND GSSAPI_FOUND AND GSSAPI_TESTING)
set(LIBSSH_CLIENT_TESTS set(LIBSSH_CLIENT_TESTS
${LIBSSH_CLIENT_TESTS} ${LIBSSH_CLIENT_TESTS}
torture_gssapi_auth) torture_gssapi_auth
torture_gssapi_key_exchange)
endif() endif()
if (DEFAULT_C_NO_DEPRECATION_FLAGS) if (DEFAULT_C_NO_DEPRECATION_FLAGS)

View File

@@ -0,0 +1,258 @@
#include "config.h"
#define LIBSSH_STATIC
#include "torture.h"
#include <libssh/libssh.h>
#include "libssh/crypto.h"
#include <errno.h>
#include <fcntl.h>
#include <gssapi.h>
#include <pwd.h>
static int
sshd_setup(void **state)
{
torture_setup_sshd_server(state, false);
torture_update_sshd_config(state,
"GSSAPIAuthentication yes\n"
"GSSAPIKeyExchange yes\n");
return 0;
}
static int
sshd_teardown(void **state)
{
assert_non_null(state);
torture_teardown_sshd_server(state);
return 0;
}
static int
session_setup(void **state)
{
struct torture_state *s = *state;
int verbosity = torture_libssh_verbosity();
struct passwd *pwd = NULL;
int rc;
bool b = false;
pwd = getpwnam("bob");
assert_non_null(pwd);
rc = setuid(pwd->pw_uid);
assert_return_code(rc, errno);
s->ssh.session = ssh_new();
assert_non_null(s->ssh.session);
ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
ssh_options_set(s->ssh.session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
/* Make sure no other configuration options from system will get used */
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_PROCESS_CONFIG, &b);
assert_ssh_return_code(s->ssh.session, rc);
return 0;
}
static int
session_teardown(void **state)
{
struct torture_state *s = *state;
assert_non_null(s);
ssh_disconnect(s->ssh.session);
ssh_free(s->ssh.session);
return 0;
}
static void
torture_gssapi_key_exchange(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
bool t = true;
/* Valid */
torture_setup_kdc_server(
state,
"kadmin.local addprinc -randkey host/server.libssh.site \n"
"kadmin.local ktadd -k $(dirname $0)/d/ssh.keytab host/server.libssh.site \n"
"kadmin.local addprinc -pw bar alice \n"
"kadmin.local list_principals",
"echo bar | kinit alice");
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE, &t);
assert_ssh_return_code(s->ssh.session, rc);
rc = ssh_connect(session);
assert_int_equal(rc, 0);
torture_teardown_kdc_server(state);
}
static void
torture_gssapi_key_exchange_no_tgt(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
bool t = true;
/* Don't run kinit */
torture_setup_kdc_server(
state,
"kadmin.local addprinc -randkey host/server.libssh.site \n"
"kadmin.local ktadd -k $(dirname $0)/d/ssh.keytab host/server.libssh.site \n"
"kadmin.local addprinc -pw bar alice \n"
"kadmin.local list_principals",
/* No TGT */
"");
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE, &t);
assert_ssh_return_code(s->ssh.session, rc);
rc = ssh_connect(session);
assert_int_equal(rc, 0);
assert_int_not_equal(session->current_crypto->kex_type, SSH_GSS_KEX_DH_GROUP14_SHA256);
assert_int_not_equal(session->current_crypto->kex_type, SSH_GSS_KEX_DH_GROUP16_SHA512);
torture_teardown_kdc_server(state);
}
static void
torture_gssapi_key_exchange_gss_group14_sha256(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
bool t = true;
/* Valid */
torture_setup_kdc_server(
state,
"kadmin.local addprinc -randkey host/server.libssh.site \n"
"kadmin.local ktadd -k $(dirname $0)/d/ssh.keytab host/server.libssh.site \n"
"kadmin.local addprinc -pw bar alice \n"
"kadmin.local list_principals",
"echo bar | kinit alice");
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE, &t);
assert_ssh_return_code(s->ssh.session, rc);
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS, "gss-group14-sha256-");
assert_ssh_return_code(s->ssh.session, rc);
rc = ssh_connect(session);
assert_int_equal(rc, 0);
assert_int_equal(session->current_crypto->kex_type, SSH_GSS_KEX_DH_GROUP14_SHA256);
torture_teardown_kdc_server(state);
}
static void
torture_gssapi_key_exchange_gss_group16_sha512(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
bool t = true;
/* Valid */
torture_setup_kdc_server(
state,
"kadmin.local addprinc -randkey host/server.libssh.site \n"
"kadmin.local ktadd -k $(dirname $0)/d/ssh.keytab host/server.libssh.site \n"
"kadmin.local addprinc -pw bar alice \n"
"kadmin.local list_principals",
"echo bar | kinit alice");
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE, &t);
assert_ssh_return_code(s->ssh.session, rc);
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS, "gss-group16-sha512-");
assert_ssh_return_code(s->ssh.session, rc);
rc = ssh_connect(session);
assert_int_equal(rc, 0);
assert_true(session->current_crypto->kex_type == SSH_GSS_KEX_DH_GROUP16_SHA512);
torture_teardown_kdc_server(state);
}
static void
torture_gssapi_key_exchange_auth(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
bool t = true;
/* Valid */
torture_setup_kdc_server(
state,
"kadmin.local addprinc -randkey host/server.libssh.site \n"
"kadmin.local ktadd -k $(dirname $0)/d/ssh.keytab host/server.libssh.site \n"
"kadmin.local addprinc -pw bar alice \n"
"kadmin.local list_principals",
"echo bar | kinit alice");
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE, &t);
assert_ssh_return_code(s->ssh.session, rc);
rc = ssh_connect(session);
assert_int_equal(rc, 0);
rc = ssh_userauth_gssapi_keyex(session);
assert_int_equal(rc, SSH_AUTH_SUCCESS);
torture_teardown_kdc_server(state);
}
int
torture_run_tests(void)
{
int rc;
struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(torture_gssapi_key_exchange,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_gssapi_key_exchange_no_tgt,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_gssapi_key_exchange_gss_group14_sha256,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_gssapi_key_exchange_gss_group16_sha512,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_gssapi_key_exchange_auth,
session_setup,
session_teardown),
};
ssh_init();
torture_filter_tests(tests);
rc = cmocka_run_group_tests(tests, sshd_setup, sshd_teardown);
ssh_finalize();
pthread_exit((void *)&rc);
}

View File

@@ -19,7 +19,8 @@ if (WITH_GSSAPI AND GSSAPI_FOUND AND GSSAPI_TESTING)
${LIBSSH_SERVER_TESTS} ${LIBSSH_SERVER_TESTS}
torture_gssapi_server_auth torture_gssapi_server_auth
torture_gssapi_server_auth_cb torture_gssapi_server_auth_cb
torture_gssapi_server_delegation) torture_gssapi_server_delegation
torture_gssapi_server_key_exchange)
endif() endif()
include_directories(${libssh_SOURCE_DIR}/include include_directories(${libssh_SOURCE_DIR}/include

View File

@@ -295,6 +295,7 @@ static int init_server_state(struct server_state_st *state,
} }
state->parse_global_config = arguments->with_global_config; state->parse_global_config = arguments->with_global_config;
state->gssapi_key_exchange_algs = NULL;
if (arguments->config_file) { if (arguments->config_file) {
state->config_file = arguments->config_file; state->config_file = arguments->config_file;

View File

@@ -194,6 +194,30 @@ int run_server(struct server_state_st *state)
goto out; goto out;
} }
#ifdef WITH_GSSAPI
rc = ssh_bind_options_set(sshbind,
SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE,
&(state->gssapi_key_exchange));
if (rc != 0) {
fprintf(stderr,
"Error setting GSSAPI key exchange: %s\n",
ssh_get_error(sshbind));
goto out;
}
if (state->gssapi_key_exchange_algs != NULL) {
rc = ssh_bind_options_set(sshbind,
SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS,
state->gssapi_key_exchange_algs);
if (rc != 0) {
fprintf(stderr,
"Error setting GSSAPI key exchange: %s\n",
ssh_get_error(sshbind));
goto out;
}
}
#endif /* WITH_GSSAPI */
rc = ssh_bind_options_set(sshbind, rc = ssh_bind_options_set(sshbind,
SSH_BIND_OPTIONS_BINDPORT, SSH_BIND_OPTIONS_BINDPORT,
&(state->port)); &(state->port));

View File

@@ -53,6 +53,8 @@ struct server_state_st {
bool parse_global_config; bool parse_global_config;
char *log_file; char *log_file;
bool gssapi_key_exchange;
const char *gssapi_key_exchange_algs;
/* State */ /* State */
int max_tries; int max_tries;

View File

@@ -160,6 +160,7 @@ setup_default_server(void **state)
ss = tss->ss; ss = tss->ss;
s = tss->state; s = tss->state;
setenv("NSS_WRAPPER_HOSTNAME", "server.libssh.site", 1);
/* Start the server using the default values */ /* Start the server using the default values */
pid = fork_run_server(ss, free_test_server_state, &tss); pid = fork_run_server(ss, free_test_server_state, &tss);
if (pid < 0) { if (pid < 0) {
@@ -335,7 +336,7 @@ torture_gssapi_server_auth_invalid_host(void **state)
"echo bar | kinit alice"); "echo bar | kinit alice");
rc = ssh_userauth_gssapi(session); rc = ssh_userauth_gssapi(session);
assert_int_equal(rc, SSH_AUTH_ERROR); assert_int_equal(rc, SSH_AUTH_DENIED);
torture_teardown_kdc_server((void **)&s); torture_teardown_kdc_server((void **)&s);
} }

View File

@@ -238,6 +238,7 @@ setup_callback_server(void **state)
ss->server_cb->gssapi_verify_mic_function = verify_mic; ss->server_cb->gssapi_verify_mic_function = verify_mic;
ss->server_cb->userdata = &sdata; ss->server_cb->userdata = &sdata;
setenv("NSS_WRAPPER_HOSTNAME", "server.libssh.site", 1);
/* Start the server using the default values */ /* Start the server using the default values */
pid = fork_run_server(ss, free_test_server_state, &tss); pid = fork_run_server(ss, free_test_server_state, &tss);
if (pid < 0) { if (pid < 0) {

View File

@@ -185,6 +185,7 @@ setup_callback_server(void **state)
ss->server_cb->auth_gssapi_mic_function = auth_gssapi_mic; ss->server_cb->auth_gssapi_mic_function = auth_gssapi_mic;
ss->server_cb->userdata = &sdata; ss->server_cb->userdata = &sdata;
setenv("NSS_WRAPPER_HOSTNAME", "server.libssh.site", 1);
/* Start the server using the default values */ /* Start the server using the default values */
pid = fork_run_server(ss, free_test_server_state, &tss); pid = fork_run_server(ss, free_test_server_state, &tss);
if (pid < 0) { if (pid < 0) {

View File

@@ -0,0 +1,338 @@
#include "config.h"
#define LIBSSH_STATIC
#include <errno.h>
#include <pwd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "libssh/libssh.h"
#include "torture.h"
#include "torture_key.h"
#include "test_server.h"
#include "default_cb.h"
#define TORTURE_KNOWN_HOSTS_FILE "libssh_torture_knownhosts"
struct test_server_st {
struct torture_state *state;
struct server_state_st *ss;
char *cwd;
};
static void
free_test_server_state(void **state)
{
struct test_server_st *tss = *state;
torture_free_state(tss->state);
SAFE_FREE(tss);
}
static void
setup_config(void **state)
{
struct torture_state *s = NULL;
struct server_state_st *ss = NULL;
struct test_server_st *tss = NULL;
char ed25519_hostkey[1024] = {0};
char rsa_hostkey[1024];
char ecdsa_hostkey[1024];
// char trusted_ca_pubkey[1024];
char sshd_path[1024];
char log_file[1024];
char kdc_env[255] = {0};
int rc;
assert_non_null(state);
tss = (struct test_server_st *)calloc(1, sizeof(struct test_server_st));
assert_non_null(tss);
torture_setup_socket_dir((void **)&s);
assert_non_null(s->socket_dir);
assert_non_null(s->gss_dir);
torture_set_kdc_env_str(s->gss_dir, kdc_env, sizeof(kdc_env));
torture_set_env_from_str(kdc_env);
/* Set the default interface for the server */
setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "10", 1);
setenv("PAM_WRAPPER", "1", 1);
snprintf(sshd_path, sizeof(sshd_path), "%s/sshd", s->socket_dir);
rc = mkdir(sshd_path, 0755);
assert_return_code(rc, errno);
snprintf(log_file, sizeof(log_file), "%s/sshd/log", s->socket_dir);
snprintf(ed25519_hostkey,
sizeof(ed25519_hostkey),
"%s/sshd/ssh_host_ed25519_key",
s->socket_dir);
torture_write_file(ed25519_hostkey,
torture_get_openssh_testkey(SSH_KEYTYPE_ED25519, 0));
snprintf(rsa_hostkey,
sizeof(rsa_hostkey),
"%s/sshd/ssh_host_rsa_key",
s->socket_dir);
torture_write_file(rsa_hostkey, torture_get_testkey(SSH_KEYTYPE_RSA, 0));
snprintf(ecdsa_hostkey,
sizeof(ecdsa_hostkey),
"%s/sshd/ssh_host_ecdsa_key",
s->socket_dir);
torture_write_file(ecdsa_hostkey,
torture_get_testkey(SSH_KEYTYPE_ECDSA_P521, 0));
/* Create default server state */
ss = (struct server_state_st *)calloc(1, sizeof(struct server_state_st));
assert_non_null(ss);
ss->address = strdup("127.0.0.10");
assert_non_null(ss->address);
ss->port = 22;
ss->ecdsa_key = strdup(ecdsa_hostkey);
assert_non_null(ss->ecdsa_key);
ss->ed25519_key = strdup(ed25519_hostkey);
assert_non_null(ss->ed25519_key);
ss->rsa_key = strdup(rsa_hostkey);
assert_non_null(ss->rsa_key);
ss->host_key = NULL;
/* Use default username and password (set in default_handle_session_cb) */
ss->expected_username = NULL;
ss->expected_password = NULL;
/* not to mix up the client and server messages */
ss->verbosity = torture_libssh_verbosity();
ss->log_file = strdup(log_file);
ss->auth_methods = SSH_AUTH_METHOD_GSSAPI_MIC;
#ifdef WITH_PCAP
ss->with_pcap = 1;
ss->pcap_file = strdup(s->pcap_file);
assert_non_null(ss->pcap_file);
#endif
/* TODO make configurable */
ss->max_tries = 3;
ss->error = 0;
/* Use the default session handling function */
ss->handle_session = default_handle_session_cb;
assert_non_null(ss->handle_session);
/* Do not use global configuration */
ss->parse_global_config = false;
/* Enable GSSAPI key exchange */
ss->gssapi_key_exchange = true;
ss->gssapi_key_exchange_algs = "gss-group14-sha256-,gss-group16-sha512-";
tss->state = s;
tss->ss = ss;
*state = tss;
}
static int
setup_default_server(void **state)
{
struct torture_state *s = NULL;
struct server_state_st *ss = NULL;
struct test_server_st *tss = NULL;
char pid_str[1024];
pid_t pid;
int rc;
setup_config(state);
tss = *state;
ss = tss->ss;
s = tss->state;
setenv("NSS_WRAPPER_HOSTNAME", "server.libssh.site", 1);
/* Start the server using the default values */
pid = fork_run_server(ss, free_test_server_state, &tss);
if (pid < 0) {
fail();
}
snprintf(pid_str, sizeof(pid_str), "%d", pid);
torture_write_file(s->srv_pidfile, (const char *)pid_str);
setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "21", 1);
unsetenv("PAM_WRAPPER");
/* Wait until the sshd is ready to accept connections */
rc = torture_wait_for_daemon(5);
assert_int_equal(rc, 0);
*state = tss;
return 0;
}
static int
teardown_default_server(void **state)
{
struct torture_state *s = NULL;
struct server_state_st *ss = NULL;
struct test_server_st *tss = NULL;
tss = *state;
assert_non_null(tss);
s = tss->state;
assert_non_null(s);
ss = tss->ss;
assert_non_null(ss);
/* This function can be reused */
torture_teardown_sshd_server((void **)&s);
free_server_state(tss->ss);
SAFE_FREE(tss->ss);
SAFE_FREE(tss);
return 0;
}
static int
session_setup(void **state)
{
struct test_server_st *tss = *state;
struct torture_state *s;
int verbosity = torture_libssh_verbosity();
char *cwd = NULL;
bool b = false;
int rc;
assert_non_null(tss);
/* Make sure we do not test the agent */
unsetenv("SSH_AUTH_SOCK");
cwd = torture_get_current_working_dir();
assert_non_null(cwd);
tss->cwd = cwd;
s = tss->state;
assert_non_null(s);
s->ssh.session = ssh_new();
assert_non_null(s->ssh.session);
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
assert_ssh_return_code(s->ssh.session, rc);
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
assert_ssh_return_code(s->ssh.session, rc);
rc = ssh_options_set(s->ssh.session,
SSH_OPTIONS_USER,
TORTURE_SSH_USER_ALICE);
assert_int_equal(rc, SSH_OK);
/* Make sure no other configuration options from system will get used */
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_PROCESS_CONFIG, &b);
assert_ssh_return_code(s->ssh.session, rc);
return 0;
}
static int
session_teardown(void **state)
{
struct test_server_st *tss = *state;
struct torture_state *s;
int rc = 0;
assert_non_null(tss);
s = tss->state;
assert_non_null(s);
ssh_disconnect(s->ssh.session);
ssh_free(s->ssh.session);
rc = torture_change_dir(tss->cwd);
assert_int_equal(rc, 0);
SAFE_FREE(tss->cwd);
return 0;
}
static void
torture_gssapi_server_key_exchange(void **state)
{
struct test_server_st *tss = *state;
struct torture_state *s;
ssh_session session;
int rc;
bool t = true;
assert_non_null(tss);
s = tss->state;
assert_non_null(s);
session = s->ssh.session;
assert_non_null(session);
/* Valid */
torture_setup_kdc_server(
(void **)&s,
"kadmin.local addprinc -randkey host/server.libssh.site\n"
"kadmin.local ktadd -k $(dirname $0)/d/ssh.keytab host/server.libssh.site\n"
"kadmin.local addprinc -pw bar alice\n"
"kadmin.local list_principals",
"echo bar | kinit alice");
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE, &t);
assert_ssh_return_code(s->ssh.session, rc);
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS, "gss-group16-sha512-");
assert_ssh_return_code(s->ssh.session, rc);
rc = ssh_connect(session);
fprintf(stderr, "%s", ssh_get_error(session));
assert_int_equal(rc, SSH_OK);
torture_teardown_kdc_server((void **)&s);
}
int
torture_run_tests(void)
{
int rc;
struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(torture_gssapi_server_key_exchange,
session_setup,
session_teardown),
};
ssh_init();
torture_filter_tests(tests);
rc = cmocka_run_group_tests(tests,
setup_default_server,
teardown_default_server);
ssh_finalize();
pthread_exit((void *)&rc);
}

View File

@@ -93,7 +93,8 @@ extern LIBSSH_THREAD int ssh_log_level;
"\tGSSAPIServerIdentity example.com\n" \ "\tGSSAPIServerIdentity example.com\n" \
"\tGSSAPIClientIdentity home.sweet\n" \ "\tGSSAPIClientIdentity home.sweet\n" \
"\tUserKnownHostsFile "USER_KNOWN_HOSTS"\n" \ "\tUserKnownHostsFile "USER_KNOWN_HOSTS"\n" \
"\tRequiredRSASize 2233\n" "\tRequiredRSASize 2233\n" \
"\tGSSAPIKexAlgorithms gss-group14-sha256-\n"
/* authentication methods */ /* authentication methods */
#define LIBSSH_TESTCONFIG_STRING8 \ #define LIBSSH_TESTCONFIG_STRING8 \
@@ -648,6 +649,9 @@ static void torture_config_new(void ** state,
assert_int_equal(session->opts.gss_delegate_creds, 1); assert_int_equal(session->opts.gss_delegate_creds, 1);
assert_string_equal(session->opts.gss_server_identity, "example.com"); assert_string_equal(session->opts.gss_server_identity, "example.com");
assert_string_equal(session->opts.gss_client_identity, "home.sweet"); assert_string_equal(session->opts.gss_client_identity, "home.sweet");
#ifdef WITH_GSSAPI
assert_string_equal(session->opts.gssapi_key_exchange_algs, "gss-group14-sha256-");
#endif /* WITH_GSSAPI */
assert_int_equal(ssh_get_log_level(), SSH_LOG_TRACE); assert_int_equal(ssh_get_log_level(), SSH_LOG_TRACE);
assert_int_equal(session->common.log_verbosity, SSH_LOG_TRACE); assert_int_equal(session->common.log_verbosity, SSH_LOG_TRACE);

View File

@@ -255,6 +255,70 @@
## libkrb5 ## libkrb5
# krb5_mcc_generate_new allocates a hashtab on a static global variable # krb5_mcc_generate_new allocates a hashtab on a static global variable
# It doesn't get freed. # It doesn't get freed.
{
Reachable memory from getaddrinfo
Memcheck:Leak
match-leak-kinds: reachable
fun:malloc
fun:malloc
fun:strdup
fun:_dl_load_cache_lookup
fun:_dl_map_object
fun:dl_open_worker_begin
fun:_dl_catch_exception
fun:dl_open_worker
fun:_dl_catch_exception
fun:_dl_open
fun:do_dlopen
fun:_dl_catch_exception
fun:_dl_catch_error
fun:dlerror_run
...
fun:getaddrinfo
...
fun:gss_init_sec_context
fun:ssh_gssapi_init_ctx
...
fun:ssh_userauth_gssapi
fun:torture_gssapi_auth_server_identity
...
fun:_cmocka_run_group_tests
fun:torture_run_tests
fun:main
}
{
Reachable memory from getaddrinfo
Memcheck:Leak
match-leak-kinds: reachable
fun:malloc
fun:UnknownInlinedFun
fun:_dl_new_object
fun:_dl_map_object_from_fd
fun:_dl_map_object
fun:dl_open_worker_begin
fun:_dl_catch_exception
fun:dl_open_worker
fun:_dl_catch_exception
fun:_dl_open
fun:do_dlopen
fun:_dl_catch_exception
fun:_dl_catch_error
fun:dlerror_run
...
fun:getaddrinfo
...
fun:gss_init_sec_context
fun:ssh_gssapi_init_ctx
...
fun:ssh_userauth_gssapi
fun:torture_gssapi_auth_server_identity
...
fun:_cmocka_run_group_tests
fun:torture_run_tests
fun:main
}
{ {
Reachable memory from libkrb5 Reachable memory from libkrb5
Memcheck:Leak Memcheck:Leak