mirror of
https://github.com/clearml/dropbear
synced 2025-06-26 18:17:32 +00:00
just checkpointing
--HG-- extra : convert_revision : fbbf404290f3fea3dfa9f6f53eba9389057e9044
This commit is contained in:
parent
254e8e3452
commit
a712baa8e5
11
Makefile.in
11
Makefile.in
@ -24,15 +24,16 @@ COMMONOBJS=dbutil.o buffer.o \
|
|||||||
|
|
||||||
SVROBJS=svr-kex.o svr-algo.o svr-auth.o sshpty.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-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 \
|
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-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 \
|
CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \
|
||||||
common-channel.o common-chansession.o termcodes.o loginrec.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
|
common-runopts.o
|
||||||
|
|
||||||
KEYOBJS=dropbearkey.o gendss.o genrsa.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 \
|
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 \
|
debug.h channel.h chansession.h config.h queue.h sshpty.h \
|
||||||
termcodes.h gendss.h genrsa.h authpubkey.h runopts.h includes.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 \
|
loginrec.h atomicio.h x11fwd.h agentfwd.h tcp-accept.h compat.h \
|
||||||
tcpfwd-remote.h listener.h
|
tcp-connect.h listener.h
|
||||||
|
|
||||||
dropbearobjs=$(COMMONOBJS) $(CLISVROBJS) $(SVROBJS)
|
dropbearobjs=$(COMMONOBJS) $(CLISVROBJS) $(SVROBJS)
|
||||||
dbclientobjs=$(COMMONOBJS) $(CLISVROBJS) $(CLIOBJS)
|
dbclientobjs=$(COMMONOBJS) $(CLISVROBJS) $(CLIOBJS)
|
||||||
|
|||||||
@ -13,17 +13,22 @@ static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign);
|
|||||||
void cli_pubkeyfail() {
|
void cli_pubkeyfail() {
|
||||||
|
|
||||||
struct PubkeyList *keyitem;
|
struct PubkeyList *keyitem;
|
||||||
|
struct PubkeyList **previtem;
|
||||||
|
|
||||||
TRACE(("enter cli_pubkeyfail"));
|
TRACE(("enter cli_pubkeyfail"));
|
||||||
|
previtem = &cli_opts.pubkeys;
|
||||||
|
|
||||||
/* Find the key we failed with, and remove it */
|
/* Find the key we failed with, and remove it */
|
||||||
for (keyitem = cli_opts.pubkeys; keyitem != NULL; keyitem = keyitem->next) {
|
for (keyitem = cli_opts.pubkeys; keyitem != NULL; keyitem = keyitem->next) {
|
||||||
if (keyitem->next == cli_ses.lastpubkey) {
|
if (keyitem == cli_ses.lastpubkey) {
|
||||||
keyitem->next = cli_ses.lastpubkey->next;
|
*previtem = keyitem->next;
|
||||||
}
|
}
|
||||||
|
previtem = &keyitem;
|
||||||
}
|
}
|
||||||
|
|
||||||
sign_key_free(cli_ses.lastpubkey->key); /* It won't be used again */
|
sign_key_free(cli_ses.lastpubkey->key); /* It won't be used again */
|
||||||
m_free(cli_ses.lastpubkey);
|
m_free(cli_ses.lastpubkey);
|
||||||
|
|
||||||
TRACE(("leave cli_pubkeyfail"));
|
TRACE(("leave cli_pubkeyfail"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,6 +150,7 @@ int cli_auth_pubkey() {
|
|||||||
/* Send a trial request */
|
/* Send a trial request */
|
||||||
send_msg_userauth_pubkey(cli_opts.pubkeys->key,
|
send_msg_userauth_pubkey(cli_opts.pubkeys->key,
|
||||||
cli_opts.pubkeys->type, 0);
|
cli_opts.pubkeys->type, 0);
|
||||||
|
cli_ses.lastpubkey = cli_opts.pubkeys;
|
||||||
TRACE(("leave cli_auth_pubkey-success"));
|
TRACE(("leave cli_auth_pubkey-success"));
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -47,6 +47,12 @@ static void printhelp() {
|
|||||||
"-T Don't allocate a pty\n"
|
"-T Don't allocate a pty\n"
|
||||||
#ifdef DROPBEAR_PUBKEY_AUTH
|
#ifdef DROPBEAR_PUBKEY_AUTH
|
||||||
"-i <identityfile> (multiple allowed)\n"
|
"-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
|
#endif
|
||||||
,DROPBEAR_VERSION, cli_opts.progname);
|
,DROPBEAR_VERSION, cli_opts.progname);
|
||||||
}
|
}
|
||||||
@ -59,6 +65,12 @@ void cli_getopts(int argc, char ** argv) {
|
|||||||
#ifdef DROPBEAR_PUBKEY_AUTH
|
#ifdef DROPBEAR_PUBKEY_AUTH
|
||||||
int nextiskey = 0; /* A flag if the next argument is a keyfile */
|
int nextiskey = 0; /* A flag if the next argument is a keyfile */
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef DROPBEAR_CLI_LOCALTCP
|
||||||
|
int nextislocal = 0;
|
||||||
|
#endif
|
||||||
|
#ifdef DROPBEAR_CLI_REMOTETCP
|
||||||
|
int nextisremote = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -71,6 +83,12 @@ void cli_getopts(int argc, char ** argv) {
|
|||||||
cli_opts.wantpty = 9; /* 9 means "it hasn't been touched", gets set later */
|
cli_opts.wantpty = 9; /* 9 means "it hasn't been touched", gets set later */
|
||||||
#ifdef DROPBEAR_PUBKEY_AUTH
|
#ifdef DROPBEAR_PUBKEY_AUTH
|
||||||
cli_opts.pubkeys = NULL;
|
cli_opts.pubkeys = NULL;
|
||||||
|
#endif
|
||||||
|
#ifdef DROPBEAR_CLI_LOCALTCP
|
||||||
|
cli_opts.localports = NULL;
|
||||||
|
#endif
|
||||||
|
#ifdef DROPBEAR_CLI_REMOTETCP
|
||||||
|
cli_opts.remoteports = NULL;
|
||||||
#endif
|
#endif
|
||||||
opts.nolocaltcp = 0;
|
opts.nolocaltcp = 0;
|
||||||
opts.noremotetcp = 0;
|
opts.noremotetcp = 0;
|
||||||
|
|||||||
@ -4,8 +4,8 @@
|
|||||||
#include "kex.h"
|
#include "kex.h"
|
||||||
#include "ssh.h"
|
#include "ssh.h"
|
||||||
#include "packet.h"
|
#include "packet.h"
|
||||||
#include "tcpfwd-direct.h"
|
#include "tcp-accept.h"
|
||||||
#include "tcpfwd-remote.h"
|
#include "tcp-connect.h"
|
||||||
#include "channel.h"
|
#include "channel.h"
|
||||||
#include "random.h"
|
#include "random.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
@ -31,7 +31,6 @@ static const packettype cli_packettypes[] = {
|
|||||||
{SSH_MSG_KEXDH_REPLY, recv_msg_kexdh_reply}, /* client */
|
{SSH_MSG_KEXDH_REPLY, recv_msg_kexdh_reply}, /* client */
|
||||||
{SSH_MSG_NEWKEYS, recv_msg_newkeys},
|
{SSH_MSG_NEWKEYS, recv_msg_newkeys},
|
||||||
{SSH_MSG_SERVICE_ACCEPT, recv_msg_service_accept}, /* client */
|
{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_REQUEST, recv_msg_channel_request},
|
||||||
{SSH_MSG_CHANNEL_OPEN, recv_msg_channel_open},
|
{SSH_MSG_CHANNEL_OPEN, recv_msg_channel_open},
|
||||||
{SSH_MSG_CHANNEL_EOF, recv_msg_channel_eof},
|
{SSH_MSG_CHANNEL_EOF, recv_msg_channel_eof},
|
||||||
|
|||||||
34
cli-tcpfwd.c
Normal file
34
cli-tcpfwd.c
Normal file
@ -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;
|
||||||
|
}
|
||||||
@ -32,8 +32,6 @@
|
|||||||
#include "dbutil.h"
|
#include "dbutil.h"
|
||||||
#include "channel.h"
|
#include "channel.h"
|
||||||
#include "ssh.h"
|
#include "ssh.h"
|
||||||
#include "tcpfwd-direct.h"
|
|
||||||
#include "tcpfwd-remote.h"
|
|
||||||
#include "listener.h"
|
#include "listener.h"
|
||||||
|
|
||||||
static void send_msg_channel_open_failure(unsigned int remotechan, int reason,
|
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) {
|
if (channel->type->closehandler) {
|
||||||
channel->type->closehandler(channel);
|
channel->type->closehandler(channel);
|
||||||
}
|
}
|
||||||
#if 0
|
|
||||||
if (channel->type == CHANNEL_ID_SESSION) {
|
|
||||||
send_exitsignalstatus(channel);
|
|
||||||
|
|
||||||
closechansess(channel);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
CHECKCLEARTOWRITE();
|
CHECKCLEARTOWRITE();
|
||||||
|
|
||||||
@ -545,23 +536,6 @@ void recv_msg_channel_request() {
|
|||||||
send_msg_channel_failure(channel);
|
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"));
|
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 */
|
/* success */
|
||||||
send_msg_channel_open_confirmation(channel, channel->recvwindow,
|
send_msg_channel_open_confirmation(channel, channel->recvwindow,
|
||||||
channel->recvmaxpacket);
|
channel->recvmaxpacket);
|
||||||
|
|||||||
173
dbutil.c
173
dbutil.c
@ -113,8 +113,109 @@ void dropbear_trace(const char* format, ...) {
|
|||||||
}
|
}
|
||||||
#endif /* DEBUG_TRACE */
|
#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
|
/* 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 connect_remote(const char* remotehost, const char* remoteport,
|
||||||
int nonblocking, char ** errstring) {
|
int nonblocking, char ** errstring) {
|
||||||
|
|
||||||
@ -197,58 +298,70 @@ int connect_remote(const char* remotehost, const char* remoteport,
|
|||||||
}
|
}
|
||||||
|
|
||||||
freeaddrinfo(res0);
|
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 sock;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return a string representation of the socket address passed. The return
|
/* Return a string representation of the socket address passed. The return
|
||||||
* value is allocated with malloc() */
|
* 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 */
|
len = sizeof(struct sockaddr_storage);
|
||||||
retstring = m_malloc(22);
|
|
||||||
|
|
||||||
switch (addr->sa_family) {
|
ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf),
|
||||||
case PF_INET:
|
sbuf, sizeof(sbuf), NI_NUMERICSERV | NI_NUMERICHOST);
|
||||||
snprintf(retstring, 22, "%s:%hu",
|
|
||||||
inet_ntoa(((struct sockaddr_in *)addr)->sin_addr),
|
|
||||||
((struct sockaddr_in *)addr)->sin_port);
|
|
||||||
break;
|
|
||||||
|
|
||||||
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;
|
return retstring;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the hostname corresponding to the address addr. On failure, the IP
|
/* Get the hostname corresponding to the address addr. On failure, the IP
|
||||||
* address is returned. The return value is allocated with strdup() */
|
* 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 hbuf[NI_MAXHOST];
|
||||||
char * retstring;
|
char sbuf[NI_MAXSERV];
|
||||||
|
int ret;
|
||||||
|
unsigned int len;
|
||||||
|
|
||||||
#ifdef DO_HOST_LOOKUP
|
len = sizeof(struct sockaddr_storage);
|
||||||
host = gethostbyaddr((char*)&((struct sockaddr_in*)addr)->sin_addr,
|
|
||||||
sizeof(struct in_addr), AF_INET);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (host == NULL) {
|
ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf),
|
||||||
/* return the address */
|
sbuf, sizeof(sbuf), NI_NUMERICSERV);
|
||||||
retstring = inet_ntoa(((struct sockaddr_in *)addr)->sin_addr);
|
|
||||||
} else {
|
if (ret != 0) {
|
||||||
/* return the hostname */
|
/* On some systems (Darwin does it) we get EINTR from getnameinfo
|
||||||
retstring = host->h_name;
|
* 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
|
#ifdef DEBUG_TRACE
|
||||||
void printhex(unsigned char* buf, int len) {
|
void printhex(unsigned char* buf, int len) {
|
||||||
|
|
||||||
|
|||||||
6
dbutil.h
6
dbutil.h
@ -44,10 +44,12 @@ void dropbear_trace(const char* format, ...);
|
|||||||
void printhex(unsigned char* buf, int len);
|
void printhex(unsigned char* buf, int len);
|
||||||
#endif
|
#endif
|
||||||
char * stripcontrol(const char * text);
|
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 connect_remote(const char* remotehost, const char* remoteport,
|
||||||
int nonblocking, char ** errstring);
|
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_readfile(buffer* buf, const char* filename);
|
||||||
int buf_getline(buffer * line, FILE * authfile);
|
int buf_getline(buffer * line, FILE * authfile);
|
||||||
|
|
||||||
|
|||||||
42
listener.c
42
listener.c
@ -14,15 +14,16 @@ void listeners_initialise() {
|
|||||||
|
|
||||||
void set_listener_fds(fd_set * readfds) {
|
void set_listener_fds(fd_set * readfds) {
|
||||||
|
|
||||||
unsigned int i;
|
unsigned int i, j;
|
||||||
struct Listener *listener;
|
struct Listener *listener;
|
||||||
|
|
||||||
/* check each in turn */
|
/* check each in turn */
|
||||||
for (i = 0; i < ses.listensize; i++) {
|
for (i = 0; i < ses.listensize; i++) {
|
||||||
listener = ses.listeners[i];
|
listener = ses.listeners[i];
|
||||||
if (listener != NULL) {
|
if (listener != NULL) {
|
||||||
TRACE(("set listener fd %d", listener->sock));
|
for (j = 0; j < listener->nsocks; j++) {
|
||||||
FD_SET(listener->sock, readfds);
|
FD_SET(listener->socks[j], readfds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -30,16 +31,19 @@ void set_listener_fds(fd_set * readfds) {
|
|||||||
|
|
||||||
void handle_listeners(fd_set * readfds) {
|
void handle_listeners(fd_set * readfds) {
|
||||||
|
|
||||||
unsigned int i;
|
unsigned int i, j;
|
||||||
struct Listener *listener;
|
struct Listener *listener;
|
||||||
|
int sock;
|
||||||
|
|
||||||
/* check each in turn */
|
/* check each in turn */
|
||||||
for (i = 0; i < ses.listensize; i++) {
|
for (i = 0; i < ses.listensize; i++) {
|
||||||
listener = ses.listeners[i];
|
listener = ses.listeners[i];
|
||||||
if (listener != NULL) {
|
if (listener != NULL) {
|
||||||
TRACE(("handle listener num %d fd %d", i, listener->sock));
|
for (j = 0; j < listener->nsocks; j++) {
|
||||||
if (FD_ISSET(listener->sock, readfds)) {
|
sock = listener->socks[j];
|
||||||
listener->accepter(listener);
|
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,
|
/* accepter(int fd, void* typedata) is a function to accept connections,
|
||||||
* cleanup(void* typedata) happens when cleaning up */
|
* cleanup(void* typedata) happens when cleaning up */
|
||||||
struct Listener* new_listener(int sock, int type, void* typedata,
|
struct Listener* new_listener(int socks[], unsigned int nsocks,
|
||||||
void (*accepter)(struct Listener*),
|
int type, void* typedata,
|
||||||
|
void (*accepter)(struct Listener*, int sock),
|
||||||
void (*cleanup)(struct Listener*)) {
|
void (*cleanup)(struct Listener*)) {
|
||||||
|
|
||||||
unsigned int i, j;
|
unsigned int i, j;
|
||||||
@ -65,7 +70,9 @@ struct Listener* new_listener(int sock, int type, void* typedata,
|
|||||||
if (i == ses.listensize) {
|
if (i == ses.listensize) {
|
||||||
if (ses.listensize > MAX_LISTENERS) {
|
if (ses.listensize > MAX_LISTENERS) {
|
||||||
TRACE(("leave newlistener: too many already"));
|
TRACE(("leave newlistener: too many already"));
|
||||||
close(sock);
|
for (j = 0; j < nsocks; j++) {
|
||||||
|
close(socks[i]);
|
||||||
|
}
|
||||||
return NULL;
|
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 = (struct Listener*)m_malloc(sizeof(struct Listener));
|
||||||
newlisten->index = i;
|
newlisten->index = i;
|
||||||
newlisten->type = type;
|
newlisten->type = type;
|
||||||
newlisten->typedata = typedata;
|
newlisten->typedata = typedata;
|
||||||
newlisten->sock = sock;
|
newlisten->nsocks = nsocks;
|
||||||
|
memcpy(newlisten->socks, socks, nsocks * sizeof(int));
|
||||||
newlisten->accepter = accepter;
|
newlisten->accepter = accepter;
|
||||||
newlisten->cleanup = cleanup;
|
newlisten->cleanup = cleanup;
|
||||||
|
|
||||||
@ -116,11 +126,15 @@ struct Listener * get_listener(int type, void* typedata,
|
|||||||
|
|
||||||
void remove_listener(struct Listener* listener) {
|
void remove_listener(struct Listener* listener) {
|
||||||
|
|
||||||
|
unsigned int j;
|
||||||
|
|
||||||
if (listener->cleanup) {
|
if (listener->cleanup) {
|
||||||
listener->cleanup(listener);
|
listener->cleanup(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
close(listener->sock);
|
for (j = 0; j < listener->nsocks; j++) {
|
||||||
|
close(listener->socks[j]);
|
||||||
|
}
|
||||||
ses.listeners[listener->index] = NULL;
|
ses.listeners[listener->index] = NULL;
|
||||||
m_free(listener);
|
m_free(listener);
|
||||||
|
|
||||||
|
|||||||
10
listener.h
10
listener.h
@ -6,11 +6,12 @@
|
|||||||
|
|
||||||
struct Listener {
|
struct Listener {
|
||||||
|
|
||||||
int sock;
|
int socks[DROPBEAR_MAX_SOCKS];
|
||||||
|
unsigned int nsocks;
|
||||||
|
|
||||||
int index; /* index in the array of listeners */
|
int index; /* index in the array of listeners */
|
||||||
|
|
||||||
void (*accepter)(struct Listener*);
|
void (*accepter)(struct Listener*, int sock);
|
||||||
void (*cleanup)(struct Listener*);
|
void (*cleanup)(struct Listener*);
|
||||||
|
|
||||||
int type; /* CHANNEL_ID_X11, CHANNEL_ID_AGENT,
|
int type; /* CHANNEL_ID_X11, CHANNEL_ID_AGENT,
|
||||||
@ -25,8 +26,9 @@ void listeners_initialise();
|
|||||||
void handle_listeners(fd_set * readfds);
|
void handle_listeners(fd_set * readfds);
|
||||||
void set_listener_fds(fd_set * readfds);
|
void set_listener_fds(fd_set * readfds);
|
||||||
|
|
||||||
struct Listener* new_listener(int sock, int type, void* typedata,
|
struct Listener* new_listener(int socks[], unsigned int nsocks,
|
||||||
void (*accepter)(struct Listener*),
|
int type, void* typedata,
|
||||||
|
void (*accepter)(struct Listener*, int sock),
|
||||||
void (*cleanup)(struct Listener*));
|
void (*cleanup)(struct Listener*));
|
||||||
|
|
||||||
struct Listener * get_listener(int type, void* typedata,
|
struct Listener * get_listener(int type, void* typedata,
|
||||||
|
|||||||
@ -280,6 +280,9 @@
|
|||||||
/* For a 4096 bit DSS key, empirically determined to be 1590 bytes */
|
/* For a 4096 bit DSS key, empirically determined to be 1590 bytes */
|
||||||
#define MAX_PRIVKEY_SIZE 1600
|
#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
|
#ifndef ENABLE_X11FWD
|
||||||
#define DISABLE_X11FWD
|
#define DISABLE_X11FWD
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -44,7 +44,7 @@
|
|||||||
|
|
||||||
static int send_msg_channel_open_agent(int fd);
|
static int send_msg_channel_open_agent(int fd);
|
||||||
static int bindagent(int fd, struct ChanSess * chansess);
|
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.
|
/* Handles client requests to start agent forwarding, sets up listening socket.
|
||||||
* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
|
* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
|
||||||
@ -78,7 +78,7 @@ int agentreq(struct ChanSess * chansess) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* pass if off to listener */
|
/* pass if off to listener */
|
||||||
chansess->agentlistener = new_listener( fd, 0, chansess,
|
chansess->agentlistener = new_listener( &fd, 1, 0, chansess,
|
||||||
agentaccept, NULL);
|
agentaccept, NULL);
|
||||||
|
|
||||||
if (chansess->agentlistener == NULL) {
|
if (chansess->agentlistener == NULL) {
|
||||||
@ -97,11 +97,11 @@ fail:
|
|||||||
/* accepts a connection on the forwarded socket and opens a new channel for it
|
/* accepts a connection on the forwarded socket and opens a new channel for it
|
||||||
* back to the client */
|
* back to the client */
|
||||||
/* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
|
/* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
|
||||||
static void agentaccept(struct Listener * listener) {
|
static void agentaccept(struct Listener * listener, int sock) {
|
||||||
|
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
fd = accept(listener->sock, NULL, NULL);
|
fd = accept(sock, NULL, NULL);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
TRACE(("accept failed"));
|
TRACE(("accept failed"));
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -408,6 +408,8 @@ static void get_termmodes(struct ChanSess *chansess) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
len = buf_getint(ses.payload);
|
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) {
|
if (len != ses.payload->len - ses.payload->pos) {
|
||||||
dropbear_exit("bad term mode string");
|
dropbear_exit("bad term mode string");
|
||||||
}
|
}
|
||||||
|
|||||||
84
svr-main.c
84
svr-main.c
@ -29,7 +29,7 @@
|
|||||||
#include "signkey.h"
|
#include "signkey.h"
|
||||||
#include "runopts.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 sigchld_handler(int dummy);
|
||||||
static void sigsegv_handler(int);
|
static void sigsegv_handler(int);
|
||||||
static void sigintterm_handler(int fish);
|
static void sigintterm_handler(int fish);
|
||||||
@ -49,10 +49,10 @@ int main(int argc, char ** argv)
|
|||||||
unsigned int i, j;
|
unsigned int i, j;
|
||||||
int val;
|
int val;
|
||||||
int maxsock = -1;
|
int maxsock = -1;
|
||||||
struct sockaddr remoteaddr;
|
struct sockaddr_storage remoteaddr;
|
||||||
int remoteaddrlen;
|
int remoteaddrlen;
|
||||||
int listensocks[MAX_LISTEN_ADDR];
|
int listensocks[MAX_LISTEN_ADDR];
|
||||||
unsigned int listensockcount = 0;
|
int listensockcount = 0;
|
||||||
FILE * pidfile;
|
FILE * pidfile;
|
||||||
|
|
||||||
int childsock;
|
int childsock;
|
||||||
@ -127,7 +127,10 @@ int main(int argc, char ** argv)
|
|||||||
|
|
||||||
/* Set up the listening sockets */
|
/* Set up the listening sockets */
|
||||||
/* XXX XXX ports */
|
/* 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 */
|
/* incoming connection select loop */
|
||||||
for(;;) {
|
for(;;) {
|
||||||
@ -138,7 +141,7 @@ int main(int argc, char ** argv)
|
|||||||
seltimeout.tv_usec = 0;
|
seltimeout.tv_usec = 0;
|
||||||
|
|
||||||
/* listening sockets */
|
/* listening sockets */
|
||||||
for (i = 0; i < listensockcount; i++) {
|
for (i = 0; i < (unsigned int)listensockcount; i++) {
|
||||||
FD_SET(listensocks[i], &fds);
|
FD_SET(listensocks[i], &fds);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,12 +182,12 @@ int main(int argc, char ** argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* handle each socket which has something to say */
|
/* 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))
|
if (!FD_ISSET(listensocks[i], &fds))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* child connection XXX - ip6 stuff here */
|
/* child connection XXX - ip6 stuff here */
|
||||||
remoteaddrlen = sizeof(struct sockaddr_in);
|
remoteaddrlen = sizeof(remoteaddr);
|
||||||
childsock = accept(listensocks[i], &remoteaddr, &remoteaddrlen);
|
childsock = accept(listensocks[i], &remoteaddr, &remoteaddrlen);
|
||||||
|
|
||||||
if (childsock < 0) {
|
if (childsock < 0) {
|
||||||
@ -222,7 +225,7 @@ int main(int argc, char ** argv)
|
|||||||
monstartup((u_long)&_start, (u_long)&etext);
|
monstartup((u_long)&_start, (u_long)&etext);
|
||||||
#endif /* DEBUG_FORKGPROF */
|
#endif /* DEBUG_FORKGPROF */
|
||||||
|
|
||||||
addrstring = getaddrstring(&remoteaddr);
|
addrstring = getaddrstring(&remoteaddr, 1);
|
||||||
dropbear_log(LOG_INFO, "Child connection from %s", addrstring);
|
dropbear_log(LOG_INFO, "Child connection from %s", addrstring);
|
||||||
m_free(addrstring);
|
m_free(addrstring);
|
||||||
|
|
||||||
@ -231,7 +234,7 @@ int main(int argc, char ** argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* make sure we close sockets */
|
/* 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) {
|
if (m_close(listensocks[i]) == DROPBEAR_FAILURE) {
|
||||||
dropbear_exit("Couldn't close socket");
|
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 */
|
/* 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;
|
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++) {
|
for (i = 0; i < svr_opts.portcount; i++) {
|
||||||
|
|
||||||
/* iterate through all the sockets to listen on */
|
snprintf(portstring, sizeof(portstring), "%d", svr_opts.ports[i]);
|
||||||
listensock = socket(PF_INET, SOCK_STREAM, 0);
|
nsock = dropbear_listen(NULL, portstring, &sock[sockpos],
|
||||||
if (listensock < 0) {
|
sockcount - sockpos,
|
||||||
dropbear_exit("Failed to create socket");
|
&errstring, maxfd);
|
||||||
|
|
||||||
|
if (nsock < 0) {
|
||||||
|
dropbear_log(LOG_WARNING, "Failed listening on port %s: %s",
|
||||||
|
portstring, errstring);
|
||||||
|
m_free(errstring);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
val = 1;
|
sockpos += nsock;
|
||||||
/* 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]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* listen */
|
|
||||||
if (listen(listensock, 20) < 0) { /* TODO set listen count */
|
|
||||||
dropbear_exit("Listen failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* nonblock */
|
|
||||||
if (fcntl(listensock, F_SETFL, O_NONBLOCK) < 0) {
|
|
||||||
dropbear_exit("Failed to set non-blocking");
|
|
||||||
}
|
|
||||||
|
|
||||||
sock[i] = listensock;
|
|
||||||
*maxfd = MAX(listensock, *maxfd);
|
|
||||||
}
|
}
|
||||||
|
return sockpos;
|
||||||
return svr_opts.portcount;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,10 +35,10 @@
|
|||||||
#include "channel.h"
|
#include "channel.h"
|
||||||
#include "chansession.h"
|
#include "chansession.h"
|
||||||
#include "atomicio.h"
|
#include "atomicio.h"
|
||||||
#include "tcpfwd-direct.h"
|
#include "tcp-accept.h"
|
||||||
|
#include "tcp-connect.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
#include "auth.h"
|
#include "auth.h"
|
||||||
#include "tcpfwd-remote.h"
|
|
||||||
#include "runopts.h"
|
#include "runopts.h"
|
||||||
|
|
||||||
static void svr_remoteclosed();
|
static void svr_remoteclosed();
|
||||||
@ -65,7 +65,7 @@ static const packettype svr_packettypes[] = {
|
|||||||
|
|
||||||
static const struct ChanType *svr_chantypes[] = {
|
static const struct ChanType *svr_chantypes[] = {
|
||||||
&svrchansess,
|
&svrchansess,
|
||||||
&chan_tcpdirect,
|
&svr_chan_tcpdirect,
|
||||||
NULL /* Null termination is mandatory. */
|
NULL /* Null termination is mandatory. */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
196
svr-tcpfwd.c
Normal file
196
svr-tcpfwd.c
Normal file
@ -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
|
||||||
@ -37,7 +37,7 @@
|
|||||||
#define X11BASEPORT 6000
|
#define X11BASEPORT 6000
|
||||||
#define X11BINDBASE 6010
|
#define X11BINDBASE 6010
|
||||||
|
|
||||||
static void x11accept(struct Listener* listener);
|
static void x11accept(struct Listener* listener, int sock);
|
||||||
static int bindport(int fd);
|
static int bindport(int fd);
|
||||||
static int send_msg_channel_open_x11(int fd, struct sockaddr_in* addr);
|
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.
|
/* listener code will handle the socket now.
|
||||||
* No cleanup handler needed, since listener_remove only happens
|
* No cleanup handler needed, since listener_remove only happens
|
||||||
* from our cleanup anyway */
|
* 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) {
|
if (chansess->x11listener == NULL) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@ -100,7 +100,7 @@ fail:
|
|||||||
|
|
||||||
/* accepts a new X11 socket */
|
/* accepts a new X11 socket */
|
||||||
/* returns DROPBEAR_FAILURE or DROPBEAR_SUCCESS */
|
/* returns DROPBEAR_FAILURE or DROPBEAR_SUCCESS */
|
||||||
static void x11accept(struct Listener* listener) {
|
static void x11accept(struct Listener* listener, int sock) {
|
||||||
|
|
||||||
int fd;
|
int fd;
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
@ -110,7 +110,7 @@ static void x11accept(struct Listener* listener) {
|
|||||||
|
|
||||||
len = sizeof(addr);
|
len = sizeof(addr);
|
||||||
|
|
||||||
fd = accept(listener->sock, (struct sockaddr*)&addr, &len);
|
fd = accept(sock, (struct sockaddr*)&addr, &len);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
88
tcp-accept.c
Normal file
88
tcp-accept.c
Normal file
@ -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 */
|
||||||
19
tcp-accept.h
Normal file
19
tcp-accept.h
Normal file
@ -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 */
|
||||||
75
tcp-connect.c
Normal file
75
tcp-connect.c
Normal file
@ -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 */
|
||||||
@ -28,7 +28,8 @@
|
|||||||
#include "includes.h"
|
#include "includes.h"
|
||||||
#include "channel.h"
|
#include "channel.h"
|
||||||
|
|
||||||
extern const struct ChanType chan_tcpdirect;
|
extern const struct ChanType svr_chan_tcpdirect;
|
||||||
|
int newtcpdirect(struct Channel * channel);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
159
tcpfwd-direct.c
159
tcpfwd-direct.c
@ -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 */
|
|
||||||
317
tcpfwd-remote.c
317
tcpfwd-remote.c
@ -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 */
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
#ifndef _REMOTETCPFWD_H
|
|
||||||
#define _REMOTETCPFWD_H
|
|
||||||
|
|
||||||
void recv_msg_global_request_remotetcp();
|
|
||||||
|
|
||||||
#endif /* _REMOTETCPFWD_H */
|
|
||||||
Loading…
Reference in New Issue
Block a user