diff options
Diffstat (limited to 'channels.c')
-rw-r--r-- | channels.c | 160 |
1 files changed, 149 insertions, 11 deletions
diff --git a/channels.c b/channels.c index 4283abc3a..a1aa937ae 100644 --- a/channels.c +++ b/channels.c | |||
@@ -40,7 +40,7 @@ | |||
40 | */ | 40 | */ |
41 | 41 | ||
42 | #include "includes.h" | 42 | #include "includes.h" |
43 | RCSID("$OpenBSD: channels.c,v 1.105 2001/04/10 12:15:23 markus Exp $"); | 43 | RCSID("$OpenBSD: channels.c,v 1.106 2001/04/11 13:56:13 markus Exp $"); |
44 | 44 | ||
45 | #include <openssl/rsa.h> | 45 | #include <openssl/rsa.h> |
46 | #include <openssl/dsa.h> | 46 | #include <openssl/dsa.h> |
@@ -576,6 +576,39 @@ channel_decode_helper(Channel *c, int start, int lookfor) | |||
576 | return 0; /* need more */ | 576 | return 0; /* need more */ |
577 | } | 577 | } |
578 | 578 | ||
579 | /* try to decode a http connect header */ | ||
580 | int | ||
581 | channel_decode_https(Channel *c, fd_set * readset, fd_set * writeset) | ||
582 | { | ||
583 | u_char *p, *host, *buf; | ||
584 | int port, ret; | ||
585 | char httpok[] = "HTTP/1.0 200\r\n\r\n"; | ||
586 | |||
587 | debug2("channel %d: decode https connect", c->self); | ||
588 | ret = channel_decode_helper(c, strlen("connect "), '\r'); | ||
589 | if (ret <= 0) | ||
590 | return ret; | ||
591 | p = buffer_ptr(&c->input); | ||
592 | buf = xmalloc(ret+1); | ||
593 | host = xmalloc(ret); | ||
594 | memcpy(buf, p, ret); | ||
595 | buf[ret] = '\0'; | ||
596 | if (sscanf(buf, "CONNECT %[^:]:%u HTTP/", host, &port) != 2) { | ||
597 | debug("channel %d: cannot parse http header", c->self); | ||
598 | return -1; | ||
599 | } | ||
600 | debug("channel %d: dynamic request: https host %s port %u", | ||
601 | c->self, host, port); | ||
602 | strlcpy(c->path, host, sizeof(c->path)); | ||
603 | c->host_port = port; | ||
604 | xfree(host); | ||
605 | xfree(buf); | ||
606 | buffer_consume(&c->input, ret+4); | ||
607 | buffer_append(&c->output, httpok, strlen(httpok)); | ||
608 | |||
609 | return 1; | ||
610 | } | ||
611 | |||
579 | /* try to decode a socks4 header */ | 612 | /* try to decode a socks4 header */ |
580 | int | 613 | int |
581 | channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) | 614 | channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) |
@@ -601,7 +634,7 @@ channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) | |||
601 | p = buffer_ptr(&c->input); | 634 | p = buffer_ptr(&c->input); |
602 | len = strlen(p); | 635 | len = strlen(p); |
603 | have = buffer_len(&c->input); | 636 | have = buffer_len(&c->input); |
604 | debug2("channel %d: pre_dynamic: user %s/%d", c->self, p, len); | 637 | debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); |
605 | if (len > have) | 638 | if (len > have) |
606 | fatal("channel %d: decode socks4: len %d > have %d", | 639 | fatal("channel %d: decode socks4: len %d > have %d", |
607 | c->self, len, have); | 640 | c->self, len, have); |
@@ -613,10 +646,8 @@ channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) | |||
613 | strlcpy(c->path, host, sizeof(c->path)); | 646 | strlcpy(c->path, host, sizeof(c->path)); |
614 | c->host_port = ntohs(s4_req.dest_port); | 647 | c->host_port = ntohs(s4_req.dest_port); |
615 | 648 | ||
616 | debug("channel %d: dynamic request: " | 649 | debug("channel %d: dynamic request: socks4 host %s port %u command %u", |
617 | "socks%x://%s@%s:%u/command?%u", | 650 | c->self, host, c->host_port, s4_req.command); |
618 | c->self, s4_req.version, username, host, c->host_port, | ||
619 | s4_req.command); | ||
620 | 651 | ||
621 | if (s4_req.command != 1) { | 652 | if (s4_req.command != 1) { |
622 | debug("channel %d: cannot handle: socks4 cn %d", | 653 | debug("channel %d: cannot handle: socks4 cn %d", |
@@ -631,6 +662,115 @@ channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) | |||
631 | return 1; | 662 | return 1; |
632 | } | 663 | } |
633 | 664 | ||
665 | /* try to decode a socks5 header */ | ||
666 | #define SSH_SOCKS5_AUTHDONE 0x1000 | ||
667 | #define SSH_SOCKS5_NOAUTH 0x00 | ||
668 | #define SSH_SOCKS5_IPV4 0x01 | ||
669 | #define SSH_SOCKS5_DOMAIN 0x03 | ||
670 | #define SSH_SOCKS5_IPV6 0x04 | ||
671 | #define SSH_SOCKS5_CONNECT 0x01 | ||
672 | #define SSH_SOCKS5_SUCCESS 0x00 | ||
673 | |||
674 | int | ||
675 | channel_decode_socks5(Channel *c, fd_set * readset, fd_set * writeset) | ||
676 | { | ||
677 | struct { | ||
678 | u_int8_t version; | ||
679 | u_int8_t command; | ||
680 | u_int8_t reserved; | ||
681 | u_int8_t atyp; | ||
682 | } s5_req, s5_rsp; | ||
683 | u_int16_t dest_port; | ||
684 | u_char *p, dest_addr[255+1]; | ||
685 | int i, have, found, nmethods, addrlen, af; | ||
686 | |||
687 | debug2("channel %d: decode socks5", c->self); | ||
688 | p = buffer_ptr(&c->input); | ||
689 | if (p[0] != 0x05) | ||
690 | return -1; | ||
691 | have = buffer_len(&c->input); | ||
692 | if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { | ||
693 | /* format: ver | nmethods | methods */ | ||
694 | if (have < 2) | ||
695 | return 0; | ||
696 | nmethods = p[1]; | ||
697 | if (have < nmethods + 2) | ||
698 | return 0; | ||
699 | /* look for method: "NO AUTHENTICATION REQUIRED" */ | ||
700 | for (found = 0, i = 2 ; i < nmethods + 2; i++) { | ||
701 | if (p[i] == SSH_SOCKS5_NOAUTH ) { | ||
702 | found = 1; | ||
703 | break; | ||
704 | } | ||
705 | } | ||
706 | if (!found) { | ||
707 | debug("channel %d: method SSH_SOCKS5_NOAUTH not found", | ||
708 | c->self); | ||
709 | return -1; | ||
710 | } | ||
711 | buffer_consume(&c->input, nmethods + 2); | ||
712 | buffer_put_char(&c->output, 0x05); /* version */ | ||
713 | buffer_put_char(&c->output, SSH_SOCKS5_NOAUTH); /* method */ | ||
714 | FD_SET(c->sock, writeset); | ||
715 | c->flags |= SSH_SOCKS5_AUTHDONE; | ||
716 | debug2("channel %d: socks5 auth done", c->self); | ||
717 | return 0; /* need more */ | ||
718 | } | ||
719 | debug2("channel %d: socks5 post auth", c->self); | ||
720 | if (have < sizeof(s5_req)+1) | ||
721 | return 0; /* need more */ | ||
722 | memcpy((char *)&s5_req, p, sizeof(s5_req)); | ||
723 | if (s5_req.version != 0x05 || | ||
724 | s5_req.command != SSH_SOCKS5_CONNECT || | ||
725 | s5_req.reserved != 0x00) { | ||
726 | debug("channel %d: only socks5 connect supported", c->self); | ||
727 | return -1; | ||
728 | } | ||
729 | switch(s5_req.atyp){ | ||
730 | case SSH_SOCKS5_IPV4: | ||
731 | addrlen = 4; | ||
732 | af = AF_INET; | ||
733 | break; | ||
734 | case SSH_SOCKS5_DOMAIN: | ||
735 | addrlen = p[sizeof(s5_req)]; | ||
736 | af = -1; | ||
737 | break; | ||
738 | case SSH_SOCKS5_IPV6: | ||
739 | addrlen = 16; | ||
740 | af = AF_INET6; | ||
741 | break; | ||
742 | default: | ||
743 | debug("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); | ||
744 | return -1; | ||
745 | } | ||
746 | if (have < 4 + addrlen + 2) | ||
747 | return 0; | ||
748 | buffer_consume(&c->input, sizeof(s5_req)); | ||
749 | buffer_get(&c->input, (char *)&dest_addr, addrlen); | ||
750 | buffer_get(&c->input, (char *)&dest_port, 2); | ||
751 | dest_addr[addrlen] = '\0'; | ||
752 | if (s5_req.atyp == SSH_SOCKS5_DOMAIN) | ||
753 | strlcpy(c->path, dest_addr, sizeof(c->path)); | ||
754 | else if (inet_ntop(af, dest_addr, c->path, sizeof(c->path)) == NULL) | ||
755 | return -1; | ||
756 | c->host_port = ntohs(dest_port); | ||
757 | |||
758 | debug("channel %d: dynamic request: socks5 host %s port %u command %u", | ||
759 | c->self, c->path, c->host_port, s5_req.command); | ||
760 | |||
761 | s5_rsp.version = 0x05; | ||
762 | s5_rsp.command = SSH_SOCKS5_SUCCESS; | ||
763 | s5_rsp.reserved = 0; /* ignored */ | ||
764 | s5_rsp.atyp = SSH_SOCKS5_IPV4; | ||
765 | ((struct in_addr *)&dest_addr)->s_addr = INADDR_ANY; | ||
766 | dest_port = 0; /* ignored */ | ||
767 | |||
768 | buffer_append(&c->output, (char *)&s5_rsp, sizeof(s5_rsp)); | ||
769 | buffer_append(&c->output, (char *)&dest_addr, sizeof(struct in_addr)); | ||
770 | buffer_append(&c->output, (char *)&dest_port, sizeof(dest_port)); | ||
771 | return 1; | ||
772 | } | ||
773 | |||
634 | /* dynamic port forwarding */ | 774 | /* dynamic port forwarding */ |
635 | void | 775 | void |
636 | channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) | 776 | channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) |
@@ -651,17 +791,15 @@ channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) | |||
651 | /* try to guess the protocol */ | 791 | /* try to guess the protocol */ |
652 | p = buffer_ptr(&c->input); | 792 | p = buffer_ptr(&c->input); |
653 | switch (p[0]) { | 793 | switch (p[0]) { |
654 | case 0x04: | ||
655 | ret = channel_decode_socks4(c, readset, writeset); | ||
656 | break; | ||
657 | #if 0 | ||
658 | case 'C': | 794 | case 'C': |
659 | ret = channel_decode_https(c, readset, writeset); | 795 | ret = channel_decode_https(c, readset, writeset); |
660 | break; | 796 | break; |
797 | case 0x04: | ||
798 | ret = channel_decode_socks4(c, readset, writeset); | ||
799 | break; | ||
661 | case 0x05: | 800 | case 0x05: |
662 | ret = channel_decode_socks5(c, readset, writeset); | 801 | ret = channel_decode_socks5(c, readset, writeset); |
663 | break; | 802 | break; |
664 | #endif | ||
665 | default: | 803 | default: |
666 | ret = -1; | 804 | ret = -1; |
667 | break; | 805 | break; |