diff options
author | Damien Miller <djm@mindrot.org> | 2000-04-04 14:38:59 +1000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2000-04-04 14:38:59 +1000 |
commit | 33b13568b520b25990261206e10c941a9270238f (patch) | |
tree | be9d549ee0c9c7774e3ec1da8d807b2e04b00bec | |
parent | 193ba88dd6e9d6bcd5f476c7f5ddde8fd0b752bf (diff) |
- OpenBSD CVS update:
- [packet.h packet.c]
ssh2 packet format
- [packet.h packet.c nchan2.ms nchan.h compat.h compat.c]
[channels.h channels.c]
channel layer support for ssh2
- [kex.h kex.c hmac.h hmac.c dsa.c dsa.h]
DSA, keyexchange, algorithm agreement for ssh2
-rw-r--r-- | ChangeLog | 8 | ||||
-rw-r--r-- | Makefile.in | 2 | ||||
-rw-r--r-- | channels.c | 460 | ||||
-rw-r--r-- | channels.h | 15 | ||||
-rw-r--r-- | compat.c | 33 | ||||
-rw-r--r-- | compat.h | 6 | ||||
-rw-r--r-- | dsa.c | 309 | ||||
-rw-r--r-- | dsa.h | 20 | ||||
-rw-r--r-- | hmac.c | 64 | ||||
-rw-r--r-- | hmac.h | 11 | ||||
-rw-r--r-- | kex.c | 407 | ||||
-rw-r--r-- | kex.h | 111 | ||||
-rw-r--r-- | nchan.c | 379 | ||||
-rw-r--r-- | nchan.h | 30 | ||||
-rw-r--r-- | nchan2.ms | 64 | ||||
-rw-r--r-- | packet.c | 514 | ||||
-rw-r--r-- | packet.h | 10 |
17 files changed, 2277 insertions, 166 deletions
@@ -1,5 +1,13 @@ | |||
1 | 20000404 | 1 | 20000404 |
2 | - Add tests for RAND_add function when searching for OpenSSL | 2 | - Add tests for RAND_add function when searching for OpenSSL |
3 | - OpenBSD CVS update: | ||
4 | - [packet.h packet.c] | ||
5 | ssh2 packet format | ||
6 | - [packet.h packet.c nchan2.ms nchan.h compat.h compat.c] | ||
7 | [channels.h channels.c] | ||
8 | channel layer support for ssh2 | ||
9 | - [kex.h kex.c hmac.h hmac.c dsa.c dsa.h] | ||
10 | DSA, keyexchange, algorithm agreement for ssh2 | ||
3 | 11 | ||
4 | 20000403 | 12 | 20000403 |
5 | - Wrote entropy collection routines for systems that lack /dev/random | 13 | - Wrote entropy collection routines for systems that lack /dev/random |
diff --git a/Makefile.in b/Makefile.in index 89b408487..bb08b53b9 100644 --- a/Makefile.in +++ b/Makefile.in | |||
@@ -31,7 +31,7 @@ LDFLAGS=-L. @LDFLAGS@ | |||
31 | 31 | ||
32 | TARGETS=ssh sshd ssh-add ssh-keygen ssh-agent scp $(EXTRA_TARGETS) | 32 | TARGETS=ssh sshd ssh-add ssh-keygen ssh-agent scp $(EXTRA_TARGETS) |
33 | 33 | ||
34 | LIBOBJS= atomicio.o authfd.o authfile.o bsd-bindresvport.o bsd-daemon.o bsd-misc.o bsd-mktemp.o bsd-rresvport.o bsd-setenv.o bsd-snprintf.o bsd-strlcat.o bsd-strlcpy.o bufaux.o buffer.o canohost.o channels.o cipher.o compat.o compress.o crc32.o deattack.o dispatch.o fake-getaddrinfo.o fake-getnameinfo.o fingerprint.o hostfile.o key.o log.o match.o mpaux.o nchan.o packet.o radix.o entropy.o readpass.o rsa.o tildexpand.o ttymodes.o uidswap.o xmalloc.o | 34 | LIBOBJS= atomicio.o authfd.o authfile.o bsd-bindresvport.o bsd-daemon.o bsd-misc.o bsd-mktemp.o bsd-rresvport.o bsd-setenv.o bsd-snprintf.o bsd-strlcat.o bsd-strlcpy.o bufaux.o buffer.o canohost.o channels.o cipher.o compat.o compress.o crc32.o deattack.o dispatch.o dsa.o fake-getaddrinfo.o fake-getnameinfo.o fingerprint.o hmac.o hostfile.o key.o kex.o log.o match.o mpaux.o nchan.o packet.o radix.o entropy.o readpass.o rsa.o tildexpand.o ttymodes.o uidswap.o xmalloc.o |
35 | 35 | ||
36 | SSHOBJS= ssh.o sshconnect.o log-client.o readconf.o clientloop.o | 36 | SSHOBJS= ssh.o sshconnect.o log-client.o readconf.o clientloop.o |
37 | 37 | ||
diff --git a/channels.c b/channels.c index b87ff9f4f..18f667f6b 100644 --- a/channels.c +++ b/channels.c | |||
@@ -13,10 +13,11 @@ | |||
13 | * There is also code for initiating connection forwarding for X11 connections, | 13 | * There is also code for initiating connection forwarding for X11 connections, |
14 | * arbitrary tcp/ip connections, and the authentication agent connection. | 14 | * arbitrary tcp/ip connections, and the authentication agent connection. |
15 | * | 15 | * |
16 | * SSH2 support added by Markus Friedl. | ||
16 | */ | 17 | */ |
17 | 18 | ||
18 | #include "includes.h" | 19 | #include "includes.h" |
19 | RCSID("$Id: channels.c,v 1.20 2000/04/01 01:09:23 damien Exp $"); | 20 | RCSID("$Id: channels.c,v 1.21 2000/04/04 04:39:00 damien Exp $"); |
20 | 21 | ||
21 | #include "ssh.h" | 22 | #include "ssh.h" |
22 | #include "packet.h" | 23 | #include "packet.h" |
@@ -31,6 +32,8 @@ RCSID("$Id: channels.c,v 1.20 2000/04/01 01:09:23 damien Exp $"); | |||
31 | #include "nchan.h" | 32 | #include "nchan.h" |
32 | #include "compat.h" | 33 | #include "compat.h" |
33 | 34 | ||
35 | #include "ssh2.h" | ||
36 | |||
34 | /* Maximum number of fake X11 displays to try. */ | 37 | /* Maximum number of fake X11 displays to try. */ |
35 | #define MAX_DISPLAYS 1000 | 38 | #define MAX_DISPLAYS 1000 |
36 | 39 | ||
@@ -165,6 +168,7 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd, | |||
165 | 168 | ||
166 | /* Do initial allocation if this is the first call. */ | 169 | /* Do initial allocation if this is the first call. */ |
167 | if (channels_alloc == 0) { | 170 | if (channels_alloc == 0) { |
171 | chan_init(); | ||
168 | channels_alloc = 10; | 172 | channels_alloc = 10; |
169 | channels = xmalloc(channels_alloc * sizeof(Channel)); | 173 | channels = xmalloc(channels_alloc * sizeof(Channel)); |
170 | for (i = 0; i < channels_alloc; i++) | 174 | for (i = 0; i < channels_alloc; i++) |
@@ -237,9 +241,28 @@ channel_free(int id) | |||
237 | if (c == NULL) | 241 | if (c == NULL) |
238 | packet_disconnect("channel free: bad local channel %d", id); | 242 | packet_disconnect("channel free: bad local channel %d", id); |
239 | debug("channel_free: channel %d: status: %s", id, channel_open_message()); | 243 | debug("channel_free: channel %d: status: %s", id, channel_open_message()); |
244 | if (c->dettach_user != NULL) { | ||
245 | debug("channel_free: channel %d: dettaching channel user", id); | ||
246 | c->dettach_user(c->self, NULL); | ||
247 | } | ||
240 | if (c->sock != -1) { | 248 | if (c->sock != -1) { |
241 | shutdown(c->sock, SHUT_RDWR); | 249 | shutdown(c->sock, SHUT_RDWR); |
242 | close(c->sock); | 250 | close(c->sock); |
251 | c->sock = -1; | ||
252 | } | ||
253 | if (compat20) { | ||
254 | if (c->rfd != -1) { | ||
255 | close(c->rfd); | ||
256 | c->rfd = -1; | ||
257 | } | ||
258 | if (c->wfd != -1) { | ||
259 | close(c->wfd); | ||
260 | c->wfd = -1; | ||
261 | } | ||
262 | if (c->efd != -1) { | ||
263 | close(c->efd); | ||
264 | c->efd = -1; | ||
265 | } | ||
243 | } | 266 | } |
244 | buffer_free(&c->input); | 267 | buffer_free(&c->input); |
245 | buffer_free(&c->output); | 268 | buffer_free(&c->output); |
@@ -296,6 +319,32 @@ channel_pre_open_15(Channel *c, fd_set * readset, fd_set * writeset) | |||
296 | } | 319 | } |
297 | 320 | ||
298 | void | 321 | void |
322 | channel_pre_open_20(Channel *c, fd_set * readset, fd_set * writeset) | ||
323 | { | ||
324 | if (c->istate == CHAN_INPUT_OPEN && | ||
325 | c->remote_window > 0 && | ||
326 | buffer_len(&c->input) < c->remote_window) | ||
327 | FD_SET(c->rfd, readset); | ||
328 | if (c->ostate == CHAN_OUTPUT_OPEN || | ||
329 | c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { | ||
330 | if (buffer_len(&c->output) > 0) { | ||
331 | FD_SET(c->wfd, writeset); | ||
332 | } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { | ||
333 | chan_obuf_empty(c); | ||
334 | } | ||
335 | } | ||
336 | /** XXX check close conditions, too */ | ||
337 | if (c->efd != -1) { | ||
338 | if (c->extended_usage == CHAN_EXTENDED_WRITE && | ||
339 | buffer_len(&c->extended) > 0) | ||
340 | FD_SET(c->efd, writeset); | ||
341 | else if (c->extended_usage == CHAN_EXTENDED_READ && | ||
342 | buffer_len(&c->extended) < c->remote_window) | ||
343 | FD_SET(c->efd, readset); | ||
344 | } | ||
345 | } | ||
346 | |||
347 | void | ||
299 | channel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset) | 348 | channel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset) |
300 | { | 349 | { |
301 | if (buffer_len(&c->input) == 0) { | 350 | if (buffer_len(&c->input) == 0) { |
@@ -483,15 +532,27 @@ channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset) | |||
483 | SSH_CHANNEL_OPENING, newsock, newsock, -1, | 532 | SSH_CHANNEL_OPENING, newsock, newsock, -1, |
484 | c->local_window_max, c->local_maxpacket, | 533 | c->local_window_max, c->local_maxpacket, |
485 | 0, xstrdup(buf)); | 534 | 0, xstrdup(buf)); |
486 | 535 | if (compat20) { | |
487 | packet_start(SSH_MSG_PORT_OPEN); | 536 | packet_start(SSH2_MSG_CHANNEL_OPEN); |
488 | packet_put_int(newch); | 537 | packet_put_cstring("direct-tcpip"); |
489 | packet_put_string(c->path, strlen(c->path)); | 538 | packet_put_int(newch); |
490 | packet_put_int(c->host_port); | 539 | packet_put_int(c->local_window_max); |
491 | if (have_hostname_in_open) { | 540 | packet_put_int(c->local_maxpacket); |
492 | packet_put_string(buf, strlen(buf)); | 541 | packet_put_string(c->path, strlen(c->path)); |
542 | packet_put_int(c->host_port); | ||
543 | packet_put_cstring(remote_hostname); | ||
544 | packet_put_int(remote_port); | ||
545 | packet_send(); | ||
546 | } else { | ||
547 | packet_start(SSH_MSG_PORT_OPEN); | ||
548 | packet_put_int(newch); | ||
549 | packet_put_string(c->path, strlen(c->path)); | ||
550 | packet_put_int(c->host_port); | ||
551 | if (have_hostname_in_open) { | ||
552 | packet_put_string(buf, strlen(buf)); | ||
553 | } | ||
554 | packet_send(); | ||
493 | } | 555 | } |
494 | packet_send(); | ||
495 | xfree(remote_hostname); | 556 | xfree(remote_hostname); |
496 | } | 557 | } |
497 | } | 558 | } |
@@ -569,6 +630,56 @@ channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) | |||
569 | return -1; | 630 | return -1; |
570 | } | 631 | } |
571 | buffer_consume(&c->output, len); | 632 | buffer_consume(&c->output, len); |
633 | if (compat20 && len > 0) { | ||
634 | c->local_consumed += len; | ||
635 | } | ||
636 | } | ||
637 | return 1; | ||
638 | } | ||
639 | int | ||
640 | channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset) | ||
641 | { | ||
642 | char buf[16*1024]; | ||
643 | int len; | ||
644 | |||
645 | if (c->efd != -1) { | ||
646 | if (c->extended_usage == CHAN_EXTENDED_WRITE && | ||
647 | FD_ISSET(c->efd, writeset) && | ||
648 | buffer_len(&c->extended) > 0) { | ||
649 | len = write(c->efd, buffer_ptr(&c->extended), | ||
650 | buffer_len(&c->extended)); | ||
651 | debug("channel %d: written %d to efd %d", | ||
652 | c->self, len, c->efd); | ||
653 | if (len > 0) { | ||
654 | buffer_consume(&c->extended, len); | ||
655 | c->local_consumed += len; | ||
656 | } | ||
657 | } else if (c->extended_usage == CHAN_EXTENDED_READ && | ||
658 | FD_ISSET(c->efd, readset)) { | ||
659 | len = read(c->efd, buf, sizeof(buf)); | ||
660 | debug("channel %d: read %d from efd %d", | ||
661 | c->self, len, c->efd); | ||
662 | if (len > 0) | ||
663 | buffer_append(&c->extended, buf, len); | ||
664 | } | ||
665 | } | ||
666 | return 1; | ||
667 | } | ||
668 | int | ||
669 | channel_check_window(Channel *c, fd_set * readset, fd_set * writeset) | ||
670 | { | ||
671 | if (!(c->flags & CHAN_CLOSE_SENT) && | ||
672 | c->local_window < c->local_window_max/2 && | ||
673 | c->local_consumed > 0) { | ||
674 | packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); | ||
675 | packet_put_int(c->remote_id); | ||
676 | packet_put_int(c->local_consumed); | ||
677 | packet_send(); | ||
678 | debug("channel %d: window %d sent adjust %d", | ||
679 | c->self, c->local_window, | ||
680 | c->local_consumed); | ||
681 | c->local_window += c->local_consumed; | ||
682 | c->local_consumed = 0; | ||
572 | } | 683 | } |
573 | return 1; | 684 | return 1; |
574 | } | 685 | } |
@@ -581,6 +692,15 @@ channel_post_open_1(Channel *c, fd_set * readset, fd_set * writeset) | |||
581 | } | 692 | } |
582 | 693 | ||
583 | void | 694 | void |
695 | channel_post_open_2(Channel *c, fd_set * readset, fd_set * writeset) | ||
696 | { | ||
697 | channel_handle_rfd(c, readset, writeset); | ||
698 | channel_handle_wfd(c, readset, writeset); | ||
699 | channel_handle_efd(c, readset, writeset); | ||
700 | channel_check_window(c, readset, writeset); | ||
701 | } | ||
702 | |||
703 | void | ||
584 | channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset) | 704 | channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset) |
585 | { | 705 | { |
586 | int len; | 706 | int len; |
@@ -596,6 +716,16 @@ channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset) | |||
596 | } | 716 | } |
597 | 717 | ||
598 | void | 718 | void |
719 | channel_handler_init_20(void) | ||
720 | { | ||
721 | channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_20; | ||
722 | channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; | ||
723 | |||
724 | channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_2; | ||
725 | channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; | ||
726 | } | ||
727 | |||
728 | void | ||
599 | channel_handler_init_13(void) | 729 | channel_handler_init_13(void) |
600 | { | 730 | { |
601 | channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_13; | 731 | channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_13; |
@@ -636,7 +766,9 @@ channel_handler_init(void) | |||
636 | channel_pre[i] = NULL; | 766 | channel_pre[i] = NULL; |
637 | channel_post[i] = NULL; | 767 | channel_post[i] = NULL; |
638 | } | 768 | } |
639 | if (compat13) | 769 | if (compat20) |
770 | channel_handler_init_20(); | ||
771 | else if (compat13) | ||
640 | channel_handler_init_13(); | 772 | channel_handler_init_13(); |
641 | else | 773 | else |
642 | channel_handler_init_15(); | 774 | channel_handler_init_15(); |
@@ -660,8 +792,7 @@ channel_handler(chan_fn *ftab[], fd_set * readset, fd_set * writeset) | |||
660 | if (ftab[c->type] == NULL) | 792 | if (ftab[c->type] == NULL) |
661 | continue; | 793 | continue; |
662 | (*ftab[c->type])(c, readset, writeset); | 794 | (*ftab[c->type])(c, readset, writeset); |
663 | if (!compat13) | 795 | chan_delete_if_full_closed(c); |
664 | chan_delete_if_full_closed(c); | ||
665 | } | 796 | } |
666 | } | 797 | } |
667 | 798 | ||
@@ -700,27 +831,39 @@ channel_output_poll() | |||
700 | c->istate != CHAN_INPUT_WAIT_DRAIN) | 831 | c->istate != CHAN_INPUT_WAIT_DRAIN) |
701 | continue; | 832 | continue; |
702 | } | 833 | } |
834 | if (compat20 && (c->flags & CHAN_CLOSE_SENT)) { | ||
835 | debug("channel: %d: no data after CLOSE", c->self); | ||
836 | continue; | ||
837 | } | ||
703 | 838 | ||
704 | /* Get the amount of buffered data for this channel. */ | 839 | /* Get the amount of buffered data for this channel. */ |
705 | len = buffer_len(&c->input); | 840 | len = buffer_len(&c->input); |
706 | if (len > 0) { | 841 | if (len > 0) { |
707 | /* Send some data for the other side over the secure connection. */ | 842 | /* Send some data for the other side over the secure connection. */ |
708 | if (packet_is_interactive()) { | 843 | if (compat20) { |
709 | if (len > 1024) | 844 | if (len > c->remote_window) |
710 | len = 512; | 845 | len = c->remote_window; |
846 | if (len > c->remote_maxpacket) | ||
847 | len = c->remote_maxpacket; | ||
711 | } else { | 848 | } else { |
712 | /* Keep the packets at reasonable size. */ | 849 | if (packet_is_interactive()) { |
713 | if (len > packet_get_maxsize()) | 850 | if (len > 1024) |
714 | len = packet_get_maxsize()/2; | 851 | len = 512; |
852 | } else { | ||
853 | /* Keep the packets at reasonable size. */ | ||
854 | if (len > packet_get_maxsize()/2) | ||
855 | len = packet_get_maxsize()/2; | ||
856 | } | ||
715 | } | 857 | } |
716 | if (len > 0) { | 858 | if (len > 0) { |
717 | packet_start(SSH_MSG_CHANNEL_DATA); | 859 | packet_start(compat20 ? |
860 | SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA); | ||
718 | packet_put_int(c->remote_id); | 861 | packet_put_int(c->remote_id); |
719 | packet_put_string(buffer_ptr(&c->input), len); | 862 | packet_put_string(buffer_ptr(&c->input), len); |
720 | packet_send(); | 863 | packet_send(); |
721 | buffer_consume(&c->input, len); | 864 | buffer_consume(&c->input, len); |
722 | c->remote_window -= len; | 865 | c->remote_window -= len; |
723 | debug("channel %d: send data len %d", c->self, len); | 866 | debug("channel %d: send data len %d", c->self, len); |
724 | } | 867 | } |
725 | } else if (c->istate == CHAN_INPUT_WAIT_DRAIN) { | 868 | } else if (c->istate == CHAN_INPUT_WAIT_DRAIN) { |
726 | if (compat13) | 869 | if (compat13) |
@@ -731,6 +874,23 @@ debug("channel %d: send data len %d", c->self, len); | |||
731 | */ | 874 | */ |
732 | chan_ibuf_empty(c); | 875 | chan_ibuf_empty(c); |
733 | } | 876 | } |
877 | /* Send extended data, i.e. stderr */ | ||
878 | if (compat20 && | ||
879 | c->remote_window > 0 && | ||
880 | (len = buffer_len(&c->extended)) > 0 && | ||
881 | c->extended_usage == CHAN_EXTENDED_READ) { | ||
882 | if (len > c->remote_window) | ||
883 | len = c->remote_window; | ||
884 | if (len > c->remote_maxpacket) | ||
885 | len = c->remote_maxpacket; | ||
886 | packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA); | ||
887 | packet_put_int(c->remote_id); | ||
888 | packet_put_int(SSH2_EXTENDED_DATA_STDERR); | ||
889 | packet_put_string(buffer_ptr(&c->extended), len); | ||
890 | packet_send(); | ||
891 | buffer_consume(&c->extended, len); | ||
892 | c->remote_window -= len; | ||
893 | } | ||
734 | } | 894 | } |
735 | } | 895 | } |
736 | 896 | ||
@@ -766,10 +926,63 @@ channel_input_data(int type, int plen) | |||
766 | /* Get the data. */ | 926 | /* Get the data. */ |
767 | data = packet_get_string(&data_len); | 927 | data = packet_get_string(&data_len); |
768 | 928 | ||
769 | packet_integrity_check(plen, 4 + 4 + data_len, SSH_MSG_CHANNEL_DATA); | 929 | if (compat20){ |
930 | if (data_len > c->local_maxpacket) { | ||
931 | log("channel %d: rcvd big packet %d, maxpack %d", | ||
932 | c->self, data_len, c->local_maxpacket); | ||
933 | } | ||
934 | if (data_len > c->local_window) { | ||
935 | log("channel %d: rcvd too much data %d, win %d", | ||
936 | c->self, data_len, c->local_window); | ||
937 | xfree(data); | ||
938 | return; | ||
939 | } | ||
940 | c->local_window -= data_len; | ||
941 | }else{ | ||
942 | packet_integrity_check(plen, 4 + 4 + data_len, type); | ||
943 | } | ||
770 | buffer_append(&c->output, data, data_len); | 944 | buffer_append(&c->output, data, data_len); |
771 | xfree(data); | 945 | xfree(data); |
772 | } | 946 | } |
947 | void | ||
948 | channel_input_extended_data(int type, int plen) | ||
949 | { | ||
950 | int id; | ||
951 | int tcode; | ||
952 | char *data; | ||
953 | unsigned int data_len; | ||
954 | Channel *c; | ||
955 | |||
956 | /* Get the channel number and verify it. */ | ||
957 | id = packet_get_int(); | ||
958 | c = channel_lookup(id); | ||
959 | |||
960 | if (c == NULL) | ||
961 | packet_disconnect("Received extended_data for bad channel %d.", id); | ||
962 | if (c->type != SSH_CHANNEL_OPEN) { | ||
963 | log("channel %d: ext data for non open", id); | ||
964 | return; | ||
965 | } | ||
966 | tcode = packet_get_int(); | ||
967 | if (c->efd == -1 || | ||
968 | c->extended_usage != CHAN_EXTENDED_WRITE || | ||
969 | tcode != SSH2_EXTENDED_DATA_STDERR) { | ||
970 | log("channel %d: bad ext data", c->self); | ||
971 | return; | ||
972 | } | ||
973 | data = packet_get_string(&data_len); | ||
974 | if (data_len > c->local_window) { | ||
975 | log("channel %d: rcvd too much extended_data %d, win %d", | ||
976 | c->self, data_len, c->local_window); | ||
977 | xfree(data); | ||
978 | return; | ||
979 | } | ||
980 | debug("channel %d: rcvd ext data %d", c->self, data_len); | ||
981 | c->local_window -= data_len; | ||
982 | buffer_append(&c->extended, data, data_len); | ||
983 | xfree(data); | ||
984 | } | ||
985 | |||
773 | 986 | ||
774 | /* | 987 | /* |
775 | * Returns true if no channel has too much buffered data, and false if one or | 988 | * Returns true if no channel has too much buffered data, and false if one or |
@@ -785,7 +998,7 @@ channel_not_very_much_buffered_data() | |||
785 | for (i = 0; i < channels_alloc; i++) { | 998 | for (i = 0; i < channels_alloc; i++) { |
786 | c = &channels[i]; | 999 | c = &channels[i]; |
787 | if (c->type == SSH_CHANNEL_OPEN) { | 1000 | if (c->type == SSH_CHANNEL_OPEN) { |
788 | if (buffer_len(&c->input) > packet_get_maxsize()) { | 1001 | if (!compat20 && buffer_len(&c->input) > packet_get_maxsize()) { |
789 | debug("channel %d: big input buffer %d", | 1002 | debug("channel %d: big input buffer %d", |
790 | c->self, buffer_len(&c->input)); | 1003 | c->self, buffer_len(&c->input)); |
791 | return 0; | 1004 | return 0; |
@@ -886,7 +1099,8 @@ channel_input_open_confirmation(int type, int plen) | |||
886 | int id, remote_id; | 1099 | int id, remote_id; |
887 | Channel *c; | 1100 | Channel *c; |
888 | 1101 | ||
889 | packet_integrity_check(plen, 4 + 4, type); | 1102 | if (!compat20) |
1103 | packet_integrity_check(plen, 4 + 4, type); | ||
890 | 1104 | ||
891 | id = packet_get_int(); | 1105 | id = packet_get_int(); |
892 | c = channel_lookup(id); | 1106 | c = channel_lookup(id); |
@@ -898,6 +1112,18 @@ channel_input_open_confirmation(int type, int plen) | |||
898 | /* Record the remote channel number and mark that the channel is now open. */ | 1112 | /* Record the remote channel number and mark that the channel is now open. */ |
899 | c->remote_id = remote_id; | 1113 | c->remote_id = remote_id; |
900 | c->type = SSH_CHANNEL_OPEN; | 1114 | c->type = SSH_CHANNEL_OPEN; |
1115 | |||
1116 | if (compat20) { | ||
1117 | c->remote_window = packet_get_int(); | ||
1118 | c->remote_maxpacket = packet_get_int(); | ||
1119 | if (c->cb_fn != NULL && c->cb_event == type) { | ||
1120 | debug("callback start"); | ||
1121 | c->cb_fn(c->self, c->cb_arg); | ||
1122 | debug("callback done"); | ||
1123 | } | ||
1124 | debug("channel %d: open confirm rwindow %d rmax %d", c->self, | ||
1125 | c->remote_window, c->remote_maxpacket); | ||
1126 | } | ||
901 | } | 1127 | } |
902 | 1128 | ||
903 | void | 1129 | void |
@@ -906,7 +1132,8 @@ channel_input_open_failure(int type, int plen) | |||
906 | int id; | 1132 | int id; |
907 | Channel *c; | 1133 | Channel *c; |
908 | 1134 | ||
909 | packet_integrity_check(plen, 4, type); | 1135 | if (!compat20) |
1136 | packet_integrity_check(plen, 4, type); | ||
910 | 1137 | ||
911 | id = packet_get_int(); | 1138 | id = packet_get_int(); |
912 | c = channel_lookup(id); | 1139 | c = channel_lookup(id); |
@@ -914,11 +1141,64 @@ channel_input_open_failure(int type, int plen) | |||
914 | if (c==NULL || c->type != SSH_CHANNEL_OPENING) | 1141 | if (c==NULL || c->type != SSH_CHANNEL_OPENING) |
915 | packet_disconnect("Received open failure for " | 1142 | packet_disconnect("Received open failure for " |
916 | "non-opening channel %d.", id); | 1143 | "non-opening channel %d.", id); |
917 | 1144 | if (compat20) { | |
1145 | int reason = packet_get_int(); | ||
1146 | char *msg = packet_get_string(NULL); | ||
1147 | log("channel_open_failure: %d: reason %d: %s", id, reason, msg); | ||
1148 | xfree(msg); | ||
1149 | } | ||
918 | /* Free the channel. This will also close the socket. */ | 1150 | /* Free the channel. This will also close the socket. */ |
919 | channel_free(id); | 1151 | channel_free(id); |
920 | } | 1152 | } |
921 | 1153 | ||
1154 | void | ||
1155 | channel_input_channel_request(int type, int plen) | ||
1156 | { | ||
1157 | int id; | ||
1158 | Channel *c; | ||
1159 | |||
1160 | id = packet_get_int(); | ||
1161 | c = channel_lookup(id); | ||
1162 | |||
1163 | if (c == NULL || | ||
1164 | (c->type != SSH_CHANNEL_OPEN && c->type != SSH_CHANNEL_LARVAL)) | ||
1165 | packet_disconnect("Received request for " | ||
1166 | "non-open channel %d.", id); | ||
1167 | if (c->cb_fn != NULL && c->cb_event == type) { | ||
1168 | debug("callback start"); | ||
1169 | c->cb_fn(c->self, c->cb_arg); | ||
1170 | debug("callback done"); | ||
1171 | } else { | ||
1172 | char *service = packet_get_string(NULL); | ||
1173 | debug("channel: %d rcvd request for %s", c->self, service); | ||
1174 | debug("cb_fn %p cb_event %d", c->cb_fn , c->cb_event); | ||
1175 | xfree(service); | ||
1176 | } | ||
1177 | } | ||
1178 | |||
1179 | void | ||
1180 | channel_input_window_adjust(int type, int plen) | ||
1181 | { | ||
1182 | Channel *c; | ||
1183 | int id, adjust; | ||
1184 | |||
1185 | if (!compat20) | ||
1186 | return; | ||
1187 | |||
1188 | /* Get the channel number and verify it. */ | ||
1189 | id = packet_get_int(); | ||
1190 | c = channel_lookup(id); | ||
1191 | |||
1192 | if (c == NULL || c->type != SSH_CHANNEL_OPEN) { | ||
1193 | log("Received window adjust for " | ||
1194 | "non-open channel %d.", id); | ||
1195 | return; | ||
1196 | } | ||
1197 | adjust = packet_get_int(); | ||
1198 | debug("channel %d: rcvd adjust %d", id, adjust); | ||
1199 | c->remote_window += adjust; | ||
1200 | } | ||
1201 | |||
922 | /* | 1202 | /* |
923 | * Stops listening for channels, and removes any unix domain sockets that we | 1203 | * Stops listening for channels, and removes any unix domain sockets that we |
924 | * might have. | 1204 | * might have. |
@@ -983,6 +1263,10 @@ channel_still_open() | |||
983 | case SSH_CHANNEL_CLOSED: | 1263 | case SSH_CHANNEL_CLOSED: |
984 | case SSH_CHANNEL_AUTH_SOCKET: | 1264 | case SSH_CHANNEL_AUTH_SOCKET: |
985 | continue; | 1265 | continue; |
1266 | case SSH_CHANNEL_LARVAL: | ||
1267 | if (!compat20) | ||
1268 | fatal("cannot happen: SSH_CHANNEL_LARVAL"); | ||
1269 | continue; | ||
986 | case SSH_CHANNEL_OPENING: | 1270 | case SSH_CHANNEL_OPENING: |
987 | case SSH_CHANNEL_OPEN: | 1271 | case SSH_CHANNEL_OPEN: |
988 | case SSH_CHANNEL_X11_OPEN: | 1272 | case SSH_CHANNEL_X11_OPEN: |
@@ -1024,6 +1308,7 @@ channel_open_message() | |||
1024 | case SSH_CHANNEL_CLOSED: | 1308 | case SSH_CHANNEL_CLOSED: |
1025 | case SSH_CHANNEL_AUTH_SOCKET: | 1309 | case SSH_CHANNEL_AUTH_SOCKET: |
1026 | continue; | 1310 | continue; |
1311 | case SSH_CHANNEL_LARVAL: | ||
1027 | case SSH_CHANNEL_OPENING: | 1312 | case SSH_CHANNEL_OPENING: |
1028 | case SSH_CHANNEL_OPEN: | 1313 | case SSH_CHANNEL_OPEN: |
1029 | case SSH_CHANNEL_X11_OPEN: | 1314 | case SSH_CHANNEL_X11_OPEN: |
@@ -1156,17 +1441,26 @@ channel_request_remote_forwarding(u_short listen_port, const char *host_to_conne | |||
1156 | num_permitted_opens++; | 1441 | num_permitted_opens++; |
1157 | 1442 | ||
1158 | /* Send the forward request to the remote side. */ | 1443 | /* Send the forward request to the remote side. */ |
1159 | packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); | 1444 | if (compat20) { |
1160 | packet_put_int(port_to_connect); | 1445 | const char *address_to_bind = "0.0.0.0"; |
1161 | packet_put_string(host_to_connect, strlen(host_to_connect)); | 1446 | packet_start(SSH2_MSG_GLOBAL_REQUEST); |
1162 | packet_put_int(listen_port); | 1447 | packet_put_cstring("tcpip-forward"); |
1163 | packet_send(); | 1448 | packet_put_char(0); /* boolean: want reply */ |
1164 | packet_write_wait(); | 1449 | packet_put_cstring(address_to_bind); |
1165 | /* | 1450 | packet_put_int(listen_port); |
1166 | * Wait for response from the remote side. It will send a disconnect | 1451 | } else { |
1167 | * message on failure, and we will never see it here. | 1452 | packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); |
1168 | */ | 1453 | packet_put_int(port_to_connect); |
1169 | packet_read_expect(&payload_len, SSH_SMSG_SUCCESS); | 1454 | packet_put_cstring(host_to_connect); |
1455 | packet_put_int(listen_port); | ||
1456 | packet_send(); | ||
1457 | packet_write_wait(); | ||
1458 | /* | ||
1459 | * Wait for response from the remote side. It will send a disconnect | ||
1460 | * message on failure, and we will never see it here. | ||
1461 | */ | ||
1462 | packet_read_expect(&payload_len, SSH_SMSG_SUCCESS); | ||
1463 | } | ||
1170 | } | 1464 | } |
1171 | 1465 | ||
1172 | /* | 1466 | /* |
@@ -1839,3 +2133,97 @@ auth_input_open_request(int type, int plen) | |||
1839 | packet_put_int(newch); | 2133 | packet_put_int(newch); |
1840 | packet_send(); | 2134 | packet_send(); |
1841 | } | 2135 | } |
2136 | |||
2137 | void | ||
2138 | channel_open(int id) | ||
2139 | { | ||
2140 | Channel *c = channel_lookup(id); | ||
2141 | if (c == NULL) { | ||
2142 | log("channel_open: %d: bad id", id); | ||
2143 | return; | ||
2144 | } | ||
2145 | packet_start(SSH2_MSG_CHANNEL_OPEN); | ||
2146 | packet_put_cstring(c->ctype); | ||
2147 | packet_put_int(c->self); | ||
2148 | packet_put_int(c->local_window); | ||
2149 | packet_put_int(c->local_maxpacket); | ||
2150 | packet_send(); | ||
2151 | debug("channel open %d", id); | ||
2152 | } | ||
2153 | void | ||
2154 | channel_request(int id, char *service, int wantconfirm) | ||
2155 | { | ||
2156 | channel_request_start(id, service, wantconfirm); | ||
2157 | packet_send(); | ||
2158 | debug("channel request %d: %s", id, service) ; | ||
2159 | } | ||
2160 | void | ||
2161 | channel_request_start(int id, char *service, int wantconfirm) | ||
2162 | { | ||
2163 | Channel *c = channel_lookup(id); | ||
2164 | if (c == NULL) { | ||
2165 | log("channel_request: %d: bad id", id); | ||
2166 | return; | ||
2167 | } | ||
2168 | packet_start(SSH2_MSG_CHANNEL_REQUEST); | ||
2169 | packet_put_int(c->remote_id); | ||
2170 | packet_put_cstring(service); | ||
2171 | packet_put_char(wantconfirm); | ||
2172 | } | ||
2173 | void | ||
2174 | channel_register_callback(int id, int mtype, channel_callback_fn *fn, void *arg) | ||
2175 | { | ||
2176 | Channel *c = channel_lookup(id); | ||
2177 | if (c == NULL) { | ||
2178 | log("channel_register_callback: %d: bad id", id); | ||
2179 | return; | ||
2180 | } | ||
2181 | c->cb_event = mtype; | ||
2182 | c->cb_fn = fn; | ||
2183 | c->cb_arg = arg; | ||
2184 | } | ||
2185 | void | ||
2186 | channel_register_cleanup(int id, channel_callback_fn *fn) | ||
2187 | { | ||
2188 | Channel *c = channel_lookup(id); | ||
2189 | if (c == NULL) { | ||
2190 | log("channel_register_cleanup: %d: bad id", id); | ||
2191 | return; | ||
2192 | } | ||
2193 | c->dettach_user = fn; | ||
2194 | } | ||
2195 | void | ||
2196 | channel_cancel_cleanup(int id) | ||
2197 | { | ||
2198 | Channel *c = channel_lookup(id); | ||
2199 | if (c == NULL) { | ||
2200 | log("channel_cancel_cleanup: %d: bad id", id); | ||
2201 | return; | ||
2202 | } | ||
2203 | c->dettach_user = NULL; | ||
2204 | } | ||
2205 | |||
2206 | void | ||
2207 | channel_set_fds(int id, int rfd, int wfd, int efd, int extusage) | ||
2208 | { | ||
2209 | Channel *c = channel_lookup(id); | ||
2210 | if (c == NULL || c->type != SSH_CHANNEL_LARVAL) | ||
2211 | fatal("channel_activate for non-larval channel %d.", id); | ||
2212 | if (rfd > channel_max_fd_value) | ||
2213 | channel_max_fd_value = rfd; | ||
2214 | if (wfd > channel_max_fd_value) | ||
2215 | channel_max_fd_value = wfd; | ||
2216 | if (efd > channel_max_fd_value) | ||
2217 | channel_max_fd_value = efd; | ||
2218 | c->type = SSH_CHANNEL_OPEN; | ||
2219 | c->rfd = rfd; | ||
2220 | c->wfd = wfd; | ||
2221 | c->efd = efd; | ||
2222 | c->extended_usage = extusage; | ||
2223 | /* XXX window size? */ | ||
2224 | c->local_window = c->local_window_max = c->local_maxpacket/2; | ||
2225 | packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); | ||
2226 | packet_put_int(c->remote_id); | ||
2227 | packet_put_int(c->local_window); | ||
2228 | packet_send(); | ||
2229 | } | ||
diff --git a/channels.h b/channels.h index 73ff5a595..33af09d9d 100644 --- a/channels.h +++ b/channels.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* RCSID("$Id: channels.h,v 1.5 2000/04/01 01:09:23 damien Exp $"); */ | 1 | /* RCSID("$Id: channels.h,v 1.6 2000/04/04 04:39:01 damien Exp $"); */ |
2 | 2 | ||
3 | #ifndef CHANNELS_H | 3 | #ifndef CHANNELS_H |
4 | #define CHANNELS_H | 4 | #define CHANNELS_H |
@@ -30,6 +30,7 @@ typedef struct Channel { | |||
30 | /* peer can be reached over encrypted connection, via packet-sent */ | 30 | /* peer can be reached over encrypted connection, via packet-sent */ |
31 | int istate; /* input from channel (state of receive half) */ | 31 | int istate; /* input from channel (state of receive half) */ |
32 | int ostate; /* output to channel (state of transmit half) */ | 32 | int ostate; /* output to channel (state of transmit half) */ |
33 | int flags; /* close sent/rcvd */ | ||
33 | int rfd; /* read fd */ | 34 | int rfd; /* read fd */ |
34 | int wfd; /* write fd */ | 35 | int wfd; /* write fd */ |
35 | int efd; /* extended fd */ | 36 | int efd; /* extended fd */ |
@@ -66,21 +67,30 @@ typedef struct Channel { | |||
66 | #define CHAN_EXTENDED_READ 1 | 67 | #define CHAN_EXTENDED_READ 1 |
67 | #define CHAN_EXTENDED_WRITE 2 | 68 | #define CHAN_EXTENDED_WRITE 2 |
68 | 69 | ||
70 | void channel_set_fds(int id, int rfd, int wfd, int efd, int extusage); | ||
69 | void channel_open(int id); | 71 | void channel_open(int id); |
72 | void channel_request(int id, char *service, int wantconfirm); | ||
73 | void channel_request_start(int id, char *service, int wantconfirm); | ||
74 | void channel_register_callback(int id, int mtype, channel_callback_fn *fn, void *arg); | ||
75 | void channel_register_cleanup(int id, channel_callback_fn *fn); | ||
76 | void channel_cancel_cleanup(int id); | ||
70 | Channel *channel_lookup(int id); | 77 | Channel *channel_lookup(int id); |
71 | 78 | ||
72 | int | 79 | int |
73 | channel_new(char *ctype, int type, int rfd, int wfd, int efd, | 80 | channel_new(char *ctype, int type, int rfd, int wfd, int efd, |
74 | int window, int maxpack, int extended_usage, char *remote_name); | 81 | int window, int maxpack, int extended_usage, char *remote_name); |
75 | 82 | ||
83 | void channel_input_channel_request(int type, int plen); | ||
76 | void channel_input_close(int type, int plen); | 84 | void channel_input_close(int type, int plen); |
77 | void channel_input_close_confirmation(int type, int plen); | 85 | void channel_input_close_confirmation(int type, int plen); |
78 | void channel_input_data(int type, int plen); | 86 | void channel_input_data(int type, int plen); |
87 | void channel_input_extended_data(int type, int plen); | ||
79 | void channel_input_ieof(int type, int plen); | 88 | void channel_input_ieof(int type, int plen); |
80 | void channel_input_oclose(int type, int plen); | 89 | void channel_input_oclose(int type, int plen); |
81 | void channel_input_open_confirmation(int type, int plen); | 90 | void channel_input_open_confirmation(int type, int plen); |
82 | void channel_input_open_failure(int type, int plen); | 91 | void channel_input_open_failure(int type, int plen); |
83 | void channel_input_port_open(int type, int plen); | 92 | void channel_input_port_open(int type, int plen); |
93 | void channel_input_window_adjust(int type, int plen); | ||
84 | void channel_input_open(int type, int plen); | 94 | void channel_input_open(int type, int plen); |
85 | 95 | ||
86 | /* Sets specific protocol options. */ | 96 | /* Sets specific protocol options. */ |
@@ -218,4 +228,7 @@ void auth_input_request_forwarding(struct passwd * pw); | |||
218 | /* This is called to process an SSH_SMSG_AGENT_OPEN message. */ | 228 | /* This is called to process an SSH_SMSG_AGENT_OPEN message. */ |
219 | void auth_input_open_request(int type, int plen); | 229 | void auth_input_open_request(int type, int plen); |
220 | 230 | ||
231 | /* XXX */ | ||
232 | int channel_connect_to(const char *host, u_short host_port); | ||
233 | |||
221 | #endif | 234 | #endif |
@@ -28,15 +28,46 @@ | |||
28 | */ | 28 | */ |
29 | 29 | ||
30 | #include "includes.h" | 30 | #include "includes.h" |
31 | RCSID("$Id: compat.c,v 1.3 1999/11/25 00:54:59 damien Exp $"); | 31 | RCSID("$Id: compat.c,v 1.4 2000/04/04 04:39:01 damien Exp $"); |
32 | 32 | ||
33 | #include "ssh.h" | 33 | #include "ssh.h" |
34 | #include "packet.h" | ||
34 | 35 | ||
35 | int compat13 = 0; | 36 | int compat13 = 0; |
37 | int compat20 = 0; | ||
38 | int datafellows = 0; | ||
36 | 39 | ||
37 | void | 40 | void |
41 | enable_compat20(void) | ||
42 | { | ||
43 | fatal("protocol 2.0 not implemented"); | ||
44 | } | ||
45 | void | ||
38 | enable_compat13(void) | 46 | enable_compat13(void) |
39 | { | 47 | { |
40 | verbose("Enabling compatibility mode for protocol 1.3"); | 48 | verbose("Enabling compatibility mode for protocol 1.3"); |
41 | compat13 = 1; | 49 | compat13 = 1; |
42 | } | 50 | } |
51 | /* datafellows bug compatibility */ | ||
52 | void | ||
53 | compat_datafellows(const char *version) | ||
54 | { | ||
55 | int i; | ||
56 | size_t len; | ||
57 | static const char *check[] = { | ||
58 | "2.0.1", | ||
59 | "2.1.0.beta.9", | ||
60 | "2.1.0.pre.3", | ||
61 | "2.1.0.public.beta.1", | ||
62 | NULL | ||
63 | }; | ||
64 | for (i = 0; check[i]; i++) { | ||
65 | len = strlen(check[i]); | ||
66 | if (strlen(version) >= len && | ||
67 | (strncmp(version, check[i], len) == 0)) { | ||
68 | log("datafellows: %.200s", version); | ||
69 | datafellows = 1; | ||
70 | return; | ||
71 | } | ||
72 | } | ||
73 | } | ||
@@ -26,10 +26,14 @@ | |||
26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 | */ | 28 | */ |
29 | /* RCSID("$Id: compat.h,v 1.3 1999/11/25 00:54:59 damien Exp $"); */ | 29 | /* RCSID("$Id: compat.h,v 1.4 2000/04/04 04:39:01 damien Exp $"); */ |
30 | 30 | ||
31 | #ifndef COMPAT_H | 31 | #ifndef COMPAT_H |
32 | #define COMPAT_H | 32 | #define COMPAT_H |
33 | void enable_compat13(void); | 33 | void enable_compat13(void); |
34 | void enable_compat20(void); | ||
35 | void compat_datafellows(const char *s); | ||
34 | extern int compat13; | 36 | extern int compat13; |
37 | extern int compat20; | ||
38 | extern int datafellows; | ||
35 | #endif | 39 | #endif |
@@ -0,0 +1,309 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | ||
3 | * | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * 1. Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * 2. Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * 3. All advertising materials mentioning features or use of this software | ||
13 | * must display the following acknowledgement: | ||
14 | * This product includes software developed by Markus Friedl. | ||
15 | * 4. The name of the author may not be used to endorse or promote products | ||
16 | * derived from this software without specific prior written permission. | ||
17 | * | ||
18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||
19 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
21 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
23 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | |||
30 | #include "includes.h" | ||
31 | RCSID("$Id: dsa.c,v 1.1 2000/04/03 20:06:14 markus Exp $"); | ||
32 | |||
33 | #include "ssh.h" | ||
34 | #include "xmalloc.h" | ||
35 | #include "buffer.h" | ||
36 | #include "bufaux.h" | ||
37 | #include "compat.h" | ||
38 | |||
39 | #if HAVE_OPENSSL | ||
40 | # include <openssl/bn.h> | ||
41 | # include <openssl/dh.h> | ||
42 | # include <openssl/rsa.h> | ||
43 | # include <openssl/dsa.h> | ||
44 | # include <openssl/evp.h> | ||
45 | # include <openssl/bio.h> | ||
46 | # include <openssl/pem.h> | ||
47 | #endif /* HAVE_OPENSSL */ | ||
48 | #if HAVE_SSL | ||
49 | # include <ssl/bn.h> | ||
50 | # include <ssl/dh.h> | ||
51 | # include <ssl/rsa.h> | ||
52 | # include <ssl/dsa.h> | ||
53 | # include <ssl/evp.h> | ||
54 | # include <ssl/bio.h> | ||
55 | # include <ssl/pem.h> | ||
56 | #endif /* HAVE_SSL */ | ||
57 | |||
58 | #include <ssl/hmac.h> | ||
59 | #include "kex.h" | ||
60 | #include "key.h" | ||
61 | |||
62 | #define INTBLOB_LEN 20 | ||
63 | #define SIGBLOB_LEN (2*INTBLOB_LEN) | ||
64 | |||
65 | Key * | ||
66 | dsa_serverkey_from_blob( | ||
67 | char *serverhostkey, int serverhostkeylen) | ||
68 | { | ||
69 | Buffer b; | ||
70 | char *ktype; | ||
71 | int rlen; | ||
72 | DSA *dsa; | ||
73 | Key *key; | ||
74 | |||
75 | /* fetch & parse DSA/DSS pubkey */ | ||
76 | key = key_new(KEY_DSA); | ||
77 | dsa = key->dsa; | ||
78 | buffer_init(&b); | ||
79 | buffer_append(&b, serverhostkey, serverhostkeylen); | ||
80 | ktype = buffer_get_string(&b, NULL); | ||
81 | if (strcmp(KEX_DSS, ktype) != 0) { | ||
82 | log("dsa_serverkey_from_blob: cannot handle type %s", ktype); | ||
83 | key_free(key); | ||
84 | return NULL; | ||
85 | } | ||
86 | buffer_get_bignum2(&b, dsa->p); | ||
87 | buffer_get_bignum2(&b, dsa->q); | ||
88 | buffer_get_bignum2(&b, dsa->g); | ||
89 | buffer_get_bignum2(&b, dsa->pub_key); | ||
90 | rlen = buffer_len(&b); | ||
91 | if(rlen != 0) | ||
92 | log("dsa_serverkey_from_blob: remaining bytes in serverhostkey %d", rlen); | ||
93 | buffer_free(&b); | ||
94 | |||
95 | log("keytype %s", ktype); | ||
96 | #ifdef DEBUG_DSS | ||
97 | DSA_print_fp(stderr, dsa, 8); | ||
98 | #endif | ||
99 | return key; | ||
100 | } | ||
101 | DSA * | ||
102 | dsa_load_private(char *filename) | ||
103 | { | ||
104 | DSA *dsa; | ||
105 | BIO *in; | ||
106 | |||
107 | in = BIO_new(BIO_s_file()); | ||
108 | if (in == NULL) | ||
109 | fatal("BIO_new failed"); | ||
110 | if (BIO_read_filename(in, filename) <= 0) | ||
111 | fatal("BIO_read failed %s: %s", filename, strerror(errno)); | ||
112 | fprintf(stderr, "read DSA private key\n"); | ||
113 | dsa = PEM_read_bio_DSAPrivateKey(in,NULL,NULL,NULL); | ||
114 | if (dsa == NULL) | ||
115 | fatal("PEM_read_bio_DSAPrivateKey failed %s", filename); | ||
116 | BIO_free(in); | ||
117 | return dsa; | ||
118 | } | ||
119 | Key * | ||
120 | dsa_get_serverkey(char *filename) | ||
121 | { | ||
122 | Key *k = key_new(KEY_EMPTY); | ||
123 | k->type = KEY_DSA; | ||
124 | k->dsa = dsa_load_private(filename); | ||
125 | #ifdef DEBUG_DSS | ||
126 | DSA_print_fp(stderr, dsa, 8); | ||
127 | #endif | ||
128 | return k; | ||
129 | } | ||
130 | int | ||
131 | dsa_make_serverkey_blob(Key *key, unsigned char **blobp, unsigned int *lenp) | ||
132 | { | ||
133 | Buffer b; | ||
134 | int len; | ||
135 | unsigned char *buf; | ||
136 | |||
137 | if (key == NULL || key->type != KEY_DSA) | ||
138 | return 0; | ||
139 | buffer_init(&b); | ||
140 | buffer_put_cstring(&b, KEX_DSS); | ||
141 | buffer_put_bignum2(&b, key->dsa->p); | ||
142 | buffer_put_bignum2(&b, key->dsa->q); | ||
143 | buffer_put_bignum2(&b, key->dsa->g); | ||
144 | buffer_put_bignum2(&b, key->dsa->pub_key); | ||
145 | len = buffer_len(&b); | ||
146 | buf = xmalloc(len); | ||
147 | memcpy(buf, buffer_ptr(&b), len); | ||
148 | memset(buffer_ptr(&b), 0, len); | ||
149 | buffer_free(&b); | ||
150 | if (lenp != NULL) | ||
151 | *lenp = len; | ||
152 | if (blobp != NULL) | ||
153 | *blobp = buf; | ||
154 | return len; | ||
155 | } | ||
156 | int | ||
157 | dsa_sign( | ||
158 | Key *key, | ||
159 | unsigned char **sigp, int *lenp, | ||
160 | unsigned char *hash, int hlen) | ||
161 | { | ||
162 | unsigned char *digest; | ||
163 | unsigned char *ret; | ||
164 | DSA_SIG *sig; | ||
165 | EVP_MD *evp_md = EVP_sha1(); | ||
166 | EVP_MD_CTX md; | ||
167 | unsigned int rlen; | ||
168 | unsigned int slen; | ||
169 | unsigned int len; | ||
170 | unsigned char sigblob[SIGBLOB_LEN]; | ||
171 | Buffer b; | ||
172 | |||
173 | if (key == NULL || key->type != KEY_DSA || key->dsa == NULL) { | ||
174 | log("dsa_sign: no DSA key"); | ||
175 | return -1; | ||
176 | } | ||
177 | digest = xmalloc(evp_md->md_size); | ||
178 | EVP_DigestInit(&md, evp_md); | ||
179 | EVP_DigestUpdate(&md, hash, hlen); | ||
180 | EVP_DigestFinal(&md, digest, NULL); | ||
181 | |||
182 | sig = DSA_do_sign(digest, evp_md->md_size, key->dsa); | ||
183 | |||
184 | rlen = BN_num_bytes(sig->r); | ||
185 | slen = BN_num_bytes(sig->s); | ||
186 | if (rlen > INTBLOB_LEN || slen > INTBLOB_LEN) { | ||
187 | log("bad sig size %d %d", rlen, slen); | ||
188 | DSA_SIG_free(sig); | ||
189 | return -1; | ||
190 | } | ||
191 | log("sig size %d %d", rlen, slen); | ||
192 | |||
193 | memset(sigblob, 0, SIGBLOB_LEN); | ||
194 | BN_bn2bin(sig->r, sigblob+ SIGBLOB_LEN - INTBLOB_LEN - rlen); | ||
195 | BN_bn2bin(sig->s, sigblob+ SIGBLOB_LEN - slen); | ||
196 | DSA_SIG_free(sig); | ||
197 | |||
198 | if (datafellows) { | ||
199 | log("datafellows"); | ||
200 | ret = xmalloc(SIGBLOB_LEN); | ||
201 | memcpy(ret, sigblob, SIGBLOB_LEN); | ||
202 | if (lenp != NULL) | ||
203 | *lenp = SIGBLOB_LEN; | ||
204 | if (sigp != NULL) | ||
205 | *sigp = ret; | ||
206 | } else { | ||
207 | /* ietf-drafts */ | ||
208 | buffer_init(&b); | ||
209 | buffer_put_cstring(&b, KEX_DSS); | ||
210 | buffer_put_string(&b, sigblob, SIGBLOB_LEN); | ||
211 | len = buffer_len(&b); | ||
212 | ret = xmalloc(len); | ||
213 | memcpy(ret, buffer_ptr(&b), len); | ||
214 | buffer_free(&b); | ||
215 | if (lenp != NULL) | ||
216 | *lenp = len; | ||
217 | if (sigp != NULL) | ||
218 | *sigp = ret; | ||
219 | } | ||
220 | return 0; | ||
221 | } | ||
222 | int | ||
223 | dsa_verify( | ||
224 | Key *key, | ||
225 | unsigned char *signature, int signaturelen, | ||
226 | unsigned char *hash, int hlen) | ||
227 | { | ||
228 | Buffer b; | ||
229 | unsigned char *digest; | ||
230 | DSA_SIG *sig; | ||
231 | EVP_MD *evp_md = EVP_sha1(); | ||
232 | EVP_MD_CTX md; | ||
233 | char *ktype; | ||
234 | unsigned char *sigblob; | ||
235 | char *txt; | ||
236 | unsigned int len; | ||
237 | int rlen; | ||
238 | int ret; | ||
239 | |||
240 | if (key == NULL || key->type != KEY_DSA || key->dsa == NULL) { | ||
241 | log("dsa_verify: no DSA key"); | ||
242 | return -1; | ||
243 | } | ||
244 | |||
245 | if (datafellows && signaturelen != SIGBLOB_LEN) { | ||
246 | log("heh? datafellows ssh2 complies with ietf-drafts????"); | ||
247 | datafellows = 0; | ||
248 | } | ||
249 | |||
250 | log("len %d datafellows %d", signaturelen, datafellows); | ||
251 | |||
252 | /* fetch signature */ | ||
253 | if (datafellows) { | ||
254 | sigblob = signature; | ||
255 | len = signaturelen; | ||
256 | } else { | ||
257 | /* ietf-drafts */ | ||
258 | buffer_init(&b); | ||
259 | buffer_append(&b, (char *) signature, signaturelen); | ||
260 | ktype = buffer_get_string(&b, NULL); | ||
261 | sigblob = (unsigned char *)buffer_get_string(&b, &len); | ||
262 | rlen = buffer_len(&b); | ||
263 | if(rlen != 0) | ||
264 | log("remaining bytes in signature %d", rlen); | ||
265 | buffer_free(&b); | ||
266 | } | ||
267 | |||
268 | if (len != SIGBLOB_LEN) { | ||
269 | fatal("bad sigbloblen %d != SIGBLOB_LEN", len); | ||
270 | } | ||
271 | |||
272 | /* parse signature */ | ||
273 | sig = DSA_SIG_new(); | ||
274 | sig->r = BN_new(); | ||
275 | sig->s = BN_new(); | ||
276 | BN_bin2bn(sigblob, INTBLOB_LEN, sig->r); | ||
277 | BN_bin2bn(sigblob+ INTBLOB_LEN, INTBLOB_LEN, sig->s); | ||
278 | if (!datafellows) { | ||
279 | memset(sigblob, 0, len); | ||
280 | xfree(sigblob); | ||
281 | } | ||
282 | |||
283 | /* sha1 the signed data (== session_id == hash) */ | ||
284 | digest = xmalloc(evp_md->md_size); | ||
285 | EVP_DigestInit(&md, evp_md); | ||
286 | EVP_DigestUpdate(&md, hash, hlen); | ||
287 | EVP_DigestFinal(&md, digest, NULL); | ||
288 | |||
289 | ret = DSA_do_verify(digest, evp_md->md_size, sig, key->dsa); | ||
290 | |||
291 | memset(digest, 0, evp_md->md_size); | ||
292 | xfree(digest); | ||
293 | DSA_SIG_free(sig); | ||
294 | |||
295 | switch (ret) { | ||
296 | case 1: | ||
297 | txt = "correct"; | ||
298 | break; | ||
299 | case 0: | ||
300 | txt = "incorrect"; | ||
301 | break; | ||
302 | case -1: | ||
303 | default: | ||
304 | txt = "error"; | ||
305 | break; | ||
306 | } | ||
307 | log("dsa_verify: signature %s", txt); | ||
308 | return ret; | ||
309 | } | ||
@@ -0,0 +1,20 @@ | |||
1 | #ifndef DSA_H | ||
2 | #define DSA_H | ||
3 | |||
4 | Key *dsa_serverkey_from_blob(char *serverhostkey, int serverhostkeylen); | ||
5 | Key *dsa_get_serverkey(char *filename); | ||
6 | int dsa_make_serverkey_blob(Key *key, unsigned char **blobp, unsigned int *lenp); | ||
7 | |||
8 | int | ||
9 | dsa_sign( | ||
10 | Key *key, | ||
11 | unsigned char **sigp, int *lenp, | ||
12 | unsigned char *hash, int hlen); | ||
13 | |||
14 | int | ||
15 | dsa_verify( | ||
16 | Key *key, | ||
17 | unsigned char *signature, int signaturelen, | ||
18 | unsigned char *hash, int hlen); | ||
19 | |||
20 | #endif | ||
@@ -0,0 +1,64 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | ||
3 | * | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * 1. Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * 2. Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * 3. All advertising materials mentioning features or use of this software | ||
13 | * must display the following acknowledgement: | ||
14 | * This product includes software developed by Markus Friedl. | ||
15 | * 4. The name of the author may not be used to endorse or promote products | ||
16 | * derived from this software without specific prior written permission. | ||
17 | * | ||
18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||
19 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
21 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
23 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | |||
30 | #include "includes.h" | ||
31 | RCSID("$Id: hmac.c,v 1.1 2000/04/03 20:06:15 markus Exp $"); | ||
32 | |||
33 | #include "xmalloc.h" | ||
34 | #include "ssh.h" | ||
35 | #include "getput.h" | ||
36 | |||
37 | #if HAVE_OPENSSL | ||
38 | # include <openssl/hmac.h> | ||
39 | #endif /* HAVE_OPENSSL */ | ||
40 | #if HAVE_SSL | ||
41 | # include <ssl/hmac.h> | ||
42 | #endif /* HAVE_SSL */ | ||
43 | |||
44 | unsigned char * | ||
45 | hmac( | ||
46 | EVP_MD *evp_md, | ||
47 | unsigned int seqno, | ||
48 | unsigned char *data, int datalen, | ||
49 | unsigned char *key, int keylen) | ||
50 | { | ||
51 | HMAC_CTX c; | ||
52 | static unsigned char m[EVP_MAX_MD_SIZE]; | ||
53 | unsigned char b[4]; | ||
54 | |||
55 | if (key == NULL) | ||
56 | fatal("hmac: no key"); | ||
57 | HMAC_Init(&c, key, keylen, evp_md); | ||
58 | PUT_32BIT(b, seqno); | ||
59 | HMAC_Update(&c, b, sizeof b); | ||
60 | HMAC_Update(&c, data, datalen); | ||
61 | HMAC_Final(&c, m, NULL); | ||
62 | HMAC_cleanup(&c); | ||
63 | return(m); | ||
64 | } | ||
@@ -0,0 +1,11 @@ | |||
1 | #ifndef HMAC_H | ||
2 | #define HMAC_H | ||
3 | |||
4 | unsigned char * | ||
5 | hmac( | ||
6 | EVP_MD *evp_md, | ||
7 | unsigned int seqno, | ||
8 | unsigned char *data, int datalen, | ||
9 | unsigned char *key, int len); | ||
10 | |||
11 | #endif | ||
@@ -0,0 +1,407 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | ||
3 | * | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * 1. Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * 2. Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * 3. All advertising materials mentioning features or use of this software | ||
13 | * must display the following acknowledgement: | ||
14 | * This product includes software developed by Markus Friedl. | ||
15 | * 4. The name of the author may not be used to endorse or promote products | ||
16 | * derived from this software without specific prior written permission. | ||
17 | * | ||
18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||
19 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
21 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
23 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | |||
30 | #include "includes.h" | ||
31 | RCSID("$Id: kex.c,v 1.1 2000/04/04 04:39:02 damien Exp $"); | ||
32 | |||
33 | #include "ssh.h" | ||
34 | #include "ssh2.h" | ||
35 | #include "xmalloc.h" | ||
36 | #include "buffer.h" | ||
37 | #include "bufaux.h" | ||
38 | #include "cipher.h" | ||
39 | #include "compat.h" | ||
40 | |||
41 | #if HAVE_OPENSSL | ||
42 | # include <openssl/bn.h> | ||
43 | # include <openssl/dh.h> | ||
44 | # include <openssl/crypto.h> | ||
45 | # include <openssl/bio.h> | ||
46 | # include <openssl/bn.h> | ||
47 | # include <openssl/dh.h> | ||
48 | # include <openssl/pem.h> | ||
49 | #endif /* HAVE_OPENSSL */ | ||
50 | #if HAVE_SSL | ||
51 | # include <ssl/bn.h> | ||
52 | # include <ssl/dh.h> | ||
53 | # include <ssl/crypto.h> | ||
54 | # include <ssl/bio.h> | ||
55 | # include <ssl/bn.h> | ||
56 | # include <ssl/dh.h> | ||
57 | # include <ssl/pem.h> | ||
58 | #endif /* HAVE_SSL */ | ||
59 | |||
60 | #include "entropy.h" | ||
61 | #include "kex.h" | ||
62 | |||
63 | Buffer * | ||
64 | kex_init(char *myproposal[PROPOSAL_MAX]) | ||
65 | { | ||
66 | char c = 0; | ||
67 | unsigned char cookie[16]; | ||
68 | u_int32_t rand = 0; | ||
69 | int i; | ||
70 | Buffer *ki = xmalloc(sizeof(*ki)); | ||
71 | for (i = 0; i < 16; i++) { | ||
72 | if (i % 4 == 0) | ||
73 | rand = arc4random(); | ||
74 | cookie[i] = rand & 0xff; | ||
75 | rand >>= 8; | ||
76 | } | ||
77 | buffer_init(ki); | ||
78 | buffer_append(ki, (char *)cookie, sizeof cookie); | ||
79 | for (i = 0; i < PROPOSAL_MAX; i++) | ||
80 | buffer_put_cstring(ki, myproposal[i]); | ||
81 | buffer_append(ki, &c, 1); /* boolean first_kex_packet_follows */ | ||
82 | buffer_put_int(ki, 0); /* uint32 0 (reserved for future extension) */ | ||
83 | return ki; | ||
84 | } | ||
85 | |||
86 | /* diffie-hellman-group1-sha1 */ | ||
87 | |||
88 | DH * | ||
89 | new_dh_group1() | ||
90 | { | ||
91 | static char *group1 = | ||
92 | "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1" | ||
93 | "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD" | ||
94 | "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245" | ||
95 | "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" | ||
96 | "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381" | ||
97 | "FFFFFFFF" "FFFFFFFF"; | ||
98 | DH *dh; | ||
99 | int ret; | ||
100 | dh = DH_new(); | ||
101 | if(dh == NULL) | ||
102 | fatal("DH_new"); | ||
103 | ret = BN_hex2bn(&dh->p,group1); | ||
104 | if(ret<0) | ||
105 | fatal("BN_hex2bn"); | ||
106 | dh->g = BN_new(); | ||
107 | if(dh->g == NULL) | ||
108 | fatal("DH_new g"); | ||
109 | BN_set_word(dh->g,2); | ||
110 | |||
111 | seed_rng(); | ||
112 | if (DH_generate_key(dh) == 0) | ||
113 | fatal("DH_generate_key"); | ||
114 | seed_rng(); | ||
115 | |||
116 | return dh; | ||
117 | } | ||
118 | |||
119 | void | ||
120 | bignum_print(BIGNUM *b) | ||
121 | { | ||
122 | BN_print_fp(stderr,b); | ||
123 | } | ||
124 | |||
125 | void | ||
126 | dump_digest(unsigned char *digest, int len) | ||
127 | { | ||
128 | int i; | ||
129 | for (i = 0; i< len; i++){ | ||
130 | fprintf(stderr, "%02x", digest[i]); | ||
131 | if(i%2!=0) | ||
132 | fprintf(stderr, " "); | ||
133 | } | ||
134 | fprintf(stderr, "\n"); | ||
135 | } | ||
136 | |||
137 | unsigned char * | ||
138 | kex_hash( | ||
139 | char *client_version_string, | ||
140 | char *server_version_string, | ||
141 | char *ckexinit, int ckexinitlen, | ||
142 | char *skexinit, int skexinitlen, | ||
143 | char *serverhostkeyblob, int sbloblen, | ||
144 | BIGNUM *client_dh_pub, | ||
145 | BIGNUM *server_dh_pub, | ||
146 | BIGNUM *shared_secret) | ||
147 | { | ||
148 | Buffer b; | ||
149 | static unsigned char digest[EVP_MAX_MD_SIZE]; | ||
150 | EVP_MD *evp_md = EVP_sha1(); | ||
151 | EVP_MD_CTX md; | ||
152 | |||
153 | buffer_init(&b); | ||
154 | buffer_put_string(&b, client_version_string, strlen(client_version_string)); | ||
155 | buffer_put_string(&b, server_version_string, strlen(server_version_string)); | ||
156 | |||
157 | /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ | ||
158 | buffer_put_int(&b, ckexinitlen+1); | ||
159 | buffer_put_char(&b, SSH2_MSG_KEXINIT); | ||
160 | buffer_append(&b, ckexinit, ckexinitlen); | ||
161 | buffer_put_int(&b, skexinitlen+1); | ||
162 | buffer_put_char(&b, SSH2_MSG_KEXINIT); | ||
163 | buffer_append(&b, skexinit, skexinitlen); | ||
164 | |||
165 | buffer_put_string(&b, serverhostkeyblob, sbloblen); | ||
166 | buffer_put_bignum2(&b, client_dh_pub); | ||
167 | buffer_put_bignum2(&b, server_dh_pub); | ||
168 | buffer_put_bignum2(&b, shared_secret); | ||
169 | |||
170 | #ifdef DEBUG_KEX | ||
171 | buffer_dump(&b); | ||
172 | #endif | ||
173 | |||
174 | EVP_DigestInit(&md, evp_md); | ||
175 | EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); | ||
176 | EVP_DigestFinal(&md, digest, NULL); | ||
177 | |||
178 | buffer_free(&b); | ||
179 | |||
180 | #ifdef DEBUG_KEX | ||
181 | dump_digest(digest, evp_md->md_size); | ||
182 | #endif | ||
183 | return digest; | ||
184 | } | ||
185 | |||
186 | unsigned char * | ||
187 | derive_key(int id, int need, char unsigned *hash, BIGNUM *shared_secret) | ||
188 | { | ||
189 | Buffer b; | ||
190 | EVP_MD *evp_md = EVP_sha1(); | ||
191 | EVP_MD_CTX md; | ||
192 | char c = id; | ||
193 | int have; | ||
194 | int mdsz = evp_md->md_size; | ||
195 | unsigned char *digest = xmalloc(((need+mdsz-1)/mdsz)*mdsz); | ||
196 | |||
197 | buffer_init(&b); | ||
198 | buffer_put_bignum2(&b, shared_secret); | ||
199 | |||
200 | EVP_DigestInit(&md, evp_md); | ||
201 | EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); /* shared_secret K */ | ||
202 | EVP_DigestUpdate(&md, hash, mdsz); /* transport-06 */ | ||
203 | EVP_DigestUpdate(&md, &c, 1); /* key id */ | ||
204 | EVP_DigestUpdate(&md, hash, mdsz); /* session id */ | ||
205 | EVP_DigestFinal(&md, digest, NULL); | ||
206 | |||
207 | /* expand */ | ||
208 | for (have = mdsz; need > have; have += mdsz) { | ||
209 | EVP_DigestInit(&md, evp_md); | ||
210 | EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); | ||
211 | EVP_DigestUpdate(&md, hash, mdsz); | ||
212 | EVP_DigestUpdate(&md, digest, have); | ||
213 | EVP_DigestFinal(&md, digest + have, NULL); | ||
214 | } | ||
215 | buffer_free(&b); | ||
216 | #ifdef DEBUG_KEX | ||
217 | fprintf(stderr, "Digest '%c'== ", c); | ||
218 | dump_digest(digest, need); | ||
219 | #endif | ||
220 | return digest; | ||
221 | } | ||
222 | |||
223 | #define NKEYS 6 | ||
224 | |||
225 | #define MAX_PROP 20 | ||
226 | #define SEP "," | ||
227 | |||
228 | char * | ||
229 | get_match(char *client, char *server) | ||
230 | { | ||
231 | char *sproposals[MAX_PROP]; | ||
232 | char *p; | ||
233 | int i, j, nproposals; | ||
234 | |||
235 | for ((p = strtok(server, SEP)), i=0; p; (p = strtok(NULL, SEP)), i++) { | ||
236 | if (i < MAX_PROP) | ||
237 | sproposals[i] = p; | ||
238 | else | ||
239 | break; | ||
240 | } | ||
241 | nproposals = i; | ||
242 | |||
243 | for ((p = strtok(client, SEP)), i=0; p; (p = strtok(NULL, SEP)), i++) { | ||
244 | for (j = 0; j < nproposals; j++) | ||
245 | if (strcmp(p, sproposals[j]) == 0) | ||
246 | return xstrdup(p); | ||
247 | } | ||
248 | return NULL; | ||
249 | } | ||
250 | void | ||
251 | choose_enc(Enc *enc, char *client, char *server) | ||
252 | { | ||
253 | char *name = get_match(client, server); | ||
254 | if (name == NULL) | ||
255 | fatal("no matching cipher found: client %s server %s", client, server); | ||
256 | enc->type = cipher_number(name); | ||
257 | |||
258 | switch (enc->type) { | ||
259 | case SSH_CIPHER_3DES_CBC: | ||
260 | enc->key_len = 24; | ||
261 | enc->iv_len = 8; | ||
262 | enc->block_size = 8; | ||
263 | break; | ||
264 | case SSH_CIPHER_BLOWFISH_CBC: | ||
265 | case SSH_CIPHER_CAST128_CBC: | ||
266 | enc->key_len = 16; | ||
267 | enc->iv_len = 8; | ||
268 | enc->block_size = 8; | ||
269 | break; | ||
270 | case SSH_CIPHER_ARCFOUR: | ||
271 | enc->key_len = 16; | ||
272 | enc->iv_len = 0; | ||
273 | enc->block_size = 8; | ||
274 | break; | ||
275 | default: | ||
276 | fatal("unsupported cipher %s", name); | ||
277 | } | ||
278 | enc->name = name; | ||
279 | enc->enabled = 0; | ||
280 | enc->iv = NULL; | ||
281 | enc->key = NULL; | ||
282 | } | ||
283 | void | ||
284 | choose_mac(Mac *mac, char *client, char *server) | ||
285 | { | ||
286 | char *name = get_match(client, server); | ||
287 | if (name == NULL) | ||
288 | fatal("no matching mac found: client %s server %s", client, server); | ||
289 | if (strcmp(name, "hmac-md5") == 0) { | ||
290 | mac->md = EVP_md5(); | ||
291 | } else if (strcmp(name, "hmac-sha1") == 0) { | ||
292 | mac->md = EVP_sha1(); | ||
293 | } else if (strcmp(name, "hmac-ripemd160@openssh.com") == 0) { | ||
294 | mac->md = EVP_ripemd160(); | ||
295 | } else { | ||
296 | fatal("unsupported mac %s", name); | ||
297 | } | ||
298 | mac->name = name; | ||
299 | mac->mac_len = mac->md->md_size; | ||
300 | mac->key_len = datafellows ? 16 : mac->mac_len; | ||
301 | mac->key = NULL; | ||
302 | mac->enabled = 0; | ||
303 | } | ||
304 | void | ||
305 | choose_comp(Comp *comp, char *client, char *server) | ||
306 | { | ||
307 | char *name = get_match(client, server); | ||
308 | if (name == NULL) | ||
309 | fatal("no matching comp found: client %s server %s", client, server); | ||
310 | if (strcmp(name, "zlib") == 0) { | ||
311 | comp->type = 1; | ||
312 | } else if (strcmp(name, "none") == 0) { | ||
313 | comp->type = 0; | ||
314 | } else { | ||
315 | fatal("unsupported comp %s", name); | ||
316 | } | ||
317 | comp->name = name; | ||
318 | } | ||
319 | void | ||
320 | choose_kex(Kex *k, char *client, char *server) | ||
321 | { | ||
322 | k->name = get_match(client, server); | ||
323 | if (k->name == NULL) | ||
324 | fatal("no kex alg"); | ||
325 | if (strcmp(k->name, KEX_DH1) != 0) | ||
326 | fatal("bad kex alg %s", k->name); | ||
327 | } | ||
328 | void | ||
329 | choose_hostkeyalg(Kex *k, char *client, char *server) | ||
330 | { | ||
331 | k->hostkeyalg = get_match(client, server); | ||
332 | if (k->hostkeyalg == NULL) | ||
333 | fatal("no hostkey alg"); | ||
334 | if (strcmp(k->hostkeyalg, KEX_DSS) != 0) | ||
335 | fatal("bad hostkey alg %s", k->hostkeyalg); | ||
336 | } | ||
337 | |||
338 | Kex * | ||
339 | kex_choose_conf(char *cprop[PROPOSAL_MAX], char *sprop[PROPOSAL_MAX], int server) | ||
340 | { | ||
341 | int i; | ||
342 | int mode; | ||
343 | int ctos; /* direction: if true client-to-server */ | ||
344 | int need; | ||
345 | Kex *k; | ||
346 | |||
347 | k = xmalloc(sizeof(*k)); | ||
348 | memset(k, 0, sizeof(*k)); | ||
349 | k->server = server; | ||
350 | |||
351 | for (mode = 0; mode < MODE_MAX; mode++) { | ||
352 | int nenc, nmac, ncomp; | ||
353 | ctos = (!k->server && mode == MODE_OUT) || (k->server && mode == MODE_IN); | ||
354 | nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC; | ||
355 | nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC; | ||
356 | ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC; | ||
357 | choose_enc (&k->enc [mode], cprop[nenc], sprop[nenc]); | ||
358 | choose_mac (&k->mac [mode], cprop[nmac], sprop[nmac]); | ||
359 | choose_comp(&k->comp[mode], cprop[ncomp], sprop[ncomp]); | ||
360 | log("kex: %s %s %s %s", | ||
361 | ctos ? "client->server" : "server->client", | ||
362 | k->enc[mode].name, | ||
363 | k->mac[mode].name, | ||
364 | k->comp[mode].name); | ||
365 | } | ||
366 | choose_kex(k, cprop[PROPOSAL_KEX_ALGS], sprop[PROPOSAL_KEX_ALGS]); | ||
367 | choose_hostkeyalg(k, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS], | ||
368 | sprop[PROPOSAL_SERVER_HOST_KEY_ALGS]); | ||
369 | for (i = 0; i < PROPOSAL_MAX; i++) { | ||
370 | xfree(cprop[i]); | ||
371 | xfree(sprop[i]); | ||
372 | } | ||
373 | need = 0; | ||
374 | for (mode = 0; mode < MODE_MAX; mode++) { | ||
375 | if (need < k->enc[mode].key_len) | ||
376 | need = k->enc[mode].key_len; | ||
377 | if (need < k->enc[mode].iv_len) | ||
378 | need = k->enc[mode].iv_len; | ||
379 | if (need < k->mac[mode].key_len) | ||
380 | need = k->mac[mode].key_len; | ||
381 | } | ||
382 | /* need runden? */ | ||
383 | #define WE_NEED 32 | ||
384 | k->we_need = WE_NEED; | ||
385 | k->we_need = need; | ||
386 | return k; | ||
387 | } | ||
388 | |||
389 | int | ||
390 | kex_derive_keys(Kex *k, unsigned char *hash, BIGNUM *shared_secret) | ||
391 | { | ||
392 | int i; | ||
393 | int mode; | ||
394 | int ctos; | ||
395 | unsigned char *keys[NKEYS]; | ||
396 | |||
397 | for (i = 0; i < NKEYS; i++) | ||
398 | keys[i] = derive_key('A'+i, k->we_need, hash, shared_secret); | ||
399 | |||
400 | for (mode = 0; mode < MODE_MAX; mode++) { | ||
401 | ctos = (!k->server && mode == MODE_OUT) || (k->server && mode == MODE_IN); | ||
402 | k->enc[mode].iv = keys[ctos ? 0 : 1]; | ||
403 | k->enc[mode].key = keys[ctos ? 2 : 3]; | ||
404 | k->mac[mode].key = keys[ctos ? 4 : 5]; | ||
405 | } | ||
406 | return 0; | ||
407 | } | ||
@@ -0,0 +1,111 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | ||
3 | * | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * 1. Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * 2. Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * 3. All advertising materials mentioning features or use of this software | ||
13 | * must display the following acknowledgement: | ||
14 | * This product includes software developed by Markus Friedl. | ||
15 | * 4. The name of the author may not be used to endorse or promote products | ||
16 | * derived from this software without specific prior written permission. | ||
17 | * | ||
18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||
19 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
21 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
23 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | #ifndef KEX_H | ||
30 | #define KEX_H | ||
31 | |||
32 | #define KEX_DH1 "diffie-hellman-group1-sha1" | ||
33 | #define KEX_DSS "ssh-dss" | ||
34 | |||
35 | enum kex_init_proposals { | ||
36 | PROPOSAL_KEX_ALGS, | ||
37 | PROPOSAL_SERVER_HOST_KEY_ALGS, | ||
38 | PROPOSAL_ENC_ALGS_CTOS, | ||
39 | PROPOSAL_ENC_ALGS_STOC, | ||
40 | PROPOSAL_MAC_ALGS_CTOS, | ||
41 | PROPOSAL_MAC_ALGS_STOC, | ||
42 | PROPOSAL_COMP_ALGS_CTOS, | ||
43 | PROPOSAL_COMP_ALGS_STOC, | ||
44 | PROPOSAL_LANG_CTOS, | ||
45 | PROPOSAL_LANG_STOC, | ||
46 | PROPOSAL_MAX | ||
47 | }; | ||
48 | |||
49 | enum kex_modes { | ||
50 | MODE_IN, | ||
51 | MODE_OUT, | ||
52 | MODE_MAX | ||
53 | }; | ||
54 | |||
55 | typedef struct Kex Kex; | ||
56 | typedef struct Mac Mac; | ||
57 | typedef struct Comp Comp; | ||
58 | typedef struct Enc Enc; | ||
59 | |||
60 | struct Enc { | ||
61 | int type; | ||
62 | int enabled; | ||
63 | int block_size; | ||
64 | unsigned char *key; | ||
65 | unsigned char *iv; | ||
66 | int key_len; | ||
67 | int iv_len; | ||
68 | char *name; | ||
69 | }; | ||
70 | struct Mac { | ||
71 | EVP_MD *md; | ||
72 | int enabled; | ||
73 | int mac_len; | ||
74 | unsigned char *key; | ||
75 | int key_len; | ||
76 | char *name; | ||
77 | }; | ||
78 | struct Comp { | ||
79 | int type; | ||
80 | int enabled; | ||
81 | char *name; | ||
82 | }; | ||
83 | struct Kex { | ||
84 | Enc enc [MODE_MAX]; | ||
85 | Mac mac [MODE_MAX]; | ||
86 | Comp comp[MODE_MAX]; | ||
87 | int we_need; | ||
88 | int server; | ||
89 | char *name; | ||
90 | char *hostkeyalg; | ||
91 | }; | ||
92 | |||
93 | Buffer *kex_init(char *myproposal[PROPOSAL_MAX]); | ||
94 | DH *new_dh_group1(); | ||
95 | Kex *kex_choose_conf(char *cprop[PROPOSAL_MAX], char *sprop[PROPOSAL_MAX], int server); | ||
96 | int kex_derive_keys(Kex *k, unsigned char *hash, BIGNUM *shared_secret); | ||
97 | void bignum_print(BIGNUM *b); | ||
98 | void packet_set_kex(Kex *k); | ||
99 | |||
100 | unsigned char * | ||
101 | kex_hash( | ||
102 | char *client_version_string, | ||
103 | char *server_version_string, | ||
104 | char *ckexinit, int ckexinitlen, | ||
105 | char *skexinit, int skexinitlen, | ||
106 | char *serverhostkeyblob, int sbloblen, | ||
107 | BIGNUM *client_dh_pub, | ||
108 | BIGNUM *server_dh_pub, | ||
109 | BIGNUM *shared_secret); | ||
110 | |||
111 | #endif | ||
@@ -28,7 +28,7 @@ | |||
28 | */ | 28 | */ |
29 | 29 | ||
30 | #include "includes.h" | 30 | #include "includes.h" |
31 | RCSID("$Id: nchan.c,v 1.6 2000/04/01 01:09:24 damien Exp $"); | 31 | RCSID("$Id: nchan.c,v 1.7 2000/04/04 04:39:02 damien Exp $"); |
32 | 32 | ||
33 | #include "ssh.h" | 33 | #include "ssh.h" |
34 | 34 | ||
@@ -37,138 +37,169 @@ RCSID("$Id: nchan.c,v 1.6 2000/04/01 01:09:24 damien Exp $"); | |||
37 | #include "channels.h" | 37 | #include "channels.h" |
38 | #include "nchan.h" | 38 | #include "nchan.h" |
39 | 39 | ||
40 | static void chan_send_ieof(Channel *c); | 40 | #include "ssh2.h" |
41 | static void chan_send_oclose(Channel *c); | 41 | #include "compat.h" |
42 | static void chan_shutdown_write(Channel *c); | ||
43 | static void chan_shutdown_read(Channel *c); | ||
44 | 42 | ||
43 | /* functions manipulating channel states */ | ||
45 | /* | 44 | /* |
46 | * EVENTS update channel input/output states execute ACTIONS | 45 | * EVENTS update channel input/output states execute ACTIONS |
47 | */ | 46 | */ |
48 | |||
49 | /* events concerning the INPUT from socket for channel (istate) */ | 47 | /* events concerning the INPUT from socket for channel (istate) */ |
50 | void | 48 | chan_event_fn *chan_rcvd_oclose = NULL; |
51 | chan_rcvd_oclose(Channel *c) | 49 | chan_event_fn *chan_read_failed = NULL; |
50 | chan_event_fn *chan_ibuf_empty = NULL; | ||
51 | /* events concerning the OUTPUT from channel for socket (ostate) */ | ||
52 | chan_event_fn *chan_rcvd_ieof = NULL; | ||
53 | chan_event_fn *chan_write_failed = NULL; | ||
54 | chan_event_fn *chan_obuf_empty = NULL; | ||
55 | /* | ||
56 | * ACTIONS: should never update the channel states | ||
57 | */ | ||
58 | static void chan_send_ieof1(Channel *c); | ||
59 | static void chan_send_oclose1(Channel *c); | ||
60 | static void chan_send_close2(Channel *c); | ||
61 | static void chan_send_eof2(Channel *c); | ||
62 | |||
63 | /* channel cleanup */ | ||
64 | chan_event_fn *chan_delete_if_full_closed = NULL; | ||
65 | |||
66 | /* helper */ | ||
67 | static void chan_shutdown_write(Channel *c); | ||
68 | static void chan_shutdown_read(Channel *c); | ||
69 | |||
70 | /* | ||
71 | * SSH1 specific implementation of event functions | ||
72 | */ | ||
73 | |||
74 | static void | ||
75 | chan_rcvd_oclose1(Channel *c) | ||
52 | { | 76 | { |
77 | debug("channel %d: rcvd oclose", c->self); | ||
53 | switch (c->istate) { | 78 | switch (c->istate) { |
54 | case CHAN_INPUT_WAIT_OCLOSE: | 79 | case CHAN_INPUT_WAIT_OCLOSE: |
55 | debug("channel %d: INPUT_WAIT_OCLOSE -> INPUT_CLOSED [rcvd OCLOSE]", c->self); | 80 | debug("channel %d: input wait_oclose -> closed", c->self); |
56 | c->istate = CHAN_INPUT_CLOSED; | 81 | c->istate = CHAN_INPUT_CLOSED; |
57 | break; | 82 | break; |
58 | case CHAN_INPUT_OPEN: | 83 | case CHAN_INPUT_OPEN: |
59 | debug("channel %d: INPUT_OPEN -> INPUT_CLOSED [rvcd OCLOSE, send IEOF]", c->self); | 84 | debug("channel %d: input open -> closed", c->self); |
60 | chan_shutdown_read(c); | 85 | chan_shutdown_read(c); |
61 | chan_send_ieof(c); | 86 | chan_send_ieof1(c); |
62 | c->istate = CHAN_INPUT_CLOSED; | 87 | c->istate = CHAN_INPUT_CLOSED; |
63 | break; | 88 | break; |
64 | case CHAN_INPUT_WAIT_DRAIN: | 89 | case CHAN_INPUT_WAIT_DRAIN: |
65 | /* both local read_failed and remote write_failed */ | 90 | /* both local read_failed and remote write_failed */ |
66 | log("channel %d: INPUT_WAIT_DRAIN -> INPUT_CLOSED [rvcd OCLOSE, send IEOF]", c->self); | 91 | log("channel %d: input drain -> closed", c->self); |
67 | debug("channel %d: INPUT_WAIT_DRAIN -> INPUT_CLOSED [rvcd OCLOSE, send IEOF]", c->self); | 92 | chan_send_ieof1(c); |
68 | chan_send_ieof(c); | ||
69 | c->istate = CHAN_INPUT_CLOSED; | 93 | c->istate = CHAN_INPUT_CLOSED; |
70 | break; | 94 | break; |
71 | default: | 95 | default: |
72 | error("protocol error: chan_rcvd_oclose %d for istate %d", c->self, c->istate); | 96 | error("channel %d: protocol error: chan_rcvd_oclose for istate %d", |
97 | c->self, c->istate); | ||
73 | return; | 98 | return; |
74 | } | 99 | } |
75 | } | 100 | } |
76 | void | 101 | static void |
77 | chan_read_failed(Channel *c) | 102 | chan_read_failed_12(Channel *c) |
78 | { | 103 | { |
104 | debug("channel %d: read failed", c->self); | ||
79 | switch (c->istate) { | 105 | switch (c->istate) { |
80 | case CHAN_INPUT_OPEN: | 106 | case CHAN_INPUT_OPEN: |
81 | debug("channel %d: INPUT_OPEN -> INPUT_WAIT_DRAIN [read failed]", c->self); | 107 | debug("channel %d: input open -> drain", c->self); |
82 | chan_shutdown_read(c); | 108 | chan_shutdown_read(c); |
83 | c->istate = CHAN_INPUT_WAIT_DRAIN; | 109 | c->istate = CHAN_INPUT_WAIT_DRAIN; |
84 | break; | 110 | break; |
85 | default: | 111 | default: |
86 | error("internal error: we do not read, but chan_read_failed %d for istate %d", | 112 | error("channel %d: internal error: we do not read, but chan_read_failed for istate %d", |
87 | c->self, c->istate); | 113 | c->self, c->istate); |
88 | break; | 114 | break; |
89 | } | 115 | } |
90 | } | 116 | } |
91 | void | 117 | static void |
92 | chan_ibuf_empty(Channel *c) | 118 | chan_ibuf_empty1(Channel *c) |
93 | { | 119 | { |
120 | debug("channel %d: ibuf empty", c->self); | ||
94 | if (buffer_len(&c->input)) { | 121 | if (buffer_len(&c->input)) { |
95 | error("internal error: chan_ibuf_empty %d for non empty buffer", c->self); | 122 | error("channel %d: internal error: chan_ibuf_empty for non empty buffer", |
123 | c->self); | ||
96 | return; | 124 | return; |
97 | } | 125 | } |
98 | switch (c->istate) { | 126 | switch (c->istate) { |
99 | case CHAN_INPUT_WAIT_DRAIN: | 127 | case CHAN_INPUT_WAIT_DRAIN: |
100 | debug("channel %d: INPUT_WAIT_DRAIN -> INPUT_WAIT_OCLOSE [inbuf empty, send IEOF]", c->self); | 128 | debug("channel %d: input drain -> wait_oclose", c->self); |
101 | chan_send_ieof(c); | 129 | chan_send_ieof1(c); |
102 | c->istate = CHAN_INPUT_WAIT_OCLOSE; | 130 | c->istate = CHAN_INPUT_WAIT_OCLOSE; |
103 | break; | 131 | break; |
104 | default: | 132 | default: |
105 | error("internal error: chan_ibuf_empty %d for istate %d", c->self, c->istate); | 133 | error("channel %d: internal error: chan_ibuf_empty for istate %d", |
134 | c->self, c->istate); | ||
106 | break; | 135 | break; |
107 | } | 136 | } |
108 | } | 137 | } |
109 | 138 | static void | |
110 | /* events concerning the OUTPUT from channel for socket (ostate) */ | 139 | chan_rcvd_ieof1(Channel *c) |
111 | void | ||
112 | chan_rcvd_ieof(Channel *c) | ||
113 | { | 140 | { |
141 | debug("channel %d: rcvd ieof", c->self); | ||
114 | switch (c->ostate) { | 142 | switch (c->ostate) { |
115 | case CHAN_OUTPUT_OPEN: | 143 | case CHAN_OUTPUT_OPEN: |
116 | debug("channel %d: OUTPUT_OPEN -> OUTPUT_WAIT_DRAIN [rvcd IEOF]", c->self); | 144 | debug("channel %d: output open -> drain", c->self); |
117 | c->ostate = CHAN_OUTPUT_WAIT_DRAIN; | 145 | c->ostate = CHAN_OUTPUT_WAIT_DRAIN; |
118 | break; | 146 | break; |
119 | case CHAN_OUTPUT_WAIT_IEOF: | 147 | case CHAN_OUTPUT_WAIT_IEOF: |
120 | debug("channel %d: OUTPUT_WAIT_IEOF -> OUTPUT_CLOSED [rvcd IEOF]", c->self); | 148 | debug("channel %d: output wait_ieof -> closed", c->self); |
121 | c->ostate = CHAN_OUTPUT_CLOSED; | 149 | c->ostate = CHAN_OUTPUT_CLOSED; |
122 | break; | 150 | break; |
123 | default: | 151 | default: |
124 | error("protocol error: chan_rcvd_ieof %d for ostate %d", c->self, c->ostate); | 152 | error("channel %d: protocol error: chan_rcvd_ieof for ostate %d", |
153 | c->self, c->ostate); | ||
125 | break; | 154 | break; |
126 | } | 155 | } |
127 | } | 156 | } |
128 | void | 157 | static void |
129 | chan_write_failed(Channel *c) | 158 | chan_write_failed1(Channel *c) |
130 | { | 159 | { |
160 | debug("channel %d: write failed", c->self); | ||
131 | switch (c->ostate) { | 161 | switch (c->ostate) { |
132 | case CHAN_OUTPUT_OPEN: | 162 | case CHAN_OUTPUT_OPEN: |
133 | debug("channel %d: OUTPUT_OPEN -> OUTPUT_WAIT_IEOF [write failed]", c->self); | 163 | debug("channel %d: output open -> wait_ieof", c->self); |
134 | chan_send_oclose(c); | 164 | chan_send_oclose1(c); |
135 | c->ostate = CHAN_OUTPUT_WAIT_IEOF; | 165 | c->ostate = CHAN_OUTPUT_WAIT_IEOF; |
136 | break; | 166 | break; |
137 | case CHAN_OUTPUT_WAIT_DRAIN: | 167 | case CHAN_OUTPUT_WAIT_DRAIN: |
138 | debug("channel %d: OUTPUT_WAIT_DRAIN -> OUTPUT_CLOSED [write failed]", c->self); | 168 | debug("channel %d: output wait_drain -> closed", c->self); |
139 | chan_send_oclose(c); | 169 | chan_send_oclose1(c); |
140 | c->ostate = CHAN_OUTPUT_CLOSED; | 170 | c->ostate = CHAN_OUTPUT_CLOSED; |
141 | break; | 171 | break; |
142 | default: | 172 | default: |
143 | error("internal error: chan_write_failed %d for ostate %d", c->self, c->ostate); | 173 | error("channel %d: internal error: chan_write_failed for ostate %d", |
174 | c->self, c->ostate); | ||
144 | break; | 175 | break; |
145 | } | 176 | } |
146 | } | 177 | } |
147 | void | 178 | static void |
148 | chan_obuf_empty(Channel *c) | 179 | chan_obuf_empty1(Channel *c) |
149 | { | 180 | { |
181 | debug("channel %d: obuf empty", c->self); | ||
150 | if (buffer_len(&c->output)) { | 182 | if (buffer_len(&c->output)) { |
151 | debug("internal error: chan_obuf_empty %d for non empty buffer", c->self); | 183 | error("channel %d: internal error: chan_obuf_empty for non empty buffer", |
184 | c->self); | ||
152 | return; | 185 | return; |
153 | } | 186 | } |
154 | switch (c->ostate) { | 187 | switch (c->ostate) { |
155 | case CHAN_OUTPUT_WAIT_DRAIN: | 188 | case CHAN_OUTPUT_WAIT_DRAIN: |
156 | debug("channel %d: OUTPUT_WAIT_DRAIN -> OUTPUT_CLOSED [obuf empty, send OCLOSE]", c->self); | 189 | debug("channel %d: output drain -> closed", c->self); |
157 | chan_send_oclose(c); | 190 | chan_send_oclose1(c); |
158 | c->ostate = CHAN_OUTPUT_CLOSED; | 191 | c->ostate = CHAN_OUTPUT_CLOSED; |
159 | break; | 192 | break; |
160 | default: | 193 | default: |
161 | error("internal error: chan_obuf_empty %d for ostate %d", c->self, c->ostate); | 194 | error("channel %d: internal error: chan_obuf_empty for ostate %d", |
195 | c->self, c->ostate); | ||
162 | break; | 196 | break; |
163 | } | 197 | } |
164 | } | 198 | } |
165 | |||
166 | /* | ||
167 | * ACTIONS: should never update the channel states: c->istate or c->ostate | ||
168 | */ | ||
169 | static void | 199 | static void |
170 | chan_send_ieof(Channel *c) | 200 | chan_send_ieof1(Channel *c) |
171 | { | 201 | { |
202 | debug("channel %d: send ieof", c->self); | ||
172 | switch (c->istate) { | 203 | switch (c->istate) { |
173 | case CHAN_INPUT_OPEN: | 204 | case CHAN_INPUT_OPEN: |
174 | case CHAN_INPUT_WAIT_DRAIN: | 205 | case CHAN_INPUT_WAIT_DRAIN: |
@@ -177,13 +208,15 @@ chan_send_ieof(Channel *c) | |||
177 | packet_send(); | 208 | packet_send(); |
178 | break; | 209 | break; |
179 | default: | 210 | default: |
180 | error("internal error: channel %d: cannot send IEOF for istate %d", c->self, c->istate); | 211 | error("channel %d: internal error: cannot send ieof for istate %d", |
212 | c->self, c->istate); | ||
181 | break; | 213 | break; |
182 | } | 214 | } |
183 | } | 215 | } |
184 | static void | 216 | static void |
185 | chan_send_oclose(Channel *c) | 217 | chan_send_oclose1(Channel *c) |
186 | { | 218 | { |
219 | debug("channel %d: send oclose", c->self); | ||
187 | switch (c->ostate) { | 220 | switch (c->ostate) { |
188 | case CHAN_OUTPUT_OPEN: | 221 | case CHAN_OUTPUT_OPEN: |
189 | case CHAN_OUTPUT_WAIT_DRAIN: | 222 | case CHAN_OUTPUT_WAIT_DRAIN: |
@@ -194,40 +227,246 @@ chan_send_oclose(Channel *c) | |||
194 | packet_send(); | 227 | packet_send(); |
195 | break; | 228 | break; |
196 | default: | 229 | default: |
197 | error("internal error: channel %d: cannot send OCLOSE for ostate %d", c->self, c->istate); | 230 | error("channel %d: internal error: cannot send oclose for ostate %d", |
231 | c->self, c->ostate); | ||
198 | break; | 232 | break; |
199 | } | 233 | } |
200 | } | 234 | } |
235 | static void | ||
236 | chan_delete_if_full_closed1(Channel *c) | ||
237 | { | ||
238 | if (c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED) { | ||
239 | debug("channel %d: full closed", c->self); | ||
240 | channel_free(c->self); | ||
241 | } | ||
242 | } | ||
201 | 243 | ||
202 | /* helper */ | 244 | /* |
245 | * the same for SSH2 | ||
246 | */ | ||
203 | static void | 247 | static void |
204 | chan_shutdown_write(Channel *c) | 248 | chan_rcvd_oclose2(Channel *c) |
205 | { | 249 | { |
206 | /* shutdown failure is allowed if write failed already */ | 250 | debug("channel %d: rcvd close", c->self); |
207 | debug("channel %d: shutdown_write", c->self); | 251 | if (c->flags & CHAN_CLOSE_RCVD) |
208 | if (shutdown(c->sock, SHUT_WR) < 0) | 252 | error("channel %d: protocol error: close rcvd twice", c->self); |
209 | debug("chan_shutdown_write failed for #%d/fd%d: %.100s", | 253 | c->flags |= CHAN_CLOSE_RCVD; |
210 | c->self, c->sock, strerror(errno)); | 254 | if (c->type == SSH_CHANNEL_LARVAL) { |
255 | /* tear down larval channels immediately */ | ||
256 | c->ostate = CHAN_OUTPUT_CLOSED; | ||
257 | c->istate = CHAN_INPUT_CLOSED; | ||
258 | return; | ||
259 | } | ||
260 | switch (c->ostate) { | ||
261 | case CHAN_OUTPUT_OPEN: | ||
262 | /* wait until a data from the channel is consumed if a CLOSE is received */ | ||
263 | debug("channel %d: output open -> drain", c->self); | ||
264 | c->ostate = CHAN_OUTPUT_WAIT_DRAIN; | ||
265 | break; | ||
266 | } | ||
267 | switch (c->istate) { | ||
268 | case CHAN_INPUT_OPEN: | ||
269 | debug("channel %d: input open -> closed", c->self); | ||
270 | chan_shutdown_read(c); | ||
271 | break; | ||
272 | case CHAN_INPUT_WAIT_DRAIN: | ||
273 | debug("channel %d: input drain -> closed", c->self); | ||
274 | chan_send_eof2(c); | ||
275 | break; | ||
276 | } | ||
277 | c->istate = CHAN_INPUT_CLOSED; | ||
211 | } | 278 | } |
212 | static void | 279 | static void |
213 | chan_shutdown_read(Channel *c) | 280 | chan_ibuf_empty2(Channel *c) |
281 | { | ||
282 | debug("channel %d: ibuf empty", c->self); | ||
283 | if (buffer_len(&c->input)) { | ||
284 | error("channel %d: internal error: chan_ibuf_empty for non empty buffer", | ||
285 | c->self); | ||
286 | return; | ||
287 | } | ||
288 | switch (c->istate) { | ||
289 | case CHAN_INPUT_WAIT_DRAIN: | ||
290 | debug("channel %d: input drain -> closed", c->self); | ||
291 | if (!(c->flags & CHAN_CLOSE_SENT)) | ||
292 | chan_send_eof2(c); | ||
293 | c->istate = CHAN_INPUT_CLOSED; | ||
294 | break; | ||
295 | default: | ||
296 | error("channel %d: internal error: chan_ibuf_empty for istate %d", | ||
297 | c->self, c->istate); | ||
298 | break; | ||
299 | } | ||
300 | } | ||
301 | static void | ||
302 | chan_rcvd_ieof2(Channel *c) | ||
214 | { | 303 | { |
215 | debug("channel %d: shutdown_read", c->self); | 304 | debug("channel %d: rcvd eof", c->self); |
216 | if (shutdown(c->sock, SHUT_RD) < 0) | 305 | if (c->ostate == CHAN_OUTPUT_OPEN) { |
217 | error("chan_shutdown_read failed for #%d/fd%d [i%d o%d]: %.100s", | 306 | debug("channel %d: output open -> drain", c->self); |
218 | c->self, c->sock, c->istate, c->ostate, strerror(errno)); | 307 | c->ostate = CHAN_OUTPUT_WAIT_DRAIN; |
308 | } | ||
219 | } | 309 | } |
220 | void | 310 | static void |
221 | chan_delete_if_full_closed(Channel *c) | 311 | chan_write_failed2(Channel *c) |
312 | { | ||
313 | debug("channel %d: write failed", c->self); | ||
314 | switch (c->ostate) { | ||
315 | case CHAN_OUTPUT_OPEN: | ||
316 | debug("channel %d: output open -> closed", c->self); | ||
317 | chan_shutdown_write(c); // ?? | ||
318 | c->ostate = CHAN_OUTPUT_CLOSED; | ||
319 | break; | ||
320 | case CHAN_OUTPUT_WAIT_DRAIN: | ||
321 | debug("channel %d: output drain -> closed", c->self); | ||
322 | chan_shutdown_write(c); | ||
323 | c->ostate = CHAN_OUTPUT_CLOSED; | ||
324 | break; | ||
325 | default: | ||
326 | error("channel %d: internal error: chan_write_failed for ostate %d", | ||
327 | c->self, c->ostate); | ||
328 | break; | ||
329 | } | ||
330 | } | ||
331 | static void | ||
332 | chan_obuf_empty2(Channel *c) | ||
333 | { | ||
334 | debug("channel %d: obuf empty", c->self); | ||
335 | if (buffer_len(&c->output)) { | ||
336 | error("internal error: chan_obuf_empty %d for non empty buffer", | ||
337 | c->self); | ||
338 | return; | ||
339 | } | ||
340 | switch (c->ostate) { | ||
341 | case CHAN_OUTPUT_WAIT_DRAIN: | ||
342 | debug("channel %d: output drain -> closed", c->self); | ||
343 | chan_shutdown_write(c); | ||
344 | c->ostate = CHAN_OUTPUT_CLOSED; | ||
345 | break; | ||
346 | default: | ||
347 | error("channel %d: internal error: chan_obuf_empty for ostate %d", | ||
348 | c->self, c->ostate); | ||
349 | break; | ||
350 | } | ||
351 | } | ||
352 | static void | ||
353 | chan_send_eof2(Channel *c) | ||
354 | { | ||
355 | debug("channel %d: send eof", c->self); | ||
356 | switch (c->istate) { | ||
357 | case CHAN_INPUT_WAIT_DRAIN: | ||
358 | packet_start(SSH2_MSG_CHANNEL_EOF); | ||
359 | packet_put_int(c->remote_id); | ||
360 | packet_send(); | ||
361 | break; | ||
362 | default: | ||
363 | error("channel %d: internal error: cannot send eof for istate %d", | ||
364 | c->self, c->istate); | ||
365 | break; | ||
366 | } | ||
367 | } | ||
368 | static void | ||
369 | chan_send_close2(Channel *c) | ||
370 | { | ||
371 | debug("channel %d: send close", c->self); | ||
372 | if (c->ostate != CHAN_OUTPUT_CLOSED || | ||
373 | c->istate != CHAN_INPUT_CLOSED) { | ||
374 | error("channel %d: internal error: cannot send close for istate/ostate %d/%d", | ||
375 | c->self, c->istate, c->ostate); | ||
376 | } else if (c->flags & CHAN_CLOSE_SENT) { | ||
377 | error("channel %d: internal error: already sent close", c->self); | ||
378 | } else { | ||
379 | packet_start(SSH2_MSG_CHANNEL_CLOSE); | ||
380 | packet_put_int(c->remote_id); | ||
381 | packet_send(); | ||
382 | c->flags |= CHAN_CLOSE_SENT; | ||
383 | } | ||
384 | } | ||
385 | static void | ||
386 | chan_delete_if_full_closed2(Channel *c) | ||
222 | { | 387 | { |
223 | if (c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED) { | 388 | if (c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED) { |
224 | debug("channel %d: full closed", c->self); | 389 | if (!(c->flags & CHAN_CLOSE_SENT)) { |
225 | channel_free(c->self); | 390 | chan_send_close2(c); |
391 | } | ||
392 | if ((c->flags & CHAN_CLOSE_SENT) && | ||
393 | (c->flags & CHAN_CLOSE_RCVD)) { | ||
394 | debug("channel %d: full closed2", c->self); | ||
395 | channel_free(c->self); | ||
396 | } | ||
226 | } | 397 | } |
227 | } | 398 | } |
399 | |||
400 | /* shared */ | ||
228 | void | 401 | void |
229 | chan_init_iostates(Channel *c) | 402 | chan_init_iostates(Channel *c) |
230 | { | 403 | { |
231 | c->ostate = CHAN_OUTPUT_OPEN; | 404 | c->ostate = CHAN_OUTPUT_OPEN; |
232 | c->istate = CHAN_INPUT_OPEN; | 405 | c->istate = CHAN_INPUT_OPEN; |
406 | c->flags = 0; | ||
407 | } | ||
408 | |||
409 | /* init */ | ||
410 | void | ||
411 | chan_init(void) | ||
412 | { | ||
413 | if (compat20) { | ||
414 | chan_rcvd_oclose = chan_rcvd_oclose2; | ||
415 | chan_read_failed = chan_read_failed_12; | ||
416 | chan_ibuf_empty = chan_ibuf_empty2; | ||
417 | |||
418 | chan_rcvd_ieof = chan_rcvd_ieof2; | ||
419 | chan_write_failed = chan_write_failed2; | ||
420 | chan_obuf_empty = chan_obuf_empty2; | ||
421 | |||
422 | chan_delete_if_full_closed = chan_delete_if_full_closed2; | ||
423 | } else { | ||
424 | chan_rcvd_oclose = chan_rcvd_oclose1; | ||
425 | chan_read_failed = chan_read_failed_12; | ||
426 | chan_ibuf_empty = chan_ibuf_empty1; | ||
427 | |||
428 | chan_rcvd_ieof = chan_rcvd_ieof1; | ||
429 | chan_write_failed = chan_write_failed1; | ||
430 | chan_obuf_empty = chan_obuf_empty1; | ||
431 | |||
432 | chan_delete_if_full_closed = chan_delete_if_full_closed1; | ||
433 | } | ||
434 | } | ||
435 | |||
436 | /* helper */ | ||
437 | static void | ||
438 | chan_shutdown_write(Channel *c) | ||
439 | { | ||
440 | buffer_consume(&c->output, buffer_len(&c->output)); | ||
441 | if (compat20 && c->type == SSH_CHANNEL_LARVAL) | ||
442 | return; | ||
443 | /* shutdown failure is allowed if write failed already */ | ||
444 | debug("channel %d: close_write", c->self); | ||
445 | if (c->sock != -1) { | ||
446 | if (shutdown(c->sock, SHUT_WR) < 0) | ||
447 | debug("channel %d: chan_shutdown_write: shutdown() failed for fd%d: %.100s", | ||
448 | c->self, c->sock, strerror(errno)); | ||
449 | } else { | ||
450 | if (close(c->wfd) < 0) | ||
451 | log("channel %d: chan_shutdown_write: close() failed for fd%d: %.100s", | ||
452 | c->self, c->wfd, strerror(errno)); | ||
453 | c->wfd = -1; | ||
454 | } | ||
455 | } | ||
456 | static void | ||
457 | chan_shutdown_read(Channel *c) | ||
458 | { | ||
459 | if (compat20 && c->type == SSH_CHANNEL_LARVAL) | ||
460 | return; | ||
461 | debug("channel %d: close_read", c->self); | ||
462 | if (c->sock != -1) { | ||
463 | if (shutdown(c->sock, SHUT_RD) < 0) | ||
464 | error("channel %d: chan_shutdown_read: shutdown() failed for fd%d [i%d o%d]: %.100s", | ||
465 | c->self, c->sock, c->istate, c->ostate, strerror(errno)); | ||
466 | } else { | ||
467 | if (close(c->rfd) < 0) | ||
468 | log("channel %d: chan_shutdown_read: close() failed for fd%d: %.100s", | ||
469 | c->self, c->rfd, strerror(errno)); | ||
470 | c->rfd = -1; | ||
471 | } | ||
233 | } | 472 | } |
@@ -27,7 +27,7 @@ | |||
27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 | */ | 28 | */ |
29 | 29 | ||
30 | /* RCSID("$Id: nchan.h,v 1.4 2000/04/01 01:09:24 damien Exp $"); */ | 30 | /* RCSID("$Id: nchan.h,v 1.5 2000/04/04 04:39:02 damien Exp $"); */ |
31 | 31 | ||
32 | #ifndef NCHAN_H | 32 | #ifndef NCHAN_H |
33 | #define NCHAN_H | 33 | #define NCHAN_H |
@@ -72,17 +72,25 @@ | |||
72 | #define CHAN_OUTPUT_WAIT_IEOF 0x40 | 72 | #define CHAN_OUTPUT_WAIT_IEOF 0x40 |
73 | #define CHAN_OUTPUT_CLOSED 0x80 | 73 | #define CHAN_OUTPUT_CLOSED 0x80 |
74 | 74 | ||
75 | /* EVENTS for the input state */ | 75 | #define CHAN_CLOSE_SENT 0x01 |
76 | void chan_rcvd_oclose(Channel * c); | 76 | #define CHAN_CLOSE_RCVD 0x02 |
77 | void chan_read_failed(Channel * c); | ||
78 | void chan_ibuf_empty(Channel * c); | ||
79 | 77 | ||
80 | /* EVENTS for the output state */ | ||
81 | void chan_rcvd_ieof(Channel * c); | ||
82 | void chan_write_failed(Channel * c); | ||
83 | void chan_obuf_empty(Channel * c); | ||
84 | 78 | ||
85 | void chan_init_iostates(Channel * c); | 79 | /* Channel EVENTS */ |
80 | typedef void chan_event_fn(Channel * c); | ||
81 | |||
82 | /* for the input state */ | ||
83 | extern chan_event_fn *chan_rcvd_oclose; | ||
84 | extern chan_event_fn *chan_read_failed; | ||
85 | extern chan_event_fn *chan_ibuf_empty; | ||
86 | |||
87 | /* for the output state */ | ||
88 | extern chan_event_fn *chan_rcvd_ieof; | ||
89 | extern chan_event_fn *chan_write_failed; | ||
90 | extern chan_event_fn *chan_obuf_empty; | ||
86 | 91 | ||
87 | void chan_delete_if_full_closed(Channel *c); | 92 | extern chan_event_fn *chan_delete_if_full_closed; |
93 | |||
94 | void chan_init_iostates(Channel * c); | ||
95 | void chan_init(void); | ||
88 | #endif | 96 | #endif |
diff --git a/nchan2.ms b/nchan2.ms new file mode 100644 index 000000000..1b119d135 --- /dev/null +++ b/nchan2.ms | |||
@@ -0,0 +1,64 @@ | |||
1 | .TL | ||
2 | OpenSSH Channel Close Protocol 2.0 Implementation | ||
3 | .SH | ||
4 | Channel Input State Diagram | ||
5 | .PS | ||
6 | reset | ||
7 | l=1 | ||
8 | s=1.2 | ||
9 | ellipsewid=s*ellipsewid | ||
10 | boxwid=s*boxwid | ||
11 | ellipseht=s*ellipseht | ||
12 | S1: ellipse "INPUT" "OPEN" | ||
13 | move right 2*l from last ellipse.e | ||
14 | S3: ellipse invis | ||
15 | move down l from last ellipse.s | ||
16 | S4: ellipse "INPUT" "CLOSED" | ||
17 | move down l from 1st ellipse.s | ||
18 | S2: ellipse "INPUT" "WAIT" "DRAIN" | ||
19 | arrow from S1.e to S4.n | ||
20 | box invis "rcvd CLOSE/" "shutdown_read" with .sw at last arrow.c | ||
21 | arrow "ibuf_empty ||" "rcvd CLOSE/" "send EOF" "" from S2.e to S4.w | ||
22 | arrow from S1.s to S2.n | ||
23 | box invis "read_failed/" "shutdown_read" with .e at last arrow.c | ||
24 | ellipse wid .9*ellipsewid ht .9*ellipseht at S4 | ||
25 | arrow "start" "" from S1.w+(-0.5,0) to S1.w | ||
26 | .PE | ||
27 | .SH | ||
28 | Channel Output State Diagram | ||
29 | .PS | ||
30 | S1: ellipse "OUTPUT" "OPEN" | ||
31 | move right 2*l from last ellipse.e | ||
32 | S3: ellipse invis | ||
33 | move down l from last ellipse.s | ||
34 | S4: ellipse "OUTPUT" "CLOSED" | ||
35 | move down l from 1st ellipse.s | ||
36 | S2: ellipse "OUTPUT" "WAIT" "DRAIN" | ||
37 | arrow from S1.e to S4.n | ||
38 | box invis "write_failed/" "shutdown_write" with .sw at last arrow.c | ||
39 | arrow "obuf_empty ||" "write_failed/" "shutdown_write" "" from S2.e to S4.w | ||
40 | arrow from S1.s to S2.n | ||
41 | box invis "rcvd EOF ||" "rcvd CLOSE/" "-" with .e at last arrow.c | ||
42 | ellipse wid .9*ellipsewid ht .9*ellipseht at S4 | ||
43 | arrow "start" "" from S1.w+(-0.5,0) to S1.w | ||
44 | .PE | ||
45 | .SH | ||
46 | Notes | ||
47 | .PP | ||
48 | The input buffer is filled with data from the socket | ||
49 | (the socket represents the local consumer/producer of the | ||
50 | forwarded channel). | ||
51 | The data is then sent over the INPUT-end (transmit-end) of the channel to the | ||
52 | remote peer. | ||
53 | Data sent by the peer is received on the OUTPUT-end (receive-end), | ||
54 | saved in the output buffer and written to the socket. | ||
55 | .PP | ||
56 | If the local protocol instance has forwarded all data on the | ||
57 | INPUT-end of the channel, it sends an EOF message to the peer. | ||
58 | .PP | ||
59 | A CLOSE message is sent to the peer if | ||
60 | both the INPUT- and the OUTOUT-half of the local | ||
61 | end of the channel are closed. | ||
62 | .PP | ||
63 | The channel can be deallocated by a protocol instance | ||
64 | if a CLOSE message he been both sent and received. | ||
@@ -11,11 +11,13 @@ | |||
11 | * | 11 | * |
12 | * This file contains code implementing the packet protocol and communication | 12 | * This file contains code implementing the packet protocol and communication |
13 | * with the other side. This same code is used both on client and server side. | 13 | * with the other side. This same code is used both on client and server side. |
14 | * | 14 | * |
15 | * SSH2 packet format added by Markus Friedl. | ||
16 | * | ||
15 | */ | 17 | */ |
16 | 18 | ||
17 | #include "includes.h" | 19 | #include "includes.h" |
18 | RCSID("$Id: packet.c,v 1.13 2000/04/01 01:09:25 damien Exp $"); | 20 | RCSID("$Id: packet.c,v 1.14 2000/04/04 04:39:03 damien Exp $"); |
19 | 21 | ||
20 | #include "xmalloc.h" | 22 | #include "xmalloc.h" |
21 | #include "buffer.h" | 23 | #include "buffer.h" |
@@ -30,6 +32,22 @@ RCSID("$Id: packet.c,v 1.13 2000/04/01 01:09:25 damien Exp $"); | |||
30 | #include "deattack.h" | 32 | #include "deattack.h" |
31 | #include "channels.h" | 33 | #include "channels.h" |
32 | 34 | ||
35 | #include "compat.h" | ||
36 | #include "ssh2.h" | ||
37 | |||
38 | #include <ssl/bn.h> | ||
39 | #include <ssl/dh.h> | ||
40 | #include <ssl/hmac.h> | ||
41 | #include "buffer.h" | ||
42 | #include "kex.h" | ||
43 | #include "hmac.h" | ||
44 | |||
45 | #ifdef PACKET_DEBUG | ||
46 | #define DBG(x) x | ||
47 | #else | ||
48 | #define DBG(x) | ||
49 | #endif | ||
50 | |||
33 | /* | 51 | /* |
34 | * This variable contains the file descriptors used for communicating with | 52 | * This variable contains the file descriptors used for communicating with |
35 | * the other side. connection_in is used for reading; connection_out for | 53 | * the other side. connection_in is used for reading; connection_out for |
@@ -81,11 +99,45 @@ static int initialized = 0; | |||
81 | /* Set to true if the connection is interactive. */ | 99 | /* Set to true if the connection is interactive. */ |
82 | static int interactive_mode = 0; | 100 | static int interactive_mode = 0; |
83 | 101 | ||
102 | /* True if SSH2 packet format is used */ | ||
103 | int use_ssh2_packet_format = 0; | ||
104 | |||
105 | /* Session key information for Encryption and MAC */ | ||
106 | Kex *kex = NULL; | ||
107 | |||
108 | void | ||
109 | packet_set_kex(Kex *k) | ||
110 | { | ||
111 | if( k->mac[MODE_IN ].key == NULL || | ||
112 | k->enc[MODE_IN ].key == NULL || | ||
113 | k->enc[MODE_IN ].iv == NULL || | ||
114 | k->mac[MODE_OUT].key == NULL || | ||
115 | k->enc[MODE_OUT].key == NULL || | ||
116 | k->enc[MODE_OUT].iv == NULL) | ||
117 | fatal("bad KEX"); | ||
118 | kex = k; | ||
119 | } | ||
120 | void | ||
121 | clear_enc_keys(Enc *enc, int len) | ||
122 | { | ||
123 | memset(enc->iv, 0, len); | ||
124 | memset(enc->key, 0, len); | ||
125 | xfree(enc->iv); | ||
126 | xfree(enc->key); | ||
127 | enc->iv = NULL; | ||
128 | enc->key = NULL; | ||
129 | } | ||
130 | void | ||
131 | packet_set_ssh2_format(void) | ||
132 | { | ||
133 | debug("use_ssh2_packet_format"); | ||
134 | use_ssh2_packet_format = 1; | ||
135 | } | ||
136 | |||
84 | /* | 137 | /* |
85 | * Sets the descriptors used for communication. Disables encryption until | 138 | * Sets the descriptors used for communication. Disables encryption until |
86 | * packet_set_encryption_key is called. | 139 | * packet_set_encryption_key is called. |
87 | */ | 140 | */ |
88 | |||
89 | void | 141 | void |
90 | packet_set_connection(int fd_in, int fd_out) | 142 | packet_set_connection(int fd_in, int fd_out) |
91 | { | 143 | { |
@@ -225,6 +277,7 @@ packet_get_protocol_flags() | |||
225 | * Level is compression level 1 (fastest) - 9 (slow, best) as in gzip. | 277 | * Level is compression level 1 (fastest) - 9 (slow, best) as in gzip. |
226 | */ | 278 | */ |
227 | 279 | ||
280 | /*** XXXXX todo: kex means re-init */ | ||
228 | void | 281 | void |
229 | packet_start_compression(int level) | 282 | packet_start_compression(int level) |
230 | { | 283 | { |
@@ -242,7 +295,7 @@ packet_start_compression(int level) | |||
242 | 295 | ||
243 | void | 296 | void |
244 | packet_encrypt(CipherContext * cc, void *dest, void *src, | 297 | packet_encrypt(CipherContext * cc, void *dest, void *src, |
245 | unsigned int bytes) | 298 | unsigned int bytes) |
246 | { | 299 | { |
247 | cipher_encrypt(cc, dest, src, bytes); | 300 | cipher_encrypt(cc, dest, src, bytes); |
248 | } | 301 | } |
@@ -254,7 +307,7 @@ packet_encrypt(CipherContext * cc, void *dest, void *src, | |||
254 | 307 | ||
255 | void | 308 | void |
256 | packet_decrypt(CipherContext * cc, void *dest, void *src, | 309 | packet_decrypt(CipherContext * cc, void *dest, void *src, |
257 | unsigned int bytes) | 310 | unsigned int bytes) |
258 | { | 311 | { |
259 | int i; | 312 | int i; |
260 | 313 | ||
@@ -266,15 +319,11 @@ packet_decrypt(CipherContext * cc, void *dest, void *src, | |||
266 | * (C)1998 CORE-SDI, Buenos Aires Argentina Ariel Futoransky(futo@core-sdi.com) | 319 | * (C)1998 CORE-SDI, Buenos Aires Argentina Ariel Futoransky(futo@core-sdi.com) |
267 | */ | 320 | */ |
268 | 321 | ||
269 | switch (cc->type) { | 322 | if (cc->type == SSH_CIPHER_NONE || compat20) { |
270 | case SSH_CIPHER_NONE: | ||
271 | i = DEATTACK_OK; | 323 | i = DEATTACK_OK; |
272 | break; | 324 | } else { |
273 | default: | ||
274 | i = detect_attack(src, bytes, NULL); | 325 | i = detect_attack(src, bytes, NULL); |
275 | break; | ||
276 | } | 326 | } |
277 | |||
278 | if (i == DEATTACK_DETECTED) | 327 | if (i == DEATTACK_DETECTED) |
279 | packet_disconnect("crc32 compensation attack: network attack detected"); | 328 | packet_disconnect("crc32 compensation attack: network attack detected"); |
280 | 329 | ||
@@ -289,8 +338,11 @@ packet_decrypt(CipherContext * cc, void *dest, void *src, | |||
289 | 338 | ||
290 | void | 339 | void |
291 | packet_set_encryption_key(const unsigned char *key, unsigned int keylen, | 340 | packet_set_encryption_key(const unsigned char *key, unsigned int keylen, |
292 | int cipher) | 341 | int cipher) |
293 | { | 342 | { |
343 | if (keylen < 20) | ||
344 | fatal("keylen too small: %d", keylen); | ||
345 | |||
294 | /* All other ciphers use the same key in both directions for now. */ | 346 | /* All other ciphers use the same key in both directions for now. */ |
295 | cipher_set_key(&receive_context, cipher, key, keylen, 0); | 347 | cipher_set_key(&receive_context, cipher, key, keylen, 0); |
296 | cipher_set_key(&send_context, cipher, key, keylen, 1); | 348 | cipher_set_key(&send_context, cipher, key, keylen, 1); |
@@ -299,7 +351,7 @@ packet_set_encryption_key(const unsigned char *key, unsigned int keylen, | |||
299 | /* Starts constructing a packet to send. */ | 351 | /* Starts constructing a packet to send. */ |
300 | 352 | ||
301 | void | 353 | void |
302 | packet_start(int type) | 354 | packet_start1(int type) |
303 | { | 355 | { |
304 | char buf[9]; | 356 | char buf[9]; |
305 | 357 | ||
@@ -309,6 +361,29 @@ packet_start(int type) | |||
309 | buffer_append(&outgoing_packet, buf, 9); | 361 | buffer_append(&outgoing_packet, buf, 9); |
310 | } | 362 | } |
311 | 363 | ||
364 | void | ||
365 | packet_start2(int type) | ||
366 | { | ||
367 | char buf[4+1+1]; | ||
368 | |||
369 | buffer_clear(&outgoing_packet); | ||
370 | memset(buf, 0, sizeof buf); | ||
371 | /* buf[0..3] = payload_len; */ | ||
372 | /* buf[4] = pad_len; */ | ||
373 | buf[5] = type & 0xff; | ||
374 | buffer_append(&outgoing_packet, buf, sizeof buf); | ||
375 | } | ||
376 | |||
377 | void | ||
378 | packet_start(int type) | ||
379 | { | ||
380 | DBG(debug("packet_start[%d]",type)); | ||
381 | if (use_ssh2_packet_format) | ||
382 | packet_start2(type); | ||
383 | else | ||
384 | packet_start1(type); | ||
385 | } | ||
386 | |||
312 | /* Appends a character to the packet data. */ | 387 | /* Appends a character to the packet data. */ |
313 | 388 | ||
314 | void | 389 | void |
@@ -333,6 +408,18 @@ packet_put_string(const char *buf, unsigned int len) | |||
333 | { | 408 | { |
334 | buffer_put_string(&outgoing_packet, buf, len); | 409 | buffer_put_string(&outgoing_packet, buf, len); |
335 | } | 410 | } |
411 | void | ||
412 | packet_put_cstring(const char *str) | ||
413 | { | ||
414 | buffer_put_string(&outgoing_packet, str, strlen(str)); | ||
415 | } | ||
416 | |||
417 | void | ||
418 | packet_put_raw(const char *buf, unsigned int len) | ||
419 | { | ||
420 | buffer_append(&outgoing_packet, buf, len); | ||
421 | } | ||
422 | |||
336 | 423 | ||
337 | /* Appends an arbitrary precision integer to packet data. */ | 424 | /* Appends an arbitrary precision integer to packet data. */ |
338 | 425 | ||
@@ -341,6 +428,11 @@ packet_put_bignum(BIGNUM * value) | |||
341 | { | 428 | { |
342 | buffer_put_bignum(&outgoing_packet, value); | 429 | buffer_put_bignum(&outgoing_packet, value); |
343 | } | 430 | } |
431 | void | ||
432 | packet_put_bignum2(BIGNUM * value) | ||
433 | { | ||
434 | buffer_put_bignum2(&outgoing_packet, value); | ||
435 | } | ||
344 | 436 | ||
345 | /* | 437 | /* |
346 | * Finalizes and sends the packet. If the encryption key has been set, | 438 | * Finalizes and sends the packet. If the encryption key has been set, |
@@ -348,7 +440,7 @@ packet_put_bignum(BIGNUM * value) | |||
348 | */ | 440 | */ |
349 | 441 | ||
350 | void | 442 | void |
351 | packet_send() | 443 | packet_send1() |
352 | { | 444 | { |
353 | char buf[8], *cp; | 445 | char buf[8], *cp; |
354 | int i, padding, len; | 446 | int i, padding, len; |
@@ -419,6 +511,139 @@ packet_send() | |||
419 | } | 511 | } |
420 | 512 | ||
421 | /* | 513 | /* |
514 | * Finalize packet in SSH2 format (compress, mac, encrypt, enqueue) | ||
515 | */ | ||
516 | void | ||
517 | packet_send2() | ||
518 | { | ||
519 | unsigned char *macbuf = NULL; | ||
520 | char *cp; | ||
521 | unsigned int packet_length = 0; | ||
522 | unsigned int i, padlen, len; | ||
523 | u_int32_t rand = 0; | ||
524 | static unsigned int seqnr = 0; | ||
525 | int type; | ||
526 | Enc *enc = NULL; | ||
527 | Mac *mac = NULL; | ||
528 | Comp *comp = NULL; | ||
529 | int block_size; | ||
530 | |||
531 | if (kex != NULL) { | ||
532 | enc = &kex->enc[MODE_OUT]; | ||
533 | mac = &kex->mac[MODE_OUT]; | ||
534 | comp = &kex->comp[MODE_OUT]; | ||
535 | } | ||
536 | block_size = enc ? enc->block_size : 8; | ||
537 | |||
538 | cp = buffer_ptr(&outgoing_packet); | ||
539 | type = cp[5] & 0xff; | ||
540 | |||
541 | #ifdef PACKET_DEBUG | ||
542 | fprintf(stderr, "plain: "); | ||
543 | buffer_dump(&outgoing_packet); | ||
544 | #endif | ||
545 | |||
546 | if (comp && comp->enabled) { | ||
547 | len = buffer_len(&outgoing_packet); | ||
548 | /* skip header, compress only payload */ | ||
549 | buffer_consume(&outgoing_packet, 5); | ||
550 | buffer_clear(&compression_buffer); | ||
551 | buffer_compress(&outgoing_packet, &compression_buffer); | ||
552 | buffer_clear(&outgoing_packet); | ||
553 | buffer_append(&outgoing_packet, "\0\0\0\0\0", 5); | ||
554 | buffer_append(&outgoing_packet, buffer_ptr(&compression_buffer), | ||
555 | buffer_len(&compression_buffer)); | ||
556 | DBG(debug("compression: raw %d compressed %d", len, | ||
557 | buffer_len(&outgoing_packet))); | ||
558 | } | ||
559 | |||
560 | /* sizeof (packet_len + pad_len + payload) */ | ||
561 | len = buffer_len(&outgoing_packet); | ||
562 | |||
563 | /* | ||
564 | * calc size of padding, alloc space, get random data, | ||
565 | * minimum padding is 4 bytes | ||
566 | */ | ||
567 | padlen = block_size - (len % block_size); | ||
568 | if (padlen < 4) | ||
569 | padlen += block_size; | ||
570 | buffer_append_space(&outgoing_packet, &cp, padlen); | ||
571 | if (enc && enc->type != SSH_CIPHER_NONE) { | ||
572 | for (i = 0; i < padlen; i++) { | ||
573 | if (i % 4 == 0) | ||
574 | rand = arc4random(); | ||
575 | cp[i] = rand & 0xff; | ||
576 | rand <<= 8; | ||
577 | } | ||
578 | } | ||
579 | /* packet_length includes payload, padding and padding length field */ | ||
580 | packet_length = buffer_len(&outgoing_packet) - 4; | ||
581 | cp = buffer_ptr(&outgoing_packet); | ||
582 | PUT_32BIT(cp, packet_length); | ||
583 | cp[4] = padlen & 0xff; | ||
584 | DBG(debug("send: len %d (includes padlen %d)", packet_length+4, padlen)); | ||
585 | |||
586 | /* compute MAC over seqnr and packet(length fields, payload, padding) */ | ||
587 | if (mac && mac->enabled) { | ||
588 | macbuf = hmac( mac->md, seqnr, | ||
589 | (unsigned char *) buffer_ptr(&outgoing_packet), | ||
590 | buffer_len(&outgoing_packet), | ||
591 | mac->key, mac->key_len | ||
592 | ); | ||
593 | DBG(debug("done calc HMAC out #%d", seqnr)); | ||
594 | } | ||
595 | /* encrypt packet and append to output buffer. */ | ||
596 | buffer_append_space(&output, &cp, buffer_len(&outgoing_packet)); | ||
597 | packet_encrypt(&send_context, cp, buffer_ptr(&outgoing_packet), | ||
598 | buffer_len(&outgoing_packet)); | ||
599 | /* append unencrypted MAC */ | ||
600 | if (mac && mac->enabled) | ||
601 | buffer_append(&output, (char *)macbuf, mac->mac_len); | ||
602 | #ifdef PACKET_DEBUG | ||
603 | fprintf(stderr, "encrypted: "); | ||
604 | buffer_dump(&output); | ||
605 | #endif | ||
606 | /* increment sequence number for outgoing packets */ | ||
607 | if (++seqnr == 0) | ||
608 | log("outgoing seqnr wraps around"); | ||
609 | buffer_clear(&outgoing_packet); | ||
610 | |||
611 | if (type == SSH2_MSG_NEWKEYS) { | ||
612 | if (kex==NULL || mac==NULL || enc==NULL || comp==NULL) | ||
613 | fatal("packet_send2: no KEX"); | ||
614 | if (mac->md != NULL) | ||
615 | mac->enabled = 1; | ||
616 | debug("cipher_set_key_iv send_context"); | ||
617 | cipher_set_key_iv(&send_context, enc->type, | ||
618 | enc->key, enc->key_len, | ||
619 | enc->iv, enc->iv_len); | ||
620 | clear_enc_keys(enc, kex->we_need); | ||
621 | if (comp->type != 0 && comp->enabled == 0) { | ||
622 | comp->enabled = 1; | ||
623 | if (! packet_compression) | ||
624 | packet_start_compression(6); | ||
625 | } | ||
626 | } | ||
627 | } | ||
628 | |||
629 | void | ||
630 | packet_send() | ||
631 | { | ||
632 | if (use_ssh2_packet_format) | ||
633 | packet_send2(); | ||
634 | else | ||
635 | packet_send1(); | ||
636 | DBG(debug("packet_send done")); | ||
637 | } | ||
638 | |||
639 | void | ||
640 | packet_send_and_wait() | ||
641 | { | ||
642 | packet_send(); | ||
643 | packet_write_wait(); | ||
644 | } | ||
645 | |||
646 | /* | ||
422 | * Waits until a packet has been received, and returns its type. Note that | 647 | * Waits until a packet has been received, and returns its type. Note that |
423 | * no other data is processed until this returns, so this function should not | 648 | * no other data is processed until this returns, so this function should not |
424 | * be used during the interactive session. | 649 | * be used during the interactive session. |
@@ -430,6 +655,7 @@ packet_read(int *payload_len_ptr) | |||
430 | int type, len; | 655 | int type, len; |
431 | fd_set set; | 656 | fd_set set; |
432 | char buf[8192]; | 657 | char buf[8192]; |
658 | DBG(debug("packet_read()")); | ||
433 | 659 | ||
434 | /* Since we are blocking, ensure that all written packets have been sent. */ | 660 | /* Since we are blocking, ensure that all written packets have been sent. */ |
435 | packet_write_wait(); | 661 | packet_write_wait(); |
@@ -483,7 +709,7 @@ packet_read_expect(int *payload_len_ptr, int expected_type) | |||
483 | type = packet_read(payload_len_ptr); | 709 | type = packet_read(payload_len_ptr); |
484 | if (type != expected_type) | 710 | if (type != expected_type) |
485 | packet_disconnect("Protocol error: expected packet type %d, got %d", | 711 | packet_disconnect("Protocol error: expected packet type %d, got %d", |
486 | expected_type, type); | 712 | expected_type, type); |
487 | } | 713 | } |
488 | 714 | ||
489 | /* Checks if a full packet is available in the data received so far via | 715 | /* Checks if a full packet is available in the data received so far via |
@@ -502,15 +728,13 @@ packet_read_expect(int *payload_len_ptr, int expected_type) | |||
502 | */ | 728 | */ |
503 | 729 | ||
504 | int | 730 | int |
505 | packet_read_poll(int *payload_len_ptr) | 731 | packet_read_poll1(int *payload_len_ptr) |
506 | { | 732 | { |
507 | unsigned int len, padded_len; | 733 | unsigned int len, padded_len; |
508 | unsigned char *ucp; | 734 | unsigned char *ucp; |
509 | char buf[8], *cp, *msg; | 735 | char buf[8], *cp; |
510 | unsigned int checksum, stored_checksum; | 736 | unsigned int checksum, stored_checksum; |
511 | 737 | ||
512 | restart: | ||
513 | |||
514 | /* Check if input size is less than minimum packet size. */ | 738 | /* Check if input size is less than minimum packet size. */ |
515 | if (buffer_len(&input) < 4 + 8) | 739 | if (buffer_len(&input) < 4 + 8) |
516 | return SSH_MSG_NONE; | 740 | return SSH_MSG_NONE; |
@@ -543,7 +767,7 @@ restart: | |||
543 | 767 | ||
544 | /* Compute packet checksum. */ | 768 | /* Compute packet checksum. */ |
545 | checksum = crc32((unsigned char *) buffer_ptr(&incoming_packet), | 769 | checksum = crc32((unsigned char *) buffer_ptr(&incoming_packet), |
546 | buffer_len(&incoming_packet) - 4); | 770 | buffer_len(&incoming_packet) - 4); |
547 | 771 | ||
548 | /* Skip padding. */ | 772 | /* Skip padding. */ |
549 | buffer_consume(&incoming_packet, 8 - len % 8); | 773 | buffer_consume(&incoming_packet, 8 - len % 8); |
@@ -552,7 +776,7 @@ restart: | |||
552 | 776 | ||
553 | if (len != buffer_len(&incoming_packet)) | 777 | if (len != buffer_len(&incoming_packet)) |
554 | packet_disconnect("packet_read_poll: len %d != buffer_len %d.", | 778 | packet_disconnect("packet_read_poll: len %d != buffer_len %d.", |
555 | len, buffer_len(&incoming_packet)); | 779 | len, buffer_len(&incoming_packet)); |
556 | 780 | ||
557 | ucp = (unsigned char *) buffer_ptr(&incoming_packet) + len - 4; | 781 | ucp = (unsigned char *) buffer_ptr(&incoming_packet) + len - 4; |
558 | stored_checksum = GET_32BIT(ucp); | 782 | stored_checksum = GET_32BIT(ucp); |
@@ -566,7 +790,7 @@ restart: | |||
566 | buffer_uncompress(&incoming_packet, &compression_buffer); | 790 | buffer_uncompress(&incoming_packet, &compression_buffer); |
567 | buffer_clear(&incoming_packet); | 791 | buffer_clear(&incoming_packet); |
568 | buffer_append(&incoming_packet, buffer_ptr(&compression_buffer), | 792 | buffer_append(&incoming_packet, buffer_ptr(&compression_buffer), |
569 | buffer_len(&compression_buffer)); | 793 | buffer_len(&compression_buffer)); |
570 | } | 794 | } |
571 | /* Get packet type. */ | 795 | /* Get packet type. */ |
572 | buffer_get(&incoming_packet, &buf[0], 1); | 796 | buffer_get(&incoming_packet, &buf[0], 1); |
@@ -574,29 +798,208 @@ restart: | |||
574 | /* Return length of payload (without type field). */ | 798 | /* Return length of payload (without type field). */ |
575 | *payload_len_ptr = buffer_len(&incoming_packet); | 799 | *payload_len_ptr = buffer_len(&incoming_packet); |
576 | 800 | ||
577 | /* Handle disconnect message. */ | ||
578 | if ((unsigned char) buf[0] == SSH_MSG_DISCONNECT) { | ||
579 | msg = packet_get_string(NULL); | ||
580 | log("Received disconnect: %.900s", msg); | ||
581 | xfree(msg); | ||
582 | fatal_cleanup(); | ||
583 | } | ||
584 | |||
585 | /* Ignore ignore messages. */ | ||
586 | if ((unsigned char) buf[0] == SSH_MSG_IGNORE) | ||
587 | goto restart; | ||
588 | |||
589 | /* Send debug messages as debugging output. */ | ||
590 | if ((unsigned char) buf[0] == SSH_MSG_DEBUG) { | ||
591 | msg = packet_get_string(NULL); | ||
592 | debug("Remote: %.900s", msg); | ||
593 | xfree(msg); | ||
594 | goto restart; | ||
595 | } | ||
596 | /* Return type. */ | 801 | /* Return type. */ |
597 | return (unsigned char) buf[0]; | 802 | return (unsigned char) buf[0]; |
598 | } | 803 | } |
599 | 804 | ||
805 | int | ||
806 | packet_read_poll2(int *payload_len_ptr) | ||
807 | { | ||
808 | unsigned int padlen, need; | ||
809 | unsigned char buf[8], *macbuf; | ||
810 | unsigned char *ucp; | ||
811 | char *cp; | ||
812 | static unsigned int packet_length = 0; | ||
813 | static unsigned int seqnr = 0; | ||
814 | int type; | ||
815 | int maclen, block_size; | ||
816 | Enc *enc = NULL; | ||
817 | Mac *mac = NULL; | ||
818 | Comp *comp = NULL; | ||
819 | |||
820 | if (kex != NULL) { | ||
821 | enc = &kex->enc[MODE_IN]; | ||
822 | mac = &kex->mac[MODE_IN]; | ||
823 | comp = &kex->comp[MODE_IN]; | ||
824 | } | ||
825 | maclen = mac && mac->enabled ? mac->mac_len : 0; | ||
826 | block_size = enc ? enc->block_size : 8; | ||
827 | |||
828 | if (packet_length == 0) { | ||
829 | /* | ||
830 | * check if input size is less than the cipher block size, | ||
831 | * decrypt first block and extract length of incoming packet | ||
832 | */ | ||
833 | if (buffer_len(&input) < block_size) | ||
834 | return SSH_MSG_NONE; | ||
835 | buffer_clear(&incoming_packet); | ||
836 | buffer_append_space(&incoming_packet, &cp, block_size); | ||
837 | packet_decrypt(&receive_context, cp, buffer_ptr(&input), | ||
838 | block_size); | ||
839 | ucp = (unsigned char *) buffer_ptr(&incoming_packet); | ||
840 | packet_length = GET_32BIT(ucp); | ||
841 | if (packet_length < 1 + 4 || packet_length > 256 * 1024) { | ||
842 | buffer_dump(&incoming_packet); | ||
843 | packet_disconnect("Bad packet length %d.", packet_length); | ||
844 | } | ||
845 | DBG(debug("input: packet len %d", packet_length+4)); | ||
846 | buffer_consume(&input, block_size); | ||
847 | } | ||
848 | /* we have a partial packet of block_size bytes */ | ||
849 | need = 4 + packet_length - block_size; | ||
850 | DBG(debug("partial packet %d, need %d, maclen %d", block_size, | ||
851 | need, maclen)); | ||
852 | if (need % block_size != 0) | ||
853 | fatal("padding error: need %d block %d mod %d", | ||
854 | need, block_size, need % block_size); | ||
855 | /* | ||
856 | * check if the entire packet has been received and | ||
857 | * decrypt into incoming_packet | ||
858 | */ | ||
859 | if (buffer_len(&input) < need + maclen) | ||
860 | return SSH_MSG_NONE; | ||
861 | #ifdef PACKET_DEBUG | ||
862 | fprintf(stderr, "read_poll enc/full: "); | ||
863 | buffer_dump(&input); | ||
864 | #endif | ||
865 | buffer_append_space(&incoming_packet, &cp, need); | ||
866 | packet_decrypt(&receive_context, cp, buffer_ptr(&input), need); | ||
867 | buffer_consume(&input, need); | ||
868 | /* | ||
869 | * compute MAC over seqnr and packet, | ||
870 | * increment sequence number for incoming packet | ||
871 | */ | ||
872 | if (mac && mac->enabled) { | ||
873 | macbuf = hmac( mac->md, seqnr, | ||
874 | (unsigned char *) buffer_ptr(&incoming_packet), | ||
875 | buffer_len(&incoming_packet), | ||
876 | mac->key, mac->key_len | ||
877 | ); | ||
878 | if (memcmp(macbuf, buffer_ptr(&input), mac->mac_len) != 0) | ||
879 | packet_disconnect("Corrupted HMAC on input."); | ||
880 | DBG(debug("HMAC #%d ok", seqnr)); | ||
881 | buffer_consume(&input, mac->mac_len); | ||
882 | } | ||
883 | if (++seqnr == 0) | ||
884 | log("incoming seqnr wraps around"); | ||
885 | |||
886 | /* get padlen */ | ||
887 | cp = buffer_ptr(&incoming_packet) + 4; | ||
888 | padlen = *cp & 0xff; | ||
889 | DBG(debug("input: padlen %d", padlen)); | ||
890 | if (padlen < 4) | ||
891 | packet_disconnect("Corrupted padlen %d on input.", padlen); | ||
892 | |||
893 | /* skip packet size + padlen, discard padding */ | ||
894 | buffer_consume(&incoming_packet, 4 + 1); | ||
895 | buffer_consume_end(&incoming_packet, padlen); | ||
896 | |||
897 | DBG(debug("input: len before de-compress %d", buffer_len(&incoming_packet))); | ||
898 | if (comp && comp->enabled) { | ||
899 | buffer_clear(&compression_buffer); | ||
900 | buffer_uncompress(&incoming_packet, &compression_buffer); | ||
901 | buffer_clear(&incoming_packet); | ||
902 | buffer_append(&incoming_packet, buffer_ptr(&compression_buffer), | ||
903 | buffer_len(&compression_buffer)); | ||
904 | DBG(debug("input: len after de-compress %d", buffer_len(&incoming_packet))); | ||
905 | } | ||
906 | /* | ||
907 | * get packet type, implies consume. | ||
908 | * return length of payload (without type field) | ||
909 | */ | ||
910 | buffer_get(&incoming_packet, (char *)&buf[0], 1); | ||
911 | *payload_len_ptr = buffer_len(&incoming_packet); | ||
912 | |||
913 | /* reset for next packet */ | ||
914 | packet_length = 0; | ||
915 | |||
916 | /* extract packet type */ | ||
917 | type = (unsigned char)buf[0]; | ||
918 | |||
919 | if (type == SSH2_MSG_NEWKEYS) { | ||
920 | if (kex==NULL || mac==NULL || enc==NULL || comp==NULL) | ||
921 | fatal("packet_read_poll2: no KEX"); | ||
922 | if (mac->md != NULL) | ||
923 | mac->enabled = 1; | ||
924 | debug("cipher_set_key_iv receive_context"); | ||
925 | cipher_set_key_iv(&receive_context, enc->type, | ||
926 | enc->key, enc->key_len, | ||
927 | enc->iv, enc->iv_len); | ||
928 | clear_enc_keys(enc, kex->we_need); | ||
929 | if (comp->type != 0 && comp->enabled == 0) { | ||
930 | comp->enabled = 1; | ||
931 | if (! packet_compression) | ||
932 | packet_start_compression(6); | ||
933 | } | ||
934 | } | ||
935 | |||
936 | #ifdef PACKET_DEBUG | ||
937 | fprintf(stderr, "read/plain[%d]:\r\n",type); | ||
938 | buffer_dump(&incoming_packet); | ||
939 | #endif | ||
940 | return (unsigned char)type; | ||
941 | } | ||
942 | |||
943 | int | ||
944 | packet_read_poll(int *payload_len_ptr) | ||
945 | { | ||
946 | char *msg; | ||
947 | for (;;) { | ||
948 | int type = use_ssh2_packet_format ? | ||
949 | packet_read_poll2(payload_len_ptr): | ||
950 | packet_read_poll1(payload_len_ptr); | ||
951 | |||
952 | if(compat20) { | ||
953 | int reason; | ||
954 | if (type != 0) | ||
955 | DBG(debug("received packet type %d", type)); | ||
956 | switch(type) { | ||
957 | case SSH2_MSG_IGNORE: | ||
958 | break; | ||
959 | case SSH2_MSG_DEBUG: | ||
960 | packet_get_char(); | ||
961 | msg = packet_get_string(NULL); | ||
962 | debug("Remote: %.900s", msg); | ||
963 | xfree(msg); | ||
964 | msg = packet_get_string(NULL); | ||
965 | xfree(msg); | ||
966 | break; | ||
967 | case SSH2_MSG_DISCONNECT: | ||
968 | reason = packet_get_int(); | ||
969 | msg = packet_get_string(NULL); | ||
970 | log("Received disconnect: %d: %.900s", reason, msg); | ||
971 | xfree(msg); | ||
972 | fatal_cleanup(); | ||
973 | break; | ||
974 | default: | ||
975 | return type; | ||
976 | break; | ||
977 | } | ||
978 | } else { | ||
979 | switch(type) { | ||
980 | case SSH_MSG_IGNORE: | ||
981 | break; | ||
982 | case SSH_MSG_DEBUG: | ||
983 | msg = packet_get_string(NULL); | ||
984 | debug("Remote: %.900s", msg); | ||
985 | xfree(msg); | ||
986 | break; | ||
987 | case SSH_MSG_DISCONNECT: | ||
988 | msg = packet_get_string(NULL); | ||
989 | log("Received disconnect: %.900s", msg); | ||
990 | fatal_cleanup(); | ||
991 | xfree(msg); | ||
992 | break; | ||
993 | default: | ||
994 | if (type != 0) | ||
995 | DBG(debug("received packet type %d", type)); | ||
996 | return type; | ||
997 | break; | ||
998 | } | ||
999 | } | ||
1000 | } | ||
1001 | } | ||
1002 | |||
600 | /* | 1003 | /* |
601 | * Buffers the given amount of input characters. This is intended to be used | 1004 | * Buffers the given amount of input characters. This is intended to be used |
602 | * together with packet_read_poll. | 1005 | * together with packet_read_poll. |
@@ -637,6 +1040,21 @@ packet_get_bignum(BIGNUM * value, int *length_ptr) | |||
637 | *length_ptr = buffer_get_bignum(&incoming_packet, value); | 1040 | *length_ptr = buffer_get_bignum(&incoming_packet, value); |
638 | } | 1041 | } |
639 | 1042 | ||
1043 | void | ||
1044 | packet_get_bignum2(BIGNUM * value, int *length_ptr) | ||
1045 | { | ||
1046 | *length_ptr = buffer_get_bignum2(&incoming_packet, value); | ||
1047 | } | ||
1048 | |||
1049 | char * | ||
1050 | packet_get_raw(int *length_ptr) | ||
1051 | { | ||
1052 | int bytes = buffer_len(&incoming_packet); | ||
1053 | if (length_ptr != NULL) | ||
1054 | *length_ptr = bytes; | ||
1055 | return buffer_ptr(&incoming_packet); | ||
1056 | } | ||
1057 | |||
640 | /* | 1058 | /* |
641 | * Returns a string from the packet data. The string is allocated using | 1059 | * Returns a string from the packet data. The string is allocated using |
642 | * xmalloc; it is the responsibility of the calling program to free it when | 1060 | * xmalloc; it is the responsibility of the calling program to free it when |
@@ -701,8 +1119,15 @@ packet_disconnect(const char *fmt,...) | |||
701 | va_end(args); | 1119 | va_end(args); |
702 | 1120 | ||
703 | /* Send the disconnect message to the other side, and wait for it to get sent. */ | 1121 | /* Send the disconnect message to the other side, and wait for it to get sent. */ |
704 | packet_start(SSH_MSG_DISCONNECT); | 1122 | if (compat20) { |
705 | packet_put_string(buf, strlen(buf)); | 1123 | packet_start(SSH2_MSG_DISCONNECT); |
1124 | packet_put_int(SSH2_DISCONNECT_PROTOCOL_ERROR); | ||
1125 | packet_put_cstring(buf); | ||
1126 | packet_put_cstring(""); | ||
1127 | } else { | ||
1128 | packet_start(SSH_MSG_DISCONNECT); | ||
1129 | packet_put_string(buf, strlen(buf)); | ||
1130 | } | ||
706 | packet_send(); | 1131 | packet_send(); |
707 | packet_write_wait(); | 1132 | packet_write_wait(); |
708 | 1133 | ||
@@ -833,7 +1258,8 @@ packet_set_maxsize(int s) | |||
833 | { | 1258 | { |
834 | static int called = 0; | 1259 | static int called = 0; |
835 | if (called) { | 1260 | if (called) { |
836 | log("packet_set_maxsize: called twice: old %d new %d", max_packet_size, s); | 1261 | log("packet_set_maxsize: called twice: old %d new %d", |
1262 | max_packet_size, s); | ||
837 | return -1; | 1263 | return -1; |
838 | } | 1264 | } |
839 | if (s < 4 * 1024 || s > 1024 * 1024) { | 1265 | if (s < 4 * 1024 || s > 1024 * 1024) { |
@@ -13,7 +13,7 @@ | |||
13 | * | 13 | * |
14 | */ | 14 | */ |
15 | 15 | ||
16 | /* RCSID("$Id: packet.h,v 1.10 2000/03/17 12:40:16 damien Exp $"); */ | 16 | /* RCSID("$Id: packet.h,v 1.11 2000/04/04 04:39:03 damien Exp $"); */ |
17 | 17 | ||
18 | #ifndef PACKET_H | 18 | #ifndef PACKET_H |
19 | #define PACKET_H | 19 | #define PACKET_H |
@@ -90,9 +90,12 @@ void packet_put_int(unsigned int value); | |||
90 | 90 | ||
91 | /* Appends an arbitrary precision integer to packet data. */ | 91 | /* Appends an arbitrary precision integer to packet data. */ |
92 | void packet_put_bignum(BIGNUM * value); | 92 | void packet_put_bignum(BIGNUM * value); |
93 | void packet_put_bignum2(BIGNUM * value); | ||
93 | 94 | ||
94 | /* Appends a string to packet data. */ | 95 | /* Appends a string to packet data. */ |
95 | void packet_put_string(const char *buf, unsigned int len); | 96 | void packet_put_string(const char *buf, unsigned int len); |
97 | void packet_put_cstring(const char *str); | ||
98 | void packet_put_raw(const char *buf, unsigned int len); | ||
96 | 99 | ||
97 | /* | 100 | /* |
98 | * Finalizes and sends the packet. If the encryption key has been set, | 101 | * Finalizes and sends the packet. If the encryption key has been set, |
@@ -136,6 +139,8 @@ unsigned int packet_get_int(void); | |||
136 | * must have been initialized before this call. | 139 | * must have been initialized before this call. |
137 | */ | 140 | */ |
138 | void packet_get_bignum(BIGNUM * value, int *length_ptr); | 141 | void packet_get_bignum(BIGNUM * value, int *length_ptr); |
142 | void packet_get_bignum2(BIGNUM * value, int *length_ptr); | ||
143 | char *packet_get_raw(int *length_ptr); | ||
139 | 144 | ||
140 | /* | 145 | /* |
141 | * Returns a string from the packet data. The string is allocated using | 146 | * Returns a string from the packet data. The string is allocated using |
@@ -202,4 +207,7 @@ do { \ | |||
202 | int packet_connection_is_on_socket(void); | 207 | int packet_connection_is_on_socket(void); |
203 | int packet_connection_is_ipv4(void); | 208 | int packet_connection_is_ipv4(void); |
204 | 209 | ||
210 | /* enable SSH2 packet format */ | ||
211 | void packet_set_ssh2_format(void); | ||
212 | |||
205 | #endif /* PACKET_H */ | 213 | #endif /* PACKET_H */ |