Initial revision

This commit is contained in:
Damien Miller 1999-10-27 13:42:43 +10:00
commit d4a8b7e34d
97 changed files with 26920 additions and 0 deletions

70
COPYING.Ylonen Normal file
View File

@ -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.

578
ChangeLog Normal file
View File

@ -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.

20
ChangeLog.linux Normal file
View File

@ -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

13
Makefile Normal file
View File

@ -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>

50
Makefile.GNU Normal file
View File

@ -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/*

11
Makefile.inc Normal file
View File

@ -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

164
OVERVIEW Normal file
View File

@ -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

563
README Normal file
View File

@ -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.

44
README.openssh Normal file
View File

@ -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).

1780
RFC.nroff Normal file

File diff suppressed because it is too large Load Diff

209
auth-krb4.c Normal file
View File

@ -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 */

209
auth-passwd.c Normal file
View File

@ -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 */
}

83
auth-rh-rsa.c Normal file
View File

@ -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;
}

298
auth-rhosts.c Normal file
View File

@ -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;
}

478
auth-rsa.c Normal file
View File

@ -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;
}

149
auth-skey.c Normal file
View File

@ -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;
}

565
authfd.c Normal file
View File

@ -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;
}

102
authfd.h Normal file
View File

@ -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 */

350
authfile.c Normal file
View File

@ -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;
}

141
bufaux.c Normal file
View File

@ -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);
}

51
bufaux.h Normal file
View File

@ -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 */

150
buffer.c Normal file
View File

@ -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");
}

66
buffer.h Normal file
View File

@ -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 */

234
canohost.c Normal file
View File

@ -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);
}

1500
channels.c Normal file

File diff suppressed because it is too large Load Diff

41
channels.h Normal file
View File

@ -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

304
cipher.c Normal file
View File

@ -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);
}
}

84
cipher.h Normal file
View File

@ -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 */

924
clientloop.c Normal file
View File

@ -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;
}

10
compat.c Normal file
View File

@ -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;
}

7
compat.h Normal file
View File

@ -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

160
compress.c Normal file
View File

@ -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);
}
}
}

46
compress.h Normal file
View File

@ -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 */

120
crc32.c Normal file
View File

@ -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;
}

25
crc32.h Normal file
View File

@ -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 */

180
deattack.c Normal file
View File

@ -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);
}

27
deattack.h Normal file
View File

@ -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

64
getput.h Normal file
View File

@ -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 */

108
helper.c Normal file
View File

@ -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);
}
}

43
helper.h Normal file
View File

@ -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 */

279
hostfile.c Normal file
View File

@ -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;
}

78
includes.h Normal file
View File

@ -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 */

138
log-client.c Normal file
View File

@ -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. */

233
log-server.c Normal file
View File

@ -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);
}

118
login.c Normal file
View File

@ -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, "", "");
}

78
match.c Normal file
View File

@ -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*/
}

181
mktemp.c Normal file
View File

@ -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*/
}

7
mktemp.h Normal file
View File

@ -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 */

46
mpaux.c Normal file
View File

@ -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);
}

32
mpaux.h Normal file
View File

@ -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 */

187
nchan.c Normal file
View File

@ -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;
}

57
nchan.h Normal file
View File

@ -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

71
nchan.ms Normal file
View File

@ -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.

105
openssh.spec Normal file
View File

@ -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

762
packet.c Normal file
View File

@ -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;
}

166
packet.h Normal file
View File

@ -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 */

264
pty.c Normal file
View File

@ -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);
}

40
pty.h Normal file
View File

@ -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 */

258
radix.c Normal file
View File

@ -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 */

105
rc4.c Normal file
View File

@ -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++;
}
}

110
rc4.h Normal file
View File

@ -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 */

684
readconf.c Normal file
View File

@ -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 */
}

116
readconf.h Normal file
View File

@ -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 */

114
readpass.c Normal file
View File

@ -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;
}

164
rsa.c Normal file
View File

@ -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;
}

36
rsa.h Normal file
View File

@ -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 */

110
scp.1 Normal file
View File

@ -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

1220
scp.c Normal file

File diff suppressed because it is too large Load Diff

567
servconf.c Normal file
View File

@ -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);
}

86
servconf.h Normal file
View File

@ -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 */

644
serverloop.c Normal file
View File

@ -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*/
}

116
ssh-add.1 Normal file
View File

@ -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

254
ssh-add.c Normal file
View File

@ -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);
}

124
ssh-agent.1 Normal file
View File

@ -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

572
ssh-agent.c Normal file
View File

@ -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*/
}

155
ssh-keygen.1 Normal file
View File

@ -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

552
ssh-keygen.c Normal file
View File

@ -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);
}

966
ssh.1 Normal file
View File

@ -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

809
ssh.c Normal file
View File

@ -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);
}

589
ssh.h Normal file
View File

@ -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 */

7
ssh.pam Normal file
View File

@ -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

30
ssh_config Normal file
View File

@ -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 ~

1495
sshconnect.c Normal file

File diff suppressed because it is too large Load Diff

781
sshd.8 Normal file
View File

@ -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

2445
sshd.c Normal file

File diff suppressed because it is too large Load Diff

49
sshd.init Executable file
View File

@ -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

44
sshd_config Normal file
View File

@ -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

68
strlcpy.c Normal file
View File

@ -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 */
}

4
strlcpy.h Normal file
View File

@ -0,0 +1,4 @@
#ifndef _STRLCPY_H
#define _STRLCPY_H
size_t strlcpy(char *dst, const char *src, size_t siz);
#endif /* _STRLCPY_H */

70
tildexpand.c Normal file
View File

@ -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;
}

359
ttymodes.c Normal file
View File

@ -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;
}

138
ttymodes.h Normal file
View File

@ -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)

95
uidswap.c Normal file
View File

@ -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));
}

30
uidswap.h Normal file
View File

@ -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 */

1
version.h Normal file
View File

@ -0,0 +1 @@
#define SSH_VERSION "OpenSSH-1.2"

56
xmalloc.c Normal file
View File

@ -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;
}

34
xmalloc.h Normal file
View File

@ -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 */