mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-02-04 12:20:42 +09:00
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:
committed by
Jakub Jelen
parent
701a2155a7
commit
bc5211d055
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 */
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
35
include/libssh/dh-gss.h
Normal 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_ */
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
135
src/auth.c
135
src/auth.c
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|||||||
10
src/bind.c
10
src/bind.c
@@ -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);
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
19
src/config.c
19
src/config.c
@@ -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
439
src/dh-gss.c
Normal 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 */
|
||||||
5
src/dh.c
5
src/dh.c
@@ -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,
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
540
src/gssapi.c
540
src/gssapi.c
@@ -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);
|
||||||
|
|||||||
59
src/kex.c
59
src/kex.c
@@ -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:
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
108
src/packet_cb.c
108
src/packet_cb.c
@@ -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");
|
||||||
|
|
||||||
|
|||||||
26
src/server.c
26
src/server.c
@@ -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) {
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
258
tests/client/torture_gssapi_key_exchange.c
Normal file
258
tests/client/torture_gssapi_key_exchange.c
Normal 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);
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
338
tests/server/torture_gssapi_server_key_exchange.c
Normal file
338
tests/server/torture_gssapi_server_key_exchange.c
Normal 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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user