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 --- chansession.h | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 chansession.h (limited to 'chansession.h') diff --git a/chansession.h b/chansession.h new file mode 100644 index 0000000..1e4ba9a --- /dev/null +++ b/chansession.h @@ -0,0 +1,89 @@ +/* + * 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 _CHANSESSION_H_ +#define _CHANSESSION_H_ + +#include "loginrec.h" +#include "channel.h" + +struct ChanSess { + + unsigned char * cmd; /* command to exec */ + pid_t pid; /* child process pid */ + + /* pty details */ + int master; /* the master terminal fd*/ + int slave; + unsigned char * tty; + unsigned char * term; + unsigned int termw, termh, termc, termr; /* width, height, col, rows */ + + /* exit details */ + int exited; + int exitstatus; + int exitsignal; + unsigned char exitcore; + +#ifndef DISABLE_X11FWD + int x11fd; /* set to -1 to indicate forwarding not established */ + int x11port; + char * x11authprot; + char * x11authcookie; + unsigned int x11screennum; + unsigned char x11singleconn; +#endif + +#ifndef DISABLE_AGENTFWD + int agentfd; + char * agentfile; + char * agentdir; +#endif +}; + +struct ChildPid { + pid_t pid; + struct ChanSess * chansess; +}; + + +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); + + +struct SigMap { + int signal; + char* name; +}; + +extern const struct SigMap signames[]; + +#endif /* _CHANSESSION_H_ */ -- 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 'chansession.h') 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 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 'chansession.h') 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 051b7454f80a52d2b0bea2e34562949a3bc70fe4 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 1 Aug 2004 08:54:01 +0000 Subject: - Added terminal mode handling etc for the client, and window change - Refactored the terminal-mode handling for the server - Improved session closing for the client --HG-- extra : convert_revision : 9d19b4f22c39798af5f3f24c2022f8caec4919e8 --- chansession.h | 4 +- cli-auth.c | 2 + cli-chansession.c | 125 +++++++++++++++++++++++++++++----- cli-main.c | 7 +- cli-runopts.c | 3 +- cli-session.c | 29 ++++++++ dbutil.c | 2 + options.h | 2 +- runopts.h | 1 + session.h | 1 + signkey.c | 1 - svr-chansession.c | 196 ++++++++++++++++++++++++++++-------------------------- 12 files changed, 255 insertions(+), 118 deletions(-) (limited to 'chansession.h') diff --git a/chansession.h b/chansession.h index 7879791..0930d9d 100644 --- a/chansession.h +++ b/chansession.h @@ -39,7 +39,6 @@ struct ChanSess { int slave; unsigned char * tty; unsigned char * term; - unsigned int termw, termh, termc, termr; /* width, height, col, rows */ /* exit details */ int exited; @@ -76,6 +75,9 @@ void send_msg_chansess_exitsignal(struct Channel * channel, struct ChanSess * chansess); void addnewvar(const char* param, const char* var); +void cli_send_chansess_request(); +void cli_tty_cleanup(); + void svr_chansessinitialise(); extern const struct ChanType svrchansess; diff --git a/cli-auth.c b/cli-auth.c index 37e7814..e081587 100644 --- a/cli-auth.c +++ b/cli-auth.c @@ -7,6 +7,8 @@ #include "packet.h" #include "runopts.h" +#undef DROPBEAR_PUBKEY_AUTH + void cli_authinitialise() { memset(&ses.authstate, 0, sizeof(ses.authstate)); diff --git a/cli-chansession.c b/cli-chansession.c index 852bffa..be6fc14 100644 --- a/cli-chansession.c +++ b/cli-chansession.c @@ -6,6 +6,7 @@ #include "channel.h" #include "ssh.h" #include "runopts.h" +#include "termcodes.h" static void cli_closechansess(struct Channel *channel); static int cli_initchansess(struct Channel *channel); @@ -16,7 +17,7 @@ static void send_chansess_pty_req(struct Channel *channel); static void send_chansess_shell_req(struct Channel *channel); static void cli_tty_setup(); -static void cli_tty_cleanup(); +void cli_tty_cleanup(); static const struct ChanType clichansess = { 0, /* sepfds */ @@ -50,7 +51,6 @@ static void start_channel_request(struct Channel *channel, } - /* Taken from OpenSSH's sshtty.c: * RCSID("OpenBSD: sshtty.c,v 1.5 2003/09/19 17:43:35 markus Exp "); */ static void cli_tty_setup() { @@ -91,12 +91,13 @@ static void cli_tty_setup() { TRACE(("leave cli_tty_setup")); } -static void cli_tty_cleanup() { +void cli_tty_cleanup() { TRACE(("enter cli_tty_cleanup")); if (cli_ses.tty_raw_mode == 0) { TRACE(("leave cli_tty_cleanup: not in raw mode")); + return; } if (tcsetattr(STDIN_FILENO, TCSADRAIN, &cli_ses.saved_tio) == -1) { @@ -108,30 +109,123 @@ static void cli_tty_cleanup() { TRACE(("leave cli_tty_cleanup")); } +static void put_termcodes() { + + TRACE(("enter put_termcodes")); + + struct termios tio; + unsigned int sshcode; + const struct TermCode *termcode; + unsigned int value; + unsigned int mapcode; + + unsigned int bufpos1, bufpos2; + + if (tcgetattr(STDIN_FILENO, &tio) == -1) { + dropbear_log(LOG_WARNING, "Failed reading termmodes"); + buf_putint(ses.writepayload, 1); /* Just the terminator */ + buf_putbyte(ses.writepayload, 0); /* TTY_OP_END */ + return; + } + + bufpos1 = ses.writepayload->pos; + buf_putint(ses.writepayload, 0); /* A placeholder for the final length */ + + /* As with Dropbear server, we ignore baud rates for now */ + for (sshcode = 1; sshcode < MAX_TERMCODE; sshcode++) { + + termcode = &termcodes[sshcode]; + mapcode = termcode->mapcode; + + switch (termcode->type) { + + case TERMCODE_NONE: + continue; + + case TERMCODE_CONTROLCHAR: + value = tio.c_cc[mapcode]; + break; + + case TERMCODE_INPUT: + value = tio.c_iflag & mapcode; + break; + + case TERMCODE_OUTPUT: + value = tio.c_oflag & mapcode; + break; + + case TERMCODE_LOCAL: + value = tio.c_lflag & mapcode; + break; + + case TERMCODE_CONTROL: + value = tio.c_cflag & mapcode; + break; + + default: + continue; + + } + + /* If we reach here, we have something to say */ + buf_putbyte(ses.writepayload, sshcode); + buf_putint(ses.writepayload, value); + } + + buf_putbyte(ses.writepayload, 0); /* THE END, aka TTY_OP_END */ + + /* Put the string length at the start of the buffer */ + bufpos2 = ses.writepayload->pos; + + buf_setpos(ses.writepayload, bufpos1); /* Jump back */ + buf_putint(ses.writepayload, bufpos2 - bufpos1); /* len(termcodes) */ + buf_setpos(ses.writepayload, bufpos2); /* Back where we were */ + + TRACE(("leave put_termcodes")); +} + +static void put_winsize() { + + struct winsize ws; + + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0) { + /* Some sane defaults */ + ws.ws_row = 25; + ws.ws_col = 80; + ws.ws_xpixel = 0; + ws.ws_ypixel = 0; + } + + buf_putint(ses.writepayload, ws.ws_col); /* Cols */ + buf_putint(ses.writepayload, ws.ws_row); /* Rows */ + buf_putint(ses.writepayload, ws.ws_xpixel); /* Width */ + buf_putint(ses.writepayload, ws.ws_ypixel); /* Height */ + +} + static void send_chansess_pty_req(struct Channel *channel) { - unsigned char* termmodes = "\0"; unsigned char* term = NULL; - int termc = 80, termr = 25, termw = 0, termh = 0; /* XXX TODO matt */ TRACE(("enter send_chansess_pty_req")); + start_channel_request(channel, "pty-req"); + /* Don't want replies */ + buf_putbyte(ses.writepayload, 0); + + /* Get the terminal */ term = getenv("TERM"); if (term == NULL) { - term = "vt100"; + term = "vt100"; /* Seems a safe default */ } - - /* XXX TODO */ - buf_putbyte(ses.writepayload, 0); /* Don't want replies */ buf_putstring(ses.writepayload, term, strlen(term)); - buf_putint(ses.writepayload, termc); /* Cols */ - buf_putint(ses.writepayload, termr); /* Rows */ - buf_putint(ses.writepayload, termw); /* Width */ - buf_putint(ses.writepayload, termh); /* Height */ - buf_putstring(ses.writepayload, termmodes, 1); /* XXX TODO */ - //m_free(termmodes); + /* Window size */ + put_winsize(); + + /* Terminal mode encoding */ + put_termcodes(); encrypt_packet(); TRACE(("leave send_chansess_pty_req")); @@ -171,7 +265,6 @@ static int cli_initchansess(struct Channel *channel) { send_chansess_pty_req(channel); } - cli_opts.cmd = "df"; send_chansess_shell_req(channel); if (cli_opts.wantpty) { diff --git a/cli-main.c b/cli-main.c index c5d5b3e..106cc64 100644 --- a/cli-main.c +++ b/cli-main.c @@ -61,9 +61,12 @@ static void cli_dropbear_exit(int exitcode, const char* format, va_list param) { cli_opts.remoteport, format); } + /* Do the cleanup first, since then the terminal will be reset */ + cli_session_cleanup(); + common_session_cleanup(); + _dropbear_log(LOG_INFO, fmtbuf, param); - common_session_cleanup(); exit(exitcode); } @@ -73,6 +76,6 @@ static void cli_dropbear_log(int priority, const char* format, va_list param) { vsnprintf(printbuf, sizeof(printbuf), format, param); - fprintf(stderr, "Dropbear: %s\n", printbuf); + fprintf(stderr, "%s: %s\n", cli_opts.progname, printbuf); } diff --git a/cli-runopts.c b/cli-runopts.c index 137e0da..2811ef3 100644 --- a/cli-runopts.c +++ b/cli-runopts.c @@ -55,11 +55,12 @@ void cli_getopts(int argc, char ** argv) { char* userhostarg = NULL; /* see printhelp() for options */ + cli_opts.progname = argv[0]; cli_opts.remotehost = NULL; cli_opts.remoteport = NULL; cli_opts.username = NULL; cli_opts.cmd = NULL; - cli_opts.wantpty = 0; + cli_opts.wantpty = 1; opts.nolocaltcp = 0; opts.noremotetcp = 0; /* not yet diff --git a/cli-session.c b/cli-session.c index 258d5c4..42770f5 100644 --- a/cli-session.c +++ b/cli-session.c @@ -9,10 +9,13 @@ #include "channel.h" #include "random.h" #include "service.h" +#include "runopts.h" +#include "chansession.h" static void cli_remoteclosed(); static void cli_sessionloop(); static void cli_session_init(); +static void cli_finished(); struct clientsession cli_ses; /* GLOBAL */ @@ -163,6 +166,12 @@ static void cli_sessionloop() { cli_ses.state = SESSION_RUNNING; return; + case SESSION_RUNNING: + if (ses.chancount < 1) { + cli_finished(); + } + return; + /* XXX more here needed */ @@ -174,6 +183,26 @@ static void cli_sessionloop() { } +void cli_session_cleanup() { + + if (!sessinitdone) { + return; + } + cli_tty_cleanup(); + +} + +static void cli_finished() { + + cli_session_cleanup(); + common_session_cleanup(); + fprintf(stderr, "Connection to %s@%s:%s closed.\n", cli_opts.username, + cli_opts.remotehost, cli_opts.remoteport); + exit(EXIT_SUCCESS); +} + + + /* called when the remote side closes the connection */ static void cli_remoteclosed() { diff --git a/dbutil.c b/dbutil.c index 1c0648c..a89c3c8 100644 --- a/dbutil.c +++ b/dbutil.c @@ -103,6 +103,7 @@ 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); @@ -110,6 +111,7 @@ void dropbear_trace(const char* format, ...) { vfprintf(stderr, format, param); fprintf(stderr, "\n"); va_end(param); +#endif } #endif /* DEBUG_TRACE */ diff --git a/options.h b/options.h index ee0ee60..1ab16c7 100644 --- a/options.h +++ b/options.h @@ -111,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 diff --git a/runopts.h b/runopts.h index b2fc149..26d15ef 100644 --- a/runopts.h +++ b/runopts.h @@ -79,6 +79,7 @@ void svr_getopts(int argc, char ** argv); /* Uncompleted XXX matt */ typedef struct cli_runopts { + char *progname; char *remotehost; char *remoteport; diff --git a/session.h b/session.h index 1000c5e..1ce944a 100644 --- a/session.h +++ b/session.h @@ -55,6 +55,7 @@ void svr_dropbear_log(int priority, const char* format, va_list param); 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); +void cli_session_cleanup(); struct key_context { diff --git a/signkey.c b/signkey.c index faf3739..2fd301f 100644 --- a/signkey.c +++ b/signkey.c @@ -53,7 +53,6 @@ int buf_get_pub_key(buffer *buf, sign_key *key, int *type) { unsigned int len; TRACE(("enter buf_get_pub_key")); - printhex(buf_getptr(buf, 0x99), 0x99); ident = buf_getstring(buf, &len); diff --git a/svr-chansession.c b/svr-chansession.c index 50e0a5e..9639961 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -56,6 +56,7 @@ static void chansessionrequest(struct Channel *channel); static void send_exitsignalstatus(struct Channel *channel); static int sesscheckclose(struct Channel *channel); +static void get_termmodes(struct ChanSess *chansess); /* required to clear environment */ @@ -192,10 +193,6 @@ static int newchansess(struct Channel *channel) { chansess->slave = -1; chansess->tty = NULL; chansess->term = NULL; - chansess->termw = 0; - chansess->termh = 0; - chansess->termc = 0; - chansess->termr = 0; chansess->exited = 0; @@ -376,22 +373,111 @@ static int sessionsignal(struct ChanSess *chansess) { * client. Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ static int sessionwinchange(struct ChanSess *chansess) { + int termc, termr, termw, termh; + if (chansess->master < 0) { /* haven't got a pty yet */ return DROPBEAR_FAILURE; } - chansess->termc = buf_getint(ses.payload); - chansess->termr = buf_getint(ses.payload); - chansess->termw = buf_getint(ses.payload); - chansess->termh = buf_getint(ses.payload); + termc = buf_getint(ses.payload); + termr = buf_getint(ses.payload); + termw = buf_getint(ses.payload); + termh = buf_getint(ses.payload); - pty_change_window_size(chansess->master, chansess->termr, chansess->termc, - chansess->termw, chansess->termh); + pty_change_window_size(chansess->master, termr, termc, termw, termh); return DROPBEAR_FAILURE; } +static void get_termmodes(struct ChanSess *chansess) { + + struct termios termio; + unsigned char opcode; + unsigned int value; + const struct TermCode * termcode; + unsigned int len; + + TRACE(("enter get_termmodes")); + + /* Term modes */ + /* We'll ignore errors and continue if we can't set modes. + * We're ignoring baud rates since they seem evil */ + if (tcgetattr(chansess->master, &termio) == -1) { + return; + } + + len = buf_getint(ses.payload); + if (len != ses.payload->len - ses.payload->pos) { + dropbear_exit("bad term mode string"); + } + + if (len == 0) { + TRACE(("leave get_termmodes: empty terminal modes string")); + } + + while (((opcode = buf_getbyte(ses.payload)) != 0x00) && opcode <= 159) { + + /* must be before checking type, so that value is consumed even if + * we don't use it */ + value = buf_getint(ses.payload); + + /* handle types of code */ + if (opcode > MAX_TERMCODE) { + continue; + } + termcode = &termcodes[(unsigned int)opcode]; + + + switch (termcode->type) { + + case TERMCODE_NONE: + break; + + case TERMCODE_CONTROLCHAR: + termio.c_cc[termcode->mapcode] = value; + break; + + case TERMCODE_INPUT: + if (value) { + termio.c_iflag |= termcode->mapcode; + } else { + termio.c_iflag &= ~(termcode->mapcode); + } + break; + + case TERMCODE_OUTPUT: + if (value) { + termio.c_oflag |= termcode->mapcode; + } else { + termio.c_oflag &= ~(termcode->mapcode); + } + break; + + case TERMCODE_LOCAL: + if (value) { + termio.c_lflag |= termcode->mapcode; + } else { + termio.c_lflag &= ~(termcode->mapcode); + } + break; + + case TERMCODE_CONTROL: + if (value) { + termio.c_cflag |= termcode->mapcode; + } else { + termio.c_cflag &= ~(termcode->mapcode); + } + break; + + } + } + if (tcsetattr(chansess->master, TCSANOW, &termio) < 0) { + dropbear_log(LOG_INFO, "error setting terminal attributes"); + } + TRACE(("leave get_termmodes")); +} + /* Set up a session pty which will be used to execute the shell or program. * The pty is allocated now, and kept for when the shell/program executes. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ @@ -399,7 +485,6 @@ static int sessionpty(struct ChanSess * chansess) { unsigned int termlen; unsigned char namebuf[65]; - struct termios termio; TRACE(("enter sessionpty")); chansess->term = buf_getstring(ses.payload, &termlen); @@ -408,10 +493,6 @@ static int sessionpty(struct ChanSess * chansess) { TRACE(("leave sessionpty: term len too long")); return DROPBEAR_FAILURE; } - chansess->termc = buf_getint(ses.payload); - chansess->termr = buf_getint(ses.payload); - chansess->termw = buf_getint(ses.payload); - chansess->termh = buf_getint(ses.payload); /* allocate the pty */ assert(chansess->master == -1); /* haven't already got one */ @@ -426,89 +507,12 @@ static int sessionpty(struct ChanSess * chansess) { } pty_setowner(ses.authstate.pw, chansess->tty); - pty_change_window_size(chansess->master, chansess->termr, chansess->termc, - chansess->termw, chansess->termh); - /* Term modes */ - /* We'll ignore errors and continue if we can't set modes. - * We're ignoring baud rates since they seem evil */ - if (tcgetattr(chansess->master, &termio) == 0) { - unsigned char opcode; - unsigned int value; - const struct TermCode * termcode; - unsigned int len; - - len = buf_getint(ses.payload); - if (len != ses.payload->len - ses.payload->pos) { - dropbear_exit("bad term mode string"); - } - - if (len == 0) { - TRACE(("empty terminal modes string")); - return DROPBEAR_SUCCESS; - } - - while (((opcode = buf_getbyte(ses.payload)) != 0x00) && - opcode <= 159) { + /* Set up the rows/col counts */ + sessionwinchange(chansess); - /* must be before checking type, so that value is consumed even if - * we don't use it */ - value = buf_getint(ses.payload); - - /* handle types of code */ - if (opcode > MAX_TERMCODE) { - continue; - } - termcode = &termcodes[(unsigned int)opcode]; - - - switch (termcode->type) { - - case TERMCODE_NONE: - break; - - case TERMCODE_CONTROLCHAR: - termio.c_cc[termcode->mapcode] = value; - break; - - case TERMCODE_INPUT: - if (value) { - termio.c_iflag |= termcode->mapcode; - } else { - termio.c_iflag &= ~(termcode->mapcode); - } - break; - - case TERMCODE_OUTPUT: - if (value) { - termio.c_oflag |= termcode->mapcode; - } else { - termio.c_oflag &= ~(termcode->mapcode); - } - break; - - case TERMCODE_LOCAL: - if (value) { - termio.c_lflag |= termcode->mapcode; - } else { - termio.c_lflag &= ~(termcode->mapcode); - } - break; - - case TERMCODE_CONTROL: - if (value) { - termio.c_cflag |= termcode->mapcode; - } else { - termio.c_cflag &= ~(termcode->mapcode); - } - break; - - } - } - if (tcsetattr(chansess->master, TCSANOW, &termio) < 0) { - dropbear_log(LOG_INFO, "error setting terminal attributes"); - } - } + /* Read the terminal modes */ + get_termmodes(chansess); TRACE(("leave sessionpty")); return DROPBEAR_SUCCESS; -- cgit v1.2.3 From 55c9b4564937d6f3348896d010bde2bb3711a1fd Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 1 Aug 2004 09:41:37 +0000 Subject: added window-size change handling --HG-- extra : convert_revision : 93a39c7b76f478035046b9c6f88af54612cce36f --- chansession.h | 1 + cli-chansession.c | 32 +++++++++++++++++++++++++++++++- cli-session.c | 5 +++++ common-session.c | 5 +++++ session.h | 2 ++ 5 files changed, 44 insertions(+), 1 deletion(-) (limited to 'chansession.h') diff --git a/chansession.h b/chansession.h index 0930d9d..3fb1901 100644 --- a/chansession.h +++ b/chansession.h @@ -77,6 +77,7 @@ void addnewvar(const char* param, const char* var); void cli_send_chansess_request(); void cli_tty_cleanup(); +void cli_chansess_winchange(); void svr_chansessinitialise(); extern const struct ChanType svrchansess; diff --git a/cli-chansession.c b/cli-chansession.c index be6fc14..e34c6fd 100644 --- a/cli-chansession.c +++ b/cli-chansession.c @@ -203,6 +203,32 @@ static void put_winsize() { } +static void sigwinch_handler(int dummy) { + + cli_ses.winchange = 1; + +} + +void cli_chansess_winchange() { + + unsigned int i; + struct Channel *channel = NULL; + + for (i = 0; i < ses.chansize; i++) { + channel = ses.channels[i]; + if (channel != NULL && channel->type == &clichansess) { + CHECKCLEARTOWRITE(); + buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST); + buf_putint(ses.writepayload, channel->remotechan); + buf_putstring(ses.writepayload, "window-change", 13); + buf_putbyte(ses.writepayload, 0); /* FALSE says the spec */ + put_winsize(); + encrypt_packet(); + } + } + cli_ses.winchange = 0; +} + static void send_chansess_pty_req(struct Channel *channel) { unsigned char* term = NULL; @@ -228,6 +254,11 @@ static void send_chansess_pty_req(struct Channel *channel) { put_termcodes(); encrypt_packet(); + + /* Set up a window-change handler */ + if (signal(SIGWINCH, sigwinch_handler) == SIG_ERR) { + dropbear_exit("signal error"); + } TRACE(("leave send_chansess_pty_req")); } @@ -275,7 +306,6 @@ static int cli_initchansess(struct Channel *channel) { } - void cli_send_chansess_request() { TRACE(("enter cli_send_chansess_request")); diff --git a/cli-session.c b/cli-session.c index 42770f5..6882d2e 100644 --- a/cli-session.c +++ b/cli-session.c @@ -80,6 +80,7 @@ static void cli_session_init() { cli_ses.kex_state = KEX_NOTHING; cli_ses.tty_raw_mode = 0; + cli_ses.winchange = 0; /* For printing "remote host closed" for the user */ ses.remoteclosed = cli_remoteclosed; @@ -170,6 +171,10 @@ static void cli_sessionloop() { if (ses.chancount < 1) { cli_finished(); } + + if (cli_ses.winchange) { + cli_chansess_winchange(); + } return; /* XXX more here needed */ diff --git a/common-session.c b/common-session.c index 79166f4..601136f 100644 --- a/common-session.c +++ b/common-session.c @@ -144,6 +144,11 @@ void session_loop(void(*loophandler)()) { if (val < 0) { if (errno == EINTR) { + /* This must happen even if we've been interrupted, so that + * changed signal-handler vars can take effect etc */ + if (loophandler) { + loophandler(); + } continue; } else { dropbear_exit("Error in select"); diff --git a/session.h b/session.h index 1ce944a..2009054 100644 --- a/session.h +++ b/session.h @@ -212,6 +212,8 @@ struct clientsession { int tty_raw_mode; /* Whether we're in raw mode (and have to clean up) */ struct termios saved_tio; + int winchange; /* Set to 1 when a windowchange signal happens */ + }; /* Global structs storing the state */ -- cgit v1.2.3 From 51a74b47995cfe24ef190b7e254f768eb92735f5 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 26 Aug 2004 13:16:40 +0000 Subject: - added circular buffering for channels - added stderr support for the client - cleaned up a bunch of "unused" warnings, duplicated header definitions - added exit-status support for the client --HG-- extra : convert_revision : 5bdf806d8b440c87f7235414662f4189195618f4 --- TODO | 2 + buffer.c | 2 +- channel.h | 12 ++--- chansession.h | 5 -- circbuffer.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ circbuffer.h | 50 ++++++++++++++++++++ cli-chansession.c | 36 ++++++++++++-- cli-main.c | 3 +- cli-session.c | 5 +- cli-tcpfwd.c | 2 +- common-channel.c | 67 ++++++++++++++------------ common-session.c | 4 +- dbutil.c | 3 +- includes.h | 10 ++++ session.h | 5 +- svr-agentfwd.c | 2 +- svr-chansession.c | 10 +++- svr-main.c | 7 ++- tcpfwd.h | 3 +- 19 files changed, 302 insertions(+), 64 deletions(-) create mode 100644 circbuffer.c create mode 100644 circbuffer.h (limited to 'chansession.h') diff --git a/TODO b/TODO index 0af56c9..4ac3757 100644 --- a/TODO +++ b/TODO @@ -2,6 +2,8 @@ Current: Things which might need doing: +- exit with returned exit codes? + - errfd needs fixing - Make options.h generated from configure perhaps? diff --git a/buffer.c b/buffer.c index df608d9..793eee1 100644 --- a/buffer.c +++ b/buffer.c @@ -1,5 +1,5 @@ /* - * Dropbear - a SSH2 server + * Dropbear SSH * * Copyright (c) 2002,2003 Matt Johnston * All rights reserved. diff --git a/channel.h b/channel.h index 3afd9f1..8a494c7 100644 --- a/channel.h +++ b/channel.h @@ -45,16 +45,15 @@ /* Not a real type */ #define SSH_OPEN_IN_PROGRESS 99 -#define MAX_CHANNELS 60 /* simple mem restriction, includes each tcp/x11 +#define MAX_CHANNELS 100 /* simple mem restriction, includes each tcp/x11 connection, so can't be _too_ small */ #define CHAN_EXTEND_SIZE 3 /* how many extra slots to add when we need more */ -#define RECV_MAXWINDOW 6000 /* tweak */ -#define RECV_WINDOWEXTEND (RECV_MAXWINDOW/2) /* We send a "window extend" every - RECV_WINDOWEXTEND bytes */ -#define RECV_MAXPACKET 1400 /* tweak */ -#define RECV_MINWINDOW 19000 /* when we get below this, we send a windowadjust */ +#define RECV_MAXWINDOW 4000 /* tweak */ +#define RECV_WINDOWEXTEND 500 /* We send a "window extend" every + RECV_WINDOWEXTEND bytes */ +#define RECV_MAXPACKET RECV_MAXWINDOW /* tweak */ struct ChanType; @@ -63,6 +62,7 @@ struct Channel { unsigned int index; /* the local channel index */ unsigned int remotechan; unsigned int recvwindow, transwindow; + unsigned int recvdonelen; unsigned int recvmaxpacket, transmaxpacket; void* typedata; /* a pointer to type specific data */ int infd; /* data to send over the wire */ diff --git a/chansession.h b/chansession.h index 3fb1901..84ca55a 100644 --- a/chansession.h +++ b/chansession.h @@ -68,11 +68,6 @@ struct ChildPid { }; -void chansessionrequest(struct Channel * channel); -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 cli_send_chansess_request(); diff --git a/circbuffer.c b/circbuffer.c new file mode 100644 index 0000000..a2edcbb --- /dev/null +++ b/circbuffer.c @@ -0,0 +1,138 @@ +/* + * Dropbear SSH + * + * Copyright (c) 2002-2004 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 "dbutil.h" +#include "circbuffer.h" + +#define MAX_CBUF_SIZE 100000000 + +circbuffer * cbuf_new(unsigned int size) { + + circbuffer *cbuf = NULL; + + if (size > MAX_CBUF_SIZE) { + dropbear_exit("bad cbuf size"); + } + + cbuf = (circbuffer*)m_malloc(sizeof(circbuffer)); + cbuf->data = (unsigned char*)m_malloc(size); + cbuf->used = 0; + cbuf->readpos = 0; + cbuf->writepos = 0; + cbuf->size = size; + + return cbuf; +} + +void cbuf_free(circbuffer * cbuf) { + + m_free(cbuf->data); + m_free(cbuf); +} + +unsigned int cbuf_getused(circbuffer * cbuf) { + + return cbuf->used; + +} + +unsigned int cbuf_getavail(circbuffer * cbuf) { + + return cbuf->size - cbuf->used; + +} + +unsigned int cbuf_readlen(circbuffer *cbuf) { + + assert(((2*cbuf->size)+cbuf->writepos-cbuf->readpos)%cbuf->size == cbuf->used%cbuf->size); + assert(((2*cbuf->size)+cbuf->readpos-cbuf->writepos)%cbuf->size == (cbuf->size-cbuf->used)%cbuf->size); + + if (cbuf->used == 0) { + TRACE(("cbuf_readlen: unused buffer")); + return 0; + } + + if (cbuf->readpos < cbuf->writepos) { + return cbuf->writepos - cbuf->readpos; + } + + return cbuf->size - cbuf->readpos; +} + +unsigned int cbuf_writelen(circbuffer *cbuf) { + + assert(cbuf->used <= cbuf->size); + assert(((2*cbuf->size)+cbuf->writepos-cbuf->readpos)%cbuf->size == cbuf->used%cbuf->size); + assert(((2*cbuf->size)+cbuf->readpos-cbuf->writepos)%cbuf->size == (cbuf->size-cbuf->used)%cbuf->size); + + if (cbuf->used == cbuf->size) { + TRACE(("cbuf_writelen: full buffer")); + return 0; /* full */ + } + + if (cbuf->writepos < cbuf->readpos) { + return cbuf->readpos - cbuf->writepos; + } + + return cbuf->size - cbuf->writepos; +} + +unsigned char* cbuf_readptr(circbuffer *cbuf, unsigned int len) { + if (len > cbuf_readlen(cbuf)) { + dropbear_exit("bad cbuf read"); + } + + return &cbuf->data[cbuf->readpos]; +} + +unsigned char* cbuf_writeptr(circbuffer *cbuf, unsigned int len) { + + if (len > cbuf_writelen(cbuf)) { + dropbear_exit("bad cbuf write"); + } + + return &cbuf->data[cbuf->writepos]; +} + +void cbuf_incrwrite(circbuffer *cbuf, unsigned int len) { + if (len > cbuf_writelen(cbuf)) { + dropbear_exit("bad cbuf write"); + } + + cbuf->used += len; + assert(cbuf->used <= cbuf->size); + cbuf->writepos = (cbuf->writepos + len) % cbuf->size; +} + + +void cbuf_incrread(circbuffer *cbuf, unsigned int len) { + if (len > cbuf_readlen(cbuf)) { + dropbear_exit("bad cbuf read"); + } + + assert(cbuf->used >= len); + cbuf->used -= len; + cbuf->readpos = (cbuf->readpos + len) % cbuf->size; +} diff --git a/circbuffer.h b/circbuffer.h new file mode 100644 index 0000000..21c5134 --- /dev/null +++ b/circbuffer.h @@ -0,0 +1,50 @@ +/* + * Dropbear SSH + * + * Copyright (c) 2002-2004 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 _CIRCBUFFER_H_ +#define _CIRCBUFFER_H_ +struct circbuf { + + unsigned int size; + unsigned int readpos; + unsigned int writepos; + unsigned int used; + unsigned char* data; +}; + +typedef struct circbuf circbuffer; + +circbuffer * cbuf_new(unsigned int size); +void cbuf_free(circbuffer * cbuf); + +unsigned int cbuf_getused(circbuffer * cbuf); /* how much data stored */ +unsigned int cbuf_getavail(circbuffer * cbuf); /* how much we can write */ +unsigned int cbuf_readlen(circbuffer *cbuf); /* max linear read len */ +unsigned int cbuf_writelen(circbuffer *cbuf); /* max linear write len */ + +unsigned char* cbuf_readptr(circbuffer *cbuf, unsigned int len); +unsigned char* cbuf_writeptr(circbuffer *cbuf, unsigned int len); +void cbuf_incrwrite(circbuffer *cbuf, unsigned int len); +void cbuf_incrread(circbuffer *cbuf, unsigned int len); +#endif diff --git a/cli-chansession.c b/cli-chansession.c index df6eb45..50226dd 100644 --- a/cli-chansession.c +++ b/cli-chansession.c @@ -32,9 +32,11 @@ #include "ssh.h" #include "runopts.h" #include "termcodes.h" +#include "chansession.h" static void cli_closechansess(struct Channel *channel); static int cli_initchansess(struct Channel *channel); +static void cli_chansessreq(struct Channel *channel); static void start_channel_request(struct Channel *channel, unsigned char *type); @@ -42,19 +44,43 @@ static void send_chansess_pty_req(struct Channel *channel); static void send_chansess_shell_req(struct Channel *channel); static void cli_tty_setup(); -void cli_tty_cleanup(); const struct ChanType clichansess = { 0, /* sepfds */ "session", /* name */ cli_initchansess, /* inithandler */ NULL, /* checkclosehandler */ - NULL, /* reqhandler */ + cli_chansessreq, /* reqhandler */ cli_closechansess, /* closehandler */ }; +static void cli_chansessreq(struct Channel *channel) { + + unsigned char* type = NULL; + int wantreply; + + TRACE(("enter cli_chansessreq")); + + type = buf_getstring(ses.payload, NULL); + wantreply = buf_getbyte(ses.payload); + + if (strcmp(type, "exit-status") != 0) { + TRACE(("unknown request '%s'", type)); + send_msg_channel_failure(channel); + goto out; + } + + /* We'll just trust what they tell us */ + cli_ses.retval = buf_getint(ses.payload); + TRACE(("got exit-status of '%d'", cli_ses.retval)); + +out: + m_free(type); +} + + /* If the main session goes, we close it up */ -static void cli_closechansess(struct Channel *channel) { +static void cli_closechansess(struct Channel *UNUSED(channel)) { /* This channel hasn't gone yet, so we have > 1 */ if (ses.chancount > 1) { @@ -228,7 +254,7 @@ static void put_winsize() { } -static void sigwinch_handler(int dummy) { +static void sigwinch_handler(int UNUSED(unused)) { cli_ses.winchange = 1; @@ -317,7 +343,7 @@ static int cli_initchansess(struct Channel *channel) { channel->infd = STDOUT_FILENO; channel->outfd = STDIN_FILENO; channel->errfd = STDERR_FILENO; - channel->extrabuf = buf_new(RECV_MAXWINDOW); + channel->extrabuf = cbuf_new(RECV_MAXWINDOW); if (cli_opts.wantpty) { send_chansess_pty_req(channel); diff --git a/cli-main.c b/cli-main.c index 659177e..34a1e42 100644 --- a/cli-main.c +++ b/cli-main.c @@ -96,7 +96,8 @@ static void cli_dropbear_exit(int exitcode, const char* format, va_list param) { exit(exitcode); } -static void cli_dropbear_log(int priority, const char* format, va_list param) { +static void cli_dropbear_log(int UNUSED(priority), + const char* format, va_list param) { char printbuf[1024]; diff --git a/cli-session.c b/cli-session.c index b51e20f..671735c 100644 --- a/cli-session.c +++ b/cli-session.c @@ -118,6 +118,9 @@ static void cli_session_init() { cli_ses.stdincopy = dup(STDIN_FILENO); cli_ses.stdinflags = fcntl(STDIN_FILENO, F_GETFL, 0); + cli_ses.retval = EXIT_SUCCESS; /* Assume it's clean if we don't get a + specific exit status */ + /* Auth */ cli_ses.lastpubkey = NULL; cli_ses.lastauthtype = NULL; @@ -261,7 +264,7 @@ static void cli_finished() { common_session_cleanup(); fprintf(stderr, "Connection to %s@%s:%s closed.\n", cli_opts.username, cli_opts.remotehost, cli_opts.remoteport); - exit(EXIT_SUCCESS); + exit(cli_ses.retval); } diff --git a/cli-tcpfwd.c b/cli-tcpfwd.c index 747d8db..4dbef01 100644 --- a/cli-tcpfwd.c +++ b/cli-tcpfwd.c @@ -89,7 +89,7 @@ static int cli_localtcp(unsigned int listenport, const char* remoteaddr, remoteport)); tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener*)); - tcpinfo->sendaddr = remoteaddr; + tcpinfo->sendaddr = m_strdup(remoteaddr); tcpinfo->sendport = remoteport; tcpinfo->listenport = listenport; tcpinfo->chantype = &cli_chan_tcplocal; diff --git a/common-channel.c b/common-channel.c index 41a7208..a4cc44b 100644 --- a/common-channel.c +++ b/common-channel.c @@ -40,7 +40,7 @@ static void send_msg_channel_open_failure(unsigned int remotechan, int reason, static void send_msg_channel_open_confirmation(struct Channel* channel, unsigned int recvwindow, unsigned int recvmaxpacket); -static void writechannel(struct Channel *channel); +static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf); static void send_msg_channel_window_adjust(struct Channel *channel, unsigned int incr); static void send_msg_channel_data(struct Channel *channel, int isextended, @@ -151,6 +151,7 @@ struct Channel* newchannel(unsigned int remotechan, newchan->writebuf = cbuf_new(RECV_MAXWINDOW); newchan->extrabuf = NULL; /* The user code can set it up */ newchan->recvwindow = RECV_MAXWINDOW; + newchan->recvdonelen = 0; newchan->recvmaxpacket = RECV_MAXPACKET; ses.channels[i] = newchan; @@ -220,7 +221,13 @@ void channelio(fd_set *readfd, fd_set *writefd) { continue; /* Important not to use the channel after checkinitdone(), as it may be NULL */ } - writechannel(channel); + writechannel(channel, channel->infd, channel->writebuf); + } + + /* stderr for client mode */ + if (channel->extrabuf != NULL + && channel->errfd >= 0 && FD_ISSET(channel->errfd, writefd)) { + writechannel(channel, channel->errfd, channel->extrabuf); } /* now handle any of the channel-closing type stuff */ @@ -350,33 +357,30 @@ static void send_msg_channel_eof(struct Channel *channel) { /* Called to write data out to the local side of the channel. * Only called when we know we can write to a channel, writes as much as * possible */ -static void writechannel(struct Channel* channel) { +static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf) { int len, maxlen; - circbuffer *cbuf; TRACE(("enter writechannel")); - cbuf = channel->writebuf; maxlen = cbuf_readlen(cbuf); - TRACE(("maxlen = %d", maxlen)); - /* Write the data out */ - len = write(channel->infd, cbuf_readptr(cbuf, maxlen), maxlen); + len = write(fd, cbuf_readptr(cbuf, maxlen), maxlen); if (len <= 0) { if (len < 0 && errno != EINTR) { - /* no more to write */ + /* no more to write - we close it even if the fd was stderr, since + * that's a nasty failure too */ closeinfd(channel); } TRACE(("leave writechannel: len <= 0")); return; } - TRACE(("len = %d", len)); cbuf_incrread(cbuf, len); + channel->recvdonelen += len; - if (len == maxlen && channel->recveof) { + if (fd == channel->infd && len == maxlen && channel->recveof) { /* Check if we're closing up */ closeinfd(channel); return; @@ -385,12 +389,17 @@ static void writechannel(struct Channel* channel) { } /* Window adjust handling */ - if (channel->recvwindow < (RECV_MAXWINDOW - RECV_WINDOWEXTEND)) { + if (channel->recvdonelen >= RECV_WINDOWEXTEND) { /* Set it back to max window */ - send_msg_channel_window_adjust(channel, RECV_MAXWINDOW - - channel->recvwindow); - channel->recvwindow = RECV_MAXWINDOW; + send_msg_channel_window_adjust(channel, channel->recvdonelen); + channel->recvwindow += channel->recvdonelen; + channel->recvdonelen = 0; } + + assert(channel->recvwindow <= RECV_MAXWINDOW); + assert(channel->recvwindow <= cbuf_getavail(channel->writebuf)); + assert(channel->extrabuf == NULL || + channel->recvwindow <= cbuf_getavail(channel->extrabuf)); TRACE(("leave writechannel")); @@ -424,6 +433,8 @@ void setchannelfds(fd_set *readfd, fd_set *writefd) { /* For checking FD status (ie closure etc) - we don't actually * read data from infd */ + TRACE(("infd = %d, outfd %d, bufused %d", channel->infd, channel->outfd, + cbuf_getused(channel->writebuf) )); if (channel->infd >= 0 && channel->infd != channel->outfd) { FD_SET(channel->infd, readfd); } @@ -435,12 +446,10 @@ void setchannelfds(fd_set *readfd, fd_set *writefd) { FD_SET(channel->infd, writefd); } - /* if (channel->extrabuf != NULL && channel->errfd >= 0 - && cbuf_getavail(channel->extrabuf) > 0 ) { + && cbuf_getused(channel->extrabuf) > 0 ) { FD_SET(channel->errfd, writefd); } - */ } /* foreach channel */ @@ -468,7 +477,9 @@ void recv_msg_channel_eof() { } channel->recveof = 1; - if (cbuf_getused(channel->writebuf) == 0) { + if (cbuf_getused(channel->writebuf) == 0 + && (channel->extrabuf == NULL + || cbuf_getused(channel->extrabuf) == 0)) { closeinfd(channel); } @@ -678,36 +689,34 @@ void common_recv_msg_channel_data(struct Channel *channel, int fd, datalen = buf_getint(ses.payload); - TRACE(("datalen = %d", datalen)); - /* if the client is going to send us more data than we've allocated, then - * it has ignored the windowsize, so we "MAY ignore all extra data" */ maxdata = cbuf_getavail(cbuf); - TRACE(("maxdata = %d", maxdata)); + + /* Whilst the spec says we "MAY ignore data past the end" this could + * lead to corrupted file transfers etc (chunks missed etc). It's better to + * just die horribly */ if (datalen > maxdata) { - TRACE(("Warning: recv_msg_channel_data: extra data past window")); - datalen = maxdata; + dropbear_exit("Oversized packet"); } - /* We may have to run throught twice, if the buffer wraps around. Can't * just "leave it for next time" like with writechannel, since this * is payload data */ len = datalen; while (len > 0) { buflen = cbuf_writelen(cbuf); - TRACE(("buflen = %d", buflen)); buflen = MIN(buflen, len); - TRACE(("buflenmin = %d", buflen)); memcpy(cbuf_writeptr(cbuf, buflen), buf_getptr(ses.payload, buflen), buflen); cbuf_incrwrite(cbuf, buflen); + buf_incrpos(ses.payload, buflen); len -= buflen; - TRACE(("len = %d", buflen)); } + assert(channel->recvwindow > datalen); channel->recvwindow -= datalen; + assert(channel->recvwindow <= RECV_MAXWINDOW); TRACE(("leave recv_msg_channel_data")); } diff --git a/common-session.c b/common-session.c index 1888eb7..f220753 100644 --- a/common-session.c +++ b/common-session.c @@ -35,6 +35,8 @@ #include "channel.h" #include "atomicio.h" +static void checktimeouts(); +static int ident_readln(int fd, char* buf, int count); struct sshsession ses; /* GLOBAL */ @@ -46,8 +48,6 @@ int sessinitdone = 0; /* GLOBAL */ int exitflag = 0; /* GLOBAL */ -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, char* remotehost) { diff --git a/dbutil.c b/dbutil.c index 0896bc9..a1ec3ed 100644 --- a/dbutil.c +++ b/dbutil.c @@ -111,7 +111,7 @@ static void generic_dropbear_exit(int exitcode, const char* format, exit(exitcode); } -static void generic_dropbear_log(int priority, const char* format, +static void generic_dropbear_log(int UNUSED(priority), const char* format, va_list param) { char printbuf[1024]; @@ -146,7 +146,6 @@ void dropbear_trace(const char* format, ...) { fprintf(stderr, "TRACE: "); vfprintf(stderr, format, param); fprintf(stderr, "\n"); - fflush(stderr); va_end(param); } #endif /* DEBUG_TRACE */ diff --git a/includes.h b/includes.h index b37422b..ceeb561 100644 --- a/includes.h +++ b/includes.h @@ -128,4 +128,14 @@ typedef u_int16_t uint16_t; #define LOG_AUTHPRIV LOG_AUTH #endif +/* so we can avoid warnings about unused params (ie in signal handlers etc) */ +#ifdef UNUSED +#elif defined(__GNUC__) +# define UNUSED(x) UNUSED_ ## x __attribute__((unused)) +#elif defined(__LCLINT__) +# define UNUSED(x) /*@unused@*/ x +#else +# define UNUSED(x) x +#endif + #endif /* _INCLUDES_H_ */ diff --git a/session.h b/session.h index 6afca85..40889a1 100644 --- a/session.h +++ b/session.h @@ -43,7 +43,6 @@ extern int exitflag; void common_session_init(int sock, char* remotehost); void session_loop(void(*loophandler)()); void common_session_cleanup(); -void checktimeouts(); void session_identification(); @@ -54,8 +53,6 @@ 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); void cli_session_cleanup(); void cleantext(unsigned char* dirtytext); @@ -220,6 +217,8 @@ struct clientsession { int lastauthtype; /* either AUTH_TYPE_PUBKEY or AUTH_TYPE_PASSWORD, for the last type of auth we tried */ struct PubkeyList *lastpubkey; + + int retval; /* What the command exit status was - we emulate it */ #if 0 TODO struct AgentkeyList *agentkeys; /* Keys to use for public-key auth */ diff --git a/svr-agentfwd.c b/svr-agentfwd.c index 4e9aa56..2674138 100644 --- a/svr-agentfwd.c +++ b/svr-agentfwd.c @@ -97,7 +97,7 @@ 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, int sock) { +static void agentaccept(struct Listener *UNUSED(listener), int sock) { int fd; diff --git a/svr-chansession.c b/svr-chansession.c index d56fb2c..8c9fa3b 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -55,6 +55,10 @@ static int newchansess(struct Channel *channel); static void chansessionrequest(struct Channel *channel); static void send_exitsignalstatus(struct Channel *channel); +static void send_msg_chansess_exitstatus(struct Channel * channel, + struct ChanSess * chansess); +static void send_msg_chansess_exitsignal(struct Channel * channel, + struct ChanSess * chansess); static int sesscheckclose(struct Channel *channel); static void get_termmodes(struct ChanSess *chansess); @@ -68,7 +72,7 @@ static int sesscheckclose(struct Channel *channel) { } /* handler for childs exiting, store the state for return to the client */ -static void sesssigchild_handler(int dummy) { +static void sesssigchild_handler(int UNUSED(dummy)) { int status; pid_t pid; @@ -498,7 +502,9 @@ static int sessionpty(struct ChanSess * chansess) { } /* allocate the pty */ - assert(chansess->master == -1); /* haven't already got one */ + if (chansess->master != -1) { + dropbear_exit("multiple pty requests"); + } if (pty_allocate(&chansess->master, &chansess->slave, namebuf, 64) == 0) { TRACE(("leave sessionpty: failed to allocate pty")); return DROPBEAR_FAILURE; diff --git a/svr-main.c b/svr-main.c index 55c4106..7dc3722 100644 --- a/svr-main.c +++ b/svr-main.c @@ -123,7 +123,6 @@ void main_noinetd() { pid_t childpid; int childpipe[2]; - struct sigaction sa_chld; /* fork */ if (svr_opts.forkbg) { int closefds = 0; @@ -303,7 +302,7 @@ void main_noinetd() { /* catch + reap zombie children */ -static void sigchld_handler(int fish) { +static void sigchld_handler(int UNUSED(unused)) { struct sigaction sa_chld; while(waitpid(-1, NULL, WNOHANG) > 0); @@ -316,14 +315,14 @@ static void sigchld_handler(int fish) { } /* catch any segvs */ -static void sigsegv_handler(int fish) { +static void sigsegv_handler(int UNUSED(unused)) { fprintf(stderr, "Aiee, segfault! You should probably report " "this as a bug to the developer\n"); exit(EXIT_FAILURE); } /* catch ctrl-c or sigterm */ -static void sigintterm_handler(int fish) { +static void sigintterm_handler(int UNUSED(unused)) { exitflag = 1; } diff --git a/tcpfwd.h b/tcpfwd.h index 569d59d..504a8d3 100644 --- a/tcpfwd.h +++ b/tcpfwd.h @@ -47,7 +47,7 @@ struct TCPListener { /* A link in a list of forwards */ struct TCPFwdList { - char* connectaddr; + const unsigned char* connectaddr; unsigned int connectport; unsigned int listenport; struct TCPFwdList * next; @@ -60,6 +60,7 @@ extern const struct ChanType svr_chan_tcpdirect; /* Client */ void setup_localtcp(); +void setup_remotetcp(); extern const struct ChanType cli_chan_tcpremote; /* Common */ -- cgit v1.2.3 From c2af67efd3e75fcb33eefb5992d1d053de282ae5 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 21 Sep 2004 10:08:21 +0000 Subject: propagate of 82bb923d0154750ef716b66b498561f882891946 and f51a272341ee12268fe7028bc2f2bad66c603069 from branch 'matt.dbclient.work' to 'matt.dbclient.rez' --HG-- branch : private-rez extra : convert_revision : 440ee4177122c8a873ebf8984a90f96a9bd8a56a --- chansession.h | 13 +++++--- session.h | 5 +++ svr-chansession.c | 92 ++++++++++++++++++++++++++++++++++++------------------- 3 files changed, 74 insertions(+), 36 deletions(-) (limited to 'chansession.h') diff --git a/chansession.h b/chansession.h index 84ca55a..213c285 100644 --- a/chansession.h +++ b/chansession.h @@ -29,6 +29,14 @@ #include "channel.h" #include "listener.h" +struct exitinfo { + + int exitpid; /* -1 if not exited */ + int exitstatus; + int exitsignal; + int exitcore; +}; + struct ChanSess { unsigned char * cmd; /* command to exec */ @@ -41,10 +49,7 @@ struct ChanSess { unsigned char * term; /* exit details */ - int exited; - int exitstatus; - int exitsignal; - unsigned char exitcore; + struct exitinfo exit; #ifndef DISABLE_X11FWD struct Listener * x11listener; diff --git a/session.h b/session.h index 40889a1..9142882 100644 --- a/session.h +++ b/session.h @@ -36,6 +36,7 @@ #include "listener.h" #include "packet.h" #include "tcpfwd.h" +#include "chansession.h" extern int sessinitdone; /* Is set to 0 somewhere */ extern int exitflag; @@ -176,6 +177,10 @@ struct serversession { struct ChildPid * childpids; /* array of mappings childpid<->channel */ unsigned int childpidsize; + /* Used to avoid a race in the exit returncode handling - see + * svr-chansession.c for details */ + struct exitinfo lastexit; + }; typedef enum { diff --git a/svr-chansession.c b/svr-chansession.c index e9a3c87..c3356d6 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -68,17 +68,24 @@ extern char** environ; static int sesscheckclose(struct Channel *channel) { struct ChanSess *chansess = (struct ChanSess*)channel->typedata; - return chansess->exited; + return chansess->exit.exitpid >= 0; } -/* handler for childs exiting, store the state for return to the client */ +/* Handler for childs exiting, store the state for return to the client */ + +/* There's a particular race we have to watch out for: if the forked child + * executes, exits, and this signal-handler is called, all before the parent + * gets to run, then the childpids[] array won't have the pid in it. Hence we + * use the svr_ses.lastexit struct to hold the exit, which is then compared by + * the parent when it runs. This work correctly at least in the case of a + * single shell spawned (ie the usual case) */ static void sesssigchild_handler(int UNUSED(dummy)) { int status; pid_t pid; unsigned int i; - struct ChanSess * chansess; struct sigaction sa_chld; + struct exitinfo *exit = NULL; TRACE(("enter sigchld handler")); while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { @@ -86,25 +93,36 @@ static void sesssigchild_handler(int UNUSED(dummy)) { for (i = 0; i < svr_ses.childpidsize; i++) { if (svr_ses.childpids[i].pid == pid) { - chansess = svr_ses.childpids[i].chansess; - chansess->exited = 1; - if (WIFEXITED(status)) { - chansess->exitstatus = WEXITSTATUS(status); - } - if (WIFSIGNALED(status)) { - chansess->exitsignal = WTERMSIG(status); + exit = &svr_ses.childpids[i].chansess->exit; + break; + } + } + + /* If the pid wasn't matched, then we might have hit the race mentioned + * above. So we just store the info for the parent to deal with */ + if (i == svr_ses.childpidsize) { + exit = &svr_ses.lastexit; + } + + exit->exitpid = pid; + if (WIFEXITED(status)) { + exit->exitstatus = WEXITSTATUS(status); + } + if (WIFSIGNALED(status)) { + exit->exitsignal = WTERMSIG(status); #if !defined(AIX) && defined(WCOREDUMP) - chansess->exitcore = WCOREDUMP(status); + exit->exitcore = WCOREDUMP(status); #else - chansess->exitcore = 0; + exit->exitcore = 0; #endif - } else { - /* we use this to determine how pid exited */ - chansess->exitsignal = -1; - } - } + } else { + /* we use this to determine how pid exited */ + exit->exitsignal = -1; } + exit = NULL; } + + sa_chld.sa_handler = sesssigchild_handler; sa_chld.sa_flags = SA_NOCLDSTOP; sigaction(SIGCHLD, &sa_chld, NULL); @@ -117,8 +135,8 @@ static void send_exitsignalstatus(struct Channel *channel) { struct ChanSess *chansess = (struct ChanSess*)channel->typedata; - if (chansess->exited) { - if (chansess->exitsignal > 0) { + if (chansess->exit.exitpid >= 0) { + if (chansess->exit.exitsignal > 0) { send_msg_chansess_exitsignal(channel, chansess); } else { send_msg_chansess_exitstatus(channel, chansess); @@ -130,8 +148,8 @@ static void send_exitsignalstatus(struct Channel *channel) { static void send_msg_chansess_exitstatus(struct Channel * channel, struct ChanSess * chansess) { - assert(chansess->exited); - assert(chansess->exitsignal == -1); + assert(chansess->exit.exitpid != -1); + assert(chansess->exit.exitsignal == -1); CHECKCLEARTOWRITE(); @@ -139,7 +157,7 @@ static void send_msg_chansess_exitstatus(struct Channel * channel, buf_putint(ses.writepayload, channel->remotechan); buf_putstring(ses.writepayload, "exit-status", 11); buf_putbyte(ses.writepayload, 0); /* boolean FALSE */ - buf_putint(ses.writepayload, chansess->exitstatus); + buf_putint(ses.writepayload, chansess->exit.exitstatus); encrypt_packet(); @@ -152,15 +170,15 @@ static void send_msg_chansess_exitsignal(struct Channel * channel, int i; char* signame = NULL; - assert(chansess->exited); - assert(chansess->exitsignal > 0); + assert(chansess->exit.exitpid != -1); + assert(chansess->exit.exitsignal > 0); CHECKCLEARTOWRITE(); /* we check that we can match a signal name, otherwise * don't send anything */ for (i = 0; signames[i].name != NULL; i++) { - if (signames[i].signal == chansess->exitsignal) { + if (signames[i].signal == chansess->exit.exitsignal) { signame = signames[i].name; break; } @@ -175,7 +193,7 @@ static void send_msg_chansess_exitsignal(struct Channel * channel, buf_putstring(ses.writepayload, "exit-signal", 11); buf_putbyte(ses.writepayload, 0); /* boolean FALSE */ buf_putstring(ses.writepayload, signame, strlen(signame)); - buf_putbyte(ses.writepayload, chansess->exitcore); + buf_putbyte(ses.writepayload, chansess->exit.exitcore); buf_putstring(ses.writepayload, "", 0); /* error msg */ buf_putstring(ses.writepayload, "", 0); /* lang */ @@ -199,7 +217,7 @@ static int newchansess(struct Channel *channel) { chansess->tty = NULL; chansess->term = NULL; - chansess->exited = 0; + chansess->exit.exitpid = -1; channel->typedata = chansess; @@ -263,7 +281,7 @@ static void closechansess(struct Channel *channel) { if (svr_ses.childpids[i].chansess == chansess) { assert(svr_ses.childpids[i].pid > 0); TRACE(("closing pid %d", svr_ses.childpids[i].pid)); - TRACE(("exited = %d", chansess->exited)); + TRACE(("exitpid = %d", chansess->exit.exitpid)); svr_ses.childpids[i].pid = -1; svr_ses.childpids[i].chansess = NULL; } @@ -592,6 +610,7 @@ static int noptycommand(struct Channel *channel, struct ChanSess *chansess) { int outfds[2]; int errfds[2]; pid_t pid; + unsigned int i; TRACE(("enter noptycommand")); @@ -635,12 +654,21 @@ static int noptycommand(struct Channel *channel, struct ChanSess *chansess) { TRACE(("continue noptycommand: parent")); chansess->pid = pid; - /* add a child pid - Beware: there's a race between this, and the - * exec() called from the child. If the child finishes before we've - * done this (ie if it was a shell builtin and fast), we won't return a - * proper return code. For now, we ignore this case. */ addchildpid(chansess, pid); + if (svr_ses.lastexit.exitpid != -1) { + /* The child probably exited and the signal handler triggered + * possibly before we got around to adding the childpid. So we fill + * out it's data manually */ + for (i = 0; i < svr_ses.childpidsize; i++) { + if (svr_ses.childpids[i].pid == pid) { + svr_ses.childpids[i].chansess->exit = svr_ses.lastexit; + svr_ses.lastexit.exitpid = -1; + } + } + } + + close(infds[FDIN]); close(outfds[FDOUT]); close(errfds[FDOUT]); -- cgit v1.2.3