From 674a60748884dc55ee7091b7c23a41240e75f73c Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 1 Jun 2004 02:46:09 +0000 Subject: Makefile.in contains updated files required --HG-- extra : convert_revision : cc8a8c49dc70e632c352853a39801089b08149be --- svr-session.c | 238 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 svr-session.c (limited to 'svr-session.c') diff --git a/svr-session.c b/svr-session.c new file mode 100644 index 0000000..bc24487 --- /dev/null +++ b/svr-session.c @@ -0,0 +1,238 @@ +/* + * Dropbear - a SSH2 server + * + * Copyright (c) 2002,2003 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + +#include "includes.h" +#include "session.h" +#include "dbutil.h" +#include "packet.h" +#include "algo.h" +#include "buffer.h" +#include "dss.h" +#include "ssh.h" +#include "random.h" +#include "kex.h" +#include "channel.h" +#include "chansession.h" +#include "atomicio.h" + +static void svr_remoteclosed(); +static void svr_dropbear_exit(int exitcode, const char* format, va_list param); +static void svr_dropbear_log(int priority, const char* format, va_list param); + +struct serversession svr_ses; + +void svr_session(int sock, runopts *opts, int childpipe, + struct sockaddr* remoteaddr) { + + fd_set readfd, writefd; + struct timeval timeout; + int val; + + crypto_init(); + common_session_init(sock, opts); + + ses.remoteaddr = remoteaddr; + ses.remotehost = getaddrhostname(remoteaddr); + + /* Initialise server specific parts of the session */ + svr_ses.childpipe = childpipe; + authinitialise(); + svr_chansessinitialise(); + + if (gettimeofday(&timeout, 0) < 0) { + dropbear_exit("Error getting time"); + } + + ses.connecttimeout = timeout.tv_sec + AUTH_TIMEOUT; + + /* set up messages etc */ + session_remoteclosed = svr_remoteclosed; + _dropbear_exit = svr_dropbear_exit; + _dropbear_log = svr_dropbear_log; + + /* We're ready to go now */ + sessinitdone = 1; + + /* exchange identification, version etc */ + session_identification(); + + seedrandom(); + + /* start off with key exchange */ + send_msg_kexinit(); + + FD_ZERO(&readfd); + FD_ZERO(&writefd); + + /* main loop, select()s for all sockets in use */ + for(;;) { + + timeout.tv_sec = SELECT_TIMEOUT; + timeout.tv_usec = 0; + FD_ZERO(&writefd); + FD_ZERO(&readfd); + assert(ses.payload == NULL); + if (ses.sock != -1) { + FD_SET(ses.sock, &readfd); + if (!isempty(&ses.writequeue)) { + FD_SET(ses.sock, &writefd); + } + } + + /* set up for channels which require reading/writing */ + if (ses.dataallowed) { + setchannelfds(&readfd, &writefd); + } + val = select(ses.maxfd+1, &readfd, &writefd, NULL, &timeout); + + if (exitflag) { + dropbear_exit("Terminated by signal"); + } + + if (val < 0) { + if (errno == EINTR) { + continue; + } else { + dropbear_exit("Error in select"); + } + } + + /* check for auth timeout, rekeying required etc */ + checktimeouts(); + + if (val == 0) { + /* timeout */ + TRACE(("select timeout")); + continue; + } + + /* process session socket's incoming/outgoing data */ + if (ses.sock != -1) { + if (FD_ISSET(ses.sock, &writefd) && !isempty(&ses.writequeue)) { + write_packet(); + } + + if (FD_ISSET(ses.sock, &readfd)) { + read_packet(); + } + + /* Process the decrypted packet. After this, the read buffer + * will be ready for a new packet */ + if (ses.payload != NULL) { + svr_process_packet(); + } + } + + /* process pipes etc for the channels, ses.dataallowed == 0 + * during rekeying ) */ + if (ses.dataallowed) { + channelio(&readfd, &writefd); + } + + } /* for(;;) */ +} + + + +/* called when the remote side closes the connection */ +static void svr_remoteclosed() { + + close(ses.sock); + ses.sock = -1; + dropbear_close("Exited normally"); + +} + +/* failure exit - format must be <= 100 chars */ +static void svr_dropbear_exit(int exitcode, const char* format, va_list param) { + + char fmtbuf[300]; + + if (!sessinitdone) { + /* before session init */ + snprintf(fmtbuf, sizeof(fmtbuf), + "premature exit: %s", format); + } else if (svr_ses.authstate.authdone) { + /* user has authenticated */ + snprintf(fmtbuf, sizeof(fmtbuf), + "exit after auth (%s): %s", + svr_ses.authstate.printableuser, format); + } else if (svr_ses.authstate.printableuser) { + /* we have a potential user */ + snprintf(fmtbuf, sizeof(fmtbuf), + "exit before auth (user '%s', %d fails): %s", + svr_ses.authstate.printableuser, svr_ses.authstate.failcount, format); + } else { + /* before userauth */ + snprintf(fmtbuf, sizeof(fmtbuf), + "exit before auth: %s", format); + } + + if (errno != 0) { + /* XXX - is this valid? */ + snprintf(fmtbuf, sizeof(fmtbuf), "%s [%d %s]", fmtbuf, + errno, strerror(errno)); + } + + _dropbear_log(LOG_INFO, fmtbuf, param); + + /* must be after we've done with username etc */ + common_session_cleanup(); + + exit(exitcode); + +} + +/* priority is priority as with syslog() */ +static void svr_dropbear_log(int priority, const char* format, va_list param) { + + char printbuf[1024]; + char datestr[20]; + time_t timesec; + int havetrace = 0; + + vsnprintf(printbuf, sizeof(printbuf), format, param); + +#ifndef DISABLE_SYSLOG + if (usingsyslog) { + syslog(priority, "%s", printbuf); + } +#endif + + /* if we are using DEBUG_TRACE, we want to print to stderr even if + * syslog is used, so it is included in error reports */ +#ifdef DEBUG_TRACE + havetrace = 1; +#endif + + if (!usingsyslog || havetrace) + { + timesec = time(NULL); + if (strftime(datestr, sizeof(datestr), "%b %d %H:%M:%S", + localtime(×ec)) == 0) { + datestr[0] = '?'; datestr[1] = '\0'; + } + fprintf(stderr, "[%d] %s %s\n", getpid(), datestr, printbuf); + } +} -- cgit v1.2.3 From 40cb39d00c8c1580c869e8bb84f83fa141d2509f Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 1 Jun 2004 04:20:12 +0000 Subject: syntactical fixups - it compiles, but channel handling code requires fixing. --HG-- extra : convert_revision : 6f8be81d5486f3499fde596d8e86df2630b24442 --- main.c | 7 +++++-- session.h | 3 +++ svr-agentfwd.c | 8 ++++---- svr-session.c | 29 ++++++++++++----------------- 4 files changed, 24 insertions(+), 23 deletions(-) (limited to 'svr-session.c') diff --git a/main.c b/main.c index 245dd86..0087895 100644 --- a/main.c +++ b/main.c @@ -62,8 +62,11 @@ int main(int argc, char ** argv) struct sigaction sa_chld; + _dropbear_exit = svr_dropbear_exit; + _dropbear_log = svr_dropbear_log; + /* get commandline options */ - opts = getrunopts(argc, argv); + opts = svr_getopts(argc, argv); /* fork */ if (opts->forkbg) { @@ -239,7 +242,7 @@ int main(int argc, char ** argv) dropbear_exit("Couldn't close socket"); } /* start the session */ - child_session(childsock, opts, childpipe[1], &remoteaddr); + svr_session(childsock, opts, childpipe[1], &remoteaddr); /* don't return */ assert(0); } diff --git a/session.h b/session.h index 2760da4..51e3ae4 100644 --- a/session.h +++ b/session.h @@ -48,6 +48,9 @@ extern void(*session_remoteclosed)(); /* Server */ void svr_session(int sock, runopts *opts, int childpipe, struct sockaddr *remoteaddr); +void svr_dropbear_exit(int exitcode, const char* format, va_list param); +void svr_dropbear_log(int priority, const char* format, va_list param); + struct key_context { diff --git a/svr-agentfwd.c b/svr-agentfwd.c index c4958c4..f8af30e 100644 --- a/svr-agentfwd.c +++ b/svr-agentfwd.c @@ -141,8 +141,8 @@ void agentcleanup(struct ChanSess * chansess) { * for themselves */ uid = getuid(); gid = getgid(); - if ((setegid(ses.authstate.pw->pw_gid)) < 0 || - (seteuid(ses.authstate.pw->pw_uid)) < 0) { + if ((setegid(svr_ses.authstate.pw->pw_gid)) < 0 || + (seteuid(svr_ses.authstate.pw->pw_uid)) < 0) { dropbear_exit("failed to set euid"); } @@ -194,8 +194,8 @@ static int bindagent(struct ChanSess * chansess) { /* drop to user privs to make the dir/file */ uid = getuid(); gid = getgid(); - if ((setegid(ses.authstate.pw->pw_gid)) < 0 || - (seteuid(ses.authstate.pw->pw_uid)) < 0) { + if ((setegid(svr_ses.authstate.pw->pw_gid)) < 0 || + (seteuid(svr_ses.authstate.pw->pw_uid)) < 0) { dropbear_exit("failed to set euid"); } diff --git a/svr-session.c b/svr-session.c index bc24487..1aade1f 100644 --- a/svr-session.c +++ b/svr-session.c @@ -37,8 +37,6 @@ #include "atomicio.h" static void svr_remoteclosed(); -static void svr_dropbear_exit(int exitcode, const char* format, va_list param); -static void svr_dropbear_log(int priority, const char* format, va_list param); struct serversession svr_ses; @@ -68,8 +66,6 @@ void svr_session(int sock, runopts *opts, int childpipe, /* set up messages etc */ session_remoteclosed = svr_remoteclosed; - _dropbear_exit = svr_dropbear_exit; - _dropbear_log = svr_dropbear_log; /* We're ready to go now */ sessinitdone = 1; @@ -153,19 +149,8 @@ void svr_session(int sock, runopts *opts, int childpipe, } /* for(;;) */ } - - -/* called when the remote side closes the connection */ -static void svr_remoteclosed() { - - close(ses.sock); - ses.sock = -1; - dropbear_close("Exited normally"); - -} - /* failure exit - format must be <= 100 chars */ -static void svr_dropbear_exit(int exitcode, const char* format, va_list param) { +void svr_dropbear_exit(int exitcode, const char* format, va_list param) { char fmtbuf[300]; @@ -205,7 +190,7 @@ static void svr_dropbear_exit(int exitcode, const char* format, va_list param) { } /* priority is priority as with syslog() */ -static void svr_dropbear_log(int priority, const char* format, va_list param) { +void svr_dropbear_log(int priority, const char* format, va_list param) { char printbuf[1024]; char datestr[20]; @@ -236,3 +221,13 @@ static void svr_dropbear_log(int priority, const char* format, va_list param) { fprintf(stderr, "[%d] %s %s\n", getpid(), datestr, printbuf); } } + +/* called when the remote side closes the connection */ +static void svr_remoteclosed() { + + close(ses.sock); + ses.sock = -1; + dropbear_close("Exited normally"); + +} + -- cgit v1.2.3 From 615226304502fa609213917042bffce8c4c83241 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 1 Jun 2004 10:48:46 +0000 Subject: Sorted out the first channel init issues. --HG-- extra : convert_revision : 67676f36b78efac878c11943d78a5de827498d05 --- chansession.h | 5 ++--- common-channel.c | 9 ++++++--- common-session.c | 2 ++ debug.h | 2 +- session.h | 2 +- svr-chansession.c | 8 ++++---- svr-session.c | 7 +++++++ 7 files changed, 23 insertions(+), 12 deletions(-) (limited to 'svr-session.c') diff --git a/chansession.h b/chansession.h index 1e4ba9a..85dc9c1 100644 --- a/chansession.h +++ b/chansession.h @@ -68,16 +68,15 @@ struct ChildPid { }; -void newchansess(struct Channel * channel); void chansessionrequest(struct Channel * channel); -void closechansess(struct Channel * channel); -void svr_chansessinitialise(); void send_msg_chansess_exitstatus(struct Channel * channel, struct ChanSess * chansess); void send_msg_chansess_exitsignal(struct Channel * channel, struct ChanSess * chansess); void addnewvar(const char* param, const char* var); +void svr_chansessinitialise(); +extern const struct ChanType svrchansess; struct SigMap { int signal; diff --git a/common-channel.c b/common-channel.c index 8b5423a..135e098 100644 --- a/common-channel.c +++ b/common-channel.c @@ -61,7 +61,7 @@ static void closechanfd(struct Channel *channel, int fd, int how); #define FD_CLOSED (-1) /* Initialise all the channels */ -void chaninitialise(struct ChanType *chantypes[]) { +void chaninitialise(const struct ChanType *chantypes[]) { /* may as well create space for a single channel */ ses.channels = (struct Channel**)m_malloc(sizeof(struct Channel*)); @@ -737,7 +737,7 @@ void recv_msg_channel_open() { unsigned int typelen; unsigned int remotechan, transwindow, transmaxpacket; struct Channel *channel; - struct ChanType *chantype; + const struct ChanType *chantype; unsigned int errtype = SSH_OPEN_UNKNOWN_CHANNEL_TYPE; int ret; @@ -775,14 +775,16 @@ void recv_msg_channel_open() { channel = newchannel(remotechan, chantype, transwindow, transmaxpacket); if (channel == NULL) { + TRACE(("newchannel returned NULL")); goto failure; } if (channel->type->inithandler) { ret = channel->type->inithandler(channel); - if (ret >= 0) { + if (ret > 0) { errtype = ret; deletechannel(channel); + TRACE(("inithandler returned failure %d", ret)); goto failure; } } @@ -810,6 +812,7 @@ void recv_msg_channel_open() { goto cleanup; failure: + TRACE(("recv_msg_channel_open failure")); send_msg_channel_open_failure(remotechan, errtype, "", ""); cleanup: diff --git a/common-session.c b/common-session.c index 5e87c9a..fce301a 100644 --- a/common-session.c +++ b/common-session.c @@ -106,6 +106,8 @@ void common_session_init(int sock, runopts *opts) { ses.dh_K = NULL; ses.remoteident = NULL; + ses.chantypes = NULL; + TRACE(("leave session_init")); } diff --git a/debug.h b/debug.h index 2b4a3dc..75f9503 100644 --- a/debug.h +++ b/debug.h @@ -34,7 +34,7 @@ /* #define DEBUG_VALGRIND */ /* Define this to print trace statements - very verbose */ -/* #define DEBUG_TRACE */ +#define DEBUG_TRACE /* All functions writing to the cleartext payload buffer call * CHECKCLEARTOWRITE() before writing. This is only really useful if you're diff --git a/session.h b/session.h index 51e3ae4..e372232 100644 --- a/session.h +++ b/session.h @@ -134,7 +134,7 @@ struct sshsession { /* Channel related */ struct Channel ** channels; /* these pointers may be null */ unsigned int chansize; /* the number of Channel*s allocated for channels */ - struct ChanType **chantypes; /* The valid channel types */ + const struct ChanType **chantypes; /* The valid channel types */ /* TCP forwarding - where manage listeners */ diff --git a/svr-chansession.c b/svr-chansession.c index dce0827..2705069 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -50,7 +50,7 @@ static void execchild(struct ChanSess *chansess); static void addchildpid(struct ChanSess *chansess, pid_t pid); static void sesssigchild_handler(int val); static void closechansess(struct Channel *channel); -static void newchansess(struct Channel *channel); +static int newchansess(struct Channel *channel); static void chansessionrequest(struct Channel *channel); static void send_exitsignalstatus(struct Channel *channel); @@ -205,7 +205,7 @@ static void send_msg_chansess_exitsignal(struct Channel * channel, } /* set up a session channel */ -static void newchansess(struct Channel *channel) { +static int newchansess(struct Channel *channel) { struct ChanSess *chansess; @@ -241,6 +241,8 @@ static void newchansess(struct Channel *channel) { chansess->agentdir = NULL; #endif + return 0; + } /* clean a session channel */ @@ -310,8 +312,6 @@ static void chansessionrequest(struct Channel *channel) { TRACE(("enter chansessionrequest")); - assert(channel->type == CHANNEL_ID_SESSION); - type = buf_getstring(ses.payload, &typelen); wantreply = buf_getbyte(ses.payload); diff --git a/svr-session.c b/svr-session.c index 1aade1f..c6f05cc 100644 --- a/svr-session.c +++ b/svr-session.c @@ -40,6 +40,12 @@ static void svr_remoteclosed(); struct serversession svr_ses; +const struct ChanType *chantypes[] = { + &svrchansess, + NULL /* Null termination is mandatory. */ +}; + + void svr_session(int sock, runopts *opts, int childpipe, struct sockaddr* remoteaddr) { @@ -56,6 +62,7 @@ void svr_session(int sock, runopts *opts, int childpipe, /* Initialise server specific parts of the session */ svr_ses.childpipe = childpipe; authinitialise(); + chaninitialise(chantypes); svr_chansessinitialise(); if (gettimeofday(&timeout, 0) < 0) { -- cgit v1.2.3 From 513f947d62351e5af77676e20740232d753cd5b1 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Wed, 2 Jun 2004 04:59:49 +0000 Subject: Chantype handling is sorted --HG-- extra : convert_revision : 807efead6ecf690f147fd8145aa9d78ff894cdb2 --- channel.h | 5 +++-- common-channel.c | 19 +++++++++++------ common-chansession.c | 2 +- debug.h | 4 +++- localtcpfwd.c | 15 ++++++++++++- localtcpfwd.h | 2 +- remotetcpfwd.c | 1 + svr-chansession.c | 60 ++++++++++++++++++++++++++-------------------------- svr-session.c | 2 ++ 9 files changed, 67 insertions(+), 43 deletions(-) (limited to 'svr-session.c') diff --git a/channel.h b/channel.h index 750a9bb..e033164 100644 --- a/channel.h +++ b/channel.h @@ -81,7 +81,7 @@ struct Channel { int initconn; /* used for TCP forwarding, whether the channel has been fully initialised */ - struct ChanType* type; + const struct ChanType* type; }; @@ -100,7 +100,8 @@ void chaninitialise(); void chancleanup(); void setchannelfds(fd_set *readfd, fd_set *writefd); void channelio(fd_set *readfd, fd_set *writefd); -struct Channel* newchannel(unsigned int remotechan, struct ChanType *type, +struct Channel* newchannel(unsigned int remotechan, + const struct ChanType *type, unsigned int transwindow, unsigned int transmaxpacket); void recv_msg_channel_open(); diff --git a/common-channel.c b/common-channel.c index 135e098..4643fc2 100644 --- a/common-channel.c +++ b/common-channel.c @@ -96,7 +96,8 @@ void chancleanup() { /* If remotechan, transwindow and transmaxpacket are not know (for a new * outgoing connection, with them to be filled on confirmation), they should * all be set to 0 */ -struct Channel* newchannel(unsigned int remotechan, struct ChanType *type, +struct Channel* newchannel(unsigned int remotechan, + const struct ChanType *type, unsigned int transwindow, unsigned int transmaxpacket) { struct Channel * newchan; @@ -535,8 +536,6 @@ void recv_msg_channel_request() { dropbear_exit("Unknown channel"); } - TRACE(("chan type is %d", channel->type)); - if (channel->type->reqhandler) { channel->type->reqhandler(channel); } else { @@ -737,6 +736,7 @@ void recv_msg_channel_open() { unsigned int typelen; unsigned int remotechan, transwindow, transmaxpacket; struct Channel *channel; + const struct ChanType **cp; const struct ChanType *chantype; unsigned int errtype = SSH_OPEN_UNKNOWN_CHANNEL_TYPE; int ret; @@ -758,19 +758,24 @@ void recv_msg_channel_open() { goto failure; } - /* Get the channel type. This will depend if it is a client or a server, - * so we iterate through the connection-specific list which was - * set up when the connection started */ - for (chantype = ses.chantypes[0]; chantype != NULL; chantype++) { + /* Get the channel type. Client and server style invokation will set up a + * different list for ses.chantypes at startup. We just iterate through + * this list and find the matching name */ + for (cp = &ses.chantypes[0], chantype = (*cp); + chantype != NULL; + cp++, chantype = (*cp)) { if (strcmp(type, chantype->name) == 0) { break; } } if (chantype == NULL) { + TRACE(("No matching type for '%s'", type)); goto failure; } + TRACE(("matched type '%s'", type)); + /* create the channel */ channel = newchannel(remotechan, chantype, transwindow, transmaxpacket); diff --git a/common-chansession.c b/common-chansession.c index ad9c7ed..b350c6c 100644 --- a/common-chansession.c +++ b/common-chansession.c @@ -25,7 +25,7 @@ #include "chansession.h" /* Mapping of signal values to ssh signal strings */ -const extern struct SigMap signames[] = { +const struct SigMap signames[] = { {SIGABRT, "ABRT"}, {SIGALRM, "ALRM"}, {SIGFPE, "FPE"}, diff --git a/debug.h b/debug.h index 75f9503..f41a2e4 100644 --- a/debug.h +++ b/debug.h @@ -34,7 +34,9 @@ /* #define DEBUG_VALGRIND */ /* Define this to print trace statements - very verbose */ -#define DEBUG_TRACE +/* Caution: Don't use this in an unfriendly environment (ie unfirewalled), + * since the printing does not sanitise strings etc */ +/*#define DEBUG_TRACE*/ /* All functions writing to the cleartext payload buffer call * CHECKCLEARTOWRITE() before writing. This is only really useful if you're diff --git a/localtcpfwd.c b/localtcpfwd.c index 9894f46..bf89fa0 100644 --- a/localtcpfwd.c +++ b/localtcpfwd.c @@ -1,14 +1,27 @@ #include "includes.h" #include "session.h" #include "dbutil.h" +#include "channel.h" #include "localtcpfwd.h" #ifndef DISABLE_LOCALTCPFWD +static int newtcpdirect(struct Channel * channel); static int newtcp(const char * host, int port); +const struct ChanType chan_tcpdirect = { + 0, /* 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 */ -int newtcpdirect(struct Channel * channel) { +static int newtcpdirect(struct Channel * channel) { unsigned char* desthost = NULL; unsigned int destport; diff --git a/localtcpfwd.h b/localtcpfwd.h index 324cb55..65efa6e 100644 --- a/localtcpfwd.h +++ b/localtcpfwd.h @@ -28,7 +28,7 @@ #include "includes.h" #include "channel.h" -int newtcpdirect(struct Channel * channel); +extern const struct ChanType chan_tcpdirect; #endif #endif diff --git a/remotetcpfwd.c b/remotetcpfwd.c index c58b820..40a3a82 100644 --- a/remotetcpfwd.c +++ b/remotetcpfwd.c @@ -90,6 +90,7 @@ static void acceptremote(struct TCPListener *listener) { return; } + /* XXX XXX XXX - type here needs fixing */ if (send_msg_channel_open_init(fd, CHANNEL_ID_TCPFORWARDED, "forwarded-tcpip") == DROPBEAR_SUCCESS) { buf_putstring(ses.writepayload, tcpinfo->addr, diff --git a/svr-chansession.c b/svr-chansession.c index 2705069..f5b4308 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -56,16 +56,6 @@ static void chansessionrequest(struct Channel *channel); static void send_exitsignalstatus(struct Channel *channel); static int sesscheckclose(struct Channel *channel); -const struct ChanType svrchansess = { - 0, /* sepfds */ - "session", /* name */ - newchansess, /* inithandler */ - sesscheckclose, /* checkclosehandler */ - chansessionrequest, /* reqhandler */ - closechansess, /* closehandler */ -}; - - /* required to clear environment */ extern char** environ; @@ -75,25 +65,6 @@ static int sesscheckclose(struct Channel *channel) { return chansess->exited; } -/* Set up the general chansession environment, in particular child-exit - * handling */ -void svr_chansessinitialise() { - - struct sigaction sa_chld; - - /* single child process intially */ - svr_ses.childpids = (struct ChildPid*)m_malloc(sizeof(struct ChildPid)); - svr_ses.childpids[0].pid = -1; /* unused */ - svr_ses.childpids[0].chansess = NULL; - svr_ses.childpidsize = 1; - sa_chld.sa_handler = sesssigchild_handler; - sa_chld.sa_flags = SA_NOCLDSTOP; - if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) { - dropbear_exit("signal() error"); - } - -} - /* handler for childs exiting, store the state for return to the client */ static void sesssigchild_handler(int dummy) { @@ -254,7 +225,7 @@ static void closechansess(struct Channel *channel) { chansess = (struct ChanSess*)channel->typedata; - send_exitsignalstatus(chansess); + send_exitsignalstatus(channel); TRACE(("enter closechansess")); if (chansess == NULL) { @@ -911,6 +882,35 @@ static void execchild(struct ChanSess *chansess) { dropbear_exit("child failed"); } +const struct ChanType svrchansess = { + 0, /* sepfds */ + "session", /* name */ + newchansess, /* inithandler */ + sesscheckclose, /* checkclosehandler */ + chansessionrequest, /* reqhandler */ + closechansess, /* closehandler */ +}; + + +/* Set up the general chansession environment, in particular child-exit + * handling */ +void svr_chansessinitialise() { + + struct sigaction sa_chld; + + /* single child process intially */ + svr_ses.childpids = (struct ChildPid*)m_malloc(sizeof(struct ChildPid)); + svr_ses.childpids[0].pid = -1; /* unused */ + svr_ses.childpids[0].chansess = NULL; + svr_ses.childpidsize = 1; + sa_chld.sa_handler = sesssigchild_handler; + sa_chld.sa_flags = SA_NOCLDSTOP; + if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) { + dropbear_exit("signal() error"); + } + +} + /* add a new environment variable, allocating space for the entry */ void addnewvar(const char* param, const char* var) { diff --git a/svr-session.c b/svr-session.c index c6f05cc..8e8eaea 100644 --- a/svr-session.c +++ b/svr-session.c @@ -35,6 +35,7 @@ #include "channel.h" #include "chansession.h" #include "atomicio.h" +#include "localtcpfwd.h" static void svr_remoteclosed(); @@ -42,6 +43,7 @@ struct serversession svr_ses; const struct ChanType *chantypes[] = { &svrchansess, + &chan_tcpdirect, NULL /* Null termination is mandatory. */ }; -- cgit v1.2.3 From 444dbb5364798925a3cacddba7b1bb3041e41a23 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 3 Jun 2004 16:45:53 +0000 Subject: - Reworked non-channel fd handling to listener.c - More channel cleaning up --HG-- extra : convert_revision : 385ec76d0304b93e277d1cc193383db5fd773703 --- Makefile.in | 10 +- channel.h | 3 +- chansession.h | 5 +- common-channel.c | 25 ++--- listener.c | 123 +++++++++++++++++++++ listener.h | 37 +++++++ localtcpfwd.c | 155 -------------------------- localtcpfwd.h | 34 ------ options.h | 2 +- remotetcpfwd.c | 302 --------------------------------------------------- remotetcpfwd.h | 6 - session.h | 6 +- svr-chansession.c | 6 +- svr-session.c | 2 +- tcpfwd-direct.c | 154 ++++++++++++++++++++++++++ tcpfwd-direct.h | 34 ++++++ tcpfwd-remote.c | 319 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tcpfwd-remote.h | 6 + tcpfwd.c | 124 --------------------- tcpfwd.h | 37 ------- x11fwd.c | 72 +++++++----- x11fwd.h | 2 - 22 files changed, 748 insertions(+), 716 deletions(-) create mode 100644 listener.c create mode 100644 listener.h delete mode 100644 localtcpfwd.c delete mode 100644 localtcpfwd.h delete mode 100644 remotetcpfwd.c delete mode 100644 remotetcpfwd.h create mode 100644 tcpfwd-direct.c create mode 100644 tcpfwd-direct.h create mode 100644 tcpfwd-remote.c create mode 100644 tcpfwd-remote.h delete mode 100644 tcpfwd.c delete mode 100644 tcpfwd.h (limited to 'svr-session.c') diff --git a/Makefile.in b/Makefile.in index 10d3cc1..cc3127a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -4,9 +4,9 @@ LTM=libtommath/libtommath.a COMMONOBJS=dbutil.o common-session.o common-packet.o common-algo.o buffer.o \ common-kex.o dss.o bignum.o \ signkey.o rsa.o random.o common-channel.o \ - common-chansession.o queue.o termcodes.o runopts.o \ - loginrec.o atomicio.o x11fwd.o agentfwd.o localtcpfwd.o compat.o \ - remotetcpfwd.o tcpfwd.o + common-chansession.o queue.o termcodes.o \ + loginrec.o atomicio.o x11fwd.o tcpfwd-direct.o compat.o \ + tcpfwd-remote.o listener.o SVROBJS=svr-kex.o svr-packet.o svr-algo.o svr-auth.o sshpty.o \ svr-authpasswd.o svr-authpubkey.o svr-session.o svr-service.o \ @@ -28,8 +28,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 debug.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 localtcpfwd.h compat.h \ - remotetcpfwd.h tcpfwd.h + loginrec.h atomicio.h x11fwd.h agentfwd.h tcpfwd-direct.h compat.h \ + tcpfwd-remote.h listener.h ALLOBJS=$(OBJS) $(DROPBEARKEYOBJS) $(DROPBEAROBJS) diff --git a/channel.h b/channel.h index e033164..b77a660 100644 --- a/channel.h +++ b/channel.h @@ -114,8 +114,7 @@ void recv_msg_channel_close(); void recv_msg_channel_eof(); #ifdef USE_LISTENERS -int send_msg_channel_open_init(int fd, struct ChanType *type, - const char * typestring); +int send_msg_channel_open_init(int fd, const struct ChanType *type); void recv_msg_channel_open_confirmation(); void recv_msg_channel_open_failure(); #endif diff --git a/chansession.h b/chansession.h index 85dc9c1..7879791 100644 --- a/chansession.h +++ b/chansession.h @@ -27,6 +27,7 @@ #include "loginrec.h" #include "channel.h" +#include "listener.h" struct ChanSess { @@ -47,7 +48,7 @@ struct ChanSess { unsigned char exitcore; #ifndef DISABLE_X11FWD - int x11fd; /* set to -1 to indicate forwarding not established */ + struct Listener * x11listener; int x11port; char * x11authprot; char * x11authcookie; @@ -56,7 +57,7 @@ struct ChanSess { #endif #ifndef DISABLE_AGENTFWD - int agentfd; + struct Listener * agentlistener; char * agentfile; char * agentdir; #endif diff --git a/common-channel.c b/common-channel.c index 4643fc2..63ed275 100644 --- a/common-channel.c +++ b/common-channel.c @@ -32,9 +32,9 @@ #include "dbutil.h" #include "channel.h" #include "ssh.h" -#include "localtcpfwd.h" -#include "remotetcpfwd.h" -#include "tcpfwd.h" +#include "tcpfwd-direct.h" +#include "tcpfwd-remote.h" +#include "listener.h" static void send_msg_channel_open_failure(unsigned int remotechan, int reason, const unsigned char *text, const unsigned char *lang); @@ -70,8 +70,8 @@ void chaninitialise(const struct ChanType *chantypes[]) { ses.chantypes = chantypes; -#ifdef USING_TCP_LISTENERS - tcp_fwd_initialise(); +#ifdef USING_LISTENERS + listeners_initialise(); #endif } @@ -219,9 +219,9 @@ void channelio(fd_set *readfd, fd_set *writefd) { } /* foreach channel */ - /* Not channel specific */ -#ifdef USING_TCP_LISTENERS - handle_tcp_fwd(readfd); + /* Listeners such as TCP, X11, agent-auth */ +#ifdef USING_LISTENERS + handle_listeners(readfd); #endif } @@ -429,8 +429,8 @@ void setchannelfds(fd_set *readfd, fd_set *writefd) { } /* foreach channel */ -#ifdef USING_TCP_LISTENERS - set_tcp_fwd_fds(readfd); +#ifdef USING_LISTENERS + set_listener_fds(readfd); #endif } @@ -895,8 +895,7 @@ static void send_msg_channel_open_confirmation(struct Channel* channel, * options, with the calling function calling encrypt_packet() after * completion. It is mandatory for the caller to encrypt_packet() if * DROPBEAR_SUCCESS is returned */ -int send_msg_channel_open_init(int fd, struct ChanType *type, - const char * typestring) { +int send_msg_channel_open_init(int fd, const struct ChanType *type) { struct Channel* chan; @@ -920,7 +919,7 @@ int send_msg_channel_open_init(int fd, struct ChanType *type, CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_OPEN); - buf_putstring(ses.writepayload, typestring, strlen(typestring)); + buf_putstring(ses.writepayload, type->name, strlen(type->name)); buf_putint(ses.writepayload, chan->index); buf_putint(ses.writepayload, RECV_MAXWINDOW); buf_putint(ses.writepayload, RECV_MAXPACKET); diff --git a/listener.c b/listener.c new file mode 100644 index 0000000..df66629 --- /dev/null +++ b/listener.c @@ -0,0 +1,123 @@ +#include "includes.h" +#include "listener.h" +#include "session.h" +#include "dbutil.h" + +void listener_initialise() { + + /* just one slot to start with */ + ses.listeners = (struct Listener**)m_malloc(sizeof(struct Listener*)); + ses.listensize = 1; + ses.listeners[0] = NULL; + +} + +void set_listener_fds(fd_set * readfds) { + + unsigned int i; + struct Listener *listener; + + /* check each in turn */ + for (i = 0; i < ses.listensize; i++) { + listener = ses.listeners[i]; + if (listener != NULL) { + FD_SET(listener->sock, readfds); + } + } +} + + +void handle_listeners(fd_set * readfds) { + + unsigned int i; + struct Listener *listener; + + /* check each in turn */ + for (i = 0; i < ses.listensize; i++) { + listener = ses.listeners[i]; + if (listener != NULL) { + if (FD_ISSET(listener->sock, readfds)) { + listener->accepter(listener); + } + } + } +} + + +/* 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*), + void (*cleanup)(struct Listener*)) { + + unsigned int i, j; + struct Listener *newlisten = NULL; + /* try get a new structure to hold it */ + for (i = 0; i < ses.listensize; i++) { + if (ses.listeners[i] == NULL) { + break; + } + } + + /* or create a new one */ + if (i == ses.listensize) { + if (ses.listensize > MAX_LISTENERS) { + TRACE(("leave newlistener: too many already")); + close(sock); + return NULL; + } + + ses.listeners = (struct Listener**)m_realloc(ses.listeners, + (ses.listensize+LISTENER_EXTEND_SIZE) + *sizeof(struct Listener*)); + + ses.listensize += LISTENER_EXTEND_SIZE; + + for (j = i; j < ses.listensize; j++) { + ses.listeners[j] = NULL; + } + } + + ses.maxfd = MAX(ses.maxfd, sock); + + newlisten = (struct Listener*)m_malloc(sizeof(struct Listener)); + newlisten->index = i; + newlisten->type = type; + newlisten->typedata = typedata; + newlisten->sock = sock; + newlisten->accepter = accepter; + newlisten->cleanup = cleanup; + + ses.listeners[i] = newlisten; + return newlisten; +} + +/* Return the first listener which matches the type-specific comparison + * function. Particularly needed for global requests, like tcp */ +struct Listener * get_listener(int type, void* typedata, + int (*match)(void*, void*)) { + + unsigned int i; + struct Listener* listener; + + for (i = 0, listener = ses.listeners[i]; i < ses.listensize; i++) { + if (listener->type == type + && match(typedata, listener->typedata)) { + return listener; + } + } + + return NULL; +} + +void remove_listener(struct Listener* listener) { + + if (listener->cleanup) { + listener->cleanup(listener); + } + + close(listener->sock); + ses.listeners[listener->index] = NULL; + m_free(listener); + +} diff --git a/listener.h b/listener.h new file mode 100644 index 0000000..ab77351 --- /dev/null +++ b/listener.h @@ -0,0 +1,37 @@ +#ifndef _LISTENER_H +#define _LISTENER_H + +#define MAX_LISTENERS 20 +#define LISTENER_EXTEND_SIZE 1 + +struct Listener { + + int sock; + + int index; /* index in the array of listeners */ + + void (*accepter)(struct Listener*); + void (*cleanup)(struct Listener*); + + int type; /* CHANNEL_ID_X11, CHANNEL_ID_AGENT, + CHANNEL_ID_TCPDIRECT (for clients), + CHANNEL_ID_TCPFORWARDED (for servers) */ + + void *typedata; + +}; + +void listener_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*), + void (*cleanup)(struct Listener*)); + +struct Listener * get_listener(int type, void* typedata, + int (*match)(void*, void*)); + +void remove_listener(struct Listener* listener); + +#endif /* _LISTENER_H */ diff --git a/localtcpfwd.c b/localtcpfwd.c deleted file mode 100644 index bf89fa0..0000000 --- a/localtcpfwd.c +++ /dev/null @@ -1,155 +0,0 @@ -#include "includes.h" -#include "session.h" -#include "dbutil.h" -#include "channel.h" -#include "localtcpfwd.h" - -#ifndef DISABLE_LOCALTCPFWD -static int newtcpdirect(struct Channel * channel); -static int newtcp(const char * host, int port); - -const struct ChanType chan_tcpdirect = { - 0, /* 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; - int sock; - int len; - int ret = DROPBEAR_FAILURE; - - if (ses.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; - } - - sock = newtcp(desthost, destport); - 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 */ -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 /* DISABLE_LOCALTCPFWD */ diff --git a/localtcpfwd.h b/localtcpfwd.h deleted file mode 100644 index 65efa6e..0000000 --- a/localtcpfwd.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Dropbear - a SSH2 server - * - * Copyright (c) 2002,2003 Matt Johnston - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ -#ifndef _LOCALTCPFWD_H_ -#define _LOCALTCPFWD_H_ -#ifndef DISABLE_LOCALTCPFWD - -#include "includes.h" -#include "channel.h" - -extern const struct ChanType chan_tcpdirect; - -#endif -#endif diff --git a/options.h b/options.h index d1f1794..703fec0 100644 --- a/options.h +++ b/options.h @@ -296,7 +296,7 @@ #endif #ifndef ENABLE_LOCALTCPFWD -#define DISABLE_LOCALTCPFWD +#define DISABLE_TCPDIRECT #endif #ifndef ENABLE_REMOTETCPFWD diff --git a/remotetcpfwd.c b/remotetcpfwd.c deleted file mode 100644 index 40a3a82..0000000 --- a/remotetcpfwd.c +++ /dev/null @@ -1,302 +0,0 @@ -#include "includes.h" -#include "ssh.h" -#include "remotetcpfwd.h" -#include "dbutil.h" -#include "session.h" -#include "buffer.h" -#include "packet.h" -#include "tcpfwd.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 newlistener(unsigned char* bindaddr, unsigned int port); -static void acceptremote(struct TCPListener *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 (ses.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 void acceptremote(struct TCPListener *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; - } - - /* XXX XXX XXX - type here needs fixing */ - if (send_msg_channel_open_init(fd, CHANNEL_ID_TCPFORWARDED, - "forwarded-tcpip") == 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(); - } -} - -static void cleanupremote(struct TCPListener *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 TCPListener * 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; - } - - /* XXX matt - server change - if (ses.authstate.pw->pw_uid != 0 - && port < IPPORT_RESERVED) { - TRACE(("can't assign port < 1024 for non-root")); - goto out; - } - */ - - ret = newlistener(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 newlistener(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; - int ret = DROPBEAR_FAILURE; - - TRACE(("enter newlistener")); - - /* 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 newlistener: 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; - - ret = new_fwd(sock, CHANNEL_ID_TCPFORWARDED, tcpinfo, - acceptremote, cleanupremote); - - if (ret == DROPBEAR_FAILURE) { - m_free(tcpinfo); - } - -done: - if (res) { - freeaddrinfo(res); - } - - TRACE(("leave newlistener")); - return ret; -} - -#endif /* DISABLE_REMOTETCPFWD */ diff --git a/remotetcpfwd.h b/remotetcpfwd.h deleted file mode 100644 index 64dbed3..0000000 --- a/remotetcpfwd.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _REMOTETCPFWD_H -#define _REMOTETCPFWD_H - -void recv_msg_global_request_remotetcp(); - -#endif /* _REMOTETCPFWD_H */ diff --git a/session.h b/session.h index e372232..0cb2eaa 100644 --- a/session.h +++ b/session.h @@ -33,7 +33,7 @@ #include "channel.h" #include "queue.h" #include "runopts.h" -#include "remotetcpfwd.h" +#include "listener.h" extern int sessinitdone; /* Is set to 0 somewhere */ extern int exitflag; @@ -139,8 +139,8 @@ struct sshsession { /* TCP forwarding - where manage listeners */ #ifndef DISABLE_REMOTETCPFWD - struct TCPListener ** tcplisteners; - unsigned int tcplistensize; + struct Listener ** listeners; + unsigned int listensize; #endif }; diff --git a/svr-chansession.c b/svr-chansession.c index f5b4308..4dd4228 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -201,13 +201,13 @@ static int newchansess(struct Channel *channel) { channel->typedata = chansess; #ifndef DISABLE_X11FWD - chansess->x11fd = -1; + chansess->x11listener = NULL; chansess->x11authprot = NULL; chansess->x11authcookie = NULL; #endif #ifndef DISABLE_AGENTFWD - chansess->agentfd = -1; + chansess->agentlistener = NULL; chansess->agentfile = NULL; chansess->agentdir = NULL; #endif @@ -881,7 +881,7 @@ static void execchild(struct ChanSess *chansess) { /* only reached on error */ dropbear_exit("child failed"); } - + const struct ChanType svrchansess = { 0, /* sepfds */ "session", /* name */ diff --git a/svr-session.c b/svr-session.c index 8e8eaea..2a97f94 100644 --- a/svr-session.c +++ b/svr-session.c @@ -35,7 +35,7 @@ #include "channel.h" #include "chansession.h" #include "atomicio.h" -#include "localtcpfwd.h" +#include "tcpfwd-direct.h" static void svr_remoteclosed(); diff --git a/tcpfwd-direct.c b/tcpfwd-direct.c new file mode 100644 index 0000000..81cd415 --- /dev/null +++ b/tcpfwd-direct.c @@ -0,0 +1,154 @@ +#include "includes.h" +#include "session.h" +#include "dbutil.h" +#include "channel.h" +#include "tcpfwd-direct.h" + +#ifndef DISABLE_TCPFWD_DIRECT +static int newtcpdirect(struct Channel * channel); +static int newtcp(const char * host, int port); + +const struct ChanType chan_tcpdirect = { + 0, /* 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; + int sock; + int len; + int ret = DROPBEAR_FAILURE; + + if (ses.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; + } + + sock = newtcp(desthost, destport); + 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 */ +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 /* DISABLE_TCPFWD_DIRECT */ diff --git a/tcpfwd-direct.h b/tcpfwd-direct.h new file mode 100644 index 0000000..20cd278 --- /dev/null +++ b/tcpfwd-direct.h @@ -0,0 +1,34 @@ +/* + * Dropbear - a SSH2 server + * + * Copyright (c) 2002,2003 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ +#ifndef _TCPFWD_DIRECT_H_ +#define _TCPFWD_DIRECT_H_ +#ifndef DISABLE_TCFWD_DIRECT + +#include "includes.h" +#include "channel.h" + +extern const struct ChanType chan_tcpdirect; + +#endif +#endif diff --git a/tcpfwd-remote.c b/tcpfwd-remote.c new file mode 100644 index 0000000..9996dbd --- /dev/null +++ b/tcpfwd-remote.c @@ -0,0 +1,319 @@ +#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" + +#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 (ses.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 = { + 0, /* 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; + } + + /* XXX matt - server change + if (ses.authstate.pw->pw_uid != 0 + && 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 new file mode 100644 index 0000000..64dbed3 --- /dev/null +++ b/tcpfwd-remote.h @@ -0,0 +1,6 @@ +#ifndef _REMOTETCPFWD_H +#define _REMOTETCPFWD_H + +void recv_msg_global_request_remotetcp(); + +#endif /* _REMOTETCPFWD_H */ diff --git a/tcpfwd.c b/tcpfwd.c deleted file mode 100644 index d95970a..0000000 --- a/tcpfwd.c +++ /dev/null @@ -1,124 +0,0 @@ -#include "includes.h" -#include "tcpfwd.h" -#include "session.h" -#include "dbutil.h" - -void tcp_fwd_initialise() { - - /* just one slot to start with */ - ses.tcplisteners = - (struct TCPListener**)m_malloc(sizeof(struct TCPListener*)); - ses.tcplistensize = 1; - ses.tcplisteners[0] = NULL; - -} - -void set_tcp_fwd_fds(fd_set * readfds) { - - unsigned int i; - struct TCPListener *listener; - - /* check each in turn */ - for (i = 0; i < ses.tcplistensize; i++) { - listener = ses.tcplisteners[i]; - if (listener != NULL) { - FD_SET(listener->sock, readfds); - } - } -} - - -void handle_tcp_fwd(fd_set * readfds) { - - unsigned int i; - struct TCPListener *listener; - - /* check each in turn */ - for (i = 0; i < ses.tcplistensize; i++) { - listener = ses.tcplisteners[i]; - if (listener != NULL) { - if (FD_ISSET(listener->sock, readfds)) { - listener->accepter(listener); - } - } - } -} - - -/* accepter(int fd, void* typedata) is a function to accept connections, - * cleanup(void* typedata) happens when cleaning up */ -int new_fwd(int sock, int type, void* typedata, - void (*accepter)(struct TCPListener*), - void (*cleanup)(struct TCPListener*)) { - - unsigned int i, j; - struct TCPListener *newtcp = NULL; - /* try get a new structure to hold it */ - for (i = 0; i < ses.tcplistensize; i++) { - if (ses.tcplisteners[i] == NULL) { - break; - } - } - - /* or create a new one */ - if (i == ses.tcplistensize) { - if (ses.tcplistensize > MAX_TCPLISTENERS) { - TRACE(("leave newlistener: too many already")); - close(sock); - return DROPBEAR_FAILURE; - } - - ses.tcplisteners = (struct TCPListener**)m_realloc(ses.tcplisteners, - (ses.tcplistensize+TCP_EXTEND_SIZE) - *sizeof(struct TCPListener*)); - - ses.tcplistensize += TCP_EXTEND_SIZE; - - for (j = i; j < ses.tcplistensize; j++) { - ses.tcplisteners[j] = NULL; - } - } - - ses.maxfd = MAX(ses.maxfd, sock); - - newtcp = (struct TCPListener*)m_malloc(sizeof(struct TCPListener)); - newtcp->index = i; - newtcp->type = type; - newtcp->typedata = typedata; - newtcp->sock = sock; - newtcp->accepter = accepter; - newtcp->cleanup = cleanup; - - ses.tcplisteners[i] = newtcp; - return DROPBEAR_SUCCESS; -} - -/* Return the first listener which matches the type-specific comparison - * function */ -struct TCPListener * get_listener(int type, void* typedata, - int (*match)(void*, void*)) { - - unsigned int i; - struct TCPListener* listener; - - for (i = 0, listener = ses.tcplisteners[i]; i < ses.tcplistensize; i++) { - if (listener->type == type - && match(typedata, listener->typedata)) { - return listener; - } - } - - return NULL; -} - -void remove_listener(struct TCPListener* listener) { - - if (listener->cleanup) { - listener->cleanup(listener); - } - - close(listener->sock); - ses.tcplisteners[listener->index] = NULL; - m_free(listener); - -} diff --git a/tcpfwd.h b/tcpfwd.h deleted file mode 100644 index 85d7373..0000000 --- a/tcpfwd.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef _TCPFWD_H -#define _TCPFWD_H - -#define MAX_TCPLISTENERS 20 -#define TCP_EXTEND_SIZE 1 - -struct TCPListener { - - int sock; - - int index; /* index in the array of listeners */ - - void (*accepter)(struct TCPListener*); - void (*cleanup)(struct TCPListener*); - - int type; /* CHANNEL_ID_X11, CHANNEL_ID_AGENT, - CHANNEL_ID_TCPDIRECT (for clients), - CHANNEL_ID_TCPFORWARDED (for servers) */ - - void *typedata; - -}; - -void tcp_fwd_initialise(); -void handle_tcp_fwd(fd_set * readfds); -void set_tcp_fwd_fds(fd_set * readfds); - -int new_fwd(int sock, int type, void* typedata, - void (*accepter)(struct TCPListener*), - void (*cleanup)(struct TCPListener*)); - -struct TCPListener * get_listener(int type, void* typedata, - int (*match)(void*, void*)); - -void remove_listener(struct TCPListener* listener); - -#endif /* _TCPFWD_H */ diff --git a/x11fwd.c b/x11fwd.c index 229b0df..e1b6961 100644 --- a/x11fwd.c +++ b/x11fwd.c @@ -37,6 +37,8 @@ #define X11BASEPORT 6000 #define X11BINDBASE 6010 +static void x11accept(struct Listener* listener); +static void x11cleanup(struct Listener *listener); static int bindport(int fd); static int send_msg_channel_open_x11(int fd, struct sockaddr_in* addr); @@ -44,8 +46,10 @@ static int send_msg_channel_open_x11(int fd, struct sockaddr_in* addr); /* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ int x11req(struct ChanSess * chansess) { + int fd; + /* we already have an x11 connection */ - if (chansess->x11fd != -1) { + if (chansess->x11listener != NULL) { return DROPBEAR_FAILURE; } @@ -55,62 +59,71 @@ int x11req(struct ChanSess * chansess) { chansess->x11screennum = buf_getint(ses.payload); /* create listening socket */ - chansess->x11fd = socket(PF_INET, SOCK_STREAM, 0); - if (chansess->x11fd < 0) { + fd = socket(PF_INET, SOCK_STREAM, 0); + if (fd < 0) { goto fail; } /* allocate port and bind */ - chansess->x11port = bindport(chansess->x11fd); + chansess->x11port = bindport(fd); if (chansess->x11port < 0) { goto fail; } /* listen */ - if (listen(chansess->x11fd, 20) < 0) { + if (listen(fd, 20) < 0) { goto fail; } /* set non-blocking */ - if (fcntl(chansess->x11fd, F_SETFL, O_NONBLOCK) < 0) { + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { goto fail; } - /* channel.c's channel fd code will handle the socket now */ - - /* set the maxfd so that select() loop will notice it */ - ses.maxfd = MAX(ses.maxfd, chansess->x11fd); + /* 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); + if (chansess->x11listener == NULL) { + goto fail; + } return DROPBEAR_SUCCESS; fail: /* cleanup */ - x11cleanup(chansess); + m_free(chansess->x11authprot); + m_free(chansess->x11authcookie); + close(fd); return DROPBEAR_FAILURE; } /* accepts a new X11 socket */ /* returns DROPBEAR_FAILURE or DROPBEAR_SUCCESS */ -int x11accept(struct ChanSess * chansess) { +static void x11accept(struct Listener* listener) { int fd; struct sockaddr_in addr; int len; + int ret; len = sizeof(addr); - fd = accept(chansess->x11fd, (struct sockaddr*)&addr, &len); + fd = accept(listener->sock, (struct sockaddr*)&addr, &len); if (fd < 0) { - return DROPBEAR_FAILURE; + return; } /* if single-connection we close it up */ - if (chansess->x11singleconn) { - x11cleanup(chansess); + if (((struct ChanSess *)(listener->typedata))->x11singleconn) { + x11cleanup(listener); } - return send_msg_channel_open_x11(fd, &addr); + ret = send_msg_channel_open_x11(fd, &addr); + if (ret == DROPBEAR_FAILURE) { + close(fd); + } } /* This is called after switching to the user, and sets up the xauth @@ -121,7 +134,7 @@ void x11setauth(struct ChanSess *chansess) { FILE * authprog; int val; - if (chansess->x11fd == -1) { + if (chansess->x11listener == NULL) { return; } @@ -154,24 +167,31 @@ void x11setauth(struct ChanSess *chansess) { } } -void x11cleanup(struct ChanSess * chansess) { +static void x11cleanup(struct Listener *listener) { - if (chansess->x11fd == -1) { - return; - } + struct ChanSess *chansess = (struct ChanSess*)listener->typedata; m_free(chansess->x11authprot); m_free(chansess->x11authcookie); - close(chansess->x11fd); - chansess->x11fd = -1; + remove_listener(listener); + chansess->x11listener = NULL; } +static const struct ChanType chan_x11 = { + 0, /* sepfds */ + "x11", + NULL, /* inithandler */ + NULL, /* checkclose */ + NULL, /* reqhandler */ + NULL /* closehandler */ +}; + + static int send_msg_channel_open_x11(int fd, struct sockaddr_in* addr) { char* ipstring; - if (send_msg_channel_open_init(fd, CHANNEL_ID_X11, "x11") - == DROPBEAR_SUCCESS) { + if (send_msg_channel_open_init(fd, &chan_x11) == DROPBEAR_SUCCESS) { ipstring = inet_ntoa(addr->sin_addr); buf_putstring(ses.writepayload, ipstring, strlen(ipstring)); buf_putint(ses.writepayload, addr->sin_port); diff --git a/x11fwd.h b/x11fwd.h index 889033f..6cf4b96 100644 --- a/x11fwd.h +++ b/x11fwd.h @@ -30,8 +30,6 @@ #include "channel.h" int x11req(struct ChanSess * chansess); -int x11accept(struct ChanSess * chansess); -void x11cleanup(struct ChanSess * chansess); void x11setauth(struct ChanSess *chansess); #endif /* DROPBEAR_X11FWD */ -- cgit v1.2.3 From 8977fbbd97d30ed5a064e2f514abfeb22b7f05dd Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Mon, 7 Jun 2004 11:36:07 +0000 Subject: Don't bother printing errno in exit messages (the code wasn't valid anyway) --HG-- extra : convert_revision : 84b4b2b17c096faebd10975a08e91954e2014d82 --- svr-session.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'svr-session.c') diff --git a/svr-session.c b/svr-session.c index 2a97f94..4310e2b 100644 --- a/svr-session.c +++ b/svr-session.c @@ -183,12 +183,6 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) { "exit before auth: %s", format); } - if (errno != 0) { - /* XXX - is this valid? */ - snprintf(fmtbuf, sizeof(fmtbuf), "%s [%d %s]", fmtbuf, - errno, strerror(errno)); - } - _dropbear_log(LOG_INFO, fmtbuf, param); /* must be after we've done with username etc */ -- cgit v1.2.3 From 18bfb4dd4812ab1e30bb4b2ecb6674ea2d368322 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 22 Jun 2004 10:47:16 +0000 Subject: - Port restriction code back in - Remove bad strerror() logging --HG-- extra : convert_revision : 8ad0c90d041d667876641822a5d870e2e73059c6 --- common-session.c | 2 ++ session.h | 5 ++++- svr-auth.c | 4 ++++ svr-session.c | 6 ------ tcpfwd-remote.c | 5 +---- 5 files changed, 11 insertions(+), 11 deletions(-) (limited to 'svr-session.c') diff --git a/common-session.c b/common-session.c index fce301a..71e9e68 100644 --- a/common-session.c +++ b/common-session.c @@ -108,6 +108,8 @@ void common_session_init(int sock, runopts *opts) { ses.chantypes = NULL; + ses.allowprivport = 0; + TRACE(("leave session_init")); } diff --git a/session.h b/session.h index 0cb2eaa..64de282 100644 --- a/session.h +++ b/session.h @@ -138,10 +138,13 @@ struct sshsession { /* TCP forwarding - where manage listeners */ -#ifndef DISABLE_REMOTETCPFWD +#ifdef USING_LISTENERS struct Listener ** listeners; unsigned int listensize; + /* Whether to allow binding to privileged ports (<1024). This doesn't + * really belong here, but nowhere else fits nicely */ #endif + int allowprivport; }; diff --git a/svr-auth.c b/svr-auth.c index f6adb05..02c16e2 100644 --- a/svr-auth.c +++ b/svr-auth.c @@ -341,6 +341,10 @@ void send_msg_userauth_success() { svr_ses.authstate.authdone = 1; + if (svr_ses.authstate.pw->pw_uid == 0) { + ses.allowprivport = 1; + } + /* Remove from the list of pre-auth sockets. Should be m_close(), since if * we fail, we might end up leaking connection slots, and disallow new * logins - a nasty situation. */ diff --git a/svr-session.c b/svr-session.c index 2a97f94..4310e2b 100644 --- a/svr-session.c +++ b/svr-session.c @@ -183,12 +183,6 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) { "exit before auth: %s", format); } - if (errno != 0) { - /* XXX - is this valid? */ - snprintf(fmtbuf, sizeof(fmtbuf), "%s [%d %s]", fmtbuf, - errno, strerror(errno)); - } - _dropbear_log(LOG_INFO, fmtbuf, param); /* must be after we've done with username etc */ diff --git a/tcpfwd-remote.c b/tcpfwd-remote.c index 880044f..16b1105 100644 --- a/tcpfwd-remote.c +++ b/tcpfwd-remote.c @@ -208,13 +208,10 @@ static int remotetcpreq() { goto out; } - /* XXX matt - server change - if (ses.authstate.pw->pw_uid != 0 - && port < IPPORT_RESERVED) { + if (!ses.allowprivport && port < IPPORT_RESERVED) { TRACE(("can't assign port < 1024 for non-root")); goto out; } - */ ret = listen_tcpfwd(bindaddr, port); -- cgit v1.2.3 From 62aab2227c2e1aca8e9b807bc203a1d6ecb14daf Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 20 Jul 2004 12:05:00 +0000 Subject: switching to global vars --HG-- extra : convert_revision : 800073097767c2ac153ab834cbcf0121cb765118 --- Makefile.in | 2 +- TODO | 2 + common-session.c | 11 ++---- dbutil.h | 1 - main.c | 27 ++++++------- runopts.h | 33 ++++++++++++---- session.h | 8 +--- svr-auth.c | 21 +++++----- svr-chansession.c | 7 ++-- svr-kex.c | 7 ++-- svr-runopts.c | 115 +++++++++++++++++++++++------------------------------- svr-session.c | 12 +++--- tcpfwd-direct.c | 3 +- tcpfwd-remote.c | 3 +- 14 files changed, 125 insertions(+), 127 deletions(-) (limited to 'svr-session.c') diff --git a/Makefile.in b/Makefile.in index 67e5e5f..4d6d342 100644 --- a/Makefile.in +++ b/Makefile.in @@ -6,7 +6,7 @@ COMMONOBJS=dbutil.o common-session.o common-packet.o common-algo.o buffer.o \ signkey.o rsa.o random.o common-channel.o \ common-chansession.o queue.o termcodes.o \ loginrec.o atomicio.o svr-x11fwd.o tcpfwd-direct.o compat.o \ - tcpfwd-remote.o listener.o process-packet.o + tcpfwd-remote.o listener.o process-packet.o common-runopts.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 \ diff --git a/TODO b/TODO index 64cbd5e..8a567c2 100644 --- a/TODO +++ b/TODO @@ -24,4 +24,6 @@ Things which need doing: - CTR mode, SSH_MSG_IGNORE sending to improve CBC security - DH Group Exchange possibly +- Use m_burn for clearing sensitive items in LTM/LTC + - fix scp.c for IRIX diff --git a/common-session.c b/common-session.c index b7793f9..6e37e29 100644 --- a/common-session.c +++ b/common-session.c @@ -35,14 +35,14 @@ #include "channel.h" #include "atomicio.h" -struct sshsession ses; +struct sshsession ses; /* GLOBAL */ /* need to know if the session struct has been initialised, this way isn't the * cleanest, but works OK */ -int sessinitdone = 0; +int sessinitdone = 0; /* GLOBAL */ /* this is set when we get SIGINT or SIGTERM, the handler is in main.c */ -int exitflag = 0; +int exitflag = 0; /* GLOBAL */ static int ident_readln(int fd, char* buf, int count); @@ -51,7 +51,7 @@ void(*session_remoteclosed)() = NULL; /* called only at the start of a session, set up initial state */ -void common_session_init(int sock, runopts *opts) { +void common_session_init(int sock) { TRACE(("enter session_init")); @@ -61,8 +61,6 @@ void common_session_init(int sock, runopts *opts) { ses.sock = sock; ses.maxfd = sock; - ses.opts = opts; - ses.connecttimeout = 0; kexinitialise(); /* initialise the kex state */ @@ -128,7 +126,6 @@ void common_session_cleanup() { } m_free(ses.session_id); - freerunopts(ses.opts); m_burn(ses.keys, sizeof(struct key_context)); m_free(ses.keys); diff --git a/dbutil.h b/dbutil.h index 3888452..3da6b2f 100644 --- a/dbutil.h +++ b/dbutil.h @@ -32,7 +32,6 @@ #ifndef DISABLE_SYSLOG void startsyslog(); #endif -extern int usingsyslog; extern void (*_dropbear_exit)(int exitcode, const char* format, va_list param); extern void (*_dropbear_log)(int priority, const char* format, va_list param); diff --git a/main.c b/main.c index 0087895..b9ff7aa 100644 --- a/main.c +++ b/main.c @@ -29,7 +29,7 @@ #include "signkey.h" #include "runopts.h" -static int listensockets(int *sock, runopts * opts, int *maxfd); +static int listensockets(int *sock, int *maxfd); static void sigchld_handler(int dummy); static void sigsegv_handler(int); static void sigintterm_handler(int fish); @@ -53,7 +53,6 @@ int main(int argc, char ** argv) int remoteaddrlen; int listensocks[MAX_LISTEN_ADDR]; unsigned int listensockcount = 0; - runopts * opts; FILE * pidfile; int childsock; @@ -66,13 +65,13 @@ int main(int argc, char ** argv) _dropbear_log = svr_dropbear_log; /* get commandline options */ - opts = svr_getopts(argc, argv); + svr_getopts(argc, argv); /* fork */ - if (opts->forkbg) { + if (svr_opts.forkbg) { int closefds = 0; #ifndef DEBUG_TRACE - if (!usingsyslog) { + if (!svr_opts.usingsyslog) { closefds = 1; } #endif @@ -83,13 +82,13 @@ int main(int argc, char ** argv) } #ifndef DISABLE_SYSLOG - if (usingsyslog) { + if (svr_opts.usingsyslog) { startsyslog(); } #endif /* should be done after syslog is working */ - if (opts->forkbg) { + if (svr_opts.forkbg) { dropbear_log(LOG_INFO, "Running in background"); } else { dropbear_log(LOG_INFO, "Not forking"); @@ -128,7 +127,7 @@ int main(int argc, char ** argv) /* Set up the listening sockets */ /* XXX XXX ports */ - listensockcount = listensockets(listensocks, opts, &maxsock); + listensockcount = listensockets(listensocks, &maxsock); /* incoming connection select loop */ for(;;) { @@ -242,7 +241,7 @@ int main(int argc, char ** argv) dropbear_exit("Couldn't close socket"); } /* start the session */ - svr_session(childsock, opts, childpipe[1], &remoteaddr); + svr_session(childsock, childpipe[1], &remoteaddr); /* don't return */ assert(0); } @@ -288,7 +287,7 @@ static void sigintterm_handler(int fish) { } /* Set up listening sockets for all the requested ports */ -static int listensockets(int *sock, runopts * opts, int *maxfd) { +static int listensockets(int *sock, int *maxfd) { int listensock; /* listening fd */ struct sockaddr_in listen_addr; @@ -296,7 +295,7 @@ static int listensockets(int *sock, runopts * opts, int *maxfd) { unsigned int i; int val; - for (i = 0; i < opts->portcount; i++) { + for (i = 0; i < svr_opts.portcount; i++) { /* iterate through all the sockets to listen on */ listensock = socket(PF_INET, SOCK_STREAM, 0); @@ -319,13 +318,13 @@ static int listensockets(int *sock, runopts * opts, int *maxfd) { memset((void*)&listen_addr, 0x0, sizeof(listen_addr)); listen_addr.sin_family = AF_INET; - listen_addr.sin_port = htons(opts->ports[i]); + 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", opts->ports[i]); + dropbear_exit("Bind failed port %d", svr_opts.ports[i]); } /* listen */ @@ -342,5 +341,5 @@ static int listensockets(int *sock, runopts * opts, int *maxfd) { *maxfd = MAX(listensock, *maxfd); } - return opts->portcount; + return svr_opts.portcount; } diff --git a/runopts.h b/runopts.h index cdf3af9..1bf1539 100644 --- a/runopts.h +++ b/runopts.h @@ -29,12 +29,23 @@ #include "signkey.h" #include "buffer.h" -struct SvrRunOpts { +typedef struct runopts { + + int nolocaltcp; + int noremotetcp; + +} runopts; + +extern runopts opts; + +typedef struct svr_runopts { char * rsakeyfile; char * dsskeyfile; char * bannerfile; + int forkbg; + int usingsyslog; /* ports is an array of the portcount listening ports */ uint16_t *ports; @@ -56,17 +67,23 @@ struct SvrRunOpts { int noauthpass; int norootpass; - int nolocaltcp; - int noremotetcp; - sign_key *hostkey; buffer * banner; -}; +} svr_runopts; + +extern svr_runopts svr_opts; + +void svr_getopts(int argc, char ** argv); + +/* Uncompleted XXX matt */ +typedef struct cli_runopts { + + int todo; -typedef struct SvrRunOpts runopts; +} cli_runopts; -runopts * getrunopts(int argc, char ** argv); -void freerunopts(runopts* opts); +extern cli_runopts cli_opts; +void cli_getopts(int argc, char ** argv); #endif /* _RUNOPTS_H_ */ diff --git a/session.h b/session.h index 1106144..cc8340d 100644 --- a/session.h +++ b/session.h @@ -32,14 +32,13 @@ #include "auth.h" #include "channel.h" #include "queue.h" -#include "runopts.h" #include "listener.h" #include "packet.h" extern int sessinitdone; /* Is set to 0 somewhere */ extern int exitflag; -void common_session_init(int sock, runopts *opts); +void common_session_init(int sock); void common_session_cleanup(); void checktimeouts(); void session_identification(); @@ -47,8 +46,7 @@ void session_identification(); extern void(*session_remoteclosed)(); /* Server */ -void svr_session(int sock, runopts *opts, int childpipe, - struct sockaddr *remoteaddr); +void svr_session(int sock, int childpipe, struct sockaddr *remoteaddr); void svr_dropbear_exit(int exitcode, const char* format, va_list param); void svr_dropbear_log(int priority, const char* format, va_list param); @@ -82,8 +80,6 @@ struct sshsession { /* Is it a client or server? */ unsigned char isserver; - runopts * opts; /* runtime options, incl hostkey, banner etc */ - long connecttimeout; /* time to disconnect if we have a timeout (for userauth etc), or 0 for no timeout */ diff --git a/svr-auth.c b/svr-auth.c index 386c3e1..7b588c0 100644 --- a/svr-auth.c +++ b/svr-auth.c @@ -34,6 +34,7 @@ #include "auth.h" #include "authpasswd.h" #include "authpubkey.h" +#include "runopts.h" static void authclear(); static int checkusername(unsigned char *username, unsigned int userlen); @@ -61,7 +62,7 @@ static void authclear() { svr_ses.authstate.authtypes |= AUTH_TYPE_PUBKEY; #endif #ifdef DROPBEAR_PASSWORD_AUTH - if (!ses.opts->noauthpass) { + if (svr_opts.noauthpass) { svr_ses.authstate.authtypes |= AUTH_TYPE_PASSWORD; } #endif @@ -73,7 +74,7 @@ static void authclear() { static void send_msg_userauth_banner() { TRACE(("enter send_msg_userauth_banner")); - if (ses.opts->banner == NULL) { + if (svr_opts.banner == NULL) { TRACE(("leave send_msg_userauth_banner: banner is NULL")); return; } @@ -81,13 +82,13 @@ static void send_msg_userauth_banner() { CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_BANNER); - buf_putstring(ses.writepayload, buf_getptr(ses.opts->banner, - ses.opts->banner->len), ses.opts->banner->len); + buf_putstring(ses.writepayload, buf_getptr(svr_opts.banner, + svr_opts.banner->len), svr_opts.banner->len); buf_putstring(ses.writepayload, "en", 2); encrypt_packet(); - buf_free(ses.opts->banner); - ses.opts->banner = NULL; + buf_free(svr_opts.banner); + svr_opts.banner = NULL; TRACE(("leave send_msg_userauth_banner")); } @@ -107,7 +108,7 @@ void recv_msg_userauth_request() { } /* send the banner if it exists, it will only exist once */ - if (ses.opts->banner) { + if (svr_opts.banner) { send_msg_userauth_banner(); } @@ -145,8 +146,8 @@ void recv_msg_userauth_request() { } #ifdef DROPBEAR_PASSWORD_AUTH - if (!ses.opts->noauthpass && - !(ses.opts->norootpass && svr_ses.authstate.pw->pw_uid == 0) ) { + if (!svr_opts.noauthpass && + !(svr_opts.norootpass && svr_ses.authstate.pw->pw_uid == 0) ) { /* user wants to try password auth */ if (methodlen == AUTH_METHOD_PASSWORD_LEN && strncmp(methodname, AUTH_METHOD_PASSWORD, @@ -217,7 +218,7 @@ static int checkusername(unsigned char *username, unsigned int userlen) { svr_ses.authstate.printableuser = m_strdup(svr_ses.authstate.pw->pw_name); /* check for non-root if desired */ - if (ses.opts->norootlogin && svr_ses.authstate.pw->pw_uid == 0) { + if (svr_opts.norootlogin && svr_ses.authstate.pw->pw_uid == 0) { TRACE(("leave checkusername: root login disabled")); dropbear_log(LOG_WARNING, "root login rejected"); send_msg_userauth_failure(0, 1); diff --git a/svr-chansession.c b/svr-chansession.c index c6526dc..cbc7ebe 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -36,6 +36,7 @@ #include "utmp.h" #include "x11fwd.h" #include "agentfwd.h" +#include "runopts.h" /* Handles sessions (either shells or programs) requested by the client */ @@ -690,7 +691,7 @@ static int ptycommand(struct Channel *channel, struct ChanSess *chansess) { m_free(chansess->tty); #ifdef DO_MOTD - if (ses.opts->domotd) { + if (svr_opts.domotd) { /* don't show the motd if ~/.hushlogin exists */ /* 11 == strlen("/hushlogin\0") */ @@ -776,8 +777,8 @@ static void execchild(struct ChanSess *chansess) { unsigned int i; /* wipe the hostkey */ - sign_key_free(ses.opts->hostkey); - ses.opts->hostkey = NULL; + sign_key_free(svr_opts.hostkey); + svr_opts.hostkey = NULL; /* overwrite the prng state */ seedrandom(); diff --git a/svr-kex.c b/svr-kex.c index 80f7c0a..4dfa6a7 100644 --- a/svr-kex.c +++ b/svr-kex.c @@ -32,6 +32,7 @@ #include "packet.h" #include "bignum.h" #include "random.h" +#include "runopts.h" static void send_msg_kexdh_reply(mp_int *dh_e); @@ -125,7 +126,7 @@ static void send_msg_kexdh_reply(mp_int *dh_e) { /* Create the remainder of the hash buffer, to generate the exchange hash */ /* K_S, the host key */ - buf_put_pub_key(ses.kexhashbuf, ses.opts->hostkey, + buf_put_pub_key(ses.kexhashbuf, svr_opts.hostkey, ses.newkeys->algo_hostkey); /* e, exchange value sent by the client */ buf_putmpint(ses.kexhashbuf, dh_e); @@ -153,7 +154,7 @@ static void send_msg_kexdh_reply(mp_int *dh_e) { /* we can start creating the kexdh_reply packet */ CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_REPLY); - buf_put_pub_key(ses.writepayload, ses.opts->hostkey, + buf_put_pub_key(ses.writepayload, svr_opts.hostkey, ses.newkeys->algo_hostkey); /* put f */ @@ -161,7 +162,7 @@ static void send_msg_kexdh_reply(mp_int *dh_e) { mp_clear(&dh_f); /* calc the signature */ - buf_put_sign(ses.writepayload, ses.opts->hostkey, + buf_put_sign(ses.writepayload, svr_opts.hostkey, ses.newkeys->algo_hostkey, ses.hash, SHA1_HASH_SIZE); /* the SSH_MSG_KEXDH_REPLY is done */ diff --git a/svr-runopts.c b/svr-runopts.c index f7a427f..1f5b0c6 100644 --- a/svr-runopts.c +++ b/svr-runopts.c @@ -29,6 +29,8 @@ #include "dbutil.h" #include "algo.h" +svr_runopts svr_opts; /* GLOBAL */ + static sign_key * loadhostkeys(const char * dsskeyfile, const char * rsakeyfile); static int readhostkey(const char * filename, sign_key * hostkey, int type); @@ -84,38 +86,34 @@ static void printhelp(const char * progname) { DROPBEAR_MAX_PORTS, DROPBEAR_PORT); } -/* returns NULL on failure, or a pointer to a freshly allocated - * runopts structure */ -runopts * svr_getopts(int argc, char ** argv) { +void svr_getopts(int argc, char ** argv) { unsigned int i; char ** next = 0; - runopts * opts; unsigned int portnum = 0; char *portstring[DROPBEAR_MAX_PORTS]; unsigned int longport; /* see printhelp() for options */ - opts = (runopts*)m_malloc(sizeof(runopts)); - opts->rsakeyfile = NULL; - opts->dsskeyfile = NULL; - opts->bannerfile = NULL; - opts->banner = NULL; - opts->forkbg = 1; - opts->norootlogin = 0; - opts->noauthpass = 0; - opts->norootpass = 0; - opts->nolocaltcp = 0; - opts->noremotetcp = 0; + svr_opts.rsakeyfile = NULL; + svr_opts.dsskeyfile = NULL; + svr_opts.bannerfile = NULL; + svr_opts.banner = NULL; + svr_opts.forkbg = 1; + svr_opts.norootlogin = 0; + svr_opts.noauthpass = 0; + svr_opts.norootpass = 0; + opts.nolocaltcp = 0; + opts.noremotetcp = 0; /* not yet - opts->ipv4 = 1; - opts->ipv6 = 1; + svr_opts.ipv4 = 1; + svr_opts.ipv6 = 1; */ #ifdef DO_MOTD - opts->domotd = 1; + svr_opts.domotd = 1; #endif #ifndef DISABLE_SYSLOG - usingsyslog = 1; + svr_opts.usingsyslog = 1; #endif for (i = 1; i < (unsigned int)argc; i++) { @@ -131,34 +129,34 @@ runopts * svr_getopts(int argc, char ** argv) { if (argv[i][0] == '-') { switch (argv[i][1]) { case 'b': - next = &opts->bannerfile; + next = &svr_opts.bannerfile; break; #ifdef DROPBEAR_DSS case 'd': - next = &opts->dsskeyfile; + next = &svr_opts.dsskeyfile; break; #endif #ifdef DROPBEAR_RSA case 'r': - next = &opts->rsakeyfile; + next = &svr_opts.rsakeyfile; break; #endif case 'F': - opts->forkbg = 0; + svr_opts.forkbg = 0; break; #ifndef DISABLE_SYSLOG case 'E': - usingsyslog = 0; + svr_opts.usingsyslog = 0; break; #endif #ifndef DISABLE_LOCALTCPFWD case 'j': - opts->nolocaltcp = 1; + opts.nolocaltcp = 1; break; #endif #ifndef DISABLE_REMOTETCPFWD case 'k': - opts->noremotetcp = 1; + opts.noremotetcp = 1; break; #endif case 'p': @@ -171,18 +169,18 @@ runopts * svr_getopts(int argc, char ** argv) { #ifdef DO_MOTD /* motd is displayed by default, -m turns it off */ case 'm': - opts->domotd = 0; + svr_opts.domotd = 0; break; #endif case 'w': - opts->norootlogin = 1; + svr_opts.norootlogin = 1; break; #ifdef DROPBEAR_PASSWORD_AUTH case 's': - opts->noauthpass = 1; + svr_opts.noauthpass = 1; break; case 'g': - opts->norootpass = 1; + svr_opts.norootpass = 1; break; #endif case 'h': @@ -191,10 +189,10 @@ runopts * svr_getopts(int argc, char ** argv) { break; /* case '4': - opts->ipv4 = 0; + svr_opts.ipv4 = 0; break; case '6': - opts->ipv6 = 0; + svr_opts.ipv6 = 0; break; */ default: @@ -206,19 +204,19 @@ runopts * svr_getopts(int argc, char ** argv) { } } - if (opts->dsskeyfile == NULL) { - opts->dsskeyfile = DSS_PRIV_FILENAME; + if (svr_opts.dsskeyfile == NULL) { + svr_opts.dsskeyfile = DSS_PRIV_FILENAME; } - if (opts->rsakeyfile == NULL) { - opts->rsakeyfile = RSA_PRIV_FILENAME; + if (svr_opts.rsakeyfile == NULL) { + svr_opts.rsakeyfile = RSA_PRIV_FILENAME; } - opts->hostkey = loadhostkeys(opts->dsskeyfile, opts->rsakeyfile); + svr_opts.hostkey = loadhostkeys(svr_opts.dsskeyfile, svr_opts.rsakeyfile); - if (opts->bannerfile) { + if (svr_opts.bannerfile) { struct stat buf; - if (stat(opts->bannerfile, &buf) != 0) { + if (stat(svr_opts.bannerfile, &buf) != 0) { dropbear_exit("Error opening banner file '%s'", - opts->bannerfile); + svr_opts.bannerfile); } if (buf.st_size > MAX_BANNER_SIZE) { @@ -226,16 +224,16 @@ runopts * svr_getopts(int argc, char ** argv) { MAX_BANNER_SIZE); } - opts->banner = buf_new(buf.st_size); - if (buf_readfile(opts->banner, opts->bannerfile)!=DROPBEAR_SUCCESS) { + svr_opts.banner = buf_new(buf.st_size); + if (buf_readfile(svr_opts.banner, svr_opts.bannerfile)!=DROPBEAR_SUCCESS) { dropbear_exit("Error reading banner file '%s'", - opts->bannerfile); + svr_opts.bannerfile); } - buf_setpos(opts->banner, 0); + buf_setpos(svr_opts.banner, 0); } /* not yet - if (!(opts->ipv4 || opts->ipv6)) { + if (!(svr_opts.ipv4 || svr_opts.ipv6)) { fprintf(stderr, "You can't disable ipv4 and ipv6.\n"); exit(1); } @@ -244,17 +242,17 @@ runopts * svr_getopts(int argc, char ** argv) { /* create the array of listening ports */ if (portnum == 0) { /* non specified */ - opts->portcount = 1; - opts->ports = m_malloc(sizeof(uint16_t)); - opts->ports[0] = DROPBEAR_PORT; + svr_opts.portcount = 1; + svr_opts.ports = m_malloc(sizeof(uint16_t)); + svr_opts.ports[0] = DROPBEAR_PORT; } else { - opts->portcount = portnum; - opts->ports = (uint16_t*)m_malloc(sizeof(uint16_t)*portnum); + svr_opts.portcount = portnum; + svr_opts.ports = (uint16_t*)m_malloc(sizeof(uint16_t)*portnum); for (i = 0; i < portnum; i++) { if (portstring[i]) { longport = atoi(portstring[i]); if (longport <= 65535 && longport > 0) { - opts->ports[i] = (uint16_t)longport; + svr_opts.ports[i] = (uint16_t)longport; continue; } } @@ -263,23 +261,8 @@ runopts * svr_getopts(int argc, char ** argv) { } } - return opts; } -void freerunopts(runopts* opts) { - - if (!opts) { - return; - } - - if (opts->hostkey) { - sign_key_free(opts->hostkey); - opts->hostkey = NULL; - } - - m_free(opts->ports); - m_free(opts); -} /* returns success or failure */ static int readhostkey(const char * filename, sign_key * hostkey, int type) { diff --git a/svr-session.c b/svr-session.c index d3094ea..927a1c1 100644 --- a/svr-session.c +++ b/svr-session.c @@ -39,10 +39,11 @@ #include "service.h" #include "auth.h" #include "tcpfwd-remote.h" +#include "runopts.h" static void svr_remoteclosed(); -struct serversession svr_ses; +struct serversession svr_ses; /* GLOBAL */ static const packettype svr_packettypes[] = { /* TYPE, AUTHREQUIRED, FUNCTION */ @@ -69,15 +70,14 @@ static const struct ChanType *svr_chantypes[] = { NULL /* Null termination is mandatory. */ }; -void svr_session(int sock, runopts *opts, int childpipe, - struct sockaddr* remoteaddr) { +void svr_session(int sock, int childpipe, struct sockaddr* remoteaddr) { fd_set readfd, writefd; struct timeval timeout; int val; crypto_init(); - common_session_init(sock, opts); + common_session_init(sock); ses.remoteaddr = remoteaddr; ses.remotehost = getaddrhostname(remoteaddr); @@ -227,7 +227,7 @@ void svr_dropbear_log(int priority, const char* format, va_list param) { vsnprintf(printbuf, sizeof(printbuf), format, param); #ifndef DISABLE_SYSLOG - if (usingsyslog) { + if (svr_opts.usingsyslog) { syslog(priority, "%s", printbuf); } #endif @@ -238,7 +238,7 @@ void svr_dropbear_log(int priority, const char* format, va_list param) { havetrace = 1; #endif - if (!usingsyslog || havetrace) + if (!svr_opts.usingsyslog || havetrace) { timesec = time(NULL); if (strftime(datestr, sizeof(datestr), "%b %d %H:%M:%S", diff --git a/tcpfwd-direct.c b/tcpfwd-direct.c index 1131602..b6a2178 100644 --- a/tcpfwd-direct.c +++ b/tcpfwd-direct.c @@ -3,6 +3,7 @@ #include "dbutil.h" #include "channel.h" #include "tcpfwd-direct.h" +#include "runopts.h" #ifndef DISABLE_TCPFWD_DIRECT static int newtcpdirect(struct Channel * channel); @@ -30,7 +31,7 @@ static int newtcpdirect(struct Channel * channel) { int len; int ret = DROPBEAR_FAILURE; - if (ses.opts->nolocaltcp) { + if (opts.nolocaltcp) { TRACE(("leave newtcpdirect: local tcp forwarding disabled")); goto out; } diff --git a/tcpfwd-remote.c b/tcpfwd-remote.c index 16b1105..d0a67a1 100644 --- a/tcpfwd-remote.c +++ b/tcpfwd-remote.c @@ -6,6 +6,7 @@ #include "buffer.h" #include "packet.h" #include "listener.h" +#include "runopts.h" #ifndef DISABLE_REMOTETCPFWD @@ -35,7 +36,7 @@ void recv_msg_global_request_remotetcp() { TRACE(("enter recv_msg_global_request_remotetcp")); - if (ses.opts->noremotetcp) { + if (opts.noremotetcp) { TRACE(("leave recv_msg_global_request_remotetcp: remote tcp forwarding disabled")); goto out; } -- cgit v1.2.3 From a9c38fb37f5fc8796435c2bcbcdecf35cf802ca6 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Mon, 26 Jul 2004 02:44:20 +0000 Subject: snapshot of stuff --HG-- extra : convert_revision : 2903853ba24669d01547710986ad531357602633 --- algo.h | 5 +- cli-algo.c | 9 +- cli-kex.c | 91 ++++++++++++++++++++ cli-main.c | 33 +++++++ cli-session.c | 101 ++++++++++++++++++++++ common-kex.c | 258 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- common-session.c | 150 ++++++++++++++++++++++++-------- dbutil.c | 90 +++++++++++++++++++ dbutil.h | 5 ++ debug.h | 2 +- kex.h | 16 +++- main.c | 4 +- options.h | 5 +- process-packet.c | 2 + session.h | 23 ++++- signkey.c | 3 +- svr-algo.c | 4 +- svr-kex.c | 182 +-------------------------------------- svr-session.c | 82 ++---------------- tcpfwd-direct.c | 6 +- 20 files changed, 750 insertions(+), 321 deletions(-) create mode 100644 cli-kex.c create mode 100644 cli-main.c create mode 100644 cli-session.c (limited to 'svr-session.c') diff --git a/algo.h b/algo.h index 369f73c..3e8ebb5 100644 --- a/algo.h +++ b/algo.h @@ -66,10 +66,9 @@ void crypto_init(); int have_algo(char* algo, size_t algolen, algo_type algos[]); void buf_put_algolist(buffer * buf, algo_type localalgos[]); -algo_type * common_buf_match_algo(buffer* buf, algo_type localalgos[], - int *goodguess); algo_type * svr_buf_match_algo(buffer* buf, algo_type localalgos[], int *goodguess); -algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[]); +algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[], + int *goodguess); #endif /* _ALGO_H_ */ diff --git a/cli-algo.c b/cli-algo.c index ba1ed85..5edd6a1 100644 --- a/cli-algo.c +++ b/cli-algo.c @@ -33,7 +33,8 @@ * direction MUST be the first algorithm on the client's list * that is also on the server's list. */ -algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[]) { +algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[], + int *goodguess) { unsigned char * algolist = NULL; unsigned char * remotealgos[MAX_PROPOSED_ALGO]; @@ -41,6 +42,8 @@ algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[]) { unsigned int count, i, j; algo_type * ret = NULL; + *goodguess = 0; + /* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */ algolist = buf_getstring(buf, &len); TRACE(("cli_buf_match_algo: %s", algolist)); @@ -78,6 +81,10 @@ algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[]) { if (len == strlen(remotealgos[i]) && strncmp(localalgos[j].name, remotealgos[i], len) == 0) { + if (i == 0 && j == 0) { + /* was a good guess */ + *goodguess = 1; + } ret = &localalgos[j]; goto out; } diff --git a/cli-kex.c b/cli-kex.c new file mode 100644 index 0000000..4d26332 --- /dev/null +++ b/cli-kex.c @@ -0,0 +1,91 @@ +/* + * Dropbear - a SSH2 server + * + * Copyright (c) 2002,2003 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + +#include "includes.h" +#include "session.h" +#include "dbutil.h" +#include "algo.h" +#include "buffer.h" +#include "session.h" +#include "kex.h" +#include "ssh.h" +#include "packet.h" +#include "bignum.h" +#include "random.h" +#include "runopts.h" + + + +void send_msg_kexdh_init() { + + cli_ses.dh_e = (mp_int*)m_malloc(sizeof(mp_int)); + cli_ses.dh_x = (mp_int*)m_malloc(sizeof(mp_int)); + + m_mp_init_multi(cli_ses.dh_e, cli_ses.dh_x); + gen_kexdh_vals(cli_ses.dh_e, cli_ses.dh_x); + + CHECKCLEARTOWRITE(); + buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_INIT); + buf_putmpint(ses.writepayload, cli_ses.dh_e); + encrypt_packet(); + ses.requirenext = SSH_MSG_KEXDH_REPLY; +} + +/* Handle a diffie-hellman key exchange reply. */ +void recv_msg_kexdh_reply() { + + mp_int dh_f; + sign_key *hostkey = NULL; + int type; + + type = ses.newkeys->algo_hostkey; + + hostkey = new_sign_key(); + if (buf_get_pub_key(ses.payload, hostkey, &type) != DROPBEAR_SUCCESS) { + dropbear_exit("Bad KEX packet"); + } + + m_mp_init(&dh_f); + if (buf_getmpint(ses.payload, &dh_f) != DROPBEAR_SUCCESS) { + dropbear_exit("Bad KEX packet"); + } + + kexdh_comb_key(cli_ses.dh_e, cli_ses.dh_x, &dh_f, hostkey); + mp_clear(&dh_f); + + if (buf_verify(ses.payload, hostkey, ses.hash, SHA1_HASH_SIZE) + != DROPBEAR_SUCCESS) { + dropbear_exit("Bad hostkey signature"); + } + + /* XXX TODO */ + dropbear_log(LOG_WARNING,"Not checking hostkey fingerprint for the moment"); + + sign_key_free(hostkey); + hostkey = NULL; + + send_msg_newkeys(); + ses.requirenext = SSH_MSG_NEWKEYS; + TRACE(("leave recv_msg_kexdh_init")); +} diff --git a/cli-main.c b/cli-main.c new file mode 100644 index 0000000..2460060 --- /dev/null +++ b/cli-main.c @@ -0,0 +1,33 @@ +#include + +int main(int argc, char ** argv) { + + int sock; + char* error = NULL; + char* hostandport; + int len; + + _dropbear_exit = cli_dropbear_exit; + _dropbear_log = cli_dropbear_log; + + cli_getopts(argc, argv); + + sock = connect_remote(cli_opts.remotehost, cli_opts.remoteport, + 0, &error); + + if (sock < 0) { + dropbear_exit("%s", error); + } + + /* Set up the host:port log */ + len = strlen(cli_opts.remotehost); + len += 10; /* 16 bit port and leeway*/ + hostandport = (char*)m_malloc(len); + snprintf(hostandport, len, "%s%d", + cli_opts.remotehost, cli_opts.remoteport); + + cli_session(sock, hostandport); + + /* not reached */ + return -1; +} diff --git a/cli-session.c b/cli-session.c new file mode 100644 index 0000000..d5afaf9 --- /dev/null +++ b/cli-session.c @@ -0,0 +1,101 @@ +#include "includes.h" +#include "session.h" +#include "dbutil.h" +#include "kex.h" +#include "ssh.h" +#include "packet.h" +#include "tcpfwd-direct.h" +#include "tcpfwd-remote.h" +#include "channel.h" +#include "random.h" + +static void cli_remoteclosed(); +static void cli_sessionloop(); + +struct clientsession cli_ses; /* GLOBAL */ + +static const packettype cli_packettypes[] = { + /* TYPE, AUTHREQUIRED, FUNCTION */ + {SSH_MSG_KEXINIT, recv_msg_kexinit}, + {SSH_MSG_KEXDH_REPLY, recv_msg_kexdh_reply}, // client + {SSH_MSG_NEWKEYS, recv_msg_newkeys}, + {SSH_MSG_CHANNEL_DATA, recv_msg_channel_data}, + {SSH_MSG_CHANNEL_WINDOW_ADJUST, recv_msg_channel_window_adjust}, + {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}, + {SSH_MSG_CHANNEL_CLOSE, recv_msg_channel_close}, + {SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation}, + {SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure}, + {0, 0} /* End */ +}; + +static const struct ChanType *cli_chantypes[] = { +// &clichansess, + /* &chan_tcpdirect etc, though need to only allow if we've requested + * that forwarding */ + NULL /* Null termination */ +}; +void cli_session(int sock, char* remotehost) { + + crypto_init(); + common_session_init(sock, remotehost); + + chaninitialise(cli_chantypes); + + /* For printing "remote host closed" for the user */ + session_remoteclosed = cli_remoteclosed; + + /* packet handlers */ + ses.packettypes = cli_packettypes; + + /* Ready to go */ + sessinitdone = 1; + + /* Exchange identification */ + session_identification(); + + seedrandom(); + + send_msg_kexinit(); + + /* XXX here we do stuff differently */ + + session_loop(cli_sessionloop); + + /* Not reached */ + + +} + +static void cli_sessionloop() { + + switch (cli_ses.state) { + + KEXINIT_RCVD: + /* We initiate the KEX. If DH wasn't the correct type, the KEXINIT + * negotiation would have failed. */ + send_msg_kexdh_init(); + cli_ses.state = KEXDH_INIT_SENT; + break; + + default: + break; + } + + if (cli_ses.donefirstkex && !cli_ses.authdone) { + + + +} + +/* called when the remote side closes the connection */ +static void cli_remoteclosed() { + + /* XXX TODO perhaps print a friendlier message if we get this but have + * already sent/received disconnect message(s) ??? */ + close(ses.sock); + ses.sock = -1; + dropbear_exit("%s closed the connection", ses.remotehost); +} diff --git a/common-kex.c b/common-kex.c index e1ae91a..21accb6 100644 --- a/common-kex.c +++ b/common-kex.c @@ -193,7 +193,6 @@ void kexinitialise() { ses.kexstate.sentnewkeys = 0; /* first_packet_follows */ - /* TODO - currently not handled */ ses.kexstate.firstfollows = 0; ses.kexstate.datatrans = 0; @@ -402,47 +401,54 @@ void recv_msg_kexinit() { if (IS_DROPBEAR_CLIENT) { +#ifdef DROPBEAR_CLIENT - /* read the peer's choice of algos */ - cli_read_kex(); + /* read the peer's choice of algos */ + read_kex_algos(cli_buf_match_algo); - /* V_C, the client's version string (CR and NL excluded) */ + /* V_C, the client's version string (CR and NL excluded) */ buf_putstring(ses.kexhashbuf, (unsigned char*)LOCAL_IDENT, strlen(LOCAL_IDENT)); - /* V_S, the server's version string (CR and NL excluded) */ + /* V_S, the server's version string (CR and NL excluded) */ buf_putstring(ses.kexhashbuf, ses.remoteident, strlen((char*)ses.remoteident)); - /* I_C, the payload of the client's SSH_MSG_KEXINIT */ + /* I_C, the payload of the client's SSH_MSG_KEXINIT */ buf_putstring(ses.kexhashbuf, buf_getptr(ses.transkexinit, ses.transkexinit->len), ses.transkexinit->len); - /* I_S, the payload of the server's SSH_MSG_KEXINIT */ + /* I_S, the payload of the server's SSH_MSG_KEXINIT */ buf_setpos(ses.payload, 0); buf_putstring(ses.kexhashbuf, buf_getptr(ses.payload, ses.payload->len), ses.payload->len); + cli_ses.state = KEXINIT_RCVD; +#endif } else { + /* SERVER */ +#ifdef DROPBEAR_SERVER - /* read the peer's choice of algos */ - svr_read_kex(); - /* V_C, the client's version string (CR and NL excluded) */ + /* read the peer's choice of algos */ + read_kex_algos(svr_buf_match_algo); + /* V_C, the client's version string (CR and NL excluded) */ buf_putstring(ses.kexhashbuf, ses.remoteident, strlen((char*)ses.remoteident)); - /* V_S, the server's version string (CR and NL excluded) */ + /* V_S, the server's version string (CR and NL excluded) */ buf_putstring(ses.kexhashbuf, (unsigned char*)LOCAL_IDENT, strlen(LOCAL_IDENT)); - /* I_C, the payload of the client's SSH_MSG_KEXINIT */ + /* I_C, the payload of the client's SSH_MSG_KEXINIT */ buf_setpos(ses.payload, 0); buf_putstring(ses.kexhashbuf, buf_getptr(ses.payload, ses.payload->len), ses.payload->len); - /* I_S, the payload of the server's SSH_MSG_KEXINIT */ + /* I_S, the payload of the server's SSH_MSG_KEXINIT */ buf_putstring(ses.kexhashbuf, buf_getptr(ses.transkexinit, ses.transkexinit->len), ses.transkexinit->len); + ses.requirenext = SSH_MSG_KEXDH_INIT; +#endif } buf_free(ses.transkexinit); @@ -450,9 +456,233 @@ void recv_msg_kexinit() { /* the rest of ses.kexhashbuf will be done after DH exchange */ ses.kexstate.recvkexinit = 1; - ses.requirenext = SSH_MSG_KEXDH_INIT; // ses.expecting = 0; // client matt TRACE(("leave recv_msg_kexinit")); } +/* Initialises and generate one side of the diffie-hellman key exchange values. + * See the ietf-secsh-transport draft, section 6, for details */ +void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv) { + + mp_int dh_p, dh_q, dh_g; + unsigned char randbuf[DH_P_LEN]; + int dh_q_len; + + TRACE(("enter send_msg_kexdh_reply")); + + m_mp_init_multi(&dh_g, &dh_p, &dh_q, dh_priv, dh_pub, NULL); + + /* read the prime and generator*/ + if (mp_read_unsigned_bin(&dh_p, (unsigned char*)dh_p_val, DH_P_LEN) + != MP_OKAY) { + dropbear_exit("Diffie-Hellman error"); + } + + if (mp_set_int(&dh_g, DH_G_VAL) != MP_OKAY) { + dropbear_exit("Diffie-Hellman error"); + } + + /* calculate q = (p-1)/2 */ + /* dh_priv is just a temp var here */ + if (mp_sub_d(&dh_p, 1, dh_priv) != MP_OKAY) { + dropbear_exit("Diffie-Hellman error"); + } + if (mp_div_2(dh_priv, &dh_q) != MP_OKAY) { + dropbear_exit("Diffie-Hellman error"); + } + + dh_q_len = mp_unsigned_bin_size(&dh_q); + + /* calculate our random value dh_y */ + do { + assert((unsigned int)dh_q_len <= sizeof(randbuf)); + genrandom(randbuf, dh_q_len); + if (mp_read_unsigned_bin(dh_priv, randbuf, dh_q_len) != MP_OKAY) { + dropbear_exit("Diffie-Hellman error"); + } + } while (mp_cmp(dh_priv, &dh_q) == MP_GT || mp_cmp_d(dh_priv, 0) != MP_GT); + + /* f = g^y mod p */ + if (mp_exptmod(&dh_g, dh_priv, &dh_p, dh_pub) != MP_OKAY) { + dropbear_exit("Diffie-Hellman error"); + } + mp_clear_multi(&dh_g, &dh_p, &dh_q, NULL); +} + +/* This function is fairly common between client/server, with some substitution + * of dh_e/dh_f etc. Hence these arguments: + * dh_pub_us is 'e' for the client, 'f' for the server. dh_pub_them is + * vice-versa. dh_priv is the x/y value corresponding to dh_pub_us */ +void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them, + sign_key *hostkey) { + + mp_int dh_p; + mp_int *dh_e = NULL, *dh_f = NULL; + hash_state hs; + + /* read the prime and generator*/ + mp_init(&dh_p); + if (mp_read_unsigned_bin(&dh_p, (unsigned char*)dh_p_val, DH_P_LEN) + != MP_OKAY) { + dropbear_exit("Diffie-Hellman error"); + } + + /* Check that dh_pub_them (dh_e or dh_f) is in the range [1, p-1] */ + if (mp_cmp(dh_pub_them, &dh_p) != MP_LT + || mp_cmp_d(dh_pub_them, 0) != MP_GT) { + dropbear_exit("Diffie-Hellman error"); + } + + /* K = e^y mod p = f^x mod p */ + ses.dh_K = (mp_int*)m_malloc(sizeof(mp_int)); + m_mp_init(ses.dh_K); + if (mp_exptmod(dh_pub_them, dh_priv, &dh_p, ses.dh_K) != MP_OKAY) { + dropbear_exit("Diffie-Hellman error"); + } + + /* clear no longer needed vars */ + mp_clear_multi(&dh_p, NULL); + + /* From here on, the code needs to work with the _same_ vars on each side, + * not vice-versaing for client/server */ + if (IS_DROPBEAR_CLIENT) { + dh_e = dh_pub_us; + dh_f = dh_pub_them; + } else { + dh_e = dh_pub_them; + dh_f = dh_pub_us; + } + + /* Create the remainder of the hash buffer, to generate the exchange hash */ + /* K_S, the host key */ + buf_put_pub_key(ses.kexhashbuf, hostkey, ses.newkeys->algo_hostkey); + /* e, exchange value sent by the client */ + buf_putmpint(ses.kexhashbuf, dh_e); + /* f, exchange value sent by the server */ + buf_putmpint(ses.kexhashbuf, dh_f); + /* K, the shared secret */ + buf_putmpint(ses.kexhashbuf, ses.dh_K); + + /* calculate the hash H to sign */ + sha1_init(&hs); + buf_setpos(ses.kexhashbuf, 0); + sha1_process(&hs, buf_getptr(ses.kexhashbuf, ses.kexhashbuf->len), + ses.kexhashbuf->len); + sha1_done(&hs, ses.hash); + buf_free(ses.kexhashbuf); + ses.kexhashbuf = NULL; + + /* first time around, we set the session_id to H */ + if (ses.session_id == NULL) { + /* create the session_id, this never needs freeing */ + ses.session_id = (unsigned char*)m_malloc(SHA1_HASH_SIZE); + memcpy(ses.session_id, ses.hash, SHA1_HASH_SIZE); + } +} + +/* read the other side's algo list. buf_match_algo is a callback to match + * algos for the client or server. */ +void read_kex_algos( + algo_type*(buf_match_algo)(buffer*buf, algo_type localalgos[], + int *goodguess)) { + + algo_type * algo; + char * erralgo = NULL; + + int goodguess = 0; + int allgood = 1; /* we AND this with each goodguess and see if its still + true after */ + + buf_incrpos(ses.payload, 16); /* start after the cookie */ + + ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context)); + + /* kex_algorithms */ + algo = buf_match_algo(ses.payload, sshkex, &goodguess); + allgood &= goodguess; + if (algo == NULL) { + erralgo = "kex"; + goto error; + } + ses.newkeys->algo_kex = algo->val; + + /* server_host_key_algorithms */ + algo = buf_match_algo(ses.payload, sshhostkey, &goodguess); + allgood &= goodguess; + if (algo == NULL) { + erralgo = "hostkey"; + goto error; + } + ses.newkeys->algo_hostkey = algo->val; + + /* encryption_algorithms_client_to_server */ + algo = buf_match_algo(ses.payload, sshciphers, &goodguess); + if (algo == NULL) { + erralgo = "enc c->s"; + goto error; + } + ses.newkeys->recv_algo_crypt = (struct dropbear_cipher*)algo->data; + + /* encryption_algorithms_server_to_client */ + algo = buf_match_algo(ses.payload, sshciphers, &goodguess); + if (algo == NULL) { + erralgo = "enc s->c"; + goto error; + } + ses.newkeys->trans_algo_crypt = (struct dropbear_cipher*)algo->data; + + /* mac_algorithms_client_to_server */ + algo = buf_match_algo(ses.payload, sshhashes, &goodguess); + if (algo == NULL) { + erralgo = "mac c->s"; + goto error; + } + ses.newkeys->recv_algo_mac = (struct dropbear_hash*)algo->data; + + /* mac_algorithms_server_to_client */ + algo = buf_match_algo(ses.payload, sshhashes, &goodguess); + if (algo == NULL) { + erralgo = "mac s->c"; + goto error; + } + ses.newkeys->trans_algo_mac = (struct dropbear_hash*)algo->data; + + /* compression_algorithms_client_to_server */ + algo = buf_match_algo(ses.payload, sshcompress, &goodguess); + if (algo == NULL) { + erralgo = "comp c->s"; + goto error; + } + ses.newkeys->recv_algo_comp = algo->val; + + /* compression_algorithms_server_to_client */ + algo = buf_match_algo(ses.payload, sshcompress, &goodguess); + if (algo == NULL) { + erralgo = "comp s->c"; + goto error; + } + ses.newkeys->trans_algo_comp = algo->val; + + /* languages_client_to_server */ + buf_eatstring(ses.payload); + + /* languages_server_to_client */ + buf_eatstring(ses.payload); + + /* first_kex_packet_follows */ + if (buf_getbyte(ses.payload)) { + ses.kexstate.firstfollows = 1; + /* if the guess wasn't good, we ignore the packet sent */ + if (!allgood) { + ses.ignorenext = 1; + } + } + + /* reserved for future extensions */ + buf_getint(ses.payload); + return; + +error: + dropbear_exit("no matching algo %s", erralgo); +} diff --git a/common-session.c b/common-session.c index 6e37e29..70ddbfe 100644 --- a/common-session.c +++ b/common-session.c @@ -35,6 +35,7 @@ #include "channel.h" #include "atomicio.h" + struct sshsession ses; /* GLOBAL */ /* need to know if the session struct has been initialised, this way isn't the @@ -44,19 +45,18 @@ int sessinitdone = 0; /* GLOBAL */ /* this is set when we get SIGINT or SIGTERM, the handler is in main.c */ int exitflag = 0; /* GLOBAL */ -static int ident_readln(int fd, char* buf, int count); - - void(*session_remoteclosed)() = NULL; +static void checktimeouts(); +static int ident_readln(int fd, char* buf, int count); + /* called only at the start of a session, set up initial state */ -void common_session_init(int sock) { +void common_session_init(int sock, char* remotehost) { TRACE(("enter session_init")); - ses.remoteaddr = NULL; - ses.remotehost = NULL; + ses.remotehost = remotehost; ses.sock = sock; ses.maxfd = sock; @@ -114,6 +114,86 @@ void common_session_init(int sock) { TRACE(("leave session_init")); } +void session_loop(void(*loophandler)()) { + + fd_set readfd, writefd; + struct timeval timeout; + int val; + + /* main loop, select()s for all sockets in use */ + for(;;) { + + timeout.tv_sec = SELECT_TIMEOUT; + timeout.tv_usec = 0; + FD_ZERO(&writefd); + FD_ZERO(&readfd); + assert(ses.payload == NULL); + if (ses.sock != -1) { + FD_SET(ses.sock, &readfd); + if (!isempty(&ses.writequeue)) { + FD_SET(ses.sock, &writefd); + } + } + + /* set up for channels which require reading/writing */ + if (ses.dataallowed) { + setchannelfds(&readfd, &writefd); + } + val = select(ses.maxfd+1, &readfd, &writefd, NULL, &timeout); + + if (exitflag) { + dropbear_exit("Terminated by signal"); + } + + if (val < 0) { + if (errno == EINTR) { + continue; + } else { + dropbear_exit("Error in select"); + } + } + + /* check for auth timeout, rekeying required etc */ + checktimeouts(); + + if (val == 0) { + /* timeout */ + TRACE(("select timeout")); + continue; + } + + /* process session socket's incoming/outgoing data */ + if (ses.sock != -1) { + if (FD_ISSET(ses.sock, &writefd) && !isempty(&ses.writequeue)) { + write_packet(); + } + + if (FD_ISSET(ses.sock, &readfd)) { + read_packet(); + } + + /* Process the decrypted packet. After this, the read buffer + * will be ready for a new packet */ + if (ses.payload != NULL) { + process_packet(); + } + } + + /* process pipes etc for the channels, ses.dataallowed == 0 + * during rekeying ) */ + if (ses.dataallowed) { + channelio(&readfd, &writefd); + } + + if (loophandler) { + loophandler(); + } + + } /* for(;;) */ + + /* Not reached */ +} + /* clean up a session on exit */ void common_session_cleanup() { @@ -134,35 +214,7 @@ void common_session_cleanup() { TRACE(("leave session_cleanup")); } -/* Check all timeouts which are required. Currently these are the time for - * user authentication, and the automatic rekeying. */ -void checktimeouts() { - - struct timeval tv; - long secs; - - if (gettimeofday(&tv, 0) < 0) { - dropbear_exit("Error getting time"); - } - - secs = tv.tv_sec; - - if (ses.connecttimeout != 0 && secs > ses.connecttimeout) { - dropbear_close("Timeout before auth"); - } - - /* we can't rekey if we haven't done remote ident exchange yet */ - if (ses.remoteident == NULL) { - return; - } - if (!ses.kexstate.sentkexinit - && (secs - ses.kexstate.lastkextime >= KEX_REKEY_TIMEOUT - || ses.kexstate.datarecv+ses.kexstate.datatrans >= KEX_REKEY_DATA)){ - TRACE(("rekeying after timeout or max data reached")); - send_msg_kexinit(); - } -} void session_identification() { /* max length of 255 chars */ @@ -268,3 +320,33 @@ static int ident_readln(int fd, char* buf, int count) { return pos+1; } +/* Check all timeouts which are required. Currently these are the time for + * user authentication, and the automatic rekeying. */ +static void checktimeouts() { + + struct timeval tv; + long secs; + + if (gettimeofday(&tv, 0) < 0) { + dropbear_exit("Error getting time"); + } + + secs = tv.tv_sec; + + if (ses.connecttimeout != 0 && secs > ses.connecttimeout) { + dropbear_close("Timeout before auth"); + } + + /* we can't rekey if we haven't done remote ident exchange yet */ + if (ses.remoteident == NULL) { + return; + } + + if (!ses.kexstate.sentkexinit + && (secs - ses.kexstate.lastkextime >= KEX_REKEY_TIMEOUT + || ses.kexstate.datarecv+ses.kexstate.datatrans >= KEX_REKEY_DATA)){ + TRACE(("rekeying after timeout or max data reached")); + send_msg_kexinit(); + } +} + diff --git a/dbutil.c b/dbutil.c index 2494f38..1c0648c 100644 --- a/dbutil.c +++ b/dbutil.c @@ -113,6 +113,95 @@ void dropbear_trace(const char* format, ...) { } #endif /* DEBUG_TRACE */ +/* Connect via TCP to a host. Connection will try ipv4 or ipv6, will + * return immediately if nonblocking is set */ +int connect_remote(const char* remotehost, const char* remoteport, + int nonblocking, char ** errstring) { + + struct addrinfo *res0 = NULL, *res = NULL, hints; + int sock; + int err; + + TRACE(("enter connect_remote")); + + if (errstring != NULL) { + *errstring = NULL; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = PF_UNSPEC; + + err = getaddrinfo(remotehost, remoteport, &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(("Error resolving: %s", gai_strerror(err))); + return -1; + } + + sock = -1; + err = EADDRNOTAVAIL; + for (res = res0; res; res = res->ai_next) { + + sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (sock < 0) { + err = errno; + continue; + } + + if (nonblocking) { + if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { + close(sock); + sock = -1; + if (errstring != NULL && *errstring == NULL) { + *errstring = m_strdup("Failed non-blocking"); + } + TRACE(("Failed non-blocking: %s", strerror(errno))); + continue; + } + } + + if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) { + if (errno == EINPROGRESS) { + TRACE(("Connect in progress")); + break; + } else { + err = errno; + close(sock); + sock = -1; + continue; + } + } + + break; /* Success */ + } + + if (sock < 0) { + /* Failed */ + 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(("Error connecting: %s", strerror(err))); + } else { + /* Success */ + /* (err is used as a dummy var here) */ + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&err, sizeof(err)); + } + + freeaddrinfo(res0); + + TRACE(("leave connect_remote: sock %d", 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) { @@ -304,3 +393,4 @@ void m_burn(void *data, unsigned int len) { *p++ = 0x66; } } + diff --git a/dbutil.h b/dbutil.h index 3da6b2f..49b7466 100644 --- a/dbutil.h +++ b/dbutil.h @@ -45,6 +45,8 @@ void printhex(unsigned char* buf, int len); #endif char * stripcontrol(const char * text); unsigned char * getaddrstring(struct sockaddr * addr); +int connect_remote(const char* remotehost, const char* remoteport, + int nonblocking, char ** errstring); char* getaddrhostname(struct sockaddr * addr); int buf_readfile(buffer* buf, const char* filename); @@ -56,4 +58,7 @@ void * m_realloc(void* ptr, size_t size); void __m_free(void* ptr); void m_burn(void* data, unsigned int len); +/* Used to force mp_ints to be initialised */ +#define DEF_MP_INT(X) mp_int X = {0, 0, 0, NULL} + #endif /* _DBUTIL_H_ */ diff --git a/debug.h b/debug.h index d619a58..736690a 100644 --- a/debug.h +++ b/debug.h @@ -36,7 +36,7 @@ /* Define this to print trace statements - very verbose */ /* Caution: Don't use this in an unfriendly environment (ie unfirewalled), * since the printing does not sanitise strings etc */ -#define DEBUG_TRACE +//#define DEBUG_TRACE /* All functions writing to the cleartext payload buffer call * CHECKCLEARTOWRITE() before writing. This is only really useful if you're diff --git a/kex.h b/kex.h index 61b022e..e6c8ac1 100644 --- a/kex.h +++ b/kex.h @@ -26,17 +26,25 @@ #define _KEX_H_ #include "includes.h" +#include "algo.h" void send_msg_kexinit(); void recv_msg_kexinit(); -void send_dh_kex(); -void recv_msg_kexdh_init(); void send_msg_newkeys(); void recv_msg_newkeys(); void kexinitialise(); +void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv); +void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them, + sign_key *hostkey); -void svr_read_kex(); -void cli_read_kex(); +void read_kex_algos( + algo_type*(buf_match_algo)(buffer*buf, algo_type localalgos[], + int *goodguess)); + +void recv_msg_kexdh_init(); // server + +void send_msg_kexdh_init(); // client +void recv_msg_kexdh_reply(); // client extern const unsigned char dh_p_val[]; #define DH_P_LEN 128 /* The length of the dh_p_val array */ diff --git a/main.c b/main.c index b9ff7aa..0ef1e62 100644 --- a/main.c +++ b/main.c @@ -240,8 +240,10 @@ int main(int argc, char ** argv) if (m_close(childpipe[0]) == DROPBEAR_FAILURE) { dropbear_exit("Couldn't close socket"); } + /* start the session */ - svr_session(childsock, childpipe[1], &remoteaddr); + svr_session(childsock, childpipe[1], + getaddrhostname(&remoteaddr)); /* don't return */ assert(0); } diff --git a/options.h b/options.h index c6b552a..3aeac0f 100644 --- a/options.h +++ b/options.h @@ -30,7 +30,7 @@ * parts are to allow for commandline -DDROPBEAR_XXX options etc. ******************************************************************/ #define DROPBEAR_SERVER -/* #define DROPBEAR_CLIENT */ +//#define DROPBEAR_CLIENT #ifndef DROPBEAR_PORT #define DROPBEAR_PORT 22 @@ -48,6 +48,7 @@ * perhaps 20% slower for pubkey operations (it is probably worth experimenting * if you want to use this) */ /*#define NO_FAST_EXPTMOD*/ +#define DROPBEAR_SMALL_CODE /* Enable X11 Forwarding */ #define ENABLE_X11FWD @@ -181,7 +182,7 @@ *******************************************************************/ #ifndef DROPBEAR_VERSION -#define DROPBEAR_VERSION "0.41" +#define DROPBEAR_VERSION "0.41-and-client" #endif #define LOCAL_IDENT "SSH-2.0-dropbear_" DROPBEAR_VERSION diff --git a/process-packet.c b/process-packet.c index 7b73cb7..afa45ef 100644 --- a/process-packet.c +++ b/process-packet.c @@ -104,9 +104,11 @@ void process_packet() { */ /* check that we aren't expecting a particular packet */ +#if 0 if (cli_ses.expecting && cli_ses.expecting == type) { cli_ses.expecting = 0; } +#endif } #endif diff --git a/session.h b/session.h index cc8340d..d929c1c 100644 --- a/session.h +++ b/session.h @@ -26,6 +26,7 @@ #define _SESSION_H_ #include "includes.h" +#include "options.h" #include "buffer.h" #include "signkey.h" #include "kex.h" @@ -38,7 +39,8 @@ extern int sessinitdone; /* Is set to 0 somewhere */ extern int exitflag; -void common_session_init(int sock); +void common_session_init(int sock, char* remotehost); +void session_loop(void(*loophandler)()); void common_session_cleanup(); void checktimeouts(); void session_identification(); @@ -46,10 +48,14 @@ void session_identification(); extern void(*session_remoteclosed)(); /* Server */ -void svr_session(int sock, int childpipe, struct sockaddr *remoteaddr); +void svr_session(int sock, int childpipe, char *remotehost); void svr_dropbear_exit(int exitcode, const char* format, va_list param); void svr_dropbear_log(int priority, const char* format, va_list param); +/* Client */ +void cli_session(int sock, char *remotehost); +void cli_dropbear_exit(int exitcode, const char* format, va_list param); +void cli_dropbear_log(int priority, const char* format, va_list param); struct key_context { @@ -85,8 +91,8 @@ struct sshsession { int sock; - struct sockaddr *remoteaddr; unsigned char *remotehost; /* the peer hostname */ + unsigned char *remoteident; int maxfd; /* the maximum file descriptor to check with select() */ @@ -166,11 +172,20 @@ struct serversession { }; +typedef enum { + NOTHING, + KEXINIT_RCVD, + KEXDH_INIT_SENT, + KEXDH_REPLY_RCVD, +} cli_state; struct clientsession { + mp_int *dh_e, *dh_x; /* Used during KEX */ + cli_state state; /* Used to progress the KEX/auth/channelsession etc */ int something; /* XXX */ + unsigned donefirstkex : 1; /* Set when we set sentnewkeys, never reset */ }; @@ -182,7 +197,7 @@ extern struct serversession svr_ses; #endif /* DROPBEAR_SERVER */ #ifdef DROPBEAR_CLIENT -extern struct serversession cli_ses; +extern struct clientsession cli_ses; #endif /* DROPBEAR_CLIENT */ #endif /* _SESSION_H_ */ diff --git a/signkey.c b/signkey.c index c529c8c..5fcdbbf 100644 --- a/signkey.c +++ b/signkey.c @@ -45,7 +45,8 @@ sign_key * new_sign_key() { } /* returns DROPBEAR_SUCCESS on success, DROPBEAR_FAILURE on fail. - * type is set to hold the type returned */ + * type should be set by the caller to specify the type to read, and + * on return is set to the type read (useful when type = _ANY) */ int buf_get_pub_key(buffer *buf, sign_key *key, int *type) { unsigned char* ident; diff --git a/svr-algo.c b/svr-algo.c index 33b9471..dc6565a 100644 --- a/svr-algo.c +++ b/svr-algo.c @@ -16,6 +16,8 @@ algo_type * svr_buf_match_algo(buffer* buf, algo_type localalgos[], unsigned int count, i, j; algo_type * ret = NULL; + *goodguess = 0; + /* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */ algolist = buf_getstring(buf, &len); /* Debug this */ @@ -57,8 +59,6 @@ algo_type * svr_buf_match_algo(buffer* buf, algo_type localalgos[], /* set if it was a good guess */ if (i == 0 && j == 0) { *goodguess = 1; - } else { - *goodguess = 0; } /* set the algo to return */ ret = &localalgos[j]; diff --git a/svr-kex.c b/svr-kex.c index 4dfa6a7..35b50a6 100644 --- a/svr-kex.c +++ b/svr-kex.c @@ -70,87 +70,15 @@ void recv_msg_kexdh_init() { * See the ietf-secsh-transport draft, section 6, for details */ static void send_msg_kexdh_reply(mp_int *dh_e) { - mp_int dh_p, dh_q, dh_g, dh_y, dh_f; - unsigned char randbuf[DH_P_LEN]; - int dh_q_len; - hash_state hs; + mp_int dh_y, dh_f; TRACE(("enter send_msg_kexdh_reply")); - m_mp_init_multi(&dh_g, &dh_p, &dh_q, &dh_y, &dh_f, NULL); + gen_kexdh_vals(&dh_f, &dh_y); - /* read the prime and generator*/ - if (mp_read_unsigned_bin(&dh_p, (unsigned char*)dh_p_val, DH_P_LEN) - != MP_OKAY) { - dropbear_exit("Diffie-Hellman error"); - } - - if (mp_set_int(&dh_g, DH_G_VAL) != MP_OKAY) { - dropbear_exit("Diffie-Hellman error"); - } - - /* calculate q = (p-1)/2 */ - if (mp_sub_d(&dh_p, 1, &dh_y) != MP_OKAY) { /*dh_y is just a temp var here*/ - dropbear_exit("Diffie-Hellman error"); - } - if (mp_div_2(&dh_y, &dh_q) != MP_OKAY) { - dropbear_exit("Diffie-Hellman error"); - } - - dh_q_len = mp_unsigned_bin_size(&dh_q); - - /* calculate our random value dh_y */ - do { - assert((unsigned int)dh_q_len <= sizeof(randbuf)); - genrandom(randbuf, dh_q_len); - if (mp_read_unsigned_bin(&dh_y, randbuf, dh_q_len) != MP_OKAY) { - dropbear_exit("Diffie-Hellman error"); - } - } while (mp_cmp(&dh_y, &dh_q) == MP_GT || mp_cmp_d(&dh_y, 0) != MP_GT); - - /* f = g^y mod p */ - if (mp_exptmod(&dh_g, &dh_y, &dh_p, &dh_f) != MP_OKAY) { - dropbear_exit("Diffie-Hellman error"); - } - mp_clear(&dh_g); - - /* K = e^y mod p */ - ses.dh_K = (mp_int*)m_malloc(sizeof(mp_int)); - m_mp_init(ses.dh_K); - if (mp_exptmod(dh_e, &dh_y, &dh_p, ses.dh_K) != MP_OKAY) { - dropbear_exit("Diffie-Hellman error"); - } + kexdh_comb_key(&dh_f, &dh_y, dh_e, svr_opts.hostkey); + mp_clear(&dh_y); - /* clear no longer needed vars */ - mp_clear_multi(&dh_y, &dh_p, &dh_q, NULL); - - /* Create the remainder of the hash buffer, to generate the exchange hash */ - /* K_S, the host key */ - buf_put_pub_key(ses.kexhashbuf, svr_opts.hostkey, - ses.newkeys->algo_hostkey); - /* e, exchange value sent by the client */ - buf_putmpint(ses.kexhashbuf, dh_e); - /* f, exchange value sent by the server */ - buf_putmpint(ses.kexhashbuf, &dh_f); - /* K, the shared secret */ - buf_putmpint(ses.kexhashbuf, ses.dh_K); - - /* calculate the hash H to sign */ - sha1_init(&hs); - buf_setpos(ses.kexhashbuf, 0); - sha1_process(&hs, buf_getptr(ses.kexhashbuf, ses.kexhashbuf->len), - ses.kexhashbuf->len); - sha1_done(&hs, ses.hash); - buf_free(ses.kexhashbuf); - ses.kexhashbuf = NULL; - - /* first time around, we set the session_id to H */ - if (ses.session_id == NULL) { - /* create the session_id, this never needs freeing */ - ses.session_id = (unsigned char*)m_malloc(SHA1_HASH_SIZE); - memcpy(ses.session_id, ses.hash, SHA1_HASH_SIZE); - } - /* we can start creating the kexdh_reply packet */ CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_REPLY); @@ -171,105 +99,3 @@ static void send_msg_kexdh_reply(mp_int *dh_e) { TRACE(("leave send_msg_kexdh_reply")); } -/* read the client's choice of algorithms */ -void svr_read_kex() { - - algo_type * algo; - char * erralgo = NULL; - - int goodguess = 0; - int allgood = 1; /* we AND this with each goodguess and see if its still - true after */ - - buf_incrpos(ses.payload, 16); /* start after the cookie */ - - ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context)); - - /* kex_algorithms */ - algo = svr_buf_match_algo(ses.payload, sshkex, &goodguess); - allgood &= goodguess; - if (algo == NULL) { - erralgo = "kex"; - goto error; - } - ses.newkeys->algo_kex = algo->val; - - /* server_host_key_algorithms */ - algo = svr_buf_match_algo(ses.payload, sshhostkey, &goodguess); - allgood &= goodguess; - if (algo == NULL) { - erralgo = "hostkey"; - goto error; - } - ses.newkeys->algo_hostkey = algo->val; - - /* encryption_algorithms_client_to_server */ - algo = svr_buf_match_algo(ses.payload, sshciphers, &goodguess); - if (algo == NULL) { - erralgo = "enc c->s"; - goto error; - } - ses.newkeys->recv_algo_crypt = (struct dropbear_cipher*)algo->data; - - /* encryption_algorithms_server_to_client */ - algo = svr_buf_match_algo(ses.payload, sshciphers, &goodguess); - if (algo == NULL) { - erralgo = "enc s->c"; - goto error; - } - ses.newkeys->trans_algo_crypt = (struct dropbear_cipher*)algo->data; - - /* mac_algorithms_client_to_server */ - algo = svr_buf_match_algo(ses.payload, sshhashes, &goodguess); - if (algo == NULL) { - erralgo = "mac c->s"; - goto error; - } - ses.newkeys->recv_algo_mac = (struct dropbear_hash*)algo->data; - - /* mac_algorithms_server_to_client */ - algo = svr_buf_match_algo(ses.payload, sshhashes, &goodguess); - if (algo == NULL) { - erralgo = "mac s->c"; - goto error; - } - ses.newkeys->trans_algo_mac = (struct dropbear_hash*)algo->data; - - /* compression_algorithms_client_to_server */ - algo = svr_buf_match_algo(ses.payload, sshcompress, &goodguess); - if (algo == NULL) { - erralgo = "comp c->s"; - goto error; - } - ses.newkeys->recv_algo_comp = algo->val; - - /* compression_algorithms_server_to_client */ - algo = svr_buf_match_algo(ses.payload, sshcompress, &goodguess); - if (algo == NULL) { - erralgo = "comp s->c"; - goto error; - } - ses.newkeys->trans_algo_comp = algo->val; - - /* languages_client_to_server */ - buf_eatstring(ses.payload); - - /* languages_server_to_client */ - buf_eatstring(ses.payload); - - /* first_kex_packet_follows */ - if (buf_getbyte(ses.payload)) { - ses.kexstate.firstfollows = 1; - /* if the guess wasn't good, we ignore the packet sent */ - if (!allgood) { - ses.ignorenext = 1; - } - } - - /* reserved for future extensions */ - buf_getint(ses.payload); - return; - -error: - dropbear_exit("no matching algo %s", erralgo); -} diff --git a/svr-session.c b/svr-session.c index 927a1c1..cb309fe 100644 --- a/svr-session.c +++ b/svr-session.c @@ -50,7 +50,7 @@ static const packettype svr_packettypes[] = { {SSH_MSG_SERVICE_REQUEST, recv_msg_service_request}, // server {SSH_MSG_USERAUTH_REQUEST, recv_msg_userauth_request}, //server {SSH_MSG_KEXINIT, recv_msg_kexinit}, - {SSH_MSG_KEXDH_INIT, recv_msg_kexdh_init}, + {SSH_MSG_KEXDH_INIT, recv_msg_kexdh_init}, // server {SSH_MSG_NEWKEYS, recv_msg_newkeys}, {SSH_MSG_CHANNEL_DATA, recv_msg_channel_data}, {SSH_MSG_CHANNEL_WINDOW_ADJUST, recv_msg_channel_window_adjust}, @@ -70,17 +70,12 @@ static const struct ChanType *svr_chantypes[] = { NULL /* Null termination is mandatory. */ }; -void svr_session(int sock, int childpipe, struct sockaddr* remoteaddr) { +void svr_session(int sock, int childpipe, char* remotehost) { - fd_set readfd, writefd; struct timeval timeout; - int val; crypto_init(); - common_session_init(sock); - - ses.remoteaddr = remoteaddr; - ses.remotehost = getaddrhostname(remoteaddr); + common_session_init(sock, remotehost); /* Initialise server specific parts of the session */ svr_ses.childpipe = childpipe; @@ -111,75 +106,12 @@ void svr_session(int sock, int childpipe, struct sockaddr* remoteaddr) { /* start off with key exchange */ send_msg_kexinit(); - FD_ZERO(&readfd); - FD_ZERO(&writefd); - - /* main loop, select()s for all sockets in use */ - for(;;) { - - timeout.tv_sec = SELECT_TIMEOUT; - timeout.tv_usec = 0; - FD_ZERO(&writefd); - FD_ZERO(&readfd); - assert(ses.payload == NULL); - if (ses.sock != -1) { - FD_SET(ses.sock, &readfd); - if (!isempty(&ses.writequeue)) { - FD_SET(ses.sock, &writefd); - } - } - - /* set up for channels which require reading/writing */ - if (ses.dataallowed) { - setchannelfds(&readfd, &writefd); - } - val = select(ses.maxfd+1, &readfd, &writefd, NULL, &timeout); - - if (exitflag) { - dropbear_exit("Terminated by signal"); - } - - if (val < 0) { - if (errno == EINTR) { - continue; - } else { - dropbear_exit("Error in select"); - } - } - - /* check for auth timeout, rekeying required etc */ - checktimeouts(); - - if (val == 0) { - /* timeout */ - TRACE(("select timeout")); - continue; - } - - /* process session socket's incoming/outgoing data */ - if (ses.sock != -1) { - if (FD_ISSET(ses.sock, &writefd) && !isempty(&ses.writequeue)) { - write_packet(); - } - - if (FD_ISSET(ses.sock, &readfd)) { - read_packet(); - } - - /* Process the decrypted packet. After this, the read buffer - * will be ready for a new packet */ - if (ses.payload != NULL) { - process_packet(); - } - } + /* Run the main for loop. NULL is for the dispatcher - only the client + * code makes use of it */ + session_loop(NULL); - /* process pipes etc for the channels, ses.dataallowed == 0 - * during rekeying ) */ - if (ses.dataallowed) { - channelio(&readfd, &writefd); - } + /* Not reached */ - } /* for(;;) */ } /* failure exit - format must be <= 100 chars */ diff --git a/tcpfwd-direct.c b/tcpfwd-direct.c index b6a2178..b611283 100644 --- a/tcpfwd-direct.c +++ b/tcpfwd-direct.c @@ -27,6 +27,7 @@ static int newtcpdirect(struct Channel * channel) { unsigned int destport; unsigned char* orighost = NULL; unsigned int origport; + char portstring[6]; int sock; int len; int ret = DROPBEAR_FAILURE; @@ -58,7 +59,8 @@ static int newtcpdirect(struct Channel * channel) { goto out; } - sock = newtcp(desthost, destport); + snprintf(portstring, sizeof(portstring), "%d", destport); + sock = connect_remote(desthost, portstring, 1, NULL); if (sock < 0) { TRACE(("leave newtcpdirect: sock failed")); goto out; @@ -86,6 +88,7 @@ out: * 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; @@ -152,4 +155,5 @@ static int newtcp(const char * host, int port) { setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&val, sizeof(val)); return sock; } +#endif #endif /* DISABLE_TCPFWD_DIRECT */ -- cgit v1.2.3 From a76b1ba06868c1743837a5267efcbf2e07c9d81d Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 27 Jul 2004 16:30:46 +0000 Subject: Progressing client support --HG-- extra : convert_revision : 48946be1cef774d1c33b0f78689962b18720c627 --- Makefile.in | 13 ++--- auth.h | 30 +++++++++-- cli-auth.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ cli-authpasswd.c | 36 +++++++++++++ cli-kex.c | 1 + cli-main.c | 49 +++++++++++++++++- cli-service.c | 62 +++++++++++++++++++++++ cli-session.c | 92 +++++++++++++++++++++++++++------ common-kex.c | 42 +++++++++------- common-runopts.c | 28 +++++++++++ common-session.c | 6 +-- dbmulti.c | 8 +++ debug.h | 2 +- kex.h | 9 ++-- options.h | 23 ++------- packet.c | 6 +-- runopts.h | 6 ++- scp.c | 7 ++- service.h | 3 +- session.h | 38 ++++++++++---- svr-agentfwd.c | 8 +-- svr-auth.c | 72 +++++++++++++------------- svr-authpasswd.c | 12 ++--- svr-authpubkey.c | 22 ++++---- svr-chansession.c | 32 ++++++------ svr-runopts.c | 4 +- svr-service.c | 3 +- svr-session.c | 13 ++--- 28 files changed, 599 insertions(+), 176 deletions(-) create mode 100644 cli-auth.c create mode 100644 cli-authpasswd.c create mode 100644 cli-service.c create mode 100644 common-runopts.c (limited to 'svr-session.c') diff --git a/Makefile.in b/Makefile.in index 645da6d..b8de23e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -27,7 +27,7 @@ SVROBJS=svr-kex.o svr-algo.o svr-auth.o sshpty.o \ svr-chansession.o svr-runopts.o svr-agentfwd.o svr-main.o svr-x11fwd.o CLIOBJS=cli-algo.o cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \ - cli-session.o cli-service.o + cli-session.o cli-service.o cli-runopts.o CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \ common-channel.o common-chansession.o termcodes.o loginrec.o \ @@ -140,11 +140,12 @@ dbclient: $(dbclientobjs) dropbearkey: $(dropbearkeyobjs) dropbearconvert: $(dropbearconvertobjs) -dropbear dbclient dropbearkey dropbearconvert: $(HEADERS) $(LTC) $(LTM) +dropbear dbclient dropbearkey dropbearconvert: $(HEADERS) $(LTC) $(LTM) \ + Makefile $(LD) $(LDFLAGS) -o $(SPREFIX)$@$(EXEEXT) $($@objs) $(LIBS) # scp doesn't use the libs so is special. -scp: $(SCPOBJS) $(HEADERS) +scp: $(SCPOBJS) $(HEADERS) Makefile $(LD) $(LDFLAGS) -o $(SPREFIX)$@$(EXEEXT) $(SCPOBJS) @@ -155,16 +156,16 @@ ifeq ($(MULTI),1) CFLAGS+=$(addprefix -DDBMULTI_, $(PROGRAMS)) -DDROPBEAR_MULTI endif -dropbearmulti: $(HEADERS) $(MULTIOBJS) $(LTC) $(LTM) +dropbearmulti: $(HEADERS) $(MULTIOBJS) $(LTC) $(LTM) Makefile $(LD) $(LDFLAGS) -o $(SPREFIX)$@$(EXEEXT) $(MULTIOBJS) $(LIBS) @echo @echo "You should now create symlinks to the programs you have included" @echo "ie 'ln -s dropbearmulti dropbear'" -$(LTC): $(HEADERS) +$(LTC): options.h cd libtomcrypt && $(MAKE) clean && $(MAKE) -$(LTM): $(HEADERS) +$(LTM): options.h cd libtommath && $(MAKE) ltc-clean: diff --git a/auth.h b/auth.h index 8d2db3e..df8ae0c 100644 --- a/auth.h +++ b/auth.h @@ -27,12 +27,28 @@ #include "includes.h" -void authinitialise(); +void svr_authinitialise(); +void cli_authinitialise(); +void svr_auth_password(); +void svr_auth_pubkey(); + +int cli_auth_password(); +int cli_auth_pubkey(); + +/* Server functions */ void recv_msg_userauth_request(); void send_msg_userauth_failure(int partial, int incrfail); void send_msg_userauth_success(); +/* Client functions */ +void recv_msg_userauth_failure(); +void recv_msg_userauth_success(); +void cli_get_user(); +void cli_auth_getmethods(); +void cli_auth_try(); + + #define MAX_USERNAME_LEN 25 /* arbitrary for the moment */ #define AUTH_TYPE_PUBKEY 1 << 0 @@ -46,17 +62,23 @@ void send_msg_userauth_success(); #define AUTH_METHOD_PASSWORD "password" #define AUTH_METHOD_PASSWORD_LEN 8 +/* This structure is shared between server and client - it contains + * relatively little extraneous bits when used for the client rather than the + * server */ struct AuthState { char *username; /* This is the username the client presents to check. It is updated each run through, used for auth checking */ - char *printableuser; /* stripped of control chars, used for logs etc */ - struct passwd * pw; unsigned char authtypes; /* Flags indicating which auth types are still valid */ unsigned int failcount; /* Number of (failed) authentication attempts.*/ - unsigned authdone : 1; /* 0 if we haven't authed, 1 if we have */ + unsigned authdone : 1; /* 0 if we haven't authed, 1 if we have. Applies for + client and server (though has differing [obvious] + meanings). */ + /* These are only used for the server */ + char *printableuser; /* stripped of control chars, used for logs etc */ + struct passwd * pw; }; diff --git a/cli-auth.c b/cli-auth.c new file mode 100644 index 0000000..952546e --- /dev/null +++ b/cli-auth.c @@ -0,0 +1,148 @@ +#include "includes.h" +#include "session.h" +#include "auth.h" +#include "dbutil.h" +#include "buffer.h" +#include "ssh.h" +#include "packet.h" +#include "runopts.h" + +void cli_authinitialise() { + + memset(&ses.authstate, 0, sizeof(ses.authstate)); +} + + +void cli_get_user() { + + uid_t uid; + struct passwd *pw; + + TRACE(("enter cli_get_user")); + if (cli_opts.username != NULL) { + ses.authstate.username = cli_opts.username; + } else { + uid = getuid(); + + pw = getpwuid(uid); + if (pw == NULL || pw->pw_name == NULL) { + dropbear_exit("Couldn't find username for current user"); + } + + ses.authstate.username = m_strdup(pw->pw_name); + } + TRACE(("leave cli_get_user: %s", cli_ses.username)); +} + +/* Send a "none" auth request to get available methods */ +void cli_auth_getmethods() { + + TRACE(("enter cli_auth_getmethods")); + + CHECKCLEARTOWRITE(); + + buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST); + buf_putstring(ses.writepayload, ses.authstate.username, + strlen(ses.authstate.username)); + buf_putstring(ses.writepayload, SSH_SERVICE_CONNECTION, + SSH_SERVICE_CONNECTION_LEN); + buf_putstring(ses.writepayload, "none", 4); /* 'none' method */ + + encrypt_packet(); + cli_ses.state = USERAUTH_METHODS_SENT; + TRACE(("leave cli_auth_getmethods")); + +} + +void recv_msg_userauth_failure() { + + unsigned char * methods = NULL; + unsigned char * tok = NULL; + unsigned int methlen = 0; + unsigned int partial = 0; + unsigned int i = 0; + + TRACE(("<- MSG_USERAUTH_FAILURE")); + TRACE(("enter recv_msg_userauth_failure")); + + methods = buf_getstring(ses.payload, &methlen); + + partial = buf_getbyte(ses.payload); + + if (partial) { + dropbear_log(LOG_INFO, "Authentication partially succeeded, more attempts required"); + } else { + ses.authstate.failcount++; + } + + TRACE(("Methods (len %d): '%s'", methlen, methods)); + + ses.authstate.authdone=0; + ses.authstate.authtypes=0; + + /* Split with nulls rather than commas */ + for (i = 0; i < methlen; i++) { + if (methods[i] == ',') { + methods[i] = '\0'; + } + } + + tok = methods; /* tok stores the next method we'll compare */ + for (i = 0; i <= methlen; i++) { + if (methods[i] == '\0') { + TRACE(("auth method '%s'\n", tok)); +#ifdef DROPBEAR_PUBKEY_AUTH + if (strncmp(AUTH_METHOD_PUBKEY, tok, + AUTH_METHOD_PUBKEY_LEN) == 0) { + ses.authstate.authtypes |= AUTH_TYPE_PUBKEY; + } +#endif +#ifdef DROPBEAR_PASSWORD_AUTH + if (strncmp(AUTH_METHOD_PASSWORD, tok, + AUTH_METHOD_PASSWORD_LEN) == 0) { + ses.authstate.authtypes |= AUTH_TYPE_PASSWORD; + } +#endif + tok = &methods[i]; /* Must make sure we don't use it after + the last loop, since it'll point + to something undefined */ + } + } + + cli_ses.state = USERAUTH_FAIL_RCVD; + + TRACE(("leave recv_msg_userauth_failure")); +} + +void recv_msg_userauth_success() { + TRACE(("received msg_userauth_success")); + ses.authstate.authdone = 1; +} + +void cli_auth_try() { + + TRACE(("enter cli_auth_try")); + int finished = 0; + + CHECKCLEARTOWRITE(); + + /* XXX We hardcode that we try a pubkey first */ +#ifdef DROPBEAR_PUBKEY_AUTH + if (ses.authstate.authtypes & AUTH_TYPE_PUBKEY) { + finished = cli_auth_pubkey(); + } +#endif + +#ifdef DROPBEAR_PASSWORD_AUTH + if (!finished && ses.authstate.authtypes & AUTH_TYPE_PASSWORD) { + finished = cli_auth_password(); + } +#endif + + if (!finished) { + dropbear_exit("No auth methods could be used."); + } + + cli_ses.state = USERAUTH_REQ_SENT; + TRACE(("leave cli_auth_try")); +} diff --git a/cli-authpasswd.c b/cli-authpasswd.c new file mode 100644 index 0000000..6185334 --- /dev/null +++ b/cli-authpasswd.c @@ -0,0 +1,36 @@ +#include "includes.h" +#include "buffer.h" +#include "dbutil.h" +#include "session.h" +#include "ssh.h" + +int cli_auth_password() { + + char* password = NULL; + TRACE(("enter cli_auth_password")); + + CHECKCLEARTOWRITE(); + password = getpass("Password: "); + + buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST); + + buf_putstring(ses.writepayload, ses.authstate.username, + strlen(ses.authstate.username)); + + buf_putstring(ses.writepayload, SSH_SERVICE_CONNECTION, + SSH_SERVICE_CONNECTION_LEN); + + buf_putstring(ses.writepayload, AUTH_METHOD_PASSWORD, + AUTH_METHOD_PASSWORD_LEN); + + buf_putbyte(ses.writepayload, 0); /* FALSE - so says the spec */ + + buf_putstring(ses.writepayload, password, strlen(password)); + + encrypt_packet(); + m_burn(password, strlen(password)); + + TRACE(("leave cli_auth_password")); + return 1; /* Password auth can always be tried */ + +} diff --git a/cli-kex.c b/cli-kex.c index 4d26332..2577caf 100644 --- a/cli-kex.c +++ b/cli-kex.c @@ -34,6 +34,7 @@ #include "bignum.h" #include "random.h" #include "runopts.h" +#include "signkey.h" diff --git a/cli-main.c b/cli-main.c index 2460060..c5d5b3e 100644 --- a/cli-main.c +++ b/cli-main.c @@ -1,6 +1,17 @@ -#include +#include "includes.h" +#include "dbutil.h" +#include "runopts.h" +#include "session.h" +static void cli_dropbear_exit(int exitcode, const char* format, va_list param); +static void cli_dropbear_log(int priority, const char* format, va_list param); + +#if defined(DBMULTI_dbclient) || !defined(DROPBEAR_MULTI) +#if defined(DBMULTI_dbclient) && defined(DROPBEAR_MULTI) +int cli_main(int argc, char ** argv) { +#else int main(int argc, char ** argv) { +#endif int sock; char* error = NULL; @@ -12,6 +23,9 @@ int main(int argc, char ** argv) { cli_getopts(argc, argv); + TRACE(("user='%s' host='%s' port='%s'", cli_opts.username, + cli_opts.remotehost, cli_opts.remoteport)); + sock = connect_remote(cli_opts.remotehost, cli_opts.remoteport, 0, &error); @@ -23,7 +37,7 @@ int main(int argc, char ** argv) { len = strlen(cli_opts.remotehost); len += 10; /* 16 bit port and leeway*/ hostandport = (char*)m_malloc(len); - snprintf(hostandport, len, "%s%d", + snprintf(hostandport, len, "%s:%s", cli_opts.remotehost, cli_opts.remoteport); cli_session(sock, hostandport); @@ -31,3 +45,34 @@ int main(int argc, char ** argv) { /* not reached */ return -1; } +#endif /* DBMULTI stuff */ + +static void cli_dropbear_exit(int exitcode, const char* format, va_list param) { + + char fmtbuf[300]; + + if (!sessinitdone) { + snprintf(fmtbuf, sizeof(fmtbuf), "exited: %s", + format); + } else { + snprintf(fmtbuf, sizeof(fmtbuf), + "connection to %s@%s:%s exited: %s", + cli_opts.username, cli_opts.remotehost, + cli_opts.remoteport, format); + } + + _dropbear_log(LOG_INFO, fmtbuf, param); + + common_session_cleanup(); + exit(exitcode); +} + +static void cli_dropbear_log(int priority, const char* format, va_list param) { + + char printbuf[1024]; + + vsnprintf(printbuf, sizeof(printbuf), format, param); + + fprintf(stderr, "Dropbear: %s\n", printbuf); + +} diff --git a/cli-service.c b/cli-service.c new file mode 100644 index 0000000..c873919 --- /dev/null +++ b/cli-service.c @@ -0,0 +1,62 @@ +#include "includes.h" +#include "service.h" +#include "dbutil.h" +#include "packet.h" +#include "buffer.h" +#include "session.h" +#include "ssh.h" + +void send_msg_service_request(char* servicename) { + + TRACE(("enter send_msg_service_request: servicename='%s'", servicename)); + + CHECKCLEARTOWRITE(); + + buf_putbyte(ses.payload, SSH_MSG_SERVICE_REQUEST); + buf_putstring(ses.payload, servicename, strlen(servicename)); + + encrypt_packet(); + TRACE(("leave send_msg_service_request")); +} + +/* This just sets up the state variables right for the main client session loop + * to deal with */ +void recv_msg_service_accept() { + + unsigned char* servicename; + unsigned int len; + + TRACE(("enter recv_msg_service_accept")); + + servicename = buf_getstring(ses.payload, &len); + + /* ssh-userauth */ + if (cli_ses.state = SERVICE_AUTH_REQ_SENT + && len == SSH_SERVICE_USERAUTH_LEN + && strncmp(SSH_SERVICE_USERAUTH, servicename, len) == 0) { + + cli_ses.state = SERVICE_AUTH_ACCEPT_RCVD; + m_free(servicename); + TRACE(("leave recv_msg_service_accept: done ssh-userauth")); + return; + } + + /* ssh-connection */ + if (cli_ses.state = SERVICE_CONN_REQ_SENT + && len == SSH_SERVICE_CONNECTION_LEN + && strncmp(SSH_SERVICE_CONNECTION, servicename, len) == 0) { + + if (ses.authstate.authdone != 1) { + dropbear_exit("request for connection before auth"); + } + + cli_ses.state = SERVICE_CONN_ACCEPT_RCVD; + m_free(servicename); + TRACE(("leave recv_msg_service_accept: done ssh-connection")); + return; + } + + dropbear_exit("unrecognised service accept"); + /* m_free(servicename); not reached */ + +} diff --git a/cli-session.c b/cli-session.c index d5afaf9..9f80bd1 100644 --- a/cli-session.c +++ b/cli-session.c @@ -8,9 +8,11 @@ #include "tcpfwd-remote.h" #include "channel.h" #include "random.h" +#include "service.h" static void cli_remoteclosed(); static void cli_sessionloop(); +static void cli_session_init(); struct clientsession cli_ses; /* GLOBAL */ @@ -28,6 +30,8 @@ static const packettype cli_packettypes[] = { {SSH_MSG_CHANNEL_CLOSE, recv_msg_channel_close}, {SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation}, {SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure}, + {SSH_MSG_USERAUTH_FAILURE, recv_msg_userauth_failure}, + {SSH_MSG_USERAUTH_SUCCESS, recv_msg_userauth_success}, {0, 0} /* End */ }; @@ -37,6 +41,7 @@ static const struct ChanType *cli_chantypes[] = { * that forwarding */ NULL /* Null termination */ }; + void cli_session(int sock, char* remotehost) { crypto_init(); @@ -44,11 +49,9 @@ void cli_session(int sock, char* remotehost) { chaninitialise(cli_chantypes); - /* For printing "remote host closed" for the user */ - session_remoteclosed = cli_remoteclosed; - /* packet handlers */ - ses.packettypes = cli_packettypes; + /* Set up cli_ses vars */ + cli_session_init(); /* Ready to go */ sessinitdone = 1; @@ -66,26 +69,85 @@ void cli_session(int sock, char* remotehost) { /* Not reached */ +} + +static void cli_session_init() { + cli_ses.state = STATE_NOTHING; + cli_ses.kex_state = KEX_NOTHING; + + /* For printing "remote host closed" for the user */ + ses.remoteclosed = cli_remoteclosed; + ses.buf_match_algo = cli_buf_match_algo; + + /* packet handlers */ + ses.packettypes = cli_packettypes; } +/* This function drives the progress of the session - it initiates KEX, + * service, userauth and channel requests */ static void cli_sessionloop() { - switch (cli_ses.state) { + TRACE(("enter cli_sessionloop")); + + if (cli_ses.kex_state == KEX_NOTHING && ses.kexstate.recvkexinit) { + cli_ses.state = KEXINIT_RCVD; + } - KEXINIT_RCVD: - /* We initiate the KEX. If DH wasn't the correct type, the KEXINIT - * negotiation would have failed. */ - send_msg_kexdh_init(); - cli_ses.state = KEXDH_INIT_SENT; - break; + if (cli_ses.state == KEXINIT_RCVD) { - default: - break; + /* We initiate the KEXDH. If DH wasn't the correct type, the KEXINIT + * negotiation would have failed. */ + send_msg_kexdh_init(); + cli_ses.kex_state = KEXDH_INIT_SENT; + TRACE(("leave cli_sessionloop: done with KEXINIT_RCVD")); + return; } - if (cli_ses.donefirstkex && !cli_ses.authdone) { + /* A KEX has finished, so we should go back to our KEX_NOTHING state */ + if (cli_ses.kex_state != KEX_NOTHING && ses.kexstate.recvkexinit == 0 + && ses.kexstate.sentkexinit == 0) { + cli_ses.kex_state = KEX_NOTHING; + } + + /* We shouldn't do anything else if a KEX is in progress */ + if (cli_ses.kex_state != KEX_NOTHING) { + TRACE(("leave cli_sessionloop: kex_state != KEX_NOTHING")); + return; + } + /* We should exit if we haven't donefirstkex: we shouldn't reach here + * in normal operation */ + if (ses.kexstate.donefirstkex == 0) { + TRACE(("XXX XXX might be bad! leave cli_sessionloop: haven't donefirstkex")); + } + + switch (cli_ses.state) { + + case STATE_NOTHING: + /* We've got the transport layer sorted, we now need to request + * userauth */ + send_msg_service_request(SSH_SERVICE_USERAUTH); + cli_ses.state = SERVICE_AUTH_REQ_SENT; + return; + + /* userauth code */ + case SERVICE_AUTH_ACCEPT_RCVD: + cli_get_user(); + cli_auth_getmethods(); + cli_ses.state = USERAUTH_METHODS_SENT; + return; + + case USERAUTH_FAIL_RCVD: + cli_auth_try(); + return; + + /* XXX more here needed */ + + + default: + break; + } } @@ -97,5 +159,5 @@ static void cli_remoteclosed() { * already sent/received disconnect message(s) ??? */ close(ses.sock); ses.sock = -1; - dropbear_exit("%s closed the connection", ses.remotehost); + dropbear_exit("remote closed the connection"); } diff --git a/common-kex.c b/common-kex.c index 21accb6..9847a48 100644 --- a/common-kex.c +++ b/common-kex.c @@ -5,7 +5,7 @@ * This code is copied from the larger file "kex.c" * some functions are verbatim, others are generalized --mihnea * - * Copyright (c) 2002,2003 Matt Johnston + * Copyright (c) 2002-2004 Matt Johnston * Portions Copyright (c) 2004 by Mihnea Stoenescu * All rights reserved. * @@ -54,10 +54,12 @@ const unsigned char dh_p_val[] = { const int DH_G_VAL = 2; +static void kexinitialise(); static void gen_new_keys(); #ifndef DISABLE_ZLIB static void gen_new_zstreams(); #endif +static void read_kex_algos(); /* helper function for gen_new_keys */ static void hashkeys(unsigned char *out, int outlen, const hash_state * hs, unsigned const char X); @@ -145,6 +147,7 @@ void send_msg_newkeys() { kexinitialise(); /* we've finished with this kex */ TRACE((" -> DATAALLOWED=1")); ses.dataallowed = 1; /* we can send other packets again now */ + ses.kexstate.donefirstkex = 1; } else { ses.kexstate.sentnewkeys = 1; TRACE(("SENTNEWKEYS=1")); @@ -168,6 +171,7 @@ void recv_msg_newkeys() { kexinitialise(); /* we've finished with this kex */ TRACE((" -> DATAALLOWED=1")); ses.dataallowed = 1; /* we can send other packets again now */ + ses.kexstate.donefirstkex = 1; } else { TRACE(("RECVNEWKEYS=1")); ses.kexstate.recvnewkeys = 1; @@ -177,8 +181,15 @@ void recv_msg_newkeys() { } -/* Duplicated verbatim from kex.c --mihnea */ -void kexinitialise() { +/* Set up the kex for the first time */ +void kexfirstinitialise() { + + ses.kexstate.donefirstkex = 0; + kexinitialise(); +} + +/* Reset the kex state, ready for a new negotiation */ +static void kexinitialise() { struct timeval tv; @@ -404,7 +415,7 @@ void recv_msg_kexinit() { #ifdef DROPBEAR_CLIENT /* read the peer's choice of algos */ - read_kex_algos(cli_buf_match_algo); + read_kex_algos(); /* V_C, the client's version string (CR and NL excluded) */ buf_putstring(ses.kexhashbuf, @@ -423,14 +434,13 @@ void recv_msg_kexinit() { buf_getptr(ses.payload, ses.payload->len), ses.payload->len); - cli_ses.state = KEXINIT_RCVD; #endif } else { /* SERVER */ #ifdef DROPBEAR_SERVER /* read the peer's choice of algos */ - read_kex_algos(svr_buf_match_algo); + read_kex_algos(); /* V_C, the client's version string (CR and NL excluded) */ buf_putstring(ses.kexhashbuf, ses.remoteident, strlen((char*)ses.remoteident)); @@ -583,9 +593,7 @@ void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them, /* read the other side's algo list. buf_match_algo is a callback to match * algos for the client or server. */ -void read_kex_algos( - algo_type*(buf_match_algo)(buffer*buf, algo_type localalgos[], - int *goodguess)) { +static void read_kex_algos() { algo_type * algo; char * erralgo = NULL; @@ -599,7 +607,7 @@ void read_kex_algos( ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context)); /* kex_algorithms */ - algo = buf_match_algo(ses.payload, sshkex, &goodguess); + algo = ses.buf_match_algo(ses.payload, sshkex, &goodguess); allgood &= goodguess; if (algo == NULL) { erralgo = "kex"; @@ -608,7 +616,7 @@ void read_kex_algos( ses.newkeys->algo_kex = algo->val; /* server_host_key_algorithms */ - algo = buf_match_algo(ses.payload, sshhostkey, &goodguess); + algo = ses.buf_match_algo(ses.payload, sshhostkey, &goodguess); allgood &= goodguess; if (algo == NULL) { erralgo = "hostkey"; @@ -617,7 +625,7 @@ void read_kex_algos( ses.newkeys->algo_hostkey = algo->val; /* encryption_algorithms_client_to_server */ - algo = buf_match_algo(ses.payload, sshciphers, &goodguess); + algo = ses.buf_match_algo(ses.payload, sshciphers, &goodguess); if (algo == NULL) { erralgo = "enc c->s"; goto error; @@ -625,7 +633,7 @@ void read_kex_algos( ses.newkeys->recv_algo_crypt = (struct dropbear_cipher*)algo->data; /* encryption_algorithms_server_to_client */ - algo = buf_match_algo(ses.payload, sshciphers, &goodguess); + algo = ses.buf_match_algo(ses.payload, sshciphers, &goodguess); if (algo == NULL) { erralgo = "enc s->c"; goto error; @@ -633,7 +641,7 @@ void read_kex_algos( ses.newkeys->trans_algo_crypt = (struct dropbear_cipher*)algo->data; /* mac_algorithms_client_to_server */ - algo = buf_match_algo(ses.payload, sshhashes, &goodguess); + algo = ses.buf_match_algo(ses.payload, sshhashes, &goodguess); if (algo == NULL) { erralgo = "mac c->s"; goto error; @@ -641,7 +649,7 @@ void read_kex_algos( ses.newkeys->recv_algo_mac = (struct dropbear_hash*)algo->data; /* mac_algorithms_server_to_client */ - algo = buf_match_algo(ses.payload, sshhashes, &goodguess); + algo = ses.buf_match_algo(ses.payload, sshhashes, &goodguess); if (algo == NULL) { erralgo = "mac s->c"; goto error; @@ -649,7 +657,7 @@ void read_kex_algos( ses.newkeys->trans_algo_mac = (struct dropbear_hash*)algo->data; /* compression_algorithms_client_to_server */ - algo = buf_match_algo(ses.payload, sshcompress, &goodguess); + algo = ses.buf_match_algo(ses.payload, sshcompress, &goodguess); if (algo == NULL) { erralgo = "comp c->s"; goto error; @@ -657,7 +665,7 @@ void read_kex_algos( ses.newkeys->recv_algo_comp = algo->val; /* compression_algorithms_server_to_client */ - algo = buf_match_algo(ses.payload, sshcompress, &goodguess); + algo = ses.buf_match_algo(ses.payload, sshcompress, &goodguess); if (algo == NULL) { erralgo = "comp s->c"; goto error; diff --git a/common-runopts.c b/common-runopts.c new file mode 100644 index 0000000..097ab12 --- /dev/null +++ b/common-runopts.c @@ -0,0 +1,28 @@ +/* + * Dropbear - a SSH2 server + * + * Copyright (c) 2002,2003 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + +#include "includes.h" +#include "runopts.h" + +runopts opts; /* GLOBAL */ diff --git a/common-session.c b/common-session.c index 70ddbfe..93e7c74 100644 --- a/common-session.c +++ b/common-session.c @@ -45,8 +45,6 @@ int sessinitdone = 0; /* GLOBAL */ /* this is set when we get SIGINT or SIGTERM, the handler is in main.c */ int exitflag = 0; /* GLOBAL */ -void(*session_remoteclosed)() = NULL; - static void checktimeouts(); static int ident_readln(int fd, char* buf, int count); @@ -63,7 +61,7 @@ void common_session_init(int sock, char* remotehost) { ses.connecttimeout = 0; - kexinitialise(); /* initialise the kex state */ + kexfirstinitialise(); /* initialise the kex state */ chaninitialise(); /* initialise the channel state */ ses.writepayload = buf_new(MAX_TRANS_PAYLOAD_LEN); @@ -104,8 +102,6 @@ void common_session_init(int sock, char* remotehost) { ses.dh_K = NULL; ses.remoteident = NULL; - ses.authdone = 0; - ses.chantypes = NULL; ses.allowprivport = 0; diff --git a/dbmulti.c b/dbmulti.c index 8f39227..24adce1 100644 --- a/dbmulti.c +++ b/dbmulti.c @@ -19,6 +19,11 @@ int main(int argc, char ** argv) { return dropbear_main(argc, argv); } #endif +#ifdef DBMULTI_dbclient + if (strcmp(progname, "dbclient") == 0) { + return cli_main(argc, argv); + } +#endif #ifdef DBMULTI_dropbearkey if (strcmp(progname, "dropbearkey") == 0) { return dropbearkey_main(argc, argv); @@ -41,6 +46,9 @@ int main(int argc, char ** argv) { #ifdef DBMULTI_dropbear "'dropbear' - the Dropbear server\n" #endif +#ifdef DBMULTI_dbclient + "'dbclient' - the Dropbear client\n" +#endif #ifdef DBMULTI_dropbearkey "'dropbearkey' - the key generator\n" #endif diff --git a/debug.h b/debug.h index 736690a..d619a58 100644 --- a/debug.h +++ b/debug.h @@ -36,7 +36,7 @@ /* Define this to print trace statements - very verbose */ /* Caution: Don't use this in an unfriendly environment (ie unfirewalled), * since the printing does not sanitise strings etc */ -//#define DEBUG_TRACE +#define DEBUG_TRACE /* All functions writing to the cleartext payload buffer call * CHECKCLEARTOWRITE() before writing. This is only really useful if you're diff --git a/kex.h b/kex.h index e6c8ac1..01626ed 100644 --- a/kex.h +++ b/kex.h @@ -32,15 +32,11 @@ void send_msg_kexinit(); void recv_msg_kexinit(); void send_msg_newkeys(); void recv_msg_newkeys(); -void kexinitialise(); +void kexfirstinitialise(); void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv); void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them, sign_key *hostkey); -void read_kex_algos( - algo_type*(buf_match_algo)(buffer*buf, algo_type localalgos[], - int *goodguess)); - void recv_msg_kexdh_init(); // server void send_msg_kexdh_init(); // client @@ -59,6 +55,9 @@ struct KEXState { unsigned sentnewkeys : 1; /* set once we've send/recv'ed MSG_NEWKEYS*/ unsigned recvnewkeys : 1; + unsigned donefirstkex : 1; /* Set to 1 after the first kex has completed, + ie the transport layer has been set up */ + long lastkextime; /* time of the last kex */ unsigned int datatrans; /* data transmitted since last kex */ unsigned int datarecv; /* data received since last kex */ diff --git a/options.h b/options.h index 3aeac0f..ee0ee60 100644 --- a/options.h +++ b/options.h @@ -29,8 +29,6 @@ * Define compile-time options below - the "#ifndef DROPBEAR_XXX .... #endif" * parts are to allow for commandline -DDROPBEAR_XXX options etc. ******************************************************************/ -#define DROPBEAR_SERVER -//#define DROPBEAR_CLIENT #ifndef DROPBEAR_PORT #define DROPBEAR_PORT 22 @@ -48,7 +46,6 @@ * perhaps 20% slower for pubkey operations (it is probably worth experimenting * if you want to use this) */ /*#define NO_FAST_EXPTMOD*/ -#define DROPBEAR_SMALL_CODE /* Enable X11 Forwarding */ #define ENABLE_X11FWD @@ -114,7 +111,7 @@ /* Authentication types to enable, at least one required. RFC Draft requires pubkey auth, and recommends password */ #define DROPBEAR_PASSWORD_AUTH -#define DROPBEAR_PUBKEY_AUTH +//#define DROPBEAR_PUBKEY_AUTH /* Random device to use - you must specify _one only_. * DEV_RANDOM is recommended on hosts with a good /dev/urandom, otherwise use @@ -162,20 +159,8 @@ /* This is used by the scp binary when used as a client binary */ #define _PATH_SSH_PROGRAM "/usr/bin/ssh" -/* Multi-purpose binary configuration - if you want to make the combined - * binary, first define DROPBEAR_MULTI, and then define which of the three - * components you want. You should then compile Dropbear with - * "make clean; make dropbearmulti". You'll need to install the binary - * manually, see MULTI for details */ - -/* #define DROPBEAR_MULTI */ - -/* The three multi binaries: dropbear, dropbearkey, dropbearconvert - * Comment out these if you don't want some of them */ -#define DBMULTI_DROPBEAR -#define DBMULTI_KEY -#define DBMULTI_CONVERT - +/* Multi-purpose binary configuration has now moved. Look at the top + * of the Makefile for instructions, or INSTALL */ /******************************************************************* * You shouldn't edit below here unless you know you need to. @@ -246,7 +231,7 @@ #define DROPBEAR_COMP_ZLIB 1 /* Required for pubkey auth */ -#ifdef DROPBEAR_PUBKEY_AUTH +#if defined(DROPBEAR_PUBKEY_AUTH) || defined(DROPBEAR_CLIENT) #define DROPBEAR_SIGNKEY_VERIFY #endif diff --git a/packet.c b/packet.c index 886fe67..997bc6f 100644 --- a/packet.c +++ b/packet.c @@ -73,7 +73,7 @@ void write_packet() { } if (written == 0) { - session_remoteclosed(); + ses.remoteclosed(); } if (written == len) { @@ -122,7 +122,7 @@ void read_packet() { len = read(ses.sock, buf_getptr(ses.readbuf, maxlen), maxlen); if (len == 0) { - session_remoteclosed(); + ses.remoteclosed(); } if (len < 0) { @@ -171,7 +171,7 @@ static void read_packet_init() { len = read(ses.sock, buf_getwriteptr(ses.readbuf, maxlen), maxlen); if (len == 0) { - session_remoteclosed(); + ses.remoteclosed(); } if (len < 0) { if (errno == EINTR) { diff --git a/runopts.h b/runopts.h index 1bf1539..125d737 100644 --- a/runopts.h +++ b/runopts.h @@ -79,7 +79,11 @@ void svr_getopts(int argc, char ** argv); /* Uncompleted XXX matt */ typedef struct cli_runopts { - int todo; + char *remotehost; + char *remoteport; + + char *username; + /* XXX TODO */ } cli_runopts; diff --git a/scp.c b/scp.c index b23dec6..b6eec88 100644 --- a/scp.c +++ b/scp.c @@ -229,8 +229,12 @@ void tolocal(int, char *[]); void toremote(char *, int, char *[]); void usage(void); -int +#if defined(DBMULTI_scp) || !defined(DROPBEAR_MULTI) +#if defined(DBMULTI_scp) && defined(DROPBEAR_MULTI) +int scp_main(int argc, char **argv) +#else main(int argc, char **argv) +#endif { int ch, fflag, tflag, status; double speed; @@ -379,6 +383,7 @@ main(int argc, char **argv) } exit(errs != 0); } +#endif /* DBMULTI stuff */ void toremote(char *targ, int argc, char **argv) diff --git a/service.h b/service.h index 0dfcd6e..5f50347 100644 --- a/service.h +++ b/service.h @@ -25,6 +25,7 @@ #ifndef _SERVICE_H_ #define _SERVICE_H_ -void recv_msg_service_request(); +void recv_msg_service_request(); /* Server */ +void send_msg_service_request(); /* Client */ #endif /* _SERVICE_H_ */ diff --git a/session.h b/session.h index d929c1c..4a7e0ac 100644 --- a/session.h +++ b/session.h @@ -45,7 +45,6 @@ void common_session_cleanup(); void checktimeouts(); void session_identification(); -extern void(*session_remoteclosed)(); /* Server */ void svr_session(int sock, int childpipe, char *remotehost); @@ -135,13 +134,18 @@ struct sshsession { buffer* transkexinit; /* the kexinit packet we send should be kept so we can add it to the hash when generating keys */ + algo_type*(*buf_match_algo)(buffer*buf, algo_type localalgos[], + int *goodguess); /* The function to use to choose which algorithm + to use from the ones presented by the remote + side. Is specific to the client/server mode, + hence the function-pointer callback.*/ - unsigned char authdone; /* Indicates when authentication has been - completed. This applies to both client and - server - in the server it gets set to 1 when - authentication is successful, in the client it - is set when the server has told us that auth - succeeded */ + void(*remoteclosed)(); /* A callback to handle closure of the + remote connection */ + + + struct AuthState authstate; /* Common amongst client and server, since most + struct elements are common */ /* Channel related */ struct Channel ** channels; /* these pointers may be null */ @@ -165,7 +169,6 @@ struct serversession { /* Server specific options */ int childpipe; /* kept open until we successfully authenticate */ /* userauth */ - struct AuthState authstate; struct ChildPid * childpids; /* array of mappings childpid<->channel */ unsigned int childpidsize; @@ -173,17 +176,30 @@ struct serversession { }; typedef enum { - NOTHING, + KEX_NOTHING, KEXINIT_RCVD, KEXDH_INIT_SENT, - KEXDH_REPLY_RCVD, + KEXDONE, + +} cli_kex_state; + +typedef enum { + STATE_NOTHING, + SERVICE_AUTH_REQ_SENT, + SERVICE_AUTH_ACCEPT_RCVD, + SERVICE_CONN_REQ_SENT, + SERVICE_CONN_ACCEPT_RCVD, + USERAUTH_METHODS_SENT, + USERAUTH_REQ_SENT, + USERAUTH_FAIL_RCVD, } cli_state; struct clientsession { mp_int *dh_e, *dh_x; /* Used during KEX */ - cli_state state; /* Used to progress the KEX/auth/channelsession etc */ + cli_kex_state kex_state; /* Used for progressing KEX */ + cli_state state; /* Used to progress auth/channelsession etc */ int something; /* XXX */ unsigned donefirstkex : 1; /* Set when we set sentnewkeys, never reset */ diff --git a/svr-agentfwd.c b/svr-agentfwd.c index fd068fe..0dad2a4 100644 --- a/svr-agentfwd.c +++ b/svr-agentfwd.c @@ -152,8 +152,8 @@ void agentcleanup(struct ChanSess * chansess) { * for themselves */ uid = getuid(); gid = getgid(); - if ((setegid(svr_ses.authstate.pw->pw_gid)) < 0 || - (seteuid(svr_ses.authstate.pw->pw_uid)) < 0) { + if ((setegid(ses.authstate.pw->pw_gid)) < 0 || + (seteuid(ses.authstate.pw->pw_uid)) < 0) { dropbear_exit("failed to set euid"); } @@ -215,8 +215,8 @@ static int bindagent(int fd, struct ChanSess * chansess) { /* drop to user privs to make the dir/file */ uid = getuid(); gid = getgid(); - if ((setegid(svr_ses.authstate.pw->pw_gid)) < 0 || - (seteuid(svr_ses.authstate.pw->pw_uid)) < 0) { + if ((setegid(ses.authstate.pw->pw_gid)) < 0 || + (seteuid(ses.authstate.pw->pw_uid)) < 0) { dropbear_exit("failed to set euid"); } diff --git a/svr-auth.c b/svr-auth.c index 7b588c0..0f0ef67 100644 --- a/svr-auth.c +++ b/svr-auth.c @@ -41,9 +41,9 @@ static int checkusername(unsigned char *username, unsigned int userlen); static void send_msg_userauth_banner(); /* initialise the first time for a session, resetting all parameters */ -void authinitialise() { +void svr_authinitialise() { - svr_ses.authstate.failcount = 0; + ses.authstate.failcount = 0; authclear(); } @@ -53,17 +53,13 @@ void authinitialise() { * on initialisation */ static void authclear() { - ses.authdone = 0; - svr_ses.authstate.pw = NULL; - svr_ses.authstate.username = NULL; - svr_ses.authstate.printableuser = NULL; - svr_ses.authstate.authtypes = 0; + memset(&ses.authstate, 0, sizeof(ses.authstate)); #ifdef DROPBEAR_PUBKEY_AUTH - svr_ses.authstate.authtypes |= AUTH_TYPE_PUBKEY; + ses.authstate.authtypes |= AUTH_TYPE_PUBKEY; #endif #ifdef DROPBEAR_PASSWORD_AUTH if (svr_opts.noauthpass) { - svr_ses.authstate.authtypes |= AUTH_TYPE_PASSWORD; + ses.authstate.authtypes |= AUTH_TYPE_PASSWORD; } #endif @@ -103,7 +99,7 @@ void recv_msg_userauth_request() { TRACE(("enter recv_msg_userauth_request")); /* ignore packets if auth is already done */ - if (ses.authdone == 1) { + if (ses.authstate.authdone == 1) { return; } @@ -147,12 +143,12 @@ void recv_msg_userauth_request() { #ifdef DROPBEAR_PASSWORD_AUTH if (!svr_opts.noauthpass && - !(svr_opts.norootpass && svr_ses.authstate.pw->pw_uid == 0) ) { + !(svr_opts.norootpass && ses.authstate.pw->pw_uid == 0) ) { /* user wants to try password auth */ if (methodlen == AUTH_METHOD_PASSWORD_LEN && strncmp(methodname, AUTH_METHOD_PASSWORD, AUTH_METHOD_PASSWORD_LEN) == 0) { - passwordauth(); + svr_auth_password(); goto out; } } @@ -163,7 +159,7 @@ void recv_msg_userauth_request() { if (methodlen == AUTH_METHOD_PUBKEY_LEN && strncmp(methodname, AUTH_METHOD_PUBKEY, AUTH_METHOD_PUBKEY_LEN) == 0) { - pubkeyauth(); + svr_auth_pubkey(); goto out; } #endif @@ -192,21 +188,21 @@ static int checkusername(unsigned char *username, unsigned int userlen) { } /* new user or username has changed */ - if (svr_ses.authstate.username == NULL || - strcmp(username, svr_ses.authstate.username) != 0) { + if (ses.authstate.username == NULL || + strcmp(username, ses.authstate.username) != 0) { /* the username needs resetting */ - if (svr_ses.authstate.username != NULL) { + if (ses.authstate.username != NULL) { dropbear_log(LOG_WARNING, "client trying multiple usernames"); - m_free(svr_ses.authstate.username); + m_free(ses.authstate.username); } authclear(); - svr_ses.authstate.pw = getpwnam((char*)username); - svr_ses.authstate.username = m_strdup(username); - m_free(svr_ses.authstate.printableuser); + ses.authstate.pw = getpwnam((char*)username); + ses.authstate.username = m_strdup(username); + m_free(ses.authstate.printableuser); } /* check that user exists */ - if (svr_ses.authstate.pw == NULL) { + if (ses.authstate.pw == NULL) { TRACE(("leave checkusername: user '%s' doesn't exist", username)); dropbear_log(LOG_WARNING, "login attempt for nonexistent user"); @@ -215,10 +211,10 @@ static int checkusername(unsigned char *username, unsigned int userlen) { } /* We can set it once we know its a real user */ - svr_ses.authstate.printableuser = m_strdup(svr_ses.authstate.pw->pw_name); + ses.authstate.printableuser = m_strdup(ses.authstate.pw->pw_name); /* check for non-root if desired */ - if (svr_opts.norootlogin && svr_ses.authstate.pw->pw_uid == 0) { + if (svr_opts.norootlogin && ses.authstate.pw->pw_uid == 0) { TRACE(("leave checkusername: root login disabled")); dropbear_log(LOG_WARNING, "root login rejected"); send_msg_userauth_failure(0, 1); @@ -226,18 +222,18 @@ static int checkusername(unsigned char *username, unsigned int userlen) { } /* check for an empty password */ - if (svr_ses.authstate.pw->pw_passwd[0] == '\0') { + if (ses.authstate.pw->pw_passwd[0] == '\0') { TRACE(("leave checkusername: empty pword")); dropbear_log(LOG_WARNING, "user '%s' has blank password, rejected", - svr_ses.authstate.printableuser); + ses.authstate.printableuser); send_msg_userauth_failure(0, 1); return DROPBEAR_FAILURE; } - TRACE(("shell is %s", svr_ses.authstate.pw->pw_shell)); + TRACE(("shell is %s", ses.authstate.pw->pw_shell)); /* check that the shell is set */ - usershell = svr_ses.authstate.pw->pw_shell; + usershell = ses.authstate.pw->pw_shell; if (usershell[0] == '\0') { /* empty shell in /etc/passwd means /bin/sh according to passwd(5) */ usershell = "/bin/sh"; @@ -258,7 +254,7 @@ static int checkusername(unsigned char *username, unsigned int userlen) { endusershell(); TRACE(("no matching shell")); dropbear_log(LOG_WARNING, "user '%s' has invalid shell, rejected", - svr_ses.authstate.printableuser); + ses.authstate.printableuser); send_msg_userauth_failure(0, 1); return DROPBEAR_FAILURE; @@ -266,7 +262,7 @@ goodshell: endusershell(); TRACE(("matching shell")); - TRACE(("uid = %d", svr_ses.authstate.pw->pw_uid)); + TRACE(("uid = %d", ses.authstate.pw->pw_uid)); TRACE(("leave checkusername")); return DROPBEAR_SUCCESS; @@ -290,14 +286,14 @@ void send_msg_userauth_failure(int partial, int incrfail) { /* put a list of allowed types */ typebuf = buf_new(30); /* long enough for PUBKEY and PASSWORD */ - if (svr_ses.authstate.authtypes & AUTH_TYPE_PUBKEY) { + if (ses.authstate.authtypes & AUTH_TYPE_PUBKEY) { buf_putbytes(typebuf, AUTH_METHOD_PUBKEY, AUTH_METHOD_PUBKEY_LEN); - if (svr_ses.authstate.authtypes & AUTH_TYPE_PASSWORD) { + if (ses.authstate.authtypes & AUTH_TYPE_PASSWORD) { buf_putbyte(typebuf, ','); } } - if (svr_ses.authstate.authtypes & AUTH_TYPE_PASSWORD) { + if (ses.authstate.authtypes & AUTH_TYPE_PASSWORD) { buf_putbytes(typebuf, AUTH_METHOD_PASSWORD, AUTH_METHOD_PASSWORD_LEN); } @@ -311,18 +307,18 @@ void send_msg_userauth_failure(int partial, int incrfail) { if (incrfail) { usleep(300000); /* XXX improve this */ - svr_ses.authstate.failcount++; + ses.authstate.failcount++; } - if (svr_ses.authstate.failcount >= MAX_AUTH_TRIES) { + if (ses.authstate.failcount >= MAX_AUTH_TRIES) { char * userstr; /* XXX - send disconnect ? */ TRACE(("Max auth tries reached, exiting")); - if (svr_ses.authstate.printableuser == NULL) { + if (ses.authstate.printableuser == NULL) { userstr = "is invalid"; } else { - userstr = svr_ses.authstate.printableuser; + userstr = ses.authstate.printableuser; } dropbear_exit("Max auth tries reached - user %s", userstr); } @@ -340,9 +336,9 @@ void send_msg_userauth_success() { buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_SUCCESS); encrypt_packet(); - ses.authdone = 1; + ses.authstate.authdone = 1; - if (svr_ses.authstate.pw->pw_uid == 0) { + if (ses.authstate.pw->pw_uid == 0) { ses.allowprivport = 1; } diff --git a/svr-authpasswd.c b/svr-authpasswd.c index 859cfd5..f161b69 100644 --- a/svr-authpasswd.c +++ b/svr-authpasswd.c @@ -35,7 +35,7 @@ /* Process a password auth request, sending success or failure messages as * appropriate */ -void passwordauth() { +void svr_auth_password() { #ifdef HAVE_SHADOW_H struct spwd *spasswd; @@ -47,10 +47,10 @@ void passwordauth() { unsigned char changepw; - passwdcrypt = svr_ses.authstate.pw->pw_passwd; + passwdcrypt = ses.authstate.pw->pw_passwd; #ifdef HAVE_SHADOW_H /* get the shadow password if possible */ - spasswd = getspnam(svr_ses.authstate.pw->pw_name); + spasswd = getspnam(ses.authstate.pw->pw_name); if (spasswd != NULL && spasswd->sp_pwdp != NULL) { passwdcrypt = spasswd->sp_pwdp; } @@ -66,7 +66,7 @@ void passwordauth() { * in auth.c */ if (passwdcrypt[0] == '\0') { dropbear_log(LOG_WARNING, "user '%s' has blank password, rejected", - svr_ses.authstate.printableuser); + ses.authstate.printableuser); send_msg_userauth_failure(0, 1); return; } @@ -92,12 +92,12 @@ void passwordauth() { /* successful authentication */ dropbear_log(LOG_NOTICE, "password auth succeeded for '%s'", - svr_ses.authstate.printableuser); + ses.authstate.printableuser); send_msg_userauth_success(); } else { dropbear_log(LOG_WARNING, "bad password attempt for '%s'", - svr_ses.authstate.printableuser); + ses.authstate.printableuser); send_msg_userauth_failure(0, 1); } diff --git a/svr-authpubkey.c b/svr-authpubkey.c index e3e6947..9c58a8d 100644 --- a/svr-authpubkey.c +++ b/svr-authpubkey.c @@ -50,7 +50,7 @@ static int getauthline(buffer * line, FILE * authfile); /* process a pubkey auth request, sending success or failure message as * appropriate */ -void pubkeyauth() { +void svr_auth_pubkey() { unsigned char testkey; /* whether we're just checking if a key is usable */ unsigned char* algo = NULL; /* pubkey algo */ @@ -113,12 +113,12 @@ void pubkeyauth() { signbuf->len) == DROPBEAR_SUCCESS) { dropbear_log(LOG_NOTICE, "pubkey auth succeeded for '%s' with key %s", - svr_ses.authstate.printableuser, fp); + ses.authstate.printableuser, fp); send_msg_userauth_success(); } else { dropbear_log(LOG_WARNING, "pubkey auth bad signature for '%s' with key %s", - svr_ses.authstate.printableuser, fp); + ses.authstate.printableuser, fp); send_msg_userauth_failure(0, 1); } m_free(fp); @@ -178,7 +178,7 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen, if (have_algo(algo, algolen, sshhostkey) == DROPBEAR_FAILURE) { dropbear_log(LOG_WARNING, "pubkey auth attempt with unknown algo for '%s'", - svr_ses.authstate.printableuser); + ses.authstate.printableuser); goto out; } @@ -190,12 +190,12 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen, /* we don't need to check pw and pw_dir for validity, since * its been done in checkpubkeyperms. */ - len = strlen(svr_ses.authstate.pw->pw_dir); + len = strlen(ses.authstate.pw->pw_dir); /* allocate max required pathname storage, * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */ filename = m_malloc(len + 22); snprintf(filename, len + 22, "%s/.ssh/authorized_keys", - svr_ses.authstate.pw->pw_dir); + ses.authstate.pw->pw_dir); /* open the file */ authfile = fopen(filename, "r"); @@ -352,19 +352,19 @@ static int checkpubkeyperms() { TRACE(("enter checkpubkeyperms")); - assert(svr_ses.authstate.pw); - if (svr_ses.authstate.pw->pw_dir == NULL) { + assert(ses.authstate.pw); + if (ses.authstate.pw->pw_dir == NULL) { goto out; } - if ((len = strlen(svr_ses.authstate.pw->pw_dir)) == 0) { + if ((len = strlen(ses.authstate.pw->pw_dir)) == 0) { goto out; } /* allocate max required pathname storage, * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */ filename = m_malloc(len + 22); - strncpy(filename, svr_ses.authstate.pw->pw_dir, len+1); + strncpy(filename, ses.authstate.pw->pw_dir, len+1); /* check ~ */ if (checkfileperm(filename) != DROPBEAR_SUCCESS) { @@ -406,7 +406,7 @@ static int checkfileperm(char * filename) { return DROPBEAR_FAILURE; } /* check ownership - user or root only*/ - if (filestat.st_uid != svr_ses.authstate.pw->pw_uid + if (filestat.st_uid != ses.authstate.pw->pw_uid && filestat.st_uid != 0) { TRACE(("leave checkfileperm: wrong ownership")); return DROPBEAR_FAILURE; diff --git a/svr-chansession.c b/svr-chansession.c index cbc7ebe..50e0a5e 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -239,7 +239,7 @@ static void closechansess(struct Channel *channel) { if (chansess->tty) { /* write the utmp/wtmp login record */ - li = login_alloc_entry(chansess->pid, svr_ses.authstate.username, + li = login_alloc_entry(chansess->pid, ses.authstate.username, ses.remotehost, chansess->tty); login_logout(li); login_free_entry(li); @@ -425,7 +425,7 @@ static int sessionpty(struct ChanSess * chansess) { dropbear_exit("out of memory"); /* TODO disconnect */ } - pty_setowner(svr_ses.authstate.pw, chansess->tty); + pty_setowner(ses.authstate.pw, chansess->tty); pty_change_window_size(chansess->master, chansess->termr, chansess->termc, chansess->termw, chansess->termh); @@ -683,7 +683,7 @@ static int ptycommand(struct Channel *channel, struct ChanSess *chansess) { /* write the utmp/wtmp login record - must be after changing the * terminal used for stdout with the dup2 above */ - li= login_alloc_entry(getpid(), svr_ses.authstate.username, + li= login_alloc_entry(getpid(), ses.authstate.username, ses.remotehost, chansess->tty); login_login(li); login_free_entry(li); @@ -695,10 +695,10 @@ static int ptycommand(struct Channel *channel, struct ChanSess *chansess) { /* don't show the motd if ~/.hushlogin exists */ /* 11 == strlen("/hushlogin\0") */ - len = strlen(svr_ses.authstate.pw->pw_dir) + 11; + len = strlen(ses.authstate.pw->pw_dir) + 11; hushpath = m_malloc(len); - snprintf(hushpath, len, "%s/hushlogin", svr_ses.authstate.pw->pw_dir); + snprintf(hushpath, len, "%s/hushlogin", ses.authstate.pw->pw_dir); if (stat(hushpath, &sb) < 0) { /* more than a screenful is stupid IMHO */ @@ -808,10 +808,10 @@ static void execchild(struct ChanSess *chansess) { /* We can only change uid/gid as root ... */ if (getuid() == 0) { - if ((setgid(svr_ses.authstate.pw->pw_gid) < 0) || - (initgroups(svr_ses.authstate.pw->pw_name, - svr_ses.authstate.pw->pw_gid) < 0) || - (setuid(svr_ses.authstate.pw->pw_uid) < 0)) { + if ((setgid(ses.authstate.pw->pw_gid) < 0) || + (initgroups(ses.authstate.pw->pw_name, + ses.authstate.pw->pw_gid) < 0) || + (setuid(ses.authstate.pw->pw_uid) < 0)) { dropbear_exit("error changing user"); } } else { @@ -822,29 +822,29 @@ static void execchild(struct ChanSess *chansess) { * usernames with the same uid, but differing groups, then the * differing groups won't be set (as with initgroups()). The solution * is for the sysadmin not to give out the UID twice */ - if (getuid() != svr_ses.authstate.pw->pw_uid) { + if (getuid() != ses.authstate.pw->pw_uid) { dropbear_exit("couldn't change user as non-root"); } } /* an empty shell should be interpreted as "/bin/sh" */ - if (svr_ses.authstate.pw->pw_shell[0] == '\0') { + if (ses.authstate.pw->pw_shell[0] == '\0') { usershell = "/bin/sh"; } else { - usershell = svr_ses.authstate.pw->pw_shell; + usershell = ses.authstate.pw->pw_shell; } /* set env vars */ - addnewvar("USER", svr_ses.authstate.pw->pw_name); - addnewvar("LOGNAME", svr_ses.authstate.pw->pw_name); - addnewvar("HOME", svr_ses.authstate.pw->pw_dir); + addnewvar("USER", ses.authstate.pw->pw_name); + addnewvar("LOGNAME", ses.authstate.pw->pw_name); + addnewvar("HOME", ses.authstate.pw->pw_dir); addnewvar("SHELL", usershell); if (chansess->term != NULL) { addnewvar("TERM", chansess->term); } /* change directory */ - if (chdir(svr_ses.authstate.pw->pw_dir) < 0) { + if (chdir(ses.authstate.pw->pw_dir) < 0) { dropbear_exit("error changing directory"); } diff --git a/svr-runopts.c b/svr-runopts.c index 1f5b0c6..c79208c 100644 --- a/svr-runopts.c +++ b/svr-runopts.c @@ -106,8 +106,8 @@ void svr_getopts(int argc, char ** argv) { opts.nolocaltcp = 0; opts.noremotetcp = 0; /* not yet - svr_opts.ipv4 = 1; - svr_opts.ipv6 = 1; + opts.ipv4 = 1; + opts.ipv6 = 1; */ #ifdef DO_MOTD svr_opts.domotd = 1; diff --git a/svr-service.c b/svr-service.c index 7b717bf..e823490 100644 --- a/svr-service.c +++ b/svr-service.c @@ -56,7 +56,7 @@ void recv_msg_service_request() { /* ssh-connection */ if (len == SSH_SERVICE_CONNECTION_LEN && (strncmp(SSH_SERVICE_CONNECTION, name, len) == 0)) { - if (ses.authdone != 1) { + if (ses.authstate.authdone != 1) { dropbear_exit("request for connection before auth"); } @@ -70,7 +70,6 @@ void recv_msg_service_request() { /* TODO this should be a MSG_DISCONNECT */ dropbear_exit("unrecognised SSH_MSG_SERVICE_REQUEST"); - TRACE(("leave recv_msg_service_request")); } diff --git a/svr-session.c b/svr-session.c index cb309fe..80c622a 100644 --- a/svr-session.c +++ b/svr-session.c @@ -79,7 +79,7 @@ void svr_session(int sock, int childpipe, char* remotehost) { /* Initialise server specific parts of the session */ svr_ses.childpipe = childpipe; - authinitialise(); + svr_authinitialise(); chaninitialise(svr_chantypes); svr_chansessinitialise(); @@ -90,10 +90,11 @@ void svr_session(int sock, int childpipe, char* remotehost) { ses.connecttimeout = timeout.tv_sec + AUTH_TIMEOUT; /* set up messages etc */ - session_remoteclosed = svr_remoteclosed; + ses.remoteclosed = svr_remoteclosed; /* packet handlers */ ses.packettypes = svr_packettypes; + ses.buf_match_algo = svr_buf_match_algo; /* We're ready to go now */ sessinitdone = 1; @@ -123,16 +124,16 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) { /* before session init */ snprintf(fmtbuf, sizeof(fmtbuf), "premature exit: %s", format); - } else if (svr_ses.authstate.authdone) { + } else if (ses.authstate.authdone) { /* user has authenticated */ snprintf(fmtbuf, sizeof(fmtbuf), "exit after auth (%s): %s", - svr_ses.authstate.printableuser, format); - } else if (svr_ses.authstate.printableuser) { + ses.authstate.printableuser, format); + } else if (ses.authstate.printableuser) { /* we have a potential user */ snprintf(fmtbuf, sizeof(fmtbuf), "exit before auth (user '%s', %d fails): %s", - svr_ses.authstate.printableuser, svr_ses.authstate.failcount, format); + ses.authstate.printableuser, ses.authstate.failcount, format); } else { /* before userauth */ snprintf(fmtbuf, sizeof(fmtbuf), -- cgit v1.2.3 From e1491b8ec67e0e24b93a7b2997172d57b23a933c Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 29 Jul 2004 02:19:03 +0000 Subject: set the isserver flag (oops) fix password auth for the server --HG-- extra : convert_revision : 234eb604aabaef9ed0dd496ff8db8ecc212ca18c --- cli-auth.c | 25 ++----------------------- cli-authpasswd.c | 5 +++-- cli-session.c | 3 ++- common-kex.c | 11 ++++++----- svr-auth.c | 7 ++++++- svr-session.c | 2 ++ 6 files changed, 21 insertions(+), 32 deletions(-) (limited to 'svr-session.c') diff --git a/cli-auth.c b/cli-auth.c index d222d7e..3759ff5 100644 --- a/cli-auth.c +++ b/cli-auth.c @@ -13,27 +13,6 @@ void cli_authinitialise() { } -void cli_get_user() { - - uid_t uid; - struct passwd *pw; - - TRACE(("enter cli_get_user")); - if (cli_opts.username != NULL) { - ses.authstate.username = cli_opts.username; - } else { - uid = getuid(); - - pw = getpwuid(uid); - if (pw == NULL || pw->pw_name == NULL) { - dropbear_exit("Couldn't find username for current user"); - } - - ses.authstate.username = m_strdup(pw->pw_name); - } - TRACE(("leave cli_get_user: %s", ses.authstate.username)); -} - /* Send a "none" auth request to get available methods */ void cli_auth_getmethods() { @@ -42,8 +21,8 @@ void cli_auth_getmethods() { CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST); - buf_putstring(ses.writepayload, ses.authstate.username, - strlen(ses.authstate.username)); + buf_putstring(ses.writepayload, cli_opts.username, + strlen(cli_opts.username)); buf_putstring(ses.writepayload, SSH_SERVICE_CONNECTION, SSH_SERVICE_CONNECTION_LEN); buf_putstring(ses.writepayload, "none", 4); /* 'none' method */ diff --git a/cli-authpasswd.c b/cli-authpasswd.c index 6185334..c04d240 100644 --- a/cli-authpasswd.c +++ b/cli-authpasswd.c @@ -3,6 +3,7 @@ #include "dbutil.h" #include "session.h" #include "ssh.h" +#include "runopts.h" int cli_auth_password() { @@ -14,8 +15,8 @@ int cli_auth_password() { buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST); - buf_putstring(ses.writepayload, ses.authstate.username, - strlen(ses.authstate.username)); + buf_putstring(ses.writepayload, cli_opts.username, + strlen(cli_opts.username)); buf_putstring(ses.writepayload, SSH_SERVICE_CONNECTION, SSH_SERVICE_CONNECTION_LEN); diff --git a/cli-session.c b/cli-session.c index 2ae2719..c999aed 100644 --- a/cli-session.c +++ b/cli-session.c @@ -83,6 +83,8 @@ static void cli_session_init() { /* packet handlers */ ses.packettypes = cli_packettypes; + + ses.isserver = 0; } /* This function drives the progress of the session - it initiates KEX, @@ -136,7 +138,6 @@ static void cli_sessionloop() { /* userauth code */ case SERVICE_AUTH_ACCEPT_RCVD: - cli_get_user(); cli_auth_getmethods(); cli_ses.state = USERAUTH_METHODS_SENT; TRACE(("leave cli_sessionloop: sent userauth methods req")); diff --git a/common-kex.c b/common-kex.c index 07b221b..49cbfa4 100644 --- a/common-kex.c +++ b/common-kex.c @@ -55,7 +55,7 @@ const unsigned char dh_p_val[] = { const int DH_G_VAL = 2; static void kexinitialise(); -static void gen_new_keys(); +void gen_new_keys(); #ifndef DISABLE_ZLIB static void gen_new_zstreams(); #endif @@ -253,7 +253,7 @@ static void hashkeys(unsigned char *out, int outlen, * taken into use after both sides have sent a newkeys message */ /* Originally from kex.c, generalized for cli/svr mode --mihnea */ -static void gen_new_keys() { +void gen_new_keys() { unsigned char C2S_IV[MAX_IV_LEN]; unsigned char C2S_key[MAX_KEY_LEN]; @@ -276,9 +276,6 @@ static void gen_new_keys() { sha1_process(&hs, ses.hash, SHA1_HASH_SIZE); m_burn(ses.hash, SHA1_HASH_SIZE); - hashkeys(C2S_IV, SHA1_HASH_SIZE, &hs, 'A'); - hashkeys(S2C_IV, SHA1_HASH_SIZE, &hs, 'B'); - if (IS_DROPBEAR_CLIENT) { trans_IV = C2S_IV; recv_IV = S2C_IV; @@ -299,6 +296,8 @@ static void gen_new_keys() { macrecvletter = 'E'; } + hashkeys(C2S_IV, SHA1_HASH_SIZE, &hs, 'A'); + hashkeys(S2C_IV, SHA1_HASH_SIZE, &hs, 'B'); hashkeys(C2S_key, C2S_keysize, &hs, 'C'); hashkeys(S2C_key, S2C_keysize, &hs, 'D'); @@ -580,6 +579,8 @@ void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them, sha1_process(&hs, buf_getptr(ses.kexhashbuf, ses.kexhashbuf->len), ses.kexhashbuf->len); sha1_done(&hs, ses.hash); + + buf_burn(ses.kexhashbuf); buf_free(ses.kexhashbuf); ses.kexhashbuf = NULL; diff --git a/svr-auth.c b/svr-auth.c index 0f0ef67..db1d6a4 100644 --- a/svr-auth.c +++ b/svr-auth.c @@ -58,7 +58,7 @@ static void authclear() { ses.authstate.authtypes |= AUTH_TYPE_PUBKEY; #endif #ifdef DROPBEAR_PASSWORD_AUTH - if (svr_opts.noauthpass) { + if (!svr_opts.noauthpass) { ses.authstate.authtypes |= AUTH_TYPE_PASSWORD; } #endif @@ -100,6 +100,7 @@ void recv_msg_userauth_request() { /* ignore packets if auth is already done */ if (ses.authstate.authdone == 1) { + TRACE(("leave recv_msg_userauth_request: authdone already")); return; } @@ -129,6 +130,7 @@ void recv_msg_userauth_request() { if (methodlen == AUTH_METHOD_NONE_LEN && strncmp(methodname, AUTH_METHOD_NONE, AUTH_METHOD_NONE_LEN) == 0) { + TRACE(("recv_msg_userauth_request: 'none' request")); send_msg_userauth_failure(0, 0); goto out; } @@ -305,6 +307,9 @@ void send_msg_userauth_failure(int partial, int incrfail) { buf_putbyte(ses.writepayload, partial ? 1 : 0); encrypt_packet(); + TRACE(("auth fail: methods %d, '%s'", ses.authstate.authtypes, + buf_getptr(typebuf, typebuf->len))); + if (incrfail) { usleep(300000); /* XXX improve this */ ses.authstate.failcount++; diff --git a/svr-session.c b/svr-session.c index 80c622a..c9aed49 100644 --- a/svr-session.c +++ b/svr-session.c @@ -96,6 +96,8 @@ void svr_session(int sock, int childpipe, char* remotehost) { ses.packettypes = svr_packettypes; ses.buf_match_algo = svr_buf_match_algo; + ses.isserver = 1; + /* We're ready to go now */ sessinitdone = 1; -- cgit v1.2.3 From 7cdad3c2006070097d65a2c4d3737c6eac0b63e6 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 3 Aug 2004 17:26:56 +0000 Subject: Pubkey auth is mostly there for the client. Something strange with remote hostkey verification though. --HG-- extra : convert_revision : 8635abe49e499e16d44a8ee79d474dc35257e9cc --- auth.h | 27 +++++++--- cli-auth.c | 19 ++++++-- cli-authpubkey.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ cli-service.c | 4 +- cli-session.c | 32 ++++++++---- dbutil.c | 2 - debug.h | 2 +- session.h | 10 +++- svr-session.c | 11 ++--- 9 files changed, 222 insertions(+), 31 deletions(-) create mode 100644 cli-authpubkey.c (limited to 'svr-session.c') diff --git a/auth.h b/auth.h index 106a1ad..061c8c4 100644 --- a/auth.h +++ b/auth.h @@ -30,24 +30,24 @@ void svr_authinitialise(); void cli_authinitialise(); -void svr_auth_password(); -void svr_auth_pubkey(); - -int cli_auth_password(); -int cli_auth_pubkey(); - /* Server functions */ void recv_msg_userauth_request(); void send_msg_userauth_failure(int partial, int incrfail); void send_msg_userauth_success(); +void svr_auth_password(); +void svr_auth_pubkey(); /* Client functions */ void recv_msg_userauth_failure(); void recv_msg_userauth_success(); +void recv_msg_userauth_pk_ok(); void cli_get_user(); void cli_auth_getmethods(); void cli_auth_try(); void recv_msg_userauth_banner(); +void cli_pubkeyfail(); +int cli_auth_password(); +int cli_auth_pubkey(); #define MAX_USERNAME_LEN 25 /* arbitrary for the moment */ @@ -63,6 +63,9 @@ void recv_msg_userauth_banner(); #define AUTH_METHOD_PASSWORD "password" #define AUTH_METHOD_PASSWORD_LEN 8 +/* For a 4096 bit DSS key, empirically determined to be 1590 bytes */ +#define MAX_PUBKEY_SIZE 1600 + /* This structure is shared between server and client - it contains * relatively little extraneous bits when used for the client rather than the * server */ @@ -83,4 +86,16 @@ struct AuthState { }; +struct PubkeyList; +/* A singly linked list of pubkeys */ +struct PubkeyList { + + sign_key *key; + int type; /* The type of key */ + struct PubkeyList *next; + /* filename? or the buffer? for encrypted keys, so we can later get + * the private key portion */ + +}; + #endif /* _AUTH_H_ */ diff --git a/cli-auth.c b/cli-auth.c index 549349e..98e2e99 100644 --- a/cli-auth.c +++ b/cli-auth.c @@ -7,7 +7,6 @@ #include "packet.h" #include "runopts.h" -#undef DROPBEAR_PUBKEY_AUTH void cli_authinitialise() { @@ -30,7 +29,6 @@ void cli_auth_getmethods() { buf_putstring(ses.writepayload, "none", 4); /* 'none' method */ encrypt_packet(); - cli_ses.state = USERAUTH_METHODS_SENT; TRACE(("leave cli_auth_getmethods")); } @@ -88,6 +86,20 @@ void recv_msg_userauth_failure() { TRACE(("<- MSG_USERAUTH_FAILURE")); TRACE(("enter recv_msg_userauth_failure")); + if (cli_ses.state != USERAUTH_REQ_SENT) { + /* Perhaps we should be more fatal? */ + TRACE(("But we didn't send a userauth request!!!!!!")); + return; + } + +#ifdef DROPBEAR_PUBKEY_AUTH + /* If it was a pubkey auth request, we should cross that key + * off the list. */ + if (cli_ses.lastauthtype == AUTH_TYPE_PUBKEY) { + cli_pubkeyfail(); + } +#endif + methods = buf_getstring(ses.payload, &methlen); partial = buf_getbyte(ses.payload); @@ -154,12 +166,14 @@ void cli_auth_try() { #ifdef DROPBEAR_PUBKEY_AUTH if (ses.authstate.authtypes & AUTH_TYPE_PUBKEY) { finished = cli_auth_pubkey(); + cli_ses.lastauthtype = AUTH_TYPE_PUBKEY; } #endif #ifdef DROPBEAR_PASSWORD_AUTH if (!finished && ses.authstate.authtypes & AUTH_TYPE_PASSWORD) { finished = cli_auth_password(); + cli_ses.lastauthtype = AUTH_TYPE_PASSWORD; } #endif @@ -167,6 +181,5 @@ void cli_auth_try() { dropbear_exit("No auth methods could be used."); } - cli_ses.state = USERAUTH_REQ_SENT; TRACE(("leave cli_auth_try")); } diff --git a/cli-authpubkey.c b/cli-authpubkey.c new file mode 100644 index 0000000..dd5a711 --- /dev/null +++ b/cli-authpubkey.c @@ -0,0 +1,146 @@ +#include "includes.h" +#include "buffer.h" +#include "dbutil.h" +#include "session.h" +#include "ssh.h" +#include "runopts.h" +#include "auth.h" + +static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign); + +/* Called when we receive a SSH_MSG_USERAUTH_FAILURE for a pubkey request. + * We use it to remove the key we tried from the list */ +void cli_pubkeyfail() { + + struct PubkeyList *keyitem; + + TRACE(("enter cli_pubkeyfail")); + /* Find the key we failed with, and remove it */ + for (keyitem = cli_ses.pubkeys; keyitem != NULL; keyitem = keyitem->next) { + if (keyitem->next == cli_ses.lastpubkey) { + keyitem->next = cli_ses.lastpubkey->next; + } + } + + sign_key_free(cli_ses.lastpubkey->key); /* It won't be used again */ + m_free(cli_ses.lastpubkey); + TRACE(("leave cli_pubkeyfail")); +} + +void recv_msg_userauth_pk_ok() { + + struct PubkeyList *keyitem; + buffer* keybuf; + char* algotype = NULL; + unsigned int algolen; + int keytype; + unsigned int remotelen; + + TRACE(("enter recv_msg_userauth_pk_ok")); + + algotype = buf_getstring(ses.payload, &algolen); + keytype = signkey_type_from_name(algotype, algolen); + m_free(algotype); + + keybuf = buf_new(MAX_PUBKEY_SIZE); + + remotelen = buf_getint(ses.payload); + + /* Iterate through our keys, find which one it was that matched, and + * send a real request with that key */ + for (keyitem = cli_ses.pubkeys; keyitem != NULL; keyitem = keyitem->next) { + + if (keyitem->type != keytype) { + /* Types differed */ + continue; + } + + /* Now we compare the contents of the key */ + keybuf->pos = keybuf->len = 0; + buf_put_pub_key(keybuf, keyitem->key, keytype); + + if (keybuf->len != remotelen) { + /* Lengths differed */ + continue; + } + + if (memcmp(keybuf->data, + buf_getptr(ses.payload, remotelen), remotelen) != 0) { + /* Data didn't match this key */ + continue; + } + + /* Success */ + break; + } + + if (keyitem != NULL) { + TRACE(("matching key")); + /* XXX TODO: if it's an encrypted key, here we ask for their + * password */ + send_msg_userauth_pubkey(keyitem->key, keytype, 1); + } else { + TRACE(("That was whacky. We got told that a key was valid, but it didn't match our list. Sounds like dodgy code on Dropbear's part")); + } + + TRACE(("leave recv_msg_userauth_pk_ok")); +} + +/* TODO: make it take an agent reference to use as well */ +static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign) { + + const char *algoname = NULL; + int algolen; + buffer* sigbuf = NULL; + + TRACE(("enter send_msg_userauth_pubkey")); + CHECKCLEARTOWRITE(); + + buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST); + + buf_putstring(ses.writepayload, cli_opts.username, + strlen(cli_opts.username)); + + buf_putstring(ses.writepayload, SSH_SERVICE_CONNECTION, + SSH_SERVICE_CONNECTION_LEN); + + buf_putstring(ses.writepayload, AUTH_METHOD_PUBKEY, + AUTH_METHOD_PUBKEY_LEN); + + buf_putbyte(ses.writepayload, realsign); + + algoname = signkey_name_from_type(type, &algolen); + + buf_putstring(ses.writepayload, algoname, algolen); + buf_put_pub_key(ses.writepayload, key, type); + + if (realsign) { + TRACE(("realsign")); + /* We put the signature as well - this contains string(session id), then + * the contents of the write payload to this point */ + sigbuf = buf_new(4 + SHA1_HASH_SIZE + ses.writepayload->len); + buf_putstring(sigbuf, ses.session_id, SHA1_HASH_SIZE); + buf_putbytes(sigbuf, ses.writepayload->data, ses.writepayload->len); + buf_put_sign(ses.writepayload, key, type, sigbuf->data, sigbuf->len); + buf_free(sigbuf); /* Nothing confidential in the buffer */ + } + + encrypt_packet(); + TRACE(("leave send_msg_userauth_pubkey")); +} + +int cli_auth_pubkey() { + + TRACE(("enter cli_auth_pubkey")); + + if (cli_ses.pubkeys != NULL) { + /* Send a trial request */ + send_msg_userauth_pubkey(cli_ses.pubkeys->key, + cli_ses.pubkeys->type, 0); + TRACE(("leave cli_auth_pubkey-success")); + return 1; + } else { + TRACE(("leave cli_auth_pubkey-failure")); + return 0; + } +} diff --git a/cli-service.c b/cli-service.c index 8ba06c6..8fbbad4 100644 --- a/cli-service.c +++ b/cli-service.c @@ -31,7 +31,7 @@ void recv_msg_service_accept() { servicename = buf_getstring(ses.payload, &len); /* ssh-userauth */ - if (cli_ses.state = SERVICE_AUTH_REQ_SENT + if (cli_ses.state == SERVICE_AUTH_REQ_SENT && len == SSH_SERVICE_USERAUTH_LEN && strncmp(SSH_SERVICE_USERAUTH, servicename, len) == 0) { @@ -42,7 +42,7 @@ void recv_msg_service_accept() { } /* ssh-connection */ - if (cli_ses.state = SERVICE_CONN_REQ_SENT + if (cli_ses.state == SERVICE_CONN_REQ_SENT && len == SSH_SERVICE_CONNECTION_LEN && strncmp(SSH_SERVICE_CONNECTION, servicename, len) == 0) { diff --git a/cli-session.c b/cli-session.c index e8c6ae6..987a79b 100644 --- a/cli-session.c +++ b/cli-session.c @@ -19,14 +19,18 @@ static void cli_finished(); struct clientsession cli_ses; /* GLOBAL */ +/* Sorted in decreasing frequency will be more efficient - data and window + * should be first */ static const packettype cli_packettypes[] = { /* TYPE, AUTHREQUIRED, FUNCTION */ - {SSH_MSG_KEXINIT, recv_msg_kexinit}, - {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_CHANNEL_DATA, recv_msg_channel_data}, {SSH_MSG_CHANNEL_WINDOW_ADJUST, recv_msg_channel_window_adjust}, + {SSH_MSG_USERAUTH_FAILURE, recv_msg_userauth_failure}, /* client */ + {SSH_MSG_USERAUTH_SUCCESS, recv_msg_userauth_success}, /* client */ + {SSH_MSG_KEXINIT, recv_msg_kexinit}, + {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}, @@ -34,9 +38,10 @@ static const packettype cli_packettypes[] = { {SSH_MSG_CHANNEL_CLOSE, recv_msg_channel_close}, {SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation}, {SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure}, - {SSH_MSG_USERAUTH_FAILURE, recv_msg_userauth_failure}, // client - {SSH_MSG_USERAUTH_SUCCESS, recv_msg_userauth_success}, // client - {SSH_MSG_USERAUTH_BANNER, recv_msg_userauth_banner}, // client + {SSH_MSG_USERAUTH_BANNER, recv_msg_userauth_banner}, /* client */ +#ifdef DROPBEAR_PUBKEY_AUTH + {SSH_MSG_USERAUTH_PK_OK, recv_msg_userauth_pk_ok}, /* client */ +#endif {0, 0} /* End */ }; @@ -145,28 +150,35 @@ static void cli_sessionloop() { /* userauth code */ case SERVICE_AUTH_ACCEPT_RCVD: cli_auth_getmethods(); - cli_ses.state = USERAUTH_METHODS_SENT; + cli_ses.state = USERAUTH_REQ_SENT; TRACE(("leave cli_sessionloop: sent userauth methods req")); return; case USERAUTH_FAIL_RCVD: cli_auth_try(); + cli_ses.state = USERAUTH_REQ_SENT; TRACE(("leave cli_sessionloop: cli_auth_try")); return; - /* case USERAUTH_SUCCESS_RCVD: send_msg_service_request(SSH_SERVICE_CONNECTION); cli_ses.state = SERVICE_CONN_REQ_SENT; TRACE(("leave cli_sessionloop: sent ssh-connection service req")); return; - */ + case SERVICE_CONN_ACCEPT_RCVD: + cli_send_chansess_request(); + TRACE(("leave cli_sessionloop: cli_send_chansess_request")); + cli_ses.state = SESSION_RUNNING; + return; + + /* case USERAUTH_SUCCESS_RCVD: cli_send_chansess_request(); TRACE(("leave cli_sessionloop: cli_send_chansess_request")); cli_ses.state = SESSION_RUNNING; return; + */ case SESSION_RUNNING: if (ses.chancount < 1) { diff --git a/dbutil.c b/dbutil.c index a89c3c8..1c0648c 100644 --- a/dbutil.c +++ b/dbutil.c @@ -103,7 +103,6 @@ void dropbear_log(int priority, const char* format, ...) { #ifdef DEBUG_TRACE void dropbear_trace(const char* format, ...) { -#if 0 va_list param; va_start(param, format); @@ -111,7 +110,6 @@ void dropbear_trace(const char* format, ...) { vfprintf(stderr, format, param); fprintf(stderr, "\n"); va_end(param); -#endif } #endif /* DEBUG_TRACE */ diff --git a/debug.h b/debug.h index d619a58..736690a 100644 --- a/debug.h +++ b/debug.h @@ -36,7 +36,7 @@ /* Define this to print trace statements - very verbose */ /* Caution: Don't use this in an unfriendly environment (ie unfirewalled), * since the printing does not sanitise strings etc */ -#define DEBUG_TRACE +//#define DEBUG_TRACE /* All functions writing to the cleartext payload buffer call * CHECKCLEARTOWRITE() before writing. This is only really useful if you're diff --git a/session.h b/session.h index 2323377..6d69508 100644 --- a/session.h +++ b/session.h @@ -194,7 +194,6 @@ typedef enum { SERVICE_AUTH_ACCEPT_RCVD, SERVICE_CONN_REQ_SENT, SERVICE_CONN_ACCEPT_RCVD, - USERAUTH_METHODS_SENT, USERAUTH_REQ_SENT, USERAUTH_FAIL_RCVD, USERAUTH_SUCCESS_RCVD, @@ -215,6 +214,15 @@ struct clientsession { int winchange; /* Set to 1 when a windowchange signal happens */ + struct PubkeyList *pubkeys; /* Keys to use for public-key auth */ + int lastauthtype; /* either AUTH_TYPE_PUBKEY or AUTH_TYPE_PASSWORD, + for the last type of auth we tried */ + struct PubkeyList *lastpubkey; +#if 0 + TODO + struct AgentkeyList *agentkeys; /* Keys to use for public-key auth */ +#endif + }; /* Global structs storing the state */ diff --git a/svr-session.c b/svr-session.c index c9aed49..e63ba32 100644 --- a/svr-session.c +++ b/svr-session.c @@ -46,14 +46,13 @@ static void svr_remoteclosed(); struct serversession svr_ses; /* GLOBAL */ static const packettype svr_packettypes[] = { - /* TYPE, AUTHREQUIRED, FUNCTION */ - {SSH_MSG_SERVICE_REQUEST, recv_msg_service_request}, // server - {SSH_MSG_USERAUTH_REQUEST, recv_msg_userauth_request}, //server - {SSH_MSG_KEXINIT, recv_msg_kexinit}, - {SSH_MSG_KEXDH_INIT, recv_msg_kexdh_init}, // server - {SSH_MSG_NEWKEYS, recv_msg_newkeys}, {SSH_MSG_CHANNEL_DATA, recv_msg_channel_data}, {SSH_MSG_CHANNEL_WINDOW_ADJUST, recv_msg_channel_window_adjust}, + {SSH_MSG_USERAUTH_REQUEST, recv_msg_userauth_request}, /* server */ + {SSH_MSG_SERVICE_REQUEST, recv_msg_service_request}, /* server */ + {SSH_MSG_KEXINIT, recv_msg_kexinit}, + {SSH_MSG_KEXDH_INIT, recv_msg_kexdh_init}, /* server */ + {SSH_MSG_NEWKEYS, recv_msg_newkeys}, {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}, -- cgit v1.2.3 From a712baa8e566bfd8403a3e2bfdf350a0dc50ea9f Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 10 Aug 2004 17:09:52 +0000 Subject: just checkpointing --HG-- extra : convert_revision : fbbf404290f3fea3dfa9f6f53eba9389057e9044 --- Makefile.in | 11 +- cli-authpubkey.c | 10 +- cli-runopts.c | 18 ++++ cli-session.c | 5 +- cli-tcpfwd.c | 34 ++++++ common-channel.c | 43 -------- dbutil.c | 173 +++++++++++++++++++++++------ dbutil.h | 6 +- listener.c | 42 +++++--- listener.h | 10 +- options.h | 3 + svr-agentfwd.c | 8 +- svr-chansession.c | 2 + svr-main.c | 82 +++++--------- svr-session.c | 6 +- svr-tcpfwd.c | 196 +++++++++++++++++++++++++++++++++ svr-x11fwd.c | 8 +- tcp-accept.c | 88 +++++++++++++++ tcp-accept.h | 19 ++++ tcp-connect.c | 75 +++++++++++++ tcp-connect.h | 35 ++++++ tcpfwd-direct.c | 159 --------------------------- tcpfwd-direct.h | 34 ------ tcpfwd-remote.c | 317 ------------------------------------------------------ tcpfwd-remote.h | 6 -- 25 files changed, 706 insertions(+), 684 deletions(-) create mode 100644 cli-tcpfwd.c create mode 100644 svr-tcpfwd.c create mode 100644 tcp-accept.c create mode 100644 tcp-accept.h create mode 100644 tcp-connect.c create mode 100644 tcp-connect.h delete mode 100644 tcpfwd-direct.c delete mode 100644 tcpfwd-direct.h delete mode 100644 tcpfwd-remote.c delete mode 100644 tcpfwd-remote.h (limited to 'svr-session.c') 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 @@ -47,6 +47,12 @@ static void printhelp() { "-T Don't allocate a pty\n" #ifdef DROPBEAR_PUBKEY_AUTH "-i (multiple allowed)\n" +#endif +#ifndef DISABLE_REMOTETCPFWD + "-L Local port forwarding\n" +#endif +#ifndef DISABLE_TCPFWD_DIRECT + "-R 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; 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); diff --git a/dbutil.c b/dbutil.c index dd68416..a377da2 100644 --- a/dbutil.c +++ b/dbutil.c @@ -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) { diff --git a/dbutil.h b/dbutil.h index 21a9955..ce0f311 100644 --- a/dbutil.h +++ b/dbutil.h @@ -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); diff --git a/listener.c b/listener.c index 7296a61..3022bd5 100644 --- a/listener.c +++ b/listener.c @@ -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); diff --git a/listener.h b/listener.h index bda24ff..c634ead 100644 --- a/listener.h +++ b/listener.h @@ -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, diff --git a/options.h b/options.h index d4abf09..99115db 100644 --- a/options.h +++ b/options.h @@ -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"); } diff --git a/svr-main.c b/svr-main.c index 312e47c..6a49626 100644 --- a/svr-main.c +++ b/svr-main.c @@ -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/tcp-connect.h b/tcp-connect.h new file mode 100644 index 0000000..40ce22b --- /dev/null +++ b/tcp-connect.h @@ -0,0 +1,35 @@ +/* + * Dropbear - a SSH2 server + * + * Copyright (c) 2002,2003 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ +#ifndef _TCPFWD_DIRECT_H_ +#define _TCPFWD_DIRECT_H_ +#ifndef DISABLE_TCFWD_DIRECT + +#include "includes.h" +#include "channel.h" + +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-direct.h b/tcpfwd-direct.h deleted file mode 100644 index 20cd278..0000000 --- a/tcpfwd-direct.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Dropbear - a SSH2 server - * - * Copyright (c) 2002,2003 Matt Johnston - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ -#ifndef _TCPFWD_DIRECT_H_ -#define _TCPFWD_DIRECT_H_ -#ifndef DISABLE_TCFWD_DIRECT - -#include "includes.h" -#include "channel.h" - -extern const struct ChanType chan_tcpdirect; - -#endif -#endif 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 */ -- cgit v1.2.3 From d7575f95f093f774145a3725a7c5a3e253962e7c Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 12 Aug 2004 14:56:22 +0000 Subject: cleaning up the pubkey defines --HG-- extra : convert_revision : 149ce7a9a9cc5fe670994d6789b40be49895c595 --- Makefile.in | 4 ++-- authpasswd.h | 33 --------------------------------- authpubkey.h | 33 --------------------------------- cli-auth.c | 10 +++++----- cli-authpasswd.c | 2 ++ cli-authpubkey.c | 2 ++ cli-runopts.c | 14 +++++++------- cli-session.c | 2 +- dbutil.c | 2 +- options.h | 12 ++++++------ runopts.h | 2 +- svr-auth.c | 10 ++++------ svr-authpasswd.c | 5 ++--- svr-authpubkey.c | 5 ++--- svr-runopts.c | 4 ++-- svr-session.c | 3 +-- 16 files changed, 38 insertions(+), 105 deletions(-) delete mode 100644 authpasswd.h delete mode 100644 authpubkey.h (limited to 'svr-session.c') diff --git a/Makefile.in b/Makefile.in index f70bca2..761a3c9 100644 --- a/Makefile.in +++ b/Makefile.in @@ -43,9 +43,9 @@ CONVERTOBJS=dropbearconvert.o keyimport.o SCPOBJS=scp.o progressmeter.o atomicio.o scpmisc.o 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 \ 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 runopts.h includes.h \ loginrec.h atomicio.h x11fwd.h agentfwd.h tcpfwd.h compat.h \ listener.h fake-rfc2553.h diff --git a/authpasswd.h b/authpasswd.h deleted file mode 100644 index 9738532..0000000 --- a/authpasswd.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Dropbear - a SSH2 server - * - * Copyright (c) 2002,2003 Matt Johnston - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ - -#ifndef _AUTH_PASSWD_ -#define _AUTH_PASSWD_ - -#ifdef DROPBEAR_PASSWORD_AUTH - -void passwordauth(); - -#endif /* DROPBEAR_PASSWORD_AUTH */ -#endif /* _AUTH_PASSWD_ */ diff --git a/authpubkey.h b/authpubkey.h deleted file mode 100644 index bf9549e..0000000 --- a/authpubkey.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Dropbear - a SSH2 server - * - * Copyright (c) 2002,2003 Matt Johnston - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ - -#ifndef _PUBKEY_AUTH_ -#define _PUBKEY_AUTH_ - -#ifdef DROPBEAR_PUBKEY_AUTH - -void pubkeyauth(); - -#endif /* DROPBEAR_PUBKEY_AUTH */ -#endif /* _PUBKEY_AUTH_ */ diff --git a/cli-auth.c b/cli-auth.c index 98e2e99..39a336e 100644 --- a/cli-auth.c +++ b/cli-auth.c @@ -92,7 +92,7 @@ void recv_msg_userauth_failure() { return; } -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH /* If it was a pubkey auth request, we should cross that key * off the list. */ if (cli_ses.lastauthtype == AUTH_TYPE_PUBKEY) { @@ -126,13 +126,13 @@ void recv_msg_userauth_failure() { for (i = 0; i <= methlen; i++) { if (methods[i] == '\0') { TRACE(("auth method '%s'", tok)); -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH if (strncmp(AUTH_METHOD_PUBKEY, tok, AUTH_METHOD_PUBKEY_LEN) == 0) { ses.authstate.authtypes |= AUTH_TYPE_PUBKEY; } #endif -#ifdef DROPBEAR_PASSWORD_AUTH +#ifdef ENABLE_CLI_PASSWORD_AUTH if (strncmp(AUTH_METHOD_PASSWORD, tok, AUTH_METHOD_PASSWORD_LEN) == 0) { ses.authstate.authtypes |= AUTH_TYPE_PASSWORD; @@ -163,14 +163,14 @@ void cli_auth_try() { CHECKCLEARTOWRITE(); /* XXX We hardcode that we try a pubkey first */ -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH if (ses.authstate.authtypes & AUTH_TYPE_PUBKEY) { finished = cli_auth_pubkey(); cli_ses.lastauthtype = AUTH_TYPE_PUBKEY; } #endif -#ifdef DROPBEAR_PASSWORD_AUTH +#ifdef ENABLE_CLI_PASSWORD_AUTH if (!finished && ses.authstate.authtypes & AUTH_TYPE_PASSWORD) { finished = cli_auth_password(); cli_ses.lastauthtype = AUTH_TYPE_PASSWORD; diff --git a/cli-authpasswd.c b/cli-authpasswd.c index c04d240..2a66a06 100644 --- a/cli-authpasswd.c +++ b/cli-authpasswd.c @@ -5,6 +5,7 @@ #include "ssh.h" #include "runopts.h" +#ifdef ENABLE_CLI_PASSWORD_AUTH int cli_auth_password() { char* password = NULL; @@ -35,3 +36,4 @@ int cli_auth_password() { return 1; /* Password auth can always be tried */ } +#endif diff --git a/cli-authpubkey.c b/cli-authpubkey.c index 33514ce..7e380e1 100644 --- a/cli-authpubkey.c +++ b/cli-authpubkey.c @@ -6,6 +6,7 @@ #include "runopts.h" #include "auth.h" +#ifdef ENABLE_CLI_PUBKEY_AUTH static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign); /* Called when we receive a SSH_MSG_USERAUTH_FAILURE for a pubkey request. @@ -158,3 +159,4 @@ int cli_auth_pubkey() { return 0; } } +#endif /* Pubkey auth */ diff --git a/cli-runopts.c b/cli-runopts.c index cef8312..e66f860 100644 --- a/cli-runopts.c +++ b/cli-runopts.c @@ -34,7 +34,7 @@ cli_runopts cli_opts; /* GLOBAL */ static void printhelp(); static void parsehostname(char* userhostarg); -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH static void loadidentityfile(const char* filename); #endif #ifdef ENABLE_CLI_ANYTCPFWD @@ -49,7 +49,7 @@ static void printhelp() { "-p \n" "-t Allocate a pty\n" "-T Don't allocate a pty\n" -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH "-i (multiple allowed)\n" #endif #ifdef ENABLE_CLI_LOCALTCPFWD @@ -67,7 +67,7 @@ void cli_getopts(int argc, char ** argv) { unsigned int i, j; char ** next = 0; unsigned int cmdlen; -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH int nextiskey = 0; /* A flag if the next argument is a keyfile */ #endif #ifdef ENABLE_CLI_LOCALTCPFWD @@ -85,7 +85,7 @@ void cli_getopts(int argc, char ** argv) { cli_opts.username = NULL; cli_opts.cmd = NULL; cli_opts.wantpty = 9; /* 9 means "it hasn't been touched", gets set later */ -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH cli_opts.pubkeys = NULL; #endif #ifdef ENABLE_CLI_LOCALTCPFWD @@ -103,7 +103,7 @@ void cli_getopts(int argc, char ** argv) { /* Iterate all the arguments */ for (i = 1; i < (unsigned int)argc; i++) { -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH if (nextiskey) { /* Load a hostkey since the previous argument was "-i" */ loadidentityfile(argv[i]); @@ -150,7 +150,7 @@ void cli_getopts(int argc, char ** argv) { case 'p': /* remoteport */ next = &cli_opts.remoteport; break; -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH case 'i': /* an identityfile */ nextiskey = 1; break; @@ -255,7 +255,7 @@ void cli_getopts(int argc, char ** argv) { } } -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH static void loadidentityfile(const char* filename) { struct PubkeyList * nextkey; diff --git a/cli-session.c b/cli-session.c index 20ce89a..07a5ba8 100644 --- a/cli-session.c +++ b/cli-session.c @@ -37,7 +37,7 @@ static const packettype cli_packettypes[] = { {SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation}, {SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure}, {SSH_MSG_USERAUTH_BANNER, recv_msg_userauth_banner}, /* client */ -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH {SSH_MSG_USERAUTH_PK_OK, recv_msg_userauth_pk_ok}, /* client */ #endif {0, 0} /* End */ diff --git a/dbutil.c b/dbutil.c index b5cd2b0..5436cbb 100644 --- a/dbutil.c +++ b/dbutil.c @@ -442,7 +442,7 @@ int buf_readfile(buffer* buf, const char* filename) { * authkeys file. * Will return DROPBEAR_SUCCESS if data is read, or DROPBEAR_FAILURE on EOF.*/ /* Only used for ~/.ssh/known_hosts and ~/.ssh/authorized_keys */ -#if defined(DROPBEAR_CLIENT) || defined(DROPBEAR_PUBKEY_AUTH) +#if defined(DROPBEAR_CLIENT) || defined(ENABLE_SVR_PUBKEY_AUTH) int buf_getline(buffer * line, FILE * authfile) { int c = EOF; diff --git a/options.h b/options.h index 9708f23..f0831b9 100644 --- a/options.h +++ b/options.h @@ -114,11 +114,11 @@ /* Authentication types to enable, at least one required. RFC Draft requires pubkey auth, and recommends password */ -#define DROPBEAR_SVR_PASSWORD_AUTH -#define DROPBEAR_SVR_PUBKEY_AUTH +#define ENABLE_SVR_PASSWORD_AUTH +#define ENABLE_SVR_PUBKEY_AUTH -#define DROPBEAR_CLI_PASSWORD_AUTH -#define DROPBEAR_CLI_PUBKEY_AUTH +#define ENABLE_CLI_PASSWORD_AUTH +#define ENABLE_CLI_PUBKEY_AUTH /* Random device to use - you must specify _one only_. * DEV_RANDOM is recommended on hosts with a good /dev/urandom, otherwise use @@ -241,7 +241,7 @@ #define DROPBEAR_COMP_ZLIB 1 /* Required for pubkey auth */ -#if defined(DROPBEAR_PUBKEY_AUTH) || defined(DROPBEAR_CLIENT) +#if defined(ENABLE_SVR_PUBKEY_AUTH) || defined(DROPBEAR_CLIENT) #define DROPBEAR_SIGNKEY_VERIFY #endif @@ -320,7 +320,7 @@ #define USING_LISTENERS #endif -#if defined(DROPBEAR_CLIENT) || defined(DROPBEAR_PUBKEY_AUTH) +#if defined(DROPBEAR_CLIENT) || defined(ENABLE_SVR_PUBKEY_AUTH) #define DROPBEAR_KEY_LINES /* ie we're using authorized_keys or known_hosts */ #endif diff --git a/runopts.h b/runopts.h index a160a29..4bd3287 100644 --- a/runopts.h +++ b/runopts.h @@ -91,7 +91,7 @@ typedef struct cli_runopts { char *cmd; int wantpty; -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH struct PubkeyList *pubkeys; /* Keys to use for public-key auth */ #endif #ifdef ENABLE_CLI_REMOTETCPFWD diff --git a/svr-auth.c b/svr-auth.c index db1d6a4..314171f 100644 --- a/svr-auth.c +++ b/svr-auth.c @@ -32,8 +32,6 @@ #include "ssh.h" #include "packet.h" #include "auth.h" -#include "authpasswd.h" -#include "authpubkey.h" #include "runopts.h" static void authclear(); @@ -54,10 +52,10 @@ void svr_authinitialise() { static void authclear() { memset(&ses.authstate, 0, sizeof(ses.authstate)); -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_SVR_PUBKEY_AUTH ses.authstate.authtypes |= AUTH_TYPE_PUBKEY; #endif -#ifdef DROPBEAR_PASSWORD_AUTH +#ifdef ENABLE_SVR_PASSWORD_AUTH if (!svr_opts.noauthpass) { ses.authstate.authtypes |= AUTH_TYPE_PASSWORD; } @@ -143,7 +141,7 @@ void recv_msg_userauth_request() { goto out; } -#ifdef DROPBEAR_PASSWORD_AUTH +#ifdef ENABLE_SVR_PASSWORD_AUTH if (!svr_opts.noauthpass && !(svr_opts.norootpass && ses.authstate.pw->pw_uid == 0) ) { /* user wants to try password auth */ @@ -156,7 +154,7 @@ void recv_msg_userauth_request() { } #endif -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_SVR_PUBKEY_AUTH /* user wants to try pubkey auth */ if (methodlen == AUTH_METHOD_PUBKEY_LEN && strncmp(methodname, AUTH_METHOD_PUBKEY, diff --git a/svr-authpasswd.c b/svr-authpasswd.c index 7249553..7c6c7b7 100644 --- a/svr-authpasswd.c +++ b/svr-authpasswd.c @@ -29,9 +29,8 @@ #include "buffer.h" #include "dbutil.h" #include "auth.h" -#include "authpasswd.h" -#ifdef DROPBEAR_PASSWORD_AUTH +#ifdef ENABLE_SVR_PASSWORD_AUTH /* Process a password auth request, sending success or failure messages as * appropriate */ @@ -105,4 +104,4 @@ void svr_auth_password() { } -#endif /* DROPBEAR_PASSWORD_AUTH */ +#endif diff --git a/svr-authpubkey.c b/svr-authpubkey.c index 53d5c06..9205078 100644 --- a/svr-authpubkey.c +++ b/svr-authpubkey.c @@ -30,12 +30,11 @@ #include "buffer.h" #include "signkey.h" #include "auth.h" -#include "authpubkey.h" #include "ssh.h" #include "packet.h" #include "algo.h" -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_SVR_PUBKEY_AUTH #define MIN_AUTHKEYS_LINE 10 /* "ssh-rsa AB" - short but doesn't matter */ #define MAX_AUTHKEYS_LINE 4200 /* max length of a line in authkeys */ @@ -336,4 +335,4 @@ static int checkfileperm(char * filename) { } -#endif /* DROPBEAR_PUBKEY_AUTH */ +#endif diff --git a/svr-runopts.c b/svr-runopts.c index 996c15c..9ecaf32 100644 --- a/svr-runopts.c +++ b/svr-runopts.c @@ -61,7 +61,7 @@ static void printhelp(const char * progname) { "-m Don't display the motd on login\n" #endif "-w Disallow root logins\n" -#ifdef DROPBEAR_PASSWORD_AUTH +#ifdef ENABLE_SVR_PASSWORD_AUTH "-s Disable password logins\n" "-g Disable password logins for root\n" #endif @@ -174,7 +174,7 @@ void svr_getopts(int argc, char ** argv) { case 'w': svr_opts.norootlogin = 1; break; -#ifdef DROPBEAR_PASSWORD_AUTH +#ifdef ENABLE_SVR_PASSWORD_AUTH case 's': svr_opts.noauthpass = 1; break; diff --git a/svr-session.c b/svr-session.c index d46adf4..a24765d 100644 --- a/svr-session.c +++ b/svr-session.c @@ -35,8 +35,7 @@ #include "channel.h" #include "chansession.h" #include "atomicio.h" -#include "tcp-accept.h" -#include "tcp-connect.h" +#include "tcpfwd.h" #include "service.h" #include "auth.h" #include "runopts.h" -- cgit v1.2.3 From 545ce7d8bfac96e4731bdf7379a65374e306ba09 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 22 Aug 2004 09:23:11 +0000 Subject: Fix for printing out things with inetd mode when we have DEBUG_TRACE compiled in but no -v: we don't want to print messages out since it goes to the socket (and over the wire - bad). --HG-- extra : convert_revision : f18a0cff74b01ad04543718db6aac12857851b3c --- svr-session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'svr-session.c') diff --git a/svr-session.c b/svr-session.c index a24765d..f37e81b 100644 --- a/svr-session.c +++ b/svr-session.c @@ -168,7 +168,7 @@ void svr_dropbear_log(int priority, const char* format, va_list param) { /* if we are using DEBUG_TRACE, we want to print to stderr even if * syslog is used, so it is included in error reports */ #ifdef DEBUG_TRACE - havetrace = 1; + havetrace = debug_trace; #endif if (!svr_opts.usingsyslog || havetrace) -- cgit v1.2.3 From e7677a5e8ddaa4787659a1a9e5320369c94564e4 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Wed, 22 Dec 2004 15:37:50 +0000 Subject: Rearrange preprocessor parts so that compilation with various options disabled works OK. --HG-- extra : convert_revision : cc92f744e34125062d052b757967e167f19d6db5 --- channel.h | 2 +- cli-tcpfwd.c | 13 +++++++++++-- common-channel.c | 5 ++--- session.h | 3 +-- svr-main.c | 4 ++++ svr-session.c | 6 ++++++ svr-tcpfwd.c | 2 +- 7 files changed, 26 insertions(+), 9 deletions(-) (limited to 'svr-session.c') diff --git a/channel.h b/channel.h index be6dd60..225fafb 100644 --- a/channel.h +++ b/channel.h @@ -122,7 +122,7 @@ void common_recv_msg_channel_data(struct Channel *channel, int fd, const struct ChanType clichansess; #endif -#ifdef USING_LISTENERS +#if defined(USING_LISTENERS) || defined(DROPBEAR_CLIENT) int send_msg_channel_open_init(int fd, const struct ChanType *type); void recv_msg_channel_open_confirmation(); void recv_msg_channel_open_failure(); diff --git a/cli-tcpfwd.c b/cli-tcpfwd.c index 4dbef01..b4d99e9 100644 --- a/cli-tcpfwd.c +++ b/cli-tcpfwd.c @@ -31,8 +31,7 @@ #include "session.h" #include "ssh.h" -static int cli_localtcp(unsigned int listenport, const char* remoteaddr, - unsigned int remoteport); +#ifdef ENABLE_CLI_REMOTETCPFWD static int newtcpforwarded(struct Channel * channel); const struct ChanType cli_chan_tcpremote = { @@ -43,6 +42,11 @@ const struct ChanType cli_chan_tcpremote = { NULL, NULL }; +#endif + +#ifdef ENABLE_CLI_LOCALTCPFWD +static int cli_localtcp(unsigned int listenport, const char* remoteaddr, + unsigned int remoteport); static const struct ChanType cli_chan_tcplocal = { 1, /* sepfds */ "direct-tcpip", @@ -51,7 +55,9 @@ static const struct ChanType cli_chan_tcplocal = { NULL, NULL }; +#endif +#ifdef ENABLE_CLI_LOCALTCPFWD void setup_localtcp() { int ret; @@ -102,7 +108,9 @@ static int cli_localtcp(unsigned int listenport, const char* remoteaddr, TRACE(("leave cli_localtcp: %d", ret)); return ret; } +#endif /* ENABLE_CLI_LOCALTCPFWD */ +#ifdef ENABLE_CLI_REMOTETCPFWD static void send_msg_global_request_remotetcp(int port) { TRACE(("enter send_msg_global_request_remotetcp")); @@ -191,3 +199,4 @@ out: TRACE(("leave newtcpdirect: err %d", err)); return err; } +#endif /* ENABLE_CLI_REMOTETCPFWD */ diff --git a/common-channel.c b/common-channel.c index d30528c..1d703a2 100644 --- a/common-channel.c +++ b/common-channel.c @@ -921,7 +921,7 @@ static void send_msg_channel_open_confirmation(struct Channel* channel, TRACE(("leave send_msg_channel_open_confirmation")); } -#ifdef USING_LISTENERS +#if defined(USING_LISTENERS) || defined(DROPBEAR_CLIENT) /* Create a new channel, and start the open request. This is intended * for X11, agent, tcp forwarding, and should be filled with channel-specific * options, with the calling function calling encrypt_packet() after @@ -1006,6 +1006,7 @@ void recv_msg_channel_open_failure() { removechannel(channel); } +#endif /* USING_LISTENERS */ /* close a stdout/stderr fd */ static void closeoutfd(struct Channel * channel, int fd) { @@ -1057,5 +1058,3 @@ static void closechanfd(struct Channel *channel, int fd, int how) { channel->errfd = FD_CLOSED; } } - -#endif /* USING_LISTENERS */ diff --git a/session.h b/session.h index 9142882..629dc65 100644 --- a/session.h +++ b/session.h @@ -158,12 +158,11 @@ struct sshsession { /* TCP forwarding - where manage listeners */ -#ifdef USING_LISTENERS struct Listener ** listeners; unsigned int listensize; + /* Whether to allow binding to privileged ports (<1024). This doesn't * really belong here, but nowhere else fits nicely */ -#endif int allowprivport; }; diff --git a/svr-main.c b/svr-main.c index ae05c0d..60ed212 100644 --- a/svr-main.c +++ b/svr-main.c @@ -33,8 +33,12 @@ 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); +#ifdef INETD_MODE static void main_inetd(); +#endif +#ifdef NON_INETD_MODE static void main_noinetd(); +#endif static void commonsetup(); static int childpipes[MAX_UNAUTH_CLIENTS]; diff --git a/svr-session.c b/svr-session.c index f37e81b..8dc8a44 100644 --- a/svr-session.c +++ b/svr-session.c @@ -52,19 +52,25 @@ static const packettype svr_packettypes[] = { {SSH_MSG_KEXINIT, recv_msg_kexinit}, {SSH_MSG_KEXDH_INIT, recv_msg_kexdh_init}, /* server */ {SSH_MSG_NEWKEYS, recv_msg_newkeys}, +#ifdef ENABLE_SVR_REMOTETCPFWD {SSH_MSG_GLOBAL_REQUEST, recv_msg_global_request_remotetcp}, +#endif {SSH_MSG_CHANNEL_REQUEST, recv_msg_channel_request}, {SSH_MSG_CHANNEL_OPEN, recv_msg_channel_open}, {SSH_MSG_CHANNEL_EOF, recv_msg_channel_eof}, {SSH_MSG_CHANNEL_CLOSE, recv_msg_channel_close}, +#ifdef USING_LISTENERS {SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation}, {SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure}, +#endif {0, 0} /* End */ }; static const struct ChanType *svr_chantypes[] = { &svrchansess, +#ifdef ENABLE_SVR_LOCALTCPFWD &svr_chan_tcpdirect, +#endif NULL /* Null termination is mandatory. */ }; diff --git a/svr-tcpfwd.c b/svr-tcpfwd.c index 7e2ee8b..68ceba5 100644 --- a/svr-tcpfwd.c +++ b/svr-tcpfwd.c @@ -33,7 +33,7 @@ #include "listener.h" #include "runopts.h" -#ifndef DISABLE_SVR_REMOTETCPFWD +#ifdef ENABLE_SVR_REMOTETCPFWD static void send_msg_request_success(); static void send_msg_request_failure(); -- cgit v1.2.3 From 9d4318370418b66796d370cb1fa05d49c28920b0 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 23 Dec 2004 17:00:15 +0000 Subject: Log the IP along with auth success/fail attempts --HG-- extra : convert_revision : 25eab43bd46e931fd4afecec49c22b9311062099 --- session.h | 5 ++++- svr-auth.c | 9 ++++++--- svr-authpam.c | 15 +++++++++------ svr-authpasswd.c | 10 ++++++---- svr-authpubkey.c | 12 ++++++------ svr-main.c | 7 +++---- svr-session.c | 4 +++- 7 files changed, 37 insertions(+), 25 deletions(-) (limited to 'svr-session.c') diff --git a/session.h b/session.h index 629dc65..1d5ebb4 100644 --- a/session.h +++ b/session.h @@ -48,7 +48,7 @@ void session_identification(); /* Server */ -void svr_session(int sock, int childpipe, char *remotehost); +void svr_session(int sock, int childpipe, char *remotehost, char *addrstring); void svr_dropbear_exit(int exitcode, const char* format, va_list param); void svr_dropbear_log(int priority, const char* format, va_list param); @@ -180,6 +180,9 @@ struct serversession { * svr-chansession.c for details */ struct exitinfo lastexit; + /* The numeric address they connected from, used for logging */ + char * addrstring; + }; typedef enum { diff --git a/svr-auth.c b/svr-auth.c index 425f94e..5eb3e27 100644 --- a/svr-auth.c +++ b/svr-auth.c @@ -205,7 +205,8 @@ static int checkusername(unsigned char *username, unsigned int userlen) { strcmp(username, ses.authstate.username) != 0) { /* the username needs resetting */ if (ses.authstate.username != NULL) { - dropbear_log(LOG_WARNING, "client trying multiple usernames"); + dropbear_log(LOG_WARNING, "client trying multiple usernames from %s", + svr_ses.addrstring); m_free(ses.authstate.username); } authclear(); @@ -218,7 +219,8 @@ static int checkusername(unsigned char *username, unsigned int userlen) { if (ses.authstate.pw == NULL) { TRACE(("leave checkusername: user '%s' doesn't exist", username)); dropbear_log(LOG_WARNING, - "login attempt for nonexistent user"); + "login attempt for nonexistent user from %s", + svr_ses.addrstring); send_msg_userauth_failure(0, 1); return DROPBEAR_FAILURE; } @@ -336,7 +338,8 @@ void send_msg_userauth_failure(int partial, int incrfail) { } else { userstr = ses.authstate.printableuser; } - dropbear_exit("Max auth tries reached - user %s", userstr); + dropbear_exit("Max auth tries reached - user '%s' from %s", + userstr, svr_ses.addrstring); } TRACE(("leave send_msg_userauth_failure")); diff --git a/svr-authpam.c b/svr-authpam.c index e3aa725..4937fa6 100644 --- a/svr-authpam.c +++ b/svr-authpam.c @@ -194,8 +194,9 @@ void svr_auth_pam() { dropbear_log(LOG_WARNING, "pam_authenticate() failed, rc=%d, %s\n", rc, pam_strerror(pamHandlep, rc)); dropbear_log(LOG_WARNING, - "bad PAM password attempt for '%s'", - ses.authstate.printableuser); + "bad PAM password attempt for '%s' from %s", + ses.authstate.printableuser, + svr_ses.addrstring); send_msg_userauth_failure(0, 1); goto cleanup; } @@ -204,15 +205,17 @@ void svr_auth_pam() { dropbear_log(LOG_WARNING, "pam_acct_mgmt() failed, rc=%d, %s\n", rc, pam_strerror(pamHandlep, rc)); dropbear_log(LOG_WARNING, - "bad PAM password attempt for '%s'", - ses.authstate.printableuser); + "bad PAM password attempt for '%s' from %s", + ses.authstate.printableuser, + svr_ses.addrstring); send_msg_userauth_failure(0, 1); goto cleanup; } /* successful authentication */ - dropbear_log(LOG_NOTICE, "PAM password auth succeeded for '%s'", - ses.authstate.printableuser); + dropbear_log(LOG_NOTICE, "PAM password auth succeeded for '%s' from %s", + ses.authstate.printableuser, + svr_ses.addrstring); send_msg_userauth_success(); cleanup: diff --git a/svr-authpasswd.c b/svr-authpasswd.c index 458deef..4348817 100644 --- a/svr-authpasswd.c +++ b/svr-authpasswd.c @@ -88,13 +88,15 @@ void svr_auth_password() { if (strcmp(testcrypt, passwdcrypt) == 0) { /* successful authentication */ dropbear_log(LOG_NOTICE, - "password auth succeeded for '%s'", - ses.authstate.printableuser); + "password auth succeeded for '%s' from %s", + ses.authstate.printableuser, + svr_ses.addrstring); send_msg_userauth_success(); } else { dropbear_log(LOG_WARNING, - "bad password attempt for '%s'", - ses.authstate.printableuser); + "bad password attempt for '%s' from %s", + ses.authstate.printableuser, + svr_ses.addrstring); send_msg_userauth_failure(0, 1); } diff --git a/svr-authpubkey.c b/svr-authpubkey.c index 14b5a78..5052b10 100644 --- a/svr-authpubkey.c +++ b/svr-authpubkey.c @@ -104,13 +104,13 @@ void svr_auth_pubkey() { if (buf_verify(ses.payload, key, buf_getptr(signbuf, signbuf->len), signbuf->len) == DROPBEAR_SUCCESS) { dropbear_log(LOG_NOTICE, - "pubkey auth succeeded for '%s' with key %s", - ses.authstate.printableuser, fp); + "pubkey auth succeeded for '%s' with key %s from %s", + ses.authstate.printableuser, fp, svr_ses.addrstring); send_msg_userauth_success(); } else { dropbear_log(LOG_WARNING, - "pubkey auth bad signature for '%s' with key %s", - ses.authstate.printableuser, fp); + "pubkey auth bad signature for '%s' with key %s from %s", + ses.authstate.printableuser, fp, svr_ses.addrstring); send_msg_userauth_failure(0, 1); } m_free(fp); @@ -165,8 +165,8 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen, /* check that we can use the algo */ if (have_algo(algo, algolen, sshhostkey) == DROPBEAR_FAILURE) { dropbear_log(LOG_WARNING, - "pubkey auth attempt with unknown algo for '%s'", - ses.authstate.printableuser); + "pubkey auth attempt with unknown algo for '%s' from %s", + ses.authstate.printableuser, svr_ses.addrstring); goto out; } diff --git a/svr-main.c b/svr-main.c index 60ed212..48e6042 100644 --- a/svr-main.c +++ b/svr-main.c @@ -94,7 +94,6 @@ static void main_inetd() { /* In case our inetd was lax in logging source addresses */ addrstring = getaddrstring(&remoteaddr, 1); dropbear_log(LOG_INFO, "Child connection from %s", addrstring); - m_free(addrstring); /* Don't check the return value - it may just fail since inetd has * already done setsid() after forking (xinetd on Darwin appears to do @@ -104,7 +103,7 @@ static void main_inetd() { /* Start service program * -1 is a dummy childpipe, just something we can close() without * mattering. */ - svr_session(0, -1, getaddrhostname(&remoteaddr)); + svr_session(0, -1, getaddrhostname(&remoteaddr), addrstring); /* notreached */ } @@ -264,7 +263,6 @@ void main_noinetd() { addrstring = getaddrstring(&remoteaddr, 1); dropbear_log(LOG_INFO, "Child connection from %s", addrstring); - m_free(addrstring); if (setsid() < 0) { dropbear_exit("setsid: %s", strerror(errno)); @@ -283,7 +281,8 @@ void main_noinetd() { /* start the session */ svr_session(childsock, childpipe[1], - getaddrhostname(&remoteaddr)); + getaddrhostname(&remoteaddr), + addrstring); /* don't return */ assert(0); } diff --git a/svr-session.c b/svr-session.c index 8dc8a44..408209d 100644 --- a/svr-session.c +++ b/svr-session.c @@ -74,7 +74,8 @@ static const struct ChanType *svr_chantypes[] = { NULL /* Null termination is mandatory. */ }; -void svr_session(int sock, int childpipe, char* remotehost) { +void svr_session(int sock, int childpipe, + char* remotehost, char *addrstring) { struct timeval timeout; @@ -83,6 +84,7 @@ void svr_session(int sock, int childpipe, char* remotehost) { /* Initialise server specific parts of the session */ svr_ses.childpipe = childpipe; + svr_ses.addrstring = addrstring; svr_authinitialise(); chaninitialise(svr_chantypes); svr_chansessinitialise(); -- cgit v1.2.3 From 1eb9209afef2046bbf74f8d2ffb0f357bdb992bb Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sat, 11 Feb 2006 15:15:37 +0000 Subject: Only read /dev/random once when the program starts rather than for every connection, to "conserve entropy". --HG-- extra : convert_revision : 21df240b71c0af8454725dec9abb428dd4bb97a2 --- cli-session.c | 8 +++----- random.c | 31 ++++++++++++++++++++++++++++--- random.h | 1 + svr-chansession.c | 2 +- svr-main.c | 4 +++- svr-session.c | 6 +++--- 6 files changed, 39 insertions(+), 13 deletions(-) (limited to 'svr-session.c') diff --git a/cli-session.c b/cli-session.c index 0e906e6..35510fa 100644 --- a/cli-session.c +++ b/cli-session.c @@ -76,12 +76,14 @@ static const struct ChanType *cli_chantypes[] = { void cli_session(int sock, char* remotehost) { + seedrandom(); + crypto_init(); + common_session_init(sock, remotehost); chaninitialise(cli_chantypes); - /* Set up cli_ses vars */ cli_session_init(); @@ -91,12 +93,8 @@ void cli_session(int sock, char* remotehost) { /* Exchange identification */ session_identification(); - seedrandom(); - send_msg_kexinit(); - /* XXX here we do stuff differently */ - session_loop(cli_sessionloop); /* Not reached */ diff --git a/random.c b/random.c index d58c8a8..cbbe016 100644 --- a/random.c +++ b/random.c @@ -30,8 +30,8 @@ static int donerandinit = 0; /* this is used to generate unique output from the same hashpool */ -static unsigned int counter = 0; -#define MAX_COUNTER 1000000/* the max value for the counter, so it won't loop */ +static uint32_t counter = 0; +#define MAX_COUNTER 1<<31 /* the max value for the counter, so it won't loop */ static unsigned char hashpool[SHA1_HASH_SIZE]; @@ -132,7 +132,8 @@ void seedrandom() { hash_state hs; - /* initialise so compilers will be happy about hashing it */ + /* initialise so that things won't warn about + * hashing an undefined buffer */ if (!donerandinit) { m_burn(hashpool, sizeof(hashpool)); } @@ -150,6 +151,30 @@ void seedrandom() { donerandinit = 1; } +/* hash the current random pool with some unique identifiers + * for this process and point-in-time. this is used to separate + * the random pools for fork()ed processes. */ +void reseedrandom() { + + pid_t pid; + struct timeval tv; + + if (!donerandinit) { + dropbear_exit("seedrandom not done"); + } + + pid = getpid(); + gettimeofday(&tv, NULL); + + hash_state hs; + unsigned char hash[SHA1_HASH_SIZE]; + sha1_init(&hs); + sha1_process(&hs, (void*)hashpool, sizeof(hashpool)); + sha1_process(&hs, (void*)&pid, sizeof(pid)); + sha1_process(&hs, (void*)&tv, sizeof(tv)); + sha1_done(&hs, hashpool); +} + /* return len bytes of pseudo-random data */ void genrandom(unsigned char* buf, unsigned int len) { diff --git a/random.h b/random.h index 5ec1f24..84a0a39 100644 --- a/random.h +++ b/random.h @@ -28,6 +28,7 @@ struct mp_int; void seedrandom(); +void reseedrandom(); void genrandom(unsigned char* buf, int len); void addrandom(unsigned char* buf, int len); void gen_random_mpint(mp_int *max, mp_int *rand); diff --git a/svr-chansession.c b/svr-chansession.c index 03ac40a..a645f69 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -833,7 +833,7 @@ static void execchild(struct ChanSess *chansess) { svr_opts.hostkey = NULL; /* overwrite the prng state */ - seedrandom(); + reseedrandom(); /* close file descriptors except stdin/stdout/stderr * Need to be sure FDs are closed here to avoid reading files as root */ diff --git a/svr-main.c b/svr-main.c index 4641e24..aef00f6 100644 --- a/svr-main.c +++ b/svr-main.c @@ -83,7 +83,7 @@ static void main_inetd() { int remoteaddrlen; char * addrstring = NULL; - /* Set up handlers, syslog */ + /* Set up handlers, syslog, seed random */ commonsetup(); remoteaddrlen = sizeof(remoteaddr); @@ -359,6 +359,8 @@ static void commonsetup() { /* Now we can setup the hostkeys - needs to be after logging is on, * otherwise we might end up blatting error messages to the socket */ loadhostkeys(); + + seedrandom(); } /* Set up listening sockets for all the requested ports */ diff --git a/svr-session.c b/svr-session.c index 408209d..70029f8 100644 --- a/svr-session.c +++ b/svr-session.c @@ -78,7 +78,9 @@ void svr_session(int sock, int childpipe, char* remotehost, char *addrstring) { struct timeval timeout; - + + reseedrandom(); + crypto_init(); common_session_init(sock, remotehost); @@ -110,8 +112,6 @@ void svr_session(int sock, int childpipe, /* exchange identification, version etc */ session_identification(); - seedrandom(); - /* start off with key exchange */ send_msg_kexinit(); -- cgit v1.2.3