ssh-keyscan for ssh2 (was Re: openssh-2.9p1)

Wayne Davison wayne at blorf.net
Fri May 18 13:47:39 EST 2001


On Mon, 14 May 2001, Peter Breitenlohner wrote:
> 2. Is there a program like ssh-keyscan for the Version2 (dsa and rsa) keys??

Here's my first cut at modifying ssh-keyscan to output either/both ssh1
and ssh2 keys.  It defaults to working as it did before (outputting just
the ssh1 rsa keys), but you can ask for either or both by using the -1
and -2 options.  You can even switch what you need between hostnames:

  ssh-keyscan -1 foo -2 bar -1 -2 baz stillboth -1 rsa -12 bothagain

My patch currently uses lots of connection code from the ssh library, so
a fatal error in protocol 2 can cut off the whole process in the middle.
Hopefully this won't be too hard to fix, but I'm just glad this is
working and doesn't hang up on out-of-touch systems (or bomb out early
on connection errors since the first part of the connection is still
handled by the same keyscan code).

The ssh2 probing only appears to work when connecting to sshd v2.9.
When I attempted to use it ask for a ssh2 key from a 2.5.2p2 host, it
reports the error "Received disconnect from ...".  If someone more
familiar with how ssh works would like to look into this, feel free.

I also added the flags -4 and -6 to allow people to set IPv4 and IPv6
modes just like with ssh.

..wayne..

---8<------8<------8<------8<---cut here--->8------>8------>8------>8---
Index: ssh-keyscan.c
@@ -19,10 +19,15 @@

 #include <openssl/bn.h>

+#include <setjmp.h>
 #include "xmalloc.h"
 #include "ssh.h"
 #include "ssh1.h"
 #include "key.h"
+#include "kex.h"
+#include "myproposal.h"
+#include "packet.h"
+#include "dispatch.h"
 #include "buffer.h"
 #include "bufaux.h"
 #include "log.h"
@@ -30,8 +35,16 @@

 static int argno = 1;		/* Number of argument currently being parsed */

-int family = AF_UNSPEC;		/* IPv4, IPv6 or both */
+/* Flag indicating whether IPv4 or IPv6.  This can be set on the command line.
+   Default value is AF_UNSPEC means both IPv4 and IPv6. */
+#ifdef IPV4_DEFAULT
+int IPv4or6 = AF_INET;
+#else
+int IPv4or6 = AF_UNSPEC;
+#endif

+int get_keytypes = 1;		/* Get only RSA keys by default */
+
 #define MAXMAXFD 256

 /* The number of seconds after which to give up on a TCP connection */
@@ -48,6 +61,7 @@
 fd_set *read_wait;
 size_t read_wait_size;
 int ncon;
+jmp_buf kexjmp;

 /*
  * Keep a connection structure for each file descriptor.  The state
@@ -63,11 +77,13 @@
 	int c_plen;		/* Packet length field for ssh packet */
 	int c_len;		/* Total bytes which must be read. */
 	int c_off;		/* Length of data read so far. */
+	int c_keytypes;		/* bits: 1 = get RSA, 2 = get DSA */
 	char *c_namebase;	/* Address to free for c_name and c_namelist */
 	char *c_name;		/* Hostname of connection for errors */
 	char *c_namelist;	/* Pointer to other possible addresses */
 	char *c_output_name;	/* Hostname of connection for output */
 	char *c_data;		/* Data read from this fd */
+	Kex *c_kex;		/* The key-exchange struct for ssh2 */
 	struct timeval c_tv;	/* Time at which connection gets aborted */
 	TAILQ_ENTRY(Connection) c_link;	/* List of connections in timeout order. */
 } con;
@@ -261,8 +277,8 @@
 	return (tok);
 }

-void
-keyprint(char *host, char *output_name, char *kd, int len)
+Key *
+keygrab_rsa(con *c)
 {
 	static Key *rsa;
 	static Buffer msg;
@@ -271,12 +287,12 @@
 		buffer_init(&msg);
 		rsa = key_new(KEY_RSA1);
 	}
-	buffer_append(&msg, kd, len);
-	buffer_consume(&msg, 8 - (len & 7));	/* padding */
+	buffer_append(&msg, c->c_data, c->c_plen);
+	buffer_consume(&msg, 8 - (c->c_plen & 7));	/* padding */
 	if (buffer_get_char(&msg) != (int) SSH_SMSG_PUBLIC_KEY) {
-		error("%s: invalid packet type", host);
+		error("%s: invalid packet type", c->c_name);
 		buffer_clear(&msg);
-		return;
+		return NULL;
 	}
 	buffer_consume(&msg, 8);		/* cookie */

