/* kex.c is used well, in key exchange :-) */ /* Copyright 2003 Aris Adamantiadis This file is part of the SSH Library 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 #include #include #include "libssh/priv.h" #include "libssh/ssh2.h" #include "libssh/ssh1.h" #ifdef HAVE_OPENSSL_BLOWFISH_H #define BLOWFISH "blowfish-cbc," #else #define BLOWFISH "" #endif #ifdef HAVE_OPENSSL_AES_H #define AES "aes256-cbc,aes192-cbc,aes128-cbc," #else #define AES "" #endif #define DES "3des-cbc" #ifdef HAVE_LIBZ #define ZLIB "none,zlib" #else #define ZLIB "none" #endif char *default_methods[]={ "diffie-hellman-group1-sha1","ssh-dss,ssh-rsa",AES BLOWFISH DES,AES BLOWFISH DES, "hmac-sha1","hmac-sha1","none","none","","",NULL }; char *supported_methods[]={ "diffie-hellman-group1-sha1","ssh-dss,ssh-rsa",AES BLOWFISH DES,AES BLOWFISH DES, "hmac-sha1","hmac-sha1",ZLIB,ZLIB,"","",NULL }; /* descriptions of the key exchange packet */ char *ssh_kex_nums[]={ "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(char *chain){ char **tokens; int n=1; int i=0; char *ptr=chain=strdup(chain); 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 */ ptr=chain; for(i=0;iin_buffer,session->server_kex.cookie,16)!=16){ ssh_set_error(session,SSH_FATAL,"get_kex(): no cookie in packet"); return -1; } hashbufin_add_cookie(session,session->server_kex.cookie); memset(strings,0,sizeof(char *)*10); for(i=0;i<10;++i){ str=buffer_get_ssh_string(session->in_buffer); if(!str) break; if(str){ buffer_add_ssh_string(session->in_hashbuf,str); strings[i]=string_to_char(str); free(str); } else strings[i]=NULL; } /* copy the server kex info into an array of strings */ if(server_kex){ session->client_kex.methods=malloc( 10 * sizeof(char **)); for(i=0;i<10;++i) session->client_kex.methods[i]=strings[i]; } else { // client session->server_kex.methods=malloc( 10 * sizeof(char **)); for(i=0;i<10;++i) session->server_kex.methods[i]=strings[i]; } return 0; } void ssh_list_kex(KEX *kex){ int i=0; #ifdef DEBUG_CRYPTO ssh_print_hexa("session cookie",kex->cookie,16); #endif for(i=0;i<10;i++){ ssh_say(2,"%s : %s\n",ssh_kex_nums[i],kex->methods[i]); } } /* set_kex basicaly look at the option structure of the session and set the output kex message */ /* it must be aware of the server kex message */ /* it can fail if option is null, not any user specified kex method matches the server one, if not any default kex matches */ int set_kex(SSH_SESSION *session){ KEX *server = &session->server_kex; KEX *client=&session->client_kex; SSH_OPTIONS *options=session->options; int i; char *wanted; /* the client might ask for a specific cookie to be sent. useful for server debugging */ if(options->wanted_cookie) memcpy(client->cookie,options->wanted_cookie,16); else ssh_get_random(client->cookie,16,0); client->methods=malloc(10 * sizeof(char **)); memset(client->methods,0,10*sizeof(char **)); for (i=0;i<10;i++){ if(!(wanted=options->wanted_methods[i])) wanted=default_methods[i]; client->methods[i]=ssh_find_matching(server->methods[i],wanted); if(!client->methods[i] && i < SSH_LANG_C_S){ ssh_set_error(session,SSH_FATAL,"kex error : did not find one of algos %s in list %s for %s", wanted,server->methods[i],ssh_kex_nums[i]); return -1; } else { if(i>=SSH_LANG_C_S && !client->methods[i]) client->methods[i]=strdup(""); // we can safely do that for languages } } return 0; } /* this function only sends the predefined set of kex methods */ void ssh_send_kex(SSH_SESSION *session, int server_kex){ STRING *str; int i=0; KEX *kex=(server_kex ? &session->server_kex : &session->client_kex); packet_clear_out(session); buffer_add_u8(session->out_buffer,SSH2_MSG_KEXINIT); buffer_add_data(session->out_buffer,kex->cookie,16); hashbufout_add_cookie(session); ssh_list_kex(kex); for(i=0;i<10;i++){ str=string_from_char(kex->methods[i]); buffer_add_ssh_string(session->out_hashbuf,str); buffer_add_ssh_string(session->out_buffer,str); free(str); } i=0; buffer_add_u8(session->out_buffer,0); buffer_add_u32(session->out_buffer,0); packet_send(session); } /* returns 1 if at least one of the name algos is in the default algorithms table */ int verify_existing_algo(int algo, 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; } /* makes a STRING contating 3 strings : ssh-rsa1,e and n */ /* this is a public key in openssh's format */ static STRING *make_rsa1_string(STRING *e, STRING *n){ BUFFER *buffer=buffer_new(); STRING *rsa=string_from_char("ssh-rsa1"); STRING *ret; buffer_add_ssh_string(buffer,rsa); free(rsa); buffer_add_ssh_string(buffer,e); buffer_add_ssh_string(buffer,n); ret=string_new(buffer_get_len(buffer)); string_fill(ret,buffer_get(buffer),buffer_get_len(buffer)); buffer_free(buffer); return ret; } static void build_session_id1(SSH_SESSION *session, STRING *servern, STRING *hostn){ MD5CTX *md5=md5_init(); ssh_print_hexa("host modulus",hostn->string,string_len(hostn)); ssh_print_hexa("server modulus",servern->string,string_len(servern)); md5_update(md5,hostn->string,string_len(hostn)); md5_update(md5,servern->string,string_len(servern)); md5_update(md5,session->server_kex.cookie,8); md5_final(session->next_crypto->session_id,md5); ssh_print_hexa("session_id",session->next_crypto->session_id,MD5_DIGEST_LEN); } STRING *encrypt_session_key(SSH_SESSION *session, PUBLIC_KEY *svrkey, PUBLIC_KEY *hostkey){ char buffer[32]; int i; STRING *data1,*data2; /* first, generate a session key */ ssh_get_random(session->next_crypto->encryptkey,32,1); memcpy(buffer,session->next_crypto->encryptkey,32); memcpy(session->next_crypto->decryptkey, session->next_crypto->encryptkey,32); ssh_print_hexa("session key",buffer,32); /* xor session key with session_id */ for (i=0;i<16;++i) buffer[i]^=session->next_crypto->session_id[i]; data1=string_new(32); string_fill(data1,buffer,32); data2=ssh_encrypt_rsa1(session,data1,svrkey); free(data1); data1=ssh_encrypt_rsa1(session,data2,hostkey); return data1; } /* SSH-1 functions */ /* 2 SSH_SMSG_PUBLIC_KEY * * 8 bytes anti_spoofing_cookie * 32-bit int server_key_bits * mp-int server_key_public_exponent * mp-int server_key_public_modulus * 32-bit int host_key_bits * mp-int host_key_public_exponent * mp-int host_key_public_modulus * 32-bit int protocol_flags * 32-bit int supported_ciphers_mask * 32-bit int supported_authentications_mask */ int ssh_get_kex1(SSH_SESSION *session){ u32 server_bits, host_bits, protocol_flags, supported_ciphers_mask, supported_authentications_mask; STRING *server_exp=NULL; STRING *server_mod=NULL; STRING *host_exp=NULL; STRING *host_mod=NULL; STRING *serverkey; STRING *hostkey; STRING *enc_session; PUBLIC_KEY *svr,*host; int ko; u16 bits; ssh_say(3,"Waiting for a SSH_SMSG_PUBLIC_KEY\n"); if(packet_wait(session,SSH_SMSG_PUBLIC_KEY,1)){ return -1; } ssh_say(3,"Got a SSH_SMSG_PUBLIC_KEY\n"); if(buffer_get_data(session->in_buffer,session->server_kex.cookie,8)!=8){ ssh_set_error(NULL,SSH_FATAL,"Can't get cookie in buffer"); return -1; } buffer_get_u32(session->in_buffer,&server_bits); server_exp=buffer_get_mpint(session->in_buffer); server_mod=buffer_get_mpint(session->in_buffer); buffer_get_u32(session->in_buffer,&host_bits); host_exp=buffer_get_mpint(session->in_buffer); host_mod=buffer_get_mpint(session->in_buffer); buffer_get_u32(session->in_buffer,&protocol_flags); buffer_get_u32(session->in_buffer,&supported_ciphers_mask); ko=buffer_get_u32(session->in_buffer,&supported_authentications_mask); if((ko!=sizeof(u32)) || !host_mod || !host_exp || !server_mod || !server_exp){ ssh_say(2,"Invalid SSH_SMSG_PUBLIC_KEY packet\n"); ssh_set_error(NULL,SSH_FATAL,"Invalid SSH_SMSG_PUBLIC_KEY packet"); if(host_mod) free(host_mod); if(host_exp) free(host_exp); if(server_mod) free(server_mod); if(server_exp) free(server_exp); return -1; } server_bits=ntohl(server_bits); host_bits=ntohl(host_bits); protocol_flags=ntohl(protocol_flags); supported_ciphers_mask=ntohl(supported_ciphers_mask); supported_authentications_mask=ntohl(supported_authentications_mask); ssh_say(1,"server bits: %d ; host bits: %d\nProtocol flags : %.8lx ; " "cipher mask : %.8lx ; auth mask: %.8lx\n",server_bits, host_bits,protocol_flags,supported_ciphers_mask, supported_authentications_mask); serverkey=make_rsa1_string(server_exp,server_mod); hostkey=make_rsa1_string(host_exp,host_mod); build_session_id1(session,server_mod,host_mod); free(server_exp); free(server_mod); free(host_exp); free(host_mod); svr=publickey_from_string(serverkey); host=publickey_from_string(hostkey); session->next_crypto->server_pubkey=string_copy(hostkey); session->next_crypto->server_pubkey_type="ssh-rsa1"; /* now, we must choose an encryption algo */ /* hardcode 3des */ if(!(supported_ciphers_mask & (1<out_buffer,SSH_CMSG_SESSION_KEY); buffer_add_u8(session->out_buffer,SSH_CIPHER_3DES); buffer_add_data(session->out_buffer,session->server_kex.cookie,8); enc_session=encrypt_session_key(session,svr,host); bits=string_len(enc_session)*8 - 7; bits=htons(bits); /* the encrypted mpint */ buffer_add_data(session->out_buffer,&bits,sizeof(u16)); buffer_add_data(session->out_buffer,enc_session->string, string_len(enc_session)); /* the protocol flags */ buffer_add_u32(session->out_buffer,0); packet_send(session); /* we can set encryption */ if(crypt_set_algorithms(session)) return -1; session->current_crypto=session->next_crypto; session->next_crypto=NULL; if(packet_wait(session,SSH_SMSG_SUCCESS,1)){ printf("qqchose a merdé: %s\n",ssh_get_error(session)); exit(1); return -1; } ssh_say(1,"received SSH_SMSG_SUCCESS\n"); return 0; }