ssh.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
* Ssh client program. This program can be used to log into a remote machine.
* The software supports strong authentication, encryption, and forwarding
*
* 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".
*
* Copyright (c) 1999 Niels Provos. All rights reserved.
* Copyright (c) 2000, 2001, 2002 Markus Friedl. All rights reserved.
*
* Modified to work with SSL by Niels Provos <provos@citi.umich.edu>
* in Canada (German citizen).
*
* 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
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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.
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include "includes.h"
#pragma ident "%Z%%M% %I% %E% SMI"
#include "ssh.h"
#include "ssh1.h"
#include "ssh2.h"
#include "compat.h"
#include "cipher.h"
#include "xmalloc.h"
#include "packet.h"
#include "buffer.h"
#include "channels.h"
#include "key.h"
#include "authfd.h"
#include "authfile.h"
#include "pathnames.h"
#include "clientloop.h"
#include "log.h"
#include "readconf.h"
#include "sshconnect.h"
#include "tildexpand.h"
#include "dispatch.h"
#include "misc.h"
#include "kex.h"
#include "mac.h"
#include "sshtty.h"
#include "g11n.h"
#ifdef SMARTCARD
#include "scard.h"
#endif
#ifdef HAVE___PROGNAME
extern char *__progname;
#else
char *__progname;
#endif
/* Flag indicating whether IPv4 or IPv6. This can be set on the command line.
Default value is AF_UNSPEC means both IPv4 and IPv6. */
#ifdef IPV4_DEFAULT
#else
#endif
/* Flag indicating whether debug mode is on. This can be set on the command line. */
int debug_flag = 0;
/* Flag indicating whether a tty should be allocated */
int tty_flag = 0;
int no_tty_flag = 0;
int force_tty_flag = 0;
/* don't exec a shell */
int no_shell_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 passphrase 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.
*/
/* optional user configfile */
/*
* 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_storage hostaddr;
/* Private host keys. */
/* Original real UID. */
/* command to be executed */
/* Should we execute a command or invoke a subsystem? */
int subsystem_flag = 0;
/* # of replies received for global requests */
static int client_global_request_id = 0;
/* pid of proxycommand child process */
pid_t proxy_command_pid = 0;
/* Prints a help message to the user. This function never returns. */
static void
usage(void)
{
gettext("Usage: %s [options] host [command]\n"
"Options:\n"
" -l user Log in using this user name.\n"
" -F config Config file (default: ~/%s).\n"
" -A Enable authentication agent forwarding.\n"
" -a Disable authentication agent forwarding "
"(default).\n"
#ifdef AFS
" -k Disable Kerberos ticket and AFS token "
"forwarding.\n"
#endif /* AFS */
" -X Enable X11 connection forwarding.\n"
" -x Disable X11 connection forwarding (default).\n"
" -i file Identity for public key authentication "
#ifdef SMARTCARD
" -I reader Set smartcard reader.\n"
#endif
" -t Tty; allocate a tty even if command is given.\n"
" -T Do not allocate a tty.\n"
" -v Verbose; display verbose debugging messages.\n"
" Multiple -v increases verbosity.\n"
" -V Display version number only.\n"
" -q Quiet; don't display any warning messages.\n"
" -f Fork into background after authentication.\n"
" -e char Set escape character; ``none'' = disable "
"(default: ~).\n"
" -c cipher Select encryption algorithm\n"
" -m macs Specify MAC algorithms for protocol version 2.\n"
" -p port Connect to this port. Server must be "
"on the same port.\n"
" -L listen-port:host:port Forward local port to "
"remote address\n"
" -R listen-port:host:port Forward remote port to "
"local address\n"
" These cause %s to listen for connections "
"on a port, and\n"
" forward them to the other side by "
"connecting to host:port.\n"
" -D port Enable dynamic application-level "
"port forwarding.\n"
" -C Enable compression.\n"
" -N Do not execute a shell or command.\n"
" -g Allow remote hosts to connect to forwarded "
"ports.\n"
" -1 Force protocol version 1.\n"
" -2 Force protocol version 2.\n"
" -4 Use IPv4 only.\n"
" -6 Use IPv6 only.\n"
" -o 'option' Process the option as if it was read "
"from a configuration file.\n"
" -s Invoke command (mandatory) as SSH2 subsystem.\n"
" -b addr Local IP address.\n"),
exit(1);
}
static int ssh_session(void);
static int ssh_session2(void);
static void load_public_identity_files(void);
/*
* Main program for the ssh client.
*/
int
{
int i, opt, exit_status;
int dummy;
extern char *optarg;
init_rng();
/*
* Save the original real uid. It will be needed later (uid-swapping
* may clobber the real uid).
*/
original_real_uid = getuid();
/*
* 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).
*/
#ifdef HAVE_SETRLIMIT
/* If we are installed setuid root be careful to not drop core. */
if (original_real_uid != original_effective_uid) {
}
#endif
/* Get user data. */
if (!pw) {
log("You don't exist, go away!");
exit(1);
}
/* Take a copy of the returned structure. */
/*
* 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);
/* Initialize option structure to indicate that no values have been set. */
/* Parse command-line arguments. */
"1246ab:c:e:fgi:kl:m:no:p:qstvxACD:F:I:L:NPR:TVX")) != -1) {
switch (opt) {
case '1':
break;
case '2':
break;
case '4':
break;
case '6':
break;
case 'n':
stdin_null_flag = 1;
break;
case 'f':
stdin_null_flag = 1;
break;
case 'x':
options.forward_x11 = 0;
break;
case 'X':
break;
case 'g':
break;
case 'P': /* deprecated */
"been deprecated\n"));
break;
case 'a':
options.forward_agent = 0;
break;
case 'A':
break;
#ifdef AFS
case 'k':
options.afs_token_passing = 0;
break;
#endif
case 'i':
gettext("Warning: Identity file %s "
"does not exist.\n"), optarg);
break;
}
if (options.num_identity_files >=
fatal("Too many identity files specified "
"(max %d)", SSH_MAX_IDENTITY_FILES);
break;
case 'I':
#ifdef SMARTCARD
#else
#endif
break;
case 't':
if (tty_flag)
force_tty_flag = 1;
tty_flag = 1;
break;
case 'v':
if (0 == debug_flag) {
debug_flag = 1;
break;
} else
fatal("Too high debugging level.");
/* FALLTHROUGH */
case 'V':
gettext("%s, SSH protocols %d.%d/%d.%d, "
"OpenSSL 0x%8.8lx\n"),
SSLeay());
if (opt == 'V')
exit(0);
break;
case 'q':
break;
case 'e':
else {
gettext("Bad escape character '%s'.\n"),
optarg);
exit(1);
}
break;
case 'c':
if (ciphers_valid(optarg)) {
/* SSH2 only */
} else {
/* SSH1 only */
gettext("Unknown cipher "
"type '%s'\n"),
optarg);
exit(1);
}
else
}
break;
case 'm':
else {
gettext("Unknown mac type '%s'\n"),
optarg);
exit(1);
}
break;
case 'p':
optarg);
exit(1);
}
break;
case 'l':
break;
case 'L':
case 'R':
gettext("Bad forwarding "
"specification '%s'\n"),
optarg);
usage();
/* NOTREACHED */
}
gettext("Bad forwarding port(s) '%s'\n"),
optarg);
exit(1);
}
if (opt == 'L')
else if (opt == 'R')
break;
case 'D':
if (fwd_port == 0) {
gettext("Bad dynamic port '%s'\n"),
optarg);
exit(1);
}
break;
case 'C':
break;
case 'N':
no_shell_flag = 1;
no_tty_flag = 1;
break;
case 'T':
no_tty_flag = 1;
break;
case 'o':
dummy = 1;
exit(1);
break;
case 's':
subsystem_flag = 1;
break;
case 'b':
break;
case 'F':
break;
default:
usage();
}
}
usage();
*cp = '\0';
} else
if (ac > 0) {
optind = 0;
optreset = 1;
goto again;
}
}
/* Check that we got a host name. */
if (!host)
usage();
/* Initialize the command to execute on remote host. */
/*
* 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 (!ac) {
/* No command specified - execute shell on a tty. */
tty_flag = 1;
if (subsystem_flag) {
gettext("You must specify a subsystem "
"to invoke.\n"));
usage();
}
} else {
/* A command has been specified. Store it into the buffer. */
for (i = 0; i < ac; i++) {
if (i)
}
}
/* Cannot fork to background if no command. */
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;
/* Force no tty */
if (no_tty_flag)
tty_flag = 0;
/* Do not allocate a tty if stdin is not a tty. */
if (tty_flag)
log("Pseudo-terminal will not be allocated because stdin is not a terminal.");
tty_flag = 0;
}
/*
* Initialize "log" output. Since we are the client all output
* actually goes to stderr.
*/
SYSLOG_FACILITY_USER, 1);
/*
* Read per-user configuration file. Ignore the system wide config
* file if the user specifies a config file on the command line.
*/
fatal("Can't open user config file %.100s: "
} else {
/* Read systemwide configuration file after use config. */
}
/* Fill configuration defaults. */
/* reinit */
seed_rng();
/* Disable rhosts authentication if not running as root. */
#ifdef HAVE_CYGWIN
/* Ignore uid if running under Windows */
if (!options.use_privileged_port) {
#else
#endif
debug("Rhosts Authentication disabled, "
"originating port will not be trusted.");
}
/* Open a connection to the remote host. */
/* XXX OpenSSH has deprecated UseRsh */
fatal("rsh_connect returned");
}
#ifdef HAVE_CYGWIN
#else
#endif
options.proxy_command) != 0) {
/* XXX OpenSSH has deprecated FallbackToRsh */
if (options.fallback_to_rsh) {
fatal("rsh_connect returned");
}
exit(1);
}
/*
* 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 we cannot access the private keys, load the public keys
* instead and try to execute the ssh-keysign helper instead.
*/
sensitive_data.nkeys = 0;
if (options.rhosts_rsa_authentication ||
sizeof(Key));
}
}
/*
* 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.
*/
/*
* Now that we are back to our own permissions, create ~/.ssh
* directory if it doesn\'t already exist.
*/
snprintf(buf, sizeof buf, "%.100s%s%.100s", pw->pw_dir, strcmp(pw->pw_dir, "/") ? "/" : "", _PATH_SSH_USER_DIR);
/* load options.identity_files */
/* Expand ~ in known host file names. */
/* XXX mem-leaks: */
/* Log into the remote system. This never returns if the login fails. */
/* We no longer need the private host keys. Clear them now. */
if (sensitive_data.nkeys != 0) {
for (i = 0; i < sensitive_data.nkeys; i++) {
/* Destroys contents safely */
debug3("clear hostkey %d", i);
}
}
}
for (i = 0; i < options.num_identity_files; i++) {
if (options.identity_files[i]) {
}
if (options.identity_keys[i]) {
}
}
packet_close();
/*
* Send SIGHUP to proxy command if used. We don't wait() in
* case it hangs and instead rely on init to reap the child
*/
if (proxy_command_pid > 1)
return exit_status;
}
static void
{
char line[512];
FILE *f;
int got_data = 0, i;
char *display;
if (!options.xauth_location ||
debug("No xauth program.");
} else {
debug("x11_get_proto: DISPLAY not set");
return;
}
/* Try to get Xauthority information for the display. */
/*
* Handle FamilyLocal case where $DISPLAY does
* not match an authorization entry. For this we
* just try "xauth list unix:displaynum.screennum".
* XXX: "localhost" match to determine FamilyLocal
* is not perfect.
*/
else
got_data = 1;
if (f)
pclose(f);
}
/*
* 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) {
log("Warning: No xauth data; using fake authentication data for X11 forwarding.");
for (i = 0; i < 16; i++) {
if (i % 4 == 0)
rand = arc4random();
rand >>= 8;
}
}
}
static void
ssh_init_forwarding(void)
{
int success = 0;
int i;
for (i = 0; i < options.num_local_forwards; i++) {
debug("Connections to local port %d forwarded to remote address %.200s:%d",
}
if (i > 0 && success == 0)
error("Could not request local forwarding.");
for (i = 0; i < options.num_remote_forwards; i++) {
debug("Connections to remote port %d forwarded to local address %.200s:%d",
}
}
static void
check_agent_present(void)
{
if (options.forward_agent) {
/* Clear agent forwarding if we don\'t have an agent. */
if (!ssh_agent_present())
options.forward_agent = 0;
}
}
static int
ssh_session(void)
{
int type;
int interactive = 0;
int have_tty = 0;
char *cp;
/* Enable compression if requested. */
if (options.compression) {
fatal("Compression level must be from 1 (fast) to 9 (slow, best).");
/* Send the request. */
packet_send();
type = packet_read();
if (type == SSH_SMSG_SUCCESS)
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. */
/* Store TERM in the packet. There is no limit on the
length of the string. */
if (!cp)
cp = "";
/* Store window size in the packet. */
/* Store tty modes in the packet. */
/* Send the packet, and wait for it to leave. */
packet_send();
/* Read response from the server. */
type = packet_read();
if (type == SSH_SMSG_SUCCESS) {
interactive = 1;
have_tty = 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. */
/* Get reasonable local authentication information. */
/* Request forwarding with authentication spoofing. */
debug("Requesting X11 forwarding with authentication spoofing.");
/* Read response from the server. */
type = packet_read();
if (type == SSH_SMSG_SUCCESS) {
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. */
/* Request authentication agent forwarding if appropriate. */
if (options.forward_agent) {
debug("Requesting authentication agent forwarding.");
/* Read response from the server. */
type = packet_read();
if (type != SSH_SMSG_SUCCESS)
log("Warning: Remote host denied authentication agent forwarding.");
}
/* Initiate port forwardings. */
/* If requested, let ssh continue in the background. */
/*
* 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) {
if (len > 900)
len = 900;
packet_send();
} else {
debug("Requesting shell.");
packet_send();
}
/* Enter the interactive session. */
}
static void
{
id = packet_get_int();
if (len > 900)
len = 900;
if (type == SSH2_MSG_CHANNEL_FAILURE)
fatal("Request for subsystem '%.*s' failed on channel %d",
}
void
{
int i;
i = client_global_request_id++;
if (i >= options.num_remote_forwards) {
debug("client_global_request_reply: too many replies %d > %d",
return;
}
debug("remote forward %s for: listen %d, connect %s:%d",
if (type == SSH2_MSG_REQUEST_FAILURE)
log("Warning: remote port forwarding failed for listen port %d",
}
static
void
ssh_session2_setlocale(int id)
{
char *loc;
#define remote_setlocale(envvar) \
loc); \
packet_send();\
}
remote_setlocale("LANG")
remote_setlocale("LC_CTYPE")
remote_setlocale("LC_COLLATE")
remote_setlocale("LC_TIME")
remote_setlocale("LC_NUMERIC")
remote_setlocale("LC_MONETARY")
remote_setlocale("LC_MESSAGES")
remote_setlocale("LC_ALL")
}
static void
{
int len;
int interactive = 0;
if (tty_flag) {
char *cp;
if (!cp)
cp = "";
/* Store window size in the packet. */
tio = get_saved_tio();
packet_send();
interactive = 1;
/* XXX wait for reply */
}
if (options.forward_x11 &&
/* Get reasonable local authentication information. */
/* Request forwarding with authentication spoofing. */
debug("Requesting X11 forwarding with authentication spoofing.");
interactive = 1;
/* XXX wait for reply */
}
if (options.forward_agent) {
debug("Requesting authentication agent forwarding.");
packet_send();
}
if (len > 0) {
if (len > 900)
len = 900;
if (subsystem_flag) {
/* register callback for reply */
/* XXX we assume that client_loop has already been called */
} else {
}
packet_send();
} else {
packet_send();
}
}
/* open new channel for a session */
static int
ssh_session2_open(void)
{
Channel *c;
if (stdin_null_flag) {
} else {
}
/* enable nonblocking unless tty */
if (tty_flag) {
window >>= 1;
packetmax >>= 1;
}
c = channel_new(
channel_send_open(c->self);
if (!no_shell_flag)
return c->self;
}
static int
ssh_session2(void)
{
int id = -1;
/* XXX should be pre-session */
id = ssh_session2_open();
/* If requested, let ssh continue in the background. */
}
static void
{
char *filename;
int i = 0;
#ifdef SMARTCARD
int count = 0;
count++;
sizeof(char *) * (SSH_MAX_IDENTITY_FILES - 1));
}
i = count;
}
#endif /* SMARTCARD */
for (; i < options.num_identity_files; i++) {
}
}
/*
* Connects to the given host using rsh(or prints an error message and exits
* if rsh is not available). This function never returns.
*/
static void
{
char *args[10];
int i;
log("Using rsh. WARNING: Connection will not be encrypted.");
/* Build argument list for rsh. */
i = 0;
/* host may have to come after user on some systems */
if (user) {
args[i++] = "-l";
}
if (buffer_len(command) > 0) {
}
if (debug_flag) {
for (i = 0; args[i]; i++) {
if (i != 0)
}
}
exit(1);
}