ssh-keyscan.c revision 124211
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" 10124211SdesRCSID("$OpenBSD: ssh-keyscan.c,v 1.44 2003/06/28 16:23:06 deraadt Exp $"); 1176259Sgreen 12106130Sdes#include "openbsd-compat/sys-queue.h" 1376259Sgreen 1476259Sgreen#include <openssl/bn.h> 1576259Sgreen 1692555Sdes#include <setjmp.h> 1776259Sgreen#include "xmalloc.h" 1876259Sgreen#include "ssh.h" 1976259Sgreen#include "ssh1.h" 2076259Sgreen#include "key.h" 2192555Sdes#include "kex.h" 2292555Sdes#include "compat.h" 2392555Sdes#include "myproposal.h" 2492555Sdes#include "packet.h" 2592555Sdes#include "dispatch.h" 2676259Sgreen#include "buffer.h" 2776259Sgreen#include "bufaux.h" 2876259Sgreen#include "log.h" 2976259Sgreen#include "atomicio.h" 3092555Sdes#include "misc.h" 3176259Sgreen 3298941Sdes/* Flag indicating whether IPv4 or IPv6. This can be set on the command line. 3398941Sdes Default value is AF_UNSPEC means both IPv4 and IPv6. */ 3498941Sdesint IPv4or6 = AF_UNSPEC; 3576259Sgreen 3692555Sdesint ssh_port = SSH_DEFAULT_PORT; 3776259Sgreen 3892555Sdes#define KT_RSA1 1 3992555Sdes#define KT_DSA 2 4092555Sdes#define KT_RSA 4 4192555Sdes 4292555Sdesint get_keytypes = KT_RSA1; /* Get only RSA1 keys by default */ 4392555Sdes 4476259Sgreen#define MAXMAXFD 256 4576259Sgreen 4676259Sgreen/* The number of seconds after which to give up on a TCP connection */ 4776259Sgreenint timeout = 5; 4876259Sgreen 4976259Sgreenint maxfd; 5076259Sgreen#define MAXCON (maxfd - 10) 5176259Sgreen 5298941Sdes#ifdef HAVE___PROGNAME 5376259Sgreenextern char *__progname; 5498941Sdes#else 5598941Sdeschar *__progname; 5698941Sdes#endif 5776259Sgreenfd_set *read_wait; 5876259Sgreensize_t read_wait_size; 5976259Sgreenint ncon; 6092555Sdesint nonfatal_fatal = 0; 6192555Sdesjmp_buf kexjmp; 6292555SdesKey *kexjmp_key; 6376259Sgreen 6476259Sgreen/* 6576259Sgreen * Keep a connection structure for each file descriptor. The state 6676259Sgreen * associated with file descriptor n is held in fdcon[n]. 6776259Sgreen */ 6876259Sgreentypedef struct Connection { 6976259Sgreen u_char c_status; /* State of connection on this file desc. */ 7076259Sgreen#define CS_UNUSED 0 /* File descriptor unused */ 7176259Sgreen#define CS_CON 1 /* Waiting to connect/read greeting */ 7276259Sgreen#define CS_SIZE 2 /* Waiting to read initial packet size */ 7376259Sgreen#define CS_KEYS 3 /* Waiting to read public key packet */ 7476259Sgreen int c_fd; /* Quick lookup: c->c_fd == c - fdcon */ 7576259Sgreen int c_plen; /* Packet length field for ssh packet */ 7676259Sgreen int c_len; /* Total bytes which must be read. */ 7776259Sgreen int c_off; /* Length of data read so far. */ 7892555Sdes int c_keytype; /* Only one of KT_RSA1, KT_DSA, or KT_RSA */ 7976259Sgreen char *c_namebase; /* Address to free for c_name and c_namelist */ 8076259Sgreen char *c_name; /* Hostname of connection for errors */ 8176259Sgreen char *c_namelist; /* Pointer to other possible addresses */ 8276259Sgreen char *c_output_name; /* Hostname of connection for output */ 8376259Sgreen char *c_data; /* Data read from this fd */ 8492555Sdes Kex *c_kex; /* The key-exchange struct for ssh2 */ 8576259Sgreen struct timeval c_tv; /* Time at which connection gets aborted */ 8676259Sgreen TAILQ_ENTRY(Connection) c_link; /* List of connections in timeout order. */ 8776259Sgreen} con; 8876259Sgreen 8976259SgreenTAILQ_HEAD(conlist, Connection) tq; /* Timeout Queue */ 9076259Sgreencon *fdcon; 9176259Sgreen 9276259Sgreen/* 9376259Sgreen * This is just a wrapper around fgets() to make it usable. 9476259Sgreen */ 9576259Sgreen 9676259Sgreen/* Stress-test. Increase this later. */ 9776259Sgreen#define LINEBUF_SIZE 16 9876259Sgreen 9976259Sgreentypedef struct { 10076259Sgreen char *buf; 10176259Sgreen u_int size; 10276259Sgreen int lineno; 10376259Sgreen const char *filename; 10476259Sgreen FILE *stream; 10576259Sgreen void (*errfun) (const char *,...); 10676259Sgreen} Linebuf; 10776259Sgreen 10892555Sdesstatic Linebuf * 10976259SgreenLinebuf_alloc(const char *filename, void (*errfun) (const char *,...)) 11076259Sgreen{ 11176259Sgreen Linebuf *lb; 11276259Sgreen 11376259Sgreen if (!(lb = malloc(sizeof(*lb)))) { 11476259Sgreen if (errfun) 115106130Sdes (*errfun) ("linebuf (%s): malloc failed\n", 116106130Sdes filename ? filename : "(stdin)"); 11776259Sgreen return (NULL); 11876259Sgreen } 11976259Sgreen if (filename) { 12076259Sgreen lb->filename = filename; 12176259Sgreen if (!(lb->stream = fopen(filename, "r"))) { 12276259Sgreen xfree(lb); 12376259Sgreen if (errfun) 12476259Sgreen (*errfun) ("%s: %s\n", filename, strerror(errno)); 12576259Sgreen return (NULL); 12676259Sgreen } 12776259Sgreen } else { 12876259Sgreen lb->filename = "(stdin)"; 12976259Sgreen lb->stream = stdin; 13076259Sgreen } 13176259Sgreen 13276259Sgreen if (!(lb->buf = malloc(lb->size = LINEBUF_SIZE))) { 13376259Sgreen if (errfun) 13476259Sgreen (*errfun) ("linebuf (%s): malloc failed\n", lb->filename); 13576259Sgreen xfree(lb); 13676259Sgreen return (NULL); 13776259Sgreen } 13876259Sgreen lb->errfun = errfun; 13976259Sgreen lb->lineno = 0; 14076259Sgreen return (lb); 14176259Sgreen} 14276259Sgreen 14392555Sdesstatic void 14476259SgreenLinebuf_free(Linebuf * lb) 14576259Sgreen{ 14676259Sgreen fclose(lb->stream); 14776259Sgreen xfree(lb->buf); 14876259Sgreen xfree(lb); 14976259Sgreen} 15076259Sgreen 15192555Sdes#if 0 15292555Sdesstatic void 15376259SgreenLinebuf_restart(Linebuf * lb) 15476259Sgreen{ 15576259Sgreen clearerr(lb->stream); 15676259Sgreen rewind(lb->stream); 15776259Sgreen lb->lineno = 0; 15876259Sgreen} 15976259Sgreen 16092555Sdesstatic int 16176259SgreenLinebuf_lineno(Linebuf * lb) 16276259Sgreen{ 16376259Sgreen return (lb->lineno); 16476259Sgreen} 16592555Sdes#endif 16676259Sgreen 16792555Sdesstatic char * 16876259SgreenLinebuf_getline(Linebuf * lb) 16976259Sgreen{ 17076259Sgreen int n = 0; 171106130Sdes void *p; 17276259Sgreen 17376259Sgreen lb->lineno++; 17476259Sgreen for (;;) { 17576259Sgreen /* Read a line */ 17676259Sgreen if (!fgets(&lb->buf[n], lb->size - n, lb->stream)) { 17776259Sgreen if (ferror(lb->stream) && lb->errfun) 178106130Sdes (*lb->errfun)("%s: %s\n", lb->filename, 17976259Sgreen strerror(errno)); 18076259Sgreen return (NULL); 18176259Sgreen } 18276259Sgreen n = strlen(lb->buf); 18376259Sgreen 18476259Sgreen /* Return it or an error if it fits */ 18576259Sgreen if (n > 0 && lb->buf[n - 1] == '\n') { 18676259Sgreen lb->buf[n - 1] = '\0'; 18776259Sgreen return (lb->buf); 18876259Sgreen } 18976259Sgreen if (n != lb->size - 1) { 19076259Sgreen if (lb->errfun) 191106130Sdes (*lb->errfun)("%s: skipping incomplete last line\n", 19276259Sgreen lb->filename); 19376259Sgreen return (NULL); 19476259Sgreen } 19576259Sgreen /* Double the buffer if we need more space */ 196106130Sdes lb->size *= 2; 197106130Sdes if ((p = realloc(lb->buf, lb->size)) == NULL) { 198106130Sdes lb->size /= 2; 19976259Sgreen if (lb->errfun) 200106130Sdes (*lb->errfun)("linebuf (%s): realloc failed\n", 20176259Sgreen lb->filename); 20276259Sgreen return (NULL); 20376259Sgreen } 204106130Sdes lb->buf = p; 20576259Sgreen } 20676259Sgreen} 20776259Sgreen 20892555Sdesstatic int 20976259Sgreenfdlim_get(int hard) 21076259Sgreen{ 21198941Sdes#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) 21276259Sgreen struct rlimit rlfd; 21376259Sgreen 21476259Sgreen if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0) 21576259Sgreen return (-1); 21676259Sgreen if ((hard ? rlfd.rlim_max : rlfd.rlim_cur) == RLIM_INFINITY) 21776259Sgreen return 10000; 21876259Sgreen else 21976259Sgreen return hard ? rlfd.rlim_max : rlfd.rlim_cur; 22098941Sdes#elif defined (HAVE_SYSCONF) 22198941Sdes return sysconf (_SC_OPEN_MAX); 22298941Sdes#else 22398941Sdes return 10000; 22498941Sdes#endif 22576259Sgreen} 22676259Sgreen 22792555Sdesstatic int 22876259Sgreenfdlim_set(int lim) 22976259Sgreen{ 23098941Sdes#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) 23176259Sgreen struct rlimit rlfd; 23298941Sdes#endif 233106130Sdes 23476259Sgreen if (lim <= 0) 23576259Sgreen return (-1); 23698941Sdes#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) 23776259Sgreen if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0) 23876259Sgreen return (-1); 23976259Sgreen rlfd.rlim_cur = lim; 24076259Sgreen if (setrlimit(RLIMIT_NOFILE, &rlfd) < 0) 24176259Sgreen return (-1); 24298941Sdes#elif defined (HAVE_SETDTABLESIZE) 24398941Sdes setdtablesize(lim); 24498941Sdes#endif 24576259Sgreen return (0); 24676259Sgreen} 24776259Sgreen 24876259Sgreen/* 24976259Sgreen * This is an strsep function that returns a null field for adjacent 25076259Sgreen * separators. This is the same as the 4.4BSD strsep, but different from the 25176259Sgreen * one in the GNU libc. 25276259Sgreen */ 25392555Sdesstatic char * 25476259Sgreenxstrsep(char **str, const char *delim) 25576259Sgreen{ 25676259Sgreen char *s, *e; 25776259Sgreen 25876259Sgreen if (!**str) 25976259Sgreen return (NULL); 26076259Sgreen 26176259Sgreen s = *str; 26276259Sgreen e = s + strcspn(s, delim); 26376259Sgreen 26476259Sgreen if (*e != '\0') 26576259Sgreen *e++ = '\0'; 26676259Sgreen *str = e; 26776259Sgreen 26876259Sgreen return (s); 26976259Sgreen} 27076259Sgreen 27176259Sgreen/* 27276259Sgreen * Get the next non-null token (like GNU strsep). Strsep() will return a 27376259Sgreen * null token for two adjacent separators, so we may have to loop. 27476259Sgreen */ 27592555Sdesstatic char * 27676259Sgreenstrnnsep(char **stringp, char *delim) 27776259Sgreen{ 27876259Sgreen char *tok; 27976259Sgreen 28076259Sgreen do { 28176259Sgreen tok = xstrsep(stringp, delim); 28276259Sgreen } while (tok && *tok == '\0'); 28376259Sgreen return (tok); 28476259Sgreen} 28576259Sgreen 28692555Sdesstatic Key * 28792555Sdeskeygrab_ssh1(con *c) 28876259Sgreen{ 28976259Sgreen static Key *rsa; 29076259Sgreen static Buffer msg; 29176259Sgreen 29276259Sgreen if (rsa == NULL) { 29376259Sgreen buffer_init(&msg); 29476259Sgreen rsa = key_new(KEY_RSA1); 29576259Sgreen } 29692555Sdes buffer_append(&msg, c->c_data, c->c_plen); 29792555Sdes buffer_consume(&msg, 8 - (c->c_plen & 7)); /* padding */ 29876259Sgreen if (buffer_get_char(&msg) != (int) SSH_SMSG_PUBLIC_KEY) { 29992555Sdes error("%s: invalid packet type", c->c_name); 30076259Sgreen buffer_clear(&msg); 30192555Sdes return NULL; 30276259Sgreen } 30376259Sgreen buffer_consume(&msg, 8); /* cookie */ 30476259Sgreen 30576259Sgreen /* server key */ 30676259Sgreen (void) buffer_get_int(&msg); 30776259Sgreen buffer_get_bignum(&msg, rsa->rsa->e); 30876259Sgreen buffer_get_bignum(&msg, rsa->rsa->n); 30976259Sgreen 31076259Sgreen /* host key */ 31176259Sgreen (void) buffer_get_int(&msg); 31276259Sgreen buffer_get_bignum(&msg, rsa->rsa->e); 31376259Sgreen buffer_get_bignum(&msg, rsa->rsa->n); 31492555Sdes 31576259Sgreen buffer_clear(&msg); 31676259Sgreen 31792555Sdes return (rsa); 31892555Sdes} 31992555Sdes 32092555Sdesstatic int 32192555Sdeshostjump(Key *hostkey) 32292555Sdes{ 32392555Sdes kexjmp_key = hostkey; 32492555Sdes longjmp(kexjmp, 1); 32592555Sdes} 32692555Sdes 32792555Sdesstatic int 32892555Sdesssh2_capable(int remote_major, int remote_minor) 32992555Sdes{ 33092555Sdes switch (remote_major) { 33192555Sdes case 1: 33292555Sdes if (remote_minor == 99) 33392555Sdes return 1; 33492555Sdes break; 33592555Sdes case 2: 33692555Sdes return 1; 33792555Sdes default: 33892555Sdes break; 33992555Sdes } 34092555Sdes return 0; 34192555Sdes} 34292555Sdes 34392555Sdesstatic Key * 34492555Sdeskeygrab_ssh2(con *c) 34592555Sdes{ 34692555Sdes int j; 34792555Sdes 34892555Sdes packet_set_connection(c->c_fd, c->c_fd); 34992555Sdes enable_compat20(); 35092555Sdes myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = c->c_keytype == KT_DSA? 35192555Sdes "ssh-dss": "ssh-rsa"; 35292555Sdes c->c_kex = kex_setup(myproposal); 353113911Sdes c->c_kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; 354113911Sdes c->c_kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; 35592555Sdes c->c_kex->verify_host_key = hostjump; 35692555Sdes 35792555Sdes if (!(j = setjmp(kexjmp))) { 35892555Sdes nonfatal_fatal = 1; 35992555Sdes dispatch_run(DISPATCH_BLOCK, &c->c_kex->done, c->c_kex); 36092555Sdes fprintf(stderr, "Impossible! dispatch_run() returned!\n"); 36192555Sdes exit(1); 36292555Sdes } 36392555Sdes nonfatal_fatal = 0; 36492555Sdes xfree(c->c_kex); 36592555Sdes c->c_kex = NULL; 36692555Sdes packet_close(); 36792555Sdes 36892555Sdes return j < 0? NULL : kexjmp_key; 36992555Sdes} 37092555Sdes 37192555Sdesstatic void 37292555Sdeskeyprint(con *c, Key *key) 37392555Sdes{ 37492555Sdes if (!key) 37592555Sdes return; 37692555Sdes 37792555Sdes fprintf(stdout, "%s ", c->c_output_name ? c->c_output_name : c->c_name); 37892555Sdes key_write(key, stdout); 37976259Sgreen fputs("\n", stdout); 38076259Sgreen} 38176259Sgreen 38292555Sdesstatic int 38376259Sgreentcpconnect(char *host) 38476259Sgreen{ 38576259Sgreen struct addrinfo hints, *ai, *aitop; 38676259Sgreen char strport[NI_MAXSERV]; 38776259Sgreen int gaierr, s = -1; 38876259Sgreen 38992555Sdes snprintf(strport, sizeof strport, "%d", ssh_port); 39076259Sgreen memset(&hints, 0, sizeof(hints)); 39192555Sdes hints.ai_family = IPv4or6; 39276259Sgreen hints.ai_socktype = SOCK_STREAM; 39376259Sgreen if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) 39476259Sgreen fatal("getaddrinfo %s: %s", host, gai_strerror(gaierr)); 39576259Sgreen for (ai = aitop; ai; ai = ai->ai_next) { 396124211Sdes s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 39776259Sgreen if (s < 0) { 39876259Sgreen error("socket: %s", strerror(errno)); 39976259Sgreen continue; 40076259Sgreen } 40176259Sgreen if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) 40276259Sgreen fatal("F_SETFL: %s", strerror(errno)); 40376259Sgreen if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0 && 40476259Sgreen errno != EINPROGRESS) 40576259Sgreen error("connect (`%s'): %s", host, strerror(errno)); 40676259Sgreen else 40776259Sgreen break; 40876259Sgreen close(s); 40976259Sgreen s = -1; 41076259Sgreen } 41176259Sgreen freeaddrinfo(aitop); 41276259Sgreen return s; 41376259Sgreen} 41476259Sgreen 41592555Sdesstatic int 41692555Sdesconalloc(char *iname, char *oname, int keytype) 41776259Sgreen{ 418106130Sdes char *namebase, *name, *namelist; 41976259Sgreen int s; 42076259Sgreen 42176259Sgreen namebase = namelist = xstrdup(iname); 42276259Sgreen 42376259Sgreen do { 42476259Sgreen name = xstrsep(&namelist, ","); 42576259Sgreen if (!name) { 42676259Sgreen xfree(namebase); 42776259Sgreen return (-1); 42876259Sgreen } 42976259Sgreen } while ((s = tcpconnect(name)) < 0); 43076259Sgreen 43176259Sgreen if (s >= maxfd) 43276259Sgreen fatal("conalloc: fdno %d too high", s); 43376259Sgreen if (fdcon[s].c_status) 43476259Sgreen fatal("conalloc: attempt to reuse fdno %d", s); 43576259Sgreen 43676259Sgreen fdcon[s].c_fd = s; 43776259Sgreen fdcon[s].c_status = CS_CON; 43876259Sgreen fdcon[s].c_namebase = namebase; 43976259Sgreen fdcon[s].c_name = name; 44076259Sgreen fdcon[s].c_namelist = namelist; 44176259Sgreen fdcon[s].c_output_name = xstrdup(oname); 44276259Sgreen fdcon[s].c_data = (char *) &fdcon[s].c_plen; 44376259Sgreen fdcon[s].c_len = 4; 44476259Sgreen fdcon[s].c_off = 0; 44592555Sdes fdcon[s].c_keytype = keytype; 44676259Sgreen gettimeofday(&fdcon[s].c_tv, NULL); 44776259Sgreen fdcon[s].c_tv.tv_sec += timeout; 44876259Sgreen TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); 44976259Sgreen FD_SET(s, read_wait); 45076259Sgreen ncon++; 45176259Sgreen return (s); 45276259Sgreen} 45376259Sgreen 45492555Sdesstatic void 45576259Sgreenconfree(int s) 45676259Sgreen{ 45776259Sgreen if (s >= maxfd || fdcon[s].c_status == CS_UNUSED) 45876259Sgreen fatal("confree: attempt to free bad fdno %d", s); 45976259Sgreen close(s); 46076259Sgreen xfree(fdcon[s].c_namebase); 46176259Sgreen xfree(fdcon[s].c_output_name); 46276259Sgreen if (fdcon[s].c_status == CS_KEYS) 46376259Sgreen xfree(fdcon[s].c_data); 46476259Sgreen fdcon[s].c_status = CS_UNUSED; 46592555Sdes fdcon[s].c_keytype = 0; 46676259Sgreen TAILQ_REMOVE(&tq, &fdcon[s], c_link); 46776259Sgreen FD_CLR(s, read_wait); 46876259Sgreen ncon--; 46976259Sgreen} 47076259Sgreen 47192555Sdesstatic void 47276259Sgreencontouch(int s) 47376259Sgreen{ 47476259Sgreen TAILQ_REMOVE(&tq, &fdcon[s], c_link); 47576259Sgreen gettimeofday(&fdcon[s].c_tv, NULL); 47676259Sgreen fdcon[s].c_tv.tv_sec += timeout; 47776259Sgreen TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); 47876259Sgreen} 47976259Sgreen 48092555Sdesstatic int 48176259Sgreenconrecycle(int s) 48276259Sgreen{ 483106130Sdes con *c = &fdcon[s]; 48476259Sgreen int ret; 48576259Sgreen 48692555Sdes ret = conalloc(c->c_namelist, c->c_output_name, c->c_keytype); 48776259Sgreen confree(s); 48876259Sgreen return (ret); 48976259Sgreen} 49076259Sgreen 49192555Sdesstatic void 49276259Sgreencongreet(int s) 49376259Sgreen{ 494106130Sdes int remote_major, remote_minor, n = 0; 49592555Sdes char buf[256], *cp; 49692555Sdes char remote_version[sizeof buf]; 49776259Sgreen size_t bufsiz; 49876259Sgreen con *c = &fdcon[s]; 49976259Sgreen 50076259Sgreen bufsiz = sizeof(buf); 50176259Sgreen cp = buf; 50292555Sdes while (bufsiz-- && (n = read(s, cp, 1)) == 1 && *cp != '\n') { 50392555Sdes if (*cp == '\r') 50492555Sdes *cp = '\n'; 50576259Sgreen cp++; 50692555Sdes } 50776259Sgreen if (n < 0) { 50876259Sgreen if (errno != ECONNREFUSED) 50976259Sgreen error("read (%s): %s", c->c_name, strerror(errno)); 51076259Sgreen conrecycle(s); 51176259Sgreen return; 51276259Sgreen } 51392555Sdes if (n == 0) { 51492555Sdes error("%s: Connection closed by remote host", c->c_name); 51592555Sdes conrecycle(s); 51692555Sdes return; 51792555Sdes } 51876259Sgreen if (*cp != '\n' && *cp != '\r') { 51976259Sgreen error("%s: bad greeting", c->c_name); 52076259Sgreen confree(s); 52176259Sgreen return; 52276259Sgreen } 52376259Sgreen *cp = '\0'; 52492555Sdes if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", 52592555Sdes &remote_major, &remote_minor, remote_version) == 3) 52692555Sdes compat_datafellows(remote_version); 52792555Sdes else 52892555Sdes datafellows = 0; 52992555Sdes if (c->c_keytype != KT_RSA1) { 53092555Sdes if (!ssh2_capable(remote_major, remote_minor)) { 53192555Sdes debug("%s doesn't support ssh2", c->c_name); 53292555Sdes confree(s); 53392555Sdes return; 53492555Sdes } 53592555Sdes } else if (remote_major != 1) { 53692555Sdes debug("%s doesn't support ssh1", c->c_name); 53792555Sdes confree(s); 53892555Sdes return; 53992555Sdes } 54092555Sdes fprintf(stderr, "# %s %s\n", c->c_name, chop(buf)); 54192555Sdes n = snprintf(buf, sizeof buf, "SSH-%d.%d-OpenSSH-keyscan\r\n", 54292555Sdes c->c_keytype == KT_RSA1? PROTOCOL_MAJOR_1 : PROTOCOL_MAJOR_2, 54392555Sdes c->c_keytype == KT_RSA1? PROTOCOL_MINOR_1 : PROTOCOL_MINOR_2); 544124211Sdes if (atomicio(vwrite, s, buf, n) != n) { 54576259Sgreen error("write (%s): %s", c->c_name, strerror(errno)); 54676259Sgreen confree(s); 54776259Sgreen return; 54876259Sgreen } 54992555Sdes if (c->c_keytype != KT_RSA1) { 55092555Sdes keyprint(c, keygrab_ssh2(c)); 55192555Sdes confree(s); 55292555Sdes return; 55392555Sdes } 55476259Sgreen c->c_status = CS_SIZE; 55576259Sgreen contouch(s); 55676259Sgreen} 55776259Sgreen 55892555Sdesstatic void 55976259Sgreenconread(int s) 56076259Sgreen{ 561106130Sdes con *c = &fdcon[s]; 56276259Sgreen int n; 56376259Sgreen 56476259Sgreen if (c->c_status == CS_CON) { 56576259Sgreen congreet(s); 56676259Sgreen return; 56776259Sgreen } 56876259Sgreen n = read(s, c->c_data + c->c_off, c->c_len - c->c_off); 56976259Sgreen if (n < 0) { 57076259Sgreen error("read (%s): %s", c->c_name, strerror(errno)); 57176259Sgreen confree(s); 57276259Sgreen return; 57376259Sgreen } 57476259Sgreen c->c_off += n; 57576259Sgreen 57676259Sgreen if (c->c_off == c->c_len) 57776259Sgreen switch (c->c_status) { 57876259Sgreen case CS_SIZE: 57976259Sgreen c->c_plen = htonl(c->c_plen); 58076259Sgreen c->c_len = c->c_plen + 8 - (c->c_plen & 7); 58176259Sgreen c->c_off = 0; 58276259Sgreen c->c_data = xmalloc(c->c_len); 58376259Sgreen c->c_status = CS_KEYS; 58476259Sgreen break; 58576259Sgreen case CS_KEYS: 58692555Sdes keyprint(c, keygrab_ssh1(c)); 58776259Sgreen confree(s); 58876259Sgreen return; 58976259Sgreen break; 59076259Sgreen default: 59176259Sgreen fatal("conread: invalid status %d", c->c_status); 59276259Sgreen break; 59376259Sgreen } 59476259Sgreen 59576259Sgreen contouch(s); 59676259Sgreen} 59776259Sgreen 59892555Sdesstatic void 59976259Sgreenconloop(void) 60076259Sgreen{ 601106130Sdes struct timeval seltime, now; 60276259Sgreen fd_set *r, *e; 603106130Sdes con *c; 60476259Sgreen int i; 60576259Sgreen 60676259Sgreen gettimeofday(&now, NULL); 60798675Sdes c = TAILQ_FIRST(&tq); 60876259Sgreen 60976259Sgreen if (c && (c->c_tv.tv_sec > now.tv_sec || 61076259Sgreen (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec > now.tv_usec))) { 61176259Sgreen seltime = c->c_tv; 61276259Sgreen seltime.tv_sec -= now.tv_sec; 61376259Sgreen seltime.tv_usec -= now.tv_usec; 61476259Sgreen if (seltime.tv_usec < 0) { 61576259Sgreen seltime.tv_usec += 1000000; 61676259Sgreen seltime.tv_sec--; 61776259Sgreen } 61876259Sgreen } else 61976259Sgreen seltime.tv_sec = seltime.tv_usec = 0; 62076259Sgreen 62176259Sgreen r = xmalloc(read_wait_size); 62276259Sgreen memcpy(r, read_wait, read_wait_size); 62376259Sgreen e = xmalloc(read_wait_size); 62476259Sgreen memcpy(e, read_wait, read_wait_size); 62576259Sgreen 62676259Sgreen while (select(maxfd, r, NULL, e, &seltime) == -1 && 62776259Sgreen (errno == EAGAIN || errno == EINTR)) 62876259Sgreen ; 62976259Sgreen 63076259Sgreen for (i = 0; i < maxfd; i++) { 63176259Sgreen if (FD_ISSET(i, e)) { 63276259Sgreen error("%s: exception!", fdcon[i].c_name); 63376259Sgreen confree(i); 63476259Sgreen } else if (FD_ISSET(i, r)) 63576259Sgreen conread(i); 63676259Sgreen } 63776259Sgreen xfree(r); 63876259Sgreen xfree(e); 63976259Sgreen 64098675Sdes c = TAILQ_FIRST(&tq); 64176259Sgreen while (c && (c->c_tv.tv_sec < now.tv_sec || 64276259Sgreen (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec < now.tv_usec))) { 64376259Sgreen int s = c->c_fd; 64476259Sgreen 64598675Sdes c = TAILQ_NEXT(c, c_link); 64676259Sgreen conrecycle(s); 64776259Sgreen } 64876259Sgreen} 64976259Sgreen 65092555Sdesstatic void 65192555Sdesdo_host(char *host) 65276259Sgreen{ 65392555Sdes char *name = strnnsep(&host, " \t\n"); 65492555Sdes int j; 65576259Sgreen 65692555Sdes if (name == NULL) 65792555Sdes return; 65892555Sdes for (j = KT_RSA1; j <= KT_RSA; j *= 2) { 65992555Sdes if (get_keytypes & j) { 66092555Sdes while (ncon >= MAXCON) 66192555Sdes conloop(); 66292555Sdes conalloc(name, *host ? host : name, j); 66376259Sgreen } 66476259Sgreen } 66576259Sgreen} 66676259Sgreen 66776259Sgreenvoid 66892555Sdesfatal(const char *fmt,...) 66992555Sdes{ 67092555Sdes va_list args; 671106130Sdes 67292555Sdes va_start(args, fmt); 67392555Sdes do_log(SYSLOG_LEVEL_FATAL, fmt, args); 67492555Sdes va_end(args); 67592555Sdes if (nonfatal_fatal) 67692555Sdes longjmp(kexjmp, -1); 67792555Sdes else 67892555Sdes fatal_cleanup(); 67992555Sdes} 68092555Sdes 68192555Sdesstatic void 68276259Sgreenusage(void) 68376259Sgreen{ 684124211Sdes fprintf(stderr, "usage: %s [-v46] [-p port] [-T timeout] [-t type] [-f file]\n" 685106130Sdes "\t\t [host | addrlist namelist] [...]\n", 68692555Sdes __progname); 68792555Sdes exit(1); 68876259Sgreen} 68976259Sgreen 69076259Sgreenint 69176259Sgreenmain(int argc, char **argv) 69276259Sgreen{ 69392555Sdes int debug_flag = 0, log_level = SYSLOG_LEVEL_INFO; 69492555Sdes int opt, fopt_count = 0; 69592555Sdes char *tname; 69676259Sgreen 69792555Sdes extern int optind; 69892555Sdes extern char *optarg; 69992555Sdes 700124211Sdes __progname = ssh_get_progname(argv[0]); 70198941Sdes init_rng(); 70298941Sdes seed_rng(); 70376259Sgreen TAILQ_INIT(&tq); 70476259Sgreen 70592555Sdes if (argc <= 1) 70676259Sgreen usage(); 70776259Sgreen 70892555Sdes while ((opt = getopt(argc, argv, "v46p:T:t:f:")) != -1) { 70992555Sdes switch (opt) { 71092555Sdes case 'p': 71192555Sdes ssh_port = a2port(optarg); 71292555Sdes if (ssh_port == 0) { 71392555Sdes fprintf(stderr, "Bad port '%s'\n", optarg); 71492555Sdes exit(1); 71592555Sdes } 71692555Sdes break; 71792555Sdes case 'T': 718106130Sdes timeout = convtime(optarg); 719106130Sdes if (timeout == -1 || timeout == 0) { 720106130Sdes fprintf(stderr, "Bad timeout '%s'\n", optarg); 72176259Sgreen usage(); 722106130Sdes } 72392555Sdes break; 72492555Sdes case 'v': 72592555Sdes if (!debug_flag) { 72692555Sdes debug_flag = 1; 72792555Sdes log_level = SYSLOG_LEVEL_DEBUG1; 72892555Sdes } 72992555Sdes else if (log_level < SYSLOG_LEVEL_DEBUG3) 73092555Sdes log_level++; 73192555Sdes else 73292555Sdes fatal("Too high debugging level."); 73392555Sdes break; 73492555Sdes case 'f': 73592555Sdes if (strcmp(optarg, "-") == 0) 73692555Sdes optarg = NULL; 73792555Sdes argv[fopt_count++] = optarg; 73892555Sdes break; 73992555Sdes case 't': 74092555Sdes get_keytypes = 0; 74192555Sdes tname = strtok(optarg, ","); 74292555Sdes while (tname) { 74392555Sdes int type = key_type_from_name(tname); 74492555Sdes switch (type) { 74592555Sdes case KEY_RSA1: 74692555Sdes get_keytypes |= KT_RSA1; 74792555Sdes break; 74892555Sdes case KEY_DSA: 74992555Sdes get_keytypes |= KT_DSA; 75092555Sdes break; 75192555Sdes case KEY_RSA: 75292555Sdes get_keytypes |= KT_RSA; 75392555Sdes break; 75492555Sdes case KEY_UNSPEC: 75592555Sdes fatal("unknown key type %s", tname); 75692555Sdes } 75792555Sdes tname = strtok(NULL, ","); 75892555Sdes } 75992555Sdes break; 76092555Sdes case '4': 76192555Sdes IPv4or6 = AF_INET; 76292555Sdes break; 76392555Sdes case '6': 76492555Sdes IPv4or6 = AF_INET6; 76592555Sdes break; 76692555Sdes case '?': 76792555Sdes default: 76892555Sdes usage(); 76976259Sgreen } 77076259Sgreen } 77192555Sdes if (optind == argc && !fopt_count) 77276259Sgreen usage(); 77376259Sgreen 77492555Sdes log_init("ssh-keyscan", log_level, SYSLOG_FACILITY_USER, 1); 77592555Sdes 77676259Sgreen maxfd = fdlim_get(1); 77776259Sgreen if (maxfd < 0) 77876259Sgreen fatal("%s: fdlim_get: bad value", __progname); 77976259Sgreen if (maxfd > MAXMAXFD) 78076259Sgreen maxfd = MAXMAXFD; 78176259Sgreen if (MAXCON <= 0) 78276259Sgreen fatal("%s: not enough file descriptors", __progname); 78376259Sgreen if (maxfd > fdlim_get(0)) 78476259Sgreen fdlim_set(maxfd); 78576259Sgreen fdcon = xmalloc(maxfd * sizeof(con)); 78676259Sgreen memset(fdcon, 0, maxfd * sizeof(con)); 78776259Sgreen 78876259Sgreen read_wait_size = howmany(maxfd, NFDBITS) * sizeof(fd_mask); 78976259Sgreen read_wait = xmalloc(read_wait_size); 79076259Sgreen memset(read_wait, 0, read_wait_size); 79176259Sgreen 79292555Sdes if (fopt_count) { 79392555Sdes Linebuf *lb; 79492555Sdes char *line; 79592555Sdes int j; 79676259Sgreen 79792555Sdes for (j = 0; j < fopt_count; j++) { 79892555Sdes lb = Linebuf_alloc(argv[j], error); 79992555Sdes if (!lb) 80092555Sdes continue; 80192555Sdes while ((line = Linebuf_getline(lb)) != NULL) 80292555Sdes do_host(line); 80392555Sdes Linebuf_free(lb); 80476259Sgreen } 80592555Sdes } 80692555Sdes 80792555Sdes while (optind < argc) 80892555Sdes do_host(argv[optind++]); 80992555Sdes 81076259Sgreen while (ncon > 0) 81176259Sgreen conloop(); 81276259Sgreen 81376259Sgreen return (0); 81476259Sgreen} 815