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