ssh-keyscan.c revision 106130
176259Sgreen/* 276259Sgreen * Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>. 376259Sgreen * 476259Sgreen * Modification and redistribution in source and binary forms is 576259Sgreen * permitted provided that due credit is given to the author and the 692555Sdes * OpenBSD project by leaving this copyright notice intact. 776259Sgreen */ 876259Sgreen 976259Sgreen#include "includes.h" 10106130SdesRCSID("$OpenBSD: ssh-keyscan.c,v 1.40 2002/07/06 17:47:58 stevesk Exp $"); 11106130SdesRCSID("$FreeBSD: head/crypto/openssh/ssh-keyscan.c 106130 2002-10-29 10:16:02Z des $"); 1276259Sgreen 13106130Sdes#include "openbsd-compat/sys-queue.h" 1476259Sgreen 1576259Sgreen#include <openssl/bn.h> 1676259Sgreen 1792555Sdes#include <setjmp.h> 1876259Sgreen#include "xmalloc.h" 1976259Sgreen#include "ssh.h" 2076259Sgreen#include "ssh1.h" 2176259Sgreen#include "key.h" 2292555Sdes#include "kex.h" 2392555Sdes#include "compat.h" 2492555Sdes#include "myproposal.h" 2592555Sdes#include "packet.h" 2692555Sdes#include "dispatch.h" 2776259Sgreen#include "buffer.h" 2876259Sgreen#include "bufaux.h" 2976259Sgreen#include "log.h" 3076259Sgreen#include "atomicio.h" 3192555Sdes#include "misc.h" 3276259Sgreen 3398941Sdes/* Flag indicating whether IPv4 or IPv6. This can be set on the command line. 3498941Sdes Default value is AF_UNSPEC means both IPv4 and IPv6. */ 3598941Sdes#ifdef IPV4_DEFAULT 3698941Sdesint IPv4or6 = AF_INET; 3798941Sdes#else 3898941Sdesint IPv4or6 = AF_UNSPEC; 3998941Sdes#endif 4076259Sgreen 4192555Sdesint ssh_port = SSH_DEFAULT_PORT; 4276259Sgreen 4392555Sdes#define KT_RSA1 1 4492555Sdes#define KT_DSA 2 4592555Sdes#define KT_RSA 4 4692555Sdes 4792555Sdesint get_keytypes = KT_RSA1; /* Get only RSA1 keys by default */ 4892555Sdes 4976259Sgreen#define MAXMAXFD 256 5076259Sgreen 5176259Sgreen/* The number of seconds after which to give up on a TCP connection */ 5276259Sgreenint timeout = 5; 5376259Sgreen 5476259Sgreenint maxfd; 5576259Sgreen#define MAXCON (maxfd - 10) 5676259Sgreen 5798941Sdes#ifdef HAVE___PROGNAME 5876259Sgreenextern char *__progname; 5998941Sdes#else 6098941Sdeschar *__progname; 6198941Sdes#endif 6276259Sgreenfd_set *read_wait; 6376259Sgreensize_t read_wait_size; 6476259Sgreenint ncon; 6592555Sdesint nonfatal_fatal = 0; 6692555Sdesjmp_buf kexjmp; 6792555SdesKey *kexjmp_key; 6876259Sgreen 6976259Sgreen/* 7076259Sgreen * Keep a connection structure for each file descriptor. The state 7176259Sgreen * associated with file descriptor n is held in fdcon[n]. 7276259Sgreen */ 7376259Sgreentypedef struct Connection { 7476259Sgreen u_char c_status; /* State of connection on this file desc. */ 7576259Sgreen#define CS_UNUSED 0 /* File descriptor unused */ 7676259Sgreen#define CS_CON 1 /* Waiting to connect/read greeting */ 7776259Sgreen#define CS_SIZE 2 /* Waiting to read initial packet size */ 7876259Sgreen#define CS_KEYS 3 /* Waiting to read public key packet */ 7976259Sgreen int c_fd; /* Quick lookup: c->c_fd == c - fdcon */ 8076259Sgreen int c_plen; /* Packet length field for ssh packet */ 8176259Sgreen int c_len; /* Total bytes which must be read. */ 8276259Sgreen int c_off; /* Length of data read so far. */ 8392555Sdes int c_keytype; /* Only one of KT_RSA1, KT_DSA, or KT_RSA */ 8476259Sgreen char *c_namebase; /* Address to free for c_name and c_namelist */ 8576259Sgreen char *c_name; /* Hostname of connection for errors */ 8676259Sgreen char *c_namelist; /* Pointer to other possible addresses */ 8776259Sgreen char *c_output_name; /* Hostname of connection for output */ 8876259Sgreen char *c_data; /* Data read from this fd */ 8992555Sdes Kex *c_kex; /* The key-exchange struct for ssh2 */ 9076259Sgreen struct timeval c_tv; /* Time at which connection gets aborted */ 9176259Sgreen TAILQ_ENTRY(Connection) c_link; /* List of connections in timeout order. */ 9276259Sgreen} con; 9376259Sgreen 9476259SgreenTAILQ_HEAD(conlist, Connection) tq; /* Timeout Queue */ 9576259Sgreencon *fdcon; 9676259Sgreen 9776259Sgreen/* 9876259Sgreen * This is just a wrapper around fgets() to make it usable. 9976259Sgreen */ 10076259Sgreen 10176259Sgreen/* Stress-test. Increase this later. */ 10276259Sgreen#define LINEBUF_SIZE 16 10376259Sgreen 10476259Sgreentypedef struct { 10576259Sgreen char *buf; 10676259Sgreen u_int size; 10776259Sgreen int lineno; 10876259Sgreen const char *filename; 10976259Sgreen FILE *stream; 11076259Sgreen void (*errfun) (const char *,...); 11176259Sgreen} Linebuf; 11276259Sgreen 11392555Sdesstatic Linebuf * 11476259SgreenLinebuf_alloc(const char *filename, void (*errfun) (const char *,...)) 11576259Sgreen{ 11676259Sgreen Linebuf *lb; 11776259Sgreen 11876259Sgreen if (!(lb = malloc(sizeof(*lb)))) { 11976259Sgreen if (errfun) 120106130Sdes (*errfun) ("linebuf (%s): malloc failed\n", 121106130Sdes filename ? filename : "(stdin)"); 12276259Sgreen return (NULL); 12376259Sgreen } 12476259Sgreen if (filename) { 12576259Sgreen lb->filename = filename; 12676259Sgreen if (!(lb->stream = fopen(filename, "r"))) { 12776259Sgreen xfree(lb); 12876259Sgreen if (errfun) 12976259Sgreen (*errfun) ("%s: %s\n", filename, strerror(errno)); 13076259Sgreen return (NULL); 13176259Sgreen } 13276259Sgreen } else { 13376259Sgreen lb->filename = "(stdin)"; 13476259Sgreen lb->stream = stdin; 13576259Sgreen } 13676259Sgreen 13776259Sgreen if (!(lb->buf = malloc(lb->size = LINEBUF_SIZE))) { 13876259Sgreen if (errfun) 13976259Sgreen (*errfun) ("linebuf (%s): malloc failed\n", lb->filename); 14076259Sgreen xfree(lb); 14176259Sgreen return (NULL); 14276259Sgreen } 14376259Sgreen lb->errfun = errfun; 14476259Sgreen lb->lineno = 0; 14576259Sgreen return (lb); 14676259Sgreen} 14776259Sgreen 14892555Sdesstatic void 14976259SgreenLinebuf_free(Linebuf * lb) 15076259Sgreen{ 15176259Sgreen fclose(lb->stream); 15276259Sgreen xfree(lb->buf); 15376259Sgreen xfree(lb); 15476259Sgreen} 15576259Sgreen 15692555Sdes#if 0 15792555Sdesstatic void 15876259SgreenLinebuf_restart(Linebuf * lb) 15976259Sgreen{ 16076259Sgreen clearerr(lb->stream); 16176259Sgreen rewind(lb->stream); 16276259Sgreen lb->lineno = 0; 16376259Sgreen} 16476259Sgreen 16592555Sdesstatic int 16676259SgreenLinebuf_lineno(Linebuf * lb) 16776259Sgreen{ 16876259Sgreen return (lb->lineno); 16976259Sgreen} 17092555Sdes#endif 17176259Sgreen 17292555Sdesstatic char * 17376259SgreenLinebuf_getline(Linebuf * lb) 17476259Sgreen{ 17576259Sgreen int n = 0; 176106130Sdes void *p; 17776259Sgreen 17876259Sgreen lb->lineno++; 17976259Sgreen for (;;) { 18076259Sgreen /* Read a line */ 18176259Sgreen if (!fgets(&lb->buf[n], lb->size - n, lb->stream)) { 18276259Sgreen if (ferror(lb->stream) && lb->errfun) 183106130Sdes (*lb->errfun)("%s: %s\n", lb->filename, 18476259Sgreen strerror(errno)); 18576259Sgreen return (NULL); 18676259Sgreen } 18776259Sgreen n = strlen(lb->buf); 18876259Sgreen 18976259Sgreen /* Return it or an error if it fits */ 19076259Sgreen if (n > 0 && lb->buf[n - 1] == '\n') { 19176259Sgreen lb->buf[n - 1] = '\0'; 19276259Sgreen return (lb->buf); 19376259Sgreen } 19476259Sgreen if (n != lb->size - 1) { 19576259Sgreen if (lb->errfun) 196106130Sdes (*lb->errfun)("%s: skipping incomplete last line\n", 19776259Sgreen lb->filename); 19876259Sgreen return (NULL); 19976259Sgreen } 20076259Sgreen /* Double the buffer if we need more space */ 201106130Sdes lb->size *= 2; 202106130Sdes if ((p = realloc(lb->buf, lb->size)) == NULL) { 203106130Sdes lb->size /= 2; 20476259Sgreen if (lb->errfun) 205106130Sdes (*lb->errfun)("linebuf (%s): realloc failed\n", 20676259Sgreen lb->filename); 20776259Sgreen return (NULL); 20876259Sgreen } 209106130Sdes lb->buf = p; 21076259Sgreen } 21176259Sgreen} 21276259Sgreen 21392555Sdesstatic int 21476259Sgreenfdlim_get(int hard) 21576259Sgreen{ 21698941Sdes#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) 21776259Sgreen struct rlimit rlfd; 21876259Sgreen 21976259Sgreen if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0) 22076259Sgreen return (-1); 22176259Sgreen if ((hard ? rlfd.rlim_max : rlfd.rlim_cur) == RLIM_INFINITY) 22276259Sgreen return 10000; 22376259Sgreen else 22476259Sgreen return hard ? rlfd.rlim_max : rlfd.rlim_cur; 22598941Sdes#elif defined (HAVE_SYSCONF) 22698941Sdes return sysconf (_SC_OPEN_MAX); 22798941Sdes#else 22898941Sdes return 10000; 22998941Sdes#endif 23076259Sgreen} 23176259Sgreen 23292555Sdesstatic int 23376259Sgreenfdlim_set(int lim) 23476259Sgreen{ 23598941Sdes#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) 23676259Sgreen struct rlimit rlfd; 23798941Sdes#endif 238106130Sdes 23976259Sgreen if (lim <= 0) 24076259Sgreen return (-1); 24198941Sdes#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) 24276259Sgreen if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0) 24376259Sgreen return (-1); 24476259Sgreen rlfd.rlim_cur = lim; 24576259Sgreen if (setrlimit(RLIMIT_NOFILE, &rlfd) < 0) 24676259Sgreen return (-1); 24798941Sdes#elif defined (HAVE_SETDTABLESIZE) 24898941Sdes setdtablesize(lim); 24998941Sdes#endif 25076259Sgreen return (0); 25176259Sgreen} 25276259Sgreen 25376259Sgreen/* 25476259Sgreen * This is an strsep function that returns a null field for adjacent 25576259Sgreen * separators. This is the same as the 4.4BSD strsep, but different from the 25676259Sgreen * one in the GNU libc. 25776259Sgreen */ 25892555Sdesstatic char * 25976259Sgreenxstrsep(char **str, const char *delim) 26076259Sgreen{ 26176259Sgreen char *s, *e; 26276259Sgreen 26376259Sgreen if (!**str) 26476259Sgreen return (NULL); 26576259Sgreen 26676259Sgreen s = *str; 26776259Sgreen e = s + strcspn(s, delim); 26876259Sgreen 26976259Sgreen if (*e != '\0') 27076259Sgreen *e++ = '\0'; 27176259Sgreen *str = e; 27276259Sgreen 27376259Sgreen return (s); 27476259Sgreen} 27576259Sgreen 27676259Sgreen/* 27776259Sgreen * Get the next non-null token (like GNU strsep). Strsep() will return a 27876259Sgreen * null token for two adjacent separators, so we may have to loop. 27976259Sgreen */ 28092555Sdesstatic char * 28176259Sgreenstrnnsep(char **stringp, char *delim) 28276259Sgreen{ 28376259Sgreen char *tok; 28476259Sgreen 28576259Sgreen do { 28676259Sgreen tok = xstrsep(stringp, delim); 28776259Sgreen } while (tok && *tok == '\0'); 28876259Sgreen return (tok); 28976259Sgreen} 29076259Sgreen 29192555Sdesstatic Key * 29292555Sdeskeygrab_ssh1(con *c) 29376259Sgreen{ 29476259Sgreen static Key *rsa; 29576259Sgreen static Buffer msg; 29676259Sgreen 29776259Sgreen if (rsa == NULL) { 29876259Sgreen buffer_init(&msg); 29976259Sgreen rsa = key_new(KEY_RSA1); 30076259Sgreen } 30192555Sdes buffer_append(&msg, c->c_data, c->c_plen); 30292555Sdes buffer_consume(&msg, 8 - (c->c_plen & 7)); /* padding */ 30376259Sgreen if (buffer_get_char(&msg) != (int) SSH_SMSG_PUBLIC_KEY) { 30492555Sdes error("%s: invalid packet type", c->c_name); 30576259Sgreen buffer_clear(&msg); 30692555Sdes return NULL; 30776259Sgreen } 30876259Sgreen buffer_consume(&msg, 8); /* cookie */ 30976259Sgreen 31076259Sgreen /* server key */ 31176259Sgreen (void) buffer_get_int(&msg); 31276259Sgreen buffer_get_bignum(&msg, rsa->rsa->e); 31376259Sgreen buffer_get_bignum(&msg, rsa->rsa->n); 31476259Sgreen 31576259Sgreen /* host key */ 31676259Sgreen (void) buffer_get_int(&msg); 31776259Sgreen buffer_get_bignum(&msg, rsa->rsa->e); 31876259Sgreen buffer_get_bignum(&msg, rsa->rsa->n); 31992555Sdes 32076259Sgreen buffer_clear(&msg); 32176259Sgreen 32292555Sdes return (rsa); 32392555Sdes} 32492555Sdes 32592555Sdesstatic int 32692555Sdeshostjump(Key *hostkey) 32792555Sdes{ 32892555Sdes kexjmp_key = hostkey; 32992555Sdes longjmp(kexjmp, 1); 33092555Sdes} 33192555Sdes 33292555Sdesstatic int 33392555Sdesssh2_capable(int remote_major, int remote_minor) 33492555Sdes{ 33592555Sdes switch (remote_major) { 33692555Sdes case 1: 33792555Sdes if (remote_minor == 99) 33892555Sdes return 1; 33992555Sdes break; 34092555Sdes case 2: 34192555Sdes return 1; 34292555Sdes default: 34392555Sdes break; 34492555Sdes } 34592555Sdes return 0; 34692555Sdes} 34792555Sdes 34892555Sdesstatic Key * 34992555Sdeskeygrab_ssh2(con *c) 35092555Sdes{ 35192555Sdes int j; 35292555Sdes 35392555Sdes packet_set_connection(c->c_fd, c->c_fd); 35492555Sdes enable_compat20(); 35592555Sdes myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = c->c_keytype == KT_DSA? 35692555Sdes "ssh-dss": "ssh-rsa"; 35792555Sdes c->c_kex = kex_setup(myproposal); 35892555Sdes c->c_kex->verify_host_key = hostjump; 35992555Sdes 36092555Sdes if (!(j = setjmp(kexjmp))) { 36192555Sdes nonfatal_fatal = 1; 36292555Sdes dispatch_run(DISPATCH_BLOCK, &c->c_kex->done, c->c_kex); 36392555Sdes fprintf(stderr, "Impossible! dispatch_run() returned!\n"); 36492555Sdes exit(1); 36592555Sdes } 36692555Sdes nonfatal_fatal = 0; 36792555Sdes xfree(c->c_kex); 36892555Sdes c->c_kex = NULL; 36992555Sdes packet_close(); 37092555Sdes 37192555Sdes return j < 0? NULL : kexjmp_key; 37292555Sdes} 37392555Sdes 37492555Sdesstatic void 37592555Sdeskeyprint(con *c, Key *key) 37692555Sdes{ 37792555Sdes if (!key) 37892555Sdes return; 37992555Sdes 38092555Sdes fprintf(stdout, "%s ", c->c_output_name ? c->c_output_name : c->c_name); 38192555Sdes key_write(key, stdout); 38276259Sgreen fputs("\n", stdout); 38376259Sgreen} 38476259Sgreen 38592555Sdesstatic int 38676259Sgreentcpconnect(char *host) 38776259Sgreen{ 38876259Sgreen struct addrinfo hints, *ai, *aitop; 38976259Sgreen char strport[NI_MAXSERV]; 39076259Sgreen int gaierr, s = -1; 39176259Sgreen 39292555Sdes snprintf(strport, sizeof strport, "%d", ssh_port); 39376259Sgreen memset(&hints, 0, sizeof(hints)); 39492555Sdes hints.ai_family = IPv4or6; 39576259Sgreen hints.ai_socktype = SOCK_STREAM; 39676259Sgreen if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) 39776259Sgreen fatal("getaddrinfo %s: %s", host, gai_strerror(gaierr)); 39876259Sgreen for (ai = aitop; ai; ai = ai->ai_next) { 39976259Sgreen s = socket(ai->ai_family, SOCK_STREAM, 0); 40076259Sgreen if (s < 0) { 40176259Sgreen error("socket: %s", strerror(errno)); 40276259Sgreen continue; 40376259Sgreen } 40476259Sgreen if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) 40576259Sgreen fatal("F_SETFL: %s", strerror(errno)); 40676259Sgreen if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0 && 40776259Sgreen errno != EINPROGRESS) 40876259Sgreen error("connect (`%s'): %s", host, strerror(errno)); 40976259Sgreen else 41076259Sgreen break; 41176259Sgreen close(s); 41276259Sgreen s = -1; 41376259Sgreen } 41476259Sgreen freeaddrinfo(aitop); 41576259Sgreen return s; 41676259Sgreen} 41776259Sgreen 41892555Sdesstatic int 41992555Sdesconalloc(char *iname, char *oname, int keytype) 42076259Sgreen{ 421106130Sdes char *namebase, *name, *namelist; 42276259Sgreen int s; 42376259Sgreen 42476259Sgreen namebase = namelist = xstrdup(iname); 42576259Sgreen 42676259Sgreen do { 42776259Sgreen name = xstrsep(&namelist, ","); 42876259Sgreen if (!name) { 42976259Sgreen xfree(namebase); 43076259Sgreen return (-1); 43176259Sgreen } 43276259Sgreen } while ((s = tcpconnect(name)) < 0); 43376259Sgreen 43476259Sgreen if (s >= maxfd) 43576259Sgreen fatal("conalloc: fdno %d too high", s); 43676259Sgreen if (fdcon[s].c_status) 43776259Sgreen fatal("conalloc: attempt to reuse fdno %d", s); 43876259Sgreen 43976259Sgreen fdcon[s].c_fd = s; 44076259Sgreen fdcon[s].c_status = CS_CON; 44176259Sgreen fdcon[s].c_namebase = namebase; 44276259Sgreen fdcon[s].c_name = name; 44376259Sgreen fdcon[s].c_namelist = namelist; 44476259Sgreen fdcon[s].c_output_name = xstrdup(oname); 44576259Sgreen fdcon[s].c_data = (char *) &fdcon[s].c_plen; 44676259Sgreen fdcon[s].c_len = 4; 44776259Sgreen fdcon[s].c_off = 0; 44892555Sdes fdcon[s].c_keytype = keytype; 44976259Sgreen gettimeofday(&fdcon[s].c_tv, NULL); 45076259Sgreen fdcon[s].c_tv.tv_sec += timeout; 45176259Sgreen TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); 45276259Sgreen FD_SET(s, read_wait); 45376259Sgreen ncon++; 45476259Sgreen return (s); 45576259Sgreen} 45676259Sgreen 45792555Sdesstatic void 45876259Sgreenconfree(int s) 45976259Sgreen{ 46076259Sgreen if (s >= maxfd || fdcon[s].c_status == CS_UNUSED) 46176259Sgreen fatal("confree: attempt to free bad fdno %d", s); 46276259Sgreen close(s); 46376259Sgreen xfree(fdcon[s].c_namebase); 46476259Sgreen xfree(fdcon[s].c_output_name); 46576259Sgreen if (fdcon[s].c_status == CS_KEYS) 46676259Sgreen xfree(fdcon[s].c_data); 46776259Sgreen fdcon[s].c_status = CS_UNUSED; 46892555Sdes fdcon[s].c_keytype = 0; 46976259Sgreen TAILQ_REMOVE(&tq, &fdcon[s], c_link); 47076259Sgreen FD_CLR(s, read_wait); 47176259Sgreen ncon--; 47276259Sgreen} 47376259Sgreen 47492555Sdesstatic void 47576259Sgreencontouch(int s) 47676259Sgreen{ 47776259Sgreen TAILQ_REMOVE(&tq, &fdcon[s], c_link); 47876259Sgreen gettimeofday(&fdcon[s].c_tv, NULL); 47976259Sgreen fdcon[s].c_tv.tv_sec += timeout; 48076259Sgreen TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); 48176259Sgreen} 48276259Sgreen 48392555Sdesstatic int 48476259Sgreenconrecycle(int s) 48576259Sgreen{ 486106130Sdes con *c = &fdcon[s]; 48776259Sgreen int ret; 48876259Sgreen 48992555Sdes ret = conalloc(c->c_namelist, c->c_output_name, c->c_keytype); 49076259Sgreen confree(s); 49176259Sgreen return (ret); 49276259Sgreen} 49376259Sgreen 49492555Sdesstatic void 49576259Sgreencongreet(int s) 49676259Sgreen{ 497106130Sdes int remote_major, remote_minor, n = 0; 49892555Sdes char buf[256], *cp; 49992555Sdes char remote_version[sizeof buf]; 50076259Sgreen size_t bufsiz; 50176259Sgreen con *c = &fdcon[s]; 50276259Sgreen 50376259Sgreen bufsiz = sizeof(buf); 50476259Sgreen cp = buf; 50592555Sdes while (bufsiz-- && (n = read(s, cp, 1)) == 1 && *cp != '\n') { 50692555Sdes if (*cp == '\r') 50792555Sdes *cp = '\n'; 50876259Sgreen cp++; 50992555Sdes } 51076259Sgreen if (n < 0) { 51176259Sgreen if (errno != ECONNREFUSED) 51276259Sgreen error("read (%s): %s", c->c_name, strerror(errno)); 51376259Sgreen conrecycle(s); 51476259Sgreen return; 51576259Sgreen } 51692555Sdes if (n == 0) { 51792555Sdes error("%s: Connection closed by remote host", c->c_name); 51892555Sdes conrecycle(s); 51992555Sdes return; 52092555Sdes } 52176259Sgreen if (*cp != '\n' && *cp != '\r') { 52276259Sgreen error("%s: bad greeting", c->c_name); 52376259Sgreen confree(s); 52476259Sgreen return; 52576259Sgreen } 52676259Sgreen *cp = '\0'; 52792555Sdes if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", 52892555Sdes &remote_major, &remote_minor, remote_version) == 3) 52992555Sdes compat_datafellows(remote_version); 53092555Sdes else 53192555Sdes datafellows = 0; 53292555Sdes if (c->c_keytype != KT_RSA1) { 53392555Sdes if (!ssh2_capable(remote_major, remote_minor)) { 53492555Sdes debug("%s doesn't support ssh2", c->c_name); 53592555Sdes confree(s); 53692555Sdes return; 53792555Sdes } 53892555Sdes } else if (remote_major != 1) { 53992555Sdes debug("%s doesn't support ssh1", c->c_name); 54092555Sdes confree(s); 54192555Sdes return; 54292555Sdes } 54392555Sdes fprintf(stderr, "# %s %s\n", c->c_name, chop(buf)); 54492555Sdes n = snprintf(buf, sizeof buf, "SSH-%d.%d-OpenSSH-keyscan\r\n", 54592555Sdes c->c_keytype == KT_RSA1? PROTOCOL_MAJOR_1 : PROTOCOL_MAJOR_2, 54692555Sdes c->c_keytype == KT_RSA1? PROTOCOL_MINOR_1 : PROTOCOL_MINOR_2); 54776259Sgreen if (atomicio(write, s, buf, n) != n) { 54876259Sgreen error("write (%s): %s", c->c_name, strerror(errno)); 54976259Sgreen confree(s); 55076259Sgreen return; 55176259Sgreen } 55292555Sdes if (c->c_keytype != KT_RSA1) { 55392555Sdes keyprint(c, keygrab_ssh2(c)); 55492555Sdes confree(s); 55592555Sdes return; 55692555Sdes } 55776259Sgreen c->c_status = CS_SIZE; 55876259Sgreen contouch(s); 55976259Sgreen} 56076259Sgreen 56192555Sdesstatic void 56276259Sgreenconread(int s) 56376259Sgreen{ 564106130Sdes con *c = &fdcon[s]; 56576259Sgreen int n; 56676259Sgreen 56776259Sgreen if (c->c_status == CS_CON) { 56876259Sgreen congreet(s); 56976259Sgreen return; 57076259Sgreen } 57176259Sgreen n = read(s, c->c_data + c->c_off, c->c_len - c->c_off); 57276259Sgreen if (n < 0) { 57376259Sgreen error("read (%s): %s", c->c_name, strerror(errno)); 57476259Sgreen confree(s); 57576259Sgreen return; 57676259Sgreen } 57776259Sgreen c->c_off += n; 57876259Sgreen 57976259Sgreen if (c->c_off == c->c_len) 58076259Sgreen switch (c->c_status) { 58176259Sgreen case CS_SIZE: 58276259Sgreen c->c_plen = htonl(c->c_plen); 58376259Sgreen c->c_len = c->c_plen + 8 - (c->c_plen & 7); 58476259Sgreen c->c_off = 0; 58576259Sgreen c->c_data = xmalloc(c->c_len); 58676259Sgreen c->c_status = CS_KEYS; 58776259Sgreen break; 58876259Sgreen case CS_KEYS: 58992555Sdes keyprint(c, keygrab_ssh1(c)); 59076259Sgreen confree(s); 59176259Sgreen return; 59276259Sgreen break; 59376259Sgreen default: 59476259Sgreen fatal("conread: invalid status %d", c->c_status); 59576259Sgreen break; 59676259Sgreen } 59776259Sgreen 59876259Sgreen contouch(s); 59976259Sgreen} 60076259Sgreen 60192555Sdesstatic void 60276259Sgreenconloop(void) 60376259Sgreen{ 604106130Sdes struct timeval seltime, now; 60576259Sgreen fd_set *r, *e; 606106130Sdes con *c; 60776259Sgreen int i; 60876259Sgreen 60976259Sgreen gettimeofday(&now, NULL); 61098675Sdes c = TAILQ_FIRST(&tq); 61176259Sgreen 61276259Sgreen if (c && (c->c_tv.tv_sec > now.tv_sec || 61376259Sgreen (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec > now.tv_usec))) { 61476259Sgreen seltime = c->c_tv; 61576259Sgreen seltime.tv_sec -= now.tv_sec; 61676259Sgreen seltime.tv_usec -= now.tv_usec; 61776259Sgreen if (seltime.tv_usec < 0) { 61876259Sgreen seltime.tv_usec += 1000000; 61976259Sgreen seltime.tv_sec--; 62076259Sgreen } 62176259Sgreen } else 62276259Sgreen seltime.tv_sec = seltime.tv_usec = 0; 62376259Sgreen 62476259Sgreen r = xmalloc(read_wait_size); 62576259Sgreen memcpy(r, read_wait, read_wait_size); 62676259Sgreen e = xmalloc(read_wait_size); 62776259Sgreen memcpy(e, read_wait, read_wait_size); 62876259Sgreen 62976259Sgreen while (select(maxfd, r, NULL, e, &seltime) == -1 && 63076259Sgreen (errno == EAGAIN || errno == EINTR)) 63176259Sgreen ; 63276259Sgreen 63376259Sgreen for (i = 0; i < maxfd; i++) { 63476259Sgreen if (FD_ISSET(i, e)) { 63576259Sgreen error("%s: exception!", fdcon[i].c_name); 63676259Sgreen confree(i); 63776259Sgreen } else if (FD_ISSET(i, r)) 63876259Sgreen conread(i); 63976259Sgreen } 64076259Sgreen xfree(r); 64176259Sgreen xfree(e); 64276259Sgreen 64398675Sdes c = TAILQ_FIRST(&tq); 64476259Sgreen while (c && (c->c_tv.tv_sec < now.tv_sec || 64576259Sgreen (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec < now.tv_usec))) { 64676259Sgreen int s = c->c_fd; 64776259Sgreen 64898675Sdes c = TAILQ_NEXT(c, c_link); 64976259Sgreen conrecycle(s); 65076259Sgreen } 65176259Sgreen} 65276259Sgreen 65392555Sdesstatic void 65492555Sdesdo_host(char *host) 65576259Sgreen{ 65692555Sdes char *name = strnnsep(&host, " \t\n"); 65792555Sdes int j; 65876259Sgreen 65992555Sdes if (name == NULL) 66092555Sdes return; 66192555Sdes for (j = KT_RSA1; j <= KT_RSA; j *= 2) { 66292555Sdes if (get_keytypes & j) { 66392555Sdes while (ncon >= MAXCON) 66492555Sdes conloop(); 66592555Sdes conalloc(name, *host ? host : name, j); 66676259Sgreen } 66776259Sgreen } 66876259Sgreen} 66976259Sgreen 67076259Sgreenvoid 67192555Sdesfatal(const char *fmt,...) 67292555Sdes{ 67392555Sdes va_list args; 674106130Sdes 67592555Sdes va_start(args, fmt); 67692555Sdes do_log(SYSLOG_LEVEL_FATAL, fmt, args); 67792555Sdes va_end(args); 67892555Sdes if (nonfatal_fatal) 67992555Sdes longjmp(kexjmp, -1); 68092555Sdes else 68192555Sdes fatal_cleanup(); 68292555Sdes} 68392555Sdes 68492555Sdesstatic void 68576259Sgreenusage(void) 68676259Sgreen{ 687106130Sdes fprintf(stderr, "usage: %s [-v46] [-p port] [-T timeout] [-f file]\n" 688106130Sdes "\t\t [host | addrlist namelist] [...]\n", 68992555Sdes __progname); 69092555Sdes exit(1); 69176259Sgreen} 69276259Sgreen 69376259Sgreenint 69476259Sgreenmain(int argc, char **argv) 69576259Sgreen{ 69692555Sdes int debug_flag = 0, log_level = SYSLOG_LEVEL_INFO; 69792555Sdes int opt, fopt_count = 0; 69892555Sdes char *tname; 69976259Sgreen 70092555Sdes extern int optind; 70192555Sdes extern char *optarg; 70292555Sdes 70398941Sdes __progname = get_progname(argv[0]); 70498941Sdes init_rng(); 70598941Sdes seed_rng(); 70676259Sgreen TAILQ_INIT(&tq); 70776259Sgreen 70892555Sdes if (argc <= 1) 70976259Sgreen usage(); 71076259Sgreen 71192555Sdes while ((opt = getopt(argc, argv, "v46p:T:t:f:")) != -1) { 71292555Sdes switch (opt) { 71392555Sdes case 'p': 71492555Sdes ssh_port = a2port(optarg); 71592555Sdes if (ssh_port == 0) { 71692555Sdes fprintf(stderr, "Bad port '%s'\n", optarg); 71792555Sdes exit(1); 71892555Sdes } 71992555Sdes break; 72092555Sdes case 'T': 721106130Sdes timeout = convtime(optarg); 722106130Sdes if (timeout == -1 || timeout == 0) { 723106130Sdes fprintf(stderr, "Bad timeout '%s'\n", optarg); 72476259Sgreen usage(); 725106130Sdes } 72692555Sdes break; 72792555Sdes case 'v': 72892555Sdes if (!debug_flag) { 72992555Sdes debug_flag = 1; 73092555Sdes log_level = SYSLOG_LEVEL_DEBUG1; 73192555Sdes } 73292555Sdes else if (log_level < SYSLOG_LEVEL_DEBUG3) 73392555Sdes log_level++; 73492555Sdes else 73592555Sdes fatal("Too high debugging level."); 73692555Sdes break; 73792555Sdes case 'f': 73892555Sdes if (strcmp(optarg, "-") == 0) 73992555Sdes optarg = NULL; 74092555Sdes argv[fopt_count++] = optarg; 74192555Sdes break; 74292555Sdes case 't': 74392555Sdes get_keytypes = 0; 74492555Sdes tname = strtok(optarg, ","); 74592555Sdes while (tname) { 74692555Sdes int type = key_type_from_name(tname); 74792555Sdes switch (type) { 74892555Sdes case KEY_RSA1: 74992555Sdes get_keytypes |= KT_RSA1; 75092555Sdes break; 75192555Sdes case KEY_DSA: 75292555Sdes get_keytypes |= KT_DSA; 75392555Sdes break; 75492555Sdes case KEY_RSA: 75592555Sdes get_keytypes |= KT_RSA; 75692555Sdes break; 75792555Sdes case KEY_UNSPEC: 75892555Sdes fatal("unknown key type %s", tname); 75992555Sdes } 76092555Sdes tname = strtok(NULL, ","); 76192555Sdes } 76292555Sdes break; 76392555Sdes case '4': 76492555Sdes IPv4or6 = AF_INET; 76592555Sdes break; 76692555Sdes case '6': 76792555Sdes IPv4or6 = AF_INET6; 76892555Sdes break; 76992555Sdes case '?': 77092555Sdes default: 77192555Sdes usage(); 77276259Sgreen } 77376259Sgreen } 77492555Sdes if (optind == argc && !fopt_count) 77576259Sgreen usage(); 77676259Sgreen 77792555Sdes log_init("ssh-keyscan", log_level, SYSLOG_FACILITY_USER, 1); 77892555Sdes 77976259Sgreen maxfd = fdlim_get(1); 78076259Sgreen if (maxfd < 0) 78176259Sgreen fatal("%s: fdlim_get: bad value", __progname); 78276259Sgreen if (maxfd > MAXMAXFD) 78376259Sgreen maxfd = MAXMAXFD; 78476259Sgreen if (MAXCON <= 0) 78576259Sgreen fatal("%s: not enough file descriptors", __progname); 78676259Sgreen if (maxfd > fdlim_get(0)) 78776259Sgreen fdlim_set(maxfd); 78876259Sgreen fdcon = xmalloc(maxfd * sizeof(con)); 78976259Sgreen memset(fdcon, 0, maxfd * sizeof(con)); 79076259Sgreen 79176259Sgreen read_wait_size = howmany(maxfd, NFDBITS) * sizeof(fd_mask); 79276259Sgreen read_wait = xmalloc(read_wait_size); 79376259Sgreen memset(read_wait, 0, read_wait_size); 79476259Sgreen 79592555Sdes if (fopt_count) { 79692555Sdes Linebuf *lb; 79792555Sdes char *line; 79892555Sdes int j; 79976259Sgreen 80092555Sdes for (j = 0; j < fopt_count; j++) { 80192555Sdes lb = Linebuf_alloc(argv[j], error); 80292555Sdes if (!lb) 80392555Sdes continue; 80492555Sdes while ((line = Linebuf_getline(lb)) != NULL) 80592555Sdes do_host(line); 80692555Sdes Linebuf_free(lb); 80776259Sgreen } 80892555Sdes } 80992555Sdes 81092555Sdes while (optind < argc) 81192555Sdes do_host(argv[optind++]); 81292555Sdes 81376259Sgreen while (ncon > 0) 81476259Sgreen conloop(); 81576259Sgreen 81676259Sgreen return (0); 81776259Sgreen} 818