1 /*
2  * This file is part of the SSH Library
3  *
4  * Copyright (c) 2019 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 <signal.h>
40 #include <sys/wait.h>
41 #include <sys/ioctl.h>
42 
43 #include "test_server.h"
44 #include "default_cb.h"
45 
46 #define TORTURE_KNOWN_HOSTS_FILE "libssh_torture_knownhosts"
47 
48 enum {
49     SUCCESS,
50     MORE,
51     FAILED
52 };
setup_default_server(void ** state)53 
54 struct test_server_st {
55     struct torture_state *state;
56     struct server_state_st *ss;
57 };
58 
59 #ifdef WITH_PCAP
60 static void set_pcap(struct session_data_st *sdata,
61                      ssh_session session,
62                      char *pcap_file)
63 {
64     int rc = 0;
65 
66     if (sdata == NULL) {
67         return;
68     }
69 
70     if (pcap_file == NULL) {
71         return;
72     }
73 
74     sdata->pcap = ssh_pcap_file_new();
75     if (sdata->pcap == NULL) {
76         return;
77     }
78 
79     rc = ssh_pcap_file_open(sdata->pcap, pcap_file);
80     if (rc == SSH_ERROR) {
81         fprintf(stderr, "Error opening pcap file\n");
82         ssh_pcap_file_free(sdata->pcap);
83         sdata->pcap = NULL;
84         return;
85     }
86     ssh_set_pcap_file(session, sdata->pcap);
87 }
88 
89 static void cleanup_pcap(struct session_data_st *sdata)
90 {
91     if (sdata == NULL) {
92         return;
93     }
94 
95     if (sdata->pcap == NULL) {
96         return;
97     }
98 
99     /* Do not free the pcap data context here since its ownership was
100      * transfered to the session object, which will take care of its cleanup.
101      * Morover it is still in use so we can very simply crash by freeing
102      * it here.
103      */
104     sdata->pcap = NULL;
105 }
106 #endif
107 
108 static int process_stdout(socket_t fd, int revents, void *userdata)
109 {
110     char buf[BUF_SIZE];
111     int n = -1;
112     ssh_channel channel = (ssh_channel) userdata;
113 
114     if (channel != NULL && (revents & POLLIN) != 0) {
115         n = read(fd, buf, BUF_SIZE);
116         if (n > 0) {
117             ssh_channel_write(channel, buf, n);
118         }
119     }
120 
121     return n;
122 }
123 
124 static int process_stderr(socket_t fd, int revents, void *userdata)
125 {
126     char buf[BUF_SIZE];
127     int n = -1;
128     ssh_channel channel = (ssh_channel) userdata;
129 
130     if (channel != NULL && (revents & POLLIN) != 0) {
131         n = read(fd, buf, BUF_SIZE);
132         if (n > 0) {
133             ssh_channel_write_stderr(channel, buf, n);
134         }
135     }
136 
137     return n;
138 }
139 
140 static int authenticate_kbdint(ssh_session session,
141                                ssh_message message,
142                                void *userdata)
143 {
144     int rc = 0;
145     int count;
146     int *step = NULL;
147     size_t expected_len;
148 
149     const char instruction[] = "Type the requested data";
150     const char name[] = "Keyboard-Interactive Authentication\n";
151     char initial_echo[] = {1, 0};
152     char retype_echo[] = {0};
153     const char *initial_prompt[2];
154     const char *retype_prompt[1];
155     int cmp;
156 
157     const char *answer;
158 
159     struct session_data_st *sdata = (struct session_data_st *)userdata;
160 
161     initial_prompt[0] = "username: ";
162     initial_prompt[1] = "password: ";
163 
164     /* Prompt for aditional prompts */
165     retype_prompt[0] = "retype password: ";
166 
167     if ((session == NULL) || (message == NULL) || (sdata == NULL)) {
168         fprintf(stderr, "Null argument provided\n");
169         goto failed;
170     }
171 
172     if (sdata->extra_data == NULL) {
173         goto failed;
174     }
175 
176     step = (int *)sdata->extra_data;
177 
178     switch (*step) {
179     case 0:
180         ssh_message_auth_interactive_request(message, name, instruction, 2,
181                 initial_prompt, initial_echo);
182         rc = MORE;
183         goto end;
184     case 1:
185         count = ssh_userauth_kbdint_getnanswers(session);
186         if (count != 2) {
187             goto failed;
188         }
189 
190         if ((sdata->username == NULL) || (sdata->password == NULL)) {
191             goto failed;
192         }
193 
194         /* Get and compare username */
195         expected_len = strlen(sdata->username);
196         if (expected_len <= 0) {
197             goto failed;
198         }
199 
200         answer = ssh_userauth_kbdint_getanswer(session, 0);
201         if (answer == NULL) {
202             goto failed;
203         }
204 
205         cmp = strncmp(answer, sdata->username, expected_len);
206         if (cmp != 0) {
207             goto failed;
208         }
209 
210         /* Get and compare password */
211         expected_len = strlen(sdata->password);
212         if (expected_len <= 0) {
213             goto failed;
214         }
215 
216         answer = ssh_userauth_kbdint_getanswer(session, 1);
217         if (answer == NULL) {
218             goto failed;
219         }
220 
221         cmp = strncmp(answer, sdata->password, expected_len);
222         if (cmp != 0) {
223             goto failed;
224         }
225 
teardown_default_server(void ** state)226         /* Username and password matched. Ask for a retype. */
227         ssh_message_auth_interactive_request(message,
228                                              name,
229                                              instruction,
230                                              1,
231                                              retype_prompt,
232                                              retype_echo);
233 
234         rc = MORE;
235         goto end;
236     case 2:
237         /* Get and compare password */
238         expected_len = strlen(sdata->password);
239         if (expected_len <= 0) {
240             goto failed;
241         }
242 
243         answer = ssh_userauth_kbdint_getanswer(session, 0);
244         if (answer == NULL) {
245             goto failed;
246         }
247 
248         cmp = strncmp(answer, sdata->password, expected_len);
249         if (cmp != 0) {
250             goto failed;
session_setup(void ** state)251         }
252 
253         /* Password was correct, authenticated */
254         rc = SUCCESS;
255         goto end;
256     default:
257         goto failed;
258     }
259 
260 failed:
261     if (step != NULL) {
262         *step = 0;
263     }
264     return FAILED;
265 
266 end:
267     if (step != NULL) {
268         (*step)++;
269     }
270     return rc;
271 }
272 
273 static int authenticate_callback(ssh_session session,
274                                  ssh_message message,
275                                  void *userdata)
276 {
277     struct session_data_st *sdata = (struct session_data_st *)userdata;
278     int rc;
279 
280     if (sdata == NULL) {
281         fprintf(stderr, "Null userdata\n");
282         goto denied;
283     }
284 
285     if (sdata->extra_data == NULL) {
286         sdata->extra_data = (void *)calloc(1, sizeof(int));
287     }
288 
289     switch (ssh_message_type(message)) {
290     case SSH_REQUEST_AUTH:
291         switch (ssh_message_subtype(message)) {
292         case SSH_AUTH_METHOD_INTERACTIVE:
293             rc = authenticate_kbdint(session, message, (void *)sdata);
294             if (rc == SUCCESS) {
295                 goto accept;
296             }
297             else if (rc == MORE) {
298                 goto more;
session_teardown(void ** state)299             }
300             ssh_message_auth_set_methods(message, SSH_AUTH_METHOD_INTERACTIVE);
301             goto denied;
302         default:
303             ssh_message_auth_set_methods(message, SSH_AUTH_METHOD_INTERACTIVE);
304             goto denied;
305         }
306     default:
307         ssh_message_auth_set_methods(message, SSH_AUTH_METHOD_INTERACTIVE);
308         goto denied;
309     }
310 
311     ssh_message_free(message);
312 
313 accept:
314     if (sdata) {
315         if (sdata->extra_data) {
316             free(sdata->extra_data);
317             sdata->extra_data = NULL;
318         }
319     }
320     ssh_message_auth_reply_success (message, 0);
321 more:
322     return 0;
323 denied:
324     if (sdata) {
torture_server_auth_none(void ** state)325         if (sdata->extra_data) {
326             free(sdata->extra_data);
327             sdata->extra_data = NULL;
328         }
329     }
330     return 1;
331 }
332 
333 static void handle_kbdint_session_cb(ssh_event event,
334                                      ssh_session session,
335                                      struct server_state_st *state)
336 {
337     int n;
338     int rc = 0;
339 
340     /* Structure for storing the pty size. */
341     struct winsize wsize = {
342         .ws_row = 0,
343         .ws_col = 0,
344         .ws_xpixel = 0,
345         .ws_ypixel = 0
346     };
347 
348     /* Our struct holding information about the channel. */
349     struct channel_data_st cdata = {
350         .pid = 0,
351         .pty_master = -1,
352         .pty_slave = -1,
353         .child_stdin = -1,
354         .child_stdout = -1,
torture_server_auth_password(void ** state)355         .child_stderr = -1,
356         .event = NULL,
357         .winsize = &wsize
358     };
359 
360     /* Our struct holding information about the session. */
361     struct session_data_st sdata = {
362         .channel = NULL,
363         .auth_attempts = 0,
364         .authenticated = 0,
365         .username = TORTURE_SSH_USER_BOB,
366         .password = TORTURE_SSH_USER_BOB_PASSWORD
367     };
368 
369     struct ssh_channel_callbacks_struct *channel_cb = NULL;
370     struct ssh_server_callbacks_struct *server_cb = NULL;
371 
372     if (state == NULL) {
373         fprintf(stderr, "NULL server state provided\n");
374         goto end;
375     }
376 
377     server_cb = get_default_server_cb();
378     if (server_cb == NULL) {
379         goto end;
380     }
381 
382     server_cb->userdata = &sdata;
383 
384     /* This is a macro, it does not return a value */
385     ssh_callbacks_init(server_cb);
386 
387     rc = ssh_set_server_callbacks(session, server_cb);
388     if (rc) {
389         goto end;
390     }
391 
torture_server_auth_pubkey(void ** state)392 #ifdef WITH_PCAP
393     set_pcap(&sdata, session, state->pcap_file);
394 #endif
395 
396     rc = ssh_handle_key_exchange(session);
397     if (rc != SSH_OK) {
398         fprintf(stderr, "%s\n", ssh_get_error(session));
399         goto end;
400     }
401 
402     /* Set the supported authentication methods */
403     ssh_set_auth_methods(session, SSH_AUTH_METHOD_INTERACTIVE);
404 
405     ssh_set_message_callback(session, authenticate_callback, &sdata);
406 
407     rc = ssh_event_add_session(event, session);
408     if (rc != 0) {
409         fprintf(stderr, "Error adding session to event\n");
410         goto end;
411     }
412 
413     n = 0;
414     while (sdata.authenticated == 0 || sdata.channel == NULL) {
415         /* If the user has used up all attempts, or if he hasn't been able to
416          * authenticate in 10 seconds (n * 100ms), disconnect. */
417         if (sdata.auth_attempts >= state->max_tries || n >= 100) {
418             goto end;
419         }
420 
421         if (ssh_event_dopoll(event, 100) == SSH_ERROR) {
422             fprintf(stderr, "do_poll error: %s\n", ssh_get_error(session));
423             goto end;
424         }
425         n++;
torture_server_hostkey_mismatch(void ** state)426     }
427 
428     channel_cb = get_default_channel_cb();
429     if (channel_cb == NULL) {
430         goto end;
431     }
432 
433     channel_cb->userdata = &cdata;
434 
435     ssh_callbacks_init(channel_cb);
436     rc = ssh_set_channel_callbacks(sdata.channel, channel_cb);
437     if (rc != 0) {
438         goto end;
439     }
440 
441     do {
442         /* Poll the main event which takes care of the session, the channel and
443          * even our child process's stdout/stderr (once it's started). */
444         rc = ssh_event_dopoll(event, -1);
445         if (rc == SSH_ERROR) {
446           ssh_channel_close(sdata.channel);
447         }
448 
449         /* If child process's stdout/stderr has been registered with the event,
450          * or the child process hasn't started yet, continue. */
451         if (cdata.event != NULL || cdata.pid == 0) {
452             continue;
453         }
454         /* Executed only once, once the child process starts. */
455         cdata.event = event;
456         /* If stdout valid, add stdout to be monitored by the poll event. */
457         if (cdata.child_stdout != -1) {
458             if (ssh_event_add_fd(event, cdata.child_stdout, POLLIN, process_stdout,
459                                  sdata.channel) != SSH_OK) {
460                 fprintf(stderr, "Failed to register stdout to poll context\n");
461                 ssh_channel_close(sdata.channel);
462             }
463         }
464 
465         /* If stderr valid, add stderr to be monitored by the poll event. */
466         if (cdata.child_stderr != -1){
467             if (ssh_event_add_fd(event, cdata.child_stderr, POLLIN, process_stderr,
468                                  sdata.channel) != SSH_OK) {
469                 fprintf(stderr, "Failed to register stderr to poll context\n");
470                 ssh_channel_close(sdata.channel);
471             }
472         }
473     } while(ssh_channel_is_open(sdata.channel) &&
474             (cdata.pid == 0 || waitpid(cdata.pid, &rc, WNOHANG) == 0));
475 
torture_server_unknown_global_request(void ** state)476     close(cdata.pty_master);
477     close(cdata.child_stdin);
478     close(cdata.child_stdout);
479     close(cdata.child_stderr);
480 
481     /* Remove the descriptors from the polling context, since they are now
482      * closed, they will always trigger during the poll calls. */
483     ssh_event_remove_fd(event, cdata.child_stdout);
484     ssh_event_remove_fd(event, cdata.child_stderr);
485 
486     /* If the child process exited. */
487     if (kill(cdata.pid, 0) < 0 && WIFEXITED(rc)) {
488         rc = WEXITSTATUS(rc);
489         ssh_channel_request_send_exit_status(sdata.channel, rc);
490     /* If client terminated the channel or the process did not exit nicely,
491      * but only if something has been forked. */
492     } else if (cdata.pid > 0) {
493         kill(cdata.pid, SIGKILL);
494     }
495 
496     ssh_channel_send_eof(sdata.channel);
497     ssh_channel_close(sdata.channel);
498 
499     /* Wait up to 5 seconds for the client to terminate the session. */
500     for (n = 0; n < 50 && (ssh_get_status(session) & SESSION_END) == 0; n++) {
501         ssh_event_dopoll(event, 100);
502     }
503 
504 end:
505 #ifdef WITH_PCAP
506     cleanup_pcap(&sdata);
507 #endif
508     if (channel_cb != NULL) {
509         free(channel_cb);
510     }
511     if (server_cb != NULL) {
512         free(server_cb);
513     }
514     return;
515 }
516 
517 static int setup_kbdint_server(void **state)
518 {
519     struct torture_state *s;
torture_run_tests(void)520     struct server_state_st *ss;
521     struct test_server_st *tss;
522 
523     char rsa_hostkey[1024] = {0};
524 
525     char sshd_path[1024];
526 
527     int rc;
528 
529     char pid_str[1024];
530 
531     pid_t pid;
532 
533     assert_non_null(state);
534 
535     tss = (struct test_server_st*)calloc(1, sizeof(struct test_server_st));
536     assert_non_null(tss);
537 
538     torture_setup_socket_dir((void **)&s);
539     assert_non_null(s->socket_dir);
540 
541     /* Set the default interface for the server */
542     setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "10", 1);
543     setenv("PAM_WRAPPER", "1", 1);
544 
545     snprintf(sshd_path,
546              sizeof(sshd_path),
547              "%s/sshd",
548              s->socket_dir);
549 
550     rc = mkdir(sshd_path, 0755);
551     assert_return_code(rc, errno);
552 
553     snprintf(rsa_hostkey,
554              sizeof(rsa_hostkey),
555              "%s/sshd/ssh_host_rsa_key",
556              s->socket_dir);
557     torture_write_file(rsa_hostkey,
558                        torture_get_openssh_testkey(SSH_KEYTYPE_RSA, 0));
559 
560     /* Create the server state */
561     ss = (struct server_state_st *)calloc(1, sizeof(struct server_state_st));
562     assert_non_null(ss);
563 
564     ss->address = strdup("127.0.0.10");
565     assert_non_null(ss->address);
566 
567     ss->port = 22;
568 
569     ss->host_key = strdup(rsa_hostkey);
570     assert_non_null(rsa_hostkey);
571 
572     ss->verbosity = torture_libssh_verbosity();
573 
574 #ifdef WITH_PCAP
575     ss->with_pcap = 1;
576     ss->pcap_file = strdup(s->pcap_file);
577     assert_non_null(ss->pcap_file);
578 #endif
579 
580     ss->max_tries = 3;
581     ss->error = 0;
582 
583     /* Set the session handling function */
584     ss->handle_session = handle_kbdint_session_cb;
585     assert_non_null(ss->handle_session);
586 
587     /* Start the server */
588     pid = fork_run_server(ss);
589     if (pid < 0) {
590         fail();
591     }
592 
593     snprintf(pid_str, sizeof(pid_str), "%d", pid);
594 
595     torture_write_file(s->srv_pidfile, (const char *)pid_str);
596 
597     setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "21", 1);
598     unsetenv("PAM_WRAPPER");
599 
600     /* Wait 200ms */
601     usleep(200 * 1000);
602 
603     tss->state = s;
604     tss->ss = ss;
605 
606     *state = tss;
607 
608     return 0;
609 }
610 
611 static int teardown_kbdint_server(void **state)
612 {
613     struct torture_state *s;
614     struct server_state_st *ss;
615     struct test_server_st *tss;
616 
617     tss = *state;
618     assert_non_null(tss);
619 
620     s = tss->state;
621     assert_non_null(s);
622 
623     ss = tss->ss;
624     assert_non_null(ss);
625 
626     /* This function can be reused */
627     torture_teardown_sshd_server((void **)&s);
628 
629     free_server_state(tss->ss);
630     SAFE_FREE(tss->ss);
631     SAFE_FREE(tss);
632 
633     return 0;
634 }
635 
636 static int session_setup(void **state)
637 {
638     struct test_server_st *tss = *state;
639     struct torture_state *s;
640     int verbosity = torture_libssh_verbosity();
641     struct passwd *pwd;
642     bool b = false;
643     int rc;
644 
645     assert_non_null(tss);
646 
647     s = tss->state;
648     assert_non_null(s);
649 
650     pwd = getpwnam("bob");
651     assert_non_null(pwd);
652 
653     rc = setuid(pwd->pw_uid);
654     assert_return_code(rc, errno);
655 
656     s->ssh.session = ssh_new();
657     assert_non_null(s->ssh.session);
658 
659     rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
660     assert_ssh_return_code(s->ssh.session, rc);
661     rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
662     assert_ssh_return_code(s->ssh.session, rc);
663     /* Make sure no other configuration options from system will get used */
664     rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_PROCESS_CONFIG, &b);
665     assert_ssh_return_code(s->ssh.session, rc);
666 
667     return 0;
668 }
669 
670 static int session_teardown(void **state)
671 {
672     struct test_server_st *tss = *state;
673     struct torture_state *s;
674 
675     assert_non_null(tss);
676 
677     s = tss->state;
678     assert_non_null(s);
679 
680     ssh_disconnect(s->ssh.session);
681     ssh_free(s->ssh.session);
682 
683     return 0;
684 }
685 
686 static void torture_server_auth_kbdint(void **state)
687 {
688     struct test_server_st *tss = *state;
689     struct torture_state *s;
690     ssh_session session;
691     int rc;
692 
693     assert_non_null(tss);
694 
695     s = tss->state;
696     assert_non_null(s);
697 
698     session = s->ssh.session;
699     assert_non_null(session);
700 
701     rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_BOB);
702     assert_ssh_return_code(session, rc);
703 
704     rc = ssh_connect(session);
705     assert_ssh_return_code(session, rc);
706 
707     rc = ssh_userauth_none(session,NULL);
708     /* This request should return a SSH_REQUEST_DENIED error */
709     if (rc == SSH_ERROR) {
710         assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
711     }
712     rc = ssh_userauth_list(session, NULL);
713     assert_true(rc & SSH_AUTH_METHOD_INTERACTIVE);
714 
715     rc = ssh_userauth_kbdint(session, NULL, NULL);
716     assert_int_equal(rc, SSH_AUTH_INFO);
717     assert_int_equal(ssh_userauth_kbdint_getnprompts(session), 2);
718 
719     /* Reply the first 2 prompts using the username and password */
720     rc = ssh_userauth_kbdint_setanswer(session, 0,
721             TORTURE_SSH_USER_BOB);
722     assert_false(rc < 0);
723 
724     rc = ssh_userauth_kbdint_setanswer(session, 1,
725             TORTURE_SSH_USER_BOB_PASSWORD);
726     assert_false(rc < 0);
727 
728     /* Resend the password */
729     rc = ssh_userauth_kbdint(session, NULL, NULL);
730     assert_int_equal(rc, SSH_AUTH_INFO);
731     assert_int_equal(ssh_userauth_kbdint_getnprompts(session), 1);
732 
733     rc = ssh_userauth_kbdint_setanswer(session, 0,
734             TORTURE_SSH_USER_BOB_PASSWORD);
735     assert_false(rc < 0);
736 
737     rc = ssh_userauth_kbdint(session, NULL, NULL);
738 
739     /* Sometimes, SSH server send an empty query at the end of exchange */
740     if(rc == SSH_AUTH_INFO) {
741         assert_int_equal(ssh_userauth_kbdint_getnprompts(session), 0);
742         rc = ssh_userauth_kbdint(session, NULL, NULL);
743     }
744 
745     assert_int_equal(rc, SSH_AUTH_SUCCESS);
746 }
747 
748 int torture_run_tests(void)
749 {
750     int rc;
751     struct CMUnitTest tests[] = {
752         cmocka_unit_test_setup_teardown(torture_server_auth_kbdint,
753                                         session_setup,
754                                         session_teardown),
755     };
756 
757     ssh_init();
758 
759     torture_filter_tests(tests);
760     rc = cmocka_run_group_tests(tests,
761             setup_kbdint_server,
762             teardown_kbdint_server);
763 
764     ssh_finalize();
765 
766     return rc;
767 }
768