@@ -289,10 +305,46 @@
 	(void) buffer_get_int(&msg);
 	buffer_get_bignum(&msg, rsa->rsa->e);
 	buffer_get_bignum(&msg, rsa->rsa->n);
+
 	buffer_clear(&msg);
+
+	return (rsa);
+}
+
+int
+hostjump(Key *hostkey)
+{
+	longjmp(kexjmp, (int)hostkey);
+}
+
+Key *
+keygrab_dsa(con *c)
+{
+	Key *key;
+
+	packet_set_connection(c->c_fd, c->c_fd);
+	packet_set_ssh2_format();
+	c->c_kex = kex_setup(myproposal);
+	c->c_kex->check_host_key = hostjump;
+
+	if (!(key = (Key*)setjmp(kexjmp))) {
+		dispatch_run(DISPATCH_BLOCK, &c->c_kex->done, c->c_kex);
+		fprintf(stderr, "Impossible! dispatch_run() returned!\n");
+		exit(1);
+	}
+	packet_close();

-	fprintf(stdout, "%s ", output_name ? output_name : host);
-	key_write(rsa, stdout);
+	return (key);
+}
+
+void
+keyprint(con *c, Key *key)
+{
+	if (!key)
+		return;
+
+	fprintf(stdout, "%s ", c->c_output_name ? c->c_output_name : c->c_name);
+	key_write(key, stdout);
 	fputs("\n", stdout);
 }

@@ -305,7 +357,7 @@

 	snprintf(strport, sizeof strport, "%d", SSH_DEFAULT_PORT);
 	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = family;
+	hints.ai_family = IPv4or6;
 	hints.ai_socktype = SOCK_STREAM;
 	if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0)
 		fatal("getaddrinfo %s: %s", host, gai_strerror(gaierr));
@@ -330,7 +382,7 @@
 }

 int
-conalloc(char *iname, char *oname)
+conalloc(char *iname, char *oname, int keytypes)
 {
 	int s;
 	char *namebase, *name, *namelist;
@@ -359,6 +411,7 @@
 	fdcon[s].c_data = (char *) &fdcon[s].c_plen;
 	fdcon[s].c_len = 4;
 	fdcon[s].c_off = 0;
+	fdcon[s].c_keytypes = keytypes;
 	gettimeofday(&fdcon[s].c_tv, NULL);
 	fdcon[s].c_tv.tv_sec += timeout;
 	TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link);
@@ -393,16 +446,23 @@
 }

 int
-conrecycle(int s)
+conrecycle(int s, int include_current_host)
 {
 	int ret;
 	con *c = &fdcon[s];
 	char *iname, *oname;
+	int keytypes = c->c_keytypes;

-	iname = xstrdup(c->c_namelist);
+	if (include_current_host) {
+		iname = xmalloc(strlen(c->c_name) + strlen(c->c_namelist) + 2);
+		strcpy(iname, c->c_name);
+		if (*c->c_namelist)
+			sprintf(iname + strlen(iname), ",%s", c->c_namelist);
+	} else
+		iname = xstrdup(c->c_namelist);
 	oname = xstrdup(c->c_output_name);
 	confree(s);
-	ret = conalloc(iname, oname);
+	ret = conalloc(iname, oname, keytypes);
 	xfree(iname);
 	xfree(oname);
 	return (ret);
@@ -423,7 +483,7 @@
 	if (n < 0) {
 		if (errno != ECONNREFUSED)
 			error("read (%s): %s", c->c_name, strerror(errno));
-		conrecycle(s);
+		conrecycle(s, 0);
 		return;
 	}
 	if (*cp != '\n' && *cp != '\r') {
@@ -433,12 +493,20 @@
 	}
 	*cp = '\0';
 	fprintf(stderr, "# %s %s\n", c->c_name, buf);
-	n = snprintf(buf, sizeof buf, "SSH-1.5-OpenSSH-keyscan\r\n");
+	n = snprintf(buf, sizeof buf, "SSH-%d.%d-OpenSSH-keyscan\r\n",
+	    c->c_keytypes & 1 ? PROTOCOL_MAJOR_1 : PROTOCOL_MAJOR_2,
+	    c->c_keytypes & 1 ? PROTOCOL_MINOR_1 : PROTOCOL_MINOR_2);
 	if (atomicio(write, s, buf, n) != n) {
 		error("write (%s): %s", c->c_name, strerror(errno));
 		confree(s);
 		return;
 	}
+	if (!(c->c_keytypes & 1)) {
+		keyprint(c, keygrab_dsa(c));
+		c->c_keytypes &= ~2;
+		confree(s);
+		return;
+	}
 	c->c_status = CS_SIZE;
 	contouch(s);
 }
