diff options
-rw-r--r-- | common-kex.c | 4 | ||||
-rw-r--r-- | common-packet.c | 15 | ||||
-rw-r--r-- | common-session.c | 6 | ||||
-rw-r--r-- | debug.h | 2 | ||||
-rw-r--r-- | packet.h | 8 | ||||
-rw-r--r-- | process-packet.c | 161 | ||||
-rw-r--r-- | session.h | 17 | ||||
-rw-r--r-- | ssh.h | 3 | ||||
-rw-r--r-- | svr-auth.c | 10 | ||||
-rw-r--r-- | svr-kex.c | 2 | ||||
-rw-r--r-- | svr-packet.c | 196 | ||||
-rw-r--r-- | svr-service.c | 2 | ||||
-rw-r--r-- | svr-session.c | 32 | ||||
-rw-r--r-- | tcpfwd-remote.c | 5 |
14 files changed, 231 insertions, 232 deletions
diff --git a/common-kex.c b/common-kex.c index 95f5db5..e1ae91a 100644 --- a/common-kex.c +++ b/common-kex.c @@ -450,8 +450,8 @@ void recv_msg_kexinit() { /* the rest of ses.kexhashbuf will be done after DH exchange */ ses.kexstate.recvkexinit = 1; -// ses.expecting = SSH_MSG_KEXDH_INIT; - ses.expecting = 0; + ses.requirenext = SSH_MSG_KEXDH_INIT; +// ses.expecting = 0; // client matt TRACE(("leave recv_msg_kexinit")); } diff --git a/common-packet.c b/common-packet.c index 4ca6f8f..39387bf 100644 --- a/common-packet.c +++ b/common-packet.c @@ -402,21 +402,6 @@ static buffer* buf_decompress(buffer* buf, unsigned int len) { -/* This must be called directly after receiving the unimplemented packet. - * Isn't the most clean implementation, it relies on packet processing - * occurring directly after decryption. This is reasonably valid, since - * there is only a single decryption buffer */ -void recv_unimplemented() { - - CHECKCLEARTOWRITE(); - - buf_putbyte(ses.writepayload, SSH_MSG_UNIMPLEMENTED); - /* the decryption routine increments the sequence number, we must - * decrement */ - buf_putint(ses.writepayload, ses.recvseq - 1); - - encrypt_packet(); -} /* encrypt the writepayload, putting into writebuf, ready for write_packet() * to put on the wire */ diff --git a/common-session.c b/common-session.c index fce301a..b7793f9 100644 --- a/common-session.c +++ b/common-session.c @@ -76,7 +76,7 @@ void common_session_init(int sock, runopts *opts) { ses.payload = NULL; ses.recvseq = 0; - ses.expecting = SSH_MSG_KEXINIT; + ses.requirenext = SSH_MSG_KEXINIT; ses.dataallowed = 0; /* don't send data yet, we'll wait until after kex */ ses.ignorenext = 0; @@ -106,8 +106,12 @@ void common_session_init(int sock, runopts *opts) { ses.dh_K = NULL; ses.remoteident = NULL; + ses.authdone = 0; + ses.chantypes = NULL; + ses.allowprivport = 0; + TRACE(("leave session_init")); } @@ -36,7 +36,7 @@ /* Define this to print trace statements - very verbose */ /* Caution: Don't use this in an unfriendly environment (ie unfirewalled), * since the printing does not sanitise strings etc */ -/*#define DEBUG_TRACE*/ +#define DEBUG_TRACE /* All functions writing to the cleartext payload buffer call * CHECKCLEARTOWRITE() before writing. This is only really useful if you're @@ -32,9 +32,13 @@ void write_packet(); void read_packet(); void decrypt_packet(); void encrypt_packet(); -void recv_unimplemented(); -void svr_process_packet(); +void process_packet(); + +typedef struct PacketType { + unsigned char type; /* SSH_MSG_FOO */ + void (*handler)(); +} packettype; #define PACKET_PADDING_OFF 4 #define PACKET_PAYLOAD_OFF 5 diff --git a/process-packet.c b/process-packet.c new file mode 100644 index 0000000..7b73cb7 --- /dev/null +++ b/process-packet.c @@ -0,0 +1,161 @@ +/* + * Dropbear - a SSH2 server + * + * 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 "packet.h" +#include "session.h" +#include "dbutil.h" +#include "ssh.h" +#include "algo.h" +#include "buffer.h" +#include "kex.h" +#include "random.h" +#include "service.h" +#include "auth.h" +#include "channel.h" + +#define MAX_UNAUTH_PACKET_TYPE SSH_MSG_USERAUTH_PK_OK + +static void recv_unimplemented(); + +/* process a decrypted packet, call the appropriate handler */ +void process_packet() { + + unsigned char type; + unsigned int i; + + TRACE(("enter process_packet")); + + type = buf_getbyte(ses.payload); + TRACE(("process_packet: packet type = %d", type)); + + /* These packets we can receive at any time */ + switch(type) { + + case SSH_MSG_IGNORE: + case SSH_MSG_DEBUG: + TRACE(("received SSH_MSG_IGNORE or SSH_MSG_DEBUG")); + goto out; + + case SSH_MSG_UNIMPLEMENTED: + /* debugging XXX */ + TRACE(("SSH_MSG_UNIMPLEMENTED")); + dropbear_exit("received SSH_MSG_UNIMPLEMENTED"); + + case SSH_MSG_DISCONNECT: + /* TODO cleanup? */ + dropbear_close("Disconnect received"); + } + + + /* This applies for KEX, where the spec says the next packet MUST be + * NEWKEYS */ + if (ses.requirenext != 0) { + if (ses.requirenext != type) { + /* TODO send disconnect? */ + dropbear_exit("unexpected packet type %d, expected %d", type, + ses.requirenext); + } else { + /* Got what we expected */ + ses.requirenext = 0; + } + } + + /* Check if we should ignore this packet. Used currently only for + * KEX code, with first_kex_packet_follows */ + if (ses.ignorenext) { + TRACE(("Ignoring packet, type = %d", type)); + ses.ignorenext = 0; + goto out; + } + + /* XXX This code somewhere else perhaps? */ +#ifdef DROPBEAR_CLIENT + if (IS_DROPBEAR_CLIENT) { + + /* XXX - needs changing */ + /* In client mode, we REUSE ses.expecting to proceed to the + * next phase when a packet was received. + * If the "expecting" flag is set to a non-null value, it will + * be reset when a packet of that type is received. + * This is different from the server-mode behaviour, when + * a packet type mismatch would have caused sudden death. + */ + + /* check that we aren't expecting a particular packet */ + if (cli_ses.expecting && cli_ses.expecting == type) { + cli_ses.expecting = 0; + } + } +#endif + + /* Kindly the protocol authors gave all the preauth packets type values + * less-than-or-equal-to 60 ( == MAX_UNAUTH_PACKET_TYPE ). + * NOTE: if the protocol changes and new types are added, revisit this + * assumption */ + if ( !ses.authdone && type > MAX_UNAUTH_PACKET_TYPE ) { + dropbear_exit("received message %d before userauth", type); + } + + for (i = 0; ; i++) { + if (ses.packettypes[i].type == 0) { + /* end of list */ + break; + } + + if (ses.packettypes[i].type == type) { + ses.packettypes[i].handler(); + goto out; + } + } + + + /* TODO do something more here? */ + TRACE(("preauth unknown packet")); + recv_unimplemented(); + +out: + buf_free(ses.payload); + ses.payload = NULL; + + TRACE(("leave process_packet")); +} + + + +/* This must be called directly after receiving the unimplemented packet. + * Isn't the most clean implementation, it relies on packet processing + * occurring directly after decryption (direct use of ses.recvseq). + * This is reasonably valid, since there is only a single decryption buffer */ +static void recv_unimplemented() { + + CHECKCLEARTOWRITE(); + + buf_putbyte(ses.writepayload, SSH_MSG_UNIMPLEMENTED); + /* the decryption routine increments the sequence number, we must + * decrement */ + buf_putint(ses.writepayload, ses.recvseq - 1); + + encrypt_packet(); +} @@ -34,6 +34,7 @@ #include "queue.h" #include "runopts.h" #include "listener.h" +#include "packet.h" extern int sessinitdone; /* Is set to 0 somewhere */ extern int exitflag; @@ -106,10 +107,13 @@ struct sshsession { unsigned int transseq, recvseq; /* Sequence IDs */ /* Packet-handling flags */ + const packettype * packettypes; /* Packet handler mappings for this + session, see process-packet.c */ + unsigned dataallowed : 1; /* whether we can send data packets or we are in the middle of a KEX or something */ - unsigned char expecting; /* byte indicating what packet we expect next, + unsigned char requirenext; /* byte indicating what packet we require next, or 0x00 for any */ unsigned char ignorenext; /* whether to ignore the next packet, @@ -130,6 +134,12 @@ struct sshsession { can add it to the hash when generating keys */ + unsigned char authdone; /* Indicates when authentication has been + completed. This applies to both client and + server - in the server it gets set to 1 when + authentication is successful, in the client it + is set when the server has told us that auth + succeeded */ /* Channel related */ struct Channel ** channels; /* these pointers may be null */ @@ -138,10 +148,13 @@ struct sshsession { /* TCP forwarding - where manage listeners */ -#ifndef DISABLE_REMOTETCPFWD +#ifdef USING_LISTENERS struct Listener ** listeners; unsigned int listensize; + /* Whether to allow binding to privileged ports (<1024). This doesn't + * really belong here, but nowhere else fits nicely */ #endif + int allowprivport; }; @@ -44,6 +44,9 @@ #define SSH_MSG_USERAUTH_BANNER 53 #define SSH_MSG_USERAUTH_PK_OK 60 +/* If adding numbers here, check MAX_UNAUTH_PACKET_TYPE in process-packet.c + * is still valid */ + /* connect message numbers */ #define SSH_MSG_GLOBAL_REQUEST 80 #define SSH_MSG_REQUEST_SUCCESS 81 @@ -52,7 +52,7 @@ void authinitialise() { * on initialisation */ static void authclear() { - svr_ses.authstate.authdone = 0; + ses.authdone = 0; svr_ses.authstate.pw = NULL; svr_ses.authstate.username = NULL; svr_ses.authstate.printableuser = NULL; @@ -102,7 +102,7 @@ void recv_msg_userauth_request() { TRACE(("enter recv_msg_userauth_request")); /* ignore packets if auth is already done */ - if (svr_ses.authstate.authdone == 1) { + if (ses.authdone == 1) { return; } @@ -339,7 +339,11 @@ void send_msg_userauth_success() { buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_SUCCESS); encrypt_packet(); - svr_ses.authstate.authdone = 1; + ses.authdone = 1; + + if (svr_ses.authstate.pw->pw_uid == 0) { + ses.allowprivport = 1; + } /* Remove from the list of pre-auth sockets. Should be m_close(), since if * we fail, we might end up leaking connection slots, and disallow new @@ -57,7 +57,7 @@ void recv_msg_kexdh_init() { mp_clear(&dh_e); send_msg_newkeys(); - ses.expecting = SSH_MSG_NEWKEYS; + ses.requirenext = SSH_MSG_NEWKEYS; TRACE(("leave recv_msg_kexdh_init")); } diff --git a/svr-packet.c b/svr-packet.c deleted file mode 100644 index 064f465..0000000 --- a/svr-packet.c +++ /dev/null @@ -1,196 +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. */ - -#include "includes.h" -#include "packet.h" -#include "session.h" -#include "dbutil.h" -#include "ssh.h" -#include "algo.h" -#include "buffer.h" -#include "kex.h" -#include "random.h" -#include "service.h" -#include "auth.h" -#include "channel.h" - -static void svr_process_postauth_packet(unsigned int type); - -/* process a decrypted packet, call the appropriate handler */ -void svr_process_packet() { - - unsigned char type; - - TRACE(("enter process_packet")); - - type = buf_getbyte(ses.payload); - TRACE(("process_packet: packet type = %d", type)); - - /* these packets we can receive at any time, regardless of expecting - * other packets: */ - switch(type) { - - case SSH_MSG_IGNORE: - case SSH_MSG_DEBUG: - TRACE(("received SSH_MSG_IGNORE or SSH_MSG_DEBUG")); - goto out; - - case SSH_MSG_UNIMPLEMENTED: - /* debugging XXX */ - TRACE(("SSH_MSG_UNIMPLEMENTED")); - dropbear_exit("received SSH_MSG_UNIMPLEMENTED"); - - case SSH_MSG_DISCONNECT: - /* TODO cleanup? */ - dropbear_close("Disconnect received"); - } - - /* Check if we should ignore this packet. Used currently only for - * KEX code, with first_kex_packet_follows */ - if (ses.ignorenext) { - TRACE(("Ignoring packet, type = %d", type)); - ses.ignorenext = 0; - goto out; - } - - /* check that we aren't expecting a particular packet */ - if (ses.expecting && ses.expecting != type) { - /* TODO send disconnect? */ - dropbear_exit("unexpected packet type %d, expected %d", type, - ses.expecting); - } - - /* handle the packet depending on type */ - ses.expecting = 0; - - switch (type) { - - case SSH_MSG_SERVICE_REQUEST: - recv_msg_service_request(); - break; - - case SSH_MSG_USERAUTH_REQUEST: - recv_msg_userauth_request(); - break; - - case SSH_MSG_KEXINIT: - recv_msg_kexinit(); - break; - - case SSH_MSG_KEXDH_INIT: - recv_msg_kexdh_init(); - break; - - case SSH_MSG_NEWKEYS: - recv_msg_newkeys(); - break; - - /* this is ugly, need to make a cleaner way to do it */ - case SSH_MSG_CHANNEL_DATA: - case SSH_MSG_CHANNEL_WINDOW_ADJUST: - case SSH_MSG_CHANNEL_REQUEST: - case SSH_MSG_CHANNEL_OPEN: - case SSH_MSG_CHANNEL_EOF: - case SSH_MSG_CHANNEL_CLOSE: - case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: - case SSH_MSG_CHANNEL_OPEN_FAILURE: - case SSH_MSG_GLOBAL_REQUEST: - /* these should be checked for authdone below */ - svr_process_postauth_packet(type); - break; - - default: - /* TODO this possibly should be handled */ - TRACE(("preauth unknown packet")); - recv_unimplemented(); - break; - } - -out: - buf_free(ses.payload); - ses.payload = NULL; - - TRACE(("leave process_packet")); -} - -/* process a packet, and also check that auth has been done */ -static void svr_process_postauth_packet(unsigned int type) { - - /* messages following here require userauth before use */ - - /* IF YOU ADD MORE PACKET TYPES, MAKE SURE THEY'RE ALSO ADDED TO THE LIST - * IN process_packet() XXX XXX XXX */ - if (!svr_ses.authstate.authdone) { - dropbear_exit("received message %d before userauth", type); - } - - switch (type) { - - case SSH_MSG_CHANNEL_DATA: - recv_msg_channel_data(); - break; - - case SSH_MSG_CHANNEL_WINDOW_ADJUST: - recv_msg_channel_window_adjust(); - break; - -#ifndef DISABLE_REMOTETCPFWD - case SSH_MSG_GLOBAL_REQUEST: - /* currently only used for remote tcp, so we cheat a little */ - recv_msg_global_request_remotetcp(); - break; -#endif - - case SSH_MSG_CHANNEL_REQUEST: - recv_msg_channel_request(); - break; - - case SSH_MSG_CHANNEL_OPEN: - recv_msg_channel_open(); - break; - - case SSH_MSG_CHANNEL_EOF: - recv_msg_channel_eof(); - break; - - case SSH_MSG_CHANNEL_CLOSE: - recv_msg_channel_close(); - break; - -#ifdef USING_LISTENERS /* for x11, tcp fwd etc */ - case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: - recv_msg_channel_open_confirmation(); - break; - - case SSH_MSG_CHANNEL_OPEN_FAILURE: - recv_msg_channel_open_failure(); - break; -#endif - - default: - TRACE(("unknown packet()")); - recv_unimplemented(); - break; - } -} diff --git a/svr-service.c b/svr-service.c index 4154df4..7b717bf 100644 --- a/svr-service.c +++ b/svr-service.c @@ -56,7 +56,7 @@ void recv_msg_service_request() { /* ssh-connection */ if (len == SSH_SERVICE_CONNECTION_LEN && (strncmp(SSH_SERVICE_CONNECTION, name, len) == 0)) { - if (svr_ses.authstate.authdone != 1) { + if (ses.authdone != 1) { dropbear_exit("request for connection before auth"); } diff --git a/svr-session.c b/svr-session.c index 4310e2b..d3094ea 100644 --- a/svr-session.c +++ b/svr-session.c @@ -36,18 +36,39 @@ #include "chansession.h" #include "atomicio.h" #include "tcpfwd-direct.h" +#include "service.h" +#include "auth.h" +#include "tcpfwd-remote.h" static void svr_remoteclosed(); struct serversession svr_ses; -const struct ChanType *chantypes[] = { +static const packettype svr_packettypes[] = { + /* TYPE, AUTHREQUIRED, FUNCTION */ + {SSH_MSG_SERVICE_REQUEST, recv_msg_service_request}, // server + {SSH_MSG_USERAUTH_REQUEST, recv_msg_userauth_request}, //server + {SSH_MSG_KEXINIT, recv_msg_kexinit}, + {SSH_MSG_KEXDH_INIT, recv_msg_kexdh_init}, + {SSH_MSG_NEWKEYS, recv_msg_newkeys}, + {SSH_MSG_CHANNEL_DATA, recv_msg_channel_data}, + {SSH_MSG_CHANNEL_WINDOW_ADJUST, recv_msg_channel_window_adjust}, + {SSH_MSG_GLOBAL_REQUEST, recv_msg_global_request_remotetcp}, + {SSH_MSG_CHANNEL_REQUEST, recv_msg_channel_request}, + {SSH_MSG_CHANNEL_OPEN, recv_msg_channel_open}, + {SSH_MSG_CHANNEL_EOF, recv_msg_channel_eof}, + {SSH_MSG_CHANNEL_CLOSE, recv_msg_channel_close}, + {SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation}, + {SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure}, + {0, 0} /* End */ +}; + +static const struct ChanType *svr_chantypes[] = { &svrchansess, &chan_tcpdirect, NULL /* Null termination is mandatory. */ }; - void svr_session(int sock, runopts *opts, int childpipe, struct sockaddr* remoteaddr) { @@ -64,7 +85,7 @@ void svr_session(int sock, runopts *opts, int childpipe, /* Initialise server specific parts of the session */ svr_ses.childpipe = childpipe; authinitialise(); - chaninitialise(chantypes); + chaninitialise(svr_chantypes); svr_chansessinitialise(); if (gettimeofday(&timeout, 0) < 0) { @@ -76,6 +97,9 @@ void svr_session(int sock, runopts *opts, int childpipe, /* set up messages etc */ session_remoteclosed = svr_remoteclosed; + /* packet handlers */ + ses.packettypes = svr_packettypes; + /* We're ready to go now */ sessinitdone = 1; @@ -145,7 +169,7 @@ void svr_session(int sock, runopts *opts, int childpipe, /* Process the decrypted packet. After this, the read buffer * will be ready for a new packet */ if (ses.payload != NULL) { - svr_process_packet(); + process_packet(); } } diff --git a/tcpfwd-remote.c b/tcpfwd-remote.c index 880044f..16b1105 100644 --- a/tcpfwd-remote.c +++ b/tcpfwd-remote.c @@ -208,13 +208,10 @@ static int remotetcpreq() { goto out; } - /* XXX matt - server change - if (ses.authstate.pw->pw_uid != 0 - && port < IPPORT_RESERVED) { + if (!ses.allowprivport && port < IPPORT_RESERVED) { TRACE(("can't assign port < 1024 for non-root")); goto out; } - */ ret = listen_tcpfwd(bindaddr, port); |