From e444f0cfe67c71d3f38854f27cefae9aea6c4cd9 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Mon, 18 Jul 2005 14:32:52 +0000 Subject: [PATCH 01/45] - progress towards client agent forwarding (incomplete and does not compile) --HG-- branch : agent-client extra : convert_revision : 01038174ec27245b51bd43a66c01ad930880f67b --- agentfwd.h | 9 ++ auth.h | 5 + cli-agentfwd.c | 245 +++++++++++++++++++++++++++++++++++++++++++++++ cli-authpubkey.c | 7 ++ cli-runopts.c | 13 +++ cli-session.c | 3 + dbutil.c | 27 ++++-- dbutil.h | 3 + options.h | 7 +- random.c | 6 +- runopts.h | 5 + ssh.h | 11 +++ svr-agentfwd.c | 4 +- 13 files changed, 328 insertions(+), 17 deletions(-) create mode 100644 cli-agentfwd.c diff --git a/agentfwd.h b/agentfwd.h index a0f675d..3684455 100644 --- a/agentfwd.h +++ b/agentfwd.h @@ -29,15 +29,24 @@ #include "chansession.h" #include "channel.h" +/* An agent reply can be reasonably large, as it can + * contain a list of all public keys held by the agent. + * 10000 is arbitrary */ +#define MAX_AGENT_REPLY 10000 + int agentreq(struct ChanSess * chansess); void agentsetauth(struct ChanSess *chansess); void agentcleanup(struct ChanSess * chansess); void agentset(struct ChanSess *chansess); +SignKeyList * load_agent_keys(); + #ifdef __hpux #define seteuid(a) setresuid(-1, (a), -1) #define setegid(a) setresgid(-1, (a), -1) #endif +extern const struct ChanSess chan_cli_agent; + #endif /* DROPBEAR_AGENTFWD */ #endif /* _AGENTFWD_H_ */ diff --git a/auth.h b/auth.h index 548e017..2bfd428 100644 --- a/auth.h +++ b/auth.h @@ -84,6 +84,10 @@ struct AuthState { }; +/* Sources for signing keys */ +#define SIGNKEY_SOURCE_RAW_FILE 1 +#define SIGNKEY_SOURCE_AGENT 21 + struct SignKeyList; /* A singly linked list of signing keys */ struct SignKeyList { @@ -91,6 +95,7 @@ struct SignKeyList { sign_key *key; int type; /* The type of key */ struct SignKeyList *next; + int source; /* filename? or the buffer? for encrypted keys, so we can later get * the private key portion */ diff --git a/cli-agentfwd.c b/cli-agentfwd.c new file mode 100644 index 0000000..f0fe385 --- /dev/null +++ b/cli-agentfwd.c @@ -0,0 +1,245 @@ +/* + * Dropbear - a SSH2 server + * + * Copyright (c) 2005 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. */ + +/* The basic protocol use to communicate with the agent is defined in + * draft-ylonen-ssh-protocol-00.txt, with the ssh2 extensions defined through + * openssh's implementation. */ + +#include "includes.h" + +#ifdef ENABLE_CLI_AGENTFWD + +#include "agentfwd.h" +#include "session.h" +#include "ssh.h" +#include "dbutil.h" +#include "chansession.h" +#include "channel.h" +#include "packet.h" +#include "buffer.h" +#include "random.h" +#include "listener.h" +#include "runopts.h" +#include "atomicio.h" +#include "signkey.h" +#include "auth.h" + +static int new_agent_chan(struct Channel * channel); + +const struct ChanType chan_cli_agent = { + 0, /* sepfds */ + "auth-agent@openssh.com", + new_agent_chan, + NULL, + NULL, + NULL +}; + +static int connect_agent() { + + int fd = -1; + char* agent_sock = NULL; + + agent_sock = getenv("SSH_AUTH_SOCK"); + if (agent_sock == NULL) + return -1; + + fd = connect_unix(agent_sock); + + return fd; +} + +// handle a request for a connection to the locally running ssh-agent +// or forward. +static int new_agent_chan(struct Channel * channel) { + + int fd = -1; + + if (!cli_opts.agent_fwd) + return SSH_OPEN_ADMINISTRATIVELY_PROHIBITED; + + fd = connect_agent(); + + setnonblocking(fd); + + ses.maxfd = MAX(ses.maxfd, fd); + + channel->infd = fd; + channel->outfd = fd; + + // success + return 0; +} + +/* Sends a request to the agent, returning a newly allocated buffer + * with the response */ +/* Packet format (from draft-ylonen) + 4 bytes Length, msb first. Does not include length itself. + 1 byte Packet type. The value 255 is reserved for future extensions. + data Any data, depending on packet type. Encoding as in the ssh packet + protocol. + + In this case, data is always empty +*/ +static buffer * agent_request(int fd, unsigned char type) { + + buffer * payload = NULL; + buffer * inbuf = NULL; + size_t readlen = 0; + ssize_t ret; + + payload = buf_new(4 + 1); + + buf_putint(payload, 1); + buf_putbyte(payload, type); + + ret = atomicio(write, fd, buf_getptr(payload, payload->len), payload->len); + if ((size_t)ret != payload->len) { + TRACE(("write failed for agent_request")) + goto out; + } + + buf_free(payload); + payload = NULL; + + /* Now we read the response */ + inbuf = buf_new(4); + ret = atomicio(read, fd, buf_getwriteptr(inbuf, 4), 4); + if (ret != 4) { + TRACE(("read of length failed for agent_request")) + goto out; + } + + readlen = buf_getint(inbuf); + if (readlen > MAX_AGENT_REPLY) { + TRACE(("agent reply is too big")); + goto out; + } + + buf_resize(inbuf, readlen); + ret = atomicio(read, fd, buf_getwriteptr(inbuf, readlen), readlen); + if ((size_t)ret != readlen) { + TRACE(("read of data failed for agent_request")) + goto out; + } + +out: + if (payload) + buf_free(payload); + + return inbuf; +} + +static SignKeyList * agent_get_key_list(int fd) +{ + buffer * inbuf = NULL; + unsigned int num = 0; + unsigned char packet_type; + unsigned int i; + struct SignKeyList *retkey = NULL, *key = NULL; + int ret; + + inbuf = agent_request(fd, SSH2_AGENTC_REQUEST_IDENTITIES); + if (!inbuf) { + goto out; + } + + /* The reply has a format of: + * byte packet_type + * int num_keys + * + * string keyblob1 + * string comment1 + * ... + * string keyblob(n) + * string comment(n) + */ + packet_type = buf_getbyte(inbuf); + if (packet_type != SSH2_AGENT_IDENTITIES_ANSWER) { + goto out; + } + + num = buf_getint(inbuf); + for (i = 0; i < num; i++) { + sign_key * pubkey = NULL; + char key_type = DROPBEAR_SIGNKEY_ANY; + struct SignKeyList *nextkey = NULL; + + nextkey = (struct SignKeyList*)m_malloc(sizeof(struct SignKeyList)); + if (key) + key->next = nextkey; + else + retkey = nextkey; + key = nextkey; + + pubkey = new_sign_key(); + ret = buf_get_pub_key(inbuf, pubkey, &key_type); + if (ret != DROPBEAR_SUCCESS) { + /* This is slack, properly would cleanup vars etc */ + dropbear_exit("Bad pubkey received from agent"); + } + + key->key = pubkey; + key->next = NULL; + key->type = key_type; + key->source = SIGNKEY_SOURCE_AGENT; + + /* We'll ignore the comment */ + buf_eatstring(inbuf); + } + +out: + if (inbuf) { + buf_free(inbuf); + inbuf = NULL; + } + + return retkey; +} + +/* return DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +SignKeyList * load_agent_keys() +{ + + SignKeyList * ret_list; + int fd; + fd = connect_agent(); + if (fd < 0) { + dropbear_log(LOG_INFO, "Failed to connect to agent"); + return NULL; + } + + ret_list = agent_get_key_list(fd); + close(fd); +} + +// general procedure: +// - get the list of keys from the agent +// - foreach, send a dummy userauth_pubkey message to the server and see +// if it lets us in +// - if it does, sign and auth +// - if not, repeat. +// + +#endif diff --git a/cli-authpubkey.c b/cli-authpubkey.c index 9d36bc3..2cc92f7 100644 --- a/cli-authpubkey.c +++ b/cli-authpubkey.c @@ -172,6 +172,13 @@ int cli_auth_pubkey() { TRACE(("enter cli_auth_pubkey")) + if (cli_opts.pubkeys == NULL && + cli_opts.agent_fwd && + !cli_opts.agent_keys_loaded) { + /* get the list of available keys from the agent */ + load_agent_keys(&cli_opts.pubkeys); + } + if (cli_opts.privkeys != NULL) { /* Send a trial request */ send_msg_userauth_pubkey(cli_opts.privkeys->key, diff --git a/cli-runopts.c b/cli-runopts.c index 285c51d..016dd95 100644 --- a/cli-runopts.c +++ b/cli-runopts.c @@ -52,6 +52,9 @@ static void printhelp() { #ifdef ENABLE_CLI_PUBKEY_AUTH "-i (multiple allowed)\n" #endif +#ifdef ENABLE_CLI_AGENTFWD + "-A Enable agent auth forwarding\n" +#endif #ifdef ENABLE_CLI_LOCALTCPFWD "-L Local port forwarding\n" #endif @@ -96,6 +99,10 @@ void cli_getopts(int argc, char ** argv) { #endif #ifdef ENABLE_CLI_REMOTETCPFWD cli_opts.remotefwds = NULL; +#endif +#ifdef ENABLE_CLI_AGENTFWD + cli_opts.agent_fwd = 0; + cli_opts.agent_keys_loaded = 0; #endif opts.nolocaltcp = 0; opts.noremotetcp = 0; @@ -180,6 +187,11 @@ void cli_getopts(int argc, char ** argv) { printhelp(); exit(EXIT_SUCCESS); break; +#ifdef ENABLE_CLI_AGENTFWD + case 'A': + cli_opts.agent_fwd = 1; + break; +#endif #ifdef DEBUG_TRACE case 'v': debug_trace = 1; @@ -288,6 +300,7 @@ static void loadidentityfile(const char* filename) { nextkey->key = key; nextkey->next = cli_opts.privkeys; nextkey->type = keytype; + nextkey->source = SIGNKEY_SOURCE_RAW_FILE; cli_opts.privkeys = nextkey; } } diff --git a/cli-session.c b/cli-session.c index 8b58526..9112e98 100644 --- a/cli-session.c +++ b/cli-session.c @@ -72,6 +72,9 @@ static const packettype cli_packettypes[] = { static const struct ChanType *cli_chantypes[] = { #ifdef ENABLE_CLI_REMOTETCPFWD &cli_chan_tcpremote, +#endif +#ifdef ENABLE_CLI_AGENTFWD + &cli_chan_agent, #endif NULL /* Null termination */ }; diff --git a/dbutil.c b/dbutil.c index 45c720e..fdac66a 100644 --- a/dbutil.c +++ b/dbutil.c @@ -252,6 +252,23 @@ int dropbear_listen(const char* address, const char* port, return nsock; } +/* Connect to a given unix socket. The socket is not non-blocking */ +#ifdef ENABLE_CONNECT_UNIX +int connect_unix(const char* addr) +{ + struct sockaddr_un egdsock; + int fd = -1; + + memset((void*)&egdsock, 0x0, sizeof(egdsock)); + egdsock.sun_family = AF_UNIX; + strlcpy(egdsock.sun_path, addr, sizeof(egdsock.sun_path)); + + fd = socket(PF_UNIX, SOCK_STREAM, 0); + + return fd; +} +#endif + /* Connect via TCP to a host. Connection will try ipv4 or ipv6, will * return immediately if nonblocking is set. On failure, if errstring * wasn't null, it will be a newly malloced error message */ @@ -297,15 +314,7 @@ int connect_remote(const char* remotehost, const char* remoteport, } 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; - } + setnonblocking(sock); } if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) { diff --git a/dbutil.h b/dbutil.h index d904949..8f41fc6 100644 --- a/dbutil.h +++ b/dbutil.h @@ -48,6 +48,9 @@ char * stripcontrol(const char * text); unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport); int dropbear_listen(const char* address, const char* port, int *socks, unsigned int sockcount, char **errstring, int *maxfd); +#ifdef ENABLE_CONNECT_UNIX +int connect_unix(const char* addr); +#endif int connect_remote(const char* remotehost, const char* remoteport, int nonblocking, char ** errstring); char* getaddrhostname(struct sockaddr_storage * addr); diff --git a/options.h b/options.h index 5211c77..9e86e68 100644 --- a/options.h +++ b/options.h @@ -59,7 +59,8 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */ #define ENABLE_SVR_REMOTETCPFWD /* Enable Authentication Agent Forwarding - server only for now */ -#define ENABLE_AGENTFWD +#define ENABLE_SVR_AGENTFWD +#define ENABLE_CLI_AGENTFWD /* Encryption - at least one required. * RFC Draft requires 3DES, and recommends Blowfish, AES128 & Twofish128 */ @@ -318,6 +319,10 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */ #define DISABLE_AGENTFWD #endif +#if defined(DROPBEAR_PRNGD_SOCKET) || defined(ENABLE_CLI_AGENTFWD) +#define ENABLE_CONNECT_UNIX +#endif + #if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD) #define ENABLE_CLI_ANYTCPFWD #endif diff --git a/random.c b/random.c index d58c8a8..b085962 100644 --- a/random.c +++ b/random.c @@ -68,12 +68,8 @@ static void readrand(unsigned char* buf, unsigned int buflen) { #endif #ifdef DROPBEAR_PRNGD_SOCKET - memset((void*)&egdsock, 0x0, sizeof(egdsock)); - egdsock.sun_family = AF_UNIX; - strlcpy(egdsock.sun_path, DROPBEAR_PRNGD_SOCKET, - sizeof(egdsock.sun_path)); + readfd = connect_unix(DROPBEAR_PRNGD_SOCKET); - readfd = socket(PF_UNIX, SOCK_STREAM, 0); if (readfd < 0) { dropbear_exit("couldn't open random device"); } diff --git a/runopts.h b/runopts.h index 3d589e7..36fdd85 100644 --- a/runopts.h +++ b/runopts.h @@ -102,6 +102,11 @@ typedef struct cli_runopts { #endif #ifdef ENABLE_CLI_LOCALTCPFWD struct TCPFwdList * localfwds; +#endif +#ifdef ENABLE_CLI_AGENTFWD + int agent_fwd; + int agent_keys_loaded; /* whether pubkeys has been populated with a + list of keys held by the agent */ #endif /* XXX TODO */ diff --git a/ssh.h b/ssh.h index e976c57..f1df273 100644 --- a/ssh.h +++ b/ssh.h @@ -94,3 +94,14 @@ #define SSH_SIGNKEY_DSS_LEN 7 #define SSH_SIGNKEY_RSA "ssh-rsa" #define SSH_SIGNKEY_RSA_LEN 7 + +/* Agent commands. These aren't part of the spec, and are defined + * only on the openssh implementation. */ +#define SSH_AGENT_FAILURE 5 +#define SSH_AGENT_SUCCESS 6 +#define SSH2_AGENTC_REQUEST_IDENTITIES 11 +#define SSH2_AGENT_IDENTITIES_ANSWER 12 +#define SSH2_AGENTC_SIGN_REQUEST 13 +#define SSH2_AGENT_SIGN_RESPONSE 14 + +#define SSH2_AGENT_FAILURE 30 diff --git a/svr-agentfwd.c b/svr-agentfwd.c index 5127158..f4909d5 100644 --- a/svr-agentfwd.c +++ b/svr-agentfwd.c @@ -176,7 +176,7 @@ void agentcleanup(struct ChanSess * chansess) { } -static const struct ChanType chan_agent = { +static const struct ChanType chan_svr_agent = { 0, /* sepfds */ "auth-agent@openssh.com", NULL, @@ -189,7 +189,7 @@ static const struct ChanType chan_agent = { /* helper for accepting an agent request */ static int send_msg_channel_open_agent(int fd) { - if (send_msg_channel_open_init(fd, &chan_agent) == DROPBEAR_SUCCESS) { + if (send_msg_channel_open_init(fd, &chan_svr_agent) == DROPBEAR_SUCCESS) { encrypt_packet(); return DROPBEAR_SUCCESS; } else { From b717efb577621773a1c53952fb5fd8482e7b9c37 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Mon, 17 Nov 2008 14:04:14 +0000 Subject: [PATCH 02/45] Only use -lcrypt for Dropbear server binary --HG-- extra : convert_revision : 7d3d93a5f58d60933277ab6f2595d662e5fb1815 --- Makefile.in | 2 +- configure.in | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile.in b/Makefile.in index 3e6c855..e1a64fd 100644 --- a/Makefile.in +++ b/Makefile.in @@ -25,7 +25,7 @@ COMMONOBJS=dbutil.o buffer.o \ SVROBJS=svr-kex.o svr-algo.o svr-auth.o sshpty.o \ svr-authpasswd.o svr-authpubkey.o svr-authpubkeyoptions.o svr-session.o svr-service.o \ svr-chansession.o svr-runopts.o svr-agentfwd.o svr-main.o svr-x11fwd.o\ - svr-tcpfwd.o svr-authpam.o + svr-tcpfwd.o svr-authpam.o $(CRYPTLIB) CLIOBJS=cli-algo.o cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \ cli-session.o cli-service.o cli-runopts.o cli-chansession.o \ diff --git a/configure.in b/configure.in index 52a75e0..c7149e8 100644 --- a/configure.in +++ b/configure.in @@ -82,7 +82,8 @@ AC_CHECK_DECL(__UCLIBC__, ],,,) # Checks for libraries. -AC_CHECK_LIB(crypt, crypt, LIBS="$LIBS -lcrypt") +AC_CHECK_LIB(crypt, crypt, CRYPTLIB="-lcrypt") +AC_SUBST(CRYPTLIB) # Check if zlib is needed AC_ARG_WITH(zlib, From 8158e952b9433c60f9a1699fa8090aab556b325b Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 18 Nov 2008 12:53:02 +0000 Subject: [PATCH 03/45] - Use the right variable notation --HG-- extra : convert_revision : 8d1eddd800cc6c405f2b3eaad148433c0d6bc0c8 --- Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index e1a64fd..4c8441f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -25,7 +25,7 @@ COMMONOBJS=dbutil.o buffer.o \ SVROBJS=svr-kex.o svr-algo.o svr-auth.o sshpty.o \ svr-authpasswd.o svr-authpubkey.o svr-authpubkeyoptions.o svr-session.o svr-service.o \ svr-chansession.o svr-runopts.o svr-agentfwd.o svr-main.o svr-x11fwd.o\ - svr-tcpfwd.o svr-authpam.o $(CRYPTLIB) + svr-tcpfwd.o svr-authpam.o @CRYPTLIB@ CLIOBJS=cli-algo.o cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \ cli-session.o cli-service.o cli-runopts.o cli-chansession.o \ From c04cc62ebf00f170f0713a4beb845a842b5f0bf1 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 18 Nov 2008 12:53:39 +0000 Subject: [PATCH 04/45] - Allow building with neither server nor client specified --HG-- extra : convert_revision : d9a8b717bf65021efa4c61c34faf24d050d95da4 --- sysoptions.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sysoptions.h b/sysoptions.h index c98e1ec..2de1184 100644 --- a/sysoptions.h +++ b/sysoptions.h @@ -202,5 +202,8 @@ #define IS_DROPBEAR_CLIENT 1 #else -#error You must compiled with either DROPBEAR_CLIENT or DROPBEAR_SERVER selected +/* Just building key utils? */ +#define IS_DROPBEAR_SERVER 0 +#define IS_DROPBEAR_CLIENT 0 + #endif From cca4e1a0809c2798f47115888d4f176bd6c13f29 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Wed, 25 Feb 2009 14:04:02 +0000 Subject: [PATCH 05/45] - Don't be dumb and encrypt/decrypt in a while() loop - why did I do this?? --HG-- extra : convert_revision : c8e1b84cfe874887ad7df0dd95a00de46dbc0136 --- packet.c | 46 ++++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/packet.c b/packet.c index 870d5d8..2c98a34 100644 --- a/packet.c +++ b/packet.c @@ -240,17 +240,16 @@ void decrypt_packet() { buf_setpos(ses.decryptreadbuf, blocksize); /* decrypt it */ - while (ses.readbuf->pos < ses.readbuf->len - macsize) { - if (ses.keys->recv_crypt_mode->decrypt( - buf_getptr(ses.readbuf, blocksize), - buf_getwriteptr(ses.decryptreadbuf, blocksize), - blocksize, - &ses.keys->recv_cipher_state) != CRYPT_OK) { - dropbear_exit("error decrypting"); - } - buf_incrpos(ses.readbuf, blocksize); - buf_incrwritepos(ses.decryptreadbuf, blocksize); + len = ses.readbuf->len - macsize - ses.readbuf->pos; + if (ses.keys->recv_crypt_mode->decrypt( + buf_getptr(ses.readbuf, len), + buf_getwriteptr(ses.decryptreadbuf, len), + len, + &ses.keys->recv_cipher_state) != CRYPT_OK) { + dropbear_exit("error decrypting"); } + buf_incrpos(ses.readbuf, len); + buf_incrwritepos(ses.decryptreadbuf, len); /* check the hmac */ buf_setpos(ses.readbuf, ses.readbuf->len - macsize); @@ -454,7 +453,7 @@ void encrypt_packet() { buffer * writebuf; /* the packet which will go on the wire */ buffer * clearwritebuf; /* unencrypted, possibly compressed */ unsigned char type; - unsigned int clear_len; + unsigned int len; type = ses.writepayload->data[0]; TRACE(("enter encrypt_packet()")) @@ -474,12 +473,12 @@ void encrypt_packet() { /* Encrypted packet len is payload+5, then worst case is if we are 3 away * from a blocksize multiple. In which case we need to pad to the * multiple, then add another blocksize (or MIN_PACKET_LEN) */ - clear_len = (ses.writepayload->len+4+1) + MIN_PACKET_LEN + 3; + len = (ses.writepayload->len+4+1) + MIN_PACKET_LEN + 3; #ifndef DISABLE_ZLIB - clear_len += ZLIB_COMPRESS_INCR; /* bit of a kludge, but we can't know len*/ + len += ZLIB_COMPRESS_INCR; /* bit of a kludge, but we can't know len*/ #endif - clearwritebuf = buf_new(clear_len); + clearwritebuf = buf_new(len); buf_setlen(clearwritebuf, PACKET_PAYLOAD_OFF); buf_setpos(clearwritebuf, PACKET_PAYLOAD_OFF); @@ -531,17 +530,16 @@ void encrypt_packet() { writebuf = buf_new(clearwritebuf->len + macsize); /* encrypt it */ - while (clearwritebuf->pos < clearwritebuf->len) { - if (ses.keys->trans_crypt_mode->encrypt( - buf_getptr(clearwritebuf, blocksize), - buf_getwriteptr(writebuf, blocksize), - blocksize, - &ses.keys->trans_cipher_state) != CRYPT_OK) { - dropbear_exit("error encrypting"); - } - buf_incrpos(clearwritebuf, blocksize); - buf_incrwritepos(writebuf, blocksize); + len = clearwritebuf->len; + if (ses.keys->trans_crypt_mode->encrypt( + buf_getptr(clearwritebuf, len), + buf_getwriteptr(writebuf, len), + len, + &ses.keys->trans_cipher_state) != CRYPT_OK) { + dropbear_exit("error encrypting"); } + buf_incrpos(clearwritebuf, len); + buf_incrwritepos(writebuf, len); /* now add a hmac and we're done */ writemac(writebuf, clearwritebuf); From a60cb7dbaa41432d359468c4f2972b1348fa8631 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 26 Feb 2009 12:18:11 +0000 Subject: [PATCH 06/45] - Try to write out as much as we can --HG-- extra : convert_revision : a101cbd046507cf723e6362a49196dbd4b924042 --- common-session.c | 2 +- packet.c | 15 ++++++++++++--- packet.h | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/common-session.c b/common-session.c index b48d210..d1545e6 100644 --- a/common-session.c +++ b/common-session.c @@ -189,7 +189,7 @@ void session_loop(void(*loophandler)()) { /* process session socket's incoming/outgoing data */ if (ses.sock_out != -1) { if (FD_ISSET(ses.sock_out, &writefd) && !isempty(&ses.writequeue)) { - write_packet(); + write_packets(); } } diff --git a/packet.c b/packet.c index 870d5d8..4e2eedf 100644 --- a/packet.c +++ b/packet.c @@ -46,14 +46,16 @@ static buffer* buf_decompress(buffer* buf, unsigned int len); static void buf_compress(buffer * dest, buffer * src, unsigned int len); #endif -/* non-blocking function writing out a current encrypted packet */ -void write_packet() { +/* non-blocking function writing out a current encrypted packet. Returns + * DROPBEAR_SUCCESS if entire packet was written, DROPBEAR_FAILURE + * otherwise */ +static int write_packet() { int len, written; + int ret = DROPBEAR_FAILURE; buffer * writebuf = NULL; TRACE(("enter write_packet")) - dropbear_assert(!isempty(&ses.writequeue)); /* Get the next buffer in the queue of encrypted packets to write*/ writebuf = (buffer*)examine(&ses.writequeue); @@ -84,12 +86,19 @@ void write_packet() { dequeue(&ses.writequeue); buf_free(writebuf); writebuf = NULL; + ret = DROPBEAR_SUCCESS; } else { /* More packet left to write, leave it in the queue for later */ buf_incrpos(writebuf, written); } TRACE(("leave write_packet")) + return ret; +} + +void write_packets() { + /* keep writing packets while we can. */ + while (!isempty(&ses.writequeue) && write_packet() == DROPBEAR_SUCCESS) {} } /* Non-blocking function reading available portion of a packet into the diff --git a/packet.h b/packet.h index 8fadeb3..db203d3 100644 --- a/packet.h +++ b/packet.h @@ -28,7 +28,7 @@ #include "includes.h" -void write_packet(); +void write_packets(); void read_packet(); void decrypt_packet(); void encrypt_packet(); From d1bfb6bfb7a346715a66318ec9d1e860903c8245 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 26 Feb 2009 13:20:53 +0000 Subject: [PATCH 07/45] disapproval of revision 'a101cbd046507cf723e6362a49196dbd4b924042' --HG-- extra : convert_revision : e1c100e6366c5d607af08f4abdbb0f4281df4fa9 --- common-session.c | 2 +- packet.c | 15 +++------------ packet.h | 2 +- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/common-session.c b/common-session.c index d1545e6..b48d210 100644 --- a/common-session.c +++ b/common-session.c @@ -189,7 +189,7 @@ void session_loop(void(*loophandler)()) { /* process session socket's incoming/outgoing data */ if (ses.sock_out != -1) { if (FD_ISSET(ses.sock_out, &writefd) && !isempty(&ses.writequeue)) { - write_packets(); + write_packet(); } } diff --git a/packet.c b/packet.c index 4e2eedf..870d5d8 100644 --- a/packet.c +++ b/packet.c @@ -46,16 +46,14 @@ static buffer* buf_decompress(buffer* buf, unsigned int len); static void buf_compress(buffer * dest, buffer * src, unsigned int len); #endif -/* non-blocking function writing out a current encrypted packet. Returns - * DROPBEAR_SUCCESS if entire packet was written, DROPBEAR_FAILURE - * otherwise */ -static int write_packet() { +/* non-blocking function writing out a current encrypted packet */ +void write_packet() { int len, written; - int ret = DROPBEAR_FAILURE; buffer * writebuf = NULL; TRACE(("enter write_packet")) + dropbear_assert(!isempty(&ses.writequeue)); /* Get the next buffer in the queue of encrypted packets to write*/ writebuf = (buffer*)examine(&ses.writequeue); @@ -86,19 +84,12 @@ static int write_packet() { dequeue(&ses.writequeue); buf_free(writebuf); writebuf = NULL; - ret = DROPBEAR_SUCCESS; } else { /* More packet left to write, leave it in the queue for later */ buf_incrpos(writebuf, written); } TRACE(("leave write_packet")) - return ret; -} - -void write_packets() { - /* keep writing packets while we can. */ - while (!isempty(&ses.writequeue) && write_packet() == DROPBEAR_SUCCESS) {} } /* Non-blocking function reading available portion of a packet into the diff --git a/packet.h b/packet.h index db203d3..8fadeb3 100644 --- a/packet.h +++ b/packet.h @@ -28,7 +28,7 @@ #include "includes.h" -void write_packets(); +void write_packet(); void read_packet(); void decrypt_packet(); void encrypt_packet(); From 8e72bbaa9db050e4a0386f89999ff2131c0afdb6 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 1 Mar 2009 14:38:25 +0000 Subject: [PATCH 08/45] Encrypt in-place, avoid an extra malloc --HG-- extra : convert_revision : 981e3e4b44e6fdc8537775518e898a33e76a38db --- packet.c | 89 +++++++++++++++++++++++++------------------------------- 1 file changed, 40 insertions(+), 49 deletions(-) diff --git a/packet.c b/packet.c index 2c98a34..683525d 100644 --- a/packet.c +++ b/packet.c @@ -36,7 +36,7 @@ #include "channel.h" static void read_packet_init(); -static void writemac(buffer * outputbuffer, buffer * clearwritebuf); +static void make_mac(buffer * clearwritebuf, unsigned char *output_mac); static int checkmac(buffer* hashbuf, buffer* readbuf); #define ZLIB_COMPRESS_INCR 20 /* this is 12 bytes + 0.1% of 8000 bytes */ @@ -449,11 +449,12 @@ void maybe_flush_reply_queue() { void encrypt_packet() { unsigned char padlen; - unsigned char blocksize, macsize; - buffer * writebuf; /* the packet which will go on the wire */ - buffer * clearwritebuf; /* unencrypted, possibly compressed */ + unsigned char blocksize, mac_size; + buffer * writebuf; /* the packet which will go on the wire. This is + encrypted in-place. */ unsigned char type; - unsigned int len; + unsigned int len, encrypt_buf_size; + unsigned char mac_bytes[MAX_MAC_LEN]; type = ses.writepayload->data[0]; TRACE(("enter encrypt_packet()")) @@ -468,33 +469,35 @@ void encrypt_packet() { } blocksize = ses.keys->trans_algo_crypt->blocksize; - macsize = ses.keys->trans_algo_mac->hashsize; + mac_size = ses.keys->trans_algo_mac->hashsize; /* Encrypted packet len is payload+5, then worst case is if we are 3 away * from a blocksize multiple. In which case we need to pad to the * multiple, then add another blocksize (or MIN_PACKET_LEN) */ - len = (ses.writepayload->len+4+1) + MIN_PACKET_LEN + 3; + encrypt_buf_size = (ses.writepayload->len+4+1) + MIN_PACKET_LEN + 3; + /* add space for the MAC at the end */ + encrypt_buf_size += mac_size; #ifndef DISABLE_ZLIB - len += ZLIB_COMPRESS_INCR; /* bit of a kludge, but we can't know len*/ + encrypt_buf_size += ZLIB_COMPRESS_INCR; /* bit of a kludge, but we can't know len*/ #endif - clearwritebuf = buf_new(len); - buf_setlen(clearwritebuf, PACKET_PAYLOAD_OFF); - buf_setpos(clearwritebuf, PACKET_PAYLOAD_OFF); + writebuf = buf_new(encrypt_buf_size); + buf_setlen(writebuf, PACKET_PAYLOAD_OFF); + buf_setpos(writebuf, PACKET_PAYLOAD_OFF); buf_setpos(ses.writepayload, 0); #ifndef DISABLE_ZLIB /* compression */ if (is_compress_trans()) { - buf_compress(clearwritebuf, ses.writepayload, ses.writepayload->len); + buf_compress(writebuf, ses.writepayload, ses.writepayload->len); } else #endif { - memcpy(buf_getwriteptr(clearwritebuf, ses.writepayload->len), + memcpy(buf_getwriteptr(writebuf, ses.writepayload->len), buf_getptr(ses.writepayload, ses.writepayload->len), ses.writepayload->len); - buf_incrwritepos(clearwritebuf, ses.writepayload->len); + buf_incrwritepos(writebuf, ses.writepayload->len); } /* finished with payload */ @@ -503,52 +506,45 @@ void encrypt_packet() { /* length of padding - packet length must be a multiple of blocksize, * with a minimum of 4 bytes of padding */ - padlen = blocksize - (clearwritebuf->len) % blocksize; + padlen = blocksize - (writebuf->len) % blocksize; if (padlen < 4) { padlen += blocksize; } /* check for min packet length */ - if (clearwritebuf->len + padlen < MIN_PACKET_LEN) { + if (writebuf->len + padlen < MIN_PACKET_LEN) { padlen += blocksize; } - buf_setpos(clearwritebuf, 0); + buf_setpos(writebuf, 0); /* packet length excluding the packetlength uint32 */ - buf_putint(clearwritebuf, clearwritebuf->len + padlen - 4); + buf_putint(writebuf, writebuf->len + padlen - 4); /* padding len */ - buf_putbyte(clearwritebuf, padlen); + buf_putbyte(writebuf, padlen); /* actual padding */ - buf_setpos(clearwritebuf, clearwritebuf->len); - buf_incrlen(clearwritebuf, padlen); - genrandom(buf_getptr(clearwritebuf, padlen), padlen); + buf_setpos(writebuf, writebuf->len); + buf_incrlen(writebuf, padlen); + genrandom(buf_getptr(writebuf, padlen), padlen); - /* do the actual encryption */ - buf_setpos(clearwritebuf, 0); - /* create a new writebuffer, this is freed when it has been put on the - * wire by writepacket() */ - writebuf = buf_new(clearwritebuf->len + macsize); + make_mac(writebuf, mac_bytes); - /* encrypt it */ - len = clearwritebuf->len; + /* do the actual encryption, in-place */ + buf_setpos(writebuf, 0); + /* encrypt it in-place*/ + len = writebuf->len; if (ses.keys->trans_crypt_mode->encrypt( - buf_getptr(clearwritebuf, len), + buf_getptr(writebuf, len), buf_getwriteptr(writebuf, len), len, &ses.keys->trans_cipher_state) != CRYPT_OK) { dropbear_exit("error encrypting"); } - buf_incrpos(clearwritebuf, len); - buf_incrwritepos(writebuf, len); + buf_incrpos(writebuf, len); - /* now add a hmac and we're done */ - writemac(writebuf, clearwritebuf); + /* stick the MAC on it */ + buf_putbytes(writebuf, mac_bytes, mac_size); - /* clearwritebuf is finished with */ - buf_free(clearwritebuf); - clearwritebuf = NULL; - - /* enqueue the packet for sending */ + /* enqueue the packet for sending. It will get freed after transmission. */ buf_setpos(writebuf, 0); enqueue(&ses.writequeue, (void*)writebuf); @@ -561,18 +557,15 @@ void encrypt_packet() { /* Create the packet mac, and append H(seqno|clearbuf) to the output */ -static void writemac(buffer * outputbuffer, buffer * clearwritebuf) { - - unsigned int macsize; +/* output_mac must have ses.keys->trans_algo_mac->hashsize bytes. */ +static void make_mac(buffer * clearwritebuf, unsigned char *output_mac) { unsigned char seqbuf[4]; - unsigned char tempbuf[MAX_MAC_LEN]; unsigned long bufsize; hmac_state hmac; TRACE(("enter writemac")) - macsize = ses.keys->trans_algo_mac->hashsize; - if (macsize > 0) { + if (ses.keys->trans_algo_mac->hashsize > 0) { /* calculate the mac */ if (hmac_init(&hmac, find_hash(ses.keys->trans_algo_mac->hashdesc->name), @@ -596,12 +589,10 @@ static void writemac(buffer * outputbuffer, buffer * clearwritebuf) { dropbear_exit("HMAC error"); } - bufsize = sizeof(tempbuf); - if (hmac_done(&hmac, tempbuf, &bufsize) - != CRYPT_OK) { + bufsize = MAX_MAC_LEN; + if (hmac_done(&hmac, output_mac, &bufsize) != CRYPT_OK) { dropbear_exit("HMAC error"); } - buf_putbytes(outputbuffer, tempbuf, macsize); } TRACE(("leave writemac")) } From ff763e40057e5a51e142d0c4a6ffb090da465b78 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 1 Mar 2009 16:15:57 +0000 Subject: [PATCH 09/45] - Get rid of decryptreadbuf, just decrypt in-place with readbuf - Share make_mac function for both packet creation and validation - Split recv/trans parts of key_context into their own structures --HG-- extra : convert_revision : 043bc598c76ed43625987e6937e32238f7ed6240 --- common-kex.c | 114 +++++++++++++-------------- common-session.c | 21 +++-- packet.c | 198 ++++++++++++++++++++--------------------------- packet.h | 2 +- session.h | 51 ++++++------ 5 files changed, 175 insertions(+), 211 deletions(-) diff --git a/common-kex.c b/common-kex.c index c62516c..052324b 100644 --- a/common-kex.c +++ b/common-kex.c @@ -272,8 +272,8 @@ void gen_new_keys() { recv_IV = S2C_IV; trans_key = C2S_key; recv_key = S2C_key; - C2S_keysize = ses.newkeys->trans_algo_crypt->keysize; - S2C_keysize = ses.newkeys->recv_algo_crypt->keysize; + C2S_keysize = ses.newkeys->trans.algo_crypt->keysize; + S2C_keysize = ses.newkeys->recv.algo_crypt->keysize; mactransletter = 'E'; macrecvletter = 'F'; } else { @@ -281,8 +281,8 @@ void gen_new_keys() { recv_IV = C2S_IV; trans_key = S2C_key; recv_key = C2S_key; - C2S_keysize = ses.newkeys->recv_algo_crypt->keysize; - S2C_keysize = ses.newkeys->trans_algo_crypt->keysize; + C2S_keysize = ses.newkeys->recv.algo_crypt->keysize; + S2C_keysize = ses.newkeys->trans.algo_crypt->keysize; mactransletter = 'F'; macrecvletter = 'E'; } @@ -292,31 +292,33 @@ void gen_new_keys() { hashkeys(C2S_key, C2S_keysize, &hs, 'C'); hashkeys(S2C_key, S2C_keysize, &hs, 'D'); - recv_cipher = find_cipher(ses.newkeys->recv_algo_crypt->cipherdesc->name); + recv_cipher = find_cipher(ses.newkeys->recv.algo_crypt->cipherdesc->name); if (recv_cipher < 0) dropbear_exit("crypto error"); - if (ses.newkeys->recv_crypt_mode->start(recv_cipher, + if (ses.newkeys->recv.crypt_mode->start(recv_cipher, recv_IV, recv_key, - ses.newkeys->recv_algo_crypt->keysize, 0, - &ses.newkeys->recv_cipher_state) != CRYPT_OK) { + ses.newkeys->recv.algo_crypt->keysize, 0, + &ses.newkeys->recv.cipher_state) != CRYPT_OK) { dropbear_exit("crypto error"); } - trans_cipher = find_cipher(ses.newkeys->trans_algo_crypt->cipherdesc->name); + trans_cipher = find_cipher(ses.newkeys->trans.algo_crypt->cipherdesc->name); if (trans_cipher < 0) dropbear_exit("crypto error"); - if (ses.newkeys->trans_crypt_mode->start(trans_cipher, + if (ses.newkeys->trans.crypt_mode->start(trans_cipher, trans_IV, trans_key, - ses.newkeys->trans_algo_crypt->keysize, 0, - &ses.newkeys->trans_cipher_state) != CRYPT_OK) { + ses.newkeys->trans.algo_crypt->keysize, 0, + &ses.newkeys->trans.cipher_state) != CRYPT_OK) { dropbear_exit("crypto error"); } /* MAC keys */ - hashkeys(ses.newkeys->transmackey, - ses.newkeys->trans_algo_mac->keysize, &hs, mactransletter); - hashkeys(ses.newkeys->recvmackey, - ses.newkeys->recv_algo_mac->keysize, &hs, macrecvletter); + hashkeys(ses.newkeys->trans.mackey, + ses.newkeys->trans.algo_mac->keysize, &hs, mactransletter); + hashkeys(ses.newkeys->recv.mackey, + ses.newkeys->recv.algo_mac->keysize, &hs, macrecvletter); + ses.newkeys->trans.hash_index = find_hash(ses.newkeys->trans.algo_mac->hashdesc->name), + ses.newkeys->recv.hash_index = find_hash(ses.newkeys->recv.algo_mac->hashdesc->name), #ifndef DISABLE_ZLIB gen_new_zstreams(); @@ -334,15 +336,15 @@ void gen_new_keys() { #ifndef DISABLE_ZLIB int is_compress_trans() { - return ses.keys->trans_algo_comp == DROPBEAR_COMP_ZLIB + return ses.keys->trans.algo_comp == DROPBEAR_COMP_ZLIB || (ses.authstate.authdone - && ses.keys->trans_algo_comp == DROPBEAR_COMP_ZLIB_DELAY); + && ses.keys->trans.algo_comp == DROPBEAR_COMP_ZLIB_DELAY); } int is_compress_recv() { - return ses.keys->recv_algo_comp == DROPBEAR_COMP_ZLIB + return ses.keys->recv.algo_comp == DROPBEAR_COMP_ZLIB || (ses.authstate.authdone - && ses.keys->recv_algo_comp == DROPBEAR_COMP_ZLIB_DELAY); + && ses.keys->recv.algo_comp == DROPBEAR_COMP_ZLIB_DELAY); } /* Set up new zlib compression streams, close the old ones. Only @@ -350,47 +352,47 @@ int is_compress_recv() { static void gen_new_zstreams() { /* create new zstreams */ - if (ses.newkeys->recv_algo_comp == DROPBEAR_COMP_ZLIB - || ses.newkeys->recv_algo_comp == DROPBEAR_COMP_ZLIB_DELAY) { - ses.newkeys->recv_zstream = (z_streamp)m_malloc(sizeof(z_stream)); - ses.newkeys->recv_zstream->zalloc = Z_NULL; - ses.newkeys->recv_zstream->zfree = Z_NULL; + if (ses.newkeys->recv.algo_comp == DROPBEAR_COMP_ZLIB + || ses.newkeys->recv.algo_comp == DROPBEAR_COMP_ZLIB_DELAY) { + ses.newkeys->recv.zstream = (z_streamp)m_malloc(sizeof(z_stream)); + ses.newkeys->recv.zstream->zalloc = Z_NULL; + ses.newkeys->recv.zstream->zfree = Z_NULL; - if (inflateInit(ses.newkeys->recv_zstream) != Z_OK) { + if (inflateInit(ses.newkeys->recv.zstream) != Z_OK) { dropbear_exit("zlib error"); } } else { - ses.newkeys->recv_zstream = NULL; + ses.newkeys->recv.zstream = NULL; } - if (ses.newkeys->trans_algo_comp == DROPBEAR_COMP_ZLIB - || ses.newkeys->trans_algo_comp == DROPBEAR_COMP_ZLIB_DELAY) { - ses.newkeys->trans_zstream = (z_streamp)m_malloc(sizeof(z_stream)); - ses.newkeys->trans_zstream->zalloc = Z_NULL; - ses.newkeys->trans_zstream->zfree = Z_NULL; + if (ses.newkeys->trans.algo_comp == DROPBEAR_COMP_ZLIB + || ses.newkeys->trans.algo_comp == DROPBEAR_COMP_ZLIB_DELAY) { + ses.newkeys->trans.zstream = (z_streamp)m_malloc(sizeof(z_stream)); + ses.newkeys->trans.zstream->zalloc = Z_NULL; + ses.newkeys->trans.zstream->zfree = Z_NULL; - if (deflateInit(ses.newkeys->trans_zstream, Z_DEFAULT_COMPRESSION) + if (deflateInit(ses.newkeys->trans.zstream, Z_DEFAULT_COMPRESSION) != Z_OK) { dropbear_exit("zlib error"); } } else { - ses.newkeys->trans_zstream = NULL; + ses.newkeys->trans.zstream = NULL; } /* clean up old keys */ - if (ses.keys->recv_zstream != NULL) { - if (inflateEnd(ses.keys->recv_zstream) == Z_STREAM_ERROR) { + if (ses.keys->recv.zstream != NULL) { + if (inflateEnd(ses.keys->recv.zstream) == Z_STREAM_ERROR) { /* Z_DATA_ERROR is ok, just means that stream isn't ended */ dropbear_exit("crypto error"); } - m_free(ses.keys->recv_zstream); + m_free(ses.keys->recv.zstream); } - if (ses.keys->trans_zstream != NULL) { - if (deflateEnd(ses.keys->trans_zstream) == Z_STREAM_ERROR) { + if (ses.keys->trans.zstream != NULL) { + if (deflateEnd(ses.keys->trans.zstream) == Z_STREAM_ERROR) { /* Z_DATA_ERROR is ok, just means that stream isn't ended */ dropbear_exit("crypto error"); } - m_free(ses.keys->trans_zstream); + m_free(ses.keys->trans.zstream); } } #endif /* DISABLE_ZLIB */ @@ -698,36 +700,36 @@ static void read_kex_algos() { /* Handle the asymmetry */ if (IS_DROPBEAR_CLIENT) { - ses.newkeys->recv_algo_crypt = + ses.newkeys->recv.algo_crypt = (struct dropbear_cipher*)s2c_cipher_algo->data; - ses.newkeys->trans_algo_crypt = + ses.newkeys->trans.algo_crypt = (struct dropbear_cipher*)c2s_cipher_algo->data; - ses.newkeys->recv_crypt_mode = + ses.newkeys->recv.crypt_mode = (struct dropbear_cipher_mode*)s2c_cipher_algo->mode; - ses.newkeys->trans_crypt_mode = + ses.newkeys->trans.crypt_mode = (struct dropbear_cipher_mode*)c2s_cipher_algo->mode; - ses.newkeys->recv_algo_mac = + ses.newkeys->recv.algo_mac = (struct dropbear_hash*)s2c_hash_algo->data; - ses.newkeys->trans_algo_mac = + ses.newkeys->trans.algo_mac = (struct dropbear_hash*)c2s_hash_algo->data; - ses.newkeys->recv_algo_comp = s2c_comp_algo->val; - ses.newkeys->trans_algo_comp = c2s_comp_algo->val; + ses.newkeys->recv.algo_comp = s2c_comp_algo->val; + ses.newkeys->trans.algo_comp = c2s_comp_algo->val; } else { /* SERVER */ - ses.newkeys->recv_algo_crypt = + ses.newkeys->recv.algo_crypt = (struct dropbear_cipher*)c2s_cipher_algo->data; - ses.newkeys->trans_algo_crypt = + ses.newkeys->trans.algo_crypt = (struct dropbear_cipher*)s2c_cipher_algo->data; - ses.newkeys->recv_crypt_mode = + ses.newkeys->recv.crypt_mode = (struct dropbear_cipher_mode*)c2s_cipher_algo->mode; - ses.newkeys->trans_crypt_mode = + ses.newkeys->trans.crypt_mode = (struct dropbear_cipher_mode*)s2c_cipher_algo->mode; - ses.newkeys->recv_algo_mac = + ses.newkeys->recv.algo_mac = (struct dropbear_hash*)c2s_hash_algo->data; - ses.newkeys->trans_algo_mac = + ses.newkeys->trans.algo_mac = (struct dropbear_hash*)s2c_hash_algo->data; - ses.newkeys->recv_algo_comp = c2s_comp_algo->val; - ses.newkeys->trans_algo_comp = s2c_comp_algo->val; + ses.newkeys->recv.algo_comp = c2s_comp_algo->val; + ses.newkeys->trans.algo_comp = s2c_comp_algo->val; } /* reserved for future extensions */ diff --git a/common-session.c b/common-session.c index b48d210..3663534 100644 --- a/common-session.c +++ b/common-session.c @@ -78,7 +78,6 @@ void common_session_init(int sock_in, int sock_out, char* remotehost) { ses.transseq = 0; ses.readbuf = NULL; - ses.decryptreadbuf = NULL; ses.payload = NULL; ses.recvseq = 0; @@ -95,22 +94,22 @@ void common_session_init(int sock_in, int sock_out, char* remotehost) { /* set all the algos to none */ ses.keys = (struct key_context*)m_malloc(sizeof(struct key_context)); ses.newkeys = NULL; - ses.keys->recv_algo_crypt = &dropbear_nocipher; - ses.keys->trans_algo_crypt = &dropbear_nocipher; - ses.keys->recv_crypt_mode = &dropbear_mode_none; - ses.keys->trans_crypt_mode = &dropbear_mode_none; + ses.keys->recv.algo_crypt = &dropbear_nocipher; + ses.keys->trans.algo_crypt = &dropbear_nocipher; + ses.keys->recv.crypt_mode = &dropbear_mode_none; + ses.keys->trans.crypt_mode = &dropbear_mode_none; - ses.keys->recv_algo_mac = &dropbear_nohash; - ses.keys->trans_algo_mac = &dropbear_nohash; + ses.keys->recv.algo_mac = &dropbear_nohash; + ses.keys->trans.algo_mac = &dropbear_nohash; ses.keys->algo_kex = -1; ses.keys->algo_hostkey = -1; - ses.keys->recv_algo_comp = DROPBEAR_COMP_NONE; - ses.keys->trans_algo_comp = DROPBEAR_COMP_NONE; + ses.keys->recv.algo_comp = DROPBEAR_COMP_NONE; + ses.keys->trans.algo_comp = DROPBEAR_COMP_NONE; #ifndef DISABLE_ZLIB - ses.keys->recv_zstream = NULL; - ses.keys->trans_zstream = NULL; + ses.keys->recv.zstream = NULL; + ses.keys->trans.zstream = NULL; #endif /* key exchange buffers */ diff --git a/packet.c b/packet.c index 683525d..6a294f4 100644 --- a/packet.c +++ b/packet.c @@ -35,9 +35,11 @@ #include "auth.h" #include "channel.h" -static void read_packet_init(); -static void make_mac(buffer * clearwritebuf, unsigned char *output_mac); -static int checkmac(buffer* hashbuf, buffer* readbuf); +static int read_packet_init(); +static void make_mac(unsigned int seqno, const struct key_context_directional * key_state, + buffer * clear_buf, unsigned int clear_len, + unsigned char *output_mac); +static int checkmac(); #define ZLIB_COMPRESS_INCR 20 /* this is 12 bytes + 0.1% of 8000 bytes */ #define ZLIB_DECOMPRESS_INCR 100 @@ -102,18 +104,18 @@ void read_packet() { unsigned char blocksize; TRACE(("enter read_packet")) - blocksize = ses.keys->recv_algo_crypt->blocksize; + blocksize = ses.keys->recv.algo_crypt->blocksize; if (ses.readbuf == NULL || ses.readbuf->len < blocksize) { + int ret; /* In the first blocksize of a packet */ /* Read the first blocksize of the packet, so we can decrypt it and * find the length of the whole packet */ - read_packet_init(); + ret = read_packet_init(); - /* If we don't have the length of decryptreadbuf, we didn't read - * a whole blocksize and should exit */ - if (ses.decryptreadbuf->len == 0) { + if (ret == DROPBEAR_FAILURE) { + /* didn't read enough to determine the length */ TRACE(("leave read_packet: packetinit done")) return; } @@ -121,7 +123,6 @@ void read_packet() { /* Attempt to read the remainder of the packet, note that there * mightn't be any available (EAGAIN) */ - dropbear_assert(ses.readbuf != NULL); maxlen = ses.readbuf->len - ses.readbuf->pos; len = read(ses.sock_in, buf_getptr(ses.readbuf, maxlen), maxlen); @@ -151,7 +152,9 @@ void read_packet() { /* Function used to read the initial portion of a packet, and determine the * length. Only called during the first BLOCKSIZE of a packet. */ -static void read_packet_init() { +/* Returns DROPBEAR_SUCCESS if the length is determined, + * DROPBEAR_FAILURE otherwise */ +static int read_packet_init() { unsigned int maxlen; int len; @@ -159,14 +162,12 @@ static void read_packet_init() { unsigned char macsize; - blocksize = ses.keys->recv_algo_crypt->blocksize; - macsize = ses.keys->recv_algo_mac->hashsize; + blocksize = ses.keys->recv.algo_crypt->blocksize; + macsize = ses.keys->recv.algo_mac->hashsize; if (ses.readbuf == NULL) { /* start of a new packet */ ses.readbuf = buf_new(INIT_READBUF); - dropbear_assert(ses.decryptreadbuf == NULL); - ses.decryptreadbuf = buf_new(blocksize); } maxlen = blocksize - ses.readbuf->pos; @@ -180,7 +181,7 @@ static void read_packet_init() { if (len < 0) { if (errno == EINTR) { TRACE(("leave read_packet_init: EINTR")) - return; + return DROPBEAR_FAILURE; } dropbear_exit("error reading: %s", strerror(errno)); } @@ -189,22 +190,22 @@ static void read_packet_init() { if ((unsigned int)len != maxlen) { /* don't have enough bytes to determine length, get next time */ - return; + return DROPBEAR_FAILURE; } /* now we have the first block, need to get packet length, so we decrypt * the first block (only need first 4 bytes) */ buf_setpos(ses.readbuf, 0); - if (ses.keys->recv_crypt_mode->decrypt(buf_getptr(ses.readbuf, blocksize), - buf_getwriteptr(ses.decryptreadbuf,blocksize), + if (ses.keys->recv.crypt_mode->decrypt(buf_getptr(ses.readbuf, blocksize), + buf_getwriteptr(ses.readbuf, blocksize), blocksize, - &ses.keys->recv_cipher_state) != CRYPT_OK) { + &ses.keys->recv.cipher_state) != CRYPT_OK) { dropbear_exit("error decrypting"); } - buf_setlen(ses.decryptreadbuf, blocksize); - len = buf_getint(ses.decryptreadbuf) + 4 + macsize; + len = buf_getint(ses.readbuf) + 4 + macsize; + + TRACE(("packet size is %d, block %d mac %d", len, blocksize, macsize)) - buf_setpos(ses.readbuf, blocksize); /* check packet length */ if ((len > RECV_MAX_PACKET_LEN) || @@ -213,9 +214,12 @@ static void read_packet_init() { dropbear_exit("bad packet size %d", len); } - buf_resize(ses.readbuf, len); + if (len > ses.readbuf->size) { + buf_resize(ses.readbuf, len); + } buf_setlen(ses.readbuf, len); - + buf_setpos(ses.readbuf, blocksize); + return DROPBEAR_SUCCESS; } /* handle the received packet */ @@ -227,68 +231,60 @@ void decrypt_packet() { unsigned int len; TRACE(("enter decrypt_packet")) - blocksize = ses.keys->recv_algo_crypt->blocksize; - macsize = ses.keys->recv_algo_mac->hashsize; + blocksize = ses.keys->recv.algo_crypt->blocksize; + macsize = ses.keys->recv.algo_mac->hashsize; ses.kexstate.datarecv += ses.readbuf->len; /* we've already decrypted the first blocksize in read_packet_init */ buf_setpos(ses.readbuf, blocksize); - buf_resize(ses.decryptreadbuf, ses.readbuf->len - macsize); - buf_setlen(ses.decryptreadbuf, ses.decryptreadbuf->size); - buf_setpos(ses.decryptreadbuf, blocksize); - - /* decrypt it */ + /* decrypt it in-place */ len = ses.readbuf->len - macsize - ses.readbuf->pos; - if (ses.keys->recv_crypt_mode->decrypt( + if (ses.keys->recv.crypt_mode->decrypt( buf_getptr(ses.readbuf, len), - buf_getwriteptr(ses.decryptreadbuf, len), + buf_getwriteptr(ses.readbuf, len), len, - &ses.keys->recv_cipher_state) != CRYPT_OK) { + &ses.keys->recv.cipher_state) != CRYPT_OK) { dropbear_exit("error decrypting"); } buf_incrpos(ses.readbuf, len); - buf_incrwritepos(ses.decryptreadbuf, len); + + printhex("readbuf decrypted", ses.readbuf->data, ses.readbuf->len); /* check the hmac */ - buf_setpos(ses.readbuf, ses.readbuf->len - macsize); - if (checkmac(ses.readbuf, ses.decryptreadbuf) != DROPBEAR_SUCCESS) { + if (checkmac() != DROPBEAR_SUCCESS) { dropbear_exit("Integrity error"); } - /* readbuf no longer required */ - buf_free(ses.readbuf); - ses.readbuf = NULL; - /* get padding length */ - buf_setpos(ses.decryptreadbuf, PACKET_PADDING_OFF); - padlen = buf_getbyte(ses.decryptreadbuf); + buf_setpos(ses.readbuf, PACKET_PADDING_OFF); + padlen = buf_getbyte(ses.readbuf); /* payload length */ /* - 4 - 1 is for LEN and PADLEN values */ - len = ses.decryptreadbuf->len - padlen - 4 - 1; + len = ses.readbuf->len - padlen - 4 - 1; if ((len > RECV_MAX_PAYLOAD_LEN) || (len < 1)) { dropbear_exit("bad packet size"); } - buf_setpos(ses.decryptreadbuf, PACKET_PAYLOAD_OFF); + buf_setpos(ses.readbuf, PACKET_PAYLOAD_OFF); #ifndef DISABLE_ZLIB if (is_compress_recv()) { /* decompress */ - ses.payload = buf_decompress(ses.decryptreadbuf, len); + ses.payload = buf_decompress(ses.readbuf, len); } else #endif { /* copy payload */ ses.payload = buf_new(len); - memcpy(ses.payload->data, buf_getptr(ses.decryptreadbuf, len), len); + memcpy(ses.payload->data, buf_getptr(ses.readbuf, len), len); buf_incrlen(ses.payload, len); } - buf_free(ses.decryptreadbuf); - ses.decryptreadbuf = NULL; + buf_free(ses.readbuf); + ses.readbuf = NULL; buf_setpos(ses.payload, 0); ses.recvseq++; @@ -296,49 +292,22 @@ void decrypt_packet() { TRACE(("leave decrypt_packet")) } -/* Checks the mac in hashbuf, for the data in readbuf. +/* Checks the mac at the end of a decrypted readbuf. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ -static int checkmac(buffer* macbuf, buffer* sourcebuf) { +static int checkmac() { - unsigned int macsize; - hmac_state hmac; - unsigned char tempbuf[MAX_MAC_LEN]; - unsigned long bufsize; - unsigned int len; - - macsize = ses.keys->recv_algo_mac->hashsize; - if (macsize == 0) { - return DROPBEAR_SUCCESS; - } - - /* calculate the mac */ - if (hmac_init(&hmac, - find_hash(ses.keys->recv_algo_mac->hashdesc->name), - ses.keys->recvmackey, - ses.keys->recv_algo_mac->keysize) - != CRYPT_OK) { - dropbear_exit("HMAC error"); - } + unsigned char mac_bytes[MAX_MAC_LEN]; + unsigned int mac_size, contents_len; - /* sequence number */ - STORE32H(ses.recvseq, tempbuf); - if (hmac_process(&hmac, tempbuf, 4) != CRYPT_OK) { - dropbear_exit("HMAC error"); - } + mac_size = ses.keys->trans.algo_mac->hashsize; + contents_len = ses.readbuf->len - mac_size; - buf_setpos(sourcebuf, 0); - len = sourcebuf->len; - if (hmac_process(&hmac, buf_getptr(sourcebuf, len), len) != CRYPT_OK) { - dropbear_exit("HMAC error"); - } - - bufsize = sizeof(tempbuf); - if (hmac_done(&hmac, tempbuf, &bufsize) != CRYPT_OK) { - dropbear_exit("HMAC error"); - } + buf_setpos(ses.readbuf, 0); + make_mac(ses.recvseq, &ses.keys->recv, ses.readbuf, contents_len, mac_bytes); /* compare the hash */ - if (memcmp(tempbuf, buf_getptr(macbuf, macsize), macsize) != 0) { + buf_setpos(ses.readbuf, contents_len); + if (memcmp(mac_bytes, buf_getptr(ses.readbuf, mac_size), mac_size) != 0) { return DROPBEAR_FAILURE; } else { return DROPBEAR_SUCCESS; @@ -353,7 +322,7 @@ static buffer* buf_decompress(buffer* buf, unsigned int len) { buffer * ret; z_streamp zstream; - zstream = ses.keys->recv_zstream; + zstream = ses.keys->recv.zstream; ret = buf_new(len); zstream->avail_in = len; @@ -468,8 +437,8 @@ void encrypt_packet() { return; } - blocksize = ses.keys->trans_algo_crypt->blocksize; - mac_size = ses.keys->trans_algo_mac->hashsize; + blocksize = ses.keys->trans.algo_crypt->blocksize; + mac_size = ses.keys->trans.algo_mac->hashsize; /* Encrypted packet len is payload+5, then worst case is if we are 3 away * from a blocksize multiple. In which case we need to pad to the @@ -526,17 +495,17 @@ void encrypt_packet() { buf_incrlen(writebuf, padlen); genrandom(buf_getptr(writebuf, padlen), padlen); - make_mac(writebuf, mac_bytes); + make_mac(ses.transseq, &ses.keys->trans, writebuf, writebuf->len, mac_bytes); /* do the actual encryption, in-place */ buf_setpos(writebuf, 0); /* encrypt it in-place*/ len = writebuf->len; - if (ses.keys->trans_crypt_mode->encrypt( + if (ses.keys->trans.crypt_mode->encrypt( buf_getptr(writebuf, len), buf_getwriteptr(writebuf, len), len, - &ses.keys->trans_cipher_state) != CRYPT_OK) { + &ses.keys->trans.cipher_state) != CRYPT_OK) { dropbear_exit("error encrypting"); } buf_incrpos(writebuf, len); @@ -557,35 +526,36 @@ void encrypt_packet() { /* Create the packet mac, and append H(seqno|clearbuf) to the output */ -/* output_mac must have ses.keys->trans_algo_mac->hashsize bytes. */ -static void make_mac(buffer * clearwritebuf, unsigned char *output_mac) { +/* output_mac must have ses.keys->trans.algo_mac->hashsize bytes. */ +static void make_mac(unsigned int seqno, const struct key_context_directional * key_state, + buffer * clear_buf, unsigned int clear_len, + unsigned char *output_mac) { unsigned char seqbuf[4]; unsigned long bufsize; hmac_state hmac; TRACE(("enter writemac")) - if (ses.keys->trans_algo_mac->hashsize > 0) { + if (key_state->algo_mac->hashsize > 0) { /* calculate the mac */ if (hmac_init(&hmac, - find_hash(ses.keys->trans_algo_mac->hashdesc->name), - ses.keys->transmackey, - ses.keys->trans_algo_mac->keysize) != CRYPT_OK) { + key_state->hash_index, + key_state->mackey, + key_state->algo_mac->keysize) != CRYPT_OK) { dropbear_exit("HMAC error"); } /* sequence number */ - STORE32H(ses.transseq, seqbuf); + STORE32H(seqno, seqbuf); if (hmac_process(&hmac, seqbuf, 4) != CRYPT_OK) { dropbear_exit("HMAC error"); } /* the actual contents */ - buf_setpos(clearwritebuf, 0); + buf_setpos(clear_buf, 0); if (hmac_process(&hmac, - buf_getptr(clearwritebuf, - clearwritebuf->len), - clearwritebuf->len) != CRYPT_OK) { + buf_getptr(clear_buf, clear_len), + clear_len) != CRYPT_OK) { dropbear_exit("HMAC error"); } @@ -609,29 +579,29 @@ static void buf_compress(buffer * dest, buffer * src, unsigned int len) { while (1) { - ses.keys->trans_zstream->avail_in = endpos - src->pos; - ses.keys->trans_zstream->next_in = - buf_getptr(src, ses.keys->trans_zstream->avail_in); + ses.keys->trans.zstream->avail_in = endpos - src->pos; + ses.keys->trans.zstream->next_in = + buf_getptr(src, ses.keys->trans.zstream->avail_in); - ses.keys->trans_zstream->avail_out = dest->size - dest->pos; - ses.keys->trans_zstream->next_out = - buf_getwriteptr(dest, ses.keys->trans_zstream->avail_out); + ses.keys->trans.zstream->avail_out = dest->size - dest->pos; + ses.keys->trans.zstream->next_out = + buf_getwriteptr(dest, ses.keys->trans.zstream->avail_out); - result = deflate(ses.keys->trans_zstream, Z_SYNC_FLUSH); + result = deflate(ses.keys->trans.zstream, Z_SYNC_FLUSH); - buf_setpos(src, endpos - ses.keys->trans_zstream->avail_in); - buf_setlen(dest, dest->size - ses.keys->trans_zstream->avail_out); + buf_setpos(src, endpos - ses.keys->trans.zstream->avail_in); + buf_setlen(dest, dest->size - ses.keys->trans.zstream->avail_out); buf_setpos(dest, dest->len); if (result != Z_OK) { dropbear_exit("zlib error"); } - if (ses.keys->trans_zstream->avail_in == 0) { + if (ses.keys->trans.zstream->avail_in == 0) { break; } - dropbear_assert(ses.keys->trans_zstream->avail_out == 0); + dropbear_assert(ses.keys->trans.zstream->avail_out == 0); /* the buffer has been filled, we must extend. This only happens in * unusual circumstances where the data grows in size after deflate(), diff --git a/packet.h b/packet.h index 8fadeb3..4645b14 100644 --- a/packet.h +++ b/packet.h @@ -44,6 +44,6 @@ typedef struct PacketType { #define PACKET_PADDING_OFF 4 #define PACKET_PAYLOAD_OFF 5 -#define INIT_READBUF 200 +#define INIT_READBUF 128 #endif /* _PACKET_H_ */ diff --git a/session.h b/session.h index 1106db4..5a4569e 100644 --- a/session.h +++ b/session.h @@ -60,42 +60,36 @@ void cli_session(int sock_in, int sock_out, char *remotehost); void cli_session_cleanup(); void cleantext(unsigned char* dirtytext); -struct key_context { - - const struct dropbear_cipher *recv_algo_crypt; /* NULL for none */ - const struct dropbear_cipher *trans_algo_crypt; /* NULL for none */ - const struct dropbear_cipher_mode *recv_crypt_mode; - const struct dropbear_cipher_mode *trans_crypt_mode; - const struct dropbear_hash *recv_algo_mac; /* NULL for none */ - const struct dropbear_hash *trans_algo_mac; /* NULL for none */ - char algo_kex; - char algo_hostkey; - - char recv_algo_comp; /* compression */ - char trans_algo_comp; - int allow_compress; /* whether compression has started (useful in - zlib@openssh.com delayed compression case) */ +/* crypto parameters that are stored individually for transmit and receive */ +struct key_context_directional { + const struct dropbear_cipher *algo_crypt; /* NULL for none */ + const struct dropbear_cipher_mode *crypt_mode; + const struct dropbear_hash *algo_mac; /* NULL for none */ + int hash_index; /* lookup for libtomcrypt */ + char algo_comp; /* compression */ #ifndef DISABLE_ZLIB - z_streamp recv_zstream; - z_streamp trans_zstream; + z_streamp zstream; #endif - /* actual keys */ union { symmetric_CBC cbc; #ifdef DROPBEAR_ENABLE_CTR_MODE symmetric_CTR ctr; #endif - } recv_cipher_state; - union { - symmetric_CBC cbc; -#ifdef DROPBEAR_ENABLE_CTR_MODE - symmetric_CTR ctr; -#endif - } trans_cipher_state; - unsigned char recvmackey[MAX_MAC_KEY]; - unsigned char transmackey[MAX_MAC_KEY]; + } cipher_state; + unsigned char mackey[MAX_MAC_KEY]; +}; +struct key_context { + + struct key_context_directional recv; + struct key_context_directional trans; + + char algo_kex; + char algo_hostkey; + + int allow_compress; /* whether compression has started (useful in + zlib@openssh.com delayed compression case) */ }; struct packetlist; @@ -128,8 +122,7 @@ struct sshsession { throughout the code, as handlers fill out this buffer with the packet to send. */ struct Queue writequeue; /* A queue of encrypted packets to send */ - buffer *readbuf; /* Encrypted */ - buffer *decryptreadbuf; /* Post-decryption */ + buffer *readbuf; /* From the wire, decrypted in-place */ buffer *payload; /* Post-decompression, the actual SSH packet */ unsigned int transseq, recvseq; /* Sequence IDs */ From 2b54d3397c0d5659b450fb5069891348dc2a1d83 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 3 Mar 2009 13:20:00 +0000 Subject: [PATCH 10/45] Remove extraneous debugging --HG-- extra : convert_revision : 58d53a027555e98d6d274b4659d38211eea7ad11 --- packet.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/packet.c b/packet.c index 6a294f4..37ffdd2 100644 --- a/packet.c +++ b/packet.c @@ -250,8 +250,6 @@ void decrypt_packet() { } buf_incrpos(ses.readbuf, len); - printhex("readbuf decrypted", ses.readbuf->data, ses.readbuf->len); - /* check the hmac */ if (checkmac() != DROPBEAR_SUCCESS) { dropbear_exit("Integrity error"); From b639e18d39788b0be5f29ef36a2b3f127d1aa7ed Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 3 Mar 2009 13:42:54 +0000 Subject: [PATCH 11/45] - Turn DROPBEAR_SMALL_CODE off by default --HG-- extra : convert_revision : ef3b41f37e9f4dd45358bc40f9559ee23f71c284 --- options.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/options.h b/options.h index def8ccb..c13a12e 100644 --- a/options.h +++ b/options.h @@ -46,9 +46,10 @@ /*#define NO_FAST_EXPTMOD*/ /* Set this if you want to use the DROPBEAR_SMALL_CODE option. This can save -several kB in binary size, however will make the symmetrical ciphers (AES, DES -etc) slower (perhaps by 50%). Recommended for most small systems. */ -#define DROPBEAR_SMALL_CODE +several kB in binary size however will make the symmetrical ciphers and hashes +slower, perhaps by 50%. Recommended for small systems that aren't doing +much traffic. */ +/*#define DROPBEAR_SMALL_CODE*/ /* Enable X11 Forwarding - server only */ #define ENABLE_X11FWD From 061565865c5c6d590ba98d1393a91f0aadb02689 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 17 Mar 2009 22:30:25 +0000 Subject: [PATCH 12/45] - Add the signal pipe to maxfd --HG-- extra : convert_revision : 0a8dfaa3e5365a2004db2b55895e11f65b5cefcc --- common-session.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common-session.c b/common-session.c index 3663534..1348da2 100644 --- a/common-session.c +++ b/common-session.c @@ -71,6 +71,9 @@ void common_session_init(int sock_in, int sock_out, char* remotehost) { } setnonblocking(ses.signal_pipe[0]); setnonblocking(ses.signal_pipe[1]); + + ses.maxfd = MAX(ses.maxfd, ses.signal_pipe[0]); + ses.maxfd = MAX(ses.maxfd, ses.signal_pipe[1]); kexfirstinitialise(); /* initialise the kex state */ From 6e78eca7c8c24b456e84faa4f695a05b5e59212d Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Mon, 8 Jun 2009 14:40:29 +0000 Subject: [PATCH 13/45] use memset() rather than bzero() --HG-- extra : convert_revision : d44b31a46d0fdfcc92bf4f16e7c49fd49eb40aa1 --- svr-main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/svr-main.c b/svr-main.c index 8d57084..45ea4be 100644 --- a/svr-main.c +++ b/svr-main.c @@ -133,7 +133,7 @@ void main_noinetd() { for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) { childpipes[i] = -1; } - bzero(preauth_addrs, sizeof(preauth_addrs)); + memset(preauth_addrs, 0x0, sizeof(preauth_addrs)); /* Set up the listening sockets */ listensockcount = listensockets(listensocks, MAX_LISTEN_ADDR, &maxsock); From fe03c39241b103202d7e12eb49a338ad6e24db48 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Mon, 8 Jun 2009 14:44:23 +0000 Subject: [PATCH 14/45] Wrap proxycmd function in ENABLE_CLI_PROXYCMD #ifdef --HG-- extra : convert_revision : 6d6160b277bfc7c2db6888a2ac91ac618cef6de3 --- cli-runopts.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cli-runopts.c b/cli-runopts.c index 9c10fc3..986f830 100644 --- a/cli-runopts.c +++ b/cli-runopts.c @@ -90,6 +90,10 @@ static void printhelp() { } +/* Note that options that affect the transport or auth layers (such as -i or + * -W) should also be passed through to a multi-hop child proxycmd, in + * parse_multihop_hostname(). */ + void cli_getopts(int argc, char ** argv) { unsigned int i, j; @@ -469,9 +473,9 @@ static void parse_multihop_hostname(const char* orighostarg, const char* argv0) if (cli_opts.remoteport == NULL) { cli_opts.remoteport = "22"; } - cmd_len = strlen(remainder) + cmd_len = strlen(argv0) + strlen(remainder) + strlen(cli_opts.remotehost) + strlen(cli_opts.remoteport) - + strlen(argv0) + 30; + + 30; cli_opts.proxycmd = m_malloc(cmd_len); snprintf(cli_opts.proxycmd, cmd_len, "%s -B %s:%s %s", argv0, cli_opts.remotehost, cli_opts.remoteport, remainder); From a21cf67a6c5bfed10175c9cdeee312d697aa3343 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Mon, 8 Jun 2009 14:51:22 +0000 Subject: [PATCH 15/45] disapproval of revision '6d6160b277bfc7c2db6888a2ac91ac618cef6de3' --HG-- extra : convert_revision : 64088637337d7b6024a9b48b9a616eecf8621cf6 --- cli-runopts.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/cli-runopts.c b/cli-runopts.c index 986f830..9c10fc3 100644 --- a/cli-runopts.c +++ b/cli-runopts.c @@ -90,10 +90,6 @@ static void printhelp() { } -/* Note that options that affect the transport or auth layers (such as -i or - * -W) should also be passed through to a multi-hop child proxycmd, in - * parse_multihop_hostname(). */ - void cli_getopts(int argc, char ** argv) { unsigned int i, j; @@ -473,9 +469,9 @@ static void parse_multihop_hostname(const char* orighostarg, const char* argv0) if (cli_opts.remoteport == NULL) { cli_opts.remoteport = "22"; } - cmd_len = strlen(argv0) + strlen(remainder) + cmd_len = strlen(remainder) + strlen(cli_opts.remotehost) + strlen(cli_opts.remoteport) - + 30; + + strlen(argv0) + 30; cli_opts.proxycmd = m_malloc(cmd_len); snprintf(cli_opts.proxycmd, cmd_len, "%s -B %s:%s %s", argv0, cli_opts.remotehost, cli_opts.remoteport, remainder); From 4be3826dd5426923b12fd0326783973e1af38228 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Mon, 8 Jun 2009 14:53:29 +0000 Subject: [PATCH 16/45] Wrap proxycmd function in ENABLE_CLI_PROXYCMD #ifdef (commit the right file this time) --HG-- extra : convert_revision : d86e846566d01b739b51fa2ecdb8f62006e38dbd --- cli-main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cli-main.c b/cli-main.c index 4ba43c0..2c329a4 100644 --- a/cli-main.c +++ b/cli-main.c @@ -32,7 +32,9 @@ static void cli_dropbear_exit(int exitcode, const char* format, va_list param); static void cli_dropbear_log(int priority, const char* format, va_list param); +#ifdef ENABLE_CLI_PROXYCMD static void cli_proxy_cmd(int *sock_in, int *sock_out); +#endif #if defined(DBMULTI_dbclient) || !defined(DROPBEAR_MULTI) #if defined(DBMULTI_dbclient) && defined(DROPBEAR_MULTI) @@ -132,6 +134,7 @@ static void exec_proxy_cmd(void *user_data_cmd) { dropbear_exit("Failed to run '%s'\n", cmd); } +#ifdef ENABLE_CLI_PROXYCMD static void cli_proxy_cmd(int *sock_in, int *sock_out) { int ret; @@ -144,3 +147,4 @@ static void cli_proxy_cmd(int *sock_in, int *sock_out) { *sock_in = *sock_out = -1; } } +#endif // ENABLE_CLI_PROXYCMD From 08893f03a5530a7bbe7ba724c575458a4fc9f31f Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 9 Jun 2009 13:18:52 +0000 Subject: [PATCH 17/45] - Don't memcpy() in place with void_encrypt --HG-- extra : convert_revision : d123343d78df1b5998d8dd2674fd83fd682ce0c0 --- common-algo.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common-algo.c b/common-algo.c index 8863367..892399f 100644 --- a/common-algo.c +++ b/common-algo.c @@ -31,7 +31,9 @@ static int void_cipher(const unsigned char* in, unsigned char* out, unsigned long len, void *cipher_state) { - memcpy(out, in, len); + if (in != out) { + memmove(out, in, len); + } return CRYPT_OK; } From d6441f4397d802115499e4262071442707873aec Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 12 Jun 2009 14:58:43 +0000 Subject: [PATCH 18/45] - Make -i and -W pass through multihop arguments --HG-- extra : convert_revision : 70c64073c9ec07b4dfb54ee60e39cec2bd8c6910 --- auth.h | 3 +- cli-authpubkey.c | 1 + cli-kex.c | 1 + cli-main.c | 1 + cli-runopts.c | 80 ++++++++++++++++++++++++++++++++++++------------ 5 files changed, 66 insertions(+), 20 deletions(-) diff --git a/auth.h b/auth.h index 83a2f8e..a5f6ee1 100644 --- a/auth.h +++ b/auth.h @@ -130,7 +130,8 @@ struct SignKeyList { sign_key *key; int type; /* The type of key */ struct SignKeyList *next; - /* filename? or the buffer? for encrypted keys, so we can later get + char *filename; + /* the buffer? for encrypted keys, so we can later get * the private key portion */ }; diff --git a/cli-authpubkey.c b/cli-authpubkey.c index c16ef90..c8cbe46 100644 --- a/cli-authpubkey.c +++ b/cli-authpubkey.c @@ -53,6 +53,7 @@ void cli_pubkeyfail() { } sign_key_free(cli_ses.lastprivkey->key); /* It won't be used again */ + m_free(cli_ses.lastprivkey->filename); m_free(cli_ses.lastprivkey); TRACE(("leave cli_pubkeyfail")) diff --git a/cli-kex.c b/cli-kex.c index 37de6e3..c4048ec 100644 --- a/cli-kex.c +++ b/cli-kex.c @@ -327,4 +327,5 @@ out: if (line != NULL) { buf_free(line); } + m_free(fingerprint); } diff --git a/cli-main.c b/cli-main.c index 2c329a4..e7ddaa8 100644 --- a/cli-main.c +++ b/cli-main.c @@ -65,6 +65,7 @@ int main(int argc, char ** argv) { #ifdef ENABLE_CLI_PROXYCMD if (cli_opts.proxycmd) { cli_proxy_cmd(&sock_in, &sock_out); + m_free(cli_opts.proxycmd); } else #endif { diff --git a/cli-runopts.c b/cli-runopts.c index 9c10fc3..8c2880e 100644 --- a/cli-runopts.c +++ b/cli-runopts.c @@ -91,7 +91,6 @@ static void printhelp() { } void cli_getopts(int argc, char ** argv) { - unsigned int i, j; char ** next = 0; unsigned int cmdlen; @@ -112,6 +111,7 @@ void cli_getopts(int argc, char ** argv) { char* recv_window_arg = NULL; char* keepalive_arg = NULL; char* idle_timeout_arg = NULL; + char *host_arg = NULL; /* see printhelp() for options */ cli_opts.progname = argv[0]; @@ -304,12 +304,8 @@ void cli_getopts(int argc, char ** argv) { /* Either the hostname or commands */ - if (cli_opts.remotehost == NULL) { -#ifdef ENABLE_CLI_MULTIHOP - parse_multihop_hostname(argv[i], argv[0]); -#else - parse_hostname(argv[i]); -#endif + if (host_arg == NULL) { + host_arg = argv[i]; } else { /* this is part of the commands to send - after this we @@ -338,7 +334,7 @@ void cli_getopts(int argc, char ** argv) { /* And now a few sanity checks and setup */ - if (cli_opts.remotehost == NULL) { + if (host_arg == NULL) { printhelp(); exit(EXIT_FAILURE); } @@ -385,7 +381,15 @@ void cli_getopts(int argc, char ** argv) { dropbear_log(LOG_INFO, "Ignoring command '%s' in netcat mode", cli_opts.cmd); } #endif - + + /* The hostname gets set up last, since + * in multi-hop mode it will require knowledge + * of other flags such as -i */ +#ifdef ENABLE_CLI_MULTIHOP + parse_multihop_hostname(host_arg, argv[0]); +#else + parse_hostname(host_arg); +#endif } #ifdef ENABLE_CLI_PUBKEY_AUTH @@ -398,14 +402,12 @@ static void loadidentityfile(const char* filename) { key = new_sign_key(); keytype = DROPBEAR_SIGNKEY_ANY; if ( readhostkey(filename, key, &keytype) != DROPBEAR_SUCCESS ) { - fprintf(stderr, "Failed loading keyfile '%s'\n", filename); sign_key_free(key); - } else { - nextkey = (struct SignKeyList*)m_malloc(sizeof(struct SignKeyList)); nextkey->key = key; + nextkey->filename = m_strdup(filename); nextkey->next = cli_opts.privkeys; nextkey->type = keytype; cli_opts.privkeys = nextkey; @@ -415,6 +417,39 @@ static void loadidentityfile(const char* filename) { #ifdef ENABLE_CLI_MULTIHOP +static char* +multihop_passthrough_args() { + char *ret; + int total; + unsigned int len = 0; + struct SignKeyList *nextkey; + /* Fill out -i and -W options that make sense for all + * the intermediate processes */ + for (nextkey = cli_opts.privkeys; nextkey; nextkey = nextkey->next) + { + len += 3 + strlen(nextkey->filename); + } + len += 20; // space for -W , terminator. + ret = m_malloc(len); + total = 0; + + if (opts.recv_window != DEFAULT_RECV_WINDOW) + { + int written = snprintf(ret+total, len-total, "-W %d", opts.recv_window); + total += written; + } + + for (nextkey = cli_opts.privkeys; nextkey; nextkey = nextkey->next) + { + const size_t size = len - total; + int written = snprintf(ret+total, size, "-i %s", nextkey->filename); + dropbear_assert(written < size); + total += written; + } + + return ret; +} + /* Sets up 'onion-forwarding' connections. This will spawn * a separate dbclient process for each hop. * As an example, if the cmdline is @@ -429,6 +464,7 @@ static void loadidentityfile(const char* filename) { */ static void parse_multihop_hostname(const char* orighostarg, const char* argv0) { char *userhostarg = NULL; + char *hostbuf = NULL; char *last_hop = NULL;; char *remainder = NULL; @@ -441,11 +477,12 @@ static void parse_multihop_hostname(const char* orighostarg, const char* argv0) && strchr(cli_opts.username, ',') && strchr(cli_opts.username, '@')) { unsigned int len = strlen(orighostarg) + strlen(cli_opts.username) + 2; - userhostarg = m_malloc(len); - snprintf(userhostarg, len, "%s@%s", cli_opts.username, orighostarg); + hostbuf = m_malloc(len); + snprintf(hostbuf, len, "%s@%s", cli_opts.username, orighostarg); } else { - userhostarg = m_strdup(orighostarg); + hostbuf = m_strdup(orighostarg); } + userhostarg = hostbuf; last_hop = strrchr(userhostarg, ','); if (last_hop) { @@ -463,19 +500,24 @@ static void parse_multihop_hostname(const char* orighostarg, const char* argv0) if (last_hop) { /* Set up the proxycmd */ unsigned int cmd_len = 0; + char *passthrough_args = multihop_passthrough_args(); if (cli_opts.proxycmd) { dropbear_exit("-J can't be used with multihop mode"); } if (cli_opts.remoteport == NULL) { cli_opts.remoteport = "22"; } - cmd_len = strlen(remainder) + cmd_len = strlen(argv0) + strlen(remainder) + strlen(cli_opts.remotehost) + strlen(cli_opts.remoteport) - + strlen(argv0) + 30; + + strlen(passthrough_args) + + 30; cli_opts.proxycmd = m_malloc(cmd_len); - snprintf(cli_opts.proxycmd, cmd_len, "%s -B %s:%s %s", - argv0, cli_opts.remotehost, cli_opts.remoteport, remainder); + snprintf(cli_opts.proxycmd, cmd_len, "%s -B %s:%s %s %s", + argv0, cli_opts.remotehost, cli_opts.remoteport, + passthrough_args, remainder); + m_free(passthrough_args); } + m_free(hostbuf); } #endif /* !ENABLE_CLI_MULTIHOP */ From cb82c6e3e0db91adc8aa65620935f4c9d57f70e9 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 16 Jun 2009 15:22:33 +0000 Subject: [PATCH 19/45] - Print banner to stderr. Probably the right way, and avoids bad interactions with multihop or netcat-alike mode. --HG-- extra : convert_revision : 899a8851a5edf840b2f7925bcc26ffe99dcac54d --- cli-auth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli-auth.c b/cli-auth.c index 731d769..8b6a440 100644 --- a/cli-auth.c +++ b/cli-auth.c @@ -91,7 +91,7 @@ void recv_msg_userauth_banner() { } } - printf("%s\n", banner); + fprintf(stderr, "%s\n", banner); out: m_free(banner); From c6582dbe370705270e6164ff8212dbdddafd9250 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Wed, 1 Jul 2009 04:53:17 +0000 Subject: [PATCH 20/45] Make it compile, update for changes in channel structure. --HG-- branch : agent-client extra : convert_revision : 84676a98a0848224078a716b1292744a34e9d80c --- Makefile.in | 3 ++- agentfwd.h | 7 +++---- auth.h | 9 ++++++--- channel.h | 2 +- chansession.h | 2 +- cli-agentfwd.c | 17 ++++++++--------- cli-authpubkey.c | 4 ++-- cli-session.c | 1 + dbutil.c | 7 ++----- session.h | 11 +++++++++++ sysoptions.h | 10 +++++----- 11 files changed, 42 insertions(+), 31 deletions(-) diff --git a/Makefile.in b/Makefile.in index 4c8441f..efd9e57 100644 --- a/Makefile.in +++ b/Makefile.in @@ -29,7 +29,8 @@ SVROBJS=svr-kex.o svr-algo.o svr-auth.o sshpty.o \ CLIOBJS=cli-algo.o cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \ cli-session.o cli-service.o cli-runopts.o cli-chansession.o \ - cli-authpubkey.o cli-tcpfwd.o cli-channel.o cli-authinteract.o + cli-authpubkey.o cli-tcpfwd.o cli-channel.o cli-authinteract.o \ + cli-agentfwd.o CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \ common-channel.o common-chansession.o termcodes.o loginrec.o \ diff --git a/agentfwd.h b/agentfwd.h index 3684455..5928220 100644 --- a/agentfwd.h +++ b/agentfwd.h @@ -23,11 +23,11 @@ * SOFTWARE. */ #ifndef _AGENTFWD_H_ #define _AGENTFWD_H_ -#ifndef DISABLE_AGENTFWD #include "includes.h" #include "chansession.h" #include "channel.h" +#include "auth.h" /* An agent reply can be reasonably large, as it can * contain a list of all public keys held by the agent. @@ -39,14 +39,13 @@ void agentsetauth(struct ChanSess *chansess); void agentcleanup(struct ChanSess * chansess); void agentset(struct ChanSess *chansess); -SignKeyList * load_agent_keys(); +void load_agent_keys(); #ifdef __hpux #define seteuid(a) setresuid(-1, (a), -1) #define setegid(a) setresgid(-1, (a), -1) #endif -extern const struct ChanSess chan_cli_agent; +extern const struct ChanType cli_chan_agent; -#endif /* DROPBEAR_AGENTFWD */ #endif /* _AGENTFWD_H_ */ diff --git a/auth.h b/auth.h index 55efeef..a35416c 100644 --- a/auth.h +++ b/auth.h @@ -26,6 +26,7 @@ #define _AUTH_H_ #include "includes.h" +#include "signkey.h" #include "chansession.h" void svr_authinitialise(); @@ -124,8 +125,10 @@ struct AuthState { }; /* Sources for signing keys */ -#define SIGNKEY_SOURCE_RAW_FILE 1 -#define SIGNKEY_SOURCE_AGENT 21 +typedef enum { + SIGNKEY_SOURCE_RAW_FILE, + SIGNKEY_SOURCE_AGENT, +} signkey_source; struct SignKeyList; /* A singly linked list of signing keys */ @@ -134,7 +137,7 @@ struct SignKeyList { sign_key *key; int type; /* The type of key */ struct SignKeyList *next; - int source; + signkey_source source; char *filename; /* the buffer? for encrypted keys, so we can later get * the private key portion */ diff --git a/channel.h b/channel.h index 46f023e..5c63226 100644 --- a/channel.h +++ b/channel.h @@ -58,7 +58,7 @@ struct Channel { unsigned int recvmaxpacket, transmaxpacket; void* typedata; /* a pointer to type specific data */ int writefd; /* read from wire, written to insecure side */ - int readfd; /* read from insecure size, written to wire */ + int readfd; /* read from insecure side, written to wire */ int errfd; /* used like writefd or readfd, depending if it's client or server. Doesn't exactly belong here, but is cleaner here */ circbuffer *writebuf; /* data from the wire, for local consumption */ diff --git a/chansession.h b/chansession.h index 4513b1a..8d8e173 100644 --- a/chansession.h +++ b/chansession.h @@ -60,7 +60,7 @@ struct ChanSess { unsigned char x11singleconn; #endif -#ifndef DISABLE_AGENTFWD +#ifdef ENABLE_SVR_AGENTFWD struct Listener * agentlistener; char * agentfile; char * agentdir; diff --git a/cli-agentfwd.c b/cli-agentfwd.c index f0fe385..f2d903a 100644 --- a/cli-agentfwd.c +++ b/cli-agentfwd.c @@ -47,7 +47,7 @@ static int new_agent_chan(struct Channel * channel); -const struct ChanType chan_cli_agent = { +const struct ChanType cli_chan_agent = { 0, /* sepfds */ "auth-agent@openssh.com", new_agent_chan, @@ -85,8 +85,8 @@ static int new_agent_chan(struct Channel * channel) { ses.maxfd = MAX(ses.maxfd, fd); - channel->infd = fd; - channel->outfd = fd; + channel->readfd = fd; + channel->writefd = fd; // success return 0; @@ -151,7 +151,7 @@ out: return inbuf; } -static SignKeyList * agent_get_key_list(int fd) +static struct SignKeyList * agent_get_key_list(int fd) { buffer * inbuf = NULL; unsigned int num = 0; @@ -183,7 +183,7 @@ static SignKeyList * agent_get_key_list(int fd) num = buf_getint(inbuf); for (i = 0; i < num; i++) { sign_key * pubkey = NULL; - char key_type = DROPBEAR_SIGNKEY_ANY; + int key_type = DROPBEAR_SIGNKEY_ANY; struct SignKeyList *nextkey = NULL; nextkey = (struct SignKeyList*)m_malloc(sizeof(struct SignKeyList)); @@ -218,16 +218,15 @@ out: return retkey; } -/* return DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ -SignKeyList * load_agent_keys() +void load_agent_keys() { - SignKeyList * ret_list; + struct SignKeyList * ret_list; int fd; fd = connect_agent(); if (fd < 0) { dropbear_log(LOG_INFO, "Failed to connect to agent"); - return NULL; + return; } ret_list = agent_get_key_list(fd); diff --git a/cli-authpubkey.c b/cli-authpubkey.c index 20277a1..b314edc 100644 --- a/cli-authpubkey.c +++ b/cli-authpubkey.c @@ -174,11 +174,11 @@ int cli_auth_pubkey() { TRACE(("enter cli_auth_pubkey")) - if (cli_opts.pubkeys == NULL && + if (cli_opts.privkeys == NULL && cli_opts.agent_fwd && !cli_opts.agent_keys_loaded) { /* get the list of available keys from the agent */ - load_agent_keys(&cli_opts.pubkeys); + load_agent_keys(&cli_opts.privkeys); } if (cli_opts.privkeys != NULL) { diff --git a/cli-session.c b/cli-session.c index 4aa2366..f9b372b 100644 --- a/cli-session.c +++ b/cli-session.c @@ -35,6 +35,7 @@ #include "service.h" #include "runopts.h" #include "chansession.h" +#include "agentfwd.h" static void cli_remoteclosed(); static void cli_sessionloop(); diff --git a/dbutil.c b/dbutil.c index 0680b75..59ffcbe 100644 --- a/dbutil.c +++ b/dbutil.c @@ -295,19 +295,16 @@ int dropbear_listen(const char* address, const char* port, return nsock; } -/* Connect to a given unix socket. The socket is not non-blocking */ +/* Connect to a given unix socket. The socket is blocking */ #ifdef ENABLE_CONNECT_UNIX -int connect_unix(const char* addr) -{ +int connect_unix(const char* addr) { struct sockaddr_un egdsock; int fd = -1; memset((void*)&egdsock, 0x0, sizeof(egdsock)); egdsock.sun_family = AF_UNIX; strlcpy(egdsock.sun_path, addr, sizeof(egdsock.sun_path)); - fd = socket(PF_UNIX, SOCK_STREAM, 0); - return fd; } #endif diff --git a/session.h b/session.h index 5a4569e..a3f7220 100644 --- a/session.h +++ b/session.h @@ -215,6 +215,17 @@ struct serversession { }; +struct protocol { + int sock; /* read/write with this */ + buffer * readbuf; /* Pending input data, should read a packet's worth */ + struct Queue writequeue; /* A queue of output buffers to send */ + void (*process)(); /* To be called after reading */ + size_t (*bytes_to_read)(); + void * state; /* protocol specific */ + void (*protocol_closed)(); /* to be run when the sock gets closed */ + void (*loop_handler)(); /* to run at end of each loop */ +}; + typedef enum { KEX_NOTHING, KEXINIT_RCVD, diff --git a/sysoptions.h b/sysoptions.h index 2de1184..28e146c 100644 --- a/sysoptions.h +++ b/sysoptions.h @@ -146,10 +146,6 @@ #define DISABLE_X11FWD #endif -#ifndef ENABLE_AGENTFWD -#define DISABLE_AGENTFWD -#endif - #if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD) #define ENABLE_CLI_ANYTCPFWD #endif @@ -160,7 +156,7 @@ #if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD) || \ defined(ENABLE_SVR_REMOTETCPFWD) || defined(ENABLE_SVR_LOCALTCPFWD) || \ - defined(ENABLE_AGENTFWD) || defined(ENABLE_X11FWD) + defined(ENABLE_SVR_AGENTFWD) || defined(ENABLE_X11FWD) #define USING_LISTENERS #endif @@ -168,6 +164,10 @@ #define ENABLE_CLI_MULTIHOP #endif +#if defined(ENABLE_CLI_AGENTFWD) || defined(DROPBEAR_PRNGD_SOCKET) +#define ENABLE_CONNECT_UNIX +#endif + #if defined(DROPBEAR_CLIENT) || defined(ENABLE_SVR_PUBKEY_AUTH) #define DROPBEAR_KEY_LINES /* ie we're using authorized_keys or known_hosts */ #endif From 9dc9aff0164ba777ae9806e9608af09aaf1a786e Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Wed, 1 Jul 2009 06:27:27 +0000 Subject: [PATCH 21/45] Talking to the agent works now. Can't interpret the pubkeys. --HG-- branch : agent-client extra : convert_revision : 357373f28e889108178b8627480fd24bc26dcbd7 --- agentfwd.h | 2 +- cli-agentfwd.c | 41 +++++++++++++++++++++++------------------ cli-authpubkey.c | 4 ++-- dbutil.c | 19 +++++++++++++------ debug.h | 2 +- session.h | 11 ----------- 6 files changed, 40 insertions(+), 39 deletions(-) diff --git a/agentfwd.h b/agentfwd.h index 5928220..8575b0c 100644 --- a/agentfwd.h +++ b/agentfwd.h @@ -39,7 +39,7 @@ void agentsetauth(struct ChanSess *chansess); void agentcleanup(struct ChanSess * chansess); void agentset(struct ChanSess *chansess); -void load_agent_keys(); +void load_agent_keys(struct SignKeyList * ret_list); #ifdef __hpux #define seteuid(a) setresuid(-1, (a), -1) diff --git a/cli-agentfwd.c b/cli-agentfwd.c index f2d903a..b47a6cc 100644 --- a/cli-agentfwd.c +++ b/cli-agentfwd.c @@ -94,13 +94,14 @@ static int new_agent_chan(struct Channel * channel) { /* Sends a request to the agent, returning a newly allocated buffer * with the response */ +/* This function will block waiting for a response - it will + * only be used by client authentication (not for forwarded requests) + * won't cause problems for interactivity. */ /* Packet format (from draft-ylonen) 4 bytes Length, msb first. Does not include length itself. 1 byte Packet type. The value 255 is reserved for future extensions. data Any data, depending on packet type. Encoding as in the ssh packet - protocol. - - In this case, data is always empty + protocol. */ static buffer * agent_request(int fd, unsigned char type) { @@ -113,16 +114,17 @@ static buffer * agent_request(int fd, unsigned char type) { buf_putint(payload, 1); buf_putbyte(payload, type); + buf_setpos(payload, 0); ret = atomicio(write, fd, buf_getptr(payload, payload->len), payload->len); if ((size_t)ret != payload->len) { - TRACE(("write failed for agent_request")) + TRACE(("write failed fd %d for agent_request, %s", fd, strerror(errno))) goto out; } buf_free(payload); payload = NULL; - + TRACE(("Wrote out bytes for agent_request")) /* Now we read the response */ inbuf = buf_new(4); ret = atomicio(read, fd, buf_getwriteptr(inbuf, 4), 4); @@ -130,19 +132,27 @@ static buffer * agent_request(int fd, unsigned char type) { TRACE(("read of length failed for agent_request")) goto out; } + buf_setpos(inbuf, 0); + buf_setlen(inbuf, ret); readlen = buf_getint(inbuf); if (readlen > MAX_AGENT_REPLY) { TRACE(("agent reply is too big")); goto out; } + + TRACE(("agent_request readlen is %d", readlen)) buf_resize(inbuf, readlen); + buf_setpos(inbuf, 0); ret = atomicio(read, fd, buf_getwriteptr(inbuf, readlen), readlen); if ((size_t)ret != readlen) { TRACE(("read of data failed for agent_request")) goto out; } + buf_incrwritepos(inbuf, readlen); + buf_setpos(inbuf, 0); + TRACE(("agent_request success, length %d", readlen)) out: if (payload) @@ -151,17 +161,18 @@ out: return inbuf; } -static struct SignKeyList * agent_get_key_list(int fd) +static void agent_get_key_list(int fd, struct SignKeyList * ret_list) { buffer * inbuf = NULL; unsigned int num = 0; unsigned char packet_type; unsigned int i; - struct SignKeyList *retkey = NULL, *key = NULL; + struct SignKeyList *key = NULL; int ret; inbuf = agent_request(fd, SSH2_AGENTC_REQUEST_IDENTITIES); if (!inbuf) { + TRACE(("agent_request returned no identities")) goto out; } @@ -187,11 +198,8 @@ static struct SignKeyList * agent_get_key_list(int fd) struct SignKeyList *nextkey = NULL; nextkey = (struct SignKeyList*)m_malloc(sizeof(struct SignKeyList)); - if (key) - key->next = nextkey; - else - retkey = nextkey; - key = nextkey; + ret_list->next = nextkey; + ret_list = nextkey; pubkey = new_sign_key(); ret = buf_get_pub_key(inbuf, pubkey, &key_type); @@ -214,14 +222,11 @@ out: buf_free(inbuf); inbuf = NULL; } - - return retkey; } -void load_agent_keys() +/* Returned keys are appended to ret_list */ +void load_agent_keys(struct SignKeyList * ret_list) { - - struct SignKeyList * ret_list; int fd; fd = connect_agent(); if (fd < 0) { @@ -229,7 +234,7 @@ void load_agent_keys() return; } - ret_list = agent_get_key_list(fd); + agent_get_key_list(fd, ret_list); close(fd); } diff --git a/cli-authpubkey.c b/cli-authpubkey.c index b314edc..b7ecd55 100644 --- a/cli-authpubkey.c +++ b/cli-authpubkey.c @@ -174,11 +174,11 @@ int cli_auth_pubkey() { TRACE(("enter cli_auth_pubkey")) - if (cli_opts.privkeys == NULL && - cli_opts.agent_fwd && + if (cli_opts.agent_fwd && !cli_opts.agent_keys_loaded) { /* get the list of available keys from the agent */ load_agent_keys(&cli_opts.privkeys); + cli_opts.agent_keys_loaded = 1; } if (cli_opts.privkeys != NULL) { diff --git a/dbutil.c b/dbutil.c index 59ffcbe..73302e0 100644 --- a/dbutil.c +++ b/dbutil.c @@ -297,14 +297,22 @@ int dropbear_listen(const char* address, const char* port, /* Connect to a given unix socket. The socket is blocking */ #ifdef ENABLE_CONNECT_UNIX -int connect_unix(const char* addr) { - struct sockaddr_un egdsock; +int connect_unix(const char* path) { + struct sockaddr_un addr; int fd = -1; - memset((void*)&egdsock, 0x0, sizeof(egdsock)); - egdsock.sun_family = AF_UNIX; - strlcpy(egdsock.sun_path, addr, sizeof(egdsock.sun_path)); + memset((void*)&addr, 0x0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strlcpy(addr.sun_path, path, sizeof(addr.sun_path)); fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + TRACE(("Failed to open unix socket")) + return -1; + } + if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { + TRACE(("Failed to connect to '%s' socket", path)) + return -1; + } return fd; } #endif @@ -574,7 +582,6 @@ unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport) { } return retstring; - } /* Get the hostname corresponding to the address addr. On failure, the IP diff --git a/debug.h b/debug.h index b8c2a57..a9cc0bd 100644 --- a/debug.h +++ b/debug.h @@ -39,7 +39,7 @@ * Caution: Don't use this in an unfriendly environment (ie unfirewalled), * since the printing may not sanitise strings etc. This will add a reasonable * amount to your executable size. */ -/*#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 a3f7220..5a4569e 100644 --- a/session.h +++ b/session.h @@ -215,17 +215,6 @@ struct serversession { }; -struct protocol { - int sock; /* read/write with this */ - buffer * readbuf; /* Pending input data, should read a packet's worth */ - struct Queue writequeue; /* A queue of output buffers to send */ - void (*process)(); /* To be called after reading */ - size_t (*bytes_to_read)(); - void * state; /* protocol specific */ - void (*protocol_closed)(); /* to be run when the sock gets closed */ - void (*loop_handler)(); /* to run at end of each loop */ -}; - typedef enum { KEX_NOTHING, KEXINIT_RCVD, From c742137dc8c8dd2163a2353d3382fdf2cae44c24 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Mon, 6 Jul 2009 12:59:13 +0000 Subject: [PATCH 22/45] New standard linked list to use, rather than adhoc SignKeyList or TCPFwdList --HG-- branch : agent-client extra : convert_revision : 5465e639cc3f5ee0c6c55f0de6e7b6d5a8769da3 --- Makefile.in | 4 +-- agentfwd.h | 7 ++-- auth.h | 20 ----------- buffer.c | 14 ++++++++ buffer.h | 1 + cli-agentfwd.c | 59 ++++++++++++++------------------- cli-authpubkey.c | 75 +++++++++++++++++++++++++----------------- cli-chansession.c | 13 -------- cli-runopts.c | 47 +++++++++++++------------- cli-tcpfwd.c | 84 ++++++++++++++++++----------------------------- list.c | 49 +++++++++++++++++++++++++++ list.h | 28 ++++++++++++++++ runopts.h | 6 ++-- session.h | 2 +- signkey.c | 14 ++++++-- signkey.h | 14 ++++++++ tcpfwd.h | 9 ++--- 17 files changed, 254 insertions(+), 192 deletions(-) create mode 100644 list.c create mode 100644 list.h diff --git a/Makefile.in b/Makefile.in index efd9e57..fe10204 100644 --- a/Makefile.in +++ b/Makefile.in @@ -20,7 +20,7 @@ COMMONOBJS=dbutil.o buffer.o \ dss.o bignum.o \ signkey.o rsa.o random.o \ queue.o \ - atomicio.o compat.o fake-rfc2553.o + atomicio.o compat.o fake-rfc2553.o SVROBJS=svr-kex.o svr-algo.o svr-auth.o sshpty.o \ svr-authpasswd.o svr-authpubkey.o svr-authpubkeyoptions.o svr-session.o svr-service.o \ @@ -30,7 +30,7 @@ SVROBJS=svr-kex.o svr-algo.o svr-auth.o sshpty.o \ CLIOBJS=cli-algo.o cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \ cli-session.o cli-service.o cli-runopts.o cli-chansession.o \ cli-authpubkey.o cli-tcpfwd.o cli-channel.o cli-authinteract.o \ - cli-agentfwd.o + cli-agentfwd.o list.o CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \ common-channel.o common-chansession.o termcodes.o loginrec.o \ diff --git a/agentfwd.h b/agentfwd.h index 8575b0c..e1794a7 100644 --- a/agentfwd.h +++ b/agentfwd.h @@ -28,6 +28,7 @@ #include "chansession.h" #include "channel.h" #include "auth.h" +#include "list.h" /* An agent reply can be reasonably large, as it can * contain a list of all public keys held by the agent. @@ -35,11 +36,13 @@ #define MAX_AGENT_REPLY 10000 int agentreq(struct ChanSess * chansess); -void agentsetauth(struct ChanSess *chansess); void agentcleanup(struct ChanSess * chansess); void agentset(struct ChanSess *chansess); -void load_agent_keys(struct SignKeyList * ret_list); +/* client functions */ +void load_agent_keys(m_list * ret_list); +void agent_buf_sign(buffer *sigblob, sign_key *key, + const unsigned char *data, unsigned int len); #ifdef __hpux #define seteuid(a) setresuid(-1, (a), -1) diff --git a/auth.h b/auth.h index a35416c..d22f812 100644 --- a/auth.h +++ b/auth.h @@ -124,26 +124,6 @@ struct AuthState { }; -/* Sources for signing keys */ -typedef enum { - SIGNKEY_SOURCE_RAW_FILE, - SIGNKEY_SOURCE_AGENT, -} signkey_source; - -struct SignKeyList; -/* A singly linked list of signing keys */ -struct SignKeyList { - - sign_key *key; - int type; /* The type of key */ - struct SignKeyList *next; - signkey_source source; - char *filename; - /* the buffer? for encrypted keys, so we can later get - * the private key portion */ - -}; - #ifdef ENABLE_SVR_PUBKEY_OPTIONS struct PubKeyOptions; struct PubKeyOptions { diff --git a/buffer.c b/buffer.c index 579fa6f..7394993 100644 --- a/buffer.c +++ b/buffer.c @@ -223,6 +223,20 @@ unsigned char* buf_getstring(buffer* buf, unsigned int *retlen) { return ret; } +/* Return a string as a newly allocated buffer */ +buffer * buf_getstringbuf(buffer *buf) { + buffer *ret; + unsigned char* str; + unsigned int len; + str = buf_getstring(buf, &len); + ret = m_malloc(sizeof(*ret)); + ret->data = str; + ret->len = len; + ret->size = len; + ret->pos = 0; + return ret; +} + /* Just increment the buffer position the same as if we'd used buf_getstring, * but don't bother copying/malloc()ing for it */ void buf_eatstring(buffer *buf) { diff --git a/buffer.h b/buffer.h index f9aa6fa..5964370 100644 --- a/buffer.h +++ b/buffer.h @@ -55,6 +55,7 @@ void buf_putbyte(buffer* buf, unsigned char val); unsigned char* buf_getptr(buffer* buf, unsigned int len); unsigned char* buf_getwriteptr(buffer* buf, unsigned int len); unsigned char* buf_getstring(buffer* buf, unsigned int *retlen); +buffer * buf_getstringbuf(buffer *buf); void buf_eatstring(buffer *buf); void buf_putint(buffer* buf, unsigned int val); void buf_putstring(buffer* buf, const unsigned char* str, unsigned int len); diff --git a/cli-agentfwd.c b/cli-agentfwd.c index b47a6cc..0a5661d 100644 --- a/cli-agentfwd.c +++ b/cli-agentfwd.c @@ -22,10 +22,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -/* The basic protocol use to communicate with the agent is defined in - * draft-ylonen-ssh-protocol-00.txt, with the ssh2 extensions defined through - * openssh's implementation. */ - #include "includes.h" #ifdef ENABLE_CLI_AGENTFWD @@ -45,6 +41,9 @@ #include "signkey.h" #include "auth.h" +/* The protocol implemented to talk to OpenSSH's SSH2 agent is documented in + PROTOCOL.agent in recent OpenSSH source distributions (5.1p1 has it). */ + static int new_agent_chan(struct Channel * channel); const struct ChanType cli_chan_agent = { @@ -161,13 +160,12 @@ out: return inbuf; } -static void agent_get_key_list(int fd, struct SignKeyList * ret_list) +static void agent_get_key_list(int fd, m_list * ret_list) { buffer * inbuf = NULL; unsigned int num = 0; unsigned char packet_type; unsigned int i; - struct SignKeyList *key = NULL; int ret; inbuf = agent_request(fd, SSH2_AGENTC_REQUEST_IDENTITIES); @@ -177,14 +175,11 @@ static void agent_get_key_list(int fd, struct SignKeyList * ret_list) } /* The reply has a format of: - * byte packet_type - * int num_keys - * - * string keyblob1 - * string comment1 - * ... - * string keyblob(n) - * string comment(n) + byte SSH2_AGENT_IDENTITIES_ANSWER + uint32 num_keys + Followed by zero or more consecutive keys, encoded as: + string key_blob + string key_comment */ packet_type = buf_getbyte(inbuf); if (packet_type != SSH2_AGENT_IDENTITIES_ANSWER) { @@ -195,25 +190,24 @@ static void agent_get_key_list(int fd, struct SignKeyList * ret_list) for (i = 0; i < num; i++) { sign_key * pubkey = NULL; int key_type = DROPBEAR_SIGNKEY_ANY; + buffer * key_buf; struct SignKeyList *nextkey = NULL; - nextkey = (struct SignKeyList*)m_malloc(sizeof(struct SignKeyList)); - ret_list->next = nextkey; - ret_list = nextkey; - + /* each public key is encoded as a string */ + key_buf = buf_getstringbuf(inbuf); pubkey = new_sign_key(); - ret = buf_get_pub_key(inbuf, pubkey, &key_type); + ret = buf_get_pub_key(key_buf, pubkey, &key_type); + buf_free(key_buf); if (ret != DROPBEAR_SUCCESS) { /* This is slack, properly would cleanup vars etc */ dropbear_exit("Bad pubkey received from agent"); } + pubkey->type = key_type; + pubkey->source = SIGNKEY_SOURCE_AGENT; - key->key = pubkey; - key->next = NULL; - key->type = key_type; - key->source = SIGNKEY_SOURCE_AGENT; + list_append(ret_list, pubkey); - /* We'll ignore the comment */ + /* We'll ignore the comment for now. might want it later.*/ buf_eatstring(inbuf); } @@ -224,8 +218,9 @@ out: } } -/* Returned keys are appended to ret_list */ -void load_agent_keys(struct SignKeyList * ret_list) +/* Returned keys are prepended to ret_list, which will + be updated. */ +void load_agent_keys(m_list *ret_list) { int fd; fd = connect_agent(); @@ -237,13 +232,9 @@ void load_agent_keys(struct SignKeyList * ret_list) agent_get_key_list(fd, ret_list); close(fd); } - -// general procedure: -// - get the list of keys from the agent -// - foreach, send a dummy userauth_pubkey message to the server and see -// if it lets us in -// - if it does, sign and auth -// - if not, repeat. -// + +void agent_buf_sign(buffer *sigblob, sign_key *key, + const unsigned char *data, unsigned int len) { +} #endif diff --git a/cli-authpubkey.c b/cli-authpubkey.c index b7ecd55..0ad0031 100644 --- a/cli-authpubkey.c +++ b/cli-authpubkey.c @@ -30,6 +30,7 @@ #include "ssh.h" #include "runopts.h" #include "auth.h" +#include "agentfwd.h" #ifdef ENABLE_CLI_PUBKEY_AUTH static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign); @@ -37,31 +38,23 @@ static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign); /* Called when we receive a SSH_MSG_USERAUTH_FAILURE for a pubkey request. * We use it to remove the key we tried from the list */ void cli_pubkeyfail() { - - struct SignKeyList *keyitem; - struct SignKeyList **previtem; - - TRACE(("enter cli_pubkeyfail")) - previtem = &cli_opts.privkeys; - - /* Find the key we failed with, and remove it */ - for (keyitem = cli_opts.privkeys; keyitem != NULL; keyitem = keyitem->next) { - if (keyitem == cli_ses.lastprivkey) { - *previtem = keyitem->next; + m_list_elem *iter; + for (iter = cli_opts.privkeys->first; iter; iter = iter->next) { + sign_key *iter_key = (sign_key*)iter->item; + + if (iter_key == cli_ses.lastprivkey) + { + /* found the failing key */ + list_remove(iter); + sign_key_free(iter_key); + cli_ses.lastprivkey = NULL; + return; } - previtem = &keyitem; } - - sign_key_free(cli_ses.lastprivkey->key); /* It won't be used again */ - m_free(cli_ses.lastprivkey->filename); - m_free(cli_ses.lastprivkey); - - TRACE(("leave cli_pubkeyfail")) } void recv_msg_userauth_pk_ok() { - - struct SignKeyList *keyitem = NULL; + m_list_elem *iter; buffer* keybuf = NULL; char* algotype = NULL; unsigned int algolen; @@ -81,9 +74,9 @@ void recv_msg_userauth_pk_ok() { /* Iterate through our keys, find which one it was that matched, and * send a real request with that key */ - for (keyitem = cli_opts.privkeys; keyitem != NULL; keyitem = keyitem->next) { - - if (keyitem->type != keytype) { + for (iter = cli_opts.privkeys->first; iter; iter = iter->next) { + sign_key *key = (sign_key*)iter->item; + if (key->type != keytype) { /* Types differed */ TRACE(("types differed")) continue; @@ -91,7 +84,7 @@ void recv_msg_userauth_pk_ok() { /* Now we compare the contents of the key */ keybuf->pos = keybuf->len = 0; - buf_put_pub_key(keybuf, keyitem->key, keytype); + buf_put_pub_key(keybuf, key, keytype); buf_setpos(keybuf, 0); buf_incrpos(keybuf, 4); /* first int is the length of the remainder (ie remotelen) which has already been taken from @@ -115,11 +108,11 @@ void recv_msg_userauth_pk_ok() { } buf_free(keybuf); - if (keyitem != NULL) { + if (iter != NULL) { TRACE(("matching key")) /* XXX TODO: if it's an encrypted key, here we ask for their * password */ - send_msg_userauth_pubkey(keyitem->key, keytype, 1); + send_msg_userauth_pubkey((sign_key*)iter->item, keytype, 1); } else { TRACE(("That was whacky. We got told that a key was valid, but it didn't match our list. Sounds like dodgy code on Dropbear's part")) } @@ -127,6 +120,25 @@ void recv_msg_userauth_pk_ok() { TRACE(("leave recv_msg_userauth_pk_ok")) } +void cli_buf_put_sign(buffer* buf, sign_key *key, int type, + const unsigned char *data, unsigned int len) +{ + if (key->source == SIGNKEY_SOURCE_AGENT) { + /* Format the agent signature ourselves, as buf_put_sign would. */ + buffer *sigblob; + sigblob = buf_new(MAX_PUBKEY_SIZE); + agent_buf_sign(sigblob, key, data, len); + buf_setpos(sigblob, 0); + buf_putstring(buf, buf_getptr(sigblob, sigblob->len), + sigblob->len); + + buf_free(sigblob); + } else { + buf_put_sign(buf, key, type, data, len); + } + +} + /* TODO: make it take an agent reference to use as well */ static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign) { @@ -170,6 +182,7 @@ static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign) { TRACE(("leave send_msg_userauth_pubkey")) } +/* Returns 1 if a key was tried */ int cli_auth_pubkey() { TRACE(("enter cli_auth_pubkey")) @@ -177,15 +190,15 @@ int cli_auth_pubkey() { if (cli_opts.agent_fwd && !cli_opts.agent_keys_loaded) { /* get the list of available keys from the agent */ - load_agent_keys(&cli_opts.privkeys); + load_agent_keys(cli_opts.privkeys); cli_opts.agent_keys_loaded = 1; } - if (cli_opts.privkeys != NULL) { + if (cli_opts.privkeys->first) { + sign_key * key = (sign_key*)cli_opts.privkeys->first->item; /* Send a trial request */ - send_msg_userauth_pubkey(cli_opts.privkeys->key, - cli_opts.privkeys->type, 0); - cli_ses.lastprivkey = cli_opts.privkeys; + send_msg_userauth_pubkey(key, key->type, 0); + cli_ses.lastprivkey = key; TRACE(("leave cli_auth_pubkey-success")) return 1; } else { diff --git a/cli-chansession.c b/cli-chansession.c index dc8e641..9dc0224 100644 --- a/cli-chansession.c +++ b/cli-chansession.c @@ -424,16 +424,3 @@ void cli_send_chansess_request() { TRACE(("leave cli_send_chansess_request")) } - - -#if 0 - while (cli_opts.localfwds != NULL) { - ret = cli_localtcp(cli_opts.localfwds->listenport, - cli_opts.localfwds->connectaddr, - cli_opts.localfwds->connectport); - if (ret == DROPBEAR_FAILURE) { - dropbear_log(LOG_WARNING, "Failed local port forward %d:%s:%d", - cli_opts.localfwds->listenport, - cli_opts.localfwds->connectaddr, - cli_opts.localfwds->connectport); -#endif diff --git a/cli-runopts.c b/cli-runopts.c index a7c0f82..eab33b7 100644 --- a/cli-runopts.c +++ b/cli-runopts.c @@ -29,6 +29,7 @@ #include "dbutil.h" #include "algo.h" #include "tcpfwd.h" +#include "list.h" cli_runopts cli_opts; /* GLOBAL */ @@ -40,7 +41,7 @@ static void fill_own_user(); static void loadidentityfile(const char* filename); #endif #ifdef ENABLE_CLI_ANYTCPFWD -static void addforward(const char* str, struct TCPFwdList** fwdlist); +static void addforward(const char* str, m_list *fwdlist); #endif #ifdef ENABLE_CLI_NETCAT static void add_netcat(const char *str); @@ -128,14 +129,14 @@ void cli_getopts(int argc, char ** argv) { cli_opts.always_accept_key = 0; cli_opts.is_subsystem = 0; #ifdef ENABLE_CLI_PUBKEY_AUTH - cli_opts.privkeys = NULL; + cli_opts.privkeys = list_new(); #endif #ifdef ENABLE_CLI_LOCALTCPFWD - cli_opts.localfwds = NULL; + cli_opts.localfwds = list_new(); opts.listen_fwd_all = 0; #endif #ifdef ENABLE_CLI_REMOTETCPFWD - cli_opts.remotefwds = NULL; + cli_opts.remotefwds = list_new(); #endif #ifdef ENABLE_CLI_AGENTFWD cli_opts.agent_fwd = 0; @@ -165,7 +166,7 @@ void cli_getopts(int argc, char ** argv) { #ifdef ENABLE_CLI_REMOTETCPFWD if (nextisremote) { TRACE(("nextisremote true")) - addforward(argv[i], &cli_opts.remotefwds); + addforward(argv[i], cli_opts.remotefwds); nextisremote = 0; continue; } @@ -173,7 +174,7 @@ void cli_getopts(int argc, char ** argv) { #ifdef ENABLE_CLI_LOCALTCPFWD if (nextislocal) { TRACE(("nextislocal true")) - addforward(argv[i], &cli_opts.localfwds); + addforward(argv[i], cli_opts.localfwds); nextislocal = 0; continue; } @@ -406,8 +407,6 @@ void cli_getopts(int argc, char ** argv) { #ifdef ENABLE_CLI_PUBKEY_AUTH static void loadidentityfile(const char* filename) { - - struct SignKeyList * nextkey; sign_key *key; int keytype; @@ -417,13 +416,10 @@ static void loadidentityfile(const char* filename) { fprintf(stderr, "Failed loading keyfile '%s'\n", filename); sign_key_free(key); } else { - nextkey = (struct SignKeyList*)m_malloc(sizeof(struct SignKeyList)); - nextkey->key = key; - nextkey->filename = m_strdup(filename); - nextkey->next = cli_opts.privkeys; - nextkey->type = keytype; - nextkey->source = SIGNKEY_SOURCE_RAW_FILE; - cli_opts.privkeys = nextkey; + key->type = keytype; + key->source = SIGNKEY_SOURCE_RAW_FILE; + key->filename = m_strdup(filename); + list_append(cli_opts.privkeys, key); } } #endif @@ -435,12 +431,13 @@ multihop_passthrough_args() { char *ret; int total; unsigned int len = 0; - struct SignKeyList *nextkey; + m_list_elem *iter; /* Fill out -i and -W options that make sense for all * the intermediate processes */ - for (nextkey = cli_opts.privkeys; nextkey; nextkey = nextkey->next) + for (iter = cli_opts.privkeys->first; iter; iter = iter->next) { - len += 3 + strlen(nextkey->filename); + sign_key * key = (sign_key*)iter->item; + len += 3 + strlen(key->filename); } len += 20; // space for -W , terminator. ret = m_malloc(len); @@ -452,10 +449,11 @@ multihop_passthrough_args() { total += written; } - for (nextkey = cli_opts.privkeys; nextkey; nextkey = nextkey->next) + for (iter = cli_opts.privkeys->first; iter; iter = iter->next) { + sign_key * key = (sign_key*)iter->item; const size_t size = len - total; - int written = snprintf(ret+total, size, "-i %s", nextkey->filename); + int written = snprintf(ret+total, size, "-i %s", key->filename); dropbear_assert(written < size); total += written; } @@ -621,12 +619,12 @@ static void fill_own_user() { #ifdef ENABLE_CLI_ANYTCPFWD /* Turn a "listenport:remoteaddr:remoteport" string into into a forwarding * set, and add it to the forwarding list */ -static void addforward(const char* origstr, struct TCPFwdList** fwdlist) { +static void addforward(const char* origstr, m_list *fwdlist) { char * listenport = NULL; char * connectport = NULL; char * connectaddr = NULL; - struct TCPFwdList* newfwd = NULL; + struct TCPFwdEntry* newfwd = NULL; char * str = NULL; TRACE(("enter addforward")) @@ -653,7 +651,7 @@ static void addforward(const char* origstr, struct TCPFwdList** fwdlist) { *connectport = '\0'; connectport++; - newfwd = (struct TCPFwdList*)m_malloc(sizeof(struct TCPFwdList)); + newfwd = m_malloc(sizeof(struct TCPFwdEntry)); /* Now we check the ports - note that the port ints are unsigned, * the check later only checks for >= MAX_PORT */ @@ -680,8 +678,7 @@ static void addforward(const char* origstr, struct TCPFwdList** fwdlist) { } newfwd->have_reply = 0; - newfwd->next = *fwdlist; - *fwdlist = newfwd; + list_append(fwdlist, newfwd); TRACE(("leave addforward: done")) return; diff --git a/cli-tcpfwd.c b/cli-tcpfwd.c index 0e60090..0242654 100644 --- a/cli-tcpfwd.c +++ b/cli-tcpfwd.c @@ -59,27 +59,22 @@ static const struct ChanType cli_chan_tcplocal = { #ifdef ENABLE_CLI_LOCALTCPFWD void setup_localtcp() { - + m_list_elem *iter; int ret; TRACE(("enter setup_localtcp")) - if (cli_opts.localfwds == NULL) { - TRACE(("cli_opts.localfwds == NULL")) - } - - while (cli_opts.localfwds != NULL) { - ret = cli_localtcp(cli_opts.localfwds->listenport, - cli_opts.localfwds->connectaddr, - cli_opts.localfwds->connectport); + for (iter = cli_opts.localfwds->first; iter; iter = iter->next) { + struct TCPFwdEntry * fwd = (struct TCPFwdEntry*)iter->item; + ret = cli_localtcp(fwd->listenport, + fwd->connectaddr, + fwd->connectport); if (ret == DROPBEAR_FAILURE) { dropbear_log(LOG_WARNING, "Failed local port forward %d:%s:%d", - cli_opts.localfwds->listenport, - cli_opts.localfwds->connectaddr, - cli_opts.localfwds->connectport); - } - - cli_opts.localfwds = cli_opts.localfwds->next; + fwd->listenport, + fwd->connectaddr, + fwd->connectport); + } } TRACE(("leave setup_localtcp")) @@ -148,60 +143,47 @@ static void send_msg_global_request_remotetcp(int port) { * being in the same order as we sent the requests. This is the ordering * of the cli_opts.remotefwds list */ void cli_recv_msg_request_success() { - /* Nothing in the packet. We just mark off that we have received the reply, * so that we can report failure for later ones. */ - struct TCPFwdList * iter = NULL; - - iter = cli_opts.remotefwds; - while (iter != NULL) { - if (!iter->have_reply) - { - iter->have_reply = 1; + m_list_elem * iter = NULL; + for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) { + struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item; + if (!fwd->have_reply) { + fwd->have_reply = 1; return; } - iter = iter->next; } } void cli_recv_msg_request_failure() { - struct TCPFwdList * iter = NULL; - - iter = cli_opts.remotefwds; - while (iter != NULL) { - if (!iter->have_reply) - { - iter->have_reply = 1; - dropbear_log(LOG_WARNING, "Remote TCP forward request failed (port %d -> %s:%d)", iter->listenport, iter->connectaddr, iter->connectport); + m_list_elem *iter; + for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) { + struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item; + if (!fwd->have_reply) { + fwd->have_reply = 1; + dropbear_log(LOG_WARNING, "Remote TCP forward request failed (port %d -> %s:%d)", fwd->listenport, fwd->connectaddr, fwd->connectport); return; } - iter = iter->next; } } void setup_remotetcp() { - - struct TCPFwdList * iter = NULL; - + m_list_elem *iter; TRACE(("enter setup_remotetcp")) - if (cli_opts.remotefwds == NULL) { - TRACE(("cli_opts.remotefwds == NULL")) + for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) { + struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item; + send_msg_global_request_remotetcp(fwd->listenport); } - iter = cli_opts.remotefwds; - - while (iter != NULL) { - send_msg_global_request_remotetcp(iter->listenport); - iter = iter->next; - } TRACE(("leave setup_remotetcp")) } static int newtcpforwarded(struct Channel * channel) { unsigned int origport; - struct TCPFwdList * iter = NULL; + m_list_elem * iter = NULL; + struct TCPFwdEntry *fwd; char portstring[NI_MAXSERV]; int sock; int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED; @@ -212,13 +194,11 @@ static int newtcpforwarded(struct Channel * channel) { origport = buf_getint(ses.payload); /* Find which port corresponds */ - iter = cli_opts.remotefwds; - - while (iter != NULL) { - if (origport == iter->listenport) { + for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) { + fwd = (struct TCPFwdEntry*)iter->item; + if (origport == fwd->listenport) { break; } - iter = iter->next; } if (iter == NULL) { @@ -228,8 +208,8 @@ static int newtcpforwarded(struct Channel * channel) { goto out; } - snprintf(portstring, sizeof(portstring), "%d", iter->connectport); - sock = connect_remote(iter->connectaddr, portstring, 1, NULL); + snprintf(portstring, sizeof(portstring), "%d", fwd->connectport); + sock = connect_remote(fwd->connectaddr, portstring, 1, NULL); if (sock < 0) { TRACE(("leave newtcpdirect: sock failed")) err = SSH_OPEN_CONNECT_FAILED; diff --git a/list.c b/list.c new file mode 100644 index 0000000..8be1a3a --- /dev/null +++ b/list.c @@ -0,0 +1,49 @@ +#include "options.h" +#include "dbutil.h" +#include "list.h" + +void list_append(m_list *list, void *item) { + m_list_elem *elem; + + elem = m_malloc(sizeof(*elem)); + elem->item = item; + elem->list = list; + elem->next = NULL; + if (!list->first) { + list->first = elem; + elem->prev = NULL; + } else { + elem->prev = list->last; + list->last->next = elem; + } + list->last = elem; +} + +m_list * list_new() { + m_list *ret = m_malloc(sizeof(m_list)); + ret->first = ret->last = NULL; + return ret; +} + +void * list_remove(m_list_elem *elem) { + void *item = elem->item; + m_list *list = elem->list; + if (list->first == elem) + { + list->first = elem->next; + } + if (list->last == elem) + { + list->last = elem->prev; + } + if (elem->prev) + { + elem->prev->next = elem->next; + } + if (elem->next) + { + elem->next->prev = elem->prev; + } + m_free(elem); + return item; +} \ No newline at end of file diff --git a/list.h b/list.h new file mode 100644 index 0000000..678fff1 --- /dev/null +++ b/list.h @@ -0,0 +1,28 @@ +#ifndef _DROPBEAR_LIST_H +#define _DROPBEAR_LIST_H + +struct _m_list; + +struct _m_list_elem { + void *item; + struct _m_list_elem *next; + struct _m_list_elem *prev; + struct _m_list *list; +}; + +typedef struct _m_list_elem m_list_elem; + +struct _m_list { + m_list_elem *first; + m_list_elem *last; +}; + +typedef struct _m_list m_list; + +m_list * list_new(); +void list_append(m_list *list, void *item); +/* returns the item for the element removed */ +void * list_remove(m_list_elem *elem); + + +#endif /* _DROPBEAR_LIST_H */ \ No newline at end of file diff --git a/runopts.h b/runopts.h index bc35435..e672a4e 100644 --- a/runopts.h +++ b/runopts.h @@ -112,13 +112,13 @@ typedef struct cli_runopts { int backgrounded; int is_subsystem; #ifdef ENABLE_CLI_PUBKEY_AUTH - struct SignKeyList *privkeys; /* Keys to use for public-key auth */ + m_list *privkeys; /* Keys to use for public-key auth */ #endif #ifdef ENABLE_CLI_REMOTETCPFWD - struct TCPFwdList * remotefwds; + m_list * remotefwds; #endif #ifdef ENABLE_CLI_LOCALTCPFWD - struct TCPFwdList * localfwds; + m_list * localfwds; #endif #ifdef ENABLE_CLI_AGENTFWD int agent_fwd; diff --git a/session.h b/session.h index 5a4569e..d02666d 100644 --- a/session.h +++ b/session.h @@ -261,7 +261,7 @@ struct clientsession { info request from the server for interactive auth.*/ #endif - struct SignKeyList *lastprivkey; + sign_key *lastprivkey; int retval; /* What the command exit status was - we emulate it */ #if 0 diff --git a/signkey.c b/signkey.c index c1ef5e2..9bc4845 100644 --- a/signkey.c +++ b/signkey.c @@ -40,8 +40,10 @@ sign_key * new_sign_key() { #ifdef DROPBEAR_RSA ret->rsakey = NULL; #endif + ret->filename = NULL; + ret->type = DROPBEAR_SIGNKEY_NONE; + ret->source = SIGNKEY_SOURCE_INVALID; return ret; - } /* Returns "ssh-dss" or "ssh-rsa" corresponding to the type. Exits fatally @@ -81,6 +83,9 @@ int signkey_type_from_name(const char* name, int namelen) { } #endif + TRACE(("signkey_type_from_name unexpected key type.")) + printhex("Key type", name, namelen); + return DROPBEAR_SIGNKEY_NONE; } @@ -101,8 +106,11 @@ int buf_get_pub_key(buffer *buf, sign_key *key, int *type) { m_free(ident); if (*type != DROPBEAR_SIGNKEY_ANY && *type != keytype) { + TRACE(("buf_get_pub_key bad type - got %d, expected %d", keytype, type)) return DROPBEAR_FAILURE; } + + TRACE(("buf_get_pub_key keytype is %d")) *type = keytype; @@ -255,6 +263,8 @@ void sign_key_free(sign_key *key) { key->rsakey = NULL; #endif + m_free(key->filename); + m_free(key); TRACE(("leave sign_key_free")) } @@ -358,7 +368,6 @@ void buf_put_sign(buffer* buf, sign_key *key, int type, const unsigned char *data, unsigned int len) { buffer *sigblob; - sigblob = buf_new(MAX_PUBKEY_SIZE); #ifdef DROPBEAR_DSS @@ -374,7 +383,6 @@ void buf_put_sign(buffer* buf, sign_key *key, int type, if (sigblob->len == 0) { dropbear_exit("non-matching signing type"); } - buf_setpos(sigblob, 0); buf_putstring(buf, buf_getptr(sigblob, sigblob->len), sigblob->len); diff --git a/signkey.h b/signkey.h index 4038b2d..9bcbfc9 100644 --- a/signkey.h +++ b/signkey.h @@ -29,8 +29,22 @@ #include "dss.h" #include "rsa.h" + +/* Sources for signing keys */ +typedef enum { + SIGNKEY_SOURCE_RAW_FILE, + SIGNKEY_SOURCE_AGENT, + SIGNKEY_SOURCE_INVALID, +} signkey_source; + struct SIGN_key { + int type; /* The type of key (dss or rsa) */ + signkey_source source; + char *filename; + /* the buffer? for encrypted keys, so we can later get + * the private key portion */ + #ifdef DROPBEAR_DSS dss_key * dsskey; #endif diff --git a/tcpfwd.h b/tcpfwd.h index 251612e..34da314 100644 --- a/tcpfwd.h +++ b/tcpfwd.h @@ -25,6 +25,7 @@ #define _TCPFWD_H #include "channel.h" +#include "list.h" struct TCPListener { @@ -43,16 +44,13 @@ struct TCPListener { enum {direct, forwarded} tcp_type; }; -/* A link in a list of forwards */ -struct TCPFwdList { - +/* A forwarding entry */ +struct TCPFwdEntry { const unsigned char* connectaddr; unsigned int connectport; unsigned int listenport; unsigned int have_reply; /* is set to 1 after a reply has been received when setting up the forwarding */ - struct TCPFwdList * next; - }; /* Server */ @@ -70,5 +68,4 @@ void cli_recv_msg_request_failure(); /* Common */ int listen_tcpfwd(struct TCPListener* tcpinfo); - #endif From 8a19a049b25f72062cc38a0940d9dc5f928cdb0e Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Mon, 6 Jul 2009 14:02:45 +0000 Subject: [PATCH 23/45] - Client auth using an agent's key works. Still need to implement client agent forwarding. --HG-- branch : agent-client extra : convert_revision : 276cf5e82276b6c879d246ba64739ec6868f5150 --- auth.h | 1 + cli-agentfwd.c | 79 ++++++++++++++++++++++++++++++++++++++++-------- cli-auth.c | 4 +++ cli-authpubkey.c | 16 +++++++++- runopts.h | 2 ++ 5 files changed, 89 insertions(+), 13 deletions(-) diff --git a/auth.h b/auth.h index d22f812..115000a 100644 --- a/auth.h +++ b/auth.h @@ -74,6 +74,7 @@ void cli_auth_password(); int cli_auth_pubkey(); void cli_auth_interactive(); char* getpass_or_cancel(char* prompt); +void cli_auth_pubkey_cleanup(); #define MAX_USERNAME_LEN 25 /* arbitrary for the moment */ diff --git a/cli-agentfwd.c b/cli-agentfwd.c index 0a5661d..ed2c59c 100644 --- a/cli-agentfwd.c +++ b/cli-agentfwd.c @@ -102,17 +102,26 @@ static int new_agent_chan(struct Channel * channel) { data Any data, depending on packet type. Encoding as in the ssh packet protocol. */ -static buffer * agent_request(int fd, unsigned char type) { +static buffer * agent_request(unsigned char type, buffer *data) { buffer * payload = NULL; buffer * inbuf = NULL; size_t readlen = 0; ssize_t ret; + const int fd = cli_opts.agent_fd; + unsigned int data_len = 0; + if (data) + { + data_len = data->len; + } - payload = buf_new(4 + 1); + payload = buf_new(4 + 1 + data_len); - buf_putint(payload, 1); + buf_putint(payload, 1 + data_len); buf_putbyte(payload, type); + if (data) { + buf_putbytes(payload, data->data, data->len); + } buf_setpos(payload, 0); ret = atomicio(write, fd, buf_getptr(payload, payload->len), payload->len); @@ -160,7 +169,7 @@ out: return inbuf; } -static void agent_get_key_list(int fd, m_list * ret_list) +static void agent_get_key_list(m_list * ret_list) { buffer * inbuf = NULL; unsigned int num = 0; @@ -168,9 +177,9 @@ static void agent_get_key_list(int fd, m_list * ret_list) unsigned int i; int ret; - inbuf = agent_request(fd, SSH2_AGENTC_REQUEST_IDENTITIES); + inbuf = agent_request(SSH2_AGENTC_REQUEST_IDENTITIES, NULL); if (!inbuf) { - TRACE(("agent_request returned no identities")) + TRACE(("agent_request failed returning identities")) goto out; } @@ -191,7 +200,6 @@ static void agent_get_key_list(int fd, m_list * ret_list) sign_key * pubkey = NULL; int key_type = DROPBEAR_SIGNKEY_ANY; buffer * key_buf; - struct SignKeyList *nextkey = NULL; /* each public key is encoded as a string */ key_buf = buf_getstringbuf(inbuf); @@ -222,19 +230,66 @@ out: be updated. */ void load_agent_keys(m_list *ret_list) { - int fd; - fd = connect_agent(); - if (fd < 0) { + /* agent_fd will be closed after successful auth */ + cli_opts.agent_fd = connect_agent(); + if (cli_opts.agent_fd < 0) { dropbear_log(LOG_INFO, "Failed to connect to agent"); return; } - agent_get_key_list(fd, ret_list); - close(fd); + agent_get_key_list(ret_list); } void agent_buf_sign(buffer *sigblob, sign_key *key, const unsigned char *data, unsigned int len) { + buffer *request_data = buf_new(MAX_PUBKEY_SIZE + len + 12); + buffer *response; + unsigned int keylen, siglen; + int packet_type; + + /* Request format + byte SSH2_AGENTC_SIGN_REQUEST + string key_blob + string data + uint32 flags + */ + /* We write the key, then figure how long it was and write that */ + //buf_putint(request_data, 0); + buf_put_pub_key(request_data, key, key->type); + keylen = request_data->len - 4; + //buf_setpos(request_data, 0); + //buf_putint(request_data, keylen); + + //buf_setpos(request_data, request_data->len); + buf_putstring(request_data, data, len); + buf_putint(request_data, 0); + + response = agent_request(SSH2_AGENTC_SIGN_REQUEST, request_data); + buf_free(request_data); + + if (!response) { + goto fail; + } + + packet_type = buf_getbyte(response); + if (packet_type != SSH2_AGENT_SIGN_RESPONSE) { + goto fail; + } + + /* Response format + byte SSH2_AGENT_SIGN_RESPONSE + string signature_blob + */ + siglen = buf_getint(response); + buf_putbytes(sigblob, buf_getptr(response, siglen), siglen); + buf_free(response); + + return; +fail: + /* XXX don't fail badly here. instead propagate a failure code back up to + the cli auth pubkey code, and just remove this key from the list of + ones to try. */ + dropbear_exit("Agent failed signing key"); } #endif diff --git a/cli-auth.c b/cli-auth.c index 8b6a440..cfcf134 100644 --- a/cli-auth.c +++ b/cli-auth.c @@ -234,6 +234,10 @@ void recv_msg_userauth_success() { ses.authstate.authdone = 1; cli_ses.state = USERAUTH_SUCCESS_RCVD; cli_ses.lastauthtype = AUTH_TYPE_NONE; + +#ifdef ENABLE_CLI_PUBKEY_AUTH + cli_auth_pubkey_cleanup(); +#endif } void cli_auth_try() { diff --git a/cli-authpubkey.c b/cli-authpubkey.c index 0ad0031..a990631 100644 --- a/cli-authpubkey.c +++ b/cli-authpubkey.c @@ -174,7 +174,7 @@ static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign) { sigbuf = buf_new(4 + SHA1_HASH_SIZE + ses.writepayload->len); buf_putstring(sigbuf, ses.session_id, SHA1_HASH_SIZE); buf_putbytes(sigbuf, ses.writepayload->data, ses.writepayload->len); - buf_put_sign(ses.writepayload, key, type, sigbuf->data, sigbuf->len); + cli_buf_put_sign(ses.writepayload, key, type, sigbuf->data, sigbuf->len); buf_free(sigbuf); /* Nothing confidential in the buffer */ } @@ -202,8 +202,22 @@ int cli_auth_pubkey() { TRACE(("leave cli_auth_pubkey-success")) return 1; } else { + /* no more keys left */ TRACE(("leave cli_auth_pubkey-failure")) return 0; } } + +void cli_auth_pubkey_cleanup() { + +#ifdef ENABLE_CLI_AGENTFWD + m_close(cli_opts.agent_fd); + cli_opts.agent_fd = -1; +#endif + + while (cli_opts.privkeys->first) { + sign_key * key = list_remove(cli_opts.privkeys->first); + sign_key_free(key); + } +} #endif /* Pubkey auth */ diff --git a/runopts.h b/runopts.h index e672a4e..22376c1 100644 --- a/runopts.h +++ b/runopts.h @@ -124,6 +124,8 @@ typedef struct cli_runopts { int agent_fwd; int agent_keys_loaded; /* whether pubkeys has been populated with a list of keys held by the agent */ + int agent_fd; /* The agent fd is only set during authentication. Forwarded + agent sessions have their own file descriptors */ #endif #ifdef ENABLE_CLI_NETCAT From a996e61a2e6b8ba4fb101a50c950a661cb6d770c Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 9 Jul 2009 16:01:30 +0000 Subject: [PATCH 24/45] - For uclinux, only cleanup on exit for the main process. This avoids trashing the state when a failing child exits. --HG-- extra : convert_revision : 5d029ce4602908c3becf0035cf2b7e62816959bc --- session.h | 4 ++++ svr-session.c | 21 +++++++++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/session.h b/session.h index 5a4569e..e52b074 100644 --- a/session.h +++ b/session.h @@ -213,6 +213,10 @@ struct serversession { /* The numeric address they connected from, used for logging */ char * addrstring; +#ifdef __uClinux__ + pid_t server_pid; +#endif + }; typedef enum { diff --git a/svr-session.c b/svr-session.c index eaccfe5..89b16cb 100644 --- a/svr-session.c +++ b/svr-session.c @@ -85,6 +85,10 @@ void svr_session(int sock, int childpipe, /* Initialise server specific parts of the session */ svr_ses.childpipe = childpipe; svr_ses.addrstring = addrstring; +#ifdef __uClinux__ + svr_ses.server_pid = getpid(); +#endif + svr_ses.addrstring = addrstring; svr_authinitialise(); chaninitialise(svr_chantypes); svr_chansessinitialise(); @@ -144,11 +148,20 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) { _dropbear_log(LOG_INFO, fmtbuf, param); - /* free potential public key options */ - svr_pubkey_options_cleanup(); +#ifdef __uClinux__ + /* only the main server process should cleanup - we don't want + * forked children doing that */ + if (svr_ses.server_pid == getpid()) +#else + if (1) +#endif + { + /* free potential public key options */ + svr_pubkey_options_cleanup(); - /* must be after we've done with username etc */ - common_session_cleanup(); + /* must be after we've done with username etc */ + common_session_cleanup(); + } exit(exitcode); From 8181d41bb508aeb6c335fe213d1d5e5412d583b4 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 24 Jul 2009 13:49:07 +0000 Subject: [PATCH 25/45] Disable Blowfish by default, it has inefficient key memory use --HG-- extra : convert_revision : a37b8ae5fb524be221dbdfd71b4f35eb6a48565c --- options.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/options.h b/options.h index c13a12e..2d6f7b6 100644 --- a/options.h +++ b/options.h @@ -86,7 +86,8 @@ much traffic. */ #define DROPBEAR_AES128 #define DROPBEAR_3DES #define DROPBEAR_AES256 -#define DROPBEAR_BLOWFISH +/* Compiling in Blowfish will add ~6kB to runtime heap memory usage */ +/*#define DROPBEAR_BLOWFISH*/ #define DROPBEAR_TWOFISH256 #define DROPBEAR_TWOFISH128 From 3608775306a0959d6cdbefa428c1e6d3af5d8af2 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 26 Jul 2009 15:39:47 +0000 Subject: [PATCH 26/45] - Add option to change zlib windowBits/memLevel --HG-- extra : convert_revision : 5fc51ba0b8f165426c78f8d32162e5ccb51e524f --- common-kex.c | 4 +++- options.h | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/common-kex.c b/common-kex.c index 052324b..cb5cd96 100644 --- a/common-kex.c +++ b/common-kex.c @@ -371,7 +371,9 @@ static void gen_new_zstreams() { ses.newkeys->trans.zstream->zalloc = Z_NULL; ses.newkeys->trans.zstream->zfree = Z_NULL; - if (deflateInit(ses.newkeys->trans.zstream, Z_DEFAULT_COMPRESSION) + if (deflateInit2(ses.newkeys->trans.zstream, Z_DEFAULT_COMPRESSION, + Z_DEFLATED, DROPBEAR_ZLIB_WINDOW_BITS, + DROPBEAR_ZLIB_MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK) { dropbear_exit("zlib error"); } diff --git a/options.h b/options.h index 2d6f7b6..3cf430b 100644 --- a/options.h +++ b/options.h @@ -130,6 +130,21 @@ much traffic. */ * if the random number source isn't good. In general this isn't required */ /* #define DSS_PROTOK */ +/* Control the memory/performance/compression tradeoff for zlib. + * Set windowBits=8, memLevel=1 for least memory usage, see your system's + * zlib.h for full details. + * Default settings (windowBits=15, memLevel=8) will use + * 256kB for compression + 32kB for decompression. + * windowBits=8, memLevel=1 will use 10kB compression + 32kB decompression. + * Note that windowBits is only set for deflate() - inflate() always uses the + * default of 15 so as to interoperate with other clients. */ +#ifndef DROPBEAR_ZLIB_WINDOW_BITS +#define DROPBEAR_ZLIB_WINDOW_BITS 15 +#endif +#ifndef DROPBEAR_ZLIB_MEM_LEVEL +#define DROPBEAR_ZLIB_MEM_LEVEL 8 +#endif + /* Whether to do reverse DNS lookups. */ #define DO_HOST_LOOKUP @@ -248,13 +263,19 @@ much traffic. */ significant difference to network performance. 24kB was empirically chosen for a 100mbit ethernet network. The value can be altered at runtime with the -W argument. */ +#ifndef DEFAULT_RECV_WINDOW #define DEFAULT_RECV_WINDOW 24576 +#endif /* Maximum size of a received SSH data packet - this _MUST_ be >= 32768 in order to interoperate with other implementations */ +#ifndef RECV_MAX_PAYLOAD_LEN #define RECV_MAX_PAYLOAD_LEN 32768 +#endif /* Maximum size of a transmitted data packet - this can be any value, though increasing it may not make a significant difference. */ +#ifndef TRANS_MAX_PAYLOAD_LEN #define TRANS_MAX_PAYLOAD_LEN 16384 +#endif /* Ensure that data is transmitted every KEEPALIVE seconds. This can be overridden at runtime with -K. 0 disables keepalives */ From bcd541d65f93365ff665749419844620ec2c6eeb Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 26 Jul 2009 16:11:27 +0000 Subject: [PATCH 27/45] - Payload length doesn't include macsize. --HG-- extra : convert_revision : 98ac17a573ab350cbd6e358b3943237d2ad5c9cf --- debug.h | 2 +- packet.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/debug.h b/debug.h index b8c2a57..a9cc0bd 100644 --- a/debug.h +++ b/debug.h @@ -39,7 +39,7 @@ * Caution: Don't use this in an unfriendly environment (ie unfirewalled), * since the printing may not sanitise strings etc. This will add a reasonable * amount to your executable size. */ -/*#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/packet.c b/packet.c index 37ffdd2..9621bbd 100644 --- a/packet.c +++ b/packet.c @@ -261,7 +261,7 @@ void decrypt_packet() { /* payload length */ /* - 4 - 1 is for LEN and PADLEN values */ - len = ses.readbuf->len - padlen - 4 - 1; + len = ses.readbuf->len - padlen - 4 - 1 - macsize; if ((len > RECV_MAX_PAYLOAD_LEN) || (len < 1)) { dropbear_exit("bad packet size"); } From 0dcecfa52612df18dd051f72d31d18e76d7848b7 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 26 Jul 2009 16:14:50 +0000 Subject: [PATCH 28/45] Turn off DEBUG_TRACE accidentally committed --HG-- extra : convert_revision : bbe4e11695a7b22bd89a722600eb4a4020b6fdf3 --- debug.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug.h b/debug.h index a9cc0bd..b8c2a57 100644 --- a/debug.h +++ b/debug.h @@ -39,7 +39,7 @@ * Caution: Don't use this in an unfriendly environment (ie unfirewalled), * since the printing may not sanitise strings etc. This will add a reasonable * amount to your executable size. */ -#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 From 103a829eaccb2b276ede0c9acdc8fa1cb71e5988 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 28 Jul 2009 16:16:14 +0000 Subject: [PATCH 29/45] Use /usr/bin/X11/xauth default path instead --HG-- extra : convert_revision : ff0abce7b29e61630e3b09e5fc5820ae6e192808 --- options.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options.h b/options.h index 3cf430b..73156a4 100644 --- a/options.h +++ b/options.h @@ -237,7 +237,7 @@ much traffic. */ /* The command to invoke for xauth when using X11 forwarding. * "-q" for quiet */ #ifndef XAUTH_COMMAND -#define XAUTH_COMMAND "/usr/X11R6/bin/xauth -q" +#define XAUTH_COMMAND "/usr/bin/X11/xauth -q" #endif /* if you want to enable running an sftp server (such as the one included with From bb8234c2f12998b6cd9590f7dddd08d1871a5d1f Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 30 Jul 2009 15:14:33 +0000 Subject: [PATCH 30/45] Agent forwarding works --HG-- branch : agent-client extra : convert_revision : eb0dae4b62e243ba37a897beb7ba81a4f637d8b3 --- agentfwd.h | 10 ++++++---- chansession.h | 1 + cli-agentfwd.c | 14 ++++++++++++-- cli-authpubkey.c | 5 ++--- cli-chansession.c | 15 ++++++++++----- cli-session.c | 2 +- dbclient.1 | 5 +++++ debug.h | 2 +- signkey.c | 1 - svr-agentfwd.c | 12 ++++++++---- svr-chansession.c | 6 +++--- 11 files changed, 49 insertions(+), 24 deletions(-) diff --git a/agentfwd.h b/agentfwd.h index e1794a7..e982460 100644 --- a/agentfwd.h +++ b/agentfwd.h @@ -35,14 +35,16 @@ * 10000 is arbitrary */ #define MAX_AGENT_REPLY 10000 -int agentreq(struct ChanSess * chansess); -void agentcleanup(struct ChanSess * chansess); -void agentset(struct ChanSess *chansess); +int svr_agentreq(struct ChanSess * chansess); +void svr_agentcleanup(struct ChanSess * chansess); +void svr_agentset(struct ChanSess *chansess); /* client functions */ -void load_agent_keys(m_list * ret_list); +void cli_load_agent_keys(m_list * ret_list); void agent_buf_sign(buffer *sigblob, sign_key *key, const unsigned char *data, unsigned int len); +void cli_setup_agent(struct Channel *channel); + #ifdef __hpux #define seteuid(a) setresuid(-1, (a), -1) diff --git a/chansession.h b/chansession.h index 8d8e173..b9d1995 100644 --- a/chansession.h +++ b/chansession.h @@ -81,6 +81,7 @@ void cli_chansess_winchange(); #ifdef ENABLE_CLI_NETCAT void cli_send_netcat_request(); #endif +void cli_start_send_channel_request(struct Channel *channel, unsigned char *type); void svr_chansessinitialise(); extern const struct ChanType svrchansess; diff --git a/cli-agentfwd.c b/cli-agentfwd.c index ed2c59c..c7df788 100644 --- a/cli-agentfwd.c +++ b/cli-agentfwd.c @@ -226,10 +226,20 @@ out: } } +void cli_setup_agent(struct Channel *channel) { + if (!getenv("SSH_AUTH_SOCK")) { + return; + } + + cli_start_send_channel_request(channel, "auth-agent-req@openssh.com"); + /* Don't want replies */ + buf_putbyte(ses.writepayload, 0); + encrypt_packet(); +} + /* Returned keys are prepended to ret_list, which will be updated. */ -void load_agent_keys(m_list *ret_list) -{ +void cli_load_agent_keys(m_list *ret_list) { /* agent_fd will be closed after successful auth */ cli_opts.agent_fd = connect_agent(); if (cli_opts.agent_fd < 0) { diff --git a/cli-authpubkey.c b/cli-authpubkey.c index a990631..a57fd6b 100644 --- a/cli-authpubkey.c +++ b/cli-authpubkey.c @@ -187,10 +187,9 @@ int cli_auth_pubkey() { TRACE(("enter cli_auth_pubkey")) - if (cli_opts.agent_fwd && - !cli_opts.agent_keys_loaded) { + if (!cli_opts.agent_keys_loaded) { /* get the list of available keys from the agent */ - load_agent_keys(cli_opts.privkeys); + cli_load_agent_keys(cli_opts.privkeys); cli_opts.agent_keys_loaded = 1; } diff --git a/cli-chansession.c b/cli-chansession.c index 9dc0224..2d97fea 100644 --- a/cli-chansession.c +++ b/cli-chansession.c @@ -33,13 +33,12 @@ #include "runopts.h" #include "termcodes.h" #include "chansession.h" +#include "agentfwd.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); - static void send_chansess_pty_req(struct Channel *channel); static void send_chansess_shell_req(struct Channel *channel); @@ -92,7 +91,7 @@ static void cli_closechansess(struct Channel *UNUSED(channel)) { } -static void start_channel_request(struct Channel *channel, +void cli_start_send_channel_request(struct Channel *channel, unsigned char *type) { CHECKCLEARTOWRITE(); @@ -287,7 +286,7 @@ static void send_chansess_pty_req(struct Channel *channel) { TRACE(("enter send_chansess_pty_req")) - start_channel_request(channel, "pty-req"); + cli_start_send_channel_request(channel, "pty-req"); /* Don't want replies */ buf_putbyte(ses.writepayload, 0); @@ -330,7 +329,7 @@ static void send_chansess_shell_req(struct Channel *channel) { reqtype = "shell"; } - start_channel_request(channel, reqtype); + cli_start_send_channel_request(channel, reqtype); /* XXX TODO */ buf_putbyte(ses.writepayload, 0); /* Don't want replies */ @@ -361,6 +360,12 @@ static int cli_initchansess(struct Channel *channel) { cli_init_stdpipe_sess(channel); +#ifdef ENABLE_CLI_AGENTFWD + if (cli_opts.agent_fwd) { + cli_setup_agent(channel); + } +#endif + if (cli_opts.wantpty) { send_chansess_pty_req(channel); } diff --git a/cli-session.c b/cli-session.c index f9b372b..ed58a11 100644 --- a/cli-session.c +++ b/cli-session.c @@ -235,7 +235,7 @@ static void cli_sessionloop() { cli_send_netcat_request(); } else #endif - if (!cli_opts.no_cmd) { + if (!cli_opts.no_cmd) { cli_send_chansess_request(); } TRACE(("leave cli_sessionloop: running")) diff --git a/dbclient.1 b/dbclient.1 index 96e6c5c..2065502 100644 --- a/dbclient.1 +++ b/dbclient.1 @@ -82,6 +82,11 @@ by the ssh server. Always accept hostkeys if they are unknown. If a hostkey mismatch occurs the connection will abort as normal. .TP +.B \-A +Forward agent connections to the remote host. dbclient will use any +OpenSSH-style agent program if available ($SSH_AUTH_SOCK will be set) for +public key authentication. Forwarding is only enabled if -A is specified. +.TP .B \-W \fIwindowsize Specify the per-channel receive window buffer size. Increasing this may improve network performance at the expense of memory use. Use -h to see the diff --git a/debug.h b/debug.h index a9cc0bd..b8c2a57 100644 --- a/debug.h +++ b/debug.h @@ -39,7 +39,7 @@ * Caution: Don't use this in an unfriendly environment (ie unfirewalled), * since the printing may not sanitise strings etc. This will add a reasonable * amount to your executable size. */ -#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/signkey.c b/signkey.c index 9bc4845..612fd58 100644 --- a/signkey.c +++ b/signkey.c @@ -84,7 +84,6 @@ int signkey_type_from_name(const char* name, int namelen) { #endif TRACE(("signkey_type_from_name unexpected key type.")) - printhex("Key type", name, namelen); return DROPBEAR_SIGNKEY_NONE; } diff --git a/svr-agentfwd.c b/svr-agentfwd.c index b86c447..940c4b7 100644 --- a/svr-agentfwd.c +++ b/svr-agentfwd.c @@ -49,10 +49,12 @@ static void agentaccept(struct Listener * listener, int sock); /* Handles client requests to start agent forwarding, sets up listening socket. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ -int agentreq(struct ChanSess * chansess) { +int svr_agentreq(struct ChanSess * chansess) { int fd; + TRACE(("enter svr_agentreq")) + if (!svr_pubkey_allows_agentfwd()) { return DROPBEAR_FAILURE; } @@ -89,10 +91,12 @@ int agentreq(struct ChanSess * chansess) { } return DROPBEAR_SUCCESS; + TRACE(("success")) fail: + TRACE(("fail")) /* cleanup */ - agentcleanup(chansess); + svr_agentcleanup(chansess); return DROPBEAR_FAILURE; } @@ -118,7 +122,7 @@ static void agentaccept(struct Listener *UNUSED(listener), int sock) { /* set up the environment variable pointing to the socket. This is called * just before command/shell execution, after dropping priveleges */ -void agentset(struct ChanSess * chansess) { +void svr_agentset(struct ChanSess * chansess) { char *path = NULL; int len; @@ -137,7 +141,7 @@ void agentset(struct ChanSess * chansess) { } /* close the socket, remove the socket-file */ -void agentcleanup(struct ChanSess * chansess) { +void svr_agentcleanup(struct ChanSess * chansess) { char *path = NULL; uid_t uid; diff --git a/svr-chansession.c b/svr-chansession.c index 23dad8c..574e46f 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -287,7 +287,7 @@ static void closechansess(struct Channel *channel) { #endif #ifndef DISABLE_AGENTFWD - agentcleanup(chansess); + svr_agentcleanup(chansess); #endif /* clear child pid entries */ @@ -346,7 +346,7 @@ static void chansessionrequest(struct Channel *channel) { #endif #ifndef DISABLE_AGENTFWD } else if (strcmp(type, "auth-agent-req@openssh.com") == 0) { - ret = agentreq(chansess); + ret = svr_agentreq(chansess); #endif } else if (strcmp(type, "signal") == 0) { ret = sessionsignal(chansess); @@ -894,7 +894,7 @@ static void execchild(void *user_data) { #endif #ifndef DISABLE_AGENTFWD /* set up agent env variable */ - agentset(chansess); + svr_agentset(chansess); #endif usershell = m_strdup(get_user_shell()); From f15feb2ac6123eebe27b859efd83f7df94dc8146 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 31 Jul 2009 15:51:33 +0000 Subject: [PATCH 31/45] - Handle failure to connect to forwarding socket --HG-- extra : convert_revision : 0cc1ae25ba35931d6ddd9e989e875ef975616be6 --- cli-agentfwd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cli-agentfwd.c b/cli-agentfwd.c index c7df788..eed4a6a 100644 --- a/cli-agentfwd.c +++ b/cli-agentfwd.c @@ -79,6 +79,10 @@ static int new_agent_chan(struct Channel * channel) { return SSH_OPEN_ADMINISTRATIVELY_PROHIBITED; fd = connect_agent(); + if (cli_opts.agent_fd < 0) { + dropbear_log(LOG_INFO, "Failed to connect to agent"); + return SSH_OPEN_CONNECT_FAILED; + } setnonblocking(fd); From c7e3eb9b3fdbfa60bd21cedc7873b89cf8cad0a6 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 13 Aug 2009 14:57:27 +0000 Subject: [PATCH 32/45] - Don't print warning if SSH_AUTH_SOCK is unset --HG-- extra : convert_revision : 0d7a9127af37d6e74efc5ec031a7001ce63d334d --- cli-agentfwd.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cli-agentfwd.c b/cli-agentfwd.c index eed4a6a..a212c3f 100644 --- a/cli-agentfwd.c +++ b/cli-agentfwd.c @@ -66,6 +66,10 @@ static int connect_agent() { fd = connect_unix(agent_sock); + if (fd < 0) { + dropbear_log(LOG_INFO, "Failed to connect to agent"); + } + return fd; } @@ -80,7 +84,6 @@ static int new_agent_chan(struct Channel * channel) { fd = connect_agent(); if (cli_opts.agent_fd < 0) { - dropbear_log(LOG_INFO, "Failed to connect to agent"); return SSH_OPEN_CONNECT_FAILED; } @@ -247,7 +250,6 @@ void cli_load_agent_keys(m_list *ret_list) { /* agent_fd will be closed after successful auth */ cli_opts.agent_fd = connect_agent(); if (cli_opts.agent_fd < 0) { - dropbear_log(LOG_INFO, "Failed to connect to agent"); return; } From d773103730da6c62e40a2d9962ce3f08d1d363e7 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 25 Aug 2009 05:24:18 +0000 Subject: [PATCH 33/45] - Move netcat struct to where it stays in scope. --HG-- extra : convert_revision : e35cd321b6d4fab6ad854827249f610da3bb6878 --- cli-chansession.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cli-chansession.c b/cli-chansession.c index 2d97fea..49b4e06 100644 --- a/cli-chansession.c +++ b/cli-chansession.c @@ -381,20 +381,20 @@ static int cli_initchansess(struct Channel *channel) { #ifdef ENABLE_CLI_NETCAT +const struct ChanType cli_chan_netcat = { + 0, /* sepfds */ + "direct-tcpip", + cli_init_stdpipe_sess, /* inithandler */ + NULL, + NULL, + cli_closechansess +}; + void cli_send_netcat_request() { const unsigned char* source_host = "127.0.0.1"; const int source_port = 22; - const struct ChanType cli_chan_netcat = { - 0, /* sepfds */ - "direct-tcpip", - cli_init_stdpipe_sess, /* inithandler */ - NULL, - NULL, - cli_closechansess - }; - cli_opts.wantpty = 0; if (send_msg_channel_open_init(STDIN_FILENO, &cli_chan_netcat) From 4b1f9e50f27d8bcd36479cb837fdb9711fc41951 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Wed, 26 Aug 2009 14:09:22 +0000 Subject: [PATCH 34/45] - set $SSH_ORIGINAL_COMMAND if a command is forced, and log it if LOG_COMMANDS is set --HG-- extra : convert_revision : d9e8aa0ecbe7607285fa4f96f0d6f9b1523719d8 --- auth.h | 4 +--- options.h | 3 ++- svr-authpubkeyoptions.c | 15 ++++++++++++--- svr-chansession.c | 18 +++++++++++++++--- 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/auth.h b/auth.h index 115000a..7ebf9ae 100644 --- a/auth.h +++ b/auth.h @@ -99,7 +99,6 @@ void cli_auth_pubkey_cleanup(); * relatively little extraneous bits when used for the client rather than the * server */ struct AuthState { - char *username; /* This is the username the client presents to check. It is updated each run through, used for auth checking */ unsigned char authtypes; /* Flags indicating which auth types are still @@ -122,7 +121,6 @@ struct AuthState { #ifdef ENABLE_SVR_PUBKEY_OPTIONS struct PubKeyOptions* pubkey_options; #endif - }; #ifdef ENABLE_SVR_PUBKEY_OPTIONS @@ -135,7 +133,7 @@ struct PubKeyOptions { int no_pty_flag; /* "command=" option. */ unsigned char * forced_command; - + unsigned char * original_command; }; #endif diff --git a/options.h b/options.h index 83b0b24..f151cdb 100644 --- a/options.h +++ b/options.h @@ -172,7 +172,8 @@ much traffic. */ /*#define ENABLE_SVR_PAM_AUTH*/ #define ENABLE_SVR_PUBKEY_AUTH -/* Wether to ake public key options in authorized_keys file into account */ +/* Whether to take public key options in + * authorized_keys file into account */ #ifdef ENABLE_SVR_PUBKEY_AUTH #define ENABLE_SVR_PUBKEY_OPTIONS #endif diff --git a/svr-authpubkeyoptions.c b/svr-authpubkeyoptions.c index 13a179d..8fa7b3d 100644 --- a/svr-authpubkeyoptions.c +++ b/svr-authpubkeyoptions.c @@ -88,10 +88,20 @@ int svr_pubkey_allows_pty() { return 1; } -/* Set chansession command to the one forced by 'command' public key option */ +/* Set chansession command to the one forced + * by any 'command' public key option. */ void svr_pubkey_set_forced_command(struct ChanSess *chansess) { - if (ses.authstate.pubkey_options) + if (ses.authstate.pubkey_options) { + ses.authstate.pubkey_options->original_command = chansess->cmd; + if (!chansess->cmd) + { + ses.authstate.pubkey_options->original_command = m_strdup(""); + } chansess->cmd = ses.authstate.pubkey_options->forced_command; +#ifdef LOG_COMMANDS + dropbear_log(LOG_INFO, "command forced to '%s'", ses.authstate.pubkey_options->original_command); +#endif + } } /* Free potential public key options */ @@ -124,7 +134,6 @@ int svr_add_pubkey_options(buffer *options_buf, int line_num, const char* filena TRACE(("enter addpubkeyoptions")) ses.authstate.pubkey_options = (struct PubKeyOptions*)m_malloc(sizeof( struct PubKeyOptions )); - memset(ses.authstate.pubkey_options, '\0', sizeof(*ses.authstate.pubkey_options)); buf_setpos(options_buf, 0); while (options_buf->pos < options_buf->len) { diff --git a/svr-chansession.c b/svr-chansession.c index 574e46f..cb5acda 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -578,6 +578,7 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess, int iscmd, int issubsys) { unsigned int cmdlen; + int is_forced; int ret; TRACE(("enter sessioncommand")) @@ -589,9 +590,6 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess, return DROPBEAR_FAILURE; } - /* take public key option 'command' into account */ - svr_pubkey_set_forced_command(chansess); - if (iscmd) { /* "exec" */ if (chansess->cmd == NULL) { @@ -616,6 +614,9 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess, } } } + + /* take public key option 'command' into account */ + svr_pubkey_set_forced_command(chansess); #ifdef LOG_COMMANDS if (chansess->cmd) { @@ -883,6 +884,17 @@ static void execchild(void *user_data) { addnewvar("TERM", chansess->term); } + printf("adding option %p %s\n", ses.authstate.pubkey_options, + ses.authstate.pubkey_options->original_command); + +#ifdef ENABLE_SVR_PUBKEY_OPTIONS + if (ses.authstate.pubkey_options && + ses.authstate.pubkey_options->original_command) { + addnewvar("SSH_ORIGINAL_COMMAND", + ses.authstate.pubkey_options->original_command); + } +#endif + /* change directory */ if (chdir(ses.authstate.pw_dir) < 0) { dropbear_exit("error changing directory"); From ccd02552ddf0fd0b7bfcc29d8c5eb38dd459c465 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Mon, 31 Aug 2009 15:25:39 +0000 Subject: [PATCH 35/45] - set $SSH_TTY environment variable - remove extraneous (crash causing) printf() --HG-- extra : convert_revision : cf4b256bf6785be384eca32f7b229b89e58539eb --- svr-chansession.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/svr-chansession.c b/svr-chansession.c index cb5acda..2664382 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -742,8 +742,6 @@ static int ptycommand(struct Channel *channel, struct ChanSess *chansess) { login_login(li); login_free_entry(li); - m_free(chansess->tty); - #ifdef DO_MOTD if (svr_opts.domotd) { /* don't show the motd if ~/.hushlogin exists */ @@ -884,9 +882,10 @@ static void execchild(void *user_data) { addnewvar("TERM", chansess->term); } - printf("adding option %p %s\n", ses.authstate.pubkey_options, - ses.authstate.pubkey_options->original_command); - + if (chansess->tty) { + addnewvar("SSH_TTY", chansess->tty); + } + #ifdef ENABLE_SVR_PUBKEY_OPTIONS if (ses.authstate.pubkey_options && ses.authstate.pubkey_options->original_command) { From f88bed7a30d4c327b42dcd28ce7642ba74dfe592 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 1 Sep 2009 16:38:26 +0000 Subject: [PATCH 36/45] Rearrange getaddrstring() etc --HG-- extra : convert_revision : 8a18c4a60aeaec085923d13d98fa0f93c506ceba --- cli-kex.c | 2 +- cli-main.c | 11 +---- cli-runopts.c | 10 +++-- cli-session.c | 4 +- common-session.c | 4 +- dbutil.c | 111 ++++++++++++++++++++++------------------------ dbutil.h | 6 ++- debug.h | 2 +- options.h | 4 +- packet.c | 17 +++---- runopts.h | 4 +- session.h | 8 ++-- svr-chansession.c | 22 ++++++--- svr-main.c | 49 +++++++++----------- svr-runopts.c | 8 +++- svr-session.c | 18 +++++--- 16 files changed, 141 insertions(+), 139 deletions(-) diff --git a/cli-kex.c b/cli-kex.c index c4048ec..0d5a9d2 100644 --- a/cli-kex.c +++ b/cli-kex.c @@ -304,7 +304,7 @@ static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen) { fseek(hostsfile, 0, SEEK_END); /* In case it wasn't opened append */ buf_setpos(line, 0); buf_setlen(line, 0); - buf_putbytes(line, ses.remotehost, hostlen); + buf_putbytes(line, cli_opts.remotehost, hostlen); buf_putbyte(line, ' '); buf_putbytes(line, algoname, algolen); buf_putbyte(line, ' '); diff --git a/cli-main.c b/cli-main.c index e7ddaa8..b1a0053 100644 --- a/cli-main.c +++ b/cli-main.c @@ -45,8 +45,6 @@ int main(int argc, char ** argv) { int sock_in, sock_out; char* error = NULL; - char* hostandport; - int len; _dropbear_exit = cli_dropbear_exit; _dropbear_log = cli_dropbear_log; @@ -78,14 +76,7 @@ int main(int argc, char ** argv) { 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:%s", - cli_opts.remotehost, cli_opts.remoteport); - - cli_session(sock_in, sock_out, hostandport); + cli_session(sock_in, sock_out); /* not reached */ return -1; diff --git a/cli-runopts.c b/cli-runopts.c index eab33b7..e32d2fb 100644 --- a/cli-runopts.c +++ b/cli-runopts.c @@ -378,15 +378,19 @@ void cli_getopts(int argc, char ** argv) { } } if (keepalive_arg) { - if (m_str_to_uint(keepalive_arg, &opts.keepalive_secs) == DROPBEAR_FAILURE) { + unsigned int val; + if (m_str_to_uint(keepalive_arg, &val) == DROPBEAR_FAILURE) { dropbear_exit("Bad keepalive '%s'", keepalive_arg); } + opts.keepalive_secs = val; } if (idle_timeout_arg) { - if (m_str_to_uint(idle_timeout_arg, &opts.idle_timeout_secs) == DROPBEAR_FAILURE) { + unsigned int val; + if (m_str_to_uint(idle_timeout_arg, &val) == DROPBEAR_FAILURE) { dropbear_exit("Bad idle_timeout '%s'", idle_timeout_arg); } + opts.idle_timeout_secs = val; } #ifdef ENABLE_CLI_NETCAT @@ -454,7 +458,7 @@ multihop_passthrough_args() { sign_key * key = (sign_key*)iter->item; const size_t size = len - total; int written = snprintf(ret+total, size, "-i %s", key->filename); - dropbear_assert(written < size); + dropbear_assert((unsigned int)written < size); total += written; } diff --git a/cli-session.c b/cli-session.c index ed58a11..96a0929 100644 --- a/cli-session.c +++ b/cli-session.c @@ -82,13 +82,13 @@ static const struct ChanType *cli_chantypes[] = { NULL /* Null termination */ }; -void cli_session(int sock_in, int sock_out, char* remotehost) { +void cli_session(int sock_in, int sock_out) { seedrandom(); crypto_init(); - common_session_init(sock_in, sock_out, remotehost); + common_session_init(sock_in, sock_out); chaninitialise(cli_chantypes); diff --git a/common-session.c b/common-session.c index 1348da2..f2ad436 100644 --- a/common-session.c +++ b/common-session.c @@ -52,12 +52,10 @@ int exitflag = 0; /* GLOBAL */ /* called only at the start of a session, set up initial state */ -void common_session_init(int sock_in, int sock_out, char* remotehost) { +void common_session_init(int sock_in, int sock_out) { TRACE(("enter session_init")) - ses.remotehost = remotehost; - ses.sock_in = sock_in; ses.sock_out = sock_out; ses.maxfd = MAX(sock_in, sock_out); diff --git a/dbutil.c b/dbutil.c index 73302e0..9fd90d1 100644 --- a/dbutil.c +++ b/dbutil.c @@ -539,14 +539,47 @@ void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell) { execv(usershell, argv); } +void get_socket_address(int fd, char **local_host, char **local_port, + char **remote_host, char **remote_port, int host_lookup) +{ + struct sockaddr_storage addr; + socklen_t addrlen; + + if (local_host || local_port) { + addrlen = sizeof(addr); + if (getsockname(fd, (struct sockaddr*)&addr, &addrlen) < 0) { + dropbear_exit("Failed socket address: %s", strerror(errno)); + } + getaddrstring(&addr, local_host, local_port, host_lookup); + } + if (remote_host || remote_port) { + addrlen = sizeof(addr); + if (getpeername(fd, (struct sockaddr*)&addr, &addrlen) < 0) { + dropbear_exit("Failed socket address: %s", strerror(errno)); + } + getaddrstring(&addr, remote_host, remote_port, host_lookup); + } +} + /* Return a string representation of the socket address passed. The return * value is allocated with malloc() */ -unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport) { +void getaddrstring(struct sockaddr_storage* addr, + char **ret_host, char **ret_port, + int host_lookup) { - char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; - char *retstring = NULL; - int ret; + char host[NI_MAXHOST+1], serv[NI_MAXSERV+1]; unsigned int len; + int ret; + + int flags = NI_NUMERICSERV | NI_NUMERICHOST; + +#ifndef DO_HOST_LOOKUP + host_lookup = 0; +#endif + + if (host_lookup) { + flags = NI_NUMERICSERV; + } len = sizeof(struct sockaddr_storage); /* Some platforms such as Solaris 8 require that len is the length @@ -564,66 +597,28 @@ unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport) { #endif #endif - ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf), - sbuf, sizeof(sbuf), NI_NUMERICSERV | NI_NUMERICHOST); + ret = getnameinfo((struct sockaddr*)addr, len, host, sizeof(host)-1, + serv, sizeof(serv)-1, flags); if (ret != 0) { - /* This is a fairly bad failure - it'll fallback to IP if it - * just can't resolve */ - dropbear_exit("failed lookup (%d, %d)", ret, errno); + if (host_lookup) { + /* On some systems (Darwin does it) we get EINTR from getnameinfo + * somehow. Eew. So we'll just return the IP, since that doesn't seem + * to exhibit that behaviour. */ + getaddrstring(addr, ret_host, ret_port, 0); + return; + } else { + /* if we can't do a numeric lookup, something's gone terribly wrong */ + dropbear_exit("Failed lookup: %s", gai_strerror(ret)); + } } - if (withport) { - len = strlen(hbuf) + 2 + strlen(sbuf); - retstring = (char*)m_malloc(len); - snprintf(retstring, len, "%s:%s", hbuf, sbuf); - } else { - retstring = m_strdup(hbuf); + if (ret_host) { + *ret_host = m_strdup(host); } - - return retstring; -} - -/* Get the hostname corresponding to the address addr. On failure, the IP - * address is returned. The return value is allocated with strdup() */ -char* getaddrhostname(struct sockaddr_storage * addr) { - - char hbuf[NI_MAXHOST]; - char sbuf[NI_MAXSERV]; - int ret; - unsigned int len; -#ifdef DO_HOST_LOOKUP - const int flags = NI_NUMERICSERV; -#else - const int flags = NI_NUMERICHOST | NI_NUMERICSERV; -#endif - - len = sizeof(struct sockaddr_storage); - /* Some platforms such as Solaris 8 require that len is the length - * of the specific structure. */ -#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY - if (addr->ss_family == AF_INET) { - len = sizeof(struct sockaddr_in); + if (ret_port) { + *ret_port = m_strdup(serv); } -#ifdef AF_INET6 - if (addr->ss_family == AF_INET6) { - len = sizeof(struct sockaddr_in6); - } -#endif -#endif - - - ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf), - sbuf, sizeof(sbuf), flags); - - if (ret != 0) { - /* On some systems (Darwin does it) we get EINTR from getnameinfo - * somehow. Eew. So we'll just return the IP, since that doesn't seem - * to exhibit that behaviour. */ - return getaddrstring(addr, 0); - } - - return m_strdup(hbuf); } #ifdef DEBUG_TRACE diff --git a/dbutil.h b/dbutil.h index fe08f18..474db62 100644 --- a/dbutil.h +++ b/dbutil.h @@ -46,7 +46,10 @@ void printhex(const char * label, const unsigned char * buf, int len); extern int debug_trace; #endif char * stripcontrol(const char * text); -unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport); +void get_socket_address(int fd, char **local_host, char **local_port, + char **remote_host, char **remote_port, int host_lookup); +void getaddrstring(struct sockaddr_storage* addr, + char **ret_host, char **ret_port, int host_lookup); int dropbear_listen(const char* address, const char* port, int *socks, unsigned int sockcount, char **errstring, int *maxfd); int spawn_command(void(*exec_fn)(void *user_data), void *exec_data, @@ -57,7 +60,6 @@ int connect_unix(const char* addr); #endif int connect_remote(const char* remotehost, const char* remoteport, int nonblocking, char ** errstring); -char* getaddrhostname(struct sockaddr_storage * addr); int buf_readfile(buffer* buf, const char* filename); int buf_getline(buffer * line, FILE * authfile); diff --git a/debug.h b/debug.h index b8c2a57..a9cc0bd 100644 --- a/debug.h +++ b/debug.h @@ -39,7 +39,7 @@ * Caution: Don't use this in an unfriendly environment (ie unfirewalled), * since the printing may not sanitise strings etc. This will add a reasonable * amount to your executable size. */ -/*#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/options.h b/options.h index f151cdb..ee315d5 100644 --- a/options.h +++ b/options.h @@ -167,9 +167,9 @@ much traffic. */ * but there's an interface via a PAM module - don't bother using it otherwise. * You can't enable both PASSWORD and PAM. */ -#define ENABLE_SVR_PASSWORD_AUTH +/*#define ENABLE_SVR_PASSWORD_AUTH*/ /* PAM requires ./configure --enable-pam */ -/*#define ENABLE_SVR_PAM_AUTH*/ +#define ENABLE_SVR_PAM_AUTH #define ENABLE_SVR_PUBKEY_AUTH /* Whether to take public key options in diff --git a/packet.c b/packet.c index 9621bbd..ea96c04 100644 --- a/packet.c +++ b/packet.c @@ -157,9 +157,10 @@ void read_packet() { static int read_packet_init() { unsigned int maxlen; - int len; - unsigned char blocksize; - unsigned char macsize; + int slen; + unsigned int len; + unsigned int blocksize; + unsigned int macsize; blocksize = ses.keys->recv.algo_crypt->blocksize; @@ -173,12 +174,12 @@ static int read_packet_init() { maxlen = blocksize - ses.readbuf->pos; /* read the rest of the packet if possible */ - len = read(ses.sock_in, buf_getwriteptr(ses.readbuf, maxlen), + slen = read(ses.sock_in, buf_getwriteptr(ses.readbuf, maxlen), maxlen); - if (len == 0) { + if (slen == 0) { ses.remoteclosed(); } - if (len < 0) { + if (slen < 0) { if (errno == EINTR) { TRACE(("leave read_packet_init: EINTR")) return DROPBEAR_FAILURE; @@ -186,9 +187,9 @@ static int read_packet_init() { dropbear_exit("error reading: %s", strerror(errno)); } - buf_incrwritepos(ses.readbuf, len); + buf_incrwritepos(ses.readbuf, slen); - if ((unsigned int)len != maxlen) { + if ((unsigned int)slen != maxlen) { /* don't have enough bytes to determine length, get next time */ return DROPBEAR_FAILURE; } diff --git a/runopts.h b/runopts.h index 22376c1..9bf0c26 100644 --- a/runopts.h +++ b/runopts.h @@ -37,8 +37,8 @@ typedef struct runopts { int listen_fwd_all; #endif unsigned int recv_window; - unsigned int keepalive_secs; - unsigned int idle_timeout_secs; + time_t keepalive_secs; + time_t idle_timeout_secs; } runopts; diff --git a/session.h b/session.h index eec7f01..128214b 100644 --- a/session.h +++ b/session.h @@ -41,7 +41,7 @@ extern int sessinitdone; /* Is set to 0 somewhere */ extern int exitflag; -void common_session_init(int sock_in, int sock_out, char* remotehost); +void common_session_init(int sock_in, int sock_out); void session_loop(void(*loophandler)()); void common_session_cleanup(); void session_identification(); @@ -51,12 +51,12 @@ const char* get_user_shell(); void fill_passwd(const char* username); /* Server */ -void svr_session(int sock, int childpipe, char *remotehost, char *addrstring); +void svr_session(int sock, int childpipe); 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_in, int sock_out, char *remotehost); +void cli_session(int sock_in, int sock_out); void cli_session_cleanup(); void cleantext(unsigned char* dirtytext); @@ -110,8 +110,6 @@ struct sshsession { int sock_in; int sock_out; - unsigned char *remotehost; /* the peer hostname */ - unsigned char *remoteident; int maxfd; /* the maximum file descriptor to check with select() */ diff --git a/svr-chansession.c b/svr-chansession.c index 2664382..503e789 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -250,6 +250,17 @@ static int newchansess(struct Channel *channel) { } +static struct logininfo* +chansess_login_alloc(struct ChanSess *chansess) { + char *remotehost; + struct logininfo * li; + get_socket_address(ses.sock_in, NULL, NULL, &remotehost, NULL, 1); + li = login_alloc_entry(chansess->pid, ses.authstate.username, + remotehost, chansess->tty); + m_free(remotehost); + return li; +} + /* clean a session channel */ static void closechansess(struct Channel *channel) { @@ -273,8 +284,7 @@ static void closechansess(struct Channel *channel) { if (chansess->tty) { /* write the utmp/wtmp login record */ - li = login_alloc_entry(chansess->pid, ses.authstate.username, - ses.remotehost, chansess->tty); + li = chansess_login_alloc(chansess); login_logout(li); login_free_entry(li); @@ -578,7 +588,6 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess, int iscmd, int issubsys) { unsigned int cmdlen; - int is_forced; int ret; TRACE(("enter sessioncommand")) @@ -628,6 +637,8 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess, } #endif + // XXX set SSH_CONNECTION string here, since about to close socket... + if (chansess->term == NULL) { /* no pty */ ret = noptycommand(channel, chansess); @@ -737,8 +748,7 @@ static int ptycommand(struct Channel *channel, struct ChanSess *chansess) { /* write the utmp/wtmp login record - must be after changing the * terminal used for stdout with the dup2 above */ - li= login_alloc_entry(getpid(), ses.authstate.username, - ses.remotehost, chansess->tty); + li = chansess_login_alloc(chansess); login_login(li); login_free_entry(li); @@ -886,6 +896,8 @@ static void execchild(void *user_data) { addnewvar("SSH_TTY", chansess->tty); } + + #ifdef ENABLE_SVR_PUBKEY_OPTIONS if (ses.authstate.pubkey_options && ses.authstate.pubkey_options->original_command) { diff --git a/svr-main.c b/svr-main.c index 45ea4be..712657e 100644 --- a/svr-main.c +++ b/svr-main.c @@ -77,22 +77,16 @@ int main(int argc, char ** argv) #ifdef INETD_MODE static void main_inetd() { - - struct sockaddr_storage remoteaddr; - socklen_t remoteaddrlen; - char * addrstring = NULL; + char *host, *port = NULL; /* Set up handlers, syslog, seed random */ commonsetup(); - remoteaddrlen = sizeof(remoteaddr); - if (getpeername(0, (struct sockaddr*)&remoteaddr, &remoteaddrlen) < 0) { - dropbear_exit("Unable to getpeername: %s", strerror(errno)); - } - /* In case our inetd was lax in logging source addresses */ - addrstring = getaddrstring(&remoteaddr, 1); - dropbear_log(LOG_INFO, "Child connection from %s", addrstring); + get_socket_address(0, NULL, NULL, &host, &port, 0); + dropbear_log(LOG_INFO, "Child connection from %s:%s", host, port); + m_free(host); + m_free(port); /* Don't check the return value - it may just fail since inetd has * already done setsid() after forking (xinetd on Darwin appears to do @@ -102,7 +96,7 @@ static void main_inetd() { /* Start service program * -1 is a dummy childpipe, just something we can close() without * mattering. */ - svr_session(0, -1, getaddrhostname(&remoteaddr), addrstring); + svr_session(0, -1); /* notreached */ } @@ -218,14 +212,13 @@ void main_noinetd() { /* handle each socket which has something to say */ for (i = 0; i < listensockcount; i++) { - - struct sockaddr_storage remoteaddr; - socklen_t remoteaddrlen = 0; size_t num_unauthed_for_addr = 0; size_t num_unauthed_total = 0; - char * remote_addr_str = NULL; + char *remote_host = NULL, *remote_port = NULL; pid_t fork_ret = 0; size_t conn_idx = 0; + struct sockaddr_storage remoteaddr; + socklen_t remoteaddrlen; if (!FD_ISSET(listensocks[i], &fds)) continue; @@ -240,14 +233,14 @@ void main_noinetd() { } /* Limit the number of unauthenticated connections per IP */ - remote_addr_str = getaddrstring(&remoteaddr, 0); + getaddrstring(&remoteaddr, &remote_host, NULL, 0); num_unauthed_for_addr = 0; num_unauthed_total = 0; for (j = 0; j < MAX_UNAUTH_CLIENTS; j++) { if (childpipes[j] >= 0) { num_unauthed_total++; - if (strcmp(remote_addr_str, preauth_addrs[j]) == 0) { + if (strcmp(remote_host, preauth_addrs[j]) == 0) { num_unauthed_for_addr++; } } else { @@ -280,21 +273,21 @@ void main_noinetd() { /* parent */ childpipes[conn_idx] = childpipe[0]; m_close(childpipe[1]); - preauth_addrs[conn_idx] = remote_addr_str; - remote_addr_str = NULL; + preauth_addrs[conn_idx] = remote_host; + remote_host = NULL; } else { /* child */ - char * addrstring = NULL; #ifdef DEBUG_FORKGPROF extern void _start(void), etext(void); monstartup((u_long)&_start, (u_long)&etext); #endif /* DEBUG_FORKGPROF */ - m_free(remote_addr_str); - addrstring = getaddrstring(&remoteaddr, 1); - dropbear_log(LOG_INFO, "Child connection from %s", addrstring); + getaddrstring(&remoteaddr, NULL, &remote_port, 0); + dropbear_log(LOG_INFO, "Child connection from %s:%s", remote_host, remote_port); + m_free(remote_host); + m_free(remote_port); #ifndef DEBUG_NOFORK if (setsid() < 0) { @@ -310,9 +303,7 @@ void main_noinetd() { m_close(childpipe[0]); /* start the session */ - svr_session(childsock, childpipe[1], - getaddrhostname(&remoteaddr), - addrstring); + svr_session(childsock, childpipe[1]); /* don't return */ dropbear_assert(0); } @@ -320,8 +311,8 @@ void main_noinetd() { out: /* This section is important for the parent too */ m_close(childsock); - if (remote_addr_str) { - m_free(remote_addr_str); + if (remote_host) { + m_free(remote_host); } } } /* for(;;) loop */ diff --git a/svr-runopts.c b/svr-runopts.c index 4f1355a..4e59b75 100644 --- a/svr-runopts.c +++ b/svr-runopts.c @@ -296,15 +296,19 @@ void svr_getopts(int argc, char ** argv) { } if (keepalive_arg) { - if (m_str_to_uint(keepalive_arg, &opts.keepalive_secs) == DROPBEAR_FAILURE) { + unsigned int val; + if (m_str_to_uint(keepalive_arg, &val) == DROPBEAR_FAILURE) { dropbear_exit("Bad keepalive '%s'", keepalive_arg); } + opts.keepalive_secs = val; } if (idle_timeout_arg) { - if (m_str_to_uint(idle_timeout_arg, &opts.idle_timeout_secs) == DROPBEAR_FAILURE) { + unsigned int val; + if (m_str_to_uint(idle_timeout_arg, &val) == DROPBEAR_FAILURE) { dropbear_exit("Bad idle_timeout '%s'", idle_timeout_arg); } + opts.idle_timeout_secs = val; } } diff --git a/svr-session.c b/svr-session.c index 89b16cb..a509906 100644 --- a/svr-session.c +++ b/svr-session.c @@ -74,27 +74,33 @@ static const struct ChanType *svr_chantypes[] = { NULL /* Null termination is mandatory. */ }; -void svr_session(int sock, int childpipe, - char* remotehost, char *addrstring) { - +void svr_session(int sock, int childpipe) { + char *host, *port; + size_t len; reseedrandom(); crypto_init(); - common_session_init(sock, sock, remotehost); + common_session_init(sock, sock); /* Initialise server specific parts of the session */ svr_ses.childpipe = childpipe; - svr_ses.addrstring = addrstring; #ifdef __uClinux__ svr_ses.server_pid = getpid(); #endif - svr_ses.addrstring = addrstring; svr_authinitialise(); chaninitialise(svr_chantypes); svr_chansessinitialise(); ses.connect_time = time(NULL); + /* for logging the remote address */ + get_socket_address(ses.sock_in, NULL, NULL, &host, &port, 0); + len = strlen(host) + strlen(port) + 2; + svr_ses.addrstring = m_malloc(len); + snprintf(svr_ses.addrstring, len, "%s:%s", host, port); + m_free(host); + m_free(port); + /* set up messages etc */ ses.remoteclosed = svr_remoteclosed; From 4e9f22c6021ca908184e2e9437f9e56b436926a1 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Wed, 2 Sep 2009 14:47:12 +0000 Subject: [PATCH 37/45] - Set $SSH_CONNECTION - Document environment variables in the manpage --HG-- extra : convert_revision : 1a93c6112f00730f5cd21a853d3bd5ca8079f725 --- chansession.h | 4 ++++ dropbear.8 | 28 ++++++++++++++++++++++++++++ svr-chansession.c | 30 ++++++++++++++++++++++++++++-- 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/chansession.h b/chansession.h index b9d1995..924518b 100644 --- a/chansession.h +++ b/chansession.h @@ -50,6 +50,10 @@ struct ChanSess { /* exit details */ struct exitinfo exit; + + /* Used to set $SSH_CONNECTION in the child session. + Is only set temporarily before forking */ + char *connection_string; #ifndef DISABLE_X11FWD struct Listener * x11listener; diff --git a/dropbear.8 b/dropbear.8 index ecbad31..7d02e12 100644 --- a/dropbear.8 +++ b/dropbear.8 @@ -154,6 +154,34 @@ By default the file /etc/motd will be printed for any login shell (unless disabled at compile-time). This can also be disabled per-user by creating a file ~/.hushlogin . +.SH ENVIRONMENT VARIABLES +Dropbear sets the standard variables USER, LOGNAME, HOME, SHELL, PATH, and TERM. + +The variables below are set for sessions as appropriate. + +.TP +.B SSH_TTY +This is set to the allocated TTY if a PTY was used. + +.TP +.B SSH_CONNECTION +Contains " ". + +.TP +.B DISPLAY +Set X11 forwarding is used. + +.TP +.B SSH_ORIGINAL_COMMAND +If a 'command=' authorized_keys option was used, the original command is specified +in this variable. If a shell was requested this is set to an empty value. + +.TP +.B SSH_AUTH_SOCK +Set to a forwarded ssh-agent connection. + + + .SH AUTHOR Matt Johnston (matt@ucc.asn.au). .br diff --git a/svr-chansession.c b/svr-chansession.c index 503e789..782e97f 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -222,6 +222,7 @@ static int newchansess(struct Channel *channel) { chansess = (struct ChanSess*)m_malloc(sizeof(struct ChanSess)); chansess->cmd = NULL; + chansess->connection_string = NULL; chansess->pid = 0; /* pty details */ @@ -580,6 +581,21 @@ static int sessionpty(struct ChanSess * chansess) { return DROPBEAR_SUCCESS; } +static char* make_connection_string() { + char *local_ip, *local_port, *remote_ip, *remote_port; + size_t len; + char *ret; + get_socket_address(ses.sock_in, &local_ip, &local_port, &remote_ip, &remote_port, 0); + len = strlen(local_ip) + strlen(local_port) + strlen(remote_ip) + strlen(remote_port) + 4; + ret = m_malloc(len); + snprintf(ret, len, "%s %s %s %s", remote_ip, remote_port, local_ip, local_port); + m_free(local_ip); + m_free(local_port); + m_free(remote_ip); + m_free(remote_port); + return ret; +} + /* Handle a command request from the client. This is used for both shell * and command-execution requests, and passes the command to * noptycommand or ptycommand as appropriate. @@ -637,7 +653,11 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess, } #endif - // XXX set SSH_CONNECTION string here, since about to close socket... + /* uClinux will vfork(), so there'll be a race as + connection_string is freed below. */ +#ifndef __uClinux__ + chansess->connection_string = make_connection_string(); +#endif if (chansess->term == NULL) { /* no pty */ @@ -647,6 +667,10 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess, ret = ptycommand(channel, chansess); } +#ifndef __uClinux__ + m_free(chansess->connection_string); +#endif + if (ret == DROPBEAR_FAILURE) { m_free(chansess->cmd); } @@ -896,7 +920,9 @@ static void execchild(void *user_data) { addnewvar("SSH_TTY", chansess->tty); } - + if (chansess->connection_string) { + addnewvar("SSH_CONNECTION", chansess->connection_string); + } #ifdef ENABLE_SVR_PUBKEY_OPTIONS if (ses.authstate.pubkey_options && From 48734bb3b573ae20e5f2a1c3372ac5510ed2ee04 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Wed, 2 Sep 2009 15:05:14 +0000 Subject: [PATCH 38/45] - scp progressbar needs strlcat(), so add compat.o --HG-- extra : convert_revision : 0743230bac5ae28d1b773fb4d89c4d88b6a9a0c1 --- Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index fe10204..599f33c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -41,7 +41,7 @@ KEYOBJS=dropbearkey.o gendss.o genrsa.o CONVERTOBJS=dropbearconvert.o keyimport.o -SCPOBJS=scp.o progressmeter.o atomicio.o scpmisc.o +SCPOBJS=scp.o progressmeter.o atomicio.o scpmisc.o compat.o HEADERS=options.h dbutil.h session.h packet.h algo.h ssh.h buffer.h kex.h \ dss.h bignum.h signkey.h rsa.h random.h service.h auth.h \ From c35e38c5e9387137a7836fa6aa1a6f9a242387cf Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Wed, 2 Sep 2009 15:17:14 +0000 Subject: [PATCH 39/45] - Remove options that dbclient won't know about --HG-- extra : convert_revision : 2d401308f73352e92d3c662d33920b24fc12bfa1 --- scp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scp.c b/scp.c index da64dd1..48b4017 100644 --- a/scp.c +++ b/scp.c @@ -343,7 +343,7 @@ main(int argc, char **argv) addargs(&args, "-p%s", optarg); break; case 'B': - addargs(&args, "-oBatchmode yes"); + fprintf(stderr, "Note: -B option is disabled in this version of scp"); break; case 'l': speed = strtod(optarg, &endp); @@ -492,9 +492,13 @@ toremote(char *targ, int argc, char **argv) addargs(&alist, "%s", ssh_program); if (verbose_mode) addargs(&alist, "-v"); +#if 0 + // Disabled since dbclient won't understand them + // and scp works fine without them. addargs(&alist, "-x"); addargs(&alist, "-oClearAllForwardings yes"); addargs(&alist, "-n"); +#endif *src++ = 0; if (*src == 0) From 4dfb834f7cd153cd90904c36dca314f5516beca2 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sat, 5 Sep 2009 11:40:00 +0000 Subject: [PATCH 40/45] Move remotehost into svr_ses structure since we can't look it up once we've forked (the connection socket has been closed). Fixes inetd mode. --HG-- extra : convert_revision : 7d5d152ec84fb11a188966c1400d213c908cc511 --- session.h | 3 +++ svr-chansession.c | 5 +---- svr-session.c | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/session.h b/session.h index 128214b..20a90a4 100644 --- a/session.h +++ b/session.h @@ -211,6 +211,9 @@ struct serversession { /* The numeric address they connected from, used for logging */ char * addrstring; + /* The resolved remote address, used for lastlog etc */ + char *remotehost; + #ifdef __uClinux__ pid_t server_pid; #endif diff --git a/svr-chansession.c b/svr-chansession.c index 782e97f..5ecc57f 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -253,12 +253,9 @@ static int newchansess(struct Channel *channel) { static struct logininfo* chansess_login_alloc(struct ChanSess *chansess) { - char *remotehost; struct logininfo * li; - get_socket_address(ses.sock_in, NULL, NULL, &remotehost, NULL, 1); li = login_alloc_entry(chansess->pid, ses.authstate.username, - remotehost, chansess->tty); - m_free(remotehost); + svr_ses.remotehost, chansess->tty); return li; } diff --git a/svr-session.c b/svr-session.c index a509906..da49e1a 100644 --- a/svr-session.c +++ b/svr-session.c @@ -101,6 +101,9 @@ void svr_session(int sock, int childpipe) { m_free(host); m_free(port); + get_socket_address(ses.sock_in, NULL, NULL, + &svr_ses.remotehost, NULL, 1); + /* set up messages etc */ ses.remoteclosed = svr_remoteclosed; From 52551cb771e8cd1ba3b5eb137fc43b61bd35cd79 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 8 Sep 2009 14:53:53 +0000 Subject: [PATCH 41/45] - Test for pam_fail_delay() function in configure - Recognise "username:" as a PAM prompt - Add some randomness to the auth-failure delay - Fix wrongly committed options.h/debug.h --HG-- extra : convert_revision : f242f0e66fb0ea5d3b374995d2f548d37dd8f3a3 --- configure.in | 1 + debug.h | 2 +- options.h | 4 ++-- svr-auth.c | 8 +++++++- svr-authpam.c | 14 ++++++++++---- 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/configure.in b/configure.in index c7149e8..97ce251 100644 --- a/configure.in +++ b/configure.in @@ -146,6 +146,7 @@ AC_ARG_ENABLE(pam, if test "x$enableval" = "xyes"; then AC_CHECK_LIB(pam, pam_authenticate, , AC_MSG_ERROR([*** PAM missing - install first or check config.log ***])) AC_MSG_NOTICE(Enabling PAM) + AC_CHECK_FUNCS(pam_fail_delay) else AC_DEFINE(DISABLE_PAM,, Use PAM) AC_MSG_NOTICE(Disabling PAM) diff --git a/debug.h b/debug.h index a9cc0bd..b20e685 100644 --- a/debug.h +++ b/debug.h @@ -39,7 +39,7 @@ * Caution: Don't use this in an unfriendly environment (ie unfirewalled), * since the printing may not sanitise strings etc. This will add a reasonable * amount to your executable size. */ -#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/options.h b/options.h index ee315d5..f151cdb 100644 --- a/options.h +++ b/options.h @@ -167,9 +167,9 @@ much traffic. */ * but there's an interface via a PAM module - don't bother using it otherwise. * You can't enable both PASSWORD and PAM. */ -/*#define ENABLE_SVR_PASSWORD_AUTH*/ +#define ENABLE_SVR_PASSWORD_AUTH /* PAM requires ./configure --enable-pam */ -#define ENABLE_SVR_PAM_AUTH +/*#define ENABLE_SVR_PAM_AUTH*/ #define ENABLE_SVR_PUBKEY_AUTH /* Whether to take public key options in diff --git a/svr-auth.c b/svr-auth.c index 5da0aa7..9e468fa 100644 --- a/svr-auth.c +++ b/svr-auth.c @@ -33,6 +33,7 @@ #include "packet.h" #include "auth.h" #include "runopts.h" +#include "random.h" static void authclear(); static int checkusername(unsigned char *username, unsigned int userlen); @@ -337,7 +338,12 @@ void send_msg_userauth_failure(int partial, int incrfail) { encrypt_packet(); if (incrfail) { - usleep(300000); /* XXX improve this */ + unsigned int delay; + genrandom((unsigned char*)&delay, sizeof(delay)); + /* We delay for 300ms +- 50ms, 0.1ms granularity */ + delay = 250000 + (delay % 1000)*100; + usleep(delay); + dropbear_log(LOG_INFO, "delay is %d", delay); ses.authstate.failcount++; } diff --git a/svr-authpam.c b/svr-authpam.c index 8d6a6e7..a570d71 100644 --- a/svr-authpam.c +++ b/svr-authpam.c @@ -102,7 +102,7 @@ pamConvFunc(int num_msg, /* We don't recognise the prompt as asking for a password, so can't handle it. Add more above as required for different pam modules/implementations */ - dropbear_log(LOG_NOTICE, "PAM unknown prompt %s (no echo)", + dropbear_log(LOG_NOTICE, "PAM unknown prompt '%s' (no echo)", compare_message); rc = PAM_CONV_ERR; break; @@ -123,12 +123,15 @@ pamConvFunc(int num_msg, case PAM_PROMPT_ECHO_ON: - if (!((strcmp(compare_message, "login:" ) == 0) - || (strcmp(compare_message, "please enter username:") == 0))) { + if (!( + (strcmp(compare_message, "login:" ) == 0) + || (strcmp(compare_message, "please enter username:") == 0) + || (strcmp(compare_message, "username:") == 0) + )) { /* We don't recognise the prompt as asking for a username, so can't handle it. Add more above as required for different pam modules/implementations */ - dropbear_log(LOG_NOTICE, "PAM unknown prompt %s (with echo)", + dropbear_log(LOG_NOTICE, "PAM unknown prompt '%s' (with echo)", compare_message); rc = PAM_CONV_ERR; break; @@ -212,7 +215,10 @@ void svr_auth_pam() { goto cleanup; } +#ifdef HAVE_PAM_FAIL_DELAY + /* We have our own random delay code already, disable PAM's */ (void) pam_fail_delay(pamHandlep, 0 /* musec_delay */); +#endif /* (void) pam_set_item(pamHandlep, PAM_FAIL_DELAY, (void*) pamDelayFunc); */ From 95a01f9002a4367e4465aa9cfe9d1ade270ab9a4 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 10 Sep 2009 11:12:31 +0000 Subject: [PATCH 42/45] Remove extraneous semicolon --HG-- extra : convert_revision : ea33f9576dd0ff344d6f1d150a01265470f56131 --- cli-runopts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli-runopts.c b/cli-runopts.c index e32d2fb..93329f2 100644 --- a/cli-runopts.c +++ b/cli-runopts.c @@ -480,7 +480,7 @@ multihop_passthrough_args() { static void parse_multihop_hostname(const char* orighostarg, const char* argv0) { char *userhostarg = NULL; char *hostbuf = NULL; - char *last_hop = NULL;; + char *last_hop = NULL; char *remainder = NULL; /* both scp and rsync parse a user@host argument From e3ca0513a08bd3cab28df0944aa82da2922e367d Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 11 Sep 2009 14:02:04 +0000 Subject: [PATCH 43/45] - Disable compression for non-final multihops --HG-- extra : convert_revision : c507a2aacb9e0db4c0266891b8915c614e32857e --- algo.h | 3 ++- cli-runopts.c | 7 +++++++ common-algo.c | 7 ++++++- common-kex.c | 19 ++++++++++++++----- runopts.h | 9 ++++++++- session.h | 3 +++ svr-runopts.c | 3 +++ 7 files changed, 43 insertions(+), 8 deletions(-) diff --git a/algo.h b/algo.h index c83cfff..87e524d 100644 --- a/algo.h +++ b/algo.h @@ -50,7 +50,8 @@ extern algo_type sshkex[]; extern algo_type sshhostkey[]; extern algo_type sshciphers[]; extern algo_type sshhashes[]; -extern algo_type sshcompress[]; +extern algo_type ssh_compress[]; +extern algo_type ssh_nocompress[]; extern const struct dropbear_cipher dropbear_nocipher; extern const struct dropbear_cipher_mode dropbear_mode_none; diff --git a/cli-runopts.c b/cli-runopts.c index 93329f2..aedd4b7 100644 --- a/cli-runopts.c +++ b/cli-runopts.c @@ -144,6 +144,9 @@ void cli_getopts(int argc, char ** argv) { #endif #ifdef ENABLE_CLI_PROXYCMD cli_opts.proxycmd = NULL; +#endif +#ifndef DISABLE_ZLIB + opts.enable_compress = 1; #endif /* not yet opts.ipv4 = 1; @@ -530,6 +533,10 @@ static void parse_multihop_hostname(const char* orighostarg, const char* argv0) snprintf(cli_opts.proxycmd, cmd_len, "%s -B %s:%s %s %s", argv0, cli_opts.remotehost, cli_opts.remoteport, passthrough_args, remainder); +#ifndef DISABLE_ZLIB + /* The stream will be incompressible since it's encrypted. */ + opts.enable_compress = 0; +#endif m_free(passthrough_args); } m_free(hostbuf); diff --git a/common-algo.c b/common-algo.c index 892399f..7d694d2 100644 --- a/common-algo.c +++ b/common-algo.c @@ -168,11 +168,16 @@ algo_type sshhashes[] = { {NULL, 0, NULL, 0, NULL} }; -algo_type sshcompress[] = { #ifndef DISABLE_ZLIB +algo_type ssh_compress[] = { {"zlib", DROPBEAR_COMP_ZLIB, NULL, 1, NULL}, {"zlib@openssh.com", DROPBEAR_COMP_ZLIB_DELAY, NULL, 1, NULL}, + {"none", DROPBEAR_COMP_NONE, NULL, 1, NULL}, + {NULL, 0, NULL, 0, NULL} +}; #endif + +algo_type ssh_nocompress[] = { {"none", DROPBEAR_COMP_NONE, NULL, 1, NULL}, {NULL, 0, NULL, 0, NULL} }; diff --git a/common-kex.c b/common-kex.c index cb5cd96..8c47a48 100644 --- a/common-kex.c +++ b/common-kex.c @@ -33,6 +33,7 @@ #include "packet.h" #include "bignum.h" #include "random.h" +#include "runopts.h" /* diffie-hellman-group1-sha1 value for p */ static const unsigned char dh_p_val[] = { @@ -91,10 +92,10 @@ void send_msg_kexinit() { buf_put_algolist(ses.writepayload, sshhashes); /* compression_algorithms_client_to_server */ - buf_put_algolist(ses.writepayload, sshcompress); + buf_put_algolist(ses.writepayload, ses.compress_algos); /* compression_algorithms_server_to_client */ - buf_put_algolist(ses.writepayload, sshcompress); + buf_put_algolist(ses.writepayload, ses.compress_algos); /* languages_client_to_server */ buf_putstring(ses.writepayload, "", 0); @@ -180,8 +181,16 @@ void recv_msg_newkeys() { /* Set up the kex for the first time */ void kexfirstinitialise() { - ses.kexstate.donefirstkex = 0; + +#ifndef DISABLE_ZLIB + if (opts.enable_compress) { + ses.compress_algos = ssh_compress; + } else +#endif + { + ses.compress_algos = ssh_nocompress; + } kexinitialise(); } @@ -670,7 +679,7 @@ static void read_kex_algos() { TRACE(("hash s2c is %s", s2c_hash_algo->name)) /* compression_algorithms_client_to_server */ - c2s_comp_algo = ses.buf_match_algo(ses.payload, sshcompress, &goodguess); + c2s_comp_algo = ses.buf_match_algo(ses.payload, ses.compress_algos, &goodguess); if (c2s_comp_algo == NULL) { erralgo = "comp c->s"; goto error; @@ -678,7 +687,7 @@ static void read_kex_algos() { TRACE(("hash c2s is %s", c2s_comp_algo->name)) /* compression_algorithms_server_to_client */ - s2c_comp_algo = ses.buf_match_algo(ses.payload, sshcompress, &goodguess); + s2c_comp_algo = ses.buf_match_algo(ses.payload, ses.compress_algos, &goodguess); if (s2c_comp_algo == NULL) { erralgo = "comp s->c"; goto error; diff --git a/runopts.h b/runopts.h index 9bf0c26..83b5861 100644 --- a/runopts.h +++ b/runopts.h @@ -40,6 +40,14 @@ typedef struct runopts { time_t keepalive_secs; time_t idle_timeout_secs; +#ifndef DISABLE_ZLIB + /* TODO: add a commandline flag. Currently this is on by default if compression + * is compiled in, but disabled for a client's non-final multihop stages. (The + * intermediate stages are compressed streams, so are uncompressible. */ + int enable_compress; +#endif + + } runopts; extern runopts opts; @@ -135,7 +143,6 @@ typedef struct cli_runopts { #ifdef ENABLE_CLI_PROXYCMD char *proxycmd; #endif - } cli_runopts; extern cli_runopts cli_opts; diff --git a/session.h b/session.h index 20a90a4..355cf03 100644 --- a/session.h +++ b/session.h @@ -160,6 +160,9 @@ struct sshsession { buffer* kexhashbuf; /* session hash buffer calculated from various packets*/ buffer* transkexinit; /* the kexinit packet we send should be kept so we can add it to the hash when generating keys */ + + /* Enables/disables compression */ + algo_type *compress_algos; /* a list of queued replies that should be sent after a KEX has concluded (ie, while dataallowed was unset)*/ diff --git a/svr-runopts.c b/svr-runopts.c index 4e59b75..8a3396a 100644 --- a/svr-runopts.c +++ b/svr-runopts.c @@ -124,6 +124,9 @@ void svr_getopts(int argc, char ** argv) { #endif #ifdef ENABLE_SVR_REMOTETCPFWD svr_opts.noremotetcp = 0; +#endif +#ifndef DISABLE_ZLIB + opts.enable_compress = 1; #endif /* not yet opts.ipv4 = 1; From 8174a2f27b9539511eb1f432df76856222cdfc1a Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 13 Sep 2009 15:31:29 +0000 Subject: [PATCH 44/45] Mention -p's address argument in manpage synopsis --HG-- extra : convert_revision : abef8fc8584889de6fd92ceaec28d094419462e0 --- dropbear.8 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dropbear.8 b/dropbear.8 index 7d02e12..194b996 100644 --- a/dropbear.8 +++ b/dropbear.8 @@ -7,7 +7,7 @@ dropbear \- lightweight SSH2 server .I banner\fR] [\-d .I dsskey\fR] [\-r .I rsakey\fR] [\-p -.IR port ] +.IR [address:]port ] .SH DESCRIPTION .B dropbear is a SSH 2 server designed to be small enough to be used in small memory From 3b078445482f42dabfe4922e67a958dc607b166f Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sat, 27 Feb 2010 11:51:19 +0000 Subject: [PATCH 45/45] - tcpfwd bindaddr support against trunk. needs merging. --HG-- extra : convert_revision : 658fd03abd21e0da7c4c89b9fff9dc693c72daae --- cli-runopts.c | 49 ++++++++++++++++++++++++++++---------- cli-tcpfwd.c | 66 +++++++++++++++++++++++++++++++++------------------ tcpfwd.h | 1 + 3 files changed, 80 insertions(+), 36 deletions(-) diff --git a/cli-runopts.c b/cli-runopts.c index aedd4b7..ddd52b5 100644 --- a/cli-runopts.c +++ b/cli-runopts.c @@ -628,13 +628,15 @@ static void fill_own_user() { } #ifdef ENABLE_CLI_ANYTCPFWD -/* Turn a "listenport:remoteaddr:remoteport" string into into a forwarding +/* Turn a "[listenaddr:]listenport:remoteaddr:remoteport" string into into a forwarding * set, and add it to the forwarding list */ static void addforward(const char* origstr, m_list *fwdlist) { + char *part1 = NULL, *part2 = NULL, *part3 = NULL, *part4 = NULL; + char * listenaddr = NULL; char * listenport = NULL; - char * connectport = NULL; char * connectaddr = NULL; + char * connectport = NULL; struct TCPFwdEntry* newfwd = NULL; char * str = NULL; @@ -644,23 +646,43 @@ static void addforward(const char* origstr, m_list *fwdlist) { is never free()d. */ str = m_strdup(origstr); - listenport = str; + part1 = str; - connectaddr = strchr(str, ':'); - if (connectaddr == NULL) { - TRACE(("connectaddr == NULL")) + part2 = strchr(str, ':'); + if (part2 == NULL) { + TRACE(("part2 == NULL")) goto fail; } - *connectaddr = '\0'; - connectaddr++; + *part2 = '\0'; + part2++; - connectport = strchr(connectaddr, ':'); - if (connectport == NULL) { - TRACE(("connectport == NULL")) + part3 = strchr(part2, ':'); + if (part3 == NULL) { + TRACE(("part3 == NULL")) goto fail; } - *connectport = '\0'; - connectport++; + *part3 = '\0'; + part3++; + + part4 = strchr(part3, ':'); + if (part4) { + *part4 = '\0'; + part4++; + } + + if (part4) { + listenaddr = part1; + listenport = part2; + connectaddr = part3; + connectport = part4; + } else { + listenaddr = NULL; + listenport = part1; + connectaddr = part2; + connectport = part3; + } + + } newfwd = m_malloc(sizeof(struct TCPFwdEntry)); @@ -676,6 +698,7 @@ static void addforward(const char* origstr, m_list *fwdlist) { goto fail; } + newfwd->listenaddr = listenaddr; newfwd->connectaddr = connectaddr; if (newfwd->listenport > 65535) { diff --git a/cli-tcpfwd.c b/cli-tcpfwd.c index 0242654..63eb70e 100644 --- a/cli-tcpfwd.c +++ b/cli-tcpfwd.c @@ -45,7 +45,9 @@ const struct ChanType cli_chan_tcpremote = { #endif #ifdef ENABLE_CLI_LOCALTCPFWD -static int cli_localtcp(unsigned int listenport, const char* remoteaddr, +static int cli_localtcp(const char* listenaddr, + unsigned int listenport, + const char* remoteaddr, unsigned int remoteport); static const struct ChanType cli_chan_tcplocal = { 1, /* sepfds */ @@ -66,11 +68,14 @@ void setup_localtcp() { for (iter = cli_opts.localfwds->first; iter; iter = iter->next) { struct TCPFwdEntry * fwd = (struct TCPFwdEntry*)iter->item; - ret = cli_localtcp(fwd->listenport, + ret = cli_localtcp( + fwd->listenaddr, + fwd->listenport, fwd->connectaddr, fwd->connectport); if (ret == DROPBEAR_FAILURE) { - dropbear_log(LOG_WARNING, "Failed local port forward %d:%s:%d", + dropbear_log(LOG_WARNING, "Failed local port forward %s:%d:%s:%d", + fwd->listenaddr, fwd->listenport, fwd->connectaddr, fwd->connectport); @@ -80,7 +85,9 @@ void setup_localtcp() { } -static int cli_localtcp(unsigned int listenport, const char* remoteaddr, +static int cli_localtcp(const char* listenaddr, + unsigned int listenport, + const char* remoteaddr, unsigned int remoteport) { struct TCPListener* tcpinfo = NULL; @@ -94,10 +101,17 @@ static int cli_localtcp(unsigned int listenport, const char* remoteaddr, tcpinfo->sendaddr = m_strdup(remoteaddr); tcpinfo->sendport = remoteport; - if (opts.listen_fwd_all) { - tcpinfo->listenaddr = m_strdup(""); - } else { - tcpinfo->listenaddr = m_strdup("localhost"); + if (listenaddr) + { + tcpinfo->listenaddr = m_strdup(listenaddr); + } + else + { + if (opts.listen_fwd_all) { + tcpinfo->listenaddr = m_strdup(""); + } else { + tcpinfo->listenaddr = m_strdup("localhost"); + } } tcpinfo->listenport = listenport; @@ -115,7 +129,7 @@ static int cli_localtcp(unsigned int listenport, const char* remoteaddr, #endif /* ENABLE_CLI_LOCALTCPFWD */ #ifdef ENABLE_CLI_REMOTETCPFWD -static void send_msg_global_request_remotetcp(int port) { +static void send_msg_global_request_remotetcp(const char *addr, int port) { char* listenspec = NULL; TRACE(("enter send_msg_global_request_remotetcp")) @@ -124,13 +138,7 @@ static void send_msg_global_request_remotetcp(int port) { buf_putbyte(ses.writepayload, SSH_MSG_GLOBAL_REQUEST); buf_putstring(ses.writepayload, "tcpip-forward", 13); buf_putbyte(ses.writepayload, 1); /* want_reply */ - if (opts.listen_fwd_all) { - listenspec = ""; - } else { - listenspec = "localhost"; - } - /* TODO: IPv6? */; - buf_putstring(ses.writepayload, listenspec, strlen(listenspec)); + buf_putstring(ses.writepayload, addr, strlen(addr)); buf_putint(ses.writepayload, port); encrypt_packet(); @@ -173,7 +181,17 @@ void setup_remotetcp() { for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) { struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item; - send_msg_global_request_remotetcp(fwd->listenport); + if (!fwd->listenaddr) + { + // we store the addresses so that we can compare them + // when the server sends them back + if (opts.listen_fwd_all) { + fwd->listenaddr = m_strdup(""); + } else { + fwd->listenaddr = m_strdup("localhost"); + } + } + send_msg_global_request_remotetcp(fwd->listenaddr, fwd->listenport); } TRACE(("leave setup_remotetcp")) @@ -181,6 +199,7 @@ void setup_remotetcp() { static int newtcpforwarded(struct Channel * channel) { + char *origaddr = NULL; unsigned int origport; m_list_elem * iter = NULL; struct TCPFwdEntry *fwd; @@ -188,23 +207,23 @@ static int newtcpforwarded(struct Channel * channel) { int sock; int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED; - /* We don't care what address they connected to */ - buf_eatstring(ses.payload); - + origaddr = buf_getstring(ses.payload, NULL); origport = buf_getint(ses.payload); /* Find which port corresponds */ for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) { fwd = (struct TCPFwdEntry*)iter->item; - if (origport == fwd->listenport) { + if (origport == fwd->listenport + && (strcmp(origaddr, fwd->listenaddr) == 0)) { break; } } if (iter == NULL) { /* We didn't request forwarding on that port */ - dropbear_log(LOG_INFO, "Server send unrequested port, from port %d", - origport); + cleantext(origaddr); + dropbear_log(LOG_INFO, "Server sent unrequested forward from \"%s:%d\"", + origaddr, origport); goto out; } @@ -226,6 +245,7 @@ static int newtcpforwarded(struct Channel * channel) { err = SSH_OPEN_IN_PROGRESS; out: + m_free(origaddr); TRACE(("leave newtcpdirect: err %d", err)) return err; } diff --git a/tcpfwd.h b/tcpfwd.h index 34da314..c33e853 100644 --- a/tcpfwd.h +++ b/tcpfwd.h @@ -48,6 +48,7 @@ struct TCPListener { struct TCPFwdEntry { const unsigned char* connectaddr; unsigned int connectport; + const unsigned char* listenaddr; unsigned int listenport; unsigned int have_reply; /* is set to 1 after a reply has been received when setting up the forwarding */