@@ -471,8 +539,13 @@
 			c->c_status = CS_KEYS;
 			break;
 		case CS_KEYS:
-			keyprint(c->c_name, c->c_output_name, c->c_data, c->c_plen);
-			confree(s);
+			keyprint(c, keygrab_rsa(c));
+			c->c_keytypes &= ~1;
+
+			if (c->c_keytypes)
+				conrecycle(s, 1);
+			else
+				confree(s);
 			return;
 			break;
 		default:
@@ -531,7 +604,7 @@
 		int s = c->c_fd;

 		c = c->c_link.tqe_next;
-		conrecycle(s);
+		conrecycle(s, 0);
 	}
 }

@@ -539,49 +612,71 @@
 nexthost(int argc, char **argv)
 {
 	static Linebuf *lb;
+	char *fname;
+	int first_proto_option = 1;

 	for (;;) {
-		if (!lb) {
-			if (argno >= argc)
-				return (NULL);
-			if (argv[argno][0] != '-')
-				return (argv[argno++]);
-			if (!strcmp(argv[argno], "--")) {
+		if (lb) {
+			char *line;
+
+			line = Linebuf_getline(lb);
+			if (line)
+				return (line);
+			Linebuf_free(lb);
+			lb = NULL;
+		}
+		if (argno >= argc)
+			return (NULL);
+		if (argv[argno][0] != '-')
+			return (argv[argno++]);
+		while (*++(argv[argno])) {
+			switch (argv[argno][0]) {
+			case '-':
 				if (++argno >= argc)
 					return (NULL);
 				return (argv[argno++]);
-			} else if (!strncmp(argv[argno], "-f", 2)) {
-				char *fname;
-
-				if (argv[argno][2])
-					fname = &argv[argno++][2];
+			case 'f':
+				if (argv[argno][1])
+					fname = &argv[argno][1];
 				else if (++argno >= argc) {
 					error("missing filename for `-f'");
 					return (NULL);
 				} else
-					fname = argv[argno++];
+					fname = argv[argno];
 				if (!strcmp(fname, "-"))
 					fname = NULL;
 				lb = Linebuf_alloc(fname, error);
-			} else
+				goto double_break;
+			case '1':
+			case '2':
+				if (first_proto_option) {
+					get_keytypes = 0;
+					first_proto_option = 0;
+				}
+				get_keytypes |= argv[argno][0] - '0';
+				break;
+			case '4':
+				IPv4or6 = AF_INET;
+				break;
+			case '6':
+				IPv4or6 = AF_INET6;
+				break;
+			default:
 				error("ignoring invalid/misplaced option `%s'",
-				    argv[argno++]);
-		} else {
-			char *line;
-
-			line = Linebuf_getline(lb);
-			if (line)
-				return (line);
-			Linebuf_free(lb);
-			lb = NULL;
+				    argv[argno]);
+				goto double_break;
+			}
 		}
+double_break:
+		argno++;
 	}
 }

 void
 usage(void)
 {
-	fatal("usage: %s [-t timeout] { [--] host | -f file } ...", __progname);
+	fatal("usage: %s [-t timeout] { [-1|-2|-4|-6] [--] host | -f file } ...",
+	    __progname);
 	return;
 }

@@ -623,6 +718,8 @@
 	fdcon = xmalloc(maxfd * sizeof(con));
 	memset(fdcon, 0, maxfd * sizeof(con));

+	seed_rng();
+
 	read_wait_size = howmany(maxfd, NFDBITS) * sizeof(fd_mask);
 	read_wait = xmalloc(read_wait_size);
 	memset(read_wait, 0, read_wait_size);
@@ -635,7 +732,7 @@
 			if (host == NULL)
 				break;
 			name = strnnsep(&host, " \t\n");
-			conalloc(name, *host ? host : name);
+			conalloc(name, *host ? host : name, get_keytypes);
 		}
 		conloop();
 	} while (host);
---8<------8<------8<------8<---cut here--->8------>8------>8------>8---




More information about the openssh-unix-dev mailing list