Mercurial > dropbear
changeset 681:a4b7627b3157 insecure-nocrypto
Update insecure-nocrypto to current head
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Wed, 16 May 2012 22:54:51 +0800 |
parents | bd4b5d7886e5 (diff) 0129fd8ccc71 (current diff) |
children | c37857676924 |
files | cli-auth.c common-algo.c common-kex.c options.h |
diffstat | 90 files changed, 2418 insertions(+), 1197 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgsigs Wed May 16 22:54:51 2012 +0800 @@ -0,0 +1,3 @@ +aa2f51a6b81d33de5e9898a7f27c792a173d9b26 0 iD8DBQBOuADmjPn4sExkf7wRAv/fAJ9FJFvjDoF+wd1ipDx1wkzdeBQNqgCgykUrSbXv76FBbxKntVbk9oS3GjI= +3f12086c2ef2b9ffe36a822fdb3ff647fcec1831 0 iD8DBQBOuSlQjPn4sExkf7wRAvkbAKCgE1e8xEMQ16CGeoywhIQ0QR4eNgCfZdYYlzjb/+521Uvh5/7FRYEmrho= +85f835f2fe0ac2c503c50a414de127222fb0a57c 0 iD8DBQBPRkMUjPn4sExkf7wRAvM4AJ9mw2OAkyjhSbamM1MizlEJUX18HACgoFKQkYf6BnYxN34Nv2HhM0cmzUc=
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgtags Wed May 16 22:54:51 2012 +0800 @@ -0,0 +1,33 @@ +03f65e461915a940939e4cc689fc89721ffc40de DROPBEAR_0.48.1 +0f967bfef5cd0056b7ec60e2305d917e51cbf30d DROPBEAR_0.44 +170329dc8ce5dfcf6298e1ad6699f109bf78e73d DROPBEAR_0.51 +1dbd2473482f320ea59f76ce961385cb3a0150a9 DROPBEAR_0.46 +2098857ab826dd42ae05a9a22c3ce2cc835b9844 DROPBEAR_0.45 +36160290a1b27451178be36752ed038840f59cdd LTC_DB_0.46 +39d5d58461d6e93337636e69d4cdf184a09c8d24 LTC_1.05 +55a99934db873be2e63b5968fb6532e5d9bd02e4 DROPBEAR_0.48 +59400faa4b44708c5d0b595e81193bc621e752d3 libtomcrypt-1.05 +66087d87c3555c78b47cf01f32bb5a32054c3ceb DROPBEAR_0.44test4 +677843bfa734238a67636b461a02c110c462ffaf DROPBEAR_0.44test1 +7faae8f46238e23975430876547b8950b4e75481 t:ltc-0.95-orig +8220862baae829ebc762587b99c662480d57bb23 DROPBEAR_0.53 +86e0b50a9b588239c3fc9cc9cfe255ef586df17b ltm-0.30-orig +88e0a1ad951add46b795511dc2698e36b4aee922 DROPBEAR_0.44test3 +8e94663164c6e106ccc5c9e997dedf6e04d77dd2 LTM_DB_0.44 +91fbc376f01084037cd5f6a5bf2e2db4903e8e99 libtommath-0.35 +97db060d0ef5f8cf8e67eb602ef037055a185ca9 libtommath-0.40 +aa2f51a6b81d33de5e9898a7f27c792a173d9b26 DROPBEAR_0.53.1 +ab370c629d363f8c9a3eca512bfa86e362034654 DROPBEAR_0.49 +c2ac796b130eeb6fa840873d8c230544c8ec7e4b DROPBEAR_0.44test2 +cd1143579f00b0248c79f63ca70efee4a35a57e8 LTC_DB_0.44 +ce104c8b0be1ff3f2c2590b7cdc3fd6870c865cd DROPBEAR_0.52 +d5faf4814ddbc5abd9e209409bb9e7a4686c8cd7 libtomcrypt-1.16 +d7da3b1e15401eb234ec866d5eac992fc4cd5878 t:ltc-0.95-db-merge1 +d8254fc979e99560c93ca2cece77a6df31927ea5 LTM_0.35 +e109027b9edfb02f0bdf96ec45bb1cd9ad41e7da LTM_DB_0.46 +e109027b9edfb02f0bdf96ec45bb1cd9ad41e7da LTM_DB_0.47 +e37b160c414cab6466622f63b0c4dcbf6ebc47a9 DROPBEAR_0.47 +e430a26064ee86ab79aef372118d6d03b2441996 DROPBEAR_0.50 +e5d119ea4c63656bc54ecfd865d04591ac2ed225 LTC_DB_0.47 +3f12086c2ef2b9ffe36a822fdb3ff647fcec1831 DROPBEAR_2011.54 +d354464b2aa6f6ba0bf44d43bcae5aa798435393 DROPBEAR_2012.55
--- a/CHANGES Thu Nov 06 13:33:06 2008 +0000 +++ b/CHANGES Wed May 16 22:54:51 2012 +0800 @@ -1,4 +1,114 @@ -0.52 +2012.55 - Wednesday 22 February 2012 + +- Security: Fix use-after-free bug that could be triggered if command="..." + authorized_keys restrictions are used. Could allow arbitrary code execution + or bypass of the command="..." restriction to an authenticated user. + This bug affects releases 0.52 onwards. Ref CVE-2012-0920. + Thanks to Danny Fullerton of Mantor Organization for reporting + the bug. + +- Compile fix, only apply IPV6 socket options if they are available in headers + Thanks to Gustavo Zacarias for the patch + +- Overwrite session key memory on exit + +- Fix minor memory leak in unusual PAM authentication configurations. + Thanks to Stathis Voukelatos + +- Other small code cleanups + +2011.54 - Tuesday 8 November 2011 + +- Building statically works again, broke in 0.53 and 0.53.1 + +- Fix crash when forwarding with -R + +- Fixed various leaks found by Klocwork analysis software, thanks to them for + running it + +- Set IPTOS_LOWDELAY for IPv6, thanks to Dave Taht + +- Bind to sockets with IPV6_V6ONLY so that it works properly on systems + regardless of the system-wide setting + +- Added ALLOW_BLANK_PASSWORD option. Dropbear also now allows public key logins + to accounts with a blank password. Thanks to Rob Landley + +- Fixed case where "-K 1" keepalive for dbclient would cause a SSH_MSG_IGNORE + packet to be sent + +- Avoid some memory allocations in big number maths routines, improves + performance slightly + +- Fix symlink target for installdropbearmulti with DESTDIR set, thanks to + Scottie Shore + +- When requesting server allocated remote ports (-R 0:host:port) print a + message informing what the port is, thanks to Ali Onur Uyar. + +- New version numbering scheme. + +Source repository has now migrated to Mercurial at +https://secure.ucc.asn.au/hg/dropbear/graph/default + +0.53.1 - Wednesday 2 March 2011 + +- -lcrypt needs to be before object files for static linking + +- Compile fix when both client and agent forwarding are disabled + +- Fix DROPBEAR_PRNGD_SOCKET mode + +- Don't allow setting zlib memLevel since it seems buggy + +0.53 - Thurs 24 February 2011 + +- Various performance/memory use improvements + +- Client agent forwarding now works, using OpenSSH's ssh-agent + +- Improve robustness of client multihop mode + +- Fix a prime generation bug in bundled libtommath. This is unlikely to have + generated any bad keys in the wild. + See + https://bugzilla.redhat.com/show_bug.cgi?id=615088 + http://bugs.gentoo.org/show_bug.cgi?id=328383 + http://bugs.gentoo.org/show_bug.cgi?id=328409 + +- Attempt to build against system libtomcrypt/libtommath if available. This + can be disabled with ./configure --enable-bundled-libtom + +- Make -K (keepalive) and -I (idle timeout) work together sensibly in the client. + The idle timeout is no longer reset by SSH_MSG_IGNORE packets. + +- Add diffie-hellman-group14-sha1 key exchange method + +- Compile fix if ENABLE_CLI_PROXYCMD is disabled + +- /usr/bin/X11/xauth is now the default path + +- Client remote forward (-L/-R) arguments now accept a listen address + +- In uClinux avoid trashing the parent process when a session exits + +- Blowfish is now disabled by default since it has large memory usage + +- Add option to change zlib windowbits/memlevel. Use less memory by default + +- DROPBEAR_SMALL_CODE is now disabled by default + +- SSH_ORIGINAL_COMMAND environment variable is set by the server when an + authorized_keys command is specified. + +- Set SSH_TTY and SSH_CONNECTION environment variables in the server + +- Client banner is now printed to standard error rather than standard output + +- Capitalisation in many log messages has been made consistent. This may affect + scripts that parse logfiles. + +0.52 - Wed 12 November 2008 - Add "netcat-alike" option (-B) to dbclient, allowing Dropbear to tunnel standard input/output to a TCP port-forwarded remote host. @@ -12,13 +122,19 @@ - Combine netcat-alike and proxy support to allow "multihop" connections, with comma-separated host syntax. Allows running + dbclient user1@host1,user2@host2,user3@host3 + to end up at host3 via the other two, using SSH TCP forwarding. It's a bit like onion-routing. All connections are established from the local machine. The comma-separated syntax can also be used for scp/rsync, eg - scp -S dbclient matt@martello,root@wrt,canyons:/tmp/dump . + + rsync -a -e dbclient m@gateway,m2@host,martello:/home/matt/ ~/backup/ + to bounce through a few hosts. +- Add -I "idle timeout" option (contributed by Farrell Aultman) + - Allow restrictions on authorized_keys logins such as restricting commands to be run etc. This is a subset of those allowed by OpenSSH, doesn't yet allow restricting source host.
--- a/Makefile.in Thu Nov 06 13:33:06 2008 +0000 +++ b/Makefile.in Wed May 16 22:54:51 2012 +0800 @@ -16,11 +16,17 @@ LTC=libtomcrypt/libtomcrypt.a LTM=libtommath/libtommath.a +ifeq (@BUNDLED_LIBTOM@, 1) +LIBTOM_DEPS=$(LTC) $(LTM) +CFLAGS+=-I$(srcdir)/libtomcrypt/src/headers/ +LIBS+=$(LTC) $(LTM) +endif + 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 \ @@ -29,7 +35,8 @@ 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 +47,7 @@ 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 \ @@ -49,7 +56,7 @@ loginrec.h atomicio.h x11fwd.h agentfwd.h tcpfwd.h compat.h \ listener.h fake-rfc2553.h -dropbearobjs=$(COMMONOBJS) $(CLISVROBJS) $(SVROBJS) +dropbearobjs=$(COMMONOBJS) $(CLISVROBJS) $(SVROBJS) @CRYPTLIB@ dbclientobjs=$(COMMONOBJS) $(CLISVROBJS) $(CLIOBJS) dropbearkeyobjs=$(COMMONOBJS) $(KEYOBJS) dropbearconvertobjs=$(COMMONOBJS) $(CONVERTOBJS) @@ -69,8 +76,8 @@ STRIP=@STRIP@ INSTALL=@INSTALL@ CPPFLAGS=@CPPFLAGS@ -CFLAGS=-I. -I$(srcdir) -I$(srcdir)/libtomcrypt/src/headers/ $(CPPFLAGS) @CFLAGS@ -LIBS=$(LTC) $(LTM) @LIBS@ +CFLAGS+=-I. -I$(srcdir) $(CPPFLAGS) @CFLAGS@ +LIBS+=@LIBS@ LDFLAGS=@LDFLAGS@ EXEEXT=@EXEEXT@ @@ -106,10 +113,6 @@ CFLAGS+=-DPROGRESS_METER endif -#%: $(HEADERS) -#%: $(HEADERS) Makefile -# TODO - all: $(TARGETS) strip: $(TARGETS) @@ -126,12 +129,14 @@ -chgrp 0 $(DESTDIR)$(bindir)/dropbearmulti$(EXEEXT) insmultidropbear: dropbearmulti + $(INSTALL) -d -m 755 $(DESTDIR)$(sbindir) -rm -f $(DESTDIR)$(sbindir)/dropbear$(EXEEXT) - -ln -s $(DESTDIR)$(bindir)/dropbearmulti$(EXEEXT) $(DESTDIR)$(sbindir)/dropbear$(EXEEXT) + -ln -s $(bindir)/dropbearmulti$(EXEEXT) $(DESTDIR)$(sbindir)/dropbear$(EXEEXT) insmulti%: dropbearmulti + $(INSTALL) -d -m 755 $(DESTDIR)$(bindir) -rm -f $(DESTDIR)$(bindir)/$*$(EXEEXT) - -ln -s $(DESTDIR)$(bindir)/dropbearmulti$(EXEEXT) $(DESTDIR)$(bindir)/$*$(EXEEXT) + -ln -s $(bindir)/dropbearmulti$(EXEEXT) $(DESTDIR)$(bindir)/$*$(EXEEXT) # dropbear should go in sbin, so it needs a seperate rule inst_dropbear: dropbear @@ -153,8 +158,7 @@ dropbearkey: $(dropbearkeyobjs) dropbearconvert: $(dropbearconvertobjs) -dropbear dbclient dropbearkey dropbearconvert: $(HEADERS) $(LTC) $(LTM) \ - Makefile +dropbear dbclient dropbearkey dropbearconvert: $(HEADERS) $(LIBTOM_DEPS) Makefile $(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBS) # scp doesn't use the libs so is special. @@ -165,13 +169,13 @@ # multi-binary compilation. MULTIOBJS= ifeq ($(MULTI),1) - MULTIOBJS=dbmulti.o $(sort $(foreach prog, $(PROGRAMS), $($(prog)objs))) + MULTIOBJS=dbmulti.o $(sort $(foreach prog, $(PROGRAMS), $($(prog)objs))) @CRYPTLIB@ CFLAGS+=$(addprefix -DDBMULTI_, $(PROGRAMS)) -DDROPBEAR_MULTI endif dropbearmulti: multilink -multibinary: $(HEADERS) $(MULTIOBJS) $(LTC) $(LTM) Makefile +multibinary: $(HEADERS) $(MULTIOBJS) $(LIBTOM_DEPS) Makefile $(CC) $(LDFLAGS) -o dropbearmulti$(EXEEXT) $(MULTIOBJS) $(LIBS) multilink: multibinary $(addprefix link, $(PROGRAMS))
--- a/agentfwd.h Thu Nov 06 13:33:06 2008 +0000 +++ b/agentfwd.h Wed May 16 22:54:51 2012 +0800 @@ -23,21 +23,41 @@ * 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" -int agentreq(struct ChanSess * chansess); -void agentsetauth(struct ChanSess *chansess); -void agentcleanup(struct ChanSess * chansess); -void agentset(struct ChanSess *chansess); +#ifdef ENABLE_CLI_AGENTFWD + +/* 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 + +/* 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); #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 /* ENABLE_CLI_AGENTFWD */ + +#ifdef ENABLE_SVR_AGENTFWD + +int svr_agentreq(struct ChanSess * chansess); +void svr_agentcleanup(struct ChanSess * chansess); +void svr_agentset(struct ChanSess *chansess); + +#endif /* ENABLE_SVR_AGENTFWD */ + #endif /* _AGENTFWD_H_ */
--- a/algo.h Thu Nov 06 13:33:06 2008 +0000 +++ b/algo.h Wed May 16 22:54:51 2012 +0800 @@ -50,7 +50,8 @@ 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;
--- a/auth.h Thu Nov 06 13:33:06 2008 +0000 +++ b/auth.h Wed May 16 22:54:51 2012 +0800 @@ -26,6 +26,7 @@ #define _AUTH_H_ #include "includes.h" +#include "signkey.h" #include "chansession.h" void svr_authinitialise(); @@ -73,6 +74,7 @@ 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 @@ * 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 @@ #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,6 @@ int no_pty_flag; /* "command=" option. */ unsigned char * forced_command; - }; #endif
--- a/bignum.c Thu Nov 06 13:33:06 2008 +0000 +++ b/bignum.c Wed May 16 22:54:51 2012 +0800 @@ -31,7 +31,7 @@ void m_mp_init(mp_int *mp) { if (mp_init(mp) != MP_OKAY) { - dropbear_exit("mem alloc error"); + dropbear_exit("Mem alloc error"); } } @@ -45,7 +45,7 @@ va_start(args, mp); /* init args to next argument from caller */ while (cur_arg != NULL) { if (mp_init(cur_arg) != MP_OKAY) { - dropbear_exit("mem alloc error"); + dropbear_exit("Mem alloc error"); } cur_arg = va_arg(args, mp_int*); } @@ -55,7 +55,7 @@ void bytes_to_mp(mp_int *mp, const unsigned char* bytes, unsigned int len) { if (mp_read_unsigned_bin(mp, (unsigned char*)bytes, len) != MP_OKAY) { - dropbear_exit("mem alloc error"); + dropbear_exit("Mem alloc error"); } }
--- a/buffer.c Thu Nov 06 13:33:06 2008 +0000 +++ b/buffer.c Wed May 16 22:54:51 2012 +0800 @@ -106,7 +106,7 @@ /* Set the length of the buffer */ void buf_setlen(buffer* buf, unsigned int len) { if (len > buf->size) { - dropbear_exit("bad buf_setlen"); + dropbear_exit("Bad buf_setlen"); } buf->len = len; } @@ -114,7 +114,7 @@ /* Increment the length of the buffer */ void buf_incrlen(buffer* buf, unsigned int incr) { if (incr > BUF_MAX_INCR || buf->len + incr > buf->size) { - dropbear_exit("bad buf_incrlen"); + dropbear_exit("Bad buf_incrlen"); } buf->len += incr; } @@ -122,7 +122,7 @@ void buf_setpos(buffer* buf, unsigned int pos) { if (pos > buf->len) { - dropbear_exit("bad buf_setpos"); + dropbear_exit("Bad buf_setpos"); } buf->pos = pos; } @@ -130,7 +130,7 @@ /* increment the postion by incr, increasing the buffer length if required */ void buf_incrwritepos(buffer* buf, unsigned int incr) { if (incr > BUF_MAX_INCR || buf->pos + incr > buf->size) { - dropbear_exit("bad buf_incrwritepos"); + dropbear_exit("Bad buf_incrwritepos"); } buf->pos += incr; if (buf->pos > buf->len) { @@ -144,7 +144,7 @@ if (incr > BUF_MAX_INCR || (unsigned int)((int)buf->pos + incr) > buf->len || ((int)buf->pos + incr) < 0) { - dropbear_exit("bad buf_incrpos"); + dropbear_exit("Bad buf_incrpos"); } buf->pos += incr; } @@ -155,7 +155,7 @@ /* This check is really just ==, but the >= allows us to check for the * bad case of pos > len, which should _never_ happen. */ if (buf->pos >= buf->len) { - dropbear_exit("bad buf_getbyte"); + dropbear_exit("Bad buf_getbyte"); } return buf->data[buf->pos++]; } @@ -185,7 +185,7 @@ unsigned char* buf_getptr(buffer* buf, unsigned int len) { if (buf->pos + len > buf->len) { - dropbear_exit("bad buf_getptr"); + dropbear_exit("Bad buf_getptr"); } return &buf->data[buf->pos]; } @@ -195,7 +195,7 @@ unsigned char* buf_getwriteptr(buffer* buf, unsigned int len) { if (buf->pos + len > buf->size) { - dropbear_exit("bad buf_getwriteptr"); + dropbear_exit("Bad buf_getwriteptr"); } return &buf->data[buf->pos]; } @@ -209,7 +209,7 @@ unsigned char* ret; len = buf_getint(buf); if (len > MAX_STRING_LEN) { - dropbear_exit("string too long"); + dropbear_exit("String too long"); } if (retlen != NULL) { @@ -223,6 +223,20 @@ 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) {
--- a/buffer.h Thu Nov 06 13:33:06 2008 +0000 +++ b/buffer.h Wed May 16 22:54:51 2012 +0800 @@ -55,6 +55,7 @@ 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);
--- a/channel.h Thu Nov 06 13:33:06 2008 +0000 +++ b/channel.h Wed May 16 22:54:51 2012 +0800 @@ -58,7 +58,7 @@ 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 */ @@ -69,6 +69,10 @@ int sent_close, recv_close; int recv_eof, sent_eof; + /* Set after running the ChanType-specific close hander + * to ensure we don't run it twice (nor type->checkclose()). */ + int close_handler_done; + int initconn; /* used for TCP forwarding, whether the channel has been fully initialised */
--- a/chansession.h Thu Nov 06 13:33:06 2008 +0000 +++ b/chansession.h Wed May 16 22:54:51 2012 +0800 @@ -50,6 +50,10 @@ /* 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,11 +64,15 @@ unsigned char x11singleconn; #endif -#ifndef DISABLE_AGENTFWD +#ifdef ENABLE_SVR_AGENTFWD struct Listener * agentlistener; char * agentfile; char * agentdir; #endif + +#ifdef ENABLE_SVR_PUBKEY_OPTIONS + char *original_command; +#endif }; struct ChildPid { @@ -81,6 +89,7 @@ #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;
--- a/circbuffer.c Thu Nov 06 13:33:06 2008 +0000 +++ b/circbuffer.c Wed May 16 22:54:51 2012 +0800 @@ -33,7 +33,7 @@ circbuffer *cbuf = NULL; if (size > MAX_CBUF_SIZE) { - dropbear_exit("bad cbuf size"); + dropbear_exit("Bad cbuf size"); } cbuf = (circbuffer*)m_malloc(sizeof(circbuffer)); @@ -48,6 +48,7 @@ void cbuf_free(circbuffer * cbuf) { + m_burn(cbuf->data, cbuf->size); m_free(cbuf->data); m_free(cbuf); } @@ -101,7 +102,7 @@ unsigned char* cbuf_readptr(circbuffer *cbuf, unsigned int len) { if (len > cbuf_readlen(cbuf)) { - dropbear_exit("bad cbuf read"); + dropbear_exit("Bad cbuf read"); } return &cbuf->data[cbuf->readpos]; @@ -110,7 +111,7 @@ unsigned char* cbuf_writeptr(circbuffer *cbuf, unsigned int len) { if (len > cbuf_writelen(cbuf)) { - dropbear_exit("bad cbuf write"); + dropbear_exit("Bad cbuf write"); } return &cbuf->data[cbuf->writepos]; @@ -118,7 +119,7 @@ void cbuf_incrwrite(circbuffer *cbuf, unsigned int len) { if (len > cbuf_writelen(cbuf)) { - dropbear_exit("bad cbuf write"); + dropbear_exit("Bad cbuf write"); } cbuf->used += len; @@ -129,7 +130,7 @@ void cbuf_incrread(circbuffer *cbuf, unsigned int len) { if (len > cbuf_readlen(cbuf)) { - dropbear_exit("bad cbuf read"); + dropbear_exit("Bad cbuf read"); } dropbear_assert(cbuf->used >= len);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cli-agentfwd.c Wed May 16 22:54:51 2012 +0800 @@ -0,0 +1,312 @@ +/* + * 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 */ + "[email protected]", + 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 (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, "[email protected]"); + /* 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 = NULL; + buffer *response = NULL; + unsigned int siglen; + int packet_type; + + /* Request format + byte SSH2_AGENTC_SIGN_REQUEST + string key_blob + string data + uint32 flags + */ + request_data = buf_new(MAX_PUBKEY_SIZE + len + 12); + buf_put_pub_key(request_data, key, key->type); + + buf_putstring(request_data, data, len); + buf_putint(request_data, 0); + + response = agent_request(SSH2_AGENTC_SIGN_REQUEST, 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); + goto cleanup; + +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"); + +cleanup: + if (request_data) { + buf_free(request_data); + } + if (response) { + buf_free(response); + } +} + +#endif
--- a/cli-algo.c Thu Nov 06 13:33:06 2008 +0000 +++ b/cli-algo.c Wed May 16 22:54:51 2012 +0800 @@ -67,7 +67,7 @@ remotealgos[count] = &algolist[i+1]; count++; } - if (count == MAX_PROPOSED_ALGO) { + if (count >= MAX_PROPOSED_ALGO) { break; } }
--- a/cli-auth.c Thu Nov 06 13:33:06 2008 +0000 +++ b/cli-auth.c Wed May 16 22:54:51 2012 +0800 @@ -91,7 +91,7 @@ } } - printf("%s\n", banner); + fprintf(stderr, "%s\n", banner); out: m_free(banner); @@ -234,6 +234,10 @@ 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() { @@ -253,7 +257,7 @@ #endif #ifdef ENABLE_CLI_INTERACT_AUTH - if (ses.keys->trans_algo_crypt->cipherdesc == NULL) { + if (ses.keys->trans.algo_crypt->cipherdesc == NULL) { fprintf(stderr, "Sorry, I won't let you use interactive auth unencrypted.\n"); } else if (!finished && ses.authstate.authtypes & AUTH_TYPE_INTERACT) { @@ -268,7 +272,7 @@ #endif #ifdef ENABLE_CLI_PASSWORD_AUTH - if (ses.keys->trans_algo_crypt->cipherdesc == NULL) { + if (ses.keys->trans.algo_crypt->cipherdesc == NULL) { fprintf(stderr, "Sorry, I won't let you use password auth unencrypted.\n"); } else if (!finished && ses.authstate.authtypes & AUTH_TYPE_PASSWORD) {
--- a/cli-authinteract.c Thu Nov 06 13:33:06 2008 +0000 +++ b/cli-authinteract.c Wed May 16 22:54:51 2012 +0800 @@ -131,6 +131,7 @@ response_len = strlen(response); buf_putstring(ses.writepayload, response, response_len); m_burn(response, response_len); + m_free(prompt); m_free(response); }
--- a/cli-authpubkey.c Thu Nov 06 13:33:06 2008 +0000 +++ b/cli-authpubkey.c Wed May 16 22:54:51 2012 +0800 @@ -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 @@ /* 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 @@ /* 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 @@ /* 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 @@ } 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,27 @@ 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) +{ +#ifdef ENABLE_CLI_AGENTFWD + 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 +#endif /* ENABLE_CLI_AGENTFWD */ + { + 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 +176,7 @@ 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 +184,43 @@ 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) { +#ifdef ENABLE_CLI_AGENTFWD + 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; + } +#endif + + 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 */
--- a/cli-chansession.c Thu Nov 06 13:33:06 2008 +0000 +++ b/cli-chansession.c Wed May 16 22:54:51 2012 +0800 @@ -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 start_channel_request(struct Channel *channel, +void cli_start_send_channel_request(struct Channel *channel, unsigned char *type) { CHECKCLEARTOWRITE(); @@ -287,7 +286,7 @@ 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); @@ -309,7 +308,7 @@ /* Set up a window-change handler */ if (signal(SIGWINCH, sigwinch_handler) == SIG_ERR) { - dropbear_exit("signal error"); + dropbear_exit("Signal error"); } TRACE(("leave send_chansess_pty_req")) } @@ -330,7 +329,7 @@ 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 @@ 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 @@ #ifdef ENABLE_CLI_NETCAT +static 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 @@ 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
--- a/cli-kex.c Thu Nov 06 13:33:06 2008 +0000 +++ b/cli-kex.c Wed May 16 22:54:51 2012 +0800 @@ -304,7 +304,7 @@ 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 @@ if (line != NULL) { buf_free(line); } + m_free(fingerprint); }
--- a/cli-main.c Thu Nov 06 13:33:06 2008 +0000 +++ b/cli-main.c Wed May 16 22:54:51 2012 +0800 @@ -29,10 +29,12 @@ #include "runopts.h" #include "session.h" -static void cli_dropbear_exit(int exitcode, const char* format, va_list param); +static void cli_dropbear_exit(int exitcode, const char* format, va_list param) ATTRIB_NORETURN; 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 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 @@ #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 @@ 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; @@ -94,11 +88,11 @@ char fmtbuf[300]; if (!sessinitdone) { - snprintf(fmtbuf, sizeof(fmtbuf), "exited: %s", + snprintf(fmtbuf, sizeof(fmtbuf), "Exited: %s", format); } else { snprintf(fmtbuf, sizeof(fmtbuf), - "connection to %s@%s:%s exited: %s", + "Connection to %s@%s:%s exited: %s", cli_opts.username, cli_opts.remotehost, cli_opts.remoteport, format); } @@ -132,6 +126,7 @@ 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 @@ *sock_in = *sock_out = -1; } } +#endif // ENABLE_CLI_PROXYCMD
--- a/cli-runopts.c Thu Nov 06 13:33:06 2008 +0000 +++ b/cli-runopts.c Wed May 16 22:54:51 2012 +0800 @@ -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 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,15 +67,19 @@ #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" + "-L <[listenaddress:]listenport:remotehost:remoteport> Local port forwarding\n" "-g Allow remote hosts to connect to forwarded ports\n" #endif #ifdef ENABLE_CLI_REMOTETCPFWD - "-R <listenport:remotehost:remoteport> Remote port forwarding\n" + "-R <[listenaddress:]listenport:remotehost:remoteport> Remote port forwarding\n" #endif "-W <receive_window_buffer> (default %d, larger may be faster, max 1MB)\n" "-K <keepalive> (0 is never, default %d)\n" + "-I <idle_timeout> (0 is never, default %d)\n" #ifdef ENABLE_CLI_NETCAT "-B <endhost:endport> Netcat-alike forwarding\n" #endif @@ -85,12 +90,11 @@ "-v verbose (compiled with DEBUG_TRACE)\n" #endif ,DROPBEAR_VERSION, cli_opts.progname, - DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE); + DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE, DEFAULT_IDLE_TIMEOUT); } void cli_getopts(int argc, char ** argv) { - unsigned int i, j; char ** next = 0; unsigned int cmdlen; @@ -110,6 +114,8 @@ 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]; @@ -123,18 +129,26 @@ 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_fd = -1; + 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; @@ -156,7 +170,7 @@ #ifdef ENABLE_CLI_REMOTETCPFWD if (nextisremote) { TRACE(("nextisremote true")) - addforward(argv[i], &cli_opts.remotefwds); + addforward(argv[i], cli_opts.remotefwds); nextisremote = 0; continue; } @@ -164,7 +178,7 @@ #ifdef ENABLE_CLI_LOCALTCPFWD if (nextislocal) { TRACE(("nextislocal true")) - addforward(argv[i], &cli_opts.localfwds); + addforward(argv[i], cli_opts.localfwds); nextislocal = 0; continue; } @@ -261,6 +275,14 @@ case 'K': next = &keepalive_arg; break; + 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; @@ -299,12 +321,8 @@ /* 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 @@ -333,7 +351,7 @@ /* And now a few sanity checks and setup */ - if (cli_opts.remotehost == NULL) { + if (host_arg == NULL) { printhelp(); exit(EXIT_FAILURE); } @@ -354,7 +372,7 @@ if (cli_opts.backgrounded && cli_opts.cmd == NULL && cli_opts.no_cmd == 0) { - dropbear_exit("command required for -f"); + dropbear_exit("Command required for -f"); } if (recv_window_arg) { @@ -364,9 +382,19 @@ } } 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) { + 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 @@ -374,36 +402,73 @@ 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 @@ -418,7 +483,8 @@ */ 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 @@ -430,11 +496,12 @@ && 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) { @@ -452,19 +519,28 @@ 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 */ @@ -553,14 +629,16 @@ } #ifdef ENABLE_CLI_ANYTCPFWD -/* Turn a "listenport:remoteaddr:remoteport" string into into a forwarding +/* Turn a "[listenaddr:]listenport:remoteaddr:remoteport" string into into a forwarding * set, and add it to the forwarding list */ -static void addforward(const char* origstr, 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; - char * connectaddr = NULL; - struct TCPFwdList* newfwd = NULL; + struct TCPFwdEntry* newfwd = NULL; char * str = NULL; TRACE(("enter addforward")) @@ -569,25 +647,43 @@ is never free()d. */ str = m_strdup(origstr); - listenport = str; + part1 = str; - connectaddr = strchr(str, ':'); - if (connectaddr == NULL) { - TRACE(("connectaddr == NULL")) + part2 = strchr(str, ':'); + if (part2 == NULL) { + TRACE(("part2 == NULL")) + goto fail; + } + *part2 = '\0'; + part2++; + + part3 = strchr(part2, ':'); + if (part3 == NULL) { + TRACE(("part3 == NULL")) goto fail; } - *connectaddr = '\0'; - connectaddr++; + *part3 = '\0'; + part3++; + + part4 = strchr(part3, ':'); + if (part4) { + *part4 = '\0'; + part4++; + } - connectport = strchr(connectaddr, ':'); - if (connectport == NULL) { - TRACE(("connectport == NULL")) - goto fail; + if (part4) { + listenaddr = part1; + listenport = part2; + connectaddr = part3; + connectport = part4; + } else { + listenaddr = NULL; + listenport = part1; + connectaddr = part2; + connectport = part3; } - *connectport = '\0'; - connectport++; - newfwd = (struct TCPFwdList*)m_malloc(sizeof(struct TCPFwdList)); + newfwd = m_malloc(sizeof(struct TCPFwdEntry)); /* Now we check the ports - note that the port ints are unsigned, * the check later only checks for >= MAX_PORT */ @@ -601,6 +697,7 @@ goto fail; } + newfwd->listenaddr = listenaddr; newfwd->connectaddr = connectaddr; if (newfwd->listenport > 65535) { @@ -614,8 +711,7 @@ } newfwd->have_reply = 0; - newfwd->next = *fwdlist; - *fwdlist = newfwd; + list_append(fwdlist, newfwd); TRACE(("leave addforward: done")) return;
--- a/cli-service.c Thu Nov 06 13:33:06 2008 +0000 +++ b/cli-service.c Wed May 16 22:54:51 2012 +0800 @@ -72,7 +72,7 @@ && strncmp(SSH_SERVICE_CONNECTION, servicename, len) == 0) { if (ses.authstate.authdone != 1) { - dropbear_exit("request for connection before auth"); + dropbear_exit("Request for connection before auth"); } cli_ses.state = SERVICE_CONN_ACCEPT_RCVD; @@ -81,5 +81,5 @@ return; } - dropbear_exit("unrecognised service accept"); + dropbear_exit("Unrecognised service accept"); }
--- a/cli-session.c Thu Nov 06 13:33:06 2008 +0000 +++ b/cli-session.c Wed May 16 22:54:51 2012 +0800 @@ -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 @@ #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); @@ -209,7 +213,7 @@ is confusing, though stdout/stderr could be useful. */ devnull = open(_PATH_DEVNULL, O_RDONLY); if (devnull < 0) { - dropbear_exit("opening /dev/null: %d %s", + dropbear_exit("Opening /dev/null: %d %s", errno, strerror(errno)); } dup2(devnull, STDIN_FILENO); @@ -231,7 +235,7 @@ 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")) @@ -294,7 +298,7 @@ m_close(ses.sock_out); ses.sock_in = -1; ses.sock_out = -1; - dropbear_exit("remote closed the connection"); + dropbear_exit("Remote closed the connection"); } /* Operates in-place turning dirty (untrusted potentially containing control
--- a/cli-tcpfwd.c Thu Nov 06 13:33:06 2008 +0000 +++ b/cli-tcpfwd.c Wed May 16 22:54:51 2012 +0800 @@ -45,7 +45,9 @@ #endif #ifdef ENABLE_CLI_LOCALTCPFWD -static int cli_localtcp(unsigned int listenport, const char* remoteaddr, +static int cli_localtcp(const char* listenaddr, + unsigned int listenport, + const char* remoteaddr, unsigned int remoteport); static const struct ChanType cli_chan_tcplocal = { 1, /* sepfds */ @@ -59,33 +61,33 @@ #ifdef ENABLE_CLI_LOCALTCPFWD void setup_localtcp() { - + m_list_elem *iter; int ret; TRACE(("enter setup_localtcp")) - if (cli_opts.localfwds == NULL) { - TRACE(("cli_opts.localfwds == NULL")) - } - - while (cli_opts.localfwds != NULL) { - ret = cli_localtcp(cli_opts.localfwds->listenport, - cli_opts.localfwds->connectaddr, - cli_opts.localfwds->connectport); + for (iter = cli_opts.localfwds->first; iter; iter = iter->next) { + struct TCPFwdEntry * fwd = (struct TCPFwdEntry*)iter->item; + ret = cli_localtcp( + fwd->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->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")) } -static int cli_localtcp(unsigned int listenport, const char* remoteaddr, +static int cli_localtcp(const char* listenaddr, + unsigned int listenport, + const char* remoteaddr, unsigned int remoteport) { struct TCPListener* tcpinfo = NULL; @@ -99,10 +101,17 @@ tcpinfo->sendaddr = m_strdup(remoteaddr); tcpinfo->sendport = remoteport; - if (opts.listen_fwd_all) { - tcpinfo->listenaddr = m_strdup(""); - } else { - tcpinfo->listenaddr = m_strdup("localhost"); + if (listenaddr) + { + tcpinfo->listenaddr = m_strdup(listenaddr); + } + else + { + if (opts.listen_fwd_all) { + tcpinfo->listenaddr = m_strdup(""); + } else { + tcpinfo->listenaddr = m_strdup("localhost"); + } } tcpinfo->listenport = listenport; @@ -120,22 +129,15 @@ #endif /* ENABLE_CLI_LOCALTCPFWD */ #ifdef ENABLE_CLI_REMOTETCPFWD -static void send_msg_global_request_remotetcp(int port) { +static void send_msg_global_request_remotetcp(const char *addr, int port) { - char* listenspec = NULL; TRACE(("enter send_msg_global_request_remotetcp")) CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_GLOBAL_REQUEST); buf_putstring(ses.writepayload, "tcpip-forward", 13); buf_putbyte(ses.writepayload, 1); /* want_reply */ - if (opts.listen_fwd_all) { - listenspec = ""; - } else { - listenspec = "localhost"; - } - /* TODO: IPv6? */; - buf_putstring(ses.writepayload, listenspec, strlen(listenspec)); + buf_putstring(ses.writepayload, addr, strlen(addr)); buf_putint(ses.writepayload, port); encrypt_packet(); @@ -146,90 +148,97 @@ /* The only global success/failure messages are for remotetcp. * Since there isn't any identifier in these messages, we have to rely on them * being in the same order as we sent the requests. This is the ordering - * of the cli_opts.remotefwds list */ + * of the cli_opts.remotefwds list. + * If the requested remote port is 0 the listen port will be + * dynamically allocated by the server and the port number will be returned + * to client and the port number reported to the user. */ void cli_recv_msg_request_success() { - - /* Nothing in the packet. We just mark off that we have received the reply, + /* 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; + if (fwd->listenport == 0) { + /* The server should let us know which port was allocated if we requestd port 0 */ + int allocport = buf_getint(ses.payload); + if (allocport > 0) { + dropbear_log(LOG_INFO, "Allocated port %d for remote forward to %s:%d", + allocport, fwd->connectaddr, fwd->connectport); + } + } return; } - iter = iter->next; } } void cli_recv_msg_request_failure() { - struct TCPFwdList * iter = NULL; - - iter = cli_opts.remotefwds; - while (iter != NULL) { - if (!iter->have_reply) - { - iter->have_reply = 1; - dropbear_log(LOG_WARNING, "Remote TCP forward request failed (port %d -> %s:%d)", iter->listenport, iter->connectaddr, iter->connectport); + m_list_elem *iter; + for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) { + struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item; + if (!fwd->have_reply) { + fwd->have_reply = 1; + dropbear_log(LOG_WARNING, "Remote TCP forward request failed (port %d -> %s:%d)", fwd->listenport, fwd->connectaddr, fwd->connectport); return; } - iter = iter->next; } } void setup_remotetcp() { - - struct TCPFwdList * iter = NULL; - + m_list_elem *iter; TRACE(("enter setup_remotetcp")) - if (cli_opts.remotefwds == NULL) { - TRACE(("cli_opts.remotefwds == NULL")) + for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) { + struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item; + if (!fwd->listenaddr) + { + // we store the addresses so that we can compare them + // when the server sends them back + if (opts.listen_fwd_all) { + fwd->listenaddr = m_strdup(""); + } else { + fwd->listenaddr = m_strdup("localhost"); + } + } + send_msg_global_request_remotetcp(fwd->listenaddr, fwd->listenport); } - iter = cli_opts.remotefwds; - - while (iter != NULL) { - send_msg_global_request_remotetcp(iter->listenport); - iter = iter->next; - } TRACE(("leave setup_remotetcp")) } static int newtcpforwarded(struct Channel * channel) { + 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; - /* We don't care what address they connected to */ - buf_eatstring(ses.payload); - + origaddr = buf_getstring(ses.payload, NULL); origport = buf_getint(ses.payload); /* Find which port corresponds */ - iter = cli_opts.remotefwds; - - while (iter != NULL) { - if (origport == iter->listenport) { + for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) { + fwd = (struct TCPFwdEntry*)iter->item; + if (origport == fwd->listenport + && (strcmp(origaddr, fwd->listenaddr) == 0)) { break; } - iter = iter->next; } if (iter == NULL) { /* We didn't request forwarding on that port */ - dropbear_log(LOG_INFO, "Server send unrequested port, from port %d", - origport); + cleantext(origaddr); + dropbear_log(LOG_INFO, "Server sent unrequested forward from \"%s:%d\"", + origaddr, origport); goto out; } - 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; @@ -246,6 +255,7 @@ err = SSH_OPEN_IN_PROGRESS; out: + m_free(origaddr); TRACE(("leave newtcpdirect: err %d", err)) return err; }
--- a/common-algo.c Thu Nov 06 13:33:06 2008 +0000 +++ b/common-algo.c Wed May 16 22:54:51 2012 +0800 @@ -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; } @@ -104,6 +106,14 @@ static const struct dropbear_hash dropbear_sha1_96 = {&sha1_desc, 20, 12}; #endif +#ifdef DROPBEAR_SHA2_256_HMAC +static const struct dropbear_hash dropbear_sha2_256 = + {&sha256_desc, 32, 32}; +#endif +#ifdef DROPBEAR_SHA2_512_HMAC +static const struct dropbear_hash dropbear_sha2_512 = + {&sha512_desc, 64, 64}; +#endif #ifdef DROPBEAR_MD5_HMAC static const struct dropbear_hash dropbear_md5 = {&md5_desc, 16, 16}; @@ -153,10 +163,16 @@ #ifdef DROPBEAR_NONE_CIPHER {"none", 0, (void*)&dropbear_nocipher, 1, &dropbear_mode_none}, #endif - {NULL, 0, NULL, 0} + {NULL, 0, NULL, 0, NULL} }; algo_type sshhashes[] = { +#ifdef DROPBEAR_SHA2_256_HMAC +// {"hmac-sha2-256", 0, &dropbear_sha2_256, 1, NULL}, +#endif +#ifdef DROPBEAR_SHA2_512_HMAC +// {"hmac-sha2-512", 0, &dropbear_sha2_512, 1, NULL}, +#endif #ifdef DROPBEAR_SHA1_96_HMAC {"hmac-sha1-96", 0, &dropbear_sha1_96, 1, NULL}, #endif @@ -164,19 +180,24 @@ {"hmac-sha1", 0, &dropbear_sha1, 1, NULL}, #endif #ifdef DROPBEAR_MD5_HMAC - {"hmac-md5", 0, (void*)&dropbear_md5, 1}, + {"hmac-md5", 0, (void*)&dropbear_md5, 1, NULL}, #endif #ifdef DROPBEAR_NONE_INTEGRITY - {"none", 0, (void*)&dropbear_nohash, 1}, + {"none", 0, (void*)&dropbear_nohash, 1, NULL}, #endif - {NULL, 0, NULL, 0} + {NULL, 0, NULL, 0, NULL} }; -algo_type sshcompress[] = { #ifndef DISABLE_ZLIB +algo_type ssh_compress[] = { {"zlib", DROPBEAR_COMP_ZLIB, NULL, 1, NULL}, {"[email protected]", 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} }; @@ -193,6 +214,7 @@ algo_type sshkex[] = { {"diffie-hellman-group1-sha1", DROPBEAR_KEX_DH_GROUP1, NULL, 1, NULL}, + {"diffie-hellman-group14-sha1", DROPBEAR_KEX_DH_GROUP14, NULL, 1, NULL}, {NULL, 0, NULL, 0, NULL} }; @@ -229,13 +251,13 @@ for (i = 0; regciphers[i] != NULL; i++) { if (register_cipher(regciphers[i]) == -1) { - dropbear_exit("error registering crypto"); + dropbear_exit("Error registering crypto"); } } for (i = 0; reghashes[i] != NULL; i++) { if (register_hash(reghashes[i]) == -1) { - dropbear_exit("error registering crypto"); + dropbear_exit("Error registering crypto"); } } }
--- a/common-channel.c Thu Nov 06 13:33:06 2008 +0000 +++ b/common-channel.c Wed May 16 22:54:51 2012 +0800 @@ -138,6 +138,7 @@ newchan->index = i; newchan->sent_close = newchan->recv_close = 0; newchan->sent_eof = newchan->recv_eof = 0; + newchan->close_handler_done = 0; newchan->remotechan = remotechan; newchan->transwindow = transwindow; @@ -270,7 +271,9 @@ cbuf_getused(channel->writebuf), channel->extrabuf ? cbuf_getused(channel->extrabuf) : 0)) - if (!channel->flushing && channel->type->check_close + if (!channel->flushing + && !channel->close_handler_done + && channel->type->check_close && channel->type->check_close(channel)) { channel->flushing = 1; @@ -281,7 +284,8 @@ channel, to ensure that the shell has exited (and the exit status retrieved) before we close things up. */ if (!channel->type->check_close - || channel->type->check_close(channel)) { + || channel->close_handler_done + || channel->type->check_close(channel)) { close_allowed = 1; } @@ -363,9 +367,11 @@ /* Send the close message and set the channel as closed */ static void send_msg_channel_close(struct Channel *channel) { - TRACE(("enter send_msg_channel_close")) - if (channel->type->closehandler) { + TRACE(("enter send_msg_channel_close %p", channel)) + if (channel->type->closehandler + && !channel->close_handler_done) { channel->type->closehandler(channel); + channel->close_handler_done = 1; } CHECKCLEARTOWRITE(); @@ -568,16 +574,17 @@ struct Channel *channel; - TRACE(("enter recv_msg_channel_request")) - channel = getchannel(); + TRACE(("enter recv_msg_channel_request %p", channel)) + if (channel->sent_close) { TRACE(("leave recv_msg_channel_request: already closed channel")) return; } - if (channel->type->reqhandler) { + if (channel->type->reqhandler + && !channel->close_handler_done) { channel->type->reqhandler(channel); } else { send_msg_channel_failure(channel); @@ -646,6 +653,8 @@ len, errno, fd)) return; } + + TRACE(("send_msg_channel_data: len %d fd %d", len, fd)) buf_incrwritepos(ses.writepayload, len); /* ... real size here */ buf_setpos(ses.writepayload, size_pos); @@ -688,10 +697,10 @@ TRACE(("enter recv_msg_channel_data")) if (channel->recv_eof) { - dropbear_exit("received data after eof"); + dropbear_exit("Received data after eof"); } - if (fd < 0) { + if (fd < 0) { /* If we have encountered failed write, the far side might still * be sending data without having yet received our close notification. * We just drop the data. */ @@ -1006,7 +1015,7 @@ channel = getchannel(); if (!channel->await_open) { - dropbear_exit("unexpected channel reply"); + dropbear_exit("Unexpected channel reply"); } channel->await_open = 0; @@ -1038,7 +1047,7 @@ channel = getchannel(); if (!channel->await_open) { - dropbear_exit("unexpected channel reply"); + dropbear_exit("Unexpected channel reply"); } channel->await_open = 0;
--- a/common-kex.c Thu Nov 06 13:33:06 2008 +0000 +++ b/common-kex.c Wed May 16 22:54:51 2012 +0800 @@ -33,9 +33,11 @@ #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[] = { +#define DH_P_1_LEN 128 +static const unsigned char dh_p_1[DH_P_1_LEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, @@ -47,8 +49,34 @@ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; -#define DH_P_LEN sizeof(dh_p_val) +/* diffie-hellman-group14-sha1 value for p */ +#define DH_P_14_LEN 256 +static const unsigned char dh_p_14[DH_P_14_LEN] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, + 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, + 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, + 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, + 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, + 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, + 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, + 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, + 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, + 0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C, + 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF}; + +/* Same for group1 and group14 */ static const int DH_G_VAL = 2; static void kexinitialise(); @@ -91,10 +119,10 @@ 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 +208,16 @@ /* Set up the kex for the first time */ void kexfirstinitialise() { + ses.kexstate.donefirstkex = 0; - ses.kexstate.donefirstkex = 0; +#ifndef DISABLE_ZLIB + if (opts.enable_compress) { + ses.compress_algos = ssh_compress; + } else +#endif + { + ses.compress_algos = ssh_nocompress; + } kexinitialise(); } @@ -212,32 +248,34 @@ * already initialised hash_state hs, which should already have processed * the dh_K and hash, since these are common. X is the letter 'A', 'B' etc. * out must have at least min(SHA1_HASH_SIZE, outlen) bytes allocated. - * The output will only be expanded once, as we are assured that - * outlen <= 2*SHA1_HASH_SIZE for all known hashes. * * See Section 7.2 of rfc4253 (ssh transport) for details */ static void hashkeys(unsigned char *out, int outlen, const hash_state * hs, const unsigned char X) { hash_state hs2; - unsigned char k2[SHA1_HASH_SIZE]; /* used to extending */ + int offset; memcpy(&hs2, hs, sizeof(hash_state)); sha1_process(&hs2, &X, 1); sha1_process(&hs2, ses.session_id, SHA1_HASH_SIZE); sha1_done(&hs2, out); - if (SHA1_HASH_SIZE < outlen) { + for (offset = SHA1_HASH_SIZE; + offset < outlen; + offset += SHA1_HASH_SIZE) + { /* need to extend */ + unsigned char k2[SHA1_HASH_SIZE]; memcpy(&hs2, hs, sizeof(hash_state)); - sha1_process(&hs2, out, SHA1_HASH_SIZE); + sha1_process(&hs2, out, offset); sha1_done(&hs2, k2); - memcpy(&out[SHA1_HASH_SIZE], k2, outlen - SHA1_HASH_SIZE); + memcpy(&out[offset], k2, MIN(outlen - offset, SHA1_HASH_SIZE)); } } /* Generate the actual encryption/integrity keys, using the results of the - * key exchange, as specified in section 5.2 of the IETF secsh-transport - * draft. This occurs after the DH key-exchange. + * key exchange, as specified in section 7.2 of the transport rfc 4253. + * This occurs after the DH key-exchange. * * ses.newkeys is the new set of keys which are generated, these are only * taken into use after both sides have sent a newkeys message */ @@ -255,7 +293,6 @@ hash_state hs; unsigned int C2S_keysize, S2C_keysize; char mactransletter, macrecvletter; /* Client or server specific */ - int recv_cipher = 0, trans_cipher = 0; TRACE(("enter gen_new_keys")) /* the dh_K and hash are the start of all hashes, we make use of that */ @@ -272,8 +309,8 @@ 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 +318,8 @@ 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,39 +329,41 @@ hashkeys(C2S_key, C2S_keysize, &hs, 'C'); hashkeys(S2C_key, S2C_keysize, &hs, 'D'); - if (ses.newkeys->recv_algo_crypt->cipherdesc != NULL) { - recv_cipher = find_cipher(ses.newkeys->recv_algo_crypt->cipherdesc->name); + if (ses.newkeys->recv.algo_crypt->cipherdesc != NULL) { + int 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, + dropbear_exit("Crypto error"); + 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) { - dropbear_exit("crypto error"); + ses.newkeys->recv.algo_crypt->keysize, 0, + &ses.newkeys->recv.cipher_state) != CRYPT_OK) { + dropbear_exit("Crypto error"); } } - if (ses.newkeys->trans_algo_crypt->cipherdesc != NULL) { - trans_cipher = find_cipher(ses.newkeys->trans_algo_crypt->cipherdesc->name); + if (ses.newkeys->trans.algo_crypt->cipherdesc != NULL) { + int 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, + dropbear_exit("Crypto error"); + 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) { - dropbear_exit("crypto error"); + ses.newkeys->trans.algo_crypt->keysize, 0, + &ses.newkeys->trans.cipher_state) != CRYPT_OK) { + dropbear_exit("Crypto error"); } } /* MAC keys */ - if (ses.newkeys->trans_algo_mac->hashdesc != NULL) { - hashkeys(ses.newkeys->transmackey, - ses.newkeys->trans_algo_mac->keysize, &hs, mactransletter); + if (ses.newkeys->trans.algo_mac->hashdesc != NULL) { + hashkeys(ses.newkeys->trans.mackey, + ses.newkeys->trans.algo_mac->keysize, &hs, mactransletter); } - if (ses.newkeys->recv_algo_mac->hashdesc != NULL) { - hashkeys(ses.newkeys->recvmackey, - ses.newkeys->recv_algo_mac->keysize, &hs, macrecvletter); + if (ses.newkeys->recv.algo_mac->hashdesc != NULL) { + 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(); @@ -336,21 +375,26 @@ ses.keys = ses.newkeys; ses.newkeys = NULL; + m_burn(C2S_IV, sizeof(C2S_IV)); + m_burn(C2S_key, sizeof(C2S_key)); + m_burn(S2C_IV, sizeof(S2C_IV)); + m_burn(S2C_key, sizeof(S2C_key)); + TRACE(("leave 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 @@ -358,47 +402,49 @@ 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"); + 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"); + dropbear_exit("Crypto error"); } - m_free(ses.keys->trans_zstream); + m_free(ses.keys->trans.zstream); } } #endif /* DISABLE_ZLIB */ @@ -487,8 +533,20 @@ TRACE(("leave recv_msg_kexinit")) } +static void load_dh_p(mp_int * dh_p) +{ + switch (ses.newkeys->algo_kex) { + case DROPBEAR_KEX_DH_GROUP1: + bytes_to_mp(dh_p, dh_p_1, DH_P_1_LEN); + break; + case DROPBEAR_KEX_DH_GROUP14: + bytes_to_mp(dh_p, dh_p_14, DH_P_14_LEN); + break; + } +} + /* Initialises and generate one side of the diffie-hellman key exchange values. - * See the ietf-secsh-transport draft, section 6, for details */ + * See the transport rfc 4253 section 8 for details */ /* dh_pub and dh_priv MUST be already initialised */ void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv) { @@ -501,7 +559,7 @@ m_mp_init_multi(&dh_g, &dh_p, &dh_q, NULL); /* read the prime and generator*/ - bytes_to_mp(&dh_p, (unsigned char*)dh_p_val, DH_P_LEN); + load_dh_p(&dh_p); if (mp_set_int(&dh_g, DH_G_VAL) != MP_OKAY) { dropbear_exit("Diffie-Hellman error"); @@ -539,7 +597,7 @@ /* read the prime and generator*/ m_mp_init(&dh_p); - bytes_to_mp(&dh_p, dh_p_val, DH_P_LEN); + load_dh_p(&dh_p); /* Check that dh_pub_them (dh_e or dh_f) is in the range [1, p-1] */ if (mp_cmp(dh_pub_them, &dh_p) != MP_LT @@ -674,7 +732,7 @@ 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; @@ -682,7 +740,7 @@ 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; @@ -706,36 +764,36 @@ /* 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 */ @@ -743,5 +801,5 @@ return; error: - dropbear_exit("no matching algo %s", erralgo); + dropbear_exit("No matching algo %s", erralgo); }
--- a/common-session.c Thu Nov 06 13:33:06 2008 +0000 +++ b/common-session.c Wed May 16 22:54:51 2012 +0800 @@ -52,24 +52,26 @@ /* 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); ses.connect_time = 0; + ses.last_trx_packet_time = 0; ses.last_packet_time = 0; if (pipe(ses.signal_pipe) < 0) { - dropbear_exit("signal pipe failed"); + dropbear_exit("Signal pipe failed"); } 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 */ @@ -77,7 +79,6 @@ ses.transseq = 0; ses.readbuf = NULL; - ses.decryptreadbuf = NULL; ses.payload = NULL; ses.recvseq = 0; @@ -94,22 +95,22 @@ /* 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 */ @@ -258,7 +259,7 @@ ses.remoteclosed(); } - /* If they send more than 50 lines, something is wrong */ + /* If they send more than 50 lines, something is wrong */ for (i = 0; i < 50; i++) { len = ident_readln(ses.sock_in, linebuf, sizeof(linebuf)); @@ -283,11 +284,11 @@ memcpy(ses.remoteident, linebuf, len); } - /* Shall assume that 2.x will be backwards compatible. */ - if (strncmp(ses.remoteident, "SSH-2.", 6) != 0 - && strncmp(ses.remoteident, "SSH-1.99-", 9) != 0) { - dropbear_exit("Incompatible remote version '%s'", ses.remoteident); - } + /* Shall assume that 2.x will be backwards compatible. */ + if (strncmp(ses.remoteident, "SSH-2.", 6) != 0 + && strncmp(ses.remoteident, "SSH-1.99-", 9) != 0) { + dropbear_exit("Incompatible remote version '%s'", ses.remoteident); + } TRACE(("remoteident: %s", ses.remoteident)) @@ -399,9 +400,14 @@ } if (opts.keepalive_secs > 0 - && now - ses.last_packet_time >= opts.keepalive_secs) { + && now - ses.last_trx_packet_time >= opts.keepalive_secs) { send_msg_ignore(); } + + if (opts.idle_timeout_secs > 0 && ses.last_packet_time > 0 + && now - ses.last_packet_time >= opts.idle_timeout_secs) { + dropbear_close("Idle timeout"); + } } static long select_timeout() { @@ -414,6 +420,8 @@ ret = MIN(AUTH_TIMEOUT, ret); if (opts.keepalive_secs > 0) ret = MIN(opts.keepalive_secs, ret); + if (opts.idle_timeout_secs > 0) + ret = MIN(opts.idle_timeout_secs, ret); return ret; } @@ -445,6 +453,16 @@ ses.authstate.pw_name = m_strdup(pw->pw_name); ses.authstate.pw_dir = m_strdup(pw->pw_dir); ses.authstate.pw_shell = m_strdup(pw->pw_shell); - ses.authstate.pw_passwd = m_strdup(pw->pw_passwd); + { + char *passwd_crypt = pw->pw_passwd; +#ifdef HAVE_SHADOW_H + /* get the shadow password if possible */ + struct spwd *spasswd = getspnam(ses.authstate.pw_name); + if (spasswd && spasswd->sp_pwdp) { + passwd_crypt = spasswd->sp_pwdp; + } +#endif + ses.authstate.pw_passwd = m_strdup(passwd_crypt); + } }
--- a/configure.in Thu Nov 06 13:33:06 2008 +0000 +++ b/configure.in Wed May 16 22:54:51 2012 +0800 @@ -82,7 +82,8 @@ ],,,) # 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 @@ 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) @@ -361,6 +363,25 @@ AC_CHECK_FUNCS(setutxent utmpxname) AC_CHECK_FUNCS(logout updwtmp logwtmp) +AC_ARG_ENABLE(bundled-libtom, + [ --enable-bundled-libtom Use bundled libtomcrypt/libtommath even if a system version exists], + [ + BUNDLED_LIBTOM=1 + AC_MSG_NOTICE(Forcing bundled libtom*) + ], + [ + BUNDLED_LIBTOM=0 + AC_CHECK_LIB(tomcrypt, register_cipher, , BUNDLED_LIBTOM=1) + AC_CHECK_LIB(tommath, mp_exptmod, , BUNDLED_LIBTOM=1) + ] +) + +if test $BUNDLED_LIBTOM = 1 ; then + AC_DEFINE(BUNDLED_LIBTOM,,Use bundled libtom) +fi + +AC_SUBST(BUNDLED_LIBTOM) + dnl Added from OpenSSH 3.6.1p2's configure.ac dnl allow user to disable some login recording features @@ -595,7 +616,7 @@ AC_FUNC_MEMCMP AC_FUNC_SELECT_ARGTYPES AC_TYPE_SIGNAL -AC_CHECK_FUNCS([dup2 getspnam getusershell memset putenv select socket strdup clearenv strlcpy strlcat daemon basename _getpty getaddrinfo freeaddrinfo getnameinfo]) +AC_CHECK_FUNCS([dup2 getspnam getusershell memset putenv select socket strdup clearenv strlcpy strlcat daemon basename _getpty getaddrinfo freeaddrinfo getnameinfo fork]) AC_SEARCH_LIBS(basename, gen, AC_DEFINE(HAVE_BASENAME)) @@ -669,5 +690,13 @@ AC_OUTPUT(Makefile) AC_OUTPUT(libtomcrypt/Makefile) AC_OUTPUT(libtommath/Makefile) + +AC_MSG_NOTICE() +if test $BUNDLED_LIBTOM = 1 ; then +AC_MSG_NOTICE(Using bundled libtomcrypt and libtommath) +else +AC_MSG_NOTICE(Using system libtomcrypt and libtommath) +fi + AC_MSG_NOTICE() AC_MSG_NOTICE(Now edit options.h to choose features.)
--- a/dbclient.1 Thu Nov 06 13:33:06 2008 +0000 +++ b/dbclient.1 Wed May 16 22:54:51 2012 +0800 @@ -36,7 +36,7 @@ .I idfile (multiple allowed). .TP -.B \-L \fIlistenport\fR:\fIhost\fR:\fIport\fR +.B \-L [\fIlistenaddress\fR]:\fIlistenport\fR:\fIhost\fR:\fIport\fR Local port forwarding. Forward the port .I listenport @@ -45,7 +45,7 @@ on the host .IR host . .TP -.B \-R \fIlistenport\fR:\fIhost\fR:\fIport\fR +.B \-R [\fIlistenaddress\fR]:\fIlistenport\fR:\fIhost\fR:\fIport\fR Remote port forwarding. Forward the port .I listenport @@ -82,6 +82,11 @@ 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 @@ -93,11 +98,16 @@ a certain period of inactivity. The trade-off is that a session may be closed if there is a temporary lapse of network connectivity. A setting if 0 disables keepalives. +.TP +.B \-I \fIidle_timeout +Disconnect the session if no traffic is transmitted or received for \fIidle_timeout\fR seconds. +.TP .B \-J \fIproxy_command Use the standard input/output of the program \fIproxy_command\fR rather than using a normal TCP connection. A hostname should be still be provided, as this is used for comparing saved hostkeys. -.B \B \fIendhost:endport +.TP +.B \-B \fIendhost:endport "Netcat-alike" mode, where Dropbear will connect to the given host, then create a forwarded connection to \fIendhost\fR. This will then be presented as dbclient's standard input/output. @@ -112,6 +122,10 @@ scp -S dbclient matt@martello,root@wrt,canyons:/tmp/dump . +Note that hostnames are resolved by the prior hop (so "canyons" would be resolved by the host "wrt") +in the example above, the same way as other -L TCP forwarded hosts are. Host keys are +checked locally based on the given hostname. + .SH ENVIRONMENT .TP .B DROPBEAR_PASSWORD
--- a/dbutil.c Thu Nov 06 13:33:06 2008 +0000 +++ b/dbutil.c Wed May 16 22:54:51 2012 +0800 @@ -57,11 +57,11 @@ #define MAX_FMT 100 static void generic_dropbear_exit(int exitcode, const char* format, - va_list param); + va_list param) ATTRIB_NORETURN; static void generic_dropbear_log(int priority, const char* format, va_list param); -void (*_dropbear_exit)(int exitcode, const char* format, va_list param) +void (*_dropbear_exit)(int exitcode, const char* format, va_list param) ATTRIB_NORETURN = generic_dropbear_exit; void (*_dropbear_log)(int priority, const char* format, va_list param) = generic_dropbear_log; @@ -111,7 +111,7 @@ } void fail_assert(const char* expr, const char* file, int line) { - dropbear_exit("failed assertion (%s:%d): `%s'", file, line, expr); + dropbear_exit("Failed assertion (%s:%d): `%s'", file, line, expr); } static void generic_dropbear_log(int UNUSED(priority), const char* format, @@ -161,10 +161,12 @@ val = 1; setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&val, sizeof(val)); - /* set the TOS bit. note that this will fail for ipv6, I can't find any - * equivalent. */ + /* set the TOS bit for either ipv4 or ipv6 */ #ifdef IPTOS_LOWDELAY val = IPTOS_LOWDELAY; +#if defined(IPPROTO_IPV6) && defined(IPV6_TCLASS) + setsockopt(sock, IPPROTO_IPV6, IPV6_TCLASS, (void*)&val, sizeof(val)); +#endif setsockopt(sock, IPPROTO_IP, IP_TOS, (void*)&val, sizeof(val)); #endif @@ -254,6 +256,16 @@ linger.l_linger = 5; setsockopt(sock, SOL_SOCKET, SO_LINGER, (void*)&linger, sizeof(linger)); +#if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY) + if (res->ai_family == AF_INET6) { + int on = 1; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, + &on, sizeof(on)) == -1) { + dropbear_log(LOG_WARNING, "Couldn't set IPV6_V6ONLY"); + } + } +#endif + set_sock_priority(sock); if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { @@ -295,6 +307,29 @@ 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)) + m_close(fd); + 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 +376,7 @@ } 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) { @@ -416,7 +443,7 @@ return DROPBEAR_FAILURE; } -#ifdef __uClinux__ +#ifdef USE_VFORK pid = vfork(); #else pid = fork(); @@ -441,7 +468,7 @@ (dup2(outfds[FDOUT], STDOUT_FILENO) < 0) || (ret_errfd && dup2(errfds[FDOUT], STDERR_FILENO) < 0)) { TRACE(("leave noptycommand: error redirecting FDs")) - dropbear_exit("child dup2() failure"); + dropbear_exit("Child dup2() failure"); } close(infds[FDOUT]); @@ -525,14 +552,47 @@ 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; + char host[NI_MAXHOST+1], serv[NI_MAXSERV+1]; + unsigned int len; int ret; - unsigned int len; + + 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 +610,28 @@ #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); + if (ret_host) { + *ret_host = m_strdup(host); } -#ifdef AF_INET6 - if (addr->ss_family == AF_INET6) { - len = sizeof(struct sockaddr_in6); + if (ret_port) { + *ret_port = m_strdup(serv); } -#endif -#endif - - - ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf), - sbuf, sizeof(sbuf), flags); - - if (ret != 0) { - /* On some systems (Darwin does it) we get EINTR from getnameinfo - * somehow. Eew. So we'll just return the IP, since that doesn't seem - * to exhibit that behaviour. */ - return getaddrstring(addr, 0); - } - - return m_strdup(hbuf); } #ifdef DEBUG_TRACE @@ -779,12 +800,6 @@ return ret; } -void __m_free(void* ptr) { - if (ptr != NULL) { - free(ptr); - } -} - void * m_realloc(void* ptr, size_t size) { void *ret; @@ -809,7 +824,7 @@ if (data == NULL) return; while (len--) { - *p++ = 0x66; + *p++ = 0x0; } }
--- a/dbutil.h Thu Nov 06 13:33:06 2008 +0000 +++ b/dbutil.h Wed May 16 22:54:51 2012 +0800 @@ -33,28 +33,49 @@ void startsyslog(); #endif -extern void (*_dropbear_exit)(int exitcode, const char* format, va_list param); +#ifdef __GNUC__ +#define ATTRIB_PRINTF(fmt,args) __attribute__((format(printf, fmt, args))) +#else +#define ATTRIB_PRINTF(fmt,args) +#endif + +#ifdef __GNUC__ +#define ATTRIB_NORETURN __attribute__((noreturn)) +#else +#define ATTRIB_NORETURN +#endif + +extern void (*_dropbear_exit)(int exitcode, const char* format, va_list param) ATTRIB_NORETURN; extern void (*_dropbear_log)(int priority, const char* format, va_list param); -void dropbear_exit(const char* format, ...); -void dropbear_close(const char* format, ...); -void dropbear_log(int priority, const char* format, ...); -void fail_assert(const char* expr, const char* file, int line); +void dropbear_exit(const char* format, ...) ATTRIB_PRINTF(1,2) ATTRIB_NORETURN; + +void dropbear_close(const char* format, ...) ATTRIB_PRINTF(1,2) ; +void dropbear_log(int priority, const char* format, ...) ATTRIB_PRINTF(2,3) ; + +void fail_assert(const char* expr, const char* file, int line) ATTRIB_NORETURN; + #ifdef DEBUG_TRACE -void dropbear_trace(const char* format, ...); +void dropbear_trace(const char* format, ...) ATTRIB_PRINTF(1,2); 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); @@ -62,8 +83,7 @@ void * m_malloc(size_t size); void * m_strdup(const char * str); void * m_realloc(void* ptr, size_t size); -#define m_free(X) __m_free(X); (X) = NULL; -void __m_free(void* ptr); +#define m_free(X) free(X); (X) = NULL; void m_burn(void* data, unsigned int len); void setnonblocking(int fd); void disallow_core();
--- a/debian/changelog Thu Nov 06 13:33:06 2008 +0000 +++ b/debian/changelog Wed May 16 22:54:51 2012 +0800 @@ -1,3 +1,33 @@ +dropbear (2012.55-0.1) unstable; urgency=low + + * New upstream release. + + -- Matt Johnston <[email protected]> Wed, 22 Feb 2012 22:54:00 +0800 + +dropbear (2011.54-0.1) unstable; urgency=low + + * New upstream release. + + -- Matt Johnston <[email protected]> Tues, 8 Nov 2011 22:54:00 +0800 + +dropbear (0.53.1-0.1) unstable; urgency=low + + * New upstream release. + + -- Matt Johnston <[email protected]> Wed, 2 Mar 2011 22:54:00 +0900 + +dropbear (0.53-0.1) unstable; urgency=low + + * New upstream release. + + -- Matt Johnston <[email protected]> Thu, 24 Feb 2011 22:54:00 +0900 + +dropbear (0.52-0.1) unstable; urgency=low + + * New upstream release. + + -- Matt Johnston <[email protected]> Wed, 12 Nov 2008 22:54:00 +0900 + dropbear (0.51-0.1) unstable; urgency=low * New upstream release.
--- a/debian/dropbear.postinst Thu Nov 06 13:33:06 2008 +0000 +++ b/debian/dropbear.postinst Wed May 16 22:54:51 2012 +0800 @@ -71,7 +71,7 @@ fi if test -n "$2" && dpkg --compare-versions "$2" lt '0.50-4' && -update-service --check dropbear; then +update-service --check dropbear 2>/dev/null; then update-service --remove /etc/dropbear 2>/dev/null || : sleep 6 rm -rf /var/run/dropbear /var/run/dropbear.log
--- a/debug.h Thu Nov 06 13:33:06 2008 +0000 +++ b/debug.h Wed May 16 22:54:51 2012 +0800 @@ -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
--- a/dropbear.8 Thu Nov 06 13:33:06 2008 +0000 +++ b/dropbear.8 Wed May 16 22:54:51 2012 +0800 @@ -7,7 +7,7 @@ .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 @@ -24,7 +24,10 @@ dsskeyfile. Use the contents of the file .I dsskey -for the dss host key (default: /etc/dropbear/dropbear_dss_host_key). +for the DSS host key (default: /etc/dropbear/dropbear_dss_host_key). +Note that +some SSH implementations +use the term "DSA" rather than "DSS", they mean the same thing. This file is generated with .BR dropbearkey (8). .TP @@ -94,6 +97,9 @@ a certain period of inactivity. The trade-off is that a session may be closed if there is a temporary lapse of network connectivity. A setting if 0 disables keepalives. +.TP +.B \-I \fIidle_timeout +Disconnect the session if no traffic is transmitted or received for \fIidle_timeout\fR seconds. .SH FILES .TP @@ -148,6 +154,34 @@ 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 ([email protected]). .br
--- a/dropbearkey.8 Thu Nov 06 13:33:06 2008 +0000 +++ b/dropbearkey.8 Wed May 16 22:54:51 2012 +0800 @@ -11,13 +11,16 @@ .IR bits ] .SH DESCRIPTION .B dropbearkey -generates a type -.I rsa +generates a +.I RSA or -.I dss -SSH private key, and saves it to a file for the use with the +.I DSS +format SSH private key, and saves it to a file for the use with the .BR dropbear (8) SSH 2 server. +Note that +some SSH implementations +use the term "DSA" rather than "DSS", they mean the same thing. .SH OPTIONS .TP .B \-t \fItype
--- a/dropbearkey.c Thu Nov 06 13:33:06 2008 +0000 +++ b/dropbearkey.c Wed May 16 22:54:51 2012 +0800 @@ -23,7 +23,7 @@ * SOFTWARE. */ /* The format of the keyfiles is basically a raw dump of the buffer. Data types - * are specified in the transport draft - string is a 32-bit len then the + * are specified in the transport rfc 4253 - string is a 32-bit len then the * non-null-terminated string, mp_int is a 32-bit len then the bignum data. * The actual functions are buf_put_rsa_priv_key() and buf_put_dss_priv_key()
--- a/dss.c Thu Nov 06 13:33:06 2008 +0000 +++ b/dss.c Wed May 16 22:54:51 2012 +0800 @@ -43,7 +43,7 @@ * The key will have the same format as buf_put_dss_key. * These should be freed with dss_key_free. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ -int buf_get_dss_pub_key(buffer* buf, dss_key *key) { +int buf_get_dss_pub_key(buffer* buf, dropbear_dss_key *key) { TRACE(("enter buf_get_dss_pub_key")) dropbear_assert(key != NULL); @@ -76,7 +76,7 @@ /* Same as buf_get_dss_pub_key, but reads a private "x" key at the end. * Loads a private dss key from a buffer * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ -int buf_get_dss_priv_key(buffer* buf, dss_key *key) { +int buf_get_dss_priv_key(buffer* buf, dropbear_dss_key *key) { int ret = DROPBEAR_FAILURE; @@ -99,7 +99,7 @@ /* Clear and free the memory used by a public or private key */ -void dss_key_free(dss_key *key) { +void dss_key_free(dropbear_dss_key *key) { TRACE(("enter dsa_key_free")) if (key == NULL) { @@ -138,7 +138,7 @@ * mpint g * mpint y */ -void buf_put_dss_pub_key(buffer* buf, dss_key *key) { +void buf_put_dss_pub_key(buffer* buf, dropbear_dss_key *key) { dropbear_assert(key != NULL); buf_putstring(buf, SSH_SIGNKEY_DSS, SSH_SIGNKEY_DSS_LEN); @@ -150,7 +150,7 @@ } /* Same as buf_put_dss_pub_key, but with the private "x" key appended */ -void buf_put_dss_priv_key(buffer* buf, dss_key *key) { +void buf_put_dss_priv_key(buffer* buf, dropbear_dss_key *key) { dropbear_assert(key != NULL); buf_put_dss_pub_key(buf, key); @@ -161,7 +161,7 @@ #ifdef DROPBEAR_SIGNKEY_VERIFY /* Verify a DSS signature (in buf) made on data by the key given. * returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ -int buf_dss_verify(buffer* buf, dss_key *key, const unsigned char* data, +int buf_dss_verify(buffer* buf, dropbear_dss_key *key, const unsigned char* data, unsigned int len) { unsigned char msghash[SHA1_HASH_SIZE]; @@ -270,7 +270,7 @@ size = mp_unsigned_bin_size(mp); ret = m_malloc(size); if (mp_to_unsigned_bin(mp, ret) != MP_OKAY) { - dropbear_exit("mem alloc error"); + dropbear_exit("Mem alloc error"); } if (len != NULL) { *len = size; @@ -292,7 +292,7 @@ * * Now we aren't relying on the random number generation to protect the private * key x, which is a long term secret */ -void buf_put_dss_sign(buffer* buf, dss_key *key, const unsigned char* data, +void buf_put_dss_sign(buffer* buf, dropbear_dss_key *key, const unsigned char* data, unsigned int len) { unsigned char msghash[SHA1_HASH_SIZE]; @@ -342,7 +342,7 @@ m_mp_init(&dss_protok); bytes_to_mp(&dss_protok, proto_k, SHA512_HASH_SIZE); if (mp_mod(&dss_protok, key->q, &dss_k) != MP_OKAY) { - dropbear_exit("dss error"); + dropbear_exit("DSS error"); } mp_clear(&dss_protok); m_burn(proto_k, SHA512_HASH_SIZE); @@ -355,30 +355,30 @@ /* g^k mod p */ if (mp_exptmod(key->g, &dss_k, key->p, &dss_temp1) != MP_OKAY) { - dropbear_exit("dss error"); + dropbear_exit("DSS error"); } /* r = (g^k mod p) mod q */ if (mp_mod(&dss_temp1, key->q, &dss_r) != MP_OKAY) { - dropbear_exit("dss error"); + dropbear_exit("DSS error"); } /* x*r mod q */ if (mp_mulmod(&dss_r, key->x, key->q, &dss_temp1) != MP_OKAY) { - dropbear_exit("dss error"); + dropbear_exit("DSS error"); } /* (SHA1(M) + xr) mod q) */ if (mp_addmod(&dss_m, &dss_temp1, key->q, &dss_temp2) != MP_OKAY) { - dropbear_exit("dss error"); + dropbear_exit("DSS error"); } /* (k^-1) mod q */ if (mp_invmod(&dss_k, key->q, &dss_temp1) != MP_OKAY) { - dropbear_exit("dss error"); + dropbear_exit("DSS error"); } /* s = (k^-1(SHA1(M) + xr)) mod q */ if (mp_mulmod(&dss_temp1, &dss_temp2, key->q, &dss_s) != MP_OKAY) { - dropbear_exit("dss error"); + dropbear_exit("DSS error"); } buf_putstring(buf, SSH_SIGNKEY_DSS, SSH_SIGNKEY_DSS_LEN); @@ -392,7 +392,7 @@ } if (mp_to_unsigned_bin(&dss_r, buf_getwriteptr(buf, writelen)) != MP_OKAY) { - dropbear_exit("dss error"); + dropbear_exit("DSS error"); } mp_clear(&dss_r); buf_incrwritepos(buf, writelen); @@ -405,7 +405,7 @@ } if (mp_to_unsigned_bin(&dss_s, buf_getwriteptr(buf, writelen)) != MP_OKAY) { - dropbear_exit("dss error"); + dropbear_exit("DSS error"); } mp_clear(&dss_s); buf_incrwritepos(buf, writelen);
--- a/dss.h Thu Nov 06 13:33:06 2008 +0000 +++ b/dss.h Wed May 16 22:54:51 2012 +0800 @@ -32,29 +32,28 @@ #define DSS_SIGNATURE_SIZE 4+SSH_SIGNKEY_DSS_LEN+4+2*SHA1_HASH_SIZE -struct DSS_key { +typedef struct { mp_int* p; mp_int* q; mp_int* g; mp_int* y; + /* x is the private part */ mp_int* x; -}; +} dropbear_dss_key; -typedef struct DSS_key dss_key; - -void buf_put_dss_sign(buffer* buf, dss_key *key, const unsigned char* data, +void buf_put_dss_sign(buffer* buf, dropbear_dss_key *key, const unsigned char* data, unsigned int len); #ifdef DROPBEAR_SIGNKEY_VERIFY -int buf_dss_verify(buffer* buf, dss_key *key, const unsigned char* data, +int buf_dss_verify(buffer* buf, dropbear_dss_key *key, const unsigned char* data, unsigned int len); #endif -int buf_get_dss_pub_key(buffer* buf, dss_key *key); -int buf_get_dss_priv_key(buffer* buf, dss_key *key); -void buf_put_dss_pub_key(buffer* buf, dss_key *key); -void buf_put_dss_priv_key(buffer* buf, dss_key *key); -void dss_key_free(dss_key *key); +int buf_get_dss_pub_key(buffer* buf, dropbear_dss_key *key); +int buf_get_dss_priv_key(buffer* buf, dropbear_dss_key *key); +void buf_put_dss_pub_key(buffer* buf, dropbear_dss_key *key); +void buf_put_dss_priv_key(buffer* buf, dropbear_dss_key *key); +void dss_key_free(dropbear_dss_key *key); #endif /* DROPBEAR_DSS */
--- a/fake-rfc2553.c Thu Nov 06 13:33:06 2008 +0000 +++ b/fake-rfc2553.c Wed May 16 22:54:51 2012 +0800 @@ -1,7 +1,6 @@ +/* Taken for Dropbear from OpenSSH 5.5p1 */ + /* - * - * Taken from OpenSSH 3.8.1p1 - * * Copyright (C) 2000-2003 Damien Miller. All rights reserved. * Copyright (C) 1999 WIDE Project. All rights reserved. * @@ -40,7 +39,11 @@ #include "includes.h" -/* RCSID("$.Id: fake-rfc2553.c,v 1.5 2003/09/22 02:08:23 dtucker Exp $");*/ +#include <stdlib.h> +#include <string.h> + +#include <netinet/in.h> +#include <arpa/inet.h> #ifndef HAVE_GETNAMEINFO int getnameinfo(const struct sockaddr *sa, size_t salen, char *host, @@ -50,6 +53,8 @@ struct hostent *hp; char tmpserv[16]; + if (sa->sa_family != AF_UNSPEC && sa->sa_family != AF_INET) + return (EAI_FAMILY); if (serv != NULL) { snprintf(tmpserv, sizeof(tmpserv), "%d", ntohs(sin->sin_port)); if (strlcpy(serv, tmpserv, servlen) >= servlen) @@ -94,6 +99,8 @@ return ("memory allocation failure."); case EAI_NONAME: return ("nodename nor servname provided, or not known"); + case EAI_FAMILY: + return ("ai_family not supported"); default: return ("unknown/invalid error."); } @@ -158,6 +165,9 @@ u_long addr; port = 0; + if (hints && hints->ai_family != AF_UNSPEC && + hints->ai_family != AF_INET) + return (EAI_FAMILY); if (servname != NULL) { char *cp;
--- a/fake-rfc2553.h Thu Nov 06 13:33:06 2008 +0000 +++ b/fake-rfc2553.h Wed May 16 22:54:51 2012 +0800 @@ -1,6 +1,6 @@ -/* Taken from OpenSSH 3.8.1p1 */ +/* Taken for Dropbear from OpenSSH 5.5p1 */ -/* $.Id: fake-rfc2553.h,v 1.9 2004/03/10 10:06:33 dtucker Exp $ */ +/* $Id: fake-rfc2553.h,v 1.16 2008/07/14 11:37:37 djm Exp $ */ /* * Copyright (C) 2000-2003 Damien Miller. All rights reserved. @@ -43,6 +43,10 @@ #define _FAKE_RFC2553_H #include "includes.h" +#include <sys/types.h> +#if defined(HAVE_NETDB_H) +# include <netdb.h> +#endif /* * First, socket and INET6 related definitions @@ -75,6 +79,7 @@ u_int16_t sin6_port; u_int32_t sin6_flowinfo; struct in6_addr sin6_addr; + u_int32_t sin6_scope_id; }; #endif /* !HAVE_STRUCT_SOCKADDR_IN6 */ @@ -115,9 +120,19 @@ #endif /* !NI_MAXHOST */ #ifndef EAI_NODATA -# define EAI_NODATA 1 -# define EAI_MEMORY 2 -# define EAI_NONAME 3 +# define EAI_NODATA (INT_MAX - 1) +#endif +#ifndef EAI_MEMORY +# define EAI_MEMORY (INT_MAX - 2) +#endif +#ifndef EAI_NONAME +# define EAI_NONAME (INT_MAX - 3) +#endif +#ifndef EAI_SYSTEM +# define EAI_SYSTEM (INT_MAX - 4) +#endif +#ifndef EAI_FAMILY +# define EAI_FAMILY (INT_MAX - 5) #endif #ifndef HAVE_STRUCT_ADDRINFO @@ -143,7 +158,7 @@ #endif /* !HAVE_GETADDRINFO */ #if !defined(HAVE_GAI_STRERROR) && !defined(HAVE_CONST_GAI_STRERROR_PROTO) -#define gai_strerror(a) (ssh_gai_strerror(a)) +#define gai_strerror(a) (_ssh_compat_gai_strerror(a)) char *gai_strerror(int); #endif /* !HAVE_GAI_STRERROR */
--- a/gendss.c Thu Nov 06 13:33:06 2008 +0000 +++ b/gendss.c Wed May 16 22:54:51 2012 +0800 @@ -37,17 +37,17 @@ #ifdef DROPBEAR_DSS -static void getq(dss_key *key); -static void getp(dss_key *key, unsigned int size); -static void getg(dss_key *key); -static void getx(dss_key *key); -static void gety(dss_key *key); +static void getq(dropbear_dss_key *key); +static void getp(dropbear_dss_key *key, unsigned int size); +static void getg(dropbear_dss_key *key); +static void getx(dropbear_dss_key *key); +static void gety(dropbear_dss_key *key); -dss_key * gen_dss_priv_key(unsigned int size) { +dropbear_dss_key * gen_dss_priv_key(unsigned int size) { - dss_key *key; + dropbear_dss_key *key; - key = (dss_key*)m_malloc(sizeof(dss_key)); + key = m_malloc(sizeof(*key)); key->p = (mp_int*)m_malloc(sizeof(mp_int)); key->q = (mp_int*)m_malloc(sizeof(mp_int)); @@ -68,7 +68,7 @@ } -static void getq(dss_key *key) { +static void getq(dropbear_dss_key *key) { char buf[QSIZE]; @@ -81,12 +81,12 @@ /* 18 rounds are required according to HAC */ if (mp_prime_next_prime(key->q, 18, 0) != MP_OKAY) { - fprintf(stderr, "dss key generation failed\n"); + fprintf(stderr, "DSS key generation failed\n"); exit(1); } } -static void getp(dss_key *key, unsigned int size) { +static void getp(dropbear_dss_key *key, unsigned int size) { DEF_MP_INT(tempX); DEF_MP_INT(tempC); @@ -100,7 +100,7 @@ /* 2*q */ if (mp_mul_d(key->q, 2, &temp2q) != MP_OKAY) { - fprintf(stderr, "dss key generation failed\n"); + fprintf(stderr, "DSS key generation failed\n"); exit(1); } @@ -117,25 +117,25 @@ /* C = X mod 2q */ if (mp_mod(&tempX, &temp2q, &tempC) != MP_OKAY) { - fprintf(stderr, "dss key generation failed\n"); + fprintf(stderr, "DSS key generation failed\n"); exit(1); } /* P = X - (C - 1) = X - C + 1*/ if (mp_sub(&tempX, &tempC, &tempP) != MP_OKAY) { - fprintf(stderr, "dss key generation failed\n"); + fprintf(stderr, "DSS key generation failed\n"); exit(1); } if (mp_add_d(&tempP, 1, key->p) != MP_OKAY) { - fprintf(stderr, "dss key generation failed\n"); + fprintf(stderr, "DSS key generation failed\n"); exit(1); } /* now check for prime, 5 rounds is enough according to HAC */ /* result == 1 => p is prime */ if (mp_prime_is_prime(key->p, 5, &result) != MP_OKAY) { - fprintf(stderr, "dss key generation failed\n"); + fprintf(stderr, "DSS key generation failed\n"); exit(1); } } while (!result); @@ -145,7 +145,7 @@ m_free(buf); } -static void getg(dss_key * key) { +static void getg(dropbear_dss_key * key) { DEF_MP_INT(div); DEF_MP_INT(h); @@ -155,11 +155,11 @@ /* get div=(p-1)/q */ if (mp_sub_d(key->p, 1, &val) != MP_OKAY) { - fprintf(stderr, "dss key generation failed\n"); + fprintf(stderr, "DSS key generation failed\n"); exit(1); } if (mp_div(&val, key->q, &div, NULL) != MP_OKAY) { - fprintf(stderr, "dss key generation failed\n"); + fprintf(stderr, "DSS key generation failed\n"); exit(1); } @@ -168,12 +168,12 @@ do { /* now keep going with g=h^div mod p, until g > 1 */ if (mp_exptmod(&h, &div, key->p, key->g) != MP_OKAY) { - fprintf(stderr, "dss key generation failed\n"); + fprintf(stderr, "DSS key generation failed\n"); exit(1); } if (mp_add_d(&h, 1, &h) != MP_OKAY) { - fprintf(stderr, "dss key generation failed\n"); + fprintf(stderr, "DSS key generation failed\n"); exit(1); } @@ -182,15 +182,15 @@ mp_clear_multi(&div, &h, &val, NULL); } -static void getx(dss_key *key) { +static void getx(dropbear_dss_key *key) { gen_random_mpint(key->q, key->x); } -static void gety(dss_key *key) { +static void gety(dropbear_dss_key *key) { if (mp_exptmod(key->g, key->x, key->p, key->y) != MP_OKAY) { - fprintf(stderr, "dss key generation failed\n"); + fprintf(stderr, "DSS key generation failed\n"); exit(1); } }
--- a/gendss.h Thu Nov 06 13:33:06 2008 +0000 +++ b/gendss.h Wed May 16 22:54:51 2012 +0800 @@ -29,7 +29,7 @@ #ifdef DROPBEAR_DSS -dss_key * gen_dss_priv_key(unsigned int size); +dropbear_dss_key * gen_dss_priv_key(unsigned int size); #endif /* DROPBEAR_DSS */
--- a/genrsa.c Thu Nov 06 13:33:06 2008 +0000 +++ b/genrsa.c Wed May 16 22:54:51 2012 +0800 @@ -37,14 +37,14 @@ mp_int* rsa_e, unsigned int size); /* mostly taken from libtomcrypt's rsa key generation routine */ -rsa_key * gen_rsa_priv_key(unsigned int size) { +dropbear_rsa_key * gen_rsa_priv_key(unsigned int size) { - rsa_key * key; + dropbear_rsa_key * key; DEF_MP_INT(pminus); DEF_MP_INT(qminus); DEF_MP_INT(lcm); - key = (rsa_key*)m_malloc(sizeof(rsa_key)); + key = m_malloc(sizeof(*key)); key->e = (mp_int*)m_malloc(sizeof(mp_int)); key->n = (mp_int*)m_malloc(sizeof(mp_int)); @@ -58,7 +58,7 @@ seedrandom(); if (mp_set_int(key->e, RSA_E) != MP_OKAY) { - fprintf(stderr, "rsa generation failed\n"); + fprintf(stderr, "RSA generation failed\n"); exit(1); } @@ -66,20 +66,20 @@ getrsaprime(key->q, &qminus, key->e, size/2); if (mp_mul(key->p, key->q, key->n) != MP_OKAY) { - fprintf(stderr, "rsa generation failed\n"); + fprintf(stderr, "RSA generation failed\n"); exit(1); } /* lcm(p-1, q-1) */ if (mp_lcm(&pminus, &qminus, &lcm) != MP_OKAY) { - fprintf(stderr, "rsa generation failed\n"); + fprintf(stderr, "RSA generation failed\n"); exit(1); } /* de = 1 mod lcm(p-1,q-1) */ /* therefore d = (e^-1) mod lcm(p-1,q-1) */ if (mp_invmod(key->e, &lcm, key->d) != MP_OKAY) { - fprintf(stderr, "rsa generation failed\n"); + fprintf(stderr, "RSA generation failed\n"); exit(1); } @@ -108,18 +108,18 @@ /* find the next integer which is prime, 8 round of miller-rabin */ if (mp_prime_next_prime(prime, 8, 0) != MP_OKAY) { - fprintf(stderr, "rsa generation failed\n"); + fprintf(stderr, "RSA generation failed\n"); exit(1); } /* subtract one to get p-1 */ if (mp_sub_d(prime, 1, primeminus) != MP_OKAY) { - fprintf(stderr, "rsa generation failed\n"); + fprintf(stderr, "RSA generation failed\n"); exit(1); } /* check relative primality to e */ if (mp_gcd(primeminus, rsa_e, &temp_gcd) != MP_OKAY) { - fprintf(stderr, "rsa generation failed\n"); + fprintf(stderr, "RSA generation failed\n"); exit(1); } } while (mp_cmp_d(&temp_gcd, 1) != MP_EQ); /* while gcd(p-1, e) != 1 */
--- a/genrsa.h Thu Nov 06 13:33:06 2008 +0000 +++ b/genrsa.h Wed May 16 22:54:51 2012 +0800 @@ -29,7 +29,7 @@ #ifdef DROPBEAR_RSA -rsa_key * gen_rsa_priv_key(unsigned int size); +dropbear_rsa_key * gen_rsa_priv_key(unsigned int size); #endif /* DROPBEAR_RSA */
--- a/includes.h Thu Nov 06 13:33:06 2008 +0000 +++ b/includes.h Wed May 16 22:54:51 2012 +0800 @@ -120,8 +120,14 @@ #include <libgen.h> #endif +#ifdef BUNDLED_LIBTOM #include "libtomcrypt/src/headers/tomcrypt.h" #include "libtommath/tommath.h" +#else +#include <tomcrypt.h> +#include <tommath.h> +#endif + #include "compat.h" #include "fake-rfc2553.h"
--- a/kex.h Thu Nov 06 13:33:06 2008 +0000 +++ b/kex.h Wed May 16 22:54:51 2012 +0800 @@ -52,8 +52,8 @@ unsigned sentkexinit : 1; /*set when we've sent/recv kexinit packet */ unsigned recvkexinit : 1; unsigned firstfollows : 1; /* true when first_kex_packet_follows is set */ - unsigned sentnewkeys : 1; /* set once we've send/recv'ed MSG_NEWKEYS*/ - unsigned recvnewkeys : 1; + unsigned sentnewkeys : 1; /* set once we've send MSG_NEWKEYS (will be cleared once we have also received */ + unsigned recvnewkeys : 1; /* set once we've received MSG_NEWKEYS (cleared once we have also sent */ unsigned donefirstkex : 1; /* Set to 1 after the first kex has completed, ie the transport layer has been set up */
--- a/libtomcrypt/src/headers/tomcrypt_custom.h Thu Nov 06 13:33:06 2008 +0000 +++ b/libtomcrypt/src/headers/tomcrypt_custom.h Wed May 16 22:54:51 2012 +0800 @@ -78,7 +78,7 @@ /* #define LTC_CLEAN_STACK */ /* disable all file related functions */ -/* #define LTC_NO_FILE */ +#define LTC_NO_FILE /* disable all forms of ASM */ /* #define LTC_NO_ASM */ @@ -118,14 +118,18 @@ #define LTC_CTR_MODE #endif -#if defined(DROPBEAR_DSS) && defined(DSS_PROTOK) -#define SHA512 +#define SHA1 + +#ifdef DROPBEAR_MD5 +#define MD5 #endif -#define SHA1 +#ifdef DROPBEAR_SHA256 +#define SHA256 +#endif -#ifdef DROPBEAR_MD5_HMAC -#define MD5 +#ifdef DROPBEAR_SHA512 +#define SHA512 #endif #define LTC_HMAC
--- a/libtommath/Makefile.in Thu Nov 06 13:33:06 2008 +0000 +++ b/libtommath/Makefile.in Wed May 16 22:54:51 2012 +0800 @@ -170,8 +170,8 @@ rm -f *.bat *.pdf *.o *.a *.obj *.lib *.exe *.dll etclib/*.o demo/demo.o test ltmtest mpitest mtest/mtest mtest/mtest.exe \ *.idx *.toc *.log *.aux *.dvi *.lof *.ind *.ilg *.ps *.log *.s mpi.c *.da *.dyn *.dpi tommath.tex `find . -type f | grep [~] | xargs` *.lo *.la rm -rf .libs - cd etc ; MAKE=${MAKE} ${MAKE} clean - cd pics ; MAKE=${MAKE} ${MAKE} clean + -cd etc && MAKE=${MAKE} ${MAKE} clean + -cd pics && MAKE=${MAKE} ${MAKE} clean #zipup the project (take that!) no_oops: clean
--- a/libtommath/bn_mp_exptmod_fast.c Thu Nov 06 13:33:06 2008 +0000 +++ b/libtommath/bn_mp_exptmod_fast.c Wed May 16 22:54:51 2012 +0800 @@ -67,13 +67,13 @@ /* init M array */ /* init first cell */ - if ((err = mp_init(&M[1])) != MP_OKAY) { + if ((err = mp_init_size(&M[1], P->alloc)) != MP_OKAY) { return err; } /* now init the second half of the array */ for (x = 1<<(winsize-1); x < (1 << winsize); x++) { - if ((err = mp_init(&M[x])) != MP_OKAY) { + if ((err = mp_init_size(&M[x], P->alloc)) != MP_OKAY) { for (y = 1<<(winsize-1); y < x; y++) { mp_clear (&M[y]); } @@ -133,7 +133,7 @@ } /* setup result */ - if ((err = mp_init (&res)) != MP_OKAY) { + if ((err = mp_init_size (&res, P->alloc)) != MP_OKAY) { goto LBL_M; }
--- a/libtommath/bn_mp_init_copy.c Thu Nov 06 13:33:06 2008 +0000 +++ b/libtommath/bn_mp_init_copy.c Wed May 16 22:54:51 2012 +0800 @@ -20,7 +20,7 @@ { int res; - if ((res = mp_init (a)) != MP_OKAY) { + if ((res = mp_init_size (a, b->used)) != MP_OKAY) { return res; } return mp_copy (b, a);
--- a/libtommath/bn_mp_mod.c Thu Nov 06 13:33:06 2008 +0000 +++ b/libtommath/bn_mp_mod.c Wed May 16 22:54:51 2012 +0800 @@ -22,7 +22,7 @@ mp_int t; int res; - if ((res = mp_init (&t)) != MP_OKAY) { + if ((res = mp_init_size (&t, b->used)) != MP_OKAY) { return res; }
--- a/libtommath/bn_mp_mulmod.c Thu Nov 06 13:33:06 2008 +0000 +++ b/libtommath/bn_mp_mulmod.c Wed May 16 22:54:51 2012 +0800 @@ -21,7 +21,7 @@ int res; mp_int t; - if ((res = mp_init (&t)) != MP_OKAY) { + if ((res = mp_init_size (&t, c->used)) != MP_OKAY) { return res; }
--- a/libtommath/bn_mp_prime_next_prime.c Thu Nov 06 13:33:06 2008 +0000 +++ b/libtommath/bn_mp_prime_next_prime.c Wed May 16 22:54:51 2012 +0800 @@ -143,7 +143,7 @@ /* is this prime? */ for (x = 0; x < t; x++) { - mp_set(&b, ltm_prime_tab[t]); + mp_set(&b, ltm_prime_tab[x]); if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) { goto LBL_ERR; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/list.c Wed May 16 22:54:51 2012 +0800 @@ -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; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/list.h Wed May 16 22:54:51 2012 +0800 @@ -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 */
--- a/options.h Thu Nov 06 13:33:06 2008 +0000 +++ b/options.h Wed May 16 22:54:51 2012 +0800 @@ -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 @@ -63,8 +64,9 @@ #define ENABLE_SVR_LOCALTCPFWD #define ENABLE_SVR_REMOTETCPFWD -/* Enable Authentication Agent Forwarding - server only for now */ -#define ENABLE_AGENTFWD +/* Enable Authentication Agent Forwarding */ +#define ENABLE_SVR_AGENTFWD +#define ENABLE_CLI_AGENTFWD /* Note: Both ENABLE_CLI_PROXYCMD and ENABLE_CLI_NETCAT must be set to @@ -82,17 +84,21 @@ * Protocol RFC requires 3DES and recommends AES128 for interoperability. * Including multiple keysize variants the same cipher * (eg AES256 as well as AES128) will result in a minimal size increase.*/ -/* #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 -*/ + +/* Enable "Counter Mode" for ciphers. This is more secure than normal + * CBC mode against certain attacks. This adds around 1kB to binary + * size and is recommended for most cases */ +#define DROPBEAR_ENABLE_CTR_MODE /* You can compile with no encryption if you want. In some circumstances - * this could be safe securitywise, though make sure you know what + * this could be safe security-wise, though make sure you know what * you're doing. Anyone can see everything that goes over the wire, so * the only safe auth method is public key. You'll have to disable all other * ciphers above in the client if you want to use this, or implement cipher @@ -101,38 +107,32 @@ * The best way to do things is probably make normal compile of dropbear with * all ciphers including "none" as the server, then recompile a special * "dbclient-insecure" client. */ -#define DROPBEAR_NONE_CIPHER - -/* Enable "Counter Mode" for ciphers. This is more secure than normal - * CBC mode against certain attacks. This adds around 1kB to binary - * size and is recommended for most cases */ -#define DROPBEAR_ENABLE_CTR_MODE +/* #define DROPBEAR_NONE_CIPHER */ /* Message Integrity - at least one required. * Protocol RFC requires sha1 and recommends sha1-96. - * sha1-96 may be of use for slow links, as it has a smaller overhead. + * sha1-96 is of use for slow links as it has a smaller overhead. * - * Note: there's no point disabling sha1 to save space, since it's used - * for the random number generator and public-key cryptography anyway. + * There's no reason to disable sha1 or sha1-96 to save space since it's + * used for the random number generator and public-key cryptography anyway. * Disabling it here will just stop it from being used as the integrity portion * of the ssh protocol. * * These hashes are also used for public key fingerprints in logs. * If you disable MD5, Dropbear will fall back to SHA1 fingerprints, * which are not the standard form. */ -/* #define DROPBEAR_SHA1_HMAC #define DROPBEAR_SHA1_96_HMAC +/*#define DROPBEAR_SHA2_256_HMAC*/ +/*#define DROPBEAR_SHA2_512_HMAC*/ #define DROPBEAR_MD5_HMAC -*/ /* You can also disable integrity. Don't bother disabling this if you're - * still using a cipher, it's relatively cheap. Don't disable this if you're - * using 'none' cipher, since it's dead simple to run arbitrary commands - * on the remote host. Beware. + * still using a cipher, it's relatively cheap. If you disable this it's dead + * simple to run arbitrary commands on the remote host. Beware. * Note again, for the client you will have to disable other hashes above * to use this. */ -#define DROPBEAR_NONE_INTEGRITY +/* #define DROPBEAR_NONE_INTEGRITY */ /* Hostkey/public key algorithms - at least one required, these are used * for hostkey as well as for verifying signatures with pubkey auth. @@ -149,9 +149,21 @@ /* Define DSS_PROTOK to use PuTTY's method of generating the value k for dss, * rather than just from the random byte source. Undefining this will save you * ~4k in binary size with static uclibc, but your DSS hostkey could be exposed - * if the random number source isn't good. In general this isn't required */ + * if the random number source isn't good. It happened to Sony. + * On systems with a decent random source this isn't required. */ /* #define DSS_PROTOK */ +/* Control the memory/performance/compression tradeoff for zlib. + * Set windowBits=8 for least memory usage, see your system's + * zlib.h for full details. + * Default settings (windowBits=15) will use 256kB for compression + * windowBits=8 will use 129kB for compression. + * Both modes will use ~35kB for decompression (using windowBits=15 for + * interoperability) */ +#ifndef DROPBEAR_ZLIB_WINDOW_BITS +#define DROPBEAR_ZLIB_WINDOW_BITS 15 +#endif + /* Whether to do reverse DNS lookups. */ #define DO_HOST_LOOKUP @@ -167,10 +179,11 @@ /* Authentication Types - at least one required. RFC Draft requires pubkey auth, and recommends password */ -/* Note: PAM auth is quite simple, and only works for PAM modules which just do +/* Note: PAM auth is quite simple and only works for PAM modules which just do * a simple "Login: " "Password: " (you can edit the strings in svr-authpam.c). - * It's useful for systems like OS X where standard password crypts don't work, - * but there's an interface via a PAM module - don't bother using it otherwise. + * It's useful for systems like OS X where standard password crypts don't work + * but there's an interface via a PAM module. It won't work for more complex + * PAM challenge/response. * You can't enable both PASSWORD and PAM. */ #define ENABLE_SVR_PASSWORD_AUTH @@ -178,11 +191,17 @@ /*#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 +/* Define this to allow logging in to accounts that have no password specified. + * Public key logins are allowed for blank-password accounts regardless of this + * setting. */ +/* #define ALLOW_BLANK_PASSWORD */ + #define ENABLE_CLI_PASSWORD_AUTH #define ENABLE_CLI_PUBKEY_AUTH #define ENABLE_CLI_INTERACT_AUTH @@ -244,7 +263,7 @@ /* 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 @@ -270,18 +289,28 @@ 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 */ #define DEFAULT_KEEPALIVE 0 +/* Ensure that data is received within IDLE_TIMEOUT seconds. This can +be overridden at runtime with -I. 0 disables idle timeouts */ +#define DEFAULT_IDLE_TIMEOUT 0 + /* The default path. This will often get replaced by the shell */ #define DEFAULT_PATH "/usr/bin:/bin"
--- a/packet.c Thu Nov 06 13:33:06 2008 +0000 +++ b/packet.c Wed May 16 22:54:51 2012 +0800 @@ -35,11 +35,13 @@ #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_COMPRESS_INCR 100 #define ZLIB_DECOMPRESS_INCR 100 #ifndef DISABLE_ZLIB static buffer* buf_decompress(buffer* buf, unsigned int len); @@ -51,6 +53,8 @@ int len, written; buffer * writebuf = NULL; + time_t now; + unsigned packet_type; TRACE(("enter write_packet")) dropbear_assert(!isempty(&ses.writequeue)); @@ -58,7 +62,10 @@ /* Get the next buffer in the queue of encrypted packets to write*/ writebuf = (buffer*)examine(&ses.writequeue); - len = writebuf->len - writebuf->pos; + /* The last byte of the buffer is not to be transmitted, but is + * a cleartext packet_type indicator */ + packet_type = writebuf->data[writebuf->len-1]; + len = writebuf->len - 1 - writebuf->pos; dropbear_assert(len > 0); /* Try to write as much as possible */ written = write(ses.sock_out, buf_getptr(writebuf, len), len); @@ -68,11 +75,16 @@ TRACE(("leave writepacket: EINTR")) return; } else { - dropbear_exit("error writing"); + dropbear_exit("Error writing"); } } - ses.last_packet_time = time(NULL); + now = time(NULL); + ses.last_trx_packet_time = now; + + if (packet_type != SSH_MSG_IGNORE) { + ses.last_packet_time = now; + } if (written == 0) { ses.remoteclosed(); @@ -101,18 +113,18 @@ 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; } @@ -120,7 +132,6 @@ /* 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); @@ -133,7 +144,7 @@ TRACE(("leave read_packet: EINTR or EAGAIN")) return; } else { - dropbear_exit("error reading: %s", strerror(errno)); + dropbear_exit("Error reading: %s", strerror(errno)); } } @@ -150,71 +161,75 @@ /* 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)); + 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) { - dropbear_exit("error decrypting"); + &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; - buf_setpos(ses.readbuf, blocksize); + TRACE(("packet size is %d, block %d mac %d", len, blocksize, macsize)) + /* check packet length */ if ((len > RECV_MAX_PACKET_LEN) || (len < MIN_PACKET_LEN + macsize) || ((len - macsize) % blocksize != 0)) { - dropbear_exit("bad packet size %d", len); + dropbear_exit("Integrity error (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 */ @@ -226,69 +241,58 @@ 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"); + dropbear_exit("Bad packet size %d", len); } - buf_setpos(ses.decryptreadbuf, PACKET_PAYLOAD_OFF); + buf_setpos(ses.readbuf, PACKET_PAYLOAD_OFF); #ifndef DISABLE_ZLIB if (is_compress_recv()) { /* decompress */ - ses.payload = buf_decompress(ses.decryptreadbuf, len); + ses.payload = buf_decompress(ses.readbuf, len); } else #endif { /* copy payload */ ses.payload = buf_new(len); - memcpy(ses.payload->data, buf_getptr(ses.decryptreadbuf, len), len); + memcpy(ses.payload->data, buf_getptr(ses.readbuf, len), len); buf_incrlen(ses.payload, len); } - buf_free(ses.decryptreadbuf); - ses.decryptreadbuf = NULL; + buf_free(ses.readbuf); + ses.readbuf = NULL; buf_setpos(ses.payload, 0); ses.recvseq++; @@ -296,49 +300,22 @@ 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) { - - 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; - } +static int checkmac() { - /* calculate the mac */ - if (hmac_init(&hmac, - find_hash(ses.keys->recv_algo_mac->hashdesc->name), - ses.keys->recvmackey, - ses.keys->recv_algo_mac->keysize) - != CRYPT_OK) { - dropbear_exit("HMAC error"); - } + unsigned char mac_bytes[MAX_MAC_LEN]; + unsigned int mac_size, contents_len; - /* sequence number */ - STORE32H(ses.recvseq, tempbuf); - if (hmac_process(&hmac, tempbuf, 4) != CRYPT_OK) { - dropbear_exit("HMAC error"); - } + mac_size = ses.keys->trans.algo_mac->hashsize; + contents_len = ses.readbuf->len - mac_size; - buf_setpos(sourcebuf, 0); - len = sourcebuf->len; - if (hmac_process(&hmac, buf_getptr(sourcebuf, len), len) != CRYPT_OK) { - dropbear_exit("HMAC error"); - } - - bufsize = sizeof(tempbuf); - if (hmac_done(&hmac, tempbuf, &bufsize) != CRYPT_OK) { - dropbear_exit("HMAC error"); - } + buf_setpos(ses.readbuf, 0); + make_mac(ses.recvseq, &ses.keys->recv, ses.readbuf, contents_len, mac_bytes); /* compare the hash */ - if (memcmp(tempbuf, buf_getptr(macbuf, macsize), macsize) != 0) { + buf_setpos(ses.readbuf, contents_len); + if (memcmp(mac_bytes, buf_getptr(ses.readbuf, mac_size), mac_size) != 0) { return DROPBEAR_FAILURE; } else { return DROPBEAR_SUCCESS; @@ -353,7 +330,7 @@ buffer * ret; z_streamp zstream; - zstream = ses.keys->recv_zstream; + zstream = ses.keys->recv.zstream; ret = buf_new(len); zstream->avail_in = len; @@ -449,52 +426,75 @@ 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 type; - unsigned int clear_len; + unsigned char blocksize, mac_size; + buffer * writebuf; /* the packet which will go on the wire. This is + encrypted in-place. */ + unsigned char packet_type; + unsigned int len, encrypt_buf_size; + unsigned char mac_bytes[MAX_MAC_LEN]; - type = ses.writepayload->data[0]; TRACE(("enter encrypt_packet()")) - TRACE(("encrypt_packet type is %d", type)) + + buf_setpos(ses.writepayload, 0); + packet_type = buf_getbyte(ses.writepayload); + buf_setpos(ses.writepayload, 0); + + TRACE(("encrypt_packet type is %d", packet_type)) - if (!ses.dataallowed && !packet_is_okay_kex(type)) { + if ((!ses.dataallowed && !packet_is_okay_kex(packet_type)) + || ses.kexstate.sentnewkeys) { /* During key exchange only particular packets are allowed. - Since this type isn't OK we just enqueue it to send + Since this packet_type isn't OK we just enqueue it to send after the KEX, see maybe_flush_reply_queue */ + + /* We also enqueue packets here when we have sent a MSG_NEWKEYS + * packet but are yet to received one. For simplicity we just switch + * over all the keys at once. This is the 'ses.kexstate.sentnewkeys' + * case. */ enqueue_reply_packet(); return; } - blocksize = ses.keys->trans_algo_crypt->blocksize; - macsize = 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; + blocksize = ses.keys->trans.algo_crypt->blocksize; + mac_size = ses.keys->trans.algo_mac->hashsize; + /* Encrypted packet len is payload+5. We need to then make sure + * there is enough space for padding or MIN_PACKET_LEN. + * Add extra 3 since we need at least 4 bytes of padding */ + encrypt_buf_size = (ses.writepayload->len+4+1) + + MAX(MIN_PACKET_LEN, blocksize) + 3 + /* add space for the MAC at the end */ + + mac_size #ifndef DISABLE_ZLIB - clear_len += ZLIB_COMPRESS_INCR; /* bit of a kludge, but we can't know len*/ + /* some extra in case 'compression' makes it larger */ + + ZLIB_COMPRESS_INCR #endif - clearwritebuf = buf_new(clear_len); - buf_setlen(clearwritebuf, PACKET_PAYLOAD_OFF); - buf_setpos(clearwritebuf, PACKET_PAYLOAD_OFF); + /* and an extra cleartext (stripped before transmission) byte for the + * packet type */ + + 1; - buf_setpos(ses.writepayload, 0); + writebuf = buf_new(encrypt_buf_size); + buf_setlen(writebuf, PACKET_PAYLOAD_OFF); + buf_setpos(writebuf, PACKET_PAYLOAD_OFF); #ifndef DISABLE_ZLIB /* compression */ if (is_compress_trans()) { - buf_compress(clearwritebuf, ses.writepayload, ses.writepayload->len); + int compress_delta; + buf_compress(writebuf, ses.writepayload, ses.writepayload->len); + compress_delta = (writebuf->len - PACKET_PAYLOAD_OFF) - ses.writepayload->len; + + /* Handle the case where 'compress' increased the size. */ + if (compress_delta > ZLIB_COMPRESS_INCR) { + buf_resize(writebuf, writebuf->size + compress_delta); + } } else #endif { - memcpy(buf_getwriteptr(clearwritebuf, ses.writepayload->len), + memcpy(buf_getwriteptr(writebuf, ses.writepayload->len), buf_getptr(ses.writepayload, ses.writepayload->len), ses.writepayload->len); - buf_incrwritepos(clearwritebuf, ses.writepayload->len); + buf_incrwritepos(writebuf, ses.writepayload->len); } /* finished with payload */ @@ -503,53 +503,48 @@ /* length of padding - packet length must be a multiple of blocksize, * with a minimum of 4 bytes of padding */ - padlen = blocksize - (clearwritebuf->len) % blocksize; + padlen = blocksize - (writebuf->len) % blocksize; if (padlen < 4) { padlen += blocksize; } /* check for min packet length */ - if (clearwritebuf->len + padlen < MIN_PACKET_LEN) { + if (writebuf->len + padlen < MIN_PACKET_LEN) { padlen += blocksize; } - buf_setpos(clearwritebuf, 0); + buf_setpos(writebuf, 0); /* packet length excluding the packetlength uint32 */ - buf_putint(clearwritebuf, clearwritebuf->len + padlen - 4); + buf_putint(writebuf, writebuf->len + padlen - 4); /* padding len */ - buf_putbyte(clearwritebuf, padlen); + buf_putbyte(writebuf, padlen); /* actual padding */ - buf_setpos(clearwritebuf, clearwritebuf->len); - buf_incrlen(clearwritebuf, padlen); - genrandom(buf_getptr(clearwritebuf, padlen), padlen); + buf_setpos(writebuf, writebuf->len); + buf_incrlen(writebuf, padlen); + genrandom(buf_getptr(writebuf, padlen), padlen); - /* do the actual encryption */ - buf_setpos(clearwritebuf, 0); - /* create a new writebuffer, this is freed when it has been put on the - * wire by writepacket() */ - writebuf = buf_new(clearwritebuf->len + macsize); + make_mac(ses.transseq, &ses.keys->trans, writebuf, writebuf->len, mac_bytes); - /* 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); + /* 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); - /* now add a hmac and we're done */ - writemac(writebuf, clearwritebuf); + /* stick the MAC on it */ + buf_putbytes(writebuf, mac_bytes, mac_size); - /* clearwritebuf is finished with */ - buf_free(clearwritebuf); - clearwritebuf = NULL; - - /* enqueue the packet for sending */ + /* The last byte of the buffer stores the cleartext packet_type. It is not + * transmitted but is used for transmit timeout purposes */ + buf_putbyte(writebuf, packet_type); + /* enqueue the packet for sending. It will get freed after transmission. */ buf_setpos(writebuf, 0); enqueue(&ses.writequeue, (void*)writebuf); @@ -562,47 +557,43 @@ /* 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")) } @@ -619,29 +610,29 @@ 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(),
--- a/packet.h Thu Nov 06 13:33:06 2008 +0000 +++ b/packet.h Wed May 16 22:54:51 2012 +0800 @@ -44,6 +44,6 @@ #define PACKET_PADDING_OFF 4 #define PACKET_PAYLOAD_OFF 5 -#define INIT_READBUF 200 +#define INIT_READBUF 128 #endif /* _PACKET_H_ */
--- a/process-packet.c Thu Nov 06 13:33:06 2008 +0000 +++ b/process-packet.c Wed May 16 22:54:51 2012 +0800 @@ -52,6 +52,8 @@ ses.lastpacket = type; + ses.last_packet_time = time(NULL); + /* These packets we can receive at any time */ switch(type) { @@ -63,20 +65,19 @@ case SSH_MSG_UNIMPLEMENTED: /* debugging XXX */ TRACE(("SSH_MSG_UNIMPLEMENTED")) - dropbear_exit("received SSH_MSG_UNIMPLEMENTED"); + dropbear_exit("Received SSH_MSG_UNIMPLEMENTED"); case SSH_MSG_DISCONNECT: /* TODO cleanup? */ dropbear_close("Disconnect received"); } - /* This applies for KEX, where the spec says the next packet MUST be * NEWKEYS */ if (ses.requirenext != 0) { if (ses.requirenext != type) { /* TODO send disconnect? */ - dropbear_exit("unexpected packet type %d, expected %d", type, + dropbear_exit("Unexpected packet type %d, expected %d", type, ses.requirenext); } else { /* Got what we expected */ @@ -98,7 +99,7 @@ * NOTE: if the protocol changes and new types are added, revisit this * assumption */ if ( !ses.authstate.authdone && type > MAX_UNAUTH_PACKET_TYPE ) { - dropbear_exit("received message %d before userauth", type); + dropbear_exit("Received message %d before userauth", type); } for (i = 0; ; i++) {
--- a/random.c Thu Nov 06 13:33:06 2008 +0000 +++ b/random.c Wed May 16 22:54:51 2012 +0800 @@ -64,32 +64,23 @@ #ifdef DROPBEAR_RANDOM_DEV readfd = open(DROPBEAR_RANDOM_DEV, O_RDONLY); if (readfd < 0) { - dropbear_exit("couldn't open random device"); + dropbear_exit("Couldn't open random device"); } #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"); - } - /* todo - try various common locations */ - if (connect(readfd, (struct sockaddr*)&egdsock, - sizeof(struct sockaddr_un)) < 0) { - dropbear_exit("couldn't open random device"); + dropbear_exit("Couldn't open random device"); } if (buflen > 255) - dropbear_exit("can't request more than 255 bytes from egd"); + dropbear_exit("Can't request more than 255 bytes from egd"); egdcmd[0] = 0x02; /* blocking read */ egdcmd[1] = (unsigned char)buflen; if (write(readfd, egdcmd, 2) < 0) - dropbear_exit("can't send command to egd"); + dropbear_exit("Can't send command to egd"); #endif /* read the actual random data */ @@ -118,7 +109,7 @@ if (readlen < 0 && errno == EINTR) { continue; } - dropbear_exit("error reading random source"); + dropbear_exit("Error reading random source"); } readpos += readlen; } while (readpos < buflen);
--- a/rsa.c Thu Nov 06 13:33:06 2008 +0000 +++ b/rsa.c Wed May 16 22:54:51 2012 +0800 @@ -38,7 +38,7 @@ #ifdef DROPBEAR_RSA -static void rsa_pad_em(rsa_key * key, +static void rsa_pad_em(dropbear_rsa_key * key, const unsigned char * data, unsigned int len, mp_int * rsa_em); @@ -46,7 +46,7 @@ * The key will have the same format as buf_put_rsa_key. * These should be freed with rsa_key_free. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ -int buf_get_rsa_pub_key(buffer* buf, rsa_key *key) { +int buf_get_rsa_pub_key(buffer* buf, dropbear_rsa_key *key) { int ret = DROPBEAR_FAILURE; TRACE(("enter buf_get_rsa_pub_key")) @@ -67,7 +67,7 @@ } if (mp_count_bits(key->n) < MIN_RSA_KEYLEN) { - dropbear_log(LOG_WARNING, "rsa key too short"); + dropbear_log(LOG_WARNING, "RSA key too short"); goto out; } @@ -84,7 +84,7 @@ /* Same as buf_get_rsa_pub_key, but reads private bits at the end. * Loads a private rsa key from a buffer * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ -int buf_get_rsa_priv_key(buffer* buf, rsa_key *key) { +int buf_get_rsa_priv_key(buffer* buf, dropbear_rsa_key *key) { int ret = DROPBEAR_FAILURE; TRACE(("enter buf_get_rsa_priv_key")) @@ -137,7 +137,7 @@ /* Clear and free the memory used by a public or private key */ -void rsa_key_free(rsa_key *key) { +void rsa_key_free(dropbear_rsa_key *key) { TRACE(("enter rsa_key_free")) @@ -175,7 +175,7 @@ * mp_int e * mp_int n */ -void buf_put_rsa_pub_key(buffer* buf, rsa_key *key) { +void buf_put_rsa_pub_key(buffer* buf, dropbear_rsa_key *key) { TRACE(("enter buf_put_rsa_pub_key")) dropbear_assert(key != NULL); @@ -189,7 +189,7 @@ } /* Same as buf_put_rsa_pub_key, but with the private "x" key appended */ -void buf_put_rsa_priv_key(buffer* buf, rsa_key *key) { +void buf_put_rsa_priv_key(buffer* buf, dropbear_rsa_key *key) { TRACE(("enter buf_put_rsa_priv_key")) @@ -213,7 +213,7 @@ #ifdef DROPBEAR_SIGNKEY_VERIFY /* Verify a signature in buf, made on data by the key given. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ -int buf_rsa_verify(buffer * buf, rsa_key *key, const unsigned char* data, +int buf_rsa_verify(buffer * buf, dropbear_rsa_key *key, const unsigned char* data, unsigned int len) { unsigned int slen; @@ -270,7 +270,7 @@ /* Sign the data presented with key, writing the signature contents * to the buffer */ -void buf_put_rsa_sign(buffer* buf, rsa_key *key, const unsigned char* data, +void buf_put_rsa_sign(buffer* buf, dropbear_rsa_key *key, const unsigned char* data, unsigned int len) { unsigned int nsize, ssize; @@ -302,26 +302,26 @@ /* rsa_s used as a temp var*/ if (mp_exptmod(&rsa_tmp2, key->e, key->n, &rsa_s) != MP_OKAY) { - dropbear_exit("rsa error"); + dropbear_exit("RSA error"); } if (mp_invmod(&rsa_tmp2, key->n, &rsa_tmp3) != MP_OKAY) { - dropbear_exit("rsa error"); + dropbear_exit("RSA error"); } if (mp_mulmod(&rsa_tmp1, &rsa_s, key->n, &rsa_tmp2) != MP_OKAY) { - dropbear_exit("rsa error"); + dropbear_exit("RSA error"); } /* rsa_tmp2 is em' */ /* s' = (em')^d mod n */ if (mp_exptmod(&rsa_tmp2, key->d, key->n, &rsa_tmp1) != MP_OKAY) { - dropbear_exit("rsa error"); + dropbear_exit("RSA error"); } /* rsa_tmp1 is s' */ /* rsa_tmp3 is r^(-1) mod n */ /* s = (s')r^(-1) mod n */ if (mp_mulmod(&rsa_tmp1, &rsa_tmp3, key->n, &rsa_s) != MP_OKAY) { - dropbear_exit("rsa error"); + dropbear_exit("RSA error"); } #else @@ -329,7 +329,7 @@ /* s = em^d mod n */ /* rsa_tmp1 is em */ if (mp_exptmod(&rsa_tmp1, key->d, key->n, &rsa_s) != MP_OKAY) { - dropbear_exit("rsa error"); + dropbear_exit("RSA error"); } #endif /* RSA_BLINDING */ @@ -351,7 +351,7 @@ } if (mp_to_unsigned_bin(&rsa_s, buf_getwriteptr(buf, ssize)) != MP_OKAY) { - dropbear_exit("rsa error"); + dropbear_exit("RSA error"); } buf_incrwritepos(buf, ssize); mp_clear(&rsa_s); @@ -376,7 +376,7 @@ * * rsa_em must be a pointer to an initialised mp_int. */ -static void rsa_pad_em(rsa_key * key, +static void rsa_pad_em(dropbear_rsa_key * key, const unsigned char * data, unsigned int len, mp_int * rsa_em) {
--- a/rsa.h Thu Nov 06 13:33:06 2008 +0000 +++ b/rsa.h Wed May 16 22:54:51 2012 +0800 @@ -32,29 +32,28 @@ #define RSA_SIGNATURE_SIZE 4+7+4+40 -struct RSA_key { +typedef struct { mp_int* n; mp_int* e; + /* d, p, and q are private parts */ mp_int* d; mp_int* p; mp_int* q; -}; +} dropbear_rsa_key; -typedef struct RSA_key rsa_key; - -void buf_put_rsa_sign(buffer* buf, rsa_key *key, const unsigned char* data, +void buf_put_rsa_sign(buffer* buf, dropbear_rsa_key *key, const unsigned char* data, unsigned int len); #ifdef DROPBEAR_SIGNKEY_VERIFY -int buf_rsa_verify(buffer * buf, rsa_key *key, const unsigned char* data, +int buf_rsa_verify(buffer * buf, dropbear_rsa_key *key, const unsigned char* data, unsigned int len); #endif -int buf_get_rsa_pub_key(buffer* buf, rsa_key *key); -int buf_get_rsa_priv_key(buffer* buf, rsa_key *key); -void buf_put_rsa_pub_key(buffer* buf, rsa_key *key); -void buf_put_rsa_priv_key(buffer* buf, rsa_key *key); -void rsa_key_free(rsa_key *key); +int buf_get_rsa_pub_key(buffer* buf, dropbear_rsa_key *key); +int buf_get_rsa_priv_key(buffer* buf, dropbear_rsa_key *key); +void buf_put_rsa_pub_key(buffer* buf, dropbear_rsa_key *key); +void buf_put_rsa_priv_key(buffer* buf, dropbear_rsa_key *key); +void rsa_key_free(dropbear_rsa_key *key); #endif /* DROPBEAR_RSA */
--- a/runopts.h Thu Nov 06 13:33:06 2008 +0000 +++ b/runopts.h Wed May 16 22:54:51 2012 +0800 @@ -37,7 +37,16 @@ int listen_fwd_all; #endif unsigned int recv_window; - unsigned int keepalive_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; @@ -111,13 +120,20 @@ 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 @@ -127,7 +143,6 @@ #ifdef ENABLE_CLI_PROXYCMD char *proxycmd; #endif - } cli_runopts; extern cli_runopts cli_opts;
--- a/scp.c Thu Nov 06 13:33:06 2008 +0000 +++ b/scp.c Wed May 16 22:54:51 2012 +0800 @@ -130,22 +130,22 @@ fprintf(stderr, " %s", a->list[i]); fprintf(stderr, "\n"); } -#ifdef __uClinux__ +#ifdef USE_VFORK pid = vfork(); #else pid = fork(); -#endif /* __uClinux__ */ +#endif if (pid == -1) fatal("do_local_cmd: fork: %s", strerror(errno)); if (pid == 0) { execvp(a->list[0], a->list); perror(a->list[0]); -#ifdef __uClinux__ +#ifdef USE_VFORK _exit(1); #else exit(1); -#endif /* __uClinux__ */ +#endif } do_cmd_pid = pid; @@ -171,6 +171,16 @@ * assigns the input and output file descriptors on success. */ +static void +arg_setup(char *host, char *remuser, char *cmd) +{ + replacearg(&args, 0, "%s", ssh_program); + if (remuser != NULL) + addargs(&args, "-l%s", remuser); + addargs(&args, "%s", host); + addargs(&args, "%s", cmd); +} + int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc) { @@ -198,22 +208,18 @@ close(reserved[0]); close(reserved[1]); - /* uClinux needs to build the args here before vforking, - otherwise we do it later on. */ -#ifdef __uClinux__ - replacearg(&args, 0, "%s", ssh_program); - if (remuser != NULL) - addargs(&args, "-l%s", remuser); - addargs(&args, "%s", host); - addargs(&args, "%s", cmd); -#endif /* __uClinux__ */ + /* uClinux needs to build the args here before vforking, + otherwise we do it later on. */ +#ifdef USE_VFORK + arg_setup(host, remuser, cmd); +#endif /* Fork a child to execute the command on the remote host using ssh. */ -#ifdef __uClinux__ +#ifdef USE_VFORK do_cmd_pid = vfork(); #else do_cmd_pid = fork(); -#endif /* __uClinux__ */ +#endif if (do_cmd_pid == 0) { /* Child. */ @@ -224,27 +230,22 @@ close(pin[0]); close(pout[1]); -#ifndef __uClinux__ - replacearg(&args, 0, "%s", ssh_program); - if (remuser != NULL) - addargs(&args, "-l%s", remuser); - addargs(&args, "%s", host); - addargs(&args, "%s", cmd); -#endif /* __uClinux__ */ +#ifdef USE_VFORK + arg_setup(host, remuser, cmd); +#endif execvp(ssh_program, args.list); perror(ssh_program); -#ifndef __uClinux__ - exit(1); +#ifdef USE_VFORK + _exit(1); #else - _exit(1); -#endif /* __uClinux__ */ + exit(1); +#endif } else if (do_cmd_pid == -1) { fatal("fork: %s", strerror(errno)); } - -#ifdef __uClinux__ +#ifdef USE_VFORK /* clean up command */ /* pop cmd */ xfree(args.list[args.num-1]); @@ -260,7 +261,7 @@ args.list[args.num-1]=NULL; args.num--; } -#endif /* __uClinux__ */ +#endif /* Parent. Close the other side, and return the local side. */ close(pin[0]); @@ -343,7 +344,7 @@ 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); @@ -364,12 +365,12 @@ addargs(&args, "-v"); verbose_mode = 1; break; + case 'q': #ifdef PROGRESS_METER - case 'q': addargs(&args, "-q"); showprogress = 0; +#endif break; -#endif /* Server options. */ case 'd': @@ -492,9 +493,13 @@ 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) @@ -769,7 +774,7 @@ { static struct timeval bwstart, bwend; static int lamt, thresh = 16384; - u_int64_t waitlen; + uint64_t waitlen; struct timespec ts, rm; if (!timerisset(&bwstart)) {
--- a/session.h Thu Nov 06 13:33:06 2008 +0000 +++ b/session.h Wed May 16 22:54:51 2012 +0800 @@ -37,11 +37,12 @@ #include "packet.h" #include "tcpfwd.h" #include "chansession.h" +#include "dbutil.h" 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 +52,45 @@ void fill_passwd(const char* username); /* Server */ -void svr_session(int sock, int childpipe, char *remotehost, char *addrstring); -void svr_dropbear_exit(int exitcode, const char* format, va_list param); +void svr_session(int sock, int childpipe); +void svr_dropbear_exit(int exitcode, const char* format, va_list param) ATTRIB_NORETURN; 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 - [email protected] 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 + [email protected] delayed compression case) */ }; struct packetlist; @@ -116,8 +111,6 @@ 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 +121,7 @@ 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 */ @@ -148,12 +140,16 @@ unsigned char lastpacket; /* What the last received packet type was */ - int signal_pipe[2]; /* stores endpoints of a self-pipe used for + int signal_pipe[2]; /* stores endpoints of a self-pipe used for race-free signal handling */ - time_t last_packet_time; /* time of the last packet transmission, for + time_t last_trx_packet_time; /* time of the last packet transmission, for keepalive purposes */ + time_t last_packet_time; /* time of the last packet transmission or receive, for + idle timeout purposes */ + + /* KEX/encryption related */ struct KEXState kexstate; struct key_context *keys; @@ -165,6 +161,9 @@ 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)*/ @@ -216,6 +215,13 @@ /* The numeric address they connected from, used for logging */ char * addrstring; + /* The resolved remote address, used for lastlog etc */ + char *remotehost; + +#ifdef USE_VFORK + pid_t server_pid; +#endif + }; typedef enum { @@ -264,7 +270,7 @@ 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
--- a/signkey.c Thu Nov 06 13:33:06 2008 +0000 +++ b/signkey.c Wed May 16 22:54:51 2012 +0800 @@ -40,8 +40,10 @@ #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 @@ -60,7 +62,7 @@ return SSH_SIGNKEY_DSS; } #endif - dropbear_exit("bad key type %d", type); + dropbear_exit("Bad key type %d", type); return NULL; /* notreached */ } @@ -81,6 +83,8 @@ } #endif + TRACE(("signkey_type_from_name unexpected key type.")) + return DROPBEAR_SIGNKEY_NONE; } @@ -101,8 +105,11 @@ 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", keytype)) *type = keytype; @@ -112,7 +119,7 @@ #ifdef DROPBEAR_DSS if (keytype == DROPBEAR_SIGNKEY_DSS) { dss_key_free(key->dsskey); - key->dsskey = (dss_key*)m_malloc(sizeof(dss_key)); + key->dsskey = m_malloc(sizeof(*key->dsskey)); ret = buf_get_dss_pub_key(buf, key->dsskey); if (ret == DROPBEAR_FAILURE) { m_free(key->dsskey); @@ -122,7 +129,7 @@ #ifdef DROPBEAR_RSA if (keytype == DROPBEAR_SIGNKEY_RSA) { rsa_key_free(key->rsakey); - key->rsakey = (rsa_key*)m_malloc(sizeof(rsa_key)); + key->rsakey = m_malloc(sizeof(*key->rsakey)); ret = buf_get_rsa_pub_key(buf, key->rsakey); if (ret == DROPBEAR_FAILURE) { m_free(key->rsakey); @@ -165,7 +172,7 @@ #ifdef DROPBEAR_DSS if (keytype == DROPBEAR_SIGNKEY_DSS) { dss_key_free(key->dsskey); - key->dsskey = (dss_key*)m_malloc(sizeof(dss_key)); + key->dsskey = m_malloc(sizeof(*key->dsskey)); ret = buf_get_dss_priv_key(buf, key->dsskey); if (ret == DROPBEAR_FAILURE) { m_free(key->dsskey); @@ -175,7 +182,7 @@ #ifdef DROPBEAR_RSA if (keytype == DROPBEAR_SIGNKEY_RSA) { rsa_key_free(key->rsakey); - key->rsakey = (rsa_key*)m_malloc(sizeof(rsa_key)); + key->rsakey = m_malloc(sizeof(*key->rsakey)); ret = buf_get_rsa_priv_key(buf, key->rsakey); if (ret == DROPBEAR_FAILURE) { m_free(key->rsakey); @@ -208,7 +215,7 @@ } #endif if (pubkeys->len == 0) { - dropbear_exit("bad key types in buf_put_pub_key"); + dropbear_exit("Bad key types in buf_put_pub_key"); } buf_setpos(pubkeys, 0); @@ -239,7 +246,7 @@ return; } #endif - dropbear_exit("bad key types in put pub key"); + dropbear_exit("Bad key types in put pub key"); } void sign_key_free(sign_key *key) { @@ -255,6 +262,8 @@ key->rsakey = NULL; #endif + m_free(key->filename); + m_free(key); TRACE(("leave sign_key_free")) } @@ -358,7 +367,6 @@ const unsigned char *data, unsigned int len) { buffer *sigblob; - sigblob = buf_new(MAX_PUBKEY_SIZE); #ifdef DROPBEAR_DSS @@ -372,9 +380,8 @@ } #endif if (sigblob->len == 0) { - dropbear_exit("non-matching signing type"); + dropbear_exit("Non-matching signing type"); } - buf_setpos(sigblob, 0); buf_putstring(buf, buf_getptr(sigblob, sigblob->len), sigblob->len); @@ -405,7 +412,7 @@ memcmp(ident, SSH_SIGNKEY_DSS, identlen) == 0) { m_free(ident); if (key->dsskey == NULL) { - dropbear_exit("no dss key to verify signature"); + dropbear_exit("No DSS key to verify signature"); } return buf_dss_verify(buf, key->dsskey, data, len); } @@ -415,14 +422,14 @@ if (memcmp(ident, SSH_SIGNKEY_RSA, identlen) == 0) { m_free(ident); if (key->rsakey == NULL) { - dropbear_exit("no rsa key to verify signature"); + dropbear_exit("No RSA key to verify signature"); } return buf_rsa_verify(buf, key->rsakey, data, len); } #endif m_free(ident); - dropbear_exit("non-matching signing type"); + dropbear_exit("Non-matching signing type"); return DROPBEAR_FAILURE; } #endif /* DROPBEAR_SIGNKEY_VERIFY */
--- a/signkey.h Thu Nov 06 13:33:06 2008 +0000 +++ b/signkey.h Wed May 16 22:54:51 2012 +0800 @@ -29,13 +29,27 @@ #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; + dropbear_dss_key * dsskey; #endif #ifdef DROPBEAR_RSA - rsa_key * rsakey; + dropbear_rsa_key * rsakey; #endif };
--- a/ssh.h Thu Nov 06 13:33:06 2008 +0000 +++ b/ssh.h Wed May 16 22:54:51 2012 +0800 @@ -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
--- a/sshpty.c Thu Nov 06 13:33:06 2008 +0000 +++ b/sshpty.c Wed May 16 22:54:51 2012 +0800 @@ -234,7 +234,7 @@ return 1; } - dropbear_log(LOG_WARNING, "failed to open any /dev/pty?? devices"); + dropbear_log(LOG_WARNING, "Failed to open any /dev/pty?? devices"); return 0; #endif /* HAVE_DEV_PTS_AND_PTC */ #endif /* USE_DEV_PTMX */
--- a/svr-agentfwd.c Thu Nov 06 13:33:06 2008 +0000 +++ b/svr-agentfwd.c Wed May 16 22:54:51 2012 +0800 @@ -27,7 +27,7 @@ #include "includes.h" -#ifndef DISABLE_AGENTFWD +#ifdef ENABLE_SVR_AGENTFWD #include "agentfwd.h" #include "session.h" @@ -49,9 +49,8 @@ /* Handles client requests to start agent forwarding, sets up listening socket. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ -int agentreq(struct ChanSess * chansess) { - - int fd; +int svr_agentreq(struct ChanSess * chansess) { + int fd = -1; if (!svr_pubkey_allows_agentfwd()) { return DROPBEAR_FAILURE; @@ -91,8 +90,9 @@ return DROPBEAR_SUCCESS; fail: + m_close(fd); /* cleanup */ - agentcleanup(chansess); + svr_agentcleanup(chansess); return DROPBEAR_FAILURE; } @@ -118,7 +118,7 @@ /* 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 +137,7 @@ } /* close the socket, remove the socket-file */ -void agentcleanup(struct ChanSess * chansess) { +void svr_agentcleanup(struct ChanSess * chansess) { char *path = NULL; uid_t uid; @@ -157,7 +157,7 @@ gid = getgid(); if ((setegid(ses.authstate.pw_gid)) < 0 || (seteuid(ses.authstate.pw_uid)) < 0) { - dropbear_exit("failed to set euid"); + dropbear_exit("Failed to set euid"); } /* 2 for "/" and "\0" */ @@ -172,7 +172,7 @@ if ((seteuid(uid)) < 0 || (setegid(gid)) < 0) { - dropbear_exit("failed to revert euid"); + dropbear_exit("Failed to revert euid"); } m_free(chansess->agentfile); @@ -181,7 +181,7 @@ } -static const struct ChanType chan_agent = { +static const struct ChanType chan_svr_agent = { 0, /* sepfds */ "[email protected]", NULL, @@ -194,7 +194,7 @@ /* 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 { @@ -220,7 +220,7 @@ gid = getgid(); if ((setegid(ses.authstate.pw_gid)) < 0 || (seteuid(ses.authstate.pw_uid)) < 0) { - dropbear_exit("failed to set euid"); + dropbear_exit("Failed to set euid"); } memset((void*)&addr, 0x0, sizeof(addr)); @@ -263,7 +263,7 @@ out: if ((seteuid(uid)) < 0 || (setegid(gid)) < 0) { - dropbear_exit("failed to revert euid"); + dropbear_exit("Failed to revert euid"); } return ret; }
--- a/svr-algo.c Thu Nov 06 13:33:06 2008 +0000 +++ b/svr-algo.c Wed May 16 22:54:51 2012 +0800 @@ -68,7 +68,7 @@ remotealgos[count] = &algolist[i+1]; count++; } - if (count == MAX_PROPOSED_ALGO) { + if (count >= MAX_PROPOSED_ALGO) { break; } }
--- a/svr-auth.c Thu Nov 06 13:33:06 2008 +0000 +++ b/svr-auth.c Wed May 16 22:54:51 2012 +0800 @@ -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); @@ -140,15 +141,6 @@ dropbear_exit("unknown service in auth"); } - /* user wants to know what methods are supported */ - if (methodlen == AUTH_METHOD_NONE_LEN && - strncmp(methodname, AUTH_METHOD_NONE, - AUTH_METHOD_NONE_LEN) == 0) { - TRACE(("recv_msg_userauth_request: 'none' request")) - send_msg_userauth_failure(0, 0); - goto out; - } - /* check username is good before continuing */ if (checkusername(username, userlen) == DROPBEAR_FAILURE) { /* username is invalid/no shell/etc - send failure */ @@ -157,6 +149,31 @@ goto out; } + /* user wants to know what methods are supported */ + if (methodlen == AUTH_METHOD_NONE_LEN && + strncmp(methodname, AUTH_METHOD_NONE, + AUTH_METHOD_NONE_LEN) == 0) { + TRACE(("recv_msg_userauth_request: 'none' request")) +#ifdef ALLOW_BLANK_PASSWORD + if (!svr_opts.noauthpass + && !(svr_opts.norootpass && ses.authstate.pw_uid == 0) + && ses.authstate.pw_passwd[0] == '\0') + { + dropbear_log(LOG_NOTICE, + "Auth succeeded with blank password for '%s' from %s", + ses.authstate.pw_name, + svr_ses.addrstring); + send_msg_userauth_success(); + goto out; + } + else +#endif + { + send_msg_userauth_failure(0, 0); + goto out; + } + } + #ifdef ENABLE_SVR_PASSWORD_AUTH if (!svr_opts.noauthpass && !(svr_opts.norootpass && ses.authstate.pw_uid == 0) ) { @@ -204,8 +221,7 @@ } -/* Check that the username exists, has a non-empty password, and has a valid - * shell. +/* Check that the username exists and isn't disallowed (root), and has a valid shell. * returns DROPBEAR_SUCCESS on valid username, DROPBEAR_FAILURE on failure */ static int checkusername(unsigned char *username, unsigned int userlen) { @@ -221,7 +237,7 @@ strcmp(username, ses.authstate.username) != 0) { /* the username needs resetting */ if (ses.authstate.username != NULL) { - dropbear_log(LOG_WARNING, "client trying multiple usernames from %s", + dropbear_log(LOG_WARNING, "Client trying multiple usernames from %s", svr_ses.addrstring); m_free(ses.authstate.username); } @@ -234,7 +250,7 @@ if (!ses.authstate.pw_name) { TRACE(("leave checkusername: user '%s' doesn't exist", username)) dropbear_log(LOG_WARNING, - "login attempt for nonexistent user from %s", + "Login attempt for nonexistent user from %s", svr_ses.addrstring); send_msg_userauth_failure(0, 1); return DROPBEAR_FAILURE; @@ -248,15 +264,6 @@ return DROPBEAR_FAILURE; } - /* check for an empty password */ - if (ses.authstate.pw_passwd[0] == '\0') { - TRACE(("leave checkusername: empty pword")) - dropbear_log(LOG_WARNING, "user '%s' has blank password, rejected", - ses.authstate.pw_name); - send_msg_userauth_failure(0, 1); - return DROPBEAR_FAILURE; - } - TRACE(("shell is %s", ses.authstate.pw_shell)) /* check that the shell is set */ @@ -280,7 +287,7 @@ /* no matching shell */ endusershell(); TRACE(("no matching shell")) - dropbear_log(LOG_WARNING, "user '%s' has invalid shell, rejected", + dropbear_log(LOG_WARNING, "User '%s' has invalid shell, rejected", ses.authstate.pw_name); send_msg_userauth_failure(0, 1); return DROPBEAR_FAILURE; @@ -337,7 +344,11 @@ 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); ses.authstate.failcount++; }
--- a/svr-authpam.c Thu Nov 06 13:33:06 2008 +0000 +++ b/svr-authpam.c Wed May 16 22:54:51 2012 +0800 @@ -56,11 +56,7 @@ struct UserDataS* userDatap = (struct UserDataS*) appdata_ptr; unsigned int msg_len = 0; unsigned int i = 0; - - const char* message = (*msg)->msg; - - /* make a copy we can strip */ - char * compare_message = m_strdup(message); + char * compare_message = NULL; TRACE(("enter pamConvFunc")) @@ -71,15 +67,10 @@ dropbear_log(LOG_INFO, "pamConvFunc() called with >1 messages: not supported."); return PAM_CONV_ERR; } + + /* make a copy we can strip */ + compare_message = m_strdup((*msg)->msg); - TRACE(("msg_style is %d", (*msg)->msg_style)) - if (compare_message) { - TRACE(("message is '%s'", compare_message)) - } else { - TRACE(("null message")) - } - - /* Make the string lowercase. */ msg_len = strlen(compare_message); for (i = 0; i < msg_len; i++) { @@ -101,8 +92,9 @@ if (!(strcmp(compare_message, "password:") == 0)) { /* 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)", + different pam modules/implementations. If you need + to add an entry here please mail the Dropbear developer */ + dropbear_log(LOG_NOTICE, "PAM unknown prompt '%s' (no echo)", compare_message); rc = PAM_CONV_ERR; break; @@ -123,12 +115,16 @@ 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)", + different pam modules/implementations. If you need + to add an entry here please mail the Dropbear developer */ + dropbear_log(LOG_NOTICE, "PAM unknown prompt '%s' (with echo)", compare_message); rc = PAM_CONV_ERR; break; @@ -212,7 +208,10 @@ 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); */ @@ -220,7 +219,7 @@ dropbear_log(LOG_WARNING, "pam_authenticate() failed, rc=%d, %s\n", rc, pam_strerror(pamHandlep, rc)); dropbear_log(LOG_WARNING, - "bad PAM password attempt for '%s' from %s", + "Bad PAM password attempt for '%s' from %s", ses.authstate.pw_name, svr_ses.addrstring); send_msg_userauth_failure(0, 1); @@ -231,7 +230,7 @@ dropbear_log(LOG_WARNING, "pam_acct_mgmt() failed, rc=%d, %s\n", rc, pam_strerror(pamHandlep, rc)); dropbear_log(LOG_WARNING, - "bad PAM password attempt for '%s' from %s", + "Bad PAM password attempt for '%s' from %s", ses.authstate.pw_name, svr_ses.addrstring); send_msg_userauth_failure(0, 1);
--- a/svr-authpasswd.c Thu Nov 06 13:33:06 2008 +0000 +++ b/svr-authpasswd.c Wed May 16 22:54:51 2012 +0800 @@ -36,9 +36,6 @@ * appropriate */ void svr_auth_password() { -#ifdef HAVE_SHADOW_H - struct spwd *spasswd = NULL; -#endif char * passwdcrypt = NULL; /* the crypt from /etc/passwd or /etc/shadow */ char * testcrypt = NULL; /* crypt generated from the user's password sent */ unsigned char * password; @@ -47,29 +44,12 @@ unsigned int changepw; passwdcrypt = ses.authstate.pw_passwd; -#ifdef HAVE_SHADOW_H - /* get the shadow password if possible */ - spasswd = getspnam(ses.authstate.pw_name); - if (spasswd != NULL && spasswd->sp_pwdp != NULL) { - passwdcrypt = spasswd->sp_pwdp; - } -#endif #ifdef DEBUG_HACKCRYPT /* debugging crypt for non-root testing with shadows */ passwdcrypt = DEBUG_HACKCRYPT; #endif - /* check for empty password - need to do this again here - * since the shadow password may differ to that tested - * in auth.c */ - if (passwdcrypt[0] == '\0') { - dropbear_log(LOG_WARNING, "user '%s' has blank password, rejected", - ses.authstate.pw_name); - send_msg_userauth_failure(0, 1); - return; - } - /* check if client wants to change password */ changepw = buf_getbool(ses.payload); if (changepw) { @@ -85,21 +65,28 @@ m_burn(password, passwordlen); m_free(password); + /* check for empty password */ + if (passwdcrypt[0] == '\0') { + dropbear_log(LOG_WARNING, "User '%s' has blank password, rejected", + ses.authstate.pw_name); + send_msg_userauth_failure(0, 1); + return; + } + if (strcmp(testcrypt, passwdcrypt) == 0) { /* successful authentication */ dropbear_log(LOG_NOTICE, - "password auth succeeded for '%s' from %s", + "Password auth succeeded for '%s' from %s", ses.authstate.pw_name, svr_ses.addrstring); send_msg_userauth_success(); } else { dropbear_log(LOG_WARNING, - "bad password attempt for '%s' from %s", + "Bad password attempt for '%s' from %s", ses.authstate.pw_name, svr_ses.addrstring); send_msg_userauth_failure(0, 1); } - } #endif
--- a/svr-authpubkey.c Thu Nov 06 13:33:06 2008 +0000 +++ b/svr-authpubkey.c Wed May 16 22:54:51 2012 +0800 @@ -135,12 +135,12 @@ if (buf_verify(ses.payload, key, buf_getptr(signbuf, signbuf->len), signbuf->len) == DROPBEAR_SUCCESS) { dropbear_log(LOG_NOTICE, - "pubkey auth succeeded for '%s' with key %s from %s", + "Pubkey auth succeeded for '%s' with key %s from %s", ses.authstate.pw_name, fp, svr_ses.addrstring); send_msg_userauth_success(); } else { dropbear_log(LOG_WARNING, - "pubkey auth bad signature for '%s' with key %s from %s", + "Pubkey auth bad signature for '%s' with key %s from %s", ses.authstate.pw_name, fp, svr_ses.addrstring); send_msg_userauth_failure(0, 1); } @@ -198,7 +198,7 @@ /* check that we can use the algo */ if (have_algo(algo, algolen, sshhostkey) == DROPBEAR_FAILURE) { dropbear_log(LOG_WARNING, - "pubkey auth attempt with unknown algo for '%s' from %s", + "Pubkey auth attempt with unknown algo for '%s' from %s", ses.authstate.pw_name, svr_ses.addrstring); goto out; }
--- a/svr-authpubkeyoptions.c Thu Nov 06 13:33:06 2008 +0000 +++ b/svr-authpubkeyoptions.c Wed May 16 22:54:51 2012 +0800 @@ -88,10 +88,21 @@ 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) - chansess->cmd = ses.authstate.pubkey_options->forced_command; + if (ses.authstate.pubkey_options) { + if (chansess->cmd) { + /* original_command takes ownership */ + chansess->original_command = chansess->cmd; + } else { + chansess->original_command = m_strdup(""); + } + chansess->cmd = m_strdup(ses.authstate.pubkey_options->forced_command); +#ifdef LOG_COMMANDS + dropbear_log(LOG_INFO, "Command forced to '%s'", chansess->original_command); +#endif + } } /* Free potential public key options */ @@ -124,7 +135,6 @@ 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) { @@ -133,7 +143,7 @@ ses.authstate.pubkey_options->no_port_forwarding_flag = 1; goto next_option; } -#ifdef ENABLE_AGENTFWD +#ifdef ENABLE_SVR_AGENTFWD if (match_option(options_buf, "no-agent-forwarding") == DROPBEAR_SUCCESS) { dropbear_log(LOG_WARNING, "Agent forwarding disabled."); ses.authstate.pubkey_options->no_agent_forwarding_flag = 1;
--- a/svr-chansession.c Thu Nov 06 13:33:06 2008 +0000 +++ b/svr-chansession.c Wed May 16 22:54:51 2012 +0800 @@ -33,7 +33,6 @@ #include "termcodes.h" #include "ssh.h" #include "random.h" -#include "utmp.h" #include "x11fwd.h" #include "agentfwd.h" #include "runopts.h" @@ -138,6 +137,7 @@ sa_chld.sa_handler = sesssigchild_handler; sa_chld.sa_flags = SA_NOCLDSTOP; + sigemptyset(&sa_chld.sa_mask); sigaction(SIGCHLD, &sa_chld, NULL); TRACE(("leave sigchld handler")) } @@ -218,10 +218,13 @@ struct ChanSess *chansess; + TRACE(("new chansess %p", channel)) + dropbear_assert(channel->typedata == NULL); chansess = (struct ChanSess*)m_malloc(sizeof(struct ChanSess)); chansess->cmd = NULL; + chansess->connection_string = NULL; chansess->pid = 0; /* pty details */ @@ -240,7 +243,7 @@ chansess->x11authcookie = NULL; #endif -#ifndef DISABLE_AGENTFWD +#ifdef ENABLE_SVR_AGENTFWD chansess->agentlistener = NULL; chansess->agentfile = NULL; chansess->agentdir = NULL; @@ -250,6 +253,14 @@ } +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) { @@ -271,10 +282,13 @@ m_free(chansess->cmd); m_free(chansess->term); +#ifdef ENABLE_SVR_PUBKEY_OPTIONS + m_free(chansess->original_command); +#endif + 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); @@ -286,8 +300,8 @@ x11cleanup(chansess); #endif -#ifndef DISABLE_AGENTFWD - agentcleanup(chansess); +#ifdef ENABLE_SVR_AGENTFWD + svr_agentcleanup(chansess); #endif /* clear child pid entries */ @@ -344,9 +358,9 @@ } else if (strcmp(type, "x11-req") == 0) { ret = x11req(chansess); #endif -#ifndef DISABLE_AGENTFWD +#ifdef ENABLE_SVR_AGENTFWD } else if (strcmp(type, "[email protected]") == 0) { - ret = agentreq(chansess); + ret = svr_agentreq(chansess); #endif } else if (strcmp(type, "signal") == 0) { ret = sessionsignal(chansess); @@ -448,7 +462,7 @@ TRACE(("term mode str %d p->l %d p->p %d", len, ses.payload->len , ses.payload->pos)); if (len != ses.payload->len - ses.payload->pos) { - dropbear_exit("bad term mode string"); + dropbear_exit("Bad term mode string"); } if (len == 0) { @@ -513,7 +527,7 @@ } } if (tcsetattr(chansess->master, TCSANOW, &termio) < 0) { - dropbear_log(LOG_INFO, "error setting terminal attributes"); + dropbear_log(LOG_INFO, "Error setting terminal attributes"); } TRACE(("leave get_termmodes")) } @@ -543,7 +557,7 @@ /* allocate the pty */ if (chansess->master != -1) { - dropbear_exit("multiple pty requests"); + dropbear_exit("Multiple pty requests"); } if (pty_allocate(&chansess->master, &chansess->slave, namebuf, 64) == 0) { TRACE(("leave sessionpty: failed to allocate pty")) @@ -552,7 +566,7 @@ chansess->tty = (char*)m_strdup(namebuf); if (!chansess->tty) { - dropbear_exit("out of memory"); /* TODO disconnect */ + dropbear_exit("Out of memory"); /* TODO disconnect */ } pw = getpwnam(ses.authstate.pw_name); @@ -570,6 +584,21 @@ 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 +618,6 @@ return DROPBEAR_FAILURE; } - /* take public key option 'command' into account */ - svr_pubkey_set_forced_command(chansess); - if (iscmd) { /* "exec" */ if (chansess->cmd == NULL) { @@ -616,17 +642,26 @@ } } } + + /* take public key option 'command' into account */ + svr_pubkey_set_forced_command(chansess); #ifdef LOG_COMMANDS if (chansess->cmd) { - dropbear_log(LOG_INFO, "user %s executing '%s'", + dropbear_log(LOG_INFO, "User %s executing '%s'", ses.authstate.pw_name, chansess->cmd); } else { - dropbear_log(LOG_INFO, "user %s executing login shell", + dropbear_log(LOG_INFO, "User %s executing login shell", ses.authstate.pw_name); } #endif + /* uClinux will vfork(), so there'll be a race as + connection_string is freed below. */ +#ifndef USE_VFORK + chansess->connection_string = make_connection_string(); +#endif + if (chansess->term == NULL) { /* no pty */ ret = noptycommand(channel, chansess); @@ -635,6 +670,10 @@ ret = ptycommand(channel, chansess); } +#ifndef USE_VFORK + m_free(chansess->connection_string); +#endif + if (ret == DROPBEAR_FAILURE) { m_free(chansess->cmd); } @@ -660,6 +699,8 @@ ses.maxfd = MAX(ses.maxfd, channel->readfd); ses.maxfd = MAX(ses.maxfd, channel->errfd); + sleep(1); + addchildpid(chansess, chansess->pid); if (svr_ses.lastexit.exitpid != -1) { @@ -673,6 +714,7 @@ TRACE(("found match for lastexitpid")) svr_ses.childpids[i].chansess->exit = svr_ses.lastexit; svr_ses.lastexit.exitpid = -1; + break; } } } @@ -699,11 +741,11 @@ /* we need to have a pty allocated */ if (chansess->master == -1 || chansess->tty == NULL) { - dropbear_log(LOG_WARNING, "no pty was allocated, couldn't execute"); + dropbear_log(LOG_WARNING, "No pty was allocated, couldn't execute"); return DROPBEAR_FAILURE; } -#ifdef __uClinux__ +#ifdef USE_VFORK pid = vfork(); #else pid = fork(); @@ -736,13 +778,10 @@ /* 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 */ @@ -824,9 +863,9 @@ struct ChanSess *chansess = user_data; char *usershell = NULL; - /* with uClinux we'll have vfork()ed, so don't want to overwrite the - * hostkey. can't think of a workaround to clear it */ -#ifndef __uClinux__ + /* with uClinux we'll have vfork()ed, so don't want to overwrite the + * hostkey. can't think of a workaround to clear it */ +#ifndef USE_VFORK /* wipe the hostkey */ sign_key_free(svr_opts.hostkey); svr_opts.hostkey = NULL; @@ -855,10 +894,10 @@ if ((setgid(ses.authstate.pw_gid) < 0) || (initgroups(ses.authstate.pw_name, ses.authstate.pw_gid) < 0)) { - dropbear_exit("error changing user group"); + dropbear_exit("Error changing user group"); } if (setuid(ses.authstate.pw_uid) < 0) { - dropbear_exit("error changing user"); + dropbear_exit("Error changing user"); } } else { /* ... but if the daemon is the same uid as the requested uid, we don't @@ -869,7 +908,7 @@ * differing groups won't be set (as with initgroups()). The solution * is for the sysadmin not to give out the UID twice */ if (getuid() != ses.authstate.pw_uid) { - dropbear_exit("couldn't change user as non-root"); + dropbear_exit("Couldn't change user as non-root"); } } @@ -883,25 +922,39 @@ 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 (chansess->original_command) { + addnewvar("SSH_ORIGINAL_COMMAND", chansess->original_command); + } +#endif + /* change directory */ if (chdir(ses.authstate.pw_dir) < 0) { - dropbear_exit("error changing directory"); + dropbear_exit("Error changing directory"); } #ifndef DISABLE_X11FWD /* set up X11 forwarding if enabled */ x11setauth(chansess); #endif -#ifndef DISABLE_AGENTFWD +#ifdef ENABLE_SVR_AGENTFWD /* set up agent env variable */ - agentset(chansess); + svr_agentset(chansess); #endif usershell = m_strdup(get_user_shell()); run_shell_command(chansess->cmd, ses.maxfd, usershell); /* only reached on error */ - dropbear_exit("child failed"); + dropbear_exit("Child failed"); } const struct ChanType svrchansess = { @@ -928,6 +981,7 @@ svr_ses.lastexit.exitpid = -1; /* Nothing has exited yet */ sa_chld.sa_handler = sesssigchild_handler; sa_chld.sa_flags = SA_NOCLDSTOP; + sigemptyset(&sa_chld.sa_mask); if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) { dropbear_exit("signal() error"); }
--- a/svr-kex.c Thu Nov 06 13:33:06 2008 +0000 +++ b/svr-kex.c Wed May 16 22:54:51 2012 +0800 @@ -70,7 +70,7 @@ * that, the session hash is calculated, and signed with RSA or DSS. The * result is sent to the client. * - * See the ietf-secsh-transport draft, section 6, for details */ + * See the transport rfc 4253 section 8 for details */ static void send_msg_kexdh_reply(mp_int *dh_e) { DEF_MP_INT(dh_y);
--- a/svr-main.c Thu Nov 06 13:33:06 2008 +0000 +++ b/svr-main.c Wed May 16 22:54:51 2012 +0800 @@ -77,22 +77,16 @@ #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 @@ /* 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 @@ 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 @@ /* 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 @@ } /* 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 { @@ -272,7 +265,7 @@ fork_ret = fork(); #endif if (fork_ret < 0) { - dropbear_log(LOG_WARNING, "error forking: %s", strerror(errno)); + dropbear_log(LOG_WARNING, "Error forking: %s", strerror(errno)); goto out; } else if (fork_ret > 0) { @@ -280,21 +273,21 @@ /* 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 @@ 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 @@ 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 */ @@ -379,6 +370,7 @@ /* catch and reap zombie children */ sa_chld.sa_handler = sigchld_handler; sa_chld.sa_flags = SA_NOCLDSTOP; + sigemptyset(&sa_chld.sa_mask); if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) { dropbear_exit("signal() error"); }
--- a/svr-runopts.c Thu Nov 06 13:33:06 2008 +0000 +++ b/svr-runopts.c Wed May 16 22:54:51 2012 +0800 @@ -43,11 +43,11 @@ " before user login\n" " (default: none)\n" #ifdef DROPBEAR_DSS - "-d dsskeyfile Use dsskeyfile for the dss host key\n" + "-d dsskeyfile Use dsskeyfile for the DSS host key\n" " (default: %s)\n" #endif #ifdef DROPBEAR_RSA - "-r rsakeyfile Use rsakeyfile for the rsa host key\n" + "-r rsakeyfile Use rsakeyfile for the RSA host key\n" " (default: %s)\n" #endif "-F Don't fork into background\n" @@ -81,7 +81,8 @@ "-i Start for inetd\n" #endif "-W <receive_window_buffer> (default %d, larger may be faster, max 1MB)\n" - "-K <keepalive> (0 is never, default %d)\n" + "-K <keepalive> (0 is never, default %d, in seconds)\n" + "-I <idle_timeout> (0 is never, default %d, in seconds)\n" #ifdef DEBUG_TRACE "-v verbose (compiled with DEBUG_TRACE)\n" #endif @@ -93,7 +94,7 @@ RSA_PRIV_FILENAME, #endif DROPBEAR_MAX_PORTS, DROPBEAR_DEFPORT, DROPBEAR_PIDFILE, - DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE); + DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE, DEFAULT_IDLE_TIMEOUT); } void svr_getopts(int argc, char ** argv) { @@ -103,6 +104,7 @@ int nextisport = 0; char* recv_window_arg = NULL; char* keepalive_arg = NULL; + char* idle_timeout_arg = NULL; /* see printhelp() for options */ svr_opts.rsakeyfile = NULL; @@ -123,6 +125,9 @@ #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; @@ -134,7 +139,8 @@ svr_opts.usingsyslog = 1; #endif opts.recv_window = DEFAULT_RECV_WINDOW; - opts.keepalive_secs = DEFAULT_KEEPALIVE; + opts.keepalive_secs = DEFAULT_KEEPALIVE; + opts.idle_timeout_secs = DEFAULT_IDLE_TIMEOUT; #ifdef ENABLE_SVR_REMOTETCPFWD opts.listen_fwd_all = 0; @@ -218,6 +224,9 @@ case 'K': next = &keepalive_arg; break; + case 'I': + next = &idle_timeout_arg; + break; #if defined(ENABLE_SVR_PASSWORD_AUTH) || defined(ENABLE_SVR_PAM_AUTH) case 's': svr_opts.noauthpass = 1; @@ -253,7 +262,7 @@ svr_opts.addresses[0] = m_strdup(DROPBEAR_DEFADDRESS); svr_opts.portcount = 1; } - + if (svr_opts.dsskeyfile == NULL) { svr_opts.dsskeyfile = DSS_PRIV_FILENAME; } @@ -290,9 +299,19 @@ } 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) { + 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; } } @@ -306,7 +325,7 @@ myspec = m_strdup(spec); /* search for ':', that separates address and port */ - svr_opts.ports[svr_opts.portcount] = strchr(myspec, ':'); + svr_opts.ports[svr_opts.portcount] = strrchr(myspec, ':'); if (svr_opts.ports[svr_opts.portcount] == NULL) { /* no ':' -> the whole string specifies just a port */
--- a/svr-service.c Thu Nov 06 13:33:06 2008 +0000 +++ b/svr-service.c Wed May 16 22:54:51 2012 +0800 @@ -57,7 +57,7 @@ if (len == SSH_SERVICE_CONNECTION_LEN && (strncmp(SSH_SERVICE_CONNECTION, name, len) == 0)) { if (ses.authstate.authdone != 1) { - dropbear_exit("request for connection before auth"); + dropbear_exit("Request for connection before auth"); } send_msg_service_accept(name, len); @@ -68,7 +68,7 @@ m_free(name); /* TODO this should be a MSG_DISCONNECT */ - dropbear_exit("unrecognised SSH_MSG_SERVICE_REQUEST"); + dropbear_exit("Unrecognised SSH_MSG_SERVICE_REQUEST"); }
--- a/svr-session.c Thu Nov 06 13:33:06 2008 +0000 +++ b/svr-session.c Wed May 16 22:54:51 2012 +0800 @@ -52,9 +52,7 @@ {SSH_MSG_KEXINIT, recv_msg_kexinit}, {SSH_MSG_KEXDH_INIT, recv_msg_kexdh_init}, /* server */ {SSH_MSG_NEWKEYS, recv_msg_newkeys}, -#ifdef ENABLE_SVR_REMOTETCPFWD {SSH_MSG_GLOBAL_REQUEST, recv_msg_global_request_remotetcp}, -#endif {SSH_MSG_CHANNEL_REQUEST, recv_msg_channel_request}, {SSH_MSG_CHANNEL_OPEN, recv_msg_channel_open}, {SSH_MSG_CHANNEL_EOF, recv_msg_channel_eof}, @@ -74,23 +72,36 @@ 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 USE_VFORK + 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; @@ -125,30 +136,37 @@ if (!sessinitdone) { /* before session init */ snprintf(fmtbuf, sizeof(fmtbuf), - "premature exit: %s", format); + "Premature exit: %s", format); } else if (ses.authstate.authdone) { /* user has authenticated */ snprintf(fmtbuf, sizeof(fmtbuf), - "exit after auth (%s): %s", + "Exit (%s): %s", ses.authstate.pw_name, format); } else if (ses.authstate.pw_name) { /* we have a potential user */ snprintf(fmtbuf, sizeof(fmtbuf), - "exit before auth (user '%s', %d fails): %s", + "Exit before auth (user '%s', %d fails): %s", ses.authstate.pw_name, ses.authstate.failcount, format); } else { /* before userauth */ snprintf(fmtbuf, sizeof(fmtbuf), - "exit before auth: %s", format); + "Exit before auth: %s", format); } _dropbear_log(LOG_INFO, fmtbuf, param); - /* free potential public key options */ - svr_pubkey_options_cleanup(); +#ifdef USE_VFORK + /* For uclinux only the main server process should cleanup - we don't want + * forked children doing that */ + if (svr_ses.server_pid == getpid()) +#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); @@ -183,7 +201,7 @@ local_tm = localtime(×ec); if (local_tm == NULL || strftime(datestr, sizeof(datestr), "%b %d %H:%M:%S", - localtime(×ec)) == 0) + local_tm) == 0) { /* upon failure, just print the epoch-seconds time. */ snprintf(datestr, sizeof(datestr), "%d", (int)timesec);
--- a/svr-tcpfwd.c Thu Nov 06 13:33:06 2008 +0000 +++ b/svr-tcpfwd.c Wed May 16 22:54:51 2012 +0800 @@ -34,24 +34,31 @@ #include "runopts.h" #include "auth.h" -#ifdef ENABLE_SVR_REMOTETCPFWD +static void send_msg_request_failure(); + +static void send_msg_request_failure() { + CHECKCLEARTOWRITE(); + buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_FAILURE); + encrypt_packet(); +} + +#ifndef ENABLE_SVR_REMOTETCPFWD + +/* This is better than SSH_MSG_UNIMPLEMENTED */ +void recv_msg_global_request_remotetcp() { + TRACE(("recv_msg_global_request_remotetcp: remote tcp forwarding not compiled in")) + send_msg_request_failure(); +} + +/* */ +#endif /* !ENABLE_SVR_REMOTETCPFWD */ static void send_msg_request_success(); -static void send_msg_request_failure(); static int svr_cancelremotetcp(); static int svr_remotetcpreq(); static int newtcpdirect(struct Channel * channel); - -const struct ChanType svr_chan_tcpdirect = { - 1, /* sepfds */ - "direct-tcpip", - newtcpdirect, /* init */ - NULL, /* checkclose */ - NULL, /* reqhandler */ - NULL /* closehandler */ -}; - +#ifdef ENABLE_SVR_REMOTETCPFWD static const struct ChanType svr_chan_tcpremote = { 1, /* sepfds */ "forwarded-tcpip", @@ -117,14 +124,6 @@ } -static void send_msg_request_failure() { - - CHECKCLEARTOWRITE(); - buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_FAILURE); - encrypt_packet(); - -} - static int matchtcp(void* typedata1, void* typedata2) { const struct TCPListener *info1 = (struct TCPListener*)typedata1; @@ -173,14 +172,14 @@ static int svr_remotetcpreq() { int ret = DROPBEAR_FAILURE; - unsigned char * bindaddr = NULL; + unsigned char * request_addr = NULL; unsigned int addrlen; struct TCPListener *tcpinfo = NULL; unsigned int port; TRACE(("enter remotetcpreq")) - bindaddr = buf_getstring(ses.payload, &addrlen); + request_addr = buf_getstring(ses.payload, &addrlen); if (addrlen > MAX_IP_LEN) { TRACE(("addr len too long: %d", addrlen)) goto out; @@ -206,24 +205,46 @@ tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener)); tcpinfo->sendaddr = NULL; tcpinfo->sendport = 0; - tcpinfo->listenaddr = bindaddr; tcpinfo->listenport = port; tcpinfo->chantype = &svr_chan_tcpremote; tcpinfo->tcp_type = forwarded; + tcpinfo->request_listenaddr = request_addr; + if (!opts.listen_fwd_all || (strcmp(request_addr, "localhost") == 0) ) { + // NULL means "localhost only" + tcpinfo->listenaddr = NULL; + } + else + { + tcpinfo->listenaddr = request_addr; + } + ret = listen_tcpfwd(tcpinfo); out: if (ret == DROPBEAR_FAILURE) { /* we only free it if a listener wasn't created, since the listener * has to remember it if it's to be cancelled */ - m_free(bindaddr); + m_free(request_addr); m_free(tcpinfo); } TRACE(("leave remotetcpreq")) return ret; } +#endif /* ENABLE_SVR_REMOTETCPFWD */ + +#ifdef ENABLE_SVR_LOCALTCPFWD + +const struct ChanType svr_chan_tcpdirect = { + 1, /* sepfds */ + "direct-tcpip", + newtcpdirect, /* init */ + NULL, /* checkclose */ + NULL, /* reqhandler */ + NULL /* closehandler */ +}; + /* Called upon creating a new direct tcp channel (ie we connect out to an * address */ static int newtcpdirect(struct Channel * channel) { @@ -288,4 +309,4 @@ return err; } -#endif +#endif /* ENABLE_SVR_LOCALTCPFWD */
--- a/svr-x11fwd.c Thu Nov 06 13:33:06 2008 +0000 +++ b/svr-x11fwd.c Wed May 16 22:54:51 2012 +0800 @@ -175,7 +175,7 @@ m_free(chansess->x11authprot); m_free(chansess->x11authcookie); - TRACE(("chansess %s", chansess)) + TRACE(("chansess %x", chansess)) if (chansess->x11listener != NULL) { remove_listener(chansess->x11listener); chansess->x11listener = NULL; @@ -233,7 +233,7 @@ continue; } /* otherwise it was an error we don't know about */ - dropbear_log(LOG_DEBUG, "failed to bind x11 socket"); + dropbear_log(LOG_DEBUG, "Failed to bind x11 socket"); break; } return -1;
--- a/sysoptions.h Thu Nov 06 13:33:06 2008 +0000 +++ b/sysoptions.h Wed May 16 22:54:51 2012 +0800 @@ -4,7 +4,7 @@ *******************************************************************/ #ifndef DROPBEAR_VERSION -#define DROPBEAR_VERSION "0.51" +#define DROPBEAR_VERSION "2012.55" #endif #define LOCAL_IDENT "SSH-2.0-dropbear_" DROPBEAR_VERSION @@ -60,6 +60,7 @@ /* various algorithm identifiers */ #define DROPBEAR_KEX_DH_GROUP1 0 +#define DROPBEAR_KEX_DH_GROUP14 1 #define DROPBEAR_SIGNKEY_ANY 0 #define DROPBEAR_SIGNKEY_RSA 1 @@ -89,7 +90,13 @@ #define MAX_KEY_LEN 32 /* 256 bits for aes256 etc */ #define MAX_IV_LEN 20 /* must be same as max blocksize, and >= SHA1_HASH_SIZE */ +#if defined(DROPBEAR_SHA2_512_HMAC) +#define MAX_MAC_KEY 64 +#elif defined(DROPBEAR_SHA2_256_HMAC) +#define MAX_MAC_KEY 32 +#else #define MAX_MAC_KEY 20 +#endif #define MAX_NAME_LEN 64 /* maximum length of a protocol name, isn't explicitly specified for all protocols (just @@ -98,6 +105,7 @@ #define MAX_PROPOSED_ALGO 20 /* size/count limits */ +/* From transport rfc */ #define MIN_PACKET_LEN 16 #define RECV_MAX_PACKET_LEN (MAX(35000, ((RECV_MAX_PAYLOAD_LEN)+100))) @@ -122,7 +130,7 @@ #define MAX_PRIVKEY_SIZE 1700 /* The maximum size of the bignum portion of the kexhash buffer */ -/* Sect. 8 of the transport draft, K_S + e + f + K */ +/* Sect. 8 of the transport rfc 4253, K_S + e + f + K */ #define KEXHASHBUF_MAX_INTS (1700 + 130 + 130 + 130) #define DROPBEAR_MAX_SOCKS 2 /* IPv4, IPv6 are all we'll get for now. Revisit @@ -142,14 +150,23 @@ #define DROPBEAR_TWOFISH #endif +#ifdef DROPBEAR_MD5_HMAC +#define DROPBEAR_MD5 +#endif + +#ifdef DROPBEAR_SHA2_256_HMAC +#define DROPBEAR_SHA256 +#endif + +#if (defined(DROPBEAR_DSS) && defined(DSS_PROTOK)) \ + || defined(DROPBEAR_SHA2_512_HMAC) +#define DROPBEAR_SHA512 +#endif + #ifndef ENABLE_X11FWD #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 +177,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,10 +185,18 @@ #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 +/* Changing this is inadvisable, it appears to have problems + * with flushing compressed data */ +#define DROPBEAR_ZLIB_MEM_LEVEL 8 + #if defined(ENABLE_SVR_PASSWORD_AUTH) && defined(ENABLE_SVR_PAM_AUTH) #error "You can't turn on PASSWORD and PAM auth both at once. Fix it in options.h" #endif @@ -202,5 +227,14 @@ #define IS_DROPBEAR_CLIENT 1 #else -#error You must compiled with either DROPBEAR_CLIENT or DROPBEAR_SERVER selected -#endif +/* Just building key utils? */ +#define IS_DROPBEAR_SERVER 0 +#define IS_DROPBEAR_CLIENT 0 + +#endif /* neither DROPBEAR_SERVER nor DROPBEAR_CLIENT */ + +#ifndef HAVE_FORK +#define USE_VFORK +#endif /* don't HAVE_FORK */ + +/* no include guard for this file */
--- a/tcp-accept.c Thu Nov 06 13:33:06 2008 +0000 +++ b/tcp-accept.c Wed May 16 22:54:51 2012 +0800 @@ -40,6 +40,7 @@ m_free(tcpinfo->sendaddr); m_free(tcpinfo->listenaddr); + m_free(tcpinfo->request_listenaddr); m_free(tcpinfo); } @@ -61,6 +62,7 @@ if (getnameinfo((struct sockaddr*)&addr, len, ipstring, sizeof(ipstring), portstring, sizeof(portstring), NI_NUMERICHOST | NI_NUMERICSERV) != 0) { + m_close(fd); return; } @@ -77,10 +79,13 @@ dropbear_assert(tcpinfo->tcp_type == forwarded); /* "forwarded-tcpip" */ /* address that was connected, port that was connected */ - addr = tcpinfo->listenaddr; + addr = tcpinfo->request_listenaddr; port = tcpinfo->listenport; } + if (addr == NULL) { + addr = "localhost"; + } buf_putstring(ses.writepayload, addr, strlen(addr)); buf_putint(ses.writepayload, port); @@ -104,21 +109,13 @@ struct Listener *listener = NULL; int nsocks; char* errstring = NULL; - /* listen_spec = NULL indicates localhost */ - const char* listen_spec = NULL; TRACE(("enter listen_tcpfwd")) /* first we try to bind, so don't need to do so much cleanup on failure */ snprintf(portstring, sizeof(portstring), "%d", tcpinfo->listenport); - /* a listenaddr of "" will indicate all interfaces */ - if (opts.listen_fwd_all - && (strcmp(tcpinfo->listenaddr, "localhost") != 0) ) { - listen_spec = tcpinfo->listenaddr; - } - - nsocks = dropbear_listen(listen_spec, portstring, socks, + nsocks = dropbear_listen(tcpinfo->listenaddr, portstring, socks, DROPBEAR_MAX_SOCKS, &errstring, &ses.maxfd); if (nsocks < 0) { dropbear_log(LOG_INFO, "TCP forward failed: %s", errstring);
--- a/tcpfwd.h Thu Nov 06 13:33:06 2008 +0000 +++ b/tcpfwd.h Wed May 16 22:54:51 2012 +0800 @@ -25,6 +25,7 @@ #define _TCPFWD_H #include "channel.h" +#include "list.h" struct TCPListener { @@ -38,21 +39,21 @@ * localhost, or a normal interface name. */ unsigned char *listenaddr; unsigned int listenport; + /* The address that the remote host asked to listen on */ + unsigned char *request_listenaddr;; const struct ChanType *chantype; 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 */ @@ -70,5 +71,4 @@ /* Common */ int listen_tcpfwd(struct TCPListener* tcpinfo); - #endif