summaryrefslogtreecommitdiffstats
path: root/libssh/src/kex.c
diff options
context:
space:
mode:
Diffstat (limited to 'libssh/src/kex.c')
-rw-r--r--libssh/src/kex.c650
1 files changed, 0 insertions, 650 deletions
diff --git a/libssh/src/kex.c b/libssh/src/kex.c
deleted file mode 100644
index f1a1b566..00000000
--- a/libssh/src/kex.c
+++ /dev/null
@@ -1,650 +0,0 @@
-/*
- * kex.c - key exchange
- *
- * This file is part of the SSH Library
- *
- * Copyright (c) 2003-2008 by Aris Adamantiadis
- *
- * 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 <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "libssh/priv.h"
-#include "libssh/buffer.h"
-#include "libssh/dh.h"
-#include "libssh/kex.h"
-#include "libssh/session.h"
-#include "libssh/ssh2.h"
-#include "libssh/string.h"
-#include "libssh/curve25519.h"
-#include "libssh/knownhosts.h"
-
-#ifdef HAVE_LIBGCRYPT
-# define BLOWFISH "blowfish-cbc,"
-# define AES "aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,"
-# define DES "3des-cbc,des-cbc-ssh1"
-#elif defined(HAVE_LIBCRYPTO)
-# ifdef HAVE_OPENSSL_BLOWFISH_H
-# define BLOWFISH "blowfish-cbc,"
-# else
-# define BLOWFISH ""
-# endif
-# ifdef HAVE_OPENSSL_AES_H
-# ifdef BROKEN_AES_CTR
-# define AES "aes256-cbc,aes192-cbc,aes128-cbc,"
-# else
-# define AES "aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,"
-# endif /* BROKEN_AES_CTR */
-# else
-# define AES ""
-# endif
-# define DES "3des-cbc,des-cbc-ssh1"
-#endif
-
-#ifdef WITH_ZLIB
-#define ZLIB "none,zlib,zlib@openssh.com"
-#else
-#define ZLIB "none"
-#endif
-
-#ifdef HAVE_CURVE25519
-#define CURVE25519 "curve25519-sha256@libssh.org,"
-#else
-#define CURVE25519 ""
-#endif
-
-#ifdef HAVE_ECDH
-#define ECDH "ecdh-sha2-nistp256,"
-#define HOSTKEYS "ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,ssh-dss"
-#else
-#define HOSTKEYS "ssh-rsa,ssh-dss"
-#define ECDH ""
-#endif
-
-#define KEY_EXCHANGE CURVE25519 ECDH "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1"
-#define KEX_METHODS_SIZE 10
-
-/* NOTE: This is a fixed API and the index is defined by ssh_kex_types_e */
-static const char *default_methods[] = {
- KEY_EXCHANGE,
- HOSTKEYS,
- AES BLOWFISH DES,
- AES BLOWFISH DES,
- "hmac-sha1,hmac-sha2-256,hmac-sha2-512",
- "hmac-sha1,hmac-sha2-256,hmac-sha2-512",
- "none",
- "none",
- "",
- "",
- NULL
-};
-
-/* NOTE: This is a fixed API and the index is defined by ssh_kex_types_e */
-static const char *supported_methods[] = {
- KEY_EXCHANGE,
- HOSTKEYS,
- AES BLOWFISH DES,
- AES BLOWFISH DES,
- "hmac-sha1,hmac-sha2-256,hmac-sha2-512",
- "hmac-sha1,hmac-sha2-256,hmac-sha2-512",
- ZLIB,
- ZLIB,
- "",
- "",
- NULL
-};
-
-/* descriptions of the key exchange packet */
-static const char *ssh_kex_descriptions[] = {
- "kex algos",
- "server host key algo",
- "encryption client->server",
- "encryption server->client",
- "mac algo client->server",
- "mac algo server->client",
- "compression algo client->server",
- "compression algo server->client",
- "languages client->server",
- "languages server->client",
- NULL
-};
-
-/* tokenize will return a token of strings delimited by ",". the first element has to be freed */
-static char **tokenize(const char *chain){
- char **tokens;
- int n=1;
- int i=0;
- char *tmp;
- char *ptr;
-
- tmp = strdup(chain);
- if (tmp == NULL) {
- return NULL;
- }
- ptr = tmp;
- while(*ptr){
- if(*ptr==','){
- n++;
- *ptr=0;
- }
- ptr++;
- }
- /* now n contains the number of tokens, the first possibly empty if the list was empty too e.g. "" */
- tokens=malloc(sizeof(char *) * (n+1) ); /* +1 for the null */
- if (tokens == NULL) {
- SAFE_FREE(tmp);
- return NULL;
- }
- ptr=tmp;
- for(i=0;i<n;i++){
- tokens[i]=ptr;
- while(*ptr)
- ptr++; // find a zero
- ptr++; // then go one step further
- }
- tokens[i]=NULL;
- return tokens;
-}
-
-/* same as tokenize(), but with spaces instead of ',' */
-/* TODO FIXME rewrite me! */
-char **space_tokenize(const char *chain){
- char **tokens;
- int n=1;
- int i=0;
- char *tmp;
- char *ptr;
-
- tmp = strdup(chain);
- if (tmp == NULL) {
- return NULL;
- }
- ptr = tmp;
-
- while(*ptr==' ')
- ++ptr; /* skip initial spaces */
- while(*ptr){
- if(*ptr==' '){
- n++; /* count one token per word */
- *ptr=0;
- while(*(ptr+1)==' '){ /* don't count if the tokens have more than 2 spaces */
- *(ptr++)=0;
- }
- }
- ptr++;
- }
- /* now n contains the number of tokens, the first possibly empty if the list was empty too e.g. "" */
- tokens = malloc(sizeof(char *) * (n + 1)); /* +1 for the null */
- if (tokens == NULL) {
- SAFE_FREE(tmp);
- return NULL;
- }
- ptr=tmp; /* we don't pass the initial spaces because the "tmp" pointer is needed by the caller */
- /* function to free the tokens. */
- for(i=0;i<n;i++){
- tokens[i]=ptr;
- if(i!=n-1){
- while(*ptr)
- ptr++; // find a zero
- while(!*(ptr+1))
- ++ptr; /* if the zero is followed by other zeros, go through them */
- ptr++; // then go one step further
- }
- }
- tokens[i]=NULL;
- return tokens;
-}
-
-const char *ssh_kex_get_supported_method(uint32_t algo) {
- if (algo >= KEX_METHODS_SIZE) {
- return NULL;
- }
-
- return supported_methods[algo];
-}
-
-const char *ssh_kex_get_description(uint32_t algo) {
- if (algo >= KEX_METHODS_SIZE) {
- return NULL;
- }
-
- return ssh_kex_descriptions[algo];
-}
-
-/* find_matching gets 2 parameters : a list of available objects (available_d), separated by colons,*/
-/* and a list of preferred objects (preferred_d) */
-/* it will return a strduped pointer on the first preferred object found in the available objects list */
-
-char *ssh_find_matching(const char *available_d, const char *preferred_d){
- char ** tok_available, **tok_preferred;
- int i_avail, i_pref;
- char *ret;
-
- if ((available_d == NULL) || (preferred_d == NULL)) {
- return NULL; /* don't deal with null args */
- }
-
- tok_available = tokenize(available_d);
- if (tok_available == NULL) {
- return NULL;
- }
-
- tok_preferred = tokenize(preferred_d);
- if (tok_preferred == NULL) {
- SAFE_FREE(tok_available[0]);
- SAFE_FREE(tok_available);
- return NULL;
- }
-
- for(i_pref=0; tok_preferred[i_pref] ; ++i_pref){
- for(i_avail=0; tok_available[i_avail]; ++i_avail){
- if(strcmp(tok_available[i_avail],tok_preferred[i_pref]) == 0){
- /* match */
- ret=strdup(tok_available[i_avail]);
- /* free the tokens */
- SAFE_FREE(tok_available[0]);
- SAFE_FREE(tok_preferred[0]);
- SAFE_FREE(tok_available);
- SAFE_FREE(tok_preferred);
- return ret;
- }
- }
- }
- SAFE_FREE(tok_available[0]);
- SAFE_FREE(tok_preferred[0]);
- SAFE_FREE(tok_available);
- SAFE_FREE(tok_preferred);
- return NULL;
-}
-
-/**
- * @internal
- * @brief returns whether the first client key exchange algorithm matches
- * the first server key exchange algorithm
- * @returns whether the first client key exchange algorithm matches
- * the first server key exchange algorithm
- */
-static int is_first_kex_packet_follows_guess_wrong(const char *client_kex,
- const char *server_kex) {
- int is_wrong = 1;
- char **server_kex_tokens = NULL;
- char **client_kex_tokens = NULL;
-
- if ((client_kex == NULL) || (server_kex == NULL)) {
- goto out;
- }
-
- client_kex_tokens = tokenize(client_kex);
-
- if (client_kex_tokens == NULL) {
- goto out;
- }
-
- if (client_kex_tokens[0] == NULL) {
- goto freeout;
- }
-
- server_kex_tokens = tokenize(server_kex);
- if (server_kex_tokens == NULL) {
- goto freeout;
- }
-
- is_wrong = (strcmp(client_kex_tokens[0], server_kex_tokens[0]) != 0);
-
- SAFE_FREE(server_kex_tokens[0]);
- SAFE_FREE(server_kex_tokens);
-freeout:
- SAFE_FREE(client_kex_tokens[0]);
- SAFE_FREE(client_kex_tokens);
-out:
- return is_wrong;
-}
-
-SSH_PACKET_CALLBACK(ssh_packet_kexinit){
- int i;
- int server_kex=session->server;
- ssh_string str = NULL;
- char *strings[KEX_METHODS_SIZE];
- int rc = SSH_ERROR;
-
- uint8_t first_kex_packet_follows = 0;
- uint32_t kexinit_reserved = 0;
-
- (void)type;
- (void)user;
-
- memset(strings, 0, sizeof(strings));
- if (session->session_state == SSH_SESSION_STATE_AUTHENTICATED){
- SSH_LOG(SSH_LOG_WARNING, "Other side initiating key re-exchange");
- } else if(session->session_state != SSH_SESSION_STATE_INITIAL_KEX){
- ssh_set_error(session,SSH_FATAL,"SSH_KEXINIT received in wrong state");
- goto error;
- }
-
- if (server_kex) {
- rc = buffer_get_data(packet,session->next_crypto->client_kex.cookie, 16);
- if (rc != 16) {
- ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: no cookie in packet");
- goto error;
- }
-
- rc = hashbufin_add_cookie(session, session->next_crypto->client_kex.cookie);
- if (rc < 0) {
- ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: adding cookie failed");
- goto error;
- }
- } else {
- rc = buffer_get_data(packet,session->next_crypto->server_kex.cookie, 16);
- if (rc != 16) {
- ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: no cookie in packet");
- goto error;
- }
-
- rc = hashbufin_add_cookie(session, session->next_crypto->server_kex.cookie);
- if (rc < 0) {
- ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: adding cookie failed");
- goto error;
- }
- }
-
- for (i = 0; i < KEX_METHODS_SIZE; i++) {
- str = buffer_get_ssh_string(packet);
- if (str == NULL) {
- break;
- }
-
- rc = buffer_add_ssh_string(session->in_hashbuf, str);
- if (rc < 0) {
- ssh_set_error(session, SSH_FATAL, "Error adding string in hash buffer");
- goto error;
- }
-
- strings[i] = ssh_string_to_char(str);
- if (strings[i] == NULL) {
- ssh_set_error_oom(session);
- goto error;
- }
- ssh_string_free(str);
- str = NULL;
- }
-
- /* copy the server kex info into an array of strings */
- if (server_kex) {
- for (i = 0; i < SSH_KEX_METHODS; i++) {
- session->next_crypto->client_kex.methods[i] = strings[i];
- }
- } else { /* client */
- for (i = 0; i < SSH_KEX_METHODS; i++) {
- session->next_crypto->server_kex.methods[i] = strings[i];
- }
- }
-
- /*
- * Handle the two final fields for the KEXINIT message (RFC 4253 7.1):
- *
- * boolean first_kex_packet_follows
- * uint32 0 (reserved for future extension)
- *
- * Notably if clients set 'first_kex_packet_follows', it is expected
- * that its value is included when computing the session ID (see
- * 'make_sessionid').
- */
- if (server_kex) {
- rc = buffer_get_u8(packet, &first_kex_packet_follows);
- if (rc != 1) {
- goto error;
- }
-
- rc = buffer_add_u8(session->in_hashbuf, first_kex_packet_follows);
- if (rc < 0) {
- goto error;
- }
-
- rc = buffer_add_u32(session->in_hashbuf, kexinit_reserved);
- if (rc < 0) {
- goto error;
- }
-
- /*
- * Remember whether 'first_kex_packet_follows' was set and the client
- * guess was wrong: in this case the next SSH_MSG_KEXDH_INIT message
- * must be ignored.
- */
- if (first_kex_packet_follows) {
- session->first_kex_follows_guess_wrong =
- is_first_kex_packet_follows_guess_wrong(session->next_crypto->client_kex.methods[SSH_KEX],
- session->next_crypto->server_kex.methods[SSH_KEX]);
- }
- }
-
- session->session_state = SSH_SESSION_STATE_KEXINIT_RECEIVED;
- session->dh_handshake_state = DH_STATE_INIT;
- session->ssh_connection_callback(session);
- return SSH_PACKET_USED;
-
-error:
- ssh_string_free(str);
- for (i = 0; i < SSH_KEX_METHODS; i++) {
- SAFE_FREE(strings[i]);
- }
-
- session->session_state = SSH_SESSION_STATE_ERROR;
-
- return SSH_PACKET_USED;
-}
-
-void ssh_list_kex(struct ssh_kex_struct *kex) {
- int i = 0;
-
-#ifdef DEBUG_CRYPTO
- ssh_print_hexa("session cookie", kex->cookie, 16);
-#endif
-
- for(i = 0; i < SSH_KEX_METHODS; i++) {
- if (kex->methods[i] == NULL) {
- continue;
- }
- SSH_LOG(SSH_LOG_FUNCTIONS, "%s: %s",
- ssh_kex_descriptions[i], kex->methods[i]);
- }
-}
-
-/**
- * @internal
- * @brief selects the hostkey mechanisms to be chosen for the key exchange,
- * as some hostkey mechanisms may be present in known_hosts file and preferred
- * @returns a cstring containing a comma-separated list of hostkey methods.
- * NULL if no method matches
- */
-static char *ssh_client_select_hostkeys(ssh_session session){
- char methods_buffer[128]={0};
- static const char *preferred_hostkeys[]={"ecdsa-sha2-nistp521","ecdsa-sha2-nistp384",
- "ecdsa-sha2-nistp256", "ssh-rsa", "ssh-dss", "ssh-rsa1", NULL};
- char **methods;
- int i,j;
- int needcoma=0;
-
- methods = ssh_knownhosts_algorithms(session);
- if (methods == NULL || methods[0] == NULL){
- SAFE_FREE(methods);
- return NULL;
- }
-
- for (i=0;preferred_hostkeys[i] != NULL; ++i){
- for (j=0; methods[j] != NULL; ++j){
- if(strcmp(preferred_hostkeys[i], methods[j]) == 0){
- if (verify_existing_algo(SSH_HOSTKEYS, methods[j])){
- if(needcoma)
- strncat(methods_buffer,",",sizeof(methods_buffer)-strlen(methods_buffer)-1);
- strncat(methods_buffer, methods[j], sizeof(methods_buffer)-strlen(methods_buffer)-1);
- needcoma = 1;
- }
- }
- }
- }
- for(i=0;methods[i]!= NULL; ++i){
- SAFE_FREE(methods[i]);
- }
- SAFE_FREE(methods);
-
- if(strlen(methods_buffer) > 0){
- SSH_LOG(SSH_LOG_DEBUG, "Changing host key method to \"%s\"", methods_buffer);
- return strdup(methods_buffer);
- } else {
- SSH_LOG(SSH_LOG_DEBUG, "No supported kex method for existing key in known_hosts file");
- return NULL;
- }
-
-}
-/**
- * @brief sets the key exchange parameters to be sent to the server,
- * in function of the options and available methods.
- */
-int set_client_kex(ssh_session session){
- struct ssh_kex_struct *client= &session->next_crypto->client_kex;
- const char *wanted;
- int i;
-
- ssh_get_random(client->cookie, 16, 0);
-
- memset(client->methods, 0, KEX_METHODS_SIZE * sizeof(char **));
- /* first check if we have specific host key methods */
- if(session->opts.wanted_methods[SSH_HOSTKEYS] == NULL){
- /* Only if no override */
- session->opts.wanted_methods[SSH_HOSTKEYS] =
- ssh_client_select_hostkeys(session);
- }
-
- for (i = 0; i < KEX_METHODS_SIZE; i++) {
- wanted = session->opts.wanted_methods[i];
- if (wanted == NULL)
- wanted = default_methods[i];
- client->methods[i] = strdup(wanted);
- }
-
- return SSH_OK;
-}
-
-/** @brief Select the different methods on basis of client's and
- * server's kex messages, and watches out if a match is possible.
- */
-int ssh_kex_select_methods (ssh_session session){
- struct ssh_kex_struct *server = &session->next_crypto->server_kex;
- struct ssh_kex_struct *client = &session->next_crypto->client_kex;
- int i;
-
- for (i = 0; i < KEX_METHODS_SIZE; i++) {
- session->next_crypto->kex_methods[i]=ssh_find_matching(server->methods[i],client->methods[i]);
- if(session->next_crypto->kex_methods[i] == NULL && i < SSH_LANG_C_S){
- ssh_set_error(session,SSH_FATAL,"kex error : no match for method %s: server [%s], client [%s]",
- ssh_kex_descriptions[i],server->methods[i],client->methods[i]);
- return SSH_ERROR;
- } else if ((i >= SSH_LANG_C_S) && (session->next_crypto->kex_methods[i] == NULL)) {
- /* we can safely do that for languages */
- session->next_crypto->kex_methods[i] = strdup("");
- }
- }
- if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group1-sha1") == 0){
- session->next_crypto->kex_type=SSH_KEX_DH_GROUP1_SHA1;
- } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group14-sha1") == 0){
- session->next_crypto->kex_type=SSH_KEX_DH_GROUP14_SHA1;
- } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "ecdh-sha2-nistp256") == 0){
- session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP256;
- } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "curve25519-sha256@libssh.org") == 0){
- session->next_crypto->kex_type=SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG;
- }
-
- return SSH_OK;
-}
-
-
-/* this function only sends the predefined set of kex methods */
-int ssh_send_kex(ssh_session session, int server_kex) {
- struct ssh_kex_struct *kex = (server_kex ? &session->next_crypto->server_kex :
- &session->next_crypto->client_kex);
- ssh_string str = NULL;
- int i;
- int rc;
-
- rc = ssh_buffer_pack(session->out_buffer,
- "bP",
- SSH2_MSG_KEXINIT,
- 16,
- kex->cookie); /* cookie */
- if (rc != SSH_OK)
- goto error;
- if (hashbufout_add_cookie(session) < 0) {
- goto error;
- }
-
- ssh_list_kex(kex);
-
- for (i = 0; i < KEX_METHODS_SIZE; i++) {
- str = ssh_string_from_char(kex->methods[i]);
- if (str == NULL) {
- goto error;
- }
-
- if (buffer_add_ssh_string(session->out_hashbuf, str) < 0) {
- goto error;
- }
- if (buffer_add_ssh_string(session->out_buffer, str) < 0) {
- goto error;
- }
- ssh_string_free(str);
- str = NULL;
- }
-
- rc = ssh_buffer_pack(session->out_buffer,
- "bd",
- 0,
- 0);
- if (rc != SSH_OK) {
- goto error;
- }
-
- if (packet_send(session) == SSH_ERROR) {
- return -1;
- }
-
- return 0;
-error:
- ssh_buffer_reinit(session->out_buffer);
- ssh_buffer_reinit(session->out_hashbuf);
- ssh_string_free(str);
-
- return -1;
-}
-
-/* returns 1 if at least one of the name algos is in the default algorithms table */
-int verify_existing_algo(int algo, const char *name){
- char *ptr;
- if(algo>9 || algo <0)
- return -1;
- ptr=ssh_find_matching(supported_methods[algo],name);
- if(ptr){
- free(ptr);
- return 1;
- }
- return 0;
-}
-
-/* vim: set ts=2 sw=2 et cindent: */