1 /* kex.c is used well, in key exchange :-) */ 2 /* 3 Copyright 2003 Aris Adamantiadis 4 5 This file is part of the SSH Library 6 7 The SSH Library is free software; you can redistribute it and/or modify 8 it under the terms of the GNU Lesser General Public License as published by 9 the Free Software Foundation; either version 2.1 of the License, or (at your 10 option) any later version. 11 12 The SSH Library is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 14 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 15 License for more details. 16 17 You should have received a copy of the GNU Lesser General Public License 18 along with the SSH Library; see the file COPYING. If not, write to 19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, 20 MA 02111-1307, USA. */ 21 22 #include <string.h> 23 #include <stdlib.h> 24 #include "libssh/priv.h" 25 #include "libssh/ssh2.h" 26 #ifdef HAVE_OPENSSL_BLOWFISH_H 27 #define BLOWFISH "blowfish-cbc" 28 #else 29 #define BLOWFISH "" 30 #endif 31 #ifdef HAVE_OPENSSL_AES_H 32 #define AES "aes256-cbc,aes192-cbc,aes128-cbc," 33 #else 34 #define AES "" 35 #endif 36 #ifdef HAVE_LIBZ 37 #define ZLIB "none,zlib" 38 #else 39 #define ZLIB "none" 40 #endif 41 char *default_methods[]={ 42 "diffie-hellman-group1-sha1","ssh-dss,ssh-rsa",AES BLOWFISH, AES BLOWFISH, 43 "hmac-sha1","hmac-sha1","none","none","","",NULL }; 44 char *supported_methods[]={ 45 "diffie-hellman-group1-sha1","ssh-dss,ssh-rsa",AES BLOWFISH,AES BLOWFISH, 46 "hmac-sha1","hmac-sha1",ZLIB,ZLIB,"","",NULL }; 47 /* descriptions of the key exchange packet */ 48 char *ssh_kex_nums[]={ 49 "kex algos","server host key algo","encryption client->server","encryption server->client", 50 "mac algo client->server","mac algo server->client","compression algo client->server", 51 "compression algo server->client","languages client->server","languages server->client",NULL}; 52 53 /* tokenize will return a token of strings delimited by ",". the first element has to be freed */ 54 static char **tokenize(char *chain){ 55 char **tokens; 56 int n=1; 57 int i=0; 58 char *ptr=chain=strdup(chain); 59 while(*ptr){ 60 if(*ptr==','){ 61 n++; 62 *ptr=0; 63 } 64 ptr++; 65 } 66 /* now n contains the number of tokens, the first possibly empty if the list was empty too e.g. "" */ 67 tokens=malloc(sizeof(char *) * (n+1) ); /* +1 for the null */ 68 ptr=chain; 69 for(i=0;i<n;i++){ 70 tokens[i]=ptr; 71 while(*ptr) 72 ptr++; // find a zero 73 ptr++; // then go one step further 74 } 75 tokens[i]=NULL; 76 return tokens; 77 } 78 79 /* same as tokenize(), but with spaces instead of ',' */ 80 char **space_tokenize(char *chain){ 81 char **tokens; 82 int n=1; 83 int i=0; 84 char *ptr=chain=strdup(chain); 85 while(*ptr==' ') 86 ++ptr; /* skip initial spaces */ 87 while(*ptr){ 88 if(*ptr==' '){ 89 n++; /* count one token per word */ 90 *ptr=0; 91 while(*(ptr+1)==' '){ /* don't count if the tokens have more than 2 spaces */ 92 *(ptr++)=0; 93 } 94 } 95 ptr++; 96 } 97 /* now n contains the number of tokens, the first possibly empty if the list was empty too e.g. "" */ 98 tokens=malloc(sizeof(char *) * (n+1) ); /* +1 for the null */ 99 ptr=chain; /* we don't pass the initial spaces because the "chain" pointer is needed by the caller */ 100 /* function to free the tokens. */ 101 for(i=0;i<n;i++){ 102 tokens[i]=ptr; 103 if(i!=n-1){ 104 while(*ptr) 105 ptr++; // find a zero 106 while(!*(ptr+1)) 107 ++ptr; /* if the zero is followed by other zeros, go through them */ 108 ptr++; // then go one step further 109 } 110 } 111 tokens[i]=NULL; 112 return tokens; 113 } 114 115 /* find_matching gets 2 parameters : a list of available objects (in_d), separated by colons,*/ 116 /* and a list of prefered objects (what_d) */ 117 /* it will return a strduped pointer on the first prefered object found in the available objects list */ 118 119 static char *find_matching(char *in_d, char *what_d){ 120 char ** tok_in, **tok_what; 121 int i_in, i_what; 122 char *ret; 123 124 if( ! (in_d && what_d)) 125 return NULL; /* don't deal with null args */ 126 ssh_say(3,"find_matching(\"%s\",\"%s\") = ",in_d,what_d); 127 tok_in=tokenize(in_d); 128 tok_what=tokenize(what_d); 129 for(i_in=0; tok_in[i_in]; ++i_in){ 130 for(i_what=0; tok_what[i_what] ; ++i_what){ 131 if(!strcmp(tok_in[i_in],tok_what[i_what])){ 132 /* match */ 133 ssh_say(3,"\"%s\"\n",tok_in[i_in]); 134 ret=strdup(tok_in[i_in]); 135 /* free the tokens */ 136 free(tok_in[0]); 137 free(tok_what[0]); 138 free(tok_in); 139 free(tok_what); 140 return ret; 141 } 142 } 143 } 144 ssh_say(3,"NULL\n"); 145 free(tok_in[0]); 146 free(tok_what[0]); 147 free(tok_in); 148 free(tok_what); 149 return NULL; 150 } 151 152 int ssh_get_kex(SSH_SESSION *session,int server_kex ){ 153 STRING *str; 154 char *strings[10]; 155 int i; 156 if(packet_wait(session,SSH2_MSG_KEXINIT,1)) 157 return -1; 158 if(buffer_get_data(session->in_buffer,session->server_kex.cookie,16)!=16){ 159 ssh_set_error((session->connected?session:NULL),SSH_FATAL,"get_kex(): no cookie in packet"); 160 return -1; 161 } 162 hashbufin_add_cookie(session,session->server_kex.cookie); 163 memset(strings,0,sizeof(char *)*10); 164 for(i=0;i<10;++i){ 165 str=buffer_get_ssh_string(session->in_buffer); 166 if(!str) 167 break; 168 if(str){ 169 buffer_add_ssh_string(session->in_hashbuf,str); 170 strings[i]=string_to_char(str); 171 free(str); 172 } else 173 strings[i]=NULL; 174 } 175 /* copy the server kex info into an array of strings */ 176 if(server_kex){ 177 session->client_kex.methods=malloc( 10 * sizeof(char **)); 178 for(i=0;i<10;++i) 179 session->client_kex.methods[i]=strings[i]; 180 } else { // client 181 session->server_kex.methods=malloc( 10 * sizeof(char **)); 182 for(i=0;i<10;++i) 183 session->server_kex.methods[i]=strings[i]; 184 } 185 return 0; 186 } 187 188 void list_kex(KEX *kex){ 189 int i=0; 190 #ifdef DEBUG_CRYPTO 191 ssh_print_hexa("session cookie",kex->cookie,16); 192 #endif 193 for(i=0;i<10;i++){ 194 ssh_say(2,"%s : %s\n",ssh_kex_nums[i],kex->methods[i]); 195 } 196 } 197 198 /* set_kex basicaly look at the option structure of the session and set the output kex message */ 199 /* it must be aware of the server kex message */ 200 /* it can fail if option is null, not any user specified kex method matches the server one, if not any default kex matches */ 201 202 int set_kex(SSH_SESSION *session){ 203 KEX *server = &session->server_kex; 204 KEX *client=&session->client_kex; 205 SSH_OPTIONS *options=session->options; 206 int i; 207 char *wanted; 208 /* the client might ask for a specific cookie to be sent. useful for server debugging */ 209 if(options->wanted_cookie) 210 memcpy(client->cookie,options->wanted_cookie,16); 211 else 212 ssh_get_random(client->cookie,16); 213 client->methods=malloc(10 * sizeof(char **)); 214 memset(client->methods,0,10*sizeof(char **)); 215 for (i=0;i<10;i++){ 216 if(!(wanted=options->wanted_methods[i])) 217 wanted=default_methods[i]; 218 client->methods[i]=find_matching(server->methods[i],wanted); 219 if(!client->methods[i] && i < KEX_LANG_C_S){ 220 ssh_set_error((session->connected?session:NULL),SSH_FATAL,"kex error : did not find one of algos %s in list %s for %s", 221 wanted,server->methods[i],ssh_kex_nums[i]); 222 return -1; 223 } else { 224 if(i>=KEX_LANG_C_S && !client->methods[i]) 225 client->methods[i]=strdup(""); // we can safely do that for languages 226 } 227 } 228 return 0; 229 } 230 231 /* this function only sends the predefined set of kex methods */ 232 void send_kex(SSH_SESSION *session, int server_kex){ 233 STRING *str; 234 int i=0; 235 KEX *kex=(server_kex ? &session->server_kex : &session->client_kex); 236 packet_clear_out(session); 237 buffer_add_u8(session->out_buffer,SSH2_MSG_KEXINIT); 238 buffer_add_data(session->out_buffer,kex->cookie,16); 239 hashbufout_add_cookie(session); 240 list_kex(kex); 241 for(i=0;i<10;i++){ 242 str=string_from_char(kex->methods[i]); 243 buffer_add_ssh_string(session->out_hashbuf,str); 244 buffer_add_ssh_string(session->out_buffer,str); 245 free(str); 246 } 247 i=0; 248 buffer_add_u8(session->out_buffer,0); 249 buffer_add_u32(session->out_buffer,0); 250 packet_send(session); 251 } 252 253 /* returns 1 if at least one of the name algos is in the default algorithms table */ 254 int verify_existing_algo(int algo, char *name){ 255 char *ptr; 256 if(algo>9 || algo <0) 257 return -1; 258 ptr=find_matching(supported_methods[algo],name); 259 if(ptr){ 260 free(ptr); 261 return 1; 262 } 263 return 0; 264 } |