diff options
-rw-r--r-- | Makefile.in | 9 | ||||
-rw-r--r-- | agentfwd.h | 25 | ||||
-rw-r--r-- | algo.h | 3 | ||||
-rw-r--r-- | auth.h | 18 | ||||
-rw-r--r-- | buffer.c | 14 | ||||
-rw-r--r-- | buffer.h | 1 | ||||
-rw-r--r-- | channel.h | 2 | ||||
-rw-r--r-- | chansession.h | 7 | ||||
-rw-r--r-- | cli-agentfwd.c | 311 | ||||
-rw-r--r-- | cli-auth.c | 6 | ||||
-rw-r--r-- | cli-authpubkey.c | 94 | ||||
-rw-r--r-- | cli-chansession.c | 46 | ||||
-rw-r--r-- | cli-kex.c | 3 | ||||
-rw-r--r-- | cli-main.c | 16 | ||||
-rw-r--r-- | cli-runopts.c | 145 | ||||
-rw-r--r-- | cli-session.c | 10 | ||||
-rw-r--r-- | cli-tcpfwd.c | 102 | ||||
-rw-r--r-- | common-algo.c | 11 | ||||
-rw-r--r-- | common-kex.c | 135 | ||||
-rw-r--r-- | common-session.c | 28 | ||||
-rw-r--r-- | configure.in | 4 | ||||
-rw-r--r-- | dbclient.1 | 5 | ||||
-rw-r--r-- | dbutil.c | 144 | ||||
-rw-r--r-- | dbutil.h | 9 | ||||
-rw-r--r-- | debug.h | 2 | ||||
-rw-r--r-- | dropbear.8 | 30 | ||||
-rw-r--r-- | list.c | 49 | ||||
-rw-r--r-- | list.h | 28 | ||||
-rw-r--r-- | options.h | 39 | ||||
-rw-r--r-- | packet.c | 312 | ||||
-rw-r--r-- | packet.h | 2 | ||||
-rw-r--r-- | random.c | 6 | ||||
-rw-r--r-- | runopts.h | 26 | ||||
-rw-r--r-- | scp.c | 6 | ||||
-rw-r--r-- | session.h | 71 | ||||
-rw-r--r-- | signkey.c | 13 | ||||
-rw-r--r-- | signkey.h | 14 | ||||
-rw-r--r-- | ssh.h | 11 | ||||
-rw-r--r-- | svr-agentfwd.c | 16 | ||||
-rw-r--r-- | svr-auth.c | 8 | ||||
-rw-r--r-- | svr-authpam.c | 14 | ||||
-rw-r--r-- | svr-authpubkeyoptions.c | 15 | ||||
-rw-r--r-- | svr-chansession.c | 70 | ||||
-rw-r--r-- | svr-main.c | 51 | ||||
-rw-r--r-- | svr-runopts.c | 11 | ||||
-rw-r--r-- | svr-session.c | 40 | ||||
-rw-r--r-- | sysoptions.h | 15 | ||||
-rw-r--r-- | tcpfwd.h | 9 |
48 files changed, 1341 insertions, 665 deletions
diff --git a/Makefile.in b/Makefile.in index 3e6c855..599f33c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -20,16 +20,17 @@ 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 \ 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 \ - 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 list.o CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \ common-channel.o common-chansession.o termcodes.o loginrec.o \ @@ -40,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 \ @@ -23,21 +23,34 @@ * SOFTWARE. */ #ifndef _AGENTFWD_H_ #define _AGENTFWD_H_ -#ifndef DISABLE_AGENTFWD #include "includes.h" #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. + * 10000 is arbitrary */ +#define MAX_AGENT_REPLY 10000 + +int svr_agentreq(struct ChanSess * chansess); +void svr_agentcleanup(struct ChanSess * chansess); +void svr_agentset(struct ChanSess *chansess); + +/* client functions */ +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); -int agentreq(struct ChanSess * chansess); -void agentsetauth(struct ChanSess *chansess); -void agentcleanup(struct ChanSess * chansess); -void agentset(struct ChanSess *chansess); #ifdef __hpux #define seteuid(a) setresuid(-1, (a), -1) #define setegid(a) setresgid(-1, (a), -1) #endif -#endif /* DROPBEAR_AGENTFWD */ +extern const struct ChanType cli_chan_agent; + #endif /* _AGENTFWD_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; @@ -26,6 +26,7 @@ #define _AUTH_H_ #include "includes.h" +#include "signkey.h" #include "chansession.h" void svr_authinitialise(); @@ -73,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 */ @@ -97,7 +99,6 @@ char* getpass_or_cancel(char* prompt); * 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 @@ -120,19 +121,6 @@ struct AuthState { #ifdef ENABLE_SVR_PUBKEY_OPTIONS struct PubKeyOptions* pubkey_options; #endif - -}; - -struct SignKeyList; -/* A singly linked list of signing keys */ -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 - * the private key portion */ - }; #ifdef ENABLE_SVR_PUBKEY_OPTIONS @@ -145,7 +133,7 @@ struct PubKeyOptions { int no_pty_flag; /* "command=" option. */ unsigned char * forced_command; - + unsigned char * original_command; }; #endif @@ -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) { @@ -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); @@ -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..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; @@ -60,7 +64,7 @@ struct ChanSess { unsigned char x11singleconn; #endif -#ifndef DISABLE_AGENTFWD +#ifdef ENABLE_SVR_AGENTFWD struct Listener * agentlistener; char * agentfile; char * agentdir; @@ -81,6 +85,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 new file mode 100644 index 0000000..a212c3f --- /dev/null +++ b/cli-agentfwd.c @@ -0,0 +1,311 @@ +/* + * 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. */ + +#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" + +/* 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 = { + 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); + + if (fd < 0) { + dropbear_log(LOG_INFO, "Failed to connect to agent"); + } + + 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(); + if (cli_opts.agent_fd < 0) { + return SSH_OPEN_CONNECT_FAILED; + } + + setnonblocking(fd); + + ses.maxfd = MAX(ses.maxfd, fd); + + channel->readfd = fd; + channel->writefd = fd; + + // success + return 0; +} + +/* 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. +*/ +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 + data_len); + + 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); + if ((size_t)ret != payload->len) { + 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); + if (ret != 4) { + 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) + buf_free(payload); + + return inbuf; +} + +static void agent_get_key_list(m_list * ret_list) +{ + buffer * inbuf = NULL; + unsigned int num = 0; + unsigned char packet_type; + unsigned int i; + int ret; + + inbuf = agent_request(SSH2_AGENTC_REQUEST_IDENTITIES, NULL); + if (!inbuf) { + TRACE(("agent_request failed returning identities")) + goto out; + } + + /* The reply has a format of: + 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) { + goto out; + } + + num = buf_getint(inbuf); + for (i = 0; i < num; i++) { + sign_key * pubkey = NULL; + int key_type = DROPBEAR_SIGNKEY_ANY; + buffer * key_buf; + + /* each public key is encoded as a string */ + key_buf = buf_getstringbuf(inbuf); + pubkey = new_sign_key(); + 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; + + list_append(ret_list, pubkey); + + /* We'll ignore the comment for now. might want it later.*/ + buf_eatstring(inbuf); + } + +out: + if (inbuf) { + buf_free(inbuf); + inbuf = NULL; + } +} + +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 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) { + return; + } + + 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 @@ -91,7 +91,7 @@ void recv_msg_userauth_banner() { } } - printf("%s\n", banner); + fprintf(stderr, "%s\n", banner); out: m_free(banner); @@ -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 c16ef90..a57fd6b 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,30 +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); - - 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; @@ -80,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; @@ -90,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 @@ -114,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")) } @@ -126,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) { @@ -161,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 */ } @@ -169,20 +182,41 @@ 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")) - if (cli_opts.privkeys != NULL) { + if (!cli_opts.agent_keys_loaded) { + /* get the list of available keys from the agent */ + cli_load_agent_keys(cli_opts.privkeys); + cli_opts.agent_keys_loaded = 1; + } + + 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 { + /* 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/cli-chansession.c b/cli-chansession.c index dc8e641..49b4e06 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); } @@ -376,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) @@ -424,16 +429,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 @@ -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, ' '); @@ -327,4 +327,5 @@ out: if (line != NULL) { buf_free(line); } + m_free(fingerprint); } @@ -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) @@ -43,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; @@ -63,6 +63,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 { @@ -75,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; @@ -132,6 +126,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 +139,4 @@ static void cli_proxy_cmd(int *sock_in, int *sock_out) { *sock_in = *sock_out = -1; } } +#endif // ENABLE_CLI_PROXYCMD diff --git a/cli-runopts.c b/cli-runopts.c index da9dd1a..ddd52b5 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); @@ -66,6 +67,9 @@ static void printhelp() { #ifdef ENABLE_CLI_PUBKEY_AUTH "-i <identityfile> (multiple allowed)\n" #endif +#ifdef ENABLE_CLI_AGENTFWD + "-A Enable agent auth forwarding\n" +#endif #ifdef ENABLE_CLI_LOCALTCPFWD "-L <listenport:remotehost:remoteport> Local port forwarding\n" "-g Allow remote hosts to connect to forwarded ports\n" @@ -91,7 +95,6 @@ static void printhelp() { } void cli_getopts(int argc, char ** argv) { - unsigned int i, j; char ** next = 0; unsigned int cmdlen; @@ -112,6 +115,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]; @@ -125,18 +129,25 @@ 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; + cli_opts.agent_keys_loaded = 0; #endif #ifdef ENABLE_CLI_PROXYCMD cli_opts.proxycmd = NULL; #endif +#ifndef DISABLE_ZLIB + opts.enable_compress = 1; +#endif /* not yet opts.ipv4 = 1; opts.ipv6 = 1; @@ -158,7 +169,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; } @@ -166,7 +177,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; } @@ -266,6 +277,11 @@ void cli_getopts(int argc, char ** argv) { case 'I': next = &idle_timeout_arg; break; +#ifdef ENABLE_CLI_AGENTFWD + case 'A': + cli_opts.agent_fwd = 1; + break; +#endif #ifdef DEBUG_TRACE case 'v': debug_trace = 1; @@ -304,12 +320,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 +350,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); } @@ -369,15 +381,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 @@ -385,36 +401,73 @@ 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 static void loadidentityfile(const char* filename) { - - struct SignKeyList * nextkey; sign_key *key; int keytype; 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->next = cli_opts.privkeys; - nextkey->type = keytype; - 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 #ifdef ENABLE_CLI_MULTIHOP +static char* +multihop_passthrough_args() { + char *ret; + int total; + unsigned int len = 0; + m_list_elem *iter; + /* Fill out -i and -W options that make sense for all + * the intermediate processes */ + for (iter = cli_opts.privkeys->first; iter; iter = iter->next) + { + sign_key * key = (sign_key*)iter->item; + len += 3 + strlen(key->filename); + } + len += 20; // space for -W <size>, 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 (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", key->filename); + dropbear_assert((unsigned int)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,7 +482,8 @@ static void loadidentityfile(const char* filename) { */ static void parse_multihop_hostname(const char* orighostarg, const char* argv0) { char *userhostarg = NULL; - char *last_hop = NULL;; + char *hostbuf = NULL; + char *last_hop = NULL; char *remainder = NULL; /* both scp and rsync parse a user@host argument @@ -441,11 +495,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 +518,28 @@ 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); +#ifndef DISABLE_ZLIB + /* The stream will be incompressible since it's encrypted. */ + opts.enable_compress = 0; +#endif + m_free(passthrough_args); } + m_free(hostbuf); } #endif /* !ENABLE_CLI_MULTIHOP */ @@ -566,14 +630,14 @@ static void fill_own_user() { #ifdef ENABLE_CLI_ANYTCPFWD /* 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, struct TCPFwdList** fwdlist) { +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 * connectaddr = NULL; char * connectport = NULL; - struct TCPFwdList* newfwd = NULL; + struct TCPFwdEntry* newfwd = NULL; char * str = NULL; TRACE(("enter addforward")) @@ -618,7 +682,9 @@ static void addforward(const char* origstr, struct TCPFwdList** fwdlist) { connectport = part3; } - 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 */ @@ -646,8 +712,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-session.c b/cli-session.c index c153f4f..96a0929 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(); @@ -75,16 +76,19 @@ 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 */ }; -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); @@ -231,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/cli-tcpfwd.c b/cli-tcpfwd.c index 0751fb2..63eb70e 100644 --- a/cli-tcpfwd.c +++ b/cli-tcpfwd.c @@ -61,30 +61,25 @@ 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) { + for (iter = cli_opts.localfwds->first; iter; iter = iter->next) { + struct TCPFwdEntry * fwd = (struct TCPFwdEntry*)iter->item; ret = cli_localtcp( - cli_opts.localfwds->listenaddr, - cli_opts.localfwds->listenport, - cli_opts.localfwds->connectaddr, - cli_opts.localfwds->connectport); + fwd->listenaddr, + fwd->listenport, + fwd->connectaddr, + fwd->connectport); if (ret == DROPBEAR_FAILURE) { - dropbear_log(LOG_WARNING, "Failed local port forward %d:%s:%d", - cli_opts.localfwds->listenaddr, - cli_opts.localfwds->listenport, - cli_opts.localfwds->connectaddr, - cli_opts.localfwds->connectport); - } - - cli_opts.localfwds = cli_opts.localfwds->next; + dropbear_log(LOG_WARNING, "Failed local port forward %s:%d:%s:%d", + fwd->listenaddr, + fwd->listenport, + fwd->connectaddr, + fwd->connectport); + } } TRACE(("leave setup_localtcp")) @@ -156,63 +151,49 @@ static void send_msg_global_request_remotetcp(const char *addr, 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")) - } - - iter = cli_opts.remotefwds; - - while (iter != NULL) { - if (!iter->listenaddr) + for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) { + struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item; + if (!fwd->listenaddr) { // we store the addresses so that we can compare them // when the server sends them back if (opts.listen_fwd_all) { - iter->listenaddr = m_strdup(""); + fwd->listenaddr = m_strdup(""); } else { - iter->listenaddr = m_strdup("localhost"); + fwd->listenaddr = m_strdup("localhost"); } } - send_msg_global_request_remotetcp(iter->listenaddr, iter->listenport); - iter = iter->next; + send_msg_global_request_remotetcp(fwd->listenaddr, fwd->listenport); } + TRACE(("leave setup_remotetcp")) } @@ -220,7 +201,8 @@ static int newtcpforwarded(struct Channel * channel) { char *origaddr = NULL; 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; @@ -229,14 +211,12 @@ 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 - && (strcmp(origaddr, iter->listenaddr) == 0)) { + for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) { + fwd = (struct TCPFwdEntry*)iter->item; + if (origport == fwd->listenport + && (strcmp(origaddr, fwd->listenaddr) == 0)) { break; } - iter = iter->next; } if (iter == NULL) { @@ -247,8 +227,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; @@ -265,7 +245,7 @@ static int newtcpforwarded(struct Channel * channel) { err = SSH_OPEN_IN_PROGRESS; out: - m_free(origaddr); + m_free(origaddr); TRACE(("leave newtcpdirect: err %d", err)) return err; } diff --git a/common-algo.c b/common-algo.c index 8863367..7d694d2 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; } @@ -166,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 c62516c..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(); } @@ -272,8 +281,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 +290,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 +301,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 +345,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 +361,49 @@ 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 (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"); } } 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 */ @@ -666,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; @@ -674,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; @@ -698,36 +711,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..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); @@ -71,6 +69,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 */ @@ -78,7 +79,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 +95,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/configure.in b/configure.in index 52a75e0..97ce251 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, @@ -145,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) @@ -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 @@ -295,6 +295,28 @@ int dropbear_listen(const char* address, const char* port, return nsock; } +/* Connect to a given unix socket. The socket is blocking */ +#ifdef ENABLE_CONNECT_UNIX +int connect_unix(const char* path) { + struct sockaddr_un addr; + int fd = -1; + + 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 + /* 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 */ @@ -341,15 +363,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) { @@ -525,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 @@ -550,67 +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 (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 (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)); + } } - 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); - } -#ifdef AF_INET6 - if (addr->ss_family == AF_INET6) { - len = sizeof(struct sockaddr_in6); + if (ret_host) { + *ret_host = m_strdup(host); } -#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); + if (ret_port) { + *ret_port = m_strdup(serv); } - - return m_strdup(hbuf); } #ifdef DEBUG_TRACE @@ -46,15 +46,20 @@ 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, int *writefd, int *readfd, int *errfd, pid_t *pid); void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell); +#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); int buf_readfile(buffer* buf, const char* filename); int buf_getline(buffer * line, FILE * authfile); @@ -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 @@ -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 @@ -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 "<remote_ip> <remote_port> <local_ip> <local_port>". + +.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 @@ -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 @@ -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 @@ -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 @@ -64,7 +65,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 /* Note: Both ENABLE_CLI_PROXYCMD and ENABLE_CLI_NETCAT must be set to @@ -85,7 +87,8 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */ #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 @@ -128,6 +131,21 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */ * 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 @@ -154,7 +172,8 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */ /*#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 @@ -220,7 +239,7 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */ /* 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 @@ -246,13 +265,19 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */ 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 */ @@ -35,9 +35,11 @@ #include "auth.h" #include "channel.h" -static void read_packet_init(); -static void writemac(buffer * outputbuffer, buffer * clearwritebuf); -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,60 +152,61 @@ 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; - unsigned char blocksize; - unsigned char macsize; + int slen; + unsigned int len; + unsigned int blocksize; + unsigned int 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; /* 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; + return DROPBEAR_FAILURE; } 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; + 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 +215,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,69 +232,58 @@ 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 */ - 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); + /* decrypt it in-place */ + len = ses.readbuf->len - macsize - ses.readbuf->pos; + if (ses.keys->recv.crypt_mode->decrypt( + buf_getptr(ses.readbuf, len), + buf_getwriteptr(ses.readbuf, len), + len, + &ses.keys->recv.cipher_state) != CRYPT_OK) { + dropbear_exit("error decrypting"); } + buf_incrpos(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 - macsize; 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++; @@ -297,49 +291,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"); - } - - buf_setpos(sourcebuf, 0); - len = sourcebuf->len; - if (hmac_process(&hmac, buf_getptr(sourcebuf, len), len) != CRYPT_OK) { - dropbear_exit("HMAC error"); - } + mac_size = ses.keys->trans.algo_mac->hashsize; + contents_len = ses.readbuf->len - mac_size; - 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; @@ -354,7 +321,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; @@ -450,11 +417,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 clear_len; + unsigned int len, encrypt_buf_size; + unsigned char mac_bytes[MAX_MAC_LEN]; type = ses.writepayload->data[0]; TRACE(("enter encrypt_packet()")) @@ -468,34 +436,36 @@ void encrypt_packet() { return; } - blocksize = ses.keys->trans_algo_crypt->blocksize; - macsize = 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 * multiple, then add another blocksize (or MIN_PACKET_LEN) */ - clear_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 - clear_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(clear_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 */ @@ -504,53 +474,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); - - /* 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); - - /* 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); - } + buf_setpos(writebuf, writebuf->len); + buf_incrlen(writebuf, padlen); + genrandom(buf_getptr(writebuf, padlen), padlen); - /* now add a hmac and we're done */ - writemac(writebuf, clearwritebuf); + make_mac(ses.transseq, &ses.keys->trans, writebuf, writebuf->len, mac_bytes); - /* clearwritebuf is finished with */ - buf_free(clearwritebuf); - clearwritebuf = NULL; + /* 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(writebuf, len), + buf_getwriteptr(writebuf, len), + len, + &ses.keys->trans.cipher_state) != CRYPT_OK) { + dropbear_exit("error encrypting"); + } + buf_incrpos(writebuf, len); + + /* stick the MAC on it */ + buf_putbytes(writebuf, mac_bytes, mac_size); - /* 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); @@ -563,47 +525,43 @@ 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(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 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 (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"); } - 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")) } @@ -620,29 +578,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(), @@ -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_ */ @@ -69,12 +69,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"); } @@ -37,8 +37,16 @@ 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; + +#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; @@ -112,13 +120,20 @@ 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; + 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 @@ -128,7 +143,6 @@ typedef struct cli_runopts { #ifdef ENABLE_CLI_PROXYCMD char *proxycmd; #endif - } cli_runopts; extern cli_runopts cli_opts; @@ -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) @@ -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,51 +51,45 @@ 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); -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; @@ -116,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() */ @@ -128,8 +120,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 */ @@ -169,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)*/ @@ -220,6 +214,13 @@ 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 + }; typedef enum { @@ -268,7 +269,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 @@ -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,8 @@ int signkey_type_from_name(const char* name, int namelen) { } #endif + TRACE(("signkey_type_from_name unexpected key type.")) + return DROPBEAR_SIGNKEY_NONE; } @@ -101,8 +105,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 +262,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 +367,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 +382,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); @@ -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 @@ -105,3 +105,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 87a1078..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; @@ -181,7 +185,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, @@ -194,7 +198,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 { @@ -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); */ 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 23dad8c..5ecc57f 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 */ @@ -250,6 +251,14 @@ static int newchansess(struct Channel *channel) { } +static struct logininfo* +chansess_login_alloc(struct ChanSess *chansess) { + struct logininfo * li; + li = login_alloc_entry(chansess->pid, ses.authstate.username, + svr_ses.remotehost, chansess->tty); + return li; +} + /* clean a session channel */ static void closechansess(struct Channel *channel) { @@ -273,8 +282,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); @@ -287,7 +295,7 @@ static void closechansess(struct Channel *channel) { #endif #ifndef DISABLE_AGENTFWD - agentcleanup(chansess); + svr_agentcleanup(chansess); #endif /* clear child pid entries */ @@ -346,7 +354,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); @@ -570,6 +578,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. @@ -589,9 +612,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 +636,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) { @@ -627,6 +650,12 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess, } #endif + /* 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 */ ret = noptycommand(channel, chansess); @@ -635,6 +664,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); } @@ -736,13 +769,10 @@ 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); - m_free(chansess->tty); - #ifdef DO_MOTD if (svr_opts.domotd) { /* don't show the motd if ~/.hushlogin exists */ @@ -883,6 +913,22 @@ static void execchild(void *user_data) { addnewvar("TERM", chansess->term); } + if (chansess->tty) { + 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 && + 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"); @@ -894,7 +940,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()); @@ -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 */ } @@ -133,7 +127,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); @@ -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..8a3396a 100644 --- a/svr-runopts.c +++ b/svr-runopts.c @@ -125,6 +125,9 @@ void svr_getopts(int argc, char ** argv) { #ifdef ENABLE_SVR_REMOTETCPFWD svr_opts.noremotetcp = 0; #endif +#ifndef DISABLE_ZLIB + opts.enable_compress = 1; +#endif /* not yet opts.ipv4 = 1; opts.ipv6 = 1; @@ -296,15 +299,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 eaccfe5..da49e1a 100644 --- a/svr-session.c +++ b/svr-session.c @@ -74,23 +74,36 @@ 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_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); + + get_socket_address(ses.sock_in, NULL, NULL, + &svr_ses.remotehost, NULL, 1); + /* set up messages etc */ ses.remoteclosed = svr_remoteclosed; @@ -144,11 +157,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); diff --git a/sysoptions.h b/sysoptions.h index c98e1ec..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 @@ -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 @@ -25,6 +25,7 @@ #define _TCPFWD_H #include "channel.h" +#include "list.h" struct TCPListener { @@ -43,17 +44,14 @@ 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; 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 */ - struct TCPFwdList * next; - }; /* Server */ @@ -71,5 +69,4 @@ void cli_recv_msg_request_failure(); /* Common */ int listen_tcpfwd(struct TCPListener* tcpinfo); - #endif |