Initial revision
diff --git a/COPYING.Ylonen b/COPYING.Ylonen
new file mode 100644
index 0000000..5e681ed
--- /dev/null
+++ b/COPYING.Ylonen
@@ -0,0 +1,70 @@
+This file is part of the ssh software, Copyright (c) 1995 Tatu Ylonen, Finland
+
+
+COPYING POLICY AND OTHER LEGAL ISSUES
+
+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".
+
+However, I am not implying to give any licenses to any patents or
+copyrights held by third parties, and the software includes parts that
+are not under my direct control.  As far as I know, all included
+source code is used in accordance with the relevant license agreements
+and can be used freely for any purpose (the GNU license being the most
+restrictive); see below for details.
+
+[ RSA is no longer included. ]
+[ IDEA is no longer included. ]
+[ DES is now external. ]
+[ GMP is now external. No more GNU licence. ]
+[ Zlib is now external. ]
+[ The make-ssh-known-hosts script is no longer included. ]
+[ TSS has been removed. ]
+[ MD5 is now external. ]
+[ RC4 support has been removed. ]
+[ Blowfish is now external. ]
+
+The 32-bit CRC implementation in crc32.c is due to Gary S. Brown.
+Comments in the file indicate it may be used for any purpose without
+restrictions.
+
+The 32-bit CRC compensation attack detector in deattack.c was
+contributed by CORE SDI S.A. under a BSD-style license. See
+http://www.core-sdi.com/english/ssh/ for details.
+
+Note that any information and cryptographic algorithms used in this
+software are publicly available on the Internet and at any major
+bookstore, scientific library, and patent office worldwide.  More
+information can be found e.g. at "http://www.cs.hut.fi/crypto".
+
+The legal status of this program is some combination of all these
+permissions and restrictions.  Use only at your own responsibility.
+You will be responsible for any legal consequences yourself; I am not
+making any claims whether possessing or using this is legal or not in
+your country, and I am not taking any responsibility on your behalf.
+
+
+			    NO WARRANTY
+
+BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..08d90f7
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,578 @@
+Fri Nov 17 16:19:20 1995  Tatu Ylonen  <ylo@trance.olari.clinet.fi>
+
+	* Released 1.2.12.
+
+	* channels.c: Commented out debugging messages about output draining.
+
+	* Added file OVERVIEW to give some idea about the structure of the
+	  ssh software.
+
+Thu Nov 16 16:40:17 1995  Tatu Ylonen  <ylo@trance.olari.clinet.fi>
+
+	* canohost.c (get_remote_hostname): Don't ever return NULL (causes
+	  segmentation violation).
+
+        * sshconnect.c: Host ip address printed incorrectly with -v.
+	
+	* Implemented SSH_TTY environment variable.
+
+Wed Nov 15 01:47:40 1995  Tatu Ylonen  <ylo@trance.olari.clinet.fi>
+
+	* Implemented server and client option KeepAlive to specify
+	  whether to set SO_KEEPALIVE.  Both default to "yes"; to disable
+	  keepalives, set the value to "no" in both the server and the
+	  client configuration files.  Updated manual pages.
+	
+	* sshd.c: Fixed Solaris utmp problem: wrong pid stored in utmp
+	  (patch from Petri Virkkula <argon@bat.cs.hut.fi>).
+
+	* login.c (record_logout): Fixed removing user from utmp on BSD
+	  (with HAVE_LIBUTIL_LOGIN).
+
+	* Added cleanup functions to be called from fatal().  Arranged for
+	  utmp to be cleaned if sshd terminates by calling fatal (e.g.,
+	  after dropping connection).  Eliminated separate client-side
+	  fatal() functions and moved fatal() to log-client.c.  Made all
+	  cleanups, including channel_stop_listening() and packet_close()
+	  be called using this mechanism.
+
+Thu Nov  9 09:58:05 1995  Tatu Ylonen  <ylo@soikko.cs.hut.fi>
+
+	* sshd.c: Permit immediate login with empty password only if
+	  password authentication is allowed.
+
+Wed Nov  8 00:43:55 1995  Tatu Ylonen  <ylo@soikko.cs.hut.fi>
+
+	* Eliminated unix-domain X11 forwarding.  Inet-domain forwarding is
+	  now the only supported form.  Renamed server option
+	  X11InetForwarding to X11Forwarding, and eliminated
+	  X11UnixForwarding.  Updated documentation.  Updated RFC (marked
+	  the SSH_CMSG_X11_REQUEST_FORWARDING message (code 26) as
+	  obsolete, and removed all references to it).  Increased protocol
+	  version number to 1.3.
+
+	* scp.c (main): Added -B (BatchMode).  Updated manual page.
+
+	* Cleaned up and updated all manual pages.
+
+	* clientloop.c: Added new escape sequences ~# (lists forwarded
+	  connections), ~& (background ssh when waiting for forwarded
+	  connections to terminate), ~? (list available escapes).
+	  Polished the output of the connection listing.  Updated
+	  documentation.
+
+	* uidswap.c: If _POSIX_SAVED_IDS is defined, don't change the real
+	  uid.  Assume that _POSIX_SAVED_IDS also applies to seteuid.
+	  This may solve problems with tcp_wrappers (libwrap) showing
+	  connections as coming from root.
+	
+Tue Nov  7 20:28:57 1995  Tatu Ylonen  <ylo@soikko.cs.hut.fi>
+
+	* Added RandomSeed server configuration option.  The argument
+	  specifies the location of the random seed file.  Updated
+	  documentation.
+	
+	* Locate perl5 in configure.  Generate make-ssh-known-hosts (with
+	  the correct path for perl5) in Makefile.in, and install it with
+	  the other programs.  Updated manual page.
+
+	* sshd.c (main): Added a call to umask to set the umask to a
+	  reasonable value.
+
+	* compress.c (buffer_compress): Fixed to follow the zlib
+	  documentation (which is slightly confusing).
+
+	* INSTALL: Added information about Linux libc.so.4 problem.
+
+Mon Nov  6 15:42:36 1995  Tatu Ylonen  <ylo@soikko.cs.hut.fi>
+
+	* (Actually autoconf fix) Installed patch to AC_ARG_PROGRAM.
+	
+	* sshd.c, sshd.8.in: Renamed $HOME/.environment ->
+	  $HOME/.ssh/environment.
+
+	* configure.in: Disable shadow password checking on convex.
+	  Convex has /etc/shadow, but sets pw_passwd automatically if
+	  running as root.
+
+	* Eliminated HAVE_ETC_MASTER_PASSWD (NetBSD, FreeBSD); the
+ 	  pw_passwd field is automatically filled if running as root.
+	  Put explicit code in configure.in to prevent shadow password
+	  checking on FreeBSD and NetBSD.
+	
+	* serverloop.c (signchld_handler): Don't print error if wait
+	  returns -1.
+
+	* Makefile.in (install): Fixed modes of data files.
+
+	* Makefile.in (install): Make links for slogin.1.
+
+	* make-ssh-known-hosts: Merged a patch from melo@ci.uminho.pt to
+	  fix the ping command.
+
+Fri Nov  3 16:25:28 1995  Tatu Ylonen  <ylo@soikko.cs.hut.fi>
+
+	* ssh.1.in: Added more information about X11 forwarding.
+
+Thu Nov  2 18:42:13 1995  Tatu Ylonen  <ylo@soikko.cs.hut.fi>
+
+	* Changes to use O_NONBLOCK_BROKEN consistently.
+
+	* pty.c (pty_make_controlling_tty): Use setpgid instead of
+	  setsid() on Ultrix.
+
+	* includes.h: Removed redundant #undefs for Ultrix and Sony News;
+	  these are already handled in configure.in.
+
+Tue Oct 31 13:31:28 1995  Tatu Ylonen  <ylo@soikko.cs.hut.fi>
+
+	* configure.in: Define SSH_WTMP to /var/adm/wtmp is wtmp not found.
+
+	* configure.in: Disable vhangup on Ultrix.  I am told this fixes
+	  the server problems.
+
+Sat Oct 28 14:22:05 1995  Tatu Ylonen  <ylo@soikko.cs.hut.fi>
+
+	* sshconnect.c: Fixed a bug in connecting to a multi-homed host.
+	  Restructured the connecting code to never try to use the same
+	  socket a second time after a failed connection.
+
+	* Makefile.in: Added explicit -m option to install, and umask 022
+	  when creating directories and the host key.
+
+Fri Oct 27 01:05:10 1995  Tatu Ylonen  <ylo@soikko.cs.hut.fi>
+
+	* Makefile.in: Added cleaning of $(ZLIBDIR) to clean and distclean.
+
+	* login.c (get_last_login_time): Fixed a typo (define -> defined).
+
+Thu Oct 26 01:28:07 1995  Tatu Ylonen  <ylo@soikko.cs.hut.fi>
+
+	* configure.in: Moved testing for ANSI C compiler after the host
+	  specific code (problems on HPUX).
+
+	* Minor fixes to /etc/default/login stuff from Bryan O'Sullivan.
+
+	* Fixed .SH NAME sections in manual pages.
+
+	* compress.c: Trying to fix a mysterious bug in the compression
+	  glue.
+
+	* ssh-1.2.11.
+
+	* scp.c: disable agent forwarding when running ssh from scp.
+
+	* Added compression of plaintext packets using the gzip library
+ 	  (zlib).  Client configuration options Compression and
+	  CompressionLevel (1-9 as in gzip).  New ssh and scp option -C
+	  (to enable compression).  Updated RFC.
+
+Wed Oct 25 05:11:55 1995  Tatu Ylonen  <ylo@soikko.cs.hut.fi>
+
+	* Implemented ProxyCommand stuff based on patches from Bryan
+	  O'Sullivan <bos@serpentine.com>.
+
+	* Merged BSD login/logout/lastlog patches from Mark Treacy
+	  <mark@labtam.oz.au>.
+	
+	* sshd.c: Added chdir("/").
+
+Tue Oct 24 00:29:01 1995  Tatu Ylonen  <ylo@soikko.cs.hut.fi>
+
+	* Merged RSA environment= patches from Felix Leitner
+	  <leitner@prz.tu-berlin.de> with some changes.
+	
+	* sshd.c: Made the packet code use two separate descriptors for
+	  the connection (one for input, the other for output).  This will
+	  make future extensions easier (e.g., non-socket transports, etc.).
+	  sshd -i now uses both stdin and stdout separately.
+	
+Mon Oct 23 21:29:28 1995  Tatu Ylonen  <ylo@soikko.cs.hut.fi>
+
+	* sshd.c: Merged execle -> execve patches from Mark Martinec
+ 	  <Mark.Martinec@nsc.ijs.si>.  This may help with execle bugs on
+ 	  Convex (environment not getting passed properly).  This might
+ 	  also solve similar problems on Sonys; please test!
+
+	* Removed all compatibility code for protocol version 1.0.
+	  THIS MEANS THAT WE ARE NO LONGER COMPATIBLE WITH SSH VERSIONS
+	  PRIOR TO 1.1.0.
+
+	* randoms.c (random_acquire_light_environmental_noise): If
+	  /dev/random is available, read up to 32 bytes (256 bits) from
+	  there in non-blocking mode, and mix the new random bytes into
+	  the pool.
+
+	* Added client configuration option StrictHostKeyChecking
+	  (disabled by default).  If this is enabled, the client will not
+	  automatically add new host keys to $HOME/.ssh/known_hosts;
+	  instead the connection will be refused if the host key is not
+	  known.  Similarly, if the host key has changed, the connection
+	  will be refused instead if just issuing a warning.  This
+	  provides additional security against man-in-the-middle/trojan
+	  horse attacks (especially in scripts where there is no-one to
+	  see the warnings), but may be quite inconvenient in everyday
+	  interactive use unless /etc/ssh_known_hosts is very complete,
+	  because new host keys must now be added manually.
+	
+	* sshconnect.c (ssh_connect): Use the user's uid when creating the
+	  socket and connecting it.  I am hoping that this might help with
+	  tcp_wrappers showing the remote user as root.
+
+	* ssh.c: Try inet-domain X11 forwarding regardless of whether we
+	  can get local authorization information.  If we don't, we just
+	  come up with fake information; the forwarding code will anyway
+	  generate its own fake information and validate that the client
+	  knows that information.  It will then substitute our fake
+	  information for that, but that info should get ignored by the
+	  server if it doesn't support it.
+
+	* Added option BatchMode to disable password/passphrase querying
+	  in scripts.
+
+	* auth-rh-rsa.c: Changed to use uid-swapping when reading
+	  .ssh/known_hosts.
+
+	* sshd.8.in (command): Improved documentation of file permissions
+	  on the manual pages.
+
+Thu Oct 19 21:05:51 1995  Tatu Ylonen  <ylo@soikko.cs.hut.fi>
+
+	* ssh-add.c (add_file): Fixed a bug causing ssh to sometimes refer
+	  to freed memory (comment -> saved_comment).
+
+	* log-server.c: Added a prefix to debug/warning/error/fatal
+	  messages describing message types.  Syslog does not include that
+	  information automatically.
+
+Sun Oct  8 01:56:01 1995  Tatu Ylonen  <ylo@shadows.cs.hut.fi>
+
+	* Merged /etc/default/login and MAIL environment variable changes
+	  from Bryan O'Sullivan <bos@serpentine.com>.
+	    - mail spool file location
+	    - process /etc/default/login
+	    - add HAVE_ETC_DEFAULT_LOGIN
+	    - new function child_get_env and read_etc_default_login (sshd.c)
+	
+	* ssh-add.c (add_file): Fixed asking for passphrase.
+
+	* Makefile.in: Fixed installing configure-generated man pages when
+	  compiling in a separate object directory.
+
+	* sshd.c (main): Moved RSA key generation until after allocating
+	  the port number.  (Actually, the code got duplicated because we
+	  never listen when run from inetd.)
+
+	* ssh.c: Fixed a problem that caused scp to hang when called with
+	  stdin closed.
+
+Sat Oct  7 03:08:06 1995  Tatu Ylonen  <ylo@shadows.cs.hut.fi>
+
+	* Added server config option StrictModes.  It specifies whether to
+	  check ownership and modes of home directory and .rhosts files.
+
+	* ssh.c: If ssh is renamed/linked to a host name, connect to that
+	  host.
+
+	* serverloop.c, clientloop.c: Ignore EAGAIN reported on read from
+	  connection.  Solaris has a kernel bug which causes select() to
+	  sometimes wake up even though there is no data available.
+
+	* Display all open connections when printing the "Waiting for
+	  forwarded connections to terminate" message.
+
+	* sshd.c, readconf.c: Added X11InetForwarding and
+	  X11UnixForwarding server config options.
+
+Thu Oct  5 17:41:16 1995  Tatu Ylonen  <ylo@shadows.cs.hut.fi>
+
+	* Some more SCO fixes.
+
+Tue Oct  3 01:04:34 1995  Tatu Ylonen  <ylo@shadows.cs.hut.fi>
+
+	* Fixes and cleanups in README, INSTALL, COPYING.
+
+Mon Oct  2 03:36:08 1995  Tatu Ylonen  <ylo@shadows.cs.hut.fi>
+
+	* ssh-add.c (add_file): Fixed a bug in ssh-add (xfree: NULL ...).
+
+	* Removed .BR from ".SH NAME" in man pages.
+
+Sun Oct  1 04:16:07 1995  Tatu Ylonen  <ylo@shadows.cs.hut.fi>
+
+	* ssh-1.2.10.
+	
+	* configure.in: When checking that the compiler works, check that
+	  it understands ANSI C prototypes.
+
+	* Made uidswap error message a debug() to avoid confusing errors
+	  on AIX (AIX geteuid is brain-damaged and fails even for root).
+
+	* Fixed an error in sshd.8 (FacistLogging -> FascistLogging).
+
+	* Fixed distribution in Makefile.in (missing manual page .in files).
+
+Sat Sep 30 17:38:46 1995  Tatu Ylonen  <ylo@shadows.cs.hut.fi>
+
+	* auth-rhosts.c: Fixed serious security problem in
+	  /etc/hosts.equiv authentication.
+
+Fri Sep 29 00:41:02 1995  Tatu Ylonen  <ylo@shadows.cs.hut.fi>
+
+	* Include machine/endian.h on Paragon.
+
+	* ssh-add.c (add_file): Made ssh-add keep asking for the
+	  passphrase until the user just types return or cancels.
+	  Make the dialog display the comment of the key.
+
+	* Read use shosts.equiv in addition to /etc/hosts.equiv.
+
+	* sshd.8 is now sshd.8.in and is processed by configure to
+	  substitute the proper paths for various files.  Ditto for ssh.1.
+	  Ditto for make-ssh-known-hosts.1.
+	
+	* configure.in: Moved /etc/sshd_pid to PIDDIR/sshd.pid.  PIDDIR
+	  will be /var/run if it exists, and ETCDIR otherwise.
+
+Thu Sep 28 21:52:42 1995  Tatu Ylonen  <ylo@shadows.cs.hut.fi>
+
+	* On Ultrix, check if sys/syslog.h needs to be included in
+	  addition to syslog.h.
+
+	* make-ssh-known-hosts.pl: Merged Kivinen's fixes for HPUX.
+
+	* configure.in: Put -lwrap, -lsocks, etc. at the head of LIBS.
+
+	* Fixed case-insensitivity in auth-rhosts.c.
+
+	* Added missing socketpair.c to EXTRA_SRCS (needed on SCO), plus
+	  other SCO fixes.
+
+	* Makefile.in: Fixed missing install_prefixes.
+
+Wed Sep 27 03:57:00 1995  Tatu Ylonen  <ylo@shadows.cs.hut.fi>
+
+	* ssh-1.2.9.
+
+	* Added SOCKS support.
+
+	* Fixed default setting of IgnoreRhosts option.
+
+	* Pass the magic cookie to xauth in stdin instead of command line;
+	  the command line is visible in ps.
+
+	* Added processing $HOME/.ssh/rc and /etc/sshrc.
+
+	* Added a section to sshd.8 on what happens at login time.
+
+Tue Sep 26 01:27:40 1995  Tatu Ylonen  <ylo@shadows.cs.hut.fi>
+
+	* Don't define speed_t on SunOS 4.1.1; it conflicts with system 
+	  headers.
+
+	* Added support for .hushlogin.
+
+	* Added --with-etcdir.
+
+	* Read $HOME/.environment after /etc/environment.
+
+Mon Sep 25 03:26:06 1995  Tatu Ylonen  <ylo@shadows.cs.hut.fi>
+
+	* Merged patches for SCO Unix (from Michael Henits).
+
+Sun Sep 24 22:28:02 1995  Tatu Ylonen  <ylo@shadows.cs.hut.fi>
+
+	* Added ssh option ConnectionAttempts.
+
+Sat Sep 23 12:30:15 1995  Tatu Ylonen  <ylo@shadows.cs.hut.fi>
+
+	* sshd.c: Don't print last login time and /etc/motd if a command
+	  has been specified (with ssh -t host command).
+
+	* Added support for passing the screen number in X11 forwarding.
+	  It is implemented as a compatible protocol extension, signalled
+	  by SSH_PROTOFLAG_SCREEN_NUMBER by the child.
+
+	* clientloop.c: Fixed bugs in the order in which things were
+	  processed.  This may solve problems with some data not getting
+	  sent to the server as soon as possible (probably solves the TCP
+	  forwarding delayed close problem).  Also, it looked like window
+	  changes might not get transmitted as early as possible in some
+	  cases.
+	
+	* clientloop.c: Changed to detect window size change that
+	  happened while ssh was suspended.
+
+	* ssh.c: Moved the do_session function (client main loop) to
+	  clientloop.c.  Divided it into smaller functions.  General cleanup.
+
+	* ssh-1.2.8
+
+Fri Sep 22 22:07:46 1995  Tatu Ylonen  <ylo@shadows.cs.hut.fi>
+
+	* sshconnect.c (ssh_login): Made ssh_login take the options
+	  structure as argument, instead of the individual arguments.
+
+	* auth-rhosts.c (check_rhosts_file): Added support for netgroups.
+	
+	* auth-rhosts.c (check_rhosts_file): Added support for negated 
+	  entries.
+
+Thu Sep 21 00:07:56 1995  Tatu Ylonen  <ylo@shadows.cs.hut.fi>
+
+	* auth-rhosts.c: Restructured rhosts authentication code.
+	  Hosts.equiv now has same format as .rhosts: user names are allowed.
+
+	* Added support for the Intel Paragon.
+
+	* sshd.c: Don't use X11 forwarding with spoofing if no xauth
+	  program.  Changed configure.in to not define XAUTH_PATH if
+	  there is no xauth program.
+
+	* ssh-1.2.7
+
+	* sshd.c: Rewrote the code to build the environment.  Now also reads
+	  /etc/environment.
+
+	* sshd.c: Fixed problems in libwrap code.  --with-libwrap now
+	  takes optional library name/path.
+
+	* ssh-1.2.6
+
+	* Define USE_PIPES by default.
+
+	* Added support for Univel Unixware and MachTen.
+	
+	* Added IgnoreRhosts server option.
+
+	* Added USE_STRLEN_FOR_AF_UNIX; it is needed at least on MachTen.
+
+Wed Sep 20 02:41:02 1995  Tatu Ylonen  <ylo@shadows.cs.hut.fi>
+
+	* sshd.c (do_child): don't call packet_close when /etc/nologin,
+	  because packet_close does shutdown, and the message does not get
+	  sent.
+
+	* pty.c (pty_allocate): Push ttcompat streams module.
+
+	* randoms.c (random_acquire_light_environmental_noise): Don't use
+	  the second argument to gettimeofday as it is not supported on
+	  all systems.
+
+	* login.c (record_login): Added NULL second argument to gettimeofday.
+
+Tue Sep 19 13:25:48 1995  Tatu Ylonen  <ylo@shadows.cs.hut.fi>
+
+	* fixed pclose wait() in sshd key regeneration (now only collects
+	  easily available noise).
+
+	* configure.in: test for bsdi before bsd*.
+
+	* ssh.c: Don't print "Connection closed" if -q.
+
+Wed Sep 13 04:19:52 1995  Tatu Ylonen  <ylo@shadows.cs.hut.fi>
+
+	* Released ssh-1.2.5.
+
+	* Hopefully fixed "Waiting for forwarded connections to terminate"
+	  message.
+
+	* randoms.c, md5.c: Large modifications to make these work on Cray
+	  (which has no 32 bit integer type).
+
+	* Fixed a problem with forwarded connection closes not being
+	  reported immediately.
+
+	* ssh.c: fixed rhosts authentication (broken by uid-swapping).
+
+	* scp.c: Don't use -l if server user not specified (it made
+	  setting User in the configuration file not work).
+
+	* configure.in: don't use -pipe on BSDI.
+
+	* randoms.c: Major modifications to make it work without 32 bit
+	  integers (e.g. Cray).
+
+	* md5.c: Major modifications to make it work without 32 bit
+	  integers (e.g. Cray).
+
+	* Eliminated HPSUX_BROKEN_PTYS.  The code is now enabled by 
+	  default on all systems.
+
+Mon Sep 11 00:53:12 1995  Tatu Ylonen  <ylo@shadows.cs.hut.fi>
+
+	* sshd.c: don't include sshd pathname in log messages.
+
+	* Added libwrap stuff (includes support for identd).
+
+	* Added OSF/1 C2 extended security stuff.
+
+	* Fixed interactions between getuid() and uid-swap stuff.
+
+Sun Sep 10 00:29:27 1995  Tatu Ylonen  <ylo@shadows.cs.hut.fi>
+
+	* serverloop.c: Don't send stdout data to client until after a few
+	  milliseconds if there is very little data.  This is because some
+	  systems give data from pty one character at a time, which would
+	  multiply data size by about 16.
+
+	* serverloop.c: Moved server do_session to a separate file and
+	  renamed it server_loop.  Split it into several functions and
+	  partially rewrote it.  Fixed "cat /etc/termcap | ssh foo cat" hangup.
+
+	* Screwed up something while checking stuff in under cvs.  No harm,
+	  but bogus log entries...
+
+Sat Sep  9 02:24:51 1995  Tatu Ylonen  <ylo@shadows.cs.hut.fi>
+
+	* minfd.c (_get_permanent_fd): Use SHELL environment variable.
+
+	* channels.c (x11_create_display_inet): Created
+	  HPSUX_NONSTANDARD_X11_KLUDGE; it causes DISPLAY to contain the
+	  IP address of the host instead of the name, because HPSUX uses
+	  some magic shared memory communication for local connections.
+
+	* Changed SIGHUP processing in server; it should now work multiple
+	  times.
+
+	* Added length limits in many debug/log/error/fatal calls just in
+ 	  case.
+
+	* login.c (get_last_login_time): Fixed location of lastlog.
+
+	* Rewrote all uid-swapping code.  New files uidswap.h, uidswap.c.
+
+	* Fixed several security problems involving chmod and chgrp (race
+	  conditions).  Added warnings about dubious modes for /tmp/.X11-unix.
+
+Fri Sep  8 20:03:36 1995  Tatu Ylonen  <ylo@shadows.cs.hut.fi>
+
+	* Changed readconf.c to never display anything from the config
+	  file.  This should now be prevented otherwise, but let's play safe.
+
+	* log-server.c: Use %.500s in syslog() just to be sure (they
+	  should already be shorter than 1024 though).
+
+	* sshd.c: Moved setuid in child a little earlier (just to be
+	  conservative, there was no security problem that I could detect).
+
+	* README, INSTALL: Added info about mailing list and WWW page.
+
+	* sshd.c: Added code to use SIGCHLD and wait zombies immediately.
+
+	* Merged patch to set ut_addr in utmp.
+
+	* Created ChangeLog and added it to Makefile.in.
+
+	* Use read_passphrase instead of getpass().
+
+	* Added SSH_FALLBACK_CIPHER.  Fixed a bug in default cipher
+ 	  selection (IDEA used to be selected even if not supported by the
+ 	  server).
+
+	* Use no encryption for key files if empty passphrase.
+
+	* Added section about --without-idea in INSTALL.
+
+	* Version 1.2.0 was released a couple of days ago.
+
diff --git a/ChangeLog.linux b/ChangeLog.linux
new file mode 100644
index 0000000..a28e577
--- /dev/null
+++ b/ChangeLog.linux
@@ -0,0 +1,20 @@
+19991027
+ - Adapted PAM patch.
+ - Released 1.0pre2
+
+ - Excised my buggy replacements for strlcpy and mkdtemp
+ - Imported correct OpenBSD strlcpy and mkdtemp routines.
+ - Reduced arc4random_stir entropy read to 32 bytes (256 bits)
+ - Picked up correct version number from OpenBSD
+ - Added sshd.pam PAM configuration file
+ - Added sshd.init Redhat init script
+ - Added openssh.spec RPM spec file
+ - Released 1.2pre3
+
+19991026
+ - Fixed include paths of OpenSSL functions
+ - Use OpenSSL MD5 routines
+ - Imported RC4 code from nanocrypt
+ - Wrote replacements for OpenBSD arc4random* functions
+ - Wrote replacements for strlcpy and mkdtemp
+ - Released 1.0pre1
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..668900c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,13 @@
+#	$OpenBSD: Makefile,v 1.5 1999/10/25 20:27:26 markus Exp $
+
+.include <bsd.own.mk>
+
+SUBDIR=	lib ssh sshd ssh-add ssh-keygen ssh-agent scp
+
+distribution:
+	install -C -o root -g wheel -m 0644 ${.CURDIR}/ssh_config \
+	    ${DESTDIR}/etc/ssh_config
+	install -C -o root -g wheel -m 0644 ${.CURDIR}/sshd_config \
+	    ${DESTDIR}/etc/sshd_config
+
+.include <bsd.subdir.mk>
diff --git a/Makefile.GNU b/Makefile.GNU
new file mode 100644
index 0000000..f36bdb3
--- /dev/null
+++ b/Makefile.GNU
@@ -0,0 +1,50 @@
+OPT_FLAGS=-g
+CFLAGS=$(OPT_FLAGS) -Wall -DETCDIR=\"/etc/ssh\" -DHAVE_PAM
+TARGETS=bin/libssh.a bin/ssh bin/sshd bin/ssh-add bin/ssh-keygen bin/ssh-agent bin/scp
+LFLAGS=-L./bin
+LIBS=-lssh -lcrypto -lz -lutil -lpam -ldl
+AR=ar
+RANLIB=ranlib
+
+OBJS=	authfd.o authfile.o auth-passwd.o auth-rhosts.o auth-rh-rsa.o \
+		auth-rsa.o bufaux.o buffer.o canohost.o channels.o cipher.o \
+		clientloop.o compress.o crc32.o deattack.o hostfile.o \
+		log-client.o login.o log-server.o match.o mpaux.o packet.o pty.o \
+		readconf.o readpass.o rsa.o servconf.o serverloop.o \
+		sshconnect.o tildexpand.o ttymodes.o uidswap.o xmalloc.o \
+		helper.o mktemp.o strlcpy.o rc4.o
+
+all: $(OBJS) $(TARGETS)
+
+bin/libssh.a: authfd.o authfile.o bufaux.o buffer.o canohost.o channels.o cipher.o compat.o compress.o crc32.o deattack.o hostfile.o match.o mpaux.o nchan.o packet.o readpass.o rsa.o tildexpand.o ttymodes.o uidswap.o xmalloc.o helper.o rc4.o mktemp.o strlcpy.o
+	[ -d bin ] || mkdir bin
+	$(AR) rv $@ $^
+	$(RANLIB) $@
+
+bin/ssh: ssh.o sshconnect.o log-client.o readconf.o clientloop.o
+	[ -d bin ] || mkdir bin
+	$(CC) -o $@ $^ $(LFLAGS) $(LIBS)
+
+bin/sshd:	sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o pty.o log-server.o login.o servconf.o serverloop.o
+	[ -d bin ] || mkdir bin
+	$(CC) -o $@ $^ $(LFLAGS) $(LIBS)
+
+bin/scp:	scp.o
+	[ -d bin ] || mkdir bin
+	$(CC) -o $@ $^ $(LFLAGS) $(LIBS)
+
+bin/ssh-add: ssh-add.o log-client.o
+	[ -d bin ] || mkdir bin
+	$(CC) -o $@ $^ $(LFLAGS) $(LIBS)
+
+bin/ssh-agent: ssh-agent.o log-client.o
+	[ -d bin ] || mkdir bin
+	$(CC) -o $@ $^ $(LFLAGS) $(LIBS)
+
+bin/ssh-keygen: ssh-keygen.o log-client.o
+	[ -d bin ] || mkdir bin
+	$(CC) -o $@ $^ $(LFLAGS) $(LIBS)
+
+clean:
+	rm -f *.o core bin/*
+	
diff --git a/Makefile.inc b/Makefile.inc
new file mode 100644
index 0000000..fddf3da
--- /dev/null
+++ b/Makefile.inc
@@ -0,0 +1,11 @@
+CFLAGS+=	-I${.CURDIR}/..
+
+.include <bsd.obj.mk>
+
+.if exists(${.CURDIR}/../lib/${__objdir})
+LDADD+=         -L${.CURDIR}/../lib/${__objdir} -lssh
+DPADD+=         ${.CURDIR}/../lib/${__objdir}/libssh.a
+.else
+LDADD+=         -L${.CURDIR}/../lib -lssh
+DPADD+=         ${.CURDIR}/../lib/libssh.a
+.endif
diff --git a/OVERVIEW b/OVERVIEW
new file mode 100644
index 0000000..a8b67e4
--- /dev/null
+++ b/OVERVIEW
@@ -0,0 +1,164 @@
+This document is inteded for those who wish to read the ssh source
+code.  This tries to give an overview of the structure of the code.
+      
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>
+Updated 17 Nov 1995.
+Updated 19 Oct 1999 for OpenSSH-1.2
+
+The software consists of ssh (client), sshd (server), scp, sdist, and
+the auxiliary programs ssh-keygen, ssh-agent, ssh-add, and
+make-ssh-known-hosts.  The main program for each of these is in a .c
+file with the same name.
+
+There are some subsystems/abstractions that are used by a number of
+these programs.
+
+  Buffer manipulation routines
+      
+    - These provide an arbitrary size buffer, where data can be appended.
+      Data can be consumed from either end.  The code is used heavily
+      throughout ssh.  The basic buffer manipulation functions are in
+      buffer.c (header buffer.h), and additional code to manipulate specific
+      data types is in bufaux.c.
+
+  Compression Library
+  
+    - Ssh uses the GNU GZIP compression library (ZLIB).
+
+  Encryption/Decryption
+
+    - Ssh contains several encryption algorithms.  These are all
+      accessed through the cipher.h interface.  The interface code is
+      in cipher.c, and the implementations are in libc.
+
+  Multiple Precision Integer Library
+
+    - Uses the SSLeay BIGNUM sublibrary.
+    - Some auxiliary functions for mp-int manipulation are in mpaux.c.
+
+  Random Numbers
+
+    - Uses arc4random() and such.
+
+  RSA key generation, encryption, decryption
+
+    - Ssh uses the RSA routines in libssl.
+
+  RSA key files
+
+    - RSA keys are stored in files with a special format.  The code to
+      read/write these files is in authfile.c.  The files are normally
+      encrypted with a passphrase.  The functions to read passphrases
+      are in readpass.c (the same code is used to read passwords).
+
+  Binary packet protocol
+
+    - The ssh binary packet protocol is implemented in packet.c.  The
+      code in packet.c does not concern itself with packet types or their
+      execution; it contains code to build packets, to receive them and
+      extract data from them, and the code to compress and/or encrypt
+      packets.  CRC code comes from crc32.c.
+
+    - The code in packet.c calls the buffer manipulation routines
+      (buffer.c, bufaux.c), compression routines (compress.c, zlib),
+      and the encryption routines.
+
+  X11, TCP/IP, and Agent forwarding
+
+    - Code for various types of channel forwarding is in channels.c.
+      The file defines a generic framework for arbitrary communication
+      channels inside the secure channel, and uses this framework to
+      implement X11 forwarding, TCP/IP forwarding, and authentication
+      agent forwarding.
+      The new, Protocol 1.5, channel close implementation is in nchan.c
+
+  Authentication agent
+
+    - Code to communicate with the authentication agent is in authfd.c.
+
+  Authentication methods
+
+    - Code for various authentication methods resides in auth-*.c
+      (auth-passwd.c, auth-rh-rsa.c, auth-rhosts.c, auth-rsa.c).  This
+      code is linked into the server.  The routines also manipulate
+      known hosts files using code in hostfile.c.  Code in canohost.c
+      is used to retrieve the canonical host name of the remote host.
+      Code in match.c is used to match host names.  
+
+    - In the client end, authentication code is in sshconnect.c.  It
+      reads Passwords/passphrases using code in readpass.c.  It reads
+      RSA key files with authfile.c.  It communicates the
+      authentication agent using authfd.c.
+
+  The ssh client
+
+    - The client main program is in ssh.c.  It first parses arguments
+      and reads configuration (readconf.c), then calls ssh_connect (in
+      sshconnect.c) to open a connection to the server (possibly via a
+      proxy), and performs authentication (ssh_login in sshconnect.c).
+      It then makes any pty, forwarding, etc. requests.  It may call
+      code in ttymodes.c to encode current tty modes.  Finally it
+      calls client_loop in clientloop.c.  This does the real work for
+      the session.
+
+    - The client is suid root.  It tries to temporarily give up this
+      rights while reading the configuration data.  The root
+      privileges are only used to make the connection (from a
+      privileged socket).  Any extra privileges are dropped before
+      calling ssh_login.
+
+  Pseudo-tty manipulation and tty modes
+
+    - Code to allocate and use a pseudo tty is in pty.c.  Code to
+      encode and set terminal modes is in ttymodes.c.
+
+  Logging in (updating utmp, lastlog, etc.)
+
+    - The code to do things that are done when a user logs in are in
+      login.c.  This includes things such as updating the utmp, wtmp,
+      and lastlog files.  Some of the code is in sshd.c.
+
+  Writing to the system log and terminal
+
+    - The programs use the functions fatal(), log(), debug(), error()
+      in many places to write messages to system log or user's
+      terminal.  The implementation that logs to system log is in
+      log-server.c; it is used in the server program.  The other
+      programs use an implementation that sends output to stderr; it
+      is in log-client.c.  The definitions are in ssh.h.
+
+  The sshd server (daemon)
+
+    - The sshd daemon starts by processing arguments and reading the
+      configuration file (servconf.c).  It then reads the host key,
+      starts listening for connections, and generates the server key.
+      The server key will be regenerated every hour by an alarm.
+
+    - When the server receives a connection, it forks, disables the
+      regeneration alarm, and starts communicating with the client.
+      They first perform identification string exchange, then
+      negotiate encryption, then perform authentication, preparatory
+      operations, and finally the server enters the normal session
+      mode by calling server_loop in serverloop.c.  This does the real
+      work, calling functions in other modules.
+      
+    - The code for the server is in sshd.c.  It contains a lot of
+      stuff, including:
+        - server main program
+	- waiting for connections
+	- processing new connection
+	- authentication
+	- preparatory operations
+	- building up the execution environment for the user program
+	- starting the user program.
+
+  Auxiliary files
+
+    - There are several other files in the distribution that contain
+      various auxiliary routines:
+        ssh.h	     the main header file for ssh (various definitions)
+        getput.h     byte-order independent storage of integers
+        includes.h   includes most system headers.  Lots of #ifdefs.
+	tildexpand.c expand tilde in file names
+	uidswap.c    uid-swapping
+	xmalloc.c    "safe" malloc routines
diff --git a/README b/README
new file mode 100644
index 0000000..ed36084
--- /dev/null
+++ b/README
@@ -0,0 +1,563 @@
+Ssh (Secure Shell) is a program to log into another computer over a
+network, to execute commands in a remote machine, and to move files
+from one machine to another.  It provides strong authentication and
+secure communications over insecure channels.  It is inteded as a
+replacement for rlogin, rsh, rcp, and rdist.
+
+See the file INSTALL for installation instructions.  See COPYING for
+license terms and other legal issues.  See RFC for a description of
+the protocol.  There is a WWW page for ssh; see http://www.cs.hut.fi/ssh.
+
+This file has been updated to match ssh-1.2.12.
+
+
+FEATURES
+
+ o  Strong authentication.  Closes several security holes (e.g., IP,
+    routing, and DNS spoofing).  New authentication methods: .rhosts
+    together with RSA based host authentication, and pure RSA
+    authentication.
+
+ o  Improved privacy.  All communications are automatically and
+    transparently encrypted.  RSA is used for key exchange, and a
+    conventional cipher (normally IDEA, DES, or triple-DES) for
+    encrypting the session.  Encryption is started before
+    authentication, and no passwords or other information is
+    transmitted in the clear.  Encryption is also used to protect
+    against spoofed packets.
+
+ o  Secure X11 sessions.  The program automatically sets DISPLAY on
+    the server machine, and forwards any X11 connections over the
+    secure channel.  Fake Xauthority information is automatically
+    generated and forwarded to the remote machine; the local client
+    automatically examines incoming X11 connections and replaces the
+    fake authorization data with the real data (never telling the 
+    remote machine the real information).
+
+ o  Arbitrary TCP/IP ports can be redirected through the encrypted channel
+    in both directions (e.g., for e-cash transactions).
+
+ o  No retraining needed for normal users; everything happens
+    automatically, and old .rhosts files will work with strong
+    authentication if administration installs host key files.
+
+ o  Never trusts the network.  Minimal trust on the remote side of
+    the connection.  Minimal trust on domain name servers.  Pure RSA
+    authentication never trusts anything but the private key.
+
+ o  Client RSA-authenticates the server machine in the beginning of
+    every connection to prevent trojan horses (by routing or DNS
+    spoofing) and man-in-the-middle attacks, and the server
+    RSA-authenticates the client machine before accepting .rhosts or
+    /etc/hosts.equiv authentication (to prevent DNS, routing, or
+    IP-spoofing).
+
+ o  Host authentication key distribution can be centrally by the
+    administration, automatically when the first connection is made
+    to a machine (the key obtained on the first connection will be
+    recorded and used for authentication in the future), or manually
+    by each user for his/her own use.  The central and per-user host
+    key repositories are both used and complement each other.  Host
+    keys can be generated centrally or automatically when the software
+    is installed.  Host authentication keys are typically 1024 bits.
+
+ o  Any user can create any number of user authentication RSA keys for
+    his/her own use.  Each user has a file which lists the RSA public
+    keys for which proof of possession of the corresponding private
+    key is accepted as authentication.  User authentication keys are
+    typically 1024 bits.
+
+ o  The server program has its own server RSA key which is
+    automatically regenerated every hour.  This key is never saved in
+    any file.  Exchanged session keys are encrypted using both the
+    server key and the server host key.  The purpose of the separate
+    server key is to make it impossible to decipher a captured session by
+    breaking into the server machine at a later time; one hour from
+    the connection even the server machine cannot decipher the session
+    key.  The key regeneration interval is configurable.  The server
+    key is normally 768 bits.
+
+ o  An authentication agent, running in the user's laptop or local
+    workstation, can be used to hold the user's RSA authentication
+    keys.  Ssh automatically forwards the connection to the
+    authentication agent over any connections, and there is no need to
+    store the RSA authentication keys on any machine in the network
+    (except the user's own local machine).  The authentication
+    protocols never reveal the keys; they can only be used to verify
+    that the user's agent has a certain key.  Eventually the agent
+    could rely on a smart card to perform all authentication
+    computations.
+
+ o  The software can be installed and used (with restricted
+    functionality) even without root privileges.
+
+ o  The client is customizable in system-wide and per-user
+    configuration files.  Most aspects of the client's operation can
+    be configured.  Different options can be specified on a per-host basis.
+
+ o  Automatically executes conventional rsh (after displaying a
+    warning) if the server machine is not running sshd.
+
+ o  Optional compression of all data with gzip (including forwarded X11
+    and TCP/IP port data), which may result in significant speedups on
+    slow connections.
+
+ o  Complete replacement for rlogin, rsh, and rcp.
+
+
+WHY TO USE SECURE SHELL
+
+Currently, almost all communications in computer networks are done
+without encryption.  As a consequence, anyone who has access to any
+machine connected to the network can listen in on any communication.
+This is being done by hackers, curious administrators, employers,
+criminals, industrial spies, and governments.  Some networks leak off
+enough electromagnetic radiation that data may be captured even from a
+distance.
+
+When you log in, your password goes in the network in plain
+text.  Thus, any listener can then use your account to do any evil he
+likes.  Many incidents have been encountered worldwide where crackers
+have started programs on workstations without the owners knowledge
+just to listen to the network and collect passwords.  Programs for
+doing this are available on the Internet, or can be built by a
+competent programmer in a few hours.
+
+Any information that you type or is printed on your screen can be
+monitored, recorded, and analyzed.  For example, an intruder who has
+penetrated a host connected to a major network can start a program
+that listens to all data flowing in the network, and whenever it
+encounters a 16-digit string, it checks if it is a valid credit card
+number (using the check digit), and saves the number plus any
+surrounding text (to catch expiration date and holder) in a file.
+When the intruder has collected a few thousand credit card numbers, he
+makes smallish mail-order purchases from a few thousand stores around
+the world, and disappears when the goods arrive but before anyone
+suspects anything.
+
+Businesses have trade secrets, patent applications in preparation,
+pricing information, subcontractor information, client data, personnel
+data, financial information, etc.  Currently, anyone with access to
+the network (any machine on the network) can listen to anything that
+goes in the network, without any regard to normal access restrictions.
+
+Many companies are not aware that information can so easily be
+recovered from the network.  They trust that their data is safe
+since nobody is supposed to know that there is sensitive information
+in the network, or because so much other data is transferred in the
+network.  This is not a safe policy.
+
+Individual persons also have confidential information, such as
+diaries, love letters, health care documents, information about their
+personal interests and habits, professional data, job applications,
+tax reports, political documents, unpublished manuscripts, etc.
+
+One should also be aware that economical intelligence and industrial
+espionage has recently become a major priority of the intelligence
+agencies of major governments.  President Clinton recently assigned
+economical espionage as the primary task of the CIA, and the French
+have repeatedly been publicly boasting about their achievements on
+this field.
+
+
+There is also another frightening aspect about the poor security of
+communications.  Computer storage and analysis capability has
+increased so much that it is feasible for governments, major
+companies, and criminal organizations to automatically analyze,
+identify, classify, and file information about millions of people over
+the years.  Because most of the work can be automated, the cost of
+collecting this information is getting very low.  
+
+Government agencies may be able to monitor major communication
+systems, telephones, fax, computer networks, etc., and passively
+collect huge amounts of information about all people with any
+significant position in the society.  Most of this information is not
+sensitive, and many people would say there is no harm in someone
+getting that information.  However, the information starts to get
+sensitive when someone has enough of it.  You may not mind someone
+knowing what you bought from the shop one random day, but you might
+not like someone knowing every small thing you have bought in the last
+ten years.
+
+If the government some day starts to move into a more totalitarian
+direction (one should remember that Nazi Germany was created by
+democratic elections), there is considerable danger of an ultimate
+totalitarian state.  With enough information (the automatically
+collected records of an individual can be manually analyzed when the
+person becomes interesting), one can form a very detailed picture of
+the individual's interests, opinions, beliefs, habits, friends,
+lovers, weaknesses, etc.  This information can be used to 1) locate
+any persons who might oppose the new system 2) use deception to
+disturb any organizations which might rise against the government 3)
+eliminate difficult individuals without anyone understanding what
+happened.  Additionally, if the government can monitor communications
+too effectively, it becomes too easy to locate and eliminate any
+persons distributing information contrary to the official truth.
+
+Fighting crime and terrorism are often used as grounds for domestic
+surveillance and restricting encryption.  These are good goals, but
+there is considerable danger that the surveillance data starts to get
+used for questionable purposes.  I find that it is better to tolerate
+a small amount of crime in the society than to let the society become
+fully controlled.  I am in favor of a fairly strong state, but the
+state must never get so strong that people become unable to spread
+contra-offical information and unable to overturn the government if it
+is bad.  The danger is that when you notice that the government is
+too powerful, it is too late.  Also, the real power may not be where
+the official government is.
+
+For these reasons (privacy, protecting trade secrets, and making it
+more difficult to create a totalitarian state), I think that strong
+cryptography should be integrated to the tools we use every day.
+Using it causes no harm (except for those who wish to monitor
+everything), but not using it can cause huge problems.  If the society
+changes in undesirable ways, then it will be to late to start
+encrypting.
+
+Encryption has had a "military" or "classified" flavor to it.  There
+are no longer any grounds for this.  The military can and will use its
+own encryption; that is no excuse to prevent the civilians from
+protecting their privacy and secrets.  Information on strong
+encryption is available in every major bookstore, scientific library,
+and patent office around the world, and strong encryption software is
+available in every country on the Internet.
+
+Some people would like to make it illegal to use encryption, or to
+force people to use encryption that governments can break.  This
+approach offers no protection if the government turns bad.  Also, the
+"bad guys" will be using true strong encryption anyway.  Good
+encryption techniques are too widely known to make them disappear.
+Thus, any "key escrow encryption" or other restrictions will only help
+monitor ordinary people and petty criminals.  It does not help against
+powerful criminals, terrorists, or espionage, because they will know
+how to use strong encryption anyway.  (One source for internationally
+available encryption software is http://www.cs.hut.fi/crypto.)
+
+
+OVERVIEW OF SECURE SHELL
+
+The software consists of a number of programs.
+
+   sshd		Server program run on the server machine.  This
+   		listens for connections from client machines, and
+		whenever it receives a connection, it performs
+		authentication and starts serving the client.
+
+   ssh		This is the client program used to log into another
+		machine or to execute commands on the other machine.
+		"slogin" is another name for this program.
+
+   scp		Securely copies files from one machine to another.
+
+   ssh-keygen	Used to create RSA keys (host keys and user
+   		authentication keys).
+
+   ssh-agent	Authentication agent.  This can be used to hold RSA
+   		keys for authentication.
+
+   ssh-add	Used to register new keys with the agent.
+
+   make-ssh-known-hosts
+   		Used to create the /etc/ssh_known_hosts file.
+
+
+Ssh is the program users normally use.  It is started as
+
+  ssh host
+
+or
+
+  ssh host command
+
+The first form opens a new shell on the remote machine (after
+authentication).  The latter form executes the command on the remote
+machine.
+
+When started, the ssh connects sshd on the server machine, verifies
+that the server machine really is the machine it wanted to connect,
+exchanges encryption keys (in a manner which prevents an outside
+listener from getting the keys), performs authentication using .rhosts
+and /etc/hosts.equiv, RSA authentication, or conventional password
+based authentication.  The server then (normally) allocates a
+pseudo-terminal and starts an interactive shell or user program.
+
+The TERM environment variable (describing the type of the user's
+terminal) is passed from the client side to the remote side.  Also,
+terminal modes will be copied from the client side to the remote side
+to preserve user preferences (e.g., the erase character).
+
+If the DISPLAY variable is set on the client side, the server will
+create a dummy X server and set DISPLAY accordingly.  Any connections
+to the dummy X server will be forwarded through the secure channel,
+and will be made to the real X server from the client side.  An
+arbitrary number of X programs can be started during the session, and
+starting them does not require anything special from the user.  (Note
+that the user must not manually set DISPLAY, because then it would
+connect directly to the real display instead of going through the
+encrypted channel).  This behavior can be disabled in the
+configuration file or by giving the -x option to the client.
+
+Arbitrary IP ports can be forwarded over the secure channel.  The
+program then creates a port on one side, and whenever a connection is
+opened to this port, it will be passed over the secure channel, and a
+connection will be made from the other side to a specified host:port
+pair.  Arbitrary IP forwarding must always be explicitly requested,
+and cannot be used to forward privileged ports (unless the user is
+root).  It is possible to specify automatic forwards in a per-user
+configuration file, for example to make electronic cash systems work
+securely.
+
+If there is an authentication agent on the client side, connection to
+it will be automatically forwarded to the server side.
+
+For more infomation, see the manual pages ssh(1), sshd(8), scp(1),
+ssh-keygen(1), ssh-agent(1), ssh-add(1), and make-ssh-known-hosts(1)
+included in this distribution.
+
+
+X11 CONNECTION FORWARDING
+
+X11 forwarding serves two purposes: it is a convenience to the user
+because there is no need to set the DISPLAY variable, and it provides
+encrypted X11 connections.  I cannot think of any other easy way to
+make X11 connections encrypted; modifying the X server, clients or
+libraries would require special work for each machine, vendor and
+application.  Widely used IP-level encryption does not seem likely for
+several years.  Thus what we have left is faking an X server on the
+same machine where the clients are run, and forwarding the connections
+to a real X server over the secure channel.
+
+X11 forwarding works as follows.  The client extracts Xauthority
+information for the server.  It then creates random authorization
+data, and sends the random data to the server.  The server allocates
+an X11 display number, and stores the (fake) Xauthority data for this
+display.  Whenever an X11 connection is opened, the server forwards
+the connection over the secure channel to the client, and the client
+parses the first packet of the X11 protocol, substitutes real
+authentication data for the fake data (if the fake data matched), and
+forwards the connection to the real X server.
+
+If the display does not have Xauthority data, the server will create a
+unix domain socket in /tmp/.X11-unix, and use the unix domain socket
+as the display.  No authentication information is forwarded in this
+case.  X11 connections are again forwarded over the secure channel.
+To the X server the connections appear to come from the client
+machine, and the server must have connections allowed from the local
+machine.  Using authentication data is always recommended because not
+using it makes the display insecure.  If XDM is used, it automatically
+generates the authentication data.
+
+One should be careful not to use "xin" or "xstart" or other similar
+scripts that explicitly set DISPLAY to start X sessions in a remote
+machine, because the connection will then not go over the secure
+channel.  The recommended way to start a shell in a remote machine is
+
+  xterm -e ssh host &
+
+and the recommended way to execute an X11 application in a remote
+machine is
+
+  ssh -n host emacs &
+
+If you need to type a password/passphrase for the remote machine,
+
+  ssh -f host emacs
+
+may be useful.
+
+
+
+RSA AUTHENTICATION
+
+RSA authentication is based on public key cryptograpy.  The idea is
+that there are two encryption keys, one for encryption and another for
+decryption.  It is not possible (on human timescale) to derive the
+decryption key from the encryption key.  The encryption key is called
+the public key, because it can be given to anyone and it is not
+secret.  The decryption key, on the other hand, is secret, and is
+called the private key.
+
+RSA authentication is based on the impossibility of deriving the
+private key from the public key.  The public key is stored on the
+server machine in the user's $HOME/.ssh/authorized_keys file.  The
+private key is only kept on the user's local machine, laptop, or other
+secure storage.  Then the user tries to log in, the client tells the
+server the public key that the user wishes to use for authentication.
+The server then checks if this public key is admissible.  If so, it
+generates a 256 bit random number, encrypts it with the public key,
+and sends the value to the client.  The client then decrypts the
+number with its private key, computes a 128 bit MD5 checksum from the
+resulting data, and sends the checksum back to the server.  (Only a
+checksum is sent to prevent chosen-plaintext attacks against RSA.)
+The server checks computes a checksum from the correct data,
+and compares the checksums.  Authentication is accepted if the
+checksums match.  (Theoretically this indicates that the client
+only probably knows the correct key, but for all practical purposes
+there is no doubt.)
+
+The RSA private key can be protected with a passphrase.  The
+passphrase can be any string; it is hashed with MD5 to produce an
+encryption key for IDEA, which is used to encrypt the private part of
+the key file.  With passphrase, authorization requires access to the key
+file and the passphrase.  Without passphrase, authorization only
+depends on possession of the key file.
+
+RSA authentication is the most secure form of authentication supported
+by this software.  It does not rely on the network, routers, domain
+name servers, or the client machine.  The only thing that matters is
+access to the private key.  
+
+All this, of course, depends on the security of the RSA algorithm
+itself.  RSA has been widely known since about 1978, and no effective
+methods for breaking it are known if it is used properly.  Care has
+been taken to avoid the well-known pitfalls.  Breaking RSA is widely
+believed to be equivalent to factoring, which is a very hard
+mathematical problem that has received considerable public research.
+So far, no effective methods are known for numbers bigger than about
+512 bits.  However, as computer speeds and factoring methods are
+increasing, 512 bits can no longer be considered secure.  The
+factoring work is exponential, and 768 or 1024 bits are widely
+considered to be secure in the near future.
+
+
+RHOSTS AUTHENTICATION
+
+Conventional .rhosts and hosts.equiv based authentication mechanisms
+are fundamentally insecure due to IP, DNS (domain name server) and
+routing spoofing attacks.  Additionally this authentication method
+relies on the integrity of the client machine.  These weaknesses is
+tolerable, and been known and exploited for a long time.
+
+Ssh provides an improved version of these types of authentication,
+because they are very convenient for the user (and allow easy
+transition from rsh and rlogin).  It permits these types of
+authentication, but additionally requires that the client host be
+authenticated using RSA.  
+
+The server has a list of host keys stored in /etc/ssh_known_host, and
+additionally each user has host keys in $HOME/.ssh/known_hosts.  Ssh
+uses the name servers to obtain the canonical name of the client host,
+looks for its public key in its known host files, and requires the
+client to prove that it knows the private host key.  This prevents IP
+and routing spoofing attacks (as long as the client machine private
+host key has not been compromized), but is still vulnerable to DNS
+attacks (to a limited extent), and relies on the integrity of the
+client machine as to who is requesting to log in.  This prevents
+outsiders from attacking, but does not protect against very powerful
+attackers.  If maximal security is desired, only RSA authentication
+should be used.
+
+It is possible to enable conventional .rhosts and /etc/hosts.equiv
+authentication (without host authentication) at compile time by giving
+the option --with-rhosts to configure.  However, this is not
+recommended, and is not done by default.
+
+These weaknesses are present in rsh and rlogin.  No improvement in
+security will be obtained unless rlogin and rsh are completely
+disabled (commented out in /etc/inetd.conf).  This is highly
+recommended.
+
+
+WEAKEST LINKS IN SECURITY
+
+One should understand that while this software may provide
+cryptographically secure communications, it may be easy to
+monitor the communications at their endpoints.
+
+Basically, anyone with root access on the local machine on which you
+are running the software may be able to do anything.  Anyone with root
+access on the server machine may be able to monitor your
+communications, and a very talented root user might even be able to
+send his/her own requests to your authentication agent.
+
+One should also be aware that computers send out electromagnetic
+radition that can sometimes be picked up hundreds of meters away.
+Your keyboard is particularly easy to listen to.  The image on your
+monitor might also be seen on another monitor in a van parked behind
+your house.
+
+Beware that unwanted visitors might come to your home or office and
+use your machine while you are away.  They might also make
+modifications or install bugs in your hardware or software.
+
+Beware that the most effective way for someone to decrypt your data
+may be with a rubber hose.
+
+
+LEGAL ISSUES
+
+As far as I am concerned, anyone is permitted to use this software
+freely.  However, see the file COPYING for detailed copying,
+licensing, and distribution information.
+
+In some countries, particularly France, Russia, Iraq, and Pakistan,
+it may be illegal to use any encryption at all without a special
+permit, and the rumor has it that you cannot get a permit for any
+strong encryption.
+
+This software may be freely imported into the United States; however,
+the United States Government may consider re-exporting it a criminal
+offence.
+
+Note that any information and cryptographic algorithms used in this
+software are publicly available on the Internet and at any major
+bookstore, scientific library, or patent office worldwide.
+
+THERE IS NO WARRANTY FOR THIS PROGRAM.  Please consult the file
+COPYING for more information.
+
+
+MAILING LISTS AND OTHER INFORMATION
+
+There is a mailing list for ossh.  It is ossh@sics.se.  If you would
+like to join, send a message to majordomo@sics.se with "subscribe
+ssh" in body.
+
+The WWW home page for ssh is http://www.cs.hut.fi/ssh.  It contains an
+archive of the mailing list, and detailed information about new
+releases, mailing lists, and other relevant issues.
+
+Bug reports should be sent to ossh-bugs@sics.se.
+
+
+ABOUT THE AUTHOR
+
+This software was written by Tatu Ylonen <ylo@cs.hut.fi>.  I work as a
+researcher at Helsinki University of Technology, Finland.  For more
+information, see http://www.cs.hut.fi/~ylo/.  My PGP public key is
+available via finger from ylo@cs.hut.fi and from the key servers.  I
+prefer PGP encrypted mail.
+
+The author can be contacted via ordinary mail at
+  Tatu Ylonen
+  Helsinki University of Technology
+  Otakaari 1
+  FIN-02150 ESPOO
+  Finland
+
+  Fax. +358-0-4513293
+
+
+ACKNOWLEDGEMENTS
+
+I thank Tero Kivinen, Timo Rinne, Janne Snabb, and Heikki Suonsivu for
+their help and comments in the design, implementation and porting of
+this software.  I also thank numerous contributors, including but not
+limited to Walker Aumann, Jurgen Botz, Hans-Werner Braun, Stephane
+Bortzmeyer, Adrian Colley, Michael Cooper, David Dombek, Jerome
+Etienne, Bill Fithen, Mark Fullmer, Bert Gijsbers, Andreas Gustafsson,
+Michael Henits, Steve Johnson, Thomas Koenig, Felix Leitner, Gunnar
+Lindberg, Andrew Macpherson, Marc Martinec, Paul Mauvais, Donald
+McKillican, Leon Mlakar, Robert Muchsel, Mark Treacy, Bryan
+O'Sullivan, Mikael Suokas, Ollivier Robert, Jakob Schlyter, Tomasz
+Surmacz, Alvar Vinacua, Petri Virkkula, Michael Warfield, and
+Cristophe Wolfhugel.
+
+Thanks also go to Philip Zimmermann, whose PGP software and the
+associated legal battle provided inspiration, motivation, and many
+useful techniques, and to Bruce Schneier whose book Applied
+Cryptography has done a great service in widely distributing knowledge
+about cryptographic methods.
+
+
+Copyright (c) 1995 Tatu Ylonen, Espoo, Finland.
diff --git a/README.openssh b/README.openssh
new file mode 100644
index 0000000..02cb3c6
--- /dev/null
+++ b/README.openssh
@@ -0,0 +1,44 @@
+This is a Linux port of OpenBSD's excellent OpenSSH. 
+
+OpenSSH is based on the last free version of Tatu Ylonen's SSH with all 
+patent-encumbered algorithms removed, all known security bugs fixed, new 
+features reintroduced and many other clean-ups.
+
+This Linux port basically consists of a few fixes to deal with the way that
+OpenSSL is usually installed on Linux systems, a few replacements for 
+OpenBSD library functions and the introduction of partial PAM support.
+
+The PAM support is less than optimal - it is only used when password 
+authentication is requested, so things like pam_limits will not apply if a
+user authenticates with a RSA key. OTOH this is exactly the level of support
+that the popular Linux SSH packages have. Perhaps a PAM hacker can rectify 
+this?
+
+All new code is released under a XFree style license, which is very liberal.
+This code is released with no warranties of any kind, neither I nor my 
+employer (Internet Business Solutions) will take any responsibility for 
+any loss, damage or liability arising from the use or abuse of this software.
+
+OpenSSH depends on Zlib, OpenSSL and PAM. Use the Makefile.GNU to build it.
+
+Damien Miller <djm@ibs.com.au>
+Internet Business Solutions
+
+
+Credits - 
+
+The OpenBSD team
+'jonchen' - the original author of PAM support of SSH
+
+Miscellania - 
+
+This version of SSH is based upon code retrieved from the OpenBSD CVS
+repository on 1999-10-26, which in turn was based on the last free 
+version of SSH released by Tatu Ylonen.
+
+Code in helper.[ch] is Copyright 1999 Internet Business Solutions and
+is released under a X11-style license (see source file for details).
+
+(A)RC4 code in rc4.[ch] is Copyright 1999 Damien Miller. It too is
+under a X11-style license (see source file for details).
+
diff --git a/RFC.nroff b/RFC.nroff
new file mode 100644
index 0000000..cc197aa
--- /dev/null
+++ b/RFC.nroff
@@ -0,0 +1,1780 @@
+.\" -*- nroff -*-
+.\"
+.\" $Id: RFC.nroff,v 1.1 1999/10/27 03:42:43 damien Exp $
+.\"
+.pl 10.0i
+.po 0
+.ll 7.2i
+.lt 7.2i
+.nr LL 7.2i
+.nr LT 7.2i
+.ds LF Ylonen
+.ds RF FORMFEED[Page %]
+.ds CF
+.ds LH Internet-Draft
+.ds RH 15 November 1995
+.ds CH SSH (Secure Shell) Remote Login Protocol
+.na
+.hy 0
+.in 0
+Network Working Group					       T. Ylonen
+Internet-Draft			       Helsinki University of Technology
+draft-ylonen-ssh-protocol-00.txt			15 November 1995
+Expires: 15 May 1996
+
+.in 3
+
+.ce
+The SSH (Secure Shell) Remote Login Protocol
+
+.ti 0
+Status of This Memo
+
+This document is an Internet-Draft.   Internet-Drafts  are  working
+documents of the Internet Engineering Task Force (IETF), its areas,
+and its working groups.  Note that other groups may also distribute
+working documents as Internet-Drafts.
+
+Internet-Drafts are draft documents valid  for  a  maximum  of  six
+months  and  may  be updated, replaced, or obsoleted by other docu-
+ments at any time.  It is inappropriate to use  Internet-Drafts  as
+reference  material  or  to  cite them other than as ``work in pro-
+gress.''
+
+To learn the current status of any Internet-Draft, please check the
+``1id-abstracts.txt'' listing contained in the Internet- Drafts Shadow
+Directories on ftp.is.co.za (Africa), nic.nordu.net (Europe),
+munnari.oz.au (Pacific Rim), ds.internic.net (US East Coast), or
+ftp.isi.edu (US West Coast).
+
+The distribution of  this  memo  is  unlimited.
+
+.ti 0
+Introduction
+
+SSH (Secure Shell) is a program to log into another computer over a
+network, to execute commands in a remote machine, and to move files
+from one machine to another.  It provides strong authentication and
+secure communications over insecure networks.  Its features include
+the following:
+.IP o
+Closes several security holes (e.g., IP, routing, and DNS spoofing).
+New authentication methods: .rhosts together with RSA [RSA] based host
+authentication, and pure RSA authentication.
+.IP o
+All communications are automatically and transparently encrypted.
+Encryption is also used to protect integrity.
+.IP o
+X11 connection forwarding provides secure X11 sessions.
+.IP o
+Arbitrary TCP/IP ports can be redirected over the encrypted channel
+in both directions.
+.IP o
+Client RSA-authenticates the server machine in the beginning of every
+connection to prevent trojan horses (by routing or DNS spoofing) and
+man-in-the-middle attacks, and the server RSA-authenticates the client
+machine before accepting .rhosts or /etc/hosts.equiv authentication
+(to prevent DNS, routing, or IP spoofing).
+.IP o
+An authentication agent, running in the user's local workstation or
+laptop, can be used to hold the user's RSA authentication keys.
+.RT
+
+The goal has been to make the software as easy to use as possible for
+ordinary users.  The protocol has been designed to be as secure as
+possible while making it possible to create implementations that
+are easy to use and install.  The sample implementation has a number
+of convenient features that are not described in this document as they
+are not relevant for the protocol.
+
+
+.ti 0
+Overview of the Protocol
+
+The software consists of a server program running on a server machine,
+and a client program running on a client machine (plus a few auxiliary
+programs).  The machines are connected by an insecure IP [RFC0791]
+network (that can be monitored, tampered with, and spoofed by hostile
+parties).
+
+A connection is always initiated by the client side.  The server
+listens on a specific port waiting for connections.  Many clients may
+connect to the same server machine.
+
+The client and the server are connected via a TCP/IP [RFC0793] socket
+that is used for bidirectional communication.  Other types of
+transport can be used but are currently not defined.
+
+When the client connects the server, the server accepts the connection
+and responds by sending back its version identification string.  The
+client parses the server's identification, and sends its own
+identification.  The purpose of the identification strings is to
+validate that the connection was to the correct port, declare the
+protocol version number used, and to declare the software version used
+on each side (for debugging purposes).  The identification strings are
+human-readable.  If either side fails to understand or support the
+other side's version, it closes the connection.
+
+After the protocol identification phase, both sides switch to a packet
+based binary protocol.  The server starts by sending its host key
+(every host has an RSA key used to authenticate the host), server key
+(an RSA key regenerated every hour), and other information to the
+client.  The client then generates a 256 bit session key, encrypts it
+using both RSA keys (see below for details), and sends the encrypted
+session key and selected cipher type to the server.  Both sides then
+turn on encryption using the selected algorithm and key.  The server
+sends an encrypted confirmation message to the client.
+
+The client then authenticates itself using any of a number of
+authentication methods.  The currently supported authentication
+methods are .rhosts or /etc/hosts.equiv authentication (disabled by
+default), the same with RSA-based host authentication, RSA
+authentication, and password authentication.
+
+After successful authentication, the client makes a number of requests
+to prepare for the session.  Typical requests include allocating a
+pseudo tty, starting X11 [X11] or TCP/IP port forwarding, starting
+authentication agent forwarding, and executing the shell or a command.
+
+When a shell or command is executed, the connection enters interactive
+session mode.  In this mode, data is passed in both directions, 
+new forwarded connections may be opened, etc.  The interactive session
+normally terminates when the server sends the exit status of the
+program to the client.
+
+
+The protocol makes several reservations for future extensibility.
+First of all, the initial protocol identification messages include the
+protocol version number.  Second, the first packet by both sides
+includes a protocol flags field, which can be used to agree on
+extensions in a compatible manner.  Third, the authentication and
+session preparation phases work so that the client sends requests to
+the server, and the server responds with success or failure.  If the
+client sends a request that the server does not support, the server
+simply returns failure for it.  This permits compatible addition of
+new authentication methods and preparation operations.  The
+interactive session phase, on the other hand, works asynchronously and
+does not permit the use of any extensions (because there is no easy
+and reliable way to signal rejection to the other side and problems
+would be hard to debug).  Any compatible extensions to this phase must
+be agreed upon during any of the earlier phases.
+
+.ti 0
+The Binary Packet Protocol
+
+After the protocol identification strings, both sides only send
+specially formatted packets.  The packet layout is as follows:
+.IP o
+Packet length: 32 bit unsigned integer, coded as four 8-bit bytes, msb
+first.  Gives the length of the packet, not including the length field
+and padding.  The maximum length of a packet (not including the length
+field and padding) is 262144 bytes.
+.IP o
+Padding: 1-8 bytes of random data (or zeroes if not encrypting).  The
+amount of padding is (8 - (length % 8)) bytes (where % stands for the
+modulo operator).  The rationale for always having some random padding
+at the beginning of each packet is to make known plaintext attacks
+more difficult.
+.IP o
+Packet type: 8-bit unsigned byte.  The value 255 is reserved for
+future extension.
+.IP o
+Data: binary data bytes, depending on the packet type.  The number of
+data bytes is the "length" field minus 5.
+.IP o
+Check bytes: 32-bit crc, four 8-bit bytes, msb first.  The crc is the
+Cyclic Redundancy Check, with the polynomial 0xedb88320, of the
+Padding, Packet type, and Data fields.  The crc is computed before
+any encryption.
+.RT
+
+The packet, except for the length field, may be encrypted using any of
+a number of algorithms.  The length of the encrypted part (Padding +
+Type + Data + Check) is always a multiple of 8 bytes.  Typically the
+cipher is used in a chained mode, with all packets chained together as
+if it was a single data stream (the length field is never included in
+the encryption process).  Details of encryption are described below.
+
+When the session starts, encryption is turned off.  Encryption is
+enabled after the client has sent the session key.  The encryption
+algorithm to use is selected by the client.
+
+
+.ti 0
+Packet Compression
+
+If compression is supported (it is an optional feature, see
+SSH_CMSG_REQUEST_COMPRESSION below), the packet type and data fields
+of the packet are compressed using the gzip deflate algorithm [GZIP].
+If compression is in effect, the packet length field indicates the
+length of the compressed data, plus 4 for the crc.  The amount of
+padding is computed from the compressed data, so that the amount of
+data to be encrypted becomes a multiple of 8 bytes.
+
+When compressing, the packets (type + data portions) in each direction
+are compressed as if they formed a continuous data stream, with only the
+current compression block flushed between packets.  This corresponds
+to the GNU ZLIB library Z_PARTIAL_FLUSH option.  The compression
+dictionary is not flushed between packets.  The two directions are
+compressed independently of each other.
+
+
+.ti 0
+Packet Encryption
+
+The protocol supports several encryption methods.  During session
+initialization, the server sends a bitmask of all encryption methods
+that it supports, and the client selects one of these methods.  The
+client also generates a 256-bit random session key (32 8-bit bytes) and
+sends it to the server.
+
+The encryption methods supported by the current implementation, and
+their codes are:
+.TS
+center;
+l r l.
+SSH_CIPHER_NONE	0	   No encryption
+SSH_CIPHER_IDEA	1	   IDEA in CFB mode
+SSH_CIPHER_DES	2	   DES in CBC mode
+SSH_CIPHER_3DES	3	   Triple-DES in CBC mode
+SSH_CIPHER_TSS	4	   An experimental stream cipher
+SSH_CIPHER_RC4	5	   RC4
+.TE
+
+All implementations are required to support SSH_CIPHER_DES and
+SSH_CIPHER_3DES.  Supporting SSH_CIPHER_IDEA, SSH_CIPHER_RC4, and
+SSH_CIPHER_NONE is recommended.  Support for SSH_CIPHER_TSS is
+optional (and it is not described in this document).  Other ciphers
+may be added at a later time; support for them is optional.
+
+For encryption, the encrypted portion of the packet is considered a
+linear byte stream.  The length of the stream is always a multiple of
+8.  The encrypted portions of consecutive packets (in the same
+direction) are encrypted as if they were a continuous buffer (that is,
+any initialization vectors are passed from the previous packet to the
+next packet).  Data in each direction is encrypted independently.
+.IP SSH_CIPHER_DES
+The key is taken from the first 8 bytes of the session key.  The least
+significant bit of each byte is ignored.  This results in 56 bits of
+key data.  DES [DES] is used in CBC mode.  The iv (initialization vector) is
+initialized to all zeroes.
+.IP SSH_CIPHER_3DES
+The variant of triple-DES used here works as follows: there are three
+independent DES-CBC ciphers, with independent initialization vectors.
+The data (the whole encrypted data stream) is first encrypted with the
+first cipher, then decrypted with the second cipher, and finally
+encrypted with the third cipher.  All these operations are performed
+in CBC mode.
+
+The key for the first cipher is taken from the first 8 bytes of the
+session key; the key for the next cipher from the next 8 bytes, and
+the key for the third cipher from the following 8 bytes.  All three
+initialization vectors are initialized to zero.
+
+(Note: the variant of 3DES used here differs from some other
+descriptions.)
+.IP SSH_CIPHER_IDEA
+The key is taken from the first 16 bytes of the session key.  IDEA
+[IDEA] is used in CFB mode.  The initialization vector is initialized
+to all zeroes.
+.IP SSH_CIPHER_TSS
+All 32 bytes of the session key are used as the key.
+
+There is no reference available for the TSS algorithm; it is currently
+only documented in the sample implementation source code.  The
+security of this cipher is unknown (but it is quite fast).  The cipher
+is basically a stream cipher that uses MD5 as a random number
+generator and takes feedback from the data.
+.IP SSH_CIPHER_RC4
+The first 16 bytes of the session key are used as the key for the
+server to client direction.  The remaining 16 bytes are used as the
+key for the client to server direction.  This gives independent
+128-bit keys for each direction.
+
+This algorithm is the alleged RC4 cipher posted to the Usenet in 1995.
+It is widely believed to be equivalent with the original RSADSI RC4
+cipher.  This is a very fast algorithm.
+.RT
+
+
+.ti 0
+Data Type Encodings
+
+The Data field of each packet contains data encoded as described in
+this section.  There may be several data items; each item is coded as
+described here, and their representations are concatenated together
+(without any alignment or padding).
+
+Each data type is stored as follows:
+.IP "8-bit byte"
+The byte is stored directly as a single byte.
+.IP "32-bit unsigned integer"
+Stored in 4 bytes, msb first.
+.IP "Arbitrary length binary string"
+First 4 bytes are the length of the string, msb first (not including
+the length itself).  The following "length" bytes are the string
+value.  There are no terminating null characters.
+.IP "Multiple-precision integer"
+First 2 bytes are the number of bits in the integer, msb first (for
+example, the value 0x00012345 would have 17 bits).  The value zero has
+zero bits.  It is permissible that the number of bits be larger than the
+real number of bits.
+
+The number of bits is followed by (bits + 7) / 8 bytes of binary data,
+msb first, giving the value of the integer.
+.RT
+
+
+.ti 0
+TCP/IP Port Number and Other Options
+
+The server listens for connections on TCP/IP port 22.
+
+The client may connect the server from any port.  However, if the
+client wishes to use any form of .rhosts or /etc/hosts.equiv
+authentication, it must connect from a privileged port (less than
+1024).
+
+For the IP Type of Service field [RFC0791], it is recommended that
+interactive sessions (those having a user terminal or forwarding X11
+connections) use the IPTOS_LOWDELAY, and non-interactive connections
+use IPTOS_THROUGHPUT.
+
+It is recommended that keepalives are used, because otherwise programs
+on the server may never notice if the other end of the connection is
+rebooted.
+
+
+.ti 0
+Protocol Version Identification
+
+After the socket is opened, the server sends an identification string,
+which is of the form
+"SSH-<protocolmajor>.<protocolminor>-<version>\\n", where
+<protocolmajor> and <protocolminor> are integers and specify the
+protocol version number (not software distribution version).
+<version> is server side software version string (max 40 characters);
+it is not interpreted by the remote side but may be useful for
+debugging.
+
+The client parses the server's string, and sends a corresponding
+string with its own information in response.  If the server has lower
+version number, and the client contains special code to emulate it,
+the client responds with the lower number; otherwise it responds with
+its own number.  The server then compares the version number the
+client sent with its own, and determines whether they can work
+together.  The server either disconnects, or sends the first packet
+using the binary packet protocol and both sides start working
+according to the lower of the protocol versions.
+
+By convention, changes which keep the protocol compatible with
+previous versions keep the same major protocol version; changes that
+are not compatible increment the major version (which will hopefully
+never happen).  The version described in this document is 1.3.
+
+The client will 
+
+.ti 0
+Key Exchange and Server Host Authentication
+
+The first message sent by the server using the packet protocol is
+SSH_SMSG_PUBLIC_KEY.  It declares the server's host key, server public
+key, supported ciphers, supported authentication methods, and flags
+for protocol extensions.  It also contains a 64-bit random number
+(cookie) that must be returned in the client's reply (to make IP
+spoofing more difficult).  No encryption is used for this message.
+
+Both sides compute a session id as follows.  The modulus of the server
+key is interpreted as a byte string (without explicit length field,
+with minimum length able to hold the whole value), most significant
+byte first.  This string is concatenated with the server host key
+interpreted the same way.  Additionally, the cookie is concatenated
+with this.  Both sides compute MD5 of the resulting string.  The
+resulting 16 bytes (128 bits) are stored by both parties and are
+called the session id.
+
+The client responds with a SSH_CMSG_SESSION_KEY message, which
+contains the selected cipher type, a copy of the 64-bit cookie sent by
+the server, client's protocol flags, and a session key encrypted
+with both the server's host key and server key.  No encryption is used
+for this message.
+
+The session key is 32 8-bit bytes (a total of 256 random bits
+generated by the client).  The client first xors the 16 bytes of the
+session id with the first 16 bytes of the session key.  The resulting
+string is then encrypted using the smaller key (one with smaller
+modulus), and the result is then encrypted using the other key.  The
+number of bits in the public modulus of the two keys must differ by at
+least 128 bits.
+
+At each encryption step, a multiple-precision integer is constructed
+from the data to be encrypted as follows (the integer is here
+interpreted as a sequence of bytes, msb first; the number of bytes is
+the number of bytes needed to represent the modulus).
+
+The most significant byte (which is only partial as the value must be
+less than the public modulus, which is never a power of two) is zero.
+
+The next byte contains the value 2 (which stands for public-key
+encrypted data in the PKCS standard [PKCS#1]).  Then, there are
+non-zero random bytes to fill any unused space, a zero byte, and the
+data to be encrypted in the least significant bytes, the last byte of
+the data in the least significant byte.
+
+This algorithm is used twice.  First, it is used to encrypt the 32
+random bytes generated by the client to be used as the session key
+(xored by the session id).  This value is converted to an integer as
+described above, and encrypted with RSA using the key with the smaller
+modulus.  The resulting integer is converted to a byte stream, msb
+first.  This byte stream is padded and encrypted identically using the
+key with the larger modulus.
+
+After the client has sent the session key, it starts to use the
+selected algorithm and key for decrypting any received packets, and
+for encrypting any sent packets.  Separate ciphers are used for
+different directions (that is, both directions have separate
+initialization vectors or other state for the ciphers).
+
+When the server has received the session key message, and has turned
+on encryption, it sends a SSH_SMSG_SUCCESS message to the client.
+
+The recommended size of the host key is 1024 bits, and 768 bits for
+the server key.  The minimum size is 512 bits for the smaller key.
+
+
+.ti 0
+Declaring the User Name
+
+The client then sends a SSH_CMSG_USER message to the server.  This
+message specifies the user name to log in as.
+
+The server validates that such a user exists, checks whether
+authentication is needed, and responds with either SSH_SMSG_SUCCESS or
+SSH_SMSG_FAILURE.  SSH_SMSG_SUCCESS indicates that no authentication
+is needed for this user (no password), and authentication phase has
+now been completed.  SSH_SMSG_FAILURE indicates that authentication is
+needed (or the user does not exist).
+
+If the user does not exist, it is recommended that this returns
+failure, but the server keeps reading messages from the client, and
+responds to any messages (except SSH_MSG_DISCONNECT, SSH_MSG_IGNORE,
+and SSH_MSG_DEBUG) with SSH_SMSG_FAILURE.  This way the client cannot
+be certain whether the user exists.
+
+
+.ti 0
+Authentication Phase
+
+Provided the server didn't immediately accept the login, an
+authentication exchange begins.  The client sends messages to the
+server requesting different types of authentication in arbitrary order as
+many times as desired (however, the server may close the connection
+after a timeout).  The server always responds with SSH_SMSG_SUCCESS if
+it has accepted the authentication, and with SSH_SMSG_FAILURE if it has
+denied authentication with the requested method or it does not
+recognize the message.  Some authentication methods cause an exchange
+of further messages before the final result is sent.  The
+authentication phase ends when the server responds with success.
+
+The recommended value for the authentication timeout (timeout before
+disconnecting if no successful authentication has been made) is 5
+minutes.
+
+The following authentication methods are currently supported:
+.TS
+center;
+l r l.
+SSH_AUTH_RHOSTS	1	.rhosts or /etc/hosts.equiv
+SSH_AUTH_RSA	2	pure RSA authentication
+SSH_AUTH_PASSWORD	3	password authentication
+SSH_AUTH_RHOSTS_RSA	4	.rhosts with RSA host authentication
+.TE
+.IP SSH_AUTH_RHOSTS
+
+This is the authentication method used by rlogin and rsh [RFC1282].
+
+The client sends SSH_CMSG_AUTH_RHOSTS with the client-side user name
+as an argument.
+
+The server checks whether to permit authentication.  On UNIX systems,
+this is usually done by checking /etc/hosts.equiv, and .rhosts in the
+user's home directory.  The connection must come from a privileged
+port.
+
+It is recommended that the server checks that there are no IP options
+(such as source routing) specified for the socket before accepting
+this type of authentication.  The client host name should be
+reverse-mapped and then forward mapped to ensure that it has the
+proper IP-address.
+
+This authentication method trusts the remote host (root on the remote
+host can pretend to be any other user on that host), the name
+services, and partially the network: anyone who can see packets coming
+out from the server machine can do IP-spoofing and pretend to be any
+machine; however, the protocol prevents blind IP-spoofing (which used
+to be possible with rlogin).
+
+Many sites probably want to disable this authentication method because
+of the fundamental insecurity of conventional .rhosts or
+/etc/hosts.equiv authentication when faced with spoofing.  It is
+recommended that this method not be supported by the server by
+default.
+.IP SSH_AUTH_RHOSTS_RSA
+
+In addition to conventional .rhosts and hosts.equiv authentication,
+this method additionally requires that the client host be
+authenticated using RSA.
+
+The client sends SSH_CMSG_AUTH_RHOSTS_RSA specifying the client-side
+user name, and the public host key of the client host.
+
+The server first checks if normal .rhosts or /etc/hosts.equiv
+authentication would be accepted, and if not, responds with
+SSH_SMSG_FAILURE.  Otherwise, it checks whether it knows the host key
+for the client machine (using the same name for the host that was used
+for checking the .rhosts and /etc/hosts.equiv files).  If it does not
+know the RSA key for the client, access is denied and SSH_SMSG_FAILURE
+is sent.
+
+If the server knows the host key of the client machine, it verifies
+that the given host key matches that known for the client.  If not,
+access is denied and SSH_SMSG_FAILURE is sent.
+
+The server then sends a SSH_SMSG_AUTH_RSA_CHALLENGE message containing
+an encrypted challenge for the client.  The challenge is 32 8-bit
+random bytes (256 bits).  When encrypted, the highest (partial) byte
+is left as zero, the next byte contains the value 2, the following are
+non-zero random bytes, followed by a zero byte, and the challenge put
+in the remaining bytes.  This is then encrypted using RSA with the
+client host's public key.  (The padding and encryption algorithm is
+the same as that used for the session key.)
+
+The client decrypts the challenge using its private host key,
+concatenates this with the session id, and computes an MD5 checksum
+of the resulting 48 bytes.  The MD5 output is returned as 16 bytes in
+a SSH_CMSG_AUTH_RSA_RESPONSE message.  (MD5 is used to deter chosen
+plaintext attacks against RSA; the session id binds it to a specific
+session).
+
+The server verifies that the MD5 of the decrypted challenge returned by
+the client matches that of the original value, and sends SSH_SMSG_SUCCESS if
+so.  Otherwise it sends SSH_SMSG_FAILURE and refuses the
+authentication attempt.
+
+This authentication method trusts the client side machine in that root
+on that machine can pretend to be any user on that machine.
+Additionally, it trusts the client host key.  The name and/or IP
+address of the client host is only used to select the public host key.
+The same host name is used when scanning .rhosts or /etc/hosts.equiv
+and when selecting the host key.  It would in principle be possible to
+eliminate the host name entirely and substitute it directly by the
+host key.  IP and/or DNS [RFC1034] spoofing can only be used
+to pretend to be a host for which the attacker has the private host
+key.
+.IP SSH_AUTH_RSA
+
+The idea behind RSA authentication is that the server recognizes the
+public key offered by the client, generates a random challenge, and
+encrypts the challenge with the public key.  The client must then
+prove that it has the corresponding private key by decrypting the
+challenge.
+
+The client sends SSH_CMSG_AUTH_RSA with public key modulus (n) as an
+argument.
+
+The server may respond immediately with SSH_SMSG_FAILURE if it does
+not permit authentication with this key.  Otherwise it generates a
+challenge, encrypts it using the user's public key (stored on the
+server and identified using the modulus), and sends
+SSH_SMSG_AUTH_RSA_CHALLENGE with the challenge (mp-int) as an
+argument.
+
+The challenge is 32 8-bit random bytes (256 bits).  When encrypted,
+the highest (partial) byte is left as zero, the next byte contains the
+value 2, the following are non-zero random bytes, followed by a zero
+byte, and the challenge put in the remaining bytes.  This is then
+encrypted with the public key.  (The padding and encryption algorithm
+is the same as that used for the session key.)
+
+The client decrypts the challenge using its private key, concatenates
+it with the session id, and computes an MD5 checksum of the resulting
+48 bytes.  The MD5 output is returned as 16 bytes in a
+SSH_CMSG_AUTH_RSA_RESPONSE message.  (Note that the MD5 is necessary
+to avoid chosen plaintext attacks against RSA; the session id binds it
+to a specific session.)
+
+The server verifies that the MD5 of the decrypted challenge returned
+by the client matches that of the original value, and sends
+SSH_SMSG_SUCCESS if so.  Otherwise it sends SSH_SMSG_FAILURE and
+refuses the authentication attempt.
+
+This authentication method does not trust the remote host, the
+network, name services, or anything else.  Authentication is based
+solely on the possession of the private identification keys.  Anyone
+in possession of the private keys can log in, but nobody else.
+
+The server may have additional requirements for a successful
+authentiation.  For example, to limit damage due to a compromised RSA
+key, a server might restrict access to a limited set of hosts.
+.IP SSH_AUTH_PASSWORD
+
+The client sends a SSH_CMSG_AUTH_PASSWORD message with the plain text
+password.  (Note that even though the password is plain text inside
+the message, it is normally encrypted by the packet mechanism.)
+
+The server verifies the password, and sends SSH_SMSG_SUCCESS if
+authentication was accepted and SSH_SMSG_FAILURE otherwise.
+
+Note that the password is read from the user by the client; the user
+never interacts with a login program.
+
+This authentication method does not trust the remote host, the
+network, name services or anything else.  Authentication is based
+solely on the possession of the password.  Anyone in possession of the
+password can log in, but nobody else.
+.RT
+
+.ti 0
+Preparatory Operations
+
+After successful authentication, the server waits for a request from
+the client, processes the request, and responds with SSH_SMSG_SUCCESS
+whenever a request has been successfully processed.  If it receives a
+message that it does not recognize or it fails to honor a request, it
+returns SSH_SMSG_FAILURE.  It is expected that new message types might
+be added to this phase in future.
+
+The following messages are currently defined for this phase.
+.IP SSH_CMSG_REQUEST_COMPRESSION
+Requests that compression be enabled for this session.  A
+gzip-compatible compression level (1-9) is passed as an argument.
+.IP SSH_CMSG_REQUEST_PTY
+Requests that a pseudo terminal device be allocated for this session.
+The user terminal type and terminal modes are supplied as arguments.
+.IP SSH_CMSG_X11_REQUEST_FORWARDING
+Requests forwarding of X11 connections from the remote machine to the
+local machine over the secure channel.  Causes an internet-domain
+socket to be allocated and the DISPLAY variable to be set on the server.
+X11 authentication data is automatically passed to the server, and the
+client may implement spoofing of authentication data for added
+security.  The authentication data is passed as arguments.
+.IP SSH_CMSG_PORT_FORWARD_REQUEST
+Requests forwarding of a TCP/IP port on the server host over the
+secure channel.  What happens is that whenever a connection is made to
+the port on the server, a connection will be made from the client end
+to the specified host/port.  Any user can forward unprivileged ports;
+only the root can forward privileged ports (as determined by
+authentication done earlier).
+.IP SSH_CMSG_AGENT_REQUEST_FORWARDING
+Requests forwarding of the connection to the authentication agent.
+.IP SSH_CMSG_EXEC_SHELL
+Starts a shell (command interpreter) for the user, and moves into
+interactive session mode.
+.IP SSH_CMSG_EXEC_CMD
+Executes the given command (actually "<shell> -c <command>" or
+equivalent) for the user, and moves into interactive session mode.
+.RT
+
+
+.ti 0
+Interactive Session and Exchange of Data
+
+During the interactive session, any data written by the shell or
+command running on the server machine is forwarded to stdin or
+stderr on the client machine, and any input available from stdin on
+the client machine is forwarded to the program on the server machine.
+
+All exchange is asynchronous; either side can send at any time, and
+there are no acknowledgements (TCP/IP already provides reliable
+transport, and the packet protocol protects against tampering or IP
+spoofing).
+
+When the client receives EOF from its standard input, it will send
+SSH_CMSG_EOF; however, this in no way terminates the exchange.  The
+exchange terminates and interactive mode is left when the server sends
+SSH_SMSG_EXITSTATUS to indicate that the client program has
+terminated.  Alternatively, either side may disconnect at any time by
+sending SSH_MSG_DISCONNECT or closing the connection.
+
+The server may send any of the following messages:
+.IP SSH_SMSG_STDOUT_DATA
+Data written to stdout by the program running on the server.  The data
+is passed as a string argument.  The client writes this data to
+stdout.
+.IP SSH_SMSG_STDERR_DATA
+Data written to stderr by the program running on the server.  The data
+is passed as a string argument.  The client writes this data to
+stderr.  (Note that if the program is running on a tty, it is not
+possible to separate stdout and stderr data, and all data will be sent
+as stdout data.)
+.IP SSH_SMSG_EXITSTATUS
+Indicates that the shell or command has exited.  Exit status is passed
+as an integer argument.  This message causes termination of the
+interactive session.
+.IP SSH_SMSG_AGENT_OPEN
+Indicates that someone on the server side is requesting a connection
+to the authentication agent.  The server-side channel number is passed
+as an argument.  The client must respond with either
+SSH_CHANNEL_OPEN_CONFIRMATION or SSH_CHANNEL_OPEN_FAILURE.
+.IP SSH_SMSG_X11_OPEN
+Indicates that a connection has been made to the X11 socket on the
+server side and should be forwarded to the real X server.  An integer
+argument indicates the channel number allocated for this connection on
+the server side.  The client should send back either
+SSH_MSG_CHANNEL_OPEN_CONFIRMATION or SSH_MSG_CHANNEL_OPEN_FAILURE with
+the same server side channel number.
+.IP SSH_MSG_PORT_OPEN
+Indicates that a connection has been made to a port on the server side
+for which forwarding has been requested.  Arguments are server side
+channel number, host name to connect to, and port to connect to.  The
+client should send back either
+SSH_MSG_CHANNEL_OPEN_CONFIRMATION or SSH_MSG_CHANNEL_OPEN_FAILURE with
+the same server side channel number.
+.IP SSH_MSG_CHANNEL_OPEN_CONFIRMATION
+This is sent by the server to indicate that it has opened a connection
+as requested in a previous message.  The first argument indicates the
+client side channel number, and the second argument is the channel number
+that the server has allocated for this connection.
+.IP SSH_MSG_CHANNEL_OPEN_FAILURE
+This is sent by the server to indicate that it failed to open a
+connection as requested in a previous message.  The client-side
+channel number is passed as an argument.  The client will close the
+descriptor associated with the channel and free the channel.
+.IP SSH_MSG_CHANNEL_DATA
+This packet contains data for a channel from the server.  The first
+argument is the client-side channel number, and the second argument (a
+string) is the data.
+.IP SSH_MSG_CHANNEL_CLOSE
+This is sent by the server to indicate that whoever was in the other
+end of the channel has closed it.  The argument is the client side channel
+number.  The client will let all buffered data in the channel to
+drain, and when ready, will close the socket, free the channel, and
+send the server a SSH_MSG_CHANNEL_CLOSE_CONFIRMATION message for the
+channel.
+.IP SSH_MSG_CHANNEL_CLOSE_CONFIRMATION
+This is send by the server to indicate that a channel previously
+closed by the client has now been closed on the server side as well.
+The argument indicates the client channel number.  The client frees
+the channel.
+.RT
+
+The client may send any of the following messages:
+.IP SSH_CMSG_STDIN_DATA
+This is data to be sent as input to the program running on the server.
+The data is passed as a string.
+.IP SSH_CMSG_EOF
+Indicates that the client has encountered EOF while reading standard
+input.  The server will allow any buffered input data to drain, and
+will then close the input to the program.
+.IP SSH_CMSG_WINDOW_SIZE
+Indicates that window size on the client has been changed.  The server
+updates the window size of the tty and causes SIGWINCH to be sent to
+the program.  The new window size is passed as four integer arguments:
+row, col, xpixel, ypixel.
+.IP SSH_MSG_PORT_OPEN
+Indicates that a connection has been made to a port on the client side
+for which forwarding has been requested.  Arguments are client side
+channel number, host name to connect to, and port to connect to.  The
+server should send back either SSH_MSG_CHANNEL_OPEN_CONFIRMATION or
+SSH_MSG_CHANNEL_OPEN_FAILURE with the same client side channel number.
+.IP SSH_MSG_CHANNEL_OPEN_CONFIRMATION
+This is sent by the client to indicate that it has opened a connection
+as requested in a previous message.  The first argument indicates the
+server side channel number, and the second argument is the channel
+number that the client has allocated for this connection.
+.IP SSH_MSG_CHANNEL_OPEN_FAILURE
+This is sent by the client to indicate that it failed to open a
+connection as requested in a previous message.  The server side
+channel number is passed as an argument.  The server will close the
+descriptor associated with the channel and free the channel.
+.IP SSH_MSG_CHANNEL_DATA
+This packet contains data for a channel from the client.  The first
+argument is the server side channel number, and the second argument (a
+string) is the data.
+.IP SSH_MSG_CHANNEL_CLOSE
+This is sent by the client to indicate that whoever was in the other
+end of the channel has closed it.  The argument is the server channel
+number.  The server will allow buffered data to drain, and when ready,
+will close the socket, free the channel, and send the client a
+SSH_MSG_CHANNEL_CLOSE_CONFIRMATION message for the channel.
+.IP SSH_MSG_CHANNEL_CLOSE_CONFIRMATION
+This is send by the client to indicate that a channel previously
+closed by the server has now been closed on the client side as well.
+The argument indicates the server channel number.  The server frees
+the channel.
+.RT
+
+Any unsupported messages during interactive mode cause the connection
+to be terminated with SSH_MSG_DISCONNECT and an error message.
+Compatible protocol upgrades should agree about any extensions during
+the preparation phase or earlier.
+
+
+.ti 0
+Termination of the Connection
+
+Normal termination of the connection is always initiated by the server
+by sending SSH_SMSG_EXITSTATUS after the program has exited.  The
+client responds to this message by sending SSH_CMSG_EXIT_CONFIRMATION
+and closes the socket; the server then closes the socket.  There are
+two purposes for the confirmation: some systems may lose previously
+sent data when the socket is closed, and closing the client side first
+causes any TCP/IP TIME_WAIT [RFC0793] waits to occur on the client side, not
+consuming server resources.
+
+If the program terminates due to a signal, the server will send
+SSH_MSG_DISCONNECT with an appropriate message.  If the connection is
+closed, all file descriptors to the program will be closed and the
+server will exit.  If the program runs on a tty, the kernel sends it
+the SIGHUP signal when the pty master side is closed.
+
+.ti 0
+Protocol Flags
+
+Both the server and the client pass 32 bits of protocol flags to the
+other side.  The flags are intended for compatible protocol extension;
+the server first announces which added capabilities it supports, and
+the client then sends the capabilities that it supports.
+
+The following flags are currently defined (the values are bit masks):
+.IP "1 SSH_PROTOFLAG_SCREEN_NUMBER"
+This flag can only be sent by the client.  It indicates that the X11
+forwarding requests it sends will include the screen number.
+.IP "2 SSH_PROTOFLAG_HOST_IN_FWD_OPEN"
+If both sides specify this flag, SSH_SMSG_X11_OPEN and
+SSH_MSG_PORT_OPEN messages will contain an additional field containing
+a description of the host at the other end of the connection.
+.RT
+
+.ti 0
+Detailed Description of Packet Types and Formats
+
+The supported packet types and the corresponding message numbers are
+given in the following table.  Messages with _MSG_ in their name may
+be sent by either side.  Messages with _CMSG_ are only sent by the
+client, and messages with _SMSG_ only by the server.
+
+A packet may contain additional data after the arguments specified
+below.  Any such data should be ignored by the receiver.  However, it
+is recommended that no such data be stored without good reason.  (This
+helps build compatible extensions.)
+.IP "0 SSH_MSG_NONE"
+This code is reserved.  This message type is never sent.
+.IP "1 SSH_MSG_DISCONNECT"
+.TS
+;
+l l.
+string	Cause of disconnection
+.TE
+This message may be sent by either party at any time.  It causes the
+immediate disconnection of the connection.  The message is intended to
+be displayed to a human, and describes the reason for disconnection.
+.IP "2 SSH_SMSG_PUBLIC_KEY"
+.TS
+;
+l l.
+8 bytes	anti_spoofing_cookie
+32-bit int	server_key_bits
+mp-int	server_key_public_exponent
+mp-int	server_key_public_modulus
+32-bit int	host_key_bits
+mp-int	host_key_public_exponent
+mp-int	host_key_public_modulus
+32-bit int	protocol_flags
+32-bit int	supported_ciphers_mask
+32-bit int	supported_authentications_mask
+.TE
+Sent as the first message by the server.  This message gives the
+server's host key, server key, protocol flags (intended for compatible
+protocol extension), supported_ciphers_mask (which is the
+bitwise or of (1 << cipher_number), where << is the left shift
+operator, for all supported ciphers), and
+supported_authentications_mask (which is the bitwise or of (1 <<
+authentication_type) for all supported authentication types).  The
+anti_spoofing_cookie is 64 random bytes, and must be sent back
+verbatim by the client in its reply.  It is used to make IP-spoofing
+more difficult (encryption and host keys are the real defense against
+spoofing).
+.IP "3 SSH_CMSG_SESSION_KEY"
+.TS
+;
+l l.
+1 byte	cipher_type (must be one of the supported values)
+8 bytes	anti_spoofing_cookie (must match data sent by the server)
+mp-int	double-encrypted session key
+32-bit int	protocol_flags
+.TE
+Sent by the client as the first message in the session.  Selects the
+cipher to use, and sends the encrypted session key to the server.  The
+anti_spoofing_cookie must be the same bytes that were sent by the
+server.  Protocol_flags is intended for negotiating compatible
+protocol extensions.
+.IP "4 SSH_CMSG_USER"
+.TS
+;
+l l.
+string	user login name on server
+.TE
+Sent by the client to begin authentication.  Specifies the user name
+on the server to log in as.  The server responds with SSH_SMSG_SUCCESS
+if no authentication is needed for this user, or SSH_SMSG_FAILURE if
+authentication is needed (or the user does not exist).  [Note to the
+implementator: the user name is of arbitrary size.  The implementation
+must be careful not to overflow internal buffers.]
+.IP "5 SSH_CMSG_AUTH_RHOSTS"
+.TS
+;
+l l.
+string	client-side user name
+.TE
+Requests authentication using /etc/hosts.equiv and .rhosts (or
+equivalent mechanisms).  This authentication method is normally
+disabled in the server because it is not secure (but this is the
+method used by rsh and rlogin).  The server responds with
+SSH_SMSG_SUCCESS if authentication was successful, and
+SSH_SMSG_FAILURE if access was not granted.  The server should check
+that the client side port number is less than 1024 (a privileged
+port), and immediately reject authentication if it is not.  Supporting
+this authentication method is optional.  This method should normally
+not be enabled in the server because it is not safe.  (However, not
+enabling this only helps if rlogind and rshd are disabled.)
+.IP "6 SSH_CMSG_AUTH_RSA"
+.TS
+;
+l l.
+mp-int	identity_public_modulus
+.TE
+Requests authentication using pure RSA authentication.  The server
+checks if the given key is permitted to log in, and if so, responds
+with SSH_SMSG_AUTH_RSA_CHALLENGE.  Otherwise, it responds with
+SSH_SMSG_FAILURE.  The client often tries several different keys in
+sequence until one supported by the server is found.  Authentication
+is accepted if the client gives the correct response to the challenge.
+The server is free to add other criteria for authentication, such as a
+requirement that the connection must come from a certain host.  Such
+additions are not visible at the protocol level.  Supporting this
+authentication method is optional but recommended.
+.IP "7 SSH_SMSG_AUTH_RSA_CHALLENGE"
+.TS
+;
+l l.
+mp-int	encrypted challenge
+.TE
+Presents an RSA authentication challenge to the client.  The challenge
+is a 256-bit random value encrypted as described elsewhere in this
+document.  The client must decrypt the challenge using the RSA private
+key, compute MD5 of the challenge plus session id, and send back the
+resulting 16 bytes using SSH_CMSG_AUTH_RSA_RESPONSE.
+.IP "8 SSH_CMSG_AUTH_RSA_RESPONSE"
+.TS
+;
+l l.
+16 bytes	MD5 of decrypted challenge
+.TE
+This message is sent by the client in response to an RSA challenge.
+The MD5 checksum is returned instead of the decrypted challenge to
+deter known-plaintext attacks against the RSA key.  The server
+responds to this message with either SSH_SMSG_SUCCESS or
+SSH_SMSG_FAILURE.
+.IP "9 SSH_CMSG_AUTH_PASSWORD"
+.TS
+;
+l l.
+string	plain text password
+.TE
+Requests password authentication using the given password.  Note that
+even though the password is plain text inside the packet, the whole
+packet is normally encrypted by the packet layer.  It would not be
+possible for the client to perform password encryption/hashing,
+because it cannot know which kind of encryption/hashing, if any, the
+server uses.  The server responds to this message with
+SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE.
+.IP "10 SSH_CMSG_REQUEST_PTY"
+.TS
+;
+l l.
+string	TERM environment variable value (e.g. vt100)
+32-bit int	terminal height, rows (e.g., 24)
+32-bit int	terminal width, columns (e.g., 80)
+32-bit int	terminal width, pixels (0 if no graphics) (e.g., 480)
+32-bit int	terminal height, pixels (0 if no graphics) (e.g., 640)
+n bytes	tty modes encoded in binary
+.TE
+Requests a pseudo-terminal to be allocated for this command.  This
+message can be used regardless of whether the session will later
+execute the shell or a command.  If a pty has been requested with this
+message, the shell or command will run on a pty.  Otherwise it will
+communicate with the server using pipes, sockets or some other similar
+mechanism.
+
+The terminal type gives the type of the user's terminal.  In the UNIX
+environment it is passed to the shell or command in the TERM
+environment variable.
+
+The width and height values give the initial size of the user's
+terminal or window.  All values can be zero if not supported by the
+operating system.  The server will pass these values to the kernel if
+supported.
+
+Terminal modes are encoded into a byte stream in a portable format.
+The exact format is described later in this document.
+
+The server responds to the request with either SSH_SMSG_SUCCESS or
+SSH_SMSG_FAILURE.  If the server does not have the concept of pseudo
+terminals, it should return success if it is possible to execute a
+shell or a command so that it looks to the client as if it was running
+on a pseudo terminal.
+.IP "11 SSH_CMSG_WINDOW_SIZE"
+.TS
+;
+l l.
+32-bit int	terminal height, rows
+32-bit int	terminal width, columns
+32-bit int	terminal width, pixels
+32-bit int	terminal height, pixels
+.TE
+This message can only be sent by the client during the interactive
+session.  This indicates that the size of the user's window has
+changed, and provides the new size.  The server will update the
+kernel's notion of the window size, and a SIGWINCH signal or
+equivalent will be sent to the shell or command (if supported by the
+operating system).
+.IP "12 SSH_CMSG_EXEC_SHELL"
+
+(no arguments)
+
+Starts a shell (command interpreter), and enters interactive session
+mode.
+.IP "13 SSH_CMSG_EXEC_CMD"
+.TS
+;
+l l.
+string	command to execute
+.TE
+Starts executing the given command, and enters interactive session
+mode.  On UNIX, the command is run as "<shell> -c <command>", where
+<shell> is the user's login shell.
+.IP "14 SSH_SMSG_SUCCESS"
+
+(no arguments)
+
+This message is sent by the server in response to the session key, a
+successful authentication request, and a successfully completed
+preparatory operation.
+.IP "15 SSH_SMSG_FAILURE"
+
+(no arguments)
+
+This message is sent by the server in response to a failed
+authentication operation to indicate that the user has not yet been
+successfully authenticated, and in response to a failed preparatory
+operation.  This is also sent in response to an authentication or
+preparatory operation request that is not recognized or supported.
+.IP "16 SSH_CMSG_STDIN_DATA"
+.TS
+;
+l l.
+string	data
+.TE
+Delivers data from the client to be supplied as input to the shell or
+program running on the server side.  This message can only be used in
+the interactive session mode.  No acknowledgement is sent for this
+message.
+.IP "17 SSH_SMSG_STDOUT_DATA"
+.TS
+;
+l l.
+string	data
+.TE
+Delivers data from the server that was read from the standard output of
+the shell or program running on the server side.  This message can
+only be used in the interactive session mode.  No acknowledgement is
+sent for this message.
+.IP "18 SSH_SMSG_STDERR_DATA"
+.TS
+;
+l l.
+string	data
+.TE
+Delivers data from the server that was read from the standard error of
+the shell or program running on the server side.  This message can
+only be used in the interactive session mode.  No acknowledgement is
+sent for this message.
+.IP "19 SSH_CMSG_EOF"
+
+(no arguments)
+
+This message is sent by the client to indicate that EOF has been
+reached on the input.  Upon receiving this message, and after all
+buffered input data has been sent to the shell or program, the server
+will close the input file descriptor to the program.  This message can
+only be used in the interactive session mode.  No acknowledgement is
+sent for this message.
+.IP "20 SSH_SMSG_EXITSTATUS"
+.TS
+;
+l l.
+32-bit int	exit status of the command
+.TE
+Returns the exit status of the shell or program after it has exited.
+The client should respond with SSH_CMSG_EXIT_CONFIRMATION when it has
+received this message.  This will be the last message sent by the
+server.  If the program being executed dies with a signal instead of
+exiting normally, the server should terminate the session with
+SSH_MSG_DISCONNECT (which can be used to pass a human-readable string
+indicating that the program died due to a signal) instead of using
+this message.
+.IP "21 SSH_MSG_CHANNEL_OPEN_CONFIRMATION"
+.TS
+;
+l l.
+32-bit int	remote_channel
+32-bit int	local_channel
+.TE
+This is sent in response to any channel open request if the channel
+has been successfully opened.  Remote_channel is the channel number
+received in the initial open request; local_channel is the channel
+number the side sending this message has allocated for the channel.
+Data can be transmitted on the channel after this message.
+.IP "22 SSH_MSG_CHANNEL_OPEN_FAILURE"
+.TS
+;
+l l.
+32-bit int	remote_channel
+.TE
+This message indicates that an earlier channel open request by the
+other side has failed or has been denied.  Remote_channel is the
+channel number given in the original request.
+.IP "23 SSH_MSG_CHANNEL_DATA"
+.TS
+;
+l l.
+32-bit int	remote_channel
+string	data
+.TE
+Data is transmitted in a channel in these messages.  A channel is
+bidirectional, and both sides can send these messages.  There is no
+acknowledgement for these messages.  It is possible that either side
+receives these messages after it has sent SSH_MSG_CHANNEL_CLOSE for
+the channel.  These messages cannot be received after the party has
+sent or received SSH_MSG_CHANNEL_CLOSE_CONFIRMATION.
+.IP "24 SSH_MSG_CHANNEL_CLOSE"
+.TS
+;
+l l.
+32-bit int	remote_channel
+.TE
+When a channel is closed at one end of the connection, that side sends
+this message.  Upon receiving this message, the channel should be
+closed.  When this message is received, if the channel is already
+closed (the receiving side has sent this message for the same channel
+earlier), the channel is freed and no further action is taken;
+otherwise the channel is freed and SSH_MSG_CHANNEL_CLOSE_CONFIRMATION
+is sent in response.  (It is possible that the channel is closed
+simultaneously at both ends.)
+.IP "25 SSH_MSG_CHANNEL_CLOSE_CONFIRMATION"
+.TS
+;
+l l.
+32-bit int	remote_channel
+.TE
+This message is sent in response to SSH_MSG_CHANNEL_CLOSE unless the
+channel was already closed.  When this message is sent or received,
+the channel is freed.
+.IP "26 (OBSOLETED; was unix-domain X11 forwarding)
+.IP "27 SSH_SMSG_X11_OPEN"
+.TS
+;
+l l.
+32-bit int	local_channel
+string	originator_string (see below)
+.TE
+This message can be sent by the server during the interactive session
+mode to indicate that a client has connected the fake X server.
+Local_channel is the channel number that the server has allocated for
+the connection.  The client should try to open a connection to the
+real X server, and respond with SSH_MSG_CHANNEL_OPEN_CONFIRMATION or
+SSH_MSG_CHANNEL_OPEN_FAILURE.
+
+The field originator_string is present if both sides
+specified SSH_PROTOFLAG_HOST_IN_FWD_OPEN in the protocol flags.  It
+contains a description of the host originating the connection.
+.IP "28 SSH_CMSG_PORT_FORWARD_REQUEST"
+.TS
+;
+l l.
+32-bit int	server_port
+string	host_to_connect
+32-bit int	port_to_connect
+.TE
+Sent by the client in the preparatory phase, this message requests
+that server_port on the server machine be forwarded over the secure
+channel to the client machine, and from there to the specified host
+and port.  The server should start listening on the port, and send
+SSH_MSG_PORT_OPEN whenever a connection is made to it.  Supporting
+this message is optional, and the server is free to reject any forward
+request.  For example, it is highly recommended that unless the user
+has been authenticated as root, forwarding any privileged port numbers
+(below 1024) is denied.
+.IP "29 SSH_MSG_PORT_OPEN"
+.TS
+;
+l l.
+32-bit int	local_channel
+string	host_name
+32-bit int	port
+string	originator_string (see below)
+.TE
+Sent by either party in interactive session mode, this message
+indicates that a connection has been opened to a forwarded TCP/IP
+port.  Local_channel is the channel number that the sending party has
+allocated for the connection.  Host_name is the host the connection
+should be be forwarded to, and the port is the port on that host to
+connect.  The receiving party should open the connection, and respond
+with SSH_MSG_CHANNEL_OPEN_CONFIRMATION or
+SSH_MSG_CHANNEL_OPEN_FAILURE.  It is recommended that the receiving
+side check the host_name and port for validity to avoid compromising
+local security by compromised remote side software.  Particularly, it
+is recommended that the client permit connections only to those ports
+for which it has requested forwarding with SSH_CMSG_PORT_FORWARD_REQUEST.
+
+The field originator_string is present if both sides
+specified SSH_PROTOFLAG_HOST_IN_FWD_OPEN in the protocol flags.  It
+contains a description of the host originating the connection.
+.IP "30 SSH_CMSG_AGENT_REQUEST_FORWARDING"
+
+(no arguments)
+
+Requests that the connection to the authentication agent be forwarded
+over the secure channel.  The method used by clients to contact the
+authentication agent within each machine is implementation and machine
+dependent.  If the server accepts this request, it should arrange that
+any clients run from this session will actually contact the server
+program when they try to contact the authentication agent.  The server
+should then send a SSH_SMSG_AGENT_OPEN to open a channel to the agent,
+and the client should forward the connection to the real
+authentication agent.  Supporting this message is optional.
+.IP "31 SSH_SMSG_AGENT_OPEN"
+.TS
+;
+l l.
+32-bit int	local_channel
+.TE
+Sent by the server in interactive session mode, this message requests
+opening a channel to the authentication agent.  The client should open
+a channel, and respond with either SSH_MSG_CHANNEL_OPEN_CONFIRMATION
+or SSH_MSG_CHANNEL_OPEN_FAILURE.
+.IP "32 SSH_MSG_IGNORE"
+.TS
+;
+l l.
+string	data
+.TE
+Either party may send this message at any time.  This message, and the
+argument string, is silently ignored.  This message might be used in
+some implementations to make traffic analysis more difficult.  This
+message is not currently sent by the implementation, but all
+implementations are required to recognize and ignore it.
+.IP "33 SSH_CMSG_EXIT_CONFIRMATION"
+
+(no arguments)
+
+Sent by the client in response to SSH_SMSG_EXITSTATUS.  This is the
+last message sent by the client.
+.IP "34 SSH_CMSG_X11_REQUEST_FORWARDING"
+.TS
+;
+l l.
+string	x11_authentication_protocol
+string	x11_authentication_data
+32-bit int	screen number (if SSH_PROTOFLAG_SCREEN_NUMBER)
+.TE
+Sent by the client during the preparatory phase, this message requests
+that the server create a fake X11 display and set the DISPLAY
+environment variable accordingly.  An internet-domain display is
+preferable.  The given authentication protocol and the associated data
+should be recorded by the server so that it is used as authentication
+on connections (e.g., in .Xauthority).  The authentication protocol
+must be one of the supported X11 authentication protocols, e.g.,
+"MIT-MAGIC-COOKIE-1".  Authentication data must be a lowercase hex
+string of even length.  Its interpretation is protocol dependent.
+The data is in a format that can be used with e.g. the xauth program.
+Supporting this message is optional.
+
+The client is permitted (and recommended) to generate fake
+authentication information and send fake information to the server.
+This way, a corrupt server will not have access to the user's terminal
+after the connection has terminated.  The correct authorization codes
+will also not be left hanging around in files on the server (many
+users keep the same X session for months, thus protecting the
+authorization data becomes important).
+
+X11 authentication spoofing works by initially sending fake (random)
+authentication data to the server, and interpreting the first packet
+sent by the X11 client after the connection has been opened.  The
+first packet contains the client's authentication.  If the packet
+contains the correct fake data, it is replaced by the client by the
+correct authentication data, and then sent to the X server.
+.IP "35 SSH_CMSG_AUTH_RHOSTS_RSA"
+.TS
+;
+l l.
+string	clint-side user name
+32-bit int	client_host_key_bits
+mp-int	client_host_key_public_exponent
+mp-int	client_host_key_public_modulus
+.TE
+Requests authentication using /etc/hosts.equiv and .rhosts (or
+equivalent) together with RSA host authentication.  The server should
+check that the client side port number is less than 1024 (a privileged
+port), and immediately reject authentication if it is not.  The server
+responds with SSH_SMSG_FAILURE or SSH_SMSG_AUTH_RSA_CHALLENGE.  The
+client must respond to the challenge with the proper
+SSH_CMSG_AUTH_RSA_RESPONSE.  The server then responds with success if
+access was granted, or failure if the client gave a wrong response.
+Supporting this authentication method is optional but recommended in
+most environments.
+.IP "36 SSH_MSG_DEBUG"
+.TS
+;
+l l.
+string	debugging message sent to the other side
+.TE
+This message may be sent by either party at any time.  It is used to
+send debugging messages that may be informative to the user in
+solving various problems.  For example, if authentication fails
+because of some configuration error (e.g., incorrect permissions for
+some file), it can be very helpful for the user to make the cause of
+failure available.  On the other hand, one should not make too much
+information available for security reasons.  It is recommended that
+the client provides an option to display the debugging information
+sent by the sender (the user probably does not want to see it by default).
+The server can log debugging data sent by the client (if any).  Either
+party is free to ignore any received debugging data.  Every
+implementation must be able to receive this message, but no
+implementation is required to send these.
+.IP "37 SSH_CMSG_REQUEST_COMPRESSION"
+.TS
+;
+l l.
+32-bit int	gzip compression level (1-9)
+.TE
+This message can be sent by the client in the preparatory operations
+phase.  The server responds with SSH_SMSG_FAILURE if it does not
+support compression or does not want to compress; it responds with
+SSH_SMSG_SUCCESS if it accepted the compression request.  In the
+latter case the response to this packet will still be uncompressed,
+but all further packets in either direction will be compressed by gzip.
+.RT
+
+
+.ti 0
+Encoding of Terminal Modes
+
+Terminal modes (as passed in SSH_CMSG_REQUEST_PTY) are encoded into a
+byte stream.  It is intended that the coding be portable across
+different environments.
+
+The tty mode description is a stream of bytes.  The stream consists of
+opcode-argument pairs.  It is terminated by opcode TTY_OP_END (0).
+Opcodes 1-127 have one-byte arguments.  Opcodes 128-159 have 32-bit
+integer arguments (stored msb first).  Opcodes 160-255 are not yet
+defined, and cause parsing to stop (they should only be used after any
+other data).
+
+The client puts in the stream any modes it knows about, and the server
+ignores any modes it does not know about.  This allows some degree of
+machine-independence, at least between systems that use a POSIX-like
+[POSIX] tty interface.  The protocol can support other systems as
+well, but the client may need to fill reasonable values for a number
+of parameters so the server pty gets set to a reasonable mode (the
+server leaves all unspecified mode bits in their default values, and
+only some combinations make sense).
+
+The following opcodes have been defined.  The naming of opcodes mostly
+follows the POSIX terminal mode flags.
+.IP "0 TTY_OP_END"
+Indicates end of options.
+.IP "1 VINTR"
+Interrupt character; 255 if none.  Similarly for the other characters.
+Not all of these characters are supported on all systems.
+.IP "2 VQUIT"
+The quit character (sends SIGQUIT signal on UNIX systems).
+.IP "3 VERASE"
+Erase the character to left of the cursor.
+.IP "4 VKILL"
+Kill the current input line.
+.IP "5 VEOF "
+End-of-file character (sends EOF from the terminal).
+.IP "6 VEOL "
+End-of-line character in addition to carriage return and/or linefeed.
+.IP "7 VEOL2"
+Additional end-of-line character.
+.IP "8 VSTART"
+Continues paused output (normally ^Q).
+.IP "9 VSTOP"
+Pauses output (^S).
+.IP "10 VSUSP"
+Suspends the current program.
+.IP "11 VDSUSP"
+Another suspend character.
+.IP "12 VREPRINT"
+Reprints the current input line.
+.IP "13 VWERASE"
+Erases a word left of cursor.
+.IP "14 VLNEXT"
+More special input characters; these are probably not supported on
+most systems.
+.IP "15 VFLUSH"
+.IP "16 VSWTCH"
+.IP "17 VSTATUS"
+.IP "18 VDISCARD"
+
+.IP "30 IGNPAR"
+The ignore parity flag.  The next byte should be 0 if this flag is not
+set, and 1 if it is set.
+.IP "31 PARMRK"
+More flags.  The exact definitions can be found in the POSIX standard.
+.IP "32 INPCK"
+.IP "33 ISTRIP"
+.IP "34 INLCR"
+.IP "35 IGNCR"
+.IP "36 ICRNL"
+.IP "37 IUCLC"
+.IP "38 IXON"
+.IP "39 IXANY"
+.IP "40 IXOFF"
+.IP "41 IMAXBEL"
+
+.IP "50 ISIG"
+.IP "51 ICANON"
+.IP "52 XCASE"
+.IP "53 ECHO"
+.IP "54 ECHOE"
+.IP "55 ECHOK"
+.IP "56 ECHONL"
+.IP "57 NOFLSH"
+.IP "58 TOSTOP"
+.IP "59 IEXTEN"
+.IP "60 ECHOCTL"
+.IP "61 ECHOKE"
+.IP "62 PENDIN"
+
+.IP "70 OPOST"
+.IP "71 OLCUC"
+.IP "72 ONLCR"
+.IP "73 OCRNL"
+.IP "74 ONOCR"
+.IP "75 ONLRET"
+
+.IP "90 CS7"
+.IP "91 CS8"
+.IP "92 PARENB"
+.IP "93 PARODD"
+
+.IP "192 TTY_OP_ISPEED"
+Specifies the input baud rate in bits per second.
+.IP "193 TTY_OP_OSPEED"
+Specifies the output baud rate in bits per second.
+.RT
+
+
+.ti 0
+The Authentication Agent Protocol
+
+The authentication agent is a program that can be used to hold RSA
+authentication keys for the user (in future, it might hold data for
+other authentication types as well).  An authorized program can send
+requests to the agent to generate a proper response to an RSA
+challenge.  How the connection is made to the agent (or its
+representative) inside a host and how access control is done inside a
+host is implementation-dependent; however, how it is forwarded and how
+one interacts with it is specified in this protocol.  The connection
+to the agent is normally automatically forwarded over the secure
+channel.
+
+A program that wishes to use the agent first opens a connection to its
+local representative (typically, the agent itself or an SSH server).
+It then writes a request to the connection, and waits for response.
+It is recommended that at least five minutes of timeout are provided
+waiting for the agent to respond to an authentication challenge (this
+gives sufficient time for the user to cut-and-paste the challenge to a
+separate machine, perform the computation there, and cut-and-paste the
+result back if so desired).
+
+Messages sent to and by the agent are in the following format:
+.TS
+;
+l l.
+4 bytes	Length, msb first.  Does not include length itself.
+1 byte	Packet type.  The value 255 is reserved for future extensions.
+data	Any data, depending on packet type.  Encoding as in the ssh packet
+protocol.
+.TE
+
+The following message types are currently defined:
+.IP "1 SSH_AGENTC_REQUEST_RSA_IDENTITIES"
+
+(no arguments)
+
+Requests the agent to send a list of all RSA keys for which it can
+answer a challenge.
+.IP "2 SSH_AGENT_RSA_IDENTITIES_ANSWER"
+.TS
+;
+l l.
+32-bit int	howmany
+howmany times:
+32-bit int	bits
+mp-int	public exponent
+mp-int	public modulus
+string	comment
+.TE
+The agent sends this message in response to the to
+SSH_AGENTC_REQUEST_RSA_IDENTITIES.  The answer lists all RSA keys for
+which the agent can answer a challenge.  The comment field is intended
+to help identify each key; it may be printed by an application to
+indicate which key is being used.  If the agent is not holding any
+keys, howmany will be zero.
+.IP "3 SSH_AGENTC_RSA_CHALLENGE
+.TS
+;
+l l.
+32-bit int	bits
+mp-int	public exponent
+mp-int	public modulus
+mp-int	challenge
+16 bytes	session_id
+32-bit int	response_type
+.TE
+Requests RSA decryption of random challenge to authenticate the other
+side.  The challenge will be decrypted with the RSA private key
+corresponding to the given public key.
+
+The decrypted challenge must contain a zero in the highest (partial)
+byte, 2 in the next byte, followed by non-zero random bytes, a zero
+byte, and then the real challenge value in the lowermost bytes.  The
+real challenge must be 32 8-bit bytes (256 bits).
+
+Response_type indicates the format of the response to be returned.
+Currently the only supported value is 1, which means to compute MD5 of
+the real challenge plus session id, and return the resulting 16 bytes
+in a SSH_AGENT_RSA_RESPONSE message.
+.IP "4 SSH_AGENT_RSA_RESPONSE"
+.TS
+;
+l l.
+16 bytes	MD5 of decrypted challenge
+.TE
+Answers an RSA authentication challenge.  The response is 16 bytes:
+the MD5 checksum of the 32-byte challenge.
+.IP "5 SSH_AGENT_FAILURE"
+
+(no arguments)
+
+This message is sent whenever the agent fails to answer a request
+properly.  For example, if the agent cannot answer a challenge (e.g.,
+no longer has the proper key), it can respond with this.  The agent
+also responds with this message if it receives a message it does not
+recognize.
+.IP "6 SSH_AGENT_SUCCESS"
+
+(no arguments)
+
+This message is sent by the agent as a response to certain requests
+that do not otherwise cause a message be sent.  Currently, this is
+only sent in response to SSH_AGENTC_ADD_RSA_IDENTITY and
+SSH_AGENTC_REMOVE_RSA_IDENTITY.
+.IP "7 SSH_AGENTC_ADD_RSA_IDENTITY"
+.TS
+;
+l l.
+32-bit int	bits
+mp-int	public modulus
+mp-int	public exponent
+mp-int	private exponent
+mp-int	multiplicative inverse of p mod q
+mp-int	p
+mp-int	q
+string	comment
+.TE
+Registers an RSA key with the agent.  After this request, the agent can
+use this RSA key to answer requests.  The agent responds with
+SSH_AGENT_SUCCESS or SSH_AGENT_FAILURE.
+.IP "8 SSH_AGENT_REMOVE_RSA_IDENTITY"
+.TS
+;
+l l.
+32-bit int	bits
+mp-int	public exponent
+mp-int	public modulus
+.TE
+Removes an RSA key from the agent.  The agent will no longer accept
+challenges for this key and will not list it as a supported identity.
+The agent responds with SSH_AGENT_SUCCESS or SSH_AGENT_FAILURE.
+.RT
+
+If the agent receives a message that it does not understand, it
+responds with SSH_AGENT_FAILURE.  This permits compatible future
+extensions.
+
+It is possible that several clients have a connection open to the
+authentication agent simultaneously.  Each client will use a separate
+connection (thus, any SSH connection can have multiple agent
+connections active simultaneously).
+
+
+.ti 0
+References
+
+.IP "[DES] "
+FIPS PUB 46-1: Data Encryption Standard.  National Bureau of
+Standards, January 1988.  FIPS PUB 81: DES Modes of Operation.
+National Bureau of Standards, December 1980.  Bruce Schneier: Applied
+Cryptography.  John Wiley & Sons, 1994.  J. Seberry and J. Pieprzyk:
+Cryptography: An Introduction to Computer Security.  Prentice-Hall,
+1989.
+.IP "[GZIP] "
+The GNU GZIP program; available for anonymous ftp at prep.ai.mit.edu.
+Please let me know if you know a paper describing the algorithm.
+.IP "[IDEA] "
+Xuejia Lai: On the Design and Security of Block Ciphers, ETH Series in
+Information Processing, vol. 1, Hartung-Gorre Verlag, Konstanz,
+Switzerland, 1992.  Bruce Schneier: Applied Cryptography, John Wiley &
+Sons, 1994.  See also the following patents: PCT/CH91/00117, EP 0 482
+154 B1, US Pat. 5,214,703.
+.IP [PKCS#1]
+PKCS #1: RSA Encryption Standard.  Version 1.5, RSA Laboratories,
+November 1993.  Available for anonymous ftp at ftp.rsa.com.
+.IP [POSIX]
+Portable Operating System Interface (POSIX) - Part 1: Application
+Program Interface (API) [C language], ISO/IEC 9945-1, IEEE Std 1003.1,
+1990.
+.IP [RFC0791]
+J. Postel: Internet Protocol, RFC 791, USC/ISI, September 1981.
+.IP [RFC0793]
+J. Postel: Transmission Control Protocol, RFC 793, USC/ISI, September
+1981.
+.IP [RFC1034]
+P. Mockapetris: Domain Names - Concepts and Facilities, RFC 1034,
+USC/ISI, November 1987.
+.IP [RFC1282]
+B. Kantor: BSD Rlogin, RFC 1258, UCSD, December 1991.
+.IP "[RSA] "
+Bruce Schneier: Applied Cryptography.  John Wiley & Sons, 1994.  See
+also R. Rivest, A. Shamir, and L. M. Adleman: Cryptographic
+Communications System and Method.  US Patent 4,405,829, 1983.
+.IP "[X11] "
+R. Scheifler: X Window System Protocol, X Consortium Standard, Version
+11, Release 6.  Massachusetts Institute of Technology, Laboratory of
+Computer Science, 1994.
+.RT
+
+
+.ti 0
+Security Considerations
+
+This protocol deals with the very issue of user authentication and
+security.
+
+First of all, as an implementation issue, the server program will have
+to run as root (or equivalent) on the server machine.  This is because
+the server program will need be able to change to an arbitrary user
+id.  The server must also be able to create a privileged TCP/IP port.
+
+The client program will need to run as root if any variant of .rhosts
+authentication is to be used.  This is because the client program will
+need to create a privileged port.  The client host key is also usually
+stored in a file which is readable by root only.  The client needs the
+host key in .rhosts authentication only.  Root privileges can be
+dropped as soon as the privileged port has been created and the host
+key has been read.
+
+The SSH protocol offers major security advantages over existing telnet
+and rlogin protocols.
+.IP o
+IP spoofing is restricted to closing a connection (by encryption, host
+keys, and the special random cookie).  If encryption is not used, IP
+spoofing is possible for those who can hear packets going out from the
+server.
+.IP o
+DNS spoofing is made ineffective (by host keys).
+.IP o
+Routing spoofing is made ineffective (by host keys).
+.IP o
+All data is encrypted with strong algorithms to make eavesdropping as
+difficult as possible.  This includes encrypting any authentication
+information such as passwords.  The information for decrypting session
+keys is destroyed every hour.
+.IP o
+Strong authentication methods: .rhosts combined with RSA host
+authentication, and pure RSA authentication.
+.IP o
+X11 connections and arbitrary TCP/IP ports can be forwarded securely.
+.IP o
+Man-in-the-middle attacks are deterred by using the server host key to
+encrypt the session key.
+.IP o
+Trojan horses to catch a password by routing manipulation are deterred
+by checking that the host key of the server machine matches that
+stored on the client host.
+.RT
+
+The security of SSH against man-in-the-middle attacks and the security
+of the new form of .rhosts authentication, as well as server host
+validation, depends on the integrity of the host key and the files
+containing known host keys.
+
+The host key is normally stored in a root-readable file.  If the host
+key is compromised, it permits attackers to use IP, DNS and routing
+spoofing as with current rlogin and rsh.  It should never be any worse
+than the current situation.
+
+The files containing known host keys are not sensitive.  However, if an
+attacker gets to modify the known host key files, it has the same
+consequences as a compromised host key, because the attacker can then
+change the recorded host key.
+
+The security improvements obtained by this protocol for X11 are of
+particular significance.  Previously, there has been no way to protect
+data communicated between an X server and a client running on a remote
+machine.  By creating a fake display on the server, and forwarding all
+X11 requests over the secure channel, SSH can be used to run any X11
+applications securely without any cooperation with the vendors of the
+X server or the application.
+
+Finally, the security of this program relies on the strength of the
+underlying cryptographic algorithms.  The RSA algorithm is used for
+authentication key exchange.  It is widely believed to be secure.  Of
+the algorithms used to encrypt the session, DES has a rather small key
+these days, probably permitting governments and organized criminals to
+break it in very short time with specialized hardware.  3DES is
+probably safe (but slower).  IDEA is widely believed to be secure.
+People have varying degrees of confidence in the other algorithms.
+This program is not secure if used with no encryption at all.
+
+
+.ti 0
+Additional Information
+
+Additional information (especially on the implementation and mailing
+lists) is available via WWW at http://www.cs.hut.fi/ssh.
+
+Comments should be sent to Tatu Ylonen <ylo@cs.hut.fi> or the SSH
+Mailing List <ssh@clinet.fi>.
+
+.ti 0
+Author's Address
+
+.TS
+;
+l.
+Tatu Ylonen
+Helsinki University of Technology
+Otakaari 1
+FIN-02150 Espoo, Finland
+
+Phone: +358-0-451-3374
+Fax: +358-0-451-3293
+EMail: ylo@cs.hut.fi
+.TE
diff --git a/auth-krb4.c b/auth-krb4.c
new file mode 100644
index 0000000..720f3a4
--- /dev/null
+++ b/auth-krb4.c
@@ -0,0 +1,209 @@
+/*
+
+   auth-kerberos.c
+
+   Dug Song <dugsong@UMICH.EDU>
+
+   Kerberos v4 authentication and ticket-passing routines.
+
+   $Id: auth-krb4.c,v 1.1 1999/10/27 03:42:43 damien Exp $
+*/
+
+#include "includes.h"
+#include "packet.h"
+#include "xmalloc.h"
+#include "ssh.h"
+
+#ifdef KRB4
+int ssh_tf_init(uid_t uid)
+{
+  extern char *ticket;
+  char *tkt_root = TKT_ROOT;
+  struct stat st;
+  int fd;
+  
+  /* Set unique ticket string manually since we're still root. */
+  ticket = xmalloc(MAXPATHLEN);
+#ifdef AFS
+  if (lstat("/ticket", &st) != -1)
+    tkt_root = "/ticket/";
+#endif /* AFS */
+  snprintf(ticket, MAXPATHLEN, "%s%d_%d", tkt_root, uid, getpid());
+  (void) krb_set_tkt_string(ticket);
+
+  /* Make sure we own this ticket file, and we created it. */
+  if (lstat(ticket, &st) == -1 && errno == ENOENT) {
+    /* good, no ticket file exists. create it. */
+    if ((fd = open(ticket, O_RDWR|O_CREAT|O_EXCL, 0600)) != -1) {
+      close(fd);
+      return 1;
+    }
+  }
+  else {
+    /* file exists. make sure server_user owns it (e.g. just passed ticket),
+       and that it isn't a symlink, and that it is mode 600. */
+    if (st.st_mode == (S_IFREG|S_IRUSR|S_IWUSR) && st.st_uid == uid)
+      return 1;
+  }
+  /* Failure. */
+  log("WARNING: bad ticket file %s", ticket);
+  return 0;
+}
+
+int auth_krb4(const char *server_user, KTEXT auth, char **client)
+{
+  AUTH_DAT adat   = { 0 };
+  KTEXT_ST reply;
+  char instance[INST_SZ];
+  int r, s;
+  u_int cksum;
+  Key_schedule schedule;
+  struct sockaddr_in local, foreign;
+  
+  s = packet_get_connection_in();
+  
+  r = sizeof(local);
+  memset(&local, 0, sizeof(local));
+  if (getsockname(s, (struct sockaddr *) &local, &r) < 0)
+    debug("getsockname failed: %.100s", strerror(errno));
+  r = sizeof(foreign);
+  memset(&foreign, 0, sizeof(foreign));
+  if (getpeername(s, (struct sockaddr *)&foreign, &r) < 0)
+    debug("getpeername failed: %.100s", strerror(errno));
+  
+  instance[0] = '*'; instance[1] = 0;
+  
+  /* Get the encrypted request, challenge, and session key. */
+  if ((r = krb_rd_req(auth, KRB4_SERVICE_NAME, instance, 0, &adat, ""))) {
+    packet_send_debug("Kerberos V4 krb_rd_req: %.100s", krb_err_txt[r]);
+    return 0;
+  }
+  des_key_sched((des_cblock *)adat.session, schedule);
+  
+  *client = xmalloc(MAX_K_NAME_SZ);
+  (void) snprintf(*client, MAX_K_NAME_SZ, "%s%s%s@%s", adat.pname,
+                  *adat.pinst ? "." : "", adat.pinst, adat.prealm);
+
+  /* Check ~/.klogin authorization now. */
+  if (kuserok(&adat, (char *)server_user) != KSUCCESS) {
+    packet_send_debug("Kerberos V4 .klogin authorization failed!");
+    log("Kerberos V4 .klogin authorization failed for %s to account %s",
+	*client, server_user);
+    return 0;
+  }
+  /* Increment the checksum, and return it encrypted with the session key. */
+  cksum = adat.checksum + 1;
+  cksum = htonl(cksum);
+  
+  /* If we can't successfully encrypt the checksum, we send back an empty
+     message, admitting our failure. */
+  if ((r = krb_mk_priv((u_char *)&cksum, reply.dat, sizeof(cksum)+1,
+		       schedule, &adat.session, &local, &foreign)) < 0) {
+    packet_send_debug("Kerberos V4 mk_priv: (%d) %s", r, krb_err_txt[r]);
+    reply.dat[0] = 0;
+    reply.length = 0;
+  }
+  else
+    reply.length = r;
+  
+  /* Clear session key. */
+  memset(&adat.session, 0, sizeof(&adat.session));
+  
+  packet_start(SSH_SMSG_AUTH_KERBEROS_RESPONSE);
+  packet_put_string((char *) reply.dat, reply.length);
+  packet_send();
+  packet_write_wait();
+  return 1;
+}
+#endif /* KRB4 */
+
+#ifdef AFS
+int auth_kerberos_tgt(struct passwd *pw, const char *string)
+{
+  CREDENTIALS creds;
+  extern char *ticket;
+  int r;
+  
+  if (!radix_to_creds(string, &creds)) {
+    log("Protocol error decoding Kerberos V4 tgt");
+    packet_send_debug("Protocol error decoding Kerberos V4 tgt");
+    goto auth_kerberos_tgt_failure;
+  }
+  if (strncmp(creds.service, "", 1) == 0) /* backward compatibility */
+    strlcpy(creds.service, "krbtgt", sizeof creds.service);
+  
+  if (strcmp(creds.service, "krbtgt")) {
+    log("Kerberos V4 tgt (%s%s%s@%s) rejected for uid %d",
+	creds.pname, creds.pinst[0] ? "." : "", creds.pinst, creds.realm,
+	pw->pw_uid);
+    packet_send_debug("Kerberos V4 tgt (%s%s%s@%s) rejected for uid %d",
+		      creds.pname, creds.pinst[0] ? "." : "", creds.pinst,
+		      creds.realm, pw->pw_uid);
+    goto auth_kerberos_tgt_failure;
+  }
+  if (!ssh_tf_init(pw->pw_uid) ||
+      (r = in_tkt(creds.pname, creds.pinst)) ||
+      (r = save_credentials(creds.service, creds.instance, creds.realm,
+			    creds.session, creds.lifetime, creds.kvno,
+			    &creds.ticket_st, creds.issue_date))) {
+    xfree(ticket);
+    ticket = NULL;
+    packet_send_debug("Kerberos V4 tgt refused: couldn't save credentials");
+    goto auth_kerberos_tgt_failure;
+  }
+  /* Successful authentication, passed all checks. */
+  chown(ticket, pw->pw_uid, pw->pw_gid);
+  packet_send_debug("Kerberos V4 tgt accepted (%s.%s@%s, %s%s%s@%s)",
+		    creds.service, creds.instance, creds.realm,
+		    creds.pname, creds.pinst[0] ? "." : "",
+		    creds.pinst, creds.realm);
+  
+  packet_start(SSH_SMSG_SUCCESS);
+  packet_send();
+  packet_write_wait();
+  return 1;
+
+auth_kerberos_tgt_failure:
+  memset(&creds, 0, sizeof(creds));
+  packet_start(SSH_SMSG_FAILURE);
+  packet_send();
+  packet_write_wait();
+  return 0;
+}
+
+int auth_afs_token(char *server_user, uid_t uid, const char *string)
+{
+  CREDENTIALS creds;
+
+  if (!radix_to_creds(string, &creds)) {
+    log("Protocol error decoding AFS token");
+    packet_send_debug("Protocol error decoding AFS token");
+    packet_start(SSH_SMSG_FAILURE);
+    packet_send();
+    packet_write_wait();
+    return 0;
+  }
+  if (strncmp(creds.service, "", 1) == 0) /* backward compatibility */
+    strlcpy(creds.service, "afs", sizeof creds.service);
+  
+  if (strncmp(creds.pname, "AFS ID ", 7) == 0)
+    uid = atoi(creds.pname + 7);
+  
+  if (kafs_settoken(creds.realm, uid, &creds)) {
+    log("AFS token (%s@%s) rejected for uid %d", creds.pname,
+	creds.realm, uid);
+    packet_send_debug("AFS token (%s@%s) rejected for uid %d", creds.pname,
+		      creds.realm, uid);
+    packet_start(SSH_SMSG_FAILURE);
+    packet_send();
+    packet_write_wait();
+    return 0;
+  }
+  packet_send_debug("AFS token accepted (%s@%s, %s@%s)", creds.service,
+		    creds.realm, creds.pname, creds.realm);
+  packet_start(SSH_SMSG_SUCCESS);
+  packet_send();
+  packet_write_wait();
+  return 1;
+}
+#endif /* AFS */
diff --git a/auth-passwd.c b/auth-passwd.c
new file mode 100644
index 0000000..7d68467
--- /dev/null
+++ b/auth-passwd.c
@@ -0,0 +1,209 @@
+/*
+
+auth-passwd.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Sat Mar 18 05:11:38 1995 ylo
+
+Password authentication.  This file contains the functions to check whether
+the password is valid for the user.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: auth-passwd.c,v 1.1 1999/10/27 03:42:43 damien Exp $");
+
+#include "packet.h"
+#include "ssh.h"
+#include "servconf.h"
+#include "xmalloc.h"
+
+#ifdef KRB4
+extern char *ticket;
+#endif /* KRB4 */
+
+#ifdef HAVE_PAM
+#include <security/pam_appl.h>
+extern pam_handle_t *pamh;
+extern int retval;
+extern char* pampasswd;
+extern int origretval;
+#endif /* HAVE_PAM */
+
+/* Tries to authenticate the user using password.  Returns true if
+   authentication succeeds. */
+
+int auth_password(struct passwd *pw, const char *password)
+{
+  extern ServerOptions options;
+  char *encrypted_password;
+
+  if (pw->pw_uid == 0 && options.permit_root_login == 2)
+  {
+      /*packet_send_debug("Server does not permit root login with password.");*/
+      return 0;
+  }
+
+  if (*password == '\0' && options.permit_empty_passwd == 0)
+  {
+      /*packet_send_debug("Server does not permit empty password login.");*/
+      return 0;
+  }
+
+  /* deny if no user. */
+  if (pw == NULL)
+    return 0;
+
+#ifdef HAVE_PAM
+  retval = origretval;
+
+  pampasswd = xstrdup(password);
+
+  if (retval == PAM_SUCCESS)
+    retval = pam_authenticate ((pam_handle_t *)pamh, 0);
+
+  if (retval == PAM_SUCCESS)
+    retval = pam_acct_mgmt ((pam_handle_t *)pamh, 0);
+
+  xfree(pampasswd);
+
+  if (retval == PAM_SUCCESS) 
+    retval = pam_open_session ((pam_handle_t *)pamh, 0);
+  
+  return (retval == PAM_SUCCESS);
+
+#else /* HAVE_PAM */
+
+#ifdef SKEY
+  if (options.skey_authentication == 1) {
+    if (strncasecmp(password, "s/key", 5) == 0) {
+      char *skeyinfo = skey_keyinfo(pw->pw_name);
+      if(skeyinfo == NULL){
+	debug("generating fake skeyinfo for %.100s.", pw->pw_name);
+        skeyinfo = skey_fake_keyinfo(pw->pw_name);
+      }
+      if(skeyinfo != NULL)
+        packet_send_debug(skeyinfo);
+      /* Try again. */
+      return 0;
+    }
+    else if (skey_haskey(pw->pw_name) == 0 && 
+	     skey_passcheck(pw->pw_name, (char *)password) != -1) {
+      /* Authentication succeeded. */
+      return 1;
+    }
+    /* Fall back to ordinary passwd authentication. */
+  }
+#endif
+
+#if defined(KRB4)
+  /* Support for Kerberos v4 authentication - Dug Song <dugsong@UMICH.EDU> */
+  if (options.kerberos_authentication)
+    {
+      AUTH_DAT adata;
+      KTEXT_ST tkt;
+      struct hostent *hp;
+      unsigned long faddr;
+      char localhost[MAXHOSTNAMELEN];	/* local host name */
+      char phost[INST_SZ];		/* host instance */
+      char realm[REALM_SZ];		/* local Kerberos realm */
+      int r;
+      
+      /* Try Kerberos password authentication only for non-root
+	 users and only if Kerberos is installed. */
+      if (pw->pw_uid != 0 && krb_get_lrealm(realm, 1) == KSUCCESS) {
+
+	/* Set up our ticket file. */
+	if (!ssh_tf_init(pw->pw_uid)) {
+	  log("Couldn't initialize Kerberos ticket file for %s!",
+	      pw->pw_name);
+	  goto kerberos_auth_failure;
+	}
+	/* Try to get TGT using our password. */
+	r = krb_get_pw_in_tkt((char *)pw->pw_name, "", realm, "krbtgt", realm,
+			      DEFAULT_TKT_LIFE, (char *)password);
+	if (r != INTK_OK) {
+	  packet_send_debug("Kerberos V4 password authentication for %s "
+			    "failed: %s", pw->pw_name, krb_err_txt[r]);
+	  goto kerberos_auth_failure;
+	}
+	/* Successful authentication. */
+	chown(ticket, pw->pw_uid, pw->pw_gid);
+	
+	(void) gethostname(localhost, sizeof(localhost));
+	(void) strlcpy(phost, (char *)krb_get_phost(localhost), INST_SZ);
+	
+	/* Now that we have a TGT, try to get a local "rcmd" ticket to
+	   ensure that we are not talking to a bogus Kerberos server. */
+	r = krb_mk_req(&tkt, KRB4_SERVICE_NAME, phost, realm, 33);
+
+	if (r == KSUCCESS) {
+	  if (!(hp = gethostbyname(localhost))) {
+	    log("Couldn't get local host address!");
+	    goto kerberos_auth_failure;
+	  }
+	  memmove((void *)&faddr, (void *)hp->h_addr, sizeof(faddr));
+
+	  /* Verify our "rcmd" ticket. */
+	  r = krb_rd_req(&tkt, KRB4_SERVICE_NAME, phost, faddr, &adata, "");
+	  if (r == RD_AP_UNDEC) {
+	    /* Probably didn't have a srvtab on localhost. Allow login. */
+	    log("Kerberos V4 TGT for %s unverifiable, no srvtab installed? "
+		"krb_rd_req: %s", pw->pw_name, krb_err_txt[r]);
+	  }
+	  else if (r != KSUCCESS) {
+	    log("Kerberos V4 %s ticket unverifiable: %s",
+		KRB4_SERVICE_NAME, krb_err_txt[r]);
+	    goto kerberos_auth_failure;
+	  }
+	}
+	else if (r == KDC_PR_UNKNOWN) {
+	  /* Allow login if no rcmd service exists, but log the error. */
+	  log("Kerberos V4 TGT for %s unverifiable: %s; %s.%s "
+	      "not registered, or srvtab is wrong?", pw->pw_name,
+	      krb_err_txt[r], KRB4_SERVICE_NAME, phost);
+	}
+	else {
+	  /* TGT is bad, forget it. Possibly spoofed! */
+	  packet_send_debug("WARNING: Kerberos V4 TGT possibly spoofed for"
+			    "%s: %s", pw->pw_name, krb_err_txt[r]);
+	  goto kerberos_auth_failure;
+	}
+	
+	/* Authentication succeeded. */
+	return 1;
+	
+      kerberos_auth_failure:
+	(void) dest_tkt();
+	xfree(ticket);
+	ticket = NULL;
+	if (!options.kerberos_or_local_passwd ) return 0;
+      }
+      else {
+	/* Logging in as root or no local Kerberos realm. */
+	packet_send_debug("Unable to authenticate to Kerberos.");
+      }
+      /* Fall back to ordinary passwd authentication. */
+    }
+#endif /* KRB4 */
+  
+  /* Check for users with no password. */
+  if (strcmp(password, "") == 0 && strcmp(pw->pw_passwd, "") == 0)
+    {
+      packet_send_debug("Login permitted without a password because the account has no password.");
+      return 1; /* The user has no password and an empty password was tried. */
+    }
+
+  /* Encrypt the candidate password using the proper salt. */
+  encrypted_password = crypt(password, 
+			     (pw->pw_passwd[0] && pw->pw_passwd[1]) ?
+			     pw->pw_passwd : "xx");
+
+  /* Authentication is accepted if the encrypted passwords are identical. */
+  return (strcmp(encrypted_password, pw->pw_passwd) == 0);
+#endif /* HAVE_PAM */
+}
diff --git a/auth-rh-rsa.c b/auth-rh-rsa.c
new file mode 100644
index 0000000..c433578
--- /dev/null
+++ b/auth-rh-rsa.c
@@ -0,0 +1,83 @@
+/*
+
+auth-rh-rsa.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Sun May  7 03:08:06 1995 ylo
+
+Rhosts or /etc/hosts.equiv authentication combined with RSA host
+authentication.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: auth-rh-rsa.c,v 1.1 1999/10/27 03:42:43 damien Exp $");
+
+#include "packet.h"
+#include "ssh.h"
+#include "xmalloc.h"
+#include "uidswap.h"
+
+/* Tries to authenticate the user using the .rhosts file and the host using
+   its host key.  Returns true if authentication succeeds. 
+   .rhosts and .shosts will be ignored if ignore_rhosts is non-zero. */
+
+int auth_rhosts_rsa(struct passwd *pw, const char *client_user,
+		    unsigned int client_host_key_bits,
+		    BIGNUM *client_host_key_e, BIGNUM *client_host_key_n,
+		    int ignore_rhosts, int strict_modes)
+{
+  const char *canonical_hostname;
+  HostStatus host_status;
+  BIGNUM *ke, *kn;
+
+  debug("Trying rhosts with RSA host authentication for %.100s", client_user);
+
+  /* Check if we would accept it using rhosts authentication. */
+  if (!auth_rhosts(pw, client_user, ignore_rhosts, strict_modes))
+    return 0;
+
+  canonical_hostname = get_canonical_hostname();
+
+  debug("Rhosts RSA authentication: canonical host %.900s",
+	canonical_hostname);
+  
+  /* Check if we know the host and its host key. */
+  /* Check system-wide host file. */
+  ke = BN_new();
+  kn = BN_new();
+  host_status = check_host_in_hostfile(SSH_SYSTEM_HOSTFILE, canonical_hostname,
+				       client_host_key_bits, client_host_key_e,
+				       client_host_key_n, ke, kn);
+  BN_free(ke);
+  BN_free(kn);
+  if (host_status != HOST_OK) {
+    /* The host key was not found. */
+    debug("Rhosts with RSA host authentication denied: unknown or invalid host key");
+    packet_send_debug("Your host key cannot be verified: unknown or invalid host key.");
+    return 0;
+  }
+
+  /* A matching host key was found and is known. */
+  
+  /* Perform the challenge-response dialog with the client for the host key. */
+  if (!auth_rsa_challenge_dialog(client_host_key_bits,
+				 client_host_key_e, client_host_key_n))
+    {
+      log("Client on %.800s failed to respond correctly to host authentication.",
+	  canonical_hostname);
+      return 0;
+    }
+
+  /* We have authenticated the user using .rhosts or /etc/hosts.equiv, and
+     the host using RSA.  We accept the authentication. */
+  
+  log("Rhosts with RSA host authentication accepted for %.100s, %.100s on %.700s.",
+      pw->pw_name, client_user, canonical_hostname);
+  packet_send_debug("Rhosts with RSA host authentication accepted.");
+  return 1;
+}
diff --git a/auth-rhosts.c b/auth-rhosts.c
new file mode 100644
index 0000000..ebf2fcb
--- /dev/null
+++ b/auth-rhosts.c
@@ -0,0 +1,298 @@
+/*
+
+auth-rhosts.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Fri Mar 17 05:12:18 1995 ylo
+
+Rhosts authentication.  This file contains code to check whether to admit
+the login based on rhosts authentication.  This file also processes
+/etc/hosts.equiv.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: auth-rhosts.c,v 1.1 1999/10/27 03:42:43 damien Exp $");
+
+#include "packet.h"
+#include "ssh.h"
+#include "xmalloc.h"
+#include "uidswap.h"
+
+/* This function processes an rhosts-style file (.rhosts, .shosts, or
+   /etc/hosts.equiv).  This returns true if authentication can be granted
+   based on the file, and returns zero otherwise. */
+
+int check_rhosts_file(const char *filename, const char *hostname,
+		      const char *ipaddr, const char *client_user,
+		      const char *server_user)
+{
+  FILE *f;
+  char buf[1024]; /* Must not be larger than host, user, dummy below. */
+  
+  /* Open the .rhosts file. */
+  f = fopen(filename, "r");
+  if (!f)
+    return 0; /* Cannot read the .rhosts - deny access. */
+
+  /* Go through the file, checking every entry. */
+  while (fgets(buf, sizeof(buf), f))
+    {
+      /* All three must be at least as big as buf to avoid overflows. */
+      char hostbuf[1024], userbuf[1024], dummy[1024], *host, *user, *cp;
+      int negated;
+      
+      for (cp = buf; *cp == ' ' || *cp == '\t'; cp++)
+	;
+      if (*cp == '#' || *cp == '\n' || !*cp)
+	continue;
+
+      /* NO_PLUS is supported at least on OSF/1.  We skip it (we don't ever
+	 support the plus syntax). */
+      if (strncmp(cp, "NO_PLUS", 7) == 0)
+	continue;
+
+      /* This should be safe because each buffer is as big as the whole
+	 string, and thus cannot be overwritten. */
+      switch (sscanf(buf, "%s %s %s", hostbuf, userbuf, dummy))
+	{
+	case 0:
+	  packet_send_debug("Found empty line in %.100s.", filename);
+	  continue; /* Empty line? */
+	case 1:
+	  /* Host name only. */
+	  strlcpy(userbuf, server_user, sizeof(userbuf));
+	  break;
+	case 2:
+	  /* Got both host and user name. */
+	  break;
+	case 3:
+	  packet_send_debug("Found garbage in %.100s.", filename);
+	  continue; /* Extra garbage */
+	default:
+	  continue; /* Weird... */
+	}
+
+      host = hostbuf;
+      user = userbuf;
+      negated = 0;
+
+      /* Process negated host names, or positive netgroups. */
+      if (host[0] == '-')
+	{
+	  negated = 1;
+	  host++;
+	}
+      else
+	if (host[0] == '+')
+	  host++;
+
+      if (user[0] == '-')
+	{
+	  negated = 1;
+	  user++;
+	}
+      else
+	if (user[0] == '+')
+	  user++;
+
+      /* Check for empty host/user names (particularly '+'). */
+      if (!host[0] || !user[0])
+	{ 
+	  /* We come here if either was '+' or '-'. */
+	  packet_send_debug("Ignoring wild host/user names in %.100s.",
+			    filename);
+	  continue;
+	}
+	  
+      /* Verify that host name matches. */
+      if (host[0] == '@')
+	{
+	  if (!innetgr(host + 1, hostname, NULL, NULL) &&
+	      !innetgr(host + 1, ipaddr, NULL, NULL))
+	    continue;
+	}
+      else
+	if (strcasecmp(host, hostname) && strcmp(host, ipaddr) != 0)
+	  continue; /* Different hostname. */
+
+      /* Verify that user name matches. */
+      if (user[0] == '@')
+	{
+	  if (!innetgr(user + 1, NULL, client_user, NULL))
+	    continue;
+	}
+      else
+	if (strcmp(user, client_user) != 0)
+	  continue; /* Different username. */
+
+      /* Found the user and host. */
+      fclose(f);
+
+      /* If the entry was negated, deny access. */
+      if (negated)
+	{
+	  packet_send_debug("Matched negative entry in %.100s.",
+			    filename);
+	  return 0;
+	}
+
+      /* Accept authentication. */
+      return 1;
+    }
+     
+  /* Authentication using this file denied. */
+  fclose(f);
+  return 0;
+}
+
+/* Tries to authenticate the user using the .shosts or .rhosts file.  
+   Returns true if authentication succeeds.  If ignore_rhosts is
+   true, only /etc/hosts.equiv will be considered (.rhosts and .shosts
+   are ignored). */
+
+int auth_rhosts(struct passwd *pw, const char *client_user,
+		int ignore_rhosts, int strict_modes)
+{
+  char buf[1024];
+  const char *hostname, *ipaddr;
+  int port;
+  struct stat st;
+  static const char *rhosts_files[] = { ".shosts", ".rhosts", NULL };
+  unsigned int rhosts_file_index;
+
+  /* Quick check: if the user has no .shosts or .rhosts files, return failure
+     immediately without doing costly lookups from name servers. */
+  /* Switch to the user's uid. */
+  temporarily_use_uid(pw->pw_uid);
+  for (rhosts_file_index = 0; rhosts_files[rhosts_file_index];
+       rhosts_file_index++)
+    {
+      /* Check users .rhosts or .shosts. */
+      snprintf(buf, sizeof buf, "%.500s/%.100s", 
+	      pw->pw_dir, rhosts_files[rhosts_file_index]);
+      if (stat(buf, &st) >= 0)
+	break;
+    }
+  /* Switch back to privileged uid. */
+  restore_uid();
+
+  if (!rhosts_files[rhosts_file_index] && stat("/etc/hosts.equiv", &st) < 0 &&
+      stat(SSH_HOSTS_EQUIV, &st) < 0)
+    return 0; /* The user has no .shosts or .rhosts file and there are no
+		 system-wide files. */
+
+  /* Get the name, address, and port of the remote host.  */
+  hostname = get_canonical_hostname();
+  ipaddr = get_remote_ipaddr();
+  port = get_remote_port();
+
+  /* Check that the connection comes from a privileged port.
+     Rhosts authentication only makes sense for priviledged programs.
+     Of course, if the intruder has root access on his local machine,
+     he can connect from any port.  So do not use .rhosts
+     authentication from machines that you do not trust. */
+  if (port >= IPPORT_RESERVED ||
+      port < IPPORT_RESERVED / 2)
+    {
+      log("Connection from %.100s from nonpriviledged port %d",
+	  hostname, port);
+      packet_send_debug("Your ssh client is not running as root.");
+      return 0;
+    }
+
+  /* If not logging in as superuser, try /etc/hosts.equiv and shosts.equiv. */
+  if (pw->pw_uid != 0)
+    {
+      if (check_rhosts_file("/etc/hosts.equiv", hostname, ipaddr, client_user,
+			    pw->pw_name))
+	{
+	  packet_send_debug("Accepted for %.100s [%.100s] by /etc/hosts.equiv.",
+			    hostname, ipaddr);
+	  return 1;
+	}
+      if (check_rhosts_file(SSH_HOSTS_EQUIV, hostname, ipaddr, client_user,
+			    pw->pw_name))
+	{
+	  packet_send_debug("Accepted for %.100s [%.100s] by %.100s.", 
+			    hostname, ipaddr, SSH_HOSTS_EQUIV);
+	  return 1;
+	}
+    }
+
+  /* Check that the home directory is owned by root or the user, and is not 
+     group or world writable. */
+  if (stat(pw->pw_dir, &st) < 0)
+    {
+      log("Rhosts authentication refused for %.100: no home directory %.200s",
+	  pw->pw_name, pw->pw_dir);
+      packet_send_debug("Rhosts authentication refused for %.100: no home directory %.200s",
+			pw->pw_name, pw->pw_dir);
+      return 0;
+    }
+  if (strict_modes && 
+      ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
+       (st.st_mode & 022) != 0))
+    {
+      log("Rhosts authentication refused for %.100s: bad ownership or modes for home directory.",
+	  pw->pw_name);
+      packet_send_debug("Rhosts authentication refused for %.100s: bad ownership or modes for home directory.",
+			pw->pw_name);
+      return 0;
+    }
+  
+  /* Check all .rhosts files (currently .shosts and .rhosts). */
+  /* Temporarily use the user's uid. */
+  temporarily_use_uid(pw->pw_uid);
+  for (rhosts_file_index = 0; rhosts_files[rhosts_file_index];
+       rhosts_file_index++)
+    {
+      /* Check users .rhosts or .shosts. */
+      snprintf(buf, sizeof buf, "%.500s/%.100s", 
+	      pw->pw_dir, rhosts_files[rhosts_file_index]);
+      if (stat(buf, &st) < 0)
+	continue; /* No such file. */
+
+      /* Make sure that the file is either owned by the user or by root,
+	 and make sure it is not writable by anyone but the owner.  This is
+	 to help avoid novices accidentally allowing access to their account
+	 by anyone. */
+      if (strict_modes &&
+	  ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
+	   (st.st_mode & 022) != 0))
+	{
+	  log("Rhosts authentication refused for %.100s: bad modes for %.200s",
+	      pw->pw_name, buf);
+	  packet_send_debug("Bad file modes for %.200s", buf);
+	  continue;
+	}
+
+      /* Check if we have been configured to ignore .rhosts and .shosts 
+	 files. */
+      if (ignore_rhosts)
+	{
+	  packet_send_debug("Server has been configured to ignore %.100s.",
+			    rhosts_files[rhosts_file_index]);
+	  continue;
+	}
+
+      /* Check if authentication is permitted by the file. */
+      if (check_rhosts_file(buf, hostname, ipaddr, client_user, pw->pw_name))
+	{
+	  packet_send_debug("Accepted by %.100s.",
+			    rhosts_files[rhosts_file_index]);
+	  /* Restore the privileged uid. */
+	  restore_uid();
+	  return 1;
+	}
+    }
+
+  /* Rhosts authentication denied. */
+  /* Restore the privileged uid. */
+  restore_uid();
+  return 0;
+}
diff --git a/auth-rsa.c b/auth-rsa.c
new file mode 100644
index 0000000..8de86d2
--- /dev/null
+++ b/auth-rsa.c
@@ -0,0 +1,478 @@
+/*
+
+auth-rsa.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Mon Mar 27 01:46:52 1995 ylo
+
+RSA-based authentication.  This code determines whether to admit a login
+based on RSA authentication.  This file also contains functions to check
+validity of the host key.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: auth-rsa.c,v 1.1 1999/10/27 03:42:43 damien Exp $");
+
+#include "rsa.h"
+#include "packet.h"
+#include "xmalloc.h"
+#include "ssh.h"
+#include "mpaux.h"
+#include "uidswap.h"
+
+#include <openssl/rsa.h>
+#include <openssl/md5.h>
+
+/* Flags that may be set in authorized_keys options. */
+extern int no_port_forwarding_flag;
+extern int no_agent_forwarding_flag;
+extern int no_x11_forwarding_flag;
+extern int no_pty_flag;
+extern char *forced_command;
+extern struct envstring *custom_environment;
+
+/* Session identifier that is used to bind key exchange and authentication
+   responses to a particular session. */
+extern unsigned char session_id[16];
+
+/* The .ssh/authorized_keys file contains public keys, one per line, in the
+   following format:
+     options bits e n comment
+   where bits, e and n are decimal numbers, 
+   and comment is any string of characters up to newline.  The maximum
+   length of a line is 8000 characters.  See the documentation for a
+   description of the options.
+*/
+
+/* Performs the RSA authentication challenge-response dialog with the client,
+   and returns true (non-zero) if the client gave the correct answer to
+   our challenge; returns zero if the client gives a wrong answer. */
+
+int
+auth_rsa_challenge_dialog(unsigned int bits, BIGNUM *e, BIGNUM *n)
+{
+  BIGNUM *challenge, *encrypted_challenge, *aux;
+  RSA *pk;
+  BN_CTX *ctx = BN_CTX_new();
+  unsigned char buf[32], mdbuf[16], response[16];
+  MD5_CTX md;
+  unsigned int i;
+  int plen, len;
+
+  encrypted_challenge = BN_new();
+  challenge = BN_new();
+  aux = BN_new();
+
+  /* Generate a random challenge. */
+  BN_rand(challenge, 256, 0, 0);
+  BN_mod(challenge, challenge, n, ctx);
+  
+  /* Create the public key data structure. */
+  pk = RSA_new();
+  pk->e = BN_new();
+  BN_copy(pk->e, e);
+  pk->n = BN_new();
+  BN_copy(pk->n, n);
+
+  /* Encrypt the challenge with the public key. */
+  rsa_public_encrypt(encrypted_challenge, challenge, pk);
+  RSA_free(pk);
+
+  /* Send the encrypted challenge to the client. */
+  packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE);
+  packet_put_bignum(encrypted_challenge);
+  packet_send();
+  packet_write_wait();
+
+  /* The response is MD5 of decrypted challenge plus session id. */
+  len = BN_num_bytes(challenge);
+  assert(len <= 32 && len);
+  memset(buf, 0, 32);
+  BN_bn2bin(challenge, buf + 32 - len);
+  MD5_Init(&md);
+  MD5_Update(&md, buf, 32);
+  MD5_Update(&md, session_id, 16);
+  MD5_Final(mdbuf, &md);
+
+  /* We will no longer need these. */
+  BN_clear_free(encrypted_challenge);
+  BN_clear_free(challenge);
+  BN_clear_free(aux);
+  BN_CTX_free(ctx);
+  
+  /* Wait for a response. */
+  packet_read_expect(&plen, SSH_CMSG_AUTH_RSA_RESPONSE);
+  packet_integrity_check(plen, 16, SSH_CMSG_AUTH_RSA_RESPONSE);
+  for (i = 0; i < 16; i++)
+    response[i] = packet_get_char();
+
+  /* Verify that the response is the original challenge. */
+  if (memcmp(response, mdbuf, 16) != 0)
+    {
+      /* Wrong answer. */
+      return 0;
+    }
+
+  /* Correct answer. */
+  return 1;
+}
+
+/* Performs the RSA authentication dialog with the client.  This returns
+   0 if the client could not be authenticated, and 1 if authentication was
+   successful.  This may exit if there is a serious protocol violation. */
+
+int
+auth_rsa(struct passwd *pw, BIGNUM *client_n, int strict_modes)
+{
+  char line[8192];
+  int authenticated;
+  unsigned int bits;
+  FILE *f;
+  unsigned long linenum = 0;
+  struct stat st;
+  BIGNUM *e, *n;
+
+  /* Temporarily use the user's uid. */
+  temporarily_use_uid(pw->pw_uid);
+
+  /* The authorized keys. */
+  snprintf(line, sizeof line, "%.500s/%.100s", pw->pw_dir,
+    SSH_USER_PERMITTED_KEYS);
+  
+  /* Fail quietly if file does not exist */
+  if (stat(line, &st) < 0)
+    {
+      /* Restore the privileged uid. */
+      restore_uid();
+      return 0;
+    }
+
+  /* Open the file containing the authorized keys. */
+  f = fopen(line, "r");
+  if (!f)
+    {
+      /* Restore the privileged uid. */
+      restore_uid();
+      packet_send_debug("Could not open %.900s for reading.", line);
+      packet_send_debug("If your home is on an NFS volume, it may need to be world-readable.");
+      return 0;
+    }
+
+  if (strict_modes) {
+    int fail=0;
+    char buf[1024];
+    /* Check open file in order to avoid open/stat races */
+    if (fstat(fileno(f), &st) < 0 ||
+        (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
+        (st.st_mode & 022) != 0) {
+      snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: "
+               "bad ownership or modes for '%s'.", pw->pw_name, line);
+      fail=1;
+    }else{
+      /* Check path to SSH_USER_PERMITTED_KEYS */
+      int i;
+      static const char *check[] = {
+            "", SSH_USER_DIR, NULL
+      };
+      for (i=0; check[i]; i++) {
+        snprintf(line, sizeof line, "%.500s/%.100s", pw->pw_dir, check[i]);
+        if (stat(line, &st) < 0 ||
+            (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
+            (st.st_mode & 022) != 0) {
+          snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: "
+                   "bad ownership or modes for '%s'.", pw->pw_name, line);
+          fail=1;
+          break;
+        }
+      }
+    }
+    if (fail) {
+      log(buf);
+      packet_send_debug(buf);
+      restore_uid();
+      return 0;
+    }
+  } 
+
+  /* Flag indicating whether authentication has succeeded. */
+  authenticated = 0;
+  
+  /* Initialize mp-int variables. */
+  e = BN_new();
+  n = BN_new();
+
+  /* Go though the accepted keys, looking for the current key.  If found,
+     perform a challenge-response dialog to verify that the user really has
+     the corresponding private key. */
+  while (fgets(line, sizeof(line), f))
+    {
+      char *cp;
+      char *options;
+
+      linenum++;
+
+      /* Skip leading whitespace. */
+      for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
+	;
+
+      /* Skip empty and comment lines. */
+      if (!*cp || *cp == '\n' || *cp == '#')
+	continue;
+
+      /* Check if there are options for this key, and if so, save their 
+	 starting address and skip the option part for now.  If there are no 
+	 options, set the starting address to NULL. */
+      if (*cp < '0' || *cp > '9')
+	{
+	  int quoted = 0;
+	  options = cp;
+	  for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++)
+	    {
+	      if (*cp == '\\' && cp[1] == '"')
+		cp++; /* Skip both */
+	      else
+		if (*cp == '"')
+		  quoted = !quoted;
+	    }
+	}
+      else
+	options = NULL;
+      
+      /* Parse the key from the line. */
+      if (!auth_rsa_read_key(&cp, &bits, e, n))
+	{
+	  debug("%.100s, line %lu: bad key syntax", 
+		SSH_USER_PERMITTED_KEYS, linenum);
+	  packet_send_debug("%.100s, line %lu: bad key syntax", 
+			    SSH_USER_PERMITTED_KEYS, linenum);
+	  continue;
+	}
+      /* cp now points to the comment part. */
+
+      /* Check if the we have found the desired key (identified by its
+	 modulus). */
+      if (BN_cmp(n, client_n) != 0)
+	continue; /* Wrong key. */
+
+      /* We have found the desired key. */
+
+      /* Perform the challenge-response dialog for this key. */
+      if (!auth_rsa_challenge_dialog(bits, e, n))
+	{
+	  /* Wrong response. */
+	  log("Wrong response to RSA authentication challenge.");
+	  packet_send_debug("Wrong response to RSA authentication challenge.");
+	  continue;
+	}
+
+      /* Correct response.  The client has been successfully authenticated.
+	 Note that we have not yet processed the options; this will be reset
+	 if the options cause the authentication to be rejected. */
+      authenticated = 1;
+
+      /* RSA part of authentication was accepted.  Now process the options. */
+      if (options)
+	{
+	  while (*options && *options != ' ' && *options != '\t')
+	    {
+	      cp = "no-port-forwarding";
+	      if (strncmp(options, cp, strlen(cp)) == 0)
+		{
+		  packet_send_debug("Port forwarding disabled.");
+		  no_port_forwarding_flag = 1;
+		  options += strlen(cp);
+		  goto next_option;
+		}
+	      cp = "no-agent-forwarding";
+	      if (strncmp(options, cp, strlen(cp)) == 0)
+		{
+		  packet_send_debug("Agent forwarding disabled.");
+		  no_agent_forwarding_flag = 1;
+		  options += strlen(cp);
+		  goto next_option;
+		}
+	      cp = "no-X11-forwarding";
+	      if (strncmp(options, cp, strlen(cp)) == 0)
+		{
+		  packet_send_debug("X11 forwarding disabled.");
+		  no_x11_forwarding_flag = 1;
+		  options += strlen(cp);
+		  goto next_option;
+		}
+	      cp = "no-pty";
+	      if (strncmp(options, cp, strlen(cp)) == 0)
+		{
+		  packet_send_debug("Pty allocation disabled.");
+		  no_pty_flag = 1;
+		  options += strlen(cp);
+		  goto next_option;
+		}
+	      cp = "command=\"";
+	      if (strncmp(options, cp, strlen(cp)) == 0)
+		{
+		  int i;
+		  options += strlen(cp);
+		  forced_command = xmalloc(strlen(options) + 1);
+		  i = 0;
+		  while (*options)
+		    {
+		      if (*options == '"')
+			break;
+		      if (*options == '\\' && options[1] == '"')
+			{
+			  options += 2;
+			  forced_command[i++] = '"';
+			  continue;
+			}
+		      forced_command[i++] = *options++;
+		    }
+		  if (!*options)
+		    {
+		      debug("%.100s, line %lu: missing end quote",
+			    SSH_USER_PERMITTED_KEYS, linenum);
+		      packet_send_debug("%.100s, line %lu: missing end quote",
+					SSH_USER_PERMITTED_KEYS, linenum);
+		      continue;
+		    }
+		  forced_command[i] = 0;
+		  packet_send_debug("Forced command: %.900s", forced_command);
+		  options++;
+		  goto next_option;
+		}
+	      cp = "environment=\"";
+	      if (strncmp(options, cp, strlen(cp)) == 0)
+		{
+		  int i;
+		  char *s;
+		  struct envstring *new_envstring;
+		  options += strlen(cp);
+		  s = xmalloc(strlen(options) + 1);
+		  i = 0;
+		  while (*options)
+		    {
+		      if (*options == '"')
+			break;
+		      if (*options == '\\' && options[1] == '"')
+			{
+			  options += 2;
+			  s[i++] = '"';
+			  continue;
+			}
+		      s[i++] = *options++;
+		    }
+		  if (!*options)
+		    {
+		      debug("%.100s, line %lu: missing end quote",
+			    SSH_USER_PERMITTED_KEYS, linenum);
+		      packet_send_debug("%.100s, line %lu: missing end quote",
+					SSH_USER_PERMITTED_KEYS, linenum);
+		      continue;
+		    }
+		  s[i] = 0;
+		  packet_send_debug("Adding to environment: %.900s", s);
+		  debug("Adding to environment: %.900s", s);
+		  options++;
+		  new_envstring = xmalloc(sizeof(struct envstring));
+		  new_envstring->s = s;
+		  new_envstring->next = custom_environment;
+		  custom_environment = new_envstring;
+		  goto next_option;
+		}
+	      cp = "from=\"";
+	      if (strncmp(options, cp, strlen(cp)) == 0)
+		{
+		  char *patterns = xmalloc(strlen(options) + 1);
+		  int i;
+		  options += strlen(cp);
+		  i = 0;
+		  while (*options)
+		    {
+		      if (*options == '"')
+			break;
+		      if (*options == '\\' && options[1] == '"')
+			{
+			  options += 2;
+			  patterns[i++] = '"';
+			  continue;
+			}
+		      patterns[i++] = *options++;
+		    }
+		  if (!*options)
+		    {
+		      debug("%.100s, line %lu: missing end quote",
+			    SSH_USER_PERMITTED_KEYS, linenum);
+		      packet_send_debug("%.100s, line %lu: missing end quote",
+					SSH_USER_PERMITTED_KEYS, linenum);
+		      continue;
+		    }
+		  patterns[i] = 0;
+		  options++;
+		  if (!match_hostname(get_canonical_hostname(), patterns,
+				     strlen(patterns)) &&
+		      !match_hostname(get_remote_ipaddr(), patterns,
+				      strlen(patterns)))
+		    {
+		      log("RSA authentication tried for %.100s with correct key but not from a permitted host (host=%.200s, ip=%.200s).",
+			  pw->pw_name, get_canonical_hostname(),
+			  get_remote_ipaddr());
+		      packet_send_debug("Your host '%.200s' is not permitted to use this key for login.",
+					get_canonical_hostname());
+		      xfree(patterns);
+		      authenticated = 0;
+		      break;
+		    }
+		  xfree(patterns);
+		  /* Host name matches. */
+		  goto next_option;
+		}
+	    bad_option:
+	      /* Unknown option. */
+	      log("Bad options in %.100s file, line %lu: %.50s",
+		  SSH_USER_PERMITTED_KEYS, linenum, options);
+	      packet_send_debug("Bad options in %.100s file, line %lu: %.50s",
+				SSH_USER_PERMITTED_KEYS, linenum, options);
+	      authenticated = 0;
+	      break;
+
+	    next_option:
+	      /* Skip the comma, and move to the next option (or break out
+		 if there are no more). */
+	      if (!*options)
+		fatal("Bugs in auth-rsa.c option processing.");
+	      if (*options == ' ' || *options == '\t')
+		break; /* End of options. */
+	      if (*options != ',')
+		goto bad_option;
+	      options++;
+	      /* Process the next option. */
+	      continue;
+	    }
+	}
+
+      /* Break out of the loop if authentication was successful; otherwise
+	 continue searching. */
+      if (authenticated)
+	break;
+    }
+
+  /* Restore the privileged uid. */
+  restore_uid();
+
+  /* Close the file. */
+  fclose(f);
+  
+  /* Clear any mp-int variables. */
+  BN_clear_free(n);
+  BN_clear_free(e);
+
+  if (authenticated)
+    packet_send_debug("RSA authentication accepted.");
+
+  /* Return authentication result. */
+  return authenticated;
+}
diff --git a/auth-skey.c b/auth-skey.c
new file mode 100644
index 0000000..9ec1704
--- /dev/null
+++ b/auth-skey.c
@@ -0,0 +1,149 @@
+#include "includes.h"
+RCSID("$Id: auth-skey.c,v 1.2 1999/10/16 20:57:52 deraadt Exp $");
+
+#include "ssh.h"
+#include <sha1.h>
+
+/* from %OpenBSD: skeylogin.c,v 1.32 1999/08/16 14:46:56 millert Exp % */
+
+
+#define ROUND(x)   (((x)[0] << 24) + (((x)[1]) << 16) + (((x)[2]) << 8) + \
+		    ((x)[3]))
+
+/*
+ * hash_collapse()
+ */
+static u_int32_t
+hash_collapse(s)
+        u_char *s;
+{
+        int len, target;
+	u_int32_t i;
+	
+	if ((strlen(s) % sizeof(u_int32_t)) == 0)
+  		target = strlen(s);    /* Multiple of 4 */
+	else
+		target = strlen(s) - (strlen(s) % sizeof(u_int32_t));
+  
+	for (i = 0, len = 0; len < target; len += 4)
+        	i ^= ROUND(s + len);
+
+	return i;
+}
+char *
+skey_fake_keyinfo(char *username)
+{
+	int i;
+	u_int ptr;
+	u_char hseed[SKEY_MAX_SEED_LEN], flg = 1, *up;
+	char pbuf[SKEY_MAX_PW_LEN+1];
+	static char skeyprompt[SKEY_MAX_CHALLENGE+1];
+	char *secret = NULL;
+	size_t secretlen = 0;
+	SHA1_CTX ctx;
+	char *p, *u;
+
+	/*
+	 * Base first 4 chars of seed on hostname.
+	 * Add some filler for short hostnames if necessary.
+	 */
+	if (gethostname(pbuf, sizeof(pbuf)) == -1)
+		*(p = pbuf) = '.';
+	else
+		for (p = pbuf; *p && isalnum(*p); p++)
+			if (isalpha(*p) && isupper(*p))
+				*p = tolower(*p);
+	if (*p && pbuf - p < 4)
+		(void)strncpy(p, "asjd", 4 - (pbuf - p));
+	pbuf[4] = '\0';
+
+	/* Hash the username if possible */
+	if ((up = SHA1Data(username, strlen(username), NULL)) != NULL) {
+		struct stat sb;
+		time_t t;
+		int fd;
+
+		/* Collapse the hash */
+		ptr = hash_collapse(up);
+		memset(up, 0, strlen(up));
+
+		/* See if the random file's there, else use ctime */
+		if ((fd = open(_SKEY_RAND_FILE_PATH_, O_RDONLY)) != -1
+		    && fstat(fd, &sb) == 0 &&
+		    sb.st_size > (off_t)SKEY_MAX_SEED_LEN &&
+		    lseek(fd, ptr % (sb.st_size - SKEY_MAX_SEED_LEN),
+		    SEEK_SET) != -1 && read(fd, hseed,
+		    SKEY_MAX_SEED_LEN) == SKEY_MAX_SEED_LEN) {
+			close(fd);
+			secret = hseed;
+			secretlen = SKEY_MAX_SEED_LEN;
+			flg = 0;
+		} else if (!stat(_PATH_MEM, &sb) || !stat("/", &sb)) {
+			t = sb.st_ctime;
+			secret = ctime(&t);
+			secretlen = strlen(secret);
+			flg = 0;
+		}
+	}
+
+	/* Put that in your pipe and smoke it */
+	if (flg == 0) {
+		/* Hash secret value with username */
+		SHA1Init(&ctx);
+		SHA1Update(&ctx, secret, secretlen);
+		SHA1Update(&ctx, username, strlen(username));
+		SHA1End(&ctx, up);
+		
+		/* Zero out */
+		memset(secret, 0, secretlen);
+
+		/* Now hash the hash */
+		SHA1Init(&ctx);
+		SHA1Update(&ctx, up, strlen(up));
+		SHA1End(&ctx, up);
+		
+		ptr = hash_collapse(up + 4);
+		
+		for (i = 4; i < 9; i++) {
+			pbuf[i] = (ptr % 10) + '0';
+			ptr /= 10;
+		}
+		pbuf[i] = '\0';
+
+		/* Sequence number */
+		ptr = ((up[2] + up[3]) % 99) + 1;
+
+		memset(up, 0, 20); /* SHA1 specific */
+		free(up);
+
+		(void)snprintf(skeyprompt, sizeof skeyprompt,
+			      "otp-%.*s %d %.*s",
+			      SKEY_MAX_HASHNAME_LEN,
+			      skey_get_algorithm(),
+			      ptr, SKEY_MAX_SEED_LEN,
+			      pbuf);
+	} else {
+		/* Base last 8 chars of seed on username */
+		u = username;
+		i = 8;
+		p = &pbuf[4];
+		do {
+			if (*u == 0) {
+				/* Pad remainder with zeros */
+				while (--i >= 0)
+					*p++ = '0';
+				break;
+			}
+
+			*p++ = (*u++ % 10) + '0';
+		} while (--i != 0);
+		pbuf[12] = '\0';
+
+		(void)snprintf(skeyprompt, sizeof skeyprompt,
+			      "otp-%.*s %d %.*s",
+			      SKEY_MAX_HASHNAME_LEN,
+			      skey_get_algorithm(),
+			      99, SKEY_MAX_SEED_LEN, pbuf);
+	}
+	return skeyprompt;
+}
diff --git a/authfd.c b/authfd.c
new file mode 100644
index 0000000..07893ca
--- /dev/null
+++ b/authfd.c
@@ -0,0 +1,565 @@
+/*
+
+authfd.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Wed Mar 29 01:30:28 1995 ylo
+
+Functions for connecting the local authentication agent.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: authfd.c,v 1.1 1999/10/27 03:42:43 damien Exp $");
+
+#include "ssh.h"
+#include "rsa.h"
+#include "authfd.h"
+#include "buffer.h"
+#include "bufaux.h"
+#include "xmalloc.h"
+#include "getput.h"
+
+#include <openssl/rsa.h>
+
+/* Returns the number of the authentication fd, or -1 if there is none. */
+
+int
+ssh_get_authentication_socket()
+{
+  const char *authsocket;
+  int sock;
+  struct sockaddr_un sunaddr;
+
+  authsocket = getenv(SSH_AUTHSOCKET_ENV_NAME);
+  if (!authsocket)
+    return -1;
+
+  sunaddr.sun_family = AF_UNIX;
+  strlcpy(sunaddr.sun_path, authsocket, sizeof(sunaddr.sun_path));
+  
+  sock = socket(AF_UNIX, SOCK_STREAM, 0);
+  if (sock < 0)
+    return -1;
+  
+  if (connect(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0)
+    {
+      close(sock);
+      return -1;
+    }
+
+  return sock;
+}
+
+/* Closes the agent socket if it should be closed (depends on how it was
+   obtained).  The argument must have been returned by 
+   ssh_get_authentication_socket(). */
+
+void ssh_close_authentication_socket(int sock)
+{
+  if (getenv(SSH_AUTHSOCKET_ENV_NAME))
+    close(sock);
+}
+
+/* Opens and connects a private socket for communication with the
+   authentication agent.  Returns the file descriptor (which must be
+   shut down and closed by the caller when no longer needed).
+   Returns NULL if an error occurred and the connection could not be
+   opened. */
+
+AuthenticationConnection *ssh_get_authentication_connection()
+{
+  AuthenticationConnection *auth;
+  int sock;
+  
+  sock = ssh_get_authentication_socket();
+
+  /* Fail if we couldn't obtain a connection.  This happens if we exited
+     due to a timeout. */
+  if (sock < 0)
+    return NULL;
+
+  /* Applocate the connection structure and initialize it. */
+  auth = xmalloc(sizeof(*auth));
+  auth->fd = sock;
+  buffer_init(&auth->packet);
+  buffer_init(&auth->identities);
+  auth->howmany = 0;
+
+  return auth;
+}
+
+/* Closes the connection to the authentication agent and frees any associated
+   memory. */
+
+void ssh_close_authentication_connection(AuthenticationConnection *ac)
+{
+  buffer_free(&ac->packet);
+  buffer_free(&ac->identities);
+  close(ac->fd);
+  /* Free the connection data structure. */
+  xfree(ac);
+}
+
+/* Returns the first authentication identity held by the agent.
+   Returns true if an identity is available, 0 otherwise.
+   The caller must initialize the integers before the call, and free the
+   comment after a successful call (before calling ssh_get_next_identity). */
+
+int
+ssh_get_first_identity(AuthenticationConnection *auth,
+		       int *bitsp, BIGNUM *e, BIGNUM *n, char **comment)
+{
+  unsigned char msg[8192];
+  int len, l;
+
+  /* Send a message to the agent requesting for a list of the identities
+     it can represent. */
+  msg[0] = 0;
+  msg[1] = 0;
+  msg[2] = 0;
+  msg[3] = 1;
+  msg[4] = SSH_AGENTC_REQUEST_RSA_IDENTITIES;
+  if (write(auth->fd, msg, 5) != 5)
+    {
+      error("write auth->fd: %.100s", strerror(errno));
+      return 0;
+    }
+
+  /* Read the length of the response.  XXX implement timeouts here. */
+  len = 4;
+  while (len > 0)
+    {
+      l = read(auth->fd, msg + 4 - len, len);
+      if (l <= 0)
+	{
+	  error("read auth->fd: %.100s", strerror(errno));
+	  return 0;
+	}
+      len -= l;
+    }
+
+  /* Extract the length, and check it for sanity.  (We cannot trust
+     authentication agents). */
+  len = GET_32BIT(msg);
+  if (len < 1 || len > 256*1024)
+    fatal("Authentication reply message too long: %d\n", len);
+
+  /* Read the packet itself. */
+  buffer_clear(&auth->identities);
+  while (len > 0)
+    {
+      l = len;
+      if (l > sizeof(msg))
+	l = sizeof(msg);
+      l = read(auth->fd, msg, l);
+      if (l <= 0)
+	fatal("Incomplete authentication reply.");
+      buffer_append(&auth->identities, (char *)msg, l);
+      len -= l;
+    }
+  
+  /* Get message type, and verify that we got a proper answer. */
+  buffer_get(&auth->identities, (char *)msg, 1);
+  if (msg[0] != SSH_AGENT_RSA_IDENTITIES_ANSWER)
+    fatal("Bad authentication reply message type: %d", msg[0]);
+  
+  /* Get the number of entries in the response and check it for sanity. */
+  auth->howmany = buffer_get_int(&auth->identities);
+  if (auth->howmany > 1024)
+    fatal("Too many identities in authentication reply: %d\n", auth->howmany);
+
+  /* Return the first entry (if any). */
+  return ssh_get_next_identity(auth, bitsp, e, n, comment);
+}
+
+/* Returns the next authentication identity for the agent.  Other functions
+   can be called between this and ssh_get_first_identity or two calls of this
+   function.  This returns 0 if there are no more identities.  The caller
+   must free comment after a successful return. */
+
+int
+ssh_get_next_identity(AuthenticationConnection *auth,
+		      int *bitsp, BIGNUM *e, BIGNUM *n, char **comment)
+{
+  /* Return failure if no more entries. */
+  if (auth->howmany <= 0)
+    return 0;
+
+  /* Get the next entry from the packet.  These will abort with a fatal
+     error if the packet is too short or contains corrupt data. */
+  *bitsp = buffer_get_int(&auth->identities);
+  buffer_get_bignum(&auth->identities, e);
+  buffer_get_bignum(&auth->identities, n);
+  *comment = buffer_get_string(&auth->identities, NULL);
+
+  /* Decrement the number of remaining entries. */
+  auth->howmany--;
+
+  return 1;
+}
+
+/* Generates a random challenge, sends it to the agent, and waits for response
+   from the agent.  Returns true (non-zero) if the agent gave the correct
+   answer, zero otherwise.  Response type selects the style of response
+   desired, with 0 corresponding to protocol version 1.0 (no longer supported)
+   and 1 corresponding to protocol version 1.1. */
+
+int
+ssh_decrypt_challenge(AuthenticationConnection *auth,
+		      int bits, BIGNUM *e, BIGNUM *n, BIGNUM *challenge,
+		      unsigned char session_id[16],
+		      unsigned int response_type,
+		      unsigned char response[16])
+{
+  Buffer buffer;
+  unsigned char buf[8192];
+  int len, l, i;
+
+  /* Response type 0 is no longer supported. */
+  if (response_type == 0)
+    fatal("Compatibility with ssh protocol version 1.0 no longer supported.");
+
+  /* Format a message to the agent. */
+  buf[0] = SSH_AGENTC_RSA_CHALLENGE;
+  buffer_init(&buffer);
+  buffer_append(&buffer, (char *)buf, 1);
+  buffer_put_int(&buffer, bits);
+  buffer_put_bignum(&buffer, e);
+  buffer_put_bignum(&buffer, n);
+  buffer_put_bignum(&buffer, challenge);
+  buffer_append(&buffer, (char *)session_id, 16);
+  buffer_put_int(&buffer, response_type);
+
+  /* Get the length of the message, and format it in the buffer. */
+  len = buffer_len(&buffer);
+  PUT_32BIT(buf, len);
+
+  /* Send the length and then the packet to the agent. */
+  if (write(auth->fd, buf, 4) != 4 ||
+      write(auth->fd, buffer_ptr(&buffer), buffer_len(&buffer)) !=
+        buffer_len(&buffer))
+    {
+      error("Error writing to authentication socket.");
+    error_cleanup:
+      buffer_free(&buffer);
+      return 0;
+    }
+
+  /* Wait for response from the agent.  First read the length of the
+     response packet. */
+  len = 4;
+  while (len > 0)
+    {
+      l = read(auth->fd, buf + 4 - len, len);
+      if (l <= 0)
+	{
+	  error("Error reading response length from authentication socket.");
+	  goto error_cleanup;
+	}
+      len -= l;
+    }
+
+  /* Extract the length, and check it for sanity. */
+  len = GET_32BIT(buf);
+  if (len > 256*1024)
+    fatal("Authentication response too long: %d", len);
+
+  /* Read the rest of the response in tothe buffer. */
+  buffer_clear(&buffer);
+  while (len > 0)
+    {
+      l = len;
+      if (l > sizeof(buf))
+	l = sizeof(buf);
+      l = read(auth->fd, buf, l);
+      if (l <= 0)
+	{
+	  error("Error reading response from authentication socket.");
+	  goto error_cleanup;
+	}
+      buffer_append(&buffer, (char *)buf, l);
+      len -= l;
+    }
+
+  /* Get the type of the packet. */
+  buffer_get(&buffer, (char *)buf, 1);
+
+  /* Check for agent failure message. */
+  if (buf[0] == SSH_AGENT_FAILURE)
+    {
+      log("Agent admitted failure to authenticate using the key.");
+      goto error_cleanup;
+    }
+      
+  /* Now it must be an authentication response packet. */
+  if (buf[0] != SSH_AGENT_RSA_RESPONSE)
+    fatal("Bad authentication response: %d", buf[0]);
+
+  /* Get the response from the packet.  This will abort with a fatal error
+     if the packet is corrupt. */
+  for (i = 0; i < 16; i++)
+    response[i] = buffer_get_char(&buffer);
+
+  /* The buffer containing the packet is no longer needed. */
+  buffer_free(&buffer);
+
+  /* Correct answer. */
+  return 1;
+}  
+
+/* Adds an identity to the authentication server.  This call is not meant to
+   be used by normal applications. */
+
+int ssh_add_identity(AuthenticationConnection *auth,
+		     RSA *key, const char *comment)
+{
+  Buffer buffer;
+  unsigned char buf[8192];
+  int len, l, type;
+
+  /* Format a message to the agent. */
+  buffer_init(&buffer);
+  buffer_put_char(&buffer, SSH_AGENTC_ADD_RSA_IDENTITY);
+  buffer_put_int(&buffer, BN_num_bits(key->n));
+  buffer_put_bignum(&buffer, key->n);
+  buffer_put_bignum(&buffer, key->e);
+  buffer_put_bignum(&buffer, key->d);
+  /* To keep within the protocol: p < q for ssh. in SSL p > q */
+  buffer_put_bignum(&buffer, key->iqmp); /* ssh key->u */
+  buffer_put_bignum(&buffer, key->q); /* ssh key->p, SSL key->q */
+  buffer_put_bignum(&buffer, key->p); /* ssh key->q, SSL key->p */
+  buffer_put_string(&buffer, comment, strlen(comment));
+
+  /* Get the length of the message, and format it in the buffer. */
+  len = buffer_len(&buffer);
+  PUT_32BIT(buf, len);
+
+  /* Send the length and then the packet to the agent. */
+  if (write(auth->fd, buf, 4) != 4 ||
+      write(auth->fd, buffer_ptr(&buffer), buffer_len(&buffer)) !=
+        buffer_len(&buffer))
+    {
+      error("Error writing to authentication socket.");
+    error_cleanup:
+      buffer_free(&buffer);
+      return 0;
+    }
+
+  /* Wait for response from the agent.  First read the length of the
+     response packet. */
+  len = 4;
+  while (len > 0)
+    {
+      l = read(auth->fd, buf + 4 - len, len);
+      if (l <= 0)
+	{
+	  error("Error reading response length from authentication socket.");
+	  goto error_cleanup;
+	}
+      len -= l;
+    }
+
+  /* Extract the length, and check it for sanity. */
+  len = GET_32BIT(buf);
+  if (len > 256*1024)
+    fatal("Add identity response too long: %d", len);
+
+  /* Read the rest of the response in tothe buffer. */
+  buffer_clear(&buffer);
+  while (len > 0)
+    {
+      l = len;
+      if (l > sizeof(buf))
+	l = sizeof(buf);
+      l = read(auth->fd, buf, l);
+      if (l <= 0)
+	{
+	  error("Error reading response from authentication socket.");
+	  goto error_cleanup;
+	}
+      buffer_append(&buffer, (char *)buf, l);
+      len -= l;
+    }
+
+  /* Get the type of the packet. */
+  type = buffer_get_char(&buffer);
+  switch (type)
+    {
+    case SSH_AGENT_FAILURE:
+      buffer_free(&buffer);
+      return 0;
+    case SSH_AGENT_SUCCESS:
+      buffer_free(&buffer);
+      return 1;
+    default:
+      fatal("Bad response to add identity from authentication agent: %d", 
+	    type);
+    }
+  /*NOTREACHED*/
+  return 0;
+}  
+
+/* Removes an identity from the authentication server.  This call is not meant 
+   to be used by normal applications. */
+
+int ssh_remove_identity(AuthenticationConnection *auth, RSA *key)
+{
+  Buffer buffer;
+  unsigned char buf[8192];
+  int len, l, type;
+
+  /* Format a message to the agent. */
+  buffer_init(&buffer);
+  buffer_put_char(&buffer, SSH_AGENTC_REMOVE_RSA_IDENTITY);
+  buffer_put_int(&buffer, BN_num_bits(key->n));
+  buffer_put_bignum(&buffer, key->e);
+  buffer_put_bignum(&buffer, key->n);
+
+  /* Get the length of the message, and format it in the buffer. */
+  len = buffer_len(&buffer);
+  PUT_32BIT(buf, len);
+
+  /* Send the length and then the packet to the agent. */
+  if (write(auth->fd, buf, 4) != 4 ||
+      write(auth->fd, buffer_ptr(&buffer), buffer_len(&buffer)) !=
+        buffer_len(&buffer))
+    {
+      error("Error writing to authentication socket.");
+    error_cleanup:
+      buffer_free(&buffer);
+      return 0;
+    }
+
+  /* Wait for response from the agent.  First read the length of the
+     response packet. */
+  len = 4;
+  while (len > 0)
+    {
+      l = read(auth->fd, buf + 4 - len, len);
+      if (l <= 0)
+	{
+	  error("Error reading response length from authentication socket.");
+	  goto error_cleanup;
+	}
+      len -= l;
+    }
+
+  /* Extract the length, and check it for sanity. */
+  len = GET_32BIT(buf);
+  if (len > 256*1024)
+    fatal("Remove identity response too long: %d", len);
+
+  /* Read the rest of the response in tothe buffer. */
+  buffer_clear(&buffer);
+  while (len > 0)
+    {
+      l = len;
+      if (l > sizeof(buf))
+	l = sizeof(buf);
+      l = read(auth->fd, buf, l);
+      if (l <= 0)
+	{
+	  error("Error reading response from authentication socket.");
+	  goto error_cleanup;
+	}
+      buffer_append(&buffer, (char *)buf, l);
+      len -= l;
+    }
+
+  /* Get the type of the packet. */
+  type = buffer_get_char(&buffer);
+  switch (type)
+    {
+    case SSH_AGENT_FAILURE:
+      buffer_free(&buffer);
+      return 0;
+    case SSH_AGENT_SUCCESS:
+      buffer_free(&buffer);
+      return 1;
+    default:
+      fatal("Bad response to remove identity from authentication agent: %d", 
+	    type);
+    }
+  /*NOTREACHED*/
+  return 0;
+}  
+
+/* Removes all identities from the agent.  This call is not meant 
+   to be used by normal applications. */
+
+int ssh_remove_all_identities(AuthenticationConnection *auth)
+{
+  Buffer buffer;
+  unsigned char buf[8192];
+  int len, l, type;
+
+  /* Get the length of the message, and format it in the buffer. */
+  PUT_32BIT(buf, 1);
+  buf[4] = SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES;
+
+  /* Send the length and then the packet to the agent. */
+  if (write(auth->fd, buf, 5) != 5)
+    {
+      error("Error writing to authentication socket.");
+      return 0;
+    }
+
+  /* Wait for response from the agent.  First read the length of the
+     response packet. */
+  len = 4;
+  while (len > 0)
+    {
+      l = read(auth->fd, buf + 4 - len, len);
+      if (l <= 0)
+	{
+	  error("Error reading response length from authentication socket.");
+	  return 0;
+	}
+      len -= l;
+    }
+
+  /* Extract the length, and check it for sanity. */
+  len = GET_32BIT(buf);
+  if (len > 256*1024)
+    fatal("Remove identity response too long: %d", len);
+
+  /* Read the rest of the response into the buffer. */
+  buffer_init(&buffer);
+  while (len > 0)
+    {
+      l = len;
+      if (l > sizeof(buf))
+	l = sizeof(buf);
+      l = read(auth->fd, buf, l);
+      if (l <= 0)
+	{
+	  error("Error reading response from authentication socket.");
+	  buffer_free(&buffer);
+	  return 0;
+	}
+      buffer_append(&buffer, (char *)buf, l);
+      len -= l;
+    }
+
+  /* Get the type of the packet. */
+  type = buffer_get_char(&buffer);
+  switch (type)
+    {
+    case SSH_AGENT_FAILURE:
+      buffer_free(&buffer);
+      return 0;
+    case SSH_AGENT_SUCCESS:
+      buffer_free(&buffer);
+      return 1;
+    default:
+      fatal("Bad response to remove identity from authentication agent: %d", 
+	    type);
+    }
+  /*NOTREACHED*/
+  return 0;
+}  
diff --git a/authfd.h b/authfd.h
new file mode 100644
index 0000000..1def920
--- /dev/null
+++ b/authfd.h
@@ -0,0 +1,102 @@
+/*
+
+authfd.h
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Wed Mar 29 01:17:41 1995 ylo
+
+Functions to interface with the SSH_AUTHENTICATION_FD socket.
+
+*/
+
+/* RCSID("$Id: authfd.h,v 1.1 1999/10/27 03:42:43 damien Exp $"); */
+
+#ifndef AUTHFD_H
+#define AUTHFD_H
+
+#include "buffer.h"
+
+/* Messages for the authentication agent connection. */
+#define SSH_AGENTC_REQUEST_RSA_IDENTITIES	1
+#define SSH_AGENT_RSA_IDENTITIES_ANSWER		2
+#define SSH_AGENTC_RSA_CHALLENGE		3
+#define SSH_AGENT_RSA_RESPONSE			4
+#define SSH_AGENT_FAILURE			5
+#define SSH_AGENT_SUCCESS			6
+#define SSH_AGENTC_ADD_RSA_IDENTITY		7
+#define SSH_AGENTC_REMOVE_RSA_IDENTITY		8
+#define SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES	9
+
+typedef struct
+{
+  int fd;
+  Buffer packet;
+  Buffer identities;
+  int howmany;
+} AuthenticationConnection;
+
+/* Returns the number of the authentication fd, or -1 if there is none. */
+int ssh_get_authentication_socket();
+
+/* This should be called for any descriptor returned by 
+   ssh_get_authentication_socket().  Depending on the way the descriptor was
+   obtained, this may close the descriptor. */
+void ssh_close_authentication_socket(int authfd);
+
+/* Opens and connects a private socket for communication with the
+   authentication agent.  Returns NULL if an error occurred and the 
+   connection could not be opened.  The connection should be closed by
+   the caller by calling ssh_close_authentication_connection(). */
+AuthenticationConnection *ssh_get_authentication_connection();
+
+/* Closes the connection to the authentication agent and frees any associated
+   memory. */
+void ssh_close_authentication_connection(AuthenticationConnection *ac);
+
+/* Returns the first authentication identity held by the agent.
+   Returns true if an identity is available, 0 otherwise.
+   The caller must initialize the integers before the call, and free the
+   comment after a successful call (before calling ssh_get_next_identity). */
+int ssh_get_first_identity(AuthenticationConnection *connection,
+			   int *bitsp, BIGNUM *e, BIGNUM *n, char **comment);
+
+/* Returns the next authentication identity for the agent.  Other functions
+   can be called between this and ssh_get_first_identity or two calls of this
+   function.  This returns 0 if there are no more identities.  The caller
+   must free comment after a successful return. */
+int ssh_get_next_identity(AuthenticationConnection *connection,
+			  int *bitsp, BIGNUM *e, BIGNUM *n, char **comment);
+
+/* Requests the agent to decrypt the given challenge.  Returns true if
+   the agent claims it was able to decrypt it. */
+int ssh_decrypt_challenge(AuthenticationConnection *auth,
+			  int bits, BIGNUM *e, BIGNUM *n, BIGNUM *challenge,
+			  unsigned char session_id[16], 
+			  unsigned int response_type,
+			  unsigned char response[16]);
+
+/* Adds an identity to the authentication server.  This call is not meant to
+   be used by normal applications.  This returns true if the identity
+   was successfully added. */
+int ssh_add_identity(AuthenticationConnection *connection,
+		     RSA *key, const char *comment);
+
+/* Removes the identity from the authentication server.  This call is
+   not meant to be used by normal applications.  This returns true if the
+   identity was successfully added. */
+int ssh_remove_identity(AuthenticationConnection *connection,
+			RSA *key);
+
+/* Removes all identities from the authentication agent.  This call is not
+   meant to be used by normal applications.  This returns true if the
+   operation was successful. */
+int ssh_remove_all_identities(AuthenticationConnection *connection);
+
+/* Closes the connection to the authentication agent. */
+void ssh_close_authentication(AuthenticationConnection *connection);
+
+#endif /* AUTHFD_H */
diff --git a/authfile.c b/authfile.c
new file mode 100644
index 0000000..49390e0
--- /dev/null
+++ b/authfile.c
@@ -0,0 +1,350 @@
+/*
+
+authfile.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Mon Mar 27 03:52:05 1995 ylo
+
+This file contains functions for reading and writing identity files, and
+for reading the passphrase from the user.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: authfile.c,v 1.1 1999/10/27 03:42:43 damien Exp $");
+
+#include <openssl/bn.h>
+#include "xmalloc.h"
+#include "buffer.h"
+#include "bufaux.h"
+#include "cipher.h"
+#include "ssh.h"
+
+/* Version identification string for identity files. */
+#define AUTHFILE_ID_STRING "SSH PRIVATE KEY FILE FORMAT 1.1\n"
+
+/* Saves the authentication (private) key in a file, encrypting it with
+   passphrase.  The identification of the file (lowest 64 bits of n)
+   will precede the key to provide identification of the key without
+   needing a passphrase. */
+
+int
+save_private_key(const char *filename, const char *passphrase,
+		 RSA *key, const char *comment)
+{
+  Buffer buffer, encrypted;
+  char buf[100], *cp;
+  int f, i;
+  CipherContext cipher;
+  int cipher_type;
+  u_int32_t rand;
+
+  /* If the passphrase is empty, use SSH_CIPHER_NONE to ease converting to
+     another cipher; otherwise use SSH_AUTHFILE_CIPHER. */
+  if (strcmp(passphrase, "") == 0)
+    cipher_type = SSH_CIPHER_NONE;
+  else
+    cipher_type = SSH_AUTHFILE_CIPHER;
+
+  /* This buffer is used to built the secret part of the private key. */
+  buffer_init(&buffer);
+  
+  /* Put checkbytes for checking passphrase validity. */
+  rand = arc4random();
+  buf[0] = rand & 0xff;
+  buf[1] = (rand >> 8) & 0xff;
+  buf[2] = buf[0];
+  buf[3] = buf[1];
+  buffer_append(&buffer, buf, 4);
+
+  /* Store the private key (n and e will not be stored because they will
+     be stored in plain text, and storing them also in encrypted format
+     would just give known plaintext). */
+  buffer_put_bignum(&buffer, key->d);
+  buffer_put_bignum(&buffer, key->iqmp);
+  buffer_put_bignum(&buffer, key->q); /* reverse from SSL p */
+  buffer_put_bignum(&buffer, key->p); /* reverse from SSL q */
+
+  /* Pad the part to be encrypted until its size is a multiple of 8. */
+  while (buffer_len(&buffer) % 8 != 0)
+    buffer_put_char(&buffer, 0);
+
+  /* This buffer will be used to contain the data in the file. */
+  buffer_init(&encrypted);
+
+  /* First store keyfile id string. */
+  cp = AUTHFILE_ID_STRING;
+  for (i = 0; cp[i]; i++)
+    buffer_put_char(&encrypted, cp[i]);
+  buffer_put_char(&encrypted, 0);
+
+  /* Store cipher type. */
+  buffer_put_char(&encrypted, cipher_type);
+  buffer_put_int(&encrypted, 0);  /* For future extension */
+
+  /* Store public key.  This will be in plain text. */
+  buffer_put_int(&encrypted, BN_num_bits(key->n));
+  buffer_put_bignum(&encrypted, key->n);
+  buffer_put_bignum(&encrypted, key->e);
+  buffer_put_string(&encrypted, comment, strlen(comment));
+
+  /* Allocate space for the private part of the key in the buffer. */
+  buffer_append_space(&encrypted, &cp, buffer_len(&buffer));
+
+  cipher_set_key_string(&cipher, cipher_type, passphrase, 1);
+  cipher_encrypt(&cipher, (unsigned char *)cp, 
+		 (unsigned char *)buffer_ptr(&buffer),
+		 buffer_len(&buffer));
+  memset(&cipher, 0, sizeof(cipher));
+
+  /* Destroy temporary data. */
+  memset(buf, 0, sizeof(buf));
+  buffer_free(&buffer);
+
+  /* Write to a file. */
+  f = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (f < 0)
+    return 0;
+
+  if (write(f, buffer_ptr(&encrypted), buffer_len(&encrypted)) != 
+      buffer_len(&encrypted))
+    {
+      debug("Write to key file %.200s failed: %.100s", filename,
+	    strerror(errno));
+      buffer_free(&encrypted);
+      close(f);
+      remove(filename);
+      return 0;
+    }
+  close(f);
+  buffer_free(&encrypted);
+  return 1;
+}
+
+/* Loads the public part of the key file.  Returns 0 if an error
+   was encountered (the file does not exist or is not readable), and
+   non-zero otherwise. */
+
+int
+load_public_key(const char *filename, RSA *pub, 
+		char **comment_return)
+{
+  int f, i;
+  off_t len;
+  Buffer buffer;
+  char *cp;
+
+  /* Read data from the file into the buffer. */
+  f = open(filename, O_RDONLY);
+  if (f < 0)
+    return 0;
+
+  len = lseek(f, (off_t)0, SEEK_END);
+  lseek(f, (off_t)0, SEEK_SET);
+  
+  buffer_init(&buffer);
+  buffer_append_space(&buffer, &cp, len);
+
+  if (read(f, cp, (size_t)len) != (size_t)len)
+    {
+      debug("Read from key file %.200s failed: %.100s", filename, 
+	    strerror(errno));
+      buffer_free(&buffer);
+      close(f);
+      return 0;
+    }
+  close(f);
+
+  /* Check that it is at least big enought to contain the ID string. */
+  if (len < strlen(AUTHFILE_ID_STRING) + 1)
+    {
+      debug("Bad key file %.200s.", filename);
+      buffer_free(&buffer);
+      return 0;
+    }
+
+  /* Make sure it begins with the id string.  Consume the id string from
+     the buffer. */
+  for (i = 0; i < (unsigned int)strlen(AUTHFILE_ID_STRING) + 1; i++)
+    if (buffer_get_char(&buffer) != (unsigned char)AUTHFILE_ID_STRING[i])
+      {
+	debug("Bad key file %.200s.", filename);
+	buffer_free(&buffer);
+	return 0;
+      }
+
+  /* Skip cipher type and reserved data. */
+  (void)buffer_get_char(&buffer); /* cipher type */
+  (void)buffer_get_int(&buffer); /* reserved */
+
+  /* Read the public key from the buffer. */
+  buffer_get_int(&buffer);
+  pub->n = BN_new();
+  buffer_get_bignum(&buffer, pub->n);
+  pub->e = BN_new();
+  buffer_get_bignum(&buffer, pub->e);
+  if (comment_return)
+    *comment_return = buffer_get_string(&buffer, NULL);
+  /* The encrypted private part is not parsed by this function. */
+
+  buffer_free(&buffer);
+  
+  return 1;
+}
+
+/* Loads the private key from the file.  Returns 0 if an error is encountered
+   (file does not exist or is not readable, or passphrase is bad).
+   This initializes the private key. */
+
+int
+load_private_key(const char *filename, const char *passphrase,
+		 RSA *prv, char **comment_return)
+{
+  int f, i, check1, check2, cipher_type;
+  off_t len;
+  Buffer buffer, decrypted;
+  char *cp;
+  CipherContext cipher;
+  BN_CTX *ctx;
+  BIGNUM *aux;
+  struct stat st;
+
+  /* Read the file into the buffer. */
+  f = open(filename, O_RDONLY);
+  if (f < 0)
+    return 0;
+
+  /* We assume we are called under uid of the owner of the file */
+  if (fstat(f, &st) < 0 ||
+      (st.st_uid != 0 && st.st_uid != getuid()) ||
+      (st.st_mode & 077) != 0) {
+    error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
+    error("@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @");
+    error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
+    error("Bad ownership or mode(0%3.3o) for '%s'.",
+	   st.st_mode & 0777, filename);
+    error("It is recommended that your private key files are NOT accessible by others.");
+    return 0;
+  }
+
+  len = lseek(f, (off_t)0, SEEK_END);
+  lseek(f, (off_t)0, SEEK_SET);
+  
+  buffer_init(&buffer);
+  buffer_append_space(&buffer, &cp, len);
+
+  if (read(f, cp, (size_t)len) != (size_t)len)
+    {
+      debug("Read from key file %.200s failed: %.100s", filename,
+	    strerror(errno));
+      buffer_free(&buffer);
+      close(f);
+      return 0;
+    }
+  close(f);
+
+  /* Check that it is at least big enought to contain the ID string. */
+  if (len < strlen(AUTHFILE_ID_STRING) + 1)
+    {
+      debug("Bad key file %.200s.", filename);
+      buffer_free(&buffer);
+      return 0;
+    }
+
+  /* Make sure it begins with the id string.  Consume the id string from
+     the buffer. */
+  for (i = 0; i < (unsigned int)strlen(AUTHFILE_ID_STRING) + 1; i++)
+    if (buffer_get_char(&buffer) != (unsigned char)AUTHFILE_ID_STRING[i])
+      {
+	debug("Bad key file %.200s.", filename);
+	buffer_free(&buffer);
+	return 0;
+      }
+
+  /* Read cipher type. */
+  cipher_type = buffer_get_char(&buffer);
+  (void)buffer_get_int(&buffer);  /* Reserved data. */
+
+  /* Read the public key from the buffer. */
+  buffer_get_int(&buffer);
+  prv->n = BN_new();
+  buffer_get_bignum(&buffer, prv->n);
+  prv->e = BN_new();
+  buffer_get_bignum(&buffer, prv->e);
+  if (comment_return)
+    *comment_return = buffer_get_string(&buffer, NULL);
+  else
+    xfree(buffer_get_string(&buffer, NULL));
+
+  /* Check that it is a supported cipher. */
+  if (((cipher_mask() | SSH_CIPHER_NONE | SSH_AUTHFILE_CIPHER) &
+	(1 << cipher_type)) == 0)
+    {
+      debug("Unsupported cipher %.100s used in key file %.200s.",
+	    cipher_name(cipher_type), filename);
+      buffer_free(&buffer);
+      goto fail;
+    }
+
+  /* Initialize space for decrypted data. */
+  buffer_init(&decrypted);
+  buffer_append_space(&decrypted, &cp, buffer_len(&buffer));
+      
+  /* Rest of the buffer is encrypted.  Decrypt it using the passphrase. */
+  cipher_set_key_string(&cipher, cipher_type, passphrase, 0);
+  cipher_decrypt(&cipher, (unsigned char *)cp,
+		 (unsigned char *)buffer_ptr(&buffer),
+		 buffer_len(&buffer));
+
+  buffer_free(&buffer);
+
+  check1 = buffer_get_char(&decrypted);
+  check2 = buffer_get_char(&decrypted);
+  if (check1 != buffer_get_char(&decrypted) ||
+      check2 != buffer_get_char(&decrypted))
+    {
+      if (strcmp(passphrase, "") != 0)
+	debug("Bad passphrase supplied for key file %.200s.", filename);
+      /* Bad passphrase. */
+      buffer_free(&decrypted);
+    fail:
+      BN_clear_free(prv->n);
+      BN_clear_free(prv->e);
+      if (comment_return)
+	xfree(*comment_return);
+      return 0;
+    }
+
+  /* Read the rest of the private key. */
+  prv->d = BN_new();
+  buffer_get_bignum(&decrypted, prv->d);
+  prv->iqmp = BN_new();
+  buffer_get_bignum(&decrypted, prv->iqmp); /* u */
+  /* in SSL and SSH p and q are exchanged */
+  prv->q = BN_new();
+  buffer_get_bignum(&decrypted, prv->q); /* p */
+  prv->p = BN_new();
+  buffer_get_bignum(&decrypted, prv->p); /* q */
+
+  ctx = BN_CTX_new();
+  aux = BN_new();
+
+  BN_sub(aux, prv->q, BN_value_one());
+  prv->dmq1 = BN_new();
+  BN_mod(prv->dmq1, prv->d, aux, ctx);
+
+  BN_sub(aux, prv->p, BN_value_one());
+  prv->dmp1 = BN_new();
+  BN_mod(prv->dmp1, prv->d, aux, ctx);
+
+  BN_clear_free(aux);
+  BN_CTX_free(ctx);
+  
+  buffer_free(&decrypted);
+
+  return 1;
+}
diff --git a/bufaux.c b/bufaux.c
new file mode 100644
index 0000000..1ae39d6
--- /dev/null
+++ b/bufaux.c
@@ -0,0 +1,141 @@
+/*
+
+bufaux.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Wed Mar 29 02:24:47 1995 ylo
+
+Auxiliary functions for storing and retrieving various data types to/from
+Buffers.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: bufaux.c,v 1.1 1999/10/27 03:42:43 damien Exp $");
+
+#include "ssh.h"
+#include <openssl/bn.h>
+#include "bufaux.h"
+#include "xmalloc.h"
+#include "getput.h"
+
+/* Stores an BIGNUM in the buffer with a 2-byte msb first bit count, followed
+   by (bits+7)/8 bytes of binary data, msb first. */
+
+void
+buffer_put_bignum(Buffer *buffer, BIGNUM *value)
+{
+  int bits = BN_num_bits(value);
+  int bin_size = (bits + 7) / 8;
+  char *buf = xmalloc(bin_size);
+  int oi;
+  char msg[2];
+  
+  /* Get the value of in binary */
+  oi = BN_bn2bin(value, buf);
+  assert(oi == bin_size);
+
+  /* Store the number of bits in the buffer in two bytes, msb first. */
+  PUT_16BIT(msg, bits);
+  buffer_append(buffer, msg, 2);
+  /* Store the binary data. */
+  buffer_append(buffer, buf, oi);
+  /* Clear the temporary data. */
+  memset(buf, 0, bin_size);
+  xfree(buf);
+}
+
+/* Retrieves an BIGNUM from the buffer. */
+
+int
+buffer_get_bignum(Buffer *buffer, BIGNUM *value)
+{
+  int bits, bytes;
+  unsigned char buf[2], *bin;
+
+  /* Get the number for bits. */
+  buffer_get(buffer, (char *)buf, 2);
+  bits = GET_16BIT(buf);
+  /* Compute the number of binary bytes that follow. */
+  bytes = (bits + 7) / 8;
+  bin = xmalloc(bytes);
+  buffer_get(buffer, bin, bytes);
+  BN_bin2bn(bin, bytes, value);
+  xfree(bin);
+
+  return 2 + bytes;
+}
+
+/* Returns an integer from the buffer (4 bytes, msb first). */
+
+unsigned int buffer_get_int(Buffer *buffer)
+{
+  unsigned char buf[4];
+  buffer_get(buffer, (char *)buf, 4);
+  return GET_32BIT(buf);
+}
+
+/* Stores an integer in the buffer in 4 bytes, msb first. */
+
+void buffer_put_int(Buffer *buffer, unsigned int value)
+{
+  char buf[4];
+  PUT_32BIT(buf, value);
+  buffer_append(buffer, buf, 4);
+}
+
+/* Returns an arbitrary binary string from the buffer.  The string cannot
+   be longer than 256k.  The returned value points to memory allocated
+   with xmalloc; it is the responsibility of the calling function to free
+   the data.  If length_ptr is non-NULL, the length of the returned data
+   will be stored there.  A null character will be automatically appended
+   to the returned string, and is not counted in length. */
+
+char *buffer_get_string(Buffer *buffer, unsigned int *length_ptr)
+{
+  unsigned int len;
+  char *value;
+  /* Get the length. */
+  len = buffer_get_int(buffer);
+  if (len > 256*1024)
+    fatal("Received packet with bad string length %d", len);
+  /* Allocate space for the string.  Add one byte for a null character. */
+  value = xmalloc(len + 1);
+  /* Get the string. */
+  buffer_get(buffer, value, len);
+  /* Append a null character to make processing easier. */
+  value[len] = 0;
+  /* Optionally return the length of the string. */
+  if (length_ptr)
+    *length_ptr = len;
+  return value;
+}
+
+/* Stores and arbitrary binary string in the buffer. */
+
+void buffer_put_string(Buffer *buffer, const void *buf, unsigned int len)
+{
+  buffer_put_int(buffer, len);
+  buffer_append(buffer, buf, len);
+}
+
+/* Returns a character from the buffer (0 - 255). */
+
+int buffer_get_char(Buffer *buffer)
+{
+  char ch;
+  buffer_get(buffer, &ch, 1);
+  return (unsigned char)ch;
+}
+
+/* Stores a character in the buffer. */
+
+void buffer_put_char(Buffer *buffer, int value)
+{
+  char ch = value;
+  buffer_append(buffer, &ch, 1);
+}
diff --git a/bufaux.h b/bufaux.h
new file mode 100644
index 0000000..bfc6684
--- /dev/null
+++ b/bufaux.h
@@ -0,0 +1,51 @@
+/*
+
+bufaux.h
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Wed Mar 29 02:18:23 1995 ylo
+
+*/
+
+/* RCSID("$Id: bufaux.h,v 1.1 1999/10/27 03:42:43 damien Exp $"); */
+
+#ifndef BUFAUX_H
+#define BUFAUX_H
+
+#include "buffer.h"
+
+/* Stores an BIGNUM in the buffer with a 2-byte msb first bit count, followed
+   by (bits+7)/8 bytes of binary data, msb first. */
+void buffer_put_bignum(Buffer *buffer, BIGNUM *value);
+
+/* Retrieves an BIGNUM from the buffer. */
+int buffer_get_bignum(Buffer *buffer, BIGNUM *value);
+
+/* Returns an integer from the buffer (4 bytes, msb first). */
+unsigned int buffer_get_int(Buffer *buffer);
+
+/* Stores an integer in the buffer in 4 bytes, msb first. */
+void buffer_put_int(Buffer *buffer, unsigned int value);
+
+/* Returns a character from the buffer (0 - 255). */
+int buffer_get_char(Buffer *buffer);
+
+/* Stores a character in the buffer. */
+void buffer_put_char(Buffer *buffer, int value);
+
+/* Returns an arbitrary binary string from the buffer.  The string cannot
+   be longer than 256k.  The returned value points to memory allocated
+   with xmalloc; it is the responsibility of the calling function to free
+   the data.  If length_ptr is non-NULL, the length of the returned data
+   will be stored there.  A null character will be automatically appended
+   to the returned string, and is not counted in length. */
+char *buffer_get_string(Buffer *buffer, unsigned int *length_ptr);
+
+/* Stores and arbitrary binary string in the buffer. */
+void buffer_put_string(Buffer *buffer, const void *buf, unsigned int len);
+
+#endif /* BUFAUX_H */
diff --git a/buffer.c b/buffer.c
new file mode 100644
index 0000000..e183d10
--- /dev/null
+++ b/buffer.c
@@ -0,0 +1,150 @@
+/*
+
+buffer.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Sat Mar 18 04:15:33 1995 ylo
+
+Functions for manipulating fifo buffers (that can grow if needed).
+
+*/
+
+#include "includes.h"
+RCSID("$Id: buffer.c,v 1.1 1999/10/27 03:42:43 damien Exp $");
+
+#include "xmalloc.h"
+#include "buffer.h"
+#include "ssh.h"
+
+/* Initializes the buffer structure. */
+
+void buffer_init(Buffer *buffer)
+{
+  buffer->alloc = 4096;
+  buffer->buf = xmalloc(buffer->alloc);
+  buffer->offset = 0;
+  buffer->end = 0;
+}
+
+/* Frees any memory used for the buffer. */
+
+void buffer_free(Buffer *buffer)
+{
+  memset(buffer->buf, 0, buffer->alloc);
+  xfree(buffer->buf);
+}
+
+/* Clears any data from the buffer, making it empty.  This does not actually
+   zero the memory. */
+
+void buffer_clear(Buffer *buffer)
+{
+  buffer->offset = 0;
+  buffer->end = 0;
+}
+
+/* Appends data to the buffer, expanding it if necessary. */
+
+void buffer_append(Buffer *buffer, const char *data, unsigned int len)
+{
+  char *cp;
+  buffer_append_space(buffer, &cp, len);
+  memcpy(cp, data, len);
+}
+
+/* Appends space to the buffer, expanding the buffer if necessary.
+   This does not actually copy the data into the buffer, but instead
+   returns a pointer to the allocated region. */
+
+void buffer_append_space(Buffer *buffer, char **datap, unsigned int len)
+{
+  /* If the buffer is empty, start using it from the beginning. */
+  if (buffer->offset == buffer->end)
+    {
+      buffer->offset = 0;
+      buffer->end = 0;
+    }
+
+ restart:
+  /* If there is enough space to store all data, store it now. */
+  if (buffer->end + len < buffer->alloc)
+    {
+      *datap = buffer->buf + buffer->end;
+      buffer->end += len;
+      return;
+    }
+
+  /* If the buffer is quite empty, but all data is at the end, move the
+     data to the beginning and retry. */
+  if (buffer->offset > buffer->alloc / 2)
+    {
+      memmove(buffer->buf, buffer->buf + buffer->offset,
+	      buffer->end - buffer->offset);
+      buffer->end -= buffer->offset;
+      buffer->offset = 0;
+      goto restart;
+    }
+
+  /* Increase the size of the buffer and retry. */
+  buffer->alloc += len + 32768;
+  buffer->buf = xrealloc(buffer->buf, buffer->alloc);
+  goto restart;
+}
+
+/* Returns the number of bytes of data in the buffer. */
+
+unsigned int buffer_len(Buffer *buffer)
+{
+  return buffer->end - buffer->offset;
+}
+
+/* Gets data from the beginning of the buffer. */
+
+void buffer_get(Buffer *buffer, char *buf, unsigned int len)
+{
+  if (len > buffer->end - buffer->offset)
+    fatal("buffer_get trying to get more bytes than in buffer");
+  memcpy(buf, buffer->buf + buffer->offset, len);
+  buffer->offset += len;
+}
+
+/* Consumes the given number of bytes from the beginning of the buffer. */
+
+void buffer_consume(Buffer *buffer, unsigned int bytes)
+{
+  if (bytes > buffer->end - buffer->offset)
+    fatal("buffer_get trying to get more bytes than in buffer");
+  buffer->offset += bytes;
+}  
+
+/* Consumes the given number of bytes from the end of the buffer. */
+
+void buffer_consume_end(Buffer *buffer, unsigned int bytes)
+{
+  if (bytes > buffer->end - buffer->offset)
+    fatal("buffer_get trying to get more bytes than in buffer");
+  buffer->end -= bytes;
+}  
+
+/* Returns a pointer to the first used byte in the buffer. */
+
+char *buffer_ptr(Buffer *buffer)
+{
+  return buffer->buf + buffer->offset;
+}
+
+/* Dumps the contents of the buffer to stderr. */
+
+void buffer_dump(Buffer *buffer)
+{
+  int i;
+  unsigned char *ucp = (unsigned char *)buffer->buf;
+  
+  for (i = buffer->offset; i < buffer->end; i++)
+    fprintf(stderr, " %02x", ucp[i]);
+  fprintf(stderr, "\n");
+}
diff --git a/buffer.h b/buffer.h
new file mode 100644
index 0000000..d0369dc
--- /dev/null
+++ b/buffer.h
@@ -0,0 +1,66 @@
+/*
+
+buffer.h
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Sat Mar 18 04:12:25 1995 ylo
+
+Code for manipulating FIFO buffers.
+
+*/
+
+/* RCSID("$Id: buffer.h,v 1.1 1999/10/27 03:42:43 damien Exp $"); */
+
+#ifndef BUFFER_H
+#define BUFFER_H
+
+typedef struct
+{
+  char *buf;			/* Buffer for data. */
+  unsigned int alloc;		/* Number of bytes allocated for data. */
+  unsigned int offset;		/* Offset of first byte containing data. */
+  unsigned int end;		/* Offset of last byte containing data. */
+} Buffer;
+
+/* Initializes the buffer structure. */
+void buffer_init(Buffer *buffer);
+
+/* Frees any memory used for the buffer. */
+void buffer_free(Buffer *buffer);
+
+/* Clears any data from the buffer, making it empty.  This does not actually
+   zero the memory. */
+void buffer_clear(Buffer *buffer);
+
+/* Appends data to the buffer, expanding it if necessary. */
+void buffer_append(Buffer *buffer, const char *data, unsigned int len);
+
+/* Appends space to the buffer, expanding the buffer if necessary.
+   This does not actually copy the data into the buffer, but instead
+   returns a pointer to the allocated region. */
+void buffer_append_space(Buffer *buffer, char **datap, unsigned int len);
+
+/* Returns the number of bytes of data in the buffer. */
+unsigned int buffer_len(Buffer *buffer);
+
+/* Gets data from the beginning of the buffer. */
+void buffer_get(Buffer *buffer, char *buf, unsigned int len);
+
+/* Consumes the given number of bytes from the beginning of the buffer. */
+void buffer_consume(Buffer *buffer, unsigned int bytes);
+
+/* Consumes the given number of bytes from the end of the buffer. */
+void buffer_consume_end(Buffer *buffer, unsigned int bytes);
+
+/* Returns a pointer to the first used byte in the buffer. */
+char *buffer_ptr(Buffer *buffer);
+
+/* Dumps the contents of the buffer to stderr in hex.  This intended for
+   debugging purposes only. */
+void buffer_dump(Buffer *buffer);
+
+#endif /* BUFFER_H */
diff --git a/canohost.c b/canohost.c
new file mode 100644
index 0000000..85d9729
--- /dev/null
+++ b/canohost.c
@@ -0,0 +1,234 @@
+/*
+
+canohost.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Sun Jul  2 17:52:22 1995 ylo
+
+Functions for returning the canonical host name of the remote site.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: canohost.c,v 1.1 1999/10/27 03:42:43 damien Exp $");
+
+#include "packet.h"
+#include "xmalloc.h"
+#include "ssh.h"
+
+/* Return the canonical name of the host at the other end of the socket. 
+   The caller should free the returned string with xfree. */
+
+char *get_remote_hostname(int socket)
+{
+  struct sockaddr_in from;
+  int fromlen, i;
+  struct hostent *hp;
+  char name[MAXHOSTNAMELEN];
+
+  /* Get IP address of client. */
+  fromlen = sizeof(from);
+  memset(&from, 0, sizeof(from));
+  if (getpeername(socket, (struct sockaddr *)&from, &fromlen) < 0)
+    {
+      error("getpeername failed: %.100s", strerror(errno));
+      strlcpy(name, "UNKNOWN", sizeof name);
+      goto check_ip_options;
+    }
+  
+  /* Map the IP address to a host name. */
+  hp = gethostbyaddr((char *)&from.sin_addr, sizeof(struct in_addr),
+		     from.sin_family);
+  if (hp)
+    {
+      /* Got host name, find canonic host name. */
+      if (strchr(hp->h_name, '.') != 0)
+	strlcpy(name, hp->h_name, sizeof(name));
+      else if (hp->h_aliases != 0
+	       && hp->h_aliases[0] != 0
+	       && strchr(hp->h_aliases[0], '.') != 0)
+	strlcpy(name, hp->h_aliases[0], sizeof(name));
+      else
+	strlcpy(name, hp->h_name, sizeof(name));
+      
+      /* Convert it to all lowercase (which is expected by the rest of this
+	 software). */
+      for (i = 0; name[i]; i++)
+	if (isupper(name[i]))
+	  name[i] = tolower(name[i]);
+
+      /* Map it back to an IP address and check that the given address actually
+	 is an address of this host.  This is necessary because anyone with
+	 access to a name server can define arbitrary names for an IP address.
+	 Mapping from name to IP address can be trusted better (but can still
+	 be fooled if the intruder has access to the name server of the
+	 domain). */
+      hp = gethostbyname(name);
+      if (!hp)
+	{
+	  log("reverse mapping checking gethostbyname for %.700s failed - POSSIBLE BREAKIN ATTEMPT!", name);
+	  strlcpy(name, inet_ntoa(from.sin_addr), sizeof name);
+	  goto check_ip_options;
+	}
+      /* Look for the address from the list of addresses. */
+      for (i = 0; hp->h_addr_list[i]; i++)
+	if (memcmp(hp->h_addr_list[i], &from.sin_addr, sizeof(from.sin_addr))
+	    == 0)
+	  break;
+      /* If we reached the end of the list, the address was not there. */
+      if (!hp->h_addr_list[i])
+	{
+	  /* Address not found for the host name. */
+	  log("Address %.100s maps to %.600s, but this does not map back to the address - POSSIBLE BREAKIN ATTEMPT!",
+	      inet_ntoa(from.sin_addr), name);
+	  strlcpy(name, inet_ntoa(from.sin_addr), sizeof name);
+	  goto check_ip_options;
+	}
+      /* Address was found for the host name.  We accept the host name. */
+    }
+  else
+    {
+      /* Host name not found.  Use ascii representation of the address. */
+      strlcpy(name, inet_ntoa(from.sin_addr), sizeof name);
+      log("Could not reverse map address %.100s.", name);
+    }
+
+ check_ip_options:
+  
+  /* If IP options are supported, make sure there are none (log and clear
+     them if any are found).  Basically we are worried about source routing;
+     it can be used to pretend you are somebody (ip-address) you are not.
+     That itself may be "almost acceptable" under certain circumstances,
+     but rhosts autentication is useless if source routing is accepted.
+     Notice also that if we just dropped source routing here, the other
+     side could use IP spoofing to do rest of the interaction and could still
+     bypass security.  So we exit here if we detect any IP options. */
+  {
+    unsigned char options[200], *ucp;
+    char text[1024], *cp;
+    int option_size, ipproto;
+    struct protoent *ip;
+    
+    if ((ip = getprotobyname("ip")) != NULL)
+      ipproto = ip->p_proto;
+    else
+      ipproto = IPPROTO_IP;
+    option_size = sizeof(options);
+    if (getsockopt(0, ipproto, IP_OPTIONS, (char *)options,
+		   &option_size) >= 0 && option_size != 0)
+      {
+	cp = text;
+	/* Note: "text" buffer must be at least 3x as big as options. */
+	for (ucp = options; option_size > 0; ucp++, option_size--, cp += 3)
+	  sprintf(cp, " %2.2x", *ucp);
+	log("Connection from %.100s with IP options:%.800s",
+	    inet_ntoa(from.sin_addr), text);
+	packet_disconnect("Connection from %.100s with IP options:%.800s", 
+			  inet_ntoa(from.sin_addr), text);
+      }
+  }
+
+  return xstrdup(name);
+}
+
+static char *canonical_host_name = NULL;
+static char *canonical_host_ip = NULL;
+
+/* Return the canonical name of the host in the other side of the current
+   connection.  The host name is cached, so it is efficient to call this 
+   several times. */
+
+const char *get_canonical_hostname()
+{
+  /* Check if we have previously retrieved this same name. */
+  if (canonical_host_name != NULL)
+    return canonical_host_name;
+
+  /* Get the real hostname if socket; otherwise return UNKNOWN. */
+  if (packet_get_connection_in() == packet_get_connection_out())
+    canonical_host_name = get_remote_hostname(packet_get_connection_in());
+  else
+    canonical_host_name = xstrdup("UNKNOWN");
+
+  return canonical_host_name;
+}
+
+/* Returns the IP-address of the remote host as a string.  The returned
+   string need not be freed. */
+
+const char *get_remote_ipaddr()
+{
+  struct sockaddr_in from;
+  int fromlen, socket;
+
+  /* Check if we have previously retrieved this same name. */
+  if (canonical_host_ip != NULL)
+    return canonical_host_ip;
+
+  /* If not a socket, return UNKNOWN. */
+  if (packet_get_connection_in() != packet_get_connection_out())
+    {
+      canonical_host_ip = xstrdup("UNKNOWN");
+      return canonical_host_ip;
+    }
+
+  /* Get client socket. */
+  socket = packet_get_connection_in();
+
+  /* Get IP address of client. */
+  fromlen = sizeof(from);
+  memset(&from, 0, sizeof(from));
+  if (getpeername(socket, (struct sockaddr *)&from, &fromlen) < 0)
+    {
+      error("getpeername failed: %.100s", strerror(errno));
+      return NULL;
+    }
+
+  /* Get the IP address in ascii. */
+  canonical_host_ip = xstrdup(inet_ntoa(from.sin_addr));
+
+  /* Return ip address string. */
+  return canonical_host_ip;
+}
+
+/* Returns the port of the peer of the socket. */
+
+int get_peer_port(int sock)
+{
+  struct sockaddr_in from;
+  int fromlen;
+
+  /* Get IP address of client. */
+  fromlen = sizeof(from);
+  memset(&from, 0, sizeof(from));
+  if (getpeername(sock, (struct sockaddr *)&from, &fromlen) < 0)
+    {
+      error("getpeername failed: %.100s", strerror(errno));
+      return 0;
+    }
+
+  /* Return port number. */
+  return ntohs(from.sin_port);
+}
+
+/* Returns the port number of the remote host.  */
+
+int get_remote_port()
+{
+  int socket;
+
+  /* If the connection is not a socket, return 65535.  This is intentionally
+     chosen to be an unprivileged port number. */
+  if (packet_get_connection_in() != packet_get_connection_out())
+    return 65535;
+
+  /* Get client socket. */
+  socket = packet_get_connection_in();
+
+  /* Get and return the peer port number. */
+  return get_peer_port(socket);
+}
diff --git a/channels.c b/channels.c
new file mode 100644
index 0000000..38a65a0
--- /dev/null
+++ b/channels.c
@@ -0,0 +1,1500 @@
+/*
+
+channels.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Fri Mar 24 16:35:24 1995 ylo
+
+This file contains functions for generic socket connection forwarding.
+There is also code for initiating connection forwarding for X11 connections,
+arbitrary tcp/ip connections, and the authentication agent connection.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: channels.c,v 1.1 1999/10/27 03:42:44 damien Exp $");
+
+#include "ssh.h"
+#include "packet.h"
+#include "xmalloc.h"
+#include "buffer.h"
+#include "authfd.h"
+#include "uidswap.h"
+#include "servconf.h"
+
+#include "channels.h"
+#include "nchan.h"
+#include "compat.h"
+
+/* Maximum number of fake X11 displays to try. */
+#define MAX_DISPLAYS  1000
+
+/* Max len of agent socket */
+#define MAX_SOCKET_NAME 100
+
+/* Pointer to an array containing all allocated channels.  The array is
+   dynamically extended as needed. */
+static Channel *channels = NULL;
+
+/* Size of the channel array.  All slots of the array must always be
+   initialized (at least the type field); unused slots are marked with
+   type SSH_CHANNEL_FREE. */
+static int channels_alloc = 0;
+
+/* Maximum file descriptor value used in any of the channels.  This is updated
+   in channel_allocate. */
+static int channel_max_fd_value = 0;
+
+/* Name and directory of socket for authentication agent forwarding. */
+static char *channel_forwarded_auth_socket_name = NULL;
+static char *channel_forwarded_auth_socket_dir  = NULL;
+
+/* Saved X11 authentication protocol name. */
+char *x11_saved_proto = NULL;
+
+/* Saved X11 authentication data.  This is the real data. */
+char *x11_saved_data = NULL;
+unsigned int x11_saved_data_len = 0;
+
+/* Fake X11 authentication data.  This is what the server will be sending
+   us; we should replace any occurrences of this by the real data. */
+char *x11_fake_data = NULL;
+unsigned int x11_fake_data_len;
+
+/* Data structure for storing which hosts are permitted for forward requests.
+   The local sides of any remote forwards are stored in this array to prevent
+   a corrupt remote server from accessing arbitrary TCP/IP ports on our
+   local network (which might be behind a firewall). */
+typedef struct
+{
+  char *host;		/* Host name. */
+  int port;		/* Port number. */
+} ForwardPermission;
+
+/* List of all permitted host/port pairs to connect. */
+static ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION];
+/* Number of permitted host/port pairs in the array. */
+static int num_permitted_opens = 0;
+/* If this is true, all opens are permitted.  This is the case on the
+   server on which we have to trust the client anyway, and the user could
+   do anything after logging in anyway. */
+static int all_opens_permitted = 0;
+
+/* This is set to true if both sides support SSH_PROTOFLAG_HOST_IN_FWD_OPEN. */
+static int have_hostname_in_open = 0;
+
+/* Sets specific protocol options. */
+
+void channel_set_options(int hostname_in_open)
+{
+  have_hostname_in_open = hostname_in_open;
+}
+
+/* Permits opening to any host/port in SSH_MSG_PORT_OPEN.  This is usually
+   called by the server, because the user could connect to any port anyway,
+   and the server has no way to know but to trust the client anyway. */
+
+void channel_permit_all_opens()
+{
+  all_opens_permitted = 1;
+}
+
+/* Allocate a new channel object and set its type and socket. 
+   This will cause remote_name to be freed. */
+
+int channel_allocate(int type, int sock, char *remote_name)
+{
+  int i, old_channels;
+
+  /* Update the maximum file descriptor value. */
+  if (sock > channel_max_fd_value)
+    channel_max_fd_value = sock;
+
+  /* Do initial allocation if this is the first call. */
+  if (channels_alloc == 0)
+    {
+      channels_alloc = 10;
+      channels = xmalloc(channels_alloc * sizeof(Channel));
+      for (i = 0; i < channels_alloc; i++)
+	channels[i].type = SSH_CHANNEL_FREE;
+
+      /* Kludge: arrange a call to channel_stop_listening if we terminate
+	 with fatal(). */
+      fatal_add_cleanup((void (*)(void *))channel_stop_listening, NULL);
+    }
+
+  /* Try to find a free slot where to put the new channel. */
+  for (i = 0; i < channels_alloc; i++)
+    if (channels[i].type == SSH_CHANNEL_FREE)
+      {
+	/* Found a free slot.  Initialize the fields and return its number. */
+	buffer_init(&channels[i].input);
+	buffer_init(&channels[i].output);
+	channels[i].self = i;
+	channels[i].type = type;
+	channels[i].x11 = 0;
+	channels[i].sock = sock;
+	channels[i].remote_id = -1;
+	channels[i].remote_name = remote_name;
+        chan_init_iostates(&channels[i]);
+	return i;
+      }
+
+  /* There are no free slots.  Must expand the array. */
+  old_channels = channels_alloc;
+  channels_alloc += 10;
+  channels = xrealloc(channels, channels_alloc * sizeof(Channel));
+  for (i = old_channels; i < channels_alloc; i++)
+    channels[i].type = SSH_CHANNEL_FREE;
+
+  /* We know that the next one after the old maximum channel number is now
+     available.  Initialize and return its number. */
+  buffer_init(&channels[old_channels].input);
+  buffer_init(&channels[old_channels].output);
+  channels[old_channels].self = old_channels;
+  channels[old_channels].type = type;
+  channels[old_channels].x11 = 0;
+  channels[old_channels].sock = sock;
+  channels[old_channels].remote_id = -1;
+  channels[old_channels].remote_name = remote_name;
+  chan_init_iostates(&channels[old_channels]);
+  return old_channels;
+}
+
+/* Free the channel and close its socket. */
+
+void channel_free(int channel)
+{
+  assert(channel >= 0 && channel < channels_alloc &&
+	 channels[channel].type != SSH_CHANNEL_FREE);
+  if(compat13)
+    shutdown(channels[channel].sock, SHUT_RDWR);
+  close(channels[channel].sock);
+  buffer_free(&channels[channel].input);
+  buffer_free(&channels[channel].output);
+  channels[channel].type = SSH_CHANNEL_FREE;
+  if (channels[channel].remote_name)
+    {
+      xfree(channels[channel].remote_name);
+      channels[channel].remote_name = NULL;
+    }
+}
+
+/* This is called just before select() to add any bits relevant to
+   channels in the select bitmasks. */
+
+void channel_prepare_select(fd_set *readset, fd_set *writeset)
+{
+  int i;
+  Channel *ch;
+  unsigned char *ucp;
+  unsigned int proto_len, data_len;
+
+  for (i = 0; i < channels_alloc; i++)
+    {
+      ch = &channels[i];
+    redo:
+      switch (ch->type)
+	{
+	case SSH_CHANNEL_X11_LISTENER:
+	case SSH_CHANNEL_PORT_LISTENER:
+	case SSH_CHANNEL_AUTH_SOCKET:
+	  FD_SET(ch->sock, readset);
+	  break;
+
+	case SSH_CHANNEL_OPEN:
+	  if(compat13){
+	    if (buffer_len(&ch->input) < 32768)
+	      FD_SET(ch->sock, readset);
+	    if (buffer_len(&ch->output) > 0)
+	      FD_SET(ch->sock, writeset);
+	    break;
+	  }
+	  /* test whether sockets are 'alive' for read/write */
+          if (ch->istate == CHAN_INPUT_OPEN)
+            if (buffer_len(&ch->input) < 32768)
+              FD_SET(ch->sock, readset);
+          if (ch->ostate == CHAN_OUTPUT_OPEN || ch->ostate == CHAN_OUTPUT_WAIT_DRAIN){
+            if (buffer_len(&ch->output) > 0){
+              FD_SET(ch->sock, writeset);
+            }else if(ch->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
+              chan_obuf_empty(ch);
+            }
+          }
+          break;
+
+	case SSH_CHANNEL_INPUT_DRAINING:
+ 	  if (!compat13)
+	    fatal("cannot happen: IN_DRAIN");
+	  if (buffer_len(&ch->input) == 0)
+	    {
+	      packet_start(SSH_MSG_CHANNEL_CLOSE);
+	      packet_put_int(ch->remote_id);
+	      packet_send();
+	      ch->type = SSH_CHANNEL_CLOSED;
+	      debug("Closing channel %d after input drain.", i);
+	      break;
+	    }
+	  break;
+
+	case SSH_CHANNEL_OUTPUT_DRAINING:
+ 	  if (!compat13)
+	    fatal("cannot happen: OUT_DRAIN");
+	  if (buffer_len(&ch->output) == 0)
+	    {
+	      /* debug("Freeing channel %d after output drain.", i); */
+	      channel_free(i);
+	      break;
+	    }
+	  FD_SET(ch->sock, writeset);
+	  break;
+
+	case SSH_CHANNEL_X11_OPEN:
+	  /* This is a special state for X11 authentication spoofing.  An
+	     opened X11 connection (when authentication spoofing is being
+	     done) remains in this state until the first packet has been
+	     completely read.  The authentication data in that packet is
+	     then substituted by the real data if it matches the fake data,
+	     and the channel is put into normal mode. */
+
+	  /* Check if the fixed size part of the packet is in buffer. */
+	  if (buffer_len(&ch->output) < 12)
+	    break;
+
+	  /* Parse the lengths of variable-length fields. */
+	  ucp = (unsigned char *)buffer_ptr(&ch->output);
+	  if (ucp[0] == 0x42)
+	    { /* Byte order MSB first. */
+	      proto_len = 256 * ucp[6] + ucp[7];
+	      data_len = 256 * ucp[8] + ucp[9];
+	    }
+	  else
+	    if (ucp[0] == 0x6c)
+	      { /* Byte order LSB first. */
+		proto_len = ucp[6] + 256 * ucp[7];
+		data_len = ucp[8] + 256 * ucp[9];
+	      }
+	    else
+	      {
+		debug("Initial X11 packet contains bad byte order byte: 0x%x",
+		      ucp[0]);
+		ch->type = SSH_CHANNEL_OPEN;
+		goto reject;
+	      }
+
+	  /* Check if the whole packet is in buffer. */
+	  if (buffer_len(&ch->output) <
+	      12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3))
+	    break;
+	  
+	  /* Check if authentication protocol matches. */
+	  if (proto_len != strlen(x11_saved_proto) || 
+	      memcmp(ucp + 12, x11_saved_proto, proto_len) != 0)
+	    {
+	      debug("X11 connection uses different authentication protocol.");
+	      ch->type = SSH_CHANNEL_OPEN;
+	      goto reject;
+	    }
+
+	  /* Check if authentication data matches our fake data. */
+	  if (data_len != x11_fake_data_len ||
+	      memcmp(ucp + 12 + ((proto_len + 3) & ~3),
+		     x11_fake_data, x11_fake_data_len) != 0)
+	    {
+	      debug("X11 auth data does not match fake data.");
+	      ch->type = SSH_CHANNEL_OPEN;
+	      goto reject;
+	    }
+
+	  /* Received authentication protocol and data match our fake data.
+	     Substitute the fake data with real data. */
+	  assert(x11_fake_data_len == x11_saved_data_len);
+	  memcpy(ucp + 12 + ((proto_len + 3) & ~3),
+		 x11_saved_data, x11_saved_data_len);
+
+	  /* Start normal processing for the channel. */
+	  ch->type = SSH_CHANNEL_OPEN;
+	  /* Enable X11 Problem FIX */
+	  ch->x11 = 1;
+	  goto redo;
+	  
+	reject:
+	  /* We have received an X11 connection that has bad authentication
+	     information. */
+	  log("X11 connection rejected because of wrong authentication.\r\n");
+	  buffer_clear(&ch->input);
+	  buffer_clear(&ch->output);
+	  if (compat13) {
+            close(ch->sock);
+            ch->sock = -1;
+            ch->type = SSH_CHANNEL_CLOSED;
+            packet_start(SSH_MSG_CHANNEL_CLOSE);
+            packet_put_int(ch->remote_id);
+            packet_send();
+          }else{
+	    debug("X11 rejected %d 0x%x 0x%x", ch->self, ch->istate, ch->ostate);
+	    chan_read_failed(ch);
+	    chan_write_failed(ch);
+	    debug("X11 rejected %d 0x%x 0x%x", ch->self, ch->istate, ch->ostate);
+	  }
+	  break;
+
+	case SSH_CHANNEL_FREE:
+	default:
+	  continue;
+	}
+    }
+}
+
+/* After select, perform any appropriate operations for channels which
+   have events pending. */
+
+void channel_after_select(fd_set *readset, fd_set *writeset)
+{
+  struct sockaddr addr;
+  int addrlen, newsock, i, newch, len;
+  Channel *ch;
+  char buf[16384], *remote_hostname;
+  
+  /* Loop over all channels... */
+  for (i = 0; i < channels_alloc; i++)
+    {
+      ch = &channels[i];
+      switch (ch->type)
+	{
+	case SSH_CHANNEL_X11_LISTENER:
+	  /* This is our fake X11 server socket. */
+	  if (FD_ISSET(ch->sock, readset))
+	    {
+	      debug("X11 connection requested.");
+	      addrlen = sizeof(addr);
+	      newsock = accept(ch->sock, &addr, &addrlen);
+	      if (newsock < 0)
+		{
+		  error("accept: %.100s", strerror(errno));
+		  break;
+		}
+	      remote_hostname = get_remote_hostname(newsock);
+	      snprintf(buf, sizeof buf, "X11 connection from %.200s port %d",
+		      remote_hostname, get_peer_port(newsock));
+	      xfree(remote_hostname);
+	      newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, 
+				       xstrdup(buf));
+	      packet_start(SSH_SMSG_X11_OPEN);
+	      packet_put_int(newch);
+	      if (have_hostname_in_open)
+		packet_put_string(buf, strlen(buf));
+	      packet_send();
+	    }
+	  break;
+	  
+	case SSH_CHANNEL_PORT_LISTENER:
+	  /* This socket is listening for connections to a forwarded TCP/IP
+	     port. */
+	  if (FD_ISSET(ch->sock, readset))
+	    {
+	      debug("Connection to port %d forwarding to %.100s:%d requested.",
+		    ch->listening_port, ch->path, ch->host_port);
+	      addrlen = sizeof(addr);
+	      newsock = accept(ch->sock, &addr, &addrlen);
+	      if (newsock < 0)
+		{
+		  error("accept: %.100s", strerror(errno));
+		  break;
+		}
+	      remote_hostname = get_remote_hostname(newsock);
+	      snprintf(buf, sizeof buf, "port %d, connection from %.200s port %d",
+		      ch->listening_port, remote_hostname,
+		      get_peer_port(newsock));
+	      xfree(remote_hostname);
+	      newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, 
+				       xstrdup(buf));
+	      packet_start(SSH_MSG_PORT_OPEN);
+	      packet_put_int(newch);
+	      packet_put_string(ch->path, strlen(ch->path));
+	      packet_put_int(ch->host_port);
+	      if (have_hostname_in_open)
+		packet_put_string(buf, strlen(buf));
+	      packet_send();
+	    }
+	  break;
+
+	case SSH_CHANNEL_AUTH_SOCKET:
+	  /* This is the authentication agent socket listening for connections
+	     from clients. */
+	  if (FD_ISSET(ch->sock, readset))
+	    {
+	      int nchan;
+	      len = sizeof(addr);
+	      newsock = accept(ch->sock, &addr, &len);
+	      if (newsock < 0)
+		{
+		  error("accept from auth socket: %.100s", strerror(errno));
+		  break;
+		}
+
+	      nchan = channel_allocate(SSH_CHANNEL_OPENING, newsock,
+				     xstrdup("accepted auth socket"));
+	      packet_start(SSH_SMSG_AGENT_OPEN);
+	      packet_put_int(nchan);
+	      packet_send();
+	    }
+	  break;
+
+	case SSH_CHANNEL_OPEN:
+	  /* This is an open two-way communication channel.  It is not of
+	     interest to us at this point what kind of data is being
+	     transmitted. */
+
+	  /* Read available incoming data and append it to buffer;
+	     shutdown socket, if read or write failes */
+	  if (FD_ISSET(ch->sock, readset))
+	    {
+	      len = read(ch->sock, buf, sizeof(buf));
+	      if (len <= 0)
+		{
+		  if (compat13) {
+                    buffer_consume(&ch->output, buffer_len(&ch->output));
+                    ch->type = SSH_CHANNEL_INPUT_DRAINING;
+                    debug("Channel %d status set to input draining.", i);
+                  }else{
+                    chan_read_failed(ch);
+		  }
+		  break;
+		}
+	      buffer_append(&ch->input, buf, len);
+	    }
+	  /* Send buffered output data to the socket. */
+	  if (FD_ISSET(ch->sock, writeset) && buffer_len(&ch->output) > 0)
+	    {
+	      len = write(ch->sock, buffer_ptr(&ch->output),
+			  buffer_len(&ch->output));
+	      if (len <= 0)
+		{
+		  if (compat13) {
+                    buffer_consume(&ch->output, buffer_len(&ch->output));
+                    debug("Channel %d status set to input draining.", i);
+                    ch->type = SSH_CHANNEL_INPUT_DRAINING;
+                  }else{
+                    chan_write_failed(ch);
+		  }
+		  break;
+		}
+	      buffer_consume(&ch->output, len);
+	    }
+	  break;
+
+	case SSH_CHANNEL_OUTPUT_DRAINING:
+ 	  if (!compat13)
+		fatal("cannot happen: OUT_DRAIN");
+	  /* Send buffered output data to the socket. */
+	  if (FD_ISSET(ch->sock, writeset) && buffer_len(&ch->output) > 0)
+	    {
+	      len = write(ch->sock, buffer_ptr(&ch->output),
+			  buffer_len(&ch->output));
+	      if (len <= 0)
+		buffer_consume(&ch->output, buffer_len(&ch->output));
+	      else
+		buffer_consume(&ch->output, len);
+	    }
+	  break;
+
+	case SSH_CHANNEL_X11_OPEN:
+	case SSH_CHANNEL_FREE:
+	default:
+	  continue;
+	}
+    }
+}
+
+/* If there is data to send to the connection, send some of it now. */
+
+void channel_output_poll()
+{
+  int len, i;
+  Channel *ch;
+
+  for (i = 0; i < channels_alloc; i++)
+    {
+      ch = &channels[i];
+      /* We are only interested in channels that can have buffered incoming
+	 data. */
+      if (ch->type != SSH_CHANNEL_OPEN && 
+	  ch->type != SSH_CHANNEL_INPUT_DRAINING)
+	continue;
+
+      /* Get the amount of buffered data for this channel. */
+      len = buffer_len(&ch->input);
+      if (len > 0)
+	{
+	  /* Send some data for the other side over the secure connection. */
+	  if (packet_is_interactive())
+	    {
+	      if (len > 1024)
+		len = 512;
+	    }
+	  else
+	    {
+	      if (len > 16384)
+		len = 16384;  /* Keep the packets at reasonable size. */
+	    }
+	  packet_start(SSH_MSG_CHANNEL_DATA);
+	  packet_put_int(ch->remote_id);
+	  packet_put_string(buffer_ptr(&ch->input), len);
+	  packet_send();
+	  buffer_consume(&ch->input, len);
+	}
+      else if(ch->istate == CHAN_INPUT_WAIT_DRAIN)
+     	{
+ 	  if (compat13)
+	     fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3");
+	  /* input-buffer is empty and read-socket shutdown:
+	     tell peer, that we will not send more data: send IEOF */
+	  chan_ibuf_empty(ch);
+     	}
+    }
+}
+
+/* This is called when a packet of type CHANNEL_DATA has just been received.
+   The message type has already been consumed, but channel number and data
+   is still there. */
+
+void channel_input_data(int payload_len)
+{
+  int channel;
+  char *data;
+  unsigned int data_len;
+
+  /* Get the channel number and verify it. */
+  channel = packet_get_int();
+  if (channel < 0 || channel >= channels_alloc ||
+      channels[channel].type == SSH_CHANNEL_FREE)
+    packet_disconnect("Received data for nonexistent channel %d.", channel);
+
+  /* Ignore any data for non-open channels (might happen on close) */
+  if (channels[channel].type != SSH_CHANNEL_OPEN &&
+      channels[channel].type != SSH_CHANNEL_X11_OPEN)
+    return; 
+
+  /* Get the data. */
+  data = packet_get_string(&data_len);
+  packet_integrity_check(payload_len, 4 + 4+data_len, SSH_MSG_CHANNEL_DATA);
+  buffer_append(&channels[channel].output, data, data_len);
+  xfree(data);
+}
+
+/* Returns true if no channel has too much buffered data, and false if
+   one or more channel is overfull. */
+
+int channel_not_very_much_buffered_data()
+{
+  unsigned int i;
+  Channel *ch;
+  
+  for (i = 0; i < channels_alloc; i++)
+    {
+      ch = &channels[i];
+      switch (ch->type)
+	{
+	case SSH_CHANNEL_X11_LISTENER:
+	case SSH_CHANNEL_PORT_LISTENER:
+	case SSH_CHANNEL_AUTH_SOCKET:
+	  continue;
+	case SSH_CHANNEL_OPEN:
+	  if (buffer_len(&ch->input) > 32768)
+	    return 0;
+	  if (buffer_len(&ch->output) > 32768)
+	    return 0;
+	  continue;
+	case SSH_CHANNEL_INPUT_DRAINING:
+	case SSH_CHANNEL_OUTPUT_DRAINING:
+	case SSH_CHANNEL_X11_OPEN:
+	case SSH_CHANNEL_FREE:
+	default:
+	  continue;
+	}
+    }
+  return 1;
+}
+
+/* This is called after receiving CHANNEL_CLOSE/IEOF. */
+
+void channel_input_close()
+{
+  int channel;
+
+  /* Get the channel number and verify it. */
+  channel = packet_get_int();
+  if (channel < 0 || channel >= channels_alloc ||
+      channels[channel].type == SSH_CHANNEL_FREE)
+    packet_disconnect("Received data for nonexistent channel %d.", channel);
+
+  if(!compat13){
+    /* proto version 1.5 overloads CLOSE with IEOF */
+    chan_rcvd_ieof(&channels[channel]);
+    return;
+  }
+
+  /* Send a confirmation that we have closed the channel and no more data is
+     coming for it. */
+  packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION);
+  packet_put_int(channels[channel].remote_id);
+  packet_send();
+
+  /* If the channel is in closed state, we have sent a close request, and
+     the other side will eventually respond with a confirmation.  Thus,
+     we cannot free the channel here, because then there would be no-one to
+     receive the confirmation.  The channel gets freed when the confirmation
+     arrives. */
+  if (channels[channel].type != SSH_CHANNEL_CLOSED)
+    {
+      /* Not a closed channel - mark it as draining, which will cause it to
+	 be freed later. */
+      buffer_consume(&channels[channel].input, 
+		     buffer_len(&channels[channel].input));
+      channels[channel].type = SSH_CHANNEL_OUTPUT_DRAINING;
+      /* debug("Setting status to output draining; output len = %d",
+	 buffer_len(&channels[channel].output)); */
+    }
+}
+
+/* This is called after receiving CHANNEL_CLOSE_CONFIRMATION/OCLOSE. */
+
+void channel_input_close_confirmation()
+{
+  int channel;
+
+  /* Get the channel number and verify it. */
+  channel = packet_get_int();
+  if (channel < 0 || channel >= channels_alloc)
+    packet_disconnect("Received close confirmation for out-of-range channel %d.",
+		      channel);
+
+  if(!compat13){
+    /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */
+    chan_rcvd_oclose(&channels[channel]);
+    return;
+  }
+
+  if (channels[channel].type != SSH_CHANNEL_CLOSED)
+    packet_disconnect("Received close confirmation for non-closed channel %d (type %d).",
+		      channel, channels[channel].type);
+
+  /* Free the channel. */
+  channel_free(channel);
+}
+
+/* This is called after receiving CHANNEL_OPEN_CONFIRMATION. */
+
+void channel_input_open_confirmation()
+{
+  int channel, remote_channel;
+
+  /* Get the channel number and verify it. */
+  channel = packet_get_int();
+  if (channel < 0 || channel >= channels_alloc ||
+      channels[channel].type != SSH_CHANNEL_OPENING)
+    packet_disconnect("Received open confirmation for non-opening channel %d.",
+		      channel);
+
+  /* Get remote side's id for this channel. */
+  remote_channel = packet_get_int();
+
+  /* Record the remote channel number and mark that the channel is now open. */
+  channels[channel].remote_id = remote_channel;
+  channels[channel].type = SSH_CHANNEL_OPEN;
+}
+
+/* This is called after receiving CHANNEL_OPEN_FAILURE from the other side. */
+
+void channel_input_open_failure()
+{
+  int channel;
+
+  /* Get the channel number and verify it. */
+  channel = packet_get_int();
+  if (channel < 0 || channel >= channels_alloc ||
+      channels[channel].type != SSH_CHANNEL_OPENING)
+    packet_disconnect("Received open failure for non-opening channel %d.",
+		      channel);
+  
+  /* Free the channel.  This will also close the socket. */
+  channel_free(channel);
+}
+
+/* Stops listening for channels, and removes any unix domain sockets that
+   we might have. */
+
+void channel_stop_listening()
+{
+  int i;
+  for (i = 0; i < channels_alloc; i++)
+    {
+      switch (channels[i].type)
+	{
+	case SSH_CHANNEL_AUTH_SOCKET:
+	  close(channels[i].sock);
+	  remove(channels[i].path);
+	  channel_free(i);
+	  break;
+	case SSH_CHANNEL_PORT_LISTENER:
+	case SSH_CHANNEL_X11_LISTENER:
+	  close(channels[i].sock);
+	  channel_free(i);
+	  break;
+	default:
+	  break;
+	}
+    }
+}
+
+/* Closes the sockets of all channels.  This is used to close extra file
+   descriptors after a fork. */
+
+void channel_close_all()
+{
+  int i;
+  for (i = 0; i < channels_alloc; i++)
+    {
+      if (channels[i].type != SSH_CHANNEL_FREE)
+	close(channels[i].sock);
+    }
+}
+
+/* Returns the maximum file descriptor number used by the channels. */
+
+int channel_max_fd()
+{
+  return channel_max_fd_value;
+}
+
+/* Returns true if any channel is still open. */
+
+int channel_still_open()
+{
+  unsigned int i;
+  for (i = 0; i < channels_alloc; i++)
+    switch (channels[i].type)
+      {
+      case SSH_CHANNEL_FREE:
+      case SSH_CHANNEL_X11_LISTENER:
+      case SSH_CHANNEL_PORT_LISTENER:
+      case SSH_CHANNEL_CLOSED:
+      case SSH_CHANNEL_AUTH_SOCKET:
+	continue;
+      case SSH_CHANNEL_OPENING:
+      case SSH_CHANNEL_OPEN:
+      case SSH_CHANNEL_X11_OPEN:
+	return 1;
+      case SSH_CHANNEL_INPUT_DRAINING:
+      case SSH_CHANNEL_OUTPUT_DRAINING:
+ 	if (!compat13)
+	  fatal("cannot happen: OUT_DRAIN");
+	return 1;
+      default:
+	fatal("channel_still_open: bad channel type %d", channels[i].type);
+	/*NOTREACHED*/
+      }
+  return 0;
+}
+
+/* Returns a message describing the currently open forwarded
+   connections, suitable for sending to the client.  The message
+   contains crlf pairs for newlines. */
+
+char *channel_open_message()
+{
+  Buffer buffer;
+  int i;
+  char buf[512], *cp;
+
+  buffer_init(&buffer);
+  snprintf(buf, sizeof buf, "The following connections are open:\r\n");
+  buffer_append(&buffer, buf, strlen(buf));
+  for (i = 0; i < channels_alloc; i++){
+    Channel *c=&channels[i];
+    switch (c->type)
+      {
+      case SSH_CHANNEL_FREE:
+      case SSH_CHANNEL_X11_LISTENER:
+      case SSH_CHANNEL_PORT_LISTENER:
+      case SSH_CHANNEL_CLOSED:
+      case SSH_CHANNEL_AUTH_SOCKET:
+	continue;
+      case SSH_CHANNEL_OPENING:
+      case SSH_CHANNEL_OPEN:
+      case SSH_CHANNEL_X11_OPEN:
+      case SSH_CHANNEL_INPUT_DRAINING:
+      case SSH_CHANNEL_OUTPUT_DRAINING:
+	snprintf(buf, sizeof buf, "  #%d/%d %.300s\r\n",
+		 c->self,c->type,c->remote_name);
+	buffer_append(&buffer, buf, strlen(buf));
+	continue;
+      default:
+	fatal("channel_still_open: bad channel type %d", c->type);
+	/*NOTREACHED*/
+      }
+  }
+  buffer_append(&buffer, "\0", 1);
+  cp = xstrdup(buffer_ptr(&buffer));
+  buffer_free(&buffer);
+  return cp;
+}
+
+/* Initiate forwarding of connections to local port "port" through the secure
+   channel to host:port from remote side. */
+
+void channel_request_local_forwarding(int port, const char *host,
+				      int host_port)
+{
+  int ch, sock;
+  struct sockaddr_in sin;
+  extern Options options;
+
+  if (strlen(host) > sizeof(channels[0].path) - 1)
+    packet_disconnect("Forward host name too long.");
+  
+  /* Create a port to listen for the host. */
+  sock = socket(AF_INET, SOCK_STREAM, 0);
+  if (sock < 0)
+    packet_disconnect("socket: %.100s", strerror(errno));
+
+  /* Initialize socket address. */
+  memset(&sin, 0, sizeof(sin));
+  sin.sin_family = AF_INET;
+  if (options.gateway_ports == 1)
+    sin.sin_addr.s_addr = htonl(INADDR_ANY);
+  else
+    sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+  sin.sin_port = htons(port);
+  
+  /* Bind the socket to the address. */
+  if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+    packet_disconnect("bind: %.100s", strerror(errno));
+      
+  /* Start listening for connections on the socket. */
+  if (listen(sock, 5) < 0)
+    packet_disconnect("listen: %.100s", strerror(errno));
+	    
+  /* Allocate a channel number for the socket. */
+  ch = channel_allocate(SSH_CHANNEL_PORT_LISTENER, sock,
+			xstrdup("port listener"));
+  strcpy(channels[ch].path, host); /* note: host name stored here */
+  channels[ch].host_port = host_port; /* port on host to connect to */
+  channels[ch].listening_port = port; /* port being listened */
+}  
+
+/* Initiate forwarding of connections to port "port" on remote host through
+   the secure channel to host:port from local side. */
+
+void channel_request_remote_forwarding(int port, const char *host,
+				       int remote_port)
+{
+  int payload_len;
+  /* Record locally that connection to this host/port is permitted. */
+  if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)
+    fatal("channel_request_remote_forwarding: too many forwards");
+  permitted_opens[num_permitted_opens].host = xstrdup(host);
+  permitted_opens[num_permitted_opens].port = remote_port;
+  num_permitted_opens++;
+
+  /* Send the forward request to the remote side. */
+  packet_start(SSH_CMSG_PORT_FORWARD_REQUEST);
+  packet_put_int(port);
+  packet_put_string(host, strlen(host));
+  packet_put_int(remote_port);
+  packet_send();
+  packet_write_wait();
+  
+  /* Wait for response from the remote side.  It will send a disconnect
+     message on failure, and we will never see it here. */
+  packet_read_expect(&payload_len, SSH_SMSG_SUCCESS);
+}
+
+/* This is called after receiving CHANNEL_FORWARDING_REQUEST.  This initates
+   listening for the port, and sends back a success reply (or disconnect
+   message if there was an error).  This never returns if there was an 
+   error. */
+
+void channel_input_port_forward_request(int is_root)
+{
+  int port, host_port;
+  char *hostname;
+  
+  /* Get arguments from the packet. */
+  port = packet_get_int();
+  hostname = packet_get_string(NULL);
+  host_port = packet_get_int();
+  
+  /* Port numbers are 16 bit quantities. */
+  if ((port & 0xffff) != port)
+    packet_disconnect("Requested forwarding of nonexistent port %d.", port);
+
+  /* Check that an unprivileged user is not trying to forward a privileged
+     port. */
+  if (port < IPPORT_RESERVED && !is_root)
+    packet_disconnect("Requested forwarding of port %d but user is not root.",
+		      port);
+
+  /* Initiate forwarding. */
+  channel_request_local_forwarding(port, hostname, host_port);
+
+  /* Free the argument string. */
+  xfree(hostname);
+}
+
+/* This is called after receiving PORT_OPEN message.  This attempts to connect
+   to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION or
+   CHANNEL_OPEN_FAILURE. */
+
+void channel_input_port_open(int payload_len)
+{
+  int remote_channel, sock, newch, host_port, i;
+  struct sockaddr_in sin;
+  char *host, *originator_string;
+  struct hostent *hp;
+  int host_len, originator_len;
+
+  /* Get remote channel number. */
+  remote_channel = packet_get_int();
+
+  /* Get host name to connect to. */
+  host = packet_get_string(&host_len);
+
+  /* Get port to connect to. */
+  host_port = packet_get_int();
+
+  /* Get remote originator name. */
+  if (have_hostname_in_open)
+    originator_string = packet_get_string(&originator_len);
+  else
+    originator_string = xstrdup("unknown (remote did not supply name)");
+
+  packet_integrity_check(payload_len,
+			 4 + 4 + host_len + 4 + 4 + originator_len,
+			 SSH_MSG_PORT_OPEN);
+
+  /* Check if opening that port is permitted. */
+  if (!all_opens_permitted)
+    {
+      /* Go trough all permitted ports. */
+      for (i = 0; i < num_permitted_opens; i++)
+	if (permitted_opens[i].port == host_port &&
+	    strcmp(permitted_opens[i].host, host) == 0)
+	  break;
+
+      /* Check if we found the requested port among those permitted. */
+      if (i >= num_permitted_opens)
+	{
+	  /* The port is not permitted. */
+	  log("Received request to connect to %.100s:%d, but the request was denied.",
+	      host, host_port);
+	  packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
+	  packet_put_int(remote_channel);
+	  packet_send();
+	}
+    }
+  
+  memset(&sin, 0, sizeof(sin));
+  sin.sin_addr.s_addr = inet_addr(host);
+  if ((sin.sin_addr.s_addr & 0xffffffff) != 0xffffffff)
+    {
+      /* It was a valid numeric host address. */
+      sin.sin_family = AF_INET;
+    }
+  else
+    {
+      /* Look up the host address from the name servers. */
+      hp = gethostbyname(host);
+      if (!hp)
+	{
+	  error("%.100s: unknown host.", host);
+	  goto fail;
+	}
+      if (!hp->h_addr_list[0])
+	{
+	  error("%.100s: host has no IP address.", host);
+	  goto fail;
+	}
+      sin.sin_family = hp->h_addrtype;
+      memcpy(&sin.sin_addr, hp->h_addr_list[0], 
+	     sizeof(sin.sin_addr));
+    }
+  sin.sin_port = htons(host_port);
+
+  /* Create the socket. */
+  sock = socket(sin.sin_family, SOCK_STREAM, 0);
+  if (sock < 0)
+    {
+      error("socket: %.100s", strerror(errno));
+      goto fail;
+    }
+
+  /* Connect to the host/port. */
+  if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+    {
+      error("connect %.100s:%d: %.100s", host, host_port,
+	    strerror(errno));
+      close(sock);
+      goto fail;
+    }
+
+  /* Successful connection. */
+
+  /* Allocate a channel for this connection. */
+  newch = channel_allocate(SSH_CHANNEL_OPEN, sock, originator_string);
+  channels[newch].remote_id = remote_channel;
+  
+  /* Send a confirmation to the remote host. */
+  packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
+  packet_put_int(remote_channel);
+  packet_put_int(newch);
+  packet_send();
+
+  /* Free the argument string. */
+  xfree(host);
+  
+  return;
+
+ fail:
+  /* Free the argument string. */
+  xfree(host);
+
+  /* Send refusal to the remote host. */
+  packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
+  packet_put_int(remote_channel);
+  packet_send();
+}
+
+/* Creates an internet domain socket for listening for X11 connections. 
+   Returns a suitable value for the DISPLAY variable, or NULL if an error
+   occurs. */
+
+char *x11_create_display_inet(int screen_number)
+{
+  extern ServerOptions options;
+  int display_number, port, sock;
+  struct sockaddr_in sin;
+  char buf[512];
+  char hostname[MAXHOSTNAMELEN];
+
+  for (display_number = options.x11_display_offset; display_number < MAX_DISPLAYS; display_number++)
+    {
+      port = 6000 + display_number;
+      memset(&sin, 0, sizeof(sin));
+      sin.sin_family = AF_INET;
+      sin.sin_addr.s_addr = htonl(INADDR_ANY);
+      sin.sin_port = htons(port);
+      
+      sock = socket(AF_INET, SOCK_STREAM, 0);
+      if (sock < 0)
+	{
+	  error("socket: %.100s", strerror(errno));
+	  return NULL;
+	}
+      
+      if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+	{
+	  debug("bind port %d: %.100s", port, strerror(errno));
+	  shutdown(sock, SHUT_RDWR);
+	  close(sock);
+	  continue;
+	}
+      break;
+    }
+  if (display_number >= MAX_DISPLAYS)
+    {
+      error("Failed to allocate internet-domain X11 display socket.");
+      return NULL;
+    }
+
+  /* Start listening for connections on the socket. */
+  if (listen(sock, 5) < 0)
+    {
+      error("listen: %.100s", strerror(errno));
+      shutdown(sock, SHUT_RDWR);
+      close(sock);
+      return NULL;
+    }
+
+  /* Set up a suitable value for the DISPLAY variable. */
+  if (gethostname(hostname, sizeof(hostname)) < 0)
+    fatal("gethostname: %.100s", strerror(errno));
+  snprintf(buf, sizeof buf, "%.400s:%d.%d", hostname,
+    display_number, screen_number);
+	    
+  /* Allocate a channel for the socket. */
+  (void)channel_allocate(SSH_CHANNEL_X11_LISTENER, sock,
+			 xstrdup("X11 inet listener"));
+
+  /* Return a suitable value for the DISPLAY environment variable. */
+  return xstrdup(buf);
+}
+
+#ifndef X_UNIX_PATH
+#define X_UNIX_PATH "/tmp/.X11-unix/X"
+#endif
+
+static
+int
+connect_local_xsocket(unsigned dnr)
+{
+  static const char *const x_sockets[] = {
+    X_UNIX_PATH "%u",
+    "/var/X/.X11-unix/X" "%u",
+    "/usr/spool/sockets/X11/" "%u",
+    NULL
+  };
+  int sock;
+  struct sockaddr_un addr;
+  const char *const *path;
+
+  for (path = x_sockets; *path; ++path)
+    {
+      sock = socket(AF_UNIX, SOCK_STREAM, 0);
+      if (sock < 0)
+	error("socket: %.100s", strerror(errno));
+      memset(&addr, 0, sizeof(addr));
+      addr.sun_family = AF_UNIX;
+      snprintf(addr.sun_path, sizeof addr.sun_path, *path, dnr);
+      if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0)
+	return sock;
+      close(sock);
+    }
+  error("connect %.100s: %.100s", addr.sun_path, strerror(errno));
+  return -1;
+}
+
+
+/* This is called when SSH_SMSG_X11_OPEN is received.  The packet contains
+   the remote channel number.  We should do whatever we want, and respond
+   with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. */
+
+void x11_input_open(int payload_len)
+{
+  int remote_channel, display_number, sock, newch;
+  const char *display;
+  struct sockaddr_in sin;
+  char buf[1024], *cp, *remote_host;
+  struct hostent *hp;
+  int remote_len;
+
+  /* Get remote channel number. */
+  remote_channel = packet_get_int();
+
+  /* Get remote originator name. */
+  if (have_hostname_in_open)
+    remote_host = packet_get_string(&remote_len);
+  else
+    remote_host = xstrdup("unknown (remote did not supply name)");
+
+  debug("Received X11 open request.");
+  packet_integrity_check(payload_len, 4 + 4+remote_len, SSH_SMSG_X11_OPEN);
+
+  /* Try to open a socket for the local X server. */
+  display = getenv("DISPLAY");
+  if (!display)
+    {
+      error("DISPLAY not set.");
+      goto fail;
+    }
+  
+  /* Now we decode the value of the DISPLAY variable and make a connection
+     to the real X server. */
+
+  /* Check if it is a unix domain socket.  Unix domain displays are in one
+     of the following formats: unix:d[.s], :d[.s], ::d[.s] */
+  if (strncmp(display, "unix:", 5) == 0 ||
+      display[0] == ':')
+    {
+      /* Connect to the unix domain socket. */
+      if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1)
+	{
+	  error("Could not parse display number from DISPLAY: %.100s",
+		display);
+	  goto fail;
+	}
+      /* Create a socket. */
+      sock = connect_local_xsocket(display_number);
+      if (sock < 0)
+	goto fail;
+
+      /* OK, we now have a connection to the display. */
+      goto success;
+    }
+  
+  /* Connect to an inet socket.  The DISPLAY value is supposedly
+      hostname:d[.s], where hostname may also be numeric IP address. */
+  strncpy(buf, display, sizeof(buf));
+  buf[sizeof(buf) - 1] = 0;
+  cp = strchr(buf, ':');
+  if (!cp)
+    {
+      error("Could not find ':' in DISPLAY: %.100s", display);
+      goto fail;
+    }
+  *cp = 0;
+  /* buf now contains the host name.  But first we parse the display number. */
+  if (sscanf(cp + 1, "%d", &display_number) != 1)
+    {
+       error("Could not parse display number from DISPLAY: %.100s",
+	     display);
+      goto fail;
+    }
+  
+  /* Try to parse the host name as a numeric IP address. */
+  memset(&sin, 0, sizeof(sin));
+  sin.sin_addr.s_addr = inet_addr(buf);
+  if ((sin.sin_addr.s_addr & 0xffffffff) != 0xffffffff)
+    {
+      /* It was a valid numeric host address. */
+      sin.sin_family = AF_INET;
+    }
+  else
+    {
+      /* Not a numeric IP address. */
+      /* Look up the host address from the name servers. */
+      hp = gethostbyname(buf);
+      if (!hp)
+	{
+	  error("%.100s: unknown host.", buf);
+	  goto fail;
+	}
+      if (!hp->h_addr_list[0])
+	{
+	  error("%.100s: host has no IP address.", buf);
+	  goto fail;
+	}
+      sin.sin_family = hp->h_addrtype;
+      memcpy(&sin.sin_addr, hp->h_addr_list[0], 
+	     sizeof(sin.sin_addr));
+    }
+  /* Set port number. */
+  sin.sin_port = htons(6000 + display_number);
+
+  /* Create a socket. */
+  sock = socket(sin.sin_family, SOCK_STREAM, 0);
+  if (sock < 0)
+    {
+      error("socket: %.100s", strerror(errno));
+      goto fail;
+    }
+  /* Connect it to the display. */
+  if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+    {
+      error("connect %.100s:%d: %.100s", buf, 6000 + display_number, 
+	    strerror(errno));
+      close(sock);
+      goto fail;
+    }
+
+ success:
+  /* We have successfully obtained a connection to the real X display. */
+  
+  /* Allocate a channel for this connection. */
+  if (x11_saved_proto == NULL)
+    newch = channel_allocate(SSH_CHANNEL_OPEN, sock, remote_host);
+  else
+    newch = channel_allocate(SSH_CHANNEL_X11_OPEN, sock, remote_host);
+  channels[newch].remote_id = remote_channel;
+  
+  /* Send a confirmation to the remote host. */
+  packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
+  packet_put_int(remote_channel);
+  packet_put_int(newch);
+  packet_send();
+  
+  return;
+
+ fail:
+  /* Send refusal to the remote host. */
+  packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
+  packet_put_int(remote_channel);
+  packet_send();
+}
+
+/* Requests forwarding of X11 connections, generates fake authentication
+   data, and enables authentication spoofing. */
+
+void x11_request_forwarding_with_spoofing(const char *proto, const char *data)
+{
+  unsigned int data_len = (unsigned int)strlen(data) / 2;
+  unsigned int i, value;
+  char *new_data;
+  int screen_number;
+  const char *cp;
+  u_int32_t rand = 0;
+
+  cp = getenv("DISPLAY");
+  if (cp)
+    cp = strchr(cp, ':');
+  if (cp)
+    cp = strchr(cp, '.');
+  if (cp)
+    screen_number = atoi(cp + 1);
+  else
+    screen_number = 0;
+
+  /* Save protocol name. */
+  x11_saved_proto = xstrdup(proto);
+
+  /* Extract real authentication data and generate fake data of the same
+     length. */
+  x11_saved_data = xmalloc(data_len);
+  x11_fake_data = xmalloc(data_len);
+  for (i = 0; i < data_len; i++)
+    {
+      if (sscanf(data + 2 * i, "%2x", &value) != 1)
+	fatal("x11_request_forwarding: bad authentication data: %.100s", data);
+      if (i % 4 == 0)
+	rand = arc4random();
+      x11_saved_data[i] = value;
+      x11_fake_data[i] = rand & 0xff;
+      rand >>= 8;
+    }
+  x11_saved_data_len = data_len;
+  x11_fake_data_len = data_len;
+
+  /* Convert the fake data into hex. */
+  new_data = xmalloc(2 * data_len + 1);
+  for (i = 0; i < data_len; i++)
+    sprintf(new_data + 2 * i, "%02x", (unsigned char)x11_fake_data[i]);
+
+  /* Send the request packet. */
+  packet_start(SSH_CMSG_X11_REQUEST_FORWARDING);
+  packet_put_string(proto, strlen(proto));
+  packet_put_string(new_data, strlen(new_data));
+  packet_put_int(screen_number);
+  packet_send();
+  packet_write_wait();
+  xfree(new_data);
+}
+
+/* Sends a message to the server to request authentication fd forwarding. */
+
+void auth_request_forwarding()
+{
+  packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING);
+  packet_send();
+  packet_write_wait();
+}
+
+/* Returns the name of the forwarded authentication socket.  Returns NULL
+   if there is no forwarded authentication socket.  The returned value
+   points to a static buffer. */
+
+char *auth_get_socket_name()
+{
+  return channel_forwarded_auth_socket_name;
+}
+
+/* removes the agent forwarding socket */
+
+void cleanup_socket(void) {
+  remove(channel_forwarded_auth_socket_name);
+  rmdir(channel_forwarded_auth_socket_dir);
+}
+
+/* This if called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server.
+   This starts forwarding authentication requests. */
+
+void auth_input_request_forwarding(struct passwd *pw)
+{
+  int sock, newch;
+  struct sockaddr_un sunaddr;
+  
+  if (auth_get_socket_name() != NULL)
+    fatal("Protocol error: authentication forwarding requested twice.");
+
+  /* Temporarily drop privileged uid for mkdir/bind. */
+  temporarily_use_uid(pw->pw_uid);
+
+  /* Allocate a buffer for the socket name, and format the name. */
+  channel_forwarded_auth_socket_name = xmalloc(MAX_SOCKET_NAME);
+  channel_forwarded_auth_socket_dir  = xmalloc(MAX_SOCKET_NAME);
+  strlcpy(channel_forwarded_auth_socket_dir, "/tmp/ssh-XXXXXXXX", MAX_SOCKET_NAME);
+
+  /* Create private directory for socket */
+  if (mkdtemp(channel_forwarded_auth_socket_dir) == NULL)
+    packet_disconnect("mkdtemp: %.100s", strerror(errno));
+  snprintf(channel_forwarded_auth_socket_name, MAX_SOCKET_NAME,
+	   "%s/agent.%d", channel_forwarded_auth_socket_dir, (int)getpid());
+
+  if (atexit(cleanup_socket) < 0) {
+    int saved=errno;
+    cleanup_socket();
+    packet_disconnect("socket: %.100s", strerror(saved));
+  }
+
+  /* Create the socket. */
+  sock = socket(AF_UNIX, SOCK_STREAM, 0);
+  if (sock < 0)
+    packet_disconnect("socket: %.100s", strerror(errno));
+
+  /* Bind it to the name. */
+  memset(&sunaddr, 0, sizeof(sunaddr));
+  sunaddr.sun_family = AF_UNIX;
+  strncpy(sunaddr.sun_path, channel_forwarded_auth_socket_name, 
+          sizeof(sunaddr.sun_path));
+
+  if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0)
+    packet_disconnect("bind: %.100s", strerror(errno));
+
+  /* Restore the privileged uid. */
+  restore_uid();
+
+  /* Start listening on the socket. */
+  if (listen(sock, 5) < 0)
+    packet_disconnect("listen: %.100s", strerror(errno));
+
+  /* Allocate a channel for the authentication agent socket. */
+  newch = channel_allocate(SSH_CHANNEL_AUTH_SOCKET, sock,
+    		       xstrdup("auth socket"));
+  strcpy(channels[newch].path, channel_forwarded_auth_socket_name);
+}
+
+/* This is called to process an SSH_SMSG_AGENT_OPEN message. */
+
+void auth_input_open_request()
+{
+  int remch, sock, newch;
+  char *dummyname;
+
+  /* Read the remote channel number from the message. */
+  remch = packet_get_int();
+  
+  /* Get a connection to the local authentication agent (this may again get
+     forwarded). */
+  sock = ssh_get_authentication_socket();
+
+  /* If we could not connect the agent, send an error message back to
+     the server. This should never happen unless the agent
+     dies, because authentication forwarding is only enabled if we have an
+     agent. */
+  if (sock < 0){
+    packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
+    packet_put_int(remch);
+    packet_send();
+    return;
+  }
+
+  debug("Forwarding authentication connection.");
+
+  /* Dummy host name.  This will be freed when the channel is freed; it will
+     still be valid in the packet_put_string below since the channel cannot
+     yet be freed at that point. */
+  dummyname = xstrdup("authentication agent connection");
+  
+  newch = channel_allocate(SSH_CHANNEL_OPEN, sock, dummyname);
+  channels[newch].remote_id = remch;
+  
+  /* Send a confirmation to the remote host. */
+  packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
+  packet_put_int(remch);
+  packet_put_int(newch);
+  packet_send();
+}
diff --git a/channels.h b/channels.h
new file mode 100644
index 0000000..9794ef5
--- /dev/null
+++ b/channels.h
@@ -0,0 +1,41 @@
+/* RCSID("$Id: channels.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */
+
+#ifndef CHANNELS_H
+#define CHANNELS_H
+
+/* Definitions for channel types. */
+#define SSH_CHANNEL_FREE		0 /* This channel is free (unused). */
+#define SSH_CHANNEL_X11_LISTENER	1 /* Listening for inet X11 conn. */
+#define SSH_CHANNEL_PORT_LISTENER	2 /* Listening on a port. */
+#define SSH_CHANNEL_OPENING		3 /* waiting for confirmation */
+#define SSH_CHANNEL_OPEN		4 /* normal open two-way channel */
+#define SSH_CHANNEL_CLOSED		5 /* waiting for close confirmation */
+/*	SSH_CHANNEL_AUTH_FD		6    authentication fd */
+#define SSH_CHANNEL_AUTH_SOCKET		7 /* authentication socket */
+/*	SSH_CHANNEL_AUTH_SOCKET_FD	8    connection to auth socket */
+#define SSH_CHANNEL_X11_OPEN		9 /* reading first X11 packet */
+#define SSH_CHANNEL_INPUT_DRAINING	10 /* sending remaining data to conn */
+#define SSH_CHANNEL_OUTPUT_DRAINING	11 /* sending remaining data to app */
+
+/* Data structure for channel data.  This is iniailized in channel_allocate
+   and cleared in channel_free. */
+
+typedef struct Channel
+{
+  int type;		/* channel type/state */
+  int self;		/* my own channel identifier */
+  int remote_id;	/* channel identifier for remote peer */
+			/* peer can be reached over encrypted connection, via packet-sent */
+  int istate;
+  int ostate;
+  int x11;
+  int sock;		/* data socket, linked to this channel */
+  Buffer input;		/* data read from socket, to be sent over encrypted connection */
+  Buffer output;	/* data received over encrypted connection for send on socket */
+  char path[200];	/* path for unix domain sockets, or host name for forwards */
+  int listening_port;	/* port being listened for forwards */
+  int host_port;	/* remote port to connect for forwards */
+  char *remote_name;	/* remote hostname */
+} Channel;
+
+#endif
diff --git a/cipher.c b/cipher.c
new file mode 100644
index 0000000..b47e7ec
--- /dev/null
+++ b/cipher.c
@@ -0,0 +1,304 @@
+/*
+
+cipher.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Wed Apr 19 17:41:39 1995 ylo
+
+*/
+
+#include "includes.h"
+RCSID("$Id: cipher.c,v 1.1 1999/10/27 03:42:44 damien Exp $");
+
+#include "ssh.h"
+#include "cipher.h"
+
+#include <openssl/md5.h>
+
+/*
+ * What kind of tripple DES are these 2 routines?
+ *
+ * Why is there a redundant initialization vector?
+ *
+ * If only iv3 was used, then, this would till effect have been
+ * outer-cbc. However, there is also a private iv1 == iv2 which
+ * perhaps makes differential analysis easier. On the other hand, the
+ * private iv1 probably makes the CRC-32 attack ineffective. This is a
+ * result of that there is no longer any known iv1 to use when
+ * choosing the X block.
+ */
+void
+SSH_3CBC_ENCRYPT(des_key_schedule ks1,
+		 des_key_schedule ks2, des_cblock *iv2,
+		 des_key_schedule ks3, des_cblock *iv3, 
+		 void *dest, void *src,
+		 unsigned int len)
+{
+  des_cblock iv1;
+
+  memcpy(&iv1, iv2, 8);
+
+  des_cbc_encrypt(src, dest, len, ks1, &iv1, DES_ENCRYPT);
+  memcpy(&iv1, dest + len - 8, 8);
+
+  des_cbc_encrypt(dest, dest, len, ks2, iv2, DES_DECRYPT);
+  memcpy(iv2, &iv1, 8);		/* Note how iv1 == iv2 on entry and exit. */
+
+  des_cbc_encrypt(dest, dest, len, ks3, iv3, DES_ENCRYPT);
+  memcpy(iv3, dest + len - 8, 8);
+}
+
+void
+SSH_3CBC_DECRYPT(des_key_schedule ks1,
+		 des_key_schedule ks2, des_cblock *iv2,
+		 des_key_schedule ks3, des_cblock *iv3,
+		 void *dest, void *src,
+		 unsigned int len)
+{
+  des_cblock iv1;
+
+  memcpy(&iv1, iv2, 8);
+
+  des_cbc_encrypt(src, dest, len, ks3, iv3, DES_DECRYPT);
+  memcpy(iv3, src + len - 8, 8);
+
+  des_cbc_encrypt(dest, dest, len, ks2, iv2, DES_ENCRYPT);
+  memcpy(iv2, dest + len - 8, 8);
+
+  des_cbc_encrypt(dest, dest, len, ks1, &iv1, DES_DECRYPT);
+  /* memcpy(&iv1, iv2, 8); */	/* Note how iv1 == iv2 on entry and exit. */
+}
+
+/*
+ * SSH uses a variation on Blowfish, all bytes must be swapped before
+ * and after encryption/decryption. Thus the swap_bytes stuff (yuk).
+ */
+static
+void
+swap_bytes(const unsigned char *src, unsigned char *dst_, int n)
+{
+  u_int32_t *dst = (u_int32_t *)dst_;	/* dst must be properly aligned. */
+  union {
+    u_int32_t i;
+    char c[4];
+  } t;
+
+  /* assert((n & 7) == 0); */
+
+  /* Process 8 bytes every lap. */
+  for (n = n / 8; n > 0; n--)
+    {
+      t.c[3] = *src++;
+      t.c[2] = *src++;
+      t.c[1] = *src++;
+      t.c[0] = *src++;
+      *dst++ = t.i;
+      
+      t.c[3] = *src++;
+      t.c[2] = *src++;
+      t.c[1] = *src++;
+      t.c[0] = *src++;
+      *dst++ = t.i;
+    }
+}
+
+void (*cipher_attack_detected)(const char *fmt, ...) = fatal;
+
+static inline
+void
+detect_cbc_attack(const unsigned char *src,
+		  unsigned int len)
+{
+  return;
+  
+  log("CRC-32 CBC insertion attack detected");
+  cipher_attack_detected("CRC-32 CBC insertion attack detected");
+}
+
+/* Names of all encryption algorithms.  These must match the numbers defined
+   int cipher.h. */
+static char *cipher_names[] =
+{
+  "none",
+  "idea",
+  "des",
+  "3des",
+  "tss",
+  "rc4",
+  "blowfish"
+};
+
+/* Returns a bit mask indicating which ciphers are supported by this
+   implementation.  The bit mask has the corresponding bit set of each
+   supported cipher. */
+
+unsigned int cipher_mask()
+{
+  unsigned int mask = 0;
+  mask |= 1 << SSH_CIPHER_3DES;	/* Mandatory */
+  mask |= 1 << SSH_CIPHER_BLOWFISH;
+  return mask;
+}
+
+/* Returns the name of the cipher. */
+
+const
+char *cipher_name(int cipher)
+{
+  if (cipher < 0 || cipher >= sizeof(cipher_names) / sizeof(cipher_names[0]) ||
+	cipher_names[cipher] == NULL)
+    fatal("cipher_name: bad cipher number: %d", cipher);
+  return cipher_names[cipher];
+}
+
+/* Parses the name of the cipher.  Returns the number of the corresponding
+   cipher, or -1 on error. */
+
+int
+cipher_number(const char *name)
+{
+  int i;
+  for (i = 0; i < sizeof(cipher_names) / sizeof(cipher_names[0]); i++)
+    if (strcmp(cipher_names[i], name) == 0 &&
+	(cipher_mask() & (1 << i)))
+      return i;
+  return -1;
+}
+
+/* Selects the cipher, and keys if by computing the MD5 checksum of the
+   passphrase and using the resulting 16 bytes as the key. */
+
+void cipher_set_key_string(CipherContext *context, int cipher,
+			   const char *passphrase, int for_encryption)
+{
+  MD5_CTX md;
+  unsigned char digest[16];
+  
+  MD5_Init(&md);
+  MD5_Update(&md, (const unsigned char *)passphrase, strlen(passphrase));
+  MD5_Final(digest, &md);
+
+  cipher_set_key(context, cipher, digest, 16, for_encryption);
+  
+  memset(digest, 0, sizeof(digest));
+  memset(&md, 0, sizeof(md));
+}
+
+/* Selects the cipher to use and sets the key. */
+
+void cipher_set_key(CipherContext *context, int cipher,
+		    const unsigned char *key, int keylen, int for_encryption)
+{
+  unsigned char padded[32];
+
+  /* Set cipher type. */
+  context->type = cipher;
+
+  /* Get 32 bytes of key data.  Pad if necessary.  (So that code below does
+     not need to worry about key size). */
+  memset(padded, 0, sizeof(padded));
+  memcpy(padded, key, keylen < sizeof(padded) ? keylen : sizeof(padded));
+
+  /* Initialize the initialization vector. */
+  switch (cipher)
+    {
+    case SSH_CIPHER_NONE:
+      /* Has to stay for authfile saving of private key with no passphrase */
+      break;
+
+    case SSH_CIPHER_3DES:
+      /* Note: the least significant bit of each byte of key is parity, 
+	 and must be ignored by the implementation.  16 bytes of key are
+	 used (first and last keys are the same). */
+      if (keylen < 16)
+	error("Key length %d is insufficient for 3DES.", keylen);
+      des_set_key((void*)padded, context->u.des3.key1);
+      des_set_key((void*)(padded + 8), context->u.des3.key2);
+      if (keylen <= 16)
+	des_set_key((void*)padded, context->u.des3.key3);
+      else
+	des_set_key((void*)(padded + 16), context->u.des3.key3);
+      memset(context->u.des3.iv2, 0, sizeof(context->u.des3.iv2));
+      memset(context->u.des3.iv3, 0, sizeof(context->u.des3.iv3));
+      break;
+
+    case SSH_CIPHER_BLOWFISH:
+      BF_set_key(&context->u.bf.key, keylen, padded);
+      memset(context->u.bf.iv, 0, 8);
+      break;
+
+    default:
+      fatal("cipher_set_key: unknown cipher: %d", cipher);
+    }
+  memset(padded, 0, sizeof(padded));
+}
+
+/* Encrypts data using the cipher. */
+
+void cipher_encrypt(CipherContext *context, unsigned char *dest,
+		    const unsigned char *src, unsigned int len)
+{
+  assert((len & 7) == 0);
+
+  switch (context->type)
+    {
+    case SSH_CIPHER_NONE:
+      memcpy(dest, src, len);
+      break;
+
+    case SSH_CIPHER_3DES:
+      SSH_3CBC_ENCRYPT(context->u.des3.key1,
+		       context->u.des3.key2, &context->u.des3.iv2,
+		       context->u.des3.key3, &context->u.des3.iv3,
+		       dest, (void*)src, len);
+      break;
+
+    case SSH_CIPHER_BLOWFISH:
+      swap_bytes(src, dest, len);
+      BF_cbc_encrypt(dest, dest, len,
+		     &context->u.bf.key, context->u.bf.iv, BF_ENCRYPT);
+      swap_bytes(dest, dest, len);
+      break;
+
+    default:
+      fatal("cipher_encrypt: unknown cipher: %d", context->type);
+    }
+}
+  
+/* Decrypts data using the cipher. */
+
+void cipher_decrypt(CipherContext *context, unsigned char *dest,
+		    const unsigned char *src, unsigned int len)
+{
+  assert((len & 7) == 0);
+
+  switch (context->type)
+    {
+    case SSH_CIPHER_NONE:
+      memcpy(dest, src, len);
+      break;
+
+    case SSH_CIPHER_3DES:
+      /* CRC-32 attack? */
+      SSH_3CBC_DECRYPT(context->u.des3.key1,
+		       context->u.des3.key2, &context->u.des3.iv2,
+		       context->u.des3.key3, &context->u.des3.iv3,
+		       dest, (void*)src, len);
+      break;
+
+    case SSH_CIPHER_BLOWFISH:
+      detect_cbc_attack(src, len);
+      swap_bytes(src, dest, len);
+      BF_cbc_encrypt((void*)dest, dest, len,
+		     &context->u.bf.key, context->u.bf.iv, BF_DECRYPT);
+      swap_bytes(dest, dest, len);
+      break;
+
+    default:
+      fatal("cipher_decrypt: unknown cipher: %d", context->type);
+    }
+}
diff --git a/cipher.h b/cipher.h
new file mode 100644
index 0000000..4ecb8f8
--- /dev/null
+++ b/cipher.h
@@ -0,0 +1,84 @@
+/*
+
+cipher.h
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Wed Apr 19 16:50:42 1995 ylo
+
+*/
+
+/* RCSID("$Id: cipher.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */
+
+#ifndef CIPHER_H
+#define CIPHER_H
+
+#include <openssl/des.h>
+#include <openssl/blowfish.h>
+
+/* Cipher types.  New types can be added, but old types should not be removed
+   for compatibility.  The maximum allowed value is 31. */
+#define SSH_CIPHER_NOT_SET	-1 /* None selected (invalid number). */
+#define SSH_CIPHER_NONE		0 /* no encryption */
+#define SSH_CIPHER_IDEA		1 /* IDEA CFB */
+#define SSH_CIPHER_DES		2 /* DES CBC */
+#define SSH_CIPHER_3DES		3 /* 3DES CBC */
+#define SSH_CIPHER_TSS		4 /* TRI's Simple Stream encryption CBC */
+#define SSH_CIPHER_RC4		5 /* Alleged RC4 */
+#define SSH_CIPHER_BLOWFISH	6
+
+typedef struct {
+  unsigned int type;
+  union {
+    struct {
+      des_key_schedule key1;
+      des_key_schedule key2;
+      des_cblock iv2;
+      des_key_schedule key3;
+      des_cblock iv3;
+    } des3;
+    struct {
+      struct bf_key_st key;
+      unsigned char iv[8];
+    } bf;
+  } u;
+} CipherContext;
+
+/* Returns a bit mask indicating which ciphers are supported by this
+   implementation.  The bit mask has the corresponding bit set of each
+   supported cipher. */
+unsigned int cipher_mask();
+
+/* Returns the name of the cipher. */
+const char *cipher_name(int cipher);
+
+/* Parses the name of the cipher.  Returns the number of the corresponding
+   cipher, or -1 on error. */
+int cipher_number(const char *name);
+
+/* Selects the cipher to use and sets the key.  If for_encryption is true,
+   the key is setup for encryption; otherwise it is setup for decryption. */
+void cipher_set_key(CipherContext *context, int cipher,
+		    const unsigned char *key, int keylen, int for_encryption);
+
+/* Sets key for the cipher by computing the MD5 checksum of the passphrase,
+   and using the resulting 16 bytes as the key. */
+void cipher_set_key_string(CipherContext *context, int cipher,
+			   const char *passphrase, int for_encryption);
+
+/* Encrypts data using the cipher. */
+void cipher_encrypt(CipherContext *context, unsigned char *dest,
+		    const unsigned char *src, unsigned int len);
+
+/* Decrypts data using the cipher. */
+void cipher_decrypt(CipherContext *context, unsigned char *dest,
+		    const unsigned char *src, unsigned int len);
+
+/* If and CRC-32 attack is detected this function is called. Defaults
+ * to fatal, changed to packet_disconnect in sshd and ssh. */
+extern void (*cipher_attack_detected)(const char *fmt, ...);
+
+#endif /* CIPHER_H */
diff --git a/clientloop.c b/clientloop.c
new file mode 100644
index 0000000..43373b7
--- /dev/null
+++ b/clientloop.c
@@ -0,0 +1,924 @@
+/*
+
+clientloop.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+
+Created: Sat Sep 23 12:23:57 1995 ylo
+
+The main loop for the interactive session (client side).
+
+*/
+
+#include "includes.h"
+RCSID("$Id: clientloop.c,v 1.1 1999/10/27 03:42:44 damien Exp $");
+
+#include "xmalloc.h"
+#include "ssh.h"
+#include "packet.h"
+#include "buffer.h"
+#include "authfd.h"
+
+/* Flag indicating whether quiet mode is on. */
+extern int quiet_flag;
+
+/* Flag indicating that stdin should be redirected from /dev/null. */
+extern int stdin_null_flag;
+
+/* Name of the host we are connecting to.  This is the name given on the
+   command line, or the HostName specified for the user-supplied name
+   in a configuration file. */
+extern char *host;
+
+/* Flag to indicate that we have received a window change signal which has
+   not yet been processed.  This will cause a message indicating the new
+   window size to be sent to the server a little later.  This is volatile
+   because this is updated in a signal handler. */
+static volatile int received_window_change_signal = 0;
+
+/* Terminal modes, as saved by enter_raw_mode. */
+static struct termios saved_tio;
+
+/* Flag indicating whether we are in raw mode.  This is used by enter_raw_mode
+   and leave_raw_mode. */
+static int in_raw_mode = 0;
+
+/* Flag indicating whether the user\'s terminal is in non-blocking mode. */
+static int in_non_blocking_mode = 0;
+
+/* Common data for the client loop code. */
+static int escape_pending;  /* Last character was the escape character */
+static int last_was_cr; /* Last character was a newline. */
+static int exit_status; /* Used to store the exit status of the command. */
+static int stdin_eof; /* EOF has been encountered on standard error. */
+static Buffer stdin_buffer;  /* Buffer for stdin data. */
+static Buffer stdout_buffer; /* Buffer for stdout data. */
+static Buffer stderr_buffer; /* Buffer for stderr data. */
+static unsigned int buffer_high; /* Soft max buffer size. */
+static int max_fd; /* Maximum file descriptor number in select(). */
+static int connection_in; /* Connection to server (input). */
+static int connection_out; /* Connection to server (output). */
+static unsigned long stdin_bytes, stdout_bytes, stderr_bytes;
+static int quit_pending; /* Set to non-zero to quit the client loop. */
+static int escape_char; /* Escape character. */
+
+/* Returns the user\'s terminal to normal mode if it had been put in raw 
+   mode. */
+
+void leave_raw_mode()
+{
+  if (!in_raw_mode)
+    return;
+  in_raw_mode = 0;
+  if (tcsetattr(fileno(stdin), TCSADRAIN, &saved_tio) < 0)
+    perror("tcsetattr");
+
+  fatal_remove_cleanup((void (*)(void *))leave_raw_mode, NULL);
+}
+
+/* Puts the user\'s terminal in raw mode. */
+
+void enter_raw_mode()
+{
+  struct termios tio;
+
+  if (tcgetattr(fileno(stdin), &tio) < 0)
+    perror("tcgetattr");
+  saved_tio = tio;
+  tio.c_iflag |= IGNPAR;
+  tio.c_iflag &= ~(ISTRIP|INLCR|IGNCR|ICRNL|IXON|IXANY|IXOFF);
+  tio.c_lflag &= ~(ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHONL);
+#ifdef IEXTEN
+  tio.c_lflag &= ~IEXTEN;
+#endif /* IEXTEN */
+  tio.c_oflag &= ~OPOST;
+  tio.c_cc[VMIN] = 1;
+  tio.c_cc[VTIME] = 0;
+  if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) < 0)
+    perror("tcsetattr");
+  in_raw_mode = 1;
+
+  fatal_add_cleanup((void (*)(void *))leave_raw_mode, NULL);
+}  
+
+/* Puts stdin terminal in non-blocking mode. */
+
+/* Restores stdin to blocking mode. */
+
+void leave_non_blocking()
+{
+  if (in_non_blocking_mode)
+    {
+      (void)fcntl(fileno(stdin), F_SETFL, 0);
+      in_non_blocking_mode = 0;
+      fatal_remove_cleanup((void (*)(void *))leave_non_blocking, NULL);
+    }
+}
+
+void enter_non_blocking()
+{
+  in_non_blocking_mode = 1;
+  (void)fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);
+  fatal_add_cleanup((void (*)(void *))leave_non_blocking, NULL);
+}
+
+/* Signal handler for the window change signal (SIGWINCH).  This just
+   sets a flag indicating that the window has changed. */
+
+void window_change_handler(int sig)
+{
+  received_window_change_signal = 1;
+  signal(SIGWINCH, window_change_handler);
+}
+
+/* Signal handler for signals that cause the program to terminate.  These
+   signals must be trapped to restore terminal modes. */
+
+void signal_handler(int sig)
+{
+  if (in_raw_mode)
+    leave_raw_mode();
+  if (in_non_blocking_mode)
+    leave_non_blocking();
+  channel_stop_listening();
+  packet_close();
+  fatal("Killed by signal %d.", sig);
+}
+
+/* Returns current time in seconds from Jan 1, 1970 with the maximum available
+   resolution. */
+
+double get_current_time()
+{
+  struct timeval tv;
+  gettimeofday(&tv, NULL);
+  return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
+}
+
+/* This is called when the interactive is entered.  This checks if there
+   is an EOF coming on stdin.  We must check this explicitly, as select()
+   does not appear to wake up when redirecting from /dev/null. */
+
+void client_check_initial_eof_on_stdin()
+{
+  int len;
+  char buf[1];
+
+  /* If standard input is to be "redirected from /dev/null", we simply
+     mark that we have seen an EOF and send an EOF message to the server.
+     Otherwise, we try to read a single character; it appears that for some
+     files, such /dev/null, select() never wakes up for read for this
+     descriptor, which means that we never get EOF.  This way we will get
+     the EOF if stdin comes from /dev/null or similar. */
+  if (stdin_null_flag)
+    {
+      /* Fake EOF on stdin. */
+      debug("Sending eof.");
+      stdin_eof = 1;
+      packet_start(SSH_CMSG_EOF);
+      packet_send();
+    }
+  else
+    {
+      /* Enter non-blocking mode for stdin. */
+      enter_non_blocking();
+
+      /* Check for immediate EOF on stdin. */
+      len = read(fileno(stdin), buf, 1);
+      if (len == 0)
+	{
+	  /* EOF.  Record that we have seen it and send EOF to server. */
+	  debug("Sending eof.");
+	  stdin_eof = 1;
+	  packet_start(SSH_CMSG_EOF);
+	  packet_send();
+	}
+      else
+	if (len > 0)
+	  {
+	    /* Got data.  We must store the data in the buffer, and also
+	       process it as an escape character if appropriate. */
+	    if ((unsigned char)buf[0] == escape_char)
+	      escape_pending = 1;
+	    else
+	      {
+		buffer_append(&stdin_buffer, buf, 1);
+		stdin_bytes += 1;
+	      }
+	  }
+      
+      /* Leave non-blocking mode. */
+      leave_non_blocking();
+    }
+}
+
+/* Get packets from the connection input buffer, and process them as long
+   as there are packets available. */
+
+void client_process_buffered_input_packets()
+{
+  int type;
+  char *data;
+  unsigned int data_len;
+  int payload_len;
+
+  /* Process any buffered packets from the server. */
+  while (!quit_pending && (type = packet_read_poll(&payload_len)) != SSH_MSG_NONE)
+    {
+      switch (type)
+	{
+	  
+	case SSH_SMSG_STDOUT_DATA:
+	  data = packet_get_string(&data_len);
+	  packet_integrity_check(payload_len, 4 + data_len, type);
+	  buffer_append(&stdout_buffer, data, data_len);
+	  stdout_bytes += data_len;
+	  memset(data, 0, data_len);
+	  xfree(data);
+	  break;
+
+	case SSH_SMSG_STDERR_DATA:
+	  data = packet_get_string(&data_len);
+	  packet_integrity_check(payload_len, 4 + data_len, type);
+	  buffer_append(&stderr_buffer, data, data_len);
+	  stdout_bytes += data_len;
+	  memset(data, 0, data_len);
+	  xfree(data);
+	  break;
+
+	case SSH_SMSG_EXITSTATUS:
+	  packet_integrity_check(payload_len, 4, type);
+	  exit_status = packet_get_int();
+	  /* Acknowledge the exit. */
+	  packet_start(SSH_CMSG_EXIT_CONFIRMATION);
+	  packet_send();
+	  /* Must wait for packet to be sent since we are exiting the
+	     loop. */
+	  packet_write_wait();
+	  /* Flag that we want to exit. */
+	  quit_pending = 1;
+	  break;
+
+	case SSH_SMSG_X11_OPEN:
+	  x11_input_open(payload_len);
+	  break;
+
+	case SSH_MSG_PORT_OPEN:
+	  channel_input_port_open(payload_len);
+	  break;
+
+	case SSH_SMSG_AGENT_OPEN:
+	  packet_integrity_check(payload_len, 4, type);
+	  auth_input_open_request();
+	  break;
+
+	case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
+	  packet_integrity_check(payload_len, 4 + 4, type);
+	  channel_input_open_confirmation();
+	  break;
+
+	case SSH_MSG_CHANNEL_OPEN_FAILURE:
+	  packet_integrity_check(payload_len, 4, type);
+	  channel_input_open_failure();
+	  break;
+
+	case SSH_MSG_CHANNEL_DATA:
+	  channel_input_data(payload_len);
+	  break;
+
+	case SSH_MSG_CHANNEL_CLOSE:
+	  packet_integrity_check(payload_len, 4, type);
+	  channel_input_close();
+	  break;
+
+	case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION:
+	  packet_integrity_check(payload_len, 4, type);
+	  channel_input_close_confirmation();
+	  break;
+
+	default:
+	  /* Any unknown packets received during the actual session
+	     cause the session to terminate.  This is intended to make
+	     debugging easier since no confirmations are sent.  Any
+	     compatible protocol extensions must be negotiated during
+	     the preparatory phase. */
+	  packet_disconnect("Protocol error during session: type %d",
+			    type);
+	}
+    }
+}
+
+/* Make packets from buffered stdin data, and buffer them for sending to
+   the connection. */
+
+void client_make_packets_from_stdin_data()
+{
+  unsigned int len;
+
+  /* Send buffered stdin data to the server. */
+  while (buffer_len(&stdin_buffer) > 0 && 
+	 packet_not_very_much_data_to_write())
+    {
+      len = buffer_len(&stdin_buffer);
+      if (len > 32768)
+	len = 32768;  /* Keep the packets at reasonable size. */
+      packet_start(SSH_CMSG_STDIN_DATA);
+      packet_put_string(buffer_ptr(&stdin_buffer), len);
+      packet_send();
+      buffer_consume(&stdin_buffer, len);
+      /* If we have a pending EOF, send it now. */
+      if (stdin_eof && buffer_len(&stdin_buffer) == 0)
+	{
+	  packet_start(SSH_CMSG_EOF);
+	  packet_send();
+	}
+    }
+}
+
+/* Checks if the client window has changed, and sends a packet about it to
+   the server if so.  The actual change is detected elsewhere (by a software
+   interrupt on Unix); this just checks the flag and sends a message if
+   appropriate. */
+
+void client_check_window_change()
+{
+  /* Send possible window change message to the server. */
+  if (received_window_change_signal)
+    {
+      struct winsize ws;
+
+      /* Clear the window change indicator. */
+      received_window_change_signal = 0;
+
+      /* Read new window size. */
+      if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) >= 0)
+	{
+	  /* Successful, send the packet now. */
+	  packet_start(SSH_CMSG_WINDOW_SIZE);
+	  packet_put_int(ws.ws_row);
+	  packet_put_int(ws.ws_col);
+	  packet_put_int(ws.ws_xpixel);
+	  packet_put_int(ws.ws_ypixel);
+	  packet_send();
+	}
+    }
+}
+
+/* Waits until the client can do something (some data becomes available on
+   one of the file descriptors). */
+
+void client_wait_until_can_do_something(fd_set *readset, fd_set *writeset)
+{
+  /* Initialize select masks. */
+  FD_ZERO(readset);
+  
+  /* Read from the connection, unless our buffers are full. */
+  if (buffer_len(&stdout_buffer) < buffer_high &&
+      buffer_len(&stderr_buffer) < buffer_high &&
+      channel_not_very_much_buffered_data())
+    FD_SET(connection_in, readset);
+
+  /* Read from stdin, unless we have seen EOF or have very much buffered
+     data to send to the server. */
+  if (!stdin_eof && packet_not_very_much_data_to_write())
+    FD_SET(fileno(stdin), readset);
+  
+  FD_ZERO(writeset);
+  
+  /* Add any selections by the channel mechanism. */
+  channel_prepare_select(readset, writeset);
+  
+  /* Select server connection if have data to write to the server. */
+  if (packet_have_data_to_write())
+    FD_SET(connection_out, writeset);
+
+  /* Select stdout if have data in buffer. */
+  if (buffer_len(&stdout_buffer) > 0)
+    FD_SET(fileno(stdout), writeset);
+
+  /* Select stderr if have data in buffer. */
+  if (buffer_len(&stderr_buffer) > 0)
+    FD_SET(fileno(stderr), writeset);
+
+  /* Update maximum file descriptor number, if appropriate. */
+  if (channel_max_fd() > max_fd)
+    max_fd = channel_max_fd();
+
+  /* Wait for something to happen.  This will suspend the process until
+     some selected descriptor can be read, written, or has some other
+     event pending.  Note: if you want to implement SSH_MSG_IGNORE
+     messages to fool traffic analysis, this might be the place to do
+     it: just have a random timeout for the select, and send a random
+     SSH_MSG_IGNORE packet when the timeout expires. */
+  if (select(max_fd + 1, readset, writeset, NULL, NULL) < 0)
+    {
+      char buf[100];
+      /* Some systems fail to clear these automatically. */
+      FD_ZERO(readset);
+      FD_ZERO(writeset);
+      if (errno == EINTR)
+	return;
+      /* Note: we might still have data in the buffers. */
+      snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno));
+      buffer_append(&stderr_buffer, buf, strlen(buf));
+      stderr_bytes += strlen(buf);
+      quit_pending = 1;
+    }
+}
+
+void client_suspend_self()
+{
+  struct winsize oldws, newws;
+
+  /* Flush stdout and stderr buffers. */
+  if (buffer_len(&stdout_buffer) > 0)
+    write(fileno(stdout), 
+	  buffer_ptr(&stdout_buffer), 
+	  buffer_len(&stdout_buffer));
+  if (buffer_len(&stderr_buffer) > 0)
+    write(fileno(stderr), 
+	  buffer_ptr(&stderr_buffer), 
+	  buffer_len(&stderr_buffer));
+
+  /* Leave raw mode. */
+  leave_raw_mode();
+
+  /* Free (and clear) the buffer to reduce the
+     amount of data that gets written to swap. */
+  buffer_free(&stdin_buffer);
+  buffer_free(&stdout_buffer);
+  buffer_free(&stderr_buffer);
+
+  /* Save old window size. */
+  ioctl(fileno(stdin), TIOCGWINSZ, &oldws);
+
+  /* Send the suspend signal to the program
+     itself. */
+  kill(getpid(), SIGTSTP);
+
+  /* Check if the window size has changed. */
+  if (ioctl(fileno(stdin), TIOCGWINSZ, &newws) >= 0 &&
+      (oldws.ws_row != newws.ws_row || oldws.ws_col != newws.ws_col ||
+       oldws.ws_xpixel != newws.ws_xpixel || 
+       oldws.ws_ypixel != newws.ws_ypixel))
+    received_window_change_signal = 1;
+
+  /* OK, we have been continued by the user. 
+     Reinitialize buffers. */
+  buffer_init(&stdin_buffer);
+  buffer_init(&stdout_buffer);
+  buffer_init(&stderr_buffer);
+
+  /* Re-enter raw mode. */
+  enter_raw_mode();
+}
+
+void client_process_input(fd_set *readset)
+{
+  int len, pid;
+  char buf[8192], *s;
+
+  /* Read input from the server, and add any such data to the buffer of the
+     packet subsystem. */
+  if (FD_ISSET(connection_in, readset))
+    {
+      /* Read as much as possible. */
+      len = read(connection_in, buf, sizeof(buf));
+      if (len == 0)
+	{ 
+	  /* Received EOF.  The remote host has closed the connection. */
+	  snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n",
+		  host);
+	  buffer_append(&stderr_buffer, buf, strlen(buf));
+	  stderr_bytes += strlen(buf);
+	  quit_pending = 1;
+	  return;
+	}
+
+      /* There is a kernel bug on Solaris that causes select to sometimes
+	 wake up even though there is no data available. */
+      if (len < 0 && errno == EAGAIN)
+	len = 0;
+
+      if (len < 0)
+	{
+	  /* An error has encountered.  Perhaps there is a network
+	     problem. */
+	  snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n", 
+		  host, strerror(errno));
+	  buffer_append(&stderr_buffer, buf, strlen(buf));
+	  stderr_bytes += strlen(buf);
+	  quit_pending = 1;
+	  return;
+	}
+      packet_process_incoming(buf, len);
+    }
+
+  /* Read input from stdin. */
+  if (FD_ISSET(fileno(stdin), readset))
+    {
+      /* Read as much as possible. */
+      len = read(fileno(stdin), buf, sizeof(buf));
+      if (len <= 0)
+	{
+	  /* Received EOF or error.  They are treated similarly,
+	     except that an error message is printed if it was
+	     an error condition. */
+	  if (len < 0)
+	    {
+	      snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno));
+	      buffer_append(&stderr_buffer, buf, strlen(buf));
+	      stderr_bytes += strlen(buf);
+	    }
+	  /* Mark that we have seen EOF. */
+	  stdin_eof = 1;
+	  /* Send an EOF message to the server unless there is data
+	     in the buffer.  If there is data in the buffer, no message
+	     will be sent now.  Code elsewhere will send the EOF
+	     when the buffer becomes empty if stdin_eof is set. */
+	  if (buffer_len(&stdin_buffer) == 0)
+	    {
+	      packet_start(SSH_CMSG_EOF);
+	      packet_send();
+	    }
+	}
+      else
+	if (escape_char == -1)
+	  {
+	    /* Normal successful read, and no escape character.  Just 
+	       append the data to buffer. */
+	    buffer_append(&stdin_buffer, buf, len);
+	    stdin_bytes += len;
+	  }
+	else
+	  {
+	    /* Normal, successful read.  But we have an escape character
+	       and have to process the characters one by one. */
+	    unsigned int i;
+	    for (i = 0; i < len; i++)
+	      {
+		unsigned char ch;
+		/* Get one character at a time. */
+		ch = buf[i];
+		
+		/* Check if we have a pending escape character. */
+		if (escape_pending)
+		  {
+		    /* We have previously seen an escape character. */
+		    /* Clear the flag now. */
+		    escape_pending = 0;
+		    /* Process the escaped character. */
+		    switch (ch)
+		      {
+		      case '.':
+			/* Terminate the connection. */
+			snprintf(buf, sizeof buf, "%c.\r\n", escape_char);
+			buffer_append(&stderr_buffer, buf, strlen(buf));
+			stderr_bytes += strlen(buf);
+			quit_pending = 1;
+			return;
+
+		      case 'Z' - 64:
+			  /* Suspend the program. */
+			  /* Print a message to that effect to the user. */
+			  snprintf(buf, sizeof buf, "%c^Z\r\n", escape_char);
+			  buffer_append(&stderr_buffer, buf, strlen(buf));
+			  stderr_bytes += strlen(buf);
+
+			  /* Restore terminal modes and suspend. */
+			  client_suspend_self();
+
+			  /* We have been continued. */
+			  continue;
+			
+		      case '&':
+			/* Detach the program (continue to serve connections,
+			   but put in background and no more new 
+			   connections). */
+			if (!stdin_eof)
+			  {
+			    /* Sending SSH_CMSG_EOF alone does not always
+			       appear to be enough.  So we try to send an
+			       EOF character first. */
+			    packet_start(SSH_CMSG_STDIN_DATA);
+			    packet_put_string("\004", 1);
+			    packet_send();
+			    /* Close stdin. */
+			    stdin_eof = 1;
+			    if (buffer_len(&stdin_buffer) == 0)
+			      {
+				packet_start(SSH_CMSG_EOF);
+				packet_send();
+			      }
+			  }
+			/* Restore tty modes. */
+			leave_raw_mode();
+
+			/* Stop listening for new connections. */
+			channel_stop_listening();
+
+			printf("%c& [backgrounded]\n", escape_char);
+			
+			/* Fork into background. */
+			pid = fork();
+			if (pid < 0)
+			  {
+			    error("fork: %.100s", strerror(errno));
+			    continue;
+			  }
+			if (pid != 0)
+			  { /* This is the parent. */
+			    /* The parent just exits. */
+			    exit(0);
+			  }
+
+			/* The child continues serving connections. */
+			continue;
+
+		      case '?':
+			snprintf(buf, sizeof buf, "%c?\r\n\
+Supported escape sequences:\r\n\
+~.  - terminate connection\r\n\
+~^Z - suspend ssh\r\n\
+~#  - list forwarded connections\r\n\
+~&  - background ssh (when waiting for connections to terminate)\r\n\
+~?  - this message\r\n\
+~~  - send the escape character by typing it twice\r\n\
+(Note that escapes are only recognized immediately after newline.)\r\n",
+				escape_char);
+			buffer_append(&stderr_buffer, buf, strlen(buf));
+			continue;
+
+		      case '#':
+			snprintf(buf, sizeof buf, "%c#\r\n", escape_char);
+			buffer_append(&stderr_buffer, buf, strlen(buf));
+			s = channel_open_message();
+			buffer_append(&stderr_buffer, s, strlen(s));
+			xfree(s);
+			continue;
+
+		      default:
+			if (ch != escape_char)
+			  {
+			    /* Escape character followed by non-special
+			       character.  Append both to the input
+			       buffer. */
+			    buf[0] = escape_char;
+			    buf[1] = ch;
+			    buffer_append(&stdin_buffer, buf, 2);
+			    stdin_bytes += 2;
+			    continue;
+			  }
+			/* Note that escape character typed twice falls through
+			   here; the latter gets processed as a normal
+			   character below. */
+			break;
+		      }
+		  }
+		else
+		  {
+		    /* The previous character was not an escape char. 
+		       Check if this is an escape. */
+		    if (last_was_cr && ch == escape_char)
+		      {
+			/* It is. Set the flag and continue to next
+			   character. */
+			escape_pending = 1;
+			continue;
+		      }
+		  }
+
+		/* Normal character.  Record whether it was a newline,
+		   and append it to the buffer. */
+		last_was_cr = (ch == '\r' || ch == '\n');
+		buf[0] = ch;
+		buffer_append(&stdin_buffer, buf, 1);
+		stdin_bytes += 1;
+		continue;
+	      }
+	  }
+    }
+}
+
+void client_process_output(fd_set *writeset)
+{
+  int len;
+  char buf[100];
+
+  /* Write buffered output to stdout. */
+  if (FD_ISSET(fileno(stdout), writeset))
+    {
+      /* Write as much data as possible. */
+      len = write(fileno(stdout), buffer_ptr(&stdout_buffer),
+		  buffer_len(&stdout_buffer));
+      if (len <= 0)
+	{
+	  if (errno == EAGAIN)
+	    len = 0;
+	  else
+	    {
+	      /* An error or EOF was encountered.  Put an error message
+		 to stderr buffer. */
+	      snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno));
+	      buffer_append(&stderr_buffer, buf, strlen(buf));
+	      stderr_bytes += strlen(buf);
+	      quit_pending = 1;
+	      return;
+	    }
+	}
+      /* Consume printed data from the buffer. */
+      buffer_consume(&stdout_buffer, len);
+    }
+
+  /* Write buffered output to stderr. */
+  if (FD_ISSET(fileno(stderr), writeset))
+    {
+      /* Write as much data as possible. */
+      len = write(fileno(stderr), buffer_ptr(&stderr_buffer),
+		  buffer_len(&stderr_buffer));
+      if (len <= 0) {
+	if (errno == EAGAIN)
+	  len = 0;
+	else
+	  {
+	    /* EOF or error, but can't even print error message. */
+	    quit_pending = 1;
+	    return;
+	  }
+      }
+      /* Consume printed characters from the buffer. */
+      buffer_consume(&stderr_buffer, len);
+    }
+}
+
+/* Implements the interactive session with the server.  This is called
+   after the user has been authenticated, and a command has been
+   started on the remote host.  If escape_char != -1, it is the character
+   used as an escape character for terminating or suspending the
+   session. */
+
+int client_loop(int have_pty, int escape_char_arg)
+{
+  double start_time, total_time;
+  int len;
+  char buf[100];
+
+  debug("Entering interactive session.");
+
+  start_time = get_current_time();
+
+  /* Initialize variables. */
+  escape_pending = 0;
+  last_was_cr = 1;
+  exit_status = -1;
+  stdin_eof = 0;
+  buffer_high = 64 * 1024;
+  connection_in = packet_get_connection_in();
+  connection_out = packet_get_connection_out();
+  max_fd = connection_in;
+  if (connection_out > max_fd)
+    max_fd = connection_out;
+  stdin_bytes = 0;
+  stdout_bytes = 0;
+  stderr_bytes = 0;
+  quit_pending = 0;
+  escape_char = escape_char_arg;
+
+  /* Initialize buffers. */
+  buffer_init(&stdin_buffer);
+  buffer_init(&stdout_buffer);
+  buffer_init(&stderr_buffer);
+
+  /* Set signal handlers to restore non-blocking mode.  */
+  signal(SIGINT, signal_handler);
+  signal(SIGQUIT, signal_handler);
+  signal(SIGTERM, signal_handler);
+  signal(SIGPIPE, SIG_IGN);
+  if (have_pty)
+    signal(SIGWINCH, window_change_handler);
+
+  /* Enter raw mode if have a pseudo terminal. */
+  if (have_pty)
+    enter_raw_mode();
+
+  /* Check if we should immediately send of on stdin. */
+  client_check_initial_eof_on_stdin();
+
+  /* Main loop of the client for the interactive session mode. */
+  while (!quit_pending)
+    {
+      fd_set readset, writeset;
+
+      /* Precess buffered packets sent by the server. */
+      client_process_buffered_input_packets();
+
+      /* Make packets of buffered stdin data, and buffer them for sending
+	 to the server. */
+      client_make_packets_from_stdin_data();
+
+      /* Make packets from buffered channel data, and buffer them for sending
+	 to the server. */
+      if (packet_not_very_much_data_to_write())
+	channel_output_poll();
+
+      /* Check if the window size has changed, and buffer a message about
+	 it to the server if so. */
+      client_check_window_change();
+
+      if (quit_pending)
+	break;
+
+      /* Wait until we have something to do (something becomes available
+	 on one of the descriptors). */
+      client_wait_until_can_do_something(&readset, &writeset);
+
+      if (quit_pending)
+	break;
+
+      /* Do channel operations. */
+      channel_after_select(&readset, &writeset);
+
+      /* Process input from the connection and from stdin.  Buffer any data
+         that is available. */
+      client_process_input(&readset);
+
+      /* Process output to stdout and stderr.   Output to the connection
+         is processed elsewhere (above). */
+      client_process_output(&writeset);
+
+      /* Send as much buffered packet data as possible to the sender. */
+      if (FD_ISSET(connection_out, &writeset))
+	packet_write_poll();
+    }
+
+  /* Terminate the session. */
+
+  /* Stop watching for window change. */
+  if (have_pty)
+    signal(SIGWINCH, SIG_DFL);
+
+  /* Stop listening for connections. */
+  channel_stop_listening();
+
+  /* In interactive mode (with pseudo tty) display a message indicating that
+     the connection has been closed. */
+  if (have_pty && !quiet_flag)
+    {
+      snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host);
+      buffer_append(&stderr_buffer, buf, strlen(buf));
+      stderr_bytes += strlen(buf);
+    }
+
+  /* Output any buffered data for stdout. */
+  while (buffer_len(&stdout_buffer) > 0)
+    {
+      len = write(fileno(stdout), buffer_ptr(&stdout_buffer), 
+		  buffer_len(&stdout_buffer));
+      if (len <= 0)
+	{
+	  error("Write failed flushing stdout buffer.");
+	  break;
+	}
+      buffer_consume(&stdout_buffer, len);
+    }
+
+  /* Output any buffered data for stderr. */
+  while (buffer_len(&stderr_buffer) > 0)
+    {
+      len = write(fileno(stderr), buffer_ptr(&stderr_buffer), 
+		  buffer_len(&stderr_buffer));
+      if (len <= 0)
+	{
+	  error("Write failed flushing stderr buffer.");
+	  break;
+	}
+      buffer_consume(&stderr_buffer, len);
+    }
+
+  /* Leave raw mode. */
+  if (have_pty)
+    leave_raw_mode();
+
+  /* Clear and free any buffers. */
+  memset(buf, 0, sizeof(buf));
+  buffer_free(&stdin_buffer);
+  buffer_free(&stdout_buffer);
+  buffer_free(&stderr_buffer);
+
+  /* Report bytes transferred, and transfer rates. */
+  total_time = get_current_time() - start_time;
+  debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f seconds",
+	stdin_bytes, stdout_bytes, stderr_bytes, total_time);
+  if (total_time > 0)
+    debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f",
+	  stdin_bytes / total_time, stdout_bytes / total_time,
+	  stderr_bytes / total_time);
+
+  /* Return the exit status of the program. */
+  debug("Exit status %d", exit_status);
+  return exit_status;
+}
diff --git a/compat.c b/compat.c
new file mode 100644
index 0000000..4974b1c
--- /dev/null
+++ b/compat.c
@@ -0,0 +1,10 @@
+#include "includes.h"
+RCSID("$Id: compat.c,v 1.1 1999/10/27 03:42:44 damien Exp $");
+
+#include "ssh.h"
+
+int compat13=0;
+void enable_compat13(void){
+	log("Enabling compatibility mode for protocol 1.3");
+	compat13=1;
+}
diff --git a/compat.h b/compat.h
new file mode 100644
index 0000000..9d896c7
--- /dev/null
+++ b/compat.h
@@ -0,0 +1,7 @@
+/* RCSID("$Id: compat.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */
+
+#ifndef COMPAT_H
+#define COMPAT_H
+void enable_compat13(void);
+extern int compat13;
+#endif
diff --git a/compress.c b/compress.c
new file mode 100644
index 0000000..c3267f7
--- /dev/null
+++ b/compress.c
@@ -0,0 +1,160 @@
+/*
+
+compress.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Wed Oct 25 22:12:46 1995 ylo
+
+Interface to packet compression for ssh.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: compress.c,v 1.1 1999/10/27 03:42:44 damien Exp $");
+
+#include "ssh.h"
+#include "buffer.h"
+#include "zlib.h"
+
+static z_stream incoming_stream;
+static z_stream outgoing_stream;
+
+/* Initializes compression; level is compression level from 1 to 9 (as in
+   gzip). */
+
+void buffer_compress_init(int level)
+{
+  debug("Enabling compression at level %d.", level);
+  if (level < 1 || level > 9)
+    fatal("Bad compression level %d.", level);
+  inflateInit(&incoming_stream);
+  deflateInit(&outgoing_stream, level);
+}
+
+/* Frees any data structures allocated for compression. */
+
+void buffer_compress_uninit()
+{
+  debug("compress outgoing: raw data %lu, compressed %lu, factor %.2f",
+	outgoing_stream.total_in, outgoing_stream.total_out,
+	outgoing_stream.total_in == 0 ? 0.0 :
+	 (double)outgoing_stream.total_out / outgoing_stream.total_in);
+  debug("compress incoming: raw data %lu, compressed %lu, factor %.2f",
+	incoming_stream.total_out, incoming_stream.total_in,
+	incoming_stream.total_out == 0 ? 0.0 :
+	  (double)incoming_stream.total_in / incoming_stream.total_out);
+  inflateEnd(&incoming_stream);
+  deflateEnd(&outgoing_stream);
+}
+
+/* Compresses the contents of input_buffer into output_buffer.  All
+   packets compressed using this function will form a single
+   compressed data stream; however, data will be flushed at the end of
+   every call so that each output_buffer can be decompressed
+   independently (but in the appropriate order since they together
+   form a single compression stream) by the receiver.  This appends
+   the compressed data to the output buffer. */
+
+void buffer_compress(Buffer *input_buffer, Buffer *output_buffer)
+{
+  char buf[4096];
+  int status;
+
+  /* This case is not handled below. */
+  if (buffer_len(input_buffer) == 0)
+    return;
+
+  /* Input is the contents of the input buffer. */
+  outgoing_stream.next_in = buffer_ptr(input_buffer);
+  outgoing_stream.avail_in = buffer_len(input_buffer);
+
+  /* Loop compressing until deflate() returns with avail_out != 0. */
+  do
+    {
+      /* Set up fixed-size output buffer. */
+      outgoing_stream.next_out = buf;
+      outgoing_stream.avail_out = sizeof(buf);
+
+      /* Compress as much data into the buffer as possible. */
+      status = deflate(&outgoing_stream, Z_PARTIAL_FLUSH);
+      switch (status)
+	{
+	case Z_OK:
+	  /* Append compressed data to output_buffer. */
+	  buffer_append(output_buffer, buf,
+			sizeof(buf) - outgoing_stream.avail_out);
+	  break;
+	case Z_STREAM_END:
+	  fatal("buffer_compress: deflate returned Z_STREAM_END");
+	  /*NOTREACHED*/
+	case Z_STREAM_ERROR:
+	  fatal("buffer_compress: deflate returned Z_STREAM_ERROR");
+	  /*NOTREACHED*/
+	case Z_BUF_ERROR:
+	  fatal("buffer_compress: deflate returned Z_BUF_ERROR");
+	  /*NOTREACHED*/
+	default:
+	  fatal("buffer_compress: deflate returned %d", status);
+	  /*NOTREACHED*/
+	}
+    }
+  while (outgoing_stream.avail_out == 0);
+}
+
+/* Uncompresses the contents of input_buffer into output_buffer.  All
+   packets uncompressed using this function will form a single
+   compressed data stream; however, data will be flushed at the end of
+   every call so that each output_buffer.  This must be called for the
+   same size units that the buffer_compress was called, and in the
+   same order that buffers compressed with that.  This appends the
+   uncompressed data to the output buffer. */
+
+void buffer_uncompress(Buffer *input_buffer, Buffer *output_buffer)
+{
+  char buf[4096];
+  int status;
+
+  incoming_stream.next_in = buffer_ptr(input_buffer);
+  incoming_stream.avail_in = buffer_len(input_buffer);
+
+  incoming_stream.next_out = buf;
+  incoming_stream.avail_out = sizeof(buf);
+
+  for (;;)
+    {
+      status = inflate(&incoming_stream, Z_PARTIAL_FLUSH);
+      switch (status)
+	{
+	case Z_OK:
+	  buffer_append(output_buffer, buf,
+			sizeof(buf) - incoming_stream.avail_out);
+	  incoming_stream.next_out = buf;
+	  incoming_stream.avail_out = sizeof(buf);
+	  break;
+	case Z_STREAM_END:
+	  fatal("buffer_uncompress: inflate returned Z_STREAM_END");
+	  /*NOTREACHED*/
+	case Z_DATA_ERROR:
+	  fatal("buffer_uncompress: inflate returned Z_DATA_ERROR");
+	  /*NOTREACHED*/
+	case Z_STREAM_ERROR:
+	  fatal("buffer_uncompress: inflate returned Z_STREAM_ERROR");
+	  /*NOTREACHED*/
+	case Z_BUF_ERROR:
+	  /* Comments in zlib.h say that we should keep calling inflate()
+	     until we get an error.  This appears to be the error that we
+	     get. */
+	  return;
+	case Z_MEM_ERROR:
+	  fatal("buffer_uncompress: inflate returned Z_MEM_ERROR");
+	  /*NOTREACHED*/
+	default:
+	  fatal("buffer_uncompress: inflate returned %d", status);
+	}
+    }
+}
+
diff --git a/compress.h b/compress.h
new file mode 100644
index 0000000..b3144d6
--- /dev/null
+++ b/compress.h
@@ -0,0 +1,46 @@
+/*
+
+compress.h
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Wed Oct 25 22:12:46 1995 ylo
+
+Interface to packet compression for ssh.
+
+*/
+
+/* RCSID("$Id: compress.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */
+
+#ifndef COMPRESS_H
+#define COMPRESS_H
+
+/* Initializes compression; level is compression level from 1 to 9 (as in
+   gzip). */
+void buffer_compress_init(int level);
+
+/* Frees any data structures allocated by buffer_compress_init. */
+void buffer_compress_uninit();
+
+/* Compresses the contents of input_buffer into output_buffer.  All
+   packets compressed using this function will form a single
+   compressed data stream; however, data will be flushed at the end of
+   every call so that each output_buffer can be decompressed
+   independently (but in the appropriate order since they together
+   form a single compression stream) by the receiver.  This appends
+   the compressed data to the output buffer. */
+void buffer_compress(Buffer *input_buffer, Buffer *output_buffer);
+
+/* Uncompresses the contents of input_buffer into output_buffer.  All
+   packets uncompressed using this function will form a single
+   compressed data stream; however, data will be flushed at the end of
+   every call so that each output_buffer.  This must be called for the
+   same size units that the buffer_compress was called, and in the
+   same order that buffers compressed with that.  This appends the
+   uncompressed data to the output buffer. */
+void buffer_uncompress(Buffer *input_buffer, Buffer *output_buffer);
+
+#endif /* COMPRESS_H */
diff --git a/crc32.c b/crc32.c
new file mode 100644
index 0000000..dbb1e6b
--- /dev/null
+++ b/crc32.c
@@ -0,0 +1,120 @@
+/* The implementation here was originally done by Gary S. Brown.  I have
+   borrowed the tables directly, and made some minor changes to the
+   crc32-function (including changing the interface). //ylo */
+
+#include "includes.h"
+RCSID("$Id: crc32.c,v 1.1 1999/10/27 03:42:44 damien Exp $");
+
+#include "crc32.h"
+
+  /* ============================================================= */
+  /*  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or       */
+  /*  code or tables extracted from it, as desired without restriction.     */
+  /*                                                                        */
+  /*  First, the polynomial itself and its table of feedback terms.  The    */
+  /*  polynomial is                                                         */
+  /*  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0   */
+  /*                                                                        */
+  /*  Note that we take it "backwards" and put the highest-order term in    */
+  /*  the lowest-order bit.  The X^32 term is "implied"; the LSB is the     */
+  /*  X^31 term, etc.  The X^0 term (usually shown as "+1") results in      */
+  /*  the MSB being 1.                                                      */
+  /*                                                                        */
+  /*  Note that the usual hardware shift register implementation, which     */
+  /*  is what we're using (we're merely optimizing it by doing eight-bit    */
+  /*  chunks at a time) shifts bits into the lowest-order term.  In our     */
+  /*  implementation, that means shifting towards the right.  Why do we     */
+  /*  do it this way?  Because the calculated CRC must be transmitted in    */
+  /*  order from highest-order term to lowest-order term.  UARTs transmit   */
+  /*  characters in order from LSB to MSB.  By storing the CRC this way,    */
+  /*  we hand it to the UART in the order low-byte to high-byte; the UART   */
+  /*  sends each low-bit to hight-bit; and the result is transmission bit   */
+  /*  by bit from highest- to lowest-order term without requiring any bit   */
+  /*  shuffling on our part.  Reception works similarly.                    */
+  /*                                                                        */
+  /*  The feedback terms table consists of 256, 32-bit entries.  Notes:     */
+  /*                                                                        */
+  /*      The table can be generated at runtime if desired; code to do so   */
+  /*      is shown later.  It might not be obvious, but the feedback        */
+  /*      terms simply represent the results of eight shift/xor opera-      */
+  /*      tions for all combinations of data and CRC register values.       */
+  /*                                                                        */
+  /*      The values must be right-shifted by eight bits by the "updcrc"    */
+  /*      logic; the shift must be unsigned (bring in zeroes).  On some     */
+  /*      hardware you could probably optimize the shift in assembler by    */
+  /*      using byte-swap instructions.                                     */
+  /*      polynomial $edb88320                                              */
+  /*                                                                        */
+  /*  --------------------------------------------------------------------  */
+
+static unsigned int crc32_tab[] = {
+      0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+      0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+      0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+      0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+      0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+      0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+      0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+      0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+      0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+      0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+      0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+      0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+      0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+      0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+      0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+      0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+      0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+      0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+      0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+      0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+      0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+      0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+      0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+      0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+      0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+      0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+      0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+      0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+      0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+      0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+      0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+      0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+      0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+      0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+      0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+      0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+      0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+      0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+      0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+      0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+      0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+      0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+      0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+      0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+      0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+      0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+      0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+      0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+      0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+      0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+      0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+      0x2d02ef8dL
+   };
+
+/* Return a 32-bit CRC of the contents of the buffer. */
+
+unsigned int crc32(const unsigned char *s, unsigned int len)
+{
+  unsigned int i;
+  unsigned int crc32val;
+  
+  crc32val = 0;
+  for (i = 0;  i < len;  i ++)
+    {
+      crc32val =
+	crc32_tab[(crc32val ^ s[i]) & 0xff] ^
+	  (crc32val >> 8);
+    }
+  return crc32val;
+}
diff --git a/crc32.h b/crc32.h
new file mode 100644
index 0000000..456b20b
--- /dev/null
+++ b/crc32.h
@@ -0,0 +1,25 @@
+/*
+
+crc32.h
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1992 Tatu Ylonen, Espoo, Finland
+                   All rights reserved
+
+Created: Tue Feb 11 14:37:27 1992 ylo
+
+Functions for computing 32-bit CRC.
+
+*/
+
+/* RCSID("$Id: crc32.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */
+
+#ifndef CRC32_H
+#define CRC32_H
+
+/* This computes a 32 bit CRC of the data in the buffer, and returns the
+   CRC.  The polynomial used is 0xedb88320. */
+unsigned int crc32(const unsigned char *buf, unsigned int len);
+
+#endif /* CRC32_H */
diff --git a/deattack.c b/deattack.c
new file mode 100644
index 0000000..d5f8608
--- /dev/null
+++ b/deattack.c
@@ -0,0 +1,180 @@
+/*
+ * $Id: deattack.c,v 1.1 1999/10/27 03:42:44 damien Exp $
+ * Cryptographic attack detector for ssh - source code
+ *
+ * Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina.
+ *
+ * All rights reserved. Redistribution and use in source and binary
+ * forms, with or without modification, are permitted provided that
+ * this copyright notice is retained.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES ARE DISCLAIMED. IN NO EVENT SHALL CORE SDI S.A. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR
+ * CONSEQUENTIAL DAMAGES RESULTING FROM THE USE OR MISUSE OF THIS
+ * SOFTWARE.
+ *
+ * Ariel Futoransky <futo@core-sdi.com>
+ * <http://www.core-sdi.com> */
+
+#include "includes.h"
+#include "deattack.h"
+#include "ssh.h"
+#include "crc32.h"
+#include "getput.h"
+#include "xmalloc.h"
+
+/* SSH Constants */
+#define SSH_MAXBLOCKS (32 * 1024)
+#define SSH_BLOCKSIZE (8)
+
+/* Hashing constants */
+#define HASH_MINSIZE (8 * 1024)
+#define HASH_ENTRYSIZE (2)
+#define HASH_FACTOR(x) ((x)*3/2)
+#define HASH_UNUSEDCHAR (0xff)
+#define HASH_UNUSED (0xffff)
+#define HASH_IV     (0xfffe)
+
+#define HASH_MINBLOCKS  (7*SSH_BLOCKSIZE)
+
+
+/* Hash function (Input keys are cipher results) */
+#define HASH(x) GET_32BIT(x)
+
+#define CMP(a,b) (memcmp(a, b, SSH_BLOCKSIZE))
+
+
+void
+crc_update(u_int32_t * a, u_int32_t b)
+{
+  b ^= *a;
+  *a = crc32((unsigned char *) &b, sizeof(b));
+}
+
+/*
+   check_crc
+   detects if a block is used in a particular pattern
+ */
+
+int
+check_crc(unsigned char *S, unsigned char *buf, u_int32_t len, unsigned char *IV)
+{
+  u_int32_t          crc;
+  unsigned char  *c;
+
+  crc = 0;
+  if (IV && !CMP(S, IV))
+  {
+    crc_update(&crc, 1);
+    crc_update(&crc, 0);
+  }
+  for (c = buf; c < buf + len; c += SSH_BLOCKSIZE)
+  {
+    if (!CMP(S, c))
+    {
+      crc_update(&crc, 1);
+      crc_update(&crc, 0);
+    } else
+    {
+      crc_update(&crc, 0);
+      crc_update(&crc, 0);
+    }
+  }
+
+  return (crc == 0);
+}
+
+
+/*
+   detect_attack
+   Detects a crc32 compensation attack on a packet
+ */
+int
+detect_attack(unsigned char *buf, u_int32_t len, unsigned char *IV)
+{
+  static u_int16_t  *h = (u_int16_t *) NULL;
+  static u_int16_t   n = HASH_MINSIZE / HASH_ENTRYSIZE;
+  register u_int32_t i, j;
+  u_int32_t          l;
+  register unsigned char *c;
+  unsigned char  *d;
+
+
+  assert(len <= (SSH_MAXBLOCKS * SSH_BLOCKSIZE));
+  assert(len % SSH_BLOCKSIZE == 0);
+
+  for (l = n; l < HASH_FACTOR(len / SSH_BLOCKSIZE); l = l << 2);
+
+  if (h == NULL)
+  {
+    debug("Installing crc compensation attack detector.");
+    n = l;
+    h = (u_int16_t *) xmalloc(n * HASH_ENTRYSIZE);
+  } else
+  {
+    if (l > n)
+    {
+      n = l;
+      h = (u_int16_t *) xrealloc(h, n * HASH_ENTRYSIZE);
+    }
+  }
+
+
+  if (len <= HASH_MINBLOCKS)
+  {
+    for (c = buf; c < buf + len; c += SSH_BLOCKSIZE)
+    {
+      if (IV && (!CMP(c, IV)))
+      {
+	if ((check_crc(c, buf, len, IV)))
+	  return (DEATTACK_DETECTED);
+	else
+	  break;
+      }
+      for (d = buf; d < c; d += SSH_BLOCKSIZE)
+      {
+	if (!CMP(c, d))
+	{
+	  if ((check_crc(c, buf, len, IV)))
+	    return (DEATTACK_DETECTED);
+	  else
+	    break;
+	}
+      }
+    }
+    return (DEATTACK_OK);
+  }
+  memset(h, HASH_UNUSEDCHAR, n * HASH_ENTRYSIZE);
+
+  if (IV)
+    h[HASH(IV) & (n - 1)] = HASH_IV;
+
+
+  for (c = buf, j = 0; c < (buf + len); c += SSH_BLOCKSIZE, j++)
+  {
+    for (i = HASH(c) & (n - 1); h[i] != HASH_UNUSED;
+	 i = (i + 1) & (n - 1))
+    {
+      if (h[i] == HASH_IV)
+      {
+	if (!CMP(c, IV))
+	{
+	  if (check_crc(c, buf, len, IV))
+	    return (DEATTACK_DETECTED);
+	  else
+	    break;
+	}
+      } else if (!CMP(c, buf + h[i] * SSH_BLOCKSIZE))
+      {
+	if (check_crc(c, buf, len, IV))
+	  return (DEATTACK_DETECTED);
+	else
+	  break;
+      }
+    }
+    h[i] = j;
+  }
+
+  return (DEATTACK_OK);
+}
diff --git a/deattack.h b/deattack.h
new file mode 100644
index 0000000..a0dcf5b
--- /dev/null
+++ b/deattack.h
@@ -0,0 +1,27 @@
+/* $Id: deattack.h,v 1.1 1999/10/27 03:42:44 damien Exp $
+ * Cryptographic attack detector for ssh - Header file
+ *
+ * Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina.
+ *
+ * All rights reserved. Redistribution and use in source and binary
+ * forms, with or without modification, are permitted provided that
+ * this copyright notice is retained.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES ARE DISCLAIMED. IN NO EVENT SHALL CORE SDI S.A. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR
+ * CONSEQUENTIAL DAMAGES RESULTING FROM THE USE OR MISUSE OF THIS
+ * SOFTWARE.
+ *
+ * Ariel Futoransky <futo@core-sdi.com>
+ * <http://www.core-sdi.com> */
+
+#ifndef _DEATTACK_H
+#define _DEATTACK_H
+
+/* Return codes */
+#define DEATTACK_OK		0
+#define DEATTACK_DETECTED	1
+
+int detect_attack(unsigned char *buf, u_int32_t len, unsigned char IV[8]);
+#endif
diff --git a/getput.h b/getput.h
new file mode 100644
index 0000000..7b5d742
--- /dev/null
+++ b/getput.h
@@ -0,0 +1,64 @@
+/*
+
+getput.h
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Wed Jun 28 22:36:30 1995 ylo
+
+Macros for storing and retrieving data in msb first and lsb first order.
+
+*/
+
+/* RCSID("$Id: getput.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */
+
+#ifndef GETPUT_H
+#define GETPUT_H
+
+/*------------ macros for storing/extracting msb first words -------------*/
+
+#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]))
+
+#define GET_16BIT(cp) (((unsigned long)(unsigned char)(cp)[0] << 8) | \
+		       ((unsigned long)(unsigned char)(cp)[1]))
+
+#define PUT_32BIT(cp, value) do { \
+  (cp)[0] = (value) >> 24; \
+  (cp)[1] = (value) >> 16; \
+  (cp)[2] = (value) >> 8; \
+  (cp)[3] = (value); } while (0)
+
+#define PUT_16BIT(cp, value) do { \
+  (cp)[0] = (value) >> 8; \
+  (cp)[1] = (value); } while (0)
+
+/*------------ macros for storing/extracting lsb first words -------------*/
+
+#define GET_32BIT_LSB_FIRST(cp) \
+  (((unsigned long)(unsigned char)(cp)[0]) | \
+  ((unsigned long)(unsigned char)(cp)[1] << 8) | \
+  ((unsigned long)(unsigned char)(cp)[2] << 16) | \
+  ((unsigned long)(unsigned char)(cp)[3] << 24))
+
+#define GET_16BIT_LSB_FIRST(cp) \
+  (((unsigned long)(unsigned char)(cp)[0]) | \
+  ((unsigned long)(unsigned char)(cp)[1] << 8))
+
+#define PUT_32BIT_LSB_FIRST(cp, value) do { \
+  (cp)[0] = (value); \
+  (cp)[1] = (value) >> 8; \
+  (cp)[2] = (value) >> 16; \
+  (cp)[3] = (value) >> 24; } while (0)
+
+#define PUT_16BIT_LSB_FIRST(cp, value) do { \
+  (cp)[0] = (value); \
+  (cp)[1] = (value) >> 8; } while (0)
+
+#endif /* GETPUT_H */
+
diff --git a/helper.c b/helper.c
new file mode 100644
index 0000000..3b0402e
--- /dev/null
+++ b/helper.c
@@ -0,0 +1,108 @@
+/*
+**
+** OpenBSD emulation routines
+**
+** Damien Miller <djm@ibs.com.au>
+** 
+** Copyright 1999 Internet Business Solutions
+**
+** 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 DAMIEN MILLER OR INTERNET
+** BUSINESS SOLUTIONS 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.
+**
+** Except as contained in this notice, the name of Internet Business
+** Solutions shall not be used in advertising or otherwise to promote
+** the sale, use or other dealings in this Software without prior
+** written authorization from Internet Business Solutions.
+**
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "rc4.h"
+#include "xmalloc.h"
+
+#include "helper.h"
+
+void get_random_bytes(unsigned char *buf, int len);
+
+static rc4_t *rc4 = NULL;
+
+void setproctitle(const char *fmt, ...)
+{
+	/* FIXME */
+}
+
+unsigned char arc4random(void)
+{
+	unsigned char r;
+
+	if (rc4 == NULL)
+		arc4random_stir();
+	
+	rc4_getbytes(rc4, &r, 1);
+	
+	return(r);
+}
+
+void arc4random_stir(void)
+{
+	unsigned char rand_buf[32];
+	
+	if (rc4 == NULL)
+		rc4 = xmalloc(sizeof(*rc4));
+	
+	get_random_bytes(rand_buf, sizeof(rand_buf));
+	rc4_key(rc4, rand_buf, sizeof(rand_buf));
+}
+
+void get_random_bytes(unsigned char *buf, int len)
+{
+	int urandom;
+	int c;
+	
+	urandom = open("/dev/urandom", O_RDONLY);
+	if (urandom == -1)
+	{
+		fprintf(stderr, "Couldn't open /dev/urandom: %s", strerror(errno));
+		exit(1);
+	}
+	
+	c = read(urandom, buf, len);
+	if (c == -1)
+	{
+		fprintf(stderr, "Couldn't read from /dev/urandom: %s", strerror(errno));
+		exit(1);
+	}
+
+	if (c != len)
+	{
+		fprintf(stderr, "Short read from /dev/urandom");
+		exit(1);
+	}
+}
+
diff --git a/helper.h b/helper.h
new file mode 100644
index 0000000..2f09daa
--- /dev/null
+++ b/helper.h
@@ -0,0 +1,43 @@
+/*
+**
+** OpenBSD emulation routines
+**
+** Damien Miller <djm@ibs.com.au>
+** 
+** Copyright 1999 Internet Business Solutions
+**
+** 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 DAMIEN MILLER OR INTERNET
+** BUSINESS SOLUTIONS 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.
+**
+** Except as contained in this notice, the name of Internet Business
+** Solutions shall not be used in advertising or otherwise to promote
+** the sale, use or other dealings in this Software without prior
+** written authorization from Internet Business Solutions.
+**
+*/
+
+#ifndef _HELPER_H
+#define _HELPER_H
+
+unsigned char arc4random(void);
+void arc4random_stir(void);
+void setproctitle(const char *fmt, ...);
+
+#endif /* _HELPER_H */
diff --git a/hostfile.c b/hostfile.c
new file mode 100644
index 0000000..ca0fe88
--- /dev/null
+++ b/hostfile.c
@@ -0,0 +1,279 @@
+/*
+
+hostfile.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Thu Jun 29 07:10:56 1995 ylo
+
+Functions for manipulating the known hosts files.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: hostfile.c,v 1.1 1999/10/27 03:42:44 damien Exp $");
+
+#include "packet.h"
+#include "ssh.h"
+
+/* Reads a multiple-precision integer in hex from the buffer, and advances the
+   pointer.  The integer must already be initialized.  This function is
+   permitted to modify the buffer.  This leaves *cpp to point just beyond
+   the last processed (and maybe modified) character.  Note that this may
+   modify the buffer containing the number. */
+
+int
+auth_rsa_read_bignum(char **cpp, BIGNUM *value)
+{
+  char *cp = *cpp;
+  int len, old;
+
+  /* Skip any leading whitespace. */
+  for (; *cp == ' ' || *cp == '\t'; cp++)
+    ;
+
+  /* Check that it begins with a hex digit. */
+  if (*cp < '0' || *cp > '9')
+    return 0;
+
+  /* Save starting position. */
+  *cpp = cp;
+
+  /* Move forward until all hex digits skipped. */
+  for (; *cp >= '0' && *cp <= '9'; cp++)
+    ;
+
+  /* Compute the length of the hex number. */
+  len = cp - *cpp;
+
+  /* Save the old terminating character, and replace it by \0. */
+  old = *cp;
+  *cp = 0;
+
+  
+  /* Parse the number. */
+  if (BN_dec2bn(&value, *cpp) == 0)
+    return 0;
+
+  /* Restore old terminating character. */
+  *cp = old;
+
+  /* Move beyond the number and return success. */
+  *cpp = cp;
+  return 1;
+}
+
+/* Parses an RSA key (number of bits, e, n) from a string.  Moves the pointer
+   over the key.  Skips any whitespace at the beginning and at end. */
+
+int
+auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM *e, BIGNUM *n)
+{
+  unsigned int bits;
+  char *cp;
+
+  /* Skip leading whitespace. */
+  for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++)
+    ;
+
+  /* Get number of bits. */
+  if (*cp < '0' || *cp > '9')
+    return 0; /* Bad bit count... */
+  for (bits = 0; *cp >= '0' && *cp <= '9'; cp++)
+    bits = 10 * bits + *cp - '0';
+
+  /* Get public exponent. */
+  if (!auth_rsa_read_bignum(&cp, e))
+    return 0;
+
+  /* Get public modulus. */
+  if (!auth_rsa_read_bignum(&cp, n))
+    return 0;
+
+  /* Skip trailing whitespace. */
+  for (; *cp == ' ' || *cp == '\t'; cp++)
+    ;
+  
+  /* Return results. */
+  *cpp = cp;
+  *bitsp = bits;
+  return 1;
+}
+
+/* Tries to match the host name (which must be in all lowercase) against the
+   comma-separated sequence of subpatterns (each possibly preceded by ! to 
+   indicate negation).  Returns true if there is a positive match; zero
+   otherwise. */
+
+int
+match_hostname(const char *host, const char *pattern, unsigned int len)
+{
+  char sub[1024];
+  int negated;
+  int got_positive;
+  unsigned int i, subi;
+
+  got_positive = 0;
+  for (i = 0; i < len;)
+    {
+      /* Check if the subpattern is negated. */
+      if (pattern[i] == '!')
+	{
+	  negated = 1;
+	  i++;
+	}
+      else
+	negated = 0;
+      
+      /* Extract the subpattern up to a comma or end.  Convert the subpattern
+         to lowercase. */
+      for (subi = 0; 
+	   i < len && subi < sizeof(sub) - 1 && pattern[i] != ',';
+	   subi++, i++)
+	sub[subi] = isupper(pattern[i]) ? tolower(pattern[i]) : pattern[i];
+      /* If subpattern too long, return failure (no match). */
+      if (subi >= sizeof(sub) - 1)
+	return 0;
+
+      /* If the subpattern was terminated by a comma, skip the comma. */
+      if (i < len && pattern[i] == ',')
+	i++;
+      
+      /* Null-terminate the subpattern. */
+      sub[subi] = '\0';
+
+      /* Try to match the subpattern against the host name. */
+      if (match_pattern(host, sub)) {
+	if (negated)
+	  return 0;  /* Fail if host matches any negated subpattern. */
+        else
+	  got_positive = 1;
+      }
+    }
+
+  /* Return success if got a positive match.  If there was a negative match,
+     we have already returned zero and never get here. */
+  return got_positive;
+}
+
+/* Checks whether the given host (which must be in all lowercase) is 
+   already in the list of our known hosts.
+   Returns HOST_OK if the host is known and has the specified key,
+   HOST_NEW if the host is not known, and HOST_CHANGED if the host is known
+   but used to have a different host key. */
+
+HostStatus
+check_host_in_hostfile(const char *filename, 
+		       const char *host, unsigned int bits,
+		       BIGNUM *e, BIGNUM *n,
+		       BIGNUM *ke, BIGNUM *kn)
+{
+  FILE *f;
+  char line[8192];
+  unsigned int kbits, hostlen;
+  char *cp, *cp2;
+  HostStatus end_return;
+  struct stat st;
+
+  /* Open the file containing the list of known hosts. */
+  f = fopen(filename, "r");
+  if (!f)
+    {
+      if (stat(filename, &st) >= 0)
+	{
+	  packet_send_debug("Could not open %.900s for reading.", filename);
+	  packet_send_debug("If your home directory is on an NFS volume, it may need to be world-readable.");
+	}
+      return HOST_NEW;
+    }
+
+  /* Cache the length of the host name. */
+  hostlen = strlen(host);
+  
+  /* Return value when the loop terminates.  This is set to HOST_CHANGED if
+     we have seen a different key for the host and have not found the proper
+     one. */
+  end_return = HOST_NEW;
+
+  /* Go trough the file. */
+  while (fgets(line, sizeof(line), f))
+    {
+      cp = line;
+
+      /* Skip any leading whitespace. */
+      for (; *cp == ' ' || *cp == '\t'; cp++)
+	;
+
+      /* Ignore comment lines and empty lines. */
+      if (!*cp || *cp == '#' || *cp == '\n')
+	continue;
+      
+      /* Find the end of the host name portion. */
+      for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++)
+	;
+
+      /* Check if the host name matches. */
+      if (!match_hostname(host, cp, (unsigned int)(cp2 - cp)))
+	continue;
+      
+      /* Got a match.  Skip host name. */
+      cp = cp2;
+      
+      /* Extract the key from the line.  This will skip any leading 
+	 whitespace.  Ignore badly formatted lines. */
+      if (!auth_rsa_read_key(&cp, &kbits, ke, kn))
+	continue;
+
+      /* Check if the current key is the same as the previous one. */
+      if (kbits == bits && BN_cmp(ke, e) == 0 && BN_cmp(kn, n) == 0)
+	{
+	  /* Ok, they match. */
+	  fclose(f);
+	  return HOST_OK;
+	}
+      
+      /* They do not match.  We will continue to go through the file; however,
+	 we note that we will not return that it is new. */
+      end_return = HOST_CHANGED;
+    }
+  /* Clear variables and close the file. */
+  fclose(f);
+
+  /* Return either HOST_NEW or HOST_CHANGED, depending on whether we saw a
+     different key for the host. */
+  return end_return;
+}
+
+/* Appends an entry to the host file.  Returns false if the entry
+   could not be appended. */
+
+int
+add_host_to_hostfile(const char *filename, const char *host,
+		     unsigned int bits, BIGNUM *e, BIGNUM *n)
+{
+  FILE *f;
+  char *buf;
+ 
+  /* Open the file for appending. */
+  f = fopen(filename, "a");
+  if (!f)
+    return 0;
+
+  /* Print the host name and key to the file. */
+  fprintf(f, "%s %u ", host, bits);
+  buf = BN_bn2dec(e);
+  assert(buf != NULL);
+  fprintf(f, "%s ", buf);
+  free (buf);
+  buf = BN_bn2dec(n);
+  assert(buf != NULL);
+  fprintf(f, "%s\n", buf);
+  free (buf);
+
+  /* Close the file. */
+  fclose(f);
+  return 1;
+}
diff --git a/includes.h b/includes.h
new file mode 100644
index 0000000..862dbd6
--- /dev/null
+++ b/includes.h
@@ -0,0 +1,78 @@
+/*
+
+includes.h
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Thu Mar 23 16:29:37 1995 ylo
+
+This file includes most of the needed system headers.
+
+*/
+
+#ifndef INCLUDES_H
+#define INCLUDES_H
+
+#define RCSID(msg) \
+static /**/const char *const rcsid[] = { (char *)rcsid, "\100(#)" msg }
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/un.h>
+#include <sys/resource.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/tcp.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <endian.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <signal.h>
+#include <termios.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <pwd.h>
+#include <grp.h>
+#include <unistd.h>
+#include <time.h>
+#include <paths.h>
+#include <dirent.h>
+
+#include "version.h"
+
+#include "helper.h"
+#include "mktemp.h"
+#include "strlcpy.h"
+
+/* Define this to be the path of the xauth program. */
+#ifndef XAUTH_PATH
+#define XAUTH_PATH "/usr/X11R6/bin/xauth"
+#endif /* XAUTH_PATH */
+
+/* Define this to be the path of the rsh program. */
+#ifndef _PATH_RSH
+#define _PATH_RSH "/usr/bin/rsh"
+#endif /* _PATH_RSH */
+
+/* Define this to use pipes instead of socketpairs for communicating with the
+   client program.  Socketpairs do not seem to work on all systems. */
+#define USE_PIPES 1
+
+#endif /* INCLUDES_H */
diff --git a/log-client.c b/log-client.c
new file mode 100644
index 0000000..1792ba8
--- /dev/null
+++ b/log-client.c
@@ -0,0 +1,138 @@
+/*
+
+log-client.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Mon Mar 20 21:13:40 1995 ylo
+
+Client-side versions of debug(), log(), etc.  These print to stderr.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: log-client.c,v 1.1 1999/10/27 03:42:44 damien Exp $");
+
+#include "xmalloc.h"
+#include "ssh.h"
+
+static int log_debug = 0;
+static int log_quiet = 0;
+
+void log_init(char *av0, int on_stderr, int debug, int quiet,
+	      SyslogFacility facility)
+{
+  log_debug = debug;
+  log_quiet = quiet;
+}
+
+void log(const char *fmt, ...)
+{
+  va_list args;
+
+  if (log_quiet)
+    return;
+  va_start(args, fmt);
+  vfprintf(stderr, fmt, args);
+  fprintf(stderr, "\r\n");
+  va_end(args);
+}
+
+void debug(const char *fmt, ...)
+{
+  va_list args;
+  if (log_quiet || !log_debug)
+    return;
+  va_start(args, fmt);
+  fprintf(stderr, "debug: ");
+  vfprintf(stderr, fmt, args);
+  fprintf(stderr, "\r\n");
+  va_end(args);
+}
+
+void error(const char *fmt, ...)
+{
+  va_list args;
+  if (log_quiet)
+    return;
+  va_start(args, fmt);
+  vfprintf(stderr, fmt, args);
+  fprintf(stderr, "\r\n");
+  va_end(args);
+}
+
+struct fatal_cleanup
+{
+  struct fatal_cleanup *next;
+  void (*proc)(void *);
+  void *context;
+};
+
+static struct fatal_cleanup *fatal_cleanups = NULL;
+
+/* Registers a cleanup function to be called by fatal() before exiting. */
+
+void fatal_add_cleanup(void (*proc)(void *), void *context)
+{
+  struct fatal_cleanup *cu;
+
+  cu = xmalloc(sizeof(*cu));
+  cu->proc = proc;
+  cu->context = context;
+  cu->next = fatal_cleanups;
+  fatal_cleanups = cu;
+}
+
+/* Removes a cleanup frunction to be called at fatal(). */
+
+void fatal_remove_cleanup(void (*proc)(void *context), void *context)
+{
+  struct fatal_cleanup **cup, *cu;
+  
+  for (cup = &fatal_cleanups; *cup; cup = &cu->next)
+    {
+      cu = *cup;
+      if (cu->proc == proc && cu->context == context)
+	{
+	  *cup = cu->next;
+	  xfree(cu);
+	  return;
+	}
+    }
+  fatal("fatal_remove_cleanup: no such cleanup function: 0x%lx 0x%lx\n",
+	(unsigned long)proc, (unsigned long)context);
+}
+
+/* Function to display an error message and exit.  This is in this file because
+   this needs to restore terminal modes before exiting.  See log-client.c
+   for other related functions. */
+
+void fatal(const char *fmt, ...)
+{
+  va_list args;
+  struct fatal_cleanup *cu, *next_cu;
+  static int fatal_called = 0;
+  
+  if (!fatal_called)
+    {
+      fatal_called = 1;
+
+      /* Call cleanup functions. */
+      for (cu = fatal_cleanups; cu; cu = next_cu)
+	{
+	  next_cu = cu->next;
+	  (*cu->proc)(cu->context);
+	}
+    }
+
+  va_start(args, fmt);
+  vfprintf(stderr, fmt, args);
+  fprintf(stderr, "\r\n");
+  va_end(args);
+  exit(255);
+}
+
+/* fatal() is in ssh.c so that it can properly reset terminal modes. */
diff --git a/log-server.c b/log-server.c
new file mode 100644
index 0000000..fce96b0
--- /dev/null
+++ b/log-server.c
@@ -0,0 +1,233 @@
+/*
+
+log-server.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Mon Mar 20 21:19:30 1995 ylo
+
+Server-side versions of debug(), log(), etc.  These normally send the output
+to the system log.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: log-server.c,v 1.1 1999/10/27 03:42:44 damien Exp $");
+
+#include <syslog.h>
+#include "packet.h"
+#include "xmalloc.h"
+#include "ssh.h"
+
+static int log_debug = 0;
+static int log_quiet = 0;
+static int log_on_stderr = 0;
+
+/* Initialize the log.
+     av0	program name (should be argv[0])
+     on_stderr	print also on stderr
+     debug	send debugging messages to system log
+     quiet	don\'t log anything
+     */
+
+void log_init(char *av0, int on_stderr, int debug, int quiet, 
+	      SyslogFacility facility)
+{
+  int log_facility;
+  
+  switch (facility)
+    {
+    case SYSLOG_FACILITY_DAEMON:
+      log_facility = LOG_DAEMON;
+      break;
+    case SYSLOG_FACILITY_USER:
+      log_facility = LOG_USER;
+      break;
+    case SYSLOG_FACILITY_AUTH:
+      log_facility = LOG_AUTH;
+      break;
+    case SYSLOG_FACILITY_LOCAL0:
+      log_facility = LOG_LOCAL0;
+      break;
+    case SYSLOG_FACILITY_LOCAL1:
+      log_facility = LOG_LOCAL1;
+      break;
+    case SYSLOG_FACILITY_LOCAL2:
+      log_facility = LOG_LOCAL2;
+      break;
+    case SYSLOG_FACILITY_LOCAL3:
+      log_facility = LOG_LOCAL3;
+      break;
+    case SYSLOG_FACILITY_LOCAL4:
+      log_facility = LOG_LOCAL4;
+      break;
+    case SYSLOG_FACILITY_LOCAL5:
+      log_facility = LOG_LOCAL5;
+      break;
+    case SYSLOG_FACILITY_LOCAL6:
+      log_facility = LOG_LOCAL6;
+      break;
+    case SYSLOG_FACILITY_LOCAL7:
+      log_facility = LOG_LOCAL7;
+      break;
+    default:
+      fprintf(stderr, "Unrecognized internal syslog facility code %d\n",
+	      (int)facility);
+      exit(1);
+    }
+
+  log_debug = debug;
+  log_quiet = quiet;
+  log_on_stderr = on_stderr;
+  closelog(); /* Close any previous log. */
+  openlog(av0, LOG_PID, log_facility);
+}
+
+#define MSGBUFSIZE 1024
+
+#define DECL_MSGBUF char msgbuf[MSGBUFSIZE]
+
+/* Log this message (information that usually should go to the log). */
+
+void log(const char *fmt, ...)
+{
+  va_list args;
+  DECL_MSGBUF;
+  if (log_quiet)
+    return;
+  va_start(args, fmt);
+  vsnprintf(msgbuf, MSGBUFSIZE, fmt, args);
+  va_end(args);
+  if (log_on_stderr)
+    fprintf(stderr, "log: %s\n", msgbuf);
+  syslog(LOG_INFO, "log: %.500s", msgbuf);
+}
+
+/* Debugging messages that should not be logged during normal operation. */
+
+void debug(const char *fmt, ...)
+{
+  va_list args;
+  DECL_MSGBUF;
+  if (!log_debug || log_quiet)
+    return;
+  va_start(args, fmt);
+  vsnprintf(msgbuf, MSGBUFSIZE, fmt, args);
+  va_end(args);
+  if (log_on_stderr)
+    fprintf(stderr, "debug: %s\n", msgbuf);
+  syslog(LOG_DEBUG, "debug: %.500s", msgbuf);
+}
+
+/* Error messages that should be logged. */
+
+void error(const char *fmt, ...)
+{
+  va_list args;
+  DECL_MSGBUF;
+  if (log_quiet)
+    return;
+  va_start(args, fmt);
+  vsnprintf(msgbuf, MSGBUFSIZE, fmt, args);
+  va_end(args);
+  if (log_on_stderr)
+    fprintf(stderr, "error: %s\n", msgbuf);
+  syslog(LOG_ERR, "error: %.500s", msgbuf);
+}
+
+struct fatal_cleanup
+{
+  struct fatal_cleanup *next;
+  void (*proc)(void *);
+  void *context;
+};
+
+static struct fatal_cleanup *fatal_cleanups = NULL;
+
+/* Registers a cleanup function to be called by fatal() before exiting. */
+
+void fatal_add_cleanup(void (*proc)(void *), void *context)
+{
+  struct fatal_cleanup *cu;
+
+  cu = xmalloc(sizeof(*cu));
+  cu->proc = proc;
+  cu->context = context;
+  cu->next = fatal_cleanups;
+  fatal_cleanups = cu;
+}
+
+/* Removes a cleanup frunction to be called at fatal(). */
+
+void fatal_remove_cleanup(void (*proc)(void *context), void *context)
+{
+  struct fatal_cleanup **cup, *cu;
+  
+  for (cup = &fatal_cleanups; *cup; cup = &cu->next)
+    {
+      cu = *cup;
+      if (cu->proc == proc && cu->context == context)
+	{
+	  *cup = cu->next;
+	  xfree(cu);
+	  return;
+	}
+    }
+  fatal("fatal_remove_cleanup: no such cleanup function: 0x%lx 0x%lx\n",
+	(unsigned long)proc, (unsigned long)context);
+}
+
+/* Fatal messages.  This function never returns. */
+
+void fatal(const char *fmt, ...)
+{
+  va_list args;
+  struct fatal_cleanup *cu, *next_cu;
+  static int fatal_called = 0;
+#if defined(KRB4)
+  extern char *ticket;
+#endif /* KRB4 */
+  DECL_MSGBUF;
+
+  if (log_quiet)
+    exit(1);
+  va_start(args, fmt);
+  vsnprintf(msgbuf, MSGBUFSIZE, fmt, args);
+  va_end(args);
+  if (log_on_stderr)
+    fprintf(stderr, "fatal: %s\n", msgbuf);
+  syslog(LOG_ERR, "fatal: %.500s", msgbuf);
+
+  if (fatal_called)
+    exit(1);
+  fatal_called = 1;
+
+  /* Call cleanup functions. */
+  for (cu = fatal_cleanups; cu; cu = next_cu)
+    {
+      next_cu = cu->next;
+      debug("Calling cleanup 0x%lx(0x%lx)",
+	    (unsigned long)cu->proc, (unsigned long)cu->context);
+      (*cu->proc)(cu->context);
+    }
+#if defined(KRB4)
+  /* If you forwarded a ticket you get one shot for proper
+     authentication. */
+  /* If tgt was passed unlink file */
+  if (ticket)
+    {
+      if (strcmp(ticket,"none"))
+	unlink(ticket);
+      else
+	ticket = NULL;
+    }
+#endif /* KRB4 */
+
+  /* If local XAUTHORITY was created, remove it. */
+  if (xauthfile) unlink(xauthfile);
+
+  exit(1);
+}
diff --git a/login.c b/login.c
new file mode 100644
index 0000000..0c1e61b
--- /dev/null
+++ b/login.c
@@ -0,0 +1,118 @@
+/*
+
+login.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Fri Mar 24 14:51:08 1995 ylo
+
+This file performs some of the things login(1) normally does.  We cannot
+easily use something like login -p -h host -f user, because there are
+several different logins around, and it is hard to determined what kind of
+login the current system has.  Also, we want to be able to execute commands
+on a tty.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: login.c,v 1.1 1999/10/27 03:42:44 damien Exp $");
+
+#include <utmp.h>
+#include "ssh.h"
+
+/* Returns the time when the user last logged in.  Returns 0 if the 
+   information is not available.  This must be called before record_login. 
+   The host the user logged in from will be returned in buf. */
+
+/* Returns the time when the user last logged in (or 0 if no previous login
+   is found).  The name of the host used last time is returned in buf. */
+
+unsigned long get_last_login_time(uid_t uid, const char *logname,
+				  char *buf, unsigned int bufsize)
+{
+  struct lastlog ll;
+  char *lastlog;
+  int fd;
+
+  lastlog = _PATH_LASTLOG;
+
+  buf[0] = '\0';
+
+  fd = open(lastlog, O_RDONLY);
+  if (fd < 0)
+    return 0;
+  lseek(fd, (off_t)((long)uid * sizeof(ll)), SEEK_SET);
+  if (read(fd, &ll, sizeof(ll)) != sizeof(ll))
+    {
+      close(fd);
+      return 0;
+    }
+  close(fd);
+  if (bufsize > sizeof(ll.ll_host) + 1)
+    bufsize = sizeof(ll.ll_host) + 1;
+  strncpy(buf, ll.ll_host, bufsize - 1);
+  buf[bufsize - 1] = 0;
+  return ll.ll_time;
+}
+
+/* Records that the user has logged in.  I these parts of operating systems
+   were more standardized. */
+
+void record_login(int pid, const char *ttyname, const char *user, uid_t uid,
+		  const char *host, struct sockaddr_in *addr)
+{
+  int fd;
+  struct lastlog ll;
+  char *lastlog;
+
+  struct utmp u;
+  const char *utmp, *wtmp;
+
+  /* Construct an utmp/wtmp entry. */
+  memset(&u, 0, sizeof(u));
+  strncpy(u.ut_line, ttyname + 5, sizeof(u.ut_line));
+  u.ut_time = time(NULL);
+  strncpy(u.ut_name, user, sizeof(u.ut_name));
+  strncpy(u.ut_host, host, sizeof(u.ut_host));
+
+  /* Figure out the file names. */
+  utmp = _PATH_UTMP;
+  wtmp = _PATH_WTMP;
+  
+  login(&u);
+
+  lastlog = _PATH_LASTLOG;
+
+  /* Update lastlog unless actually recording a logout. */
+  if (strcmp(user, "") != 0)
+    {
+      /* It is safer to bzero the lastlog structure first because some
+	 systems might have some extra fields in it (e.g. SGI) */
+      memset(&ll, 0, sizeof(ll));
+
+      /* Update lastlog. */
+      ll.ll_time = time(NULL);
+      strncpy(ll.ll_line, ttyname + 5, sizeof(ll.ll_line));
+      strncpy(ll.ll_host, host, sizeof(ll.ll_host));
+      fd = open(lastlog, O_RDWR);
+      if (fd >= 0)
+	{
+	  lseek(fd, (off_t)((long)uid * sizeof(ll)), SEEK_SET);
+	  if (write(fd, &ll, sizeof(ll)) != sizeof(ll))
+	    log("Could not write %.100s: %.100s", lastlog, strerror(errno));
+	  close(fd);
+	}
+    }
+}
+  
+/* Records that the user has logged out. */
+
+void record_logout(int pid, const char *ttyname)
+{
+  const char *line = ttyname + 5; /* /dev/ttyq8 -> ttyq8 */
+  if (logout(line))
+    logwtmp(line, "", "");
+}
diff --git a/match.c b/match.c
new file mode 100644
index 0000000..b7a7d33
--- /dev/null
+++ b/match.c
@@ -0,0 +1,78 @@
+/*
+
+match.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Thu Jun 22 01:17:50 1995 ylo
+
+Simple pattern matching, with '*' and '?' as wildcards.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: match.c,v 1.1 1999/10/27 03:42:44 damien Exp $");
+
+#include "ssh.h"
+
+/* Returns true if the given string matches the pattern (which may contain
+   ? and * as wildcards), and zero if it does not match. */
+	  
+int match_pattern(const char *s, const char *pattern)
+{
+  while (1)
+    {
+      /* If at end of pattern, accept if also at end of string. */
+      if (!*pattern)
+        return !*s;
+
+      /* Process '*'. */
+      if (*pattern == '*')
+        {
+	  /* Skip the asterisk. */
+	  pattern++;
+
+	  /* If at end of pattern, accept immediately. */
+          if (!*pattern)
+            return 1;
+
+	  /* If next character in pattern is known, optimize. */
+          if (*pattern != '?' && *pattern != '*')
+            {
+	      /* Look instances of the next character in pattern, and try
+		 to match starting from those. */
+              for (; *s; s++)
+                if (*s == *pattern &&
+                    match_pattern(s + 1, pattern + 1))
+                  return 1;
+	      /* Failed. */
+              return 0;
+            }
+
+	  /* Move ahead one character at a time and try to match at each
+	     position. */
+          for (; *s; s++)
+            if (match_pattern(s, pattern))
+              return 1;
+	  /* Failed. */
+          return 0;
+        }
+
+      /* There must be at least one more character in the string.  If we are
+	 at the end, fail. */
+      if (!*s)
+        return 0;
+
+      /* Check if the next character of the string is acceptable. */
+      if (*pattern != '?' && *pattern != *s)
+	return 0;
+      
+      /* Move to the next character, both in string and in pattern. */
+      s++;
+      pattern++;
+    }
+  /*NOTREACHED*/
+}
diff --git a/mktemp.c b/mktemp.c
new file mode 100644
index 0000000..919c531
--- /dev/null
+++ b/mktemp.c
@@ -0,0 +1,181 @@
+/* THIS FILE HAS BEEN MODIFIED FROM THE ORIGINAL OPENBSD SOURCE */
+/* Changes: Removed mktemp */
+
+/*
+ * Copyright (c) 1987, 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char rcsid[] = "$OpenBSD: mktemp.c,v 1.13 1998/06/30 23:03:13 deraadt Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+
+static int _gettemp __P((char *, int *, int, int));
+
+int
+mkstemps(path, slen)
+	char *path;
+	int slen;
+{
+	int fd;
+
+	return (_gettemp(path, &fd, 0, slen) ? fd : -1);
+}
+
+int
+mkstemp(path)
+	char *path;
+{
+	int fd;
+
+	return (_gettemp(path, &fd, 0, 0) ? fd : -1);
+}
+
+char *
+mkdtemp(path)
+	char *path;
+{
+	return(_gettemp(path, (int *)NULL, 1, 0) ? path : (char *)NULL);
+}
+
+static int
+_gettemp(path, doopen, domkdir, slen)
+	char *path;
+	register int *doopen;
+	int domkdir;
+	int slen;
+{
+	register char *start, *trv, *suffp;
+	struct stat sbuf;
+	int pid, rval;
+
+	if (doopen && domkdir) {
+		errno = EINVAL;
+		return(0);
+	}
+
+	for (trv = path; *trv; ++trv)
+		;
+	trv -= slen;
+	suffp = trv;
+	--trv;
+	if (trv < path) {
+		errno = EINVAL;
+		return (0);
+	}
+	pid = getpid();
+	while (*trv == 'X' && pid != 0) {
+		*trv-- = (pid % 10) + '0';
+		pid /= 10;
+	}
+	while (*trv == 'X') {
+		char c;
+
+		pid = (arc4random() & 0xffff) % (26+26);
+		if (pid < 26)
+			c = pid + 'A';
+		else
+			c = (pid - 26) + 'a';
+		*trv-- = c;
+	}
+	start = trv + 1;
+
+	/*
+	 * check the target directory; if you have six X's and it
+	 * doesn't exist this runs for a *very* long time.
+	 */
+	if (doopen || domkdir) {
+		for (;; --trv) {
+			if (trv <= path)
+				break;
+			if (*trv == '/') {
+				*trv = '\0';
+				rval = stat(path, &sbuf);
+				*trv = '/';
+				if (rval != 0)
+					return(0);
+				if (!S_ISDIR(sbuf.st_mode)) {
+					errno = ENOTDIR;
+					return(0);
+				}
+				break;
+			}
+		}
+	}
+
+	for (;;) {
+		if (doopen) {
+			if ((*doopen =
+			    open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0)
+				return(1);
+			if (errno != EEXIST)
+				return(0);
+		} else if (domkdir) {
+			if (mkdir(path, 0700) == 0)
+				return(1);
+			if (errno != EEXIST)
+				return(0);
+		} else if (lstat(path, &sbuf))
+			return(errno == ENOENT ? 1 : 0);
+
+		/* tricky little algorithm for backward compatibility */
+		for (trv = start;;) {
+			if (!*trv)
+				return (0);
+			if (*trv == 'Z') {
+				if (trv == suffp)
+					return (0);
+				*trv++ = 'a';
+			} else {
+				if (isdigit(*trv))
+					*trv = 'a';
+				else if (*trv == 'z')	/* inc from z to A */
+					*trv = 'A';
+				else {
+					if (trv == suffp)
+						return (0);
+					++*trv;
+				}
+				break;
+			}
+		}
+	}
+	/*NOTREACHED*/
+}
diff --git a/mktemp.h b/mktemp.h
new file mode 100644
index 0000000..5d38005
--- /dev/null
+++ b/mktemp.h
@@ -0,0 +1,7 @@
+#ifndef _MKTEMP_H
+#define _MKTEMP_H
+int mkstemps(char *path, int slen);
+int mkstemp(char *path);
+char *mkdtemp(char *path);
+
+#endif /* _MKTEMP_H */
diff --git a/mpaux.c b/mpaux.c
new file mode 100644
index 0000000..fd2c180
--- /dev/null
+++ b/mpaux.c
@@ -0,0 +1,46 @@
+/*
+
+mpaux.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Sun Jul 16 04:29:30 1995 ylo
+
+This file contains various auxiliary functions related to multiple
+precision integers.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: mpaux.c,v 1.1 1999/10/27 03:42:44 damien Exp $");
+
+#include <openssl/bn.h>
+#include "getput.h"
+#include "xmalloc.h"
+
+#include <openssl/md5.h>
+
+void
+compute_session_id(unsigned char session_id[16],
+		   unsigned char cookie[8],
+		   unsigned int host_key_bits,
+		   BIGNUM *host_key_n,
+		   unsigned int session_key_bits,
+		   BIGNUM *session_key_n)
+{
+  unsigned int bytes = (host_key_bits + 7) / 8 + (session_key_bits + 7) / 8 + 8;
+  unsigned char *buf = xmalloc(bytes);
+  MD5_CTX md;
+  
+  BN_bn2bin(host_key_n, buf);
+  BN_bn2bin(session_key_n, buf + (host_key_bits + 7 ) / 8);
+  memcpy(buf + (host_key_bits + 7) / 8 + (session_key_bits + 7) / 8,
+	 cookie, 8);
+  MD5_Init(&md);
+  MD5_Update(&md, buf, bytes);
+  MD5_Final(session_id, &md);
+  xfree(buf);
+}
diff --git a/mpaux.h b/mpaux.h
new file mode 100644
index 0000000..3ad0681
--- /dev/null
+++ b/mpaux.h
@@ -0,0 +1,32 @@
+/*
+
+mpaux.h
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Sun Jul 16 04:29:30 1995 ylo
+
+This file contains various auxiliary functions related to multiple
+precision integers.
+
+*/
+
+/* RCSID("$Id: mpaux.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */
+
+#ifndef MPAUX_H
+#define MPAUX_H
+
+/* Computes a 16-byte session id in the global variable session_id.
+   The session id is computed by concatenating the linearized, msb
+   first representations of host_key_n, session_key_n, and the cookie. */
+void compute_session_id(unsigned char session_id[16],
+			unsigned char cookie[8],
+			unsigned int host_key_bits,
+			BIGNUM *host_key_n,
+			unsigned int session_key_bits,
+			BIGNUM *session_key_n);
+
+#endif /* MPAUX_H */
diff --git a/nchan.c b/nchan.c
new file mode 100644
index 0000000..fcaeae4
--- /dev/null
+++ b/nchan.c
@@ -0,0 +1,187 @@
+#include "includes.h"
+RCSID("$Id: nchan.c,v 1.1 1999/10/27 03:42:44 damien Exp $");
+
+#include "ssh.h"
+
+#include "buffer.h"
+#include "packet.h"
+#include "channels.h"
+#include "nchan.h"
+
+static void chan_send_ieof(Channel *c);
+static void chan_send_oclose(Channel *c);
+static void chan_shutdown_write(Channel *c);
+static void chan_shutdown_read(Channel *c);
+static void chan_delele_if_full_closed(Channel *c);
+
+/*
+ * EVENTS: update channel input/output states
+ *	   execute ACTIONS
+ */
+/* events concerning the INPUT from socket for channel (istate) */
+void
+chan_rcvd_oclose(Channel *c){
+	switch(c->istate){
+	case CHAN_INPUT_WAIT_OCLOSE:
+		debug("channel %d: INPUT_WAIT_OCLOSE -> INPUT_CLOSED [rcvd OCLOSE]", c->self);
+		c->istate=CHAN_INPUT_CLOSED;
+		chan_delele_if_full_closed(c);
+		break;
+	case CHAN_INPUT_OPEN:
+		debug("channel %d: INPUT_OPEN -> INPUT_CLOSED [rvcd OCLOSE, send IEOF]", c->self);
+		chan_shutdown_read(c);
+		chan_send_ieof(c);
+		c->istate=CHAN_INPUT_CLOSED;
+		chan_delele_if_full_closed(c);
+		break;
+	default:
+		debug("protocol error: chan_rcvd_oclose %d for istate %d",c->self,c->istate);
+		break;
+	}
+}
+void
+chan_read_failed(Channel *c){
+	switch(c->istate){
+	case CHAN_INPUT_OPEN:
+		debug("channel %d: INPUT_OPEN -> INPUT_WAIT_DRAIN [read failed]", c->self);
+		chan_shutdown_read(c);
+		c->istate=CHAN_INPUT_WAIT_DRAIN;
+		break;
+	default:
+		debug("internal error: we do not read, but chan_read_failed %d for istate %d",
+			c->self,c->istate);
+		break;
+	}
+}
+void
+chan_ibuf_empty(Channel *c){
+	if(buffer_len(&c->input)){
+		debug("internal error: chan_ibuf_empty %d for non empty buffer",c->self);
+		return;
+	}
+	switch(c->istate){
+	case CHAN_INPUT_WAIT_DRAIN:
+		debug("channel %d: INPUT_WAIT_DRAIN -> INPUT_WAIT_OCLOSE [inbuf empty, send IEOF]", c->self);
+		chan_send_ieof(c);
+		c->istate=CHAN_INPUT_WAIT_OCLOSE;
+		break;
+	default:
+		debug("internal error: chan_ibuf_empty %d for istate %d",c->self,c->istate);
+		break;
+	}
+}
+/* events concerning the OUTPUT from channel for socket (ostate) */
+void
+chan_rcvd_ieof(Channel *c){
+	switch(c->ostate){
+	case CHAN_OUTPUT_OPEN:
+		debug("channel %d: OUTPUT_OPEN -> OUTPUT_WAIT_DRAIN [rvcd IEOF]", c->self);
+		c->ostate=CHAN_OUTPUT_WAIT_DRAIN;
+		break;
+	case CHAN_OUTPUT_WAIT_IEOF:
+		debug("channel %d: OUTPUT_WAIT_IEOF -> OUTPUT_CLOSED [rvcd IEOF]", c->self);
+		c->ostate=CHAN_OUTPUT_CLOSED;
+		chan_delele_if_full_closed(c);
+		break;
+	default:
+		debug("protocol error: chan_rcvd_ieof %d for ostate %d", c->self,c->ostate);
+		break;
+	}
+}
+void
+chan_write_failed(Channel *c){
+	switch(c->ostate){
+	case CHAN_OUTPUT_OPEN:
+		debug("channel %d: OUTPUT_OPEN -> OUTPUT_WAIT_IEOF [write failed]", c->self);
+		chan_send_oclose(c);
+		c->ostate=CHAN_OUTPUT_WAIT_IEOF;
+		break;
+	case CHAN_OUTPUT_WAIT_DRAIN:
+		debug("channel %d: OUTPUT_WAIT_DRAIN -> OUTPUT_CLOSED [write failed]", c->self);
+		chan_send_oclose(c);
+		c->ostate=CHAN_OUTPUT_CLOSED;
+		chan_delele_if_full_closed(c);
+		break;
+	default:
+		debug("internal error: chan_write_failed %d for ostate %d",c->self,c->ostate);
+		break;
+	}
+}
+void
+chan_obuf_empty(Channel *c){
+	if(buffer_len(&c->output)){
+		debug("internal error: chan_obuf_empty %d for non empty buffer",c->self);
+		return;
+	}
+	switch(c->ostate){
+	case CHAN_OUTPUT_WAIT_DRAIN:
+		debug("channel %d: OUTPUT_WAIT_DRAIN -> OUTPUT_CLOSED [obuf empty, send OCLOSE]", c->self);
+		chan_send_oclose(c);
+		c->ostate=CHAN_OUTPUT_CLOSED;
+		chan_delele_if_full_closed(c);
+		break;
+	default:
+		debug("internal error: chan_obuf_empty %d for ostate %d",c->self,c->ostate);
+		break;
+	}
+}
+/*
+ * ACTIONS: should never update c->istate or c->ostate
+ */
+static void
+chan_send_ieof(Channel *c){
+	switch(c->istate){
+	case CHAN_INPUT_OPEN:
+	case CHAN_INPUT_WAIT_DRAIN:
+		packet_start(SSH_MSG_CHANNEL_INPUT_EOF);
+		packet_put_int(c->remote_id);
+		packet_send();
+		break;
+	default:
+		debug("internal error: channel %d: cannot send IEOF for istate %d",c->self,c->istate);
+		break;
+	}
+}
+static void
+chan_send_oclose(Channel *c){
+	switch(c->ostate){
+	case CHAN_OUTPUT_OPEN:
+	case CHAN_OUTPUT_WAIT_DRAIN:
+		chan_shutdown_write(c);
+		buffer_consume(&c->output, buffer_len(&c->output));
+		packet_start(SSH_MSG_CHANNEL_OUTPUT_CLOSE);
+		packet_put_int(c->remote_id);
+		packet_send();
+		break;
+	default:
+		debug("internal error: channel %d: cannot send OCLOSE for ostate %d",c->self,c->istate);
+		break;
+	}
+}
+/* helper */
+static void
+chan_shutdown_write(Channel *c){
+	debug("channel %d: shutdown_write", c->self);
+	if(shutdown(c->sock, SHUT_WR)<0)
+		error("chan_shutdown_write failed for #%d/fd%d: %.100s",
+			c->self, c->sock, strerror(errno));
+}
+static void
+chan_shutdown_read(Channel *c){
+	debug("channel %d: shutdown_read", c->self);
+	if(shutdown(c->sock, SHUT_RD)<0)
+		error("chan_shutdown_read failed for #%d/fd%d: %.100s",
+			c->self, c->sock, strerror(errno));
+}
+static void
+chan_delele_if_full_closed(Channel *c){
+	if(c->istate==CHAN_INPUT_CLOSED && c->ostate==CHAN_OUTPUT_CLOSED){
+		debug("channel %d: closing", c->self);
+		channel_free(c->self);
+	}
+}
+void
+chan_init_iostates(Channel *c){
+	c->ostate=CHAN_OUTPUT_OPEN;
+	c->istate=CHAN_INPUT_OPEN;
+}
diff --git a/nchan.h b/nchan.h
new file mode 100644
index 0000000..16d360d
--- /dev/null
+++ b/nchan.h
@@ -0,0 +1,57 @@
+/* RCSID("$Id: nchan.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */
+
+#ifndef NCHAN_H
+#define NCHAN_H
+
+/*
+ * SSH Protocol 1.5 aka New Channel Protocol
+ * Thanks to Martina, Axel and everyone who left Erlangen, leaving me bored.
+ * Written by Markus Friedl in October 1999
+ * 
+ * Protocol versions 1.3 and 1.5 differ in the handshake protocol used for the
+ * tear down of channels:
+ * 
+ * 1.3:	strict request-ack-protocol:
+ * 	CLOSE	->
+ * 		<-  CLOSE_CONFIRM
+ * 
+ * 1.5:	uses variations of:
+ * 	IEOF	->
+ * 		<-  OCLOSE
+ * 		<-  IEOF
+ * 	OCLOSE	->
+ * 	i.e. both sides have to close the channel
+ * 
+ * See the debugging output from 'ssh -v' and 'sshd -d' of
+ * ssh-1.2.27 as an example.
+ * 
+ */
+
+/* ssh-proto-1.5 overloads prot-1.3-message-types */
+#define SSH_MSG_CHANNEL_INPUT_EOF	SSH_MSG_CHANNEL_CLOSE
+#define SSH_MSG_CHANNEL_OUTPUT_CLOSE	SSH_MSG_CHANNEL_CLOSE_CONFIRMATION
+
+/* possible input states */
+#define CHAN_INPUT_OPEN			0x01
+#define CHAN_INPUT_WAIT_DRAIN		0x02
+#define CHAN_INPUT_WAIT_OCLOSE		0x04
+#define CHAN_INPUT_CLOSED		0x08
+
+/* possible output states */
+#define CHAN_OUTPUT_OPEN		0x10
+#define CHAN_OUTPUT_WAIT_DRAIN		0x20
+#define CHAN_OUTPUT_WAIT_IEOF		0x40
+#define CHAN_OUTPUT_CLOSED		0x80
+
+/* EVENTS for the input state */
+void chan_rcvd_oclose(Channel *c);
+void chan_read_failed(Channel *c);
+void chan_ibuf_empty(Channel *c);
+
+/* EVENTS for the output state */
+void chan_rcvd_ieof(Channel *c);
+void chan_write_failed(Channel *c);
+void chan_obuf_empty(Channel *c);
+
+void chan_init_iostates(Channel *c);
+#endif
diff --git a/nchan.ms b/nchan.ms
new file mode 100644
index 0000000..b01512f
--- /dev/null
+++ b/nchan.ms
@@ -0,0 +1,71 @@
+.TL
+OpenSSH Channel Close Protocol 1.5 Implementation
+.SH
+Channel Input State Diagram
+.PS
+reset
+l=1
+s=1.2
+ellipsewid=s*ellipsewid
+boxwid=s*boxwid
+ellipseht=s*ellipseht
+S1: ellipse "INPUT" "OPEN"
+move right 2*l from last ellipse.e
+S4: ellipse "INPUT" "CLOSED"
+move down l from last ellipse.s
+S3: ellipse "INPUT" "WAIT" "OCLOSED"
+move down l from 1st ellipse.s
+S2: ellipse "INPUT" "WAIT" "DRAIN"
+arrow "" "rcvd OCLOSE/" "shutdown_read" "send IEOF" from S1.e to S4.w
+arrow "ibuf_empty/" "send IEOF" from S2.e to S3.w
+arrow from S1.s to S2.n
+box invis "read_failed/" "shutdown_read" with .e at last arrow.c
+arrow  from S3.n to S4.s
+box invis "rcvd OCLOSE/" "-" with .w at last arrow.c
+ellipse wid .9*ellipsewid ht .9*ellipseht at S4
+arrow "start" "" from S1.w+(-0.5,0) to S1.w
+.PE
+.SH
+Channel Output State Diagram
+.PS
+S1: ellipse "OUTPUT" "OPEN"
+move right 2*l from last ellipse.e
+S3: ellipse "OUTPUT" "WAIT" "IEOF"
+move down l from last ellipse.s
+S4: ellipse "OUTPUT" "CLOSED"
+move down l from 1st ellipse.s
+S2: ellipse "OUTPUT" "WAIT" "DRAIN"
+arrow "" "write_failed/" "shutdown_write" "send OCLOSE" from S1.e to S3.w
+arrow "obuf_empty ||" "write_failed/" "shutdown_write" "send OCLOSE" from S2.e to S4.w
+arrow from S1.s to S2.n
+box invis "rcvd IEOF/" "-" with .e at last arrow.c
+arrow from S3.s to S4.n
+box invis "rcvd IEOF/" "-" with .w at last arrow.c
+ellipse wid .9*ellipsewid ht .9*ellipseht at S4
+arrow "start" "" from S1.w+(-0.5,0) to S1.w
+.PE
+.SH
+Notes
+.PP
+The input buffer is filled with data from the socket
+(the socket represents the local comsumer/producer of the
+forwarded channel).
+The data is then sent over the INPUT-end of the channel to the
+remote peer.
+Data sent by the peer is received on the OUTPUT-end,
+saved in the output buffer and written to the socket.
+.PP
+If the local protocol instance has forwarded all data on the
+INPUT-end of the channel, it sends an IEOF message to the peer.
+If the peer receives the IEOF and has comsumed all
+data he replies with an OCLOSE.
+When the local instance receives the OCLOSE
+he considers the INPUT-half of the channel closed.
+The peer has his OUTOUT-half closed.
+.PP
+A channel can be deallocated by a protocol instance
+if both the INPUT- and the OUTOUT-half on his
+side of the channel are closed.
+Note that when an instance is unable to comsume the
+received data, he is permitted to send an OCLOSE
+before the matching IEOF is received.
diff --git a/openssh.spec b/openssh.spec
new file mode 100644
index 0000000..7ce5884
--- /dev/null
+++ b/openssh.spec
@@ -0,0 +1,105 @@
+Summary: OpenSSH free Secure Shell (SSH) implementation
+Name: openssh
+Version: 1.2pre3
+Release: 1
+Packager: Damien Miller <djm@ibs.com.au>
+Source0: openssh-%{version}-linux.tar.gz
+Copyright: BSD
+Group: Applications/Internet
+BuildRoot: /tmp/openssh-%{version}-buildroot
+
+%description
+Ssh (Secure Shell) a program for logging into a remote machine and for
+executing commands in a remote machine.  It is intended to replace
+rlogin and rsh, and provide secure encrypted communications between
+two untrusted hosts over an insecure network.  X11 connections and
+arbitrary TCP/IP ports can also be forwarded over the secure channel.
+
+OpenSSH is OpenBSD's rework of the last free version of SSH, bringing it
+up to date in terms of security and features, as well as removing all 
+patented algorithms to seperate libraries (OpenSSL).
+
+%changelog
+* Wed Oct 27 1999 Damien Miller <djm@ibs.com.au>
+- Initial RPMification, based on Jan "Yenya" Kasprzak's <kas@fi.muni.cz> spec.
+
+%prep
+
+%setup -n openssh
+
+%build
+
+make -f Makefile.GNU OPT_FLAGS="$RPM_OPT_FLAGS"
+
+%install
+rm -rf $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT/usr/bin 
+mkdir -p $RPM_BUILD_ROOT/usr/sbin 
+mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d
+mkdir -p $RPM_BUILD_ROOT/etc/pam.d
+mkdir -p $RPM_BUILD_ROOT/etc/ssh
+mkdir -p $RPM_BUILD_ROOT/usr/man/man1
+mkdir -p $RPM_BUILD_ROOT/usr/man/man8
+
+install -m644 ssh.pam $RPM_BUILD_ROOT/etc/pam.d/ssh
+install -m755 sshd.init $RPM_BUILD_ROOT/etc/rc.d/init.d/sshd
+install -m600 ssh_config $RPM_BUILD_ROOT/etc/ssh/ssh_config
+install -m600 sshd_config $RPM_BUILD_ROOT/etc/ssh/sshd_config
+
+install -s -m755 bin/sshd $RPM_BUILD_ROOT/usr/sbin
+install -s -m755 bin/ssh $RPM_BUILD_ROOT/usr/bin
+install -s -m755 bin/scp $RPM_BUILD_ROOT/usr/bin
+install -s -m755 bin/ssh-agent $RPM_BUILD_ROOT/usr/bin
+install -s -m755 bin/ssh-add $RPM_BUILD_ROOT/usr/bin
+install -s -m755 bin/ssh-keygen $RPM_BUILD_ROOT/usr/bin
+
+install -m644 sshd.8 $RPM_BUILD_ROOT/usr/man/man8
+install -m644 ssh.1 $RPM_BUILD_ROOT/usr/man/man1
+install -m644 scp.1 $RPM_BUILD_ROOT/usr/man/man1
+install -m644 ssh-agent.1 $RPM_BUILD_ROOT/usr/man/man1
+install -m644 ssh-add.1 $RPM_BUILD_ROOT/usr/man/man1
+install -m644 ssh-keygen.1 $RPM_BUILD_ROOT/usr/man/man1
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+/sbin/chkconfig --add sshd
+if [ ! -f /etc/ssh/ssh_host_key -o ! -s /etc/ssh/ssh_host_key ]; then
+	/usr/bin/ssh-keygen -b 1024 -f /etc/ssh/ssh_host_key -N '' >&2
+fi
+if test -r /var/run/sshd.pid
+then
+	/etc/rc.d/init.d/sshd restart >&2
+fi
+
+%preun
+if [ "$1" = 0 ]
+then
+	/etc/rc.d/init.d/sshd stop >&2
+	/sbin/chkconfig --del sshd
+fi
+
+%files
+%defattr(-,root,root)
+%doc COPYING.Ylonen ChangeLog ChangeLog.linux OVERVIEW 
+%doc README README.openssh
+%attr(0755,root,root) /usr/sbin/sshd
+%attr(0755,root,root) /usr/bin/ssh
+%attr(0755,root,root) /usr/bin/ssh-agent
+%attr(0755,root,root) /usr/bin/ssh-keygen
+%attr(0755,root,root) /usr/bin/ssh-add
+%attr(0755,root,root) /usr/bin/scp
+
+%attr(0755,root,root) /usr/man/man8/sshd.8
+%attr(0755,root,root) /usr/man/man1/ssh.1
+%attr(0755,root,root) /usr/man/man1/ssh-agent.1
+%attr(0755,root,root) /usr/man/man1/ssh-keygen.1
+%attr(0755,root,root) /usr/man/man1/ssh-add.1
+%attr(0755,root,root) /usr/man/man1/scp.1
+
+%attr(0600,root,root) %config /etc/ssh/sshd_config
+%attr(0600,root,root) %config /etc/pam.d/ssh
+%attr(0755,root,root) %config /etc/rc.d/init.d/sshd
+%attr(0644,root,root) %config /etc/ssh/ssh_config
+
diff --git a/packet.c b/packet.c
new file mode 100644
index 0000000..7e74c73
--- /dev/null
+++ b/packet.c
@@ -0,0 +1,762 @@
+/*
+
+packet.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Sat Mar 18 02:40:40 1995 ylo
+
+This file contains code implementing the packet protocol and communication
+with the other side.  This same code is used both on client and server side.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: packet.c,v 1.1 1999/10/27 03:42:44 damien Exp $");
+
+#include "xmalloc.h"
+#include "buffer.h"
+#include "packet.h"
+#include "bufaux.h"
+#include "ssh.h"
+#include "crc32.h"
+#include "cipher.h"
+#include "getput.h"
+
+#include "compress.h"
+#include "deattack.h"
+
+/* This variable contains the file descriptors used for communicating with
+   the other side.  connection_in is used for reading; connection_out
+   for writing.  These can be the same descriptor, in which case it is
+   assumed to be a socket. */
+static int connection_in = -1;
+static int connection_out = -1;
+
+/* Cipher type.  This value is only used to determine whether to pad the
+   packets with zeroes or random data. */
+static int cipher_type = SSH_CIPHER_NONE;
+
+/* Protocol flags for the remote side. */
+static unsigned int remote_protocol_flags = 0;
+
+/* Encryption context for receiving data.  This is only used for decryption. */
+static CipherContext receive_context;
+/* Encryption coontext for sending data.  This is only used for encryption. */
+static CipherContext send_context;
+
+/* Buffer for raw input data from the socket. */
+static Buffer input;
+
+/* Buffer for raw output data going to the socket. */
+static Buffer output;
+
+/* Buffer for the partial outgoing packet being constructed. */
+static Buffer outgoing_packet;
+
+/* Buffer for the incoming packet currently being processed. */
+static Buffer incoming_packet;
+
+/* Scratch buffer for packet compression/decompression. */
+static Buffer compression_buffer;
+
+/* Flag indicating whether packet compression/decompression is enabled. */
+static int packet_compression = 0;
+
+/* Flag indicating whether this module has been initialized. */
+static int initialized = 0;
+
+/* Set to true if the connection is interactive. */
+static int interactive_mode = 0;
+
+/* Sets the descriptors used for communication.  Disables encryption until
+   packet_set_encryption_key is called. */
+
+void
+packet_set_connection(int fd_in, int fd_out)
+{
+  connection_in = fd_in;
+  connection_out = fd_out;
+  cipher_type = SSH_CIPHER_NONE;
+  cipher_set_key(&send_context, SSH_CIPHER_NONE, (unsigned char *)"", 0, 1);
+  cipher_set_key(&receive_context, SSH_CIPHER_NONE, (unsigned char *)"", 0, 0);
+  if (!initialized)
+    {
+      initialized = 1;
+      buffer_init(&input);
+      buffer_init(&output);
+      buffer_init(&outgoing_packet);
+      buffer_init(&incoming_packet);
+    }
+
+  /* Kludge: arrange the close function to be called from fatal(). */
+  fatal_add_cleanup((void (*)(void *))packet_close, NULL);
+}
+
+/* Sets the connection into non-blocking mode. */
+
+void
+packet_set_nonblocking()
+{
+  /* Set the socket into non-blocking mode. */
+  if (fcntl(connection_in, F_SETFL, O_NONBLOCK) < 0)
+    error("fcntl O_NONBLOCK: %.100s", strerror(errno));
+
+  if (connection_out != connection_in)
+    {
+      if (fcntl(connection_out, F_SETFL, O_NONBLOCK) < 0)
+	error("fcntl O_NONBLOCK: %.100s", strerror(errno));
+    }
+}
+
+/* Returns the socket used for reading. */
+
+int
+packet_get_connection_in()
+{
+  return connection_in;
+}
+
+/* Returns the descriptor used for writing. */
+
+int
+packet_get_connection_out()
+{
+  return connection_out;
+}
+
+/* Closes the connection and clears and frees internal data structures. */
+
+void
+packet_close()
+{
+  if (!initialized)
+    return;
+  initialized = 0;
+  if (connection_in == connection_out)
+    {
+      shutdown(connection_out, SHUT_RDWR);
+      close(connection_out);
+    }
+  else
+    {
+      close(connection_in);
+      close(connection_out);
+    }
+  buffer_free(&input);
+  buffer_free(&output);
+  buffer_free(&outgoing_packet);
+  buffer_free(&incoming_packet);
+  if (packet_compression)
+    {
+      buffer_free(&compression_buffer);
+      buffer_compress_uninit();
+    }
+}
+
+/* Sets remote side protocol flags. */
+
+void
+packet_set_protocol_flags(unsigned int protocol_flags)
+{
+  remote_protocol_flags = protocol_flags;
+  channel_set_options((protocol_flags & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) != 0);
+}
+
+/* Returns the remote protocol flags set earlier by the above function. */
+
+unsigned int
+packet_get_protocol_flags()
+{
+  return remote_protocol_flags;
+}
+
+/* Starts packet compression from the next packet on in both directions. 
+   Level is compression level 1 (fastest) - 9 (slow, best) as in gzip. */
+
+void
+packet_start_compression(int level)
+{
+  if (packet_compression)
+    fatal("Compression already enabled.");
+  packet_compression = 1;
+  buffer_init(&compression_buffer);
+  buffer_compress_init(level);
+}
+
+/* Encrypts the given number of bytes, copying from src to dest.
+   bytes is known to be a multiple of 8. */
+
+void
+packet_encrypt(CipherContext *cc, void *dest, void *src, 
+	       unsigned int bytes)
+{
+  assert((bytes % 8) == 0);
+  cipher_encrypt(cc, dest, src, bytes);
+}
+
+/* Decrypts the given number of bytes, copying from src to dest.
+   bytes is known to be a multiple of 8. */
+
+void
+packet_decrypt(CipherContext *cc, void *dest, void *src, 
+	       unsigned int bytes)
+{
+  int i;
+  
+  assert((bytes % 8) == 0);
+  
+  /*
+    Cryptographic attack detector for ssh - Modifications for packet.c 
+    (C)1998 CORE-SDI, Buenos Aires Argentina
+    Ariel Futoransky(futo@core-sdi.com)
+  */
+  switch (cc->type)
+    {
+    case SSH_CIPHER_NONE:
+      i = DEATTACK_OK;
+      break;
+    default:
+      i = detect_attack(src, bytes, NULL);
+      break;
+    }
+  
+  if (i == DEATTACK_DETECTED)
+    packet_disconnect("crc32 compensation attack: network attack detected");
+  
+  cipher_decrypt(cc, dest, src, bytes);
+}
+
+/* Causes any further packets to be encrypted using the given key.  The same
+   key is used for both sending and reception.  However, both directions
+   are encrypted independently of each other. */
+
+void
+packet_set_encryption_key(const unsigned char *key, unsigned int keylen,
+			  int cipher, int is_client)
+{
+  cipher_type = cipher;
+  if (cipher == SSH_CIPHER_RC4)
+    {
+      if (is_client)
+	{ /* In client: use first half for receiving, second for sending. */
+	  cipher_set_key(&receive_context, cipher, key, keylen / 2, 0);
+	  cipher_set_key(&send_context, cipher, key + keylen / 2, 
+			 keylen / 2, 1);
+	}
+      else
+	{ /* In server: use first half for sending, second for receiving. */
+	  cipher_set_key(&receive_context, cipher, key + keylen / 2, 
+			 keylen / 2, 0);
+	  cipher_set_key(&send_context, cipher, key, keylen / 2, 1);
+	}
+    }
+  else
+    {
+      /* All other ciphers use the same key in both directions for now. */
+      cipher_set_key(&receive_context, cipher, key, keylen, 0);
+      cipher_set_key(&send_context, cipher, key, keylen, 1);
+    }
+}
+
+/* Starts constructing a packet to send. */
+
+void
+packet_start(int type)
+{
+  char buf[9];
+
+  buffer_clear(&outgoing_packet);
+  memset(buf, 0, 8);
+  buf[8] = type;
+  buffer_append(&outgoing_packet, buf, 9);
+}
+
+/* Appends a character to the packet data. */
+
+void
+packet_put_char(int value)
+{
+  char ch = value;
+  buffer_append(&outgoing_packet, &ch, 1);
+}
+
+/* Appends an integer to the packet data. */
+
+void
+packet_put_int(unsigned int value)
+{
+  buffer_put_int(&outgoing_packet, value);
+}
+
+/* Appends a string to packet data. */
+
+void
+packet_put_string(const char *buf, unsigned int len)
+{
+  buffer_put_string(&outgoing_packet, buf, len);
+}
+
+/* Appends an arbitrary precision integer to packet data. */
+
+void
+packet_put_bignum(BIGNUM *value)
+{
+  buffer_put_bignum(&outgoing_packet, value);
+}
+
+/* Finalizes and sends the packet.  If the encryption key has been set,
+   encrypts the packet before sending. */
+  
+void
+packet_send()
+{
+  char buf[8], *cp;
+  int i, padding, len;
+  unsigned int checksum;
+  u_int32_t rand = 0;
+
+  /* If using packet compression, compress the payload of the outgoing
+     packet. */
+  if (packet_compression)
+    {
+      buffer_clear(&compression_buffer);
+      buffer_consume(&outgoing_packet, 8); /* Skip padding. */
+      buffer_append(&compression_buffer, "\0\0\0\0\0\0\0\0", 8); /* padding */
+      buffer_compress(&outgoing_packet, &compression_buffer);
+      buffer_clear(&outgoing_packet);
+      buffer_append(&outgoing_packet, buffer_ptr(&compression_buffer),
+		    buffer_len(&compression_buffer));
+    }
+
+  /* Compute packet length without padding (add checksum, remove padding). */
+  len = buffer_len(&outgoing_packet) + 4 - 8;
+  
+  /* Insert padding. */
+  padding = 8 - len % 8;
+  if (cipher_type != SSH_CIPHER_NONE)
+    {
+      cp = buffer_ptr(&outgoing_packet);
+      for (i = 0; i < padding; i++) {
+        if (i % 4 == 0)
+          rand = arc4random();
+        cp[7 - i] = rand & 0xff;
+        rand >>= 8;
+      }
+    }
+  buffer_consume(&outgoing_packet, 8 - padding);
+  
+  /* Add check bytes. */
+  checksum = crc32((unsigned char *)buffer_ptr(&outgoing_packet),
+		   buffer_len(&outgoing_packet));
+  PUT_32BIT(buf, checksum);
+  buffer_append(&outgoing_packet, buf, 4);
+
+#ifdef PACKET_DEBUG
+  fprintf(stderr, "packet_send plain: ");
+  buffer_dump(&outgoing_packet);
+#endif
+
+  /* Append to output. */
+  PUT_32BIT(buf, len);
+  buffer_append(&output, buf, 4);
+  buffer_append_space(&output, &cp, buffer_len(&outgoing_packet));
+  packet_encrypt(&send_context, cp, buffer_ptr(&outgoing_packet),
+		 buffer_len(&outgoing_packet));
+  
+#ifdef PACKET_DEBUG
+  fprintf(stderr, "encrypted: "); buffer_dump(&output);
+#endif
+
+  buffer_clear(&outgoing_packet);
+
+  /* Note that the packet is now only buffered in output.  It won\'t be
+     actually sent until packet_write_wait or packet_write_poll is called. */
+}
+
+/* Waits until a packet has been received, and returns its type.  Note that
+   no other data is processed until this returns, so this function should
+   not be used during the interactive session. */
+
+int
+packet_read(int *payload_len_ptr)
+{
+  int type, len;
+  fd_set set;
+  char buf[8192];
+
+  /* Since we are blocking, ensure that all written packets have been sent. */
+  packet_write_wait();
+
+  /* Stay in the loop until we have received a complete packet. */
+  for (;;)
+    {
+      /* Try to read a packet from the buffer. */
+      type = packet_read_poll(payload_len_ptr);
+      if (type == SSH_SMSG_SUCCESS
+	  || type == SSH_SMSG_FAILURE
+	  || type == SSH_CMSG_EOF
+	  || type == SSH_CMSG_EXIT_CONFIRMATION)
+	packet_integrity_check(*payload_len_ptr, 0, type);
+      /* If we got a packet, return it. */
+      if (type != SSH_MSG_NONE)
+	return type;
+      /* Otherwise, wait for some data to arrive, add it to the buffer,
+	 and try again. */
+      FD_ZERO(&set);
+      FD_SET(connection_in, &set);
+      /* Wait for some data to arrive. */
+      select(connection_in + 1, &set, NULL, NULL, NULL);
+      /* Read data from the socket. */
+      len = read(connection_in, buf, sizeof(buf));
+      if (len == 0)
+	fatal("Connection closed by remote host.");
+      if (len < 0)
+	fatal("Read from socket failed: %.100s", strerror(errno));
+      /* Append it to the buffer. */
+      packet_process_incoming(buf, len);
+    }
+  /*NOTREACHED*/
+}
+
+/* Waits until a packet has been received, verifies that its type matches
+   that given, and gives a fatal error and exits if there is a mismatch. */
+
+void
+packet_read_expect(int *payload_len_ptr, int expected_type)
+{
+  int type;
+
+  type = packet_read(payload_len_ptr);
+  if (type != expected_type)
+    packet_disconnect("Protocol error: expected packet type %d, got %d",
+		      expected_type, type);
+}
+
+/* Checks if a full packet is available in the data received so far via
+   packet_process_incoming.  If so, reads the packet; otherwise returns
+   SSH_MSG_NONE.  This does not wait for data from the connection. 
+   
+   SSH_MSG_DISCONNECT is handled specially here.  Also,
+   SSH_MSG_IGNORE messages are skipped by this function and are never returned
+   to higher levels.
+
+   The returned payload_len does include space consumed by:
+   Packet length
+   Padding
+   Packet type
+   Check bytes
+
+   
+   */
+
+int
+packet_read_poll(int *payload_len_ptr)
+{
+  unsigned int len, padded_len;
+  unsigned char *ucp;
+  char buf[8], *cp;
+  unsigned int checksum, stored_checksum;
+  
+ restart:
+
+  /* Check if input size is less than minimum packet size. */
+  if (buffer_len(&input) < 4 + 8)
+    return SSH_MSG_NONE;
+  /* Get length of incoming packet. */
+  ucp = (unsigned char *)buffer_ptr(&input);
+  len = GET_32BIT(ucp);
+  if (len < 1 + 2 + 2 || len > 256*1024)
+    packet_disconnect("Bad packet length %d.", len);
+  padded_len = (len + 8) & ~7;
+
+  /* Check if the packet has been entirely received. */
+  if (buffer_len(&input) < 4 + padded_len)
+    return SSH_MSG_NONE;
+
+  /* The entire packet is in buffer. */
+
+  /* Consume packet length. */
+  buffer_consume(&input, 4);
+
+  /* Copy data to incoming_packet. */
+  buffer_clear(&incoming_packet);
+  buffer_append_space(&incoming_packet, &cp, padded_len);
+  packet_decrypt(&receive_context, cp, buffer_ptr(&input), padded_len);
+  buffer_consume(&input, padded_len);
+
+#ifdef PACKET_DEBUG
+  fprintf(stderr, "read_poll plain: "); buffer_dump(&incoming_packet);
+#endif
+  
+  /* Compute packet checksum. */
+  checksum = crc32((unsigned char *)buffer_ptr(&incoming_packet),
+		   buffer_len(&incoming_packet) - 4);
+
+  /* Skip padding. */
+  buffer_consume(&incoming_packet, 8 - len % 8);
+
+  /* Test check bytes. */
+  assert(len == buffer_len(&incoming_packet));
+  ucp = (unsigned char *)buffer_ptr(&incoming_packet) + len - 4;
+  stored_checksum = GET_32BIT(ucp);
+  if (checksum != stored_checksum)
+    packet_disconnect("Corrupted check bytes on input.");
+  buffer_consume_end(&incoming_packet, 4);
+
+  /* If using packet compression, decompress the packet. */
+  if (packet_compression)
+    {
+      buffer_clear(&compression_buffer);
+      buffer_uncompress(&incoming_packet, &compression_buffer);
+      buffer_clear(&incoming_packet);
+      buffer_append(&incoming_packet, buffer_ptr(&compression_buffer),
+		    buffer_len(&compression_buffer));
+    }
+
+  /* Get packet type. */
+  buffer_get(&incoming_packet, &buf[0], 1);
+
+  /* Return length of payload (without type field). */
+  *payload_len_ptr = buffer_len(&incoming_packet);
+
+  /* Handle disconnect message. */
+  if ((unsigned char)buf[0] == SSH_MSG_DISCONNECT)
+    fatal("%.900s", packet_get_string(NULL));
+
+  /* Ignore ignore messages. */
+  if ((unsigned char)buf[0] == SSH_MSG_IGNORE)
+    goto restart;
+
+  /* Send debug messages as debugging output. */
+  if ((unsigned char)buf[0] == SSH_MSG_DEBUG)
+    {
+      debug("Remote: %.900s", packet_get_string(NULL));
+      goto restart;
+    }
+
+  /* Return type. */
+  return (unsigned char)buf[0];
+}
+  
+/* Buffers the given amount of input characters.  This is intended to be
+   used together with packet_read_poll. */
+
+void
+packet_process_incoming(const char *buf, unsigned int len)
+{
+  buffer_append(&input, buf, len);
+}
+
+/* Returns a character from the packet. */
+
+unsigned int
+packet_get_char()
+{
+  char ch;
+  buffer_get(&incoming_packet, &ch, 1);
+  return (unsigned char)ch;
+}
+
+/* Returns an integer from the packet data. */
+
+unsigned int
+packet_get_int()
+{
+  return buffer_get_int(&incoming_packet);
+}
+
+/* Returns an arbitrary precision integer from the packet data.  The integer
+   must have been initialized before this call. */
+
+void
+packet_get_bignum(BIGNUM *value, int *length_ptr)
+{
+  *length_ptr = buffer_get_bignum(&incoming_packet, value);
+}
+
+/* Returns a string from the packet data.  The string is allocated using
+   xmalloc; it is the responsibility of the calling program to free it when
+   no longer needed.  The length_ptr argument may be NULL, or point to an
+   integer into which the length of the string is stored. */
+
+char
+*packet_get_string(unsigned int *length_ptr)
+{
+  return buffer_get_string(&incoming_packet, length_ptr);
+}
+
+/* Sends a diagnostic message from the server to the client.  This message
+   can be sent at any time (but not while constructing another message).
+   The message is printed immediately, but only if the client is being
+   executed in verbose mode.  These messages are primarily intended to
+   ease debugging authentication problems.   The length of the formatted
+   message must not exceed 1024 bytes.  This will automatically call
+   packet_write_wait. */
+
+void
+packet_send_debug(const char *fmt, ...)
+{
+  char buf[1024];
+  va_list args;
+  
+  va_start(args, fmt);
+  vsnprintf(buf, sizeof(buf), fmt, args);
+  va_end(args);
+  
+  packet_start(SSH_MSG_DEBUG);
+  packet_put_string(buf, strlen(buf));
+  packet_send();
+  packet_write_wait();
+}
+
+/* Logs the error plus constructs and sends a disconnect
+   packet, closes the connection, and exits.  This function never returns.
+   The error message should not contain a newline.  The length of the
+   formatted message must not exceed 1024 bytes. */
+
+void
+packet_disconnect(const char *fmt, ...)
+{
+  char buf[1024];
+  va_list args;
+  static int disconnecting = 0;
+  if (disconnecting) /* Guard against recursive invocations. */
+    fatal("packet_disconnect called recursively.");
+  disconnecting = 1;
+
+  /* Format the message.  Note that the caller must make sure the message
+     is of limited size. */
+  va_start(args, fmt);
+  vsnprintf(buf, sizeof(buf), fmt, args);
+  va_end(args);
+
+  /* Send the disconnect message to the other side, and wait for it to get 
+     sent. */
+  packet_start(SSH_MSG_DISCONNECT);
+  packet_put_string(buf, strlen(buf));
+  packet_send();
+  packet_write_wait();
+
+  /* Stop listening for connections. */
+  channel_stop_listening();
+  
+  /* Close the connection. */
+  packet_close();
+
+  /* Display the error locally and exit. */
+  fatal("Local: %.100s", buf);
+}
+
+/* Checks if there is any buffered output, and tries to write some of the
+   output. */
+
+void
+packet_write_poll()
+{
+  int len = buffer_len(&output);
+  if (len > 0)
+    {
+      len = write(connection_out, buffer_ptr(&output), len);
+      if (len <= 0) {
+	if (errno == EAGAIN)
+	  return;
+        else
+	  fatal("Write failed: %.100s", strerror(errno));
+      }
+      buffer_consume(&output, len);
+    }
+}
+
+/* Calls packet_write_poll repeatedly until all pending output data has
+   been written. */
+
+void
+packet_write_wait()
+{
+  packet_write_poll();
+  while (packet_have_data_to_write())
+    {
+      fd_set set;
+      FD_ZERO(&set);
+      FD_SET(connection_out, &set);
+      select(connection_out + 1, NULL, &set, NULL, NULL);
+      packet_write_poll();
+    }
+}
+
+/* Returns true if there is buffered data to write to the connection. */
+
+int
+packet_have_data_to_write()
+{
+  return buffer_len(&output) != 0;
+}
+
+/* Returns true if there is not too much data to write to the connection. */
+
+int
+packet_not_very_much_data_to_write()
+{
+  if (interactive_mode)
+    return buffer_len(&output) < 16384;
+  else
+    return buffer_len(&output) < 128*1024;
+}
+
+/* Informs that the current session is interactive.  Sets IP flags for that. */
+
+void
+packet_set_interactive(int interactive, int keepalives)
+{
+  int on = 1;
+
+  /* Record that we are in interactive mode. */
+  interactive_mode = interactive;
+
+  /* Only set socket options if using a socket (as indicated by the descriptors
+     being the same). */
+  if (connection_in != connection_out)
+    return;
+
+  if (keepalives)
+    {
+      /* Set keepalives if requested. */
+      if (setsockopt(connection_in, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, 
+		     sizeof(on)) < 0)
+	error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno));
+    }
+
+  if (interactive)
+    {
+      /* Set IP options for an interactive connection.  Use IPTOS_LOWDELAY
+	 and TCP_NODELAY. */
+      int lowdelay = IPTOS_LOWDELAY;
+      if (setsockopt(connection_in, IPPROTO_IP, IP_TOS, (void *)&lowdelay, 
+		     sizeof(lowdelay)) < 0)
+	error("setsockopt IPTOS_LOWDELAY: %.100s", strerror(errno));
+      if (setsockopt(connection_in, IPPROTO_TCP, TCP_NODELAY, (void *)&on, 
+		     sizeof(on)) < 0)
+	error("setsockopt TCP_NODELAY: %.100s", strerror(errno));
+    }
+  else
+    {
+      /* Set IP options for a non-interactive connection.  Use 
+	 IPTOS_THROUGHPUT. */
+      int throughput = IPTOS_THROUGHPUT;
+      if (setsockopt(connection_in, IPPROTO_IP, IP_TOS, (void *)&throughput, 
+		     sizeof(throughput)) < 0)
+	error("setsockopt IPTOS_THROUGHPUT: %.100s", strerror(errno));
+    }
+}
+
+/* Returns true if the current connection is interactive. */
+
+int
+packet_is_interactive()
+{
+  return interactive_mode;
+}
diff --git a/packet.h b/packet.h
new file mode 100644
index 0000000..fb84e6c
--- /dev/null
+++ b/packet.h
@@ -0,0 +1,166 @@
+/*
+
+packet.h
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Sat Mar 18 02:02:14 1995 ylo
+
+Interface for the packet protocol functions.
+
+*/
+
+/* RCSID("$Id: packet.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */
+
+#ifndef PACKET_H
+#define PACKET_H
+
+#include <openssl/bn.h>
+
+/* Sets the socket used for communication.  Disables encryption until
+   packet_set_encryption_key is called.  It is permissible that fd_in
+   and fd_out are the same descriptor; in that case it is assumed to
+   be a socket. */
+void packet_set_connection(int fd_in, int fd_out);
+
+/* Puts the connection file descriptors into non-blocking mode. */
+void packet_set_nonblocking(void);
+
+/* Returns the file descriptor used for input. */
+int packet_get_connection_in(void);
+
+/* Returns the file descriptor used for output. */
+int packet_get_connection_out(void);
+
+/* Closes the connection (both descriptors) and clears and frees
+   internal data structures. */ 
+void packet_close(void);
+
+/* Causes any further packets to be encrypted using the given key.  The same
+   key is used for both sending and reception.  However, both directions
+   are encrypted independently of each other.  Cipher types are
+   defined in ssh.h. */
+void packet_set_encryption_key(const unsigned char *key, unsigned int keylen,
+			       int cipher_type, int is_client);
+
+/* Sets remote side protocol flags for the current connection.  This can
+   be called at any time. */
+void packet_set_protocol_flags(unsigned int flags);
+
+/* Returns the remote protocol flags set earlier by the above function. */
+unsigned int packet_get_protocol_flags(void);
+
+/* Enables compression in both directions starting from the next packet. */
+void packet_start_compression(int level);
+
+/* Informs that the current session is interactive.  Sets IP flags for optimal
+   performance in interactive use. */
+void packet_set_interactive(int interactive, int keepalives);
+
+/* Returns true if the current connection is interactive. */
+int packet_is_interactive(void);
+
+/* Starts constructing a packet to send. */
+void packet_start(int type);
+
+/* Appends a character to the packet data. */
+void packet_put_char(int ch);
+
+/* Appends an integer to the packet data. */
+void packet_put_int(unsigned int value);
+
+/* Appends an arbitrary precision integer to packet data. */
+void packet_put_bignum(BIGNUM *value);
+
+/* Appends a string to packet data. */
+void packet_put_string(const char *buf, unsigned int len);
+
+/* Finalizes and sends the packet.  If the encryption key has been set,
+   encrypts the packet before sending. */
+void packet_send(void);
+
+/* Waits until a packet has been received, and returns its type. */
+int packet_read(int *payload_len_ptr);
+
+/* Waits until a packet has been received, verifies that its type matches
+   that given, and gives a fatal error and exits if there is a mismatch. */
+void packet_read_expect(int *payload_len_ptr, int type);
+
+/* Checks if a full packet is available in the data received so far via
+   packet_process_incoming.  If so, reads the packet; otherwise returns
+   SSH_MSG_NONE.  This does not wait for data from the connection. 
+   
+   SSH_MSG_DISCONNECT is handled specially here.  Also,
+   SSH_MSG_IGNORE messages are skipped by this function and are never returned
+   to higher levels. */
+int packet_read_poll(int *packet_len_ptr);
+
+/* Buffers the given amount of input characters.  This is intended to be
+   used together with packet_read_poll. */
+void packet_process_incoming(const char *buf, unsigned int len);
+
+/* Returns a character (0-255) from the packet data. */
+unsigned int packet_get_char(void);
+
+/* Returns an integer from the packet data. */
+unsigned int packet_get_int(void);
+
+/* Returns an arbitrary precision integer from the packet data.  The integer
+   must have been initialized before this call. */
+void packet_get_bignum(BIGNUM *value, int *length_ptr);
+
+/* Returns a string from the packet data.  The string is allocated using
+   xmalloc; it is the responsibility of the calling program to free it when
+   no longer needed.  The length_ptr argument may be NULL, or point to an
+   integer into which the length of the string is stored. */
+char *packet_get_string(unsigned int *length_ptr);
+
+/* Logs the error in syslog using LOG_INFO, constructs and sends a disconnect
+   packet, closes the connection, and exits.  This function never returns.
+   The error message should not contain a newline.  The total length of the
+   message must not exceed 1024 bytes. */
+void packet_disconnect(const char *fmt, ...);
+
+/* Sends a diagnostic message to the other side.  This message
+   can be sent at any time (but not while constructing another message).
+   The message is printed immediately, but only if the client is being
+   executed in verbose mode.  These messages are primarily intended to
+   ease debugging authentication problems.  The total length of the message
+   must not exceed 1024 bytes.  This will automatically call
+   packet_write_wait.  If the remote side protocol flags do not indicate
+   that it supports SSH_MSG_DEBUG, this will do nothing. */
+void packet_send_debug(const char *fmt, ...);
+
+/* Checks if there is any buffered output, and tries to write some of the
+   output. */
+void packet_write_poll(void);
+
+/* Waits until all pending output data has been written. */
+void packet_write_wait(void);
+
+/* Returns true if there is buffered data to write to the connection. */
+int packet_have_data_to_write(void);
+
+/* Returns true if there is not too much data to write to the connection. */
+int packet_not_very_much_data_to_write(void);
+
+/* Stores tty modes from the fd into current packet. */
+void tty_make_modes(int fd);
+
+/* Parses tty modes for the fd from the current packet. */
+void tty_parse_modes(int fd, int *n_bytes_ptr);
+
+#define packet_integrity_check(payload_len, expected_len, type) \
+do { \
+  int _p = (payload_len), _e = (expected_len); \
+  if (_p != _e) { \
+    log("Packet integrity error (%d != %d) at %s:%d", \
+	_p, _e, __FILE__, __LINE__); \
+    packet_disconnect("Packet integrity error. (%d)", (type)); \
+  } \
+} while (0)
+
+#endif /* PACKET_H */
diff --git a/pty.c b/pty.c
new file mode 100644
index 0000000..440994b
--- /dev/null
+++ b/pty.c
@@ -0,0 +1,264 @@
+/*
+
+pty.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Fri Mar 17 04:37:25 1995 ylo
+
+Allocating a pseudo-terminal, and making it the controlling tty.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: pty.c,v 1.1 1999/10/27 03:42:44 damien Exp $");
+
+#include "pty.h"
+#include "ssh.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
+
+#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)
+{
+#ifdef HAVE_OPENPTY
+
+  /* openpty(3) exists in OSF/1 and some other os'es */
+
+  int i;
+
+  i = openpty(ptyfd, ttyfd, namebuf, NULL, NULL);
+
+  if (i < 0) 
+    {
+      error("openpty: %.100s", strerror(errno));
+      return 0;
+    }
+  
+  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)
+    {
+      error("_getpty: %.100s", strerror(errno));
+      return 0;
+    }
+  strcpy(namebuf, slave);
+  /* Open the slave side. */
+  *ttyfd = open(namebuf, O_RDWR|O_NOCTTY);
+  if (*ttyfd < 0)
+    {
+      error("%.200s: %.100s", namebuf, strerror(errno));
+      close(*ptyfd);
+      return 0;
+    }
+  return 1;
+
+#else /* HAVE__GETPTY */
+#ifdef HAVE_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.) */
+
+  int ptm;
+  char *pts;
+  
+  ptm = open("/dev/ptmx", O_RDWR|O_NOCTTY);
+  if (ptm < 0)
+    {
+      error("/dev/ptmx: %.100s", strerror(errno));
+      return 0;
+    }
+  if (grantpt(ptm) < 0)
+    {
+      error("grantpt: %.100s", strerror(errno));
+      return 0;
+    }
+  if (unlockpt(ptm) < 0)
+    {
+      error("unlockpt: %.100s", strerror(errno));
+      return 0;
+    }
+  pts = ptsname(ptm);
+  if (pts == NULL)
+    error("Slave pty side name could not be obtained.");
+  strcpy(namebuf, pts);
+  *ptyfd = ptm;
+
+  /* Open the slave side. */
+  *ttyfd = open(namebuf, O_RDWR|O_NOCTTY);
+  if (*ttyfd < 0)
+    {
+      error("%.100s: %.100s", namebuf, strerror(errno));
+      close(*ptyfd);
+      return 0;
+    }
+  /* Push the appropriate streams modules, as described in Solaris pts(7). */
+  if (ioctl(*ttyfd, I_PUSH, "ptem") < 0)
+    error("ioctl I_PUSH ptem: %.100s", strerror(errno));
+  if (ioctl(*ttyfd, I_PUSH, "ldterm") < 0)
+    error("ioctl I_PUSH ldterm: %.100s", strerror(errno));
+  if (ioctl(*ttyfd, I_PUSH, "ttcompat") < 0)
+    error("ioctl I_PUSH ttcompat: %.100s", strerror(errno));
+  return 1;
+
+#else /* HAVE_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)
+    {
+      error("Could not open /dev/ptc: %.100s", strerror(errno));
+      return 0;
+    }
+  name = ttyname(*ptyfd);
+  if (!name)
+    fatal("Open of /dev/ptc returns device for which ttyname fails.");
+  strcpy(namebuf, name);
+  *ttyfd = open(name, O_RDWR|O_NOCTTY);
+  if (*ttyfd < 0)
+    {
+      error("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;
+
+  for (i = 0; i < num_ptys; i++)
+    {
+      snprintf(buf, sizeof buf, "/dev/pty%c%c", ptymajors[i / num_minors], 
+	      ptyminors[i % num_minors]);
+      *ptyfd = open(buf, O_RDWR|O_NOCTTY);
+      if (*ptyfd < 0)
+	continue;
+      snprintf(namebuf, sizeof buf, "/dev/tty%c%c", ptymajors[i / num_minors], 
+	      ptyminors[i % num_minors]);
+
+      /* Open the slave side. */
+      *ttyfd = open(namebuf, O_RDWR|O_NOCTTY);
+      if (*ttyfd < 0)
+	{
+	  error("%.100s: %.100s", namebuf, strerror(errno));
+	  close(*ptyfd);
+	  return 0;
+	}
+      return 1;
+    }
+  return 0;
+#endif /* HAVE_DEV_PTS_AND_PTC */
+#endif /* HAVE_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 *ttyname)
+{
+  if (chown(ttyname, (uid_t)0, (gid_t)0) < 0)
+    debug("chown %.100s 0 0 failed: %.100s", ttyname, strerror(errno));
+  if (chmod(ttyname, (mode_t)0666) < 0)
+    debug("chmod %.100s 0666 failed: %.100s", ttyname, strerror(errno));
+}
+
+/* Makes the tty the processes controlling tty and sets it to sane modes. */
+
+void pty_make_controlling_tty(int *ttyfd, const char *ttyname)
+{
+  int fd;
+
+  /* First disconnect from the old controlling tty. */
+#ifdef TIOCNOTTY
+  fd = open("/dev/tty", O_RDWR|O_NOCTTY);
+  if (fd >= 0)
+    {
+      (void)ioctl(fd, TIOCNOTTY, NULL);
+      close(fd);
+    }
+#endif /* TIOCNOTTY */
+  if (setsid() < 0)
+    error("setsid: %.100s", strerror(errno));
+  
+  /* Verify that we are successfully disconnected from the controlling tty. */
+  fd = open("/dev/tty", O_RDWR|O_NOCTTY);
+  if (fd >= 0)
+    {
+      error("Failed to disconnect from controlling tty.");
+      close(fd);
+    }
+
+  /* Make it our controlling tty. */
+#ifdef TIOCSCTTY
+  debug("Setting controlling tty using TIOCSCTTY.");
+  /* We ignore errors from this, because HPSUX defines TIOCSCTTY, but returns
+     EINVAL with these arguments, and there is absolutely no documentation. */
+  ioctl(*ttyfd, TIOCSCTTY, NULL);
+#endif /* TIOCSCTTY */
+  fd = open(ttyname, O_RDWR);
+  if (fd < 0)
+    error("%.100s: %.100s", ttyname, strerror(errno));
+  else
+    close(fd);
+
+  /* Verify that we now have a controlling tty. */
+  fd = open("/dev/tty", O_WRONLY);
+  if (fd < 0)
+    error("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);
+}
+
diff --git a/pty.h b/pty.h
new file mode 100644
index 0000000..20ee90a
--- /dev/null
+++ b/pty.h
@@ -0,0 +1,40 @@
+/*
+
+pty.h
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Fri Mar 17 05:03:28 1995 ylo
+
+Functions for allocating a pseudo-terminal and making it the controlling
+tty.
+
+*/
+
+/* RCSID("$Id: pty.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */
+
+#ifndef PTY_H
+#define PTY_H
+
+/* 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 *ttyname);
+
+/* Releases the tty.  Its ownership is returned to root, and permissions to
+   0666. */
+void pty_release(const char *ttyname);
+
+/* Makes the tty the processes controlling tty and sets it to sane modes. 
+   This may need to reopen the tty to get rid of possible eavesdroppers. */
+void pty_make_controlling_tty(int *ttyfd, const char *ttyname);
+
+/* Changes the window size associated with the pty. */
+void pty_change_window_size(int ptyfd, int row, int col,
+			    int xpixel, int ypixel);
+
+#endif /* PTY_H */
diff --git a/radix.c b/radix.c
new file mode 100644
index 0000000..1c49794
--- /dev/null
+++ b/radix.c
@@ -0,0 +1,258 @@
+/*
+  radix.c
+
+  base-64 encoding pinched from lynx2-7-2, who pinched it from rpem.
+  Originally written by Mark Riordan 12 August 1990 and 17 Feb 1991
+  and placed in the public domain.
+
+  Dug Song <dugsong@UMICH.EDU>
+*/
+  
+#include "includes.h"
+
+#ifdef AFS
+#include <krb.h>
+
+char six2pr[64] = {
+    'A','B','C','D','E','F','G','H','I','J','K','L','M',
+    'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
+    'a','b','c','d','e','f','g','h','i','j','k','l','m',
+    'n','o','p','q','r','s','t','u','v','w','x','y','z',
+    '0','1','2','3','4','5','6','7','8','9','+','/'
+};
+
+unsigned char pr2six[256];
+
+int uuencode(unsigned char *bufin, unsigned int nbytes, char *bufcoded)
+{
+  /* ENC is the basic 1 character encoding function to make a char printing */
+#define ENC(c) six2pr[c]
+  
+  register char *outptr = bufcoded;
+  unsigned int i;
+  
+  for (i=0; i<nbytes; i += 3) {
+    *(outptr++) = ENC(*bufin >> 2);            /* c1 */
+    *(outptr++) = ENC(((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)); /*c2*/
+    *(outptr++) = ENC(((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03));/*c3*/
+    *(outptr++) = ENC(bufin[2] & 077);         /* c4 */
+    bufin += 3;
+  }
+  if (i == nbytes+1) {
+    outptr[-1] = '=';
+  } else if (i == nbytes+2) {
+    outptr[-1] = '=';
+    outptr[-2] = '=';
+  }
+  *outptr = '\0';
+  return(outptr - bufcoded);
+}
+
+int uudecode(const char *bufcoded, unsigned char *bufplain, int outbufsize)
+{
+  /* single character decode */
+#define DEC(c) pr2six[(unsigned char)c]
+#define MAXVAL 63
+  
+  static int first = 1;
+  int nbytesdecoded, j;
+  const char *bufin = bufcoded;
+  register unsigned char *bufout = bufplain;
+  register int nprbytes;
+  
+  /* If this is the first call, initialize the mapping table. */
+  if (first) {
+    first = 0;
+    for(j=0; j<256; j++) pr2six[j] = MAXVAL+1;
+    for(j=0; j<64; j++) pr2six[(unsigned char)six2pr[j]] = (unsigned char)j;
+  }
+  
+  /* Strip leading whitespace. */
+  while (*bufcoded==' ' || *bufcoded == '\t') bufcoded++;
+  
+  /* Figure out how many characters are in the input buffer.
+     If this would decode into more bytes than would fit into
+     the output buffer, adjust the number of input bytes downwards. */
+  bufin = bufcoded;
+  while (DEC(*(bufin++)) <= MAXVAL);
+  nprbytes = bufin - bufcoded - 1;
+  nbytesdecoded = ((nprbytes+3)/4) * 3;
+  if (nbytesdecoded > outbufsize)
+    nprbytes = (outbufsize*4)/3;
+  
+  bufin = bufcoded;
+  
+  while (nprbytes > 0) {
+    *(bufout++) = (unsigned char) (DEC(*bufin) << 2 | DEC(bufin[1]) >> 4);
+    *(bufout++) = (unsigned char) (DEC(bufin[1]) << 4 | DEC(bufin[2]) >> 2);
+    *(bufout++) = (unsigned char) (DEC(bufin[2]) << 6 | DEC(bufin[3]));
+    bufin += 4;
+    nprbytes -= 4;
+  }
+  if (nprbytes & 03) {
+    if (DEC(bufin[-2]) > MAXVAL)
+      nbytesdecoded -= 2;
+    else 
+      nbytesdecoded -= 1;
+  }
+  return(nbytesdecoded);
+}
+
+typedef unsigned char my_u_char;
+typedef unsigned int my_u_int32_t;
+typedef unsigned short my_u_short;
+
+/* Nasty macros from BIND-4.9.2 */
+
+#define GETSHORT(s, cp) { \
+	register my_u_char *t_cp = (my_u_char*)(cp); \
+	(s) = (((my_u_short)t_cp[0]) << 8) \
+	    | (((my_u_short)t_cp[1])) \
+	    ; \
+	(cp) += 2; \
+}
+
+#define GETLONG(l, cp) { \
+	register my_u_char *t_cp = (my_u_char*)(cp); \
+	(l) = (((my_u_int32_t)t_cp[0]) << 24) \
+	    | (((my_u_int32_t)t_cp[1]) << 16) \
+	    | (((my_u_int32_t)t_cp[2]) << 8) \
+	    | (((my_u_int32_t)t_cp[3])) \
+	    ; \
+	(cp) += 4; \
+}
+
+#define PUTSHORT(s, cp) { \
+	register my_u_short t_s = (my_u_short)(s); \
+	register my_u_char *t_cp = (my_u_char*)(cp); \
+	*t_cp++ = t_s >> 8; \
+	*t_cp   = t_s; \
+	(cp) += 2; \
+}
+
+#define PUTLONG(l, cp) { \
+	register my_u_int32_t t_l = (my_u_int32_t)(l); \
+	register my_u_char *t_cp = (my_u_char*)(cp); \
+	*t_cp++ = t_l >> 24; \
+	*t_cp++ = t_l >> 16; \
+	*t_cp++ = t_l >> 8; \
+	*t_cp   = t_l; \
+	(cp) += 4; \
+}
+
+#define GETSTRING(s, p, p_l) {			\
+    register char* p_targ = (p) + p_l;		\
+    register char* s_c = (s);			\
+    register char* p_c = (p);			\
+    while (*p_c && (p_c < p_targ)) {		\
+	*s_c++ = *p_c++;			\
+    }						\
+    if (p_c == p_targ) {			\
+	return 1;				\
+    }						\
+    *s_c = *p_c++;				\
+    (p_l) = (p_l) - (p_c - (p));		\
+    (p) = p_c;					\
+}
+
+
+int creds_to_radix(CREDENTIALS *creds, unsigned char *buf)
+{
+  char *p, *s;
+  int len;
+  char temp[2048];
+  
+  p = temp;
+  *p++ = 1; /* version */
+  s = creds->service;	while (*s) *p++ = *s++; *p++ = *s;
+  s = creds->instance;	while (*s) *p++ = *s++; *p++ = *s;
+  s = creds->realm;	while (*s) *p++ = *s++; *p++ = *s;
+
+  s = creds->pname;	while (*s) *p++ = *s++;   *p++ = *s;
+  s = creds->pinst;	while (*s) *p++ = *s++;   *p++ = *s;
+  /* Null string to repeat the realm. */
+  *p++ = '\0';
+
+  PUTLONG(creds->issue_date,p);
+  {
+    unsigned int	endTime ;
+    endTime = (unsigned int)krb_life_to_time(creds->issue_date,
+					      creds->lifetime);
+    PUTLONG(endTime,p);
+  }
+
+  memcpy(p,&creds->session, sizeof(creds->session));
+  p += sizeof(creds->session);
+  
+  PUTSHORT(creds->kvno,p);
+  PUTLONG(creds->ticket_st.length,p);
+  
+  memcpy(p,creds->ticket_st.dat, creds->ticket_st.length);
+  p += creds->ticket_st.length;
+  len = p - temp;
+
+  return(uuencode(temp, len, buf));
+}
+
+int radix_to_creds(const char *buf, CREDENTIALS *creds)
+{
+
+  char *p;
+  int len, tl;
+  char version;
+  char temp[2048];
+  
+  if (!(len = uudecode(buf, temp, sizeof(temp))))
+    return 0;
+  
+  p = temp;
+
+  /* check version and length! */
+  if (len < 1) return 0;
+  version = *p; p++; len--;
+
+  GETSTRING(creds->service, p, len);
+  GETSTRING(creds->instance, p, len);
+  GETSTRING(creds->realm, p, len);
+  
+  GETSTRING(creds->pname, p, len);
+  GETSTRING(creds->pinst, p, len);
+  /* Ignore possibly different realm. */
+  while (*p && len) p++, len--;
+  if (len == 0) return 0;
+  p++, len--;
+  
+  /* Enough space for remaining fixed-length parts? */
+  if (len < (4 + 4 + sizeof(creds->session) + 2 + 4))
+    return 0;
+  
+  GETLONG(creds->issue_date,p);
+  len -= 4;
+  {
+    unsigned int	endTime;
+    GETLONG(endTime,p);
+    len -= 4;
+    creds->lifetime = krb_time_to_life(creds->issue_date, endTime);
+  }
+
+  memcpy(&creds->session, p, sizeof(creds->session));
+  p += sizeof(creds->session);
+  len -= sizeof(creds->session);
+  
+  GETSHORT(creds->kvno,p);
+  len -= 2;
+  GETLONG(creds->ticket_st.length,p);
+  len -= 4;
+
+  tl = creds->ticket_st.length;
+  if (tl < 0 || tl > len || tl > sizeof(creds->ticket_st.dat))
+    return 0;
+  
+  memcpy(creds->ticket_st.dat, p, tl);
+  p += tl;
+  len -= tl;
+  
+  return 1;
+}
+
+#endif /* AFS */
diff --git a/rc4.c b/rc4.c
new file mode 100644
index 0000000..a426188
--- /dev/null
+++ b/rc4.c
@@ -0,0 +1,105 @@
+/*! \file rc4.c
+    \brief Source file for RC4 stream cipher routines
+	 \author Damien Miller <djm@mindrot.org>
+	 \version 0.0.0
+	 \date 1999
+	 
+	 A simple implementation of the RC4 stream cipher, based on the
+	 description given in _Bruce Schneier's_ "Applied Cryptography"
+	 2nd edition.
+
+	 Copyright 1999 Damien Miller
+
+	 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 DAMIEN MILLER 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.
+
+	 \warning None of these functions clears its memory after use. It
+	 \warning is the responsability of the calling routines to ensure
+	 \warning that any sensitive data (keystream, key or plaintext) is
+	 \warning properly erased after use.
+	 
+	 \warning The name "RC4" is trademarked in the United States, 
+	 \warning you may need to use "RC4 compatible" or "ARC4" 
+	 \warning (Alleged RC4).
+*/
+
+/* $Id: rc4.c,v 1.1.1.1 1999/10/26 05:48:13 damien Exp $ */
+
+#include "rc4.h"
+
+
+void rc4_key(rc4_t *r, unsigned char *key, int len)
+{
+	int t;
+	
+	for(r->i = 0; r->i < 256; r->i++)
+		r->s[r->i] = r->i;
+
+	r->j = 0;
+	for(r->i = 0; r->i < 256; r->i++)
+	{
+		r->j = (r->j + r->s[r->i] + key[r->i % len]) % 256;
+		t = r->s[r->i];
+		r->s[r->i] = r->s[r->j];
+		r->s[r->j] = t;
+	}
+	r->i = r->j = 0;
+}
+
+void rc4_crypt(rc4_t *r, unsigned char *plaintext, int len)
+{
+	int t;
+	int c;
+
+	c = 0;	
+	while(c < len)
+	{
+		r->i = (r->i + 1) % 256;
+		r->j = (r->j + r->s[r->i]) % 256;
+		t = r->s[r->i];
+		r->s[r->i] = r->s[r->j];
+		r->s[r->j] = t;
+
+		t = (r->s[r->i] + r->s[r->j]) % 256;
+
+		plaintext[c] ^= r->s[t];
+		c++;
+	}
+}
+
+void rc4_getbytes(rc4_t *r, unsigned char *buffer, int len)
+{
+	int t;
+	int c;
+
+	c = 0;	
+	while(c < len)
+	{
+		r->i = (r->i + 1) % 256;
+		r->j = (r->j + r->s[r->i]) % 256;
+		t = r->s[r->i];
+		r->s[r->i] = r->s[r->j];
+		r->s[r->j] = t;
+
+		t = (r->s[r->i] + r->s[r->j]) % 256;
+		
+		buffer[c] = r->s[t];
+		c++;
+	}
+}
diff --git a/rc4.h b/rc4.h
new file mode 100644
index 0000000..904affe
--- /dev/null
+++ b/rc4.h
@@ -0,0 +1,110 @@
+/*! \file rc4.h
+    \brief Header file for RC4 stream cipher routines
+	 \author Damien Miller <djm@mindrot.org>
+	 \version 0.0.0
+	 \date 1999
+	 
+	 A simple implementation of the RC4 stream cipher, based on the
+	 description given in _Bruce Schneier's_ "Applied Cryptography"
+	 2nd edition.
+
+	 Copyright 1999 Damien Miller
+
+	 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 DAMIEN MILLER 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.
+
+	 \warning None of these functions clears its memory after use. It
+	 \warning is the responsability of the calling routines to ensure
+	 \warning that any sensitive data (keystream, key or plaintext) is
+	 \warning properly erased after use.
+	 
+	 \warning The name "RC4" is trademarked in the United States, 
+	 \warning you may need to use "RC4 compatible" or "ARC4" 
+	 \warning (Alleged RC4).
+*/
+
+/* $Id: rc4.h,v 1.1.1.1 1999/10/26 05:48:13 damien Exp $ */
+
+#ifndef _RC4_H
+#define _RC4_H
+
+/*! \struct rc4_t
+    \brief RC4 stream cipher state object
+	 \var s State array
+	 \var i Monotonic index
+	 \var j Randomised index
+	 
+	 \warning This structure should not be accessed directly. To
+	 \warning initialise a rc4_t object, you should use the rc4_key()
+	 \warning function
+	 
+	 This structure holds the current state of the RC4 algorithm.
+*/
+typedef struct
+{
+	unsigned int s[256];
+	int i;
+	int j;
+} rc4_t;
+
+/*! \fn void rc4_key(rc4_t *r, unsigned char *key, int len);
+    \brief Set up key structure of RC4 stream cipher
+	 \param r pointer to RC4 structure to be seeded
+	 \param key pointer to buffer containing raw key
+	 \param len length of key
+	 
+	 This function set the internal state of the RC4 data structure
+	 pointed to by \a r using the specified \a key of length \a len.
+	 
+	 This function can use up to 256 bytes of key, any more are ignored.
+	 
+	 \warning Stream ciphers (such as RC4) can be insecure if the same
+	 \warning key is used repeatedly. Ensure that any key specified has
+	 \warning an reasonably sized Initialisation Vector component.
+*/
+void rc4_key(rc4_t *r, unsigned char *key, int len);
+
+/*! \fn rc4_crypt(rc4_t *r, unsigned char *plaintext, int len);
+    \brief Crypt bytes using RC4 algorithm
+	 \param r pointer to RC4 structure to be used
+	 \param plaintext Pointer to bytes to encrypt
+	 \param len number of bytes to crypt
+
+	 This function encrypts one or more bytes (pointed to by \a plaintext)
+	 using the RC4 algorithm. \a r is a state structure that must be 
+	 initialiased using the rc4_key() function prior to use.
+	 
+	 Since RC4 XORs each byte of plaintext with a byte of keystream,
+	 this function can be used for both encryption and decryption.
+*/
+void rc4_crypt(rc4_t *r, unsigned char *plaintext, int len);
+
+/*! \fn rc4_getbytes(rc4_t *r, unsigned char *buffer, int len);
+    \brief Generate key stream using the RC4 stream cipher
+	 \param r pointer to RC4 structure to be used
+	 \param buffer pointer to buffer in which to deposit keystream
+	 \param len number of bytes to deposit
+
+	 This function gives access to the raw RC4 key stream. In this 
+	 consiguration RC4 can be used as a fast, strong pseudo-random 
+	 number generator with a very long period.
+*/
+void rc4_getbytes(rc4_t *r, unsigned char *buffer, int len);
+
+#endif /* _RC4_H */
diff --git a/readconf.c b/readconf.c
new file mode 100644
index 0000000..281548d
--- /dev/null
+++ b/readconf.c
@@ -0,0 +1,684 @@
+/*
+
+readconf.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Sat Apr 22 00:03:10 1995 ylo
+
+Functions for reading the configuration files.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: readconf.c,v 1.1 1999/10/27 03:42:44 damien Exp $");
+
+#include "ssh.h"
+#include "cipher.h"
+#include "readconf.h"
+#include "xmalloc.h"
+
+/* Format of the configuration file:
+
+   # Configuration data is parsed as follows:
+   #  1. command line options
+   #  2. user-specific file
+   #  3. system-wide file
+   # Any configuration value is only changed the first time it is set.
+   # Thus, host-specific definitions should be at the beginning of the
+   # configuration file, and defaults at the end.
+
+   # Host-specific declarations.  These may override anything above.  A single
+   # host may match multiple declarations; these are processed in the order
+   # that they are given in.
+
+   Host *.ngs.fi ngs.fi
+     FallBackToRsh no
+
+   Host fake.com
+     HostName another.host.name.real.org
+     User blaah
+     Port 34289
+     ForwardX11 no
+     ForwardAgent no
+
+   Host books.com
+     RemoteForward 9999 shadows.cs.hut.fi:9999
+     Cipher 3des
+
+   Host fascist.blob.com
+     Port 23123
+     User tylonen
+     RhostsAuthentication no
+     PasswordAuthentication no
+
+   Host puukko.hut.fi
+     User t35124p
+     ProxyCommand ssh-proxy %h %p
+
+   Host *.fr
+     UseRsh yes
+
+   Host *.su
+     Cipher none
+     PasswordAuthentication no
+
+   # Defaults for various options
+   Host *
+     ForwardAgent no
+     ForwardX11 yes
+     RhostsAuthentication yes
+     PasswordAuthentication yes
+     RSAAuthentication yes
+     RhostsRSAAuthentication yes
+     FallBackToRsh no
+     UseRsh no
+     StrictHostKeyChecking yes
+     KeepAlives no
+     IdentityFile ~/.ssh/identity
+     Port 22
+     EscapeChar ~
+
+*/
+
+/* Keyword tokens. */
+
+typedef enum
+{
+  oForwardAgent, oForwardX11, oGatewayPorts, oRhostsAuthentication,
+  oPasswordAuthentication, oRSAAuthentication, oFallBackToRsh, oUseRsh,
+#ifdef KRB4
+  oKerberosAuthentication,
+#endif /* KRB4 */
+#ifdef AFS
+  oKerberosTgtPassing, oAFSTokenPassing,
+#endif
+  oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward, 
+  oUser, oHost, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand,
+  oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts,
+  oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression,
+  oCompressionLevel, oKeepAlives, oNumberOfPasswordPrompts, oTISAuthentication,
+  oUsePrivilegedPort
+} OpCodes;
+
+/* Textual representations of the tokens. */
+
+static struct
+{
+  const char *name;
+  OpCodes opcode;
+} keywords[] =
+{
+  { "forwardagent", oForwardAgent },
+  { "forwardx11", oForwardX11 },
+  { "gatewayports", oGatewayPorts },
+  { "useprivilegedport", oUsePrivilegedPort },
+  { "rhostsauthentication", oRhostsAuthentication },
+  { "passwordauthentication", oPasswordAuthentication },
+  { "rsaauthentication", oRSAAuthentication },
+#ifdef KRB4
+  { "kerberosauthentication", oKerberosAuthentication },
+#endif /* KRB4 */
+#ifdef AFS
+  { "kerberostgtpassing", oKerberosTgtPassing },
+  { "afstokenpassing", oAFSTokenPassing },
+#endif
+  { "fallbacktorsh", oFallBackToRsh },
+  { "usersh", oUseRsh },
+  { "identityfile", oIdentityFile },
+  { "hostname", oHostName },
+  { "proxycommand", oProxyCommand },
+  { "port", oPort },
+  { "cipher", oCipher },
+  { "remoteforward", oRemoteForward },
+  { "localforward", oLocalForward },
+  { "user", oUser },
+  { "host", oHost },
+  { "escapechar", oEscapeChar },
+  { "rhostsrsaauthentication", oRhostsRSAAuthentication },
+  { "globalknownhostsfile", oGlobalKnownHostsFile },
+  { "userknownhostsfile", oUserKnownHostsFile },
+  { "connectionattempts", oConnectionAttempts },
+  { "batchmode", oBatchMode },
+  { "checkhostip", oCheckHostIP },
+  { "stricthostkeychecking", oStrictHostKeyChecking },
+  { "compression", oCompression },
+  { "compressionlevel", oCompressionLevel },
+  { "keepalive", oKeepAlives },
+  { "numberofpasswordprompts", oNumberOfPasswordPrompts },
+  { "tisauthentication", oTISAuthentication },
+  { NULL, 0 }
+};
+
+/* Characters considered whitespace in strtok calls. */
+#define WHITESPACE " \t\r\n"
+
+
+/* Adds a local TCP/IP port forward to options.  Never returns if there
+   is an error. */
+
+void add_local_forward(Options *options, int port, const char *host,
+		       int host_port)
+{
+  Forward *fwd;
+  extern uid_t original_real_uid;
+  if ((port & 0xffff) != port)
+    fatal("Requested forwarding of nonexistent port %d.", port);
+  if (port < IPPORT_RESERVED && original_real_uid != 0)
+    fatal("Privileged ports can only be forwarded by root.\n");
+  if (options->num_local_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION)
+    fatal("Too many local forwards (max %d).", SSH_MAX_FORWARDS_PER_DIRECTION);
+  fwd = &options->local_forwards[options->num_local_forwards++];
+  fwd->port = port;
+  fwd->host = xstrdup(host);
+  fwd->host_port = host_port;
+}
+
+/* Adds a remote TCP/IP port forward to options.  Never returns if there
+   is an error. */
+
+void add_remote_forward(Options *options, int port, const char *host,
+		       int host_port)
+{
+  Forward *fwd;
+  if (options->num_remote_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION)
+    fatal("Too many remote forwards (max %d).", 
+	  SSH_MAX_FORWARDS_PER_DIRECTION);
+  fwd = &options->remote_forwards[options->num_remote_forwards++];
+  fwd->port = port;
+  fwd->host = xstrdup(host);
+  fwd->host_port = host_port;
+}
+
+/* Returns the number of the token pointed to by cp of length len.
+   Never returns if the token is not known. */
+
+static OpCodes parse_token(const char *cp, const char *filename, int linenum)
+{
+  unsigned int i;
+
+  for (i = 0; keywords[i].name; i++)
+    if (strcmp(cp, keywords[i].name) == 0)
+      return keywords[i].opcode;
+
+  fatal("%.200s line %d: Bad configuration option.",
+	filename, linenum);
+  /*NOTREACHED*/
+  return 0;
+}
+
+/* Processes a single option line as used in the configuration files.
+   This only sets those values that have not already been set. */
+
+void process_config_line(Options *options, const char *host,
+			 char *line, const char *filename, int linenum,
+			 int *activep)
+{
+  char buf[256], *cp, *string, **charptr;
+  int opcode, *intptr, value, fwd_port, fwd_host_port;
+
+  /* Skip leading whitespace. */
+  cp = line + strspn(line, WHITESPACE);
+  if (!*cp || *cp == '\n' || *cp == '#')
+    return;
+
+  /* Get the keyword. (Each line is supposed to begin with a keyword). */
+  cp = strtok(cp, WHITESPACE);
+  {
+    char *t = cp;
+    for (; *t != 0; t++)
+      if ('A' <= *t && *t <= 'Z')
+	*t = *t - 'A' + 'a';	/* tolower */
+      
+  }
+  opcode = parse_token(cp, filename, linenum);
+
+  switch (opcode)
+    {
+
+    case oForwardAgent:
+      intptr = &options->forward_agent;
+    parse_flag:
+      cp = strtok(NULL, WHITESPACE);
+      if (!cp)
+	fatal("%.200s line %d: Missing yes/no argument.", filename, linenum);
+      value = 0; /* To avoid compiler warning... */
+      if (strcmp(cp, "yes") == 0 || strcmp(cp, "true") == 0)
+	value = 1;
+      else if (strcmp(cp, "no") == 0 || strcmp(cp, "false") == 0)
+	value = 0;
+      else
+	fatal("%.200s line %d: Bad yes/no argument.", filename, linenum);
+      if (*activep && *intptr == -1)
+	*intptr = value;
+      break;
+      
+    case oForwardX11:
+      intptr = &options->forward_x11;
+      goto parse_flag;
+
+    case oGatewayPorts:
+      intptr = &options->gateway_ports;
+      goto parse_flag;
+      
+    case oUsePrivilegedPort:
+      intptr = &options->use_privileged_port;
+      goto parse_flag;
+      
+    case oRhostsAuthentication:
+      intptr = &options->rhosts_authentication;
+      goto parse_flag;
+      
+    case oPasswordAuthentication:
+      intptr = &options->password_authentication;
+      goto parse_flag;
+      
+    case oRSAAuthentication:
+      intptr = &options->rsa_authentication;
+      goto parse_flag;
+      
+    case oRhostsRSAAuthentication:
+      intptr = &options->rhosts_rsa_authentication;
+      goto parse_flag;
+
+#ifdef KRB4
+    case oKerberosAuthentication:
+      intptr = &options->kerberos_authentication;
+      goto parse_flag;
+#endif /* KRB4 */
+
+#ifdef AFS
+    case oKerberosTgtPassing:
+      intptr = &options->kerberos_tgt_passing;
+      goto parse_flag;
+
+    case oAFSTokenPassing:
+      intptr = &options->afs_token_passing;
+      goto parse_flag;
+#endif
+      
+    case oFallBackToRsh:
+      intptr = &options->fallback_to_rsh;
+      goto parse_flag;
+      
+    case oUseRsh:
+      intptr = &options->use_rsh;
+      goto parse_flag;
+
+    case oBatchMode:
+      intptr = &options->batch_mode;
+      goto parse_flag;
+
+    case oCheckHostIP:
+      intptr = &options->check_host_ip;
+      goto parse_flag;
+
+    case oStrictHostKeyChecking:
+      intptr = &options->strict_host_key_checking;
+      cp = strtok(NULL, WHITESPACE);
+      if (!cp)
+	fatal("%.200s line %d: Missing yes/no argument.",
+	      filename, linenum);
+      value = 0; /* To avoid compiler warning... */
+      if (strcmp(cp, "yes") == 0 || strcmp(cp, "true") == 0)
+	value = 1;
+      else if (strcmp(cp, "no") == 0 || strcmp(cp, "false") == 0)
+	value = 0;
+      else if (strcmp(cp, "ask") == 0)
+	value = 2;
+      else
+	fatal("%.200s line %d: Bad yes/no/ask argument.", filename, linenum);
+      if (*activep && *intptr == -1)
+	*intptr = value;
+      break;
+      
+    case oCompression:
+      intptr = &options->compression;
+      goto parse_flag;
+
+    case oKeepAlives:
+      intptr = &options->keepalives;
+      goto parse_flag;
+
+    case oNumberOfPasswordPrompts:
+      intptr = &options->number_of_password_prompts;
+      goto parse_int;
+      
+    case oTISAuthentication:
+      cp = strtok(NULL, WHITESPACE);
+      if (cp != 0 && (strcmp(cp, "yes") == 0 || strcmp(cp, "true") == 0))
+	fprintf(stderr,
+		"%.99s line %d: Warning, TIS is not supported.\n",
+		filename,
+		linenum);
+      break;
+
+    case oCompressionLevel:
+      intptr = &options->compression_level;
+      goto parse_int;
+
+    case oIdentityFile:
+      cp = strtok(NULL, WHITESPACE);
+      if (!cp)
+	fatal("%.200s line %d: Missing argument.", filename, linenum);
+      if (*activep)
+	{
+	  if (options->num_identity_files >= SSH_MAX_IDENTITY_FILES)
+	    fatal("%.200s line %d: Too many identity files specified (max %d).",
+		  filename, linenum, SSH_MAX_IDENTITY_FILES);
+	  options->identity_files[options->num_identity_files++] = xstrdup(cp);
+	}
+      break;
+      
+    case oUser:
+      charptr = &options->user;
+    parse_string:
+      cp = strtok(NULL, WHITESPACE);
+      if (!cp)
+	fatal("%.200s line %d: Missing argument.", filename, linenum);
+      if (*activep && *charptr == NULL)
+	*charptr = xstrdup(cp);
+      break;
+      
+    case oGlobalKnownHostsFile:
+      charptr = &options->system_hostfile;
+      goto parse_string;
+      
+    case oUserKnownHostsFile:
+      charptr = &options->user_hostfile;
+      goto parse_string;
+
+    case oHostName:
+      charptr = &options->hostname;
+      goto parse_string;
+      
+    case oProxyCommand:
+      charptr = &options->proxy_command;
+      string = xstrdup("");
+      while ((cp = strtok(NULL, WHITESPACE)) != NULL)
+	{
+	  string = xrealloc(string, strlen(string) + strlen(cp) + 2);
+	  strcat(string, " ");
+	  strcat(string, cp);
+	}
+      if (*activep && *charptr == NULL)
+	*charptr = string;
+      else
+	xfree(string);
+      return;
+
+    case oPort:
+      intptr = &options->port;
+    parse_int:
+      cp = strtok(NULL, WHITESPACE);
+      if (!cp)
+	fatal("%.200s line %d: Missing argument.", filename, linenum);
+      if (cp[0] < '0' || cp[0] > '9')
+	fatal("%.200s line %d: Bad number.", filename, linenum);
+#if 0
+      value = atoi(cp);
+#else
+      {
+	char *ptr;
+	value = strtol(cp, &ptr, 0); /* Octal, decimal, or hex format? */
+	if (cp == ptr)
+	  fatal("%.200s line %d: Bad number.", filename, linenum);	    
+      }
+#endif
+      if (*activep && *intptr == -1)
+	*intptr = value;
+      break;
+      
+    case oConnectionAttempts:
+      intptr = &options->connection_attempts;
+      goto parse_int;
+
+    case oCipher:
+      intptr = &options->cipher;
+      cp = strtok(NULL, WHITESPACE);
+      value = cipher_number(cp);
+      if (value == -1)
+	fatal("%.200s line %d: Bad cipher.", filename, linenum);
+      if (*activep && *intptr == -1)
+	*intptr = value;
+      break;
+      
+    case oRemoteForward:
+      cp = strtok(NULL, WHITESPACE);
+      if (!cp)
+	fatal("%.200s line %d: Missing argument.", filename, linenum);
+      if (cp[0] < '0' || cp[0] > '9')
+	fatal("%.200s line %d: Badly formatted port number.", 
+	      filename, linenum);
+      fwd_port = atoi(cp);
+      cp = strtok(NULL, WHITESPACE);
+      if (!cp)
+	fatal("%.200s line %d: Missing second argument.", 
+	      filename, linenum);
+      if (sscanf(cp, "%255[^:]:%d", buf, &fwd_host_port) != 2)
+	fatal("%.200s line %d: Badly formatted host:port.", 
+	      filename, linenum);
+      if (*activep)
+	add_remote_forward(options, fwd_port, buf, fwd_host_port);
+      break;
+      
+    case oLocalForward:
+      cp = strtok(NULL, WHITESPACE);
+      if (!cp)
+	fatal("%.200s line %d: Missing argument.", filename, linenum);
+      if (cp[0] < '0' || cp[0] > '9')
+	fatal("%.200s line %d: Badly formatted port number.", 
+	      filename, linenum);
+      fwd_port = atoi(cp);
+      cp = strtok(NULL, WHITESPACE);
+      if (!cp)
+	fatal("%.200s line %d: Missing second argument.", 
+	      filename, linenum);
+      if (sscanf(cp, "%255[^:]:%d", buf, &fwd_host_port) != 2)
+	fatal("%.200s line %d: Badly formatted host:port.", 
+	      filename, linenum);
+      if (*activep)
+	add_local_forward(options, fwd_port, buf, fwd_host_port);
+      break;
+      
+    case oHost:
+      *activep = 0;
+      while ((cp = strtok(NULL, WHITESPACE)) != NULL)
+	if (match_pattern(host, cp))
+	  {
+	    debug("Applying options for %.100s", cp);
+	    *activep = 1;
+	    break;
+	  }
+      /* Avoid garbage check below, as strtok already returned NULL. */
+      return;
+
+    case oEscapeChar:
+      intptr = &options->escape_char;
+      cp = strtok(NULL, WHITESPACE);
+      if (!cp)
+	fatal("%.200s line %d: Missing argument.", filename, linenum);
+      if (cp[0] == '^' && cp[2] == 0 && 
+	  (unsigned char)cp[1] >= 64 && (unsigned char)cp[1] < 128)
+	value = (unsigned char)cp[1] & 31;
+      else
+	if (strlen(cp) == 1)
+	  value = (unsigned char)cp[0];
+	else
+	  if (strcmp(cp, "none") == 0)
+	    value = -2;
+	  else
+	    {
+	      fatal("%.200s line %d: Bad escape character.", 
+		    filename, linenum);
+	      /*NOTREACHED*/
+	      value = 0; /* Avoid compiler warning. */
+	    }
+      if (*activep && *intptr == -1)
+	*intptr = value;
+      break;
+      
+    default:
+      fatal("parse_config_file: Unimplemented opcode %d", opcode);
+    }
+  
+  /* Check that there is no garbage at end of line. */
+  if (strtok(NULL, WHITESPACE) != NULL)
+    fatal("%.200s line %d: garbage at end of line.",
+	  filename, linenum);
+}
+
+
+/* Reads the config file and modifies the options accordingly.  Options should
+   already be initialized before this call.  This never returns if there
+   is an error.  If the file does not exist, this returns immediately. */
+
+void read_config_file(const char *filename, const char *host, Options *options)
+{
+  FILE *f;
+  char line[1024];
+  int active, linenum;
+
+  /* Open the file. */
+  f = fopen(filename, "r");
+  if (!f)
+    return;
+
+  debug("Reading configuration data %.200s", filename);
+
+  /* Mark that we are now processing the options.  This flag is turned on/off
+     by Host specifications. */
+  active = 1;
+  linenum = 0;
+  while (fgets(line, sizeof(line), f))
+    {
+      /* Update line number counter. */
+      linenum++;
+
+      process_config_line(options, host, line, filename, linenum, &active);
+    }
+  fclose(f);
+}
+
+/* Initializes options to special values that indicate that they have not
+   yet been set.  Read_config_file will only set options with this value.
+   Options are processed in the following order: command line, user config
+   file, system config file.  Last, fill_default_options is called. */
+
+void initialize_options(Options *options)
+{
+  memset(options, 'X', sizeof(*options));
+  options->forward_agent = -1;
+  options->forward_x11 = -1;
+  options->gateway_ports = -1;
+  options->use_privileged_port = -1;
+  options->rhosts_authentication = -1;
+  options->rsa_authentication = -1;
+#ifdef KRB4
+  options->kerberos_authentication = -1;
+#endif
+#ifdef AFS
+  options->kerberos_tgt_passing = -1;
+  options->afs_token_passing = -1;
+#endif
+  options->password_authentication = -1;
+  options->rhosts_rsa_authentication = -1;
+  options->fallback_to_rsh = -1;
+  options->use_rsh = -1;
+  options->batch_mode = -1;
+  options->check_host_ip = -1;
+  options->strict_host_key_checking = -1;
+  options->compression = -1;
+  options->keepalives = -1;
+  options->compression_level = -1;
+  options->port = -1;
+  options->connection_attempts = -1;
+  options->number_of_password_prompts = -1;
+  options->cipher = -1;
+  options->num_identity_files = 0;
+  options->hostname = NULL;
+  options->proxy_command = NULL;
+  options->user = NULL;
+  options->escape_char = -1;
+  options->system_hostfile = NULL;
+  options->user_hostfile = NULL;
+  options->num_local_forwards = 0;
+  options->num_remote_forwards = 0;
+}
+
+/* Called after processing other sources of option data, this fills those
+   options for which no value has been specified with their default values. */
+
+void fill_default_options(Options *options)
+{
+  if (options->forward_agent == -1)
+    options->forward_agent = 1;
+  if (options->forward_x11 == -1)
+    options->forward_x11 = 1;
+  if (options->gateway_ports == -1)
+    options->gateway_ports = 0;
+  if (options->use_privileged_port == -1)
+    options->use_privileged_port = 1;
+  if (options->rhosts_authentication == -1)
+    options->rhosts_authentication = 1;
+  if (options->rsa_authentication == -1)
+    options->rsa_authentication = 1;
+#ifdef KRB4
+  if (options->kerberos_authentication == -1)
+    options->kerberos_authentication = 1;
+#endif /* KRB4 */
+#ifdef AFS
+  if (options->kerberos_tgt_passing == -1)
+    options->kerberos_tgt_passing = 1;
+  if (options->afs_token_passing == -1)
+    options->afs_token_passing = 1;
+#endif /* AFS */
+  if (options->password_authentication == -1)
+    options->password_authentication = 1;
+  if (options->rhosts_rsa_authentication == -1)
+    options->rhosts_rsa_authentication = 1;
+  if (options->fallback_to_rsh == -1)
+    options->fallback_to_rsh = 1;
+  if (options->use_rsh == -1)
+    options->use_rsh = 0;
+  if (options->batch_mode == -1)
+    options->batch_mode = 0;
+  if (options->check_host_ip == -1)
+    options->check_host_ip = 1;
+  if (options->strict_host_key_checking == -1)
+    options->strict_host_key_checking = 2; /* 2 is default */
+  if (options->compression == -1)
+    options->compression = 0;
+  if (options->keepalives == -1)
+    options->keepalives = 1;
+  if (options->compression_level == -1)
+    options->compression_level = 6;
+  if (options->port == -1)
+    options->port = 0; /* Filled in ssh_connect. */
+  if (options->connection_attempts == -1)
+    options->connection_attempts = 4;
+  if (options->number_of_password_prompts == -1)
+    options->number_of_password_prompts = 3;
+  if (options->cipher == -1)
+    options->cipher = SSH_CIPHER_NOT_SET; /* Selected in ssh_login(). */
+  if (options->num_identity_files == 0)
+    {
+      options->identity_files[0] = 
+	xmalloc(2 + strlen(SSH_CLIENT_IDENTITY) + 1);
+      sprintf(options->identity_files[0], "~/%.100s", SSH_CLIENT_IDENTITY);
+      options->num_identity_files = 1;
+    }
+  if (options->escape_char == -1)
+    options->escape_char = '~';
+  if (options->system_hostfile == NULL)
+    options->system_hostfile = SSH_SYSTEM_HOSTFILE;
+  if (options->user_hostfile == NULL)
+    options->user_hostfile = SSH_USER_HOSTFILE;
+  /* options->proxy_command should not be set by default */
+  /* options->user will be set in the main program if appropriate */
+  /* options->hostname will be set in the main program if appropriate */
+}
+
diff --git a/readconf.h b/readconf.h
new file mode 100644
index 0000000..71655bd
--- /dev/null
+++ b/readconf.h
@@ -0,0 +1,116 @@
+/*
+
+readconf.h
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Sat Apr 22 00:25:29 1995 ylo
+
+Functions for reading the configuration file.
+
+*/
+
+/* RCSID("$Id: readconf.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */
+
+#ifndef READCONF_H
+#define READCONF_H
+
+/* Data structure for representing a forwarding request. */
+
+typedef struct
+{
+  int port;		/* Port to forward. */
+  char *host;		/* Host to connect. */
+  int host_port;	/* Port to connect on host. */
+} Forward;
+
+/* Data structure for representing option data. */
+
+typedef struct
+{
+  int forward_agent;		/* Forward authentication agent. */
+  int forward_x11;		/* Forward X11 display. */
+  int gateway_ports;		/* Allow remote connects to forwarded ports. */
+  int use_privileged_port;	/* Don't use privileged port if false. */
+  int rhosts_authentication;	/* Try rhosts authentication. */
+  int rhosts_rsa_authentication;/* Try rhosts with RSA authentication. */
+  int rsa_authentication;	/* Try RSA authentication. */
+#ifdef KRB4
+  int kerberos_authentication;	/* Try Kerberos authentication. */
+#endif
+#ifdef AFS
+  int kerberos_tgt_passing;	/* Try Kerberos tgt passing. */
+  int afs_token_passing;	/* Try AFS token passing. */
+#endif
+  int password_authentication;	/* Try password authentication. */
+  int fallback_to_rsh;		/* Use rsh if cannot connect with ssh. */
+  int use_rsh;			/* Always use rsh (don\'t try ssh). */
+  int batch_mode;		/* Batch mode: do not ask for passwords. */
+  int check_host_ip;		/* Also keep track of keys for IP address */
+  int strict_host_key_checking;	/* Strict host key checking. */
+  int compression;		/* Compress packets in both directions. */
+  int compression_level;	/* Compression level 1 (fast) to 9 (best). */
+  int keepalives;		/* Set SO_KEEPALIVE. */
+
+  int port;			/* Port to connect. */
+  int connection_attempts;	/* Max attempts (seconds) before giving up */
+  int number_of_password_prompts; /* Max number of password prompts. */
+  int cipher;			/* Cipher to use. */
+  char *hostname;		/* Real host to connect. */
+  char *proxy_command;		/* Proxy command for connecting the host. */
+  char *user;			/* User to log in as. */
+  int escape_char;		/* Escape character; -2 = none */
+
+  char *system_hostfile;	/* Path for /etc/ssh_known_hosts. */
+  char *user_hostfile;		/* Path for $HOME/.ssh/known_hosts. */
+
+  int num_identity_files;	/* Number of files for RSA identities. */
+  char *identity_files[SSH_MAX_IDENTITY_FILES];
+
+  /* Local TCP/IP forward requests. */
+  int num_local_forwards;
+  Forward local_forwards[SSH_MAX_FORWARDS_PER_DIRECTION];
+
+  /* Remote TCP/IP forward requests. */
+  int num_remote_forwards;
+  Forward remote_forwards[SSH_MAX_FORWARDS_PER_DIRECTION];
+} Options;
+
+
+/* Initializes options to special values that indicate that they have not
+   yet been set.  Read_config_file will only set options with this value.
+   Options are processed in the following order: command line, user config
+   file, system config file.  Last, fill_default_options is called. */
+void initialize_options(Options *options);
+
+/* Called after processing other sources of option data, this fills those
+   options for which no value has been specified with their default values. */
+void fill_default_options(Options *options);
+
+/* Processes a single option line as used in the configuration files. 
+   This only sets those values that have not already been set. */
+void process_config_line(Options *options, const char *host,
+			 char *line, const char *filename, int linenum,
+			 int *activep);
+
+/* Reads the config file and modifies the options accordingly.  Options should
+   already be initialized before this call.  This never returns if there
+   is an error.  If the file does not exist, this returns immediately. */
+void read_config_file(const char *filename, const char *host, 
+		      Options *options);
+
+/* Adds a local TCP/IP port forward to options.  Never returns if there
+   is an error. */
+void add_local_forward(Options *options, int port, const char *host,
+		       int host_port);
+
+/* Adds a remote TCP/IP port forward to options.  Never returns if there
+   is an error. */
+void add_remote_forward(Options *options, int port, const char *host,
+			int host_port);
+
+
+#endif /* READCONF_H */
diff --git a/readpass.c b/readpass.c
new file mode 100644
index 0000000..3031825
--- /dev/null
+++ b/readpass.c
@@ -0,0 +1,114 @@
+/*
+
+readpass.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Mon Jul 10 22:08:59 1995 ylo
+
+Functions for reading passphrases and passwords.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: readpass.c,v 1.1 1999/10/27 03:42:44 damien Exp $");
+
+#include "xmalloc.h"
+#include "ssh.h"
+
+/* Saved old terminal mode for read_passphrase. */
+static struct termios saved_tio;
+
+/* Old interrupt signal handler for read_passphrase. */
+static void (*old_handler)(int sig) = NULL;
+
+/* Interrupt signal handler for read_passphrase. */
+
+void intr_handler(int sig)
+{
+  /* Restore terminal modes. */
+  tcsetattr(fileno(stdin), TCSANOW, &saved_tio);
+  /* Restore the old signal handler. */
+  signal(sig, old_handler);
+  /* Resend the signal, with the old handler. */
+  kill(getpid(), sig);
+}
+
+/* Reads a passphrase from /dev/tty with echo turned off.  Returns the 
+   passphrase (allocated with xmalloc).  Exits if EOF is encountered. 
+   The passphrase if read from stdin if from_stdin is true (as is the
+   case with ssh-keygen).  */
+
+char *read_passphrase(const char *prompt, int from_stdin)
+{
+  char buf[1024], *cp;
+  struct termios tio;
+  FILE *f;
+  
+  if (from_stdin)
+    f = stdin;
+  else
+    {
+      /* Read the passphrase from /dev/tty to make it possible to ask it even 
+	 when stdin has been redirected. */
+      f = fopen("/dev/tty", "r");
+      if (!f)
+	{
+	  /* No controlling terminal and no DISPLAY.  Nowhere to read. */
+	  fprintf(stderr, "You have no controlling tty and no DISPLAY.  Cannot read passphrase.\n");
+	  exit(1);
+	}
+    }
+
+  /* Display the prompt (on stderr because stdout might be redirected). */
+  fflush(stdout);
+  fprintf(stderr, "%s", prompt);
+  fflush(stderr);
+
+  /* Get terminal modes. */
+  tcgetattr(fileno(f), &tio);
+  saved_tio = tio;
+  /* Save signal handler and set the new handler. */
+  old_handler = signal(SIGINT, intr_handler);
+
+  /* Set new terminal modes disabling all echo. */
+  tio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
+  tcsetattr(fileno(f), TCSANOW, &tio);
+
+  /* Read the passphrase from the terminal. */
+  if (fgets(buf, sizeof(buf), f) == NULL)
+    {
+      /* Got EOF.  Just exit. */
+      /* Restore terminal modes. */
+      tcsetattr(fileno(f), TCSANOW, &saved_tio);
+      /* Restore the signal handler. */
+      signal(SIGINT, old_handler);
+      /* Print a newline (the prompt probably didn\'t have one). */
+      fprintf(stderr, "\n");
+      /* Close the file. */
+      if (f != stdin)
+	fclose(f);
+      exit(1);
+    }
+  /* Restore terminal modes. */
+  tcsetattr(fileno(f), TCSANOW, &saved_tio);
+  /* Restore the signal handler. */
+  (void)signal(SIGINT, old_handler);
+  /* Remove newline from the passphrase. */
+  if (strchr(buf, '\n'))
+    *strchr(buf, '\n') = 0;
+  /* Allocate a copy of the passphrase. */
+  cp = xstrdup(buf);
+  /* Clear the buffer so we don\'t leave copies of the passphrase laying
+     around. */
+  memset(buf, 0, sizeof(buf));
+  /* Print a newline since the prompt probably didn\'t have one. */
+  fprintf(stderr, "\n");
+  /* Close the file. */
+  if (f != stdin)
+    fclose(f);
+  return cp;
+}
diff --git a/rsa.c b/rsa.c
new file mode 100644
index 0000000..6d4b704
--- /dev/null
+++ b/rsa.c
@@ -0,0 +1,164 @@
+/*
+
+rsa.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Fri Mar  3 22:07:06 1995 ylo
+
+Description of the RSA algorithm can be found e.g. from the following sources:
+
+  Bruce Schneier: Applied Cryptography.  John Wiley & Sons, 1994.
+
+  Jennifer Seberry and Josed Pieprzyk: Cryptography: An Introduction to 
+    Computer Security.  Prentice-Hall, 1989.
+
+  Man Young Rhee: Cryptography and Secure Data Communications.  McGraw-Hill, 
+    1994.
+
+  R. Rivest, A. Shamir, and L. M. Adleman: Cryptographic Communications
+    System and Method.  US Patent 4,405,829, 1983.
+
+  Hans Riesel: Prime Numbers and Computer Methods for Factorization.  
+    Birkhauser, 1994.
+
+  The RSA Frequently Asked Questions document by RSA Data Security, Inc., 1995.
+
+  RSA in 3 lines of perl by Adam Back <aba@atlax.ex.ac.uk>, 1995, as included
+    below:
+
+    gone - had to be deleted - what a pity
+
+*/
+
+#include "includes.h"
+RCSID("$Id: rsa.c,v 1.1 1999/10/27 03:42:44 damien Exp $");
+
+#include "rsa.h"
+#include "ssh.h"
+#include "xmalloc.h"
+
+int rsa_verbose = 1;
+
+int
+rsa_alive()
+{
+  RSA *key;
+
+  key = RSA_generate_key(32, 3, NULL, NULL);
+  if (key == NULL)
+    return (0);
+  RSA_free(key);
+  return (1);
+}
+
+/* Generates RSA public and private keys.  This initializes the data
+   structures; they should be freed with rsa_clear_private_key and
+   rsa_clear_public_key. */
+
+void
+rsa_generate_key(RSA *prv, RSA *pub, unsigned int bits)
+{
+  RSA *key;
+
+  if (rsa_verbose) {
+    printf("Generating RSA keys:  "); 
+    fflush(stdout);
+  }
+
+  key = RSA_generate_key(bits, 35, NULL, NULL);
+
+  assert(key != NULL);
+
+  /* Copy public key parameters */
+  pub->n = BN_new();
+  BN_copy(pub->n, key->n);
+  pub->e = BN_new();
+  BN_copy(pub->e, key->e);
+
+  /* Copy private key parameters */
+  prv->n = BN_new();
+  BN_copy(prv->n, key->n);
+  prv->e = BN_new();
+  BN_copy(prv->e, key->e);
+  prv->d = BN_new();
+  BN_copy(prv->d, key->d);
+  prv->p = BN_new();
+  BN_copy(prv->p, key->p);
+  prv->q = BN_new();
+  BN_copy(prv->q, key->q);
+
+  prv->dmp1 = BN_new();
+  BN_copy(prv->dmp1, key->dmp1);
+
+  prv->dmq1 = BN_new();
+  BN_copy(prv->dmq1, key->dmq1);
+
+  prv->iqmp = BN_new();
+  BN_copy(prv->iqmp, key->iqmp);
+
+  RSA_free(key);
+  
+  if (rsa_verbose)
+    printf("Key generation complete.\n");
+}
+
+void
+rsa_public_encrypt(BIGNUM *out, BIGNUM *in, RSA* key)
+{
+  char *inbuf, *outbuf;
+  int len;
+
+  if (BN_num_bits(key->e) < 2 || !BN_is_odd(key->e))
+    fatal("rsa_public_encrypt() exponent too small or not odd");
+
+  len = BN_num_bytes(key->n);
+  outbuf = xmalloc(len);
+
+  len = BN_num_bytes(in);
+  inbuf = xmalloc(len);
+  BN_bn2bin(in, inbuf);
+
+  if ((len = RSA_public_encrypt(len, inbuf, outbuf, key,
+				RSA_PKCS1_PADDING)) <= 0)
+    fatal("rsa_public_encrypt() failed");
+
+  BN_bin2bn(outbuf, len, out);
+
+  xfree(outbuf);
+  xfree(inbuf);
+}
+
+void
+rsa_private_decrypt(BIGNUM *out, BIGNUM *in, RSA *key)
+{
+  char *inbuf, *outbuf;
+  int len;
+
+  len = BN_num_bytes(key->n);
+  outbuf = xmalloc(len);
+
+  len = BN_num_bytes(in);
+  inbuf = xmalloc(len);
+  BN_bn2bin(in, inbuf);
+
+  if ((len = RSA_private_decrypt(len, inbuf, outbuf, key,
+				 RSA_SSLV23_PADDING)) <= 0)
+    fatal("rsa_private_decrypt() failed");
+
+  BN_bin2bn(outbuf, len, out);
+
+  xfree(outbuf);
+  xfree(inbuf);
+}
+
+/* Set whether to output verbose messages during key generation. */
+
+void
+rsa_set_verbose(int verbose)
+{
+  rsa_verbose = verbose;
+}
diff --git a/rsa.h b/rsa.h
new file mode 100644
index 0000000..6aaabfa
--- /dev/null
+++ b/rsa.h
@@ -0,0 +1,36 @@
+/*
+
+rsa.h
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Fri Mar  3 22:01:06 1995 ylo
+
+RSA key generation, encryption and decryption.
+
+*/
+
+/* RCSID("$Id: rsa.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */
+
+#ifndef RSA_H
+#define RSA_H
+
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+
+/* Calls SSL RSA_generate_key, only copies to prv and pub */
+void rsa_generate_key(RSA *prv, RSA *pub, unsigned int bits);
+
+/* Indicates whether the rsa module is permitted to show messages on
+   the terminal. */
+void rsa_set_verbose __P((int verbose));
+
+int  rsa_alive __P((void));
+
+void rsa_public_encrypt __P((BIGNUM *out, BIGNUM *in, RSA *prv));
+void rsa_private_decrypt __P((BIGNUM *out, BIGNUM *in, RSA *prv));
+
+#endif /* RSA_H */
diff --git a/scp.1 b/scp.1
new file mode 100644
index 0000000..45cd2ad
--- /dev/null
+++ b/scp.1
@@ -0,0 +1,110 @@
+.\"  -*- nroff -*-
+.\"
+.\" scp.1
+.\"
+.\" Author: Tatu Ylonen <ylo@cs.hut.fi>
+.\"
+.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+.\"                    All rights reserved
+.\"
+.\" Created: Sun May  7 00:14:37 1995 ylo
+.\"
+.\" $Id: scp.1,v 1.1 1999/10/27 03:42:44 damien Exp $
+.\"
+.Dd September 25, 1999
+.Dt SCP 1
+.Os
+.Sh NAME
+.Nm scp
+.Nd secure copy (remote file copy program)
+.Sh SYNOPSIS
+.Nm scp
+.Op Fl pqrvC
+.Op Fl P Ar port
+.Op Fl c Ar cipher
+.Op Fl i Ar identity_file
+.Sm off
+.Oo
+.Op Ar user@
+.Ar host1 No :
+.Oc Ns Ar file1
+.Sm on
+.Op Ar ...
+.Sm off
+.Oo
+.Op Ar user@
+.Ar host2 No :
+.Oc Ar file2
+.Sm on
+.Sh DESCRIPTION 
+.Nm
+copies files between hosts on a network.  It uses
+.Xr ssh 1
+for data transfer, and uses the same authentication and provides the
+same security as
+.Xr ssh 1 .
+Unlike
+.Xr rcp 1 ,
+.Nm
+will ask for passwords or passphrases if they are needed for
+authentication.
+.Pp
+Any file name may contain a host and user specification to indicate
+that the file is to be copied to/from that host.  Copies between two
+remote hosts are permitted.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl c Ar cipher
+Selects the cipher to use for encrypting the data transfer.  This
+option is directly passed to
+.Xr ssh 1 .
+.It Fl i Ar identity_file
+Selects the file from which the identity (private key) for RSA
+authentication is read.  This option is directly passed to
+.Xr ssh 1 .
+.It Fl p
+Preserves modification times, access times, and modes from the
+original file.
+.It Fl r
+Recursively copy entire directories.
+.It Fl v
+Verbose mode.  Causes
+.Nm
+and 
+.Xr ssh 1
+to print debugging messages about their progress.  This is helpful in
+debugging connection, authentication, and configuration problems.
+.It Fl B
+Selects batch mode (prevents asking for passwords or passphrases).
+.It Fl q
+Disables the progress meter.
+.It Fl C
+Compression enable.  Passes the
+.Fl C
+flag to
+.Xr ssh 1
+to enable compression.
+.It Fl P Ar port
+Specifies the port to connect to on the remote host.  Note that this
+option is written with a capital
+.Sq P ,
+because
+.Fl p
+is already reserved for preserving the times and modes of the file in
+.Xr rcp 1 .
+.Sh AUTHORS
+Timo Rinne <tri@iki.fi> and Tatu Ylonen <ylo@cs.hut.fi>
+.Sh HISTORY
+.Nm
+is based on the
+.Xr rcp 1
+program in BSD source code from the Regents of the University of
+California.
+.Sh SEE ALSO
+.Xr rcp 1 ,
+.Xr ssh 1 ,
+.Xr ssh-add 1 ,
+.Xr ssh-agent 1 ,
+.Xr ssh-keygen 1 ,
+.Xr sshd 8
diff --git a/scp.c b/scp.c
new file mode 100644
index 0000000..6145fde
--- /dev/null
+++ b/scp.c
@@ -0,0 +1,1220 @@
+/*
+
+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 <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi>
+     
+*/
+
+/*
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *	$Id: scp.c,v 1.1 1999/10/27 03:42:45 damien Exp $
+ */
+
+#include "includes.h"
+RCSID("$Id: scp.c,v 1.1 1999/10/27 03:42:45 damien Exp $");
+
+#include "ssh.h"
+#include "xmalloc.h"
+#include <utime.h>
+
+#define _PATH_CP "cp"
+
+/* For progressmeter() -- number of seconds before xfer considered "stalled" */
+#define STALLTIME	5
+
+/* Visual statistics about files as they are transferred. */
+void progressmeter(int);
+
+/* Returns width of the terminal (for progress meter calculations). */
+int getttywidth(void);
+
+/* Time a transfer started. */
+static struct timeval start;
+
+/* Number of bytes of current file transferred so far. */
+volatile unsigned long statbytes;
+
+/* Total size of current file. */
+unsigned long totalbytes = 0;
+
+/* Name of current file being transferred. */
+char *curfile;
+
+/* This is set to non-zero to enable verbose mode. */
+int verbose = 0;
+
+/* This is set to non-zero if compression is desired. */
+int compress = 0;
+
+/* This is set to zero if the progressmeter is not desired. */
+int showprogress = 1;
+
+/* This is set to non-zero if running in batch mode (that is, password
+   and passphrase queries are not allowed). */
+int batchmode = 0;
+
+/* This is set to the cipher type string if given on the command line. */
+char *cipher = NULL;
+
+/* This is set to the RSA authentication identity file name if given on 
+   the command line. */
+char *identity = NULL;
+
+/* This is the port to use in contacting the remote site (is non-NULL). */
+char *port = NULL;
+
+/* 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 pin[2], pout[2], reserved[2];
+
+  if (verbose)
+    fprintf(stderr, "Executing: host %s, user %s, command %s\n",
+	    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)
+    fatal("pipe: %s", strerror(errno));
+  if (pipe(pout) < 0)
+    fatal("pipe: %s", strerror(errno));
+
+  /* Free the reserved descriptors. */
+  close(reserved[0]);
+  close(reserved[1]);
+
+  /* For a child to execute the command on the remote host using ssh. */
+  if (fork() == 0) 
+    {
+      char *args[100];
+      unsigned int i;
+
+      /* Child. */
+      close(pin[1]);
+      close(pout[0]);
+      dup2(pin[0], 0);
+      dup2(pout[1], 1);
+      close(pin[0]);
+      close(pout[1]);
+
+      i = 0;
+      args[i++] = SSH_PROGRAM;
+      args[i++] = "-x";
+      args[i++] = "-oFallBackToRsh no";
+      if (verbose)
+	args[i++] = "-v";
+      if (compress)
+	args[i++] = "-C";
+      if (batchmode)
+	args[i++] = "-oBatchMode yes";
+      if (cipher != NULL)
+	{
+	  args[i++] = "-c";
+	  args[i++] = cipher;
+	}
+      if (identity != NULL)
+	{
+	  args[i++] = "-i";
+	  args[i++] = identity;
+	}
+      if (port != NULL)
+	{
+	  args[i++] = "-p";
+	  args[i++] = port;
+	}
+      if (remuser != NULL)
+	{
+	  args[i++] = "-l";
+	  args[i++] = remuser;
+	}
+      args[i++] = host;
+      args[i++] = cmd;
+      args[i++] = NULL;
+
+      execvp(SSH_PROGRAM, args);
+      perror(SSH_PROGRAM);
+      exit(1);
+    }
+  /* Parent.  Close the other side, and return the local side. */
+  close(pin[0]);
+  *fdout = pin[1];
+  close(pout[1]);
+  *fdin = pout[0];
+  return 0;
+}
+
+void fatal(const char *fmt, ...)
+{
+  va_list ap;
+  char buf[1024];
+
+  va_start(ap, fmt);
+  vsnprintf(buf, sizeof(buf), fmt, ap);
+  va_end(ap);
+  fprintf(stderr, "%s\n", buf);
+  exit(255);
+}
+
+/* This stuff used to be in BSD rcp extern.h. */
+
+typedef struct {
+	int cnt;
+	char *buf;
+} BUF;
+
+extern int iamremote;
+
+BUF	*allocbuf(BUF *, int, int);
+char	*colon(char *);
+void	 lostconn(int);
+void	 nospace(void);
+int	 okname(char *);
+void	 run_err(const char *, ...);
+void	 verifydir(char *);
+
+/* Stuff from BSD rcp.c continues. */
+
+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(argc, argv)
+	int argc;
+	char *argv[];
+{
+	int ch, fflag, tflag;
+	char *targ;
+	extern char *optarg;
+	extern int optind;
+
+	fflag = tflag = 0;
+	while ((ch = getopt(argc, argv,  "dfprtvBCc:i:P:q")) != EOF)
+		switch(ch) {			/* User-visible flags. */
+		case 'p':
+			pflag = 1;
+			break;
+		case 'P':
+		  	port = optarg;
+		  	break;
+		case 'r':
+			iamrecursive = 1;
+			break;
+						/* Server options. */
+		case 'd':
+			targetshouldbedirectory = 1;
+			break;
+		case 'f':			/* "from" */
+			iamremote = 1;
+			fflag = 1;
+			break;
+		case 't':			/* "to" */
+			iamremote = 1;
+			tflag = 1;
+			break;
+		case 'c':
+			cipher = optarg;
+		  	break;
+		case 'i':
+		  	identity = optarg;
+			break;
+		case 'v':
+			verbose = 1;
+		  	break;
+		case 'B':
+		  	batchmode = 1;
+		  	break;
+		case 'C':
+		  	compress = 1;
+		  	break;
+		case 'q':
+		  	showprogress = 0;
+		  	break;
+		case '?':
+		default:
+			usage();
+		}
+	argc -= optind;
+	argv += optind;
+
+	if ((pwd = getpwuid(userid = getuid())) == NULL)
+		fatal("unknown user %d", (int)userid);
+
+	if (! isatty(STDERR_FILENO))
+		showprogress = 0;
+
+	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;
+	/* Command to be executed on remote system using "ssh". */
+  	(void)sprintf(cmd, "scp%s%s%s%s", verbose ? " -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]);
+	}
+	exit(errs != 0);
+}
+
+void
+toremote(targ, argc, argv)
+	char *targ, *argv[];
+	int argc;
+{
+	int i, len;
+	char *bp, *host, *src, *suser, *thost, *tuser;
+
+	*targ++ = 0;
+	if (*targ == 0)
+		targ = ".";
+
+	if ((thost = strchr(argv[argc - 1], '@'))) {
+		/* user@host */
+		*thost++ = 0;
+		tuser = argv[argc - 1];
+		if (*tuser == '\0')
+			tuser = NULL;
+		else if (!okname(tuser))
+			exit(1);
+	} else {
+		thost = argv[argc - 1];
+		tuser = NULL;
+	}
+
+	for (i = 0; i < argc - 1; i++) {
+		src = colon(argv[i]);
+		if (src) {			/* remote to remote */
+			*src++ = 0;
+			if (*src == 0)
+				src = ".";
+			host = strchr(argv[i], '@');
+			len = strlen(SSH_PROGRAM) + strlen(argv[i]) +
+			    strlen(src) + (tuser ? strlen(tuser) : 0) +
+			    strlen(thost) + strlen(targ) + CMDNEEDS + 32;
+		        bp = xmalloc(len);
+			if (host) {
+				*host++ = 0;
+				suser = argv[i];
+				if (*suser == '\0')
+					suser = pwd->pw_name;
+				else if (!okname(suser))
+					continue;
+				(void)sprintf(bp, 
+				    "%s%s -x -o'FallBackToRsh no' -n -l %s %s %s %s '%s%s%s:%s'",
+				    SSH_PROGRAM, verbose ? " -v" : "",
+				    suser, host, cmd, src,
+				    tuser ? tuser : "", tuser ? "@" : "",
+				    thost, targ);
+			} else
+				(void)sprintf(bp,
+				    "exec %s%s -x -o'FallBackToRsh no' -n %s %s %s '%s%s%s:%s'",
+				    SSH_PROGRAM, verbose ? " -v" : "",
+				    argv[i], cmd, src,
+				    tuser ? tuser : "", tuser ? "@" : "",
+				    thost, targ);
+		        if (verbose)
+			  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)sprintf(bp, "%s -t %s", cmd, targ);
+				host = thost;
+				if (do_cmd(host,  tuser,
+					   bp, &remin, &remout) < 0)
+				  exit(1);
+				if (response() < 0)
+					exit(1);
+				(void)xfree(bp);
+			}
+			source(1, argv+i);
+		}
+	}
+}
+
+void
+tolocal(argc, argv)
+	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)sprintf(bp, "exec %s%s%s %s %s", _PATH_CP,
+			    iamrecursive ? " -r" : "", pflag ? " -p" : "",
+			    argv[i], argv[argc - 1]);
+	  		if (verbose)
+			  fprintf(stderr, "Executing: %s\n", bp);
+			if (system(bp))
+				++errs;
+			(void)xfree(bp);
+			continue;
+		}
+		*src++ = 0;
+		if (*src == 0)
+			src = ".";
+		if ((host = strchr(argv[i], '@')) == NULL) {
+			host = argv[i];
+			suser = NULL;
+		} else {
+			*host++ = 0;
+			suser = argv[i];
+			if (*suser == '\0')
+				suser = pwd->pw_name;
+			else if (!okname(suser))
+				continue;
+		}
+		len = strlen(src) + CMDNEEDS + 20;
+	        bp = xmalloc(len);
+		(void)sprintf(bp, "%s -f %s", cmd, src);
+	  	if (do_cmd(host, suser, bp, &remin, &remout) < 0) {
+		  (void)xfree(bp);
+		  ++errs;
+		  continue;
+		}
+	  	xfree(bp);
+		sink(1, argv + argc - 1);
+		(void)close(remin);
+		remin = remout = -1;
+	}
+}
+
+void
+source(argc, argv)
+	int argc;
+	char *argv[];
+{
+	struct stat stb;
+	static BUF buffer;
+	BUF *bp;
+	off_t i;
+	int amt, fd, haderr, indx, result;
+	char *last, *name, buf[2048];
+
+	for (indx = 0; indx < argc; ++indx) {
+                name = argv[indx];
+		statbytes = 0;
+		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)sprintf(buf, "T%lu 0 %lu 0\n",
+				      (unsigned long)stb.st_mtime, 
+				      (unsigned long)stb.st_atime);
+			(void)write(remout, buf, strlen(buf));
+			if (response() < 0)
+				goto next;
+		}
+#define	FILEMODEMASK	(S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
+		(void)sprintf(buf, "C%04o %lu %s\n",
+			      (unsigned int)(stb.st_mode & FILEMODEMASK), 
+			      (unsigned long)stb.st_size, 
+			      last);
+	        if (verbose)
+		  {
+		    fprintf(stderr, "Sending file modes: %s", buf);
+		    fflush(stderr);
+		  }
+		(void)write(remout, buf, strlen(buf));
+		if (response() < 0)
+			goto next;
+		if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) {
+next:			(void)close(fd);
+			continue;
+		}
+
+		if (showprogress) {
+			totalbytes = stb.st_size;
+			progressmeter(-1);
+		}
+
+		/* 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 = read(fd, bp->buf, amt);
+				if (result != amt)
+					haderr = result >= 0 ? EIO : errno;
+			}
+			if (haderr)
+				(void)write(remout, bp->buf, amt);
+			else {
+				result = write(remout, bp->buf, amt);
+				if (result != amt)
+					haderr = result >= 0 ? EIO : errno;
+				statbytes += result;
+			}
+		}
+		if(showprogress)
+			progressmeter(1);
+
+		if (close(fd) < 0 && !haderr)
+			haderr = errno;
+		if (!haderr)
+			(void)write(remout, "", 1);
+		else
+			run_err("%s: %s", name, strerror(haderr));
+		(void)response();
+	}
+}
+
+void
+rsource(name, statp)
+	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)sprintf(path, "T%lu 0 %lu 0\n",
+			      (unsigned long)statp->st_mtime, 
+			      (unsigned long)statp->st_atime);
+		(void)write(remout, path, strlen(path));
+		if (response() < 0) {
+			closedir(dirp);
+			return;
+		}
+	}
+	(void)sprintf(path, 
+	    "D%04o %d %.1024s\n", (unsigned int)(statp->st_mode & FILEMODEMASK),
+		      0, last);
+  	if (verbose)
+	  fprintf(stderr, "Entering directory: %s", path);
+	(void)write(remout, path, strlen(path));
+	if (response() < 0) {
+		closedir(dirp);
+		return;
+	}
+	while ((dp = readdir(dirp))) {
+		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)sprintf(path, "%s/%s", name, dp->d_name);
+		vect[0] = path;
+		source(1, vect);
+	}
+	(void)closedir(dirp);
+	(void)write(remout, "E\n", 2);
+	(void)response();
+}
+
+void
+sink(argc, argv)
+	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;
+	int setimes, size, targisdir, wrerrno = 0;
+	char ch, *cp, *np, *targ, *why, *vect[1], buf[2048];
+  	struct utimbuf ut;
+  	int dummy_usec;
+
+#define	SCREWUP(str)	{ why = str; goto screwup; }
+
+	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)write(remout, "", 1);
+	if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
+		targisdir = 1;
+	for (first = 1;; first = 0) {
+		cp = buf;
+		if (read(remin, cp, 1) <= 0)
+			return;
+		if (*cp++ == '\n')
+			SCREWUP("unexpected <newline>");
+		do {
+			if (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)write(STDERR_FILENO,
+				    buf + 1, strlen(buf + 1));
+			if (buf[0] == '\02')
+				exit(1);
+			++errs;
+			continue;
+		}
+		if (buf[0] == 'E') {
+			(void)write(remout, "", 1);
+			return;
+		}
+
+		if (ch == '\n')
+			*--cp = 0;
+
+#define getnum(t) (t) = 0; \
+  while (*cp >= '0' && *cp <= '9') (t) = (t) * 10 + (*cp++ - '0');
+		cp = buf;
+		if (*cp == 'T') {
+			setimes++;
+			cp++;
+			getnum(ut.modtime);
+			if (*cp++ != ' ')
+				SCREWUP("mtime.sec not delimited");
+			getnum(dummy_usec);
+			if (*cp++ != ' ')
+				SCREWUP("mtime.usec not delimited");
+			getnum(ut.actime);
+			if (*cp++ != ' ')
+				SCREWUP("atime.sec not delimited");
+			getnum(dummy_usec);
+			if (*cp++ != '\0')
+				SCREWUP("atime.usec not delimited");
+			(void)write(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; *cp >= '0' && *cp <= '9';)
+			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)
+			  namebuf = xmalloc(need);
+			(void)sprintf(namebuf, "%s%s%s", targ,
+			    *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] = np;
+			sink(1, vect);
+			if (setimes) {
+				setimes = 0;
+				if (utime(np, &ut) < 0)
+				    run_err("%s: set times: %s",
+					np, strerror(errno));
+			}
+			if (mod_flag)
+				(void)chmod(np, mode);
+			continue;
+		}
+		omode = mode;
+		mode |= S_IWRITE;
+		if ((ofd = open(np, O_WRONLY|O_CREAT|O_TRUNC, mode)) < 0) {
+bad:			run_err("%s: %s", np, strerror(errno));
+			continue;
+		}
+		(void)write(remout, "", 1);
+		if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) {
+			(void)close(ofd);
+			continue;
+		}
+		cp = bp->buf;
+		wrerr = NO;
+
+		if (showprogress) {
+			totalbytes = size;
+			progressmeter(-1);
+		}
+		statbytes = 0;
+		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 <= 0) {
+					run_err("%s", j ? strerror(errno) :
+					    "dropped connection");
+					exit(1);
+				}
+				amt -= j;
+				cp += j;
+			statbytes += j;
+			} while (amt > 0);
+			if (count == bp->cnt) {
+				/* Keep reading so we stay sync'd up. */
+				if (wrerr == NO) {
+					j = write(ofd, bp->buf, count);
+					if (j != count) {
+						wrerr = YES;
+						wrerrno = j >= 0 ? EIO : errno; 
+					}
+				}
+				count = 0;
+				cp = bp->buf;
+			}
+		}
+		if (showprogress)
+			progressmeter(1);
+		if (count != 0 && wrerr == NO &&
+		    (j = write(ofd, bp->buf, count)) != count) {
+			wrerr = YES;
+			wrerrno = j >= 0 ? EIO : errno; 
+		}
+#if 0
+		if (ftruncate(ofd, size)) {
+			run_err("%s: truncate: %s", np, strerror(errno));
+			wrerr = DISPLAYED;
+		}
+#endif
+		if (pflag) {
+			if (exists || omode != mode)
+				if (fchmod(ofd, omode))
+					run_err("%s: set mode: %s",
+					    np, strerror(errno));
+		} else {
+			if (!exists && omode != mode)
+				if (fchmod(ofd, omode & ~mask))
+					run_err("%s: set mode: %s",
+					    np, strerror(errno));
+		}
+		(void)close(ofd);
+		(void)response();
+		if (setimes && wrerr == NO) {
+			setimes = 0;
+			if (utime(np, &ut) < 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)write(remout, "", 1);
+			break;
+		case DISPLAYED:
+			break;
+		}
+	}
+screwup:
+	run_err("protocol error: %s", why);
+	exit(1);
+}
+
+int
+response()
+{
+	char ch, *cp, resp, rbuf[2048];
+
+	if (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 (read(remin, &ch, sizeof(ch)) != sizeof(ch))
+				lostconn(0);
+			*cp++ = ch;
+		} while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n');
+
+		if (!iamremote)
+			(void)write(STDERR_FILENO, rbuf, cp - rbuf);
+		++errs;
+		if (resp == 1)
+			return (-1);
+		exit(1);
+	}
+	/* NOTREACHED */
+}
+
+void
+usage()
+{
+	(void)fprintf(stderr,
+	    "usage: scp [-pqrvC] [-P port] [-c cipher] [-i identity] f1 f2; or:\n       scp [options] f1 ... fn directory\n");
+	exit(1);
+}
+
+void
+run_err(const char *fmt, ...)
+{
+	static FILE *fp;
+	va_list ap;
+	va_start(ap, fmt);
+
+	++errs;
+	if (fp == NULL && !(fp = fdopen(remout, "w")))
+		return;
+	(void)fprintf(fp, "%c", 0x01);
+	(void)fprintf(fp, "scp: ");
+	(void)vfprintf(fp, fmt, ap);
+	(void)fprintf(fp, "\n");
+	(void)fflush(fp);
+
+	if (!iamremote)
+	  {
+	    vfprintf(stderr, fmt, ap);
+	    fprintf(stderr, "\n");
+	  }
+
+	va_end(ap);
+}
+
+/* Stuff below is from BSD rcp util.c. */
+
+/*-
+ * Copyright (c) 1992, 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. 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.
+ *
+ *	$Id: scp.c,v 1.1 1999/10/27 03:42:45 damien Exp $
+ */
+
+char *
+colon(cp)
+	char *cp;
+{
+	if (*cp == ':')		/* Leading colon is part of file name. */
+		return (0);
+
+	for (; *cp; ++cp) {
+		if (*cp == ':')
+			return (cp);
+		if (*cp == '/')
+			return (0);
+	}
+	return (0);
+}
+
+void
+verifydir(cp)
+	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(cp0)
+	char *cp0;
+{
+	int c;
+	char *cp;
+
+	cp = cp0;
+	do {
+		c = *cp;
+		if (c & 0200)
+			goto bad;
+		if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
+			goto bad;
+	} while (*++cp);
+	return (1);
+
+bad:	fprintf(stderr, "%s: invalid user name", cp0);
+	return (0);
+}
+
+BUF *
+allocbuf(bp, fd, blksize)
+	BUF *bp;
+	int fd, blksize;
+{
+	size_t size;
+	struct stat stb;
+
+	if (fstat(fd, &stb) < 0) {
+		run_err("fstat: %s", strerror(errno));
+		return (0);
+	}
+        if (stb.st_blksize == 0)
+	  size = blksize;
+        else
+  	  size = blksize + (stb.st_blksize - blksize % stb.st_blksize) %
+	    stb.st_blksize;
+	if (bp->cnt >= size)
+		return (bp);
+  	if (bp->buf == NULL)
+	  bp->buf = xmalloc(size);
+  	else
+	  bp->buf = xrealloc(bp->buf, size);
+	bp->cnt = size;
+	return (bp);
+}
+
+void
+lostconn(signo)
+	int signo;
+{
+	if (!iamremote)
+		fprintf(stderr, "lost connection\n");
+	exit(1);
+}
+
+/*
+ * ensure all of data on socket comes through. f==read || f==write
+ */
+int
+atomicio(f, fd, s, n)
+int (*f)();
+char *s;
+{
+	int res, pos = 0;
+
+	while (n>pos) {
+		res = (f)(fd, s+pos, n-pos);
+		switch (res) {
+		case -1:
+			if (errno==EINTR || errno==EAGAIN)
+				continue;
+		case 0:
+			return (res);
+		default:
+			pos += res;
+		}
+	}
+	return (pos);
+}
+
+void
+alarmtimer(int wait)
+{
+   struct itimerval itv;
+
+   itv.it_value.tv_sec = wait;
+   itv.it_value.tv_usec = 0;
+   itv.it_interval = itv.it_value;
+   setitimer(ITIMER_REAL, &itv, NULL);
+}
+
+void
+updateprogressmeter(void)
+{
+	int save_errno = errno;
+
+	progressmeter(0);
+	errno = save_errno;
+}
+
+void
+progressmeter(int flag)
+{
+	static const char prefixes[] = " KMGTP";
+	static struct timeval lastupdate;
+	static off_t lastsize;
+	struct timeval now, td, wait;
+	off_t cursize, abbrevsize;
+	double elapsed;
+	int ratio, barlength, i, remaining;
+	char buf[256];
+
+	if (flag == -1) {
+		(void)gettimeofday(&start, (struct timezone *)0);
+		lastupdate = start;
+		lastsize = 0;
+	}   
+	(void)gettimeofday(&now, (struct timezone *)0);
+	cursize = statbytes;
+	if (totalbytes != 0) {
+		ratio = cursize * 100 / totalbytes;
+		ratio = MAX(ratio, 0);
+		ratio = MIN(ratio, 100);
+	}
+	else
+		ratio = 100;
+
+	snprintf(buf, sizeof(buf), "\r%-20.20s %3d%% ", curfile, ratio); 
+
+	barlength = getttywidth() - 51;
+	if (barlength > 0) {
+		i = barlength * ratio / 100;
+		snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+		"|%.*s%*s|", i,
+"*****************************************************************************"
+"*****************************************************************************",
+                 barlength - i, "");
+	}
+
+	i = 0;
+	abbrevsize = cursize;
+	while (abbrevsize >= 100000 && i < sizeof(prefixes)) {
+		i++;
+		abbrevsize >>= 10;
+	}
+	snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5qd %c%c ",
+	    (quad_t)abbrevsize, prefixes[i], prefixes[i] == ' ' ? ' ' :
+	    'B');
+
+	timersub(&now, &lastupdate, &wait);
+	if (cursize > lastsize) {
+		lastupdate = now;
+		lastsize = cursize;
+		if (wait.tv_sec >= STALLTIME) {
+			start.tv_sec += wait.tv_sec;
+			start.tv_usec += wait.tv_usec;
+		}
+		wait.tv_sec = 0;
+	}
+
+	timersub(&now, &start, &td);
+	elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
+
+	if (statbytes <= 0 || elapsed <= 0.0 || cursize > totalbytes) {
+		snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+		    "   --:-- ETA");
+	} else if (wait.tv_sec >= STALLTIME) {
+		snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+		    " - stalled -");
+	} else {
+		remaining = (int)(totalbytes / (statbytes / elapsed) - elapsed);
+		i = elapsed / 3600;
+		if (i)
+			snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+			    "%2d:", i);
+		else
+			snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+			    "   ");
+		i = remaining % 3600;
+		snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+		    "%02d:%02d ETA", i / 60, i % 60);
+	}
+	atomicio(write, fileno(stdout), buf, strlen(buf));
+
+	if (flag == -1) {
+		signal(SIGALRM, (void *)updateprogressmeter);
+		alarmtimer(1);
+	} else if (flag == 1) {
+		alarmtimer(0);
+		write(fileno(stdout), "\n", 1);
+		statbytes = 0;
+	}
+}
+
+int
+getttywidth(void)
+{
+	struct winsize winsize;
+
+	if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1)
+		return(winsize.ws_col ? winsize.ws_col : 80);
+	else
+		return(80);
+}
+
+
diff --git a/servconf.c b/servconf.c
new file mode 100644
index 0000000..5fa4879
--- /dev/null
+++ b/servconf.c
@@ -0,0 +1,567 @@
+/*
+
+servconf.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Mon Aug 21 15:48:58 1995 ylo
+
+*/
+
+#include "includes.h"
+RCSID("$Id: servconf.c,v 1.1 1999/10/27 03:42:45 damien Exp $");
+
+#include "ssh.h"
+#include "servconf.h"
+#include "xmalloc.h"
+
+/* Initializes the server options to their default values. */
+
+void initialize_server_options(ServerOptions *options)
+{
+  memset(options, 0, sizeof(*options));
+  options->port = -1;
+  options->listen_addr.s_addr = htonl(INADDR_ANY);
+  options->host_key_file = NULL;
+  options->server_key_bits = -1;
+  options->login_grace_time = -1;
+  options->key_regeneration_time = -1;
+  options->permit_root_login = -1;
+  options->ignore_rhosts = -1;
+  options->quiet_mode = -1;
+  options->fascist_logging = -1;
+  options->print_motd = -1;
+  options->check_mail = -1;
+  options->x11_forwarding = -1;
+  options->x11_display_offset = -1;
+  options->strict_modes = -1;
+  options->keepalives = -1;
+  options->log_facility = (SyslogFacility)-1;
+  options->rhosts_authentication = -1;
+  options->rhosts_rsa_authentication = -1;
+  options->rsa_authentication = -1;
+#ifdef KRB4
+  options->kerberos_authentication = -1;
+  options->kerberos_or_local_passwd = -1;
+  options->kerberos_ticket_cleanup = -1;
+#endif
+#ifdef AFS
+  options->kerberos_tgt_passing = -1;
+  options->afs_token_passing = -1;
+#endif
+  options->password_authentication = -1;
+#ifdef SKEY
+  options->skey_authentication = -1;
+#endif
+  options->permit_empty_passwd = -1;
+  options->use_login = -1;
+  options->num_allow_users = 0;
+  options->num_deny_users = 0;
+  options->num_allow_groups = 0;
+  options->num_deny_groups = 0;
+}
+
+void fill_default_server_options(ServerOptions *options)
+{
+  if (options->port == -1)
+    {
+      struct servent *sp;
+
+      sp = getservbyname(SSH_SERVICE_NAME, "tcp");
+      if (sp)
+	options->port = ntohs(sp->s_port);
+      else
+	options->port = SSH_DEFAULT_PORT;
+      endservent();
+    }
+  if (options->host_key_file == NULL)
+    options->host_key_file = HOST_KEY_FILE;
+  if (options->server_key_bits == -1)
+    options->server_key_bits = 768;
+  if (options->login_grace_time == -1)
+    options->login_grace_time = 600;
+  if (options->key_regeneration_time == -1)
+    options->key_regeneration_time = 3600;
+  if (options->permit_root_login == -1)
+    options->permit_root_login = 1;		 /* yes */
+  if (options->ignore_rhosts == -1)
+    options->ignore_rhosts = 0;
+  if (options->quiet_mode == -1)
+    options->quiet_mode = 0;
+  if (options->check_mail == -1)
+    options->check_mail = 0;
+  if (options->fascist_logging == -1)
+    options->fascist_logging = 1;
+  if (options->print_motd == -1)
+    options->print_motd = 1;
+  if (options->x11_forwarding == -1)
+    options->x11_forwarding = 1;
+  if (options->x11_display_offset == -1)
+    options->x11_display_offset = 1;
+  if (options->strict_modes == -1)
+    options->strict_modes = 1;
+  if (options->keepalives == -1)
+    options->keepalives = 1;
+  if (options->log_facility == (SyslogFacility)(-1))
+    options->log_facility = SYSLOG_FACILITY_AUTH;
+  if (options->rhosts_authentication == -1)
+    options->rhosts_authentication = 0;
+  if (options->rhosts_rsa_authentication == -1)
+    options->rhosts_rsa_authentication = 1;
+  if (options->rsa_authentication == -1)
+    options->rsa_authentication = 1;
+#ifdef KRB4
+  if (options->kerberos_authentication == -1)
+    options->kerberos_authentication = (access(KEYFILE, R_OK) == 0);
+  if (options->kerberos_or_local_passwd == -1)
+    options->kerberos_or_local_passwd = 1;
+  if (options->kerberos_ticket_cleanup == -1)
+    options->kerberos_ticket_cleanup = 1;
+#endif /* KRB4 */
+#ifdef AFS
+  if (options->kerberos_tgt_passing == -1)
+    options->kerberos_tgt_passing = 0;
+  if (options->afs_token_passing == -1)
+    options->afs_token_passing = k_hasafs();
+#endif /* AFS */
+  if (options->password_authentication == -1)
+    options->password_authentication = 1;
+#ifdef SKEY
+  if (options->skey_authentication == -1)
+    options->skey_authentication = 1;
+#endif
+  if (options->permit_empty_passwd == -1)
+    options->permit_empty_passwd = 1;
+  if (options->use_login == -1)
+    options->use_login = 0;
+}
+
+#define WHITESPACE " \t\r\n"
+
+/* Keyword tokens. */
+typedef enum 
+{
+  sPort, sHostKeyFile, sServerKeyBits, sLoginGraceTime, sKeyRegenerationTime,
+  sPermitRootLogin, sQuietMode, sFascistLogging, sLogFacility,
+  sRhostsAuthentication, sRhostsRSAAuthentication, sRSAAuthentication,
+#ifdef KRB4
+  sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup,
+#endif
+#ifdef AFS
+  sKerberosTgtPassing, sAFSTokenPassing,
+#endif
+#ifdef SKEY
+  sSkeyAuthentication,
+#endif
+  sPasswordAuthentication, sListenAddress,
+  sPrintMotd, sIgnoreRhosts, sX11Forwarding, sX11DisplayOffset,
+  sStrictModes, sEmptyPasswd, sRandomSeedFile, sKeepAlives, sCheckMail,
+  sUseLogin, sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups
+
+} ServerOpCodes;
+
+/* Textual representation of the tokens. */
+static struct
+{
+  const char *name;
+  ServerOpCodes opcode;
+} keywords[] =
+{
+  { "port", sPort },
+  { "hostkey", sHostKeyFile },
+  { "serverkeybits", sServerKeyBits },
+  { "logingracetime", sLoginGraceTime },
+  { "keyregenerationinterval", sKeyRegenerationTime },
+  { "permitrootlogin", sPermitRootLogin },
+  { "quietmode", sQuietMode },
+  { "fascistlogging", sFascistLogging },
+  { "syslogfacility", sLogFacility },
+  { "rhostsauthentication", sRhostsAuthentication },
+  { "rhostsrsaauthentication", sRhostsRSAAuthentication },
+  { "rsaauthentication", sRSAAuthentication },
+#ifdef KRB4
+  { "kerberosauthentication", sKerberosAuthentication },
+  { "kerberosorlocalpasswd", sKerberosOrLocalPasswd },
+  { "kerberosticketcleanup", sKerberosTicketCleanup },
+#endif
+#ifdef AFS
+  { "kerberostgtpassing", sKerberosTgtPassing },
+  { "afstokenpassing", sAFSTokenPassing },
+#endif
+  { "passwordauthentication", sPasswordAuthentication },
+#ifdef SKEY
+  { "skeyauthentication", sSkeyAuthentication },
+#endif
+  { "checkmail", sCheckMail },
+  { "listenaddress", sListenAddress },
+  { "printmotd", sPrintMotd },
+  { "ignorerhosts", sIgnoreRhosts },
+  { "x11forwarding", sX11Forwarding },
+  { "x11displayoffset", sX11DisplayOffset },
+  { "strictmodes", sStrictModes },
+  { "permitemptypasswords", sEmptyPasswd },
+  { "uselogin", sUseLogin },
+  { "randomseed", sRandomSeedFile },
+  { "keepalive", sKeepAlives },
+  { "allowusers", sAllowUsers },
+  { "denyusers", sDenyUsers },
+  { "allowgroups", sAllowGroups },
+  { "denygroups", sDenyGroups },
+  { NULL, 0 }
+};
+
+static struct 
+{
+  const char *name;
+  SyslogFacility facility;
+} log_facilities[] =
+{
+  { "DAEMON", SYSLOG_FACILITY_DAEMON },
+  { "USER", SYSLOG_FACILITY_USER },
+  { "AUTH", SYSLOG_FACILITY_AUTH },
+  { "LOCAL0", SYSLOG_FACILITY_LOCAL0 },
+  { "LOCAL1", SYSLOG_FACILITY_LOCAL1 },
+  { "LOCAL2", SYSLOG_FACILITY_LOCAL2 },
+  { "LOCAL3", SYSLOG_FACILITY_LOCAL3 },
+  { "LOCAL4", SYSLOG_FACILITY_LOCAL4 },
+  { "LOCAL5", SYSLOG_FACILITY_LOCAL5 },
+  { "LOCAL6", SYSLOG_FACILITY_LOCAL6 },
+  { "LOCAL7", SYSLOG_FACILITY_LOCAL7 },
+  { NULL, 0 }
+};
+
+/* Returns the number of the token pointed to by cp of length len.
+   Never returns if the token is not known. */
+
+static ServerOpCodes parse_token(const char *cp, const char *filename,
+				 int linenum)
+{
+  unsigned int i;
+
+  for (i = 0; keywords[i].name; i++)
+    if (strcmp(cp, keywords[i].name) == 0)
+      return keywords[i].opcode;
+
+  fprintf(stderr, "%s line %d: Bad configuration option: %s\n", 
+	  filename, linenum, cp);
+  exit(1);
+}
+
+/* Reads the server configuration file. */
+
+void read_server_config(ServerOptions *options, const char *filename)
+{
+  FILE *f;
+  char line[1024];
+  char *cp, **charptr;
+  int linenum, *intptr, i, value;
+  ServerOpCodes opcode;
+
+  f = fopen(filename, "r");
+  if (!f)
+    {
+      perror(filename);
+      exit(1);
+    }
+
+  linenum = 0;
+  while (fgets(line, sizeof(line), f))
+    {
+      linenum++;
+      cp = line + strspn(line, WHITESPACE);
+      if (!*cp || *cp == '#')
+	continue;
+      cp = strtok(cp, WHITESPACE);
+      {
+	char *t = cp;
+	for (; *t != 0; t++)
+	  if ('A' <= *t && *t <= 'Z')
+	    *t = *t - 'A' + 'a';	/* tolower */
+      
+      }
+      opcode = parse_token(cp, filename, linenum);
+      switch (opcode)
+	{
+	case sPort:
+	  intptr = &options->port;
+	parse_int:
+	  cp = strtok(NULL, WHITESPACE);
+	  if (!cp)
+	    {
+	      fprintf(stderr, "%s line %d: missing integer value.\n", 
+		      filename, linenum);
+	      exit(1);
+	    }
+	  value = atoi(cp);
+	  if (*intptr == -1)
+	    *intptr = value;
+	  break;
+
+	case sServerKeyBits:
+	  intptr = &options->server_key_bits;
+	  goto parse_int;
+
+	case sLoginGraceTime:
+	  intptr = &options->login_grace_time;
+	  goto parse_int;
+	  
+	case sKeyRegenerationTime:
+	  intptr = &options->key_regeneration_time;
+	  goto parse_int;
+
+	case sListenAddress:
+	  cp = strtok(NULL, WHITESPACE);
+	  if (!cp)
+	    {
+	      fprintf(stderr, "%s line %d: missing inet addr.\n",
+		      filename, linenum);
+	      exit(1);
+	    }
+	  options->listen_addr.s_addr = inet_addr(cp);
+	  break;
+
+	case sHostKeyFile:
+	  charptr = &options->host_key_file;
+	  cp = strtok(NULL, WHITESPACE);
+	  if (!cp)
+	    {
+	      fprintf(stderr, "%s line %d: missing file name.\n",
+		      filename, linenum);
+	      exit(1);
+	    }
+	  if (*charptr == NULL)
+	    *charptr = tilde_expand_filename(cp, getuid());
+	  break;
+
+	case sRandomSeedFile:
+	  fprintf(stderr, "%s line %d: \"randomseed\" option is obsolete.\n",
+		  filename, linenum);
+	  cp = strtok(NULL, WHITESPACE);
+	  break;
+
+	case sPermitRootLogin:
+	  intptr = &options->permit_root_login;
+	  cp = strtok(NULL, WHITESPACE);
+	  if (!cp)
+	    {
+	      fprintf(stderr, "%s line %d: missing yes/without-password/no argument.\n",
+		      filename, linenum);
+	      exit(1);
+	    }
+	  if (strcmp(cp, "without-password") == 0)
+	    value = 2;
+	  else if (strcmp(cp, "yes") == 0)
+	    value = 1;
+	  else if (strcmp(cp, "no") == 0)
+	    value = 0;
+	  else
+	    {
+	      fprintf(stderr, "%s line %d: Bad yes/without-password/no argument: %s\n", 
+	      	filename, linenum, cp);
+	      exit(1);
+	    }
+	  if (*intptr == -1)
+	    *intptr = value;
+	  break;
+
+	case sIgnoreRhosts:
+	  intptr = &options->ignore_rhosts;
+	parse_flag:
+	  cp = strtok(NULL, WHITESPACE);
+	  if (!cp)
+	    {
+	      fprintf(stderr, "%s line %d: missing yes/no argument.\n",
+		      filename, linenum);
+	      exit(1);
+	    }
+	  if (strcmp(cp, "yes") == 0)
+	    value = 1;
+	  else
+	    if (strcmp(cp, "no") == 0)
+	      value = 0;
+	    else
+	      {
+		fprintf(stderr, "%s line %d: Bad yes/no argument: %s\n", 
+			filename, linenum, cp);
+		exit(1);
+	      }
+	  if (*intptr == -1)
+	    *intptr = value;
+	  break;
+	  
+	case sQuietMode:
+	  intptr = &options->quiet_mode;
+	  goto parse_flag;
+
+	case sFascistLogging:
+	  intptr = &options->fascist_logging;
+	  goto parse_flag;
+
+	case sRhostsAuthentication:
+	  intptr = &options->rhosts_authentication;
+	  goto parse_flag;
+
+	case sRhostsRSAAuthentication:
+	  intptr = &options->rhosts_rsa_authentication;
+	  goto parse_flag;
+	  
+	case sRSAAuthentication:
+	  intptr = &options->rsa_authentication;
+	  goto parse_flag;
+	  
+#ifdef KRB4
+	case sKerberosAuthentication:
+	  intptr = &options->kerberos_authentication;
+	  goto parse_flag;
+	  
+ 	case sKerberosOrLocalPasswd:
+ 	  intptr = &options->kerberos_or_local_passwd;
+ 	  goto parse_flag;
+
+	case sKerberosTicketCleanup:
+	  intptr = &options->kerberos_ticket_cleanup;
+	  goto parse_flag;
+#endif
+	  
+#ifdef AFS
+	case sKerberosTgtPassing:
+	  intptr = &options->kerberos_tgt_passing;
+	  goto parse_flag;
+
+	case sAFSTokenPassing:
+	  intptr = &options->afs_token_passing;
+	  goto parse_flag;
+#endif
+
+	case sPasswordAuthentication:
+	  intptr = &options->password_authentication;
+	  goto parse_flag;
+
+        case sCheckMail:
+          intptr = &options->check_mail;
+          goto parse_flag;
+
+#ifdef SKEY
+	case sSkeyAuthentication:
+	  intptr = &options->skey_authentication;
+	  goto parse_flag;
+#endif
+
+	case sPrintMotd:
+	  intptr = &options->print_motd;
+	  goto parse_flag;
+
+	case sX11Forwarding:
+	  intptr = &options->x11_forwarding;
+	  goto parse_flag;
+
+	case sX11DisplayOffset:
+	  intptr = &options->x11_display_offset;
+	  goto parse_int;
+
+	case sStrictModes:
+	  intptr = &options->strict_modes;
+	  goto parse_flag;
+
+	case sKeepAlives:
+	  intptr = &options->keepalives;
+	  goto parse_flag;
+	  
+	case sEmptyPasswd:
+	  intptr = &options->permit_empty_passwd;
+	  goto parse_flag;
+
+        case sUseLogin:
+          intptr = &options->use_login;
+          goto parse_flag;
+
+	case sLogFacility:
+	  cp = strtok(NULL, WHITESPACE);
+	  if (!cp)
+	    {
+	      fprintf(stderr, "%s line %d: missing facility name.\n",
+		      filename, linenum);
+	      exit(1);
+	    }
+	  for (i = 0; log_facilities[i].name; i++)
+	    if (strcmp(log_facilities[i].name, cp) == 0)
+	      break;
+	  if (!log_facilities[i].name)
+	    {
+	      fprintf(stderr, "%s line %d: unsupported log facility %s\n",
+		      filename, linenum, cp);
+	      exit(1);
+	    }
+	  if (options->log_facility == (SyslogFacility)(-1))
+	    options->log_facility = log_facilities[i].facility;
+	  break;
+	  
+	case sAllowUsers:
+	  while ((cp = strtok(NULL, WHITESPACE)))
+	    {
+	      if (options->num_allow_users >= MAX_ALLOW_USERS)
+		{
+		  fprintf(stderr, "%s line %d: too many allow users.\n",
+			  filename, linenum);
+		  exit(1);
+		}
+	      options->allow_users[options->num_allow_users++] = xstrdup(cp);
+	    }
+	  break;
+
+	case sDenyUsers:
+	  while ((cp = strtok(NULL, WHITESPACE)))
+	    {
+	      if (options->num_deny_users >= MAX_DENY_USERS)
+		{
+		  fprintf(stderr, "%s line %d: too many deny users.\n",
+			  filename, linenum);
+		  exit(1);
+		}
+	      options->deny_users[options->num_deny_users++] = xstrdup(cp);
+	    }
+	  break;
+
+	case sAllowGroups:
+	  while ((cp = strtok(NULL, WHITESPACE)))
+	    {
+	      if (options->num_allow_groups >= MAX_ALLOW_GROUPS)
+		{
+		  fprintf(stderr, "%s line %d: too many allow groups.\n",
+			  filename, linenum);
+		  exit(1);
+		}
+	      options->allow_groups[options->num_allow_groups++] = xstrdup(cp);
+	    }
+	  break;
+
+	case sDenyGroups:
+	  while ((cp = strtok(NULL, WHITESPACE)))
+	    {
+	      if (options->num_deny_groups >= MAX_DENY_GROUPS)
+		{
+		  fprintf(stderr, "%s line %d: too many deny groups.\n",
+			  filename, linenum);
+		  exit(1);
+		}
+	      options->deny_groups[options->num_deny_groups++] = xstrdup(cp);
+	    }
+	  break;
+
+	default:
+	  fprintf(stderr, "%s line %d: Missing handler for opcode %s (%d)\n",
+		  filename, linenum, cp, opcode);
+	  exit(1);
+	}
+      if (strtok(NULL, WHITESPACE) != NULL)
+	{
+	  fprintf(stderr, "%s line %d: garbage at end of line.\n",
+		  filename, linenum);
+	  exit(1);
+	}
+    }
+  fclose(f);
+}
diff --git a/servconf.h b/servconf.h
new file mode 100644
index 0000000..22c73fd
--- /dev/null
+++ b/servconf.h
@@ -0,0 +1,86 @@
+/*
+
+servconf.h
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Mon Aug 21 15:35:03 1995 ylo
+
+Definitions for server configuration data and for the functions reading it.
+
+*/
+
+/* RCSID("$Id: servconf.h,v 1.1 1999/10/27 03:42:45 damien Exp $"); */
+
+#ifndef SERVCONF_H
+#define SERVCONF_H
+
+#define MAX_ALLOW_USERS		256 /* Max # users on allow list. */
+#define MAX_DENY_USERS		256 /* Max # users on deny list. */
+#define MAX_ALLOW_GROUPS	256 /* Max # groups on allow list. */
+#define MAX_DENY_GROUPS		256 /* Max # groups on deny list. */
+
+typedef struct
+{
+  int port;			/* Port number to listen on. */
+  struct in_addr listen_addr;	/* Address on which the server listens. */
+  char *host_key_file;		/* File containing host key. */
+  int server_key_bits;		/* Size of the server key. */
+  int login_grace_time;		/* Disconnect if no auth in this time (sec). */
+  int key_regeneration_time;	/* Server key lifetime (seconds). */
+  int permit_root_login;	/* If true, permit root login. */
+  int ignore_rhosts;		/* Ignore .rhosts and .shosts. */
+  int quiet_mode;		/* If true, don't log anything but fatals. */
+  int fascist_logging;		/* Perform very verbose logging. */
+  int print_motd;		/* If true, print /etc/motd. */
+  int check_mail;               /* If true, check for new mail. */
+  int x11_forwarding;		/* If true, permit inet (spoofing) X11 fwd. */
+  int x11_display_offset;	/* What DISPLAY number to start searching at */
+  int strict_modes;		/* If true, require string home dir modes. */
+  int keepalives;		/* If true, set SO_KEEPALIVE. */
+  SyslogFacility log_facility;	/* Facility for system logging. */
+  int rhosts_authentication;	/* If true, permit rhosts authentication. */
+  int rhosts_rsa_authentication;/* If true, permit rhosts RSA authentication.*/
+  int rsa_authentication;	/* If true, permit RSA authentication. */
+#ifdef KRB4
+  int kerberos_authentication;	/* If true, permit Kerberos authentication. */
+  int kerberos_or_local_passwd;	/* If true, permit kerberos and any other
+				   password authentication mechanism, such
+				   as SecurID or /etc/passwd */
+  int kerberos_ticket_cleanup;	/* If true, destroy ticket file on logout. */
+#endif
+#ifdef AFS
+  int kerberos_tgt_passing;	/* If true, permit Kerberos tgt passing. */
+  int afs_token_passing;	/* If true, permit AFS token passing. */
+#endif
+  int password_authentication;  /* If true, permit password authentication. */
+#ifdef SKEY
+  int skey_authentication;      /* If true, permit s/key authentication. */
+#endif
+  int permit_empty_passwd;      /* If false, do not permit empty passwords. */
+  int use_login;                /* If true, login(1) is used */
+  unsigned int num_allow_users;
+  char *allow_users[MAX_ALLOW_USERS];
+  unsigned int num_deny_users;
+  char *deny_users[MAX_DENY_USERS];
+  unsigned int num_allow_groups;
+  char *allow_groups[MAX_ALLOW_GROUPS];
+  unsigned int num_deny_groups;
+  char *deny_groups[MAX_DENY_GROUPS];
+} ServerOptions;
+
+/* Initializes the server options to special values that indicate that they
+   have not yet been set. */
+void initialize_server_options(ServerOptions *options);
+
+/* Reads the server configuration file.  This only sets the values for those
+   options that have the special value indicating they have not been set. */
+void read_server_config(ServerOptions *options, const char *filename);
+
+/* Sets values for those values that have not yet been set. */
+void fill_default_server_options(ServerOptions *options);
+
+#endif /* SERVCONF_H */
diff --git a/serverloop.c b/serverloop.c
new file mode 100644
index 0000000..552c69c
--- /dev/null
+++ b/serverloop.c
@@ -0,0 +1,644 @@
+/*
+
+serverloop.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Sun Sep 10 00:30:37 1995 ylo
+
+Server main loop for handling the interactive session.
+
+*/
+
+#include "includes.h"
+#include "xmalloc.h"
+#include "ssh.h"
+#include "packet.h"
+#include "buffer.h"
+#include "servconf.h"
+#include "pty.h"
+
+static Buffer stdin_buffer;	/* Buffer for stdin data. */
+static Buffer stdout_buffer;	/* Buffer for stdout data. */
+static Buffer stderr_buffer;	/* Buffer for stderr data. */
+static int fdin;		/* Descriptor for stdin (for writing) */
+static int fdout;		/* Descriptor for stdout (for reading);
+				   May be same number as fdin. */
+static int fderr;		/* Descriptor for stderr.  May be -1. */
+static long stdin_bytes = 0;	/* Number of bytes written to stdin. */
+static long stdout_bytes = 0;	/* Number of stdout bytes sent to client. */
+static long stderr_bytes = 0;	/* Number of stderr bytes sent to client. */
+static long fdout_bytes = 0;	/* Number of stdout bytes read from program. */
+static int stdin_eof = 0;	/* EOF message received from client. */
+static int fdout_eof = 0;	/* EOF encountered reading from fdout. */
+static int fderr_eof = 0;	/* EOF encountered readung from fderr. */
+static int connection_in;	/* Connection to client (input). */
+static int connection_out;	/* Connection to client (output). */
+static unsigned int buffer_high;/* "Soft" max buffer size. */
+static int max_fd;		/* Max file descriptor number for select(). */
+
+/* This SIGCHLD kludge is used to detect when the child exits.  The server
+   will exit after that, as soon as forwarded connections have terminated. */
+
+static int child_pid;  			/* Pid of the child. */
+static volatile int child_terminated;	/* The child has terminated. */
+static volatile int child_wait_status;	/* Status from wait(). */
+
+void sigchld_handler(int sig)
+{
+  int save_errno = errno;
+  int wait_pid;
+  debug("Received SIGCHLD.");
+  wait_pid = wait((int *)&child_wait_status);
+  if (wait_pid != -1)
+    {
+      if (wait_pid != child_pid)
+	error("Strange, got SIGCHLD and wait returned pid %d but child is %d",
+	      wait_pid, child_pid);
+      if (WIFEXITED(child_wait_status) ||
+	  WIFSIGNALED(child_wait_status))
+	child_terminated = 1;
+    }
+  signal(SIGCHLD, sigchld_handler);
+  errno = save_errno;
+}
+
+/* Process any buffered packets that have been received from the client. */
+
+void process_buffered_input_packets()
+{
+  int type;
+  char *data;
+  unsigned int data_len;
+  int row, col, xpixel, ypixel;
+  int payload_len;
+
+  /* Process buffered packets from the client. */
+  while ((type = packet_read_poll(&payload_len)) != SSH_MSG_NONE)
+    {
+      switch (type)
+	{
+	case SSH_CMSG_STDIN_DATA:
+	  /* Stdin data from the client.  Append it to the buffer. */
+	  if (fdin == -1)
+	    break; /* Ignore any data if the client has closed stdin. */
+	  data = packet_get_string(&data_len);
+	  packet_integrity_check(payload_len, (4 + data_len), type);
+	  buffer_append(&stdin_buffer, data, data_len);
+	  memset(data, 0, data_len);
+	  xfree(data);
+	  break;
+	  
+	case SSH_CMSG_EOF:
+	  /* Eof from the client.  The stdin descriptor to the program
+	     will be closed when all buffered data has drained. */
+	  debug("EOF received for stdin.");
+	  packet_integrity_check(payload_len, 0, type);
+	  stdin_eof = 1;
+	  break;
+
+	case SSH_CMSG_WINDOW_SIZE:
+	  debug("Window change received.");
+	  packet_integrity_check(payload_len, 4*4, type);
+	  row = packet_get_int();
+	  col = packet_get_int();
+	  xpixel = packet_get_int();
+	  ypixel = packet_get_int();
+	  if (fdin != -1)
+	    pty_change_window_size(fdin, row, col, xpixel, ypixel);
+	  break;
+	  
+	case SSH_MSG_PORT_OPEN:
+	  debug("Received port open request.");
+	  channel_input_port_open(payload_len);
+	  break;
+	  
+	case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
+	  debug("Received channel open confirmation.");
+	  packet_integrity_check(payload_len, 4 + 4, type);
+	  channel_input_open_confirmation();
+	  break;
+
+	case SSH_MSG_CHANNEL_OPEN_FAILURE:
+	  debug("Received channel open failure.");
+	  packet_integrity_check(payload_len, 4, type);
+	  channel_input_open_failure();
+	  break;
+	  
+	case SSH_MSG_CHANNEL_DATA:
+	  channel_input_data(payload_len);
+	  break;
+	  
+	case SSH_MSG_CHANNEL_CLOSE:
+	  debug("Received channel close.");
+	  packet_integrity_check(payload_len, 4, type);
+	  channel_input_close();
+	  break;
+
+	case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION:
+	  debug("Received channel close confirmation.");
+	  packet_integrity_check(payload_len, 4, type);
+	  channel_input_close_confirmation();
+	  break;
+
+	default:
+	  /* In this phase, any unexpected messages cause a protocol
+	     error.  This is to ease debugging; also, since no 
+	     confirmations are sent messages, unprocessed unknown 
+	     messages could cause strange problems.  Any compatible 
+	     protocol extensions must be negotiated before entering the 
+	     interactive session. */
+	  packet_disconnect("Protocol error during session: type %d", 
+			    type);
+	}
+    }
+}
+
+/* Make packets from buffered stderr data, and buffer it for sending
+   to the client. */
+
+void make_packets_from_stderr_data()
+{
+  int len;
+
+  /* Send buffered stderr data to the client. */
+  while (buffer_len(&stderr_buffer) > 0 && 
+	 packet_not_very_much_data_to_write())
+    {
+      len = buffer_len(&stderr_buffer);
+      if (packet_is_interactive())
+	{
+	  if (len > 512)
+	    len = 512;
+	}
+      else
+	{
+	  if (len > 32768)
+	    len = 32768;  /* Keep the packets at reasonable size. */
+	}
+      packet_start(SSH_SMSG_STDERR_DATA);
+      packet_put_string(buffer_ptr(&stderr_buffer), len);
+      packet_send();
+      buffer_consume(&stderr_buffer, len);
+      stderr_bytes += len;
+    }
+}
+
+/* Make packets from buffered stdout data, and buffer it for sending to the
+   client. */
+
+void make_packets_from_stdout_data()
+{
+  int len;
+
+  /* Send buffered stdout data to the client. */
+  while (buffer_len(&stdout_buffer) > 0 && 
+	 packet_not_very_much_data_to_write())
+    {
+      len = buffer_len(&stdout_buffer);
+      if (packet_is_interactive())
+	{
+	  if (len > 512)
+	    len = 512;
+	}
+      else
+	{
+	  if (len > 32768)
+	    len = 32768;  /* Keep the packets at reasonable size. */
+	}
+      packet_start(SSH_SMSG_STDOUT_DATA);
+      packet_put_string(buffer_ptr(&stdout_buffer), len);
+      packet_send();
+      buffer_consume(&stdout_buffer, len);
+      stdout_bytes += len;
+    }
+}
+
+/* Sleep in select() until we can do something.  This will initialize the
+   select masks.  Upon return, the masks will indicate which descriptors
+   have data or can accept data.  Optionally, a maximum time can be specified
+   for the duration of the wait (0 = infinite). */
+
+void wait_until_can_do_something(fd_set *readset, fd_set *writeset,
+				 unsigned int max_time_milliseconds)
+{
+  struct timeval tv, *tvp;
+  int ret;
+
+  /* When select fails we restart from here. */
+retry_select:
+
+  /* Initialize select() masks. */
+  FD_ZERO(readset);
+  
+  /* Read packets from the client unless we have too much buffered stdin
+     or channel data. */
+  if (buffer_len(&stdin_buffer) < 4096 &&
+      channel_not_very_much_buffered_data())
+    FD_SET(connection_in, readset);
+  
+  /* If there is not too much data already buffered going to the client,
+     try to get some more data from the program. */
+  if (packet_not_very_much_data_to_write())
+    {
+      if (!fdout_eof)
+	FD_SET(fdout, readset);
+      if (!fderr_eof)
+	FD_SET(fderr, readset);
+    }
+  
+  FD_ZERO(writeset);
+  
+  /* Set masks for channel descriptors. */
+  channel_prepare_select(readset, writeset);
+  
+  /* If we have buffered packet data going to the client, mark that
+     descriptor. */
+  if (packet_have_data_to_write())
+    FD_SET(connection_out, writeset);
+  
+  /* If we have buffered data, try to write some of that data to the
+     program. */
+  if (fdin != -1 && buffer_len(&stdin_buffer) > 0)
+    FD_SET(fdin, writeset);
+  
+  /* Update the maximum descriptor number if appropriate. */
+  if (channel_max_fd() > max_fd)
+    max_fd = channel_max_fd();
+  
+  /* If child has terminated, read as much as is available and then exit. */
+  if (child_terminated)
+    if (max_time_milliseconds == 0)
+      max_time_milliseconds = 100;
+  
+  if (max_time_milliseconds == 0)
+    tvp = NULL;
+  else
+    {
+      tv.tv_sec = max_time_milliseconds / 1000;
+      tv.tv_usec = 1000 * (max_time_milliseconds % 1000);
+      tvp = &tv;
+    }
+
+  /* Wait for something to happen, or the timeout to expire. */
+  ret = select(max_fd + 1, readset, writeset, NULL, tvp);
+  
+  if (ret < 0)
+    {
+      if (errno != EINTR)
+	error("select: %.100s", strerror(errno));
+      else
+	goto retry_select;
+    }
+}
+
+/* Processes input from the client and the program.  Input data is stored
+   in buffers and processed later. */
+
+void process_input(fd_set *readset)
+{
+  int len;
+  char buf[16384];
+
+  /* Read and buffer any input data from the client. */
+  if (FD_ISSET(connection_in, readset))
+    {
+      len = read(connection_in, buf, sizeof(buf));
+      if (len == 0)
+	fatal("Connection closed by remote host.");
+
+      /* There is a kernel bug on Solaris that causes select to sometimes
+	 wake up even though there is no data available. */
+      if (len < 0 && errno == EAGAIN)
+	len = 0;
+
+      if (len < 0)
+	fatal("Read error from remote host: %.100s", strerror(errno));
+
+      /* Buffer any received data. */
+      packet_process_incoming(buf, len);
+    }
+  
+  /* Read and buffer any available stdout data from the program. */
+  if (!fdout_eof && FD_ISSET(fdout, readset))
+    {
+      len = read(fdout, buf, sizeof(buf));
+      if (len <= 0)
+	fdout_eof = 1;
+      else
+	{
+	  buffer_append(&stdout_buffer, buf, len);
+	  fdout_bytes += len;
+	}
+    }
+  
+  /* Read and buffer any available stderr data from the program. */
+  if (!fderr_eof && FD_ISSET(fderr, readset))
+    {
+      len = read(fderr, buf, sizeof(buf));
+      if (len <= 0)
+	fderr_eof = 1;
+      else
+	buffer_append(&stderr_buffer, buf, len);
+    }
+}
+
+/* Sends data from internal buffers to client program stdin. */
+
+void process_output(fd_set *writeset)
+{
+  int len;
+
+  /* Write buffered data to program stdin. */
+  if (fdin != -1 && FD_ISSET(fdin, writeset))
+    {
+      len = write(fdin, buffer_ptr(&stdin_buffer),
+		  buffer_len(&stdin_buffer));
+      if (len <= 0)
+	{
+#ifdef USE_PIPES
+	  close(fdin); 
+#else
+          if (fdout == -1)
+            close(fdin);
+	  else
+	    shutdown(fdin, SHUT_WR); /* We will no longer send. */
+#endif
+	  fdin = -1;
+	}
+      else
+	{
+	  /* Successful write.  Consume the data from the buffer. */
+	  buffer_consume(&stdin_buffer, len);
+	  /* Update the count of bytes written to the program. */
+	  stdin_bytes += len;
+	}
+    }
+  
+  /* Send any buffered packet data to the client. */
+  if (FD_ISSET(connection_out, writeset))
+    packet_write_poll();
+}
+
+/* Wait until all buffered output has been sent to the client.
+   This is used when the program terminates. */
+
+void drain_output()
+{
+  /* Send any buffered stdout data to the client. */
+  if (buffer_len(&stdout_buffer) > 0)
+    {
+      packet_start(SSH_SMSG_STDOUT_DATA);
+      packet_put_string(buffer_ptr(&stdout_buffer), 
+			buffer_len(&stdout_buffer));
+      packet_send();
+      /* Update the count of sent bytes. */
+      stdout_bytes += buffer_len(&stdout_buffer);
+    }
+  
+  /* Send any buffered stderr data to the client. */
+  if (buffer_len(&stderr_buffer) > 0)
+    {
+      packet_start(SSH_SMSG_STDERR_DATA);
+      packet_put_string(buffer_ptr(&stderr_buffer), 
+			buffer_len(&stderr_buffer));
+      packet_send();
+      /* Update the count of sent bytes. */
+      stderr_bytes += buffer_len(&stderr_buffer);
+    }
+  
+  /* Wait until all buffered data has been written to the client. */
+  packet_write_wait();
+}
+
+/* Performs the interactive session.  This handles data transmission between
+   the client and the program.  Note that the notion of stdin, stdout, and
+   stderr in this function is sort of reversed: this function writes to
+   stdin (of the child program), and reads from stdout and stderr (of the
+   child program). */
+
+void server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg)
+{
+  int wait_status, wait_pid;	/* Status and pid returned by wait(). */
+  int waiting_termination = 0;  /* Have displayed waiting close message. */
+  unsigned int max_time_milliseconds;
+  unsigned int previous_stdout_buffer_bytes;
+  unsigned int stdout_buffer_bytes;
+  int type;
+
+  debug("Entering interactive session.");
+
+  /* Initialize the SIGCHLD kludge. */
+  child_pid = pid;
+  child_terminated = 0;
+  signal(SIGCHLD, sigchld_handler);
+
+  /* Initialize our global variables. */
+  fdin = fdin_arg;
+  fdout = fdout_arg;
+  fderr = fderr_arg;
+  connection_in = packet_get_connection_in();
+  connection_out = packet_get_connection_out();
+  
+  previous_stdout_buffer_bytes = 0;
+
+  /* Set approximate I/O buffer size. */
+  if (packet_is_interactive())
+    buffer_high = 4096;
+  else
+    buffer_high = 64 * 1024;
+
+  /* Initialize max_fd to the maximum of the known file descriptors. */
+  max_fd = fdin;
+  if (fdout > max_fd)
+    max_fd = fdout;
+  if (fderr != -1 && fderr > max_fd)
+    max_fd = fderr;
+  if (connection_in > max_fd)
+    max_fd = connection_in;
+  if (connection_out > max_fd)
+    max_fd = connection_out;
+
+  /* Initialize Initialize buffers. */
+  buffer_init(&stdin_buffer);
+  buffer_init(&stdout_buffer);
+  buffer_init(&stderr_buffer);
+
+  /* If we have no separate fderr (which is the case when we have a pty - there
+     we cannot make difference between data sent to stdout and stderr),
+     indicate that we have seen an EOF from stderr.  This way we don\'t
+     need to check the descriptor everywhere. */
+  if (fderr == -1)
+    fderr_eof = 1;
+
+  /* Main loop of the server for the interactive session mode. */
+  for (;;)
+    {
+      fd_set readset, writeset;
+
+      /* Process buffered packets from the client. */
+      process_buffered_input_packets();
+
+      /* If we have received eof, and there is no more pending input data,
+	 cause a real eof by closing fdin. */
+      if (stdin_eof && fdin != -1 && buffer_len(&stdin_buffer) == 0)
+	{
+#ifdef USE_PIPES
+	  close(fdin);
+#else
+          if (fdout == -1)
+            close(fdin);
+	  else
+	    shutdown(fdin, SHUT_WR); /* We will no longer send. */
+#endif
+	  fdin = -1;
+	}
+
+      /* Make packets from buffered stderr data to send to the client. */
+      make_packets_from_stderr_data();
+
+      /* Make packets from buffered stdout data to send to the client.
+	 If there is very little to send, this arranges to not send them
+	 now, but to wait a short while to see if we are getting more data.
+	 This is necessary, as some systems wake up readers from a pty after
+	 each separate character. */
+      max_time_milliseconds = 0;
+      stdout_buffer_bytes = buffer_len(&stdout_buffer);
+      if (stdout_buffer_bytes != 0 && stdout_buffer_bytes < 256 &&
+	  stdout_buffer_bytes != previous_stdout_buffer_bytes)
+	max_time_milliseconds = 10; /* try again after a while */
+      else
+	make_packets_from_stdout_data(); /* Send it now. */
+      previous_stdout_buffer_bytes = buffer_len(&stdout_buffer);
+
+      /* Send channel data to the client. */
+      if (packet_not_very_much_data_to_write())
+	channel_output_poll();
+
+      /* Bail out of the loop if the program has closed its output descriptors,
+	 and we have no more data to send to the client, and there is no
+	 pending buffered data. */
+      if (fdout_eof && fderr_eof && !packet_have_data_to_write() &&
+	  buffer_len(&stdout_buffer) == 0 && buffer_len(&stderr_buffer) == 0)
+	{
+	  if (!channel_still_open())
+	    goto quit;
+	  if (!waiting_termination)
+	    {
+	      const char *s = 
+		"Waiting for forwarded connections to terminate...\r\n";
+	      char *cp;
+	      waiting_termination = 1;
+	      buffer_append(&stderr_buffer, s, strlen(s));
+
+	      /* Display list of open channels. */
+	      cp = channel_open_message();
+	      buffer_append(&stderr_buffer, cp, strlen(cp));
+	      xfree(cp);
+	    }
+	}
+
+      /* Sleep in select() until we can do something. */
+      wait_until_can_do_something(&readset, &writeset,
+				  max_time_milliseconds);
+
+      /* Process any channel events. */
+      channel_after_select(&readset, &writeset);
+
+      /* Process input from the client and from program stdout/stderr. */
+      process_input(&readset);
+
+      /* Process output to the client and to program stdin. */
+      process_output(&writeset);
+    }
+
+ quit:
+  /* Cleanup and termination code. */
+
+  /* Wait until all output has been sent to the client. */
+  drain_output();
+
+  debug("End of interactive session; stdin %ld, stdout (read %ld, sent %ld), stderr %ld bytes.",
+	stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes);
+
+  /* Free and clear the buffers. */
+  buffer_free(&stdin_buffer);
+  buffer_free(&stdout_buffer);
+  buffer_free(&stderr_buffer);
+
+  /* Close the file descriptors. */
+  if (fdout != -1)
+    close(fdout);
+  fdout = -1;
+  fdout_eof = 1;
+  if (fderr != -1)
+    close(fderr);
+  fderr = -1;
+  fderr_eof = 1;
+  if (fdin != -1)
+    close(fdin);
+  fdin = -1;
+
+  /* Stop listening for channels; this removes unix domain sockets. */
+  channel_stop_listening();
+  
+  /* Wait for the child to exit.  Get its exit status. */
+  wait_pid = wait(&wait_status);
+  if (wait_pid < 0)
+    {
+      /* It is possible that the wait was handled by SIGCHLD handler.  This
+	 may result in either: this call returning with EINTR, or: this
+	 call returning ECHILD. */
+      if (child_terminated)
+	wait_status = child_wait_status;
+      else
+	packet_disconnect("wait: %.100s", strerror(errno));
+    }
+  else
+    {
+      /* Check if it matches the process we forked. */
+      if (wait_pid != pid)
+	error("Strange, wait returned pid %d, expected %d", wait_pid, pid);
+    }
+
+  /* We no longer want our SIGCHLD handler to be called. */
+  signal(SIGCHLD, SIG_DFL);
+
+  /* Check if it exited normally. */
+  if (WIFEXITED(wait_status))
+    {
+      /* Yes, normal exit.  Get exit status and send it to the client. */
+      debug("Command exited with status %d.", WEXITSTATUS(wait_status));
+      packet_start(SSH_SMSG_EXITSTATUS);
+      packet_put_int(WEXITSTATUS(wait_status));
+      packet_send();
+      packet_write_wait();
+
+      /* Wait for exit confirmation.  Note that there might be other
+         packets coming before it; however, the program has already died
+	 so we just ignore them.  The client is supposed to respond with
+	 the confirmation when it receives the exit status. */
+      do
+	{
+	  int plen;
+	  type = packet_read(&plen);
+	}
+      while (type != SSH_CMSG_EXIT_CONFIRMATION);
+
+      debug("Received exit confirmation.");
+      return;
+    }
+
+  /* Check if the program terminated due to a signal. */
+  if (WIFSIGNALED(wait_status))
+    packet_disconnect("Command terminated on signal %d.", 
+		      WTERMSIG(wait_status));
+
+  /* Some weird exit cause.  Just exit. */
+  packet_disconnect("wait returned status %04x.", wait_status);
+  /*NOTREACHED*/
+}
+
diff --git a/ssh-add.1 b/ssh-add.1
new file mode 100644
index 0000000..4c64ab2
--- /dev/null
+++ b/ssh-add.1
@@ -0,0 +1,116 @@
+.\"  -*- nroff -*-
+.\"
+.\" ssh-add.1
+.\"
+.\" Author: Tatu Ylonen <ylo@cs.hut.fi>
+.\"
+.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+.\"                    All rights reserved
+.\"
+.\" Created: Sat Apr 22 23:55:14 1995 ylo
+.\"
+.\" $Id: ssh-add.1,v 1.1 1999/10/27 03:42:45 damien Exp $
+.\"
+.Dd September 25, 1999
+.Dt SSH-ADD 1
+.Os
+.Sh NAME
+.Nm ssh-add
+.Nd adds identities for the authentication agent
+.Sh SYNOPSIS
+.Nm ssh-add
+.Op Fl ldD
+.Op Ar
+.Sh DESCRIPTION 
+.Nm
+adds identities to the authentication agent,
+.Xr ssh-agent 1 .
+When run without arguments, it adds the file
+.Pa $HOME/.ssh/identity .
+Alternative file names can be given on the
+command line.  If any file requires a passphrase,
+.Nm
+asks for the passphrase from the user. 
+The Passphrase it is read from the user's tty.  
+.Pp
+The authentication agent must be running and must be an ancestor of
+the current process for
+.Nm
+to work.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl l
+Lists all identities currently represented by the agent.
+.It Fl d
+Instead of adding the identity, removes the identity from the agent.
+.It Fl D
+Deletes all identities from the agent.
+.El
+.Sh FILES
+.Bl -tag -width Ds
+.Pa $HOME/.ssh/identity
+Contains the RSA authentication identity of the user.  This file
+should not be readable by anyone but the user.
+Note that
+.Nm
+ignores this file if it is accessible by others.
+It is possible to
+specify a passphrase when generating the key; that passphrase will be
+used to encrypt the private part of this file.  This is the
+default file added by
+.Nm
+when no other files have been specified.
+.Pp
+If
+.Nm
+needs a passphrase, it will read the passphrase from the current
+terminal if it was run from a terminal.  If
+.Nm
+does not have a terminal associated with it but
+.Ev DISPLAY
+is set, it
+will open an X11 window to read the passphrase.  This is particularly
+useful when calling
+.Nm
+from a
+.Pa .Xsession
+or related script.  (Note that on some machines it
+may be necessary to redirect the input from
+.Pa /dev/null
+to make this work.)
+.Sh AUTHOR
+Tatu Ylonen <ylo@cs.hut.fi>
+.Pp
+OpenSSH
+is a derivative of the original (free) ssh 1.2.12 release, but with bugs
+removed and newer features re-added.   Rapidly after the 1.2.12 release,
+newer versions bore successively more restrictive licenses.  This version
+of OpenSSH
+.Bl -bullet
+.It
+has all components of a restrictive nature (ie. patents, see
+.Xr ssl 8 )
+directly removed from the source code; any licensed or patented components
+are chosen from
+external libraries.
+.It
+has been updated to support ssh protocol 1.5.
+.It
+contains added support for 
+.Xr kerberos 8
+authentication and ticket passing.
+.It
+supports one-time password authentication with
+.Xr skey 1 .
+.El
+.Pp
+The libraries described in
+.Xr ssl 8
+are required for proper operation.
+.Sh SEE ALSO
+.Xr ssh 1 ,
+.Xr ssh-agent 1 ,
+.Xr ssh-keygen 1 ,
+.Xr sshd 8 ,
+.Xr ssl 8
diff --git a/ssh-add.c b/ssh-add.c
new file mode 100644
index 0000000..5ac3c30
--- /dev/null
+++ b/ssh-add.c
@@ -0,0 +1,254 @@
+/*
+
+ssh-add.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Thu Apr  6 00:52:24 1995 ylo
+
+Adds an identity to the authentication server, or removes an identity.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: ssh-add.c,v 1.1 1999/10/27 03:42:45 damien Exp $");
+
+#include "rsa.h"
+#include "ssh.h"
+#include "xmalloc.h"
+#include "authfd.h"
+
+void
+delete_file(const char *filename)
+{
+  RSA *key;
+  char *comment;
+  AuthenticationConnection *ac;
+
+  key = RSA_new();
+  if (!load_public_key(filename, key, &comment))
+    {
+      printf("Bad key file %s: %s\n", filename, strerror(errno));
+      return;
+    }
+
+  /* Send the request to the authentication agent. */
+  ac = ssh_get_authentication_connection();
+  if (!ac)
+    {
+      fprintf(stderr,
+	      "Could not open a connection to your authentication agent.\n");
+      RSA_free(key);
+      xfree(comment);
+      return;
+    }
+  if (ssh_remove_identity(ac, key))
+    fprintf(stderr, "Identity removed: %s (%s)\n", filename, comment);
+  else
+    fprintf(stderr, "Could not remove identity: %s\n", filename);
+  RSA_free(key);
+  xfree(comment);
+  ssh_close_authentication_connection(ac);
+}
+
+void
+delete_all()
+{
+  AuthenticationConnection *ac;
+  
+  /* Get a connection to the agent. */
+  ac = ssh_get_authentication_connection();
+  if (!ac)
+    {
+      fprintf(stderr,
+	      "Could not open a connection to your authentication agent.\n");
+      return;
+    }
+
+  /* Send a request to remove all identities. */
+  if (ssh_remove_all_identities(ac))
+    fprintf(stderr, "All identities removed.\n");
+  else
+    fprintf(stderr, "Failed to remove all identitities.\n");
+  
+  /* Close the connection to the agent. */
+  ssh_close_authentication_connection(ac);
+}
+
+void
+add_file(const char *filename)
+{
+  RSA *key;
+  RSA *public_key;
+  AuthenticationConnection *ac;
+  char *saved_comment, *comment, *pass;
+  int first;
+  
+  key = RSA_new();
+  public_key = RSA_new();
+  if (!load_public_key(filename, public_key, &saved_comment))
+    {
+      printf("Bad key file %s: %s\n", filename, strerror(errno));
+      return;
+    }
+  RSA_free(public_key);
+  
+  pass = xstrdup("");
+  first = 1;
+  while (!load_private_key(filename, pass, key, &comment))
+    {
+      /* Free the old passphrase. */
+      memset(pass, 0, strlen(pass));
+      xfree(pass);
+
+      /* Ask for a passphrase. */
+      if (getenv("DISPLAY") && !isatty(fileno(stdin)))
+	{
+	      xfree(saved_comment);
+	      return;
+	}
+      else
+	{
+	  if (first)
+	    printf("Need passphrase for %s (%s).\n", filename, saved_comment);
+	  else
+	    printf("Bad passphrase.\n");
+	  pass = read_passphrase("Enter passphrase: ", 1);
+	  if (strcmp(pass, "") == 0)
+	    {
+	      xfree(saved_comment);
+	      xfree(pass);
+	      return;
+	    }
+	}
+      first = 0;
+    }
+  memset(pass, 0, strlen(pass));
+  xfree(pass);
+
+  xfree(saved_comment);
+
+  /* Send the key to the authentication agent. */
+  ac = ssh_get_authentication_connection();
+  if (!ac)
+    {
+      fprintf(stderr,
+	      "Could not open a connection to your authentication agent.\n");
+      RSA_free(key);
+      xfree(comment);
+      return;
+    }
+  if (ssh_add_identity(ac, key, comment))
+    fprintf(stderr, "Identity added: %s (%s)\n", filename, comment);
+  else
+    fprintf(stderr, "Could not add identity: %s\n", filename);
+  RSA_free(key);
+  xfree(comment);
+  ssh_close_authentication_connection(ac);
+}
+
+void
+list_identities()
+{
+  AuthenticationConnection *ac;
+  BIGNUM *e, *n;
+  int bits, status;
+  char *comment;
+  int had_identities;
+
+  ac = ssh_get_authentication_connection();
+  if (!ac)
+    {
+      fprintf(stderr, "Could not connect to authentication server.\n");
+      return;
+    }
+  e = BN_new();
+  n = BN_new();
+  had_identities = 0;
+  for (status = ssh_get_first_identity(ac, &bits, e, n, &comment);
+       status;
+       status = ssh_get_next_identity(ac, &bits, e, n, &comment))
+    {
+      char *buf;
+      had_identities = 1;
+      printf("%d ", bits);
+      buf = BN_bn2dec(e);
+      assert(buf != NULL);
+      printf("%s ", buf);
+      free (buf);
+      buf = BN_bn2dec(n);
+      assert(buf != NULL);
+      printf("%s %s\n", buf, comment);
+      free (buf);
+      xfree(comment);
+    }
+  BN_clear_free(e);
+  BN_clear_free(n);
+  if (!had_identities)
+    printf("The agent has no identities.\n");
+  ssh_close_authentication_connection(ac);
+}
+
+int
+main(int ac, char **av)
+{
+  struct passwd *pw;
+  char buf[1024];
+  int no_files = 1;
+  int i;
+  int deleting = 0;
+
+  /* check if RSA support exists */
+  if (rsa_alive() == 0) {
+    extern char *__progname;
+
+    fprintf(stderr,
+      "%s: no RSA support in libssl and libcrypto.  See ssl(8).\n",
+      __progname);
+    exit(1);
+  }
+
+  for (i = 1; i < ac; i++)
+    {
+      if (strcmp(av[i], "-l") == 0)
+	{
+	  list_identities();
+	  no_files = 0; /* Don't default-add/delete if -l. */
+	  continue;
+	}
+      if (strcmp(av[i], "-d") == 0)
+	{
+	  deleting = 1;
+	  continue;
+	}
+      if (strcmp(av[i], "-D") == 0)
+	{
+	  delete_all();
+	  no_files = 0;
+	  continue;
+	}
+      no_files = 0;
+      if (deleting)
+	delete_file(av[i]);
+      else
+	add_file(av[i]);
+    }
+  if (no_files)
+    {
+      pw = getpwuid(getuid());
+      if (!pw)
+	{
+	  fprintf(stderr, "No user found with uid %d\n", (int)getuid());
+	  exit(1);
+	}
+      snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY);
+      if (deleting)
+	delete_file(buf);
+      else
+	add_file(buf);
+    }
+  exit(0);
+}
diff --git a/ssh-agent.1 b/ssh-agent.1
new file mode 100644
index 0000000..01c43cd
--- /dev/null
+++ b/ssh-agent.1
@@ -0,0 +1,124 @@
+.\"  -*- nroff -*-
+.\"
+.\" ssh-agent.1
+.\"
+.\" Author: Tatu Ylonen <ylo@cs.hut.fi>
+.\"
+.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+.\"                    All rights reserved
+.\"
+.\" Created: Sat Apr 23 20:10:43 1995 ylo
+.\"
+.\" $Id: ssh-agent.1,v 1.1 1999/10/27 03:42:45 damien Exp $
+.\"
+.Dd September 25, 1999
+.Dt SSH-AGENT 1
+.Os
+.Sh NAME
+.Nm ssh-agent
+.Nd authentication agent
+.Sh SYNOPSIS
+.Nm ssh-agent 
+.Ar command
+.Sh DESCRIPTION 
+.Nm
+is a program to hold authentication private keys.  The
+idea is that
+.Nm
+is started in the beginning of an X-session or a login session, and
+all other windows or programs are started as children of the ssh-agent
+program (the
+.Ar command
+normally starts X or is the user shell).  Programs started under
+the agent inherit a connection to the agent, and the agent is
+automatically used for RSA authentication when logging to other
+machines using
+.Xr ssh 1 .
+.Pp
+The agent initially does not have any private keys.  Keys are added
+using
+.Xr ssh-add 1 .
+When executed without arguments, 
+.Xr ssh-add 1
+adds the 
+.Pa $HOME/.ssh/identity
+file.  If the identity has a passphrase, 
+.Xr ssh-add 1
+asks for the passphrase (using a small X11 application if running
+under X11, or from the terminal if running without X).  It then sends
+the identity to the agent.  Several identities can be stored in the
+agent; the agent can automatically use any of these identities.
+.Ic ssh-add -l
+displays the identities currently held by the agent.
+.Pp
+The idea is that the agent is run in the user's local PC, laptop, or
+terminal.  Authentication data need not be stored on any other
+machine, and authentication passphrases never go over the network.
+However, the connection to the agent is forwarded over SSH
+remote logins, and the user can thus use the privileges given by the
+identities anywhere in the network in a secure way.
+.Pp
+A connection to the agent is inherited by child programs:
+A unix-domain socket is created
+.Pq Pa /tmp/ssh-XXXX/agent.<pid> ,
+and the name of this socket is stored in the
+.Ev SSH_AUTH_SOCK
+environment
+variable.  The socket is made accessible only to the current user.
+This method is easily abused by root or another instance of the same
+user.
+.Pp
+The agent exits automatically when the command given on the command
+line terminates.
+.Sh FILES
+.Bl -tag -width Ds
+.It Pa $HOME/.ssh/identity
+Contains the RSA authentication identity of the user.  This file
+should not be readable by anyone but the user.  It is possible to
+specify a passphrase when generating the key; that passphrase will be
+used to encrypt the private part of this file.  This file
+is not used by
+.Nm
+but is normally added to the agent using
+.Xr ssh-add 1
+at login time.
+.It Pa /tmp/ssh-XXXX/agent.<pid> ,
+Unix-domain sockets used to contain the connection to the
+authentication agent.  These sockets should only be readable by the
+owner.  The sockets should get automatically removed when the agent
+exits.
+.Sh AUTHOR
+Tatu Ylonen <ylo@cs.hut.fi>
+.Pp
+OpenSSH
+is a derivative of the original (free) ssh 1.2.12 release, but with bugs
+removed and newer features re-added.   Rapidly after the 1.2.12 release,
+newer versions bore successively more restrictive licenses.  This version
+of OpenSSH
+.Bl -bullet
+.It
+has all components of a restrictive nature (ie. patents, see
+.Xr ssl 8 )
+directly removed from the source code; any licensed or patented components
+are chosen from
+external libraries.
+.It
+has been updated to support ssh protocol 1.5.
+.It
+contains added support for 
+.Xr kerberos 8
+authentication and ticket passing.
+.It
+supports one-time password authentication with
+.Xr skey 1 .
+.El
+.Pp
+The libraries described in
+.Xr ssl 8
+are required for proper operation.
+.Sh SEE ALSO
+.Xr ssh 1 ,
+.Xr ssh-add 1 ,
+.Xr ssh-keygen 1 ,
+.Xr sshd 8 ,
+.Xr ssl 8
diff --git a/ssh-agent.c b/ssh-agent.c
new file mode 100644
index 0000000..19165b8
--- /dev/null
+++ b/ssh-agent.c
@@ -0,0 +1,572 @@
+/*
+
+ssh-agent.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Wed Mar 29 03:46:59 1995 ylo
+
+The authentication agent program.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: ssh-agent.c,v 1.1 1999/10/27 03:42:45 damien Exp $");
+
+#include "ssh.h"
+#include "rsa.h"
+#include "authfd.h"
+#include "buffer.h"
+#include "bufaux.h"
+#include "xmalloc.h"
+#include "packet.h"
+#include "getput.h"
+#include "mpaux.h"
+
+#include <openssl/md5.h>
+
+typedef struct
+{
+  int fd;
+  enum { AUTH_UNUSED, AUTH_SOCKET, AUTH_CONNECTION } type;
+  Buffer input;
+  Buffer output;
+} SocketEntry;
+
+unsigned int sockets_alloc = 0;
+SocketEntry *sockets = NULL;
+
+typedef struct
+{
+  RSA *key;
+  char *comment;
+} Identity;
+
+unsigned int num_identities = 0;
+Identity *identities = NULL;
+
+int max_fd = 0;
+
+/* pid of shell == parent of agent */
+int parent_pid = -1;
+
+/* pathname and directory for AUTH_SOCKET */
+char socket_name[1024];
+char socket_dir[1024];
+
+void
+process_request_identity(SocketEntry *e)
+{
+  Buffer msg;
+  int i;
+
+  buffer_init(&msg);
+  buffer_put_char(&msg, SSH_AGENT_RSA_IDENTITIES_ANSWER);
+  buffer_put_int(&msg, num_identities);
+  for (i = 0; i < num_identities; i++)
+    {
+      buffer_put_int(&msg, BN_num_bits(identities[i].key->n));
+      buffer_put_bignum(&msg, identities[i].key->e);
+      buffer_put_bignum(&msg, identities[i].key->n);
+      buffer_put_string(&msg, identities[i].comment, 
+			strlen(identities[i].comment));
+    }
+  buffer_put_int(&e->output, buffer_len(&msg));
+  buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg));
+  buffer_free(&msg);
+}
+
+void
+process_authentication_challenge(SocketEntry *e)
+{
+  int i, pub_bits, len;
+  BIGNUM *pub_e, *pub_n, *challenge;
+  Buffer msg;
+  MD5_CTX md;
+  unsigned char buf[32], mdbuf[16], session_id[16];
+  unsigned int response_type;
+
+  buffer_init(&msg);
+  pub_e = BN_new();
+  pub_n = BN_new();
+  challenge = BN_new();
+  pub_bits = buffer_get_int(&e->input);
+  buffer_get_bignum(&e->input, pub_e);
+  buffer_get_bignum(&e->input, pub_n);
+  buffer_get_bignum(&e->input, challenge);
+  if (buffer_len(&e->input) == 0)
+    {
+      /* Compatibility code for old servers. */
+      memset(session_id, 0, 16);
+      response_type = 0;
+    }
+  else
+    {
+      /* New code. */
+      buffer_get(&e->input, (char *)session_id, 16);
+      response_type = buffer_get_int(&e->input);
+    }
+  for (i = 0; i < num_identities; i++)
+    if (pub_bits == BN_num_bits(identities[i].key->n) &&
+	BN_cmp(pub_e, identities[i].key->e) == 0 &&
+	BN_cmp(pub_n, identities[i].key->n) == 0)
+      {
+	/* Decrypt the challenge using the private key. */
+	rsa_private_decrypt(challenge, challenge, identities[i].key);
+
+	/* Compute the desired response. */
+	switch (response_type)
+	  {
+	  case 0: /* As of protocol 1.0 */
+	    /* This response type is no longer supported. */
+	    log("Compatibility with ssh protocol 1.0 no longer supported.");
+	    buffer_put_char(&msg, SSH_AGENT_FAILURE);
+	    goto send;
+
+	  case 1: /* As of protocol 1.1 */
+	    /* The response is MD5 of decrypted challenge plus session id. */
+	    len = BN_num_bytes(challenge);
+	    assert(len <= 32 && len);
+	    memset(buf, 0, 32);
+	    BN_bn2bin(challenge, buf + 32 - len);
+	    MD5_Init(&md);
+	    MD5_Update(&md, buf, 32);
+	    MD5_Update(&md, session_id, 16);
+	    MD5_Final(mdbuf, &md);
+	    break;
+
+	  default:
+	    fatal("process_authentication_challenge: bad response_type %d", 
+		  response_type);
+	    break;
+	  }
+
+	/* Send the response. */
+	buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE);
+	for (i = 0; i < 16; i++)
+	  buffer_put_char(&msg, mdbuf[i]);
+
+	goto send;
+      }
+  /* Unknown identity.  Send failure. */
+  buffer_put_char(&msg, SSH_AGENT_FAILURE);
+ send:
+  buffer_put_int(&e->output, buffer_len(&msg));
+  buffer_append(&e->output, buffer_ptr(&msg),
+		buffer_len(&msg));
+  buffer_free(&msg);
+  BN_clear_free(pub_e);
+  BN_clear_free(pub_n);
+  BN_clear_free(challenge);
+}
+
+void
+process_remove_identity(SocketEntry *e)
+{
+  unsigned int bits;
+  unsigned int i;
+  BIGNUM *dummy, *n;
+  
+  dummy = BN_new();
+  n = BN_new();
+  
+  /* Get the key from the packet. */
+  bits = buffer_get_int(&e->input);
+  buffer_get_bignum(&e->input, dummy);
+  buffer_get_bignum(&e->input, n);
+  
+  /* Check if we have the key. */
+  for (i = 0; i < num_identities; i++)
+    if (BN_cmp(identities[i].key->n, n) == 0)
+      {
+	/* We have this key.  Free the old key.  Since we don\'t want to leave
+	   empty slots in the middle of the array, we actually free the
+	   key there and copy data from the last entry. */
+	RSA_free(identities[i].key);
+	xfree(identities[i].comment);
+	if (i < num_identities - 1)
+	  identities[i] = identities[num_identities - 1];
+	num_identities--;
+	BN_clear_free(dummy);
+	BN_clear_free(n);
+
+	/* Send success. */
+	buffer_put_int(&e->output, 1);
+	buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
+	return;
+      }
+  /* We did not have the key. */
+  BN_clear(dummy);
+  BN_clear(n);
+
+  /* Send failure. */
+  buffer_put_int(&e->output, 1);
+  buffer_put_char(&e->output, SSH_AGENT_FAILURE);
+}
+
+/* Removes all identities from the agent. */
+
+void
+process_remove_all_identities(SocketEntry *e)
+{
+  unsigned int i;
+  
+  /* Loop over all identities and clear the keys. */
+  for (i = 0; i < num_identities; i++)
+    {
+      RSA_free(identities[i].key);
+      xfree(identities[i].comment);
+    }
+
+  /* Mark that there are no identities. */
+  num_identities = 0;
+
+  /* Send success. */
+  buffer_put_int(&e->output, 1);
+  buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
+  return;
+}
+
+/* Adds an identity to the agent. */
+
+void
+process_add_identity(SocketEntry *e)
+{
+  RSA *k;
+  int i;
+  BIGNUM *aux;
+  BN_CTX *ctx;
+  
+  if (num_identities == 0)
+    identities = xmalloc(sizeof(Identity));
+  else
+    identities = xrealloc(identities, (num_identities + 1) * sizeof(Identity));
+
+  identities[num_identities].key = RSA_new();
+  k = identities[num_identities].key;
+  buffer_get_int(&e->input); /* bits */
+  k->n = BN_new();
+  buffer_get_bignum(&e->input, k->n);
+  k->e = BN_new();
+  buffer_get_bignum(&e->input, k->e);
+  k->d = BN_new();
+  buffer_get_bignum(&e->input, k->d);
+  k->iqmp = BN_new();
+  buffer_get_bignum(&e->input, k->iqmp);
+  /* SSH and SSL have p and q swapped */
+  k->q = BN_new();
+  buffer_get_bignum(&e->input, k->q); /* p */
+  k->p = BN_new();
+  buffer_get_bignum(&e->input, k->p); /* q */
+
+  /* Generate additional parameters */
+  aux = BN_new();
+  ctx = BN_CTX_new();
+
+  BN_sub(aux, k->q, BN_value_one());
+  k->dmq1 = BN_new();
+  BN_mod(k->dmq1, k->d, aux, ctx);
+
+  BN_sub(aux, k->p, BN_value_one());
+  k->dmp1 = BN_new();
+  BN_mod(k->dmp1, k->d, aux, ctx);
+
+  BN_clear_free(aux);
+  BN_CTX_free(ctx);
+  
+  identities[num_identities].comment = buffer_get_string(&e->input, NULL);
+
+  /* Check if we already have the key. */
+  for (i = 0; i < num_identities; i++)
+    if (BN_cmp(identities[i].key->n, k->n) == 0)
+      {
+	/* We already have this key.  Clear and free the new data and
+	   return success. */
+	RSA_free(k);
+	xfree(identities[num_identities].comment);
+
+	/* Send success. */
+	buffer_put_int(&e->output, 1);
+	buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
+	return;
+      }
+
+  /* Increment the number of identities. */
+  num_identities++;
+  
+  /* Send a success message. */
+  buffer_put_int(&e->output, 1);
+  buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
+}
+
+void
+process_message(SocketEntry *e)
+{
+  unsigned int msg_len;
+  unsigned int type;
+  unsigned char *cp;
+  if (buffer_len(&e->input) < 5)
+    return; /* Incomplete message. */
+  cp = (unsigned char *)buffer_ptr(&e->input);
+  msg_len = GET_32BIT(cp);
+  if (msg_len > 256 * 1024)
+    {
+      shutdown(e->fd, SHUT_RDWR);
+      close(e->fd);
+      e->type = AUTH_UNUSED;
+      return;
+    }
+  if (buffer_len(&e->input) < msg_len + 4)
+    return;
+  buffer_consume(&e->input, 4);
+  type = buffer_get_char(&e->input);
+
+  switch (type)
+    {
+    case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
+      process_request_identity(e);
+      break;
+    case SSH_AGENTC_RSA_CHALLENGE:
+      process_authentication_challenge(e);
+      break;
+    case SSH_AGENTC_ADD_RSA_IDENTITY:
+      process_add_identity(e);
+      break;
+    case SSH_AGENTC_REMOVE_RSA_IDENTITY:
+      process_remove_identity(e);
+      break;
+    case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
+      process_remove_all_identities(e);
+      break;
+    default:
+      /* Unknown message.  Respond with failure. */
+      error("Unknown message %d", type);
+      buffer_clear(&e->input);
+      buffer_put_int(&e->output, 1);
+      buffer_put_char(&e->output, SSH_AGENT_FAILURE);
+      break;
+    }
+}
+
+void
+new_socket(int type, int fd)
+{
+  unsigned int i, old_alloc;
+  if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
+    error("fcntl O_NONBLOCK: %s", strerror(errno));
+
+  if (fd > max_fd)
+    max_fd = fd;
+
+  for (i = 0; i < sockets_alloc; i++)
+    if (sockets[i].type == AUTH_UNUSED)
+      {
+	sockets[i].fd = fd;
+	sockets[i].type = type;
+	buffer_init(&sockets[i].input);
+	buffer_init(&sockets[i].output);
+	return;
+      }
+  old_alloc = sockets_alloc;
+  sockets_alloc += 10;
+  if (sockets)
+    sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0]));
+  else
+    sockets = xmalloc(sockets_alloc * sizeof(sockets[0]));
+  for (i = old_alloc; i < sockets_alloc; i++)
+    sockets[i].type = AUTH_UNUSED;
+  sockets[old_alloc].type = type;
+  sockets[old_alloc].fd = fd;
+  buffer_init(&sockets[old_alloc].input);
+  buffer_init(&sockets[old_alloc].output);
+}
+
+void
+prepare_select(fd_set *readset, fd_set *writeset)
+{
+  unsigned int i;
+  for (i = 0; i < sockets_alloc; i++)
+    switch (sockets[i].type)
+      {
+      case AUTH_SOCKET:
+      case AUTH_CONNECTION:
+	FD_SET(sockets[i].fd, readset);
+	if (buffer_len(&sockets[i].output) > 0)
+	  FD_SET(sockets[i].fd, writeset);
+	break;
+      case AUTH_UNUSED:
+	break;
+      default:
+	fatal("Unknown socket type %d", sockets[i].type);
+	break;
+      }
+}
+
+void after_select(fd_set *readset, fd_set *writeset)
+{
+  unsigned int i;
+  int len, sock;
+  char buf[1024];
+  struct sockaddr_un sunaddr;
+
+  for (i = 0; i < sockets_alloc; i++)
+    switch (sockets[i].type)
+      {
+      case AUTH_UNUSED:
+	break;
+      case AUTH_SOCKET:
+	if (FD_ISSET(sockets[i].fd, readset))
+	  {
+	    len = sizeof(sunaddr);
+	    sock = accept(sockets[i].fd, (struct sockaddr *)&sunaddr, &len);
+	    if (sock < 0)
+	      {
+		perror("accept from AUTH_SOCKET");
+		break;
+	      }
+	    new_socket(AUTH_CONNECTION, sock);
+	  }
+	break;
+      case AUTH_CONNECTION:
+	if (buffer_len(&sockets[i].output) > 0 &&
+	    FD_ISSET(sockets[i].fd, writeset))
+	  {
+	    len = write(sockets[i].fd, buffer_ptr(&sockets[i].output),
+			buffer_len(&sockets[i].output));
+	    if (len <= 0)
+	      {
+		shutdown(sockets[i].fd, SHUT_RDWR);
+		close(sockets[i].fd);
+		sockets[i].type = AUTH_UNUSED;
+		break;
+	      }
+	    buffer_consume(&sockets[i].output, len);
+	  }
+	if (FD_ISSET(sockets[i].fd, readset))
+	  {
+	    len = read(sockets[i].fd, buf, sizeof(buf));
+	    if (len <= 0)
+	      {
+		shutdown(sockets[i].fd, SHUT_RDWR);
+		close(sockets[i].fd);
+		sockets[i].type = AUTH_UNUSED;
+		break;
+	      }
+	    buffer_append(&sockets[i].input, buf, len);
+	    process_message(&sockets[i]);
+	  }
+	break;
+      default:
+	fatal("Unknown type %d", sockets[i].type);
+      }
+}
+
+void
+check_parent_exists(int sig)
+{
+  if (kill(parent_pid, 0) < 0)
+    {
+      /* printf("Parent has died - Authentication agent exiting.\n"); */
+      exit(1);
+    }
+  signal(SIGALRM, check_parent_exists);
+  alarm(10);
+}
+
+void cleanup_socket(void) {
+  remove(socket_name);
+  rmdir(socket_dir);
+}
+
+int
+main(int ac, char **av)
+{
+  fd_set readset, writeset;
+  int sock;
+  struct sockaddr_un sunaddr;
+
+  /* check if RSA support exists */
+  if (rsa_alive() == 0) {
+    extern char *__progname;
+    fprintf(stderr,
+      "%s: no RSA support in libssl and libcrypto.  See ssl(8).\n",
+      __progname);
+    exit(1);
+  }
+
+  if (ac < 2)
+    {
+      fprintf(stderr, "ssh-agent version %s\n", SSH_VERSION);
+      fprintf(stderr, "Usage: %s command\n", av[0]);
+      exit(1);
+    }
+
+  parent_pid = getpid();
+
+  /* Create private directory for agent socket */
+  strlcpy(socket_dir, "/tmp/ssh-XXXXXXXX", sizeof socket_dir);
+  if (mkdtemp(socket_dir) == NULL) {
+      perror("mkdtemp: private socket dir");
+      exit(1);
+  }
+  snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir, parent_pid);
+  
+  /* Fork, and have the parent execute the command.  The child continues as
+     the authentication agent. */
+  if (fork() != 0)
+    { /* Parent - execute the given command. */
+      setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1);
+      execvp(av[1], av + 1);
+      perror(av[1]);
+      exit(1);
+    }
+
+  if (atexit(cleanup_socket) < 0) {
+	perror("atexit");
+	cleanup_socket();
+	exit(1);
+  }
+
+  sock = socket(AF_UNIX, SOCK_STREAM, 0);
+  if (sock < 0)
+    {
+      perror("socket");
+      exit(1);
+    }
+  memset(&sunaddr, 0, sizeof(sunaddr));
+  sunaddr.sun_family = AF_UNIX;
+  strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path));
+  if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0)
+    {
+      perror("bind");
+      exit(1);
+    }
+  if (listen(sock, 5) < 0)
+    {
+      perror("listen");
+      exit(1);
+    }
+  new_socket(AUTH_SOCKET, sock);
+  signal(SIGALRM, check_parent_exists);
+  alarm(10);
+
+  signal(SIGINT, SIG_IGN);
+  while (1)
+    {
+      FD_ZERO(&readset);
+      FD_ZERO(&writeset);
+      prepare_select(&readset, &writeset);
+      if (select(max_fd + 1, &readset, &writeset, NULL, NULL) < 0)
+	{
+	  if (errno == EINTR)
+	    continue;
+	  perror("select");
+	  exit(1);
+	}
+      after_select(&readset, &writeset);
+    }
+  /*NOTREACHED*/
+}
diff --git a/ssh-keygen.1 b/ssh-keygen.1
new file mode 100644
index 0000000..67fbfd2
--- /dev/null
+++ b/ssh-keygen.1
@@ -0,0 +1,155 @@
+.\"  -*- nroff -*-
+.\"
+.\" ssh-keygen.1
+.\"
+.\" Author: Tatu Ylonen <ylo@cs.hut.fi>
+.\"
+.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+.\"                    All rights reserved
+.\"
+.\" Created: Sat Apr 22 23:55:14 1995 ylo
+.\"
+.\" $Id: ssh-keygen.1,v 1.1 1999/10/27 03:42:45 damien Exp $
+.\"
+.Dd September 25, 1999
+.Dt SSH-KEYGEN 1
+.Os
+.Sh NAME
+.Nm ssh-keygen
+.Nd authentication key generation
+.Sh SYNOPSIS
+.Nm ssh-keygen
+.Op Fl q
+.Op Fl b Ar bits
+.Op Fl N Ar new_passphrase
+.Op Fl C Ar comment
+.Nm ssh-keygen
+.Fl p
+.Op Fl P Ar old_passphrase
+.Op Fl N Ar new_passphrase
+.Nm ssh-keygen
+.Fl c
+.Op Fl P Ar passphrase
+.Op Fl C Ar comment
+.Sh DESCRIPTION 
+.Nm
+generates and manages authentication keys for 
+.Xr ssh 1 .
+Normally each user wishing to use SSH
+with RSA authentication runs this once to create the authentication
+key in
+.Pa $HOME/.ssh/identity .
+Additionally, the system administrator may use this to generate host keys.
+.Pp
+Normally this program generates the key and asks for a file in which
+to store the private key.  The public key is stored in a file with the
+same name but
+.Dq .pub
+appended.  The program also asks for a
+passphrase.  The passphrase may be empty to indicate no passphrase
+(host keys must have empty passphrase), or it may be a string of
+arbitrary length.  Good passphrases are 10-30 characters long and are
+not simple sentences or otherwise easily guessable (English
+prose has only 1-2 bits of entropy per word, and provides very bad
+passphrases).  The passphrase can be changed later by using the
+.Fl p
+option.
+.Pp
+There is no way to recover a lost passphrase.  If the passphrase is
+lost or forgotten, you will have to generate a new key and copy the
+corresponding public key to other machines.
+.Pp
+There is also a comment field in the key file that is only for
+convenience to the user to help identify the key.  The comment can
+tell what the key is for, or whatever is useful.  The comment is
+initialized to
+.Dq user@host
+when the key is created, but can be changed using the
+.Fl c
+option.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl b Ar bits
+Specifies the number of bits in the key to create.  Minimum is 512
+bits.  Generally 1024 bits is considered sufficient, and key sizes
+above that no longer improve security but make things slower.  The
+default is 1024 bits.
+.It Fl c
+Requests changing the comment in the private and public key files.
+The program will prompt for the file containing the private keys, for
+passphrase if the key has one, and for the new comment.
+.It Fl p
+Requests changing the passphrase of a private key file instead of
+creating a new private key.  The program will prompt for the file
+containing the private key, for the old passphrase, and twice for the
+new passphrase.
+.It Fl q
+Silence
+.Nm ssh-keygen .
+Used by
+.Pa /etc/rc
+when creating a new key.
+.It Fl C Ar comment
+Provides the new comment.
+.It Fl N Ar new_passphrase
+Provides the new passphrase.
+.It Fl P Ar passphrase
+Provides the (old) passphrase.
+.El
+.Sh FILES
+.Bl -tag -width Ds
+.It Pa $HOME/.ssh/random_seed
+Used for seeding the random number generator.  This file should not be
+readable by anyone but the user.  This file is created the first time
+the program is run, and is updated every time.
+.It Pa $HOME/.ssh/identity
+Contains the RSA authentication identity of the user.  This file
+should not be readable by anyone but the user.  It is possible to
+specify a passphrase when generating the key; that passphrase will be
+used to encrypt the private part of this file using 3DES.  This file
+is not automatically accessed by
+.Nm
+but it is offered as the default file for the private key.
+.It Pa $HOME/.ssh/identity.pub
+Contains the public key for authentication.  The contents of this file
+should be added to
+.Pa $HOME/.ssh/authorized_keys
+on all machines
+where you wish to log in using RSA authentication.  There is no
+need to keep the contents of this file secret.
+.Sh AUTHOR
+Tatu Ylonen <ylo@cs.hut.fi>
+.Pp
+OpenSSH
+is a derivative of the original (free) ssh 1.2.12 release, but with bugs
+removed and newer features re-added.   Rapidly after the 1.2.12 release,
+newer versions bore successively more restrictive licenses.  This version
+of OpenSSH
+.Bl -bullet
+.It
+has all components of a restrictive nature (ie. patents, see
+.Xr ssl 8 )
+directly removed from the source code; any licensed or patented components
+are chosen from
+external libraries.
+.It
+has been updated to support ssh protocol 1.5.
+.It
+contains added support for 
+.Xr kerberos 8
+authentication and ticket passing.
+.It
+supports one-time password authentication with
+.Xr skey 1 .
+.El
+.Pp
+The libraries described in
+.Xr ssl 8
+are required for proper operation.
+.Sh SEE ALSO
+.Xr ssh 1 ,
+.Xr ssh-add 1 ,
+.Xr ssh-agent 1,
+.Xr sshd 8 ,
+.Xr ssl 8
diff --git a/ssh-keygen.c b/ssh-keygen.c
new file mode 100644
index 0000000..2ba64e7
--- /dev/null
+++ b/ssh-keygen.c
@@ -0,0 +1,552 @@
+/*
+
+ssh-keygen.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Mon Mar 27 02:26:40 1995 ylo
+
+Identity and host key generation and maintenance.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: ssh-keygen.c,v 1.1 1999/10/27 03:42:45 damien Exp $");
+
+#include "rsa.h"
+#include "ssh.h"
+#include "xmalloc.h"
+
+/* Generated private key. */
+RSA *private_key;
+
+/* Generated public key. */
+RSA *public_key;
+
+/* Number of bits in the RSA key.  This value can be changed on the command
+   line. */
+int bits = 1024;
+
+/* Flag indicating that we just want to change the passphrase.  This can be
+   set on the command line. */
+int change_passphrase = 0;
+
+/* Flag indicating that we just want to change the comment.  This can be set
+   on the command line. */
+int change_comment = 0;
+
+int quiet = 0;
+
+/* This is set to the identity file name if given on the command line. */
+char *identity_file = NULL;
+
+/* This is set to the passphrase if given on the command line. */
+char *identity_passphrase = NULL;
+
+/* This is set to the new passphrase if given on the command line. */
+char *identity_new_passphrase = NULL;
+
+/* This is set to the new comment if given on the command line. */
+char *identity_comment = NULL;
+
+/* Perform changing a passphrase.  The argument is the passwd structure
+   for the current user. */
+
+void
+do_change_passphrase(struct passwd *pw)
+{
+  char buf[1024], *comment;
+  char *old_passphrase, *passphrase1, *passphrase2;
+  struct stat st;
+  RSA *private_key;
+
+  /* Read key file name. */
+  if (identity_file != NULL) {
+      strncpy(buf, identity_file, sizeof(buf));
+      buf[sizeof(buf) - 1] = '\0';
+  } else {
+    printf("Enter file in which the key is ($HOME/%s): ", SSH_CLIENT_IDENTITY);
+    fflush(stdout);
+    if (fgets(buf, sizeof(buf), stdin) == NULL)
+      exit(1);
+    if (strchr(buf, '\n'))
+      *strchr(buf, '\n') = 0;
+    if (strcmp(buf, "") == 0)
+      snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY);
+  }
+
+  /* Check if the file exists. */
+  if (stat(buf, &st) < 0)
+    {
+      perror(buf);
+      exit(1);
+    }
+  
+  /* Try to load the public key from the file the verify that it is
+     readable and of the proper format. */
+  public_key = RSA_new();
+  if (!load_public_key(buf, public_key, NULL))
+    {
+      printf("%s is not a valid key file.\n", buf);
+      exit(1);
+    }
+  /* Clear the public key since we are just about to load the whole file. */
+  RSA_free(public_key);
+
+  /* Try to load the file with empty passphrase. */
+  private_key = RSA_new();
+  if (!load_private_key(buf, "", private_key, &comment)) {
+    /* Read passphrase from the user. */
+    if (identity_passphrase)
+      old_passphrase = xstrdup(identity_passphrase);
+    else
+      old_passphrase = read_passphrase("Enter old passphrase: ", 1);
+    /* Try to load using the passphrase. */
+    if (!load_private_key(buf, old_passphrase, private_key, &comment))
+      {
+	memset(old_passphrase, 0, strlen(old_passphrase));
+	xfree(old_passphrase);
+	printf("Bad passphrase.\n");
+	exit(1);
+      }
+    /* Destroy the passphrase. */
+    memset(old_passphrase, 0, strlen(old_passphrase));
+    xfree(old_passphrase);
+  }
+  printf("Key has comment '%s'\n", comment);
+  
+  /* Ask the new passphrase (twice). */
+  if (identity_new_passphrase)
+    {
+      passphrase1 = xstrdup(identity_new_passphrase);
+      passphrase2 = NULL;
+    }
+  else
+    {
+      passphrase1 = 
+	read_passphrase("Enter new passphrase (empty for no passphrase): ", 1);
+      passphrase2 = read_passphrase("Enter same passphrase again: ", 1);
+
+      /* Verify that they are the same. */
+      if (strcmp(passphrase1, passphrase2) != 0)
+	{
+	  memset(passphrase1, 0, strlen(passphrase1));
+	  memset(passphrase2, 0, strlen(passphrase2));
+	  xfree(passphrase1);
+	  xfree(passphrase2);
+	  printf("Pass phrases do not match.  Try again.\n");
+	  exit(1);
+	}
+      /* Destroy the other copy. */
+      memset(passphrase2, 0, strlen(passphrase2));
+      xfree(passphrase2);
+    }
+
+  /* Save the file using the new passphrase. */
+  if (!save_private_key(buf, passphrase1, private_key, comment))
+    {
+      printf("Saving the key failed: %s: %s.\n",
+	     buf, strerror(errno));
+      memset(passphrase1, 0, strlen(passphrase1));
+      xfree(passphrase1);
+      RSA_free(private_key);
+      xfree(comment);
+      exit(1);
+    }
+  /* Destroy the passphrase and the copy of the key in memory. */
+  memset(passphrase1, 0, strlen(passphrase1));
+  xfree(passphrase1);
+  RSA_free(private_key); /* Destroys contents */
+  xfree(comment);
+
+  printf("Your identification has been saved with the new passphrase.\n");
+  exit(0);
+}
+
+/* Change the comment of a private key file. */
+
+void
+do_change_comment(struct passwd *pw)
+{
+  char buf[1024], new_comment[1024], *comment;
+  RSA *private_key;
+  char *passphrase;
+  struct stat st;
+  FILE *f;
+  char *tmpbuf;
+
+  /* Read key file name. */
+  if (identity_file)
+    {
+      strncpy(buf, identity_file, sizeof(buf));
+      buf[sizeof(buf) - 1] = '\0';
+    }
+  else
+    {
+      printf("Enter file in which the key is ($HOME/%s): ", 
+	     SSH_CLIENT_IDENTITY);
+      fflush(stdout);
+      if (fgets(buf, sizeof(buf), stdin) == NULL)
+	exit(1);
+      if (strchr(buf, '\n'))
+	*strchr(buf, '\n') = 0;
+      if (strcmp(buf, "") == 0)
+	snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY);
+    }
+
+  /* Check if the file exists. */
+  if (stat(buf, &st) < 0)
+    {
+      perror(buf);
+      exit(1);
+    }
+  
+  /* Try to load the public key from the file the verify that it is
+     readable and of the proper format. */
+  public_key = RSA_new();
+  if (!load_public_key(buf, public_key, NULL))
+    {
+      printf("%s is not a valid key file.\n", buf);
+      exit(1);
+    }
+
+  private_key = RSA_new();
+  /* Try to load the file with empty passphrase. */
+  if (load_private_key(buf, "", private_key, &comment))
+    passphrase = xstrdup("");
+  else
+    {
+      /* Read passphrase from the user. */
+      if (identity_passphrase)
+	passphrase = xstrdup(identity_passphrase);
+      else
+	if (identity_new_passphrase)
+	  passphrase = xstrdup(identity_new_passphrase);
+	else
+	  passphrase = read_passphrase("Enter passphrase: ", 1);
+      /* Try to load using the passphrase. */
+      if (!load_private_key(buf, passphrase, private_key, &comment))
+	{
+	  memset(passphrase, 0, strlen(passphrase));
+	  xfree(passphrase);
+	  printf("Bad passphrase.\n");
+	  exit(1);
+	}
+    }
+  printf("Key now has comment '%s'\n", comment);
+
+  if (identity_comment)
+    {
+      strncpy(new_comment, identity_comment, sizeof(new_comment));
+      new_comment[sizeof(new_comment) - 1] = '\0';
+    }
+  else
+    {
+      printf("Enter new comment: ");
+      fflush(stdout);
+      if (!fgets(new_comment, sizeof(new_comment), stdin))
+	{
+	  memset(passphrase, 0, strlen(passphrase));
+	  RSA_free(private_key);
+	  exit(1);
+	}
+      
+      /* Remove terminating newline from comment. */
+      if (strchr(new_comment, '\n'))
+	*strchr(new_comment, '\n') = 0;
+    }
+      
+  /* Save the file using the new passphrase. */
+  if (!save_private_key(buf, passphrase, private_key, new_comment))
+    {
+      printf("Saving the key failed: %s: %s.\n",
+	     buf, strerror(errno));
+      memset(passphrase, 0, strlen(passphrase));
+      xfree(passphrase);
+      RSA_free(private_key);
+      xfree(comment);
+      exit(1);
+    }
+
+  /* Destroy the passphrase and the private key in memory. */
+  memset(passphrase, 0, strlen(passphrase));
+  xfree(passphrase);
+  RSA_free(private_key);
+
+  /* Save the public key in text format in a file with the same name but
+     .pub appended. */
+  strcat(buf, ".pub");
+  f = fopen(buf, "w");
+  if (!f)
+    {
+      printf("Could not save your public key in %s\n", buf);
+      exit(1);
+    }
+  fprintf(f, "%d ", BN_num_bits(public_key->n));
+  tmpbuf = BN_bn2dec(public_key->e);
+  fprintf(f, "%s ", tmpbuf);
+  free (tmpbuf);
+  tmpbuf = BN_bn2dec(public_key->n);
+  fprintf(f, "%s %s\n", tmpbuf, new_comment);
+  free (tmpbuf);
+  fclose(f);
+
+  xfree(comment);
+
+  printf("The comment in your key file has been changed.\n");
+  exit(0);
+}
+
+/* Main program for key management. */
+
+int
+main(int ac, char **av)
+{
+  char buf[16384], buf2[1024], *passphrase1, *passphrase2;
+  struct passwd *pw;
+  char *tmpbuf;
+  int opt;
+  struct stat st;
+  FILE *f;
+  char hostname[MAXHOSTNAMELEN];
+  extern int optind;
+  extern char *optarg;
+
+  /* check if RSA support exists */
+  if (rsa_alive() == 0) {
+    extern char *__progname;
+
+    fprintf(stderr,
+      "%s: no RSA support in libssl and libcrypto.  See ssl(8).\n",
+      __progname);
+    exit(1);
+  }
+
+  /* Get user\'s passwd structure.  We need this for the home directory. */
+  pw = getpwuid(getuid());
+  if (!pw)
+    {
+      printf("You don't exist, go away!\n");
+      exit(1);
+    }
+
+  /* Create ~/.ssh directory if it doesn\'t already exist. */
+  snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_USER_DIR);
+  if (stat(buf, &st) < 0)
+    if (mkdir(buf, 0755) < 0)
+      error("Could not create directory '%s'.", buf);
+
+  /* Parse command line arguments. */
+  while ((opt = getopt(ac, av, "qpcb:f:P:N:C:")) != EOF)
+    {
+      switch (opt)
+	{
+	case 'b':
+	  bits = atoi(optarg);
+	  if (bits < 512 || bits > 32768)
+	    {
+	      printf("Bits has bad value.\n");
+	      exit(1);
+	    }
+	  break;
+
+	case 'p':
+	  change_passphrase = 1;
+	  break;
+
+	case 'c':
+	  change_comment = 1;
+	  break;
+
+	case 'f':
+	  identity_file = optarg;
+	  break;
+	  
+	case 'P':
+	  identity_passphrase = optarg;
+	  break;
+
+	case 'N':
+	  identity_new_passphrase = optarg;
+	  break;
+
+	case 'C':
+	  identity_comment = optarg;
+	  break;
+
+        case 'q':
+	  quiet = 1;
+	  break;
+
+	case '?':
+	default:
+	  printf("ssh-keygen version %s\n", SSH_VERSION);
+	  printf("Usage: %s [-b bits] [-p] [-c] [-f file] [-P pass] [-N new-pass] [-C comment]\n", av[0]);
+	  exit(1);
+	}
+    }
+  if (optind < ac)
+    {
+      printf("Too many arguments.\n");
+      exit(1);
+    }
+  if (change_passphrase && change_comment)
+    {
+      printf("Can only have one of -p and -c.\n");
+      exit(1);
+    }
+
+  /* If the user requested to change the passphrase, do it now.  This
+     function never returns. */
+  if (change_passphrase)
+    do_change_passphrase(pw);
+
+  /* If the user requested to change the comment, do it now.  This function
+     never returns. */
+  if (change_comment)
+    do_change_comment(pw);
+
+  arc4random_stir();
+
+  if (quiet)
+    rsa_set_verbose(0);
+
+  /* Generate the rsa key pair. */
+  private_key = RSA_new();
+  public_key = RSA_new();
+  rsa_generate_key(private_key, public_key, bits);
+
+ ask_file_again:
+
+  /* Ask for a file to save the key in. */
+  if (identity_file)
+    {
+      strncpy(buf, identity_file, sizeof(buf));
+      buf[sizeof(buf) - 1] = '\0';
+    }
+  else
+    {
+      printf("Enter file in which to save the key ($HOME/%s): ", 
+	     SSH_CLIENT_IDENTITY);
+      fflush(stdout);
+      if (fgets(buf, sizeof(buf), stdin) == NULL)
+	exit(1);
+      if (strchr(buf, '\n'))
+	*strchr(buf, '\n') = 0;
+      if (strcmp(buf, "") == 0)
+	snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY);
+    }
+
+  /* If the file aready exists, ask the user to confirm. */
+  if (stat(buf, &st) >= 0)
+    {
+      printf("%s already exists.\n", buf);
+      printf("Overwrite (y/n)? ");
+      fflush(stdout);
+      if (fgets(buf2, sizeof(buf2), stdin) == NULL)
+	exit(1);
+      if (buf2[0] != 'y' && buf2[0] != 'Y')
+	exit(1);
+    }
+  
+  /* Ask for a passphrase (twice). */
+  if (identity_passphrase)
+    passphrase1 = xstrdup(identity_passphrase);
+  else
+    if (identity_new_passphrase)
+      passphrase1 = xstrdup(identity_new_passphrase);
+    else
+      {
+      passphrase_again:
+	passphrase1 = 
+	  read_passphrase("Enter passphrase (empty for no passphrase): ", 1);
+	passphrase2 = read_passphrase("Enter same passphrase again: ", 1);
+	if (strcmp(passphrase1, passphrase2) != 0)
+	  {
+	    /* The passphrases do not match.  Clear them and retry. */
+	    memset(passphrase1, 0, strlen(passphrase1));
+	    memset(passphrase2, 0, strlen(passphrase2));
+	    xfree(passphrase1);
+	    xfree(passphrase2);
+	    printf("Passphrases do not match.  Try again.\n");
+	    goto passphrase_again;
+	  }
+	/* Clear the other copy of the passphrase. */
+	memset(passphrase2, 0, strlen(passphrase2));
+	xfree(passphrase2);
+      }
+
+  /* Create default commend field for the passphrase.  The user can later
+     edit this field. */
+  if (identity_comment)
+    {
+      strlcpy(buf2, identity_comment, sizeof(buf2));
+    }
+  else
+    {
+      if (gethostname(hostname, sizeof(hostname)) < 0)
+	{
+	  perror("gethostname");
+	  exit(1);
+	}
+      snprintf(buf2, sizeof buf2, "%s@%s", pw->pw_name, hostname);
+    }
+
+  /* Save the key with the given passphrase and comment. */
+  if (!save_private_key(buf, passphrase1, private_key, buf2))
+    {
+      printf("Saving the key failed: %s: %s.\n",
+	     buf, strerror(errno));
+      memset(passphrase1, 0, strlen(passphrase1));
+      xfree(passphrase1);
+      goto ask_file_again;
+    }
+  /* Clear the passphrase. */
+  memset(passphrase1, 0, strlen(passphrase1));
+  xfree(passphrase1);
+
+  /* Clear the private key and the random number generator. */
+  RSA_free(private_key);
+  arc4random_stir();
+
+  if (!quiet)
+    printf("Your identification has been saved in %s.\n", buf);
+
+  /* Display the public key on the screen. */
+  if (!quiet) {
+    printf("Your public key is:\n");
+    printf("%d ", BN_num_bits(public_key->n));
+    tmpbuf = BN_bn2dec(public_key->e);
+    printf("%s ", tmpbuf);
+    free(tmpbuf);
+    tmpbuf = BN_bn2dec(public_key->n);
+    printf("%s %s\n", tmpbuf, buf2);
+    free(tmpbuf);
+  }
+
+  /* Save the public key in text format in a file with the same name but
+     .pub appended. */
+  strcat(buf, ".pub");
+  f = fopen(buf, "w");
+  if (!f)
+    {
+      printf("Could not save your public key in %s\n", buf);
+      exit(1);
+    }
+  fprintf(f, "%d ", BN_num_bits(public_key->n));
+  tmpbuf = BN_bn2dec(public_key->e);
+  fprintf(f, "%s ", tmpbuf);
+  free(tmpbuf);
+  tmpbuf = BN_bn2dec(public_key->n);
+  fprintf(f, "%s %s\n", tmpbuf, buf2);
+  free(tmpbuf);
+  fclose(f);
+
+  if (!quiet)
+    printf("Your public key has been saved in %s\n", buf);
+  
+  exit(0);
+}
diff --git a/ssh.1 b/ssh.1
new file mode 100644
index 0000000..a6d76a9
--- /dev/null
+++ b/ssh.1
@@ -0,0 +1,966 @@
+.\"  -*- nroff -*-
+.\"
+.\" ssh.1.in
+.\"
+.\" Author: Tatu Ylonen <ylo@cs.hut.fi>
+.\"
+.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+.\"                    All rights reserved
+.\"
+.\" Created: Sat Apr 22 21:55:14 1995 ylo
+.\"
+.\" $Id: ssh.1,v 1.1 1999/10/27 03:42:45 damien Exp $
+.\"
+.Dd September 25, 1999
+.Dt SSH 1
+.Os
+.Sh NAME
+.Nm ssh
+.Nd OpenSSH secure shell client (remote login program)
+.Sh SYNOPSIS
+.Nm ssh
+.Op Fl l Ar login_name
+.Op Ar hostname | user@hostname
+.Op Ar command
+.Pp
+.Nm ssh
+.Op Fl afgknqtvxCPX
+.Op Fl c Ar blowfish | 3des
+.Op Fl e Ar escape_char
+.Op Fl i Ar identity_file
+.Op Fl l Ar login_name
+.Op Fl o Ar option
+.Op Fl p Ar port
+.Oo Fl L Xo
+.Sm off
+.Ar host :
+.Ar port :
+.Ar hostport
+.Sm on
+.Xc
+.Oc
+.Oo Fl R Xo
+.Sm off
+.Ar host :
+.Ar port :
+.Ar hostport
+.Sm on
+.Xc
+.Oc
+.Op Ar hostname | user@hostname
+.Op Ar command
+.Sh DESCRIPTION 
+.Nm
+(Secure Shell) is a program for logging into a remote machine and for
+executing commands on a remote machine.  It is intended to replace
+rlogin and rsh, and provide secure encrypted communications between
+two untrusted hosts over an insecure network.  X11 connections and
+arbitrary TCP/IP ports can also be forwarded over the secure channel.
+.Pp
+.Nm
+connects and logs into the specified 
+.Ar hostname .
+The user must prove
+his/her identity to the remote machine using one of several methods.
+.Pp
+First, if the machine the user logs in from is listed in
+.Pa /etc/hosts.equiv
+or
+.Pa /etc/shosts.equiv
+on the remote machine, and the user names are
+the same on both sides, the user is immediately permitted to log in.
+Second, if 
+.Pa \&.rhosts
+or
+.Pa \&.shosts
+exists in the user's home directory on the
+remote machine and contains a line containing the name of the client
+machine and the name of the user on that machine, the user is
+permitted to log in.  This form of authentication alone is normally not
+allowed by the server because it is not secure.
+.Pp
+The second (and primary) authentication method is the
+.Pa rhosts
+or
+.Pa hosts.equiv
+method combined with RSA-based host authentication.  It
+means that if the login would be permitted by
+.Pa \&.rhosts ,
+.Pa \&.shosts ,
+.Pa /etc/hosts.equiv ,
+or
+.Pa /etc/shosts.equiv ,
+and if additionally the server can verify the client's
+host key (see 
+.Pa /etc/ssh_known_hosts
+in the
+.Sx FILES
+section), only then login is
+permitted.  This authentication method closes security holes due to IP
+spoofing, DNS spoofing and routing spoofing.  [Note to the
+administrator:
+.Pa /etc/hosts.equiv ,
+.Pa \&.rhosts ,
+and the rlogin/rsh protocol in general, are inherently insecure and should be
+disabled if security is desired.]
+.Pp
+As a third authentication method, 
+.Nm
+supports RSA based authentication.
+The scheme is based on public-key cryptography: there are cryptosystems
+where encryption and decryption are done using separate keys, and it
+is not possible to derive the decryption key from the encryption key.
+RSA is one such system.  The idea is that each user creates a public/private 
+key pair for authentication purposes.  The
+server knows the public key, and only the user knows the private key.
+The file 
+.Pa $HOME/.ssh/authorized_keys
+lists the public keys that are permitted for logging
+in.  When the user logs in, the
+.Nm
+program tells the server which key pair it would like to use for
+authentication.  The server checks if this key is permitted, and if
+so, sends the user (actually the
+.Nm
+program running on behalf of the user) a challenge, a random number,
+encrypted by the user's public key.  The challenge can only be
+decrypted using the proper private key.  The user's client then decrypts the
+challenge using the private key, proving that he/she knows the private
+key but without disclosing it to the server.
+.Pp
+.Nm
+implements the RSA authentication protocol automatically.  The user
+creates his/her RSA key pair by running
+.Xr ssh-keygen 1 .
+This stores the private key in 
+.Pa \&.ssh/identity
+and the public key in
+.Pa \&.ssh/identity.pub
+in the user's home directory.  The user should then
+copy the 
+.Pa identity.pub
+to 
+.Pa \&.ssh/authorized_keys
+in his/her home directory on the remote machine (the 
+.Pa authorized_keys
+file corresponds to the conventional 
+.Pa \&.rhosts
+file, and has one key
+per line, though the lines can be very long).  After this, the user
+can log in without giving the password.  RSA authentication is much
+more secure than rhosts authentication.
+.Pp
+The most convenient way to use RSA authentication may be with an
+authentication agent.  See
+.Xr ssh-agent 1
+for more information.
+.Pp
+If other authentication methods fail, 
+.Nm
+prompts the user for a password.  The password is sent to the remote
+host for checking; however, since all communications are encrypted,
+the password cannot be seen by someone listening on the network.
+.Pp
+When the user's identity has been accepted by the server, the server
+either executes the given command, or logs into the machine and gives
+the user a normal shell on the remote machine.  All communication with
+the remote command or shell will be automatically encrypted.
+.Pp
+If a pseudo-terminal has been allocated (normal login session), the
+user can disconnect with
+.Ic ~. ,
+and suspend
+.Nm
+with
+.Ic ~^Z .
+All forwarded connections can be listed with
+.Ic ~# 
+and if
+the session blocks waiting for forwarded X11 or TCP/IP
+connections to terminate, it can be backgrounded with
+.Ic ~&
+(this should not be used while the user shell is active, as it can cause the
+shell to hang).  All available escapes can be listed with
+.Ic ~? .
+.Pp
+A single tilde character can be sent as
+.Ic ~~
+(or by following the tilde by a character other than those described above).
+The escape character must always follow a newline to be interpreted as
+special.  The escape character can be changed in configuration files
+or on the command line.  
+.Pp
+If no pseudo tty has been allocated, the
+session is transparent and can be used to reliably transfer binary
+data.  On most systems, setting the escape character to
+.Dq none
+will also make the session transparent even if a tty is used.
+.Pp
+The session terminates when the command or shell in on the remote
+machine exists and all X11 and TCP/IP connections have been closed.
+The exit status of the remote program is returned as the exit status
+of
+.Nm ssh .
+.Pp
+If the user is using X11 (the
+.Ev DISPLAY
+environment variable is set), the connection to the X11 display is
+automatically forwarded to the remote side in such a way that any X11
+programs started from the shell (or command) will go through the
+encrypted channel, and the connection to the real X server will be made
+from the local machine.  The user should not manually set
+.Ev DISPLAY .
+Forwarding of X11 connections can be
+configured on the command line or in configuration files.
+.Pp
+The
+.Ev DISPLAY 
+value set by
+.Nm
+will point to the server machine, but with a display number greater
+than zero.  This is normal, and happens because
+.Nm
+creates a
+.Dq proxy
+X server on the server machine for forwarding the
+connections over the encrypted channel.
+.Pp
+.Nm
+will also automatically set up Xauthority data on the server machine.
+For this purpose, it will generate a random authorization cookie,
+store it in Xauthority on the server, and verify that any forwarded
+connections carry this cookie and replace it by the real cookie when
+the connection is opened.  The real authentication cookie is never
+sent to the server machine (and no cookies are sent in the plain).
+.Pp
+If the user is using an authentication agent, the connection to the agent
+is automatically forwarded to the remote side unless disabled on
+command line or in a configuration file.
+.Pp
+Forwarding of arbitrary TCP/IP connections over the secure channel can
+be specified either on command line or in a configuration file.  One
+possible application of TCP/IP forwarding is a secure connection to an
+electronic purse; another is going trough firewalls.
+.Pp
+.Nm
+automatically maintains and checks a database containing RSA-based
+identifications for all hosts it has ever been used with.  The
+database is stored in 
+.Pa \&.ssh/known_hosts
+in the user's home directory.  Additionally, the file 
+.Pa /etc/ssh_known_hosts
+is automatically checked for known hosts.  Any new hosts are
+automatically added to the user's file.  If a host's identification
+ever changes,
+.Nm
+warns about this and disables password authentication to prevent a
+trojan horse from getting the user's password.  Another purpose of
+this mechanism is to prevent man-in-the-middle attacks which could
+otherwise be used to circumvent the encryption.  The
+.Cm StrictHostKeyChecking
+option (see below) can be used to prevent logins to machines whose
+host key is not known or has changed.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl a
+Disables forwarding of the authentication agent connection. This may
+also be specified on a per-host basis in the configuration file.
+.It Fl c Ar blowfish|3des
+Selects the cipher to use for encrypting the session. 
+.Ar 3des
+is used by default.  It is believed to be secure. 
+.Ar 3des
+(triple-des) is an encrypt-decrypt-encrypt triple with three different keys.
+It is presumably more secure than the
+.Ar des
+cipher which is no longer supported in ssh.
+.Ar blowfish
+is a fast block cipher, it appears very secure and is much faster than
+.Ar 3des .  
+.It Fl e Ar ch|^ch|none
+Sets the escape character for sessions with a pty (default:
+.Ql ~ ) .
+The escape character is only recognized at the beginning of a line.  The
+escape character followed by a dot
+.Pq Ql \&.
+closes the connection, followed
+by control-Z suspends the connection, and followed by itself sends the
+escape character once.  Setting the character to
+.Dq none
+disables any escapes and makes the session fully transparent.
+.It Fl f
+Requests
+.Nm
+to go to background after authentication.  This is useful
+if
+.Nm
+is going to ask for passwords or passphrases, but the user
+wants it in the background.  This implies 
+.Fl n .
+The recommended way to start X11 programs at a remote site is with
+something like
+.Ic ssh -f host xterm .
+.It Fl i Ar identity_file
+Selects the file from which the identity (private key) for 
+RSA authentication is read.  Default is 
+.Pa \&.ssh/identity
+in the user's home directory.  Identity files may also be specified on
+a per-host basis in the configuration file.  It is possible to have
+multiple
+.Fl i
+options (and multiple identities specified in
+configuration files).
+.It Fl g
+Allows remote hosts to connect to local forwarded ports.
+.It Fl k
+Disables forwarding of Kerberos tickets and AFS tokens. This may
+also be specified on a per-host basis in the configuration file.
+.It Fl l Ar login_name
+Specifies the user to log in as on the remote machine.  This may also
+be specified on a per-host basis in the configuration file.
+.It Fl n
+Redirects stdin from
+.Pa /dev/null
+(actually, prevents reading from stdin).
+This must be used when
+.Nm
+is run in the background.  A common trick is to use this to run X11
+programs in a remote machine.  For example,
+.Ic ssh -n shadows.cs.hut.fi emacs &
+will start an emacs on shadows.cs.hut.fi, and the X11
+connection will be automatically forwarded over an encrypted channel.
+The
+.Nm
+program will be put in the background.
+(This does not work if
+.Nm
+needs to ask for a password or passphrase; see also the
+.Fl f
+option.)
+.It Fl o Ar option
+Can be used to give options in the format used in the config file.
+This is useful for specifying options for which there is no separate
+command-line flag.  The option has the same format as a line in the
+configuration file.
+.It Fl p Ar port
+Port to connect to on the remote host.  This can be specified on a
+per-host basis in the configuration file.
+.It Fl P
+Use a non-privileged port for outgoing connections.
+This can be used if your firewall does
+not permit connections from privileged ports.
+Note that this option turns of
+.Cm RhostsAuthentication
+and
+.Cm RhostsRSAAuthentication .
+.It Fl q
+Quiet mode.  Causes all warning and diagnostic messages to be
+suppressed.  Only fatal errors are displayed.
+.It Fl t
+Force pseudo-tty allocation.  This can be used to execute arbitary
+screen-based programs on a remote machine, which can be very useful
+e.g. when implementing menu services.
+.It Fl v
+Verbose mode.  Causes
+.Nm
+to print debugging messages about its progress.  This is helpful in
+debugging connection, authentication, and configuration problems.
+The verbose mode is also used to display
+.Xr skey 1
+challenges, if the user entered "s/key" as password.
+.It Fl x
+Disables X11 forwarding.  This can also be specified on a per-host
+basis in a configuration file.
+.It Fl X
+Enables X11 forwarding.
+.It Fl C
+Requests compression of all data (including stdin, stdout, stderr, and
+data for forwarded X11 and TCP/IP connections).  The compression
+algorithm is the same used by gzip, and the
+.Dq level
+can be controlled by the
+.Cm CompressionLevel
+option (see below).  Compression is desirable on modem lines and other
+slow connections, but will only slow down things on fast networks.
+The default value can be set on a host-by-host basis in the
+configuration files; see the
+.Cm Compress
+option below.
+.It Fl L Ar port:host:hostport
+Specifies that the given port on the local (client) host is to be
+forwarded to the given host and port on the remote side.  This works
+by allocating a socket to listen to
+.Ar port
+on the local side, and whenever a connection is made to this port, the
+connection is forwarded over the secure channel, and a connection is
+made to
+.Ar host:hostport
+from the remote machine.  Port forwardings can also be specified in the
+configuration file.  Only root can forward privileged ports.
+.It Fl R Ar port:host:hostport
+Specifies that the given port on the remote (server) host is to be
+forwarded to the given host and port on the local side.  This works
+by allocating a socket to listen to
+.Ar port
+on the remote side, and whenever a connection is made to this port, the
+connection is forwarded over the secure channel, and a connection is
+made to
+.Ar host:hostport
+from the local machine.  Port forwardings can also be specified in the
+configuration file.  Privileged ports can be forwarded only when
+logging in as root on the remote machine.
+.El
+.Sh CONFIGURATION FILES
+.Nm
+obtains configuration data from the following sources (in this order):
+command line options, user's configuration file
+.Pq Pa $HOME/.ssh/config ,
+and system-wide configuration file
+.Pq Pa /etc/ssh_config .
+For each parameter, the first obtained value
+will be used.  The configuration files contain sections bracketed by
+"Host" specifications, and that section is only applied for hosts that
+match one of the patterns given in the specification.  The matched
+host name is the one given on the command line.
+.Pp
+Since the first obtained value for each parameter is used, more
+host-specific declarations should be given near the beginning of the
+file, and general defaults at the end.
+.Pp
+The configuration file has the following format:
+.Pp
+Empty lines and lines starting with
+.Ql #
+are comments.
+.Pp
+Otherwise a line is of the format
+.Dq keyword arguments .
+The possible
+keywords and their meanings are as follows (note that the
+configuration files are case-sensitive):
+.Bl -tag -width Ds
+.It Cm Host
+Restricts the following declarations (up to the next
+.Cm Host
+keyword) to be only for those hosts that match one of the patterns
+given after the keyword.
+.Ql \&*
+and
+.Ql ?
+can be used as wildcards in the
+patterns.  A single
+.Ql \&*
+as a pattern can be used to provide global
+defaults for all hosts.  The host is the
+.Ar hostname
+argument given on the command line (i.e., the name is not converted to
+a canonicalized host name before matching).
+.It Cm AFSTokenPassing
+Specifies whether to pass AFS tokens to remote host. The argument to 
+this keyword must be
+.Dq yes
+or
+.Dq no .
+.It Cm BatchMode
+If set to
+.Dq yes ,
+passphrase/password querying will be disabled.  This
+option is useful in scripts and other batch jobs where you have no
+user to supply the password.  The argument must be
+.Dq yes
+or
+.Dq no .
+.It Cm Cipher
+Specifies the cipher to use for encrypting the session.  Currently,
+.Dq blowfish ,
+and
+.Dq 3des
+are supported.  The default is
+.Dq 3des .
+.It Cm Compression
+Specifies whether to use compression.  The argument must be
+.Dq yes
+or
+.Dq no .
+.It Cm CompressionLevel
+Specifies the compression level to use if compression is enable.  The
+argument must be an integer from 1 (fast) to 9 (slow, best).  The
+default level is 6, which is good for most applications.  The meaning
+of the values is the same as in GNU GZIP.
+.It Cm ConnectionAttempts
+Specifies the number of tries (one per second) to make before falling
+back to rsh or exiting.  The argument must be an integer.  This may be
+useful in scripts if the connection sometimes fails.
+.It Cm EscapeChar
+Sets the escape character (default:
+.Ql ~ ) .
+The escape character can also
+be set on the command line.  The argument should be a single
+character,
+.Ql ^
+followed by a letter, or
+.Dq none
+to disable the escape
+character entirely (making the connection transparent for binary
+data).
+.It Cm FallBackToRsh 
+Specifies that if connecting via
+.Nm
+fails due to a connection refused error (there is no
+.Xr sshd 8
+listening on the remote host), 
+.Xr rsh 1
+should automatically be used instead (after a suitable warning about
+the session being unencrypted).  The argument must be
+.Dq yes
+or
+.Dq no .
+.It Cm ForwardAgent
+Specifies whether the connection to the authentication agent (if any)
+will be forwarded to the remote machine.  The argument must be
+.Dq yes
+or
+.Dq no .
+.It Cm ForwardX11
+Specifies whether X11 connections will be automatically redirected
+over the secure channel and 
+.Ev DISPLAY
+set.  The argument must be 
+.Dq yes
+or
+.Dq no .
+.It Cm GatewayPorts
+Specifies whether remote hosts are allowed to connect to local
+forwarded ports.
+The argument must be
+.Dq yes
+or
+.Dq no .
+The default is
+.Dq no .
+.It Cm GlobalKnownHostsFile
+Specifies a file to use instead of 
+.Pa /etc/ssh_known_hosts .
+.It Cm HostName
+Specifies the real host name to log into.  This can be used to specify
+nicnames or abbreviations for hosts.  Default is the name given on the
+command line.  Numeric IP addresses are also permitted (both on the
+command line and in
+.Cm HostName
+specifications).
+.It Cm IdentityFile
+Specifies the file from which the user's RSA authentication identity
+is read (default
+.Pa .ssh/identity
+in the user's home directory).
+Additionally, any identities represented by the authentication agent
+will be used for authentication.  The file name may use the tilde
+syntax to refer to a user's home directory.  It is possible to have
+multiple identity files specified in configuration files; all these
+identities will be tried in sequence.
+.It Cm KeepAlive
+Specifies whether the system should send keepalive messages to the
+other side.  If they are sent, death of the connection or crash of one
+of the machines will be properly noticed.  However, this means that
+connections will die if the route is down temporarily, and some people
+find it annoying.  
+.Pp
+The default is
+.Dq yes
+(to send keepalives), and the client will notice
+if the network goes down or the remote host dies.  This is important
+in scripts, and many users want it too.
+.Pp
+To disable keepalives, the value should be set to
+.Dq no
+in both the server and the client configuration files.
+.It Cm KerberosAuthentication
+Specifies whether Kerberos authentication will be used. The argument to 
+this keyword must be
+.Dq yes
+or
+.Dq no .
+.It Cm KerberosTgtPassing
+Specifies whether a Kerberos TGT will be forwarded to the server. This
+will only work if the Kerberos server is actually an AFS kaserver. The
+argument to this keyword must be
+.Dq yes
+or
+.Dq no .
+.It Cm LocalForward
+Specifies that a TCP/IP port on the local machine be forwarded over
+the secure channel to given host:port from the remote machine.  The
+first argument must be a port number, and the second must be
+host:port.  Multiple forwardings may be specified, and additional
+forwardings can be given on the command line.  Only the root can
+forward privileged ports.
+.It Cm PasswordAuthentication
+Specifies whether to use password authentication.  The argument to
+this keyword must be
+.Dq yes
+or
+.Dq no .
+.It Cm NumberOfPasswordPrompts
+Specifies the number of password prompts before giving up. The
+argument to this keyword must be an integer. Default is 3.
+.It Cm Port
+Specifies the port number to connect on the remote host.  Default is
+22.
+.It Cm ProxyCommand
+Specifies the command to use to connect to the server.  The command
+string extends to the end of the line, and is executed with /bin/sh.
+In the command string, %h will be substituted by the host name to
+connect and %p by the port.  The command can be basically anything,
+and should read from its stdin and write to its stdout.  It should
+eventually connect an
+.Xr sshd 8
+server running on some machine, or execute
+.Ic sshd -i
+somewhere.  Host key management will be done using the
+HostName of the host being connected (defaulting to the name typed by
+the user).
+.Pp
+.It Cm RemoteForward
+Specifies that a TCP/IP port on the remote machine be forwarded over
+the secure channel to given host:port from the local machine.  The
+first argument must be a port number, and the second must be
+host:port.  Multiple forwardings may be specified, and additional
+forwardings can be given on the command line.  Only the root can
+forward privileged ports.
+.It Cm RhostsAuthentication
+Specifies whether to try rhosts based authentication.  Note that this
+declaration only affects the client side and has no effect whatsoever
+on security.  Disabling rhosts authentication may reduce
+authentication time on slow connections when rhosts authentication is
+not used.  Most servers do not permit RhostsAuthentication because it
+is not secure (see RhostsRSAAuthentication).  The argument to this
+keyword must be
+.Dq yes
+or
+.Dq no .
+.It Cm RhostsRSAAuthentication
+Specifies whether to try rhosts based authentication with RSA host
+authentication.  This is the primary authentication method for most
+sites.  The argument must be
+.Dq yes
+or
+.Dq no .
+.It Cm RSAAuthentication
+Specifies whether to try RSA authentication.  The argument to this
+keyword must be
+.Dq yes
+or
+.Dq no .
+RSA authentication will only be
+attempted if the identity file exists, or an authentication agent is
+running.
+.It Cm CheckHostIP
+If this flag is set to
+.Dq yes ,
+ssh will additionally check the host ip address in the
+.Pa known_hosts
+file. This allows ssh to detect if a host key changed due to DNS spoofing.
+If the option is set to
+.Dq no ,
+the check will not be executed.
+.It Cm StrictHostKeyChecking
+If this flag is set to
+.Dq yes , 
+.Nm
+ssh will never automatically add host keys to the
+.Pa $HOME/.ssh/known_hosts
+file, and refuses to connect hosts whose host key has changed.  This
+provides maximum protection against trojan horse attacks.  However, it
+can be somewhat annoying if you don't have good
+.Pa /etc/ssh_known_hosts
+files installed and frequently
+connect new hosts.  Basically this option forces the user to manually
+add any new hosts.  Normally this option is disabled, and new hosts
+will automatically be added to the known host files.  The host keys of
+known hosts will be verified automatically in either case.  The
+argument must be
+.Dq yes
+or
+.Dq no .
+.It Cm User
+Specifies the user to log in as.  This can be useful if you have a
+different user name in different machines.  This saves the trouble of
+having to remember to give the user name on the command line.
+.It Cm UserKnownHostsFile
+Specifies a file to use instead of
+.Pa $HOME/.ssh/known_hosts .
+.It Cm UsePrivilegedPort
+Specifies whether to use a privileged port for outgoing connections.
+The argument must be
+.Dq yes
+or
+.Dq no .
+The default is
+.Dq yes .
+Note that setting this option to
+.Dq no
+turns of
+.Cm RhostsAuthentication
+and
+.Cm RhostsRSAAuthentication .
+.It Cm UseRsh
+Specifies that rlogin/rsh should be used for this host.  It is
+possible that the host does not at all support the
+.Nm
+protocol.  This causes
+.Nm
+to immediately exec 
+.Xr rsh 1 .
+All other options (except
+.Cm HostName )
+are ignored if this has been specified.  The argument must be
+.Dq yes
+or
+.Dq no .
+.Sh ENVIRONMENT
+.Nm
+will normally set the following environment variables:
+.Bl -tag -width Ds
+.It Ev DISPLAY
+The
+.Ev DISPLAY
+variable indicates the location of the X11 server.  It is
+automatically set by 
+.Nm
+to point to a value of the form
+.Dq hostname:n
+where hostname indicates
+the host where the shell runs, and n is an integer >= 1.  Ssh uses
+this special value to forward X11 connections over the secure
+channel.  The user should normally not set DISPLAY explicitly, as that
+will render the X11 connection insecure (and will require the user to
+manually copy any required authorization cookies).
+.It Ev HOME
+Set to the path of the user's home directory.
+.It Ev LOGNAME
+Synonym for
+.Ev USER ;
+set for compatibility with systems that use this variable.
+.It Ev MAIL
+Set to point the user's mailbox.
+.It Ev  PATH
+Set to the default
+.Ev PATH ,
+as specified when compiling
+.Nm ssh .
+.It Ev SSH_AUTH_SOCK
+indicates the path of a unix-domain socket used to communicate with the
+agent.
+.It Ev SSH_CLIENT
+Identifies the client end of the connection.  The variable contains
+three space-separated values: client ip-address, client port number,
+and server port number.
+.It Ev SSH_TTY
+This is set to the name of the tty (path to the device) associated
+with the current shell or command.  If the current session has no tty,
+this variable is not set.
+.It Ev TZ
+The timezone variable is set to indicate the present timezone if it
+was set when the daemon was started (e.i., the daemon passes the value
+on to new connections).
+.It Ev USER
+Set to the name of the user logging in.
+.El
+.Pp
+Additionally, 
+.Nm
+reads 
+.Pa $HOME/.ssh/environment , 
+and adds lines of the format
+.Dq VARNAME=value
+to the environment.
+.Sh FILES
+.Bl -tag -width $HOME/.ssh/known_hosts
+.It Pa $HOME/.ssh/known_hosts
+Records host keys for all hosts the user has logged into (that are not
+in
+.Pa /etc/ssh_known_hosts ) .
+See
+.Xr sshd 8 .
+.It Pa $HOME/.ssh/random_seed
+Used for seeding the random number generator.  This file contains
+sensitive data and should read/write for the user and not accessible
+for others.  This file is created the first time the program is run
+and updated automatically.  The user should never need to read or
+modify this file.
+.It Pa $HOME/.ssh/identity
+Contains the RSA authentication identity of the user.  This file
+contains sensitive data and should be readable by the user but not
+accessible by others (read/write/execute).
+Note that
+.Nm
+ignores this file if it is accessible by others.
+It is possible to specify a passphrase when
+generating the key; the passphrase will be used to encrypt the
+sensitive part of this file using 3DES.
+.It Pa $HOME/.ssh/identity.pub 
+Contains the public key for authentication (public part of the
+identity file in human-readable form).  The contents of this file
+should be added to
+.Pa $HOME/.ssh/authorized_keys
+on all machines
+where you wish to log in using RSA authentication.  This file is not
+sensitive and can (but need not) be readable by anyone.  This file is
+never used automatically and is not necessary; it is only provided for
+the convenience of the user.
+.It Pa $HOME/.ssh/config
+This is the per-user configuration file.  The format of this file is
+described above.  This file is used by the
+.Nm
+client.  This file does not usually contain any sensitive information,
+but the recommended permissions are read/write for the user, and not
+accessible by others.
+.It Pa $HOME/.ssh/authorized_keys
+Lists the RSA keys that can be used for logging in as this user.  The
+format of this file is described in the
+.Xr sshd 8
+manual page.  In the simplest form the format is the same as the .pub
+identity files (that is, each line contains the number of bits in
+modulus, public exponent, modulus, and comment fields, separated by
+spaces).  This file is not highly sensitive, but the recommended
+permissions are read/write for the user, and not accessible by others.
+.It Pa /etc/ssh_known_hosts
+Systemwide list of known host keys.  This file should be prepared by the
+system administrator to contain the public host keys of all machines in the
+organization.  This file should be world-readable.  This file contains
+public keys, one per line, in the following format (fields separated
+by spaces): system name, number of bits in modulus, public exponent,
+modulus, and optional comment field.  When different names are used
+for the same machine, all such names should be listed, separated by
+commas.  The format is described on the
+.Xr sshd 8
+manual page.
+.Pp
+The canonical system name (as returned by name servers) is used by
+.Xr sshd 8
+to verify the client host when logging in; other names are needed because
+.Nm
+does not convert the user-supplied name to a canonical name before
+checking the key, because someone with access to the name servers
+would then be able to fool host authentication.
+.It Pa /etc/ssh_config
+Systemwide configuration file.  This file provides defaults for those
+values that are not specified in the user's configuration file, and
+for those users who do not have a configuration file.  This file must
+be world-readable.
+.It Pa $HOME/.rhosts
+This file is used in
+.Pa \&.rhosts
+authentication to list the
+host/user pairs that are permitted to log in.  (Note that this file is
+also used by rlogin and rsh, which makes using this file insecure.)
+Each line of the file contains a host name (in the canonical form
+returned by name servers), and then a user name on that host,
+separated by a space.  One some machines this file may need to be
+world-readable if the user's home directory is on a NFS partition,
+because
+.Xr sshd 8
+reads it as root.  Additionally, this file must be owned by the user,
+and must not have write permissions for anyone else.  The recommended
+permission for most machines is read/write for the user, and not
+accessible by others.
+.Pp
+Note that by default
+.Xr sshd 8
+will be installed so that it requires successful RSA host
+authentication before permitting \s+2.\s0rhosts authentication.  If your
+server machine does not have the client's host key in
+.Pa /etc/ssh_known_hosts ,
+you can store it in
+.Pa $HOME/.ssh/known_hosts .
+The easiest way to do this is to
+connect back to the client from the server machine using ssh; this
+will automatically add the host key inxi
+.Pa $HOME/.ssh/known_hosts .
+.It Pa $HOME/.shosts
+This file is used exactly the same way as
+.Pa \&.rhosts .
+The purpose for
+having this file is to be able to use rhosts authentication with
+.Nm
+without permitting login with
+.Xr rlogin 1
+or
+.Xr rsh 1 .
+.It Pa /etc/hosts.equiv
+This file is used during
+.Pa \&.rhosts authentication.  It contains
+canonical hosts names, one per line (the full format is described on
+the
+.Xr sshd 8
+manual page).  If the client host is found in this file, login is
+automatically permitted provided client and server user names are the
+same.  Additionally, successful RSA host authentication is normally
+required.  This file should only be writable by root.
+.It Pa /etc/shosts.equiv
+This file is processed exactly as 
+.Pa /etc/hosts.equiv .
+This file may be useful to permit logins using
+.Nm
+but not using rsh/rlogin.
+.It Pa /etc/sshrc
+Commands in this file are executed by
+.Nm
+when the user logs in just before the user's shell (or command) is started.
+See the
+.Xr sshd 8
+manual page for more information.
+.It Pa $HOME/.ssh/rc
+Commands in this file are executed by
+.Nm
+when the user logs in just before the user's shell (or command) is
+started.
+See the 
+.Xr sshd 8
+manual page for more information.
+.It Pa libcrypto.so.X.1
+A version of this library which includes support for the RSA algorithm
+is required for proper operation.
+.Sh AUTHOR
+Tatu Ylonen <ylo@cs.hut.fi>
+.Pp
+Issues can be found from the SSH WWW home page:
+.Pp
+.Dl http://www.cs.hut.fi/ssh
+.Pp
+OpenSSH
+is a derivative of the original (free) ssh 1.2.12 release, but with bugs
+removed and newer features re-added.   Rapidly after the 1.2.12 release,
+newer versions bore successively more restrictive licenses.  This version
+of OpenSSH
+.Bl -bullet
+.It
+has all components of a restrictive nature (ie. patents, see
+.Xr ssl 8 )
+directly removed from the source code; any licensed or patented components
+are chosen from
+external libraries.
+.It
+has been updated to support ssh protocol 1.5.
+.It
+contains added support for 
+.Xr kerberos 8
+authentication and ticket passing.
+.It
+supports one-time password authentication with
+.Xr skey 1 .
+.El
+.Pp
+The libraries described in
+.Xr ssl 8
+are required for proper operation.
+.Sh SEE ALSO
+.Xr rlogin 1 ,
+.Xr rsh 1 ,
+.Xr scp 1 ,
+.Xr ssh-add 1 ,
+.Xr ssh-agent 1 ,
+.Xr ssh-keygen 1 ,
+.Xr telnet 1 ,
+.Xr sshd 8 ,
+.Xr ssl 8
diff --git a/ssh.c b/ssh.c
new file mode 100644
index 0000000..c5a92b5
--- /dev/null
+++ b/ssh.c
@@ -0,0 +1,809 @@
+/*
+
+ssh.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Sat Mar 18 16:36:11 1995 ylo
+
+Ssh client program.  This program can be used to log into a remote machine.
+The software supports strong authentication, encryption, and forwarding
+of X11, TCP/IP, and authentication connections.
+
+Modified to work with SSL by Niels Provos <provos@citi.umich.edu> in Canada.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: ssh.c,v 1.1 1999/10/27 03:42:45 damien Exp $");
+
+#include "xmalloc.h"
+#include "ssh.h"
+#include "packet.h"
+#include "buffer.h"
+#include "authfd.h"
+#include "readconf.h"
+#include "uidswap.h"
+
+/* Flag indicating whether debug mode is on.  This can be set on the
+   command line. */
+int debug_flag = 0;
+
+/* Flag indicating whether quiet mode is on. */
+int quiet_flag = 0;
+
+/* Flag indicating whether to allocate a pseudo tty.  This can be set on the
+   command line, and is automatically set if no command is given on the command
+   line. */
+int tty_flag = 0;
+
+/* Flag indicating that nothing should be read from stdin.  This can be set
+   on the command line. */
+int stdin_null_flag = 0;
+
+/* Flag indicating that ssh should fork after authentication.  This is useful
+   so that the pasphrase can be entered manually, and then ssh goes to the
+   background. */
+int fork_after_authentication_flag = 0;
+
+/* General data structure for command line options and options configurable
+   in configuration files.  See readconf.h. */
+Options options;
+
+/* Name of the host we are connecting to.  This is the name given on the
+   command line, or the HostName specified for the user-supplied name
+   in a configuration file. */
+char *host;
+
+/* socket address the host resolves to */
+struct sockaddr_in hostaddr;
+
+/* Flag to indicate that we have received a window change signal which has
+   not yet been processed.  This will cause a message indicating the new
+   window size to be sent to the server a little later.  This is volatile
+   because this is updated in a signal handler. */
+volatile int received_window_change_signal = 0;
+
+/* Value of argv[0] (set in the main program). */
+char *av0;
+
+/* Flag indicating whether we have a valid host private key loaded. */
+int host_private_key_loaded = 0;
+
+/* Host private key. */
+RSA *host_private_key = NULL;
+
+/* Original real UID. */
+uid_t original_real_uid;
+
+/* Prints a help message to the user.  This function never returns. */
+
+void
+usage()
+{
+  fprintf(stderr, "Usage: %s [options] host [command]\n", av0);
+  fprintf(stderr, "Options:\n");
+  fprintf(stderr, "  -l user     Log in using this user name.\n");
+  fprintf(stderr, "  -n          Redirect input from /dev/null.\n");
+  fprintf(stderr, "  -a          Disable authentication agent forwarding.\n");
+#ifdef AFS
+  fprintf(stderr, "  -k          Disable Kerberos ticket and AFS token forwarding.\n");
+#endif /* AFS */
+  fprintf(stderr, "  -x          Disable X11 connection forwarding.\n");
+  fprintf(stderr, "  -i file     Identity for RSA authentication (default: ~/.ssh/identity).\n");
+  fprintf(stderr, "  -t          Tty; allocate a tty even if command is given.\n");
+  fprintf(stderr, "  -v          Verbose; display verbose debugging messages.\n");
+  fprintf(stderr, "  -V          Display version number only.\n");
+  fprintf(stderr, "  -P          Don't allocate a privileged port.\n");
+  fprintf(stderr, "  -q          Quiet; don't display any warning messages.\n");
+  fprintf(stderr, "  -f          Fork into background after authentication.\n");
+  fprintf(stderr, "  -e char     Set escape character; ``none'' = disable (default: ~).\n");
+
+  fprintf(stderr, "  -c cipher   Select encryption algorithm: "
+		  "``3des'', "
+		  "``blowfish''\n");
+  fprintf(stderr, "  -p port     Connect to this port.  Server must be on the same port.\n");
+  fprintf(stderr, "  -L listen-port:host:port   Forward local port to remote address\n");
+  fprintf(stderr, "  -R listen-port:host:port   Forward remote port to local address\n");
+  fprintf(stderr, "              These cause %s to listen for connections on a port, and\n", av0);
+  fprintf(stderr, "              forward them to the other side by connecting to host:port.\n");
+  fprintf(stderr, "  -C          Enable compression.\n");
+  fprintf(stderr, "  -g          Allow remote hosts to connect to forwarded ports.\n");
+  fprintf(stderr, "  -o 'option' Process the option as if it was read from a configuration file.\n");
+  exit(1);
+}
+
+/* Connects to the given host using rsh (or prints an error message and exits
+   if rsh is not available).  This function never returns. */
+
+void
+rsh_connect(char *host, char *user, Buffer *command)
+{
+  char *args[10];
+  int i;
+  
+  log("Using rsh.  WARNING: Connection will not be encrypted.");
+  /* Build argument list for rsh. */
+  i = 0;
+  args[i++] = _PATH_RSH;
+  args[i++] = host;    /* may have to come after user on some systems */
+  if (user)
+    {
+      args[i++] = "-l";
+      args[i++] = user;
+    }
+  if (buffer_len(command) > 0)
+    {
+      buffer_append(command, "\0", 1);
+      args[i++] = buffer_ptr(command);
+    }
+  args[i++] = NULL;
+  if (debug_flag)
+    {
+      for (i = 0; args[i]; i++)
+	{
+	  if (i != 0)
+	    fprintf(stderr, " ");
+	  fprintf(stderr, "%s", args[i]);
+	}
+      fprintf(stderr, "\n");
+    }
+  execv(_PATH_RSH, args);
+  perror(_PATH_RSH);
+  exit(1);
+}
+
+/* Main program for the ssh client. */
+
+uid_t original_real_uid;
+
+int
+main(int ac, char **av)
+{
+  int i, opt, optind, type, exit_status, ok, fwd_port, fwd_host_port, authfd;
+  char *optarg, *cp, buf[256];
+  Buffer command;
+  struct winsize ws;
+  struct stat st;
+  struct passwd *pw, pwcopy;
+  int interactive = 0, dummy;
+  uid_t original_effective_uid;
+  int plen;
+
+  /* Save the original real uid.  It will be needed later (uid-swapping may
+     clobber the real uid).  */
+  original_real_uid = getuid();
+  original_effective_uid = geteuid();
+
+  /* If we are installed setuid root be careful to not drop core. */
+  if (original_real_uid != original_effective_uid)
+    {
+      struct rlimit rlim;
+      rlim.rlim_cur = rlim.rlim_max = 0;
+      if (setrlimit(RLIMIT_CORE, &rlim) < 0)
+	fatal("setrlimit failed: %.100s", strerror(errno));
+    }
+
+  /* Use uid-swapping to give up root privileges for the duration of option
+     processing.  We will re-instantiate the rights when we are ready to
+     create the privileged port, and will permanently drop them when the
+     port has been created (actually, when the connection has been made, as
+     we may need to create the port several times). */
+  temporarily_use_uid(original_real_uid);
+
+  /* Set our umask to something reasonable, as some files are created with 
+     the default umask.  This will make them world-readable but writable 
+     only by the owner, which is ok for all files for which we don't set
+     the modes explicitly. */
+  umask(022);
+  
+  /* Save our own name. */
+  av0 = av[0];
+
+  /* Initialize option structure to indicate that no values have been set. */
+  initialize_options(&options);
+
+  /* Parse command-line arguments. */
+  host = NULL;
+
+  /* If program name is not one of the standard names, use it as host name. */
+  if (strchr(av0, '/'))
+    cp = strrchr(av0, '/') + 1;
+  else
+    cp = av0;
+  if (strcmp(cp, "rsh") != 0 && strcmp(cp, "ssh") != 0 &&
+      strcmp(cp, "rlogin") != 0 && strcmp(cp, "slogin") != 0)
+    host = cp;
+  
+  for (optind = 1; optind < ac; optind++)
+    {
+      if (av[optind][0] != '-')
+	{
+	  if (host)
+	    break;
+          if ((cp = strchr(av[optind], '@'))) {
+            options.user = av[optind];
+            *cp = '\0';
+            host = ++cp;
+          }
+          else
+	    host = av[optind];
+	  continue;
+	}
+      opt = av[optind][1];
+      if (!opt)
+	usage();
+      if (strchr("eilcpLRo", opt)) /* options with arguments */
+	{
+	  optarg = av[optind] + 2;
+	  if (strcmp(optarg, "") == 0)
+	    {
+	      if (optind >= ac - 1)
+		usage();
+	      optarg = av[++optind];
+	    }
+	}
+      else
+	{
+	  if (av[optind][2])
+	    usage();
+	  optarg = NULL;
+	}
+      switch (opt)
+	{
+	case 'n':
+	  stdin_null_flag = 1;
+	  break;
+
+	case 'f':
+	  fork_after_authentication_flag = 1;
+	  stdin_null_flag = 1;
+	  break;
+
+	case 'x':
+	  options.forward_x11 = 0;
+	  break;
+
+	case 'X':
+	  options.forward_x11 = 1;
+	  break;
+
+	case 'g':
+	  options.gateway_ports = 1;
+	  break;
+
+	case 'P':
+	  options.use_privileged_port = 0;
+	  break;
+
+	case 'a':
+	  options.forward_agent = 0;
+	  break;
+#ifdef AFS
+	case 'k':
+	  options.kerberos_tgt_passing = 0;
+	  options.afs_token_passing = 0;
+	  break;
+#endif
+	case 'i':
+	  if (stat(optarg, &st) < 0)
+	    {
+	      fprintf(stderr, "Warning: Identity file %s does not exist.\n",
+		      optarg);
+	      break;
+	    }
+	  if (options.num_identity_files >= SSH_MAX_IDENTITY_FILES)
+	    fatal("Too many identity files specified (max %d)",
+		  SSH_MAX_IDENTITY_FILES);
+	  options.identity_files[options.num_identity_files++] = 
+	    xstrdup(optarg);
+	  break;
+
+	case 't':
+	  tty_flag = 1;
+	  break;
+
+	case 'v':
+	case 'V':
+	  debug_flag = 1;
+	  fprintf(stderr, "SSH Version %s, protocol version %d.%d.\n",
+		  SSH_VERSION, PROTOCOL_MAJOR, PROTOCOL_MINOR);
+	  fprintf(stderr, "Compiled with SSL.\n");
+	  if (opt == 'V')
+	    exit(0);
+	  break;
+
+	case 'q':
+	  quiet_flag = 1;
+	  break;
+
+	case 'e':
+	  if (optarg[0] == '^' && optarg[2] == 0 &&
+	      (unsigned char)optarg[1] >= 64 && (unsigned char)optarg[1] < 128)
+	    options.escape_char = (unsigned char)optarg[1] & 31;
+	  else
+	    if (strlen(optarg) == 1)
+	      options.escape_char = (unsigned char)optarg[0];
+	    else
+	      if (strcmp(optarg, "none") == 0)
+		options.escape_char = -2;
+	      else
+		{
+		  fprintf(stderr, "Bad escape character '%s'.\n", optarg);
+		  exit(1);
+		}
+	  break;
+
+	case 'c':
+	  options.cipher = cipher_number(optarg);
+	  if (options.cipher == -1)
+	    {
+	      fprintf(stderr, "Unknown cipher type '%s'\n", optarg);
+	      exit(1);
+	    }
+	  break;
+
+	case 'p':
+	  options.port = atoi(optarg);
+	  if (options.port < 1 || options.port > 65535)
+	    {
+	      fprintf(stderr, "Bad port %s.\n", optarg);
+	      exit(1);
+	    }
+	  break;
+
+	case 'l':
+	  options.user = optarg;
+	  break;
+
+	case 'R':
+	  if (sscanf(optarg, "%d:%255[^:]:%d", &fwd_port, buf, 
+		     &fwd_host_port) != 3)
+	    {
+	      fprintf(stderr, "Bad forwarding specification '%s'.\n", optarg);
+	      usage();
+	      /*NOTREACHED*/
+	    }
+	  add_remote_forward(&options, fwd_port, buf, fwd_host_port);
+	  break;
+
+	case 'L':
+	  if (sscanf(optarg, "%d:%255[^:]:%d", &fwd_port, buf, 
+		     &fwd_host_port) != 3)
+	    {
+	      fprintf(stderr, "Bad forwarding specification '%s'.\n", optarg);
+	      usage();
+	      /*NOTREACHED*/
+	    }
+	  add_local_forward(&options, fwd_port, buf, fwd_host_port);
+	  break;
+
+	case 'C':
+	  options.compression = 1;
+	  break;
+
+	case 'o':
+	  dummy = 1;
+	  process_config_line(&options, host ? host : "", optarg,
+			      "command-line", 0, &dummy);
+	  break;
+
+	default:
+	  usage();
+	}
+    }
+
+ /* Check that we got a host name. */
+  if (!host)
+    usage();
+
+  /* check if RSA support exists */
+  if (rsa_alive() == 0) {
+    extern char *__progname;
+
+    fprintf(stderr,
+      "%s: no RSA support in libssl and libcrypto.  See ssl(8).\n",
+      __progname);
+    exit(1);
+  }
+
+  /* Initialize the command to execute on remote host. */
+  buffer_init(&command);
+
+  /* Save the command to execute on the remote host in a buffer.  There is
+     no limit on the length of the command, except by the maximum packet
+     size.  Also sets the tty flag if there is no command. */
+  if (optind == ac)
+    {
+      /* No command specified - execute shell on a tty. */
+      tty_flag = 1;
+    }
+  else
+    {
+      /* A command has been specified.  Store it into the buffer. */
+      for (i = optind; i < ac; i++)
+	{
+	  if (i > optind)
+	    buffer_append(&command, " ", 1);
+	  buffer_append(&command, av[i], strlen(av[i]));
+	}
+    }
+
+  /* Cannot fork to background if no command. */
+  if (fork_after_authentication_flag && buffer_len(&command) == 0)
+    fatal("Cannot fork into background without a command to execute.");
+  
+  /* Allocate a tty by default if no command specified. */
+  if (buffer_len(&command) == 0)
+    tty_flag = 1;
+
+  /* Do not allocate a tty if stdin is not a tty. */
+  if (!isatty(fileno(stdin)))
+    {
+      if (tty_flag)
+	fprintf(stderr, "Pseudo-terminal will not be allocated because stdin is not a terminal.\n");
+      tty_flag = 0;
+    }
+
+  /* Get user data. */
+  pw = getpwuid(original_real_uid);
+  if (!pw)
+    {
+      fprintf(stderr, "You don't exist, go away!\n");
+      exit(1);
+    }
+  
+  /* Take a copy of the returned structure. */
+  memset(&pwcopy, 0, sizeof(pwcopy));
+  pwcopy.pw_name = xstrdup(pw->pw_name);
+  pwcopy.pw_passwd = xstrdup(pw->pw_passwd);
+  pwcopy.pw_uid = pw->pw_uid;
+  pwcopy.pw_gid = pw->pw_gid;
+  pwcopy.pw_dir = xstrdup(pw->pw_dir);
+  pwcopy.pw_shell = xstrdup(pw->pw_shell);
+  pw = &pwcopy;
+
+  /* Initialize "log" output.  Since we are the client all output actually
+     goes to the terminal. */
+  log_init(av[0], 1, debug_flag, quiet_flag, SYSLOG_FACILITY_USER);
+
+  /* Read per-user configuration file. */
+  snprintf(buf, sizeof buf, "%.100s/%.100s", pw->pw_dir, SSH_USER_CONFFILE);
+  read_config_file(buf, host, &options);
+
+  /* Read systemwide configuration file. */
+  read_config_file(HOST_CONFIG_FILE, host, &options);
+
+  /* Fill configuration defaults. */
+  fill_default_options(&options);
+  if (options.user == NULL)
+    options.user = xstrdup(pw->pw_name);
+
+  if (options.hostname != NULL)
+    host = options.hostname;
+
+  /* Find canonic host name. */
+  if (strchr(host, '.') == 0)
+    {
+      struct hostent *hp = gethostbyname(host);
+      if (hp != 0)
+	{
+	  if (strchr(hp->h_name, '.') != 0)
+	    host = xstrdup(hp->h_name);
+	  else if (hp->h_aliases != 0
+		   && hp->h_aliases[0] != 0
+		   && strchr(hp->h_aliases[0], '.') != 0)
+	    host = xstrdup(hp->h_aliases[0]);
+	}
+    }
+
+  /* Disable rhosts authentication if not running as root. */
+  if (original_effective_uid != 0)
+    {
+      options.rhosts_authentication = 0;
+      options.rhosts_rsa_authentication = 0;
+    }
+
+  /* If using rsh has been selected, exec it now (without trying anything
+     else).  Note that we must release privileges first. */
+  if (options.use_rsh)
+    {
+      /* Restore our superuser privileges.  This must be done before
+         permanently setting the uid. */
+      restore_uid();
+
+      /* Switch to the original uid permanently. */
+      permanently_set_uid(original_real_uid);
+
+      /* Execute rsh. */
+      rsh_connect(host, options.user, &command);
+      fatal("rsh_connect returned");
+    }
+
+  /* Restore our superuser privileges. */
+  restore_uid();
+
+  /* Open a connection to the remote host.  This needs root privileges if
+     rhosts_{rsa_}authentication is true. */
+
+  if (!options.use_privileged_port)
+    {
+       options.rhosts_authentication = 0;
+       options.rhosts_rsa_authentication = 0;
+    }
+
+  ok = ssh_connect(host, &hostaddr, options.port, options.connection_attempts,
+		   !options.rhosts_authentication &&
+		   !options.rhosts_rsa_authentication,
+		   original_real_uid, options.proxy_command);
+
+  /* If we successfully made the connection, load the host private key in
+     case we will need it later for combined rsa-rhosts authentication. 
+     This must be done before releasing extra privileges, because the file
+     is only readable by root. */
+  if (ok)
+    {
+      host_private_key = RSA_new();
+      if (load_private_key(HOST_KEY_FILE, "", host_private_key, NULL))
+	host_private_key_loaded = 1;
+    }
+
+  /* Get rid of any extra privileges that we may have.  We will no longer need
+     them.  Also, extra privileges could make it very hard to read identity
+     files and other non-world-readable files from the user's home directory
+     if it happens to be on a NFS volume where root is mapped to nobody. */
+  permanently_set_uid(original_real_uid);
+
+  /* Now that we are back to our own permissions, create ~/.ssh directory
+     if it doesn\'t already exist. */
+  snprintf(buf, sizeof buf, "%.100s/%.100s", pw->pw_dir, SSH_USER_DIR);
+  if (stat(buf, &st) < 0)
+    if (mkdir(buf, 0755) < 0)
+      error("Could not create directory '%.200s'.", buf);
+
+  /* Check if the connection failed, and try "rsh" if appropriate. */
+  if (!ok)
+    {
+      if (options.port != 0)
+	log("Secure connection to %.100s on port %d refused%.100s.", 
+	    host, options.port,
+	    options.fallback_to_rsh ? "; reverting to insecure method" : "");
+      else
+	log("Secure connection to %.100s refused%.100s.", host,
+	    options.fallback_to_rsh ? "; reverting to insecure method" : "");
+
+      if (options.fallback_to_rsh)
+	{
+	  rsh_connect(host, options.user, &command);
+	  fatal("rsh_connect returned");
+	}
+      exit(1);
+    }
+
+  /* Expand ~ in options.identity_files. */
+  for (i = 0; i < options.num_identity_files; i++)
+    options.identity_files[i] = 
+      tilde_expand_filename(options.identity_files[i], original_real_uid);
+
+  /* Expand ~ in known host file names. */
+  options.system_hostfile = tilde_expand_filename(options.system_hostfile,
+						  original_real_uid);
+  options.user_hostfile = tilde_expand_filename(options.user_hostfile,
+						original_real_uid);
+
+  /* Log into the remote system.  This never returns if the login fails. */
+  ssh_login(host_private_key_loaded, host_private_key, 
+	    host, &hostaddr, &options, original_real_uid);
+
+  /* We no longer need the host private key.  Clear it now. */
+  if (host_private_key_loaded)
+    RSA_free(host_private_key); /* Destroys contents safely */
+
+  /* Close connection cleanly after attack. */
+  cipher_attack_detected = packet_disconnect;
+
+  /* If requested, fork and let ssh continue in the background. */
+  if (fork_after_authentication_flag)
+    {
+      int ret = fork();
+      if (ret == -1)
+	fatal("fork failed: %.100s", strerror(errno));
+      if (ret != 0)
+	exit(0);
+      setsid();
+    }
+
+  /* Enable compression if requested. */
+  if (options.compression)
+    {
+      debug("Requesting compression at level %d.", options.compression_level);
+
+      if (options.compression_level < 1 || options.compression_level > 9)
+	fatal("Compression level must be from 1 (fast) to 9 (slow, best).");
+
+      /* Send the request. */
+      packet_start(SSH_CMSG_REQUEST_COMPRESSION);
+      packet_put_int(options.compression_level);
+      packet_send();
+      packet_write_wait();
+      type = packet_read(&plen);
+      if (type == SSH_SMSG_SUCCESS)
+	packet_start_compression(options.compression_level);
+      else if (type == SSH_SMSG_FAILURE)
+	log("Warning: Remote host refused compression.");
+      else
+	packet_disconnect("Protocol error waiting for compression response.");
+    }
+
+  /* Allocate a pseudo tty if appropriate. */
+  if (tty_flag)
+    {
+      debug("Requesting pty.");
+
+      /* Start the packet. */
+      packet_start(SSH_CMSG_REQUEST_PTY);
+
+      /* Store TERM in the packet.  There is no limit on the length of the
+         string. */
+      cp = getenv("TERM");
+      if (!cp)
+	cp = "";
+      packet_put_string(cp, strlen(cp));
+
+      /* Store window size in the packet. */
+      if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0)
+	memset(&ws, 0, sizeof(ws));
+      packet_put_int(ws.ws_row);
+      packet_put_int(ws.ws_col);
+      packet_put_int(ws.ws_xpixel);
+      packet_put_int(ws.ws_ypixel);
+      
+      /* Store tty modes in the packet. */
+      tty_make_modes(fileno(stdin));
+
+      /* Send the packet, and wait for it to leave. */
+      packet_send();
+      packet_write_wait();
+
+      /* Read response from the server. */
+      type = packet_read(&plen);
+      if (type == SSH_SMSG_SUCCESS)
+	interactive = 1;
+      else if (type == SSH_SMSG_FAILURE)
+	log("Warning: Remote host failed or refused to allocate a pseudo tty.");
+      else
+	packet_disconnect("Protocol error waiting for pty request response.");
+    }
+
+  /* Request X11 forwarding if enabled and DISPLAY is set. */
+  if (options.forward_x11 && getenv("DISPLAY") != NULL)
+    {
+      char line[512], proto[512], data[512];
+      FILE *f;
+      int forwarded = 0, got_data = 0, i;
+
+#ifdef XAUTH_PATH
+      /* Try to get Xauthority information for the display. */
+      snprintf(line, sizeof line, "%.100s list %.200s 2>/dev/null", 
+	      XAUTH_PATH, getenv("DISPLAY"));
+      f = popen(line, "r");
+      if (f && fgets(line, sizeof(line), f) && 
+	  sscanf(line, "%*s %s %s", proto, data) == 2)
+	got_data = 1;
+      if (f)
+	pclose(f);
+#endif /* XAUTH_PATH */
+      /* If we didn't get authentication data, just make up some data.  The
+	 forwarding code will check the validity of the response anyway, and
+	 substitute this data.  The X11 server, however, will ignore this
+	 fake data and use whatever authentication mechanisms it was using
+	 otherwise for the local connection. */
+      if (!got_data)
+	{
+          u_int32_t rand = 0;
+
+	  strlcpy(proto, "MIT-MAGIC-COOKIE-1", sizeof proto);
+          for (i = 0; i < 16; i++) {
+            if (i % 4 == 0)
+              rand = arc4random();
+            snprintf(data + 2 * i, sizeof data - 2 * i, "%02x", rand & 0xff);
+            rand >>= 8;
+          }
+	}
+
+      /* Got local authentication reasonable information.  Request forwarding
+	 with authentication spoofing. */
+      debug("Requesting X11 forwarding with authentication spoofing.");
+      x11_request_forwarding_with_spoofing(proto, data);
+
+      /* Read response from the server. */
+      type = packet_read(&plen);
+      if (type == SSH_SMSG_SUCCESS)
+	{
+	  forwarded = 1;
+	  interactive = 1;
+	}
+      else if (type == SSH_SMSG_FAILURE)
+	log("Warning: Remote host denied X11 forwarding.");
+      else
+	packet_disconnect("Protocol error waiting for X11 forwarding");
+    }
+
+  /* Tell the packet module whether this is an interactive session. */
+  packet_set_interactive(interactive, options.keepalives);
+
+  /* Clear agent forwarding if we don\'t have an agent. */
+  authfd = ssh_get_authentication_socket();
+  if (authfd < 0)
+    options.forward_agent = 0;
+  else
+    ssh_close_authentication_socket(authfd);
+
+  /* Request authentication agent forwarding if appropriate. */
+  if (options.forward_agent)
+    {
+      debug("Requesting authentication agent forwarding.");
+      auth_request_forwarding();
+      
+      /* Read response from the server. */
+      type = packet_read(&plen);
+      packet_integrity_check(plen, 0, type);
+      if (type != SSH_SMSG_SUCCESS)
+	log("Warning: Remote host denied authentication agent forwarding.");
+    }
+
+  /* Initiate local TCP/IP port forwardings. */
+  for (i = 0; i < options.num_local_forwards; i++)
+    {
+      debug("Connections to local port %d forwarded to remote address %.200s:%d",
+	    options.local_forwards[i].port, options.local_forwards[i].host, 
+	    options.local_forwards[i].host_port);
+      channel_request_local_forwarding(options.local_forwards[i].port,
+				       options.local_forwards[i].host,
+				       options.local_forwards[i].host_port);
+    }
+
+  /* Initiate remote TCP/IP port forwardings. */
+  for (i = 0; i < options.num_remote_forwards; i++)
+    {
+      debug("Connections to remote port %d forwarded to local address %.200s:%d",
+	    options.remote_forwards[i].port, options.remote_forwards[i].host, 
+	    options.remote_forwards[i].host_port);
+      channel_request_remote_forwarding(options.remote_forwards[i].port,
+					options.remote_forwards[i].host,
+					options.remote_forwards[i].host_port);
+    }
+
+  /* If a command was specified on the command line, execute the command now.
+     Otherwise request the server to start a shell. */
+  if (buffer_len(&command) > 0)
+    {
+      int len = buffer_len(&command);
+      if (len > 900)
+	len = 900;
+      debug("Sending command: %.*s", len, buffer_ptr(&command));
+      packet_start(SSH_CMSG_EXEC_CMD);
+      packet_put_string(buffer_ptr(&command), buffer_len(&command));
+      packet_send();
+      packet_write_wait();
+    }
+  else
+    {
+      debug("Requesting shell.");
+      packet_start(SSH_CMSG_EXEC_SHELL);
+      packet_send();
+      packet_write_wait();
+    }
+
+  /* Enter the interactive session. */
+  exit_status = client_loop(tty_flag, tty_flag ? options.escape_char : -1);
+
+  /* Close the connection to the remote host. */
+  packet_close();
+  
+  /* Exit with the status returned by the program on the remote side. */
+  exit(exit_status);
+}
diff --git a/ssh.h b/ssh.h
new file mode 100644
index 0000000..a4bf136
--- /dev/null
+++ b/ssh.h
@@ -0,0 +1,589 @@
+/*
+
+ssh.h
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Fri Mar 17 17:09:37 1995 ylo
+
+Generic header file for ssh.
+
+*/
+
+/* RCSID("$Id: ssh.h,v 1.1 1999/10/27 03:42:45 damien Exp $"); */
+
+#ifndef SSH_H
+#define SSH_H
+
+#include "rsa.h"
+#include "cipher.h"
+
+/* The default cipher used if IDEA is not supported by the remote host. 
+   It is recommended that this be one of the mandatory ciphers (DES, 3DES),
+   though that is not required. */
+#define SSH_FALLBACK_CIPHER	SSH_CIPHER_3DES
+
+/* Cipher used for encrypting authentication files. */
+#define SSH_AUTHFILE_CIPHER	SSH_CIPHER_3DES
+
+/* Default port number. */
+#define SSH_DEFAULT_PORT	22
+
+/* Maximum number of TCP/IP ports forwarded per direction. */
+#define SSH_MAX_FORWARDS_PER_DIRECTION	100
+
+/* Maximum number of RSA authentication identity files that can be specified
+   in configuration files or on the command line. */
+#define SSH_MAX_IDENTITY_FILES		100
+
+/* Major protocol version.  Different version indicates major incompatiblity
+   that prevents communication.  */
+#define PROTOCOL_MAJOR		1
+
+/* Minor protocol version.  Different version indicates minor incompatibility
+   that does not prevent interoperation. */
+#define PROTOCOL_MINOR		5
+
+/* Name for the service.  The port named by this service overrides the default
+   port if present. */
+#define SSH_SERVICE_NAME	"ssh"
+
+#ifndef ETCDIR
+#define ETCDIR			"/etc"
+#endif /* ETCDIR */
+
+#define PIDDIR			"/var/run"
+
+/* System-wide file containing host keys of known hosts.  This file should be
+   world-readable. */
+#define SSH_SYSTEM_HOSTFILE	ETCDIR "/ssh_known_hosts"
+
+/*  HOST_KEY_FILE		/etc/ssh_host_key,
+    SERVER_CONFIG_FILE		/etc/sshd_config,
+and HOST_CONFIG_FILE		/etc/ssh_config
+are all defined in Makefile.in.  Of these, ssh_host_key should be readable
+only by root, whereas ssh_config should be world-readable. */
+
+#define HOST_KEY_FILE		ETCDIR "/ssh_host_key"
+#define SERVER_CONFIG_FILE	ETCDIR "/sshd_config"
+#define HOST_CONFIG_FILE	ETCDIR "/ssh_config"
+
+#define SSH_PROGRAM		"/usr/bin/ssh"
+
+/* The process id of the daemon listening for connections is saved
+   here to make it easier to kill the correct daemon when necessary. */
+#define SSH_DAEMON_PID_FILE	PIDDIR "/sshd.pid"
+
+/* The directory in user\'s home directory in which the files reside.
+   The directory should be world-readable (though not all files are). */
+#define SSH_USER_DIR		".ssh"
+
+/* Per-user file containing host keys of known hosts.  This file need
+   not be readable by anyone except the user him/herself, though this does
+   not contain anything particularly secret. */
+#define SSH_USER_HOSTFILE	"~/.ssh/known_hosts"
+
+/* Name of the default file containing client-side authentication key. 
+   This file should only be readable by the user him/herself. */
+#define SSH_CLIENT_IDENTITY	".ssh/identity"
+
+/* Configuration file in user\'s home directory.  This file need not be
+   readable by anyone but the user him/herself, but does not contain
+   anything particularly secret.  If the user\'s home directory resides
+   on an NFS volume where root is mapped to nobody, this may need to be
+   world-readable. */
+#define SSH_USER_CONFFILE	".ssh/config"
+
+/* File containing a list of those rsa keys that permit logging in as
+   this user.  This file need not be
+   readable by anyone but the user him/herself, but does not contain
+   anything particularly secret.  If the user\'s home directory resides
+   on an NFS volume where root is mapped to nobody, this may need to be
+   world-readable.  (This file is read by the daemon which is running as 
+   root.) */
+#define SSH_USER_PERMITTED_KEYS	".ssh/authorized_keys"
+
+/* Per-user and system-wide ssh "rc" files.  These files are executed with
+   /bin/sh before starting the shell or command if they exist.  They
+   will be passed "proto cookie" as arguments if X11 forwarding with
+   spoofing is in use.  xauth will be run if neither of these exists. */
+#define SSH_USER_RC		".ssh/rc"
+#define SSH_SYSTEM_RC		ETCDIR "/sshrc"
+
+/* Ssh-only version of /etc/hosts.equiv. */
+#define SSH_HOSTS_EQUIV		ETCDIR "/shosts.equiv"
+
+/* Additionally, the daemon may use ~/.rhosts and /etc/hosts.equiv if 
+   rhosts authentication is enabled. */
+
+/* Name of the environment variable containing the pathname of the
+   authentication socket. */
+#define SSH_AUTHSOCKET_ENV_NAME	"SSH_AUTH_SOCK"
+
+/* Force host key length and server key length to differ by at least this
+   many bits.  This is to make double encryption with rsaref work. */
+#define SSH_KEY_BITS_RESERVED		128
+
+/* Length of the session key in bytes.  (Specified as 256 bits in the 
+   protocol.)  */
+#define SSH_SESSION_KEY_LENGTH		32
+
+/* Name of Kerberos service for SSH to use. */
+#define KRB4_SERVICE_NAME		"rcmd"
+
+/* Authentication methods.  New types can be added, but old types should not
+   be removed for compatibility.  The maximum allowed value is 31. */
+#define SSH_AUTH_RHOSTS		1
+#define SSH_AUTH_RSA		2
+#define SSH_AUTH_PASSWORD	3
+#define SSH_AUTH_RHOSTS_RSA	4
+				/* 5 is TIS */
+#define SSH_AUTH_KERBEROS	6
+#define SSH_PASS_KERBEROS_TGT	7
+				/* 8 to 15 are reserved */
+#define SSH_PASS_AFS_TOKEN	21
+
+/* Protocol flags.  These are bit masks. */
+#define SSH_PROTOFLAG_SCREEN_NUMBER	1 /* X11 forwarding includes screen */
+#define SSH_PROTOFLAG_HOST_IN_FWD_OPEN	2 /* forwarding opens contain host */
+
+/* Definition of message types.  New values can be added, but old values
+   should not be removed or without careful consideration of the consequences
+   for compatibility.  The maximum value is 254; value 255 is reserved
+   for future extension. */
+/* Message name */			/* msg code */  /* arguments */
+#define SSH_MSG_NONE				0	/* no message */
+#define SSH_MSG_DISCONNECT			1	/* cause (string) */
+#define SSH_SMSG_PUBLIC_KEY			2	/* ck,msk,srvk,hostk */
+#define SSH_CMSG_SESSION_KEY			3	/* key (BIGNUM) */
+#define SSH_CMSG_USER				4	/* user (string) */
+#define SSH_CMSG_AUTH_RHOSTS			5	/* user (string) */
+#define SSH_CMSG_AUTH_RSA			6	/* modulus (BIGNUM) */
+#define SSH_SMSG_AUTH_RSA_CHALLENGE		7	/* int (BIGNUM) */
+#define SSH_CMSG_AUTH_RSA_RESPONSE		8	/* int (BIGNUM) */
+#define SSH_CMSG_AUTH_PASSWORD			9	/* pass (string) */
+#define SSH_CMSG_REQUEST_PTY		        10	/* TERM, tty modes */
+#define SSH_CMSG_WINDOW_SIZE		        11	/* row,col,xpix,ypix */
+#define SSH_CMSG_EXEC_SHELL			12	/* */
+#define SSH_CMSG_EXEC_CMD			13	/* cmd (string) */
+#define SSH_SMSG_SUCCESS			14	/* */
+#define SSH_SMSG_FAILURE			15	/* */
+#define SSH_CMSG_STDIN_DATA			16	/* data (string) */
+#define SSH_SMSG_STDOUT_DATA			17	/* data (string) */
+#define SSH_SMSG_STDERR_DATA			18	/* data (string) */
+#define SSH_CMSG_EOF				19	/* */
+#define SSH_SMSG_EXITSTATUS			20	/* status (int) */
+#define SSH_MSG_CHANNEL_OPEN_CONFIRMATION	21	/* channel (int) */
+#define SSH_MSG_CHANNEL_OPEN_FAILURE		22	/* channel (int) */
+#define SSH_MSG_CHANNEL_DATA			23	/* ch,data (int,str) */
+#define SSH_MSG_CHANNEL_CLOSE			24	/* channel (int) */
+#define SSH_MSG_CHANNEL_CLOSE_CONFIRMATION	25	/* channel (int) */
+/*      SSH_CMSG_X11_REQUEST_FORWARDING         26         OBSOLETE */
+#define SSH_SMSG_X11_OPEN			27	/* channel (int) */
+#define SSH_CMSG_PORT_FORWARD_REQUEST		28	/* p,host,hp (i,s,i) */
+#define SSH_MSG_PORT_OPEN			29	/* ch,h,p (i,s,i) */
+#define SSH_CMSG_AGENT_REQUEST_FORWARDING	30	/* */
+#define SSH_SMSG_AGENT_OPEN			31	/* port (int) */
+#define SSH_MSG_IGNORE				32	/* string */
+#define SSH_CMSG_EXIT_CONFIRMATION		33	/* */
+#define SSH_CMSG_X11_REQUEST_FORWARDING		34	/* proto,data (s,s) */
+#define SSH_CMSG_AUTH_RHOSTS_RSA		35	/* user,mod (s,mpi) */
+#define SSH_MSG_DEBUG				36	/* string */
+#define SSH_CMSG_REQUEST_COMPRESSION		37	/* level 1-9 (int) */
+#define SSH_CMSG_MAX_PACKET_SIZE		38	/* size 4k-1024k (int) */
+#define SSH_CMSG_AUTH_TIS			39	/* this is proto-1.5, but we ignore TIS */
+#define SSH_SMSG_AUTH_TIS_CHALLENGE		40
+#define SSH_CMSG_AUTH_TIS_RESPONSE		41
+
+#define SSH_CMSG_AUTH_KERBEROS			42	/* (KTEXT) */
+#define SSH_SMSG_AUTH_KERBEROS_RESPONSE		43	/* (KTEXT) */
+#define SSH_CMSG_HAVE_KERBEROS_TGT		44	/* credentials (s) */
+#define SSH_CMSG_HAVE_AFS_TOKEN			65	/* token (s) */
+
+
+/* Includes that need definitions above. */
+
+#include "readconf.h"
+
+/*------------ definitions for login.c -------------*/
+
+/* Returns the time when the user last logged in.  Returns 0 if the 
+   information is not available.  This must be called before record_login. 
+   The host from which the user logged in is stored in buf. */
+unsigned long get_last_login_time(uid_t uid, const char *logname, 
+				  char *buf, unsigned int bufsize);
+
+/* Records that the user has logged in.  This does many things normally
+   done by login(1). */
+void record_login(int pid, const char *ttyname, const char *user, uid_t uid,
+		  const char *host, struct sockaddr_in *addr);
+
+/* Records that the user has logged out.  This does many thigs normally
+   done by login(1) or init. */
+void record_logout(int pid, const char *ttyname);
+
+/*------------ definitions for sshconnect.c ----------*/
+
+/* Opens a TCP/IP connection to the remote server on the given host.  If
+   port is 0, the default port will be used.  If anonymous is zero,
+   a privileged port will be allocated to make the connection. 
+   This requires super-user privileges if anonymous is false. 
+   Connection_attempts specifies the maximum number of tries, one per
+   second.  This returns true on success, and zero on failure.  If the
+   connection is successful, this calls packet_set_connection for the
+   connection. */
+int ssh_connect(const char *host, struct sockaddr_in *hostaddr,
+		int port, int connection_attempts,
+		int anonymous, uid_t original_real_uid,
+		const char *proxy_command);
+
+/* Starts a dialog with the server, and authenticates the current user on the
+   server.  This does not need any extra privileges.  The basic connection
+   to the server must already have been established before this is called. 
+   If login fails, this function prints an error and never returns. 
+   This initializes the random state, and leaves it initialized (it will also
+   have references from the packet module). */
+void ssh_login(int host_key_valid, RSA *host_key, const char *host,
+	       struct sockaddr_in *hostaddr, Options *options,
+	       uid_t original_real_uid);
+
+/*------------ Definitions for various authentication methods. -------*/
+
+/* Tries to authenticate the user using the .rhosts file.  Returns true if
+   authentication succeeds.  If ignore_rhosts is non-zero, this will not
+   consider .rhosts and .shosts (/etc/hosts.equiv will still be used). 
+   If strict_modes is true, checks ownership and modes of .rhosts/.shosts. */
+int auth_rhosts(struct passwd *pw, const char *client_user,
+		int ignore_rhosts, int strict_modes);
+
+/* Tries to authenticate the user using the .rhosts file and the host using
+   its host key.  Returns true if authentication succeeds. */
+int auth_rhosts_rsa(struct passwd *pw, const char *client_user,
+		    unsigned int bits, BIGNUM *client_host_key_e,
+		    BIGNUM *client_host_key_n, int ignore_rhosts,
+		    int strict_modes);
+
+/* Tries to authenticate the user using password.  Returns true if
+   authentication succeeds. */
+int auth_password(struct passwd *pw, const char *password);
+
+/* Performs the RSA authentication dialog with the client.  This returns
+   0 if the client could not be authenticated, and 1 if authentication was
+   successful.  This may exit if there is a serious protocol violation. */
+int auth_rsa(struct passwd *pw, BIGNUM *client_n, int strict_modes);
+
+/* Parses an RSA key (number of bits, e, n) from a string.  Moves the pointer
+   over the key.  Skips any whitespace at the beginning and at end. */
+int auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM *e, BIGNUM *n);
+
+/* Returns the name of the machine at the other end of the socket.  The
+   returned string should be freed by the caller. */
+char *get_remote_hostname(int socket);
+
+/* Return the canonical name of the host in the other side of the current
+   connection (as returned by packet_get_connection).  The host name is
+   cached, so it is efficient to call this several times. */
+const char *get_canonical_hostname(void);
+
+/* Returns the remote IP address as an ascii string.  The value need not be
+   freed by the caller. */
+const char *get_remote_ipaddr(void);
+
+/* Returns the port number of the peer of the socket. */
+int get_peer_port(int sock);
+
+/* Returns the port number of the remote host. */
+int get_remote_port(void);
+
+/* Tries to match the host name (which must be in all lowercase) against the
+   comma-separated sequence of subpatterns (each possibly preceded by ! to 
+   indicate negation).  Returns true if there is a positive match; zero
+   otherwise. */
+int match_hostname(const char *host, const char *pattern, unsigned int len);
+
+/* Checks whether the given host is already in the list of our known hosts.
+   Returns HOST_OK if the host is known and has the specified key,
+   HOST_NEW if the host is not known, and HOST_CHANGED if the host is known
+   but used to have a different host key.  The host must be in all lowercase. */
+typedef enum { HOST_OK, HOST_NEW, HOST_CHANGED } HostStatus;
+HostStatus check_host_in_hostfile(const char *filename, 
+				  const char *host, unsigned int bits,
+				  BIGNUM *e, BIGNUM *n,
+				  BIGNUM *ke, BIGNUM *kn);
+
+/* Appends an entry to the host file.  Returns false if the entry
+   could not be appended. */
+int add_host_to_hostfile(const char *filename, const char *host,
+			 unsigned int bits, BIGNUM *e, BIGNUM *n);
+
+/* Performs the RSA authentication challenge-response dialog with the client,
+   and returns true (non-zero) if the client gave the correct answer to
+   our challenge; returns zero if the client gives a wrong answer. */
+int auth_rsa_challenge_dialog(unsigned int bits, BIGNUM *e, BIGNUM *n);
+
+/* Reads a passphrase from /dev/tty with echo turned off.  Returns the 
+   passphrase (allocated with xmalloc).  Exits if EOF is encountered. 
+   If from_stdin is true, the passphrase will be read from stdin instead. */
+char *read_passphrase(const char *prompt, int from_stdin);
+
+/* Saves the authentication (private) key in a file, encrypting it with
+   passphrase.  The identification of the file (lowest 64 bits of n)
+   will precede the key to provide identification of the key without
+   needing a passphrase. */
+int save_private_key(const char *filename, const char *passphrase,
+		     RSA *private_key, const char *comment);
+
+/* Loads the public part of the key file (public key and comment). 
+   Returns 0 if an error occurred; zero if the public key was successfully
+   read.  The comment of the key is returned in comment_return if it is
+   non-NULL; the caller must free the value with xfree. */
+int load_public_key(const char *filename, RSA *pub, 
+		    char **comment_return);
+
+/* Loads the private key from the file.  Returns 0 if an error is encountered
+   (file does not exist or is not readable, or passphrase is bad).
+   This initializes the private key.  The comment of the key is returned 
+   in comment_return if it is non-NULL; the caller must free the value 
+   with xfree. */
+int load_private_key(const char *filename, const char *passphrase,
+		     RSA *private_key, char **comment_return);
+
+/*------------ Definitions for logging. -----------------------*/
+
+/* Supported syslog facilities. */
+typedef enum
+{
+  SYSLOG_FACILITY_DAEMON,
+  SYSLOG_FACILITY_USER,
+  SYSLOG_FACILITY_AUTH,
+  SYSLOG_FACILITY_LOCAL0,
+  SYSLOG_FACILITY_LOCAL1,
+  SYSLOG_FACILITY_LOCAL2,
+  SYSLOG_FACILITY_LOCAL3,
+  SYSLOG_FACILITY_LOCAL4,
+  SYSLOG_FACILITY_LOCAL5,
+  SYSLOG_FACILITY_LOCAL6,
+  SYSLOG_FACILITY_LOCAL7
+} SyslogFacility;
+
+/* Initializes logging.  If debug is non-zero, debug() will output something.
+   If quiet is non-zero, none of these will log send anything to syslog
+   (but maybe to stderr). */
+void log_init(char *av0, int on_stderr, int debug, int quiet,
+	      SyslogFacility facility);
+
+/* Outputs a message to syslog or stderr, depending on the implementation. 
+   The format must guarantee that the final message does not exceed 1024 
+   characters.  The message should not contain newline. */
+void log(const char *fmt, ...);
+
+/* Outputs a message to syslog or stderr, depending on the implementation. 
+   The format must guarantee that the final message does not exceed 1024 
+   characters.  The message should not contain newline. */
+void debug(const char *fmt, ...);
+
+/* Outputs a message to syslog or stderr, depending on the implementation. 
+   The format must guarantee that the final message does not exceed 1024 
+   characters.  The message should not contain newline. */
+void error(const char *fmt, ...);
+
+/* Outputs a message to syslog or stderr, depending on the implementation. 
+   The format must guarantee that the final message does not exceed 1024 
+   characters.  The message should not contain newline.  
+   This call never returns. */
+void fatal(const char *fmt, ...);
+
+/* Registers a cleanup function to be called by fatal() before exiting. 
+   It is permissible to call fatal_remove_cleanup for the function itself
+   from the function. */
+void fatal_add_cleanup(void (*proc)(void *context), void *context);
+
+/* Removes a cleanup frunction to be called at fatal(). */
+void fatal_remove_cleanup(void (*proc)(void *context), void *context);
+
+/*---------------- definitions for channels ------------------*/
+
+/* Sets specific protocol options. */
+void channel_set_options(int hostname_in_open);
+
+/* Allocate a new channel object and set its type and socket.  Remote_name
+   must have been allocated with xmalloc; this will free it when the channel
+   is freed. */
+int channel_allocate(int type, int sock, char *remote_name);
+
+/* Free the channel and close its socket. */
+void channel_free(int channel);
+
+/* Add any bits relevant to channels in select bitmasks. */
+void channel_prepare_select(fd_set *readset, fd_set *writeset);
+
+/* After select, perform any appropriate operations for channels which
+   have events pending. */
+void channel_after_select(fd_set *readset, fd_set *writeset);
+
+/* If there is data to send to the connection, send some of it now. */
+void channel_output_poll(void);
+
+/* This is called when a packet of type CHANNEL_DATA has just been received.
+   The message type has already been consumed, but channel number and data
+   is still there. */
+void channel_input_data(int payload_len);
+
+/* Returns true if no channel has too much buffered data. */
+int channel_not_very_much_buffered_data(void);
+
+/* This is called after receiving CHANNEL_CLOSE. */
+void channel_input_close(void);
+
+/* This is called after receiving CHANNEL_CLOSE_CONFIRMATION. */
+void channel_input_close_confirmation(void);
+
+/* This is called after receiving CHANNEL_OPEN_CONFIRMATION. */
+void channel_input_open_confirmation(void);
+
+/* This is called after receiving CHANNEL_OPEN_FAILURE from the other side. */
+void channel_input_open_failure(void);
+
+/* This closes any sockets that are listening for connections; this removes
+   any unix domain sockets. */
+void channel_stop_listening(void);
+
+/* Closes the sockets of all channels.  This is used to close extra file
+   descriptors after a fork. */
+void channel_close_all(void);
+
+/* Returns the maximum file descriptor number used by the channels. */
+int channel_max_fd(void);
+
+/* Returns true if there is still an open channel over the connection. */
+int channel_still_open(void);
+
+/* Returns a string containing a list of all open channels.  The list is
+   suitable for displaying to the user.  It uses crlf instead of newlines.
+   The caller should free the string with xfree. */
+char *channel_open_message(void);
+
+/* Initiate forwarding of connections to local port "port" through the secure
+   channel to host:port from remote side.  This never returns if there
+   was an error. */
+void channel_request_local_forwarding(int port, const char *host,
+				      int remote_port);
+
+/* Initiate forwarding of connections to port "port" on remote host through
+   the secure channel to host:port from local side.  This never returns
+   if there was an error.  This registers that open requests for that
+   port are permitted. */
+void channel_request_remote_forwarding(int port, const char *host,
+				       int remote_port);
+
+/* Permits opening to any host/port in SSH_MSG_PORT_OPEN.  This is usually
+   called by the server, because the user could connect to any port anyway,
+   and the server has no way to know but to trust the client anyway. */
+void channel_permit_all_opens(void);
+
+/* This is called after receiving CHANNEL_FORWARDING_REQUEST.  This initates
+   listening for the port, and sends back a success reply (or disconnect
+   message if there was an error).  This never returns if there was an 
+   error. */
+void channel_input_port_forward_request(int is_root);
+
+/* This is called after receiving PORT_OPEN message.  This attempts to connect
+   to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION or
+   CHANNEL_OPEN_FAILURE. */
+void channel_input_port_open(int payload_len);
+
+/* Creates a port for X11 connections, and starts listening for it.
+   Returns the display name, or NULL if an error was encountered. */
+char *x11_create_display(int screen);
+
+/* Creates an internet domain socket for listening for X11 connections. 
+   Returns a suitable value for the DISPLAY variable, or NULL if an error
+   occurs. */
+char *x11_create_display_inet(int screen);
+
+/* This is called when SSH_SMSG_X11_OPEN is received.  The packet contains
+   the remote channel number.  We should do whatever we want, and respond
+   with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. */
+void x11_input_open(int payload_len);
+
+/* Requests forwarding of X11 connections.  This should be called on the 
+   client only. */
+void x11_request_forwarding(void);
+
+/* Requests forwarding for X11 connections, with authentication spoofing.
+   This should be called in the client only.  */
+void x11_request_forwarding_with_spoofing(const char *proto, const char *data);
+
+/* Local Xauthority file (server only). */
+extern char *xauthfile;
+
+/* Sends a message to the server to request authentication fd forwarding. */
+void auth_request_forwarding(void);
+
+/* Returns the name of the forwarded authentication socket.  Returns NULL
+   if there is no forwarded authentication socket.  The returned value points
+   to a static buffer. */
+char *auth_get_socket_name(void);
+
+/* This if called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server.
+   This starts forwarding authentication requests. */
+void auth_input_request_forwarding(struct passwd *pw);
+
+/* This is called to process an SSH_SMSG_AGENT_OPEN message. */
+void auth_input_open_request(void);
+
+/* Returns true if the given string matches the pattern (which may contain
+   ? and * as wildcards), and zero if it does not match. */
+int match_pattern(const char *s, const char *pattern);
+
+/* Expands tildes in the file name.  Returns data allocated by xmalloc.
+   Warning: this calls getpw*. */
+char *tilde_expand_filename(const char *filename, uid_t my_uid);
+
+/* Performs the interactive session.  This handles data transmission between
+   the client and the program.  Note that the notion of stdin, stdout, and
+   stderr in this function is sort of reversed: this function writes to
+   stdin (of the child program), and reads from stdout and stderr (of the
+   child program). */
+void server_loop(int pid, int fdin, int fdout, int fderr);
+
+/* Client side main loop for the interactive session. */
+int client_loop(int have_pty, int escape_char);
+
+/* Linked list of custom environment strings (see auth-rsa.c). */
+struct envstring {
+  struct envstring *next;
+  char *s;
+};
+
+#ifdef KRB4
+#include <krb.h>
+
+/* Performs Kerberos v4 mutual authentication with the client. This returns
+   0 if the client could not be authenticated, and 1 if authentication was
+   successful.  This may exit if there is a serious protocol violation. */
+int auth_krb4(const char *server_user, KTEXT auth, char **client);
+int ssh_tf_init(uid_t uid);
+
+#ifdef AFS
+#include <kafs.h>
+
+/* Accept passed Kerberos v4 ticket-granting ticket and AFS tokens. */
+int auth_kerberos_tgt(struct passwd *pw, const char *string);
+int auth_afs_token(char *server_user, uid_t uid, const char *string);
+
+int creds_to_radix(CREDENTIALS *creds, unsigned char *buf);
+int radix_to_creds(const char *buf, CREDENTIALS *creds);
+#endif /* AFS */
+
+#endif /* KRB4 */
+
+#ifdef SKEY
+#include <skey.h>
+char *skey_fake_keyinfo(char *username);
+#endif /* SKEY */
+
+#endif /* SSH_H */
diff --git a/ssh.pam b/ssh.pam
new file mode 100644
index 0000000..2a7d1fb
--- /dev/null
+++ b/ssh.pam
@@ -0,0 +1,7 @@
+#%PAM-1.0
+auth       required     /lib/security/pam_pwdb.so shadow
+auth       required     /lib/security/pam_nologin.so
+account    required     /lib/security/pam_pwdb.so
+password   required     /lib/security/pam_cracklib.so
+password   required     /lib/security/pam_pwdb.so shadow nullok use_authtok
+session    required     /lib/security/pam_pwdb.so
diff --git a/ssh_config b/ssh_config
new file mode 100644
index 0000000..9fb064d
--- /dev/null
+++ b/ssh_config
@@ -0,0 +1,30 @@
+# This is ssh client systemwide configuration file.  This file provides 
+# defaults for users, and the values can be changed in per-user configuration
+# files or on the command line.
+
+# Configuration data is parsed as follows:
+#  1. command line options
+#  2. user-specific file
+#  3. system-wide file
+# Any configuration value is only changed the first time it is set.
+# Thus, host-specific definitions should be at the beginning of the
+# configuration file, and defaults at the end.
+
+# Site-wide defaults for various options
+
+# Host *
+#   ForwardAgent yes
+#   ForwardX11 yes
+#   RhostsAuthentication yes
+#   RhostsRSAAuthentication yes
+#   RSAAuthentication yes
+#   PasswordAuthentication yes
+#   FallBackToRsh yes
+#   UseRsh no
+#   BatchMode no
+#   CheckHostIP yes
+#   StrictHostKeyChecking no
+#   IdentityFile ~/.ssh/identity
+#   Port 22
+#   Cipher blowfish
+#   EscapeChar ~
diff --git a/sshconnect.c b/sshconnect.c
new file mode 100644
index 0000000..3437b04
--- /dev/null
+++ b/sshconnect.c
@@ -0,0 +1,1495 @@
+/*
+
+sshconnect.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Sat Mar 18 22:15:47 1995 ylo
+
+Code to connect to a remote host, and to perform the client side of the
+login (authentication) dialog.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: sshconnect.c,v 1.1 1999/10/27 03:42:45 damien Exp $");
+
+#include <openssl/bn.h>
+#include "xmalloc.h"
+#include "rsa.h"
+#include "ssh.h"
+#include "packet.h"
+#include "authfd.h"
+#include "cipher.h"
+#include "mpaux.h"
+#include "uidswap.h"
+#include "compat.h"
+
+#include <openssl/md5.h>
+
+/* Session id for the current session. */
+unsigned char session_id[16];
+
+/* Connect to the given ssh server using a proxy command. */
+
+int
+ssh_proxy_connect(const char *host, int port, uid_t original_real_uid,
+		  const char *proxy_command)
+{
+  Buffer command;
+  const char *cp;
+  char *command_string;
+  int pin[2], pout[2];
+  int pid;
+  char portstring[100];
+
+  /* Convert the port number into a string. */
+  snprintf(portstring, sizeof portstring, "%d", port);
+
+  /* Build the final command string in the buffer by making the appropriate
+     substitutions to the given proxy command. */
+  buffer_init(&command);
+  for (cp = proxy_command; *cp; cp++)
+    {
+      if (cp[0] == '%' && cp[1] == '%')
+	{
+	  buffer_append(&command, "%", 1);
+	  cp++;
+	  continue;
+	}
+      if (cp[0] == '%' && cp[1] == 'h')
+	{
+	  buffer_append(&command, host, strlen(host));
+	  cp++;
+	  continue;
+	}
+      if (cp[0] == '%' && cp[1] == 'p')
+	{
+	  buffer_append(&command, portstring, strlen(portstring));
+	  cp++;
+	  continue;
+	}
+      buffer_append(&command, cp, 1);
+    }
+  buffer_append(&command, "\0", 1);
+
+  /* Get the final command string. */
+  command_string = buffer_ptr(&command);
+
+  /* Create pipes for communicating with the proxy. */
+  if (pipe(pin) < 0 || pipe(pout) < 0)
+    fatal("Could not create pipes to communicate with the proxy: %.100s",
+	  strerror(errno));
+
+  debug("Executing proxy command: %.500s", command_string);
+
+  /* Fork and execute the proxy command. */
+  if ((pid = fork()) == 0)
+    {
+      char *argv[10];
+
+      /* Child.  Permanently give up superuser privileges. */
+      permanently_set_uid(original_real_uid);
+
+      /* Redirect stdin and stdout. */
+      close(pin[1]);
+      if (pin[0] != 0)
+	{
+	  if (dup2(pin[0], 0) < 0)
+	    perror("dup2 stdin");
+	  close(pin[0]);
+	}
+      close(pout[0]);
+      if (dup2(pout[1], 1) < 0)
+	perror("dup2 stdout");
+      close(pout[1]); /* Cannot be 1 because pin allocated two descriptors. */
+
+      /* Stderr is left as it is so that error messages get printed on
+	 the user's terminal. */
+      argv[0] = "/bin/sh";
+      argv[1] = "-c";
+      argv[2] = command_string;
+      argv[3] = NULL;
+      
+      /* Execute the proxy command.  Note that we gave up any extra 
+	 privileges above. */
+      execv("/bin/sh", argv);
+      perror("/bin/sh");
+      exit(1);
+    }
+  /* Parent. */
+  if (pid < 0)
+    fatal("fork failed: %.100s", strerror(errno));
+  
+  /* Close child side of the descriptors. */
+  close(pin[0]);
+  close(pout[1]);
+
+  /* Free the command name. */
+  buffer_free(&command);
+  
+  /* Set the connection file descriptors. */
+  packet_set_connection(pout[0], pin[1]);
+
+  return 1;
+}
+
+/* Creates a (possibly privileged) socket for use as the ssh connection. */
+
+int ssh_create_socket(uid_t original_real_uid, int privileged)
+{
+  int sock;
+
+  /* If we are running as root and want to connect to a privileged port,
+     bind our own socket to a privileged port. */
+  if (privileged)
+    {
+      int p = IPPORT_RESERVED - 1;
+
+      sock = rresvport(&p);
+      if (sock < 0)
+        fatal("rresvport: %.100s", strerror(errno));
+      debug("Allocated local port %d.", p);
+    }
+  else
+    { 
+      /* Just create an ordinary socket on arbitrary port.  We use the
+	 user's uid to create the socket. */
+      temporarily_use_uid(original_real_uid);
+      sock = socket(AF_INET, SOCK_STREAM, 0);
+      if (sock < 0)
+	fatal("socket: %.100s", strerror(errno));
+      restore_uid();
+    }
+  return sock;
+}
+
+/* Opens a TCP/IP connection to the remote server on the given host.  If
+   port is 0, the default port will be used.  If anonymous is zero,
+   a privileged port will be allocated to make the connection. 
+   This requires super-user privileges if anonymous is false. 
+   Connection_attempts specifies the maximum number of tries (one per
+   second).  If proxy_command is non-NULL, it specifies the command (with %h 
+   and %p substituted for host and port, respectively) to use to contact
+   the daemon. */
+
+int ssh_connect(const char *host, struct sockaddr_in *hostaddr,
+		int port, int connection_attempts,
+		int anonymous, uid_t original_real_uid, 
+		const char *proxy_command)
+{
+  int sock = -1, attempt, i;
+  int on = 1;
+  struct servent *sp;
+  struct hostent *hp;
+  struct linger linger;
+
+  debug("ssh_connect: getuid %d geteuid %d anon %d", 
+	(int)getuid(), (int)geteuid(), anonymous);
+
+  /* Get default port if port has not been set. */
+  if (port == 0)
+    {
+      sp = getservbyname(SSH_SERVICE_NAME, "tcp");
+      if (sp)
+	port = ntohs(sp->s_port);
+      else
+	port = SSH_DEFAULT_PORT;
+    }
+
+  /* If a proxy command is given, connect using it. */
+  if (proxy_command != NULL)
+    return ssh_proxy_connect(host, port, original_real_uid, proxy_command);
+
+  /* No proxy command. */
+
+  /* No host lookup made yet. */
+  hp = NULL;
+  
+  /* Try to connect several times.  On some machines, the first time will
+     sometimes fail.  In general socket code appears to behave quite
+     magically on many machines. */
+  for (attempt = 0; attempt < connection_attempts; attempt++)
+    {
+      if (attempt > 0)
+	debug("Trying again...");
+
+      /* Try to parse the host name as a numeric inet address. */
+      memset(hostaddr, 0, sizeof(hostaddr));
+      hostaddr->sin_family = AF_INET;
+      hostaddr->sin_port = htons(port);
+      hostaddr->sin_addr.s_addr = inet_addr(host);
+      if ((hostaddr->sin_addr.s_addr & 0xffffffff) != 0xffffffff)
+	{ 
+	  /* Valid numeric IP address */
+	  debug("Connecting to %.100s port %d.", 
+		inet_ntoa(hostaddr->sin_addr), port);
+      
+	  /* Create a socket. */
+	  sock = ssh_create_socket(original_real_uid, 
+				   !anonymous && geteuid() == 0 && 
+				     port < IPPORT_RESERVED);
+      
+	  /* Connect to the host.  We use the user's uid in the hope that
+	     it will help with the problems of tcp_wrappers showing the
+	     remote uid as root. */
+	  temporarily_use_uid(original_real_uid);
+	  if (connect(sock, (struct sockaddr *)hostaddr, sizeof(*hostaddr))
+	      >= 0)
+	    {
+	      /* Successful connect. */
+	      restore_uid();
+	      break;
+	    }
+	  debug("connect: %.100s", strerror(errno));
+	  restore_uid();
+
+	  /* Destroy the failed socket. */
+	  shutdown(sock, SHUT_RDWR);
+	  close(sock);
+	}
+      else
+	{ 
+	  /* Not a valid numeric inet address. */
+	  /* Map host name to an address. */
+	  if (!hp)
+	    hp = gethostbyname(host);
+	  if (!hp)
+	    fatal("Bad host name: %.100s", host);
+	  if (!hp->h_addr_list[0])
+	    fatal("Host does not have an IP address: %.100s", host);
+
+	  /* Loop through addresses for this host, and try each one in
+	     sequence until the connection succeeds. */
+	  for (i = 0; hp->h_addr_list[i]; i++)
+	    {
+	      /* Set the address to connect to. */
+	      hostaddr->sin_family = hp->h_addrtype;
+	      memcpy(&hostaddr->sin_addr, hp->h_addr_list[i],
+		     sizeof(hostaddr->sin_addr));
+
+	      debug("Connecting to %.200s [%.100s] port %d.",
+		    host, inet_ntoa(hostaddr->sin_addr), port);
+
+	      /* Create a socket for connecting. */
+	      sock = ssh_create_socket(original_real_uid, 
+				       !anonymous && geteuid() == 0 && 
+				         port < IPPORT_RESERVED);
+
+	      /* Connect to the host.  We use the user's uid in the hope that
+	         it will help with tcp_wrappers showing the remote uid as
+		 root. */
+	      temporarily_use_uid(original_real_uid);
+	      if (connect(sock, (struct sockaddr *)hostaddr, 
+			  sizeof(*hostaddr)) >= 0)
+		{
+		  /* Successful connection. */
+		  restore_uid();
+		  break;
+		}
+	      debug("connect: %.100s", strerror(errno));
+	      restore_uid();
+
+	      /* Close the failed socket; there appear to be some problems 
+		 when reusing a socket for which connect() has already 
+		 returned an error. */
+	      shutdown(sock, SHUT_RDWR);
+	      close(sock);
+	    }
+	  if (hp->h_addr_list[i])
+	    break; /* Successful connection. */
+	}
+
+      /* Sleep a moment before retrying. */
+      sleep(1);
+    }
+  /* Return failure if we didn't get a successful connection. */
+  if (attempt >= connection_attempts)
+    return 0;
+
+  debug("Connection established.");
+
+  /* Set socket options.  We would like the socket to disappear as soon as
+     it has been closed for whatever reason. */
+  /* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */
+  setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&on, sizeof(on));
+  linger.l_onoff = 1;
+  linger.l_linger = 5;
+  setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger));
+
+  /* Set the connection. */
+  packet_set_connection(sock, sock);
+
+  return 1;
+}
+
+/* Checks if the user has an authentication agent, and if so, tries to
+   authenticate using the agent. */
+
+int
+try_agent_authentication()
+{
+  int status, type, bits;
+  char *comment;
+  AuthenticationConnection *auth;
+  unsigned char response[16];
+  unsigned int i;
+  BIGNUM *e, *n, *challenge;
+  
+  /* Get connection to the agent. */
+  auth = ssh_get_authentication_connection();
+  if (!auth)
+    return 0;
+  
+  e = BN_new();
+  n = BN_new();
+  challenge = BN_new();
+  
+  /* Loop through identities served by the agent. */
+  for (status = ssh_get_first_identity(auth, &bits, e, n, &comment);
+       status;
+       status = ssh_get_next_identity(auth, &bits, e, n, &comment))
+    {
+      int plen, clen;
+
+      /* Try this identity. */
+      debug("Trying RSA authentication via agent with '%.100s'", comment);
+      xfree(comment);
+      
+      /* Tell the server that we are willing to authenticate using this key. */
+      packet_start(SSH_CMSG_AUTH_RSA);
+      packet_put_bignum(n);
+      packet_send();
+      packet_write_wait();
+      
+      /* Wait for server's response. */
+      type = packet_read(&plen);
+      
+      /* The server sends failure if it doesn\'t like our key or does not
+	 support RSA authentication. */
+      if (type == SSH_SMSG_FAILURE)
+	{
+	  debug("Server refused our key.");
+	  continue;
+	}
+      
+      /* Otherwise it should have sent a challenge. */
+      if (type != SSH_SMSG_AUTH_RSA_CHALLENGE)
+	packet_disconnect("Protocol error during RSA authentication: %d", 
+			  type);
+      
+      packet_get_bignum(challenge, &clen);
+      
+      packet_integrity_check(plen, clen, type);
+
+      debug("Received RSA challenge from server.");
+      
+      /* Ask the agent to decrypt the challenge. */
+      if (!ssh_decrypt_challenge(auth, bits, e, n, challenge, 
+				 session_id, 1, response))
+	{
+	  /* The agent failed to authenticate this identifier although it
+	     advertised it supports this.  Just return a wrong value. */
+	  log("Authentication agent failed to decrypt challenge.");
+	  memset(response, 0, sizeof(response));
+	}
+      
+      debug("Sending response to RSA challenge.");
+      
+      /* Send the decrypted challenge back to the server. */
+      packet_start(SSH_CMSG_AUTH_RSA_RESPONSE);
+      for (i = 0; i < 16; i++)
+	packet_put_char(response[i]);
+      packet_send();
+      packet_write_wait();
+      
+      /* Wait for response from the server. */
+      type = packet_read(&plen);
+
+      /* The server returns success if it accepted the authentication. */
+      if (type == SSH_SMSG_SUCCESS)
+	{
+	  debug("RSA authentication accepted by server.");
+	  BN_clear_free(e);
+	  BN_clear_free(n);
+	  BN_clear_free(challenge);
+	  return 1;
+	}
+
+      /* Otherwise it should return failure. */
+      if (type != SSH_SMSG_FAILURE)
+	packet_disconnect("Protocol error waiting RSA auth response: %d", 
+			  type);
+    }
+
+  BN_clear_free(e);
+  BN_clear_free(n);
+  BN_clear_free(challenge);
+
+  debug("RSA authentication using agent refused.");
+  return 0;
+}
+
+/* Computes the proper response to a RSA challenge, and sends the response to
+   the server. */
+
+void
+respond_to_rsa_challenge(BIGNUM *challenge, RSA *prv)
+{
+  unsigned char buf[32], response[16];
+  MD5_CTX md;
+  int i, len;
+
+  /* Decrypt the challenge using the private key. */
+  rsa_private_decrypt(challenge, challenge, prv);
+
+  /* Compute the response. */
+  /* The response is MD5 of decrypted challenge plus session id. */
+  len = BN_num_bytes(challenge);
+  assert(len <= sizeof(buf) && len);
+  memset(buf, 0, sizeof(buf));
+  BN_bn2bin(challenge, buf + sizeof(buf) - len);
+  MD5_Init(&md);
+  MD5_Update(&md, buf, 32);
+  MD5_Update(&md, session_id, 16);
+  MD5_Final(response, &md);
+  
+  debug("Sending response to host key RSA challenge.");
+
+  /* Send the response back to the server. */
+  packet_start(SSH_CMSG_AUTH_RSA_RESPONSE);
+  for (i = 0; i < 16; i++)
+    packet_put_char(response[i]);
+  packet_send();
+  packet_write_wait();
+  
+  memset(buf, 0, sizeof(buf));
+  memset(response, 0, sizeof(response));
+  memset(&md, 0, sizeof(md));
+}
+
+/* Checks if the user has authentication file, and if so, tries to authenticate
+   the user using it. */
+
+int
+try_rsa_authentication(struct passwd *pw, const char *authfile,
+		       int may_ask_passphrase)
+{
+  BIGNUM *challenge;
+  RSA *private_key;
+  RSA *public_key;
+  char *passphrase, *comment;
+  int type, i;
+  int plen, clen;
+
+  /* Try to load identification for the authentication key. */
+  public_key = RSA_new();
+  if (!load_public_key(authfile, public_key, &comment)) {
+    RSA_free(public_key);
+    return 0; /* Could not load it.  Fail. */
+  }
+
+  debug("Trying RSA authentication with key '%.100s'", comment);
+
+  /* Tell the server that we are willing to authenticate using this key. */
+  packet_start(SSH_CMSG_AUTH_RSA);
+  packet_put_bignum(public_key->n);
+  packet_send();
+  packet_write_wait();
+
+  /* We no longer need the public key. */
+  RSA_free(public_key);
+  
+  /* Wait for server's response. */
+  type = packet_read(&plen);
+
+  /* The server responds with failure if it doesn\'t like our key or doesn\'t
+     support RSA authentication. */
+  if (type == SSH_SMSG_FAILURE)
+    {
+      debug("Server refused our key.");
+      xfree(comment);
+      return 0; /* Server refuses to authenticate with this key. */
+    }
+
+  /* Otherwise, the server should respond with a challenge. */
+  if (type != SSH_SMSG_AUTH_RSA_CHALLENGE)
+    packet_disconnect("Protocol error during RSA authentication: %d", type);
+
+  /* Get the challenge from the packet. */
+  challenge = BN_new();
+  packet_get_bignum(challenge, &clen);
+
+  packet_integrity_check(plen, clen, type);
+
+  debug("Received RSA challenge from server.");
+
+  private_key = RSA_new();
+  /* Load the private key.  Try first with empty passphrase; if it fails, 
+     ask for a passphrase. */
+  if (!load_private_key(authfile, "", private_key, NULL))
+    {
+      char buf[300];
+      /* Request passphrase from the user.  We read from /dev/tty to make
+         this work even if stdin has been redirected.  If running in
+	 batch mode, we just use the empty passphrase, which will fail and
+	 return. */
+      snprintf(buf, sizeof buf,
+	"Enter passphrase for RSA key '%.100s': ", comment);
+      if (may_ask_passphrase)
+	passphrase = read_passphrase(buf, 0);
+      else
+	{
+	  debug("Will not query passphrase for %.100s in batch mode.", 
+		comment);
+	  passphrase = xstrdup("");
+	}
+      
+      /* Load the authentication file using the pasphrase. */
+      if (!load_private_key(authfile, passphrase, private_key, NULL))
+	{
+	  memset(passphrase, 0, strlen(passphrase));
+	  xfree(passphrase);
+	  error("Bad passphrase.");
+
+	  /* Send a dummy response packet to avoid protocol error. */
+	  packet_start(SSH_CMSG_AUTH_RSA_RESPONSE);
+	  for (i = 0; i < 16; i++)
+	    packet_put_char(0);
+	  packet_send();
+	  packet_write_wait();
+
+	  /* Expect the server to reject it... */
+	  packet_read_expect(&plen, SSH_SMSG_FAILURE);
+	  xfree(comment);
+	  return 0;
+	}
+
+      /* Destroy the passphrase. */
+      memset(passphrase, 0, strlen(passphrase));
+      xfree(passphrase);
+    }
+  
+  /* We no longer need the comment. */
+  xfree(comment);
+
+  /* Compute and send a response to the challenge. */
+  respond_to_rsa_challenge(challenge, private_key);
+  
+  /* Destroy the private key. */
+  RSA_free(private_key);
+
+  /* We no longer need the challenge. */
+  BN_clear_free(challenge);
+  
+  /* Wait for response from the server. */
+  type = packet_read(&plen);
+  if (type == SSH_SMSG_SUCCESS)
+    {
+      debug("RSA authentication accepted by server.");
+      return 1;
+    }
+  if (type != SSH_SMSG_FAILURE)
+    packet_disconnect("Protocol error waiting RSA auth response: %d", type);
+  debug("RSA authentication refused.");
+  return 0;
+}
+
+/* Tries to authenticate the user using combined rhosts or /etc/hosts.equiv
+   authentication and RSA host authentication. */
+
+int
+try_rhosts_rsa_authentication(const char *local_user, RSA *host_key)
+{
+  int type;
+  BIGNUM *challenge;
+  int plen, clen;
+
+  debug("Trying rhosts or /etc/hosts.equiv with RSA host authentication.");
+
+  /* Tell the server that we are willing to authenticate using this key. */
+  packet_start(SSH_CMSG_AUTH_RHOSTS_RSA);
+  packet_put_string(local_user, strlen(local_user));
+  packet_put_int(BN_num_bits(host_key->n));
+  packet_put_bignum(host_key->e);
+  packet_put_bignum(host_key->n);
+  packet_send();
+  packet_write_wait();
+
+  /* Wait for server's response. */
+  type = packet_read(&plen);
+
+  /* The server responds with failure if it doesn't admit our .rhosts
+     authentication or doesn't know our host key. */
+  if (type == SSH_SMSG_FAILURE)
+    {
+      debug("Server refused our rhosts authentication or host key.");
+      return 0; /* Server refuses to authenticate us with this method. */
+    }
+
+  /* Otherwise, the server should respond with a challenge. */
+  if (type != SSH_SMSG_AUTH_RSA_CHALLENGE)
+    packet_disconnect("Protocol error during RSA authentication: %d", type);
+
+  /* Get the challenge from the packet. */
+  challenge = BN_new();
+  packet_get_bignum(challenge, &clen);
+
+  packet_integrity_check(plen, clen, type);
+
+  debug("Received RSA challenge for host key from server.");
+
+  /* Compute a response to the challenge. */
+  respond_to_rsa_challenge(challenge, host_key);
+
+  /* We no longer need the challenge. */
+  BN_clear_free(challenge);
+  
+  /* Wait for response from the server. */
+  type = packet_read(&plen);
+  if (type == SSH_SMSG_SUCCESS)
+    {
+      debug("Rhosts or /etc/hosts.equiv with RSA host authentication accepted by server.");
+      return 1;
+    }
+  if (type != SSH_SMSG_FAILURE)
+    packet_disconnect("Protocol error waiting RSA auth response: %d", type);
+  debug("Rhosts or /etc/hosts.equiv with RSA host authentication refused.");
+  return 0;
+}
+
+#ifdef KRB4
+int try_kerberos_authentication()
+{
+  KTEXT_ST auth;                     /* Kerberos data */
+  char *reply;
+  char inst[INST_SZ];
+  char *realm;
+  CREDENTIALS cred;
+  int r, type, plen;
+  Key_schedule schedule;
+  u_long checksum, cksum;
+  MSG_DAT msg_data;
+  struct sockaddr_in local, foreign;
+  struct stat st;
+
+  /* Don't do anything if we don't have any tickets. */
+  if (stat(tkt_string(), &st) < 0) return 0;
+  
+  strncpy(inst, (char *) krb_get_phost(get_canonical_hostname()), INST_SZ);
+  
+  realm = (char *)krb_realmofhost(get_canonical_hostname());
+  if (!realm) {
+    debug("Kerberos V4: no realm for %s", get_canonical_hostname());
+    return 0;
+  }
+  /* This can really be anything. */
+  checksum = (u_long) getpid();
+  
+  r = krb_mk_req(&auth, KRB4_SERVICE_NAME, inst, realm, checksum);
+  if (r != KSUCCESS) {
+    debug("Kerberos V4 krb_mk_req failed: %s", krb_err_txt[r]);
+    return 0;
+  }
+  /* Get session key to decrypt the server's reply with. */
+  r = krb_get_cred(KRB4_SERVICE_NAME, inst, realm, &cred);
+  if (r != KSUCCESS) {
+     debug("get_cred failed: %s", krb_err_txt[r]);
+     return 0;
+  }
+  des_key_sched((des_cblock *)cred.session, schedule);
+  
+  /* Send authentication info to server. */
+  packet_start(SSH_CMSG_AUTH_KERBEROS);
+  packet_put_string((char *)auth.dat, auth.length);
+  packet_send();
+  packet_write_wait();
+  
+  /* Zero the buffer. */
+  (void) memset(auth.dat, 0, MAX_KTXT_LEN);
+  
+  r = sizeof(local);
+  memset(&local, 0, sizeof(local));
+  if (getsockname(packet_get_connection_in(),
+ 		  (struct sockaddr *) &local, &r) < 0)
+    debug("getsockname failed: %s", strerror(errno));
+  
+  r = sizeof(foreign);
+  memset(&foreign, 0, sizeof(foreign));
+   if (getpeername(packet_get_connection_in(),
+		   (struct sockaddr *)&foreign, &r) < 0)
+     debug("getpeername failed: %s", strerror(errno));
+   
+   /* Get server reply. */
+   type = packet_read(&plen);
+   switch(type) {
+     
+   case SSH_SMSG_FAILURE: /* Should really be SSH_SMSG_AUTH_KERBEROS_FAILURE */
+     debug("Kerberos V4 authentication failed.");
+     return 0;
+     break;
+     
+   case SSH_SMSG_AUTH_KERBEROS_RESPONSE: /* SSH_SMSG_AUTH_KERBEROS_SUCCESS */
+     debug("Kerberos V4 authentication accepted.");
+     
+     /* Get server's response. */
+     reply = packet_get_string((unsigned int *)&auth.length);
+     memcpy(auth.dat, reply, auth.length);
+     xfree(reply);
+     
+     packet_integrity_check(plen, 4 + auth.length, type);
+
+     /* If his response isn't properly encrypted with the session key,
+        and the decrypted checksum fails to match, he's bogus. Bail out. */
+     r = krb_rd_priv(auth.dat, auth.length, schedule, &cred.session,
+		     &foreign, &local, &msg_data);
+     if (r != KSUCCESS) {
+       debug("Kerberos V4 krb_rd_priv failed: %s", krb_err_txt[r]);
+       packet_disconnect("Kerberos V4 challenge failed!");
+     }
+     /* Fetch the (incremented) checksum that we supplied in the request. */
+     (void)memcpy((char *)&cksum, (char *)msg_data.app_data, sizeof(cksum));
+     cksum = ntohl(cksum);
+     
+     /* If it matches, we're golden. */
+     if (cksum == checksum + 1) {
+       debug("Kerberos V4 challenge successful.");
+       return 1;
+     }
+     else
+       packet_disconnect("Kerberos V4 challenge failed!");
+     break;
+     
+   default:
+     packet_disconnect("Protocol error on Kerberos V4 response: %d", type);
+   }
+   return 0;
+}
+#endif /* KRB4 */
+
+#ifdef AFS
+int send_kerberos_tgt()
+{
+  CREDENTIALS *creds;
+  char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ];
+  int r, type, plen;
+  unsigned char buffer[8192];
+  struct stat st;
+
+  /* Don't do anything if we don't have any tickets. */
+  if (stat(tkt_string(), &st) < 0) return 0;
+    
+  creds = xmalloc(sizeof(*creds));
+  
+  if ((r = krb_get_tf_fullname(TKT_FILE, pname, pinst, prealm)) != KSUCCESS) {
+    debug("Kerberos V4 tf_fullname failed: %s",krb_err_txt[r]);
+    return 0;
+  }
+  if ((r = krb_get_cred("krbtgt", prealm, prealm, creds)) != GC_OK) {
+    debug("Kerberos V4 get_cred failed: %s", krb_err_txt[r]);
+    return 0;
+  }
+  if (time(0) > krb_life_to_time(creds->issue_date, creds->lifetime)) {
+    debug("Kerberos V4 ticket expired: %s", TKT_FILE);
+    return 0;
+  }
+
+  creds_to_radix(creds, buffer);
+  xfree(creds);
+    
+  packet_start(SSH_CMSG_HAVE_KERBEROS_TGT);
+  packet_put_string((char *)buffer, strlen(buffer));
+  packet_send();
+  packet_write_wait();
+
+  type = packet_read(&plen);
+  
+  if (type == SSH_SMSG_FAILURE)
+    debug("Kerberos TGT for realm %s rejected.", prealm);
+  else if (type != SSH_SMSG_SUCCESS)
+    packet_disconnect("Protocol error on Kerberos TGT response: %d", type);
+
+  return 1;
+}
+
+void send_afs_tokens(void)
+{
+  CREDENTIALS creds;
+  struct ViceIoctl parms;
+  struct ClearToken ct;
+  int i, type, len, plen;
+  char buf[2048], *p, *server_cell;
+  unsigned char buffer[8192];
+
+  /* Move over ktc_GetToken, here's something leaner. */
+  for (i = 0; i < 100; i++) { /* just in case */
+    parms.in = (char *)&i;
+    parms.in_size = sizeof(i);
+    parms.out = buf;
+    parms.out_size = sizeof(buf);
+    if (k_pioctl(0, VIOCGETTOK, &parms, 0) != 0) break;
+    p = buf;
+    
+    /* Get secret token. */
+    memcpy(&creds.ticket_st.length, p, sizeof(unsigned int));
+    if (creds.ticket_st.length > MAX_KTXT_LEN) break;
+    p += sizeof(unsigned int);
+    memcpy(creds.ticket_st.dat, p, creds.ticket_st.length);
+    p += creds.ticket_st.length;
+        
+    /* Get clear token. */
+    memcpy(&len, p, sizeof(len));
+    if (len != sizeof(struct ClearToken)) break;
+    p += sizeof(len);
+    memcpy(&ct, p, len);
+    p += len;
+    p += sizeof(len); /* primary flag */
+    server_cell = p;
+
+    /* Flesh out our credentials. */
+    strlcpy(creds.service, "afs", sizeof creds.service);
+    creds.instance[0] = '\0';
+    strlcpy(creds.realm, server_cell, REALM_SZ);
+    memcpy(creds.session, ct.HandShakeKey, DES_KEY_SZ);
+    creds.issue_date = ct.BeginTimestamp;
+    creds.lifetime = krb_time_to_life(creds.issue_date, ct.EndTimestamp);
+    creds.kvno = ct.AuthHandle;
+    snprintf(creds.pname, sizeof(creds.pname), "AFS ID %d", ct.ViceId);
+    creds.pinst[0] = '\0';
+
+    /* Encode token, ship it off. */
+    if (!creds_to_radix(&creds, buffer)) break;
+    packet_start(SSH_CMSG_HAVE_AFS_TOKEN);
+    packet_put_string((char *)buffer, strlen(buffer));
+    packet_send();
+    packet_write_wait();
+
+    /* Roger, Roger. Clearance, Clarence. What's your vector, Victor? */
+    type = packet_read(&plen);
+
+    if (type == SSH_SMSG_FAILURE)
+      debug("AFS token for cell %s rejected.", server_cell);
+    else if (type != SSH_SMSG_SUCCESS)
+      packet_disconnect("Protocol error on AFS token response: %d", type);
+  }  
+}
+#endif /* AFS */
+
+/* Waits for the server identification string, and sends our own identification
+   string. */
+
+void ssh_exchange_identification()
+{
+  char buf[256], remote_version[256]; /* must be same size! */
+  int remote_major, remote_minor, i;
+  int connection_in = packet_get_connection_in();
+  int connection_out = packet_get_connection_out();
+  extern Options options;
+
+  /* Read other side\'s version identification. */
+  for (i = 0; i < sizeof(buf) - 1; i++)
+    {
+      if (read(connection_in, &buf[i], 1) != 1)
+	fatal("read: %.100s", strerror(errno));
+      if (buf[i] == '\r')
+	{
+	  buf[i] = '\n';
+	  buf[i + 1] = 0;
+	  break;
+	}
+      if (buf[i] == '\n')
+	{
+	  buf[i + 1] = 0;
+	  break;
+	}
+    }
+  buf[sizeof(buf) - 1] = 0;
+  
+  /* Check that the versions match.  In future this might accept several
+     versions and set appropriate flags to handle them. */
+  if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, 
+	     remote_version) != 3)
+    fatal("Bad remote protocol version identification: '%.100s'", buf);
+  debug("Remote protocol version %d.%d, remote software version %.100s",
+	remote_major, remote_minor, remote_version);
+
+  /* Check if the remote protocol version is too old. */
+  if (remote_major == 1 && remote_minor < 3)
+    fatal("Remote machine has too old SSH software version.");
+
+  /* We speak 1.3, too. */
+  if (remote_major == 1 && remote_minor == 3) {
+    enable_compat13();
+    if (options.forward_agent && strcmp(remote_version, SSH_VERSION) != 0) {
+      log("Agent forwarding disabled, remote version '%s' is not compatible.",
+	    remote_version);
+      options.forward_agent = 0;
+    }
+  }
+#if 0
+  /* Removed for now, to permit compatibility with latter versions.  The server
+     will reject our version and disconnect if it doesn't support it. */
+  if (remote_major != PROTOCOL_MAJOR)
+    fatal("Protocol major versions differ: %d vs. %d",
+	  PROTOCOL_MAJOR, remote_major);
+#endif
+
+  /* Send our own protocol version identification. */
+  snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", 
+	  PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION);
+  if (write(connection_out, buf, strlen(buf)) != strlen(buf))
+    fatal("write: %.100s", strerror(errno));
+}
+
+int ssh_cipher_default = SSH_CIPHER_3DES;
+
+int read_yes_or_no(const char *prompt, int defval)
+{
+  char buf[1024];
+  FILE *f;
+  int retval = -1;
+      
+  if (isatty(0))
+    f = stdin;
+  else
+    f = fopen("/dev/tty", "rw");
+
+  if (f == NULL)
+    return 0;
+
+  fflush(stdout);
+
+  while (1)
+    {
+      fprintf(stderr, "%s", prompt);
+      if (fgets(buf, sizeof(buf), f) == NULL)
+	{
+	  /* Print a newline (the prompt probably didn\'t have one). */
+	  fprintf(stderr, "\n");
+	  strlcpy(buf, "no", sizeof buf);
+	}
+      /* Remove newline from response. */
+      if (strchr(buf, '\n'))
+	*strchr(buf, '\n') = 0;
+
+      if (buf[0] == 0)
+	retval = defval;
+      if (strcmp(buf, "yes") == 0)
+	retval = 1;
+      if (strcmp(buf, "no") == 0)
+	retval = 0;
+
+      if (retval != -1)
+	{
+	  if (f != stdin)
+	    fclose(f);
+	  return retval;
+	}
+    }
+}
+
+/* Starts a dialog with the server, and authenticates the current user on the
+   server.  This does not need any extra privileges.  The basic connection
+   to the server must already have been established before this is called. 
+   User is the remote user; if it is NULL, the current local user name will
+   be used.  Anonymous indicates that no rhosts authentication will be used.
+   If login fails, this function prints an error and never returns. 
+   This function does not require super-user privileges. */
+
+void ssh_login(int host_key_valid, 
+	       RSA *own_host_key,
+	       const char *orighost, 
+	       struct sockaddr_in *hostaddr,
+	       Options *options, uid_t original_real_uid)
+{
+  int i, type;
+  char *password;
+  struct passwd *pw;
+  BIGNUM *key;
+  RSA *host_key, *file_key;
+  RSA *public_key;
+  unsigned char session_key[SSH_SESSION_KEY_LENGTH];
+  const char *server_user, *local_user;
+  char *cp, *host, *ip = NULL;
+  unsigned char check_bytes[8];
+  unsigned int supported_ciphers, supported_authentications, protocol_flags;
+  HostStatus host_status;
+  HostStatus ip_status;
+  int host_ip_differ = 0;
+  int local = (ntohl(hostaddr->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET;
+  int payload_len, clen, sum_len = 0;
+  u_int32_t rand = 0;
+
+  if (options->check_host_ip)
+    ip = xstrdup(inet_ntoa(hostaddr->sin_addr));
+
+  /* Convert the user-supplied hostname into all lowercase. */
+  host = xstrdup(orighost);
+  for (cp = host; *cp; cp++)
+    if (isupper(*cp))
+      *cp = tolower(*cp);
+
+  /* Exchange protocol version identification strings with the server. */
+  ssh_exchange_identification();
+
+  /* Put the connection into non-blocking mode. */
+  packet_set_nonblocking();
+
+  /* Get local user name.  Use it as server user if no user name
+     was given. */
+  pw = getpwuid(original_real_uid);
+  if (!pw)
+    fatal("User id %d not found from user database.", original_real_uid);
+  local_user = xstrdup(pw->pw_name);
+  server_user = options->user ? options->user : local_user;
+
+  debug("Waiting for server public key.");
+
+  /* Wait for a public key packet from the server. */
+  packet_read_expect(&payload_len, SSH_SMSG_PUBLIC_KEY);
+
+  /* Get check bytes from the packet. */
+  for (i = 0; i < 8; i++)
+    check_bytes[i] = packet_get_char();
+
+  /* Get the public key. */
+  public_key = RSA_new();
+  packet_get_int();	/* bits */
+  public_key->e = BN_new();
+  packet_get_bignum(public_key->e, &clen);
+  sum_len += clen;
+  public_key->n = BN_new();
+  packet_get_bignum(public_key->n, &clen);
+  sum_len += clen;
+
+  /* Get the host key. */
+  host_key = RSA_new();
+  packet_get_int();	/* bits */
+  host_key->e = BN_new();
+  packet_get_bignum(host_key->e, &clen);
+  sum_len += clen;
+  host_key->n = BN_new();
+  packet_get_bignum(host_key->n, &clen);
+  sum_len += clen;
+
+  /* Store the host key from the known host file in here
+   * so that we can compare it with the key for the IP
+   * address. */
+  file_key = RSA_new();
+  file_key->n = BN_new();
+  file_key->e = BN_new();
+
+  /* Get protocol flags. */
+  protocol_flags = packet_get_int();
+  packet_set_protocol_flags(protocol_flags);
+
+  /* Get supported cipher types. */
+  supported_ciphers = packet_get_int();
+
+  /* Get supported authentication types. */
+  supported_authentications = packet_get_int();
+
+  debug("Received server public key (%d bits) and host key (%d bits).", 
+	BN_num_bits(public_key->n), BN_num_bits(host_key->n));
+
+  packet_integrity_check(payload_len,
+			 8 + 4 + sum_len + 0 + 4 + 0 + 0 + 4 + 4 + 4,
+			 SSH_SMSG_PUBLIC_KEY);
+
+  /* Compute the session id. */
+  compute_session_id(session_id, check_bytes, 
+		     BN_num_bits(host_key->n), host_key->n, 
+		     BN_num_bits(public_key->n), public_key->n);
+
+  /* Check if the host key is present in the user\'s list of known hosts
+     or in the systemwide list. */
+  host_status = check_host_in_hostfile(options->user_hostfile, 
+				       host, BN_num_bits(host_key->n), 
+				       host_key->e, host_key->n,
+				       file_key->e, file_key->n);
+  if (host_status == HOST_NEW)
+    host_status = check_host_in_hostfile(options->system_hostfile, host, 
+					 BN_num_bits(host_key->n),
+					 host_key->e, host_key->n,
+					 file_key->e, file_key->n);
+  /* Force accepting of the host key for localhost and 127.0.0.1.
+     The problem is that if the home directory is NFS-mounted to multiple
+     machines, localhost will refer to a different machine in each of them,
+     and the user will get bogus HOST_CHANGED warnings.  This essentially
+     disables host authentication for localhost; however, this is probably
+     not a real problem. */
+  if (local) {
+    debug("Forcing accepting of host key for localhost.");
+    host_status = HOST_OK;
+  }
+
+  /* Also perform check for the ip address, skip the check if we are
+     localhost or the hostname was an ip address to begin with */
+  if (options->check_host_ip && !local && strcmp(host, ip)) {
+    RSA *ip_key = RSA_new();
+    ip_key->n = BN_new();
+    ip_key->e = BN_new();
+    ip_status = check_host_in_hostfile(options->user_hostfile, ip,
+				       BN_num_bits(host_key->n),
+				       host_key->e, host_key->n,
+				       ip_key->e, ip_key->n);
+
+    if (ip_status == HOST_NEW)
+      ip_status = check_host_in_hostfile(options->system_hostfile, ip,
+					 BN_num_bits(host_key->n),
+					 host_key->e, host_key->n,
+					 ip_key->e, ip_key->n);
+    if (host_status == HOST_CHANGED &&
+	(ip_status != HOST_CHANGED || 
+	 (BN_cmp(ip_key->e, file_key->e) || BN_cmp(ip_key->n, file_key->n))))
+      host_ip_differ = 1;
+
+    RSA_free(ip_key);
+  } else
+    ip_status = host_status;
+
+  RSA_free(file_key);
+
+  switch (host_status) {
+  case HOST_OK:
+    /* The host is known and the key matches. */
+    debug("Host '%.200s' is known and matches the host key.", host);
+    if (options->check_host_ip) {
+      if (ip_status == HOST_NEW) {
+	if (!add_host_to_hostfile(options->user_hostfile, ip,
+				  BN_num_bits(host_key->n), 
+				  host_key->e, host_key->n))
+	  log("Failed to add the host ip to the list of known hosts (%.30s).", 
+	      options->user_hostfile);
+	else
+	  log("Warning: Permanently added host ip '%.30s' to the list of known hosts.", ip);
+      } else if (ip_status != HOST_OK)
+	log("Warning: the host key differ from the key of the ip address '%.30s' differs", ip);
+    }
+    
+    break;
+  case HOST_NEW:
+    {
+      char hostline[1000], *hostp = hostline;
+      /* The host is new. */
+      if (options->strict_host_key_checking == 1) {
+	/* User has requested strict host key checking.  We will not
+	   add the host key automatically.  The only alternative left
+	   is to abort. */
+	fatal("No host key is known for %.200s and you have requested strict checking.", host);
+      } else if (options->strict_host_key_checking == 2) { /* The default */
+	char prompt[1024];
+	snprintf(prompt, sizeof(prompt),
+		 "The authenticity of host '%.200s' can't be established.\n"
+		 "Are you sure you want to continue connecting (yes/no)? ",
+		 host);
+	if (!read_yes_or_no(prompt, -1))
+	  fatal("Aborted by user!\n");
+      }
+      
+      if (options->check_host_ip && ip_status == HOST_NEW && strcmp(host, ip))
+	snprintf(hostline, sizeof(hostline), "%s,%s", host, ip);
+      else
+	hostp = host;
+      
+      /* If not in strict mode, add the key automatically to the local
+	 known_hosts file. */
+      if (!add_host_to_hostfile(options->user_hostfile, hostp,
+				BN_num_bits(host_key->n), 
+				host_key->e, host_key->n))
+	log("Failed to add the host to the list of known hosts (%.500s).", 
+	    options->user_hostfile);
+      else
+	log("Warning: Permanently added '%.200s' to the list of known hosts.",
+	    hostp);
+      break;
+    }
+  case HOST_CHANGED:
+    if (options->check_host_ip) {
+      if (host_ip_differ) {
+	error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
+	error("@       WARNING: POSSIBLE DNS SPOOFING DETECTED!          @");
+	error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
+	error("The host key for %s has changed,", host);
+	error("but the key for the according IP address %s has", ip);
+	error("a different status.  This could either mean that DNS");
+	error("SPOOFING is happening or the IP address for the host");
+	error("and its host key have changed at the same time");
+      }
+    }
+    
+    /* The host key has changed. */
+    error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
+    error("@       WARNING: HOST IDENTIFICATION HAS CHANGED!         @");
+    error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
+    error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!");
+    error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!");
+    error("It is also possible that the host key has just been changed.");
+    error("Please contact your system administrator.");
+    error("Add correct host key in %.100s to get rid of this message.", 
+	  options->user_hostfile);
+    
+    /* If strict host key checking is in use, the user will have to edit
+       the key manually and we can only abort. */
+    if (options->strict_host_key_checking)
+      fatal("Host key for %.200s has changed and you have requested strict checking.", host);
+    
+    /* If strict host key checking has not been requested, allow the
+       connection but without password authentication or
+       agent forwarding. */
+    if (options->password_authentication) {
+      error("Password authentication is disabled to avoid trojan horses.");
+      options->password_authentication = 0;
+    }
+    if (options->forward_agent) {
+      error("Agent forwarding is disabled to avoid trojan horses.");
+      options->forward_agent = 0;
+    }
+    /* XXX Should permit the user to change to use the new id.  This could
+       be done by converting the host key to an identifying sentence, tell
+       that the host identifies itself by that sentence, and ask the user
+       if he/she whishes to accept the authentication. */
+    break;
+  }
+
+  if (options->check_host_ip)
+    xfree(ip);
+  
+  /* Generate a session key. */
+  arc4random_stir();
+  
+  /* Generate an encryption key for the session.   The key is a 256 bit
+     random number, interpreted as a 32-byte key, with the least significant
+     8 bits being the first byte of the key. */
+  for (i = 0; i < 32; i++) {
+    if (i % 4 == 0)
+      rand = arc4random();
+    session_key[i] = rand & 0xff;
+    rand >>= 8;
+  }
+
+  /* According to the protocol spec, the first byte of the session key is
+     the highest byte of the integer.  The session key is xored with the
+     first 16 bytes of the session id. */
+  key = BN_new();
+  BN_set_word(key, 0);
+  for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++)
+    {
+      BN_lshift(key, key, 8);
+      if (i < 16)
+	BN_add_word(key, session_key[i] ^ session_id[i]);
+      else
+	BN_add_word(key, session_key[i]);
+    }
+
+  /* Encrypt the integer using the public key and host key of the server
+     (key with smaller modulus first). */
+  if (BN_cmp(public_key->n, host_key->n) < 0)
+    {
+      /* Public key has smaller modulus. */
+      assert(BN_num_bits(host_key->n) >= 
+	     BN_num_bits(public_key->n) + SSH_KEY_BITS_RESERVED);
+
+      rsa_public_encrypt(key, key, public_key);
+      rsa_public_encrypt(key, key, host_key);
+    }
+  else
+    {
+      /* Host key has smaller modulus (or they are equal). */
+      assert(BN_num_bits(public_key->n) >=
+	     BN_num_bits(host_key->n) + SSH_KEY_BITS_RESERVED);
+
+      rsa_public_encrypt(key, key, host_key);
+      rsa_public_encrypt(key, key, public_key);
+    }
+
+  if (options->cipher == SSH_CIPHER_NOT_SET) {
+    if (cipher_mask() & supported_ciphers & (1 << ssh_cipher_default))
+      options->cipher = ssh_cipher_default;
+    else {
+      debug("Cipher %d not supported, using %.100s instead.",
+	    cipher_name(ssh_cipher_default),
+	    cipher_name(SSH_FALLBACK_CIPHER));
+      options->cipher = SSH_FALLBACK_CIPHER;
+    }
+  }
+
+  /* Check that the selected cipher is supported. */
+  if (!(supported_ciphers & (1 << options->cipher)))
+    fatal("Selected cipher type %.100s not supported by server.", 
+	  cipher_name(options->cipher));
+
+  debug("Encryption type: %.100s", cipher_name(options->cipher));
+
+  /* Send the encrypted session key to the server. */
+  packet_start(SSH_CMSG_SESSION_KEY);
+  packet_put_char(options->cipher);
+
+  /* Send the check bytes back to the server. */
+  for (i = 0; i < 8; i++)
+    packet_put_char(check_bytes[i]);
+
+  /* Send the encrypted encryption key. */
+  packet_put_bignum(key);
+
+  /* Send protocol flags. */
+  packet_put_int(SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN);
+
+  /* Send the packet now. */
+  packet_send();
+  packet_write_wait();
+
+  /* Destroy the session key integer and the public keys since we no longer
+     need them. */
+  BN_clear_free(key);
+  RSA_free(public_key);
+  RSA_free(host_key);
+
+  debug("Sent encrypted session key.");
+  
+  /* Set the encryption key. */
+  packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, 
+			    options->cipher, 1);
+
+  /* We will no longer need the session key here.  Destroy any extra copies. */
+  memset(session_key, 0, sizeof(session_key));
+
+  /* Expect a success message from the server.  Note that this message will
+     be received in encrypted form. */
+  packet_read_expect(&payload_len, SSH_SMSG_SUCCESS);
+
+  debug("Received encrypted confirmation.");
+
+  /* Send the name of the user to log in as on the server. */
+  packet_start(SSH_CMSG_USER);
+  packet_put_string(server_user, strlen(server_user));
+  packet_send();
+  packet_write_wait();
+
+  /* The server should respond with success if no authentication is needed
+     (the user has no password).  Otherwise the server responds with 
+     failure. */
+  type = packet_read(&payload_len);
+  if (type == SSH_SMSG_SUCCESS)
+    return;  /* Connection was accepted without authentication. */
+  if (type != SSH_SMSG_FAILURE)
+    packet_disconnect("Protocol error: got %d in response to SSH_CMSG_USER",
+		      type);
+  
+#ifdef AFS
+  /* Try Kerberos tgt passing if the server supports it. */
+  if ((supported_authentications & (1 << SSH_PASS_KERBEROS_TGT)) &&
+      options->kerberos_tgt_passing)
+    {
+      if (options->cipher == SSH_CIPHER_NONE)
+	log("WARNING: Encryption is disabled! Ticket will be transmitted in the clear!");
+      (void)send_kerberos_tgt();
+    }
+
+  /* Try AFS token passing if the server supports it. */
+  if ((supported_authentications & (1 << SSH_PASS_AFS_TOKEN)) &&
+      options->afs_token_passing && k_hasafs())  {
+    if (options->cipher == SSH_CIPHER_NONE)
+      log("WARNING: Encryption is disabled! Token will be transmitted in the clear!");
+    send_afs_tokens();
+  }
+#endif /* AFS */
+  
+#ifdef KRB4
+  if ((supported_authentications & (1 << SSH_AUTH_KERBEROS)) &&
+      options->kerberos_authentication)
+    {
+      debug("Trying Kerberos authentication.");
+      if (try_kerberos_authentication()) {
+        /* The server should respond with success or failure. */
+        type = packet_read(&payload_len);
+        if (type == SSH_SMSG_SUCCESS)
+          return; /* Successful connection. */
+        if (type != SSH_SMSG_FAILURE)
+          packet_disconnect("Protocol error: got %d in response to Kerberos auth", type);
+      }
+    }
+#endif /* KRB4 */
+  
+  /* Use rhosts authentication if running in privileged socket and we do not
+     wish to remain anonymous. */
+  if ((supported_authentications & (1 << SSH_AUTH_RHOSTS)) && 
+      options->rhosts_authentication)
+    {
+      debug("Trying rhosts authentication.");
+      packet_start(SSH_CMSG_AUTH_RHOSTS);
+      packet_put_string(local_user, strlen(local_user));
+      packet_send();
+      packet_write_wait();
+
+      /* The server should respond with success or failure. */
+      type = packet_read(&payload_len);
+      if (type == SSH_SMSG_SUCCESS)
+	return; /* Successful connection. */
+      if (type != SSH_SMSG_FAILURE)
+	packet_disconnect("Protocol error: got %d in response to rhosts auth",
+			  type);
+    }
+
+  /* Try .rhosts or /etc/hosts.equiv authentication with RSA host 
+     authentication. */
+  if ((supported_authentications & (1 << SSH_AUTH_RHOSTS_RSA)) &&
+      options->rhosts_rsa_authentication && host_key_valid)
+    {
+      if (try_rhosts_rsa_authentication(local_user, own_host_key))
+	return; /* Successful authentication. */
+    }
+
+  /* Try RSA authentication if the server supports it. */
+  if ((supported_authentications & (1 << SSH_AUTH_RSA)) &&
+      options->rsa_authentication)
+    {
+      /* Try RSA authentication using the authentication agent.  The agent
+         is tried first because no passphrase is needed for it, whereas
+	 identity files may require passphrases. */
+      if (try_agent_authentication())
+	return; /* Successful connection. */
+
+      /* Try RSA authentication for each identity. */
+      for (i = 0; i < options->num_identity_files; i++)
+	if (try_rsa_authentication(pw, options->identity_files[i],
+				   !options->batch_mode))
+	  return; /* Successful connection. */
+    }
+  
+  /* Try password authentication if the server supports it. */
+  if ((supported_authentications & (1 << SSH_AUTH_PASSWORD)) &&
+      options->password_authentication && !options->batch_mode)
+    {
+      char prompt[80];
+      snprintf(prompt, sizeof(prompt), "%.30s@%.30s's password: ",
+	server_user, host);
+      debug("Doing password authentication.");
+      if (options->cipher == SSH_CIPHER_NONE)
+	log("WARNING: Encryption is disabled! Password will be transmitted in clear text.");
+      for (i = 0; i < options->number_of_password_prompts; i++) {
+        if (i != 0)
+	  error("Permission denied, please try again.");
+	password = read_passphrase(prompt, 0);
+	packet_start(SSH_CMSG_AUTH_PASSWORD);
+	packet_put_string(password, strlen(password));
+	memset(password, 0, strlen(password));
+	xfree(password);
+	packet_send();
+	packet_write_wait();
+	
+	type = packet_read(&payload_len);
+	if (type == SSH_SMSG_SUCCESS)
+	  return; /* Successful connection. */
+	if (type != SSH_SMSG_FAILURE)
+	  packet_disconnect("Protocol error: got %d in response to passwd auth", type);
+      }
+    }
+
+  /* All authentication methods have failed.  Exit with an error message. */
+  fatal("Permission denied.");
+  /*NOTREACHED*/
+}
diff --git a/sshd.8 b/sshd.8
new file mode 100644
index 0000000..981c5ff
--- /dev/null
+++ b/sshd.8
@@ -0,0 +1,781 @@
+.\"  -*- nroff -*-
+.\"
+.\" sshd.8.in
+.\"
+.\" Author: Tatu Ylonen <ylo@cs.hut.fi>
+.\"
+.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+.\"                    All rights reserved
+.\"
+.\" Created: Sat Apr 22 21:55:14 1995 ylo
+.\"
+.\" $Id: sshd.8,v 1.1 1999/10/27 03:42:46 damien Exp $
+.\"
+.Dd September 25, 1999
+.Dt SSHD 8
+.Os
+.Sh NAME
+.Nm sshd
+.Nd secure shell daemon
+.Sh SYNOPSIS
+.Nm sshd
+.Op Fl diq
+.Op Fl b Ar bits
+.Op Fl f Ar config_file
+.Op Fl g Ar login_grace_time
+.Op Fl h Ar host_key_file
+.Op Fl k Ar key_gen_time
+.Op Fl p Ar port
+.Sh DESCRIPTION 
+.Nm
+(Secure Shell Daemon) is the daemon program for 
+.Xr ssh 1 .
+Together these programs replace rlogin and rsh programs, and
+provide secure encrypted communications between two untrusted hosts
+over an insecure network.  The programs are intended to be as easy to
+install and use as possible.
+.Pp
+.Nm
+is the daemon that listens for connections from clients.  It is
+normally started at boot from 
+.Pa /etc/rc .
+It forks a new
+daemon for each incoming connection.  The forked daemons handle
+key exchange, encryption, authentication, command execution,
+and data exchange.
+.Pp
+.Nm
+works as follows.  Each host has a host-specific RSA key
+(normally 1024 bits) used to identify the host.  Additionally, when
+the daemon starts, it generates a server RSA key (normally 768 bits).
+This key is normally regenerated every hour if it has been used, and
+is never stored on disk.
+.Pp
+Whenever a client connects the daemon, the daemon sends its host
+and server public keys to the client.  The client compares the
+host key against its own database to verify that it has not changed.
+The client then generates a 256 bit random number.  It encrypts this
+random number using both the host key and the server key, and sends
+the encrypted number to the server.  Both sides then start to use this
+random number as a session key which is used to encrypt all further
+communications in the session.  The rest of the session is encrypted
+using a conventional cipher, currently Blowfish and 3DES, with 3DES
+being is used by default.  The client selects the encryption algorithm
+to use from those offered by the server.
+.Pp
+Next, the server and the client enter an authentication dialog.  The
+client tries to authenticate itself using
+.Pa .rhosts
+authentication,
+.Pa .rhosts
+authentication combined with RSA host
+authentication, RSA challenge-response authentication, or password
+based authentication.
+.Pp
+Rhosts authentication is normally disabled
+because it is fundamentally insecure, but can be enabled in the server
+configuration file if desired.  System security is not improved unless
+.Xr rshd 8 ,
+.Xr rlogind 8 ,
+.Xr rexecd 8 ,
+and
+.Xr rexd 8
+are disabled (thus completely disabling
+.Xr rlogin 1
+and
+.Xr rsh 1
+into that machine).
+.Pp
+If the client successfully authenticates itself, a dialog for
+preparing the session is entered.  At this time the client may request
+things like allocating a pseudo-tty, forwarding X11 connections,
+forwarding TCP/IP connections, or forwarding the authentication agent
+connection over the secure channel.
+.Pp
+Finally, the client either requests a shell or execution of a command.
+The sides then enter session mode.  In this mode, either side may send
+data at any time, and such data is forwarded to/from the shell or
+command on the server side, and the user terminal in the client side.
+.Pp
+When the user program terminates and all forwarded X11 and other
+connections have been closed, the server sends command exit status to
+the client, and both sides exit.
+.Pp
+.Nm
+can be configured using command-line options or a configuration
+file.  Command-line options override values specified in the
+configuration file.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl b Ar bits
+Specifies the number of bits in the server key (default 768).
+.Pp
+.It Fl d
+Debug mode.  The server sends verbose debug output to the system
+log, and does not put itself in the background.  The server also will
+not fork and will only process one connection.  This option is only
+intended for debugging for the server.
+.It Fl f Ar configuration_file
+Specifies the name of the configuration file.  The default is
+.Pa /etc/sshd_config .
+.Nm
+refuses to start if there is no configuration file.
+.It Fl g Ar login_grace_time
+Gives the grace time for clients to authenticate themselves (default
+300 seconds).  If the client fails to authenticate the user within
+this many seconds, the server disconnects and exits.  A value of zero
+indicates no limit.
+.It Fl h Ar host_key_file
+Specifies the file from which the host key is read (default
+.Pa /etc/ssh_host_key ) .
+This option must be given if
+.Nm
+is not run as root (as the normal
+host file is normally not readable by anyone but root).
+.It Fl i
+Specifies that
+.Nm
+is being run from inetd. 
+.Nm
+is normally not run
+from inetd because it needs to generate the server key before it can
+respond to the client, and this may take tens of seconds.  Clients
+would have to wait too long if the key was regenerated every time.
+However, with small key sizes (e.g.  512) using
+.Nm
+from inetd may
+be feasible.
+.It Fl k Ar key_gen_time
+Specifies how often the server key is regenerated (default 3600
+seconds, or one hour).  The motivation for regenerating the key fairly
+often is that the key is not stored anywhere, and after about an hour,
+it becomes impossible to recover the key for decrypting intercepted
+communications even if the machine is cracked into or physically
+seized.  A value of zero indicates that the key will never be regenerated.
+.It Fl p Ar port
+Specifies the port on which the server listens for connections
+(default 22).
+.It Fl q
+Quiet mode.  Nothing is sent to the system log.  Normally the beginning,
+authentication, and termination of each connection is logged.
+.It Fl Q
+Do not print an error message if RSA support is missing.
+.El
+.Sh CONFIGURATION FILE
+.Nm
+reads configuration data from 
+.Pa /etc/sshd_config
+(or the file specified with
+.Fl f
+on the command line).  The file
+contains keyword-value pairs, one per line.  Lines starting with
+.Ql #
+and empty lines are interpreted as comments.
+.Pp
+The following keywords are possible.
+.Bl -tag -width Ds
+.It Cm AFSTokenPassing
+Specifies whether an AFS token may be forwarded to the server. Default is
+.Dq yes .
+.It Cm AllowGroups
+This keyword can be followed by a number of group names, separated
+by spaces.  If specified, login is allowed only for users whose primary
+group matches one of the patterns.
+.Ql \&*
+and
+.Ql ?
+can be used as
+wildcards in the patterns.  Only group names are valid, a numerical group
+id isn't recognized.  By default login is allowed regardless of
+the primary group.
+.Pp
+.It Cm AllowUsers
+This keyword can be followed by a number of user names, separated
+by spaces.  If specified, login is allowed only for users names that
+match one of the patterns.
+.Ql \&*
+and
+.Ql ?
+can be used as
+wildcards in the patterns.  Only user names are valid, a numerical user
+id isn't recognized.  By default login is allowed regardless of
+the user name.
+.Pp
+.It Cm CheckMail
+Specifies whether
+.Nm
+should check for new mail for interactive logins.
+The default is
+.Dq no .
+.It Cm DenyGroups
+This keyword can be followed by a number of group names, separated
+by spaces.  Users whose primary group matches one of the patterns
+aren't allowed to log in.
+.Ql \&*
+and
+.Ql ?
+can be used as
+wildcards in the patterns.  Only group names are valid, a numerical group
+id isn't recognized.  By default login is allowed regardless of
+the primary group.
+.Pp
+.It Cm DenyUsers
+This keyword can be followed by a number of user names, separated
+by spaces.  Login is allowed disallowed for user names that match
+one of the patterns.
+.Ql \&*
+and
+.Ql ?
+can be used as
+wildcards in the patterns.  Only user names are valid, a numerical user
+id isn't recognized.  By default login is allowed regardless of
+the user name.
+.Pp
+.It Cm FascistLogging
+Specifies whether to use verbose logging.  Verbose logging violates
+the privacy of users and is not recommended.  The argument must be
+.Dq yes
+or
+.Dq no .
+The default is
+.Dq no .
+.It Cm HostKey
+Specifies the file containing the private host key (default
+.Pa /etc/ssh_host_key ) .
+Note that
+.Nm
+does not start if this file is group/world-accessible.
+.It Cm IgnoreRhosts
+Specifies that rhosts and shosts files will not be used in
+authentication.
+.Pa /etc/hosts.equiv
+and
+.Pa /etc/shosts.equiv 
+are still used.  The default is 
+.Dq no .
+.It Cm KeepAlive
+Specifies whether the system should send keepalive messages to the
+other side.  If they are sent, death of the connection or crash of one
+of the machines will be properly noticed.  However, this means that
+connections will die if the route is down temporarily, and some people
+find it annoying.  On the other hand, if keepalives are not send,
+sessions may hang indefinitely on the server, leaving
+.Dq ghost
+users and consuming server resources.
+.Pp
+The default is
+.Dq yes
+(to send keepalives), and the server will notice
+if the network goes down or the client host reboots.  This avoids
+infinitely hanging sessions.
+.Pp
+To disable keepalives, the value should be set to
+.Dq no
+in both the server and the client configuration files.
+.It Cm KerberosAuthentication
+Specifies whether Kerberos authentication is allowed. This can
+be in the form of a Kerberos ticket, or if
+.Cm PasswordAuthentication
+is yes, the password provided by the user will be validated through
+the Kerberos KDC. Default is
+.Dq yes .
+.It Cm KerberosOrLocalPasswd
+If set then if password authentication through Kerberos fails then
+the password will be validated via any additional local mechanism
+such as
+.Pa /etc/passwd
+or SecurID. Default is
+.Dq yes .
+.It Cm KerberosTgtPassing
+Specifies whether a Kerberos TGT may be forwarded to the server.
+Default is 
+.Dq no ,
+as this only works when the Kerberos KDC is actually an AFS kaserver.
+.It Cm KerberosTicketCleanup
+Specifies whether to automatically destroy the user's ticket cache
+file on logout. Default is
+.Dq yes .
+.It Cm KeyRegenerationInterval
+The server key is automatically regenerated after this many seconds
+(if it has been used).  The purpose of regeneration is to prevent
+decrypting captured sessions by later breaking into the machine and
+stealing the keys.  The key is never stored anywhere.  If the value is
+0, the key is never regenerated.  The default is 3600
+(seconds).
+.It Cm ListenAddress
+Specifies what local address
+.Nm
+should listen on.
+The default is to listen to all local addresses.
+.It Cm LoginGraceTime
+The server disconnects after this time if the user has not
+successfully logged in.  If the value is 0, there is no time limit.
+The default is 600 (seconds).
+.It Cm PasswordAuthentication
+Specifies whether password authentication is allowed.
+The default is
+.Dq yes .
+.It Cm PermitEmptyPasswords
+When password authentication is allowed, it specifies whether the
+server allows login to accounts with empty password strings.  The default
+is
+.Dq yes .
+.It Cm PermitRootLogin
+Specifies whether the root can log in using
+.Xr ssh 1 .
+The argument must be
+.Dq yes ,
+.Dq without-password
+or
+.Dq no .
+The default is
+.Dq yes .
+If this options is set to
+.Dq without-password
+only password authentication is disabled for root.
+.Pp
+Root login with RSA authentication when the
+.Ar command
+option has been
+specified will be allowed regardless of the value of this setting
+(which may be useful for taking remote backups even if root login is
+normally not allowed).
+.It Cm Port
+Specifies the port number that
+.Nm
+listens on.  The default is 22.
+.It Cm PrintMotd
+Specifies whether
+.Nm
+should print 
+.Pa /etc/motd
+when a user logs in interactively.  (On some systems it is also
+printed by the shell,
+.Pa /etc/profile ,
+or equivalent.)  The default is
+.Dq yes .
+.It Cm QuietMode
+Specifies whether the system runs in quiet mode.  In quiet mode,
+nothing is logged in the system log, except fatal errors.  The default
+is
+.Dq no .
+.It Cm RandomSeed
+Obsolete.  Random number generation uses other techniques.
+.It Cm RhostsAuthentication
+Specifies whether authentication using rhosts or /etc/hosts.equiv
+files is sufficient.  Normally, this method should not be permitted
+because it is insecure. 
+.Cm RhostsRSAAuthentication
+should be used
+instead, because it performs RSA-based host authentication in addition
+to normal rhosts or /etc/hosts.equiv authentication.
+The default is
+.Dq no .
+.It Cm RhostsRSAAuthentication
+Specifies whether rhosts or /etc/hosts.equiv authentication together
+with successful RSA host authentication is allowed.  The default is
+.Dq yes .
+.It Cm RSAAuthentication
+Specifies whether pure RSA authentication is allowed.  The default is
+.Dq yes .
+.It Cm ServerKeyBits
+Defines the number of bits in the server key.  The minimum value is
+512, and the default is 768.
+.It Cm SkeyAuthentication
+Specifies whether
+.Xr skey 1 
+authentication is allowed.  The default is
+.Dq yes .
+Note that s/key authentication is enabled only if
+.Cm PasswordAuthentication
+is allowed, too.
+.It Cm StrictModes
+Specifies whether
+.Nm
+should check file modes and ownership of the
+user's files and home directory before accepting login.  This
+is normally desirable because novices sometimes accidentally leave their
+directory or files world-writable.  The default is
+.Dq yes .
+.It Cm SyslogFacility
+Gives the facility code that is used when logging messages from
+.Nm sshd .
+The possible values are: DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2,
+LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7.  The default is AUTH.
+.It Cm UseLogin
+Specifies whether
+.Xr login 1
+is used. The default is
+.Dq no .
+.It Cm X11Forwarding
+Specifies whether X11 forwarding is permitted.  The default is
+.Dq yes .
+Note that disabling X11 forwarding does not improve security in any
+way, as users can always install their own forwarders.
+.It Cm X11DisplayOffset
+Specifies the first display number available for
+.Nm sshd Ns 's
+X11 forwarding.  This prevents
+.Nm
+from interfering with real X11 servers.
+.El
+.Sh LOGIN PROCESS
+When a user successfully logs in,
+.Nm
+does the following:
+.Bl -enum -offset indent
+.It
+If the login is on a tty, and no command has been specified,
+prints last login time and 
+.Pa /etc/motd
+(unless prevented in the configuration file or by
+.Pa $HOME/.hushlogin ;
+see the
+.Sx FILES 
+section).
+.It
+If the login is on a tty, records login time.
+.It
+Checks
+.Pa /etc/nologin ;
+if it exists, prints contents and quits
+(unless root).
+.It
+Changes to run with normal user privileges.
+.It
+Sets up basic environment.
+.It
+Reads
+.Pa $HOME/.ssh/environment
+if it exists.
+.It
+Changes to user's home directory.
+.It
+If
+.Pa $HOME/.ssh/rc
+exists, runs it; else if
+.Pa /etc/sshrc
+exists, runs
+it; otherwise runs xauth.  The
+.Dq rc
+files are given the X11
+authentication protocol and cookie in standard input.
+.It
+Runs user's shell or command.
+.El
+.Sh AUTHORIZED_KEYS FILE FORMAT
+The 
+.Pa $HOME/.ssh/authorized_keys
+file lists the RSA keys that are
+permitted for RSA authentication.  Each line of the file contains one
+key (empty lines and lines starting with a
+.Ql #
+are ignored as
+comments).  Each line consists of the following fields, separated by
+spaces: options, bits, exponent, modulus, comment.  The options field
+is optional; its presence is determined by whether the line starts
+with a number or not (the option field never starts with a number).
+The bits, exponent, modulus and comment fields give the RSA key; the
+comment field is not used for anything (but may be convenient for the
+user to identify the key).
+.Pp
+Note that lines in this file are usually several hundred bytes long
+(because of the size of the RSA key modulus).  You don't want to type
+them in; instead, copy the 
+.Pa identity.pub
+file and edit it.
+.Pp
+The options (if present) consists of comma-separated option
+specifications.  No spaces are permitted, except within double quotes.
+The following option specifications are supported:
+.Bl -tag -width Ds
+.It Cm from="pattern-list"
+Specifies that in addition to RSA authentication, the canonical name
+of the remote host must be present in the comma-separated list of
+patterns ('*' and '?' serve as wildcards).  The list may also contain
+patterns negated by prefixing them with '!'; if the canonical host
+name matches a negated pattern, the key is not accepted.  The purpose
+of this option is to optionally increase security: RSA authentication
+by itself does not trust the network or name servers or anything (but
+the key); however, if somebody somehow steals the key, the key
+permits an intruder to log in from anywhere in the world.  This
+additional option makes using a stolen key more difficult (name
+servers and/or routers would have to be compromised in addition to
+just the key).
+.It Cm command="command"
+Specifies that the command is executed whenever this key is used for
+authentication.  The command supplied by the user (if any) is ignored.
+The command is run on a pty if the connection requests a pty;
+otherwise it is run without a tty.  A quote may be included in the
+command by quoting it with a backslash.  This option might be useful
+to restrict certain RSA keys to perform just a specific operation.  An
+example might be a key that permits remote backups but nothing
+else.  Notice that the client may specify TCP/IP and/or X11
+forwardings unless they are explicitly prohibited.
+.It Cm environment="NAME=value"
+Specifies that the string is to be added to the environment when
+logging in using this key.  Environment variables set this way
+override other default environment values.  Multiple options of this
+type are permitted.
+.It Cm no-port-forwarding
+Forbids TCP/IP forwarding when this key is used for authentication.
+Any port forward requests by the client will return an error.  This
+might be used, e.g., in connection with the
+.Cm command
+option.
+.It Cm no-X11-forwarding
+Forbids X11 forwarding when this key is used for authentication.
+Any X11 forward requests by the client will return an error.
+.It Cm no-agent-forwarding
+Forbids authentication agent forwarding when this key is used for
+authentication.
+.It Cm no-pty
+Prevents tty allocation (a request to allocate a pty will fail).
+.El
+.Ss Examples
+1024 33 12121.\|.\|.\|312314325 ylo@foo.bar
+.Pp
+from="*.niksula.hut.fi,!pc.niksula.hut.fi" 1024 35 23.\|.\|.\|2334 ylo@niksula
+.Pp
+command="dump /home",no-pty,no-port-forwarding 1024 33 23.\|.\|.\|2323 backup.hut.fi
+.Sh SSH_KNOWN_HOSTS FILE FORMAT
+The 
+.Pa /etc/ssh_known_hosts
+and 
+.Pa $HOME/.ssh/known_hosts
+files contain host public keys for all known hosts.  The global file should
+be prepared by the admistrator (optional), and the per-user file is
+maintained automatically: whenever the user connects an unknown host
+its key is added to the per-user file.  
+.Pp
+Each line in these files contains the following fields: hostnames,
+bits, exponent, modulus, comment.  The fields are separated by spaces.
+.Pp
+Hostnames is a comma-separated list of patterns ('*' and '?' act as
+wildcards); each pattern in turn is matched against the canonical host
+name (when authenticating a client) or against the user-supplied
+name (when authenticating a server).  A pattern may also be preceded
+by
+.Ql !
+to indicate negation: if the host name matches a negated
+pattern, it is not accepted (by that line) even if it matched another
+pattern on the line.
+.Pp
+Bits, exponent, and modulus are taken directly from the host key; they
+can be obtained, e.g., from
+.Pa /etc/ssh_host_key.pub .
+The optional comment field continues to the end of the line, and is not used.
+.Pp
+Lines starting with
+.Ql #
+and empty lines are ignored as comments.
+.Pp
+When performing host authentication, authentication is accepted if any
+matching line has the proper key.  It is thus permissible (but not
+recommended) to have several lines or different host keys for the same
+names.  This will inevitably happen when short forms of host names
+from different domains are put in the file.  It is possible
+that the files contain conflicting information; authentication is
+accepted if valid information can be found from either file.
+.Pp
+Note that the lines in these files are typically hundreds of characters
+long, and you definitely don't want to type in the host keys by hand.
+Rather, generate them by a script
+or by taking 
+.Pa /etc/ssh_host_key.pub
+and adding the host names at the front.
+.Ss Examples
+closenet,closenet.hut.fi,.\|.\|.\|,130.233.208.41 1024 37 159.\|.\|.93 closenet.hut.fi
+.Sh FILES
+.Bl -tag -width Ds
+.It Pa /etc/sshd_config
+Contains configuration data for
+.Nm sshd .
+This file should be writable by root only, but it is recommended
+(though not necessary) that it be world-readable.
+.It Pa /etc/ssh_host_key
+Contains the private part of the host key.
+This file should only be owned by root, readable only by root, and not
+accessible to others.
+Note that
+.Nm
+does not start if this file is group/world-accessible.
+.It Pa /etc/ssh_host_key.pub
+Contains the public part of the host key.
+This file should be world-readable but writable only by
+root.  Its contents should match the private part.  This file is not
+really used for anything; it is only provided for the convenience of
+the user so its contents can be copied to known hosts files.
+These two files are created using
+.Xr ssh-keygen 1 .
+.It Pa /var/run/sshd.pid
+Contains the process ID of the
+.Nm
+listening for connections (if there are several daemons running
+concurrently for different ports, this contains the pid of the one
+started last).  The contents of this file are not sensitive; it can be
+world-readable.
+.It Pa $HOME/.ssh/authorized_keys
+Lists the RSA keys that can be used to log into the user's account.
+This file must be readable by root (which may on some machines imply
+it being world-readable if the user's home directory resides on an NFS
+volume).  It is recommended that it not be accessible by others.  The
+format of this file is described above.
+.It Pa /etc/ssh_known_hosts
+This file is consulted when using rhosts with RSA host
+authentication to check the public key of the host.  The key must be
+listed in this file to be accepted.
+.It Pa $HOME/.ssh/known_hosts
+The client uses this file
+and
+.Pa /etc/ssh_known_hosts
+to verify that the remote host is the one we intended to
+connect. These files should be writable only by root/the owner.
+.Pa /etc/ssh_known_hosts
+should be world-readable, and
+.Pa $HOME/.ssh/known_hosts
+can but need not be world-readable.
+.It Pa /etc/nologin
+If this file exists, 
+.Nm
+refuses to let anyone except root log in.  The contents of the file
+are displayed to anyone trying to log in, and non-root connections are
+refused.  The file should be world-readable.
+.It Pa /etc/hosts.allow, /etc/hosts.deny
+If compiled with
+.Sy LIBWRAP
+support, tcp-wrappers access controls may be defined here as described in
+.Xr hosts_access 5 .
+.It Pa $HOME/.rhosts
+This file contains host-username pairs, separated by a space, one per
+line.  The given user on the corresponding host is permitted to log in
+without password.  The same file is used by rlogind and rshd.
+The file must
+be writable only by the user; it is recommended that it not be
+accessible by others.
+.Pp
+If is also possible to use netgroups in the file.  Either host or user
+name may be of the form +@groupname to specify all hosts or all users
+in the group.
+.It Pa $HOME/.shosts
+For ssh,
+this file is exactly the same as for
+.Pa .rhosts .
+However, this file is
+not used by rlogin and rshd, so using this permits access using SSH only.
+.Pa /etc/hosts.equiv
+This file is used during
+.Pa .rhosts
+authentication.  In the
+simplest form, this file contains host names, one per line.  Users on
+those hosts are permitted to log in without a password, provided they
+have the same user name on both machines.  The host name may also be
+followed by a user name; such users are permitted to log in as
+.Em any
+user on this machine (except root).  Additionally, the syntax
+.Dq +@group
+can be used to specify netgroups.  Negated entries start with
+.Ql \&- .
+.Pp
+If the client host/user is successfully matched in this file, login is
+automatically permitted provided the client and server user names are the
+same.  Additionally, successful RSA host authentication is normally
+required.  This file must be writable only by root; it is recommended
+that it be world-readable.
+.Pp
+.Sy "Warning: It is almost never a good idea to use user names in"
+.Pa hosts.equiv .
+Beware that it really means that the named user(s) can log in as
+.Em anybody ,
+which includes bin, daemon, adm, and other accounts that own critical
+binaries and directories.  Using a user name practically grants the
+user root access.  The only valid use for user names that I can think
+of is in negative entries.
+.Pp
+Note that this warning also applies to rsh/rlogin.
+.It Pa /etc/shosts.equiv
+This is processed exactly as
+.Pa /etc/hosts.equiv .
+However, this file may be useful in environments that want to run both
+rsh/rlogin and ssh.
+.It Pa $HOME/.ssh/environment
+This file is read into the environment at login (if it exists).  It
+can only contain empty lines, comment lines (that start with
+.Ql # ) ,
+and assignment lines of the form name=value.  The file should be writable
+only by the user; it need not be readable by anyone else.
+.It Pa $HOME/.ssh/rc
+If this file exists, it is run with /bin/sh after reading the
+environment files but before starting the user's shell or command.  If
+X11 spoofing is in use, this will receive the "proto cookie" pair in
+standard input (and
+.Ev DISPLAY
+in environment).  This must call
+.Xr xauth 1
+in that case.
+.Pp
+The primary purpose of this file is to run any initialization routines
+which may be needed before the user's home directory becomes
+accessible; AFS is a particular example of such an environment.
+.Pp
+This file will probably contain some initialization code followed by
+something similar to: "if read proto cookie; then echo add $DISPLAY
+$proto $cookie | xauth -q -; fi".
+.Pp
+If this file does not exist,
+.Pa /etc/sshrc
+is run, and if that
+does not exist either, xauth is used to store the cookie.
+.Pp
+This file should be writable only by the user, and need not be
+readable by anyone else.
+.It Pa /etc/sshrc
+Like
+.Pa $HOME/.ssh/rc .
+This can be used to specify
+machine-specific login-time initializations globally.  This file
+should be writable only by root, and should be world-readable.
+.Sh AUTHOR
+Tatu Ylonen <ylo@cs.hut.fi>
+.Pp
+Information about new releases, mailing lists, and other related
+issues can be found from the SSH WWW home page:
+.Pp
+.Dl http://www.cs.hut.fi/ssh.
+.Pp
+OpenSSH
+is a derivative of the original (free) ssh 1.2.12 release, but with bugs
+removed and newer features re-added.   Rapidly after the 1.2.12 release,
+newer versions bore successively more restrictive licenses.  This version
+of OpenSSH
+.Bl -bullet
+.It
+has all components of a restrictive nature (ie. patents, see
+.Xr ssl 8 )
+directly removed from the source code; any licensed or patented components
+are chosen from
+external libraries.
+.It
+has been updated to support ssh protocol 1.5.
+.It
+contains added support for 
+.Xr kerberos 8
+authentication and ticket passing.
+.It
+supports one-time password authentication with
+.Xr skey 1 .
+.El
+.Pp
+The libraries described in
+.Xr ssl 8
+are required for proper operation.
+.Sh SEE ALSO
+.Xr rlogin 1 ,
+.Xr rsh 1 ,
+.Xr scp 1 ,
+.Xr ssh 1 ,
+.Xr ssh-add 1 ,
+.Xr ssh-agent 1 ,
+.Xr ssh-keygen 1 ,
+.Xr ssl 8
diff --git a/sshd.c b/sshd.c
new file mode 100644
index 0000000..059f311
--- /dev/null
+++ b/sshd.c
@@ -0,0 +1,2445 @@
+/*
+
+sshd.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Fri Mar 17 17:09:28 1995 ylo
+
+This program is the ssh daemon.  It listens for connections from clients, and
+performs authentication, executes use commands or shell, and forwards
+information to/from the application to the user client over an encrypted
+connection.  This can also handle forwarding of X11, TCP/IP, and authentication
+agent connections.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: sshd.c,v 1.1 1999/10/27 03:42:46 damien Exp $");
+
+#include "xmalloc.h"
+#include "rsa.h"
+#include "ssh.h"
+#include "pty.h"
+#include "packet.h"
+#include "buffer.h"
+#include "cipher.h"
+#include "mpaux.h"
+#include "servconf.h"
+#include "uidswap.h"
+#include "compat.h"
+
+#ifdef LIBWRAP
+#include <tcpd.h>
+#include <syslog.h>
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_WARNING;
+#endif /* LIBWRAP */
+
+#ifndef O_NOCTTY
+#define O_NOCTTY	0
+#endif
+
+#ifdef KRB4
+char *ticket = NULL;
+#endif /* KRB4 */
+
+#ifdef HAVE_PAM
+#include <security/pam_appl.h>
+struct pam_handle_t *pamh=NULL;
+char *pampasswd=NULL;
+int retval;
+int origretval;
+#endif /* HAVE_PAM */
+
+/* Local Xauthority file. */
+char *xauthfile = NULL;
+
+/* Server configuration options. */
+ServerOptions options;
+
+/* Name of the server configuration file. */
+char *config_file_name = SERVER_CONFIG_FILE;
+
+/* Debug mode flag.  This can be set on the command line.  If debug
+   mode is enabled, extra debugging output will be sent to the system
+   log, the daemon will not go to background, and will exit after processing
+   the first connection. */
+int debug_flag = 0;
+
+/* Flag indicating that the daemon is being started from inetd. */
+int inetd_flag = 0;
+
+/* argv[0] without path. */
+char *av0;
+
+/* Saved arguments to main(). */
+char **saved_argv;
+
+/* This is set to the socket that the server is listening; this is used in
+   the SIGHUP signal handler. */
+int listen_sock;
+
+/* Flags set in auth-rsa from authorized_keys flags.  These are set in
+  auth-rsa.c. */
+int no_port_forwarding_flag = 0;
+int no_agent_forwarding_flag = 0;
+int no_x11_forwarding_flag = 0;
+int no_pty_flag = 0;
+char *forced_command = NULL;  /* RSA authentication "command=" option. */
+struct envstring *custom_environment = NULL; 
+			  /* RSA authentication "environment=" options. */
+
+/* Session id for the current session. */
+unsigned char session_id[16];
+
+/* Any really sensitive data in the application is contained in this structure.
+   The idea is that this structure could be locked into memory so that the
+   pages do not get written into swap.  However, there are some problems.
+   The private key contains BIGNUMs, and we do not (in principle) have
+   access to the internals of them, and locking just the structure is not
+   very useful.  Currently, memory locking is not implemented. */
+struct
+{
+  /* Private part of server key. */
+  RSA *private_key;
+
+  /* Private part of host key. */
+  RSA *host_key;
+} sensitive_data;
+
+/* Flag indicating whether the current session key has been used.  This flag
+   is set whenever the key is used, and cleared when the key is regenerated. */
+int key_used = 0;
+
+/* This is set to true when SIGHUP is received. */
+int received_sighup = 0;
+
+/* Public side of the server key.  This value is regenerated regularly with
+   the private key. */
+RSA *public_key;
+
+/* Prototypes for various functions defined later in this file. */
+void do_connection(int privileged_port);
+void do_authentication(char *user, int privileged_port);
+void do_authenticated(struct passwd *pw);
+void do_exec_pty(const char *command, int ptyfd, int ttyfd, 
+		 const char *ttyname, struct passwd *pw, const char *term,
+		 const char *display, const char *auth_proto,
+		 const char *auth_data);
+void do_exec_no_pty(const char *command, struct passwd *pw,
+		    const char *display, const char *auth_proto,
+		    const char *auth_data);
+void do_child(const char *command, struct passwd *pw, const char *term,
+	      const char *display, const char *auth_proto,
+	      const char *auth_data, const char *ttyname);
+#ifdef HAVE_PAM
+static int pamconv(int num_msg, const struct pam_message **msg,
+                   struct pam_response **resp, void *appdata_ptr);
+
+static struct pam_conv conv = {
+    pamconv,
+    NULL
+};
+
+static int pamconv(int num_msg, const struct pam_message **msg,
+                   struct pam_response **resp, void *appdata_ptr)
+{
+  int count = 0;
+  int replies = 0;
+  struct pam_response *reply = NULL;
+  int size = sizeof(struct pam_response);
+
+  for(count = 0; count < num_msg; count++)
+  {
+    switch (msg[count]->msg_style)
+    {
+      case PAM_PROMPT_ECHO_ON:
+      case PAM_PROMPT_ECHO_OFF:
+        if (reply == NULL) 
+          reply = xmalloc(size); 
+        else 
+          reply = realloc(reply, size);
+			 
+		  if (reply == NULL)
+          return PAM_CONV_ERR; 
+			 
+        size += sizeof(struct pam_response);
+		  
+		  reply[replies].resp_retcode = PAM_SUCCESS;
+		  
+		  reply[replies++].resp = xstrdup(pampasswd);
+			 /* PAM frees resp */
+		  break;
+
+		case PAM_TEXT_INFO:
+		  /* ignore it... */
+		  break;
+
+		case PAM_ERROR_MSG:
+		default:
+		  /* Must be an error of some sort... */
+		  if (reply != NULL)
+          free(reply);
+
+		  return PAM_CONV_ERR;
+	 }
+  }
+
+  if (reply != NULL)
+    *resp = reply;
+	 
+  return PAM_SUCCESS;
+}
+
+void pam_cleanup_proc(void *context)
+{
+  if (retval == PAM_SUCCESS) 
+    retval = pam_close_session((pam_handle_t *)pamh, 0);
+	 
+  if (pam_end((pam_handle_t *)pamh, retval) != PAM_SUCCESS)
+    log("Cannot release PAM authentication.");
+}
+#endif /* HAVE_PAM */
+
+/* Signal handler for SIGHUP.  Sshd execs itself when it receives SIGHUP;
+   the effect is to reread the configuration file (and to regenerate
+   the server key). */
+
+void sighup_handler(int sig)
+{
+  received_sighup = 1;
+  signal(SIGHUP, sighup_handler);
+}
+
+/* Called from the main program after receiving SIGHUP.  Restarts the 
+   server. */
+
+void sighup_restart()
+{
+  log("Received SIGHUP; restarting.");
+  close(listen_sock);
+  execv(saved_argv[0], saved_argv);
+  log("RESTART FAILED: av0='%s', error: %s.", av0, strerror(errno));
+  exit(1);
+}
+
+/* Generic signal handler for terminating signals in the master daemon. 
+   These close the listen socket; not closing it seems to cause "Address
+   already in use" problems on some machines, which is inconvenient. */
+
+void sigterm_handler(int sig)
+{
+  log("Received signal %d; terminating.", sig);
+  close(listen_sock);
+  exit(255);
+}
+
+/* SIGCHLD handler.  This is called whenever a child dies.  This will then 
+   reap any zombies left by exited c. */
+
+void main_sigchld_handler(int sig)
+{
+  int save_errno = errno;
+  int status;
+  wait(&status);
+  signal(SIGCHLD, main_sigchld_handler);
+  errno = save_errno;
+}
+
+/* Signal handler for the alarm after the login grace period has expired. */
+
+void grace_alarm_handler(int sig)
+{
+  /* Close the connection. */
+  packet_close();
+  
+  /* Log error and exit. */
+  fatal("Timeout before authentication.");
+}
+
+/* Signal handler for the key regeneration alarm.  Note that this
+   alarm only occurs in the daemon waiting for connections, and it does not
+   do anything with the private key or random state before forking.  Thus there
+   should be no concurrency control/asynchronous execution problems. */
+
+void key_regeneration_alarm(int sig)
+{
+  int save_errno = errno;
+
+  /* Check if we should generate a new key. */
+  if (key_used)
+    {
+      /* This should really be done in the background. */
+      log("Generating new %d bit RSA key.", options.server_key_bits);
+
+      if (sensitive_data.private_key != NULL)
+	RSA_free(sensitive_data.private_key);
+      sensitive_data.private_key = RSA_new();
+
+      if (public_key != NULL) 
+	RSA_free(public_key);
+      public_key = RSA_new();
+
+      rsa_generate_key(sensitive_data.private_key, public_key, 
+		       options.server_key_bits);
+      arc4random_stir();
+      key_used = 0;
+      log("RSA key generation complete.");
+    }
+
+  /* Reschedule the alarm. */
+  signal(SIGALRM, key_regeneration_alarm);
+  alarm(options.key_regeneration_time);
+  errno = save_errno;
+}
+
+/* Main program for the daemon. */
+
+int
+main(int ac, char **av)
+{
+  extern char *optarg;
+  extern int optind;
+  int opt, aux, sock_in, sock_out, newsock, i, pid, on = 1;
+  int remote_major, remote_minor;
+  int silentrsa = 0;
+  struct sockaddr_in sin;
+  char buf[100]; /* Must not be larger than remote_version. */
+  char remote_version[100]; /* Must be at least as big as buf. */
+  char *comment;
+  FILE *f;
+  struct linger linger;
+
+  /* Save argv[0]. */
+  saved_argv = av;
+  if (strchr(av[0], '/'))
+    av0 = strrchr(av[0], '/') + 1;
+  else
+    av0 = av[0];
+
+  /* Initialize configuration options to their default values. */
+  initialize_server_options(&options);
+
+  /* Parse command-line arguments. */
+  while ((opt = getopt(ac, av, "f:p:b:k:h:g:diqQ")) != EOF)
+    {
+      switch (opt)
+	{
+	case 'f':
+	  config_file_name = optarg;
+	  break;
+	case 'd':
+	  debug_flag = 1;
+	  break;
+	case 'i':
+	  inetd_flag = 1;
+	  break;
+	case 'Q':
+          silentrsa = 1;
+	  break;
+	case 'q':
+	  options.quiet_mode = 1;
+	  break;
+	case 'b':
+	  options.server_key_bits = atoi(optarg);
+	  break;
+	case 'p':
+	  options.port = atoi(optarg);
+	  break;
+	case 'g':
+	  options.login_grace_time = atoi(optarg);
+	  break;
+	case 'k':
+	  options.key_regeneration_time = atoi(optarg);
+	  break;
+	case 'h':
+	  options.host_key_file = optarg;
+	  break;
+	case '?':
+	default:
+	  fprintf(stderr, "sshd version %s\n", SSH_VERSION);
+	  fprintf(stderr, "Usage: %s [options]\n", av0);
+	  fprintf(stderr, "Options:\n");
+	  fprintf(stderr, "  -f file    Configuration file (default %s/sshd_config)\n", ETCDIR);
+	  fprintf(stderr, "  -d         Debugging mode\n");
+	  fprintf(stderr, "  -i         Started from inetd\n");
+	  fprintf(stderr, "  -q         Quiet (no logging)\n");
+	  fprintf(stderr, "  -p port    Listen on the specified port (default: 22)\n");
+	  fprintf(stderr, "  -k seconds Regenerate server key every this many seconds (default: 3600)\n");
+	  fprintf(stderr, "  -g seconds Grace period for authentication (default: 300)\n");
+	  fprintf(stderr, "  -b bits    Size of server RSA key (default: 768 bits)\n");
+	  fprintf(stderr, "  -h file    File from which to read host key (default: %s)\n",
+		  HOST_KEY_FILE);
+	  exit(1);
+	}
+    }
+
+  /* check if RSA support exists */
+  if (rsa_alive() == 0) {
+    if (silentrsa == 0)
+      printf("sshd: no RSA support in libssl and libcrypto -- exiting.  See ssl(8)\n");
+    log("no RSA support in libssl and libcrypto -- exiting.  See ssl(8)");
+    exit(1);
+  }
+
+  /* Read server configuration options from the configuration file. */
+  read_server_config(&options, config_file_name);
+
+  /* Fill in default values for those options not explicitly set. */
+  fill_default_server_options(&options);
+
+  /* Check certain values for sanity. */
+  if (options.server_key_bits < 512 || 
+      options.server_key_bits > 32768)
+    {
+      fprintf(stderr, "Bad server key size.\n");
+      exit(1);
+    }
+  if (options.port < 1 || options.port > 65535)
+    {
+      fprintf(stderr, "Bad port number.\n");
+      exit(1);
+    }
+
+  /* Check that there are no remaining arguments. */
+  if (optind < ac)
+    {
+      fprintf(stderr, "Extra argument %s.\n", av[optind]);
+      exit(1);
+    }
+
+  /* Initialize the log (it is reinitialized below in case we forked). */
+  log_init(av0, debug_flag && !inetd_flag, 
+	   debug_flag || options.fascist_logging, 
+	   options.quiet_mode, options.log_facility);
+
+  debug("sshd version %.100s", SSH_VERSION);
+
+  sensitive_data.host_key = RSA_new();
+  /* Load the host key.  It must have empty passphrase. */
+  if (!load_private_key(options.host_key_file, "", 
+			sensitive_data.host_key, &comment))
+    {
+      if (debug_flag)
+	fprintf(stderr, "Could not load host key: %s: %s\n",
+		options.host_key_file, strerror(errno));
+      else
+	{
+	  int err = errno;
+	  log_init(av0, !inetd_flag, 1, 0, options.log_facility);
+	  error("Could not load host key: %.200s: %.100s", 
+		options.host_key_file, strerror(err));
+	}
+      exit(1);
+    }
+  xfree(comment);
+
+  /* If not in debugging mode, and not started from inetd, disconnect from
+     the controlling terminal, and fork.  The original process exits. */
+  if (!debug_flag && !inetd_flag)
+    { 
+#ifdef TIOCNOTTY
+      int fd;
+#endif /* TIOCNOTTY */
+      if (daemon(0, 0) < 0)
+	  fatal("daemon() failed: %.200s", strerror(errno));
+     
+      /* Disconnect from the controlling tty. */
+#ifdef TIOCNOTTY
+      fd = open("/dev/tty", O_RDWR|O_NOCTTY);
+      if (fd >= 0)
+	{
+	  (void)ioctl(fd, TIOCNOTTY, NULL);
+	  close(fd);
+	}
+#endif /* TIOCNOTTY */
+    }
+
+  /* Reinitialize the log (because of the fork above). */
+  log_init(av0, debug_flag && !inetd_flag, 
+	   debug_flag || options.fascist_logging, 
+	   options.quiet_mode, options.log_facility);
+
+  /* Check that server and host key lengths differ sufficiently.  This is
+     necessary to make double encryption work with rsaref.  Oh, I hate
+     software patents. I dont know if this can go? Niels */
+  if (options.server_key_bits > 
+      BN_num_bits(sensitive_data.host_key->n) - SSH_KEY_BITS_RESERVED &&
+      options.server_key_bits < 
+      BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED)
+    {
+      options.server_key_bits = 
+	BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED;
+      debug("Forcing server key to %d bits to make it differ from host key.", 
+	    options.server_key_bits);
+    }
+
+  /* Do not display messages to stdout in RSA code. */
+  rsa_set_verbose(0);
+
+  /* Initialize the random number generator. */
+  arc4random_stir();
+  
+  /* Chdir to the root directory so that the current disk can be unmounted
+     if desired. */
+  chdir("/");
+  
+  /* Close connection cleanly after attack. */
+  cipher_attack_detected = packet_disconnect;
+
+  /* Start listening for a socket, unless started from inetd. */
+  if (inetd_flag)
+    {
+      int s1, s2;
+      s1 = dup(0);  /* Make sure descriptors 0, 1, and 2 are in use. */
+      s2 = dup(s1);
+      sock_in = dup(0);
+      sock_out = dup(1);
+      /* We intentionally do not close the descriptors 0, 1, and 2 as our
+	 code for setting the descriptors won\'t work if ttyfd happens to
+	 be one of those. */
+      debug("inetd sockets after dupping: %d, %d", sock_in, sock_out);
+
+      public_key = RSA_new();
+      sensitive_data.private_key = RSA_new();
+      /* Generate an rsa key. */
+      log("Generating %d bit RSA key.", options.server_key_bits);
+      rsa_generate_key(sensitive_data.private_key, public_key,
+		       options.server_key_bits);
+      arc4random_stir();
+      log("RSA key generation complete.");
+    }
+  else
+    {
+      /* Create socket for listening. */
+      listen_sock = socket(AF_INET, SOCK_STREAM, 0);
+      if (listen_sock < 0)
+	fatal("socket: %.100s", strerror(errno));
+
+      /* Set socket options.  We try to make the port reusable and have it
+	 close as fast as possible without waiting in unnecessary wait states
+	 on close. */
+      setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, 
+		 sizeof(on));
+      linger.l_onoff = 1;
+      linger.l_linger = 5;
+      setsockopt(listen_sock, SOL_SOCKET, SO_LINGER, (void *)&linger, 
+		 sizeof(linger));
+
+      /* Initialize the socket address. */
+      memset(&sin, 0, sizeof(sin));
+      sin.sin_family = AF_INET;
+      sin.sin_addr = options.listen_addr;
+      sin.sin_port = htons(options.port);
+
+      /* Bind the socket to the desired port. */
+      if (bind(listen_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+	{
+	  error("bind: %.100s", strerror(errno));
+	  shutdown(listen_sock, SHUT_RDWR);
+	  close(listen_sock);
+	  fatal("Bind to port %d failed.", options.port);
+	}
+
+      if (!debug_flag)
+	{
+	  /* Record our pid in /etc/sshd_pid to make it easier to kill the
+	     correct sshd.  We don\'t want to do this before the bind above
+	     because the bind will fail if there already is a daemon, and this
+	     will overwrite any old pid in the file. */
+	  f = fopen(SSH_DAEMON_PID_FILE, "w");
+	  if (f)
+	    {
+	      fprintf(f, "%u\n", (unsigned int)getpid());
+	      fclose(f);
+	    }
+	}
+
+      /* Start listening on the port. */
+      log("Server listening on port %d.", options.port);
+      if (listen(listen_sock, 5) < 0)
+	fatal("listen: %.100s", strerror(errno));
+
+      public_key = RSA_new();
+      sensitive_data.private_key = RSA_new();
+      /* Generate an rsa key. */
+      log("Generating %d bit RSA key.", options.server_key_bits);
+      rsa_generate_key(sensitive_data.private_key, public_key,
+		       options.server_key_bits);
+      arc4random_stir();
+      log("RSA key generation complete.");
+
+      /* Schedule server key regeneration alarm. */
+      signal(SIGALRM, key_regeneration_alarm);
+      alarm(options.key_regeneration_time);
+
+      /* Arrange to restart on SIGHUP.  The handler needs listen_sock. */
+      signal(SIGHUP, sighup_handler);
+      signal(SIGTERM, sigterm_handler);
+      signal(SIGQUIT, sigterm_handler);
+      
+      /* Arrange SIGCHLD to be caught. */
+      signal(SIGCHLD, main_sigchld_handler);
+
+      /* Stay listening for connections until the system crashes or the
+	 daemon is killed with a signal. */
+      for (;;)
+	{
+	  if (received_sighup)
+	    sighup_restart();
+	  /* Wait in accept until there is a connection. */
+	  aux = sizeof(sin);
+	  newsock = accept(listen_sock, (struct sockaddr *)&sin, &aux);
+	  if (received_sighup)
+	    sighup_restart();
+	  if (newsock < 0)
+	    {
+	      if (errno == EINTR)
+		continue;
+	      error("accept: %.100s", strerror(errno));
+	      continue;
+	    }
+
+	  /* Got connection.  Fork a child to handle it, unless we are in
+	     debugging mode. */
+	  if (debug_flag)
+	    {
+	      /* In debugging mode.  Close the listening socket, and start
+		 processing the connection without forking. */
+	      debug("Server will not fork when running in debugging mode.");
+	      close(listen_sock);
+	      sock_in = newsock;
+	      sock_out = newsock;
+	      pid = getpid();
+	      break;
+	    }
+	  else
+	    {
+	      /* Normal production daemon.  Fork, and have the child process
+		 the connection.  The parent continues listening. */
+	      if ((pid = fork()) == 0)
+		{ 
+		  /* Child.  Close the listening socket, and start using
+		     the accepted socket.  Reinitialize logging (since our
+		     pid has changed).  We break out of the loop to handle
+		     the connection. */
+		  close(listen_sock);
+		  sock_in = newsock;
+		  sock_out = newsock;
+		  log_init(av0, debug_flag && !inetd_flag, 
+			   options.fascist_logging || debug_flag, 
+			   options.quiet_mode, options.log_facility);
+		  break;
+		}
+	    }
+
+	  /* Parent.  Stay in the loop. */
+	  if (pid < 0)
+	    error("fork: %.100s", strerror(errno));
+	  else
+	    debug("Forked child %d.", pid);
+
+	  /* Mark that the key has been used (it was "given" to the child). */
+	  key_used = 1;
+
+	  arc4random_stir();
+
+	  /* Close the new socket (the child is now taking care of it). */
+	  close(newsock);
+	}
+    }
+  
+  /* This is the child processing a new connection. */
+
+  /* Disable the key regeneration alarm.  We will not regenerate the key
+     since we are no longer in a position to give it to anyone.  We will
+     not restart on SIGHUP since it no longer makes sense. */
+  alarm(0);
+  signal(SIGALRM, SIG_DFL);
+  signal(SIGHUP, SIG_DFL);
+  signal(SIGTERM, SIG_DFL);
+  signal(SIGQUIT, SIG_DFL);
+  signal(SIGCHLD, SIG_DFL);
+
+  /* Set socket options for the connection.  We want the socket to close
+     as fast as possible without waiting for anything.  If the connection
+     is not a socket, these will do nothing. */
+  /* setsockopt(sock_in, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */
+  linger.l_onoff = 1;
+  linger.l_linger = 5;
+  setsockopt(sock_in, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger));
+
+  /* Register our connection.  This turns encryption off because we do not
+     have a key. */
+  packet_set_connection(sock_in, sock_out);
+
+  /* Check whether logins are denied from this host. */
+#ifdef LIBWRAP
+  {
+    struct request_info req;
+
+    request_init(&req, RQ_DAEMON, av0, RQ_FILE, sock_in, NULL);
+    fromhost(&req);
+
+    if (!hosts_access(&req)) {
+      close(sock_in);
+      close(sock_out);
+      refuse(&req);
+    }
+    log("Connection from %.500s port %d",
+	eval_client(&req), get_remote_port());
+  }
+#else
+  /* Log the connection. */
+  log("Connection from %.100s port %d", 
+      get_remote_ipaddr(), get_remote_port());
+#endif /* LIBWRAP */
+
+  /* We don\'t want to listen forever unless the other side successfully
+     authenticates itself.  So we set up an alarm which is cleared after
+     successful authentication.  A limit of zero indicates no limit.
+     Note that we don\'t set the alarm in debugging mode; it is just annoying
+     to have the server exit just when you are about to discover the bug. */
+  signal(SIGALRM, grace_alarm_handler);
+  if (!debug_flag)
+    alarm(options.login_grace_time);
+
+  /* Send our protocol version identification. */
+  snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", 
+	  PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION);
+  if (write(sock_out, buf, strlen(buf)) != strlen(buf))
+    fatal("Could not write ident string.");
+
+  /* Read other side\'s version identification. */
+  for (i = 0; i < sizeof(buf) - 1; i++)
+    {
+      if (read(sock_in, &buf[i], 1) != 1)
+	fatal("Did not receive ident string.");
+      if (buf[i] == '\r')
+	{
+	  buf[i] = '\n';
+	  buf[i + 1] = 0;
+	  break;
+	}
+      if (buf[i] == '\n')
+	{
+	  /* buf[i] == '\n' */
+	  buf[i + 1] = 0;
+	  break;
+	}
+    }
+  buf[sizeof(buf) - 1] = 0;
+  
+  /* Check that the versions match.  In future this might accept several
+     versions and set appropriate flags to handle them. */
+  if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, 
+	     remote_version) != 3)
+    {
+      const char *s = "Protocol mismatch.\n";
+      (void) write(sock_out, s, strlen(s));
+      close(sock_in);
+      close(sock_out);
+      fatal("Bad protocol version identification: %.100s", buf);
+    }
+  debug("Client protocol version %d.%d; client software version %.100s",
+	remote_major, remote_minor, remote_version);
+  if (remote_major != PROTOCOL_MAJOR)
+    {
+      const char *s = "Protocol major versions differ.\n";
+      (void) write(sock_out, s, strlen(s));
+      close(sock_in);
+      close(sock_out);
+      fatal("Protocol major versions differ: %d vs. %d", 
+	    PROTOCOL_MAJOR, remote_major);
+    }
+
+  /* Check that the client has sufficiently high software version. */
+  if (remote_major == 1 && remote_minor < 3)
+    packet_disconnect("Your ssh version is too old and is no longer supported.  Please install a newer version.");
+
+  if (remote_major == 1 && remote_minor == 3) {
+    enable_compat13();
+    if (strcmp(remote_version, "OpenSSH-1.1") != 0) {
+        debug("Agent forwarding disabled, remote version is not compatible.");
+        no_agent_forwarding_flag = 1;
+    }
+  }
+
+  packet_set_nonblocking();
+  
+  /* Handle the connection.   We pass as argument whether the connection
+     came from a privileged port. */
+  do_connection(get_remote_port() < IPPORT_RESERVED);
+
+#ifdef KRB4
+  /* Cleanup user's ticket cache file. */
+  if (options.kerberos_ticket_cleanup)
+    (void) dest_tkt();
+#endif /* KRB4 */
+
+  /* Cleanup user's local Xauthority file. */
+  if (xauthfile) unlink(xauthfile);
+
+  /* The connection has been terminated. */
+  log("Closing connection to %.100s", inet_ntoa(sin.sin_addr));
+
+#ifdef HAVE_PAM
+  if (retval == PAM_SUCCESS)
+    retval = pam_close_session((pam_handle_t *)pamh, 0);
+
+  if (pam_end((pam_handle_t *)pamh, retval) != PAM_SUCCESS)
+    log("Cannot release PAM authentication.");
+	 
+  fatal_remove_cleanup(&pam_cleanup_proc, NULL);
+#endif /* HAVE_PAM */
+
+  packet_close();
+
+  exit(0);
+}
+
+/* Process an incoming connection.  Protocol version identifiers have already
+   been exchanged.  This sends server key and performs the key exchange.
+   Server and host keys will no longer be needed after this functions. */
+
+void do_connection(int privileged_port)
+{
+  int i;
+  BIGNUM *session_key_int;
+  unsigned char session_key[SSH_SESSION_KEY_LENGTH];
+  unsigned char check_bytes[8];
+  char *user;
+  unsigned int cipher_type, auth_mask, protocol_flags;
+  int plen, slen;
+  u_int32_t rand = 0;
+
+  /* Generate check bytes that the client must send back in the user packet
+     in order for it to be accepted; this is used to defy ip spoofing 
+     attacks.  Note that this only works against somebody doing IP spoofing 
+     from a remote machine; any machine on the local network can still see 
+     outgoing packets and catch the random cookie.  This only affects
+     rhosts authentication, and this is one of the reasons why it is
+     inherently insecure. */
+  for (i = 0; i < 8; i++) {
+    if (i % 4 == 0)
+      rand = arc4random();
+    check_bytes[i] = rand & 0xff;
+    rand >>= 8;
+  }
+  
+  /* Send our public key.  We include in the packet 64 bits of random
+     data that must be matched in the reply in order to prevent IP spoofing. */
+  packet_start(SSH_SMSG_PUBLIC_KEY);
+  for (i = 0; i < 8; i++)
+    packet_put_char(check_bytes[i]);
+
+  /* Store our public server RSA key. */
+  packet_put_int(BN_num_bits(public_key->n));
+  packet_put_bignum(public_key->e);
+  packet_put_bignum(public_key->n);
+
+  /* Store our public host RSA key. */
+  packet_put_int(BN_num_bits(sensitive_data.host_key->n));
+  packet_put_bignum(sensitive_data.host_key->e);
+  packet_put_bignum(sensitive_data.host_key->n);
+
+  /* Put protocol flags. */
+  packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN);
+
+  /* Declare which ciphers we support. */
+  packet_put_int(cipher_mask());
+
+  /* Declare supported authentication types. */
+  auth_mask = 0;
+  if (options.rhosts_authentication)
+    auth_mask |= 1 << SSH_AUTH_RHOSTS;
+  if (options.rhosts_rsa_authentication)
+    auth_mask |= 1 << SSH_AUTH_RHOSTS_RSA;
+  if (options.rsa_authentication)
+    auth_mask |= 1 << SSH_AUTH_RSA;
+#ifdef KRB4
+  if (options.kerberos_authentication)
+    auth_mask |= 1 << SSH_AUTH_KERBEROS;
+#endif
+#ifdef AFS
+  if (options.kerberos_tgt_passing)
+    auth_mask |= 1 << SSH_PASS_KERBEROS_TGT;
+  if (options.afs_token_passing)
+    auth_mask |= 1 << SSH_PASS_AFS_TOKEN;
+#endif
+  if (options.password_authentication)
+    auth_mask |= 1 << SSH_AUTH_PASSWORD;
+  packet_put_int(auth_mask);
+
+  /* Send the packet and wait for it to be sent. */
+  packet_send();
+  packet_write_wait();
+
+  debug("Sent %d bit public key and %d bit host key.", 
+	BN_num_bits(public_key->n), BN_num_bits(sensitive_data.host_key->n));
+
+  /* Read clients reply (cipher type and session key). */
+  packet_read_expect(&plen, SSH_CMSG_SESSION_KEY);
+
+  /* Get cipher type. */
+  cipher_type = packet_get_char();
+
+  /* Get check bytes from the packet.  These must match those we sent earlier
+     with the public key packet. */
+  for (i = 0; i < 8; i++)
+    if (check_bytes[i] != packet_get_char())
+      packet_disconnect("IP Spoofing check bytes do not match.");
+
+  debug("Encryption type: %.200s", cipher_name(cipher_type));
+
+  /* Get the encrypted integer. */
+  session_key_int = BN_new();
+  packet_get_bignum(session_key_int, &slen);
+
+  /* Get protocol flags. */
+  protocol_flags = packet_get_int();
+  packet_set_protocol_flags(protocol_flags);
+
+  packet_integrity_check(plen, 1 + 8 + slen + 4, SSH_CMSG_SESSION_KEY);
+
+  /* Decrypt it using our private server key and private host key (key with 
+     larger modulus first). */
+  if (BN_cmp(sensitive_data.private_key->n, sensitive_data.host_key->n) > 0)
+    {
+      /* Private key has bigger modulus. */
+      assert(BN_num_bits(sensitive_data.private_key->n) >= 
+	     BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED);
+      rsa_private_decrypt(session_key_int, session_key_int,
+			  sensitive_data.private_key);
+      rsa_private_decrypt(session_key_int, session_key_int,
+			  sensitive_data.host_key);
+    }
+  else
+    {
+      /* Host key has bigger modulus (or they are equal). */
+      assert(BN_num_bits(sensitive_data.host_key->n) >= 
+	     BN_num_bits(sensitive_data.private_key->n) +
+	     SSH_KEY_BITS_RESERVED);
+      rsa_private_decrypt(session_key_int, session_key_int,
+			  sensitive_data.host_key);
+      rsa_private_decrypt(session_key_int, session_key_int,
+			  sensitive_data.private_key);
+    }
+
+  /* Compute session id for this session. */
+  compute_session_id(session_id, check_bytes,
+		     BN_num_bits(sensitive_data.host_key->n),
+		     sensitive_data.host_key->n, 
+		     BN_num_bits(sensitive_data.private_key->n),
+		     sensitive_data.private_key->n);
+
+  /* Extract session key from the decrypted integer.  The key is in the 
+     least significant 256 bits of the integer; the first byte of the 
+     key is in the highest bits. */
+  BN_mask_bits(session_key_int, sizeof(session_key) * 8);
+  assert(BN_num_bytes(session_key_int) == sizeof(session_key));
+  BN_bn2bin(session_key_int, session_key);
+  
+  /* Xor the first 16 bytes of the session key with the session id. */
+  for (i = 0; i < 16; i++)
+    session_key[i] ^= session_id[i];
+
+  /* Destroy the decrypted integer.  It is no longer needed. */
+  BN_clear_free(session_key_int);
+  
+  /* Set the session key.  From this on all communications will be
+     encrypted. */
+  packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, 
+			    cipher_type, 0);
+  
+  /* Destroy our copy of the session key.  It is no longer needed. */
+  memset(session_key, 0, sizeof(session_key));
+
+  debug("Received session key; encryption turned on.");
+
+  /* Send an acknowledgement packet.  Note that this packet is sent
+     encrypted. */
+  packet_start(SSH_SMSG_SUCCESS);
+  packet_send();
+  packet_write_wait();
+
+  /* Get the name of the user that we wish to log in as. */
+  packet_read_expect(&plen, SSH_CMSG_USER);
+
+  /* Get the user name. */
+  {
+    int ulen;
+    user = packet_get_string(&ulen);
+    packet_integrity_check(plen, (4 + ulen), SSH_CMSG_USER);
+  }
+
+  /* Destroy the private and public keys.  They will no longer be needed. */
+  RSA_free(public_key);
+  RSA_free(sensitive_data.private_key);
+  RSA_free(sensitive_data.host_key);
+
+  setproctitle("%s", user);
+  /* Do the authentication. */
+  do_authentication(user, privileged_port);
+}
+
+/* Check if the user is allowed to log in via ssh. If user is listed in
+   DenyUsers or user's primary group is listed in DenyGroups, false will
+   be returned. If AllowUsers isn't empty and user isn't listed there, or
+   if AllowGroups isn't empty and user isn't listed there, false will be
+   returned. Otherwise true is returned.
+   XXX This function should also check if user has a valid shell */
+
+static int
+allowed_user(struct passwd *pw)
+{
+  struct group *grp;
+  int i;
+
+  /* Shouldn't be called if pw is NULL, but better safe than sorry... */
+  if (!pw)
+    return 0;
+
+  /* XXX Should check for valid login shell */
+
+  /* Return false if user is listed in DenyUsers */
+  if (options.num_deny_users > 0)
+    {
+      if (!pw->pw_name)
+	return 0;
+      for (i = 0; i < options.num_deny_users; i++)
+	if (match_pattern(pw->pw_name, options.deny_users[i]))
+	  return 0;
+    }
+
+  /* Return false if AllowUsers isn't empty and user isn't listed there */
+  if (options.num_allow_users > 0)
+    {
+      if (!pw->pw_name)
+	return 0;
+      for (i = 0; i < options.num_allow_users; i++)
+	if (match_pattern(pw->pw_name, options.allow_users[i]))
+	  break;
+      /* i < options.num_allow_users iff we break for loop */
+      if (i >= options.num_allow_users)
+	return 0;
+    }
+
+  /* Get the primary group name if we need it. Return false if it fails */
+  if (options.num_deny_groups > 0 || options.num_allow_groups > 0 )
+    {
+      grp = getgrgid(pw->pw_gid);
+      if (!grp)
+	return 0;
+
+      /* Return false if user's group is listed in DenyGroups */
+      if (options.num_deny_groups > 0)
+        {
+          if (!grp->gr_name)
+	    return 0;
+          for (i = 0; i < options.num_deny_groups; i++)
+	    if (match_pattern(grp->gr_name, options.deny_groups[i]))
+	      return 0;
+        }
+
+      /* Return false if AllowGroups isn't empty and user's group isn't
+	 listed there */
+      if (options.num_allow_groups > 0)
+        {
+          if (!grp->gr_name)
+	    return 0;
+          for (i = 0; i < options.num_allow_groups; i++)
+	    if (match_pattern(grp->gr_name, options.allow_groups[i]))
+	      break;
+          /* i < options.num_allow_groups iff we break for loop */
+          if (i >= options.num_allow_groups)
+	    return 0;
+        }
+    }
+
+  /* We found no reason not to let this user try to log on... */
+  return 1;
+}
+
+/* Performs authentication of an incoming connection.  Session key has already
+   been exchanged and encryption is enabled.  User is the user name to log
+   in as (received from the clinet).  Privileged_port is true if the
+   connection comes from a privileged port (used for .rhosts authentication).*/
+
+#define MAX_AUTH_FAILURES 5
+
+void
+do_authentication(char *user, int privileged_port)
+{
+  int type;
+  int authenticated = 0;
+  int authentication_failures = 0;
+  char *password;
+  struct passwd *pw, pwcopy;
+  char *client_user;
+  unsigned int client_host_key_bits;
+  BIGNUM *client_host_key_e, *client_host_key_n;
+#ifdef HAVE_PAM
+  int pam_auth_ok;
+#endif /* HAVE_PAM */
+			 
+#ifdef AFS
+  /* If machine has AFS, set process authentication group. */
+  if (k_hasafs()) {
+    k_setpag();
+    k_unlog();
+  }
+#endif /* AFS */
+       
+  /* Verify that the user is a valid user. */
+  pw = getpwnam(user);
+#ifdef HAVE_PAM
+  if ((pw != NULL) && allowed_user(pw))
+  {
+    /* Initialise PAM */
+    retval = pam_start("ssh", pw->pw_name, &conv, (pam_handle_t **)&pamh);
+    fatal_add_cleanup(&pam_cleanup_proc, NULL); 
+    origretval = retval;
+    if (retval == PAM_SUCCESS)
+	 	pam_auth_ok = 1;
+  }
+  
+  if (pam_auth_ok == 0)
+#else /* HAVE_PAM */
+  if (!pw || !allowed_user(pw))
+#endif /* HAVE_PAM */
+    {
+      /* The user does not exist or access is denied,
+         but fake indication that authentication is needed. */
+      packet_start(SSH_SMSG_FAILURE);
+      packet_send();
+      packet_write_wait();
+
+      /* Keep reading packets, and always respond with a failure.  This is to
+	 avoid disclosing whether such a user really exists. */
+      for (;;)
+	{
+	  /* Read a packet.  This will not return if the client disconnects. */
+	  int plen;
+	  int type = packet_read(&plen);
+#ifdef SKEY
+	  int passw_len;
+	  char *password, *skeyinfo;
+	  if (options.password_authentication &&
+	     options.skey_authentication == 1 &&
+	     type == SSH_CMSG_AUTH_PASSWORD &&
+	     (password = packet_get_string(&passw_len)) != NULL &&
+	     passw_len == 5 &&
+	     strncasecmp(password, "s/key", 5) == 0 &&
+	     (skeyinfo = skey_fake_keyinfo(user)) != NULL ){
+	    /* Send a fake s/key challenge. */
+	    packet_send_debug(skeyinfo);
+          }
+#endif
+	  /* Send failure.  This should be indistinguishable from a failed
+	     authentication. */
+	  packet_start(SSH_SMSG_FAILURE);
+	  packet_send();
+	  packet_write_wait();
+          if (++authentication_failures >= MAX_AUTH_FAILURES) {
+	    packet_disconnect("Too many authentication failures for %.100s from %.200s", 
+            		       user, get_canonical_hostname());
+          }
+	}
+      /*NOTREACHED*/
+      abort();
+    }
+  
+  /* Take a copy of the returned structure. */
+  memset(&pwcopy, 0, sizeof(pwcopy));
+  pwcopy.pw_name = xstrdup(pw->pw_name);
+  pwcopy.pw_passwd = xstrdup(pw->pw_passwd);
+  pwcopy.pw_uid = pw->pw_uid;
+  pwcopy.pw_gid = pw->pw_gid;
+  pwcopy.pw_dir = xstrdup(pw->pw_dir);
+  pwcopy.pw_shell = xstrdup(pw->pw_shell);
+  pw = &pwcopy;
+
+  /* If we are not running as root, the user must have the same uid as the
+     server. */
+  if (getuid() != 0 && pw->pw_uid != getuid())
+    packet_disconnect("Cannot change user when server not running as root.");
+
+  debug("Attempting authentication for %.100s.", user);
+
+  /* If the user has no password, accept authentication immediately. */
+  if (options.password_authentication &&
+#ifdef KRB4
+      (!options.kerberos_authentication || options.kerberos_or_local_passwd) &&
+#endif /* KRB4 */
+      auth_password(pw, ""))
+    {
+      /* Authentication with empty password succeeded. */
+      debug("Login for user %.100s accepted without authentication.", user);
+      /* authentication_type = SSH_AUTH_PASSWORD; */
+      authenticated = 1;
+      /* Success packet will be sent after loop below. */
+    }
+  else
+    {
+      /* Indicate that authentication is needed. */
+      packet_start(SSH_SMSG_FAILURE);
+      packet_send();
+      packet_write_wait();
+    }
+
+  /* Loop until the user has been authenticated or the connection is closed. */
+  while (!authenticated)
+    {
+      int plen;
+      /* Get a packet from the client. */
+      type = packet_read(&plen);
+      
+      /* Process the packet. */
+      switch (type)
+	{
+
+#ifdef AFS
+	case SSH_CMSG_HAVE_KERBEROS_TGT:
+	  if (!options.kerberos_tgt_passing)
+	    {
+	      /* packet_get_all(); */
+	      log("Kerberos tgt passing disabled.");
+	      break;
+	    }
+	  else {
+	    /* Accept Kerberos tgt. */
+	    int dlen;
+	    char *tgt = packet_get_string(&dlen);
+	    packet_integrity_check(plen, 4 + dlen, type);
+	    if (!auth_kerberos_tgt(pw, tgt))
+	      debug("Kerberos tgt REFUSED for %s", user);
+	    xfree(tgt);
+	  }
+	  continue;
+
+	case SSH_CMSG_HAVE_AFS_TOKEN:
+	  if (!options.afs_token_passing || !k_hasafs()) {
+	    /* packet_get_all(); */
+	    log("AFS token passing disabled.");
+	    break;
+	  }
+	  else {
+	    /* Accept AFS token. */
+	    int dlen;
+	    char *token_string = packet_get_string(&dlen);
+	    packet_integrity_check(plen, 4 + dlen, type);
+	    if (!auth_afs_token(user, pw->pw_uid, token_string))
+	      debug("AFS token REFUSED for %s", user);
+	    xfree(token_string);
+	    continue;
+	  }
+#endif /* AFS */
+	  
+#ifdef KRB4
+	case SSH_CMSG_AUTH_KERBEROS:
+	  if (!options.kerberos_authentication)
+	    {
+	      /* packet_get_all(); */
+	      log("Kerberos authentication disabled.");
+	      break;
+	    }
+	  else {
+	    /* Try Kerberos v4 authentication. */
+	    KTEXT_ST auth;
+	    char *tkt_user = NULL;
+	    char *kdata = packet_get_string((unsigned int *)&auth.length);
+	    packet_integrity_check(plen, 4 + auth.length, type);
+
+	    if (auth.length < MAX_KTXT_LEN)
+	      memcpy(auth.dat, kdata, auth.length);
+	    xfree(kdata);
+	    
+	    if (auth_krb4(user, &auth, &tkt_user)) {
+	      /* Client has successfully authenticated to us. */
+	      log("Kerberos authentication accepted %s for account "
+		  "%s from %s", tkt_user, user, get_canonical_hostname());
+	      /* authentication_type = SSH_AUTH_KERBEROS; */
+	      authenticated = 1;
+	      xfree(tkt_user);
+	    }
+	    else {
+	      log("Kerberos authentication failed for account "
+		  "%s from %s", user, get_canonical_hostname());
+	    }
+	  }
+	  break;
+#endif /* KRB4 */
+	  
+	case SSH_CMSG_AUTH_RHOSTS:
+	  if (!options.rhosts_authentication)
+	    {
+	      log("Rhosts authentication disabled.");
+	      break;
+	    }
+
+	  /* Rhosts authentication (also uses /etc/hosts.equiv). */
+	  if (!privileged_port)
+	    {
+	      log("Rhosts authentication not available for connections from unprivileged port.");
+	      break;
+	    }
+
+	  /* Get client user name.  Note that we just have to trust the client;
+	     this is one reason why rhosts authentication is insecure. 
+	     (Another is IP-spoofing on a local network.) */
+	  {
+	    int dlen;
+	    client_user = packet_get_string(&dlen);
+	    packet_integrity_check(plen, 4 + dlen, type);
+	  }
+
+	  /* Try to authenticate using /etc/hosts.equiv and .rhosts. */
+	  if (auth_rhosts(pw, client_user, options.ignore_rhosts,
+			  options.strict_modes))
+	    {
+	      /* Authentication accepted. */
+	      log("Rhosts authentication accepted for %.100s, remote %.100s on %.700s.",
+		  user, client_user, get_canonical_hostname());
+	      authenticated = 1;
+	      xfree(client_user);
+	      break;
+	    }
+	  log("Rhosts authentication failed for %.100s, remote %.100s.",
+		user, client_user);
+	  xfree(client_user);
+	  break;
+
+	case SSH_CMSG_AUTH_RHOSTS_RSA:
+	  if (!options.rhosts_rsa_authentication)
+	    {
+	      log("Rhosts with RSA authentication disabled.");
+	      break;
+	    }
+
+	  /* Rhosts authentication (also uses /etc/hosts.equiv) with RSA
+	     host authentication. */
+	  if (!privileged_port)
+	    {
+	      log("Rhosts authentication not available for connections from unprivileged port.");
+	      break;
+	    }
+
+	  {
+	    int ulen, elen, nlen;
+	    /* Get client user name.  Note that we just have to trust
+	       the client; root on the client machine can claim to be
+	       any user. */
+	    client_user = packet_get_string(&ulen);
+
+	    /* Get the client host key. */
+	    client_host_key_e = BN_new();
+	    client_host_key_n = BN_new();
+	    client_host_key_bits = packet_get_int();
+	    packet_get_bignum(client_host_key_e, &elen);
+	    packet_get_bignum(client_host_key_n, &nlen);
+
+	    packet_integrity_check(plen, (4 + ulen) + 4 + elen + nlen, type);
+	  }
+
+	  /* Try to authenticate using /etc/hosts.equiv and .rhosts. */
+	  if (auth_rhosts_rsa(pw, client_user,
+			      client_host_key_bits, client_host_key_e,
+			      client_host_key_n, options.ignore_rhosts,
+			      options.strict_modes))
+	    {
+	      /* Authentication accepted. */
+	      authenticated = 1;
+	      xfree(client_user);
+	      BN_clear_free(client_host_key_e);
+	      BN_clear_free(client_host_key_n);
+	      break;
+	    }
+	  log("Rhosts authentication failed for %.100s, remote %.100s.",
+		user, client_user);
+	  xfree(client_user);
+	  BN_clear_free(client_host_key_e);
+	  BN_clear_free(client_host_key_n);
+	  break;
+	  
+	case SSH_CMSG_AUTH_RSA:
+	  if (!options.rsa_authentication)
+	    {
+	      log("RSA authentication disabled.");
+	      break;
+	    }
+
+	  /* RSA authentication requested. */
+	  {
+	    int nlen;
+	    BIGNUM *n;
+	    n = BN_new();
+	    packet_get_bignum(n, &nlen);
+
+	    packet_integrity_check(plen, nlen, type);
+	    
+	    if (auth_rsa(pw, n, options.strict_modes))
+	      { 
+		/* Successful authentication. */
+		BN_clear_free(n);
+		log("RSA authentication for %.100s accepted.", user);
+		authenticated = 1;
+		break;
+	      }
+	    BN_clear_free(n);
+	    log("RSA authentication for %.100s failed.", user);
+	  }
+	  break;
+
+	case SSH_CMSG_AUTH_PASSWORD:
+	  if (!options.password_authentication)
+	    {
+	      log("Password authentication disabled.");
+	      break;
+	    }
+
+	  /* Password authentication requested. */
+	  /* Read user password.  It is in plain text, but was transmitted
+	     over the encrypted channel so it is not visible to an outside
+	     observer. */
+	  {
+	    int passw_len;
+	    password = packet_get_string(&passw_len);
+	    packet_integrity_check(plen, 4 + passw_len, type);
+	  }
+
+	  /* Try authentication with the password. */
+	  if (auth_password(pw, password))
+	    {
+	      /* Successful authentication. */
+	      /* Clear the password from memory. */
+	      memset(password, 0, strlen(password));
+	      xfree(password);
+	      log("Password authentication for %.100s accepted.", user);
+	      authenticated = 1;
+	      break;
+	    }
+	  log("Password authentication for %.100s failed.", user);
+	  memset(password, 0, strlen(password));
+	  xfree(password);
+	  break;
+
+	case SSH_CMSG_AUTH_TIS:
+	  /* TIS Authentication is unsupported */
+	  log("TIS authentication disabled.");
+	  break;
+
+	default:
+	  /* Any unknown messages will be ignored (and failure returned)
+	     during authentication. */
+	  log("Unknown message during authentication: type %d", type);
+	  break; /* Respond with a failure message. */
+	}
+      /* If successfully authenticated, break out of loop. */
+      if (authenticated)
+	break;
+
+      /* Send a message indicating that the authentication attempt failed. */
+      packet_start(SSH_SMSG_FAILURE);
+      packet_send();
+      packet_write_wait();
+
+      if (++authentication_failures >= MAX_AUTH_FAILURES) {
+	packet_disconnect("Too many authentication failures for %.100s from %.200s", 
+          pw->pw_name, get_canonical_hostname());
+      }
+    }
+
+  /* Check if the user is logging in as root and root logins are disallowed. */
+  if (pw->pw_uid == 0 && !options.permit_root_login)
+    {
+      if (forced_command)
+	log("Root login accepted for forced command.", forced_command);
+      else
+	packet_disconnect("ROOT LOGIN REFUSED FROM %.200s", 
+			  get_canonical_hostname());
+    }
+
+  /* The user has been authenticated and accepted. */
+  packet_start(SSH_SMSG_SUCCESS);
+  packet_send();
+  packet_write_wait();
+
+  /* Perform session preparation. */
+  do_authenticated(pw);
+}
+
+/* Prepares for an interactive session.  This is called after the user has
+   been successfully authenticated.  During this message exchange, pseudo
+   terminals are allocated, X11, TCP/IP, and authentication agent forwardings
+   are requested, etc. */
+
+void do_authenticated(struct passwd *pw)
+{
+  int type;
+  int compression_level = 0, enable_compression_after_reply = 0;
+  int have_pty = 0, ptyfd = -1, ttyfd = -1, xauthfd = -1;
+  int row, col, xpixel, ypixel, screen;
+  char ttyname[64];
+  char *command, *term = NULL, *display = NULL, *proto = NULL, *data = NULL;
+  struct group *grp;
+  gid_t tty_gid;
+  mode_t tty_mode;
+  int n_bytes;
+  
+  /* Cancel the alarm we set to limit the time taken for authentication. */
+  alarm(0);
+
+  /* Inform the channel mechanism that we are the server side and that
+     the client may request to connect to any port at all.  (The user could
+     do it anyway, and we wouldn\'t know what is permitted except by the
+     client telling us, so we can equally well trust the client not to request
+     anything bogus.) */
+  channel_permit_all_opens();
+
+  /* We stay in this loop until the client requests to execute a shell or a
+     command. */
+  while (1)
+    {
+      int plen, dlen;
+
+      /* Get a packet from the client. */
+      type = packet_read(&plen);
+      
+      /* Process the packet. */
+      switch (type)
+	{
+	case SSH_CMSG_REQUEST_COMPRESSION:
+	  packet_integrity_check(plen, 4, type);
+	  compression_level = packet_get_int();
+	  if (compression_level < 1 || compression_level > 9)
+	    {
+	      packet_send_debug("Received illegal compression level %d.",
+				compression_level);
+	      goto fail;
+	    }
+	  /* Enable compression after we have responded with SUCCESS. */
+	  enable_compression_after_reply = 1;
+	  break;
+
+	case SSH_CMSG_REQUEST_PTY:
+	  if (no_pty_flag)
+	    {
+	      debug("Allocating a pty not permitted for this authentication.");
+	      goto fail;
+	    }
+	  if (have_pty)
+	    packet_disconnect("Protocol error: you already have a pty.");
+
+	  debug("Allocating pty.");
+
+	  /* Allocate a pty and open it. */
+	  if (!pty_allocate(&ptyfd, &ttyfd, ttyname))
+	    {
+	      error("Failed to allocate pty.");
+	      goto fail;
+	    }
+
+	  /* Determine the group to make the owner of the tty. */
+	  grp = getgrnam("tty");
+	  if (grp)
+	    {
+	      tty_gid = grp->gr_gid;
+	      tty_mode = S_IRUSR|S_IWUSR|S_IWGRP;
+	    }
+	  else
+	    {
+	      tty_gid = pw->pw_gid;
+	      tty_mode = S_IRUSR|S_IWUSR|S_IWGRP|S_IWOTH;
+	    }
+
+	  /* Change ownership of the tty. */
+	  if (chown(ttyname, pw->pw_uid, tty_gid) < 0)
+	    fatal("chown(%.100s, %d, %d) failed: %.100s",
+		  ttyname, pw->pw_uid, tty_gid, strerror(errno));
+	  if (chmod(ttyname, tty_mode) < 0)
+	    fatal("chmod(%.100s, 0%o) failed: %.100s",
+		  ttyname, tty_mode, strerror(errno));
+
+	  /* Get TERM from the packet.  Note that the value may be of arbitrary
+	     length. */
+
+	  term = packet_get_string(&dlen);
+	  packet_integrity_check(dlen, strlen(term), type);
+	  /* packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type); */
+	  /* Remaining bytes */
+	  n_bytes = plen - (4 + dlen + 4*4);
+	  
+	  if (strcmp(term, "") == 0)
+	    term = NULL;
+
+	  /* Get window size from the packet. */
+	  row = packet_get_int();
+	  col = packet_get_int();
+	  xpixel = packet_get_int();
+	  ypixel = packet_get_int();
+	  pty_change_window_size(ptyfd, row, col, xpixel, ypixel);
+
+	  /* Get tty modes from the packet. */
+	  tty_parse_modes(ttyfd, &n_bytes);
+	  packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type);
+
+	  /* Indicate that we now have a pty. */
+	  have_pty = 1;
+	  break;
+
+	case SSH_CMSG_X11_REQUEST_FORWARDING:
+	  if (!options.x11_forwarding)
+	    {
+	      packet_send_debug("X11 forwarding disabled in server configuration file.");
+	      goto fail;
+	    }
+#ifdef XAUTH_PATH
+	  if (no_x11_forwarding_flag)
+	    {
+	      packet_send_debug("X11 forwarding not permitted for this authentication.");
+	      goto fail;
+	    }
+	  debug("Received request for X11 forwarding with auth spoofing.");
+	  if (display)
+	    packet_disconnect("Protocol error: X11 display already set.");
+	  {
+	    int proto_len, data_len;
+	    proto = packet_get_string(&proto_len);
+	    data = packet_get_string(&data_len);
+	    packet_integrity_check(plen, 4+proto_len + 4+data_len + 4, type);
+	  }
+	  if (packet_get_protocol_flags() & SSH_PROTOFLAG_SCREEN_NUMBER)
+	    screen = packet_get_int();
+	  else
+	    screen = 0;
+	  display = x11_create_display_inet(screen);
+	  if (!display)
+	    goto fail;
+
+	  /* Setup to always have a local .Xauthority. */
+	  xauthfile = xmalloc(MAXPATHLEN);
+	  snprintf(xauthfile, MAXPATHLEN, "/tmp/XauthXXXXXX");
+	  
+	  if ((xauthfd = mkstemp(xauthfile)) != -1) {
+	    fchown(xauthfd, pw->pw_uid, pw->pw_gid);
+	    close(xauthfd);
+	  }
+	  else {
+	    xfree(xauthfile);
+	    xauthfile = NULL;
+	  }
+	  break;
+#else /* XAUTH_PATH */
+	  /* No xauth program; we won't accept forwarding with spoofing. */
+	  packet_send_debug("No xauth program; cannot forward with spoofing.");
+	  goto fail;
+#endif /* XAUTH_PATH */
+
+	case SSH_CMSG_AGENT_REQUEST_FORWARDING:
+	  if (no_agent_forwarding_flag)
+	    {
+	      debug("Authentication agent forwarding not permitted for this authentication.");
+	      goto fail;
+	    }
+	  debug("Received authentication agent forwarding request.");
+	  auth_input_request_forwarding(pw);
+	  break;
+
+	case SSH_CMSG_PORT_FORWARD_REQUEST:
+	  if (no_port_forwarding_flag)
+	    {
+	      debug("Port forwarding not permitted for this authentication.");
+	      goto fail;
+	    }
+	  debug("Received TCP/IP port forwarding request.");
+	  channel_input_port_forward_request(pw->pw_uid == 0);
+	  break;
+
+	case SSH_CMSG_EXEC_SHELL:
+	  /* Set interactive/non-interactive mode. */
+	  packet_set_interactive(have_pty || display != NULL, 
+				 options.keepalives);
+	    
+	  if (forced_command != NULL)
+	    goto do_forced_command;
+	  debug("Forking shell.");
+	  packet_integrity_check(plen, 0, type);
+	  if (have_pty)
+	    do_exec_pty(NULL, ptyfd, ttyfd, ttyname, pw, term, display, proto,
+			data);
+	  else
+	    do_exec_no_pty(NULL, pw, display, proto, data);
+	  return;
+
+	case SSH_CMSG_EXEC_CMD:
+	  /* Set interactive/non-interactive mode. */
+	  packet_set_interactive(have_pty || display != NULL,
+				 options.keepalives);
+
+	  if (forced_command != NULL)
+	    goto do_forced_command;
+	  /* Get command from the packet. */
+	  {
+	    int dlen;
+	    command = packet_get_string(&dlen);
+	    debug("Executing command '%.500s'", command);
+	    packet_integrity_check(plen, 4 + dlen, type);
+	  }
+	  if (have_pty)
+	    do_exec_pty(command, ptyfd, ttyfd, ttyname, pw, term, display,
+			proto, data);
+	  else
+	    do_exec_no_pty(command, pw, display, proto, data);
+	  xfree(command);
+	  return;
+
+	case SSH_CMSG_MAX_PACKET_SIZE:
+      	  debug("The server does not support limiting packet size.");
+	  goto fail;
+
+	default:
+	  /* Any unknown messages in this phase are ignored, and a failure
+	     message is returned. */
+	  log("Unknown packet type received after authentication: %d", type);
+	  goto fail;
+	}
+
+      /* The request was successfully processed. */
+      packet_start(SSH_SMSG_SUCCESS);
+      packet_send();
+      packet_write_wait();
+
+      /* Enable compression now that we have replied if appropriate. */
+      if (enable_compression_after_reply)
+	{
+	  enable_compression_after_reply = 0;
+	  packet_start_compression(compression_level);
+	}
+
+      continue;
+
+    fail:
+      /* The request failed. */
+      packet_start(SSH_SMSG_FAILURE);
+      packet_send();
+      packet_write_wait();
+      continue;
+      
+    do_forced_command:
+      /* There is a forced command specified for this login.  Execute it. */
+      debug("Executing forced command: %.900s", forced_command);
+      if (have_pty)
+	do_exec_pty(forced_command, ptyfd, ttyfd, ttyname, pw, term, display,
+		    proto, data);
+      else
+	do_exec_no_pty(forced_command, pw, display, proto, data);
+      return;
+    }
+}
+
+/* This is called to fork and execute a command when we have no tty.  This
+   will call do_child from the child, and server_loop from the parent after
+   setting up file descriptors and such. */
+
+void do_exec_no_pty(const char *command, struct passwd *pw,
+		    const char *display, const char *auth_proto,
+		    const char *auth_data)
+{  
+  int pid;
+
+#ifdef USE_PIPES
+  int pin[2], pout[2], perr[2];
+  /* Allocate pipes for communicating with the program. */
+  if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0)
+    packet_disconnect("Could not create pipes: %.100s",
+		      strerror(errno));
+#else /* USE_PIPES */
+  int inout[2], err[2];
+  /* Uses socket pairs to communicate with the program. */
+  if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 ||
+      socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0)
+    packet_disconnect("Could not create socket pairs: %.100s",
+		      strerror(errno));
+#endif /* USE_PIPES */
+  
+  setproctitle("%s@notty", pw->pw_name);
+
+  /* Fork the child. */
+  if ((pid = fork()) == 0)
+    {
+      /* Child.  Reinitialize the log since the pid has changed. */
+      log_init(av0, debug_flag && !inetd_flag, debug_flag, 
+	       options.quiet_mode, options.log_facility);
+
+      /* Create a new session and process group since the 4.4BSD setlogin()
+	 affects the entire process group. */
+      if (setsid() < 0)
+	error("setsid failed: %.100s", strerror(errno));
+
+#ifdef USE_PIPES
+      /* Redirect stdin.  We close the parent side of the socket pair,
+         and make the child side the standard input. */
+      close(pin[1]);
+      if (dup2(pin[0], 0) < 0)
+	perror("dup2 stdin");
+      close(pin[0]);
+      
+      /* Redirect stdout. */
+      close(pout[0]);
+      if (dup2(pout[1], 1) < 0)
+	perror("dup2 stdout");
+      close(pout[1]);
+
+      /* Redirect stderr. */
+      close(perr[0]);
+      if (dup2(perr[1], 2) < 0)
+	perror("dup2 stderr");
+      close(perr[1]);
+#else /* USE_PIPES */
+      /* Redirect stdin, stdout, and stderr.  Stdin and stdout will use the
+	 same socket, as some programs (particularly rdist) seem to depend
+	 on it. */
+      close(inout[1]);
+      close(err[1]);
+      if (dup2(inout[0], 0) < 0) /* stdin */
+	perror("dup2 stdin");
+      if (dup2(inout[0], 1) < 0) /* stdout.  Note: same socket as stdin. */
+	perror("dup2 stdout");
+      if (dup2(err[0], 2) < 0) /* stderr */
+	perror("dup2 stderr");
+#endif /* USE_PIPES */
+
+      /* Do processing for the child (exec command etc). */
+      do_child(command, pw, NULL, display, auth_proto, auth_data, NULL);
+      /*NOTREACHED*/
+    }
+  if (pid < 0)
+    packet_disconnect("fork failed: %.100s", strerror(errno));
+#ifdef USE_PIPES
+  /* We are the parent.  Close the child sides of the pipes. */
+  close(pin[0]);
+  close(pout[1]);
+  close(perr[1]);
+
+  /* Enter the interactive session. */
+  server_loop(pid, pin[1], pout[0], perr[0]);
+  /* server_loop has closed pin[1], pout[1], and perr[1]. */
+#else /* USE_PIPES */
+  /* We are the parent.  Close the child sides of the socket pairs. */
+  close(inout[0]);
+  close(err[0]);
+  
+  /* Enter the interactive session.  Note: server_loop must be able to handle
+     the case that fdin and fdout are the same. */
+  server_loop(pid, inout[1], inout[1], err[1]);
+  /* server_loop has closed inout[1] and err[1]. */
+#endif /* USE_PIPES */
+}
+
+struct pty_cleanup_context
+{
+  const char *ttyname;
+  int pid;
+};
+
+/* Function to perform cleanup if we get aborted abnormally (e.g., due to a
+   dropped connection). */
+
+void pty_cleanup_proc(void *context)
+{
+  struct pty_cleanup_context *cu = context;
+
+  debug("pty_cleanup_proc called");
+
+#if defined(KRB4)
+  /* Destroy user's ticket cache file. */
+  (void) dest_tkt();
+#endif /* KRB4 */
+  
+  /* Record that the user has logged out. */
+  record_logout(cu->pid, cu->ttyname);
+
+  /* Release the pseudo-tty. */
+  pty_release(cu->ttyname);
+}
+
+/* This is called to fork and execute a command when we have a tty.  This
+   will call do_child from the child, and server_loop from the parent after
+   setting up file descriptors, controlling tty, updating wtmp, utmp,
+   lastlog, and other such operations. */
+
+void do_exec_pty(const char *command, int ptyfd, int ttyfd, 
+		 const char *ttyname, struct passwd *pw, const char *term,
+		 const char *display, const char *auth_proto, 
+		 const char *auth_data)
+{
+  int pid, fdout;
+  const char *hostname;
+  time_t last_login_time;
+  char buf[100], *time_string;
+  FILE *f;
+  char line[256];
+  struct stat st;
+  int quiet_login;
+  struct sockaddr_in from;
+  int fromlen;
+  struct pty_cleanup_context cleanup_context;
+
+  /* Get remote host name. */
+  hostname = get_canonical_hostname();
+
+  /* Get the time when the user last logged in.  Buf will be set to contain
+     the hostname the last login was from. */
+  if(!options.use_login) {
+    last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name,
+					  buf, sizeof(buf));
+  }
+
+  setproctitle("%s@%s", pw->pw_name, strrchr(ttyname, '/') + 1);
+
+  /* Fork the child. */
+  if ((pid = fork()) == 0)
+    { 
+      pid = getpid();
+
+      /* Child.  Reinitialize the log because the pid has changed. */
+      log_init(av0, debug_flag && !inetd_flag, debug_flag, options.quiet_mode, 
+	       options.log_facility);
+
+      /* Close the master side of the pseudo tty. */
+      close(ptyfd);
+
+      /* Make the pseudo tty our controlling tty. */
+      pty_make_controlling_tty(&ttyfd, ttyname);
+
+      /* Redirect stdin from the pseudo tty. */
+      if (dup2(ttyfd, fileno(stdin)) < 0)
+	error("dup2 stdin failed: %.100s", strerror(errno));
+
+      /* Redirect stdout to the pseudo tty. */
+      if (dup2(ttyfd, fileno(stdout)) < 0)
+	error("dup2 stdin failed: %.100s", strerror(errno));
+
+      /* Redirect stderr to the pseudo tty. */
+      if (dup2(ttyfd, fileno(stderr)) < 0)
+	error("dup2 stdin failed: %.100s", strerror(errno));
+
+      /* Close the extra descriptor for the pseudo tty. */
+      close(ttyfd);
+
+      /* Get IP address of client.  This is needed because we want to record 
+	 where the user logged in from.  If the connection is not a socket,
+	 let the ip address be 0.0.0.0. */
+      memset(&from, 0, sizeof(from));
+      if (packet_get_connection_in() == packet_get_connection_out())
+	{
+	  fromlen = sizeof(from);
+	  if (getpeername(packet_get_connection_in(),
+			  (struct sockaddr *)&from, &fromlen) < 0)
+	    fatal("getpeername: %.100s", strerror(errno));
+	}
+
+      /* Record that there was a login on that terminal. */
+      record_login(pid, ttyname, pw->pw_name, pw->pw_uid, hostname, 
+		   &from);
+
+      /* Check if .hushlogin exists. */
+      snprintf(line, sizeof line, "%.200s/.hushlogin", pw->pw_dir);
+      quiet_login = stat(line, &st) >= 0;
+      
+      /* If the user has logged in before, display the time of last login. 
+         However, don't display anything extra if a command has been 
+	 specified (so that ssh can be used to execute commands on a remote
+         machine without users knowing they are going to another machine). 
+         Login(1) will do this for us as well, so check if login(1) is used */
+      if (command == NULL && last_login_time != 0 && !quiet_login && 
+          !options.use_login)
+	{
+	  /* Convert the date to a string. */
+	  time_string = ctime(&last_login_time);
+	  /* Remove the trailing newline. */
+	  if (strchr(time_string, '\n'))
+	    *strchr(time_string, '\n') = 0;
+	  /* Display the last login time.  Host if displayed if known. */
+	  if (strcmp(buf, "") == 0)
+	    printf("Last login: %s\r\n", time_string);
+	  else
+	    printf("Last login: %s from %s\r\n", time_string, buf);
+	}
+
+      /* Print /etc/motd unless a command was specified or printing it was
+         disabled in server options or login(1) will be used.  Note that 
+         some machines appear to print it in /etc/profile or similar. */
+      if (command == NULL && options.print_motd && !quiet_login && 
+          !options.use_login)
+	{
+	  /* Print /etc/motd if it exists. */
+	  f = fopen("/etc/motd", "r");
+	  if (f)
+	    {
+	      while (fgets(line, sizeof(line), f))
+		fputs(line, stdout);
+	      fclose(f);
+	    }
+	}
+
+      /* Do common processing for the child, such as execing the command. */
+      do_child(command, pw, term, display, auth_proto, auth_data, ttyname);
+      /*NOTREACHED*/
+    }
+  if (pid < 0)
+    packet_disconnect("fork failed: %.100s", strerror(errno));
+  /* Parent.  Close the slave side of the pseudo tty. */
+  close(ttyfd);
+  
+  /* Create another descriptor of the pty master side for use as the standard
+     input.  We could use the original descriptor, but this simplifies code
+     in server_loop.  The descriptor is bidirectional. */
+  fdout = dup(ptyfd);
+  if (fdout < 0)
+    packet_disconnect("dup failed: %.100s", strerror(errno));
+
+  /* Add a cleanup function to clear the utmp entry and record logout time
+     in case we call fatal() (e.g., the connection gets closed). */
+  cleanup_context.pid = pid;
+  cleanup_context.ttyname = ttyname;
+  fatal_add_cleanup(pty_cleanup_proc, (void *)&cleanup_context);
+
+  /* Enter interactive session. */
+  server_loop(pid, ptyfd, fdout, -1);
+  /* server_loop has not closed ptyfd and fdout. */
+
+  /* Cancel the cleanup function. */
+  fatal_remove_cleanup(pty_cleanup_proc, (void *)&cleanup_context);
+
+  /* Record that the user has logged out. */
+  record_logout(pid, ttyname);
+
+  /* Release the pseudo-tty. */
+  pty_release(ttyname);
+
+  /* Close the server side of the socket pairs.  We must do this after the
+     pty cleanup, so that another process doesn't get this pty while we're
+     still cleaning up. */
+  close(ptyfd);
+  close(fdout);
+}
+
+/* Sets the value of the given variable in the environment.  If the variable
+   already exists, its value is overriden. */
+
+void child_set_env(char ***envp, unsigned int *envsizep, const char *name,
+		   const char *value)
+{
+  unsigned int i, namelen;
+  char **env;
+
+  /* Find the slot where the value should be stored.  If the variable already
+     exists, we reuse the slot; otherwise we append a new slot at the end
+     of the array, expanding if necessary. */
+  env = *envp;
+  namelen = strlen(name);
+  for (i = 0; env[i]; i++)
+    if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=')
+      break;
+  if (env[i])
+    {
+      /* Name already exists.  Reuse the slot. */
+      xfree(env[i]);
+    }
+  else
+    {
+      /* New variable.  Expand the array if necessary. */
+      if (i >= (*envsizep) - 1)
+	{
+	  (*envsizep) += 50;
+	  env = (*envp) = xrealloc(env, (*envsizep) * sizeof(char *));
+	}
+
+      /* Need to set the NULL pointer at end of array beyond the new 
+	 slot. */
+      env[i + 1] = NULL;
+    }
+
+  /* Allocate space and format the variable in the appropriate slot. */
+  env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1);
+  snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value);
+}
+
+/* Reads environment variables from the given file and adds/overrides them
+   into the environment.  If the file does not exist, this does nothing.
+   Otherwise, it must consist of empty lines, comments (line starts with '#')
+   and assignments of the form name=value.  No other forms are allowed. */
+
+void read_environment_file(char ***env, unsigned int *envsize,
+			   const char *filename)
+{
+  FILE *f;
+  char buf[4096];
+  char *cp, *value;
+  
+  /* Open the environment file. */
+  f = fopen(filename, "r");
+  if (!f)
+    return;  /* Not found. */
+  
+  /* Process each line. */
+  while (fgets(buf, sizeof(buf), f))
+    {
+      /* Skip leading whitespace. */
+      for (cp = buf; *cp == ' ' || *cp == '\t'; cp++)
+	;
+
+      /* Ignore empty and comment lines. */
+      if (!*cp || *cp == '#' || *cp == '\n')
+	continue;
+
+      /* Remove newline. */
+      if (strchr(cp, '\n'))
+	*strchr(cp, '\n') = '\0';
+
+      /* Find the equals sign.  Its lack indicates badly formatted line. */
+      value = strchr(cp, '=');
+      if (value == NULL)
+	{
+	  fprintf(stderr, "Bad line in %.100s: %.200s\n", filename, buf);
+	  continue;
+	}
+
+      /* Replace the equals sign by nul, and advance value to the value 
+	 string. */
+      *value = '\0';
+      value++;
+
+      /* Set the value in environment. */
+      child_set_env(env, envsize, cp, value);
+    }
+  
+  fclose(f);
+}
+
+/* Performs common processing for the child, such as setting up the 
+   environment, closing extra file descriptors, setting the user and group 
+   ids, and executing the command or shell. */
+
+void do_child(const char *command, struct passwd *pw, const char *term,
+	      const char *display, const char *auth_proto, 
+	      const char *auth_data, const char *ttyname)
+{
+  const char *shell, *cp = NULL;
+  char buf[256];
+  FILE *f;
+  unsigned int envsize, i;
+  char **env;
+  extern char **environ;
+  struct stat st;
+  char *argv[10];
+
+  /* Check /etc/nologin. */
+  f = fopen("/etc/nologin", "r");
+  if (f)
+    { /* /etc/nologin exists.  Print its contents and exit. */
+      while (fgets(buf, sizeof(buf), f))
+	fputs(buf, stderr);
+      fclose(f);
+      if (pw->pw_uid != 0)
+	exit(254);
+    }
+
+  /* Set login name in the kernel. */
+  if (setlogin(pw->pw_name) < 0)
+    error("setlogin failed: %s", strerror(errno));
+
+  /* Set uid, gid, and groups. */
+  /* Login(1) does this as well, and it needs uid 0 for the "-h" switch,
+     so we let login(1) to this for us. */
+  if(!options.use_login) {
+    if (getuid() == 0 || geteuid() == 0)
+      { 
+        if (setgid(pw->pw_gid) < 0)
+          {
+            perror("setgid");
+            exit(1);
+          }
+        /* Initialize the group list. */
+        if (initgroups(pw->pw_name, pw->pw_gid) < 0)
+          {
+            perror("initgroups");
+            exit(1);
+          }
+        endgrent();
+   
+        /* Permanently switch to the desired uid. */
+        permanently_set_uid(pw->pw_uid);
+      }
+   
+    if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid)
+      fatal("Failed to set uids to %d.", (int)pw->pw_uid);
+  }
+
+  /* Get the shell from the password data.  An empty shell field is legal,
+     and means /bin/sh. */
+  shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell;
+
+#ifdef AFS
+  /* Try to get AFS tokens for the local cell. */
+  if (k_hasafs()) {
+    char cell[64];
+    
+    if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0)
+      krb_afslog(cell, 0);
+
+    krb_afslog(0, 0);
+  }
+#endif /* AFS */
+  
+  /* Initialize the environment.  In the first part we allocate space for
+     all environment variables. */
+  envsize = 100;
+  env = xmalloc(envsize * sizeof(char *));
+  env[0] = NULL;
+
+  if(!options.use_login) {
+    /* Set basic environment. */
+    child_set_env(&env, &envsize, "USER", pw->pw_name);
+    child_set_env(&env, &envsize, "LOGNAME", pw->pw_name);
+    child_set_env(&env, &envsize, "HOME", pw->pw_dir);
+    child_set_env(&env, &envsize, "PATH", _PATH_STDPATH);
+   
+    snprintf(buf, sizeof buf, "%.200s/%.50s",
+      _PATH_MAILDIR, pw->pw_name);
+    child_set_env(&env, &envsize, "MAIL", buf);
+   
+    /* Normal systems set SHELL by default. */
+    child_set_env(&env, &envsize, "SHELL", shell);
+  }
+
+  /* Let it inherit timezone if we have one. */
+  if (getenv("TZ"))
+    child_set_env(&env, &envsize, "TZ", getenv("TZ"));
+
+  /* Set custom environment options from RSA authentication. */
+  while (custom_environment) 
+    {
+      struct envstring *ce = custom_environment;
+      char *s = ce->s;
+      int i;
+      for (i = 0; s[i] != '=' && s[i]; i++)
+	;
+      if (s[i] == '=') 
+	{
+	  s[i] = 0;
+	  child_set_env(&env, &envsize, s, s + i + 1);
+	}
+      custom_environment = ce->next;
+      xfree(ce->s);
+      xfree(ce);
+    }
+
+  /* Set SSH_CLIENT. */
+  snprintf(buf, sizeof buf, "%.50s %d %d", 
+	  get_remote_ipaddr(), get_remote_port(), options.port);
+  child_set_env(&env, &envsize, "SSH_CLIENT", buf);
+
+  /* Set SSH_TTY if we have a pty. */
+  if (ttyname)
+    child_set_env(&env, &envsize, "SSH_TTY", ttyname);
+
+  /* Set TERM if we have a pty. */
+  if (term)
+    child_set_env(&env, &envsize, "TERM", term);
+
+  /* Set DISPLAY if we have one. */
+  if (display)
+    child_set_env(&env, &envsize, "DISPLAY", display);
+
+#ifdef KRB4
+  if (ticket)
+    child_set_env(&env, &envsize, "KRBTKFILE", ticket);
+#endif /* KRB4 */
+
+  /* Set XAUTHORITY to always be a local file. */
+  if (xauthfile)
+      child_set_env(&env, &envsize, "XAUTHORITY", xauthfile);
+
+  /* Set variable for forwarded authentication connection, if we have one. */
+  if (auth_get_socket_name() != NULL)
+      child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, 
+		    auth_get_socket_name());
+
+  /* Read $HOME/.ssh/environment. */
+  if(!options.use_login) {
+    snprintf(buf, sizeof buf, "%.200s/.ssh/environment", pw->pw_dir);
+    read_environment_file(&env, &envsize, buf);
+  }
+
+  /* If debugging, dump the environment to stderr. */
+  if (debug_flag)
+    {
+      fprintf(stderr, "Environment:\n");
+      for (i = 0; env[i]; i++)
+	fprintf(stderr, "  %.200s\n", env[i]);
+    }
+
+  /* Close the connection descriptors; note that this is the child, and the 
+     server will still have the socket open, and it is important that we
+     do not shutdown it.  Note that the descriptors cannot be closed before
+     building the environment, as we call get_remote_ipaddr there. */
+  if (packet_get_connection_in() == packet_get_connection_out())
+    close(packet_get_connection_in());
+  else
+    {
+      close(packet_get_connection_in());
+      close(packet_get_connection_out());
+    }
+  /* Close all descriptors related to channels.  They will still remain
+     open in the parent. */
+  channel_close_all();
+
+  /* Close any extra file descriptors.  Note that there may still be
+     descriptors left by system functions.  They will be closed later. */
+  endpwent();
+  endhostent();
+
+  /* Close any extra open file descriptors so that we don\'t have them
+     hanging around in clients.  Note that we want to do this after
+     initgroups, because at least on Solaris 2.3 it leaves file descriptors
+     open. */
+  for (i = 3; i < 64; i++)
+    close(i);
+
+  /* Change current directory to the user\'s home directory. */
+  if (chdir(pw->pw_dir) < 0)
+    fprintf(stderr, "Could not chdir to home directory %s: %s\n",
+	    pw->pw_dir, strerror(errno));
+
+  /* Must take new environment into use so that .ssh/rc, /etc/sshrc and
+     xauth are run in the proper environment. */
+  environ = env;
+
+  /* Run $HOME/.ssh/rc, /etc/sshrc, or xauth (whichever is found first
+     in this order). */
+  if(!options.use_login) {
+    if (stat(SSH_USER_RC, &st) >= 0)
+      {
+        if (debug_flag)
+      	fprintf(stderr, "Running /bin/sh %s\n", SSH_USER_RC);
+ 
+        f = popen("/bin/sh " SSH_USER_RC, "w");
+        if (f)
+      	{
+      	  if (auth_proto != NULL && auth_data != NULL)
+      	    fprintf(f, "%s %s\n", auth_proto, auth_data);
+      	  pclose(f);
+      	}
+        else
+      	fprintf(stderr, "Could not run %s\n", SSH_USER_RC);
+      }
+    else
+      if (stat(SSH_SYSTEM_RC, &st) >= 0)
+        {
+      	if (debug_flag)
+      	  fprintf(stderr, "Running /bin/sh %s\n", SSH_SYSTEM_RC);
+ 
+      	f = popen("/bin/sh " SSH_SYSTEM_RC, "w");
+      	if (f)
+      	  {
+      	    if (auth_proto != NULL && auth_data != NULL)
+      	      fprintf(f, "%s %s\n", auth_proto, auth_data);
+      	    pclose(f);
+      	  }
+      	else
+      	  fprintf(stderr, "Could not run %s\n", SSH_SYSTEM_RC);
+        }
+#ifdef XAUTH_PATH
+      else
+        {
+      	/* Add authority data to .Xauthority if appropriate. */
+      	if (auth_proto != NULL && auth_data != NULL)
+      	  {
+      	    if (debug_flag)
+      	      fprintf(stderr, "Running %.100s add %.100s %.100s %.100s\n",
+      		      XAUTH_PATH, display, auth_proto, auth_data);
+      	    
+      	    f = popen(XAUTH_PATH " -q -", "w");
+      	    if (f)
+      	      {
+      		fprintf(f, "add %s %s %s\n", display, auth_proto, auth_data);
+      		fclose(f);
+      	      }
+      	    else
+      	      fprintf(stderr, "Could not run %s -q -\n", XAUTH_PATH);
+      	  }
+        }
+#endif /* XAUTH_PATH */
+
+    /* Get the last component of the shell name. */
+    cp = strrchr(shell, '/');
+    if (cp)
+      cp++;
+    else
+      cp = shell;
+  }
+
+  /* If we have no command, execute the shell.  In this case, the shell name
+     to be passed in argv[0] is preceded by '-' to indicate that this is
+     a login shell. */
+  if (!command)
+    {
+      if(!options.use_login) {
+        char buf[256];
+
+        /* Check for mail if we have a tty and it was enabled in server options. */
+        if (ttyname && options.check_mail) {
+          char *mailbox;
+          struct stat mailstat;
+          mailbox = getenv("MAIL");
+          if(mailbox != NULL) {
+            if(stat(mailbox, &mailstat) != 0 || mailstat.st_size == 0) {
+              printf("No mail.\n");
+            } else if(mailstat.st_mtime < mailstat.st_atime) {
+              printf("You have mail.\n");
+            } else {
+              printf("You have new mail.\n");
+            }
+          }
+        }
+        /* Start the shell.  Set initial character to '-'. */
+        buf[0] = '-';
+        strncpy(buf + 1, cp, sizeof(buf) - 1);
+        buf[sizeof(buf) - 1] = 0;
+        /* Execute the shell. */
+        argv[0] = buf;
+        argv[1] = NULL;
+        execve(shell, argv, env);
+        /* Executing the shell failed. */
+        perror(shell);
+        exit(1);
+
+      } else {
+        /* Launch login(1). */
+
+        execl("/usr/bin/login", "login", "-h", get_remote_ipaddr(), "-p", "-f", "--", pw->pw_name, NULL);
+
+        /* Login couldn't be executed, die. */
+
+        perror("login");
+        exit(1);
+      }
+    }
+
+  /* Execute the command using the user's shell.  This uses the -c option
+     to execute the command. */
+  argv[0] = (char *)cp;
+  argv[1] = "-c";
+  argv[2] = (char *)command;
+  argv[3] = NULL;
+  execve(shell, argv, env);
+  perror(shell);
+  exit(1);
+}
diff --git a/sshd.init b/sshd.init
new file mode 100755
index 0000000..b36b57a
--- /dev/null
+++ b/sshd.init
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# Init file for OpenSSH sshd
+#
+# chkconfig: 2345 55 25
+# description: OpenSSH server daemon
+#
+# processname: sshd
+# config: /etc/ssh/ssh_host_key
+# config: /etc/ssh/ssh_host_key.pub
+# config: /etc/ssh/ssh_random_seed
+# config: /etc/ssh/sshd_config
+# pidfile: /var/run/sshd.pid
+
+# source function library
+. /etc/rc.d/init.d/functions
+
+RETVAL=0
+
+case "$1" in
+  start)
+	echo -n "Starting sshd: "
+        daemon /usr/sbin/sshd
+	RETVAL=$?
+	[ $RETVAL -eq 0 ] && touch /var/lock/subsys/sshd
+	echo
+	;;
+  stop)
+	echo -n "Shutting down sshd: "
+	killproc sshd
+	RETVAL=$?
+	[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/sshd
+	echo
+	;;
+  restart)
+        $0 stop
+        $0 start
+	RETVAL=$?
+        ;;
+  status)
+        status sshd
+	RETVAL=$?
+        ;;
+  *)
+	echo "Usage: sshd {start|stop|restart|status}"
+	exit 1
+esac
+
+exit $RETVAL
diff --git a/sshd_config b/sshd_config
new file mode 100644
index 0000000..97f6f8e
--- /dev/null
+++ b/sshd_config
@@ -0,0 +1,44 @@
+# This is ssh server systemwide configuration file.
+
+Port 22
+ListenAddress 0.0.0.0
+HostKey /etc/ssh/ssh_host_key
+ServerKeyBits 768
+LoginGraceTime 600
+KeyRegenerationInterval 3600
+PermitRootLogin yes
+#
+# Don't read ~/.rhosts and ~/.shosts files
+IgnoreRhosts yes
+StrictModes yes
+QuietMode no
+X11Forwarding yes
+X11DisplayOffset 10
+FascistLogging no
+PrintMotd yes
+KeepAlive yes
+SyslogFacility AUTH
+RhostsAuthentication no
+#
+# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
+RhostsRSAAuthentication no
+#
+RSAAuthentication yes
+
+# To disable tunneled clear text passwords, change to no here!
+PasswordAuthentication yes
+PermitEmptyPasswords no
+# Uncomment to disable s/key passwords 
+#SkeyAuthentication no
+
+# To change Kerberos options
+#KerberosAuthentication no
+#KerberosOrLocalPasswd yes
+#AFSTokenPassing no
+#KerberosTicketCleanup no
+
+# Kerberos TGT Passing does only work with the AFS kaserver
+#KerberosTgtPassing yes
+
+#CheckMail yes
+#UseLogin no
diff --git a/strlcpy.c b/strlcpy.c
new file mode 100644
index 0000000..300a28b
--- /dev/null
+++ b/strlcpy.c
@@ -0,0 +1,68 @@
+/*	$OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $	*/
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Copy src to string dst of size siz.  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t strlcpy(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;
+
+	/* Copy as many bytes as will fit */
+	if (n != 0 && --n != 0) {
+		do {
+			if ((*d++ = *s++) == 0)
+				break;
+		} while (--n != 0);
+	}
+
+	/* Not enough room in dst, add NUL and traverse rest of src */
+	if (n == 0) {
+		if (siz != 0)
+			*d = '\0';		/* NUL-terminate dst */
+		while (*s++)
+			;
+	}
+
+	return(s - src - 1);	/* count does not include NUL */
+}
diff --git a/strlcpy.h b/strlcpy.h
new file mode 100644
index 0000000..824df30
--- /dev/null
+++ b/strlcpy.h
@@ -0,0 +1,4 @@
+#ifndef _STRLCPY_H
+#define _STRLCPY_H
+size_t strlcpy(char *dst, const char *src, size_t siz);
+#endif /* _STRLCPY_H */
diff --git a/tildexpand.c b/tildexpand.c
new file mode 100644
index 0000000..e4b5709
--- /dev/null
+++ b/tildexpand.c
@@ -0,0 +1,70 @@
+/*
+
+tildexpand.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Wed Jul 12 01:07:36 1995 ylo
+
+*/
+
+#include "includes.h"
+RCSID("$Id: tildexpand.c,v 1.1 1999/10/27 03:42:46 damien Exp $");
+
+#include "xmalloc.h"
+#include "ssh.h"
+
+/* Expands tildes in the file name.  Returns data allocated by xmalloc.
+   Warning: this calls getpw*. */
+
+char *tilde_expand_filename(const char *filename, uid_t my_uid)
+{
+  const char *cp;
+  unsigned int userlen;
+  char *expanded;
+  struct passwd *pw;
+  char user[100];
+
+  /* Return immediately if no tilde. */
+  if (filename[0] != '~')
+    return xstrdup(filename);
+
+  /* Skip the tilde. */
+  filename++;
+
+  /* Find where the username ends. */
+  cp = strchr(filename, '/');
+  if (cp)
+    userlen = cp - filename;  /* Have something after username. */
+  else
+    userlen = strlen(filename); /* Nothign after username. */
+  if (userlen == 0)
+    pw = getpwuid(my_uid);  /* Own home directory. */
+  else
+    {
+      /* Tilde refers to someone elses home directory. */
+      if (userlen > sizeof(user) - 1)
+	fatal("User name after tilde too long.");
+      memcpy(user, filename, userlen);
+      user[userlen] = 0;
+      pw = getpwnam(user);
+    }
+
+  /* Check that we found the user. */
+  if (!pw)
+    fatal("Unknown user %100s.", user);
+  
+  /* If referring to someones home directory, return it now. */
+  if (!cp)
+    { /* Only home directory specified */
+      return xstrdup(pw->pw_dir);
+    }
+  
+  /* Build a path combining the specified directory and path. */
+  expanded = xmalloc(strlen(pw->pw_dir) + strlen(cp + 1) + 2);
+  sprintf(expanded, "%s/%s", pw->pw_dir, cp + 1);
+  return expanded;
+}
diff --git a/ttymodes.c b/ttymodes.c
new file mode 100644
index 0000000..cbb7f2f
--- /dev/null
+++ b/ttymodes.c
@@ -0,0 +1,359 @@
+/*
+
+ttymodes.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Tue Mar 21 15:59:15 1995 ylo
+
+Encoding and decoding of terminal modes in a portable way.
+Much of the format is defined in ttymodes.h; it is included multiple times
+into this file with the appropriate macro definitions to generate the
+suitable code.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: ttymodes.c,v 1.1 1999/10/27 03:42:46 damien Exp $");
+
+#include "packet.h"
+#include "ssh.h"
+
+#define TTY_OP_END	0
+#define TTY_OP_ISPEED	192 /* int follows */
+#define TTY_OP_OSPEED	193 /* int follows */
+
+/* Converts POSIX speed_t to a baud rate.  The values of the constants
+   for speed_t are not themselves portable. */
+
+static int speed_to_baud(speed_t speed)
+{
+  switch (speed)
+    {
+    case B0:
+      return 0;
+    case B50:
+      return 50;
+    case B75:
+      return 75;
+    case B110:
+      return 110;
+    case B134:
+      return 134;
+    case B150:
+      return 150;
+    case B200:
+      return 200;
+    case B300:
+      return 300;
+    case B600:
+      return 600;
+    case B1200:
+      return 1200;
+    case B1800:
+      return 1800;
+    case B2400:
+      return 2400;
+    case B4800:
+      return 4800;
+    case B9600:
+      return 9600;
+
+#ifdef B19200
+    case B19200:
+      return 19200;
+#else /* B19200 */
+#ifdef EXTA
+    case EXTA:
+      return 19200;
+#endif /* EXTA */
+#endif /* B19200 */
+
+#ifdef B38400
+    case B38400:
+      return 38400;
+#else /* B38400 */
+#ifdef EXTB
+    case EXTB:
+      return 38400;
+#endif /* EXTB */
+#endif /* B38400 */
+
+#ifdef B7200
+    case B7200:
+      return 7200;
+#endif /* B7200 */
+#ifdef B14400
+    case B14400:
+      return 14400;
+#endif /* B14400 */
+#ifdef B28800
+    case B28800:
+      return 28800;
+#endif /* B28800 */
+#ifdef B57600
+    case B57600:
+      return 57600;
+#endif /* B57600 */
+#ifdef B76800
+    case B76800:
+      return 76800;
+#endif /* B76800 */
+#ifdef B115200
+    case B115200:
+      return 115200;
+#endif /* B115200 */
+#ifdef B230400
+    case B230400:
+      return 230400;
+#endif /* B230400 */
+    default:
+      return 9600;
+    }
+}
+
+/* Converts a numeric baud rate to a POSIX speed_t. */
+
+static speed_t baud_to_speed(int baud)
+{
+  switch (baud)
+    {
+    case 0:
+      return B0;
+    case 50:
+      return B50;
+    case 75:
+      return B75;
+    case 110:
+      return B110;
+    case 134:
+      return B134;
+    case 150:
+      return B150;
+    case 200:
+      return B200;
+    case 300:
+      return B300;
+    case 600:
+      return B600;
+    case 1200:
+      return B1200;
+    case 1800:
+      return B1800;
+    case 2400:
+      return B2400;
+    case 4800:
+      return B4800;
+    case 9600:
+      return B9600;
+
+#ifdef B19200
+    case 19200:
+      return B19200;
+#else /* B19200 */
+#ifdef EXTA
+    case 19200:
+      return EXTA;
+#endif /* EXTA */
+#endif /* B19200 */
+
+#ifdef B38400
+    case 38400:
+      return B38400;
+#else /* B38400 */
+#ifdef EXTB
+    case 38400:
+      return EXTB;
+#endif /* EXTB */
+#endif /* B38400 */
+
+#ifdef B7200
+    case 7200:
+      return B7200;
+#endif /* B7200 */
+#ifdef B14400
+    case 14400:
+      return B14400;
+#endif /* B14400 */
+#ifdef B28800
+    case 28800:
+      return B28800;
+#endif /* B28800 */
+#ifdef B57600
+    case 57600:
+      return B57600;
+#endif /* B57600 */
+#ifdef B76800
+    case 76800:
+      return B76800;
+#endif /* B76800 */
+#ifdef B115200
+    case 115200:
+      return B115200;
+#endif /* B115200 */
+#ifdef B230400
+    case 230400:
+      return B230400;
+#endif /* B230400 */
+    default:
+      return B9600;
+    }
+}
+
+/* Encodes terminal modes for the terminal referenced by fd in a portable
+   manner, and appends the modes to a packet being constructed. */
+
+void tty_make_modes(int fd)
+{
+  struct termios tio;
+  int baud;
+
+  /* Get the modes. */
+  if (tcgetattr(fd, &tio) < 0)
+    {
+      packet_put_char(TTY_OP_END);
+      log("tcgetattr: %.100s", strerror(errno));
+      return;
+    }
+
+  /* Store input and output baud rates. */
+  baud = speed_to_baud(cfgetospeed(&tio));
+  packet_put_char(TTY_OP_OSPEED);
+  packet_put_int(baud);
+  baud = speed_to_baud(cfgetispeed(&tio));
+  packet_put_char(TTY_OP_ISPEED);
+  packet_put_int(baud);
+
+  /* Store values of mode flags. */
+#define TTYCHAR(NAME, OP) \
+  packet_put_char(OP); packet_put_char(tio.c_cc[NAME]);
+#define TTYMODE(NAME, FIELD, OP) \
+  packet_put_char(OP); packet_put_char((tio.FIELD & NAME) != 0);
+#define SGTTYCHAR(NAME, OP)
+#define SGTTYMODE(NAME, FIELD, OP)
+#define SGTTYMODEN(NAME, FIELD, OP)
+
+#include "ttymodes.h"
+
+#undef TTYCHAR
+#undef TTYMODE
+#undef SGTTYCHAR
+#undef SGTTYMODE
+#undef SGTTYMODEN
+
+  /* Mark end of mode data. */
+  packet_put_char(TTY_OP_END);
+}
+
+/* Decodes terminal modes for the terminal referenced by fd in a portable
+   manner from a packet being read. */
+
+void tty_parse_modes(int fd, int *n_bytes_ptr)
+{
+  struct termios tio;
+  int opcode, baud;
+  int n_bytes = 0;
+  int failure = 0;
+
+  /* Get old attributes for the terminal.  We will modify these flags. 
+     I am hoping that if there are any machine-specific modes, they will
+     initially have reasonable values. */
+  if (tcgetattr(fd, &tio) < 0)
+    failure = -1;
+
+  for (;;)
+    {
+      n_bytes += 1;
+      opcode = packet_get_char();
+      switch (opcode)
+	{
+	case TTY_OP_END:
+	  goto set;
+
+	case TTY_OP_ISPEED:
+	  n_bytes += 4;
+	  baud = packet_get_int();
+	  if (failure != -1 && cfsetispeed(&tio, baud_to_speed(baud)) < 0)
+	    error("cfsetispeed failed for %d", baud);
+	  break;
+
+	case TTY_OP_OSPEED:
+	  n_bytes += 4;
+	  baud = packet_get_int();
+	  if (failure != -1 && cfsetospeed(&tio, baud_to_speed(baud)) < 0)
+	    error("cfsetospeed failed for %d", baud);
+	  break;
+
+#define TTYCHAR(NAME, OP) 				\
+	case OP:					\
+	  n_bytes += 1;					\
+	  tio.c_cc[NAME] = packet_get_char();		\
+	  break;
+#define TTYMODE(NAME, FIELD, OP)		       	\
+	case OP:					\
+	  n_bytes += 1;					\
+	  if (packet_get_char())			\
+	    tio.FIELD |= NAME;				\
+	  else						\
+	    tio.FIELD &= ~NAME;				\
+	  break;
+#define SGTTYCHAR(NAME, OP)
+#define SGTTYMODE(NAME, FIELD, OP)
+#define SGTTYMODEN(NAME, FIELD, OP)
+
+#include "ttymodes.h"
+
+#undef TTYCHAR
+#undef TTYMODE
+#undef SGTTYCHAR
+#undef SGTTYMODE
+#undef SGTTYMODEN
+
+	default:
+	  debug("Ignoring unsupported tty mode opcode %d (0x%x)",
+		opcode, opcode);
+	  /* Opcodes 0 to 127 are defined to have a one-byte argument. */
+	  if (opcode >= 0 && opcode < 128)
+	    {
+	      n_bytes += 1;
+	      (void)packet_get_char();
+	      break;
+	    }
+	  else
+	    {
+	      /* Opcodes 128 to 159 are defined to have an integer argument. */
+	      if (opcode >= 128 && opcode < 160)
+		{
+		  n_bytes += 4;
+		  (void)packet_get_int();
+		  break;
+		}
+	    }
+	  /* It is a truly undefined opcode (160 to 255).  We have no idea
+	     about its arguments.  So we must stop parsing.  Note that some
+	     data may be left in the packet; hopefully there is nothing more
+	     coming after the mode data. */
+	  log("parse_tty_modes: unknown opcode %d", opcode);
+	  packet_integrity_check(0, 1, SSH_CMSG_REQUEST_PTY);
+	  goto set;
+	}
+    }
+
+ set:
+  if (*n_bytes_ptr != n_bytes)
+    {
+      *n_bytes_ptr = n_bytes;
+      return;			/* Don't process bytes passed */
+    }
+
+  if (failure == -1)
+    return;			/* Packet parsed ok but tty stuff failed */
+  
+  /* Set the new modes for the terminal. */
+  if (tcsetattr(fd, TCSANOW, &tio) < 0)
+    log("Setting tty modes failed: %.100s", strerror(errno));
+  return;
+}
diff --git a/ttymodes.h b/ttymodes.h
new file mode 100644
index 0000000..2a33eb7
--- /dev/null
+++ b/ttymodes.h
@@ -0,0 +1,138 @@
+/*
+
+ttymodes.h
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+	SGTTY stuff contributed by Janne Snabb <snabb@niksula.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Tue Mar 21 15:42:09 1995 ylo
+
+*/
+
+/* RCSID("$Id: ttymodes.h,v 1.1 1999/10/27 03:42:46 damien Exp $"); */
+
+/* The tty mode description is a stream of bytes.  The stream consists of
+   opcode-arguments pairs.  It is terminated by opcode TTY_OP_END (0).
+   Opcodes 1-127 have one-byte arguments.  Opcodes 128-159 have integer
+   arguments.  Opcodes 160-255 are not yet defined, and cause parsing to
+   stop (they should only be used after any other data).
+
+   The client puts in the stream any modes it knows about, and the
+   server ignores any modes it does not know about.  This allows some degree
+   of machine-independence, at least between systems that use a posix-like
+   tty interface.  The protocol can support other systems as well, but might
+   require reimplementing as mode names would likely be different. */
+     
+/* Some constants and prototypes are defined in packet.h; this file
+   is only intended for including from ttymodes.h. */
+
+/* termios macro */		/* sgtty macro */
+/* name, op */
+TTYCHAR(VINTR, 1) 		SGTTYCHAR(tiotc.t_intrc, 1)
+TTYCHAR(VQUIT, 2)		SGTTYCHAR(tiotc.t_quitc, 2)
+TTYCHAR(VERASE, 3)		SGTTYCHAR(tio.sg_erase, 3)
+#if defined(VKILL)
+TTYCHAR(VKILL, 4)		SGTTYCHAR(tio.sg_kill, 4)
+#endif /* VKILL */
+TTYCHAR(VEOF, 5)		SGTTYCHAR(tiotc.t_eofc, 5)
+#if defined(VEOL)
+TTYCHAR(VEOL, 6)		SGTTYCHAR(tiotc.t_brkc, 6)
+#endif /* VEOL */
+#ifdef VEOL2			/* n/a */
+TTYCHAR(VEOL2, 7)
+#endif /* VEOL2 */
+TTYCHAR(VSTART, 8)		SGTTYCHAR(tiotc.t_startc, 8)
+TTYCHAR(VSTOP, 9)		SGTTYCHAR(tiotc.t_stopc, 9)
+#if defined(VSUSP)
+TTYCHAR(VSUSP, 10)		SGTTYCHAR(tioltc.t_suspc, 10)
+#endif /* VSUSP */
+#if defined(VDSUSP)
+TTYCHAR(VDSUSP, 11)		SGTTYCHAR(tioltc.t_dsuspc, 11)
+#endif /* VDSUSP */
+#if defined(VREPRINT)
+TTYCHAR(VREPRINT, 12)		SGTTYCHAR(tioltc.t_rprntc, 12)
+#endif /* VREPRINT */
+#if defined(VWERASE)
+TTYCHAR(VWERASE, 13)		SGTTYCHAR(tioltc.t_werasc, 13)
+#endif /* VWERASE */
+#if defined(VLNEXT)
+TTYCHAR(VLNEXT, 14)		SGTTYCHAR(tioltc.t_lnextc, 14)
+#endif /* VLNEXT */
+#if defined(VFLUSH)
+TTYCHAR(VFLUSH, 15)		SGTTYCHAR(tioltc.t_flushc, 15)
+#endif /* VFLUSH */
+#ifdef VSWTCH
+TTYCHAR(VSWTCH, 16)		/* n/a */
+#endif /* VSWTCH */
+#if defined(VSTATUS)
+TTYCHAR(VSTATUS, 17)		SGTTYCHAR(tiots.tc_statusc, 17)
+#endif /* VSTATUS */
+#ifdef VDISCARD
+TTYCHAR(VDISCARD, 18)		/* n/a */
+#endif /* VDISCARD */
+
+/* name, field, op */
+TTYMODE(IGNPAR,	c_iflag, 30)	/* n/a */
+TTYMODE(PARMRK,	c_iflag, 31)	/* n/a */
+TTYMODE(INPCK, 	c_iflag, 32)	SGTTYMODEN(ANYP, tio.sg_flags, 32)
+TTYMODE(ISTRIP,	c_iflag, 33)	SGTTYMODEN(LPASS8, tiolm, 33)
+TTYMODE(INLCR, 	c_iflag, 34)	/* n/a */
+TTYMODE(IGNCR, 	c_iflag, 35)	/* n/a */
+TTYMODE(ICRNL, 	c_iflag, 36)	SGTTYMODE(CRMOD, tio.sg_flags, 36)
+#if defined(IUCLC)
+TTYMODE(IUCLC, 	c_iflag, 37)	SGTTYMODE(LCASE, tio.sg_flags, 37)
+#endif
+TTYMODE(IXON,  	c_iflag, 38)	/* n/a */
+TTYMODE(IXANY, 	c_iflag, 39)	SGTTYMODEN(LDECCTQ, tiolm, 39)
+TTYMODE(IXOFF, 	c_iflag, 40)	SGTTYMODE(TANDEM, tio.sg_flags, 40)
+#ifdef IMAXBEL
+TTYMODE(IMAXBEL,c_iflag, 41)	/* n/a */
+#endif /* IMAXBEL */
+
+TTYMODE(ISIG,	c_lflag, 50)	/* n/a */
+TTYMODE(ICANON,	c_lflag, 51)	SGTTYMODEN(CBREAK, tio.sg_flags, 51)
+#ifdef XCASE
+TTYMODE(XCASE,	c_lflag, 52)	/* n/a */
+#endif
+TTYMODE(ECHO,	c_lflag, 53)	SGTTYMODE(ECHO, tio.sg_flags, 53)
+TTYMODE(ECHOE,	c_lflag, 54)	SGTTYMODE(LCRTERA, tiolm, 54)
+TTYMODE(ECHOK,	c_lflag, 55)	SGTTYMODE(LCRTKIL, tiolm, 55)
+TTYMODE(ECHONL,	c_lflag, 56)	/* n/a */
+TTYMODE(NOFLSH,	c_lflag, 57)	SGTTYMODE(LNOFLSH, tiolm, 57)
+TTYMODE(TOSTOP,	c_lflag, 58)	SGTTYMODE(LTOSTOP, tiolm, 58)
+#ifdef IEXTEN
+TTYMODE(IEXTEN, c_lflag, 59)	/* n/a */
+#endif /* IEXTEN */
+#if defined(ECHOCTL)
+TTYMODE(ECHOCTL,c_lflag, 60)	SGTTYMODE(LCTLECH, tiolm, 60)
+#endif /* ECHOCTL */
+#ifdef ECHOKE
+TTYMODE(ECHOKE,	c_lflag, 61)	/* n/a */
+#endif /* ECHOKE */
+#if defined(PENDIN)
+TTYMODE(PENDIN,	c_lflag, 62)	SGTTYMODE(LPENDIN, tiolm, 62)
+#endif /* PENDIN */
+
+TTYMODE(OPOST,	c_oflag, 70)	/* n/a */
+#if defined(OLCUC)
+TTYMODE(OLCUC,	c_oflag, 71)	SGTTYMODE(LCASE, tio.sg_flags, 71)
+#endif
+TTYMODE(ONLCR,	c_oflag, 72)	SGTTYMODE(CRMOD, tio.sg_flags, 72)
+#ifdef OCRNL
+TTYMODE(OCRNL,	c_oflag, 73)	/* n/a */
+#endif
+#ifdef ONOCR
+TTYMODE(ONOCR,	c_oflag, 74)	/* n/a */
+#endif
+#ifdef ONLRET
+TTYMODE(ONLRET,	c_oflag, 75)	/* n/a */
+#endif
+
+TTYMODE(CS7,	c_cflag, 90)	/* n/a */
+TTYMODE(CS8,	c_cflag, 91)	SGTTYMODE(LPASS8, tiolm, 91)
+TTYMODE(PARENB,	c_cflag, 92)	/* n/a */
+TTYMODE(PARODD,	c_cflag, 93)	SGTTYMODE(ODDP, tio.sg_flags, 93)
+
diff --git a/uidswap.c b/uidswap.c
new file mode 100644
index 0000000..0eb1fd0
--- /dev/null
+++ b/uidswap.c
@@ -0,0 +1,95 @@
+/*
+
+uidswap.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Sat Sep  9 01:56:14 1995 ylo
+
+Code for uid-swapping.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: uidswap.c,v 1.1 1999/10/27 03:42:46 damien Exp $");
+
+#include "ssh.h"
+#include "uidswap.h"
+
+/* Note: all these functions must work in all of the following cases:
+
+   1. euid=0, ruid=0
+   2. euid=0, ruid!=0
+   3. euid!=0, ruid!=0
+
+   Additionally, they must work regardless of whether the system has
+   POSIX saved uids or not. */
+
+#ifdef _POSIX_SAVED_IDS
+/* Lets assume that posix saved ids also work with seteuid, even though that
+   is not part of the posix specification. */
+#define SAVED_IDS_WORK_WITH_SETEUID
+#endif /* _POSIX_SAVED_IDS */
+
+/* Saved effective uid. */
+static uid_t saved_euid = 0;
+
+/* Temporarily changes to the given uid.  If the effective user id is not
+   root, this does nothing.  This call cannot be nested. */
+
+void temporarily_use_uid(uid_t uid)
+{
+#ifdef SAVED_IDS_WORK_WITH_SETEUID
+
+  /* Save the current euid. */
+  saved_euid = geteuid();
+
+  /* Set the effective uid to the given (unprivileged) uid. */
+  if (seteuid(uid) == -1)
+    debug("seteuid %d: %.100s", (int)uid, strerror(errno));
+
+#else /* SAVED_IDS_WORK_WITH_SETUID */
+
+  /* Propagate the privileged uid to all of our uids. */
+  if (setuid(geteuid()) < 0)
+    debug("setuid %d: %.100s", (int)geteuid(), strerror(errno));
+
+  /* Set the effective uid to the given (unprivileged) uid. */
+  if (seteuid(uid) == -1)
+    debug("seteuid %d: %.100s", (int)uid, strerror(errno));
+
+#endif /* SAVED_IDS_WORK_WITH_SETEUID */
+
+}
+
+/* Restores to the original uid. */
+
+void restore_uid()
+{
+#ifdef SAVED_IDS_WORK_WITH_SETEUID
+
+  /* Set the effective uid back to the saved uid. */
+  if (seteuid(saved_euid) < 0)
+    debug("seteuid %d: %.100s", (int)saved_euid, strerror(errno));
+
+#else /* SAVED_IDS_WORK_WITH_SETEUID */
+
+  /* We are unable to restore the real uid to its unprivileged value. */
+  /* Propagate the real uid (usually more privileged) to effective uid
+     as well. */
+  setuid(getuid());
+
+#endif /* SAVED_IDS_WORK_WITH_SETEUID */
+}
+
+/* Permanently sets all uids to the given uid.  This cannot be called while
+   temporarily_use_uid is effective. */
+
+void permanently_set_uid(uid_t uid)
+{
+  if (setuid(uid) < 0)
+    debug("setuid %d: %.100s", (int)uid, strerror(errno));
+}
diff --git a/uidswap.h b/uidswap.h
new file mode 100644
index 0000000..af4f924
--- /dev/null
+++ b/uidswap.h
@@ -0,0 +1,30 @@
+/*
+
+uidswap.h
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Sat Sep  9 01:43:15 1995 ylo
+Last modified: Sat Sep  9 02:34:04 1995 ylo
+
+*/
+
+#ifndef UIDSWAP_H
+#define UIDSWAP_H
+
+/* Temporarily changes to the given uid.  If the effective user id is not
+   root, this does nothing.  This call cannot be nested. */
+void temporarily_use_uid(uid_t uid);
+
+/* Restores the original effective user id after temporarily_use_uid().
+   This should only be called while temporarily_use_uid is effective. */
+void restore_uid();
+
+/* Permanently sets all uids to the given uid.  This cannot be called while
+   temporarily_use_uid is effective.  This must also clear any saved uids. */
+void permanently_set_uid(uid_t uid);
+
+#endif /* UIDSWAP_H */
diff --git a/version.h b/version.h
new file mode 100644
index 0000000..8d2fc5c
--- /dev/null
+++ b/version.h
@@ -0,0 +1 @@
+#define SSH_VERSION	"OpenSSH-1.2"
diff --git a/xmalloc.c b/xmalloc.c
new file mode 100644
index 0000000..b536f9d
--- /dev/null
+++ b/xmalloc.c
@@ -0,0 +1,56 @@
+/*
+
+xmalloc.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Mon Mar 20 21:23:10 1995 ylo
+
+Versions of malloc and friends that check their results, and never return
+failure (they call fatal if they encounter an error).
+
+*/
+
+#include "includes.h"
+RCSID("$Id: xmalloc.c,v 1.1 1999/10/27 03:42:46 damien Exp $");
+
+#include "ssh.h"
+
+void *xmalloc(size_t size)
+{
+  void *ptr = malloc(size);
+  if (ptr == NULL)
+    fatal("xmalloc: out of memory (allocating %d bytes)", (int)size);
+  return ptr;
+}
+
+void *xrealloc(void *ptr, size_t new_size)
+{
+  void *new_ptr;
+
+  if (ptr == NULL)
+    fatal("xrealloc: NULL pointer given as argument");
+  new_ptr = realloc(ptr, new_size);
+  if (new_ptr == NULL)
+    fatal("xrealloc: out of memory (new_size %d bytes)", (int)new_size);
+  return new_ptr;
+}
+
+void xfree(void *ptr)
+{
+  if (ptr == NULL)
+    fatal("xfree: NULL pointer given as argument");
+  free(ptr);
+}
+
+char *xstrdup(const char *str)
+{
+  int len = strlen(str) + 1;
+
+  char *cp = xmalloc(len);
+  strlcpy(cp, str, len);
+  return cp;
+}
diff --git a/xmalloc.h b/xmalloc.h
new file mode 100644
index 0000000..7a9610e
--- /dev/null
+++ b/xmalloc.h
@@ -0,0 +1,34 @@
+/*
+
+xmalloc.h
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                   All rights reserved
+
+Created: Mon Mar 20 22:09:17 1995 ylo
+
+Versions of malloc and friends that check their results, and never return
+failure (they call fatal if they encounter an error).
+
+*/
+
+/* RCSID("$Id: xmalloc.h,v 1.1 1999/10/27 03:42:46 damien Exp $"); */
+
+#ifndef XMALLOC_H
+#define XMALLOC_H
+
+/* Like malloc, but calls fatal() if out of memory. */
+void *xmalloc(size_t size);
+
+/* Like realloc, but calls fatal() if out of memory. */
+void *xrealloc(void *ptr, size_t new_size);
+
+/* Frees memory allocated using xmalloc or xrealloc. */
+void xfree(void *ptr);
+
+/* Allocates memory using xmalloc, and copies the string into that memory. */
+char *xstrdup(const char *str);
+
+#endif /* XMALLOC_H */