ssh-keyscan.c revision 237568
1193323Sed/* $OpenBSD: ssh-keyscan.c,v 1.86 2012/04/11 13:34:17 djm Exp $ */
2193323Sed/*
3193323Sed * Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>.
4193323Sed *
5193323Sed * Modification and redistribution in source and binary forms is
6193323Sed * permitted provided that due credit is given to the author and the
7193323Sed * OpenBSD project by leaving this copyright notice intact.
8193323Sed */
9193323Sed
10193323Sed#include "includes.h"
11193323Sed
12193323Sed#include "openbsd-compat/sys-queue.h"
13193323Sed#include <sys/resource.h>
14193323Sed#ifdef HAVE_SYS_TIME_H
15193323Sed# include <sys/time.h>
16193323Sed#endif
17193323Sed
18193323Sed#include <netinet/in.h>
19193323Sed#include <arpa/inet.h>
20249423Sdim
21249423Sdim#include <openssl/bn.h>
22193323Sed
23198090Srdivacky#include <netdb.h>
24193323Sed#include <errno.h>
25193323Sed#include <setjmp.h>
26193323Sed#include <stdarg.h>
27193323Sed#include <stdio.h>
28249423Sdim#include <stdlib.h>
29193323Sed#include <signal.h>
30193323Sed#include <string.h>
31193323Sed#include <unistd.h>
32193323Sed
33201360Srdivacky#include "xmalloc.h"
34193323Sed#include "ssh.h"
35206083Srdivacky#include "ssh1.h"
36201360Srdivacky#include "buffer.h"
37205218Srdivacky#include "key.h"
38201360Srdivacky#include "cipher.h"
39208599Srdivacky#include "kex.h"
40249423Sdim#include "compat.h"
41193323Sed#include "myproposal.h"
42193323Sed#include "packet.h"
43193323Sed#include "dispatch.h"
44198090Srdivacky#include "log.h"
45193323Sed#include "atomicio.h"
46193323Sed#include "misc.h"
47193323Sed#include "hostfile.h"
48193323Sed
49193323Sed/* Flag indicating whether IPv4 or IPv6.  This can be set on the command line.
50193323Sed   Default value is AF_UNSPEC means both IPv4 and IPv6. */
51193323Sedint IPv4or6 = AF_UNSPEC;
52193323Sed
53193323Sedint ssh_port = SSH_DEFAULT_PORT;
54193323Sed
55193323Sed#define KT_RSA1		1
56234353Sdim#define KT_DSA		2
57193323Sed#define KT_RSA		4
58193323Sed#define KT_ECDSA	8
59193323Sed
60193323Sedint get_keytypes = KT_RSA|KT_ECDSA;/* Get RSA and ECDSA keys by default */
61193323Sed
62205218Srdivackyint hash_hosts = 0;		/* Hash hostname on output */
63205218Srdivacky
64206083Srdivacky#define MAXMAXFD 256
65206083Srdivacky
66206083Srdivacky/* The number of seconds after which to give up on a TCP connection */
67207618Srdivackyint timeout = 5;
68207618Srdivacky
69207618Srdivackyint maxfd;
70207618Srdivacky#define MAXCON (maxfd - 10)
71207618Srdivacky
72207618Srdivackyextern char *__progname;
73205218Srdivackyfd_set *read_wait;
74206083Srdivackysize_t read_wait_nfdset;
75207618Srdivackyint ncon;
76206083Srdivackyint nonfatal_fatal = 0;
77205218Srdivackyjmp_buf kexjmp;
78243830SdimKey *kexjmp_key;
79243830Sdim
80205218Srdivacky/*
81205218Srdivacky * Keep a connection structure for each file descriptor.  The state
82205218Srdivacky * associated with file descriptor n is held in fdcon[n].
83207618Srdivacky */
84207618Srdivackytypedef struct Connection {
85207618Srdivacky	u_char c_status;	/* State of connection on this file desc. */
86207618Srdivacky#define CS_UNUSED 0		/* File descriptor unused */
87206083Srdivacky#define CS_CON 1		/* Waiting to connect/read greeting */
88206083Srdivacky#define CS_SIZE 2		/* Waiting to read initial packet size */
89205218Srdivacky#define CS_KEYS 3		/* Waiting to read public key packet */
90206083Srdivacky	int c_fd;		/* Quick lookup: c->c_fd == c - fdcon */
91205218Srdivacky	int c_plen;		/* Packet length field for ssh packet */
92206083Srdivacky	int c_len;		/* Total bytes which must be read. */
93206083Srdivacky	int c_off;		/* Length of data read so far. */
94207618Srdivacky	int c_keytype;		/* Only one of KT_RSA1, KT_DSA, or KT_RSA */
95205218Srdivacky	char *c_namebase;	/* Address to free for c_name and c_namelist */
96206083Srdivacky	char *c_name;		/* Hostname of connection for errors */
97206083Srdivacky	char *c_namelist;	/* Pointer to other possible addresses */
98207618Srdivacky	char *c_output_name;	/* Hostname of connection for output */
99205218Srdivacky	char *c_data;		/* Data read from this fd */
100206083Srdivacky	Kex *c_kex;		/* The key-exchange struct for ssh2 */
101224145Sdim	struct timeval c_tv;	/* Time at which connection gets aborted */
102224145Sdim	TAILQ_ENTRY(Connection) c_link;	/* List of connections in timeout order. */
103224145Sdim} con;
104224145Sdim
105224145SdimTAILQ_HEAD(conlist, Connection) tq;	/* Timeout Queue */
106224145Sdimcon *fdcon;
107206083Srdivacky
108206083Srdivackystatic int
109206083Srdivackyfdlim_get(int hard)
110206083Srdivacky{
111206083Srdivacky#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)
112207618Srdivacky	struct rlimit rlfd;
113207618Srdivacky
114205218Srdivacky	if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0)
115205218Srdivacky		return (-1);
116202878Srdivacky	if ((hard ? rlfd.rlim_max : rlfd.rlim_cur) == RLIM_INFINITY)
117202878Srdivacky		return SSH_SYSFDMAX;
118202878Srdivacky	else
119202878Srdivacky		return hard ? rlfd.rlim_max : rlfd.rlim_cur;
120193323Sed#else
121193323Sed	return SSH_SYSFDMAX;
122193323Sed#endif
123193323Sed}
124193323Sed
125193323Sedstatic int
126193323Sedfdlim_set(int lim)
127193323Sed{
128193323Sed#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
129193323Sed	struct rlimit rlfd;
130193323Sed#endif
131193323Sed
132207618Srdivacky	if (lim <= 0)
133207618Srdivacky		return (-1);
134208599Srdivacky#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
135249423Sdim	if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0)
136193323Sed		return (-1);
137206274Srdivacky	rlfd.rlim_cur = lim;
138234353Sdim	if (setrlimit(RLIMIT_NOFILE, &rlfd) < 0)
139193323Sed		return (-1);
140193323Sed#elif defined (HAVE_SETDTABLESIZE)
141193323Sed	setdtablesize(lim);
142193323Sed#endif
143193323Sed	return (0);
144193323Sed}
145193323Sed
146193323Sed/*
147193323Sed * This is an strsep function that returns a null field for adjacent
148193323Sed * separators.  This is the same as the 4.4BSD strsep, but different from the
149193323Sed * one in the GNU libc.
150193323Sed */
151193323Sedstatic char *
152193323Sedxstrsep(char **str, const char *delim)
153193323Sed{
154193323Sed	char *s, *e;
155193323Sed
156193323Sed	if (!**str)
157193323Sed		return (NULL);
158193323Sed
159198090Srdivacky	s = *str;
160193323Sed	e = s + strcspn(s, delim);
161193323Sed
162193323Sed	if (*e != '\0')
163193323Sed		*e++ = '\0';
164193323Sed	*str = e;
165193323Sed
166193323Sed	return (s);
167193323Sed}
168193323Sed
169201360Srdivacky/*
170201360Srdivacky * Get the next non-null token (like GNU strsep).  Strsep() will return a
171201360Srdivacky * null token for two adjacent separators, so we may have to loop.
172200581Srdivacky */
173205218Srdivackystatic char *
174205218Srdivackystrnnsep(char **stringp, char *delim)
175205218Srdivacky{
176239462Sdim	char *tok;
177239462Sdim
178239462Sdim	do {
179239462Sdim		tok = xstrsep(stringp, delim);
180239462Sdim	} while (tok && *tok == '\0');
181239462Sdim	return (tok);
182239462Sdim}
183239462Sdim
184239462Sdimstatic Key *
185239462Sdimkeygrab_ssh1(con *c)
186239462Sdim{
187239462Sdim	static Key *rsa;
188239462Sdim	static Buffer msg;
189239462Sdim
190239462Sdim	if (rsa == NULL) {
191239462Sdim		buffer_init(&msg);
192239462Sdim		rsa = key_new(KEY_RSA1);
193239462Sdim	}
194239462Sdim	buffer_append(&msg, c->c_data, c->c_plen);
195239462Sdim	buffer_consume(&msg, 8 - (c->c_plen & 7));	/* padding */
196239462Sdim	if (buffer_get_char(&msg) != (int) SSH_SMSG_PUBLIC_KEY) {
197239462Sdim		error("%s: invalid packet type", c->c_name);
198239462Sdim		buffer_clear(&msg);
199239462Sdim		return NULL;
200239462Sdim	}
201239462Sdim	buffer_consume(&msg, 8);		/* cookie */
202239462Sdim
203239462Sdim	/* server key */
204239462Sdim	(void) buffer_get_int(&msg);
205239462Sdim	buffer_get_bignum(&msg, rsa->rsa->e);
206239462Sdim	buffer_get_bignum(&msg, rsa->rsa->n);
207239462Sdim
208239462Sdim	/* host key */
209239462Sdim	(void) buffer_get_int(&msg);
210239462Sdim	buffer_get_bignum(&msg, rsa->rsa->e);
211239462Sdim	buffer_get_bignum(&msg, rsa->rsa->n);
212239462Sdim
213239462Sdim	buffer_clear(&msg);
214193323Sed
215193323Sed	return (rsa);
216193323Sed}
217193323Sed
218193323Sedstatic int
219193323Sedhostjump(Key *hostkey)
220193323Sed{
221243830Sdim	kexjmp_key = hostkey;
222243830Sdim	longjmp(kexjmp, 1);
223200581Srdivacky}
224193323Sed
225234353Sdimstatic int
226193323Sedssh2_capable(int remote_major, int remote_minor)
227193323Sed{
228193323Sed	switch (remote_major) {
229193323Sed	case 1:
230193323Sed		if (remote_minor == 99)
231249423Sdim			return 1;
232193323Sed		break;
233193323Sed	case 2:
234193323Sed		return 1;
235193323Sed	default:
236193323Sed		break;
237193323Sed	}
238193323Sed	return 0;
239207618Srdivacky}
240207618Srdivacky
241208599Srdivackystatic Key *
242249423Sdimkeygrab_ssh2(con *c)
243198090Srdivacky{
244193323Sed	int j;
245193323Sed
246193323Sed	packet_set_connection(c->c_fd, c->c_fd);
247193323Sed	enable_compat20();
248193323Sed	myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = c->c_keytype == KT_DSA?
249193323Sed	    "ssh-dss" : (c->c_keytype == KT_RSA ? "ssh-rsa" :
250193323Sed	    "ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521");
251193323Sed	c->c_kex = kex_setup(myproposal);
252193323Sed	c->c_kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client;
253193323Sed	c->c_kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client;
254193323Sed	c->c_kex->kex[KEX_DH_GEX_SHA1] = kexgex_client;
255193323Sed	c->c_kex->kex[KEX_DH_GEX_SHA256] = kexgex_client;
256193323Sed	c->c_kex->kex[KEX_ECDH_SHA2] = kexecdh_client;
257193323Sed	c->c_kex->verify_host_key = hostjump;
258193323Sed
259193323Sed	if (!(j = setjmp(kexjmp))) {
260193323Sed		nonfatal_fatal = 1;
261193323Sed		dispatch_run(DISPATCH_BLOCK, &c->c_kex->done, c->c_kex);
262193323Sed		fprintf(stderr, "Impossible! dispatch_run() returned!\n");
263193323Sed		exit(1);
264193323Sed	}
265193323Sed	nonfatal_fatal = 0;
266193323Sed	xfree(c->c_kex);
267193323Sed	c->c_kex = NULL;
268193323Sed	packet_close();
269193323Sed
270193323Sed	return j < 0? NULL : kexjmp_key;
271193323Sed}
272193323Sed
273193323Sedstatic void
274193323Sedkeyprint(con *c, Key *key)
275193323Sed{
276193323Sed	char *host = c->c_output_name ? c->c_output_name : c->c_name;
277193323Sed
278193323Sed	if (!key)
279193323Sed		return;
280193323Sed	if (hash_hosts && (host = host_hash(host, NULL, 0)) == NULL)
281193323Sed		fatal("host_hash failed");
282193323Sed
283193323Sed	fprintf(stdout, "%s ", host);
284193323Sed	key_write(key, stdout);
285193323Sed	fputs("\n", stdout);
286193323Sed}
287193323Sed
288193323Sedstatic int
289193323Sedtcpconnect(char *host)
290193323Sed{
291193323Sed	struct addrinfo hints, *ai, *aitop;
292193323Sed	char strport[NI_MAXSERV];
293193323Sed	int gaierr, s = -1;
294193323Sed
295193323Sed	snprintf(strport, sizeof strport, "%d", ssh_port);
296193323Sed	memset(&hints, 0, sizeof(hints));
297193323Sed	hints.ai_family = IPv4or6;
298193323Sed	hints.ai_socktype = SOCK_STREAM;
299202878Srdivacky	if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0)
300202878Srdivacky		fatal("getaddrinfo %s: %s", host, ssh_gai_strerror(gaierr));
301202878Srdivacky	for (ai = aitop; ai; ai = ai->ai_next) {
302202878Srdivacky		s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
303202878Srdivacky		if (s < 0) {
304202878Srdivacky			error("socket: %s", strerror(errno));
305193323Sed			continue;
306193323Sed		}
307193323Sed		if (set_nonblock(s) == -1)
308193323Sed			fatal("%s: set_nonblock(%d)", __func__, s);
309193323Sed		if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0 &&
310193323Sed		    errno != EINPROGRESS)
311193323Sed			error("connect (`%s'): %s", host, strerror(errno));
312193323Sed		else
313193323Sed			break;
314193323Sed		close(s);
315193323Sed		s = -1;
316193323Sed	}
317193323Sed	freeaddrinfo(aitop);
318193323Sed	return s;
319193323Sed}
320193323Sed
321193323Sedstatic int
322193323Sedconalloc(char *iname, char *oname, int keytype)
323193323Sed{
324193323Sed	char *namebase, *name, *namelist;
325193323Sed	int s;
326193323Sed
327193323Sed	namebase = namelist = xstrdup(iname);
328223017Sdim
329193323Sed	do {
330193323Sed		name = xstrsep(&namelist, ",");
331193323Sed		if (!name) {
332193323Sed			xfree(namebase);
333193323Sed			return (-1);
334193323Sed		}
335193323Sed	} while ((s = tcpconnect(name)) < 0);
336193323Sed
337193323Sed	if (s >= maxfd)
338193323Sed		fatal("conalloc: fdno %d too high", s);
339193323Sed	if (fdcon[s].c_status)
340193323Sed		fatal("conalloc: attempt to reuse fdno %d", s);
341193323Sed
342193323Sed	fdcon[s].c_fd = s;
343193323Sed	fdcon[s].c_status = CS_CON;
344193323Sed	fdcon[s].c_namebase = namebase;
345193323Sed	fdcon[s].c_name = name;
346193323Sed	fdcon[s].c_namelist = namelist;
347193323Sed	fdcon[s].c_output_name = xstrdup(oname);
348193323Sed	fdcon[s].c_data = (char *) &fdcon[s].c_plen;
349193323Sed	fdcon[s].c_len = 4;
350193323Sed	fdcon[s].c_off = 0;
351193323Sed	fdcon[s].c_keytype = keytype;
352193323Sed	gettimeofday(&fdcon[s].c_tv, NULL);
353198090Srdivacky	fdcon[s].c_tv.tv_sec += timeout;
354198090Srdivacky	TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link);
355198090Srdivacky	FD_SET(s, read_wait);
356198090Srdivacky	ncon++;
357198090Srdivacky	return (s);
358193323Sed}
359193323Sed
360193323Sedstatic void
361193323Sedconfree(int s)
362198090Srdivacky{
363198090Srdivacky	if (s >= maxfd || fdcon[s].c_status == CS_UNUSED)
364198090Srdivacky		fatal("confree: attempt to free bad fdno %d", s);
365193323Sed	close(s);
366198090Srdivacky	xfree(fdcon[s].c_namebase);
367193323Sed	xfree(fdcon[s].c_output_name);
368193323Sed	if (fdcon[s].c_status == CS_KEYS)
369198090Srdivacky		xfree(fdcon[s].c_data);
370193323Sed	fdcon[s].c_status = CS_UNUSED;
371193323Sed	fdcon[s].c_keytype = 0;
372198090Srdivacky	TAILQ_REMOVE(&tq, &fdcon[s], c_link);
373193323Sed	FD_CLR(s, read_wait);
374193323Sed	ncon--;
375208599Srdivacky}
376208599Srdivacky
377198090Srdivackystatic void
378198090Srdivackycontouch(int s)
379198090Srdivacky{
380198090Srdivacky	TAILQ_REMOVE(&tq, &fdcon[s], c_link);
381193323Sed	gettimeofday(&fdcon[s].c_tv, NULL);
382193323Sed	fdcon[s].c_tv.tv_sec += timeout;
383198090Srdivacky	TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link);
384193323Sed}
385193323Sed
386198090Srdivackystatic int
387193323Sedconrecycle(int s)
388193323Sed{
389210299Sed	con *c = &fdcon[s];
390195098Sed	int ret;
391195098Sed
392210299Sed	ret = conalloc(c->c_namelist, c->c_output_name, c->c_keytype);
393195098Sed	confree(s);
394195098Sed	return (ret);
395210299Sed}
396193323Sed
397198090Srdivackystatic void
398198090Srdivackycongreet(int s)
399193323Sed{
400193323Sed	int n = 0, remote_major = 0, remote_minor = 0;
401198090Srdivacky	char buf[256], *cp;
402195098Sed	char remote_version[sizeof buf];
403198090Srdivacky	size_t bufsiz;
404195098Sed	con *c = &fdcon[s];
405193323Sed
406207618Srdivacky	for (;;) {
407195098Sed		memset(buf, '\0', sizeof(buf));
408195098Sed		bufsiz = sizeof(buf);
409207618Srdivacky		cp = buf;
410195098Sed		while (bufsiz-- &&
411195098Sed		    (n = atomicio(read, s, cp, 1)) == 1 && *cp != '\n') {
412195098Sed			if (*cp == '\r')
413193323Sed				*cp = '\n';
414198090Srdivacky			cp++;
415195098Sed		}
416195098Sed		if (n != 1 || strncmp(buf, "SSH-", 4) == 0)
417193323Sed			break;
418198090Srdivacky	}
419195098Sed	if (n == 0) {
420195098Sed		switch (errno) {
421193323Sed		case EPIPE:
422239462Sdim			error("%s: Connection closed by remote host", c->c_name);
423239462Sdim			break;
424193323Sed		case ECONNREFUSED:
425193323Sed			break;
426193323Sed		default:
427193323Sed			error("read (%s): %s", c->c_name, strerror(errno));
428198090Srdivacky			break;
429198090Srdivacky		}
430198090Srdivacky		conrecycle(s);
431195098Sed		return;
432198090Srdivacky	}
433198090Srdivacky	if (*cp != '\n' && *cp != '\r') {
434234353Sdim		error("%s: bad greeting", c->c_name);
435205218Srdivacky		confree(s);
436207618Srdivacky		return;
437243830Sdim	}
438243830Sdim	*cp = '\0';
439243830Sdim	if (sscanf(buf, "SSH-%d.%d-%[^\n]\n",
440243830Sdim	    &remote_major, &remote_minor, remote_version) == 3)
441243830Sdim		compat_datafellows(remote_version);
442243830Sdim	else
443243830Sdim		datafellows = 0;
444193323Sed	if (c->c_keytype != KT_RSA1) {
445193323Sed		if (!ssh2_capable(remote_major, remote_minor)) {
446193323Sed			debug("%s doesn't support ssh2", c->c_name);
447193323Sed			confree(s);
448193323Sed			return;
449193323Sed		}
450193323Sed	} else if (remote_major != 1) {
451218893Sdim		debug("%s doesn't support ssh1", c->c_name);
452218893Sdim		confree(s);
453193323Sed		return;
454218893Sdim	}
455218893Sdim	fprintf(stderr, "# %s %s\n", c->c_name, chop(buf));
456218893Sdim	n = snprintf(buf, sizeof buf, "SSH-%d.%d-OpenSSH-keyscan\r\n",
457218893Sdim	    c->c_keytype == KT_RSA1? PROTOCOL_MAJOR_1 : PROTOCOL_MAJOR_2,
458193323Sed	    c->c_keytype == KT_RSA1? PROTOCOL_MINOR_1 : PROTOCOL_MINOR_2);
459193323Sed	if (n < 0 || (size_t)n >= sizeof(buf)) {
460193323Sed		error("snprintf: buffer too small");
461193323Sed		confree(s);
462218893Sdim		return;
463218893Sdim	}
464218893Sdim	if (atomicio(vwrite, s, buf, n) != (size_t)n) {
465218893Sdim		error("write (%s): %s", c->c_name, strerror(errno));
466193323Sed		confree(s);
467193323Sed		return;
468198090Srdivacky	}
469193323Sed	if (c->c_keytype != KT_RSA1) {
470193323Sed		keyprint(c, keygrab_ssh2(c));
471193323Sed		confree(s);
472193323Sed		return;
473193323Sed	}
474193323Sed	c->c_status = CS_SIZE;
475218893Sdim	contouch(s);
476218893Sdim}
477198090Srdivacky
478218893Sdimstatic void
479218893Sdimconread(int s)
480218893Sdim{
481218893Sdim	con *c = &fdcon[s];
482193323Sed	size_t n;
483193323Sed
484193323Sed	if (c->c_status == CS_CON) {
485193323Sed		congreet(s);
486193323Sed		return;
487193323Sed	}
488198090Srdivacky	n = atomicio(read, s, c->c_data + c->c_off, c->c_len - c->c_off);
489193323Sed	if (n == 0) {
490193323Sed		error("read (%s): %s", c->c_name, strerror(errno));
491221345Sdim		confree(s);
492193323Sed		return;
493193323Sed	}
494193323Sed	c->c_off += n;
495193323Sed
496221345Sdim	if (c->c_off == c->c_len)
497193323Sed		switch (c->c_status) {
498193323Sed		case CS_SIZE:
499226633Sdim			c->c_plen = htonl(c->c_plen);
500226633Sdim			c->c_len = c->c_plen + 8 - (c->c_plen & 7);
501226633Sdim			c->c_off = 0;
502226633Sdim			c->c_data = xmalloc(c->c_len);
503198090Srdivacky			c->c_status = CS_KEYS;
504198090Srdivacky			break;
505198090Srdivacky		case CS_KEYS:
506198090Srdivacky			keyprint(c, keygrab_ssh1(c));
507198090Srdivacky			confree(s);
508198090Srdivacky			return;
509198090Srdivacky		default:
510198090Srdivacky			fatal("conread: invalid status %d", c->c_status);
511193323Sed			break;
512193323Sed		}
513198090Srdivacky
514193323Sed	contouch(s);
515193323Sed}
516198090Srdivacky
517193323Sedstatic void
518193323Sedconloop(void)
519218893Sdim{
520193323Sed	struct timeval seltime, now;
521193323Sed	fd_set *r, *e;
522218893Sdim	con *c;
523193323Sed	int i;
524206124Srdivacky
525193323Sed	gettimeofday(&now, NULL);
526193323Sed	c = TAILQ_FIRST(&tq);
527193323Sed
528218893Sdim	if (c && (c->c_tv.tv_sec > now.tv_sec ||
529193323Sed	    (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec > now.tv_usec))) {
530193323Sed		seltime = c->c_tv;
531218893Sdim		seltime.tv_sec -= now.tv_sec;
532218893Sdim		seltime.tv_usec -= now.tv_usec;
533193323Sed		if (seltime.tv_usec < 0) {
534193323Sed			seltime.tv_usec += 1000000;
535193323Sed			seltime.tv_sec--;
536193323Sed		}
537218893Sdim	} else
538206124Srdivacky		timerclear(&seltime);
539218893Sdim
540193323Sed	r = xcalloc(read_wait_nfdset, sizeof(fd_mask));
541193323Sed	e = xcalloc(read_wait_nfdset, sizeof(fd_mask));
542193323Sed	memcpy(r, read_wait, read_wait_nfdset * sizeof(fd_mask));
543198090Srdivacky	memcpy(e, read_wait, read_wait_nfdset * sizeof(fd_mask));
544206124Srdivacky
545193323Sed	while (select(maxfd, r, NULL, e, &seltime) == -1 &&
546193323Sed	    (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK))
547193323Sed		;
548193323Sed
549198090Srdivacky	for (i = 0; i < maxfd; i++) {
550206124Srdivacky		if (FD_ISSET(i, e)) {
551193323Sed			error("%s: exception!", fdcon[i].c_name);
552193323Sed			confree(i);
553193323Sed		} else if (FD_ISSET(i, r))
554193323Sed			conread(i);
555198090Srdivacky	}
556198090Srdivacky	xfree(r);
557198090Srdivacky	xfree(e);
558198090Srdivacky
559193323Sed	c = TAILQ_FIRST(&tq);
560198090Srdivacky	while (c && (c->c_tv.tv_sec < now.tv_sec ||
561193323Sed	    (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec < now.tv_usec))) {
562198090Srdivacky		int s = c->c_fd;
563193323Sed
564193323Sed		c = TAILQ_NEXT(c, c_link);
565198090Srdivacky		conrecycle(s);
566193323Sed	}
567198090Srdivacky}
568193323Sed
569193323Sedstatic void
570249423Sdimdo_host(char *host)
571193323Sed{
572198090Srdivacky	char *name = strnnsep(&host, " \t\n");
573193323Sed	int j;
574193323Sed
575193323Sed	if (name == NULL)
576193323Sed		return;
577193323Sed	for (j = KT_RSA1; j <= KT_ECDSA; j *= 2) {
578193323Sed		if (get_keytypes & j) {
579193323Sed			while (ncon >= MAXCON)
580193323Sed				conloop();
581193323Sed			conalloc(name, *host ? host : name, j);
582193323Sed		}
583193323Sed	}
584193323Sed}
585193323Sed
586193323Sedvoid
587193323Sedfatal(const char *fmt,...)
588198090Srdivacky{
589198090Srdivacky	va_list args;
590198090Srdivacky
591198090Srdivacky	va_start(args, fmt);
592198090Srdivacky	do_log(SYSLOG_LEVEL_FATAL, fmt, args);
593198090Srdivacky	va_end(args);
594193323Sed	if (nonfatal_fatal)
595206274Srdivacky		longjmp(kexjmp, -1);
596218893Sdim	else
597218893Sdim		exit(255);
598193323Sed}
599193323Sed
600206274Srdivackystatic void
601218893Sdimusage(void)
602218893Sdim{
603193323Sed	fprintf(stderr,
604193323Sed	    "usage: %s [-46Hv] [-f file] [-p port] [-T timeout] [-t type]\n"
605206274Srdivacky	    "\t\t   [host | addrlist namelist] ...\n",
606218893Sdim	    __progname);
607193323Sed	exit(1);
608193323Sed}
609193323Sed
610193323Sedint
611198090Srdivackymain(int argc, char **argv)
612193323Sed{
613226633Sdim	int debug_flag = 0, log_level = SYSLOG_LEVEL_INFO;
614226633Sdim	int opt, fopt_count = 0, j;
615226633Sdim	char *tname, *cp, line[NI_MAXHOST];
616226633Sdim	FILE *fp;
617193323Sed	u_long linenum;
618193323Sed
619193323Sed	extern int optind;
620193323Sed	extern char *optarg;
621193323Sed
622193323Sed	__progname = ssh_get_progname(argv[0]);
623193323Sed	seed_rng();
624193323Sed	TAILQ_INIT(&tq);
625193323Sed
626193323Sed	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
627193323Sed	sanitise_stdfd();
628193323Sed
629193323Sed	if (argc <= 1)
630193323Sed		usage();
631198090Srdivacky
632210299Sed	while ((opt = getopt(argc, argv, "Hv46p:T:t:f:")) != -1) {
633193323Sed		switch (opt) {
634193323Sed		case 'H':
635193323Sed			hash_hosts = 1;
636198090Srdivacky			break;
637218893Sdim		case 'p':
638226633Sdim			ssh_port = a2port(optarg);
639226633Sdim			if (ssh_port <= 0) {
640226633Sdim				fprintf(stderr, "Bad port '%s'\n", optarg);
641198090Srdivacky				exit(1);
642198090Srdivacky			}
643226633Sdim			break;
644226633Sdim		case 'T':
645226633Sdim			timeout = convtime(optarg);
646193323Sed			if (timeout == -1 || timeout == 0) {
647226633Sdim				fprintf(stderr, "Bad timeout '%s'\n", optarg);
648226633Sdim				usage();
649198090Srdivacky			}
650193323Sed			break;
651226633Sdim		case 'v':
652226633Sdim			if (!debug_flag) {
653198090Srdivacky				debug_flag = 1;
654226633Sdim				log_level = SYSLOG_LEVEL_DEBUG1;
655226633Sdim			}
656226633Sdim			else if (log_level < SYSLOG_LEVEL_DEBUG3)
657193323Sed				log_level++;
658226633Sdim			else
659226633Sdim				fatal("Too high debugging level.");
660226633Sdim			break;
661226633Sdim		case 'f':
662226633Sdim			if (strcmp(optarg, "-") == 0)
663226633Sdim				optarg = NULL;
664226633Sdim			argv[fopt_count++] = optarg;
665226633Sdim			break;
666226633Sdim		case 't':
667226633Sdim			get_keytypes = 0;
668226633Sdim			tname = strtok(optarg, ",");
669226633Sdim			while (tname) {
670193323Sed				int type = key_type_from_name(tname);
671198090Srdivacky				switch (type) {
672198090Srdivacky				case KEY_RSA1:
673198090Srdivacky					get_keytypes |= KT_RSA1;
674193323Sed					break;
675198090Srdivacky				case KEY_DSA:
676193323Sed					get_keytypes |= KT_DSA;
677218893Sdim					break;
678193323Sed				case KEY_ECDSA:
679193323Sed					get_keytypes |= KT_ECDSA;
680193323Sed					break;
681193323Sed				case KEY_RSA:
682193323Sed					get_keytypes |= KT_RSA;
683218893Sdim					break;
684193323Sed				case KEY_UNSPEC:
685193323Sed					fatal("unknown key type %s", tname);
686193323Sed				}
687198090Srdivacky				tname = strtok(NULL, ",");
688198090Srdivacky			}
689198090Srdivacky			break;
690198090Srdivacky		case '4':
691193323Sed			IPv4or6 = AF_INET;
692193323Sed			break;
693193323Sed		case '6':
694193323Sed			IPv4or6 = AF_INET6;
695193323Sed			break;
696193323Sed		case '?':
697198090Srdivacky		default:
698218893Sdim			usage();
699234353Sdim		}
700234353Sdim	}
701218893Sdim	if (optind == argc && !fopt_count)
702218893Sdim		usage();
703218893Sdim
704218893Sdim	log_init("ssh-keyscan", log_level, SYSLOG_FACILITY_USER, 1);
705218893Sdim
706193323Sed	maxfd = fdlim_get(1);
707210299Sed	if (maxfd < 0)
708210299Sed		fatal("%s: fdlim_get: bad value", __progname);
709210299Sed	if (maxfd > MAXMAXFD)
710210299Sed		maxfd = MAXMAXFD;
711218893Sdim	if (MAXCON <= 0)
712234353Sdim		fatal("%s: not enough file descriptors", __progname);
713234353Sdim	if (maxfd > fdlim_get(0))
714234353Sdim		fdlim_set(maxfd);
715210299Sed	fdcon = xcalloc(maxfd, sizeof(con));
716210299Sed
717210299Sed	read_wait_nfdset = howmany(maxfd, NFDBITS);
718198090Srdivacky	read_wait = xcalloc(read_wait_nfdset, sizeof(fd_mask));
719193323Sed
720193323Sed	for (j = 0; j < fopt_count; j++) {
721193323Sed		if (argv[j] == NULL)
722193323Sed			fp = stdin;
723218893Sdim		else if ((fp = fopen(argv[j], "r")) == NULL)
724218893Sdim			fatal("%s: %s: %s", __progname, argv[j],
725218893Sdim			    strerror(errno));
726198090Srdivacky		linenum = 0;
727198090Srdivacky
728193323Sed		while (read_keyfile_line(fp,
729218893Sdim		    argv[j] == NULL ? "(stdin)" : argv[j], line, sizeof(line),
730203954Srdivacky		    &linenum) != -1) {
731218893Sdim			/* Chomp off trailing whitespace and comments */
732218893Sdim			if ((cp = strchr(line, '#')) == NULL)
733198090Srdivacky				cp = line + strlen(line) - 1;
734198090Srdivacky			while (cp >= line) {
735193323Sed				if (*cp == ' ' || *cp == '\t' ||
736193323Sed				    *cp == '\n' || *cp == '#')
737193323Sed					*cp-- = '\0';
738193323Sed				else
739193323Sed					break;
740193323Sed			}
741207618Srdivacky
742207618Srdivacky			/* Skip empty lines */
743221345Sdim			if (*line == '\0')
744193323Sed				continue;
745193323Sed
746221345Sdim			do_host(line);
747193323Sed		}
748193323Sed
749193323Sed		if (ferror(fp))
750193323Sed			fatal("%s: %s: %s", __progname, argv[j],
751193323Sed			    strerror(errno));
752193323Sed
753193323Sed		fclose(fp);
754210299Sed	}
755210299Sed
756210299Sed	while (optind < argc)
757193323Sed		do_host(argv[optind++]);
758210299Sed
759193323Sed	while (ncon > 0)
760210299Sed		conloop();
761193323Sed
762210299Sed	return (0);
763193323Sed}
764193323Sed