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 *private_key;
+
+/* Generated public key. */
+RSA *public_key;
+
+/* Number of bits in the RSA key. This value can be changed on the command
+ line. */
+int bits = 1024;
+
+/* Flag indicating that we just want to change the passphrase. This can be
+ set on the command line. */
+int change_passphrase = 0;
+
+/* Flag indicating that we just want to change the comment. This can be set
+ on the command line. */
+int change_comment = 0;
+
+int quiet = 0;
+
+/* This is set to the identity file name if given on the command line. */
+char *identity_file = NULL;
+
+/* This is set to the passphrase if given on the command line. */
+char *identity_passphrase = NULL;
+
+/* This is set to the new passphrase if given on the command line. */
+char *identity_new_passphrase = NULL;
+
+/* This is set to the new comment if given on the command line. */
+char *identity_comment = NULL;
+
+/* Perform changing a passphrase. The argument is the passwd structure
+ for the current user. */
+
+void
+do_change_passphrase(struct passwd *pw)
+{
+ char buf[1024], *comment;
+ char *old_passphrase, *passphrase1, *passphrase2;
+ struct stat st;
+ RSA *private_key;
+
+ /* Read key file name. */
+ if (identity_file != NULL) {
+ strncpy(buf, identity_file, sizeof(buf));
+ buf[sizeof(buf) - 1] = '\0';
+ } else {
+ printf("Enter file in which the key is ($HOME/%s): ", SSH_CLIENT_IDENTITY);
+ fflush(stdout);
+ if (fgets(buf, sizeof(buf), stdin) == NULL)
+ exit(1);
+ if (strchr(buf, '\n'))
+ *strchr(buf, '\n') = 0;
+ if (strcmp(buf, "") == 0)
+ snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY);
+ }
+
+ /* Check if the file exists. */
+ if (stat(buf, &st) < 0)
+ {
+ perror(buf);
+ exit(1);
+ }
+
+ /* Try to load the public key from the file the verify that it is
+ readable and of the proper format. */
+ public_key = RSA_new();
+ if (!load_public_key(buf, public_key, NULL))
+ {
+ printf("%s is not a valid key file.\n", buf);
+ exit(1);
+ }
+ /* Clear the public key since we are just about to load the whole file. */
+ RSA_free(public_key);
+
+ /* Try to load the file with empty passphrase. */
+ private_key = RSA_new();
+ if (!load_private_key(buf, "", private_key, &comment)) {
+ /* Read passphrase from the user. */
+ if (identity_passphrase)
+ old_passphrase = xstrdup(identity_passphrase);
+ else
+ old_passphrase = read_passphrase("Enter old passphrase: ", 1);
+ /* Try to load using the passphrase. */
+ if (!load_private_key(buf, old_passphrase, private_key, &comment))
+ {
+ memset(old_passphrase, 0, strlen(old_passphrase));
+ xfree(old_passphrase);
+ printf("Bad passphrase.\n");
+ exit(1);
+ }
+ /* Destroy the passphrase. */
+ memset(old_passphrase, 0, strlen(old_passphrase));
+ xfree(old_passphrase);
+ }
+ printf("Key has comment '%s'\n", comment);
+
+ /* Ask the new passphrase (twice). */
+ if (identity_new_passphrase)
+ {
+ passphrase1 = xstrdup(identity_new_passphrase);
+ passphrase2 = NULL;
+ }
+ else
+ {
+ passphrase1 =
+ read_passphrase("Enter new passphrase (empty for no passphrase): ", 1);
+ passphrase2 = read_passphrase("Enter same passphrase again: ", 1);
+
+ /* Verify that they are the same. */
+ if (strcmp(passphrase1, passphrase2) != 0)
+ {
+ memset(passphrase1, 0, strlen(passphrase1));
+ memset(passphrase2, 0, strlen(passphrase2));
+ xfree(passphrase1);
+ xfree(passphrase2);
+ printf("Pass phrases do not match. Try again.\n");
+ exit(1);
+ }
+ /* Destroy the other copy. */
+ memset(passphrase2, 0, strlen(passphrase2));
+ xfree(passphrase2);
+ }
+
+ /* Save the file using the new passphrase. */
+ if (!save_private_key(buf, passphrase1, private_key, comment))
+ {
+ printf("Saving the key failed: %s: %s.\n",
+ buf, strerror(errno));
+ memset(passphrase1, 0, strlen(passphrase1));
+ xfree(passphrase1);
+ RSA_free(private_key);
+ xfree(comment);
+ exit(1);
+ }
+ /* Destroy the passphrase and the copy of the key in memory. */
+ memset(passphrase1, 0, strlen(passphrase1));
+ xfree(passphrase1);
+ RSA_free(private_key); /* Destroys contents */
+ xfree(comment);
+
+ printf("Your identification has been saved with the new passphrase.\n");
+ exit(0);
+}
+
+/* Change the comment of a private key file. */
+
+void
+do_change_comment(struct passwd *pw)
+{
+ char buf[1024], new_comment[1024], *comment;
+ RSA *private_key;
+ char *passphrase;
+ struct stat st;
+ FILE *f;
+ char *tmpbuf;
+
+ /* Read key file name. */
+ if (identity_file)
+ {
+ strncpy(buf, identity_file, sizeof(buf));
+ buf[sizeof(buf) - 1] = '\0';
+ }
+ else
+ {
+ printf("Enter file in which the key is ($HOME/%s): ",
+ SSH_CLIENT_IDENTITY);
+ fflush(stdout);
+ if (fgets(buf, sizeof(buf), stdin) == NULL)
+ exit(1);
+ if (strchr(buf, '\n'))
+ *strchr(buf, '\n') = 0;
+ if (strcmp(buf, "") == 0)
+ snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY);
+ }
+
+ /* Check if the file exists. */
+ if (stat(buf, &st) < 0)
+ {
+ perror(buf);
+ exit(1);
+ }
+
+ /* Try to load the public key from the file the verify that it is
+ readable and of the proper format. */
+ public_key = RSA_new();
+ if (!load_public_key(buf, public_key, NULL))
+ {
+ printf("%s is not a valid key file.\n", buf);
+ exit(1);
+ }
+
+ private_key = RSA_new();
+ /* Try to load the file with empty passphrase. */
+ if (load_private_key(buf, "", private_key, &comment))
+ passphrase = xstrdup("");
+ else
+ {
+ /* Read passphrase from the user. */
+ if (identity_passphrase)
+ passphrase = xstrdup(identity_passphrase);
+ else
+ if (identity_new_passphrase)
+ passphrase = xstrdup(identity_new_passphrase);
+ else
+ passphrase = read_passphrase("Enter passphrase: ", 1);
+ /* Try to load using the passphrase. */
+ if (!load_private_key(buf, passphrase, private_key, &comment))
+ {
+ memset(passphrase, 0, strlen(passphrase));
+ xfree(passphrase);
+ printf("Bad passphrase.\n");
+ exit(1);
+ }
+ }
+ printf("Key now has comment '%s'\n", comment);
+
+ if (identity_comment)
+ {
+ strncpy(new_comment, identity_comment, sizeof(new_comment));
+ new_comment[sizeof(new_comment) - 1] = '\0';
+ }
+ else
+ {
+ printf("Enter new comment: ");
+ fflush(stdout);
+ if (!fgets(new_comment, sizeof(new_comment), stdin))
+ {
+ memset(passphrase, 0, strlen(passphrase));
+ RSA_free(private_key);
+ exit(1);
+ }
+
+ /* Remove terminating newline from comment. */
+ if (strchr(new_comment, '\n'))
+ *strchr(new_comment, '\n') = 0;
+ }
+
+ /* Save the file using the new passphrase. */
+ if (!save_private_key(buf, passphrase, private_key, new_comment))
+ {
+ printf("Saving the key failed: %s: %s.\n",
+ buf, strerror(errno));
+ memset(passphrase, 0, strlen(passphrase));
+ xfree(passphrase);
+ RSA_free(private_key);
+ xfree(comment);
+ exit(1);
+ }
+
+ /* Destroy the passphrase and the private key in memory. */
+ memset(passphrase, 0, strlen(passphrase));
+ xfree(passphrase);
+ RSA_free(private_key);
+
+ /* Save the public key in text format in a file with the same name but
+ .pub appended. */
+ strcat(buf, ".pub");
+ f = fopen(buf, "w");
+ if (!f)
+ {
+ printf("Could not save your public key in %s\n", buf);
+ exit(1);
+ }
+ fprintf(f, "%d ", BN_num_bits(public_key->n));
+ tmpbuf = BN_bn2dec(public_key->e);
+ fprintf(f, "%s ", tmpbuf);
+ free (tmpbuf);
+ tmpbuf = BN_bn2dec(public_key->n);
+ fprintf(f, "%s %s\n", tmpbuf, new_comment);
+ free (tmpbuf);
+ fclose(f);
+
+ xfree(comment);
+
+ printf("The comment in your key file has been changed.\n");
+ exit(0);
+}
+
+/* Main program for key management. */
+
+int
+main(int ac, char **av)
+{
+ char buf[16384], buf2[1024], *passphrase1, *passphrase2;
+ struct passwd *pw;
+ char *tmpbuf;
+ int opt;
+ struct stat st;
+ FILE *f;
+ char hostname[MAXHOSTNAMELEN];
+ extern int optind;
+ extern char *optarg;
+
+ /* check if RSA support exists */
+ if (rsa_alive() == 0) {
+ extern char *__progname;
+
+ fprintf(stderr,
+ "%s: no RSA support in libssl and libcrypto. See ssl(8).\n",
+ __progname);
+ exit(1);
+ }
+
+ /* Get user\'s passwd structure. We need this for the home directory. */
+ pw = getpwuid(getuid());
+ if (!pw)
+ {
+ printf("You don't exist, go away!\n");
+ exit(1);
+ }
+
+ /* Create ~/.ssh directory if it doesn\'t already exist. */
+ snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_USER_DIR);
+ if (stat(buf, &st) < 0)
+ if (mkdir(buf, 0755) < 0)
+ error("Could not create directory '%s'.", buf);
+
+ /* Parse command line arguments. */
+ while ((opt = getopt(ac, av, "qpcb:f:P:N:C:")) != EOF)
+ {
+ switch (opt)
+ {
+ case 'b':
+ bits = atoi(optarg);
+ if (bits < 512 || bits > 32768)
+ {
+ printf("Bits has bad value.\n");
+ exit(1);
+ }
+ break;
+
+ case 'p':
+ change_passphrase = 1;
+ break;
+
+ case 'c':
+ change_comment = 1;
+ break;
+
+ case 'f':
+ identity_file = optarg;
+ break;
+
+ case 'P':
+ identity_passphrase = optarg;
+ break;
+
+ case 'N':
+ identity_new_passphrase = optarg;
+ break;
+
+ case 'C':
+ identity_comment = optarg;
+ break;
+
+ case 'q':
+ quiet = 1;
+ break;
+
+ case '?':
+ default:
+ printf("ssh-keygen version %s\n", SSH_VERSION);
+ printf("Usage: %s [-b bits] [-p] [-c] [-f file] [-P pass] [-N new-pass] [-C comment]\n", av[0]);
+ exit(1);
+ }
+ }
+ if (optind < ac)
+ {
+ printf("Too many arguments.\n");
+ exit(1);
+ }
+ if (change_passphrase && change_comment)
+ {
+ printf("Can only have one of -p and -c.\n");
+ exit(1);
+ }
+
+ /* If the user requested to change the passphrase, do it now. This
+ function never returns. */
+ if (change_passphrase)
+ do_change_passphrase(pw);
+
+ /* If the user requested to change the comment, do it now. This function
+ never returns. */
+ if (change_comment)
+ do_change_comment(pw);
+
+ arc4random_stir();
+
+ if (quiet)
+ rsa_set_verbose(0);
+
+ /* Generate the rsa key pair. */
+ private_key = RSA_new();
+ public_key = RSA_new();
+ rsa_generate_key(private_key, public_key, bits);
+
+ ask_file_again:
+
+ /* Ask for a file to save the key in. */
+ if (identity_file)
+ {
+ strncpy(buf, identity_file, sizeof(buf));
+ buf[sizeof(buf) - 1] = '\0';
+ }
+ else
+ {
+ printf("Enter file in which to save the key ($HOME/%s): ",
+ SSH_CLIENT_IDENTITY);
+ fflush(stdout);
+ if (fgets(buf, sizeof(buf), stdin) == NULL)
+ exit(1);
+ if (strchr(buf, '\n'))
+ *strchr(buf, '\n') = 0;
+ if (strcmp(buf, "") == 0)
+ snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY);
+ }
+
+ /* If the file aready exists, ask the user to confirm. */
+ if (stat(buf, &st) >= 0)
+ {
+ printf("%s already exists.\n", buf);
+ printf("Overwrite (y/n)? ");
+ fflush(stdout);
+ if (fgets(buf2, sizeof(buf2), stdin) == NULL)
+ exit(1);
+ if (buf2[0] != 'y' && buf2[0] != 'Y')
+ exit(1);
+ }
+
+ /* Ask for a passphrase (twice). */
+ if (identity_passphrase)
+ passphrase1 = xstrdup(identity_passphrase);
+ else
+ if (identity_new_passphrase)
+ passphrase1 = xstrdup(identity_new_passphrase);
+ else
+ {
+ passphrase_again:
+ passphrase1 =
+ read_passphrase("Enter passphrase (empty for no passphrase): ", 1);
+ passphrase2 = read_passphrase("Enter same passphrase again: ", 1);
+ if (strcmp(passphrase1, passphrase2) != 0)
+ {
+ /* The passphrases do not match. Clear them and retry. */
+ memset(passphrase1, 0, strlen(passphrase1));
+ memset(passphrase2, 0, strlen(passphrase2));
+ xfree(passphrase1);
+ xfree(passphrase2);
+ printf("Passphrases do not match. Try again.\n");
+ goto passphrase_again;
+ }
+ /* Clear the other copy of the passphrase. */
+ memset(passphrase2, 0, strlen(passphrase2));
+ xfree(passphrase2);
+ }
+
+ /* Create default commend field for the passphrase. The user can later
+ edit this field. */
+ if (identity_comment)
+ {
+ strlcpy(buf2, identity_comment, sizeof(buf2));
+ }
+ else
+ {
+ if (gethostname(hostname, sizeof(hostname)) < 0)
+ {
+ perror("gethostname");
+ exit(1);
+ }
+ snprintf(buf2, sizeof buf2, "%s@%s", pw->pw_name, hostname);
+ }
+
+ /* Save the key with the given passphrase and comment. */
+ if (!save_private_key(buf, passphrase1, private_key, buf2))
+ {
+ printf("Saving the key failed: %s: %s.\n",
+ buf, strerror(errno));
+ memset(passphrase1, 0, strlen(passphrase1));
+ xfree(passphrase1);
+ goto ask_file_again;
+ }
+ /* Clear the passphrase. */
+ memset(passphrase1, 0, strlen(passphrase1));
+ xfree(passphrase1);
+
+ /* Clear the private key and the random number generator. */
+ RSA_free(private_key);
+ arc4random_stir();
+
+ if (!quiet)
+ printf("Your identification has been saved in %s.\n", buf);
+
+ /* Display the public key on the screen. */
+ if (!quiet) {
+ printf("Your public key is:\n");
+ printf("%d ", BN_num_bits(public_key->n));
+ tmpbuf = BN_bn2dec(public_key->e);
+ printf("%s ", tmpbuf);
+ free(tmpbuf);
+ tmpbuf = BN_bn2dec(public_key->n);
+ printf("%s %s\n", tmpbuf, buf2);
+ free(tmpbuf);
+ }
+
+ /* Save the public key in text format in a file with the same name but
+ .pub appended. */
+ strcat(buf, ".pub");
+ f = fopen(buf, "w");
+ if (!f)
+ {
+ printf("Could not save your public key in %s\n", buf);
+ exit(1);
+ }
+ fprintf(f, "%d ", BN_num_bits(public_key->n));
+ tmpbuf = BN_bn2dec(public_key->e);
+ fprintf(f, "%s ", tmpbuf);
+ free(tmpbuf);
+ tmpbuf = BN_bn2dec(public_key->n);
+ fprintf(f, "%s %s\n", tmpbuf, buf2);
+ free(tmpbuf);
+ fclose(f);
+
+ if (!quiet)
+ printf("Your public key has been saved in %s\n", buf);
+
+ exit(0);
+}
diff --git a/ssh.1 b/ssh.1
new file mode 100644
index 00000000..a6d76a97
--- /dev/null
+++ b/ssh.1
@@ -0,0 +1,966 @@
+.\" -*- nroff -*-
+.\"
+.\" ssh.1.in
+.\"
+.\" Author: Tatu Ylonen <ylo@cs.hut.fi>
+.\"
+.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+.\" All rights reserved
+.\"
+.\" Created: Sat Apr 22 21:55:14 1995 ylo
+.\"
+.\" $Id: ssh.1,v 1.1 1999/10/27 03:42:45 damien Exp $
+.\"
+.Dd September 25, 1999
+.Dt SSH 1
+.Os
+.Sh NAME
+.Nm ssh
+.Nd OpenSSH secure shell client (remote login program)
+.Sh SYNOPSIS
+.Nm ssh
+.Op Fl l Ar login_name
+.Op Ar hostname | user@hostname
+.Op Ar command
+.Pp
+.Nm ssh
+.Op Fl afgknqtvxCPX
+.Op Fl c Ar blowfish | 3des
+.Op Fl e Ar escape_char
+.Op Fl i Ar identity_file
+.Op Fl l Ar login_name
+.Op Fl o Ar option
+.Op Fl p Ar port
+.Oo Fl L Xo
+.Sm off
+.Ar host :
+.Ar port :
+.Ar hostport
+.Sm on
+.Xc
+.Oc
+.Oo Fl R Xo
+.Sm off
+.Ar host :
+.Ar port :
+.Ar hostport
+.Sm on
+.Xc
+.Oc
+.Op Ar hostname | user@hostname
+.Op Ar command
+.Sh DESCRIPTION
+.Nm
+(Secure Shell) is a program for logging into a remote machine and for
+executing commands on a remote machine. It is intended to replace
+rlogin and rsh, and provide secure encrypted communications between
+two untrusted hosts over an insecure network. X11 connections and
+arbitrary TCP/IP ports can also be forwarded over the secure channel.
+.Pp
+.Nm
+connects and logs into the specified
+.Ar hostname .
+The user must prove
+his/her identity to the remote machine using one of several methods.
+.Pp
+First, if the machine the user logs in from is listed in
+.Pa /etc/hosts.equiv
+or
+.Pa /etc/shosts.equiv
+on the remote machine, and the user names are
+the same on both sides, the user is immediately permitted to log in.
+Second, if
+.Pa \&.rhosts
+or
+.Pa \&.shosts
+exists in the user's home directory on the
+remote machine and contains a line containing the name of the client
+machine and the name of the user on that machine, the user is
+permitted to log in. This form of authentication alone is normally not
+allowed by the server because it is not secure.
+.Pp
+The second (and primary) authentication method is the
+.Pa rhosts
+or
+.Pa hosts.equiv
+method combined with RSA-based host authentication. It
+means that if the login would be permitted by
+.Pa \&.rhosts ,
+.Pa \&.shosts ,
+.Pa /etc/hosts.equiv ,
+or
+.Pa /etc/shosts.equiv ,
+and if additionally the server can verify the client's
+host key (see
+.Pa /etc/ssh_known_hosts
+in the
+.Sx FILES
+section), only then login is
+permitted. This authentication method closes security holes due to IP
+spoofing, DNS spoofing and routing spoofing. [Note to the
+administrator:
+.Pa /etc/hosts.equiv ,
+.Pa \&.rhosts ,
+and the rlogin/rsh protocol in general, are inherently insecure and should be
+disabled if security is desired.]
+.Pp
+As a third authentication method,
+.Nm
+supports RSA based authentication.
+The scheme is based on public-key cryptography: there are cryptosystems
+where encryption and decryption are done using separate keys, and it
+is not possible to derive the decryption key from the encryption key.
+RSA is one such system. The idea is that each user creates a public/private
+key pair for authentication purposes. The
+server knows the public key, and only the user knows the private key.
+The file
+.Pa $HOME/.ssh/authorized_keys
+lists the public keys that are permitted for logging
+in. When the user logs in, the
+.Nm
+program tells the server which key pair it would like to use for
+authentication. The server checks if this key is permitted, and if
+so, sends the user (actually the
+.Nm
+program running on behalf of the user) a challenge, a random number,
+encrypted by the user's public key. The challenge can only be
+decrypted using the proper private key. The user's client then decrypts the
+challenge using the private key, proving that he/she knows the private
+key but without disclosing it to the server.
+.Pp
+.Nm
+implements the RSA authentication protocol automatically. The user
+creates his/her RSA key pair by running
+.Xr ssh-keygen 1 .
+This stores the private key in
+.Pa \&.ssh/identity
+and the public key in
+.Pa \&.ssh/identity.pub
+in the user's home directory. The user should then
+copy the
+.Pa identity.pub
+to
+.Pa \&.ssh/authorized_keys
+in his/her home directory on the remote machine (the
+.Pa authorized_keys
+file corresponds to the conventional
+.Pa \&.rhosts
+file, and has one key
+per line, though the lines can be very long). After this, the user
+can log in without giving the password. RSA authentication is much
+more secure than rhosts authentication.
+.Pp
+The most convenient way to use RSA authentication may be with an
+authentication agent. See
+.Xr ssh-agent 1
+for more information.
+.Pp
+If other authentication methods fail,
+.Nm
+prompts the user for a password. The password is sent to the remote
+host for checking; however, since all communications are encrypted,
+the password cannot be seen by someone listening on the network.
+.Pp
+When the user's identity has been accepted by the server, the server
+either executes the given command, or logs into the machine and gives
+the user a normal shell on the remote machine. All communication with
+the remote command or shell will be automatically encrypted.
+.Pp
+If a pseudo-terminal has been allocated (normal login session), the
+user can disconnect with
+.Ic ~. ,
+and suspend
+.Nm
+with
+.Ic ~^Z .
+All forwarded connections can be listed with
+.Ic ~#
+and if
+the session blocks waiting for forwarded X11 or TCP/IP
+connections to terminate, it can be backgrounded with
+.Ic ~&
+(this should not be used while the user shell is active, as it can cause the
+shell to hang). All available escapes can be listed with
+.Ic ~? .
+.Pp
+A single tilde character can be sent as
+.Ic ~~
+(or by following the tilde by a character other than those described above).
+The escape character must always follow a newline to be interpreted as
+special. The escape character can be changed in configuration files
+or on the command line.
+.Pp
+If no pseudo tty has been allocated, the
+session is transparent and can be used to reliably transfer binary
+data. On most systems, setting the escape character to
+.Dq none
+will also make the session transparent even if a tty is used.
+.Pp
+The session terminates when the command or shell in on the remote
+machine exists and all X11 and TCP/IP connections have been closed.
+The exit status of the remote program is returned as the exit status
+of
+.Nm ssh .
+.Pp
+If the user is using X11 (the
+.Ev DISPLAY
+environment variable is set), the connection to the X11 display is
+automatically forwarded to the remote side in such a way that any X11
+programs started from the shell (or command) will go through the
+encrypted channel, and the connection to the real X server will be made
+from the local machine. The user should not manually set
+.Ev DISPLAY .
+Forwarding of X11 connections can be
+configured on the command line or in configuration files.
+.Pp
+The
+.Ev DISPLAY
+value set by
+.Nm
+will point to the server machine, but with a display number greater
+than zero. This is normal, and happens because
+.Nm
+creates a
+.Dq proxy
+X server on the server machine for forwarding the
+connections over the encrypted channel.
+.Pp
+.Nm
+will also automatically set up Xauthority data on the server machine.
+For this purpose, it will generate a random authorization cookie,
+store it in Xauthority on the server, and verify that any forwarded
+connections carry this cookie and replace it by the real cookie when
+the connection is opened. The real authentication cookie is never
+sent to the server machine (and no cookies are sent in the plain).
+.Pp
+If the user is using an authentication agent, the connection to the agent
+is automatically forwarded to the remote side unless disabled on
+command line or in a configuration file.
+.Pp
+Forwarding of arbitrary TCP/IP connections over the secure channel can
+be specified either on command line or in a configuration file. One
+possible application of TCP/IP forwarding is a secure connection to an
+electronic purse; another is going trough firewalls.
+.Pp
+.Nm
+automatically maintains and checks a database containing RSA-based
+identifications for all hosts it has ever been used with. The
+database is stored in
+.Pa \&.ssh/known_hosts
+in the user's home directory. Additionally, the file
+.Pa /etc/ssh_known_hosts
+is automatically checked for known hosts. Any new hosts are
+automatically added to the user's file. If a host's identification
+ever changes,
+.Nm
+warns about this and disables password authentication to prevent a
+trojan horse from getting the user's password. Another purpose of
+this mechanism is to prevent man-in-the-middle attacks which could
+otherwise be used to circumvent the encryption. The
+.Cm StrictHostKeyChecking
+option (see below) can be used to prevent logins to machines whose
+host key is not known or has changed.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl a
+Disables forwarding of the authentication agent connection. This may
+also be specified on a per-host basis in the configuration file.
+.It Fl c Ar blowfish|3des
+Selects the cipher to use for encrypting the session.
+.Ar 3des
+is used by default. It is believed to be secure.
+.Ar 3des
+(triple-des) is an encrypt-decrypt-encrypt triple with three different keys.
+It is presumably more secure than the
+.Ar des
+cipher which is no longer supported in ssh.
+.Ar blowfish
+is a fast block cipher, it appears very secure and is much faster than
+.Ar 3des .
+.It Fl e Ar ch|^ch|none
+Sets the escape character for sessions with a pty (default:
+.Ql ~ ) .
+The escape character is only recognized at the beginning of a line. The
+escape character followed by a dot
+.Pq Ql \&.
+closes the connection, followed
+by control-Z suspends the connection, and followed by itself sends the
+escape character once. Setting the character to
+.Dq none
+disables any escapes and makes the session fully transparent.
+.It Fl f
+Requests
+.Nm
+to go to background after authentication. This is useful
+if
+.Nm
+is going to ask for passwords or passphrases, but the user
+wants it in the background. This implies
+.Fl n .
+The recommended way to start X11 programs at a remote site is with
+something like
+.Ic ssh -f host xterm .
+.It Fl i Ar identity_file
+Selects the file from which the identity (private key) for
+RSA authentication is read. Default is
+.Pa \&.ssh/identity
+in the user's home directory. Identity files may also be specified on
+a per-host basis in the configuration file. It is possible to have
+multiple
+.Fl i
+options (and multiple identities specified in
+configuration files).
+.It Fl g
+Allows remote hosts to connect to local forwarded ports.
+.It Fl k
+Disables forwarding of Kerberos tickets and AFS tokens. This may
+also be specified on a per-host basis in the configuration file.
+.It Fl l Ar login_name
+Specifies the user to log in as on the remote machine. This may also
+be specified on a per-host basis in the configuration file.
+.It Fl n
+Redirects stdin from
+.Pa /dev/null
+(actually, prevents reading from stdin).
+This must be used when
+.Nm
+is run in the background. A common trick is to use this to run X11
+programs in a remote machine. For example,
+.Ic ssh -n shadows.cs.hut.fi emacs &
+will start an emacs on shadows.cs.hut.fi, and the X11
+connection will be automatically forwarded over an encrypted channel.
+The
+.Nm
+program will be put in the background.
+(This does not work if
+.Nm
+needs to ask for a password or passphrase; see also the
+.Fl f
+option.)
+.It Fl o Ar option
+Can be used to give options in the format used in the config file.
+This is useful for specifying options for which there is no separate
+command-line flag. The option has the same format as a line in the
+configuration file.
+.It Fl p Ar port
+Port to connect to on the remote host. This can be specified on a
+per-host basis in the configuration file.
+.It Fl P
+Use a non-privileged port for outgoing connections.
+This can be used if your firewall does
+not permit connections from privileged ports.
+Note that this option turns of
+.Cm RhostsAuthentication
+and
+.Cm RhostsRSAAuthentication .
+.It Fl q
+Quiet mode. Causes all warning and diagnostic messages to be
+suppressed. Only fatal errors are displayed.
+.It Fl t
+Force pseudo-tty allocation. This can be used to execute arbitary
+screen-based programs on a remote machine, which can be very useful
+e.g. when implementing menu services.
+.It Fl v
+Verbose mode. Causes
+.Nm
+to print debugging messages about its progress. This is helpful in
+debugging connection, authentication, and configuration problems.
+The verbose mode is also used to display
+.Xr skey 1
+challenges, if the user entered "s/key" as password.
+.It Fl x
+Disables X11 forwarding. This can also be specified on a per-host
+basis in a configuration file.
+.It Fl X
+Enables X11 forwarding.
+.It Fl C
+Requests compression of all data (including stdin, stdout, stderr, and
+data for forwarded X11 and TCP/IP connections). The compression
+algorithm is the same used by gzip, and the
+.Dq level
+can be controlled by the
+.Cm CompressionLevel
+option (see below). Compression is desirable on modem lines and other
+slow connections, but will only slow down things on fast networks.
+The default value can be set on a host-by-host basis in the
+configuration files; see the
+.Cm Compress
+option below.
+.It Fl L Ar port:host:hostport
+Specifies that the given port on the local (client) host is to be
+forwarded to the given host and port on the remote side. This works
+by allocating a socket to listen to
+.Ar port
+on the local side, and whenever a connection is made to this port, the
+connection is forwarded over the secure channel, and a connection is
+made to
+.Ar host:hostport
+from the remote machine. Port forwardings can also be specified in the
+configuration file. Only root can forward privileged ports.
+.It Fl R Ar port:host:hostport
+Specifies that the given port on the remote (server) host is to be
+forwarded to the given host and port on the local side. This works
+by allocating a socket to listen to
+.Ar port
+on the remote side, and whenever a connection is made to this port, the
+connection is forwarded over the secure channel, and a connection is
+made to
+.Ar host:hostport
+from the local machine. Port forwardings can also be specified in the
+configuration file. Privileged ports can be forwarded only when
+logging in as root on the remote machine.
+.El
+.Sh CONFIGURATION FILES
+.Nm
+obtains configuration data from the following sources (in this order):
+command line options, user's configuration file
+.Pq Pa $HOME/.ssh/config ,
+and system-wide configuration file
+.Pq Pa /etc/ssh_config .
+For each parameter, the first obtained value
+will be used. The configuration files contain sections bracketed by
+"Host" specifications, and that section is only applied for hosts that
+match one of the patterns given in the specification. The matched
+host name is the one given on the command line.
+.Pp
+Since the first obtained value for each parameter is used, more
+host-specific declarations should be given near the beginning of the
+file, and general defaults at the end.
+.Pp
+The configuration file has the following format:
+.Pp
+Empty lines and lines starting with
+.Ql #
+are comments.
+.Pp
+Otherwise a line is of the format
+.Dq keyword arguments .
+The possible
+keywords and their meanings are as follows (note that the
+configuration files are case-sensitive):
+.Bl -tag -width Ds
+.It Cm Host
+Restricts the following declarations (up to the next
+.Cm Host
+keyword) to be only for those hosts that match one of the patterns
+given after the keyword.
+.Ql \&*
+and
+.Ql ?
+can be used as wildcards in the
+patterns. A single
+.Ql \&*
+as a pattern can be used to provide global
+defaults for all hosts. The host is the
+.Ar hostname
+argument given on the command line (i.e., the name is not converted to
+a canonicalized host name before matching).
+.It Cm AFSTokenPassing
+Specifies whether to pass AFS tokens to remote host. The argument to
+this keyword must be
+.Dq yes
+or
+.Dq no .
+.It Cm BatchMode
+If set to
+.Dq yes ,
+passphrase/password querying will be disabled. This
+option is useful in scripts and other batch jobs where you have no
+user to supply the password. The argument must be
+.Dq yes
+or
+.Dq no .
+.It Cm Cipher
+Specifies the cipher to use for encrypting the session. Currently,
+.Dq blowfish ,
+and
+.Dq 3des
+are supported. The default is
+.Dq 3des .
+.It Cm Compression
+Specifies whether to use compression. The argument must be
+.Dq yes
+or
+.Dq no .
+.It Cm CompressionLevel
+Specifies the compression level to use if compression is enable. The
+argument must be an integer from 1 (fast) to 9 (slow, best). The
+default level is 6, which is good for most applications. The meaning
+of the values is the same as in GNU GZIP.
+.It Cm ConnectionAttempts
+Specifies the number of tries (one per second) to make before falling
+back to rsh or exiting. The argument must be an integer. This may be
+useful in scripts if the connection sometimes fails.
+.It Cm EscapeChar
+Sets the escape character (default:
+.Ql ~ ) .
+The escape character can also
+be set on the command line. The argument should be a single
+character,
+.Ql ^
+followed by a letter, or
+.Dq none
+to disable the escape
+character entirely (making the connection transparent for binary
+data).
+.It Cm FallBackToRsh
+Specifies that if connecting via
+.Nm
+fails due to a connection refused error (there is no
+.Xr sshd 8
+listening on the remote host),
+.Xr rsh 1
+should automatically be used instead (after a suitable warning about
+the session being unencrypted). The argument must be
+.Dq yes
+or
+.Dq no .
+.It Cm ForwardAgent
+Specifies whether the connection to the authentication agent (if any)
+will be forwarded to the remote machine. The argument must be
+.Dq yes
+or
+.Dq no .
+.It Cm ForwardX11
+Specifies whether X11 connections will be automatically redirected
+over the secure channel and
+.Ev DISPLAY
+set. The argument must be
+.Dq yes
+or
+.Dq no .
+.It Cm GatewayPorts
+Specifies whether remote hosts are allowed to connect to local
+forwarded ports.
+The argument must be
+.Dq yes
+or
+.Dq no .
+The default is
+.Dq no .
+.It Cm GlobalKnownHostsFile
+Specifies a file to use instead of
+.Pa /etc/ssh_known_hosts .
+.It Cm HostName
+Specifies the real host name to log into. This can be used to specify
+nicnames or abbreviations for hosts. Default is the name given on the
+command line. Numeric IP addresses are also permitted (both on the
+command line and in
+.Cm HostName
+specifications).
+.It Cm IdentityFile
+Specifies the file from which the user's RSA authentication identity
+is read (default
+.Pa .ssh/identity
+in the user's home directory).
+Additionally, any identities represented by the authentication agent
+will be used for authentication. The file name may use the tilde
+syntax to refer to a user's home directory. It is possible to have
+multiple identity files specified in configuration files; all these
+identities will be tried in sequence.
+.It Cm KeepAlive
+Specifies whether the system should send keepalive messages to the
+other side. If they are sent, death of the connection or crash of one
+of the machines will be properly noticed. However, this means that
+connections will die if the route is down temporarily, and some people
+find it annoying.
+.Pp
+The default is
+.Dq yes
+(to send keepalives), and the client will notice
+if the network goes down or the remote host dies. This is important
+in scripts, and many users want it too.
+.Pp
+To disable keepalives, the value should be set to
+.Dq no
+in both the server and the client configuration files.
+.It Cm KerberosAuthentication
+Specifies whether Kerberos authentication will be used. The argument to
+this keyword must be
+.Dq yes
+or
+.Dq no .
+.It Cm KerberosTgtPassing
+Specifies whether a Kerberos TGT will be forwarded to the server. This
+will only work if the Kerberos server is actually an AFS kaserver. The
+argument to this keyword must be
+.Dq yes
+or
+.Dq no .
+.It Cm LocalForward
+Specifies that a TCP/IP port on the local machine be forwarded over
+the secure channel to given host:port from the remote machine. The
+first argument must be a port number, and the second must be
+host:port. Multiple forwardings may be specified, and additional
+forwardings can be given on the command line. Only the root can
+forward privileged ports.
+.It Cm PasswordAuthentication
+Specifies whether to use password authentication. The argument to
+this keyword must be
+.Dq yes
+or
+.Dq no .
+.It Cm NumberOfPasswordPrompts
+Specifies the number of password prompts before giving up. The
+argument to this keyword must be an integer. Default is 3.
+.It Cm Port
+Specifies the port number to connect on the remote host. Default is
+22.
+.It Cm ProxyCommand
+Specifies the command to use to connect to the server. The command
+string extends to the end of the line, and is executed with /bin/sh.
+In the command string, %h will be substituted by the host name to
+connect and %p by the port. The command can be basically anything,
+and should read from its stdin and write to its stdout. It should
+eventually connect an
+.Xr sshd 8
+server running on some machine, or execute
+.Ic sshd -i
+somewhere. Host key management will be done using the
+HostName of the host being connected (defaulting to the name typed by
+the user).
+.Pp
+.It Cm RemoteForward
+Specifies that a TCP/IP port on the remote machine be forwarded over
+the secure channel to given host:port from the local machine. The
+first argument must be a port number, and the second must be
+host:port. Multiple forwardings may be specified, and additional
+forwardings can be given on the command line. Only the root can
+forward privileged ports.
+.It Cm RhostsAuthentication
+Specifies whether to try rhosts based authentication. Note that this
+declaration only affects the client side and has no effect whatsoever
+on security. Disabling rhosts authentication may reduce
+authentication time on slow connections when rhosts authentication is
+not used. Most servers do not permit RhostsAuthentication because it
+is not secure (see RhostsRSAAuthentication). The argument to this
+keyword must be
+.Dq yes
+or
+.Dq no .
+.It Cm RhostsRSAAuthentication
+Specifies whether to try rhosts based authentication with RSA host
+authentication. This is the primary authentication method for most
+sites. The argument must be
+.Dq yes
+or
+.Dq no .
+.It Cm RSAAuthentication
+Specifies whether to try RSA authentication. The argument to this
+keyword must be
+.Dq yes
+or
+.Dq no .
+RSA authentication will only be
+attempted if the identity file exists, or an authentication agent is
+running.
+.It Cm CheckHostIP
+If this flag is set to
+.Dq yes ,
+ssh will additionally check the host ip address in the
+.Pa known_hosts
+file. This allows ssh to detect if a host key changed due to DNS spoofing.
+If the option is set to
+.Dq no ,
+the check will not be executed.
+.It Cm StrictHostKeyChecking
+If this flag is set to
+.Dq yes ,
+.Nm
+ssh will never automatically add host keys to the
+.Pa $HOME/.ssh/known_hosts
+file, and refuses to connect hosts whose host key has changed. This
+provides maximum protection against trojan horse attacks. However, it
+can be somewhat annoying if you don't have good
+.Pa /etc/ssh_known_hosts
+files installed and frequently
+connect new hosts. Basically this option forces the user to manually
+add any new hosts. Normally this option is disabled, and new hosts
+will automatically be added to the known host files. The host keys of
+known hosts will be verified automatically in either case. The
+argument must be
+.Dq yes
+or
+.Dq no .
+.It Cm User
+Specifies the user to log in as. This can be useful if you have a
+different user name in different machines. This saves the trouble of
+having to remember to give the user name on the command line.
+.It Cm UserKnownHostsFile
+Specifies a file to use instead of
+.Pa $HOME/.ssh/known_hosts .
+.It Cm UsePrivilegedPort
+Specifies whether to use a privileged port for outgoing connections.
+The argument must be
+.Dq yes
+or
+.Dq no .
+The default is
+.Dq yes .
+Note that setting this option to
+.Dq no
+turns of
+.Cm RhostsAuthentication
+and
+.Cm RhostsRSAAuthentication .
+.It Cm UseRsh
+Specifies that rlogin/rsh should be used for this host. It is
+possible that the host does not at all support the
+.Nm
+protocol. This causes
+.Nm
+to immediately exec
+.Xr rsh 1 .
+All other options (except
+.Cm HostName )
+are ignored if this has been specified. The argument must be
+.Dq yes
+or
+.Dq no .
+.Sh ENVIRONMENT
+.Nm
+will normally set the following environment variables:
+.Bl -tag -width Ds
+.It Ev DISPLAY
+The
+.Ev DISPLAY
+variable indicates the location of the X11 server. It is
+automatically set by
+.Nm
+to point to a value of the form
+.Dq hostname:n
+where hostname indicates
+the host where the shell runs, and n is an integer >= 1. Ssh uses
+this special value to forward X11 connections over the secure
+channel. The user should normally not set DISPLAY explicitly, as that
+will render the X11 connection insecure (and will require the user to
+manually copy any required authorization cookies).
+.It Ev HOME
+Set to the path of the user's home directory.
+.It Ev LOGNAME
+Synonym for
+.Ev USER ;
+set for compatibility with systems that use this variable.
+.It Ev MAIL
+Set to point the user's mailbox.
+.It Ev PATH
+Set to the default
+.Ev PATH ,
+as specified when compiling
+.Nm ssh .
+.It Ev SSH_AUTH_SOCK
+indicates the path of a unix-domain socket used to communicate with the
+agent.
+.It Ev SSH_CLIENT
+Identifies the client end of the connection. The variable contains
+three space-separated values: client ip-address, client port number,
+and server port number.
+.It Ev SSH_TTY
+This is set to the name of the tty (path to the device) associated
+with the current shell or command. If the current session has no tty,
+this variable is not set.
+.It Ev TZ
+The timezone variable is set to indicate the present timezone if it
+was set when the daemon was started (e.i., the daemon passes the value
+on to new connections).
+.It Ev USER
+Set to the name of the user logging in.
+.El
+.Pp
+Additionally,
+.Nm
+reads
+.Pa $HOME/.ssh/environment ,
+and adds lines of the format
+.Dq VARNAME=value
+to the environment.
+.Sh FILES
+.Bl -tag -width $HOME/.ssh/known_hosts
+.It Pa $HOME/.ssh/known_hosts
+Records host keys for all hosts the user has logged into (that are not
+in
+.Pa /etc/ssh_known_hosts ) .
+See
+.Xr sshd 8 .
+.It Pa $HOME/.ssh/random_seed
+Used for seeding the random number generator. This file contains
+sensitive data and should read/write for the user and not accessible
+for others. This file is created the first time the program is run
+and updated automatically. The user should never need to read or
+modify this file.
+.It Pa $HOME/.ssh/identity
+Contains the RSA authentication identity of the user. This file
+contains sensitive data and should be readable by the user but not
+accessible by others (read/write/execute).
+Note that
+.Nm
+ignores this file if it is accessible by others.
+It is possible to specify a passphrase when
+generating the key; the passphrase will be used to encrypt the
+sensitive part of this file using 3DES.
+.It Pa $HOME/.ssh/identity.pub
+Contains the public key for authentication (public part of the
+identity file in human-readable form). The contents of this file
+should be added to
+.Pa $HOME/.ssh/authorized_keys
+on all machines
+where you wish to log in using RSA authentication. This file is not
+sensitive and can (but need not) be readable by anyone. This file is
+never used automatically and is not necessary; it is only provided for
+the convenience of the user.
+.It Pa $HOME/.ssh/config
+This is the per-user configuration file. The format of this file is
+described above. This file is used by the
+.Nm
+client. This file does not usually contain any sensitive information,
+but the recommended permissions are read/write for the user, and not
+accessible by others.
+.It Pa $HOME/.ssh/authorized_keys
+Lists the RSA keys that can be used for logging in as this user. The
+format of this file is described in the
+.Xr sshd 8
+manual page. In the simplest form the format is the same as the .pub
+identity files (that is, each line contains the number of bits in
+modulus, public exponent, modulus, and comment fields, separated by
+spaces). This file is not highly sensitive, but the recommended
+permissions are read/write for the user, and not accessible by others.
+.It Pa /etc/ssh_known_hosts
+Systemwide list of known host keys. This file should be prepared by the
+system administrator to contain the public host keys of all machines in the
+organization. This file should be world-readable. This file contains
+public keys, one per line, in the following format (fields separated
+by spaces): system name, number of bits in modulus, public exponent,
+modulus, and optional comment field. When different names are used
+for the same machine, all such names should be listed, separated by
+commas. The format is described on the
+.Xr sshd 8
+manual page.
+.Pp
+The canonical system name (as returned by name servers) is used by
+.Xr sshd 8
+to verify the client host when logging in; other names are needed because
+.Nm
+does not convert the user-supplied name to a canonical name before
+checking the key, because someone with access to the name servers
+would then be able to fool host authentication.
+.It Pa /etc/ssh_config
+Systemwide configuration file. This file provides defaults for those
+values that are not specified in the user's configuration file, and
+for those users who do not have a configuration file. This file must
+be world-readable.
+.It Pa $HOME/.rhosts
+This file is used in
+.Pa \&.rhosts
+authentication to list the
+host/user pairs that are permitted to log in. (Note that this file is
+also used by rlogin and rsh, which makes using this file insecure.)
+Each line of the file contains a host name (in the canonical form
+returned by name servers), and then a user name on that host,
+separated by a space. One some machines this file may need to be
+world-readable if the user's home directory is on a NFS partition,
+because
+.Xr sshd 8
+reads it as root. Additionally, this file must be owned by the user,
+and must not have write permissions for anyone else. The recommended
+permission for most machines is read/write for the user, and not
+accessible by others.
+.Pp
+Note that by default
+.Xr sshd 8
+will be installed so that it requires successful RSA host
+authentication before permitting \s+2.\s0rhosts authentication. If your
+server machine does not have the client's host key in
+.Pa /etc/ssh_known_hosts ,
+you can store it in
+.Pa $HOME/.ssh/known_hosts .
+The easiest way to do this is to
+connect back to the client from the server machine using ssh; this
+will automatically add the host key inxi
+.Pa $HOME/.ssh/known_hosts .
+.It Pa $HOME/.shosts
+This file is used exactly the same way as
+.Pa \&.rhosts .
+The purpose for
+having this file is to be able to use rhosts authentication with
+.Nm
+without permitting login with
+.Xr rlogin 1
+or
+.Xr rsh 1 .
+.It Pa /etc/hosts.equiv
+This file is used during
+.Pa \&.rhosts authentication. It contains
+canonical hosts names, one per line (the full format is described on
+the
+.Xr sshd 8
+manual page). If the client host is found in this file, login is
+automatically permitted provided client and server user names are the
+same. Additionally, successful RSA host authentication is normally
+required. This file should only be writable by root.
+.It Pa /etc/shosts.equiv
+This file is processed exactly as
+.Pa /etc/hosts.equiv .
+This file may be useful to permit logins using
+.Nm
+but not using rsh/rlogin.
+.It Pa /etc/sshrc
+Commands in this file are executed by
+.Nm
+when the user logs in just before the user's shell (or command) is started.
+See the
+.Xr sshd 8
+manual page for more information.
+.It Pa $HOME/.ssh/rc
+Commands in this file are executed by
+.Nm
+when the user logs in just before the user's shell (or command) is
+started.
+See the
+.Xr sshd 8
+manual page for more information.
+.It Pa libcrypto.so.X.1
+A version of this library which includes support for the RSA algorithm
+is required for proper operation.
+.Sh AUTHOR
+Tatu Ylonen <ylo@cs.hut.fi>
+.Pp
+Issues can be found from the SSH WWW home page:
+.Pp
+.Dl http://www.cs.hut.fi/ssh
+.Pp
+OpenSSH
+is a derivative of the original (free) ssh 1.2.12 release, but with bugs
+removed and newer features re-added. Rapidly after the 1.2.12 release,
+newer versions bore successively more restrictive licenses. This version
+of OpenSSH
+.Bl -bullet
+.It
+has all components of a restrictive nature (ie. patents, see
+.Xr ssl 8 )
+directly removed from the source code; any licensed or patented components
+are chosen from
+external libraries.
+.It
+has been updated to support ssh protocol 1.5.
+.It
+contains added support for
+.Xr kerberos 8
+authentication and ticket passing.
+.It
+supports one-time password authentication with
+.Xr skey 1 .
+.El
+.Pp
+The libraries described in
+.Xr ssl 8
+are required for proper operation.
+.Sh SEE ALSO
+.Xr rlogin 1 ,
+.Xr rsh 1 ,
+.Xr scp 1 ,
+.Xr ssh-add 1 ,
+.Xr ssh-agent 1 ,
+.Xr ssh-keygen 1 ,
+.Xr telnet 1 ,
+.Xr sshd 8 ,
+.Xr ssl 8
diff --git a/ssh.c b/ssh.c
new file mode 100644
index 00000000..c5a92b5c
--- /dev/null
+++ b/ssh.c
@@ -0,0 +1,809 @@
+/*
+
+ssh.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ All rights reserved
+
+Created: Sat Mar 18 16:36:11 1995 ylo
+
+Ssh client program. This program can be used to log into a remote machine.
+The software supports strong authentication, encryption, and forwarding
+of X11, TCP/IP, and authentication connections.
+
+Modified to work with SSL by Niels Provos <provos@citi.umich.edu> in Canada.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: ssh.c,v 1.1 1999/10/27 03:42:45 damien Exp $");
+
+#include "xmalloc.h"
+#include "ssh.h"
+#include "packet.h"
+#include "buffer.h"
+#include "authfd.h"
+#include "readconf.h"
+#include "uidswap.h"
+
+/* Flag indicating whether debug mode is on. This can be set on the
+ command line. */
+int debug_flag = 0;
+
+/* Flag indicating whether quiet mode is on. */
+int quiet_flag = 0;
+
+/* Flag indicating whether to allocate a pseudo tty. This can be set on the
+ command line, and is automatically set if no command is given on the command
+ line. */
+int tty_flag = 0;
+
+/* Flag indicating that nothing should be read from stdin. This can be set
+ on the command line. */
+int stdin_null_flag = 0;
+
+/* Flag indicating that ssh should fork after authentication. This is useful
+ so that the pasphrase can be entered manually, and then ssh goes to the
+ background. */
+int fork_after_authentication_flag = 0;
+
+/* General data structure for command line options and options configurable
+ in configuration files. See readconf.h. */
+Options options;
+
+/* Name of the host we are connecting to. This is the name given on the
+ command line, or the HostName specified for the user-supplied name
+ in a configuration file. */
+char *host;
+
+/* socket address the host resolves to */
+struct sockaddr_in hostaddr;
+
+/* Flag to indicate that we have received a window change signal which has
+ not yet been processed. This will cause a message indicating the new
+ window size to be sent to the server a little later. This is volatile
+ because this is updated in a signal handler. */
+volatile int received_window_change_signal = 0;
+
+/* Value of argv[0] (set in the main program). */
+char *av0;
+
+/* Flag indicating whether we have a valid host private key loaded. */
+int host_private_key_loaded = 0;
+
+/* Host private key. */
+RSA *host_private_key = NULL;
+
+/* Original real UID. */
+uid_t original_real_uid;
+
+/* Prints a help message to the user. This function never returns. */
+
+void
+usage()
+{
+ fprintf(stderr, "Usage: %s [options] host [command]\n", av0);
+ fprintf(stderr, "Options:\n");
+ fprintf(stderr, " -l user Log in using this user name.\n");
+ fprintf(stderr, " -n Redirect input from /dev/null.\n");
+ fprintf(stderr, " -a Disable authentication agent forwarding.\n");
+#ifdef AFS
+ fprintf(stderr, " -k Disable Kerberos ticket and AFS token forwarding.\n");
+#endif /* AFS */
+ fprintf(stderr, " -x Disable X11 connection forwarding.\n");
+ fprintf(stderr, " -i file Identity for RSA authentication (default: ~/.ssh/identity).\n");
+ fprintf(stderr, " -t Tty; allocate a tty even if command is given.\n");
+ fprintf(stderr, " -v Verbose; display verbose debugging messages.\n");
+ fprintf(stderr, " -V Display version number only.\n");
+ fprintf(stderr, " -P Don't allocate a privileged port.\n");
+ fprintf(stderr, " -q Quiet; don't display any warning messages.\n");
+ fprintf(stderr, " -f Fork into background after authentication.\n");
+ fprintf(stderr, " -e char Set escape character; ``none'' = disable (default: ~).\n");
+
+ fprintf(stderr, " -c cipher Select encryption algorithm: "
+ "``3des'', "
+ "``blowfish''\n");
+ fprintf(stderr, " -p port Connect to this port. Server must be on the same port.\n");
+ fprintf(stderr, " -L listen-port:host:port Forward local port to remote address\n");
+ fprintf(stderr, " -R listen-port:host:port Forward remote port to local address\n");
+ fprintf(stderr, " These cause %s to listen for connections on a port, and\n", av0);
+ fprintf(stderr, " forward them to the other side by connecting to host:port.\n");
+ fprintf(stderr, " -C Enable compression.\n");
+ fprintf(stderr, " -g Allow remote hosts to connect to forwarded ports.\n");
+ fprintf(stderr, " -o 'option' Process the option as if it was read from a configuration file.\n");
+ exit(1);
+}
+
+/* Connects to the given host using rsh (or prints an error message and exits
+ if rsh is not available). This function never returns. */
+
+void
+rsh_connect(char *host, char *user, Buffer *command)
+{
+ char *args[10];
+ int i;
+
+ log("Using rsh. WARNING: Connection will not be encrypted.");
+ /* Build argument list for rsh. */
+ i = 0;
+ args[i++] = _PATH_RSH;
+ args[i++] = host; /* may have to come after user on some systems */
+ if (user)
+ {
+ args[i++] = "-l";
+ args[i++] = user;
+ }
+ if (buffer_len(command) > 0)
+ {
+ buffer_append(command, "\0", 1);
+ args[i++] = buffer_ptr(command);
+ }
+ args[i++] = NULL;
+ if (debug_flag)
+ {
+ for (i = 0; args[i]; i++)
+ {
+ if (i != 0)
+ fprintf(stderr, " ");
+ fprintf(stderr, "%s", args[i]);
+ }
+ fprintf(stderr, "\n");
+ }
+ execv(_PATH_RSH, args);
+ perror(_PATH_RSH);
+ exit(1);
+}
+
+/* Main program for the ssh client. */
+
+uid_t original_real_uid;
+
+int
+main(int ac, char **av)
+{
+ int i, opt, optind, type, exit_status, ok, fwd_port, fwd_host_port, authfd;
+ char *optarg, *cp, buf[256];
+ Buffer command;
+ struct winsize ws;
+ struct stat st;
+ struct passwd *pw, pwcopy;
+ int interactive = 0, dummy;
+ uid_t original_effective_uid;
+ int plen;
+
+ /* Save the original real uid. It will be needed later (uid-swapping may
+ clobber the real uid). */
+ original_real_uid = getuid();
+ original_effective_uid = geteuid();
+
+ /* If we are installed setuid root be careful to not drop core. */
+ if (original_real_uid != original_effective_uid)
+ {
+ struct rlimit rlim;
+ rlim.rlim_cur = rlim.rlim_max = 0;
+ if (setrlimit(RLIMIT_CORE, &rlim) < 0)
+ fatal("setrlimit failed: %.100s", strerror(errno));
+ }
+
+ /* Use uid-swapping to give up root privileges for the duration of option
+ processing. We will re-instantiate the rights when we are ready to
+ create the privileged port, and will permanently drop them when the
+ port has been created (actually, when the connection has been made, as
+ we may need to create the port several times). */
+ temporarily_use_uid(original_real_uid);
+
+ /* Set our umask to something reasonable, as some files are created with
+ the default umask. This will make them world-readable but writable
+ only by the owner, which is ok for all files for which we don't set
+ the modes explicitly. */
+ umask(022);
+
+ /* Save our own name. */
+ av0 = av[0];
+
+ /* Initialize option structure to indicate that no values have been set. */
+ initialize_options(&options);
+
+ /* Parse command-line arguments. */
+ host = NULL;
+
+ /* If program name is not one of the standard names, use it as host name. */
+ if (strchr(av0, '/'))
+ cp = strrchr(av0, '/') + 1;
+ else
+ cp = av0;
+ if (strcmp(cp, "rsh") != 0 && strcmp(cp, "ssh") != 0 &&
+ strcmp(cp, "rlogin") != 0 && strcmp(cp, "slogin") != 0)
+ host = cp;
+
+ for (optind = 1; optind < ac; optind++)
+ {
+ if (av[optind][0] != '-')
+ {
+ if (host)
+ break;
+ if ((cp = strchr(av[optind], '@'))) {
+ options.user = av[optind];
+ *cp = '\0';
+ host = ++cp;
+ }
+ else
+ host = av[optind];
+ continue;
+ }
+ opt = av[optind][1];
+ if (!opt)
+ usage();
+ if (strchr("eilcpLRo", opt)) /* options with arguments */
+ {
+ optarg = av[optind] + 2;
+ if (strcmp(optarg, "") == 0)
+ {
+ if (optind >= ac - 1)
+ usage();
+ optarg = av[++optind];
+ }
+ }
+ else
+ {
+ if (av[optind][2])
+ usage();
+ optarg = NULL;
+ }
+ switch (opt)
+ {
+ case 'n':
+ stdin_null_flag = 1;
+ break;
+
+ case 'f':
+ fork_after_authentication_flag = 1;
+ stdin_null_flag = 1;
+ break;
+
+ case 'x':
+ options.forward_x11 = 0;
+ break;
+
+ case 'X':
+ options.forward_x11 = 1;
+ break;
+
+ case 'g':
+ options.gateway_ports = 1;
+ break;
+
+ case 'P':
+ options.use_privileged_port = 0;
+ break;
+
+ case 'a':
+ options.forward_agent = 0;
+ break;
+#ifdef AFS
+ case 'k':
+ options.kerberos_tgt_passing = 0;
+ options.afs_token_passing = 0;
+ break;
+#endif
+ case 'i':
+ if (stat(optarg, &st) < 0)
+ {
+ fprintf(stderr, "Warning: Identity file %s does not exist.\n",
+ optarg);
+ break;
+ }
+ if (options.num_identity_files >= SSH_MAX_IDENTITY_FILES)
+ fatal("Too many identity files specified (max %d)",
+ SSH_MAX_IDENTITY_FILES);
+ options.identity_files[options.num_identity_files++] =
+ xstrdup(optarg);
+ break;
+
+ case 't':
+ tty_flag = 1;
+ break;
+
+ case 'v':
+ case 'V':
+ debug_flag = 1;
+ fprintf(stderr, "SSH Version %s, protocol version %d.%d.\n",
+ SSH_VERSION, PROTOCOL_MAJOR, PROTOCOL_MINOR);
+ fprintf(stderr, "Compiled with SSL.\n");
+ if (opt == 'V')
+ exit(0);
+ break;
+
+ case 'q':
+ quiet_flag = 1;
+ break;
+
+ case 'e':
+ if (optarg[0] == '^' && optarg[2] == 0 &&
+ (unsigned char)optarg[1] >= 64 && (unsigned char)optarg[1] < 128)
+ options.escape_char = (unsigned char)optarg[1] & 31;
+ else
+ if (strlen(optarg) == 1)
+ options.escape_char = (unsigned char)optarg[0];
+ else
+ if (strcmp(optarg, "none") == 0)
+ options.escape_char = -2;
+ else
+ {
+ fprintf(stderr, "Bad escape character '%s'.\n", optarg);
+ exit(1);
+ }
+ break;
+
+ case 'c':
+ options.cipher = cipher_number(optarg);
+ if (options.cipher == -1)
+ {
+ fprintf(stderr, "Unknown cipher type '%s'\n", optarg);
+ exit(1);
+ }
+ break;
+
+ case 'p':
+ options.port = atoi(optarg);
+ if (options.port < 1 || options.port > 65535)
+ {
+ fprintf(stderr, "Bad port %s.\n", optarg);
+ exit(1);
+ }
+ break;
+
+ case 'l':
+ options.user = optarg;
+ break;
+
+ case 'R':
+ if (sscanf(optarg, "%d:%255[^:]:%d", &fwd_port, buf,
+ &fwd_host_port) != 3)
+ {
+ fprintf(stderr, "Bad forwarding specification '%s'.\n", optarg);
+ usage();
+ /*NOTREACHED*/
+ }
+ add_remote_forward(&options, fwd_port, buf, fwd_host_port);
+ break;
+
+ case 'L':
+ if (sscanf(optarg, "%d:%255[^:]:%d", &fwd_port, buf,
+ &fwd_host_port) != 3)
+ {
+ fprintf(stderr, "Bad forwarding specification '%s'.\n", optarg);
+ usage();
+ /*NOTREACHED*/
+ }
+ add_local_forward(&options, fwd_port, buf, fwd_host_port);
+ break;
+
+ case 'C':
+ options.compression = 1;
+ break;
+
+ case 'o':
+ dummy = 1;
+ process_config_line(&options, host ? host : "", optarg,
+ "command-line", 0, &dummy);
+ break;
+
+ default:
+ usage();
+ }
+ }
+
+ /* Check that we got a host name. */
+ if (!host)
+ usage();
+
+ /* check if RSA support exists */
+ if (rsa_alive() == 0) {
+ extern char *__progname;
+
+ fprintf(stderr,
+ "%s: no RSA support in libssl and libcrypto. See ssl(8).\n",
+ __progname);
+ exit(1);
+ }
+
+ /* Initialize the command to execute on remote host. */
+ buffer_init(&command);
+
+ /* Save the command to execute on the remote host in a buffer. There is
+ no limit on the length of the command, except by the maximum packet
+ size. Also sets the tty flag if there is no command. */
+ if (optind == ac)
+ {
+ /* No command specified - execute shell on a tty. */
+ tty_flag = 1;
+ }
+ else
+ {
+ /* A command has been specified. Store it into the buffer. */
+ for (i = optind; i < ac; i++)
+ {
+ if (i > optind)
+ buffer_append(&command, " ", 1);
+ buffer_append(&command, av[i], strlen(av[i]));
+ }
+ }
+
+ /* Cannot fork to background if no command. */
+ if (fork_after_authentication_flag && buffer_len(&command) == 0)
+ fatal("Cannot fork into background without a command to execute.");
+
+ /* Allocate a tty by default if no command specified. */
+ if (buffer_len(&command) == 0)
+ tty_flag = 1;
+
+ /* Do not allocate a tty if stdin is not a tty. */
+ if (!isatty(fileno(stdin)))
+ {
+ if (tty_flag)
+ fprintf(stderr, "Pseudo-terminal will not be allocated because stdin is not a terminal.\n");
+ tty_flag = 0;
+ }
+
+ /* Get user data. */
+ pw = getpwuid(original_real_uid);
+ if (!pw)
+ {
+ fprintf(stderr, "You don't exist, go away!\n");
+ exit(1);
+ }
+
+ /* Take a copy of the returned structure. */
+ memset(&pwcopy, 0, sizeof(pwcopy));
+ pwcopy.pw_name = xstrdup(pw->pw_name);
+ pwcopy.pw_passwd = xstrdup(pw->pw_passwd);
+ pwcopy.pw_uid = pw->pw_uid;
+ pwcopy.pw_gid = pw->pw_gid;
+ pwcopy.pw_dir = xstrdup(pw->pw_dir);
+ pwcopy.pw_shell = xstrdup(pw->pw_shell);
+ pw = &pwcopy;
+
+ /* Initialize "log" output. Since we are the client all output actually
+ goes to the terminal. */
+ log_init(av[0], 1, debug_flag, quiet_flag, SYSLOG_FACILITY_USER);
+
+ /* Read per-user configuration file. */
+ snprintf(buf, sizeof buf, "%.100s/%.100s", pw->pw_dir, SSH_USER_CONFFILE);
+ read_config_file(buf, host, &options);
+
+ /* Read systemwide configuration file. */
+ read_config_file(HOST_CONFIG_FILE, host, &options);
+
+ /* Fill configuration defaults. */
+ fill_default_options(&options);
+ if (options.user == NULL)
+ options.user = xstrdup(pw->pw_name);
+
+ if (options.hostname != NULL)
+ host = options.hostname;
+
+ /* Find canonic host name. */
+ if (strchr(host, '.') == 0)
+ {
+ struct hostent *hp = gethostbyname(host);
+ if (hp != 0)
+ {
+ if (strchr(hp->h_name, '.') != 0)
+ host = xstrdup(hp->h_name);
+ else if (hp->h_aliases != 0
+ && hp->h_aliases[0] != 0
+ && strchr(hp->h_aliases[0], '.') != 0)
+ host = xstrdup(hp->h_aliases[0]);
+ }
+ }
+
+ /* Disable rhosts authentication if not running as root. */
+ if (original_effective_uid != 0)
+ {
+ options.rhosts_authentication = 0;
+ options.rhosts_rsa_authentication = 0;
+ }
+
+ /* If using rsh has been selected, exec it now (without trying anything
+ else). Note that we must release privileges first. */
+ if (options.use_rsh)
+ {
+ /* Restore our superuser privileges. This must be done before
+ permanently setting the uid. */
+ restore_uid();
+
+ /* Switch to the original uid permanently. */
+ permanently_set_uid(original_real_uid);
+
+ /* Execute rsh. */
+ rsh_connect(host, options.user, &command);
+ fatal("rsh_connect returned");
+ }
+
+ /* Restore our superuser privileges. */
+ restore_uid();
+
+ /* Open a connection to the remote host. This needs root privileges if
+ rhosts_{rsa_}authentication is true. */
+
+ if (!options.use_privileged_port)
+ {
+ options.rhosts_authentication = 0;
+ options.rhosts_rsa_authentication = 0;
+ }
+
+ ok = ssh_connect(host, &hostaddr, options.port, options.connection_attempts,
+ !options.rhosts_authentication &&
+ !options.rhosts_rsa_authentication,
+ original_real_uid, options.proxy_command);
+
+ /* If we successfully made the connection, load the host private key in
+ case we will need it later for combined rsa-rhosts authentication.
+ This must be done before releasing extra privileges, because the file
+ is only readable by root. */
+ if (ok)
+ {
+ host_private_key = RSA_new();
+ if (load_private_key(HOST_KEY_FILE, "", host_private_key, NULL))
+ host_private_key_loaded = 1;
+ }
+
+ /* Get rid of any extra privileges that we may have. We will no longer need
+ them. Also, extra privileges could make it very hard to read identity
+ files and other non-world-readable files from the user's home directory
+ if it happens to be on a NFS volume where root is mapped to nobody. */
+ permanently_set_uid(original_real_uid);
+
+ /* Now that we are back to our own permissions, create ~/.ssh directory
+ if it doesn\'t already exist. */
+ snprintf(buf, sizeof buf, "%.100s/%.100s", pw->pw_dir, SSH_USER_DIR);
+ if (stat(buf, &st) < 0)
+ if (mkdir(buf, 0755) < 0)
+ error("Could not create directory '%.200s'.", buf);
+
+ /* Check if the connection failed, and try "rsh" if appropriate. */
+ if (!ok)
+ {
+ if (options.port != 0)
+ log("Secure connection to %.100s on port %d refused%.100s.",
+ host, options.port,
+ options.fallback_to_rsh ? "; reverting to insecure method" : "");
+ else
+ log("Secure connection to %.100s refused%.100s.", host,
+ options.fallback_to_rsh ? "; reverting to insecure method" : "");
+
+ if (options.fallback_to_rsh)
+ {
+ rsh_connect(host, options.user, &command);
+ fatal("rsh_connect returned");
+ }
+ exit(1);
+ }
+
+ /* Expand ~ in options.identity_files. */
+ for (i = 0; i < options.num_identity_files; i++)
+ options.identity_files[i] =
+ tilde_expand_filename(options.identity_files[i], original_real_uid);
+
+ /* Expand ~ in known host file names. */
+ options.system_hostfile = tilde_expand_filename(options.system_hostfile,
+ original_real_uid);
+ options.user_hostfile = tilde_expand_filename(options.user_hostfile,
+ original_real_uid);
+
+ /* Log into the remote system. This never returns if the login fails. */
+ ssh_login(host_private_key_loaded, host_private_key,
+ host, &hostaddr, &options, original_real_uid);
+
+ /* We no longer need the host private key. Clear it now. */
+ if (host_private_key_loaded)
+ RSA_free(host_private_key); /* Destroys contents safely */
+
+ /* Close connection cleanly after attack. */
+ cipher_attack_detected = packet_disconnect;
+
+ /* If requested, fork and let ssh continue in the background. */
+ if (fork_after_authentication_flag)
+ {
+ int ret = fork();
+ if (ret == -1)
+ fatal("fork failed: %.100s", strerror(errno));
+ if (ret != 0)
+ exit(0);
+ setsid();
+ }
+
+ /* Enable compression if requested. */
+ if (options.compression)
+ {
+ debug("Requesting compression at level %d.", options.compression_level);
+
+ if (options.compression_level < 1 || options.compression_level > 9)
+ fatal("Compression level must be from 1 (fast) to 9 (slow, best).");
+
+ /* Send the request. */
+ packet_start(SSH_CMSG_REQUEST_COMPRESSION);
+ packet_put_int(options.compression_level);
+ packet_send();
+ packet_write_wait();
+ type = packet_read(&plen);
+ if (type == SSH_SMSG_SUCCESS)
+ packet_start_compression(options.compression_level);
+ else if (type == SSH_SMSG_FAILURE)
+ log("Warning: Remote host refused compression.");
+ else
+ packet_disconnect("Protocol error waiting for compression response.");
+ }
+
+ /* Allocate a pseudo tty if appropriate. */
+ if (tty_flag)
+ {
+ debug("Requesting pty.");
+
+ /* Start the packet. */
+ packet_start(SSH_CMSG_REQUEST_PTY);
+
+ /* Store TERM in the packet. There is no limit on the length of the
+ string. */
+ cp = getenv("TERM");
+ if (!cp)
+ cp = "";
+ packet_put_string(cp, strlen(cp));
+
+ /* Store window size in the packet. */
+ if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0)
+ memset(&ws, 0, sizeof(ws));
+ packet_put_int(ws.ws_row);
+ packet_put_int(ws.ws_col);
+ packet_put_int(ws.ws_xpixel);
+ packet_put_int(ws.ws_ypixel);
+
+ /* Store tty modes in the packet. */
+ tty_make_modes(fileno(stdin));
+
+ /* Send the packet, and wait for it to leave. */
+ packet_send();
+ packet_write_wait();
+
+ /* Read response from the server. */
+ type = packet_read(&plen);
+ if (type == SSH_SMSG_SUCCESS)
+ interactive = 1;
+ else if (type == SSH_SMSG_FAILURE)
+ log("Warning: Remote host failed or refused to allocate a pseudo tty.");
+ else
+ packet_disconnect("Protocol error waiting for pty request response.");
+ }
+
+ /* Request X11 forwarding if enabled and DISPLAY is set. */
+ if (options.forward_x11 && getenv("DISPLAY") != NULL)
+ {
+ char line[512], proto[512], data[512];
+ FILE *f;
+ int forwarded = 0, got_data = 0, i;
+
+#ifdef XAUTH_PATH
+ /* Try to get Xauthority information for the display. */
+ snprintf(line, sizeof line, "%.100s list %.200s 2>/dev/null",
+ XAUTH_PATH, getenv("DISPLAY"));
+ f = popen(line, "r");
+ if (f && fgets(line, sizeof(line), f) &&
+ sscanf(line, "%*s %s %s", proto, data) == 2)
+ got_data = 1;
+ if (f)
+ pclose(f);
+#endif /* XAUTH_PATH */
+ /* If we didn't get authentication data, just make up some data. The
+ forwarding code will check the validity of the response anyway, and
+ substitute this data. The X11 server, however, will ignore this
+ fake data and use whatever authentication mechanisms it was using
+ otherwise for the local connection. */
+ if (!got_data)
+ {
+ u_int32_t rand = 0;
+
+ strlcpy(proto, "MIT-MAGIC-COOKIE-1", sizeof proto);
+ for (i = 0; i < 16; i++) {
+ if (i % 4 == 0)
+ rand = arc4random();
+ snprintf(data + 2 * i, sizeof data - 2 * i, "%02x", rand & 0xff);
+ rand >>= 8;
+ }
+ }
+
+ /* Got local authentication reasonable information. Request forwarding
+ with authentication spoofing. */
+ debug("Requesting X11 forwarding with authentication spoofing.");
+ x11_request_forwarding_with_spoofing(proto, data);
+
+ /* Read response from the server. */
+ type = packet_read(&plen);
+ if (type == SSH_SMSG_SUCCESS)
+ {
+ forwarded = 1;
+ interactive = 1;
+ }
+ else if (type == SSH_SMSG_FAILURE)
+ log("Warning: Remote host denied X11 forwarding.");
+ else
+ packet_disconnect("Protocol error waiting for X11 forwarding");
+ }
+
+ /* Tell the packet module whether this is an interactive session. */
+ packet_set_interactive(interactive, options.keepalives);
+
+ /* Clear agent forwarding if we don\'t have an agent. */
+ authfd = ssh_get_authentication_socket();
+ if (authfd < 0)
+ options.forward_agent = 0;
+ else
+ ssh_close_authentication_socket(authfd);
+
+ /* Request authentication agent forwarding if appropriate. */
+ if (options.forward_agent)
+ {
+ debug("Requesting authentication agent forwarding.");
+ auth_request_forwarding();
+
+ /* Read response from the server. */
+ type = packet_read(&plen);
+ packet_integrity_check(plen, 0, type);
+ if (type != SSH_SMSG_SUCCESS)
+ log("Warning: Remote host denied authentication agent forwarding.");
+ }
+
+ /* Initiate local TCP/IP port forwardings. */
+ for (i = 0; i < options.num_local_forwards; i++)
+ {
+ debug("Connections to local port %d forwarded to remote address %.200s:%d",
+ options.local_forwards[i].port, options.local_forwards[i].host,
+ options.local_forwards[i].host_port);
+ channel_request_local_forwarding(options.local_forwards[i].port,
+ options.local_forwards[i].host,
+ options.local_forwards[i].host_port);
+ }
+
+ /* Initiate remote TCP/IP port forwardings. */
+ for (i = 0; i < options.num_remote_forwards; i++)
+ {
+ debug("Connections to remote port %d forwarded to local address %.200s:%d",
+ options.remote_forwards[i].port, options.remote_forwards[i].host,
+ options.remote_forwards[i].host_port);
+ channel_request_remote_forwarding(options.remote_forwards[i].port,
+ options.remote_forwards[i].host,
+ options.remote_forwards[i].host_port);
+ }
+
+ /* If a command was specified on the command line, execute the command now.
+ Otherwise request the server to start a shell. */
+ if (buffer_len(&command) > 0)
+ {
+ int len = buffer_len(&command);
+ if (len > 900)
+ len = 900;
+ debug("Sending command: %.*s", len, buffer_ptr(&command));
+ packet_start(SSH_CMSG_EXEC_CMD);
+ packet_put_string(buffer_ptr(&command), buffer_len(&command));
+ packet_send();
+ packet_write_wait();
+ }
+ else
+ {
+ debug("Requesting shell.");
+ packet_start(SSH_CMSG_EXEC_SHELL);
+ packet_send();
+ packet_write_wait();
+ }
+
+ /* Enter the interactive session. */
+ exit_status = client_loop(tty_flag, tty_flag ? options.escape_char : -1);
+
+ /* Close the connection to the remote host. */
+ packet_close();
+
+ /* Exit with the status returned by the program on the remote side. */
+ exit(exit_status);
+}
diff --git a/ssh.h b/ssh.h
new file mode 100644
index 00000000..a4bf136e
--- /dev/null
+++ b/ssh.h
@@ -0,0 +1,589 @@
+/*
+
+ssh.h
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ All rights reserved
+
+Created: Fri Mar 17 17:09:37 1995 ylo
+
+Generic header file for ssh.
+
+*/
+
+/* RCSID("$Id: ssh.h,v 1.1 1999/10/27 03:42:45 damien Exp $"); */
+
+#ifndef SSH_H
+#define SSH_H
+
+#include "rsa.h"
+#include "cipher.h"
+
+/* The default cipher used if IDEA is not supported by the remote host.
+ It is recommended that this be one of the mandatory ciphers (DES, 3DES),
+ though that is not required. */
+#define SSH_FALLBACK_CIPHER SSH_CIPHER_3DES
+
+/* Cipher used for encrypting authentication files. */
+#define SSH_AUTHFILE_CIPHER SSH_CIPHER_3DES
+
+/* Default port number. */
+#define SSH_DEFAULT_PORT 22
+
+/* Maximum number of TCP/IP ports forwarded per direction. */
+#define SSH_MAX_FORWARDS_PER_DIRECTION 100
+
+/* Maximum number of RSA authentication identity files that can be specified
+ in configuration files or on the command line. */
+#define SSH_MAX_IDENTITY_FILES 100
+
+/* Major protocol version. Different version indicates major incompatiblity
+ that prevents communication. */
+#define PROTOCOL_MAJOR 1
+
+/* Minor protocol version. Different version indicates minor incompatibility
+ that does not prevent interoperation. */
+#define PROTOCOL_MINOR 5
+
+/* Name for the service. The port named by this service overrides the default
+ port if present. */
+#define SSH_SERVICE_NAME "ssh"
+
+#ifndef ETCDIR
+#define ETCDIR "/etc"
+#endif /* ETCDIR */
+
+#define PIDDIR "/var/run"
+
+/* System-wide file containing host keys of known hosts. This file should be
+ world-readable. */
+#define SSH_SYSTEM_HOSTFILE ETCDIR "/ssh_known_hosts"
+
+/* HOST_KEY_FILE /etc/ssh_host_key,
+ SERVER_CONFIG_FILE /etc/sshd_config,
+and HOST_CONFIG_FILE /etc/ssh_config
+are all defined in Makefile.in. Of these, ssh_host_key should be readable
+only by root, whereas ssh_config should be world-readable. */
+
+#define HOST_KEY_FILE ETCDIR "/ssh_host_key"
+#define SERVER_CONFIG_FILE ETCDIR "/sshd_config"
+#define HOST_CONFIG_FILE ETCDIR "/ssh_config"
+
+#define SSH_PROGRAM "/usr/bin/ssh"
+
+/* The process id of the daemon listening for connections is saved
+ here to make it easier to kill the correct daemon when necessary. */
+#define SSH_DAEMON_PID_FILE PIDDIR "/sshd.pid"
+
+/* The directory in user\'s home directory in which the files reside.
+ The directory should be world-readable (though not all files are). */
+#define SSH_USER_DIR ".ssh"
+
+/* Per-user file containing host keys of known hosts. This file need
+ not be readable by anyone except the user him/herself, though this does
+ not contain anything particularly secret. */
+#define SSH_USER_HOSTFILE "~/.ssh/known_hosts"
+
+/* Name of the default file containing client-side authentication key.
+ This file should only be readable by the user him/herself. */
+#define SSH_CLIENT_IDENTITY ".ssh/identity"
+
+/* Configuration file in user\'s home directory. This file need not be
+ readable by anyone but the user him/herself, but does not contain
+ anything particularly secret. If the user\'s home directory resides
+ on an NFS volume where root is mapped to nobody, this may need to be
+ world-readable. */
+#define SSH_USER_CONFFILE ".ssh/config"
+
+/* File containing a list of those rsa keys that permit logging in as
+ this user. This file need not be
+ readable by anyone but the user him/herself, but does not contain
+ anything particularly secret. If the user\'s home directory resides
+ on an NFS volume where root is mapped to nobody, this may need to be
+ world-readable. (This file is read by the daemon which is running as
+ root.) */
+#define SSH_USER_PERMITTED_KEYS ".ssh/authorized_keys"
+
+/* Per-user and system-wide ssh "rc" files. These files are executed with
+ /bin/sh before starting the shell or command if they exist. They
+ will be passed "proto cookie" as arguments if X11 forwarding with
+ spoofing is in use. xauth will be run if neither of these exists. */
+#define SSH_USER_RC ".ssh/rc"
+#define SSH_SYSTEM_RC ETCDIR "/sshrc"
+
+/* Ssh-only version of /etc/hosts.equiv. */
+#define SSH_HOSTS_EQUIV ETCDIR "/shosts.equiv"
+
+/* Additionally, the daemon may use ~/.rhosts and /etc/hosts.equiv if
+ rhosts authentication is enabled. */
+
+/* Name of the environment variable containing the pathname of the
+ authentication socket. */
+#define SSH_AUTHSOCKET_ENV_NAME "SSH_AUTH_SOCK"
+
+/* Force host key length and server key length to differ by at least this
+ many bits. This is to make double encryption with rsaref work. */
+#define SSH_KEY_BITS_RESERVED 128
+
+/* Length of the session key in bytes. (Specified as 256 bits in the
+ protocol.) */
+#define SSH_SESSION_KEY_LENGTH 32
+
+/* Name of Kerberos service for SSH to use. */
+#define KRB4_SERVICE_NAME "rcmd"
+
+/* Authentication methods. New types can be added, but old types should not
+ be removed for compatibility. The maximum allowed value is 31. */
+#define SSH_AUTH_RHOSTS 1
+#define SSH_AUTH_RSA 2
+#define SSH_AUTH_PASSWORD 3
+#define SSH_AUTH_RHOSTS_RSA 4
+ /* 5 is TIS */
+#define SSH_AUTH_KERBEROS 6
+#define SSH_PASS_KERBEROS_TGT 7
+ /* 8 to 15 are reserved */
+#define SSH_PASS_AFS_TOKEN 21
+
+/* Protocol flags. These are bit masks. */
+#define SSH_PROTOFLAG_SCREEN_NUMBER 1 /* X11 forwarding includes screen */
+#define SSH_PROTOFLAG_HOST_IN_FWD_OPEN 2 /* forwarding opens contain host */
+
+/* Definition of message types. New values can be added, but old values
+ should not be removed or without careful consideration of the consequences
+ for compatibility. The maximum value is 254; value 255 is reserved
+ for future extension. */
+/* Message name */ /* msg code */ /* arguments */
+#define SSH_MSG_NONE 0 /* no message */
+#define SSH_MSG_DISCONNECT 1 /* cause (string) */
+#define SSH_SMSG_PUBLIC_KEY 2 /* ck,msk,srvk,hostk */
+#define SSH_CMSG_SESSION_KEY 3 /* key (BIGNUM) */
+#define SSH_CMSG_USER 4 /* user (string) */
+#define SSH_CMSG_AUTH_RHOSTS 5 /* user (string) */
+#define SSH_CMSG_AUTH_RSA 6 /* modulus (BIGNUM) */
+#define SSH_SMSG_AUTH_RSA_CHALLENGE 7 /* int (BIGNUM) */
+#define SSH_CMSG_AUTH_RSA_RESPONSE 8 /* int (BIGNUM) */
+#define SSH_CMSG_AUTH_PASSWORD 9 /* pass (string) */
+#define SSH_CMSG_REQUEST_PTY 10 /* TERM, tty modes */
+#define SSH_CMSG_WINDOW_SIZE 11 /* row,col,xpix,ypix */
+#define SSH_CMSG_EXEC_SHELL 12 /* */
+#define SSH_CMSG_EXEC_CMD 13 /* cmd (string) */
+#define SSH_SMSG_SUCCESS 14 /* */
+#define SSH_SMSG_FAILURE 15 /* */
+#define SSH_CMSG_STDIN_DATA 16 /* data (string) */
+#define SSH_SMSG_STDOUT_DATA 17 /* data (string) */
+#define SSH_SMSG_STDERR_DATA 18 /* data (string) */
+#define SSH_CMSG_EOF 19 /* */
+#define SSH_SMSG_EXITSTATUS 20 /* status (int) */
+#define SSH_MSG_CHANNEL_OPEN_CONFIRMATION 21 /* channel (int) */
+#define SSH_MSG_CHANNEL_OPEN_FAILURE 22 /* channel (int) */
+#define SSH_MSG_CHANNEL_DATA 23 /* ch,data (int,str) */
+#define SSH_MSG_CHANNEL_CLOSE 24 /* channel (int) */
+#define SSH_MSG_CHANNEL_CLOSE_CONFIRMATION 25 /* channel (int) */
+/* SSH_CMSG_X11_REQUEST_FORWARDING 26 OBSOLETE */
+#define SSH_SMSG_X11_OPEN 27 /* channel (int) */
+#define SSH_CMSG_PORT_FORWARD_REQUEST 28 /* p,host,hp (i,s,i) */
+#define SSH_MSG_PORT_OPEN 29 /* ch,h,p (i,s,i) */
+#define SSH_CMSG_AGENT_REQUEST_FORWARDING 30 /* */
+#define SSH_SMSG_AGENT_OPEN 31 /* port (int) */
+#define SSH_MSG_IGNORE 32 /* string */
+#define SSH_CMSG_EXIT_CONFIRMATION 33 /* */
+#define SSH_CMSG_X11_REQUEST_FORWARDING 34 /* proto,data (s,s) */
+#define SSH_CMSG_AUTH_RHOSTS_RSA 35 /* user,mod (s,mpi) */
+#define SSH_MSG_DEBUG 36 /* string */
+#define SSH_CMSG_REQUEST_COMPRESSION 37 /* level 1-9 (int) */
+#define SSH_CMSG_MAX_PACKET_SIZE 38 /* size 4k-1024k (int) */
+#define SSH_CMSG_AUTH_TIS 39 /* this is proto-1.5, but we ignore TIS */
+#define SSH_SMSG_AUTH_TIS_CHALLENGE 40
+#define SSH_CMSG_AUTH_TIS_RESPONSE 41
+
+#define SSH_CMSG_AUTH_KERBEROS 42 /* (KTEXT) */
+#define SSH_SMSG_AUTH_KERBEROS_RESPONSE 43 /* (KTEXT) */
+#define SSH_CMSG_HAVE_KERBEROS_TGT 44 /* credentials (s) */
+#define SSH_CMSG_HAVE_AFS_TOKEN 65 /* token (s) */
+
+
+/* Includes that need definitions above. */
+
+#include "readconf.h"
+
+/*------------ definitions for login.c -------------*/
+
+/* Returns the time when the user last logged in. Returns 0 if the
+ information is not available. This must be called before record_login.
+ The host from which the user logged in is stored in buf. */
+unsigned long get_last_login_time(uid_t uid, const char *logname,
+ char *buf, unsigned int bufsize);
+
+/* Records that the user has logged in. This does many things normally
+ done by login(1). */
+void record_login(int pid, const char *ttyname, const char *user, uid_t uid,
+ const char *host, struct sockaddr_in *addr);
+
+/* Records that the user has logged out. This does many thigs normally
+ done by login(1) or init. */
+void record_logout(int pid, const char *ttyname);
+
+/*------------ definitions for sshconnect.c ----------*/
+
+/* Opens a TCP/IP connection to the remote server on the given host. If
+ port is 0, the default port will be used. If anonymous is zero,
+ a privileged port will be allocated to make the connection.
+ This requires super-user privileges if anonymous is false.
+ Connection_attempts specifies the maximum number of tries, one per
+ second. This returns true on success, and zero on failure. If the
+ connection is successful, this calls packet_set_connection for the
+ connection. */
+int ssh_connect(const char *host, struct sockaddr_in *hostaddr,
+ int port, int connection_attempts,
+ int anonymous, uid_t original_real_uid,
+ const char *proxy_command);
+
+/* Starts a dialog with the server, and authenticates the current user on the
+ server. This does not need any extra privileges. The basic connection
+ to the server must already have been established before this is called.
+ If login fails, this function prints an error and never returns.
+ This initializes the random state, and leaves it initialized (it will also
+ have references from the packet module). */
+void ssh_login(int host_key_valid, RSA *host_key, const char *host,
+ struct sockaddr_in *hostaddr, Options *options,
+ uid_t original_real_uid);
+
+/*------------ Definitions for various authentication methods. -------*/
+
+/* Tries to authenticate the user using the .rhosts file. Returns true if
+ authentication succeeds. If ignore_rhosts is non-zero, this will not
+ consider .rhosts and .shosts (/etc/hosts.equiv will still be used).
+ If strict_modes is true, checks ownership and modes of .rhosts/.shosts. */
+int auth_rhosts(struct passwd *pw, const char *client_user,
+ int ignore_rhosts, int strict_modes);
+
+/* Tries to authenticate the user using the .rhosts file and the host using
+ its host key. Returns true if authentication succeeds. */
+int auth_rhosts_rsa(struct passwd *pw, const char *client_user,
+ unsigned int bits, BIGNUM *client_host_key_e,
+ BIGNUM *client_host_key_n, int ignore_rhosts,
+ int strict_modes);
+
+/* Tries to authenticate the user using password. Returns true if
+ authentication succeeds. */
+int auth_password(struct passwd *pw, const char *password);
+
+/* Performs the RSA authentication dialog with the client. This returns
+ 0 if the client could not be authenticated, and 1 if authentication was
+ successful. This may exit if there is a serious protocol violation. */
+int auth_rsa(struct passwd *pw, BIGNUM *client_n, int strict_modes);
+
+/* Parses an RSA key (number of bits, e, n) from a string. Moves the pointer
+ over the key. Skips any whitespace at the beginning and at end. */
+int auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM *e, BIGNUM *n);
+
+/* Returns the name of the machine at the other end of the socket. The
+ returned string should be freed by the caller. */
+char *get_remote_hostname(int socket);
+
+/* Return the canonical name of the host in the other side of the current
+ connection (as returned by packet_get_connection). The host name is
+ cached, so it is efficient to call this several times. */
+const char *get_canonical_hostname(void);
+
+/* Returns the remote IP address as an ascii string. The value need not be
+ freed by the caller. */
+const char *get_remote_ipaddr(void);
+
+/* Returns the port number of the peer of the socket. */
+int get_peer_port(int sock);
+
+/* Returns the port number of the remote host. */
+int get_remote_port(void);
+
+/* Tries to match the host name (which must be in all lowercase) against the
+ comma-separated sequence of subpatterns (each possibly preceded by ! to
+ indicate negation). Returns true if there is a positive match; zero
+ otherwise. */
+int match_hostname(const char *host, const char *pattern, unsigned int len);
+
+/* Checks whether the given host is already in the list of our known hosts.
+ Returns HOST_OK if the host is known and has the specified key,
+ HOST_NEW if the host is not known, and HOST_CHANGED if the host is known
+ but used to have a different host key. The host must be in all lowercase. */
+typedef enum { HOST_OK, HOST_NEW, HOST_CHANGED } HostStatus;
+HostStatus check_host_in_hostfile(const char *filename,
+ const char *host, unsigned int bits,
+ BIGNUM *e, BIGNUM *n,
+ BIGNUM *ke, BIGNUM *kn);
+
+/* Appends an entry to the host file. Returns false if the entry
+ could not be appended. */
+int add_host_to_hostfile(const char *filename, const char *host,
+ unsigned int bits, BIGNUM *e, BIGNUM *n);
+
+/* Performs the RSA authentication challenge-response dialog with the client,
+ and returns true (non-zero) if the client gave the correct answer to
+ our challenge; returns zero if the client gives a wrong answer. */
+int auth_rsa_challenge_dialog(unsigned int bits, BIGNUM *e, BIGNUM *n);
+
+/* Reads a passphrase from /dev/tty with echo turned off. Returns the
+ passphrase (allocated with xmalloc). Exits if EOF is encountered.
+ If from_stdin is true, the passphrase will be read from stdin instead. */
+char *read_passphrase(const char *prompt, int from_stdin);
+
+/* Saves the authentication (private) key in a file, encrypting it with
+ passphrase. The identification of the file (lowest 64 bits of n)
+ will precede the key to provide identification of the key without
+ needing a passphrase. */
+int save_private_key(const char *filename, const char *passphrase,
+ RSA *private_key, const char *comment);
+
+/* Loads the public part of the key file (public key and comment).
+ Returns 0 if an error occurred; zero if the public key was successfully
+ read. The comment of the key is returned in comment_return if it is
+ non-NULL; the caller must free the value with xfree. */
+int load_public_key(const char *filename, RSA *pub,
+ char **comment_return);
+
+/* Loads the private key from the file. Returns 0 if an error is encountered
+ (file does not exist or is not readable, or passphrase is bad).
+ This initializes the private key. The comment of the key is returned
+ in comment_return if it is non-NULL; the caller must free the value
+ with xfree. */
+int load_private_key(const char *filename, const char *passphrase,
+ RSA *private_key, char **comment_return);
+
+/*------------ Definitions for logging. -----------------------*/
+
+/* Supported syslog facilities. */
+typedef enum
+{
+ SYSLOG_FACILITY_DAEMON,
+ SYSLOG_FACILITY_USER,
+ SYSLOG_FACILITY_AUTH,
+ SYSLOG_FACILITY_LOCAL0,
+ SYSLOG_FACILITY_LOCAL1,
+ SYSLOG_FACILITY_LOCAL2,
+ SYSLOG_FACILITY_LOCAL3,
+ SYSLOG_FACILITY_LOCAL4,
+ SYSLOG_FACILITY_LOCAL5,
+ SYSLOG_FACILITY_LOCAL6,
+ SYSLOG_FACILITY_LOCAL7
+} SyslogFacility;
+
+/* Initializes logging. If debug is non-zero, debug() will output something.
+ If quiet is non-zero, none of these will log send anything to syslog
+ (but maybe to stderr). */
+void log_init(char *av0, int on_stderr, int debug, int quiet,
+ SyslogFacility facility);
+
+/* Outputs a message to syslog or stderr, depending on the implementation.
+ The format must guarantee that the final message does not exceed 1024
+ characters. The message should not contain newline. */
+void log(const char *fmt, ...);
+
+/* Outputs a message to syslog or stderr, depending on the implementation.
+ The format must guarantee that the final message does not exceed 1024
+ characters. The message should not contain newline. */
+void debug(const char *fmt, ...);
+
+/* Outputs a message to syslog or stderr, depending on the implementation.
+ The format must guarantee that the final message does not exceed 1024
+ characters. The message should not contain newline. */
+void error(const char *fmt, ...);
+
+/* Outputs a message to syslog or stderr, depending on the implementation.
+ The format must guarantee that the final message does not exceed 1024
+ characters. The message should not contain newline.
+ This call never returns. */
+void fatal(const char *fmt, ...);
+
+/* Registers a cleanup function to be called by fatal() before exiting.
+ It is permissible to call fatal_remove_cleanup for the function itself
+ from the function. */
+void fatal_add_cleanup(void (*proc)(void *context), void *context);
+
+/* Removes a cleanup frunction to be called at fatal(). */
+void fatal_remove_cleanup(void (*proc)(void *context), void *context);
+
+/*---------------- definitions for channels ------------------*/
+
+/* Sets specific protocol options. */
+void channel_set_options(int hostname_in_open);
+
+/* Allocate a new channel object and set its type and socket. Remote_name
+ must have been allocated with xmalloc; this will free it when the channel
+ is freed. */
+int channel_allocate(int type, int sock, char *remote_name);
+
+/* Free the channel and close its socket. */
+void channel_free(int channel);
+
+/* Add any bits relevant to channels in select bitmasks. */
+void channel_prepare_select(fd_set *readset, fd_set *writeset);
+
+/* After select, perform any appropriate operations for channels which
+ have events pending. */
+void channel_after_select(fd_set *readset, fd_set *writeset);
+
+/* If there is data to send to the connection, send some of it now. */
+void channel_output_poll(void);
+
+/* This is called when a packet of type CHANNEL_DATA has just been received.
+ The message type has already been consumed, but channel number and data
+ is still there. */
+void channel_input_data(int payload_len);
+
+/* Returns true if no channel has too much buffered data. */
+int channel_not_very_much_buffered_data(void);
+
+/* This is called after receiving CHANNEL_CLOSE. */
+void channel_input_close(void);
+
+/* This is called after receiving CHANNEL_CLOSE_CONFIRMATION. */
+void channel_input_close_confirmation(void);
+
+/* This is called after receiving CHANNEL_OPEN_CONFIRMATION. */
+void channel_input_open_confirmation(void);
+
+/* This is called after receiving CHANNEL_OPEN_FAILURE from the other side. */
+void channel_input_open_failure(void);
+
+/* This closes any sockets that are listening for connections; this removes
+ any unix domain sockets. */
+void channel_stop_listening(void);
+
+/* Closes the sockets of all channels. This is used to close extra file
+ descriptors after a fork. */
+void channel_close_all(void);
+
+/* Returns the maximum file descriptor number used by the channels. */
+int channel_max_fd(void);
+
+/* Returns true if there is still an open channel over the connection. */
+int channel_still_open(void);
+
+/* Returns a string containing a list of all open channels. The list is
+ suitable for displaying to the user. It uses crlf instead of newlines.
+ The caller should free the string with xfree. */
+char *channel_open_message(void);
+
+/* Initiate forwarding of connections to local port "port" through the secure
+ channel to host:port from remote side. This never returns if there
+ was an error. */
+void channel_request_local_forwarding(int port, const char *host,
+ int remote_port);
+
+/* Initiate forwarding of connections to port "port" on remote host through
+ the secure channel to host:port from local side. This never returns
+ if there was an error. This registers that open requests for that
+ port are permitted. */
+void channel_request_remote_forwarding(int port, const char *host,
+ int remote_port);
+
+/* Permits opening to any host/port in SSH_MSG_PORT_OPEN. This is usually
+ called by the server, because the user could connect to any port anyway,
+ and the server has no way to know but to trust the client anyway. */
+void channel_permit_all_opens(void);
+
+/* This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates
+ listening for the port, and sends back a success reply (or disconnect
+ message if there was an error). This never returns if there was an
+ error. */
+void channel_input_port_forward_request(int is_root);
+
+/* This is called after receiving PORT_OPEN message. This attempts to connect
+ to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION or
+ CHANNEL_OPEN_FAILURE. */
+void channel_input_port_open(int payload_len);
+
+/* Creates a port for X11 connections, and starts listening for it.
+ Returns the display name, or NULL if an error was encountered. */
+char *x11_create_display(int screen);
+
+/* Creates an internet domain socket for listening for X11 connections.
+ Returns a suitable value for the DISPLAY variable, or NULL if an error
+ occurs. */
+char *x11_create_display_inet(int screen);
+
+/* This is called when SSH_SMSG_X11_OPEN is received. The packet contains
+ the remote channel number. We should do whatever we want, and respond
+ with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. */
+void x11_input_open(int payload_len);
+
+/* Requests forwarding of X11 connections. This should be called on the
+ client only. */
+void x11_request_forwarding(void);
+
+/* Requests forwarding for X11 connections, with authentication spoofing.
+ This should be called in the client only. */
+void x11_request_forwarding_with_spoofing(const char *proto, const char *data);
+
+/* Local Xauthority file (server only). */
+extern char *xauthfile;
+
+/* Sends a message to the server to request authentication fd forwarding. */
+void auth_request_forwarding(void);
+
+/* Returns the name of the forwarded authentication socket. Returns NULL
+ if there is no forwarded authentication socket. The returned value points
+ to a static buffer. */
+char *auth_get_socket_name(void);
+
+/* This if called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server.
+ This starts forwarding authentication requests. */
+void auth_input_request_forwarding(struct passwd *pw);
+
+/* This is called to process an SSH_SMSG_AGENT_OPEN message. */
+void auth_input_open_request(void);
+
+/* Returns true if the given string matches the pattern (which may contain
+ ? and * as wildcards), and zero if it does not match. */
+int match_pattern(const char *s, const char *pattern);
+
+/* Expands tildes in the file name. Returns data allocated by xmalloc.
+ Warning: this calls getpw*. */
+char *tilde_expand_filename(const char *filename, uid_t my_uid);
+
+/* Performs the interactive session. This handles data transmission between
+ the client and the program. Note that the notion of stdin, stdout, and
+ stderr in this function is sort of reversed: this function writes to
+ stdin (of the child program), and reads from stdout and stderr (of the
+ child program). */
+void server_loop(int pid, int fdin, int fdout, int fderr);
+
+/* Client side main loop for the interactive session. */
+int client_loop(int have_pty, int escape_char);
+
+/* Linked list of custom environment strings (see auth-rsa.c). */
+struct envstring {
+ struct envstring *next;
+ char *s;
+};
+
+#ifdef KRB4
+#include <krb.h>
+
+/* Performs Kerberos v4 mutual authentication with the client. This returns
+ 0 if the client could not be authenticated, and 1 if authentication was
+ successful. This may exit if there is a serious protocol violation. */
+int auth_krb4(const char *server_user, KTEXT auth, char **client);
+int ssh_tf_init(uid_t uid);
+
+#ifdef AFS
+#include <kafs.h>
+
+/* Accept passed Kerberos v4 ticket-granting ticket and AFS tokens. */
+int auth_kerberos_tgt(struct passwd *pw, const char *string);
+int auth_afs_token(char *server_user, uid_t uid, const char *string);
+
+int creds_to_radix(CREDENTIALS *creds, unsigned char *buf);
+int radix_to_creds(const char *buf, CREDENTIALS *creds);
+#endif /* AFS */
+
+#endif /* KRB4 */
+
+#ifdef SKEY
+#include <skey.h>
+char *skey_fake_keyinfo(char *username);
+#endif /* SKEY */
+
+#endif /* SSH_H */
diff --git a/ssh.pam b/ssh.pam
new file mode 100644
index 00000000..2a7d1fbd
--- /dev/null
+++ b/ssh.pam
@@ -0,0 +1,7 @@
+#%PAM-1.0
+auth required /lib/security/pam_pwdb.so shadow
+auth required /lib/security/pam_nologin.so
+account required /lib/security/pam_pwdb.so
+password required /lib/security/pam_cracklib.so
+password required /lib/security/pam_pwdb.so shadow nullok use_authtok
+session required /lib/security/pam_pwdb.so
diff --git a/ssh_config b/ssh_config
new file mode 100644
index 00000000..9fb064de
--- /dev/null
+++ b/ssh_config
@@ -0,0 +1,30 @@
+# This is ssh client systemwide configuration file. This file provides
+# defaults for users, and the values can be changed in per-user configuration
+# files or on the command line.
+
+# Configuration data is parsed as follows:
+# 1. command line options
+# 2. user-specific file
+# 3. system-wide file
+# Any configuration value is only changed the first time it is set.
+# Thus, host-specific definitions should be at the beginning of the
+# configuration file, and defaults at the end.
+
+# Site-wide defaults for various options
+
+# Host *
+# ForwardAgent yes
+# ForwardX11 yes
+# RhostsAuthentication yes
+# RhostsRSAAuthentication yes
+# RSAAuthentication yes
+# PasswordAuthentication yes
+# FallBackToRsh yes
+# UseRsh no
+# BatchMode no
+# CheckHostIP yes
+# StrictHostKeyChecking no
+# IdentityFile ~/.ssh/identity
+# Port 22
+# Cipher blowfish
+# EscapeChar ~
diff --git a/sshconnect.c b/sshconnect.c
new file mode 100644
index 00000000..3437b04c
--- /dev/null
+++ b/sshconnect.c
@@ -0,0 +1,1495 @@
+/*
+
+sshconnect.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ All rights reserved
+
+Created: Sat Mar 18 22:15:47 1995 ylo
+
+Code to connect to a remote host, and to perform the client side of the
+login (authentication) dialog.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: sshconnect.c,v 1.1 1999/10/27 03:42:45 damien Exp $");
+
+#include <openssl/bn.h>
+#include "xmalloc.h"
+#include "rsa.h"
+#include "ssh.h"
+#include "packet.h"
+#include "authfd.h"
+#include "cipher.h"
+#include "mpaux.h"
+#include "uidswap.h"
+#include "compat.h"
+
+#include <openssl/md5.h>
+
+/* Session id for the current session. */
+unsigned char session_id[16];
+
+/* Connect to the given ssh server using a proxy command. */
+
+int
+ssh_proxy_connect(const char *host, int port, uid_t original_real_uid,
+ const char *proxy_command)
+{
+ Buffer command;
+ const char *cp;
+ char *command_string;
+ int pin[2], pout[2];
+ int pid;
+ char portstring[100];
+
+ /* Convert the port number into a string. */
+ snprintf(portstring, sizeof portstring, "%d", port);
+
+ /* Build the final command string in the buffer by making the appropriate
+ substitutions to the given proxy command. */
+ buffer_init(&command);
+ for (cp = proxy_command; *cp; cp++)
+ {
+ if (cp[0] == '%' && cp[1] == '%')
+ {
+ buffer_append(&command, "%", 1);
+ cp++;
+ continue;
+ }
+ if (cp[0] == '%' && cp[1] == 'h')
+ {
+ buffer_append(&command, host, strlen(host));
+ cp++;
+ continue;
+ }
+ if (cp[0] == '%' && cp[1] == 'p')
+ {
+ buffer_append(&command, portstring, strlen(portstring));
+ cp++;
+ continue;
+ }
+ buffer_append(&command, cp, 1);
+ }
+ buffer_append(&command, "\0", 1);
+
+ /* Get the final command string. */
+ command_string = buffer_ptr(&command);
+
+ /* Create pipes for communicating with the proxy. */
+ if (pipe(pin) < 0 || pipe(pout) < 0)
+ fatal("Could not create pipes to communicate with the proxy: %.100s",
+ strerror(errno));
+
+ debug("Executing proxy command: %.500s", command_string);
+
+ /* Fork and execute the proxy command. */
+ if ((pid = fork()) == 0)
+ {
+ char *argv[10];
+
+ /* Child. Permanently give up superuser privileges. */
+ permanently_set_uid(original_real_uid);
+
+ /* Redirect stdin and stdout. */
+ close(pin[1]);
+ if (pin[0] != 0)
+ {
+ if (dup2(pin[0], 0) < 0)
+ perror("dup2 stdin");
+ close(pin[0]);
+ }
+ close(pout[0]);
+ if (dup2(pout[1], 1) < 0)
+ perror("dup2 stdout");
+ close(pout[1]); /* Cannot be 1 because pin allocated two descriptors. */
+
+ /* Stderr is left as it is so that error messages get printed on
+ the user's terminal. */
+ argv[0] = "/bin/sh";
+ argv[1] = "-c";
+ argv[2] = command_string;
+ argv[3] = NULL;
+
+ /* Execute the proxy command. Note that we gave up any extra
+ privileges above. */
+ execv("/bin/sh", argv);
+ perror("/bin/sh");
+ exit(1);
+ }
+ /* Parent. */
+ if (pid < 0)
+ fatal("fork failed: %.100s", strerror(errno));
+
+ /* Close child side of the descriptors. */
+ close(pin[0]);
+ close(pout[1]);
+
+ /* Free the command name. */
+ buffer_free(&command);
+
+ /* Set the connection file descriptors. */
+ packet_set_connection(pout[0], pin[1]);
+
+ return 1;
+}
+
+/* Creates a (possibly privileged) socket for use as the ssh connection. */
+
+int ssh_create_socket(uid_t original_real_uid, int privileged)
+{
+ int sock;
+
+ /* If we are running as root and want to connect to a privileged port,
+ bind our own socket to a privileged port. */
+ if (privileged)
+ {
+ int p = IPPORT_RESERVED - 1;
+
+ sock = rresvport(&p);
+ if (sock < 0)
+ fatal("rresvport: %.100s", strerror(errno));
+ debug("Allocated local port %d.", p);
+ }
+ else
+ {
+ /* Just create an ordinary socket on arbitrary port. We use the
+ user's uid to create the socket. */
+ temporarily_use_uid(original_real_uid);
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0)
+ fatal("socket: %.100s", strerror(errno));
+ restore_uid();
+ }
+ return sock;
+}
+
+/* Opens a TCP/IP connection to the remote server on the given host. If
+ port is 0, the default port will be used. If anonymous is zero,
+ a privileged port will be allocated to make the connection.
+ This requires super-user privileges if anonymous is false.
+ Connection_attempts specifies the maximum number of tries (one per
+ second). If proxy_command is non-NULL, it specifies the command (with %h
+ and %p substituted for host and port, respectively) to use to contact
+ the daemon. */
+
+int ssh_connect(const char *host, struct sockaddr_in *hostaddr,
+ int port, int connection_attempts,
+ int anonymous, uid_t original_real_uid,
+ const char *proxy_command)
+{
+ int sock = -1, attempt, i;
+ int on = 1;
+ struct servent *sp;
+ struct hostent *hp;
+ struct linger linger;
+
+ debug("ssh_connect: getuid %d geteuid %d anon %d",
+ (int)getuid(), (int)geteuid(), anonymous);
+
+ /* Get default port if port has not been set. */
+ if (port == 0)
+ {
+ sp = getservbyname(SSH_SERVICE_NAME, "tcp");
+ if (sp)
+ port = ntohs(sp->s_port);
+ else
+ port = SSH_DEFAULT_PORT;
+ }
+
+ /* If a proxy command is given, connect using it. */
+ if (proxy_command != NULL)
+ return ssh_proxy_connect(host, port, original_real_uid, proxy_command);
+
+ /* No proxy command. */
+
+ /* No host lookup made yet. */
+ hp = NULL;
+
+ /* Try to connect several times. On some machines, the first time will
+ sometimes fail. In general socket code appears to behave quite
+ magically on many machines. */
+ for (attempt = 0; attempt < connection_attempts; attempt++)
+ {
+ if (attempt > 0)
+ debug("Trying again...");
+
+ /* Try to parse the host name as a numeric inet address. */
+ memset(hostaddr, 0, sizeof(hostaddr));
+ hostaddr->sin_family = AF_INET;
+ hostaddr->sin_port = htons(port);
+ hostaddr->sin_addr.s_addr = inet_addr(host);
+ if ((hostaddr->sin_addr.s_addr & 0xffffffff) != 0xffffffff)
+ {
+ /* Valid numeric IP address */
+ debug("Connecting to %.100s port %d.",
+ inet_ntoa(hostaddr->sin_addr), port);
+
+ /* Create a socket. */
+ sock = ssh_create_socket(original_real_uid,
+ !anonymous && geteuid() == 0 &&
+ port < IPPORT_RESERVED);
+
+ /* Connect to the host. We use the user's uid in the hope that
+ it will help with the problems of tcp_wrappers showing the
+ remote uid as root. */
+ temporarily_use_uid(original_real_uid);
+ if (connect(sock, (struct sockaddr *)hostaddr, sizeof(*hostaddr))
+ >= 0)
+ {
+ /* Successful connect. */
+ restore_uid();
+ break;
+ }
+ debug("connect: %.100s", strerror(errno));
+ restore_uid();
+
+ /* Destroy the failed socket. */
+ shutdown(sock, SHUT_RDWR);
+ close(sock);
+ }
+ else
+ {
+ /* Not a valid numeric inet address. */
+ /* Map host name to an address. */
+ if (!hp)
+ hp = gethostbyname(host);
+ if (!hp)
+ fatal("Bad host name: %.100s", host);
+ if (!hp->h_addr_list[0])
+ fatal("Host does not have an IP address: %.100s", host);
+
+ /* Loop through addresses for this host, and try each one in
+ sequence until the connection succeeds. */
+ for (i = 0; hp->h_addr_list[i]; i++)
+ {
+ /* Set the address to connect to. */
+ hostaddr->sin_family = hp->h_addrtype;
+ memcpy(&hostaddr->sin_addr, hp->h_addr_list[i],
+ sizeof(hostaddr->sin_addr));
+
+ debug("Connecting to %.200s [%.100s] port %d.",
+ host, inet_ntoa(hostaddr->sin_addr), port);
+
+ /* Create a socket for connecting. */
+ sock = ssh_create_socket(original_real_uid,
+ !anonymous && geteuid() == 0 &&
+ port < IPPORT_RESERVED);
+
+ /* Connect to the host. We use the user's uid in the hope that
+ it will help with tcp_wrappers showing the remote uid as
+ root. */
+ temporarily_use_uid(original_real_uid);
+ if (connect(sock, (struct sockaddr *)hostaddr,
+ sizeof(*hostaddr)) >= 0)
+ {
+ /* Successful connection. */
+ restore_uid();
+ break;
+ }
+ debug("connect: %.100s", strerror(errno));
+ restore_uid();
+
+ /* Close the failed socket; there appear to be some problems
+ when reusing a socket for which connect() has already
+ returned an error. */
+ shutdown(sock, SHUT_RDWR);
+ close(sock);
+ }
+ if (hp->h_addr_list[i])
+ break; /* Successful connection. */
+ }
+
+ /* Sleep a moment before retrying. */
+ sleep(1);
+ }
+ /* Return failure if we didn't get a successful connection. */
+ if (attempt >= connection_attempts)
+ return 0;
+
+ debug("Connection established.");
+
+ /* Set socket options. We would like the socket to disappear as soon as
+ it has been closed for whatever reason. */
+ /* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */
+ setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&on, sizeof(on));
+ linger.l_onoff = 1;
+ linger.l_linger = 5;
+ setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger));
+
+ /* Set the connection. */
+ packet_set_connection(sock, sock);
+
+ return 1;
+}
+
+/* Checks if the user has an authentication agent, and if so, tries to
+ authenticate using the agent. */
+
+int
+try_agent_authentication()
+{
+ int status, type, bits;
+ char *comment;
+ AuthenticationConnection *auth;
+ unsigned char response[16];
+ unsigned int i;
+ BIGNUM *e, *n, *challenge;
+
+ /* Get connection to the agent. */
+ auth = ssh_get_authentication_connection();
+ if (!auth)
+ return 0;
+
+ e = BN_new();
+ n = BN_new();
+ challenge = BN_new();
+
+ /* Loop through identities served by the agent. */
+ for (status = ssh_get_first_identity(auth, &bits, e, n, &comment);
+ status;
+ status = ssh_get_next_identity(auth, &bits, e, n, &comment))
+ {
+ int plen, clen;
+
+ /* Try this identity. */
+ debug("Trying RSA authentication via agent with '%.100s'", comment);
+ xfree(comment);
+
+ /* Tell the server that we are willing to authenticate using this key. */
+ packet_start(SSH_CMSG_AUTH_RSA);
+ packet_put_bignum(n);
+ packet_send();
+ packet_write_wait();
+
+ /* Wait for server's response. */
+ type = packet_read(&plen);
+
+ /* The server sends failure if it doesn\'t like our key or does not
+ support RSA authentication. */
+ if (type == SSH_SMSG_FAILURE)
+ {
+ debug("Server refused our key.");
+ continue;
+ }
+
+ /* Otherwise it should have sent a challenge. */
+ if (type != SSH_SMSG_AUTH_RSA_CHALLENGE)
+ packet_disconnect("Protocol error during RSA authentication: %d",
+ type);
+
+ packet_get_bignum(challenge, &clen);
+
+ packet_integrity_check(plen, clen, type);
+
+ debug("Received RSA challenge from server.");
+
+ /* Ask the agent to decrypt the challenge. */
+ if (!ssh_decrypt_challenge(auth, bits, e, n, challenge,
+ session_id, 1, response))
+ {
+ /* The agent failed to authenticate this identifier although it
+ advertised it supports this. Just return a wrong value. */
+ log("Authentication agent failed to decrypt challenge.");
+ memset(response, 0, sizeof(response));
+ }
+
+ debug("Sending response to RSA challenge.");
+
+ /* Send the decrypted challenge back to the server. */
+ packet_start(SSH_CMSG_AUTH_RSA_RESPONSE);
+ for (i = 0; i < 16; i++)
+ packet_put_char(response[i]);
+ packet_send();
+ packet_write_wait();
+
+ /* Wait for response from the server. */
+ type = packet_read(&plen);
+
+ /* The server returns success if it accepted the authentication. */
+ if (type == SSH_SMSG_SUCCESS)
+ {
+ debug("RSA authentication accepted by server.");
+ BN_clear_free(e);
+ BN_clear_free(n);
+ BN_clear_free(challenge);
+ return 1;
+ }
+
+ /* Otherwise it should return failure. */
+ if (type != SSH_SMSG_FAILURE)
+ packet_disconnect("Protocol error waiting RSA auth response: %d",
+ type);
+ }
+
+ BN_clear_free(e);
+ BN_clear_free(n);
+ BN_clear_free(challenge);
+
+ debug("RSA authentication using agent refused.");
+ return 0;
+}
+
+/* Computes the proper response to a RSA challenge, and sends the response to
+ the server. */
+
+void
+respond_to_rsa_challenge(BIGNUM *challenge, RSA *prv)
+{
+ unsigned char buf[32], response[16];
+ MD5_CTX md;
+ int i, len;
+
+ /* Decrypt the challenge using the private key. */
+ rsa_private_decrypt(challenge, challenge, prv);
+
+ /* Compute the response. */
+ /* The response is MD5 of decrypted challenge plus session id. */
+ len = BN_num_bytes(challenge);
+ assert(len <= sizeof(buf) && len);
+ memset(buf, 0, sizeof(buf));
+ BN_bn2bin(challenge, buf + sizeof(buf) - len);
+ MD5_Init(&md);
+ MD5_Update(&md, buf, 32);
+ MD5_Update(&md, session_id, 16);
+ MD5_Final(response, &md);
+
+ debug("Sending response to host key RSA challenge.");
+
+ /* Send the response back to the server. */
+ packet_start(SSH_CMSG_AUTH_RSA_RESPONSE);
+ for (i = 0; i < 16; i++)
+ packet_put_char(response[i]);
+ packet_send();
+ packet_write_wait();
+
+ memset(buf, 0, sizeof(buf));
+ memset(response, 0, sizeof(response));
+ memset(&md, 0, sizeof(md));
+}
+
+/* Checks if the user has authentication file, and if so, tries to authenticate
+ the user using it. */
+
+int
+try_rsa_authentication(struct passwd *pw, const char *authfile,
+ int may_ask_passphrase)
+{
+ BIGNUM *challenge;
+ RSA *private_key;
+ RSA *public_key;
+ char *passphrase, *comment;
+ int type, i;
+ int plen, clen;
+
+ /* Try to load identification for the authentication key. */
+ public_key = RSA_new();
+ if (!load_public_key(authfile, public_key, &comment)) {
+ RSA_free(public_key);
+ return 0; /* Could not load it. Fail. */
+ }
+
+ debug("Trying RSA authentication with key '%.100s'", comment);
+
+ /* Tell the server that we are willing to authenticate using this key. */
+ packet_start(SSH_CMSG_AUTH_RSA);
+ packet_put_bignum(public_key->n);
+ packet_send();
+ packet_write_wait();
+
+ /* We no longer need the public key. */
+ RSA_free(public_key);
+
+ /* Wait for server's response. */
+ type = packet_read(&plen);
+
+ /* The server responds with failure if it doesn\'t like our key or doesn\'t
+ support RSA authentication. */
+ if (type == SSH_SMSG_FAILURE)
+ {
+ debug("Server refused our key.");
+ xfree(comment);
+ return 0; /* Server refuses to authenticate with this key. */
+ }
+
+ /* Otherwise, the server should respond with a challenge. */
+ if (type != SSH_SMSG_AUTH_RSA_CHALLENGE)
+ packet_disconnect("Protocol error during RSA authentication: %d", type);
+
+ /* Get the challenge from the packet. */
+ challenge = BN_new();
+ packet_get_bignum(challenge, &clen);
+
+ packet_integrity_check(plen, clen, type);
+
+ debug("Received RSA challenge from server.");
+
+ private_key = RSA_new();
+ /* Load the private key. Try first with empty passphrase; if it fails,
+ ask for a passphrase. */
+ if (!load_private_key(authfile, "", private_key, NULL))
+ {
+ char buf[300];
+ /* Request passphrase from the user. We read from /dev/tty to make
+ this work even if stdin has been redirected. If running in
+ batch mode, we just use the empty passphrase, which will fail and
+ return. */
+ snprintf(buf, sizeof buf,
+ "Enter passphrase for RSA key '%.100s': ", comment);
+ if (may_ask_passphrase)
+ passphrase = read_passphrase(buf, 0);
+ else
+ {
+ debug("Will not query passphrase for %.100s in batch mode.",
+ comment);
+ passphrase = xstrdup("");
+ }
+
+ /* Load the authentication file using the pasphrase. */
+ if (!load_private_key(authfile, passphrase, private_key, NULL))
+ {
+ memset(passphrase, 0, strlen(passphrase));
+ xfree(passphrase);
+ error("Bad passphrase.");
+
+ /* Send a dummy response packet to avoid protocol error. */
+ packet_start(SSH_CMSG_AUTH_RSA_RESPONSE);
+ for (i = 0; i < 16; i++)
+ packet_put_char(0);
+ packet_send();
+ packet_write_wait();
+
+ /* Expect the server to reject it... */
+ packet_read_expect(&plen, SSH_SMSG_FAILURE);
+ xfree(comment);
+ return 0;
+ }
+
+ /* Destroy the passphrase. */
+ memset(passphrase, 0, strlen(passphrase));
+ xfree(passphrase);
+ }
+
+ /* We no longer need the comment. */
+ xfree(comment);
+
+ /* Compute and send a response to the challenge. */
+ respond_to_rsa_challenge(challenge, private_key);
+
+ /* Destroy the private key. */
+ RSA_free(private_key);
+
+ /* We no longer need the challenge. */
+ BN_clear_free(challenge);
+
+ /* Wait for response from the server. */
+ type = packet_read(&plen);
+ if (type == SSH_SMSG_SUCCESS)
+ {
+ debug("RSA authentication accepted by server.");
+ return 1;
+ }
+ if (type != SSH_SMSG_FAILURE)
+ packet_disconnect("Protocol error waiting RSA auth response: %d", type);
+ debug("RSA authentication refused.");
+ return 0;
+}
+
+/* Tries to authenticate the user using combined rhosts or /etc/hosts.equiv
+ authentication and RSA host authentication. */
+
+int
+try_rhosts_rsa_authentication(const char *local_user, RSA *host_key)
+{
+ int type;
+ BIGNUM *challenge;
+ int plen, clen;
+
+ debug("Trying rhosts or /etc/hosts.equiv with RSA host authentication.");
+
+ /* Tell the server that we are willing to authenticate using this key. */
+ packet_start(SSH_CMSG_AUTH_RHOSTS_RSA);
+ packet_put_string(local_user, strlen(local_user));
+ packet_put_int(BN_num_bits(host_key->n));
+ packet_put_bignum(host_key->e);
+ packet_put_bignum(host_key->n);
+ packet_send();
+ packet_write_wait();
+
+ /* Wait for server's response. */
+ type = packet_read(&plen);
+
+ /* The server responds with failure if it doesn't admit our .rhosts
+ authentication or doesn't know our host key. */
+ if (type == SSH_SMSG_FAILURE)
+ {
+ debug("Server refused our rhosts authentication or host key.");
+ return 0; /* Server refuses to authenticate us with this method. */
+ }
+
+ /* Otherwise, the server should respond with a challenge. */
+ if (type != SSH_SMSG_AUTH_RSA_CHALLENGE)
+ packet_disconnect("Protocol error during RSA authentication: %d", type);
+
+ /* Get the challenge from the packet. */
+ challenge = BN_new();
+ packet_get_bignum(challenge, &clen);
+
+ packet_integrity_check(plen, clen, type);
+
+ debug("Received RSA challenge for host key from server.");
+
+ /* Compute a response to the challenge. */
+ respond_to_rsa_challenge(challenge, host_key);
+
+ /* We no longer need the challenge. */
+ BN_clear_free(challenge);
+
+ /* Wait for response from the server. */
+ type = packet_read(&plen);
+ if (type == SSH_SMSG_SUCCESS)
+ {
+ debug("Rhosts or /etc/hosts.equiv with RSA host authentication accepted by server.");
+ return 1;
+ }
+ if (type != SSH_SMSG_FAILURE)
+ packet_disconnect("Protocol error waiting RSA auth response: %d", type);
+ debug("Rhosts or /etc/hosts.equiv with RSA host authentication refused.");
+ return 0;
+}
+
+#ifdef KRB4
+int try_kerberos_authentication()
+{
+ KTEXT_ST auth; /* Kerberos data */
+ char *reply;
+ char inst[INST_SZ];
+ char *realm;
+ CREDENTIALS cred;
+ int r, type, plen;
+ Key_schedule schedule;
+ u_long checksum, cksum;
+ MSG_DAT msg_data;
+ struct sockaddr_in local, foreign;
+ struct stat st;
+
+ /* Don't do anything if we don't have any tickets. */
+ if (stat(tkt_string(), &st) < 0) return 0;
+
+ strncpy(inst, (char *) krb_get_phost(get_canonical_hostname()), INST_SZ);
+
+ realm = (char *)krb_realmofhost(get_canonical_hostname());
+ if (!realm) {
+ debug("Kerberos V4: no realm for %s", get_canonical_hostname());
+ return 0;
+ }
+ /* This can really be anything. */
+ checksum = (u_long) getpid();
+
+ r = krb_mk_req(&auth, KRB4_SERVICE_NAME, inst, realm, checksum);
+ if (r != KSUCCESS) {
+ debug("Kerberos V4 krb_mk_req failed: %s", krb_err_txt[r]);
+ return 0;
+ }
+ /* Get session key to decrypt the server's reply with. */
+ r = krb_get_cred(KRB4_SERVICE_NAME, inst, realm, &cred);
+ if (r != KSUCCESS) {
+ debug("get_cred failed: %s", krb_err_txt[r]);
+ return 0;
+ }
+ des_key_sched((des_cblock *)cred.session, schedule);
+
+ /* Send authentication info to server. */
+ packet_start(SSH_CMSG_AUTH_KERBEROS);
+ packet_put_string((char *)auth.dat, auth.length);
+ packet_send();
+ packet_write_wait();
+
+ /* Zero the buffer. */
+ (void) memset(auth.dat, 0, MAX_KTXT_LEN);
+
+ r = sizeof(local);
+ memset(&local, 0, sizeof(local));
+ if (getsockname(packet_get_connection_in(),
+ (struct sockaddr *) &local, &r) < 0)
+ debug("getsockname failed: %s", strerror(errno));
+
+ r = sizeof(foreign);
+ memset(&foreign, 0, sizeof(foreign));
+ if (getpeername(packet_get_connection_in(),
+ (struct sockaddr *)&foreign, &r) < 0)
+ debug("getpeername failed: %s", strerror(errno));
+
+ /* Get server reply. */
+ type = packet_read(&plen);
+ switch(type) {
+
+ case SSH_SMSG_FAILURE: /* Should really be SSH_SMSG_AUTH_KERBEROS_FAILURE */
+ debug("Kerberos V4 authentication failed.");
+ return 0;
+ break;
+
+ case SSH_SMSG_AUTH_KERBEROS_RESPONSE: /* SSH_SMSG_AUTH_KERBEROS_SUCCESS */
+ debug("Kerberos V4 authentication accepted.");
+
+ /* Get server's response. */
+ reply = packet_get_string((unsigned int *)&auth.length);
+ memcpy(auth.dat, reply, auth.length);
+ xfree(reply);
+
+ packet_integrity_check(plen, 4 + auth.length, type);
+
+ /* If his response isn't properly encrypted with the session key,
+ and the decrypted checksum fails to match, he's bogus. Bail out. */
+ r = krb_rd_priv(auth.dat, auth.length, schedule, &cred.session,
+ &foreign, &local, &msg_data);
+ if (r != KSUCCESS) {
+ debug("Kerberos V4 krb_rd_priv failed: %s", krb_err_txt[r]);
+ packet_disconnect("Kerberos V4 challenge failed!");
+ }
+ /* Fetch the (incremented) checksum that we supplied in the request. */
+ (void)memcpy((char *)&cksum, (char *)msg_data.app_data, sizeof(cksum));
+ cksum = ntohl(cksum);
+
+ /* If it matches, we're golden. */
+ if (cksum == checksum + 1) {
+ debug("Kerberos V4 challenge successful.");
+ return 1;
+ }
+ else
+ packet_disconnect("Kerberos V4 challenge failed!");
+ break;
+
+ default:
+ packet_disconnect("Protocol error on Kerberos V4 response: %d", type);
+ }
+ return 0;
+}
+#endif /* KRB4 */
+
+#ifdef AFS
+int send_kerberos_tgt()
+{
+ CREDENTIALS *creds;
+ char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ];
+ int r, type, plen;
+ unsigned char buffer[8192];
+ struct stat st;
+
+ /* Don't do anything if we don't have any tickets. */
+ if (stat(tkt_string(), &st) < 0) return 0;
+
+ creds = xmalloc(sizeof(*creds));
+
+ if ((r = krb_get_tf_fullname(TKT_FILE, pname, pinst, prealm)) != KSUCCESS) {
+ debug("Kerberos V4 tf_fullname failed: %s",krb_err_txt[r]);
+ return 0;
+ }
+ if ((r = krb_get_cred("krbtgt", prealm, prealm, creds)) != GC_OK) {
+ debug("Kerberos V4 get_cred failed: %s", krb_err_txt[r]);
+ return 0;
+ }
+ if (time(0) > krb_life_to_time(creds->issue_date, creds->lifetime)) {
+ debug("Kerberos V4 ticket expired: %s", TKT_FILE);
+ return 0;
+ }
+
+ creds_to_radix(creds, buffer);
+ xfree(creds);
+
+ packet_start(SSH_CMSG_HAVE_KERBEROS_TGT);
+ packet_put_string((char *)buffer, strlen(buffer));
+ packet_send();
+ packet_write_wait();
+
+ type = packet_read(&plen);
+
+ if (type == SSH_SMSG_FAILURE)
+ debug("Kerberos TGT for realm %s rejected.", prealm);
+ else if (type != SSH_SMSG_SUCCESS)
+ packet_disconnect("Protocol error on Kerberos TGT response: %d", type);
+
+ return 1;
+}
+
+void send_afs_tokens(void)
+{
+ CREDENTIALS creds;
+ struct ViceIoctl parms;
+ struct ClearToken ct;
+ int i, type, len, plen;
+ char buf[2048], *p, *server_cell;
+ unsigned char buffer[8192];
+
+ /* Move over ktc_GetToken, here's something leaner. */
+ for (i = 0; i < 100; i++) { /* just in case */
+ parms.in = (char *)&i;
+ parms.in_size = sizeof(i);
+ parms.out = buf;
+ parms.out_size = sizeof(buf);
+ if (k_pioctl(0, VIOCGETTOK, &parms, 0) != 0) break;
+ p = buf;
+
+ /* Get secret token. */
+ memcpy(&creds.ticket_st.length, p, sizeof(unsigned int));
+ if (creds.ticket_st.length > MAX_KTXT_LEN) break;
+ p += sizeof(unsigned int);
+ memcpy(creds.ticket_st.dat, p, creds.ticket_st.length);
+ p += creds.ticket_st.length;
+
+ /* Get clear token. */
+ memcpy(&len, p, sizeof(len));
+ if (len != sizeof(struct ClearToken)) break;
+ p += sizeof(len);
+ memcpy(&ct, p, len);
+ p += len;
+ p += sizeof(len); /* primary flag */
+ server_cell = p;
+
+ /* Flesh out our credentials. */
+ strlcpy(creds.service, "afs", sizeof creds.service);
+ creds.instance[0] = '\0';
+ strlcpy(creds.realm, server_cell, REALM_SZ);
+ memcpy(creds.session, ct.HandShakeKey, DES_KEY_SZ);
+ creds.issue_date = ct.BeginTimestamp;
+ creds.lifetime = krb_time_to_life(creds.issue_date, ct.EndTimestamp);
+ creds.kvno = ct.AuthHandle;
+ snprintf(creds.pname, sizeof(creds.pname), "AFS ID %d", ct.ViceId);
+ creds.pinst[0] = '\0';
+
+ /* Encode token, ship it off. */
+ if (!creds_to_radix(&creds, buffer)) break;
+ packet_start(SSH_CMSG_HAVE_AFS_TOKEN);
+ packet_put_string((char *)buffer, strlen(buffer));
+ packet_send();
+ packet_write_wait();
+
+ /* Roger, Roger. Clearance, Clarence. What's your vector, Victor? */
+ type = packet_read(&plen);
+
+ if (type == SSH_SMSG_FAILURE)
+ debug("AFS token for cell %s rejected.", server_cell);
+ else if (type != SSH_SMSG_SUCCESS)
+ packet_disconnect("Protocol error on AFS token response: %d", type);
+ }
+}
+#endif /* AFS */
+
+/* Waits for the server identification string, and sends our own identification
+ string. */
+
+void ssh_exchange_identification()
+{
+ char buf[256], remote_version[256]; /* must be same size! */
+ int remote_major, remote_minor, i;
+ int connection_in = packet_get_connection_in();
+ int connection_out = packet_get_connection_out();
+ extern Options options;
+
+ /* Read other side\'s version identification. */
+ for (i = 0; i < sizeof(buf) - 1; i++)
+ {
+ if (read(connection_in, &buf[i], 1) != 1)
+ fatal("read: %.100s", strerror(errno));
+ if (buf[i] == '\r')
+ {
+ buf[i] = '\n';
+ buf[i + 1] = 0;
+ break;
+ }
+ if (buf[i] == '\n')
+ {
+ buf[i + 1] = 0;
+ break;
+ }
+ }
+ buf[sizeof(buf) - 1] = 0;
+
+ /* Check that the versions match. In future this might accept several
+ versions and set appropriate flags to handle them. */
+ if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor,
+ remote_version) != 3)
+ fatal("Bad remote protocol version identification: '%.100s'", buf);
+ debug("Remote protocol version %d.%d, remote software version %.100s",
+ remote_major, remote_minor, remote_version);
+
+ /* Check if the remote protocol version is too old. */
+ if (remote_major == 1 && remote_minor < 3)
+ fatal("Remote machine has too old SSH software version.");
+
+ /* We speak 1.3, too. */
+ if (remote_major == 1 && remote_minor == 3) {
+ enable_compat13();
+ if (options.forward_agent && strcmp(remote_version, SSH_VERSION) != 0) {
+ log("Agent forwarding disabled, remote version '%s' is not compatible.",
+ remote_version);
+ options.forward_agent = 0;
+ }
+ }
+#if 0
+ /* Removed for now, to permit compatibility with latter versions. The server
+ will reject our version and disconnect if it doesn't support it. */
+ if (remote_major != PROTOCOL_MAJOR)
+ fatal("Protocol major versions differ: %d vs. %d",
+ PROTOCOL_MAJOR, remote_major);
+#endif
+
+ /* Send our own protocol version identification. */
+ snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n",
+ PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION);
+ if (write(connection_out, buf, strlen(buf)) != strlen(buf))
+ fatal("write: %.100s", strerror(errno));
+}
+
+int ssh_cipher_default = SSH_CIPHER_3DES;
+
+int read_yes_or_no(const char *prompt, int defval)
+{
+ char buf[1024];
+ FILE *f;
+ int retval = -1;
+
+ if (isatty(0))
+ f = stdin;
+ else
+ f = fopen("/dev/tty", "rw");
+
+ if (f == NULL)
+ return 0;
+
+ fflush(stdout);
+
+ while (1)
+ {
+ fprintf(stderr, "%s", prompt);
+ if (fgets(buf, sizeof(buf), f) == NULL)
+ {
+ /* Print a newline (the prompt probably didn\'t have one). */
+ fprintf(stderr, "\n");
+ strlcpy(buf, "no", sizeof buf);
+ }
+ /* Remove newline from response. */
+ if (strchr(buf, '\n'))
+ *strchr(buf, '\n') = 0;
+
+ if (buf[0] == 0)
+ retval = defval;
+ if (strcmp(buf, "yes") == 0)
+ retval = 1;
+ if (strcmp(buf, "no") == 0)
+ retval = 0;
+
+ if (retval != -1)
+ {
+ if (f != stdin)
+ fclose(f);
+ return retval;
+ }
+ }
+}
+
+/* Starts a dialog with the server, and authenticates the current user on the
+ server. This does not need any extra privileges. The basic connection
+ to the server must already have been established before this is called.
+ User is the remote user; if it is NULL, the current local user name will
+ be used. Anonymous indicates that no rhosts authentication will be used.
+ If login fails, this function prints an error and never returns.
+ This function does not require super-user privileges. */
+
+void ssh_login(int host_key_valid,
+ RSA *own_host_key,
+ const char *orighost,
+ struct sockaddr_in *hostaddr,
+ Options *options, uid_t original_real_uid)
+{
+ int i, type;
+ char *password;
+ struct passwd *pw;
+ BIGNUM *key;
+ RSA *host_key, *file_key;
+ RSA *public_key;
+ unsigned char session_key[SSH_SESSION_KEY_LENGTH];
+ const char *server_user, *local_user;
+ char *cp, *host, *ip = NULL;
+ unsigned char check_bytes[8];
+ unsigned int supported_ciphers, supported_authentications, protocol_flags;
+ HostStatus host_status;
+ HostStatus ip_status;
+ int host_ip_differ = 0;
+ int local = (ntohl(hostaddr->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET;
+ int payload_len, clen, sum_len = 0;
+ u_int32_t rand = 0;
+
+ if (options->check_host_ip)
+ ip = xstrdup(inet_ntoa(hostaddr->sin_addr));
+
+ /* Convert the user-supplied hostname into all lowercase. */
+ host = xstrdup(orighost);
+ for (cp = host; *cp; cp++)
+ if (isupper(*cp))
+ *cp = tolower(*cp);
+
+ /* Exchange protocol version identification strings with the server. */
+ ssh_exchange_identification();
+
+ /* Put the connection into non-blocking mode. */
+ packet_set_nonblocking();
+
+ /* Get local user name. Use it as server user if no user name
+ was given. */
+ pw = getpwuid(original_real_uid);
+ if (!pw)
+ fatal("User id %d not found from user database.", original_real_uid);
+ local_user = xstrdup(pw->pw_name);
+ server_user = options->user ? options->user : local_user;
+
+ debug("Waiting for server public key.");
+
+ /* Wait for a public key packet from the server. */
+ packet_read_expect(&payload_len, SSH_SMSG_PUBLIC_KEY);
+
+ /* Get check bytes from the packet. */
+ for (i = 0; i < 8; i++)
+ check_bytes[i] = packet_get_char();
+
+ /* Get the public key. */
+ public_key = RSA_new();
+ packet_get_int(); /* bits */
+ public_key->e = BN_new();
+ packet_get_bignum(public_key->e, &clen);
+ sum_len += clen;
+ public_key->n = BN_new();
+ packet_get_bignum(public_key->n, &clen);
+ sum_len += clen;
+
+ /* Get the host key. */
+ host_key = RSA_new();
+ packet_get_int(); /* bits */
+ host_key->e = BN_new();
+ packet_get_bignum(host_key->e, &clen);
+ sum_len += clen;
+ host_key->n = BN_new();
+ packet_get_bignum(host_key->n, &clen);
+ sum_len += clen;
+
+ /* Store the host key from the known host file in here
+ * so that we can compare it with the key for the IP
+ * address. */
+ file_key = RSA_new();
+ file_key->n = BN_new();
+ file_key->e = BN_new();
+
+ /* Get protocol flags. */
+ protocol_flags = packet_get_int();
+ packet_set_protocol_flags(protocol_flags);
+
+ /* Get supported cipher types. */
+ supported_ciphers = packet_get_int();
+
+ /* Get supported authentication types. */
+ supported_authentications = packet_get_int();
+
+ debug("Received server public key (%d bits) and host key (%d bits).",
+ BN_num_bits(public_key->n), BN_num_bits(host_key->n));
+
+ packet_integrity_check(payload_len,
+ 8 + 4 + sum_len + 0 + 4 + 0 + 0 + 4 + 4 + 4,
+ SSH_SMSG_PUBLIC_KEY);
+
+ /* Compute the session id. */
+ compute_session_id(session_id, check_bytes,
+ BN_num_bits(host_key->n), host_key->n,
+ BN_num_bits(public_key->n), public_key->n);
+
+ /* Check if the host key is present in the user\'s list of known hosts
+ or in the systemwide list. */
+ host_status = check_host_in_hostfile(options->user_hostfile,
+ host, BN_num_bits(host_key->n),
+ host_key->e, host_key->n,
+ file_key->e, file_key->n);
+ if (host_status == HOST_NEW)
+ host_status = check_host_in_hostfile(options->system_hostfile, host,
+ BN_num_bits(host_key->n),
+ host_key->e, host_key->n,
+ file_key->e, file_key->n);
+ /* Force accepting of the host key for localhost and 127.0.0.1.
+ The problem is that if the home directory is NFS-mounted to multiple
+ machines, localhost will refer to a different machine in each of them,
+ and the user will get bogus HOST_CHANGED warnings. This essentially
+ disables host authentication for localhost; however, this is probably
+ not a real problem. */
+ if (local) {
+ debug("Forcing accepting of host key for localhost.");
+ host_status = HOST_OK;
+ }
+
+ /* Also perform check for the ip address, skip the check if we are
+ localhost or the hostname was an ip address to begin with */
+ if (options->check_host_ip && !local && strcmp(host, ip)) {
+ RSA *ip_key = RSA_new();
+ ip_key->n = BN_new();
+ ip_key->e = BN_new();
+ ip_status = check_host_in_hostfile(options->user_hostfile, ip,
+ BN_num_bits(host_key->n),
+ host_key->e, host_key->n,
+ ip_key->e, ip_key->n);
+
+ if (ip_status == HOST_NEW)
+ ip_status = check_host_in_hostfile(options->system_hostfile, ip,
+ BN_num_bits(host_key->n),
+ host_key->e, host_key->n,
+ ip_key->e, ip_key->n);
+ if (host_status == HOST_CHANGED &&
+ (ip_status != HOST_CHANGED ||
+ (BN_cmp(ip_key->e, file_key->e) || BN_cmp(ip_key->n, file_key->n))))
+ host_ip_differ = 1;
+
+ RSA_free(ip_key);
+ } else
+ ip_status = host_status;
+
+ RSA_free(file_key);
+
+ switch (host_status) {
+ case HOST_OK:
+ /* The host is known and the key matches. */
+ debug("Host '%.200s' is known and matches the host key.", host);
+ if (options->check_host_ip) {
+ if (ip_status == HOST_NEW) {
+ if (!add_host_to_hostfile(options->user_hostfile, ip,
+ BN_num_bits(host_key->n),
+ host_key->e, host_key->n))
+ log("Failed to add the host ip to the list of known hosts (%.30s).",
+ options->user_hostfile);
+ else
+ log("Warning: Permanently added host ip '%.30s' to the list of known hosts.", ip);
+ } else if (ip_status != HOST_OK)
+ log("Warning: the host key differ from the key of the ip address '%.30s' differs", ip);
+ }
+
+ break;
+ case HOST_NEW:
+ {
+ char hostline[1000], *hostp = hostline;
+ /* The host is new. */
+ if (options->strict_host_key_checking == 1) {
+ /* User has requested strict host key checking. We will not
+ add the host key automatically. The only alternative left
+ is to abort. */
+ fatal("No host key is known for %.200s and you have requested strict checking.", host);
+ } else if (options->strict_host_key_checking == 2) { /* The default */
+ char prompt[1024];
+ snprintf(prompt, sizeof(prompt),
+ "The authenticity of host '%.200s' can't be established.\n"
+ "Are you sure you want to continue connecting (yes/no)? ",
+ host);
+ if (!read_yes_or_no(prompt, -1))
+ fatal("Aborted by user!\n");
+ }
+
+ if (options->check_host_ip && ip_status == HOST_NEW && strcmp(host, ip))
+ snprintf(hostline, sizeof(hostline), "%s,%s", host, ip);
+ else
+ hostp = host;
+
+ /* If not in strict mode, add the key automatically to the local
+ known_hosts file. */
+ if (!add_host_to_hostfile(options->user_hostfile, hostp,
+ BN_num_bits(host_key->n),
+ host_key->e, host_key->n))
+ log("Failed to add the host to the list of known hosts (%.500s).",
+ options->user_hostfile);
+ else
+ log("Warning: Permanently added '%.200s' to the list of known hosts.",
+ hostp);
+ break;
+ }
+ case HOST_CHANGED:
+ if (options->check_host_ip) {
+ if (host_ip_differ) {
+ error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
+ error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @");
+ error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
+ error("The host key for %s has changed,", host);
+ error("but the key for the according IP address %s has", ip);
+ error("a different status. This could either mean that DNS");
+ error("SPOOFING is happening or the IP address for the host");
+ error("and its host key have changed at the same time");
+ }
+ }
+
+ /* The host key has changed. */
+ error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
+ error("@ WARNING: HOST IDENTIFICATION HAS CHANGED! @");
+ error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
+ error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!");
+ error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!");
+ error("It is also possible that the host key has just been changed.");
+ error("Please contact your system administrator.");
+ error("Add correct host key in %.100s to get rid of this message.",
+ options->user_hostfile);
+
+ /* If strict host key checking is in use, the user will have to edit
+ the key manually and we can only abort. */
+ if (options->strict_host_key_checking)
+ fatal("Host key for %.200s has changed and you have requested strict checking.", host);
+
+ /* If strict host key checking has not been requested, allow the
+ connection but without password authentication or
+ agent forwarding. */
+ if (options->password_authentication) {
+ error("Password authentication is disabled to avoid trojan horses.");
+ options->password_authentication = 0;
+ }
+ if (options->forward_agent) {
+ error("Agent forwarding is disabled to avoid trojan horses.");
+ options->forward_agent = 0;
+ }
+ /* XXX Should permit the user to change to use the new id. This could
+ be done by converting the host key to an identifying sentence, tell
+ that the host identifies itself by that sentence, and ask the user
+ if he/she whishes to accept the authentication. */
+ break;
+ }
+
+ if (options->check_host_ip)
+ xfree(ip);
+
+ /* Generate a session key. */
+ arc4random_stir();
+
+ /* Generate an encryption key for the session. The key is a 256 bit
+ random number, interpreted as a 32-byte key, with the least significant
+ 8 bits being the first byte of the key. */
+ for (i = 0; i < 32; i++) {
+ if (i % 4 == 0)
+ rand = arc4random();
+ session_key[i] = rand & 0xff;
+ rand >>= 8;
+ }
+
+ /* According to the protocol spec, the first byte of the session key is
+ the highest byte of the integer. The session key is xored with the
+ first 16 bytes of the session id. */
+ key = BN_new();
+ BN_set_word(key, 0);
+ for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++)
+ {
+ BN_lshift(key, key, 8);
+ if (i < 16)
+ BN_add_word(key, session_key[i] ^ session_id[i]);
+ else
+ BN_add_word(key, session_key[i]);
+ }
+
+ /* Encrypt the integer using the public key and host key of the server
+ (key with smaller modulus first). */
+ if (BN_cmp(public_key->n, host_key->n) < 0)
+ {
+ /* Public key has smaller modulus. */
+ assert(BN_num_bits(host_key->n) >=
+ BN_num_bits(public_key->n) + SSH_KEY_BITS_RESERVED);
+
+ rsa_public_encrypt(key, key, public_key);
+ rsa_public_encrypt(key, key, host_key);
+ }
+ else
+ {
+ /* Host key has smaller modulus (or they are equal). */
+ assert(BN_num_bits(public_key->n) >=
+ BN_num_bits(host_key->n) + SSH_KEY_BITS_RESERVED);
+
+ rsa_public_encrypt(key, key, host_key);
+ rsa_public_encrypt(key, key, public_key);
+ }
+
+ if (options->cipher == SSH_CIPHER_NOT_SET) {
+ if (cipher_mask() & supported_ciphers & (1 << ssh_cipher_default))
+ options->cipher = ssh_cipher_default;
+ else {
+ debug("Cipher %d not supported, using %.100s instead.",
+ cipher_name(ssh_cipher_default),
+ cipher_name(SSH_FALLBACK_CIPHER));
+ options->cipher = SSH_FALLBACK_CIPHER;
+ }
+ }
+
+ /* Check that the selected cipher is supported. */
+ if (!(supported_ciphers & (1 << options->cipher)))
+ fatal("Selected cipher type %.100s not supported by server.",
+ cipher_name(options->cipher));
+
+ debug("Encryption type: %.100s", cipher_name(options->cipher));
+
+ /* Send the encrypted session key to the server. */
+ packet_start(SSH_CMSG_SESSION_KEY);
+ packet_put_char(options->cipher);
+
+ /* Send the check bytes back to the server. */
+ for (i = 0; i < 8; i++)
+ packet_put_char(check_bytes[i]);
+
+ /* Send the encrypted encryption key. */
+ packet_put_bignum(key);
+
+ /* Send protocol flags. */
+ packet_put_int(SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN);
+
+ /* Send the packet now. */
+ packet_send();
+ packet_write_wait();
+
+ /* Destroy the session key integer and the public keys since we no longer
+ need them. */
+ BN_clear_free(key);
+ RSA_free(public_key);
+ RSA_free(host_key);
+
+ debug("Sent encrypted session key.");
+
+ /* Set the encryption key. */
+ packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH,
+ options->cipher, 1);
+
+ /* We will no longer need the session key here. Destroy any extra copies. */
+ memset(session_key, 0, sizeof(session_key));
+
+ /* Expect a success message from the server. Note that this message will
+ be received in encrypted form. */
+ packet_read_expect(&payload_len, SSH_SMSG_SUCCESS);
+
+ debug("Received encrypted confirmation.");
+
+ /* Send the name of the user to log in as on the server. */
+ packet_start(SSH_CMSG_USER);
+ packet_put_string(server_user, strlen(server_user));
+ packet_send();
+ packet_write_wait();
+
+ /* The server should respond with success if no authentication is needed
+ (the user has no password). Otherwise the server responds with
+ failure. */
+ type = packet_read(&payload_len);
+ if (type == SSH_SMSG_SUCCESS)
+ return; /* Connection was accepted without authentication. */
+ if (type != SSH_SMSG_FAILURE)
+ packet_disconnect("Protocol error: got %d in response to SSH_CMSG_USER",
+ type);
+
+#ifdef AFS
+ /* Try Kerberos tgt passing if the server supports it. */
+ if ((supported_authentications & (1 << SSH_PASS_KERBEROS_TGT)) &&
+ options->kerberos_tgt_passing)
+ {
+ if (options->cipher == SSH_CIPHER_NONE)
+ log("WARNING: Encryption is disabled! Ticket will be transmitted in the clear!");
+ (void)send_kerberos_tgt();
+ }
+
+ /* Try AFS token passing if the server supports it. */
+ if ((supported_authentications & (1 << SSH_PASS_AFS_TOKEN)) &&
+ options->afs_token_passing && k_hasafs()) {
+ if (options->cipher == SSH_CIPHER_NONE)
+ log("WARNING: Encryption is disabled! Token will be transmitted in the clear!");
+ send_afs_tokens();
+ }
+#endif /* AFS */
+
+#ifdef KRB4
+ if ((supported_authentications & (1 << SSH_AUTH_KERBEROS)) &&
+ options->kerberos_authentication)
+ {
+ debug("Trying Kerberos authentication.");
+ if (try_kerberos_authentication()) {
+ /* The server should respond with success or failure. */
+ type = packet_read(&payload_len);
+ if (type == SSH_SMSG_SUCCESS)
+ return; /* Successful connection. */
+ if (type != SSH_SMSG_FAILURE)
+ packet_disconnect("Protocol error: got %d in response to Kerberos auth", type);
+ }
+ }
+#endif /* KRB4 */
+
+ /* Use rhosts authentication if running in privileged socket and we do not
+ wish to remain anonymous. */
+ if ((supported_authentications & (1 << SSH_AUTH_RHOSTS)) &&
+ options->rhosts_authentication)
+ {
+ debug("Trying rhosts authentication.");
+ packet_start(SSH_CMSG_AUTH_RHOSTS);
+ packet_put_string(local_user, strlen(local_user));
+ packet_send();
+ packet_write_wait();
+
+ /* The server should respond with success or failure. */
+ type = packet_read(&payload_len);
+ if (type == SSH_SMSG_SUCCESS)
+ return; /* Successful connection. */
+ if (type != SSH_SMSG_FAILURE)
+ packet_disconnect("Protocol error: got %d in response to rhosts auth",
+ type);
+ }
+
+ /* Try .rhosts or /etc/hosts.equiv authentication with RSA host
+ authentication. */
+ if ((supported_authentications & (1 << SSH_AUTH_RHOSTS_RSA)) &&
+ options->rhosts_rsa_authentication && host_key_valid)
+ {
+ if (try_rhosts_rsa_authentication(local_user, own_host_key))
+ return; /* Successful authentication. */
+ }
+
+ /* Try RSA authentication if the server supports it. */
+ if ((supported_authentications & (1 << SSH_AUTH_RSA)) &&
+ options->rsa_authentication)
+ {
+ /* Try RSA authentication using the authentication agent. The agent
+ is tried first because no passphrase is needed for it, whereas
+ identity files may require passphrases. */
+ if (try_agent_authentication())
+ return; /* Successful connection. */
+
+ /* Try RSA authentication for each identity. */
+ for (i = 0; i < options->num_identity_files; i++)
+ if (try_rsa_authentication(pw, options->identity_files[i],
+ !options->batch_mode))
+ return; /* Successful connection. */
+ }
+
+ /* Try password authentication if the server supports it. */
+ if ((supported_authentications & (1 << SSH_AUTH_PASSWORD)) &&
+ options->password_authentication && !options->batch_mode)
+ {
+ char prompt[80];
+ snprintf(prompt, sizeof(prompt), "%.30s@%.30s's password: ",
+ server_user, host);
+ debug("Doing password authentication.");
+ if (options->cipher == SSH_CIPHER_NONE)
+ log("WARNING: Encryption is disabled! Password will be transmitted in clear text.");
+ for (i = 0; i < options->number_of_password_prompts; i++) {
+ if (i != 0)
+ error("Permission denied, please try again.");
+ password = read_passphrase(prompt, 0);
+ packet_start(SSH_CMSG_AUTH_PASSWORD);
+ packet_put_string(password, strlen(password));
+ memset(password, 0, strlen(password));
+ xfree(password);
+ packet_send();
+ packet_write_wait();
+
+ type = packet_read(&payload_len);
+ if (type == SSH_SMSG_SUCCESS)
+ return; /* Successful connection. */
+ if (type != SSH_SMSG_FAILURE)
+ packet_disconnect("Protocol error: got %d in response to passwd auth", type);
+ }
+ }
+
+ /* All authentication methods have failed. Exit with an error message. */
+ fatal("Permission denied.");
+ /*NOTREACHED*/
+}
diff --git a/sshd.8 b/sshd.8
new file mode 100644
index 00000000..981c5ff7
--- /dev/null
+++ b/sshd.8
@@ -0,0 +1,781 @@
+.\" -*- nroff -*-
+.\"
+.\" sshd.8.in
+.\"
+.\" Author: Tatu Ylonen <ylo@cs.hut.fi>
+.\"
+.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+.\" All rights reserved
+.\"
+.\" Created: Sat Apr 22 21:55:14 1995 ylo
+.\"
+.\" $Id: sshd.8,v 1.1 1999/10/27 03:42:46 damien Exp $
+.\"
+.Dd September 25, 1999
+.Dt SSHD 8
+.Os
+.Sh NAME
+.Nm sshd
+.Nd secure shell daemon
+.Sh SYNOPSIS
+.Nm sshd
+.Op Fl diq
+.Op Fl b Ar bits
+.Op Fl f Ar config_file
+.Op Fl g Ar login_grace_time
+.Op Fl h Ar host_key_file
+.Op Fl k Ar key_gen_time
+.Op Fl p Ar port
+.Sh DESCRIPTION
+.Nm
+(Secure Shell Daemon) is the daemon program for
+.Xr ssh 1 .
+Together these programs replace rlogin and rsh programs, and
+provide secure encrypted communications between two untrusted hosts
+over an insecure network. The programs are intended to be as easy to
+install and use as possible.
+.Pp
+.Nm
+is the daemon that listens for connections from clients. It is
+normally started at boot from
+.Pa /etc/rc .
+It forks a new
+daemon for each incoming connection. The forked daemons handle
+key exchange, encryption, authentication, command execution,
+and data exchange.
+.Pp
+.Nm
+works as follows. Each host has a host-specific RSA key
+(normally 1024 bits) used to identify the host. Additionally, when
+the daemon starts, it generates a server RSA key (normally 768 bits).
+This key is normally regenerated every hour if it has been used, and
+is never stored on disk.
+.Pp
+Whenever a client connects the daemon, the daemon sends its host
+and server public keys to the client. The client compares the
+host key against its own database to verify that it has not changed.
+The client then generates a 256 bit random number. It encrypts this
+random number using both the host key and the server key, and sends
+the encrypted number to the server. Both sides then start to use this
+random number as a session key which is used to encrypt all further
+communications in the session. The rest of the session is encrypted
+using a conventional cipher, currently Blowfish and 3DES, with 3DES
+being is used by default. The client selects the encryption algorithm
+to use from those offered by the server.
+.Pp
+Next, the server and the client enter an authentication dialog. The
+client tries to authenticate itself using
+.Pa .rhosts
+authentication,
+.Pa .rhosts
+authentication combined with RSA host
+authentication, RSA challenge-response authentication, or password
+based authentication.
+.Pp
+Rhosts authentication is normally disabled
+because it is fundamentally insecure, but can be enabled in the server
+configuration file if desired. System security is not improved unless
+.Xr rshd 8 ,
+.Xr rlogind 8 ,
+.Xr rexecd 8 ,
+and
+.Xr rexd 8
+are disabled (thus completely disabling
+.Xr rlogin 1
+and
+.Xr rsh 1
+into that machine).
+.Pp
+If the client successfully authenticates itself, a dialog for
+preparing the session is entered. At this time the client may request
+things like allocating a pseudo-tty, forwarding X11 connections,
+forwarding TCP/IP connections, or forwarding the authentication agent
+connection over the secure channel.
+.Pp
+Finally, the client either requests a shell or execution of a command.
+The sides then enter session mode. In this mode, either side may send
+data at any time, and such data is forwarded to/from the shell or
+command on the server side, and the user terminal in the client side.
+.Pp
+When the user program terminates and all forwarded X11 and other
+connections have been closed, the server sends command exit status to
+the client, and both sides exit.
+.Pp
+.Nm
+can be configured using command-line options or a configuration
+file. Command-line options override values specified in the
+configuration file.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl b Ar bits
+Specifies the number of bits in the server key (default 768).
+.Pp
+.It Fl d
+Debug mode. The server sends verbose debug output to the system
+log, and does not put itself in the background. The server also will
+not fork and will only process one connection. This option is only
+intended for debugging for the server.
+.It Fl f Ar configuration_file
+Specifies the name of the configuration file. The default is
+.Pa /etc/sshd_config .
+.Nm
+refuses to start if there is no configuration file.
+.It Fl g Ar login_grace_time
+Gives the grace time for clients to authenticate themselves (default
+300 seconds). If the client fails to authenticate the user within
+this many seconds, the server disconnects and exits. A value of zero
+indicates no limit.
+.It Fl h Ar host_key_file
+Specifies the file from which the host key is read (default
+.Pa /etc/ssh_host_key ) .
+This option must be given if
+.Nm
+is not run as root (as the normal
+host file is normally not readable by anyone but root).
+.It Fl i
+Specifies that
+.Nm
+is being run from inetd.
+.Nm
+is normally not run
+from inetd because it needs to generate the server key before it can
+respond to the client, and this may take tens of seconds. Clients
+would have to wait too long if the key was regenerated every time.
+However, with small key sizes (e.g. 512) using
+.Nm
+from inetd may
+be feasible.
+.It Fl k Ar key_gen_time
+Specifies how often the server key is regenerated (default 3600
+seconds, or one hour). The motivation for regenerating the key fairly
+often is that the key is not stored anywhere, and after about an hour,
+it becomes impossible to recover the key for decrypting intercepted
+communications even if the machine is cracked into or physically
+seized. A value of zero indicates that the key will never be regenerated.
+.It Fl p Ar port
+Specifies the port on which the server listens for connections
+(default 22).
+.It Fl q
+Quiet mode. Nothing is sent to the system log. Normally the beginning,
+authentication, and termination of each connection is logged.
+.It Fl Q
+Do not print an error message if RSA support is missing.
+.El
+.Sh CONFIGURATION FILE
+.Nm
+reads configuration data from
+.Pa /etc/sshd_config
+(or the file specified with
+.Fl f
+on the command line). The file
+contains keyword-value pairs, one per line. Lines starting with
+.Ql #
+and empty lines are interpreted as comments.
+.Pp
+The following keywords are possible.
+.Bl -tag -width Ds
+.It Cm AFSTokenPassing
+Specifies whether an AFS token may be forwarded to the server. Default is
+.Dq yes .
+.It Cm AllowGroups
+This keyword can be followed by a number of group names, separated
+by spaces. If specified, login is allowed only for users whose primary
+group matches one of the patterns.
+.Ql \&*
+and
+.Ql ?
+can be used as
+wildcards in the patterns. Only group names are valid, a numerical group
+id isn't recognized. By default login is allowed regardless of
+the primary group.
+.Pp
+.It Cm AllowUsers
+This keyword can be followed by a number of user names, separated
+by spaces. If specified, login is allowed only for users names that
+match one of the patterns.
+.Ql \&*
+and
+.Ql ?
+can be used as
+wildcards in the patterns. Only user names are valid, a numerical user
+id isn't recognized. By default login is allowed regardless of
+the user name.
+.Pp
+.It Cm CheckMail
+Specifies whether
+.Nm
+should check for new mail for interactive logins.
+The default is
+.Dq no .
+.It Cm DenyGroups
+This keyword can be followed by a number of group names, separated
+by spaces. Users whose primary group matches one of the patterns
+aren't allowed to log in.
+.Ql \&*
+and
+.Ql ?
+can be used as
+wildcards in the patterns. Only group names are valid, a numerical group
+id isn't recognized. By default login is allowed regardless of
+the primary group.
+.Pp
+.It Cm DenyUsers
+This keyword can be followed by a number of user names, separated
+by spaces. Login is allowed disallowed for user names that match
+one of the patterns.
+.Ql \&*
+and
+.Ql ?
+can be used as
+wildcards in the patterns. Only user names are valid, a numerical user
+id isn't recognized. By default login is allowed regardless of
+the user name.
+.Pp
+.It Cm FascistLogging
+Specifies whether to use verbose logging. Verbose logging violates
+the privacy of users and is not recommended. The argument must be
+.Dq yes
+or
+.Dq no .
+The default is
+.Dq no .
+.It Cm HostKey
+Specifies the file containing the private host key (default
+.Pa /etc/ssh_host_key ) .
+Note that
+.Nm
+does not start if this file is group/world-accessible.
+.It Cm IgnoreRhosts
+Specifies that rhosts and shosts files will not be used in
+authentication.
+.Pa /etc/hosts.equiv
+and
+.Pa /etc/shosts.equiv
+are still used. The default is
+.Dq no .
+.It Cm KeepAlive
+Specifies whether the system should send keepalive messages to the
+other side. If they are sent, death of the connection or crash of one
+of the machines will be properly noticed. However, this means that
+connections will die if the route is down temporarily, and some people
+find it annoying. On the other hand, if keepalives are not send,
+sessions may hang indefinitely on the server, leaving
+.Dq ghost
+users and consuming server resources.
+.Pp
+The default is
+.Dq yes
+(to send keepalives), and the server will notice
+if the network goes down or the client host reboots. This avoids
+infinitely hanging sessions.
+.Pp
+To disable keepalives, the value should be set to
+.Dq no
+in both the server and the client configuration files.
+.It Cm KerberosAuthentication
+Specifies whether Kerberos authentication is allowed. This can
+be in the form of a Kerberos ticket, or if
+.Cm PasswordAuthentication
+is yes, the password provided by the user will be validated through
+the Kerberos KDC. Default is
+.Dq yes .
+.It Cm KerberosOrLocalPasswd
+If set then if password authentication through Kerberos fails then
+the password will be validated via any additional local mechanism
+such as
+.Pa /etc/passwd
+or SecurID. Default is
+.Dq yes .
+.It Cm KerberosTgtPassing
+Specifies whether a Kerberos TGT may be forwarded to the server.
+Default is
+.Dq no ,
+as this only works when the Kerberos KDC is actually an AFS kaserver.
+.It Cm KerberosTicketCleanup
+Specifies whether to automatically destroy the user's ticket cache
+file on logout. Default is
+.Dq yes .
+.It Cm KeyRegenerationInterval
+The server key is automatically regenerated after this many seconds
+(if it has been used). The purpose of regeneration is to prevent
+decrypting captured sessions by later breaking into the machine and
+stealing the keys. The key is never stored anywhere. If the value is
+0, the key is never regenerated. The default is 3600
+(seconds).
+.It Cm ListenAddress
+Specifies what local address
+.Nm
+should listen on.
+The default is to listen to all local addresses.
+.It Cm LoginGraceTime
+The server disconnects after this time if the user has not
+successfully logged in. If the value is 0, there is no time limit.
+The default is 600 (seconds).
+.It Cm PasswordAuthentication
+Specifies whether password authentication is allowed.
+The default is
+.Dq yes .
+.It Cm PermitEmptyPasswords
+When password authentication is allowed, it specifies whether the
+server allows login to accounts with empty password strings. The default
+is
+.Dq yes .
+.It Cm PermitRootLogin
+Specifies whether the root can log in using
+.Xr ssh 1 .
+The argument must be
+.Dq yes ,
+.Dq without-password
+or
+.Dq no .
+The default is
+.Dq yes .
+If this options is set to
+.Dq without-password
+only password authentication is disabled for root.
+.Pp
+Root login with RSA authentication when the
+.Ar command
+option has been
+specified will be allowed regardless of the value of this setting
+(which may be useful for taking remote backups even if root login is
+normally not allowed).
+.It Cm Port
+Specifies the port number that
+.Nm
+listens on. The default is 22.
+.It Cm PrintMotd
+Specifies whether
+.Nm
+should print
+.Pa /etc/motd
+when a user logs in interactively. (On some systems it is also
+printed by the shell,
+.Pa /etc/profile ,
+or equivalent.) The default is
+.Dq yes .
+.It Cm QuietMode
+Specifies whether the system runs in quiet mode. In quiet mode,
+nothing is logged in the system log, except fatal errors. The default
+is
+.Dq no .
+.It Cm RandomSeed
+Obsolete. Random number generation uses other techniques.
+.It Cm RhostsAuthentication
+Specifies whether authentication using rhosts or /etc/hosts.equiv
+files is sufficient. Normally, this method should not be permitted
+because it is insecure.
+.Cm RhostsRSAAuthentication
+should be used
+instead, because it performs RSA-based host authentication in addition
+to normal rhosts or /etc/hosts.equiv authentication.
+The default is
+.Dq no .
+.It Cm RhostsRSAAuthentication
+Specifies whether rhosts or /etc/hosts.equiv authentication together
+with successful RSA host authentication is allowed. The default is
+.Dq yes .
+.It Cm RSAAuthentication
+Specifies whether pure RSA authentication is allowed. The default is
+.Dq yes .
+.It Cm ServerKeyBits
+Defines the number of bits in the server key. The minimum value is
+512, and the default is 768.
+.It Cm SkeyAuthentication
+Specifies whether
+.Xr skey 1
+authentication is allowed. The default is
+.Dq yes .
+Note that s/key authentication is enabled only if
+.Cm PasswordAuthentication
+is allowed, too.
+.It Cm StrictModes
+Specifies whether
+.Nm
+should check file modes and ownership of the
+user's files and home directory before accepting login. This
+is normally desirable because novices sometimes accidentally leave their
+directory or files world-writable. The default is
+.Dq yes .
+.It Cm SyslogFacility
+Gives the facility code that is used when logging messages from
+.Nm sshd .
+The possible values are: DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2,
+LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. The default is AUTH.
+.It Cm UseLogin
+Specifies whether
+.Xr login 1
+is used. The default is
+.Dq no .
+.It Cm X11Forwarding
+Specifies whether X11 forwarding is permitted. The default is
+.Dq yes .
+Note that disabling X11 forwarding does not improve security in any
+way, as users can always install their own forwarders.
+.It Cm X11DisplayOffset
+Specifies the first display number available for
+.Nm sshd Ns 's
+X11 forwarding. This prevents
+.Nm
+from interfering with real X11 servers.
+.El
+.Sh LOGIN PROCESS
+When a user successfully logs in,
+.Nm
+does the following:
+.Bl -enum -offset indent
+.It
+If the login is on a tty, and no command has been specified,
+prints last login time and
+.Pa /etc/motd
+(unless prevented in the configuration file or by
+.Pa $HOME/.hushlogin ;
+see the
+.Sx FILES
+section).
+.It
+If the login is on a tty, records login time.
+.It
+Checks
+.Pa /etc/nologin ;
+if it exists, prints contents and quits
+(unless root).
+.It
+Changes to run with normal user privileges.
+.It
+Sets up basic environment.
+.It
+Reads
+.Pa $HOME/.ssh/environment
+if it exists.
+.It
+Changes to user's home directory.
+.It
+If
+.Pa $HOME/.ssh/rc
+exists, runs it; else if
+.Pa /etc/sshrc
+exists, runs
+it; otherwise runs xauth. The
+.Dq rc
+files are given the X11
+authentication protocol and cookie in standard input.
+.It
+Runs user's shell or command.
+.El
+.Sh AUTHORIZED_KEYS FILE FORMAT
+The
+.Pa $HOME/.ssh/authorized_keys
+file lists the RSA keys that are
+permitted for RSA authentication. Each line of the file contains one
+key (empty lines and lines starting with a
+.Ql #
+are ignored as
+comments). Each line consists of the following fields, separated by
+spaces: options, bits, exponent, modulus, comment. The options field
+is optional; its presence is determined by whether the line starts
+with a number or not (the option field never starts with a number).
+The bits, exponent, modulus and comment fields give the RSA key; the
+comment field is not used for anything (but may be convenient for the
+user to identify the key).
+.Pp
+Note that lines in this file are usually several hundred bytes long
+(because of the size of the RSA key modulus). You don't want to type
+them in; instead, copy the
+.Pa identity.pub
+file and edit it.
+.Pp
+The options (if present) consists of comma-separated option
+specifications. No spaces are permitted, except within double quotes.
+The following option specifications are supported:
+.Bl -tag -width Ds
+.It Cm from="pattern-list"
+Specifies that in addition to RSA authentication, the canonical name
+of the remote host must be present in the comma-separated list of
+patterns ('*' and '?' serve as wildcards). The list may also contain
+patterns negated by prefixing them with '!'; if the canonical host
+name matches a negated pattern, the key is not accepted. The purpose
+of this option is to optionally increase security: RSA authentication
+by itself does not trust the network or name servers or anything (but
+the key); however, if somebody somehow steals the key, the key
+permits an intruder to log in from anywhere in the world. This
+additional option makes using a stolen key more difficult (name
+servers and/or routers would have to be compromised in addition to
+just the key).
+.It Cm command="command"
+Specifies that the command is executed whenever this key is used for
+authentication. The command supplied by the user (if any) is ignored.
+The command is run on a pty if the connection requests a pty;
+otherwise it is run without a tty. A quote may be included in the
+command by quoting it with a backslash. This option might be useful
+to restrict certain RSA keys to perform just a specific operation. An
+example might be a key that permits remote backups but nothing
+else. Notice that the client may specify TCP/IP and/or X11
+forwardings unless they are explicitly prohibited.
+.It Cm environment="NAME=value"
+Specifies that the string is to be added to the environment when
+logging in using this key. Environment variables set this way
+override other default environment values. Multiple options of this
+type are permitted.
+.It Cm no-port-forwarding
+Forbids TCP/IP forwarding when this key is used for authentication.
+Any port forward requests by the client will return an error. This
+might be used, e.g., in connection with the
+.Cm command
+option.
+.It Cm no-X11-forwarding
+Forbids X11 forwarding when this key is used for authentication.
+Any X11 forward requests by the client will return an error.
+.It Cm no-agent-forwarding
+Forbids authentication agent forwarding when this key is used for
+authentication.
+.It Cm no-pty
+Prevents tty allocation (a request to allocate a pty will fail).
+.El
+.Ss Examples
+1024 33 12121.\|.\|.\|312314325 ylo@foo.bar
+.Pp
+from="*.niksula.hut.fi,!pc.niksula.hut.fi" 1024 35 23.\|.\|.\|2334 ylo@niksula
+.Pp
+command="dump /home",no-pty,no-port-forwarding 1024 33 23.\|.\|.\|2323 backup.hut.fi
+.Sh SSH_KNOWN_HOSTS FILE FORMAT
+The
+.Pa /etc/ssh_known_hosts
+and
+.Pa $HOME/.ssh/known_hosts
+files contain host public keys for all known hosts. The global file should
+be prepared by the admistrator (optional), and the per-user file is
+maintained automatically: whenever the user connects an unknown host
+its key is added to the per-user file.
+.Pp
+Each line in these files contains the following fields: hostnames,
+bits, exponent, modulus, comment. The fields are separated by spaces.
+.Pp
+Hostnames is a comma-separated list of patterns ('*' and '?' act as
+wildcards); each pattern in turn is matched against the canonical host
+name (when authenticating a client) or against the user-supplied
+name (when authenticating a server). A pattern may also be preceded
+by
+.Ql !
+to indicate negation: if the host name matches a negated
+pattern, it is not accepted (by that line) even if it matched another
+pattern on the line.
+.Pp
+Bits, exponent, and modulus are taken directly from the host key; they
+can be obtained, e.g., from
+.Pa /etc/ssh_host_key.pub .
+The optional comment field continues to the end of the line, and is not used.
+.Pp
+Lines starting with
+.Ql #
+and empty lines are ignored as comments.
+.Pp
+When performing host authentication, authentication is accepted if any
+matching line has the proper key. It is thus permissible (but not
+recommended) to have several lines or different host keys for the same
+names. This will inevitably happen when short forms of host names
+from different domains are put in the file. It is possible
+that the files contain conflicting information; authentication is
+accepted if valid information can be found from either file.
+.Pp
+Note that the lines in these files are typically hundreds of characters
+long, and you definitely don't want to type in the host keys by hand.
+Rather, generate them by a script
+or by taking
+.Pa /etc/ssh_host_key.pub
+and adding the host names at the front.
+.Ss Examples
+closenet,closenet.hut.fi,.\|.\|.\|,130.233.208.41 1024 37 159.\|.\|.93 closenet.hut.fi
+.Sh FILES
+.Bl -tag -width Ds
+.It Pa /etc/sshd_config
+Contains configuration data for
+.Nm sshd .
+This file should be writable by root only, but it is recommended
+(though not necessary) that it be world-readable.
+.It Pa /etc/ssh_host_key
+Contains the private part of the host key.
+This file should only be owned by root, readable only by root, and not
+accessible to others.
+Note that
+.Nm
+does not start if this file is group/world-accessible.
+.It Pa /etc/ssh_host_key.pub
+Contains the public part of the host key.
+This file should be world-readable but writable only by
+root. Its contents should match the private part. This file is not
+really used for anything; it is only provided for the convenience of
+the user so its contents can be copied to known hosts files.
+These two files are created using
+.Xr ssh-keygen 1 .
+.It Pa /var/run/sshd.pid
+Contains the process ID of the
+.Nm
+listening for connections (if there are several daemons running
+concurrently for different ports, this contains the pid of the one
+started last). The contents of this file are not sensitive; it can be
+world-readable.
+.It Pa $HOME/.ssh/authorized_keys
+Lists the RSA keys that can be used to log into the user's account.
+This file must be readable by root (which may on some machines imply
+it being world-readable if the user's home directory resides on an NFS
+volume). It is recommended that it not be accessible by others. The
+format of this file is described above.
+.It Pa /etc/ssh_known_hosts
+This file is consulted when using rhosts with RSA host
+authentication to check the public key of the host. The key must be
+listed in this file to be accepted.
+.It Pa $HOME/.ssh/known_hosts
+The client uses this file
+and
+.Pa /etc/ssh_known_hosts
+to verify that the remote host is the one we intended to
+connect. These files should be writable only by root/the owner.
+.Pa /etc/ssh_known_hosts
+should be world-readable, and
+.Pa $HOME/.ssh/known_hosts
+can but need not be world-readable.
+.It Pa /etc/nologin
+If this file exists,
+.Nm
+refuses to let anyone except root log in. The contents of the file
+are displayed to anyone trying to log in, and non-root connections are
+refused. The file should be world-readable.
+.It Pa /etc/hosts.allow, /etc/hosts.deny
+If compiled with
+.Sy LIBWRAP
+support, tcp-wrappers access controls may be defined here as described in
+.Xr hosts_access 5 .
+.It Pa $HOME/.rhosts
+This file contains host-username pairs, separated by a space, one per
+line. The given user on the corresponding host is permitted to log in
+without password. The same file is used by rlogind and rshd.
+The file must
+be writable only by the user; it is recommended that it not be
+accessible by others.
+.Pp
+If is also possible to use netgroups in the file. Either host or user
+name may be of the form +@groupname to specify all hosts or all users
+in the group.
+.It Pa $HOME/.shosts
+For ssh,
+this file is exactly the same as for
+.Pa .rhosts .
+However, this file is
+not used by rlogin and rshd, so using this permits access using SSH only.
+.Pa /etc/hosts.equiv
+This file is used during
+.Pa .rhosts
+authentication. In the
+simplest form, this file contains host names, one per line. Users on
+those hosts are permitted to log in without a password, provided they
+have the same user name on both machines. The host name may also be
+followed by a user name; such users are permitted to log in as
+.Em any
+user on this machine (except root). Additionally, the syntax
+.Dq +@group
+can be used to specify netgroups. Negated entries start with
+.Ql \&- .
+.Pp
+If the client host/user is successfully matched in this file, login is
+automatically permitted provided the client and server user names are the
+same. Additionally, successful RSA host authentication is normally
+required. This file must be writable only by root; it is recommended
+that it be world-readable.
+.Pp
+.Sy "Warning: It is almost never a good idea to use user names in"
+.Pa hosts.equiv .
+Beware that it really means that the named user(s) can log in as
+.Em anybody ,
+which includes bin, daemon, adm, and other accounts that own critical
+binaries and directories. Using a user name practically grants the
+user root access. The only valid use for user names that I can think
+of is in negative entries.
+.Pp
+Note that this warning also applies to rsh/rlogin.
+.It Pa /etc/shosts.equiv
+This is processed exactly as
+.Pa /etc/hosts.equiv .
+However, this file may be useful in environments that want to run both
+rsh/rlogin and ssh.
+.It Pa $HOME/.ssh/environment
+This file is read into the environment at login (if it exists). It
+can only contain empty lines, comment lines (that start with
+.Ql # ) ,
+and assignment lines of the form name=value. The file should be writable
+only by the user; it need not be readable by anyone else.
+.It Pa $HOME/.ssh/rc
+If this file exists, it is run with /bin/sh after reading the
+environment files but before starting the user's shell or command. If
+X11 spoofing is in use, this will receive the "proto cookie" pair in
+standard input (and
+.Ev DISPLAY
+in environment). This must call
+.Xr xauth 1
+in that case.
+.Pp
+The primary purpose of this file is to run any initialization routines
+which may be needed before the user's home directory becomes
+accessible; AFS is a particular example of such an environment.
+.Pp
+This file will probably contain some initialization code followed by
+something similar to: "if read proto cookie; then echo add $DISPLAY
+$proto $cookie | xauth -q -; fi".
+.Pp
+If this file does not exist,
+.Pa /etc/sshrc
+is run, and if that
+does not exist either, xauth is used to store the cookie.
+.Pp
+This file should be writable only by the user, and need not be
+readable by anyone else.
+.It Pa /etc/sshrc
+Like
+.Pa $HOME/.ssh/rc .
+This can be used to specify
+machine-specific login-time initializations globally. This file
+should be writable only by root, and should be world-readable.
+.Sh AUTHOR
+Tatu Ylonen <ylo@cs.hut.fi>
+.Pp
+Information about new releases, mailing lists, and other related
+issues can be found from the SSH WWW home page:
+.Pp
+.Dl http://www.cs.hut.fi/ssh.
+.Pp
+OpenSSH
+is a derivative of the original (free) ssh 1.2.12 release, but with bugs
+removed and newer features re-added. Rapidly after the 1.2.12 release,
+newer versions bore successively more restrictive licenses. This version
+of OpenSSH
+.Bl -bullet
+.It
+has all components of a restrictive nature (ie. patents, see
+.Xr ssl 8 )
+directly removed from the source code; any licensed or patented components
+are chosen from
+external libraries.
+.It
+has been updated to support ssh protocol 1.5.
+.It
+contains added support for
+.Xr kerberos 8
+authentication and ticket passing.
+.It
+supports one-time password authentication with
+.Xr skey 1 .
+.El
+.Pp
+The libraries described in
+.Xr ssl 8
+are required for proper operation.
+.Sh SEE ALSO
+.Xr rlogin 1 ,
+.Xr rsh 1 ,
+.Xr scp 1 ,
+.Xr ssh 1 ,
+.Xr ssh-add 1 ,
+.Xr ssh-agent 1 ,
+.Xr ssh-keygen 1 ,
+.Xr ssl 8
diff --git a/sshd.c b/sshd.c
new file mode 100644
index 00000000..059f3119
--- /dev/null
+++ b/sshd.c
@@ -0,0 +1,2445 @@
+/*
+
+sshd.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ All rights reserved
+
+Created: Fri Mar 17 17:09:28 1995 ylo
+
+This program is the ssh daemon. It listens for connections from clients, and
+performs authentication, executes use commands or shell, and forwards
+information to/from the application to the user client over an encrypted
+connection. This can also handle forwarding of X11, TCP/IP, and authentication
+agent connections.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: sshd.c,v 1.1 1999/10/27 03:42:46 damien Exp $");
+
+#include "xmalloc.h"
+#include "rsa.h"
+#include "ssh.h"
+#include "pty.h"
+#include "packet.h"
+#include "buffer.h"
+#include "cipher.h"
+#include "mpaux.h"
+#include "servconf.h"
+#include "uidswap.h"
+#include "compat.h"
+
+#ifdef LIBWRAP
+#include <tcpd.h>
+#include <syslog.h>
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_WARNING;
+#endif /* LIBWRAP */
+
+#ifndef O_NOCTTY
+#define O_NOCTTY 0
+#endif
+
+#ifdef KRB4
+char *ticket = NULL;
+#endif /* KRB4 */
+
+#ifdef HAVE_PAM
+#include <security/pam_appl.h>
+struct pam_handle_t *pamh=NULL;
+char *pampasswd=NULL;
+int retval;
+int origretval;
+#endif /* HAVE_PAM */
+
+/* Local Xauthority file. */
+char *xauthfile = NULL;
+
+/* Server configuration options. */
+ServerOptions options;
+
+/* Name of the server configuration file. */
+char *config_file_name = SERVER_CONFIG_FILE;
+
+/* Debug mode flag. This can be set on the command line. If debug
+ mode is enabled, extra debugging output will be sent to the system
+ log, the daemon will not go to background, and will exit after processing
+ the first connection. */
+int debug_flag = 0;
+
+/* Flag indicating that the daemon is being started from inetd. */
+int inetd_flag = 0;
+
+/* argv[0] without path. */
+char *av0;
+
+/* Saved arguments to main(). */
+char **saved_argv;
+
+/* This is set to the socket that the server is listening; this is used in
+ the SIGHUP signal handler. */
+int listen_sock;
+
+/* Flags set in auth-rsa from authorized_keys flags. These are set in
+ auth-rsa.c. */
+int no_port_forwarding_flag = 0;
+int no_agent_forwarding_flag = 0;
+int no_x11_forwarding_flag = 0;
+int no_pty_flag = 0;
+char *forced_command = NULL; /* RSA authentication "command=" option. */
+struct envstring *custom_environment = NULL;
+ /* RSA authentication "environment=" options. */
+
+/* Session id for the current session. */
+unsigned char session_id[16];
+
+/* Any really sensitive data in the application is contained in this structure.
+ The idea is that this structure could be locked into memory so that the
+ pages do not get written into swap. However, there are some problems.
+ The private key contains BIGNUMs, and we do not (in principle) have
+ access to the internals of them, and locking just the structure is not
+ very useful. Currently, memory locking is not implemented. */
+struct
+{
+ /* Private part of server key. */
+ RSA *private_key;
+
+ /* Private part of host key. */
+ RSA *host_key;
+} sensitive_data;
+
+/* Flag indicating whether the current session key has been used. This flag
+ is set whenever the key is used, and cleared when the key is regenerated. */
+int key_used = 0;
+
+/* This is set to true when SIGHUP is received. */
+int received_sighup = 0;
+
+/* Public side of the server key. This value is regenerated regularly with
+ the private key. */
+RSA *public_key;
+
+/* Prototypes for various functions defined later in this file. */
+void do_connection(int privileged_port);
+void do_authentication(char *user, int privileged_port);
+void do_authenticated(struct passwd *pw);
+void do_exec_pty(const char *command, int ptyfd, int ttyfd,
+ const char *ttyname, struct passwd *pw, const char *term,
+ const char *display, const char *auth_proto,
+ const char *auth_data);
+void do_exec_no_pty(const char *command, struct passwd *pw,
+ const char *display, const char *auth_proto,
+ const char *auth_data);
+void do_child(const char *command, struct passwd *pw, const char *term,
+ const char *display, const char *auth_proto,
+ const char *auth_data, const char *ttyname);
+#ifdef HAVE_PAM
+static int pamconv(int num_msg, const struct pam_message **msg,
+ struct pam_response **resp, void *appdata_ptr);
+
+static struct pam_conv conv = {
+ pamconv,
+ NULL
+};
+
+static int pamconv(int num_msg, const struct pam_message **msg,
+ struct pam_response **resp, void *appdata_ptr)
+{
+ int count = 0;
+ int replies = 0;
+ struct pam_response *reply = NULL;
+ int size = sizeof(struct pam_response);
+
+ for(count = 0; count < num_msg; count++)
+ {
+ switch (msg[count]->msg_style)
+ {
+ case PAM_PROMPT_ECHO_ON:
+ case PAM_PROMPT_ECHO_OFF:
+ if (reply == NULL)
+ reply = xmalloc(size);
+ else
+ reply = realloc(reply, size);
+
+ if (reply == NULL)
+ return PAM_CONV_ERR;
+
+ size += sizeof(struct pam_response);
+
+ reply[replies].resp_retcode = PAM_SUCCESS;
+
+ reply[replies++].resp = xstrdup(pampasswd);
+ /* PAM frees resp */
+ break;
+
+ case PAM_TEXT_INFO:
+ /* ignore it... */
+ break;
+
+ case PAM_ERROR_MSG:
+ default:
+ /* Must be an error of some sort... */
+ if (reply != NULL)
+ free(reply);
+
+ return PAM_CONV_ERR;
+ }
+ }
+
+ if (reply != NULL)
+ *resp = reply;
+
+ return PAM_SUCCESS;
+}
+
+void pam_cleanup_proc(void *context)
+{
+ if (retval == PAM_SUCCESS)
+ retval = pam_close_session((pam_handle_t *)pamh, 0);
+
+ if (pam_end((pam_handle_t *)pamh, retval) != PAM_SUCCESS)
+ log("Cannot release PAM authentication.");
+}
+#endif /* HAVE_PAM */
+
+/* Signal handler for SIGHUP. Sshd execs itself when it receives SIGHUP;
+ the effect is to reread the configuration file (and to regenerate
+ the server key). */
+
+void sighup_handler(int sig)
+{
+ received_sighup = 1;
+ signal(SIGHUP, sighup_handler);
+}
+
+/* Called from the main program after receiving SIGHUP. Restarts the
+ server. */
+
+void sighup_restart()
+{
+ log("Received SIGHUP; restarting.");
+ close(listen_sock);
+ execv(saved_argv[0], saved_argv);
+ log("RESTART FAILED: av0='%s', error: %s.", av0, strerror(errno));
+ exit(1);
+}
+
+/* Generic signal handler for terminating signals in the master daemon.
+ These close the listen socket; not closing it seems to cause "Address
+ already in use" problems on some machines, which is inconvenient. */
+
+void sigterm_handler(int sig)
+{
+ log("Received signal %d; terminating.", sig);
+ close(listen_sock);
+ exit(255);
+}
+
+/* SIGCHLD handler. This is called whenever a child dies. This will then
+ reap any zombies left by exited c. */
+
+void main_sigchld_handler(int sig)
+{
+ int save_errno = errno;
+ int status;
+ wait(&status);
+ signal(SIGCHLD, main_sigchld_handler);
+ errno = save_errno;
+}
+
+/* Signal handler for the alarm after the login grace period has expired. */
+
+void grace_alarm_handler(int sig)
+{
+ /* Close the connection. */
+ packet_close();
+
+ /* Log error and exit. */
+ fatal("Timeout before authentication.");
+}
+
+/* Signal handler for the key regeneration alarm. Note that this
+ alarm only occurs in the daemon waiting for connections, and it does not
+ do anything with the private key or random state before forking. Thus there
+ should be no concurrency control/asynchronous execution problems. */
+
+void key_regeneration_alarm(int sig)
+{
+ int save_errno = errno;
+
+ /* Check if we should generate a new key. */
+ if (key_used)
+ {
+ /* This should really be done in the background. */
+ log("Generating new %d bit RSA key.", options.server_key_bits);
+
+ if (sensitive_data.private_key != NULL)
+ RSA_free(sensitive_data.private_key);
+ sensitive_data.private_key = RSA_new();
+
+ if (public_key != NULL)
+ RSA_free(public_key);
+ public_key = RSA_new();
+
+ rsa_generate_key(sensitive_data.private_key, public_key,
+ options.server_key_bits);
+ arc4random_stir();
+ key_used = 0;
+ log("RSA key generation complete.");
+ }
+
+ /* Reschedule the alarm. */
+ signal(SIGALRM, key_regeneration_alarm);
+ alarm(options.key_regeneration_time);
+ errno = save_errno;
+}
+
+/* Main program for the daemon. */
+
+int
+main(int ac, char **av)
+{
+ extern char *optarg;
+ extern int optind;
+ int opt, aux, sock_in, sock_out, newsock, i, pid, on = 1;
+ int remote_major, remote_minor;
+ int silentrsa = 0;
+ struct sockaddr_in sin;
+ char buf[100]; /* Must not be larger than remote_version. */
+ char remote_version[100]; /* Must be at least as big as buf. */
+ char *comment;
+ FILE *f;
+ struct linger linger;
+
+ /* Save argv[0]. */
+ saved_argv = av;
+ if (strchr(av[0], '/'))
+ av0 = strrchr(av[0], '/') + 1;
+ else
+ av0 = av[0];
+
+ /* Initialize configuration options to their default values. */
+ initialize_server_options(&options);
+
+ /* Parse command-line arguments. */
+ while ((opt = getopt(ac, av, "f:p:b:k:h:g:diqQ")) != EOF)
+ {
+ switch (opt)
+ {
+ case 'f':
+ config_file_name = optarg;
+ break;
+ case 'd':
+ debug_flag = 1;
+ break;
+ case 'i':
+ inetd_flag = 1;
+ break;
+ case 'Q':
+ silentrsa = 1;
+ break;
+ case 'q':
+ options.quiet_mode = 1;
+ break;
+ case 'b':
+ options.server_key_bits = atoi(optarg);
+ break;
+ case 'p':
+ options.port = atoi(optarg);
+ break;
+ case 'g':
+ options.login_grace_time = atoi(optarg);
+ break;
+ case 'k':
+ options.key_regeneration_time = atoi(optarg);
+ break;
+ case 'h':
+ options.host_key_file = optarg;
+ break;
+ case '?':
+ default:
+ fprintf(stderr, "sshd version %s\n", SSH_VERSION);
+ fprintf(stderr, "Usage: %s [options]\n", av0);
+ fprintf(stderr, "Options:\n");
+ fprintf(stderr, " -f file Configuration file (default %s/sshd_config)\n", ETCDIR);
+ fprintf(stderr, " -d Debugging mode\n");
+ fprintf(stderr, " -i Started from inetd\n");
+ fprintf(stderr, " -q Quiet (no logging)\n");
+ fprintf(stderr, " -p port Listen on the specified port (default: 22)\n");
+ fprintf(stderr, " -k seconds Regenerate server key every this many seconds (default: 3600)\n");
+ fprintf(stderr, " -g seconds Grace period for authentication (default: 300)\n");
+ fprintf(stderr, " -b bits Size of server RSA key (default: 768 bits)\n");
+ fprintf(stderr, " -h file File from which to read host key (default: %s)\n",
+ HOST_KEY_FILE);
+ exit(1);
+ }
+ }
+
+ /* check if RSA support exists */
+ if (rsa_alive() == 0) {
+ if (silentrsa == 0)
+ printf("sshd: no RSA support in libssl and libcrypto -- exiting. See ssl(8)\n");
+ log("no RSA support in libssl and libcrypto -- exiting. See ssl(8)");
+ exit(1);
+ }
+
+ /* Read server configuration options from the configuration file. */
+ read_server_config(&options, config_file_name);
+
+ /* Fill in default values for those options not explicitly set. */
+ fill_default_server_options(&options);
+
+ /* Check certain values for sanity. */
+ if (options.server_key_bits < 512 ||
+ options.server_key_bits > 32768)
+ {
+ fprintf(stderr, "Bad server key size.\n");
+ exit(1);
+ }
+ if (options.port < 1 || options.port > 65535)
+ {
+ fprintf(stderr, "Bad port number.\n");
+ exit(1);
+ }
+
+ /* Check that there are no remaining arguments. */
+ if (optind < ac)
+ {
+ fprintf(stderr, "Extra argument %s.\n", av[optind]);
+ exit(1);
+ }
+
+ /* Initialize the log (it is reinitialized below in case we forked). */
+ log_init(av0, debug_flag && !inetd_flag,
+ debug_flag || options.fascist_logging,
+ options.quiet_mode, options.log_facility);
+
+ debug("sshd version %.100s", SSH_VERSION);
+
+ sensitive_data.host_key = RSA_new();
+ /* Load the host key. It must have empty passphrase. */
+ if (!load_private_key(options.host_key_file, "",
+ sensitive_data.host_key, &comment))
+ {
+ if (debug_flag)
+ fprintf(stderr, "Could not load host key: %s: %s\n",
+ options.host_key_file, strerror(errno));
+ else
+ {
+ int err = errno;
+ log_init(av0, !inetd_flag, 1, 0, options.log_facility);
+ error("Could not load host key: %.200s: %.100s",
+ options.host_key_file, strerror(err));
+ }
+ exit(1);
+ }
+ xfree(comment);
+
+ /* If not in debugging mode, and not started from inetd, disconnect from
+ the controlling terminal, and fork. The original process exits. */
+ if (!debug_flag && !inetd_flag)
+ {
+#ifdef TIOCNOTTY
+ int fd;
+#endif /* TIOCNOTTY */
+ if (daemon(0, 0) < 0)
+ fatal("daemon() failed: %.200s", strerror(errno));
+
+ /* Disconnect from the controlling tty. */
+#ifdef TIOCNOTTY
+ fd = open("/dev/tty", O_RDWR|O_NOCTTY);
+ if (fd >= 0)
+ {
+ (void)ioctl(fd, TIOCNOTTY, NULL);
+ close(fd);
+ }
+#endif /* TIOCNOTTY */
+ }
+
+ /* Reinitialize the log (because of the fork above). */
+ log_init(av0, debug_flag && !inetd_flag,
+ debug_flag || options.fascist_logging,
+ options.quiet_mode, options.log_facility);
+
+ /* Check that server and host key lengths differ sufficiently. This is
+ necessary to make double encryption work with rsaref. Oh, I hate
+ software patents. I dont know if this can go? Niels */
+ if (options.server_key_bits >
+ BN_num_bits(sensitive_data.host_key->n) - SSH_KEY_BITS_RESERVED &&
+ options.server_key_bits <
+ BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED)
+ {
+ options.server_key_bits =
+ BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED;
+ debug("Forcing server key to %d bits to make it differ from host key.",
+ options.server_key_bits);
+ }
+
+ /* Do not display messages to stdout in RSA code. */
+ rsa_set_verbose(0);
+
+ /* Initialize the random number generator. */
+ arc4random_stir();
+
+ /* Chdir to the root directory so that the current disk can be unmounted
+ if desired. */
+ chdir("/");
+
+ /* Close connection cleanly after attack. */
+ cipher_attack_detected = packet_disconnect;
+
+ /* Start listening for a socket, unless started from inetd. */
+ if (inetd_flag)
+ {
+ int s1, s2;
+ s1 = dup(0); /* Make sure descriptors 0, 1, and 2 are in use. */
+ s2 = dup(s1);
+ sock_in = dup(0);
+ sock_out = dup(1);
+ /* We intentionally do not close the descriptors 0, 1, and 2 as our
+ code for setting the descriptors won\'t work if ttyfd happens to
+ be one of those. */
+ debug("inetd sockets after dupping: %d, %d", sock_in, sock_out);
+
+ public_key = RSA_new();
+ sensitive_data.private_key = RSA_new();
+ /* Generate an rsa key. */
+ log("Generating %d bit RSA key.", options.server_key_bits);
+ rsa_generate_key(sensitive_data.private_key, public_key,
+ options.server_key_bits);
+ arc4random_stir();
+ log("RSA key generation complete.");
+ }
+ else
+ {
+ /* Create socket for listening. */
+ listen_sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (listen_sock < 0)
+ fatal("socket: %.100s", strerror(errno));
+
+ /* Set socket options. We try to make the port reusable and have it
+ close as fast as possible without waiting in unnecessary wait states
+ on close. */
+ setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on,
+ sizeof(on));
+ linger.l_onoff = 1;
+ linger.l_linger = 5;
+ setsockopt(listen_sock, SOL_SOCKET, SO_LINGER, (void *)&linger,
+ sizeof(linger));
+
+ /* Initialize the socket address. */
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr = options.listen_addr;
+ sin.sin_port = htons(options.port);
+
+ /* Bind the socket to the desired port. */
+ if (bind(listen_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+ {
+ error("bind: %.100s", strerror(errno));
+ shutdown(listen_sock, SHUT_RDWR);
+ close(listen_sock);
+ fatal("Bind to port %d failed.", options.port);
+ }
+
+ if (!debug_flag)
+ {
+ /* Record our pid in /etc/sshd_pid to make it easier to kill the
+ correct sshd. We don\'t want to do this before the bind above
+ because the bind will fail if there already is a daemon, and this
+ will overwrite any old pid in the file. */
+ f = fopen(SSH_DAEMON_PID_FILE, "w");
+ if (f)
+ {
+ fprintf(f, "%u\n", (unsigned int)getpid());
+ fclose(f);
+ }
+ }
+
+ /* Start listening on the port. */
+ log("Server listening on port %d.", options.port);
+ if (listen(listen_sock, 5) < 0)
+ fatal("listen: %.100s", strerror(errno));
+
+ public_key = RSA_new();
+ sensitive_data.private_key = RSA_new();
+ /* Generate an rsa key. */
+ log("Generating %d bit RSA key.", options.server_key_bits);
+ rsa_generate_key(sensitive_data.private_key, public_key,
+ options.server_key_bits);
+ arc4random_stir();
+ log("RSA key generation complete.");
+
+ /* Schedule server key regeneration alarm. */
+ signal(SIGALRM, key_regeneration_alarm);
+ alarm(options.key_regeneration_time);
+
+ /* Arrange to restart on SIGHUP. The handler needs listen_sock. */
+ signal(SIGHUP, sighup_handler);
+ signal(SIGTERM, sigterm_handler);
+ signal(SIGQUIT, sigterm_handler);
+
+ /* Arrange SIGCHLD to be caught. */
+ signal(SIGCHLD, main_sigchld_handler);
+
+ /* Stay listening for connections until the system crashes or the
+ daemon is killed with a signal. */
+ for (;;)
+ {
+ if (received_sighup)
+ sighup_restart();
+ /* Wait in accept until there is a connection. */
+ aux = sizeof(sin);
+ newsock = accept(listen_sock, (struct sockaddr *)&sin, &aux);
+ if (received_sighup)
+ sighup_restart();
+ if (newsock < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ error("accept: %.100s", strerror(errno));
+ continue;
+ }
+
+ /* Got connection. Fork a child to handle it, unless we are in
+ debugging mode. */
+ if (debug_flag)
+ {
+ /* In debugging mode. Close the listening socket, and start
+ processing the connection without forking. */
+ debug("Server will not fork when running in debugging mode.");
+ close(listen_sock);
+ sock_in = newsock;
+ sock_out = newsock;
+ pid = getpid();
+ break;
+ }
+ else
+ {
+ /* Normal production daemon. Fork, and have the child process
+ the connection. The parent continues listening. */
+ if ((pid = fork()) == 0)
+ {
+ /* Child. Close the listening socket, and start using
+ the accepted socket. Reinitialize logging (since our
+ pid has changed). We break out of the loop to handle
+ the connection. */
+ close(listen_sock);
+ sock_in = newsock;
+ sock_out = newsock;
+ log_init(av0, debug_flag && !inetd_flag,
+ options.fascist_logging || debug_flag,
+ options.quiet_mode, options.log_facility);
+ break;
+ }
+ }
+
+ /* Parent. Stay in the loop. */
+ if (pid < 0)
+ error("fork: %.100s", strerror(errno));
+ else
+ debug("Forked child %d.", pid);
+
+ /* Mark that the key has been used (it was "given" to the child). */
+ key_used = 1;
+
+ arc4random_stir();
+
+ /* Close the new socket (the child is now taking care of it). */
+ close(newsock);
+ }
+ }
+
+ /* This is the child processing a new connection. */
+
+ /* Disable the key regeneration alarm. We will not regenerate the key
+ since we are no longer in a position to give it to anyone. We will
+ not restart on SIGHUP since it no longer makes sense. */
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+ signal(SIGHUP, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ signal(SIGCHLD, SIG_DFL);
+
+ /* Set socket options for the connection. We want the socket to close
+ as fast as possible without waiting for anything. If the connection
+ is not a socket, these will do nothing. */
+ /* setsockopt(sock_in, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */
+ linger.l_onoff = 1;
+ linger.l_linger = 5;
+ setsockopt(sock_in, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger));
+
+ /* Register our connection. This turns encryption off because we do not
+ have a key. */
+ packet_set_connection(sock_in, sock_out);
+
+ /* Check whether logins are denied from this host. */
+#ifdef LIBWRAP
+ {
+ struct request_info req;
+
+ request_init(&req, RQ_DAEMON, av0, RQ_FILE, sock_in, NULL);
+ fromhost(&req);
+
+ if (!hosts_access(&req)) {
+ close(sock_in);
+ close(sock_out);
+ refuse(&req);
+ }
+ log("Connection from %.500s port %d",
+ eval_client(&req), get_remote_port());
+ }
+#else
+ /* Log the connection. */
+ log("Connection from %.100s port %d",
+ get_remote_ipaddr(), get_remote_port());
+#endif /* LIBWRAP */
+
+ /* We don\'t want to listen forever unless the other side successfully
+ authenticates itself. So we set up an alarm which is cleared after
+ successful authentication. A limit of zero indicates no limit.
+ Note that we don\'t set the alarm in debugging mode; it is just annoying
+ to have the server exit just when you are about to discover the bug. */
+ signal(SIGALRM, grace_alarm_handler);
+ if (!debug_flag)
+ alarm(options.login_grace_time);
+
+ /* Send our protocol version identification. */
+ snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n",
+ PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION);
+ if (write(sock_out, buf, strlen(buf)) != strlen(buf))
+ fatal("Could not write ident string.");
+
+ /* Read other side\'s version identification. */
+ for (i = 0; i < sizeof(buf) - 1; i++)
+ {
+ if (read(sock_in, &buf[i], 1) != 1)
+ fatal("Did not receive ident string.");
+ if (buf[i] == '\r')
+ {
+ buf[i] = '\n';
+ buf[i + 1] = 0;
+ break;
+ }
+ if (buf[i] == '\n')
+ {
+ /* buf[i] == '\n' */
+ buf[i + 1] = 0;
+ break;
+ }
+ }
+ buf[sizeof(buf) - 1] = 0;
+
+ /* Check that the versions match. In future this might accept several
+ versions and set appropriate flags to handle them. */
+ if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor,
+ remote_version) != 3)
+ {
+ const char *s = "Protocol mismatch.\n";
+ (void) write(sock_out, s, strlen(s));
+ close(sock_in);
+ close(sock_out);
+ fatal("Bad protocol version identification: %.100s", buf);
+ }
+ debug("Client protocol version %d.%d; client software version %.100s",
+ remote_major, remote_minor, remote_version);
+ if (remote_major != PROTOCOL_MAJOR)
+ {
+ const char *s = "Protocol major versions differ.\n";
+ (void) write(sock_out, s, strlen(s));
+ close(sock_in);
+ close(sock_out);
+ fatal("Protocol major versions differ: %d vs. %d",
+ PROTOCOL_MAJOR, remote_major);
+ }
+
+ /* Check that the client has sufficiently high software version. */
+ if (remote_major == 1 && remote_minor < 3)
+ packet_disconnect("Your ssh version is too old and is no longer supported. Please install a newer version.");
+
+ if (remote_major == 1 && remote_minor == 3) {
+ enable_compat13();
+ if (strcmp(remote_version, "OpenSSH-1.1") != 0) {
+ debug("Agent forwarding disabled, remote version is not compatible.");
+ no_agent_forwarding_flag = 1;
+ }
+ }
+
+ packet_set_nonblocking();
+
+ /* Handle the connection. We pass as argument whether the connection
+ came from a privileged port. */
+ do_connection(get_remote_port() < IPPORT_RESERVED);
+
+#ifdef KRB4
+ /* Cleanup user's ticket cache file. */
+ if (options.kerberos_ticket_cleanup)
+ (void) dest_tkt();
+#endif /* KRB4 */
+
+ /* Cleanup user's local Xauthority file. */
+ if (xauthfile) unlink(xauthfile);
+
+ /* The connection has been terminated. */
+ log("Closing connection to %.100s", inet_ntoa(sin.sin_addr));
+
+#ifdef HAVE_PAM
+ if (retval == PAM_SUCCESS)
+ retval = pam_close_session((pam_handle_t *)pamh, 0);
+
+ if (pam_end((pam_handle_t *)pamh, retval) != PAM_SUCCESS)
+ log("Cannot release PAM authentication.");
+
+ fatal_remove_cleanup(&pam_cleanup_proc, NULL);
+#endif /* HAVE_PAM */
+
+ packet_close();
+
+ exit(0);
+}
+
+/* Process an incoming connection. Protocol version identifiers have already
+ been exchanged. This sends server key and performs the key exchange.
+ Server and host keys will no longer be needed after this functions. */
+
+void do_connection(int privileged_port)
+{
+ int i;
+ BIGNUM *session_key_int;
+ unsigned char session_key[SSH_SESSION_KEY_LENGTH];
+ unsigned char check_bytes[8];
+ char *user;
+ unsigned int cipher_type, auth_mask, protocol_flags;
+ int plen, slen;
+ u_int32_t rand = 0;
+
+ /* Generate check bytes that the client must send back in the user packet
+ in order for it to be accepted; this is used to defy ip spoofing
+ attacks. Note that this only works against somebody doing IP spoofing
+ from a remote machine; any machine on the local network can still see
+ outgoing packets and catch the random cookie. This only affects
+ rhosts authentication, and this is one of the reasons why it is
+ inherently insecure. */
+ for (i = 0; i < 8; i++) {
+ if (i % 4 == 0)
+ rand = arc4random();
+ check_bytes[i] = rand & 0xff;
+ rand >>= 8;
+ }
+
+ /* Send our public key. We include in the packet 64 bits of random
+ data that must be matched in the reply in order to prevent IP spoofing. */
+ packet_start(SSH_SMSG_PUBLIC_KEY);
+ for (i = 0; i < 8; i++)
+ packet_put_char(check_bytes[i]);
+
+ /* Store our public server RSA key. */
+ packet_put_int(BN_num_bits(public_key->n));
+ packet_put_bignum(public_key->e);
+ packet_put_bignum(public_key->n);
+
+ /* Store our public host RSA key. */
+ packet_put_int(BN_num_bits(sensitive_data.host_key->n));
+ packet_put_bignum(sensitive_data.host_key->e);
+ packet_put_bignum(sensitive_data.host_key->n);
+
+ /* Put protocol flags. */
+ packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN);
+
+ /* Declare which ciphers we support. */
+ packet_put_int(cipher_mask());
+
+ /* Declare supported authentication types. */
+ auth_mask = 0;
+ if (options.rhosts_authentication)
+ auth_mask |= 1 << SSH_AUTH_RHOSTS;
+ if (options.rhosts_rsa_authentication)
+ auth_mask |= 1 << SSH_AUTH_RHOSTS_RSA;
+ if (options.rsa_authentication)
+ auth_mask |= 1 << SSH_AUTH_RSA;
+#ifdef KRB4
+ if (options.kerberos_authentication)
+ auth_mask |= 1 << SSH_AUTH_KERBEROS;
+#endif
+#ifdef AFS
+ if (options.kerberos_tgt_passing)
+ auth_mask |= 1 << SSH_PASS_KERBEROS_TGT;
+ if (options.afs_token_passing)
+ auth_mask |= 1 << SSH_PASS_AFS_TOKEN;
+#endif
+ if (options.password_authentication)
+ auth_mask |= 1 << SSH_AUTH_PASSWORD;
+ packet_put_int(auth_mask);
+
+ /* Send the packet and wait for it to be sent. */
+ packet_send();
+ packet_write_wait();
+
+ debug("Sent %d bit public key and %d bit host key.",
+ BN_num_bits(public_key->n), BN_num_bits(sensitive_data.host_key->n));
+
+ /* Read clients reply (cipher type and session key). */
+ packet_read_expect(&plen, SSH_CMSG_SESSION_KEY);
+
+ /* Get cipher type. */
+ cipher_type = packet_get_char();
+
+ /* Get check bytes from the packet. These must match those we sent earlier
+ with the public key packet. */
+ for (i = 0; i < 8; i++)
+ if (check_bytes[i] != packet_get_char())
+ packet_disconnect("IP Spoofing check bytes do not match.");
+
+ debug("Encryption type: %.200s", cipher_name(cipher_type));
+
+ /* Get the encrypted integer. */
+ session_key_int = BN_new();
+ packet_get_bignum(session_key_int, &slen);
+
+ /* Get protocol flags. */
+ protocol_flags = packet_get_int();
+ packet_set_protocol_flags(protocol_flags);
+
+ packet_integrity_check(plen, 1 + 8 + slen + 4, SSH_CMSG_SESSION_KEY);
+
+ /* Decrypt it using our private server key and private host key (key with
+ larger modulus first). */
+ if (BN_cmp(sensitive_data.private_key->n, sensitive_data.host_key->n) > 0)
+ {
+ /* Private key has bigger modulus. */
+ assert(BN_num_bits(sensitive_data.private_key->n) >=
+ BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED);
+ rsa_private_decrypt(session_key_int, session_key_int,
+ sensitive_data.private_key);
+ rsa_private_decrypt(session_key_int, session_key_int,
+ sensitive_data.host_key);
+ }
+ else
+ {
+ /* Host key has bigger modulus (or they are equal). */
+ assert(BN_num_bits(sensitive_data.host_key->n) >=
+ BN_num_bits(sensitive_data.private_key->n) +
+ SSH_KEY_BITS_RESERVED);
+ rsa_private_decrypt(session_key_int, session_key_int,
+ sensitive_data.host_key);
+ rsa_private_decrypt(session_key_int, session_key_int,
+ sensitive_data.private_key);
+ }
+
+ /* Compute session id for this session. */
+ compute_session_id(session_id, check_bytes,
+ BN_num_bits(sensitive_data.host_key->n),
+ sensitive_data.host_key->n,
+ BN_num_bits(sensitive_data.private_key->n),
+ sensitive_data.private_key->n);
+
+ /* Extract session key from the decrypted integer. The key is in the
+ least significant 256 bits of the integer; the first byte of the
+ key is in the highest bits. */
+ BN_mask_bits(session_key_int, sizeof(session_key) * 8);
+ assert(BN_num_bytes(session_key_int) == sizeof(session_key));
+ BN_bn2bin(session_key_int, session_key);
+
+ /* Xor the first 16 bytes of the session key with the session id. */
+ for (i = 0; i < 16; i++)
+ session_key[i] ^= session_id[i];
+
+ /* Destroy the decrypted integer. It is no longer needed. */
+ BN_clear_free(session_key_int);
+
+ /* Set the session key. From this on all communications will be
+ encrypted. */
+ packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH,
+ cipher_type, 0);
+
+ /* Destroy our copy of the session key. It is no longer needed. */
+ memset(session_key, 0, sizeof(session_key));
+
+ debug("Received session key; encryption turned on.");
+
+ /* Send an acknowledgement packet. Note that this packet is sent
+ encrypted. */
+ packet_start(SSH_SMSG_SUCCESS);
+ packet_send();
+ packet_write_wait();
+
+ /* Get the name of the user that we wish to log in as. */
+ packet_read_expect(&plen, SSH_CMSG_USER);
+
+ /* Get the user name. */
+ {
+ int ulen;
+ user = packet_get_string(&ulen);
+ packet_integrity_check(plen, (4 + ulen), SSH_CMSG_USER);
+ }
+
+ /* Destroy the private and public keys. They will no longer be needed. */
+ RSA_free(public_key);
+ RSA_free(sensitive_data.private_key);
+ RSA_free(sensitive_data.host_key);
+
+ setproctitle("%s", user);
+ /* Do the authentication. */
+ do_authentication(user, privileged_port);
+}
+
+/* Check if the user is allowed to log in via ssh. If user is listed in
+ DenyUsers or user's primary group is listed in DenyGroups, false will
+ be returned. If AllowUsers isn't empty and user isn't listed there, or
+ if AllowGroups isn't empty and user isn't listed there, false will be
+ returned. Otherwise true is returned.
+ XXX This function should also check if user has a valid shell */
+
+static int
+allowed_user(struct passwd *pw)
+{
+ struct group *grp;
+ int i;
+
+ /* Shouldn't be called if pw is NULL, but better safe than sorry... */
+ if (!pw)
+ return 0;
+
+ /* XXX Should check for valid login shell */
+
+ /* Return false if user is listed in DenyUsers */
+ if (options.num_deny_users > 0)
+ {
+ if (!pw->pw_name)
+ return 0;
+ for (i = 0; i < options.num_deny_users; i++)
+ if (match_pattern(pw->pw_name, options.deny_users[i]))
+ return 0;
+ }
+
+ /* Return false if AllowUsers isn't empty and user isn't listed there */
+ if (options.num_allow_users > 0)
+ {
+ if (!pw->pw_name)
+ return 0;
+ for (i = 0; i < options.num_allow_users; i++)
+ if (match_pattern(pw->pw_name, options.allow_users[i]))
+ break;
+ /* i < options.num_allow_users iff we break for loop */
+ if (i >= options.num_allow_users)
+ return 0;
+ }
+
+ /* Get the primary group name if we need it. Return false if it fails */
+ if (options.num_deny_groups > 0 || options.num_allow_groups > 0 )
+ {
+ grp = getgrgid(pw->pw_gid);
+ if (!grp)
+ return 0;
+
+ /* Return false if user's group is listed in DenyGroups */
+ if (options.num_deny_groups > 0)
+ {
+ if (!grp->gr_name)
+ return 0;
+ for (i = 0; i < options.num_deny_groups; i++)
+ if (match_pattern(grp->gr_name, options.deny_groups[i]))
+ return 0;
+ }
+
+ /* Return false if AllowGroups isn't empty and user's group isn't
+ listed there */
+ if (options.num_allow_groups > 0)
+ {
+ if (!grp->gr_name)
+ return 0;
+ for (i = 0; i < options.num_allow_groups; i++)
+ if (match_pattern(grp->gr_name, options.allow_groups[i]))
+ break;
+ /* i < options.num_allow_groups iff we break for loop */
+ if (i >= options.num_allow_groups)
+ return 0;
+ }
+ }
+
+ /* We found no reason not to let this user try to log on... */
+ return 1;
+}
+
+/* Performs authentication of an incoming connection. Session key has already
+ been exchanged and encryption is enabled. User is the user name to log
+ in as (received from the clinet). Privileged_port is true if the
+ connection comes from a privileged port (used for .rhosts authentication).*/
+
+#define MAX_AUTH_FAILURES 5
+
+void
+do_authentication(char *user, int privileged_port)
+{
+ int type;
+ int authenticated = 0;
+ int authentication_failures = 0;
+ char *password;
+ struct passwd *pw, pwcopy;
+ char *client_user;
+ unsigned int client_host_key_bits;
+ BIGNUM *client_host_key_e, *client_host_key_n;
+#ifdef HAVE_PAM
+ int pam_auth_ok;
+#endif /* HAVE_PAM */
+
+#ifdef AFS
+ /* If machine has AFS, set process authentication group. */
+ if (k_hasafs()) {
+ k_setpag();
+ k_unlog();
+ }
+#endif /* AFS */
+
+ /* Verify that the user is a valid user. */
+ pw = getpwnam(user);
+#ifdef HAVE_PAM
+ if ((pw != NULL) && allowed_user(pw))
+ {
+ /* Initialise PAM */
+ retval = pam_start("ssh", pw->pw_name, &conv, (pam_handle_t **)&pamh);
+ fatal_add_cleanup(&pam_cleanup_proc, NULL);
+ origretval = retval;
+ if (retval == PAM_SUCCESS)
+ pam_auth_ok = 1;
+ }
+
+ if (pam_auth_ok == 0)
+#else /* HAVE_PAM */
+ if (!pw || !allowed_user(pw))
+#endif /* HAVE_PAM */
+ {
+ /* The user does not exist or access is denied,
+ but fake indication that authentication is needed. */
+ packet_start(SSH_SMSG_FAILURE);
+ packet_send();
+ packet_write_wait();
+
+ /* Keep reading packets, and always respond with a failure. This is to
+ avoid disclosing whether such a user really exists. */
+ for (;;)
+ {
+ /* Read a packet. This will not return if the client disconnects. */
+ int plen;
+ int type = packet_read(&plen);
+#ifdef SKEY
+ int passw_len;
+ char *password, *skeyinfo;
+ if (options.password_authentication &&
+ options.skey_authentication == 1 &&
+ type == SSH_CMSG_AUTH_PASSWORD &&
+ (password = packet_get_string(&passw_len)) != NULL &&
+ passw_len == 5 &&
+ strncasecmp(password, "s/key", 5) == 0 &&
+ (skeyinfo = skey_fake_keyinfo(user)) != NULL ){
+ /* Send a fake s/key challenge. */
+ packet_send_debug(skeyinfo);
+ }
+#endif
+ /* Send failure. This should be indistinguishable from a failed
+ authentication. */
+ packet_start(SSH_SMSG_FAILURE);
+ packet_send();
+ packet_write_wait();
+ if (++authentication_failures >= MAX_AUTH_FAILURES) {
+ packet_disconnect("Too many authentication failures for %.100s from %.200s",
+ user, get_canonical_hostname());
+ }
+ }
+ /*NOTREACHED*/
+ abort();
+ }
+
+ /* Take a copy of the returned structure. */
+ memset(&pwcopy, 0, sizeof(pwcopy));
+ pwcopy.pw_name = xstrdup(pw->pw_name);
+ pwcopy.pw_passwd = xstrdup(pw->pw_passwd);
+ pwcopy.pw_uid = pw->pw_uid;
+ pwcopy.pw_gid = pw->pw_gid;
+ pwcopy.pw_dir = xstrdup(pw->pw_dir);
+ pwcopy.pw_shell = xstrdup(pw->pw_shell);
+ pw = &pwcopy;
+
+ /* If we are not running as root, the user must have the same uid as the
+ server. */
+ if (getuid() != 0 && pw->pw_uid != getuid())
+ packet_disconnect("Cannot change user when server not running as root.");
+
+ debug("Attempting authentication for %.100s.", user);
+
+ /* If the user has no password, accept authentication immediately. */
+ if (options.password_authentication &&
+#ifdef KRB4
+ (!options.kerberos_authentication || options.kerberos_or_local_passwd) &&
+#endif /* KRB4 */
+ auth_password(pw, ""))
+ {
+ /* Authentication with empty password succeeded. */
+ debug("Login for user %.100s accepted without authentication.", user);
+ /* authentication_type = SSH_AUTH_PASSWORD; */
+ authenticated = 1;
+ /* Success packet will be sent after loop below. */
+ }
+ else
+ {
+ /* Indicate that authentication is needed. */
+ packet_start(SSH_SMSG_FAILURE);
+ packet_send();
+ packet_write_wait();
+ }
+
+ /* Loop until the user has been authenticated or the connection is closed. */
+ while (!authenticated)
+ {
+ int plen;
+ /* Get a packet from the client. */
+ type = packet_read(&plen);
+
+ /* Process the packet. */
+ switch (type)
+ {
+
+#ifdef AFS
+ case SSH_CMSG_HAVE_KERBEROS_TGT:
+ if (!options.kerberos_tgt_passing)
+ {
+ /* packet_get_all(); */
+ log("Kerberos tgt passing disabled.");
+ break;
+ }
+ else {
+ /* Accept Kerberos tgt. */
+ int dlen;
+ char *tgt = packet_get_string(&dlen);
+ packet_integrity_check(plen, 4 + dlen, type);
+ if (!auth_kerberos_tgt(pw, tgt))
+ debug("Kerberos tgt REFUSED for %s", user);
+ xfree(tgt);
+ }
+ continue;
+
+ case SSH_CMSG_HAVE_AFS_TOKEN:
+ if (!options.afs_token_passing || !k_hasafs()) {
+ /* packet_get_all(); */
+ log("AFS token passing disabled.");
+ break;
+ }
+ else {
+ /* Accept AFS token. */
+ int dlen;
+ char *token_string = packet_get_string(&dlen);
+ packet_integrity_check(plen, 4 + dlen, type);
+ if (!auth_afs_token(user, pw->pw_uid, token_string))
+ debug("AFS token REFUSED for %s", user);
+ xfree(token_string);
+ continue;
+ }
+#endif /* AFS */
+
+#ifdef KRB4
+ case SSH_CMSG_AUTH_KERBEROS:
+ if (!options.kerberos_authentication)
+ {
+ /* packet_get_all(); */
+ log("Kerberos authentication disabled.");
+ break;
+ }
+ else {
+ /* Try Kerberos v4 authentication. */
+ KTEXT_ST auth;
+ char *tkt_user = NULL;
+ char *kdata = packet_get_string((unsigned int *)&auth.length);
+ packet_integrity_check(plen, 4 + auth.length, type);
+
+ if (auth.length < MAX_KTXT_LEN)
+ memcpy(auth.dat, kdata, auth.length);
+ xfree(kdata);
+
+ if (auth_krb4(user, &auth, &tkt_user)) {
+ /* Client has successfully authenticated to us. */
+ log("Kerberos authentication accepted %s for account "
+ "%s from %s", tkt_user, user, get_canonical_hostname());
+ /* authentication_type = SSH_AUTH_KERBEROS; */
+ authenticated = 1;
+ xfree(tkt_user);
+ }
+ else {
+ log("Kerberos authentication failed for account "
+ "%s from %s", user, get_canonical_hostname());
+ }
+ }
+ break;
+#endif /* KRB4 */
+
+ case SSH_CMSG_AUTH_RHOSTS:
+ if (!options.rhosts_authentication)
+ {
+ log("Rhosts authentication disabled.");
+ break;
+ }
+
+ /* Rhosts authentication (also uses /etc/hosts.equiv). */
+ if (!privileged_port)
+ {
+ log("Rhosts authentication not available for connections from unprivileged port.");
+ break;
+ }
+
+ /* Get client user name. Note that we just have to trust the client;
+ this is one reason why rhosts authentication is insecure.
+ (Another is IP-spoofing on a local network.) */
+ {
+ int dlen;
+ client_user = packet_get_string(&dlen);
+ packet_integrity_check(plen, 4 + dlen, type);
+ }
+
+ /* Try to authenticate using /etc/hosts.equiv and .rhosts. */
+ if (auth_rhosts(pw, client_user, options.ignore_rhosts,
+ options.strict_modes))
+ {
+ /* Authentication accepted. */
+ log("Rhosts authentication accepted for %.100s, remote %.100s on %.700s.",
+ user, client_user, get_canonical_hostname());
+ authenticated = 1;
+ xfree(client_user);
+ break;
+ }
+ log("Rhosts authentication failed for %.100s, remote %.100s.",
+ user, client_user);
+ xfree(client_user);
+ break;
+
+ case SSH_CMSG_AUTH_RHOSTS_RSA:
+ if (!options.rhosts_rsa_authentication)
+ {
+ log("Rhosts with RSA authentication disabled.");
+ break;
+ }
+
+ /* Rhosts authentication (also uses /etc/hosts.equiv) with RSA
+ host authentication. */
+ if (!privileged_port)
+ {
+ log("Rhosts authentication not available for connections from unprivileged port.");
+ break;
+ }
+
+ {
+ int ulen, elen, nlen;
+ /* Get client user name. Note that we just have to trust
+ the client; root on the client machine can claim to be
+ any user. */
+ client_user = packet_get_string(&ulen);
+
+ /* Get the client host key. */
+ client_host_key_e = BN_new();
+ client_host_key_n = BN_new();
+ client_host_key_bits = packet_get_int();
+ packet_get_bignum(client_host_key_e, &elen);
+ packet_get_bignum(client_host_key_n, &nlen);
+
+ packet_integrity_check(plen, (4 + ulen) + 4 + elen + nlen, type);
+ }
+
+ /* Try to authenticate using /etc/hosts.equiv and .rhosts. */
+ if (auth_rhosts_rsa(pw, client_user,
+ client_host_key_bits, client_host_key_e,
+ client_host_key_n, options.ignore_rhosts,
+ options.strict_modes))
+ {
+ /* Authentication accepted. */
+ authenticated = 1;
+ xfree(client_user);
+ BN_clear_free(client_host_key_e);
+ BN_clear_free(client_host_key_n);
+ break;
+ }
+ log("Rhosts authentication failed for %.100s, remote %.100s.",
+ user, client_user);
+ xfree(client_user);
+ BN_clear_free(client_host_key_e);
+ BN_clear_free(client_host_key_n);
+ break;
+
+ case SSH_CMSG_AUTH_RSA:
+ if (!options.rsa_authentication)
+ {
+ log("RSA authentication disabled.");
+ break;
+ }
+
+ /* RSA authentication requested. */
+ {
+ int nlen;
+ BIGNUM *n;
+ n = BN_new();
+ packet_get_bignum(n, &nlen);
+
+ packet_integrity_check(plen, nlen, type);
+
+ if (auth_rsa(pw, n, options.strict_modes))
+ {
+ /* Successful authentication. */
+ BN_clear_free(n);
+ log("RSA authentication for %.100s accepted.", user);
+ authenticated = 1;
+ break;
+ }
+ BN_clear_free(n);
+ log("RSA authentication for %.100s failed.", user);
+ }
+ break;
+
+ case SSH_CMSG_AUTH_PASSWORD:
+ if (!options.password_authentication)
+ {
+ log("Password authentication disabled.");
+ break;
+ }
+
+ /* Password authentication requested. */
+ /* Read user password. It is in plain text, but was transmitted
+ over the encrypted channel so it is not visible to an outside
+ observer. */
+ {
+ int passw_len;
+ password = packet_get_string(&passw_len);
+ packet_integrity_check(plen, 4 + passw_len, type);
+ }
+
+ /* Try authentication with the password. */
+ if (auth_password(pw, password))
+ {
+ /* Successful authentication. */
+ /* Clear the password from memory. */
+ memset(password, 0, strlen(password));
+ xfree(password);
+ log("Password authentication for %.100s accepted.", user);
+ authenticated = 1;
+ break;
+ }
+ log("Password authentication for %.100s failed.", user);
+ memset(password, 0, strlen(password));
+ xfree(password);
+ break;
+
+ case SSH_CMSG_AUTH_TIS:
+ /* TIS Authentication is unsupported */
+ log("TIS authentication disabled.");
+ break;
+
+ default:
+ /* Any unknown messages will be ignored (and failure returned)
+ during authentication. */
+ log("Unknown message during authentication: type %d", type);
+ break; /* Respond with a failure message. */
+ }
+ /* If successfully authenticated, break out of loop. */
+ if (authenticated)
+ break;
+
+ /* Send a message indicating that the authentication attempt failed. */
+ packet_start(SSH_SMSG_FAILURE);
+ packet_send();
+ packet_write_wait();
+
+ if (++authentication_failures >= MAX_AUTH_FAILURES) {
+ packet_disconnect("Too many authentication failures for %.100s from %.200s",
+ pw->pw_name, get_canonical_hostname());
+ }
+ }
+
+ /* Check if the user is logging in as root and root logins are disallowed. */
+ if (pw->pw_uid == 0 && !options.permit_root_login)
+ {
+ if (forced_command)
+ log("Root login accepted for forced command.", forced_command);
+ else
+ packet_disconnect("ROOT LOGIN REFUSED FROM %.200s",
+ get_canonical_hostname());
+ }
+
+ /* The user has been authenticated and accepted. */
+ packet_start(SSH_SMSG_SUCCESS);
+ packet_send();
+ packet_write_wait();
+
+ /* Perform session preparation. */
+ do_authenticated(pw);
+}
+
+/* Prepares for an interactive session. This is called after the user has
+ been successfully authenticated. During this message exchange, pseudo
+ terminals are allocated, X11, TCP/IP, and authentication agent forwardings
+ are requested, etc. */
+
+void do_authenticated(struct passwd *pw)
+{
+ int type;
+ int compression_level = 0, enable_compression_after_reply = 0;
+ int have_pty = 0, ptyfd = -1, ttyfd = -1, xauthfd = -1;
+ int row, col, xpixel, ypixel, screen;
+ char ttyname[64];
+ char *command, *term = NULL, *display = NULL, *proto = NULL, *data = NULL;
+ struct group *grp;
+ gid_t tty_gid;
+ mode_t tty_mode;
+ int n_bytes;
+
+ /* Cancel the alarm we set to limit the time taken for authentication. */
+ alarm(0);
+
+ /* Inform the channel mechanism that we are the server side and that
+ the client may request to connect to any port at all. (The user could
+ do it anyway, and we wouldn\'t know what is permitted except by the
+ client telling us, so we can equally well trust the client not to request
+ anything bogus.) */
+ channel_permit_all_opens();
+
+ /* We stay in this loop until the client requests to execute a shell or a
+ command. */
+ while (1)
+ {
+ int plen, dlen;
+
+ /* Get a packet from the client. */
+ type = packet_read(&plen);
+
+ /* Process the packet. */
+ switch (type)
+ {
+ case SSH_CMSG_REQUEST_COMPRESSION:
+ packet_integrity_check(plen, 4, type);
+ compression_level = packet_get_int();
+ if (compression_level < 1 || compression_level > 9)
+ {
+ packet_send_debug("Received illegal compression level %d.",
+ compression_level);
+ goto fail;
+ }
+ /* Enable compression after we have responded with SUCCESS. */
+ enable_compression_after_reply = 1;
+ break;
+
+ case SSH_CMSG_REQUEST_PTY:
+ if (no_pty_flag)
+ {
+ debug("Allocating a pty not permitted for this authentication.");
+ goto fail;
+ }
+ if (have_pty)
+ packet_disconnect("Protocol error: you already have a pty.");
+
+ debug("Allocating pty.");
+
+ /* Allocate a pty and open it. */
+ if (!pty_allocate(&ptyfd, &ttyfd, ttyname))
+ {
+ error("Failed to allocate pty.");
+ goto fail;
+ }
+
+ /* Determine the group to make the owner of the tty. */
+ grp = getgrnam("tty");
+ if (grp)
+ {
+ tty_gid = grp->gr_gid;
+ tty_mode = S_IRUSR|S_IWUSR|S_IWGRP;
+ }
+ else
+ {
+ tty_gid = pw->pw_gid;
+ tty_mode = S_IRUSR|S_IWUSR|S_IWGRP|S_IWOTH;
+ }
+
+ /* Change ownership of the tty. */
+ if (chown(ttyname, pw->pw_uid, tty_gid) < 0)
+ fatal("chown(%.100s, %d, %d) failed: %.100s",
+ ttyname, pw->pw_uid, tty_gid, strerror(errno));
+ if (chmod(ttyname, tty_mode) < 0)
+ fatal("chmod(%.100s, 0%o) failed: %.100s",
+ ttyname, tty_mode, strerror(errno));
+
+ /* Get TERM from the packet. Note that the value may be of arbitrary
+ length. */
+
+ term = packet_get_string(&dlen);
+ packet_integrity_check(dlen, strlen(term), type);
+ /* packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type); */
+ /* Remaining bytes */
+ n_bytes = plen - (4 + dlen + 4*4);
+
+ if (strcmp(term, "") == 0)
+ term = NULL;
+
+ /* Get window size from the packet. */
+ row = packet_get_int();
+ col = packet_get_int();
+ xpixel = packet_get_int();
+ ypixel = packet_get_int();
+ pty_change_window_size(ptyfd, row, col, xpixel, ypixel);
+
+ /* Get tty modes from the packet. */
+ tty_parse_modes(ttyfd, &n_bytes);
+ packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type);
+
+ /* Indicate that we now have a pty. */
+ have_pty = 1;
+ break;
+
+ case SSH_CMSG_X11_REQUEST_FORWARDING:
+ if (!options.x11_forwarding)
+ {
+ packet_send_debug("X11 forwarding disabled in server configuration file.");
+ goto fail;
+ }
+#ifdef XAUTH_PATH
+ if (no_x11_forwarding_flag)
+ {
+ packet_send_debug("X11 forwarding not permitted for this authentication.");
+ goto fail;
+ }
+ debug("Received request for X11 forwarding with auth spoofing.");
+ if (display)
+ packet_disconnect("Protocol error: X11 display already set.");
+ {
+ int proto_len, data_len;
+ proto = packet_get_string(&proto_len);
+ data = packet_get_string(&data_len);
+ packet_integrity_check(plen, 4+proto_len + 4+data_len + 4, type);
+ }
+ if (packet_get_protocol_flags() & SSH_PROTOFLAG_SCREEN_NUMBER)
+ screen = packet_get_int();
+ else
+ screen = 0;
+ display = x11_create_display_inet(screen);
+ if (!display)
+ goto fail;
+
+ /* Setup to always have a local .Xauthority. */
+ xauthfile = xmalloc(MAXPATHLEN);
+ snprintf(xauthfile, MAXPATHLEN, "/tmp/XauthXXXXXX");
+
+ if ((xauthfd = mkstemp(xauthfile)) != -1) {
+ fchown(xauthfd, pw->pw_uid, pw->pw_gid);
+ close(xauthfd);
+ }
+ else {
+ xfree(xauthfile);
+ xauthfile = NULL;
+ }
+ break;
+#else /* XAUTH_PATH */
+ /* No xauth program; we won't accept forwarding with spoofing. */
+ packet_send_debug("No xauth program; cannot forward with spoofing.");
+ goto fail;
+#endif /* XAUTH_PATH */
+
+ case SSH_CMSG_AGENT_REQUEST_FORWARDING:
+ if (no_agent_forwarding_flag)
+ {
+ debug("Authentication agent forwarding not permitted for this authentication.");
+ goto fail;
+ }
+ debug("Received authentication agent forwarding request.");
+ auth_input_request_forwarding(pw);
+ break;
+
+ case SSH_CMSG_PORT_FORWARD_REQUEST:
+ if (no_port_forwarding_flag)
+ {
+ debug("Port forwarding not permitted for this authentication.");
+ goto fail;
+ }
+ debug("Received TCP/IP port forwarding request.");
+ channel_input_port_forward_request(pw->pw_uid == 0);
+ break;
+
+ case SSH_CMSG_EXEC_SHELL:
+ /* Set interactive/non-interactive mode. */
+ packet_set_interactive(have_pty || display != NULL,
+ options.keepalives);
+
+ if (forced_command != NULL)
+ goto do_forced_command;
+ debug("Forking shell.");
+ packet_integrity_check(plen, 0, type);
+ if (have_pty)
+ do_exec_pty(NULL, ptyfd, ttyfd, ttyname, pw, term, display, proto,
+ data);
+ else
+ do_exec_no_pty(NULL, pw, display, proto, data);
+ return;
+
+ case SSH_CMSG_EXEC_CMD:
+ /* Set interactive/non-interactive mode. */
+ packet_set_interactive(have_pty || display != NULL,
+ options.keepalives);
+
+ if (forced_command != NULL)
+ goto do_forced_command;
+ /* Get command from the packet. */
+ {
+ int dlen;
+ command = packet_get_string(&dlen);
+ debug("Executing command '%.500s'", command);
+ packet_integrity_check(plen, 4 + dlen, type);
+ }
+ if (have_pty)
+ do_exec_pty(command, ptyfd, ttyfd, ttyname, pw, term, display,
+ proto, data);
+ else
+ do_exec_no_pty(command, pw, display, proto, data);
+ xfree(command);
+ return;
+
+ case SSH_CMSG_MAX_PACKET_SIZE:
+ debug("The server does not support limiting packet size.");
+ goto fail;
+
+ default:
+ /* Any unknown messages in this phase are ignored, and a failure
+ message is returned. */
+ log("Unknown packet type received after authentication: %d", type);
+ goto fail;
+ }
+
+ /* The request was successfully processed. */
+ packet_start(SSH_SMSG_SUCCESS);
+ packet_send();
+ packet_write_wait();
+
+ /* Enable compression now that we have replied if appropriate. */
+ if (enable_compression_after_reply)
+ {
+ enable_compression_after_reply = 0;
+ packet_start_compression(compression_level);
+ }
+
+ continue;
+
+ fail:
+ /* The request failed. */
+ packet_start(SSH_SMSG_FAILURE);
+ packet_send();
+ packet_write_wait();
+ continue;
+
+ do_forced_command:
+ /* There is a forced command specified for this login. Execute it. */
+ debug("Executing forced command: %.900s", forced_command);
+ if (have_pty)
+ do_exec_pty(forced_command, ptyfd, ttyfd, ttyname, pw, term, display,
+ proto, data);
+ else
+ do_exec_no_pty(forced_command, pw, display, proto, data);
+ return;
+ }
+}
+
+/* This is called to fork and execute a command when we have no tty. This
+ will call do_child from the child, and server_loop from the parent after
+ setting up file descriptors and such. */
+
+void do_exec_no_pty(const char *command, struct passwd *pw,
+ const char *display, const char *auth_proto,
+ const char *auth_data)
+{
+ int pid;
+
+#ifdef USE_PIPES
+ int pin[2], pout[2], perr[2];
+ /* Allocate pipes for communicating with the program. */
+ if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0)
+ packet_disconnect("Could not create pipes: %.100s",
+ strerror(errno));
+#else /* USE_PIPES */
+ int inout[2], err[2];
+ /* Uses socket pairs to communicate with the program. */
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 ||
+ socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0)
+ packet_disconnect("Could not create socket pairs: %.100s",
+ strerror(errno));
+#endif /* USE_PIPES */
+
+ setproctitle("%s@notty", pw->pw_name);
+
+ /* Fork the child. */
+ if ((pid = fork()) == 0)
+ {
+ /* Child. Reinitialize the log since the pid has changed. */
+ log_init(av0, debug_flag && !inetd_flag, debug_flag,
+ options.quiet_mode, options.log_facility);
+
+ /* Create a new session and process group since the 4.4BSD setlogin()
+ affects the entire process group. */
+ if (setsid() < 0)
+ error("setsid failed: %.100s", strerror(errno));
+
+#ifdef USE_PIPES
+ /* Redirect stdin. We close the parent side of the socket pair,
+ and make the child side the standard input. */
+ close(pin[1]);
+ if (dup2(pin[0], 0) < 0)
+ perror("dup2 stdin");
+ close(pin[0]);
+
+ /* Redirect stdout. */
+ close(pout[0]);
+ if (dup2(pout[1], 1) < 0)
+ perror("dup2 stdout");
+ close(pout[1]);
+
+ /* Redirect stderr. */
+ close(perr[0]);
+ if (dup2(perr[1], 2) < 0)
+ perror("dup2 stderr");
+ close(perr[1]);
+#else /* USE_PIPES */
+ /* Redirect stdin, stdout, and stderr. Stdin and stdout will use the
+ same socket, as some programs (particularly rdist) seem to depend
+ on it. */
+ close(inout[1]);
+ close(err[1]);
+ if (dup2(inout[0], 0) < 0) /* stdin */
+ perror("dup2 stdin");
+ if (dup2(inout[0], 1) < 0) /* stdout. Note: same socket as stdin. */
+ perror("dup2 stdout");
+ if (dup2(err[0], 2) < 0) /* stderr */
+ perror("dup2 stderr");
+#endif /* USE_PIPES */
+
+ /* Do processing for the child (exec command etc). */
+ do_child(command, pw, NULL, display, auth_proto, auth_data, NULL);
+ /*NOTREACHED*/
+ }
+ if (pid < 0)
+ packet_disconnect("fork failed: %.100s", strerror(errno));
+#ifdef USE_PIPES
+ /* We are the parent. Close the child sides of the pipes. */
+ close(pin[0]);
+ close(pout[1]);
+ close(perr[1]);
+
+ /* Enter the interactive session. */
+ server_loop(pid, pin[1], pout[0], perr[0]);
+ /* server_loop has closed pin[1], pout[1], and perr[1]. */
+#else /* USE_PIPES */
+ /* We are the parent. Close the child sides of the socket pairs. */
+ close(inout[0]);
+ close(err[0]);
+
+ /* Enter the interactive session. Note: server_loop must be able to handle
+ the case that fdin and fdout are the same. */
+ server_loop(pid, inout[1], inout[1], err[1]);
+ /* server_loop has closed inout[1] and err[1]. */
+#endif /* USE_PIPES */
+}
+
+struct pty_cleanup_context
+{
+ const char *ttyname;
+ int pid;
+};
+
+/* Function to perform cleanup if we get aborted abnormally (e.g., due to a
+ dropped connection). */
+
+void pty_cleanup_proc(void *context)
+{
+ struct pty_cleanup_context *cu = context;
+
+ debug("pty_cleanup_proc called");
+
+#if defined(KRB4)
+ /* Destroy user's ticket cache file. */
+ (void) dest_tkt();
+#endif /* KRB4 */
+
+ /* Record that the user has logged out. */
+ record_logout(cu->pid, cu->ttyname);
+
+ /* Release the pseudo-tty. */
+ pty_release(cu->ttyname);
+}
+
+/* This is called to fork and execute a command when we have a tty. This
+ will call do_child from the child, and server_loop from the parent after
+ setting up file descriptors, controlling tty, updating wtmp, utmp,
+ lastlog, and other such operations. */
+
+void do_exec_pty(const char *command, int ptyfd, int ttyfd,
+ const char *ttyname, struct passwd *pw, const char *term,
+ const char *display, const char *auth_proto,
+ const char *auth_data)
+{
+ int pid, fdout;
+ const char *hostname;
+ time_t last_login_time;
+ char buf[100], *time_string;
+ FILE *f;
+ char line[256];
+ struct stat st;
+ int quiet_login;
+ struct sockaddr_in from;
+ int fromlen;
+ struct pty_cleanup_context cleanup_context;
+
+ /* Get remote host name. */
+ hostname = get_canonical_hostname();
+
+ /* Get the time when the user last logged in. Buf will be set to contain
+ the hostname the last login was from. */
+ if(!options.use_login) {
+ last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name,
+ buf, sizeof(buf));
+ }
+
+ setproctitle("%s@%s", pw->pw_name, strrchr(ttyname, '/') + 1);
+
+ /* Fork the child. */
+ if ((pid = fork()) == 0)
+ {
+ pid = getpid();
+
+ /* Child. Reinitialize the log because the pid has changed. */
+ log_init(av0, debug_flag && !inetd_flag, debug_flag, options.quiet_mode,
+ options.log_facility);
+
+ /* Close the master side of the pseudo tty. */
+ close(ptyfd);
+
+ /* Make the pseudo tty our controlling tty. */
+ pty_make_controlling_tty(&ttyfd, ttyname);
+
+ /* Redirect stdin from the pseudo tty. */
+ if (dup2(ttyfd, fileno(stdin)) < 0)
+ error("dup2 stdin failed: %.100s", strerror(errno));
+
+ /* Redirect stdout to the pseudo tty. */
+ if (dup2(ttyfd, fileno(stdout)) < 0)
+ error("dup2 stdin failed: %.100s", strerror(errno));
+
+ /* Redirect stderr to the pseudo tty. */
+ if (dup2(ttyfd, fileno(stderr)) < 0)
+ error("dup2 stdin failed: %.100s", strerror(errno));
+
+ /* Close the extra descriptor for the pseudo tty. */
+ close(ttyfd);
+
+ /* Get IP address of client. This is needed because we want to record
+ where the user logged in from. If the connection is not a socket,
+ let the ip address be 0.0.0.0. */
+ memset(&from, 0, sizeof(from));
+ if (packet_get_connection_in() == packet_get_connection_out())
+ {
+ fromlen = sizeof(from);
+ if (getpeername(packet_get_connection_in(),
+ (struct sockaddr *)&from, &fromlen) < 0)
+ fatal("getpeername: %.100s", strerror(errno));
+ }
+
+ /* Record that there was a login on that terminal. */
+ record_login(pid, ttyname, pw->pw_name, pw->pw_uid, hostname,
+ &from);
+
+ /* Check if .hushlogin exists. */
+ snprintf(line, sizeof line, "%.200s/.hushlogin", pw->pw_dir);
+ quiet_login = stat(line, &st) >= 0;
+
+ /* If the user has logged in before, display the time of last login.
+ However, don't display anything extra if a command has been
+ specified (so that ssh can be used to execute commands on a remote
+ machine without users knowing they are going to another machine).
+ Login(1) will do this for us as well, so check if login(1) is used */
+ if (command == NULL && last_login_time != 0 && !quiet_login &&
+ !options.use_login)
+ {
+ /* Convert the date to a string. */
+ time_string = ctime(&last_login_time);
+ /* Remove the trailing newline. */
+ if (strchr(time_string, '\n'))
+ *strchr(time_string, '\n') = 0;
+ /* Display the last login time. Host if displayed if known. */
+ if (strcmp(buf, "") == 0)
+ printf("Last login: %s\r\n", time_string);
+ else
+ printf("Last login: %s from %s\r\n", time_string, buf);
+ }
+
+ /* Print /etc/motd unless a command was specified or printing it was
+ disabled in server options or login(1) will be used. Note that
+ some machines appear to print it in /etc/profile or similar. */
+ if (command == NULL && options.print_motd && !quiet_login &&
+ !options.use_login)
+ {
+ /* Print /etc/motd if it exists. */
+ f = fopen("/etc/motd", "r");
+ if (f)
+ {
+ while (fgets(line, sizeof(line), f))
+ fputs(line, stdout);
+ fclose(f);
+ }
+ }
+
+ /* Do common processing for the child, such as execing the command. */
+ do_child(command, pw, term, display, auth_proto, auth_data, ttyname);
+ /*NOTREACHED*/
+ }
+ if (pid < 0)
+ packet_disconnect("fork failed: %.100s", strerror(errno));
+ /* Parent. Close the slave side of the pseudo tty. */
+ close(ttyfd);
+
+ /* Create another descriptor of the pty master side for use as the standard
+ input. We could use the original descriptor, but this simplifies code
+ in server_loop. The descriptor is bidirectional. */
+ fdout = dup(ptyfd);
+ if (fdout < 0)
+ packet_disconnect("dup failed: %.100s", strerror(errno));
+
+ /* Add a cleanup function to clear the utmp entry and record logout time
+ in case we call fatal() (e.g., the connection gets closed). */
+ cleanup_context.pid = pid;
+ cleanup_context.ttyname = ttyname;
+ fatal_add_cleanup(pty_cleanup_proc, (void *)&cleanup_context);
+
+ /* Enter interactive session. */
+ server_loop(pid, ptyfd, fdout, -1);
+ /* server_loop has not closed ptyfd and fdout. */
+
+ /* Cancel the cleanup function. */
+ fatal_remove_cleanup(pty_cleanup_proc, (void *)&cleanup_context);
+
+ /* Record that the user has logged out. */
+ record_logout(pid, ttyname);
+
+ /* Release the pseudo-tty. */
+ pty_release(ttyname);
+
+ /* Close the server side of the socket pairs. We must do this after the
+ pty cleanup, so that another process doesn't get this pty while we're
+ still cleaning up. */
+ close(ptyfd);
+ close(fdout);
+}
+
+/* Sets the value of the given variable in the environment. If the variable
+ already exists, its value is overriden. */
+
+void child_set_env(char ***envp, unsigned int *envsizep, const char *name,
+ const char *value)
+{
+ unsigned int i, namelen;
+ char **env;
+
+ /* Find the slot where the value should be stored. If the variable already
+ exists, we reuse the slot; otherwise we append a new slot at the end
+ of the array, expanding if necessary. */
+ env = *envp;
+ namelen = strlen(name);
+ for (i = 0; env[i]; i++)
+ if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=')
+ break;
+ if (env[i])
+ {
+ /* Name already exists. Reuse the slot. */
+ xfree(env[i]);
+ }
+ else
+ {
+ /* New variable. Expand the array if necessary. */
+ if (i >= (*envsizep) - 1)
+ {
+ (*envsizep) += 50;
+ env = (*envp) = xrealloc(env, (*envsizep) * sizeof(char *));
+ }
+
+ /* Need to set the NULL pointer at end of array beyond the new
+ slot. */
+ env[i + 1] = NULL;
+ }
+
+ /* Allocate space and format the variable in the appropriate slot. */
+ env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1);
+ snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value);
+}
+
+/* Reads environment variables from the given file and adds/overrides them
+ into the environment. If the file does not exist, this does nothing.
+ Otherwise, it must consist of empty lines, comments (line starts with '#')
+ and assignments of the form name=value. No other forms are allowed. */
+
+void read_environment_file(char ***env, unsigned int *envsize,
+ const char *filename)
+{
+ FILE *f;
+ char buf[4096];
+ char *cp, *value;
+
+ /* Open the environment file. */
+ f = fopen(filename, "r");
+ if (!f)
+ return; /* Not found. */
+
+ /* Process each line. */
+ while (fgets(buf, sizeof(buf), f))
+ {
+ /* Skip leading whitespace. */
+ for (cp = buf; *cp == ' ' || *cp == '\t'; cp++)
+ ;
+
+ /* Ignore empty and comment lines. */
+ if (!*cp || *cp == '#' || *cp == '\n')
+ continue;
+
+ /* Remove newline. */
+ if (strchr(cp, '\n'))
+ *strchr(cp, '\n') = '\0';
+
+ /* Find the equals sign. Its lack indicates badly formatted line. */
+ value = strchr(cp, '=');
+ if (value == NULL)
+ {
+ fprintf(stderr, "Bad line in %.100s: %.200s\n", filename, buf);
+ continue;
+ }
+
+ /* Replace the equals sign by nul, and advance value to the value
+ string. */
+ *value = '\0';
+ value++;
+
+ /* Set the value in environment. */
+ child_set_env(env, envsize, cp, value);
+ }
+
+ fclose(f);
+}
+
+/* Performs common processing for the child, such as setting up the
+ environment, closing extra file descriptors, setting the user and group
+ ids, and executing the command or shell. */
+
+void do_child(const char *command, struct passwd *pw, const char *term,
+ const char *display, const char *auth_proto,
+ const char *auth_data, const char *ttyname)
+{
+ const char *shell, *cp = NULL;
+ char buf[256];
+ FILE *f;
+ unsigned int envsize, i;
+ char **env;
+ extern char **environ;
+ struct stat st;
+ char *argv[10];
+
+ /* Check /etc/nologin. */
+ f = fopen("/etc/nologin", "r");
+ if (f)
+ { /* /etc/nologin exists. Print its contents and exit. */
+ while (fgets(buf, sizeof(buf), f))
+ fputs(buf, stderr);
+ fclose(f);
+ if (pw->pw_uid != 0)
+ exit(254);
+ }
+
+ /* Set login name in the kernel. */
+ if (setlogin(pw->pw_name) < 0)
+ error("setlogin failed: %s", strerror(errno));
+
+ /* Set uid, gid, and groups. */
+ /* Login(1) does this as well, and it needs uid 0 for the "-h" switch,
+ so we let login(1) to this for us. */
+ if(!options.use_login) {
+ if (getuid() == 0 || geteuid() == 0)
+ {
+ if (setgid(pw->pw_gid) < 0)
+ {
+ perror("setgid");
+ exit(1);
+ }
+ /* Initialize the group list. */
+ if (initgroups(pw->pw_name, pw->pw_gid) < 0)
+ {
+ perror("initgroups");
+ exit(1);
+ }
+ endgrent();
+
+ /* Permanently switch to the desired uid. */
+ permanently_set_uid(pw->pw_uid);
+ }
+
+ if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid)
+ fatal("Failed to set uids to %d.", (int)pw->pw_uid);
+ }
+
+ /* Get the shell from the password data. An empty shell field is legal,
+ and means /bin/sh. */
+ shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell;
+
+#ifdef AFS
+ /* Try to get AFS tokens for the local cell. */
+ if (k_hasafs()) {
+ char cell[64];
+
+ if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0)
+ krb_afslog(cell, 0);
+
+ krb_afslog(0, 0);
+ }
+#endif /* AFS */
+
+ /* Initialize the environment. In the first part we allocate space for
+ all environment variables. */
+ envsize = 100;
+ env = xmalloc(envsize * sizeof(char *));
+ env[0] = NULL;
+
+ if(!options.use_login) {
+ /* Set basic environment. */
+ child_set_env(&env, &envsize, "USER", pw->pw_name);
+ child_set_env(&env, &envsize, "LOGNAME", pw->pw_name);
+ child_set_env(&env, &envsize, "HOME", pw->pw_dir);
+ child_set_env(&env, &envsize, "PATH", _PATH_STDPATH);
+
+ snprintf(buf, sizeof buf, "%.200s/%.50s",
+ _PATH_MAILDIR, pw->pw_name);
+ child_set_env(&env, &envsize, "MAIL", buf);
+
+ /* Normal systems set SHELL by default. */
+ child_set_env(&env, &envsize, "SHELL", shell);
+ }
+
+ /* Let it inherit timezone if we have one. */
+ if (getenv("TZ"))
+ child_set_env(&env, &envsize, "TZ", getenv("TZ"));
+
+ /* Set custom environment options from RSA authentication. */
+ while (custom_environment)
+ {
+ struct envstring *ce = custom_environment;
+ char *s = ce->s;
+ int i;
+ for (i = 0; s[i] != '=' && s[i]; i++)
+ ;
+ if (s[i] == '=')
+ {
+ s[i] = 0;
+ child_set_env(&env, &envsize, s, s + i + 1);
+ }
+ custom_environment = ce->next;
+ xfree(ce->s);
+ xfree(ce);
+ }
+
+ /* Set SSH_CLIENT. */
+ snprintf(buf, sizeof buf, "%.50s %d %d",
+ get_remote_ipaddr(), get_remote_port(), options.port);
+ child_set_env(&env, &envsize, "SSH_CLIENT", buf);
+
+ /* Set SSH_TTY if we have a pty. */
+ if (ttyname)
+ child_set_env(&env, &envsize, "SSH_TTY", ttyname);
+
+ /* Set TERM if we have a pty. */
+ if (term)
+ child_set_env(&env, &envsize, "TERM", term);
+
+ /* Set DISPLAY if we have one. */
+ if (display)
+ child_set_env(&env, &envsize, "DISPLAY", display);
+
+#ifdef KRB4
+ if (ticket)
+ child_set_env(&env, &envsize, "KRBTKFILE", ticket);
+#endif /* KRB4 */
+
+ /* Set XAUTHORITY to always be a local file. */
+ if (xauthfile)
+ child_set_env(&env, &envsize, "XAUTHORITY", xauthfile);
+
+ /* Set variable for forwarded authentication connection, if we have one. */
+ if (auth_get_socket_name() != NULL)
+ child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME,
+ auth_get_socket_name());
+
+ /* Read $HOME/.ssh/environment. */
+ if(!options.use_login) {
+ snprintf(buf, sizeof buf, "%.200s/.ssh/environment", pw->pw_dir);
+ read_environment_file(&env, &envsize, buf);
+ }
+
+ /* If debugging, dump the environment to stderr. */
+ if (debug_flag)
+ {
+ fprintf(stderr, "Environment:\n");
+ for (i = 0; env[i]; i++)
+ fprintf(stderr, " %.200s\n", env[i]);
+ }
+
+ /* Close the connection descriptors; note that this is the child, and the
+ server will still have the socket open, and it is important that we
+ do not shutdown it. Note that the descriptors cannot be closed before
+ building the environment, as we call get_remote_ipaddr there. */
+ if (packet_get_connection_in() == packet_get_connection_out())
+ close(packet_get_connection_in());
+ else
+ {
+ close(packet_get_connection_in());
+ close(packet_get_connection_out());
+ }
+ /* Close all descriptors related to channels. They will still remain
+ open in the parent. */
+ channel_close_all();
+
+ /* Close any extra file descriptors. Note that there may still be
+ descriptors left by system functions. They will be closed later. */
+ endpwent();
+ endhostent();
+
+ /* Close any extra open file descriptors so that we don\'t have them
+ hanging around in clients. Note that we want to do this after
+ initgroups, because at least on Solaris 2.3 it leaves file descriptors
+ open. */
+ for (i = 3; i < 64; i++)
+ close(i);
+
+ /* Change current directory to the user\'s home directory. */
+ if (chdir(pw->pw_dir) < 0)
+ fprintf(stderr, "Could not chdir to home directory %s: %s\n",
+ pw->pw_dir, strerror(errno));
+
+ /* Must take new environment into use so that .ssh/rc, /etc/sshrc and
+ xauth are run in the proper environment. */
+ environ = env;
+
+ /* Run $HOME/.ssh/rc, /etc/sshrc, or xauth (whichever is found first
+ in this order). */
+ if(!options.use_login) {
+ if (stat(SSH_USER_RC, &st) >= 0)
+ {
+ if (debug_flag)
+ fprintf(stderr, "Running /bin/sh %s\n", SSH_USER_RC);
+
+ f = popen("/bin/sh " SSH_USER_RC, "w");
+ if (f)
+ {
+ if (auth_proto != NULL && auth_data != NULL)
+ fprintf(f, "%s %s\n", auth_proto, auth_data);
+ pclose(f);
+ }
+ else
+ fprintf(stderr, "Could not run %s\n", SSH_USER_RC);
+ }
+ else
+ if (stat(SSH_SYSTEM_RC, &st) >= 0)
+ {
+ if (debug_flag)
+ fprintf(stderr, "Running /bin/sh %s\n", SSH_SYSTEM_RC);
+
+ f = popen("/bin/sh " SSH_SYSTEM_RC, "w");
+ if (f)
+ {
+ if (auth_proto != NULL && auth_data != NULL)
+ fprintf(f, "%s %s\n", auth_proto, auth_data);
+ pclose(f);
+ }
+ else
+ fprintf(stderr, "Could not run %s\n", SSH_SYSTEM_RC);
+ }
+#ifdef XAUTH_PATH
+ else
+ {
+ /* Add authority data to .Xauthority if appropriate. */
+ if (auth_proto != NULL && auth_data != NULL)
+ {
+ if (debug_flag)
+ fprintf(stderr, "Running %.100s add %.100s %.100s %.100s\n",
+ XAUTH_PATH, display, auth_proto, auth_data);
+
+ f = popen(XAUTH_PATH " -q -", "w");
+ if (f)
+ {
+ fprintf(f, "add %s %s %s\n", display, auth_proto, auth_data);
+ fclose(f);
+ }
+ else
+ fprintf(stderr, "Could not run %s -q -\n", XAUTH_PATH);
+ }
+ }
+#endif /* XAUTH_PATH */
+
+ /* Get the last component of the shell name. */
+ cp = strrchr(shell, '/');
+ if (cp)
+ cp++;
+ else
+ cp = shell;
+ }
+
+ /* If we have no command, execute the shell. In this case, the shell name
+ to be passed in argv[0] is preceded by '-' to indicate that this is
+ a login shell. */
+ if (!command)
+ {
+ if(!options.use_login) {
+ char buf[256];
+
+ /* Check for mail if we have a tty and it was enabled in server options. */
+ if (ttyname && options.check_mail) {
+ char *mailbox;
+ struct stat mailstat;
+ mailbox = getenv("MAIL");
+ if(mailbox != NULL) {
+ if(stat(mailbox, &mailstat) != 0 || mailstat.st_size == 0) {
+ printf("No mail.\n");
+ } else if(mailstat.st_mtime < mailstat.st_atime) {
+ printf("You have mail.\n");
+ } else {
+ printf("You have new mail.\n");
+ }
+ }
+ }
+ /* Start the shell. Set initial character to '-'. */
+ buf[0] = '-';
+ strncpy(buf + 1, cp, sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = 0;
+ /* Execute the shell. */
+ argv[0] = buf;
+ argv[1] = NULL;
+ execve(shell, argv, env);
+ /* Executing the shell failed. */
+ perror(shell);
+ exit(1);
+
+ } else {
+ /* Launch login(1). */
+
+ execl("/usr/bin/login", "login", "-h", get_remote_ipaddr(), "-p", "-f", "--", pw->pw_name, NULL);
+
+ /* Login couldn't be executed, die. */
+
+ perror("login");
+ exit(1);
+ }
+ }
+
+ /* Execute the command using the user's shell. This uses the -c option
+ to execute the command. */
+ argv[0] = (char *)cp;
+ argv[1] = "-c";
+ argv[2] = (char *)command;
+ argv[3] = NULL;
+ execve(shell, argv, env);
+ perror(shell);
+ exit(1);
+}
diff --git a/sshd.init b/sshd.init
new file mode 100755
index 00000000..b36b57aa
--- /dev/null
+++ b/sshd.init
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# Init file for OpenSSH sshd
+#
+# chkconfig: 2345 55 25
+# description: OpenSSH server daemon
+#
+# processname: sshd
+# config: /etc/ssh/ssh_host_key
+# config: /etc/ssh/ssh_host_key.pub
+# config: /etc/ssh/ssh_random_seed
+# config: /etc/ssh/sshd_config
+# pidfile: /var/run/sshd.pid
+
+# source function library
+. /etc/rc.d/init.d/functions
+
+RETVAL=0
+
+case "$1" in
+ start)
+ echo -n "Starting sshd: "
+ daemon /usr/sbin/sshd
+ RETVAL=$?
+ [ $RETVAL -eq 0 ] && touch /var/lock/subsys/sshd
+ echo
+ ;;
+ stop)
+ echo -n "Shutting down sshd: "
+ killproc sshd
+ RETVAL=$?
+ [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/sshd
+ echo
+ ;;
+ restart)
+ $0 stop
+ $0 start
+ RETVAL=$?
+ ;;
+ status)
+ status sshd
+ RETVAL=$?
+ ;;
+ *)
+ echo "Usage: sshd {start|stop|restart|status}"
+ exit 1
+esac
+
+exit $RETVAL
diff --git a/sshd_config b/sshd_config
new file mode 100644
index 00000000..97f6f8e1
--- /dev/null
+++ b/sshd_config
@@ -0,0 +1,44 @@
+# This is ssh server systemwide configuration file.
+
+Port 22
+ListenAddress 0.0.0.0
+HostKey /etc/ssh/ssh_host_key
+ServerKeyBits 768
+LoginGraceTime 600
+KeyRegenerationInterval 3600
+PermitRootLogin yes
+#
+# Don't read ~/.rhosts and ~/.shosts files
+IgnoreRhosts yes
+StrictModes yes
+QuietMode no
+X11Forwarding yes
+X11DisplayOffset 10
+FascistLogging no
+PrintMotd yes
+KeepAlive yes
+SyslogFacility AUTH
+RhostsAuthentication no
+#
+# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
+RhostsRSAAuthentication no
+#
+RSAAuthentication yes
+
+# To disable tunneled clear text passwords, change to no here!
+PasswordAuthentication yes
+PermitEmptyPasswords no
+# Uncomment to disable s/key passwords
+#SkeyAuthentication no
+
+# To change Kerberos options
+#KerberosAuthentication no
+#KerberosOrLocalPasswd yes
+#AFSTokenPassing no
+#KerberosTicketCleanup no
+
+# Kerberos TGT Passing does only work with the AFS kaserver
+#KerberosTgtPassing yes
+
+#CheckMail yes
+#UseLogin no
diff --git a/strlcpy.c b/strlcpy.c
new file mode 100644
index 00000000..300a28bc
--- /dev/null
+++ b/strlcpy.c
@@ -0,0 +1,68 @@
+/* $OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $ */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t strlcpy(dst, src, siz)
+ char *dst;
+ const char *src;
+ size_t siz;
+{
+ register char *d = dst;
+ register const char *s = src;
+ register size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0 && --n != 0) {
+ do {
+ if ((*d++ = *s++) == 0)
+ break;
+ } while (--n != 0);
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}
diff --git a/strlcpy.h b/strlcpy.h
new file mode 100644
index 00000000..824df300
--- /dev/null
+++ b/strlcpy.h
@@ -0,0 +1,4 @@
+#ifndef _STRLCPY_H
+#define _STRLCPY_H
+size_t strlcpy(char *dst, const char *src, size_t siz);
+#endif /* _STRLCPY_H */
diff --git a/tildexpand.c b/tildexpand.c
new file mode 100644
index 00000000..e4b57091
--- /dev/null
+++ b/tildexpand.c
@@ -0,0 +1,70 @@
+/*
+
+tildexpand.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ All rights reserved
+
+Created: Wed Jul 12 01:07:36 1995 ylo
+
+*/
+
+#include "includes.h"
+RCSID("$Id: tildexpand.c,v 1.1 1999/10/27 03:42:46 damien Exp $");
+
+#include "xmalloc.h"
+#include "ssh.h"
+
+/* Expands tildes in the file name. Returns data allocated by xmalloc.
+ Warning: this calls getpw*. */
+
+char *tilde_expand_filename(const char *filename, uid_t my_uid)
+{
+ const char *cp;
+ unsigned int userlen;
+ char *expanded;
+ struct passwd *pw;
+ char user[100];
+
+ /* Return immediately if no tilde. */
+ if (filename[0] != '~')
+ return xstrdup(filename);
+
+ /* Skip the tilde. */
+ filename++;
+
+ /* Find where the username ends. */
+ cp = strchr(filename, '/');
+ if (cp)
+ userlen = cp - filename; /* Have something after username. */
+ else
+ userlen = strlen(filename); /* Nothign after username. */
+ if (userlen == 0)
+ pw = getpwuid(my_uid); /* Own home directory. */
+ else
+ {
+ /* Tilde refers to someone elses home directory. */
+ if (userlen > sizeof(user) - 1)
+ fatal("User name after tilde too long.");
+ memcpy(user, filename, userlen);
+ user[userlen] = 0;
+ pw = getpwnam(user);
+ }
+
+ /* Check that we found the user. */
+ if (!pw)
+ fatal("Unknown user %100s.", user);
+
+ /* If referring to someones home directory, return it now. */
+ if (!cp)
+ { /* Only home directory specified */
+ return xstrdup(pw->pw_dir);
+ }
+
+ /* Build a path combining the specified directory and path. */
+ expanded = xmalloc(strlen(pw->pw_dir) + strlen(cp + 1) + 2);
+ sprintf(expanded, "%s/%s", pw->pw_dir, cp + 1);
+ return expanded;
+}
diff --git a/ttymodes.c b/ttymodes.c
new file mode 100644
index 00000000..cbb7f2f6
--- /dev/null
+++ b/ttymodes.c
@@ -0,0 +1,359 @@
+/*
+
+ttymodes.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ All rights reserved
+
+Created: Tue Mar 21 15:59:15 1995 ylo
+
+Encoding and decoding of terminal modes in a portable way.
+Much of the format is defined in ttymodes.h; it is included multiple times
+into this file with the appropriate macro definitions to generate the
+suitable code.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: ttymodes.c,v 1.1 1999/10/27 03:42:46 damien Exp $");
+
+#include "packet.h"
+#include "ssh.h"
+
+#define TTY_OP_END 0
+#define TTY_OP_ISPEED 192 /* int follows */
+#define TTY_OP_OSPEED 193 /* int follows */
+
+/* Converts POSIX speed_t to a baud rate. The values of the constants
+ for speed_t are not themselves portable. */
+
+static int speed_to_baud(speed_t speed)
+{
+ switch (speed)
+ {
+ case B0:
+ return 0;
+ case B50:
+ return 50;
+ case B75:
+ return 75;
+ case B110:
+ return 110;
+ case B134:
+ return 134;
+ case B150:
+ return 150;
+ case B200:
+ return 200;
+ case B300:
+ return 300;
+ case B600:
+ return 600;
+ case B1200:
+ return 1200;
+ case B1800:
+ return 1800;
+ case B2400:
+ return 2400;
+ case B4800:
+ return 4800;
+ case B9600:
+ return 9600;
+
+#ifdef B19200
+ case B19200:
+ return 19200;
+#else /* B19200 */
+#ifdef EXTA
+ case EXTA:
+ return 19200;
+#endif /* EXTA */
+#endif /* B19200 */
+
+#ifdef B38400
+ case B38400:
+ return 38400;
+#else /* B38400 */
+#ifdef EXTB
+ case EXTB:
+ return 38400;
+#endif /* EXTB */
+#endif /* B38400 */
+
+#ifdef B7200
+ case B7200:
+ return 7200;
+#endif /* B7200 */
+#ifdef B14400
+ case B14400:
+ return 14400;
+#endif /* B14400 */
+#ifdef B28800
+ case B28800:
+ return 28800;
+#endif /* B28800 */
+#ifdef B57600
+ case B57600:
+ return 57600;
+#endif /* B57600 */
+#ifdef B76800
+ case B76800:
+ return 76800;
+#endif /* B76800 */
+#ifdef B115200
+ case B115200:
+ return 115200;
+#endif /* B115200 */
+#ifdef B230400
+ case B230400:
+ return 230400;
+#endif /* B230400 */
+ default:
+ return 9600;
+ }
+}
+
+/* Converts a numeric baud rate to a POSIX speed_t. */
+
+static speed_t baud_to_speed(int baud)
+{
+ switch (baud)
+ {
+ case 0:
+ return B0;
+ case 50:
+ return B50;
+ case 75:
+ return B75;
+ case 110:
+ return B110;
+ case 134:
+ return B134;
+ case 150:
+ return B150;
+ case 200:
+ return B200;
+ case 300:
+ return B300;
+ case 600:
+ return B600;
+ case 1200:
+ return B1200;
+ case 1800:
+ return B1800;
+ case 2400:
+ return B2400;
+ case 4800:
+ return B4800;
+ case 9600:
+ return B9600;
+
+#ifdef B19200
+ case 19200:
+ return B19200;
+#else /* B19200 */
+#ifdef EXTA
+ case 19200:
+ return EXTA;
+#endif /* EXTA */
+#endif /* B19200 */
+
+#ifdef B38400
+ case 38400:
+ return B38400;
+#else /* B38400 */
+#ifdef EXTB
+ case 38400:
+ return EXTB;
+#endif /* EXTB */
+#endif /* B38400 */
+
+#ifdef B7200
+ case 7200:
+ return B7200;
+#endif /* B7200 */
+#ifdef B14400
+ case 14400:
+ return B14400;
+#endif /* B14400 */
+#ifdef B28800
+ case 28800:
+ return B28800;
+#endif /* B28800 */
+#ifdef B57600
+ case 57600:
+ return B57600;
+#endif /* B57600 */
+#ifdef B76800
+ case 76800:
+ return B76800;
+#endif /* B76800 */
+#ifdef B115200
+ case 115200:
+ return B115200;
+#endif /* B115200 */
+#ifdef B230400
+ case 230400:
+ return B230400;
+#endif /* B230400 */
+ default:
+ return B9600;
+ }
+}
+
+/* Encodes terminal modes for the terminal referenced by fd in a portable
+ manner, and appends the modes to a packet being constructed. */
+
+void tty_make_modes(int fd)
+{
+ struct termios tio;
+ int baud;
+
+ /* Get the modes. */
+ if (tcgetattr(fd, &tio) < 0)
+ {
+ packet_put_char(TTY_OP_END);
+ log("tcgetattr: %.100s", strerror(errno));
+ return;
+ }
+
+ /* Store input and output baud rates. */
+ baud = speed_to_baud(cfgetospeed(&tio));
+ packet_put_char(TTY_OP_OSPEED);
+ packet_put_int(baud);
+ baud = speed_to_baud(cfgetispeed(&tio));
+ packet_put_char(TTY_OP_ISPEED);
+ packet_put_int(baud);
+
+ /* Store values of mode flags. */
+#define TTYCHAR(NAME, OP) \
+ packet_put_char(OP); packet_put_char(tio.c_cc[NAME]);
+#define TTYMODE(NAME, FIELD, OP) \
+ packet_put_char(OP); packet_put_char((tio.FIELD & NAME) != 0);
+#define SGTTYCHAR(NAME, OP)
+#define SGTTYMODE(NAME, FIELD, OP)
+#define SGTTYMODEN(NAME, FIELD, OP)
+
+#include "ttymodes.h"
+
+#undef TTYCHAR
+#undef TTYMODE
+#undef SGTTYCHAR
+#undef SGTTYMODE
+#undef SGTTYMODEN
+
+ /* Mark end of mode data. */
+ packet_put_char(TTY_OP_END);
+}
+
+/* Decodes terminal modes for the terminal referenced by fd in a portable
+ manner from a packet being read. */
+
+void tty_parse_modes(int fd, int *n_bytes_ptr)
+{
+ struct termios tio;
+ int opcode, baud;
+ int n_bytes = 0;
+ int failure = 0;
+
+ /* Get old attributes for the terminal. We will modify these flags.
+ I am hoping that if there are any machine-specific modes, they will
+ initially have reasonable values. */
+ if (tcgetattr(fd, &tio) < 0)
+ failure = -1;
+
+ for (;;)
+ {
+ n_bytes += 1;
+ opcode = packet_get_char();
+ switch (opcode)
+ {
+ case TTY_OP_END:
+ goto set;
+
+ case TTY_OP_ISPEED:
+ n_bytes += 4;
+ baud = packet_get_int();
+ if (failure != -1 && cfsetispeed(&tio, baud_to_speed(baud)) < 0)
+ error("cfsetispeed failed for %d", baud);
+ break;
+
+ case TTY_OP_OSPEED:
+ n_bytes += 4;
+ baud = packet_get_int();
+ if (failure != -1 && cfsetospeed(&tio, baud_to_speed(baud)) < 0)
+ error("cfsetospeed failed for %d", baud);
+ break;
+
+#define TTYCHAR(NAME, OP) \
+ case OP: \
+ n_bytes += 1; \
+ tio.c_cc[NAME] = packet_get_char(); \
+ break;
+#define TTYMODE(NAME, FIELD, OP) \
+ case OP: \
+ n_bytes += 1; \
+ if (packet_get_char()) \
+ tio.FIELD |= NAME; \
+ else \
+ tio.FIELD &= ~NAME; \
+ break;
+#define SGTTYCHAR(NAME, OP)
+#define SGTTYMODE(NAME, FIELD, OP)
+#define SGTTYMODEN(NAME, FIELD, OP)
+
+#include "ttymodes.h"
+
+#undef TTYCHAR
+#undef TTYMODE
+#undef SGTTYCHAR
+#undef SGTTYMODE
+#undef SGTTYMODEN
+
+ default:
+ debug("Ignoring unsupported tty mode opcode %d (0x%x)",
+ opcode, opcode);
+ /* Opcodes 0 to 127 are defined to have a one-byte argument. */
+ if (opcode >= 0 && opcode < 128)
+ {
+ n_bytes += 1;
+ (void)packet_get_char();
+ break;
+ }
+ else
+ {
+ /* Opcodes 128 to 159 are defined to have an integer argument. */
+ if (opcode >= 128 && opcode < 160)
+ {
+ n_bytes += 4;
+ (void)packet_get_int();
+ break;
+ }
+ }
+ /* It is a truly undefined opcode (160 to 255). We have no idea
+ about its arguments. So we must stop parsing. Note that some
+ data may be left in the packet; hopefully there is nothing more
+ coming after the mode data. */
+ log("parse_tty_modes: unknown opcode %d", opcode);
+ packet_integrity_check(0, 1, SSH_CMSG_REQUEST_PTY);
+ goto set;
+ }
+ }
+
+ set:
+ if (*n_bytes_ptr != n_bytes)
+ {
+ *n_bytes_ptr = n_bytes;
+ return; /* Don't process bytes passed */
+ }
+
+ if (failure == -1)
+ return; /* Packet parsed ok but tty stuff failed */
+
+ /* Set the new modes for the terminal. */
+ if (tcsetattr(fd, TCSANOW, &tio) < 0)
+ log("Setting tty modes failed: %.100s", strerror(errno));
+ return;
+}
diff --git a/ttymodes.h b/ttymodes.h
new file mode 100644
index 00000000..2a33eb78
--- /dev/null
+++ b/ttymodes.h
@@ -0,0 +1,138 @@
+/*
+
+ttymodes.h
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+ SGTTY stuff contributed by Janne Snabb <snabb@niksula.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ All rights reserved
+
+Created: Tue Mar 21 15:42:09 1995 ylo
+
+*/
+
+/* RCSID("$Id: ttymodes.h,v 1.1 1999/10/27 03:42:46 damien Exp $"); */
+
+/* The tty mode description is a stream of bytes. The stream consists of
+ opcode-arguments pairs. It is terminated by opcode TTY_OP_END (0).
+ Opcodes 1-127 have one-byte arguments. Opcodes 128-159 have integer
+ arguments. Opcodes 160-255 are not yet defined, and cause parsing to
+ stop (they should only be used after any other data).
+
+ The client puts in the stream any modes it knows about, and the
+ server ignores any modes it does not know about. This allows some degree
+ of machine-independence, at least between systems that use a posix-like
+ tty interface. The protocol can support other systems as well, but might
+ require reimplementing as mode names would likely be different. */
+
+/* Some constants and prototypes are defined in packet.h; this file
+ is only intended for including from ttymodes.h. */
+
+/* termios macro */ /* sgtty macro */
+/* name, op */
+TTYCHAR(VINTR, 1) SGTTYCHAR(tiotc.t_intrc, 1)
+TTYCHAR(VQUIT, 2) SGTTYCHAR(tiotc.t_quitc, 2)
+TTYCHAR(VERASE, 3) SGTTYCHAR(tio.sg_erase, 3)
+#if defined(VKILL)
+TTYCHAR(VKILL, 4) SGTTYCHAR(tio.sg_kill, 4)
+#endif /* VKILL */
+TTYCHAR(VEOF, 5) SGTTYCHAR(tiotc.t_eofc, 5)
+#if defined(VEOL)
+TTYCHAR(VEOL, 6) SGTTYCHAR(tiotc.t_brkc, 6)
+#endif /* VEOL */
+#ifdef VEOL2 /* n/a */
+TTYCHAR(VEOL2, 7)
+#endif /* VEOL2 */
+TTYCHAR(VSTART, 8) SGTTYCHAR(tiotc.t_startc, 8)
+TTYCHAR(VSTOP, 9) SGTTYCHAR(tiotc.t_stopc, 9)
+#if defined(VSUSP)
+TTYCHAR(VSUSP, 10) SGTTYCHAR(tioltc.t_suspc, 10)
+#endif /* VSUSP */
+#if defined(VDSUSP)
+TTYCHAR(VDSUSP, 11) SGTTYCHAR(tioltc.t_dsuspc, 11)
+#endif /* VDSUSP */
+#if defined(VREPRINT)
+TTYCHAR(VREPRINT, 12) SGTTYCHAR(tioltc.t_rprntc, 12)
+#endif /* VREPRINT */
+#if defined(VWERASE)
+TTYCHAR(VWERASE, 13) SGTTYCHAR(tioltc.t_werasc, 13)
+#endif /* VWERASE */
+#if defined(VLNEXT)
+TTYCHAR(VLNEXT, 14) SGTTYCHAR(tioltc.t_lnextc, 14)
+#endif /* VLNEXT */
+#if defined(VFLUSH)
+TTYCHAR(VFLUSH, 15) SGTTYCHAR(tioltc.t_flushc, 15)
+#endif /* VFLUSH */
+#ifdef VSWTCH
+TTYCHAR(VSWTCH, 16) /* n/a */
+#endif /* VSWTCH */
+#if defined(VSTATUS)
+TTYCHAR(VSTATUS, 17) SGTTYCHAR(tiots.tc_statusc, 17)
+#endif /* VSTATUS */
+#ifdef VDISCARD
+TTYCHAR(VDISCARD, 18) /* n/a */
+#endif /* VDISCARD */
+
+/* name, field, op */
+TTYMODE(IGNPAR, c_iflag, 30) /* n/a */
+TTYMODE(PARMRK, c_iflag, 31) /* n/a */
+TTYMODE(INPCK, c_iflag, 32) SGTTYMODEN(ANYP, tio.sg_flags, 32)
+TTYMODE(ISTRIP, c_iflag, 33) SGTTYMODEN(LPASS8, tiolm, 33)
+TTYMODE(INLCR, c_iflag, 34) /* n/a */
+TTYMODE(IGNCR, c_iflag, 35) /* n/a */
+TTYMODE(ICRNL, c_iflag, 36) SGTTYMODE(CRMOD, tio.sg_flags, 36)
+#if defined(IUCLC)
+TTYMODE(IUCLC, c_iflag, 37) SGTTYMODE(LCASE, tio.sg_flags, 37)
+#endif
+TTYMODE(IXON, c_iflag, 38) /* n/a */
+TTYMODE(IXANY, c_iflag, 39) SGTTYMODEN(LDECCTQ, tiolm, 39)
+TTYMODE(IXOFF, c_iflag, 40) SGTTYMODE(TANDEM, tio.sg_flags, 40)
+#ifdef IMAXBEL
+TTYMODE(IMAXBEL,c_iflag, 41) /* n/a */
+#endif /* IMAXBEL */
+
+TTYMODE(ISIG, c_lflag, 50) /* n/a */
+TTYMODE(ICANON, c_lflag, 51) SGTTYMODEN(CBREAK, tio.sg_flags, 51)
+#ifdef XCASE
+TTYMODE(XCASE, c_lflag, 52) /* n/a */
+#endif
+TTYMODE(ECHO, c_lflag, 53) SGTTYMODE(ECHO, tio.sg_flags, 53)
+TTYMODE(ECHOE, c_lflag, 54) SGTTYMODE(LCRTERA, tiolm, 54)
+TTYMODE(ECHOK, c_lflag, 55) SGTTYMODE(LCRTKIL, tiolm, 55)
+TTYMODE(ECHONL, c_lflag, 56) /* n/a */
+TTYMODE(NOFLSH, c_lflag, 57) SGTTYMODE(LNOFLSH, tiolm, 57)
+TTYMODE(TOSTOP, c_lflag, 58) SGTTYMODE(LTOSTOP, tiolm, 58)
+#ifdef IEXTEN
+TTYMODE(IEXTEN, c_lflag, 59) /* n/a */
+#endif /* IEXTEN */
+#if defined(ECHOCTL)
+TTYMODE(ECHOCTL,c_lflag, 60) SGTTYMODE(LCTLECH, tiolm, 60)
+#endif /* ECHOCTL */
+#ifdef ECHOKE
+TTYMODE(ECHOKE, c_lflag, 61) /* n/a */
+#endif /* ECHOKE */
+#if defined(PENDIN)
+TTYMODE(PENDIN, c_lflag, 62) SGTTYMODE(LPENDIN, tiolm, 62)
+#endif /* PENDIN */
+
+TTYMODE(OPOST, c_oflag, 70) /* n/a */
+#if defined(OLCUC)
+TTYMODE(OLCUC, c_oflag, 71) SGTTYMODE(LCASE, tio.sg_flags, 71)
+#endif
+TTYMODE(ONLCR, c_oflag, 72) SGTTYMODE(CRMOD, tio.sg_flags, 72)
+#ifdef OCRNL
+TTYMODE(OCRNL, c_oflag, 73) /* n/a */
+#endif
+#ifdef ONOCR
+TTYMODE(ONOCR, c_oflag, 74) /* n/a */
+#endif
+#ifdef ONLRET
+TTYMODE(ONLRET, c_oflag, 75) /* n/a */
+#endif
+
+TTYMODE(CS7, c_cflag, 90) /* n/a */
+TTYMODE(CS8, c_cflag, 91) SGTTYMODE(LPASS8, tiolm, 91)
+TTYMODE(PARENB, c_cflag, 92) /* n/a */
+TTYMODE(PARODD, c_cflag, 93) SGTTYMODE(ODDP, tio.sg_flags, 93)
+
diff --git a/uidswap.c b/uidswap.c
new file mode 100644
index 00000000..0eb1fd08
--- /dev/null
+++ b/uidswap.c
@@ -0,0 +1,95 @@
+/*
+
+uidswap.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ All rights reserved
+
+Created: Sat Sep 9 01:56:14 1995 ylo
+
+Code for uid-swapping.
+
+*/
+
+#include "includes.h"
+RCSID("$Id: uidswap.c,v 1.1 1999/10/27 03:42:46 damien Exp $");
+
+#include "ssh.h"
+#include "uidswap.h"
+
+/* Note: all these functions must work in all of the following cases:
+
+ 1. euid=0, ruid=0
+ 2. euid=0, ruid!=0
+ 3. euid!=0, ruid!=0
+
+ Additionally, they must work regardless of whether the system has
+ POSIX saved uids or not. */
+
+#ifdef _POSIX_SAVED_IDS
+/* Lets assume that posix saved ids also work with seteuid, even though that
+ is not part of the posix specification. */
+#define SAVED_IDS_WORK_WITH_SETEUID
+#endif /* _POSIX_SAVED_IDS */
+
+/* Saved effective uid. */
+static uid_t saved_euid = 0;
+
+/* Temporarily changes to the given uid. If the effective user id is not
+ root, this does nothing. This call cannot be nested. */
+
+void temporarily_use_uid(uid_t uid)
+{
+#ifdef SAVED_IDS_WORK_WITH_SETEUID
+
+ /* Save the current euid. */
+ saved_euid = geteuid();
+
+ /* Set the effective uid to the given (unprivileged) uid. */
+ if (seteuid(uid) == -1)
+ debug("seteuid %d: %.100s", (int)uid, strerror(errno));
+
+#else /* SAVED_IDS_WORK_WITH_SETUID */
+
+ /* Propagate the privileged uid to all of our uids. */
+ if (setuid(geteuid()) < 0)
+ debug("setuid %d: %.100s", (int)geteuid(), strerror(errno));
+
+ /* Set the effective uid to the given (unprivileged) uid. */
+ if (seteuid(uid) == -1)
+ debug("seteuid %d: %.100s", (int)uid, strerror(errno));
+
+#endif /* SAVED_IDS_WORK_WITH_SETEUID */
+
+}
+
+/* Restores to the original uid. */
+
+void restore_uid()
+{
+#ifdef SAVED_IDS_WORK_WITH_SETEUID
+
+ /* Set the effective uid back to the saved uid. */
+ if (seteuid(saved_euid) < 0)
+ debug("seteuid %d: %.100s", (int)saved_euid, strerror(errno));
+
+#else /* SAVED_IDS_WORK_WITH_SETEUID */
+
+ /* We are unable to restore the real uid to its unprivileged value. */
+ /* Propagate the real uid (usually more privileged) to effective uid
+ as well. */
+ setuid(getuid());
+
+#endif /* SAVED_IDS_WORK_WITH_SETEUID */
+}
+
+/* Permanently sets all uids to the given uid. This cannot be called while
+ temporarily_use_uid is effective. */
+
+void permanently_set_uid(uid_t uid)
+{
+ if (setuid(uid) < 0)
+ debug("setuid %d: %.100s", (int)uid, strerror(errno));
+}
diff --git a/uidswap.h b/uidswap.h
new file mode 100644
index 00000000..af4f924f
--- /dev/null
+++ b/uidswap.h
@@ -0,0 +1,30 @@
+/*
+
+uidswap.h
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ All rights reserved
+
+Created: Sat Sep 9 01:43:15 1995 ylo
+Last modified: Sat Sep 9 02:34:04 1995 ylo
+
+*/
+
+#ifndef UIDSWAP_H
+#define UIDSWAP_H
+
+/* Temporarily changes to the given uid. If the effective user id is not
+ root, this does nothing. This call cannot be nested. */
+void temporarily_use_uid(uid_t uid);
+
+/* Restores the original effective user id after temporarily_use_uid().
+ This should only be called while temporarily_use_uid is effective. */
+void restore_uid();
+
+/* Permanently sets all uids to the given uid. This cannot be called while
+ temporarily_use_uid is effective. This must also clear any saved uids. */
+void permanently_set_uid(uid_t uid);
+
+#endif /* UIDSWAP_H */
diff --git a/version.h b/version.h
new file mode 100644
index 00000000..8d2fc5c2
--- /dev/null
+++ b/version.h
@@ -0,0 +1 @@
+#define SSH_VERSION "OpenSSH-1.2"
diff --git a/xmalloc.c b/xmalloc.c
new file mode 100644
index 00000000..b536f9d1
--- /dev/null
+++ b/xmalloc.c
@@ -0,0 +1,56 @@
+/*
+
+xmalloc.c
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ All rights reserved
+
+Created: Mon Mar 20 21:23:10 1995 ylo
+
+Versions of malloc and friends that check their results, and never return
+failure (they call fatal if they encounter an error).
+
+*/
+
+#include "includes.h"
+RCSID("$Id: xmalloc.c,v 1.1 1999/10/27 03:42:46 damien Exp $");
+
+#include "ssh.h"
+
+void *xmalloc(size_t size)
+{
+ void *ptr = malloc(size);
+ if (ptr == NULL)
+ fatal("xmalloc: out of memory (allocating %d bytes)", (int)size);
+ return ptr;
+}
+
+void *xrealloc(void *ptr, size_t new_size)
+{
+ void *new_ptr;
+
+ if (ptr == NULL)
+ fatal("xrealloc: NULL pointer given as argument");
+ new_ptr = realloc(ptr, new_size);
+ if (new_ptr == NULL)
+ fatal("xrealloc: out of memory (new_size %d bytes)", (int)new_size);
+ return new_ptr;
+}
+
+void xfree(void *ptr)
+{
+ if (ptr == NULL)
+ fatal("xfree: NULL pointer given as argument");
+ free(ptr);
+}
+
+char *xstrdup(const char *str)
+{
+ int len = strlen(str) + 1;
+
+ char *cp = xmalloc(len);
+ strlcpy(cp, str, len);
+ return cp;
+}
diff --git a/xmalloc.h b/xmalloc.h
new file mode 100644
index 00000000..7a9610e7
--- /dev/null
+++ b/xmalloc.h
@@ -0,0 +1,34 @@
+/*
+
+xmalloc.h
+
+Author: Tatu Ylonen <ylo@cs.hut.fi>
+
+Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ All rights reserved
+
+Created: Mon Mar 20 22:09:17 1995 ylo
+
+Versions of malloc and friends that check their results, and never return
+failure (they call fatal if they encounter an error).
+
+*/
+
+/* RCSID("$Id: xmalloc.h,v 1.1 1999/10/27 03:42:46 damien Exp $"); */
+
+#ifndef XMALLOC_H
+#define XMALLOC_H
+
+/* Like malloc, but calls fatal() if out of memory. */
+void *xmalloc(size_t size);
+
+/* Like realloc, but calls fatal() if out of memory. */
+void *xrealloc(void *ptr, size_t new_size);
+
+/* Frees memory allocated using xmalloc or xrealloc. */
+void xfree(void *ptr);
+
+/* Allocates memory using xmalloc, and copies the string into that memory. */
+char *xstrdup(const char *str);
+
+#endif /* XMALLOC_H */