1262566Sdes/* $OpenBSD: ssh-keyscan.c,v 1.89 2013/12/06 13:39:49 markus 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
12106130Sdes#include "openbsd-compat/sys-queue.h"
13162856Sdes#include <sys/resource.h>
14162856Sdes#ifdef HAVE_SYS_TIME_H
15162856Sdes# include <sys/time.h>
16162856Sdes#endif
1776259Sgreen
18162856Sdes#include <netinet/in.h>
19162856Sdes#include <arpa/inet.h>
20162856Sdes
2176259Sgreen#include <openssl/bn.h>
2276259Sgreen
23162856Sdes#include <netdb.h>
24162856Sdes#include <errno.h>
2592555Sdes#include <setjmp.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"
36162856Sdes#include "buffer.h"
3776259Sgreen#include "key.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"
4876259Sgreen
4998941Sdes/* Flag indicating whether IPv4 or IPv6.  This can be set on the command line.
5098941Sdes   Default value is AF_UNSPEC means both IPv4 and IPv6. */
5198941Sdesint IPv4or6 = AF_UNSPEC;
5276259Sgreen
5392555Sdesint ssh_port = SSH_DEFAULT_PORT;
5476259Sgreen
55221420Sdes#define KT_RSA1		1
56221420Sdes#define KT_DSA		2
57221420Sdes#define KT_RSA		4
58221420Sdes#define KT_ECDSA	8
59262566Sdes#define KT_ED25519	16
6092555Sdes
61237568Sdelphijint get_keytypes = KT_RSA|KT_ECDSA;/* Get RSA and ECDSA keys by default */
6292555Sdes
63147005Sdesint hash_hosts = 0;		/* Hash hostname on output */
64147005Sdes
6576259Sgreen#define MAXMAXFD 256
6676259Sgreen
6776259Sgreen/* The number of seconds after which to give up on a TCP connection */
6876259Sgreenint timeout = 5;
6976259Sgreen
7076259Sgreenint maxfd;
7176259Sgreen#define MAXCON (maxfd - 10)
7276259Sgreen
7376259Sgreenextern char *__progname;
7476259Sgreenfd_set *read_wait;
75162856Sdessize_t read_wait_nfdset;
7676259Sgreenint ncon;
7792555Sdesint nonfatal_fatal = 0;
7892555Sdesjmp_buf kexjmp;
7992555SdesKey *kexjmp_key;
8076259Sgreen
8176259Sgreen/*
8276259Sgreen * Keep a connection structure for each file descriptor.  The state
8376259Sgreen * associated with file descriptor n is held in fdcon[n].
8476259Sgreen */
8576259Sgreentypedef struct Connection {
8676259Sgreen	u_char c_status;	/* State of connection on this file desc. */
8776259Sgreen#define CS_UNUSED 0		/* File descriptor unused */
8876259Sgreen#define CS_CON 1		/* Waiting to connect/read greeting */
8976259Sgreen#define CS_SIZE 2		/* Waiting to read initial packet size */
9076259Sgreen#define CS_KEYS 3		/* Waiting to read public key packet */
9176259Sgreen	int c_fd;		/* Quick lookup: c->c_fd == c - fdcon */
9276259Sgreen	int c_plen;		/* Packet length field for ssh packet */
9376259Sgreen	int c_len;		/* Total bytes which must be read. */
9476259Sgreen	int c_off;		/* Length of data read so far. */
9592555Sdes	int c_keytype;		/* Only one of KT_RSA1, KT_DSA, or KT_RSA */
9676259Sgreen	char *c_namebase;	/* Address to free for c_name and c_namelist */
9776259Sgreen	char *c_name;		/* Hostname of connection for errors */
9876259Sgreen	char *c_namelist;	/* Pointer to other possible addresses */
9976259Sgreen	char *c_output_name;	/* Hostname of connection for output */
10076259Sgreen	char *c_data;		/* Data read from this fd */
10192555Sdes	Kex *c_kex;		/* The key-exchange struct for ssh2 */
10276259Sgreen	struct timeval c_tv;	/* Time at which connection gets aborted */
10376259Sgreen	TAILQ_ENTRY(Connection) c_link;	/* List of connections in timeout order. */
10476259Sgreen} con;
10576259Sgreen
10676259SgreenTAILQ_HEAD(conlist, Connection) tq;	/* Timeout Queue */
10776259Sgreencon *fdcon;
10876259Sgreen
10992555Sdesstatic int
11076259Sgreenfdlim_get(int hard)
11176259Sgreen{
11298941Sdes#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)
11376259Sgreen	struct rlimit rlfd;
11476259Sgreen
11576259Sgreen	if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0)
11676259Sgreen		return (-1);
11776259Sgreen	if ((hard ? rlfd.rlim_max : rlfd.rlim_cur) == RLIM_INFINITY)
118126277Sdes		return SSH_SYSFDMAX;
11976259Sgreen	else
12076259Sgreen		return hard ? rlfd.rlim_max : rlfd.rlim_cur;
12198941Sdes#else
122126277Sdes	return SSH_SYSFDMAX;
12398941Sdes#endif
12476259Sgreen}
12576259Sgreen
12692555Sdesstatic int
12776259Sgreenfdlim_set(int lim)
12876259Sgreen{
12998941Sdes#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
13076259Sgreen	struct rlimit rlfd;
13198941Sdes#endif
132106130Sdes
13376259Sgreen	if (lim <= 0)
13476259Sgreen		return (-1);
13598941Sdes#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
13676259Sgreen	if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0)
13776259Sgreen		return (-1);
13876259Sgreen	rlfd.rlim_cur = lim;
13976259Sgreen	if (setrlimit(RLIMIT_NOFILE, &rlfd) < 0)
14076259Sgreen		return (-1);
14198941Sdes#elif defined (HAVE_SETDTABLESIZE)
14298941Sdes	setdtablesize(lim);
14398941Sdes#endif
14476259Sgreen	return (0);
14576259Sgreen}
14676259Sgreen
14776259Sgreen/*
14876259Sgreen * This is an strsep function that returns a null field for adjacent
14976259Sgreen * separators.  This is the same as the 4.4BSD strsep, but different from the
15076259Sgreen * one in the GNU libc.
15176259Sgreen */
15292555Sdesstatic char *
15376259Sgreenxstrsep(char **str, const char *delim)
15476259Sgreen{
15576259Sgreen	char *s, *e;
15676259Sgreen
15776259Sgreen	if (!**str)
15876259Sgreen		return (NULL);
15976259Sgreen
16076259Sgreen	s = *str;
16176259Sgreen	e = s + strcspn(s, delim);
16276259Sgreen
16376259Sgreen	if (*e != '\0')
16476259Sgreen		*e++ = '\0';
16576259Sgreen	*str = e;
16676259Sgreen
16776259Sgreen	return (s);
16876259Sgreen}
16976259Sgreen
17076259Sgreen/*
17176259Sgreen * Get the next non-null token (like GNU strsep).  Strsep() will return a
17276259Sgreen * null token for two adjacent separators, so we may have to loop.
17376259Sgreen */
17492555Sdesstatic char *
17576259Sgreenstrnnsep(char **stringp, char *delim)
17676259Sgreen{
17776259Sgreen	char *tok;
17876259Sgreen
17976259Sgreen	do {
18076259Sgreen		tok = xstrsep(stringp, delim);
18176259Sgreen	} while (tok && *tok == '\0');
18276259Sgreen	return (tok);
18376259Sgreen}
18476259Sgreen
18592555Sdesstatic Key *
18692555Sdeskeygrab_ssh1(con *c)
18776259Sgreen{
18876259Sgreen	static Key *rsa;
18976259Sgreen	static Buffer msg;
19076259Sgreen
19176259Sgreen	if (rsa == NULL) {
19276259Sgreen		buffer_init(&msg);
19376259Sgreen		rsa = key_new(KEY_RSA1);
19476259Sgreen	}
19592555Sdes	buffer_append(&msg, c->c_data, c->c_plen);
19692555Sdes	buffer_consume(&msg, 8 - (c->c_plen & 7));	/* padding */
19776259Sgreen	if (buffer_get_char(&msg) != (int) SSH_SMSG_PUBLIC_KEY) {
19892555Sdes		error("%s: invalid packet type", c->c_name);
19976259Sgreen		buffer_clear(&msg);
20092555Sdes		return NULL;
20176259Sgreen	}
20276259Sgreen	buffer_consume(&msg, 8);		/* cookie */
20376259Sgreen
20476259Sgreen	/* server key */
20576259Sgreen	(void) buffer_get_int(&msg);
20676259Sgreen	buffer_get_bignum(&msg, rsa->rsa->e);
20776259Sgreen	buffer_get_bignum(&msg, rsa->rsa->n);
20876259Sgreen
20976259Sgreen	/* host key */
21076259Sgreen	(void) buffer_get_int(&msg);
21176259Sgreen	buffer_get_bignum(&msg, rsa->rsa->e);
21276259Sgreen	buffer_get_bignum(&msg, rsa->rsa->n);
21392555Sdes
21476259Sgreen	buffer_clear(&msg);
21576259Sgreen
21692555Sdes	return (rsa);
21792555Sdes}
21892555Sdes
21992555Sdesstatic int
22092555Sdeshostjump(Key *hostkey)
22192555Sdes{
22292555Sdes	kexjmp_key = hostkey;
22392555Sdes	longjmp(kexjmp, 1);
22492555Sdes}
22592555Sdes
22692555Sdesstatic int
22792555Sdesssh2_capable(int remote_major, int remote_minor)
22892555Sdes{
22992555Sdes	switch (remote_major) {
23092555Sdes	case 1:
23192555Sdes		if (remote_minor == 99)
23292555Sdes			return 1;
23392555Sdes		break;
23492555Sdes	case 2:
23592555Sdes		return 1;
23692555Sdes	default:
23792555Sdes		break;
23892555Sdes	}
23992555Sdes	return 0;
24092555Sdes}
24192555Sdes
24292555Sdesstatic Key *
24392555Sdeskeygrab_ssh2(con *c)
24492555Sdes{
24592555Sdes	int j;
24692555Sdes
24792555Sdes	packet_set_connection(c->c_fd, c->c_fd);
24892555Sdes	enable_compat20();
249262566Sdes	myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] =
250262566Sdes	    c->c_keytype == KT_DSA ?  "ssh-dss" :
251262566Sdes	    (c->c_keytype == KT_RSA ? "ssh-rsa" :
252262566Sdes	    (c->c_keytype == KT_ED25519 ? "ssh-ed25519" :
253262566Sdes	    "ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521"));
25492555Sdes	c->c_kex = kex_setup(myproposal);
255113911Sdes	c->c_kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client;
256137019Sdes	c->c_kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client;
257113911Sdes	c->c_kex->kex[KEX_DH_GEX_SHA1] = kexgex_client;
258162856Sdes	c->c_kex->kex[KEX_DH_GEX_SHA256] = kexgex_client;
259221420Sdes	c->c_kex->kex[KEX_ECDH_SHA2] = kexecdh_client;
260262566Sdes	c->c_kex->kex[KEX_C25519_SHA256] = kexc25519_client;
26192555Sdes	c->c_kex->verify_host_key = hostjump;
26292555Sdes
26392555Sdes	if (!(j = setjmp(kexjmp))) {
26492555Sdes		nonfatal_fatal = 1;
26592555Sdes		dispatch_run(DISPATCH_BLOCK, &c->c_kex->done, c->c_kex);
26692555Sdes		fprintf(stderr, "Impossible! dispatch_run() returned!\n");
26792555Sdes		exit(1);
26892555Sdes	}
26992555Sdes	nonfatal_fatal = 0;
270255767Sdes	free(c->c_kex);
27192555Sdes	c->c_kex = NULL;
27292555Sdes	packet_close();
27392555Sdes
27492555Sdes	return j < 0? NULL : kexjmp_key;
27592555Sdes}
27692555Sdes
27792555Sdesstatic void
27892555Sdeskeyprint(con *c, Key *key)
27992555Sdes{
280147005Sdes	char *host = c->c_output_name ? c->c_output_name : c->c_name;
281147005Sdes
28292555Sdes	if (!key)
28392555Sdes		return;
284147005Sdes	if (hash_hosts && (host = host_hash(host, NULL, 0)) == NULL)
285147005Sdes		fatal("host_hash failed");
28692555Sdes
287147005Sdes	fprintf(stdout, "%s ", host);
28892555Sdes	key_write(key, stdout);
28976259Sgreen	fputs("\n", stdout);
29076259Sgreen}
29176259Sgreen
29292555Sdesstatic int
29376259Sgreentcpconnect(char *host)
29476259Sgreen{
29576259Sgreen	struct addrinfo hints, *ai, *aitop;
29676259Sgreen	char strport[NI_MAXSERV];
29776259Sgreen	int gaierr, s = -1;
29876259Sgreen
29992555Sdes	snprintf(strport, sizeof strport, "%d", ssh_port);
30076259Sgreen	memset(&hints, 0, sizeof(hints));
30192555Sdes	hints.ai_family = IPv4or6;
30276259Sgreen	hints.ai_socktype = SOCK_STREAM;
30376259Sgreen	if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0)
304181111Sdes		fatal("getaddrinfo %s: %s", host, ssh_gai_strerror(gaierr));
30576259Sgreen	for (ai = aitop; ai; ai = ai->ai_next) {
306124211Sdes		s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
30776259Sgreen		if (s < 0) {
30876259Sgreen			error("socket: %s", strerror(errno));
30976259Sgreen			continue;
31076259Sgreen		}
311137019Sdes		if (set_nonblock(s) == -1)
312137019Sdes			fatal("%s: set_nonblock(%d)", __func__, s);
31376259Sgreen		if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0 &&
31476259Sgreen		    errno != EINPROGRESS)
31576259Sgreen			error("connect (`%s'): %s", host, strerror(errno));
31676259Sgreen		else
31776259Sgreen			break;
31876259Sgreen		close(s);
31976259Sgreen		s = -1;
32076259Sgreen	}
32176259Sgreen	freeaddrinfo(aitop);
32276259Sgreen	return s;
32376259Sgreen}
32476259Sgreen
32592555Sdesstatic int
32692555Sdesconalloc(char *iname, char *oname, int keytype)
32776259Sgreen{
328106130Sdes	char *namebase, *name, *namelist;
32976259Sgreen	int s;
33076259Sgreen
33176259Sgreen	namebase = namelist = xstrdup(iname);
33276259Sgreen
33376259Sgreen	do {
33476259Sgreen		name = xstrsep(&namelist, ",");
33576259Sgreen		if (!name) {
336255767Sdes			free(namebase);
33776259Sgreen			return (-1);
33876259Sgreen		}
33976259Sgreen	} while ((s = tcpconnect(name)) < 0);
34076259Sgreen
34176259Sgreen	if (s >= maxfd)
34276259Sgreen		fatal("conalloc: fdno %d too high", s);
34376259Sgreen	if (fdcon[s].c_status)
34476259Sgreen		fatal("conalloc: attempt to reuse fdno %d", s);
34576259Sgreen
34676259Sgreen	fdcon[s].c_fd = s;
34776259Sgreen	fdcon[s].c_status = CS_CON;
34876259Sgreen	fdcon[s].c_namebase = namebase;
34976259Sgreen	fdcon[s].c_name = name;
35076259Sgreen	fdcon[s].c_namelist = namelist;
35176259Sgreen	fdcon[s].c_output_name = xstrdup(oname);
35276259Sgreen	fdcon[s].c_data = (char *) &fdcon[s].c_plen;
35376259Sgreen	fdcon[s].c_len = 4;
35476259Sgreen	fdcon[s].c_off = 0;
35592555Sdes	fdcon[s].c_keytype = keytype;
35676259Sgreen	gettimeofday(&fdcon[s].c_tv, NULL);
35776259Sgreen	fdcon[s].c_tv.tv_sec += timeout;
35876259Sgreen	TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link);
35976259Sgreen	FD_SET(s, read_wait);
36076259Sgreen	ncon++;
36176259Sgreen	return (s);
36276259Sgreen}
36376259Sgreen
36492555Sdesstatic void
36576259Sgreenconfree(int s)
36676259Sgreen{
36776259Sgreen	if (s >= maxfd || fdcon[s].c_status == CS_UNUSED)
36876259Sgreen		fatal("confree: attempt to free bad fdno %d", s);
36976259Sgreen	close(s);
370255767Sdes	free(fdcon[s].c_namebase);
371255767Sdes	free(fdcon[s].c_output_name);
37276259Sgreen	if (fdcon[s].c_status == CS_KEYS)
373255767Sdes		free(fdcon[s].c_data);
37476259Sgreen	fdcon[s].c_status = CS_UNUSED;
37592555Sdes	fdcon[s].c_keytype = 0;
37676259Sgreen	TAILQ_REMOVE(&tq, &fdcon[s], c_link);
37776259Sgreen	FD_CLR(s, read_wait);
37876259Sgreen	ncon--;
37976259Sgreen}
38076259Sgreen
38192555Sdesstatic void
38276259Sgreencontouch(int s)
38376259Sgreen{
38476259Sgreen	TAILQ_REMOVE(&tq, &fdcon[s], c_link);
38576259Sgreen	gettimeofday(&fdcon[s].c_tv, NULL);
38676259Sgreen	fdcon[s].c_tv.tv_sec += timeout;
38776259Sgreen	TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link);
38876259Sgreen}
38976259Sgreen
39092555Sdesstatic int
39176259Sgreenconrecycle(int s)
39276259Sgreen{
393106130Sdes	con *c = &fdcon[s];
39476259Sgreen	int ret;
39576259Sgreen
39692555Sdes	ret = conalloc(c->c_namelist, c->c_output_name, c->c_keytype);
39776259Sgreen	confree(s);
39876259Sgreen	return (ret);
39976259Sgreen}
40076259Sgreen
40192555Sdesstatic void
40276259Sgreencongreet(int s)
40376259Sgreen{
404149753Sdes	int n = 0, remote_major = 0, remote_minor = 0;
40592555Sdes	char buf[256], *cp;
40692555Sdes	char remote_version[sizeof buf];
40776259Sgreen	size_t bufsiz;
40876259Sgreen	con *c = &fdcon[s];
40976259Sgreen
410157019Sdes	for (;;) {
411157019Sdes		memset(buf, '\0', sizeof(buf));
412157019Sdes		bufsiz = sizeof(buf);
413157019Sdes		cp = buf;
414157019Sdes		while (bufsiz-- &&
415157019Sdes		    (n = atomicio(read, s, cp, 1)) == 1 && *cp != '\n') {
416157019Sdes			if (*cp == '\r')
417157019Sdes				*cp = '\n';
418157019Sdes			cp++;
419157019Sdes		}
420157019Sdes		if (n != 1 || strncmp(buf, "SSH-", 4) == 0)
421157019Sdes			break;
42292555Sdes	}
423149753Sdes	if (n == 0) {
424149753Sdes		switch (errno) {
425149753Sdes		case EPIPE:
426149753Sdes			error("%s: Connection closed by remote host", c->c_name);
427149753Sdes			break;
428149753Sdes		case ECONNREFUSED:
429149753Sdes			break;
430149753Sdes		default:
43176259Sgreen			error("read (%s): %s", c->c_name, strerror(errno));
432149753Sdes			break;
433149753Sdes		}
43476259Sgreen		conrecycle(s);
43576259Sgreen		return;
43676259Sgreen	}
43776259Sgreen	if (*cp != '\n' && *cp != '\r') {
43876259Sgreen		error("%s: bad greeting", c->c_name);
43976259Sgreen		confree(s);
44076259Sgreen		return;
44176259Sgreen	}
44276259Sgreen	*cp = '\0';
44392555Sdes	if (sscanf(buf, "SSH-%d.%d-%[^\n]\n",
44492555Sdes	    &remote_major, &remote_minor, remote_version) == 3)
44592555Sdes		compat_datafellows(remote_version);
44692555Sdes	else
44792555Sdes		datafellows = 0;
44892555Sdes	if (c->c_keytype != KT_RSA1) {
44992555Sdes		if (!ssh2_capable(remote_major, remote_minor)) {
45092555Sdes			debug("%s doesn't support ssh2", c->c_name);
45192555Sdes			confree(s);
45292555Sdes			return;
45392555Sdes		}
45492555Sdes	} else if (remote_major != 1) {
45592555Sdes		debug("%s doesn't support ssh1", c->c_name);
45692555Sdes		confree(s);
45792555Sdes		return;
45892555Sdes	}
45992555Sdes	fprintf(stderr, "# %s %s\n", c->c_name, chop(buf));
46092555Sdes	n = snprintf(buf, sizeof buf, "SSH-%d.%d-OpenSSH-keyscan\r\n",
46192555Sdes	    c->c_keytype == KT_RSA1? PROTOCOL_MAJOR_1 : PROTOCOL_MAJOR_2,
46292555Sdes	    c->c_keytype == KT_RSA1? PROTOCOL_MINOR_1 : PROTOCOL_MINOR_2);
463149753Sdes	if (n < 0 || (size_t)n >= sizeof(buf)) {
464149753Sdes		error("snprintf: buffer too small");
465149753Sdes		confree(s);
466149753Sdes		return;
467149753Sdes	}
468149753Sdes	if (atomicio(vwrite, s, buf, n) != (size_t)n) {
46976259Sgreen		error("write (%s): %s", c->c_name, strerror(errno));
47076259Sgreen		confree(s);
47176259Sgreen		return;
47276259Sgreen	}
47392555Sdes	if (c->c_keytype != KT_RSA1) {
47492555Sdes		keyprint(c, keygrab_ssh2(c));
47592555Sdes		confree(s);
47692555Sdes		return;
47792555Sdes	}
47876259Sgreen	c->c_status = CS_SIZE;
47976259Sgreen	contouch(s);
48076259Sgreen}
48176259Sgreen
48292555Sdesstatic void
48376259Sgreenconread(int s)
48476259Sgreen{
485106130Sdes	con *c = &fdcon[s];
486149753Sdes	size_t n;
48776259Sgreen
48876259Sgreen	if (c->c_status == CS_CON) {
48976259Sgreen		congreet(s);
49076259Sgreen		return;
49176259Sgreen	}
492137019Sdes	n = atomicio(read, s, c->c_data + c->c_off, c->c_len - c->c_off);
493149753Sdes	if (n == 0) {
49476259Sgreen		error("read (%s): %s", c->c_name, strerror(errno));
49576259Sgreen		confree(s);
49676259Sgreen		return;
49776259Sgreen	}
49876259Sgreen	c->c_off += n;
49976259Sgreen
50076259Sgreen	if (c->c_off == c->c_len)
50176259Sgreen		switch (c->c_status) {
50276259Sgreen		case CS_SIZE:
50376259Sgreen			c->c_plen = htonl(c->c_plen);
50476259Sgreen			c->c_len = c->c_plen + 8 - (c->c_plen & 7);
50576259Sgreen			c->c_off = 0;
50676259Sgreen			c->c_data = xmalloc(c->c_len);
50776259Sgreen			c->c_status = CS_KEYS;
50876259Sgreen			break;
50976259Sgreen		case CS_KEYS:
51092555Sdes			keyprint(c, keygrab_ssh1(c));
51176259Sgreen			confree(s);
51276259Sgreen			return;
51376259Sgreen		default:
51476259Sgreen			fatal("conread: invalid status %d", c->c_status);
51576259Sgreen			break;
51676259Sgreen		}
51776259Sgreen
51876259Sgreen	contouch(s);
51976259Sgreen}
52076259Sgreen
52192555Sdesstatic void
52276259Sgreenconloop(void)
52376259Sgreen{
524106130Sdes	struct timeval seltime, now;
52576259Sgreen	fd_set *r, *e;
526106130Sdes	con *c;
52776259Sgreen	int i;
52876259Sgreen
52976259Sgreen	gettimeofday(&now, NULL);
53098675Sdes	c = TAILQ_FIRST(&tq);
53176259Sgreen
53276259Sgreen	if (c && (c->c_tv.tv_sec > now.tv_sec ||
53376259Sgreen	    (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec > now.tv_usec))) {
53476259Sgreen		seltime = c->c_tv;
53576259Sgreen		seltime.tv_sec -= now.tv_sec;
53676259Sgreen		seltime.tv_usec -= now.tv_usec;
53776259Sgreen		if (seltime.tv_usec < 0) {
53876259Sgreen			seltime.tv_usec += 1000000;
53976259Sgreen			seltime.tv_sec--;
54076259Sgreen		}
54176259Sgreen	} else
542226046Sdes		timerclear(&seltime);
54376259Sgreen
544162856Sdes	r = xcalloc(read_wait_nfdset, sizeof(fd_mask));
545162856Sdes	e = xcalloc(read_wait_nfdset, sizeof(fd_mask));
546162856Sdes	memcpy(r, read_wait, read_wait_nfdset * sizeof(fd_mask));
547162856Sdes	memcpy(e, read_wait, read_wait_nfdset * sizeof(fd_mask));
54876259Sgreen
54976259Sgreen	while (select(maxfd, r, NULL, e, &seltime) == -1 &&
550181111Sdes	    (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK))
55176259Sgreen		;
55276259Sgreen
55376259Sgreen	for (i = 0; i < maxfd; i++) {
55476259Sgreen		if (FD_ISSET(i, e)) {
55576259Sgreen			error("%s: exception!", fdcon[i].c_name);
55676259Sgreen			confree(i);
55776259Sgreen		} else if (FD_ISSET(i, r))
55876259Sgreen			conread(i);
55976259Sgreen	}
560255767Sdes	free(r);
561255767Sdes	free(e);
56276259Sgreen
56398675Sdes	c = TAILQ_FIRST(&tq);
56476259Sgreen	while (c && (c->c_tv.tv_sec < now.tv_sec ||
56576259Sgreen	    (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec < now.tv_usec))) {
56676259Sgreen		int s = c->c_fd;
56776259Sgreen
56898675Sdes		c = TAILQ_NEXT(c, c_link);
56976259Sgreen		conrecycle(s);
57076259Sgreen	}
57176259Sgreen}
57276259Sgreen
57392555Sdesstatic void
57492555Sdesdo_host(char *host)
57576259Sgreen{
57692555Sdes	char *name = strnnsep(&host, " \t\n");
57792555Sdes	int j;
57876259Sgreen
57992555Sdes	if (name == NULL)
58092555Sdes		return;
581262566Sdes	for (j = KT_RSA1; j <= KT_ED25519; j *= 2) {
58292555Sdes		if (get_keytypes & j) {
58392555Sdes			while (ncon >= MAXCON)
58492555Sdes				conloop();
58592555Sdes			conalloc(name, *host ? host : name, j);
58676259Sgreen		}
58776259Sgreen	}
58876259Sgreen}
58976259Sgreen
59076259Sgreenvoid
59192555Sdesfatal(const char *fmt,...)
59292555Sdes{
59392555Sdes	va_list args;
594106130Sdes
59592555Sdes	va_start(args, fmt);
59692555Sdes	do_log(SYSLOG_LEVEL_FATAL, fmt, args);
59792555Sdes	va_end(args);
59892555Sdes	if (nonfatal_fatal)
59992555Sdes		longjmp(kexjmp, -1);
60092555Sdes	else
601126277Sdes		exit(255);
60292555Sdes}
60392555Sdes
60492555Sdesstatic void
60576259Sgreenusage(void)
60676259Sgreen{
607192595Sdes	fprintf(stderr,
608192595Sdes	    "usage: %s [-46Hv] [-f file] [-p port] [-T timeout] [-t type]\n"
609192595Sdes	    "\t\t   [host | addrlist namelist] ...\n",
61092555Sdes	    __progname);
61192555Sdes	exit(1);
61276259Sgreen}
61376259Sgreen
61476259Sgreenint
61576259Sgreenmain(int argc, char **argv)
61676259Sgreen{
61792555Sdes	int debug_flag = 0, log_level = SYSLOG_LEVEL_INFO;
618215116Sdes	int opt, fopt_count = 0, j;
619215116Sdes	char *tname, *cp, line[NI_MAXHOST];
620215116Sdes	FILE *fp;
621215116Sdes	u_long linenum;
62276259Sgreen
62392555Sdes	extern int optind;
62492555Sdes	extern char *optarg;
62592555Sdes
626124211Sdes	__progname = ssh_get_progname(argv[0]);
62798941Sdes	seed_rng();
62876259Sgreen	TAILQ_INIT(&tq);
62976259Sgreen
630157019Sdes	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
631157019Sdes	sanitise_stdfd();
632157019Sdes
63392555Sdes	if (argc <= 1)
63476259Sgreen		usage();
63576259Sgreen
636147005Sdes	while ((opt = getopt(argc, argv, "Hv46p:T:t:f:")) != -1) {
63792555Sdes		switch (opt) {
638147005Sdes		case 'H':
639147005Sdes			hash_hosts = 1;
640147005Sdes			break;
64192555Sdes		case 'p':
64292555Sdes			ssh_port = a2port(optarg);
643192595Sdes			if (ssh_port <= 0) {
64492555Sdes				fprintf(stderr, "Bad port '%s'\n", optarg);
64592555Sdes				exit(1);
64692555Sdes			}
64792555Sdes			break;
64892555Sdes		case 'T':
649106130Sdes			timeout = convtime(optarg);
650106130Sdes			if (timeout == -1 || timeout == 0) {
651106130Sdes				fprintf(stderr, "Bad timeout '%s'\n", optarg);
65276259Sgreen				usage();
653106130Sdes			}
65492555Sdes			break;
65592555Sdes		case 'v':
65692555Sdes			if (!debug_flag) {
65792555Sdes				debug_flag = 1;
65892555Sdes				log_level = SYSLOG_LEVEL_DEBUG1;
65992555Sdes			}
66092555Sdes			else if (log_level < SYSLOG_LEVEL_DEBUG3)
66192555Sdes				log_level++;
66292555Sdes			else
66392555Sdes				fatal("Too high debugging level.");
66492555Sdes			break;
66592555Sdes		case 'f':
66692555Sdes			if (strcmp(optarg, "-") == 0)
66792555Sdes				optarg = NULL;
66892555Sdes			argv[fopt_count++] = optarg;
66992555Sdes			break;
67092555Sdes		case 't':
67192555Sdes			get_keytypes = 0;
67292555Sdes			tname = strtok(optarg, ",");
67392555Sdes			while (tname) {
67492555Sdes				int type = key_type_from_name(tname);
67592555Sdes				switch (type) {
67692555Sdes				case KEY_RSA1:
67792555Sdes					get_keytypes |= KT_RSA1;
67892555Sdes					break;
67992555Sdes				case KEY_DSA:
68092555Sdes					get_keytypes |= KT_DSA;
68192555Sdes					break;
682221420Sdes				case KEY_ECDSA:
683221420Sdes					get_keytypes |= KT_ECDSA;
684221420Sdes					break;
68592555Sdes				case KEY_RSA:
68692555Sdes					get_keytypes |= KT_RSA;
68792555Sdes					break;
688262566Sdes				case KEY_ED25519:
689262566Sdes					get_keytypes |= KT_ED25519;
690262566Sdes					break;
69192555Sdes				case KEY_UNSPEC:
69292555Sdes					fatal("unknown key type %s", tname);
69392555Sdes				}
69492555Sdes				tname = strtok(NULL, ",");
69592555Sdes			}
69692555Sdes			break;
69792555Sdes		case '4':
69892555Sdes			IPv4or6 = AF_INET;
69992555Sdes			break;
70092555Sdes		case '6':
70192555Sdes			IPv4or6 = AF_INET6;
70292555Sdes			break;
70392555Sdes		case '?':
70492555Sdes		default:
70592555Sdes			usage();
70676259Sgreen		}
70776259Sgreen	}
70892555Sdes	if (optind == argc && !fopt_count)
70976259Sgreen		usage();
71076259Sgreen
71192555Sdes	log_init("ssh-keyscan", log_level, SYSLOG_FACILITY_USER, 1);
71292555Sdes
71376259Sgreen	maxfd = fdlim_get(1);
71476259Sgreen	if (maxfd < 0)
71576259Sgreen		fatal("%s: fdlim_get: bad value", __progname);
71676259Sgreen	if (maxfd > MAXMAXFD)
71776259Sgreen		maxfd = MAXMAXFD;
71876259Sgreen	if (MAXCON <= 0)
71976259Sgreen		fatal("%s: not enough file descriptors", __progname);
72076259Sgreen	if (maxfd > fdlim_get(0))
72176259Sgreen		fdlim_set(maxfd);
722162856Sdes	fdcon = xcalloc(maxfd, sizeof(con));
72376259Sgreen
724162856Sdes	read_wait_nfdset = howmany(maxfd, NFDBITS);
725162856Sdes	read_wait = xcalloc(read_wait_nfdset, sizeof(fd_mask));
72676259Sgreen
727215116Sdes	for (j = 0; j < fopt_count; j++) {
728215116Sdes		if (argv[j] == NULL)
729215116Sdes			fp = stdin;
730215116Sdes		else if ((fp = fopen(argv[j], "r")) == NULL)
731215116Sdes			fatal("%s: %s: %s", __progname, argv[j],
732215116Sdes			    strerror(errno));
733215116Sdes		linenum = 0;
73476259Sgreen
735215116Sdes		while (read_keyfile_line(fp,
736215116Sdes		    argv[j] == NULL ? "(stdin)" : argv[j], line, sizeof(line),
737215116Sdes		    &linenum) != -1) {
738215116Sdes			/* Chomp off trailing whitespace and comments */
739215116Sdes			if ((cp = strchr(line, '#')) == NULL)
740215116Sdes				cp = line + strlen(line) - 1;
741215116Sdes			while (cp >= line) {
742215116Sdes				if (*cp == ' ' || *cp == '\t' ||
743215116Sdes				    *cp == '\n' || *cp == '#')
744215116Sdes					*cp-- = '\0';
745215116Sdes				else
746215116Sdes					break;
747215116Sdes			}
748215116Sdes
749215116Sdes			/* Skip empty lines */
750215116Sdes			if (*line == '\0')
75192555Sdes				continue;
752215116Sdes
753215116Sdes			do_host(line);
75476259Sgreen		}
755215116Sdes
756215116Sdes		if (ferror(fp))
757215116Sdes			fatal("%s: %s: %s", __progname, argv[j],
758215116Sdes			    strerror(errno));
759215116Sdes
760215116Sdes		fclose(fp);
76192555Sdes	}
76292555Sdes
76392555Sdes	while (optind < argc)
76492555Sdes		do_host(argv[optind++]);
76592555Sdes
76676259Sgreen	while (ncon > 0)
76776259Sgreen		conloop();
76876259Sgreen
76976259Sgreen	return (0);
77076259Sgreen}
771