Commit e1057cd4 authored by Matt Johnston's avatar Matt Johnston
Browse files

TCP forwarding works.

--HG--
extra : convert_revision : 57dfb36d0d482ad84f31506904eb67863bd303ab
parent 453261a0
......@@ -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)
......
......@@ -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;
......@@ -106,6 +108,22 @@ void cli_getopts(int argc, char ** argv) {
nextiskey = 0;
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 */
......@@ -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 [[email protected]]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
......@@ -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() {
......
#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;
}
......
/*
* 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;