# HG changeset patch # User Matt Johnston # Date 1094965010 0 # Node ID 5312ca05ed48ad375401c82cca2c4b4844fe0022 # Parent 3b2a5a1c434756146fd6aa15dde5130a229e19a9# Parent e0acad552a929da306eae8ba1385828fd4ef58fa propagate of 717950f4061f1123659ee87c7c168805af920ab7 and 839f98f136788cc1466e4641bf796f96040a085d from branch 'matt.dbclient.authpam' to 'matt.dbclient.rez' diff -r 3b2a5a1c4347 -r 5312ca05ed48 CHANGES --- a/CHANGES Sun Aug 08 16:57:37 2004 +0000 +++ b/CHANGES Sun Sep 12 04:56:50 2004 +0000 @@ -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 rather than 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 -r 3b2a5a1c4347 -r 5312ca05ed48 INSTALL --- a/INSTALL Sun Aug 08 16:57:37 2004 +0000 +++ b/INSTALL Sun Sep 12 04:56:50 2004 +0000 @@ -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: +Now compile: -(the Makefile requires GNU make, if you want to make it portable, send me - some patches) +make PROGRAMS="dropbear dbclient dropbearkey dropbearconvert scp" -make -make install (installs to /usr/local/sbin, /usr/local/bin by default) +And install (/usr/local/bin is usual default): -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 +make PROGRAMS="dropbear dbclient dropbearkey dropbearconvert scp" install -And you can now run the server. -./dropbear - -or './dropbear -h' to get options. +(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) -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. +See MULTI for instructions on making all-in-one binaries. -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". +If you want to compile statically, add "STATIC=1" to the make command-line. + +Binaries can be strippd with "make strip" ============================================================================ @@ -50,18 +35,11 @@ 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 @@ 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 @@ 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 -r 3b2a5a1c4347 -r 5312ca05ed48 LICENSE --- a/LICENSE Sun Aug 08 16:57:37 2004 +0000 +++ b/LICENSE Sun Sep 12 04:56:50 2004 +0000 @@ -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 @@ ===== -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 -r 3b2a5a1c4347 -r 5312ca05ed48 MULTI --- a/MULTI Sun Aug 08 16:57:37 2004 +0000 +++ b/MULTI Sun Sep 12 04:56:50 2004 +0000 @@ -3,29 +3,24 @@ 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 - -Then enable which of the binaries you want to compile, also in options.h -(by default these are all enabled). +To compile the multi-binary, first "make clean" (if you've compiled +previously), then -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 -"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 -r 3b2a5a1c4347 -r 5312ca05ed48 Makefile.in --- a/Makefile.in Sun Aug 08 16:57:37 2004 +0000 +++ b/Makefile.in Sun Sep 12 04:56:50 2004 +0000 @@ -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 @@ 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-authpam.o svr-chansession.o svr-runopts.o svr-agentfwd.o svr-main.o svr-x11fwd.o\ - svr-authpam.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 @@ 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 @@ 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 @@ 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 @@ 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 -r 3b2a5a1c4347 -r 5312ca05ed48 README --- a/README Sun Aug 08 16:57:37 2004 +0000 +++ b/README Sun Sep 12 04:56:50 2004 +0000 @@ -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 @@ 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 + +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 -r 3b2a5a1c4347 -r 5312ca05ed48 TODO --- a/TODO Sun Aug 08 16:57:37 2004 +0000 +++ b/TODO Sun Sep 12 04:56:50 2004 +0000 @@ -1,29 +1,29 @@ Current: -Things which need doing: +Things which might need doing: - Make options.h generated from configure perhaps? -- investigate self-pipe? -- fix agent fwd problems -- improve channel window adjustment algorithm (circular buffering) +- Improved queueing of unauthed connections -- Don't use pregenerated AES tables +- fix agent fwd problems -- check PRNG +- handle /etc/environment in AIX + - 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 -r 3b2a5a1c4347 -r 5312ca05ed48 authpasswd.h --- a/authpasswd.h Sun Aug 08 16:57:37 2004 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -/* - * Dropbear - a SSH2 server - * - * Copyright (c) 2002,2003 Matt Johnston - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ - -#ifndef _AUTH_PASSWD_ -#define _AUTH_PASSWD_ - -#ifdef DROPBEAR_PASSWORD_AUTH - -void passwordauth(); - -#endif /* DROPBEAR_PASSWORD_AUTH */ -#endif /* _AUTH_PASSWD_ */ diff -r 3b2a5a1c4347 -r 5312ca05ed48 authpubkey.h --- a/authpubkey.h Sun Aug 08 16:57:37 2004 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -/* - * Dropbear - a SSH2 server - * - * Copyright (c) 2002,2003 Matt Johnston - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ - -#ifndef _PUBKEY_AUTH_ -#define _PUBKEY_AUTH_ - -#ifdef DROPBEAR_PUBKEY_AUTH - -void pubkeyauth(); - -#endif /* DROPBEAR_PUBKEY_AUTH */ -#endif /* _PUBKEY_AUTH_ */ diff -r 3b2a5a1c4347 -r 5312ca05ed48 buffer.c --- a/buffer.c Sun Aug 08 16:57:37 2004 +0000 +++ b/buffer.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 @@ } -/* 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 @@ /* 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 -r 3b2a5a1c4347 -r 5312ca05ed48 channel.h --- a/channel.h Sun Aug 08 16:57:37 2004 +0000 +++ b/channel.h Sun Sep 12 04:56:50 2004 +0000 @@ -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 @@ 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 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 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 -r 3b2a5a1c4347 -r 5312ca05ed48 chansession.h --- a/chansession.h Sun Aug 08 16:57:37 2004 +0000 +++ b/chansession.h Sun Sep 12 04:56:50 2004 +0000 @@ -68,11 +68,6 @@ }; -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 -r 3b2a5a1c4347 -r 5312ca05ed48 circbuffer.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/circbuffer.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 -r 3b2a5a1c4347 -r 5312ca05ed48 circbuffer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/circbuffer.h Sun Sep 12 04:56:50 2004 +0000 @@ -0,0 +1,50 @@ +/* + * 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. */ + +#ifndef _CIRCBUFFER_H_ +#define _CIRCBUFFER_H_ +struct circbuf { + + unsigned int size; + unsigned int readpos; + unsigned int writepos; + unsigned int used; + unsigned char* data; +}; + +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 -r 3b2a5a1c4347 -r 5312ca05ed48 cli-auth.c --- a/cli-auth.c Sun Aug 08 16:57:37 2004 +0000 +++ b/cli-auth.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 @@ 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 @@ 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 @@ } } + m_free(methods); + cli_ses.state = USERAUTH_FAIL_RCVD; TRACE(("leave recv_msg_userauth_failure")); @@ -163,14 +190,14 @@ 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 -r 3b2a5a1c4347 -r 5312ca05ed48 cli-authpasswd.c --- a/cli-authpasswd.c Sun Aug 08 16:57:37 2004 +0000 +++ b/cli-authpasswd.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 @@ return 1; /* Password auth can always be tried */ } +#endif diff -r 3b2a5a1c4347 -r 5312ca05ed48 cli-authpubkey.c --- a/cli-authpubkey.c Sun Aug 08 16:57:37 2004 +0000 +++ b/cli-authpubkey.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 @@ 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 @@ /* 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 @@ return 0; } } +#endif /* Pubkey auth */ diff -r 3b2a5a1c4347 -r 5312ca05ed48 cli-channel.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cli-channel.c Sun Sep 12 04:56:50 2004 +0000 @@ -0,0 +1,65 @@ +/* + * 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 "channel.h" +#include "buffer.h" +#include "circbuffer.h" +#include "dbutil.h" +#include "session.h" +#include "ssh.h" + +/* We receive channel data - only used by the client chansession code*/ +void recv_msg_channel_extended_data() { + + unsigned int chan; + struct Channel *channel; + unsigned int datatype; + + 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 -r 3b2a5a1c4347 -r 5312ca05ed48 cli-chansession.c --- a/cli-chansession.c Sun Aug 08 16:57:37 2004 +0000 +++ b/cli-chansession.c Sun Sep 12 04:56:50 2004 +0000 @@ -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_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 @@ 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 sigwinch_handler(int dummy) { +static void sigwinch_handler(int UNUSED(unused)) { cli_ses.winchange = 1; @@ -288,9 +339,17 @@ 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 -r 3b2a5a1c4347 -r 5312ca05ed48 cli-kex.c --- a/cli-kex.c Sun Aug 08 16:57:37 2004 +0000 +++ b/cli-kex.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 @@ 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); - 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 @@ /* 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 @@ 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 @@ 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 @@ 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 @@ /* 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 @@ 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 @@ /* 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 -r 3b2a5a1c4347 -r 5312ca05ed48 cli-main.c --- a/cli-main.c Sun Aug 08 16:57:37 2004 +0000 +++ b/cli-main.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 @@ 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 @@ 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 -r 3b2a5a1c4347 -r 5312ca05ed48 cli-runopts.c --- a/cli-runopts.c Sun Aug 08 16:57:37 2004 +0000 +++ b/cli-runopts.c Sun Sep 12 04:56:50 2004 +0000 @@ -28,26 +28,40 @@ #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 \n" "-t Allocate a pty\n" "-T Don't allocate a pty\n" -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH "-i (multiple allowed)\n" #endif +#ifdef ENABLE_CLI_LOCALTCPFWD + "-L Local port forwarding\n" +#endif +#ifdef ENABLE_CLI_REMOTETCPFWD + "-R Remote port forwarding\n" +#endif + "-l \n" +#ifdef DEBUG_TRACE + "-v verbose\n" +#endif ,DROPBEAR_VERSION, cli_opts.progname); } @@ -56,11 +70,16 @@ 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,9 +88,15 @@ 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; /* not yet @@ -81,7 +106,7 @@ /* 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]); @@ -89,6 +114,22 @@ 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 */ *next = argv[i]; @@ -106,7 +147,7 @@ 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 @@ case 'T': /* don't want a pty */ cli_opts.wantpty = 0; break; +#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_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, "Unknown argument '%s'\n", argv[i]); - printhelp(); - exit(EXIT_FAILURE); + 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 @@ } if (cli_opts.remotehost == NULL) { - dropbear_exit("Bad syntax"); + printhelp(); + exit(EXIT_FAILURE); } if (cli_opts.remoteport == NULL) { @@ -180,7 +263,7 @@ } } -#ifdef DROPBEAR_PUBKEY_AUTH +#ifdef ENABLE_CLI_PUBKEY_AUTH static void loadidentityfile(const char* filename) { struct PubkeyList * nextkey; @@ -208,10 +291,14 @@ /* 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 @@ 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 -r 3b2a5a1c4347 -r 5312ca05ed48 cli-service.c --- a/cli-service.c Sun Aug 08 16:57:37 2004 +0000 +++ b/cli-service.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 -r 3b2a5a1c4347 -r 5312ca05ed48 cli-session.c --- a/cli-session.c Sun Aug 08 16:57:37 2004 +0000 +++ b/cli-session.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 @@ /* 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 @@ {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 @@ {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 @@ 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 @@ */ 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 @@ 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 @@ 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 -r 3b2a5a1c4347 -r 5312ca05ed48 cli-tcpfwd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cli-tcpfwd.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 -r 3b2a5a1c4347 -r 5312ca05ed48 common-algo.c --- a/common-algo.c Sun Aug 08 16:57:37 2004 +0000 +++ b/common-algo.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 -r 3b2a5a1c4347 -r 5312ca05ed48 common-channel.c --- a/common-channel.c Sun Aug 08 16:57:37 2004 +0000 +++ b/common-channel.c Sun Sep 12 04:56:50 2004 +0000 @@ -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_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 @@ 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 @@ } /* 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 @@ 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 @@ } /* 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 @@ * 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 @@ 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 @@ /* 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 @@ 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 @@ 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 @@ 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 @@ 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); + + cbuf_incrread(cbuf, len); + channel->recvdonelen += len; + + if (fd == channel->infd && len == maxlen && channel->recveof) { + /* Check if we're closing up */ + closeinfd(channel); + TRACE(("leave writechannel: recveof set")); + return; + } - if (channel->recveof) { - /* we're closing up */ - closeinfd(channel); - return; - TRACE(("leave writechannel: recveof set")); - } + /* 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; + } - /* 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); - } + 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 @@ 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 @@ } 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 @@ 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 @@ 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 @@ } /* 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 @@ buf_putstring(ses.writepayload, buf_getptr(buf, len), len); buf_free(buf); + buf = NULL; channel->transwindow -= len; @@ -634,59 +663,72 @@ 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); - channel->recvwindow -= datalen; + memcpy(cbuf_writeptr(cbuf, buflen), + buf_getptr(ses.payload, buflen), buflen); + cbuf_incrwrite(cbuf, buflen); + buf_incrpos(ses.payload, buflen); + len -= buflen; + } - /* 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 >= datalen); + channel->recvwindow -= datalen; + assert(channel->recvwindow <= RECV_MAXWINDOW); TRACE(("leave recv_msg_channel_data")); } @@ -790,6 +832,10 @@ 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 @@ } } -#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 @@ } /* 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 @@ 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 -r 3b2a5a1c4347 -r 5312ca05ed48 common-kex.c --- a/common-kex.c Sun Aug 08 16:57:37 2004 +0000 +++ b/common-kex.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 @@ /* 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 @@ /* 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 -r 3b2a5a1c4347 -r 5312ca05ed48 common-session.c --- a/common-session.c Sun Aug 08 16:57:37 2004 +0000 +++ b/common-session.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 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 @@ 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 @@ 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 -r 3b2a5a1c4347 -r 5312ca05ed48 compat.c --- a/compat.c Sun Aug 08 16:57:37 2004 +0000 +++ b/compat.c Sun Sep 12 04:56:50 2004 +0000 @@ -190,7 +190,7 @@ #ifndef HAVE_BASENAME -char *basename(char *path) { +char *basename(const char *path) { char *foo = strrchr(path, '/'); return ++foo; diff -r 3b2a5a1c4347 -r 5312ca05ed48 compat.h --- a/compat.h Sun Aug 08 16:57:37 2004 +0000 +++ b/compat.h Sun Sep 12 04:56:50 2004 +0000 @@ -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 -r 3b2a5a1c4347 -r 5312ca05ed48 configure.in --- a/configure.in Sun Aug 08 16:57:37 2004 +0000 +++ b/configure.in Sun Sep 12 04:56:50 2004 +0000 @@ -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 @@ *-*-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 @@ [#include #include ]) +# 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 +#include + ], + [ 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 +#include + ], + [ 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 +#include + ], + [ 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 +#include +#include + ], + [ 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 +#include +#include + +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_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 @@ fi fi +AC_EXEEXT AC_CONFIG_HEADER(config.h) AC_OUTPUT(Makefile) AC_MSG_RESULT() diff -r 3b2a5a1c4347 -r 5312ca05ed48 dbmulti.c --- a/dbmulti.c Sun Aug 08 16:57:37 2004 +0000 +++ b/dbmulti.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 @@ } #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 @@ "'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 -r 3b2a5a1c4347 -r 5312ca05ed48 dbutil.c --- a/dbutil.c Sun Aug 08 16:57:37 2004 +0000 +++ b/dbutil.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 @@ 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 @@ va_list param; + if (!debug_trace) { + return; + } + va_start(param, format); fprintf(stderr, "TRACE: "); vfprintf(stderr, format, param); @@ -113,8 +150,114 @@ } #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 @@ } 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 @@ break; /* Success */ } - if (sock < 0) { + if (sock < 0 && !(errno == EINPROGRESS && nonblocking)) { /* Failed */ if (errstring != NULL && *errstring == NULL) { int len; @@ -197,58 +340,70 @@ } 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); + + ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf), + sbuf, sizeof(sbuf), NI_NUMERICSERV | NI_NUMERICHOST); - 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; + 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); + } - default: - /* XXX ipv6 */ - strcpy(retstring, "Bad protocol"); + 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; + + len = sizeof(struct sockaddr_storage); -#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; + 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 @@ * 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 @@ 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 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 -r 3b2a5a1c4347 -r 5312ca05ed48 dbutil.h --- a/dbutil.h Sun Aug 08 16:57:37 2004 +0000 +++ b/dbutil.h Sun Sep 12 04:56:50 2004 +0000 @@ -42,12 +42,15 @@ #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 @@ #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 -r 3b2a5a1c4347 -r 5312ca05ed48 debian/README.runit --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/README.runit Sun Sep 12 04:56:50 2004 +0000 @@ -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 , Sun, 16 May 2004 15:52:34 +0000 diff -r 3b2a5a1c4347 -r 5312ca05ed48 debian/changelog --- a/debian/changelog Sun Aug 08 16:57:37 2004 +0000 +++ b/debian/changelog Sun Sep 12 04:56:50 2004 +0000 @@ -1,3 +1,74 @@ +dropbear (0.44test3-1) unstable; urgency=medium + + * New upstream beta, various useful fixes. + + -- Matt Johnston Fri, 27 August 2004 22:20:00 +0800 + +dropbear (0.44test2-1) unstable; urgency=low + + * New upstream beta, various minor fixes. + + -- Matt Johnston 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 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 Fri, 16 July 2004 17:44:54 +0800 + +dropbear (0.42-1) unstable; urgency=low + + * New upstream release 0.42 + + -- Matt Johnston 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 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 Sun, 16 May 2004 16:50:55 +0000 + dropbear (0.41-1) unstable; urgency=low * Updated to 0.41 release. diff -r 3b2a5a1c4347 -r 5312ca05ed48 debian/compat --- a/debian/compat Sun Aug 08 16:57:37 2004 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -4 diff -r 3b2a5a1c4347 -r 5312ca05ed48 debian/conffiles diff -r 3b2a5a1c4347 -r 5312ca05ed48 debian/control --- a/debian/control Sun Aug 08 16:57:37 2004 +0000 +++ b/debian/control Sun Sep 12 04:56:50 2004 +0000 @@ -1,14 +1,20 @@ Source: dropbear Section: net -Priority: standard -Maintainer: Grahame Bowland -Build-Depends: debhelper (>> 4.0.0), zlib1g-dev -Standards-Version: 3.5.8 +Priority: optional +Maintainer: Gerrit Pape +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 -r 3b2a5a1c4347 -r 5312ca05ed48 debian/copyright.in --- a/debian/copyright.in Sun Aug 08 16:57:37 2004 +0000 +++ b/debian/copyright.in Sun Sep 12 04:56:50 2004 +0000 @@ -1,9 +1,11 @@ This package was debianized by Grahame Bowland on -Tue, 17 Jun 2003 15:04:47 +0800. +Tue, 17 Jun 2003 15:04:47 +0800, maintained temporarily by Matt Johnston +, and was adopted by Gerrit Pape on +Sun, 16 May 2004 14:38:33 +0000. It was downloaded from http://matt.ucc.asn.au/dropbear/ -Upstream Author(s): Matt Johnston +Upstream Author: Matt Johnston Copyright: diff -r 3b2a5a1c4347 -r 5312ca05ed48 debian/dirs --- a/debian/dirs Sun Aug 08 16:57:37 2004 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -usr/bin -usr/sbin diff -r 3b2a5a1c4347 -r 5312ca05ed48 debian/docs --- a/debian/docs Sun Aug 08 16:57:37 2004 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -README -TODO diff -r 3b2a5a1c4347 -r 5312ca05ed48 debian/dropbear.README.Debian --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/dropbear.README.Debian Sun Sep 12 04:56:50 2004 +0000 @@ -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 -r 3b2a5a1c4347 -r 5312ca05ed48 debian/dropbear.conffiles --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/dropbear.conffiles Sun Sep 12 04:56:50 2004 +0000 @@ -0,0 +1,3 @@ +/etc/init.d/dropbear +/etc/dropbear/run +/etc/dropbear/log/run diff -r 3b2a5a1c4347 -r 5312ca05ed48 debian/dropbear.docs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/dropbear.docs Sun Sep 12 04:56:50 2004 +0000 @@ -0,0 +1,3 @@ +README +TODO +debian/README.runit diff -r 3b2a5a1c4347 -r 5312ca05ed48 debian/dropbear.init --- a/debian/dropbear.init Sun Aug 08 16:57:37 2004 +0000 +++ b/debian/dropbear.init Sun Sep 12 04:56:50 2004 +0000 @@ -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 . -# Modified for Debian -# by Ian Murdock . -# -# 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 @@ 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 +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 -if [ -z "$DROPBEAR_DSSKEY" ]; then - DROPBEAR_DSSKEY="/etc/dropbear/dropbear_dss_host_key" -fi - -test "$NO_START" != "0" && 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 -r 3b2a5a1c4347 -r 5312ca05ed48 debian/dropbear.postinst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/dropbear.postinst Sun Sep 12 04:56:50 2004 +0000 @@ -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 <>/etc/default/dropbear <>/etc/default/dropbear </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 -r 3b2a5a1c4347 -r 5312ca05ed48 debian/dropbear.postrm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/dropbear.postrm Sun Sep 12 04:56:50 2004 +0000 @@ -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 -r 3b2a5a1c4347 -r 5312ca05ed48 debian/dropbear.prerm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/dropbear.prerm Sun Sep 12 04:56:50 2004 +0000 @@ -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 -r 3b2a5a1c4347 -r 5312ca05ed48 debian/implicit --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/implicit Sun Sep 12 04:56:50 2004 +0000 @@ -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 -r 3b2a5a1c4347 -r 5312ca05ed48 debian/postinst --- a/debian/postinst Sun Aug 08 16:57:37 2004 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,90 +0,0 @@ -#! /bin/sh -# postinst script for #PACKAGE# -# -# see: dh_installdeb(1) - -set -e - -# summary of how this script can be called: -# * `configure' -# * `abort-upgrade' -# * `abort-remove' `in-favour' -# -# * `abort-deconfigure' `in-favour' -# `removing' -# -# 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 -r 3b2a5a1c4347 -r 5312ca05ed48 debian/postrm --- a/debian/postrm Sun Aug 08 16:57:37 2004 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -#! /bin/sh -# postrm script for #PACKAGE# -# -# see: dh_installdeb(1) - -set -e - -# summary of how this script can be called: -# * `remove' -# * `purge' -# * `upgrade' -# * `failed-upgrade' -# * `abort-install' -# * `abort-install' -# * `abort-upgrade' -# * `disappear' overwrit>r> -# 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 -r 3b2a5a1c4347 -r 5312ca05ed48 debian/rules --- a/debian/rules Sun Aug 08 16:57:37 2004 +0000 +++ b/debian/rules Sun Sep 12 04:56:50 2004 +0000 @@ -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 - -# 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 +#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) -ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) - CFLAGS += -O0 -else - CFLAGS += -O2 -endif -ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) - INSTALL_PROGRAM += -s +STRIP =strip +ifneq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) + STRIP =: nostrip 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 +CFLAGS =-Wall -g +ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) + CFLAGS +=-O0 +else + CFLAGS +=-O2 +endif -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 +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 -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/-doc. - #INSTALLDOC# - - dh_install -i +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 -install-arch: - dh_testdir - dh_testroot - dh_clean -k -a - dh_installdirs -a - dh_installdirs /etc/dropbear +build: deb-checkdir build-stamp +build-stamp: config.status + $(MAKE) CC="$(CC)" LD="$(CC)" + touch build-stamp - # Add here commands to install the arch part of the package into - # debian/tmp. - $(MAKE) install prefix=$(CURDIR)/debian/dropbear/usr +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 - 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 +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 -# Build architecture dependant packages using the common target. -binary-arch: build-arch install-arch - $(MAKE) -f debian/rules DH_OPTIONS=-a binary-common +binary-indep: -binary: binary-arch #binary-indep -.PHONY: build clean binary-indep binary-arch binary install install-indep install-arch +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 -r 3b2a5a1c4347 -r 5312ca05ed48 debian/service/log --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/service/log Sun Sep 12 04:56:50 2004 +0000 @@ -0,0 +1,2 @@ +#!/bin/sh +exec chpst -udropbearlog svlogd -tt ./main diff -r 3b2a5a1c4347 -r 5312ca05ed48 debian/service/run --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/service/run Sun Sep 12 04:56:50 2004 +0000 @@ -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 -r 3b2a5a1c4347 -r 5312ca05ed48 debug.h --- a/debug.h Sun Aug 08 16:57:37 2004 +0000 +++ b/debug.h Sun Sep 12 04:56:50 2004 +0000 @@ -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 -r 3b2a5a1c4347 -r 5312ca05ed48 dropbearconvert.c --- a/dropbearconvert.c Sun Aug 08 16:57:37 2004 +0000 +++ b/dropbearconvert.c Sun Sep 12 04:56:50 2004 +0000 @@ -47,10 +47,8 @@ "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 @@ 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 -r 3b2a5a1c4347 -r 5312ca05ed48 dropbearkey.c --- a/dropbearkey.c Sun Aug 08 16:57:37 2004 +0000 +++ b/dropbearkey.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 -f [-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 " - "multiple of 8 (optional)\n", - progname); + "-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 .\n" +#ifdef DEBUG_TRACE + "-v verbose\n" +#endif + ,progname); } #if defined(DBMULTI_dropbearkey) || !defined(DROPBEAR_MULTI) @@ -89,23 +91,24 @@ 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 @@ 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 @@ } } + 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 @@ } } - 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 @@ 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 @@ 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 -r 3b2a5a1c4347 -r 5312ca05ed48 dss.c --- a/dss.c Sun Aug 08 16:57:37 2004 +0000 +++ b/dss.c Sun Sep 12 04:56:50 2004 +0000 @@ -164,13 +164,18 @@ 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 @@ 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 @@ 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 -r 3b2a5a1c4347 -r 5312ca05ed48 fake-rfc2553.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fake-rfc2553.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 -r 3b2a5a1c4347 -r 5312ca05ed48 fake-rfc2553.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fake-rfc2553.h Sun Sep 12 04:56:50 2004 +0000 @@ -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 -r 3b2a5a1c4347 -r 5312ca05ed48 filelist.txt --- a/filelist.txt Sun Aug 08 16:57:37 2004 +0000 +++ b/filelist.txt Sun Sep 12 04:56:50 2004 +0000 @@ -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 -r 3b2a5a1c4347 -r 5312ca05ed48 gendss.c --- a/gendss.c Sun Aug 08 16:57:37 2004 +0000 +++ b/gendss.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 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 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 @@ } 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 -r 3b2a5a1c4347 -r 5312ca05ed48 genrsa.c --- a/genrsa.c Sun Aug 08 16:57:37 2004 +0000 +++ b/genrsa.c Sun Sep 12 04:56:50 2004 +0000 @@ -40,7 +40,9 @@ 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 @@ 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 -r 3b2a5a1c4347 -r 5312ca05ed48 includes.h --- a/includes.h Sun Aug 08 16:57:37 2004 +0000 +++ b/includes.h Sun Sep 12 04:56:50 2004 +0000 @@ -38,7 +38,6 @@ #include #include #include -#include #include #include @@ -56,6 +55,7 @@ #include #include #include +#include #include @@ -111,14 +111,11 @@ #include #endif -#ifdef HAVE_SYS_DIRENT_H -#include -#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 @@ #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 -r 3b2a5a1c4347 -r 5312ca05ed48 keyimport.c --- a/keyimport.c Sun Aug 08 16:57:37 2004 +0000 +++ b/keyimport.c Sun Sep 12 04:56:50 2004 +0000 @@ -108,30 +108,14 @@ 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 @@ } #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 -r 3b2a5a1c4347 -r 5312ca05ed48 keyimport.h --- a/keyimport.h Sun Aug 08 16:57:37 2004 +0000 +++ b/keyimport.h Sun Sep 12 04:56:50 2004 +0000 @@ -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 -r 3b2a5a1c4347 -r 5312ca05ed48 listener.c --- a/listener.c Sun Aug 08 16:57:37 2004 +0000 +++ b/listener.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 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 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 @@ 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 @@ } } - 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 @@ 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 -r 3b2a5a1c4347 -r 5312ca05ed48 listener.h --- a/listener.h Sun Aug 08 16:57:37 2004 +0000 +++ b/listener.h Sun Sep 12 04:56:50 2004 +0000 @@ -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 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 -r 3b2a5a1c4347 -r 5312ca05ed48 options.h --- a/options.h Sun Aug 08 16:57:37 2004 +0000 +++ b/options.h Sun Sep 12 04:56:50 2004 +0000 @@ -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) */ -/* Enable Authentication Agent Forwarding */ +#define ENABLE_CLI_LOCALTCPFWD +#define ENABLE_CLI_REMOTETCPFWD + +#define ENABLE_SVR_LOCALTCPFWD +#define ENABLE_SVR_REMOTETCPFWD + +/* 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 -r 3b2a5a1c4347 -r 5312ca05ed48 packet.c --- a/packet.c Sun Aug 08 16:57:37 2004 +0000 +++ b/packet.c Sun Sep 12 04:56:50 2004 +0000 @@ -50,7 +50,7 @@ void write_packet() { int len, written; - buffer * writebuf; + buffer * writebuf = NULL; TRACE(("enter write_packet")); assert(!isempty(&ses.writequeue)); @@ -80,6 +80,7 @@ /* 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 @@ /* clearwritebuf is finished with */ buf_free(clearwritebuf); + clearwritebuf = NULL; /* enqueue the packet for sending */ buf_setpos(writebuf, 0); diff -r 3b2a5a1c4347 -r 5312ca05ed48 random.c --- a/random.c Sun Aug 08 16:57:37 2004 +0000 +++ b/random.c Sun Sep 12 04:56:50 2004 +0000 @@ -60,7 +60,7 @@ #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 @@ 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 -r 3b2a5a1c4347 -r 5312ca05ed48 rsa.c --- a/rsa.c Sun Aug 08 16:57:37 2004 +0000 +++ b/rsa.c Sun Sep 12 04:56:50 2004 +0000 @@ -201,7 +201,8 @@ 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 @@ } 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 @@ 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); + m_mp_init(&rsa_s); + /* the actual signing of the padded data */ - m_mp_init(&rsa_s); /* 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 @@ {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 -r 3b2a5a1c4347 -r 5312ca05ed48 runopts.h --- a/runopts.h Sun Aug 08 16:57:37 2004 +0000 +++ b/runopts.h Sun Sep 12 04:56:50 2004 +0000 @@ -29,6 +29,7 @@ #include "signkey.h" #include "buffer.h" #include "auth.h" +#include "tcpfwd.h" typedef struct runopts { @@ -51,9 +52,11 @@ 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 @@ 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 @@ 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 -r 3b2a5a1c4347 -r 5312ca05ed48 scp.c --- a/scp.c Sun Aug 08 16:57:37 2004 +0000 +++ b/scp.c Sun Sep 12 04:56:50 2004 +0000 @@ -178,8 +178,10 @@ 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 -r 3b2a5a1c4347 -r 5312ca05ed48 session.h --- a/session.h Sun Aug 08 16:57:37 2004 +0000 +++ b/session.h Sun Sep 12 04:56:50 2004 +0000 @@ -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 @@ 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 @@ /* 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 @@ 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 -r 3b2a5a1c4347 -r 5312ca05ed48 signkey.c --- a/signkey.c Sun Aug 08 16:57:37 2004 +0000 +++ b/signkey.c Sun Sep 12 04:56:50 2004 +0000 @@ -153,6 +153,7 @@ 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 @@ 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 @@ buffer *sigblob; - sigblob = buf_new(1000); + sigblob = buf_new(MAX_PUBKEY_SIZE); #ifdef DROPBEAR_DSS if (type == DROPBEAR_SIGNKEY_DSS) { diff -r 3b2a5a1c4347 -r 5312ca05ed48 svr-agentfwd.c --- a/svr-agentfwd.c Sun Aug 08 16:57:37 2004 +0000 +++ b/svr-agentfwd.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 @@ /* create the unix socket dir and file */ if (bindagent(fd, chansess) == DROPBEAR_FAILURE) { - return DROPBEAR_FAILURE; + goto fail; } /* listen */ @@ -73,12 +73,10 @@ } /* 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 @@ /* 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 @@ 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 -r 3b2a5a1c4347 -r 5312ca05ed48 svr-algo.c --- a/svr-algo.c Sun Aug 08 16:57:37 2004 +0000 +++ b/svr-algo.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 -r 3b2a5a1c4347 -r 5312ca05ed48 svr-auth.c --- a/svr-auth.c Sun Aug 08 16:57:37 2004 +0000 +++ b/svr-auth.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 @@ 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 @@ * 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 @@ 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 @@ } #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 @@ * 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 @@ encrypt_packet(); ses.authstate.authdone = 1; + ses.connecttimeout = 0; + if (ses.authstate.pw->pw_uid == 0) { ses.allowprivport = 1; diff -r 3b2a5a1c4347 -r 5312ca05ed48 svr-authpasswd.c --- a/svr-authpasswd.c Sun Aug 08 16:57:37 2004 +0000 +++ b/svr-authpasswd.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 @@ } -#endif /* DROPBEAR_PASSWORD_AUTH */ +#endif diff -r 3b2a5a1c4347 -r 5312ca05ed48 svr-authpubkey.c --- a/svr-authpubkey.c Sun Aug 08 16:57:37 2004 +0000 +++ b/svr-authpubkey.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 @@ 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 @@ } -#endif /* DROPBEAR_PUBKEY_AUTH */ +#endif diff -r 3b2a5a1c4347 -r 5312ca05ed48 svr-chansession.c --- a/svr-chansession.c Sun Aug 08 16:57:37 2004 +0000 +++ b/svr-chansession.c Sun Sep 12 04:56:50 2004 +0000 @@ -55,6 +55,10 @@ 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 @@ } /* 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 @@ 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 @@ } 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 @@ * 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 @@ 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 @@ 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 @@ } 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 @@ } /* 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 @@ 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 @@ 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 @@ } else #endif { + m_free(chansess->cmd); return DROPBEAR_FAILURE; } } @@ -557,11 +571,16 @@ 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 @@ 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 @@ 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 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 @@ /* 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 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 @@ 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 @@ /* 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 -r 3b2a5a1c4347 -r 5312ca05ed48 svr-kex.c --- a/svr-kex.c Sun Aug 08 16:57:37 2004 +0000 +++ b/svr-kex.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 @@ * 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 @@ * 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 -r 3b2a5a1c4347 -r 5312ca05ed48 svr-main.c --- a/svr-main.c Sun Aug 08 16:57:37 2004 +0000 +++ b/svr-main.c Sun Sep 12 04:56:50 2004 +0000 @@ -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,22 +47,6 @@ #endif { - fd_set fds; - struct timeval seltimeout; - unsigned int i, j; - int val; - int maxsock = -1; - struct sockaddr remoteaddr; - int remoteaddrlen; - int listensocks[MAX_LISTEN_ADDR]; - unsigned int listensockcount = 0; - FILE * pidfile; - - int childsock; - pid_t childpid; - int childpipe[2]; - - struct sigaction sa_chld; _dropbear_exit = svr_dropbear_exit; _dropbear_log = svr_dropbear_log; @@ -67,6 +54,75 @@ /* 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_storage remoteaddr; + int remoteaddrlen; + int listensocks[MAX_LISTEN_ADDR]; + int listensockcount = 0; + FILE *pidfile = NULL; + + int childsock; + pid_t childpid; + int childpipe[2]; + /* fork */ if (svr_opts.forkbg) { int closefds = 0; @@ -76,16 +132,12 @@ } #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 @@ 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 @@ /* 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 @@ 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 @@ } /* 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 @@ 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 @@ } /* 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 @@ } /* 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 @@ } /* 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])); + + nsock = dropbear_listen(NULL, svr_opts.ports[i], &sock[sockpos], + sockcount - sockpos, + &errstring, maxfd); + + if (nsock < 0) { + dropbear_log(LOG_WARNING, "Failed listening on '%s': %s", + svr_opts.ports[i], errstring); + m_free(errstring); + continue; } - 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); + sockpos += nsock; - if (bind(listensock, (struct sockaddr *)&listen_addr, - sizeof(listen_addr)) < 0) { - dropbear_exit("Bind failed port %d", svr_opts.ports[i]); - } - - /* listen */ - if (listen(listensock, 20) < 0) { /* TODO set listen count */ - dropbear_exit("Listen failed"); - } - - /* nonblock */ - if (fcntl(listensock, F_SETFL, O_NONBLOCK) < 0) { - dropbear_exit("Failed to set non-blocking"); - } - - sock[i] = listensock; - *maxfd = MAX(listensock, *maxfd); } - - return svr_opts.portcount; + return sockpos; } diff -r 3b2a5a1c4347 -r 5312ca05ed48 svr-runopts.c --- a/svr-runopts.c Sun Aug 08 16:57:37 2004 +0000 +++ b/svr-runopts.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 @@ "-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 @@ "-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 @@ #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 @@ 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 @@ -158,11 +160,18 @@ 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 @@ 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 @@ printhelp(argv[0]); exit(EXIT_FAILURE); break; - /* - case '4': - svr_opts.ipv4 = 0; +#ifdef DEBUG_TRACE + case 'v': + debug_trace = 1; break; - case '6': - svr_opts.ipv6 = 0; - break; - */ +#endif default: fprintf(stderr, "Unknown argument %s\n", argv[i]); printhelp(argv[0]); @@ -203,13 +209,24 @@ } } + /* 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 @@ 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 @@ 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 -r 3b2a5a1c4347 -r 5312ca05ed48 svr-session.c --- a/svr-session.c Sun Aug 08 16:57:37 2004 +0000 +++ b/svr-session.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 struct ChanType *svr_chantypes[] = { &svrchansess, - &chan_tcpdirect, + &svr_chan_tcpdirect, NULL /* Null termination is mandatory. */ }; @@ -169,7 +168,7 @@ /* 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 -r 3b2a5a1c4347 -r 5312ca05ed48 svr-tcpfwd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/svr-tcpfwd.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 -r 3b2a5a1c4347 -r 5312ca05ed48 svr-x11fwd.c --- a/svr-x11fwd.c Sun Aug 08 16:57:37 2004 +0000 +++ b/svr-x11fwd.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 @@ } /* 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 @@ /* 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 @@ 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 @@ 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 @@ 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 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 -r 3b2a5a1c4347 -r 5312ca05ed48 tcp-accept.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tcp-accept.c Sun Sep 12 04:56:50 2004 +0000 @@ -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 -r 3b2a5a1c4347 -r 5312ca05ed48 tcpfwd-direct.c --- a/tcpfwd-direct.c Sun Aug 08 16:57:37 2004 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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 -r 3b2a5a1c4347 -r 5312ca05ed48 tcpfwd-direct.h --- a/tcpfwd-direct.h Sun Aug 08 16:57:37 2004 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -/* - * Dropbear - a SSH2 server - * - * Copyright (c) 2002,2003 Matt Johnston - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ -#ifndef _TCPFWD_DIRECT_H_ -#define _TCPFWD_DIRECT_H_ -#ifndef DISABLE_TCFWD_DIRECT - -#include "includes.h" -#include "channel.h" - -extern const struct ChanType chan_tcpdirect; - -#endif -#endif diff -r 3b2a5a1c4347 -r 5312ca05ed48 tcpfwd-remote.c --- a/tcpfwd-remote.c Sun Aug 08 16:57:37 2004 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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 -r 3b2a5a1c4347 -r 5312ca05ed48 tcpfwd-remote.h --- a/tcpfwd-remote.h Sun Aug 08 16:57:37 2004 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -#ifndef _REMOTETCPFWD_H -#define _REMOTETCPFWD_H - -void recv_msg_global_request_remotetcp(); - -#endif /* _REMOTETCPFWD_H */ diff -r 3b2a5a1c4347 -r 5312ca05ed48 tcpfwd.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tcpfwd.h Sun Sep 12 04:56:50 2004 +0000 @@ -0,0 +1,70 @@ +/* + * Dropbear - a SSH2 server + * + * Copyright (c) 2002,2003 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ +#ifndef _TCPFWD_H +#define _TCPFWD_H + +#include "channel.h" + +struct TCPListener { + + /* 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; + + /* 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 -r 3b2a5a1c4347 -r 5312ca05ed48 termcodes.c --- a/termcodes.c Sun Aug 08 16:57:37 2004 +0000 +++ b/termcodes.c Sun Sep 12 04:56:50 2004 +0000 @@ -131,7 +131,11 @@ {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},