ssh-keyscan.c revision 215116
1/* $OpenBSD: ssh-keyscan.c,v 1.82 2010/06/22 04:54:30 djm Exp $ */
2/*
3 * Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>.
4 *
5 * Modification and redistribution in source and binary forms is
6 * permitted provided that due credit is given to the author and the
7 * OpenBSD project by leaving this copyright notice intact.
8 */
9
10#include "includes.h"
11
12#include "openbsd-compat/sys-queue.h"
13#include <sys/resource.h>
14#ifdef HAVE_SYS_TIME_H
15# include <sys/time.h>
16#endif
17
18#include <netinet/in.h>
19#include <arpa/inet.h>
20
21#include <openssl/bn.h>
22
23#include <netdb.h>
24#include <errno.h>
25#include <setjmp.h>
26#include <stdarg.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <signal.h>
30#include <string.h>
31#include <unistd.h>
32
33#include "xmalloc.h"
34#include "ssh.h"
35#include "ssh1.h"
36#include "buffer.h"
37#include "key.h"
38#include "cipher.h"
39#include "kex.h"
40#include "compat.h"
41#include "myproposal.h"
42#include "packet.h"
43#include "dispatch.h"
44#include "log.h"
45#include "atomicio.h"
46#include "misc.h"
47#include "hostfile.h"
48
49/* Flag indicating whether IPv4 or IPv6.  This can be set on the command line.
50   Default value is AF_UNSPEC means both IPv4 and IPv6. */
51int IPv4or6 = AF_UNSPEC;
52
53int ssh_port = SSH_DEFAULT_PORT;
54
55#define KT_RSA1	1
56#define KT_DSA	2
57#define KT_RSA	4
58
59int get_keytypes = KT_RSA;	/* Get only RSA keys by default */
60
61int hash_hosts = 0;		/* Hash hostname on output */
62
63#define MAXMAXFD 256
64
65/* The number of seconds after which to give up on a TCP connection */
66int timeout = 5;
67
68int maxfd;
69#define MAXCON (maxfd - 10)
70
71extern char *__progname;
72fd_set *read_wait;
73size_t read_wait_nfdset;
74int ncon;
75int nonfatal_fatal = 0;
76jmp_buf kexjmp;
77Key *kexjmp_key;
78
79/*
80 * Keep a connection structure for each file descriptor.  The state
81 * associated with file descriptor n is held in fdcon[n].
82 */
83typedef struct Connection {
84	u_char c_status;	/* State of connection on this file desc. */
85#define CS_UNUSED 0		/* File descriptor unused */
86#define CS_CON 1		/* Waiting to connect/read greeting */
87#define CS_SIZE 2		/* Waiting to read initial packet size */
88#define CS_KEYS 3		/* Waiting to read public key packet */
89	int c_fd;		/* Quick lookup: c->c_fd == c - fdcon */
90	int c_plen;		/* Packet length field for ssh packet */
91	int c_len;		/* Total bytes which must be read. */
92	int c_off;		/* Length of data read so far. */
93	int c_keytype;		/* Only one of KT_RSA1, KT_DSA, or KT_RSA */
94	char *c_namebase;	/* Address to free for c_name and c_namelist */
95	char *c_name;		/* Hostname of connection for errors */
96	char *c_namelist;	/* Pointer to other possible addresses */
97	char *c_output_name;	/* Hostname of connection for output */
98	char *c_data;		/* Data read from this fd */
99	Kex *c_kex;		/* The key-exchange struct for ssh2 */
100	struct timeval c_tv;	/* Time at which connection gets aborted */
101	TAILQ_ENTRY(Connection) c_link;	/* List of connections in timeout order. */
102} con;
103
104TAILQ_HEAD(conlist, Connection) tq;	/* Timeout Queue */
105con *fdcon;
106
107static int
108fdlim_get(int hard)
109{
110#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)
111	struct rlimit rlfd;
112
113	if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0)
114		return (-1);
115	if ((hard ? rlfd.rlim_max : rlfd.rlim_cur) == RLIM_INFINITY)
116		return SSH_SYSFDMAX;
117	else
118		return hard ? rlfd.rlim_max : rlfd.rlim_cur;
119#else
120	return SSH_SYSFDMAX;
121#endif
122}
123
124static int
125fdlim_set(int lim)
126{
127#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
128	struct rlimit rlfd;
129#endif
130
131	if (lim <= 0)
132		return (-1);
133#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
134	if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0)
135		return (-1);
136	rlfd.rlim_cur = lim;
137	if (setrlimit(RLIMIT_NOFILE, &rlfd) < 0)
138		return (-1);
139#elif defined (HAVE_SETDTABLESIZE)
140	setdtablesize(lim);
141#endif
142	return (0);
143}
144
145/*
146 * This is an strsep function that returns a null field for adjacent
147 * separators.  This is the same as the 4.4BSD strsep, but different from the
148 * one in the GNU libc.
149 */
150static char *
151xstrsep(char **str, const char *delim)
152{
153	char *s, *e;
154
155	if (!**str)
156		return (NULL);
157
158	s = *str;
159	e = s + strcspn(s, delim);
160
161	if (*e != '\0')
162		*e++ = '\0';
163	*str = e;
164
165	return (s);
166}
167
168/*
169 * Get the next non-null token (like GNU strsep).  Strsep() will return a
170 * null token for two adjacent separators, so we may have to loop.
171 */
172static char *
173strnnsep(char **stringp, char *delim)
174{
175	char *tok;
176
177	do {
178		tok = xstrsep(stringp, delim);
179	} while (tok && *tok == '\0');
180	return (tok);
181}
182
183static Key *
184keygrab_ssh1(con *c)
185{
186	static Key *rsa;
187	static Buffer msg;
188
189	if (rsa == NULL) {
190		buffer_init(&msg);
191		rsa = key_new(KEY_RSA1);
192	}
193	buffer_append(&msg, c->c_data, c->c_plen);
194	buffer_consume(&msg, 8 - (c->c_plen & 7));	/* padding */
195	if (buffer_get_char(&msg) != (int) SSH_SMSG_PUBLIC_KEY) {
196		error("%s: invalid packet type", c->c_name);
197		buffer_clear(&msg);
198		return NULL;
199	}
200	buffer_consume(&msg, 8);		/* cookie */
201
202	/* server key */
203	(void) buffer_get_int(&msg);
204	buffer_get_bignum(&msg, rsa->rsa->e);
205	buffer_get_bignum(&msg, rsa->rsa->n);
206
207	/* host key */
208	(void) buffer_get_int(&msg);
209	buffer_get_bignum(&msg, rsa->rsa->e);
210	buffer_get_bignum(&msg, rsa->rsa->n);
211
212	buffer_clear(&msg);
213
214	return (rsa);
215}
216
217static int
218hostjump(Key *hostkey)
219{
220	kexjmp_key = hostkey;
221	longjmp(kexjmp, 1);
222}
223
224static int
225ssh2_capable(int remote_major, int remote_minor)
226{
227	switch (remote_major) {
228	case 1:
229		if (remote_minor == 99)
230			return 1;
231		break;
232	case 2:
233		return 1;
234	default:
235		break;
236	}
237	return 0;
238}
239
240static Key *
241keygrab_ssh2(con *c)
242{
243	int j;
244
245	packet_set_connection(c->c_fd, c->c_fd);
246	enable_compat20();
247	myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = c->c_keytype == KT_DSA?
248	    "ssh-dss": "ssh-rsa";
249	c->c_kex = kex_setup(myproposal);
250	c->c_kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client;
251	c->c_kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client;
252	c->c_kex->kex[KEX_DH_GEX_SHA1] = kexgex_client;
253	c->c_kex->kex[KEX_DH_GEX_SHA256] = kexgex_client;
254	c->c_kex->verify_host_key = hostjump;
255
256	if (!(j = setjmp(kexjmp))) {
257		nonfatal_fatal = 1;
258		dispatch_run(DISPATCH_BLOCK, &c->c_kex->done, c->c_kex);
259		fprintf(stderr, "Impossible! dispatch_run() returned!\n");
260		exit(1);
261	}
262	nonfatal_fatal = 0;
263	xfree(c->c_kex);
264	c->c_kex = NULL;
265	packet_close();
266
267	return j < 0? NULL : kexjmp_key;
268}
269
270static void
271keyprint(con *c, Key *key)
272{
273	char *host = c->c_output_name ? c->c_output_name : c->c_name;
274
275	if (!key)
276		return;
277	if (hash_hosts && (host = host_hash(host, NULL, 0)) == NULL)
278		fatal("host_hash failed");
279
280	fprintf(stdout, "%s ", host);
281	key_write(key, stdout);
282	fputs("\n", stdout);
283}
284
285static int
286tcpconnect(char *host)
287{
288	struct addrinfo hints, *ai, *aitop;
289	char strport[NI_MAXSERV];
290	int gaierr, s = -1;
291
292	snprintf(strport, sizeof strport, "%d", ssh_port);
293	memset(&hints, 0, sizeof(hints));
294	hints.ai_family = IPv4or6;
295	hints.ai_socktype = SOCK_STREAM;
296	if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0)
297		fatal("getaddrinfo %s: %s", host, ssh_gai_strerror(gaierr));
298	for (ai = aitop; ai; ai = ai->ai_next) {
299		s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
300		if (s < 0) {
301			error("socket: %s", strerror(errno));
302			continue;
303		}
304		if (set_nonblock(s) == -1)
305			fatal("%s: set_nonblock(%d)", __func__, s);
306		if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0 &&
307		    errno != EINPROGRESS)
308			error("connect (`%s'): %s", host, strerror(errno));
309		else
310			break;
311		close(s);
312		s = -1;
313	}
314	freeaddrinfo(aitop);
315	return s;
316}
317
318static int
319conalloc(char *iname, char *oname, int keytype)
320{
321	char *namebase, *name, *namelist;
322	int s;
323
324	namebase = namelist = xstrdup(iname);
325
326	do {
327		name = xstrsep(&namelist, ",");
328		if (!name) {
329			xfree(namebase);
330			return (-1);
331		}
332	} while ((s = tcpconnect(name)) < 0);
333
334	if (s >= maxfd)
335		fatal("conalloc: fdno %d too high", s);
336	if (fdcon[s].c_status)
337		fatal("conalloc: attempt to reuse fdno %d", s);
338
339	fdcon[s].c_fd = s;
340	fdcon[s].c_status = CS_CON;
341	fdcon[s].c_namebase = namebase;
342	fdcon[s].c_name = name;
343	fdcon[s].c_namelist = namelist;
344	fdcon[s].c_output_name = xstrdup(oname);
345	fdcon[s].c_data = (char *) &fdcon[s].c_plen;
346	fdcon[s].c_len = 4;
347	fdcon[s].c_off = 0;
348	fdcon[s].c_keytype = keytype;
349	gettimeofday(&fdcon[s].c_tv, NULL);
350	fdcon[s].c_tv.tv_sec += timeout;
351	TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link);
352	FD_SET(s, read_wait);
353	ncon++;
354	return (s);
355}
356
357static void
358confree(int s)
359{
360	if (s >= maxfd || fdcon[s].c_status == CS_UNUSED)
361		fatal("confree: attempt to free bad fdno %d", s);
362	close(s);
363	xfree(fdcon[s].c_namebase);
364	xfree(fdcon[s].c_output_name);
365	if (fdcon[s].c_status == CS_KEYS)
366		xfree(fdcon[s].c_data);
367	fdcon[s].c_status = CS_UNUSED;
368	fdcon[s].c_keytype = 0;
369	TAILQ_REMOVE(&tq, &fdcon[s], c_link);
370	FD_CLR(s, read_wait);
371	ncon--;
372}
373
374static void
375contouch(int s)
376{
377	TAILQ_REMOVE(&tq, &fdcon[s], c_link);
378	gettimeofday(&fdcon[s].c_tv, NULL);
379	fdcon[s].c_tv.tv_sec += timeout;
380	TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link);
381}
382
383static int
384conrecycle(int s)
385{
386	con *c = &fdcon[s];
387	int ret;
388
389	ret = conalloc(c->c_namelist, c->c_output_name, c->c_keytype);
390	confree(s);
391	return (ret);
392}
393
394static void
395congreet(int s)
396{
397	int n = 0, remote_major = 0, remote_minor = 0;
398	char buf[256], *cp;
399	char remote_version[sizeof buf];
400	size_t bufsiz;
401	con *c = &fdcon[s];
402
403	for (;;) {
404		memset(buf, '\0', sizeof(buf));
405		bufsiz = sizeof(buf);
406		cp = buf;
407		while (bufsiz-- &&
408		    (n = atomicio(read, s, cp, 1)) == 1 && *cp != '\n') {
409			if (*cp == '\r')
410				*cp = '\n';
411			cp++;
412		}
413		if (n != 1 || strncmp(buf, "SSH-", 4) == 0)
414			break;
415	}
416	if (n == 0) {
417		switch (errno) {
418		case EPIPE:
419			error("%s: Connection closed by remote host", c->c_name);
420			break;
421		case ECONNREFUSED:
422			break;
423		default:
424			error("read (%s): %s", c->c_name, strerror(errno));
425			break;
426		}
427		conrecycle(s);
428		return;
429	}
430	if (*cp != '\n' && *cp != '\r') {
431		error("%s: bad greeting", c->c_name);
432		confree(s);
433		return;
434	}
435	*cp = '\0';
436	if (sscanf(buf, "SSH-%d.%d-%[^\n]\n",
437	    &remote_major, &remote_minor, remote_version) == 3)
438		compat_datafellows(remote_version);
439	else
440		datafellows = 0;
441	if (c->c_keytype != KT_RSA1) {
442		if (!ssh2_capable(remote_major, remote_minor)) {
443			debug("%s doesn't support ssh2", c->c_name);
444			confree(s);
445			return;
446		}
447	} else if (remote_major != 1) {
448		debug("%s doesn't support ssh1", c->c_name);
449		confree(s);
450		return;
451	}
452	fprintf(stderr, "# %s %s\n", c->c_name, chop(buf));
453	n = snprintf(buf, sizeof buf, "SSH-%d.%d-OpenSSH-keyscan\r\n",
454	    c->c_keytype == KT_RSA1? PROTOCOL_MAJOR_1 : PROTOCOL_MAJOR_2,
455	    c->c_keytype == KT_RSA1? PROTOCOL_MINOR_1 : PROTOCOL_MINOR_2);
456	if (n < 0 || (size_t)n >= sizeof(buf)) {
457		error("snprintf: buffer too small");
458		confree(s);
459		return;
460	}
461	if (atomicio(vwrite, s, buf, n) != (size_t)n) {
462		error("write (%s): %s", c->c_name, strerror(errno));
463		confree(s);
464		return;
465	}
466	if (c->c_keytype != KT_RSA1) {
467		keyprint(c, keygrab_ssh2(c));
468		confree(s);
469		return;
470	}
471	c->c_status = CS_SIZE;
472	contouch(s);
473}
474
475static void
476conread(int s)
477{
478	con *c = &fdcon[s];
479	size_t n;
480
481	if (c->c_status == CS_CON) {
482		congreet(s);
483		return;
484	}
485	n = atomicio(read, s, c->c_data + c->c_off, c->c_len - c->c_off);
486	if (n == 0) {
487		error("read (%s): %s", c->c_name, strerror(errno));
488		confree(s);
489		return;
490	}
491	c->c_off += n;
492
493	if (c->c_off == c->c_len)
494		switch (c->c_status) {
495		case CS_SIZE:
496			c->c_plen = htonl(c->c_plen);
497			c->c_len = c->c_plen + 8 - (c->c_plen & 7);
498			c->c_off = 0;
499			c->c_data = xmalloc(c->c_len);
500			c->c_status = CS_KEYS;
501			break;
502		case CS_KEYS:
503			keyprint(c, keygrab_ssh1(c));
504			confree(s);
505			return;
506		default:
507			fatal("conread: invalid status %d", c->c_status);
508			break;
509		}
510
511	contouch(s);
512}
513
514static void
515conloop(void)
516{
517	struct timeval seltime, now;
518	fd_set *r, *e;
519	con *c;
520	int i;
521
522	gettimeofday(&now, NULL);
523	c = TAILQ_FIRST(&tq);
524
525	if (c && (c->c_tv.tv_sec > now.tv_sec ||
526	    (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec > now.tv_usec))) {
527		seltime = c->c_tv;
528		seltime.tv_sec -= now.tv_sec;
529		seltime.tv_usec -= now.tv_usec;
530		if (seltime.tv_usec < 0) {
531			seltime.tv_usec += 1000000;
532			seltime.tv_sec--;
533		}
534	} else
535		seltime.tv_sec = seltime.tv_usec = 0;
536
537	r = xcalloc(read_wait_nfdset, sizeof(fd_mask));
538	e = xcalloc(read_wait_nfdset, sizeof(fd_mask));
539	memcpy(r, read_wait, read_wait_nfdset * sizeof(fd_mask));
540	memcpy(e, read_wait, read_wait_nfdset * sizeof(fd_mask));
541
542	while (select(maxfd, r, NULL, e, &seltime) == -1 &&
543	    (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK))
544		;
545
546	for (i = 0; i < maxfd; i++) {
547		if (FD_ISSET(i, e)) {
548			error("%s: exception!", fdcon[i].c_name);
549			confree(i);
550		} else if (FD_ISSET(i, r))
551			conread(i);
552	}
553	xfree(r);
554	xfree(e);
555
556	c = TAILQ_FIRST(&tq);
557	while (c && (c->c_tv.tv_sec < now.tv_sec ||
558	    (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec < now.tv_usec))) {
559		int s = c->c_fd;
560
561		c = TAILQ_NEXT(c, c_link);
562		conrecycle(s);
563	}
564}
565
566static void
567do_host(char *host)
568{
569	char *name = strnnsep(&host, " \t\n");
570	int j;
571
572	if (name == NULL)
573		return;
574	for (j = KT_RSA1; j <= KT_RSA; j *= 2) {
575		if (get_keytypes & j) {
576			while (ncon >= MAXCON)
577				conloop();
578			conalloc(name, *host ? host : name, j);
579		}
580	}
581}
582
583void
584fatal(const char *fmt,...)
585{
586	va_list args;
587
588	va_start(args, fmt);
589	do_log(SYSLOG_LEVEL_FATAL, fmt, args);
590	va_end(args);
591	if (nonfatal_fatal)
592		longjmp(kexjmp, -1);
593	else
594		exit(255);
595}
596
597static void
598usage(void)
599{
600	fprintf(stderr,
601	    "usage: %s [-46Hv] [-f file] [-p port] [-T timeout] [-t type]\n"
602	    "\t\t   [host | addrlist namelist] ...\n",
603	    __progname);
604	exit(1);
605}
606
607int
608main(int argc, char **argv)
609{
610	int debug_flag = 0, log_level = SYSLOG_LEVEL_INFO;
611	int opt, fopt_count = 0, j;
612	char *tname, *cp, line[NI_MAXHOST];
613	FILE *fp;
614	u_long linenum;
615
616	extern int optind;
617	extern char *optarg;
618
619	__progname = ssh_get_progname(argv[0]);
620	init_rng();
621	seed_rng();
622	TAILQ_INIT(&tq);
623
624	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
625	sanitise_stdfd();
626
627	if (argc <= 1)
628		usage();
629
630	while ((opt = getopt(argc, argv, "Hv46p:T:t:f:")) != -1) {
631		switch (opt) {
632		case 'H':
633			hash_hosts = 1;
634			break;
635		case 'p':
636			ssh_port = a2port(optarg);
637			if (ssh_port <= 0) {
638				fprintf(stderr, "Bad port '%s'\n", optarg);
639				exit(1);
640			}
641			break;
642		case 'T':
643			timeout = convtime(optarg);
644			if (timeout == -1 || timeout == 0) {
645				fprintf(stderr, "Bad timeout '%s'\n", optarg);
646				usage();
647			}
648			break;
649		case 'v':
650			if (!debug_flag) {
651				debug_flag = 1;
652				log_level = SYSLOG_LEVEL_DEBUG1;
653			}
654			else if (log_level < SYSLOG_LEVEL_DEBUG3)
655				log_level++;
656			else
657				fatal("Too high debugging level.");
658			break;
659		case 'f':
660			if (strcmp(optarg, "-") == 0)
661				optarg = NULL;
662			argv[fopt_count++] = optarg;
663			break;
664		case 't':
665			get_keytypes = 0;
666			tname = strtok(optarg, ",");
667			while (tname) {
668				int type = key_type_from_name(tname);
669				switch (type) {
670				case KEY_RSA1:
671					get_keytypes |= KT_RSA1;
672					break;
673				case KEY_DSA:
674					get_keytypes |= KT_DSA;
675					break;
676				case KEY_RSA:
677					get_keytypes |= KT_RSA;
678					break;
679				case KEY_UNSPEC:
680					fatal("unknown key type %s", tname);
681				}
682				tname = strtok(NULL, ",");
683			}
684			break;
685		case '4':
686			IPv4or6 = AF_INET;
687			break;
688		case '6':
689			IPv4or6 = AF_INET6;
690			break;
691		case '?':
692		default:
693			usage();
694		}
695	}
696	if (optind == argc && !fopt_count)
697		usage();
698
699	log_init("ssh-keyscan", log_level, SYSLOG_FACILITY_USER, 1);
700
701	maxfd = fdlim_get(1);
702	if (maxfd < 0)
703		fatal("%s: fdlim_get: bad value", __progname);
704	if (maxfd > MAXMAXFD)
705		maxfd = MAXMAXFD;
706	if (MAXCON <= 0)
707		fatal("%s: not enough file descriptors", __progname);
708	if (maxfd > fdlim_get(0))
709		fdlim_set(maxfd);
710	fdcon = xcalloc(maxfd, sizeof(con));
711
712	read_wait_nfdset = howmany(maxfd, NFDBITS);
713	read_wait = xcalloc(read_wait_nfdset, sizeof(fd_mask));
714
715	for (j = 0; j < fopt_count; j++) {
716		if (argv[j] == NULL)
717			fp = stdin;
718		else if ((fp = fopen(argv[j], "r")) == NULL)
719			fatal("%s: %s: %s", __progname, argv[j],
720			    strerror(errno));
721		linenum = 0;
722
723		while (read_keyfile_line(fp,
724		    argv[j] == NULL ? "(stdin)" : argv[j], line, sizeof(line),
725		    &linenum) != -1) {
726			/* Chomp off trailing whitespace and comments */
727			if ((cp = strchr(line, '#')) == NULL)
728				cp = line + strlen(line) - 1;
729			while (cp >= line) {
730				if (*cp == ' ' || *cp == '\t' ||
731				    *cp == '\n' || *cp == '#')
732					*cp-- = '\0';
733				else
734					break;
735			}
736
737			/* Skip empty lines */
738			if (*line == '\0')
739				continue;
740
741			do_host(line);
742		}
743
744		if (ferror(fp))
745			fatal("%s: %s: %s", __progname, argv[j],
746			    strerror(errno));
747
748		fclose(fp);
749	}
750
751	while (optind < argc)
752		do_host(argv[optind++]);
753
754	while (ncon > 0)
755		conloop();
756
757	return (0);
758}
759