1 /*
2 * This file is part of the SSH Library
3 *
4 * Copyright (c) 2018 by Red Hat, Inc.
5 *
6 * Authors: Jakub Jelen <jjelen@redhat.com>
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 #define LIBSSH_STATIC
27
28 #include "torture.h"
29 #include "libssh/sftp.h"
30 #include "libssh/libssh.h"
31 #include "libssh/priv.h"
32 #include "libssh/session.h"
33 #include "libssh/crypto.h"
34
35 #include <errno.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39 #include <pwd.h>
40
41 static uint64_t bytes = 2048; /* 2KB (more than the authentication phase) */
42
sshd_setup(void ** state)43 static int sshd_setup(void **state)
44 {
45 torture_setup_sshd_server(state, false);
46
47 return 0;
48 }
49
sshd_teardown(void ** state)50 static int sshd_teardown(void **state)
51 {
52 torture_teardown_sshd_server(state);
53
54 return 0;
55 }
56
session_setup(void ** state)57 static int session_setup(void **state)
58 {
59 struct torture_state *s = *state;
60 int verbosity = torture_libssh_verbosity();
61 struct passwd *pwd;
62 bool b = false;
63 int rc;
64
65 pwd = getpwnam("bob");
66 assert_non_null(pwd);
67
68 rc = setuid(pwd->pw_uid);
69 assert_return_code(rc, errno);
70
71 s->ssh.session = ssh_new();
72 assert_non_null(s->ssh.session);
73
74 ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
75 ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
76
77 /* Authenticate as alice with bob's pubkey */
78 rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
79 assert_int_equal(rc, SSH_OK);
80
81 /* Make sure no other configuration options from system will get used */
82 rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_PROCESS_CONFIG, &b);
83 assert_ssh_return_code(s->ssh.session, rc);
84
85 /* Make sure we do not interfere with another ssh-agent */
86 unsetenv("SSH_AUTH_SOCK");
87 unsetenv("SSH_AGENT_PID");
88
89 return 0;
90 }
91
session_teardown(void ** state)92 static int session_teardown(void **state)
93 {
94 struct torture_state *s = *state;
95
96 ssh_free(s->ssh.session);
97
98 return 0;
99 }
100
101 /* Check that the default limits for rekeying are enforced.
102 * the limits are too high for testsuite to verify so
103 * we should be fine with checking the values in internal
104 * structures
105 */
torture_rekey_default(void ** state)106 static void torture_rekey_default(void **state)
107 {
108 struct torture_state *s = *state;
109 int rc;
110 struct ssh_crypto_struct *c = NULL;
111
112 /* Define preferred ciphers: */
113 if (ssh_fips_mode()) {
114 /* We do not have any FIPS allowed cipher with different block size */
115 rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_CIPHERS_C_S,
116 "aes128-gcm@openssh.com");
117 } else {
118 /* (out) C->S has 8B block */
119 rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_CIPHERS_C_S,
120 "chacha20-poly1305@openssh.com");
121 }
122 assert_ssh_return_code(s->ssh.session, rc);
123 /* (in) S->C has 16B block */
124 rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_CIPHERS_S_C,
125 "aes128-cbc");
126 assert_ssh_return_code(s->ssh.session, rc);
127
128 rc = ssh_connect(s->ssh.session);
129 assert_ssh_return_code(s->ssh.session, rc);
130
131 c = s->ssh.session->current_crypto;
132 /* The blocks limit is set correctly */
133 /* For S->C (in) we have 16B block => 2**(L/4) blocks */
134 assert_int_equal(c->in_cipher->max_blocks,
135 (uint64_t)1 << (2 * c->in_cipher->blocksize));
136 if (ssh_fips_mode()) {
137 /* We do not have any FIPS allowed cipher with different block size */
138 assert_int_equal(c->in_cipher->max_blocks,
139 (uint64_t)1 << (2 * c->in_cipher->blocksize));
140 } else {
141 /* The C->S (out) we have 8B block => 1 GB limit */
142 assert_int_equal(c->out_cipher->max_blocks,
143 ((uint64_t)1 << 30) / c->out_cipher->blocksize);
144 }
145
146 ssh_disconnect(s->ssh.session);
147 }
148
149 /* We lower the rekey limits manually and check that the rekey
150 * really happens when sending data
151 */
torture_rekey_send(void ** state)152 static void torture_rekey_send(void **state)
153 {
154 struct torture_state *s = *state;
155 int rc;
156 char data[256];
157 unsigned int i;
158 struct ssh_crypto_struct *c = NULL;
159 unsigned char *secret_hash = NULL;
160
161 rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_REKEY_DATA, &bytes);
162 assert_ssh_return_code(s->ssh.session, rc);
163
164 rc = ssh_connect(s->ssh.session);
165 assert_ssh_return_code(s->ssh.session, rc);
166
167 /* The blocks limit is set correctly */
168 c = s->ssh.session->current_crypto;
169 assert_int_equal(c->in_cipher->max_blocks,
170 bytes / c->in_cipher->blocksize);
171 assert_int_equal(c->out_cipher->max_blocks,
172 bytes / c->out_cipher->blocksize);
173 /* We should have less encrypted packets than transfered (first are not encrypted) */
174 assert_true(c->out_cipher->packets < s->ssh.session->send_seq);
175 assert_true(c->in_cipher->packets < s->ssh.session->recv_seq);
176 /* Copy the initial secret hash = session_id so we know we changed keys later */
177 secret_hash = malloc(c->digest_len);
178 assert_non_null(secret_hash);
179 memcpy(secret_hash, c->secret_hash, c->digest_len);
180
181 /* OpenSSH can not rekey before authentication so authenticate here */
182 rc = ssh_userauth_none(s->ssh.session, NULL);
183 /* This request should return a SSH_REQUEST_DENIED error */
184 if (rc == SSH_ERROR) {
185 assert_int_equal(ssh_get_error_code(s->ssh.session), SSH_REQUEST_DENIED);
186 }
187 rc = ssh_userauth_list(s->ssh.session, NULL);
188 assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
189
190 rc = ssh_userauth_publickey_auto(s->ssh.session, NULL, NULL);
191 assert_int_equal(rc, SSH_AUTH_SUCCESS);
192
193 /* send ignore packets of up to 1KB to trigger rekey */
194 memset(data, 0, sizeof(data));
195 memset(data, 'A', 128);
196 for (i = 0; i < 16; i++) {
197 ssh_send_ignore(s->ssh.session, data);
198 ssh_handle_packets(s->ssh.session, 50);
199 }
200
201 /* The rekey limit was restored in the new crypto to the same value */
202 c = s->ssh.session->current_crypto;
203 assert_int_equal(c->in_cipher->max_blocks, bytes / c->in_cipher->blocksize);
204 assert_int_equal(c->out_cipher->max_blocks, bytes / c->out_cipher->blocksize);
205 /* Check that the secret hash is different than initially */
206 assert_memory_not_equal(secret_hash, c->secret_hash, c->digest_len);
207 free(secret_hash);
208
209 ssh_disconnect(s->ssh.session);
210 }
211
212 #ifdef WITH_SFTP
session_setup_sftp(void ** state)213 static void session_setup_sftp(void **state)
214 {
215 struct torture_state *s = *state;
216 int rc;
217
218 rc = ssh_connect(s->ssh.session);
219 assert_ssh_return_code(s->ssh.session, rc);
220
221 /* OpenSSH can not rekey before authentication so authenticate here */
222 rc = ssh_userauth_none(s->ssh.session, NULL);
223 /* This request should return a SSH_REQUEST_DENIED error */
224 if (rc == SSH_ERROR) {
225 assert_int_equal(ssh_get_error_code(s->ssh.session), SSH_REQUEST_DENIED);
226 }
227 rc = ssh_userauth_list(s->ssh.session, NULL);
228 assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
229
230 rc = ssh_userauth_publickey_auto(s->ssh.session, NULL, NULL);
231 assert_int_equal(rc, SSH_AUTH_SUCCESS);
232
233 /* Initialize SFTP session */
234 s->ssh.tsftp = torture_sftp_session(s->ssh.session);
235 assert_non_null(s->ssh.tsftp);
236 }
237
session_setup_sftp_client(void ** state)238 static int session_setup_sftp_client(void **state)
239 {
240 struct torture_state *s = *state;
241 int rc;
242
243 session_setup(state);
244
245 rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_REKEY_DATA, &bytes);
246 assert_ssh_return_code(s->ssh.session, rc);
247
248 session_setup_sftp(state);
249
250 return 0;
251 }
252
253 #define MAX_XFER_BUF_SIZE 16384
254
255 /* To trigger rekey by receiving data, the easiest thing is probably to
256 * use sftp
257 */
torture_rekey_recv(void ** state)258 static void torture_rekey_recv(void **state)
259 {
260 struct torture_state *s = *state;
261 struct ssh_crypto_struct *c = NULL;
262 unsigned char *secret_hash = NULL;
263
264 char libssh_tmp_file[] = "/tmp/libssh_sftp_test_XXXXXX";
265 char buf[MAX_XFER_BUF_SIZE];
266 ssize_t bytesread;
267 ssize_t byteswritten;
268 int fd;
269 sftp_file file;
270 mode_t mask;
271
272 /* The blocks limit is set correctly */
273 c = s->ssh.session->current_crypto;
274 assert_int_equal(c->in_cipher->max_blocks, bytes / c->in_cipher->blocksize);
275 assert_int_equal(c->out_cipher->max_blocks, bytes / c->out_cipher->blocksize);
276 /* We should have less encrypted packets than transfered (first are not encrypted) */
277 assert_true(c->out_cipher->packets < s->ssh.session->send_seq);
278 assert_true(c->in_cipher->packets < s->ssh.session->recv_seq);
279 /* Copy the initial secret hash = session_id so we know we changed keys later */
280 secret_hash = malloc(c->digest_len);
281 assert_non_null(secret_hash);
282 memcpy(secret_hash, c->secret_hash, c->digest_len);
283
284 /* Download a file */
285 file = sftp_open(s->ssh.tsftp->sftp, SSH_EXECUTABLE, O_RDONLY, 0);
286 assert_non_null(file);
287
288 mask = umask(S_IRWXO | S_IRWXG);
289 fd = mkstemp(libssh_tmp_file);
290 umask(mask);
291 unlink(libssh_tmp_file);
292
293 for (;;) {
294 bytesread = sftp_read(file, buf, MAX_XFER_BUF_SIZE);
295 if (bytesread == 0) {
296 break; /* EOF */
297 }
298 assert_false(bytesread < 0);
299
300 byteswritten = write(fd, buf, bytesread);
301 assert_int_equal(byteswritten, bytesread);
302 }
303
304 close(fd);
305
306 /* The rekey limit was restored in the new crypto to the same value */
307 c = s->ssh.session->current_crypto;
308 assert_int_equal(c->in_cipher->max_blocks, bytes / c->in_cipher->blocksize);
309 assert_int_equal(c->out_cipher->max_blocks, bytes / c->out_cipher->blocksize);
310 /* Check that the secret hash is different than initially */
311 assert_memory_not_equal(secret_hash, c->secret_hash, c->digest_len);
312 free(secret_hash);
313
314 torture_sftp_close(s->ssh.tsftp);
315 ssh_disconnect(s->ssh.session);
316 }
317 #endif /* WITH_SFTP */
318
319 /* Rekey time requires rekey after specified time and is off by default.
320 * Setting the time to small enough value and waiting, we should trigger
321 * rekey on the first sent packet afterward.
322 */
torture_rekey_time(void ** state)323 static void torture_rekey_time(void **state)
324 {
325 struct torture_state *s = *state;
326 int rc;
327 char data[256];
328 unsigned int i;
329 uint32_t time = 3; /* 3 seconds */
330 struct ssh_crypto_struct *c = NULL;
331 unsigned char *secret_hash = NULL;
332
333 rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_REKEY_TIME, &time);
334 assert_ssh_return_code(s->ssh.session, rc);
335 /* The time is internally stored in microseconds */
336 assert_int_equal(time * 1000, s->ssh.session->opts.rekey_time);
337
338 rc = ssh_connect(s->ssh.session);
339 assert_ssh_return_code(s->ssh.session, rc);
340
341 /* Copy the initial secret hash = session_id so we know we changed keys later */
342 c = s->ssh.session->current_crypto;
343 secret_hash = malloc(c->digest_len);
344 assert_non_null(secret_hash);
345 memcpy(secret_hash, c->secret_hash, c->digest_len);
346
347 /* OpenSSH can not rekey before authentication so authenticate here */
348 rc = ssh_userauth_none(s->ssh.session, NULL);
349 /* This request should return a SSH_REQUEST_DENIED error */
350 if (rc == SSH_ERROR) {
351 assert_int_equal(ssh_get_error_code(s->ssh.session), SSH_REQUEST_DENIED);
352 }
353 rc = ssh_userauth_list(s->ssh.session, NULL);
354 assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
355
356 rc = ssh_userauth_publickey_auto(s->ssh.session, NULL, NULL);
357 assert_int_equal(rc, SSH_AUTH_SUCCESS);
358
359 /* Send some data. This should not trigger rekey yet */
360 memset(data, 0, sizeof(data));
361 memset(data, 'A', 8);
362 for (i = 0; i < 3; i++) {
363 ssh_send_ignore(s->ssh.session, data);
364 ssh_handle_packets(s->ssh.session, 50);
365 }
366
367 /* Check that the secret hash is the same */
368 c = s->ssh.session->current_crypto;
369 assert_memory_equal(secret_hash, c->secret_hash, c->digest_len);
370
371 /* Wait some more time */
372 sleep(3);
373
374 /* send some more data to trigger rekey and handle the
375 * key exchange "in background" */
376 for (i = 0; i < 8; i++) {
377 ssh_send_ignore(s->ssh.session, data);
378 ssh_handle_packets(s->ssh.session, 50);
379 }
380
381 /* Check that the secret hash is different than initially */
382 c = s->ssh.session->current_crypto;
383 assert_memory_not_equal(secret_hash, c->secret_hash, c->digest_len);
384 free(secret_hash);
385
386 ssh_disconnect(s->ssh.session);
387 }
388
389 /* We lower the rekey limits manually and check that the rekey
390 * really happens when sending data
391 */
torture_rekey_server_send(void ** state)392 static void torture_rekey_server_send(void **state)
393 {
394 struct torture_state *s = *state;
395 int rc;
396 char data[256];
397 unsigned int i;
398 struct ssh_crypto_struct *c = NULL;
399 unsigned char *secret_hash = NULL;
400 const char *sshd_config = "RekeyLimit 2K none";
401
402 torture_update_sshd_config(state, sshd_config);
403
404 rc = ssh_connect(s->ssh.session);
405 assert_ssh_return_code(s->ssh.session, rc);
406
407 /* Copy the initial secret hash = session_id so we know we changed keys later */
408 c = s->ssh.session->current_crypto;
409 secret_hash = malloc(c->digest_len);
410 assert_non_null(secret_hash);
411 memcpy(secret_hash, c->secret_hash, c->digest_len);
412
413 /* OpenSSH can not rekey before authentication so authenticate here */
414 rc = ssh_userauth_none(s->ssh.session, NULL);
415 /* This request should return a SSH_REQUEST_DENIED error */
416 if (rc == SSH_ERROR) {
417 assert_int_equal(ssh_get_error_code(s->ssh.session), SSH_REQUEST_DENIED);
418 }
419 rc = ssh_userauth_list(s->ssh.session, NULL);
420 assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
421
422 rc = ssh_userauth_publickey_auto(s->ssh.session, NULL, NULL);
423 assert_int_equal(rc, SSH_AUTH_SUCCESS);
424
425 /* send ignore packets of up to 1KB to trigger rekey */
426 memset(data, 0, sizeof(data));
427 memset(data, 'A', 128);
428 for (i = 0; i < 20; i++) {
429 ssh_send_ignore(s->ssh.session, data);
430 ssh_handle_packets(s->ssh.session, 50);
431 }
432
433 /* Check that the secret hash is different than initially */
434 c = s->ssh.session->current_crypto;
435 assert_memory_not_equal(secret_hash, c->secret_hash, c->digest_len);
436 free(secret_hash);
437
438 ssh_disconnect(s->ssh.session);
439 }
440
torture_rekey_different_kex(void ** state)441 static void torture_rekey_different_kex(void **state)
442 {
443 struct torture_state *s = *state;
444 int rc;
445 char data[256];
446 unsigned int i;
447 struct ssh_crypto_struct *c = NULL;
448 unsigned char *secret_hash = NULL;
449 size_t secret_hash_len = 0;
450 const char *kex1 = "diffie-hellman-group14-sha256,curve25519-sha256,ecdh-sha2-nistp256";
451 const char *kex2 = "diffie-hellman-group18-sha512,diffie-hellman-group16-sha512,ecdh-sha2-nistp521";
452
453 /* Use short digest for initial key exchange */
454 rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_KEY_EXCHANGE, kex1);
455 assert_ssh_return_code(s->ssh.session, rc);
456
457 rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_REKEY_DATA, &bytes);
458 assert_ssh_return_code(s->ssh.session, rc);
459
460 rc = ssh_connect(s->ssh.session);
461 assert_ssh_return_code(s->ssh.session, rc);
462
463 /* The blocks limit is set correctly */
464 c = s->ssh.session->current_crypto;
465 assert_int_equal(c->in_cipher->max_blocks,
466 bytes / c->in_cipher->blocksize);
467 assert_int_equal(c->out_cipher->max_blocks,
468 bytes / c->out_cipher->blocksize);
469 /* We should have less encrypted packets than transfered (first are not encrypted) */
470 assert_true(c->out_cipher->packets < s->ssh.session->send_seq);
471 assert_true(c->in_cipher->packets < s->ssh.session->recv_seq);
472 /* Copy the initial secret hash = session_id so we know we changed keys later */
473 secret_hash = malloc(c->digest_len);
474 assert_non_null(secret_hash);
475 memcpy(secret_hash, c->secret_hash, c->digest_len);
476 secret_hash_len = c->digest_len;
477 assert_int_equal(secret_hash_len, 32); /* SHA256 len */
478
479 /* OpenSSH can not rekey before authentication so authenticate here */
480 rc = ssh_userauth_none(s->ssh.session, NULL);
481 /* This request should return a SSH_REQUEST_DENIED error */
482 if (rc == SSH_ERROR) {
483 assert_int_equal(ssh_get_error_code(s->ssh.session), SSH_REQUEST_DENIED);
484 }
485 rc = ssh_userauth_list(s->ssh.session, NULL);
486 assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
487
488 rc = ssh_userauth_publickey_auto(s->ssh.session, NULL, NULL);
489 assert_int_equal(rc, SSH_AUTH_SUCCESS);
490
491 /* Now try to change preference of key exchange algorithm to something with larger digest */
492 rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_KEY_EXCHANGE, kex2);
493 assert_ssh_return_code(s->ssh.session, rc);
494
495 /* send ignore packets of up to 1KB to trigger rekey. Send litle bit more
496 * to make sure the rekey it completes with all different ciphers (paddings */
497 memset(data, 0, sizeof(data));
498 memset(data, 'A', 128);
499 for (i = 0; i < 20; i++) {
500 ssh_send_ignore(s->ssh.session, data);
501 ssh_handle_packets(s->ssh.session, 50);
502 }
503
504 /* The rekey limit was restored in the new crypto to the same value */
505 c = s->ssh.session->current_crypto;
506 assert_int_equal(c->in_cipher->max_blocks, bytes / c->in_cipher->blocksize);
507 assert_int_equal(c->out_cipher->max_blocks, bytes / c->out_cipher->blocksize);
508 /* Check that the secret hash is different than initially */
509 assert_int_equal(c->digest_len, 64); /* SHA512 len */
510 assert_memory_not_equal(secret_hash, c->secret_hash, secret_hash_len);
511 /* Session ID stays same after one rekey */
512 assert_memory_equal(secret_hash, c->session_id, secret_hash_len);
513 free(secret_hash);
514
515 assert_int_equal(ssh_is_connected(s->ssh.session), 1);
516 assert_int_equal(s->ssh.session->session_state, SSH_SESSION_STATE_AUTHENTICATED);
517
518 ssh_disconnect(s->ssh.session);
519 }
520
torture_rekey_server_different_kex(void ** state)521 static void torture_rekey_server_different_kex(void **state)
522 {
523 struct torture_state *s = *state;
524 int rc;
525 char data[256];
526 unsigned int i;
527 struct ssh_crypto_struct *c = NULL;
528 unsigned char *secret_hash = NULL;
529 size_t secret_hash_len = 0;
530 const char *sshd_config = "RekeyLimit 2K none";
531 const char *kex1 = "diffie-hellman-group14-sha256,curve25519-sha256,ecdh-sha2-nistp256";
532 const char *kex2 = "diffie-hellman-group18-sha512,diffie-hellman-group16-sha512";
533
534 /* Use short digest for initial key exchange */
535 rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_KEY_EXCHANGE, kex1);
536 assert_ssh_return_code(s->ssh.session, rc);
537
538 torture_update_sshd_config(state, sshd_config);
539
540 rc = ssh_connect(s->ssh.session);
541 assert_ssh_return_code(s->ssh.session, rc);
542
543 /* Copy the initial secret hash = session_id so we know we changed keys later */
544 c = s->ssh.session->current_crypto;
545 secret_hash = malloc(c->digest_len);
546 assert_non_null(secret_hash);
547 memcpy(secret_hash, c->secret_hash, c->digest_len);
548 secret_hash_len = c->digest_len;
549 assert_int_equal(secret_hash_len, 32); /* SHA256 len */
550
551 /* OpenSSH can not rekey before authentication so authenticate here */
552 rc = ssh_userauth_none(s->ssh.session, NULL);
553 /* This request should return a SSH_REQUEST_DENIED error */
554 if (rc == SSH_ERROR) {
555 assert_int_equal(ssh_get_error_code(s->ssh.session), SSH_REQUEST_DENIED);
556 }
557 rc = ssh_userauth_list(s->ssh.session, NULL);
558 assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
559
560 rc = ssh_userauth_publickey_auto(s->ssh.session, NULL, NULL);
561 assert_int_equal(rc, SSH_AUTH_SUCCESS);
562
563 /* Now try to change preference of key exchange algorithm to something with larger digest */
564 rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_KEY_EXCHANGE, kex2);
565 assert_ssh_return_code(s->ssh.session, rc);
566
567 /* send ignore packets of up to 1KB to trigger rekey. Send litle bit more
568 * to make sure the rekey it completes with all different ciphers (paddings */
569 memset(data, 0, sizeof(data));
570 memset(data, 'A', 128);
571 for (i = 0; i < 25; i++) {
572 ssh_send_ignore(s->ssh.session, data);
573 ssh_handle_packets(s->ssh.session, 50);
574 }
575
576 /* Check that the secret hash is different than initially */
577 c = s->ssh.session->current_crypto;
578 assert_int_equal(c->digest_len, 64); /* SHA512 len */
579 assert_memory_not_equal(secret_hash, c->secret_hash, secret_hash_len);
580 /* Session ID stays same after one rekey */
581 assert_memory_equal(secret_hash, c->session_id, secret_hash_len);
582 free(secret_hash);
583
584 ssh_disconnect(s->ssh.session);
585 }
586
587
588 #ifdef WITH_SFTP
session_setup_sftp_server(void ** state)589 static int session_setup_sftp_server(void **state)
590 {
591 const char *sshd_config = "RekeyLimit 2K none";
592
593 session_setup(state);
594
595 torture_update_sshd_config(state, sshd_config);
596
597 session_setup_sftp(state);
598
599 return 0;
600 }
601
torture_rekey_server_recv(void ** state)602 static void torture_rekey_server_recv(void **state)
603 {
604 struct torture_state *s = *state;
605 struct ssh_crypto_struct *c = NULL;
606 unsigned char *secret_hash = NULL;
607 char libssh_tmp_file[] = "/tmp/libssh_sftp_test_XXXXXX";
608 char buf[MAX_XFER_BUF_SIZE];
609 ssize_t bytesread;
610 ssize_t byteswritten;
611 int fd;
612 sftp_file file;
613 mode_t mask;
614
615 /* Copy the initial secret hash = session_id so we know we changed keys later */
616 c = s->ssh.session->current_crypto;
617 secret_hash = malloc(c->digest_len);
618 assert_non_null(secret_hash);
619 memcpy(secret_hash, c->secret_hash, c->digest_len);
620
621 /* Download a file */
622 file = sftp_open(s->ssh.tsftp->sftp, SSH_EXECUTABLE, O_RDONLY, 0);
623 assert_non_null(file);
624
625 mask = umask(S_IRWXO | S_IRWXG);
626 fd = mkstemp(libssh_tmp_file);
627 umask(mask);
628 unlink(libssh_tmp_file);
629
630 for (;;) {
631 bytesread = sftp_read(file, buf, MAX_XFER_BUF_SIZE);
632 if (bytesread == 0) {
633 break; /* EOF */
634 }
635 assert_false(bytesread < 0);
636
637 byteswritten = write(fd, buf, bytesread);
638 assert_int_equal(byteswritten, bytesread);
639 }
640
641 close(fd);
642
643 /* Check that the secret hash is different than initially */
644 c = s->ssh.session->current_crypto;
645 assert_memory_not_equal(secret_hash, c->secret_hash, c->digest_len);
646 free(secret_hash);
647
648 torture_sftp_close(s->ssh.tsftp);
649 ssh_disconnect(s->ssh.session);
650 }
651 #endif /* WITH_SFTP */
652
653
torture_run_tests(void)654 int torture_run_tests(void) {
655 int rc;
656 struct CMUnitTest tests[] = {
657 cmocka_unit_test_setup_teardown(torture_rekey_default,
658 session_setup,
659 session_teardown),
660 cmocka_unit_test_setup_teardown(torture_rekey_time,
661 session_setup,
662 session_teardown),
663 #ifdef WITH_SFTP
664 cmocka_unit_test_setup_teardown(torture_rekey_recv,
665 session_setup_sftp_client,
666 session_teardown),
667 #endif /* WITH_SFTP */
668 cmocka_unit_test_setup_teardown(torture_rekey_send,
669 session_setup,
670 session_teardown),
671 cmocka_unit_test_setup_teardown(torture_rekey_different_kex,
672 session_setup,
673 session_teardown),
674 /* Note, that this modifies the sshd_config */
675 cmocka_unit_test_setup_teardown(torture_rekey_server_send,
676 session_setup,
677 session_teardown),
678 #ifdef WITH_SFTP
679 cmocka_unit_test_setup_teardown(torture_rekey_server_recv,
680 session_setup_sftp_server,
681 session_teardown),
682 #endif /* WITH_SFTP */
683 cmocka_unit_test_setup_teardown(torture_rekey_server_different_kex,
684 session_setup,
685 session_teardown),
686 /* TODO verify the two rekey are possible and the states are not broken after rekey */
687 };
688
689 ssh_init();
690
691 torture_filter_tests(tests);
692 rc = cmocka_run_group_tests(tests, sshd_setup, sshd_teardown);
693
694 ssh_finalize();
695
696 return rc;
697 }
698