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