diff options
-rw-r--r-- | Makefile.in | 11 | ||||
-rw-r--r-- | cli-authpubkey.c | 10 | ||||
-rw-r--r-- | cli-runopts.c | 18 | ||||
-rw-r--r-- | cli-session.c | 5 | ||||
-rw-r--r-- | cli-tcpfwd.c | 34 | ||||
-rw-r--r-- | common-channel.c | 43 | ||||
-rw-r--r-- | dbutil.c | 173 | ||||
-rw-r--r-- | dbutil.h | 6 | ||||
-rw-r--r-- | listener.c | 42 | ||||
-rw-r--r-- | listener.h | 10 | ||||
-rw-r--r-- | options.h | 3 | ||||
-rw-r--r-- | svr-agentfwd.c | 8 | ||||
-rw-r--r-- | svr-chansession.c | 2 | ||||
-rw-r--r-- | svr-main.c | 82 | ||||
-rw-r--r-- | svr-session.c | 6 | ||||
-rw-r--r-- | svr-tcpfwd.c | 196 | ||||
-rw-r--r-- | svr-x11fwd.c | 8 | ||||
-rw-r--r-- | tcp-accept.c | 88 | ||||
-rw-r--r-- | tcp-accept.h | 19 | ||||
-rw-r--r-- | tcp-connect.c | 75 | ||||
-rw-r--r-- | tcp-connect.h (renamed from tcpfwd-direct.h) | 3 | ||||
-rw-r--r-- | tcpfwd-direct.c | 159 | ||||
-rw-r--r-- | tcpfwd-remote.c | 317 | ||||
-rw-r--r-- | tcpfwd-remote.h | 6 |
24 files changed, 673 insertions, 651 deletions
diff --git a/Makefile.in b/Makefile.in index 2bffe90..af24bb0 100644 --- a/Makefile.in +++ b/Makefile.in @@ -24,15 +24,16 @@ COMMONOBJS=dbutil.o buffer.o \ SVROBJS=svr-kex.o svr-algo.o svr-auth.o sshpty.o \ svr-authpasswd.o svr-authpubkey.o svr-session.o svr-service.o \ - svr-chansession.o svr-runopts.o svr-agentfwd.o svr-main.o svr-x11fwd.o + svr-chansession.o svr-runopts.o svr-agentfwd.o svr-main.o svr-x11fwd.o\ + svr-tcpfwd.o CLIOBJS=cli-algo.o cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \ cli-session.o cli-service.o cli-runopts.o cli-chansession.o \ - cli-authpubkey.o + cli-authpubkey.o cli-tcpfwd.o CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \ common-channel.o common-chansession.o termcodes.o loginrec.o \ - tcpfwd-direct.o tcpfwd-remote.o listener.o process-packet.o \ + tcp-accept.o tcp-connect.o listener.o process-packet.o \ common-runopts.o KEYOBJS=dropbearkey.o gendss.o genrsa.o @@ -45,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 tcpfwd-direct.h compat.h \ - tcpfwd-remote.h listener.h + loginrec.h atomicio.h x11fwd.h agentfwd.h tcp-accept.h compat.h \ + tcp-connect.h listener.h dropbearobjs=$(COMMONOBJS) $(CLISVROBJS) $(SVROBJS) dbclientobjs=$(COMMONOBJS) $(CLISVROBJS) $(CLIOBJS) diff --git a/cli-authpubkey.c b/cli-authpubkey.c index 6b6ab51..33514ce 100644 --- a/cli-authpubkey.c +++ b/cli-authpubkey.c @@ -13,17 +13,22 @@ static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign); void cli_pubkeyfail() { struct PubkeyList *keyitem; + struct PubkeyList **previtem; TRACE(("enter cli_pubkeyfail")); + previtem = &cli_opts.pubkeys; + /* Find the key we failed with, and remove it */ for (keyitem = cli_opts.pubkeys; keyitem != NULL; keyitem = keyitem->next) { - if (keyitem->next == cli_ses.lastpubkey) { - keyitem->next = cli_ses.lastpubkey->next; + if (keyitem == cli_ses.lastpubkey) { + *previtem = keyitem->next; } + previtem = &keyitem; } sign_key_free(cli_ses.lastpubkey->key); /* It won't be used again */ m_free(cli_ses.lastpubkey); + TRACE(("leave cli_pubkeyfail")); } @@ -145,6 +150,7 @@ int cli_auth_pubkey() { /* Send a trial request */ send_msg_userauth_pubkey(cli_opts.pubkeys->key, cli_opts.pubkeys->type, 0); + cli_ses.lastpubkey = cli_opts.pubkeys; TRACE(("leave cli_auth_pubkey-success")); return 1; } else { diff --git a/cli-runopts.c b/cli-runopts.c index eb718a4..4c84cc8 100644 --- a/cli-runopts.c +++ b/cli-runopts.c @@ -48,6 +48,12 @@ static void printhelp() { #ifdef DROPBEAR_PUBKEY_AUTH "-i <identityfile> (multiple allowed)\n" #endif +#ifndef DISABLE_REMOTETCPFWD + "-L <listenport:remotehsot:reportport> Local port forwarding\n" +#endif +#ifndef DISABLE_TCPFWD_DIRECT + "-R <listenport:remotehost:remoteport> Remote port forwarding\n" +#endif ,DROPBEAR_VERSION, cli_opts.progname); } @@ -59,6 +65,12 @@ 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 + int nextislocal = 0; +#endif +#ifdef DROPBEAR_CLI_REMOTETCP + int nextisremote = 0; +#endif @@ -72,6 +84,12 @@ void cli_getopts(int argc, char ** argv) { #ifdef DROPBEAR_PUBKEY_AUTH cli_opts.pubkeys = NULL; #endif +#ifdef DROPBEAR_CLI_LOCALTCP + cli_opts.localports = NULL; +#endif +#ifdef DROPBEAR_CLI_REMOTETCP + cli_opts.remoteports = NULL; +#endif opts.nolocaltcp = 0; opts.noremotetcp = 0; /* not yet diff --git a/cli-session.c b/cli-session.c index 973e9c7..22f7001 100644 --- a/cli-session.c +++ b/cli-session.c @@ -4,8 +4,8 @@ #include "kex.h" #include "ssh.h" #include "packet.h" -#include "tcpfwd-direct.h" -#include "tcpfwd-remote.h" +#include "tcp-accept.h" +#include "tcp-connect.h" #include "channel.h" #include "random.h" #include "service.h" @@ -31,7 +31,6 @@ static const packettype cli_packettypes[] = { {SSH_MSG_KEXDH_REPLY, recv_msg_kexdh_reply}, /* client */ {SSH_MSG_NEWKEYS, recv_msg_newkeys}, {SSH_MSG_SERVICE_ACCEPT, recv_msg_service_accept}, /* client */ - {SSH_MSG_GLOBAL_REQUEST, recv_msg_global_request_remotetcp}, {SSH_MSG_CHANNEL_REQUEST, recv_msg_channel_request}, {SSH_MSG_CHANNEL_OPEN, recv_msg_channel_open}, {SSH_MSG_CHANNEL_EOF, recv_msg_channel_eof}, diff --git a/cli-tcpfwd.c b/cli-tcpfwd.c new file mode 100644 index 0000000..3dc6e20 --- /dev/null +++ b/cli-tcpfwd.c @@ -0,0 +1,34 @@ +#include "includes.h" +#include "options.h" +#include "tcp-accept.h" +#include "tcp-connect.h" +#include "channel.h" + +static const struct ChanType cli_chan_tcplocal = { + 1, /* sepfds */ + "direct-tcpip", + NULL, + NULL, + NULL +}; + + + + +static int cli_localtcp(char* port) { + + struct TCPListener* tcpinfo = NULL; + + tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener*)); + tcpinfo->addr = NULL; + tcpinfo->port = port; + tcpinfo->chantype = &cli_chan_tcplocal; + + ret = listen_tcpfwd(tcpinfo); + + if (ret == DROPBEAR_FAILURE) { + DROPBEAR_LOG(LOG_WARNING, "Failed to listen on port %s", port); + m_free(tcpinfo); + } + return ret; +} diff --git a/common-channel.c b/common-channel.c index fbfb00b..5079031 100644 --- a/common-channel.c +++ b/common-channel.c @@ -32,8 +32,6 @@ #include "dbutil.h" #include "channel.h" #include "ssh.h" -#include "tcpfwd-direct.h" -#include "tcpfwd-remote.h" #include "listener.h" static void send_msg_channel_open_failure(unsigned int remotechan, int reason, @@ -307,13 +305,6 @@ static void send_msg_channel_close(struct Channel *channel) { if (channel->type->closehandler) { channel->type->closehandler(channel); } -#if 0 - if (channel->type == CHANNEL_ID_SESSION) { - send_exitsignalstatus(channel); - - closechansess(channel); - } -#endif CHECKCLEARTOWRITE(); @@ -545,23 +536,6 @@ void recv_msg_channel_request() { send_msg_channel_failure(channel); } -#if 0 - /* handle according to channel type */ - switch (channel->type) { - - case CHANNEL_ID_SESSION: - TRACE(("continue recv_msg_channel_request: session request")); - /* XXX server */ - /* Here we need to do things channel-specific style. Function - * pointer callbacks perhaps */ - chansessionrequest(channel); - break; - - default: - send_msg_channel_failure(channel); - } -#endif - TRACE(("leave recv_msg_channel_request")); } @@ -797,23 +771,6 @@ void recv_msg_channel_open() { } } -#if 0 - /* type specific initialisation */ - if (typeval == CHANNEL_ID_SESSION) { - newchansess(channel); -#ifndef DISABLE_LOCALTCPFWD - } else if (typeval == CHANNEL_ID_TCPDIRECT) { - if (ses.opts->nolocaltcp) { - errtype = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED; - } else if (newtcpdirect(channel) == DROPBEAR_FAILURE) { - errtype = SSH_OPEN_CONNECT_FAILED; - deletechannel(channel); - goto failure; - } -#endif - } -#endif - /* success */ send_msg_channel_open_confirmation(channel, channel->recvwindow, channel->recvmaxpacket); @@ -113,8 +113,109 @@ void dropbear_trace(const char* format, ...) { } #endif /* DEBUG_TRACE */ +/* Listen on address:port. Unless address is NULL, in which case listen on + * everything (ie 0.0.0.0, or ::1 - note that this is IPv? agnostic. Linux is + * broken with respect to listening to v6 or v4, so the addresses you get when + * people connect will be wrong. It doesn't break things, just looks quite + * ugly. Returns the number of sockets bound on success, or -1 on failure. On + * failure, if errstring wasn't NULL, it'll be a newly malloced error + * string.*/ +int dropbear_listen(const char* address, const char* port, + int *socks, unsigned int sockcount, char **errstring, int *maxfd) { + + struct addrinfo hints, *res, *res0; + int err; + unsigned int nsock; + struct linger linger; + int val; + int sock; + + TRACE(("enter dropbear_listen")); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; /* TODO: let them flag v4 only etc */ + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + err = getaddrinfo(address, port, &hints, &res0); + + if (err) { + if (errstring != NULL && *errstring == NULL) { + int len; + len = 20 + strlen(gai_strerror(err)); + *errstring = (char*)m_malloc(len); + snprintf(*errstring, len, "Error resolving: %s", gai_strerror(err)); + } + TRACE(("leave dropbear_listen: failed resolving")); + return -1; + } + + + nsock = 0; + for (res = res0; res != NULL && nsock < sockcount; + res = res->ai_next) { + + /* Get a socket */ + socks[nsock] = socket(res->ai_family, res->ai_socktype, + res->ai_protocol); + + sock = socks[nsock]; /* For clarity */ + + if (sock < 0) { + err = errno; + TRACE(("socket() failed")); + continue; + } + + /* Various useful socket options */ + val = 1; + /* set to reuse, quick timeout */ + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*) &val, sizeof(val)); + linger.l_onoff = 1; + linger.l_linger = 5; + setsockopt(sock, SOL_SOCKET, SO_LINGER, (void*)&linger, sizeof(linger)); + + /* disable nagle */ + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&val, sizeof(val)); + + if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { + err = errno; + close(sock); + TRACE(("bind() failed")); + continue; + } + + if (listen(sock, 20) < 0) { + err = errno; + close(sock); + TRACE(("listen() failed")); + continue; + } + + *maxfd = MAX(*maxfd, sock); + + nsock++; + } + + if (nsock == 0) { + if (errstring != NULL && *errstring == NULL) { + int len; + len = 20 + strlen(strerror(err)); + *errstring = (char*)m_malloc(len); + snprintf(*errstring, len, "Error connecting: %s", strerror(err)); + TRACE(("leave dropbear_listen: failure, %s", strerror(err))); + return -1; + } + } + + TRACE(("leave dropbear_listen: success, %d socks bound", nsock)); + return nsock; +} + /* Connect via TCP to a host. Connection will try ipv4 or ipv6, will - * return immediately if nonblocking is set */ + * return immediately if nonblocking is set. On failure, if errstring + * wasn't null, it will be a newly malloced error message */ + +/* TODO: maxfd */ int connect_remote(const char* remotehost, const char* remoteport, int nonblocking, char ** errstring) { @@ -197,58 +298,70 @@ int connect_remote(const char* remotehost, const char* remoteport, } freeaddrinfo(res0); + if (sock > 0 && errstring != NULL && *errstring != NULL) { + m_free(*errstring); + } - TRACE(("leave connect_remote: sock %d", sock)); + TRACE(("leave connect_remote: sock %d\n", sock)); return sock; } /* Return a string representation of the socket address passed. The return * value is allocated with malloc() */ -unsigned char * getaddrstring(struct sockaddr * addr) { +unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport) { - char *retstring; + char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; + char *retstring = NULL; + int ret; + unsigned int len; - /* space for "255.255.255.255:65535\0" = 22 */ - retstring = m_malloc(22); + len = sizeof(struct sockaddr_storage); - switch (addr->sa_family) { - case PF_INET: - snprintf(retstring, 22, "%s:%hu", - inet_ntoa(((struct sockaddr_in *)addr)->sin_addr), - ((struct sockaddr_in *)addr)->sin_port); - break; + ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf), + sbuf, sizeof(sbuf), NI_NUMERICSERV | NI_NUMERICHOST); - default: - /* XXX ipv6 */ - strcpy(retstring, "Bad protocol"); + if (ret != 0) { + /* This is a fairly bad failure - it'll fallback to IP if it + * just can't resolve */ + dropbear_exit("failed lookup (%d, %d)", ret, errno); + } + if (withport) { + len = strlen(hbuf) + 2 + strlen(sbuf); + retstring = (char*)m_malloc(len); + snprintf(retstring, len, "%s:%s", hbuf, sbuf); + } else { + retstring = m_strdup(hbuf); } + return retstring; } /* Get the hostname corresponding to the address addr. On failure, the IP * address is returned. The return value is allocated with strdup() */ -char* getaddrhostname(struct sockaddr * addr) { +char* getaddrhostname(struct sockaddr_storage * addr) { - struct hostent *host = NULL; - char * retstring; + char hbuf[NI_MAXHOST]; + char sbuf[NI_MAXSERV]; + int ret; + unsigned int len; -#ifdef DO_HOST_LOOKUP - host = gethostbyaddr((char*)&((struct sockaddr_in*)addr)->sin_addr, - sizeof(struct in_addr), AF_INET); -#endif - - if (host == NULL) { - /* return the address */ - retstring = inet_ntoa(((struct sockaddr_in *)addr)->sin_addr); - } else { - /* return the hostname */ - retstring = host->h_name; + len = sizeof(struct sockaddr_storage); + + ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf), + sbuf, sizeof(sbuf), NI_NUMERICSERV); + + if (ret != 0) { + /* On some systems (Darwin does it) we get EINTR from getnameinfo + * somehow. Eew. So we'll just return the IP, since that doesn't seem + * to exhibit that behaviour. */ + return getaddrstring(addr, 0); } - return m_strdup(retstring); + return m_strdup(hbuf); } + #ifdef DEBUG_TRACE void printhex(unsigned char* buf, int len) { @@ -44,10 +44,12 @@ void dropbear_trace(const char* format, ...); void printhex(unsigned char* buf, int len); #endif char * stripcontrol(const char * text); -unsigned char * getaddrstring(struct sockaddr * addr); +unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport); +int dropbear_listen(const char* address, const char* port, + int *socks, unsigned int sockcount, char **errstring, int *maxfd); int connect_remote(const char* remotehost, const char* remoteport, int nonblocking, char ** errstring); -char* getaddrhostname(struct sockaddr * addr); +char* getaddrhostname(struct sockaddr_storage * addr); int buf_readfile(buffer* buf, const char* filename); int buf_getline(buffer * line, FILE * authfile); @@ -14,15 +14,16 @@ void listeners_initialise() { void set_listener_fds(fd_set * readfds) { - unsigned int i; + unsigned int i, j; struct Listener *listener; /* check each in turn */ for (i = 0; i < ses.listensize; i++) { listener = ses.listeners[i]; if (listener != NULL) { - TRACE(("set listener fd %d", listener->sock)); - FD_SET(listener->sock, readfds); + for (j = 0; j < listener->nsocks; j++) { + FD_SET(listener->socks[j], readfds); + } } } } @@ -30,16 +31,19 @@ void set_listener_fds(fd_set * readfds) { void handle_listeners(fd_set * readfds) { - unsigned int i; + unsigned int i, j; struct Listener *listener; + int sock; /* check each in turn */ for (i = 0; i < ses.listensize; i++) { listener = ses.listeners[i]; if (listener != NULL) { - TRACE(("handle listener num %d fd %d", i, listener->sock)); - if (FD_ISSET(listener->sock, readfds)) { - listener->accepter(listener); + for (j = 0; j < listener->nsocks; j++) { + sock = listener->socks[j]; + if (FD_ISSET(sock, readfds)) { + listener->accepter(listener, sock); + } } } } @@ -48,8 +52,9 @@ void handle_listeners(fd_set * readfds) { /* accepter(int fd, void* typedata) is a function to accept connections, * cleanup(void* typedata) happens when cleaning up */ -struct Listener* new_listener(int sock, int type, void* typedata, - void (*accepter)(struct Listener*), +struct Listener* new_listener(int socks[], unsigned int nsocks, + int type, void* typedata, + void (*accepter)(struct Listener*, int sock), void (*cleanup)(struct Listener*)) { unsigned int i, j; @@ -65,7 +70,9 @@ struct Listener* new_listener(int sock, int type, void* typedata, if (i == ses.listensize) { if (ses.listensize > MAX_LISTENERS) { TRACE(("leave newlistener: too many already")); - close(sock); + for (j = 0; j < nsocks; j++) { + close(socks[i]); + } return NULL; } @@ -80,15 +87,18 @@ struct Listener* new_listener(int sock, int type, void* typedata, } } - ses.maxfd = MAX(ses.maxfd, sock); + for (j = 0; j < nsocks; j++) { + ses.maxfd = MAX(ses.maxfd, socks[j]); + } - TRACE(("new listener num %d fd %d", i, sock)); + TRACE(("new listener num %d ", i)); newlisten = (struct Listener*)m_malloc(sizeof(struct Listener)); newlisten->index = i; newlisten->type = type; newlisten->typedata = typedata; - newlisten->sock = sock; + newlisten->nsocks = nsocks; + memcpy(newlisten->socks, socks, nsocks * sizeof(int)); newlisten->accepter = accepter; newlisten->cleanup = cleanup; @@ -116,11 +126,15 @@ struct Listener * get_listener(int type, void* typedata, void remove_listener(struct Listener* listener) { + unsigned int j; + if (listener->cleanup) { listener->cleanup(listener); } - close(listener->sock); + for (j = 0; j < listener->nsocks; j++) { + close(listener->socks[j]); + } ses.listeners[listener->index] = NULL; m_free(listener); @@ -6,11 +6,12 @@ struct Listener { - int sock; + int socks[DROPBEAR_MAX_SOCKS]; + unsigned int nsocks; int index; /* index in the array of listeners */ - void (*accepter)(struct Listener*); + void (*accepter)(struct Listener*, int sock); void (*cleanup)(struct Listener*); int type; /* CHANNEL_ID_X11, CHANNEL_ID_AGENT, @@ -25,8 +26,9 @@ void listeners_initialise(); void handle_listeners(fd_set * readfds); void set_listener_fds(fd_set * readfds); -struct Listener* new_listener(int sock, int type, void* typedata, - void (*accepter)(struct Listener*), +struct Listener* new_listener(int socks[], unsigned int nsocks, + int type, void* typedata, + void (*accepter)(struct Listener*, int sock), void (*cleanup)(struct Listener*)); struct Listener * get_listener(int type, void* typedata, @@ -280,6 +280,9 @@ /* For a 4096 bit DSS key, empirically determined to be 1590 bytes */ #define MAX_PRIVKEY_SIZE 1600 +#define DROPBEAR_MAX_SOCKS 2 /* IPv4, IPv6 are all we'll get for now. Revisit + in a few years time.... */ + #ifndef ENABLE_X11FWD #define DISABLE_X11FWD #endif diff --git a/svr-agentfwd.c b/svr-agentfwd.c index 0dad2a4..b588586 100644 --- a/svr-agentfwd.c +++ b/svr-agentfwd.c @@ -44,7 +44,7 @@ static int send_msg_channel_open_agent(int fd); static int bindagent(int fd, struct ChanSess * chansess); -static void agentaccept(struct Listener * listener); +static void agentaccept(struct Listener * listener, int sock); /* Handles client requests to start agent forwarding, sets up listening socket. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ @@ -78,7 +78,7 @@ int agentreq(struct ChanSess * chansess) { } /* pass if off to listener */ - chansess->agentlistener = new_listener( fd, 0, chansess, + chansess->agentlistener = new_listener( &fd, 1, 0, chansess, agentaccept, NULL); if (chansess->agentlistener == NULL) { @@ -97,11 +97,11 @@ fail: /* accepts a connection on the forwarded socket and opens a new channel for it * back to the client */ /* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ -static void agentaccept(struct Listener * listener) { +static void agentaccept(struct Listener * listener, int sock) { int fd; - fd = accept(listener->sock, NULL, NULL); + fd = accept(sock, NULL, NULL); if (fd < 0) { TRACE(("accept failed")); return; diff --git a/svr-chansession.c b/svr-chansession.c index 9639961..a0e877c 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -408,6 +408,8 @@ static void get_termmodes(struct ChanSess *chansess) { } len = buf_getint(ses.payload); + TRACE(("term mode str %d p->l %d p->p %d", + len, ses.payload->len , ses.payload->pos)); if (len != ses.payload->len - ses.payload->pos) { dropbear_exit("bad term mode string"); } @@ -29,7 +29,7 @@ #include "signkey.h" #include "runopts.h" -static int listensockets(int *sock, int *maxfd); +static int listensockets(int *sock, int sockcount, int *maxfd); static void sigchld_handler(int dummy); static void sigsegv_handler(int); static void sigintterm_handler(int fish); @@ -49,10 +49,10 @@ int main(int argc, char ** argv) unsigned int i, j; int val; int maxsock = -1; - struct sockaddr remoteaddr; + struct sockaddr_storage remoteaddr; int remoteaddrlen; int listensocks[MAX_LISTEN_ADDR]; - unsigned int listensockcount = 0; + int listensockcount = 0; FILE * pidfile; int childsock; @@ -127,7 +127,10 @@ int main(int argc, char ** argv) /* Set up the listening sockets */ /* XXX XXX ports */ - listensockcount = listensockets(listensocks, &maxsock); + listensockcount = listensockets(listensocks, MAX_LISTEN_ADDR, &maxsock); + if (listensockcount < 0) { + dropbear_exit("No listening ports available."); + } /* incoming connection select loop */ for(;;) { @@ -138,7 +141,7 @@ int main(int argc, char ** argv) seltimeout.tv_usec = 0; /* listening sockets */ - for (i = 0; i < listensockcount; i++) { + for (i = 0; i < (unsigned int)listensockcount; i++) { FD_SET(listensocks[i], &fds); } @@ -179,12 +182,12 @@ int main(int argc, char ** argv) } /* handle each socket which has something to say */ - for (i = 0; i < listensockcount; i++) { + for (i = 0; i < (unsigned int)listensockcount; i++) { if (!FD_ISSET(listensocks[i], &fds)) continue; /* child connection XXX - ip6 stuff here */ - remoteaddrlen = sizeof(struct sockaddr_in); + remoteaddrlen = sizeof(remoteaddr); childsock = accept(listensocks[i], &remoteaddr, &remoteaddrlen); if (childsock < 0) { @@ -222,7 +225,7 @@ int main(int argc, char ** argv) monstartup((u_long)&_start, (u_long)&etext); #endif /* DEBUG_FORKGPROF */ - addrstring = getaddrstring(&remoteaddr); + addrstring = getaddrstring(&remoteaddr, 1); dropbear_log(LOG_INFO, "Child connection from %s", addrstring); m_free(addrstring); @@ -231,7 +234,7 @@ int main(int argc, char ** argv) } /* make sure we close sockets */ - for (i = 0; i < listensockcount; i++) { + for (i = 0; i < (unsigned int)listensockcount; i++) { if (m_close(listensocks[i]) == DROPBEAR_FAILURE) { dropbear_exit("Couldn't close socket"); } @@ -289,59 +292,30 @@ static void sigintterm_handler(int fish) { } /* Set up listening sockets for all the requested ports */ -static int listensockets(int *sock, int *maxfd) { +static int listensockets(int *sock, int sockcount, int *maxfd) { - int listensock; /* listening fd */ - struct sockaddr_in listen_addr; - struct linger linger; unsigned int i; - int val; + char portstring[6]; + char* errstring = NULL; + unsigned int sockpos = 0; + int nsock; for (i = 0; i < svr_opts.portcount; i++) { - /* iterate through all the sockets to listen on */ - listensock = socket(PF_INET, SOCK_STREAM, 0); - if (listensock < 0) { - dropbear_exit("Failed to create socket"); - } - - val = 1; - /* set to reuse, quick timeout */ - setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR, - (void*) &val, sizeof(val)); - linger.l_onoff = 1; - linger.l_linger = 5; - setsockopt(listensock, SOL_SOCKET, SO_LINGER, - (void*)&linger, sizeof(linger)); - - /* disable nagle */ - setsockopt(listensock, IPPROTO_TCP, TCP_NODELAY, - (void*)&val, sizeof(val)); - - memset((void*)&listen_addr, 0x0, sizeof(listen_addr)); - listen_addr.sin_family = AF_INET; - listen_addr.sin_port = htons(svr_opts.ports[i]); - listen_addr.sin_addr.s_addr = htonl(INADDR_ANY); - memset(&(listen_addr.sin_zero), '\0', 8); - - if (bind(listensock, (struct sockaddr *)&listen_addr, - sizeof(listen_addr)) < 0) { - dropbear_exit("Bind failed port %d", svr_opts.ports[i]); - } + snprintf(portstring, sizeof(portstring), "%d", svr_opts.ports[i]); + nsock = dropbear_listen(NULL, portstring, &sock[sockpos], + sockcount - sockpos, + &errstring, maxfd); - /* listen */ - if (listen(listensock, 20) < 0) { /* TODO set listen count */ - dropbear_exit("Listen failed"); + if (nsock < 0) { + dropbear_log(LOG_WARNING, "Failed listening on port %s: %s", + portstring, errstring); + m_free(errstring); + continue; } - /* nonblock */ - if (fcntl(listensock, F_SETFL, O_NONBLOCK) < 0) { - dropbear_exit("Failed to set non-blocking"); - } + sockpos += nsock; - sock[i] = listensock; - *maxfd = MAX(listensock, *maxfd); } - - return svr_opts.portcount; + return sockpos; } diff --git a/svr-session.c b/svr-session.c index e63ba32..d46adf4 100644 --- a/svr-session.c +++ b/svr-session.c @@ -35,10 +35,10 @@ #include "channel.h" #include "chansession.h" #include "atomicio.h" -#include "tcpfwd-direct.h" +#include "tcp-accept.h" +#include "tcp-connect.h" #include "service.h" #include "auth.h" -#include "tcpfwd-remote.h" #include "runopts.h" static void svr_remoteclosed(); @@ -65,7 +65,7 @@ static const packettype svr_packettypes[] = { static const struct ChanType *svr_chantypes[] = { &svrchansess, - &chan_tcpdirect, + &svr_chan_tcpdirect, NULL /* Null termination is mandatory. */ }; diff --git a/svr-tcpfwd.c b/svr-tcpfwd.c new file mode 100644 index 0000000..499ee46 --- /dev/null +++ b/svr-tcpfwd.c @@ -0,0 +1,196 @@ +#include "includes.h" +#include "ssh.h" +#include "tcp-accept.h" +#include "tcp-connect.h" +#include "dbutil.h" +#include "session.h" +#include "buffer.h" +#include "packet.h" +#include "listener.h" +#include "runopts.h" + +#ifndef DISABLE_SVR_REMOTETCPFWD + +static void send_msg_request_success(); +static void send_msg_request_failure(); +static int svr_cancelremotetcp(); +static int svr_remotetcpreq(); + + +const struct ChanType svr_chan_tcpdirect = { + 1, /* sepfds */ + "direct-tcpip", + newtcpdirect, /* init */ + NULL, /* checkclose */ + NULL, /* reqhandler */ + NULL /* closehandler */ +}; + +static const struct ChanType svr_chan_tcpremote = { + 1, /* sepfds */ + "forwarded-tcpip", + NULL, + NULL, + NULL, + NULL +}; + +/* At the moment this is completely used for tcp code (with the name reflecting + * that). If new request types are added, this should be replaced with code + * similar to the request-switching in chansession.c */ +void recv_msg_global_request_remotetcp() { + + unsigned char* reqname = NULL; + unsigned int namelen; + unsigned int wantreply = 0; + int ret = DROPBEAR_FAILURE; + + TRACE(("enter recv_msg_global_request_remotetcp")); + + if (opts.noremotetcp) { + TRACE(("leave recv_msg_global_request_remotetcp: remote tcp forwarding disabled")); + goto out; + } + + reqname = buf_getstring(ses.payload, &namelen); + wantreply = buf_getbyte(ses.payload); + + if (namelen > MAXNAMLEN) { + TRACE(("name len is wrong: %d", namelen)); + goto out; + } + + if (strcmp("tcpip-forward", reqname) == 0) { + ret = svr_remotetcpreq(); + } else if (strcmp("cancel-tcpip-forward", reqname) == 0) { + ret = svr_cancelremotetcp(); + } else { + TRACE(("reqname isn't tcpip-forward: '%s'", reqname)); + } + +out: + if (wantreply) { + if (ret == DROPBEAR_SUCCESS) { + send_msg_request_success(); + } else { + send_msg_request_failure(); + } + } + + m_free(reqname); + + TRACE(("leave recv_msg_global_request")); +} + + +static void send_msg_request_success() { + + CHECKCLEARTOWRITE(); + buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_SUCCESS); + encrypt_packet(); + +} + +static void send_msg_request_failure() { + + CHECKCLEARTOWRITE(); + buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_FAILURE); + encrypt_packet(); + +} + +static int matchtcp(void* typedata1, void* typedata2) { + + const struct TCPListener *info1 = (struct TCPListener*)typedata1; + const struct TCPListener *info2 = (struct TCPListener*)typedata2; + + return (info1->port == info2->port) + && (info1->chantype == info2->chantype) + && (strcmp(info1->addr, info2->addr) == 0); +} + +static int svr_cancelremotetcp() { + + int ret = DROPBEAR_FAILURE; + unsigned char * bindaddr = NULL; + unsigned int addrlen; + unsigned int port; + struct Listener * listener = NULL; + struct TCPListener tcpinfo; + + TRACE(("enter cancelremotetcp")); + + bindaddr = buf_getstring(ses.payload, &addrlen); + if (addrlen > MAX_IP_LEN) { + TRACE(("addr len too long: %d", addrlen)); + goto out; + } + + port = buf_getint(ses.payload); + + tcpinfo.addr = bindaddr; + tcpinfo.port = port; + listener = get_listener(CHANNEL_ID_TCPFORWARDED, &tcpinfo, matchtcp); + if (listener) { + remove_listener( listener ); + ret = DROPBEAR_SUCCESS; + } + +out: + m_free(bindaddr); + TRACE(("leave cancelremotetcp")); + return ret; +} + +static int svr_remotetcpreq() { + + int ret = DROPBEAR_FAILURE; + unsigned char * bindaddr = NULL; + unsigned int addrlen; + struct TCPListener *tcpinfo = NULL; + unsigned int port; + + TRACE(("enter remotetcpreq")); + + bindaddr = buf_getstring(ses.payload, &addrlen); + if (addrlen > MAX_IP_LEN) { + TRACE(("addr len too long: %d", addrlen)); + goto out; + } + + port = buf_getint(ses.payload); + + if (port == 0) { + dropbear_log(LOG_INFO, "Server chosen tcpfwd ports are unsupported"); + goto out; + } + + if (port < 1 || port > 65535) { + TRACE(("invalid port: %d", port)); + goto out; + } + + if (!ses.allowprivport && port < IPPORT_RESERVED) { + TRACE(("can't assign port < 1024 for non-root")); + goto out; + } + + tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener)); + tcpinfo->addr = bindaddr; + tcpinfo->port = port; + tcpinfo->localport = -1; + tcpinfo->chantype = &svr_chan_tcpremote; + + ret = listen_tcpfwd(tcpinfo); + +out: + if (ret == DROPBEAR_FAILURE) { + /* we only free it if a listener wasn't created, since the listener + * has to remember it if it's to be cancelled */ + m_free(tcpinfo->addr); + m_free(tcpinfo); + } + TRACE(("leave remotetcpreq")); + return ret; +} +#endif diff --git a/svr-x11fwd.c b/svr-x11fwd.c index aa0ba2d..0f4f71e 100644 --- a/svr-x11fwd.c +++ b/svr-x11fwd.c @@ -37,7 +37,7 @@ #define X11BASEPORT 6000 #define X11BINDBASE 6010 -static void x11accept(struct Listener* listener); +static void x11accept(struct Listener* listener, int sock); static int bindport(int fd); static int send_msg_channel_open_x11(int fd, struct sockaddr_in* addr); @@ -82,7 +82,7 @@ int x11req(struct ChanSess * chansess) { /* listener code will handle the socket now. * No cleanup handler needed, since listener_remove only happens * from our cleanup anyway */ - chansess->x11listener = new_listener( fd, 0, chansess, x11accept, NULL); + chansess->x11listener = new_listener( &fd, 1, 0, chansess, x11accept, NULL); if (chansess->x11listener == NULL) { goto fail; } @@ -100,7 +100,7 @@ fail: /* accepts a new X11 socket */ /* returns DROPBEAR_FAILURE or DROPBEAR_SUCCESS */ -static void x11accept(struct Listener* listener) { +static void x11accept(struct Listener* listener, int sock) { int fd; struct sockaddr_in addr; @@ -110,7 +110,7 @@ static void x11accept(struct Listener* listener) { len = sizeof(addr); - fd = accept(listener->sock, (struct sockaddr*)&addr, &len); + fd = accept(sock, (struct sockaddr*)&addr, &len); if (fd < 0) { return; } diff --git a/tcp-accept.c b/tcp-accept.c new file mode 100644 index 0000000..1fb80dd --- /dev/null +++ b/tcp-accept.c @@ -0,0 +1,88 @@ +#include "includes.h" +#include "ssh.h" +#include "tcp-accept.h" +#include "dbutil.h" +#include "session.h" +#include "buffer.h" +#include "packet.h" +#include "listener.h" +#include "runopts.h" + +#ifndef DISABLE_TCP_ACCEPT + +static void accept_tcp(struct Listener *listener, int sock) { + + int fd; + struct sockaddr_storage addr; + int len; + char ipstring[NI_MAXHOST], portstring[NI_MAXSERV]; + struct TCPListener *tcpinfo = (struct TCPListener*)(listener->typedata); + + len = sizeof(addr); + + fd = accept(sock, (struct sockaddr*)&addr, &len); + if (fd < 0) { + return; + } + + if (getnameinfo((struct sockaddr*)&addr, len, ipstring, sizeof(ipstring), + portstring, sizeof(portstring), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) { + return; + } + + if (send_msg_channel_open_init(fd, tcpinfo->chantype) == DROPBEAR_SUCCESS) { + + buf_putstring(ses.writepayload, tcpinfo->addr, strlen(tcpinfo->addr)); + buf_putint(ses.writepayload, tcpinfo->port); + buf_putstring(ses.writepayload, ipstring, strlen(ipstring)); + buf_putint(ses.writepayload, atol(portstring)); + encrypt_packet(); + + } else { + /* XXX debug? */ + close(fd); + } +} + +static void cleanup_tcp(struct Listener *listener) { + + struct TCPListener *tcpinfo = (struct TCPListener*)(listener->typedata); + + m_free(tcpinfo->addr); + m_free(tcpinfo); +} + + +int listen_tcpfwd(struct TCPListener* tcpinfo) { + + char portstring[6]; /* "65535\0" */ + int socks[DROPBEAR_MAX_SOCKS]; + struct Listener *listener = NULL; + int nsocks; + + 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->port); + nsocks = dropbear_listen(tcpinfo->addr, portstring, socks, + DROPBEAR_MAX_SOCKS, NULL, &ses.maxfd); + if (nsocks < 0) { + TRACE(("leave listen_tcpfwd: dropbear_listen failed")); + return DROPBEAR_FAILURE; + } + + listener = new_listener(socks, nsocks, CHANNEL_ID_TCPFORWARDED, tcpinfo, + accept_tcp, cleanup_tcp); + + if (listener == NULL) { + m_free(tcpinfo); + TRACE(("leave listen_tcpfwd: listener failed")); + return DROPBEAR_FAILURE; + } + + TRACE(("leave listen_tcpfwd: success")); + return DROPBEAR_SUCCESS; +} + +#endif /* DISABLE_REMOTETCPFWD */ diff --git a/tcp-accept.h b/tcp-accept.h new file mode 100644 index 0000000..96ddb76 --- /dev/null +++ b/tcp-accept.h @@ -0,0 +1,19 @@ +#ifndef _REMOTETCPFWD_H +#define _REMOTETCPFWD_H + +struct TCPListener { + + /* Local ones */ + unsigned char *localaddr; /* Can be NULL */ + unsigned int localport; + /* Remote ones: */ + unsigned char *remoteaddr; + unsigned int remoteport; + 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 new file mode 100644 index 0000000..00dbd8a --- /dev/null +++ b/tcp-connect.c @@ -0,0 +1,75 @@ +#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[6]; + 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 */ diff --git a/tcpfwd-direct.h b/tcp-connect.h index 20cd278..40ce22b 100644 --- a/tcpfwd-direct.h +++ b/tcp-connect.h @@ -28,7 +28,8 @@ #include "includes.h" #include "channel.h" -extern const struct ChanType chan_tcpdirect; +extern const struct ChanType svr_chan_tcpdirect; +int newtcpdirect(struct Channel * channel); #endif #endif diff --git a/tcpfwd-direct.c b/tcpfwd-direct.c deleted file mode 100644 index b611283..0000000 --- a/tcpfwd-direct.c +++ /dev/null @@ -1,159 +0,0 @@ -#include "includes.h" -#include "session.h" -#include "dbutil.h" -#include "channel.h" -#include "tcpfwd-direct.h" -#include "runopts.h" - -#ifndef DISABLE_TCPFWD_DIRECT -static int newtcpdirect(struct Channel * channel); -static int newtcp(const char * host, int port); - -const struct ChanType chan_tcpdirect = { - 1, /* sepfds */ - "direct-tcpip", - newtcpdirect, /* init */ - NULL, /* checkclose */ - NULL, /* reqhandler */ - NULL /* closehandler */ -}; - - -/* 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[6]; - 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; -} - -/* Initiate a new TCP connection - this is non-blocking, so the socket - * returned will need to be checked for success when it is first written. - * Similarities with OpenSSH's connect_to() are not coincidental. - * Returns -1 on failure */ -#if 0 -static int newtcp(const char * host, int port) { - - int sock = -1; - char portstring[6]; - struct addrinfo *res = NULL, *ai; - int val; - - struct addrinfo hints; - - TRACE(("enter newtcp")); - - memset(&hints, 0, sizeof(hints)); - /* TCP, either ip4 or ip6 */ - hints.ai_socktype = SOCK_STREAM; - hints.ai_family = PF_UNSPEC; - - snprintf(portstring, sizeof(portstring), "%d", port); - if (getaddrinfo(host, portstring, &hints, &res) != 0) { - if (res) { - freeaddrinfo(res); - } - TRACE(("leave newtcp: failed getaddrinfo")); - return -1; - } - - /* Use the first socket that works */ - for (ai = res; ai != NULL; ai = ai->ai_next) { - - if (ai->ai_family != PF_INET && ai->ai_family != PF_INET6) { - continue; - } - - sock = socket(ai->ai_family, SOCK_STREAM, 0); - if (sock < 0) { - TRACE(("TCP socket() failed")); - continue; - } - - if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { - close(sock); - TRACE(("TCP non-blocking failed")); - continue; - } - - /* non-blocking, so it might return without success (EINPROGRESS) */ - if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { - if (errno == EINPROGRESS) { - TRACE(("connect in progress")); - } else { - close(sock); - TRACE(("TCP connect failed")); - continue; - } - } - break; - } - - freeaddrinfo(res); - - if (ai == NULL) { - return -1; - } - - setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&val, sizeof(val)); - return sock; -} -#endif -#endif /* DISABLE_TCPFWD_DIRECT */ diff --git a/tcpfwd-remote.c b/tcpfwd-remote.c deleted file mode 100644 index d0a67a1..0000000 --- a/tcpfwd-remote.c +++ /dev/null @@ -1,317 +0,0 @@ -#include "includes.h" -#include "ssh.h" -#include "tcpfwd-remote.h" -#include "dbutil.h" -#include "session.h" -#include "buffer.h" -#include "packet.h" -#include "listener.h" -#include "runopts.h" - -#ifndef DISABLE_REMOTETCPFWD - -struct RemoteTCP { - - unsigned char* addr; - unsigned int port; - -}; - -static void send_msg_request_success(); -static void send_msg_request_failure(); -static int cancelremotetcp(); -static int remotetcpreq(); -static int listen_tcpfwd(unsigned char* bindaddr, unsigned int port); -static void acceptremote(struct Listener *listener); - -/* At the moment this is completely used for tcp code (with the name reflecting - * that). If new request types are added, this should be replaced with code - * similar to the request-switching in chansession.c */ -void recv_msg_global_request_remotetcp() { - - unsigned char* reqname = NULL; - unsigned int namelen; - unsigned int wantreply = 0; - int ret = DROPBEAR_FAILURE; - - TRACE(("enter recv_msg_global_request_remotetcp")); - - if (opts.noremotetcp) { - TRACE(("leave recv_msg_global_request_remotetcp: remote tcp forwarding disabled")); - goto out; - } - - reqname = buf_getstring(ses.payload, &namelen); - wantreply = buf_getbyte(ses.payload); - - if (namelen > MAXNAMLEN) { - TRACE(("name len is wrong: %d", namelen)); - goto out; - } - - if (strcmp("tcpip-forward", reqname) == 0) { - ret = remotetcpreq(); - } else if (strcmp("cancel-tcpip-forward", reqname) == 0) { - ret = cancelremotetcp(); - } else { - TRACE(("reqname isn't tcpip-forward: '%s'", reqname)); - } - -out: - if (wantreply) { - if (ret == DROPBEAR_SUCCESS) { - send_msg_request_success(); - } else { - send_msg_request_failure(); - } - } - - m_free(reqname); - - TRACE(("leave recv_msg_global_request")); -} - -static const struct ChanType chan_tcpremote = { - 1, /* sepfds */ - "forwarded-tcpip", - NULL, - NULL, - NULL, - NULL -}; - - -static void acceptremote(struct Listener *listener) { - - int fd; - struct sockaddr addr; - int len; - char ipstring[NI_MAXHOST], portstring[NI_MAXSERV]; - struct RemoteTCP *tcpinfo = (struct RemoteTCP*)(listener->typedata); - - len = sizeof(addr); - - fd = accept(listener->sock, &addr, &len); - if (fd < 0) { - return; - } - - if (getnameinfo(&addr, len, ipstring, sizeof(ipstring), portstring, - sizeof(portstring), NI_NUMERICHOST | NI_NUMERICSERV) != 0) { - return; - } - - if (send_msg_channel_open_init(fd, &chan_tcpremote) == DROPBEAR_SUCCESS) { - - buf_putstring(ses.writepayload, tcpinfo->addr, - strlen(tcpinfo->addr)); - buf_putint(ses.writepayload, tcpinfo->port); - buf_putstring(ses.writepayload, ipstring, strlen(ipstring)); - buf_putint(ses.writepayload, atol(portstring)); - encrypt_packet(); - - } else { - /* XXX debug? */ - close(fd); - } -} - -static void cleanupremote(struct Listener *listener) { - - struct RemoteTCP *tcpinfo = (struct RemoteTCP*)(listener->typedata); - - m_free(tcpinfo->addr); - m_free(tcpinfo); -} - -static void send_msg_request_success() { - - CHECKCLEARTOWRITE(); - buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_SUCCESS); - encrypt_packet(); - -} - -static void send_msg_request_failure() { - - CHECKCLEARTOWRITE(); - buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_FAILURE); - encrypt_packet(); - -} - -static int matchtcp(void* typedata1, void* typedata2) { - - const struct RemoteTCP *info1 = (struct RemoteTCP*)typedata1; - const struct RemoteTCP *info2 = (struct RemoteTCP*)typedata2; - - return info1->port == info2->port - && (strcmp(info1->addr, info2->addr) == 0); -} - -static int cancelremotetcp() { - - int ret = DROPBEAR_FAILURE; - unsigned char * bindaddr = NULL; - unsigned int addrlen; - unsigned int port; - struct Listener * listener = NULL; - struct RemoteTCP tcpinfo; - - TRACE(("enter cancelremotetcp")); - - bindaddr = buf_getstring(ses.payload, &addrlen); - if (addrlen > MAX_IP_LEN) { - TRACE(("addr len too long: %d", addrlen)); - goto out; - } - - port = buf_getint(ses.payload); - - tcpinfo.addr = bindaddr; - tcpinfo.port = port; - listener = get_listener(CHANNEL_ID_TCPFORWARDED, &tcpinfo, matchtcp); - if (listener) { - remove_listener( listener ); - ret = DROPBEAR_SUCCESS; - } - -out: - m_free(bindaddr); - TRACE(("leave cancelremotetcp")); - return ret; -} - -static int remotetcpreq() { - - int ret = DROPBEAR_FAILURE; - unsigned char * bindaddr = NULL; - unsigned int addrlen; - unsigned int port; - - TRACE(("enter remotetcpreq")); - - bindaddr = buf_getstring(ses.payload, &addrlen); - if (addrlen > MAX_IP_LEN) { - TRACE(("addr len too long: %d", addrlen)); - goto out; - } - - port = buf_getint(ses.payload); - - if (port == 0) { - dropbear_log(LOG_INFO, "Server chosen tcpfwd ports are unsupported"); - goto out; - } - - if (port < 1 || port > 65535) { - TRACE(("invalid port: %d", port)); - goto out; - } - - if (!ses.allowprivport && port < IPPORT_RESERVED) { - TRACE(("can't assign port < 1024 for non-root")); - goto out; - } - - ret = listen_tcpfwd(bindaddr, port); - -out: - if (ret == DROPBEAR_FAILURE) { - /* we only free it if a listener wasn't created, since the listener - * has to remember it if it's to be cancelled */ - m_free(bindaddr); - } - TRACE(("leave remotetcpreq")); - return ret; -} - -static int listen_tcpfwd(unsigned char* bindaddr, unsigned int port) { - - struct RemoteTCP * tcpinfo = NULL; - char portstring[6]; /* "65535\0" */ - struct addrinfo *res = NULL, *ai = NULL; - struct addrinfo hints; - int sock = -1; - struct Listener *listener = NULL; - - 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", port); - memset(&hints, 0x0, sizeof(hints)); - hints.ai_socktype = SOCK_STREAM; - hints.ai_family = PF_INET; - hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; - - if (getaddrinfo(bindaddr, portstring, &hints, &res) < 0) { - TRACE(("leave listen_tcpfwd: getaddrinfo failed: %s", - strerror(errno))); - goto done; - } - - /* find the first one which works */ - for (ai = res; ai != NULL; ai = ai->ai_next) { - if (ai->ai_family != PF_INET && ai->ai_family != PF_INET6) { - continue; - } - - sock = socket(ai->ai_family, SOCK_STREAM, 0); - if (sock < 0) { - TRACE(("socket failed: %s", strerror(errno))); - goto fail; - } - - if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { - TRACE(("bind failed: %s", strerror(errno))); - goto fail; - } - - if (listen(sock, 20) < 0) { - TRACE(("listen failed: %s", strerror(errno))); - goto fail; - } - - if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { - TRACE(("fcntl nonblocking failed: %s", strerror(errno))); - goto fail; - } - - /* success */ - break; - -fail: - close(sock); - } - - - if (ai == NULL) { - TRACE(("no successful sockets")); - goto done; - } - - tcpinfo = (struct RemoteTCP*)m_malloc(sizeof(struct RemoteTCP)); - tcpinfo->addr = bindaddr; - tcpinfo->port = port; - - listener = new_listener(sock, CHANNEL_ID_TCPFORWARDED, tcpinfo, - acceptremote, cleanupremote); - - if (listener == NULL) { - m_free(tcpinfo); - } - -done: - if (res) { - freeaddrinfo(res); - } - - TRACE(("leave listen_tcpfwd")); - if (listener == NULL) { - return DROPBEAR_FAILURE; - } else { - return DROPBEAR_SUCCESS; - } -} - -#endif /* DISABLE_REMOTETCPFWD */ diff --git a/tcpfwd-remote.h b/tcpfwd-remote.h deleted file mode 100644 index 64dbed3..0000000 --- a/tcpfwd-remote.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _REMOTETCPFWD_H -#define _REMOTETCPFWD_H - -void recv_msg_global_request_remotetcp(); - -#endif /* _REMOTETCPFWD_H */ |