Mercurial > dropbear
changeset 26:0969767bca0d
snapshot of stuff
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Mon, 26 Jul 2004 02:44:20 +0000 |
parents | e4b6e2d569b2 |
children | 08da099e8337 |
files | algo.h cli-algo.c cli-kex.c cli-main.c cli-session.c common-kex.c common-session.c dbutil.c dbutil.h debug.h kex.h main.c options.h process-packet.c session.h signkey.c svr-algo.c svr-kex.c svr-session.c tcpfwd-direct.c |
diffstat | 20 files changed, 750 insertions(+), 321 deletions(-) [+] |
line wrap: on
line diff
--- a/algo.h Tue Jul 20 12:06:37 2004 +0000 +++ b/algo.h Mon Jul 26 02:44:20 2004 +0000 @@ -66,10 +66,9 @@ int have_algo(char* algo, size_t algolen, algo_type algos[]); void buf_put_algolist(buffer * buf, algo_type localalgos[]); -algo_type * common_buf_match_algo(buffer* buf, algo_type localalgos[], - int *goodguess); algo_type * svr_buf_match_algo(buffer* buf, algo_type localalgos[], int *goodguess); -algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[]); +algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[], + int *goodguess); #endif /* _ALGO_H_ */
--- a/cli-algo.c Tue Jul 20 12:06:37 2004 +0000 +++ b/cli-algo.c Mon Jul 26 02:44:20 2004 +0000 @@ -33,7 +33,8 @@ * direction MUST be the first algorithm on the client's list * that is also on the server's list. */ -algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[]) { +algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[], + int *goodguess) { unsigned char * algolist = NULL; unsigned char * remotealgos[MAX_PROPOSED_ALGO]; @@ -41,6 +42,8 @@ unsigned int count, i, j; algo_type * ret = NULL; + *goodguess = 0; + /* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */ algolist = buf_getstring(buf, &len); TRACE(("cli_buf_match_algo: %s", algolist)); @@ -78,6 +81,10 @@ if (len == strlen(remotealgos[i]) && strncmp(localalgos[j].name, remotealgos[i], len) == 0) { + if (i == 0 && j == 0) { + /* was a good guess */ + *goodguess = 1; + } ret = &localalgos[j]; goto out; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cli-kex.c Mon Jul 26 02:44:20 2004 +0000 @@ -0,0 +1,91 @@ +/* + * Dropbear - a SSH2 server + * + * Copyright (c) 2002,2003 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + +#include "includes.h" +#include "session.h" +#include "dbutil.h" +#include "algo.h" +#include "buffer.h" +#include "session.h" +#include "kex.h" +#include "ssh.h" +#include "packet.h" +#include "bignum.h" +#include "random.h" +#include "runopts.h" + + + +void send_msg_kexdh_init() { + + cli_ses.dh_e = (mp_int*)m_malloc(sizeof(mp_int)); + cli_ses.dh_x = (mp_int*)m_malloc(sizeof(mp_int)); + + m_mp_init_multi(cli_ses.dh_e, cli_ses.dh_x); + gen_kexdh_vals(cli_ses.dh_e, cli_ses.dh_x); + + CHECKCLEARTOWRITE(); + buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_INIT); + buf_putmpint(ses.writepayload, cli_ses.dh_e); + encrypt_packet(); + ses.requirenext = SSH_MSG_KEXDH_REPLY; +} + +/* Handle a diffie-hellman key exchange reply. */ +void recv_msg_kexdh_reply() { + + mp_int dh_f; + sign_key *hostkey = NULL; + int type; + + type = ses.newkeys->algo_hostkey; + + hostkey = new_sign_key(); + if (buf_get_pub_key(ses.payload, hostkey, &type) != DROPBEAR_SUCCESS) { + dropbear_exit("Bad KEX packet"); + } + + m_mp_init(&dh_f); + if (buf_getmpint(ses.payload, &dh_f) != DROPBEAR_SUCCESS) { + dropbear_exit("Bad KEX packet"); + } + + kexdh_comb_key(cli_ses.dh_e, cli_ses.dh_x, &dh_f, hostkey); + mp_clear(&dh_f); + + if (buf_verify(ses.payload, hostkey, ses.hash, SHA1_HASH_SIZE) + != DROPBEAR_SUCCESS) { + dropbear_exit("Bad hostkey signature"); + } + + /* XXX TODO */ + dropbear_log(LOG_WARNING,"Not checking hostkey fingerprint for the moment"); + + sign_key_free(hostkey); + hostkey = NULL; + + send_msg_newkeys(); + ses.requirenext = SSH_MSG_NEWKEYS; + TRACE(("leave recv_msg_kexdh_init")); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cli-main.c Mon Jul 26 02:44:20 2004 +0000 @@ -0,0 +1,33 @@ +#include <includes.h> + +int main(int argc, char ** argv) { + + int sock; + char* error = NULL; + char* hostandport; + int len; + + _dropbear_exit = cli_dropbear_exit; + _dropbear_log = cli_dropbear_log; + + cli_getopts(argc, argv); + + sock = connect_remote(cli_opts.remotehost, cli_opts.remoteport, + 0, &error); + + if (sock < 0) { + dropbear_exit("%s", error); + } + + /* Set up the host:port log */ + len = strlen(cli_opts.remotehost); + len += 10; /* 16 bit port and leeway*/ + hostandport = (char*)m_malloc(len); + snprintf(hostandport, len, "%s%d", + cli_opts.remotehost, cli_opts.remoteport); + + cli_session(sock, hostandport); + + /* not reached */ + return -1; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cli-session.c Mon Jul 26 02:44:20 2004 +0000 @@ -0,0 +1,101 @@ +#include "includes.h" +#include "session.h" +#include "dbutil.h" +#include "kex.h" +#include "ssh.h" +#include "packet.h" +#include "tcpfwd-direct.h" +#include "tcpfwd-remote.h" +#include "channel.h" +#include "random.h" + +static void cli_remoteclosed(); +static void cli_sessionloop(); + +struct clientsession cli_ses; /* GLOBAL */ + +static const packettype cli_packettypes[] = { + /* TYPE, AUTHREQUIRED, FUNCTION */ + {SSH_MSG_KEXINIT, recv_msg_kexinit}, + {SSH_MSG_KEXDH_REPLY, recv_msg_kexdh_reply}, // client + {SSH_MSG_NEWKEYS, recv_msg_newkeys}, + {SSH_MSG_CHANNEL_DATA, recv_msg_channel_data}, + {SSH_MSG_CHANNEL_WINDOW_ADJUST, recv_msg_channel_window_adjust}, + {SSH_MSG_GLOBAL_REQUEST, recv_msg_global_request_remotetcp}, + {SSH_MSG_CHANNEL_REQUEST, recv_msg_channel_request}, + {SSH_MSG_CHANNEL_OPEN, recv_msg_channel_open}, + {SSH_MSG_CHANNEL_EOF, recv_msg_channel_eof}, + {SSH_MSG_CHANNEL_CLOSE, recv_msg_channel_close}, + {SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation}, + {SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure}, + {0, 0} /* End */ +}; + +static const struct ChanType *cli_chantypes[] = { +// &clichansess, + /* &chan_tcpdirect etc, though need to only allow if we've requested + * that forwarding */ + NULL /* Null termination */ +}; +void cli_session(int sock, char* remotehost) { + + crypto_init(); + common_session_init(sock, remotehost); + + chaninitialise(cli_chantypes); + + /* For printing "remote host closed" for the user */ + session_remoteclosed = cli_remoteclosed; + + /* packet handlers */ + ses.packettypes = cli_packettypes; + + /* Ready to go */ + sessinitdone = 1; + + /* Exchange identification */ + session_identification(); + + seedrandom(); + + send_msg_kexinit(); + + /* XXX here we do stuff differently */ + + session_loop(cli_sessionloop); + + /* Not reached */ + + +} + +static void cli_sessionloop() { + + switch (cli_ses.state) { + + KEXINIT_RCVD: + /* We initiate the KEX. If DH wasn't the correct type, the KEXINIT + * negotiation would have failed. */ + send_msg_kexdh_init(); + cli_ses.state = KEXDH_INIT_SENT; + break; + + default: + break; + } + + if (cli_ses.donefirstkex && !cli_ses.authdone) { + + + +} + +/* called when the remote side closes the connection */ +static void cli_remoteclosed() { + + /* XXX TODO perhaps print a friendlier message if we get this but have + * already sent/received disconnect message(s) ??? */ + close(ses.sock); + ses.sock = -1; + dropbear_exit("%s closed the connection", ses.remotehost); +}
--- a/common-kex.c Tue Jul 20 12:06:37 2004 +0000 +++ b/common-kex.c Mon Jul 26 02:44:20 2004 +0000 @@ -193,7 +193,6 @@ ses.kexstate.sentnewkeys = 0; /* first_packet_follows */ - /* TODO - currently not handled */ ses.kexstate.firstfollows = 0; ses.kexstate.datatrans = 0; @@ -402,47 +401,54 @@ if (IS_DROPBEAR_CLIENT) { +#ifdef DROPBEAR_CLIENT - /* read the peer's choice of algos */ - cli_read_kex(); + /* read the peer's choice of algos */ + read_kex_algos(cli_buf_match_algo); - /* V_C, the client's version string (CR and NL excluded) */ + /* V_C, the client's version string (CR and NL excluded) */ buf_putstring(ses.kexhashbuf, (unsigned char*)LOCAL_IDENT, strlen(LOCAL_IDENT)); - /* V_S, the server's version string (CR and NL excluded) */ + /* V_S, the server's version string (CR and NL excluded) */ buf_putstring(ses.kexhashbuf, ses.remoteident, strlen((char*)ses.remoteident)); - /* I_C, the payload of the client's SSH_MSG_KEXINIT */ + /* I_C, the payload of the client's SSH_MSG_KEXINIT */ buf_putstring(ses.kexhashbuf, buf_getptr(ses.transkexinit, ses.transkexinit->len), ses.transkexinit->len); - /* I_S, the payload of the server's SSH_MSG_KEXINIT */ + /* I_S, the payload of the server's SSH_MSG_KEXINIT */ buf_setpos(ses.payload, 0); buf_putstring(ses.kexhashbuf, buf_getptr(ses.payload, ses.payload->len), ses.payload->len); + cli_ses.state = KEXINIT_RCVD; +#endif } else { + /* SERVER */ +#ifdef DROPBEAR_SERVER - /* read the peer's choice of algos */ - svr_read_kex(); - /* V_C, the client's version string (CR and NL excluded) */ + /* read the peer's choice of algos */ + read_kex_algos(svr_buf_match_algo); + /* V_C, the client's version string (CR and NL excluded) */ buf_putstring(ses.kexhashbuf, ses.remoteident, strlen((char*)ses.remoteident)); - /* V_S, the server's version string (CR and NL excluded) */ + /* V_S, the server's version string (CR and NL excluded) */ buf_putstring(ses.kexhashbuf, (unsigned char*)LOCAL_IDENT, strlen(LOCAL_IDENT)); - /* I_C, the payload of the client's SSH_MSG_KEXINIT */ + /* I_C, the payload of the client's SSH_MSG_KEXINIT */ buf_setpos(ses.payload, 0); buf_putstring(ses.kexhashbuf, buf_getptr(ses.payload, ses.payload->len), ses.payload->len); - /* I_S, the payload of the server's SSH_MSG_KEXINIT */ + /* I_S, the payload of the server's SSH_MSG_KEXINIT */ buf_putstring(ses.kexhashbuf, buf_getptr(ses.transkexinit, ses.transkexinit->len), ses.transkexinit->len); + ses.requirenext = SSH_MSG_KEXDH_INIT; +#endif } buf_free(ses.transkexinit); @@ -450,9 +456,233 @@ /* the rest of ses.kexhashbuf will be done after DH exchange */ ses.kexstate.recvkexinit = 1; - ses.requirenext = SSH_MSG_KEXDH_INIT; // ses.expecting = 0; // client matt TRACE(("leave recv_msg_kexinit")); } +/* Initialises and generate one side of the diffie-hellman key exchange values. + * See the ietf-secsh-transport draft, section 6, for details */ +void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv) { + + mp_int dh_p, dh_q, dh_g; + unsigned char randbuf[DH_P_LEN]; + int dh_q_len; + + TRACE(("enter send_msg_kexdh_reply")); + + m_mp_init_multi(&dh_g, &dh_p, &dh_q, dh_priv, dh_pub, NULL); + + /* read the prime and generator*/ + if (mp_read_unsigned_bin(&dh_p, (unsigned char*)dh_p_val, DH_P_LEN) + != MP_OKAY) { + dropbear_exit("Diffie-Hellman error"); + } + + if (mp_set_int(&dh_g, DH_G_VAL) != MP_OKAY) { + dropbear_exit("Diffie-Hellman error"); + } + + /* calculate q = (p-1)/2 */ + /* dh_priv is just a temp var here */ + if (mp_sub_d(&dh_p, 1, dh_priv) != MP_OKAY) { + dropbear_exit("Diffie-Hellman error"); + } + if (mp_div_2(dh_priv, &dh_q) != MP_OKAY) { + dropbear_exit("Diffie-Hellman error"); + } + + dh_q_len = mp_unsigned_bin_size(&dh_q); + + /* calculate our random value dh_y */ + do { + assert((unsigned int)dh_q_len <= sizeof(randbuf)); + genrandom(randbuf, dh_q_len); + if (mp_read_unsigned_bin(dh_priv, randbuf, dh_q_len) != MP_OKAY) { + dropbear_exit("Diffie-Hellman error"); + } + } while (mp_cmp(dh_priv, &dh_q) == MP_GT || mp_cmp_d(dh_priv, 0) != MP_GT); + + /* f = g^y mod p */ + if (mp_exptmod(&dh_g, dh_priv, &dh_p, dh_pub) != MP_OKAY) { + dropbear_exit("Diffie-Hellman error"); + } + mp_clear_multi(&dh_g, &dh_p, &dh_q, NULL); +} + +/* This function is fairly common between client/server, with some substitution + * of dh_e/dh_f etc. Hence these arguments: + * dh_pub_us is 'e' for the client, 'f' for the server. dh_pub_them is + * vice-versa. dh_priv is the x/y value corresponding to dh_pub_us */ +void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them, + sign_key *hostkey) { + + mp_int dh_p; + mp_int *dh_e = NULL, *dh_f = NULL; + hash_state hs; + + /* read the prime and generator*/ + mp_init(&dh_p); + if (mp_read_unsigned_bin(&dh_p, (unsigned char*)dh_p_val, DH_P_LEN) + != MP_OKAY) { + dropbear_exit("Diffie-Hellman error"); + } + + /* Check that dh_pub_them (dh_e or dh_f) is in the range [1, p-1] */ + if (mp_cmp(dh_pub_them, &dh_p) != MP_LT + || mp_cmp_d(dh_pub_them, 0) != MP_GT) { + dropbear_exit("Diffie-Hellman error"); + } + + /* K = e^y mod p = f^x mod p */ + ses.dh_K = (mp_int*)m_malloc(sizeof(mp_int)); + m_mp_init(ses.dh_K); + if (mp_exptmod(dh_pub_them, dh_priv, &dh_p, ses.dh_K) != MP_OKAY) { + dropbear_exit("Diffie-Hellman error"); + } + + /* clear no longer needed vars */ + mp_clear_multi(&dh_p, NULL); + + /* From here on, the code needs to work with the _same_ vars on each side, + * not vice-versaing for client/server */ + if (IS_DROPBEAR_CLIENT) { + dh_e = dh_pub_us; + dh_f = dh_pub_them; + } else { + dh_e = dh_pub_them; + dh_f = dh_pub_us; + } + + /* Create the remainder of the hash buffer, to generate the exchange hash */ + /* K_S, the host key */ + buf_put_pub_key(ses.kexhashbuf, hostkey, ses.newkeys->algo_hostkey); + /* e, exchange value sent by the client */ + buf_putmpint(ses.kexhashbuf, dh_e); + /* f, exchange value sent by the server */ + buf_putmpint(ses.kexhashbuf, dh_f); + /* K, the shared secret */ + buf_putmpint(ses.kexhashbuf, ses.dh_K); + + /* calculate the hash H to sign */ + sha1_init(&hs); + buf_setpos(ses.kexhashbuf, 0); + sha1_process(&hs, buf_getptr(ses.kexhashbuf, ses.kexhashbuf->len), + ses.kexhashbuf->len); + sha1_done(&hs, ses.hash); + buf_free(ses.kexhashbuf); + ses.kexhashbuf = NULL; + + /* first time around, we set the session_id to H */ + if (ses.session_id == NULL) { + /* create the session_id, this never needs freeing */ + ses.session_id = (unsigned char*)m_malloc(SHA1_HASH_SIZE); + memcpy(ses.session_id, ses.hash, SHA1_HASH_SIZE); + } +} + +/* read the other side's algo list. buf_match_algo is a callback to match + * algos for the client or server. */ +void read_kex_algos( + algo_type*(buf_match_algo)(buffer*buf, algo_type localalgos[], + int *goodguess)) { + + algo_type * algo; + char * erralgo = NULL; + + int goodguess = 0; + int allgood = 1; /* we AND this with each goodguess and see if its still + true after */ + + buf_incrpos(ses.payload, 16); /* start after the cookie */ + + ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context)); + + /* kex_algorithms */ + algo = buf_match_algo(ses.payload, sshkex, &goodguess); + allgood &= goodguess; + if (algo == NULL) { + erralgo = "kex"; + goto error; + } + ses.newkeys->algo_kex = algo->val; + + /* server_host_key_algorithms */ + algo = buf_match_algo(ses.payload, sshhostkey, &goodguess); + allgood &= goodguess; + if (algo == NULL) { + erralgo = "hostkey"; + goto error; + } + ses.newkeys->algo_hostkey = algo->val; + + /* encryption_algorithms_client_to_server */ + algo = buf_match_algo(ses.payload, sshciphers, &goodguess); + if (algo == NULL) { + erralgo = "enc c->s"; + goto error; + } + ses.newkeys->recv_algo_crypt = (struct dropbear_cipher*)algo->data; + + /* encryption_algorithms_server_to_client */ + algo = buf_match_algo(ses.payload, sshciphers, &goodguess); + if (algo == NULL) { + erralgo = "enc s->c"; + goto error; + } + ses.newkeys->trans_algo_crypt = (struct dropbear_cipher*)algo->data; + + /* mac_algorithms_client_to_server */ + algo = buf_match_algo(ses.payload, sshhashes, &goodguess); + if (algo == NULL) { + erralgo = "mac c->s"; + goto error; + } + ses.newkeys->recv_algo_mac = (struct dropbear_hash*)algo->data; + + /* mac_algorithms_server_to_client */ + algo = buf_match_algo(ses.payload, sshhashes, &goodguess); + if (algo == NULL) { + erralgo = "mac s->c"; + goto error; + } + ses.newkeys->trans_algo_mac = (struct dropbear_hash*)algo->data; + + /* compression_algorithms_client_to_server */ + algo = buf_match_algo(ses.payload, sshcompress, &goodguess); + if (algo == NULL) { + erralgo = "comp c->s"; + goto error; + } + ses.newkeys->recv_algo_comp = algo->val; + + /* compression_algorithms_server_to_client */ + algo = buf_match_algo(ses.payload, sshcompress, &goodguess); + if (algo == NULL) { + erralgo = "comp s->c"; + goto error; + } + ses.newkeys->trans_algo_comp = algo->val; + + /* languages_client_to_server */ + buf_eatstring(ses.payload); + + /* languages_server_to_client */ + buf_eatstring(ses.payload); + + /* first_kex_packet_follows */ + if (buf_getbyte(ses.payload)) { + ses.kexstate.firstfollows = 1; + /* if the guess wasn't good, we ignore the packet sent */ + if (!allgood) { + ses.ignorenext = 1; + } + } + + /* reserved for future extensions */ + buf_getint(ses.payload); + return; + +error: + dropbear_exit("no matching algo %s", erralgo); +}
--- a/common-session.c Tue Jul 20 12:06:37 2004 +0000 +++ b/common-session.c Mon Jul 26 02:44:20 2004 +0000 @@ -35,6 +35,7 @@ #include "channel.h" #include "atomicio.h" + struct sshsession ses; /* GLOBAL */ /* need to know if the session struct has been initialised, this way isn't the @@ -44,19 +45,18 @@ /* this is set when we get SIGINT or SIGTERM, the handler is in main.c */ int exitflag = 0; /* GLOBAL */ -static int ident_readln(int fd, char* buf, int count); - - void(*session_remoteclosed)() = NULL; +static void checktimeouts(); +static int ident_readln(int fd, char* buf, int count); + /* called only at the start of a session, set up initial state */ -void common_session_init(int sock) { +void common_session_init(int sock, char* remotehost) { TRACE(("enter session_init")); - ses.remoteaddr = NULL; - ses.remotehost = NULL; + ses.remotehost = remotehost; ses.sock = sock; ses.maxfd = sock; @@ -114,6 +114,86 @@ TRACE(("leave session_init")); } +void session_loop(void(*loophandler)()) { + + fd_set readfd, writefd; + struct timeval timeout; + int val; + + /* main loop, select()s for all sockets in use */ + for(;;) { + + timeout.tv_sec = SELECT_TIMEOUT; + timeout.tv_usec = 0; + FD_ZERO(&writefd); + FD_ZERO(&readfd); + assert(ses.payload == NULL); + if (ses.sock != -1) { + FD_SET(ses.sock, &readfd); + if (!isempty(&ses.writequeue)) { + FD_SET(ses.sock, &writefd); + } + } + + /* set up for channels which require reading/writing */ + if (ses.dataallowed) { + setchannelfds(&readfd, &writefd); + } + val = select(ses.maxfd+1, &readfd, &writefd, NULL, &timeout); + + if (exitflag) { + dropbear_exit("Terminated by signal"); + } + + if (val < 0) { + if (errno == EINTR) { + continue; + } else { + dropbear_exit("Error in select"); + } + } + + /* check for auth timeout, rekeying required etc */ + checktimeouts(); + + if (val == 0) { + /* timeout */ + TRACE(("select timeout")); + continue; + } + + /* process session socket's incoming/outgoing data */ + if (ses.sock != -1) { + if (FD_ISSET(ses.sock, &writefd) && !isempty(&ses.writequeue)) { + write_packet(); + } + + if (FD_ISSET(ses.sock, &readfd)) { + read_packet(); + } + + /* Process the decrypted packet. After this, the read buffer + * will be ready for a new packet */ + if (ses.payload != NULL) { + process_packet(); + } + } + + /* process pipes etc for the channels, ses.dataallowed == 0 + * during rekeying ) */ + if (ses.dataallowed) { + channelio(&readfd, &writefd); + } + + if (loophandler) { + loophandler(); + } + + } /* for(;;) */ + + /* Not reached */ +} + /* clean up a session on exit */ void common_session_cleanup() { @@ -134,35 +214,7 @@ TRACE(("leave session_cleanup")); } -/* Check all timeouts which are required. Currently these are the time for - * user authentication, and the automatic rekeying. */ -void checktimeouts() { - struct timeval tv; - long secs; - - if (gettimeofday(&tv, 0) < 0) { - dropbear_exit("Error getting time"); - } - - secs = tv.tv_sec; - - if (ses.connecttimeout != 0 && secs > ses.connecttimeout) { - dropbear_close("Timeout before auth"); - } - - /* we can't rekey if we haven't done remote ident exchange yet */ - if (ses.remoteident == NULL) { - return; - } - - if (!ses.kexstate.sentkexinit - && (secs - ses.kexstate.lastkextime >= KEX_REKEY_TIMEOUT - || ses.kexstate.datarecv+ses.kexstate.datatrans >= KEX_REKEY_DATA)){ - TRACE(("rekeying after timeout or max data reached")); - send_msg_kexinit(); - } -} void session_identification() { /* max length of 255 chars */ @@ -268,3 +320,33 @@ return pos+1; } +/* Check all timeouts which are required. Currently these are the time for + * user authentication, and the automatic rekeying. */ +static void checktimeouts() { + + struct timeval tv; + long secs; + + if (gettimeofday(&tv, 0) < 0) { + dropbear_exit("Error getting time"); + } + + secs = tv.tv_sec; + + if (ses.connecttimeout != 0 && secs > ses.connecttimeout) { + dropbear_close("Timeout before auth"); + } + + /* we can't rekey if we haven't done remote ident exchange yet */ + if (ses.remoteident == NULL) { + return; + } + + if (!ses.kexstate.sentkexinit + && (secs - ses.kexstate.lastkextime >= KEX_REKEY_TIMEOUT + || ses.kexstate.datarecv+ses.kexstate.datatrans >= KEX_REKEY_DATA)){ + TRACE(("rekeying after timeout or max data reached")); + send_msg_kexinit(); + } +} +
--- a/dbutil.c Tue Jul 20 12:06:37 2004 +0000 +++ b/dbutil.c Mon Jul 26 02:44:20 2004 +0000 @@ -113,6 +113,95 @@ } #endif /* DEBUG_TRACE */ +/* Connect via TCP to a host. Connection will try ipv4 or ipv6, will + * return immediately if nonblocking is set */ +int connect_remote(const char* remotehost, const char* remoteport, + int nonblocking, char ** errstring) { + + struct addrinfo *res0 = NULL, *res = NULL, hints; + int sock; + int err; + + TRACE(("enter connect_remote")); + + if (errstring != NULL) { + *errstring = NULL; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = PF_UNSPEC; + + err = getaddrinfo(remotehost, remoteport, &hints, &res0); + if (err) { + if (errstring != NULL && *errstring == NULL) { + int len; + len = 20 + strlen(gai_strerror(err)); + *errstring = (char*)m_malloc(len); + snprintf(*errstring, len, "Error resolving: %s", gai_strerror(err)); + } + TRACE(("Error resolving: %s", gai_strerror(err))); + return -1; + } + + sock = -1; + err = EADDRNOTAVAIL; + for (res = res0; res; res = res->ai_next) { + + sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (sock < 0) { + err = errno; + continue; + } + + if (nonblocking) { + if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { + close(sock); + sock = -1; + if (errstring != NULL && *errstring == NULL) { + *errstring = m_strdup("Failed non-blocking"); + } + TRACE(("Failed non-blocking: %s", strerror(errno))); + continue; + } + } + + if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) { + if (errno == EINPROGRESS) { + TRACE(("Connect in progress")); + break; + } else { + err = errno; + close(sock); + sock = -1; + continue; + } + } + + break; /* Success */ + } + + if (sock < 0) { + /* Failed */ + if (errstring != NULL && *errstring == NULL) { + int len; + len = 20 + strlen(strerror(err)); + *errstring = (char*)m_malloc(len); + snprintf(*errstring, len, "Error connecting: %s", strerror(err)); + } + TRACE(("Error connecting: %s", strerror(err))); + } else { + /* Success */ + /* (err is used as a dummy var here) */ + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&err, sizeof(err)); + } + + freeaddrinfo(res0); + + TRACE(("leave connect_remote: sock %d", sock)); + return sock; +} + /* Return a string representation of the socket address passed. The return * value is allocated with malloc() */ unsigned char * getaddrstring(struct sockaddr * addr) { @@ -304,3 +393,4 @@ *p++ = 0x66; } } +
--- a/dbutil.h Tue Jul 20 12:06:37 2004 +0000 +++ b/dbutil.h Mon Jul 26 02:44:20 2004 +0000 @@ -45,6 +45,8 @@ #endif char * stripcontrol(const char * text); unsigned char * getaddrstring(struct sockaddr * addr); +int connect_remote(const char* remotehost, const char* remoteport, + int nonblocking, char ** errstring); char* getaddrhostname(struct sockaddr * addr); int buf_readfile(buffer* buf, const char* filename); @@ -56,4 +58,7 @@ void __m_free(void* ptr); void m_burn(void* data, unsigned int len); +/* Used to force mp_ints to be initialised */ +#define DEF_MP_INT(X) mp_int X = {0, 0, 0, NULL} + #endif /* _DBUTIL_H_ */
--- a/debug.h Tue Jul 20 12:06:37 2004 +0000 +++ b/debug.h Mon Jul 26 02:44:20 2004 +0000 @@ -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
--- a/kex.h Tue Jul 20 12:06:37 2004 +0000 +++ b/kex.h Mon Jul 26 02:44:20 2004 +0000 @@ -26,17 +26,25 @@ #define _KEX_H_ #include "includes.h" +#include "algo.h" void send_msg_kexinit(); void recv_msg_kexinit(); -void send_dh_kex(); -void recv_msg_kexdh_init(); void send_msg_newkeys(); void recv_msg_newkeys(); void kexinitialise(); +void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv); +void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them, + sign_key *hostkey); -void svr_read_kex(); -void cli_read_kex(); +void read_kex_algos( + algo_type*(buf_match_algo)(buffer*buf, algo_type localalgos[], + int *goodguess)); + +void recv_msg_kexdh_init(); // server + +void send_msg_kexdh_init(); // client +void recv_msg_kexdh_reply(); // client extern const unsigned char dh_p_val[]; #define DH_P_LEN 128 /* The length of the dh_p_val array */
--- a/main.c Tue Jul 20 12:06:37 2004 +0000 +++ b/main.c Mon Jul 26 02:44:20 2004 +0000 @@ -240,8 +240,10 @@ if (m_close(childpipe[0]) == DROPBEAR_FAILURE) { dropbear_exit("Couldn't close socket"); } + /* start the session */ - svr_session(childsock, childpipe[1], &remoteaddr); + svr_session(childsock, childpipe[1], + getaddrhostname(&remoteaddr)); /* don't return */ assert(0); }
--- a/options.h Tue Jul 20 12:06:37 2004 +0000 +++ b/options.h Mon Jul 26 02:44:20 2004 +0000 @@ -30,7 +30,7 @@ * parts are to allow for commandline -DDROPBEAR_XXX options etc. ******************************************************************/ #define DROPBEAR_SERVER -/* #define DROPBEAR_CLIENT */ +//#define DROPBEAR_CLIENT #ifndef DROPBEAR_PORT #define DROPBEAR_PORT 22 @@ -48,6 +48,7 @@ * perhaps 20% slower for pubkey operations (it is probably worth experimenting * if you want to use this) */ /*#define NO_FAST_EXPTMOD*/ +#define DROPBEAR_SMALL_CODE /* Enable X11 Forwarding */ #define ENABLE_X11FWD @@ -181,7 +182,7 @@ *******************************************************************/ #ifndef DROPBEAR_VERSION -#define DROPBEAR_VERSION "0.41" +#define DROPBEAR_VERSION "0.41-and-client" #endif #define LOCAL_IDENT "SSH-2.0-dropbear_" DROPBEAR_VERSION
--- a/process-packet.c Tue Jul 20 12:06:37 2004 +0000 +++ b/process-packet.c Mon Jul 26 02:44:20 2004 +0000 @@ -104,9 +104,11 @@ */ /* check that we aren't expecting a particular packet */ +#if 0 if (cli_ses.expecting && cli_ses.expecting == type) { cli_ses.expecting = 0; } +#endif } #endif
--- a/session.h Tue Jul 20 12:06:37 2004 +0000 +++ b/session.h Mon Jul 26 02:44:20 2004 +0000 @@ -26,6 +26,7 @@ #define _SESSION_H_ #include "includes.h" +#include "options.h" #include "buffer.h" #include "signkey.h" #include "kex.h" @@ -38,7 +39,8 @@ extern int sessinitdone; /* Is set to 0 somewhere */ extern int exitflag; -void common_session_init(int sock); +void common_session_init(int sock, char* remotehost); +void session_loop(void(*loophandler)()); void common_session_cleanup(); void checktimeouts(); void session_identification(); @@ -46,10 +48,14 @@ extern void(*session_remoteclosed)(); /* Server */ -void svr_session(int sock, int childpipe, struct sockaddr *remoteaddr); +void svr_session(int sock, int childpipe, char *remotehost); void svr_dropbear_exit(int exitcode, const char* format, va_list param); void svr_dropbear_log(int priority, const char* format, va_list param); +/* Client */ +void cli_session(int sock, char *remotehost); +void cli_dropbear_exit(int exitcode, const char* format, va_list param); +void cli_dropbear_log(int priority, const char* format, va_list param); struct key_context { @@ -85,8 +91,8 @@ int sock; - struct sockaddr *remoteaddr; unsigned char *remotehost; /* the peer hostname */ + unsigned char *remoteident; int maxfd; /* the maximum file descriptor to check with select() */ @@ -166,11 +172,20 @@ }; +typedef enum { + NOTHING, + KEXINIT_RCVD, + KEXDH_INIT_SENT, + KEXDH_REPLY_RCVD, +} cli_state; struct clientsession { + mp_int *dh_e, *dh_x; /* Used during KEX */ + cli_state state; /* Used to progress the KEX/auth/channelsession etc */ int something; /* XXX */ + unsigned donefirstkex : 1; /* Set when we set sentnewkeys, never reset */ }; @@ -182,7 +197,7 @@ #endif /* DROPBEAR_SERVER */ #ifdef DROPBEAR_CLIENT -extern struct serversession cli_ses; +extern struct clientsession cli_ses; #endif /* DROPBEAR_CLIENT */ #endif /* _SESSION_H_ */
--- a/signkey.c Tue Jul 20 12:06:37 2004 +0000 +++ b/signkey.c Mon Jul 26 02:44:20 2004 +0000 @@ -45,7 +45,8 @@ } /* returns DROPBEAR_SUCCESS on success, DROPBEAR_FAILURE on fail. - * type is set to hold the type returned */ + * type should be set by the caller to specify the type to read, and + * on return is set to the type read (useful when type = _ANY) */ int buf_get_pub_key(buffer *buf, sign_key *key, int *type) { unsigned char* ident;
--- a/svr-algo.c Tue Jul 20 12:06:37 2004 +0000 +++ b/svr-algo.c Mon Jul 26 02:44:20 2004 +0000 @@ -16,6 +16,8 @@ unsigned int count, i, j; algo_type * ret = NULL; + *goodguess = 0; + /* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */ algolist = buf_getstring(buf, &len); /* Debug this */ @@ -57,8 +59,6 @@ /* set if it was a good guess */ if (i == 0 && j == 0) { *goodguess = 1; - } else { - *goodguess = 0; } /* set the algo to return */ ret = &localalgos[j];
--- a/svr-kex.c Tue Jul 20 12:06:37 2004 +0000 +++ b/svr-kex.c Mon Jul 26 02:44:20 2004 +0000 @@ -70,87 +70,15 @@ * See the ietf-secsh-transport draft, section 6, for details */ static void send_msg_kexdh_reply(mp_int *dh_e) { - mp_int dh_p, dh_q, dh_g, dh_y, dh_f; - unsigned char randbuf[DH_P_LEN]; - int dh_q_len; - hash_state hs; + mp_int dh_y, dh_f; TRACE(("enter send_msg_kexdh_reply")); - m_mp_init_multi(&dh_g, &dh_p, &dh_q, &dh_y, &dh_f, NULL); - - /* read the prime and generator*/ - if (mp_read_unsigned_bin(&dh_p, (unsigned char*)dh_p_val, DH_P_LEN) - != MP_OKAY) { - dropbear_exit("Diffie-Hellman error"); - } - - if (mp_set_int(&dh_g, DH_G_VAL) != MP_OKAY) { - dropbear_exit("Diffie-Hellman error"); - } - - /* calculate q = (p-1)/2 */ - if (mp_sub_d(&dh_p, 1, &dh_y) != MP_OKAY) { /*dh_y is just a temp var here*/ - dropbear_exit("Diffie-Hellman error"); - } - if (mp_div_2(&dh_y, &dh_q) != MP_OKAY) { - dropbear_exit("Diffie-Hellman error"); - } - - dh_q_len = mp_unsigned_bin_size(&dh_q); - - /* calculate our random value dh_y */ - do { - assert((unsigned int)dh_q_len <= sizeof(randbuf)); - genrandom(randbuf, dh_q_len); - if (mp_read_unsigned_bin(&dh_y, randbuf, dh_q_len) != MP_OKAY) { - dropbear_exit("Diffie-Hellman error"); - } - } while (mp_cmp(&dh_y, &dh_q) == MP_GT || mp_cmp_d(&dh_y, 0) != MP_GT); - - /* f = g^y mod p */ - if (mp_exptmod(&dh_g, &dh_y, &dh_p, &dh_f) != MP_OKAY) { - dropbear_exit("Diffie-Hellman error"); - } - mp_clear(&dh_g); + gen_kexdh_vals(&dh_f, &dh_y); - /* K = e^y mod p */ - ses.dh_K = (mp_int*)m_malloc(sizeof(mp_int)); - m_mp_init(ses.dh_K); - if (mp_exptmod(dh_e, &dh_y, &dh_p, ses.dh_K) != MP_OKAY) { - dropbear_exit("Diffie-Hellman error"); - } - - /* clear no longer needed vars */ - mp_clear_multi(&dh_y, &dh_p, &dh_q, NULL); + kexdh_comb_key(&dh_f, &dh_y, dh_e, svr_opts.hostkey); + mp_clear(&dh_y); - /* Create the remainder of the hash buffer, to generate the exchange hash */ - /* K_S, the host key */ - buf_put_pub_key(ses.kexhashbuf, svr_opts.hostkey, - ses.newkeys->algo_hostkey); - /* e, exchange value sent by the client */ - buf_putmpint(ses.kexhashbuf, dh_e); - /* f, exchange value sent by the server */ - buf_putmpint(ses.kexhashbuf, &dh_f); - /* K, the shared secret */ - buf_putmpint(ses.kexhashbuf, ses.dh_K); - - /* calculate the hash H to sign */ - sha1_init(&hs); - buf_setpos(ses.kexhashbuf, 0); - sha1_process(&hs, buf_getptr(ses.kexhashbuf, ses.kexhashbuf->len), - ses.kexhashbuf->len); - sha1_done(&hs, ses.hash); - buf_free(ses.kexhashbuf); - ses.kexhashbuf = NULL; - - /* first time around, we set the session_id to H */ - if (ses.session_id == NULL) { - /* create the session_id, this never needs freeing */ - ses.session_id = (unsigned char*)m_malloc(SHA1_HASH_SIZE); - memcpy(ses.session_id, ses.hash, SHA1_HASH_SIZE); - } - /* we can start creating the kexdh_reply packet */ CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_REPLY); @@ -171,105 +99,3 @@ TRACE(("leave send_msg_kexdh_reply")); } -/* read the client's choice of algorithms */ -void svr_read_kex() { - - algo_type * algo; - char * erralgo = NULL; - - int goodguess = 0; - int allgood = 1; /* we AND this with each goodguess and see if its still - true after */ - - buf_incrpos(ses.payload, 16); /* start after the cookie */ - - ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context)); - - /* kex_algorithms */ - algo = svr_buf_match_algo(ses.payload, sshkex, &goodguess); - allgood &= goodguess; - if (algo == NULL) { - erralgo = "kex"; - goto error; - } - ses.newkeys->algo_kex = algo->val; - - /* server_host_key_algorithms */ - algo = svr_buf_match_algo(ses.payload, sshhostkey, &goodguess); - allgood &= goodguess; - if (algo == NULL) { - erralgo = "hostkey"; - goto error; - } - ses.newkeys->algo_hostkey = algo->val; - - /* encryption_algorithms_client_to_server */ - algo = svr_buf_match_algo(ses.payload, sshciphers, &goodguess); - if (algo == NULL) { - erralgo = "enc c->s"; - goto error; - } - ses.newkeys->recv_algo_crypt = (struct dropbear_cipher*)algo->data; - - /* encryption_algorithms_server_to_client */ - algo = svr_buf_match_algo(ses.payload, sshciphers, &goodguess); - if (algo == NULL) { - erralgo = "enc s->c"; - goto error; - } - ses.newkeys->trans_algo_crypt = (struct dropbear_cipher*)algo->data; - - /* mac_algorithms_client_to_server */ - algo = svr_buf_match_algo(ses.payload, sshhashes, &goodguess); - if (algo == NULL) { - erralgo = "mac c->s"; - goto error; - } - ses.newkeys->recv_algo_mac = (struct dropbear_hash*)algo->data; - - /* mac_algorithms_server_to_client */ - algo = svr_buf_match_algo(ses.payload, sshhashes, &goodguess); - if (algo == NULL) { - erralgo = "mac s->c"; - goto error; - } - ses.newkeys->trans_algo_mac = (struct dropbear_hash*)algo->data; - - /* compression_algorithms_client_to_server */ - algo = svr_buf_match_algo(ses.payload, sshcompress, &goodguess); - if (algo == NULL) { - erralgo = "comp c->s"; - goto error; - } - ses.newkeys->recv_algo_comp = algo->val; - - /* compression_algorithms_server_to_client */ - algo = svr_buf_match_algo(ses.payload, sshcompress, &goodguess); - if (algo == NULL) { - erralgo = "comp s->c"; - goto error; - } - ses.newkeys->trans_algo_comp = algo->val; - - /* languages_client_to_server */ - buf_eatstring(ses.payload); - - /* languages_server_to_client */ - buf_eatstring(ses.payload); - - /* first_kex_packet_follows */ - if (buf_getbyte(ses.payload)) { - ses.kexstate.firstfollows = 1; - /* if the guess wasn't good, we ignore the packet sent */ - if (!allgood) { - ses.ignorenext = 1; - } - } - - /* reserved for future extensions */ - buf_getint(ses.payload); - return; - -error: - dropbear_exit("no matching algo %s", erralgo); -}
--- a/svr-session.c Tue Jul 20 12:06:37 2004 +0000 +++ b/svr-session.c Mon Jul 26 02:44:20 2004 +0000 @@ -50,7 +50,7 @@ {SSH_MSG_SERVICE_REQUEST, recv_msg_service_request}, // server {SSH_MSG_USERAUTH_REQUEST, recv_msg_userauth_request}, //server {SSH_MSG_KEXINIT, recv_msg_kexinit}, - {SSH_MSG_KEXDH_INIT, recv_msg_kexdh_init}, + {SSH_MSG_KEXDH_INIT, recv_msg_kexdh_init}, // server {SSH_MSG_NEWKEYS, recv_msg_newkeys}, {SSH_MSG_CHANNEL_DATA, recv_msg_channel_data}, {SSH_MSG_CHANNEL_WINDOW_ADJUST, recv_msg_channel_window_adjust}, @@ -70,17 +70,12 @@ NULL /* Null termination is mandatory. */ }; -void svr_session(int sock, int childpipe, struct sockaddr* remoteaddr) { +void svr_session(int sock, int childpipe, char* remotehost) { - fd_set readfd, writefd; struct timeval timeout; - int val; crypto_init(); - common_session_init(sock); - - ses.remoteaddr = remoteaddr; - ses.remotehost = getaddrhostname(remoteaddr); + common_session_init(sock, remotehost); /* Initialise server specific parts of the session */ svr_ses.childpipe = childpipe; @@ -111,75 +106,12 @@ /* start off with key exchange */ send_msg_kexinit(); - FD_ZERO(&readfd); - FD_ZERO(&writefd); - - /* main loop, select()s for all sockets in use */ - for(;;) { - - timeout.tv_sec = SELECT_TIMEOUT; - timeout.tv_usec = 0; - FD_ZERO(&writefd); - FD_ZERO(&readfd); - assert(ses.payload == NULL); - if (ses.sock != -1) { - FD_SET(ses.sock, &readfd); - if (!isempty(&ses.writequeue)) { - FD_SET(ses.sock, &writefd); - } - } - - /* set up for channels which require reading/writing */ - if (ses.dataallowed) { - setchannelfds(&readfd, &writefd); - } - val = select(ses.maxfd+1, &readfd, &writefd, NULL, &timeout); + /* Run the main for loop. NULL is for the dispatcher - only the client + * code makes use of it */ + session_loop(NULL); - if (exitflag) { - dropbear_exit("Terminated by signal"); - } - - if (val < 0) { - if (errno == EINTR) { - continue; - } else { - dropbear_exit("Error in select"); - } - } - - /* check for auth timeout, rekeying required etc */ - checktimeouts(); - - if (val == 0) { - /* timeout */ - TRACE(("select timeout")); - continue; - } + /* Not reached */ - /* process session socket's incoming/outgoing data */ - if (ses.sock != -1) { - if (FD_ISSET(ses.sock, &writefd) && !isempty(&ses.writequeue)) { - write_packet(); - } - - if (FD_ISSET(ses.sock, &readfd)) { - read_packet(); - } - - /* Process the decrypted packet. After this, the read buffer - * will be ready for a new packet */ - if (ses.payload != NULL) { - process_packet(); - } - } - - /* process pipes etc for the channels, ses.dataallowed == 0 - * during rekeying ) */ - if (ses.dataallowed) { - channelio(&readfd, &writefd); - } - - } /* for(;;) */ } /* failure exit - format must be <= 100 chars */
--- a/tcpfwd-direct.c Tue Jul 20 12:06:37 2004 +0000 +++ b/tcpfwd-direct.c Mon Jul 26 02:44:20 2004 +0000 @@ -27,6 +27,7 @@ unsigned int destport; unsigned char* orighost = NULL; unsigned int origport; + char portstring[6]; int sock; int len; int ret = DROPBEAR_FAILURE; @@ -58,7 +59,8 @@ goto out; } - sock = newtcp(desthost, destport); + snprintf(portstring, sizeof(portstring), "%d", destport); + sock = connect_remote(desthost, portstring, 1, NULL); if (sock < 0) { TRACE(("leave newtcpdirect: sock failed")); goto out; @@ -86,6 +88,7 @@ * returned will need to be checked for success when it is first written. * Similarities with OpenSSH's connect_to() are not coincidental. * Returns -1 on failure */ +#if 0 static int newtcp(const char * host, int port) { int sock = -1; @@ -152,4 +155,5 @@ setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&val, sizeof(val)); return sock; } +#endif #endif /* DISABLE_TCPFWD_DIRECT */