diff options
author | Matt Johnston <matt@ucc.asn.au> | 2004-08-12 13:48:42 +0000 |
---|---|---|
committer | Matt Johnston <matt@ucc.asn.au> | 2004-08-12 13:48:42 +0000 |
commit | e1057cd47720a03512e3ed0d5bbc2d5296b94fc2 (patch) | |
tree | 4908aee50c5555c9964030148934641fba2c1a51 | |
parent | 453261a0420a1e4ee5d0feb3df6806c39ae3e0ff (diff) |
TCP forwarding works.
--HG--
extra : convert_revision : 57dfb36d0d482ad84f31506904eb67863bd303ab
-rw-r--r-- | Makefile.in | 4 | ||||
-rw-r--r-- | cli-runopts.c | 134 | ||||
-rw-r--r-- | cli-session.c | 13 | ||||
-rw-r--r-- | cli-tcpfwd.c | 130 | ||||
-rw-r--r-- | dbutil.c | 4 | ||||
-rw-r--r-- | fake-rfc2553.c | 224 | ||||
-rw-r--r-- | fake-rfc2553.h | 161 | ||||
-rw-r--r-- | options.h | 15 | ||||
-rw-r--r-- | runopts.h | 9 | ||||
-rw-r--r-- | session.h | 1 | ||||
-rw-r--r-- | svr-tcpfwd.c | 72 | ||||
-rw-r--r-- | tcp-accept.c | 4 | ||||
-rw-r--r-- | tcp-accept.h | 25 | ||||
-rw-r--r-- | tcp-connect.c | 75 | ||||
-rw-r--r-- | tcpfwd.h (renamed from tcp-connect.h) | 46 |
15 files changed, 777 insertions, 140 deletions
diff --git a/Makefile.in b/Makefile.in index af24bb0..68ef5d8 100644 --- a/Makefile.in +++ b/Makefile.in @@ -46,8 +46,8 @@ HEADERS=options.h dbutil.h session.h packet.h algo.h ssh.h buffer.h kex.h \ dss.h bignum.h signkey.h rsa.h random.h service.h auth.h authpasswd.h \ debug.h channel.h chansession.h config.h queue.h sshpty.h \ termcodes.h gendss.h genrsa.h authpubkey.h runopts.h includes.h \ - loginrec.h atomicio.h x11fwd.h agentfwd.h tcp-accept.h compat.h \ - tcp-connect.h listener.h + loginrec.h atomicio.h x11fwd.h agentfwd.h tcpfwd.h compat.h \ + listener.h dropbearobjs=$(COMMONOBJS) $(CLISVROBJS) $(SVROBJS) dbclientobjs=$(COMMONOBJS) $(CLISVROBJS) $(CLIOBJS) diff --git a/cli-runopts.c b/cli-runopts.c index 4c84cc8..26eb634 100644 --- a/cli-runopts.c +++ b/cli-runopts.c @@ -28,6 +28,7 @@ #include "buffer.h" #include "dbutil.h" #include "algo.h" +#include "tcpfwd.h" cli_runopts cli_opts; /* GLOBAL */ @@ -36,6 +37,9 @@ static void parsehostname(char* userhostarg); #ifdef DROPBEAR_PUBKEY_AUTH static void loadidentityfile(const char* filename); #endif +#ifdef ENABLE_CLI_ANYTCPFWD +static void addforward(char* str, struct TCPFwdList** fwdlist); +#endif static void printhelp() { @@ -48,10 +52,10 @@ static void printhelp() { #ifdef DROPBEAR_PUBKEY_AUTH "-i <identityfile> (multiple allowed)\n" #endif -#ifndef DISABLE_REMOTETCPFWD +#ifdef ENABLE_CLI_LOCALTCPFWD "-L <listenport:remotehsot:reportport> Local port forwarding\n" #endif -#ifndef DISABLE_TCPFWD_DIRECT +#ifdef ENABLE_CLI_REMOTETCPFWD "-R <listenport:remotehost:remoteport> Remote port forwarding\n" #endif ,DROPBEAR_VERSION, cli_opts.progname); @@ -65,15 +69,13 @@ void cli_getopts(int argc, char ** argv) { #ifdef DROPBEAR_PUBKEY_AUTH int nextiskey = 0; /* A flag if the next argument is a keyfile */ #endif -#ifdef DROPBEAR_CLI_LOCALTCP +#ifdef ENABLE_CLI_LOCALTCPFWD int nextislocal = 0; #endif -#ifdef DROPBEAR_CLI_REMOTETCP +#ifdef ENABLE_CLI_REMOTETCPFWD int nextisremote = 0; #endif - - /* see printhelp() for options */ cli_opts.progname = argv[0]; cli_opts.remotehost = NULL; @@ -84,11 +86,11 @@ void cli_getopts(int argc, char ** argv) { #ifdef DROPBEAR_PUBKEY_AUTH cli_opts.pubkeys = NULL; #endif -#ifdef DROPBEAR_CLI_LOCALTCP - cli_opts.localports = NULL; +#ifdef ENABLE_CLI_LOCALTCPFWD + cli_opts.localfwds = NULL; #endif -#ifdef DROPBEAR_CLI_REMOTETCP - cli_opts.remoteports = NULL; +#ifdef ENABLE_CLI_REMOTETCPFWD + cli_opts.remotefwds = NULL; #endif opts.nolocaltcp = 0; opts.noremotetcp = 0; @@ -107,6 +109,22 @@ void cli_getopts(int argc, char ** argv) { continue; } #endif +#ifdef ENABLE_CLI_REMOTETCPFWD + if (nextisremote) { + TRACE(("nextisremote true")); + addforward(argv[i], &cli_opts.remotefwds); + nextisremote = 0; + continue; + } +#endif +#ifdef ENABLE_CLI_LOCALTCPFWD + if (nextislocal) { + TRACE(("nextislocal true")); + addforward(argv[i], &cli_opts.localfwds); + nextislocal = 0; + continue; + } +#endif if (next) { /* The previous flag set a value to assign */ *next = argv[i]; @@ -135,6 +153,16 @@ void cli_getopts(int argc, char ** argv) { case 'T': /* don't want a pty */ cli_opts.wantpty = 0; break; +#ifdef ENABLE_CLI_LOCALTCPFWD + case 'L': + nextislocal = 1; + break; +#endif +#ifdef ENABLE_CLI_REMOTETCPFWD + case 'R': + nextisremote = 1; + break; +#endif default: fprintf(stderr, "Unknown argument '%s'\n", argv[i]); printhelp(); @@ -145,7 +173,7 @@ void cli_getopts(int argc, char ** argv) { continue; /* next argument */ } else { - TRACE(("non-flag arg")); + TRACE(("non-flag arg: '%s'", argv[i])); /* Either the hostname or commands */ @@ -226,10 +254,14 @@ static void loadidentityfile(const char* filename) { /* Parses a [user@]hostname argument. userhostarg is the argv[i] corresponding * - note that it will be modified */ -static void parsehostname(char* userhostarg) { +static void parsehostname(char* orighostarg) { uid_t uid; struct passwd *pw = NULL; + char *userhostarg = NULL; + + /* We probably don't want to be editing argvs */ + userhostarg = m_strdup(orighostarg); cli_opts.remotehost = strchr(userhostarg, '@'); if (cli_opts.remotehost == NULL) { @@ -257,3 +289,81 @@ static void parsehostname(char* userhostarg) { dropbear_exit("Bad hostname"); } } + +#ifdef ENABLE_CLI_ANYTCPFWD +/* Turn a "listenport:remoteaddr:remoteport" string into into a forwarding + * set, and add it to the forwarding list */ +static void addforward(char* origstr, struct TCPFwdList** fwdlist) { + + char * listenport = NULL; + char * connectport = NULL; + char * connectaddr = NULL; + struct TCPFwdList* newfwd = NULL; + char * str = NULL; + + TRACE(("enter addforward")); + + /* We probably don't want to be editing argvs */ + str = m_strdup(origstr); + + listenport = str; + + connectaddr = strchr(str, ':'); + if (connectaddr == NULL) { + TRACE(("connectaddr == NULL")); + goto fail; + } + + connectaddr[0] = '\0'; + connectaddr++; + + connectport = strchr(connectaddr, ':'); + if (connectport == NULL) { + TRACE(("connectport == NULL")); + goto fail; + } + + connectport[0] = '\0'; + connectport++; + + newfwd = (struct TCPFwdList*)m_malloc(sizeof(struct TCPFwdList)); + + /* Now we check the ports - note that the port ints are unsigned, + * the check later only checks for >= MAX_PORT */ + newfwd->listenport = strtol(listenport, NULL, 10); + if (errno != 0) { + TRACE(("bad listenport strtol")); + goto fail; + } + + newfwd->connectport = strtol(connectport, NULL, 10); + if (errno != 0) { + TRACE(("bad connectport strtol")); + goto fail; + } + + newfwd->connectaddr = connectaddr; + + if (newfwd->listenport > 65535) { + TRACE(("listenport > 65535")); + goto badport; + } + + if (newfwd->connectport > 65535) { + TRACE(("connectport > 65535")); + goto badport; + } + + newfwd->next = *fwdlist; + *fwdlist = newfwd; + + TRACE(("leave addforward: done")); + return; + +fail: + dropbear_exit("Bad TCP forward '%s'", origstr); + +badport: + dropbear_exit("Bad TCP port in '%s'", origstr); +} +#endif diff --git a/cli-session.c b/cli-session.c index 22f7001..318bc64 100644 --- a/cli-session.c +++ b/cli-session.c @@ -4,8 +4,7 @@ #include "kex.h" #include "ssh.h" #include "packet.h" -#include "tcp-accept.h" -#include "tcp-connect.h" +#include "tcpfwd.h" #include "channel.h" #include "random.h" #include "service.h" @@ -45,8 +44,9 @@ static const packettype cli_packettypes[] = { }; static const struct ChanType *cli_chantypes[] = { - /* &chan_tcpdirect etc, though need to only allow if we've requested - * that forwarding */ +#ifdef ENABLE_CLI_REMOTETCPFWD + &cli_chan_tcpremote, +#endif NULL /* Null termination */ }; @@ -178,6 +178,10 @@ static void cli_sessionloop() { */ case USERAUTH_SUCCESS_RCVD: +#ifdef ENABLE_CLI_LOCALTCPFWD + TRACE(("recvd USERAUTH_SUCCESS_RCVD")); + setup_localtcp(); +#endif cli_send_chansess_request(); TRACE(("leave cli_sessionloop: cli_send_chansess_request")); cli_ses.state = SESSION_RUNNING; @@ -223,7 +227,6 @@ static void cli_finished() { } - /* called when the remote side closes the connection */ static void cli_remoteclosed() { diff --git a/cli-tcpfwd.c b/cli-tcpfwd.c index 955ffab..f32a53f 100644 --- a/cli-tcpfwd.c +++ b/cli-tcpfwd.c @@ -1,20 +1,54 @@ #include "includes.h" #include "options.h" -#include "tcp-accept.h" -#include "tcp-connect.h" +#include "dbutil.h" +#include "tcpfwd.h" #include "channel.h" +#include "runopts.h" +#include "session.h" +#include "ssh.h" +static int cli_localtcp(unsigned int listenport, const char* remoteaddr, + unsigned int remoteport); +static int newtcpforwarded(struct Channel * channel); + +const struct ChanType cli_chan_tcpremote = { + 1, /* sepfds */ + "forwarded-tcpip", + newtcpforwarded, + NULL, + NULL, + NULL +}; static const struct ChanType cli_chan_tcplocal = { 1, /* sepfds */ "direct-tcpip", NULL, NULL, + NULL, NULL }; void setup_localtcp() { - qv + int ret; + + if (cli_opts.localfwds == NULL) { + TRACE(("cli_opts.localfwds == NULL")); + } + + while (cli_opts.localfwds != NULL) { + ret = cli_localtcp(cli_opts.localfwds->listenport, + cli_opts.localfwds->connectaddr, + cli_opts.localfwds->connectport); + if (ret == DROPBEAR_FAILURE) { + dropbear_log(LOG_WARNING, "Failed local port forward %d:%s:%d", + cli_opts.localfwds->listenport, + cli_opts.localfwds->connectaddr, + cli_opts.localfwds->connectport); + } + + cli_opts.localfwds = cli_opts.localfwds->next; + } } @@ -22,6 +56,10 @@ static int cli_localtcp(unsigned int listenport, const char* remoteaddr, unsigned int remoteport) { struct TCPListener* tcpinfo = NULL; + int ret; + + TRACE(("enter cli_localtcp: %d %s %d", listenport, remoteaddr, + remoteport)); tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener*)); tcpinfo->sendaddr = remoteaddr; @@ -34,5 +72,91 @@ static int cli_localtcp(unsigned int listenport, const char* remoteaddr, if (ret == DROPBEAR_FAILURE) { m_free(tcpinfo); } + TRACE(("leave cli_localtcp: %d", ret)); + return ret; +} + +static void send_msg_global_request_remotetcp(int port) { + + TRACE(("enter send_msg_global_request_remotetcp")); + + CHECKCLEARTOWRITE(); + buf_putbyte(ses.writepayload, SSH_MSG_GLOBAL_REQUEST); + buf_putstring(ses.writepayload, "tcpip-forward", 13); + buf_putbyte(ses.writepayload, 0); + buf_putstring(ses.writepayload, "0.0.0.0", 7); /* TODO: IPv6? */ + buf_putint(ses.writepayload, port); + + encrypt_packet(); + + TRACE(("leave send_msg_global_request_remotetcp")); +} + +void setup_remotetcp() { + + struct TCPFwdList * iter = NULL; + + if (cli_opts.remotefwds == NULL) { + TRACE(("cli_opts.remotefwds == NULL")); + } + + iter = cli_opts.remotefwds; + + while (iter != NULL) { + send_msg_global_request_remotetcp(iter->listenport); + iter = iter->next; + } +} + +static int newtcpforwarded(struct Channel * channel) { + + unsigned int origport; + struct TCPFwdList * iter = NULL; + char portstring[NI_MAXSERV]; + int sock; + int ret = DROPBEAR_FAILURE; + + /* We don't care what address they connected to */ + buf_eatstring(ses.payload); + + origport = buf_getint(ses.payload); + + /* Find which port corresponds */ + iter = cli_opts.remotefwds; + + while (iter != NULL) { + if (origport == iter->listenport) { + break; + } + iter = iter->next; + } + + if (iter == NULL) { + /* We didn't request forwarding on that port */ + dropbear_log(LOG_INFO, "Server send unrequested port, from port %d", + origport); + goto out; + } + + snprintf(portstring, sizeof(portstring), "%d", iter->connectport); + sock = connect_remote(iter->connectaddr, portstring, 1, NULL); + if (sock < 0) { + TRACE(("leave newtcpdirect: sock failed")); + goto out; + } + + ses.maxfd = MAX(ses.maxfd, sock); + + /* Note that infd is actually the "outgoing" direction on the + * tcp connection, vice versa for outfd. + * We don't set outfd, that will get set after the connection's + * progress succeeds */ + channel->infd = sock; + channel->initconn = 1; + + ret = DROPBEAR_SUCCESS; + +out: + TRACE(("leave newtcpdirect: ret %d", ret)); return ret; } @@ -185,7 +185,7 @@ int dropbear_listen(const char* address, const char* port, if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { err = errno; close(sock); - TRACE(("bind() failed")); + TRACE(("bind(%s) failed", port)); continue; } @@ -206,7 +206,7 @@ int dropbear_listen(const char* address, const char* port, int len; len = 20 + strlen(strerror(err)); *errstring = (char*)m_malloc(len); - snprintf(*errstring, len, "Error connecting: %s", strerror(err)); + snprintf(*errstring, len, "Error listening: %s", strerror(err)); TRACE(("leave dropbear_listen: failure, %s", strerror(err))); return -1; } diff --git a/fake-rfc2553.c b/fake-rfc2553.c new file mode 100644 index 0000000..0186b53 --- /dev/null +++ b/fake-rfc2553.c @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2000-2003 Damien Miller. All rights reserved. + * Copyright (C) 1999 WIDE Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Pseudo-implementation of RFC2553 name / address resolution functions + * + * But these functions are not implemented correctly. The minimum subset + * is implemented for ssh use only. For example, this routine assumes + * that ai_family is AF_INET. Don't use it for another purpose. + */ + +#include "includes.h" + +RCSID("$Id: fake-rfc2553.c,v 1.5 2003/09/22 02:08:23 dtucker Exp $"); + +#ifndef HAVE_GETNAMEINFO +int getnameinfo(const struct sockaddr *sa, size_t salen, char *host, + size_t hostlen, char *serv, size_t servlen, int flags) +{ + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + struct hostent *hp; + char tmpserv[16]; + + if (serv != NULL) { + snprintf(tmpserv, sizeof(tmpserv), "%d", ntohs(sin->sin_port)); + if (strlcpy(serv, tmpserv, servlen) >= servlen) + return (EAI_MEMORY); + } + + if (host != NULL) { + if (flags & NI_NUMERICHOST) { + if (strlcpy(host, inet_ntoa(sin->sin_addr), + hostlen) >= hostlen) + return (EAI_MEMORY); + else + return (0); + } else { + hp = gethostbyaddr((char *)&sin->sin_addr, + sizeof(struct in_addr), AF_INET); + if (hp == NULL) + return (EAI_NODATA); + + if (strlcpy(host, hp->h_name, hostlen) >= hostlen) + return (EAI_MEMORY); + else + return (0); + } + } + return (0); +} +#endif /* !HAVE_GETNAMEINFO */ + +#ifndef HAVE_GAI_STRERROR +#ifdef HAVE_CONST_GAI_STRERROR_PROTO +const char * +#else +char * +#endif +gai_strerror(int err) +{ + switch (err) { + case EAI_NODATA: + return ("no address associated with name"); + case EAI_MEMORY: + return ("memory allocation failure."); + case EAI_NONAME: + return ("nodename nor servname provided, or not known"); + default: + return ("unknown/invalid error."); + } +} +#endif /* !HAVE_GAI_STRERROR */ + +#ifndef HAVE_FREEADDRINFO +void +freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *next; + + for(; ai != NULL;) { + next = ai->ai_next; + free(ai); + ai = next; + } +} +#endif /* !HAVE_FREEADDRINFO */ + +#ifndef HAVE_GETADDRINFO +static struct +addrinfo *malloc_ai(int port, u_long addr, const struct addrinfo *hints) +{ + struct addrinfo *ai; + + ai = malloc(sizeof(*ai) + sizeof(struct sockaddr_in)); + if (ai == NULL) + return (NULL); + + memset(ai, '\0', sizeof(*ai) + sizeof(struct sockaddr_in)); + + ai->ai_addr = (struct sockaddr *)(ai + 1); + /* XXX -- ssh doesn't use sa_len */ + ai->ai_addrlen = sizeof(struct sockaddr_in); + ai->ai_addr->sa_family = ai->ai_family = AF_INET; + + ((struct sockaddr_in *)(ai)->ai_addr)->sin_port = port; + ((struct sockaddr_in *)(ai)->ai_addr)->sin_addr.s_addr = addr; + + /* XXX: the following is not generally correct, but does what we want */ + if (hints->ai_socktype) + ai->ai_socktype = hints->ai_socktype; + else + ai->ai_socktype = SOCK_STREAM; + + if (hints->ai_protocol) + ai->ai_protocol = hints->ai_protocol; + + return (ai); +} + +int +getaddrinfo(const char *hostname, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + struct hostent *hp; + struct servent *sp; + struct in_addr in; + int i; + long int port; + u_long addr; + + port = 0; + if (servname != NULL) { + char *cp; + + port = strtol(servname, &cp, 10); + if (port > 0 && port <= 65535 && *cp == '\0') + port = htons(port); + else if ((sp = getservbyname(servname, NULL)) != NULL) + port = sp->s_port; + else + port = 0; + } + + if (hints && hints->ai_flags & AI_PASSIVE) { + addr = htonl(0x00000000); + if (hostname && inet_aton(hostname, &in) != 0) + addr = in.s_addr; + *res = malloc_ai(port, addr, hints); + if (*res == NULL) + return (EAI_MEMORY); + return (0); + } + + if (!hostname) { + *res = malloc_ai(port, htonl(0x7f000001), hints); + if (*res == NULL) + return (EAI_MEMORY); + return (0); + } + + if (inet_aton(hostname, &in)) { + *res = malloc_ai(port, in.s_addr, hints); + if (*res == NULL) + return (EAI_MEMORY); + return (0); + } + + /* Don't try DNS if AI_NUMERICHOST is set */ + if (hints && hints->ai_flags & AI_NUMERICHOST) + return (EAI_NONAME); + + hp = gethostbyname(hostname); + if (hp && hp->h_name && hp->h_name[0] && hp->h_addr_list[0]) { + struct addrinfo *cur, *prev; + + cur = prev = *res = NULL; + for (i = 0; hp->h_addr_list[i]; i++) { + struct in_addr *in = (struct in_addr *)hp->h_addr_list[i]; + + cur = malloc_ai(port, in->s_addr, hints); + if (cur == NULL) { + if (*res != NULL) + freeaddrinfo(*res); + return (EAI_MEMORY); + } + if (prev) + prev->ai_next = cur; + else + *res = cur; + + prev = cur; + } + return (0); + } + + return (EAI_NODATA); +} +#endif /* !HAVE_GETADDRINFO */ diff --git a/fake-rfc2553.h b/fake-rfc2553.h new file mode 100644 index 0000000..baea070 --- /dev/null +++ b/fake-rfc2553.h @@ -0,0 +1,161 @@ +/* $Id: fake-rfc2553.h,v 1.9 2004/03/10 10:06:33 dtucker Exp $ */ + +/* + * Copyright (C) 2000-2003 Damien Miller. All rights reserved. + * Copyright (C) 1999 WIDE Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Pseudo-implementation of RFC2553 name / address resolution functions + * + * But these functions are not implemented correctly. The minimum subset + * is implemented for ssh use only. For example, this routine assumes + * that ai_family is AF_INET. Don't use it for another purpose. + */ + +#ifndef _FAKE_RFC2553_H +#define _FAKE_RFC2553_H + +#include "includes.h" +#include "sys/types.h" + +/* + * First, socket and INET6 related definitions + */ +#ifndef HAVE_STRUCT_SOCKADDR_STORAGE +# define _SS_MAXSIZE 128 /* Implementation specific max size */ +# define _SS_PADSIZE (_SS_MAXSIZE - sizeof (struct sockaddr)) +struct sockaddr_storage { + struct sockaddr ss_sa; + char __ss_pad2[_SS_PADSIZE]; +}; +# define ss_family ss_sa.sa_family +#endif /* !HAVE_STRUCT_SOCKADDR_STORAGE */ + +#ifndef IN6_IS_ADDR_LOOPBACK +# define IN6_IS_ADDR_LOOPBACK(a) \ + (((u_int32_t *)(a))[0] == 0 && ((u_int32_t *)(a))[1] == 0 && \ + ((u_int32_t *)(a))[2] == 0 && ((u_int32_t *)(a))[3] == htonl(1)) +#endif /* !IN6_IS_ADDR_LOOPBACK */ + +#ifndef HAVE_STRUCT_IN6_ADDR +struct in6_addr { + u_int8_t s6_addr[16]; +}; +#endif /* !HAVE_STRUCT_IN6_ADDR */ + +#ifndef HAVE_STRUCT_SOCKADDR_IN6 +struct sockaddr_in6 { + unsigned short sin6_family; + u_int16_t sin6_port; + u_int32_t sin6_flowinfo; + struct in6_addr sin6_addr; +}; +#endif /* !HAVE_STRUCT_SOCKADDR_IN6 */ + +#ifndef AF_INET6 +/* Define it to something that should never appear */ +#define AF_INET6 AF_MAX +#endif + +/* + * Next, RFC2553 name / address resolution API + */ + +#ifndef NI_NUMERICHOST +# define NI_NUMERICHOST (1) +#endif +#ifndef NI_NAMEREQD +# define NI_NAMEREQD (1<<1) +#endif +#ifndef NI_NUMERICSERV +# define NI_NUMERICSERV (1<<2) +#endif + +#ifndef AI_PASSIVE +# define AI_PASSIVE (1) +#endif +#ifndef AI_CANONNAME +# define AI_CANONNAME (1<<1) +#endif +#ifndef AI_NUMERICHOST +# define AI_NUMERICHOST (1<<2) +#endif + +#ifndef NI_MAXSERV +# define NI_MAXSERV 32 +#endif /* !NI_MAXSERV */ +#ifndef NI_MAXHOST +# define NI_MAXHOST 1025 +#endif /* !NI_MAXHOST */ + +#ifndef EAI_NODATA +# define EAI_NODATA 1 +# define EAI_MEMORY 2 +# define EAI_NONAME 3 +#endif + +#ifndef HAVE_STRUCT_ADDRINFO +struct addrinfo { + int ai_flags; /* AI_PASSIVE, AI_CANONNAME */ + int ai_family; /* PF_xxx */ + int ai_socktype; /* SOCK_xxx */ + int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */ + size_t ai_addrlen; /* length of ai_addr */ + char *ai_canonname; /* canonical name for hostname */ + struct sockaddr *ai_addr; /* binary address */ + struct addrinfo *ai_next; /* next structure in linked list */ +}; +#endif /* !HAVE_STRUCT_ADDRINFO */ + +#ifndef HAVE_GETADDRINFO +#ifdef getaddrinfo +# undef getaddrinfo +#endif +#define getaddrinfo(a,b,c,d) (ssh_getaddrinfo(a,b,c,d)) +int getaddrinfo(const char *, const char *, + const struct addrinfo *, struct addrinfo **); +#endif /* !HAVE_GETADDRINFO */ + +#if !defined(HAVE_GAI_STRERROR) && !defined(HAVE_CONST_GAI_STRERROR_PROTO) +#define gai_strerror(a) (ssh_gai_strerror(a)) +char *gai_strerror(int); +#endif /* !HAVE_GAI_STRERROR */ + +#ifndef HAVE_FREEADDRINFO +#define freeaddrinfo(a) (ssh_freeaddrinfo(a)) +void freeaddrinfo(struct addrinfo *); +#endif /* !HAVE_FREEADDRINFO */ + +#ifndef HAVE_GETNAMEINFO +#define getnameinfo(a,b,c,d,e,f,g) (ssh_getnameinfo(a,b,c,d,e,f,g)) +int getnameinfo(const struct sockaddr *, size_t, char *, size_t, + char *, size_t, int); +#endif /* !HAVE_GETNAMEINFO */ + +#endif /* !_FAKE_RFC2553_H */ + @@ -51,10 +51,13 @@ #define ENABLE_X11FWD /* Enable TCP Fowarding */ -/* OpenSSH's "-L" style forwarding (client port forwarded via server) */ -#define ENABLE_LOCALTCPFWD -/* OpenSSH's "-R" style forwarding (server port forwarded via client) */ -#define ENABLE_REMOTETCPFWD +/* "-L" style forwarding (client listening port forwarded via server) */ +#define ENABLE_CLI_LOCALTCPFWD +/* "-R" style forwarding (server listening port forwarded via client) */ +#define ENABLE_CLI_REMOTETCPFWD + +#define ENABLE_SVR_LOCALTCPFWD +#define ENABLE_SVR_REMOTETCPFWD /* Enable Authentication Agent Forwarding */ #define ENABLE_AGENTFWD @@ -299,6 +302,10 @@ #define DISABLE_REMOTETCPFWD #endif +#if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD) +#define ENABLE_CLI_ANYTCPFWD +#endif + #if defined(ENABLE_REMOTETCPFWD) || defined(ENABLE_LOCALTCPFWD) || \ defined(ENABLE_AGENTFWD) || defined(ENABLE_X11FWD) #define USING_LISTENERS @@ -29,6 +29,7 @@ #include "signkey.h" #include "buffer.h" #include "auth.h" +#include "tcpfwd.h" typedef struct runopts { @@ -90,8 +91,14 @@ typedef struct cli_runopts { char *cmd; int wantpty; - struct PubkeyList *pubkeys; /* Keys to use for public-key auth */ #ifdef DROPBEAR_PUBKEY_AUTH + struct PubkeyList *pubkeys; /* Keys to use for public-key auth */ +#endif +#ifdef ENABLE_CLI_REMOTETCPFWD + struct TCPFwdList * remotefwds; +#endif +#ifdef ENABLE_CLI_LOCALTCPFWD + struct TCPFwdList * localfwds; #endif /* XXX TODO */ @@ -35,6 +35,7 @@ #include "queue.h" #include "listener.h" #include "packet.h" +#include "tcpfwd.h" extern int sessinitdone; /* Is set to 0 somewhere */ extern int exitflag; diff --git a/svr-tcpfwd.c b/svr-tcpfwd.c index 46c7129..0eccae4 100644 --- a/svr-tcpfwd.c +++ b/svr-tcpfwd.c @@ -1,7 +1,6 @@ #include "includes.h" #include "ssh.h" -#include "tcp-accept.h" -#include "tcp-connect.h" +#include "tcpfwd.h" #include "dbutil.h" #include "session.h" #include "buffer.h" @@ -15,6 +14,7 @@ static void send_msg_request_success(); static void send_msg_request_failure(); static int svr_cancelremotetcp(); static int svr_remotetcpreq(); +static int newtcpdirect(struct Channel * channel); const struct ChanType svr_chan_tcpdirect = { @@ -178,8 +178,8 @@ static int svr_remotetcpreq() { tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener)); tcpinfo->sendaddr = bindaddr; - TRACE(("sendport = %d", port)); tcpinfo->sendport = port; + tcpinfo->listenport = port; tcpinfo->chantype = &svr_chan_tcpremote; /* Note: bindaddr is actually ignored by listen_tcpfwd, since @@ -196,4 +196,70 @@ out: TRACE(("leave remotetcpreq")); return ret; } + +/* Called upon creating a new direct tcp channel (ie we connect out to an + * address */ +static int newtcpdirect(struct Channel * channel) { + + unsigned char* desthost = NULL; + unsigned int destport; + unsigned char* orighost = NULL; + unsigned int origport; + char portstring[NI_MAXSERV]; + int sock; + int len; + int ret = DROPBEAR_FAILURE; + + if (opts.nolocaltcp) { + TRACE(("leave newtcpdirect: local tcp forwarding disabled")); + goto out; + } + + desthost = buf_getstring(ses.payload, &len); + if (len > MAX_HOST_LEN) { + TRACE(("leave newtcpdirect: desthost too long")); + goto out; + } + + destport = buf_getint(ses.payload); + + orighost = buf_getstring(ses.payload, &len); + if (len > MAX_HOST_LEN) { + TRACE(("leave newtcpdirect: orighost too long")); + goto out; + } + + origport = buf_getint(ses.payload); + + /* best be sure */ + if (origport > 65535 || destport > 65535) { + TRACE(("leave newtcpdirect: port > 65535")); + goto out; + } + + snprintf(portstring, sizeof(portstring), "%d", destport); + sock = connect_remote(desthost, portstring, 1, NULL); + if (sock < 0) { + TRACE(("leave newtcpdirect: sock failed")); + goto out; + } + + ses.maxfd = MAX(ses.maxfd, sock); + + /* Note that infd is actually the "outgoing" direction on the + * tcp connection, vice versa for outfd. + * We don't set outfd, that will get set after the connection's + * progress succeeds */ + channel->infd = sock; + channel->initconn = 1; + + ret = DROPBEAR_SUCCESS; + +out: + m_free(desthost); + m_free(orighost); + TRACE(("leave newtcpdirect: ret %d", ret)); + return ret; +} + #endif diff --git a/tcp-accept.c b/tcp-accept.c index 8f8b5c0..6b82914 100644 --- a/tcp-accept.c +++ b/tcp-accept.c @@ -1,6 +1,6 @@ #include "includes.h" #include "ssh.h" -#include "tcp-accept.h" +#include "tcpfwd.h" #include "dbutil.h" #include "session.h" #include "buffer.h" @@ -67,7 +67,7 @@ int listen_tcpfwd(struct TCPListener* tcpinfo) { TRACE(("enter listen_tcpfwd")); /* first we try to bind, so don't need to do so much cleanup on failure */ - snprintf(portstring, sizeof(portstring), "%d", tcpinfo->sendport); + snprintf(portstring, sizeof(portstring), "%d", tcpinfo->listenport); /* XXX Note: we're just listening on localhost, no matter what they tell * us. If someone wants to make it listen otherways, then change diff --git a/tcp-accept.h b/tcp-accept.h deleted file mode 100644 index 8c795dc..0000000 --- a/tcp-accept.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef _REMOTETCPFWD_H -#define _REMOTETCPFWD_H - -struct TCPListener { - - /* sendaddr/sendport are what we send in the channel init request. For a - * forwarded-tcpip request, it's the addr/port we were binding to. - * For a direct-tcpip request, it's the addr/port we want the other - * end to connect to */ - - unsigned char *sendaddr; - unsigned int sendport; - - /* This is for direct-tcpip (ie the client listening), and specifies the - * port to listen on. Is unspecified for the server */ - unsigned int listenport; - - const struct ChanType *chantype; - -}; - -void recv_msg_global_request_remotetcp(); -int listen_tcpfwd(struct TCPListener* tcpinfo); - -#endif /* _REMOTETCPFWD_H */ diff --git a/tcp-connect.c b/tcp-connect.c deleted file mode 100644 index d24f32d..0000000 --- a/tcp-connect.c +++ /dev/null @@ -1,75 +0,0 @@ -#include "includes.h" -#include "session.h" -#include "dbutil.h" -#include "channel.h" -#include "tcp-connect.h" -#include "runopts.h" - -#ifndef DISABLE_TCP_CONNECT - -/* Called upon creating a new direct tcp channel (ie we connect out to an - * address */ -int newtcpdirect(struct Channel * channel) { - - unsigned char* desthost = NULL; - unsigned int destport; - unsigned char* orighost = NULL; - unsigned int origport; - char portstring[NI_MAXSERV]; - int sock; - int len; - int ret = DROPBEAR_FAILURE; - - if (opts.nolocaltcp) { - TRACE(("leave newtcpdirect: local tcp forwarding disabled")); - goto out; - } - - desthost = buf_getstring(ses.payload, &len); - if (len > MAX_HOST_LEN) { - TRACE(("leave newtcpdirect: desthost too long")); - goto out; - } - - destport = buf_getint(ses.payload); - - orighost = buf_getstring(ses.payload, &len); - if (len > MAX_HOST_LEN) { - TRACE(("leave newtcpdirect: orighost too long")); - goto out; - } - - origport = buf_getint(ses.payload); - - /* best be sure */ - if (origport > 65535 || destport > 65535) { - TRACE(("leave newtcpdirect: port > 65535")); - goto out; - } - - snprintf(portstring, sizeof(portstring), "%d", destport); - sock = connect_remote(desthost, portstring, 1, NULL); - if (sock < 0) { - TRACE(("leave newtcpdirect: sock failed")); - goto out; - } - - ses.maxfd = MAX(ses.maxfd, sock); - - /* Note that infd is actually the "outgoing" direction on the - * tcp connection, vice versa for outfd. - * We don't set outfd, that will get set after the connection's - * progress succeeds */ - channel->infd = sock; - channel->initconn = 1; - - ret = DROPBEAR_SUCCESS; - -out: - m_free(desthost); - m_free(orighost); - TRACE(("leave newtcpdirect: ret %d", ret)); - return ret; -} - -#endif /* DISABLE_TCPFWD_DIRECT */ @@ -21,15 +21,49 @@ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#ifndef _TCPFWD_DIRECT_H_ -#define _TCPFWD_DIRECT_H_ -#ifndef DISABLE_TCFWD_DIRECT +#ifndef _TCPFWD_H +#define _TCPFWD_H -#include "includes.h" #include "channel.h" +struct TCPListener { + + /* sendaddr/sendport are what we send in the channel init request. For a + * forwarded-tcpip request, it's the addr/port we were binding to. + * For a direct-tcpip request, it's the addr/port we want the other + * end to connect to */ + + unsigned char *sendaddr; + unsigned int sendport; + + /* This is for direct-tcpip (ie the client listening), and specifies the + * port to listen on. Is unspecified for the server */ + unsigned int listenport; + + const struct ChanType *chantype; + +}; + +/* A link in a list of forwards */ +struct TCPFwdList { + + char* connectaddr; + unsigned int connectport; + unsigned int listenport; + struct TCPFwdList * next; + +}; + +/* Server */ +void recv_msg_global_request_remotetcp(); extern const struct ChanType svr_chan_tcpdirect; -int newtcpdirect(struct Channel * channel); -#endif +/* Client */ +void setup_localtcp(); +extern const struct ChanType cli_chan_tcpremote; + +/* Common */ +int listen_tcpfwd(struct TCPListener* tcpinfo); + + #endif |