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

just checkpointing

--HG--
extra : convert_revision : fbbf404290f3fea3dfa9f6f53eba9389057e9044
parent 254e8e34
......@@ -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)
......
......@@ -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 {
......
......@@ -47,6 +47,12 @@ static void printhelp() {
"-T Don't allocate a pty\n"
#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
......@@ -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 */
#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;
......
......@@ -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},
......
#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 "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
......
......@@ -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;
......
......@@ -408,6 +408,8 @@ static void get_termmodes(struct ChanSess *chansess) {
}
len = buf_getint(ses.payload);