1323136Sdes/* $OpenBSD: ssh-keyscan.c,v 1.109 2017/03/10 04:26:06 djm Exp $ */
276259Sgreen/*
376259Sgreen * Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>.
476259Sgreen *
576259Sgreen * Modification and redistribution in source and binary forms is
676259Sgreen * permitted provided that due credit is given to the author and the
792555Sdes * OpenBSD project by leaving this copyright notice intact.
876259Sgreen */
976259Sgreen
1076259Sgreen#include "includes.h"
11162856Sdes
12294332Sdes#include <sys/types.h>
13106130Sdes#include "openbsd-compat/sys-queue.h"
14162856Sdes#include <sys/resource.h>
15162856Sdes#ifdef HAVE_SYS_TIME_H
16162856Sdes# include <sys/time.h>
17162856Sdes#endif
1876259Sgreen
19162856Sdes#include <netinet/in.h>
20162856Sdes#include <arpa/inet.h>
21162856Sdes
2276259Sgreen#include <openssl/bn.h>
2376259Sgreen
24162856Sdes#include <netdb.h>
25162856Sdes#include <errno.h>
26162856Sdes#include <stdarg.h>
27162856Sdes#include <stdio.h>
28162856Sdes#include <stdlib.h>
29162856Sdes#include <signal.h>
30162856Sdes#include <string.h>
31162856Sdes#include <unistd.h>
32162856Sdes
3376259Sgreen#include "xmalloc.h"
3476259Sgreen#include "ssh.h"
3576259Sgreen#include "ssh1.h"
36294332Sdes#include "sshbuf.h"
37294332Sdes#include "sshkey.h"
38162856Sdes#include "cipher.h"
3992555Sdes#include "kex.h"
4092555Sdes#include "compat.h"
4192555Sdes#include "myproposal.h"
4292555Sdes#include "packet.h"
4392555Sdes#include "dispatch.h"
4476259Sgreen#include "log.h"
4576259Sgreen#include "atomicio.h"
4692555Sdes#include "misc.h"
47147005Sdes#include "hostfile.h"
48294332Sdes#include "ssherr.h"
49294332Sdes#include "ssh_api.h"
5076259Sgreen
5198941Sdes/* Flag indicating whether IPv4 or IPv6.  This can be set on the command line.
5298941Sdes   Default value is AF_UNSPEC means both IPv4 and IPv6. */
5398941Sdesint IPv4or6 = AF_UNSPEC;
5476259Sgreen
5592555Sdesint ssh_port = SSH_DEFAULT_PORT;
5676259Sgreen
57221420Sdes#define KT_RSA1		1
58221420Sdes#define KT_DSA		2
59221420Sdes#define KT_RSA		4
60221420Sdes#define KT_ECDSA	8
61261320Sdes#define KT_ED25519	16
6292555Sdes
63296633Sdesint get_cert = 0;
64294328Sdesint get_keytypes = KT_RSA|KT_ECDSA|KT_ED25519;
6592555Sdes
66147005Sdesint hash_hosts = 0;		/* Hash hostname on output */
67147005Sdes
6876259Sgreen#define MAXMAXFD 256
6976259Sgreen
7076259Sgreen/* The number of seconds after which to give up on a TCP connection */
7176259Sgreenint timeout = 5;
7276259Sgreen
7376259Sgreenint maxfd;
7476259Sgreen#define MAXCON (maxfd - 10)
7576259Sgreen
7676259Sgreenextern char *__progname;
7776259Sgreenfd_set *read_wait;
78162856Sdessize_t read_wait_nfdset;
7976259Sgreenint ncon;
8076259Sgreen
81294332Sdesstruct ssh *active_state = NULL; /* XXX needed for linking */
82294332Sdes
8376259Sgreen/*
8476259Sgreen * Keep a connection structure for each file descriptor.  The state
8576259Sgreen * associated with file descriptor n is held in fdcon[n].
8676259Sgreen */
8776259Sgreentypedef struct Connection {
8876259Sgreen	u_char c_status;	/* State of connection on this file desc. */
8976259Sgreen#define CS_UNUSED 0		/* File descriptor unused */
9076259Sgreen#define CS_CON 1		/* Waiting to connect/read greeting */
9176259Sgreen#define CS_SIZE 2		/* Waiting to read initial packet size */
9276259Sgreen#define CS_KEYS 3		/* Waiting to read public key packet */
9376259Sgreen	int c_fd;		/* Quick lookup: c->c_fd == c - fdcon */
9476259Sgreen	int c_plen;		/* Packet length field for ssh packet */
9576259Sgreen	int c_len;		/* Total bytes which must be read. */
9676259Sgreen	int c_off;		/* Length of data read so far. */
9792555Sdes	int c_keytype;		/* Only one of KT_RSA1, KT_DSA, or KT_RSA */
98294336Sdes	sig_atomic_t c_done;	/* SSH2 done */
9976259Sgreen	char *c_namebase;	/* Address to free for c_name and c_namelist */
10076259Sgreen	char *c_name;		/* Hostname of connection for errors */
10176259Sgreen	char *c_namelist;	/* Pointer to other possible addresses */
10276259Sgreen	char *c_output_name;	/* Hostname of connection for output */
10376259Sgreen	char *c_data;		/* Data read from this fd */
104294332Sdes	struct ssh *c_ssh;	/* SSH-connection */
10576259Sgreen	struct timeval c_tv;	/* Time at which connection gets aborted */
10676259Sgreen	TAILQ_ENTRY(Connection) c_link;	/* List of connections in timeout order. */
10776259Sgreen} con;
10876259Sgreen
10976259SgreenTAILQ_HEAD(conlist, Connection) tq;	/* Timeout Queue */
11076259Sgreencon *fdcon;
11176259Sgreen
112294332Sdesstatic void keyprint(con *c, struct sshkey *key);
113294332Sdes
11492555Sdesstatic int
11576259Sgreenfdlim_get(int hard)
11676259Sgreen{
11798941Sdes#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)
11876259Sgreen	struct rlimit rlfd;
11976259Sgreen
12076259Sgreen	if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0)
12176259Sgreen		return (-1);
12276259Sgreen	if ((hard ? rlfd.rlim_max : rlfd.rlim_cur) == RLIM_INFINITY)
123126277Sdes		return SSH_SYSFDMAX;
12476259Sgreen	else
12576259Sgreen		return hard ? rlfd.rlim_max : rlfd.rlim_cur;
12698941Sdes#else
127126277Sdes	return SSH_SYSFDMAX;
12898941Sdes#endif
12976259Sgreen}
13076259Sgreen
13192555Sdesstatic int
13276259Sgreenfdlim_set(int lim)
13376259Sgreen{
13498941Sdes#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
13576259Sgreen	struct rlimit rlfd;
13698941Sdes#endif
137106130Sdes
13876259Sgreen	if (lim <= 0)
13976259Sgreen		return (-1);
14098941Sdes#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
14176259Sgreen	if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0)
14276259Sgreen		return (-1);
14376259Sgreen	rlfd.rlim_cur = lim;
14476259Sgreen	if (setrlimit(RLIMIT_NOFILE, &rlfd) < 0)
14576259Sgreen		return (-1);
14698941Sdes#elif defined (HAVE_SETDTABLESIZE)
14798941Sdes	setdtablesize(lim);
14898941Sdes#endif
14976259Sgreen	return (0);
15076259Sgreen}
15176259Sgreen
15276259Sgreen/*
15376259Sgreen * This is an strsep function that returns a null field for adjacent
15476259Sgreen * separators.  This is the same as the 4.4BSD strsep, but different from the
15576259Sgreen * one in the GNU libc.
15676259Sgreen */
15792555Sdesstatic char *
15876259Sgreenxstrsep(char **str, const char *delim)
15976259Sgreen{
16076259Sgreen	char *s, *e;
16176259Sgreen
16276259Sgreen	if (!**str)
16376259Sgreen		return (NULL);
16476259Sgreen
16576259Sgreen	s = *str;
16676259Sgreen	e = s + strcspn(s, delim);
16776259Sgreen
16876259Sgreen	if (*e != '\0')
16976259Sgreen		*e++ = '\0';
17076259Sgreen	*str = e;
17176259Sgreen
17276259Sgreen	return (s);
17376259Sgreen}
17476259Sgreen
17576259Sgreen/*
17676259Sgreen * Get the next non-null token (like GNU strsep).  Strsep() will return a
17776259Sgreen * null token for two adjacent separators, so we may have to loop.
17876259Sgreen */
17992555Sdesstatic char *
18076259Sgreenstrnnsep(char **stringp, char *delim)
18176259Sgreen{
18276259Sgreen	char *tok;
18376259Sgreen
18476259Sgreen	do {
18576259Sgreen		tok = xstrsep(stringp, delim);
18676259Sgreen	} while (tok && *tok == '\0');
18776259Sgreen	return (tok);
18876259Sgreen}
18976259Sgreen
190294328Sdes#ifdef WITH_SSH1
191294332Sdesstatic struct sshkey *
19292555Sdeskeygrab_ssh1(con *c)
19376259Sgreen{
194294332Sdes	static struct sshkey *rsa;
195294332Sdes	static struct sshbuf *msg;
196294332Sdes	int r;
197294332Sdes	u_char type;
19876259Sgreen
19976259Sgreen	if (rsa == NULL) {
200294332Sdes		if ((rsa = sshkey_new(KEY_RSA1)) == NULL) {
201294332Sdes			error("%s: sshkey_new failed", __func__);
202294332Sdes			return NULL;
203294332Sdes		}
204294332Sdes		if ((msg = sshbuf_new()) == NULL)
205294332Sdes			fatal("%s: sshbuf_new failed", __func__);
20676259Sgreen	}
207294332Sdes	if ((r = sshbuf_put(msg, c->c_data, c->c_plen)) != 0 ||
208294332Sdes	    (r = sshbuf_consume(msg, 8 - (c->c_plen & 7))) != 0 || /* padding */
209294332Sdes	    (r = sshbuf_get_u8(msg, &type)) != 0)
210294332Sdes		goto buf_err;
211294332Sdes	if (type != (int) SSH_SMSG_PUBLIC_KEY) {
21292555Sdes		error("%s: invalid packet type", c->c_name);
213294332Sdes		sshbuf_reset(msg);
21492555Sdes		return NULL;
21576259Sgreen	}
216294332Sdes	if ((r = sshbuf_consume(msg, 8)) != 0 || /* cookie */
217294332Sdes	    /* server key */
218294332Sdes	    (r = sshbuf_get_u32(msg, NULL)) != 0 ||
219294332Sdes	    (r = sshbuf_get_bignum1(msg, NULL)) != 0 ||
220294332Sdes	    (r = sshbuf_get_bignum1(msg, NULL)) != 0 ||
221294332Sdes	    /* host key */
222294332Sdes	    (r = sshbuf_get_u32(msg, NULL)) != 0 ||
223294332Sdes	    (r = sshbuf_get_bignum1(msg, rsa->rsa->e)) != 0 ||
224294332Sdes	    (r = sshbuf_get_bignum1(msg, rsa->rsa->n)) != 0) {
225294332Sdes buf_err:
226294332Sdes		error("%s: buffer error: %s", __func__, ssh_err(r));
227294332Sdes		sshbuf_reset(msg);
228294332Sdes		return NULL;
229294332Sdes	}
23076259Sgreen
231294332Sdes	sshbuf_reset(msg);
23276259Sgreen
23392555Sdes	return (rsa);
23492555Sdes}
235294328Sdes#endif
23692555Sdes
23792555Sdesstatic int
238294332Sdeskey_print_wrapper(struct sshkey *hostkey, struct ssh *ssh)
23992555Sdes{
240294332Sdes	con *c;
241294332Sdes
242294332Sdes	if ((c = ssh_get_app_data(ssh)) != NULL)
243294332Sdes		keyprint(c, hostkey);
244294332Sdes	/* always abort key exchange */
245294332Sdes	return -1;
24692555Sdes}
24792555Sdes
24892555Sdesstatic int
24992555Sdesssh2_capable(int remote_major, int remote_minor)
25092555Sdes{
25192555Sdes	switch (remote_major) {
25292555Sdes	case 1:
25392555Sdes		if (remote_minor == 99)
25492555Sdes			return 1;
25592555Sdes		break;
25692555Sdes	case 2:
25792555Sdes		return 1;
25892555Sdes	default:
25992555Sdes		break;
26092555Sdes	}
26192555Sdes	return 0;
26292555Sdes}
26392555Sdes
264294332Sdesstatic void
26592555Sdeskeygrab_ssh2(con *c)
26692555Sdes{
267294328Sdes	char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT };
268294332Sdes	int r;
26992555Sdes
27092555Sdes	enable_compat20();
271296633Sdes	switch (c->c_keytype) {
272296633Sdes	case KT_DSA:
273296633Sdes		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
274296633Sdes		    "ssh-dss-cert-v01@openssh.com" : "ssh-dss";
275296633Sdes		break;
276296633Sdes	case KT_RSA:
277296633Sdes		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
278296633Sdes		    "ssh-rsa-cert-v01@openssh.com" : "ssh-rsa";
279296633Sdes		break;
280296633Sdes	case KT_ED25519:
281296633Sdes		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
282296633Sdes		    "ssh-ed25519-cert-v01@openssh.com" : "ssh-ed25519";
283296633Sdes		break;
284296633Sdes	case KT_ECDSA:
285296633Sdes		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
286296633Sdes		    "ecdsa-sha2-nistp256-cert-v01@openssh.com,"
287296633Sdes		    "ecdsa-sha2-nistp384-cert-v01@openssh.com,"
288296633Sdes		    "ecdsa-sha2-nistp521-cert-v01@openssh.com" :
289296633Sdes		    "ecdsa-sha2-nistp256,"
290296633Sdes		    "ecdsa-sha2-nistp384,"
291296633Sdes		    "ecdsa-sha2-nistp521";
292296633Sdes		break;
293296633Sdes	default:
294296633Sdes		fatal("unknown key type %d", c->c_keytype);
295296633Sdes		break;
296296633Sdes	}
297294332Sdes	if ((r = kex_setup(c->c_ssh, myproposal)) != 0) {
298294332Sdes		free(c->c_ssh);
299294332Sdes		fprintf(stderr, "kex_setup: %s\n", ssh_err(r));
300294332Sdes		exit(1);
301294332Sdes	}
302294328Sdes#ifdef WITH_OPENSSL
303294332Sdes	c->c_ssh->kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client;
304294332Sdes	c->c_ssh->kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client;
305323129Sdes	c->c_ssh->kex->kex[KEX_DH_GRP14_SHA256] = kexdh_client;
306323129Sdes	c->c_ssh->kex->kex[KEX_DH_GRP16_SHA512] = kexdh_client;
307323129Sdes	c->c_ssh->kex->kex[KEX_DH_GRP18_SHA512] = kexdh_client;
308294332Sdes	c->c_ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_client;
309294332Sdes	c->c_ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_client;
310294332Sdes# ifdef OPENSSL_HAS_ECC
311294332Sdes	c->c_ssh->kex->kex[KEX_ECDH_SHA2] = kexecdh_client;
312294332Sdes# endif
313294328Sdes#endif
314294332Sdes	c->c_ssh->kex->kex[KEX_C25519_SHA256] = kexc25519_client;
315294332Sdes	ssh_set_verify_host_key_callback(c->c_ssh, key_print_wrapper);
316294332Sdes	/*
317294332Sdes	 * do the key-exchange until an error occurs or until
318294332Sdes	 * the key_print_wrapper() callback sets c_done.
319294332Sdes	 */
320294332Sdes	ssh_dispatch_run(c->c_ssh, DISPATCH_BLOCK, &c->c_done, c->c_ssh);
32192555Sdes}
32292555Sdes
32392555Sdesstatic void
324323136Sdeskeyprint_one(const char *host, struct sshkey *key)
32592555Sdes{
326296633Sdes	char *hostport;
327323136Sdes	const char *known_host, *hashed;
328147005Sdes
329323136Sdes	hostport = put_host_port(host, ssh_port);
330323136Sdes	lowercase(hostport);
331323136Sdes	if (hash_hosts && (hashed = host_hash(host, NULL, 0)) == NULL)
332147005Sdes		fatal("host_hash failed");
333323136Sdes	known_host = hash_hosts ? hashed : hostport;
334296633Sdes	if (!get_cert)
335323136Sdes		fprintf(stdout, "%s ", known_host);
336294332Sdes	sshkey_write(key, stdout);
33776259Sgreen	fputs("\n", stdout);
338294336Sdes	free(hostport);
33976259Sgreen}
34076259Sgreen
341296633Sdesstatic void
342296633Sdeskeyprint(con *c, struct sshkey *key)
343296633Sdes{
344296633Sdes	char *hosts = c->c_output_name ? c->c_output_name : c->c_name;
345296633Sdes	char *host, *ohosts;
346296633Sdes
347296633Sdes	if (key == NULL)
348296633Sdes		return;
349296633Sdes	if (get_cert || (!hash_hosts && ssh_port == SSH_DEFAULT_PORT)) {
350296633Sdes		keyprint_one(hosts, key);
351296633Sdes		return;
352296633Sdes	}
353296633Sdes	ohosts = hosts = xstrdup(hosts);
354296633Sdes	while ((host = strsep(&hosts, ",")) != NULL)
355296633Sdes		keyprint_one(host, key);
356296633Sdes	free(ohosts);
357296633Sdes}
358296633Sdes
35992555Sdesstatic int
36076259Sgreentcpconnect(char *host)
36176259Sgreen{
36276259Sgreen	struct addrinfo hints, *ai, *aitop;
36376259Sgreen	char strport[NI_MAXSERV];
36476259Sgreen	int gaierr, s = -1;
36576259Sgreen
36692555Sdes	snprintf(strport, sizeof strport, "%d", ssh_port);
36776259Sgreen	memset(&hints, 0, sizeof(hints));
36892555Sdes	hints.ai_family = IPv4or6;
36976259Sgreen	hints.ai_socktype = SOCK_STREAM;
370294332Sdes	if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) {
371294332Sdes		error("getaddrinfo %s: %s", host, ssh_gai_strerror(gaierr));
372294332Sdes		return -1;
373294332Sdes	}
37476259Sgreen	for (ai = aitop; ai; ai = ai->ai_next) {
375124211Sdes		s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
37676259Sgreen		if (s < 0) {
37776259Sgreen			error("socket: %s", strerror(errno));
37876259Sgreen			continue;
37976259Sgreen		}
380137019Sdes		if (set_nonblock(s) == -1)
381137019Sdes			fatal("%s: set_nonblock(%d)", __func__, s);
38276259Sgreen		if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0 &&
38376259Sgreen		    errno != EINPROGRESS)
38476259Sgreen			error("connect (`%s'): %s", host, strerror(errno));
38576259Sgreen		else
38676259Sgreen			break;
38776259Sgreen		close(s);
38876259Sgreen		s = -1;
38976259Sgreen	}
39076259Sgreen	freeaddrinfo(aitop);
39176259Sgreen	return s;
39276259Sgreen}
39376259Sgreen
39492555Sdesstatic int
39592555Sdesconalloc(char *iname, char *oname, int keytype)
39676259Sgreen{
397106130Sdes	char *namebase, *name, *namelist;
39876259Sgreen	int s;
39976259Sgreen
40076259Sgreen	namebase = namelist = xstrdup(iname);
40176259Sgreen
40276259Sgreen	do {
40376259Sgreen		name = xstrsep(&namelist, ",");
40476259Sgreen		if (!name) {
405255767Sdes			free(namebase);
40676259Sgreen			return (-1);
40776259Sgreen		}
40876259Sgreen	} while ((s = tcpconnect(name)) < 0);
40976259Sgreen
41076259Sgreen	if (s >= maxfd)
41176259Sgreen		fatal("conalloc: fdno %d too high", s);
41276259Sgreen	if (fdcon[s].c_status)
41376259Sgreen		fatal("conalloc: attempt to reuse fdno %d", s);
41476259Sgreen
415296633Sdes	debug3("%s: oname %s kt %d", __func__, oname, keytype);
41676259Sgreen	fdcon[s].c_fd = s;
41776259Sgreen	fdcon[s].c_status = CS_CON;
41876259Sgreen	fdcon[s].c_namebase = namebase;
41976259Sgreen	fdcon[s].c_name = name;
42076259Sgreen	fdcon[s].c_namelist = namelist;
42176259Sgreen	fdcon[s].c_output_name = xstrdup(oname);
42276259Sgreen	fdcon[s].c_data = (char *) &fdcon[s].c_plen;
42376259Sgreen	fdcon[s].c_len = 4;
42476259Sgreen	fdcon[s].c_off = 0;
42592555Sdes	fdcon[s].c_keytype = keytype;
42676259Sgreen	gettimeofday(&fdcon[s].c_tv, NULL);
42776259Sgreen	fdcon[s].c_tv.tv_sec += timeout;
42876259Sgreen	TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link);
42976259Sgreen	FD_SET(s, read_wait);
43076259Sgreen	ncon++;
43176259Sgreen	return (s);
43276259Sgreen}
43376259Sgreen
43492555Sdesstatic void
43576259Sgreenconfree(int s)
43676259Sgreen{
43776259Sgreen	if (s >= maxfd || fdcon[s].c_status == CS_UNUSED)
43876259Sgreen		fatal("confree: attempt to free bad fdno %d", s);
43976259Sgreen	close(s);
440255767Sdes	free(fdcon[s].c_namebase);
441255767Sdes	free(fdcon[s].c_output_name);
44276259Sgreen	if (fdcon[s].c_status == CS_KEYS)
443255767Sdes		free(fdcon[s].c_data);
44476259Sgreen	fdcon[s].c_status = CS_UNUSED;
44592555Sdes	fdcon[s].c_keytype = 0;
446294332Sdes	if (fdcon[s].c_ssh) {
447294332Sdes		ssh_packet_close(fdcon[s].c_ssh);
448294332Sdes		free(fdcon[s].c_ssh);
449294332Sdes		fdcon[s].c_ssh = NULL;
450294332Sdes	}
45176259Sgreen	TAILQ_REMOVE(&tq, &fdcon[s], c_link);
45276259Sgreen	FD_CLR(s, read_wait);
45376259Sgreen	ncon--;
45476259Sgreen}
45576259Sgreen
45692555Sdesstatic void
45776259Sgreencontouch(int s)
45876259Sgreen{
45976259Sgreen	TAILQ_REMOVE(&tq, &fdcon[s], c_link);
46076259Sgreen	gettimeofday(&fdcon[s].c_tv, NULL);
46176259Sgreen	fdcon[s].c_tv.tv_sec += timeout;
46276259Sgreen	TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link);
46376259Sgreen}
46476259Sgreen
46592555Sdesstatic int
46676259Sgreenconrecycle(int s)
46776259Sgreen{
468106130Sdes	con *c = &fdcon[s];
46976259Sgreen	int ret;
47076259Sgreen
47192555Sdes	ret = conalloc(c->c_namelist, c->c_output_name, c->c_keytype);
47276259Sgreen	confree(s);
47376259Sgreen	return (ret);
47476259Sgreen}
47576259Sgreen
47692555Sdesstatic void
47776259Sgreencongreet(int s)
47876259Sgreen{
479149753Sdes	int n = 0, remote_major = 0, remote_minor = 0;
48092555Sdes	char buf[256], *cp;
48192555Sdes	char remote_version[sizeof buf];
48276259Sgreen	size_t bufsiz;
48376259Sgreen	con *c = &fdcon[s];
48476259Sgreen
485157019Sdes	for (;;) {
486157019Sdes		memset(buf, '\0', sizeof(buf));
487157019Sdes		bufsiz = sizeof(buf);
488157019Sdes		cp = buf;
489157019Sdes		while (bufsiz-- &&
490157019Sdes		    (n = atomicio(read, s, cp, 1)) == 1 && *cp != '\n') {
491157019Sdes			if (*cp == '\r')
492157019Sdes				*cp = '\n';
493157019Sdes			cp++;
494157019Sdes		}
495157019Sdes		if (n != 1 || strncmp(buf, "SSH-", 4) == 0)
496157019Sdes			break;
49792555Sdes	}
498149753Sdes	if (n == 0) {
499149753Sdes		switch (errno) {
500149753Sdes		case EPIPE:
501149753Sdes			error("%s: Connection closed by remote host", c->c_name);
502149753Sdes			break;
503149753Sdes		case ECONNREFUSED:
504149753Sdes			break;
505149753Sdes		default:
50676259Sgreen			error("read (%s): %s", c->c_name, strerror(errno));
507149753Sdes			break;
508149753Sdes		}
50976259Sgreen		conrecycle(s);
51076259Sgreen		return;
51176259Sgreen	}
51276259Sgreen	if (*cp != '\n' && *cp != '\r') {
51376259Sgreen		error("%s: bad greeting", c->c_name);
51476259Sgreen		confree(s);
51576259Sgreen		return;
51676259Sgreen	}
51776259Sgreen	*cp = '\0';
518294332Sdes	if ((c->c_ssh = ssh_packet_set_connection(NULL, s, s)) == NULL)
519294332Sdes		fatal("ssh_packet_set_connection failed");
520294332Sdes	ssh_packet_set_timeout(c->c_ssh, timeout, 1);
521294332Sdes	ssh_set_app_data(c->c_ssh, c);	/* back link */
52292555Sdes	if (sscanf(buf, "SSH-%d.%d-%[^\n]\n",
52392555Sdes	    &remote_major, &remote_minor, remote_version) == 3)
524294332Sdes		c->c_ssh->compat = compat_datafellows(remote_version);
52592555Sdes	else
526294332Sdes		c->c_ssh->compat = 0;
52792555Sdes	if (c->c_keytype != KT_RSA1) {
52892555Sdes		if (!ssh2_capable(remote_major, remote_minor)) {
52992555Sdes			debug("%s doesn't support ssh2", c->c_name);
53092555Sdes			confree(s);
53192555Sdes			return;
53292555Sdes		}
53392555Sdes	} else if (remote_major != 1) {
53492555Sdes		debug("%s doesn't support ssh1", c->c_name);
53592555Sdes		confree(s);
53692555Sdes		return;
53792555Sdes	}
538294336Sdes	fprintf(stderr, "# %s:%d %s\n", c->c_name, ssh_port, chop(buf));
53992555Sdes	n = snprintf(buf, sizeof buf, "SSH-%d.%d-OpenSSH-keyscan\r\n",
54092555Sdes	    c->c_keytype == KT_RSA1? PROTOCOL_MAJOR_1 : PROTOCOL_MAJOR_2,
54192555Sdes	    c->c_keytype == KT_RSA1? PROTOCOL_MINOR_1 : PROTOCOL_MINOR_2);
542149753Sdes	if (n < 0 || (size_t)n >= sizeof(buf)) {
543149753Sdes		error("snprintf: buffer too small");
544149753Sdes		confree(s);
545149753Sdes		return;
546149753Sdes	}
547149753Sdes	if (atomicio(vwrite, s, buf, n) != (size_t)n) {
54876259Sgreen		error("write (%s): %s", c->c_name, strerror(errno));
54976259Sgreen		confree(s);
55076259Sgreen		return;
55176259Sgreen	}
55292555Sdes	if (c->c_keytype != KT_RSA1) {
553294332Sdes		keygrab_ssh2(c);
55492555Sdes		confree(s);
55592555Sdes		return;
55692555Sdes	}
55776259Sgreen	c->c_status = CS_SIZE;
55876259Sgreen	contouch(s);
55976259Sgreen}
56076259Sgreen
56192555Sdesstatic void
56276259Sgreenconread(int s)
56376259Sgreen{
564106130Sdes	con *c = &fdcon[s];
565149753Sdes	size_t n;
56676259Sgreen
56776259Sgreen	if (c->c_status == CS_CON) {
56876259Sgreen		congreet(s);
56976259Sgreen		return;
57076259Sgreen	}
571137019Sdes	n = atomicio(read, s, c->c_data + c->c_off, c->c_len - c->c_off);
572149753Sdes	if (n == 0) {
57376259Sgreen		error("read (%s): %s", c->c_name, strerror(errno));
57476259Sgreen		confree(s);
57576259Sgreen		return;
57676259Sgreen	}
57776259Sgreen	c->c_off += n;
57876259Sgreen
57976259Sgreen	if (c->c_off == c->c_len)
58076259Sgreen		switch (c->c_status) {
58176259Sgreen		case CS_SIZE:
58276259Sgreen			c->c_plen = htonl(c->c_plen);
58376259Sgreen			c->c_len = c->c_plen + 8 - (c->c_plen & 7);
58476259Sgreen			c->c_off = 0;
58576259Sgreen			c->c_data = xmalloc(c->c_len);
58676259Sgreen			c->c_status = CS_KEYS;
58776259Sgreen			break;
588294328Sdes#ifdef WITH_SSH1
58976259Sgreen		case CS_KEYS:
59092555Sdes			keyprint(c, keygrab_ssh1(c));
59176259Sgreen			confree(s);
59276259Sgreen			return;
593294328Sdes#endif
59476259Sgreen		default:
59576259Sgreen			fatal("conread: invalid status %d", c->c_status);
59676259Sgreen			break;
59776259Sgreen		}
59876259Sgreen
59976259Sgreen	contouch(s);
60076259Sgreen}
60176259Sgreen
60292555Sdesstatic void
60376259Sgreenconloop(void)
60476259Sgreen{
605106130Sdes	struct timeval seltime, now;
60676259Sgreen	fd_set *r, *e;
607106130Sdes	con *c;
60876259Sgreen	int i;
60976259Sgreen
61076259Sgreen	gettimeofday(&now, NULL);
61198675Sdes	c = TAILQ_FIRST(&tq);
61276259Sgreen
61376259Sgreen	if (c && (c->c_tv.tv_sec > now.tv_sec ||
61476259Sgreen	    (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec > now.tv_usec))) {
61576259Sgreen		seltime = c->c_tv;
61676259Sgreen		seltime.tv_sec -= now.tv_sec;
61776259Sgreen		seltime.tv_usec -= now.tv_usec;
61876259Sgreen		if (seltime.tv_usec < 0) {
61976259Sgreen			seltime.tv_usec += 1000000;
62076259Sgreen			seltime.tv_sec--;
62176259Sgreen		}
62276259Sgreen	} else
623226046Sdes		timerclear(&seltime);
62476259Sgreen
625162856Sdes	r = xcalloc(read_wait_nfdset, sizeof(fd_mask));
626162856Sdes	e = xcalloc(read_wait_nfdset, sizeof(fd_mask));
627162856Sdes	memcpy(r, read_wait, read_wait_nfdset * sizeof(fd_mask));
628162856Sdes	memcpy(e, read_wait, read_wait_nfdset * sizeof(fd_mask));
62976259Sgreen
63076259Sgreen	while (select(maxfd, r, NULL, e, &seltime) == -1 &&
631181111Sdes	    (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK))
63276259Sgreen		;
63376259Sgreen
63476259Sgreen	for (i = 0; i < maxfd; i++) {
63576259Sgreen		if (FD_ISSET(i, e)) {
63676259Sgreen			error("%s: exception!", fdcon[i].c_name);
63776259Sgreen			confree(i);
63876259Sgreen		} else if (FD_ISSET(i, r))
63976259Sgreen			conread(i);
64076259Sgreen	}
641255767Sdes	free(r);
642255767Sdes	free(e);
64376259Sgreen
64498675Sdes	c = TAILQ_FIRST(&tq);
64576259Sgreen	while (c && (c->c_tv.tv_sec < now.tv_sec ||
64676259Sgreen	    (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec < now.tv_usec))) {
64776259Sgreen		int s = c->c_fd;
64876259Sgreen
64998675Sdes		c = TAILQ_NEXT(c, c_link);
65076259Sgreen		conrecycle(s);
65176259Sgreen	}
65276259Sgreen}
65376259Sgreen
65492555Sdesstatic void
65592555Sdesdo_host(char *host)
65676259Sgreen{
65792555Sdes	char *name = strnnsep(&host, " \t\n");
65892555Sdes	int j;
65976259Sgreen
66092555Sdes	if (name == NULL)
66192555Sdes		return;
662261320Sdes	for (j = KT_RSA1; j <= KT_ED25519; j *= 2) {
66392555Sdes		if (get_keytypes & j) {
66492555Sdes			while (ncon >= MAXCON)
66592555Sdes				conloop();
66692555Sdes			conalloc(name, *host ? host : name, j);
66776259Sgreen		}
66876259Sgreen	}
66976259Sgreen}
67076259Sgreen
67176259Sgreenvoid
67292555Sdesfatal(const char *fmt,...)
67392555Sdes{
67492555Sdes	va_list args;
675106130Sdes
67692555Sdes	va_start(args, fmt);
67792555Sdes	do_log(SYSLOG_LEVEL_FATAL, fmt, args);
67892555Sdes	va_end(args);
679294332Sdes	exit(255);
68092555Sdes}
68192555Sdes
68292555Sdesstatic void
68376259Sgreenusage(void)
68476259Sgreen{
685192595Sdes	fprintf(stderr,
686296633Sdes	    "usage: %s [-46cHv] [-f file] [-p port] [-T timeout] [-t type]\n"
687192595Sdes	    "\t\t   [host | addrlist namelist] ...\n",
68892555Sdes	    __progname);
68992555Sdes	exit(1);
69076259Sgreen}
69176259Sgreen
69276259Sgreenint
69376259Sgreenmain(int argc, char **argv)
69476259Sgreen{
69592555Sdes	int debug_flag = 0, log_level = SYSLOG_LEVEL_INFO;
696215116Sdes	int opt, fopt_count = 0, j;
697215116Sdes	char *tname, *cp, line[NI_MAXHOST];
698215116Sdes	FILE *fp;
699215116Sdes	u_long linenum;
70076259Sgreen
70192555Sdes	extern int optind;
70292555Sdes	extern char *optarg;
70392555Sdes
704296633Sdes	ssh_malloc_init();	/* must be called before any mallocs */
705124211Sdes	__progname = ssh_get_progname(argv[0]);
70698941Sdes	seed_rng();
70776259Sgreen	TAILQ_INIT(&tq);
70876259Sgreen
709157019Sdes	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
710157019Sdes	sanitise_stdfd();
711157019Sdes
71292555Sdes	if (argc <= 1)
71376259Sgreen		usage();
71476259Sgreen
715296633Sdes	while ((opt = getopt(argc, argv, "cHv46p:T:t:f:")) != -1) {
71692555Sdes		switch (opt) {
717147005Sdes		case 'H':
718147005Sdes			hash_hosts = 1;
719147005Sdes			break;
720296633Sdes		case 'c':
721296633Sdes			get_cert = 1;
722296633Sdes			break;
72392555Sdes		case 'p':
72492555Sdes			ssh_port = a2port(optarg);
725192595Sdes			if (ssh_port <= 0) {
72692555Sdes				fprintf(stderr, "Bad port '%s'\n", optarg);
72792555Sdes				exit(1);
72892555Sdes			}
72992555Sdes			break;
73092555Sdes		case 'T':
731106130Sdes			timeout = convtime(optarg);
732106130Sdes			if (timeout == -1 || timeout == 0) {
733106130Sdes				fprintf(stderr, "Bad timeout '%s'\n", optarg);
73476259Sgreen				usage();
735106130Sdes			}
73692555Sdes			break;
73792555Sdes		case 'v':
73892555Sdes			if (!debug_flag) {
73992555Sdes				debug_flag = 1;
74092555Sdes				log_level = SYSLOG_LEVEL_DEBUG1;
74192555Sdes			}
74292555Sdes			else if (log_level < SYSLOG_LEVEL_DEBUG3)
74392555Sdes				log_level++;
74492555Sdes			else
74592555Sdes				fatal("Too high debugging level.");
74692555Sdes			break;
74792555Sdes		case 'f':
74892555Sdes			if (strcmp(optarg, "-") == 0)
74992555Sdes				optarg = NULL;
75092555Sdes			argv[fopt_count++] = optarg;
75192555Sdes			break;
75292555Sdes		case 't':
75392555Sdes			get_keytypes = 0;
75492555Sdes			tname = strtok(optarg, ",");
75592555Sdes			while (tname) {
756294332Sdes				int type = sshkey_type_from_name(tname);
757323136Sdes
75892555Sdes				switch (type) {
759323136Sdes#ifdef WITH_SSH1
76092555Sdes				case KEY_RSA1:
76192555Sdes					get_keytypes |= KT_RSA1;
76292555Sdes					break;
763323136Sdes#endif
76492555Sdes				case KEY_DSA:
76592555Sdes					get_keytypes |= KT_DSA;
76692555Sdes					break;
767221420Sdes				case KEY_ECDSA:
768221420Sdes					get_keytypes |= KT_ECDSA;
769221420Sdes					break;
77092555Sdes				case KEY_RSA:
77192555Sdes					get_keytypes |= KT_RSA;
77292555Sdes					break;
773261320Sdes				case KEY_ED25519:
774261320Sdes					get_keytypes |= KT_ED25519;
775261320Sdes					break;
77692555Sdes				case KEY_UNSPEC:
777323136Sdes				default:
778323136Sdes					fatal("Unknown key type \"%s\"", tname);
77992555Sdes				}
78092555Sdes				tname = strtok(NULL, ",");
78192555Sdes			}
78292555Sdes			break;
78392555Sdes		case '4':
78492555Sdes			IPv4or6 = AF_INET;
78592555Sdes			break;
78692555Sdes		case '6':
78792555Sdes			IPv4or6 = AF_INET6;
78892555Sdes			break;
78992555Sdes		case '?':
79092555Sdes		default:
79192555Sdes			usage();
79276259Sgreen		}
79376259Sgreen	}
79492555Sdes	if (optind == argc && !fopt_count)
79576259Sgreen		usage();
79676259Sgreen
79792555Sdes	log_init("ssh-keyscan", log_level, SYSLOG_FACILITY_USER, 1);
79892555Sdes
79976259Sgreen	maxfd = fdlim_get(1);
80076259Sgreen	if (maxfd < 0)
80176259Sgreen		fatal("%s: fdlim_get: bad value", __progname);
80276259Sgreen	if (maxfd > MAXMAXFD)
80376259Sgreen		maxfd = MAXMAXFD;
80476259Sgreen	if (MAXCON <= 0)
80576259Sgreen		fatal("%s: not enough file descriptors", __progname);
80676259Sgreen	if (maxfd > fdlim_get(0))
80776259Sgreen		fdlim_set(maxfd);
808162856Sdes	fdcon = xcalloc(maxfd, sizeof(con));
80976259Sgreen
810162856Sdes	read_wait_nfdset = howmany(maxfd, NFDBITS);
811162856Sdes	read_wait = xcalloc(read_wait_nfdset, sizeof(fd_mask));
81276259Sgreen
813215116Sdes	for (j = 0; j < fopt_count; j++) {
814215116Sdes		if (argv[j] == NULL)
815215116Sdes			fp = stdin;
816215116Sdes		else if ((fp = fopen(argv[j], "r")) == NULL)
817215116Sdes			fatal("%s: %s: %s", __progname, argv[j],
818215116Sdes			    strerror(errno));
819215116Sdes		linenum = 0;
82076259Sgreen
821215116Sdes		while (read_keyfile_line(fp,
822215116Sdes		    argv[j] == NULL ? "(stdin)" : argv[j], line, sizeof(line),
823215116Sdes		    &linenum) != -1) {
824215116Sdes			/* Chomp off trailing whitespace and comments */
825215116Sdes			if ((cp = strchr(line, '#')) == NULL)
826215116Sdes				cp = line + strlen(line) - 1;
827215116Sdes			while (cp >= line) {
828215116Sdes				if (*cp == ' ' || *cp == '\t' ||
829215116Sdes				    *cp == '\n' || *cp == '#')
830215116Sdes					*cp-- = '\0';
831215116Sdes				else
832215116Sdes					break;
833215116Sdes			}
834215116Sdes
835215116Sdes			/* Skip empty lines */
836215116Sdes			if (*line == '\0')
83792555Sdes				continue;
838215116Sdes
839215116Sdes			do_host(line);
84076259Sgreen		}
841215116Sdes
842215116Sdes		if (ferror(fp))
843215116Sdes			fatal("%s: %s: %s", __progname, argv[j],
844215116Sdes			    strerror(errno));
845215116Sdes
846215116Sdes		fclose(fp);
84792555Sdes	}
84892555Sdes
84992555Sdes	while (optind < argc)
85092555Sdes		do_host(argv[optind++]);
85192555Sdes
85276259Sgreen	while (ncon > 0)
85376259Sgreen		conloop();
85476259Sgreen
85576259Sgreen	return (0);
85676259Sgreen}
857