ssh-keyscan.c revision 137019
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" 10137019SdesRCSID("$OpenBSD: ssh-keyscan.c,v 1.50 2004/08/11 21:44:32 avsm 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 5276259Sgreenextern char *__progname; 5376259Sgreenfd_set *read_wait; 5476259Sgreensize_t read_wait_size; 5576259Sgreenint ncon; 5692555Sdesint nonfatal_fatal = 0; 5792555Sdesjmp_buf kexjmp; 5892555SdesKey *kexjmp_key; 5976259Sgreen 6076259Sgreen/* 6176259Sgreen * Keep a connection structure for each file descriptor. The state 6276259Sgreen * associated with file descriptor n is held in fdcon[n]. 6376259Sgreen */ 6476259Sgreentypedef struct Connection { 6576259Sgreen u_char c_status; /* State of connection on this file desc. */ 6676259Sgreen#define CS_UNUSED 0 /* File descriptor unused */ 6776259Sgreen#define CS_CON 1 /* Waiting to connect/read greeting */ 6876259Sgreen#define CS_SIZE 2 /* Waiting to read initial packet size */ 6976259Sgreen#define CS_KEYS 3 /* Waiting to read public key packet */ 7076259Sgreen int c_fd; /* Quick lookup: c->c_fd == c - fdcon */ 7176259Sgreen int c_plen; /* Packet length field for ssh packet */ 7276259Sgreen int c_len; /* Total bytes which must be read. */ 7376259Sgreen int c_off; /* Length of data read so far. */ 7492555Sdes int c_keytype; /* Only one of KT_RSA1, KT_DSA, or KT_RSA */ 7576259Sgreen char *c_namebase; /* Address to free for c_name and c_namelist */ 7676259Sgreen char *c_name; /* Hostname of connection for errors */ 7776259Sgreen char *c_namelist; /* Pointer to other possible addresses */ 7876259Sgreen char *c_output_name; /* Hostname of connection for output */ 7976259Sgreen char *c_data; /* Data read from this fd */ 8092555Sdes Kex *c_kex; /* The key-exchange struct for ssh2 */ 8176259Sgreen struct timeval c_tv; /* Time at which connection gets aborted */ 8276259Sgreen TAILQ_ENTRY(Connection) c_link; /* List of connections in timeout order. */ 8376259Sgreen} con; 8476259Sgreen 8576259SgreenTAILQ_HEAD(conlist, Connection) tq; /* Timeout Queue */ 8676259Sgreencon *fdcon; 8776259Sgreen 8876259Sgreen/* 8976259Sgreen * This is just a wrapper around fgets() to make it usable. 9076259Sgreen */ 9176259Sgreen 9276259Sgreen/* Stress-test. Increase this later. */ 9376259Sgreen#define LINEBUF_SIZE 16 9476259Sgreen 9576259Sgreentypedef struct { 9676259Sgreen char *buf; 9776259Sgreen u_int size; 9876259Sgreen int lineno; 9976259Sgreen const char *filename; 10076259Sgreen FILE *stream; 10176259Sgreen void (*errfun) (const char *,...); 10276259Sgreen} Linebuf; 10376259Sgreen 10492555Sdesstatic Linebuf * 10576259SgreenLinebuf_alloc(const char *filename, void (*errfun) (const char *,...)) 10676259Sgreen{ 10776259Sgreen Linebuf *lb; 10876259Sgreen 10976259Sgreen if (!(lb = malloc(sizeof(*lb)))) { 11076259Sgreen if (errfun) 111106130Sdes (*errfun) ("linebuf (%s): malloc failed\n", 112106130Sdes filename ? filename : "(stdin)"); 11376259Sgreen return (NULL); 11476259Sgreen } 11576259Sgreen if (filename) { 11676259Sgreen lb->filename = filename; 11776259Sgreen if (!(lb->stream = fopen(filename, "r"))) { 11876259Sgreen xfree(lb); 11976259Sgreen if (errfun) 12076259Sgreen (*errfun) ("%s: %s\n", filename, strerror(errno)); 12176259Sgreen return (NULL); 12276259Sgreen } 12376259Sgreen } else { 12476259Sgreen lb->filename = "(stdin)"; 12576259Sgreen lb->stream = stdin; 12676259Sgreen } 12776259Sgreen 12876259Sgreen if (!(lb->buf = malloc(lb->size = LINEBUF_SIZE))) { 12976259Sgreen if (errfun) 13076259Sgreen (*errfun) ("linebuf (%s): malloc failed\n", lb->filename); 13176259Sgreen xfree(lb); 13276259Sgreen return (NULL); 13376259Sgreen } 13476259Sgreen lb->errfun = errfun; 13576259Sgreen lb->lineno = 0; 13676259Sgreen return (lb); 13776259Sgreen} 13876259Sgreen 13992555Sdesstatic void 14076259SgreenLinebuf_free(Linebuf * lb) 14176259Sgreen{ 14276259Sgreen fclose(lb->stream); 14376259Sgreen xfree(lb->buf); 14476259Sgreen xfree(lb); 14576259Sgreen} 14676259Sgreen 14792555Sdes#if 0 14892555Sdesstatic void 14976259SgreenLinebuf_restart(Linebuf * lb) 15076259Sgreen{ 15176259Sgreen clearerr(lb->stream); 15276259Sgreen rewind(lb->stream); 15376259Sgreen lb->lineno = 0; 15476259Sgreen} 15576259Sgreen 15692555Sdesstatic int 15776259SgreenLinebuf_lineno(Linebuf * lb) 15876259Sgreen{ 15976259Sgreen return (lb->lineno); 16076259Sgreen} 16192555Sdes#endif 16276259Sgreen 16392555Sdesstatic char * 16476259SgreenLinebuf_getline(Linebuf * lb) 16576259Sgreen{ 16676259Sgreen int n = 0; 167106130Sdes void *p; 16876259Sgreen 16976259Sgreen lb->lineno++; 17076259Sgreen for (;;) { 17176259Sgreen /* Read a line */ 17276259Sgreen if (!fgets(&lb->buf[n], lb->size - n, lb->stream)) { 17376259Sgreen if (ferror(lb->stream) && lb->errfun) 174106130Sdes (*lb->errfun)("%s: %s\n", lb->filename, 17576259Sgreen strerror(errno)); 17676259Sgreen return (NULL); 17776259Sgreen } 17876259Sgreen n = strlen(lb->buf); 17976259Sgreen 18076259Sgreen /* Return it or an error if it fits */ 18176259Sgreen if (n > 0 && lb->buf[n - 1] == '\n') { 18276259Sgreen lb->buf[n - 1] = '\0'; 18376259Sgreen return (lb->buf); 18476259Sgreen } 18576259Sgreen if (n != lb->size - 1) { 18676259Sgreen if (lb->errfun) 187106130Sdes (*lb->errfun)("%s: skipping incomplete last line\n", 18876259Sgreen lb->filename); 18976259Sgreen return (NULL); 19076259Sgreen } 19176259Sgreen /* Double the buffer if we need more space */ 192106130Sdes lb->size *= 2; 193106130Sdes if ((p = realloc(lb->buf, lb->size)) == NULL) { 194106130Sdes lb->size /= 2; 19576259Sgreen if (lb->errfun) 196106130Sdes (*lb->errfun)("linebuf (%s): realloc failed\n", 19776259Sgreen lb->filename); 19876259Sgreen return (NULL); 19976259Sgreen } 200106130Sdes lb->buf = p; 20176259Sgreen } 20276259Sgreen} 20376259Sgreen 20492555Sdesstatic int 20576259Sgreenfdlim_get(int hard) 20676259Sgreen{ 20798941Sdes#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) 20876259Sgreen struct rlimit rlfd; 20976259Sgreen 21076259Sgreen if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0) 21176259Sgreen return (-1); 21276259Sgreen if ((hard ? rlfd.rlim_max : rlfd.rlim_cur) == RLIM_INFINITY) 213126277Sdes return SSH_SYSFDMAX; 21476259Sgreen else 21576259Sgreen return hard ? rlfd.rlim_max : rlfd.rlim_cur; 21698941Sdes#else 217126277Sdes return SSH_SYSFDMAX; 21898941Sdes#endif 21976259Sgreen} 22076259Sgreen 22192555Sdesstatic int 22276259Sgreenfdlim_set(int lim) 22376259Sgreen{ 22498941Sdes#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) 22576259Sgreen struct rlimit rlfd; 22698941Sdes#endif 227106130Sdes 22876259Sgreen if (lim <= 0) 22976259Sgreen return (-1); 23098941Sdes#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) 23176259Sgreen if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0) 23276259Sgreen return (-1); 23376259Sgreen rlfd.rlim_cur = lim; 23476259Sgreen if (setrlimit(RLIMIT_NOFILE, &rlfd) < 0) 23576259Sgreen return (-1); 23698941Sdes#elif defined (HAVE_SETDTABLESIZE) 23798941Sdes setdtablesize(lim); 23898941Sdes#endif 23976259Sgreen return (0); 24076259Sgreen} 24176259Sgreen 24276259Sgreen/* 24376259Sgreen * This is an strsep function that returns a null field for adjacent 24476259Sgreen * separators. This is the same as the 4.4BSD strsep, but different from the 24576259Sgreen * one in the GNU libc. 24676259Sgreen */ 24792555Sdesstatic char * 24876259Sgreenxstrsep(char **str, const char *delim) 24976259Sgreen{ 25076259Sgreen char *s, *e; 25176259Sgreen 25276259Sgreen if (!**str) 25376259Sgreen return (NULL); 25476259Sgreen 25576259Sgreen s = *str; 25676259Sgreen e = s + strcspn(s, delim); 25776259Sgreen 25876259Sgreen if (*e != '\0') 25976259Sgreen *e++ = '\0'; 26076259Sgreen *str = e; 26176259Sgreen 26276259Sgreen return (s); 26376259Sgreen} 26476259Sgreen 26576259Sgreen/* 26676259Sgreen * Get the next non-null token (like GNU strsep). Strsep() will return a 26776259Sgreen * null token for two adjacent separators, so we may have to loop. 26876259Sgreen */ 26992555Sdesstatic char * 27076259Sgreenstrnnsep(char **stringp, char *delim) 27176259Sgreen{ 27276259Sgreen char *tok; 27376259Sgreen 27476259Sgreen do { 27576259Sgreen tok = xstrsep(stringp, delim); 27676259Sgreen } while (tok && *tok == '\0'); 27776259Sgreen return (tok); 27876259Sgreen} 27976259Sgreen 28092555Sdesstatic Key * 28192555Sdeskeygrab_ssh1(con *c) 28276259Sgreen{ 28376259Sgreen static Key *rsa; 28476259Sgreen static Buffer msg; 28576259Sgreen 28676259Sgreen if (rsa == NULL) { 28776259Sgreen buffer_init(&msg); 28876259Sgreen rsa = key_new(KEY_RSA1); 28976259Sgreen } 29092555Sdes buffer_append(&msg, c->c_data, c->c_plen); 29192555Sdes buffer_consume(&msg, 8 - (c->c_plen & 7)); /* padding */ 29276259Sgreen if (buffer_get_char(&msg) != (int) SSH_SMSG_PUBLIC_KEY) { 29392555Sdes error("%s: invalid packet type", c->c_name); 29476259Sgreen buffer_clear(&msg); 29592555Sdes return NULL; 29676259Sgreen } 29776259Sgreen buffer_consume(&msg, 8); /* cookie */ 29876259Sgreen 29976259Sgreen /* server key */ 30076259Sgreen (void) buffer_get_int(&msg); 30176259Sgreen buffer_get_bignum(&msg, rsa->rsa->e); 30276259Sgreen buffer_get_bignum(&msg, rsa->rsa->n); 30376259Sgreen 30476259Sgreen /* host key */ 30576259Sgreen (void) buffer_get_int(&msg); 30676259Sgreen buffer_get_bignum(&msg, rsa->rsa->e); 30776259Sgreen buffer_get_bignum(&msg, rsa->rsa->n); 30892555Sdes 30976259Sgreen buffer_clear(&msg); 31076259Sgreen 31192555Sdes return (rsa); 31292555Sdes} 31392555Sdes 31492555Sdesstatic int 31592555Sdeshostjump(Key *hostkey) 31692555Sdes{ 31792555Sdes kexjmp_key = hostkey; 31892555Sdes longjmp(kexjmp, 1); 31992555Sdes} 32092555Sdes 32192555Sdesstatic int 32292555Sdesssh2_capable(int remote_major, int remote_minor) 32392555Sdes{ 32492555Sdes switch (remote_major) { 32592555Sdes case 1: 32692555Sdes if (remote_minor == 99) 32792555Sdes return 1; 32892555Sdes break; 32992555Sdes case 2: 33092555Sdes return 1; 33192555Sdes default: 33292555Sdes break; 33392555Sdes } 33492555Sdes return 0; 33592555Sdes} 33692555Sdes 33792555Sdesstatic Key * 33892555Sdeskeygrab_ssh2(con *c) 33992555Sdes{ 34092555Sdes int j; 34192555Sdes 34292555Sdes packet_set_connection(c->c_fd, c->c_fd); 34392555Sdes enable_compat20(); 34492555Sdes myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = c->c_keytype == KT_DSA? 34592555Sdes "ssh-dss": "ssh-rsa"; 34692555Sdes c->c_kex = kex_setup(myproposal); 347113911Sdes c->c_kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; 348137019Sdes c->c_kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; 349113911Sdes c->c_kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; 35092555Sdes c->c_kex->verify_host_key = hostjump; 35192555Sdes 35292555Sdes if (!(j = setjmp(kexjmp))) { 35392555Sdes nonfatal_fatal = 1; 35492555Sdes dispatch_run(DISPATCH_BLOCK, &c->c_kex->done, c->c_kex); 35592555Sdes fprintf(stderr, "Impossible! dispatch_run() returned!\n"); 35692555Sdes exit(1); 35792555Sdes } 35892555Sdes nonfatal_fatal = 0; 35992555Sdes xfree(c->c_kex); 36092555Sdes c->c_kex = NULL; 36192555Sdes packet_close(); 36292555Sdes 36392555Sdes return j < 0? NULL : kexjmp_key; 36492555Sdes} 36592555Sdes 36692555Sdesstatic void 36792555Sdeskeyprint(con *c, Key *key) 36892555Sdes{ 36992555Sdes if (!key) 37092555Sdes return; 37192555Sdes 37292555Sdes fprintf(stdout, "%s ", c->c_output_name ? c->c_output_name : c->c_name); 37392555Sdes key_write(key, stdout); 37476259Sgreen fputs("\n", stdout); 37576259Sgreen} 37676259Sgreen 37792555Sdesstatic int 37876259Sgreentcpconnect(char *host) 37976259Sgreen{ 38076259Sgreen struct addrinfo hints, *ai, *aitop; 38176259Sgreen char strport[NI_MAXSERV]; 38276259Sgreen int gaierr, s = -1; 38376259Sgreen 38492555Sdes snprintf(strport, sizeof strport, "%d", ssh_port); 38576259Sgreen memset(&hints, 0, sizeof(hints)); 38692555Sdes hints.ai_family = IPv4or6; 38776259Sgreen hints.ai_socktype = SOCK_STREAM; 38876259Sgreen if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) 38976259Sgreen fatal("getaddrinfo %s: %s", host, gai_strerror(gaierr)); 39076259Sgreen for (ai = aitop; ai; ai = ai->ai_next) { 391124211Sdes s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 39276259Sgreen if (s < 0) { 39376259Sgreen error("socket: %s", strerror(errno)); 39476259Sgreen continue; 39576259Sgreen } 396137019Sdes if (set_nonblock(s) == -1) 397137019Sdes fatal("%s: set_nonblock(%d)", __func__, s); 39876259Sgreen if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0 && 39976259Sgreen errno != EINPROGRESS) 40076259Sgreen error("connect (`%s'): %s", host, strerror(errno)); 40176259Sgreen else 40276259Sgreen break; 40376259Sgreen close(s); 40476259Sgreen s = -1; 40576259Sgreen } 40676259Sgreen freeaddrinfo(aitop); 40776259Sgreen return s; 40876259Sgreen} 40976259Sgreen 41092555Sdesstatic int 41192555Sdesconalloc(char *iname, char *oname, int keytype) 41276259Sgreen{ 413106130Sdes char *namebase, *name, *namelist; 41476259Sgreen int s; 41576259Sgreen 41676259Sgreen namebase = namelist = xstrdup(iname); 41776259Sgreen 41876259Sgreen do { 41976259Sgreen name = xstrsep(&namelist, ","); 42076259Sgreen if (!name) { 42176259Sgreen xfree(namebase); 42276259Sgreen return (-1); 42376259Sgreen } 42476259Sgreen } while ((s = tcpconnect(name)) < 0); 42576259Sgreen 42676259Sgreen if (s >= maxfd) 42776259Sgreen fatal("conalloc: fdno %d too high", s); 42876259Sgreen if (fdcon[s].c_status) 42976259Sgreen fatal("conalloc: attempt to reuse fdno %d", s); 43076259Sgreen 43176259Sgreen fdcon[s].c_fd = s; 43276259Sgreen fdcon[s].c_status = CS_CON; 43376259Sgreen fdcon[s].c_namebase = namebase; 43476259Sgreen fdcon[s].c_name = name; 43576259Sgreen fdcon[s].c_namelist = namelist; 43676259Sgreen fdcon[s].c_output_name = xstrdup(oname); 43776259Sgreen fdcon[s].c_data = (char *) &fdcon[s].c_plen; 43876259Sgreen fdcon[s].c_len = 4; 43976259Sgreen fdcon[s].c_off = 0; 44092555Sdes fdcon[s].c_keytype = keytype; 44176259Sgreen gettimeofday(&fdcon[s].c_tv, NULL); 44276259Sgreen fdcon[s].c_tv.tv_sec += timeout; 44376259Sgreen TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); 44476259Sgreen FD_SET(s, read_wait); 44576259Sgreen ncon++; 44676259Sgreen return (s); 44776259Sgreen} 44876259Sgreen 44992555Sdesstatic void 45076259Sgreenconfree(int s) 45176259Sgreen{ 45276259Sgreen if (s >= maxfd || fdcon[s].c_status == CS_UNUSED) 45376259Sgreen fatal("confree: attempt to free bad fdno %d", s); 45476259Sgreen close(s); 45576259Sgreen xfree(fdcon[s].c_namebase); 45676259Sgreen xfree(fdcon[s].c_output_name); 45776259Sgreen if (fdcon[s].c_status == CS_KEYS) 45876259Sgreen xfree(fdcon[s].c_data); 45976259Sgreen fdcon[s].c_status = CS_UNUSED; 46092555Sdes fdcon[s].c_keytype = 0; 46176259Sgreen TAILQ_REMOVE(&tq, &fdcon[s], c_link); 46276259Sgreen FD_CLR(s, read_wait); 46376259Sgreen ncon--; 46476259Sgreen} 46576259Sgreen 46692555Sdesstatic void 46776259Sgreencontouch(int s) 46876259Sgreen{ 46976259Sgreen TAILQ_REMOVE(&tq, &fdcon[s], c_link); 47076259Sgreen gettimeofday(&fdcon[s].c_tv, NULL); 47176259Sgreen fdcon[s].c_tv.tv_sec += timeout; 47276259Sgreen TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); 47376259Sgreen} 47476259Sgreen 47592555Sdesstatic int 47676259Sgreenconrecycle(int s) 47776259Sgreen{ 478106130Sdes con *c = &fdcon[s]; 47976259Sgreen int ret; 48076259Sgreen 48192555Sdes ret = conalloc(c->c_namelist, c->c_output_name, c->c_keytype); 48276259Sgreen confree(s); 48376259Sgreen return (ret); 48476259Sgreen} 48576259Sgreen 48692555Sdesstatic void 48776259Sgreencongreet(int s) 48876259Sgreen{ 489128460Sdes int remote_major = 0, remote_minor = 0, n = 0; 49092555Sdes char buf[256], *cp; 49192555Sdes char remote_version[sizeof buf]; 49276259Sgreen size_t bufsiz; 49376259Sgreen con *c = &fdcon[s]; 49476259Sgreen 49576259Sgreen bufsiz = sizeof(buf); 49676259Sgreen cp = buf; 497137019Sdes while (bufsiz-- && (n = atomicio(read, s, cp, 1)) == 1 && *cp != '\n') { 49892555Sdes if (*cp == '\r') 49992555Sdes *cp = '\n'; 50076259Sgreen cp++; 50192555Sdes } 50276259Sgreen if (n < 0) { 50376259Sgreen if (errno != ECONNREFUSED) 50476259Sgreen error("read (%s): %s", c->c_name, strerror(errno)); 50576259Sgreen conrecycle(s); 50676259Sgreen return; 50776259Sgreen } 50892555Sdes if (n == 0) { 50992555Sdes error("%s: Connection closed by remote host", c->c_name); 51092555Sdes conrecycle(s); 51192555Sdes return; 51292555Sdes } 51376259Sgreen if (*cp != '\n' && *cp != '\r') { 51476259Sgreen error("%s: bad greeting", c->c_name); 51576259Sgreen confree(s); 51676259Sgreen return; 51776259Sgreen } 51876259Sgreen *cp = '\0'; 51992555Sdes if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", 52092555Sdes &remote_major, &remote_minor, remote_version) == 3) 52192555Sdes compat_datafellows(remote_version); 52292555Sdes else 52392555Sdes datafellows = 0; 52492555Sdes if (c->c_keytype != KT_RSA1) { 52592555Sdes if (!ssh2_capable(remote_major, remote_minor)) { 52692555Sdes debug("%s doesn't support ssh2", c->c_name); 52792555Sdes confree(s); 52892555Sdes return; 52992555Sdes } 53092555Sdes } else if (remote_major != 1) { 53192555Sdes debug("%s doesn't support ssh1", c->c_name); 53292555Sdes confree(s); 53392555Sdes return; 53492555Sdes } 53592555Sdes fprintf(stderr, "# %s %s\n", c->c_name, chop(buf)); 53692555Sdes n = snprintf(buf, sizeof buf, "SSH-%d.%d-OpenSSH-keyscan\r\n", 53792555Sdes c->c_keytype == KT_RSA1? PROTOCOL_MAJOR_1 : PROTOCOL_MAJOR_2, 53892555Sdes c->c_keytype == KT_RSA1? PROTOCOL_MINOR_1 : PROTOCOL_MINOR_2); 539124211Sdes if (atomicio(vwrite, s, buf, n) != n) { 54076259Sgreen error("write (%s): %s", c->c_name, strerror(errno)); 54176259Sgreen confree(s); 54276259Sgreen return; 54376259Sgreen } 54492555Sdes if (c->c_keytype != KT_RSA1) { 54592555Sdes keyprint(c, keygrab_ssh2(c)); 54692555Sdes confree(s); 54792555Sdes return; 54892555Sdes } 54976259Sgreen c->c_status = CS_SIZE; 55076259Sgreen contouch(s); 55176259Sgreen} 55276259Sgreen 55392555Sdesstatic void 55476259Sgreenconread(int s) 55576259Sgreen{ 556106130Sdes con *c = &fdcon[s]; 55776259Sgreen int n; 55876259Sgreen 55976259Sgreen if (c->c_status == CS_CON) { 56076259Sgreen congreet(s); 56176259Sgreen return; 56276259Sgreen } 563137019Sdes n = atomicio(read, s, c->c_data + c->c_off, c->c_len - c->c_off); 56476259Sgreen if (n < 0) { 56576259Sgreen error("read (%s): %s", c->c_name, strerror(errno)); 56676259Sgreen confree(s); 56776259Sgreen return; 56876259Sgreen } 56976259Sgreen c->c_off += n; 57076259Sgreen 57176259Sgreen if (c->c_off == c->c_len) 57276259Sgreen switch (c->c_status) { 57376259Sgreen case CS_SIZE: 57476259Sgreen c->c_plen = htonl(c->c_plen); 57576259Sgreen c->c_len = c->c_plen + 8 - (c->c_plen & 7); 57676259Sgreen c->c_off = 0; 57776259Sgreen c->c_data = xmalloc(c->c_len); 57876259Sgreen c->c_status = CS_KEYS; 57976259Sgreen break; 58076259Sgreen case CS_KEYS: 58192555Sdes keyprint(c, keygrab_ssh1(c)); 58276259Sgreen confree(s); 58376259Sgreen return; 58476259Sgreen break; 58576259Sgreen default: 58676259Sgreen fatal("conread: invalid status %d", c->c_status); 58776259Sgreen break; 58876259Sgreen } 58976259Sgreen 59076259Sgreen contouch(s); 59176259Sgreen} 59276259Sgreen 59392555Sdesstatic void 59476259Sgreenconloop(void) 59576259Sgreen{ 596106130Sdes struct timeval seltime, now; 59776259Sgreen fd_set *r, *e; 598106130Sdes con *c; 59976259Sgreen int i; 60076259Sgreen 60176259Sgreen gettimeofday(&now, NULL); 60298675Sdes c = TAILQ_FIRST(&tq); 60376259Sgreen 60476259Sgreen if (c && (c->c_tv.tv_sec > now.tv_sec || 60576259Sgreen (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec > now.tv_usec))) { 60676259Sgreen seltime = c->c_tv; 60776259Sgreen seltime.tv_sec -= now.tv_sec; 60876259Sgreen seltime.tv_usec -= now.tv_usec; 60976259Sgreen if (seltime.tv_usec < 0) { 61076259Sgreen seltime.tv_usec += 1000000; 61176259Sgreen seltime.tv_sec--; 61276259Sgreen } 61376259Sgreen } else 61476259Sgreen seltime.tv_sec = seltime.tv_usec = 0; 61576259Sgreen 61676259Sgreen r = xmalloc(read_wait_size); 61776259Sgreen memcpy(r, read_wait, read_wait_size); 61876259Sgreen e = xmalloc(read_wait_size); 61976259Sgreen memcpy(e, read_wait, read_wait_size); 62076259Sgreen 62176259Sgreen while (select(maxfd, r, NULL, e, &seltime) == -1 && 62276259Sgreen (errno == EAGAIN || errno == EINTR)) 62376259Sgreen ; 62476259Sgreen 62576259Sgreen for (i = 0; i < maxfd; i++) { 62676259Sgreen if (FD_ISSET(i, e)) { 62776259Sgreen error("%s: exception!", fdcon[i].c_name); 62876259Sgreen confree(i); 62976259Sgreen } else if (FD_ISSET(i, r)) 63076259Sgreen conread(i); 63176259Sgreen } 63276259Sgreen xfree(r); 63376259Sgreen xfree(e); 63476259Sgreen 63598675Sdes c = TAILQ_FIRST(&tq); 63676259Sgreen while (c && (c->c_tv.tv_sec < now.tv_sec || 63776259Sgreen (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec < now.tv_usec))) { 63876259Sgreen int s = c->c_fd; 63976259Sgreen 64098675Sdes c = TAILQ_NEXT(c, c_link); 64176259Sgreen conrecycle(s); 64276259Sgreen } 64376259Sgreen} 64476259Sgreen 64592555Sdesstatic void 64692555Sdesdo_host(char *host) 64776259Sgreen{ 64892555Sdes char *name = strnnsep(&host, " \t\n"); 64992555Sdes int j; 65076259Sgreen 65192555Sdes if (name == NULL) 65292555Sdes return; 65392555Sdes for (j = KT_RSA1; j <= KT_RSA; j *= 2) { 65492555Sdes if (get_keytypes & j) { 65592555Sdes while (ncon >= MAXCON) 65692555Sdes conloop(); 65792555Sdes conalloc(name, *host ? host : name, j); 65876259Sgreen } 65976259Sgreen } 66076259Sgreen} 66176259Sgreen 66276259Sgreenvoid 66392555Sdesfatal(const char *fmt,...) 66492555Sdes{ 66592555Sdes va_list args; 666106130Sdes 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 673126277Sdes exit(255); 67492555Sdes} 67592555Sdes 67692555Sdesstatic void 67776259Sgreenusage(void) 67876259Sgreen{ 679124211Sdes fprintf(stderr, "usage: %s [-v46] [-p port] [-T timeout] [-t type] [-f file]\n" 680106130Sdes "\t\t [host | addrlist namelist] [...]\n", 68192555Sdes __progname); 68292555Sdes exit(1); 68376259Sgreen} 68476259Sgreen 68576259Sgreenint 68676259Sgreenmain(int argc, char **argv) 68776259Sgreen{ 68892555Sdes int debug_flag = 0, log_level = SYSLOG_LEVEL_INFO; 68992555Sdes int opt, fopt_count = 0; 69092555Sdes char *tname; 69176259Sgreen 69292555Sdes extern int optind; 69392555Sdes extern char *optarg; 69492555Sdes 695124211Sdes __progname = ssh_get_progname(argv[0]); 69698941Sdes init_rng(); 69798941Sdes seed_rng(); 69876259Sgreen TAILQ_INIT(&tq); 69976259Sgreen 70092555Sdes if (argc <= 1) 70176259Sgreen usage(); 70276259Sgreen 70392555Sdes while ((opt = getopt(argc, argv, "v46p:T:t:f:")) != -1) { 70492555Sdes switch (opt) { 70592555Sdes case 'p': 70692555Sdes ssh_port = a2port(optarg); 70792555Sdes if (ssh_port == 0) { 70892555Sdes fprintf(stderr, "Bad port '%s'\n", optarg); 70992555Sdes exit(1); 71092555Sdes } 71192555Sdes break; 71292555Sdes case 'T': 713106130Sdes timeout = convtime(optarg); 714106130Sdes if (timeout == -1 || timeout == 0) { 715106130Sdes fprintf(stderr, "Bad timeout '%s'\n", optarg); 71676259Sgreen usage(); 717106130Sdes } 71892555Sdes break; 71992555Sdes case 'v': 72092555Sdes if (!debug_flag) { 72192555Sdes debug_flag = 1; 72292555Sdes log_level = SYSLOG_LEVEL_DEBUG1; 72392555Sdes } 72492555Sdes else if (log_level < SYSLOG_LEVEL_DEBUG3) 72592555Sdes log_level++; 72692555Sdes else 72792555Sdes fatal("Too high debugging level."); 72892555Sdes break; 72992555Sdes case 'f': 73092555Sdes if (strcmp(optarg, "-") == 0) 73192555Sdes optarg = NULL; 73292555Sdes argv[fopt_count++] = optarg; 73392555Sdes break; 73492555Sdes case 't': 73592555Sdes get_keytypes = 0; 73692555Sdes tname = strtok(optarg, ","); 73792555Sdes while (tname) { 73892555Sdes int type = key_type_from_name(tname); 73992555Sdes switch (type) { 74092555Sdes case KEY_RSA1: 74192555Sdes get_keytypes |= KT_RSA1; 74292555Sdes break; 74392555Sdes case KEY_DSA: 74492555Sdes get_keytypes |= KT_DSA; 74592555Sdes break; 74692555Sdes case KEY_RSA: 74792555Sdes get_keytypes |= KT_RSA; 74892555Sdes break; 74992555Sdes case KEY_UNSPEC: 75092555Sdes fatal("unknown key type %s", tname); 75192555Sdes } 75292555Sdes tname = strtok(NULL, ","); 75392555Sdes } 75492555Sdes break; 75592555Sdes case '4': 75692555Sdes IPv4or6 = AF_INET; 75792555Sdes break; 75892555Sdes case '6': 75992555Sdes IPv4or6 = AF_INET6; 76092555Sdes break; 76192555Sdes case '?': 76292555Sdes default: 76392555Sdes usage(); 76476259Sgreen } 76576259Sgreen } 76692555Sdes if (optind == argc && !fopt_count) 76776259Sgreen usage(); 76876259Sgreen 76992555Sdes log_init("ssh-keyscan", log_level, SYSLOG_FACILITY_USER, 1); 77092555Sdes 77176259Sgreen maxfd = fdlim_get(1); 77276259Sgreen if (maxfd < 0) 77376259Sgreen fatal("%s: fdlim_get: bad value", __progname); 77476259Sgreen if (maxfd > MAXMAXFD) 77576259Sgreen maxfd = MAXMAXFD; 77676259Sgreen if (MAXCON <= 0) 77776259Sgreen fatal("%s: not enough file descriptors", __progname); 77876259Sgreen if (maxfd > fdlim_get(0)) 77976259Sgreen fdlim_set(maxfd); 78076259Sgreen fdcon = xmalloc(maxfd * sizeof(con)); 78176259Sgreen memset(fdcon, 0, maxfd * sizeof(con)); 78276259Sgreen 78376259Sgreen read_wait_size = howmany(maxfd, NFDBITS) * sizeof(fd_mask); 78476259Sgreen read_wait = xmalloc(read_wait_size); 78576259Sgreen memset(read_wait, 0, read_wait_size); 78676259Sgreen 78792555Sdes if (fopt_count) { 78892555Sdes Linebuf *lb; 78992555Sdes char *line; 79092555Sdes int j; 79176259Sgreen 79292555Sdes for (j = 0; j < fopt_count; j++) { 79392555Sdes lb = Linebuf_alloc(argv[j], error); 79492555Sdes if (!lb) 79592555Sdes continue; 79692555Sdes while ((line = Linebuf_getline(lb)) != NULL) 79792555Sdes do_host(line); 79892555Sdes Linebuf_free(lb); 79976259Sgreen } 80092555Sdes } 80192555Sdes 80292555Sdes while (optind < argc) 80392555Sdes do_host(argv[optind++]); 80492555Sdes 80576259Sgreen while (ncon > 0) 80676259Sgreen conloop(); 80776259Sgreen 80876259Sgreen return (0); 80976259Sgreen} 810