summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--COPYING.Ylonen70
-rw-r--r--ChangeLog578
-rw-r--r--ChangeLog.linux20
-rw-r--r--Makefile13
-rw-r--r--Makefile.GNU50
-rw-r--r--Makefile.inc11
-rw-r--r--OVERVIEW164
-rw-r--r--README563
-rw-r--r--README.openssh44
-rw-r--r--RFC.nroff1780
-rw-r--r--auth-krb4.c209
-rw-r--r--auth-passwd.c209
-rw-r--r--auth-rh-rsa.c83
-rw-r--r--auth-rhosts.c298
-rw-r--r--auth-rsa.c478
-rw-r--r--auth-skey.c149
-rw-r--r--authfd.c565
-rw-r--r--authfd.h102
-rw-r--r--authfile.c350
-rw-r--r--bufaux.c141
-rw-r--r--bufaux.h51
-rw-r--r--buffer.c150
-rw-r--r--buffer.h66
-rw-r--r--canohost.c234
-rw-r--r--channels.c1500
-rw-r--r--channels.h41
-rw-r--r--cipher.c304
-rw-r--r--cipher.h84
-rw-r--r--clientloop.c924
-rw-r--r--compat.c10
-rw-r--r--compat.h7
-rw-r--r--compress.c160
-rw-r--r--compress.h46
-rw-r--r--crc32.c120
-rw-r--r--crc32.h25
-rw-r--r--deattack.c180
-rw-r--r--deattack.h27
-rw-r--r--getput.h64
-rw-r--r--helper.c108
-rw-r--r--helper.h43
-rw-r--r--hostfile.c279
-rw-r--r--includes.h78
-rw-r--r--log-client.c138
-rw-r--r--log-server.c233
-rw-r--r--login.c118
-rw-r--r--match.c78
-rw-r--r--mktemp.c181
-rw-r--r--mktemp.h7
-rw-r--r--mpaux.c46
-rw-r--r--mpaux.h32
-rw-r--r--nchan.c187
-rw-r--r--nchan.h57
-rw-r--r--nchan.ms71
-rw-r--r--openssh.spec105
-rw-r--r--packet.c762
-rw-r--r--packet.h166
-rw-r--r--pty.c264
-rw-r--r--pty.h40
-rw-r--r--radix.c258
-rw-r--r--rc4.c105
-rw-r--r--rc4.h110
-rw-r--r--readconf.c684
-rw-r--r--readconf.h116
-rw-r--r--readpass.c114
-rw-r--r--rsa.c164
-rw-r--r--rsa.h36
-rw-r--r--scp.1110
-rw-r--r--scp.c1220
-rw-r--r--servconf.c567
-rw-r--r--servconf.h86
-rw-r--r--serverloop.c644
-rw-r--r--ssh-add.1116
-rw-r--r--ssh-add.c254
-rw-r--r--ssh-agent.1124
-rw-r--r--ssh-agent.c572
-rw-r--r--ssh-keygen.1155
-rw-r--r--ssh-keygen.c552
-rw-r--r--ssh.1966
-rw-r--r--ssh.c809
-rw-r--r--ssh.h589
-rw-r--r--ssh.pam7
-rw-r--r--ssh_config30
-rw-r--r--sshconnect.c1495
-rw-r--r--sshd.8781
-rw-r--r--sshd.c2445
-rwxr-xr-xsshd.init49
-rw-r--r--sshd_config44
-rw-r--r--strlcpy.c68
-rw-r--r--strlcpy.h4
-rw-r--r--tildexpand.c70
-rw-r--r--ttymodes.c359
-rw-r--r--ttymodes.h138
-rw-r--r--uidswap.c95
-rw-r--r--uidswap.h30
-rw-r--r--version.h1
-rw-r--r--xmalloc.c56
-rw-r--r--xmalloc.h34
97 files changed, 26920 insertions, 0 deletions
diff --git a/COPYING.Ylonen b/COPYING.Ylonen
new file mode 100644
index 00000000..5e681edd
--- /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 00000000..08d90f78
--- /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 00000000..a28e577a
--- /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 00000000..668900c3
--- /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 00000000..f36bdb3d
--- /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 00000000..fddf3da2
--- /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 00000000..a8b67e4e
--- /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 00000000..ed360844
--- /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 00000000..02cb3c60
--- /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 00000000..cc197aaf
--- /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 00000000..720f3a4c
--- /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 00000000..7d684678
--- /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 00000000..c433578b
--- /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 00000000..ebf2fcbc
--- /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 00000000..8de86d2d
--- /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 00000000..9ec17049
--- /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 00000000..07893caf
--- /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 00000000..1def920e
--- /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 00000000..49390e08
--- /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 00000000..1ae39d67
--- /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 00000000..bfc66848
--- /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 00000000..e183d101
--- /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 00000000..d0369dc3
--- /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 00000000..85d97292
--- /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 00000000..38a65a07
--- /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 00000000..9794ef50
--- /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 00000000..b47e7ecd
--- /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 00000000..4ecb8f8d
--- /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 00000000..43373b72
--- /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 00000000..4974b1cb
--- /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 00000000..9d896c7d
--- /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 00000000..c3267f73
--- /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 00000000..b3144d62
--- /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 00000000..dbb1e6b7
--- /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 00000000..456b20b8
--- /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 00000000..d5f8608c
--- /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 00000000..a0dcf5b6
--- /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 00000000..7b5d7425
--- /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 00000000..3b0402ec
--- /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 00000000..2f09daa8
--- /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 00000000..ca0fe88a
--- /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 00000000..862dbd64
--- /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 00000000..1792ba84
--- /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 00000000..fce96b01
--- /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 00000000..0c1e61b7
--- /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 00000000..b7a7d338
--- /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 00000000..919c5317
--- /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 00000000..5d380058
--- /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 00000000..fd2c1803
--- /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 00000000..3ad06813
--- /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 00000000..fcaeae40
--- /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 00000000..16d360d3
--- /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 00000000..b01512f7
--- /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 00000000..7ce58849
--- /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 00000000..7e74c73b
--- /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 00000000..fb84e6c1
--- /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 00000000..440994b5
--- /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 00000000..20ee90a1
--- /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 00000000..1c497945
--- /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 00000000..a426188a
--- /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 00000000..904affec
--- /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 00000000..281548d2
--- /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 00000000..71655bd2
--- /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 00000000..3031825e
--- /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 00000000..6d4b7044
--- /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 00000000..6aaabfae
--- /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 00000000..45cd2ad3
--- /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 00000000..6145fdeb
--- /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 00000000..5fa48790
--- /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 00000000..22c73fd7
--- /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 00000000..552c69c2
--- /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 00000000..4c64ab2b
--- /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 00000000..5ac3c303
--- /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 00000000..01c43cde
--- /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 00000000..19165b8f
--- /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 00000000..67fbfd2c
--- /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 00000000..2ba64e75
--- /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 *