1 /*
2 * kex.c - key exchange
3 *
4 * This file is part of the SSH Library
5 *
6 * Copyright (c) 2003-2008 by Aris Adamantiadis
7 *
8 * The SSH Library is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or (at your
11 * option) any later version.
12 *
13 * The SSH Library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
16 * License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with the SSH Library; see the file COPYING. If not, write to
20 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
21 * MA 02111-1307, USA.
22 */
23
24 #include "config.h"
25
26 #include <string.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29
30 #include "libssh/priv.h"
31 #include "libssh/buffer.h"
32 #include "libssh/dh.h"
33 #include "libssh/kex.h"
34 #include "libssh/session.h"
35 #include "libssh/ssh2.h"
36 #include "libssh/string.h"
37
38 #ifdef HAVE_LIBGCRYPT
39 #define BLOWFISH "blowfish-cbc,"
40 #define AES "aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,"
41 #define DES "3des-cbc"
42 #elif defined HAVE_LIBCRYPTO
43 #ifdef HAVE_OPENSSL_BLOWFISH_H
44 #define BLOWFISH "blowfish-cbc,"
45 #else
46 #define BLOWFISH ""
47 #endif
48 #ifdef HAVE_OPENSSL_AES_H
49 #ifdef BROKEN_AES_CTR
50 #define AES "aes256-cbc,aes192-cbc,aes128-cbc,"
51 #else
52 #define AES "aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,"
53 #endif /* BROKEN_AES_CTR */
54 #else
55 #define AES ""
56 #endif
57
58 #define DES "3des-cbc"
59 #endif
60
61 #ifdef WITH_ZLIB
62 #define ZLIB "none,zlib,zlib@openssh.com"
63 #else
64 #define ZLIB "none"
65 #endif
66
67 #ifdef HAVE_ECDH
68 #define KEY_EXCHANGE "ecdh-sha2-nistp256,diffie-hellman-group1-sha1"
69 #else
70 #define KEY_EXCHANGE "diffie-hellman-group1-sha1"
71 #endif
72
73 static const char *default_methods[] = {
74 KEY_EXCHANGE,
75 "ssh-rsa,ssh-dss",
76 AES BLOWFISH DES,
77 AES BLOWFISH DES,
78 "hmac-sha1",
79 "hmac-sha1",
80 "none",
81 "none",
82 "",
83 "",
84 NULL
85 };
86
87 const char *supported_methods[] = {
88 KEY_EXCHANGE,
89 "ssh-rsa,ssh-dss",
90 AES BLOWFISH DES,
91 AES BLOWFISH DES,
92 "hmac-sha1",
93 "hmac-sha1",
94 ZLIB,
95 ZLIB,
96 "",
97 "",
98 NULL
99 };
100
101 /* descriptions of the key exchange packet */
102 const char *ssh_kex_nums[] = {
103 "kex algos",
104 "server host key algo",
105 "encryption client->server",
106 "encryption server->client",
107 "mac algo client->server",
108 "mac algo server->client",
109 "compression algo client->server",
110 "compression algo server->client",
111 "languages client->server",
112 "languages server->client",
113 NULL
114 };
115
116 /* tokenize will return a token of strings delimited by ",". the first element has to be freed */
tokenize(const char * chain)117 static char **tokenize(const char *chain){
118 char **tokens;
119 int n=1;
120 int i=0;
121 char *tmp;
122 char *ptr;
123
124 tmp = strdup(chain);
125 if (tmp == NULL) {
126 return NULL;
127 }
128 ptr = tmp;
129 while(*ptr){
130 if(*ptr==','){
131 n++;
132 *ptr=0;
133 }
134 ptr++;
135 }
136 /* now n contains the number of tokens, the first possibly empty if the list was empty too e.g. "" */
137 tokens=malloc(sizeof(char *) * (n+1) ); /* +1 for the null */
138 if (tokens == NULL) {
139 SAFE_FREE(tmp);
140 return NULL;
141 }
142 ptr=tmp;
143 for(i=0;i<n;i++){
144 tokens[i]=ptr;
145 while(*ptr)
146 ptr++; // find a zero
147 ptr++; // then go one step further
148 }
149 tokens[i]=NULL;
150 return tokens;
151 }
152
153 /* same as tokenize(), but with spaces instead of ',' */
154 /* TODO FIXME rewrite me! */
space_tokenize(const char * chain)155 char **space_tokenize(const char *chain){
156 char **tokens;
157 int n=1;
158 int i=0;
159 char *tmp;
160 char *ptr;
161
162 tmp = strdup(chain);
163 if (tmp == NULL) {
164 return NULL;
165 }
166 ptr = tmp;
167
168 while(*ptr==' ')
169 ++ptr; /* skip initial spaces */
170 while(*ptr){
171 if(*ptr==' '){
172 n++; /* count one token per word */
173 *ptr=0;
174 while(*(ptr+1)==' '){ /* don't count if the tokens have more than 2 spaces */
175 *(ptr++)=0;
176 }
177 }
178 ptr++;
179 }
180 /* now n contains the number of tokens, the first possibly empty if the list was empty too e.g. "" */
181 tokens = malloc(sizeof(char *) * (n + 1)); /* +1 for the null */
182 if (tokens == NULL) {
183 SAFE_FREE(tmp);
184 return NULL;
185 }
186 ptr=tmp; /* we don't pass the initial spaces because the "tmp" pointer is needed by the caller */
187 /* function to free the tokens. */
188 for(i=0;i<n;i++){
189 tokens[i]=ptr;
190 if(i!=n-1){
191 while(*ptr)
192 ptr++; // find a zero
193 while(!*(ptr+1))
194 ++ptr; /* if the zero is followed by other zeros, go through them */
195 ptr++; // then go one step further
196 }
197 }
198 tokens[i]=NULL;
199 return tokens;
200 }
201
202 /* find_matching gets 2 parameters : a list of available objects (available_d), separated by colons,*/
203 /* and a list of preferred objects (preferred_d) */
204 /* it will return a strduped pointer on the first preferred object found in the available objects list */
205
ssh_find_matching(const char * available_d,const char * preferred_d)206 char *ssh_find_matching(const char *available_d, const char *preferred_d){
207 char ** tok_available, **tok_preferred;
208 int i_avail, i_pref;
209 char *ret;
210
211 if ((available_d == NULL) || (preferred_d == NULL)) {
212 return NULL; /* don't deal with null args */
213 }
214
215 tok_available = tokenize(available_d);
216 if (tok_available == NULL) {
217 return NULL;
218 }
219
220 tok_preferred = tokenize(preferred_d);
221 if (tok_preferred == NULL) {
222 SAFE_FREE(tok_available[0]);
223 SAFE_FREE(tok_available);
224 return NULL;
225 }
226
227 for(i_pref=0; tok_preferred[i_pref] ; ++i_pref){
228 for(i_avail=0; tok_available[i_avail]; ++i_avail){
229 if(!strcmp(tok_available[i_avail],tok_preferred[i_pref])){
230 /* match */
231 ret=strdup(tok_available[i_avail]);
232 /* free the tokens */
233 SAFE_FREE(tok_available[0]);
234 SAFE_FREE(tok_preferred[0]);
235 SAFE_FREE(tok_available);
236 SAFE_FREE(tok_preferred);
237 return ret;
238 }
239 }
240 }
241 SAFE_FREE(tok_available[0]);
242 SAFE_FREE(tok_preferred[0]);
243 SAFE_FREE(tok_available);
244 SAFE_FREE(tok_preferred);
245 return NULL;
246 }
247
SSH_PACKET_CALLBACK(ssh_packet_kexinit)248 SSH_PACKET_CALLBACK(ssh_packet_kexinit){
249 int server_kex=session->server;
250 ssh_string str = NULL;
251 char *strings[10];
252 int i;
253
254 enter_function();
255 (void)type;
256 (void)user;
257 memset(strings, 0, sizeof(strings));
258 if(session->session_state != SSH_SESSION_STATE_INITIAL_KEX){
259 ssh_set_error(session,SSH_FATAL,"SSH_KEXINIT received in wrong state");
260 goto error;
261 }
262 if (server_kex) {
263 if (buffer_get_data(packet,session->next_crypto->client_kex.cookie,16) != 16) {
264 ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: no cookie in packet");
265 goto error;
266 }
267
268 if (hashbufin_add_cookie(session, session->next_crypto->client_kex.cookie) < 0) {
269 ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: adding cookie failed");
270 goto error;
271 }
272 } else {
273 if (buffer_get_data(packet,session->next_crypto->server_kex.cookie,16) != 16) {
274 ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: no cookie in packet");
275 goto error;
276 }
277
278 if (hashbufin_add_cookie(session, session->next_crypto->server_kex.cookie) < 0) {
279 ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: adding cookie failed");
280 goto error;
281 }
282 }
283
284 for (i = 0; i < 10; i++) {
285 str = buffer_get_ssh_string(packet);
286 if (str == NULL) {
287 break;
288 }
289
290 if (buffer_add_ssh_string(session->in_hashbuf, str) < 0) {
291 ssh_set_error(session, SSH_FATAL, "Error adding string in hash buffer");
292 goto error;
293 }
294
295 strings[i] = ssh_string_to_char(str);
296 if (strings[i] == NULL) {
297 ssh_set_error_oom(session);
298 goto error;
299 }
300 ssh_string_free(str);
301 str = NULL;
302 }
303
304 /* copy the server kex info into an array of strings */
305 if (server_kex) {
306 for (i = 0; i < SSH_KEX_METHODS; i++) {
307 session->next_crypto->client_kex.methods[i] = strings[i];
308 }
309 } else { /* client */
310 for (i = 0; i < SSH_KEX_METHODS; i++) {
311 session->next_crypto->server_kex.methods[i] = strings[i];
312 }
313 }
314
315 leave_function();
316 session->session_state=SSH_SESSION_STATE_KEXINIT_RECEIVED;
317 session->ssh_connection_callback(session);
318 return SSH_PACKET_USED;
319 error:
320 ssh_string_free(str);
321 for (i = 0; i < 10; i++) {
322 SAFE_FREE(strings[i]);
323 }
324
325 session->session_state = SSH_SESSION_STATE_ERROR;
326 leave_function();
327 return SSH_PACKET_USED;
328 }
329
ssh_list_kex(ssh_session session,struct ssh_kex_struct * kex)330 void ssh_list_kex(ssh_session session, struct ssh_kex_struct *kex) {
331 int i = 0;
332
333 #ifdef DEBUG_CRYPTO
334 ssh_print_hexa("session cookie", kex->cookie, 16);
335 #endif
336 if(kex->methods==NULL){
337 ssh_log(session, SSH_LOG_RARE,"kex->methods is NULL");
338 return;
339 }
340 for(i = 0; i < 10; i++) {
341 ssh_log(session, SSH_LOG_FUNCTIONS, "%s: %s",
342 ssh_kex_nums[i], kex->methods[i]);
343 }
344 }
345
346 /**
347 * @brief sets the key exchange parameters to be sent to the server,
348 * in function of the options and available methods.
349 */
set_client_kex(ssh_session session)350 int set_client_kex(ssh_session session){
351 struct ssh_kex_struct *client= &session->next_crypto->client_kex;
352 int i;
353 const char *wanted;
354 enter_function();
355 ssh_get_random(client->cookie,16,0);
356 memset(client->methods,0,10*sizeof(char **));
357 for (i=0;i<10;i++){
358 wanted=session->wanted_methods[i];
359 if(wanted == NULL)
360 wanted=default_methods[i];
361 client->methods[i]=strdup(wanted);
362 }
363 leave_function();
364 return SSH_OK;
365 }
366
367 /** @brief Select the different methods on basis of client's and
368 * server's kex messages, and watches out if a match is possible.
369 */
ssh_kex_select_methods(ssh_session session)370 int ssh_kex_select_methods (ssh_session session){
371 struct ssh_kex_struct *server = &session->next_crypto->server_kex;
372 struct ssh_kex_struct *client = &session->next_crypto->client_kex;
373 int rc = SSH_ERROR;
374 int i;
375
376 enter_function();
377
378 for (i=0;i<10;i++){
379 session->next_crypto->kex_methods[i]=ssh_find_matching(server->methods[i],client->methods[i]);
380 if(session->next_crypto->kex_methods[i] == NULL && i < SSH_LANG_C_S){
381 ssh_set_error(session,SSH_FATAL,"kex error : no match for method %s: server [%s], client [%s]",
382 ssh_kex_nums[i],server->methods[i],client->methods[i]);
383 goto error;
384 } else if ((i >= SSH_LANG_C_S) && (session->next_crypto->kex_methods[i] == NULL)) {
385 /* we can safely do that for languages */
386 session->next_crypto->kex_methods[i] = strdup("");
387 }
388 }
389 if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group1-sha1") == 0){
390 session->next_crypto->kex_type=SSH_KEX_DH_GROUP1_SHA1;
391 } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "ecdh-sha2-nistp256") == 0){
392 session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP256;
393 }
394 rc = SSH_OK;
395 error:
396 leave_function();
397 return rc;
398 }
399
400
401 /* this function only sends the predefined set of kex methods */
ssh_send_kex(ssh_session session,int server_kex)402 int ssh_send_kex(ssh_session session, int server_kex) {
403 struct ssh_kex_struct *kex = (server_kex ? &session->next_crypto->server_kex :
404 &session->next_crypto->client_kex);
405 ssh_string str = NULL;
406 int i;
407
408 enter_function();
409
410 if (buffer_add_u8(session->out_buffer, SSH2_MSG_KEXINIT) < 0) {
411 goto error;
412 }
413 if (buffer_add_data(session->out_buffer, kex->cookie, 16) < 0) {
414 goto error;
415 }
416
417 if (hashbufout_add_cookie(session) < 0) {
418 goto error;
419 }
420
421 ssh_list_kex(session, kex);
422
423 for (i = 0; i < 10; i++) {
424 str = ssh_string_from_char(kex->methods[i]);
425 if (str == NULL) {
426 goto error;
427 }
428
429 if (buffer_add_ssh_string(session->out_hashbuf, str) < 0) {
430 goto error;
431 }
432 if (buffer_add_ssh_string(session->out_buffer, str) < 0) {
433 goto error;
434 }
435 ssh_string_free(str);
436 }
437
438 if (buffer_add_u8(session->out_buffer, 0) < 0) {
439 goto error;
440 }
441 if (buffer_add_u32(session->out_buffer, 0) < 0) {
442 goto error;
443 }
444
445 if (packet_send(session) == SSH_ERROR) {
446 leave_function();
447 return -1;
448 }
449
450 leave_function();
451 return 0;
452 error:
453 buffer_reinit(session->out_buffer);
454 buffer_reinit(session->out_hashbuf);
455 ssh_string_free(str);
456
457 leave_function();
458 return -1;
459 }
460
461 /* returns 1 if at least one of the name algos is in the default algorithms table */
verify_existing_algo(int algo,const char * name)462 int verify_existing_algo(int algo, const char *name){
463 char *ptr;
464 if(algo>9 || algo <0)
465 return -1;
466 ptr=ssh_find_matching(supported_methods[algo],name);
467 if(ptr){
468 free(ptr);
469 return 1;
470 }
471 return 0;
472 }
473
474 /* vim: set ts=2 sw=2 et cindent: */
475