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