summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarkus Friedl <markus@cvs.openbsd.org>2013-07-19 07:37:49 +0000
committerMarkus Friedl <markus@cvs.openbsd.org>2013-07-19 07:37:49 +0000
commit03d2fde6ad89a51fef89d02617e8c711833a5abe (patch)
tree534233e68b0bcd212ab790e7c7f3f1eef7d63f46
parent0d38b94a2cd827c21ab8cb80bc122b39b2188f0b (diff)
add ssh-agent(1) support to sshd(8); allows encrypted hostkeys,
or hostkeys on smartcards; most of the work by Zev Weiss; bz #1974 ok djm@
-rw-r--r--usr.bin/ssh/auth.h4
-rw-r--r--usr.bin/ssh/kex.h3
-rw-r--r--usr.bin/ssh/kexdhs.c10
-rw-r--r--usr.bin/ssh/kexecdhs.c10
-rw-r--r--usr.bin/ssh/kexgexs.c11
-rw-r--r--usr.bin/ssh/monitor.c18
-rw-r--r--usr.bin/ssh/servconf.c18
-rw-r--r--usr.bin/ssh/servconf.h3
-rw-r--r--usr.bin/ssh/session.c10
-rw-r--r--usr.bin/ssh/sshd.c83
-rw-r--r--usr.bin/ssh/sshd_config.516
11 files changed, 144 insertions, 42 deletions
diff --git a/usr.bin/ssh/auth.h b/usr.bin/ssh/auth.h
index 2ce518a17df..cb0db428172 100644
--- a/usr.bin/ssh/auth.h
+++ b/usr.bin/ssh/auth.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth.h,v 1.75 2013/06/21 00:34:49 djm Exp $ */
+/* $OpenBSD: auth.h,v 1.76 2013/07/19 07:37:48 markus Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
@@ -174,10 +174,12 @@ check_key_in_hostfiles(struct passwd *, Key *, const char *,
/* hostkey handling */
Key *get_hostkey_by_index(int);
+Key *get_hostkey_public_by_index(int);
Key *get_hostkey_public_by_type(int);
Key *get_hostkey_private_by_type(int);
int get_hostkey_index(Key *);
int ssh1_session_key(BIGNUM *);
+void sshd_hostkey_sign(Key *, Key *, u_char **, u_int *, u_char *, u_int);
/* debug messages during authentication */
void auth_debug_add(const char *fmt,...) __attribute__((format(printf, 1, 2)));
diff --git a/usr.bin/ssh/kex.h b/usr.bin/ssh/kex.h
index 28e15c8520b..6d8252c1290 100644
--- a/usr.bin/ssh/kex.h
+++ b/usr.bin/ssh/kex.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: kex.h,v 1.55 2013/04/19 01:06:50 djm Exp $ */
+/* $OpenBSD: kex.h,v 1.56 2013/07/19 07:37:48 markus Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@@ -136,6 +136,7 @@ struct Kex {
Key *(*load_host_public_key)(int);
Key *(*load_host_private_key)(int);
int (*host_key_index)(Key *);
+ void (*sign)(Key *, Key *, u_char **, u_int *, u_char *, u_int);
void (*kex[KEX_MAX])(Kex *);
};
diff --git a/usr.bin/ssh/kexdhs.c b/usr.bin/ssh/kexdhs.c
index abbd54465bc..de085ba155b 100644
--- a/usr.bin/ssh/kexdhs.c
+++ b/usr.bin/ssh/kexdhs.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kexdhs.c,v 1.13 2013/05/17 00:13:13 djm Exp $ */
+/* $OpenBSD: kexdhs.c,v 1.14 2013/07/19 07:37:48 markus Exp $ */
/*
* Copyright (c) 2001 Markus Friedl. All rights reserved.
*
@@ -77,9 +77,6 @@ kexdh_server(Kex *kex)
if (server_host_public == NULL)
fatal("Unsupported hostkey type %d", kex->hostkey_type);
server_host_private = kex->load_host_private_key(kex->hostkey_type);
- if (server_host_private == NULL)
- fatal("Missing private key for hostkey type %d",
- kex->hostkey_type);
/* key, cert */
if ((dh_client_pub = BN_new()) == NULL)
@@ -141,9 +138,8 @@ kexdh_server(Kex *kex)
}
/* sign H */
- if (PRIVSEP(key_sign(server_host_private, &signature, &slen, hash,
- hashlen)) < 0)
- fatal("kexdh_server: key_sign failed");
+ kex->sign(server_host_private, server_host_public, &signature, &slen,
+ hash, hashlen);
/* destroy_sensitive_data(); */
diff --git a/usr.bin/ssh/kexecdhs.c b/usr.bin/ssh/kexecdhs.c
index 5a3307e5152..fbca7ea7b32 100644
--- a/usr.bin/ssh/kexecdhs.c
+++ b/usr.bin/ssh/kexecdhs.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kexecdhs.c,v 1.4 2013/05/17 00:13:13 djm Exp $ */
+/* $OpenBSD: kexecdhs.c,v 1.5 2013/07/19 07:37:48 markus Exp $ */
/*
* Copyright (c) 2001 Markus Friedl. All rights reserved.
* Copyright (c) 2010 Damien Miller. All rights reserved.
@@ -74,9 +74,6 @@ kexecdh_server(Kex *kex)
if (server_host_public == NULL)
fatal("Unsupported hostkey type %d", kex->hostkey_type);
server_host_private = kex->load_host_private_key(kex->hostkey_type);
- if (server_host_private == NULL)
- fatal("Missing private key for hostkey type %d",
- kex->hostkey_type);
debug("expecting SSH2_MSG_KEX_ECDH_INIT");
packet_read_expect(SSH2_MSG_KEX_ECDH_INIT);
@@ -135,9 +132,8 @@ kexecdh_server(Kex *kex)
}
/* sign H */
- if (PRIVSEP(key_sign(server_host_private, &signature, &slen,
- hash, hashlen)) < 0)
- fatal("kexdh_server: key_sign failed");
+ kex->sign(server_host_private, server_host_public, &signature, &slen,
+ hash, hashlen);
/* destroy_sensitive_data(); */
diff --git a/usr.bin/ssh/kexgexs.c b/usr.bin/ssh/kexgexs.c
index cabe3b5c813..1df2243c398 100644
--- a/usr.bin/ssh/kexgexs.c
+++ b/usr.bin/ssh/kexgexs.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kexgexs.c,v 1.15 2013/05/17 00:13:13 djm Exp $ */
+/* $OpenBSD: kexgexs.c,v 1.16 2013/07/19 07:37:48 markus Exp $ */
/*
* Copyright (c) 2000 Niels Provos. All rights reserved.
* Copyright (c) 2001 Markus Friedl. All rights reserved.
@@ -65,10 +65,6 @@ kexgex_server(Kex *kex)
if (server_host_public == NULL)
fatal("Unsupported hostkey type %d", kex->hostkey_type);
server_host_private = kex->load_host_private_key(kex->hostkey_type);
- if (server_host_private == NULL)
- fatal("Missing private key for hostkey type %d",
- kex->hostkey_type);
-
type = packet_read();
switch (type) {
@@ -184,9 +180,8 @@ kexgex_server(Kex *kex)
}
/* sign H */
- if (PRIVSEP(key_sign(server_host_private, &signature, &slen, hash,
- hashlen)) < 0)
- fatal("kexgex_server: key_sign failed");
+ kex->sign(server_host_private, server_host_public, &signature, &slen,
+ hash, hashlen);
/* destroy_sensitive_data(); */
diff --git a/usr.bin/ssh/monitor.c b/usr.bin/ssh/monitor.c
index 4fad8eee5ee..9c03bc98d45 100644
--- a/usr.bin/ssh/monitor.c
+++ b/usr.bin/ssh/monitor.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor.c,v 1.126 2013/06/21 00:34:49 djm Exp $ */
+/* $OpenBSD: monitor.c,v 1.127 2013/07/19 07:37:48 markus Exp $ */
/*
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
* Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -75,6 +75,7 @@
#include "ssh2.h"
#include "jpake.h"
#include "roaming.h"
+#include "authfd.h"
#ifdef GSSAPI
static Gssctxt *gsscontext = NULL;
@@ -594,6 +595,8 @@ mm_answer_moduli(int sock, Buffer *m)
return (0);
}
+extern AuthenticationConnection *auth_conn;
+
int
mm_answer_sign(int sock, Buffer *m)
{
@@ -622,10 +625,16 @@ mm_answer_sign(int sock, Buffer *m)
memcpy(session_id2, p, session_id2_len);
}
- if ((key = get_hostkey_by_index(keyid)) == NULL)
+ if ((key = get_hostkey_by_index(keyid)) != NULL) {
+ if (key_sign(key, &signature, &siglen, p, datlen) < 0)
+ fatal("%s: key_sign failed", __func__);
+ } else if ((key = get_hostkey_public_by_index(keyid)) != NULL &&
+ auth_conn != NULL) {
+ if (ssh_agent_sign(auth_conn, key, &signature, &siglen, p,
+ datlen) < 0)
+ fatal("%s: ssh_agent_sign failed", __func__);
+ } else
fatal("%s: no hostkey from index %d", __func__, keyid);
- if (key_sign(key, &signature, &siglen, p, datlen) < 0)
- fatal("%s: key_sign failed", __func__);
debug3("%s: signature %p(%u)", __func__, signature, siglen);
@@ -1533,6 +1542,7 @@ mm_get_kex(Buffer *m)
kex->load_host_public_key=&get_hostkey_public_by_type;
kex->load_host_private_key=&get_hostkey_private_by_type;
kex->host_key_index=&get_hostkey_index;
+ kex->sign = sshd_hostkey_sign;
return (kex);
}
diff --git a/usr.bin/ssh/servconf.c b/usr.bin/ssh/servconf.c
index 518d86e7547..5ee9bb9db95 100644
--- a/usr.bin/ssh/servconf.c
+++ b/usr.bin/ssh/servconf.c
@@ -1,5 +1,5 @@
-/* $OpenBSD: servconf.c,v 1.239 2013/05/17 00:13:14 djm Exp $ */
+/* $OpenBSD: servconf.c,v 1.240 2013/07/19 07:37:48 markus Exp $ */
/*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
@@ -70,6 +70,7 @@ initialize_server_options(ServerOptions *options)
options->address_family = -1;
options->num_host_key_files = 0;
options->num_host_cert_files = 0;
+ options->host_key_agent = NULL;
options->pid_file = NULL;
options->server_key_bits = -1;
options->login_grace_time = -1;
@@ -315,7 +316,7 @@ typedef enum {
sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
sKexAlgorithms, sIPQoS, sVersionAddendum,
sAuthorizedKeysCommand, sAuthorizedKeysCommandUser,
- sAuthenticationMethods,
+ sAuthenticationMethods, sHostKeyAgent,
sDeprecated, sUnsupported
} ServerOpCodes;
@@ -332,6 +333,7 @@ static struct {
{ "port", sPort, SSHCFG_GLOBAL },
{ "hostkey", sHostKeyFile, SSHCFG_GLOBAL },
{ "hostdsakey", sHostKeyFile, SSHCFG_GLOBAL }, /* alias */
+ { "hostkeyagent", sHostKeyAgent, SSHCFG_GLOBAL },
{ "pidfile", sPidFile, SSHCFG_GLOBAL },
{ "serverkeybits", sServerKeyBits, SSHCFG_GLOBAL },
{ "logingracetime", sLoginGraceTime, SSHCFG_GLOBAL },
@@ -931,6 +933,17 @@ process_server_config_line(ServerOptions *options, char *line,
}
break;
+ case sHostKeyAgent:
+ charptr = &options->host_key_agent;
+ arg = strdelim(&cp);
+ if (!arg || *arg == '\0')
+ fatal("%s line %d: missing socket name.",
+ filename, linenum);
+ if (*activep && *charptr == NULL)
+ *charptr = !strcmp(arg, SSH_AUTHSOCKET_ENV_NAME) ?
+ xstrdup(arg) : derelativise_path(arg);
+ break;
+
case sHostCertificate:
intptr = &options->num_host_cert_files;
if (*intptr >= MAX_HOSTKEYS)
@@ -1959,6 +1972,7 @@ dump_config(ServerOptions *o)
dump_cfg_string(sVersionAddendum, o->version_addendum);
dump_cfg_string(sAuthorizedKeysCommand, o->authorized_keys_command);
dump_cfg_string(sAuthorizedKeysCommandUser, o->authorized_keys_command_user);
+ dump_cfg_string(sHostKeyAgent, o->host_key_agent);
/* string arguments requiring a lookup */
dump_cfg_string(sLogLevel, log_level_name(o->log_level));
diff --git a/usr.bin/ssh/servconf.h b/usr.bin/ssh/servconf.h
index d32f2a141fe..fd7c7151ba9 100644
--- a/usr.bin/ssh/servconf.h
+++ b/usr.bin/ssh/servconf.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: servconf.h,v 1.108 2013/05/16 04:09:14 dtucker Exp $ */
+/* $OpenBSD: servconf.h,v 1.109 2013/07/19 07:37:48 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -65,6 +65,7 @@ typedef struct {
int num_host_key_files; /* Number of files for host keys. */
char *host_cert_files[MAX_HOSTCERTS]; /* Files containing host certs. */
int num_host_cert_files; /* Number of files for host certs. */
+ char *host_key_agent; /* ssh-agent socket for host keys. */
char *pid_file; /* Where to put our pid */
int server_key_bits;/* Size of the server key. */
int login_grace_time; /* Disconnect if no auth in this time
diff --git a/usr.bin/ssh/session.c b/usr.bin/ssh/session.c
index cf04d33df6a..ef54cbc0b86 100644
--- a/usr.bin/ssh/session.c
+++ b/usr.bin/ssh/session.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: session.c,v 1.265 2013/05/17 00:13:14 djm Exp $ */
+/* $OpenBSD: session.c,v 1.266 2013/07/19 07:37:48 markus Exp $ */
/*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
@@ -70,6 +70,7 @@
#include "hostfile.h"
#include "auth.h"
#include "auth-options.h"
+#include "authfd.h"
#include "pathnames.h"
#include "log.h"
#include "servconf.h"
@@ -1261,6 +1262,13 @@ launch_login(struct passwd *pw, const char *hostname)
static void
child_close_fds(void)
{
+ extern AuthenticationConnection *auth_conn;
+
+ if (auth_conn) {
+ ssh_close_authentication_connection(auth_conn);
+ auth_conn = NULL;
+ }
+
if (packet_get_connection_in() == packet_get_connection_out())
close(packet_get_connection_in());
else {
diff --git a/usr.bin/ssh/sshd.c b/usr.bin/ssh/sshd.c
index 76b7f2535be..c0b09330224 100644
--- a/usr.bin/ssh/sshd.c
+++ b/usr.bin/ssh/sshd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshd.c,v 1.403 2013/06/05 02:27:50 dtucker Exp $ */
+/* $OpenBSD: sshd.c,v 1.404 2013/07/19 07:37:48 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -90,6 +90,7 @@
#include "canohost.h"
#include "hostfile.h"
#include "auth.h"
+#include "authfd.h"
#include "misc.h"
#include "msg.h"
#include "dispatch.h"
@@ -177,6 +178,10 @@ char *server_version_string = NULL;
/* for rekeying XXX fixme */
Kex *xxx_kex;
+/* Daemon's agent connection */
+AuthenticationConnection *auth_conn = NULL;
+int have_agent = 0;
+
/*
* Any really sensitive data in the application is contained in this
* structure. The idea is that this structure could be locked into memory so
@@ -189,6 +194,7 @@ struct {
Key *server_key; /* ephemeral server key */
Key *ssh1_host_key; /* ssh1 host key */
Key **host_keys; /* all private host keys */
+ Key **host_pubkeys; /* all public host keys */
Key **host_certificates; /* all public host certificates */
int have_ssh1_key;
int have_ssh2_key;
@@ -640,6 +646,8 @@ privsep_preauth(Authctxt *authctxt)
debug2("Network child is on pid %ld", (long)pid);
pmonitor->m_pid = pid;
+ if (have_agent)
+ auth_conn = ssh_get_authentication_connection();
if (box != NULL)
ssh_sandbox_parent_preauth(box, pid);
monitor_child_preauth(authctxt, pmonitor);
@@ -750,6 +758,8 @@ list_hostkey_types(void)
for (i = 0; i < options.num_host_key_files; i++) {
key = sensitive_data.host_keys[i];
if (key == NULL)
+ key = sensitive_data.host_pubkeys[i];
+ if (key == NULL)
continue;
switch (key->type) {
case KEY_RSA:
@@ -802,6 +812,8 @@ get_hostkey_by_type(int type, int need_private)
break;
default:
key = sensitive_data.host_keys[i];
+ if (key == NULL && !need_private)
+ key = sensitive_data.host_pubkeys[i];
break;
}
if (key != NULL && key->type == type)
@@ -831,6 +843,14 @@ get_hostkey_by_index(int ind)
return (sensitive_data.host_keys[ind]);
}
+Key *
+get_hostkey_public_by_index(int ind)
+{
+ if (ind < 0 || ind >= options.num_host_key_files)
+ return (NULL);
+ return (sensitive_data.host_pubkeys[ind]);
+}
+
int
get_hostkey_index(Key *key)
{
@@ -843,6 +863,8 @@ get_hostkey_index(Key *key)
} else {
if (key == sensitive_data.host_keys[i])
return (i);
+ if (key == sensitive_data.host_pubkeys[i])
+ return (i);
}
}
return (-1);
@@ -1310,6 +1332,8 @@ main(int ac, char **av)
u_int64_t ibytes, obytes;
mode_t new_umask;
Key *key;
+ Key *pubkey;
+ int keytype;
Authctxt *authctxt;
struct connection_info *connection_info = get_connection_info(0, 0);
@@ -1542,22 +1566,45 @@ main(int ac, char **av)
debug("sshd version %s, %s", SSH_VERSION,
SSLeay_version(SSLEAY_VERSION));
- /* load private host keys */
+ /* load host keys */
sensitive_data.host_keys = xcalloc(options.num_host_key_files,
sizeof(Key *));
- for (i = 0; i < options.num_host_key_files; i++)
+ sensitive_data.host_pubkeys = xcalloc(options.num_host_key_files,
+ sizeof(Key *));
+ for (i = 0; i < options.num_host_key_files; i++) {
sensitive_data.host_keys[i] = NULL;
+ sensitive_data.host_pubkeys[i] = NULL;
+ }
+
+ if (options.host_key_agent) {
+ if (strcmp(options.host_key_agent, SSH_AUTHSOCKET_ENV_NAME))
+ setenv(SSH_AUTHSOCKET_ENV_NAME,
+ options.host_key_agent, 1);
+ have_agent = ssh_agent_present();
+ }
for (i = 0; i < options.num_host_key_files; i++) {
key = key_load_private(options.host_key_files[i], "", NULL);
+ pubkey = key_load_public(options.host_key_files[i], NULL);
sensitive_data.host_keys[i] = key;
- if (key == NULL) {
+ sensitive_data.host_pubkeys[i] = pubkey;
+
+ if (key == NULL && pubkey != NULL && pubkey->type != KEY_RSA1 &&
+ have_agent) {
+ debug("will rely on agent for hostkey %s",
+ options.host_key_files[i]);
+ keytype = pubkey->type;
+ } else if (key != NULL) {
+ keytype = key->type;
+ } else {
error("Could not load host key: %s",
options.host_key_files[i]);
sensitive_data.host_keys[i] = NULL;
+ sensitive_data.host_pubkeys[i] = NULL;
continue;
}
- switch (key->type) {
+
+ switch (keytype) {
case KEY_RSA1:
sensitive_data.ssh1_host_key = key;
sensitive_data.have_ssh1_key = 1;
@@ -1568,8 +1615,8 @@ main(int ac, char **av)
sensitive_data.have_ssh2_key = 1;
break;
}
- debug("private host key: #%d type %d %s", i, key->type,
- key_type(key));
+ debug("private host key: #%d type %d %s", i, keytype,
+ key_type(key ? key : pubkey));
}
if ((options.protocol & SSH_PROTO_1) && !sensitive_data.have_ssh1_key) {
logit("Disabling protocol version 1. Could not load host key");
@@ -1906,9 +1953,11 @@ main(int ac, char **av)
buffer_init(&loginmsg);
auth_debug_reset();
- if (use_privsep)
+ if (use_privsep) {
if (privsep_preauth(authctxt) == 1)
goto authenticated;
+ } else if (compat20 && have_agent)
+ auth_conn = ssh_get_authentication_connection();
/* perform the key exchange */
/* authenticate user and start session */
@@ -2194,6 +2243,23 @@ do_ssh1_kex(void)
packet_write_wait();
}
+void
+sshd_hostkey_sign(Key *privkey, Key *pubkey, u_char **signature, u_int *slen,
+ u_char *data, u_int dlen)
+{
+ if (privkey) {
+ if (PRIVSEP(key_sign(privkey, signature, slen, data, dlen) < 0))
+ fatal("%s: key_sign failed", __func__);
+ } else if (use_privsep) {
+ if (mm_key_sign(pubkey, signature, slen, data, dlen) < 0)
+ fatal("%s: pubkey_sign failed", __func__);
+ } else {
+ if (ssh_agent_sign(auth_conn, pubkey, signature, slen, data,
+ dlen))
+ fatal("%s: ssh_agent_sign failed", __func__);
+ }
+}
+
/*
* SSH2 key exchange: diffie-hellman-group1-sha1
*/
@@ -2244,6 +2310,7 @@ do_ssh2_kex(void)
kex->load_host_public_key=&get_hostkey_public_by_type;
kex->load_host_private_key=&get_hostkey_private_by_type;
kex->host_key_index=&get_hostkey_index;
+ kex->sign = sshd_hostkey_sign;
xxx_kex = kex;
diff --git a/usr.bin/ssh/sshd_config.5 b/usr.bin/ssh/sshd_config.5
index 23970b8c391..f6870a2b581 100644
--- a/usr.bin/ssh/sshd_config.5
+++ b/usr.bin/ssh/sshd_config.5
@@ -33,8 +33,8 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.\" $OpenBSD: sshd_config.5,v 1.161 2013/06/27 14:05:37 jmc Exp $
-.Dd $Mdocdate: June 27 2013 $
+.\" $OpenBSD: sshd_config.5,v 1.162 2013/07/19 07:37:48 markus Exp $
+.Dd $Mdocdate: July 19 2013 $
.Dt SSHD_CONFIG 5
.Os
.Sh NAME
@@ -547,6 +547,18 @@ keys are used for version 1 and
or
.Dq rsa
are used for version 2 of the SSH protocol.
+It is also possible to specify public host key files instead.
+In this case operations on the private key will be delegated
+to an
+.Xr ssh-agent 1 .
+.It Cm HostKeyAgent
+Identifies the UNIX-domain socket used to communicate
+with an agent that has access to the private host keys.
+If
+.Dq SSH_AUTH_SOCK
+is specified, the location of the socket will be read from the
+.Ev SSH_AUTH_SOCK
+environment variable.
.It Cm IgnoreRhosts
Specifies that
.Pa .rhosts