diff options
author | Darren Tucker <dtucker@zip.com.au> | 2003-07-03 13:55:19 +1000 |
---|---|---|
committer | Darren Tucker <dtucker@zip.com.au> | 2003-07-03 13:55:19 +1000 |
commit | 46471c9a81bdd0d797149a20364645bc6ffcf2cc (patch) | |
tree | b21df0ef269b109a7dd6c6baaf8477ae87f7d478 /channels.c | |
parent | 9189ff89c3c15f152d8daedb09c4101a96365da4 (diff) |
- markus@cvs.openbsd.org 2003/07/02 14:51:16
[channels.c ssh.1 ssh_config.5]
(re)add socks5 suppport to -D; ok djm@
now ssh(1) can act both as a socks 4 and socks 5 server and
dynamically forward ports.
Diffstat (limited to 'channels.c')
-rw-r--r-- | channels.c | 120 |
1 files changed, 117 insertions, 3 deletions
diff --git a/channels.c b/channels.c index ce07db5c0..e5b2b8c51 100644 --- a/channels.c +++ b/channels.c | |||
@@ -39,7 +39,7 @@ | |||
39 | */ | 39 | */ |
40 | 40 | ||
41 | #include "includes.h" | 41 | #include "includes.h" |
42 | RCSID("$OpenBSD: channels.c,v 1.192 2003/07/02 12:56:34 markus Exp $"); | 42 | RCSID("$OpenBSD: channels.c,v 1.193 2003/07/02 14:51:16 markus Exp $"); |
43 | 43 | ||
44 | #include "ssh.h" | 44 | #include "ssh.h" |
45 | #include "ssh1.h" | 45 | #include "ssh1.h" |
@@ -54,7 +54,7 @@ RCSID("$OpenBSD: channels.c,v 1.192 2003/07/02 12:56:34 markus Exp $"); | |||
54 | #include "key.h" | 54 | #include "key.h" |
55 | #include "authfd.h" | 55 | #include "authfd.h" |
56 | #include "pathnames.h" | 56 | #include "pathnames.h" |
57 | 57 | #include "bufaux.h" | |
58 | 58 | ||
59 | /* -- channel core */ | 59 | /* -- channel core */ |
60 | 60 | ||
@@ -941,6 +941,117 @@ channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) | |||
941 | return 1; | 941 | return 1; |
942 | } | 942 | } |
943 | 943 | ||
944 | /* try to decode a socks5 header */ | ||
945 | #define SSH_SOCKS5_AUTHDONE 0x1000 | ||
946 | #define SSH_SOCKS5_NOAUTH 0x00 | ||
947 | #define SSH_SOCKS5_IPV4 0x01 | ||
948 | #define SSH_SOCKS5_DOMAIN 0x03 | ||
949 | #define SSH_SOCKS5_IPV6 0x04 | ||
950 | #define SSH_SOCKS5_CONNECT 0x01 | ||
951 | #define SSH_SOCKS5_SUCCESS 0x00 | ||
952 | |||
953 | static int | ||
954 | channel_decode_socks5(Channel *c, fd_set * readset, fd_set * writeset) | ||
955 | { | ||
956 | struct { | ||
957 | u_int8_t version; | ||
958 | u_int8_t command; | ||
959 | u_int8_t reserved; | ||
960 | u_int8_t atyp; | ||
961 | } s5_req, s5_rsp; | ||
962 | u_int16_t dest_port; | ||
963 | u_char *p, dest_addr[255+1]; | ||
964 | int i, have, found, nmethods, addrlen, af; | ||
965 | |||
966 | debug2("channel %d: decode socks5", c->self); | ||
967 | p = buffer_ptr(&c->input); | ||
968 | if (p[0] != 0x05) | ||
969 | return -1; | ||
970 | have = buffer_len(&c->input); | ||
971 | if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { | ||
972 | /* format: ver | nmethods | methods */ | ||
973 | if (have < 2) | ||
974 | return 0; | ||
975 | nmethods = p[1]; | ||
976 | if (have < nmethods + 2) | ||
977 | return 0; | ||
978 | /* look for method: "NO AUTHENTICATION REQUIRED" */ | ||
979 | for (found = 0, i = 2 ; i < nmethods + 2; i++) { | ||
980 | if (p[i] == SSH_SOCKS5_NOAUTH ) { | ||
981 | found = 1; | ||
982 | break; | ||
983 | } | ||
984 | } | ||
985 | if (!found) { | ||
986 | debug("channel %d: method SSH_SOCKS5_NOAUTH not found", | ||
987 | c->self); | ||
988 | return -1; | ||
989 | } | ||
990 | buffer_consume(&c->input, nmethods + 2); | ||
991 | buffer_put_char(&c->output, 0x05); /* version */ | ||
992 | buffer_put_char(&c->output, SSH_SOCKS5_NOAUTH); /* method */ | ||
993 | FD_SET(c->sock, writeset); | ||
994 | c->flags |= SSH_SOCKS5_AUTHDONE; | ||
995 | debug2("channel %d: socks5 auth done", c->self); | ||
996 | return 0; /* need more */ | ||
997 | } | ||
998 | debug2("channel %d: socks5 post auth", c->self); | ||
999 | if (have < sizeof(s5_req)+1) | ||
1000 | return 0; /* need more */ | ||
1001 | memcpy((char *)&s5_req, p, sizeof(s5_req)); | ||
1002 | if (s5_req.version != 0x05 || | ||
1003 | s5_req.command != SSH_SOCKS5_CONNECT || | ||
1004 | s5_req.reserved != 0x00) { | ||
1005 | debug("channel %d: only socks5 connect supported", c->self); | ||
1006 | return -1; | ||
1007 | } | ||
1008 | switch(s5_req.atyp){ | ||
1009 | case SSH_SOCKS5_IPV4: | ||
1010 | addrlen = 4; | ||
1011 | af = AF_INET; | ||
1012 | break; | ||
1013 | case SSH_SOCKS5_DOMAIN: | ||
1014 | addrlen = p[sizeof(s5_req)]; | ||
1015 | af = -1; | ||
1016 | break; | ||
1017 | case SSH_SOCKS5_IPV6: | ||
1018 | addrlen = 16; | ||
1019 | af = AF_INET6; | ||
1020 | break; | ||
1021 | default: | ||
1022 | debug("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); | ||
1023 | return -1; | ||
1024 | } | ||
1025 | if (have < 4 + addrlen + 2) | ||
1026 | return 0; | ||
1027 | buffer_consume(&c->input, sizeof(s5_req)); | ||
1028 | if (s5_req.atyp == SSH_SOCKS5_DOMAIN) | ||
1029 | buffer_consume(&c->input, 1); /* host string length */ | ||
1030 | buffer_get(&c->input, (char *)&dest_addr, addrlen); | ||
1031 | buffer_get(&c->input, (char *)&dest_port, 2); | ||
1032 | dest_addr[addrlen] = '\0'; | ||
1033 | if (s5_req.atyp == SSH_SOCKS5_DOMAIN) | ||
1034 | strlcpy(c->path, dest_addr, sizeof(c->path)); | ||
1035 | else if (inet_ntop(af, dest_addr, c->path, sizeof(c->path)) == NULL) | ||
1036 | return -1; | ||
1037 | c->host_port = ntohs(dest_port); | ||
1038 | |||
1039 | debug("channel %d: dynamic request: socks5 host %s port %u command %u", | ||
1040 | c->self, c->path, c->host_port, s5_req.command); | ||
1041 | |||
1042 | s5_rsp.version = 0x05; | ||
1043 | s5_rsp.command = SSH_SOCKS5_SUCCESS; | ||
1044 | s5_rsp.reserved = 0; /* ignored */ | ||
1045 | s5_rsp.atyp = SSH_SOCKS5_IPV4; | ||
1046 | ((struct in_addr *)&dest_addr)->s_addr = INADDR_ANY; | ||
1047 | dest_port = 0; /* ignored */ | ||
1048 | |||
1049 | buffer_append(&c->output, (char *)&s5_rsp, sizeof(s5_rsp)); | ||
1050 | buffer_append(&c->output, (char *)&dest_addr, sizeof(struct in_addr)); | ||
1051 | buffer_append(&c->output, (char *)&dest_port, sizeof(dest_port)); | ||
1052 | return 1; | ||
1053 | } | ||
1054 | |||
944 | /* dynamic port forwarding */ | 1055 | /* dynamic port forwarding */ |
945 | static void | 1056 | static void |
946 | channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) | 1057 | channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) |
@@ -953,7 +1064,7 @@ channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) | |||
953 | debug2("channel %d: pre_dynamic: have %d", c->self, have); | 1064 | debug2("channel %d: pre_dynamic: have %d", c->self, have); |
954 | /* buffer_dump(&c->input); */ | 1065 | /* buffer_dump(&c->input); */ |
955 | /* check if the fixed size part of the packet is in buffer. */ | 1066 | /* check if the fixed size part of the packet is in buffer. */ |
956 | if (have < 4) { | 1067 | if (have < 3) { |
957 | /* need more */ | 1068 | /* need more */ |
958 | FD_SET(c->sock, readset); | 1069 | FD_SET(c->sock, readset); |
959 | return; | 1070 | return; |
@@ -964,6 +1075,9 @@ channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) | |||
964 | case 0x04: | 1075 | case 0x04: |
965 | ret = channel_decode_socks4(c, readset, writeset); | 1076 | ret = channel_decode_socks4(c, readset, writeset); |
966 | break; | 1077 | break; |
1078 | case 0x05: | ||
1079 | ret = channel_decode_socks5(c, readset, writeset); | ||
1080 | break; | ||
967 | default: | 1081 | default: |
968 | ret = -1; | 1082 | ret = -1; |
969 | break; | 1083 | break; |