summaryrefslogtreecommitdiff
path: root/ssh-keyscan.c
diff options
context:
space:
mode:
Diffstat (limited to 'ssh-keyscan.c')
-rw-r--r--ssh-keyscan.c303
1 files changed, 168 insertions, 135 deletions
diff --git a/ssh-keyscan.c b/ssh-keyscan.c
index c5fb3b52..a5e64407 100644
--- a/ssh-keyscan.c
+++ b/ssh-keyscan.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-keyscan.c,v 1.99 2015/01/30 10:44:49 djm Exp $ */
+/* $OpenBSD: ssh-keyscan.c,v 1.131 2019/12/15 19:47:10 djm Exp $ */
/*
* Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>.
*
@@ -19,7 +19,9 @@
#include <netinet/in.h>
#include <arpa/inet.h>
+#ifdef WITH_OPENSSL
#include <openssl/bn.h>
+#endif
#include <netdb.h>
#include <errno.h>
@@ -32,7 +34,6 @@
#include "xmalloc.h"
#include "ssh.h"
-#include "ssh1.h"
#include "sshbuf.h"
#include "sshkey.h"
#include "cipher.h"
@@ -47,6 +48,7 @@
#include "hostfile.h"
#include "ssherr.h"
#include "ssh_api.h"
+#include "dns.h"
/* Flag indicating whether IPv4 or IPv6. This can be set on the command line.
Default value is AF_UNSPEC means both IPv4 and IPv6. */
@@ -54,16 +56,26 @@ int IPv4or6 = AF_UNSPEC;
int ssh_port = SSH_DEFAULT_PORT;
-#define KT_RSA1 1
-#define KT_DSA 2
-#define KT_RSA 4
-#define KT_ECDSA 8
-#define KT_ED25519 16
+#define KT_DSA (1)
+#define KT_RSA (1<<1)
+#define KT_ECDSA (1<<2)
+#define KT_ED25519 (1<<3)
+#define KT_XMSS (1<<4)
+#define KT_ECDSA_SK (1<<5)
+#define KT_ED25519_SK (1<<6)
+
+#define KT_MIN KT_DSA
+#define KT_MAX KT_ED25519_SK
-int get_keytypes = KT_RSA|KT_ECDSA|KT_ED25519;
+int get_cert = 0;
+int get_keytypes = KT_RSA|KT_ECDSA|KT_ED25519|KT_ECDSA_SK|KT_ED25519_SK;
int hash_hosts = 0; /* Hash hostname on output */
+int print_sshfp = 0; /* Print SSHFP records instead of known_hosts */
+
+int found_one = 0; /* Successfully found a key */
+
#define MAXMAXFD 256
/* The number of seconds after which to give up on a TCP connection */
@@ -77,8 +89,6 @@ fd_set *read_wait;
size_t read_wait_nfdset;
int ncon;
-struct ssh *active_state = NULL; /* XXX needed for linking */
-
/*
* Keep a connection structure for each file descriptor. The state
* associated with file descriptor n is held in fdcon[n].
@@ -93,8 +103,8 @@ typedef struct Connection {
int c_plen; /* Packet length field for ssh packet */
int c_len; /* Total bytes which must be read. */
int c_off; /* Length of data read so far. */
- int c_keytype; /* Only one of KT_RSA1, KT_DSA, or KT_RSA */
- int c_done; /* SSH2 done */
+ int c_keytype; /* Only one of KT_* */
+ sig_atomic_t c_done; /* SSH2 done */
char *c_namebase; /* Address to free for c_name and c_namelist */
char *c_name; /* Hostname of connection for errors */
char *c_namelist; /* Pointer to other possible addresses */
@@ -116,7 +126,7 @@ fdlim_get(int hard)
#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)
struct rlimit rlfd;
- if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0)
+ if (getrlimit(RLIMIT_NOFILE, &rlfd) == -1)
return (-1);
if ((hard ? rlfd.rlim_max : rlfd.rlim_cur) == RLIM_INFINITY)
return SSH_SYSFDMAX;
@@ -137,10 +147,10 @@ fdlim_set(int lim)
if (lim <= 0)
return (-1);
#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
- if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0)
+ if (getrlimit(RLIMIT_NOFILE, &rlfd) == -1)
return (-1);
rlfd.rlim_cur = lim;
- if (setrlimit(RLIMIT_NOFILE, &rlfd) < 0)
+ if (setrlimit(RLIMIT_NOFILE, &rlfd) == -1)
return (-1);
#elif defined (HAVE_SETDTABLESIZE)
setdtablesize(lim);
@@ -186,52 +196,6 @@ strnnsep(char **stringp, char *delim)
return (tok);
}
-#ifdef WITH_SSH1
-static struct sshkey *
-keygrab_ssh1(con *c)
-{
- static struct sshkey *rsa;
- static struct sshbuf *msg;
- int r;
- u_char type;
-
- if (rsa == NULL) {
- if ((rsa = sshkey_new(KEY_RSA1)) == NULL) {
- error("%s: sshkey_new failed", __func__);
- return NULL;
- }
- if ((msg = sshbuf_new()) == NULL)
- fatal("%s: sshbuf_new failed", __func__);
- }
- if ((r = sshbuf_put(msg, c->c_data, c->c_plen)) != 0 ||
- (r = sshbuf_consume(msg, 8 - (c->c_plen & 7))) != 0 || /* padding */
- (r = sshbuf_get_u8(msg, &type)) != 0)
- goto buf_err;
- if (type != (int) SSH_SMSG_PUBLIC_KEY) {
- error("%s: invalid packet type", c->c_name);
- sshbuf_reset(msg);
- return NULL;
- }
- if ((r = sshbuf_consume(msg, 8)) != 0 || /* cookie */
- /* server key */
- (r = sshbuf_get_u32(msg, NULL)) != 0 ||
- (r = sshbuf_get_bignum1(msg, NULL)) != 0 ||
- (r = sshbuf_get_bignum1(msg, NULL)) != 0 ||
- /* host key */
- (r = sshbuf_get_u32(msg, NULL)) != 0 ||
- (r = sshbuf_get_bignum1(msg, rsa->rsa->e)) != 0 ||
- (r = sshbuf_get_bignum1(msg, rsa->rsa->n)) != 0) {
- buf_err:
- error("%s: buffer error: %s", __func__, ssh_err(r));
- sshbuf_reset(msg);
- return NULL;
- }
-
- sshbuf_reset(msg);
-
- return (rsa);
-}
-#endif
static int
key_print_wrapper(struct sshkey *hostkey, struct ssh *ssh)
@@ -266,48 +230,119 @@ keygrab_ssh2(con *c)
char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT };
int r;
- enable_compat20();
- myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] =
- c->c_keytype == KT_DSA ? "ssh-dss" :
- (c->c_keytype == KT_RSA ? "ssh-rsa" :
- (c->c_keytype == KT_ED25519 ? "ssh-ed25519" :
- "ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521"));
+ switch (c->c_keytype) {
+ case KT_DSA:
+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
+ "ssh-dss-cert-v01@openssh.com" : "ssh-dss";
+ break;
+ case KT_RSA:
+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
+ "rsa-sha2-512-cert-v01@openssh.com,"
+ "rsa-sha2-256-cert-v01@openssh.com,"
+ "ssh-rsa-cert-v01@openssh.com" :
+ "rsa-sha2-512,"
+ "rsa-sha2-256,"
+ "ssh-rsa";
+ break;
+ case KT_ED25519:
+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
+ "ssh-ed25519-cert-v01@openssh.com" : "ssh-ed25519";
+ break;
+ case KT_XMSS:
+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
+ "ssh-xmss-cert-v01@openssh.com" : "ssh-xmss@openssh.com";
+ break;
+ case KT_ECDSA:
+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
+ "ecdsa-sha2-nistp256-cert-v01@openssh.com,"
+ "ecdsa-sha2-nistp384-cert-v01@openssh.com,"
+ "ecdsa-sha2-nistp521-cert-v01@openssh.com" :
+ "ecdsa-sha2-nistp256,"
+ "ecdsa-sha2-nistp384,"
+ "ecdsa-sha2-nistp521";
+ break;
+ case KT_ECDSA_SK:
+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
+ "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com" :
+ "sk-ecdsa-sha2-nistp256@openssh.com";
+ break;
+ case KT_ED25519_SK:
+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
+ "sk-ssh-ed25519-cert-v01@openssh.com" :
+ "sk-ssh-ed25519@openssh.com";
+ break;
+ default:
+ fatal("unknown key type %d", c->c_keytype);
+ break;
+ }
if ((r = kex_setup(c->c_ssh, myproposal)) != 0) {
free(c->c_ssh);
fprintf(stderr, "kex_setup: %s\n", ssh_err(r));
exit(1);
}
#ifdef WITH_OPENSSL
- c->c_ssh->kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client;
- c->c_ssh->kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client;
+ c->c_ssh->kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_client;
+ c->c_ssh->kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_client;
+ c->c_ssh->kex->kex[KEX_DH_GRP14_SHA256] = kex_gen_client;
+ c->c_ssh->kex->kex[KEX_DH_GRP16_SHA512] = kex_gen_client;
+ c->c_ssh->kex->kex[KEX_DH_GRP18_SHA512] = kex_gen_client;
c->c_ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_client;
c->c_ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_client;
# ifdef OPENSSL_HAS_ECC
- c->c_ssh->kex->kex[KEX_ECDH_SHA2] = kexecdh_client;
+ c->c_ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client;
# endif
#endif
- c->c_ssh->kex->kex[KEX_C25519_SHA256] = kexc25519_client;
+ c->c_ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client;
+ c->c_ssh->kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_client;
ssh_set_verify_host_key_callback(c->c_ssh, key_print_wrapper);
/*
* do the key-exchange until an error occurs or until
* the key_print_wrapper() callback sets c_done.
*/
- ssh_dispatch_run(c->c_ssh, DISPATCH_BLOCK, &c->c_done, c->c_ssh);
+ ssh_dispatch_run(c->c_ssh, DISPATCH_BLOCK, &c->c_done);
}
static void
-keyprint(con *c, struct sshkey *key)
+keyprint_one(const char *host, struct sshkey *key)
{
- char *host = c->c_output_name ? c->c_output_name : c->c_name;
+ char *hostport;
+ const char *known_host, *hashed;
+
+ found_one = 1;
- if (!key)
+ if (print_sshfp) {
+ export_dns_rr(host, key, stdout, 0);
return;
- if (hash_hosts && (host = host_hash(host, NULL, 0)) == NULL)
- fatal("host_hash failed");
+ }
- fprintf(stdout, "%s ", host);
+ hostport = put_host_port(host, ssh_port);
+ lowercase(hostport);
+ if (hash_hosts && (hashed = host_hash(host, NULL, 0)) == NULL)
+ fatal("host_hash failed");
+ known_host = hash_hosts ? hashed : hostport;
+ if (!get_cert)
+ fprintf(stdout, "%s ", known_host);
sshkey_write(key, stdout);
fputs("\n", stdout);
+ free(hostport);
+}
+
+static void
+keyprint(con *c, struct sshkey *key)
+{
+ char *hosts = c->c_output_name ? c->c_output_name : c->c_name;
+ char *host, *ohosts;
+
+ if (key == NULL)
+ return;
+ if (get_cert || (!hash_hosts && ssh_port == SSH_DEFAULT_PORT)) {
+ keyprint_one(hosts, key);
+ return;
+ }
+ ohosts = hosts = xstrdup(hosts);
+ while ((host = strsep(&hosts, ",")) != NULL)
+ keyprint_one(host, key);
+ free(ohosts);
}
static int
@@ -327,13 +362,13 @@ tcpconnect(char *host)
}
for (ai = aitop; ai; ai = ai->ai_next) {
s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
- if (s < 0) {
+ if (s == -1) {
error("socket: %s", strerror(errno));
continue;
}
if (set_nonblock(s) == -1)
fatal("%s: set_nonblock(%d)", __func__, s);
- if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0 &&
+ if (connect(s, ai->ai_addr, ai->ai_addrlen) == -1 &&
errno != EINPROGRESS)
error("connect (`%s'): %s", host, strerror(errno));
else
@@ -366,6 +401,7 @@ conalloc(char *iname, char *oname, int keytype)
if (fdcon[s].c_status)
fatal("conalloc: attempt to reuse fdno %d", s);
+ debug3("%s: oname %s kt %d", __func__, oname, keytype);
fdcon[s].c_fd = s;
fdcon[s].c_status = CS_CON;
fdcon[s].c_namebase = namebase;
@@ -376,7 +412,7 @@ conalloc(char *iname, char *oname, int keytype)
fdcon[s].c_len = 4;
fdcon[s].c_off = 0;
fdcon[s].c_keytype = keytype;
- gettimeofday(&fdcon[s].c_tv, NULL);
+ monotime_tv(&fdcon[s].c_tv);
fdcon[s].c_tv.tv_sec += timeout;
TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link);
FD_SET(s, read_wait);
@@ -389,7 +425,6 @@ confree(int s)
{
if (s >= maxfd || fdcon[s].c_status == CS_UNUSED)
fatal("confree: attempt to free bad fdno %d", s);
- close(s);
free(fdcon[s].c_namebase);
free(fdcon[s].c_output_name);
if (fdcon[s].c_status == CS_KEYS)
@@ -400,7 +435,8 @@ confree(int s)
ssh_packet_close(fdcon[s].c_ssh);
free(fdcon[s].c_ssh);
fdcon[s].c_ssh = NULL;
- }
+ } else
+ close(s);
TAILQ_REMOVE(&tq, &fdcon[s], c_link);
FD_CLR(s, read_wait);
ncon--;
@@ -410,7 +446,7 @@ static void
contouch(int s)
{
TAILQ_REMOVE(&tq, &fdcon[s], c_link);
- gettimeofday(&fdcon[s].c_tv, NULL);
+ monotime_tv(&fdcon[s].c_tv);
fdcon[s].c_tv.tv_sec += timeout;
TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link);
}
@@ -435,6 +471,20 @@ congreet(int s)
size_t bufsiz;
con *c = &fdcon[s];
+ /* send client banner */
+ n = snprintf(buf, sizeof buf, "SSH-%d.%d-OpenSSH-keyscan\r\n",
+ PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2);
+ if (n < 0 || (size_t)n >= sizeof(buf)) {
+ error("snprintf: buffer too small");
+ confree(s);
+ return;
+ }
+ if (atomicio(vwrite, s, buf, n) != (size_t)n) {
+ error("write (%s): %s", c->c_name, strerror(errno));
+ confree(s);
+ return;
+ }
+
for (;;) {
memset(buf, '\0', sizeof(buf));
bufsiz = sizeof(buf);
@@ -477,38 +527,15 @@ congreet(int s)
c->c_ssh->compat = compat_datafellows(remote_version);
else
c->c_ssh->compat = 0;
- if (c->c_keytype != KT_RSA1) {
- if (!ssh2_capable(remote_major, remote_minor)) {
- debug("%s doesn't support ssh2", c->c_name);
- confree(s);
- return;
- }
- } else if (remote_major != 1) {
- debug("%s doesn't support ssh1", c->c_name);
+ if (!ssh2_capable(remote_major, remote_minor)) {
+ debug("%s doesn't support ssh2", c->c_name);
confree(s);
return;
}
- fprintf(stderr, "# %s %s\n", c->c_name, chop(buf));
- n = snprintf(buf, sizeof buf, "SSH-%d.%d-OpenSSH-keyscan\r\n",
- c->c_keytype == KT_RSA1? PROTOCOL_MAJOR_1 : PROTOCOL_MAJOR_2,
- c->c_keytype == KT_RSA1? PROTOCOL_MINOR_1 : PROTOCOL_MINOR_2);
- if (n < 0 || (size_t)n >= sizeof(buf)) {
- error("snprintf: buffer too small");
- confree(s);
- return;
- }
- if (atomicio(vwrite, s, buf, n) != (size_t)n) {
- error("write (%s): %s", c->c_name, strerror(errno));
- confree(s);
- return;
- }
- if (c->c_keytype != KT_RSA1) {
- keygrab_ssh2(c);
- confree(s);
- return;
- }
- c->c_status = CS_SIZE;
- contouch(s);
+ fprintf(stderr, "%c %s:%d %s\n", print_sshfp ? ';' : '#',
+ c->c_name, ssh_port, chop(buf));
+ keygrab_ssh2(c);
+ confree(s);
}
static void
@@ -538,12 +565,6 @@ conread(int s)
c->c_data = xmalloc(c->c_len);
c->c_status = CS_KEYS;
break;
-#ifdef WITH_SSH1
- case CS_KEYS:
- keyprint(c, keygrab_ssh1(c));
- confree(s);
- return;
-#endif
default:
fatal("conread: invalid status %d", c->c_status);
break;
@@ -560,7 +581,7 @@ conloop(void)
con *c;
int i;
- gettimeofday(&now, NULL);
+ monotime_tv(&now);
c = TAILQ_FIRST(&tq);
if (c && (c->c_tv.tv_sec > now.tv_sec ||
@@ -612,7 +633,7 @@ do_host(char *host)
if (name == NULL)
return;
- for (j = KT_RSA1; j <= KT_ED25519; j *= 2) {
+ for (j = KT_MIN; j <= KT_MAX; j *= 2) {
if (get_keytypes & j) {
while (ncon >= MAXCON)
conloop();
@@ -636,8 +657,8 @@ static void
usage(void)
{
fprintf(stderr,
- "usage: %s [-46Hv] [-f file] [-p port] [-T timeout] [-t type]\n"
- "\t\t [host | addrlist namelist] ...\n",
+ "usage: %s [-46cDHv] [-f file] [-p port] [-T timeout] [-t type]\n"
+ "\t\t [host | addrlist namelist]\n",
__progname);
exit(1);
}
@@ -647,9 +668,9 @@ main(int argc, char **argv)
{
int debug_flag = 0, log_level = SYSLOG_LEVEL_INFO;
int opt, fopt_count = 0, j;
- char *tname, *cp, line[NI_MAXHOST];
+ char *tname, *cp, *line = NULL;
+ size_t linesize = 0;
FILE *fp;
- u_long linenum;
extern int optind;
extern char *optarg;
@@ -664,11 +685,17 @@ main(int argc, char **argv)
if (argc <= 1)
usage();
- while ((opt = getopt(argc, argv, "Hv46p:T:t:f:")) != -1) {
+ while ((opt = getopt(argc, argv, "cDHv46p:T:t:f:")) != -1) {
switch (opt) {
case 'H':
hash_hosts = 1;
break;
+ case 'c':
+ get_cert = 1;
+ break;
+ case 'D':
+ print_sshfp = 1;
+ break;
case 'p':
ssh_port = a2port(optarg);
if (ssh_port <= 0) {
@@ -703,10 +730,8 @@ main(int argc, char **argv)
tname = strtok(optarg, ",");
while (tname) {
int type = sshkey_type_from_name(tname);
+
switch (type) {
- case KEY_RSA1:
- get_keytypes |= KT_RSA1;
- break;
case KEY_DSA:
get_keytypes |= KT_DSA;
break;
@@ -719,8 +744,18 @@ main(int argc, char **argv)
case KEY_ED25519:
get_keytypes |= KT_ED25519;
break;
+ case KEY_XMSS:
+ get_keytypes |= KT_XMSS;
+ break;
+ case KEY_ED25519_SK:
+ get_keytypes |= KT_ED25519_SK;
+ break;
+ case KEY_ECDSA_SK:
+ get_keytypes |= KT_ECDSA_SK;
+ break;
case KEY_UNSPEC:
- fatal("unknown key type %s", tname);
+ default:
+ fatal("Unknown key type \"%s\"", tname);
}
tname = strtok(NULL, ",");
}
@@ -761,11 +796,8 @@ main(int argc, char **argv)
else if ((fp = fopen(argv[j], "r")) == NULL)
fatal("%s: %s: %s", __progname, argv[j],
strerror(errno));
- linenum = 0;
- while (read_keyfile_line(fp,
- argv[j] == NULL ? "(stdin)" : argv[j], line, sizeof(line),
- &linenum) != -1) {
+ while (getline(&line, &linesize, fp) != -1) {
/* Chomp off trailing whitespace and comments */
if ((cp = strchr(line, '#')) == NULL)
cp = line + strlen(line) - 1;
@@ -790,6 +822,7 @@ main(int argc, char **argv)
fclose(fp);
}
+ free(line);
while (optind < argc)
do_host(argv[optind++]);
@@ -797,5 +830,5 @@ main(int argc, char **argv)
while (ncon > 0)
conloop();
- return (0);
+ return found_one ? 0 : 1;
}