1 /*
2 * torture.c - torture library for testing libssh
3 *
4 * This file is part of the SSH Library
5 *
6 * Copyright (c) 2008-2009 by Andreas Schneider <asn@cryptomilk.org>
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 #include "tests_config.h"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <signal.h>
32
33 #ifndef _WIN32
34 # include <dirent.h>
35 # include <errno.h>
36 # include <sys/socket.h>
37 #endif
38
39 #ifdef HAVE_UNISTD_H
40 #include <unistd.h>
41 #elif (defined _WIN32) || (defined _WIN64)
42 #include <direct.h>
43 #include <io.h>
44 #define read _read
45 #define open _open
46 #define write _write
47 #define close _close
48 #define chdir _chdir
49 #endif
50
51 #include "torture.h"
52 #include "torture_key.h"
53 #include "libssh/misc.h"
54
55 #define TORTURE_SSHD_SRV_IPV4 "127.0.0.10"
56 /* socket wrapper IPv6 prefix fd00::5357:5fxx */
57 #define TORTURE_SSHD_SRV_IPV6 "fd00::5357:5f0a"
58 #define TORTURE_SSHD_SRV_PORT 22
59
60 #define TORTURE_SOCKET_DIR "/tmp/test_socket_wrapper_XXXXXX"
61 #define TORTURE_SSHD_PIDFILE "sshd/sshd.pid"
62 #define TORTURE_SSHD_CONFIG "sshd/sshd_config"
63 #define TORTURE_PCAP_FILE "socket_trace.pcap"
64
65 #ifndef PATH_MAX
66 # define PATH_MAX 4096
67 #endif
68
69 static const char torture_rsa_certauth_pub[]=
70 "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCnA2n5vHzZbs/GvRkGloJNV1CXHI"
71 "S5Xnrm05HusUJSWyPq3I1iCMHdYA7oezHa9GCFYbIenaYPy+G6USQRjYQz8SvAZo06"
72 "SFNeJSsa1kAIqxzdPT9kBrRrYK39PZQPsYVfRPqZBdmc+jwrfz97IFEJyXMI47FoTG"
73 "kgEq7eu3z2px/tdIZ34I5Hr5DDBxicZi4jluyRUJHfSPoBxyhF7OkPX4bYkrc691je"
74 "IQDxubl650WYLHgFfad0xTzBIFE6XUb55Dp5AgRdevSoso1Pe0IKFxxMVpP664LCbY"
75 "K06Lv6kcotfFlpvUtR1yx8jToGcSoq5sSzTwvXSHCQQ9ZA1hvF "
76 "torture_certauth_key";
77
78 static int verbosity = 0;
79 static const char *pattern = NULL;
80
81 #ifndef _WIN32
82
_torture_auth_kbdint(ssh_session session,const char * password)83 static int _torture_auth_kbdint(ssh_session session,
84 const char *password) {
85 const char *prompt;
86 char echo;
87 int err;
88
89 if (session == NULL || password == NULL) {
90 return SSH_AUTH_ERROR;
91 }
92
93 err = ssh_userauth_kbdint(session, NULL, NULL);
94 if (err == SSH_AUTH_ERROR) {
95 return err;
96 }
97
98 if (ssh_userauth_kbdint_getnprompts(session) != 1) {
99 return SSH_AUTH_ERROR;
100 }
101
102 prompt = ssh_userauth_kbdint_getprompt(session, 0, &echo);
103 if (prompt == NULL) {
104 return SSH_AUTH_ERROR;
105 }
106
107 if (ssh_userauth_kbdint_setanswer(session, 0, password) < 0) {
108 return SSH_AUTH_ERROR;
109 }
110 err = ssh_userauth_kbdint(session, NULL, NULL);
111 if (err == SSH_AUTH_INFO) {
112 if (ssh_userauth_kbdint_getnprompts(session) != 0) {
113 return SSH_AUTH_ERROR;
114 }
115 err = ssh_userauth_kbdint(session, NULL, NULL);
116 }
117
118 return err;
119 }
120
torture_rmdirs(const char * path)121 int torture_rmdirs(const char *path) {
122 DIR *d;
123 struct dirent *dp;
124 struct stat sb;
125 char *fname;
126
127 if ((d = opendir(path)) != NULL) {
128 while(stat(path, &sb) == 0) {
129 /* if we can remove the directory we're done */
130 if (rmdir(path) == 0) {
131 break;
132 }
133 switch (errno) {
134 case ENOTEMPTY:
135 case EEXIST:
136 case EBADF:
137 break; /* continue */
138 default:
139 closedir(d);
140 return 0;
141 }
142
143 while ((dp = readdir(d)) != NULL) {
144 size_t len;
145 /* skip '.' and '..' */
146 if (dp->d_name[0] == '.' &&
147 (dp->d_name[1] == '\0' ||
148 (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) {
149 continue;
150 }
151
152 len = strlen(path) + strlen(dp->d_name) + 2;
153 fname = malloc(len);
154 if (fname == NULL) {
155 closedir(d);
156 return -1;
157 }
158 snprintf(fname, len, "%s/%s", path, dp->d_name);
159
160 /* stat the file */
161 if (lstat(fname, &sb) != -1) {
162 if (S_ISDIR(sb.st_mode) && !S_ISLNK(sb.st_mode)) {
163 if (rmdir(fname) < 0) { /* can't be deleted */
164 if (errno == EACCES) {
165 closedir(d);
166 SAFE_FREE(fname);
167 return -1;
168 }
169 torture_rmdirs(fname);
170 }
171 } else {
172 unlink(fname);
173 }
174 } /* lstat */
175 SAFE_FREE(fname);
176 } /* readdir */
177
178 rewinddir(d);
179 }
180 } else {
181 return -1;
182 }
183
184 closedir(d);
185 return 0;
186 }
187
torture_isdir(const char * path)188 int torture_isdir(const char *path) {
189 struct stat sb;
190
191 if (lstat (path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
192 return 1;
193 }
194
195 return 0;
196 }
197
198 static pid_t
torture_read_pidfile(const char * pidfile)199 torture_read_pidfile(const char *pidfile)
200 {
201 char buf[8] = {0};
202 long int tmp;
203 pid_t ret;
204 ssize_t rc;
205 int fd;
206
207 fd = open(pidfile, O_RDONLY);
208 if (fd < 0) {
209 return -1;
210 }
211
212 rc = read(fd, buf, sizeof(buf));
213 close(fd);
214 if (rc <= 0) {
215 return -1;
216 }
217
218 buf[sizeof(buf) - 1] = '\0';
219
220 tmp = strtol(buf, NULL, 10);
221 if (tmp == 0 || errno == ERANGE) {
222 return -1;
223 }
224 ret = (pid_t)tmp;
225 /* Check if we are out of pid_t range on this system */
226 if ((long)ret != tmp) {
227 return -1;
228 }
229
230 return ret;
231 }
232
torture_terminate_process(const char * pidfile)233 int torture_terminate_process(const char *pidfile)
234 {
235 ssize_t rc;
236 pid_t pid;
237 int is_running = 1;
238 int count;
239
240 /* read the pidfile */
241 pid = torture_read_pidfile(pidfile);
242 assert_int_not_equal(pid, -1);
243
244 for (count = 0; count < 10; count++) {
245 /* Make sure the daemon goes away! */
246 kill(pid, SIGTERM);
247
248 /* 10 ms */
249 usleep(10 * 1000);
250
251 rc = kill(pid, 0);
252 if (rc != 0) {
253 is_running = 0;
254 break;
255 }
256 }
257
258 if (is_running) {
259 fprintf(stderr,
260 "WARNING: The process with pid %u is still running!\n", pid);
261 }
262
263 return 0;
264 }
265
torture_ssh_session(struct torture_state * s,const char * host,const unsigned int * port,const char * user,const char * password)266 ssh_session torture_ssh_session(struct torture_state *s,
267 const char *host,
268 const unsigned int *port,
269 const char *user,
270 const char *password) {
271 ssh_session session;
272 int method;
273 int rc;
274
275 bool process_config = false;
276
277 if (host == NULL) {
278 return NULL;
279 }
280
281 session = ssh_new();
282 if (session == NULL) {
283 return NULL;
284 }
285
286 #ifdef WITH_PCAP
287 if (s != NULL && s->plain_pcap != NULL) {
288 ssh_set_pcap_file(session, s->plain_pcap);
289 }
290 #endif /* WITH_PCAP */
291
292 if (ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity) < 0) {
293 goto failed;
294 }
295
296 if (ssh_options_set(session, SSH_OPTIONS_HOST, host) < 0) {
297 goto failed;
298 }
299
300 if (port != NULL) {
301 if (ssh_options_set(session, SSH_OPTIONS_PORT, port) < 0) {
302 goto failed;
303 }
304 }
305
306 if (user != NULL) {
307 if (ssh_options_set(session, SSH_OPTIONS_USER, user) < 0) {
308 goto failed;
309 }
310 }
311
312 if (ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG,
313 &process_config) < 0) {
314 goto failed;
315 }
316
317 if (ssh_connect(session)) {
318 goto failed;
319 }
320
321 /* We are in testing mode, so consinder the hostkey as verified ;) */
322
323 /* This request should return a SSH_REQUEST_DENIED error */
324 rc = ssh_userauth_none(session, NULL);
325 if (rc == SSH_ERROR) {
326 goto failed;
327 }
328 method = ssh_userauth_list(session, NULL);
329 if (method == 0) {
330 goto failed;
331 }
332
333 if (password != NULL) {
334 if (method & SSH_AUTH_METHOD_PASSWORD) {
335 rc = ssh_userauth_password(session, NULL, password);
336 } else if (method & SSH_AUTH_METHOD_INTERACTIVE) {
337 rc = _torture_auth_kbdint(session, password);
338 }
339 } else {
340 rc = ssh_userauth_publickey_auto(session, NULL, NULL);
341 if (rc == SSH_AUTH_ERROR) {
342 goto failed;
343 }
344 }
345 if (rc != SSH_AUTH_SUCCESS) {
346 goto failed;
347 }
348
349 return session;
350 failed:
351 if (ssh_is_connected(session)) {
352 ssh_disconnect(session);
353 }
354 ssh_free(session);
355
356 return NULL;
357 }
358
359 #ifdef WITH_SERVER
360
torture_ssh_bind(const char * addr,const unsigned int port,enum ssh_keytypes_e key_type,const char * private_key_file)361 ssh_bind torture_ssh_bind(const char *addr,
362 const unsigned int port,
363 enum ssh_keytypes_e key_type,
364 const char *private_key_file) {
365 int rc;
366 ssh_bind sshbind = NULL;
367 enum ssh_bind_options_e opts = -1;
368
369 sshbind = ssh_bind_new();
370 if (sshbind == NULL) {
371 goto out;
372 }
373
374 rc = ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, addr);
375 if (rc != 0) {
376 goto out_free;
377 }
378
379 rc = ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT, &port);
380 if (rc != 0) {
381 goto out_free;
382 }
383
384 switch (key_type) {
385 #ifdef HAVE_DSA
386 case SSH_KEYTYPE_DSS:
387 opts = SSH_BIND_OPTIONS_DSAKEY;
388 break;
389 #endif /* HAVE_DSA */
390 case SSH_KEYTYPE_RSA:
391 opts = SSH_BIND_OPTIONS_RSAKEY;
392 break;
393 case SSH_KEYTYPE_ECDSA_P256:
394 case SSH_KEYTYPE_ECDSA_P384:
395 case SSH_KEYTYPE_ECDSA_P521:
396 opts = SSH_BIND_OPTIONS_ECDSAKEY;
397 break;
398 default:
399 goto out_free;
400 }
401
402 rc = ssh_bind_options_set(sshbind, opts, private_key_file);
403 if (rc != 0) {
404 goto out_free;
405 }
406
407 rc = ssh_bind_listen(sshbind);
408 if (rc != SSH_OK) {
409 goto out_free;
410 }
411
412 goto out;
413 out_free:
414 ssh_bind_free(sshbind);
415 sshbind = NULL;
416 out:
417 return sshbind;
418 }
419
420 #endif /* WITH_SERVER */
421
422 #ifdef WITH_SFTP
423
torture_sftp_session_channel(ssh_session session,ssh_channel channel)424 struct torture_sftp *torture_sftp_session_channel(ssh_session session, ssh_channel channel)
425 {
426 struct torture_sftp *t;
427 char template[] = "/tmp/ssh_torture_XXXXXX";
428 char *p;
429 int rc;
430
431 if (session == NULL) {
432 return NULL;
433 }
434
435 t = malloc(sizeof(struct torture_sftp));
436 if (t == NULL) {
437 return NULL;
438 }
439
440 t->ssh = session;
441 if (channel == NULL) {
442 t->sftp = sftp_new(session);
443 if (t->sftp == NULL) {
444 goto failed;
445 }
446 } else {
447 t->sftp = sftp_new_channel(session, channel);
448 if (t->sftp == NULL) {
449 goto failed;
450 }
451
452 rc = ssh_channel_open_session(channel);
453 if (rc != SSH_OK) {
454 goto failed;
455 }
456
457 rc = ssh_channel_request_sftp(channel);
458 if (rc != SSH_OK) {
459 goto failed;
460 }
461 }
462
463 rc = sftp_init(t->sftp);
464 if (rc < 0) {
465 goto failed;
466 }
467
468 p = mkdtemp(template);
469 if (p == NULL) {
470 goto failed;
471 }
472 /* useful if TESTUSER is not the local user */
473 chmod(template,0777);
474 t->testdir = strdup(p);
475 if (t->testdir == NULL) {
476 goto failed;
477 }
478
479 return t;
480 failed:
481 if (t->sftp != NULL) {
482 sftp_free(t->sftp);
483 }
484 ssh_disconnect(t->ssh);
485 ssh_free(t->ssh);
486 free(t);
487
488 return NULL;
489 }
490
torture_sftp_session(ssh_session session)491 struct torture_sftp *torture_sftp_session(ssh_session session)
492 {
493 return torture_sftp_session_channel(session, NULL);
494 }
495
torture_sftp_close(struct torture_sftp * t)496 void torture_sftp_close(struct torture_sftp *t) {
497 if (t == NULL) {
498 return;
499 }
500
501 if (t->sftp != NULL) {
502 sftp_free(t->sftp);
503 }
504
505 free(t->testdir);
506 free(t);
507 }
508 #endif /* WITH_SFTP */
509
torture_server_port(void)510 int torture_server_port(void)
511 {
512 char *env = getenv("TORTURE_SERVER_PORT");
513
514 if (env != NULL && env[0] != '\0' && strlen(env) < 6) {
515 int port = atoi(env);
516
517 if (port > 0 && port < 65536) {
518 return port;
519 }
520 }
521
522 return TORTURE_SSHD_SRV_PORT;
523 }
524
torture_server_address(int family)525 const char *torture_server_address(int family)
526 {
527 switch (family) {
528 case AF_INET: {
529 const char *ip4 = getenv("TORTURE_SERVER_ADDRESS_IPV4");
530
531 if (ip4 != NULL && ip4[0] != '\0') {
532 return ip4;
533 }
534
535 return TORTURE_SSHD_SRV_IPV4;
536 }
537 case AF_INET6: {
538 const char *ip6 = getenv("TORTURE_SERVER_ADDRESS_IPV6");
539
540 if (ip6 != NULL && ip6[0] != '\0') {
541 return ip6;
542 }
543
544 return TORTURE_SSHD_SRV_IPV6;
545 }
546 default:
547 return NULL;
548 }
549
550 return NULL;
551 }
552
torture_setup_socket_dir(void ** state)553 void torture_setup_socket_dir(void **state)
554 {
555 struct torture_state *s;
556 const char *p;
557 size_t len;
558 char *env = NULL;
559 int rc;
560
561 s = calloc(1, sizeof(struct torture_state));
562 assert_non_null(s);
563
564 #ifdef WITH_PCAP
565 env = getenv("TORTURE_PLAIN_PCAP_FILE");
566 if (env != NULL && env[0] != '\0') {
567 s->plain_pcap = ssh_pcap_file_new();
568 assert_non_null(s->plain_pcap);
569
570 rc = ssh_pcap_file_open(s->plain_pcap, env);
571 assert_int_equal(rc, SSH_OK);
572 }
573 #endif /* WITH_PCAP */
574
575 s->socket_dir = torture_make_temp_dir(TORTURE_SOCKET_DIR);
576 assert_non_null(s->socket_dir);
577
578 p = s->socket_dir;
579
580 /* pcap file */
581 len = strlen(p) + 1 + strlen(TORTURE_PCAP_FILE) + 1;
582
583 s->pcap_file = malloc(len);
584 assert_non_null(s->pcap_file);
585
586 snprintf(s->pcap_file, len, "%s/%s", p, TORTURE_PCAP_FILE);
587
588 /* pid file */
589 len = strlen(p) + 1 + strlen(TORTURE_SSHD_PIDFILE) + 1;
590
591 s->srv_pidfile = malloc(len);
592 assert_non_null(s->srv_pidfile);
593
594 snprintf(s->srv_pidfile, len, "%s/%s", p, TORTURE_SSHD_PIDFILE);
595
596 /* config file */
597 len = strlen(p) + 1 + strlen(TORTURE_SSHD_CONFIG) + 1;
598
599 s->srv_config = malloc(len);
600 assert_non_null(s->srv_config);
601
602 snprintf(s->srv_config, len, "%s/%s", p, TORTURE_SSHD_CONFIG);
603
604 setenv("SOCKET_WRAPPER_DIR", p, 1);
605 setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "170", 1);
606 env = getenv("TORTURE_GENERATE_PCAP");
607 if (env != NULL && env[0] == '1') {
608 setenv("SOCKET_WRAPPER_PCAP_FILE", s->pcap_file, 1);
609 }
610
611 *state = s;
612 }
613
torture_setup_create_sshd_config(void ** state,bool pam)614 static void torture_setup_create_sshd_config(void **state, bool pam)
615 {
616 struct torture_state *s = *state;
617 char ed25519_hostkey[1024] = {0};
618 #ifdef HAVE_DSA
619 char dsa_hostkey[1024];
620 #endif /* HAVE_DSA */
621 char rsa_hostkey[1024];
622 char ecdsa_hostkey[1024];
623 char trusted_ca_pubkey[1024];
624 char sshd_config[4096];
625 char sshd_path[1024];
626 const char *additional_config = NULL;
627 struct stat sb;
628 const char *sftp_server_locations[] = {
629 "/usr/lib/ssh/sftp-server",
630 "/usr/libexec/ssh/sftp-server", /* Tumbleweed 20200829 */
631 "/usr/libexec/sftp-server",
632 "/usr/libexec/openssh/sftp-server",
633 "/usr/lib/openssh/sftp-server", /* Debian */
634 };
635 const char config_string[]=
636 "Port 22\n"
637 "ListenAddress 127.0.0.10\n"
638 "%s %s\n" /* ed25519 HostKey */
639 #ifdef HAVE_DSA
640 "%s %s\n" /* DSA HostKey */
641 #endif /* HAVE_DSA */
642 "%s %s\n" /* RSA HostKey */
643 "%s %s\n" /* ECDSA HostKey */
644 "\n"
645 "TrustedUserCAKeys %s\n"
646 "\n"
647 "LogLevel DEBUG3\n"
648 "Subsystem sftp %s -l DEBUG2\n"
649 "\n"
650 "PasswordAuthentication yes\n"
651 "PubkeyAuthentication yes\n"
652 "\n"
653 "StrictModes no\n"
654 "\n"
655 "%s\n" /* Here comes UsePam */
656 "%s" /* The space for test-specific options */
657 "\n"
658 /* add all supported algorithms */
659 "HostKeyAlgorithms " OPENSSH_KEYS "\n"
660 #if OPENSSH_VERSION_MAJOR == 8 && OPENSSH_VERSION_MINOR >= 2
661 "CASignatureAlgorithms " OPENSSH_KEYS "\n"
662 #endif
663 "Ciphers " OPENSSH_CIPHERS "\n"
664 "KexAlgorithms " OPENSSH_KEX "\n"
665 "MACs " OPENSSH_MACS "\n"
666 "\n"
667 "AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES\n"
668 "AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT\n"
669 "AcceptEnv LC_IDENTIFICATION LC_ALL LC_LIBSSH\n"
670 "\n"
671 "PidFile %s\n";
672 /* FIPS config */
673 const char fips_config_string[]=
674 "Port 22\n"
675 "ListenAddress 127.0.0.10\n"
676 "%s %s\n" /* RSA HostKey */
677 "%s %s\n" /* ECDSA HostKey */
678 "\n"
679 "TrustedUserCAKeys %s\n" /* Trusted CA */
680 "\n"
681 "LogLevel DEBUG3\n"
682 "Subsystem sftp %s -l DEBUG2\n" /* SFTP server */
683 "\n"
684 "PasswordAuthentication yes\n"
685 "PubkeyAuthentication yes\n"
686 "\n"
687 "StrictModes no\n"
688 "\n"
689 "%s\n" /* Here comes UsePam */
690 "%s" /* The space for test-specific options */
691 "\n"
692 "Ciphers "
693 "aes256-gcm@openssh.com,aes256-ctr,aes256-cbc,"
694 "aes128-gcm@openssh.com,aes128-ctr,aes128-cbc"
695 "\n"
696 "MACs "
697 "hmac-sha2-256-etm@openssh.com,hmac-sha1-etm@openssh.com,"
698 "hmac-sha2-512-etm@openssh.com,hmac-sha2-256,"
699 "hmac-sha1,hmac-sha2-512"
700 "\n"
701 "GSSAPIKeyExchange no\n"
702 "KexAlgorithms "
703 "ecdh-sha2-nistp256,ecdh-sha2-nistp384,"
704 "ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,"
705 "diffie-hellman-group14-sha256,diffie-hellman-group16-sha512,"
706 "diffie-hellman-group18-sha512"
707 "\n"
708 "PubkeyAcceptedKeyTypes "
709 "rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com,"
710 "ecdsa-sha2-nistp256,ecdsa-sha2-nistp256-cert-v01@openssh.com,"
711 "ecdsa-sha2-nistp384,ecdsa-sha2-nistp384-cert-v01@openssh.com,"
712 "rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com,"
713 "ecdsa-sha2-nistp521,ecdsa-sha2-nistp521-cert-v01@openssh.com"
714 "\n"
715 "AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES\n"
716 "AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT\n"
717 "AcceptEnv LC_IDENTIFICATION LC_ALL LC_LIBSSH\n"
718 "\n"
719 "PidFile %s\n"; /* PID file */
720 const char usepam_yes[] =
721 "UsePAM yes\n"
722 "KbdInteractiveAuthentication yes\n";
723 const char usepam_no[] =
724 "UsePAM no\n"
725 "KbdInteractiveAuthentication no\n";
726 size_t sftp_sl_size = ARRAY_SIZE(sftp_server_locations);
727 const char *sftp_server, *usepam;
728 size_t i;
729 bool written = false;
730 int rc;
731
732 s->srv_pam = pam;
733 if (pam) {
734 usepam = usepam_yes;
735 } else {
736 usepam = usepam_no;
737 }
738
739 assert_non_null(s->socket_dir);
740
741 snprintf(sshd_path,
742 sizeof(sshd_path),
743 "%s/sshd",
744 s->socket_dir);
745
746 rc = lstat(sshd_path, &sb);
747 if (rc == 0 ) { /* The directory is already in place */
748 written = true;
749 }
750
751 if (!written) {
752 rc = mkdir(sshd_path, 0755);
753 assert_return_code(rc, errno);
754 }
755
756 snprintf(ed25519_hostkey,
757 sizeof(ed25519_hostkey),
758 "%s/sshd/ssh_host_ed25519_key",
759 s->socket_dir);
760
761 #ifdef HAVE_DSA
762 snprintf(dsa_hostkey,
763 sizeof(dsa_hostkey),
764 "%s/sshd/ssh_host_dsa_key",
765 s->socket_dir);
766 #endif /* HAVE_DSA */
767
768 snprintf(rsa_hostkey,
769 sizeof(rsa_hostkey),
770 "%s/sshd/ssh_host_rsa_key",
771 s->socket_dir);
772
773 snprintf(ecdsa_hostkey,
774 sizeof(ecdsa_hostkey),
775 "%s/sshd/ssh_host_ecdsa_key",
776 s->socket_dir);
777
778 snprintf(trusted_ca_pubkey,
779 sizeof(trusted_ca_pubkey),
780 "%s/sshd/user_ca.pub",
781 s->socket_dir);
782
783 if (!written) {
784 torture_write_file(ed25519_hostkey,
785 torture_get_openssh_testkey(SSH_KEYTYPE_ED25519, 0));
786 #ifdef HAVE_DSA
787 torture_write_file(dsa_hostkey,
788 torture_get_testkey(SSH_KEYTYPE_DSS, 0));
789 #endif /* HAVE_DSA */
790 torture_write_file(rsa_hostkey,
791 torture_get_testkey(SSH_KEYTYPE_RSA, 0));
792 torture_write_file(ecdsa_hostkey,
793 torture_get_testkey(SSH_KEYTYPE_ECDSA_P521, 0));
794 torture_write_file(trusted_ca_pubkey, torture_rsa_certauth_pub);
795 }
796
797 sftp_server = getenv("TORTURE_SFTP_SERVER");
798 if (sftp_server == NULL) {
799 for (i = 0; i < sftp_sl_size; i++) {
800 sftp_server = sftp_server_locations[i];
801 rc = lstat(sftp_server, &sb);
802 if (rc == 0) {
803 break;
804 }
805 }
806 }
807 assert_non_null(sftp_server);
808
809 additional_config = (s->srv_additional_config != NULL ?
810 s->srv_additional_config : "");
811
812 if (ssh_fips_mode()) {
813 snprintf(sshd_config, sizeof(sshd_config),
814 fips_config_string,
815 "HostKey", rsa_hostkey,
816 "HostKey", ecdsa_hostkey,
817 trusted_ca_pubkey,
818 sftp_server,
819 usepam,
820 additional_config,
821 s->srv_pidfile);
822 } else {
823 snprintf(sshd_config, sizeof(sshd_config),
824 config_string,
825 "HostKey", ed25519_hostkey,
826 #ifdef HAVE_DSA
827 "HostKey", dsa_hostkey,
828 #endif /* HAVE_DSA */
829 "HostKey", rsa_hostkey,
830 "HostKey", ecdsa_hostkey,
831 trusted_ca_pubkey,
832 sftp_server,
833 usepam,
834 additional_config,
835 s->srv_pidfile);
836 }
837
838 torture_write_file(s->srv_config, sshd_config);
839 }
840
torture_wait_for_daemon(unsigned int seconds)841 static int torture_wait_for_daemon(unsigned int seconds)
842 {
843 struct ssh_timestamp start;
844 int rc;
845
846 ssh_timestamp_init(&start);
847
848 while (!ssh_timeout_elapsed(&start, seconds * 1000)) {
849 rc = system(SSH_PING_EXECUTABLE " " TORTURE_SSH_SERVER);
850 if (rc == 0) {
851 return 0;
852 }
853 /* Wait 200 ms before retrying */
854 usleep(200 * 1000);
855 }
856 return 1;
857 }
858
torture_setup_sshd_server(void ** state,bool pam)859 void torture_setup_sshd_server(void **state, bool pam)
860 {
861 struct torture_state *s;
862 char sshd_start_cmd[1024];
863 int rc;
864
865 torture_setup_socket_dir(state);
866 torture_setup_create_sshd_config(state, pam);
867
868 /* Set the default interface for the server */
869 setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "10", 1);
870 setenv("PAM_WRAPPER", "1", 1);
871
872 s = *state;
873
874 snprintf(sshd_start_cmd, sizeof(sshd_start_cmd),
875 SSHD_EXECUTABLE " -r -f %s -E %s/sshd/daemon.log 2> %s/sshd/cwrap.log",
876 s->srv_config, s->socket_dir, s->socket_dir);
877
878 rc = system(sshd_start_cmd);
879 assert_return_code(rc, errno);
880
881 setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "21", 1);
882 unsetenv("PAM_WRAPPER");
883
884 /* Wait until the sshd is ready to accept connections */
885 rc = torture_wait_for_daemon(5);
886 assert_int_equal(rc, 0);
887 }
888
torture_teardown_socket_dir(void ** state)889 void torture_teardown_socket_dir(void **state)
890 {
891 struct torture_state *s = *state;
892 char *env = getenv("TORTURE_SKIP_CLEANUP");
893 int rc;
894
895 if (env != NULL && env[0] == '1') {
896 fprintf(stderr, "[ TORTURE ] >>> Skipping cleanup of %s\n", s->socket_dir);
897 } else {
898 rc = torture_rmdirs(s->socket_dir);
899 if (rc < 0) {
900 fprintf(stderr,
901 "torture_rmdirs(%s) failed: %s",
902 s->socket_dir,
903 strerror(errno));
904 }
905 }
906 #ifdef WITH_PCAP
907 if (s->plain_pcap != NULL) {
908 ssh_pcap_file_free(s->plain_pcap);
909 }
910 s->plain_pcap = NULL;
911 #endif /* WITH_PCAP */
912
913 free(s->srv_config);
914 free(s->socket_dir);
915 free(s->pcap_file);
916 free(s->srv_pidfile);
917 free(s->srv_additional_config);
918 free(s);
919 }
920
921 static int
torture_reload_sshd_server(void ** state)922 torture_reload_sshd_server(void **state)
923 {
924 struct torture_state *s = *state;
925 pid_t pid;
926 int rc;
927
928 /* read the pidfile */
929 pid = torture_read_pidfile(s->srv_pidfile);
930 assert_int_not_equal(pid, -1);
931
932 kill(pid, SIGHUP);
933
934 /* 10 ms */
935 usleep(10 * 1000);
936
937 rc = kill(pid, 0);
938 if (rc != 0) {
939 fprintf(stderr,
940 "ERROR: SSHD process %u died during reload!\n", pid);
941 return SSH_ERROR;
942 }
943
944 /* Wait until the sshd is ready to accept connections */
945 rc = torture_wait_for_daemon(5);
946 assert_int_equal(rc, 0);
947 return SSH_OK;
948 }
949
950 /* @brief: Updates SSHD server configuration with more options and
951 * reloads the server to apply them.
952 * Note, that this still uses the default configuration options specified
953 * in this file and overwrites options previously specified by this function.
954 */
955 int
torture_update_sshd_config(void ** state,const char * config)956 torture_update_sshd_config(void **state, const char *config)
957 {
958 struct torture_state *s = *state;
959 int rc;
960
961 /* Store the configuration in internal structure */
962 SAFE_FREE(s->srv_additional_config);
963 s->srv_additional_config = strdup(config);
964 assert_non_null(s->srv_additional_config);
965
966 /* Rewrite the configuration file */
967 torture_setup_create_sshd_config(state, s->srv_pam);
968
969 /* Reload the server */
970 rc = torture_reload_sshd_server(state);
971 assert_int_equal(rc, SSH_OK);
972
973 return SSH_OK;
974 }
975
976
torture_teardown_sshd_server(void ** state)977 void torture_teardown_sshd_server(void **state)
978 {
979 struct torture_state *s = *state;
980 int rc;
981
982 rc = torture_terminate_process(s->srv_pidfile);
983 if (rc != 0) {
984 fprintf(stderr, "XXXXXX Failed to terminate sshd\n");
985 }
986
987 torture_teardown_socket_dir(state);
988 }
989
torture_make_temp_dir(const char * template)990 char *torture_make_temp_dir(const char *template)
991 {
992 char *new_dir = NULL;
993 char *template_copy = NULL;
994
995 if (template == NULL) {
996 goto end;
997 }
998
999 template_copy = strdup(template);
1000 if (template_copy == NULL) {
1001 goto end;
1002 }
1003
1004 new_dir = mkdtemp(template_copy);
1005 if (new_dir == NULL) {
1006 SAFE_FREE(template_copy);
1007 }
1008
1009 end:
1010 return template_copy;
1011 }
1012
torture_create_temp_file(const char * template)1013 char *torture_create_temp_file(const char *template)
1014 {
1015 char *new_file = NULL;
1016 FILE *fp = NULL;
1017 mode_t mask;
1018 int fd;
1019
1020 new_file = strdup(template);
1021 if (new_file == NULL) {
1022 goto end;
1023 }
1024
1025 mask = umask(S_IRWXO | S_IRWXG);
1026 fd = mkstemp(new_file);
1027 umask(mask);
1028 if (fd == -1) {
1029 goto end;
1030 }
1031
1032 fp = fdopen(fd, "w");
1033 if (fp == NULL) {
1034 SAFE_FREE(new_file);
1035 close(fd);
1036 goto end;
1037 }
1038
1039 fclose(fp);
1040
1041 end:
1042 return new_file;
1043 }
1044
torture_get_current_working_dir(void)1045 char *torture_get_current_working_dir(void)
1046 {
1047
1048 char *cwd = NULL;
1049 char *result = NULL;
1050
1051 cwd = (char *)malloc(PATH_MAX + 1);
1052 if (cwd == NULL) {
1053 goto end;
1054 }
1055
1056 result = getcwd(cwd, PATH_MAX);
1057
1058 if (result == NULL) {
1059 SAFE_FREE(cwd);
1060 goto end;
1061 }
1062
1063 end:
1064 return cwd;
1065 }
1066
1067 #else /* _WIN32 */
1068
torture_make_temp_dir(const char * template)1069 char *torture_make_temp_dir(const char *template)
1070 {
1071 DWORD rc = 0;
1072 char tmp_dir_path[MAX_PATH];
1073 char tmp_file_name[MAX_PATH];
1074 char *prefix = NULL;
1075 char *path = NULL;
1076 char *prefix_end = NULL;
1077 char *slash = NULL;
1078
1079 BOOL created;
1080
1081 if (template == NULL) {
1082 goto end;
1083 }
1084
1085 prefix = strdup(template);
1086 if (prefix == NULL) {
1087 goto end;
1088 }
1089
1090 /* Replace slashes with backslashes */
1091 slash = strchr(prefix, '/');
1092 for (; slash != NULL; slash = strchr(prefix, '/')) {
1093 *slash = '\\';
1094 }
1095
1096 prefix_end = strstr(prefix, "XXXXXX");
1097 if (prefix_end != NULL) {
1098 *prefix_end = '\0';
1099 }
1100
1101 rc = GetTempPathA(MAX_PATH, tmp_dir_path);
1102 if ((rc > MAX_PATH) || (rc == 0)) {
1103 goto free_prefix;
1104 }
1105
1106 rc = GetTempFileNameA(tmp_dir_path, TEXT(prefix), 0, tmp_file_name);
1107 if (rc == 0) {
1108 goto free_prefix;
1109 }
1110
1111 path = strdup(tmp_file_name);
1112 if (path == NULL) {
1113 goto free_prefix;
1114 }
1115
1116 /* GetTempFileNameA() creates a temporary file; we need to remove it */
1117 rc = DeleteFileA(path);
1118 if (rc == 0) {
1119 rc = -1;
1120 SAFE_FREE(path);
1121 goto free_prefix;
1122 }
1123
1124 created = CreateDirectoryA(path, NULL);
1125 if (!created) {
1126 SAFE_FREE(path);
1127 }
1128
1129 free_prefix:
1130 SAFE_FREE(prefix);
1131 end:
1132 return path;
1133 }
1134
recursive_rm_dir_content(const char * path)1135 static int recursive_rm_dir_content(const char *path)
1136 {
1137 WIN32_FIND_DATA file_data;
1138 HANDLE file_handle;
1139 DWORD attributes;
1140
1141 DWORD last_error = 0;
1142
1143 char file_path[MAX_PATH];
1144
1145 int rc = 0;
1146 BOOL removed;
1147
1148 strcpy(file_path, path);
1149 strcat(file_path, "\\*");
1150
1151 file_handle = FindFirstFile(file_path, &file_data);
1152
1153 if (file_handle == INVALID_HANDLE_VALUE) {
1154 last_error = GetLastError();
1155
1156 /* Empty directory */
1157 if (last_error == ERROR_FILE_NOT_FOUND) {
1158 rc = 0;
1159 }
1160 else {
1161 /*TODO print error message?*/
1162 rc = last_error;
1163 }
1164 goto end;
1165 }
1166 else {
1167 do {
1168 rc = strcmp(file_data.cFileName, ".");
1169 if (rc == 0) {
1170 continue;
1171 }
1172
1173 rc = strcmp(file_data.cFileName, "..");
1174 if (rc == 0) {
1175 continue;
1176 }
1177
1178 /* Create full file path */
1179 strcpy(file_path, path);
1180 strcat(file_path, "\\");
1181 strcat(file_path, file_data.cFileName);
1182
1183 attributes = GetFileAttributes(file_path);
1184 if (attributes & FILE_ATTRIBUTE_DIRECTORY) {
1185 rc = recursive_rm_dir_content((const char *)file_path);
1186 if (rc != 0) {
1187 goto end;
1188 }
1189
1190 removed = RemoveDirectoryA(file_path);
1191
1192 if (!removed) {
1193 last_error = GetLastError();
1194
1195 /*TODO print error message?*/
1196
1197 rc = last_error;
1198 goto end;
1199 }
1200 }
1201 else {
1202 rc = remove(file_path);
1203 if (rc) {
1204 goto end;
1205 }
1206 }
1207
1208 } while(FindNextFile(file_handle, &file_data));
1209
1210 FindClose(file_handle);
1211 }
1212
1213 end:
1214 return rc;
1215 }
1216
torture_rmdirs(const char * path)1217 int torture_rmdirs(const char *path)
1218 {
1219 int rc = 0;
1220 BOOL removed;
1221
1222 rc = recursive_rm_dir_content(path);
1223 if (rc) {
1224 return rc;
1225 }
1226
1227 removed = RemoveDirectoryA(path);
1228 if (!removed) {
1229 rc = -1;
1230 }
1231
1232 return rc;
1233 }
1234
torture_isdir(const char * path)1235 int torture_isdir(const char *path)
1236 {
1237
1238 DWORD attributes = 0;
1239
1240 attributes = GetFileAttributes(path);
1241 if (attributes & FILE_ATTRIBUTE_DIRECTORY) {
1242 return 1;
1243 }
1244
1245 return 0;
1246 }
1247
torture_create_temp_file(const char * template)1248 char *torture_create_temp_file(const char *template)
1249 {
1250 DWORD rc = 0;
1251 char tmp_dir_path[MAX_PATH];
1252 char tmp_file_name[MAX_PATH];
1253 char *prefix = NULL;
1254 char *path = NULL;
1255 char *prefix_end = NULL;
1256 char *slash = NULL;
1257
1258 if (template == NULL) {
1259 goto end;
1260 }
1261
1262 prefix = strdup(template);
1263 if (prefix == NULL) {
1264 goto end;
1265 }
1266
1267 /* Replace slashes with backslashes */
1268 slash = strchr(prefix, '/');
1269 for (; slash != NULL; slash = strchr(prefix, '/')) {
1270 *slash = '\\';
1271 }
1272
1273 prefix_end = strstr(prefix, "XXXXXX");
1274 if (prefix_end != NULL) {
1275 *prefix_end = '\0';
1276 }
1277
1278 rc = GetTempPathA(MAX_PATH, tmp_dir_path);
1279 if ((rc > MAX_PATH) || (rc == 0)) {
1280 goto free_prefix;
1281 }
1282
1283 /* Remark: this function creates the file */
1284 rc = GetTempFileNameA(tmp_dir_path, TEXT(prefix), 0, tmp_file_name);
1285 if (rc == 0) {
1286 goto free_prefix;
1287 }
1288
1289 path = strdup(tmp_file_name);
1290
1291 free_prefix:
1292 SAFE_FREE(prefix);
1293 end:
1294 return path;
1295 }
1296
torture_get_current_working_dir(void)1297 char *torture_get_current_working_dir(void)
1298 {
1299 char *cwd = NULL;
1300 char *result = NULL;
1301
1302 cwd = (char *)malloc(_MAX_PATH + 1);
1303 if (cwd == NULL) {
1304 goto end;
1305 }
1306
1307 result = _getcwd(cwd, _MAX_PATH);
1308
1309 if (result == NULL) {
1310 SAFE_FREE(cwd);
1311 goto end;
1312 }
1313
1314 end:
1315 return cwd;
1316 }
1317
1318 #endif /* _WIN32 */
1319
torture_change_dir(char * path)1320 int torture_change_dir(char *path)
1321 {
1322 int rc = 0;
1323
1324 if (path == NULL) {
1325 rc = -1;
1326 goto end;
1327 }
1328
1329 rc = chdir(path);
1330
1331 end:
1332 return rc;
1333 }
1334
torture_libssh_verbosity(void)1335 int torture_libssh_verbosity(void){
1336 return verbosity;
1337 }
1338
_torture_filter_tests(struct CMUnitTest * tests,size_t ntests)1339 void _torture_filter_tests(struct CMUnitTest *tests, size_t ntests)
1340 {
1341 (void) tests;
1342 (void) ntests;
1343
1344 return;
1345 }
1346
torture_write_file(const char * filename,const char * data)1347 void torture_write_file(const char *filename, const char *data){
1348 int fd;
1349 int rc;
1350
1351 assert_non_null(filename);
1352 assert_true(filename[0] != '\0');
1353 assert_non_null(data);
1354
1355 fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0600);
1356 assert_true(fd >= 0);
1357
1358 rc = write(fd, data, strlen(data));
1359 assert_int_equal(rc, strlen(data));
1360
1361 close(fd);
1362 }
1363
torture_reset_config(ssh_session session)1364 void torture_reset_config(ssh_session session)
1365 {
1366 memset(session->opts.options_seen, 0, sizeof(session->opts.options_seen));
1367 }
1368
main(int argc,char ** argv)1369 int main(int argc, char **argv) {
1370 struct argument_s arguments;
1371 char *env = getenv("LIBSSH_VERBOSITY");
1372
1373 arguments.verbose=0;
1374 arguments.pattern=NULL;
1375 torture_cmdline_parse(argc, argv, &arguments);
1376 verbosity=arguments.verbose;
1377 pattern=arguments.pattern;
1378
1379 if (verbosity == 0 && env != NULL && env[0] != '\0') {
1380 if (env[0] > '0' && env[0] < '9') {
1381 verbosity = atoi(env);
1382 }
1383 }
1384
1385 #if defined HAVE_CMOCKA_SET_TEST_FILTER
1386 cmocka_set_test_filter(pattern);
1387 #endif
1388
1389 return torture_run_tests();
1390 }
1391