Files
libssh/include/libssh/session.h
Jakub Jelen 08386d4787 CVE-2023-1667:kex: Add support for sending first_kex_packet_follows flag
This is not completely straightforward as it requires us to do some state
shuffling.

We introduce internal flag that can turn this on in client side, so far for
testing only as we do not want to universally enable this. We also repurpose the
server flag indicating the guess was wrong also for the client to make desired
decisions.

If we found out our guess was wrong, we need to hope the server was able to
figure out this much, we need to revert the DH FSM state, drop the callbacks
from the "wrong" key exchange method and initiate the right one.

The server side is already tested by the pkd_hello_i1, which is executing tests
against dropbrear clients, which is using this flag by default out of the box.

Tested manually also with the pkd_hello --rekey to make sure the server is able
to handle the rekeying with all key exchange methods.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Norbert Pocs <npocs@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2023-05-04 11:51:17 +02:00

283 lines
9.4 KiB
C

/*
* This file is part of the SSH Library
*
* Copyright (c) 2009 by Aris Adamantiadis
*
* This 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.
*
* This 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 this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SESSION_H_
#define SESSION_H_
#include <stdbool.h>
#include "libssh/priv.h"
#include "libssh/callbacks.h"
#include "libssh/kex.h"
#include "libssh/packet.h"
#include "libssh/pcap.h"
#include "libssh/auth.h"
#include "libssh/channels.h"
#include "libssh/poll.h"
#include "libssh/config.h"
#include "libssh/misc.h"
/* These are the different states a SSH session can be into its life */
enum ssh_session_state_e {
SSH_SESSION_STATE_NONE=0,
SSH_SESSION_STATE_CONNECTING,
SSH_SESSION_STATE_SOCKET_CONNECTED,
SSH_SESSION_STATE_BANNER_RECEIVED,
SSH_SESSION_STATE_INITIAL_KEX,
SSH_SESSION_STATE_KEXINIT_RECEIVED,
SSH_SESSION_STATE_DH,
SSH_SESSION_STATE_AUTHENTICATING,
SSH_SESSION_STATE_AUTHENTICATED,
SSH_SESSION_STATE_ERROR,
SSH_SESSION_STATE_DISCONNECTED
};
enum ssh_dh_state_e {
DH_STATE_INIT=0,
DH_STATE_GROUP_SENT,
DH_STATE_REQUEST_SENT,
DH_STATE_INIT_SENT,
DH_STATE_NEWKEYS_SENT,
DH_STATE_FINISHED
};
enum ssh_pending_call_e {
SSH_PENDING_CALL_NONE = 0,
SSH_PENDING_CALL_CONNECT,
SSH_PENDING_CALL_AUTH_NONE,
SSH_PENDING_CALL_AUTH_PASSWORD,
SSH_PENDING_CALL_AUTH_OFFER_PUBKEY,
SSH_PENDING_CALL_AUTH_PUBKEY,
SSH_PENDING_CALL_AUTH_AGENT,
SSH_PENDING_CALL_AUTH_KBDINT_INIT,
SSH_PENDING_CALL_AUTH_KBDINT_SEND,
SSH_PENDING_CALL_AUTH_GSSAPI_MIC
};
/* libssh calls may block an undefined amount of time */
#define SSH_SESSION_FLAG_BLOCKING 0x0001
/* Client successfully authenticated */
#define SSH_SESSION_FLAG_AUTHENTICATED 0x0002
/* Do not accept new session channels (no-more-sessions@openssh.com) */
#define SSH_SESSION_FLAG_NO_MORE_SESSIONS 0x0004
/* The KEXINIT message can be sent first by either of the parties so this flag
* indicates that the message was already sent to make sure it is sent and avoid
* sending it twice during key exchange to simplify the state machine. */
#define SSH_SESSION_FLAG_KEXINIT_SENT 0x0008
/* codes to use with ssh_handle_packets*() */
/* Infinite timeout */
#define SSH_TIMEOUT_INFINITE -1
/* Use the timeout defined by user if any. Mostly used with new connections */
#define SSH_TIMEOUT_USER -2
/* Use the default timeout, depending on ssh_is_blocking() */
#define SSH_TIMEOUT_DEFAULT -3
/* Don't block at all */
#define SSH_TIMEOUT_NONBLOCKING 0
/* options flags */
/* Authentication with *** allowed */
#define SSH_OPT_FLAG_PASSWORD_AUTH 0x1
#define SSH_OPT_FLAG_PUBKEY_AUTH 0x2
#define SSH_OPT_FLAG_KBDINT_AUTH 0x4
#define SSH_OPT_FLAG_GSSAPI_AUTH 0x8
/* Escape expansion of different variables */
#define SSH_OPT_EXP_FLAG_KNOWNHOSTS 0x1
#define SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS 0x2
#define SSH_OPT_EXP_FLAG_PROXYCOMMAND 0x4
#define SSH_OPT_EXP_FLAG_IDENTITY 0x8
/* extensions flags */
/* negotiation enabled */
#define SSH_EXT_NEGOTIATION 0x01
/* server-sig-algs extension */
#define SSH_EXT_SIG_RSA_SHA256 0x02
#define SSH_EXT_SIG_RSA_SHA512 0x04
/* members that are common to ssh_session and ssh_bind */
struct ssh_common_struct {
struct error_struct error;
ssh_callbacks callbacks; /* Callbacks to user functions */
int log_verbosity; /* verbosity of the log functions */
};
struct ssh_session_struct {
struct ssh_common_struct common;
struct ssh_socket_struct *socket;
char *serverbanner;
char *clientbanner;
int protoversion;
int server;
int client;
int openssh;
uint32_t send_seq;
uint32_t recv_seq;
struct ssh_timestamp last_rekey_time;
int connected;
/* !=0 when the user got a session handle */
int alive;
/* two previous are deprecated */
/* int auth_service_asked; */
/* session flags (SSH_SESSION_FLAG_*) */
int flags;
/* Extensions negotiated using RFC 8308 */
uint32_t extensions;
ssh_string banner; /* that's the issue banner from the server */
char *peer_discon_msg; /* disconnect message from the remote host */
char *disconnect_message; /* disconnect message to be set */
ssh_buffer in_buffer;
PACKET in_packet;
ssh_buffer out_buffer;
struct ssh_list *out_queue; /* This list is used for delaying packets
when rekeying is required */
/* the states are used by the nonblocking stuff to remember */
/* where it was before being interrupted */
enum ssh_pending_call_e pending_call_state;
enum ssh_session_state_e session_state;
enum ssh_packet_state_e packet_state;
enum ssh_dh_state_e dh_handshake_state;
enum ssh_channel_request_state_e global_req_state;
struct ssh_agent_state_struct *agent_state;
struct {
struct ssh_auth_auto_state_struct *auto_state;
enum ssh_auth_service_state_e service_state;
enum ssh_auth_state_e state;
uint32_t supported_methods;
uint32_t current_method;
} auth;
/* Sending this flag before key exchange to save one round trip during the
* key exchange. This might make sense on high-latency connections.
* So far internal only for testing. Usable only on the client side --
* there is no key exchange method that would start with server message */
bool send_first_kex_follows;
/*
* RFC 4253, 7.1: if the first_kex_packet_follows flag was set in
* the received SSH_MSG_KEXINIT, but the guess was wrong, this
* field will be set such that the following guessed packet will
* be ignored on the receiving side. Once that packet has been received and
* ignored, this field is cleared.
* On the sending side, this is set after we got peer KEXINIT message and we
* need to resend the initial message of the negotiated KEX algorithm.
*/
bool first_kex_follows_guess_wrong;
ssh_buffer in_hashbuf;
ssh_buffer out_hashbuf;
struct ssh_crypto_struct *current_crypto;
/* next_crypto is going to be used after a SSH2_MSG_NEWKEYS */
struct ssh_crypto_struct *next_crypto;
struct ssh_list *channels; /* linked list of channels */
uint32_t maxchannel;
ssh_agent agent; /* ssh agent */
/* keyboard interactive data */
struct ssh_kbdint_struct *kbdint;
struct ssh_gssapi_struct *gssapi;
/* server host keys */
struct {
ssh_key rsa_key;
ssh_key ecdsa_key;
ssh_key ed25519_key;
/* The type of host key wanted by client */
enum ssh_keytypes_e hostkey;
enum ssh_digest_e hostkey_digest;
} srv;
/* auths accepted by server */
struct ssh_list *ssh_message_list; /* list of delayed SSH messages */
int (*ssh_message_callback)(struct ssh_session_struct *session,
ssh_message msg, void *userdata);
void *ssh_message_callback_data;
ssh_server_callbacks server_callbacks;
void (*ssh_connection_callback)( struct ssh_session_struct *session);
struct ssh_packet_callbacks_struct default_packet_callbacks;
struct ssh_list *packet_callbacks;
struct ssh_socket_callbacks_struct socket_callbacks;
ssh_poll_ctx default_poll_ctx;
/* options */
#ifdef WITH_PCAP
ssh_pcap_context pcap_ctx; /* pcap debugging context */
#endif
struct {
struct ssh_list *identity;
struct ssh_list *identity_non_exp;
char *username;
char *host;
char *bindaddr; /* bind the client to an ip addr */
char *sshdir;
char *knownhosts;
char *global_knownhosts;
char *wanted_methods[SSH_KEX_METHODS];
char *pubkey_accepted_types;
char *ProxyCommand;
char *custombanner;
char *moduli_file;
char *agent_socket;
unsigned long timeout; /* seconds */
unsigned long timeout_usec;
uint16_t port;
socket_t fd;
int StrictHostKeyChecking;
char compressionlevel;
char *gss_server_identity;
char *gss_client_identity;
int gss_delegate_creds;
int flags;
int exp_flags;
int nodelay;
bool config_processed;
uint8_t options_seen[SOC_MAX];
uint64_t rekey_data;
uint32_t rekey_time;
int rsa_min_size;
bool identities_only;
} opts;
/* counters */
ssh_counter socket_counter;
ssh_counter raw_counter;
};
/** @internal
* @brief a termination function evaluates the status of an object
* @param user[in] object to evaluate
* @returns 1 if the polling routine should terminate, 0 instead
*/
typedef int (*ssh_termination_function)(void *user);
int ssh_handle_packets(ssh_session session, int timeout);
int ssh_handle_packets_termination(ssh_session session,
int timeout,
ssh_termination_function fct,
void *user);
void ssh_socket_exception_callback(int code, int errno_code, void *user);
#endif /* SESSION_H_ */