ssh-keyscan.c revision 98941
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" 1098675SdesRCSID("$OpenBSD: ssh-keyscan.c,v 1.36 2002/06/16 21:30:58 itojun Exp $"); 1176259Sgreen 1298941Sdes#include "openbsd-compat/fake-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. */ 3498941Sdes#ifdef IPV4_DEFAULT 3598941Sdesint IPv4or6 = AF_INET; 3698941Sdes#else 3798941Sdesint IPv4or6 = AF_UNSPEC; 3898941Sdes#endif 3976259Sgreen 4092555Sdesint ssh_port = SSH_DEFAULT_PORT; 4176259Sgreen 4292555Sdes#define KT_RSA1 1 4392555Sdes#define KT_DSA 2 4492555Sdes#define KT_RSA 4 4592555Sdes 4692555Sdesint get_keytypes = KT_RSA1; /* Get only RSA1 keys by default */ 4792555Sdes 4876259Sgreen#define MAXMAXFD 256 4976259Sgreen 5076259Sgreen/* The number of seconds after which to give up on a TCP connection */ 5176259Sgreenint timeout = 5; 5276259Sgreen 5376259Sgreenint maxfd; 5476259Sgreen#define MAXCON (maxfd - 10) 5576259Sgreen 5698941Sdes#ifdef HAVE___PROGNAME 5776259Sgreenextern char *__progname; 5898941Sdes#else 5998941Sdeschar *__progname; 6098941Sdes#endif 6176259Sgreenfd_set *read_wait; 6276259Sgreensize_t read_wait_size; 6376259Sgreenint ncon; 6492555Sdesint nonfatal_fatal = 0; 6592555Sdesjmp_buf kexjmp; 6692555SdesKey *kexjmp_key; 6776259Sgreen 6876259Sgreen/* 6976259Sgreen * Keep a connection structure for each file descriptor. The state 7076259Sgreen * associated with file descriptor n is held in fdcon[n]. 7176259Sgreen */ 7276259Sgreentypedef struct Connection { 7376259Sgreen u_char c_status; /* State of connection on this file desc. */ 7476259Sgreen#define CS_UNUSED 0 /* File descriptor unused */ 7576259Sgreen#define CS_CON 1 /* Waiting to connect/read greeting */ 7676259Sgreen#define CS_SIZE 2 /* Waiting to read initial packet size */ 7776259Sgreen#define CS_KEYS 3 /* Waiting to read public key packet */ 7876259Sgreen int c_fd; /* Quick lookup: c->c_fd == c - fdcon */ 7976259Sgreen int c_plen; /* Packet length field for ssh packet */ 8076259Sgreen int c_len; /* Total bytes which must be read. */ 8176259Sgreen int c_off; /* Length of data read so far. */ 8292555Sdes int c_keytype; /* Only one of KT_RSA1, KT_DSA, or KT_RSA */ 8376259Sgreen char *c_namebase; /* Address to free for c_name and c_namelist */ 8476259Sgreen char *c_name; /* Hostname of connection for errors */ 8576259Sgreen char *c_namelist; /* Pointer to other possible addresses */ 8676259Sgreen char *c_output_name; /* Hostname of connection for output */ 8776259Sgreen char *c_data; /* Data read from this fd */ 8892555Sdes Kex *c_kex; /* The key-exchange struct for ssh2 */ 8976259Sgreen struct timeval c_tv; /* Time at which connection gets aborted */ 9076259Sgreen TAILQ_ENTRY(Connection) c_link; /* List of connections in timeout order. */ 9176259Sgreen} con; 9276259Sgreen 9376259SgreenTAILQ_HEAD(conlist, Connection) tq; /* Timeout Queue */ 9476259Sgreencon *fdcon; 9576259Sgreen 9676259Sgreen/* 9776259Sgreen * This is just a wrapper around fgets() to make it usable. 9876259Sgreen */ 9976259Sgreen 10076259Sgreen/* Stress-test. Increase this later. */ 10176259Sgreen#define LINEBUF_SIZE 16 10276259Sgreen 10376259Sgreentypedef struct { 10476259Sgreen char *buf; 10576259Sgreen u_int size; 10676259Sgreen int lineno; 10776259Sgreen const char *filename; 10876259Sgreen FILE *stream; 10976259Sgreen void (*errfun) (const char *,...); 11076259Sgreen} Linebuf; 11176259Sgreen 11292555Sdesstatic Linebuf * 11376259SgreenLinebuf_alloc(const char *filename, void (*errfun) (const char *,...)) 11476259Sgreen{ 11576259Sgreen Linebuf *lb; 11676259Sgreen 11776259Sgreen if (!(lb = malloc(sizeof(*lb)))) { 11876259Sgreen if (errfun) 11976259Sgreen (*errfun) ("linebuf (%s): malloc failed\n", lb->filename); 12076259Sgreen return (NULL); 12176259Sgreen } 12276259Sgreen if (filename) { 12376259Sgreen lb->filename = filename; 12476259Sgreen if (!(lb->stream = fopen(filename, "r"))) { 12576259Sgreen xfree(lb); 12676259Sgreen if (errfun) 12776259Sgreen (*errfun) ("%s: %s\n", filename, strerror(errno)); 12876259Sgreen return (NULL); 12976259Sgreen } 13076259Sgreen } else { 13176259Sgreen lb->filename = "(stdin)"; 13276259Sgreen lb->stream = stdin; 13376259Sgreen } 13476259Sgreen 13576259Sgreen if (!(lb->buf = malloc(lb->size = LINEBUF_SIZE))) { 13676259Sgreen if (errfun) 13776259Sgreen (*errfun) ("linebuf (%s): malloc failed\n", lb->filename); 13876259Sgreen xfree(lb); 13976259Sgreen return (NULL); 14076259Sgreen } 14176259Sgreen lb->errfun = errfun; 14276259Sgreen lb->lineno = 0; 14376259Sgreen return (lb); 14476259Sgreen} 14576259Sgreen 14692555Sdesstatic void 14776259SgreenLinebuf_free(Linebuf * lb) 14876259Sgreen{ 14976259Sgreen fclose(lb->stream); 15076259Sgreen xfree(lb->buf); 15176259Sgreen xfree(lb); 15276259Sgreen} 15376259Sgreen 15492555Sdes#if 0 15592555Sdesstatic void 15676259SgreenLinebuf_restart(Linebuf * lb) 15776259Sgreen{ 15876259Sgreen clearerr(lb->stream); 15976259Sgreen rewind(lb->stream); 16076259Sgreen lb->lineno = 0; 16176259Sgreen} 16276259Sgreen 16392555Sdesstatic int 16476259SgreenLinebuf_lineno(Linebuf * lb) 16576259Sgreen{ 16676259Sgreen return (lb->lineno); 16776259Sgreen} 16892555Sdes#endif 16976259Sgreen 17092555Sdesstatic char * 17176259SgreenLinebuf_getline(Linebuf * lb) 17276259Sgreen{ 17376259Sgreen int n = 0; 17476259Sgreen 17576259Sgreen lb->lineno++; 17676259Sgreen for (;;) { 17776259Sgreen /* Read a line */ 17876259Sgreen if (!fgets(&lb->buf[n], lb->size - n, lb->stream)) { 17976259Sgreen if (ferror(lb->stream) && lb->errfun) 18076259Sgreen (*lb->errfun) ("%s: %s\n", lb->filename, 18176259Sgreen strerror(errno)); 18276259Sgreen return (NULL); 18376259Sgreen } 18476259Sgreen n = strlen(lb->buf); 18576259Sgreen 18676259Sgreen /* Return it or an error if it fits */ 18776259Sgreen if (n > 0 && lb->buf[n - 1] == '\n') { 18876259Sgreen lb->buf[n - 1] = '\0'; 18976259Sgreen return (lb->buf); 19076259Sgreen } 19176259Sgreen if (n != lb->size - 1) { 19276259Sgreen if (lb->errfun) 19376259Sgreen (*lb->errfun) ("%s: skipping incomplete last line\n", 19476259Sgreen lb->filename); 19576259Sgreen return (NULL); 19676259Sgreen } 19776259Sgreen /* Double the buffer if we need more space */ 19876259Sgreen if (!(lb->buf = realloc(lb->buf, (lb->size *= 2)))) { 19976259Sgreen if (lb->errfun) 20076259Sgreen (*lb->errfun) ("linebuf (%s): realloc failed\n", 20176259Sgreen lb->filename); 20276259Sgreen return (NULL); 20376259Sgreen } 20476259Sgreen } 20576259Sgreen} 20676259Sgreen 20792555Sdesstatic int 20876259Sgreenfdlim_get(int hard) 20976259Sgreen{ 21098941Sdes#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) 21176259Sgreen struct rlimit rlfd; 21276259Sgreen 21376259Sgreen if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0) 21476259Sgreen return (-1); 21576259Sgreen if ((hard ? rlfd.rlim_max : rlfd.rlim_cur) == RLIM_INFINITY) 21676259Sgreen return 10000; 21776259Sgreen else 21876259Sgreen return hard ? rlfd.rlim_max : rlfd.rlim_cur; 21998941Sdes#elif defined (HAVE_SYSCONF) 22098941Sdes return sysconf (_SC_OPEN_MAX); 22198941Sdes#else 22298941Sdes return 10000; 22398941Sdes#endif 22476259Sgreen} 22576259Sgreen 22692555Sdesstatic int 22776259Sgreenfdlim_set(int lim) 22876259Sgreen{ 22998941Sdes#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) 23076259Sgreen struct rlimit rlfd; 23198941Sdes#endif 23276259Sgreen if (lim <= 0) 23376259Sgreen return (-1); 23498941Sdes#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) 23576259Sgreen if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0) 23676259Sgreen return (-1); 23776259Sgreen rlfd.rlim_cur = lim; 23876259Sgreen if (setrlimit(RLIMIT_NOFILE, &rlfd) < 0) 23976259Sgreen return (-1); 24098941Sdes#elif defined (HAVE_SETDTABLESIZE) 24198941Sdes setdtablesize(lim); 24298941Sdes#endif 24376259Sgreen return (0); 24476259Sgreen} 24576259Sgreen 24676259Sgreen/* 24776259Sgreen * This is an strsep function that returns a null field for adjacent 24876259Sgreen * separators. This is the same as the 4.4BSD strsep, but different from the 24976259Sgreen * one in the GNU libc. 25076259Sgreen */ 25192555Sdesstatic char * 25276259Sgreenxstrsep(char **str, const char *delim) 25376259Sgreen{ 25476259Sgreen char *s, *e; 25576259Sgreen 25676259Sgreen if (!**str) 25776259Sgreen return (NULL); 25876259Sgreen 25976259Sgreen s = *str; 26076259Sgreen e = s + strcspn(s, delim); 26176259Sgreen 26276259Sgreen if (*e != '\0') 26376259Sgreen *e++ = '\0'; 26476259Sgreen *str = e; 26576259Sgreen 26676259Sgreen return (s); 26776259Sgreen} 26876259Sgreen 26976259Sgreen/* 27076259Sgreen * Get the next non-null token (like GNU strsep). Strsep() will return a 27176259Sgreen * null token for two adjacent separators, so we may have to loop. 27276259Sgreen */ 27392555Sdesstatic char * 27476259Sgreenstrnnsep(char **stringp, char *delim) 27576259Sgreen{ 27676259Sgreen char *tok; 27776259Sgreen 27876259Sgreen do { 27976259Sgreen tok = xstrsep(stringp, delim); 28076259Sgreen } while (tok && *tok == '\0'); 28176259Sgreen return (tok); 28276259Sgreen} 28376259Sgreen 28492555Sdesstatic Key * 28592555Sdeskeygrab_ssh1(con *c) 28676259Sgreen{ 28776259Sgreen static Key *rsa; 28876259Sgreen static Buffer msg; 28976259Sgreen 29076259Sgreen if (rsa == NULL) { 29176259Sgreen buffer_init(&msg); 29276259Sgreen rsa = key_new(KEY_RSA1); 29376259Sgreen } 29492555Sdes buffer_append(&msg, c->c_data, c->c_plen); 29592555Sdes buffer_consume(&msg, 8 - (c->c_plen & 7)); /* padding */ 29676259Sgreen if (buffer_get_char(&msg) != (int) SSH_SMSG_PUBLIC_KEY) { 29792555Sdes error("%s: invalid packet type", c->c_name); 29876259Sgreen buffer_clear(&msg); 29992555Sdes return NULL; 30076259Sgreen } 30176259Sgreen buffer_consume(&msg, 8); /* cookie */ 30276259Sgreen 30376259Sgreen /* server key */ 30476259Sgreen (void) buffer_get_int(&msg); 30576259Sgreen buffer_get_bignum(&msg, rsa->rsa->e); 30676259Sgreen buffer_get_bignum(&msg, rsa->rsa->n); 30776259Sgreen 30876259Sgreen /* host key */ 30976259Sgreen (void) buffer_get_int(&msg); 31076259Sgreen buffer_get_bignum(&msg, rsa->rsa->e); 31176259Sgreen buffer_get_bignum(&msg, rsa->rsa->n); 31292555Sdes 31376259Sgreen buffer_clear(&msg); 31476259Sgreen 31592555Sdes return (rsa); 31692555Sdes} 31792555Sdes 31892555Sdesstatic int 31992555Sdeshostjump(Key *hostkey) 32092555Sdes{ 32192555Sdes kexjmp_key = hostkey; 32292555Sdes longjmp(kexjmp, 1); 32392555Sdes} 32492555Sdes 32592555Sdesstatic int 32692555Sdesssh2_capable(int remote_major, int remote_minor) 32792555Sdes{ 32892555Sdes switch (remote_major) { 32992555Sdes case 1: 33092555Sdes if (remote_minor == 99) 33192555Sdes return 1; 33292555Sdes break; 33392555Sdes case 2: 33492555Sdes return 1; 33592555Sdes default: 33692555Sdes break; 33792555Sdes } 33892555Sdes return 0; 33992555Sdes} 34092555Sdes 34192555Sdesstatic Key * 34292555Sdeskeygrab_ssh2(con *c) 34392555Sdes{ 34492555Sdes int j; 34592555Sdes 34692555Sdes packet_set_connection(c->c_fd, c->c_fd); 34792555Sdes enable_compat20(); 34892555Sdes myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = c->c_keytype == KT_DSA? 34992555Sdes "ssh-dss": "ssh-rsa"; 35092555Sdes c->c_kex = kex_setup(myproposal); 35192555Sdes c->c_kex->verify_host_key = hostjump; 35292555Sdes 35392555Sdes if (!(j = setjmp(kexjmp))) { 35492555Sdes nonfatal_fatal = 1; 35592555Sdes dispatch_run(DISPATCH_BLOCK, &c->c_kex->done, c->c_kex); 35692555Sdes fprintf(stderr, "Impossible! dispatch_run() returned!\n"); 35792555Sdes exit(1); 35892555Sdes } 35992555Sdes nonfatal_fatal = 0; 36092555Sdes xfree(c->c_kex); 36192555Sdes c->c_kex = NULL; 36292555Sdes packet_close(); 36392555Sdes 36492555Sdes return j < 0? NULL : kexjmp_key; 36592555Sdes} 36692555Sdes 36792555Sdesstatic void 36892555Sdeskeyprint(con *c, Key *key) 36992555Sdes{ 37092555Sdes if (!key) 37192555Sdes return; 37292555Sdes 37392555Sdes fprintf(stdout, "%s ", c->c_output_name ? c->c_output_name : c->c_name); 37492555Sdes key_write(key, stdout); 37576259Sgreen fputs("\n", stdout); 37676259Sgreen} 37776259Sgreen 37892555Sdesstatic int 37976259Sgreentcpconnect(char *host) 38076259Sgreen{ 38176259Sgreen struct addrinfo hints, *ai, *aitop; 38276259Sgreen char strport[NI_MAXSERV]; 38376259Sgreen int gaierr, s = -1; 38476259Sgreen 38592555Sdes snprintf(strport, sizeof strport, "%d", ssh_port); 38676259Sgreen memset(&hints, 0, sizeof(hints)); 38792555Sdes hints.ai_family = IPv4or6; 38876259Sgreen hints.ai_socktype = SOCK_STREAM; 38976259Sgreen if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) 39076259Sgreen fatal("getaddrinfo %s: %s", host, gai_strerror(gaierr)); 39176259Sgreen for (ai = aitop; ai; ai = ai->ai_next) { 39276259Sgreen s = socket(ai->ai_family, SOCK_STREAM, 0); 39376259Sgreen if (s < 0) { 39476259Sgreen error("socket: %s", strerror(errno)); 39576259Sgreen continue; 39676259Sgreen } 39776259Sgreen if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) 39876259Sgreen fatal("F_SETFL: %s", strerror(errno)); 39976259Sgreen if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0 && 40076259Sgreen errno != EINPROGRESS) 40176259Sgreen error("connect (`%s'): %s", host, strerror(errno)); 40276259Sgreen else 40376259Sgreen break; 40476259Sgreen close(s); 40576259Sgreen s = -1; 40676259Sgreen } 40776259Sgreen freeaddrinfo(aitop); 40876259Sgreen return s; 40976259Sgreen} 41076259Sgreen 41192555Sdesstatic int 41292555Sdesconalloc(char *iname, char *oname, int keytype) 41376259Sgreen{ 41476259Sgreen int s; 41576259Sgreen char *namebase, *name, *namelist; 41676259Sgreen 41776259Sgreen namebase = namelist = xstrdup(iname); 41876259Sgreen 41976259Sgreen do { 42076259Sgreen name = xstrsep(&namelist, ","); 42176259Sgreen if (!name) { 42276259Sgreen xfree(namebase); 42376259Sgreen return (-1); 42476259Sgreen } 42576259Sgreen } while ((s = tcpconnect(name)) < 0); 42676259Sgreen 42776259Sgreen if (s >= maxfd) 42876259Sgreen fatal("conalloc: fdno %d too high", s); 42976259Sgreen if (fdcon[s].c_status) 43076259Sgreen fatal("conalloc: attempt to reuse fdno %d", s); 43176259Sgreen 43276259Sgreen fdcon[s].c_fd = s; 43376259Sgreen fdcon[s].c_status = CS_CON; 43476259Sgreen fdcon[s].c_namebase = namebase; 43576259Sgreen fdcon[s].c_name = name; 43676259Sgreen fdcon[s].c_namelist = namelist; 43776259Sgreen fdcon[s].c_output_name = xstrdup(oname); 43876259Sgreen fdcon[s].c_data = (char *) &fdcon[s].c_plen; 43976259Sgreen fdcon[s].c_len = 4; 44076259Sgreen fdcon[s].c_off = 0; 44192555Sdes fdcon[s].c_keytype = keytype; 44276259Sgreen gettimeofday(&fdcon[s].c_tv, NULL); 44376259Sgreen fdcon[s].c_tv.tv_sec += timeout; 44476259Sgreen TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); 44576259Sgreen FD_SET(s, read_wait); 44676259Sgreen ncon++; 44776259Sgreen return (s); 44876259Sgreen} 44976259Sgreen 45092555Sdesstatic void 45176259Sgreenconfree(int s) 45276259Sgreen{ 45376259Sgreen if (s >= maxfd || fdcon[s].c_status == CS_UNUSED) 45476259Sgreen fatal("confree: attempt to free bad fdno %d", s); 45576259Sgreen close(s); 45676259Sgreen xfree(fdcon[s].c_namebase); 45776259Sgreen xfree(fdcon[s].c_output_name); 45876259Sgreen if (fdcon[s].c_status == CS_KEYS) 45976259Sgreen xfree(fdcon[s].c_data); 46076259Sgreen fdcon[s].c_status = CS_UNUSED; 46192555Sdes fdcon[s].c_keytype = 0; 46276259Sgreen TAILQ_REMOVE(&tq, &fdcon[s], c_link); 46376259Sgreen FD_CLR(s, read_wait); 46476259Sgreen ncon--; 46576259Sgreen} 46676259Sgreen 46792555Sdesstatic void 46876259Sgreencontouch(int s) 46976259Sgreen{ 47076259Sgreen TAILQ_REMOVE(&tq, &fdcon[s], c_link); 47176259Sgreen gettimeofday(&fdcon[s].c_tv, NULL); 47276259Sgreen fdcon[s].c_tv.tv_sec += timeout; 47376259Sgreen TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); 47476259Sgreen} 47576259Sgreen 47692555Sdesstatic int 47776259Sgreenconrecycle(int s) 47876259Sgreen{ 47976259Sgreen int ret; 48076259Sgreen con *c = &fdcon[s]; 48176259Sgreen 48292555Sdes ret = conalloc(c->c_namelist, c->c_output_name, c->c_keytype); 48376259Sgreen confree(s); 48476259Sgreen return (ret); 48576259Sgreen} 48676259Sgreen 48792555Sdesstatic void 48876259Sgreencongreet(int s) 48976259Sgreen{ 49092555Sdes char buf[256], *cp; 49192555Sdes char remote_version[sizeof buf]; 49276259Sgreen size_t bufsiz; 49392555Sdes int remote_major, remote_minor, n = 0; 49476259Sgreen con *c = &fdcon[s]; 49576259Sgreen 49676259Sgreen bufsiz = sizeof(buf); 49776259Sgreen cp = buf; 49892555Sdes while (bufsiz-- && (n = read(s, cp, 1)) == 1 && *cp != '\n') { 49992555Sdes if (*cp == '\r') 50092555Sdes *cp = '\n'; 50176259Sgreen cp++; 50292555Sdes } 50376259Sgreen if (n < 0) { 50476259Sgreen if (errno != ECONNREFUSED) 50576259Sgreen error("read (%s): %s", c->c_name, strerror(errno)); 50676259Sgreen conrecycle(s); 50776259Sgreen return; 50876259Sgreen } 50992555Sdes if (n == 0) { 51092555Sdes error("%s: Connection closed by remote host", c->c_name); 51192555Sdes conrecycle(s); 51292555Sdes return; 51392555Sdes } 51476259Sgreen if (*cp != '\n' && *cp != '\r') { 51576259Sgreen error("%s: bad greeting", c->c_name); 51676259Sgreen confree(s); 51776259Sgreen return; 51876259Sgreen } 51976259Sgreen *cp = '\0'; 52092555Sdes if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", 52192555Sdes &remote_major, &remote_minor, remote_version) == 3) 52292555Sdes compat_datafellows(remote_version); 52392555Sdes else 52492555Sdes datafellows = 0; 52592555Sdes if (c->c_keytype != KT_RSA1) { 52692555Sdes if (!ssh2_capable(remote_major, remote_minor)) { 52792555Sdes debug("%s doesn't support ssh2", c->c_name); 52892555Sdes confree(s); 52992555Sdes return; 53092555Sdes } 53192555Sdes } else if (remote_major != 1) { 53292555Sdes debug("%s doesn't support ssh1", c->c_name); 53392555Sdes confree(s); 53492555Sdes return; 53592555Sdes } 53692555Sdes fprintf(stderr, "# %s %s\n", c->c_name, chop(buf)); 53792555Sdes n = snprintf(buf, sizeof buf, "SSH-%d.%d-OpenSSH-keyscan\r\n", 53892555Sdes c->c_keytype == KT_RSA1? PROTOCOL_MAJOR_1 : PROTOCOL_MAJOR_2, 53992555Sdes c->c_keytype == KT_RSA1? PROTOCOL_MINOR_1 : PROTOCOL_MINOR_2); 54076259Sgreen if (atomicio(write, s, buf, n) != n) { 54176259Sgreen error("write (%s): %s", c->c_name, strerror(errno)); 54276259Sgreen confree(s); 54376259Sgreen return; 54476259Sgreen } 54592555Sdes if (c->c_keytype != KT_RSA1) { 54692555Sdes keyprint(c, keygrab_ssh2(c)); 54792555Sdes confree(s); 54892555Sdes return; 54992555Sdes } 55076259Sgreen c->c_status = CS_SIZE; 55176259Sgreen contouch(s); 55276259Sgreen} 55376259Sgreen 55492555Sdesstatic void 55576259Sgreenconread(int s) 55676259Sgreen{ 55776259Sgreen int n; 55876259Sgreen con *c = &fdcon[s]; 55976259Sgreen 56076259Sgreen if (c->c_status == CS_CON) { 56176259Sgreen congreet(s); 56276259Sgreen return; 56376259Sgreen } 56476259Sgreen n = read(s, c->c_data + c->c_off, c->c_len - c->c_off); 56576259Sgreen if (n < 0) { 56676259Sgreen error("read (%s): %s", c->c_name, strerror(errno)); 56776259Sgreen confree(s); 56876259Sgreen return; 56976259Sgreen } 57076259Sgreen c->c_off += n; 57176259Sgreen 57276259Sgreen if (c->c_off == c->c_len) 57376259Sgreen switch (c->c_status) { 57476259Sgreen case CS_SIZE: 57576259Sgreen c->c_plen = htonl(c->c_plen); 57676259Sgreen c->c_len = c->c_plen + 8 - (c->c_plen & 7); 57776259Sgreen c->c_off = 0; 57876259Sgreen c->c_data = xmalloc(c->c_len); 57976259Sgreen c->c_status = CS_KEYS; 58076259Sgreen break; 58176259Sgreen case CS_KEYS: 58292555Sdes keyprint(c, keygrab_ssh1(c)); 58376259Sgreen confree(s); 58476259Sgreen return; 58576259Sgreen break; 58676259Sgreen default: 58776259Sgreen fatal("conread: invalid status %d", c->c_status); 58876259Sgreen break; 58976259Sgreen } 59076259Sgreen 59176259Sgreen contouch(s); 59276259Sgreen} 59376259Sgreen 59492555Sdesstatic void 59576259Sgreenconloop(void) 59676259Sgreen{ 59776259Sgreen fd_set *r, *e; 59876259Sgreen struct timeval seltime, now; 59976259Sgreen int i; 60076259Sgreen con *c; 60176259Sgreen 60276259Sgreen gettimeofday(&now, NULL); 60398675Sdes c = TAILQ_FIRST(&tq); 60476259Sgreen 60576259Sgreen if (c && (c->c_tv.tv_sec > now.tv_sec || 60676259Sgreen (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec > now.tv_usec))) { 60776259Sgreen seltime = c->c_tv; 60876259Sgreen seltime.tv_sec -= now.tv_sec; 60976259Sgreen seltime.tv_usec -= now.tv_usec; 61076259Sgreen if (seltime.tv_usec < 0) { 61176259Sgreen seltime.tv_usec += 1000000; 61276259Sgreen seltime.tv_sec--; 61376259Sgreen } 61476259Sgreen } else 61576259Sgreen seltime.tv_sec = seltime.tv_usec = 0; 61676259Sgreen 61776259Sgreen r = xmalloc(read_wait_size); 61876259Sgreen memcpy(r, read_wait, read_wait_size); 61976259Sgreen e = xmalloc(read_wait_size); 62076259Sgreen memcpy(e, read_wait, read_wait_size); 62176259Sgreen 62276259Sgreen while (select(maxfd, r, NULL, e, &seltime) == -1 && 62376259Sgreen (errno == EAGAIN || errno == EINTR)) 62476259Sgreen ; 62576259Sgreen 62676259Sgreen for (i = 0; i < maxfd; i++) { 62776259Sgreen if (FD_ISSET(i, e)) { 62876259Sgreen error("%s: exception!", fdcon[i].c_name); 62976259Sgreen confree(i); 63076259Sgreen } else if (FD_ISSET(i, r)) 63176259Sgreen conread(i); 63276259Sgreen } 63376259Sgreen xfree(r); 63476259Sgreen xfree(e); 63576259Sgreen 63698675Sdes c = TAILQ_FIRST(&tq); 63776259Sgreen while (c && (c->c_tv.tv_sec < now.tv_sec || 63876259Sgreen (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec < now.tv_usec))) { 63976259Sgreen int s = c->c_fd; 64076259Sgreen 64198675Sdes c = TAILQ_NEXT(c, c_link); 64276259Sgreen conrecycle(s); 64376259Sgreen } 64476259Sgreen} 64576259Sgreen 64692555Sdesstatic void 64792555Sdesdo_host(char *host) 64876259Sgreen{ 64992555Sdes char *name = strnnsep(&host, " \t\n"); 65092555Sdes int j; 65176259Sgreen 65292555Sdes if (name == NULL) 65392555Sdes return; 65492555Sdes for (j = KT_RSA1; j <= KT_RSA; j *= 2) { 65592555Sdes if (get_keytypes & j) { 65692555Sdes while (ncon >= MAXCON) 65792555Sdes conloop(); 65892555Sdes conalloc(name, *host ? host : name, j); 65976259Sgreen } 66076259Sgreen } 66176259Sgreen} 66276259Sgreen 66376259Sgreenvoid 66492555Sdesfatal(const char *fmt,...) 66592555Sdes{ 66692555Sdes va_list args; 66792555Sdes va_start(args, fmt); 66892555Sdes do_log(SYSLOG_LEVEL_FATAL, fmt, args); 66992555Sdes va_end(args); 67092555Sdes if (nonfatal_fatal) 67192555Sdes longjmp(kexjmp, -1); 67292555Sdes else 67392555Sdes fatal_cleanup(); 67492555Sdes} 67592555Sdes 67692555Sdesstatic void 67776259Sgreenusage(void) 67876259Sgreen{ 67992555Sdes fprintf(stderr, "Usage: %s [options] host ...\n", 68092555Sdes __progname); 68192555Sdes fprintf(stderr, "Options:\n"); 68292555Sdes fprintf(stderr, " -f file Read hosts or addresses from file.\n"); 68392555Sdes fprintf(stderr, " -p port Connect to the specified port.\n"); 68492555Sdes fprintf(stderr, " -t keytype Specify the host key type.\n"); 68592555Sdes fprintf(stderr, " -T timeout Set connection timeout.\n"); 68692555Sdes fprintf(stderr, " -v Verbose; display verbose debugging messages.\n"); 68792555Sdes fprintf(stderr, " -4 Use IPv4 only.\n"); 68892555Sdes fprintf(stderr, " -6 Use IPv6 only.\n"); 68992555Sdes exit(1); 69076259Sgreen} 69176259Sgreen 69276259Sgreenint 69376259Sgreenmain(int argc, char **argv) 69476259Sgreen{ 69592555Sdes int debug_flag = 0, log_level = SYSLOG_LEVEL_INFO; 69692555Sdes int opt, fopt_count = 0; 69792555Sdes char *tname; 69876259Sgreen 69992555Sdes extern int optind; 70092555Sdes extern char *optarg; 70192555Sdes 70298941Sdes __progname = get_progname(argv[0]); 70398941Sdes init_rng(); 70498941Sdes seed_rng(); 70576259Sgreen TAILQ_INIT(&tq); 70676259Sgreen 70792555Sdes if (argc <= 1) 70876259Sgreen usage(); 70976259Sgreen 71092555Sdes while ((opt = getopt(argc, argv, "v46p:T:t:f:")) != -1) { 71192555Sdes switch (opt) { 71292555Sdes case 'p': 71392555Sdes ssh_port = a2port(optarg); 71492555Sdes if (ssh_port == 0) { 71592555Sdes fprintf(stderr, "Bad port '%s'\n", optarg); 71692555Sdes exit(1); 71792555Sdes } 71892555Sdes break; 71992555Sdes case 'T': 72092555Sdes timeout = atoi(optarg); 72192555Sdes if (timeout <= 0) 72276259Sgreen usage(); 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