1 /*
2  * This file is part of the SSH Library
3  *
4  * Copyright (c) 2018 by Red Hat, Inc.
5  *
6  * Author: Anderson Toshiyuki Sasaki <ansasaki@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 <sys/types.h>
29 #include <sys/stat.h>
30 #include <errno.h>
31 #include <pwd.h>
32 
33 #include "torture.h"
34 #include "torture_key.h"
35 #include "libssh/libssh.h"
36 #include "libssh/priv.h"
37 #include "libssh/session.h"
38 
39 #include "test_server.h"
40 #include "default_cb.h"
41 
42 #define TORTURE_KNOWN_HOSTS_FILE "libssh_torture_knownhosts"
43 
44 const char template[] = "temp_dir_XXXXXX";
45 
46 struct test_server_st {
47     struct torture_state *state;
48     struct server_state_st *ss;
49     char *cwd;
50     char *temp_dir;
51 };
52 
53 static int setup_default_server(void **state)
54 {
55     struct torture_state *s;
56     struct server_state_st *ss;
57     struct test_server_st *tss;
58 #ifdef HAVE_DSA
59     char dsa_hostkey[1024];
60 #endif /* HAVE_DSA */
61 
62     char ed25519_hostkey[1024] = {0};
63     char rsa_hostkey[1024];
64     char ecdsa_hostkey[1024];
65     //char trusted_ca_pubkey[1024];
66 
67     char sshd_path[1024];
68     struct stat sb;
69 
70     const char *sftp_server_locations[] = {
71         "/usr/lib/ssh/sftp-server",
72         "/usr/libexec/sftp-server",
73         "/usr/libexec/openssh/sftp-server",
74         "/usr/lib/openssh/sftp-server",     /* Debian */
75     };
76 
77     size_t sftp_sl_size = ARRAY_SIZE(sftp_server_locations);
78     const char *sftp_server;
79 
80     size_t i;
81     int rc;
82 
83     char pid_str[1024];
84 
85     pid_t pid;
86 
87     assert_non_null(state);
88 
89     tss = (struct test_server_st*)calloc(1, sizeof(struct test_server_st));
90     assert_non_null(tss);
91 
92     torture_setup_socket_dir((void **)&s);
93     assert_non_null(s->socket_dir);
94 
95     /* Set the default interface for the server */
96     setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "10", 1);
97     setenv("PAM_WRAPPER", "1", 1);
98 
99     snprintf(sshd_path,
100              sizeof(sshd_path),
101              "%s/sshd",
102              s->socket_dir);
103 
104     rc = mkdir(sshd_path, 0755);
105     assert_return_code(rc, errno);
106 
107     snprintf(ed25519_hostkey,
108              sizeof(ed25519_hostkey),
109              "%s/sshd/ssh_host_ed25519_key",
110              s->socket_dir);
111     torture_write_file(ed25519_hostkey,
112                        torture_get_openssh_testkey(SSH_KEYTYPE_ED25519, 0));
113 
114 #ifdef HAVE_DSA
115     snprintf(dsa_hostkey,
116              sizeof(dsa_hostkey),
117              "%s/sshd/ssh_host_dsa_key",
118              s->socket_dir);
119     torture_write_file(dsa_hostkey, torture_get_testkey(SSH_KEYTYPE_DSS, 0));
120 #endif /* HAVE_DSA */
121 
122     snprintf(rsa_hostkey,
123              sizeof(rsa_hostkey),
124              "%s/sshd/ssh_host_rsa_key",
125              s->socket_dir);
126     torture_write_file(rsa_hostkey, torture_get_testkey(SSH_KEYTYPE_RSA, 0));
127 
128     snprintf(ecdsa_hostkey,
129              sizeof(ecdsa_hostkey),
130              "%s/sshd/ssh_host_ecdsa_key",
131              s->socket_dir);
132     torture_write_file(ecdsa_hostkey,
133                        torture_get_testkey(SSH_KEYTYPE_ECDSA_P521, 0));
134 
135     sftp_server = getenv("TORTURE_SFTP_SERVER");
136     if (sftp_server == NULL) {
137         for (i = 0; i < sftp_sl_size; i++) {
138             sftp_server = sftp_server_locations[i];
139             rc = lstat(sftp_server, &sb);
140             if (rc == 0) {
141                 break;
142             }
143         }
144     }
145     assert_non_null(sftp_server);
146 
147     /* Create default server state */
148     ss = (struct server_state_st *)calloc(1, sizeof(struct server_state_st));
149     assert_non_null(ss);
150 
151     ss->address = strdup("127.0.0.10");
152     assert_non_null(ss->address);
153 
154     ss->port = 22;
155 
156     ss->ecdsa_key = strdup(ecdsa_hostkey);
157     assert_non_null(ss->ecdsa_key);
158 
159 #ifdef HAVE_DSA
160     ss->dsa_key = strdup(dsa_hostkey);
161     assert_non_null(ss->dsa_key);
162 #endif /* HAVE_DSA */
163 
164     ss->ed25519_key = strdup(ed25519_hostkey);
165     assert_non_null(ed25519_hostkey);
166 
167     ss->rsa_key = strdup(rsa_hostkey);
168     assert_non_null(ss->rsa_key);
169 
170     ss->host_key = NULL;
171 
172     /* Use default username and password (set in default_handle_session_cb) */
173     ss->expected_username = NULL;
174     ss->expected_password = NULL;
175 
176     ss->verbosity = torture_libssh_verbosity();
177 
178     ss->auth_methods = SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_PUBLICKEY;
179 
180 #ifdef WITH_PCAP
181     ss->with_pcap = 1;
182     ss->pcap_file = strdup(s->pcap_file);
183     assert_non_null(ss->pcap_file);
184 #endif
185 
186     /* TODO make configurable */
187     ss->max_tries = 3;
188     ss->error = 0;
189 
190     /* Use the default session handling function */
191     ss->handle_session = default_handle_session_cb;
192     assert_non_null(ss->handle_session);
193 
194     /* Do not use global configuration */
195     ss->parse_global_config = false;
196 
197     /* Start the server using the default values */
198     pid = fork_run_server(ss);
199     if (pid < 0) {
200         fail();
201     }
202 
203     snprintf(pid_str, sizeof(pid_str), "%d", pid);
204 
205     torture_write_file(s->srv_pidfile, (const char *)pid_str);
206 
207     setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "21", 1);
208     unsetenv("PAM_WRAPPER");
209 
210     /* Wait until the sshd is ready to accept connections */
211     //rc = torture_wait_for_daemon(5);
212     //assert_int_equal(rc, 0);
213 
214     /* TODO properly wait for the server (use ping approach) */
215     /* Wait 200ms */
216     usleep(200 * 1000);
217 
218     tss->state = s;
219     tss->ss = ss;
220 
221     *state = tss;
222 
223     return 0;
224 }
225 
226 static int teardown_default_server(void **state)
227 {
228     struct torture_state *s;
229     struct server_state_st *ss;
230     struct test_server_st *tss;
231 
232     tss = *state;
233     assert_non_null(tss);
234 
235     s = tss->state;
236     assert_non_null(s);
237 
238     ss = tss->ss;
239     assert_non_null(ss);
240 
241     /* This function can be reused */
242     torture_teardown_sshd_server((void **)&s);
243 
244     free_server_state(tss->ss);
245     SAFE_FREE(tss->ss);
246     SAFE_FREE(tss);
247 
248     return 0;
249 }
250 
251 static int session_setup(void **state)
252 {
253     struct test_server_st *tss = *state;
254     struct torture_state *s;
255     int verbosity = torture_libssh_verbosity();
256     struct passwd *pwd;
257     char *cwd = NULL;
258     char *tmp_dir = NULL;
259     bool b = false;
260     int rc;
261 
262     assert_non_null(tss);
263 
264     /* Make sure we do not test the agent */
265     unsetenv("SSH_AUTH_SOCK");
266 
267     cwd = torture_get_current_working_dir();
268     assert_non_null(cwd);
269 
270     tmp_dir = torture_make_temp_dir(template);
271     assert_non_null(tmp_dir);
272 
273     tss->cwd = cwd;
274     tss->temp_dir = tmp_dir;
275 
276     s = tss->state;
277     assert_non_null(s);
278 
279     pwd = getpwnam("bob");
280     assert_non_null(pwd);
281 
282     rc = setuid(pwd->pw_uid);
283     assert_return_code(rc, errno);
284 
285     s->ssh.session = ssh_new();
286     assert_non_null(s->ssh.session);
287 
288     rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
289     assert_ssh_return_code(s->ssh.session, rc);
290     rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
291     assert_ssh_return_code(s->ssh.session, rc);
292     /* Make sure no other configuration options from system will get used */
293     rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_PROCESS_CONFIG, &b);
294     assert_ssh_return_code(s->ssh.session, rc);
295 
296     return 0;
297 }
298 
299 static int session_teardown(void **state)
300 {
301     struct test_server_st *tss = *state;
302     struct torture_state *s;
303     int rc = 0;
304 
305     assert_non_null(tss);
306 
307     s = tss->state;
308     assert_non_null(s);
309 
310     ssh_disconnect(s->ssh.session);
311     ssh_free(s->ssh.session);
312 
313     rc = torture_change_dir(tss->cwd);
314     assert_int_equal(rc, 0);
315 
316     rc = torture_rmdirs(tss->temp_dir);
317     assert_int_equal(rc, 0);
318 
319     SAFE_FREE(tss->temp_dir);
320     SAFE_FREE(tss->cwd);
321 
322     return 0;
323 }
324 
325 static void torture_server_auth_none(void **state)
326 {
327     struct test_server_st *tss = *state;
328     struct torture_state *s = NULL;
329     ssh_session session = NULL;
330     int rc;
331 
332     assert_non_null(tss);
333 
334     s = tss->state;
335     assert_non_null(s);
336 
337     session = s->ssh.session;
338     assert_non_null(session);
339 
340     rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_BOB);
341     assert_int_equal(rc, SSH_OK);
342 
343     rc = ssh_connect(session);
344     assert_int_equal(rc, SSH_OK);
345 
346     rc = ssh_userauth_none(session, NULL);
347     assert_int_equal(rc, SSH_AUTH_DENIED);
348 
349     /* This request should return a SSH_REQUEST_DENIED error */
350     if (rc == SSH_ERROR) {
351         assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
352     }
353 }
354 
355 static void torture_server_auth_password(void **state)
356 {
357     struct test_server_st *tss = *state;
358     struct torture_state *s;
359     ssh_session session;
360     int rc;
361 
362     assert_non_null(tss);
363 
364     s = tss->state;
365     assert_non_null(s);
366 
367     session = s->ssh.session;
368     assert_non_null(session);
369 
370     /* TODO: implement proper pam authentication in callback */
371     /* Using the default user for the server */
372     rc = ssh_options_set(session, SSH_OPTIONS_USER, SSHD_DEFAULT_USER);
373     assert_int_equal(rc, SSH_OK);
374 
375     rc = ssh_connect(session);
376     assert_int_equal(rc, SSH_OK);
377 
378     rc = ssh_userauth_none(session, NULL);
379     /* This request should return a SSH_REQUEST_DENIED error */
380     if (rc == SSH_AUTH_ERROR) {
381         assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
382     }
383     rc = ssh_userauth_list(session, NULL);
384     assert_true(rc & SSH_AUTH_METHOD_PASSWORD);
385 
386     /* TODO: implement proper pam authentication in callback */
387     /* Using the default password for the server */
388     rc = ssh_userauth_password(session, NULL, SSHD_DEFAULT_PASSWORD);
389     assert_int_equal(rc, SSH_AUTH_SUCCESS);
390 }
391 
392 static void torture_server_auth_pubkey(void **state)
393 {
394     struct test_server_st *tss = *state;
395     struct torture_state *s;
396     ssh_session session;
397     int rc;
398 
399     assert_non_null(tss);
400 
401     s = tss->state;
402     assert_non_null(s);
403 
404     session = s->ssh.session;
405     assert_non_null(session);
406 
407     /* Authenticate as alice with bob's pubkey */
408     rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
409     assert_int_equal(rc, SSH_OK);
410 
411     rc = ssh_connect(session);
412     assert_int_equal(rc, SSH_OK);
413 
414     rc = ssh_userauth_none(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(session), SSH_REQUEST_DENIED);
418     }
419     rc = ssh_userauth_list(session, NULL);
420     assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
421 
422     rc = ssh_userauth_publickey_auto(session, NULL, NULL);
423     assert_int_equal(rc, SSH_AUTH_SUCCESS);
424 }
425 
426 static void torture_server_hostkey_mismatch(void **state)
427 {
428     struct test_server_st *tss = *state;
429     struct torture_state *s = NULL;
430     ssh_session session = NULL;
431     char known_hosts_file[1024] = {0};
432     FILE *file = NULL;
433     enum ssh_known_hosts_e found;
434     int rc;
435 
436     assert_non_null(tss);
437 
438     s = tss->state;
439     assert_non_null(s);
440 
441     session = s->ssh.session;
442     assert_non_null(session);
443 
444     /* Store the testkey in the knownhosts file */
445     snprintf(known_hosts_file,
446              sizeof(known_hosts_file),
447              "%s/%s",
448              s->socket_dir,
449              TORTURE_KNOWN_HOSTS_FILE);
450 
451     file = fopen(known_hosts_file, "w");
452     assert_non_null(file);
453     fprintf(file,
454             "127.0.0.10 %s\n",
455             torture_get_testkey_pub(SSH_KEYTYPE_RSA));
456     fclose(file);
457 
458     rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, known_hosts_file);
459     assert_ssh_return_code(session, rc);
460     /* Using the default user for the server */
461     rc = ssh_options_set(session, SSH_OPTIONS_USER, SSHD_DEFAULT_USER);
462     assert_ssh_return_code(session, rc);
463 
464     /* Configure the client to offer only rsa-sha2-256 hostkey algorithm */
465     rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "rsa-sha2-256");
466     assert_ssh_return_code(session, rc);
467 
468     rc = ssh_connect(session);
469     assert_ssh_return_code(session, rc);
470 
471     /* Make sure we can verify the signature */
472     found = ssh_session_is_known_server(session);
473     assert_int_equal(found, SSH_KNOWN_HOSTS_OK);
474 }
475 
476 static void torture_server_unknown_global_request(void **state)
477 {
478     struct test_server_st *tss = *state;
479     struct torture_state *s = NULL;
480     ssh_session session = NULL;
481     ssh_channel channel;
482     int rc;
483 
484     assert_non_null(tss);
485 
486     s = tss->state;
487     assert_non_null(s);
488 
489     session = s->ssh.session;
490     assert_non_null(session);
491 
492     rc = ssh_options_set(session, SSH_OPTIONS_USER, SSHD_DEFAULT_USER);
493     assert_int_equal(rc, SSH_OK);
494 
495     rc = ssh_connect(session);
496     assert_int_equal(rc, SSH_OK);
497 
498     /* Using the default password for the server */
499     rc = ssh_userauth_password(session, NULL, SSHD_DEFAULT_PASSWORD);
500     assert_int_equal(rc, SSH_AUTH_SUCCESS);
501 
502     /* Request asking for reply */
503     rc = ssh_global_request(session, "unknown-request-00@test.com", NULL, 1);
504     assert_ssh_return_code_equal(session, rc, SSH_ERROR);
505 
506     /* Request and don't ask for reply */
507     rc = ssh_global_request(session, "another-bad-req-00@test.com", NULL, 0);
508     assert_ssh_return_code(session, rc);
509 
510     /* Open channel to make sure the session is still working */
511     channel = ssh_channel_new(session);
512     assert_non_null(channel);
513 
514     rc = ssh_channel_open_session(channel);
515     assert_ssh_return_code(session, rc);
516 
517     ssh_channel_close(channel);
518 }
519 
520 int torture_run_tests(void) {
521     int rc;
522     struct CMUnitTest tests[] = {
523         cmocka_unit_test_setup_teardown(torture_server_auth_none,
524                                         session_setup,
525                                         session_teardown),
526         cmocka_unit_test_setup_teardown(torture_server_auth_password,
527                                         session_setup,
528                                         session_teardown),
529         cmocka_unit_test_setup_teardown(torture_server_auth_pubkey,
530                                         session_setup,
531                                         session_teardown),
532         cmocka_unit_test_setup_teardown(torture_server_hostkey_mismatch,
533                                         session_setup,
534                                         session_teardown),
535         cmocka_unit_test_setup_teardown(torture_server_unknown_global_request,
536                                         session_setup,
537                                         session_teardown),
538     };
539 
540     ssh_init();
541 
542     torture_filter_tests(tests);
543     rc = cmocka_run_group_tests(tests,
544             setup_default_server,
545             teardown_default_server);
546 
547     ssh_finalize();
548 
549     return rc;
550 }
551