Initial revision
This commit is contained in:
commit
d4a8b7e34d
|
@ -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.
|
|
@ -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.
|
||||
|
|
@ -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
|
|
@ -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>
|
|
@ -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/*
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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.
|
|
@ -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).
|
||||
|
|
@ -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 */
|
|
@ -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 */
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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");
|
||||
}
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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 */
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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. */
|
|
@ -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);
|
||||
}
|
|
@ -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, "", "");
|
||||
}
|
|
@ -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*/
|
||||
}
|
|
@ -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*/
|
||||
}
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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.
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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++;
|
||||
}
|
||||
}
|
|
@ -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 */
|
|
@ -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 */
|
||||
}
|
||||
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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*/
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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*/
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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
|
|
@ -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 ~
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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 */
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
#ifndef _STRLCPY_H
|
||||
#define _STRLCPY_H
|
||||
size_t strlcpy(char *dst, const char *src, size_t siz);
|
||||
#endif /* _STRLCPY_H */
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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)
|
||||
|
|
@ -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));
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
Loading…
Reference in New Issue