From 674a60748884dc55ee7091b7c23a41240e75f73c Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 1 Jun 2004 02:46:09 +0000 Subject: Makefile.in contains updated files required --HG-- extra : convert_revision : cc8a8c49dc70e632c352853a39801089b08149be --- CHANGES | 252 +++++++ INSTALL | 108 +++ LICENSE | 82 +++ MULTI | 31 + Makefile.in | 164 +++++ README | 14 + SMALL | 42 ++ TODO | 27 + agentfwd.h | 44 ++ algo.h | 75 ++ atomicio.c | 64 ++ atomicio.h | 36 + auth.h | 63 ++ authpasswd.h | 33 + authpubkey.h | 33 + bignum.c | 94 +++ bignum.h | 36 + buffer.c | 325 +++++++++ buffer.h | 65 ++ channel.h | 122 ++++ chansession.h | 89 +++ cli_algo.c | 92 +++ common-algo.c | 206 ++++++ common-channel.c | 1008 +++++++++++++++++++++++++++ common-chansession.c | 43 ++ common-kex.c | 458 +++++++++++++ common-packet.c | 620 +++++++++++++++++ common-session.c | 267 ++++++++ compat.c | 281 ++++++++ compat.h | 32 + config.guess | 1391 +++++++++++++++++++++++++++++++++++++ config.sub | 1492 ++++++++++++++++++++++++++++++++++++++++ configure.in | 489 +++++++++++++ dbmulti.c | 48 ++ dbutil.c | 296 ++++++++ dbutil.h | 59 ++ debian/README.Debian | 41 ++ debian/changelog | 79 +++ debian/compat | 1 + debian/conffiles | 0 debian/control | 14 + debian/copyright.in | 9 + debian/dirs | 2 + debian/docs | 2 + debian/dropbear.default | 0 debian/dropbear.init | 78 +++ debian/postinst | 90 +++ debian/postrm | 45 ++ debian/rules | 134 ++++ debug.h | 68 ++ dropbearconvert.c | 146 ++++ dropbearkey.c | 272 ++++++++ dss.c | 399 +++++++++++ dss.h | 61 ++ filelist.txt | 113 +++ gendss.c | 215 ++++++ gendss.h | 36 + genrsa.c | 138 ++++ genrsa.h | 36 + includes.h | 134 ++++ install-sh | 251 +++++++ kex.h | 62 ++ keyimport.c | 1748 +++++++++++++++++++++++++++++++++++++++++++++++ keyimport.h | 18 + localtcpfwd.c | 142 ++++ localtcpfwd.h | 34 + loginrec.c | 1540 +++++++++++++++++++++++++++++++++++++++++ loginrec.h | 194 ++++++ main.c | 343 ++++++++++ options.h | 327 +++++++++ packet.h | 44 ++ progressmeter.c | 267 ++++++++ progressmeter.h | 27 + queue.c | 89 +++ queue.h | 49 ++ random.c | 178 +++++ random.h | 32 + remotetcpfwd.c | 301 ++++++++ remotetcpfwd.h | 6 + rsa.c | 362 ++++++++++ rsa.h | 61 ++ runopts.h | 72 ++ scp.c | 1163 +++++++++++++++++++++++++++++++ scpmisc.c | 161 +++++ scpmisc.h | 44 ++ service.h | 30 + session.h | 176 +++++ signkey.c | 376 ++++++++++ signkey.h | 58 ++ ssh.h | 93 +++ sshpty.c | 412 +++++++++++ sshpty.h | 28 + svr-agentfwd.c | 247 +++++++ svr-algo.c | 74 ++ svr-auth.c | 351 ++++++++++ svr-authpasswd.c | 108 +++ svr-authpubkey.c | 424 ++++++++++++ svr-chansession.c | 931 +++++++++++++++++++++++++ svr-kex.c | 274 ++++++++ svr-packet.c | 196 ++++++ svr-runopts.c | 349 ++++++++++ svr-service.c | 88 +++ svr-session.c | 238 +++++++ tcpfwd.c | 124 ++++ tcpfwd.h | 37 + termcodes.c | 183 +++++ termcodes.h | 46 ++ x11fwd.c | 216 ++++++ x11fwd.h | 38 ++ 109 files changed, 23306 insertions(+) create mode 100644 CHANGES create mode 100644 INSTALL create mode 100644 LICENSE create mode 100644 MULTI create mode 100644 Makefile.in create mode 100644 README create mode 100644 SMALL create mode 100644 TODO create mode 100644 agentfwd.h create mode 100644 algo.h create mode 100644 atomicio.c create mode 100644 atomicio.h create mode 100644 auth.h create mode 100644 authpasswd.h create mode 100644 authpubkey.h create mode 100644 bignum.c create mode 100644 bignum.h create mode 100644 buffer.c create mode 100644 buffer.h create mode 100644 channel.h create mode 100644 chansession.h create mode 100644 cli_algo.c create mode 100644 common-algo.c create mode 100644 common-channel.c create mode 100644 common-chansession.c create mode 100644 common-kex.c create mode 100644 common-packet.c create mode 100644 common-session.c create mode 100644 compat.c create mode 100644 compat.h create mode 100644 config.guess create mode 100644 config.sub create mode 100644 configure.in create mode 100644 dbmulti.c create mode 100644 dbutil.c create mode 100644 dbutil.h create mode 100644 debian/README.Debian create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/conffiles create mode 100644 debian/control create mode 100644 debian/copyright.in create mode 100644 debian/dirs create mode 100644 debian/docs create mode 100644 debian/dropbear.default create mode 100644 debian/dropbear.init create mode 100644 debian/postinst create mode 100644 debian/postrm create mode 100644 debian/rules create mode 100644 debug.h create mode 100644 dropbearconvert.c create mode 100644 dropbearkey.c create mode 100644 dss.c create mode 100644 dss.h create mode 100644 filelist.txt create mode 100644 gendss.c create mode 100644 gendss.h create mode 100644 genrsa.c create mode 100644 genrsa.h create mode 100644 includes.h create mode 100644 install-sh create mode 100644 kex.h create mode 100644 keyimport.c create mode 100644 keyimport.h create mode 100644 localtcpfwd.c create mode 100644 localtcpfwd.h create mode 100644 loginrec.c create mode 100644 loginrec.h create mode 100644 main.c create mode 100644 options.h create mode 100644 packet.h create mode 100644 progressmeter.c create mode 100644 progressmeter.h create mode 100644 queue.c create mode 100644 queue.h create mode 100644 random.c create mode 100644 random.h create mode 100644 remotetcpfwd.c create mode 100644 remotetcpfwd.h create mode 100644 rsa.c create mode 100644 rsa.h create mode 100644 runopts.h create mode 100644 scp.c create mode 100644 scpmisc.c create mode 100644 scpmisc.h create mode 100644 service.h create mode 100644 session.h create mode 100644 signkey.c create mode 100644 signkey.h create mode 100644 ssh.h create mode 100644 sshpty.c create mode 100644 sshpty.h create mode 100644 svr-agentfwd.c create mode 100644 svr-algo.c create mode 100644 svr-auth.c create mode 100644 svr-authpasswd.c create mode 100644 svr-authpubkey.c create mode 100644 svr-chansession.c create mode 100644 svr-kex.c create mode 100644 svr-packet.c create mode 100644 svr-runopts.c create mode 100644 svr-service.c create mode 100644 svr-session.c create mode 100644 tcpfwd.c create mode 100644 tcpfwd.h create mode 100644 termcodes.c create mode 100644 termcodes.h create mode 100644 x11fwd.c create mode 100644 x11fwd.h diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..74bc13c --- /dev/null +++ b/CHANGES @@ -0,0 +1,252 @@ +0.41 - Mon Jan 19 2004 22:40:19 +0800 + +- Fix in configure so that cross-compiling works, thanks to numerous people for + reporting and testing + +- Terminal mode parsing now handles empty terminal mode strings (sent by + Windows ssh.com clients), thanks to Ricardo Derbes for the report + +- Handling is improved for users with no shell specified in /etc/passwd, + thanks again to Ricardo Derbes + +- Fix for compiling with --disable-syslog, thanks to gordonfh + +- Various minor fixes allow scp to work with irix, thanks to Paul Marinceu for + fixing it up + +- Use not , since the former seems more common + +0.40 - Tue Jan 13 2004 21:05:19 +0800 + +- Remote TCP forwarding (-R) style implemented + +- Local and remote TCP forwarding can each be disabled at runtime (-k and -j + switches) + +- Fix for problems detecting openpty() with uClibc - many thanks to various + people for reporting and testing fixes, including (in random order) Cristian + Ionescu-Idbohrn, James Ewing, Steve Dover, Thomas Lundquist and Frederic + Lavernhe + +- Improved portability for IRIX, thanks to Paul Marinceu + +- AIX and HPUX portability fixes, thanks to Darren Tucker for patches + +- prngd should now work correctly, thanks to Darren Tucker for the patch + +- scp compilation on systems without strlcpy() is fixed, thanks to Peter + Jannesen and David Muse for reporting it (independently and simultaneously :) + +- Merged in new LibTomCrypt 0.92 and LibTomMath 0.28 + +0.39 - Tue Dec 16 2003 15:19:19 +0800 + +- Better checking of key lengths and parameters for DSS and RSA auth + +- Print fingerprint of keys used for pubkey auth + +- More consistent logging of usernames and IPs + +- Added option to disable password auth (or just for root) at runtime + +- Avoid including bignum functions which don't give much speed benefit but + take up binary size + +- Added a stripped down version of OpenSSH's scp binary + +- Added additional supporting functions for Irix, thanks to Paul Marinceu + +- Don't check for unused libraries in configure script + +- Removed trailing comma in algorithm lists (thanks to Mihnea Stoenescu) + +- Fixed up channel close handling, always send close packet in response + (also thanks to Mihnea Stoenescu) + +- Various makefile improvements for cross-compiling, thanks to Friedrich + Lobenstock and Mihnea Stoenescu + +- Use daemon() function if available (or our own copy) rather than separate + code (thanks to Frédéric Lavernhe for the report and debugging, and Bernard + Blackham for his suggestion on what to look at) + +- Fixed up support for first_kex_packet_follows, required to talk to ssh.com + clients. Thanks to Marian Stagarescu for the bug report. + +- Avoid using MAXPATHLEN, pointer from Ian Morris + +- Improved input sanity checking + +0.38 - Sat Oct 11 2003 16:28:13 +0800 + +- Default hostkey path changed to /etc/dropbear/dropbear_{rsa,dss}_host_key + rather than /etc/dropbear_{rsa,dss}_host_key + +- Added SMALL and MULTI text files which have info on compiling for multiple + binaries or small binaries + +- Allow for commandline definition of some options.h settings + (without warnings) + +- Be more careful handling EINTR + +- More fixes for channel closing + +- Added multi-binary support + +- Improved logging of IPs, now get logged in all cases + +- Don't chew cpu when waiting for version identification string, also + make sure that we kick off people if they don't auth within 5 minutes. + +- Various small fixes, warnings etc + +- Display MOTD if requested - suggested by + Trent Lloyd and + Zach White + +- sftp support works (relies on OpenSSH sftp binary or similar) + +- Added --disable-shadow option (requested by the floppyfw guys) + +0.37 - Wed Sept 24 2003 19:42:12 +0800 + +- Various portability fixes, fixes for Solaris 9, Tru64 5.1, Mac OS X 10.2, + AIX, BSDs + +- Updated LibTomMath to 0.27 and LibTomCrypt to 0.90 + +- Renamed util.{c,h} to dbutil.{c,h} to avoid conflicts with system util.h + +- Added some small changes so it'll work with AIX (plus Linux Affinity). + Thanks to Shig for them. + +- Improved the closing messages, so a clean exit is "Exited normally" + +- Added some more robust integer/size checking in buffer.c as a backstop for + integer overflows + +- X11 forwarding fixed for OSX, path for xauth changed to /usr/X11R6/bin/xauth + +- Channel code handles closing more nicely, doesn't sit waiting for an extra + keystroke on BSD/OSX platforms, and data is flushed fully before closing + child processes (thanks to + Cristian Ionescu-Idbohrn for + pointing that out). + +- Changed "DISABLE_TCPFWD" to "ENABLE_TCPFWD" (and for x11/auth) so + "disable DISABLE_TCPWD" isn't so confusing. + +- Fix authorized_keys handling (don't crash on too-long keys, and + use fgetc not getc to avoid strange macro-related issues), thanks to + Cristian Ionescu-Idbohrn + and Steve Rodgers for reporting and testing. + +- Fixes to the README with regard to uClibc systems, thanks to + Cristian Ionescu-Idbohrn , + as well as general improvements to documentation (split README/INSTALL) + +- Fixed up some compilation problems with dropbearconvert/dropbearkey if + DSS or RSA were disabled, reported by Patrik Karlsson + +- Fix double-free bug for hostkeys, reported by + Vincent Sanders + +- Fix up missing \ns from dropbearconvert help message, + thanks to Mordy Ovits for the patch + +0.36 - Tue August 19 2003 12:16:23 +0800 + +- Fix uninitialised temporary variable in DSS signing code + (thanks to Matthew Franz for reporting, and the authors + of Valgrind for making it easy to track down) +- Fix remote version-string parsing error + (thanks to Bernard Blackham for noticing) +- Improved host-algorithm-matching algorithm in algo.c +- Decreased MAX_STRING_LEN to a more realistic value +- Fix incorrect version (0.34) in this CHANGES file for the previous release. + +0.35 - Sun August 17 2003 05:37:47 +0800 + +- Fix for remotely exploitable format string buffer overflow. + (thanks to Joel Eriksson ) + +0.34 - Fri August 15 2003 15:10:00 +0800 + +- Made syslog optional, both at compile time and as a compile option + (suggested by Laurent Bercot ) +- Fixup for bad base64 parsing in authorized_keys + (noticed by Davyd Madeley ) +- Added initial tcp forwarding code, only -L (local) at this stage +- Improved "make install" with DESTDIR and changing ownership seperately, + don't check for setpgrp on Linux for crosscompiling. + (from Erik Andersen ) +- More commenting, fix minor compile warnings, make return values more + consistent etc +- Various signedness fixes +- Can listen on multiple ports +- added option to disable openpty with configure script, + (from K.-P. Kirchdörfer ) +- Various cleanups to bignum code + (thanks to Tom St Denis ) +- Fix compile error when disabling RSA + (from Marc Kleine-Budde ) +- Other cleanups, splitting large functions for packet and kex handling etc + +0.33 - Sun June 22 2003 22:24:12 +0800 + +- Fixed some invalid assertions in the channel code, fixing the server dying + when forwarding X11 connections. +- Add dropbearconvert to convert to/from OpenSSH host keys and Dropbear keys +- RSA keys now keep p and q parameters for compatibility -- old Dropbear keys + still work, but can't be converted to OpenSSH etc. +- Debian packaging directory added, thanks to + Grahame (grahame at angrygoats.net) +- 'install' target added to the makefile +- general tidying, improve consistency of functions etc +- If RSA or DSS hostkeys don't exist, that algorithm won't be used. +- Improved RSA and DSS key generation, more efficient and fixed some minor bugs + (thanks to Tom St Denis for the advice) +- Merged new versions of LibTomCrypt (0.86) and LibTomMath (0.21) + +0.32 - Sat May 24 2003 12:44:11 +0800 + +- Don't compile unused code from libtomcrypt (test vectors etc) +- Updated to libtommath 0.17 and libtomcrypt 0.83. New libtommath results + in smaller binary size, due to not linking unrequired code +- X11 forwarding added +- Agent forwarding added (for OpenSSH.com ssh client/agent) +- Fix incorrect buffer freeing when banners are used +- Hostname resolution works +- Various minor bugfixes/code size improvements etc + +0.31 - Fri May 9 2003 17:57:16 +0800 + +- Improved syslog messages - IP logging etc +- Strip control characters from log messages (specified username currently) +- Login recording (utmp/wtmp) support, so last/w/who work - taken from OpenSSH +- Shell is started as a proper login shell, so /etc/profile etc is sourced +- Ptys work on Solaris (2.8 x86 tested) now +- Fixed bug in specifying the rsa hostkey +- Fixed bug in compression code, could trigger if compression resulted in + larger output than input (uncommon but possible). + +0.30 - Thu Apr 17 2003 18:46:15 +0800 + +- SECURITY: buffer.c had bad checking for buffer increment length - fixed +- channel code now closes properly on EOF - scp processes don't hang around +- syslog support added - improved auth/login/failure messages +- general code tidying, made return codes more consistent +- Makefile fixed for dependencies and makes libtomcrypt as well +- Implemented sending SSH_MSG_UNIMPLEMENTED :) + +0.29 - Wed Apr 9 2003 + +- Fixed a stupid bug in 0.28 release, 'newstr = strdup(oldstr)', + not 'newstr=oldstr' + +0.28 - Sun Apr 6 2003 + +- Initial public release + +Development was started in October 2002 diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..921c099 --- /dev/null +++ b/INSTALL @@ -0,0 +1,108 @@ +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 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) + +./configure (optionally with --disable-zlib or --disable-syslog, + or --help for other options) + +- Then compile and optionally install Dropbear: + +(the Makefile requires GNU make, if you want to make it portable, send me + some patches) + +make +make install (installs to /usr/local/sbin, /usr/local/bin by 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 + +And you can now run the server. +./dropbear + +or './dropbear -h' to get options. + +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 with +"make scp-progress". + +============================================================================ + +Compiling with uClibc: + +Firstly, make sure you have at least uclibc 0.9.17, as getusershell() in prior +versions is broken. Also note that you may get strange issues if your uClibc +headers don't match the library you are running with, ie the headers might +say that shadow password support exists, but the libraries don't have it. + +To compile for uClibc the following should work: + +rm config.cache +CC=i386-uclib-gcc ./configure --disable-zlib +make clean +make +make strip + +... and that should be it. You can use "make static" to make statically linked +binaries, and it is advisable to strip the binaries too. If you're looking +to make a small binary, you should remove unneeded ciphers and MD5, by +editing options.h + +It is possible to compile zlib in, by copying zlib.h and zconf.h into a +subdirectory (ie zlibincludes), and + +export CFLAGS="-Izlibincludes -I../zlibincludes" +export LDFLAGS=/usr/lib/libz.a + +before ./configure and make. + +If you disable zlib, you must explicitly disable compression for the client - +OpenSSH is possibly buggy in this regard, it seems you need to disable it +globally in ~/.ssh/config, not just in the host entry in that file. + +You may want to manually disable lastlog recording when using uClibc, configure +with --disable-lastlog. + +One common problem is pty allocation. There are a number of types of pty allocation which can be used -- if they work properly, the end result is the same for each type. Running configure should detect the best type to use automatically, however for some embedded systems, this may be incorrect. Some things to note: + + If your system expects /dev/pts to be mounted (this is a uClibc option), + make sure that it is. + + Make sure that your libc headers match the library version you are using. + + If openpty() is being used (HAVE_OPENPTY defined in config.h) and it fails, + you can try compiling with --disable-openpty. You will probably then need + to create all the /dev/pty?? and /dev/tty?? devices, which can be + problematic for devfs. In general, openpty() is the best way to allocate + PTYs, so it's best to try and get it working. + + +============================================================================ + +Public key auth: + +You can use ~/.ssh/authorized_keys in the same way as with OpenSSH, just put +the key entries in that file. They should be of the form: + +ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAwVa6M6cGVmUcLl2cFzkxEoJd06Ub4bVDsYrWvXhvUV+ZAM9uGuewZBDoAqNKJxoIn0Hyd0Nk/yU99UVv6NWV/5YSHtnf35LKds56j7cuzoQpFIdjNwdxAN0PCET/MG8qyskG/2IE2DPNIaJ3Wy+Ws4IZEgdJgPlTYUBWWtCWOGc= someone@hostname + +You must make sure that ~/.ssh, and the key file, are only writable by the +user. + +NOTE: Dropbear ignores authorized_keys options such as those described in the +OpenSSH sshd manpage, and will not allow a login for these keys. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..254efbd --- /dev/null +++ b/LICENSE @@ -0,0 +1,82 @@ +The majority of code is written by Matt Johnston, under the following license: + +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. + +===== + +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 + +===== + +sshpty.c is taken from OpenSSH 3.5p1, + Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + All rights reserved + "As far as I am concerned, the code I have written for this software + can be used freely for any purpose. Any derived versions of this + software must be clearly marked as such, and if the derived work is + incompatible with the protocol description in the RFC file, it must be + called by a name other than "ssh" or "Secure Shell". " + +===== + +loginrec.c +loginrec.h +atomicio.h +atomicio.c +and strlcat() (included in util.c) are from OpenSSH 3.6.1p2, and are licensed +under the 2 point BSD license. + +loginrec is written primarily by Andre Lucas, atomicio.c by Theo de Raadt. + +strlcat() is (c) Todd C. Miller + +===== + +Import code in keyimport.c is modified from PuTTY's import.c, licensed as +follows: + +PuTTY is copyright 1997-2003 Simon Tatham. + +Portions copyright Robert de Bath, Joris van Rantwijk, Delian +Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, +Justin Bradford, and CORE SDI S.A. + +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 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. diff --git a/MULTI b/MULTI new file mode 100644 index 0000000..4e49f08 --- /dev/null +++ b/MULTI @@ -0,0 +1,31 @@ +Multi-binary compilation +======================== + +To compile for systems without much space (floppy distributions etc), you +can create a single binary. This will save disk space by avoiding repeated +code between the three components (dropbear, dropbearkey, dropbearconvert). +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). + +You should then "make clean" (if you compiled previously), then + +"make dropbearmulti" + +("make dropbearmultistatic" will make a static binary). + +To use the binary, symlink it from the desired executable: + +ln -s dropbearmulti dropbear + +then execute as normal: + +./dropbear + +"make install" doesn't currently work for multi-binary configuration, however +in most situations where it is being used, the target and build systems will +differ. diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..10d3cc1 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,164 @@ +LTC=libtomcrypt/libtomcrypt.a +LTM=libtommath/libtommath.a + +COMMONOBJS=dbutil.o common-session.o common-packet.o common-algo.o buffer.o \ + common-kex.o dss.o bignum.o \ + signkey.o rsa.o random.o common-channel.o \ + common-chansession.o queue.o termcodes.o runopts.o \ + loginrec.o atomicio.o x11fwd.o agentfwd.o localtcpfwd.o compat.o \ + remotetcpfwd.o tcpfwd.o + +SVROBJS=svr-kex.o svr-packet.o svr-algo.o svr-auth.o sshpty.o \ + svr-authpasswd.o svr-authpubkey.o svr-session.o svr-service.o \ + svr-chansession.o svr-runopts.o svr-agentfwd.o + +CLIOBJS= + +OBJS=$(COMMONOBJS) $(SVROBJS) + +DROPBEAROBJS=main.o + +DROPBEARKEYOBJS=dropbearkey.o gendss.o genrsa.o + +CONVERTOBJS=dropbearconvert.o keyimport.o + +SCPOBJS=scp.o progressmeter.o atomicio.o scpmisc.o + +HEADERS=options.h dbutil.h session.h packet.h algo.h ssh.h buffer.h kex.h \ + dss.h bignum.h signkey.h rsa.h random.h service.h auth.h authpasswd.h \ + debug.h channel.h chansession.h debug.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 localtcpfwd.h compat.h \ + remotetcpfwd.h tcpfwd.h + +ALLOBJS=$(OBJS) $(DROPBEARKEYOBJS) $(DROPBEAROBJS) + +prefix=@prefix@ +exec_prefix=${prefix} +bindir=${exec_prefix}/bin +sbindir=${exec_prefix}/sbin + +CC=@CC@ +LD=@CC@ +AR=@AR@ +RANLIB=@RANLIB@ +STRIP=@STRIP@ +INSTALL=@INSTALL@ +CFLAGS=-Ilibtomcrypt @CFLAGS@ +LIBS=$(LTC) $(LTM) @LIBS@ +LDFLAGS=@LDFLAGS@ + +# these are exported so that libtomcrypt's makefile will use them +export CC +export CFLAGS +export RANLIB AR STRIP + +all: dropbear dropbearkey dropbearconvert + @echo + @echo Run \"make strip\" if you want stripped binaries, + @echo or \"make install\" to install to ${prefix} + +strip: + -$(STRIP) dropbear + -$(STRIP) dropbearkey + -$(STRIP) dropbearconvert + +install: all + $(INSTALL) -d -m 755 $(DESTDIR)$(sbindir) + $(INSTALL) -d -m 755 $(DESTDIR)$(bindir) + $(INSTALL) -m 755 dropbear $(DESTDIR)$(sbindir) + $(INSTALL) -m 755 dropbearkey $(DESTDIR)$(bindir) + $(INSTALL) -m 755 dropbearconvert $(DESTDIR)$(bindir) + # chown might fail, so do it separately to the install + -chown root $(DESTDIR)$(sbindir)/dropbear + -chgrp 0 $(DESTDIR)$(sbindir)/dropbear + -chown root $(DESTDIR)$(bindir)/dropbearkey + -chgrp 0 $(DESTDIR)$(bindir)/dropbearkey + -chown root $(DESTDIR)$(bindir)/dropbearconvert + -chgrp 0 $(DESTDIR)$(bindir)/dropbearconvert + + +static: dropbear-static dropbearkey-static dropbearconvert-static + +$(ALLOBJS): $(HEADERS) Makefile + +dropbear: $(DROPBEAROBJS) $(OBJS) $(HEADERS) Makefile $(LTC) $(LTM) + $(LD) $(LDFLAGS) -o dropbear $(DROPBEAROBJS) $(OBJS) $(LIBS) + +dropbear-static: $(DROPBEAROBJS) $(OBJS) $(HEADERS) Makefile $(LTC) $(LTM) + $(LD) $(LDFLAGS) -o staticdropbear $(DROPBEAROBJS) $(OBJS) $(LIBS) -static + +dropbearkey: $(OBJS) $(HEADERS) Makefile $(DROPBEARKEYOBJS) $(LTC) $(LTM) + $(LD) $(LDFLAGS) -o dropbearkey $(DROPBEARKEYOBJS) \ + $(OBJS) $(LIBS) + +dropbearkey-static: $(OBJS) $(HEADERS) Makefile $(DROPBEARKEYOBJS) $(LTC) $(LTM) + $(LD) $(LDFLAGS) -o staticdropbearkey $(DROPBEARKEYOBJS) \ + $(OBJS) $(LIBS) -static + +dropbearconvert: $(OBJS) $(HEADERS) Makefile $(CONVERTOBJS) $(LTC) $(LTM) + $(LD) $(LDFLAGS) -o dropbearconvert $(CONVERTOBJS) $(OBJS) $(LIBS) + +dropbearconvert-static: $(OBJS) $(HEADERS) Makefile $(CONVERTOBJS) $(LTC) $(LTM) + $(LD) $(LDFLAGS) -o staticdropbearconvert $(CONVERTOBJS) $(OBJS) $(LIBS) \ + -static + +multi: dropbearmulti + +dropbearmulti: $(HEADERS) $(OBJS) $(LTC) $(LTM) $(CONVERTOBJS) \ + $(DROPBEARKEYOBJS) $(DROPBEAROBJS) dbmulti.o + $(LD) $(LDFLAGS) -o dropbearmulti $(OBJS) $(LTM) $(LTM) $(CONVERTOBJS) \ + $(DROPBEARKEYOBJS) $(DROPBEAROBJS) dbmulti.o $(LIBS) + @echo "You should now create symlinks to the programs you have included" + @echo "ie 'ln -s dropbearmulti dropbear'" + +dropbearmultistatic: $(HEADERS) $(OBJS) $(LTC) $(LTM) $(CONVERTOBJS) \ + $(DROPBEARKEYOBJS) $(DROPBEAROBJS) dbmulti.o + $(LD) $(LDFLAGS) -o staticdropbearmulti $(OBJS) $(LTM) $(LTM) \ + $(CONVERTOBJS) $(DROPBEARKEYOBJS) $(DROPBEAROBJS) \ + dbmulti.o $(LIBS) -static + @echo "You should now create symlinks to the programs you have included" + @echo "ie 'ln -s dropbearmultistatic dropbear'" + +stripmulti: dropbearmulti + -$(STRIP) dropbearmulti + + +scp: $(SCPOBJS) Makefile + $(LD) $(LDFLAGS) -o $@ $(SCPOBJS) + +# gnumake before 3.80 is broken. So this is uglyish +scp-progress: atomicio.o scpmisc.o $(HEADERS) Makefile + -rm scp.o progressmeter.o + $(MAKE) CFLAGS="$(CFLAGS) -DPROGRESS_METER" scp.o progressmeter.o + $(LD) $(LDFLAGS) -o $@ $(SCPOBJS) + +scpstatic: $(SCPOBJS) $(HEADERS) Makefile + $(LD) $(LDFLAGS) -o $@ $(SCPOBJS) -static + +$(LTC): $(HEADERS) + cd libtomcrypt && $(MAKE) clean && $(MAKE) + +$(LTM): $(HEADERS) + cd libtommath && $(MAKE) + +ltc-clean: + cd libtomcrypt && $(MAKE) clean + +ltm-clean: + cd libtommath && $(MAKE) clean + +sizes: dropbear + objdump -t dropbear|grep ".text"|cut -d "." -f 2|sort -rn + +clean: ltc-clean ltm-clean + -rm -f dropbear dropbear dropbearkey staticdropbear staticdropbearkey + -rm -f dropbearconvert staticdropbearconvert scp scp-progress scpstatic + -rm -f dropbearmulti dropbearmultistatic + -rm -f *.o *.da *.bb *.bbg *.prof + +distclean: clean tidy + -rm -f Makefile config.h + +tidy: + -rm -f *~ *.gcov */*~ diff --git a/README b/README new file mode 100644 index 0000000..4dae30f --- /dev/null +++ b/README @@ -0,0 +1,14 @@ +This is Dropbear, a smallish SSH 2 server. + +INSTALL has compilation instructions. + +MULTI has instructions on making a multi-purpose binary (ie a single binary +which performs multiple tasks, to save disk space) + +SMALL has some tips on creating small binaries. + +See TODO for a few of the things I know need looking at, and please contact +me if you have any questions/bugs found/features/ideas/comments etc :) + +Matt Johnston +matt@ucc.asn.au diff --git a/SMALL b/SMALL new file mode 100644 index 0000000..a7442aa --- /dev/null +++ b/SMALL @@ -0,0 +1,42 @@ +Tips for a small system: + +The following are set in options.h + +- You can safely disable blowfish and twofish ciphers, and MD5 hmac, without + affecting interoperability + +- If you're compiling statically, you can turn off host lookups + +- You can disable either password or public-key authentication, though note + that the IETF draft states that pubkey authentication is required. + +- Similarly with DSS and RSA, you can disable one of these if you know that + all clients will be able to support a particular one. The IETF draft + states that DSS is required, however you may prefer to use RSA. + DON'T disable either of these on systems where you aren't 100% sure about + who will be connecting and what clients they will be using. + +- Disabling the MOTD code and SFTP-SERVER may save a small amount of codesize + +- You can disable x11, tcp and agent forwarding as desired. None of these are + essential, although agent-forwarding is often useful even on firewall boxes. + +If you are compiling statically, you may want to disable zlib, as it will use +a few tens of kB of binary-size (./configure --disable-zlib). + +You can create a combined binary, see the file MULTI, which will put all +the functions into one binary, avoiding repeated code. + +If you're compiling with gcc, you might want to look at gcc's options for +stripping unused code. The relevant vars to set before configure are: + +LDFLAGS=-Wl,--gc-sections +CFLAGS="-ffunction-sections -fdata-sections" + +You can also experiment with optimisation flags such as -Os, note that in some +cases these flags actually seem to increase size, so experiment before +deciding. + +Of course using small C libraries such as uClibc and dietlibc can also help. + +If you have any queries, mail me and I'll see if I can help. diff --git a/TODO b/TODO new file mode 100644 index 0000000..64cbd5e --- /dev/null +++ b/TODO @@ -0,0 +1,27 @@ +Current: + +Things which need doing: + +- Make options.h generated from configure perhaps? + +- investigate self-pipe? +- fix agent fwd problems +- improve channel window adjustment algorithm (circular buffering) + +- Don't use pregenerated AES tables + +- check PRNG +- check that there aren't timing issues with valid/invalid user authentication + feedback. + +- IP6 (binding to :: takes over ipv4 as well, sigh. If anyone wants to suggest + a clean way (ie no V4MAPPED or setsockopt things) please let me know :) +- Binding to different interfaces (see ipv6 probably) + +- PAM ?? +- inetd +- possible RSA blinding? need to check whether this is vuln to timing attacks +- CTR mode, SSH_MSG_IGNORE sending to improve CBC security +- DH Group Exchange possibly + +- fix scp.c for IRIX diff --git a/agentfwd.h b/agentfwd.h new file mode 100644 index 0000000..0b42cb2 --- /dev/null +++ b/agentfwd.h @@ -0,0 +1,44 @@ +/* + * 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 _AGENTFWD_H_ +#define _AGENTFWD_H_ +#ifndef DISABLE_AGENTFWD + +#include "includes.h" +#include "chansession.h" +#include "channel.h" + +int agentreq(struct ChanSess * chansess); +int agentaccept(struct ChanSess * chansess); +void agentcleanup(struct ChanSess * chansess); +void agentsetauth(struct ChanSess *chansess); +void agentset(struct ChanSess *chansess); + +#ifdef __hpux +#define seteuid(a) setresuid(-1, (a), -1) +#define setegid(a) setresgid(-1, (a), -1) +#endif + +#endif /* DROPBEAR_AGENTFWD */ +#endif /* _AGENTFWD_H_ */ diff --git a/algo.h b/algo.h new file mode 100644 index 0000000..369f73c --- /dev/null +++ b/algo.h @@ -0,0 +1,75 @@ +/* + * 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 _ALGO_H_ + +#define _ALGO_H_ + +#include "includes.h" +#include "buffer.h" + +struct Algo_Type { + + unsigned char *name; /* identifying name */ + char val; /* a value for this cipher, or -1 for invalid */ + void *data; /* algorithm specific data */ + unsigned usable : 1; /* whether we can use this algorithm */ + +}; + +typedef struct Algo_Type algo_type; + +/* lists mapping ssh types of algorithms to internal values */ +extern algo_type sshkex[]; +extern algo_type sshhostkey[]; +extern algo_type sshciphers[]; +extern algo_type sshhashes[]; +extern algo_type sshcompress[]; + +extern const struct dropbear_cipher dropbear_nocipher; +extern const struct dropbear_hash dropbear_nohash; + +struct dropbear_cipher { + const struct _cipher_descriptor *cipherdesc; + unsigned long keysize; + unsigned char blocksize; +}; + +struct dropbear_hash { + const struct _hash_descriptor *hashdesc; + unsigned long keysize; + unsigned char hashsize; +}; + +void crypto_init(); +int have_algo(char* algo, size_t algolen, algo_type algos[]); +void buf_put_algolist(buffer * buf, algo_type localalgos[]); + +algo_type * common_buf_match_algo(buffer* buf, algo_type localalgos[], + int *goodguess); +algo_type * svr_buf_match_algo(buffer* buf, algo_type localalgos[], + int *goodguess); +algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[]); + +#endif /* _ALGO_H_ */ diff --git a/atomicio.c b/atomicio.c new file mode 100644 index 0000000..5b0b393 --- /dev/null +++ b/atomicio.c @@ -0,0 +1,64 @@ +/* + * Copied from OpenSSH 3.6.1p2, required for loginrec.c + * + * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* Taken from OpenSSH for use with the loginrec code */ + +/* RCSID("$OpenBSD: atomicio.c,v 1.10 2001/05/08 22:48:07 markus Exp $"); */ + +#include "atomicio.h" + +/* + * ensure all of data on socket comes through. f==read || f==write + */ +ssize_t +atomicio(f, fd, _s, n) + ssize_t (*f) (); + int fd; + void *_s; + size_t n; +{ + char *s = _s; + ssize_t res, pos = 0; + + while (n > pos) { + res = (f) (fd, s + pos, n - pos); + switch (res) { + case -1: +#ifdef EWOULDBLOCK + if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) +#else + if (errno == EINTR || errno == EAGAIN) +#endif + continue; + case 0: + return (res); + default: + pos += res; + } + } + return (pos); +} diff --git a/atomicio.h b/atomicio.h new file mode 100644 index 0000000..6c1f3ac --- /dev/null +++ b/atomicio.h @@ -0,0 +1,36 @@ + +/* + * Copied from OpenSSH 3.6.1p2, required for loginrec.c + * + * $OpenBSD: atomicio.h,v 1.4 2001/06/26 06:32:46 itojun Exp $ + * + * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "includes.h" + +/* + * Ensure all of data on socket comes through. f==read || f==write + */ +ssize_t atomicio(ssize_t (*)(), int, void *, size_t); diff --git a/auth.h b/auth.h new file mode 100644 index 0000000..8d2db3e --- /dev/null +++ b/auth.h @@ -0,0 +1,63 @@ +/* + * 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_H_ +#define _AUTH_H_ + +#include "includes.h" + +void authinitialise(); + +void recv_msg_userauth_request(); +void send_msg_userauth_failure(int partial, int incrfail); +void send_msg_userauth_success(); + +#define MAX_USERNAME_LEN 25 /* arbitrary for the moment */ + +#define AUTH_TYPE_PUBKEY 1 << 0 +#define AUTH_TYPE_PASSWORD 1 << 1 + +/* auth types, "none" means we should return list of acceptable types */ +#define AUTH_METHOD_NONE "none" +#define AUTH_METHOD_NONE_LEN 4 +#define AUTH_METHOD_PUBKEY "publickey" +#define AUTH_METHOD_PUBKEY_LEN 9 +#define AUTH_METHOD_PASSWORD "password" +#define AUTH_METHOD_PASSWORD_LEN 8 + +struct AuthState { + + char *username; /* This is the username the client presents to check. It + is updated each run through, used for auth checking */ + char *printableuser; /* stripped of control chars, used for logs etc */ + struct passwd * pw; + unsigned char authtypes; /* Flags indicating which auth types are still + valid */ + unsigned int failcount; /* Number of (failed) authentication attempts.*/ + unsigned authdone : 1; /* 0 if we haven't authed, 1 if we have */ + + +}; + +#endif /* _AUTH_H_ */ diff --git a/authpasswd.h b/authpasswd.h new file mode 100644 index 0000000..9738532 --- /dev/null +++ b/authpasswd.h @@ -0,0 +1,33 @@ +/* + * 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 --git a/authpubkey.h b/authpubkey.h new file mode 100644 index 0000000..bf9549e --- /dev/null +++ b/authpubkey.h @@ -0,0 +1,33 @@ +/* + * 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 --git a/bignum.c b/bignum.c new file mode 100644 index 0000000..4552d30 --- /dev/null +++ b/bignum.c @@ -0,0 +1,94 @@ +/* + * 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. */ + +/* Contains helper functions for mp_int handling */ + +#include "includes.h" +#include "dbutil.h" + +/* wrapper for mp_init, failing fatally on errors (memory allocation) */ +void m_mp_init(mp_int *mp) { + + if (mp_init(mp) != MP_OKAY) { + dropbear_exit("mem alloc error"); + } +} + +/* simplified duplication of bn_mp_multi's mp_init_multi, but die fatally + * on error */ +void m_mp_init_multi(mp_int *mp, ...) +{ + mp_int* cur_arg = mp; + va_list args; + + va_start(args, mp); /* init args to next argument from caller */ + while (cur_arg != NULL) { + if (mp_init(cur_arg) != MP_OKAY) { + dropbear_exit("mem alloc error"); + } + cur_arg = va_arg(args, mp_int*); + } + va_end(args); +} + +/* convert an unsigned mp into an array of bytes, malloced. + * This array must be freed after use, len contains the length of the array, + * if len != NULL */ +unsigned char* mptobytes(mp_int *mp, int *len) { + + unsigned char* ret; + int size; + + size = mp_unsigned_bin_size(mp); + ret = m_malloc(size); + if (mp_to_unsigned_bin(mp, ret) != MP_OKAY) { + dropbear_exit("mem alloc error"); + } + if (len != NULL) { + *len = size; + } + return ret; +} + +void bytestomp(mp_int *mp, unsigned char* bytes, unsigned int len) { + + if (mp_read_unsigned_bin(mp, bytes, len) != MP_OKAY) { + dropbear_exit("mem alloc error"); + } +} + +/* hash the ssh representation of the mp_int mp */ +void sha1_process_mp(hash_state *hs, mp_int *mp) { + + int i; + buffer * buf; + + buf = buf_new(512 + 20); /* max buffer is a 4096 bit key, + plus header + some leeway*/ + buf_putmpint(buf, mp); + i = buf->pos; + buf_setpos(buf, 0); + sha1_process(hs, buf_getptr(buf, i), i); + buf_free(buf); +} diff --git a/bignum.h b/bignum.h new file mode 100644 index 0000000..27f5e77 --- /dev/null +++ b/bignum.h @@ -0,0 +1,36 @@ +/* + * 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 _BIGNUM_H_ +#define _BIGNUM_H_ + +#include "includes.h" + +void m_mp_init(mp_int *mp); +void m_mp_init_multi(mp_int *mp, ...); +unsigned char* mptobytes(mp_int *mp, int *len); +void bytestomp(mp_int *mp, unsigned char* bytes, unsigned int len); +void sha1_process_mp(hash_state *hs, mp_int *mp); + +#endif /* _BIGNUM_H_ */ diff --git a/buffer.c b/buffer.c new file mode 100644 index 0000000..7181fca --- /dev/null +++ b/buffer.c @@ -0,0 +1,325 @@ +/* + * 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. */ + +/* Buffer handling routines, designed to avoid overflows/using invalid data */ + +#include "includes.h" +#include "dbutil.h" +#include "buffer.h" + +/* Prevent integer overflows when incrementing buffer position/length. + * Calling functions should check arguments first, but this provides a + * backstop */ +#define BUF_MAX_INCR 1000000000 +#define BUF_MAX_SIZE 1000000000 + +/* avoid excessively large numbers, > 5000 bit */ +#define BUF_MAX_MPINT (5000 / 8) + +/* Create (malloc) a new buffer of size */ +buffer* buf_new(unsigned int size) { + + buffer* buf; + + if (size > BUF_MAX_SIZE) { + dropbear_exit("buf->size too big"); + } + + buf = (buffer*)m_malloc(sizeof(buffer)); + + if (size > 0) { + buf->data = (unsigned char*)m_malloc(size); + } else { + buf->data = NULL; + } + + buf->size = size; + buf->pos = 0; + buf->len = 0; + + return buf; + +} + +/* free the buffer's data and the buffer itself */ +void buf_free(buffer* buf) { + + m_free(buf->data) + m_free(buf); +} + +/* overwrite the contents of the buffer to clear it */ +void buf_burn(buffer* buf) { + + m_burn(buf->data, buf->size); + +} + +/* resize a buffer, pos and len will be repositioned if required */ +void buf_resize(buffer *buf, unsigned int newsize) { + + if (newsize > BUF_MAX_SIZE) { + dropbear_exit("buf->size too big"); + } + + buf->data = m_realloc(buf->data, newsize); + buf->size = newsize; + buf->len = MIN(newsize, buf->len); + buf->pos = MIN(newsize, buf->pos); + +} + +/* Create a copy of buf, allocating required memory etc. */ +/* The new buffer is sized the same as the length of the source buffer. */ +buffer* buf_newcopy(buffer* buf) { + + buffer* ret; + + ret = buf_new(buf->len); + ret->len = buf->len; + memcpy(ret->data, buf->data, buf->len); + return ret; +} + +/* Set the length of the buffer */ +void buf_setlen(buffer* buf, unsigned int len) { + if (len > buf->size) { + dropbear_exit("bad buf_setlen"); + } + buf->len = len; +} + +/* Increment the length of the buffer */ +void buf_incrlen(buffer* buf, unsigned int incr) { + if (incr > BUF_MAX_INCR || buf->len + incr > buf->size) { + dropbear_exit("bad buf_incrlen"); + } + buf->len += incr; +} +/* Set the position of the buffer */ +void buf_setpos(buffer* buf, unsigned int pos) { + + if (pos > buf->len) { + dropbear_exit("bad buf_setpos"); + } + buf->pos = pos; +} + +/* increment the postion by incr, increasing the buffer length if required */ +void buf_incrwritepos(buffer* buf, unsigned int incr) { + if (incr > BUF_MAX_INCR || buf->pos + incr > buf->size) { + dropbear_exit("bad buf_incrwritepos"); + } + buf->pos += incr; + if (buf->pos > buf->len) { + buf->len = buf->pos; + } +} + +/* increment the position by incr, negative values are allowed, to + * decrement the pos*/ +void buf_incrpos(buffer* buf, int incr) { + if (incr > BUF_MAX_INCR || + (unsigned int)((int)buf->pos + incr) > buf->len + || ((int)buf->pos + incr) < 0) { + dropbear_exit("bad buf_incrpos"); + } + buf->pos += incr; +} + +/* Get a byte from the buffer and increment the pos */ +unsigned char buf_getbyte(buffer* buf) { + + if (buf->pos >= buf->len) { + dropbear_exit("bad buf_getbyte"); + } + return buf->data[buf->pos++]; +} + +/* put a byte, incrementing the length if required */ +void buf_putbyte(buffer* buf, unsigned char val) { + + if (buf->pos >= buf->len) { + buf_incrlen(buf, 1); + } + buf->data[buf->pos] = val; + buf->pos++; +} + +/* returns an in-place pointer to the buffer, checking that + * the next len bytes from that position can be used */ +unsigned char* buf_getptr(buffer* buf, unsigned int len) { + + if (buf->pos + len > buf->len) { + dropbear_exit("bad buf_getptr"); + } + return &buf->data[buf->pos]; +} + +/* like buf_getptr, but checks against total size, not used length. + * This allows writing past the used length, but not past the size */ +unsigned char* buf_getwriteptr(buffer* buf, unsigned int len) { + + if (buf->pos + len > buf->size) { + dropbear_exit("bad buf_getwriteptr"); + } + return &buf->data[buf->pos]; +} + +/* Return a null-terminated string, it is malloced, so must be free()ed + * Note that the string isn't checked for null bytes, hence the retlen + * may be longer than what is returned by strlen */ +unsigned char* buf_getstring(buffer* buf, unsigned int *retlen) { + + unsigned int len; + unsigned char* ret; + len = buf_getint(buf); + if (len > MAX_STRING_LEN) { + dropbear_exit("string too long"); + } + + if (retlen != NULL) { + *retlen = len; + } + ret = m_malloc(len+1); + memcpy(ret, buf_getptr(buf, len), len); + buf_incrpos(buf, len); + ret[len] = '\0'; + + return ret; +} + +/* Just increment the buffer position the same as if we'd used buf_getstring, + * but don't bother copying/malloc()ing for it */ +void buf_eatstring(buffer *buf) { + + buf_incrpos( buf, buf_getint(buf) ); +} + +/* Get an uint32 from the buffer and increment the pos */ +unsigned int buf_getint(buffer* buf) { + unsigned int ret; + + LOAD32H(ret, buf_getptr(buf, 4)); + buf_incrpos(buf, 4); + return ret; +} + +/* put a 32bit uint into the buffer, incr bufferlen & pos if required */ +void buf_putint(buffer* buf, int unsigned val) { + + STORE32H(val, buf_getwriteptr(buf, 4)); + buf_incrwritepos(buf, 4); + +} + +/* put a SSH style string into the buffer, increasing buffer len if required */ +void buf_putstring(buffer* buf, const unsigned char* str, unsigned int len) { + + buf_putint(buf, len); + buf_putbytes(buf, str, len); + +} + +/* put the set of len bytes into the buffer, incrementing the pos, increasing + * len if required */ +void buf_putbytes(buffer *buf, const unsigned char *bytes, unsigned int len) { + memcpy(buf_getwriteptr(buf, len), bytes, len); + buf_incrwritepos(buf, len); +} + + +/* for our purposes we only need positive (or 0) numbers, so will + * fail if we get negative numbers */ +void buf_putmpint(buffer* buf, mp_int * mp) { + + unsigned int len, pad = 0; + TRACE(("enter buf_putmpint")); + + assert(mp != NULL); + + if (SIGN(mp) == MP_NEG) { + dropbear_exit("negative bignum"); + } + + /* zero check */ + if (USED(mp) == 1 && DIGIT(mp, 0) == 0) { + len = 0; + } else { + /* SSH spec requires padding for mpints with the MSB set, this code + * implements it */ + len = mp_count_bits(mp); + /* if the top bit of MSB is set, we need to pad */ + pad = (len%8 == 0) ? 1 : 0; + len = len / 8 + 1; /* don't worry about rounding, we need it for + padding anyway when len%8 == 0 */ + + } + + /* store the length */ + buf_putint(buf, len); + + /* store the actual value */ + if (len > 0) { + if (pad) { + buf_putbyte(buf, 0x00); + } + if (mp_to_unsigned_bin(mp, buf_getwriteptr(buf, len-pad)) != MP_OKAY) { + dropbear_exit("mpint error"); + } + buf_incrwritepos(buf, len-pad); + } + + TRACE(("leave buf_putmpint")); +} + +/* Retrieve an mp_int from the buffer. + * Will fail for -ve since they shouldn't be required here. + * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +int buf_getmpint(buffer* buf, mp_int* mp) { + + unsigned int len; + len = buf_getint(buf); + + if (len == 0) { + mp_zero(mp); + return DROPBEAR_SUCCESS; + } + + if (len > BUF_MAX_MPINT) { + return DROPBEAR_FAILURE; + } + + /* check for negative */ + if (*buf_getptr(buf, 1) & (1 << (CHAR_BIT-1))) { + return DROPBEAR_FAILURE; + } + + if (mp_read_unsigned_bin(mp, buf_getptr(buf, len), len) != MP_OKAY) { + return DROPBEAR_FAILURE; + } + + buf_incrpos(buf, len); + return DROPBEAR_SUCCESS; +} diff --git a/buffer.h b/buffer.h new file mode 100644 index 0000000..e1031a1 --- /dev/null +++ b/buffer.h @@ -0,0 +1,65 @@ +/* + * 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 _BUFFER_H_ + +#define _BUFFER_H_ + +#include "includes.h" + +struct buf { + + unsigned char * data; + unsigned int len; /* the used size */ + unsigned int pos; + unsigned int size; /* the memory size */ + +}; + +typedef struct buf buffer; + +buffer * buf_new(unsigned int size); +void buf_resize(buffer *buf, unsigned int newsize); +void buf_free(buffer* buf); +void buf_burn(buffer* buf); +buffer* buf_newcopy(buffer* buf); +void buf_setlen(buffer* buf, unsigned int len); +void buf_incrlen(buffer* buf, unsigned int incr); +void buf_setpos(buffer* buf, unsigned int pos); +void buf_incrpos(buffer* buf, int incr); /* -ve is ok, to go backwards */ +void buf_incrwritepos(buffer* buf, unsigned int incr); +unsigned char buf_getbyte(buffer* buf); +void buf_putbyte(buffer* buf, unsigned char val); +unsigned char* buf_getptr(buffer* buf, unsigned int len); +unsigned char* buf_getwriteptr(buffer* buf, unsigned int len); +unsigned char* buf_getstring(buffer* buf, unsigned int *retlen); +void buf_eatstring(buffer *buf); +void buf_putint(buffer* buf, unsigned int val); +void buf_putstring(buffer* buf, const unsigned char* str, unsigned int len); +void buf_putbytes(buffer *buf, const unsigned char *bytes, unsigned int len); +void buf_putmpint(buffer* buf, mp_int * mp); +int buf_getmpint(buffer* buf, mp_int* mp); +unsigned int buf_getint(buffer* buf); + +#endif /* _BUFFER_H_ */ diff --git a/channel.h b/channel.h new file mode 100644 index 0000000..750a9bb --- /dev/null +++ b/channel.h @@ -0,0 +1,122 @@ +/* + * 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 _CHANNEL_H_ +#define _CHANNEL_H_ + +#include "includes.h" +#include "buffer.h" + +/* channel->type values */ +#define CHANNEL_ID_NONE 0 +#define CHANNEL_ID_SESSION 1 +#define CHANNEL_ID_X11 2 +#define CHANNEL_ID_AGENT 3 +#define CHANNEL_ID_TCPDIRECT 4 +#define CHANNEL_ID_TCPFORWARDED 5 + +#define SSH_OPEN_ADMINISTRATIVELY_PROHIBITED 1 +#define SSH_OPEN_CONNECT_FAILED 2 +#define SSH_OPEN_UNKNOWN_CHANNEL_TYPE 3 +#define SSH_OPEN_RESOURCE_SHORTAGE 4 + +#define MAX_CHANNELS 60 /* 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 */ + +/* a simpler way to define that we need code for listeners */ +#if !defined(DISABLE_X11FWD) || !defined(DISABLE_AUTHFWD) || \ + !defined(DISABLE_REMOTETCPFWD) +#define USE_LISTENERS +#endif + +struct ChanType; + +struct Channel { + + unsigned int index; /* the local channel index */ + unsigned int remotechan; + unsigned int recvwindow, transwindow; + 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 sentclosed, recvclosed; + + /* this is set when we receive/send a channel eof packet */ + int recveof, senteof; + + int initconn; /* used for TCP forwarding, whether the channel has been + fully initialised */ + + struct ChanType* type; + +}; + +struct ChanType { + + int sepfds; /* Whether this channel has seperate pipes for in/out or not */ + char *name; + int (*inithandler)(struct Channel*); + int (*checkclose)(struct Channel*); + void (*reqhandler)(struct Channel*); + void (*closehandler)(struct Channel*); + +}; + +void chaninitialise(); +void chancleanup(); +void setchannelfds(fd_set *readfd, fd_set *writefd); +void channelio(fd_set *readfd, fd_set *writefd); +struct Channel* newchannel(unsigned int remotechan, struct ChanType *type, + unsigned int transwindow, unsigned int transmaxpacket); + +void recv_msg_channel_open(); +void recv_msg_channel_request(); +void send_msg_channel_failure(struct Channel *channel); +void send_msg_channel_success(struct Channel *channel); +void recv_msg_channel_data(); +void recv_msg_channel_window_adjust(); +void recv_msg_channel_close(); +void recv_msg_channel_eof(); + +#ifdef USE_LISTENERS +int send_msg_channel_open_init(int fd, struct ChanType *type, + const char * typestring); +void recv_msg_channel_open_confirmation(); +void recv_msg_channel_open_failure(); +#endif + +#endif /* _CHANNEL_H_ */ diff --git a/chansession.h b/chansession.h new file mode 100644 index 0000000..1e4ba9a --- /dev/null +++ b/chansession.h @@ -0,0 +1,89 @@ +/* + * 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 _CHANSESSION_H_ +#define _CHANSESSION_H_ + +#include "loginrec.h" +#include "channel.h" + +struct ChanSess { + + unsigned char * cmd; /* command to exec */ + pid_t pid; /* child process pid */ + + /* pty details */ + int master; /* the master terminal fd*/ + int slave; + unsigned char * tty; + unsigned char * term; + unsigned int termw, termh, termc, termr; /* width, height, col, rows */ + + /* exit details */ + int exited; + int exitstatus; + int exitsignal; + unsigned char exitcore; + +#ifndef DISABLE_X11FWD + int x11fd; /* set to -1 to indicate forwarding not established */ + int x11port; + char * x11authprot; + char * x11authcookie; + unsigned int x11screennum; + unsigned char x11singleconn; +#endif + +#ifndef DISABLE_AGENTFWD + int agentfd; + char * agentfile; + char * agentdir; +#endif +}; + +struct ChildPid { + pid_t pid; + struct ChanSess * chansess; +}; + + +void newchansess(struct Channel * channel); +void chansessionrequest(struct Channel * channel); +void closechansess(struct Channel * channel); +void svr_chansessinitialise(); +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); + + +struct SigMap { + int signal; + char* name; +}; + +extern const struct SigMap signames[]; + +#endif /* _CHANSESSION_H_ */ diff --git a/cli_algo.c b/cli_algo.c new file mode 100644 index 0000000..ba1ed85 --- /dev/null +++ b/cli_algo.c @@ -0,0 +1,92 @@ +/* + * 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" + + +/* + * The chosen [encryption | MAC | compression] algorithm to each + * direction MUST be the first algorithm on the client's list + * that is also on the server's list. + */ +algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[]) { + + unsigned char * algolist = NULL; + unsigned char * remotealgos[MAX_PROPOSED_ALGO]; + unsigned int len; + unsigned int count, i, j; + algo_type * ret = NULL; + + /* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */ + algolist = buf_getstring(buf, &len); + TRACE(("cli_buf_match_algo: %s", algolist)); + if (len > MAX_PROPOSED_ALGO*(MAX_NAME_LEN+1)) { + goto out; /* just a sanity check, no other use */ + } + + /* remotealgos will contain a list of the strings parsed out */ + /* We will have at least one string (even if it's just "") */ + remotealgos[0] = algolist; + count = 1; + /* Iterate through, replacing ','s with NULs, to split it into + * words. */ + for (i = 0; i < len; i++) { + if (algolist[i] == '\0') { + /* someone is trying something strange */ + goto out; + } + if (algolist[i] == ',') { + algolist[i] = '\0'; + remotealgos[count] = &algolist[i+1]; + count++; + } + if (count == MAX_PROPOSED_ALGO) { + break; + } + } + + /* iterate and find the first match */ + + for (j = 0; localalgos[j].name != NULL; j++) { + if (localalgos[j].usable) { + len = strlen(localalgos[j].name); + for (i = 0; i < count; i++) { + if (len == strlen(remotealgos[i]) + && strncmp(localalgos[j].name, + remotealgos[i], len) == 0) { + ret = &localalgos[j]; + goto out; + } + } + } + } + +out: + m_free(algolist); + return ret; +} + diff --git a/common-algo.c b/common-algo.c new file mode 100644 index 0000000..f71fd33 --- /dev/null +++ b/common-algo.c @@ -0,0 +1,206 @@ +/* + * 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. */ + +#include "algo.h" +#include "dbutil.h" + +/* This file (algo.c) organises the ciphers which can be used, and is used to + * decide which ciphers/hashes/compression/signing to use during key exchange*/ + +/* Mappings for ciphers, parameters are + {&cipher_desc, keysize, blocksize} */ + +#ifdef DROPBEAR_AES128_CBC +const struct dropbear_cipher dropbear_aes128 = + {&rijndael_desc, 16, 16}; +#endif +#ifdef DROPBEAR_BLOWFISH_CBC +const struct dropbear_cipher dropbear_blowfish = + {&blowfish_desc, 16, 8}; +#endif +#ifdef DROPBEAR_TWOFISH128_CBC +const struct dropbear_cipher dropbear_twofish128 = + {&twofish_desc, 16, 16}; +#endif +#ifdef DROPBEAR_3DES_CBC +const struct dropbear_cipher dropbear_3des = + {&des3_desc, 24, 8}; +#endif + +/* used to indicate no encryption, as defined in rfc2410 */ +const struct dropbear_cipher dropbear_nocipher = + {NULL, 16, 8}; + +/* Mapping of ssh hashes to libtomcrypt hashes, including keysize etc. + {&hash_desc, keysize, hashsize} */ + +#ifdef DROPBEAR_SHA1_HMAC +const struct dropbear_hash dropbear_sha1 = + {&sha1_desc, 20, 20}; +#endif +#ifdef DROPBEAR_MD5_HMAC +const struct dropbear_hash dropbear_md5 = + {&md5_desc, 16, 16}; +#endif + +const struct dropbear_hash dropbear_nohash = + {NULL, 16, 0}; /* used initially */ + + +/* The following map ssh names to internal values */ + +algo_type sshciphers[] = { +#ifdef DROPBEAR_AES128_CBC + {"aes128-cbc", 0, (void*)&dropbear_aes128, 1}, +#endif +#ifdef DROPBEAR_BLOWFISH_CBC + {"blowfish-cbc", 0, (void*)&dropbear_blowfish, 1}, +#endif +#ifdef DROPBEAR_TWOFISH128_CBC + {"twofish-cbc", 0, (void*)&dropbear_twofish128, 1}, +#endif +#ifdef DROPBEAR_3DES_CBC + {"3des-cbc", 0, (void*)&dropbear_3des, 1}, +#endif + {NULL, 0, NULL, 0} +}; + +algo_type sshhashes[] = { +#ifdef DROPBEAR_SHA1_HMAC + {"hmac-sha1", 0, (void*)&dropbear_sha1, 1}, +#endif +#ifdef DROPBEAR_MD5_HMAC + {"hmac-md5", 0, (void*)&dropbear_md5, 1}, +#endif + {NULL, 0, NULL, 0} +}; + +algo_type sshcompress[] = { + {"none", DROPBEAR_COMP_NONE, NULL, 1}, +#ifndef DISABLE_ZLIB + {"zlib", DROPBEAR_COMP_ZLIB, NULL, 1}, +#endif + {NULL, 0, NULL, 0} +}; + +algo_type sshhostkey[] = { +#ifdef DROPBEAR_RSA + {"ssh-rsa", DROPBEAR_SIGNKEY_RSA, NULL, 1}, +#endif +#ifdef DROPBEAR_DSS + {"ssh-dss", DROPBEAR_SIGNKEY_DSS, NULL, 1}, +#endif + {NULL, 0, NULL, 0} +}; + +algo_type sshkex[] = { + {"diffie-hellman-group1-sha1", DROPBEAR_KEX_DH_GROUP1, NULL, 1}, + {NULL, 0, NULL, 0} +}; + + +/* Register the compiled in ciphers. + * This should be run before using any of the ciphers/hashes */ +void crypto_init() { + + const struct _cipher_descriptor *regciphers[] = { +#ifdef DROPBEAR_AES128_CBC + &rijndael_desc, +#endif +#ifdef DROPBEAR_BLOWFISH_CBC + &blowfish_desc, +#endif +#ifdef DROPBEAR_TWOFISH128_CBC + &twofish_desc, +#endif +#ifdef DROPBEAR_3DES_CBC + &des3_desc, +#endif + NULL + }; + + const struct _hash_descriptor *reghashes[] = { + /* we need sha1 for hostkey stuff regardless */ + &sha1_desc, +#ifdef DROPBEAR_MD5_HMAC + &md5_desc, +#endif + NULL + }; + int i; + + for (i = 0; regciphers[i] != NULL; i++) { + if (register_cipher(regciphers[i]) == -1) { + dropbear_exit("error registering crypto"); + } + } + + for (i = 0; reghashes[i] != NULL; i++) { + if (register_hash(reghashes[i]) == -1) { + dropbear_exit("error registering crypto"); + } + } +} + +/* algolen specifies the length of algo, algos is our local list to match + * against. + * Returns DROPBEAR_SUCCESS if we have a match for algo, DROPBEAR_FAILURE + * otherwise */ +int have_algo(char* algo, size_t algolen, algo_type algos[]) { + + int i; + + for (i = 0; algos[i].name != NULL; i++) { + if (strlen(algos[i].name) == algolen + && (strncmp(algos[i].name, algo, algolen) == 0)) { + return DROPBEAR_SUCCESS; + } + } + + return DROPBEAR_FAILURE; +} + + + +/* Output a comma separated list of algorithms to a buffer */ +void buf_put_algolist(buffer * buf, algo_type localalgos[]) { + + unsigned int pos = 0, i, len; + char str[50]; /* enough for local algo storage */ + + for (i = 0; localalgos[i].name != NULL; i++) { + if (localalgos[i].usable) { + /* Avoid generating a trailing comma */ + if (pos) + str[pos++] = ','; + len = strlen(localalgos[i].name); + memcpy(&str[pos], localalgos[i].name, len); + pos += len; + } + } + str[pos]=0; + /* Debug this */ + TRACE(("buf_put_algolist: %s", str)); + buf_putstring(buf, str, pos); +} diff --git a/common-channel.c b/common-channel.c new file mode 100644 index 0000000..8b5423a --- /dev/null +++ b/common-channel.c @@ -0,0 +1,1008 @@ +/* + * 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. */ + +/* Handle the multiplexed channels, such as sessions, x11, agent connections */ + +#include "includes.h" +#include "session.h" +#include "packet.h" +#include "ssh.h" +#include "buffer.h" +#include "dbutil.h" +#include "channel.h" +#include "ssh.h" +#include "localtcpfwd.h" +#include "remotetcpfwd.h" +#include "tcpfwd.h" + +static void send_msg_channel_open_failure(unsigned int remotechan, int reason, + const unsigned char *text, const unsigned char *lang); +static void send_msg_channel_open_confirmation(struct Channel* channel, + unsigned int recvwindow, + unsigned int recvmaxpacket); +static void writechannel(struct Channel *channel); +static void send_msg_channel_window_adjust(struct Channel *channel, + unsigned int incr); +static void send_msg_channel_data(struct Channel *channel, int isextended, + unsigned int exttype); +static void send_msg_channel_eof(struct Channel *channel); +static void send_msg_channel_close(struct Channel *channel); +static void removechannel(struct Channel *channel); +static void deletechannel(struct Channel *channel); +static void checkinitdone(struct Channel *channel); +static void checkclose(struct Channel *channel); + +static void closeinfd(struct Channel * channel); +static void closeoutfd(struct Channel * channel, int fd); +static void closechanfd(struct Channel *channel, int fd, int how); + +#define FD_UNINIT (-2) +#define FD_CLOSED (-1) + +/* Initialise all the channels */ +void chaninitialise(struct ChanType *chantypes[]) { + + /* may as well create space for a single channel */ + ses.channels = (struct Channel**)m_malloc(sizeof(struct Channel*)); + ses.chansize = 1; + ses.channels[0] = NULL; + + ses.chantypes = chantypes; + +#ifdef USING_TCP_LISTENERS + tcp_fwd_initialise(); +#endif + +} + +/* Clean up channels, freeing allocated memory */ +void chancleanup() { + + unsigned int i; + + TRACE(("enter chancleanup")); + for (i = 0; i < ses.chansize; i++) { + if (ses.channels[i] != NULL) { + TRACE(("channel %d closing", i)); + removechannel(ses.channels[i]); + } + } + m_free(ses.channels); + TRACE(("leave chancleanup")); +} + +/* Create a new channel entry, send a reply confirm or failure */ +/* If remotechan, transwindow and transmaxpacket are not know (for a new + * outgoing connection, with them to be filled on confirmation), they should + * all be set to 0 */ +struct Channel* newchannel(unsigned int remotechan, struct ChanType *type, + unsigned int transwindow, unsigned int transmaxpacket) { + + struct Channel * newchan; + unsigned int i, j; + + TRACE(("enter newchannel")); + + /* first see if we can use existing channels */ + for (i = 0; i < ses.chansize; i++) { + if (ses.channels[i] == NULL) { + break; + } + } + + /* otherwise extend the list */ + if (i == ses.chansize) { + if (ses.chansize >= MAX_CHANNELS) { + TRACE(("leave newchannel: max chans reached")); + return NULL; + } + + /* extend the channels */ + ses.channels = (struct Channel**)m_realloc(ses.channels, + (ses.chansize+CHAN_EXTEND_SIZE)*sizeof(struct Channel*)); + + ses.chansize += CHAN_EXTEND_SIZE; + + /* set the new channels to null */ + for (j = i; j < ses.chansize; j++) { + ses.channels[j] = NULL; + } + + } + + newchan = (struct Channel*)m_malloc(sizeof(struct Channel)); + newchan->type = type; + newchan->index = i; + newchan->sentclosed = newchan->recvclosed = 0; + newchan->senteof = newchan->recveof = 0; + + newchan->remotechan = remotechan; + newchan->transwindow = transwindow; + newchan->transmaxpacket = transmaxpacket; + + newchan->typedata = NULL; + newchan->infd = FD_UNINIT; + newchan->outfd = FD_UNINIT; + newchan->errfd = FD_CLOSED; /* this isn't always set to start with */ + newchan->initconn = 0; + + newchan->writebuf = buf_new(RECV_MAXWINDOW); + newchan->recvwindow = RECV_MAXWINDOW; + newchan->recvmaxpacket = RECV_MAXPACKET; + + ses.channels[i] = newchan; + + TRACE(("leave newchannel")); + + return newchan; +} + +/* Get the channel structure corresponding to a channel number */ +static struct Channel* getchannel(unsigned int chan) { + if (chan >= ses.chansize || ses.channels[chan] == NULL) { + return NULL; + } + return ses.channels[chan]; +} + +/* Iterate through the channels, performing IO if available */ +void channelio(fd_set *readfd, fd_set *writefd) { + + struct Channel *channel; + unsigned int i; + + /* iterate through all the possible channels */ + for (i = 0; i < ses.chansize; i++) { + + channel = ses.channels[i]; + if (channel == NULL) { + /* only process in-use channels */ + continue; + } + + /* read from program/pipe stdout */ + if (channel->outfd >= 0 && FD_ISSET(channel->outfd, readfd)) { + send_msg_channel_data(channel, 0, 0); + } + + /* read from program/pipe stderr */ + if (channel->errfd >= 0 && FD_ISSET(channel->errfd, readfd)) { + send_msg_channel_data(channel, 1, SSH_EXTENDED_DATA_STDERR); + } + + /* if we can read from the infd, it might be closed, so we try to + * 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 (ret < 0 && errno != EINTR && errno != EAGAIN) { + closeinfd(channel); + } + } + + /* write to program/pipe stdin */ + if (channel->infd >= 0 && FD_ISSET(channel->infd, writefd)) { + if (channel->initconn) { + checkinitdone(channel); + continue; /* Important not to use the channel after + checkinitdone(), as it may be NULL */ + } else { + writechannel(channel); + } + } + + /* now handle any of the channel-closing type stuff */ + checkclose(channel); + + } /* foreach channel */ + + /* Not channel specific */ +#ifdef USING_TCP_LISTENERS + handle_tcp_fwd(readfd); +#endif +} + + +/* do all the EOF/close type stuff checking for a channel */ +static void checkclose(struct Channel *channel) { + + if (!channel->sentclosed) { + + /* check for exited - currently only used for server sessions, + * if the shell has exited etc */ + if (channel->type->checkclose) { + if (channel->type->checkclose(channel)) { + closeinfd(channel); + } + } + + if (!channel->senteof + && channel->outfd == FD_CLOSED + && channel->errfd == FD_CLOSED) { + send_msg_channel_eof(channel); + } + + if (channel->infd == FD_CLOSED + && channel->outfd == FD_CLOSED + && channel->errfd == FD_CLOSED) { + send_msg_channel_close(channel); + } + } + + /* When either party wishes to terminate the channel, it sends + * SSH_MSG_CHANNEL_CLOSE. Upon receiving this message, a party MUST + * send back a SSH_MSG_CHANNEL_CLOSE unless it has already sent this + * message for the channel. The channel is considered closed for a + * party when it has both sent and received SSH_MSG_CHANNEL_CLOSE, and + * the party may then reuse the channel number. A party MAY send + * SSH_MSG_CHANNEL_CLOSE without having sent or received + * SSH_MSG_CHANNEL_EOF. + * (from draft-ietf-secsh-connect) + */ + if (channel->recvclosed) { + if (! channel->sentclosed) { + TRACE(("Sending MSG_CHANNEL_CLOSE in response to same.")); + send_msg_channel_close(channel); + } + removechannel(channel); + } +} + + +/* Check whether a deferred (EINPROGRESS) connect() was successful, and + * if so, set up the channel properly. Otherwise, the channel is cleaned up, so + * it is important that the channel reference isn't used after a call to this + * function */ +static void checkinitdone(struct Channel *channel) { + + int val; + socklen_t vallen = sizeof(val); + + TRACE(("enter checkinitdone")); + + if (getsockopt(channel->infd, SOL_SOCKET, SO_ERROR, &val, &vallen) + || val != 0) { + close(channel->infd); + deletechannel(channel); + TRACE(("leave checkinitdone: fail")); + } else { + channel->outfd = channel->infd; + channel->initconn = 0; + TRACE(("leave checkinitdone: success")); + } +} + + + +/* Send the close message and set the channel as closed */ +static void send_msg_channel_close(struct Channel *channel) { + + TRACE(("enter send_msg_channel_close")); + /* XXX server */ + if (channel->type->closehandler) { + channel->type->closehandler(channel); + } +#if 0 + if (channel->type == CHANNEL_ID_SESSION) { + send_exitsignalstatus(channel); + + closechansess(channel); + } +#endif + + CHECKCLEARTOWRITE(); + + buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_CLOSE); + buf_putint(ses.writepayload, channel->remotechan); + + encrypt_packet(); + + channel->senteof = 1; + channel->sentclosed = 1; + TRACE(("leave send_msg_channel_close")); +} + +/* call this when trans/eof channels are closed */ +static void send_msg_channel_eof(struct Channel *channel) { + + TRACE(("enter send_msg_channel_eof")); + CHECKCLEARTOWRITE(); + + buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_EOF); + buf_putint(ses.writepayload, channel->remotechan); + + encrypt_packet(); + + channel->senteof = 1; + + TRACE(("leave send_msg_channel_eof")); +} + +/* Called to write data out to the server side of a channel (eg a shell or a + * program. + * Only called when we know we can write to a channel, writes as much as + * possible */ +static void writechannel(struct Channel* channel) { + + int len, maxlen; + buffer *buf; + + TRACE(("enter writechannel")); + + buf = channel->writebuf; + maxlen = buf->len - buf->pos; + + len = write(channel->infd, buf_getptr(buf, maxlen), maxlen); + if (len <= 0) { + if (len < 0 && errno != EINTR) { + /* no more to write */ + closeinfd(channel); + } + TRACE(("leave writechannel: len <= 0")); + return; + } + + if (len == maxlen) { + buf_setpos(buf, 0); + buf_setlen(buf, 0); + + if (channel->recveof) { + /* we're closing up */ + closeinfd(channel); + return; + TRACE(("leave writechannel: recveof set")); + } + + /* 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); + } + TRACE(("leave writechannel")); +} + +/* Set the file descriptors for the main select in session.c + * This avoid channels which don't have any window available, are closed, etc*/ +void setchannelfds(fd_set *readfd, fd_set *writefd) { + + unsigned int i; + struct Channel * channel; + + for (i = 0; i < ses.chansize; i++) { + + channel = ses.channels[i]; + if (channel == NULL) { + continue; + } + + /* stdout and stderr */ + 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) { + FD_SET(channel->errfd, readfd); + } + } + + 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); + } + + } /* foreach channel */ + +#ifdef USING_TCP_LISTENERS + set_tcp_fwd_fds(readfd); +#endif + +} + +/* handle the channel EOF event, by closing the channel filedescriptor. The + * channel isn't closed yet, it is left until the incoming (from the program + * etc) FD is also EOF */ +void recv_msg_channel_eof() { + + unsigned int chan; + struct Channel * channel; + + TRACE(("enter recv_msg_channel_eof")); + + chan = buf_getint(ses.payload); + channel = getchannel(chan); + + if (channel == NULL) { + dropbear_exit("EOF for unknown channel"); + } + + channel->recveof = 1; + if (channel->writebuf->len == 0) { + closeinfd(channel); + } + + TRACE(("leave recv_msg_channel_eof")); +} + + +/* Handle channel closure(), respond in kind and close the channels */ +void recv_msg_channel_close() { + + unsigned int chan; + struct Channel * channel; + + TRACE(("enter recv_msg_channel_close")); + + chan = buf_getint(ses.payload); + TRACE(("close channel = %d", chan)); + channel = getchannel(chan); + + if (channel == NULL) { + /* disconnect ? */ + dropbear_exit("Close for unknown channel"); + } + + channel->recveof = 1; + channel->recvclosed = 1; + + if (channel->sentclosed) { + removechannel(channel); + } + + TRACE(("leave recv_msg_channel_close")); +} + +/* Remove a channel entry, this is only executed after both sides have sent + * channel close */ +static void removechannel(struct Channel * channel) { + + TRACE(("enter removechannel")); + TRACE(("channel index is %d", channel->index)); + + buf_free(channel->writebuf); + + /* 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); + } + + deletechannel(channel); + + TRACE(("leave removechannel")); +} + +/* Remove a channel entry */ +static void deletechannel(struct Channel *channel) { + + ses.channels[channel->index] = NULL; + m_free(channel); + +} + + +/* Handle channel specific requests, passing off to corresponding handlers + * such as chansession or x11fwd */ +void recv_msg_channel_request() { + + unsigned int chan; + struct Channel *channel; + + TRACE(("enter recv_msg_channel_request")); + + chan = buf_getint(ses.payload); + channel = getchannel(chan); + + if (channel == NULL) { + /* disconnect ? */ + dropbear_exit("Unknown channel"); + } + + TRACE(("chan type is %d", channel->type)); + + if (channel->type->reqhandler) { + channel->type->reqhandler(channel); + } else { + 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")); + +} + +/* Reads data from the server's program/shell/etc, and puts it in a + * channel_data packet to send. + * chan is the remote channel, isextended is 0 if it is normal data, 1 + * if it is extended data. if it is extended, then the type is in + * exttype */ +static void send_msg_channel_data(struct Channel *channel, int isextended, + unsigned int exttype) { + + buffer *buf; + int len; + unsigned int maxlen; + int fd; + +/* TRACE(("enter send_msg_channel_data")); + TRACE(("extended = %d type = %d", isextended, exttype));*/ + + CHECKCLEARTOWRITE(); + + assert(!channel->sentclosed); + + if (isextended) { + fd = channel->errfd; + } else { + fd = channel->outfd; + } + assert(fd >= 0); + + maxlen = MIN(channel->transwindow, channel->transmaxpacket); + /* -(1+4+4) is SSH_MSG_CHANNEL_DATA, channel number, string length, and + * exttype if is extended */ + maxlen = MIN(maxlen, + ses.writepayload->size - 1 - 4 - 4 - (isextended ? 4 : 0)); + if (maxlen == 0) { + TRACE(("leave send_msg_channel_data: no window")); + return; /* the data will get written later */ + } + + /* read the data */ + buf = buf_new(maxlen); + 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")); + } + buf_free(buf); + return; + } + buf_incrlen(buf, len); + + buf_putbyte(ses.writepayload, + isextended ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA); + buf_putint(ses.writepayload, channel->remotechan); + + if (isextended) { + buf_putint(ses.writepayload, exttype); + } + + buf_putstring(ses.writepayload, buf_getptr(buf, len), len); + buf_free(buf); + + channel->transwindow -= len; + + encrypt_packet(); + TRACE(("leave send_msg_channel_data")); +} + + +/* when we receive channel data, put it in a buffer for writing to the program/ + * shell etc */ +void recv_msg_channel_data() { + + unsigned int chan; + struct Channel * channel; + unsigned int datalen; + unsigned int pos; + unsigned int maxdata; + + TRACE(("enter recv_msg_channel_data")); + + chan = buf_getint(ses.payload); + channel = getchannel(chan); + if (channel == NULL) { + dropbear_exit("Unknown channel"); + } + + if (channel->recveof) { + dropbear_exit("received data after eof"); + } + + if (channel->infd < 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; + if (datalen > maxdata) { + TRACE(("Warning: recv_msg_channel_data: extra data past window")); + datalen = maxdata; + } + + /* 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 */ + + channel->recvwindow -= datalen; + + /* matt - this might be for later */ +/* if (channel->recvwindow < RECV_MINWINDOW) { + send_msg_channel_window_adjust(channel, + RECV_MAXWINDOW - channel->recvwindow); + channel->recvwindow = RECV_MAXWINDOW; + }*/ + + TRACE(("leave recv_msg_channel_data")); +} + +/* Increment the outgoing data window for a channel - the remote end limits + * the amount of data which may be transmitted, this window is decremented + * as data is sent, and incremented upon receiving window-adjust messages */ +void recv_msg_channel_window_adjust() { + + unsigned int chan; + struct Channel * channel; + unsigned int incr; + + chan = buf_getint(ses.payload); + channel = getchannel(chan); + + if (channel == NULL) { + dropbear_exit("Unknown channel"); + } + + incr = buf_getint(ses.payload); + TRACE(("received window increment %d", incr)); + incr = MIN(incr, MAX_TRANS_WIN_INCR); + + channel->transwindow += incr; + channel->transwindow = MIN(channel->transwindow, MAX_TRANS_WINDOW); + +} + +/* Increment the incoming data window for a channel, and let the remote + * end know */ +static void send_msg_channel_window_adjust(struct Channel* channel, + unsigned int incr) { + + TRACE(("sending window adjust %d", incr)); + CHECKCLEARTOWRITE(); + + buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_WINDOW_ADJUST); + buf_putint(ses.writepayload, channel->remotechan); + buf_putint(ses.writepayload, incr); + + encrypt_packet(); +} + +/* Handle a new channel request, performing any channel-type-specific setup */ +/* XXX server */ +void recv_msg_channel_open() { + + unsigned char *type; + unsigned int typelen; + unsigned int remotechan, transwindow, transmaxpacket; + struct Channel *channel; + struct ChanType *chantype; + unsigned int errtype = SSH_OPEN_UNKNOWN_CHANNEL_TYPE; + int ret; + + + TRACE(("enter recv_msg_channel_open")); + + /* get the packet contents */ + type = buf_getstring(ses.payload, &typelen); + + remotechan = buf_getint(ses.payload); + transwindow = buf_getint(ses.payload); + transwindow = MIN(transwindow, MAX_TRANS_WINDOW); + transmaxpacket = buf_getint(ses.payload); + transmaxpacket = MIN(transmaxpacket, MAX_TRANS_PAYLOAD_LEN); + + /* figure what type of packet it is */ + if (typelen > MAX_NAME_LEN) { + goto failure; + } + + /* Get the channel type. This will depend if it is a client or a server, + * so we iterate through the connection-specific list which was + * set up when the connection started */ + for (chantype = ses.chantypes[0]; chantype != NULL; chantype++) { + if (strcmp(type, chantype->name) == 0) { + break; + } + } + + if (chantype == NULL) { + goto failure; + } + + /* create the channel */ + channel = newchannel(remotechan, chantype, transwindow, transmaxpacket); + + if (channel == NULL) { + goto failure; + } + + if (channel->type->inithandler) { + ret = channel->type->inithandler(channel); + if (ret >= 0) { + errtype = ret; + deletechannel(channel); + goto failure; + } + } + +#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); + goto cleanup; + +failure: + send_msg_channel_open_failure(remotechan, errtype, "", ""); + +cleanup: + m_free(type); + + TRACE(("leave recv_msg_channel_open")); +} + +/* Send a failure message */ +void send_msg_channel_failure(struct Channel *channel) { + + TRACE(("enter send_msg_channel_failure")); + CHECKCLEARTOWRITE(); + + buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_FAILURE); + buf_putint(ses.writepayload, channel->remotechan); + + encrypt_packet(); + TRACE(("leave send_msg_channel_failure")); +} + +/* Send a success message */ +void send_msg_channel_success(struct Channel *channel) { + + TRACE(("enter send_msg_channel_success")); + CHECKCLEARTOWRITE(); + + buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_SUCCESS); + buf_putint(ses.writepayload, channel->remotechan); + + encrypt_packet(); + TRACE(("leave send_msg_channel_success")); +} + +/* Send a channel open failure message, with a corresponding reason + * code (usually resource shortage or unknown chan type) */ +static void send_msg_channel_open_failure(unsigned int remotechan, + int reason, const unsigned char *text, const unsigned char *lang) { + + TRACE(("enter send_msg_channel_open_failure")); + CHECKCLEARTOWRITE(); + + buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_OPEN_FAILURE); + buf_putint(ses.writepayload, remotechan); + buf_putint(ses.writepayload, reason); + buf_putstring(ses.writepayload, text, strlen((char*)text)); + buf_putstring(ses.writepayload, lang, strlen((char*)lang)); + + encrypt_packet(); + TRACE(("leave send_msg_channel_open_failure")); +} + +/* Confirm a channel open, and let the remote end know what number we've + * allocated and the receive parameters */ +static void send_msg_channel_open_confirmation(struct Channel* channel, + unsigned int recvwindow, + unsigned int recvmaxpacket) { + + TRACE(("enter send_msg_channel_open_confirmation")); + CHECKCLEARTOWRITE(); + + buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_OPEN_CONFIRMATION); + buf_putint(ses.writepayload, channel->remotechan); + buf_putint(ses.writepayload, channel->index); + buf_putint(ses.writepayload, recvwindow); + buf_putint(ses.writepayload, recvmaxpacket); + + encrypt_packet(); + TRACE(("leave send_msg_channel_open_confirmation")); +} + +#ifdef USE_LISTENERS +/* Create a new channel, and start the open request. This is intended + * for X11, agent, tcp forwarding, and should be filled with channel-specific + * options, with the calling function calling encrypt_packet() after + * completion. It is mandatory for the caller to encrypt_packet() if + * DROPBEAR_SUCCESS is returned */ +int send_msg_channel_open_init(int fd, struct ChanType *type, + const char * typestring) { + + struct Channel* chan; + + TRACE(("enter send_msg_channel_open_init()")); + chan = newchannel(0, type, 0, 0); + if (!chan) { + TRACE(("leave send_msg_channel_open_init() - FAILED in newchannel()")); + return DROPBEAR_FAILURE; + } + + /* 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; + } + + chan->infd = chan->outfd = fd; + ses.maxfd = MAX(ses.maxfd, fd); + + /* now open the channel connection */ + CHECKCLEARTOWRITE(); + + buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_OPEN); + buf_putstring(ses.writepayload, typestring, strlen(typestring)); + buf_putint(ses.writepayload, chan->index); + buf_putint(ses.writepayload, RECV_MAXWINDOW); + buf_putint(ses.writepayload, RECV_MAXPACKET); + + TRACE(("leave send_msg_channel_open_init()")); + return DROPBEAR_SUCCESS; +} + +/* Confirmation that our channel open request (for forwardings) was + * successful*/ +void recv_msg_channel_open_confirmation() { + + unsigned int chan; + struct Channel * channel; + + TRACE(("enter recv_msg_channel_open_confirmation")); + chan = buf_getint(ses.payload); + + channel = getchannel(chan); + if (channel == NULL) { + dropbear_exit("Unknown channel"); + } + + channel->remotechan = buf_getint(ses.payload); + channel->transwindow = buf_getint(ses.payload); + channel->transmaxpacket = buf_getint(ses.payload); + + TRACE(("leave recv_msg_channel_open_confirmation")); +} + +/* Notification that our channel open request failed */ +void recv_msg_channel_open_failure() { + + unsigned int chan; + struct Channel * channel; + chan = buf_getbyte(ses.payload); + + channel = getchannel(chan); + if (channel == NULL) { + dropbear_exit("Unknown channel"); + } + + removechannel(channel); +} + +/* close a stdout/stderr fd */ +static void closeoutfd(struct Channel * channel, int fd) { + + /* don't close it if it is the same as infd, + * unless infd is already set -1 */ + TRACE(("enter closeoutfd")); + closechanfd(channel, fd, 0); + TRACE(("leave closeoutfd")); +} + +/* close a stdin fd */ +static void closeinfd(struct Channel * channel) { + + TRACE(("enter closeinfd")); + closechanfd(channel, channel->infd, 1); + TRACE(("leave closeinfd")); +} + +/* close a fd, how is 0 for stdout/stderr, 1 for stdin */ +static void closechanfd(struct Channel *channel, int fd, int how) { + + int closein = 0, closeout = 0; + + /* XXX server */ + if (channel->type->sepfds) { + shutdown(fd, how); + if (how == 0) { + closeout = 1; + } else { + closein = 1; + } + } else { + close(fd); + closein = closeout = 1; + } + + if (closeout && fd == channel->errfd) { + channel->errfd = FD_CLOSED; + } + if (closeout && fd == channel->outfd) { + channel->outfd = FD_CLOSED; + } + if (closein && fd == channel->infd) { + channel->infd = FD_CLOSED; + } +} + +#endif /* USE_LISTENERS */ diff --git a/common-chansession.c b/common-chansession.c new file mode 100644 index 0000000..ad9c7ed --- /dev/null +++ b/common-chansession.c @@ -0,0 +1,43 @@ +/* + * 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. */ + +#include "chansession.h" + +/* Mapping of signal values to ssh signal strings */ +const extern struct SigMap signames[] = { + {SIGABRT, "ABRT"}, + {SIGALRM, "ALRM"}, + {SIGFPE, "FPE"}, + {SIGHUP, "HUP"}, + {SIGILL, "ILL"}, + {SIGINT, "INT"}, + {SIGKILL, "KILL"}, + {SIGPIPE, "PIPE"}, + {SIGQUIT, "QUIT"}, + {SIGSEGV, "SEGV"}, + {SIGTERM, "TERM"}, + {SIGUSR1, "USR1"}, + {SIGUSR2, "USR2"}, + {0, NULL} +}; diff --git a/common-kex.c b/common-kex.c new file mode 100644 index 0000000..95f5db5 --- /dev/null +++ b/common-kex.c @@ -0,0 +1,458 @@ +/* + * 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 + * + * Copyright (c) 2002,2003 Matt Johnston + * Portions 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 "algo.h" +#include "buffer.h" +#include "session.h" +#include "kex.h" +#include "ssh.h" +#include "packet.h" +#include "bignum.h" +#include "random.h" + +/* diffie-hellman-group1-sha1 value for p */ +const unsigned char dh_p_val[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, + 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, + 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, + 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, + 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, + 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + +const int DH_G_VAL = 2; + +static void gen_new_keys(); +#ifndef DISABLE_ZLIB +static void gen_new_zstreams(); +#endif +/* helper function for gen_new_keys */ +static void hashkeys(unsigned char *out, int outlen, + const hash_state * hs, unsigned const char X); + + +/* Send our list of algorithms we can use */ +void send_msg_kexinit() { + + CHECKCLEARTOWRITE(); + buf_putbyte(ses.writepayload, SSH_MSG_KEXINIT); + + /* cookie */ + genrandom(buf_getwriteptr(ses.writepayload, 16), 16); + buf_incrwritepos(ses.writepayload, 16); + + /* kex algos */ + buf_put_algolist(ses.writepayload, sshkex); + + /* server_host_key_algorithms */ + buf_put_algolist(ses.writepayload, sshhostkey); + + /* encryption_algorithms_client_to_server */ + buf_put_algolist(ses.writepayload, sshciphers); + + /* encryption_algorithms_server_to_client */ + buf_put_algolist(ses.writepayload, sshciphers); + + /* mac_algorithms_client_to_server */ + buf_put_algolist(ses.writepayload, sshhashes); + + /* mac_algorithms_server_to_client */ + buf_put_algolist(ses.writepayload, sshhashes); + + /* compression_algorithms_client_to_server */ + buf_put_algolist(ses.writepayload, sshcompress); + + /* compression_algorithms_server_to_client */ + buf_put_algolist(ses.writepayload, sshcompress); + + /* languages_client_to_server */ + buf_putstring(ses.writepayload, "", 0); + + /* languages_server_to_client */ + buf_putstring(ses.writepayload, "", 0); + + /* first_kex_packet_follows - unimplemented for now */ + buf_putbyte(ses.writepayload, 0x00); + + /* reserved unit32 */ + buf_putint(ses.writepayload, 0); + + /* set up transmitted kex packet buffer for hashing. + * This is freed after the end of the kex */ + ses.transkexinit = buf_newcopy(ses.writepayload); + + encrypt_packet(); + ses.dataallowed = 0; /* don't send other packets during kex */ + + TRACE(("DATAALLOWED=0")); + TRACE(("-> KEXINIT")); + ses.kexstate.sentkexinit = 1; +} + +/* *** NOTE regarding (send|recv)_msg_newkeys *** + * Changed by mihnea from the original kex.c to set dataallowed after a + * completed key exchange, no matter the order in which it was performed. + * This enables client mode without affecting server functionality. + */ + +/* Bring new keys into use after a key exchange, and let the client know*/ +void send_msg_newkeys() { + + TRACE(("enter send_msg_newkeys")); + + /* generate the kexinit request */ + CHECKCLEARTOWRITE(); + buf_putbyte(ses.writepayload, SSH_MSG_NEWKEYS); + encrypt_packet(); + + + /* set up our state */ + if (ses.kexstate.recvnewkeys) { + TRACE(("while RECVNEWKEYS=1")); + gen_new_keys(); + kexinitialise(); /* we've finished with this kex */ + TRACE((" -> DATAALLOWED=1")); + ses.dataallowed = 1; /* we can send other packets again now */ + } else { + ses.kexstate.sentnewkeys = 1; + TRACE(("SENTNEWKEYS=1")); + } + + TRACE(("-> MSG_NEWKEYS")); + TRACE(("leave send_msg_newkeys")); +} + +/* Bring the new keys into use after a key exchange */ +void recv_msg_newkeys() { + + TRACE(("<- MSG_NEWKEYS")); + TRACE(("enter recv_msg_newkeys")); + + /* simply check if we've sent SSH_MSG_NEWKEYS, and if so, + * switch to the new keys */ + if (ses.kexstate.sentnewkeys) { + TRACE(("while SENTNEWKEYS=1")); + gen_new_keys(); + kexinitialise(); /* we've finished with this kex */ + TRACE((" -> DATAALLOWED=1")); + ses.dataallowed = 1; /* we can send other packets again now */ + } else { + TRACE(("RECVNEWKEYS=1")); + ses.kexstate.recvnewkeys = 1; + } + + TRACE(("leave recv_msg_newkeys")); +} + + +/* Duplicated verbatim from kex.c --mihnea */ +void kexinitialise() { + + struct timeval tv; + + TRACE(("kexinitialise()")); + + /* sent/recv'd MSG_KEXINIT */ + ses.kexstate.sentkexinit = 0; + ses.kexstate.recvkexinit = 0; + + /* sent/recv'd MSG_NEWKEYS */ + ses.kexstate.recvnewkeys = 0; + ses.kexstate.sentnewkeys = 0; + + /* first_packet_follows */ + /* TODO - currently not handled */ + ses.kexstate.firstfollows = 0; + + ses.kexstate.datatrans = 0; + ses.kexstate.datarecv = 0; + + if (gettimeofday(&tv, 0) < 0) { + dropbear_exit("Error getting time"); + } + ses.kexstate.lastkextime = tv.tv_sec; + +} + +/* Helper function for gen_new_keys, creates a hash. It makes a copy of the + * already initialised hash_state hs, which should already have processed + * the dh_K and hash, since these are common. X is the letter 'A', 'B' etc. + * out must have at least min(SHA1_HASH_SIZE, outlen) bytes allocated. + * The output will only be expanded once, since that is all that is required + * (for 3DES and SHA, with 24 and 20 bytes respectively). + * + * See Section 5.2 of the IETF secsh Transport Draft for details */ + +/* Duplicated verbatim from kex.c --mihnea */ +static void hashkeys(unsigned char *out, int outlen, + const hash_state * hs, const unsigned char X) { + + hash_state hs2; + unsigned char k2[SHA1_HASH_SIZE]; /* used to extending */ + + memcpy(&hs2, hs, sizeof(hash_state)); + sha1_process(&hs2, &X, 1); + sha1_process(&hs2, ses.session_id, SHA1_HASH_SIZE); + sha1_done(&hs2, out); + if (SHA1_HASH_SIZE < outlen) { + /* need to extend */ + memcpy(&hs2, hs, sizeof(hash_state)); + sha1_process(&hs2, out, SHA1_HASH_SIZE); + sha1_done(&hs2, k2); + memcpy(&out[SHA1_HASH_SIZE], k2, outlen - SHA1_HASH_SIZE); + } +} + +/* Generate the actual encryption/integrity keys, using the results of the + * key exchange, as specified in section 5.2 of the IETF secsh-transport + * draft. This occurs after the DH key-exchange. + * + * ses.newkeys is the new set of keys which are generated, these are only + * taken into use after both sides have sent a newkeys message */ + +/* Originally from kex.c, generalized for cli/svr mode --mihnea */ +static void gen_new_keys() { + + unsigned char C2S_IV[MAX_IV_LEN]; + unsigned char C2S_key[MAX_KEY_LEN]; + unsigned char S2C_IV[MAX_IV_LEN]; + unsigned char S2C_key[MAX_KEY_LEN]; + /* unsigned char key[MAX_KEY_LEN]; */ + unsigned char *trans_IV, *trans_key, *recv_IV, *recv_key; + + hash_state hs; + unsigned int C2S_keysize, S2C_keysize; + char mactransletter, macrecvletter; /* Client or server specific */ + + TRACE(("enter gen_new_keys")); + /* the dh_K and hash are the start of all hashes, we make use of that */ + + sha1_init(&hs); + sha1_process_mp(&hs, ses.dh_K); + mp_clear(ses.dh_K); + m_free(ses.dh_K); + sha1_process(&hs, ses.hash, SHA1_HASH_SIZE); + m_burn(ses.hash, SHA1_HASH_SIZE); + + hashkeys(C2S_IV, SHA1_HASH_SIZE, &hs, 'A'); + hashkeys(S2C_IV, SHA1_HASH_SIZE, &hs, 'B'); + + if (IS_DROPBEAR_CLIENT) { + trans_IV = C2S_IV; + recv_IV = S2C_IV; + trans_key = C2S_key; + recv_key = S2C_key; + C2S_keysize = ses.newkeys->trans_algo_crypt->keysize; + S2C_keysize = ses.newkeys->recv_algo_crypt->keysize; + mactransletter = 'E'; + macrecvletter = 'F'; + } else { + trans_IV = S2C_IV; + recv_IV = C2S_IV; + trans_key = S2C_key; + recv_key = C2S_key; + C2S_keysize = ses.newkeys->recv_algo_crypt->keysize; + S2C_keysize = ses.newkeys->trans_algo_crypt->keysize; + mactransletter = 'F'; + macrecvletter = 'E'; + } + + hashkeys(C2S_key, C2S_keysize, &hs, 'C'); + hashkeys(S2C_key, S2C_keysize, &hs, 'D'); + + if (cbc_start( + find_cipher(ses.newkeys->recv_algo_crypt->cipherdesc->name), + recv_IV, recv_key, + ses.newkeys->recv_algo_crypt->keysize, 0, + &ses.newkeys->recv_symmetric_struct) != CRYPT_OK) { + dropbear_exit("crypto error"); + } + + if (cbc_start( + find_cipher(ses.newkeys->trans_algo_crypt->cipherdesc->name), + trans_IV, trans_key, + ses.newkeys->trans_algo_crypt->keysize, 0, + &ses.newkeys->trans_symmetric_struct) != CRYPT_OK) { + dropbear_exit("crypto error"); + } + + /* MAC keys */ + hashkeys(ses.newkeys->transmackey, + ses.newkeys->trans_algo_mac->keysize, &hs, mactransletter); + hashkeys(ses.newkeys->recvmackey, + ses.newkeys->recv_algo_mac->keysize, &hs, macrecvletter); + +#ifndef DISABLE_ZLIB + gen_new_zstreams(); +#endif + + /* Switch over to the new keys */ + m_burn(ses.keys, sizeof(struct key_context)); + m_free(ses.keys); + ses.keys = ses.newkeys; + ses.newkeys = NULL; + + TRACE(("leave gen_new_keys")); +} + +#ifndef DISABLE_ZLIB +/* Set up new zlib compression streams, close the old ones. Only + * called from gen_new_keys() */ +static void gen_new_zstreams() { + + /* create new zstreams */ + if (ses.newkeys->recv_algo_comp == DROPBEAR_COMP_ZLIB) { + ses.newkeys->recv_zstream = (z_streamp)m_malloc(sizeof(z_stream)); + ses.newkeys->recv_zstream->zalloc = Z_NULL; + ses.newkeys->recv_zstream->zfree = Z_NULL; + + if (inflateInit(ses.newkeys->recv_zstream) != Z_OK) { + dropbear_exit("zlib error"); + } + } else { + ses.newkeys->recv_zstream = NULL; + } + + if (ses.newkeys->trans_algo_comp == DROPBEAR_COMP_ZLIB) { + ses.newkeys->trans_zstream = (z_streamp)m_malloc(sizeof(z_stream)); + ses.newkeys->trans_zstream->zalloc = Z_NULL; + ses.newkeys->trans_zstream->zfree = Z_NULL; + + if (deflateInit(ses.newkeys->trans_zstream, Z_DEFAULT_COMPRESSION) + != Z_OK) { + dropbear_exit("zlib error"); + } + } else { + ses.newkeys->trans_zstream = NULL; + } + + /* clean up old keys */ + if (ses.keys->recv_zstream != NULL) { + if (inflateEnd(ses.keys->recv_zstream) == Z_STREAM_ERROR) { + /* Z_DATA_ERROR is ok, just means that stream isn't ended */ + dropbear_exit("crypto error"); + } + m_free(ses.keys->recv_zstream); + } + if (ses.keys->trans_zstream != NULL) { + if (deflateEnd(ses.keys->trans_zstream) == Z_STREAM_ERROR) { + /* Z_DATA_ERROR is ok, just means that stream isn't ended */ + dropbear_exit("crypto error"); + } + m_free(ses.keys->trans_zstream); + } +} +#endif + + +/* Executed upon receiving a kexinit message from the client to initiate + * key exchange. If we haven't already done so, we send the list of our + * preferred algorithms. The client's requested algorithms are processed, + * and we calculate the first portion of the key-exchange-hash for used + * later in the key exchange. No response is sent, as the client should + * initiate the diffie-hellman key exchange */ + +/* Originally from kex.c, generalized for cli/svr mode --mihnea */ +/* Belongs in common_kex.c where it should be moved after review */ +void recv_msg_kexinit() { + + TRACE(("<- KEXINIT")); + TRACE(("enter recv_msg_kexinit")); + + /* start the kex hash */ + ses.kexhashbuf = buf_new(MAX_KEXHASHBUF); + + if (!ses.kexstate.sentkexinit) { + /* we need to send a kex packet */ + send_msg_kexinit(); + TRACE(("continue recv_msg_kexinit: sent kexinit")); + } + + + if (IS_DROPBEAR_CLIENT) { + + /* read the peer's choice of algos */ + cli_read_kex(); + + /* V_C, the client's version string (CR and NL excluded) */ + buf_putstring(ses.kexhashbuf, + (unsigned char*)LOCAL_IDENT, strlen(LOCAL_IDENT)); + /* V_S, the server's version string (CR and NL excluded) */ + buf_putstring(ses.kexhashbuf, + ses.remoteident, strlen((char*)ses.remoteident)); + + /* I_C, the payload of the client's SSH_MSG_KEXINIT */ + buf_putstring(ses.kexhashbuf, + buf_getptr(ses.transkexinit, ses.transkexinit->len), + ses.transkexinit->len); + /* I_S, the payload of the server's SSH_MSG_KEXINIT */ + buf_setpos(ses.payload, 0); + buf_putstring(ses.kexhashbuf, + buf_getptr(ses.payload, ses.payload->len), + ses.payload->len); + + } else { + + /* read the peer's choice of algos */ + svr_read_kex(); + /* V_C, the client's version string (CR and NL excluded) */ + buf_putstring(ses.kexhashbuf, + ses.remoteident, strlen((char*)ses.remoteident)); + /* V_S, the server's version string (CR and NL excluded) */ + buf_putstring(ses.kexhashbuf, + (unsigned char*)LOCAL_IDENT, strlen(LOCAL_IDENT)); + + /* I_C, the payload of the client's SSH_MSG_KEXINIT */ + buf_setpos(ses.payload, 0); + buf_putstring(ses.kexhashbuf, + buf_getptr(ses.payload, ses.payload->len), + ses.payload->len); + /* I_S, the payload of the server's SSH_MSG_KEXINIT */ + buf_putstring(ses.kexhashbuf, + buf_getptr(ses.transkexinit, ses.transkexinit->len), + ses.transkexinit->len); + } + + buf_free(ses.transkexinit); + ses.transkexinit = NULL; + /* the rest of ses.kexhashbuf will be done after DH exchange */ + + ses.kexstate.recvkexinit = 1; +// ses.expecting = SSH_MSG_KEXDH_INIT; + ses.expecting = 0; + + TRACE(("leave recv_msg_kexinit")); +} + diff --git a/common-packet.c b/common-packet.c new file mode 100644 index 0000000..4ca6f8f --- /dev/null +++ b/common-packet.c @@ -0,0 +1,620 @@ +/* + * 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. */ + +#include "includes.h" +#include "packet.h" +#include "session.h" +#include "dbutil.h" +#include "ssh.h" +#include "algo.h" +#include "buffer.h" +#include "kex.h" +#include "random.h" +#include "service.h" +#include "auth.h" +#include "channel.h" + +static void read_packet_init(); +static void writemac(buffer * outputbuffer, buffer * clearwritebuf); +static int checkmac(buffer* hashbuf, buffer* readbuf); + +#define ZLIB_COMPRESS_INCR 20 /* this is 12 bytes + 0.1% of 8000 bytes */ +#define ZLIB_DECOMPRESS_INCR 100 +#ifndef DISABLE_ZLIB +static buffer* buf_decompress(buffer* buf, unsigned int len); +static void buf_compress(buffer * dest, buffer * src, unsigned int len); +#endif + +/* non-blocking function writing out a current encrypted packet */ +void write_packet() { + + int len, written; + buffer * writebuf; + + TRACE(("enter write_packet")); + assert(!isempty(&ses.writequeue)); + + /* Get the next buffer in the queue of encrypted packets to write*/ + writebuf = (buffer*)examine(&ses.writequeue); + + len = writebuf->len - writebuf->pos; + assert(len > 0); + /* Try to write as much as possible */ + written = write(ses.sock, buf_getptr(writebuf, len), len); + + if (written < 0) { + if (errno == EINTR) { + TRACE(("leave writepacket: EINTR")); + return; + } else { + dropbear_exit("error writing"); + } + } + + if (written == 0) { + session_remoteclosed(); + } + + if (written == len) { + /* We've finished with the packet, free it */ + dequeue(&ses.writequeue); + buf_free(writebuf); + } else { + /* More packet left to write, leave it in the queue for later */ + buf_incrpos(writebuf, written); + } + + TRACE(("leave write_packet")); +} + +/* Non-blocking function reading available portion of a packet into the + * ses's buffer, decrypting the length if encrypted, decrypting the + * full portion if possible */ +void read_packet() { + + int len; + unsigned int maxlen; + unsigned char blocksize; + + TRACE(("enter read_packet")); + blocksize = ses.keys->recv_algo_crypt->blocksize; + + if (ses.readbuf == NULL || ses.readbuf->len < blocksize) { + /* In the first blocksize of a packet */ + + /* Read the first blocksize of the packet, so we can decrypt it and + * find the length of the whole packet */ + read_packet_init(); + + /* If we don't have the length of decryptreadbuf, we didn't read + * a whole blocksize and should exit */ + if (ses.decryptreadbuf->len == 0) { + TRACE(("leave read_packet: packetinit done")); + return; + } + } + + /* Attempt to read the remainder of the packet, note that there + * mightn't be any available (EAGAIN) */ + assert(ses.readbuf != NULL); + maxlen = ses.readbuf->len - ses.readbuf->pos; + len = read(ses.sock, buf_getptr(ses.readbuf, maxlen), maxlen); + + if (len == 0) { + session_remoteclosed(); + } + + if (len < 0) { + if (errno == EINTR || errno == EAGAIN) { + TRACE(("leave read_packet: EINTR or EAGAIN")); + return; + } else { + dropbear_exit("error reading: %s", strerror(errno)); + } + } + + buf_incrpos(ses.readbuf, len); + + if ((unsigned int)len == maxlen) { + /* The whole packet has been read */ + decrypt_packet(); + /* The main select() loop process_packet() to + * handle the packet contents... */ + } + TRACE(("leave read_packet")); +} + +/* Function used to read the initial portion of a packet, and determine the + * length. Only called during the first BLOCKSIZE of a packet. */ +static void read_packet_init() { + + unsigned int maxlen; + int len; + unsigned char blocksize; + unsigned char macsize; + + + blocksize = ses.keys->recv_algo_crypt->blocksize; + macsize = ses.keys->recv_algo_mac->hashsize; + + if (ses.readbuf == NULL) { + /* start of a new packet */ + ses.readbuf = buf_new(INIT_READBUF); + assert(ses.decryptreadbuf == NULL); + ses.decryptreadbuf = buf_new(blocksize); + } + + maxlen = blocksize - ses.readbuf->pos; + + /* read the rest of the packet if possible */ + len = read(ses.sock, buf_getwriteptr(ses.readbuf, maxlen), + maxlen); + if (len == 0) { + session_remoteclosed(); + } + if (len < 0) { + if (errno == EINTR) { + TRACE(("leave read_packet_init: EINTR")); + return; + } + dropbear_exit("error reading: %s", strerror(errno)); + } + + buf_incrwritepos(ses.readbuf, len); + + if ((unsigned int)len != maxlen) { + /* don't have enough bytes to determine length, get next time */ + return; + } + + /* now we have the first block, need to get packet length, so we decrypt + * the first block (only need first 4 bytes) */ + buf_setpos(ses.readbuf, 0); + if (ses.keys->recv_algo_crypt->cipherdesc == NULL) { + /* copy it */ + memcpy(buf_getwriteptr(ses.decryptreadbuf, blocksize), + buf_getptr(ses.readbuf, blocksize), + blocksize); + } else { + /* decrypt it */ + if (cbc_decrypt(buf_getptr(ses.readbuf, blocksize), + buf_getwriteptr(ses.decryptreadbuf,blocksize), + &ses.keys->recv_symmetric_struct) != CRYPT_OK) { + dropbear_exit("error decrypting"); + } + } + buf_setlen(ses.decryptreadbuf, blocksize); + len = buf_getint(ses.decryptreadbuf) + 4 + macsize; + + buf_setpos(ses.readbuf, blocksize); + + /* check packet length */ + if ((len > MAX_PACKET_LEN) || + (len < MIN_PACKET_LEN + macsize) || + ((len - macsize) % blocksize != 0)) { + dropbear_exit("bad packet size"); + } + + buf_resize(ses.readbuf, len); + buf_setlen(ses.readbuf, len); + +} + +/* handle the received packet */ +void decrypt_packet() { + + unsigned char blocksize; + unsigned char macsize; + unsigned int padlen; + unsigned int len; + + TRACE(("enter decrypt_packet")); + blocksize = ses.keys->recv_algo_crypt->blocksize; + macsize = ses.keys->recv_algo_mac->hashsize; + + ses.kexstate.datarecv += ses.readbuf->len; + + /* we've already decrypted the first blocksize in read_packet_init */ + buf_setpos(ses.readbuf, blocksize); + + buf_resize(ses.decryptreadbuf, ses.readbuf->len - macsize); + buf_setlen(ses.decryptreadbuf, ses.decryptreadbuf->size); + buf_setpos(ses.decryptreadbuf, blocksize); + + /* decrypt if encryption is set, memcpy otherwise */ + if (ses.keys->recv_algo_crypt->cipherdesc == NULL) { + /* copy it */ + len = ses.readbuf->len - macsize - blocksize; + memcpy(buf_getwriteptr(ses.decryptreadbuf, len), + buf_getptr(ses.readbuf, len), len); + } else { + /* decrypt */ + while (ses.readbuf->pos < ses.readbuf->len - macsize) { + if (cbc_decrypt(buf_getptr(ses.readbuf, blocksize), + buf_getwriteptr(ses.decryptreadbuf, blocksize), + &ses.keys->recv_symmetric_struct) != CRYPT_OK) { + dropbear_exit("error decrypting"); + } + buf_incrpos(ses.readbuf, blocksize); + buf_incrwritepos(ses.decryptreadbuf, blocksize); + } + } + + /* check the hmac */ + buf_setpos(ses.readbuf, ses.readbuf->len - macsize); + if (checkmac(ses.readbuf, ses.decryptreadbuf) != DROPBEAR_SUCCESS) { + dropbear_exit("Integrity error"); + } + + /* readbuf no longer required */ + buf_free(ses.readbuf); + ses.readbuf = NULL; + + /* get padding length */ + buf_setpos(ses.decryptreadbuf, PACKET_PADDING_OFF); + padlen = buf_getbyte(ses.decryptreadbuf); + + /* payload length */ + /* - 4 - 1 is for LEN and PADLEN values */ + len = ses.decryptreadbuf->len - padlen - 4 - 1; + if ((len > MAX_PAYLOAD_LEN) || (len < 1)) { + dropbear_exit("bad packet size"); + } + + buf_setpos(ses.decryptreadbuf, PACKET_PAYLOAD_OFF); + +#ifndef DISABLE_ZLIB + if (ses.keys->recv_algo_comp == DROPBEAR_COMP_ZLIB) { + /* decompress */ + ses.payload = buf_decompress(ses.decryptreadbuf, len); + + } else +#endif + { + /* copy payload */ + ses.payload = buf_new(len); + memcpy(ses.payload->data, buf_getptr(ses.decryptreadbuf, len), len); + buf_incrlen(ses.payload, len); + } + + buf_free(ses.decryptreadbuf); + ses.decryptreadbuf = NULL; + buf_setpos(ses.payload, 0); + + ses.recvseq++; + + TRACE(("leave decrypt_packet")); +} + +/* Checks the mac in hashbuf, for the data in readbuf. + * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +static int checkmac(buffer* macbuf, buffer* sourcebuf) { + + unsigned char macsize; + hmac_state hmac; + unsigned char tempbuf[MAX_MAC_LEN]; + unsigned long hashsize; + int len; + + macsize = ses.keys->recv_algo_mac->hashsize; + + if (macsize == 0) { + return DROPBEAR_SUCCESS; + } + + /* calculate the mac */ + if (hmac_init(&hmac, + find_hash(ses.keys->recv_algo_mac->hashdesc->name), + ses.keys->recvmackey, + ses.keys->recv_algo_mac->keysize) + != CRYPT_OK) { + dropbear_exit("HMAC error"); + } + + /* sequence number */ + STORE32H(ses.recvseq, tempbuf); + if (hmac_process(&hmac, tempbuf, 4) != CRYPT_OK) { + dropbear_exit("HMAC error"); + } + + buf_setpos(sourcebuf, 0); + len = sourcebuf->len; + if (hmac_process(&hmac, buf_getptr(sourcebuf, len), len) != CRYPT_OK) { + dropbear_exit("HMAC error"); + } + + hashsize = sizeof(tempbuf); + if (hmac_done(&hmac, tempbuf, &hashsize) != CRYPT_OK) { + dropbear_exit("HMAC error"); + } + + /* compare the hash */ + if (memcmp(tempbuf, buf_getptr(macbuf, macsize), macsize) != 0) { + return DROPBEAR_FAILURE; + } else { + return DROPBEAR_SUCCESS; + } +} + +#ifndef DISABLE_ZLIB +/* returns a pointer to a newly created buffer */ +static buffer* buf_decompress(buffer* buf, unsigned int len) { + + int result; + buffer * ret; + z_streamp zstream; + + zstream = ses.keys->recv_zstream; + ret = buf_new(len); + + zstream->avail_in = len; + zstream->next_in = buf_getptr(buf, len); + + /* decompress the payload, incrementally resizing the output buffer */ + while (1) { + + zstream->avail_out = ret->size - ret->pos; + zstream->next_out = buf_getwriteptr(ret, zstream->avail_out); + + result = inflate(zstream, Z_SYNC_FLUSH); + + buf_setlen(ret, ret->size - zstream->avail_out); + buf_setpos(ret, ret->len); + + if (result != Z_BUF_ERROR && result != Z_OK) { + dropbear_exit("zlib error"); + } + + if (zstream->avail_in == 0 && + (zstream->avail_out != 0 || result == Z_BUF_ERROR)) { + /* we can only exit if avail_out hasn't all been used, + * and there's no remaining input */ + return ret; + } + + if (zstream->avail_out == 0) { + buf_resize(ret, ret->size + ZLIB_DECOMPRESS_INCR); + } + } +} +#endif + + + +/* This must be called directly after receiving the unimplemented packet. + * Isn't the most clean implementation, it relies on packet processing + * occurring directly after decryption. This is reasonably valid, since + * there is only a single decryption buffer */ +void recv_unimplemented() { + + CHECKCLEARTOWRITE(); + + buf_putbyte(ses.writepayload, SSH_MSG_UNIMPLEMENTED); + /* the decryption routine increments the sequence number, we must + * decrement */ + buf_putint(ses.writepayload, ses.recvseq - 1); + + encrypt_packet(); +} + +/* encrypt the writepayload, putting into writebuf, ready for write_packet() + * to put on the wire */ +void encrypt_packet() { + + unsigned char padlen; + unsigned char blocksize, macsize; + buffer * writebuf; /* the packet which will go on the wire */ + buffer * clearwritebuf; /* unencrypted, possibly compressed */ + + TRACE(("enter encrypt_packet()")); + TRACE(("encrypt_packet type is %d", ses.writepayload->data[0])); + blocksize = ses.keys->trans_algo_crypt->blocksize; + macsize = ses.keys->trans_algo_mac->hashsize; + + /* Encrypted packet len is payload+5, then worst case is if we are 3 away + * from a blocksize multiple. In which case we need to pad to the + * multiple, then add another blocksize (or MIN_PACKET_LEN) */ + clearwritebuf = buf_new((ses.writepayload->len+4+1) + MIN_PACKET_LEN + 3 +#ifndef DISABLE_ZLIB + + ZLIB_COMPRESS_INCR /* bit of a kludge, but we can't know len*/ +#endif + ); + buf_setlen(clearwritebuf, PACKET_PAYLOAD_OFF); + buf_setpos(clearwritebuf, PACKET_PAYLOAD_OFF); + + buf_setpos(ses.writepayload, 0); + +#ifndef DISABLE_ZLIB + /* compression */ + if (ses.keys->trans_algo_comp == DROPBEAR_COMP_ZLIB) { + buf_compress(clearwritebuf, ses.writepayload, ses.writepayload->len); + } else +#endif + { + memcpy(buf_getwriteptr(clearwritebuf, ses.writepayload->len), + buf_getptr(ses.writepayload, ses.writepayload->len), + ses.writepayload->len); + buf_incrwritepos(clearwritebuf, ses.writepayload->len); + } + + /* finished with payload */ + buf_setpos(ses.writepayload, 0); + buf_setlen(ses.writepayload, 0); + + /* length of padding - packet length must be a multiple of blocksize, + * with a minimum of 4 bytes of padding */ + padlen = blocksize - (clearwritebuf->len) % blocksize; + if (padlen < 4) { + padlen += blocksize; + } + /* check for min packet length */ + if (clearwritebuf->len + padlen < MIN_PACKET_LEN) { + padlen += blocksize; + } + + buf_setpos(clearwritebuf, 0); + /* packet length excluding the packetlength uint32 */ + buf_putint(clearwritebuf, clearwritebuf->len + padlen - 4); + + /* padding len */ + buf_putbyte(clearwritebuf, padlen); + /* actual padding */ + buf_setpos(clearwritebuf, clearwritebuf->len); + buf_incrlen(clearwritebuf, padlen); + genrandom(buf_getptr(clearwritebuf, padlen), padlen); + + /* do the actual encryption */ + buf_setpos(clearwritebuf, 0); + /* create a new writebuffer, this is freed when it has been put on the + * wire by writepacket() */ + writebuf = buf_new(clearwritebuf->len + macsize); + + if (ses.keys->trans_algo_crypt->cipherdesc == NULL) { + /* copy it */ + memcpy(buf_getwriteptr(writebuf, clearwritebuf->len), + buf_getptr(clearwritebuf, clearwritebuf->len), + clearwritebuf->len); + buf_incrwritepos(writebuf, clearwritebuf->len); + } else { + /* encrypt it */ + while (clearwritebuf->pos < clearwritebuf->len) { + if (cbc_encrypt(buf_getptr(clearwritebuf, blocksize), + buf_getwriteptr(writebuf, blocksize), + &ses.keys->trans_symmetric_struct) != CRYPT_OK) { + dropbear_exit("error encrypting"); + } + buf_incrpos(clearwritebuf, blocksize); + buf_incrwritepos(writebuf, blocksize); + } + } + + /* now add a hmac and we're done */ + writemac(writebuf, clearwritebuf); + + /* clearwritebuf is finished with */ + buf_free(clearwritebuf); + + /* enqueue the packet for sending */ + buf_setpos(writebuf, 0); + enqueue(&ses.writequeue, (void*)writebuf); + + /* Update counts */ + ses.kexstate.datatrans += writebuf->len; + ses.transseq++; + + TRACE(("leave encrypt_packet()")); +} + + +/* Create the packet mac, and append H(seqno|clearbuf) to the output */ +static void writemac(buffer * outputbuffer, buffer * clearwritebuf) { + + int macsize; + unsigned char seqbuf[4]; + unsigned long hashsize; + hmac_state hmac; + + TRACE(("enter writemac")); + + macsize = ses.keys->trans_algo_mac->hashsize; + + if (macsize > 0) { + /* calculate the mac */ + if (hmac_init(&hmac, + find_hash(ses.keys->trans_algo_mac->hashdesc->name), + ses.keys->transmackey, + ses.keys->trans_algo_mac->keysize) != CRYPT_OK) { + dropbear_exit("HMAC error"); + } + + /* sequence number */ + STORE32H(ses.transseq, seqbuf); + if (hmac_process(&hmac, seqbuf, 4) != CRYPT_OK) { + dropbear_exit("HMAC error"); + } + + /* the actual contents */ + buf_setpos(clearwritebuf, 0); + if (hmac_process(&hmac, + buf_getptr(clearwritebuf, + clearwritebuf->len), + clearwritebuf->len) != CRYPT_OK) { + dropbear_exit("HMAC error"); + } + + hashsize = macsize; + if (hmac_done(&hmac, buf_getwriteptr(outputbuffer, macsize), &hashsize) + != CRYPT_OK) { + dropbear_exit("HMAC error"); + } + buf_incrwritepos(outputbuffer, macsize); + } + TRACE(("leave writemac")); +} + +#ifndef DISABLE_ZLIB +/* compresses len bytes from src, outputting to dest (starting from the + * respective current positions. */ +static void buf_compress(buffer * dest, buffer * src, unsigned int len) { + + unsigned int endpos = src->pos + len; + int result; + + TRACE(("enter buf_compress")); + + while (1) { + + ses.keys->trans_zstream->avail_in = endpos - src->pos; + ses.keys->trans_zstream->next_in = + buf_getptr(src, ses.keys->trans_zstream->avail_in); + + ses.keys->trans_zstream->avail_out = dest->size - dest->pos; + ses.keys->trans_zstream->next_out = + buf_getwriteptr(dest, ses.keys->trans_zstream->avail_out); + + result = deflate(ses.keys->trans_zstream, Z_SYNC_FLUSH); + + buf_setpos(src, endpos - ses.keys->trans_zstream->avail_in); + buf_setlen(dest, dest->size - ses.keys->trans_zstream->avail_out); + buf_setpos(dest, dest->len); + + if (result != Z_OK) { + dropbear_exit("zlib error"); + } + + if (ses.keys->trans_zstream->avail_in == 0) { + break; + } + + assert(ses.keys->trans_zstream->avail_out == 0); + + /* the buffer has been filled, we must extend. This only happens in + * unusual circumstances where the data grows in size after deflate(), + * but it is possible */ + buf_resize(dest, dest->size + ZLIB_COMPRESS_INCR); + + } + TRACE(("leave buf_compress")); +} +#endif diff --git a/common-session.c b/common-session.c new file mode 100644 index 0000000..5e87c9a --- /dev/null +++ b/common-session.c @@ -0,0 +1,267 @@ +/* + * 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. */ + +#include "includes.h" +#include "session.h" +#include "dbutil.h" +#include "packet.h" +#include "algo.h" +#include "buffer.h" +#include "dss.h" +#include "ssh.h" +#include "random.h" +#include "kex.h" +#include "channel.h" +#include "atomicio.h" + +struct sshsession ses; + +/* need to know if the session struct has been initialised, this way isn't the + * cleanest, but works OK */ +int sessinitdone = 0; + +/* this is set when we get SIGINT or SIGTERM, the handler is in main.c */ +int exitflag = 0; + +static int ident_readln(int fd, char* buf, int count); + + +void(*session_remoteclosed)() = NULL; + + +/* called only at the start of a session, set up initial state */ +void common_session_init(int sock, runopts *opts) { + + TRACE(("enter session_init")); + + ses.remoteaddr = NULL; + ses.remotehost = NULL; + + ses.sock = sock; + ses.maxfd = sock; + + ses.opts = opts; + + ses.connecttimeout = 0; + + kexinitialise(); /* initialise the kex state */ + chaninitialise(); /* initialise the channel state */ + + ses.writepayload = buf_new(MAX_TRANS_PAYLOAD_LEN); + ses.transseq = 0; + + ses.readbuf = NULL; + ses.decryptreadbuf = NULL; + ses.payload = NULL; + ses.recvseq = 0; + + ses.expecting = SSH_MSG_KEXINIT; + ses.dataallowed = 0; /* don't send data yet, we'll wait until after kex */ + ses.ignorenext = 0; + + /* set all the algos to none */ + ses.keys = (struct key_context*)m_malloc(sizeof(struct key_context)); + ses.newkeys = NULL; + ses.keys->recv_algo_crypt = &dropbear_nocipher; + ses.keys->trans_algo_crypt = &dropbear_nocipher; + + ses.keys->recv_algo_mac = &dropbear_nohash; + ses.keys->trans_algo_mac = &dropbear_nohash; + + ses.keys->algo_kex = -1; + ses.keys->algo_hostkey = -1; + ses.keys->recv_algo_comp = DROPBEAR_COMP_NONE; + ses.keys->trans_algo_comp = DROPBEAR_COMP_NONE; + +#ifndef DISABLE_ZLIB + ses.keys->recv_zstream = NULL; + ses.keys->trans_zstream = NULL; +#endif + + /* key exchange buffers */ + ses.session_id = NULL; + ses.kexhashbuf = NULL; + ses.transkexinit = NULL; + ses.dh_K = NULL; + ses.remoteident = NULL; + + + TRACE(("leave session_init")); +} + +/* clean up a session on exit */ +void common_session_cleanup() { + + TRACE(("enter session_cleanup")); + + /* we can't cleanup if we don't know the session state */ + if (!sessinitdone) { + TRACE(("leave session_cleanup: !sessinitdone")); + return; + } + + m_free(ses.session_id); + freerunopts(ses.opts); + m_burn(ses.keys, sizeof(struct key_context)); + m_free(ses.keys); + + chancleanup(); + + TRACE(("leave session_cleanup")); +} + +/* Check all timeouts which are required. Currently these are the time for + * user authentication, and the automatic rekeying. */ +void checktimeouts() { + + struct timeval tv; + long secs; + + if (gettimeofday(&tv, 0) < 0) { + dropbear_exit("Error getting time"); + } + + secs = tv.tv_sec; + + if (ses.connecttimeout != 0 && secs > ses.connecttimeout) { + dropbear_close("Timeout before auth"); + } + + /* we can't rekey if we haven't done remote ident exchange yet */ + if (ses.remoteident == NULL) { + return; + } + + if (!ses.kexstate.sentkexinit + && (secs - ses.kexstate.lastkextime >= KEX_REKEY_TIMEOUT + || ses.kexstate.datarecv+ses.kexstate.datatrans >= KEX_REKEY_DATA)){ + TRACE(("rekeying after timeout or max data reached")); + send_msg_kexinit(); + } +} +void session_identification() { + + /* max length of 255 chars */ + char linebuf[256]; + int len = 0; + char done = 0; + + /* write our version string, this blocks */ + if (atomicio(write, ses.sock, LOCAL_IDENT "\r\n", + strlen(LOCAL_IDENT "\r\n")) == DROPBEAR_FAILURE) { + 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; + } + + if (!done) { + dropbear_exit("Failed to get client version"); + } else { + /* linebuf is already null terminated */ + ses.remoteident = m_malloc(len); + memcpy(ses.remoteident, linebuf, len); + } + + TRACE(("remoteident: %s", ses.remoteident)); + +} + +/* returns the length including null-terminating zero on success, + * or -1 on failure */ +static int ident_readln(int fd, char* buf, int count) { + + char in; + int pos = 0; + int num = 0; + fd_set fds; + struct timeval timeout; + + TRACE(("enter ident_readln")); + + if (count < 1) { + return -1; + } + + FD_ZERO(&fds); + + /* select since it's a non-blocking fd */ + + /* leave space to null-terminate */ + while (pos < count-1) { + + FD_SET(fd, &fds); + + timeout.tv_sec = 1; + timeout.tv_usec = 0; + if (select(fd+1, &fds, NULL, NULL, &timeout) < 0) { + if (errno == EINTR) { + continue; + } + TRACE(("leave ident_readln: select error")); + return -1; + } + + checktimeouts(); + + /* Have to go one byte at a time, since we don't want to read past + * the end, and have to somehow shove bytes back into the normal + * packet reader */ + if (FD_ISSET(fd, &fds)) { + num = read(fd, &in, 1); + /* a "\n" is a newline, "\r" we want to read in and keep going + * so that it won't be read as part of the next line */ + if (num < 0) { + /* error */ + if (errno == EINTR) { + continue; /* not a real error */ + } + TRACE(("leave ident_readln: read error")); + return -1; + } + if (num == 0) { + /* EOF */ + TRACE(("leave ident_readln: EOF")); + return -1; + } + if (in == '\n') { + /* end of ident string */ + break; + } + /* we don't want to include '\r's */ + if (in != '\r') { + buf[pos] = in; + pos++; + } + } + } + + buf[pos] = '\0'; + TRACE(("leave ident_readln: return %d", pos+1)); + return pos+1; +} + diff --git a/compat.c b/compat.c new file mode 100644 index 0000000..fb6e70a --- /dev/null +++ b/compat.c @@ -0,0 +1,281 @@ +/* + * 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. + * + * strlcat() is copyright as follows: + * Copyright (c) 1998 Todd C. Miller + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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. + * + * daemon() and getusershell() is copyright as follows: + * + * Copyright (c) 1990, 1993 + * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. +* +* Modifications for Dropbear to getusershell() are by Paul Marinceu +*/ + +#include "includes.h" + +#ifndef HAVE_GETUSERSHELL +static char **curshell, **shells, *strings; +static char **initshells(); +#endif + +#ifndef HAVE_STRLCPY +/* Implemented by matt as specified in freebsd 4.7 manpage. + * We don't require great speed, is simply for use with sshpty code */ +size_t strlcpy(char *dst, const char *src, size_t size) { + + size_t i; + + /* this is undefined, though size==0 -> return 0 */ + if (size < 1) { + return 0; + } + + for (i = 0; i < size-1; i++) { + if (src[i] == '\0') { + break; + } else { + dst[i] = src[i]; + } + } + + dst[i] = '\0'; + return strlen(src); + +} +#endif /* HAVE_STRLCPY */ + +#ifndef HAVE_STRLCAT +/* taken from openbsd-compat for OpenSSH 3.6.1p1 */ +/* "$OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp $" + * + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ + size_t +strlcat(dst, src, siz) + char *dst; + const char *src; + size_t siz; +{ + register char *d = dst; + register const char *s = src; + register size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} +#endif /* HAVE_STRLCAT */ + +#ifndef HAVE_DAEMON +/* From NetBSD - daemonise a process */ + +int daemon(int nochdir, int noclose) { + + int fd; + + switch (fork()) { + case -1: + return (-1); + case 0: + break; + default: + _exit(0); + } + + if (setsid() == -1) + return -1; + + if (!nochdir) + (void)chdir("/"); + + if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { + (void)dup2(fd, STDIN_FILENO); + (void)dup2(fd, STDOUT_FILENO); + (void)dup2(fd, STDERR_FILENO); + if (fd > STDERR_FILENO) + (void)close(fd); + } + return 0; +} +#endif /* HAVE_DAEMON */ + +#ifndef HAVE_BASENAME + +char *basename(char *path) { + + char *foo = strrchr(path, '/'); + return ++foo; +} + +#endif /* HAVE_BASENAME */ + +#ifndef HAVE_GETUSERSHELL + +/* + * Get a list of shells from /etc/shells, if it exists. + */ +char * getusershell() { + char *ret; + + if (curshell == NULL) + curshell = initshells(); + ret = *curshell; + if (ret != NULL) + curshell++; + return (ret); +} + +void endusershell() { + + if (shells != NULL) + free(shells); + shells = NULL; + if (strings != NULL) + free(strings); + strings = NULL; + curshell = NULL; +} + +void setusershell() { + curshell = initshells(); +} + +static char **initshells() { + /* don't touch this list. */ + const char *okshells[] = { "/bin/sh", "/bin/csh", NULL }; + register char **sp, *cp; + register FILE *fp; + struct stat statb; + int flen; + + if (shells != NULL) + free(shells); + shells = NULL; + if (strings != NULL) + free(strings); + strings = NULL; + if ((fp = fopen("/etc/shells", "rc")) == NULL) + return (char **) okshells; + if (fstat(fileno(fp), &statb) == -1) { + (void)fclose(fp); + return (char **) okshells; + } + if ((strings = malloc((u_int)statb.st_size + 1)) == NULL) { + (void)fclose(fp); + return (char **) okshells; + } + shells = calloc((unsigned)statb.st_size / 3, sizeof (char *)); + if (shells == NULL) { + (void)fclose(fp); + free(strings); + strings = NULL; + return (char **) okshells; + } + sp = shells; + cp = strings; + flen = statb.st_size; + while (fgets(cp, flen - (cp - strings), fp) != NULL) { + while (*cp != '#' && *cp != '/' && *cp != '\0') + cp++; + if (*cp == '#' || *cp == '\0') + continue; + *sp++ = cp; + while (!isspace(*cp) && *cp != '#' && *cp != '\0') + cp++; + *cp++ = '\0'; + } + *sp = NULL; + (void)fclose(fp); + return (shells); +} + +#endif /* HAVE_GETUSERSHELL */ diff --git a/compat.h b/compat.h new file mode 100644 index 0000000..f4ac823 --- /dev/null +++ b/compat.h @@ -0,0 +1,32 @@ +#ifndef _COMPAT_H_ +#define _COMPAT_H_ + +#include "includes.h" + +#ifndef HAVE_STRLCPY +size_t strlcpy(char *dst, const char *src, size_t size); +#endif + +#ifndef HAVE_STRLCAT +size_t strlcat(char *dst, const char *src, size_t siz); +#endif + +#ifndef HAVE_DAEMON +int daemon(int nochdir, int noclose); +#endif + +#ifndef HAVE_BASENAME +char *basename(const char* path); +#endif + +#ifndef HAVE_GETUSERSHELL +char *getusershell(); +void setusershell(); +void endusershell(); +#endif + +#ifndef _PATH_DEVNULL +#define _PATH_DEVNULL "/dev/null" +#endif + +#endif /* _COMPAT_H_ */ diff --git a/config.guess b/config.guess new file mode 100644 index 0000000..9e55e1a --- /dev/null +++ b/config.guess @@ -0,0 +1,1391 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + +timestamp='2003-05-19' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Per Bothner . +# Please send patches to . Submit a context +# diff and a properly formatted ChangeLog entry. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit build system type. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep __ELF__ >/dev/null + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit 0 ;; + amiga:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + arc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + hp300:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mac68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + macppc:OpenBSD:*:*) + echo powerpc-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme88k:OpenBSD:*:*) + echo m88k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvmeppc:OpenBSD:*:*) + echo powerpc-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + pmax:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sgi:OpenBSD:*:*) + echo mipseb-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sun3:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + wgrisc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:OpenBSD:*:*) + echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + alpha:OSF1:*:*) + if test $UNAME_RELEASE = "V4.0"; then + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + fi + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit 0 ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit 0 ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit 0 ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit 0;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit 0 ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit 0 ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit 0 ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit 0;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit 0;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit 0 ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit 0 ;; + DRS?6000:UNIX_SV:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7 && exit 0 ;; + esac ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit 0 ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit 0 ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit 0 ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit 0 ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit 0 ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit 0 ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit 0 ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit 0 ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit 0 ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit 0 ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c \ + && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ + && exit 0 + echo mips-mips-riscos${UNAME_RELEASE} + exit 0 ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit 0 ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit 0 ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit 0 ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit 0 ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit 0 ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit 0 ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit 0 ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit 0 ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit 0 ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit 0 ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit 0 ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit 0 ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit 0 ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit 0 ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0 + echo rs6000-ibm-aix3.2.5 + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit 0 ;; + *:AIX:*:[45]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit 0 ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit 0 ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit 0 ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit 0 ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit 0 ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit 0 ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit 0 ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + # avoid double evaluation of $set_cc_for_build + test -n "$CC_FOR_BUILD" || eval $set_cc_for_build + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit 0 ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit 0 ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0 + echo unknown-hitachi-hiuxwe2 + exit 0 ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit 0 ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit 0 ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit 0 ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit 0 ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit 0 ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit 0 ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit 0 ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit 0 ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit 0 ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit 0 ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit 0 ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + *:UNICOS/mp:*:*) + echo nv1-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit 0 ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit 0 ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:FreeBSD:*:*|*:GNU/FreeBSD:*:*) + # Determine whether the default compiler uses glibc. + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + #if __GLIBC__ >= 2 + LIBC=gnu + #else + LIBC= + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=` + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`${LIBC:+-$LIBC} + exit 0 ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit 0 ;; + i*:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit 0 ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit 0 ;; + x86:Interix*:3*) + echo i586-pc-interix3 + exit 0 ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit 0 ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit 0 ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit 0 ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit 0 ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + *:GNU:*:*) + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit 0 ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit 0 ;; + arm*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + cris:Linux:*:*) + echo cris-axis-linux-gnu + exit 0 ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + mips:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips + #undef mipsel + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mipsel + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` + test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0 + ;; + mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips64 + #undef mips64el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mips64el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips64 + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` + test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0 + ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit 0 ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit 0 ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit 0 ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit 0 ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit 0 ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit 0 ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit 0 ;; + i*86:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. cd to the root directory to prevent + # problems with other programs or directories called `ld' in the path. + # Set LC_ALL=C to ensure ld outputs messages in English. + ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ + | sed -ne '/supported targets:/!d + s/[ ][ ]*/ /g + s/.*supported targets: *// + s/ .*// + p'` + case "$ld_supported_targets" in + elf32-i386) + TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" + ;; + a.out-i386-linux) + echo "${UNAME_MACHINE}-pc-linux-gnuaout" + exit 0 ;; + coff-i386) + echo "${UNAME_MACHINE}-pc-linux-gnucoff" + exit 0 ;; + "") + # Either a pre-BFD a.out linker (linux-gnuoldld) or + # one that does not give us useful --help. + echo "${UNAME_MACHINE}-pc-linux-gnuoldld" + exit 0 ;; + esac + # Determine whether the default compiler is a.out or elf + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + #ifdef __ELF__ + # ifdef __GLIBC__ + # if __GLIBC__ >= 2 + LIBC=gnu + # else + LIBC=gnulibc1 + # endif + # else + LIBC=gnulibc1 + # endif + #else + #ifdef __INTEL_COMPILER + LIBC=gnu + #else + LIBC=gnuaout + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=` + test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0 + test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0 + ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit 0 ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit 0 ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit 0 ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit 0 ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit 0 ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit 0 ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit 0 ;; + i*86:*:5:[78]*) + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit 0 ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit 0 ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i386. + echo i386-pc-msdosdjgpp + exit 0 ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit 0 ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit 0 ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit 0 ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit 0 ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit 0 ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit 0 ;; + M68*:*:R3V[567]*:*) + test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; + 3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4.3${OS_REL} && exit 0 + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4 && exit 0 ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit 0 ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit 0 ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit 0 ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit 0 ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit 0 ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit 0 ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit 0 ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit 0 ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit 0 ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit 0 ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit 0 ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit 0 ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit 0 ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit 0 ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Darwin:*:*) + case `uname -p` in + *86) UNAME_PROCESSOR=i686 ;; + powerpc) UNAME_PROCESSOR=powerpc ;; + esac + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit 0 ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit 0 ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit 0 ;; + NSR-[DGKLNPTVW]:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit 0 ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit 0 ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit 0 ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit 0 ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit 0 ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit 0 ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit 0 ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit 0 ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit 0 ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit 0 ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit 0 ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0 + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit 0 ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + c34*) + echo c34-convex-bsd + exit 0 ;; + c38*) + echo c38-convex-bsd + exit 0 ;; + c4*) + echo c4-convex-bsd + exit 0 ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/config.sub b/config.sub new file mode 100644 index 0000000..fe4f1ed --- /dev/null +++ b/config.sub @@ -0,0 +1,1492 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + +timestamp='2003-05-09' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Please send patches to . Submit a context +# diff and a properly formatted ChangeLog entry. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit 0;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | freebsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis) + os= + basic_machine=$1 + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ + | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | ip2k \ + | m32r | m68000 | m68k | m88k | mcore \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64vr | mips64vrel \ + | mips64orion | mips64orionel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | msp430 \ + | ns16k | ns32k \ + | openrisc | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv9 | sparcv9b \ + | strongarm \ + | tahoe | thumb | tic80 | tron \ + | v850 | v850e \ + | we32k \ + | x86 | xscale | xstormy16 | xtensa \ + | z8k) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* \ + | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ + | clipper-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* \ + | m32r-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | mcore-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipstx39-* | mipstx39el-* \ + | msp430-* \ + | none-* | np1-* | nv1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* \ + | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \ + | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \ + | tahoe-* | thumb-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tron-* \ + | v850-* | v850e-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xps100-* | xscale-* | xstormy16-* \ + | xtensa-* \ + | ymp-* \ + | z8k-*) + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + crds | unos) + basic_machine=m68k-crds + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + mmix*) + basic_machine=mmix-knuth + os=-mmixware + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nv1) + basic_machine=nv1-cray + os=-unicosmp + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + or32 | or32-*) + basic_machine=or32-unknown + os=-coff + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2) + basic_machine=i686-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tic4x | c4x*) + basic_machine=tic4x-unknown + os=-coff + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tic55x | c55x*) + basic_machine=tic55x-unknown + os=-coff + ;; + tic6x | c6x*) + basic_machine=tic6x-unknown + os=-coff + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparc | sparcv9 | sparcv9b) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \ + | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -kaos*) + os=-kaos + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-ibm) + os=-aix + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..78f8e80 --- /dev/null +++ b/configure.in @@ -0,0 +1,489 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +# This Autoconf file was cobbled from various locations. + +AC_PREREQ(2.50) +AC_INIT(buffer.c) + +OLDCFLAGS=$CFLAGS +# Checks for programs. +AC_PROG_CC +AC_PROG_MAKE_SET + +if test -z "$LD" ; then + LD=$CC +fi +AC_SUBST(LD) + +if test -z "$OLDCFLAGS" && test "$GCC" = "yes"; then + AC_MSG_RESULT(No \$CFLAGS set... using "-Os -W -Wall for GCC") + CFLAGS="-Os -W -Wall" +fi + +# Host specific options +# this isn't a definitive list of hosts, they are just added as required +AC_CANONICAL_HOST + +case "$host" in + +*-*-linux*) + no_ptmx_check=1 + ;; + +*-*-solaris*) + CFLAGS="$CFLAGS -I/usr/local/include" + LDFLAGS="$LDFLAGS -L/usr/local/lib -R/usr/local/lib" + conf_lastlog_location="/var/adm/lastlog" + AC_MSG_CHECKING(for obsolete utmp and wtmp in solaris2.x) + sol2ver=`echo "$host"| sed -e 's/.*[[0-9]]\.//'` + if test "$sol2ver" -ge 8; then + AC_MSG_RESULT(yes) + AC_DEFINE(DISABLE_UTMP,,Disable utmp) + AC_DEFINE(DISABLE_WTMP,,Disable wtmp) + else + AC_MSG_RESULT(no) + fi + AC_CHECK_LIB(socket, socket, LIBS="$LIBS -lsocket") + AC_CHECK_LIB(nsl, yp_match, LIBS="$LIBS -lnsl") + ;; + +*-*-aix*) + AC_DEFINE(AIX,,Using AIX) + ;; + +*-*-hpux*) + LIBS="$LIBS -lsec" + ;; +esac + +AC_CHECK_TOOL(AR, ar, :) +AC_CHECK_TOOL(RANLIB, ranlib, :) +AC_CHECK_TOOL(STRIP, strip, :) +AC_CHECK_TOOL(INSTALL, install, :) + +dnl Can't use login() or logout() with uclibc +AC_CHECK_DECL(__UCLIBC__, + [ + no_loginfunc_check=1 + AC_MSG_RESULT(Using uClibc - login() and logout() probably don't work, so we won't use them.) + ],,,) + +# Checks for libraries. +AC_CHECK_LIB(crypt, crypt, LIBS="$LIBS -lcrypt") + +# Check if zlib is needed +AC_ARG_WITH(zlib, + [ --with-zlib=PATH Use zlib in PATH], + [ + # option is given + if test -d "$withval/lib"; then + LDFLAGS="-L${withval}/lib ${LDFLAGS}" + else + LDFLAGS="-L${withval} ${LDFLAGS}" + fi + if test -d "$withval/include"; then + CPPFLAGS="-I${withval}/include ${CPPFLAGS}" + else + CPPFLAGS="-I${withval} ${CPPFLAGS}" + fi + ] +) + +AC_ARG_ENABLE(zlib, + [ --disable-zlib Don't include zlib support], + [ + if test "x$enableval" = "xno"; then + AC_DEFINE(DISABLE_ZLIB,, Use zlib) + AC_MSG_RESULT(Disabling zlib) + else + AC_CHECK_LIB(z, deflate, , AC_MSG_ERROR([*** zlib missing - install first or check config.log ***])) + AC_MSG_RESULT(Enabling zlib) + fi + ], + [ + # if not disabled, check for zlib + AC_CHECK_LIB(z, deflate, , AC_MSG_ERROR([*** zlib missing - install first or check config.log ***])) + AC_MSG_RESULT(Enabling zlib) + ] +) + +AC_ARG_ENABLE(openpty, + [ --disable-openpty Don't use openpty, use alternative method], + [ + if test "x$enableval" = "xno"; then + AC_MSG_RESULT(Not using openpty) + else + AC_MSG_RESULT(Using openpty if available) + AC_SEARCH_LIBS(openpty, util, [AC_DEFINE(HAVE_OPENPTY,,Have openpty() function)]) + fi + ], + [ + AC_MSG_RESULT(Using openpty if available) + AC_SEARCH_LIBS(openpty, util, [AC_DEFINE(HAVE_OPENPTY)]) + ] +) + + +AC_ARG_ENABLE(syslog, + [ --disable-syslog Don't include syslog support], + [ + if test "x$enableval" = "xno"; then + AC_DEFINE(DISABLE_SYSLOG,, Using syslog) + AC_MSG_RESULT(Disabling syslog) + else + AC_MSG_RESULT(Enabling syslog) + fi + ], + [ + AC_MSG_RESULT(Enabling syslog) + ] +) + +AC_ARG_ENABLE(shadow, + [ --disable-shadow Don't use shadow passwords (if available)], + [ + if test "x$enableval" = "xno"; then + AC_MSG_RESULT(Not using shadow passwords) + else + AC_CHECK_HEADERS([shadow.h]) + AC_MSG_RESULT(Using shadow passwords if available) + fi + ], + [ + AC_CHECK_HEADERS([shadow.h]) + AC_MSG_RESULT(Using shadow passwords if available) + ] +) + + +# Checks for header files. +AC_HEADER_STDC +AC_HEADER_SYS_WAIT +AC_CHECK_HEADERS([fcntl.h limits.h netinet/in.h netinet/tcp.h stdlib.h string.h sys/socket.h sys/time.h termios.h unistd.h crypt.h pty.h ioctl.h libutil.h libgen.h inttypes.h stropts.h utmp.h utmpx.h lastlog.h paths.h util.h netdb.h sys/dirent.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_UID_T +AC_TYPE_MODE_T +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_HEADER_TIME + +AC_CHECK_TYPES([uint16_t, u_int16_t, struct sockaddr_storage]) +AC_CHECK_TYPE([socklen_t], ,[ + AC_MSG_CHECKING([for socklen_t equivalent]) + AC_CACHE_VAL([curl_cv_socklen_t_equiv], + [ + # Systems have either "struct sockaddr *" or + # "void *" as the second argument to getpeername + curl_cv_socklen_t_equiv= + for arg2 in "struct sockaddr" void; do + for t in int size_t unsigned long "unsigned long"; do + AC_TRY_COMPILE([ + #include + #include + + int getpeername (int, $arg2 *, $t *); + ],[ + $t len; + getpeername(0,0,&len); + ],[ + curl_cv_socklen_t_equiv="$t" + break + ]) + done + done + + if test "x$curl_cv_socklen_t_equiv" = x; then + AC_MSG_ERROR([Cannot find a type to use in place of socklen_t]) + fi + ]) + AC_MSG_RESULT($curl_cv_socklen_t_equiv) + AC_DEFINE_UNQUOTED(socklen_t, $curl_cv_socklen_t_equiv, + [type to use in place of socklen_t if not defined])], + [#include + #include ]) + + +# for loginrec.c + +AC_CHECK_MEMBERS([struct utmp.ut_host, struct utmp.ut_pid, struct utmp.ut_type, struct utmp.ut_tv, struct utmp.ut_id, struct utmp.ut_addr, struct utmp.ut_addr_v6, struct utmp.ut_exit, struct utmp.ut_time],,,[ +#include +#if HAVE_UTMP_H +#include +#endif +]) + +AC_CHECK_MEMBERS([struct utmpx.ut_host, struct utmpx.ut_syslen, struct utmpx.ut_type, struct utmpx.ut_id, struct utmpx.ut_addr, struct utmpx.ut_addr_v6, struct utmpx.ut_time, struct utmpx.ut_tv],,,[ +#include +#if HAVE_UTMPX_H +#include +#endif +]) + +AC_CHECK_FUNCS(endutent getutent getutid getutline pututline setutent) +AC_CHECK_FUNCS(utmpname) +AC_CHECK_FUNCS(endutxent getutxent getutxid getutxline pututxline ) +AC_CHECK_FUNCS(setutxent utmpxname) +AC_CHECK_FUNCS(logout updwtmp logwtmp) + +dnl Added from OpenSSH 3.6.1p2's configure.ac + +dnl allow user to disable some login recording features +AC_ARG_ENABLE(lastlog, + [ --disable-lastlog Disable use of lastlog even if detected [no]], + [ AC_DEFINE(DISABLE_LASTLOG,,Disable use of lastlog()) ] +) +AC_ARG_ENABLE(utmp, + [ --disable-utmp Disable use of utmp even if detected [no]], + [ AC_DEFINE(DISABLE_UTMP,,Disable use of utmp) ] +) +AC_ARG_ENABLE(utmpx, + [ --disable-utmpx Disable use of utmpx even if detected [no]], + [ AC_DEFINE(DISABLE_UTMPX,,Disable use of utmpx) ] +) +AC_ARG_ENABLE(wtmp, + [ --disable-wtmp Disable use of wtmp even if detected [no]], + [ AC_DEFINE(DISABLE_WTMP,,Disable use of wtmp) ] +) +AC_ARG_ENABLE(wtmpx, + [ --disable-wtmpx Disable use of wtmpx even if detected [no]], + [ AC_DEFINE(DISABLE_WTMPX,,Disable use of wtmpx) ] +) +AC_ARG_ENABLE(loginfunc, + [ --disable-loginfunc Disable use of login() etc. [no]], + [ no_loginfunc_check=1 + AC_MSG_RESULT(Not using login() etc) ] +) +AC_ARG_ENABLE(pututline, + [ --disable-pututline Disable use of pututline() etc. ([uw]tmp) [no]], + [ AC_DEFINE(DISABLE_PUTUTLINE,,Disable use of pututline()) ] +) +AC_ARG_ENABLE(pututxline, + [ --disable-pututxline Disable use of pututxline() etc. ([uw]tmpx) [no]], + [ AC_DEFINE(DISABLE_PUTUTXLINE,,Disable use of pututxline()) ] +) +AC_ARG_WITH(lastlog, + [ --with-lastlog=FILE|DIR specify lastlog location [common locations]], + [ + if test "x$withval" = "xno" ; then + AC_DEFINE(DISABLE_LASTLOG) + else + conf_lastlog_location=$withval + fi + ] +) + +if test -z "$no_loginfunc_check"; then + dnl Checks for libutil functions (login(), logout() etc, not openpty() ) + AC_SEARCH_LIBS(login, util bsd, [AC_DEFINE(HAVE_LOGIN,,Have login() function)]) + AC_CHECK_FUNCS(logout updwtmp logwtmp) +fi + +dnl lastlog, [uw]tmpx? detection +dnl NOTE: set the paths in the platform section to avoid the +dnl need for command-line parameters +dnl lastlog and [uw]tmp are subject to a file search if all else fails + +dnl lastlog detection +dnl NOTE: the code itself will detect if lastlog is a directory +AC_MSG_CHECKING([if your system defines LASTLOG_FILE]) +AC_TRY_COMPILE([ +#include +#include +#ifdef HAVE_LASTLOG_H +# include +#endif +#ifdef HAVE_PATHS_H +# include +#endif +#ifdef HAVE_LOGIN_H +# include +#endif + ], + [ char *lastlog = LASTLOG_FILE; ], + [ AC_MSG_RESULT(yes) ], + [ + AC_MSG_RESULT(no) + AC_MSG_CHECKING([if your system defines _PATH_LASTLOG]) + AC_TRY_COMPILE([ +#include +#include +#ifdef HAVE_LASTLOG_H +# include +#endif +#ifdef HAVE_PATHS_H +# include +#endif + ], + [ char *lastlog = _PATH_LASTLOG; ], + [ AC_MSG_RESULT(yes) ], + [ + AC_MSG_RESULT(no) + system_lastlog_path=no + ]) + ] +) + +if test -z "$conf_lastlog_location"; then + if test x"$system_lastlog_path" = x"no" ; then + for f in /var/log/lastlog /usr/adm/lastlog /var/adm/lastlog /etc/security/lastlog ; do + if (test -d "$f" || test -f "$f") ; then + conf_lastlog_location=$f + fi + done + if test -z "$conf_lastlog_location"; then + AC_MSG_WARN([** Cannot find lastlog **]) + dnl Don't define DISABLE_LASTLOG - that means we don't try wtmp/wtmpx + fi + fi +fi + +if test -n "$conf_lastlog_location"; then + AC_DEFINE_UNQUOTED(CONF_LASTLOG_FILE, "$conf_lastlog_location", lastlog file location) +fi + +dnl utmp detection +AC_MSG_CHECKING([if your system defines UTMP_FILE]) +AC_TRY_COMPILE([ +#include +#include +#ifdef HAVE_PATHS_H +# include +#endif + ], + [ char *utmp = UTMP_FILE; ], + [ AC_MSG_RESULT(yes) ], + [ AC_MSG_RESULT(no) + system_utmp_path=no ] +) +if test -z "$conf_utmp_location"; then + if test x"$system_utmp_path" = x"no" ; then + for f in /etc/utmp /usr/adm/utmp /var/run/utmp; do + if test -f $f ; then + conf_utmp_location=$f + fi + done + if test -z "$conf_utmp_location"; then + AC_DEFINE(DISABLE_UTMP) + fi + fi +fi +if test -n "$conf_utmp_location"; then + AC_DEFINE_UNQUOTED(CONF_UTMP_FILE, "$conf_utmp_location", utmp file location) +fi + +dnl wtmp detection +AC_MSG_CHECKING([if your system defines WTMP_FILE]) +AC_TRY_COMPILE([ +#include +#include +#ifdef HAVE_PATHS_H +# include +#endif + ], + [ char *wtmp = WTMP_FILE; ], + [ AC_MSG_RESULT(yes) ], + [ AC_MSG_RESULT(no) + system_wtmp_path=no ] +) +if test -z "$conf_wtmp_location"; then + if test x"$system_wtmp_path" = x"no" ; then + for f in /usr/adm/wtmp /var/log/wtmp; do + if test -f $f ; then + conf_wtmp_location=$f + fi + done + if test -z "$conf_wtmp_location"; then + AC_DEFINE(DISABLE_WTMP) + fi + fi +fi +if test -n "$conf_wtmp_location"; then + AC_DEFINE_UNQUOTED(CONF_WTMP_FILE, "$conf_wtmp_location", wtmp file location) +fi + + +dnl utmpx detection - I don't know any system so perverse as to require +dnl utmpx, but not define UTMPX_FILE (ditto wtmpx.) No doubt it's out +dnl there, though. +AC_MSG_CHECKING([if your system defines UTMPX_FILE]) +AC_TRY_COMPILE([ +#include +#include +#ifdef HAVE_UTMPX_H +#include +#endif +#ifdef HAVE_PATHS_H +# include +#endif + ], + [ char *utmpx = UTMPX_FILE; ], + [ AC_MSG_RESULT(yes) ], + [ AC_MSG_RESULT(no) + system_utmpx_path=no ] +) +if test -z "$conf_utmpx_location"; then + if test x"$system_utmpx_path" = x"no" ; then + AC_DEFINE(DISABLE_UTMPX) + fi +else + AC_DEFINE_UNQUOTED(CONF_UTMPX_FILE, "$conf_utmpx_location", utmpx file location) +fi + +dnl wtmpx detection +AC_MSG_CHECKING([if your system defines WTMPX_FILE]) +AC_TRY_COMPILE([ +#include +#include +#ifdef HAVE_UTMPX_H +#include +#endif +#ifdef HAVE_PATHS_H +# include +#endif + ], + [ char *wtmpx = WTMPX_FILE; ], + [ AC_MSG_RESULT(yes) ], + [ AC_MSG_RESULT(no) + system_wtmpx_path=no ] +) +if test -z "$conf_wtmpx_location"; then + if test x"$system_wtmpx_path" = x"no" ; then + AC_DEFINE(DISABLE_WTMPX) + fi +else + AC_DEFINE_UNQUOTED(CONF_WTMPX_FILE, "$conf_wtmpx_location", wtmpx file location) +fi + +# Checks for library functions. +AC_PROG_GCC_TRADITIONAL +AC_FUNC_MEMCMP +AC_FUNC_SELECT_ARGTYPES +AC_TYPE_SIGNAL +AC_CHECK_FUNCS([dup2 getspnam getusershell memset putenv select socket strdup clearenv strlcpy strlcat daemon basename _getpty ]) + +AC_SEARCH_LIBS(basename, gen, AC_DEFINE(HAVE_BASENAME)) + +# Solaris needs ptmx +if test -z "$no_ptmx_check" ; then + if test x"$cross_compiling" = x"no" ; then + AC_CHECK_FILE("/dev/ptmx", AC_DEFINE(USE_DEV_PTMX,,Use /dev/ptmx)) + else + AC_MSG_RESULT(Not checking for /dev/ptmx, we're cross-compiling) + fi +fi + +if test -z "$no_ptc_check" ; then + if test x"$cross_compiling" = x"no" ; then + AC_CHECK_FILE("/dev/ptc", AC_DEFINE(HAVE_DEV_PTS_AND_PTC,,Use /dev/ptc & /dev/pts)) + else + AC_MSG_RESULT(Not checking for /dev/ptc & /dev/pts\, we're cross-compiling) + fi +fi + +AC_CONFIG_HEADER(config.h) +AC_OUTPUT(Makefile) +AC_MSG_RESULT() +AC_MSG_RESULT(Now edit options.h to choose features.) diff --git a/dbmulti.c b/dbmulti.c new file mode 100644 index 0000000..d82eff0 --- /dev/null +++ b/dbmulti.c @@ -0,0 +1,48 @@ +#include "includes.h" + +/* definitions are cleanest if we just put them here */ +int dropbear_main(int argc, char ** argv); +int dropbearkey_main(int argc, char ** argv); +int dropbearconvert_main(int argc, char ** argv); + +int main(int argc, char ** argv) { + + char * progname; + + if (argc > 0) { + /* figure which form we're being called as */ + progname = basename(argv[0]); + +#ifdef DBMULTI_DROPBEAR + if (strcmp(progname, "dropbear") == 0) { + return dropbear_main(argc, argv); + } +#endif +#ifdef DBMULTI_KEY + if (strcmp(progname, "dropbearkey") == 0) { + return dropbearkey_main(argc, argv); + } +#endif +#ifdef DBMULTI_CONVERT + if (strcmp(progname, "dropbearconvert") == 0) { + return dropbearconvert_main(argc, argv); + } +#endif + } + + fprintf(stderr, "Dropbear multi-purpose version %s\n" + "Make a symlink pointing at this binary with one of the following names:\n" +#ifdef DBMULTI_DROPBEAR + "'dropbear' - the Dropbear server\n" +#endif +#ifdef DBMULTI_KEY + "'dropbearkey' - the key generator\n" +#endif +#ifdef DBMULTI_CONVERT + "'dropbearconvert' - the key converter\n" +#endif + , + DROPBEAR_VERSION); + exit(1); + +} diff --git a/dbutil.c b/dbutil.c new file mode 100644 index 0000000..e03d648 --- /dev/null +++ b/dbutil.c @@ -0,0 +1,296 @@ +/* + * 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. + * + * strlcat() is copyright as follows: + * Copyright (c) 1998 Todd C. Miller + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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. */ + +#include "includes.h" +#include "dbutil.h" +#include "buffer.h" +#include "session.h" +#include "atomicio.h" + +#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; + +int usingsyslog = 0; /* set by runopts, but required externally to sessions */ +#ifndef DISABLE_SYSLOG +void startsyslog() { + + openlog(PROGNAME, LOG_PID, LOG_AUTHPRIV); + +} +#endif /* DISABLE_SYSLOG */ + +/* the "format" string must be <= 100 characters */ +void dropbear_close(const char* format, ...) { + + va_list param; + + va_start(param, format); + _dropbear_exit(EXIT_SUCCESS, format, param); + va_end(param); + +} + +void dropbear_exit(const char* format, ...) { + + va_list param; + + va_start(param, format); + _dropbear_exit(EXIT_FAILURE, format, param); + va_end(param); +} + + +/* this is what can be called to write arbitrary log messages */ +void dropbear_log(int priority, const char* format, ...) { + + va_list param; + + va_start(param, format); + _dropbear_log(priority, format, param); + va_end(param); +} + + +#ifdef DEBUG_TRACE +void dropbear_trace(const char* format, ...) { + + va_list param; + + va_start(param, format); + fprintf(stderr, "TRACE: "); + vfprintf(stderr, format, param); + fprintf(stderr, "\n"); + va_end(param); +} +#endif /* DEBUG_TRACE */ + +/* Return a string representation of the socket address passed. The return + * value is allocated with malloc() */ +unsigned char * getaddrstring(struct sockaddr * addr) { + + char *retstring; + + /* space for "255.255.255.255:65535\0" = 22 */ + retstring = m_malloc(22); + + 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; + + default: + /* XXX ipv6 */ + strcpy(retstring, "Bad protocol"); + + } + 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) { + + struct hostent *host = NULL; + char * retstring; + +#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; + } + + return strdup(retstring); +} +#ifdef DEBUG_TRACE +void printhex(unsigned char* buf, int len) { + + int i; + + for (i = 0; i < len; i++) { + fprintf(stderr, "%02x", buf[i]); + if (i % 16 == 15) { + fprintf(stderr, "\n"); + } + else if (i % 2 == 1) { + fprintf(stderr, " "); + } + } + fprintf(stderr, "\n"); +} +#endif + +/* Strip all control characters from text (a null-terminated string), except + * for '\n', '\r' and '\t'. + * The result returned is a newly allocated string, this must be free()d after + * use */ +char * stripcontrol(const char * text) { + + char * ret; + int len, pos; + int i; + + len = strlen(text); + ret = m_malloc(len+1); + + pos = 0; + for (i = 0; i < len; i++) { + if ((text[i] <= '~' && text[i] >= ' ') /* normal printable range */ + || text[i] == '\n' || text[i] == '\r' || text[i] == '\t') { + ret[pos] = text[i]; + pos++; + } + } + ret[pos] = 0x0; + return ret; +} + + +/* reads the contents of filename into the buffer buf, from the current + * position, either to the end of the file, or the buffer being full. + * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +int buf_readfile(buffer* buf, const char* filename) { + + int fd; + int len; + int maxlen; + + fd = open(filename, O_RDONLY); + + if (fd < 0) { + close(fd); + return DROPBEAR_FAILURE; + } + + do { + maxlen = buf->size - buf->pos; + len = read(fd, buf_getwriteptr(buf, maxlen), + maxlen); + buf_incrwritepos(buf, len); + } while (len < maxlen && len > 0); + + close(fd); + return DROPBEAR_SUCCESS; +} + +/* loop until the socket is closed (in case of EINTR) or + * we get and error. + * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +int m_close(int fd) { + + int val; + do { + val = close(fd); + } while (val < 0 && errno == EINTR); + + if (val == 0 || errno == EBADF) { + return DROPBEAR_SUCCESS; + } else { + return DROPBEAR_FAILURE; + } +} + +void * m_malloc(size_t size) { + + void* ret; + + if (size == 0) { + dropbear_exit("m_malloc failed"); + } + ret = malloc(size); + if (ret == NULL) { + dropbear_exit("m_malloc failed"); + } + return ret; + +} + +void __m_free(void* ptr) { + if (ptr != NULL) { + free(ptr); + } +} + +void * m_realloc(void* ptr, size_t size) { + + void *ret; + + if (size == 0) { + dropbear_exit("m_realloc failed"); + } + ret = realloc(ptr, size); + if (ret == NULL) { + dropbear_exit("m_realloc failed"); + } + return ret; +} + +/* Clear the data, based on the method in David Wheeler's + * "Secure Programming for Linux and Unix HOWTO" */ +void m_burn(void *data, unsigned int len) { + volatile char *p = data; + + if (data == NULL) + return; + while (len--) { + *p++ = 0x66; + } +} diff --git a/dbutil.h b/dbutil.h new file mode 100644 index 0000000..d13b13e --- /dev/null +++ b/dbutil.h @@ -0,0 +1,59 @@ +/* + * 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 _DBUTIL_H_ + +#define _DBUTIL_H_ + +#include "includes.h" +#include "buffer.h" + +#ifndef DISABLE_SYSLOG +void startsyslog(); +#endif +extern int usingsyslog; + +extern void (*_dropbear_exit)(int exitcode, const char* format, va_list param); +extern void (*_dropbear_log)(int priority, const char* format, va_list param); + +void dropbear_exit(const char* format, ...); +void dropbear_close(const char* format, ...); +void dropbear_log(int priority, const char* format, ...); +#ifdef DEBUG_TRACE +void dropbear_trace(const char* format, ...); +void printhex(unsigned char* buf, int len); +#endif +char * stripcontrol(const char * text); +unsigned char * getaddrstring(struct sockaddr * addr); +char* getaddrhostname(struct sockaddr * addr); +int buf_readfile(buffer* buf, const char* filename); + +int m_close(int fd); +void * m_malloc(size_t size); +void * m_realloc(void* ptr, size_t size); +#define m_free(X) __m_free(X); (X) = NULL; +void __m_free(void* ptr); +void m_burn(void* data, unsigned int len); + +#endif /* _DBUTIL_H_ */ diff --git a/debian/README.Debian b/debian/README.Debian new file mode 100644 index 0000000..8cdac38 --- /dev/null +++ b/debian/README.Debian @@ -0,0 +1,41 @@ +Dropbear for Debian +------------------- + +This package will attempt to listen on port 22. If the OpenSSH +package ("ssh") is installed, the file /etc/default/dropbear +will be set up so that the server does not start by default. + +You can run Dropbear concurrently with OpenSSH 'sshd' by +modifying /etc/default/dropbear so that "NO_START" is set to +"0" and changing the port number that Dropbear runs on. Follow +the instructions in the file. + +This package suggests you install the "ssh" package. This package +provides the "ssh" client program, as well as the "/usr/bin/scp" +binary you will need to be able to retrieve files from a server +running Dropbear via SCP. + +Replacing OpenSSH "sshd" with Dropbear +-------------------------------------- + +You will still want to have the "ssh" package installed, as it +provides the "ssh" and "scp" binaries. When you install this +package, it checks for existing OpenSSH host keys and if found, +converts them to the Dropbear format. + +If this appears to have worked, you should be able to change over +by following these steps: + +1. Stop the OpenSSH server + % /etc/init.d/ssh stop +2. Prevent the OpenSSH server from starting in the future + % touch /etc/ssh/sshd_not_to_be_run +3. Modify the Dropbear defaults file, set NO_START to 0 and + ensure DROPBEAR_PORT is set to 22. + % editor /etc/default/dropbear +4. Restart the Dropbear server. + % /etc/init.d/dropbear restart + +See the Dropbear homepage for more information: + http://matt.ucc.asn.au/dropbear/dropbear.html + diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..cb7253f --- /dev/null +++ b/debian/changelog @@ -0,0 +1,79 @@ +dropbear (0.41-1) unstable; urgency=low + + * Updated to 0.41 release. + * Various minor fixes + + -- Matt Johnston Mon, 19 Jan 2004 23:20:54 +0800 + +dropbear (0.39-1) unstable; urgency=low + + * updated to 0.39 release. Some new features, some bugfixes. + + -- Matt Johnston Tue, 16 Dec 2003 16:20:54 +0800 + +dropbear (0.38-1) unstable; urgency=medium + + * updated to 0.38 release - various important bugfixes + + -- Matt Johnston Sat, 11 Oct 2003 16:28:54 +0800 + +dropbear (0.37-1) unstable; urgency=medium + + * updated to 0.37 release - various important bugfixes + + -- Matt Johnston Wed, 24 Sept 2003 19:43:54 +0800 + +dropbear (0.36-1) unstable; urgency=high + + * updated to 0.36 release - various important bugfixes + + -- Matt Johnston Tues, 19 Aug 2003 12:20:54 +0800 + +dropbear (0.35-1) unstable; urgency=high + + * updated to 0.35 release - contains fix for remotely exploitable + vulnerability. + + -- Matt Johnston Sun, 17 Aug 2003 05:37:47 +0800 + +dropbear (0.34-1) unstable; urgency=medium + + * updated to 0.34 release + + -- Matt Johnston Fri, 15 Aug 2003 15:10:00 +0800 + +dropbear (0.33-1) unstable; urgency=medium + + * updated to 0.33 release + + -- Matt Johnston Sun, 22 Jun 2003 22:22:00 +0800 + +dropbear (0.32cvs-1) unstable; urgency=medium + + * now maintained in UCC CVS + * debian/copyright.in file added, generated from LICENSE + + -- Grahame Bowland Tue, 21 Jun 2003 17:57:02 +0800 + +dropbear (0.32cvs-1) unstable; urgency=medium + + * sync with CVS + * fixes X crash bug + + -- Grahame Bowland Tue, 20 Jun 2003 15:04:47 +0800 + +dropbear (0.32-2) unstable; urgency=low + + * fix creation of host keys to use correct names in /etc/dropbear + * init script "restart" function fixed + * purging this package now deletes the host keys and /etc/dropbear + * change priority in debian/control to 'standard' + + -- Grahame Bowland Tue, 17 Jun 2003 15:04:47 +0800 + +dropbear (0.32-1) unstable; urgency=low + + * Initial Release. + + -- Grahame Bowland Tue, 17 Jun 2003 15:04:47 +0800 + diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..b8626c4 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +4 diff --git a/debian/conffiles b/debian/conffiles new file mode 100644 index 0000000..e69de29 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..e528454 --- /dev/null +++ b/debian/control @@ -0,0 +1,14 @@ +Source: dropbear +Section: net +Priority: standard +Maintainer: Grahame Bowland +Build-Depends: debhelper (>> 4.0.0), zlib1g-dev +Standards-Version: 3.5.8 + +Package: dropbear +Architecture: any +Depends: ${shlibs:Depends} ${misc:Depends} +Suggests: ssh +Description: a minimal SSH2 server + A small secure shell version 2 server. + diff --git a/debian/copyright.in b/debian/copyright.in new file mode 100644 index 0000000..015d9ab --- /dev/null +++ b/debian/copyright.in @@ -0,0 +1,9 @@ +This package was debianized by Grahame Bowland on +Tue, 17 Jun 2003 15:04:47 +0800. + +It was downloaded from http://matt.ucc.asn.au/dropbear/ + +Upstream Author(s): Matt Johnston + +Copyright: + diff --git a/debian/dirs b/debian/dirs new file mode 100644 index 0000000..ca882bb --- /dev/null +++ b/debian/dirs @@ -0,0 +1,2 @@ +usr/bin +usr/sbin diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..724e084 --- /dev/null +++ b/debian/docs @@ -0,0 +1,2 @@ +README +TODO diff --git a/debian/dropbear.default b/debian/dropbear.default new file mode 100644 index 0000000..e69de29 diff --git a/debian/dropbear.init b/debian/dropbear.init new file mode 100644 index 0000000..25eda9f --- /dev/null +++ b/debian/dropbear.init @@ -0,0 +1,78 @@ +#! /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 +# + +# +# Do not configure this file. Edit /etc/default/dropbear instead! +# + +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin +DAEMON=/usr/sbin/dropbear +NAME=dropbear +DESC="Dropbear SSH server" + +DROPBEAR_PORT=22 +DROPBEAR_EXTRA_ARGS= +NO_START=0 +set -e +test -f /etc/default/dropbear && . /etc/default/dropbear + +if [ -n "$DROPBEAR_BANNER" ]; then + DROPBEAR_EXTRA_ARGS="$DROPBEAR_EXTRA_ARGS -b $DROPBEAR_BANNER" +fi + +if [ -z "$DROPBEAR_RSAKEY" ]; then + DROPBEAR_RSAKEY="/etc/dropbear/dropbear_rsa_host_key" +fi + +if [ -z "$DROPBEAR_DSSKEY" ]; then + DROPBEAR_DSSKEY="/etc/dropbear/dropbear_dss_host_key" +fi + +test "$NO_START" != "0" && exit 0 + +test -x $DAEMON || exit 0 + +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 + echo "$NAME." + ;; + stop) + echo -n "Stopping $DESC: " + 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 + 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 + 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 + ;; +esac + +exit 0 diff --git a/debian/postinst b/debian/postinst new file mode 100644 index 0000000..010618d --- /dev/null +++ b/debian/postinst @@ -0,0 +1,90 @@ +#! /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 --git a/debian/postrm b/debian/postrm new file mode 100644 index 0000000..da11dc4 --- /dev/null +++ b/debian/postrm @@ -0,0 +1,45 @@ +#! /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 --git a/debian/rules b/debian/rules new file mode 100644 index 0000000..4d73093 --- /dev/null +++ b/debian/rules @@ -0,0 +1,134 @@ +#!/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 + +ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) + CFLAGS += -O0 +else + CFLAGS += -O2 +endif +ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) + INSTALL_PROGRAM += -s +endif + +config.status: configure + dh_testdir + # Add here commands to configure the package. + CFLAGS='-DSFTPSERVER_PATH="\"/usr/lib/sftp-server\""' ./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --prefix=/usr --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info + + +#Architecture +build: build-arch #build-indep + +build-arch: build-arch-stamp +build-arch-stamp: config.status + + # Add here commands to compile the arch part of the package. + $(MAKE) CC=gcc LD=gcc + +build-indep: build-indep-stamp +build-indep-stamp: config.status + + # Add here commands to compile the indep part of the package. + #$(MAKE) doc + +clean: + dh_testdir + dh_testroot + rm -f build-arch-stamp build-indep-stamp config-stamp + + # Add here commands to clean up after the build process. + -$(MAKE) clean +ifneq "$(wildcard /usr/share/misc/config.sub)" "" + cp -f /usr/share/misc/config.sub config.sub +endif +ifneq "$(wildcard /usr/share/misc/config.guess)" "" + cp -f /usr/share/misc/config.guess config.guess +endif + + + dh_clean + +install: install-indep install-arch +install-indep: + dh_testdir + dh_testroot + dh_clean -k -i + dh_installdirs -i + + # Add here commands to install the indep part of the package into + # debian/-doc. + #INSTALLDOC# + + dh_install -i + +install-arch: + dh_testdir + dh_testroot + dh_clean -k -a + dh_installdirs -a + dh_installdirs /etc/dropbear + + # Add here commands to install the arch part of the package into + # debian/tmp. + $(MAKE) install prefix=$(CURDIR)/debian/dropbear/usr + + dh_install -a +# Must not depend on anything. This is to be called by +# binary-arch/binary-multi +# in another 'make' thread. +binary-common: + cat $(CURDIR)/debian/copyright.in $(CURDIR)/LICENSE > $(CURDIR)/debian/copyright + dh_testdir + dh_testroot + dh_installchangelogs CHANGES + dh_installdocs + dh_installexamples +# dh_installmenu +# dh_installdebconf +# dh_installlogrotate +# dh_installemacsen +# dh_installpam +# dh_installmime + dh_installinit +# dh_installcron +# dh_installinfo + dh_installman + dh_link + dh_strip + dh_compress + dh_fixperms +# dh_perl +# dh_python + dh_makeshlibs + dh_installdeb + dh_gencontrol + dh_md5sums + dh_builddeb +# Build architecture independant packages using the common target. +binary-indep: build-indep install-indep + $(MAKE) -f debian/rules DH_OPTIONS=-i binary-common + +# Build architecture dependant packages using the common target. +binary-arch: build-arch install-arch + $(MAKE) -f debian/rules DH_OPTIONS=-a binary-common + +binary: binary-arch #binary-indep +.PHONY: build clean binary-indep binary-arch binary install install-indep install-arch diff --git a/debug.h b/debug.h new file mode 100644 index 0000000..2b4a3dc --- /dev/null +++ b/debug.h @@ -0,0 +1,68 @@ +/* + * 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 _DEBUG_H_ +#define _DEBUG_H_ + +#include "includes.h" + +/* Debugging */ + +/* Work well for valgrind - don't clear environment, be nicer with signals + * etc. Don't use this normally, it might cause problems */ +/* #define DEBUG_VALGRIND */ + +/* Define this to print trace statements - very verbose */ +/* #define DEBUG_TRACE */ + +/* All functions writing to the cleartext payload buffer call + * CHECKCLEARTOWRITE() before writing. This is only really useful if you're + * attempting to track down a problem */ +#define CHECKCLEARTOWRITE() assert(ses.writepayload->len == 0 && \ + ses.writepayload->pos == 0) + +/* 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 */ +/*#define DEBUG_FORKGPROF*/ + +/* A couple of flags, not usually useful, and mightn't do anything */ + +/*#define DEBUG_KEXHASH*/ +/*#define DEBUG_RSA*/ + +/* you don't need to touch this block */ +#ifdef DEBUG_TRACE +#define TRACE(X) (dropbear_trace X) +#else /*DEBUG_TRACE*/ +#define TRACE(X) +#endif /*DEBUG_TRACE*/ + +/* For testing as non-root on shadowed systems, include the crypt of a password + * here. You can then log in as any user with this password. Ensure that you + * make your own password, and are careful about using this. This will also + * disable some of the chown pty code etc*/ +/* #define DEBUG_HACKCRYPT "hL8nrFDt0aJ3E" */ /* this is crypt("password") */ + +#endif diff --git a/dropbearconvert.c b/dropbearconvert.c new file mode 100644 index 0000000..0929ba8 --- /dev/null +++ b/dropbearconvert.c @@ -0,0 +1,146 @@ +/* + * 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. */ + +/* This program converts to/from Dropbear and OpenSSH private-key formats */ +#include "includes.h" +#include "signkey.h" +#include "buffer.h" +#include "dbutil.h" +#include "keyimport.h" + + +static int do_convert(int intype, const char* infile, int outtype, + const char* outfile); + +static void printhelp(char * progname); + +static void printhelp(char * progname) { + + fprintf(stderr, "Usage: %s \n\n" + "CAUTION: This program is for convenience only, and is not secure if used on\n" + "untrusted input files, ie it could allow arbitrary code execution.\n" + "All parameters must be specified in order.\n" + "\n" + "The input and output types are one of:\n" + "openssh\n" + "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); +} + +#if defined(DBMULTI_CONVERT) || !defined(DROPBEAR_MULTI) +#if defined(DBMULTI_CONVERT) && defined(DROPBEAR_MULTI) +int dropbearconvert_main(int argc, char ** argv) { +#else +int main(int argc, char ** argv) { +#endif + + int intype, outtype; + const char* infile; + const char* outfile; + + /* get the commandline options */ + if (argc != 5) { + fprintf(stderr, "All arguments must be specified\n"); + goto usage; + } + + /* input type */ + if (argv[1][0] == 'd') { + intype = KEYFILE_DROPBEAR; + } else if (argv[1][0] == 'o') { + intype = KEYFILE_OPENSSH; + } else { + fprintf(stderr, "Invalid input key type\n"); + goto usage; + } + + /* output type */ + if (argv[2][0] == 'd') { + outtype = KEYFILE_DROPBEAR; + } else if (argv[2][0] == 'o') { + outtype = KEYFILE_OPENSSH; + } else { + fprintf(stderr, "Invalid output key type\n"); + goto usage; + } + + /* we don't want output readable by others */ + umask(077); + + infile = argv[3]; + outfile = argv[4]; + + return do_convert(intype, infile, outtype, outfile); + +usage: + printhelp(argv[0]); + return 1; +} +#endif + +static int do_convert(int intype, const char* infile, int outtype, + const char* outfile) { + + sign_key * key = NULL; + char * keytype = NULL; + int ret = 1; + + key = import_read(infile, NULL, intype); + if (!key) { + fprintf(stderr, "Error reading key from '%s'\n", + infile); + goto out; + } + +#ifdef DROPBEAR_RSA + if (key->rsakey != NULL) { + keytype = "RSA"; + } +#endif +#ifdef DROPBEAR_DSS + if (key->dsskey != NULL) { + keytype = "DSS"; + } +#endif + + fprintf(stderr, "Key is a %s key\n", keytype); + + if (import_write(outfile, key, NULL, outtype) != 1) { + fprintf(stderr, "Error writing key to '%s'\n", outfile); + } else { + fprintf(stderr, "Wrote key to '%s'\n", outfile); + ret = 0; + } + +out: + if (key) { + sign_key_free(key); + } + return ret; +} diff --git a/dropbearkey.c b/dropbearkey.c new file mode 100644 index 0000000..bc2b1ae --- /dev/null +++ b/dropbearkey.c @@ -0,0 +1,272 @@ +/* + * 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. */ + +/* The format of the keyfiles is basically a raw dump of the buffer. Data types + * are specified in the transport draft - string is a 32-bit len then the + * non-null-terminated string, mp_int is a 32-bit len then the bignum data. + * The actual functions are buf_put_rsa_priv_key() and buf_put_dss_priv_key() + + * RSA: + * string "ssh-rsa" + * mp_int e + * mp_int n + * mp_int d + * mp_int p (newer versions only) + * mp_int q (newer versions only) + * + * DSS: + * string "ssh-dss" + * mp_int p + * mp_int q + * mp_int g + * mp_int y + * mp_int x + * + */ +#include "includes.h" +#include "runopts.h" +#include "signkey.h" +#include "buffer.h" +#include "dbutil.h" + +#include "genrsa.h" +#include "gendss.h" + +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); + +/* 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" +#ifdef DROPBEAR_RSA + " rsa\n" +#endif +#ifdef DROPBEAR_DSS + " 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); +} + +#if defined(DBMULTI_KEY) || !defined(DROPBEAR_MULTI) +#if defined(DBMULTI_KEY) && defined(DROPBEAR_MULTI) +int dropbearkey_main(int argc, char ** argv) { +#else +int main(int argc, char ** argv) { +#endif + + int i; + char ** next = 0; + sign_key *key; + buffer *buf; + char * filename = NULL; + int keytype = -1; + char * typetext = NULL; + char * sizetext = NULL; + unsigned int bits; + unsigned int keysize; + + /* get the commandline options */ + for (i = 1; i < argc; i++) { + if (next) { + *next = argv[i]; + if (*next == NULL) { + fprintf(stderr, "Invalid null argument"); + } + next = 0x00; + continue; + } + + if (argv[i][0] == '-') { + switch (argv[i][1]) { + case 'f': + next = &filename; + break; + case 't': + next = &typetext; + break; + case 's': + next = &sizetext; + break; + case 'h': + printhelp(argv[0]); + exit(EXIT_SUCCESS); + break; + default: + fprintf(stderr, "Unknown argument %s\n", argv[i]); + printhelp(argv[0]); + exit(EXIT_FAILURE); + break; + } + } + } + + /* 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" + ); + printhelp(argv[0]); + exit(EXIT_FAILURE); + } + + if (strlen(typetext) == 3) { +#ifdef DROPBEAR_RSA + if (strncmp(typetext, "rsa", 3) == 0) { + keytype = DROPBEAR_SIGNKEY_RSA; + TRACE(("type is rsa")); + } +#endif +#ifdef DROPBEAR_DSS + if (strncmp(typetext, "dss", 3) == 0) { + keytype = DROPBEAR_SIGNKEY_DSS; + TRACE(("type is dss")); + } +#endif + } + if (keytype == -1) { + fprintf(stderr, "Unknown key type '%s'\n", typetext); + printhelp(argv[0]); + exit(EXIT_FAILURE); + } + + if (sizetext) { + if (sscanf(sizetext, "%u", &bits) != 1) { + fprintf(stderr, "Bits must be an integer\n"); + exit(EXIT_FAILURE); + } + + if (bits < 512 || bits > 4096 || (bits % 8 != 0)) { + fprintf(stderr, "Bits must satisfy 512 <= bits <= 4096, and be a" + " multiple of 8\n"); + exit(EXIT_FAILURE); + } + + keysize = bits / 8; + } else { + if (keytype == DROPBEAR_SIGNKEY_DSS) { + keysize = DSS_SIZE; + } else if (keytype == DROPBEAR_SIGNKEY_RSA) { + keysize = RSA_SIZE; + } else { + exit(EXIT_FAILURE); /* not reached */ + } + } + + 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); + + /* don't want the file readable by others */ + umask(077); + + /* now we can generate the key */ + key = new_sign_key(); + + fprintf(stderr, "Generating key, this may take a while...\n"); + switch(keytype) { +#ifdef DROPBEAR_RSA + case DROPBEAR_SIGNKEY_RSA: + key->rsakey = gen_rsa_priv_key(keysize); /* 128 bytes = 1024 bit */ + break; +#endif +#ifdef DROPBEAR_DSS + case DROPBEAR_SIGNKEY_DSS: + key->dsskey = gen_dss_priv_key(keysize); /* 128 bytes = 1024 bit */ + break; +#endif + default: + fprintf(stderr, "Internal error, bad key type\n"); + exit(EXIT_FAILURE); + } + + buf = buf_new(BUF_SIZE); + + buf_put_priv_key(buf, key, keytype); + buf_setpos(buf, 0); + buf_writefile(buf, filename); + + buf_burn(buf); + buf_free(buf); + sign_key_free(key); + + fprintf(stderr, "Done.\n"); + + return EXIT_SUCCESS; +} +#endif + +/* Write a buffer to a file specified, failing if the file exists */ +static void buf_writefile(buffer * buf, const char * filename) { + + int fd; + int len; + + fd = open(filename, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); + if (fd < 0) { + fprintf(stderr, "Couldn't create new file %s\n", filename); + perror("Reason"); + buf_burn(buf); + exit(EXIT_FAILURE); + } + + /* write the file now */ + while (buf->pos != buf->len) { + len = write(fd, buf_getptr(buf, buf->len - buf->pos), + buf->len - buf->pos); + if (errno == EINTR) { + continue; + } + if (len <= 0) { + fprintf(stderr, "Failed writing file '%s'\n",filename); + perror("Reason"); + exit(EXIT_FAILURE); + } + buf_incrpos(buf, len); + } + + close(fd); +} diff --git a/dss.c b/dss.c new file mode 100644 index 0000000..ebe80c6 --- /dev/null +++ b/dss.c @@ -0,0 +1,399 @@ +/* + * 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. */ + +#include "includes.h" +#include "dbutil.h" +#include "bignum.h" +#include "dss.h" +#include "buffer.h" +#include "ssh.h" +#include "random.h" + +/* Handle DSS (Digital Signature Standard), aka DSA (D.S. Algorithm), + * operations, such as key reading, signing, verification. Key generation + * is in gendss.c, since it isn't required in the server itself. + * + * See FIPS186 or the Handbook of Applied Cryptography for details of the + * algorithm */ + +#ifdef DROPBEAR_DSS + +/* Load a dss key from a buffer, initialising the values. + * The key will have the same format as buf_put_dss_key. + * These should be freed with dss_key_free. + * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +int buf_get_dss_pub_key(buffer* buf, dss_key *key) { + + assert(key != NULL); + key->p = m_malloc(sizeof(mp_int)); + key->q = m_malloc(sizeof(mp_int)); + key->g = m_malloc(sizeof(mp_int)); + key->y = m_malloc(sizeof(mp_int)); + m_mp_init_multi(key->p, key->q, key->g, key->y, NULL); + key->x = NULL; + + buf_incrpos(buf, 4+SSH_SIGNKEY_DSS_LEN); /* int + "ssh-dss" */ + if (buf_getmpint(buf, key->p) == DROPBEAR_FAILURE + || buf_getmpint(buf, key->q) == DROPBEAR_FAILURE + || buf_getmpint(buf, key->g) == DROPBEAR_FAILURE + || buf_getmpint(buf, key->y) == DROPBEAR_FAILURE) { + return DROPBEAR_FAILURE; + } + + if (mp_count_bits(key->p) < MIN_DSS_KEYLEN) { + dropbear_log(LOG_WARNING, "DSS key too short"); + return DROPBEAR_FAILURE; + } + + return DROPBEAR_SUCCESS; +} + +/* Same as buf_get_dss_pub_key, but reads a private "x" key at the end. + * Loads a private dss key from a buffer + * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +int buf_get_dss_priv_key(buffer* buf, dss_key *key) { + + int ret = DROPBEAR_FAILURE; + + assert(key != NULL); + + ret = buf_get_dss_pub_key(buf, key); + if (ret == DROPBEAR_FAILURE) { + return DROPBEAR_FAILURE; + } + + key->x = m_malloc(sizeof(mp_int)); + m_mp_init(key->x); + ret = buf_getmpint(buf, key->x); + + return ret; +} + + +/* Clear and free the memory used by a public or private key */ +void dss_key_free(dss_key *key) { + + TRACE(("enter dsa_key_free")); + if (key == NULL) { + TRACE(("enter dsa_key_free: key == NULL")); + return; + } + if (key->p) { + mp_clear(key->p); + m_free(key->p); + } + if (key->q) { + mp_clear(key->q); + m_free(key->q); + } + if (key->g) { + mp_clear(key->g); + m_free(key->g); + } + if (key->y) { + mp_clear(key->y); + m_free(key->y); + } + if (key->x) { + mp_clear(key->x); + m_free(key->x); + } + m_free(key); + TRACE(("leave dsa_key_free")); +} + +/* put the dss public key into the buffer in the required format: + * + * string "ssh-dss" + * mpint p + * mpint q + * mpint g + * mpint y + */ +void buf_put_dss_pub_key(buffer* buf, dss_key *key) { + + assert(key != NULL); + buf_putstring(buf, SSH_SIGNKEY_DSS, SSH_SIGNKEY_DSS_LEN); + buf_putmpint(buf, key->p); + buf_putmpint(buf, key->q); + buf_putmpint(buf, key->g); + buf_putmpint(buf, key->y); + +} + +/* Same as buf_put_dss_pub_key, but with the private "x" key appended */ +void buf_put_dss_priv_key(buffer* buf, dss_key *key) { + + assert(key != NULL); + buf_put_dss_pub_key(buf, key); + buf_putmpint(buf, key->x); + +} + +#ifdef DROPBEAR_SIGNKEY_VERIFY +/* Verify a DSS signature (in buf) made on data by the key given. + * returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +int buf_dss_verify(buffer* buf, dss_key *key, const unsigned char* data, + unsigned int len) { + + unsigned char msghash[SHA1_HASH_SIZE]; + hash_state hs; + int ret = DROPBEAR_FAILURE; + mp_int val1, val2, val3, val4; + char * string = NULL; + int stringlen; + + TRACE(("enter buf_dss_verify")); + assert(key != NULL); + + /* get blob, check length */ + string = buf_getstring(buf, &stringlen); + if (stringlen != 2*SHA1_HASH_SIZE) { + goto out; + } + + /* hash the data */ + sha1_init(&hs); + 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' */ + if (mp_read_unsigned_bin(&val1, &string[SHA1_HASH_SIZE], SHA1_HASH_SIZE) + != MP_OKAY) { + goto out; + } + if (mp_cmp(&val1, key->q) != MP_LT) { + TRACE(("verify failed, s' >= q")); + goto out; + } + /* let val2 = w = (s')^-1 mod q*/ + if (mp_invmod(&val1, key->q, &val2) != MP_OKAY) { + goto out; + } + + /* u1 = ((SHA(M')w) mod q */ + /* let val1 = SHA(M') = msghash */ + if (mp_read_unsigned_bin(&val1, msghash, SHA1_HASH_SIZE) != MP_OKAY) { + goto out; + } + /* let val3 = u1 = ((SHA(M')w) mod q */ + if (mp_mulmod(&val1, &val2, key->q, &val3) != MP_OKAY) { + goto out; + } + + /* u2 = ((r')w) mod q */ + /* let val1 = r' */ + if (mp_read_unsigned_bin(&val1, &string[0], SHA1_HASH_SIZE) + != MP_OKAY) { + goto out; + } + if (mp_cmp(&val1, key->q) != MP_LT) { + TRACE(("verify failed, r' >= q")); + goto out; + } + /* let val4 = u2 = ((r')w) mod q */ + if (mp_mulmod(&val1, &val2, key->q, &val4) != MP_OKAY) { + goto out; + } + + /* v = (((g)^u1 (y)^u2) mod p) mod q */ + /* val2 = g^u1 mod p */ + if (mp_exptmod(key->g, &val3, key->p, &val2) != MP_OKAY) { + goto out; + } + /* val3 = y^u2 mod p */ + if (mp_exptmod(key->y, &val4, key->p, &val3) != MP_OKAY) { + goto out; + } + /* val4 = ((g)^u1 (y)^u2) mod p */ + if (mp_mulmod(&val2, &val3, key->p, &val4) != MP_OKAY) { + goto out; + } + /* val2 = v = (((g)^u1 (y)^u2) mod p) mod q */ + if (mp_mod(&val4, key->q, &val2) != MP_OKAY) { + goto out; + } + + /* check whether signatures verify */ + if (mp_cmp(&val2, &val1) == MP_EQ) { + /* good sig */ + ret = DROPBEAR_SUCCESS; + } + +out: + mp_clear_multi(&val1, &val2, &val3, &val4, NULL); + m_free(string); + + return ret; + +} +#endif /* DROPBEAR_SIGNKEY_VERIFY */ + +/* Sign the data presented with key, writing the signature contents + * to the buffer + * + * When DSS_PROTOK is #defined: + * The alternate k generation method is based on the method used in PuTTY. + * In particular to avoid being vulnerable to attacks using flaws in random + * generation of k, we use the following: + * + * proto_k = SHA512 ( SHA512(x) || SHA160(message) ) + * k = proto_k mod q + * + * Now we aren't relying on the random number generation to protect the private + * key x, which is a long term secret */ +void buf_put_dss_sign(buffer* buf, dss_key *key, const unsigned char* data, + unsigned int len) { + + unsigned char msghash[SHA1_HASH_SIZE]; + unsigned int writelen; + unsigned int i; +#ifdef DSS_PROTOK + unsigned char privkeyhash[SHA512_HASH_SIZE]; + unsigned char *privkeytmp; + unsigned char proto_k[SHA512_HASH_SIZE]; + 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; + hash_state hs; + + TRACE(("enter buf_put_dss_sign")); + assert(key != NULL); + + /* hash the data */ + sha1_init(&hs); + sha1_process(&hs, data, len); + sha1_done(&hs, msghash); + + m_mp_init_multi(&dss_k, &dss_temp1, &dss_temp2, &dss_r, &dss_s, + &dss_m, NULL); +#ifdef DSS_PROTOK + /* hash the privkey */ + privkeytmp = mptobytes(key->x, &i); + sha512_init(&hs); + sha512_process(&hs, "the quick brown fox jumped over the lazy dog", 44); + sha512_process(&hs, privkeytmp, i); + sha512_done(&hs, privkeyhash); + m_burn(privkeytmp, i); + m_free(privkeytmp); + + /* calculate proto_k */ + sha512_init(&hs); + sha512_process(&hs, privkeyhash, SHA512_HASH_SIZE); + sha512_process(&hs, msghash, SHA1_HASH_SIZE); + sha512_done(&hs, proto_k); + + /* generate k */ + m_mp_init(&dss_protok); + bytestomp(&dss_protok, proto_k, SHA512_HASH_SIZE); + mp_mod(&dss_protok, key->q, &dss_k); + mp_clear(&dss_protok); + m_burn(proto_k, SHA512_HASH_SIZE); +#else /* DSS_PROTOK not defined*/ + do { + genrandom(kbuf, SHA1_HASH_SIZE); + if (mp_read_unsigned_bin(&dss_k, kbuf, SHA1_HASH_SIZE) != MP_OKAY) { + dropbear_exit("dss error"); + } + } while (mp_cmp(&dss_k, key->q) == MP_GT || mp_cmp_d(&dss_k, 0) != MP_GT); + m_burn(kbuf, SHA1_HASH_SIZE); +#endif + + /* now generate the actual signature */ + bytestomp(&dss_m, msghash, SHA1_HASH_SIZE); + + /* g^k mod p */ + if (mp_exptmod(key->g, &dss_k, key->p, &dss_temp1) != MP_OKAY) { + dropbear_exit("dss error"); + } + /* r = (g^k mod p) mod q */ + if (mp_mod(&dss_temp1, key->q, &dss_r) != MP_OKAY) { + dropbear_exit("dss error"); + } + + /* x*r mod q */ + if (mp_mulmod(&dss_r, key->x, key->q, &dss_temp1) != MP_OKAY) { + dropbear_exit("dss error"); + } + /* (SHA1(M) + xr) mod q) */ + if (mp_addmod(&dss_m, &dss_temp1, key->q, &dss_temp2) != MP_OKAY) { + dropbear_exit("dss error"); + } + + /* (k^-1) mod q */ + if (mp_invmod(&dss_k, key->q, &dss_temp1) != MP_OKAY) { + dropbear_exit("dss error"); + } + + /* s = (k^-1(SHA1(M) + xr)) mod q */ + if (mp_mulmod(&dss_temp1, &dss_temp2, key->q, &dss_s) != MP_OKAY) { + dropbear_exit("dss error"); + } + + buf_putstring(buf, SSH_SIGNKEY_DSS, SSH_SIGNKEY_DSS_LEN); + buf_putint(buf, 2*SHA1_HASH_SIZE); + + writelen = mp_unsigned_bin_size(&dss_r); + assert(writelen <= SHA1_HASH_SIZE); + /* need to pad to 160 bits with leading zeros */ + for (i = 0; i < SHA1_HASH_SIZE - writelen; i++) { + buf_putbyte(buf, 0); + } + if (mp_to_unsigned_bin(&dss_r, buf_getwriteptr(buf, writelen)) + != MP_OKAY) { + dropbear_exit("dss error"); + } + mp_clear(&dss_r); + buf_incrwritepos(buf, writelen); + + writelen = mp_unsigned_bin_size(&dss_s); + assert(writelen <= SHA1_HASH_SIZE); + /* need to pad to 160 bits with leading zeros */ + for (i = 0; i < SHA1_HASH_SIZE - writelen; i++) { + buf_putbyte(buf, 0); + } + if (mp_to_unsigned_bin(&dss_s, buf_getwriteptr(buf, writelen)) + != MP_OKAY) { + dropbear_exit("dss error"); + } + mp_clear(&dss_s); + buf_incrwritepos(buf, writelen); + + mp_clear_multi(&dss_k, &dss_temp1, &dss_temp1, &dss_r, &dss_s, + &dss_m, NULL); + + /* create the signature to return */ + + TRACE(("leave buf_put_dss_sign")); +} + +#endif /* DROPBEAR_DSS */ diff --git a/dss.h b/dss.h new file mode 100644 index 0000000..0b69256 --- /dev/null +++ b/dss.h @@ -0,0 +1,61 @@ +/* + * 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 _DSS_H_ +#define _DSS_H_ + +#include "includes.h" +#include "buffer.h" + +#ifdef DROPBEAR_DSS + +#define DSS_SIGNATURE_SIZE 4+SSH_SIGNKEY_DSS_LEN+4+2*SHA1_HASH_SIZE + +struct DSS_key { + + mp_int* p; + mp_int* q; + mp_int* g; + mp_int* y; + mp_int* x; + +}; + +typedef struct DSS_key dss_key; + +void buf_put_dss_sign(buffer* buf, dss_key *key, const unsigned char* data, + unsigned int len); +#ifdef DROPBEAR_SIGNKEY_VERIFY +int buf_dss_verify(buffer* buf, dss_key *key, const unsigned char* data, + unsigned int len); +#endif +int buf_get_dss_pub_key(buffer* buf, dss_key *key); +int buf_get_dss_priv_key(buffer* buf, dss_key *key); +void buf_put_dss_pub_key(buffer* buf, dss_key *key); +void buf_put_dss_priv_key(buffer* buf, dss_key *key); +void dss_key_free(dss_key *key); + +#endif /* DROPBEAR_DSS */ + +#endif /* _DSS_H_ */ diff --git a/filelist.txt b/filelist.txt new file mode 100644 index 0000000..7d92e71 --- /dev/null +++ b/filelist.txt @@ -0,0 +1,113 @@ +A brief rundown on which files do what, and their corresponding sections +in the IETF drafts. The .c files usually have corresponding .h files. + +Transport layer draft-ietf-secsh-transport-16.txt +=============== + +session.c Contains the main select() loop, and handles setting + up/closing down ssh connections + +algo.c Framework for handling various ciphers/hashes/algos, + and choosing between the lists of client/server + preferred ones + +kex.c Key exchange routines, used at startup to negotiate + which algorithms to use, and also to obtain session + keys. This also runs when rekeying during the + connection. + +packet.c Handles the basic packet encryption/decryption, + and switching to the appropriate packet handlers. + Called from session.c's main select loop. + +service.c Handles service requests (userauth or connection) + + +Authentication draft-ietf-secsh-userauth-17.txt +============== + +auth.c General auth handling, including user checking etc, + passes different auth types to auth{passwd,pubkey} + +authpasswd.c Handles /etc/passwd or /etc/shadow auth + +authpubkey.c Handles ~/.ssh/authorized_keys auth + + +Connection draft-ietf-secsh-connect-17.txt +========== + +channel.c Channel handling routines - each shell/tcp conn/agent + etc is a channel. + +chansession.c Handles shell/exec requests + +sshpty.c From OpenSSH, allocates PTYs etc + +termcodes.c Mapping of POSIX terminal codes to SSH terminal codes + +loginrec.c From OpenSSH, handles utmp/wtmp logging + +x11fwd.c Handles X11 forwarding + +agentfwd.c Handles auth-agent forwarding requests + +localtcpfwd.c Handles -L style tcp forwarding requests, setting + up the listening port and also handling connections + to that port (and subsequent channels) + + +Program-related +=============== + +dbmulti.c Combination binary chooser main() function + +dbutil.c Various utility functions, incl logging, memory etc + +dropbearconvert.c Conversion from dropbear<->openssh keys, uses + keyimport.c to do most of the work + +dropbearkey.c Generates keys, calling gen{dss,rsa} + +keyimport.c Modified from PuTTY, converts between key types + +main.c dropbear's main(), handles listening, forking for + new connections, child-process limits + +runopts.c Parses commandline options + +options.h Compile-time feature selection + +config.h Features selected from configure + +debug.h Compile-time selection of debug features + +includes.h Included system headers etc + + +Generic Routines +================ + +signkey.c A generic handler for pubkeys, switches to dss or rsa + depending on the key type + +rsa.c RSA asymmetric crypto routines + +dss.c DSS asymmetric crypto routines + +gendss.c DSS key generation + +genrsa.c RSA key generation + +bignum.c Some bignum helper functions + +queue.c A queue, used to enqueue encrypted packets to send + +random.c PRNG, based on /dev/urandom or prngd + +atomicio.c From OpenSSH, does `blocking' IO on non-blocking fds + +buffer.c Buffer-usage routines, with size checking etc + + +vim:set ts=8: diff --git a/gendss.c b/gendss.c new file mode 100644 index 0000000..3e9db09 --- /dev/null +++ b/gendss.c @@ -0,0 +1,215 @@ +/* + * 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. */ + +#include "includes.h" +#include "dbutil.h" +#include "signkey.h" +#include "bignum.h" +#include "random.h" +#include "buffer.h" +#include "gendss.h" +#include "dss.h" + +#define PSIZE 128 /* 1024 bit*/ +#define QSIZE 20 /* 160 bit */ + +#ifdef DROPBEAR_DSS + +static void getq(dss_key *key); +static void getp(dss_key *key, unsigned int size); +static void getg(dss_key *key); +static void getx(dss_key *key); +static void gety(dss_key *key); + +dss_key * gen_dss_priv_key(unsigned int size) { + + dss_key *key; + + key = (dss_key*)m_malloc(sizeof(dss_key)); + + key->p = (mp_int*)m_malloc(sizeof(mp_int)); + key->q = (mp_int*)m_malloc(sizeof(mp_int)); + key->g = (mp_int*)m_malloc(sizeof(mp_int)); + key->y = (mp_int*)m_malloc(sizeof(mp_int)); + key->x = (mp_int*)m_malloc(sizeof(mp_int)); + m_mp_init_multi(key->p, key->q, key->g, key->y, key->x, NULL); + + seedrandom(); + + getq(key); + getp(key, size); + getg(key); + getx(key); + gety(key); + + return key; + +} + +static void getq(dss_key *key) { + + char buf[QSIZE]; + + /* 160 bit prime */ + genrandom(buf, QSIZE); + buf[0] |= 0x80; /* top bit high */ + buf[QSIZE-1] |= 0x01; /* bottom bit high */ + + if (mp_read_unsigned_bin(key->q, buf, QSIZE) != MP_OKAY) { + fprintf(stderr, "dss key generation failed\n"); + exit(1); + } + + /* 18 rounds are required according to HAC */ + if (mp_prime_next_prime(key->q, 18, 0) != MP_OKAY) { + fprintf(stderr, "dss key generation failed\n"); + exit(1); + } +} + +static void getp(dss_key *key, unsigned int size) { + + mp_int tempX, tempC, tempP, temp2q; + int result; + unsigned char *buf; + + m_mp_init_multi(&tempX, &tempC, &tempP, &temp2q, NULL); + + + /* 2*q */ + if (mp_mul_d(key->q, 2, &temp2q) != MP_OKAY) { + fprintf(stderr, "dss key generation failed\n"); + exit(1); + } + + buf = (unsigned char*)m_malloc(size); + + result = 0; + do { + + genrandom(buf, size); + buf[0] |= 0x80; /* set the top bit high */ + + /* X is a random mp_int */ + if (mp_read_unsigned_bin(&tempX, buf, size) != MP_OKAY) { + fprintf(stderr, "dss key generation failed\n"); + exit(1); + } + + /* C = X mod 2q */ + if (mp_mod(&tempX, &temp2q, &tempC) != MP_OKAY) { + fprintf(stderr, "dss key generation failed\n"); + exit(1); + } + + /* P = X - (C - 1) = X - C + 1*/ + if (mp_sub(&tempX, &tempC, &tempP) != MP_OKAY) { + fprintf(stderr, "dss key generation failed\n"); + exit(1); + } + + if (mp_add_d(&tempP, 1, key->p) != MP_OKAY) { + fprintf(stderr, "dss key generation failed\n"); + exit(1); + } + + /* now check for prime, 5 rounds is enough according to HAC */ + /* result == 1 => p is prime */ + if (mp_prime_is_prime(key->p, 5, &result) != MP_OKAY) { + fprintf(stderr, "dss key generation failed\n"); + exit(1); + } + } while (!result); + + mp_clear_multi(&tempX, &tempC, &tempP, &temp2q, NULL); + m_free(buf); +} + +static void getg(dss_key * key) { + + char printbuf[1000]; + mp_int div, h, val; + + m_mp_init_multi(&div, &h, &val, NULL); + + /* get div=(p-1)/q */ + if (mp_sub_d(key->p, 1, &val) != MP_OKAY) { + fprintf(stderr, "dss key generation failed\n"); + exit(1); + } + if (mp_div(&val, key->q, &div, NULL) != MP_OKAY) { + fprintf(stderr, "dss key generation failed\n"); + exit(1); + } + + /* initialise h=1 */ + mp_set(&h, 1); + do { + /* now keep going with g=h^div mod p, until g > 1 */ + if (mp_exptmod(&h, &div, key->p, key->g) != MP_OKAY) { + fprintf(stderr, "dss key generation failed\n"); + exit(1); + } + + if (mp_add_d(&h, 1, &h) != MP_OKAY) { + fprintf(stderr, "dss key generation failed\n"); + exit(1); + } + + } 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; + char buf[QSIZE]; + + m_mp_init(&val); + + do { + genrandom(buf, QSIZE); + + if (mp_read_unsigned_bin(&val, buf, QSIZE) != MP_OKAY) { + fprintf(stderr, "dss key generation failed\n"); + } + } while ((mp_cmp_d(&val, 1) == MP_GT) && (mp_cmp(&val, key->q) == MP_LT)); + + mp_copy(&val, key->x); + mp_clear(&val); + +} + +static void gety(dss_key *key) { + + if (mp_exptmod(key->g, key->x, key->p, key->y) != MP_OKAY) { + fprintf(stderr, "dss key generation failed\n"); + exit(1); + } +} + +#endif /* DROPBEAR_DSS */ diff --git a/gendss.h b/gendss.h new file mode 100644 index 0000000..246dae3 --- /dev/null +++ b/gendss.h @@ -0,0 +1,36 @@ +/* + * 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 _GENDSS_H_ +#define _GENDSS_H_ + +#include "dss.h" + +#ifdef DROPBEAR_DSS + +dss_key * gen_dss_priv_key(unsigned int size); + +#endif /* DROPBEAR_DSS */ + +#endif /* _GENDSS_H_ */ diff --git a/genrsa.c b/genrsa.c new file mode 100644 index 0000000..330ebf2 --- /dev/null +++ b/genrsa.c @@ -0,0 +1,138 @@ +/* + * 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. */ + +#include "includes.h" +#include "dbutil.h" +#include "bignum.h" +#include "random.h" +#include "rsa.h" +#include "genrsa.h" + +#define RSA_E 65537 + +#ifdef DROPBEAR_RSA + +static void getrsaprime(mp_int* prime, mp_int *primeminus, + mp_int* rsa_e, unsigned int size); + +/* mostly taken from libtomcrypt's rsa key generation routine */ +rsa_key * gen_rsa_priv_key(unsigned int size) { + + rsa_key * key; + mp_int pminus, qminus, lcm; + + key = (rsa_key*)m_malloc(sizeof(rsa_key)); + + key->e = (mp_int*)m_malloc(sizeof(mp_int)); + key->n = (mp_int*)m_malloc(sizeof(mp_int)); + key->d = (mp_int*)m_malloc(sizeof(mp_int)); + key->p = (mp_int*)m_malloc(sizeof(mp_int)); + key->q = (mp_int*)m_malloc(sizeof(mp_int)); + + m_mp_init_multi(key->e, key->n, key->d, key->p, key->q, + &pminus, &lcm, &qminus, NULL); + + seedrandom(); + + if (mp_set_int(key->e, RSA_E) != MP_OKAY) { + fprintf(stderr, "rsa generation failed\n"); + exit(1); + } + + /* PuTTY doesn't like it if the modulus isn't a multiple of 8 bits, + * so we just generate them until we get one which is OK */ + getrsaprime(key->p, &pminus, key->e, size/2); + do { + getrsaprime(key->q, &qminus, key->e, size/2); + + if (mp_mul(key->p, key->q, key->n) != MP_OKAY) { + fprintf(stderr, "rsa generation failed\n"); + exit(1); + } + } while (mp_count_bits(key->n) % 8 != 0); + + /* lcm(p-1, q-1) */ + if (mp_lcm(&pminus, &qminus, &lcm) != MP_OKAY) { + fprintf(stderr, "rsa generation failed\n"); + exit(1); + } + + /* de = 1 mod lcm(p-1,q-1) */ + /* therefore d = (e^-1) mod lcm(p-1,q-1) */ + if (mp_invmod(key->e, &lcm, key->d) != MP_OKAY) { + fprintf(stderr, "rsa generation failed\n"); + exit(1); + } + + mp_clear_multi(&pminus, &qminus, &lcm, NULL); + + return key; +} + +/* return a prime suitable for p or q */ +static void getrsaprime(mp_int* prime, mp_int *primeminus, + mp_int* rsa_e, unsigned int size) { + + unsigned char *buf; + mp_int temp_gcd; + + buf = (unsigned char*)m_malloc(size+1); + + m_mp_init(&temp_gcd); + do { + /* generate a random odd number with MSB set, then find the + the next prime above it */ + genrandom(buf, size+1); + buf[0] |= 0x80; /* MSB set */ + + if (mp_read_unsigned_bin(prime, buf, size+1) != MP_OKAY) { + fprintf(stderr, "rsa generation failed\n"); + exit(1); + } + + /* find the next integer which is prime, 8 round of miller-rabin */ + if (mp_prime_next_prime(prime, 8, 0) != MP_OKAY) { + fprintf(stderr, "rsa generation failed\n"); + exit(1); + } + + /* subtract one to get p-1 */ + if (mp_sub_d(prime, 1, primeminus) != MP_OKAY) { + fprintf(stderr, "rsa generation failed\n"); + exit(1); + } + /* check relative primality to e */ + if (mp_gcd(primeminus, rsa_e, &temp_gcd) != MP_OKAY) { + fprintf(stderr, "rsa generation failed\n"); + exit(1); + } + } while (mp_cmp_d(&temp_gcd, 1) != MP_EQ); /* while gcd(p-1, e) != 1 */ + + /* now we have a good value for result */ + mp_clear(&temp_gcd); + m_burn(buf, size+1); + m_free(buf); +} + +#endif /* DROPBEAR_RSA */ diff --git a/genrsa.h b/genrsa.h new file mode 100644 index 0000000..ef9f579 --- /dev/null +++ b/genrsa.h @@ -0,0 +1,36 @@ +/* + * 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 _GENRSA_H_ +#define _GENRSA_H_ + +#include "rsa.h" + +#ifdef DROPBEAR_RSA + +rsa_key * gen_rsa_priv_key(unsigned int size); + +#endif /* DROPBEAR_RSA */ + +#endif /* _GENRSA_H_ */ diff --git a/includes.h b/includes.h new file mode 100644 index 0000000..3fdd729 --- /dev/null +++ b/includes.h @@ -0,0 +1,134 @@ +/* + * 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 _INCLUDES_H_ +#define _INCLUDES_H_ + + +#include "config.h" +#include "options.h" +#include "debug.h" + +#include +#include +#include /* required for BSD4_4 define */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef HAVE_UTMP_H +#include +#endif + +#ifdef HAVE_UTMPX_H +#include +#endif + +#ifdef HAVE_PATHS_H +#include +#endif + +#ifdef HAVE_LASTLOG_H +#include +#endif + +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#ifdef HAVE_NETINET_TCP_H +#include +#endif + +#ifdef HAVE_INTTYPES_H +#include +#endif + +#ifdef HAVE_LIBUTIL_H +#include +#endif + +#ifdef HAVE_CRYPT_H +#include +#endif + +#ifndef DISABLE_ZLIB +#include +#endif + +#ifdef HAVE_UTIL_H +#include +#endif + +#ifdef HAVE_SHADOW_H +#include +#endif + +#ifdef HAVE_LIBGEN_H +#include +#endif + +#ifdef HAVE_SYS_DIRENT_H +#include +#endif + +#include "libtomcrypt/mycrypt_custom.h" +#include "libtommath/tommath.h" + +#include "compat.h" + +#ifndef HAVE_UINT16_T +#ifndef HAVE_U_INT16_T +typedef unsigned short u_int16_t; +#endif /* HAVE_U_INT16_T */ +typedef u_int16_t uint16_t; +#endif /* HAVE_UINT16_T */ + +#ifndef LOG_AUTHPRIV +#define LOG_AUTHPRIV LOG_AUTH +#endif + +#endif /* _INCLUDES_H_ */ diff --git a/install-sh b/install-sh new file mode 100644 index 0000000..e9de238 --- /dev/null +++ b/install-sh @@ -0,0 +1,251 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + chmodcmd="" + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/kex.h b/kex.h new file mode 100644 index 0000000..61b022e --- /dev/null +++ b/kex.h @@ -0,0 +1,62 @@ +/* + * 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 _KEX_H_ +#define _KEX_H_ + +#include "includes.h" + +void send_msg_kexinit(); +void recv_msg_kexinit(); +void send_dh_kex(); +void recv_msg_kexdh_init(); +void send_msg_newkeys(); +void recv_msg_newkeys(); +void kexinitialise(); + +void svr_read_kex(); +void cli_read_kex(); + +extern const unsigned char dh_p_val[]; +#define DH_P_LEN 128 /* The length of the dh_p_val array */ + +extern const int DH_G_VAL; /* == 2 */ + +struct KEXState { + + unsigned sentkexinit : 1; /*set when we've sent/recv kexinit packet */ + unsigned recvkexinit : 1; + unsigned firstfollows : 1; /* true when first_kex_packet_follows is set */ + unsigned sentnewkeys : 1; /* set once we've send/recv'ed MSG_NEWKEYS*/ + unsigned recvnewkeys : 1; + + long lastkextime; /* time of the last kex */ + unsigned int datatrans; /* data transmitted since last kex */ + unsigned int datarecv; /* data received since last kex */ + +}; + +#define MAX_KEXHASHBUF 2000 + +#endif /* _KEX_H_ */ diff --git a/keyimport.c b/keyimport.c new file mode 100644 index 0000000..34fac2f --- /dev/null +++ b/keyimport.c @@ -0,0 +1,1748 @@ +/* + * Based on PuTTY's import.c for importing/exporting OpenSSH and SSH.com + * keyfiles. + * + * The horribleness of the code is probably mine (matt). + * + * Modifications copyright 2003 Matt Johnston + * + * PuTTY is copyright 1997-2003 Simon Tatham. + * + * Portions copyright Robert de Bath, Joris van Rantwijk, Delian + * Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, + * Justin Bradford, and CORE SDI S.A. + * + * 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 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 "keyimport.h" +#include "bignum.h" +#include "buffer.h" +#include "dbutil.h" + +#define PUT_32BIT(cp, value) do { \ + (cp)[3] = (unsigned char)(value); \ + (cp)[2] = (unsigned char)((value) >> 8); \ + (cp)[1] = (unsigned char)((value) >> 16); \ + (cp)[0] = (unsigned char)((value) >> 24); } while (0) + +#define GET_32BIT(cp) \ + (((unsigned long)(unsigned char)(cp)[0] << 24) | \ + ((unsigned long)(unsigned char)(cp)[1] << 16) | \ + ((unsigned long)(unsigned char)(cp)[2] << 8) | \ + ((unsigned long)(unsigned char)(cp)[3])) + +static int openssh_encrypted(const char *filename); +static sign_key *openssh_read(const char *filename, char *passphrase); +static int openssh_write(const char *filename, sign_key *key, + char *passphrase); + +static int dropbear_write(const char*filename, sign_key * key); +static sign_key *dropbear_read(const char* filename); + +#if 0 +static int sshcom_encrypted(const char *filename, char **comment); +static struct ssh2_userkey *sshcom_read(const char *filename, char *passphrase); +static int sshcom_write(const char *filename, struct ssh2_userkey *key, + char *passphrase); +#endif + +int import_encrypted(const char* filename, int filetype) { + + if (filetype == KEYFILE_OPENSSH) { + return openssh_encrypted(filename); +#if 0 + } else if (filetype == KEYFILE_SSHCOM) { + return sshcom_encrypted(filename, NULL); +#endif + } + return 0; +} + +sign_key *import_read(const char *filename, char *passphrase, int filetype) { + + if (filetype == KEYFILE_OPENSSH) { + return openssh_read(filename, passphrase); + } else if (filetype == KEYFILE_DROPBEAR) { + return dropbear_read(filename); +#if 0 + } else if (filetype == KEYFILE_SSHCOM) { + return sshcom_read(filename, passphrase); +#endif + } + return NULL; +} + +int import_write(const char *filename, sign_key *key, char *passphrase, + int filetype) { + + if (filetype == KEYFILE_OPENSSH) { + return openssh_write(filename, key, passphrase); + } else if (filetype == KEYFILE_DROPBEAR) { + return dropbear_write(filename, key); +#if 0 + } else if (filetype == KEYFILE_SSHCOM) { + return sshcom_write(filename, key, passphrase); +#endif + } + return 0; +} + +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) { + 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(); + + type = DROPBEAR_SIGNKEY_ANY; + if (buf_get_priv_key(buf, ret, &type) == DROPBEAR_FAILURE){ + goto error; + } + buf_free(buf); + + return ret; + +error: + if (buf) { + buf_free(buf); + } + if (ret) { + sign_key_free(ret); + } + return NULL; +} + +/* returns 0 on fail, 1 on success */ +static int dropbear_write(const char*filename, sign_key * key) { + + int keytype = -1; + buffer * buf; + FILE*fp; + int len; + int ret; + +#ifdef DROPBEAR_RSA + if (key->rsakey != NULL) { + keytype = DROPBEAR_SIGNKEY_RSA; + } +#endif +#ifdef DROPBEAR_DSS + if (key->dsskey != NULL) { + keytype = DROPBEAR_SIGNKEY_DSS; + } +#endif + + buf = buf_new(2000); + buf_put_priv_key(buf, key, keytype); + + if (strlen(filename) == 1 && filename[0] == '-') { + fp = stdout; + } else { + fp = fopen(filename, "w"); + } + if (!fp) { + ret = 0; + goto out; + } + + buf_setpos(buf, 0); + do { + len = fwrite(buf_getptr(buf, buf->len - buf->pos), + 1, buf->len - buf->pos, fp); + buf_incrpos(buf, len); + } while (len > 0 && buf->len != buf->pos); + + if (buf->pos != buf->len) { + ret = 0; + } else { + ret = 1; + } +out: + buf_free(buf); + return ret; +} + + +/* ---------------------------------------------------------------------- + * Helper routines. (The base64 ones are defined in sshpubk.c.) + */ + +#define isbase64(c) ( ((c) >= 'A' && (c) <= 'Z') || \ + ((c) >= 'a' && (c) <= 'z') || \ + ((c) >= '0' && (c) <= '9') || \ + (c) == '+' || (c) == '/' || (c) == '=' \ + ) + +/* cpl has to be less than 100 */ +static void base64_encode_fp(FILE * fp, unsigned char *data, + int datalen, int cpl) +{ + char out[100]; + int n; + unsigned long outlen; + int rawcpl; + rawcpl = cpl * 3 / 4; + assert((unsigned int)cpl < sizeof(out)); + + while (datalen > 0) { + n = (datalen < rawcpl ? datalen : rawcpl); + outlen = sizeof(out); + base64_encode(data, n, out, &outlen); + data += n; + datalen -= n; + fwrite(out, 1, outlen, fp); + fputc('\n', fp); + } +} +/* + * Read an ASN.1/BER identifier and length pair. + * + * Flags are a combination of the #defines listed below. + * + * Returns -1 if unsuccessful; otherwise returns the number of + * bytes used out of the source data. + */ + +/* ASN.1 tag classes. */ +#define ASN1_CLASS_UNIVERSAL (0 << 6) +#define ASN1_CLASS_APPLICATION (1 << 6) +#define ASN1_CLASS_CONTEXT_SPECIFIC (2 << 6) +#define ASN1_CLASS_PRIVATE (3 << 6) +#define ASN1_CLASS_MASK (3 << 6) + +/* Primitive versus constructed bit. */ +#define ASN1_CONSTRUCTED (1 << 5) + +static int ber_read_id_len(void *source, int sourcelen, + int *id, int *length, int *flags) +{ + unsigned char *p = (unsigned char *) source; + + if (sourcelen == 0) + return -1; + + *flags = (*p & 0xE0); + if ((*p & 0x1F) == 0x1F) { + *id = 0; + while (*p & 0x80) { + *id = (*id << 7) | (*p & 0x7F); + p++, sourcelen--; + if (sourcelen == 0) + return -1; + } + *id = (*id << 7) | (*p & 0x7F); + p++, sourcelen--; + } else { + *id = *p & 0x1F; + p++, sourcelen--; + } + + if (sourcelen == 0) + return -1; + + if (*p & 0x80) { + int n = *p & 0x7F; + p++, sourcelen--; + if (sourcelen < n) + return -1; + *length = 0; + while (n--) + *length = (*length << 8) | (*p++); + sourcelen -= n; + } else { + *length = *p; + p++, sourcelen--; + } + + return p - (unsigned char *) source; +} + +/* + * Write an ASN.1/BER identifier and length pair. Returns the + * number of bytes consumed. Assumes dest contains enough space. + * Will avoid writing anything if dest is NULL, but still return + * amount of space required. + */ +static int ber_write_id_len(void *dest, int id, int length, int flags) +{ + unsigned char *d = (unsigned char *)dest; + int len = 0; + + if (id <= 30) { + /* + * Identifier is one byte. + */ + len++; + if (d) *d++ = id | flags; + } else { + int n; + /* + * Identifier is multiple bytes: the first byte is 11111 + * plus the flags, and subsequent bytes encode the value of + * the identifier, 7 bits at a time, with the top bit of + * each byte 1 except the last one which is 0. + */ + len++; + if (d) *d++ = 0x1F | flags; + for (n = 1; (id >> (7*n)) > 0; n++) + continue; /* count the bytes */ + while (n--) { + len++; + if (d) *d++ = (n ? 0x80 : 0) | ((id >> (7*n)) & 0x7F); + } + } + + if (length < 128) { + /* + * Length is one byte. + */ + len++; + if (d) *d++ = length; + } else { + int n; + /* + * Length is multiple bytes. The first is 0x80 plus the + * number of subsequent bytes, and the subsequent bytes + * encode the actual length. + */ + for (n = 1; (length >> (8*n)) > 0; n++) + continue; /* count the bytes */ + len++; + if (d) *d++ = 0x80 | n; + while (n--) { + len++; + if (d) *d++ = (length >> (8*n)) & 0xFF; + } + } + + return len; +} + + +/* Simple structure to point to an mp-int within a blob. */ +struct mpint_pos { void *start; int bytes; }; + +/* ---------------------------------------------------------------------- + * Code to read and write OpenSSH private keys. + */ + +enum { OSSH_DSA, OSSH_RSA }; +struct openssh_key { + int type; + int encrypted; + char iv[32]; + unsigned char *keyblob; + unsigned int keyblob_len, keyblob_size; +}; + +static struct openssh_key *load_openssh_key(const char *filename) +{ + struct openssh_key *ret; + FILE *fp; + char buffer[256]; + char *errmsg = NULL, *p = NULL; + int headers_done; + unsigned long len, outlen; + + ret = (struct openssh_key*)m_malloc(sizeof(struct openssh_key)); + ret->keyblob = NULL; + ret->keyblob_len = ret->keyblob_size = 0; + ret->encrypted = 0; + memset(ret->iv, 0, sizeof(ret->iv)); + + if (strlen(filename) == 1 && filename[0] == '-') { + fp = stdin; + } else { + fp = fopen(filename, "r"); + } + if (!fp) { + errmsg = "Unable to open key file"; + goto error; + } + if (!fgets(buffer, sizeof(buffer), fp) || + 0 != strncmp(buffer, "-----BEGIN ", 11) || + 0 != strcmp(buffer+strlen(buffer)-17, "PRIVATE KEY-----\n")) { + errmsg = "File does not begin with OpenSSH key header"; + goto error; + } + if (!strcmp(buffer, "-----BEGIN RSA PRIVATE KEY-----\n")) + ret->type = OSSH_RSA; + else if (!strcmp(buffer, "-----BEGIN DSA PRIVATE KEY-----\n")) + ret->type = OSSH_DSA; + else { + errmsg = "Unrecognised key type"; + goto error; + } + + headers_done = 0; + while (1) { + if (!fgets(buffer, sizeof(buffer), fp)) { + errmsg = "Unexpected end of file"; + goto error; + } + if (0 == strncmp(buffer, "-----END ", 9) && + 0 == strcmp(buffer+strlen(buffer)-17, "PRIVATE KEY-----\n")) + break; /* done */ + if ((p = strchr(buffer, ':')) != NULL) { + if (headers_done) { + errmsg = "Header found in body of key data"; + goto error; + } + *p++ = '\0'; + while (*p && isspace((unsigned char)*p)) p++; + if (!strcmp(buffer, "Proc-Type")) { + if (p[0] != '4' || p[1] != ',') { + errmsg = "Proc-Type is not 4 (only 4 is supported)"; + goto error; + } + p += 2; + if (!strcmp(p, "ENCRYPTED\n")) + ret->encrypted = 1; + } else if (!strcmp(buffer, "DEK-Info")) { + int i, j; + + if (strncmp(p, "DES-EDE3-CBC,", 13)) { + errmsg = "Ciphers other than DES-EDE3-CBC not supported"; + goto error; + } + p += 13; + for (i = 0; i < 8; i++) { + if (1 != sscanf(p, "%2x", &j)) + break; + ret->iv[i] = j; + p += 2; + } + if (i < 8) { + errmsg = "Expected 16-digit iv in DEK-Info"; + goto error; + } + } + } else { + headers_done = 1; + len = strlen(buffer); + outlen = len*4/3; + if (ret->keyblob_len + outlen > ret->keyblob_size) { + ret->keyblob_size = ret->keyblob_len + outlen + 256; + ret->keyblob = (unsigned char*)m_realloc(ret->keyblob, + ret->keyblob_size); + } + outlen = ret->keyblob_size - ret->keyblob_len; + if (base64_decode(buffer, len, + ret->keyblob + ret->keyblob_len, &outlen) != CRYPT_OK){ + errmsg = "Error decoding base64"; + goto error; + } + ret->keyblob_len += outlen; + } + } + + if (ret->keyblob_len == 0 || !ret->keyblob) { + errmsg = "Key body not present"; + goto error; + } + + if (ret->encrypted && ret->keyblob_len % 8 != 0) { + errmsg = "Encrypted key blob is not a multiple of cipher block size"; + goto error; + } + + memset(buffer, 0, sizeof(buffer)); + return ret; + + error: + memset(buffer, 0, sizeof(buffer)); + if (ret) { + if (ret->keyblob) { + memset(ret->keyblob, 0, ret->keyblob_size); + m_free(ret->keyblob); + } + memset(&ret, 0, sizeof(ret)); + m_free(ret); + } + if (errmsg) { + fprintf(stderr, "Error: %s\n", errmsg); + } + return NULL; +} + +static int openssh_encrypted(const char *filename) +{ + struct openssh_key *key = load_openssh_key(filename); + int ret; + + if (!key) + return 0; + ret = key->encrypted; + memset(key->keyblob, 0, key->keyblob_size); + m_free(key->keyblob); + memset(&key, 0, sizeof(key)); + m_free(key); + return ret; +} + +static sign_key *openssh_read(const char *filename, char *passphrase) +{ + struct openssh_key *key; + unsigned char *p; + int ret, id, len, flags; + int i, num_integers = 0; + sign_key *retval = NULL; + char *errmsg; + char *modptr = NULL; + int modlen = -9999; + int type; + + sign_key *retkey; + buffer * blobbuf = NULL; + + key = load_openssh_key(filename); + + if (!key) + return NULL; + + if (key->encrypted) { + errmsg = "encrypted keys not supported currently"; + goto error; +#if 0 + /* matt TODO */ + /* + * Derive encryption key from passphrase and iv/salt: + * + * - let block A equal MD5(passphrase || iv) + * - let block B equal MD5(A || passphrase || iv) + * - block C would be MD5(B || passphrase || iv) and so on + * - encryption key is the first N bytes of A || B + */ + struct MD5Context md5c; + unsigned char keybuf[32]; + + MD5Init(&md5c); + MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); + MD5Update(&md5c, (unsigned char *)key->iv, 8); + MD5Final(keybuf, &md5c); + + MD5Init(&md5c); + MD5Update(&md5c, keybuf, 16); + MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); + MD5Update(&md5c, (unsigned char *)key->iv, 8); + MD5Final(keybuf+16, &md5c); + + /* + * Now decrypt the key blob. + */ + des3_decrypt_pubkey_ossh(keybuf, (unsigned char *)key->iv, + key->keyblob, key->keyblob_len); + + memset(&md5c, 0, sizeof(md5c)); + memset(keybuf, 0, sizeof(keybuf)); +#endif + } + + /* + * Now we have a decrypted key blob, which contains an ASN.1 + * encoded private key. We must now untangle the ASN.1. + * + * We expect the whole key blob to be formatted as a SEQUENCE + * (0x30 followed by a length code indicating that the rest of + * the blob is part of the sequence). Within that SEQUENCE we + * expect to see a bunch of INTEGERs. What those integers mean + * depends on the key type: + * + * - For RSA, we expect the integers to be 0, n, e, d, p, q, + * dmp1, dmq1, iqmp in that order. (The last three are d mod + * (p-1), d mod (q-1), inverse of q mod p respectively.) + * + * - For DSA, we expect them to be 0, p, q, g, y, x in that + * order. + */ + + p = key->keyblob; + + /* Expect the SEQUENCE header. Take its absence as a failure to decrypt. */ + ret = ber_read_id_len(p, key->keyblob_len, &id, &len, &flags); + p += ret; + if (ret < 0 || id != 16) { + errmsg = "ASN.1 decoding failure - wrong password?"; + goto error; + } + + /* Expect a load of INTEGERs. */ + if (key->type == OSSH_RSA) + num_integers = 9; + else if (key->type == OSSH_DSA) + num_integers = 6; + + /* + * Space to create key blob in. + */ + blobbuf = buf_new(3000); + + if (key->type == OSSH_DSA) { + buf_putstring(blobbuf, "ssh-dss", 7); + } else if (key->type == OSSH_RSA) { + buf_putstring(blobbuf, "ssh-rsa", 7); + } + + for (i = 0; i < num_integers; i++) { + ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p, + &id, &len, &flags); + p += ret; + if (ret < 0 || id != 2 || + key->keyblob+key->keyblob_len-p < len) { + errmsg = "ASN.1 decoding failure"; + goto error; + } + + if (i == 0) { + /* + * The first integer should be zero always (I think + * this is some sort of version indication). + */ + if (len != 1 || p[0] != 0) { + errmsg = "Version number mismatch"; + goto error; + } + } else if (key->type == OSSH_RSA) { + /* + * OpenSSH key order is n, e, d, p, q, dmp1, dmq1, iqmp + * but we want e, n, d, p, q + */ + if (i == 1) { + /* Save the details for after we deal with number 2. */ + modptr = (char *)p; + modlen = len; + } else if (i >= 2 && i <= 5) { + buf_putstring(blobbuf, p, len); + if (i == 2) { + buf_putstring(blobbuf, modptr, modlen); + } + } + } else if (key->type == OSSH_DSA) { + /* + * OpenSSH key order is p, q, g, y, x, + * we want the same. + */ + buf_putstring(blobbuf, p, len); + } + + /* Skip past the number. */ + p += len; + } + + /* + * Now put together the actual key. Simplest way to do this is + * to assemble our own key blobs and feed them to the createkey + * functions; this is a bit faffy but it does mean we get all + * the sanity checks for free. + */ + retkey = new_sign_key(); + buf_setpos(blobbuf, 0); + type = DROPBEAR_SIGNKEY_ANY; + if (buf_get_priv_key(blobbuf, retkey, &type) + != DROPBEAR_SUCCESS) { + errmsg = "unable to create key structure"; + sign_key_free(retkey); + retkey = NULL; + goto error; + } + + errmsg = NULL; /* no error */ + retval = retkey; + + error: + if (blobbuf) { + buf_burn(blobbuf); + buf_free(blobbuf); + } + m_burn(key->keyblob, key->keyblob_size); + m_free(key->keyblob); + m_burn(key, sizeof(key)); + m_free(key); + if (errmsg) { + fprintf(stderr, "Error: %s\n", errmsg); + } + return retval; +} + +static int openssh_write(const char *filename, sign_key *key, + char *passphrase) +{ + buffer * keyblob = NULL; + buffer * extrablob = NULL; /* used for calculated values to write */ + unsigned char *outblob = NULL; + int outlen = -9999; + struct mpint_pos numbers[9]; + int nnumbers = -1, pos, len, seqlen, i; + char *header = NULL, *footer = NULL; + char zero[1]; + unsigned char iv[8]; + int ret = 0; + FILE *fp; + int keytype = -1; + +#ifdef DROPBEAR_RSA + mp_int dmp1, dmq1, iqmp, tmpval; /* for rsa */ + + if (key->rsakey != NULL) { + keytype = DROPBEAR_SIGNKEY_RSA; + } +#endif +#ifdef DROPBEAR_DSS + if (key->dsskey != NULL) { + keytype = DROPBEAR_SIGNKEY_DSS; + } +#endif + + assert(keytype != -1); + + /* + * Fetch the key blobs. + */ + keyblob = buf_new(3000); + buf_put_priv_key(keyblob, key, keytype); + + buf_setpos(keyblob, 0); + /* skip the "ssh-rsa" or "ssh-dss" header */ + buf_incrpos(keyblob, buf_getint(keyblob)); + + /* + * Find the sequence of integers to be encoded into the OpenSSH + * key blob, and also decide on the header line. + */ + numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0'; + +#ifdef DROPBEAR_RSA + if (keytype == DROPBEAR_SIGNKEY_RSA) { + + if (key->rsakey->p == NULL || key->rsakey->q == NULL) { + fprintf(stderr, "Pre-0.33 Dropbear keys cannot be converted to OpenSSH keys.\n"); + goto error; + } + + /* e */ + numbers[2].bytes = buf_getint(keyblob); + numbers[2].start = buf_getptr(keyblob, numbers[2].bytes); + buf_incrpos(keyblob, numbers[2].bytes); + + /* n */ + numbers[1].bytes = buf_getint(keyblob); + numbers[1].start = buf_getptr(keyblob, numbers[1].bytes); + buf_incrpos(keyblob, numbers[1].bytes); + + /* d */ + numbers[3].bytes = buf_getint(keyblob); + numbers[3].start = buf_getptr(keyblob, numbers[3].bytes); + buf_incrpos(keyblob, numbers[3].bytes); + + /* p */ + numbers[4].bytes = buf_getint(keyblob); + numbers[4].start = buf_getptr(keyblob, numbers[4].bytes); + buf_incrpos(keyblob, numbers[4].bytes); + + /* q */ + numbers[5].bytes = buf_getint(keyblob); + numbers[5].start = buf_getptr(keyblob, numbers[5].bytes); + buf_incrpos(keyblob, numbers[5].bytes); + + /* now calculate some extra parameters: */ + m_mp_init(&tmpval); + m_mp_init(&dmp1); + m_mp_init(&dmq1); + m_mp_init(&iqmp); + + /* dmp1 = d mod (p-1) */ + if (mp_sub_d(key->rsakey->p, 1, &tmpval) != MP_OKAY) { + fprintf(stderr, "Bignum error for p-1\n"); + goto error; + } + if (mp_mod(key->rsakey->d, &tmpval, &dmp1) != MP_OKAY) { + fprintf(stderr, "Bignum error for dmp1\n"); + goto error; + } + + /* dmq1 = d mod (q-1) */ + if (mp_sub_d(key->rsakey->q, 1, &tmpval) != MP_OKAY) { + fprintf(stderr, "Bignum error for q-1\n"); + goto error; + } + if (mp_mod(key->rsakey->d, &tmpval, &dmq1) != MP_OKAY) { + fprintf(stderr, "Bignum error for dmq1\n"); + goto error; + } + + /* iqmp = (q^-1) mod p */ + if (mp_invmod(key->rsakey->q, key->rsakey->p, &iqmp) != MP_OKAY) { + fprintf(stderr, "Bignum error for iqmp\n"); + goto error; + } + + extrablob = buf_new(2000); + buf_putmpint(extrablob, &dmp1); + buf_putmpint(extrablob, &dmq1); + buf_putmpint(extrablob, &iqmp); + buf_setpos(extrablob, 0); + mp_clear(&dmp1); + mp_clear(&dmq1); + mp_clear(&iqmp); + mp_clear(&tmpval); + + /* dmp1 */ + numbers[6].bytes = buf_getint(extrablob); + numbers[6].start = buf_getptr(extrablob, numbers[6].bytes); + buf_incrpos(extrablob, numbers[6].bytes); + + /* dmq1 */ + numbers[7].bytes = buf_getint(extrablob); + numbers[7].start = buf_getptr(extrablob, numbers[7].bytes); + buf_incrpos(extrablob, numbers[7].bytes); + + /* iqmp */ + numbers[8].bytes = buf_getint(extrablob); + numbers[8].start = buf_getptr(extrablob, numbers[8].bytes); + buf_incrpos(extrablob, numbers[8].bytes); + + nnumbers = 9; + header = "-----BEGIN RSA PRIVATE KEY-----\n"; + footer = "-----END RSA PRIVATE KEY-----\n"; + } +#endif /* DROPBEAR_RSA */ + +#ifdef DROPBEAR_DSS + if (keytype == DROPBEAR_SIGNKEY_DSS) { + + /* p */ + numbers[1].bytes = buf_getint(keyblob); + numbers[1].start = buf_getptr(keyblob, numbers[1].bytes); + buf_incrpos(keyblob, numbers[1].bytes); + + /* q */ + numbers[2].bytes = buf_getint(keyblob); + numbers[2].start = buf_getptr(keyblob, numbers[2].bytes); + buf_incrpos(keyblob, numbers[2].bytes); + + /* g */ + numbers[3].bytes = buf_getint(keyblob); + numbers[3].start = buf_getptr(keyblob, numbers[3].bytes); + buf_incrpos(keyblob, numbers[3].bytes); + + /* y */ + numbers[4].bytes = buf_getint(keyblob); + numbers[4].start = buf_getptr(keyblob, numbers[4].bytes); + buf_incrpos(keyblob, numbers[4].bytes); + + /* x */ + numbers[5].bytes = buf_getint(keyblob); + numbers[5].start = buf_getptr(keyblob, numbers[5].bytes); + buf_incrpos(keyblob, numbers[5].bytes); + + nnumbers = 6; + header = "-----BEGIN DSA PRIVATE KEY-----\n"; + footer = "-----END DSA PRIVATE KEY-----\n"; + } +#endif /* DROPBEAR_DSS */ + + /* + * Now count up the total size of the ASN.1 encoded integers, + * so as to determine the length of the containing SEQUENCE. + */ + len = 0; + for (i = 0; i < nnumbers; i++) { + len += ber_write_id_len(NULL, 2, numbers[i].bytes, 0); + len += numbers[i].bytes; + } + seqlen = len; + /* Now add on the SEQUENCE header. */ + len += ber_write_id_len(NULL, 16, seqlen, ASN1_CONSTRUCTED); + /* Round up to the cipher block size, ensuring we have at least one + * byte of padding (see below). */ + outlen = len; + if (passphrase) + outlen = (outlen+8) &~ 7; + + /* + * Now we know how big outblob needs to be. Allocate it. + */ + outblob = (unsigned char*)m_malloc(outlen); + + /* + * And write the data into it. + */ + pos = 0; + pos += ber_write_id_len(outblob+pos, 16, seqlen, ASN1_CONSTRUCTED); + for (i = 0; i < nnumbers; i++) { + pos += ber_write_id_len(outblob+pos, 2, numbers[i].bytes, 0); + memcpy(outblob+pos, numbers[i].start, numbers[i].bytes); + pos += numbers[i].bytes; + } + + /* + * Padding on OpenSSH keys is deterministic. The number of + * padding bytes is always more than zero, and always at most + * the cipher block length. The value of each padding byte is + * equal to the number of padding bytes. So a plaintext that's + * an exact multiple of the block size will be padded with 08 + * 08 08 08 08 08 08 08 (assuming a 64-bit block cipher); a + * plaintext one byte less than a multiple of the block size + * will be padded with just 01. + * + * This enables the OpenSSL key decryption function to strip + * off the padding algorithmically and return the unpadded + * plaintext to the next layer: it looks at the final byte, and + * then expects to find that many bytes at the end of the data + * with the same value. Those are all removed and the rest is + * returned. + */ + assert(pos == len); + while (pos < outlen) { + outblob[pos++] = outlen - len; + } + + /* + * Encrypt the key. + */ + if (passphrase) { + fprintf(stderr, "Encrypted keys aren't supported currently\n"); + goto error; +#if 0 + /* + * Invent an iv. Then derive encryption key from passphrase + * and iv/salt: + * + * - let block A equal MD5(passphrase || iv) + * - let block B equal MD5(A || passphrase || iv) + * - block C would be MD5(B || passphrase || iv) and so on + * - encryption key is the first N bytes of A || B + */ + struct MD5Context md5c; + unsigned char keybuf[32]; + + for (i = 0; i < 8; i++) iv[i] = random_byte(); + + MD5Init(&md5c); + MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); + MD5Update(&md5c, iv, 8); + MD5Final(keybuf, &md5c); + + MD5Init(&md5c); + MD5Update(&md5c, keybuf, 16); + MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); + MD5Update(&md5c, iv, 8); + MD5Final(keybuf+16, &md5c); + + /* + * Now encrypt the key blob. + */ + des3_encrypt_pubkey_ossh(keybuf, iv, outblob, outlen); + + memset(&md5c, 0, sizeof(md5c)); + memset(keybuf, 0, sizeof(keybuf)); +#endif + } + + /* + * And save it. We'll use Unix line endings just in case it's + * subsequently transferred in binary mode. + */ + if (strlen(filename) == 1 && filename[0] == '-') { + fp = stdout; + } else { + fp = fopen(filename, "wb"); /* ensure Unix line endings */ + } + if (!fp) { + fprintf(stderr, "Failed opening output file\n"); + goto error; + } + fputs(header, fp); + if (passphrase) { + fprintf(fp, "Proc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,"); + for (i = 0; i < 8; i++) + fprintf(fp, "%02X", iv[i]); + fprintf(fp, "\n\n"); + } + base64_encode_fp(fp, outblob, outlen, 64); + fputs(footer, fp); + fclose(fp); + ret = 1; + + error: + if (outblob) { + memset(outblob, 0, outlen); + m_free(outblob); + } + if (keyblob) { + buf_burn(keyblob); + buf_free(keyblob); + } + if (extrablob) { + buf_burn(extrablob); + buf_free(extrablob); + } + return ret; +} + +#if 0 +/* XXX TODO ssh.com stuff isn't going yet */ + +/* ---------------------------------------------------------------------- + * Code to read ssh.com private keys. + */ + +/* + * The format of the base64 blob is largely ssh2-packet-formatted, + * except that mpints are a bit different: they're more like the + * old ssh1 mpint. You have a 32-bit bit count N, followed by + * (N+7)/8 bytes of data. + * + * So. The blob contains: + * + * - uint32 0x3f6ff9eb (magic number) + * - uint32 size (total blob size) + * - string key-type (see below) + * - string cipher-type (tells you if key is encrypted) + * - string encrypted-blob + * + * (The first size field includes the size field itself and the + * magic number before it. All other size fields are ordinary ssh2 + * strings, so the size field indicates how much data is to + * _follow_.) + * + * The encrypted blob, once decrypted, contains a single string + * which in turn contains the payload. (This allows padding to be + * added after that string while still making it clear where the + * real payload ends. Also it probably makes for a reasonable + * decryption check.) + * + * The payload blob, for an RSA key, contains: + * - mpint e + * - mpint d + * - mpint n (yes, the public and private stuff is intermixed) + * - mpint u (presumably inverse of p mod q) + * - mpint p (p is the smaller prime) + * - mpint q (q is the larger) + * + * For a DSA key, the payload blob contains: + * - uint32 0 + * - mpint p + * - mpint g + * - mpint q + * - mpint y + * - mpint x + * + * Alternatively, if the parameters are `predefined', that + * (0,p,g,q) sequence can be replaced by a uint32 1 and a string + * containing some predefined parameter specification. *shudder*, + * but I doubt we'll encounter this in real life. + * + * The key type strings are ghastly. The RSA key I looked at had a + * type string of + * + * `if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}' + * + * and the DSA key wasn't much better: + * + * `dl-modp{sign{dsa-nist-sha1},dh{plain}}' + * + * It isn't clear that these will always be the same. I think it + * might be wise just to look at the `if-modn{sign{rsa' and + * `dl-modp{sign{dsa' prefixes. + * + * Finally, the encryption. The cipher-type string appears to be + * either `none' or `3des-cbc'. Looks as if this is SSH2-style + * 3des-cbc (i.e. outer cbc rather than inner). The key is created + * from the passphrase by means of yet another hashing faff: + * + * - first 16 bytes are MD5(passphrase) + * - next 16 bytes are MD5(passphrase || first 16 bytes) + * - if there were more, they'd be MD5(passphrase || first 32), + * and so on. + */ + +#define SSHCOM_MAGIC_NUMBER 0x3f6ff9eb + +struct sshcom_key { + char comment[256]; /* allowing any length is overkill */ + unsigned char *keyblob; + int keyblob_len, keyblob_size; +}; + +static struct sshcom_key *load_sshcom_key(const char *filename) +{ + struct sshcom_key *ret; + FILE *fp; + char buffer[256]; + int len; + char *errmsg, *p; + int headers_done; + char base64_bit[4]; + int base64_chars = 0; + + ret = snew(struct sshcom_key); + ret->comment[0] = '\0'; + ret->keyblob = NULL; + ret->keyblob_len = ret->keyblob_size = 0; + + fp = fopen(filename, "r"); + if (!fp) { + errmsg = "Unable to open key file"; + goto error; + } + if (!fgets(buffer, sizeof(buffer), fp) || + 0 != strcmp(buffer, "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----\n")) { + errmsg = "File does not begin with ssh.com key header"; + goto error; + } + + headers_done = 0; + while (1) { + if (!fgets(buffer, sizeof(buffer), fp)) { + errmsg = "Unexpected end of file"; + goto error; + } + if (!strcmp(buffer, "---- END SSH2 ENCRYPTED PRIVATE KEY ----\n")) + break; /* done */ + if ((p = strchr(buffer, ':')) != NULL) { + if (headers_done) { + errmsg = "Header found in body of key data"; + goto error; + } + *p++ = '\0'; + while (*p && isspace((unsigned char)*p)) p++; + /* + * Header lines can end in a trailing backslash for + * continuation. + */ + while ((len = strlen(p)) > (int)(sizeof(buffer) - (p-buffer) -1) || + p[len-1] != '\n' || p[len-2] == '\\') { + if (len > (int)((p-buffer) + sizeof(buffer)-2)) { + errmsg = "Header line too long to deal with"; + goto error; + } + if (!fgets(p+len-2, sizeof(buffer)-(p-buffer)-(len-2), fp)) { + errmsg = "Unexpected end of file"; + goto error; + } + } + p[strcspn(p, "\n")] = '\0'; + if (!strcmp(buffer, "Comment")) { + /* Strip quotes in comment if present. */ + if (p[0] == '"' && p[strlen(p)-1] == '"') { + p++; + p[strlen(p)-1] = '\0'; + } + strncpy(ret->comment, p, sizeof(ret->comment)); + ret->comment[sizeof(ret->comment)-1] = '\0'; + } + } else { + headers_done = 1; + + p = buffer; + while (isbase64(*p)) { + base64_bit[base64_chars++] = *p; + if (base64_chars == 4) { + unsigned char out[3]; + + base64_chars = 0; + + len = base64_decode_atom(base64_bit, out); + + if (len <= 0) { + errmsg = "Invalid base64 encoding"; + goto error; + } + + if (ret->keyblob_len + len > ret->keyblob_size) { + ret->keyblob_size = ret->keyblob_len + len + 256; + ret->keyblob = sresize(ret->keyblob, ret->keyblob_size, + unsigned char); + } + + memcpy(ret->keyblob + ret->keyblob_len, out, len); + ret->keyblob_len += len; + } + + p++; + } + } + } + + if (ret->keyblob_len == 0 || !ret->keyblob) { + errmsg = "Key body not present"; + goto error; + } + + return ret; + + error: + if (ret) { + if (ret->keyblob) { + memset(ret->keyblob, 0, ret->keyblob_size); + m_free(ret->keyblob); + } + memset(&ret, 0, sizeof(ret)); + m_free(ret); + } + return NULL; +} + +int sshcom_encrypted(const char *filename, char **comment) +{ + struct sshcom_key *key = load_sshcom_key(filename); + int pos, len, answer; + + *comment = NULL; + if (!key) + return 0; + + /* + * Check magic number. + */ + if (GET_32BIT(key->keyblob) != 0x3f6ff9eb) + return 0; /* key is invalid */ + + /* + * Find the cipher-type string. + */ + answer = 0; + pos = 8; + if (key->keyblob_len < pos+4) + goto done; /* key is far too short */ + pos += 4 + GET_32BIT(key->keyblob + pos); /* skip key type */ + if (key->keyblob_len < pos+4) + goto done; /* key is far too short */ + len = GET_32BIT(key->keyblob + pos); /* find cipher-type length */ + if (key->keyblob_len < pos+4+len) + goto done; /* cipher type string is incomplete */ + if (len != 4 || 0 != memcmp(key->keyblob + pos + 4, "none", 4)) + answer = 1; + + done: + *comment = dupstr(key->comment); + memset(key->keyblob, 0, key->keyblob_size); + m_free(key->keyblob); + memset(&key, 0, sizeof(key)); + m_free(key); + return answer; +} + +static int sshcom_read_mpint(void *data, int len, struct mpint_pos *ret) +{ + int bits; + int bytes; + unsigned char *d = (unsigned char *) data; + + if (len < 4) + goto error; + bits = GET_32BIT(d); + + bytes = (bits + 7) / 8; + if (len < 4+bytes) + goto error; + + ret->start = d + 4; + ret->bytes = bytes; + return bytes+4; + + error: + ret->start = NULL; + ret->bytes = -1; + return len; /* ensure further calls fail as well */ +} + +static int sshcom_put_mpint(void *target, void *data, int len) +{ + unsigned char *d = (unsigned char *)target; + unsigned char *i = (unsigned char *)data; + int bits = len * 8 - 1; + + while (bits > 0) { + if (*i & (1 << (bits & 7))) + break; + if (!(bits-- & 7)) + i++, len--; + } + + PUT_32BIT(d, bits+1); + memcpy(d+4, i, len); + return len+4; +} + +sign_key *sshcom_read(const char *filename, char *passphrase) +{ + struct sshcom_key *key = load_sshcom_key(filename); + char *errmsg; + int pos, len; + const char prefix_rsa[] = "if-modn{sign{rsa"; + const char prefix_dsa[] = "dl-modp{sign{dsa"; + enum { RSA, DSA } type; + int encrypted; + char *ciphertext; + int cipherlen; + struct ssh2_userkey *ret = NULL, *retkey; + const struct ssh_signkey *alg; + unsigned char *blob = NULL; + int blobsize, publen, privlen; + + if (!key) + return NULL; + + /* + * Check magic number. + */ + if (GET_32BIT(key->keyblob) != SSHCOM_MAGIC_NUMBER) { + errmsg = "Key does not begin with magic number"; + goto error; + } + + /* + * Determine the key type. + */ + pos = 8; + if (key->keyblob_len < pos+4 || + (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) { + errmsg = "Key blob does not contain a key type string"; + goto error; + } + if (len > sizeof(prefix_rsa) - 1 && + !memcmp(key->keyblob+pos+4, prefix_rsa, sizeof(prefix_rsa) - 1)) { + type = RSA; + } else if (len > sizeof(prefix_dsa) - 1 && + !memcmp(key->keyblob+pos+4, prefix_dsa, sizeof(prefix_dsa) - 1)) { + type = DSA; + } else { + errmsg = "Key is of unknown type"; + goto error; + } + pos += 4+len; + + /* + * Determine the cipher type. + */ + if (key->keyblob_len < pos+4 || + (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) { + errmsg = "Key blob does not contain a cipher type string"; + goto error; + } + if (len == 4 && !memcmp(key->keyblob+pos+4, "none", 4)) + encrypted = 0; + else if (len == 8 && !memcmp(key->keyblob+pos+4, "3des-cbc", 8)) + encrypted = 1; + else { + errmsg = "Key encryption is of unknown type"; + goto error; + } + pos += 4+len; + + /* + * Get hold of the encrypted part of the key. + */ + if (key->keyblob_len < pos+4 || + (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) { + errmsg = "Key blob does not contain actual key data"; + goto error; + } + ciphertext = (char *)key->keyblob + pos + 4; + cipherlen = len; + if (cipherlen == 0) { + errmsg = "Length of key data is zero"; + goto error; + } + + /* + * Decrypt it if necessary. + */ + if (encrypted) { + /* + * Derive encryption key from passphrase and iv/salt: + * + * - let block A equal MD5(passphrase) + * - let block B equal MD5(passphrase || A) + * - block C would be MD5(passphrase || A || B) and so on + * - encryption key is the first N bytes of A || B + */ + struct MD5Context md5c; + unsigned char keybuf[32], iv[8]; + + if (cipherlen % 8 != 0) { + errmsg = "Encrypted part of key is not a multiple of cipher block" + " size"; + goto error; + } + + MD5Init(&md5c); + MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); + MD5Final(keybuf, &md5c); + + MD5Init(&md5c); + MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); + MD5Update(&md5c, keybuf, 16); + MD5Final(keybuf+16, &md5c); + + /* + * Now decrypt the key blob. + */ + memset(iv, 0, sizeof(iv)); + des3_decrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext, + cipherlen); + + memset(&md5c, 0, sizeof(md5c)); + memset(keybuf, 0, sizeof(keybuf)); + + /* + * Hereafter we return WRONG_PASSPHRASE for any parsing + * error. (But only if we've just tried to decrypt it! + * Returning WRONG_PASSPHRASE for an unencrypted key is + * automatic doom.) + */ + if (encrypted) + ret = SSH2_WRONG_PASSPHRASE; + } + + /* + * Strip away the containing string to get to the real meat. + */ + len = GET_32BIT(ciphertext); + if (len > cipherlen-4) { + errmsg = "containing string was ill-formed"; + goto error; + } + ciphertext += 4; + cipherlen = len; + + /* + * Now we break down into RSA versus DSA. In either case we'll + * construct public and private blobs in our own format, and + * end up feeding them to alg->createkey(). + */ + blobsize = cipherlen + 256; + blob = snewn(blobsize, unsigned char); + privlen = 0; + if (type == RSA) { + struct mpint_pos n, e, d, u, p, q; + int pos = 0; + pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &e); + pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &d); + pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &n); + pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &u); + pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &p); + pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &q); + if (!q.start) { + errmsg = "key data did not contain six integers"; + goto error; + } + + alg = &ssh_rsa; + pos = 0; + pos += put_string(blob+pos, "ssh-rsa", 7); + pos += put_mp(blob+pos, e.start, e.bytes); + pos += put_mp(blob+pos, n.start, n.bytes); + publen = pos; + pos += put_string(blob+pos, d.start, d.bytes); + pos += put_mp(blob+pos, q.start, q.bytes); + pos += put_mp(blob+pos, p.start, p.bytes); + pos += put_mp(blob+pos, u.start, u.bytes); + privlen = pos - publen; + } else if (type == DSA) { + struct mpint_pos p, q, g, x, y; + int pos = 4; + if (GET_32BIT(ciphertext) != 0) { + errmsg = "predefined DSA parameters not supported"; + goto error; + } + pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &p); + pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &g); + pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &q); + pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &y); + pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &x); + if (!x.start) { + errmsg = "key data did not contain five integers"; + goto error; + } + + alg = &ssh_dss; + pos = 0; + pos += put_string(blob+pos, "ssh-dss", 7); + pos += put_mp(blob+pos, p.start, p.bytes); + pos += put_mp(blob+pos, q.start, q.bytes); + pos += put_mp(blob+pos, g.start, g.bytes); + pos += put_mp(blob+pos, y.start, y.bytes); + publen = pos; + pos += put_mp(blob+pos, x.start, x.bytes); + privlen = pos - publen; + } + + assert(privlen > 0); /* should have bombed by now if not */ + + retkey = snew(struct ssh2_userkey); + retkey->alg = alg; + retkey->data = alg->createkey(blob, publen, blob+publen, privlen); + if (!retkey->data) { + m_free(retkey); + errmsg = "unable to create key data structure"; + goto error; + } + retkey->comment = dupstr(key->comment); + + errmsg = NULL; /* no error */ + ret = retkey; + + error: + if (blob) { + memset(blob, 0, blobsize); + m_free(blob); + } + memset(key->keyblob, 0, key->keyblob_size); + m_free(key->keyblob); + memset(&key, 0, sizeof(key)); + m_free(key); + return ret; +} + +int sshcom_write(const char *filename, sign_key *key, + char *passphrase) +{ + unsigned char *pubblob, *privblob; + int publen, privlen; + unsigned char *outblob; + int outlen; + struct mpint_pos numbers[6]; + int nnumbers, initial_zero, pos, lenpos, i; + char *type; + char *ciphertext; + int cipherlen; + int ret = 0; + FILE *fp; + + /* + * Fetch the key blobs. + */ + pubblob = key->alg->public_blob(key->data, &publen); + privblob = key->alg->private_blob(key->data, &privlen); + outblob = NULL; + + /* + * Find the sequence of integers to be encoded into the OpenSSH + * key blob, and also decide on the header line. + */ + if (key->alg == &ssh_rsa) { + int pos; + struct mpint_pos n, e, d, p, q, iqmp; + + pos = 4 + GET_32BIT(pubblob); + pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e); + pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n); + pos = 0; + pos += ssh2_read_mpint(privblob+pos, privlen-pos, &d); + pos += ssh2_read_mpint(privblob+pos, privlen-pos, &p); + pos += ssh2_read_mpint(privblob+pos, privlen-pos, &q); + pos += ssh2_read_mpint(privblob+pos, privlen-pos, &iqmp); + + assert(e.start && iqmp.start); /* can't go wrong */ + + numbers[0] = e; + numbers[1] = d; + numbers[2] = n; + numbers[3] = iqmp; + numbers[4] = q; + numbers[5] = p; + + nnumbers = 6; + initial_zero = 0; + type = "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}"; + } else if (key->alg == &ssh_dss) { + int pos; + struct mpint_pos p, q, g, y, x; + + pos = 4 + GET_32BIT(pubblob); + pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p); + pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q); + pos += ssh2_read_mpint(pubblob+pos, publen-pos, &g); + pos += ssh2_read_mpint(pubblob+pos, publen-pos, &y); + pos = 0; + pos += ssh2_read_mpint(privblob+pos, privlen-pos, &x); + + assert(y.start && x.start); /* can't go wrong */ + + numbers[0] = p; + numbers[1] = g; + numbers[2] = q; + numbers[3] = y; + numbers[4] = x; + + nnumbers = 5; + initial_zero = 1; + type = "dl-modp{sign{dsa-nist-sha1},dh{plain}}"; + } else { + assert(0); /* zoinks! */ + } + + /* + * Total size of key blob will be somewhere under 512 plus + * combined length of integers. We'll calculate the more + * precise size as we construct the blob. + */ + outlen = 512; + for (i = 0; i < nnumbers; i++) + outlen += 4 + numbers[i].bytes; + outblob = snewn(outlen, unsigned char); + + /* + * Create the unencrypted key blob. + */ + pos = 0; + PUT_32BIT(outblob+pos, SSHCOM_MAGIC_NUMBER); pos += 4; + pos += 4; /* length field, fill in later */ + pos += put_string(outblob+pos, type, strlen(type)); + { + char *ciphertype = passphrase ? "3des-cbc" : "none"; + pos += put_string(outblob+pos, ciphertype, strlen(ciphertype)); + } + lenpos = pos; /* remember this position */ + pos += 4; /* encrypted-blob size */ + pos += 4; /* encrypted-payload size */ + if (initial_zero) { + PUT_32BIT(outblob+pos, 0); + pos += 4; + } + for (i = 0; i < nnumbers; i++) + pos += sshcom_put_mpint(outblob+pos, + numbers[i].start, numbers[i].bytes); + /* Now wrap up the encrypted payload. */ + PUT_32BIT(outblob+lenpos+4, pos - (lenpos+8)); + /* Pad encrypted blob to a multiple of cipher block size. */ + if (passphrase) { + int padding = -(pos - (lenpos+4)) & 7; + while (padding--) + outblob[pos++] = random_byte(); + } + ciphertext = (char *)outblob+lenpos+4; + cipherlen = pos - (lenpos+4); + assert(!passphrase || cipherlen % 8 == 0); + /* Wrap up the encrypted blob string. */ + PUT_32BIT(outblob+lenpos, cipherlen); + /* And finally fill in the total length field. */ + PUT_32BIT(outblob+4, pos); + + assert(pos < outlen); + + /* + * Encrypt the key. + */ + if (passphrase) { + /* + * Derive encryption key from passphrase and iv/salt: + * + * - let block A equal MD5(passphrase) + * - let block B equal MD5(passphrase || A) + * - block C would be MD5(passphrase || A || B) and so on + * - encryption key is the first N bytes of A || B + */ + struct MD5Context md5c; + unsigned char keybuf[32], iv[8]; + + MD5Init(&md5c); + MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); + MD5Final(keybuf, &md5c); + + MD5Init(&md5c); + MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); + MD5Update(&md5c, keybuf, 16); + MD5Final(keybuf+16, &md5c); + + /* + * Now decrypt the key blob. + */ + memset(iv, 0, sizeof(iv)); + des3_encrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext, + cipherlen); + + memset(&md5c, 0, sizeof(md5c)); + memset(keybuf, 0, sizeof(keybuf)); + } + + /* + * And save it. We'll use Unix line endings just in case it's + * subsequently transferred in binary mode. + */ + fp = fopen(filename, "wb"); /* ensure Unix line endings */ + if (!fp) + goto error; + fputs("---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----\n", fp); + fprintf(fp, "Comment: \""); + /* + * Comment header is broken with backslash-newline if it goes + * over 70 chars. Although it's surrounded by quotes, it + * _doesn't_ escape backslashes or quotes within the string. + * Don't ask me, I didn't design it. + */ + { + int slen = 60; /* starts at 60 due to "Comment: " */ + char *c = key->comment; + while ((int)strlen(c) > slen) { + fprintf(fp, "%.*s\\\n", slen, c); + c += slen; + slen = 70; /* allow 70 chars on subsequent lines */ + } + fprintf(fp, "%s\"\n", c); + } + base64_encode_fp(fp, outblob, pos, 70); + fputs("---- END SSH2 ENCRYPTED PRIVATE KEY ----\n", fp); + fclose(fp); + ret = 1; + + error: + if (outblob) { + memset(outblob, 0, outlen); + m_free(outblob); + } + if (privblob) { + memset(privblob, 0, privlen); + m_free(privblob); + } + if (pubblob) { + memset(pubblob, 0, publen); + m_free(pubblob); + } + return ret; +} +#endif /* ssh.com stuff disabled */ diff --git a/keyimport.h b/keyimport.h new file mode 100644 index 0000000..f8c7b64 --- /dev/null +++ b/keyimport.h @@ -0,0 +1,18 @@ +#ifndef _KEYIMPORT_H_ +#define _KEYIMPORT_H_ + +#include "includes.h" +#include "signkey.h" + +enum { + KEYFILE_DROPBEAR, + KEYFILE_OPENSSH, + KEYFILE_SSHCOM +}; + +int import_write(const char *filename, sign_key *key, char *passphrase, + int filetype); +sign_key *import_read(const char *filename, char *passphrase, int filetype); +int import_encrypted(const char* filename, int filetype); + +#endif /* _KEYIMPORT_H_ */ diff --git a/localtcpfwd.c b/localtcpfwd.c new file mode 100644 index 0000000..9894f46 --- /dev/null +++ b/localtcpfwd.c @@ -0,0 +1,142 @@ +#include "includes.h" +#include "session.h" +#include "dbutil.h" +#include "localtcpfwd.h" + +#ifndef DISABLE_LOCALTCPFWD +static int newtcp(const char * host, int port); + +/* Called upon creating a new direct tcp channel (ie we connect out to an + * address */ +int newtcpdirect(struct Channel * channel) { + + unsigned char* desthost = NULL; + unsigned int destport; + unsigned char* orighost = NULL; + unsigned int origport; + int sock; + int len; + int ret = DROPBEAR_FAILURE; + + if (ses.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; + } + + sock = newtcp(desthost, destport); + 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 */ +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 /* DISABLE_LOCALTCPFWD */ diff --git a/localtcpfwd.h b/localtcpfwd.h new file mode 100644 index 0000000..324cb55 --- /dev/null +++ b/localtcpfwd.h @@ -0,0 +1,34 @@ +/* + * 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 _LOCALTCPFWD_H_ +#define _LOCALTCPFWD_H_ +#ifndef DISABLE_LOCALTCPFWD + +#include "includes.h" +#include "channel.h" + +int newtcpdirect(struct Channel * channel); + +#endif +#endif diff --git a/loginrec.c b/loginrec.c new file mode 100644 index 0000000..5ec0bc1 --- /dev/null +++ b/loginrec.c @@ -0,0 +1,1540 @@ +/* + * Copyright (c) 2000 Andre Lucas. All rights reserved. + * Portions copyright (c) 1998 Todd C. Miller + * Portions copyright (c) 1996 Jason Downs + * Portions copyright (c) 1996 Theo de Raadt + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/** + ** loginrec.c: platform-independent login recording and lastlog retrieval + **/ + +/* + The new login code explained + ============================ + + This code attempts to provide a common interface to login recording + (utmp and friends) and last login time retrieval. + + Its primary means of achieving this is to use 'struct logininfo', a + union of all the useful fields in the various different types of + system login record structures one finds on UNIX variants. + + We depend on autoconf to define which recording methods are to be + used, and which fields are contained in the relevant data structures + on the local system. Many C preprocessor symbols affect which code + gets compiled here. + + The code is designed to make it easy to modify a particular + recording method, without affecting other methods nor requiring so + many nested conditional compilation blocks as were commonplace in + the old code. + + For login recording, we try to use the local system's libraries as + these are clearly most likely to work correctly. For utmp systems + this usually means login() and logout() or setutent() etc., probably + in libutil, along with logwtmp() etc. On these systems, we fall back + to writing the files directly if we have to, though this method + requires very thorough testing so we do not corrupt local auditing + information. These files and their access methods are very system + specific indeed. + + For utmpx systems, the corresponding library functions are + setutxent() etc. To the author's knowledge, all utmpx systems have + these library functions and so no direct write is attempted. If such + a system exists and needs support, direct analogues of the [uw]tmp + code should suffice. + + Retrieving the time of last login ('lastlog') is in some ways even + more problemmatic than login recording. Some systems provide a + simple table of all users which we seek based on uid and retrieve a + relatively standard structure. Others record the same information in + a directory with a separate file, and others don't record the + information separately at all. For systems in the latter category, + we look backwards in the wtmp or wtmpx file for the last login entry + for our user. Naturally this is slower and on busy systems could + incur a significant performance penalty. + + Calling the new code + -------------------- + + In OpenSSH all login recording and retrieval is performed in + login.c. Here you'll find working examples. Also, in the logintest.c + program there are more examples. + + Internal handler calling method + ------------------------------- + + When a call is made to login_login() or login_logout(), both + routines set a struct logininfo flag defining which action (log in, + or log out) is to be taken. They both then call login_write(), which + calls whichever of the many structure-specific handlers autoconf + selects for the local system. + + The handlers themselves handle system data structure specifics. Both + struct utmp and struct utmpx have utility functions (see + construct_utmp*()) to try to make it simpler to add extra systems + that introduce new features to either structure. + + While it may seem terribly wasteful to replicate so much similar + code for each method, experience has shown that maintaining code to + write both struct utmp and utmpx in one function, whilst maintaining + support for all systems whether they have library support or not, is + a difficult and time-consuming task. + + Lastlog support proceeds similarly. Functions login_get_lastlog() + (and its OpenSSH-tuned friend login_get_lastlog_time()) call + getlast_entry(), which tries one of three methods to find the last + login time. It uses local system lastlog support if it can, + otherwise it tries wtmp or wtmpx before giving up and returning 0, + meaning "tilt". + + Maintenance + ----------- + + In many cases it's possible to tweak autoconf to select the correct + methods for a particular platform, either by improving the detection + code (best), or by presetting DISABLE_ or CONF__FILE + symbols for the platform. + + Use logintest to check which symbols are defined before modifying + configure.ac and loginrec.c. (You have to build logintest yourself + with 'make logintest' as it's not built by default.) + + Otherwise, patches to the specific method(s) are very helpful! + +*/ + +/** + ** TODO: + ** homegrown ttyslot() + ** test, test, test + ** + ** Platform status: + ** ---------------- + ** + ** Known good: + ** Linux (Redhat 6.2, Debian) + ** Solaris + ** HP-UX 10.20 (gcc only) + ** IRIX + ** NeXT - M68k/HPPA/Sparc (4.2/3.3) + ** + ** Testing required: Please send reports! + ** NetBSD + ** HP-UX 11 + ** AIX + ** + ** Platforms with known problems: + ** Some variants of Slackware Linux + ** + **/ + +/*RCSID("$Id: loginrec.c,v 1.2 2004/05/04 10:17:43 matt Exp $");*/ + +#include "includes.h" +#include "loginrec.h" +#include "dbutil.h" +#include "atomicio.h" + +/** + ** prototypes for helper functions in this file + **/ + +#if HAVE_UTMP_H +void set_utmp_time(struct logininfo *li, struct utmp *ut); +void construct_utmp(struct logininfo *li, struct utmp *ut); +#endif + +#ifdef HAVE_UTMPX_H +void set_utmpx_time(struct logininfo *li, struct utmpx *ut); +void construct_utmpx(struct logininfo *li, struct utmpx *ut); +#endif + +int utmp_write_entry(struct logininfo *li); +int utmpx_write_entry(struct logininfo *li); +int wtmp_write_entry(struct logininfo *li); +int wtmpx_write_entry(struct logininfo *li); +int lastlog_write_entry(struct logininfo *li); +int syslogin_write_entry(struct logininfo *li); + +int getlast_entry(struct logininfo *li); +int lastlog_get_entry(struct logininfo *li); +int wtmp_get_entry(struct logininfo *li); +int wtmpx_get_entry(struct logininfo *li); + +/* pick the shortest string */ +#define MIN_SIZEOF(s1,s2) ( sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2) ) + +/** + ** platform-independent login functions + **/ + +/* login_login(struct logininfo *) -Record a login + * + * Call with a pointer to a struct logininfo initialised with + * login_init_entry() or login_alloc_entry() + * + * Returns: + * >0 if successful + * 0 on failure (will use OpenSSH's logging facilities for diagnostics) + */ +int +login_login (struct logininfo *li) +{ + li->type = LTYPE_LOGIN; + return login_write(li); +} + + +/* login_logout(struct logininfo *) - Record a logout + * + * Call as with login_login() + * + * Returns: + * >0 if successful + * 0 on failure (will use OpenSSH's logging facilities for diagnostics) + */ +int +login_logout(struct logininfo *li) +{ + li->type = LTYPE_LOGOUT; + return login_write(li); +} + +/* login_get_lastlog_time(int) - Retrieve the last login time + * + * Retrieve the last login time for the given uid. Will try to use the + * system lastlog facilities if they are available, but will fall back + * to looking in wtmp/wtmpx if necessary + * + * Returns: + * 0 on failure, or if user has never logged in + * Time in seconds from the epoch if successful + * + * Useful preprocessor symbols: + * DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog + * info + * USE_LASTLOG: If set, indicates the presence of system lastlog + * facilities. If this and DISABLE_LASTLOG are not set, + * try to retrieve lastlog information from wtmp/wtmpx. + */ +unsigned int +login_get_lastlog_time(const int uid) +{ + struct logininfo li; + + if (login_get_lastlog(&li, uid)) + return li.tv_sec; + else + return 0; +} + +/* login_get_lastlog(struct logininfo *, int) - Retrieve a lastlog entry + * + * Retrieve a logininfo structure populated (only partially) with + * information from the system lastlog data, or from wtmp/wtmpx if no + * system lastlog information exists. + * + * Note this routine must be given a pre-allocated logininfo. + * + * Returns: + * >0: A pointer to your struct logininfo if successful + * 0 on failure (will use OpenSSH's logging facilities for diagnostics) + * + */ +struct logininfo * +login_get_lastlog(struct logininfo *li, const int uid) +{ + struct passwd *pw; + + memset(li, '\0', sizeof(*li)); + li->uid = uid; + + /* + * If we don't have a 'real' lastlog, we need the username to + * reliably search wtmp(x) for the last login (see + * wtmp_get_entry().) + */ + pw = getpwuid(uid); + if (pw == NULL) + dropbear_exit("login_get_lastlog: Cannot find account for uid %i", uid); + + /* No MIN_SIZEOF here - we absolutely *must not* truncate the + * username */ + strlcpy(li->username, pw->pw_name, sizeof(li->username)); + + if (getlast_entry(li)) + return li; + else + return NULL; +} + + +/* login_alloc_entry(int, char*, char*, char*) - Allocate and initialise + * a logininfo structure + * + * This function creates a new struct logininfo, a data structure + * meant to carry the information required to portably record login info. + * + * Returns a pointer to a newly created struct logininfo. If memory + * allocation fails, the program halts. + */ +struct +logininfo *login_alloc_entry(int pid, const char *username, + const char *hostname, const char *line) +{ + struct logininfo *newli; + + newli = (struct logininfo *) m_malloc (sizeof(*newli)); + (void)login_init_entry(newli, pid, username, hostname, line); + return newli; +} + + +/* login_free_entry(struct logininfo *) - free struct memory */ +void +login_free_entry(struct logininfo *li) +{ + m_free(li); +} + + +/* login_init_entry(struct logininfo *, int, char*, char*, char*) + * - initialise a struct logininfo + * + * Populates a new struct logininfo, a data structure meant to carry + * the information required to portably record login info. + * + * Returns: 1 + */ +int +login_init_entry(struct logininfo *li, int pid, const char *username, + const char *hostname, const char *line) +{ + struct passwd *pw; + + memset(li, 0, sizeof(*li)); + + li->pid = pid; + + /* set the line information */ + if (line) + line_fullname(li->line, line, sizeof(li->line)); + + if (username) { + strlcpy(li->username, username, sizeof(li->username)); + pw = getpwnam(li->username); + if (pw == NULL) + dropbear_exit("login_init_entry: Cannot find user \"%s\"", + li->username); + li->uid = pw->pw_uid; + } + + if (hostname) + strlcpy(li->hostname, hostname, sizeof(li->hostname)); + + return 1; +} + +/* login_set_current_time(struct logininfo *) - set the current time + * + * Set the current time in a logininfo structure. This function is + * meant to eliminate the need to deal with system dependencies for + * time handling. + */ +void +login_set_current_time(struct logininfo *li) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + li->tv_sec = tv.tv_sec; + li->tv_usec = tv.tv_usec; +} + +/* copy a sockaddr_* into our logininfo */ +void +login_set_addr(struct logininfo *li, const struct sockaddr *sa, + const unsigned int sa_size) +{ + unsigned int bufsize = sa_size; + + /* make sure we don't overrun our union */ + if (sizeof(li->hostaddr) < sa_size) + bufsize = sizeof(li->hostaddr); + + memcpy((void *)&(li->hostaddr.sa), (const void *)sa, bufsize); +} + + +/** + ** login_write: Call low-level recording functions based on autoconf + ** results + **/ +int +login_write (struct logininfo *li) +{ +#ifndef HAVE_CYGWIN + if ((int)geteuid() != 0) { + dropbear_log(LOG_WARNING, + "Attempt to write login records by non-root user (aborting)"); + return 1; + } +#endif + + /* set the timestamp */ + login_set_current_time(li); +#ifdef USE_LOGIN + syslogin_write_entry(li); +#endif +#ifdef USE_LASTLOG + if (li->type == LTYPE_LOGIN) { + lastlog_write_entry(li); + } +#endif +#ifdef USE_UTMP + utmp_write_entry(li); +#endif +#ifdef USE_WTMP + wtmp_write_entry(li); +#endif +#ifdef USE_UTMPX + utmpx_write_entry(li); +#endif +#ifdef USE_WTMPX + wtmpx_write_entry(li); +#endif + return 0; +} + +#ifdef LOGIN_NEEDS_UTMPX +int +login_utmp_only(struct logininfo *li) +{ + li->type = LTYPE_LOGIN; + login_set_current_time(li); +# ifdef USE_UTMP + utmp_write_entry(li); +# endif +# ifdef USE_WTMP + wtmp_write_entry(li); +# endif +# ifdef USE_UTMPX + utmpx_write_entry(li); +# endif +# ifdef USE_WTMPX + wtmpx_write_entry(li); +# endif + return 0; +} +#endif + +/** + ** getlast_entry: Call low-level functions to retrieve the last login + ** time. + **/ + +/* take the uid in li and return the last login time */ +int +getlast_entry(struct logininfo *li) +{ +#ifdef USE_LASTLOG + return(lastlog_get_entry(li)); +#else /* !USE_LASTLOG */ + +#ifdef DISABLE_LASTLOG + /* On some systems we shouldn't even try to obtain last login + * time, e.g. AIX */ + return 0; +# else /* DISABLE_LASTLOG */ + /* Try to retrieve the last login time from wtmp */ +# if defined(USE_WTMP) && (defined(HAVE_STRUCT_UTMP_UT_TIME) || defined(HAVE_STRUCT_UTMP_UT_TV)) + /* retrieve last login time from utmp */ + return (wtmp_get_entry(li)); +# else /* defined(USE_WTMP) && (defined(HAVE_STRUCT_UTMP_UT_TIME) || defined(HAVE_STRUCT_UTMP_UT_TV)) */ + /* If wtmp isn't available, try wtmpx */ +# if defined(USE_WTMPX) && (defined(HAVE_STRUCT_UTMPX_UT_TIME) || defined(HAVE_STRUCT_UTMPX_UT_TV)) + /* retrieve last login time from utmpx */ + return (wtmpx_get_entry(li)); +# else + /* Give up: No means of retrieving last login time */ + return 0; +# endif /* USE_WTMPX && (HAVE_STRUCT_UTMPX_UT_TIME || HAVE_STRUCT_UTMPX_UT_TV) */ +# endif /* USE_WTMP && (HAVE_STRUCT_UTMP_UT_TIME || HAVE_STRUCT_UTMP_UT_TV) */ +# endif /* DISABLE_LASTLOG */ +#endif /* USE_LASTLOG */ +} + + + +/* + * 'line' string utility functions + * + * These functions process the 'line' string into one of three forms: + * + * 1. The full filename (including '/dev') + * 2. The stripped name (excluding '/dev') + * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00 + * /dev/pts/1 -> ts/1 ) + * + * Form 3 is used on some systems to identify a .tmp.? entry when + * attempting to remove it. Typically both addition and removal is + * performed by one application - say, sshd - so as long as the choice + * uniquely identifies a terminal it's ok. + */ + + +/* line_fullname(): add the leading '/dev/' if it doesn't exist make + * sure dst has enough space, if not just copy src (ugh) */ +char * +line_fullname(char *dst, const char *src, size_t dstsize) +{ + memset(dst, '\0', dstsize); + if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5))) { + strlcpy(dst, src, dstsize); + } else { + strlcpy(dst, "/dev/", dstsize); + strlcat(dst, src, dstsize); + } + return dst; +} + +/* line_stripname(): strip the leading '/dev' if it exists, return dst */ +char * +line_stripname(char *dst, const char *src, size_t dstsize) +{ + memset(dst, '\0', dstsize); + if (strncmp(src, "/dev/", 5) == 0) + strlcpy(dst, src + 5, dstsize); + else + strlcpy(dst, src, dstsize); + return dst; +} + +/* line_abbrevname(): Return the abbreviated (usually four-character) + * form of the line (Just use the last characters of the + * full name.) + * + * NOTE: use strncpy because we do NOT necessarily want zero + * termination */ +char * +line_abbrevname(char *dst, const char *src, size_t dstsize) +{ + size_t len; + + memset(dst, '\0', dstsize); + + /* Always skip prefix if present */ + if (strncmp(src, "/dev/", 5) == 0) + src += 5; + +#ifdef WITH_ABBREV_NO_TTY + if (strncmp(src, "tty", 3) == 0) + src += 3; +#endif + + len = strlen(src); + + if (len > 0) { + if (((int)len - dstsize) > 0) + src += ((int)len - dstsize); + + /* note: _don't_ change this to strlcpy */ + strncpy(dst, src, (size_t)dstsize); + } + + return dst; +} + +/** + ** utmp utility functions + ** + ** These functions manipulate struct utmp, taking system differences + ** into account. + **/ + +#if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN) + +/* build the utmp structure */ +void +set_utmp_time(struct logininfo *li, struct utmp *ut) +{ +# ifdef HAVE_STRUCT_UTMP_UT_TV + ut->ut_tv.tv_sec = li->tv_sec; + ut->ut_tv.tv_usec = li->tv_usec; +# else +# ifdef HAVE_STRUCT_UTMP_UT_TIME + ut->ut_time = li->tv_sec; +# endif +# endif +} + +void +construct_utmp(struct logininfo *li, + struct utmp *ut) +{ +# ifdef HAVE_ADDR_V6_IN_UTMP + struct sockaddr_in6 *sa6; +# endif + memset(ut, '\0', sizeof(*ut)); + + /* First fill out fields used for both logins and logouts */ + +# ifdef HAVE_STRUCT_UTMP_UT_ID + line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id)); +# endif + +# ifdef HAVE_STRUCT_UTMP_UT_TYPE + /* This is done here to keep utmp constants out of struct logininfo */ + switch (li->type) { + case LTYPE_LOGIN: + ut->ut_type = USER_PROCESS; +#ifdef _UNICOS + cray_set_tmpdir(ut); +#endif + break; + case LTYPE_LOGOUT: + ut->ut_type = DEAD_PROCESS; +#ifdef _UNICOS + cray_retain_utmp(ut, li->pid); +#endif + break; + } +# endif + set_utmp_time(li, ut); + + line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line)); + +# ifdef HAVE_STRUCT_UTMP_UT_PID + ut->ut_pid = li->pid; +# endif + + /* If we're logging out, leave all other fields blank */ + if (li->type == LTYPE_LOGOUT) + return; + + /* + * These fields are only used when logging in, and are blank + * for logouts. + */ + + /* Use strncpy because we don't necessarily want null termination */ + strncpy(ut->ut_name, li->username, MIN_SIZEOF(ut->ut_name, li->username)); +# ifdef HAVE_STRUCT_UTMP_UT_HOST + strncpy(ut->ut_host, li->hostname, MIN_SIZEOF(ut->ut_host, li->hostname)); +# endif +# ifdef HAVE_STRUCT_UTMP_UT_ADDR + /* this is just a 32-bit IP address */ + if (li->hostaddr.sa.sa_family == AF_INET) + ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr; +# endif +# ifdef HAVE_ADDR_V6_IN_UTMP + /* this is just a 128-bit IPv6 address */ + if (li->hostaddr.sa.sa_family == AF_INET6) { + sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa); + memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16); + if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) { + ut->ut_addr_v6[0] = ut->ut_addr_v6[3]; + ut->ut_addr_v6[1] = 0; + ut->ut_addr_v6[2] = 0; + ut->ut_addr_v6[3] = 0; + } + } +# endif +} +#endif /* USE_UTMP || USE_WTMP || USE_LOGIN */ + +/** + ** utmpx utility functions + ** + ** These functions manipulate struct utmpx, accounting for system + ** variations. + **/ + +#if defined(USE_UTMPX) || defined (USE_WTMPX) +/* build the utmpx structure */ +void +set_utmpx_time(struct logininfo *li, struct utmpx *utx) +{ +# ifdef HAVE_STRUCT_UTMPX_UT_TV + utx->ut_tv.tv_sec = li->tv_sec; + utx->ut_tv.tv_usec = li->tv_usec; +# else /* HAVE_STRUCT_UTMPX_UT_TV */ +# ifdef HAVE_STRUCT_UTMPX_UT_TIME + utx->ut_time = li->tv_sec; +# endif /* HAVE_STRUCT_UTMPX_UT_TIME */ +# endif /* HAVE_STRUCT_UTMPX_UT_TV */ +} + +void +construct_utmpx(struct logininfo *li, struct utmpx *utx) +{ +# ifdef HAVE_ADDR_V6_IN_UTMP + struct sockaddr_in6 *sa6; +# endif + memset(utx, '\0', sizeof(*utx)); +# ifdef HAVE_STRUCT_UTMPX_UT_ID + line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id)); +# endif + + /* this is done here to keep utmp constants out of loginrec.h */ + switch (li->type) { + case LTYPE_LOGIN: + utx->ut_type = USER_PROCESS; + break; + case LTYPE_LOGOUT: + utx->ut_type = DEAD_PROCESS; + break; + } + line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line)); + set_utmpx_time(li, utx); + utx->ut_pid = li->pid; + /* strncpy(): Don't necessarily want null termination */ + strncpy(utx->ut_name, li->username, MIN_SIZEOF(utx->ut_name, li->username)); + + if (li->type == LTYPE_LOGOUT) + return; + + /* + * These fields are only used when logging in, and are blank + * for logouts. + */ + +# ifdef HAVE_STRUCT_UTMPX_UT_HOST + strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname)); +# endif +# ifdef HAVE_STRUCT_UTMPX_UT_ADDR + /* this is just a 32-bit IP address */ + if (li->hostaddr.sa.sa_family == AF_INET) + utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr; +# endif +# ifdef HAVE_ADDR_V6_IN_UTMP + /* this is just a 128-bit IPv6 address */ + if (li->hostaddr.sa.sa_family == AF_INET6) { + sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa); + memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16); + if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) { + ut->ut_addr_v6[0] = ut->ut_addr_v6[3]; + ut->ut_addr_v6[1] = 0; + ut->ut_addr_v6[2] = 0; + ut->ut_addr_v6[3] = 0; + } + } +# endif +# ifdef HAVE_STRUCT_UTMPX_UT_SYSLEN + /* ut_syslen is the length of the utx_host string */ + utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host)); +# endif +} +#endif /* USE_UTMPX || USE_WTMPX */ + +/** + ** Low-level utmp functions + **/ + +/* FIXME: (ATL) utmp_write_direct needs testing */ +#ifdef USE_UTMP + +/* if we can, use pututline() etc. */ +# if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \ + defined(HAVE_PUTUTLINE) +# define UTMP_USE_LIBRARY +# endif + + +/* write a utmp entry with the system's help (pututline() and pals) */ +# ifdef UTMP_USE_LIBRARY +static int +utmp_write_library(struct logininfo *li, struct utmp *ut) +{ + setutent(); + pututline(ut); + +# ifdef HAVE_ENDUTENT + endutent(); +# endif + return 1; +} +# else /* UTMP_USE_LIBRARY */ + +/* write a utmp entry direct to the file */ +/* This is a slightly modification of code in OpenBSD's login.c */ +static int +utmp_write_direct(struct logininfo *li, struct utmp *ut) +{ + struct utmp old_ut; + register int fd; + int tty; + + /* FIXME: (ATL) ttyslot() needs local implementation */ + +#if defined(HAVE_GETTTYENT) + register struct ttyent *ty; + + tty=0; + + setttyent(); + while ((struct ttyent *)0 != (ty = getttyent())) { + tty++; + if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line))) + break; + } + endttyent(); + + if((struct ttyent *)0 == ty) { + dropbear_log(LOG_WARNING, "utmp_write_entry: tty not found"); + return(1); + } +#else /* FIXME */ + + tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */ + +#endif /* HAVE_GETTTYENT */ + + if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) { + (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET); + /* + * Prevent luser from zero'ing out ut_host. + * If the new ut_line is empty but the old one is not + * and ut_line and ut_name match, preserve the old ut_line. + */ + if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) && + (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') && + (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) && + (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) { + (void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host)); + } + + (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET); + if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut)) + dropbear_log(LOG_WARNING, "utmp_write_direct: error writing %s: %s", + UTMP_FILE, strerror(errno)); + + (void)close(fd); + return 1; + } else { + return 0; + } +} +# endif /* UTMP_USE_LIBRARY */ + +static int +utmp_perform_login(struct logininfo *li) +{ + struct utmp ut; + + construct_utmp(li, &ut); +# ifdef UTMP_USE_LIBRARY + if (!utmp_write_library(li, &ut)) { + dropbear_log(LOG_WARNING, "utmp_perform_login: utmp_write_library() failed"); + return 0; + } +# else + if (!utmp_write_direct(li, &ut)) { + dropbear_log(LOG_WARNING, "utmp_perform_login: utmp_write_direct() failed"); + return 0; + } +# endif + return 1; +} + + +static int +utmp_perform_logout(struct logininfo *li) +{ + struct utmp ut; + + construct_utmp(li, &ut); +# ifdef UTMP_USE_LIBRARY + if (!utmp_write_library(li, &ut)) { + dropbear_log(LOG_WARNING, "utmp_perform_logout: utmp_write_library() failed"); + return 0; + } +# else + if (!utmp_write_direct(li, &ut)) { + dropbear_log(LOG_WARNING, "utmp_perform_logout: utmp_write_direct() failed"); + return 0; + } +# endif + return 1; +} + + +int +utmp_write_entry(struct logininfo *li) +{ + switch(li->type) { + case LTYPE_LOGIN: + return utmp_perform_login(li); + + case LTYPE_LOGOUT: + return utmp_perform_logout(li); + + default: + dropbear_log(LOG_WARNING, "utmp_write_entry: invalid type field"); + return 0; + } +} +#endif /* USE_UTMP */ + + +/** + ** Low-level utmpx functions + **/ + +/* not much point if we don't want utmpx entries */ +#ifdef USE_UTMPX + +/* if we have the wherewithall, use pututxline etc. */ +# if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \ + defined(HAVE_PUTUTXLINE) +# define UTMPX_USE_LIBRARY +# endif + + +/* write a utmpx entry with the system's help (pututxline() and pals) */ +# ifdef UTMPX_USE_LIBRARY +static int +utmpx_write_library(struct logininfo *li, struct utmpx *utx) +{ + setutxent(); + pututxline(utx); + +# ifdef HAVE_ENDUTXENT + endutxent(); +# endif + return 1; +} + +# else /* UTMPX_USE_LIBRARY */ + +/* write a utmp entry direct to the file */ +static int +utmpx_write_direct(struct logininfo *li, struct utmpx *utx) +{ + dropbear_log(LOG_WARNING, "utmpx_write_direct: not implemented!"); + return 0; +} +# endif /* UTMPX_USE_LIBRARY */ + +static int +utmpx_perform_login(struct logininfo *li) +{ + struct utmpx utx; + + construct_utmpx(li, &utx); +# ifdef UTMPX_USE_LIBRARY + if (!utmpx_write_library(li, &utx)) { + dropbear_log(LOG_WARNING, "utmpx_perform_login: utmp_write_library() failed"); + return 0; + } +# else + if (!utmpx_write_direct(li, &ut)) { + dropbear_log(LOG_WARNING, "utmpx_perform_login: utmp_write_direct() failed"); + return 0; + } +# endif + return 1; +} + + +static int +utmpx_perform_logout(struct logininfo *li) +{ + struct utmpx utx; + + construct_utmpx(li, &utx); +# ifdef HAVE_STRUCT_UTMPX_UT_ID + line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id)); +# endif +# ifdef HAVE_STRUCT_UTMPX_UT_TYPE + utx.ut_type = DEAD_PROCESS; +# endif + +# ifdef UTMPX_USE_LIBRARY + utmpx_write_library(li, &utx); +# else + utmpx_write_direct(li, &utx); +# endif + return 1; +} + +int +utmpx_write_entry(struct logininfo *li) +{ + switch(li->type) { + case LTYPE_LOGIN: + return utmpx_perform_login(li); + case LTYPE_LOGOUT: + return utmpx_perform_logout(li); + default: + dropbear_log(LOG_WARNING, "utmpx_write_entry: invalid type field"); + return 0; + } +} +#endif /* USE_UTMPX */ + + +/** + ** Low-level wtmp functions + **/ + +#ifdef USE_WTMP + +/* write a wtmp entry direct to the end of the file */ +/* This is a slight modification of code in OpenBSD's logwtmp.c */ +static int +wtmp_write(struct logininfo *li, struct utmp *ut) +{ + struct stat buf; + int fd, ret = 1; + + if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) { + dropbear_log(LOG_WARNING, "wtmp_write: problem writing %s: %s", + WTMP_FILE, strerror(errno)); + return 0; + } + if (fstat(fd, &buf) == 0) + if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut)) { + ftruncate(fd, buf.st_size); + dropbear_log(LOG_WARNING, "wtmp_write: problem writing %s: %s", + WTMP_FILE, strerror(errno)); + ret = 0; + } + (void)close(fd); + return ret; +} + +static int +wtmp_perform_login(struct logininfo *li) +{ + struct utmp ut; + + construct_utmp(li, &ut); + return wtmp_write(li, &ut); +} + + +static int +wtmp_perform_logout(struct logininfo *li) +{ + struct utmp ut; + + construct_utmp(li, &ut); + return wtmp_write(li, &ut); +} + + +int +wtmp_write_entry(struct logininfo *li) +{ + switch(li->type) { + case LTYPE_LOGIN: + return wtmp_perform_login(li); + case LTYPE_LOGOUT: + return wtmp_perform_logout(li); + default: + dropbear_log(LOG_WARNING, "wtmp_write_entry: invalid type field"); + return 0; + } +} + + +/* Notes on fetching login data from wtmp/wtmpx + * + * Logouts are usually recorded with (amongst other things) a blank + * username on a given tty line. However, some systems (HP-UX is one) + * leave all fields set, but change the ut_type field to DEAD_PROCESS. + * + * Since we're only looking for logins here, we know that the username + * must be set correctly. On systems that leave it in, we check for + * ut_type==USER_PROCESS (indicating a login.) + * + * Portability: Some systems may set something other than USER_PROCESS + * to indicate a login process. I don't know of any as I write. Also, + * it's possible that some systems may both leave the username in + * place and not have ut_type. + */ + +/* return true if this wtmp entry indicates a login */ +static int +wtmp_islogin(struct logininfo *li, struct utmp *ut) +{ + if (strncmp(li->username, ut->ut_name, + MIN_SIZEOF(li->username, ut->ut_name)) == 0) { +# ifdef HAVE_STRUCT_UTMP_UT_TYPE + if (ut->ut_type & USER_PROCESS) + return 1; +# else + return 1; +# endif + } + return 0; +} + +int +wtmp_get_entry(struct logininfo *li) +{ + struct stat st; + struct utmp ut; + int fd, found=0; + + /* Clear the time entries in our logininfo */ + li->tv_sec = li->tv_usec = 0; + + if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) { + dropbear_log(LOG_WARNING, "wtmp_get_entry: problem opening %s: %s", + WTMP_FILE, strerror(errno)); + return 0; + } + if (fstat(fd, &st) != 0) { + dropbear_log(LOG_WARNING, "wtmp_get_entry: couldn't stat %s: %s", + WTMP_FILE, strerror(errno)); + close(fd); + return 0; + } + + /* Seek to the start of the last struct utmp */ + if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) { + /* Looks like we've got a fresh wtmp file */ + close(fd); + return 0; + } + + while (!found) { + if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) { + dropbear_log(LOG_WARNING, "wtmp_get_entry: read of %s failed: %s", + WTMP_FILE, strerror(errno)); + close (fd); + return 0; + } + if ( wtmp_islogin(li, &ut) ) { + found = 1; + /* We've already checked for a time in struct + * utmp, in login_getlast(). */ +# ifdef HAVE_STRUCT_UTMP_UT_TIME + li->tv_sec = ut.ut_time; +# else +# if HAVE_STRUCT_UTMP_UT_TV + li->tv_sec = ut.ut_tv.tv_sec; +# endif +# endif + line_fullname(li->line, ut.ut_line, + MIN_SIZEOF(li->line, ut.ut_line)); +# ifdef HAVE_STRUCT_UTMP_UT_HOST + strlcpy(li->hostname, ut.ut_host, + MIN_SIZEOF(li->hostname, ut.ut_host)); +# endif + continue; + } + /* Seek back 2 x struct utmp */ + if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) { + /* We've found the start of the file, so quit */ + close (fd); + return 0; + } + } + + /* We found an entry. Tidy up and return */ + close(fd); + return 1; +} +# endif /* USE_WTMP */ + + +/** + ** Low-level wtmpx functions + **/ + +#ifdef USE_WTMPX +/* write a wtmpx entry direct to the end of the file */ +/* This is a slight modification of code in OpenBSD's logwtmp.c */ +static int +wtmpx_write(struct logininfo *li, struct utmpx *utx) +{ + struct stat buf; + int fd, ret = 1; + + if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) { + dropbear_log(LOG_WARNING, "wtmpx_write: problem opening %s: %s", + WTMPX_FILE, strerror(errno)); + return 0; + } + + if (fstat(fd, &buf) == 0) + if (atomicio(write, fd, utx, sizeof(*utx)) != sizeof(*utx)) { + ftruncate(fd, buf.st_size); + dropbear_log(LOG_WARNING, "wtmpx_write: problem writing %s: %s", + WTMPX_FILE, strerror(errno)); + ret = 0; + } + (void)close(fd); + + return ret; +} + + +static int +wtmpx_perform_login(struct logininfo *li) +{ + struct utmpx utx; + + construct_utmpx(li, &utx); + return wtmpx_write(li, &utx); +} + + +static int +wtmpx_perform_logout(struct logininfo *li) +{ + struct utmpx utx; + + construct_utmpx(li, &utx); + return wtmpx_write(li, &utx); +} + + +int +wtmpx_write_entry(struct logininfo *li) +{ + switch(li->type) { + case LTYPE_LOGIN: + return wtmpx_perform_login(li); + case LTYPE_LOGOUT: + return wtmpx_perform_logout(li); + default: + dropbear_log(LOG_WARNING, "wtmpx_write_entry: invalid type field"); + return 0; + } +} + +/* Please see the notes above wtmp_islogin() for information about the + next two functions */ + +/* Return true if this wtmpx entry indicates a login */ +static int +wtmpx_islogin(struct logininfo *li, struct utmpx *utx) +{ + if ( strncmp(li->username, utx->ut_name, + MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) { +# ifdef HAVE_STRUCT_UTMPX_UT_TYPE + if (utx->ut_type == USER_PROCESS) + return 1; +# else + return 1; +# endif + } + return 0; +} + + +int +wtmpx_get_entry(struct logininfo *li) +{ + struct stat st; + struct utmpx utx; + int fd, found=0; + + /* Clear the time entries */ + li->tv_sec = li->tv_usec = 0; + + if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) { + dropbear_log(LOG_WARNING, "wtmpx_get_entry: problem opening %s: %s", + WTMPX_FILE, strerror(errno)); + return 0; + } + if (fstat(fd, &st) != 0) { + dropbear_log(LOG_WARNING, "wtmpx_get_entry: couldn't stat %s: %s", + WTMPX_FILE, strerror(errno)); + close(fd); + return 0; + } + + /* Seek to the start of the last struct utmpx */ + if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) { + /* probably a newly rotated wtmpx file */ + close(fd); + return 0; + } + + while (!found) { + if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) { + dropbear_log(LOG_WARNING, "wtmpx_get_entry: read of %s failed: %s", + WTMPX_FILE, strerror(errno)); + close (fd); + return 0; + } + /* Logouts are recorded as a blank username on a particular line. + * So, we just need to find the username in struct utmpx */ + if ( wtmpx_islogin(li, &utx) ) { + found = 1; +# ifdef HAVE_STRUCT_UTMPX_UT_TV + li->tv_sec = utx.ut_tv.tv_sec; +# else +# ifdef HAVE_STRUCT_UTMPX_UT_TIME + li->tv_sec = utx.ut_time; +# endif +# endif + line_fullname(li->line, utx.ut_line, sizeof(li->line)); +# ifdef HAVE_STRUCT_UTMPX_UT_HOST + strlcpy(li->hostname, utx.ut_host, + MIN_SIZEOF(li->hostname, utx.ut_host)); +# endif + continue; + } + if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) { + close (fd); + return 0; + } + } + + close(fd); + return 1; +} +#endif /* USE_WTMPX */ + +/** + ** Low-level libutil login() functions + **/ + +#ifdef USE_LOGIN +static int +syslogin_perform_login(struct logininfo *li) +{ + struct utmp *ut; + + if (! (ut = (struct utmp *)malloc(sizeof(*ut)))) { + dropbear_log(LOG_WARNING, "syslogin_perform_login: couldn't malloc()"); + return 0; + } + construct_utmp(li, ut); + login(ut); + free(ut); + + return 1; +} + +static int +syslogin_perform_logout(struct logininfo *li) +{ +# ifdef HAVE_LOGOUT + char line[8]; + + (void)line_stripname(line, li->line, sizeof(line)); + + if (!logout(line)) { + dropbear_log(LOG_WARNING, "syslogin_perform_logout: logout(%s) returned an error: %s", line, strerror(errno)); +# ifdef HAVE_LOGWTMP + } else { + logwtmp(line, "", ""); +# endif + } + /* FIXME: (ATL - if the need arises) What to do if we have + * login, but no logout? what if logout but no logwtmp? All + * routines are in libutil so they should all be there, + * but... */ +# endif + return 1; +} + +int +syslogin_write_entry(struct logininfo *li) +{ + switch (li->type) { + case LTYPE_LOGIN: + return syslogin_perform_login(li); + case LTYPE_LOGOUT: + return syslogin_perform_logout(li); + default: + dropbear_log(LOG_WARNING, "syslogin_write_entry: Invalid type field"); + return 0; + } +} +#endif /* USE_LOGIN */ + +/* end of file log-syslogin.c */ + +/** + ** Low-level lastlog functions + **/ + +#ifdef USE_LASTLOG +#define LL_FILE 1 +#define LL_DIR 2 +#define LL_OTHER 3 + +static void +lastlog_construct(struct logininfo *li, struct lastlog *last) +{ + /* clear the structure */ + memset(last, '\0', sizeof(*last)); + + (void)line_stripname(last->ll_line, li->line, sizeof(last->ll_line)); + strlcpy(last->ll_host, li->hostname, + MIN_SIZEOF(last->ll_host, li->hostname)); + last->ll_time = li->tv_sec; +} + +static int +lastlog_filetype(char *filename) +{ + struct stat st; + + if (stat(LASTLOG_FILE, &st) != 0) { + dropbear_log(LOG_WARNING, "lastlog_perform_login: Couldn't stat %s: %s", LASTLOG_FILE, + strerror(errno)); + return 0; + } + if (S_ISDIR(st.st_mode)) + return LL_DIR; + else if (S_ISREG(st.st_mode)) + return LL_FILE; + else + return LL_OTHER; +} + + +/* open the file (using filemode) and seek to the login entry */ +static int +lastlog_openseek(struct logininfo *li, int *fd, int filemode) +{ + off_t offset; + int type; + char lastlog_file[1024]; + + type = lastlog_filetype(LASTLOG_FILE); + switch (type) { + case LL_FILE: + strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file)); + break; + case LL_DIR: + snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s", + LASTLOG_FILE, li->username); + break; + default: + dropbear_log(LOG_WARNING, "lastlog_openseek: %.100s is not a file or directory!", + LASTLOG_FILE); + return 0; + } + + *fd = open(lastlog_file, filemode); + if ( *fd < 0) { + dropbear_log(LOG_INFO, "lastlog_openseek: Couldn't open %s: %s", + lastlog_file, strerror(errno)); + return 0; + } + + if (type == LL_FILE) { + /* find this uid's offset in the lastlog file */ + offset = (off_t) ((long)li->uid * sizeof(struct lastlog)); + + if ( lseek(*fd, offset, SEEK_SET) != offset ) { + dropbear_log(LOG_WARNING, "lastlog_openseek: %s->lseek(): %s", + lastlog_file, strerror(errno)); + return 0; + } + } + + return 1; +} + +static int +lastlog_perform_login(struct logininfo *li) +{ + struct lastlog last; + int fd; + + /* create our struct lastlog */ + lastlog_construct(li, &last); + + if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT)) + return(0); + + /* write the entry */ + if (atomicio(write, fd, &last, sizeof(last)) != sizeof(last)) { + close(fd); + dropbear_log(LOG_WARNING, "lastlog_write_filemode: Error writing to %s: %s", + LASTLOG_FILE, strerror(errno)); + return 0; + } + + close(fd); + return 1; +} + +int +lastlog_write_entry(struct logininfo *li) +{ + switch(li->type) { + case LTYPE_LOGIN: + return lastlog_perform_login(li); + default: + dropbear_log(LOG_WARNING, "lastlog_write_entry: Invalid type field"); + return 0; + } +} + +static void +lastlog_populate_entry(struct logininfo *li, struct lastlog *last) +{ + line_fullname(li->line, last->ll_line, sizeof(li->line)); + strlcpy(li->hostname, last->ll_host, + MIN_SIZEOF(li->hostname, last->ll_host)); + li->tv_sec = last->ll_time; +} + +int +lastlog_get_entry(struct logininfo *li) +{ + struct lastlog last; + int fd, ret; + + if (!lastlog_openseek(li, &fd, O_RDONLY)) + return (0); + + ret = atomicio(read, fd, &last, sizeof(last)); + close(fd); + + switch (ret) { + case 0: + memset(&last, '\0', sizeof(last)); + /* FALLTHRU */ + case sizeof(last): + lastlog_populate_entry(li, &last); + return (1); + case -1: + dropbear_log(LOG_ERR, "Error reading from %s: %s", + LASTLOG_FILE, strerror(errno)); + return (0); + default: + dropbear_log(LOG_ERR, "Error reading from %s: Expecting %d, got %d", + LASTLOG_FILE, sizeof(last), ret); + return (0); + } + + /* NOTREACHED */ + return (0); +} +#endif /* USE_LASTLOG */ diff --git a/loginrec.h b/loginrec.h new file mode 100644 index 0000000..3141b10 --- /dev/null +++ b/loginrec.h @@ -0,0 +1,194 @@ +#ifndef _HAVE_LOGINREC_H_ +#define _HAVE_LOGINREC_H_ + +/* + * Copyright (c) 2000 Andre Lucas. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/** + ** loginrec.h: platform-independent login recording and lastlog retrieval + **/ + +#include "includes.h" + +/* RCSID("$Id: loginrec.h,v 1.2 2004/05/04 10:17:43 matt Exp $"); */ + +/* The following #defines are from OpenSSH's defines.h, required for loginrec */ + +/* FIXME: put default paths back in */ +#ifndef UTMP_FILE +# ifdef _PATH_UTMP +# define UTMP_FILE _PATH_UTMP +# else +# ifdef CONF_UTMP_FILE +# define UTMP_FILE CONF_UTMP_FILE +# endif +# endif +#endif +#ifndef WTMP_FILE +# ifdef _PATH_WTMP +# define WTMP_FILE _PATH_WTMP +# else +# ifdef CONF_WTMP_FILE +# define WTMP_FILE CONF_WTMP_FILE +# endif +# endif +#endif +/* pick up the user's location for lastlog if given */ +#ifndef LASTLOG_FILE +# ifdef _PATH_LASTLOG +# define LASTLOG_FILE _PATH_LASTLOG +# else +# ifdef CONF_LASTLOG_FILE +# define LASTLOG_FILE CONF_LASTLOG_FILE +# endif +# endif +#endif + + +/* The login() library function in libutil is first choice */ +#if defined(HAVE_LOGIN) && !defined(DISABLE_LOGIN) +# define USE_LOGIN + +#else +/* Simply select your favourite login types. */ +/* Can't do if-else because some systems use several... */ +# if defined(UTMPX_FILE) && !defined(DISABLE_UTMPX) +# define USE_UTMPX +# endif +# if defined(UTMP_FILE) && !defined(DISABLE_UTMP) +# define USE_UTMP +# endif +# if defined(WTMPX_FILE) && !defined(DISABLE_WTMPX) +# define USE_WTMPX +# endif +# if defined(WTMP_FILE) && !defined(DISABLE_WTMP) +# define USE_WTMP +# endif + +#endif + +/* I hope that the presence of LASTLOG_FILE is enough to detect this */ +#if defined(LASTLOG_FILE) && !defined(DISABLE_LASTLOG) +# define USE_LASTLOG +#endif + + +/** + ** you should use the login_* calls to work around platform dependencies + **/ + +/* + * login_netinfo structure + */ + +union login_netinfo { + struct sockaddr sa; + struct sockaddr_in sa_in; +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE + struct sockaddr_storage sa_storage; +#endif +}; + +/* + * * logininfo structure * + */ +/* types - different to utmp.h 'type' macros */ +/* (though set to the same value as linux, openbsd and others...) */ +#define LTYPE_LOGIN 7 +#define LTYPE_LOGOUT 8 + +/* string lengths - set very long */ +#define LINFO_PROGSIZE 64 +#define LINFO_LINESIZE 64 +#define LINFO_NAMESIZE 64 +#define LINFO_HOSTSIZE 256 + +struct logininfo { + char progname[LINFO_PROGSIZE]; /* name of program (for PAM) */ + int progname_null; + short int type; /* type of login (LTYPE_*) */ + int pid; /* PID of login process */ + int uid; /* UID of this user */ + char line[LINFO_LINESIZE]; /* tty/pty name */ + char username[LINFO_NAMESIZE]; /* login username */ + char hostname[LINFO_HOSTSIZE]; /* remote hostname */ + /* 'exit_status' structure components */ + int exit; /* process exit status */ + int termination; /* process termination status */ + /* struct timeval (sys/time.h) isn't always available, if it isn't we'll + * use time_t's value as tv_sec and set tv_usec to 0 + */ + unsigned int tv_sec; + unsigned int tv_usec; + union login_netinfo hostaddr; /* caller's host address(es) */ +}; /* struct logininfo */ + +/* + * login recording functions + */ + +/** 'public' functions */ + +/* construct a new login entry */ +struct logininfo *login_alloc_entry(int pid, const char *username, + const char *hostname, const char *line); +/* free a structure */ +void login_free_entry(struct logininfo *li); +/* fill out a pre-allocated structure with useful information */ +int login_init_entry(struct logininfo *li, int pid, const char *username, + const char *hostname, const char *line); +/* place the current time in a logininfo struct */ +void login_set_current_time(struct logininfo *li); + +/* record the entry */ +int login_login (struct logininfo *li); +int login_logout(struct logininfo *li); +#ifdef LOGIN_NEEDS_UTMPX +int login_utmp_only(struct logininfo *li); +#endif + +/** End of public functions */ + +/* record the entry */ +int login_write (struct logininfo *li); +int login_log_entry(struct logininfo *li); + +/* set the network address based on network address type */ +void login_set_addr(struct logininfo *li, const struct sockaddr *sa, + const unsigned int sa_size); + +/* + * lastlog retrieval functions + */ +/* lastlog *entry* functions fill out a logininfo */ +struct logininfo *login_get_lastlog(struct logininfo *li, const int uid); +/* lastlog *time* functions return time_t equivalent (uint) */ +unsigned int login_get_lastlog_time(const int uid); + +/* produce various forms of the line filename */ +char *line_fullname(char *dst, const char *src, size_t dstsize); +char *line_stripname(char *dst, const char *src, size_t dstsize); +char *line_abbrevname(char *dst, const char *src, size_t dstsize); + +#endif /* _HAVE_LOGINREC_H_ */ diff --git a/main.c b/main.c new file mode 100644 index 0000000..245dd86 --- /dev/null +++ b/main.c @@ -0,0 +1,343 @@ +/* + * 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. */ + +#include "includes.h" +#include "dbutil.h" +#include "session.h" +#include "buffer.h" +#include "signkey.h" +#include "runopts.h" + +static int listensockets(int *sock, runopts * opts, int *maxfd); +static void sigchld_handler(int dummy); +static void sigsegv_handler(int); +static void sigintterm_handler(int fish); + +static int childpipes[MAX_UNAUTH_CLIENTS]; + +#if defined(DBMULTI_DROPBEAR) || !defined(DROPBEAR_MULTI) +#if defined(DBMULTI_DROPBEAR) && defined(DROPBEAR_MULTI) +int dropbear_main(int argc, char ** argv) +#else +int main(int argc, char ** argv) +#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; + runopts * opts; + FILE * pidfile; + + int childsock; + pid_t childpid; + int childpipe[2]; + + struct sigaction sa_chld; + + /* get commandline options */ + opts = getrunopts(argc, argv); + + /* fork */ + if (opts->forkbg) { + int closefds = 0; +#ifndef DEBUG_TRACE + if (!usingsyslog) { + closefds = 1; + } +#endif + if (daemon(0, closefds) < 0) { + dropbear_exit("Failed to create background process: %s", + strerror(errno)); + } + } + +#ifndef DISABLE_SYSLOG + if (usingsyslog) { + startsyslog(); + } +#endif + + /* should be done after syslog is working */ + if (opts->forkbg) { + dropbear_log(LOG_INFO, "Running in background"); + } else { + dropbear_log(LOG_INFO, "Not forking"); + } + + /* create a PID file so that we can be killed easily */ + pidfile = fopen(DROPBEAR_PIDFILE, "w"); + if (pidfile) { + fprintf(pidfile, "%d\n", getpid()); + 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; + } + + /* Set up the listening sockets */ + /* XXX XXX ports */ + listensockcount = listensockets(listensocks, opts, &maxsock); + + /* incoming connection select loop */ + for(;;) { + + FD_ZERO(&fds); + + seltimeout.tv_sec = 60; + seltimeout.tv_usec = 0; + + /* listening sockets */ + for (i = 0; i < listensockcount; i++) { + FD_SET(listensocks[i], &fds); + } + + /* pre-authentication clients */ + for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) { + if (childpipes[i] >= 0) { + FD_SET(childpipes[i], &fds); + maxsock = MAX(maxsock, childpipes[i]); + } + } + + val = select(maxsock+1, &fds, NULL, NULL, &seltimeout); + + if (exitflag) { + unlink(DROPBEAR_PIDFILE); + dropbear_exit("Terminated by signal"); + } + + if (val == 0) { + /* timeout reached */ + continue; + } + + if (val < 0) { + if (errno == EINTR) { + continue; + } + dropbear_exit("Listening socket error"); + } + + /* close fds which have been authed or closed - auth.c handles + * closing the auth sockets on success */ + for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) { + if (childpipes[i] >= 0 && FD_ISSET(childpipes[i], &fds)) { + close(childpipes[i]); + childpipes[i] = -1; + } + } + + /* handle each socket which has something to say */ + for (i = 0; i < 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); + + if (childsock < 0) { + /* accept failed */ + continue; + } + + /* check for max number of connections not authorised */ + for (j = 0; j < MAX_UNAUTH_CLIENTS; j++) { + if (childpipes[j] < 0) { + break; + } + } + + if (j == MAX_UNAUTH_CLIENTS) { + /* no free connections */ + /* TODO - possibly log, though this would be an easy way + * to fill logs/disk */ + close(childsock); + continue; + } + + if (pipe(childpipe) < 0) { + TRACE(("error creating child pipe")); + close(childsock); + continue; + } + + if ((childpid = fork()) == 0) { + + /* child */ + char * addrstring = NULL; +#ifdef DEBUG_FORKGPROF + extern void _start(void), etext(void); + monstartup((u_long)&_start, (u_long)&etext); +#endif /* DEBUG_FORKGPROF */ + + addrstring = getaddrstring(&remoteaddr); + dropbear_log(LOG_INFO, "Child connection from %s", addrstring); + m_free(addrstring); + + if (setsid() < 0) { + dropbear_exit("setsid: %s", strerror(errno)); + } + + /* make sure we close sockets */ + for (i = 0; i < listensockcount; i++) { + if (m_close(listensocks[i]) == DROPBEAR_FAILURE) { + dropbear_exit("Couldn't close socket"); + } + } + + if (m_close(childpipe[0]) == DROPBEAR_FAILURE) { + dropbear_exit("Couldn't close socket"); + } + /* start the session */ + child_session(childsock, opts, childpipe[1], &remoteaddr); + /* don't return */ + assert(0); + } + + /* parent */ + childpipes[j] = childpipe[0]; + if (m_close(childpipe[1]) == DROPBEAR_FAILURE + || m_close(childsock) == DROPBEAR_FAILURE) { + dropbear_exit("Couldn't close socket"); + } + } + } /* for(;;) loop */ + + /* don't reach here */ + return -1; +} +#endif + +/* catch + reap zombie children */ +static void sigchld_handler(int fish) { + struct sigaction sa_chld; + + while(waitpid(-1, NULL, WNOHANG) > 0); + + sa_chld.sa_handler = sigchld_handler; + sa_chld.sa_flags = SA_NOCLDSTOP; + if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) { + dropbear_exit("signal() error"); + } +} + +/* catch any segvs */ +static void sigsegv_handler(int fish) { + 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) { + + exitflag = 1; +} + +/* Set up listening sockets for all the requested ports */ +static int listensockets(int *sock, runopts * opts, int *maxfd) { + + int listensock; /* listening fd */ + struct sockaddr_in listen_addr; + struct linger linger; + unsigned int i; + int val; + + for (i = 0; i < 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"); + } + + 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(opts->ports[i]); + listen_addr.sin_addr.s_addr = htonl(INADDR_ANY); + memset(&(listen_addr.sin_zero), '\0', 8); + + if (bind(listensock, (struct sockaddr *)&listen_addr, + sizeof(listen_addr)) < 0) { + dropbear_exit("Bind failed port %d", 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 opts->portcount; +} diff --git a/options.h b/options.h new file mode 100644 index 0000000..d1f1794 --- /dev/null +++ b/options.h @@ -0,0 +1,327 @@ +/* + * 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 _OPTIONS_H_ +#define _OPTIONS_H_ + +/****************************************************************** + * Define compile-time options below - the "#ifndef DROPBEAR_XXX .... #endif" + * parts are to allow for commandline -DDROPBEAR_XXX options etc. + ******************************************************************/ +#define DROPBEAR_SERVER +/* #define DROPBEAR_CLIENT */ + +#ifndef DROPBEAR_PORT +#define DROPBEAR_PORT 22 +#endif + +/* Default hostkey paths - these can be specified on the command line */ +#ifndef DSS_PRIV_FILENAME +#define DSS_PRIV_FILENAME "/etc/dropbear/dropbear_dss_host_key" +#endif +#ifndef RSA_PRIV_FILENAME +#define RSA_PRIV_FILENAME "/etc/dropbear/dropbear_rsa_host_key" +#endif + +/* 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 */ +#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 + +/* Enable Authentication Agent Forwarding */ +#define ENABLE_AGENTFWD + +/* Encryption - at least one required. + * RFC Draft requires 3DES, and recommends Blowfish, AES128 & Twofish128 */ +#define DROPBEAR_AES128_CBC +#define DROPBEAR_BLOWFISH_CBC +#define DROPBEAR_TWOFISH128_CBC +#define DROPBEAR_3DES_CBC + +/* Integrity - at least one required. + * RFC Draft requires sha1-hmac, and recommends md5-hmac. + * + * Note: there's no point disabling sha1 to save space, since it's used in the + * for the random number generator and public-key cryptography anyway. + * Disabling it here will just stop it from being used as the integrity portion + * of the ssh protocol. + * + * These are also used for key fingerprints in logs (when pubkey auth is used), + * MD5 fingerprints are printed if available, however SHA1 fingerprints will be + * generated otherwise. This isn't exactly optimal, although SHA1 fingerprints + * are not too hard to create from pubkeys if required. */ +#define DROPBEAR_SHA1_HMAC +#define DROPBEAR_MD5_HMAC + +/* Hostkey/public key algorithms - at least one required, these are used + * for hostkey as well as for verifying signatures with pubkey auth. + * Removing either of these won't save very much space. + * SSH2 RFC Draft requires dss, recommends rsa */ +#define DROPBEAR_RSA +#define DROPBEAR_DSS + +/* Define DSS_PROTOK to use PuTTY's method of generating the value k for dss, + * rather than just from the random byte source. Undefining this will save you + * ~4k in binary size with static uclibc, but your DSS hostkey could be exposed + * if the random number source isn't good. In general this isn't required */ +/* #define DSS_PROTOK */ + +/* Whether to do reverse DNS lookups. This is advisable, though will add + * code size with gethostbyname() etc, so for very small environments where + * you are statically linking, you might want to undefine this */ +#define DO_HOST_LOOKUP + +/* Whether to print the message of the day (MOTD). This doesn't add much code + * size */ +#define DO_MOTD + +/* The MOTD file path */ +#ifndef MOTD_FILENAME +#define MOTD_FILENAME "/etc/motd" +#endif + +/* Authentication types to enable, at least one required. + RFC Draft requires pubkey auth, and recommends password */ +#define DROPBEAR_PASSWORD_AUTH +#define DROPBEAR_PUBKEY_AUTH + +/* Random device to use - you must specify _one only_. + * DEV_RANDOM 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 + * data */ +#define DROPBEAR_DEV_URANDOM /* use /dev/urandom */ + +/*#undef DROPBEAR_PRNGD */ /* use prngd socket - you must manually set up prngd + to produce output */ +#ifndef DROPBEAR_PRNGD_SOCKET +#define DROPBEAR_PRNGD_SOCKET "/var/run/dropbear-rng" +#endif + +/* Specify the number of clients we will allow to be connected but + * not yet authenticated. After this limit, connections are rejected */ +#ifndef MAX_UNAUTH_CLIENTS +#define MAX_UNAUTH_CLIENTS 30 +#endif + +/* Maximum number of failed authentication tries */ +#ifndef MAX_AUTH_TRIES +#define MAX_AUTH_TRIES 10 +#endif + +/* The file to store the daemon's process ID, for shutdown scripts etc */ +#ifndef DROPBEAR_PIDFILE +#define DROPBEAR_PIDFILE "/var/run/dropbear.pid" +#endif + +/* The command to invoke for xauth when using X11 forwarding. + * "-q" for quiet */ +#ifndef XAUTH_COMMAND +#define XAUTH_COMMAND "/usr/X11R6/bin/xauth -q" +#endif + +/* if you want to enable running an sftp server (such as the one included with + * OpenSSH), set the path below. If the path isn't defined, sftp will not + * be enabled */ +#ifndef SFTPSERVER_PATH +#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" + +/* Multi-purpose binary configuration - if you want to make the combined + * binary, first define DROPBEAR_MULTI, and then define which of the three + * components you want. You should then compile Dropbear with + * "make clean; make dropbearmulti". You'll need to install the binary + * manually, see MULTI for details */ + +/* #define DROPBEAR_MULTI */ + +/* The three multi binaries: dropbear, dropbearkey, dropbearconvert + * Comment out these if you don't want some of them */ +#define DBMULTI_DROPBEAR +#define DBMULTI_KEY +#define DBMULTI_CONVERT + + +/******************************************************************* + * You shouldn't edit below here unless you know you need to. + *******************************************************************/ + +#ifndef DROPBEAR_VERSION +#define DROPBEAR_VERSION "0.41" +#endif + +#define LOCAL_IDENT "SSH-2.0-dropbear_" DROPBEAR_VERSION +#define PROGNAME "dropbear" + +/* Spec recommends after one hour or 1 gigabyte of data. One hour + * is a bit too verbose, so we try 8 hours */ +#ifndef KEX_REKEY_TIMEOUT +#define KEX_REKEY_TIMEOUT (3600 * 8) +#endif +#ifndef KEX_REKEY_DATA +#define KEX_REKEY_DATA (1<<30) /* 2^30 == 1GB, this value must be < INT_MAX */ +#endif +/* Close connections to clients which haven't authorised after AUTH_TIMEOUT */ +#ifndef AUTH_TIMEOUT +#define AUTH_TIMEOUT 300 /* we choose 5 minutes */ +#endif + +/* Minimum key sizes for DSS and RSA */ +#ifndef MIN_DSS_KEYLEN +#define MIN_DSS_KEYLEN 512 +#endif +#ifndef MIN_RSA_KEYLEN +#define MIN_RSA_KEYLEN 512 +#endif + +#define MAX_BANNER_SIZE 2000 /* this is 25*80 chars, any more is foolish */ + +#define DEV_URANDOM "/dev/urandom" + +/* the number of NAME=VALUE pairs to malloc for environ, if we don't have + * the clearenv() function */ +#define ENV_SIZE 100 + +#define MAX_CMD_LEN 1024 /* max length of a command */ +#define MAX_TERM_LEN 200 /* max length of TERM name */ + +#define MAX_HOST_LEN 254 /* max hostname len for tcp fwding */ +#define MAX_IP_LEN 15 /* strlen("255.255.255.255") == 15 */ + +#define DROPBEAR_MAX_PORTS 10 /* max number of ports which can be specified, + ipv4 and ipv6 don't count twice */ + +#define _PATH_TTY "/dev/tty" + +/* Timeouts in seconds */ +#define SELECT_TIMEOUT 20 + +/* success/failure defines */ +#define DROPBEAR_SUCCESS 0 +#define DROPBEAR_FAILURE -1 + +/* various algorithm identifiers */ +#define DROPBEAR_KEX_DH_GROUP1 0 + +#define DROPBEAR_SIGNKEY_ANY 0 +#define DROPBEAR_SIGNKEY_RSA 1 +#define DROPBEAR_SIGNKEY_DSS 2 + +#define DROPBEAR_COMP_NONE 0 +#define DROPBEAR_COMP_ZLIB 1 + +/* Required for pubkey auth */ +#ifdef DROPBEAR_PUBKEY_AUTH +#define DROPBEAR_SIGNKEY_VERIFY +#endif + +/* SHA1 is 20 bytes == 160 bits */ +#define SHA1_HASH_SIZE 20 +/* SHA512 is 64 bytes == 512 bits */ +#define SHA512_HASH_SIZE 64 +/* MD5 is 16 bytes = 128 bits */ +#define MD5_HASH_SIZE 16 + +/* largest of MD5 and SHA1 */ +#define MAX_MAC_LEN SHA1_HASH_SIZE + + +#define MAX_KEY_LEN 24 /* 3DES requires a 24 byte key */ +#define MAX_IV_LEN 20 /* must be same as max blocksize, + and >= SHA1_HASH_SIZE */ +#define MAX_MAC_KEY 20 + +#define MAX_NAME_LEN 64 /* maximum length of a protocol name, isn't + explicitly specified for all protocols (just + for algos) but seems valid */ + +#define MAX_PROPOSED_ALGO 20 + +/* size/count limits */ +#define MAX_LISTEN_ADDR 10 + +#define MAX_PACKET_LEN 35000 +#define MIN_PACKET_LEN 16 +#define MAX_PAYLOAD_LEN 32768 + +#define MAX_TRANS_PAYLOAD_LEN 32768 +#define MAX_TRANS_PACKET_LEN (MAX_TRANS_PAYLOAD_LEN+50) + +#define MAX_TRANS_WINDOW 500000000 /* 500MB is sufficient, stopping overflow */ +#define MAX_TRANS_WIN_INCR 500000000 /* overflow prevention */ + +#define MAX_STRING_LEN 1400 /* ~= MAX_PROPOSED_ALGO * MAX_NAME_LEN, also + is the max length for a password etc */ + +#ifndef ENABLE_X11FWD +#define DISABLE_X11FWD +#endif + +#ifndef ENABLE_AGENTFWD +#define DISABLE_AGENTFWD +#endif + +#ifndef ENABLE_LOCALTCPFWD +#define DISABLE_LOCALTCPFWD +#endif + +#ifndef ENABLE_REMOTETCPFWD +#define DISABLE_REMOTETCPFWD +#endif + +/* We use dropbear_client and dropbear_server as shortcuts to avoid redundant + * code, if we're just compiling as client or server */ +#if defined(DROPBEAR_SERVER) && defined(DROPBEAR_CLIENT) + +#define IS_DROPBEAR_SERVER (ses.isserver == 1) +#define IS_DROPBEAR_CLIENT (ses.isserver == 0) + +#elif defined(DROPBEAR_SERVER) + +#define IS_DROPBEAR_SERVER 1 +#define IS_DROPBEAR_CLIENT 0 + +#elif defined(DROPBEAR_CLIENT) + +#define IS_DROPBEAR_SERVER 0 +#define IS_DROPBEAR_CLIENT 1 + +#else +#error You must compiled with either DROPBEAR_CLIENT or DROPBEAR_SERVER selected +#endif + +#endif /* _OPTIONS_H_ */ diff --git a/packet.h b/packet.h new file mode 100644 index 0000000..d39c9bc --- /dev/null +++ b/packet.h @@ -0,0 +1,44 @@ +/* + * 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 _PACKET_H_ + +#define _PACKET_H_ + +#include "includes.h" + +void write_packet(); +void read_packet(); +void decrypt_packet(); +void encrypt_packet(); +void recv_unimplemented(); + +void svr_process_packet(); + +#define PACKET_PADDING_OFF 4 +#define PACKET_PAYLOAD_OFF 5 + +#define INIT_READBUF 200 + +#endif /* _PACKET_H_ */ diff --git a/progressmeter.c b/progressmeter.c new file mode 100644 index 0000000..39e8a77 --- /dev/null +++ b/progressmeter.c @@ -0,0 +1,267 @@ +#ifdef PROGRESS_METER +/* + * Copyright (c) 2003 Nils Nordman. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "includes.h" +/*RCSID("$OpenBSD: progressmeter.c,v 1.15 2003/08/31 12:14:22 markus Exp $");*/ + +#include "progressmeter.h" +#include "atomicio.h" +#include "scpmisc.h" + +#define DEFAULT_WINSIZE 80 +#define MAX_WINSIZE 512 +#define PADDING 1 /* padding between the progress indicators */ +#define UPDATE_INTERVAL 1 /* update the progress meter every second */ +#define STALL_TIME 5 /* we're stalled after this many seconds */ + +/* determines whether we can output to the terminal */ +static int can_output(void); + +/* formats and inserts the specified size into the given buffer */ +static void format_size(char *, int, off_t); +static void format_rate(char *, int, off_t); + +/* updates the progressmeter to reflect the current state of the transfer */ +void refresh_progress_meter(void); + +/* signal handler for updating the progress meter */ +static void update_progress_meter(int); + +static time_t start; /* start progress */ +static time_t last_update; /* last progress update */ +static char *file; /* name of the file being transferred */ +static off_t end_pos; /* ending position of transfer */ +static off_t cur_pos; /* transfer position as of last refresh */ +static volatile off_t *counter; /* progress counter */ +static long stalled; /* how long we have been stalled */ +static int bytes_per_second; /* current speed in bytes per second */ +static int win_size; /* terminal window size */ + +/* units for format_size */ +static const char unit[] = " KMGT"; + +static int +can_output(void) +{ + return (getpgrp() == tcgetpgrp(STDOUT_FILENO)); +} + +static void +format_rate(char *buf, int size, off_t bytes) +{ + int i; + + bytes *= 100; + for (i = 0; bytes >= 100*1000 && unit[i] != 'T'; i++) + bytes = (bytes + 512) / 1024; + if (i == 0) { + i++; + bytes = (bytes + 512) / 1024; + } + snprintf(buf, size, "%3lld.%1lld%c%s", + (int64_t) bytes / 100, + (int64_t) (bytes + 5) / 10 % 10, + unit[i], + i ? "B" : " "); +} + +static void +format_size(char *buf, int size, off_t bytes) +{ + int i; + + for (i = 0; bytes >= 10000 && unit[i] != 'T'; i++) + bytes = (bytes + 512) / 1024; + snprintf(buf, size, "%4lld%c%s", + (int64_t) bytes, + unit[i], + i ? "B" : " "); +} + +void +refresh_progress_meter(void) +{ + char buf[MAX_WINSIZE + 1]; + time_t now; + off_t transferred; + double elapsed; + int percent; + int bytes_left; + int cur_speed; + int hours, minutes, seconds; + int i, len; + int file_len; + + transferred = *counter - cur_pos; + cur_pos = *counter; + now = time(NULL); + bytes_left = end_pos - cur_pos; + + if (bytes_left > 0) + elapsed = now - last_update; + else + elapsed = now - start; + + /* calculate speed */ + if (elapsed != 0) + cur_speed = (transferred / elapsed); + else + cur_speed = 0; + +#define AGE_FACTOR 0.9 + if (bytes_per_second != 0) { + bytes_per_second = (bytes_per_second * AGE_FACTOR) + + (cur_speed * (1.0 - AGE_FACTOR)); + } else + bytes_per_second = cur_speed; + + /* filename */ + buf[0] = '\0'; + file_len = win_size - 35; + if (file_len > 0) { + len = snprintf(buf, file_len + 1, "\r%s", file); + if (len < 0) + len = 0; + for (i = len; i < file_len; i++ ) + buf[i] = ' '; + buf[file_len] = '\0'; + } + + /* percent of transfer done */ + if (end_pos != 0) + percent = ((float)cur_pos / end_pos) * 100; + else + percent = 100; + snprintf(buf + strlen(buf), win_size - strlen(buf), + " %3d%% ", percent); + + /* amount transferred */ + format_size(buf + strlen(buf), win_size - strlen(buf), + cur_pos); + strlcat(buf, " ", win_size); + + /* bandwidth usage */ + format_rate(buf + strlen(buf), win_size - strlen(buf), + bytes_per_second); + strlcat(buf, "/s ", win_size); + + /* ETA */ + if (!transferred) + stalled += elapsed; + else + stalled = 0; + + if (stalled >= STALL_TIME) + strlcat(buf, "- stalled -", win_size); + else if (bytes_per_second == 0 && bytes_left) + strlcat(buf, " --:-- ETA", win_size); + else { + if (bytes_left > 0) + seconds = bytes_left / bytes_per_second; + else + seconds = elapsed; + + hours = seconds / 3600; + seconds -= hours * 3600; + minutes = seconds / 60; + seconds -= minutes * 60; + + if (hours != 0) + snprintf(buf + strlen(buf), win_size - strlen(buf), + "%d:%02d:%02d", hours, minutes, seconds); + else + snprintf(buf + strlen(buf), win_size - strlen(buf), + " %02d:%02d", minutes, seconds); + + if (bytes_left > 0) + strlcat(buf, " ETA", win_size); + else + strlcat(buf, " ", win_size); + } + + atomicio(vwrite, STDOUT_FILENO, buf, win_size); + last_update = now; +} + +static void +update_progress_meter(int ignore) +{ + int save_errno; + + save_errno = errno; + + if (can_output()) + refresh_progress_meter(); + + signal(SIGALRM, update_progress_meter); + alarm(UPDATE_INTERVAL); + errno = save_errno; +} + +void +start_progress_meter(char *f, off_t filesize, off_t *stat) +{ + struct winsize winsize; + + start = last_update = time(NULL); + file = f; + end_pos = filesize; + cur_pos = 0; + counter = stat; + stalled = 0; + bytes_per_second = 0; + + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) != -1 && + winsize.ws_col != 0) { + if (winsize.ws_col > MAX_WINSIZE) + win_size = MAX_WINSIZE; + else + win_size = winsize.ws_col; + } else + win_size = DEFAULT_WINSIZE; + win_size += 1; /* trailing \0 */ + + if (can_output()) + refresh_progress_meter(); + + signal(SIGALRM, update_progress_meter); + alarm(UPDATE_INTERVAL); +} + +void +stop_progress_meter(void) +{ + alarm(0); + + if (!can_output()) + return; + + /* Ensure we complete the progress */ + if (cur_pos != end_pos) + refresh_progress_meter(); + + atomicio(vwrite, STDOUT_FILENO, "\n", 1); +} +#endif /* PROGRESS_METER */ diff --git a/progressmeter.h b/progressmeter.h new file mode 100644 index 0000000..bfb9a0b --- /dev/null +++ b/progressmeter.h @@ -0,0 +1,27 @@ +/* $OpenBSD: progressmeter.h,v 1.1 2003/01/10 08:19:07 fgsch Exp $ */ +/* + * Copyright (c) 2002 Nils Nordman. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +void start_progress_meter(char *, off_t, off_t *); +void stop_progress_meter(void); diff --git a/queue.c b/queue.c new file mode 100644 index 0000000..a1352dc --- /dev/null +++ b/queue.c @@ -0,0 +1,89 @@ +/* + * 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. */ + +#include "includes.h" +#include "dbutil.h" +#include "queue.h" + +void initqueue(struct Queue* queue) { + + queue->head = NULL; + queue->tail = NULL; + queue->count = 0; +} + +int isempty(struct Queue* queue) { + + return (queue->head == NULL); +} + +void* dequeue(struct Queue* queue) { + + void* ret; + struct Link* oldhead; + assert(!isempty(queue)); + + ret = queue->head->item; + oldhead = queue->head; + + if (oldhead->link != NULL) { + queue->head = oldhead->link; + } else { + queue->head = NULL; + queue->tail = NULL; + TRACE(("empty queue dequeing")); + } + + m_free(oldhead); + queue->count--; + return ret; +} + +void *examine(struct Queue* queue) { + + assert(!isempty(queue)); + return queue->head->item; +} + +void enqueue(struct Queue* queue, void* item) { + + struct Link* newlink; + + TRACE(("enter enqueue")); + newlink = (struct Link*)m_malloc(sizeof(struct Link)); + + newlink->item = item; + newlink->link = NULL; + + if (queue->tail != NULL) { + queue->tail->link = newlink; + } + queue->tail = newlink; + + if (queue->head == NULL) { + queue->head = newlink; + } + queue->count++; + TRACE(("leave enqueue")); +} diff --git a/queue.h b/queue.h new file mode 100644 index 0000000..80fbb9d --- /dev/null +++ b/queue.h @@ -0,0 +1,49 @@ +/* + * 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 _QUEUE_H_ +#define _QUEUE_H_ + +struct Link { + + void* item; + struct Link* link; + +}; + +struct Queue { + + struct Link* head; + struct Link* tail; + unsigned int count; /* safety value */ + +}; + +void initqueue(struct Queue* queue); +int isempty(struct Queue* queue); +void* dequeue(struct Queue* queue); +void *examine(struct Queue* queue); +void enqueue(struct Queue* queue, void* item); + +#endif diff --git a/random.c b/random.c new file mode 100644 index 0000000..725b29c --- /dev/null +++ b/random.c @@ -0,0 +1,178 @@ +/* + * 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. */ + +#include "includes.h" +#include "buffer.h" +#include "dbutil.h" + +int donerandinit = 0; + +/* this is used to generate unique output from the same hashpool */ +unsigned int counter = 0; +#define MAX_COUNTER 1000000/* the max value for the counter, so it won't loop */ + +unsigned char hashpool[SHA1_HASH_SIZE]; + +#define INIT_SEED_SIZE 32 /* 256 bits */ + +static void readrand(unsigned char* buf, unsigned int buflen); + +/* The basic setup is we read some data from DEV_URANDOM or PRNGD and hash it + * into hashpool. To read data, we hash together current hashpool contents, + * and a counter. We feed more data in by hashing the current pool and new + * data into the pool. + * + * It is important to ensure that counter doesn't wrap around before we + * feed in new entropy. + * + */ + +static void readrand(unsigned char* buf, unsigned int buflen) { + + int readfd; + unsigned int readpos; + int readlen; +#ifdef DROPBEAR_EGD + struct sockaddr_un egdsock; + char egdcmd[2]; +#endif + +#ifdef DROPBEAR_DEV_URANDOM + readfd = open(DEV_URANDOM, O_RDONLY); + if (!readfd) { + dropbear_exit("couldn't open random device"); + } +#endif + +#ifdef DROPBEAR_EGD + memset((void*)&egdsock, 0x0, sizeof(egdsock)); + egdsock.sun_family = AF_UNIX; + strlcpy(egdsock.sun_path, DROPBEAR_EGD_SOCKET, + sizeof(egdsock.sun_path)); + + if ((readfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { + dropbear_exit("couldn't open random device"); + } + /* todo - try various common locations */ + if (connect(readfd, (struct sockaddr*)&egdsock, + sizeof(struct sockaddr_un)) < 0) { + dropbear_exit("couldn't open random device"); + } + + if (buflen > 255) + dropbear_exit("can't request more than 255 bytes from egd"); + egdcmd[0] = 0x02; /* blocking read */ + egdcmd[1] = (unsigned char)buflen; + if (write(readfd, egdcmd, 2) < 0) + dropbear_exit("can't send command to egd"); +#endif + + /* read the actual random data */ + readpos = 0; + do { + readlen = read(readfd, &buf[readpos], buflen - readpos); + if (readlen <= 0) { + if (readlen < 0 && errno == EINTR) { + continue; + } + dropbear_exit("error reading random source"); + } + readpos += readlen; + } while (readpos < buflen); + + close (readfd); +} + +/* initialise the prng from /dev/urandom or prngd */ +void seedrandom() { + + unsigned char readbuf[INIT_SEED_SIZE]; + + hash_state hs; + + /* initialise so compilers will be happy about hashing it */ + if (!donerandinit) { + m_burn(hashpool, sizeof(hashpool)); + } + + /* get the seed data */ + readrand(readbuf, sizeof(readbuf)); + + /* hash in the new seed data */ + sha1_init(&hs); + sha1_process(&hs, (void*)hashpool, sizeof(hashpool)); + sha1_process(&hs, (void*)readbuf, sizeof(readbuf)); + sha1_done(&hs, hashpool); + + counter = 0; + donerandinit = 1; +} + +/* return len bytes of pseudo-random data */ +void genrandom(unsigned char* buf, unsigned int len) { + + hash_state hs; + unsigned char hash[SHA1_HASH_SIZE]; + unsigned int copylen; + + if (!donerandinit) { + dropbear_exit("seedrandom not done"); + } + + while (len > 0) { + sha1_init(&hs); + sha1_process(&hs, (void*)hashpool, sizeof(hashpool)); + sha1_process(&hs, (void*)&counter, sizeof(counter)); + sha1_done(&hs, hash); + + counter++; + if (counter > MAX_COUNTER) { + seedrandom(); + } + + copylen = MIN(len, SHA1_HASH_SIZE); + memcpy(buf, hash, copylen); + len -= copylen; + buf += copylen; + } + m_burn(hash, sizeof(hash)); +} + +/* Adds entropy to the PRNG state. As long as the hash is strong, then we + * don't need to worry about entropy being added "diluting" the current + * state - it should only make it stronger. */ +void addrandom(unsigned char* buf, unsigned int len) { + + hash_state hs; + if (!donerandinit) { + dropbear_exit("seedrandom not done"); + } + + sha1_init(&hs); + sha1_process(&hs, (void*)buf, len); + sha1_process(&hs, (void*)hashpool, sizeof(hashpool)); + sha1_done(&hs, hashpool); + counter = 0; + +} diff --git a/random.h b/random.h new file mode 100644 index 0000000..91aa342 --- /dev/null +++ b/random.h @@ -0,0 +1,32 @@ +/* + * 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 _RANDOM_H_ +#define _RANDOM_H_ + +void seedrandom(); +void genrandom(unsigned char* buf, int len); +void addrandom(unsigned char* buf, int len); + +#endif /* _RANDOM_H_ */ diff --git a/remotetcpfwd.c b/remotetcpfwd.c new file mode 100644 index 0000000..c58b820 --- /dev/null +++ b/remotetcpfwd.c @@ -0,0 +1,301 @@ +#include "includes.h" +#include "ssh.h" +#include "remotetcpfwd.h" +#include "dbutil.h" +#include "session.h" +#include "buffer.h" +#include "packet.h" +#include "tcpfwd.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 newlistener(unsigned char* bindaddr, unsigned int port); +static void acceptremote(struct TCPListener *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 (ses.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 void acceptremote(struct TCPListener *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, CHANNEL_ID_TCPFORWARDED, + "forwarded-tcpip") == 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(); + } +} + +static void cleanupremote(struct TCPListener *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 TCPListener * 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; + } + + /* XXX matt - server change + if (ses.authstate.pw->pw_uid != 0 + && port < IPPORT_RESERVED) { + TRACE(("can't assign port < 1024 for non-root")); + goto out; + } + */ + + ret = newlistener(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 newlistener(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; + int ret = DROPBEAR_FAILURE; + + TRACE(("enter newlistener")); + + /* 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 newlistener: 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; + + ret = new_fwd(sock, CHANNEL_ID_TCPFORWARDED, tcpinfo, + acceptremote, cleanupremote); + + if (ret == DROPBEAR_FAILURE) { + m_free(tcpinfo); + } + +done: + if (res) { + freeaddrinfo(res); + } + + TRACE(("leave newlistener")); + return ret; +} + +#endif /* DISABLE_REMOTETCPFWD */ diff --git a/remotetcpfwd.h b/remotetcpfwd.h new file mode 100644 index 0000000..64dbed3 --- /dev/null +++ b/remotetcpfwd.h @@ -0,0 +1,6 @@ +#ifndef _REMOTETCPFWD_H +#define _REMOTETCPFWD_H + +void recv_msg_global_request_remotetcp(); + +#endif /* _REMOTETCPFWD_H */ diff --git a/rsa.c b/rsa.c new file mode 100644 index 0000000..936b2c8 --- /dev/null +++ b/rsa.c @@ -0,0 +1,362 @@ +/* + * 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. */ + +/* Perform RSA operations on data, including reading keys, signing and + * verification. + * + * The format is specified in rfc2437, Applied Cryptography or The Handbook of + * Applied Cryptography detail the general algorithm. */ + +#include "includes.h" +#include "dbutil.h" +#include "bignum.h" +#include "rsa.h" +#include "buffer.h" +#include "ssh.h" +#include "random.h" + +#ifdef DROPBEAR_RSA + +static mp_int * rsa_pad_em(rsa_key * key, + const unsigned char * data, unsigned int len); + +/* Load a public rsa key from a buffer, initialising the values. + * The key will have the same format as buf_put_rsa_key. + * These should be freed with rsa_key_free. + * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +int buf_get_rsa_pub_key(buffer* buf, rsa_key *key) { + + TRACE(("enter buf_get_rsa_pub_key")); + assert(key != NULL); + key->e = m_malloc(sizeof(mp_int)); + key->n = m_malloc(sizeof(mp_int)); + m_mp_init_multi(key->e, key->n, NULL); + key->d = NULL; + key->p = NULL; + key->q = NULL; + + buf_incrpos(buf, 4+SSH_SIGNKEY_RSA_LEN); /* int + "ssh-rsa" */ + + if (buf_getmpint(buf, key->e) == DROPBEAR_FAILURE + || buf_getmpint(buf, key->n) == DROPBEAR_FAILURE) { + TRACE(("leave buf_get_rsa_pub_key: failure")); + return DROPBEAR_FAILURE; + } + + if (mp_count_bits(key->n) < MIN_RSA_KEYLEN) { + dropbear_log(LOG_WARNING, "rsa key too short"); + return DROPBEAR_FAILURE; + } + + TRACE(("leave buf_get_rsa_pub_key: success")); + return DROPBEAR_SUCCESS; + +} + +/* Same as buf_get_rsa_pub_key, but reads a private "x" key at the end. + * Loads a private rsa key from a buffer + * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +int buf_get_rsa_priv_key(buffer* buf, rsa_key *key) { + + assert(key != NULL); + + TRACE(("enter buf_get_rsa_priv_key")); + + if (buf_get_rsa_pub_key(buf, key) == DROPBEAR_FAILURE) { + TRACE(("leave buf_get_rsa_priv_key: pub: ret == DROPBEAR_FAILURE")); + return DROPBEAR_FAILURE; + } + + key->d = m_malloc(sizeof(mp_int)); + m_mp_init(key->d); + if (buf_getmpint(buf, key->d) == DROPBEAR_FAILURE) { + TRACE(("leave buf_get_rsa_priv_key: d: ret == DROPBEAR_FAILURE")); + return DROPBEAR_FAILURE; + } + + /* old Dropbear private keys didn't keep p and q, so we will ignore them*/ + if (buf->pos == buf->len) { + key->p = NULL; + key->q = NULL; + } else { + key->p = m_malloc(sizeof(mp_int)); + key->q = m_malloc(sizeof(mp_int)); + m_mp_init_multi(key->p, key->q, NULL); + + if (buf_getmpint(buf, key->p) == DROPBEAR_FAILURE) { + TRACE(("leave buf_get_rsa_priv_key: p: ret == DROPBEAR_FAILURE")); + return DROPBEAR_FAILURE; + } + + if (buf_getmpint(buf, key->q) == DROPBEAR_FAILURE) { + TRACE(("leave buf_get_rsa_priv_key: q: ret == DROPBEAR_FAILURE")); + return DROPBEAR_FAILURE; + } + } + + TRACE(("leave buf_get_rsa_priv_key")); + return DROPBEAR_SUCCESS; +} + + +/* Clear and free the memory used by a public or private key */ +void rsa_key_free(rsa_key *key) { + + TRACE(("enter rsa_key_free")); + + if (key == NULL) { + TRACE(("leave rsa_key_free: key == NULL")); + return; + } + if (key->d) { + mp_clear(key->d); + m_free(key->d); + } + if (key->e) { + mp_clear(key->e); + m_free(key->e); + } + if (key->n) { + mp_clear(key->n); + m_free(key->n); + } + if (key->p) { + mp_clear(key->p); + m_free(key->p); + } + if (key->q) { + mp_clear(key->q); + m_free(key->q); + } + m_free(key); + TRACE(("leave rsa_key_free")); +} + +/* Put the public rsa key into the buffer in the required format: + * + * string "ssh-rsa" + * mp_int e + * mp_int n + */ +void buf_put_rsa_pub_key(buffer* buf, rsa_key *key) { + + TRACE(("enter buf_put_rsa_pub_key")); + assert(key != NULL); + + buf_putstring(buf, SSH_SIGNKEY_RSA, SSH_SIGNKEY_RSA_LEN); + buf_putmpint(buf, key->e); + buf_putmpint(buf, key->n); + + TRACE(("leave buf_put_rsa_pub_key")); + +} + +/* Same as buf_put_rsa_pub_key, but with the private "x" key appended */ +void buf_put_rsa_priv_key(buffer* buf, rsa_key *key) { + + TRACE(("enter buf_put_rsa_priv_key")); + + assert(key != NULL); + buf_put_rsa_pub_key(buf, key); + buf_putmpint(buf, key->d); + + /* new versions have p and q, old versions don't */ + if (key->p) { + buf_putmpint(buf, key->p); + } + if (key->q) { + buf_putmpint(buf, key->q); + } + + + TRACE(("leave buf_put_rsa_priv_key")); + +} + +#ifdef DROPBEAR_SIGNKEY_VERIFY +/* Verify a signature in buf, made on data by the key given. + * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +int buf_rsa_verify(buffer * buf, rsa_key *key, const unsigned char* data, + unsigned int len) { + + unsigned int slen; + mp_int rsa_s, rsa_mdash; + mp_int *rsa_em = NULL; + int ret = DROPBEAR_FAILURE; + + assert(key != NULL); + + m_mp_init_multi(&rsa_mdash, &rsa_s, NULL); + + slen = buf_getint(buf); + if (slen != (unsigned int)mp_unsigned_bin_size(key->n)) { + TRACE(("bad size")); + goto out; + } + + if (mp_read_unsigned_bin(&rsa_s, buf_getptr(buf, buf->len - buf->pos), + buf->len - buf->pos) != MP_OKAY) { + goto out; + } + + /* check that s <= n-1 */ + if (mp_cmp(&rsa_s, key->n) != MP_LT) { + TRACE(("s > n-1")); + goto out; + } + + /* create the magic PKCS padded value */ + rsa_em = rsa_pad_em(key, data, len); + + if (mp_exptmod(&rsa_s, key->e, key->n, &rsa_mdash) != MP_OKAY) { + goto out; + } + + if (mp_cmp(rsa_em, &rsa_mdash) == MP_EQ) { + /* signature is valid */ + ret = DROPBEAR_SUCCESS; + } + +out: + mp_clear_multi(rsa_em, &rsa_mdash, &rsa_s, NULL); + m_free(rsa_em); + return ret; + +} +#endif /* DROPBEAR_SIGNKEY_VERIFY */ + +/* Sign the data presented with key, writing the signature contents + * to the buffer */ +void buf_put_rsa_sign(buffer* buf, rsa_key *key, const unsigned char* data, + unsigned int len) { + + unsigned int nsize, ssize; + unsigned int i; + mp_int rsa_s; + mp_int *rsa_em; + + TRACE(("enter buf_put_rsa_sign")); + assert(key != NULL); + + rsa_em = rsa_pad_em(key, data, len); + + /* the actual signing of the padded data */ + m_mp_init(&rsa_s); + /* s = em^d mod n */ + if (mp_exptmod(rsa_em, key->d, key->n, &rsa_s) != MP_OKAY) { + dropbear_exit("rsa error"); + } + mp_clear(rsa_em); + m_free(rsa_em); + + /* create the signature to return */ + buf_putstring(buf, SSH_SIGNKEY_RSA, SSH_SIGNKEY_RSA_LEN); + + nsize = mp_unsigned_bin_size(key->n); + + /* string rsa_signature_blob length */ + buf_putint(buf, nsize); + /* pad out s to same length as n */ + ssize = mp_unsigned_bin_size(&rsa_s); + assert(ssize <= nsize); + for (i = 0; i < nsize-ssize; i++) { + buf_putbyte(buf, 0x00); + } + + if (mp_to_unsigned_bin(&rsa_s, buf_getwriteptr(buf, ssize)) != MP_OKAY) { + dropbear_exit("rsa error"); + } + buf_incrwritepos(buf, ssize); + mp_clear(&rsa_s); + +#if defined(DEBUG_RSA) && defined(DEBUG_TRACE) + printhex(buf->data, buf->len); +#endif + + + TRACE(("leave buf_put_rsa_sign")); +} + +/* Creates the message value as expected by PKCS, see rfc2437 etc */ +/* format to be padded to is: + * EM = 01 | FF* | 00 | prefix | hash + * + * where FF is repeated enough times to make EM one byte + * shorter than the size of key->n + * + * prefix is the ASN1 designator prefix, + * hex 30 21 30 09 06 05 2B 0E 03 02 1A 05 00 04 14 + */ +static mp_int * rsa_pad_em(rsa_key * key, + const unsigned char * data, unsigned int len) { + + /* ASN1 designator (including the 0x00 preceding) */ + const char rsa_asn1_magic[] = + {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; + hash_state hs; + unsigned int nsize; + mp_int * rsa_em; + + assert(key != NULL); + assert(data != NULL); + nsize = mp_unsigned_bin_size(key->n); + + rsa_EM = buf_new(nsize-1); + /* type byte */ + buf_putbyte(rsa_EM, 0x01); + /* Padding with 0xFF bytes */ + while(rsa_EM->pos != rsa_EM->size - RSA_ASN1_MAGIC_LEN - SHA1_HASH_SIZE) { + buf_putbyte(rsa_EM, 0xff); + } + /* Magic ASN1 stuff */ + memcpy(buf_getwriteptr(rsa_EM, RSA_ASN1_MAGIC_LEN), + rsa_asn1_magic, RSA_ASN1_MAGIC_LEN); + buf_incrwritepos(rsa_EM, RSA_ASN1_MAGIC_LEN); + + /* The hash of the data */ + sha1_init(&hs); + sha1_process(&hs, data, len); + sha1_done(&hs, buf_getwriteptr(rsa_EM, SHA1_HASH_SIZE)); + buf_incrwritepos(rsa_EM, SHA1_HASH_SIZE); + + assert(rsa_EM->pos == rsa_EM->size); + + /* Create the mp_int from the encoded bytes */ + buf_setpos(rsa_EM, 0); + rsa_em = (mp_int*)m_malloc(sizeof(mp_int)); + m_mp_init(rsa_em); + if (mp_read_unsigned_bin(rsa_em, buf_getptr(rsa_EM, rsa_EM->size), + rsa_EM->size) != MP_OKAY) { + dropbear_exit("rsa error"); + } + buf_free(rsa_EM); + + return rsa_em; + +} + +#endif /* DROPBEAR_RSA */ diff --git a/rsa.h b/rsa.h new file mode 100644 index 0000000..545ba9b --- /dev/null +++ b/rsa.h @@ -0,0 +1,61 @@ +/* + * 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 _RSA_H_ +#define _RSA_H_ + +#include "includes.h" +#include "buffer.h" + +#ifdef DROPBEAR_RSA + +#define RSA_SIGNATURE_SIZE 4+7+4+40 + +struct RSA_key { + + mp_int* n; + mp_int* e; + mp_int* d; + mp_int* p; + mp_int* q; + +}; + +typedef struct RSA_key rsa_key; + +void buf_put_rsa_sign(buffer* buf, rsa_key *key, const unsigned char* data, + unsigned int len); +#ifdef DROPBEAR_SIGNKEY_VERIFY +int buf_rsa_verify(buffer * buf, rsa_key *key, const unsigned char* data, + unsigned int len); +#endif +int buf_get_rsa_pub_key(buffer* buf, rsa_key *key); +int buf_get_rsa_priv_key(buffer* buf, rsa_key *key); +void buf_put_rsa_pub_key(buffer* buf, rsa_key *key); +void buf_put_rsa_priv_key(buffer* buf, rsa_key *key); +void rsa_key_free(rsa_key *key); + +#endif /* DROPBEAR_RSA */ + +#endif /* _RSA_H_ */ diff --git a/runopts.h b/runopts.h new file mode 100644 index 0000000..cdf3af9 --- /dev/null +++ b/runopts.h @@ -0,0 +1,72 @@ +/* + * 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 _RUNOPTS_H_ +#define _RUNOPTS_H_ + +#include "includes.h" +#include "signkey.h" +#include "buffer.h" + +struct SvrRunOpts { + + char * rsakeyfile; + char * dsskeyfile; + char * bannerfile; + int forkbg; + + /* ports is an array of the portcount listening ports */ + uint16_t *ports; + unsigned int portcount; + + /* Flags indicating whether to use ipv4 and ipv6 */ + /* not used yet + int ipv4; + int ipv6; + */ + +#ifdef DO_MOTD + /* whether to print the MOTD */ + int domotd; +#endif + + int norootlogin; + + int noauthpass; + int norootpass; + + int nolocaltcp; + int noremotetcp; + + sign_key *hostkey; + buffer * banner; + +}; + +typedef struct SvrRunOpts runopts; + +runopts * getrunopts(int argc, char ** argv); +void freerunopts(runopts* opts); + +#endif /* _RUNOPTS_H_ */ diff --git a/scp.c b/scp.c new file mode 100644 index 0000000..b23dec6 --- /dev/null +++ b/scp.c @@ -0,0 +1,1163 @@ +/* + * scp - secure remote copy. This is basically patched BSD rcp which + * uses ssh to do the data transfer (instead of using rcmd). + * + * NOTE: This version should NOT be suid root. (This uses ssh to + * do the transfer and ssh has the necessary privileges.) + * + * 1995 Timo Rinne , Tatu Ylonen + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ +/* + * Copyright (c) 1999 Theo de Raadt. All rights reserved. + * Copyright (c) 1999 Aaron Campbell. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * Parts from: + * + * Copyright (c) 1983, 1990, 1992, 1993, 1995 + * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. + * + */ + +#include "includes.h" +#include "atomicio.h" +#include "compat.h" +#include "scpmisc.h" +#include "progressmeter.h" + +#define _PATH_CP "/bin/cp" + +#ifndef TIMEVAL_TO_TIMESPEC +#define TIMEVAL_TO_TIMESPEC(tv, ts) { \ + (ts)->tv_sec = (tv)->tv_sec; \ + (ts)->tv_nsec = (tv)->tv_usec * 1000; \ +} +#endif + +#ifndef timersub +#define timersub(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ + if ((vvp)->tv_usec < 0) { \ + (vvp)->tv_sec--; \ + (vvp)->tv_usec += 1000000; \ + } \ + } while (/* CONSTCOND */ 0) +#endif /* timersub */ + + +void bwlimit(int); + +/* Struct for addargs */ +arglist args; + +/* Bandwidth limit */ +off_t limitbw = 0; + +/* Name of current file being transferred. */ +char *curfile; + +/* This is set to non-zero to enable verbose mode. */ +int verbose_mode = 0; + +#ifdef PROGRESS_METER +/* This is set to zero if the progressmeter is not desired. */ +int showprogress = 1; +#endif + +/* This is the program to execute for the secured connection. ("ssh" or -S) */ +char *ssh_program = _PATH_SSH_PROGRAM; + +/* This is used to store the pid of ssh_program */ +pid_t do_cmd_pid = -1; + +static void +killchild(int signo) +{ + if (do_cmd_pid > 1) + kill(do_cmd_pid, signo); + + _exit(1); +} + +/* + * This function executes the given command as the specified user on the + * given host. This returns < 0 if execution fails, and >= 0 otherwise. This + * assigns the input and output file descriptors on success. + */ + +int +do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc) +{ + int pin[2], pout[2], reserved[2]; + + if (verbose_mode) + fprintf(stderr, + "Executing: program %s host %s, user %s, command %s\n", + ssh_program, host, + remuser ? remuser : "(unspecified)", cmd); + + /* + * Reserve two descriptors so that the real pipes won't get + * descriptors 0 and 1 because that will screw up dup2 below. + */ + pipe(reserved); + + /* Create a socket pair for communicating with ssh. */ + if (pipe(pin) < 0 || pipe(pout) < 0) + { + fprintf(stderr, "Fatal error: pipe: %s\n", strerror(errno)); + exit(1); + } + + /* Free the reserved descriptors. */ + close(reserved[0]); + close(reserved[1]); + + /* Fork a child to execute the command on the remote host using ssh. */ + do_cmd_pid = fork(); + if (do_cmd_pid == 0) { + /* Child. */ + close(pin[1]); + close(pout[0]); + dup2(pin[0], 0); + dup2(pout[1], 1); + close(pin[0]); + close(pout[1]); + + args.list[0] = ssh_program; + if (remuser != NULL) + addargs(&args, "-l%s", remuser); + addargs(&args, "%s", host); + addargs(&args, "%s", cmd); + + execvp(ssh_program, args.list); + perror(ssh_program); + exit(1); + } else if (do_cmd_pid == -1) { + fprintf(stderr, "Fatal error: fork: %s\n", strerror(errno)); + exit(1); + } + /* Parent. Close the other side, and return the local side. */ + close(pin[0]); + *fdout = pin[1]; + close(pout[1]); + *fdin = pout[0]; + signal(SIGTERM, killchild); + signal(SIGINT, killchild); + signal(SIGHUP, killchild); + return 0; +} + +typedef struct { + int cnt; + char *buf; +} BUF; + +BUF *allocbuf(BUF *, int, int); +void lostconn(int); +void nospace(void); +int okname(char *); +void run_err(const char *,...); +void verifydir(char *); + +struct passwd *pwd; +uid_t userid; +int errs, remin, remout; +int pflag, iamremote, iamrecursive, targetshouldbedirectory; + +#define CMDNEEDS 64 +char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ + +int response(void); +void rsource(char *, struct stat *); +void sink(int, char *[]); +void source(int, char *[]); +void tolocal(int, char *[]); +void toremote(char *, int, char *[]); +void usage(void); + +int +main(int argc, char **argv) +{ + int ch, fflag, tflag, status; + double speed; + char *targ, *endp; + extern char *optarg; + extern int optind; + + /* hack, seems to work */ +// __progname = argv[0]; + + args.list = NULL; + addargs(&args, "ssh"); /* overwritten with ssh_program */ + addargs(&args, "-x"); + addargs(&args, "-oForwardAgent no"); + addargs(&args, "-oClearAllForwardings yes"); + + fflag = tflag = 0; + while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q1246S:o:F:")) != -1) + switch (ch) { + /* User-visible flags. */ + case '1': + case '2': + case '4': + case '6': + case 'C': + addargs(&args, "-%c", ch); + break; + case 'o': + case 'c': + case 'i': + case 'F': + addargs(&args, "-%c%s", ch, optarg); + break; + case 'P': + addargs(&args, "-p%s", optarg); + break; + case 'B': + addargs(&args, "-oBatchmode yes"); + break; + case 'l': + speed = strtod(optarg, &endp); + if (speed <= 0 || *endp != '\0') + usage(); + limitbw = speed * 1024; + break; + case 'p': + pflag = 1; + break; + case 'r': + iamrecursive = 1; + break; + case 'S': + ssh_program = xstrdup(optarg); + break; + case 'v': + addargs(&args, "-v"); + verbose_mode = 1; + break; +#ifdef PROGRESS_METER + case 'q': + showprogress = 0; + break; +#endif + + /* Server options. */ + case 'd': + targetshouldbedirectory = 1; + break; + case 'f': /* "from" */ + iamremote = 1; + fflag = 1; + break; + case 't': /* "to" */ + iamremote = 1; + tflag = 1; +#ifdef HAVE_CYGWIN + setmode(0, O_BINARY); +#endif + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + if ((pwd = getpwuid(userid = getuid())) == NULL) { + fprintf(stderr, "unknown user %u", (u_int) userid); + } + +#ifdef PROGRESS_METER + if (!isatty(STDERR_FILENO)) + showprogress = 0; +#endif + + remin = STDIN_FILENO; + remout = STDOUT_FILENO; + + if (fflag) { + /* Follow "protocol", send data. */ + (void) response(); + source(argc, argv); + exit(errs != 0); + } + if (tflag) { + /* Receive data. */ + sink(argc, argv); + exit(errs != 0); + } + if (argc < 2) + usage(); + if (argc > 2) + targetshouldbedirectory = 1; + + remin = remout = -1; + do_cmd_pid = -1; + /* Command to be executed on remote system using "ssh". */ + (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", + verbose_mode ? " -v" : "", + iamrecursive ? " -r" : "", pflag ? " -p" : "", + targetshouldbedirectory ? " -d" : ""); + + (void) signal(SIGPIPE, lostconn); + + if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ + toremote(targ, argc, argv); + else { + tolocal(argc, argv); /* Dest is local host. */ + if (targetshouldbedirectory) + verifydir(argv[argc - 1]); + } + /* + * Finally check the exit status of the ssh process, if one was forked + * and no error has occured yet + */ + if (do_cmd_pid != -1 && errs == 0) { + if (remin != -1) + (void) close(remin); + if (remout != -1) + (void) close(remout); + if (waitpid(do_cmd_pid, &status, 0) == -1) + errs = 1; + else { + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errs = 1; + } + } + exit(errs != 0); +} + +void +toremote(char *targ, int argc, char **argv) +{ + int i, len; + char *bp, *host, *src, *suser, *thost, *tuser; + + *targ++ = 0; + if (*targ == 0) + targ = "."; + + if ((thost = strrchr(argv[argc - 1], '@'))) { + /* user@host */ + *thost++ = 0; + tuser = argv[argc - 1]; + if (*tuser == '\0') + tuser = NULL; + } else { + thost = argv[argc - 1]; + tuser = NULL; + } + + for (i = 0; i < argc - 1; i++) { + src = colon(argv[i]); + if (src) { /* remote to remote */ + static char *ssh_options = + "-x -o'ClearAllForwardings yes'"; + *src++ = 0; + if (*src == 0) + src = "."; + host = strrchr(argv[i], '@'); + len = strlen(ssh_program) + strlen(argv[i]) + + strlen(src) + (tuser ? strlen(tuser) : 0) + + strlen(thost) + strlen(targ) + + strlen(ssh_options) + CMDNEEDS + 20; + bp = xmalloc(len); + if (host) { + *host++ = 0; + host = cleanhostname(host); + suser = argv[i]; + if (*suser == '\0') + suser = pwd->pw_name; + else if (!okname(suser)) { + xfree(bp); + continue; + } + if (tuser && !okname(tuser)) { + xfree(bp); + continue; + } + snprintf(bp, len, + "%s%s %s -n " + "-l %s %s %s %s '%s%s%s:%s'", + ssh_program, verbose_mode ? " -v" : "", + ssh_options, suser, host, cmd, src, + tuser ? tuser : "", tuser ? "@" : "", + thost, targ); + } else { + host = cleanhostname(argv[i]); + snprintf(bp, len, + "exec %s%s %s -n %s " + "%s %s '%s%s%s:%s'", + ssh_program, verbose_mode ? " -v" : "", + ssh_options, host, cmd, src, + tuser ? tuser : "", tuser ? "@" : "", + thost, targ); + } + if (verbose_mode) + fprintf(stderr, "Executing: %s\n", bp); + (void) system(bp); + (void) xfree(bp); + } else { /* local to remote */ + if (remin == -1) { + len = strlen(targ) + CMDNEEDS + 20; + bp = xmalloc(len); + (void) snprintf(bp, len, "%s -t %s", cmd, targ); + host = cleanhostname(thost); + if (do_cmd(host, tuser, bp, &remin, + &remout, argc) < 0) + exit(1); + if (response() < 0) + exit(1); + (void) xfree(bp); + } + source(1, argv + i); + } + } +} + +void +tolocal(int argc, char **argv) +{ + int i, len; + char *bp, *host, *src, *suser; + + for (i = 0; i < argc - 1; i++) { + if (!(src = colon(argv[i]))) { /* Local to local. */ + len = strlen(_PATH_CP) + strlen(argv[i]) + + strlen(argv[argc - 1]) + 20; + bp = xmalloc(len); + (void) snprintf(bp, len, "exec %s%s%s %s %s", _PATH_CP, + iamrecursive ? " -r" : "", pflag ? " -p" : "", + argv[i], argv[argc - 1]); + if (verbose_mode) + fprintf(stderr, "Executing: %s\n", bp); + if (system(bp)) + ++errs; + (void) xfree(bp); + continue; + } + *src++ = 0; + if (*src == 0) + src = "."; + if ((host = strrchr(argv[i], '@')) == NULL) { + host = argv[i]; + suser = NULL; + } else { + *host++ = 0; + suser = argv[i]; + if (*suser == '\0') + suser = pwd->pw_name; + } + host = cleanhostname(host); + len = strlen(src) + CMDNEEDS + 20; + bp = xmalloc(len); + (void) snprintf(bp, len, "%s -f %s", cmd, src); + if (do_cmd(host, suser, bp, &remin, &remout, argc) < 0) { + (void) xfree(bp); + ++errs; + continue; + } + xfree(bp); + sink(1, argv + argc - 1); + (void) close(remin); + remin = remout = -1; + } +} + +void +source(int argc, char **argv) +{ + struct stat stb; + static BUF buffer; + BUF *bp; + off_t i, amt, result, statbytes; + int fd, haderr, indx; + char *last, *name, buf[2048]; + int len; + + for (indx = 0; indx < argc; ++indx) { + name = argv[indx]; + statbytes = 0; + len = strlen(name); + while (len > 1 && name[len-1] == '/') + name[--len] = '\0'; + if (strchr(name, '\n') != NULL) { + run_err("%s: skipping, filename contains a newline", + name); + goto next; + } + if ((fd = open(name, O_RDONLY, 0)) < 0) + goto syserr; + if (fstat(fd, &stb) < 0) { +syserr: run_err("%s: %s", name, strerror(errno)); + goto next; + } + switch (stb.st_mode & S_IFMT) { + case S_IFREG: + break; + case S_IFDIR: + if (iamrecursive) { + rsource(name, &stb); + goto next; + } + /* FALLTHROUGH */ + default: + run_err("%s: not a regular file", name); + goto next; + } + if ((last = strrchr(name, '/')) == NULL) + last = name; + else + ++last; + curfile = last; + if (pflag) { + /* + * Make it compatible with possible future + * versions expecting microseconds. + */ + (void) snprintf(buf, sizeof buf, "T%lu 0 %lu 0\n", + (u_long) stb.st_mtime, + (u_long) stb.st_atime); + (void) atomicio(vwrite, remout, buf, strlen(buf)); + if (response() < 0) + goto next; + } +#define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) + snprintf(buf, sizeof buf, "C%04o %lld %s\n", + (u_int) (stb.st_mode & FILEMODEMASK), + (int64_t)stb.st_size, last); + if (verbose_mode) { + fprintf(stderr, "Sending file modes: %s", buf); + } + (void) atomicio(vwrite, remout, buf, strlen(buf)); + if (response() < 0) + goto next; + if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) { +next: (void) close(fd); + continue; + } +#ifdef PROGRESS_METER + if (showprogress) + start_progress_meter(curfile, stb.st_size, &statbytes); +#endif + /* Keep writing after an error so that we stay sync'd up. */ + for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { + amt = bp->cnt; + if (i + amt > stb.st_size) + amt = stb.st_size - i; + if (!haderr) { + result = atomicio(read, fd, bp->buf, amt); + if (result != amt) + haderr = result >= 0 ? EIO : errno; + } + if (haderr) + (void) atomicio(vwrite, remout, bp->buf, amt); + else { + result = atomicio(vwrite, remout, bp->buf, amt); + if (result != amt) + haderr = result >= 0 ? EIO : errno; + statbytes += result; + } + if (limitbw) + bwlimit(amt); + } +#ifdef PROGRESS_METER + if (showprogress) + stop_progress_meter(); +#endif + + if (close(fd) < 0 && !haderr) + haderr = errno; + if (!haderr) + (void) atomicio(vwrite, remout, "", 1); + else + run_err("%s: %s", name, strerror(haderr)); + (void) response(); + } +} + +void +rsource(char *name, struct stat *statp) +{ + DIR *dirp; + struct dirent *dp; + char *last, *vect[1], path[1100]; + + if (!(dirp = opendir(name))) { + run_err("%s: %s", name, strerror(errno)); + return; + } + last = strrchr(name, '/'); + if (last == 0) + last = name; + else + last++; + if (pflag) { + (void) snprintf(path, sizeof(path), "T%lu 0 %lu 0\n", + (u_long) statp->st_mtime, + (u_long) statp->st_atime); + (void) atomicio(vwrite, remout, path, strlen(path)); + if (response() < 0) { + closedir(dirp); + return; + } + } + (void) snprintf(path, sizeof path, "D%04o %d %.1024s\n", + (u_int) (statp->st_mode & FILEMODEMASK), 0, last); + if (verbose_mode) + fprintf(stderr, "Entering directory: %s", path); + (void) atomicio(vwrite, remout, path, strlen(path)); + if (response() < 0) { + closedir(dirp); + return; + } + while ((dp = readdir(dirp)) != NULL) { + if (dp->d_ino == 0) + continue; + if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) + continue; + if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) { + run_err("%s/%s: name too long", name, dp->d_name); + continue; + } + (void) snprintf(path, sizeof path, "%s/%s", name, dp->d_name); + vect[0] = path; + source(1, vect); + } + (void) closedir(dirp); + (void) atomicio(vwrite, remout, "E\n", 2); + (void) response(); +} + +void +bwlimit(int amount) +{ + static struct timeval bwstart, bwend; + static int lamt, thresh = 16384; + uint64_t wait; + struct timespec ts, rm; + + if (!timerisset(&bwstart)) { + gettimeofday(&bwstart, NULL); + return; + } + + lamt += amount; + if (lamt < thresh) + return; + + gettimeofday(&bwend, NULL); + timersub(&bwend, &bwstart, &bwend); + if (!timerisset(&bwend)) + return; + + lamt *= 8; + wait = (double)1000000L * lamt / limitbw; + + bwstart.tv_sec = wait / 1000000L; + bwstart.tv_usec = wait % 1000000L; + + if (timercmp(&bwstart, &bwend, >)) { + timersub(&bwstart, &bwend, &bwend); + + /* Adjust the wait time */ + if (bwend.tv_sec) { + thresh /= 2; + if (thresh < 2048) + thresh = 2048; + } else if (bwend.tv_usec < 100) { + thresh *= 2; + if (thresh > 32768) + thresh = 32768; + } + + TIMEVAL_TO_TIMESPEC(&bwend, &ts); + while (nanosleep(&ts, &rm) == -1) { + if (errno != EINTR) + break; + ts = rm; + } + } + + lamt = 0; + gettimeofday(&bwstart, NULL); +} + +void +sink(int argc, char **argv) +{ + static BUF buffer; + struct stat stb; + enum { + YES, NO, DISPLAYED + } wrerr; + BUF *bp; + off_t i, j; + int amt, count, exists, first, mask, mode, ofd, omode; + off_t size, statbytes; + int setimes, targisdir, wrerrno = 0; + char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; + struct timeval tv[2]; + +#define atime tv[0] +#define mtime tv[1] +#define SCREWUP(str) do { why = str; goto screwup; } while (0) + + setimes = targisdir = 0; + mask = umask(0); + if (!pflag) + (void) umask(mask); + if (argc != 1) { + run_err("ambiguous target"); + exit(1); + } + targ = *argv; + if (targetshouldbedirectory) + verifydir(targ); + + (void) atomicio(vwrite, remout, "", 1); + if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) + targisdir = 1; + for (first = 1;; first = 0) { + cp = buf; + if (atomicio(read, remin, cp, 1) <= 0) + return; + if (*cp++ == '\n') + SCREWUP("unexpected "); + do { + if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) + SCREWUP("lost connection"); + *cp++ = ch; + } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); + *cp = 0; + + if (buf[0] == '\01' || buf[0] == '\02') { + if (iamremote == 0) + (void) atomicio(vwrite, STDERR_FILENO, + buf + 1, strlen(buf + 1)); + if (buf[0] == '\02') + exit(1); + ++errs; + continue; + } + if (buf[0] == 'E') { + (void) atomicio(vwrite, remout, "", 1); + return; + } + if (ch == '\n') + *--cp = 0; + + cp = buf; + if (*cp == 'T') { + setimes++; + cp++; + mtime.tv_sec = strtol(cp, &cp, 10); + if (!cp || *cp++ != ' ') + SCREWUP("mtime.sec not delimited"); + mtime.tv_usec = strtol(cp, &cp, 10); + if (!cp || *cp++ != ' ') + SCREWUP("mtime.usec not delimited"); + atime.tv_sec = strtol(cp, &cp, 10); + if (!cp || *cp++ != ' ') + SCREWUP("atime.sec not delimited"); + atime.tv_usec = strtol(cp, &cp, 10); + if (!cp || *cp++ != '\0') + SCREWUP("atime.usec not delimited"); + (void) atomicio(vwrite, remout, "", 1); + continue; + } + if (*cp != 'C' && *cp != 'D') { + /* + * Check for the case "rcp remote:foo\* local:bar". + * In this case, the line "No match." can be returned + * by the shell before the rcp command on the remote is + * executed so the ^Aerror_message convention isn't + * followed. + */ + if (first) { + run_err("%s", cp); + exit(1); + } + SCREWUP("expected control record"); + } + mode = 0; + for (++cp; cp < buf + 5; cp++) { + if (*cp < '0' || *cp > '7') + SCREWUP("bad mode"); + mode = (mode << 3) | (*cp - '0'); + } + if (*cp++ != ' ') + SCREWUP("mode not delimited"); + + for (size = 0; isdigit(*cp);) + size = size * 10 + (*cp++ - '0'); + if (*cp++ != ' ') + SCREWUP("size not delimited"); + if (targisdir) { + static char *namebuf; + static int cursize; + size_t need; + + need = strlen(targ) + strlen(cp) + 250; + if (need > cursize) { + if (namebuf) + xfree(namebuf); + namebuf = xmalloc(need); + cursize = need; + } + (void) snprintf(namebuf, need, "%s%s%s", targ, + strcmp(targ, "/") ? "/" : "", cp); + np = namebuf; + } else + np = targ; + curfile = cp; + exists = stat(np, &stb) == 0; + if (buf[0] == 'D') { + int mod_flag = pflag; + if (exists) { + if (!S_ISDIR(stb.st_mode)) { + errno = ENOTDIR; + goto bad; + } + if (pflag) + (void) chmod(np, mode); + } else { + /* Handle copying from a read-only + directory */ + mod_flag = 1; + if (mkdir(np, mode | S_IRWXU) < 0) + goto bad; + } + vect[0] = xstrdup(np); + sink(1, vect); + if (setimes) { + setimes = 0; + if (utimes(vect[0], tv) < 0) + run_err("%s: set times: %s", + vect[0], strerror(errno)); + } + if (mod_flag) + (void) chmod(vect[0], mode); + if (vect[0]) + xfree(vect[0]); + continue; + } + omode = mode; + mode |= S_IWRITE; + if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { +bad: run_err("%s: %s", np, strerror(errno)); + continue; + } + (void) atomicio(vwrite, remout, "", 1); + if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) { + (void) close(ofd); + continue; + } + cp = bp->buf; + wrerr = NO; + + statbytes = 0; +#ifdef PROGRESS_METER + if (showprogress) + start_progress_meter(curfile, size, &statbytes); +#endif + for (count = i = 0; i < size; i += 4096) { + amt = 4096; + if (i + amt > size) + amt = size - i; + count += amt; + do { + j = read(remin, cp, amt); + if (j == -1 && (errno == EINTR || + errno == EAGAIN)) { + continue; + } else if (j <= 0) { + run_err("%s", j ? strerror(errno) : + "dropped connection"); + exit(1); + } + amt -= j; + cp += j; + statbytes += j; + } while (amt > 0); + + if (limitbw) + bwlimit(4096); + + if (count == bp->cnt) { + /* Keep reading so we stay sync'd up. */ + if (wrerr == NO) { + j = atomicio(vwrite, ofd, bp->buf, count); + if (j != count) { + wrerr = YES; + wrerrno = j >= 0 ? EIO : errno; + } + } + count = 0; + cp = bp->buf; + } + } +#ifdef PROGRESS_METER + if (showprogress) + stop_progress_meter(); +#endif + if (count != 0 && wrerr == NO && + (j = atomicio(vwrite, ofd, bp->buf, count)) != count) { + wrerr = YES; + wrerrno = j >= 0 ? EIO : errno; + } + if (wrerr == NO && ftruncate(ofd, size) != 0) { + run_err("%s: truncate: %s", np, strerror(errno)); + wrerr = DISPLAYED; + } + if (pflag) { + if (exists || omode != mode) +#ifdef HAVE_FCHMOD + if (fchmod(ofd, omode)) +#else /* HAVE_FCHMOD */ + if (chmod(np, omode)) +#endif /* HAVE_FCHMOD */ + run_err("%s: set mode: %s", + np, strerror(errno)); + } else { + if (!exists && omode != mode) +#ifdef HAVE_FCHMOD + if (fchmod(ofd, omode & ~mask)) +#else /* HAVE_FCHMOD */ + if (chmod(np, omode & ~mask)) +#endif /* HAVE_FCHMOD */ + run_err("%s: set mode: %s", + np, strerror(errno)); + } + if (close(ofd) == -1) { + wrerr = YES; + wrerrno = errno; + } + (void) response(); + if (setimes && wrerr == NO) { + setimes = 0; + if (utimes(np, tv) < 0) { + run_err("%s: set times: %s", + np, strerror(errno)); + wrerr = DISPLAYED; + } + } + switch (wrerr) { + case YES: + run_err("%s: %s", np, strerror(wrerrno)); + break; + case NO: + (void) atomicio(vwrite, remout, "", 1); + break; + case DISPLAYED: + break; + } + } +screwup: + run_err("protocol error: %s", why); + exit(1); +} + +int +response(void) +{ + char ch, *cp, resp, rbuf[2048]; + + if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp)) + lostconn(0); + + cp = rbuf; + switch (resp) { + case 0: /* ok */ + return (0); + default: + *cp++ = resp; + /* FALLTHROUGH */ + case 1: /* error, followed by error msg */ + case 2: /* fatal error, "" */ + do { + if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) + lostconn(0); + *cp++ = ch; + } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); + + if (!iamremote) + (void) atomicio(vwrite, STDERR_FILENO, rbuf, cp - rbuf); + ++errs; + if (resp == 1) + return (-1); + exit(1); + } + /* NOTREACHED */ +} + +void +usage(void) +{ + (void) fprintf(stderr, + "usage: scp [-pqrvBC1246] [-F config] [-S program] [-P port]\n" + " [-c cipher] [-i identity] [-l limit] [-o option]\n" + " [[user@]host1:]file1 [...] [[user@]host2:]file2\n"); + exit(1); +} + +void +run_err(const char *fmt,...) +{ + static FILE *fp; + va_list ap; + + ++errs; + if (fp == NULL && !(fp = fdopen(remout, "w"))) + return; + (void) fprintf(fp, "%c", 0x01); + (void) fprintf(fp, "scp: "); + va_start(ap, fmt); + (void) vfprintf(fp, fmt, ap); + va_end(ap); + (void) fprintf(fp, "\n"); + (void) fflush(fp); + + if (!iamremote) { + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + } +} + +void +verifydir(char *cp) +{ + struct stat stb; + + if (!stat(cp, &stb)) { + if (S_ISDIR(stb.st_mode)) + return; + errno = ENOTDIR; + } + run_err("%s: %s", cp, strerror(errno)); + exit(1); +} + +int +okname(char *cp0) +{ + int c; + char *cp; + + cp = cp0; + do { + c = (int)*cp; + if (c & 0200) + goto bad; + if (!isalpha(c) && !isdigit(c)) { + switch (c) { + case '\'': + case '"': + case '`': + case ' ': + case '#': + goto bad; + default: + break; + } + } + } while (*++cp); + return (1); + +bad: fprintf(stderr, "%s: invalid user name\n", cp0); + return (0); +} + +BUF * +allocbuf(BUF *bp, int fd, int blksize) +{ + size_t size; +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE + struct stat stb; + + if (fstat(fd, &stb) < 0) { + run_err("fstat: %s", strerror(errno)); + return (0); + } + size = roundup(stb.st_blksize, blksize); + if (size == 0) + size = blksize; +#else /* HAVE_STRUCT_STAT_ST_BLKSIZE */ + size = blksize; +#endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */ + if (bp->cnt >= size) + return (bp); + if (bp->buf == NULL) + bp->buf = xmalloc(size); + else + bp->buf = xrealloc(bp->buf, size); + memset(bp->buf, 0, size); + bp->cnt = size; + return (bp); +} + +void +lostconn(int signo) +{ + if (!iamremote) + write(STDERR_FILENO, "lost connection\n", 16); + if (signo) + _exit(1); + else + exit(1); +} diff --git a/scpmisc.c b/scpmisc.c new file mode 100644 index 0000000..2d37753 --- /dev/null +++ b/scpmisc.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2000 Markus Friedl. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/*RCSID("$OpenBSD: misc.c,v 1.22 2003/09/18 08:49:45 markus Exp $");*/ + +/* For xmalloc, xfree etc: + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Versions of malloc and friends that check their results, and never return + * failure (they call fatal if they encounter an error). + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/*RCSID("$OpenBSD: xmalloc.c,v 1.16 2001/07/23 18:21:46 stevesk Exp $");*/ + +#include "includes.h" +#include "scpmisc.h" + +void * +xmalloc(size_t size) +{ + void *ptr; + + if (size == 0) { + fprintf(stderr, "xmalloc: zero size\n"); + exit(EXIT_FAILURE); + } + ptr = malloc(size); + if (ptr == NULL) { + fprintf(stderr, "xmalloc: out of memory (allocating %lu bytes)\n", (u_long) size); + exit(EXIT_FAILURE); + } + return ptr; +} + +void * +xrealloc(void *ptr, size_t new_size) +{ + void *new_ptr; + + if (new_size == 0) { + fprintf(stderr, "xrealloc: zero size\n"); + exit(EXIT_FAILURE); + } + if (ptr == NULL) + new_ptr = malloc(new_size); + else + new_ptr = realloc(ptr, new_size); + if (new_ptr == NULL) { + fprintf(stderr, "xrealloc: out of memory (new_size %lu bytes)\n", (u_long) new_size); + exit(EXIT_FAILURE); + } + return new_ptr; +} + +void +xfree(void *ptr) +{ + if (ptr == NULL) { + fprintf(stderr, "xfree: NULL pointer given as argument\n"); + exit(EXIT_FAILURE); + } + free(ptr); +} + +char * +xstrdup(const char *str) +{ + size_t len; + char *cp; + + len = strlen(str) + 1; + cp = xmalloc(len); + strncpy(cp, str, len); + return cp; +} + +char * +cleanhostname(char *host) +{ + if (*host == '[' && host[strlen(host) - 1] == ']') { + host[strlen(host) - 1] = '\0'; + return (host + 1); + } else + return host; +} + +char * +colon(char *cp) +{ + int flag = 0; + + if (*cp == ':') /* Leading colon is part of file name. */ + return (0); + if (*cp == '[') + flag = 1; + + for (; *cp; ++cp) { + if (*cp == '@' && *(cp+1) == '[') + flag = 1; + if (*cp == ']' && *(cp+1) == ':' && flag) + return (cp+1); + if (*cp == ':' && !flag) + return (cp); + if (*cp == '/') + return (0); + } + return (0); +} + +/* function to assist building execv() arguments */ +void +addargs(arglist *args, char *fmt, ...) +{ + va_list ap; + char buf[1024]; + int nalloc; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + nalloc = args->nalloc; + if (args->list == NULL) { + nalloc = 32; + args->num = 0; + } else if (args->num+2 >= nalloc) + nalloc *= 2; + + args->list = xrealloc(args->list, nalloc * sizeof(char *)); + args->nalloc = nalloc; + args->list[args->num++] = xstrdup(buf); + args->list[args->num] = NULL; +} diff --git a/scpmisc.h b/scpmisc.h new file mode 100644 index 0000000..b6059c6 --- /dev/null +++ b/scpmisc.h @@ -0,0 +1,44 @@ +/* $OpenBSD: misc.h,v 1.12 2002/03/19 10:49:35 markus Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* actually from atomicio, but is only used in scp code */ +#define vwrite (ssize_t (*)(int, void *, size_t))write + +char *chop(char *); +char *strdelim(char **); +void set_nonblock(int); +void unset_nonblock(int); +void set_nodelay(int); +int a2port(const char *); +char *cleanhostname(char *); +char *colon(char *); +long convtime(const char *); + +struct passwd *pwcopy(struct passwd *); + +typedef struct arglist arglist; +struct arglist { + char **list; + int num; + int nalloc; +}; +void addargs(arglist *, char *, ...); + +/* from xmalloc.h */ +void *xmalloc(size_t); +void *xrealloc(void *, size_t); +void xfree(void *); +char *xstrdup(const char *); + + diff --git a/service.h b/service.h new file mode 100644 index 0000000..0dfcd6e --- /dev/null +++ b/service.h @@ -0,0 +1,30 @@ +/* + * 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 _SERVICE_H_ +#define _SERVICE_H_ + +void recv_msg_service_request(); + +#endif /* _SERVICE_H_ */ diff --git a/session.h b/session.h new file mode 100644 index 0000000..2760da4 --- /dev/null +++ b/session.h @@ -0,0 +1,176 @@ +/* + * 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 _SESSION_H_ +#define _SESSION_H_ + +#include "includes.h" +#include "buffer.h" +#include "signkey.h" +#include "kex.h" +#include "auth.h" +#include "channel.h" +#include "queue.h" +#include "runopts.h" +#include "remotetcpfwd.h" + +extern int sessinitdone; /* Is set to 0 somewhere */ +extern int exitflag; + +void common_session_init(int sock, runopts *opts); +void common_session_cleanup(); +void checktimeouts(); +void session_identification(); + +extern void(*session_remoteclosed)(); + +/* Server */ +void svr_session(int sock, runopts *opts, int childpipe, + struct sockaddr *remoteaddr); + +struct key_context { + + const struct dropbear_cipher *recv_algo_crypt; /* NULL for none */ + const struct dropbear_cipher *trans_algo_crypt; /* NULL for none */ + const struct dropbear_hash *recv_algo_mac; /* NULL for none */ + const struct dropbear_hash *trans_algo_mac; /* NULL for none */ + char algo_kex; + char algo_hostkey; + + char recv_algo_comp; /* compression */ + char trans_algo_comp; +#ifndef DISABLE_ZLIB + z_streamp recv_zstream; + z_streamp trans_zstream; +#endif + + /* actual keys */ + symmetric_CBC recv_symmetric_struct; + symmetric_CBC trans_symmetric_struct; + unsigned char recvmackey[MAX_MAC_KEY]; + unsigned char transmackey[MAX_MAC_KEY]; + +}; + +struct sshsession { + + /* Is it a client or server? */ + unsigned char isserver; + + runopts * opts; /* runtime options, incl hostkey, banner etc */ + + long connecttimeout; /* time to disconnect if we have a timeout (for + userauth etc), or 0 for no timeout */ + + int sock; + + struct sockaddr *remoteaddr; + unsigned char *remotehost; /* the peer hostname */ + unsigned char *remoteident; + + int maxfd; /* the maximum file descriptor to check with select() */ + + + /* Packet buffers/values etc */ + buffer *writepayload; /* Unencrypted payload to write - this is used + throughout the code, as handlers fill out this + buffer with the packet to send. */ + struct Queue writequeue; /* A queue of encrypted packets to send */ + buffer *readbuf; /* Encrypted */ + buffer *decryptreadbuf; /* Post-decryption */ + buffer *payload; /* Post-decompression, the actual SSH packet */ + unsigned int transseq, recvseq; /* Sequence IDs */ + + /* Packet-handling flags */ + unsigned dataallowed : 1; /* whether we can send data packets or we are in + the middle of a KEX or something */ + + unsigned char expecting; /* byte indicating what packet we expect next, + or 0x00 for any */ + + unsigned char ignorenext; /* whether to ignore the next packet, + used for kex_follows stuff */ + + + + /* KEX/encryption related */ + struct KEXState kexstate; + struct key_context *keys; + struct key_context *newkeys; + unsigned char *session_id; /* this is the hash from the first kex */ + /* The below are used temorarily during kex, are freed after use */ + mp_int * dh_K; /* SSH_MSG_KEXDH_REPLY and sending SSH_MSH_NEWKEYS */ + unsigned char hash[SHA1_HASH_SIZE]; /* the hash*/ + buffer* kexhashbuf; /* session hash buffer calculated from various packets*/ + buffer* transkexinit; /* the kexinit packet we send should be kept so we + can add it to the hash when generating keys */ + + + + /* Channel related */ + struct Channel ** channels; /* these pointers may be null */ + unsigned int chansize; /* the number of Channel*s allocated for channels */ + struct ChanType **chantypes; /* The valid channel types */ + + + /* TCP forwarding - where manage listeners */ +#ifndef DISABLE_REMOTETCPFWD + struct TCPListener ** tcplisteners; + unsigned int tcplistensize; +#endif + +}; + +struct serversession { + + /* Server specific options */ + int childpipe; /* kept open until we successfully authenticate */ + /* userauth */ + struct AuthState authstate; + + struct ChildPid * childpids; /* array of mappings childpid<->channel */ + unsigned int childpidsize; + +}; + + + +struct clientsession { + + int something; /* XXX */ + +}; + +/* Global structs storing the state */ +extern struct sshsession ses; + +#ifdef DROPBEAR_SERVER +extern struct serversession svr_ses; +#endif /* DROPBEAR_SERVER */ + +#ifdef DROPBEAR_CLIENT +extern struct serversession cli_ses; +#endif /* DROPBEAR_CLIENT */ + +#endif /* _SESSION_H_ */ diff --git a/signkey.c b/signkey.c new file mode 100644 index 0000000..c529c8c --- /dev/null +++ b/signkey.c @@ -0,0 +1,376 @@ +/* + * 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. */ + +#include "includes.h" +#include "dbutil.h" +#include "signkey.h" +#include "buffer.h" +#include "ssh.h" + +/* malloc a new sign_key and set the dss and rsa keys to NULL */ +sign_key * new_sign_key() { + + sign_key * ret; + + ret = (sign_key*)m_malloc(sizeof(sign_key)); +#ifdef DROPBEAR_DSS + ret->dsskey = NULL; +#endif +#ifdef DROPBEAR_RSA + ret->rsakey = NULL; +#endif + return ret; + +} + +/* returns DROPBEAR_SUCCESS on success, DROPBEAR_FAILURE on fail. + * type is set to hold the type returned */ +int buf_get_pub_key(buffer *buf, sign_key *key, int *type) { + + unsigned char* ident; + unsigned int len; + + ident = buf_getstring(buf, &len); + +#ifdef DROPBEAR_DSS + if (memcmp(ident, SSH_SIGNKEY_DSS, len) == 0 + && (*type == DROPBEAR_SIGNKEY_ANY + || *type == DROPBEAR_SIGNKEY_DSS)) { + m_free(ident); + buf_setpos(buf, buf->pos - len - 4); + dss_key_free(key->dsskey); + key->dsskey = (dss_key*)m_malloc(sizeof(dss_key)); + *type = DROPBEAR_SIGNKEY_DSS; + return buf_get_dss_pub_key(buf, key->dsskey); + } +#endif +#ifdef DROPBEAR_RSA + if (memcmp(ident, SSH_SIGNKEY_RSA, len) == 0 + && (*type == DROPBEAR_SIGNKEY_ANY + || *type == DROPBEAR_SIGNKEY_RSA)) { + m_free(ident); + buf_setpos(buf, buf->pos - len - 4); + rsa_key_free(key->rsakey); + key->rsakey = (rsa_key*)m_malloc(sizeof(rsa_key)); + *type = DROPBEAR_SIGNKEY_RSA; + return buf_get_rsa_pub_key(buf, key->rsakey); + } +#endif + + m_free(ident); + + return DROPBEAR_FAILURE; + +} + +/* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +/* type is set to hold the type returned */ +int buf_get_priv_key(buffer *buf, sign_key *key, int *type) { + + unsigned char* ident; + unsigned int len; + int ret; + + TRACE(("enter buf_get_priv_key")); + ident = buf_getstring(buf, &len); + +#ifdef DROPBEAR_DSS + if (memcmp(ident, SSH_SIGNKEY_DSS, len) == 0 + && (*type == DROPBEAR_SIGNKEY_ANY + || *type == DROPBEAR_SIGNKEY_DSS)) { + m_free(ident); + buf_setpos(buf, buf->pos - len - 4); + dss_key_free(key->dsskey); + key->dsskey = (dss_key*)m_malloc(sizeof(dss_key)); + ret = buf_get_dss_priv_key(buf, key->dsskey); + *type = DROPBEAR_SIGNKEY_DSS; + if (ret == DROPBEAR_FAILURE) { + m_free(key->dsskey); + } + TRACE(("leave buf_get_priv_key: done get dss")); + return ret; + } +#endif +#ifdef DROPBEAR_RSA + if (memcmp(ident, SSH_SIGNKEY_RSA, len) == 0 + && (*type == DROPBEAR_SIGNKEY_ANY + || *type == DROPBEAR_SIGNKEY_RSA)) { + m_free(ident); + buf_setpos(buf, buf->pos - len - 4); + rsa_key_free(key->rsakey); + key->rsakey = (rsa_key*)m_malloc(sizeof(rsa_key)); + ret = buf_get_rsa_priv_key(buf, key->rsakey); + *type = DROPBEAR_SIGNKEY_RSA; + if (ret == DROPBEAR_FAILURE) { + m_free(key->rsakey); + } + TRACE(("leave buf_get_priv_key: done get rsa")); + return ret; + } +#endif + + m_free(ident); + + TRACE(("leave buf_get_priv_key")); + return DROPBEAR_FAILURE; + +} + +/* type is either DROPBEAR_SIGNKEY_DSS or DROPBEAR_SIGNKEY_RSA */ +void buf_put_pub_key(buffer* buf, sign_key *key, int type) { + + buffer *pubkeys; + + TRACE(("enter buf_put_pub_key")); + pubkeys = buf_new(1000); + +#ifdef DROPBEAR_DSS + if (type == DROPBEAR_SIGNKEY_DSS) { + buf_put_dss_pub_key(pubkeys, key->dsskey); + } +#endif +#ifdef DROPBEAR_RSA + if (type == DROPBEAR_SIGNKEY_RSA) { + buf_put_rsa_pub_key(pubkeys, key->rsakey); + } +#endif + if (pubkeys->len == 0) { + dropbear_exit("bad key types in buf_put_pub_key"); + } + + buf_setpos(pubkeys, 0); + buf_putstring(buf, buf_getptr(pubkeys, pubkeys->len), + pubkeys->len); + + buf_free(pubkeys); + TRACE(("leave buf_put_pub_key")); +} + +/* type is either DROPBEAR_SIGNKEY_DSS or DROPBEAR_SIGNKEY_RSA */ +void buf_put_priv_key(buffer* buf, sign_key *key, int type) { + + TRACE(("enter buf_put_priv_key")); + TRACE(("type is %d", type)); + +#ifdef DROPBEAR_DSS + if (type == DROPBEAR_SIGNKEY_DSS) { + buf_put_dss_priv_key(buf, key->dsskey); + TRACE(("leave buf_put_priv_key: dss done")); + return; + } +#endif +#ifdef DROPBEAR_RSA + if (type == DROPBEAR_SIGNKEY_RSA) { + buf_put_rsa_priv_key(buf, key->rsakey); + TRACE(("leave buf_put_priv_key: rsa done")); + return; + } +#endif + dropbear_exit("bad key types in put pub key"); +} + +void sign_key_free(sign_key *key) { + + TRACE(("enter sign_key_free")); + +#ifdef DROPBEAR_DSS + dss_key_free(key->dsskey); + key->dsskey = NULL; +#endif +#ifdef DROPBEAR_RSA + rsa_key_free(key->rsakey); + key->rsakey = NULL; +#endif + + m_free(key); + TRACE(("leave sign_key_free")); +} + +static char hexdig(unsigned char x) { + + if (x > 0xf) + return 'X'; + + if (x < 10) + return '0' + x; + else + return 'a' + x - 10; +} + +/* Since we're not sure if we'll have md5 or sha1, we present both. + * MD5 is used in preference, but sha1 could still be useful */ +#ifdef DROPBEAR_MD5_HMAC +static char * sign_key_md5_fingerprint(sign_key *key, int type) { + + char * ret; + hash_state hs; + buffer *pubkeys; + unsigned char hash[MD5_HASH_SIZE]; + unsigned int h, i; + unsigned int buflen; + + md5_init(&hs); + + pubkeys = buf_new(1000); + buf_put_pub_key(pubkeys, key, type); + /* skip the size int of the string - this is a bit messy */ + buf_setpos(pubkeys, 4); + md5_process(&hs, buf_getptr(pubkeys, pubkeys->len-pubkeys->pos), + pubkeys->len-pubkeys->pos); + + buf_free(pubkeys); + md5_done(&hs, hash); + + /* "md5 hexfingerprinthere\0", each hex digit is "AB:" etc */ + buflen = 4 + 3*MD5_HASH_SIZE; + ret = (char*)m_malloc(buflen); + + memset(ret, 'Z', buflen); + strcpy(ret, "md5 "); + + for (i = 4, h = 0; i < buflen; i+=3, h++) { + ret[i] = hexdig(hash[h] >> 4); + ret[i+1] = hexdig(hash[h] & 0x0f); + ret[i+2] = ':'; + } + ret[buflen-1] = 0x0; + + return ret; +} + +#else /* use SHA1 rather than MD5 for fingerprint */ +static char * sign_key_sha1_fingerprint(sign_key *key, int type) { + + char * ret; + hash_state hs; + buffer *pubkeys; + unsigned char hash[SHA1_HASH_SIZE]; + unsigned int h, i; + unsigned int buflen; + + sha1_init(&hs); + + pubkeys = buf_new(1000); + buf_put_pub_key(pubkeys, key, type); + buf_setpos(pubkeys, 4); + /* skip the size int of the string - this is a bit messy */ + sha1_process(&hs, buf_getptr(pubkeys, pubkeys->len-pubkeys->pos), + pubkeys->len-pubkeys->pos); + + buf_free(pubkeys); + sha1_done(&hs, hash); + + /* "sha1 hexfingerprinthere\0", each hex digit is "AB:" etc */ + buflen = 5 + 3*SHA1_HASH_SIZE; + ret = (char*)m_malloc(buflen); + + strcpy(ret, "sha1 "); + + for (i = 5, h = 0; i < buflen; i+=3, h++) { + ret[i] = hexdig(hash[h] >> 4); + ret[i+1] = hexdig(hash[h] & 0x0f); + ret[i+2] = ':'; + } + ret[buflen-1] = 0x0; + + return ret; +} + +#endif /* MD5/SHA1 switch */ + +/* This will return a freshly malloced string, containing a fingerprint + * in either sha1 or md5 */ +char * sign_key_fingerprint(sign_key *key, int type) { + +#ifdef DROPBEAR_MD5_HMAC + return sign_key_md5_fingerprint(key, type); +#else + return sign_key_sha1_fingerprint(key, type); +#endif +} + +void buf_put_sign(buffer* buf, sign_key *key, int type, + const unsigned char *data, unsigned int len) { + + buffer *sigblob; + + sigblob = buf_new(1000); + +#ifdef DROPBEAR_DSS + if (type == DROPBEAR_SIGNKEY_DSS) { + buf_put_dss_sign(sigblob, key->dsskey, data, len); + } +#endif +#ifdef DROPBEAR_RSA + if (type == DROPBEAR_SIGNKEY_RSA) { + buf_put_rsa_sign(sigblob, key->rsakey, data, len); + } +#endif + if (sigblob->len == 0) { + dropbear_exit("non-matching signing type"); + } + + buf_setpos(sigblob, 0); + buf_putstring(buf, buf_getptr(sigblob, sigblob->len), + sigblob->len); + + buf_free(sigblob); + +} + +#ifdef DROPBEAR_SIGNKEY_VERIFY +/* Return DROPBEAR_SUCCESS or DROPBEAR_FAILURE. + * If FAILURE is returned, the position of + * buf is undefined. If SUCCESS is returned, buf will be positioned after the + * signature blob */ +int buf_verify(buffer * buf, sign_key *key, const unsigned char *data, + unsigned int len) { + + unsigned int bloblen; + unsigned char * ident = NULL; + unsigned int identlen = 0; + + bloblen = buf_getint(buf); + ident = buf_getstring(buf, &identlen); + +#ifdef DROPBEAR_DSS + if (bloblen == DSS_SIGNATURE_SIZE && + memcmp(ident, SSH_SIGNKEY_DSS, identlen) == 0) { + m_free(ident); + return buf_dss_verify(buf, key->dsskey, data, len); + } +#endif + +#ifdef DROPBEAR_RSA + if (memcmp(ident, SSH_SIGNKEY_RSA, identlen) == 0) { + m_free(ident); + return buf_rsa_verify(buf, key->rsakey, data, len); + } +#endif + + m_free(ident); + dropbear_exit("non-matching signing type"); + return DROPBEAR_FAILURE; +} +#endif /* DROPBEAR_SIGNKEY_VERIFY */ diff --git a/signkey.h b/signkey.h new file mode 100644 index 0000000..396db7f --- /dev/null +++ b/signkey.h @@ -0,0 +1,58 @@ +/* + * 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 _SIGNKEY_H_ +#define _SIGNKEY_H_ + +#include "buffer.h" +#include "dss.h" +#include "rsa.h" + +struct SIGN_key { + +#ifdef DROPBEAR_DSS + dss_key * dsskey; +#endif +#ifdef DROPBEAR_RSA + rsa_key * rsakey; +#endif +}; + +typedef struct SIGN_key sign_key; + +sign_key * new_sign_key(); +int buf_get_pub_key(buffer *buf, sign_key *key, int *type); +int buf_get_priv_key(buffer* buf, sign_key *key, int *type); +void buf_put_pub_key(buffer* buf, sign_key *key, int type); +void buf_put_priv_key(buffer* buf, sign_key *key, int type); +void sign_key_free(sign_key *key); +void buf_put_sign(buffer* buf, sign_key *key, int type, + const unsigned char *data, unsigned int len); +#ifdef DROPBEAR_SIGNKEY_VERIFY +int buf_verify(buffer * buf, sign_key *key, const unsigned char *data, + unsigned int len); +char * sign_key_fingerprint(sign_key *key, int type); +#endif + +#endif /* _SIGNKEY_H_ */ diff --git a/ssh.h b/ssh.h new file mode 100644 index 0000000..fbca540 --- /dev/null +++ b/ssh.h @@ -0,0 +1,93 @@ +/* + * 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. */ + +/* This file contains the various numbers in the protocol */ + + +/* message numbers */ +#define SSH_MSG_DISCONNECT 1 +#define SSH_MSG_IGNORE 2 +#define SSH_MSG_UNIMPLEMENTED 3 +#define SSH_MSG_DEBUG 4 +#define SSH_MSG_SERVICE_REQUEST 5 +#define SSH_MSG_SERVICE_ACCEPT 6 +#define SSH_MSG_KEXINIT 20 +#define SSH_MSG_NEWKEYS 21 +#define SSH_MSG_KEXDH_INIT 30 +#define SSH_MSG_KEXDH_REPLY 31 + +/* userauth message numbers */ +#define SSH_MSG_USERAUTH_REQUEST 50 +#define SSH_MSG_USERAUTH_FAILURE 51 +#define SSH_MSG_USERAUTH_SUCCESS 52 +#define SSH_MSG_USERAUTH_BANNER 53 +#define SSH_MSG_USERAUTH_PK_OK 60 + +/* connect message numbers */ +#define SSH_MSG_GLOBAL_REQUEST 80 +#define SSH_MSG_REQUEST_SUCCESS 81 +#define SSH_MSG_REQUEST_FAILURE 82 +#define SSH_MSG_CHANNEL_OPEN 90 +#define SSH_MSG_CHANNEL_OPEN_CONFIRMATION 91 +#define SSH_MSG_CHANNEL_OPEN_FAILURE 92 +#define SSH_MSG_CHANNEL_WINDOW_ADJUST 93 +#define SSH_MSG_CHANNEL_DATA 94 +#define SSH_MSG_CHANNEL_EXTENDED_DATA 95 +#define SSH_MSG_CHANNEL_EOF 96 +#define SSH_MSG_CHANNEL_CLOSE 97 +#define SSH_MSG_CHANNEL_REQUEST 98 +#define SSH_MSG_CHANNEL_SUCCESS 99 +#define SSH_MSG_CHANNEL_FAILURE 100 + +/* extended data types */ +#define SSH_EXTENDED_DATA_STDERR 1 + +/* disconnect codes */ +#define SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1 +#define SSH_DISCONNECT_PROTOCOL_ERROR 2 +#define SSH_DISCONNECT_KEY_EXCHANGE_FAILED 3 +#define SSH_DISCONNECT_RESERVED 4 +#define SSH_DISCONNECT_MAC_ERROR 5 +#define SSH_DISCONNECT_COMPRESSION_ERROR 6 +#define SSH_DISCONNECT_SERVICE_NOT_AVAILABLE 7 +#define SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8 +#define SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE 9 +#define SSH_DISCONNECT_CONNECTION_LOST 10 +#define SSH_DISCONNECT_BY_APPLICATION 11 +#define SSH_DISCONNECT_TOO_MANY_CONNECTIONS 12 +#define SSH_DISCONNECT_AUTH_CANCELLED_BY_USER 13 +#define SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14 +#define SSH_DISCONNECT_ILLEGAL_USER_NAME 15 + +/* service types */ +#define SSH_SERVICE_USERAUTH "ssh-userauth" +#define SSH_SERVICE_USERAUTH_LEN 12 +#define SSH_SERVICE_CONNECTION "ssh-connection" +#define SSH_SERVICE_CONNECTION_LEN 14 + +/* public key types */ +#define SSH_SIGNKEY_DSS "ssh-dss" +#define SSH_SIGNKEY_DSS_LEN 7 +#define SSH_SIGNKEY_RSA "ssh-rsa" +#define SSH_SIGNKEY_RSA_LEN 7 diff --git a/sshpty.c b/sshpty.c new file mode 100644 index 0000000..799a92c --- /dev/null +++ b/sshpty.c @@ -0,0 +1,412 @@ +/* + * Dropbear - a SSH2 server + * + * Copied from OpenSSH-3.5p1 source, modified by Matt Johnston 2003 + * + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Allocating a pseudo-terminal, and making it the controlling tty. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/*RCSID("$OpenBSD: sshpty.c,v 1.7 2002/06/24 17:57:20 deraadt Exp $");*/ + +#include "includes.h" +#include "dbutil.h" +#include "errno.h" +#include "sshpty.h" + +/* Pty allocated with _getpty gets broken if we do I_PUSH:es to it. */ +#if defined(HAVE__GETPTY) || defined(HAVE_OPENPTY) +#undef HAVE_DEV_PTMX +#endif + +#ifdef HAVE_PTY_H +# include +#endif +#if defined(USE_DEV_PTMX) && defined(HAVE_STROPTS_H) +# include +#endif + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +/* + * Allocates and opens a pty. Returns 0 if no pty could be allocated, or + * nonzero if a pty was successfully allocated. On success, open file + * descriptors for the pty and tty sides and the name of the tty side are + * returned (the buffer must be able to hold at least 64 characters). + */ + +int +pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, int namebuflen) +{ +#if defined(HAVE_OPENPTY) + /* exists in recent (4.4) BSDs and OSF/1 */ + char *name; + int i; + + i = openpty(ptyfd, ttyfd, NULL, NULL, NULL); + if (i < 0) { + dropbear_log(LOG_WARNING, + "pty_allocate: openpty: %.100s", strerror(errno)); + return 0; + } + name = ttyname(*ttyfd); + if (!name) { + dropbear_exit("ttyname fails for openpty device"); + } + + strlcpy(namebuf, name, namebuflen); /* possible truncation */ + return 1; +#else /* HAVE_OPENPTY */ +#ifdef HAVE__GETPTY + /* + * _getpty(3) exists in SGI Irix 4.x, 5.x & 6.x -- it generates more + * pty's automagically when needed + */ + char *slave; + + slave = _getpty(ptyfd, O_RDWR, 0622, 0); + if (slave == NULL) { + dropbear_log(LOG_WARNING, + "pty_allocate: _getpty: %.100s", strerror(errno)); + return 0; + } + strlcpy(namebuf, slave, namebuflen); + /* Open the slave side. */ + *ttyfd = open(namebuf, O_RDWR | O_NOCTTY); + if (*ttyfd < 0) { + dropbear_log(LOG_WARNING, + "pty_allocate error: ttyftd open error"); + close(*ptyfd); + return 0; + } + return 1; +#else /* HAVE__GETPTY */ +#if defined(USE_DEV_PTMX) + /* + * This code is used e.g. on Solaris 2.x. (Note that Solaris 2.3 + * also has bsd-style ptys, but they simply do not work.) + * + * Linux systems may have the /dev/ptmx device, but this code won't work. + */ + int ptm; + char *pts; + + ptm = open("/dev/ptmx", O_RDWR | O_NOCTTY); + if (ptm < 0) { + dropbear_log(LOG_WARNING, + "pty_allocate: /dev/ptmx: %.100s", strerror(errno)); + return 0; + } + if (grantpt(ptm) < 0) { + dropbear_log(LOG_WARNING, + "grantpt: %.100s", strerror(errno)); + return 0; + } + if (unlockpt(ptm) < 0) { + dropbear_log(LOG_WARNING, + "unlockpt: %.100s", strerror(errno)); + return 0; + } + pts = ptsname(ptm); + if (pts == NULL) { + dropbear_log(LOG_WARNING, + "Slave pty side name could not be obtained."); + } + strlcpy(namebuf, pts, namebuflen); + *ptyfd = ptm; + + /* Open the slave side. */ + *ttyfd = open(namebuf, O_RDWR | O_NOCTTY); + if (*ttyfd < 0) { + dropbear_log(LOG_ERR, + "error opening pts %.100s: %.100s", namebuf, strerror(errno)); + close(*ptyfd); + return 0; + } +#ifndef HAVE_CYGWIN + /* + * Push the appropriate streams modules, as described in Solaris pts(7). + * HP-UX pts(7) doesn't have ttcompat module. + */ + if (ioctl(*ttyfd, I_PUSH, "ptem") < 0) { + dropbear_log(LOG_WARNING, + "ioctl I_PUSH ptem: %.100s", strerror(errno)); + } + if (ioctl(*ttyfd, I_PUSH, "ldterm") < 0) { + dropbear_log(LOG_WARNING, + "ioctl I_PUSH ldterm: %.100s", strerror(errno)); + } +#ifndef __hpux + if (ioctl(*ttyfd, I_PUSH, "ttcompat") < 0) { + dropbear_log(LOG_WARNING, + "ioctl I_PUSH ttcompat: %.100s", strerror(errno)); + } +#endif +#endif + return 1; +#else /* USE_DEV_PTMX */ +#ifdef HAVE_DEV_PTS_AND_PTC + /* AIX-style pty code. */ + const char *name; + + *ptyfd = open("/dev/ptc", O_RDWR | O_NOCTTY); + if (*ptyfd < 0) { + dropbear_log(LOG_ERR, + "Could not open /dev/ptc: %.100s", strerror(errno)); + return 0; + } + name = ttyname(*ptyfd); + if (!name) { + dropbear_exit("ttyname fails for /dev/ptc device"); + } + strlcpy(namebuf, name, namebuflen); + *ttyfd = open(name, O_RDWR | O_NOCTTY); + if (*ttyfd < 0) { + dropbear_log(LOG_ERR, + "Could not open pty slave side %.100s: %.100s", + name, strerror(errno)); + close(*ptyfd); + return 0; + } + return 1; +#else /* HAVE_DEV_PTS_AND_PTC */ + + /* BSD-style pty code. */ + char buf[64]; + int i; + const char *ptymajors = "pqrstuvwxyzabcdefghijklmnoABCDEFGHIJKLMNOPQRSTUVWXYZ"; + const char *ptyminors = "0123456789abcdef"; + int num_minors = strlen(ptyminors); + int num_ptys = strlen(ptymajors) * num_minors; + struct termios tio; + + for (i = 0; i < num_ptys; i++) { + snprintf(buf, sizeof buf, "/dev/pty%c%c", ptymajors[i / num_minors], + ptyminors[i % num_minors]); + snprintf(namebuf, namebuflen, "/dev/tty%c%c", + ptymajors[i / num_minors], ptyminors[i % num_minors]); + + *ptyfd = open(buf, O_RDWR | O_NOCTTY); + if (*ptyfd < 0) { + /* Try SCO style naming */ + snprintf(buf, sizeof buf, "/dev/ptyp%d", i); + snprintf(namebuf, namebuflen, "/dev/ttyp%d", i); + *ptyfd = open(buf, O_RDWR | O_NOCTTY); + if (*ptyfd < 0) { + continue; + } + } + + /* Open the slave side. */ + *ttyfd = open(namebuf, O_RDWR | O_NOCTTY); + if (*ttyfd < 0) { + dropbear_log(LOG_ERR, + "pty_allocate: %.100s: %.100s", namebuf, strerror(errno)); + close(*ptyfd); + return 0; + } + /* set tty modes to a sane state for broken clients */ + if (tcgetattr(*ptyfd, &tio) < 0) { + dropbear_log(LOG_WARNING, + "ptyallocate: tty modes failed: %.100s", strerror(errno)); + } else { + tio.c_lflag |= (ECHO | ISIG | ICANON); + tio.c_oflag |= (OPOST | ONLCR); + tio.c_iflag |= ICRNL; + + /* Set the new modes for the terminal. */ + if (tcsetattr(*ptyfd, TCSANOW, &tio) < 0) { + dropbear_log(LOG_WARNING, + "Setting tty modes for pty failed: %.100s", + strerror(errno)); + } + } + + return 1; + } + dropbear_log(LOG_WARNING, "failed to open any /dev/pty?? devices"); + return 0; +#endif /* HAVE_DEV_PTS_AND_PTC */ +#endif /* USE_DEV_PTMX */ +#endif /* HAVE__GETPTY */ +#endif /* HAVE_OPENPTY */ +} + +/* Releases the tty. Its ownership is returned to root, and permissions to 0666. */ + +void +pty_release(const char *tty_name) +{ + if (chown(tty_name, (uid_t) 0, (gid_t) 0) < 0 + && (errno != ENOENT)) { + dropbear_log(LOG_ERR, + "chown %.100s 0 0 failed: %.100s", tty_name, strerror(errno)); + } + if (chmod(tty_name, (mode_t) 0666) < 0 + && (errno != ENOENT)) { + dropbear_log(LOG_ERR, + "chmod %.100s 0666 failed: %.100s", tty_name, strerror(errno)); + } +} + +/* Makes the tty the processes controlling tty and sets it to sane modes. */ + +void +pty_make_controlling_tty(int *ttyfd, const char *tty_name) +{ + int fd; +#ifdef USE_VHANGUP + void *old; +#endif /* USE_VHANGUP */ + + /* Solaris has a problem with TIOCNOTTY for a bg process, so + * we disable the signal which would STOP the process - matt */ + signal(SIGTTOU, SIG_IGN); + + /* First disconnect from the old controlling tty. */ +#ifdef TIOCNOTTY + fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); + if (fd >= 0) { + (void) ioctl(fd, TIOCNOTTY, NULL); + close(fd); + } +#endif /* TIOCNOTTY */ + if (setsid() < 0) { + dropbear_log(LOG_ERR, + "setsid: %.100s", strerror(errno)); + } + + /* + * Verify that we are successfully disconnected from the controlling + * tty. + */ + fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); + if (fd >= 0) { + dropbear_log(LOG_ERR, + "Failed to disconnect from controlling tty.\n"); + close(fd); + } + /* Make it our controlling tty. */ +#ifdef TIOCSCTTY + if (ioctl(*ttyfd, TIOCSCTTY, NULL) < 0) { + dropbear_log(LOG_ERR, + "ioctl(TIOCSCTTY): %.100s", strerror(errno)); + } +#endif /* TIOCSCTTY */ +#ifdef HAVE_NEWS4 + if (setpgrp(0,0) < 0) { + dropbear_log(LOG_ERR, + error("SETPGRP %s",strerror(errno))); + } +#endif /* HAVE_NEWS4 */ +#ifdef USE_VHANGUP + old = mysignal(SIGHUP, SIG_IGN); + vhangup(); + mysignal(SIGHUP, old); +#endif /* USE_VHANGUP */ + fd = open(tty_name, O_RDWR); + if (fd < 0) { + dropbear_log(LOG_ERR, + "%.100s: %.100s", tty_name, strerror(errno)); + } else { +#ifdef USE_VHANGUP + close(*ttyfd); + *ttyfd = fd; +#else /* USE_VHANGUP */ + close(fd); +#endif /* USE_VHANGUP */ + } + /* Verify that we now have a controlling tty. */ + fd = open(_PATH_TTY, O_WRONLY); + if (fd < 0) { + dropbear_log(LOG_ERR, + "open /dev/tty failed - could not set controlling tty: %.100s", + strerror(errno)); + } else { + close(fd); + } +} + +/* Changes the window size associated with the pty. */ + +void +pty_change_window_size(int ptyfd, int row, int col, + int xpixel, int ypixel) +{ + struct winsize w; + + w.ws_row = row; + w.ws_col = col; + w.ws_xpixel = xpixel; + w.ws_ypixel = ypixel; + (void) ioctl(ptyfd, TIOCSWINSZ, &w); +} + +void +pty_setowner(struct passwd *pw, const char *tty_name) +{ + struct group *grp; + gid_t gid; + mode_t mode; + struct stat st; + + /* Determine the group to make the owner of the tty. */ + grp = getgrnam("tty"); + if (grp) { + gid = grp->gr_gid; + mode = S_IRUSR | S_IWUSR | S_IWGRP; + } else { + gid = pw->pw_gid; + mode = S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH; + } + + /* + * Change owner and mode of the tty as required. + * Warn but continue if filesystem is read-only and the uids match/ + * tty is owned by root. + */ + if (stat(tty_name, &st)) { + dropbear_exit("pty_setowner: stat(%.101s) failed: %.100s", + tty_name, strerror(errno)); + } + + if (st.st_uid != pw->pw_uid || st.st_gid != gid) { + if (chown(tty_name, pw->pw_uid, gid) < 0) { + if (errno == EROFS && + (st.st_uid == pw->pw_uid || st.st_uid == 0)) { + dropbear_log(LOG_ERR, + "chown(%.100s, %u, %u) failed: %.100s", + tty_name, (unsigned int)pw->pw_uid, (unsigned int)gid, + strerror(errno)); + } else { + dropbear_exit("chown(%.100s, %u, %u) failed: %.100s", + tty_name, (unsigned int)pw->pw_uid, (unsigned int)gid, + strerror(errno)); + } + } + } + + if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode) { + if (chmod(tty_name, mode) < 0) { + if (errno == EROFS && + (st.st_mode & (S_IRGRP | S_IROTH)) == 0) { + dropbear_log(LOG_ERR, + "chmod(%.100s, 0%o) failed: %.100s", + tty_name, mode, strerror(errno)); + } else { + dropbear_exit("chmod(%.100s, 0%o) failed: %.100s", + tty_name, mode, strerror(errno)); + } + } + } +} diff --git a/sshpty.h b/sshpty.h new file mode 100644 index 0000000..cf72072 --- /dev/null +++ b/sshpty.h @@ -0,0 +1,28 @@ +/* $OpenBSD: sshpty.h,v 1.4 2002/03/04 17:27:39 stevesk Exp $ */ + +/* + * Copied from openssh-3.5p1 source by Matt Johnston 2003 + * + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Functions for allocating a pseudo-terminal and making it the controlling + * tty. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#ifndef SSHPTY_H +#define SSHPTY_H + +int pty_allocate(int *, int *, char *, int); +void pty_release(const char *); +void pty_make_controlling_tty(int *, const char *); +void pty_change_window_size(int, int, int, int, int); +void pty_setowner(struct passwd *, const char *); + +#endif /* SSHPTY_H */ diff --git a/svr-agentfwd.c b/svr-agentfwd.c new file mode 100644 index 0000000..c4958c4 --- /dev/null +++ b/svr-agentfwd.c @@ -0,0 +1,247 @@ +/* + * 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. */ + +/* This file (agentfwd.c) handles authentication agent forwarding, for OpenSSH + * style agents. */ + +#include "includes.h" + +#ifndef DISABLE_AGENTFWD + +#include "agentfwd.h" +#include "session.h" +#include "ssh.h" +#include "dbutil.h" +#include "chansession.h" +#include "channel.h" +#include "packet.h" +#include "buffer.h" +#include "random.h" + +#define AGENTDIRPREFIX "/tmp/dropbear-" + +static int send_msg_channel_open_agent(int fd); +static int bindagent(struct ChanSess * chansess); + +/* Handles client requests to start agent forwarding, sets up listening socket. + * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +int agentreq(struct ChanSess * chansess) { + + if (chansess->agentfd != -1) { + return DROPBEAR_FAILURE; + } + + /* create listening socket */ + chansess->agentfd = socket(PF_UNIX, SOCK_STREAM, 0); + if (chansess->agentfd < 0) { + goto fail; + } + + /* create the unix socket dir and file */ + if (bindagent(chansess) == DROPBEAR_FAILURE) { + return DROPBEAR_FAILURE; + } + + /* listen */ + if (listen(chansess->agentfd, 20) < 0) { + goto fail; + } + + /* set non-blocking */ + if (fcntl(chansess->agentfd, F_SETFL, O_NONBLOCK) < 0) { + goto fail; + } + + /* channel.c's channel fd code will handle the socket now */ + + /* set the maxfd so that select() loop will notice it */ + ses.maxfd = MAX(ses.maxfd, chansess->agentfd); + + return DROPBEAR_SUCCESS; + +fail: + /* cleanup */ + agentcleanup(chansess); + + return DROPBEAR_FAILURE; +} + +/* accepts a connection on the forwarded socket and opens a new channel for it + * back to the client */ +/* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +int agentaccept(struct ChanSess * chansess) { + + int fd; + + fd = accept(chansess->agentfd, NULL, NULL); + if (fd < 0) { + return DROPBEAR_FAILURE; + } + + return send_msg_channel_open_agent(fd); + +} + +/* set up the environment variable pointing to the socket. This is called + * just before command/shell execution, after dropping priveleges */ +void agentset(struct ChanSess * chansess) { + + char *path = NULL; + int len; + + if (chansess->agentfd == -1) { + return; + } + + /* 2 for "/" and "\0" */ + len = strlen(chansess->agentdir) + strlen(chansess->agentfile) + 2; + + path = m_malloc(len); + snprintf(path, len, "%s/%s", chansess->agentdir, chansess->agentfile); + addnewvar("SSH_AUTH_SOCK", path); + m_free(path); +} + +/* close the socket, remove the socket-file */ +void agentcleanup(struct ChanSess * chansess) { + + char *path = NULL; + uid_t uid; + gid_t gid; + int len; + + if (chansess->agentfd == -1) { + return; + } + + close(chansess->agentfd); + + /* Remove the dir as the user. That way they can't cause problems except + * for themselves */ + uid = getuid(); + gid = getgid(); + if ((setegid(ses.authstate.pw->pw_gid)) < 0 || + (seteuid(ses.authstate.pw->pw_uid)) < 0) { + dropbear_exit("failed to set euid"); + } + + /* 2 for "/" and "\0" */ + len = strlen(chansess->agentdir) + strlen(chansess->agentfile) + 2; + + path = m_malloc(len); + snprintf(path, len, "%s/%s", chansess->agentdir, chansess->agentfile); + unlink(path); + m_free(path); + + rmdir(chansess->agentdir); + + if ((seteuid(uid)) < 0 || + (setegid(gid)) < 0) { + dropbear_exit("failed to revert euid"); + } + + m_free(chansess->agentfile); + m_free(chansess->agentdir); + +} + +/* helper for accepting an agent request */ +static int send_msg_channel_open_agent(int fd) { + + if (send_msg_channel_open_init(fd, CHANNEL_ID_AGENT, + "auth-agent@openssh.com") == DROPBEAR_SUCCESS) { + encrypt_packet(); + return DROPBEAR_SUCCESS; + } else { + return DROPBEAR_FAILURE; + } +} + +/* helper for creating the agent socket-file + returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +static int bindagent(struct ChanSess * chansess) { + + struct sockaddr_un addr; + unsigned int prefix; + char path[sizeof(addr.sun_path)], sockfile[sizeof(addr.sun_path)]; + mode_t mode; + int i; + uid_t uid; + gid_t gid; + int ret = DROPBEAR_FAILURE; + + /* drop to user privs to make the dir/file */ + uid = getuid(); + gid = getgid(); + if ((setegid(ses.authstate.pw->pw_gid)) < 0 || + (seteuid(ses.authstate.pw->pw_uid)) < 0) { + dropbear_exit("failed to set euid"); + } + + memset((void*)&addr, 0x0, sizeof(addr)); + addr.sun_family = AF_UNIX; + + mode = S_IRWXU; + + for (i = 0; i < 20; i++) { + genrandom((unsigned char*)&prefix, sizeof(prefix)); + /* we want 32 bits (8 hex digits) - "/tmp/dropbear-f19c62c0" */ + snprintf(path, sizeof(path), AGENTDIRPREFIX "%.8x", prefix); + + if (mkdir(path, mode) == 0) { + goto bindsocket; + } + if (errno != EEXIST) { + break; + } + } + /* couldn't make a dir */ + goto out; + +bindsocket: + /* Format is "/tmp/dropbear-0246dead/auth-d00f7654-23". + * The "23" is the file desc, the random data is to avoid collisions + * between subsequent user processes reusing socket fds (odds are now + * 1/(2^64) */ + genrandom((unsigned char*)&prefix, sizeof(prefix)); + snprintf(sockfile, sizeof(sockfile), "auth-%.8x-%d", prefix, + chansess->agentfd); + snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", path, sockfile); + + if (bind(chansess->agentfd, (struct sockaddr*)&addr, sizeof(addr)) == 0) { + chansess->agentdir = strdup(path); + chansess->agentfile = strdup(sockfile); + ret = DROPBEAR_SUCCESS; + } + + +out: + if ((seteuid(uid)) < 0 || + (setegid(gid)) < 0) { + dropbear_exit("failed to revert euid"); + } + return ret; +} + +#endif diff --git a/svr-algo.c b/svr-algo.c new file mode 100644 index 0000000..33b9471 --- /dev/null +++ b/svr-algo.c @@ -0,0 +1,74 @@ +#include "algo.h" +#include "dbutil.h" + +/* match the first algorithm in the comma-separated list in buf which is + * also in localalgos[], or return NULL on failure. + * (*goodguess) is set to 1 if the preferred client/server algos match, + * 0 otherwise. This is used for checking if the kexalgo/hostkeyalgos are + * guessed correctly */ +algo_type * svr_buf_match_algo(buffer* buf, algo_type localalgos[], + int *goodguess) +{ + + unsigned char * algolist = NULL; + unsigned char * remotealgos[MAX_PROPOSED_ALGO]; + unsigned int len; + unsigned int count, i, j; + algo_type * ret = NULL; + + /* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */ + algolist = buf_getstring(buf, &len); + /* Debug this */ + TRACE(("buf_match_algo: %s", algolist)); + if (len > MAX_PROPOSED_ALGO*(MAX_NAME_LEN+1)) { + goto out; /* just a sanity check, no other use */ + } + + /* remotealgos will contain a list of the strings parsed out */ + /* We will have at least one string (even if it's just "") */ + remotealgos[0] = algolist; + count = 1; + /* Iterate through, replacing ','s with NULs, to split it into + * words. */ + for (i = 0; i < len; i++) { + if (algolist[i] == '\0') { + /* someone is trying something strange */ + goto out; + } + if (algolist[i] == ',') { + algolist[i] = '\0'; + remotealgos[count] = &algolist[i+1]; + count++; + } + if (count == MAX_PROPOSED_ALGO) { + break; + } + } + + /* iterate and find the first match */ + for (i = 0; i < count; i++) { + + len = strlen(remotealgos[i]); + + for (j = 0; localalgos[j].name != NULL; j++) { + if (localalgos[j].usable) { + if (len == strlen(localalgos[j].name) && + strncmp(localalgos[j].name, remotealgos[i], len) == 0) { + /* set if it was a good guess */ + if (i == 0 && j == 0) { + *goodguess = 1; + } else { + *goodguess = 0; + } + /* set the algo to return */ + ret = &localalgos[j]; + goto out; + } + } + } + } + +out: + m_free(algolist); + return ret; +} diff --git a/svr-auth.c b/svr-auth.c new file mode 100644 index 0000000..46ca70a --- /dev/null +++ b/svr-auth.c @@ -0,0 +1,351 @@ +/* + * 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. */ + +/* This file (auth.c) handles authentication requests, passing it to the + * particular type (auth-passwd, auth-pubkey). */ + +#include "includes.h" +#include "dbutil.h" +#include "session.h" +#include "buffer.h" +#include "ssh.h" +#include "packet.h" +#include "auth.h" +#include "authpasswd.h" +#include "authpubkey.h" + +static void authclear(); +static int checkusername(unsigned char *username, unsigned int userlen); +static void send_msg_userauth_banner(); + +/* initialise the first time for a session, resetting all parameters */ +void authinitialise() { + + svr_ses.authstate.failcount = 0; + authclear(); + +} + +/* Reset the auth state, but don't reset the failcount. This is for if the + * user decides to try with a different username etc, and is also invoked + * on initialisation */ +static void authclear() { + + svr_ses.authstate.authdone = 0; + svr_ses.authstate.pw = NULL; + svr_ses.authstate.username = NULL; + svr_ses.authstate.printableuser = NULL; + svr_ses.authstate.authtypes = 0; +#ifdef DROPBEAR_PUBKEY_AUTH + svr_ses.authstate.authtypes |= AUTH_TYPE_PUBKEY; +#endif +#ifdef DROPBEAR_PASSWORD_AUTH + if (!ses.opts->noauthpass) { + svr_ses.authstate.authtypes |= AUTH_TYPE_PASSWORD; + } +#endif + +} + +/* Send a banner message if specified to the client. The client might + * ignore this, but possibly serves as a legal "no trespassing" sign */ +static void send_msg_userauth_banner() { + + TRACE(("enter send_msg_userauth_banner")); + if (ses.opts->banner == NULL) { + TRACE(("leave send_msg_userauth_banner: banner is NULL")); + return; + } + + CHECKCLEARTOWRITE(); + + buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_BANNER); + buf_putstring(ses.writepayload, buf_getptr(ses.opts->banner, + ses.opts->banner->len), ses.opts->banner->len); + buf_putstring(ses.writepayload, "en", 2); + + encrypt_packet(); + buf_free(ses.opts->banner); + ses.opts->banner = NULL; + + TRACE(("leave send_msg_userauth_banner")); +} + +/* handle a userauth request, check validity, pass to password or pubkey + * checking, and handle success or failure */ +void recv_msg_userauth_request() { + + unsigned char *username, *servicename, *methodname; + unsigned int userlen, servicelen, methodlen; + + TRACE(("enter recv_msg_userauth_request")); + + /* ignore packets if auth is already done */ + if (svr_ses.authstate.authdone == 1) { + return; + } + + /* send the banner if it exists, it will only exist once */ + if (ses.opts->banner) { + send_msg_userauth_banner(); + } + + + username = buf_getstring(ses.payload, &userlen); + servicename = buf_getstring(ses.payload, &servicelen); + methodname = buf_getstring(ses.payload, &methodlen); + + /* only handle 'ssh-connection' currently */ + if (servicelen != SSH_SERVICE_CONNECTION_LEN + && (strncmp(servicename, SSH_SERVICE_CONNECTION, + SSH_SERVICE_CONNECTION_LEN) != 0)) { + + /* TODO - disconnect here */ + m_free(username); + m_free(servicename); + m_free(methodname); + dropbear_exit("unknown service in auth"); + } + + /* user wants to know what methods are supported */ + if (methodlen == AUTH_METHOD_NONE_LEN && + strncmp(methodname, AUTH_METHOD_NONE, + AUTH_METHOD_NONE_LEN) == 0) { + send_msg_userauth_failure(0, 0); + goto out; + } + + /* check username is good before continuing */ + if (checkusername(username, userlen) == DROPBEAR_FAILURE) { + /* username is invalid/no shell/etc - send failure */ + TRACE(("sending checkusername failure")); + send_msg_userauth_failure(0, 1); + goto out; + } + +#ifdef DROPBEAR_PASSWORD_AUTH + if (!ses.opts->noauthpass && + !(ses.opts->norootpass && svr_ses.authstate.pw->pw_uid == 0) ) { + /* user wants to try password auth */ + if (methodlen == AUTH_METHOD_PASSWORD_LEN && + strncmp(methodname, AUTH_METHOD_PASSWORD, + AUTH_METHOD_PASSWORD_LEN) == 0) { + passwordauth(username, userlen); + goto out; + } + } +#endif + +#ifdef DROPBEAR_PUBKEY_AUTH + /* user wants to try pubkey auth */ + if (methodlen == AUTH_METHOD_PUBKEY_LEN && + strncmp(methodname, AUTH_METHOD_PUBKEY, + AUTH_METHOD_PUBKEY_LEN) == 0) { + pubkeyauth(username, userlen); + goto out; + } +#endif + + /* nothing matched, we just fail */ + send_msg_userauth_failure(0, 1); + +out: + + m_free(username); + m_free(servicename); + m_free(methodname); +} + +/* Check that the username exists, has a non-empty password, and has a valid + * shell. + * returns DROPBEAR_SUCCESS on valid username, DROPBEAR_FAILURE on failure */ +static int checkusername(unsigned char *username, unsigned int userlen) { + + char* listshell = NULL; + char* usershell = NULL; + + TRACE(("enter checkusername")); + if (userlen > MAX_USERNAME_LEN) { + return DROPBEAR_FAILURE; + } + + /* new user or username has changed */ + if (svr_ses.authstate.username == NULL || + strcmp(username, svr_ses.authstate.username) != 0) { + /* the username needs resetting */ + if (svr_ses.authstate.username != NULL) { + dropbear_log(LOG_WARNING, "client trying multiple usernames"); + m_free(svr_ses.authstate.username); + } + authclear(); + svr_ses.authstate.pw = getpwnam((char*)username); + svr_ses.authstate.username = strdup(username); + m_free(svr_ses.authstate.printableuser); + } + + /* check that user exists */ + if (svr_ses.authstate.pw == NULL) { + TRACE(("leave checkusername: user '%s' doesn't exist", username)); + dropbear_log(LOG_WARNING, + "login attempt for nonexistent user"); + send_msg_userauth_failure(0, 1); + return DROPBEAR_FAILURE; + } + + /* We can set it once we know its a real user */ + svr_ses.authstate.printableuser = strdup(svr_ses.authstate.pw->pw_name); + + /* check for non-root if desired */ + if (ses.opts->norootlogin && svr_ses.authstate.pw->pw_uid == 0) { + TRACE(("leave checkusername: root login disabled")); + dropbear_log(LOG_WARNING, "root login rejected"); + send_msg_userauth_failure(0, 1); + return DROPBEAR_FAILURE; + } + + /* check for an empty password */ + if (svr_ses.authstate.pw->pw_passwd[0] == '\0') { + TRACE(("leave checkusername: empty pword")); + dropbear_log(LOG_WARNING, "user '%s' has blank password, rejected", + svr_ses.authstate.printableuser); + send_msg_userauth_failure(0, 1); + return DROPBEAR_FAILURE; + } + + TRACE(("shell is %s", svr_ses.authstate.pw->pw_shell)); + + /* check that the shell is set */ + usershell = svr_ses.authstate.pw->pw_shell; + if (usershell[0] == '\0') { + /* empty shell in /etc/passwd means /bin/sh according to passwd(5) */ + usershell = "/bin/sh"; + } + + /* check the shell is valid. If /etc/shells doesn't exist, getusershell() + * should return some standard shells like "/bin/sh" and "/bin/csh" (this + * is platform-specific) */ + setusershell(); + while ((listshell = getusershell()) != NULL) { + TRACE(("test shell is '%s'", listshell)); + if (strcmp(listshell, usershell) == 0) { + /* have a match */ + goto goodshell; + } + } + /* no matching shell */ + endusershell(); + TRACE(("no matching shell")); + dropbear_log(LOG_WARNING, "user '%s' has invalid shell, rejected", + svr_ses.authstate.printableuser); + send_msg_userauth_failure(0, 1); + return DROPBEAR_FAILURE; + +goodshell: + endusershell(); + TRACE(("matching shell")); + + TRACE(("uid = %d", svr_ses.authstate.pw->pw_uid)); + TRACE(("leave checkusername")); + return DROPBEAR_SUCCESS; + +} + +/* Send a failure message to the client, in responds to a userauth_request. + * Partial indicates whether to set the "partial success" flag, + * incrfail is whether to count this failure in the failure count (which + * is limited. This function also handles disconnection after too many + * failures */ +void send_msg_userauth_failure(int partial, int incrfail) { + + buffer *typebuf; + + TRACE(("enter send_msg_userauth_failure")); + + CHECKCLEARTOWRITE(); + + buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_FAILURE); + + /* put a list of allowed types */ + typebuf = buf_new(30); /* long enough for PUBKEY and PASSWORD */ + + if (svr_ses.authstate.authtypes & AUTH_TYPE_PUBKEY) { + buf_putbytes(typebuf, AUTH_METHOD_PUBKEY, AUTH_METHOD_PUBKEY_LEN); + if (svr_ses.authstate.authtypes & AUTH_TYPE_PASSWORD) { + buf_putbyte(typebuf, ','); + } + } + + if (svr_ses.authstate.authtypes & AUTH_TYPE_PASSWORD) { + buf_putbytes(typebuf, AUTH_METHOD_PASSWORD, AUTH_METHOD_PASSWORD_LEN); + } + + buf_setpos(typebuf, 0); + buf_putstring(ses.writepayload, buf_getptr(typebuf, typebuf->len), + typebuf->len); + buf_free(typebuf); + + buf_putbyte(ses.writepayload, partial ? 1 : 0); + encrypt_packet(); + + if (incrfail) { + usleep(300000); /* XXX improve this */ + svr_ses.authstate.failcount++; + } + + if (svr_ses.authstate.failcount >= MAX_AUTH_TRIES) { + char * userstr; + /* XXX - send disconnect ? */ + TRACE(("Max auth tries reached, exiting")); + + if (svr_ses.authstate.printableuser == NULL) { + userstr = "is invalid"; + } else { + userstr = svr_ses.authstate.printableuser; + } + dropbear_exit("Max auth tries reached - user %s", userstr); + } + + TRACE(("leave send_msg_userauth_failure")); +} + +/* Send a success message to the user, and set the "authdone" flag */ +void send_msg_userauth_success() { + + TRACE(("enter send_msg_userauth_success")); + + CHECKCLEARTOWRITE(); + + buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_SUCCESS); + encrypt_packet(); + + svr_ses.authstate.authdone = 1; + + /* Remove from the list of pre-auth sockets. Should be m_close(), since if + * we fail, we might end up leaking connection slots, and disallow new + * logins - a nasty situation. */ + m_close(svr_ses.childpipe); + + TRACE(("leave send_msg_userauth_success")); + +} diff --git a/svr-authpasswd.c b/svr-authpasswd.c new file mode 100644 index 0000000..859cfd5 --- /dev/null +++ b/svr-authpasswd.c @@ -0,0 +1,108 @@ +/* + * 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. */ + +/* Validates a user password */ + +#include "includes.h" +#include "session.h" +#include "buffer.h" +#include "dbutil.h" +#include "auth.h" +#include "authpasswd.h" + +#ifdef DROPBEAR_PASSWORD_AUTH + +/* Process a password auth request, sending success or failure messages as + * appropriate */ +void passwordauth() { + +#ifdef HAVE_SHADOW_H + struct spwd *spasswd; +#endif + char * passwdcrypt; /* the crypt from /etc/passwd or /etc/shadow */ + char * testcrypt; /* crypt generated from the user's password sent */ + unsigned char * password; + unsigned int passwordlen; + + unsigned char changepw; + + passwdcrypt = svr_ses.authstate.pw->pw_passwd; +#ifdef HAVE_SHADOW_H + /* get the shadow password if possible */ + spasswd = getspnam(svr_ses.authstate.pw->pw_name); + if (spasswd != NULL && spasswd->sp_pwdp != NULL) { + passwdcrypt = spasswd->sp_pwdp; + } +#endif + +#ifdef DEBUG_HACKCRYPT + /* debugging crypt for non-root testing with shadows */ + passwdcrypt = DEBUG_HACKCRYPT; +#endif + + /* check for empty password - need to do this again here + * since the shadow password may differ to that tested + * in auth.c */ + if (passwdcrypt[0] == '\0') { + dropbear_log(LOG_WARNING, "user '%s' has blank password, rejected", + svr_ses.authstate.printableuser); + send_msg_userauth_failure(0, 1); + return; + } + + /* check if client wants to change password */ + changepw = buf_getbyte(ses.payload); + if (changepw) { + /* not implemented by this server */ + send_msg_userauth_failure(0, 1); + return; + } + + password = buf_getstring(ses.payload, &passwordlen); + + /* clear the buffer containing the password */ + buf_incrpos(ses.payload, -passwordlen - 4); + m_burn(buf_getptr(ses.payload, passwordlen + 4), passwordlen + 4); + + /* the first bytes of passwdcrypt are the salt */ + testcrypt = crypt((char*)password, passwdcrypt); + + if (strcmp(testcrypt, passwdcrypt) == 0) { + /* successful authentication */ + dropbear_log(LOG_NOTICE, + "password auth succeeded for '%s'", + svr_ses.authstate.printableuser); + send_msg_userauth_success(); + } else { + dropbear_log(LOG_WARNING, + "bad password attempt for '%s'", + svr_ses.authstate.printableuser); + send_msg_userauth_failure(0, 1); + } + + m_burn(password, passwordlen); + m_free(password); +} + +#endif /* DROPBEAR_PASSWORD_AUTH */ diff --git a/svr-authpubkey.c b/svr-authpubkey.c new file mode 100644 index 0000000..e3e6947 --- /dev/null +++ b/svr-authpubkey.c @@ -0,0 +1,424 @@ +/* + * 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. */ + +/* Process a pubkey auth request */ + +#include "includes.h" +#include "session.h" +#include "dbutil.h" +#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 + +#define MIN_AUTHKEYS_LINE 10 /* "ssh-rsa AB" - short but doesn't matter */ +#define MAX_AUTHKEYS_LINE 1000 /* max length of a line in authkeys */ + +static int checkpubkey(unsigned char* algo, unsigned int algolen, + unsigned char* keyblob, unsigned int keybloblen); +static int checkpubkeyperms(); +static void send_msg_userauth_pk_ok(unsigned char* algo, unsigned int algolen, + unsigned char* keyblob, unsigned int keybloblen); +static int checkfileperm(char * filename); +static int getauthline(buffer * line, FILE * authfile); + +/* process a pubkey auth request, sending success or failure message as + * appropriate */ +void pubkeyauth() { + + 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 int keybloblen; + buffer * signbuf = NULL; + unsigned int sigoffset; + sign_key * key = NULL; + char* fp = NULL; + int type = -1; + + TRACE(("enter pubkeyauth")); + + /* 0 indicates user just wants to check if key can be used, 1 is an + * actual attempt*/ + testkey = (buf_getbyte(ses.payload) == 0); + + algo = buf_getstring(ses.payload, &algolen); + keybloblen = buf_getint(ses.payload); + keyblob = buf_getptr(ses.payload, keybloblen); + + /* check if the key is valid */ + if (checkpubkey(algo, algolen, keyblob, keybloblen) == DROPBEAR_FAILURE) { + send_msg_userauth_failure(0, 0); + goto out; + } + + /* let them know that the key is ok to use */ + if (testkey) { + send_msg_userauth_pk_ok(algo, algolen, keyblob, keybloblen); + goto out; + } + + /* now we can actually verify the signature */ + + /* get the key */ + key = new_sign_key(); + type = DROPBEAR_SIGNKEY_ANY; + if (buf_get_pub_key(ses.payload, key, &type) == DROPBEAR_FAILURE) { + send_msg_userauth_failure(0, 1); + goto out; + } + + /* create the data which has been signed - this a string containing + * session_id, concatenated with the payload packet up to the signature */ + signbuf = buf_new(ses.payload->pos + 4 + SHA1_HASH_SIZE); + buf_putstring(signbuf, ses.session_id, SHA1_HASH_SIZE); + sigoffset = ses.payload->pos; + buf_setpos(ses.payload, 0); + memcpy(buf_getwriteptr(signbuf, sigoffset), + buf_getptr(ses.payload, sigoffset), sigoffset); + buf_incrwritepos(signbuf, sigoffset); + buf_setpos(ses.payload, sigoffset); + + buf_setpos(signbuf, 0); + /* ... and finally verify the signature */ + fp = sign_key_fingerprint(key, type); + if (buf_verify(ses.payload, key, buf_getptr(signbuf, signbuf->len), + signbuf->len) == DROPBEAR_SUCCESS) { + dropbear_log(LOG_NOTICE, + "pubkey auth succeeded for '%s' with key %s", + svr_ses.authstate.printableuser, fp); + send_msg_userauth_success(); + } else { + dropbear_log(LOG_WARNING, + "pubkey auth bad signature for '%s' with key %s", + svr_ses.authstate.printableuser, fp); + send_msg_userauth_failure(0, 1); + } + m_free(fp); + +out: + /* cleanup stuff */ + if (signbuf) { + buf_free(signbuf); + } + if (algo) { + m_free(algo); + } + if (key) { + sign_key_free(key); + key = NULL; + } + TRACE(("leave pubkeyauth")); +} + +/* Reply that the key is valid for auth, this is sent when the user sends + * a straight copy of their pubkey to test, to avoid having to perform + * expensive signing operations with a worthless key */ +static void send_msg_userauth_pk_ok(unsigned char* algo, unsigned int algolen, + unsigned char* keyblob, unsigned int keybloblen) { + + TRACE(("enter send_msg_userauth_pk_ok")); + CHECKCLEARTOWRITE(); + + buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_PK_OK); + buf_putstring(ses.writepayload, algo, algolen); + buf_putstring(ses.writepayload, keyblob, keybloblen); + + encrypt_packet(); + TRACE(("leave send_msg_userauth_pk_ok")); + +} + +/* Checks whether a specified publickey (and associated algorithm) is an + * acceptable key for authentication */ +/* Returns DROPBEAR_SUCCESS if key is ok for auth, DROPBEAR_FAILURE otherwise */ +static int checkpubkey(unsigned char* algo, unsigned int algolen, + unsigned char* keyblob, unsigned int keybloblen) { + + FILE * authfile = NULL; + char * filename = NULL; + int ret = DROPBEAR_FAILURE; + buffer * line = NULL; + buffer * decodekey = NULL; + unsigned long decodekeylen; + unsigned char* filealgo = NULL; + unsigned int filealgolen; + unsigned int len, pos; + + TRACE(("enter checkpubkey")); + + /* check that we can use the algo */ + if (have_algo(algo, algolen, sshhostkey) == DROPBEAR_FAILURE) { + dropbear_log(LOG_WARNING, + "pubkey auth attempt with unknown algo for '%s'", + svr_ses.authstate.printableuser); + goto out; + } + + /* check file permissions, also whether file exists */ + if (checkpubkeyperms() == DROPBEAR_FAILURE) { + TRACE(("bad authorized_keys permissions, or file doesn't exist")); + goto out; + } + + /* we don't need to check pw and pw_dir for validity, since + * its been done in checkpubkeyperms. */ + len = strlen(svr_ses.authstate.pw->pw_dir); + /* allocate max required pathname storage, + * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */ + filename = m_malloc(len + 22); + snprintf(filename, len + 22, "%s/.ssh/authorized_keys", + svr_ses.authstate.pw->pw_dir); + + /* open the file */ + authfile = fopen(filename, "r"); + if (authfile == NULL) { + goto out; + } + TRACE(("checkpubkey: opened authorized_keys OK")); + + line = buf_new(MAX_AUTHKEYS_LINE); + + /* iterate through the lines */ + do { + /* free reused vars */ + if (decodekey) { + buf_free(decodekey); + decodekey = NULL; + } + m_free(filealgo); + + if (getauthline(line, authfile) == DROPBEAR_FAILURE) { + /* EOF reached */ + TRACE(("checkpubkey: authorized_keys EOF reached")); + break; + } + + if (line->len < MIN_AUTHKEYS_LINE) { + TRACE(("checkpubkey: line too short")); + continue; /* line is too short for it to be a valid key */ + } + + /* check the key type - this also stops us from using keys + * which have options with them */ + if (strncmp(buf_getptr(line, algolen), algo, algolen) != 0) { + continue; + } + buf_incrpos(line, algolen); + + /* check for space (' ') character */ + if (buf_getbyte(line) != ' ') { + TRACE(("checkpubkey: space character expected, isn't there")); + continue; + } + + /* truncate the line at the space after the base64 data */ + pos = line->pos; + for (len = 0; line->pos < line->len; len++) { + if (buf_getbyte(line) == ' ') break; + } + buf_setpos(line, pos); + buf_setlen(line, line->pos + len); + + TRACE(("checkpubkey: line pos = %d len = %d", line->pos, line->len)); + + /* now we have the actual data */ + decodekeylen = (line->len - line->pos) * 2; + decodekey = buf_new(decodekeylen); + if (base64_decode(buf_getptr(line, line->len - line->pos), + line->len - line->pos, + buf_getwriteptr(decodekey, decodekey->size), + &decodekeylen) != CRYPT_OK) { + TRACE(("checkpubkey: base64 decode failed")); + continue; + } + TRACE(("checkpubkey: base64_decode success")); + buf_incrlen(decodekey, decodekeylen); + + /* compare the keys */ + if (decodekeylen != keybloblen || memcmp( + buf_getptr(decodekey, decodekey->len), + keyblob, decodekey->len) != 0) { + TRACE(("checkpubkey: compare failed")); + continue; + } + + /* and also check that the algo specified and the algo in the key + * itself match */ + filealgo = buf_getstring(decodekey, &filealgolen); + if (filealgolen != algolen || memcmp(filealgo, algo, algolen) != 0) { + TRACE(("checkpubkey: algo match failed")); + continue; + } + + /* now we know this key is good */ + ret = DROPBEAR_SUCCESS; + break; + + } while (1); + +out: + if (authfile) { + fclose(authfile); + } + if (line) { + buf_free(line); + } + if (decodekey) { + buf_free(decodekey); + } + m_free(filename); + m_free(filealgo); + TRACE(("leave checkpubkey: ret=%d", ret)); + return ret; +} + +/* get a line from the file into buffer in the style expected for an + * authkeys file. + * Will return DROPBEAR_SUCCESS if data is read, or DROPBEAR_FAILURE on EOF.*/ +static int getauthline(buffer * line, FILE * authfile) { + + int c = EOF; + + TRACE(("enter getauthline")); + + buf_setpos(line, 0); + buf_setlen(line, 0); + + while (line->pos < line->size) { + c = fgetc(authfile); /*getc() is weird with some uClibc systems*/ + if (c == EOF || c == '\n' || c == '\r') { + goto out; + } + buf_putbyte(line, (unsigned char)c); + } + + TRACE(("leave getauthline: line too long")); + return DROPBEAR_FAILURE; + +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")); + return DROPBEAR_FAILURE; + } else { + TRACE(("leave getauthline: success")); + return DROPBEAR_SUCCESS; + } + + TRACE(("leave getauthline")); +} + +/* Returns DROPBEAR_SUCCESS if file permissions for pubkeys are ok, + * DROPBEAR_FAILURE otherwise. + * Checks that the user's homedir, ~/.ssh, and + * ~/.ssh/authorized_keys are all owned by either root or the user, and are + * g-w, o-w */ +static int checkpubkeyperms() { + + char* filename = NULL; + int ret = DROPBEAR_FAILURE; + unsigned int len; + + TRACE(("enter checkpubkeyperms")); + + assert(svr_ses.authstate.pw); + if (svr_ses.authstate.pw->pw_dir == NULL) { + goto out; + } + + if ((len = strlen(svr_ses.authstate.pw->pw_dir)) == 0) { + goto out; + } + + /* allocate max required pathname storage, + * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */ + filename = m_malloc(len + 22); + strncpy(filename, svr_ses.authstate.pw->pw_dir, len+1); + + /* check ~ */ + if (checkfileperm(filename) != DROPBEAR_SUCCESS) { + goto out; + } + + /* check ~/.ssh */ + strncat(filename, "/.ssh", 5); /* strlen("/.ssh") == 5 */ + if (checkfileperm(filename) != DROPBEAR_SUCCESS) { + goto out; + } + + /* now check ~/.ssh/authorized_keys */ + strncat(filename, "/authorized_keys", 16); + if (checkfileperm(filename) != DROPBEAR_SUCCESS) { + goto out; + } + + /* file looks ok, return success */ + ret = DROPBEAR_SUCCESS; + +out: + m_free(filename); + + TRACE(("leave checkpubkeyperms")); + return ret; +} + +/* Checks that a file is owned by the user or root, and isn't writable by + * group or other */ +/* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +static int checkfileperm(char * filename) { + struct stat filestat; + + TRACE(("enter checkfileperm(%s)", filename)); + + if (stat(filename, &filestat) != 0) { + TRACE(("leave checkfileperm: stat() != 0")); + return DROPBEAR_FAILURE; + } + /* check ownership - user or root only*/ + if (filestat.st_uid != svr_ses.authstate.pw->pw_uid + && filestat.st_uid != 0) { + TRACE(("leave checkfileperm: wrong ownership")); + return DROPBEAR_FAILURE; + } + /* check permissions - don't want group or others +w */ + if (filestat.st_mode & (S_IWGRP | S_IWOTH)) { + TRACE(("leave checkfileperm: wrong perms")); + return DROPBEAR_FAILURE; + } + TRACE(("leave checkfileperm: success")); + return DROPBEAR_SUCCESS; +} + + +#endif /* DROPBEAR_PUBKEY_AUTH */ diff --git a/svr-chansession.c b/svr-chansession.c new file mode 100644 index 0000000..dce0827 --- /dev/null +++ b/svr-chansession.c @@ -0,0 +1,931 @@ +/* + * 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. */ + +#include "includes.h" +#include "packet.h" +#include "buffer.h" +#include "session.h" +#include "dbutil.h" +#include "channel.h" +#include "chansession.h" +#include "sshpty.h" +#include "termcodes.h" +#include "ssh.h" +#include "random.h" +#include "utmp.h" +#include "x11fwd.h" +#include "agentfwd.h" + +/* Handles sessions (either shells or programs) requested by the client */ + +static int sessioncommand(struct Channel *channel, struct ChanSess *chansess, + int iscmd, int issubsys); +static int sessionpty(struct ChanSess * chansess); +static int sessionsignal(struct ChanSess *chansess); +static int noptycommand(struct Channel *channel, struct ChanSess *chansess); +static int ptycommand(struct Channel *channel, struct ChanSess *chansess); +static int sessionwinchange(struct ChanSess *chansess); +static void execchild(struct ChanSess *chansess); +static void addchildpid(struct ChanSess *chansess, pid_t pid); +static void sesssigchild_handler(int val); +static void closechansess(struct Channel *channel); +static void newchansess(struct Channel *channel); +static void chansessionrequest(struct Channel *channel); + +static void send_exitsignalstatus(struct Channel *channel); +static int sesscheckclose(struct Channel *channel); + +const struct ChanType svrchansess = { + 0, /* sepfds */ + "session", /* name */ + newchansess, /* inithandler */ + sesscheckclose, /* checkclosehandler */ + chansessionrequest, /* reqhandler */ + closechansess, /* closehandler */ +}; + + + +/* required to clear environment */ +extern char** environ; + +static int sesscheckclose(struct Channel *channel) { + struct ChanSess *chansess = (struct ChanSess*)channel->typedata; + return chansess->exited; +} + +/* Set up the general chansession environment, in particular child-exit + * handling */ +void svr_chansessinitialise() { + + struct sigaction sa_chld; + + /* single child process intially */ + svr_ses.childpids = (struct ChildPid*)m_malloc(sizeof(struct ChildPid)); + svr_ses.childpids[0].pid = -1; /* unused */ + svr_ses.childpids[0].chansess = NULL; + svr_ses.childpidsize = 1; + sa_chld.sa_handler = sesssigchild_handler; + sa_chld.sa_flags = SA_NOCLDSTOP; + if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) { + dropbear_exit("signal() error"); + } + +} + +/* handler for childs exiting, store the state for return to the client */ +static void sesssigchild_handler(int dummy) { + + int status; + pid_t pid; + unsigned int i; + struct ChanSess * chansess; + struct sigaction sa_chld; + + 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) { + + chansess = svr_ses.childpids[i].chansess; + chansess->exited = 1; + if (WIFEXITED(status)) { + chansess->exitstatus = WEXITSTATUS(status); + } + if (WIFSIGNALED(status)) { + chansess->exitsignal = WTERMSIG(status); +#ifndef AIX + chansess->exitcore = WCOREDUMP(status); +#endif + } else { + /* we use this to determine how pid exited */ + chansess->exitsignal = -1; + } + } + } + } + sa_chld.sa_handler = sesssigchild_handler; + sa_chld.sa_flags = SA_NOCLDSTOP; + sigaction(SIGCHLD, &sa_chld, NULL); + TRACE(("leave sigchld handler")); +} + +/* send the exit status or the signal causing termination for a session */ +/* XXX server */ +static void send_exitsignalstatus(struct Channel *channel) { + + struct ChanSess *chansess = (struct ChanSess*)channel->typedata; + + if (chansess->exited) { + if (chansess->exitsignal > 0) { + send_msg_chansess_exitsignal(channel, chansess); + } else { + send_msg_chansess_exitstatus(channel, chansess); + } + } +} + +/* send the exitstatus to the client */ +static void send_msg_chansess_exitstatus(struct Channel * channel, + struct ChanSess * chansess) { + + assert(chansess->exited); + assert(chansess->exitsignal == -1); + + CHECKCLEARTOWRITE(); + + buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST); + buf_putint(ses.writepayload, channel->remotechan); + buf_putstring(ses.writepayload, "exit-status", 11); + buf_putbyte(ses.writepayload, 0); /* boolean FALSE */ + buf_putint(ses.writepayload, chansess->exitstatus); + + encrypt_packet(); + +} + +/* send the signal causing the exit to the client */ +static void send_msg_chansess_exitsignal(struct Channel * channel, + struct ChanSess * chansess) { + + int i; + char* signame = NULL; + + assert(chansess->exited); + assert(chansess->exitsignal > 0); + + CHECKCLEARTOWRITE(); + + /* we check that we can match a signal name, otherwise + * don't send anything */ + for (i = 0; signames[i].name != NULL; i++) { + if (signames[i].signal == chansess->exitsignal) { + signame = signames[i].name; + break; + } + } + + if (signame == NULL) { + return; + } + + buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST); + buf_putint(ses.writepayload, channel->remotechan); + buf_putstring(ses.writepayload, "exit-signal", 11); + buf_putbyte(ses.writepayload, 0); /* boolean FALSE */ + buf_putstring(ses.writepayload, signame, strlen(signame)); + buf_putbyte(ses.writepayload, chansess->exitcore); + buf_putstring(ses.writepayload, "", 0); /* error msg */ + buf_putstring(ses.writepayload, "", 0); /* lang */ + + encrypt_packet(); +} + +/* set up a session channel */ +static void newchansess(struct Channel *channel) { + + struct ChanSess *chansess; + + assert(channel->typedata == NULL); + + chansess = (struct ChanSess*)m_malloc(sizeof(struct ChanSess)); + chansess->cmd = NULL; + chansess->pid = 0; + + /* pty details */ + chansess->master = -1; + chansess->slave = -1; + chansess->tty = NULL; + chansess->term = NULL; + chansess->termw = 0; + chansess->termh = 0; + chansess->termc = 0; + chansess->termr = 0; + + chansess->exited = 0; + + channel->typedata = chansess; + +#ifndef DISABLE_X11FWD + chansess->x11fd = -1; + chansess->x11authprot = NULL; + chansess->x11authcookie = NULL; +#endif + +#ifndef DISABLE_AGENTFWD + chansess->agentfd = -1; + chansess->agentfile = NULL; + chansess->agentdir = NULL; +#endif + +} + +/* clean a session channel */ +static void closechansess(struct Channel *channel) { + + struct ChanSess *chansess; + unsigned int i; + struct logininfo *li; + + chansess = (struct ChanSess*)channel->typedata; + + send_exitsignalstatus(chansess); + + TRACE(("enter closechansess")); + if (chansess == NULL) { + TRACE(("leave closechansess: chansess == NULL")); + return; + } + + m_free(chansess->cmd); + m_free(chansess->term); + + if (chansess->tty) { + /* write the utmp/wtmp login record */ + li = login_alloc_entry(chansess->pid, svr_ses.authstate.username, + ses.remotehost, chansess->tty); + login_logout(li); + login_free_entry(li); + + pty_release(chansess->tty); + m_free(chansess->tty); + } + +#ifndef DISABLE_X11FWD + x11cleanup(chansess); +#endif + +#ifndef DISABLE_AGENTFWD + agentcleanup(chansess); +#endif + + /* clear child pid entries */ + for (i = 0; i < svr_ses.childpidsize; i++) { + if (svr_ses.childpids[i].chansess == chansess) { + assert(svr_ses.childpids[i].pid > 0); + TRACE(("closing pid %d", svr_ses.childpids[i].pid)); + TRACE(("exited = %d", chansess->exited)); + svr_ses.childpids[i].pid = -1; + svr_ses.childpids[i].chansess = NULL; + } + } + + m_free(chansess); + + TRACE(("leave closechansess")); +} + +/* Handle requests for a channel. These can be execution requests, + * or x11/authagent forwarding. These are passed to appropriate handlers */ +static void chansessionrequest(struct Channel *channel) { + + unsigned char * type; + unsigned int typelen; + unsigned char wantreply; + int ret = 1; + struct ChanSess *chansess; + + TRACE(("enter chansessionrequest")); + + assert(channel->type == CHANNEL_ID_SESSION); + + type = buf_getstring(ses.payload, &typelen); + wantreply = buf_getbyte(ses.payload); + + if (typelen > MAX_NAME_LEN) { + TRACE(("leave chansessionrequest: type too long")); /* XXX send error?*/ + goto out; + } + + chansess = (struct ChanSess*)channel->typedata; + assert(chansess != NULL); + TRACE(("type is %s", type)); + + if (strcmp(type, "window-change") == 0) { + ret = sessionwinchange(chansess); + } else if (strcmp(type, "shell") == 0) { + ret = sessioncommand(channel, chansess, 0, 0); + } else if (strcmp(type, "pty-req") == 0) { + ret = sessionpty(chansess); + } else if (strcmp(type, "exec") == 0) { + ret = sessioncommand(channel, chansess, 1, 0); + } else if (strcmp(type, "subsystem") == 0) { + ret = sessioncommand(channel, chansess, 1, 1); +#ifndef DISABLE_X11FWD + } else if (strcmp(type, "x11-req") == 0) { + ret = x11req(chansess); +#endif +#ifndef DISABLE_AGENTFWD + } else if (strcmp(type, "auth-agent-req@openssh.com") == 0) { + ret = agentreq(chansess); +#endif + } else if (strcmp(type, "signal") == 0) { + ret = sessionsignal(chansess); + } else { + /* etc, todo "env", "subsystem" */ + } + +out: + + if (wantreply) { + if (ret == 0) { + send_msg_channel_success(channel); + } else { + send_msg_channel_failure(channel); + } + } + + m_free(type); + TRACE(("leave chansessionrequest")); +} + + +/* Send a signal to a session's process as requested by the client*/ +static int sessionsignal(struct ChanSess *chansess) { + + int sig = 0; + unsigned char* signame; + int i; + + if (chansess->pid == 0) { + /* haven't got a process pid yet */ + return DROPBEAR_FAILURE; + } + + signame = buf_getstring(ses.payload, NULL); + + i = 0; + while (signames[i].name != 0) { + if (strcmp(signames[i].name, signame) == 0) { + sig = signames[i].signal; + break; + } + i++; + } + + m_free(signame); + + if (sig == 0) { + /* failed */ + return DROPBEAR_FAILURE; + } + + if (kill(chansess->pid, sig) < 0) { + return DROPBEAR_FAILURE; + } + + return DROPBEAR_SUCCESS; +} + +/* Let the process know that the window size has changed, as notified from the + * client. Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +static int sessionwinchange(struct ChanSess *chansess) { + + if (chansess->master < 0) { + /* haven't got a pty yet */ + return DROPBEAR_FAILURE; + } + + chansess->termc = buf_getint(ses.payload); + chansess->termr = buf_getint(ses.payload); + chansess->termw = buf_getint(ses.payload); + chansess->termh = buf_getint(ses.payload); + + pty_change_window_size(chansess->master, chansess->termr, chansess->termc, + chansess->termw, chansess->termh); + + return DROPBEAR_FAILURE; +} + +/* Set up a session pty which will be used to execute the shell or program. + * The pty is allocated now, and kept for when the shell/program executes. + * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +static int sessionpty(struct ChanSess * chansess) { + + unsigned int termlen; + unsigned char namebuf[65]; + struct termios termio; + + TRACE(("enter sessionpty")); + chansess->term = buf_getstring(ses.payload, &termlen); + if (termlen > MAX_TERM_LEN) { + /* TODO send disconnect ? */ + TRACE(("leave sessionpty: term len too long")); + return DROPBEAR_FAILURE; + } + chansess->termc = buf_getint(ses.payload); + chansess->termr = buf_getint(ses.payload); + chansess->termw = buf_getint(ses.payload); + chansess->termh = buf_getint(ses.payload); + + /* allocate the pty */ + assert(chansess->master == -1); /* haven't already got one */ + if (pty_allocate(&chansess->master, &chansess->slave, namebuf, 64) == 0) { + TRACE(("leave sessionpty: failed to allocate pty")); + return DROPBEAR_FAILURE; + } + + chansess->tty = (char*)strdup(namebuf); + if (!chansess->tty) { + dropbear_exit("out of memory"); /* TODO disconnect */ + } + + pty_setowner(svr_ses.authstate.pw, chansess->tty); + pty_change_window_size(chansess->master, chansess->termr, chansess->termc, + chansess->termw, chansess->termh); + + /* Term modes */ + /* We'll ignore errors and continue if we can't set modes. + * We're ignoring baud rates since they seem evil */ + if (tcgetattr(chansess->master, &termio) == 0) { + unsigned char opcode; + unsigned int value; + const struct TermCode * termcode; + unsigned int len; + + len = buf_getint(ses.payload); + if (len != ses.payload->len - ses.payload->pos) { + dropbear_exit("bad term mode string"); + } + + if (len == 0) { + TRACE(("empty terminal modes string")); + return DROPBEAR_SUCCESS; + } + + while (((opcode = buf_getbyte(ses.payload)) != 0x00) && + opcode <= 159) { + + /* must be before checking type, so that value is consumed even if + * we don't use it */ + value = buf_getint(ses.payload); + + /* handle types of code */ + if (opcode > MAX_TERMCODE) { + continue; + } + termcode = &termcodes[(unsigned int)opcode]; + + + switch (termcode->type) { + + case TERMCODE_NONE: + break; + + case TERMCODE_CONTROLCHAR: + termio.c_cc[termcode->mapcode] = value; + break; + + case TERMCODE_INPUT: + if (value) { + termio.c_iflag |= termcode->mapcode; + } else { + termio.c_iflag &= ~(termcode->mapcode); + } + break; + + case TERMCODE_OUTPUT: + if (value) { + termio.c_oflag |= termcode->mapcode; + } else { + termio.c_oflag &= ~(termcode->mapcode); + } + break; + + case TERMCODE_LOCAL: + if (value) { + termio.c_lflag |= termcode->mapcode; + } else { + termio.c_lflag &= ~(termcode->mapcode); + } + break; + + case TERMCODE_CONTROL: + if (value) { + termio.c_cflag |= termcode->mapcode; + } else { + termio.c_cflag &= ~(termcode->mapcode); + } + break; + + } + } + if (tcsetattr(chansess->master, TCSANOW, &termio) < 0) { + dropbear_log(LOG_INFO, "error setting terminal attributes"); + } + } + + TRACE(("leave sessionpty")); + return DROPBEAR_SUCCESS; +} + +/* Handle a command request from the client. This is used for both shell + * and command-execution requests, and passes the command to + * noptycommand or ptycommand as appropriate. + * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +static int sessioncommand(struct Channel *channel, struct ChanSess *chansess, + int iscmd, int issubsys) { + + unsigned int cmdlen; + + TRACE(("enter sessioncommand")); + + if (chansess->cmd != NULL) { + /* TODO - send error - multiple commands? */ + return DROPBEAR_FAILURE; + } + + if (iscmd) { + /* "exec" */ + chansess->cmd = buf_getstring(ses.payload, &cmdlen); + + if (cmdlen > MAX_CMD_LEN) { + /* TODO - send error - too long ? */ + return DROPBEAR_FAILURE; + } + if (issubsys) { +#ifdef SFTPSERVER_PATH + if ((cmdlen == 4) && strncmp(chansess->cmd, "sftp", 4) == 0) { + m_free(chansess->cmd); + chansess->cmd = strdup(SFTPSERVER_PATH); + } else +#endif + { + return DROPBEAR_FAILURE; + } + } + } + + if (chansess->term == NULL) { + /* no pty */ + return noptycommand(channel, chansess); + } else { + /* want pty */ + return ptycommand(channel, chansess); + } +} + +/* Execute a command and set up redirection of stdin/stdout/stderr without a + * pty. + * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +static int noptycommand(struct Channel *channel, struct ChanSess *chansess) { + + int infds[2]; + int outfds[2]; + int errfds[2]; + pid_t pid; + + TRACE(("enter noptycommand")); + + /* redirect stdin/stdout/stderr */ + if (pipe(infds) != 0) + return DROPBEAR_FAILURE; + if (pipe(outfds) != 0) + return DROPBEAR_FAILURE; + if (pipe(errfds) != 0) + return DROPBEAR_FAILURE; + + pid = fork(); + if (pid < 0) + return DROPBEAR_FAILURE; + + if (!pid) { + /* child */ + + /* redirect stdin/stdout */ +#define FDIN 0 +#define FDOUT 1 + if ((dup2(infds[FDIN], STDIN_FILENO) < 0) || + (dup2(outfds[FDOUT], STDOUT_FILENO) < 0) || + (dup2(errfds[FDOUT], STDERR_FILENO) < 0)) { + TRACE(("leave noptycommand: error redirecting FDs")); + return DROPBEAR_FAILURE; + } + + close(infds[FDOUT]); + close(infds[FDIN]); + close(outfds[FDIN]); + close(outfds[FDOUT]); + close(errfds[FDIN]); + close(errfds[FDOUT]); + + execchild(chansess); + /* not reached */ + + } else { + /* parent */ + TRACE(("continue noptycommand: parent")); + chansess->pid = pid; + + /* add a child pid */ + addchildpid(chansess, pid); + + close(infds[FDIN]); + close(outfds[FDOUT]); + close(errfds[FDOUT]); + channel->infd = infds[FDOUT]; + channel->outfd = outfds[FDIN]; + channel->errfd = errfds[FDIN]; + ses.maxfd = MAX(ses.maxfd, channel->infd); + 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"); + } + } +#undef FDIN +#undef FDOUT + + TRACE(("leave noptycommand")); + return DROPBEAR_SUCCESS; +} + +/* Execute a command or shell within a pty environment, and set up + * redirection as appropriate. + * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +static int ptycommand(struct Channel *channel, struct ChanSess *chansess) { + + pid_t pid; + struct logininfo *li; +#ifdef DO_MOTD + buffer * motdbuf = NULL; + int len; + struct stat sb; + char *hushpath = NULL; +#endif + + TRACE(("enter ptycommand")); + + /* we need to have a pty allocated */ + if (chansess->master == -1 || chansess->tty == NULL) { + dropbear_log(LOG_WARNING, "no pty was allocated, couldn't execute"); + return DROPBEAR_FAILURE; + } + + pid = fork(); + if (pid < 0) + return DROPBEAR_FAILURE; + + if (pid == 0) { + /* child */ + + /* redirect stdin/stdout/stderr */ + close(chansess->master); + + pty_make_controlling_tty(&chansess->slave, chansess->tty); + + if ((dup2(chansess->slave, STDIN_FILENO) < 0) || + (dup2(chansess->slave, STDERR_FILENO) < 0) || + (dup2(chansess->slave, STDOUT_FILENO) < 0)) { + TRACE(("leave ptycommand: error redirecting filedesc")); + return DROPBEAR_FAILURE; + } + + close(chansess->slave); + + /* write the utmp/wtmp login record - must be after changing the + * terminal used for stdout with the dup2 above */ + li= login_alloc_entry(getpid(), svr_ses.authstate.username, + ses.remotehost, chansess->tty); + login_login(li); + login_free_entry(li); + + m_free(chansess->tty); + +#ifdef DO_MOTD + if (ses.opts->domotd) { + /* don't show the motd if ~/.hushlogin exists */ + + /* 11 == strlen("/hushlogin\0") */ + len = strlen(svr_ses.authstate.pw->pw_dir) + 11; + + hushpath = m_malloc(len); + snprintf(hushpath, len, "%s/hushlogin", svr_ses.authstate.pw->pw_dir); + + if (stat(hushpath, &sb) < 0) { + /* more than a screenful is stupid IMHO */ + motdbuf = buf_new(80 * 25); + if (buf_readfile(motdbuf, MOTD_FILENAME) == DROPBEAR_SUCCESS) { + buf_setpos(motdbuf, 0); + while (motdbuf->pos != motdbuf->len) { + len = motdbuf->len - motdbuf->pos; + len = write(STDOUT_FILENO, + buf_getptr(motdbuf, len), len); + buf_incrpos(motdbuf, len); + } + } + buf_free(motdbuf); + } + m_free(hushpath); + } +#endif /* DO_MOTD */ + + execchild(chansess); + /* not reached */ + + } else { + /* parent */ + TRACE(("continue ptycommand: parent")); + chansess->pid = pid; + + /* add a child pid */ + addchildpid(chansess, pid); + + close(chansess->slave); + channel->infd = chansess->master; + channel->outfd = chansess->master; + /* 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"); + } + + } + + TRACE(("leave ptycommand")); + return DROPBEAR_SUCCESS; +} + +/* Add the pid of a child to the list for exit-handling */ +static void addchildpid(struct ChanSess *chansess, pid_t pid) { + + unsigned int i; + for (i = 0; i < svr_ses.childpidsize; i++) { + if (svr_ses.childpids[i].pid == -1) { + break; + } + } + + /* need to increase size */ + if (i == svr_ses.childpidsize) { + svr_ses.childpids = (struct ChildPid*)m_realloc(svr_ses.childpids, + sizeof(struct ChildPid) * svr_ses.childpidsize+1); + svr_ses.childpidsize++; + } + + svr_ses.childpids[i].pid = pid; + svr_ses.childpids[i].chansess = chansess; + +} + +/* Clean up, drop to user privileges, set up the environment and execute + * the command/shell. This function does not return. */ +static void execchild(struct ChanSess *chansess) { + + char *argv[4]; + char * usershell; + char * baseshell; + unsigned int i; + + /* wipe the hostkey */ + sign_key_free(ses.opts->hostkey); + ses.opts->hostkey = NULL; + + /* overwrite the prng state */ + seedrandom(); + + /* close file descriptors except stdin/stdout/stderr + * Need to be sure FDs are closed here to avoid reading files as root */ + for (i = 3; i < (unsigned int)ses.maxfd; i++) { + if (m_close(i) == DROPBEAR_FAILURE) { + dropbear_exit("Error closing file desc"); + } + } + + /* clear environment */ + /* if we're debugging using valgrind etc, we need to keep the LD_PRELOAD + * etc. This is hazardous, so should only be used for debugging. */ +#ifndef DEBUG_VALGRIND +#ifdef HAVE_CLEARENV + clearenv(); +#else /* don't HAVE_CLEARENV */ + /* Yay for posix. */ + if (environ) { + environ[0] = NULL; + } +#endif /* HAVE_CLEARENV */ +#endif /* DEBUG_VALGRIND */ + + /* We can only change uid/gid as root ... */ + if (getuid() == 0) { + + if ((setgid(svr_ses.authstate.pw->pw_gid) < 0) || + (initgroups(svr_ses.authstate.pw->pw_name, + svr_ses.authstate.pw->pw_gid) < 0) || + (setuid(svr_ses.authstate.pw->pw_uid) < 0)) { + dropbear_exit("error changing user"); + } + } else { + /* ... but if the daemon is the same uid as the requested uid, we don't + * need to */ + + /* XXX - there is a minor issue here, in that if there are multiple + * usernames with the same uid, but differing groups, then the + * differing groups won't be set (as with initgroups()). The solution + * is for the sysadmin not to give out the UID twice */ + if (getuid() != svr_ses.authstate.pw->pw_uid) { + dropbear_exit("couldn't change user as non-root"); + } + } + + /* an empty shell should be interpreted as "/bin/sh" */ + if (svr_ses.authstate.pw->pw_shell[0] == '\0') { + usershell = "/bin/sh"; + } else { + usershell = svr_ses.authstate.pw->pw_shell; + } + + /* set env vars */ + addnewvar("USER", svr_ses.authstate.pw->pw_name); + addnewvar("LOGNAME", svr_ses.authstate.pw->pw_name); + addnewvar("HOME", svr_ses.authstate.pw->pw_dir); + addnewvar("SHELL", usershell); + if (chansess->term != NULL) { + addnewvar("TERM", chansess->term); + } + + /* change directory */ + if (chdir(svr_ses.authstate.pw->pw_dir) < 0) { + dropbear_exit("error changing directory"); + } + +#ifndef DISABLE_X11FWD + /* set up X11 forwarding if enabled */ + x11setauth(chansess); +#endif +#ifndef DISABLE_AGENTFWD + /* set up agent env variable */ + agentset(chansess); +#endif + + baseshell = basename(usershell); + + if (chansess->cmd != NULL) { + argv[0] = baseshell; + } else { + /* a login shell should be "-bash" for "/bin/bash" etc */ + int len = strlen(baseshell) + 2; /* 2 for "-" */ + argv[0] = (char*)m_malloc(len); + snprintf(argv[0], len, "-%s", baseshell); + } + + if (chansess->cmd != NULL) { + argv[1] = "-c"; + argv[2] = chansess->cmd; + argv[3] = NULL; + } else { + /* construct a shell of the form "-bash" etc */ + argv[1] = NULL; + } + + execv(usershell, argv); + + /* only reached on error */ + dropbear_exit("child failed"); +} + +/* add a new environment variable, allocating space for the entry */ +void addnewvar(const char* param, const char* var) { + + char* newvar; + int plen, vlen; + + plen = strlen(param); + vlen = strlen(var); + + newvar = m_malloc(plen + vlen + 2); /* 2 is for '=' and '\0' */ + memcpy(newvar, param, plen); + newvar[plen] = '='; + memcpy(&newvar[plen+1], var, vlen); + newvar[plen+vlen+1] = '\0'; + if (putenv(newvar) < 0) { + dropbear_exit("environ error"); + } +} diff --git a/svr-kex.c b/svr-kex.c new file mode 100644 index 0000000..ebd3858 --- /dev/null +++ b/svr-kex.c @@ -0,0 +1,274 @@ +/* + * 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. */ + +#include "includes.h" +#include "dbutil.h" +#include "algo.h" +#include "buffer.h" +#include "session.h" +#include "kex.h" +#include "ssh.h" +#include "packet.h" +#include "bignum.h" +#include "random.h" + + +static void send_msg_kexdh_reply(mp_int *dh_e); + +/* Handle a diffie-hellman key exchange initialisation. This involves + * calculating a session key reply value, and corresponding hash. These + * are carried out by send_msg_kexdh_reply(). recv_msg_kexdh_init() calls + * that function, then brings the new keys into use */ +void recv_msg_kexdh_init() { + + mp_int dh_e; + + TRACE(("enter recv_msg_kexdh_init")); + if (!ses.kexstate.recvkexinit) { + dropbear_exit("Premature kexdh_init message received"); + } + + m_mp_init(&dh_e); + buf_getmpint(ses.payload, &dh_e); + + send_msg_kexdh_reply(&dh_e); + + mp_clear(&dh_e); + + send_msg_newkeys(); + ses.expecting = SSH_MSG_NEWKEYS; + TRACE(("leave recv_msg_kexdh_init")); +} + +/* Generate our side of the diffie-hellman key exchange value (dh_f), and + * calculate the session key using the diffie-hellman algorithm. Following + * that, the session hash is calculated, and signed with RSA or DSS. The + * result is sent to the client. + * + * See the ietf-secsh-transport draft, section 6, for details */ +static void send_msg_kexdh_reply(mp_int *dh_e) { + + mp_int dh_p, dh_q, dh_g, dh_y, dh_f; + unsigned char randbuf[DH_P_LEN]; + int dh_q_len; + hash_state hs; + + TRACE(("enter send_msg_kexdh_reply")); + + m_mp_init_multi(&dh_g, &dh_p, &dh_q, &dh_y, &dh_f, NULL); + + /* read the prime and generator*/ + if (mp_read_unsigned_bin(&dh_p, (unsigned char*)dh_p_val, DH_P_LEN) + != MP_OKAY) { + dropbear_exit("Diffie-Hellman error"); + } + + if (mp_set_int(&dh_g, DH_G_VAL) != MP_OKAY) { + dropbear_exit("Diffie-Hellman error"); + } + + /* calculate q = (p-1)/2 */ + if (mp_sub_d(&dh_p, 1, &dh_y) != MP_OKAY) { /*dh_y is just a temp var here*/ + dropbear_exit("Diffie-Hellman error"); + } + if (mp_div_2(&dh_y, &dh_q) != MP_OKAY) { + dropbear_exit("Diffie-Hellman error"); + } + + dh_q_len = mp_unsigned_bin_size(&dh_q); + + /* calculate our random value dh_y */ + do { + assert((unsigned int)dh_q_len <= sizeof(randbuf)); + genrandom(randbuf, dh_q_len); + if (mp_read_unsigned_bin(&dh_y, randbuf, dh_q_len) != MP_OKAY) { + dropbear_exit("Diffie-Hellman error"); + } + } while (mp_cmp(&dh_y, &dh_q) == MP_GT || mp_cmp_d(&dh_y, 0) != MP_GT); + + /* f = g^y mod p */ + if (mp_exptmod(&dh_g, &dh_y, &dh_p, &dh_f) != MP_OKAY) { + dropbear_exit("Diffie-Hellman error"); + } + mp_clear(&dh_g); + + /* K = e^y mod p */ + ses.dh_K = (mp_int*)m_malloc(sizeof(mp_int)); + m_mp_init(ses.dh_K); + if (mp_exptmod(dh_e, &dh_y, &dh_p, ses.dh_K) != MP_OKAY) { + dropbear_exit("Diffie-Hellman error"); + } + + /* clear no longer needed vars */ + mp_clear_multi(&dh_y, &dh_p, &dh_q, NULL); + + /* Create the remainder of the hash buffer, to generate the exchange hash */ + /* K_S, the host key */ + buf_put_pub_key(ses.kexhashbuf, ses.opts->hostkey, + ses.newkeys->algo_hostkey); + /* e, exchange value sent by the client */ + buf_putmpint(ses.kexhashbuf, dh_e); + /* f, exchange value sent by the server */ + buf_putmpint(ses.kexhashbuf, &dh_f); + /* K, the shared secret */ + buf_putmpint(ses.kexhashbuf, ses.dh_K); + + /* calculate the hash H to sign */ + sha1_init(&hs); + buf_setpos(ses.kexhashbuf, 0); + sha1_process(&hs, buf_getptr(ses.kexhashbuf, ses.kexhashbuf->len), + ses.kexhashbuf->len); + sha1_done(&hs, ses.hash); + buf_free(ses.kexhashbuf); + ses.kexhashbuf = NULL; + + /* first time around, we set the session_id to H */ + if (ses.session_id == NULL) { + /* create the session_id, this never needs freeing */ + ses.session_id = (unsigned char*)m_malloc(SHA1_HASH_SIZE); + memcpy(ses.session_id, ses.hash, SHA1_HASH_SIZE); + } + + /* we can start creating the kexdh_reply packet */ + CHECKCLEARTOWRITE(); + buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_REPLY); + buf_put_pub_key(ses.writepayload, ses.opts->hostkey, + ses.newkeys->algo_hostkey); + + /* put f */ + buf_putmpint(ses.writepayload, &dh_f); + mp_clear(&dh_f); + + /* calc the signature */ + buf_put_sign(ses.writepayload, ses.opts->hostkey, + ses.newkeys->algo_hostkey, ses.hash, SHA1_HASH_SIZE); + + /* the SSH_MSG_KEXDH_REPLY is done */ + encrypt_packet(); + + TRACE(("leave send_msg_kexdh_reply")); +} + +/* read the client's choice of algorithms */ +void svr_read_kex() { + + algo_type * algo; + char * erralgo = NULL; + + int goodguess = 0; + int allgood = 1; /* we AND this with each goodguess and see if its still + true after */ + + buf_incrpos(ses.payload, 16); /* start after the cookie */ + + ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context)); + + /* kex_algorithms */ + algo = svr_buf_match_algo(ses.payload, sshkex, &goodguess); + allgood &= goodguess; + if (algo == NULL) { + erralgo = "kex"; + goto error; + } + ses.newkeys->algo_kex = algo->val; + + /* server_host_key_algorithms */ + algo = svr_buf_match_algo(ses.payload, sshhostkey, &goodguess); + allgood &= goodguess; + if (algo == NULL) { + erralgo = "hostkey"; + goto error; + } + ses.newkeys->algo_hostkey = algo->val; + + /* encryption_algorithms_client_to_server */ + algo = svr_buf_match_algo(ses.payload, sshciphers, &goodguess); + if (algo == NULL) { + erralgo = "enc c->s"; + goto error; + } + ses.newkeys->recv_algo_crypt = (struct dropbear_cipher*)algo->data; + + /* encryption_algorithms_server_to_client */ + algo = svr_buf_match_algo(ses.payload, sshciphers, &goodguess); + if (algo == NULL) { + erralgo = "enc s->c"; + goto error; + } + ses.newkeys->trans_algo_crypt = (struct dropbear_cipher*)algo->data; + + /* mac_algorithms_client_to_server */ + algo = svr_buf_match_algo(ses.payload, sshhashes, &goodguess); + if (algo == NULL) { + erralgo = "mac c->s"; + goto error; + } + ses.newkeys->recv_algo_mac = (struct dropbear_hash*)algo->data; + + /* mac_algorithms_server_to_client */ + algo = svr_buf_match_algo(ses.payload, sshhashes, &goodguess); + if (algo == NULL) { + erralgo = "mac s->c"; + goto error; + } + ses.newkeys->trans_algo_mac = (struct dropbear_hash*)algo->data; + + /* compression_algorithms_client_to_server */ + algo = svr_buf_match_algo(ses.payload, sshcompress, &goodguess); + if (algo == NULL) { + erralgo = "comp c->s"; + goto error; + } + ses.newkeys->recv_algo_comp = algo->val; + + /* compression_algorithms_server_to_client */ + algo = svr_buf_match_algo(ses.payload, sshcompress, &goodguess); + if (algo == NULL) { + erralgo = "comp s->c"; + goto error; + } + ses.newkeys->trans_algo_comp = algo->val; + + /* languages_client_to_server */ + buf_eatstring(ses.payload); + + /* languages_server_to_client */ + buf_eatstring(ses.payload); + + /* first_kex_packet_follows */ + if (buf_getbyte(ses.payload)) { + ses.kexstate.firstfollows = 1; + /* if the guess wasn't good, we ignore the packet sent */ + if (!allgood) { + ses.ignorenext = 1; + } + } + + /* reserved for future extensions */ + buf_getint(ses.payload); + return; + +error: + dropbear_exit("no matching algo %s", erralgo); +} diff --git a/svr-packet.c b/svr-packet.c new file mode 100644 index 0000000..ac09822 --- /dev/null +++ b/svr-packet.c @@ -0,0 +1,196 @@ +/* + * 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. */ + +#include "includes.h" +#include "packet.h" +#include "session.h" +#include "dbutil.h" +#include "ssh.h" +#include "algo.h" +#include "buffer.h" +#include "kex.h" +#include "random.h" +#include "service.h" +#include "auth.h" +#include "channel.h" + +static void svr_process_postauth_packet(unsigned int type); + +/* process a decrypted packet, call the appropriate handler */ +void svr_process_packet() { + + unsigned char type; + + TRACE(("enter process_packet")); + + type = buf_getbyte(ses.payload); + TRACE(("process_packet: packet type = %d", type)); + + /* these packets we can receive at any time, regardless of expecting + * other packets: */ + switch(type) { + + case SSH_MSG_IGNORE: + case SSH_MSG_DEBUG: + TRACE(("received SSH_MSG_IGNORE or SSH_MSG_DEBUG")); + goto out; + + case SSH_MSG_UNIMPLEMENTED: + /* debugging XXX */ + TRACE(("SSH_MSG_UNIMPLEMENTED")); + dropbear_exit("received SSH_MSG_UNIMPLEMENTED"); + + case SSH_MSG_DISCONNECT: + /* TODO cleanup? */ + dropbear_close("Disconnect received"); + } + + /* Check if we should ignore this packet. Used currently only for + * KEX code, with first_kex_packet_follows */ + if (ses.ignorenext) { + TRACE(("Ignoring packet, type = %d", type)); + ses.ignorenext = 0; + goto out; + } + + /* check that we aren't expecting a particular packet */ + if (ses.expecting && ses.expecting != type) { + /* TODO send disconnect? */ + dropbear_exit("unexpected packet type %d, expected %d", type, + ses.expecting); + } + + /* handle the packet depending on type */ + ses.expecting = 0; + + switch (type) { + + case SSH_MSG_SERVICE_REQUEST: + recv_msg_service_request(); + break; + + case SSH_MSG_USERAUTH_REQUEST: + recv_msg_userauth_request(); + break; + + case SSH_MSG_KEXINIT: + recv_msg_kexinit(); + break; + + case SSH_MSG_KEXDH_INIT: + recv_msg_kexdh_init(); + break; + + case SSH_MSG_NEWKEYS: + recv_msg_newkeys(); + break; + + /* this is ugly, need to make a cleaner way to do it */ + case SSH_MSG_CHANNEL_DATA: + case SSH_MSG_CHANNEL_WINDOW_ADJUST: + case SSH_MSG_CHANNEL_REQUEST: + case SSH_MSG_CHANNEL_OPEN: + case SSH_MSG_CHANNEL_EOF: + case SSH_MSG_CHANNEL_CLOSE: + case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: + case SSH_MSG_CHANNEL_OPEN_FAILURE: + case SSH_MSG_GLOBAL_REQUEST: + /* these should be checked for authdone below */ + svr_process_postauth_packet(type); + break; + + default: + /* TODO this possibly should be handled */ + TRACE(("preauth unknown packet")); + recv_unimplemented(); + break; + } + +out: + buf_free(ses.payload); + ses.payload = NULL; + + TRACE(("leave process_packet")); +} + +/* process a packet, and also check that auth has been done */ +static void svr_process_postauth_packet(unsigned int type) { + + /* messages following here require userauth before use */ + + /* IF YOU ADD MORE PACKET TYPES, MAKE SURE THEY'RE ALSO ADDED TO THE LIST + * IN process_packet() XXX XXX XXX */ + if (!svr_ses.authstate.authdone) { + dropbear_exit("received message %d before userauth", type); + } + + switch (type) { + + case SSH_MSG_CHANNEL_DATA: + recv_msg_channel_data(); + break; + + case SSH_MSG_CHANNEL_WINDOW_ADJUST: + recv_msg_channel_window_adjust(); + break; + +#ifndef DISABLE_REMOTETCPFWD + case SSH_MSG_GLOBAL_REQUEST: + /* currently only used for remote tcp, so we cheat a little */ + recv_msg_global_request_remotetcp(); + break; +#endif + + case SSH_MSG_CHANNEL_REQUEST: + recv_msg_channel_request(); + break; + + case SSH_MSG_CHANNEL_OPEN: + recv_msg_channel_open(); + break; + + case SSH_MSG_CHANNEL_EOF: + recv_msg_channel_eof(); + break; + + case SSH_MSG_CHANNEL_CLOSE: + recv_msg_channel_close(); + break; + +#ifdef USE_LISTENERS /* for x11, tcp fwd etc */ + case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: + recv_msg_channel_open_confirmation(); + break; + + case SSH_MSG_CHANNEL_OPEN_FAILURE: + recv_msg_channel_open_failure(); + break; +#endif + + default: + TRACE(("unknown packet()")); + recv_unimplemented(); + break; + } +} diff --git a/svr-runopts.c b/svr-runopts.c new file mode 100644 index 0000000..f7a427f --- /dev/null +++ b/svr-runopts.c @@ -0,0 +1,349 @@ +/* + * 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. */ + +#include "includes.h" +#include "runopts.h" +#include "signkey.h" +#include "buffer.h" +#include "dbutil.h" +#include "algo.h" + +static sign_key * loadhostkeys(const char * dsskeyfile, + const char * rsakeyfile); +static int readhostkey(const char * filename, sign_key * hostkey, int type); +static void printhelp(const char * progname); + +static void printhelp(const char * progname) { + + fprintf(stderr, "Dropbear sshd v%s\n" + "Usage: %s [options]\n" + "Options are:\n" + "-b bannerfile Display the contents of bannerfile" + " before user login\n" + " (default: none)\n" +#ifdef DROPBEAR_DSS + "-d dsskeyfile Use dsskeyfile for the dss host key\n" + " (default: %s)\n" +#endif +#ifdef DROPBEAR_RSA + "-r rsakeyfile Use rsakeyfile for the rsa host key\n" + " (default: %s)\n" +#endif + "-F Don't fork into background\n" +#ifdef DISABLE_SYSLOG + "(Syslog support not compiled in, using stderr)\n" +#else + "-E Log to stderr rather than syslog\n" +#endif +#ifdef DO_MOTD + "-m Don't display the motd on login\n" +#endif + "-w Disallow root logins\n" +#ifdef DROPBEAR_PASSWORD_AUTH + "-s Disable password logins\n" + "-g Disable password logins for root\n" +#endif +#ifndef DISABLE_LOCALTCPFWD + "-j Disable local port forwarding\n" +#endif +#ifndef DISABLE_REMOTETCPFWD + "-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"*/ + + ,DROPBEAR_VERSION, progname, +#ifdef DROPBEAR_DSS + DSS_PRIV_FILENAME, +#endif +#ifdef DROPBEAR_RSA + RSA_PRIV_FILENAME, +#endif + DROPBEAR_MAX_PORTS, DROPBEAR_PORT); +} + +/* returns NULL on failure, or a pointer to a freshly allocated + * runopts structure */ +runopts * svr_getopts(int argc, char ** argv) { + + unsigned int i; + char ** next = 0; + runopts * opts; + unsigned int portnum = 0; + char *portstring[DROPBEAR_MAX_PORTS]; + unsigned int longport; + + /* see printhelp() for options */ + opts = (runopts*)m_malloc(sizeof(runopts)); + opts->rsakeyfile = NULL; + opts->dsskeyfile = NULL; + opts->bannerfile = NULL; + opts->banner = NULL; + opts->forkbg = 1; + opts->norootlogin = 0; + opts->noauthpass = 0; + opts->norootpass = 0; + opts->nolocaltcp = 0; + opts->noremotetcp = 0; + /* not yet + opts->ipv4 = 1; + opts->ipv6 = 1; + */ +#ifdef DO_MOTD + opts->domotd = 1; +#endif +#ifndef DISABLE_SYSLOG + usingsyslog = 1; +#endif + + for (i = 1; i < (unsigned int)argc; i++) { + if (next) { + *next = argv[i]; + if (*next == NULL) { + dropbear_exit("Invalid null argument"); + } + next = 0x00; + continue; + } + + if (argv[i][0] == '-') { + switch (argv[i][1]) { + case 'b': + next = &opts->bannerfile; + break; +#ifdef DROPBEAR_DSS + case 'd': + next = &opts->dsskeyfile; + break; +#endif +#ifdef DROPBEAR_RSA + case 'r': + next = &opts->rsakeyfile; + break; +#endif + case 'F': + opts->forkbg = 0; + break; +#ifndef DISABLE_SYSLOG + case 'E': + usingsyslog = 0; + break; +#endif +#ifndef DISABLE_LOCALTCPFWD + case 'j': + opts->nolocaltcp = 1; + break; +#endif +#ifndef DISABLE_REMOTETCPFWD + case 'k': + opts->noremotetcp = 1; + break; +#endif + case 'p': + if (portnum < DROPBEAR_MAX_PORTS) { + portstring[portnum] = NULL; + next = &portstring[portnum]; + portnum++; + } + break; +#ifdef DO_MOTD + /* motd is displayed by default, -m turns it off */ + case 'm': + opts->domotd = 0; + break; +#endif + case 'w': + opts->norootlogin = 1; + break; +#ifdef DROPBEAR_PASSWORD_AUTH + case 's': + opts->noauthpass = 1; + break; + case 'g': + opts->norootpass = 1; + break; +#endif + case 'h': + printhelp(argv[0]); + exit(EXIT_FAILURE); + break; + /* + case '4': + opts->ipv4 = 0; + break; + case '6': + opts->ipv6 = 0; + break; + */ + default: + fprintf(stderr, "Unknown argument %s\n", argv[i]); + printhelp(argv[0]); + exit(EXIT_FAILURE); + break; + } + } + } + + if (opts->dsskeyfile == NULL) { + opts->dsskeyfile = DSS_PRIV_FILENAME; + } + if (opts->rsakeyfile == NULL) { + opts->rsakeyfile = RSA_PRIV_FILENAME; + } + opts->hostkey = loadhostkeys(opts->dsskeyfile, opts->rsakeyfile); + + if (opts->bannerfile) { + struct stat buf; + if (stat(opts->bannerfile, &buf) != 0) { + dropbear_exit("Error opening banner file '%s'", + opts->bannerfile); + } + + if (buf.st_size > MAX_BANNER_SIZE) { + dropbear_exit("Banner file too large, max is %d bytes", + MAX_BANNER_SIZE); + } + + opts->banner = buf_new(buf.st_size); + if (buf_readfile(opts->banner, opts->bannerfile)!=DROPBEAR_SUCCESS) { + dropbear_exit("Error reading banner file '%s'", + opts->bannerfile); + } + buf_setpos(opts->banner, 0); + } + + /* not yet + if (!(opts->ipv4 || 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 */ + opts->portcount = 1; + opts->ports = m_malloc(sizeof(uint16_t)); + opts->ports[0] = DROPBEAR_PORT; + } else { + opts->portcount = portnum; + 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) { + opts->ports[i] = (uint16_t)longport; + continue; + } + } + fprintf(stderr, "Bad port '%s'\n", + portstring[i] ? portstring[i] : "null"); + } + } + + return opts; +} + +void freerunopts(runopts* opts) { + + if (!opts) { + return; + } + + if (opts->hostkey) { + sign_key_free(opts->hostkey); + opts->hostkey = NULL; + } + + m_free(opts->ports); + m_free(opts); +} + +/* returns success or failure */ +static int readhostkey(const char * filename, sign_key * hostkey, int type) { + + int ret = DROPBEAR_FAILURE; + int i; + buffer *buf; + + buf = buf_new(2000); + + if (buf_readfile(buf, filename) == DROPBEAR_FAILURE) { + goto out; + } + buf_setpos(buf, 0); + if (buf_get_priv_key(buf, hostkey, &type) == DROPBEAR_FAILURE) { + goto out; + } + + ret = DROPBEAR_SUCCESS; +out: + if (ret == DROPBEAR_FAILURE) { + for (i = 0; sshhostkey[i].name != NULL; i++) { + if (sshhostkey[i].val == type) { + sshhostkey[i].usable = 0; + break; + } + } + fprintf(stderr, "Failed reading '%s', disabling %s\n", filename, + type == DROPBEAR_SIGNKEY_DSS ? "DSS" : "RSA"); + } + + buf_burn(buf); + buf_free(buf); + return ret; +} + +static sign_key * loadhostkeys(const char * dsskeyfile, + const char * rsakeyfile) { + + sign_key * hostkey; + + TRACE(("enter loadhostkeys")); + + hostkey = new_sign_key(); + +#ifdef DROPBEAR_RSA + (void)readhostkey(rsakeyfile, hostkey, DROPBEAR_SIGNKEY_RSA); +#endif + +#ifdef DROPBEAR_DSS + (void)readhostkey(dsskeyfile, hostkey, DROPBEAR_SIGNKEY_DSS); +#endif + + if ( 1 +#ifdef DROPBEAR_DSS + && hostkey->dsskey == NULL +#endif +#ifdef DROPBEAR_RSA + && hostkey->rsakey == NULL +#endif + ) { + dropbear_exit("No hostkeys available"); + } + + TRACE(("leave loadhostkeys")); + return hostkey; +} diff --git a/svr-service.c b/svr-service.c new file mode 100644 index 0000000..4154df4 --- /dev/null +++ b/svr-service.c @@ -0,0 +1,88 @@ +/* + * 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. */ + +#include "includes.h" +#include "dbutil.h" +#include "service.h" +#include "session.h" +#include "packet.h" +#include "ssh.h" +#include "auth.h" + +static void send_msg_service_accept(unsigned char *name, int len); + +/* processes a SSH_MSG_SERVICE_REQUEST, returning 0 if finished, + * 1 if not */ +void recv_msg_service_request() { + + unsigned char * name; + unsigned int len; + + TRACE(("enter recv_msg_service_request")); + + name = buf_getstring(ses.payload, &len); + + /* ssh-userauth */ + if (len == SSH_SERVICE_USERAUTH_LEN && + strncmp(SSH_SERVICE_USERAUTH, name, len) == 0) { + + send_msg_service_accept(name, len); + m_free(name); + TRACE(("leave recv_msg_service_request: done ssh-userauth")); + return; + } + + /* ssh-connection */ + if (len == SSH_SERVICE_CONNECTION_LEN && + (strncmp(SSH_SERVICE_CONNECTION, name, len) == 0)) { + if (svr_ses.authstate.authdone != 1) { + dropbear_exit("request for connection before auth"); + } + + send_msg_service_accept(name, len); + m_free(name); + TRACE(("leave recv_msg_service_request: done ssh-connection")); + return; + } + + m_free(name); + /* TODO this should be a MSG_DISCONNECT */ + dropbear_exit("unrecognised SSH_MSG_SERVICE_REQUEST"); + + TRACE(("leave recv_msg_service_request")); + +} + +static void send_msg_service_accept(unsigned char *name, int len) { + + TRACE(("accepting service %s", name)); + + CHECKCLEARTOWRITE(); + + buf_putbyte(ses.writepayload, SSH_MSG_SERVICE_ACCEPT); + buf_putstring(ses.writepayload, name, len); + + encrypt_packet(); + +} diff --git a/svr-session.c b/svr-session.c new file mode 100644 index 0000000..bc24487 --- /dev/null +++ b/svr-session.c @@ -0,0 +1,238 @@ +/* + * 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. */ + +#include "includes.h" +#include "session.h" +#include "dbutil.h" +#include "packet.h" +#include "algo.h" +#include "buffer.h" +#include "dss.h" +#include "ssh.h" +#include "random.h" +#include "kex.h" +#include "channel.h" +#include "chansession.h" +#include "atomicio.h" + +static void svr_remoteclosed(); +static void svr_dropbear_exit(int exitcode, const char* format, va_list param); +static void svr_dropbear_log(int priority, const char* format, va_list param); + +struct serversession svr_ses; + +void svr_session(int sock, runopts *opts, int childpipe, + struct sockaddr* remoteaddr) { + + fd_set readfd, writefd; + struct timeval timeout; + int val; + + crypto_init(); + common_session_init(sock, opts); + + ses.remoteaddr = remoteaddr; + ses.remotehost = getaddrhostname(remoteaddr); + + /* Initialise server specific parts of the session */ + svr_ses.childpipe = childpipe; + authinitialise(); + svr_chansessinitialise(); + + if (gettimeofday(&timeout, 0) < 0) { + dropbear_exit("Error getting time"); + } + + ses.connecttimeout = timeout.tv_sec + AUTH_TIMEOUT; + + /* set up messages etc */ + session_remoteclosed = svr_remoteclosed; + _dropbear_exit = svr_dropbear_exit; + _dropbear_log = svr_dropbear_log; + + /* We're ready to go now */ + sessinitdone = 1; + + /* exchange identification, version etc */ + session_identification(); + + seedrandom(); + + /* start off with key exchange */ + send_msg_kexinit(); + + FD_ZERO(&readfd); + FD_ZERO(&writefd); + + /* main loop, select()s for all sockets in use */ + for(;;) { + + timeout.tv_sec = SELECT_TIMEOUT; + timeout.tv_usec = 0; + FD_ZERO(&writefd); + FD_ZERO(&readfd); + assert(ses.payload == NULL); + if (ses.sock != -1) { + FD_SET(ses.sock, &readfd); + if (!isempty(&ses.writequeue)) { + FD_SET(ses.sock, &writefd); + } + } + + /* set up for channels which require reading/writing */ + if (ses.dataallowed) { + setchannelfds(&readfd, &writefd); + } + val = select(ses.maxfd+1, &readfd, &writefd, NULL, &timeout); + + if (exitflag) { + dropbear_exit("Terminated by signal"); + } + + if (val < 0) { + if (errno == EINTR) { + continue; + } else { + dropbear_exit("Error in select"); + } + } + + /* check for auth timeout, rekeying required etc */ + checktimeouts(); + + if (val == 0) { + /* timeout */ + TRACE(("select timeout")); + continue; + } + + /* process session socket's incoming/outgoing data */ + if (ses.sock != -1) { + if (FD_ISSET(ses.sock, &writefd) && !isempty(&ses.writequeue)) { + write_packet(); + } + + if (FD_ISSET(ses.sock, &readfd)) { + read_packet(); + } + + /* Process the decrypted packet. After this, the read buffer + * will be ready for a new packet */ + if (ses.payload != NULL) { + svr_process_packet(); + } + } + + /* process pipes etc for the channels, ses.dataallowed == 0 + * during rekeying ) */ + if (ses.dataallowed) { + channelio(&readfd, &writefd); + } + + } /* for(;;) */ +} + + + +/* called when the remote side closes the connection */ +static void svr_remoteclosed() { + + close(ses.sock); + ses.sock = -1; + dropbear_close("Exited normally"); + +} + +/* failure exit - format must be <= 100 chars */ +static void svr_dropbear_exit(int exitcode, const char* format, va_list param) { + + char fmtbuf[300]; + + if (!sessinitdone) { + /* before session init */ + snprintf(fmtbuf, sizeof(fmtbuf), + "premature exit: %s", format); + } else if (svr_ses.authstate.authdone) { + /* user has authenticated */ + snprintf(fmtbuf, sizeof(fmtbuf), + "exit after auth (%s): %s", + svr_ses.authstate.printableuser, format); + } else if (svr_ses.authstate.printableuser) { + /* we have a potential user */ + snprintf(fmtbuf, sizeof(fmtbuf), + "exit before auth (user '%s', %d fails): %s", + svr_ses.authstate.printableuser, svr_ses.authstate.failcount, format); + } else { + /* before userauth */ + snprintf(fmtbuf, sizeof(fmtbuf), + "exit before auth: %s", format); + } + + if (errno != 0) { + /* XXX - is this valid? */ + snprintf(fmtbuf, sizeof(fmtbuf), "%s [%d %s]", fmtbuf, + errno, strerror(errno)); + } + + _dropbear_log(LOG_INFO, fmtbuf, param); + + /* must be after we've done with username etc */ + common_session_cleanup(); + + exit(exitcode); + +} + +/* priority is priority as with syslog() */ +static void svr_dropbear_log(int priority, const char* format, va_list param) { + + char printbuf[1024]; + char datestr[20]; + time_t timesec; + int havetrace = 0; + + vsnprintf(printbuf, sizeof(printbuf), format, param); + +#ifndef DISABLE_SYSLOG + if (usingsyslog) { + syslog(priority, "%s", printbuf); + } +#endif + + /* 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; +#endif + + if (!usingsyslog || havetrace) + { + timesec = time(NULL); + if (strftime(datestr, sizeof(datestr), "%b %d %H:%M:%S", + localtime(×ec)) == 0) { + datestr[0] = '?'; datestr[1] = '\0'; + } + fprintf(stderr, "[%d] %s %s\n", getpid(), datestr, printbuf); + } +} diff --git a/tcpfwd.c b/tcpfwd.c new file mode 100644 index 0000000..d95970a --- /dev/null +++ b/tcpfwd.c @@ -0,0 +1,124 @@ +#include "includes.h" +#include "tcpfwd.h" +#include "session.h" +#include "dbutil.h" + +void tcp_fwd_initialise() { + + /* just one slot to start with */ + ses.tcplisteners = + (struct TCPListener**)m_malloc(sizeof(struct TCPListener*)); + ses.tcplistensize = 1; + ses.tcplisteners[0] = NULL; + +} + +void set_tcp_fwd_fds(fd_set * readfds) { + + unsigned int i; + struct TCPListener *listener; + + /* check each in turn */ + for (i = 0; i < ses.tcplistensize; i++) { + listener = ses.tcplisteners[i]; + if (listener != NULL) { + FD_SET(listener->sock, readfds); + } + } +} + + +void handle_tcp_fwd(fd_set * readfds) { + + unsigned int i; + struct TCPListener *listener; + + /* check each in turn */ + for (i = 0; i < ses.tcplistensize; i++) { + listener = ses.tcplisteners[i]; + if (listener != NULL) { + if (FD_ISSET(listener->sock, readfds)) { + listener->accepter(listener); + } + } + } +} + + +/* accepter(int fd, void* typedata) is a function to accept connections, + * cleanup(void* typedata) happens when cleaning up */ +int new_fwd(int sock, int type, void* typedata, + void (*accepter)(struct TCPListener*), + void (*cleanup)(struct TCPListener*)) { + + unsigned int i, j; + struct TCPListener *newtcp = NULL; + /* try get a new structure to hold it */ + for (i = 0; i < ses.tcplistensize; i++) { + if (ses.tcplisteners[i] == NULL) { + break; + } + } + + /* or create a new one */ + if (i == ses.tcplistensize) { + if (ses.tcplistensize > MAX_TCPLISTENERS) { + TRACE(("leave newlistener: too many already")); + close(sock); + return DROPBEAR_FAILURE; + } + + ses.tcplisteners = (struct TCPListener**)m_realloc(ses.tcplisteners, + (ses.tcplistensize+TCP_EXTEND_SIZE) + *sizeof(struct TCPListener*)); + + ses.tcplistensize += TCP_EXTEND_SIZE; + + for (j = i; j < ses.tcplistensize; j++) { + ses.tcplisteners[j] = NULL; + } + } + + ses.maxfd = MAX(ses.maxfd, sock); + + newtcp = (struct TCPListener*)m_malloc(sizeof(struct TCPListener)); + newtcp->index = i; + newtcp->type = type; + newtcp->typedata = typedata; + newtcp->sock = sock; + newtcp->accepter = accepter; + newtcp->cleanup = cleanup; + + ses.tcplisteners[i] = newtcp; + return DROPBEAR_SUCCESS; +} + +/* Return the first listener which matches the type-specific comparison + * function */ +struct TCPListener * get_listener(int type, void* typedata, + int (*match)(void*, void*)) { + + unsigned int i; + struct TCPListener* listener; + + for (i = 0, listener = ses.tcplisteners[i]; i < ses.tcplistensize; i++) { + if (listener->type == type + && match(typedata, listener->typedata)) { + return listener; + } + } + + return NULL; +} + +void remove_listener(struct TCPListener* listener) { + + if (listener->cleanup) { + listener->cleanup(listener); + } + + close(listener->sock); + ses.tcplisteners[listener->index] = NULL; + m_free(listener); + +} diff --git a/tcpfwd.h b/tcpfwd.h new file mode 100644 index 0000000..85d7373 --- /dev/null +++ b/tcpfwd.h @@ -0,0 +1,37 @@ +#ifndef _TCPFWD_H +#define _TCPFWD_H + +#define MAX_TCPLISTENERS 20 +#define TCP_EXTEND_SIZE 1 + +struct TCPListener { + + int sock; + + int index; /* index in the array of listeners */ + + void (*accepter)(struct TCPListener*); + void (*cleanup)(struct TCPListener*); + + int type; /* CHANNEL_ID_X11, CHANNEL_ID_AGENT, + CHANNEL_ID_TCPDIRECT (for clients), + CHANNEL_ID_TCPFORWARDED (for servers) */ + + void *typedata; + +}; + +void tcp_fwd_initialise(); +void handle_tcp_fwd(fd_set * readfds); +void set_tcp_fwd_fds(fd_set * readfds); + +int new_fwd(int sock, int type, void* typedata, + void (*accepter)(struct TCPListener*), + void (*cleanup)(struct TCPListener*)); + +struct TCPListener * get_listener(int type, void* typedata, + int (*match)(void*, void*)); + +void remove_listener(struct TCPListener* listener); + +#endif /* _TCPFWD_H */ diff --git a/termcodes.c b/termcodes.c new file mode 100644 index 0000000..78c3dd8 --- /dev/null +++ b/termcodes.c @@ -0,0 +1,183 @@ +/* + * 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. */ + +#include "includes.h" +#include "termcodes.h" + +const struct TermCode termcodes[MAX_TERMCODE+1] = { + + {0, 0}, /* TTY_OP_END */ + {VINTR, TERMCODE_CONTROLCHAR}, /* control character codes */ + {VQUIT, TERMCODE_CONTROLCHAR}, + {VERASE, TERMCODE_CONTROLCHAR}, + {VKILL, TERMCODE_CONTROLCHAR}, + {VEOF, TERMCODE_CONTROLCHAR}, + {VEOL, TERMCODE_CONTROLCHAR}, + {VEOL2, TERMCODE_CONTROLCHAR}, + {VSTART, TERMCODE_CONTROLCHAR}, + {VSTOP, TERMCODE_CONTROLCHAR}, + {VSUSP, TERMCODE_CONTROLCHAR}, +#ifdef VDSUSP + {VDSUSP, TERMCODE_CONTROLCHAR}, +#else + {0, 0}, +#endif +#ifdef VREPRINT + {VREPRINT, TERMCODE_CONTROLCHAR}, +#else + {0, 0}, +#endif +#ifdef AIX + {CERASE, TERMCODE_CONTROLCHAR}, +#else + {VWERASE, TERMCODE_CONTROLCHAR}, +#endif + {VLNEXT, TERMCODE_CONTROLCHAR}, +#ifdef VFLUSH + {VFLUSH, TERMCODE_CONTROLCHAR}, +#else + {0, 0}, +#endif +#ifdef VSWTCH + {VSWTCH, TERMCODE_CONTROLCHAR}, +#else + {0, 0}, +#endif +#ifdef VSTATUS + {VSTATUS, TERMCODE_CONTROLCHAR}, +#else + {0, 0}, +#endif +#ifdef AIX + {CKILL, TERMCODE_CONTROLCHAR}, +#elif defined(VDISCARD) + {VDISCARD, TERMCODE_CONTROLCHAR}, +#else + {0, 0}, +#endif + {0, 0}, /* 19 */ + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, /* 29 */ + {IGNPAR, TERMCODE_INPUT}, /* input flags */ + {PARMRK, TERMCODE_INPUT}, + {INPCK, TERMCODE_INPUT}, + {ISTRIP, TERMCODE_INPUT}, + {INLCR, TERMCODE_INPUT}, + {IGNCR, TERMCODE_INPUT}, + {ICRNL, TERMCODE_INPUT}, +#ifdef IUCLC + {IUCLC, TERMCODE_INPUT}, +#else + {0, 0}, +#endif + {IXON, TERMCODE_INPUT}, + {IXANY, TERMCODE_INPUT}, + {IXOFF, TERMCODE_INPUT}, +#ifdef IMAXBEL + {IMAXBEL, TERMCODE_INPUT}, +#else + {0, 0}, +#endif + {0, 0}, /* 42 */ + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, /* 49 */ + {ISIG, TERMCODE_LOCAL}, /* local flags */ + {ICANON, TERMCODE_LOCAL}, +#ifdef XCASE + {XCASE, TERMCODE_LOCAL}, +#else + {0, 0}, +#endif + {ECHO, TERMCODE_LOCAL}, + {ECHOE, TERMCODE_LOCAL}, + {ECHOK, TERMCODE_LOCAL}, + {ECHONL, TERMCODE_LOCAL}, + {NOFLSH, TERMCODE_LOCAL}, + {TOSTOP, TERMCODE_LOCAL}, + {IEXTEN, TERMCODE_LOCAL}, + {ECHOCTL, TERMCODE_LOCAL}, + {ECHOKE, TERMCODE_LOCAL}, + {PENDIN, TERMCODE_LOCAL}, + {0, 0}, /* 63 */ + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, /* 69 */ + {OPOST, TERMCODE_OUTPUT}, /* output flags */ +#ifdef OLCUC + {OLCUC, TERMCODE_OUTPUT}, +#else + {0, 0}, +#endif + {ONLCR, TERMCODE_OUTPUT}, +#ifdef OCRNL + {OCRNL, TERMCODE_OUTPUT}, +#else + {0, 0}, +#endif +#ifdef ONOCR + {ONOCR, TERMCODE_OUTPUT}, +#else + {0, 0}, +#endif +#ifdef ONLRET + {ONLRET, TERMCODE_OUTPUT}, +#else + {0, 0}, +#endif + {0, 0}, /* 76 */ + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, /* 89 */ + {CS7, TERMCODE_CONTROL}, + {CS8, TERMCODE_CONTROL}, + {PARENB, TERMCODE_CONTROL}, + {PARODD, TERMCODE_CONTROL} + /* 94 */ +}; diff --git a/termcodes.h b/termcodes.h new file mode 100644 index 0000000..00792ea --- /dev/null +++ b/termcodes.h @@ -0,0 +1,46 @@ +/* + * 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 _TERMCODES_H_ +#define _TERMCODES_H_ + +#define TERMCODE_NONE 0 +#define TERMCODE_CONTROL 1 +#define TERMCODE_INPUT 2 +#define TERMCODE_OUTPUT 3 +#define TERMCODE_LOCAL 4 +#define TERMCODE_CONTROLCHAR 5 + +#define MAX_TERMCODE 93 + +struct TermCode { + + unsigned int mapcode; + unsigned char type; + +}; + +extern const struct TermCode termcodes[]; + +#endif /* _TERMCODES_H_ */ diff --git a/x11fwd.c b/x11fwd.c new file mode 100644 index 0000000..229b0df --- /dev/null +++ b/x11fwd.c @@ -0,0 +1,216 @@ +/* + * 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. */ + +#include "includes.h" + +#ifndef DISABLE_X11FWD +#include "x11fwd.h" +#include "session.h" +#include "ssh.h" +#include "dbutil.h" +#include "chansession.h" +#include "channel.h" +#include "packet.h" +#include "buffer.h" + +#define X11BASEPORT 6000 +#define X11BINDBASE 6010 + +static int bindport(int fd); +static int send_msg_channel_open_x11(int fd, struct sockaddr_in* addr); + +/* called as a request for a session channel, sets up listening X11 */ +/* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +int x11req(struct ChanSess * chansess) { + + /* we already have an x11 connection */ + if (chansess->x11fd != -1) { + return DROPBEAR_FAILURE; + } + + chansess->x11singleconn = buf_getbyte(ses.payload); + chansess->x11authprot = buf_getstring(ses.payload, NULL); + chansess->x11authcookie = buf_getstring(ses.payload, NULL); + chansess->x11screennum = buf_getint(ses.payload); + + /* create listening socket */ + chansess->x11fd = socket(PF_INET, SOCK_STREAM, 0); + if (chansess->x11fd < 0) { + goto fail; + } + + /* allocate port and bind */ + chansess->x11port = bindport(chansess->x11fd); + if (chansess->x11port < 0) { + goto fail; + } + + /* listen */ + if (listen(chansess->x11fd, 20) < 0) { + goto fail; + } + + /* set non-blocking */ + if (fcntl(chansess->x11fd, F_SETFL, O_NONBLOCK) < 0) { + goto fail; + } + + /* channel.c's channel fd code will handle the socket now */ + + /* set the maxfd so that select() loop will notice it */ + ses.maxfd = MAX(ses.maxfd, chansess->x11fd); + + return DROPBEAR_SUCCESS; + +fail: + /* cleanup */ + x11cleanup(chansess); + + return DROPBEAR_FAILURE; +} + +/* accepts a new X11 socket */ +/* returns DROPBEAR_FAILURE or DROPBEAR_SUCCESS */ +int x11accept(struct ChanSess * chansess) { + + int fd; + struct sockaddr_in addr; + int len; + + len = sizeof(addr); + + fd = accept(chansess->x11fd, (struct sockaddr*)&addr, &len); + if (fd < 0) { + return DROPBEAR_FAILURE; + } + + /* if single-connection we close it up */ + if (chansess->x11singleconn) { + x11cleanup(chansess); + } + + return send_msg_channel_open_x11(fd, &addr); +} + +/* This is called after switching to the user, and sets up the xauth + * and environment variables. */ +void x11setauth(struct ChanSess *chansess) { + + char display[20]; /* space for "localhost:12345.123" */ + FILE * authprog; + int val; + + if (chansess->x11fd == -1) { + return; + } + + /* create the DISPLAY string */ + val = snprintf(display, sizeof(display), "localhost:%d.%d", + chansess->x11port - X11BASEPORT, chansess->x11screennum); + if (val < 0 || val >= (int)sizeof(display)) { + /* string was truncated */ + return; + } + + addnewvar("DISPLAY", display); + + /* create the xauth string */ + val = snprintf(display, sizeof(display), "unix:%d.%d", + chansess->x11port - X11BASEPORT, chansess->x11screennum); + if (val < 0 || val >= (int)sizeof(display)) { + /* string was truncated */ + return; + } + + /* popen is a nice function - code is strongly based on OpenSSH's */ + authprog = popen(XAUTH_COMMAND, "w"); + if (authprog) { + fprintf(authprog, "add %s %s %s\n", + display, chansess->x11authprot, chansess->x11authcookie); + pclose(authprog); + } else { + fprintf(stderr, "Failed to run %s\n", XAUTH_COMMAND); + } +} + +void x11cleanup(struct ChanSess * chansess) { + + if (chansess->x11fd == -1) { + return; + } + + m_free(chansess->x11authprot); + m_free(chansess->x11authcookie); + close(chansess->x11fd); + chansess->x11fd = -1; +} + +static int send_msg_channel_open_x11(int fd, struct sockaddr_in* addr) { + + char* ipstring; + + if (send_msg_channel_open_init(fd, CHANNEL_ID_X11, "x11") + == DROPBEAR_SUCCESS) { + ipstring = inet_ntoa(addr->sin_addr); + buf_putstring(ses.writepayload, ipstring, strlen(ipstring)); + buf_putint(ses.writepayload, addr->sin_port); + + encrypt_packet(); + return DROPBEAR_SUCCESS; + } else { + return DROPBEAR_FAILURE; + } + +} + +/* returns the port bound to, or -1 on failure. + * Will attempt to bind to a port X11BINDBASE (6010 usually) or upwards */ +static int bindport(int fd) { + + struct sockaddr_in addr; + uint16_t port; + + memset((void*)&addr, 0x0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + /* if we can't find one in 2000 ports free, something's wrong */ + for (port = X11BINDBASE; port < X11BINDBASE + 2000; port++) { + addr.sin_port = htons(port); + if (bind(fd, (struct sockaddr*)&addr, + sizeof(struct sockaddr_in)) == 0) { + /* success */ + return port; + } + if (errno == EADDRINUSE) { + /* try the next port */ + continue; + } + /* otherwise it was an error we don't know about */ + dropbear_log(LOG_DEBUG, "failed to bind x11 socket"); + break; + } + return -1; +} +#endif /* DROPBEAR_X11FWD */ diff --git a/x11fwd.h b/x11fwd.h new file mode 100644 index 0000000..889033f --- /dev/null +++ b/x11fwd.h @@ -0,0 +1,38 @@ +/* + * 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 _X11FWD_H_ +#define _X11FWD_H_ +#ifndef DISABLE_X11FWD + +#include "includes.h" +#include "chansession.h" +#include "channel.h" + +int x11req(struct ChanSess * chansess); +int x11accept(struct ChanSess * chansess); +void x11cleanup(struct ChanSess * chansess); +void x11setauth(struct ChanSess *chansess); + +#endif /* DROPBEAR_X11FWD */ +#endif /* _X11FWD_H_ */ -- cgit v1.2.3