diff --git a/CHANGES b/CHANGES index 74bc13cfdb86d2ed8176f26a2f51a112e91c8934..01ca4bfebb4c00f395d5833e224132a4de5eec68 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,133 @@ +0.44test3 - Fri Aug 27 22:20:54 +0800 + +- Fixed a bunch of warnings. + +- scp works correctly when passed a username (fix for the dbclient program + itself as well, "-lmatt" works as well as "-l matt"). + +- Remove unrequired debian files + +- Exit with the remote process's return code for dbclient + +- Display stderr messages from the server in the client + +- Add circular buffering to the channel code. This should dramatically reduce + the amount of backtraffic sent in response to traffic incoming to the + Dropbear end - improves high-latency performance (ie dialup). + +- Various other related channel-handling fixups. + +- Allow leading lines in the banner when connecting to servers + +- Fixed printing out errors onto the network socket with stderr (for inetd + mode when using xinetd) + +- Remove obselete documentation + +- Fix a null-pointer exception when trying to free non-existant listeners + at cleanup. + +- DEBUG_TRACE now only works if you add "-v" to the program commandline + +- Don't leave stdin non-blocking on exit - this caused the parent shell + of dbclient to close when dbclient exited, for some shells in BusyBox + +- Server connections no longer timeout after 5 minutes + +- Fixed stupid DSS hostkey typo (server couldn't load host keys) + +0.44test2 - Tues Aug 17 2004 17:43:54 +0800 + +- Fix up dropbearmulti targets in the Makefile - symlinks are now created + +- Compile fake-rfc2553 even with dropbearconvert/dropbearkey - this + allows them to work on platforms without a native getaddrinfo() + +- Create ~/.ssh/known_hosts properly if it doesn't exist + +- Fix basename() function prototype + +- Backport some local changes (more #ifdefs for termcodes.c, a fix for missing + defines on AIX). + +- Let dbclient be run as "ssh" + +- Initialise mp_ints by default + +0.44test1 - Sun Aug 16 2005 17:43:54 +0800 + +- TESTING RELEASE - this is the first public release of the client codebase, + so there are sure to be bugs to be found. In addition, if you're just using + the server portion, the final binary size probably will increase - I'll + be trying to get it back down in future releases. + +- Dropbear client added - lots of changes to the server code as well to + generalise things + +- IPv6 support added for client, server, and forwarding + +- New makefile with more generic support for multiple-program binaries + +0.43 - Fri Jul 16 2004 17:44:54 +0800 + +- SECURITY: Don't try to free() uninitialised variables in DSS verification + code. Thanks to Arne Bernin for pointing out this bug. This is possibly + exploitable, all users with DSS and pubkey-auth compiled in are advised to + upgrade. + +- Clean up agent forwarding socket files correctly, patch from Gerrit Pape. + +- Don't go into an infinite loop when portforwarding to servers which don't + send any initial data/banner. Patch from Nikola Vladov + +- Fix for network vs. host byte order in logging remote TCP ports, also + from Gerrit Pape. + +- Initialise many pointers to NULL, for general safety. Also checked cleanup + code for mp_ints (related to security issues above). + +0.42 - Wed Jun 16 2004 12:44:54 +0800 + +- Updated to Gerrit Pape's official Debian subdirectory + +- Fixed bad check when opening /dev/urandom - thanks to Danny Sung. + +- Added -i inetd mode flag, and associated options in options.h . Dropbear + can be compiled with either normal mode, inetd, or both modes. Thanks + to Gerrit Pape for basic patch and motivation. + +- Use <dirent.h> rather than <sys/dir.h> for POSIX compliance. Thanks to Bill + Sommerfield. + +- Fixed a TCP forwarding (client-local, -L style) bug which caused the whole + session to close if the TCP connection failed. Thanks to Andrew Braund for + reporting it and helping track it down. + +- Re-enable sigpipe for child processes. Thanks to Gerrit Pape for some + suggestions, and BSD manpages for a clearer explanation of the behaviour. + +- Added manpages, thanks to Gerrit Pape. + +- Changed license text for LibTomCrypt and LibTomMath. + +- Added strip-static target + +- Fixed a bug in agent-forwarding cleanup handler - would segfault + (dereferencing a null pointer) if agent forwarding had failed. + +- Fix behaviour of authorized_keys parsing, so larger (>1024 bit) DSA keys will + work. Thanks to Dr. Markus Waldeck for the report. + +- Fixed local port forwarding code so that the "-j" option will make forwarding + attempts fail more gracefully. + +- Allow repeated requests in a single session if previous ones fail - this fixes PuTTY and some other SCP clients, which try SFTP, then fall-back to SCP if it + isn't available. Thanks to Stirling Westrup for the report. + +- Updated to LibTomCrypt 0.96 and LibTomMath 0.30. The AES code now uses + smaller non-precomputed tables if DROPBEAR_SMALL_CODE is defined in + options.h, leading to a significant reduction in the binary size. + 0.41 - Mon Jan 19 2004 22:40:19 +0800 - Fix in configure so that cross-compiling works, thanks to numerous people for diff --git a/INSTALL b/INSTALL index 921c0993cab8f3460b414b9a10a410932466a533..13c87e774b373189a9443bab1c260a9280747b8a 100644 --- a/INSTALL +++ b/INSTALL @@ -1,45 +1,30 @@ Basic Dropbear build instructions: -- First, edit options.h to choose user-defined features to choose, such as - which ciphers/hashes you want, which forwarding you want, etc. +- Edit options.h to set which features you want. +- Edit debug.h if you want any debug options (not usually required). -- Edit debug.h if you want any debug options - -- Now configure Dropbear's host-specific options - (if you are using a cvs copy, "autoconf; autoheader" first) +(If using a non-tarball copy, "autoconf; autoheader") ./configure (optionally with --disable-zlib or --disable-syslog, or --help for other options) -- Then compile and optionally install Dropbear: - -(the Makefile requires GNU make, if you want to make it portable, send me - some patches) +Now compile: -make -make install (installs to /usr/local/sbin, /usr/local/bin by default) +make PROGRAMS="dropbear dbclient dropbearkey dropbearconvert scp" -You need to generate server keys, this is one-off: -./dropbearkey -t rsa -f dropbear_rsa_host_key -./dropbearkey -t dss -f dropbear_dss_host_key +And install (/usr/local/bin is usual default): -or alternatively convert OpenSSH keys to Dropbear: -./dropbearconvert openssh dropbear /etc/ssh/ssh_host_dsa_key dropbear_dss_host_key +make PROGRAMS="dropbear dbclient dropbearkey dropbearconvert scp" install -And you can now run the server. -./dropbear +(you can leave items out of the PROGRAMS list to avoid compiling them. If you +recompile after changing the PROGRAMS list, you *MUST* "make clean" before +recompiling - bad things will happen otherwise) -or './dropbear -h' to get options. +See MULTI for instructions on making all-in-one binaries. -If the server is run as non-root, you most likely won't be able to allocate a -pty, and you cannot login as any user other than that running the daemon -(obviously). Shadow passwords will also be unusable as non-root. +If you want to compile statically, add "STATIC=1" to the make command-line. -The Dropbear distribution includes a standalone version of OpenSSH's scp -program. You can compile it with "make scp", you may want to change the path -of the ssh binary, specified near the top of the scp.c file. By default -the progress meter isn't compiled in to save space, you can enable it with -"make scp-progress". +Binaries can be strippd with "make strip" ============================================================================ @@ -50,18 +35,11 @@ versions is broken. Also note that you may get strange issues if your uClibc headers don't match the library you are running with, ie the headers might say that shadow password support exists, but the libraries don't have it. -To compile for uClibc the following should work: - -rm config.cache -CC=i386-uclib-gcc ./configure --disable-zlib -make clean -make -make strip - -... and that should be it. You can use "make static" to make statically linked -binaries, and it is advisable to strip the binaries too. If you're looking -to make a small binary, you should remove unneeded ciphers and MD5, by -editing options.h +Compiling for uClibc should be the same as normal, just set CC to the magic +uClibc toolchain compiler (ie export CC=i386-uclibc-gcc or whatever). +You can use "make STATIC=1" to make statically linked binaries, and it is +advisable to strip the binaries too. If you're looking to make a small binary, +you should remove unneeded ciphers and MD5, by editing options.h It is possible to compile zlib in, by copying zlib.h and zconf.h into a subdirectory (ie zlibincludes), and @@ -78,7 +56,11 @@ globally in ~/.ssh/config, not just in the host entry in that file. You may want to manually disable lastlog recording when using uClibc, configure with --disable-lastlog. -One common problem is pty allocation. There are a number of types of pty allocation which can be used -- if they work properly, the end result is the same for each type. Running configure should detect the best type to use automatically, however for some embedded systems, this may be incorrect. Some things to note: +One common problem is pty allocation. There are a number of types of pty +allocation which can be used -- if they work properly, the end result is the +same for each type. Running configure should detect the best type to use +automatically, however for some systems, this may be incorrect. Some +things to note: If your system expects /dev/pts to be mounted (this is a uClibc option), make sure that it is. @@ -90,19 +72,3 @@ One common problem is pty allocation. There are a number of types of pty allocat to create all the /dev/pty?? and /dev/tty?? devices, which can be problematic for devfs. In general, openpty() is the best way to allocate PTYs, so it's best to try and get it working. - - -============================================================================ - -Public key auth: - -You can use ~/.ssh/authorized_keys in the same way as with OpenSSH, just put -the key entries in that file. They should be of the form: - -ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAwVa6M6cGVmUcLl2cFzkxEoJd06Ub4bVDsYrWvXhvUV+ZAM9uGuewZBDoAqNKJxoIn0Hyd0Nk/yU99UVv6NWV/5YSHtnf35LKds56j7cuzoQpFIdjNwdxAN0PCET/MG8qyskG/2IE2DPNIaJ3Wy+Ws4IZEgdJgPlTYUBWWtCWOGc= someone@hostname - -You must make sure that ~/.ssh, and the key file, are only writable by the -user. - -NOTE: Dropbear ignores authorized_keys options such as those described in the -OpenSSH sshd manpage, and will not allow a login for these keys. diff --git a/LICENSE b/LICENSE index 254efbd8f52d0c15ffd4cf7b57e647dc351c6001..e0a11acad44549e8832010c244970882dc81c463 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,15 @@ -The majority of code is written by Matt Johnston, under the following license: +Dropbear contains a number of components from different sources, hence there +are a few licenses and authors involved. All licenses are fairly +non-restrictive. -Copyright (c) 2002,2003 Matt Johnston + +The majority of code is written by Matt Johnston, under the license below. + +Portions of the client-mode work are (c) 2004 Mihnea Stoenescu, under the +same license: + +Copyright (c) 2002-2004 Matt Johnston +Portions copyright (c) 2004 Mihnea Stoenescu All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy @@ -23,9 +32,7 @@ SOFTWARE. ===== -LibTomCrypt and LibTomMath are (c) Tom St Denis, under TDCAL (Tom Doesn't Care -About Licenses) some files are from public domain sources, see -libtomcrypt/legal.txt +LibTomCrypt and LibTomMath are written by Tom St Denis, and are Public Domain. ===== diff --git a/MULTI b/MULTI index 4e49f08e7431db5a2fb6cd8aa09f27e6dc01e661..a50e30eb17f3f7ece30f71bd76841e8550798c9e 100644 --- a/MULTI +++ b/MULTI @@ -3,29 +3,24 @@ Multi-binary compilation To compile for systems without much space (floppy distributions etc), you can create a single binary. This will save disk space by avoiding repeated -code between the three components (dropbear, dropbearkey, dropbearconvert). +code between the various parts. If you are familiar with "busybox", it's the same principle. -To use the multi-purpose binary, firstly enable the "#define DROPBEAR_MULTI" -line in options.h +To compile the multi-binary, first "make clean" (if you've compiled +previously), then -Then enable which of the binaries you want to compile, also in options.h -(by default these are all enabled). - -You should then "make clean" (if you compiled previously), then - -"make dropbearmulti" - -("make dropbearmultistatic" will make a static binary). +make PROGRAMS="programs you want here" MULTI=1 To use the binary, symlink it from the desired executable: ln -s dropbearmulti dropbear +ln -s dropbearmulti dbclient +etc then execute as normal: ./dropbear <options here> -"make install" doesn't currently work for multi-binary configuration, however +"make install" doesn't currently work for multi-binary configuration, though in most situations where it is being used, the target and build systems will differ. diff --git a/Makefile.in b/Makefile.in index 6cb94e4f032377e9c7c9e7d450b83aa4bf2dd73d..cc221d5036ebda93d4ecc9ffa3cf325d8f007205 100644 --- a/Makefile.in +++ b/Makefile.in @@ -3,14 +3,13 @@ # invocation: # make PROGRAMS="dropbear dbclient scp" MULTI=1 STATIC=1 SCPPROGRESS=1 # -# to make a single multiple statically linked binary "staticdropbearmulti", -# which includes dropbear, scp and dbclient functionality, and includes the -# progress-bar functionality in scp. Hopefully that seems intuitive. - -# This makefile is quite evil. +# to make a multiple-program statically linked binary "staticdropbearmulti". +# This example will include dropbear, scp, dropbearkey, dropbearconvert, and +# dbclient functionality, and includes the progress-bar functionality in scp. +# Hopefully that seems intuitive. ifndef PROGRAMS - PROGRAMS=dropbear dbclient dropbearkey dropbearkey + PROGRAMS=dropbear dbclient dropbearkey dropbearconvert endif LTC=libtomcrypt/libtomcrypt.a @@ -20,21 +19,22 @@ COMMONOBJS=dbutil.o buffer.o \ dss.o bignum.o \ signkey.o rsa.o random.o \ queue.o \ - atomicio.o compat.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-session.o svr-service.o \ - svr-chansession.o svr-runopts.o svr-agentfwd.o svr-main.o svr-x11fwd.o\ svr-authpam.o + svr-chansession.o svr-runopts.o svr-agentfwd.o svr-main.o svr-x11fwd.o\ + svr-tcpfwd.o CLIOBJS=cli-algo.o cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \ cli-session.o cli-service.o cli-runopts.o cli-chansession.o \ - cli-authpubkey.o + cli-authpubkey.o cli-tcpfwd.o cli-channel.o CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \ common-channel.o common-chansession.o termcodes.o loginrec.o \ - tcpfwd-direct.o tcpfwd-remote.o listener.o process-packet.o \ - common-runopts.o + tcp-accept.o listener.o process-packet.o \ + common-runopts.o circbuffer.o KEYOBJS=dropbearkey.o gendss.o genrsa.o @@ -43,11 +43,11 @@ CONVERTOBJS=dropbearconvert.o keyimport.o SCPOBJS=scp.o progressmeter.o atomicio.o scpmisc.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 authpasswd.h \ + dss.h bignum.h signkey.h rsa.h random.h service.h auth.h \ debug.h channel.h chansession.h config.h queue.h sshpty.h \ - termcodes.h gendss.h genrsa.h authpubkey.h runopts.h includes.h \ - loginrec.h atomicio.h x11fwd.h agentfwd.h tcpfwd-direct.h compat.h \ - tcpfwd-remote.h listener.h + termcodes.h gendss.h genrsa.h runopts.h includes.h \ + loginrec.h atomicio.h x11fwd.h agentfwd.h tcpfwd.h compat.h \ + listener.h fake-rfc2553.h dropbearobjs=$(COMMONOBJS) $(CLISVROBJS) $(SVROBJS) dbclientobjs=$(COMMONOBJS) $(CLISVROBJS) $(CLIOBJS) @@ -115,25 +115,36 @@ all: $(TARGETS) strip: $(TARGETS) $(STRIP) $(addsuffix $(EXEEXT), $(addprefix $(SPREFIX), $(TARGETS))) -install: $(addprefix install, $(TARGETS)) +install: $(addprefix inst, $(TARGETS)) + +installdropbearmulti: insdbmulti $(addprefix insmulti, $(PROGRAMS)) + +insdbmulti: dropbearmulti + $(INSTALL) -d -m 755 $(DESTDIR)$(bindir) + $(INSTALL) -m 755 $(SPREFIX)dropbearmulti$(EXEEXT) $(DESTDIR)$(bindir) + -chown root $(DESTDIR)$(bindir)/$(SPREFIX)dropbearmulti$(EXEEXT) + -chgrp 0 $(DESTDIR)$(bindir)/$(SPREFIX)dropbearmulti$(EXEEXT) + +insmultidropbear: dropbearmulti + -rm -f $(DESTDIR)$(sbindir)/$(SPREFIX)dropbear$(EXEEXT) + -ln -s $(DESTDIR)$(bindir)/$(SPREFIX)dropbearmulti$(EXEEXT) $(DESTDIR)$(sbindir)/$(SPREFIX)dropbear$(EXEEXT) + +insmulti%: dropbearmulti + -rm -f $(DESTDIR)$(bindir)/$(SPREFIX)$*$(EXEEXT) + -ln -s $(DESTDIR)$(bindir)/$(SPREFIX)dropbearmulti$(EXEEXT) $(DESTDIR)$(bindir)/$(SPREFIX)$*$(EXEEXT) # dropbear should go in sbin, so it needs a seperate rule -installdropbear: dropbear +instdropbear: dropbear $(INSTALL) -d -m 755 $(DESTDIR)$(sbindir) $(INSTALL) -m 755 $(SPREFIX)dropbear$(EXEEXT) $(DESTDIR)$(sbindir) -chown root $(DESTDIR)$(sbindir)/$(SPREFIX)dropbear$(EXEEXT) -chgrp 0 $(DESTDIR)$(sbindir)/$(SPREFIX)dropbear$(EXEEXT) -install%: $* +inst%: $* $(INSTALL) -d -m 755 $(DESTDIR)$(bindir) $(INSTALL) -m 755 $(SPREFIX)$*$(EXEEXT) $(DESTDIR)$(bindir) - -chown root $(DESTDIR)$(sbindir)/$(SPREFIX)$*$(EXEEXT) - -chgrp 0 $(DESTDIR)$(sbindir)/$(SPREFIX)$*$(EXEEXT) - ifeq ($(MULTI), 1) - @echo - @echo "You must manually create links for $*" - endif - + -chown root $(DESTDIR)$(bindir)/$(SPREFIX)$*$(EXEEXT) + -chgrp 0 $(DESTDIR)$(bindir)/$(SPREFIX)$*$(EXEEXT) # for some reason the rule further down doesn't like $($@objs) as a prereq. @@ -158,11 +169,16 @@ ifeq ($(MULTI),1) CFLAGS+=$(addprefix -DDBMULTI_, $(PROGRAMS)) -DDROPBEAR_MULTI endif -dropbearmulti: $(HEADERS) $(MULTIOBJS) $(LTC) $(LTM) Makefile - $(LD) $(LDFLAGS) -o $(SPREFIX)$@$(EXEEXT) $(MULTIOBJS) $(LIBS) - @echo - @echo "You should now create symlinks to the programs you have included" - @echo "ie 'ln -s dropbearmulti dropbear'" +dropbearmulti: multilink + +multibinary: $(HEADERS) $(MULTIOBJS) $(LTC) $(LTM) Makefile + $(LD) $(LDFLAGS) -o $(SPREFIX)dropbearmulti$(EXEEXT) $(MULTIOBJS) $(LIBS) + +multilink: multibinary $(addprefix link, $(PROGRAMS)) + +link%: + -rm -f $(SPREFIX)$*$(EXEEXT) + -ln -s $(SPREFIX)dropbearmulti$(EXEEXT) $(SPREFIX)$*$(EXEEXT) $(LTC): options.h cd libtomcrypt && $(MAKE) clean && $(MAKE) @@ -179,7 +195,9 @@ ltm-clean: sizes: dropbear objdump -t dropbear|grep ".text"|cut -d "." -f 2|sort -rn -clean: ltc-clean ltm-clean +clean: ltc-clean ltm-clean thisclean + +thisclean: -rm -f dropbear dbclient dropbearkey dropbearconvert scp scp-progress -rm -f staticdropbear staticdropbearkey staticdropbearconvert staticscp -rm -f dropbearmulti staticdropbearmulti diff --git a/README b/README index 4dae30f373816a5ae9b389e373d86f8babe7a4ca..52e3cf052a90597563f556f4da2c20310bb5c1fa 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -This is Dropbear, a smallish SSH 2 server. +This is Dropbear, a smallish SSH 2 server and client. INSTALL has compilation instructions. @@ -12,3 +12,63 @@ me if you have any questions/bugs found/features/ideas/comments etc :) Matt Johnston matt@ucc.asn.au + + +In the absence of detailed documentation, some notes follow: +============================================================================ + +Server public key auth: + +You can use ~/.ssh/authorized_keys in the same way as with OpenSSH, just put +the key entries in that file. They should be of the form: + +ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAwVa6M6cGVmUcLl2cFzkxEoJd06Ub4bVDsYrWvXhvUV+ZAM9uGuewZBDoAqNKJxoIn0Hyd0Nk/yU99UVv6NWV/5YSHtnf35LKds56j7cuzoQpFIdjNwdxAN0PCET/MG8qyskG/2IE2DPNIaJ3Wy+Ws4IZEgdJgPlTYUBWWtCWOGc= someone@hostname + +You must make sure that ~/.ssh, and the key file, are only writable by the +user. + +NOTE: Dropbear ignores authorized_keys options such as those described in the +OpenSSH sshd manpage, and will not allow a login for these keys. + +============================================================================ + +Client public key auth: + +Dropbear can do public key auth as a client, but you will have to convert +OpenSSH style keys to Dropbear format, or use dropbearkey to create them. + +If you have an OpenSSH-style private key ~/.ssh/id_rsa, you need to do: + +dropbearconvert openssh dropbear ~/.ssh/id_rsa ~/.ssh/id_rsa.db +dbclient -i ~/.ssh/id_rsa.db <hostname> + +Currently encrypted keys aren't supported, neither is agent forwarding. At some +stage both hopefully will be. + +============================================================================ + +If you want to get the public-key portion of a Dropbear private key, look at +dropbearkey's '-y' option. + +============================================================================ + +To run the server, you need to generate server keys, this is one-off: +./dropbearkey -t rsa -f dropbear_rsa_host_key +./dropbearkey -t dss -f dropbear_dss_host_key + +or alternatively convert OpenSSH keys to Dropbear: +./dropbearconvert openssh dropbear /etc/ssh/ssh_host_dsa_key dropbear_dss_host_key + +============================================================================ + +If the server is run as non-root, you most likely won't be able to allocate a +pty, and you cannot login as any user other than that running the daemon +(obviously). Shadow passwords will also be unusable as non-root. + +============================================================================ + +The Dropbear distribution includes a standalone version of OpenSSH's scp +program. You can compile it with "make scp", you may want to change the path +of the ssh binary, specified near the top of the scp.c file. By default +the progress meter isn't compiled in to save space, you can enable it by +adding 'SCPPROGRESS=1' to the make commandline. diff --git a/TODO b/TODO index 8a567c2e84acd319499152eeb7941d1b71c67027..dac9a0428dfe022d4362954349afe6f587dfcc85 100644 --- a/TODO +++ b/TODO @@ -1,29 +1,29 @@ Current: -Things which need doing: +Things which might need doing: - Make options.h generated from configure perhaps? -- investigate self-pipe? +- Improved queueing of unauthed connections + - fix agent fwd problems -- improve channel window adjustment algorithm (circular buffering) -- Don't use pregenerated AES tables +- handle /etc/environment in AIX -- check PRNG - check that there aren't timing issues with valid/invalid user authentication feedback. -- IP6 (binding to :: takes over ipv4 as well, sigh. If anyone wants to suggest - a clean way (ie no V4MAPPED or setsockopt things) please let me know :) -- Binding to different interfaces (see ipv6 probably) +- Binding to different interfaces -- PAM ?? -- inetd - possible RSA blinding? need to check whether this is vuln to timing attacks +- check PRNG - CTR mode, SSH_MSG_IGNORE sending to improve CBC security -- DH Group Exchange possibly +- DH Group Exchange possibly, or just add group14 (whatever it's called today) - Use m_burn for clearing sensitive items in LTM/LTC - fix scp.c for IRIX + +- Be able to use OpenSSH keys for the client? or at least have some form of + encrypted keys. +- Client agent forwarding diff --git a/buffer.c b/buffer.c index 7181fcaad40273932c0a9c5f616579221a8f773e..793eee10a4da11e262ad635f0e31c76a5a64cc7e 100644 --- a/buffer.c +++ b/buffer.c @@ -1,5 +1,5 @@ /* - * Dropbear - a SSH2 server + * Dropbear SSH * * Copyright (c) 2002,2003 Matt Johnston * All rights reserved. @@ -34,8 +34,8 @@ #define BUF_MAX_INCR 1000000000 #define BUF_MAX_SIZE 1000000000 -/* avoid excessively large numbers, > 5000 bit */ -#define BUF_MAX_MPINT (5000 / 8) +/* avoid excessively large numbers, > ~8192 bits */ +#define BUF_MAX_MPINT (8240 / 8) /* Create (malloc) a new buffer of size */ buffer* buf_new(unsigned int size) { @@ -76,7 +76,8 @@ void buf_burn(buffer* buf) { } -/* resize a buffer, pos and len will be repositioned if required */ +/* resize a buffer, pos and len will be repositioned if required when + * downsizing */ void buf_resize(buffer *buf, unsigned int newsize) { if (newsize > BUF_MAX_SIZE) { @@ -151,6 +152,8 @@ void buf_incrpos(buffer* buf, int incr) { /* Get a byte from the buffer and increment the pos */ unsigned char buf_getbyte(buffer* buf) { + /* This check is really just ==, but the >= allows us to check for the + * assert()able case of pos > len, which should _never_ happen. */ if (buf->pos >= buf->len) { dropbear_exit("bad buf_getbyte"); } diff --git a/channel.h b/channel.h index e1bdae2923bd5487cc33c816b36ff8c0f1793881..8a494c76a64e63e718d24250b19301e095e7b20c 100644 --- a/channel.h +++ b/channel.h @@ -27,6 +27,7 @@ #include "includes.h" #include "buffer.h" +#include "circbuffer.h" /* channel->type values */ #define CHANNEL_ID_NONE 0 @@ -41,14 +42,18 @@ #define SSH_OPEN_UNKNOWN_CHANNEL_TYPE 3 #define SSH_OPEN_RESOURCE_SHORTAGE 4 -#define MAX_CHANNELS 60 /* simple mem restriction, includes each tcp/x11 +/* Not a real type */ +#define SSH_OPEN_IN_PROGRESS 99 + +#define MAX_CHANNELS 100 /* simple mem restriction, includes each tcp/x11 connection, so can't be _too_ small */ #define CHAN_EXTEND_SIZE 3 /* how many extra slots to add when we need more */ -#define RECV_MAXWINDOW 6000 /* tweak */ -#define RECV_MAXPACKET 1400 /* tweak */ -#define RECV_MINWINDOW 19000 /* when we get below this, we send a windowadjust */ +#define RECV_MAXWINDOW 4000 /* tweak */ +#define RECV_WINDOWEXTEND 500 /* We send a "window extend" every + RECV_WINDOWEXTEND bytes */ +#define RECV_MAXPACKET RECV_MAXWINDOW /* tweak */ struct ChanType; @@ -57,15 +62,16 @@ struct Channel { unsigned int index; /* the local channel index */ unsigned int remotechan; unsigned int recvwindow, transwindow; + unsigned int recvdonelen; unsigned int recvmaxpacket, transmaxpacket; void* typedata; /* a pointer to type specific data */ - int infd; /* stdin for the program, we write to this */ - int outfd; /* stdout for the program, we read from this */ - int errfd; /* stdout for a program. This doesn't really fit here, - but makes the code a lot tidyer without being too bad. This - is -1 for channels which don't requre it. Currently only - a 'session' without a pty will use it */ - buffer *writebuf; /* data for the program */ + int infd; /* data to send over the wire */ + int outfd; /* data for consumption, what was in writebuf */ + int errfd; /* used like infd or errfd, 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 */ + circbuffer *extrabuf; /* extended-data for the program - used like writebuf + but for stderr */ int sentclosed, recvclosed; @@ -94,6 +100,7 @@ void chaninitialise(); void chancleanup(); void setchannelfds(fd_set *readfd, fd_set *writefd); void channelio(fd_set *readfd, fd_set *writefd); +struct Channel* getchannel(unsigned int chan); struct Channel* newchannel(unsigned int remotechan, const struct ChanType *type, unsigned int transwindow, unsigned int transmaxpacket); @@ -103,10 +110,16 @@ void recv_msg_channel_request(); void send_msg_channel_failure(struct Channel *channel); void send_msg_channel_success(struct Channel *channel); void recv_msg_channel_data(); +void recv_msg_channel_extended_data(); void recv_msg_channel_window_adjust(); void recv_msg_channel_close(); void recv_msg_channel_eof(); +void common_recv_msg_channel_data(struct Channel *channel, int fd, + circbuffer * buf); + +const struct ChanType clichansess; + #ifdef USING_LISTENERS int send_msg_channel_open_init(int fd, const struct ChanType *type); void recv_msg_channel_open_confirmation(); diff --git a/chansession.h b/chansession.h index 3fb19019beb55394839c471a8bee53f5562f4aa7..84ca55a7699b5317050760bbdd04b1d3e1ce90bd 100644 --- a/chansession.h +++ b/chansession.h @@ -68,11 +68,6 @@ struct ChildPid { }; -void chansessionrequest(struct Channel * channel); -void send_msg_chansess_exitstatus(struct Channel * channel, - struct ChanSess * chansess); -void send_msg_chansess_exitsignal(struct Channel * channel, - struct ChanSess * chansess); void addnewvar(const char* param, const char* var); void cli_send_chansess_request(); diff --git a/circbuffer.c b/circbuffer.c new file mode 100644 index 0000000000000000000000000000000000000000..a2edcbb48f3ca787f8196476c38a0d9dd4677f09 --- /dev/null +++ b/circbuffer.c @@ -0,0 +1,138 @@ +/* + * Dropbear SSH + * + * Copyright (c) 2002-2004 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + +#include "includes.h" +#include "dbutil.h" +#include "circbuffer.h" + +#define MAX_CBUF_SIZE 100000000 + +circbuffer * cbuf_new(unsigned int size) { + + circbuffer *cbuf = NULL; + + if (size > MAX_CBUF_SIZE) { + dropbear_exit("bad cbuf size"); + } + + cbuf = (circbuffer*)m_malloc(sizeof(circbuffer)); + cbuf->data = (unsigned char*)m_malloc(size); + cbuf->used = 0; + cbuf->readpos = 0; + cbuf->writepos = 0; + cbuf->size = size; + + return cbuf; +} + +void cbuf_free(circbuffer * cbuf) { + + m_free(cbuf->data); + m_free(cbuf); +} + +unsigned int cbuf_getused(circbuffer * cbuf) { + + return cbuf->used; + +} + +unsigned int cbuf_getavail(circbuffer * cbuf) { + + return cbuf->size - cbuf->used; + +} + +unsigned int cbuf_readlen(circbuffer *cbuf) { + + assert(((2*cbuf->size)+cbuf->writepos-cbuf->readpos)%cbuf->size == cbuf->used%cbuf->size); + assert(((2*cbuf->size)+cbuf->readpos-cbuf->writepos)%cbuf->size == (cbuf->size-cbuf->used)%cbuf->size); + + if (cbuf->used == 0) { + TRACE(("cbuf_readlen: unused buffer")); + return 0; + } + + if (cbuf->readpos < cbuf->writepos) { + return cbuf->writepos - cbuf->readpos; + } + + return cbuf->size - cbuf->readpos; +} + +unsigned int cbuf_writelen(circbuffer *cbuf) { + + assert(cbuf->used <= cbuf->size); + assert(((2*cbuf->size)+cbuf->writepos-cbuf->readpos)%cbuf->size == cbuf->used%cbuf->size); + assert(((2*cbuf->size)+cbuf->readpos-cbuf->writepos)%cbuf->size == (cbuf->size-cbuf->used)%cbuf->size); + + if (cbuf->used == cbuf->size) { + TRACE(("cbuf_writelen: full buffer")); + return 0; /* full */ + } + + if (cbuf->writepos < cbuf->readpos) { + return cbuf->readpos - cbuf->writepos; + } + + return cbuf->size - cbuf->writepos; +} + +unsigned char* cbuf_readptr(circbuffer *cbuf, unsigned int len) { + if (len > cbuf_readlen(cbuf)) { + dropbear_exit("bad cbuf read"); + } + + return &cbuf->data[cbuf->readpos]; +} + +unsigned char* cbuf_writeptr(circbuffer *cbuf, unsigned int len) { + + if (len > cbuf_writelen(cbuf)) { + dropbear_exit("bad cbuf write"); + } + + return &cbuf->data[cbuf->writepos]; +} + +void cbuf_incrwrite(circbuffer *cbuf, unsigned int len) { + if (len > cbuf_writelen(cbuf)) { + dropbear_exit("bad cbuf write"); + } + + cbuf->used += len; + assert(cbuf->used <= cbuf->size); + cbuf->writepos = (cbuf->writepos + len) % cbuf->size; +} + + +void cbuf_incrread(circbuffer *cbuf, unsigned int len) { + if (len > cbuf_readlen(cbuf)) { + dropbear_exit("bad cbuf read"); + } + + assert(cbuf->used >= len); + cbuf->used -= len; + cbuf->readpos = (cbuf->readpos + len) % cbuf->size; +} diff --git a/tcpfwd-direct.h b/circbuffer.h similarity index 55% rename from tcpfwd-direct.h rename to circbuffer.h index 20cd27819b63faf086833f55b360b585a3369b5d..21c5134b74b9a91d1c87718063783a1524310c86 100644 --- a/tcpfwd-direct.h +++ b/circbuffer.h @@ -1,7 +1,7 @@ /* - * Dropbear - a SSH2 server + * Dropbear SSH * - * Copyright (c) 2002,2003 Matt Johnston + * Copyright (c) 2002-2004 Matt Johnston * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -21,14 +21,30 @@ * 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. */ -#ifndef _TCPFWD_DIRECT_H_ -#define _TCPFWD_DIRECT_H_ -#ifndef DISABLE_TCFWD_DIRECT -#include "includes.h" -#include "channel.h" +#ifndef _CIRCBUFFER_H_ +#define _CIRCBUFFER_H_ +struct circbuf { -extern const struct ChanType chan_tcpdirect; + unsigned int size; + unsigned int readpos; + unsigned int writepos; + unsigned int used; + unsigned char* data; +}; -#endif +typedef struct circbuf circbuffer; + +circbuffer * cbuf_new(unsigned int size); +void cbuf_free(circbuffer * cbuf); + +unsigned int cbuf_getused(circbuffer * cbuf); /* how much data stored */ +unsigned int cbuf_getavail(circbuffer * cbuf); /* how much we can write */ +unsigned int cbuf_readlen(circbuffer *cbuf); /* max linear read len */ +unsigned int cbuf_writelen(circbuffer *cbuf); /* max linear write len */ + +unsigned char* cbuf_readptr(circbuffer *cbuf, unsigned int len); +unsigned char* cbuf_writeptr(circbuffer *cbuf, unsigned int len); +void cbuf_incrwrite(circbuffer *cbuf, unsigned int len); +void cbuf_incrread(circbuffer *cbuf, unsigned int len); #endif diff --git a/cli-auth.c b/cli-auth.c index 98e2e998536ef129fbfd111c49afdc63c78dede0..9dedfa8b99d8ac311e66fa1404204e75f83a0843 100644 --- a/cli-auth.c +++ b/cli-auth.c @@ -1,3 +1,28 @@ +/* + * Dropbear SSH + * + * Copyright (c) 2002,2003 Matt Johnston + * Copyright (c) 2004 by Mihnea Stoenescu + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + #include "includes.h" #include "session.h" #include "auth.h" @@ -92,7 +117,7 @@ void recv_msg_userauth_failure() { return; } -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH /* If it was a pubkey auth request, we should cross that key * off the list. */ if (cli_ses.lastauthtype == AUTH_TYPE_PUBKEY) { @@ -126,13 +151,13 @@ void recv_msg_userauth_failure() { for (i = 0; i <= methlen; i++) { if (methods[i] == '\0') { TRACE(("auth method '%s'", tok)); -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH if (strncmp(AUTH_METHOD_PUBKEY, tok, AUTH_METHOD_PUBKEY_LEN) == 0) { ses.authstate.authtypes |= AUTH_TYPE_PUBKEY; } #endif -#ifdef DROPBEAR_PASSWORD_AUTH +#ifdef ENABLE_CLI_PASSWORD_AUTH if (strncmp(AUTH_METHOD_PASSWORD, tok, AUTH_METHOD_PASSWORD_LEN) == 0) { ses.authstate.authtypes |= AUTH_TYPE_PASSWORD; @@ -144,6 +169,8 @@ void recv_msg_userauth_failure() { } } + m_free(methods); + cli_ses.state = USERAUTH_FAIL_RCVD; TRACE(("leave recv_msg_userauth_failure")); @@ -163,14 +190,14 @@ void cli_auth_try() { CHECKCLEARTOWRITE(); /* XXX We hardcode that we try a pubkey first */ -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH if (ses.authstate.authtypes & AUTH_TYPE_PUBKEY) { finished = cli_auth_pubkey(); cli_ses.lastauthtype = AUTH_TYPE_PUBKEY; } #endif -#ifdef DROPBEAR_PASSWORD_AUTH +#ifdef ENABLE_CLI_PASSWORD_AUTH if (!finished && ses.authstate.authtypes & AUTH_TYPE_PASSWORD) { finished = cli_auth_password(); cli_ses.lastauthtype = AUTH_TYPE_PASSWORD; diff --git a/cli-authpasswd.c b/cli-authpasswd.c index c04d2400ea666605d774a55f057cbe28fd27b366..724203be9daf97fd73a1826d8fc0703c0cbd29ea 100644 --- a/cli-authpasswd.c +++ b/cli-authpasswd.c @@ -1,3 +1,27 @@ +/* + * Dropbear SSH + * + * Copyright (c) 2002,2003 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + #include "includes.h" #include "buffer.h" #include "dbutil.h" @@ -5,6 +29,7 @@ #include "ssh.h" #include "runopts.h" +#ifdef ENABLE_CLI_PASSWORD_AUTH int cli_auth_password() { char* password = NULL; @@ -35,3 +60,4 @@ int cli_auth_password() { return 1; /* Password auth can always be tried */ } +#endif diff --git a/cli-authpubkey.c b/cli-authpubkey.c index 6b6ab516ebb037a47e73025d4bb1cc1739ce8873..d308dc3cc17d8926a2c005b76bde166b8ba82639 100644 --- a/cli-authpubkey.c +++ b/cli-authpubkey.c @@ -1,3 +1,28 @@ +/* + * Dropbear SSH + * + * Copyright (c) 2002,2003 Matt Johnston + * Copyright (c) 2004 by Mihnea Stoenescu + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + #include "includes.h" #include "buffer.h" #include "dbutil.h" @@ -6,6 +31,7 @@ #include "runopts.h" #include "auth.h" +#ifdef ENABLE_CLI_PUBKEY_AUTH static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign); /* Called when we receive a SSH_MSG_USERAUTH_FAILURE for a pubkey request. @@ -13,17 +39,22 @@ static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign); void cli_pubkeyfail() { struct PubkeyList *keyitem; + struct PubkeyList **previtem; TRACE(("enter cli_pubkeyfail")); + previtem = &cli_opts.pubkeys; + /* Find the key we failed with, and remove it */ for (keyitem = cli_opts.pubkeys; keyitem != NULL; keyitem = keyitem->next) { - if (keyitem->next == cli_ses.lastpubkey) { - keyitem->next = cli_ses.lastpubkey->next; + if (keyitem == cli_ses.lastpubkey) { + *previtem = keyitem->next; } + previtem = &keyitem; } sign_key_free(cli_ses.lastpubkey->key); /* It won't be used again */ m_free(cli_ses.lastpubkey); + TRACE(("leave cli_pubkeyfail")); } @@ -145,6 +176,7 @@ int cli_auth_pubkey() { /* Send a trial request */ send_msg_userauth_pubkey(cli_opts.pubkeys->key, cli_opts.pubkeys->type, 0); + cli_ses.lastpubkey = cli_opts.pubkeys; TRACE(("leave cli_auth_pubkey-success")); return 1; } else { @@ -152,3 +184,4 @@ int cli_auth_pubkey() { return 0; } } +#endif /* Pubkey auth */ diff --git a/authpubkey.h b/cli-channel.c similarity index 52% rename from authpubkey.h rename to cli-channel.c index bf9549e67ce656e818c4851a31155bcdc1c861f9..ea4af6962c08d2529135c723c70a784706e727bd 100644 --- a/authpubkey.h +++ b/cli-channel.c @@ -1,7 +1,7 @@ /* - * Dropbear - a SSH2 server + * Dropbear SSH * - * Copyright (c) 2002,2003 Matt Johnston + * Copyright (c) 2002-2004 Matt Johnston * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -22,12 +22,44 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#ifndef _PUBKEY_AUTH_ -#define _PUBKEY_AUTH_ +#include "includes.h" +#include "channel.h" +#include "buffer.h" +#include "circbuffer.h" +#include "dbutil.h" +#include "session.h" +#include "ssh.h" -#ifdef DROPBEAR_PUBKEY_AUTH +/* We receive channel data - only used by the client chansession code*/ +void recv_msg_channel_extended_data() { -void pubkeyauth(); + unsigned int chan; + struct Channel *channel; + unsigned int datatype; -#endif /* DROPBEAR_PUBKEY_AUTH */ -#endif /* _PUBKEY_AUTH_ */ + TRACE(("enter recv_msg_channel_extended_data")); + + chan = buf_getint(ses.payload); + channel = getchannel(chan); + + if (channel == NULL) { + dropbear_exit("Unknown channel"); + } + + if (channel->type != &clichansess) { + TRACE(("leave recv_msg_channel_extended_data: chantype is wrong")); + return; /* we just ignore it */ + } + + datatype = buf_getint(ses.payload); + + if (datatype != SSH_EXTENDED_DATA_STDERR) { + TRACE(("leave recv_msg_channel_extended_data: wrong datatype: %d", + datatype)); + return; + } + + common_recv_msg_channel_data(channel, channel->errfd, channel->extrabuf); + + TRACE(("leave recv_msg_channel_extended_data")); +} diff --git a/cli-chansession.c b/cli-chansession.c index e34c6fdca352e55ee67a0306f9e5f1c5602f8592..35be6710a119f50d8dc65955f01360ff50db7e78 100644 --- a/cli-chansession.c +++ b/cli-chansession.c @@ -1,3 +1,28 @@ +/* + * Dropbear SSH + * + * Copyright (c) 2002,2003 Matt Johnston + * Copyright (c) 2004 by Mihnea Stoenescu + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + #include "includes.h" #include "packet.h" #include "buffer.h" @@ -7,9 +32,11 @@ #include "ssh.h" #include "runopts.h" #include "termcodes.h" +#include "chansession.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); @@ -17,19 +44,43 @@ static void send_chansess_pty_req(struct Channel *channel); static void send_chansess_shell_req(struct Channel *channel); static void cli_tty_setup(); -void cli_tty_cleanup(); -static const struct ChanType clichansess = { +const struct ChanType clichansess = { 0, /* sepfds */ "session", /* name */ cli_initchansess, /* inithandler */ NULL, /* checkclosehandler */ - NULL, /* reqhandler */ + cli_chansessreq, /* reqhandler */ cli_closechansess, /* closehandler */ }; +static void cli_chansessreq(struct Channel *channel) { + + unsigned char* type = NULL; + int wantreply; + + TRACE(("enter cli_chansessreq")); + + type = buf_getstring(ses.payload, NULL); + wantreply = buf_getbyte(ses.payload); + + if (strcmp(type, "exit-status") != 0) { + TRACE(("unknown request '%s'", type)); + send_msg_channel_failure(channel); + goto out; + } + + /* We'll just trust what they tell us */ + cli_ses.retval = buf_getint(ses.payload); + TRACE(("got exit-status of '%d'", cli_ses.retval)); + +out: + m_free(type); +} + + /* If the main session goes, we close it up */ -static void cli_closechansess(struct Channel *channel) { +static void cli_closechansess(struct Channel *UNUSED(channel)) { /* This channel hasn't gone yet, so we have > 1 */ if (ses.chancount > 1) { @@ -178,7 +229,7 @@ static void put_termcodes() { bufpos2 = ses.writepayload->pos; buf_setpos(ses.writepayload, bufpos1); /* Jump back */ - buf_putint(ses.writepayload, bufpos2 - bufpos1); /* len(termcodes) */ + buf_putint(ses.writepayload, bufpos2 - bufpos1 - 4); /* len(termcodes) */ buf_setpos(ses.writepayload, bufpos2); /* Back where we were */ TRACE(("leave put_termcodes")); @@ -203,7 +254,7 @@ static void put_winsize() { } -static void sigwinch_handler(int dummy) { +static void sigwinch_handler(int UNUSED(unused)) { cli_ses.winchange = 1; @@ -288,9 +339,17 @@ static void send_chansess_shell_req(struct Channel *channel) { static int cli_initchansess(struct Channel *channel) { + channel->infd = STDOUT_FILENO; - //channel->outfd = STDIN_FILENO; - //channel->errfd = STDERR_FILENO; + setnonblocking(STDOUT_FILENO); + + channel->outfd = STDIN_FILENO; + setnonblocking(STDIN_FILENO); + + channel->errfd = STDERR_FILENO; + setnonblocking(STDERR_FILENO); + + channel->extrabuf = cbuf_new(RECV_MAXWINDOW); if (cli_opts.wantpty) { send_chansess_pty_req(channel); diff --git a/cli-kex.c b/cli-kex.c index 436fdad7d205d9cd5779417cac0bf04cd220f0bb..c8949efb287e94f5e32b3e18d42e5d96917c3266 100644 --- a/cli-kex.c +++ b/cli-kex.c @@ -1,7 +1,8 @@ /* * Dropbear - a SSH2 server * - * Copyright (c) 2002,2003 Matt Johnston + * Copyright (c) 2002-2004 Matt Johnston + * Copyright (c) 2004 by Mihnea Stoenescu * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -44,8 +45,8 @@ void send_msg_kexdh_init() { cli_ses.dh_e = (mp_int*)m_malloc(sizeof(mp_int)); cli_ses.dh_x = (mp_int*)m_malloc(sizeof(mp_int)); - m_mp_init_multi(cli_ses.dh_e, cli_ses.dh_x, NULL); + gen_kexdh_vals(cli_ses.dh_e, cli_ses.dh_x); CHECKCLEARTOWRITE(); @@ -58,13 +59,18 @@ void send_msg_kexdh_init() { /* Handle a diffie-hellman key exchange reply. */ void recv_msg_kexdh_reply() { - mp_int dh_f; + DEF_MP_INT(dh_f); sign_key *hostkey = NULL; unsigned int type, keybloblen; unsigned char* keyblob = NULL; TRACE(("enter recv_msg_kexdh_reply")); + + if (cli_ses.kex_state != KEXDH_INIT_SENT) { + dropbear_exit("Received out-of-order kexdhreply"); + } + m_mp_init(&dh_f); type = ses.newkeys->algo_hostkey; TRACE(("type is %d", type)); @@ -82,7 +88,6 @@ void recv_msg_kexdh_reply() { dropbear_exit("Bad KEX packet"); } - m_mp_init(&dh_f); if (buf_getmpint(ses.payload, &dh_f) != DROPBEAR_SUCCESS) { TRACE(("failed getting mpint")); dropbear_exit("Bad KEX packet"); @@ -90,6 +95,9 @@ void recv_msg_kexdh_reply() { kexdh_comb_key(cli_ses.dh_e, cli_ses.dh_x, &dh_f, hostkey); mp_clear(&dh_f); + mp_clear_multi(cli_ses.dh_e, cli_ses.dh_x, NULL); + m_free(cli_ses.dh_e); + m_free(cli_ses.dh_x); if (buf_verify(ses.payload, hostkey, ses.hash, SHA1_HASH_SIZE) != DROPBEAR_SUCCESS) { @@ -125,8 +133,10 @@ static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen) { char * filename = NULL; FILE *hostsfile = NULL; + int readonly = 0; struct passwd *pw = NULL; - unsigned int len, hostlen; + unsigned int hostlen, algolen; + unsigned long len; const char *algoname = NULL; buffer * line = NULL; int ret; @@ -144,20 +154,37 @@ static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen) { /* Check that ~/.ssh exists - easiest way is just to mkdir */ if (mkdir(filename, S_IRWXU) != 0) { if (errno != EEXIST) { + dropbear_log(LOG_INFO, "Warning: failed creating ~/.ssh: %s", + strerror(errno)); + TRACE(("mkdir didn't work: %s", strerror(errno))); ask_to_confirm(keyblob, keybloblen); goto out; /* only get here on success */ } } snprintf(filename, len+18, "%s/.ssh/known_hosts", pw->pw_dir); - hostsfile = fopen(filename, "r+"); + hostsfile = fopen(filename, "a+"); + + if (hostsfile != NULL) { + fseek(hostsfile, 0, SEEK_SET); + } else { + /* We mightn't have been able to open it if it was read-only */ + if (errno == EACCES || errno == EROFS) { + TRACE(("trying readonly: %s", strerror(errno))); + readonly = 1; + hostsfile = fopen(filename, "r"); + } + } + if (hostsfile == NULL) { + TRACE(("hostsfile didn't open: %s", strerror(errno))); ask_to_confirm(keyblob, keybloblen); goto out; /* We only get here on success */ } line = buf_new(MAX_KNOWNHOSTS_LINE); hostlen = strlen(cli_opts.remotehost); + algoname = signkey_name_from_type(ses.newkeys->algo_hostkey, &algolen); do { if (buf_getline(line, hostsfile) == DROPBEAR_FAILURE) { @@ -188,20 +215,19 @@ static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen) { continue; } - algoname = signkey_name_from_type(ses.newkeys->algo_hostkey, &len); - if ( strncmp(buf_getptr(line, len), algoname, len) != 0) { + if ( strncmp(buf_getptr(line, algolen), algoname, algolen) != 0) { TRACE(("algo doesn't match")); continue; } - buf_incrpos(line, len); + buf_incrpos(line, algolen); if (buf_getbyte(line) != ' ') { TRACE(("missing space after algo")); continue; } /* Now we're at the interesting hostkey */ - ret = cmp_base64_key(keyblob, keybloblen, algoname, len, line); + ret = cmp_base64_key(keyblob, keybloblen, algoname, algolen, line); if (ret == DROPBEAR_SUCCESS) { /* Good matching key */ @@ -214,12 +240,39 @@ static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen) { /* Key doesn't exist yet */ ask_to_confirm(keyblob, keybloblen); + /* If we get here, they said yes */ + if (readonly) { + TRACE(("readonly")); + goto out; + } + + /* put the new entry in the file */ + 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_putbyte(line, ' '); + buf_putbytes(line, algoname, algolen); + buf_putbyte(line, ' '); + len = line->size - line->pos; + TRACE(("keybloblen %d, len %d", keybloblen, len)); + /* The only failure with base64 is buffer_overflow, but buf_getwriteptr + * will die horribly in the case anyway */ + base64_encode(keyblob, keybloblen, buf_getwriteptr(line, len), &len); + buf_incrwritepos(line, len); + buf_putbyte(line, '\n'); + buf_setpos(line, 0); + fwrite(buf_getptr(line, line->len), line->len, 1, hostsfile); + /* We ignore errors, since there's not much we can do about them */ + out: if (hostsfile != NULL) { fclose(hostsfile); } m_free(filename); - buf_free(line); + if (line != NULL) { + buf_free(line); + } } diff --git a/cli-main.c b/cli-main.c index 106cc64a53f6ac81ed5aa32b30a65be6884a0486..def2c72b91c408fc311238ffcc931c69e27a2a3c 100644 --- a/cli-main.c +++ b/cli-main.c @@ -1,3 +1,29 @@ +/* + * Dropbear - a SSH2 server + * SSH client implementation + * + * Copyright (c) 2002,2003 Matt Johnston + * Copyright (c) 2004 by Mihnea Stoenescu + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + #include "includes.h" #include "dbutil.h" #include "runopts.h" @@ -26,6 +52,10 @@ int main(int argc, char ** argv) { TRACE(("user='%s' host='%s' port='%s'", cli_opts.username, cli_opts.remotehost, cli_opts.remoteport)); + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { + dropbear_exit("signal() error"); + } + sock = connect_remote(cli_opts.remotehost, cli_opts.remoteport, 0, &error); @@ -70,7 +100,8 @@ static void cli_dropbear_exit(int exitcode, const char* format, va_list param) { exit(exitcode); } -static void cli_dropbear_log(int priority, const char* format, va_list param) { +static void cli_dropbear_log(int UNUSED(priority), + const char* format, va_list param) { char printbuf[1024]; diff --git a/cli-runopts.c b/cli-runopts.c index eb718a41650982afb336de39ea3f0e90a5f0cba9..2aa341344b85eddedbda2f3799846c99eb46ce30 100644 --- a/cli-runopts.c +++ b/cli-runopts.c @@ -28,25 +28,39 @@ #include "buffer.h" #include "dbutil.h" #include "algo.h" +#include "tcpfwd.h" cli_runopts cli_opts; /* GLOBAL */ static void printhelp(); static void parsehostname(char* userhostarg); -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH static void loadidentityfile(const char* filename); #endif +#ifdef ENABLE_CLI_ANYTCPFWD +static void addforward(char* str, struct TCPFwdList** fwdlist); +#endif static void printhelp() { fprintf(stderr, "Dropbear client v%s\n" - "Usage: %s [options] user@host\n" + "Usage: %s [options] [user@]host\n" "Options are:\n" "-p <remoteport>\n" "-t Allocate a pty\n" "-T Don't allocate a pty\n" -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH "-i <identityfile> (multiple allowed)\n" +#endif +#ifdef ENABLE_CLI_LOCALTCPFWD + "-L <listenport:remotehsot:reportport> Local port forwarding\n" +#endif +#ifdef ENABLE_CLI_REMOTETCPFWD + "-R <listenport:remotehost:remoteport> Remote port forwarding\n" +#endif + "-l <username>\n" +#ifdef DEBUG_TRACE + "-v verbose\n" #endif ,DROPBEAR_VERSION, cli_opts.progname); } @@ -56,11 +70,16 @@ void cli_getopts(int argc, char ** argv) { unsigned int i, j; char ** next = 0; unsigned int cmdlen; -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH int nextiskey = 0; /* A flag if the next argument is a keyfile */ #endif - - +#ifdef ENABLE_CLI_LOCALTCPFWD + int nextislocal = 0; +#endif +#ifdef ENABLE_CLI_REMOTETCPFWD + int nextisremote = 0; +#endif + char* dummy = NULL; /* Not used for anything real */ /* see printhelp() for options */ cli_opts.progname = argv[0]; @@ -69,8 +88,14 @@ void cli_getopts(int argc, char ** argv) { cli_opts.username = NULL; cli_opts.cmd = NULL; cli_opts.wantpty = 9; /* 9 means "it hasn't been touched", gets set later */ -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH cli_opts.pubkeys = NULL; +#endif +#ifdef ENABLE_CLI_LOCALTCPFWD + cli_opts.localfwds = NULL; +#endif +#ifdef ENABLE_CLI_REMOTETCPFWD + cli_opts.remotefwds = NULL; #endif opts.nolocaltcp = 0; opts.noremotetcp = 0; @@ -81,13 +106,29 @@ void cli_getopts(int argc, char ** argv) { /* Iterate all the arguments */ for (i = 1; i < (unsigned int)argc; i++) { -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH if (nextiskey) { /* Load a hostkey since the previous argument was "-i" */ loadidentityfile(argv[i]); nextiskey = 0; continue; } +#endif +#ifdef ENABLE_CLI_REMOTETCPFWD + if (nextisremote) { + TRACE(("nextisremote true")); + addforward(argv[i], &cli_opts.remotefwds); + nextisremote = 0; + continue; + } +#endif +#ifdef ENABLE_CLI_LOCALTCPFWD + if (nextislocal) { + TRACE(("nextislocal true")); + addforward(argv[i], &cli_opts.localfwds); + nextislocal = 0; + continue; + } #endif if (next) { /* The previous flag set a value to assign */ @@ -106,7 +147,7 @@ void cli_getopts(int argc, char ** argv) { case 'p': /* remoteport */ next = &cli_opts.remoteport; break; -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH case 'i': /* an identityfile */ nextiskey = 1; break; @@ -117,17 +158,58 @@ void cli_getopts(int argc, char ** argv) { case 'T': /* don't want a pty */ cli_opts.wantpty = 0; break; - default: - fprintf(stderr, "Unknown argument '%s'\n", argv[i]); +#ifdef ENABLE_CLI_LOCALTCPFWD + case 'L': + nextislocal = 1; + break; +#endif +#ifdef ENABLE_CLI_REMOTETCPFWD + case 'R': + nextisremote = 1; + break; +#endif + case 'l': + next = &cli_opts.username; + break; + case 'h': printhelp(); - exit(EXIT_FAILURE); + exit(EXIT_SUCCESS); + break; +#ifdef DEBUG_TRACE + case 'v': + debug_trace = 1; + break; +#endif + case 'F': + case 'e': + case 'c': + case 'm': + case 'D': +#ifndef ENABLE_CLI_REMOTETCPFWD + case 'R': +#endif +#ifndef ENABLE_CLI_LOCALTCPFWD + case 'L': +#endif + case 'o': + case 'b': + next = &dummy; + default: + fprintf(stderr, + "WARNING: Ignoring unknown argument '%s'\n", argv[i]); break; } /* Switch */ + + /* Now we handle args where they might be "-luser" (no spaces)*/ + if (next && strlen(argv[i]) > 2) { + *next = &argv[i][2]; + next = NULL; + } continue; /* next argument */ } else { - TRACE(("non-flag arg")); + TRACE(("non-flag arg: '%s'", argv[i])); /* Either the hostname or commands */ @@ -162,7 +244,8 @@ void cli_getopts(int argc, char ** argv) { } if (cli_opts.remotehost == NULL) { - dropbear_exit("Bad syntax"); + printhelp(); + exit(EXIT_FAILURE); } if (cli_opts.remoteport == NULL) { @@ -180,7 +263,7 @@ void cli_getopts(int argc, char ** argv) { } } -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH static void loadidentityfile(const char* filename) { struct PubkeyList * nextkey; @@ -208,10 +291,14 @@ static void loadidentityfile(const char* filename) { /* Parses a [user@]hostname argument. userhostarg is the argv[i] corresponding * - note that it will be modified */ -static void parsehostname(char* userhostarg) { +static void parsehostname(char* orighostarg) { uid_t uid; struct passwd *pw = NULL; + char *userhostarg = NULL; + + /* We probably don't want to be editing argvs */ + userhostarg = m_strdup(orighostarg); cli_opts.remotehost = strchr(userhostarg, '@'); if (cli_opts.remotehost == NULL) { @@ -239,3 +326,81 @@ static void parsehostname(char* userhostarg) { dropbear_exit("Bad hostname"); } } + +#ifdef ENABLE_CLI_ANYTCPFWD +/* Turn a "listenport:remoteaddr:remoteport" string into into a forwarding + * set, and add it to the forwarding list */ +static void addforward(char* origstr, struct TCPFwdList** fwdlist) { + + char * listenport = NULL; + char * connectport = NULL; + char * connectaddr = NULL; + struct TCPFwdList* newfwd = NULL; + char * str = NULL; + + TRACE(("enter addforward")); + + /* We probably don't want to be editing argvs */ + str = m_strdup(origstr); + + listenport = str; + + connectaddr = strchr(str, ':'); + if (connectaddr == NULL) { + TRACE(("connectaddr == NULL")); + goto fail; + } + + connectaddr[0] = '\0'; + connectaddr++; + + connectport = strchr(connectaddr, ':'); + if (connectport == NULL) { + TRACE(("connectport == NULL")); + goto fail; + } + + connectport[0] = '\0'; + connectport++; + + newfwd = (struct TCPFwdList*)m_malloc(sizeof(struct TCPFwdList)); + + /* Now we check the ports - note that the port ints are unsigned, + * the check later only checks for >= MAX_PORT */ + newfwd->listenport = strtol(listenport, NULL, 10); + if (errno != 0) { + TRACE(("bad listenport strtol")); + goto fail; + } + + newfwd->connectport = strtol(connectport, NULL, 10); + if (errno != 0) { + TRACE(("bad connectport strtol")); + goto fail; + } + + newfwd->connectaddr = connectaddr; + + if (newfwd->listenport > 65535) { + TRACE(("listenport > 65535")); + goto badport; + } + + if (newfwd->connectport > 65535) { + TRACE(("connectport > 65535")); + goto badport; + } + + newfwd->next = *fwdlist; + *fwdlist = newfwd; + + TRACE(("leave addforward: done")); + return; + +fail: + dropbear_exit("Bad TCP forward '%s'", origstr); + +badport: + dropbear_exit("Bad TCP port in '%s'", origstr); +} +#endif diff --git a/cli-service.c b/cli-service.c index 8fbbad48ccedbdaaf795a77aa8488e4f5fb2d45b..d14c77ffaee1e45112ff386afed5e9885f4f356b 100644 --- a/cli-service.c +++ b/cli-service.c @@ -1,3 +1,28 @@ +/* + * Dropbear SSH + * + * Copyright (c) 2002,2003 Matt Johnston + * Copyright (c) 2004 by Mihnea Stoenescu + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + #include "includes.h" #include "service.h" #include "dbutil.h" diff --git a/cli-session.c b/cli-session.c index 973e9c7c8475c24b1c0455bdd3e2ff2065c5524b..671735c036409ffccef68fa962b805c3fe2d7916 100644 --- a/cli-session.c +++ b/cli-session.c @@ -1,11 +1,35 @@ +/* + * Dropbear SSH + * + * Copyright (c) 2002,2003 Matt Johnston + * Copyright (c) 2004 by Mihnea Stoenescu + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + #include "includes.h" #include "session.h" #include "dbutil.h" #include "kex.h" #include "ssh.h" #include "packet.h" -#include "tcpfwd-direct.h" -#include "tcpfwd-remote.h" +#include "tcpfwd.h" #include "channel.h" #include "random.h" #include "service.h" @@ -22,8 +46,9 @@ struct clientsession cli_ses; /* GLOBAL */ /* Sorted in decreasing frequency will be more efficient - data and window * should be first */ static const packettype cli_packettypes[] = { - /* TYPE, AUTHREQUIRED, FUNCTION */ + /* TYPE, FUNCTION */ {SSH_MSG_CHANNEL_DATA, recv_msg_channel_data}, + {SSH_MSG_CHANNEL_EXTENDED_DATA, recv_msg_channel_extended_data}, {SSH_MSG_CHANNEL_WINDOW_ADJUST, recv_msg_channel_window_adjust}, {SSH_MSG_USERAUTH_FAILURE, recv_msg_userauth_failure}, /* client */ {SSH_MSG_USERAUTH_SUCCESS, recv_msg_userauth_success}, /* client */ @@ -31,7 +56,6 @@ static const packettype cli_packettypes[] = { {SSH_MSG_KEXDH_REPLY, recv_msg_kexdh_reply}, /* client */ {SSH_MSG_NEWKEYS, recv_msg_newkeys}, {SSH_MSG_SERVICE_ACCEPT, recv_msg_service_accept}, /* client */ - {SSH_MSG_GLOBAL_REQUEST, recv_msg_global_request_remotetcp}, {SSH_MSG_CHANNEL_REQUEST, recv_msg_channel_request}, {SSH_MSG_CHANNEL_OPEN, recv_msg_channel_open}, {SSH_MSG_CHANNEL_EOF, recv_msg_channel_eof}, @@ -39,15 +63,16 @@ static const packettype cli_packettypes[] = { {SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation}, {SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure}, {SSH_MSG_USERAUTH_BANNER, recv_msg_userauth_banner}, /* client */ -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH {SSH_MSG_USERAUTH_PK_OK, recv_msg_userauth_pk_ok}, /* client */ #endif {0, 0} /* End */ }; static const struct ChanType *cli_chantypes[] = { - /* &chan_tcpdirect etc, though need to only allow if we've requested - * that forwarding */ +#ifdef ENABLE_CLI_REMOTETCPFWD + &cli_chan_tcpremote, +#endif NULL /* Null termination */ }; @@ -88,6 +113,14 @@ static void cli_session_init() { cli_ses.tty_raw_mode = 0; cli_ses.winchange = 0; + /* We store stdin's flags, so we can set them back on exit (otherwise + * busybox's ash isn't happy */ + cli_ses.stdincopy = dup(STDIN_FILENO); + cli_ses.stdinflags = fcntl(STDIN_FILENO, F_GETFL, 0); + + cli_ses.retval = EXIT_SUCCESS; /* Assume it's clean if we don't get a + specific exit status */ + /* Auth */ cli_ses.lastpubkey = NULL; cli_ses.lastauthtype = NULL; @@ -179,6 +212,12 @@ static void cli_sessionloop() { */ case USERAUTH_SUCCESS_RCVD: +#ifdef ENABLE_CLI_LOCALTCPFWD + setup_localtcp(); +#endif +#ifdef ENABLE_CLI_REMOTETCPFWD + setup_remotetcp(); +#endif cli_send_chansess_request(); TRACE(("leave cli_sessionloop: cli_send_chansess_request")); cli_ses.state = SESSION_RUNNING; @@ -210,6 +249,11 @@ void cli_session_cleanup() { if (!sessinitdone) { return; } + + /* Set stdin back to non-blocking - busybox ash dies nastily + * if we don't revert the flags */ + fcntl(cli_ses.stdincopy, F_SETFL, cli_ses.stdinflags); + cli_tty_cleanup(); } @@ -220,11 +264,10 @@ static void cli_finished() { common_session_cleanup(); fprintf(stderr, "Connection to %s@%s:%s closed.\n", cli_opts.username, cli_opts.remotehost, cli_opts.remoteport); - exit(EXIT_SUCCESS); + exit(cli_ses.retval); } - /* called when the remote side closes the connection */ static void cli_remoteclosed() { diff --git a/cli-tcpfwd.c b/cli-tcpfwd.c new file mode 100644 index 0000000000000000000000000000000000000000..4dbef01ffc72795104113d0a622600eb39096408 --- /dev/null +++ b/cli-tcpfwd.c @@ -0,0 +1,193 @@ +/* + * Dropbear SSH + * + * Copyright (c) 2002,2003 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + +#include "includes.h" +#include "options.h" +#include "dbutil.h" +#include "tcpfwd.h" +#include "channel.h" +#include "runopts.h" +#include "session.h" +#include "ssh.h" + +static int cli_localtcp(unsigned int listenport, const char* remoteaddr, + unsigned int remoteport); +static int newtcpforwarded(struct Channel * channel); + +const struct ChanType cli_chan_tcpremote = { + 1, /* sepfds */ + "forwarded-tcpip", + newtcpforwarded, + NULL, + NULL, + NULL +}; +static const struct ChanType cli_chan_tcplocal = { + 1, /* sepfds */ + "direct-tcpip", + NULL, + NULL, + NULL, + NULL +}; + +void setup_localtcp() { + + 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); + 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; + } + TRACE(("leave setup_localtcp")); + +} + +static int cli_localtcp(unsigned int listenport, const char* remoteaddr, + unsigned int remoteport) { + + struct TCPListener* tcpinfo = NULL; + int ret; + + TRACE(("enter cli_localtcp: %d %s %d", listenport, remoteaddr, + remoteport)); + + tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener*)); + tcpinfo->sendaddr = m_strdup(remoteaddr); + tcpinfo->sendport = remoteport; + tcpinfo->listenport = listenport; + tcpinfo->chantype = &cli_chan_tcplocal; + + ret = listen_tcpfwd(tcpinfo); + + if (ret == DROPBEAR_FAILURE) { + m_free(tcpinfo); + } + TRACE(("leave cli_localtcp: %d", ret)); + return ret; +} + +static void send_msg_global_request_remotetcp(int port) { + + 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, 0); + buf_putstring(ses.writepayload, "0.0.0.0", 7); /* TODO: IPv6? */ + buf_putint(ses.writepayload, port); + + encrypt_packet(); + + TRACE(("leave send_msg_global_request_remotetcp")); +} + +void setup_remotetcp() { + + struct TCPFwdList * iter = NULL; + + TRACE(("enter setup_remotetcp")); + + if (cli_opts.remotefwds == NULL) { + TRACE(("cli_opts.remotefwds == NULL")); + } + + iter = cli_opts.remotefwds; + + while (iter != NULL) { + send_msg_global_request_remotetcp(iter->listenport); + iter = iter->next; + } + TRACE(("leave setup_remotetcp")); +} + +static int newtcpforwarded(struct Channel * channel) { + + unsigned int origport; + struct TCPFwdList * iter = NULL; + 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); + + origport = buf_getint(ses.payload); + + /* Find which port corresponds */ + iter = cli_opts.remotefwds; + + while (iter != NULL) { + if (origport == iter->listenport) { + 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); + goto out; + } + + snprintf(portstring, sizeof(portstring), "%d", iter->connectport); + sock = connect_remote(iter->connectaddr, portstring, 1, NULL); + if (sock < 0) { + TRACE(("leave newtcpdirect: sock failed")); + err = SSH_OPEN_CONNECT_FAILED; + goto out; + } + + ses.maxfd = MAX(ses.maxfd, sock); + + /* Note that infd is actually the "outgoing" direction on the + * tcp connection, vice versa for outfd. + * We don't set outfd, that will get set after the connection's + * progress succeeds */ + channel->infd = sock; + channel->initconn = 1; + + err = SSH_OPEN_IN_PROGRESS; + +out: + TRACE(("leave newtcpdirect: err %d", err)); + return err; +} diff --git a/common-algo.c b/common-algo.c index f71fd33376f182072c62e2567d032d3455900ebd..e1d22e19098abff3c7cabce1c573cf3142c53c46 100644 --- a/common-algo.c +++ b/common-algo.c @@ -1,7 +1,8 @@ /* - * Dropbear - a SSH2 server + * Dropbear SSH * * Copyright (c) 2002,2003 Matt Johnston + * Copyright (c) 2004 by Mihnea Stoenescu * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/common-channel.c b/common-channel.c index fbfb00b88cd2471a15033a433caa76a0a17cf062..d30528c5a5acd286f9c8d68d3d25da475096bb46 100644 --- a/common-channel.c +++ b/common-channel.c @@ -1,7 +1,7 @@ /* - * Dropbear - a SSH2 server + * Dropbear SSH * - * Copyright (c) 2002,2003 Matt Johnston + * Copyright (c) 2002-2004 Matt Johnston * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -29,11 +29,10 @@ #include "packet.h" #include "ssh.h" #include "buffer.h" +#include "circbuffer.h" #include "dbutil.h" #include "channel.h" #include "ssh.h" -#include "tcpfwd-direct.h" -#include "tcpfwd-remote.h" #include "listener.h" static void send_msg_channel_open_failure(unsigned int remotechan, int reason, @@ -41,7 +40,7 @@ static void send_msg_channel_open_failure(unsigned int remotechan, int reason, static void send_msg_channel_open_confirmation(struct Channel* channel, unsigned int recvwindow, unsigned int recvmaxpacket); -static void writechannel(struct Channel *channel); +static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf); static void send_msg_channel_window_adjust(struct Channel *channel, unsigned int incr); static void send_msg_channel_data(struct Channel *channel, int isextended, @@ -149,8 +148,10 @@ struct Channel* newchannel(unsigned int remotechan, newchan->errfd = FD_CLOSED; /* this isn't always set to start with */ newchan->initconn = 0; - newchan->writebuf = buf_new(RECV_MAXWINDOW); + newchan->writebuf = cbuf_new(RECV_MAXWINDOW); + newchan->extrabuf = NULL; /* The user code can set it up */ newchan->recvwindow = RECV_MAXWINDOW; + newchan->recvdonelen = 0; newchan->recvmaxpacket = RECV_MAXPACKET; ses.channels[i] = newchan; @@ -162,7 +163,7 @@ struct Channel* newchannel(unsigned int remotechan, } /* Get the channel structure corresponding to a channel number */ -static struct Channel* getchannel(unsigned int chan) { +struct Channel* getchannel(unsigned int chan) { if (chan >= ses.chansize || ses.channels[chan] == NULL) { return NULL; } @@ -174,6 +175,7 @@ void channelio(fd_set *readfd, fd_set *writefd) { struct Channel *channel; unsigned int i; + int ret; /* iterate through all the possible channels */ for (i = 0; i < ses.chansize; i++) { @@ -190,7 +192,8 @@ void channelio(fd_set *readfd, fd_set *writefd) { } /* read from program/pipe stderr */ - if (channel->errfd >= 0 && FD_ISSET(channel->errfd, readfd)) { + if (channel->extrabuf == NULL && + channel->errfd >= 0 && FD_ISSET(channel->errfd, readfd)) { send_msg_channel_data(channel, 1, SSH_EXTENDED_DATA_STDERR); } @@ -198,8 +201,15 @@ void channelio(fd_set *readfd, fd_set *writefd) { * see if it has errors */ if (channel->infd >= 0 && channel->infd != channel->outfd && FD_ISSET(channel->infd, readfd)) { - int ret; - ret = write(channel->infd, NULL, 0); + if (channel->initconn) { + /* Handling for "in progress" connection - this is needed + * to avoid spinning 100% CPU when we connect to a server + * which doesn't send anything (tcpfwding) */ + checkinitdone(channel); + continue; /* Important not to use the channel after + checkinitdone(), as it may be NULL */ + } + ret = write(channel->infd, NULL, 0); /* Fake write */ if (ret < 0 && errno != EINTR && errno != EAGAIN) { closeinfd(channel); } @@ -211,9 +221,14 @@ void channelio(fd_set *readfd, fd_set *writefd) { checkinitdone(channel); continue; /* Important not to use the channel after checkinitdone(), as it may be NULL */ - } else { - writechannel(channel); } + writechannel(channel, channel->infd, channel->writebuf); + } + + /* stderr for client mode */ + if (channel->extrabuf != NULL + && channel->errfd >= 0 && FD_ISSET(channel->errfd, writefd)) { + writechannel(channel, channel->errfd, channel->extrabuf); } /* now handle any of the channel-closing type stuff */ @@ -231,6 +246,14 @@ void channelio(fd_set *readfd, fd_set *writefd) { /* do all the EOF/close type stuff checking for a channel */ static void checkclose(struct Channel *channel) { + TRACE(("checkclose: infd %d, outfd %d, errfd %d, sentclosed %d, recvclosed %d", + channel->infd, channel->outfd, + channel->errfd, channel->sentclosed, channel->recvclosed)); + TRACE(("writebuf %d extrabuf %s extrabuf %d", + cbuf_getused(channel->writebuf), + channel->writebuf, + channel->writebuf ? 0 : cbuf_getused(channel->extrabuf))); + if (!channel->sentclosed) { /* check for exited - currently only used for server sessions, @@ -243,13 +266,13 @@ static void checkclose(struct Channel *channel) { if (!channel->senteof && channel->outfd == FD_CLOSED - && channel->errfd == FD_CLOSED) { + && (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) { send_msg_channel_eof(channel); } if (channel->infd == FD_CLOSED - && channel->outfd == FD_CLOSED - && channel->errfd == FD_CLOSED) { + && channel->outfd == FD_CLOSED + && (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) { send_msg_channel_close(channel); } } @@ -287,10 +310,14 @@ static void checkinitdone(struct Channel *channel) { if (getsockopt(channel->infd, SOL_SOCKET, SO_ERROR, &val, &vallen) || val != 0) { + send_msg_channel_open_failure(channel->remotechan, + SSH_OPEN_CONNECT_FAILED, "", ""); close(channel->infd); deletechannel(channel); TRACE(("leave checkinitdone: fail")); } else { + send_msg_channel_open_confirmation(channel, channel->recvwindow, + channel->recvmaxpacket); channel->outfd = channel->infd; channel->initconn = 0; TRACE(("leave checkinitdone: success")); @@ -307,13 +334,6 @@ static void send_msg_channel_close(struct Channel *channel) { if (channel->type->closehandler) { channel->type->closehandler(channel); } -#if 0 - if (channel->type == CHANNEL_ID_SESSION) { - send_exitsignalstatus(channel); - - closechansess(channel); - } -#endif CHECKCLEARTOWRITE(); @@ -343,49 +363,53 @@ static void send_msg_channel_eof(struct Channel *channel) { TRACE(("leave send_msg_channel_eof")); } -/* Called to write data out to the server side of a channel (eg a shell or a - * program. +/* Called to write data out to the local side of the channel. * Only called when we know we can write to a channel, writes as much as * possible */ -static void writechannel(struct Channel* channel) { +static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf) { int len, maxlen; - buffer *buf; TRACE(("enter writechannel")); - buf = channel->writebuf; - maxlen = buf->len - buf->pos; + maxlen = cbuf_readlen(cbuf); - len = write(channel->infd, buf_getptr(buf, maxlen), maxlen); + /* Write the data out */ + len = write(fd, cbuf_readptr(cbuf, maxlen), maxlen); if (len <= 0) { if (len < 0 && errno != EINTR) { - /* no more to write */ + /* no more to write - we close it even if the fd was stderr, since + * that's a nasty failure too */ closeinfd(channel); } TRACE(("leave writechannel: len <= 0")); return; } - - if (len == maxlen) { - buf_setpos(buf, 0); - buf_setlen(buf, 0); - if (channel->recveof) { - /* we're closing up */ - closeinfd(channel); - return; - TRACE(("leave writechannel: recveof set")); - } + cbuf_incrread(cbuf, len); + channel->recvdonelen += len; - /* extend the window if we're at the end*/ - /* TODO - this is inefficient */ - send_msg_channel_window_adjust(channel, buf->size - - channel->recvwindow); - channel->recvwindow = buf->size; - } else { - buf_incrpos(buf, len); + if (fd == channel->infd && len == maxlen && channel->recveof) { + /* Check if we're closing up */ + closeinfd(channel); + TRACE(("leave writechannel: recveof set")); + return; + } + + /* Window adjust handling */ + if (channel->recvdonelen >= RECV_WINDOWEXTEND) { + /* Set it back to max window */ + send_msg_channel_window_adjust(channel, channel->recvdonelen); + channel->recvwindow += channel->recvdonelen; + channel->recvdonelen = 0; } + + assert(channel->recvwindow <= RECV_MAXWINDOW); + assert(channel->recvwindow <= cbuf_getavail(channel->writebuf)); + assert(channel->extrabuf == NULL || + channel->recvwindow <= cbuf_getavail(channel->extrabuf)); + + TRACE(("leave writechannel")); } @@ -403,30 +427,38 @@ void setchannelfds(fd_set *readfd, fd_set *writefd) { continue; } - /* stdout and stderr */ + /* Stuff to put over the wire */ if (channel->transwindow > 0) { - /* stdout */ if (channel->outfd >= 0) { - /* there's space to read more from the program */ FD_SET(channel->outfd, readfd); } - /* stderr */ - if (channel->errfd >= 0) { + + if (channel->extrabuf == NULL && channel->errfd >= 0) { FD_SET(channel->errfd, readfd); } } + /* For checking FD status (ie closure etc) - we don't actually + * read data from infd */ + TRACE(("infd = %d, outfd %d, errfd %d, bufused %d", + channel->infd, channel->outfd, + channel->errfd, + cbuf_getused(channel->writebuf) )); if (channel->infd >= 0 && channel->infd != channel->outfd) { FD_SET(channel->infd, readfd); } - /* stdin */ - if (channel->infd >= 0 && - (channel->writebuf->pos < channel->writebuf->len || - channel->initconn)) { - /* there's space to write more to the program */ - FD_SET(channel->infd, writefd); + /* Stuff from the wire, to local program/shell/user etc */ + if ((channel->infd >= 0 && cbuf_getused(channel->writebuf) > 0 ) + || channel->initconn) { + + FD_SET(channel->infd, writefd); + } + + if (channel->extrabuf != NULL && channel->errfd >= 0 + && cbuf_getused(channel->extrabuf) > 0 ) { + FD_SET(channel->errfd, writefd); } } /* foreach channel */ @@ -455,7 +487,9 @@ void recv_msg_channel_eof() { } channel->recveof = 1; - if (channel->writebuf->len == 0) { + if (cbuf_getused(channel->writebuf) == 0 + && (channel->extrabuf == NULL + || cbuf_getused(channel->extrabuf) == 0)) { closeinfd(channel); } @@ -497,15 +531,22 @@ static void removechannel(struct Channel * channel) { TRACE(("enter removechannel")); TRACE(("channel index is %d", channel->index)); - buf_free(channel->writebuf); + cbuf_free(channel->writebuf); + channel->writebuf = NULL; + + if (channel->extrabuf) { + cbuf_free(channel->extrabuf); + channel->extrabuf = NULL; + } + /* close the FDs in case they haven't been done * yet (ie they were shutdown etc */ close(channel->infd); close(channel->outfd); - if (channel->errfd >= 0) { - close(channel->errfd); - } + close(channel->errfd); + + channel->typedata = NULL; deletechannel(channel); @@ -545,23 +586,6 @@ void recv_msg_channel_request() { send_msg_channel_failure(channel); } -#if 0 - /* handle according to channel type */ - switch (channel->type) { - - case CHANNEL_ID_SESSION: - TRACE(("continue recv_msg_channel_request: session request")); - /* XXX server */ - /* Here we need to do things channel-specific style. Function - * pointer callbacks perhaps */ - chansessionrequest(channel); - break; - - default: - send_msg_channel_failure(channel); - } -#endif - TRACE(("leave recv_msg_channel_request")); } @@ -604,15 +628,19 @@ static void send_msg_channel_data(struct Channel *channel, int isextended, } /* read the data */ + TRACE(("maxlen %d", maxlen)); buf = buf_new(maxlen); + TRACE(("buf pos %d data %x", buf->pos, buf->data)); len = read(fd, buf_getwriteptr(buf, maxlen), maxlen); if (len <= 0) { /* on error/eof, send eof */ if (len == 0 || errno != EINTR) { closeoutfd(channel, fd); - TRACE(("leave send_msg_channel_data: read err %d", channel->index)); } buf_free(buf); + buf = NULL; + TRACE(("leave send_msg_channel_data: read err or EOF for fd %d", + channel->index)); return; } buf_incrlen(buf, len); @@ -627,6 +655,7 @@ static void send_msg_channel_data(struct Channel *channel, int isextended, buf_putstring(ses.writepayload, buf_getptr(buf, len), len); buf_free(buf); + buf = NULL; channel->transwindow -= len; @@ -634,59 +663,72 @@ static void send_msg_channel_data(struct Channel *channel, int isextended, TRACE(("leave send_msg_channel_data")); } - -/* when we receive channel data, put it in a buffer for writing to the program/ - * shell etc */ +/* We receive channel data */ void recv_msg_channel_data() { unsigned int chan; - struct Channel * channel; - unsigned int datalen; - unsigned int pos; - unsigned int maxdata; + struct Channel *channel; - TRACE(("enter recv_msg_channel_data")); - chan = buf_getint(ses.payload); channel = getchannel(chan); + if (channel == NULL) { dropbear_exit("Unknown channel"); } + common_recv_msg_channel_data(channel, channel->infd, channel->writebuf); +} + +/* Shared for data and stderr data - when we receive data, put it in a buffer + * for writing to the local file descriptor */ +void common_recv_msg_channel_data(struct Channel *channel, int fd, + circbuffer * cbuf) { + + unsigned int datalen; + unsigned int maxdata; + unsigned int buflen; + unsigned int len; + + TRACE(("enter recv_msg_channel_data")); + if (channel->recveof) { dropbear_exit("received data after eof"); } - if (channel->infd < 0) { + if (fd < 0) { dropbear_exit("received data with bad infd"); } datalen = buf_getint(ses.payload); - /* if the client is going to send us more data than we've allocated, then - * it has ignored the windowsize, so we "MAY ignore all extra data" */ - maxdata = channel->writebuf->size - channel->writebuf->pos; + + maxdata = cbuf_getavail(cbuf); + + /* Whilst the spec says we "MAY ignore data past the end" this could + * lead to corrupted file transfers etc (chunks missed etc). It's better to + * just die horribly */ if (datalen > maxdata) { - TRACE(("Warning: recv_msg_channel_data: extra data past window")); - datalen = maxdata; + dropbear_exit("Oversized packet"); } - /* write to the buffer - we always append to the end of the buffer */ - pos = channel->writebuf->pos; - buf_setpos(channel->writebuf, channel->writebuf->len); - memcpy(buf_getwriteptr(channel->writebuf, datalen), - buf_getptr(ses.payload, datalen), datalen); - buf_incrwritepos(channel->writebuf, datalen); - buf_setpos(channel->writebuf, pos); /* revert pos */ + /* We may have to run throught twice, if the buffer wraps around. Can't + * just "leave it for next time" like with writechannel, since this + * is payload data */ + len = datalen; + while (len > 0) { + buflen = cbuf_writelen(cbuf); + buflen = MIN(buflen, len); + + memcpy(cbuf_writeptr(cbuf, buflen), + buf_getptr(ses.payload, buflen), buflen); + cbuf_incrwrite(cbuf, buflen); + buf_incrpos(ses.payload, buflen); + len -= buflen; + } + assert(channel->recvwindow >= datalen); channel->recvwindow -= datalen; - - /* matt - this might be for later */ -/* if (channel->recvwindow < RECV_MINWINDOW) { - send_msg_channel_window_adjust(channel, - RECV_MAXWINDOW - channel->recvwindow); - channel->recvwindow = RECV_MAXWINDOW; - }*/ + assert(channel->recvwindow <= RECV_MAXWINDOW); TRACE(("leave recv_msg_channel_data")); } @@ -790,6 +832,10 @@ void recv_msg_channel_open() { if (channel->type->inithandler) { ret = channel->type->inithandler(channel); if (ret > 0) { + if (ret == SSH_OPEN_IN_PROGRESS) { + /* We'll send the confirmation later */ + goto cleanup; + } errtype = ret; deletechannel(channel); TRACE(("inithandler returned failure %d", ret)); @@ -797,23 +843,6 @@ void recv_msg_channel_open() { } } -#if 0 - /* type specific initialisation */ - if (typeval == CHANNEL_ID_SESSION) { - newchansess(channel); -#ifndef DISABLE_LOCALTCPFWD - } else if (typeval == CHANNEL_ID_TCPDIRECT) { - if (ses.opts->nolocaltcp) { - errtype = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED; - } else if (newtcpdirect(channel) == DROPBEAR_FAILURE) { - errtype = SSH_OPEN_CONNECT_FAILED; - deletechannel(channel); - goto failure; - } -#endif - } -#endif - /* success */ send_msg_channel_open_confirmation(channel, channel->recvwindow, channel->recvmaxpacket); @@ -910,10 +939,7 @@ int send_msg_channel_open_init(int fd, const struct ChanType *type) { } /* set fd non-blocking */ - if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { - TRACE(("leave send_msg_channel_open_init() - FAILED in fcntl()")); - return DROPBEAR_FAILURE; - } + setnonblocking(fd); chan->infd = chan->outfd = fd; ses.maxfd = MAX(ses.maxfd, fd); @@ -1017,15 +1043,19 @@ static void closechanfd(struct Channel *channel, int fd, int how) { closein = closeout = 1; } - if (closeout && fd == channel->errfd) { - channel->errfd = FD_CLOSED; - } if (closeout && fd == channel->outfd) { channel->outfd = FD_CLOSED; } + if (closeout && (channel->extrabuf == NULL) && (fd == channel->errfd)) { + channel->errfd = FD_CLOSED; + } + if (closein && fd == channel->infd) { channel->infd = FD_CLOSED; } + if (closein && (channel->extrabuf != NULL) && (fd == channel->errfd)) { + channel->errfd = FD_CLOSED; + } } #endif /* USING_LISTENERS */ diff --git a/common-kex.c b/common-kex.c index b9b8ba3e3c7e5b371db0a6bd143c2fce41faf60c..38ce0903249cc9a82ce490876779ad370f3cf78d 100644 --- a/common-kex.c +++ b/common-kex.c @@ -1,9 +1,5 @@ /* - * Dropbear - a SSH2 server - * SSH client implementation - * - * This code is copied from the larger file "kex.c" - * some functions are verbatim, others are generalized --mihnea + * Dropbear SSH * * Copyright (c) 2002-2004 Matt Johnston * Portions Copyright (c) 2004 by Mihnea Stoenescu @@ -468,15 +464,18 @@ void recv_msg_kexinit() { /* Initialises and generate one side of the diffie-hellman key exchange values. * See the ietf-secsh-transport draft, section 6, for details */ +/* dh_pub and dh_priv MUST be already initialised */ void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv) { - mp_int dh_p, dh_q, dh_g; + DEF_MP_INT(dh_p); + DEF_MP_INT(dh_q); + DEF_MP_INT(dh_g); unsigned char randbuf[DH_P_LEN]; int dh_q_len; TRACE(("enter send_msg_kexdh_reply")); - m_mp_init_multi(&dh_g, &dh_p, &dh_q, dh_priv, dh_pub, NULL); + m_mp_init_multi(&dh_g, &dh_p, &dh_q, NULL); /* read the prime and generator*/ if (mp_read_unsigned_bin(&dh_p, (unsigned char*)dh_p_val, DH_P_LEN) @@ -635,42 +634,44 @@ static void read_kex_algos() { /* encryption_algorithms_client_to_server */ c2s_cipher_algo = ses.buf_match_algo(ses.payload, sshciphers, &goodguess); - if (algo == NULL) { + if (c2s_cipher_algo == NULL) { erralgo = "enc c->s"; goto error; } + TRACE(("c2s is %s", c2s_cipher_algo->name)); /* encryption_algorithms_server_to_client */ s2c_cipher_algo = ses.buf_match_algo(ses.payload, sshciphers, &goodguess); - if (algo == NULL) { + if (s2c_cipher_algo == NULL) { erralgo = "enc s->c"; goto error; } + TRACE(("s2c is %s", s2c_cipher_algo->name)); /* mac_algorithms_client_to_server */ c2s_hash_algo = ses.buf_match_algo(ses.payload, sshhashes, &goodguess); - if (algo == NULL) { + if (c2s_hash_algo == NULL) { erralgo = "mac c->s"; goto error; } /* mac_algorithms_server_to_client */ s2c_hash_algo = ses.buf_match_algo(ses.payload, sshhashes, &goodguess); - if (algo == NULL) { + if (s2c_hash_algo == NULL) { erralgo = "mac s->c"; goto error; } /* compression_algorithms_client_to_server */ c2s_comp_algo = ses.buf_match_algo(ses.payload, sshcompress, &goodguess); - if (algo == NULL) { + if (c2s_comp_algo == NULL) { erralgo = "comp c->s"; goto error; } /* compression_algorithms_server_to_client */ s2c_comp_algo = ses.buf_match_algo(ses.payload, sshcompress, &goodguess); - if (algo == NULL) { + if (s2c_comp_algo == NULL) { erralgo = "comp s->c"; goto error; } diff --git a/common-session.c b/common-session.c index 601136fef0f9bf7ba5a81b158daa9101e0cb2658..f2207535f0f7903ac519bf8205f5b986c27a0ae5 100644 --- a/common-session.c +++ b/common-session.c @@ -35,6 +35,8 @@ #include "channel.h" #include "atomicio.h" +static void checktimeouts(); +static int ident_readln(int fd, char* buf, int count); struct sshsession ses; /* GLOBAL */ @@ -46,8 +48,6 @@ int sessinitdone = 0; /* GLOBAL */ int exitflag = 0; /* GLOBAL */ -static void checktimeouts(); -static int ident_readln(int fd, char* buf, int count); /* called only at the start of a session, set up initial state */ void common_session_init(int sock, char* remotehost) { @@ -223,6 +223,7 @@ void session_identification() { char linebuf[256]; int len = 0; char done = 0; + int i; /* write our version string, this blocks */ if (atomicio(write, ses.sock, LOCAL_IDENT "\r\n", @@ -230,14 +231,27 @@ void session_identification() { dropbear_exit("Error writing ident string"); } - len = ident_readln(ses.sock, linebuf, 256); - if (len >= 4 && memcmp(linebuf, "SSH-", 4) == 0) { - /* start of line matches */ - done = 1; + /* We allow up to 9 lines before the actual version string, to + * account for wrappers/cruft etc. According to the spec only the client + * needs to handle this, but no harm in letting the server handle it too */ + for (i = 0; i < 10; i++) { + len = ident_readln(ses.sock, linebuf, sizeof(linebuf)); + + if (len < 0 && errno != EINTR) { + /* It failed */ + break; + } + + if (len >= 4 && memcmp(linebuf, "SSH-", 4) == 0) { + /* start of line matches */ + done = 1; + break; + } } if (!done) { - dropbear_exit("Failed to get client version"); + TRACE(("err: %s for '%s'\n", strerror(errno), linebuf)); + dropbear_exit("Failed to get remote version"); } else { /* linebuf is already null terminated */ ses.remoteident = m_malloc(len); diff --git a/compat.c b/compat.c index fb6e70a0786b3b3038692122e00e241f8a801b59..7e0c1ac765383d115ad1cbe6627d4c5d407b4802 100644 --- a/compat.c +++ b/compat.c @@ -190,7 +190,7 @@ int daemon(int nochdir, int noclose) { #ifndef HAVE_BASENAME -char *basename(char *path) { +char *basename(const char *path) { char *foo = strrchr(path, '/'); return ++foo; diff --git a/compat.h b/compat.h index f4ac823e6019a4b41a5ca1acd7e4b6cce39c44af..1ab344fe7e20a18b581333290855bd2097cf381c 100644 --- a/compat.h +++ b/compat.h @@ -1,3 +1,27 @@ +/* + * Dropbear SSH + * + * Copyright (c) 2002,2003 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + #ifndef _COMPAT_H_ #define _COMPAT_H_ diff --git a/configure.in b/configure.in index d3c5229897a65d0c9c37478598dbe021911daafb..d29ae89f031ed2a0326a18548b7361ab2f4a2844 100644 --- a/configure.in +++ b/configure.in @@ -1,7 +1,9 @@ # -*- Autoconf -*- # Process this file with autoconf and autoheader to produce a configure script. -# This Autoconf file was cobbled from various locations. +# This Autoconf file was cobbled from various locations. In particular, a bunch +# of the platform checks have been taken straight from OpenSSH's configure.ac +# Huge thanks to them for dealing with the horrible platform-specifics :) AC_PREREQ(2.50) AC_INIT(buffer.c) @@ -50,10 +52,17 @@ case "$host" in *-*-aix*) AC_DEFINE(AIX,,Using AIX) + # OpenSSH thinks it's broken. If it isn't, let me know. + AC_DEFINE(BROKEN_GETADDRINFO,,Broken getaddrinfo) ;; *-*-hpux*) LIBS="$LIBS -lsec" + # It's probably broken. + AC_DEFINE(BROKEN_GETADDRINFO,,Broken getaddrinfo) + ;; +*-dec-osf*) + AC_DEFINE(BROKEN_GETADDRINFO,,Broken getaddrinfo) ;; esac @@ -241,6 +250,84 @@ AC_CHECK_TYPE([socklen_t], ,[ [#include <sys/types.h> #include <sys/socket.h>]) +# for the fake-rfc2553 stuff - straight from OpenSSH + +AC_CACHE_CHECK([for struct sockaddr_storage], ac_cv_have_struct_sockaddr_storage, [ + AC_TRY_COMPILE( + [ +#include <sys/types.h> +#include <sys/socket.h> + ], + [ struct sockaddr_storage s; ], + [ ac_cv_have_struct_sockaddr_storage="yes" ], + [ ac_cv_have_struct_sockaddr_storage="no" ] + ) +]) +if test "x$ac_cv_have_struct_sockaddr_storage" = "xyes" ; then + AC_DEFINE(HAVE_STRUCT_SOCKADDR_STORAGE) +fi + +AC_CACHE_CHECK([for struct sockaddr_in6], ac_cv_have_struct_sockaddr_in6, [ + AC_TRY_COMPILE( + [ +#include <sys/types.h> +#include <netinet/in.h> + ], + [ struct sockaddr_in6 s; s.sin6_family = 0; ], + [ ac_cv_have_struct_sockaddr_in6="yes" ], + [ ac_cv_have_struct_sockaddr_in6="no" ] + ) +]) +if test "x$ac_cv_have_struct_sockaddr_in6" = "xyes" ; then + AC_DEFINE(HAVE_STRUCT_SOCKADDR_IN6,,Have struct sockaddr_in6) +fi + +AC_CACHE_CHECK([for struct in6_addr], ac_cv_have_struct_in6_addr, [ + AC_TRY_COMPILE( + [ +#include <sys/types.h> +#include <netinet/in.h> + ], + [ struct in6_addr s; s.s6_addr[0] = 0; ], + [ ac_cv_have_struct_in6_addr="yes" ], + [ ac_cv_have_struct_in6_addr="no" ] + ) +]) +if test "x$ac_cv_have_struct_in6_addr" = "xyes" ; then + AC_DEFINE(HAVE_STRUCT_IN6_ADDR,,Have struct in6_addr) +fi + +AC_CACHE_CHECK([for struct addrinfo], ac_cv_have_struct_addrinfo, [ + AC_TRY_COMPILE( + [ +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> + ], + [ struct addrinfo s; s.ai_flags = AI_PASSIVE; ], + [ ac_cv_have_struct_addrinfo="yes" ], + [ ac_cv_have_struct_addrinfo="no" ] + ) +]) +if test "x$ac_cv_have_struct_addrinfo" = "xyes" ; then + AC_DEFINE(HAVE_STRUCT_ADDRINFO,,Have struct addrinfo) +fi + + +# IRIX has a const char return value for gai_strerror() +AC_CHECK_FUNCS(gai_strerror,[ + AC_DEFINE(HAVE_GAI_STRERROR) + AC_TRY_COMPILE([ +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> + +const char *gai_strerror(int);],[ +char *str; + +str = gai_strerror(0);],[ + AC_DEFINE(HAVE_CONST_GAI_STRERROR_PROTO, 1, + [Define if gai_strerror() returns const char *])])]) # for loginrec.c @@ -498,7 +585,7 @@ AC_PROG_GCC_TRADITIONAL 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 ]) +AC_CHECK_FUNCS([dup2 getspnam getusershell memset putenv select socket strdup clearenv strlcpy strlcat daemon basename _getpty getaddrinfo freeaddrinfo getnameinfo]) AC_SEARCH_LIBS(basename, gen, AC_DEFINE(HAVE_BASENAME)) @@ -519,6 +606,7 @@ if test -z "$no_ptc_check" ; then fi fi +AC_EXEEXT AC_CONFIG_HEADER(config.h) AC_OUTPUT(Makefile) AC_MSG_RESULT() diff --git a/dbmulti.c b/dbmulti.c index 24adce17cce2fed515780935bd0630428174abdf..2b8df3f1fb8408d89550e6e9fcaeb2432ce8cfe4 100644 --- a/dbmulti.c +++ b/dbmulti.c @@ -1,3 +1,27 @@ +/* + * Dropbear SSH + * + * Copyright (c) 2002,2003 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + #include "includes.h" /* definitions are cleanest if we just put them here */ @@ -20,7 +44,8 @@ int main(int argc, char ** argv) { } #endif #ifdef DBMULTI_dbclient - if (strcmp(progname, "dbclient") == 0) { + if (strcmp(progname, "dbclient") == 0 + || strcmp(progname, "ssh") == 0) { return cli_main(argc, argv); } #endif @@ -47,7 +72,7 @@ int main(int argc, char ** argv) { "'dropbear' - the Dropbear server\n" #endif #ifdef DBMULTI_dbclient - "'dbclient' - the Dropbear client\n" + "'dbclient' or 'ssh' - the Dropbear client\n" #endif #ifdef DBMULTI_dropbearkey "'dropbearkey' - the key generator\n" diff --git a/dbutil.c b/dbutil.c index dd6841683b3483faf8cac8b5dcbe95d654f4ca6f..4036841a2b35284361321399c1a77746c4a62041 100644 --- a/dbutil.c +++ b/dbutil.c @@ -56,8 +56,19 @@ #define MAX_FMT 100 -void (*_dropbear_exit)(int exitcode, const char* format, va_list param) = NULL; -void (*_dropbear_log)(int priority, const char* format, va_list param) = NULL; +static void generic_dropbear_exit(int exitcode, const char* format, + va_list param); +static void generic_dropbear_log(int priority, const char* format, + va_list param); + +void (*_dropbear_exit)(int exitcode, const char* format, va_list param) + = generic_dropbear_exit; +void (*_dropbear_log)(int priority, const char* format, va_list param) + = generic_dropbear_log; + +#ifdef DEBUG_TRACE +int debug_trace = 0; +#endif int usingsyslog = 0; /* set by runopts, but required externally to sessions */ #ifndef DISABLE_SYSLOG @@ -88,6 +99,28 @@ void dropbear_exit(const char* format, ...) { va_end(param); } +static void generic_dropbear_exit(int exitcode, const char* format, + va_list param) { + + char fmtbuf[300]; + + snprintf(fmtbuf, sizeof(fmtbuf), "Exited: %s", format); + + _dropbear_log(LOG_INFO, fmtbuf, param); + + exit(exitcode); +} + +static void generic_dropbear_log(int UNUSED(priority), const char* format, + va_list param) { + + char printbuf[1024]; + + vsnprintf(printbuf, sizeof(printbuf), format, param); + + fprintf(stderr, "%s\n", printbuf); + +} /* this is what can be called to write arbitrary log messages */ void dropbear_log(int priority, const char* format, ...) { @@ -105,6 +138,10 @@ void dropbear_trace(const char* format, ...) { va_list param; + if (!debug_trace) { + return; + } + va_start(param, format); fprintf(stderr, "TRACE: "); vfprintf(stderr, format, param); @@ -113,8 +150,114 @@ void dropbear_trace(const char* format, ...) { } #endif /* DEBUG_TRACE */ +/* Listen on address:port. Unless address is NULL, in which case listen on + * everything. If called with address == "", we'll listen on localhost/loopback. + * Returns the number of sockets bound on success, or -1 on failure. On + * failure, if errstring wasn't NULL, it'll be a newly malloced error + * string.*/ +int dropbear_listen(const char* address, const char* port, + int *socks, unsigned int sockcount, char **errstring, int *maxfd) { + + struct addrinfo hints, *res = NULL, *res0 = NULL; + int err; + unsigned int nsock; + struct linger linger; + int val; + int sock; + + TRACE(("enter dropbear_listen")); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; /* TODO: let them flag v4 only etc */ + hints.ai_socktype = SOCK_STREAM; + + if (address && address[0] == '\0') { + TRACE(("dropbear_listen: local loopback")); + address = NULL; + } else { + TRACE(("dropbear_listen: not local loopback")); + hints.ai_flags = AI_PASSIVE; + } + err = getaddrinfo(address, port, &hints, &res0); + + if (err) { + if (errstring != NULL && *errstring == NULL) { + int len; + len = 20 + strlen(gai_strerror(err)); + *errstring = (char*)m_malloc(len); + snprintf(*errstring, len, "Error resolving: %s", gai_strerror(err)); + } + TRACE(("leave dropbear_listen: failed resolving")); + return -1; + } + + + nsock = 0; + for (res = res0; res != NULL && nsock < sockcount; + res = res->ai_next) { + + /* Get a socket */ + socks[nsock] = socket(res->ai_family, res->ai_socktype, + res->ai_protocol); + + sock = socks[nsock]; /* For clarity */ + + if (sock < 0) { + err = errno; + TRACE(("socket() failed")); + continue; + } + + /* Various useful socket options */ + val = 1; + /* set to reuse, quick timeout */ + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*) &val, sizeof(val)); + linger.l_onoff = 1; + linger.l_linger = 5; + setsockopt(sock, SOL_SOCKET, SO_LINGER, (void*)&linger, sizeof(linger)); + + /* disable nagle */ + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&val, sizeof(val)); + + if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { + err = errno; + close(sock); + TRACE(("bind(%s) failed", port)); + continue; + } + + if (listen(sock, 20) < 0) { + err = errno; + close(sock); + TRACE(("listen() failed")); + continue; + } + + *maxfd = MAX(*maxfd, sock); + + nsock++; + } + + if (nsock == 0) { + if (errstring != NULL && *errstring == NULL) { + int len; + len = 20 + strlen(strerror(err)); + *errstring = (char*)m_malloc(len); + snprintf(*errstring, len, "Error listening: %s", strerror(err)); + TRACE(("leave dropbear_listen: failure, %s", strerror(err))); + return -1; + } + } + + TRACE(("leave dropbear_listen: success, %d socks bound", nsock)); + return nsock; +} + /* Connect via TCP to a host. Connection will try ipv4 or ipv6, will - * return immediately if nonblocking is set */ + * return immediately if nonblocking is set. On failure, if errstring + * wasn't null, it will be a newly malloced error message */ + +/* TODO: maxfd */ int connect_remote(const char* remotehost, const char* remoteport, int nonblocking, char ** errstring) { @@ -167,7 +310,7 @@ int connect_remote(const char* remotehost, const char* remoteport, } if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) { - if (errno == EINPROGRESS) { + if (errno == EINPROGRESS && nonblocking) { TRACE(("Connect in progress")); break; } else { @@ -181,7 +324,7 @@ int connect_remote(const char* remotehost, const char* remoteport, break; /* Success */ } - if (sock < 0) { + if (sock < 0 && !(errno == EINPROGRESS && nonblocking)) { /* Failed */ if (errstring != NULL && *errstring == NULL) { int len; @@ -197,58 +340,70 @@ int connect_remote(const char* remotehost, const char* remoteport, } freeaddrinfo(res0); + if (sock > 0 && errstring != NULL && *errstring != NULL) { + m_free(*errstring); + } - TRACE(("leave connect_remote: sock %d", sock)); + TRACE(("leave connect_remote: sock %d\n", sock)); return sock; } /* Return a string representation of the socket address passed. The return * value is allocated with malloc() */ -unsigned char * getaddrstring(struct sockaddr * addr) { +unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport) { - char *retstring; + char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; + char *retstring = NULL; + int ret; + unsigned int len; - /* space for "255.255.255.255:65535\0" = 22 */ - retstring = m_malloc(22); + len = sizeof(struct sockaddr_storage); - switch (addr->sa_family) { - case PF_INET: - snprintf(retstring, 22, "%s:%hu", - inet_ntoa(((struct sockaddr_in *)addr)->sin_addr), - ((struct sockaddr_in *)addr)->sin_port); - break; + ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf), + sbuf, sizeof(sbuf), NI_NUMERICSERV | NI_NUMERICHOST); - default: - /* XXX ipv6 */ - strcpy(retstring, "Bad protocol"); + 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); } + 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 * addr) { +char* getaddrhostname(struct sockaddr_storage * addr) { - struct hostent *host = NULL; - char * retstring; + char hbuf[NI_MAXHOST]; + char sbuf[NI_MAXSERV]; + int ret; + unsigned int len; -#ifdef DO_HOST_LOOKUP - host = gethostbyaddr((char*)&((struct sockaddr_in*)addr)->sin_addr, - sizeof(struct in_addr), AF_INET); -#endif - - if (host == NULL) { - /* return the address */ - retstring = inet_ntoa(((struct sockaddr_in *)addr)->sin_addr); - } else { - /* return the hostname */ - retstring = host->h_name; + len = sizeof(struct sockaddr_storage); + + ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf), + sbuf, sizeof(sbuf), NI_NUMERICSERV); + + 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(retstring); + return m_strdup(hbuf); } + #ifdef DEBUG_TRACE void printhex(unsigned char* buf, int len) { @@ -324,7 +479,7 @@ int buf_readfile(buffer* buf, const char* filename) { * authkeys file. * Will return DROPBEAR_SUCCESS if data is read, or DROPBEAR_FAILURE on EOF.*/ /* Only used for ~/.ssh/known_hosts and ~/.ssh/authorized_keys */ -#if defined(DROPBEAR_CLIENT) || defined(DROPBEAR_PUBKEY_AUTH) +#if defined(DROPBEAR_CLIENT) || defined(ENABLE_SVR_PUBKEY_AUTH) int buf_getline(buffer * line, FILE * authfile) { int c = EOF; @@ -351,18 +506,17 @@ int buf_getline(buffer * line, FILE * authfile) { out: - buf_setpos(line, 0); /* if we didn't read anything before EOF or error, exit */ if (c == EOF && line->pos == 0) { - TRACE(("leave getauthline: failure")); + TRACE(("leave buf_getline: failure")); return DROPBEAR_FAILURE; } else { - TRACE(("leave getauthline: success")); + TRACE(("leave buf_getline: success")); + buf_setpos(line, 0); return DROPBEAR_SUCCESS; } - TRACE(("leave buf_getline")); } #endif @@ -440,3 +594,13 @@ void m_burn(void *data, unsigned int len) { } } + +void setnonblocking(int fd) { + + TRACE(("setnonblocking: %d", fd)); + + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { + dropbear_exit("Couldn't set nonblocking"); + } + TRACE(("leave setnonblocking")); +} diff --git a/dbutil.h b/dbutil.h index 21a9955648fe0ea65e296962cee90048d1c0144a..6363f70d1107d135f638c4500c9fe30f89b50fe2 100644 --- a/dbutil.h +++ b/dbutil.h @@ -42,12 +42,15 @@ void dropbear_log(int priority, const char* format, ...); #ifdef DEBUG_TRACE void dropbear_trace(const char* format, ...); void printhex(unsigned char* buf, int len); +extern int debug_trace; #endif char * stripcontrol(const char * text); -unsigned char * getaddrstring(struct sockaddr * addr); +unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport); +int dropbear_listen(const char* address, const char* port, + int *socks, unsigned int sockcount, char **errstring, int *maxfd); int connect_remote(const char* remotehost, const char* remoteport, int nonblocking, char ** errstring); -char* getaddrhostname(struct sockaddr * addr); +char* getaddrhostname(struct sockaddr_storage * addr); int buf_readfile(buffer* buf, const char* filename); int buf_getline(buffer * line, FILE * authfile); @@ -58,6 +61,7 @@ void * m_realloc(void* ptr, size_t size); #define m_free(X) __m_free(X); (X) = NULL; void __m_free(void* ptr); void m_burn(void* data, unsigned int len); +void setnonblocking(int fd); /* Used to force mp_ints to be initialised */ #define DEF_MP_INT(X) mp_int X = {0, 0, 0, NULL} diff --git a/debian/README.runit b/debian/README.runit new file mode 100644 index 0000000000000000000000000000000000000000..4ac2814ac22b3904d36f8e768a1ab38eae7bccb0 --- /dev/null +++ b/debian/README.runit @@ -0,0 +1,46 @@ +Using the dropbear SSH server with runit's services supervision +--------------------------------------------------------------- + +The dropbear SSH server is perfectly suited to be run under runit's +service supervision, and this package already has prepared an adequate +service directory. Follow these steps to enable the dropbear service +using the runit package. + +If not yet installed on your system, install the runit package, and make +sure its service supervision is enabled (it's by default) + + # apt-get install runit + +Make sure the dropbear service normally handled through the sysv init +script is stopped + + # /etc/init.d/dropbear stop + +Create the system user ``dropbearlog'' which will run the logger service, +and own the logs + + # adduser --system --home /var/log/dropbear --no-create-home dropbearlog + +Create the log directory and make the newly created system user the owner +of this directory + + # mkdir -p /var/log/dropbear && chown dropbearlog /var/log/dropbear + +Optionally adjust the configuration of the dropbear service by editing the +run script + + # vi /etc/dropbear/run + +Finally enable the service by linking dropbear's service directory to +/var/service/. The service will be started within five seconds, and +automatically at boot time. The sysv init script is disabled; see the +runsvctrl(8) program for information on how to control services handled by +runit. See the svlogd(8) program on how to configure the log service. + + # ln -s /etc/dropbear /var/service/ + +Optionally check the status of the service a few seconds later + + # runsvstat -l /var/service/dropbear + + -- Gerrit Pape <pape@smarden.org>, Sun, 16 May 2004 15:52:34 +0000 diff --git a/debian/changelog b/debian/changelog index cb7253fb42d17ec88aa2cb59e1f6d79d68993209..c728227262d289cfb6ac207a9bf59ecf985ae85d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,74 @@ +dropbear (0.44test3-1) unstable; urgency=medium + + * New upstream beta, various useful fixes. + + -- Matt Johnston <matt@ucc.asn.au> Fri, 27 August 2004 22:20:00 +0800 + +dropbear (0.44test2-1) unstable; urgency=low + + * New upstream beta, various minor fixes. + + -- Matt Johnston <matt@ucc.asn.au> Tues, 17 August 2004 19:00:00 +0800 + +dropbear (0.44test1-1) unstable; urgency=low + + * Upstream beta 0.44test1 + * Huge changes to allow client functionality + + -- Matt Johnston <matt@ucc.asn.au> Sat, 14 August 2004 23:00:00 +0800 + +dropbear (0.43-1) unstable; urgency=high + + * New upstream release 0.43 + * SECURITY: Don't attempt to free uninitialised buffers in DSS verification + code + * Handle portforwarding to servers which don't send any initial data + (Closes: #258426) + + -- Matt Johnston <matt@ucc.asn.au> Fri, 16 July 2004 17:44:54 +0800 + +dropbear (0.42-1) unstable; urgency=low + + * New upstream release 0.42 + + -- Matt Johnston <matt@ucc.asn.au> Wed, 16 June 2004 12:44:54 +0800 + +dropbear (0.41-3) unstable; urgency=low + + * 1st upload to the Debian archive (closes: #216553). + * debian/diff/cvs-20040520.diff: new; stable cvs snapshot. + * debian/rules: new target patch: apply diffs in debian/diff/, reverse + apply in target clean; install man pages. + * debian/control: Priority: optional. + + -- Gerrit Pape <pape@smarden.org> Sun, 23 May 2004 08:32:37 +0000 + +dropbear (0.41-2) unstable; urgency=low + + * new maintainer. + * debian/control: no longer Build-Depends: debhelper; Build-Depends: + libz-dev; Standards-Version: 3.6.1.0; Suggests: runit; update + descriptions. + * debian/rules: stop using debhelper, use implicit rules; cleanup; + install dropbearconvert into /usr/lib/dropbear/. + * debian/impicit: new; implicit rules. + * debian/copyright.in: adapt. + * debian/dropbear.init: minor adaptions; test for dropbear service + directory. + * debian/README.runit: new; how to use dropbear with runit. + * debian/README.Debian, debian/docs: rename to debian/dropbear.*. + * debian/dropbear.docs: add debian/README.runit + * debian/conffiles: rename to debian/dropbear.conffiles; add init + script, and run scripts. + * debian/postinst: rename to debian/dropbear.postinst; adapt; use + invloke-rc.d dropbear start. + * debian/dropbear.prerm: new; invoke-rc.d dropbear stop. + * debian/postrm: rename to debian/dropbear.postrm; adapt; clean up + service directories. + * debian/compat, debian/dirs, dropbear.default: remove; obsolete. + + -- Gerrit Pape <pape@smarden.org> Sun, 16 May 2004 16:50:55 +0000 + dropbear (0.41-1) unstable; urgency=low * Updated to 0.41 release. diff --git a/debian/compat b/debian/compat deleted file mode 100644 index b8626c4cff2849624fb67f87cd0ad72b163671ad..0000000000000000000000000000000000000000 --- a/debian/compat +++ /dev/null @@ -1 +0,0 @@ -4 diff --git a/debian/conffiles b/debian/conffiles deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/debian/control b/debian/control index e528454d5793a258e74c27568a39f05e038f948a..33c717c92f07cddefbcc23f0eed5428a44af6f55 100644 --- a/debian/control +++ b/debian/control @@ -1,14 +1,20 @@ Source: dropbear Section: net -Priority: standard -Maintainer: Grahame Bowland <grahame@angrygoats.net> -Build-Depends: debhelper (>> 4.0.0), zlib1g-dev -Standards-Version: 3.5.8 +Priority: optional +Maintainer: Gerrit Pape <pape@smarden.org> +Build-Depends: libz-dev +Standards-Version: 3.6.1.0 Package: dropbear Architecture: any -Depends: ${shlibs:Depends} ${misc:Depends} -Suggests: ssh -Description: a minimal SSH2 server - A small secure shell version 2 server. - +Depends: ${shlibs:Depends} +Suggests: ssh, runit +Description: lightweight SSH2 server + dropbear is a SSH 2 server designed to be small enough to be used in small + memory environments, while still being functional and secure enough for + general use. + . + It implements most required features of the SSH 2 protocol, and other + features such as X11 and authentication agent forwarding. + . + See http://matt.ucc.asn.au/dropbear/dropbear.html diff --git a/debian/copyright.in b/debian/copyright.in index 015d9ab7300a5c35bf4fd7eb3eed8a1c28a41a1f..79526d3faa96ccf732942780aec44fe3fb08e409 100644 --- a/debian/copyright.in +++ b/debian/copyright.in @@ -1,9 +1,11 @@ This package was debianized by Grahame Bowland <grahame.angrygoats.net> on -Tue, 17 Jun 2003 15:04:47 +0800. +Tue, 17 Jun 2003 15:04:47 +0800, maintained temporarily by Matt Johnston +<matt@ucc.asn.au>, and was adopted by Gerrit Pape <pape@smarden.org> on +Sun, 16 May 2004 14:38:33 +0000. It was downloaded from http://matt.ucc.asn.au/dropbear/ -Upstream Author(s): Matt Johnston <matt@ucc.asn.au> +Upstream Author: Matt Johnston <matt@ucc.asn.au> Copyright: diff --git a/debian/dirs b/debian/dirs deleted file mode 100644 index ca882bbb78588982a650ff2685c36fe51ae99ae3..0000000000000000000000000000000000000000 --- a/debian/dirs +++ /dev/null @@ -1,2 +0,0 @@ -usr/bin -usr/sbin diff --git a/debian/docs b/debian/docs deleted file mode 100644 index 724e0844955307c264b28a9319b65da3e99bb2c5..0000000000000000000000000000000000000000 --- a/debian/docs +++ /dev/null @@ -1,2 +0,0 @@ -README -TODO diff --git a/debian/dropbear.README.Debian b/debian/dropbear.README.Debian new file mode 100644 index 0000000000000000000000000000000000000000..8cdac38218e5bad6f11e242716d17e307c9709c4 --- /dev/null +++ b/debian/dropbear.README.Debian @@ -0,0 +1,41 @@ +Dropbear for Debian +------------------- + +This package will attempt to listen on port 22. If the OpenSSH +package ("ssh") is installed, the file /etc/default/dropbear +will be set up so that the server does not start by default. + +You can run Dropbear concurrently with OpenSSH 'sshd' by +modifying /etc/default/dropbear so that "NO_START" is set to +"0" and changing the port number that Dropbear runs on. Follow +the instructions in the file. + +This package suggests you install the "ssh" package. This package +provides the "ssh" client program, as well as the "/usr/bin/scp" +binary you will need to be able to retrieve files from a server +running Dropbear via SCP. + +Replacing OpenSSH "sshd" with Dropbear +-------------------------------------- + +You will still want to have the "ssh" package installed, as it +provides the "ssh" and "scp" binaries. When you install this +package, it checks for existing OpenSSH host keys and if found, +converts them to the Dropbear format. + +If this appears to have worked, you should be able to change over +by following these steps: + +1. Stop the OpenSSH server + % /etc/init.d/ssh stop +2. Prevent the OpenSSH server from starting in the future + % touch /etc/ssh/sshd_not_to_be_run +3. Modify the Dropbear defaults file, set NO_START to 0 and + ensure DROPBEAR_PORT is set to 22. + % editor /etc/default/dropbear +4. Restart the Dropbear server. + % /etc/init.d/dropbear restart + +See the Dropbear homepage for more information: + http://matt.ucc.asn.au/dropbear/dropbear.html + diff --git a/debian/dropbear.conffiles b/debian/dropbear.conffiles new file mode 100644 index 0000000000000000000000000000000000000000..6919006992724e15c132c79d2a4eb0e0f884a2cc --- /dev/null +++ b/debian/dropbear.conffiles @@ -0,0 +1,3 @@ +/etc/init.d/dropbear +/etc/dropbear/run +/etc/dropbear/log/run diff --git a/debian/dropbear.docs b/debian/dropbear.docs new file mode 100644 index 0000000000000000000000000000000000000000..599d48c4d3a171ab95bff28ada1beee15cca68dd --- /dev/null +++ b/debian/dropbear.docs @@ -0,0 +1,3 @@ +README +TODO +debian/README.runit diff --git a/debian/dropbear.init b/debian/dropbear.init index 25eda9f9652abaebc8943031031f21ad046f5d65..d9578db7596858e3ef41cc4a10d7961658ec875a 100644 --- a/debian/dropbear.init +++ b/debian/dropbear.init @@ -1,15 +1,4 @@ -#! /bin/sh -# -# skeleton example file to build /etc/init.d/ scripts. -# This file should be used to construct scripts for /etc/init.d. -# -# Written by Miquel van Smoorenburg <miquels@cistron.nl>. -# Modified for Debian -# by Ian Murdock <imurdock@gnu.ai.mit.edu>. -# -# Version: @(#)skeleton 1.9 26-Feb-2001 miquels@cistron.nl -# - +#!/bin/sh # # Do not configure this file. Edit /etc/default/dropbear instead! # @@ -22,54 +11,45 @@ DESC="Dropbear SSH server" DROPBEAR_PORT=22 DROPBEAR_EXTRA_ARGS= NO_START=0 -set -e -test -f /etc/default/dropbear && . /etc/default/dropbear - -if [ -n "$DROPBEAR_BANNER" ]; then - DROPBEAR_EXTRA_ARGS="$DROPBEAR_EXTRA_ARGS -b $DROPBEAR_BANNER" -fi -if [ -z "$DROPBEAR_RSAKEY" ]; then - DROPBEAR_RSAKEY="/etc/dropbear/dropbear_rsa_host_key" -fi - -if [ -z "$DROPBEAR_DSSKEY" ]; then - DROPBEAR_DSSKEY="/etc/dropbear/dropbear_dss_host_key" -fi +set -e -test "$NO_START" != "0" && exit 0 +test ! -r /etc/default/dropbear || . /etc/default/dropbear +test "$NO_START" = "0" || exit 0 +test -x "$DAEMON" || exit 0 +test ! -h /var/service/dropbear || exit 0 -test -x $DAEMON || exit 0 +test -z "$DROPBEAR_BANNER" || \ + DROPBEAR_EXTRA_ARGS="$DROPBEAR_EXTRA_ARGS -b $DROPBEAR_BANNER" +test -n "$DROPBEAR_RSAKEY" || \ + DROPBEAR_RSAKEY="/etc/dropbear/dropbear_rsa_host_key" +test -n "$DROPBEAR_DSSKEY" || \ + DROPBEAR_DSSKEY="/etc/dropbear/dropbear_dss_host_key" case "$1" in start) echo -n "Starting $DESC: " - start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \ - --exec $DAEMON -- -d $DROPBEAR_DSSKEY -r $DROPBEAR_RSAKEY -p $DROPBEAR_PORT $DROPBEAR_EXTRA_ARGS + start-stop-daemon --start --quiet --pidfile /var/run/"$NAME".pid \ + --exec "$DAEMON" -- -d "$DROPBEAR_DSSKEY" -r "$DROPBEAR_RSAKEY" \ + -p "$DROPBEAR_PORT" $DROPBEAR_EXTRA_ARGS echo "$NAME." ;; stop) echo -n "Stopping $DESC: " - start-stop-daemon --stop --quiet --oknodo --pidfile /var/run/$NAME.pid + start-stop-daemon --stop --quiet --oknodo --pidfile /var/run/"$NAME".pid echo "$NAME." ;; restart|force-reload) - # - # If the "reload" option is implemented, move the "force-reload" - # option to the "reload" entry above. If not, "force-reload" is - # just the same as "restart". - # echo -n "Restarting $DESC: " - start-stop-daemon --stop --quiet --oknodo --pidfile \ - /var/run/$NAME.pid + start-stop-daemon --stop --quiet --oknodo --pidfile /var/run/"$NAME".pid sleep 1 - start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \ - --exec $DAEMON -- -d $DROPBEAR_DSSKEY -r $DROPBEAR_RSAKEY -p $DROPBEAR_PORT $DROPBEAR_EXTRA_ARGS + start-stop-daemon --start --quiet --pidfile /var/run/"$NAME".pid \ + --exec "$DAEMON" -- -d "$DROPBEAR_DSSKEY" -r "$DROPBEAR_RSAKEY" \ + -p "$DROPBEAR_PORT" $DROPBEAR_EXTRA_ARGS echo "$NAME." ;; *) N=/etc/init.d/$NAME - # echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2 echo "Usage: $N {start|stop|restart|force-reload}" >&2 exit 1 ;; diff --git a/debian/dropbear.postinst b/debian/dropbear.postinst new file mode 100644 index 0000000000000000000000000000000000000000..749ffd162a267b6a9e1ef1a6babce7e913494b91 --- /dev/null +++ b/debian/dropbear.postinst @@ -0,0 +1,68 @@ +#!/bin/sh +set -e + +test "$1" = 'configure' || exit 0 +test -n "$2" || chown log /etc/dropbear/log/main || true + +if test ! -e /etc/dropbear/dropbear_rsa_host_key; then + if test -f /etc/ssh/ssh_host_rsa_key; then + echo "Converting existing OpenSSH RSA host key to Dropbear format." + /usr/lib/dropbear/dropbearconvert openssh dropbear \ + /etc/ssh/ssh_host_rsa_key /etc/dropbear/dropbear_rsa_host_key + else + echo "Generating Dropbear RSA key. Please wait." + dropbearkey -t rsa -f /etc/dropbear/dropbear_rsa_host_key + fi +fi +if test ! -e /etc/dropbear/dropbear_dss_host_key; then + if test -f /etc/ssh/ssh_host_dsa_key; then + echo "Converting existing OpenSSH RSA host key to Dropbear format." + /usr/lib/dropbear/dropbearconvert openssh dropbear \ + /etc/ssh/ssh_host_dsa_key /etc/dropbear/dropbear_dss_host_key + else + echo "Generating Dropbear DSS key. Please wait." + dropbearkey -t dss -f /etc/dropbear/dropbear_dss_host_key + fi +fi +if test ! -s /etc/default/dropbear; then + # check whether OpenSSH seems to be installed. + if test -x /usr/sbin/sshd; then + cat <<EOT +OpenSSH appears to be installed. Setting /etc/default/dropbear so that +Dropbear will not start by default. Edit this file to change this behaviour. + +EOT + cat >>/etc/default/dropbear <<EOT +# disabled because OpenSSH is installed +# change to NO_START=0 to enable Dropbear +NO_START=1 + +EOT + fi + cat >>/etc/default/dropbear <<EOT +# the TCP port that Dropbear listens on +DROPBEAR_PORT=22 + +# any additional arguments for Dropbear +DROPBEAR_EXTRA_ARGS= + +# specify an optional banner file containing a message to be +# sent to clients before they connect, such as "/etc/issue.net" +DROPBEAR_BANNER="" + +# RSA hostkey file (default: /etc/dropbear/dropbear_rsa_host_key) +#DROPBEAR_RSAKEY="/etc/dropbear/dropbear_rsa_host_key" + +# DSS hostkey file (default: /etc/dropbear/dropbear_dss_host_key) +#DROPBEAR_DSSKEY="/etc/dropbear/dropbear_dss_host_key" +EOT +fi + +if test -x /etc/init.d/dropbear; then + update-rc.d dropbear defaults >/dev/null + if test -x /usr/sbin/invoke-rc.d; then + invoke-rc.d dropbear start + else + /etc/init.d/dropbear start + fi +fi diff --git a/debian/dropbear.postrm b/debian/dropbear.postrm new file mode 100644 index 0000000000000000000000000000000000000000..d09dab0712cc99e4d372c3908f3cdde4df7107bb --- /dev/null +++ b/debian/dropbear.postrm @@ -0,0 +1,12 @@ +#! /bin/sh +set -e + +test "$1" = 'purge' || exit 0 +if test -e /etc/dropbear; then + rm -f /etc/dropbear/dropbear_rsa_host_key + rm -f /etc/dropbear/dropbear_dss_host_key + rmdir --ignore-fail-on-non-empty /etc/dropbear +fi +update-rc.d dropbear remove >/dev/null +rm -f /etc/default/dropbear +rm -rf /etc/dropbear/supervise /etc/dropbear/log/supervise diff --git a/debian/dropbear.prerm b/debian/dropbear.prerm new file mode 100644 index 0000000000000000000000000000000000000000..89bb5b63e49e72269a6052e57671867446d8979a --- /dev/null +++ b/debian/dropbear.prerm @@ -0,0 +1,11 @@ +#!/bin/sh +set -u + +test "$1" = 'remove' || test "$1" = 'deconfigure' || exit 0 +if test -x /etc/init.d/dropbear; then + if test -x /usr/sbin/invoke-rc.d; then + invoke-rc.d dropbear stop + else + /etc/init.d/dropbear stop + fi +fi diff --git a/debian/implicit b/debian/implicit new file mode 100644 index 0000000000000000000000000000000000000000..d28b62985ed16eea8d6cd5298406291c0fd72b0c --- /dev/null +++ b/debian/implicit @@ -0,0 +1,79 @@ +# $Id: implicit,v 1.1 2004/06/16 05:08:32 matt Exp $ + +.PHONY: deb-checkdir deb-checkuid + +deb-checkdir: + @test -e debian/control || sh -cx '! : wrong directory' +deb-checkuid: + @test "`id -u`" -eq 0 || sh -cx '! : need root privileges' + +%.deb: %.deb-docs %.deb-DEBIAN + @rm -f $*.deb $*.deb-checkdir $*.deb-docs $*.deb-docs-base \ + $*.deb-docs-docs $*.deb-docs-examples $*.deb-DEBIAN \ + $*.deb-DEBIAN-dir $*.deb-DEBIAN-scripts $*.deb-DEBIAN-md5sums + +%.deb-checkdir: + @test -d debian/$* || sh -cx '! : directory debian/$* missing' + @test "`id -u`" -eq 0 || sh -cx '! : need root privileges' + +%.deb-docs-base: + : implicit + @rm -f debian/$*/usr/share/doc/$*/* || : + @install -d -m0755 debian/$*/usr/share/doc/$* + : debian/$*/usr/share/doc/$*/ + @sh -cx 'install -m0644 debian/copyright debian/$*/usr/share/doc/$*/' + @sh -cx 'install -m0644 debian/changelog \ + debian/$*/usr/share/doc/$*/changelog.Debian' + @test ! -r changelog || \ + sh -cx 'install -m0644 changelog debian/$*/usr/share/doc/$*/' + @test -r debian/$*/usr/share/doc/$*/changelog || \ + sh -cx 'mv debian/$*/usr/share/doc/$*/changelog.Debian \ + debian/$*/usr/share/doc/$*/changelog' + @gzip -9 debian/$*/usr/share/doc/$*/changelog* +%.deb-docs-docs: + @for i in `cat debian/$*.docs 2>/dev/null || :`; do \ + sh -cx "install -m0644 $$i debian/$*/usr/share/doc/$*/" || exit 1; \ + done + @test ! -r debian/$*.README.Debian || \ + sh -cx 'install -m0644 debian/$*.README.Debian \ + debian/$*/usr/share/doc/$*/README.Debian' + @if test -r debian/$*.NEWS.Debian; then \ + sh -cx 'install -m0644 debian/$*.NEWS.Debian \ + debian/$*/usr/share/doc/$*/NEWS.Debian && \ + gzip -9 debian/$*/usr/share/doc/$*/NEWS.Debian'; \ + fi +%.deb-docs-examples: + @rm -rf debian/$*/usr/share/doc/$*/examples + : debian/$*/usr/share/doc/$*/examples/ + @test ! -r debian/$*.examples || \ + install -d -m0755 debian/$*/usr/share/doc/$*/examples + @for i in `cat debian/$*.examples 2>/dev/null || :`; do \ + sh -cx "install -m0644 $$i debian/$*/usr/share/doc/$*/examples/" \ + || exit 1; \ + done +%.deb-docs: %.deb-checkdir %.deb-docs-base %.deb-docs-docs %.deb-docs-examples + : debian/$*/usr/share/doc/$*/ ok + +%.deb-DEBIAN-base: + @rm -rf debian/$*/DEBIAN + : debian/$*/DEBIAN/ + @install -d -m0755 debian/$*/DEBIAN + @for i in conffiles shlibs; do \ + test ! -r debian/$*.$$i || \ + sh -cx "install -m0644 debian/$*.$$i debian/$*/DEBIAN/$$i" \ + || exit 1; \ + done +%.deb-DEBIAN-scripts: + @for i in preinst prerm postinst postrm; do \ + test ! -r debian/$*.$$i || \ + sh -cx "install -m0755 debian/$*.$$i debian/$*/DEBIAN/$$i" \ + || exit 1; \ + done +%.deb-DEBIAN-md5sums: + : debian/$*/DEBIAN/md5sums + @rm -f debian/$*/DEBIAN/md5sums + @cd debian/$* && find * -path 'DEBIAN' -prune -o \ + -type f -exec md5sum {} >>DEBIAN/md5sums \; +%.deb-DEBIAN: %.deb-checkdir %.deb-DEBIAN-base %.deb-DEBIAN-scripts \ + %.deb-DEBIAN-md5sums + : debian/$*/DEBIAN/ ok diff --git a/debian/postinst b/debian/postinst deleted file mode 100644 index 010618dc578810bda95e25169a43f738c6ee4a40..0000000000000000000000000000000000000000 --- a/debian/postinst +++ /dev/null @@ -1,90 +0,0 @@ -#! /bin/sh -# postinst script for #PACKAGE# -# -# see: dh_installdeb(1) - -set -e - -# summary of how this script can be called: -# * <postinst> `configure' <most-recently-configured-version> -# * <old-postinst> `abort-upgrade' <new version> -# * <conflictor's-postinst> `abort-remove' `in-favour' <package> -# <new-version> -# * <deconfigured's-postinst> `abort-deconfigure' `in-favour' -# <failed-install-package> <version> `removing' -# <conflicting-package> <version> -# for details, see http://www.debian.org/doc/debian-policy/ or -# the debian-policy package -# -# quoting from the policy: -# Any necessary prompting should almost always be confined to the -# post-installation script, and should be protected with a conditional -# so that unnecessary prompting doesn't happen if a package's -# installation fails and the `postinst' is called with `abort-upgrade', -# `abort-remove' or `abort-deconfigure'. - -case "$1" in - configure) - if [ ! -e /etc/dropbear/dropbear_rsa_host_key ]; then - if [ -f /etc/ssh/ssh_host_rsa_key ]; then - echo "Converting existing OpenSSH RSA host key to Dropbear format." - /usr/bin/dropbearconvert openssh dropbear /etc/ssh/ssh_host_rsa_key /etc/dropbear/dropbear_rsa_host_key - else - echo "Generating Dropbear RSA key. Please wait." - /usr/bin/dropbearkey -t rsa -f /etc/dropbear/dropbear_rsa_host_key - fi - fi - if [ ! -e /etc/dropbear/dropbear_dss_host_key ]; then - if [ -f /etc/ssh/ssh_host_dsa_key ]; then - echo "Converting existing OpenSSH RSA host key to Dropbear format." - /usr/bin/dropbearconvert openssh dropbear /etc/ssh/ssh_host_dsa_key /etc/dropbear/dropbear_dss_host_key - else - echo "Generating Dropbear DSS key. Please wait." - /usr/bin/dropbearkey -t dss -f /etc/dropbear/dropbear_dss_host_key - fi - fi - if [ ! -s /etc/default/dropbear ]; then - # check whether OpenSSH seems to be installed. - if dpkg -l ssh >/dev/null 2>&1; then - echo "OpenSSH appears to be installed. Setting /etc/default/dropbear" - echo "so that Dropbear will not start by default. Edit this file to change" - echo "this behaviour." - echo "# disabled because OpenSSH is installed, change to NO_START=0 to enable Dropbear" > /etc/default/dropbear - echo "NO_START=1" >> /etc/default/dropbear - fi - echo "# the TCP port that Dropbear listens on" >> /etc/default/dropbear - echo "DROPBEAR_PORT=22" >> /etc/default/dropbear - echo "# any additional arguments for Dropbear" >> /etc/default/dropbear - echo "DROPBEAR_EXTRA_ARGS=" >> /etc/default/dropbear - echo "# specify an optional banner file containing a message to be" >> /etc/default/dropbear - echo "# sent to clients before they connect, such as \"/etc/issue.net\"" >> /etc/default/dropbear - echo "DROPBEAR_BANNER=\"\"" >> /etc/default/dropbear - echo "# RSA hostkey file (default: /etc/dropbear/dropbear_rsa_host_key" >> /etc/default/dropbear - echo "#DROPBEAR_RSAKEY=\"/etc/dropbear/dropbear_rsa_host_key\"" >> /etc/default/dropbear - echo "# DSS hostkey file (default: /etc/dropbear/dropbear_dss_host_key" >> /etc/default/dropbear - echo "#DROPBEAR_DSSKEY=\"/etc/dropbear/dropbear_dss_host_key\"" >> /etc/default/dropbear - fi - if [ -e /etc/init.d/dropbear ]; then - update-rc.d dropbear defaults >/dev/null - /etc/init.d/dropbear restart - fi - ;; - - abort-upgrade|abort-remove|abort-deconfigure) - - ;; - - *) - echo "postinst called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - - - -exit 0 - - diff --git a/debian/postrm b/debian/postrm deleted file mode 100644 index da11dc442991e49a4214694cda932ad06b4791ed..0000000000000000000000000000000000000000 --- a/debian/postrm +++ /dev/null @@ -1,45 +0,0 @@ -#! /bin/sh -# postrm script for #PACKAGE# -# -# see: dh_installdeb(1) - -set -e - -# summary of how this script can be called: -# * <postrm> `remove' -# * <postrm> `purge' -# * <old-postrm> `upgrade' <new-version> -# * <new-postrm> `failed-upgrade' <old-version> -# * <new-postrm> `abort-install' -# * <new-postrm> `abort-install' <old-version> -# * <new-postrm> `abort-upgrade' <old-version> -# * <disappearer's-postrm> `disappear' <r>overwrit>r> <new-version> -# for details, see http://www.debian.org/doc/debian-policy/ or -# the debian-policy package - - -case "$1" in - purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) - if [ "$1" = "purge" ] - then - if [ -e /etc/dropbear ]; then - rm -f /etc/dropbear/dropbear_rsa_host_key - rm -f /etc/dropbear/dropbear_dss_host_key - rmdir --ignore-fail-on-non-empty /etc/dropbear - fi - update-rc.d dropbear remove >/dev/null - fi - ;; - - *) - echo "postrm called with unknown argument \`$1'" >&2 - exit 1 - -esac - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - -#DEBHELPER# - -exit 0 diff --git a/debian/rules b/debian/rules index 4d7309376e3d660f4152c9aafcc4e7982e5ee83e..30b3a0a850ad9f50c5c175fbf61d24a40b33b8cf 100644 --- a/debian/rules +++ b/debian/rules @@ -1,134 +1,97 @@ #!/usr/bin/make -f -# Sample debian/rules that uses debhelper. -# GNU copyright 1997 to 1999 by Joey Hess. -# -# Modified to make a template file for a multi-binary package with separated -# build-arch and build-indep targets by Bill Allombert 2001 -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 +#export DH_OPTIONS +DEB_HOST_GNU_TYPE ?=$(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) +DEB_BUILD_GNU_TYPE ?=$(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) -# This has to be exported to make some magic below work. -export DH_OPTIONS - -# These are used for cross-compiling and for saving the configure script -# from having to guess our platform (since we know it already) -DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) -DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) - - -CFLAGS = -Wall -g +STRIP =strip +ifneq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) + STRIP =: nostrip +endif +CFLAGS =-Wall -g ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) - CFLAGS += -O0 + CFLAGS +=-O0 else - CFLAGS += -O2 -endif -ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) - INSTALL_PROGRAM += -s + CFLAGS +=-O2 endif -config.status: configure - dh_testdir - # Add here commands to configure the package. - CFLAGS='-DSFTPSERVER_PATH="\"/usr/lib/sftp-server\""' ./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --prefix=/usr --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info - - -#Architecture -build: build-arch #build-indep - -build-arch: build-arch-stamp -build-arch-stamp: config.status - - # Add here commands to compile the arch part of the package. - $(MAKE) CC=gcc LD=gcc - -build-indep: build-indep-stamp -build-indep-stamp: config.status - - # Add here commands to compile the indep part of the package. - #$(MAKE) doc - -clean: - dh_testdir - dh_testroot - rm -f build-arch-stamp build-indep-stamp config-stamp - - # Add here commands to clean up after the build process. - -$(MAKE) clean -ifneq "$(wildcard /usr/share/misc/config.sub)" "" - cp -f /usr/share/misc/config.sub config.sub -endif -ifneq "$(wildcard /usr/share/misc/config.guess)" "" - cp -f /usr/share/misc/config.guess config.guess +CC =gcc +ifneq (,$(findstring diet,$(DEB_BUILD_OPTIONS))) + CC =diet -v -Os gcc endif - - dh_clean - -install: install-indep install-arch -install-indep: - dh_testdir - dh_testroot - dh_clean -k -i - dh_installdirs -i - - # Add here commands to install the indep part of the package into - # debian/<package>-doc. - #INSTALLDOC# - - dh_install -i - -install-arch: - dh_testdir - dh_testroot - dh_clean -k -a - dh_installdirs -a - dh_installdirs /etc/dropbear - - # Add here commands to install the arch part of the package into - # debian/tmp. - $(MAKE) install prefix=$(CURDIR)/debian/dropbear/usr - - dh_install -a -# Must not depend on anything. This is to be called by -# binary-arch/binary-multi -# in another 'make' thread. -binary-common: - cat $(CURDIR)/debian/copyright.in $(CURDIR)/LICENSE > $(CURDIR)/debian/copyright - dh_testdir - dh_testroot - dh_installchangelogs CHANGES - dh_installdocs - dh_installexamples -# dh_installmenu -# dh_installdebconf -# dh_installlogrotate -# dh_installemacsen -# dh_installpam -# dh_installmime - dh_installinit -# dh_installcron -# dh_installinfo - dh_installman - dh_link - dh_strip - dh_compress - dh_fixperms -# dh_perl -# dh_python - dh_makeshlibs - dh_installdeb - dh_gencontrol - dh_md5sums - dh_builddeb -# Build architecture independant packages using the common target. -binary-indep: build-indep install-indep - $(MAKE) -f debian/rules DH_OPTIONS=-i binary-common - -# Build architecture dependant packages using the common target. -binary-arch: build-arch install-arch - $(MAKE) -f debian/rules DH_OPTIONS=-a binary-common - -binary: binary-arch #binary-indep -.PHONY: build clean binary-indep binary-arch binary install install-indep install-arch +DIR=`pwd`/debian/dropbear + +patch: deb-checkdir patch-stamp +patch-stamp: +# no patches for now +# for i in debian/diff/*.diff; do patch -p0 <$$i || exit 1; done + touch patch-stamp + +config.status: patch-stamp configure + CFLAGS="$(CFLAGS)"' -DSFTPSERVER_PATH="\"/usr/lib/sftp-server\""' \ + ./configure --host="$(DEB_HOST_GNU_TYPE)" \ + --build="$(DEB_BUILD_GNU_TYPE)" --prefix=/usr \ + --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info + +build: deb-checkdir build-stamp +build-stamp: config.status + $(MAKE) CC="$(CC)" LD="$(CC)" + touch build-stamp + +clean: deb-checkdir deb-checkuid + -$(MAKE) distclean +# test ! -e patch-stamp || \ +# for i in debian/diff/*.diff; do patch -p0 -R <$$i; done + rm -f patch-stamp build-stamp config.log config.status + rm -rf "$(DIR)" + rm -f debian/files debian/substvars debian/copyright changelog + +install: deb-checkdir deb-checkuid build-stamp + rm -rf "$(DIR)" + install -d -m0755 "$(DIR)"/etc/dropbear + # programs + install -d -m0755 "$(DIR)"/usr/sbin + install -m0755 dropbear "$(DIR)"/usr/sbin/dropbear + install -d -m0755 "$(DIR)"/usr/bin + install -m0755 dbclient "$(DIR)"/usr/bin/dbclient + install -m0755 dropbearkey "$(DIR)"/usr/bin/dropbearkey + install -d -m0755 "$(DIR)"/usr/lib/dropbear + install -m0755 dropbearconvert \ + "$(DIR)"/usr/lib/dropbear/dropbearconvert + $(STRIP) -R .comment -R .note "$(DIR)"/usr/sbin/* \ + "$(DIR)"/usr/bin/* "$(DIR)"/usr/lib/dropbear/* + # init and run scripts + install -d -m0755 "$(DIR)"/etc/init.d + install -m0755 debian/dropbear.init "$(DIR)"/etc/init.d/dropbear + install -m0755 debian/service/run "$(DIR)"/etc/dropbear/run + install -d -m0755 "$(DIR)"/etc/dropbear/log + install -m0755 debian/service/log "$(DIR)"/etc/dropbear/log/run + ln -s /var/log/dropbear "$(DIR)"/etc/dropbear/log/main + ln -s /var/run/dropbear "$(DIR)"/etc/dropbear/supervise + ln -s /var/run/dropbear.log "$(DIR)"/etc/dropbear/log/supervise + # man pages + install -d -m0755 "$(DIR)"/usr/share/man/man8 + for i in dropbear.8 dropbearkey.8; do \ + install -m644 $$i "$(DIR)"/usr/share/man/man8/ || exit 1; \ + done + gzip -9 "$(DIR)"/usr/share/man/man8/*.8 + # copyright, changelog + cat debian/copyright.in LICENSE >debian/copyright + ln -s CHANGES changelog + +binary-indep: + +binary-arch: install dropbear.deb + test "$(CC)" != 'gcc' || \ + dpkg-shlibdeps "$(DIR)"/usr/sbin/* "$(DIR)"/usr/bin/* \ + "$(DIR)"/usr/lib/dropbear/* + dpkg-gencontrol -isp -pdropbear -P"$(DIR)" + dpkg -b "$(DIR)" .. + +binary: binary-arch binary-indep + +.PHONY: patch build clean install binary-indep binary-arch binary + +include debian/implicit diff --git a/debian/service/log b/debian/service/log new file mode 100644 index 0000000000000000000000000000000000000000..2ffb13d455e9cbb32cce847fa91063b5d3145cab --- /dev/null +++ b/debian/service/log @@ -0,0 +1,2 @@ +#!/bin/sh +exec chpst -udropbearlog svlogd -tt ./main diff --git a/debian/service/run b/debian/service/run new file mode 100644 index 0000000000000000000000000000000000000000..f208085bda88be6f7b86eba9a3e9a11bfcec194f --- /dev/null +++ b/debian/service/run @@ -0,0 +1,3 @@ +#!/bin/sh +exec 2>&1 +exec dropbear -d ./dropbear_dss_host_key -r ./dropbear_rsa_host_key -F -E -p 22 diff --git a/debug.h b/debug.h index d619a58e5729b811582616337325d2b9561a13e9..37f51ac656195760b38d81eebf259f96e0cd484d 100644 --- a/debug.h +++ b/debug.h @@ -33,10 +33,13 @@ * etc. Don't use this normally, it might cause problems */ /* #define DEBUG_VALGRIND */ -/* Define this to print trace statements - very verbose */ -/* Caution: Don't use this in an unfriendly environment (ie unfirewalled), - * since the printing does not sanitise strings etc */ -#define DEBUG_TRACE +/* Define this to compile in trace debugging printf()s. + * You'll need to run programs with "-v" to turn this on. + * + * 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 */ /* All functions writing to the cleartext payload buffer call * CHECKCLEARTOWRITE() before writing. This is only really useful if you're @@ -47,6 +50,7 @@ /* Define this, compile with -pg and set GMON_OUT_PREFIX=gmon to get gmon * output when Dropbear forks. This will allow it gprof to be used. * It's useful to run dropbear -F, so you don't fork as much */ +/* (This is Linux specific) */ /*#define DEBUG_FORKGPROF*/ /* A couple of flags, not usually useful, and mightn't do anything */ diff --git a/dropbearconvert.c b/dropbearconvert.c index 3ceccff2abf8aeaac4492f543c657cac3425adab..9e16fe72f63ec45404d4c7f70e8b65c04137478f 100644 --- a/dropbearconvert.c +++ b/dropbearconvert.c @@ -47,10 +47,8 @@ static void printhelp(char * progname) { "dropbear\n" "\n" "Example:\n" - "dropbearconvert openssh dropbear /etc/ssh/ssh_host_rsa_key /etc/dropbear_rsa_host_key\n" - "\n" - "The inputfile and output file can be '-' to specify\n" - "standard input or standard output.\n", progname); + "dropbearconvert openssh dropbear /etc/ssh/ssh_host_rsa_key /etc/dropbear_rsa_host_key\n", + progname); } #if defined(DBMULTI_dropbearconvert) || !defined(DROPBEAR_MULTI) @@ -64,6 +62,11 @@ int main(int argc, char ** argv) { const char* infile; const char* outfile; +#ifdef DEBUG_TRACE + /* It's hard for it to get in the way _too_ much */ + debug_trace = 1; +#endif + /* get the commandline options */ if (argc != 5) { fprintf(stderr, "All arguments must be specified\n"); diff --git a/dropbearkey.c b/dropbearkey.c index eac08231662d368211d87e0b7840446dae7d8328..364b76523391d08a0781dd05ee5305717b2aa645 100644 --- a/dropbearkey.c +++ b/dropbearkey.c @@ -45,7 +45,6 @@ * */ #include "includes.h" -#include "runopts.h" #include "signkey.h" #include "buffer.h" #include "dbutil.h" @@ -55,29 +54,32 @@ static void printhelp(char * progname); -#define BUF_SIZE 2000 - #define RSA_SIZE (1024/8) /* 1024 bit */ #define DSS_SIZE (1024/8) /* 1024 bit */ static void buf_writefile(buffer * buf, const char * filename); +static void printpubkey(sign_key * key, int keytype); +static void justprintpub(const char* filename); /* Print a help message */ static void printhelp(char * progname) { fprintf(stderr, "Usage: %s -t <type> -f <filename> [-s bits]\n" "Options are:\n" - "-t type Type of key to generate. One of:\n" + "-t type Type of key to generate. One of:\n" #ifdef DROPBEAR_RSA - " rsa\n" + " rsa\n" #endif #ifdef DROPBEAR_DSS - " dss\n" + " dss\n" +#endif + "-f filename Use filename for the secret key\n" + "-s bits Key size in bits, should be a multiple of 8 (optional)\n" + "-y Just print the publickey and fingerprint for the\n private key in <filename>.\n" +#ifdef DEBUG_TRACE + "-v verbose\n" #endif - "-f filename Use filename for the secret key\n" - "-s bits Key size in bits, should be " - "multiple of 8 (optional)\n", - progname); + ,progname); } #if defined(DBMULTI_dropbearkey) || !defined(DROPBEAR_MULTI) @@ -89,23 +91,24 @@ int main(int argc, char ** argv) { int i; char ** next = 0; - sign_key *key; - buffer *buf; + sign_key *key = NULL; + buffer *buf = NULL; char * filename = NULL; int keytype = -1; char * typetext = NULL; char * sizetext = NULL; unsigned int bits; unsigned int keysize; + int printpub = 0; /* get the commandline options */ for (i = 1; i < argc; i++) { + if (argv[i] == NULL) { + continue; /* Whack */ + } if (next) { *next = argv[i]; - if (*next == NULL) { - fprintf(stderr, "Invalid null argument"); - } - next = 0x00; + next = NULL; continue; } @@ -120,10 +123,18 @@ int main(int argc, char ** argv) { case 's': next = &sizetext; break; + case 'y': + printpub = 1; + break; case 'h': printhelp(argv[0]); exit(EXIT_SUCCESS); break; +#ifdef DEBUG_TRACE + case 'v': + debug_trace = 1; + break; +#endif default: fprintf(stderr, "Unknown argument %s\n", argv[i]); printhelp(argv[0]); @@ -133,17 +144,20 @@ int main(int argc, char ** argv) { } } + if (!filename) { + fprintf(stderr, "Must specify a key filename\n"); + printhelp(argv[0]); + exit(EXIT_FAILURE); + } + + if (printpub) { + justprintpub(filename); + /* Not reached */ + } + /* check/parse args */ if (!typetext) { - fprintf(stderr, "Must specify file type, one of:\n" -#ifdef DROPBEAR_RSA - "rsa\n" -#endif -#ifdef DROPBEAR_DSS - "dss\n" -#endif - "\n" - ); + fprintf(stderr, "Must specify key type\n"); printhelp(argv[0]); exit(EXIT_FAILURE); } @@ -191,11 +205,6 @@ int main(int argc, char ** argv) { } } - if (!filename) { - fprintf(stderr, "Must specify a key filename\n"); - printhelp(argv[0]); - exit(EXIT_FAILURE); - } fprintf(stderr, "Will output %d bit %s secret key to '%s'\n", keysize*8, typetext, filename); @@ -223,7 +232,7 @@ int main(int argc, char ** argv) { exit(EXIT_FAILURE); } - buf = buf_new(BUF_SIZE); + buf = buf_new(MAX_PRIVKEY_SIZE); buf_put_priv_key(buf, key, keytype); buf_setpos(buf, 0); @@ -231,14 +240,88 @@ int main(int argc, char ** argv) { buf_burn(buf); buf_free(buf); - sign_key_free(key); - fprintf(stderr, "Done.\n"); + printpubkey(key, keytype); + + sign_key_free(key); return EXIT_SUCCESS; } #endif +static void justprintpub(const char* filename) { + + buffer *buf = NULL; + sign_key *key = NULL; + int keytype; + int ret; + int err = DROPBEAR_FAILURE; + + buf = buf_new(MAX_PRIVKEY_SIZE); + ret = buf_readfile(buf, filename); + + if (ret != DROPBEAR_SUCCESS) { + fprintf(stderr, "Failed reading '%s'\n", filename); + goto out; + } + + key = new_sign_key(); + keytype = DROPBEAR_SIGNKEY_ANY; + + buf_setpos(buf, 0); + ret = buf_get_priv_key(buf, key, &keytype); + if (ret == DROPBEAR_FAILURE) { + fprintf(stderr, "Bad key in '%s'\n", filename); + goto out; + } + + printpubkey(key, keytype); + + err = DROPBEAR_SUCCESS; + +out: + buf_burn(buf); + buf_free(buf); + buf = NULL; + sign_key_free(key); + key = NULL; + exit(err); +} + +static void printpubkey(sign_key * key, int keytype) { + + buffer * buf = NULL; + unsigned char base64key[MAX_PUBKEY_SIZE*2]; + unsigned long base64len; + int err; + const char * typestring = NULL; + char *fp = NULL; + int len; + + buf = buf_new(MAX_PUBKEY_SIZE); + buf_put_pub_key(buf, key, keytype); + buf_setpos(buf, 4); + + len = buf->len - buf->pos; + + base64len = sizeof(base64key); + err = base64_encode(buf_getptr(buf, len), len, base64key, &base64len); + + if (err != CRYPT_OK) { + fprintf(stderr, "base64 failed"); + } + + typestring = signkey_name_from_type(keytype, &err); + + fp = sign_key_fingerprint(buf_getptr(buf, len), len); + + printf("Public key portion is:\n%s %s\nFingerprint: %s\n", + typestring, base64key, fp); + + m_free(fp); + buf_free(buf); +} + /* Write a buffer to a file specified, failing if the file exists */ static void buf_writefile(buffer * buf, const char * filename) { diff --git a/dss.c b/dss.c index 74b92c70b0e8e166abe30fd5dc2a6e55d9a0ca38..5bd358a4ec77e3c6725dad2444ef3571d1ff2829 100644 --- a/dss.c +++ b/dss.c @@ -164,13 +164,18 @@ int buf_dss_verify(buffer* buf, dss_key *key, const unsigned char* data, unsigned char msghash[SHA1_HASH_SIZE]; hash_state hs; int ret = DROPBEAR_FAILURE; - mp_int val1, val2, val3, val4; + DEF_MP_INT(val1); + DEF_MP_INT(val2); + DEF_MP_INT(val3); + DEF_MP_INT(val4); char * string = NULL; int stringlen; TRACE(("enter buf_dss_verify")); assert(key != NULL); + m_mp_init_multi(&val1, &val2, &val3, &val4, NULL); + /* get blob, check length */ string = buf_getstring(buf, &stringlen); if (stringlen != 2*SHA1_HASH_SIZE) { @@ -182,8 +187,6 @@ int buf_dss_verify(buffer* buf, dss_key *key, const unsigned char* data, sha1_process(&hs, data, len); sha1_done(&hs, msghash); - m_mp_init_multi(&val1, &val2, &val3, &val4, NULL); - /* create the signature - s' and r' are the received signatures in buf */ /* w = (s')-1 mod q */ /* let val1 = s' */ @@ -281,13 +284,16 @@ void buf_put_dss_sign(buffer* buf, dss_key *key, const unsigned char* data, unsigned char privkeyhash[SHA512_HASH_SIZE]; unsigned char *privkeytmp; unsigned char proto_k[SHA512_HASH_SIZE]; - mp_int dss_protok; + DEF_MP_INT(dss_protok); #else unsigned char kbuf[SHA1_HASH_SIZE]; #endif - mp_int dss_k, dss_m; - mp_int dss_temp1, dss_temp2; - mp_int dss_r, dss_s; + DEF_MP_INT(dss_k); + DEF_MP_INT(dss_m); + DEF_MP_INT(dss_temp1); + DEF_MP_INT(dss_temp2); + DEF_MP_INT(dss_r); + DEF_MP_INT(dss_s); hash_state hs; TRACE(("enter buf_put_dss_sign")); diff --git a/fake-rfc2553.c b/fake-rfc2553.c new file mode 100644 index 0000000000000000000000000000000000000000..afbea88777d7612caf83b5a742b72f3179f45ec5 --- /dev/null +++ b/fake-rfc2553.c @@ -0,0 +1,227 @@ +/* + * + * Taken from OpenSSH 3.8.1p1 + * + * Copyright (C) 2000-2003 Damien Miller. All rights reserved. + * Copyright (C) 1999 WIDE Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Pseudo-implementation of RFC2553 name / address resolution functions + * + * But these functions are not implemented correctly. The minimum subset + * is implemented for ssh use only. For example, this routine assumes + * that ai_family is AF_INET. Don't use it for another purpose. + */ + +#include "includes.h" + +/* RCSID("$.Id: fake-rfc2553.c,v 1.5 2003/09/22 02:08:23 dtucker Exp $");*/ + +#ifndef HAVE_GETNAMEINFO +int getnameinfo(const struct sockaddr *sa, size_t salen, char *host, + size_t hostlen, char *serv, size_t servlen, int flags) +{ + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + struct hostent *hp; + char tmpserv[16]; + + if (serv != NULL) { + snprintf(tmpserv, sizeof(tmpserv), "%d", ntohs(sin->sin_port)); + if (strlcpy(serv, tmpserv, servlen) >= servlen) + return (EAI_MEMORY); + } + + if (host != NULL) { + if (flags & NI_NUMERICHOST) { + if (strlcpy(host, inet_ntoa(sin->sin_addr), + hostlen) >= hostlen) + return (EAI_MEMORY); + else + return (0); + } else { + hp = gethostbyaddr((char *)&sin->sin_addr, + sizeof(struct in_addr), AF_INET); + if (hp == NULL) + return (EAI_NODATA); + + if (strlcpy(host, hp->h_name, hostlen) >= hostlen) + return (EAI_MEMORY); + else + return (0); + } + } + return (0); +} +#endif /* !HAVE_GETNAMEINFO */ + +#ifndef HAVE_GAI_STRERROR +#ifdef HAVE_CONST_GAI_STRERROR_PROTO +const char * +#else +char * +#endif +gai_strerror(int err) +{ + switch (err) { + case EAI_NODATA: + return ("no address associated with name"); + case EAI_MEMORY: + return ("memory allocation failure."); + case EAI_NONAME: + return ("nodename nor servname provided, or not known"); + default: + return ("unknown/invalid error."); + } +} +#endif /* !HAVE_GAI_STRERROR */ + +#ifndef HAVE_FREEADDRINFO +void +freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *next; + + for(; ai != NULL;) { + next = ai->ai_next; + free(ai); + ai = next; + } +} +#endif /* !HAVE_FREEADDRINFO */ + +#ifndef HAVE_GETADDRINFO +static struct +addrinfo *malloc_ai(int port, u_long addr, const struct addrinfo *hints) +{ + struct addrinfo *ai; + + ai = malloc(sizeof(*ai) + sizeof(struct sockaddr_in)); + if (ai == NULL) + return (NULL); + + memset(ai, '\0', sizeof(*ai) + sizeof(struct sockaddr_in)); + + ai->ai_addr = (struct sockaddr *)(ai + 1); + /* XXX -- ssh doesn't use sa_len */ + ai->ai_addrlen = sizeof(struct sockaddr_in); + ai->ai_addr->sa_family = ai->ai_family = AF_INET; + + ((struct sockaddr_in *)(ai)->ai_addr)->sin_port = port; + ((struct sockaddr_in *)(ai)->ai_addr)->sin_addr.s_addr = addr; + + /* XXX: the following is not generally correct, but does what we want */ + if (hints->ai_socktype) + ai->ai_socktype = hints->ai_socktype; + else + ai->ai_socktype = SOCK_STREAM; + + if (hints->ai_protocol) + ai->ai_protocol = hints->ai_protocol; + + return (ai); +} + +int +getaddrinfo(const char *hostname, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + struct hostent *hp; + struct servent *sp; + struct in_addr in; + int i; + long int port; + u_long addr; + + port = 0; + if (servname != NULL) { + char *cp; + + port = strtol(servname, &cp, 10); + if (port > 0 && port <= 65535 && *cp == '\0') + port = htons(port); + else if ((sp = getservbyname(servname, NULL)) != NULL) + port = sp->s_port; + else + port = 0; + } + + if (hints && hints->ai_flags & AI_PASSIVE) { + addr = htonl(0x00000000); + if (hostname && inet_aton(hostname, &in) != 0) + addr = in.s_addr; + *res = malloc_ai(port, addr, hints); + if (*res == NULL) + return (EAI_MEMORY); + return (0); + } + + if (!hostname) { + *res = malloc_ai(port, htonl(0x7f000001), hints); + if (*res == NULL) + return (EAI_MEMORY); + return (0); + } + + if (inet_aton(hostname, &in)) { + *res = malloc_ai(port, in.s_addr, hints); + if (*res == NULL) + return (EAI_MEMORY); + return (0); + } + + /* Don't try DNS if AI_NUMERICHOST is set */ + if (hints && hints->ai_flags & AI_NUMERICHOST) + return (EAI_NONAME); + + hp = gethostbyname(hostname); + if (hp && hp->h_name && hp->h_name[0] && hp->h_addr_list[0]) { + struct addrinfo *cur, *prev; + + cur = prev = *res = NULL; + for (i = 0; hp->h_addr_list[i]; i++) { + struct in_addr *in = (struct in_addr *)hp->h_addr_list[i]; + + cur = malloc_ai(port, in->s_addr, hints); + if (cur == NULL) { + if (*res != NULL) + freeaddrinfo(*res); + return (EAI_MEMORY); + } + if (prev) + prev->ai_next = cur; + else + *res = cur; + + prev = cur; + } + return (0); + } + + return (EAI_NODATA); +} +#endif /* !HAVE_GETADDRINFO */ diff --git a/fake-rfc2553.h b/fake-rfc2553.h new file mode 100644 index 0000000000000000000000000000000000000000..053e6a6a33cb4316afd9eda69beddad0bc304d7b --- /dev/null +++ b/fake-rfc2553.h @@ -0,0 +1,162 @@ +/* Taken from OpenSSH 3.8.1p1 */ + +/* $.Id: fake-rfc2553.h,v 1.9 2004/03/10 10:06:33 dtucker Exp $ */ + +/* + * Copyright (C) 2000-2003 Damien Miller. All rights reserved. + * Copyright (C) 1999 WIDE Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Pseudo-implementation of RFC2553 name / address resolution functions + * + * But these functions are not implemented correctly. The minimum subset + * is implemented for ssh use only. For example, this routine assumes + * that ai_family is AF_INET. Don't use it for another purpose. + */ + +#ifndef _FAKE_RFC2553_H +#define _FAKE_RFC2553_H + +#include "includes.h" + +/* + * First, socket and INET6 related definitions + */ +#ifndef HAVE_STRUCT_SOCKADDR_STORAGE +# define _SS_MAXSIZE 128 /* Implementation specific max size */ +# define _SS_PADSIZE (_SS_MAXSIZE - sizeof (struct sockaddr)) +struct sockaddr_storage { + struct sockaddr ss_sa; + char __ss_pad2[_SS_PADSIZE]; +}; +# define ss_family ss_sa.sa_family +#endif /* !HAVE_STRUCT_SOCKADDR_STORAGE */ + +#ifndef IN6_IS_ADDR_LOOPBACK +# define IN6_IS_ADDR_LOOPBACK(a) \ + (((u_int32_t *)(a))[0] == 0 && ((u_int32_t *)(a))[1] == 0 && \ + ((u_int32_t *)(a))[2] == 0 && ((u_int32_t *)(a))[3] == htonl(1)) +#endif /* !IN6_IS_ADDR_LOOPBACK */ + +#ifndef HAVE_STRUCT_IN6_ADDR +struct in6_addr { + u_int8_t s6_addr[16]; +}; +#endif /* !HAVE_STRUCT_IN6_ADDR */ + +#ifndef HAVE_STRUCT_SOCKADDR_IN6 +struct sockaddr_in6 { + unsigned short sin6_family; + u_int16_t sin6_port; + u_int32_t sin6_flowinfo; + struct in6_addr sin6_addr; +}; +#endif /* !HAVE_STRUCT_SOCKADDR_IN6 */ + +#ifndef AF_INET6 +/* Define it to something that should never appear */ +#define AF_INET6 AF_MAX +#endif + +/* + * Next, RFC2553 name / address resolution API + */ + +#ifndef NI_NUMERICHOST +# define NI_NUMERICHOST (1) +#endif +#ifndef NI_NAMEREQD +# define NI_NAMEREQD (1<<1) +#endif +#ifndef NI_NUMERICSERV +# define NI_NUMERICSERV (1<<2) +#endif + +#ifndef AI_PASSIVE +# define AI_PASSIVE (1) +#endif +#ifndef AI_CANONNAME +# define AI_CANONNAME (1<<1) +#endif +#ifndef AI_NUMERICHOST +# define AI_NUMERICHOST (1<<2) +#endif + +#ifndef NI_MAXSERV +# define NI_MAXSERV 32 +#endif /* !NI_MAXSERV */ +#ifndef NI_MAXHOST +# define NI_MAXHOST 1025 +#endif /* !NI_MAXHOST */ + +#ifndef EAI_NODATA +# define EAI_NODATA 1 +# define EAI_MEMORY 2 +# define EAI_NONAME 3 +#endif + +#ifndef HAVE_STRUCT_ADDRINFO +struct addrinfo { + int ai_flags; /* AI_PASSIVE, AI_CANONNAME */ + int ai_family; /* PF_xxx */ + int ai_socktype; /* SOCK_xxx */ + int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */ + size_t ai_addrlen; /* length of ai_addr */ + char *ai_canonname; /* canonical name for hostname */ + struct sockaddr *ai_addr; /* binary address */ + struct addrinfo *ai_next; /* next structure in linked list */ +}; +#endif /* !HAVE_STRUCT_ADDRINFO */ + +#ifndef HAVE_GETADDRINFO +#ifdef getaddrinfo +# undef getaddrinfo +#endif +#define getaddrinfo(a,b,c,d) (ssh_getaddrinfo(a,b,c,d)) +int getaddrinfo(const char *, const char *, + const struct addrinfo *, struct addrinfo **); +#endif /* !HAVE_GETADDRINFO */ + +#if !defined(HAVE_GAI_STRERROR) && !defined(HAVE_CONST_GAI_STRERROR_PROTO) +#define gai_strerror(a) (ssh_gai_strerror(a)) +char *gai_strerror(int); +#endif /* !HAVE_GAI_STRERROR */ + +#ifndef HAVE_FREEADDRINFO +#define freeaddrinfo(a) (ssh_freeaddrinfo(a)) +void freeaddrinfo(struct addrinfo *); +#endif /* !HAVE_FREEADDRINFO */ + +#ifndef HAVE_GETNAMEINFO +#define getnameinfo(a,b,c,d,e,f,g) (ssh_getnameinfo(a,b,c,d,e,f,g)) +int getnameinfo(const struct sockaddr *, size_t, char *, size_t, + char *, size_t, int); +#endif /* !HAVE_GETNAMEINFO */ + +#endif /* !_FAKE_RFC2553_H */ + diff --git a/filelist.txt b/filelist.txt index 7d92e7115b0bc740f97197237d08af3e154b50b2..8281c140295dfc60fbca8104c92d23e38b60f0c5 100644 --- a/filelist.txt +++ b/filelist.txt @@ -1,3 +1,7 @@ +This file is out of date - it remains here in case it is still of use. +The basic naming convention is svr- and cli- for seperate parts, +then common- for common parts. Some files have no prefix. + A brief rundown on which files do what, and their corresponding sections in the IETF drafts. The .c files usually have corresponding .h files. diff --git a/gendss.c b/gendss.c index 3e9db0977677f923fe2a17f8cb8c368b96e9e0c0..d5897227d9780f3d8975bdfb0fc164daa2d44393 100644 --- a/gendss.c +++ b/gendss.c @@ -31,9 +31,10 @@ #include "gendss.h" #include "dss.h" -#define PSIZE 128 /* 1024 bit*/ #define QSIZE 20 /* 160 bit */ +/* This is just a test */ + #ifdef DROPBEAR_DSS static void getq(dss_key *key); @@ -90,7 +91,10 @@ static void getq(dss_key *key) { static void getp(dss_key *key, unsigned int size) { - mp_int tempX, tempC, tempP, temp2q; + DEF_MP_INT(tempX); + DEF_MP_INT(tempC); + DEF_MP_INT(tempP); + DEF_MP_INT(temp2q); int result; unsigned char *buf; @@ -148,8 +152,9 @@ static void getp(dss_key *key, unsigned int size) { static void getg(dss_key * key) { - char printbuf[1000]; - mp_int div, h, val; + DEF_MP_INT(div); + DEF_MP_INT(h); + DEF_MP_INT(val); m_mp_init_multi(&div, &h, &val, NULL); @@ -179,14 +184,12 @@ static void getg(dss_key * key) { } while (mp_cmp_d(key->g, 1) != MP_GT); - mp_toradix(key->g, printbuf, 10); - mp_clear_multi(&div, &h, &val, NULL); } static void getx(dss_key *key) { - mp_int val; + DEF_MP_INT(val); char buf[QSIZE]; m_mp_init(&val); diff --git a/genrsa.c b/genrsa.c index 330ebf2bd96f547472f6bd433d37024c8ce45a51..57115193ad6f5c083b633768d9437711faf50824 100644 --- a/genrsa.c +++ b/genrsa.c @@ -40,7 +40,9 @@ static void getrsaprime(mp_int* prime, mp_int *primeminus, rsa_key * gen_rsa_priv_key(unsigned int size) { rsa_key * key; - mp_int pminus, qminus, lcm; + DEF_MP_INT(pminus); + DEF_MP_INT(qminus); + DEF_MP_INT(lcm); key = (rsa_key*)m_malloc(sizeof(rsa_key)); @@ -95,7 +97,7 @@ static void getrsaprime(mp_int* prime, mp_int *primeminus, mp_int* rsa_e, unsigned int size) { unsigned char *buf; - mp_int temp_gcd; + DEF_MP_INT(temp_gcd); buf = (unsigned char*)m_malloc(size+1); diff --git a/includes.h b/includes.h index 3fdd729f385d7291ea9b1710002ec212179a5b8c..ceeb561399ab962fd01a0f925bc2cec6990b7740 100644 --- a/includes.h +++ b/includes.h @@ -38,7 +38,6 @@ #include <sys/time.h> #include <sys/un.h> #include <sys/wait.h> -#include <sys/dir.h> #include <stdio.h> #include <errno.h> @@ -56,6 +55,7 @@ #include <netdb.h> #include <ctype.h> #include <stdarg.h> +#include <dirent.h> #include <arpa/inet.h> @@ -111,14 +111,11 @@ #include <libgen.h> #endif -#ifdef HAVE_SYS_DIRENT_H -#include <sys/dirent.h> -#endif - #include "libtomcrypt/mycrypt_custom.h" #include "libtommath/tommath.h" #include "compat.h" +#include "fake-rfc2553.h" #ifndef HAVE_UINT16_T #ifndef HAVE_U_INT16_T @@ -131,4 +128,14 @@ typedef u_int16_t uint16_t; #define LOG_AUTHPRIV LOG_AUTH #endif +/* so we can avoid warnings about unused params (ie in signal handlers etc) */ +#ifdef UNUSED +#elif defined(__GNUC__) +# define UNUSED(x) UNUSED_ ## x __attribute__((unused)) +#elif defined(__LCLINT__) +# define UNUSED(x) /*@unused@*/ x +#else +# define UNUSED(x) x +#endif + #endif /* _INCLUDES_H_ */ diff --git a/keyimport.c b/keyimport.c index 34fac2f13f74dd81f3f9861d4f264003984ad108..9f2305df6c62e890e181a7777071fee2acf8ff69 100644 --- a/keyimport.c +++ b/keyimport.c @@ -108,30 +108,14 @@ int import_write(const char *filename, sign_key *key, char *passphrase, static sign_key *dropbear_read(const char* filename) { buffer * buf = NULL; - int len, maxlen; - FILE *fp; sign_key *ret = NULL; int type; - buf = buf_new(2000); - /* can't use buf_readfile since we might have "-" as filename */ - if (strlen(filename) == 1 && filename[0] == '-') { - fp = stdin; - } else { - fp = fopen(filename, "r"); - } - if (!fp) { + buf = buf_new(MAX_PRIVKEY_SIZE); + if (buf_readfile(buf, filename) == DROPBEAR_FAILURE) { goto error; } - do { - maxlen = buf->size - buf->pos; - len = fread(buf_getwriteptr(buf, maxlen), 1, maxlen, fp); - buf_incrwritepos(buf, len); - } while (len != maxlen && len > 0); - - fclose(fp); - buf_setpos(buf, 0); ret = new_sign_key(); @@ -173,14 +157,10 @@ static int dropbear_write(const char*filename, sign_key * key) { } #endif - buf = buf_new(2000); + buf = buf_new(MAX_PRIVKEY_SIZE); buf_put_priv_key(buf, key, keytype); - if (strlen(filename) == 1 && filename[0] == '-') { - fp = stdout; - } else { - fp = fopen(filename, "w"); - } + fp = fopen(filename, "w"); if (!fp) { ret = 0; goto out; diff --git a/keyimport.h b/keyimport.h index f8c7b642e57caddc6d86e2cf89d818cac8efab62..19f212f6334977527b293df0da7d15930a753a41 100644 --- a/keyimport.h +++ b/keyimport.h @@ -1,3 +1,27 @@ +/* + * Dropbear SSH + * + * Copyright (c) 2002,2003 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + #ifndef _KEYIMPORT_H_ #define _KEYIMPORT_H_ diff --git a/listener.c b/listener.c index 7296a6183ac5e1315bb9ad972bc01df10af9ab93..1a6da041429138a6321d60f2e50e36b94139dfc1 100644 --- a/listener.c +++ b/listener.c @@ -1,3 +1,27 @@ +/* + * Dropbear SSH + * + * Copyright (c) 2002,2003 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + #include "includes.h" #include "listener.h" #include "session.h" @@ -14,15 +38,16 @@ void listeners_initialise() { void set_listener_fds(fd_set * readfds) { - unsigned int i; + unsigned int i, j; struct Listener *listener; /* check each in turn */ for (i = 0; i < ses.listensize; i++) { listener = ses.listeners[i]; if (listener != NULL) { - TRACE(("set listener fd %d", listener->sock)); - FD_SET(listener->sock, readfds); + for (j = 0; j < listener->nsocks; j++) { + FD_SET(listener->socks[j], readfds); + } } } } @@ -30,26 +55,30 @@ void set_listener_fds(fd_set * readfds) { void handle_listeners(fd_set * readfds) { - unsigned int i; + unsigned int i, j; struct Listener *listener; + int sock; /* check each in turn */ for (i = 0; i < ses.listensize; i++) { listener = ses.listeners[i]; if (listener != NULL) { - TRACE(("handle listener num %d fd %d", i, listener->sock)); - if (FD_ISSET(listener->sock, readfds)) { - listener->accepter(listener); + for (j = 0; j < listener->nsocks; j++) { + sock = listener->socks[j]; + if (FD_ISSET(sock, readfds)) { + listener->acceptor(listener, sock); + } } } } -} +} /* Woo brace matching */ -/* accepter(int fd, void* typedata) is a function to accept connections, +/* acceptor(int fd, void* typedata) is a function to accept connections, * cleanup(void* typedata) happens when cleaning up */ -struct Listener* new_listener(int sock, int type, void* typedata, - void (*accepter)(struct Listener*), +struct Listener* new_listener(int socks[], unsigned int nsocks, + int type, void* typedata, + void (*acceptor)(struct Listener* listener, int sock), void (*cleanup)(struct Listener*)) { unsigned int i, j; @@ -65,7 +94,9 @@ struct Listener* new_listener(int sock, int type, void* typedata, if (i == ses.listensize) { if (ses.listensize > MAX_LISTENERS) { TRACE(("leave newlistener: too many already")); - close(sock); + for (j = 0; j < nsocks; j++) { + close(socks[i]); + } return NULL; } @@ -80,16 +111,19 @@ struct Listener* new_listener(int sock, int type, void* typedata, } } - ses.maxfd = MAX(ses.maxfd, sock); + for (j = 0; j < nsocks; j++) { + ses.maxfd = MAX(ses.maxfd, socks[j]); + } - TRACE(("new listener num %d fd %d", i, sock)); + TRACE(("new listener num %d ", i)); newlisten = (struct Listener*)m_malloc(sizeof(struct Listener)); newlisten->index = i; newlisten->type = type; newlisten->typedata = typedata; - newlisten->sock = sock; - newlisten->accepter = accepter; + newlisten->nsocks = nsocks; + memcpy(newlisten->socks, socks, nsocks * sizeof(int)); + newlisten->acceptor = acceptor; newlisten->cleanup = cleanup; ses.listeners[i] = newlisten; @@ -116,11 +150,15 @@ struct Listener * get_listener(int type, void* typedata, void remove_listener(struct Listener* listener) { + unsigned int j; + if (listener->cleanup) { listener->cleanup(listener); } - close(listener->sock); + for (j = 0; j < listener->nsocks; j++) { + close(listener->socks[j]); + } ses.listeners[listener->index] = NULL; m_free(listener); diff --git a/listener.h b/listener.h index bda24ff6cc456e8fb77acfba72a4baa70287d039..5092efd9a24ff809d69f892c88a3cbdb3a650405 100644 --- a/listener.h +++ b/listener.h @@ -1,3 +1,27 @@ +/* + * Dropbear SSH + * + * Copyright (c) 2002,2003 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + #ifndef _LISTENER_H #define _LISTENER_H @@ -6,11 +30,12 @@ struct Listener { - int sock; + int socks[DROPBEAR_MAX_SOCKS]; + unsigned int nsocks; int index; /* index in the array of listeners */ - void (*accepter)(struct Listener*); + void (*acceptor)(struct Listener*, int sock); void (*cleanup)(struct Listener*); int type; /* CHANNEL_ID_X11, CHANNEL_ID_AGENT, @@ -25,8 +50,9 @@ void listeners_initialise(); void handle_listeners(fd_set * readfds); void set_listener_fds(fd_set * readfds); -struct Listener* new_listener(int sock, int type, void* typedata, - void (*accepter)(struct Listener*), +struct Listener* new_listener(int socks[], unsigned int nsocks, + int type, void* typedata, + void (*acceptor)(struct Listener* listener, int sock), void (*cleanup)(struct Listener*)); struct Listener * get_listener(int type, void* typedata, diff --git a/options.h b/options.h index 6ea527746d90a5335c48f01b573be6d16d9dc24b..057ed16e49549bb23b2a02ac53c08b4999f73dac 100644 --- a/options.h +++ b/options.h @@ -1,26 +1,6 @@ -/* - * Dropbear - a SSH2 server - * +/* Dropbear SSH * Copyright (c) 2002,2003 Matt Johnston - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ + * All rights reserved. See LICENSE for the license. */ #ifndef _OPTIONS_H_ #define _OPTIONS_H_ @@ -30,8 +10,8 @@ * parts are to allow for commandline -DDROPBEAR_XXX options etc. ******************************************************************/ -#ifndef DROPBEAR_PORT -#define DROPBEAR_PORT 22 +#ifndef DROPBEAR_DEFPORT +#define DROPBEAR_DEFPORT "22" #endif /* Default hostkey paths - these can be specified on the command line */ @@ -42,21 +22,43 @@ #define RSA_PRIV_FILENAME "/etc/dropbear/dropbear_rsa_host_key" #endif +/* Set NON_INETD_MODE if you require daemon functionality (ie Dropbear listens + * on chosen ports and keeps accepting connections. This is the default. + * + * Set INETD_MODE if you want to be able to run Dropbear with inetd (or + * similar), where it will use stdin/stdout for connections, and each process + * lasts for a single connection. Dropbear should be invoked with the -i flag + * for inetd, and can only accept IPv4 connections. + * + * Both of these flags can be defined at once, don't compile without at least + * one of them. */ +#define NON_INETD_MODE +#define INETD_MODE + /* Setting this disables the fast exptmod bignum code. It saves ~5kB, but is * perhaps 20% slower for pubkey operations (it is probably worth experimenting * if you want to use this) */ /*#define NO_FAST_EXPTMOD*/ -/* Enable X11 Forwarding */ +/* 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 + +/* Enable X11 Forwarding - server only */ #define ENABLE_X11FWD /* Enable TCP Fowarding */ -/* OpenSSH's "-L" style forwarding (client port forwarded via server) */ -#define ENABLE_LOCALTCPFWD -/* OpenSSH's "-R" style forwarding (server port forwarded via client) */ -#define ENABLE_REMOTETCPFWD +/* 'Local' is "-L" style (client listening port forwarded via server) + * 'Remote' is "-R" style (server listening port forwarded via client) */ + +#define ENABLE_CLI_LOCALTCPFWD +#define ENABLE_CLI_REMOTETCPFWD + +#define ENABLE_SVR_LOCALTCPFWD +#define ENABLE_SVR_REMOTETCPFWD -/* Enable Authentication Agent Forwarding */ +/* Enable Authentication Agent Forwarding - server only for now */ #define ENABLE_AGENTFWD /* Encryption - at least one required. @@ -115,9 +117,14 @@ * to make sure PAM libraries etc are installed */ #define DROPBEAR_PAM_AUTH #define DROPBEAR_PUBKEY_AUTH +#define ENABLE_SVR_PASSWORD_AUTH +#define ENABLE_SVR_PUBKEY_AUTH + +#define ENABLE_CLI_PASSWORD_AUTH +#define ENABLE_CLI_PUBKEY_AUTH /* Random device to use - you must specify _one only_. - * DEV_RANDOM is recommended on hosts with a good /dev/urandom, otherwise use + * DEV_URANDOM is recommended on hosts with a good /dev/urandom, otherwise use * PRNGD and run prngd, specifying the socket. This device must be able to * produce a large amount of random data, so using /dev/random or Entropy * Gathering Daemon (egd) may result in halting, as it waits for more random @@ -136,7 +143,7 @@ #define MAX_UNAUTH_CLIENTS 30 #endif -/* Maximum number of failed authentication tries */ +/* Maximum number of failed authentication tries (server option) */ #ifndef MAX_AUTH_TRIES #define MAX_AUTH_TRIES 10 #endif @@ -159,8 +166,9 @@ #define SFTPSERVER_PATH "/usr/libexec/sftp-server" #endif -/* This is used by the scp binary when used as a client binary */ -#define _PATH_SSH_PROGRAM "/usr/bin/ssh" +/* This is used by the scp binary when used as a client binary. If you're + * not using the Dropbear client, you'll need to change it */ +#define _PATH_SSH_PROGRAM "/usr/bin/dbclient" /* Multi-purpose binary configuration has now moved. Look at the top * of the Makefile for instructions, or INSTALL */ @@ -170,7 +178,7 @@ *******************************************************************/ #ifndef DROPBEAR_VERSION -#define DROPBEAR_VERSION "0.41-and-client" +#define DROPBEAR_VERSION "0.44test3" #endif #define LOCAL_IDENT "SSH-2.0-dropbear_" DROPBEAR_VERSION @@ -236,7 +244,7 @@ #define DROPBEAR_COMP_ZLIB 1 /* Required for pubkey auth */ -#if defined(DROPBEAR_PUBKEY_AUTH) || defined(DROPBEAR_CLIENT) +#if defined(ENABLE_SVR_PUBKEY_AUTH) || defined(DROPBEAR_CLIENT) #define DROPBEAR_SIGNKEY_VERIFY #endif @@ -283,6 +291,9 @@ /* For a 4096 bit DSS key, empirically determined to be 1590 bytes */ #define MAX_PRIVKEY_SIZE 1600 +#define DROPBEAR_MAX_SOCKS 2 /* IPv4, IPv6 are all we'll get for now. Revisit + in a few years time.... */ + #ifndef ENABLE_X11FWD #define DISABLE_X11FWD #endif @@ -299,12 +310,20 @@ #define DISABLE_REMOTETCPFWD #endif +#if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD) +#define ENABLE_CLI_ANYTCPFWD +#endif + +#if defined(ENABLE_CLI_LOCALTCPFWD) || defined(ENABLE_SVR_REMOTETCPFWD) +#define DROPBEAR_TCP_ACCEPT +#endif + #if defined(ENABLE_REMOTETCPFWD) || defined(ENABLE_LOCALTCPFWD) || \ defined(ENABLE_AGENTFWD) || defined(ENABLE_X11FWD) #define USING_LISTENERS #endif -#if defined(DROPBEAR_CLIENT) || defined(DROPBEAR_PUBKEY_AUTH) +#if defined(DROPBEAR_CLIENT) || defined(ENABLE_SVR_PUBKEY_AUTH) #define DROPBEAR_KEY_LINES /* ie we're using authorized_keys or known_hosts */ #endif diff --git a/packet.c b/packet.c index 997bc6f9a7b28cf11fa2db66224f5f01e6edeb3a..5e8e14d4ff6cf3a4b2fd50b93355ad174e2b8397 100644 --- a/packet.c +++ b/packet.c @@ -50,7 +50,7 @@ static void buf_compress(buffer * dest, buffer * src, unsigned int len); void write_packet() { int len, written; - buffer * writebuf; + buffer * writebuf = NULL; TRACE(("enter write_packet")); assert(!isempty(&ses.writequeue)); @@ -80,6 +80,7 @@ void write_packet() { /* We've finished with the packet, free it */ dequeue(&ses.writequeue); buf_free(writebuf); + writebuf = NULL; } else { /* More packet left to write, leave it in the queue for later */ buf_incrpos(writebuf, written); @@ -503,6 +504,7 @@ void encrypt_packet() { /* clearwritebuf is finished with */ buf_free(clearwritebuf); + clearwritebuf = NULL; /* enqueue the packet for sending */ buf_setpos(writebuf, 0); diff --git a/random.c b/random.c index 725b29ce6d78b27aae4a49e3b26d49abd2e602b8..65a9c6462a7e8c1c0e10d4815a24d2ca739ad840 100644 --- a/random.c +++ b/random.c @@ -60,7 +60,7 @@ static void readrand(unsigned char* buf, unsigned int buflen) { #ifdef DROPBEAR_DEV_URANDOM readfd = open(DEV_URANDOM, O_RDONLY); - if (!readfd) { + if (readfd < 0) { dropbear_exit("couldn't open random device"); } #endif @@ -71,7 +71,8 @@ static void readrand(unsigned char* buf, unsigned int buflen) { strlcpy(egdsock.sun_path, DROPBEAR_EGD_SOCKET, sizeof(egdsock.sun_path)); - if ((readfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { + readfd = socket(PF_UNIX, SOCK_STREAM, 0); + if (readfd < 0) { dropbear_exit("couldn't open random device"); } /* todo - try various common locations */ diff --git a/rsa.c b/rsa.c index 2d63c0232775dad4825f9b1b2cfbe8bc859b1a1a..7e891e043f48821bb7641e5eb42bcd26e91f6948 100644 --- a/rsa.c +++ b/rsa.c @@ -201,7 +201,8 @@ int buf_rsa_verify(buffer * buf, rsa_key *key, const unsigned char* data, unsigned int len) { unsigned int slen; - mp_int rsa_s, rsa_mdash; + DEF_MP_INT(rsa_s); + DEF_MP_INT(rsa_mdash); mp_int *rsa_em = NULL; int ret = DROPBEAR_FAILURE; @@ -244,8 +245,11 @@ int buf_rsa_verify(buffer * buf, rsa_key *key, const unsigned char* data, } out: - mp_clear_multi(rsa_em, &rsa_mdash, &rsa_s, NULL); - m_free(rsa_em); + if (rsa_em) { + mp_clear(rsa_em); + m_free(rsa_em); + } + mp_clear_multi(&rsa_mdash, &rsa_s, NULL); TRACE(("leave buf_rsa_verify: ret %d", ret)); return ret; @@ -259,16 +263,17 @@ void buf_put_rsa_sign(buffer* buf, rsa_key *key, const unsigned char* data, unsigned int nsize, ssize; unsigned int i; - mp_int rsa_s; - mp_int *rsa_em; + DEF_MP_INT(rsa_s); + mp_int *rsa_em = NULL; TRACE(("enter buf_put_rsa_sign")); assert(key != NULL); rsa_em = rsa_pad_em(key, data, len); - /* the actual signing of the padded data */ m_mp_init(&rsa_s); + + /* the actual signing of the padded data */ /* s = em^d mod n */ if (mp_exptmod(rsa_em, key->d, key->n, &rsa_s) != MP_OKAY) { dropbear_exit("rsa error"); @@ -322,10 +327,10 @@ static mp_int * rsa_pad_em(rsa_key * key, {0x00, 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14}; #define RSA_ASN1_MAGIC_LEN 16 - buffer * rsa_EM; + buffer * rsa_EM = NULL; hash_state hs; unsigned int nsize; - mp_int * rsa_em; + mp_int * rsa_em = NULL; assert(key != NULL); assert(data != NULL); diff --git a/runopts.h b/runopts.h index c74dab5dd9995466d6d1a8de1810e429fa671ae2..9597ac07c49d956054faa92f56a38ab830341831 100644 --- a/runopts.h +++ b/runopts.h @@ -29,6 +29,7 @@ #include "signkey.h" #include "buffer.h" #include "auth.h" +#include "tcpfwd.h" typedef struct runopts { @@ -51,9 +52,11 @@ typedef struct svr_runopts { int usingsyslog; /* ports is an array of the portcount listening ports */ - uint16_t *ports; + char *ports[DROPBEAR_MAX_PORTS]; unsigned int portcount; + int inetdmode; + /* Flags indicating whether to use ipv4 and ipv6 */ /* not used yet int ipv4; @@ -78,6 +81,7 @@ typedef struct svr_runopts { extern svr_runopts svr_opts; void svr_getopts(int argc, char ** argv); +void loadhostkeys(); /* Uncompleted XXX matt */ typedef struct cli_runopts { @@ -90,8 +94,14 @@ typedef struct cli_runopts { char *cmd; int wantpty; +#ifdef ENABLE_CLI_PUBKEY_AUTH struct PubkeyList *pubkeys; /* Keys to use for public-key auth */ -#ifdef DROPBEAR_PUBKEY_AUTH +#endif +#ifdef ENABLE_CLI_REMOTETCPFWD + struct TCPFwdList * remotefwds; +#endif +#ifdef ENABLE_CLI_LOCALTCPFWD + struct TCPFwdList * localfwds; #endif /* XXX TODO */ diff --git a/scp.c b/scp.c index b6eec88adbe82fc9171e867d5108ace5a2660768..e356b8b040440f6c154bd71f1c92fc4b4eae841e 100644 --- a/scp.c +++ b/scp.c @@ -178,8 +178,10 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc) close(pout[1]); args.list[0] = ssh_program; - if (remuser != NULL) - addargs(&args, "-l%s", remuser); + if (remuser != NULL) { + addargs(&args, "-l"); + addargs(&args, "%s", remuser); + } addargs(&args, "%s", host); addargs(&args, "%s", cmd); diff --git a/session.h b/session.h index 4a9d4ed4030238d75716a5b9251420e8079a4ccb..40889a1e69a740b118e9ce75917c1b28658e9a7a 100644 --- a/session.h +++ b/session.h @@ -35,6 +35,7 @@ #include "queue.h" #include "listener.h" #include "packet.h" +#include "tcpfwd.h" extern int sessinitdone; /* Is set to 0 somewhere */ extern int exitflag; @@ -42,7 +43,6 @@ extern int exitflag; void common_session_init(int sock, char* remotehost); void session_loop(void(*loophandler)()); void common_session_cleanup(); -void checktimeouts(); void session_identification(); @@ -53,8 +53,6 @@ void svr_dropbear_log(int priority, const char* format, va_list param); /* Client */ void cli_session(int sock, char *remotehost); -void cli_dropbear_exit(int exitcode, const char* format, va_list param); -void cli_dropbear_log(int priority, const char* format, va_list param); void cli_session_cleanup(); void cleantext(unsigned char* dirtytext); @@ -211,12 +209,16 @@ struct clientsession { int tty_raw_mode; /* Whether we're in raw mode (and have to clean up) */ struct termios saved_tio; + int stdincopy; + int stdinflags; int winchange; /* Set to 1 when a windowchange signal happens */ int lastauthtype; /* either AUTH_TYPE_PUBKEY or AUTH_TYPE_PASSWORD, for the last type of auth we tried */ struct PubkeyList *lastpubkey; + + int retval; /* What the command exit status was - we emulate it */ #if 0 TODO struct AgentkeyList *agentkeys; /* Keys to use for public-key auth */ diff --git a/signkey.c b/signkey.c index 3efcc2b25f4699057fbc60bb89daa6a437c21033..2c8da55889311ce79eaffe86543a1da0a9bb833a 100644 --- a/signkey.c +++ b/signkey.c @@ -153,6 +153,7 @@ int buf_get_priv_key(buffer *buf, sign_key *key, int *type) { m_free(ident); if (*type != DROPBEAR_SIGNKEY_ANY && *type != keytype) { + TRACE(("wrong key type: %d %d", *type, keytype)); return DROPBEAR_FAILURE; } @@ -194,7 +195,7 @@ void buf_put_pub_key(buffer* buf, sign_key *key, int type) { buffer *pubkeys; TRACE(("enter buf_put_pub_key")); - pubkeys = buf_new(1000); + pubkeys = buf_new(MAX_PUBKEY_SIZE); #ifdef DROPBEAR_DSS if (type == DROPBEAR_SIGNKEY_DSS) { @@ -356,7 +357,7 @@ void buf_put_sign(buffer* buf, sign_key *key, int type, buffer *sigblob; - sigblob = buf_new(1000); + sigblob = buf_new(MAX_PUBKEY_SIZE); #ifdef DROPBEAR_DSS if (type == DROPBEAR_SIGNKEY_DSS) { diff --git a/svr-agentfwd.c b/svr-agentfwd.c index 0dad2a491251f7562f7bb671dfdf49e92d88ed19..60c23f7eb04569e209391f1c59cee652a3353ba0 100644 --- a/svr-agentfwd.c +++ b/svr-agentfwd.c @@ -44,7 +44,7 @@ static int send_msg_channel_open_agent(int fd); static int bindagent(int fd, struct ChanSess * chansess); -static void agentaccept(struct Listener * listener); +static void agentaccept(struct Listener * listener, int sock); /* Handles client requests to start agent forwarding, sets up listening socket. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ @@ -64,7 +64,7 @@ int agentreq(struct ChanSess * chansess) { /* create the unix socket dir and file */ if (bindagent(fd, chansess) == DROPBEAR_FAILURE) { - return DROPBEAR_FAILURE; + goto fail; } /* listen */ @@ -73,12 +73,10 @@ int agentreq(struct ChanSess * chansess) { } /* set non-blocking */ - if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { - goto fail; - } + setnonblocking(fd); /* pass if off to listener */ - chansess->agentlistener = new_listener( fd, 0, chansess, + chansess->agentlistener = new_listener( &fd, 1, 0, chansess, agentaccept, NULL); if (chansess->agentlistener == NULL) { @@ -97,11 +95,11 @@ fail: /* accepts a connection on the forwarded socket and opens a new channel for it * back to the client */ /* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ -static void agentaccept(struct Listener * listener) { +static void agentaccept(struct Listener *UNUSED(listener), int sock) { int fd; - fd = accept(listener->sock, NULL, NULL); + fd = accept(sock, NULL, NULL); if (fd < 0) { TRACE(("accept failed")); return; @@ -146,7 +144,7 @@ void agentcleanup(struct ChanSess * chansess) { chansess->agentlistener = NULL; } - if (chansess->agentfile && chansess->agentdir) { + if (chansess->agentfile != NULL && chansess->agentdir != NULL) { /* Remove the dir as the user. That way they can't cause problems except * for themselves */ diff --git a/svr-algo.c b/svr-algo.c index dc6565ac6456bf94c8deb5076a08d85f686b084f..5559da6976d9223345759569dfb2479a869b8674 100644 --- a/svr-algo.c +++ b/svr-algo.c @@ -1,3 +1,29 @@ +/* + * Dropbear - a SSH2 server + * SSH client implementation + * + * Copyright (c) 2002,2003 Matt Johnston + * Copyright (c) 2004 by Mihnea Stoenescu + * 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 "algo.h" #include "dbutil.h" diff --git a/svr-auth.c b/svr-auth.c index 9e82b253418eb8217abc83a12984d0189e1caaff..1f80b317bf6204c17b42c960b3097ece56fa6513 100644 --- a/svr-auth.c +++ b/svr-auth.c @@ -32,8 +32,6 @@ #include "ssh.h" #include "packet.h" #include "auth.h" -#include "authpasswd.h" -#include "authpubkey.h" #include "runopts.h" static void authclear(); @@ -54,10 +52,11 @@ void svr_authinitialise() { static void authclear() { memset(&ses.authstate, 0, sizeof(ses.authstate)); -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_SVR_PUBKEY_AUTH ses.authstate.authtypes |= AUTH_TYPE_PUBKEY; #endif #if defined(DROPBEAR_PASSWORD_AUTH) || defined(DROPBEAR_PAM_AUTH) +#ifdef ENABLE_SVR_PASSWORD_AUTH if (!svr_opts.noauthpass) { ses.authstate.authtypes |= AUTH_TYPE_PASSWORD; } @@ -93,7 +92,7 @@ static void send_msg_userauth_banner() { * checking, and handle success or failure */ void recv_msg_userauth_request() { - unsigned char *username, *servicename, *methodname; + unsigned char *username = NULL, *servicename = NULL, *methodname = NULL; unsigned int userlen, servicelen, methodlen; TRACE(("enter recv_msg_userauth_request")); @@ -143,7 +142,7 @@ void recv_msg_userauth_request() { goto out; } -#ifdef DROPBEAR_PASSWORD_AUTH +#ifdef ENABLE_SVR_PASSWORD_AUTH if (!svr_opts.noauthpass && !(svr_opts.norootpass && ses.authstate.pw->pw_uid == 0) ) { /* user wants to try password auth */ @@ -169,7 +168,7 @@ void recv_msg_userauth_request() { } #endif -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_SVR_PUBKEY_AUTH /* user wants to try pubkey auth */ if (methodlen == AUTH_METHOD_PUBKEY_LEN && strncmp(methodname, AUTH_METHOD_PUBKEY, @@ -290,7 +289,7 @@ goodshell: * failures */ void send_msg_userauth_failure(int partial, int incrfail) { - buffer *typebuf; + buffer *typebuf = NULL; TRACE(("enter send_msg_userauth_failure")); @@ -355,6 +354,8 @@ void send_msg_userauth_success() { encrypt_packet(); ses.authstate.authdone = 1; + ses.connecttimeout = 0; + if (ses.authstate.pw->pw_uid == 0) { ses.allowprivport = 1; diff --git a/svr-authpasswd.c b/svr-authpasswd.c index 72495536467c4f97fe8ffd0582df579da0a02a9c..6f7c9098d5a9a25c1f960195e28868df593120a5 100644 --- a/svr-authpasswd.c +++ b/svr-authpasswd.c @@ -29,23 +29,22 @@ #include "buffer.h" #include "dbutil.h" #include "auth.h" -#include "authpasswd.h" -#ifdef DROPBEAR_PASSWORD_AUTH +#ifdef ENABLE_SVR_PASSWORD_AUTH /* Process a password auth request, sending success or failure messages as * appropriate */ void svr_auth_password() { #ifdef HAVE_SHADOW_H - struct spwd *spasswd; + struct spwd *spasswd = NULL; #endif - char * passwdcrypt; /* the crypt from /etc/passwd or /etc/shadow */ - char * testcrypt; /* crypt generated from the user's password sent */ + 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; unsigned int passwordlen; - unsigned char changepw; + unsigned int changepw; passwdcrypt = ses.authstate.pw->pw_passwd; #ifdef HAVE_SHADOW_H @@ -105,4 +104,4 @@ void svr_auth_password() { } -#endif /* DROPBEAR_PASSWORD_AUTH */ +#endif diff --git a/svr-authpubkey.c b/svr-authpubkey.c index 53d5c064ac1c58011c38305ba70ded27f23ea616..14b5a78439a2fc3182db3f0e77940f332b438a07 100644 --- a/svr-authpubkey.c +++ b/svr-authpubkey.c @@ -30,12 +30,11 @@ #include "buffer.h" #include "signkey.h" #include "auth.h" -#include "authpubkey.h" #include "ssh.h" #include "packet.h" #include "algo.h" -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_SVR_PUBKEY_AUTH #define MIN_AUTHKEYS_LINE 10 /* "ssh-rsa AB" - short but doesn't matter */ #define MAX_AUTHKEYS_LINE 4200 /* max length of a line in authkeys */ @@ -54,7 +53,7 @@ void svr_auth_pubkey() { unsigned char testkey; /* whether we're just checking if a key is usable */ unsigned char* algo = NULL; /* pubkey algo */ unsigned int algolen; - unsigned char* keyblob; + unsigned char* keyblob = NULL; unsigned int keybloblen; buffer * signbuf = NULL; sign_key * key = NULL; @@ -336,4 +335,4 @@ static int checkfileperm(char * filename) { } -#endif /* DROPBEAR_PUBKEY_AUTH */ +#endif diff --git a/svr-chansession.c b/svr-chansession.c index 9639961b15457c1efe14053be97a403d4951fa0d..e9a3c8753cb90d272e881f4cee7c6a3b6fa95aa5 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -55,6 +55,10 @@ static int newchansess(struct Channel *channel); static void chansessionrequest(struct Channel *channel); static void send_exitsignalstatus(struct Channel *channel); +static void send_msg_chansess_exitstatus(struct Channel * channel, + struct ChanSess * chansess); +static void send_msg_chansess_exitsignal(struct Channel * channel, + struct ChanSess * chansess); static int sesscheckclose(struct Channel *channel); static void get_termmodes(struct ChanSess *chansess); @@ -68,7 +72,7 @@ static int sesscheckclose(struct Channel *channel) { } /* handler for childs exiting, store the state for return to the client */ -static void sesssigchild_handler(int dummy) { +static void sesssigchild_handler(int UNUSED(dummy)) { int status; pid_t pid; @@ -78,7 +82,6 @@ static void sesssigchild_handler(int dummy) { TRACE(("enter sigchld handler")); while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { - /* find the corresponding chansess */ for (i = 0; i < svr_ses.childpidsize; i++) { if (svr_ses.childpids[i].pid == pid) { @@ -90,8 +93,10 @@ static void sesssigchild_handler(int dummy) { } if (WIFSIGNALED(status)) { chansess->exitsignal = WTERMSIG(status); -#ifndef AIX +#if !defined(AIX) && defined(WCOREDUMP) chansess->exitcore = WCOREDUMP(status); +#else + chansess->exitcore = 0; #endif } else { /* we use this to determine how pid exited */ @@ -273,7 +278,7 @@ static void closechansess(struct Channel *channel) { * or x11/authagent forwarding. These are passed to appropriate handlers */ static void chansessionrequest(struct Channel *channel) { - unsigned char * type; + unsigned char * type = NULL; unsigned int typelen; unsigned char wantreply; int ret = 1; @@ -320,7 +325,7 @@ static void chansessionrequest(struct Channel *channel) { out: if (wantreply) { - if (ret == 0) { + if (ret == DROPBEAR_SUCCESS) { send_msg_channel_success(channel); } else { send_msg_channel_failure(channel); @@ -336,7 +341,7 @@ out: static int sessionsignal(struct ChanSess *chansess) { int sig = 0; - unsigned char* signame; + unsigned char* signame = NULL; int i; if (chansess->pid == 0) { @@ -408,6 +413,8 @@ static void get_termmodes(struct ChanSess *chansess) { } len = buf_getint(ses.payload); + 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"); } @@ -495,7 +502,9 @@ static int sessionpty(struct ChanSess * chansess) { } /* allocate the pty */ - assert(chansess->master == -1); /* haven't already got one */ + if (chansess->master != -1) { + dropbear_exit("multiple pty requests"); + } if (pty_allocate(&chansess->master, &chansess->slave, namebuf, 64) == 0) { TRACE(("leave sessionpty: failed to allocate pty")); return DROPBEAR_FAILURE; @@ -526,11 +535,14 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess, int iscmd, int issubsys) { unsigned int cmdlen; + int ret; TRACE(("enter sessioncommand")); if (chansess->cmd != NULL) { - /* TODO - send error - multiple commands? */ + /* Note that only one command can _succeed_. The client might try + * one command (which fails), then try another. Ie fallback + * from sftp to scp */ return DROPBEAR_FAILURE; } @@ -539,6 +551,7 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess, chansess->cmd = buf_getstring(ses.payload, &cmdlen); if (cmdlen > MAX_CMD_LEN) { + m_free(chansess->cmd); /* TODO - send error - too long ? */ return DROPBEAR_FAILURE; } @@ -550,6 +563,7 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess, } else #endif { + m_free(chansess->cmd); return DROPBEAR_FAILURE; } } @@ -557,11 +571,16 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess, if (chansess->term == NULL) { /* no pty */ - return noptycommand(channel, chansess); + ret = noptycommand(channel, chansess); } else { /* want pty */ - return ptycommand(channel, chansess); + ret = ptycommand(channel, chansess); + } + + if (ret == DROPBEAR_FAILURE) { + m_free(chansess->cmd); } + return ret; } /* Execute a command and set up redirection of stdin/stdout/stderr without a @@ -616,7 +635,10 @@ static int noptycommand(struct Channel *channel, struct ChanSess *chansess) { TRACE(("continue noptycommand: parent")); chansess->pid = pid; - /* add a child pid */ + /* add a child pid - Beware: there's a race between this, and the + * exec() called from the child. If the child finishes before we've + * done this (ie if it was a shell builtin and fast), we won't return a + * proper return code. For now, we ignore this case. */ addchildpid(chansess, pid); close(infds[FDIN]); @@ -629,11 +651,10 @@ static int noptycommand(struct Channel *channel, struct ChanSess *chansess) { ses.maxfd = MAX(ses.maxfd, channel->outfd); ses.maxfd = MAX(ses.maxfd, channel->errfd); - if ((fcntl(channel->outfd, F_SETFL, O_NONBLOCK) < 0) || - (fcntl(channel->infd, F_SETFL, O_NONBLOCK) < 0) || - (fcntl(channel->errfd, F_SETFL, O_NONBLOCK) < 0)) { - dropbear_exit("Couldn't set nonblocking"); - } + setnonblocking(channel->outfd); + setnonblocking(channel->infd); + setnonblocking(channel->errfd); + } #undef FDIN #undef FDOUT @@ -648,7 +669,7 @@ static int noptycommand(struct Channel *channel, struct ChanSess *chansess) { static int ptycommand(struct Channel *channel, struct ChanSess *chansess) { pid_t pid; - struct logininfo *li; + struct logininfo *li = NULL; #ifdef DO_MOTD buffer * motdbuf = NULL; int len; @@ -739,9 +760,7 @@ static int ptycommand(struct Channel *channel, struct ChanSess *chansess) { /* don't need to set stderr here */ ses.maxfd = MAX(ses.maxfd, chansess->master); - if (fcntl(chansess->master, F_SETFL, O_NONBLOCK) < 0) { - dropbear_exit("Couldn't set nonblocking"); - } + setnonblocking(chansess->master); } @@ -776,8 +795,8 @@ static void addchildpid(struct ChanSess *chansess, pid_t pid) { static void execchild(struct ChanSess *chansess) { char *argv[4]; - char * usershell; - char * baseshell; + char * usershell = NULL; + char * baseshell = NULL; unsigned int i; /* wipe the hostkey */ @@ -861,6 +880,11 @@ static void execchild(struct ChanSess *chansess) { agentset(chansess); #endif + /* Re-enable SIGPIPE for the executed process */ + if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) { + dropbear_exit("signal() error"); + } + baseshell = basename(usershell); if (chansess->cmd != NULL) { @@ -919,7 +943,7 @@ void svr_chansessinitialise() { /* add a new environment variable, allocating space for the entry */ void addnewvar(const char* param, const char* var) { - char* newvar; + char* newvar = NULL; int plen, vlen; plen = strlen(param); diff --git a/svr-kex.c b/svr-kex.c index 35b50a6913117c4a4b891cbf7ad128a8d9e254cb..47c540ebde8892b02eeb2e6868bad070c6067911 100644 --- a/svr-kex.c +++ b/svr-kex.c @@ -2,6 +2,7 @@ * Dropbear - a SSH2 server * * Copyright (c) 2002,2003 Matt Johnston + * Copyright (c) 2004 by Mihnea Stoenescu * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -43,7 +44,7 @@ static void send_msg_kexdh_reply(mp_int *dh_e); * that function, then brings the new keys into use */ void recv_msg_kexdh_init() { - mp_int dh_e; + DEF_MP_INT(dh_e); TRACE(("enter recv_msg_kexdh_init")); if (!ses.kexstate.recvkexinit) { @@ -70,9 +71,11 @@ void recv_msg_kexdh_init() { * See the ietf-secsh-transport draft, section 6, for details */ static void send_msg_kexdh_reply(mp_int *dh_e) { - mp_int dh_y, dh_f; + DEF_MP_INT(dh_y); + DEF_MP_INT(dh_f); TRACE(("enter send_msg_kexdh_reply")); + m_mp_init_multi(&dh_y, &dh_f, NULL); gen_kexdh_vals(&dh_f, &dh_y); diff --git a/svr-main.c b/svr-main.c index 312e47c7ba09e7e3c00b5035983944da94476732..ae05c0dbe7bffd6503113d8161e4704e73ccdf07 100644 --- a/svr-main.c +++ b/svr-main.c @@ -29,10 +29,13 @@ #include "signkey.h" #include "runopts.h" -static int listensockets(int *sock, int *maxfd); +static int listensockets(int *sock, int sockcount, int *maxfd); static void sigchld_handler(int dummy); static void sigsegv_handler(int); static void sigintterm_handler(int fish); +static void main_inetd(); +static void main_noinetd(); +static void commonsetup(); static int childpipes[MAX_UNAUTH_CLIENTS]; @@ -44,29 +47,82 @@ int main(int argc, char ** argv) #endif { + + _dropbear_exit = svr_dropbear_exit; + _dropbear_log = svr_dropbear_log; + + /* get commandline options */ + svr_getopts(argc, argv); + +#ifdef INETD_MODE + /* service program mode */ + if (svr_opts.inetdmode) { + main_inetd(); + /* notreached */ + } +#endif + +#ifdef NON_INETD_MODE + main_noinetd(); + /* notreached */ +#endif + + dropbear_exit("Compiled without normal mode, can't run without -i\n"); + return -1; +} +#endif + +#ifdef INETD_MODE +static void main_inetd() { + + struct sockaddr_storage remoteaddr; + int remoteaddrlen; + char * addrstring = NULL; + + /* Set up handlers, syslog */ + 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); + m_free(addrstring); + + /* Don't check the return value - it may just fail since inetd has + * already done setsid() after forking (xinetd on Darwin appears to do + * this */ + setsid(); + + /* Start service program + * -1 is a dummy childpipe, just something we can close() without + * mattering. */ + svr_session(0, -1, getaddrhostname(&remoteaddr)); + + /* notreached */ +} +#endif /* INETD_MODE */ + +#ifdef NON_INETD_MODE +void main_noinetd() { fd_set fds; struct timeval seltimeout; unsigned int i, j; int val; int maxsock = -1; - struct sockaddr remoteaddr; + struct sockaddr_storage remoteaddr; int remoteaddrlen; int listensocks[MAX_LISTEN_ADDR]; - unsigned int listensockcount = 0; - FILE * pidfile; + int listensockcount = 0; + FILE *pidfile = NULL; int childsock; pid_t childpid; int childpipe[2]; - struct sigaction sa_chld; - - _dropbear_exit = svr_dropbear_exit; - _dropbear_log = svr_dropbear_log; - - /* get commandline options */ - svr_getopts(argc, argv); - /* fork */ if (svr_opts.forkbg) { int closefds = 0; @@ -76,16 +132,12 @@ int main(int argc, char ** argv) } #endif if (daemon(0, closefds) < 0) { - dropbear_exit("Failed to create background process: %s", - strerror(errno)); + dropbear_exit("Failed to daemonize: %s", strerror(errno)); } } -#ifndef DISABLE_SYSLOG - if (svr_opts.usingsyslog) { - startsyslog(); - } -#endif + commonsetup(); + /* should be done after syslog is working */ if (svr_opts.forkbg) { @@ -101,25 +153,6 @@ int main(int argc, char ** argv) fclose(pidfile); } - /* set up cleanup handler */ - if (signal(SIGINT, sigintterm_handler) == SIG_ERR || -#ifndef DEBUG_VALGRIND - signal(SIGTERM, sigintterm_handler) == SIG_ERR || -#endif - signal(SIGPIPE, SIG_IGN) == SIG_ERR) { - dropbear_exit("signal() error"); - } - - /* catch and reap zombie children */ - sa_chld.sa_handler = sigchld_handler; - sa_chld.sa_flags = SA_NOCLDSTOP; - if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) { - dropbear_exit("signal() error"); - } - if (signal(SIGSEGV, sigsegv_handler) == SIG_ERR) { - dropbear_exit("signal() error"); - } - /* sockets to identify pre-authenticated clients */ for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) { childpipes[i] = -1; @@ -127,7 +160,10 @@ int main(int argc, char ** argv) /* Set up the listening sockets */ /* XXX XXX ports */ - listensockcount = listensockets(listensocks, &maxsock); + listensockcount = listensockets(listensocks, MAX_LISTEN_ADDR, &maxsock); + if (listensockcount < 0) { + dropbear_exit("No listening ports available."); + } /* incoming connection select loop */ for(;;) { @@ -138,7 +174,7 @@ int main(int argc, char ** argv) seltimeout.tv_usec = 0; /* listening sockets */ - for (i = 0; i < listensockcount; i++) { + for (i = 0; i < (unsigned int)listensockcount; i++) { FD_SET(listensocks[i], &fds); } @@ -179,13 +215,13 @@ int main(int argc, char ** argv) } /* handle each socket which has something to say */ - for (i = 0; i < listensockcount; i++) { + for (i = 0; i < (unsigned int)listensockcount; i++) { if (!FD_ISSET(listensocks[i], &fds)) continue; - /* child connection XXX - ip6 stuff here */ - remoteaddrlen = sizeof(struct sockaddr_in); - childsock = accept(listensocks[i], &remoteaddr, &remoteaddrlen); + remoteaddrlen = sizeof(remoteaddr); + childsock = accept(listensocks[i], + (struct sockaddr*)&remoteaddr, &remoteaddrlen); if (childsock < 0) { /* accept failed */ @@ -222,7 +258,7 @@ int main(int argc, char ** argv) monstartup((u_long)&_start, (u_long)&etext); #endif /* DEBUG_FORKGPROF */ - addrstring = getaddrstring(&remoteaddr); + addrstring = getaddrstring(&remoteaddr, 1); dropbear_log(LOG_INFO, "Child connection from %s", addrstring); m_free(addrstring); @@ -231,7 +267,7 @@ int main(int argc, char ** argv) } /* make sure we close sockets */ - for (i = 0; i < listensockcount; i++) { + for (i = 0; i < (unsigned int)listensockcount; i++) { if (m_close(listensocks[i]) == DROPBEAR_FAILURE) { dropbear_exit("Couldn't close socket"); } @@ -258,12 +294,12 @@ int main(int argc, char ** argv) } /* for(;;) loop */ /* don't reach here */ - return -1; } -#endif +#endif /* NON_INETD_MODE */ + /* catch + reap zombie children */ -static void sigchld_handler(int fish) { +static void sigchld_handler(int UNUSED(unused)) { struct sigaction sa_chld; while(waitpid(-1, NULL, WNOHANG) > 0); @@ -276,72 +312,79 @@ static void sigchld_handler(int fish) { } /* catch any segvs */ -static void sigsegv_handler(int fish) { +static void sigsegv_handler(int UNUSED(unused)) { fprintf(stderr, "Aiee, segfault! You should probably report " "this as a bug to the developer\n"); exit(EXIT_FAILURE); } /* catch ctrl-c or sigterm */ -static void sigintterm_handler(int fish) { +static void sigintterm_handler(int UNUSED(unused)) { exitflag = 1; } +/* Things used by inetd and non-inetd modes */ +static void commonsetup() { + + struct sigaction sa_chld; +#ifndef DISABLE_SYSLOG + if (svr_opts.usingsyslog) { + startsyslog(); + } +#endif + + /* set up cleanup handler */ + if (signal(SIGINT, sigintterm_handler) == SIG_ERR || +#ifndef DEBUG_VALGRIND + signal(SIGTERM, sigintterm_handler) == SIG_ERR || +#endif + signal(SIGPIPE, SIG_IGN) == SIG_ERR) { + dropbear_exit("signal() error"); + } + + /* catch and reap zombie children */ + sa_chld.sa_handler = sigchld_handler; + sa_chld.sa_flags = SA_NOCLDSTOP; + if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) { + dropbear_exit("signal() error"); + } + if (signal(SIGSEGV, sigsegv_handler) == SIG_ERR) { + dropbear_exit("signal() error"); + } + + /* Now we can setup the hostkeys - needs to be after logging is on, + * otherwise we might end up blatting error messages to the socket */ + loadhostkeys(); +} + /* Set up listening sockets for all the requested ports */ -static int listensockets(int *sock, int *maxfd) { +static int listensockets(int *sock, int sockcount, int *maxfd) { - int listensock; /* listening fd */ - struct sockaddr_in listen_addr; - struct linger linger; unsigned int i; - int val; + char* errstring = NULL; + unsigned int sockpos = 0; + int nsock; + + TRACE(("listensockets: %d to try\n", svr_opts.portcount)); for (i = 0; i < svr_opts.portcount; i++) { - /* iterate through all the sockets to listen on */ - listensock = socket(PF_INET, SOCK_STREAM, 0); - if (listensock < 0) { - dropbear_exit("Failed to create socket"); - } + TRACE(("listening on '%s'", svr_opts.ports[i])); - val = 1; - /* set to reuse, quick timeout */ - setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR, - (void*) &val, sizeof(val)); - linger.l_onoff = 1; - linger.l_linger = 5; - setsockopt(listensock, SOL_SOCKET, SO_LINGER, - (void*)&linger, sizeof(linger)); - - /* disable nagle */ - setsockopt(listensock, IPPROTO_TCP, TCP_NODELAY, - (void*)&val, sizeof(val)); - - memset((void*)&listen_addr, 0x0, sizeof(listen_addr)); - listen_addr.sin_family = AF_INET; - listen_addr.sin_port = htons(svr_opts.ports[i]); - listen_addr.sin_addr.s_addr = htonl(INADDR_ANY); - memset(&(listen_addr.sin_zero), '\0', 8); - - if (bind(listensock, (struct sockaddr *)&listen_addr, - sizeof(listen_addr)) < 0) { - dropbear_exit("Bind failed port %d", svr_opts.ports[i]); - } + nsock = dropbear_listen(NULL, svr_opts.ports[i], &sock[sockpos], + sockcount - sockpos, + &errstring, maxfd); - /* listen */ - if (listen(listensock, 20) < 0) { /* TODO set listen count */ - dropbear_exit("Listen failed"); + if (nsock < 0) { + dropbear_log(LOG_WARNING, "Failed listening on '%s': %s", + svr_opts.ports[i], errstring); + m_free(errstring); + continue; } - /* nonblock */ - if (fcntl(listensock, F_SETFL, O_NONBLOCK) < 0) { - dropbear_exit("Failed to set non-blocking"); - } + sockpos += nsock; - sock[i] = listensock; - *maxfd = MAX(listensock, *maxfd); } - - return svr_opts.portcount; + return sockpos; } diff --git a/svr-runopts.c b/svr-runopts.c index 996c15c63c3ee0b4d7656ae6a3e4aade76218a68..dfd5daf388fa9ddcb6ab069014e0a5fddc02b353 100644 --- a/svr-runopts.c +++ b/svr-runopts.c @@ -31,8 +31,6 @@ svr_runopts svr_opts; /* GLOBAL */ -static sign_key * loadhostkeys(const char * dsskeyfile, - const char * rsakeyfile); static void printhelp(const char * progname); static void printhelp(const char * progname) { @@ -61,7 +59,7 @@ static void printhelp(const char * progname) { "-m Don't display the motd on login\n" #endif "-w Disallow root logins\n" -#ifdef DROPBEAR_PASSWORD_AUTH +#ifdef ENABLE_SVR_PASSWORD_AUTH "-s Disable password logins\n" "-g Disable password logins for root\n" #endif @@ -72,9 +70,13 @@ static void printhelp(const char * progname) { "-k Disable remote port forwarding\n" #endif "-p port Listen on specified tcp port, up to %d can be specified\n" - " (default %d if none specified)\n" -/* "-4/-6 Disable listening on ipv4/ipv6 respectively\n"*/ - + " (default %s if none specified)\n" +#ifdef INETD_MODE + "-i Start for inetd\n" +#endif +#ifdef DEBUG_TRACE + "-v verbose\n" +#endif ,DROPBEAR_VERSION, progname, #ifdef DROPBEAR_DSS DSS_PRIV_FILENAME, @@ -82,16 +84,13 @@ static void printhelp(const char * progname) { #ifdef DROPBEAR_RSA RSA_PRIV_FILENAME, #endif - DROPBEAR_MAX_PORTS, DROPBEAR_PORT); + DROPBEAR_MAX_PORTS, DROPBEAR_DEFPORT); } void svr_getopts(int argc, char ** argv) { unsigned int i; char ** next = 0; - unsigned int portnum = 0; - char *portstring[DROPBEAR_MAX_PORTS]; - unsigned int longport; /* see printhelp() for options */ svr_opts.rsakeyfile = NULL; @@ -102,6 +101,9 @@ void svr_getopts(int argc, char ** argv) { svr_opts.norootlogin = 0; svr_opts.noauthpass = 0; svr_opts.norootpass = 0; + svr_opts.inetdmode = 0; + svr_opts.portcount = 0; + svr_opts.hostkey = NULL; opts.nolocaltcp = 0; opts.noremotetcp = 0; /* not yet @@ -157,12 +159,19 @@ void svr_getopts(int argc, char ** argv) { case 'k': opts.noremotetcp = 1; break; +#endif +#ifdef INETD_MODE + case 'i': + svr_opts.inetdmode = 1; + break; #endif case 'p': - if (portnum < DROPBEAR_MAX_PORTS) { - portstring[portnum] = NULL; - next = &portstring[portnum]; - portnum++; + if (svr_opts.portcount < DROPBEAR_MAX_PORTS) { + svr_opts.ports[svr_opts.portcount] = NULL; + next = &svr_opts.ports[svr_opts.portcount]; + /* Note: if it doesn't actually get set, we'll + * decrement it after the loop */ + svr_opts.portcount++; } break; #ifdef DO_MOTD @@ -174,7 +183,7 @@ void svr_getopts(int argc, char ** argv) { case 'w': svr_opts.norootlogin = 1; break; -#ifdef DROPBEAR_PASSWORD_AUTH +#ifdef ENABLE_SVR_PASSWORD_AUTH case 's': svr_opts.noauthpass = 1; break; @@ -186,14 +195,11 @@ void svr_getopts(int argc, char ** argv) { printhelp(argv[0]); exit(EXIT_FAILURE); break; - /* - case '4': - svr_opts.ipv4 = 0; - break; - case '6': - svr_opts.ipv6 = 0; +#ifdef DEBUG_TRACE + case 'v': + debug_trace = 1; break; - */ +#endif default: fprintf(stderr, "Unknown argument %s\n", argv[i]); printhelp(argv[0]); @@ -203,13 +209,24 @@ void svr_getopts(int argc, char ** argv) { } } + /* Set up listening ports */ + if (svr_opts.portcount == 0) { + svr_opts.ports[0] = m_strdup(DROPBEAR_DEFPORT); + svr_opts.portcount = 1; + } else { + /* we may have been given a -p option but no argument to go with + * it */ + if (svr_opts.ports[svr_opts.portcount-1] == NULL) { + svr_opts.portcount--; + } + } + if (svr_opts.dsskeyfile == NULL) { svr_opts.dsskeyfile = DSS_PRIV_FILENAME; } if (svr_opts.rsakeyfile == NULL) { svr_opts.rsakeyfile = RSA_PRIV_FILENAME; } - svr_opts.hostkey = loadhostkeys(svr_opts.dsskeyfile, svr_opts.rsakeyfile); if (svr_opts.bannerfile) { struct stat buf; @@ -231,35 +248,6 @@ void svr_getopts(int argc, char ** argv) { buf_setpos(svr_opts.banner, 0); } - /* not yet - if (!(svr_opts.ipv4 || svr_opts.ipv6)) { - fprintf(stderr, "You can't disable ipv4 and ipv6.\n"); - exit(1); - } - */ - - /* create the array of listening ports */ - if (portnum == 0) { - /* non specified */ - svr_opts.portcount = 1; - svr_opts.ports = m_malloc(sizeof(uint16_t)); - svr_opts.ports[0] = DROPBEAR_PORT; - } else { - svr_opts.portcount = portnum; - svr_opts.ports = (uint16_t*)m_malloc(sizeof(uint16_t)*portnum); - for (i = 0; i < portnum; i++) { - if (portstring[i]) { - longport = atoi(portstring[i]); - if (longport <= 65535 && longport > 0) { - svr_opts.ports[i] = (uint16_t)longport; - continue; - } - } - fprintf(stderr, "Bad port '%s'\n", - portstring[i] ? portstring[i] : "null"); - } - } - } static void disablekey(int type, const char* filename) { @@ -272,47 +260,45 @@ static void disablekey(int type, const char* filename) { break; } } - fprintf(stderr, "Failed reading '%s', disabling %s\n", filename, + dropbear_log(LOG_WARNING, "Failed reading '%s', disabling %s", filename, type == DROPBEAR_SIGNKEY_DSS ? "DSS" : "RSA"); } -static sign_key * loadhostkeys(const char * dsskeyfile, - const char * rsakeyfile) { +/* Must be called after syslog/etc is working */ +void loadhostkeys() { - sign_key * hostkey; int ret; int type; TRACE(("enter loadhostkeys")); - hostkey = new_sign_key(); + svr_opts.hostkey = new_sign_key(); #ifdef DROPBEAR_RSA type = DROPBEAR_SIGNKEY_RSA; - ret = readhostkey(rsakeyfile, hostkey, &type); + ret = readhostkey(svr_opts.rsakeyfile, svr_opts.hostkey, &type); if (ret == DROPBEAR_FAILURE) { - disablekey(DROPBEAR_SIGNKEY_RSA, rsakeyfile); + disablekey(DROPBEAR_SIGNKEY_RSA, svr_opts.rsakeyfile); } #endif #ifdef DROPBEAR_DSS - type = DROPBEAR_SIGNKEY_RSA; - ret = readhostkey(dsskeyfile, hostkey, &type); + type = DROPBEAR_SIGNKEY_DSS; + ret = readhostkey(svr_opts.dsskeyfile, svr_opts.hostkey, &type); if (ret == DROPBEAR_FAILURE) { - disablekey(DROPBEAR_SIGNKEY_DSS, dsskeyfile); + disablekey(DROPBEAR_SIGNKEY_DSS, svr_opts.dsskeyfile); } #endif if ( 1 #ifdef DROPBEAR_DSS - && hostkey->dsskey == NULL + && svr_opts.hostkey->dsskey == NULL #endif #ifdef DROPBEAR_RSA - && hostkey->rsakey == NULL + && svr_opts.hostkey->rsakey == NULL #endif ) { dropbear_exit("No hostkeys available"); } TRACE(("leave loadhostkeys")); - return hostkey; } diff --git a/svr-session.c b/svr-session.c index e63ba32ddef68f7c50e451db9994f3870825b354..f37e81bfa261643a3dbc12a854ba84db550d39f9 100644 --- a/svr-session.c +++ b/svr-session.c @@ -35,10 +35,9 @@ #include "channel.h" #include "chansession.h" #include "atomicio.h" -#include "tcpfwd-direct.h" +#include "tcpfwd.h" #include "service.h" #include "auth.h" -#include "tcpfwd-remote.h" #include "runopts.h" static void svr_remoteclosed(); @@ -65,7 +64,7 @@ static const packettype svr_packettypes[] = { static const struct ChanType *svr_chantypes[] = { &svrchansess, - &chan_tcpdirect, + &svr_chan_tcpdirect, NULL /* Null termination is mandatory. */ }; @@ -169,7 +168,7 @@ void svr_dropbear_log(int priority, const char* format, va_list param) { /* if we are using DEBUG_TRACE, we want to print to stderr even if * syslog is used, so it is included in error reports */ #ifdef DEBUG_TRACE - havetrace = 1; + havetrace = debug_trace; #endif if (!svr_opts.usingsyslog || havetrace) diff --git a/svr-tcpfwd.c b/svr-tcpfwd.c new file mode 100644 index 0000000000000000000000000000000000000000..7e2ee8b0acc5abbb50ba73c60e2fb73cd971d847 --- /dev/null +++ b/svr-tcpfwd.c @@ -0,0 +1,291 @@ +/* + * Dropbear SSH + * + * Copyright (c) 2002,2003 Matt Johnston + * Copyright (c) 2004 by Mihnea Stoenescu + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + +#include "includes.h" +#include "ssh.h" +#include "tcpfwd.h" +#include "dbutil.h" +#include "session.h" +#include "buffer.h" +#include "packet.h" +#include "listener.h" +#include "runopts.h" + +#ifndef DISABLE_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 */ +}; + +static const struct ChanType svr_chan_tcpremote = { + 1, /* sepfds */ + "forwarded-tcpip", + NULL, + NULL, + NULL, + NULL +}; + +/* At the moment this is completely used for tcp code (with the name reflecting + * that). If new request types are added, this should be replaced with code + * similar to the request-switching in chansession.c */ +void recv_msg_global_request_remotetcp() { + + unsigned char* reqname = NULL; + unsigned int namelen; + unsigned int wantreply = 0; + int ret = DROPBEAR_FAILURE; + + TRACE(("enter recv_msg_global_request_remotetcp")); + + if (opts.noremotetcp) { + TRACE(("leave recv_msg_global_request_remotetcp: remote tcp forwarding disabled")); + goto out; + } + + reqname = buf_getstring(ses.payload, &namelen); + wantreply = buf_getbyte(ses.payload); + + if (namelen > MAXNAMLEN) { + TRACE(("name len is wrong: %d", namelen)); + goto out; + } + + if (strcmp("tcpip-forward", reqname) == 0) { + ret = svr_remotetcpreq(); + } else if (strcmp("cancel-tcpip-forward", reqname) == 0) { + ret = svr_cancelremotetcp(); + } else { + TRACE(("reqname isn't tcpip-forward: '%s'", reqname)); + } + +out: + if (wantreply) { + if (ret == DROPBEAR_SUCCESS) { + send_msg_request_success(); + } else { + send_msg_request_failure(); + } + } + + m_free(reqname); + + TRACE(("leave recv_msg_global_request")); +} + + +static void send_msg_request_success() { + + CHECKCLEARTOWRITE(); + buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_SUCCESS); + encrypt_packet(); + +} + +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; + const struct TCPListener *info2 = (struct TCPListener*)typedata2; + + return (info1->sendport == info2->sendport) + && (info1->chantype == info2->chantype) + && (strcmp(info1->sendaddr, info2->sendaddr) == 0); +} + +static int svr_cancelremotetcp() { + + int ret = DROPBEAR_FAILURE; + unsigned char * bindaddr = NULL; + unsigned int addrlen; + unsigned int port; + struct Listener * listener = NULL; + struct TCPListener tcpinfo; + + TRACE(("enter cancelremotetcp")); + + bindaddr = buf_getstring(ses.payload, &addrlen); + if (addrlen > MAX_IP_LEN) { + TRACE(("addr len too long: %d", addrlen)); + goto out; + } + + port = buf_getint(ses.payload); + + tcpinfo.sendaddr = bindaddr; + tcpinfo.sendport = port; + listener = get_listener(CHANNEL_ID_TCPFORWARDED, &tcpinfo, matchtcp); + if (listener) { + remove_listener( listener ); + ret = DROPBEAR_SUCCESS; + } + +out: + m_free(bindaddr); + TRACE(("leave cancelremotetcp")); + return ret; +} + +static int svr_remotetcpreq() { + + int ret = DROPBEAR_FAILURE; + unsigned char * bindaddr = NULL; + unsigned int addrlen; + struct TCPListener *tcpinfo = NULL; + unsigned int port; + + TRACE(("enter remotetcpreq")); + + /* NOTE: at this stage, we ignore bindaddr. see below and listen_tcpfwd */ + bindaddr = buf_getstring(ses.payload, &addrlen); + if (addrlen > MAX_IP_LEN) { + TRACE(("addr len too long: %d", addrlen)); + goto out; + } + + port = buf_getint(ses.payload); + + if (port == 0) { + dropbear_log(LOG_INFO, "Server chosen tcpfwd ports are unsupported"); + goto out; + } + + if (port < 1 || port > 65535) { + TRACE(("invalid port: %d", port)); + goto out; + } + + if (!ses.allowprivport && port < IPPORT_RESERVED) { + TRACE(("can't assign port < 1024 for non-root")); + goto out; + } + + tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener)); + tcpinfo->sendaddr = bindaddr; + tcpinfo->sendport = port; + tcpinfo->listenport = port; + tcpinfo->chantype = &svr_chan_tcpremote; + + /* Note: bindaddr is actually ignored by listen_tcpfwd, since + * we only want to bind to localhost */ + 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(tcpinfo->sendaddr); + m_free(tcpinfo); + } + TRACE(("leave remotetcpreq")); + return ret; +} + +/* Called upon creating a new direct tcp channel (ie we connect out to an + * address */ +static int newtcpdirect(struct Channel * channel) { + + unsigned char* desthost = NULL; + unsigned int destport; + unsigned char* orighost = NULL; + unsigned int origport; + char portstring[NI_MAXSERV]; + int sock; + int len; + int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED; + + if (opts.nolocaltcp) { + TRACE(("leave newtcpdirect: local tcp forwarding disabled")); + goto out; + } + + desthost = buf_getstring(ses.payload, &len); + if (len > MAX_HOST_LEN) { + TRACE(("leave newtcpdirect: desthost too long")); + goto out; + } + + destport = buf_getint(ses.payload); + + orighost = buf_getstring(ses.payload, &len); + if (len > MAX_HOST_LEN) { + TRACE(("leave newtcpdirect: orighost too long")); + goto out; + } + + origport = buf_getint(ses.payload); + + /* best be sure */ + if (origport > 65535 || destport > 65535) { + TRACE(("leave newtcpdirect: port > 65535")); + goto out; + } + + snprintf(portstring, sizeof(portstring), "%d", destport); + sock = connect_remote(desthost, portstring, 1, NULL); + if (sock < 0) { + err = SSH_OPEN_CONNECT_FAILED; + TRACE(("leave newtcpdirect: sock failed")); + goto out; + } + + ses.maxfd = MAX(ses.maxfd, sock); + + /* Note that infd is actually the "outgoing" direction on the + * tcp connection, vice versa for outfd. + * We don't set outfd, that will get set after the connection's + * progress succeeds */ + channel->infd = sock; + channel->initconn = 1; + + err = SSH_OPEN_IN_PROGRESS; + +out: + m_free(desthost); + m_free(orighost); + TRACE(("leave newtcpdirect: err %d", err)); + return err; +} + +#endif diff --git a/svr-x11fwd.c b/svr-x11fwd.c index aa0ba2dba34fec58dd79dbc395cbb5ca01466d30..45c9060cf0baca562184695475bc9d433f011dd4 100644 --- a/svr-x11fwd.c +++ b/svr-x11fwd.c @@ -37,7 +37,7 @@ #define X11BASEPORT 6000 #define X11BINDBASE 6010 -static void x11accept(struct Listener* listener); +static void x11accept(struct Listener* listener, int sock); static int bindport(int fd); static int send_msg_channel_open_x11(int fd, struct sockaddr_in* addr); @@ -75,14 +75,12 @@ int x11req(struct ChanSess * chansess) { } /* set non-blocking */ - if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { - goto fail; - } + setnonblocking(fd); /* listener code will handle the socket now. * No cleanup handler needed, since listener_remove only happens * from our cleanup anyway */ - chansess->x11listener = new_listener( fd, 0, chansess, x11accept, NULL); + chansess->x11listener = new_listener( &fd, 1, 0, chansess, x11accept, NULL); if (chansess->x11listener == NULL) { goto fail; } @@ -100,7 +98,7 @@ fail: /* accepts a new X11 socket */ /* returns DROPBEAR_FAILURE or DROPBEAR_SUCCESS */ -static void x11accept(struct Listener* listener) { +static void x11accept(struct Listener* listener, int sock) { int fd; struct sockaddr_in addr; @@ -110,7 +108,7 @@ static void x11accept(struct Listener* listener) { len = sizeof(addr); - fd = accept(listener->sock, (struct sockaddr*)&addr, &len); + fd = accept(sock, (struct sockaddr*)&addr, &len); if (fd < 0) { return; } @@ -131,7 +129,7 @@ static void x11accept(struct Listener* listener) { void x11setauth(struct ChanSess *chansess) { char display[20]; /* space for "localhost:12345.123" */ - FILE * authprog; + FILE * authprog = NULL; int val; if (chansess->x11listener == NULL) { @@ -171,8 +169,12 @@ void x11cleanup(struct ChanSess *chansess) { m_free(chansess->x11authprot); m_free(chansess->x11authcookie); - remove_listener(chansess->x11listener); - chansess->x11listener = NULL; + + TRACE(("chansess %s", chansess)); + if (chansess->x11listener != NULL) { + remove_listener(chansess->x11listener); + chansess->x11listener = NULL; + } } static const struct ChanType chan_x11 = { @@ -187,7 +189,7 @@ static const struct ChanType chan_x11 = { static int send_msg_channel_open_x11(int fd, struct sockaddr_in* addr) { - char* ipstring; + char* ipstring = NULL; if (send_msg_channel_open_init(fd, &chan_x11) == DROPBEAR_SUCCESS) { ipstring = inet_ntoa(addr->sin_addr); diff --git a/tcp-accept.c b/tcp-accept.c new file mode 100644 index 0000000000000000000000000000000000000000..c143319d6931ccd61e1ebcd347c63227375018c9 --- /dev/null +++ b/tcp-accept.c @@ -0,0 +1,120 @@ +/* + * Dropbear SSH + * + * Copyright (c) 2002,2003 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + +#include "includes.h" +#include "ssh.h" +#include "tcpfwd.h" +#include "dbutil.h" +#include "session.h" +#include "buffer.h" +#include "packet.h" +#include "listener.h" +#include "runopts.h" + +#ifdef DROPBEAR_TCP_ACCEPT + +static void cleanup_tcp(struct Listener *listener) { + + struct TCPListener *tcpinfo = (struct TCPListener*)(listener->typedata); + + m_free(tcpinfo->sendaddr); + m_free(tcpinfo); +} + +static void tcp_acceptor(struct Listener *listener, int sock) { + + int fd; + struct sockaddr_storage addr; + int len; + char ipstring[NI_MAXHOST], portstring[NI_MAXSERV]; + struct TCPListener *tcpinfo = (struct TCPListener*)(listener->typedata); + + len = sizeof(addr); + + fd = accept(sock, (struct sockaddr*)&addr, &len); + if (fd < 0) { + return; + } + + if (getnameinfo((struct sockaddr*)&addr, len, ipstring, sizeof(ipstring), + portstring, sizeof(portstring), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) { + return; + } + + if (send_msg_channel_open_init(fd, tcpinfo->chantype) == DROPBEAR_SUCCESS) { + + buf_putstring(ses.writepayload, tcpinfo->sendaddr, + strlen(tcpinfo->sendaddr)); + buf_putint(ses.writepayload, tcpinfo->sendport); + buf_putstring(ses.writepayload, ipstring, strlen(ipstring)); + buf_putint(ses.writepayload, atol(portstring)); + + encrypt_packet(); + + } else { + /* XXX debug? */ + close(fd); + } +} + +int listen_tcpfwd(struct TCPListener* tcpinfo) { + + char portstring[NI_MAXSERV]; + int socks[DROPBEAR_MAX_SOCKS]; + struct Listener *listener = NULL; + int nsocks; + char* errstring = 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); + + /* XXX Note: we're just listening on localhost, no matter what they tell + * us. If someone wants to make it listen otherways, then change + * the "" argument. but that requires UI changes too */ + nsocks = dropbear_listen("", portstring, socks, + DROPBEAR_MAX_SOCKS, &errstring, &ses.maxfd); + if (nsocks < 0) { + dropbear_log(LOG_INFO, "TCP forward failed: %s", errstring); + m_free(errstring); + TRACE(("leave listen_tcpfwd: dropbear_listen failed")); + return DROPBEAR_FAILURE; + } + + listener = new_listener(socks, nsocks, CHANNEL_ID_TCPFORWARDED, tcpinfo, + tcp_acceptor, cleanup_tcp); + + if (listener == NULL) { + m_free(tcpinfo); + TRACE(("leave listen_tcpfwd: listener failed")); + return DROPBEAR_FAILURE; + } + + TRACE(("leave listen_tcpfwd: success")); + return DROPBEAR_SUCCESS; +} + +#endif /* DROPBEAR_TCP_ACCEPT */ diff --git a/tcpfwd-direct.c b/tcpfwd-direct.c deleted file mode 100644 index b6112839201edc5e09cc62551096ce3b35c3020a..0000000000000000000000000000000000000000 --- a/tcpfwd-direct.c +++ /dev/null @@ -1,159 +0,0 @@ -#include "includes.h" -#include "session.h" -#include "dbutil.h" -#include "channel.h" -#include "tcpfwd-direct.h" -#include "runopts.h" - -#ifndef DISABLE_TCPFWD_DIRECT -static int newtcpdirect(struct Channel * channel); -static int newtcp(const char * host, int port); - -const struct ChanType 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) { - - unsigned char* desthost = NULL; - unsigned int destport; - unsigned char* orighost = NULL; - unsigned int origport; - char portstring[6]; - int sock; - int len; - int ret = DROPBEAR_FAILURE; - - if (opts.nolocaltcp) { - TRACE(("leave newtcpdirect: local tcp forwarding disabled")); - goto out; - } - - desthost = buf_getstring(ses.payload, &len); - if (len > MAX_HOST_LEN) { - TRACE(("leave newtcpdirect: desthost too long")); - goto out; - } - - destport = buf_getint(ses.payload); - - orighost = buf_getstring(ses.payload, &len); - if (len > MAX_HOST_LEN) { - TRACE(("leave newtcpdirect: orighost too long")); - goto out; - } - - origport = buf_getint(ses.payload); - - /* best be sure */ - if (origport > 65535 || destport > 65535) { - TRACE(("leave newtcpdirect: port > 65535")); - goto out; - } - - snprintf(portstring, sizeof(portstring), "%d", destport); - sock = connect_remote(desthost, portstring, 1, NULL); - if (sock < 0) { - TRACE(("leave newtcpdirect: sock failed")); - goto out; - } - - ses.maxfd = MAX(ses.maxfd, sock); - - /* Note that infd is actually the "outgoing" direction on the - * tcp connection, vice versa for outfd. - * We don't set outfd, that will get set after the connection's - * progress succeeds */ - channel->infd = sock; - channel->initconn = 1; - - ret = DROPBEAR_SUCCESS; - -out: - m_free(desthost); - m_free(orighost); - TRACE(("leave newtcpdirect: ret %d", ret)); - return ret; -} - -/* Initiate a new TCP connection - this is non-blocking, so the socket - * returned will need to be checked for success when it is first written. - * Similarities with OpenSSH's connect_to() are not coincidental. - * Returns -1 on failure */ -#if 0 -static int newtcp(const char * host, int port) { - - int sock = -1; - char portstring[6]; - struct addrinfo *res = NULL, *ai; - int val; - - struct addrinfo hints; - - TRACE(("enter newtcp")); - - memset(&hints, 0, sizeof(hints)); - /* TCP, either ip4 or ip6 */ - hints.ai_socktype = SOCK_STREAM; - hints.ai_family = PF_UNSPEC; - - snprintf(portstring, sizeof(portstring), "%d", port); - if (getaddrinfo(host, portstring, &hints, &res) != 0) { - if (res) { - freeaddrinfo(res); - } - TRACE(("leave newtcp: failed getaddrinfo")); - return -1; - } - - /* Use the first socket that works */ - for (ai = res; ai != NULL; ai = ai->ai_next) { - - if (ai->ai_family != PF_INET && ai->ai_family != PF_INET6) { - continue; - } - - sock = socket(ai->ai_family, SOCK_STREAM, 0); - if (sock < 0) { - TRACE(("TCP socket() failed")); - continue; - } - - if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { - close(sock); - TRACE(("TCP non-blocking failed")); - continue; - } - - /* non-blocking, so it might return without success (EINPROGRESS) */ - if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { - if (errno == EINPROGRESS) { - TRACE(("connect in progress")); - } else { - close(sock); - TRACE(("TCP connect failed")); - continue; - } - } - break; - } - - freeaddrinfo(res); - - if (ai == NULL) { - return -1; - } - - setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&val, sizeof(val)); - return sock; -} -#endif -#endif /* DISABLE_TCPFWD_DIRECT */ diff --git a/tcpfwd-remote.c b/tcpfwd-remote.c deleted file mode 100644 index d0a67a1420c97704ea080a368f4e19980fe9abab..0000000000000000000000000000000000000000 --- a/tcpfwd-remote.c +++ /dev/null @@ -1,317 +0,0 @@ -#include "includes.h" -#include "ssh.h" -#include "tcpfwd-remote.h" -#include "dbutil.h" -#include "session.h" -#include "buffer.h" -#include "packet.h" -#include "listener.h" -#include "runopts.h" - -#ifndef DISABLE_REMOTETCPFWD - -struct RemoteTCP { - - unsigned char* addr; - unsigned int port; - -}; - -static void send_msg_request_success(); -static void send_msg_request_failure(); -static int cancelremotetcp(); -static int remotetcpreq(); -static int listen_tcpfwd(unsigned char* bindaddr, unsigned int port); -static void acceptremote(struct Listener *listener); - -/* At the moment this is completely used for tcp code (with the name reflecting - * that). If new request types are added, this should be replaced with code - * similar to the request-switching in chansession.c */ -void recv_msg_global_request_remotetcp() { - - unsigned char* reqname = NULL; - unsigned int namelen; - unsigned int wantreply = 0; - int ret = DROPBEAR_FAILURE; - - TRACE(("enter recv_msg_global_request_remotetcp")); - - if (opts.noremotetcp) { - TRACE(("leave recv_msg_global_request_remotetcp: remote tcp forwarding disabled")); - goto out; - } - - reqname = buf_getstring(ses.payload, &namelen); - wantreply = buf_getbyte(ses.payload); - - if (namelen > MAXNAMLEN) { - TRACE(("name len is wrong: %d", namelen)); - goto out; - } - - if (strcmp("tcpip-forward", reqname) == 0) { - ret = remotetcpreq(); - } else if (strcmp("cancel-tcpip-forward", reqname) == 0) { - ret = cancelremotetcp(); - } else { - TRACE(("reqname isn't tcpip-forward: '%s'", reqname)); - } - -out: - if (wantreply) { - if (ret == DROPBEAR_SUCCESS) { - send_msg_request_success(); - } else { - send_msg_request_failure(); - } - } - - m_free(reqname); - - TRACE(("leave recv_msg_global_request")); -} - -static const struct ChanType chan_tcpremote = { - 1, /* sepfds */ - "forwarded-tcpip", - NULL, - NULL, - NULL, - NULL -}; - - -static void acceptremote(struct Listener *listener) { - - int fd; - struct sockaddr addr; - int len; - char ipstring[NI_MAXHOST], portstring[NI_MAXSERV]; - struct RemoteTCP *tcpinfo = (struct RemoteTCP*)(listener->typedata); - - len = sizeof(addr); - - fd = accept(listener->sock, &addr, &len); - if (fd < 0) { - return; - } - - if (getnameinfo(&addr, len, ipstring, sizeof(ipstring), portstring, - sizeof(portstring), NI_NUMERICHOST | NI_NUMERICSERV) != 0) { - return; - } - - if (send_msg_channel_open_init(fd, &chan_tcpremote) == DROPBEAR_SUCCESS) { - - buf_putstring(ses.writepayload, tcpinfo->addr, - strlen(tcpinfo->addr)); - buf_putint(ses.writepayload, tcpinfo->port); - buf_putstring(ses.writepayload, ipstring, strlen(ipstring)); - buf_putint(ses.writepayload, atol(portstring)); - encrypt_packet(); - - } else { - /* XXX debug? */ - close(fd); - } -} - -static void cleanupremote(struct Listener *listener) { - - struct RemoteTCP *tcpinfo = (struct RemoteTCP*)(listener->typedata); - - m_free(tcpinfo->addr); - m_free(tcpinfo); -} - -static void send_msg_request_success() { - - CHECKCLEARTOWRITE(); - buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_SUCCESS); - encrypt_packet(); - -} - -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 RemoteTCP *info1 = (struct RemoteTCP*)typedata1; - const struct RemoteTCP *info2 = (struct RemoteTCP*)typedata2; - - return info1->port == info2->port - && (strcmp(info1->addr, info2->addr) == 0); -} - -static int cancelremotetcp() { - - int ret = DROPBEAR_FAILURE; - unsigned char * bindaddr = NULL; - unsigned int addrlen; - unsigned int port; - struct Listener * listener = NULL; - struct RemoteTCP tcpinfo; - - TRACE(("enter cancelremotetcp")); - - bindaddr = buf_getstring(ses.payload, &addrlen); - if (addrlen > MAX_IP_LEN) { - TRACE(("addr len too long: %d", addrlen)); - goto out; - } - - port = buf_getint(ses.payload); - - tcpinfo.addr = bindaddr; - tcpinfo.port = port; - listener = get_listener(CHANNEL_ID_TCPFORWARDED, &tcpinfo, matchtcp); - if (listener) { - remove_listener( listener ); - ret = DROPBEAR_SUCCESS; - } - -out: - m_free(bindaddr); - TRACE(("leave cancelremotetcp")); - return ret; -} - -static int remotetcpreq() { - - int ret = DROPBEAR_FAILURE; - unsigned char * bindaddr = NULL; - unsigned int addrlen; - unsigned int port; - - TRACE(("enter remotetcpreq")); - - bindaddr = buf_getstring(ses.payload, &addrlen); - if (addrlen > MAX_IP_LEN) { - TRACE(("addr len too long: %d", addrlen)); - goto out; - } - - port = buf_getint(ses.payload); - - if (port == 0) { - dropbear_log(LOG_INFO, "Server chosen tcpfwd ports are unsupported"); - goto out; - } - - if (port < 1 || port > 65535) { - TRACE(("invalid port: %d", port)); - goto out; - } - - if (!ses.allowprivport && port < IPPORT_RESERVED) { - TRACE(("can't assign port < 1024 for non-root")); - goto out; - } - - ret = listen_tcpfwd(bindaddr, port); - -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); - } - TRACE(("leave remotetcpreq")); - return ret; -} - -static int listen_tcpfwd(unsigned char* bindaddr, unsigned int port) { - - struct RemoteTCP * tcpinfo = NULL; - char portstring[6]; /* "65535\0" */ - struct addrinfo *res = NULL, *ai = NULL; - struct addrinfo hints; - int sock = -1; - struct Listener *listener = 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", port); - memset(&hints, 0x0, sizeof(hints)); - hints.ai_socktype = SOCK_STREAM; - hints.ai_family = PF_INET; - hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; - - if (getaddrinfo(bindaddr, portstring, &hints, &res) < 0) { - TRACE(("leave listen_tcpfwd: getaddrinfo failed: %s", - strerror(errno))); - goto done; - } - - /* find the first one which works */ - for (ai = res; ai != NULL; ai = ai->ai_next) { - if (ai->ai_family != PF_INET && ai->ai_family != PF_INET6) { - continue; - } - - sock = socket(ai->ai_family, SOCK_STREAM, 0); - if (sock < 0) { - TRACE(("socket failed: %s", strerror(errno))); - goto fail; - } - - if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { - TRACE(("bind failed: %s", strerror(errno))); - goto fail; - } - - if (listen(sock, 20) < 0) { - TRACE(("listen failed: %s", strerror(errno))); - goto fail; - } - - if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { - TRACE(("fcntl nonblocking failed: %s", strerror(errno))); - goto fail; - } - - /* success */ - break; - -fail: - close(sock); - } - - - if (ai == NULL) { - TRACE(("no successful sockets")); - goto done; - } - - tcpinfo = (struct RemoteTCP*)m_malloc(sizeof(struct RemoteTCP)); - tcpinfo->addr = bindaddr; - tcpinfo->port = port; - - listener = new_listener(sock, CHANNEL_ID_TCPFORWARDED, tcpinfo, - acceptremote, cleanupremote); - - if (listener == NULL) { - m_free(tcpinfo); - } - -done: - if (res) { - freeaddrinfo(res); - } - - TRACE(("leave listen_tcpfwd")); - if (listener == NULL) { - return DROPBEAR_FAILURE; - } else { - return DROPBEAR_SUCCESS; - } -} - -#endif /* DISABLE_REMOTETCPFWD */ diff --git a/tcpfwd-remote.h b/tcpfwd-remote.h deleted file mode 100644 index 64dbed34562985b0d1aa4d530f4b6230c5bc7883..0000000000000000000000000000000000000000 --- a/tcpfwd-remote.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _REMOTETCPFWD_H -#define _REMOTETCPFWD_H - -void recv_msg_global_request_remotetcp(); - -#endif /* _REMOTETCPFWD_H */ diff --git a/authpasswd.h b/tcpfwd.h similarity index 53% rename from authpasswd.h rename to tcpfwd.h index 97385327470d3ab052f5be6519a04aeacba140fb..504a8d3a61ad647041fa9e20148820763cd1694c 100644 --- a/authpasswd.h +++ b/tcpfwd.h @@ -21,13 +21,50 @@ * 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. */ +#ifndef _TCPFWD_H +#define _TCPFWD_H -#ifndef _AUTH_PASSWD_ -#define _AUTH_PASSWD_ +#include "channel.h" -#ifdef DROPBEAR_PASSWORD_AUTH +struct TCPListener { -void passwordauth(); + /* sendaddr/sendport are what we send in the channel init request. For a + * forwarded-tcpip request, it's the addr/port we were binding to. + * For a direct-tcpip request, it's the addr/port we want the other + * end to connect to */ + + unsigned char *sendaddr; + unsigned int sendport; -#endif /* DROPBEAR_PASSWORD_AUTH */ -#endif /* _AUTH_PASSWD_ */ + /* This is for direct-tcpip (ie the client listening), and specifies the + * port to listen on. Is unspecified for the server */ + unsigned int listenport; + + const struct ChanType *chantype; + +}; + +/* A link in a list of forwards */ +struct TCPFwdList { + + const unsigned char* connectaddr; + unsigned int connectport; + unsigned int listenport; + struct TCPFwdList * next; + +}; + +/* Server */ +void recv_msg_global_request_remotetcp(); +extern const struct ChanType svr_chan_tcpdirect; + +/* Client */ +void setup_localtcp(); +void setup_remotetcp(); +extern const struct ChanType cli_chan_tcpremote; + +/* Common */ +int listen_tcpfwd(struct TCPListener* tcpinfo); + + +#endif diff --git a/termcodes.c b/termcodes.c index 78c3dd8171075a8eb9bff429b33a5a30b68c7056..d59505cfbacbf5f361d3f7a3b7a120c673651b73 100644 --- a/termcodes.c +++ b/termcodes.c @@ -131,7 +131,11 @@ const struct TermCode termcodes[MAX_TERMCODE+1] = { {IEXTEN, TERMCODE_LOCAL}, {ECHOCTL, TERMCODE_LOCAL}, {ECHOKE, TERMCODE_LOCAL}, +#ifdef PENDIN {PENDIN, TERMCODE_LOCAL}, +#else + {0, 0}, +#endif {0, 0}, /* 63 */ {0, 0}, {0, 0},