1323124Sdes/* $OpenBSD: ssh-keyscan.c,v 1.106 2016/05/02 10:26:04 djm Exp $ */ 276259Sgreen/* 376259Sgreen * Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>. 476259Sgreen * 576259Sgreen * Modification and redistribution in source and binary forms is 676259Sgreen * permitted provided that due credit is given to the author and the 792555Sdes * OpenBSD project by leaving this copyright notice intact. 876259Sgreen */ 976259Sgreen 1076259Sgreen#include "includes.h" 11162856Sdes 12295367Sdes#include <sys/types.h> 13106130Sdes#include "openbsd-compat/sys-queue.h" 14162856Sdes#include <sys/resource.h> 15162856Sdes#ifdef HAVE_SYS_TIME_H 16162856Sdes# include <sys/time.h> 17162856Sdes#endif 1876259Sgreen 19162856Sdes#include <netinet/in.h> 20162856Sdes#include <arpa/inet.h> 21162856Sdes 2276259Sgreen#include <openssl/bn.h> 2376259Sgreen 24162856Sdes#include <netdb.h> 25162856Sdes#include <errno.h> 26162856Sdes#include <stdarg.h> 27162856Sdes#include <stdio.h> 28162856Sdes#include <stdlib.h> 29162856Sdes#include <signal.h> 30162856Sdes#include <string.h> 31162856Sdes#include <unistd.h> 32162856Sdes 3376259Sgreen#include "xmalloc.h" 3476259Sgreen#include "ssh.h" 3576259Sgreen#include "ssh1.h" 36295367Sdes#include "sshbuf.h" 37295367Sdes#include "sshkey.h" 38162856Sdes#include "cipher.h" 3992555Sdes#include "kex.h" 4092555Sdes#include "compat.h" 4192555Sdes#include "myproposal.h" 4292555Sdes#include "packet.h" 4392555Sdes#include "dispatch.h" 4476259Sgreen#include "log.h" 4576259Sgreen#include "atomicio.h" 4692555Sdes#include "misc.h" 47147005Sdes#include "hostfile.h" 48295367Sdes#include "ssherr.h" 49295367Sdes#include "ssh_api.h" 5076259Sgreen 5198941Sdes/* Flag indicating whether IPv4 or IPv6. This can be set on the command line. 5298941Sdes Default value is AF_UNSPEC means both IPv4 and IPv6. */ 5398941Sdesint IPv4or6 = AF_UNSPEC; 5476259Sgreen 5592555Sdesint ssh_port = SSH_DEFAULT_PORT; 5676259Sgreen 57221420Sdes#define KT_RSA1 1 58221420Sdes#define KT_DSA 2 59221420Sdes#define KT_RSA 4 60221420Sdes#define KT_ECDSA 8 61262566Sdes#define KT_ED25519 16 6292555Sdes 63296781Sdesint get_cert = 0; 64295367Sdesint get_keytypes = KT_RSA|KT_ECDSA|KT_ED25519; 6592555Sdes 66147005Sdesint hash_hosts = 0; /* Hash hostname on output */ 67147005Sdes 6876259Sgreen#define MAXMAXFD 256 6976259Sgreen 7076259Sgreen/* The number of seconds after which to give up on a TCP connection */ 7176259Sgreenint timeout = 5; 7276259Sgreen 7376259Sgreenint maxfd; 7476259Sgreen#define MAXCON (maxfd - 10) 7576259Sgreen 7676259Sgreenextern char *__progname; 7776259Sgreenfd_set *read_wait; 78162856Sdessize_t read_wait_nfdset; 7976259Sgreenint ncon; 8076259Sgreen 81295367Sdesstruct ssh *active_state = NULL; /* XXX needed for linking */ 82295367Sdes 8376259Sgreen/* 8476259Sgreen * Keep a connection structure for each file descriptor. The state 8576259Sgreen * associated with file descriptor n is held in fdcon[n]. 8676259Sgreen */ 8776259Sgreentypedef struct Connection { 8876259Sgreen u_char c_status; /* State of connection on this file desc. */ 8976259Sgreen#define CS_UNUSED 0 /* File descriptor unused */ 9076259Sgreen#define CS_CON 1 /* Waiting to connect/read greeting */ 9176259Sgreen#define CS_SIZE 2 /* Waiting to read initial packet size */ 9276259Sgreen#define CS_KEYS 3 /* Waiting to read public key packet */ 9376259Sgreen int c_fd; /* Quick lookup: c->c_fd == c - fdcon */ 9476259Sgreen int c_plen; /* Packet length field for ssh packet */ 9576259Sgreen int c_len; /* Total bytes which must be read. */ 9676259Sgreen int c_off; /* Length of data read so far. */ 9792555Sdes int c_keytype; /* Only one of KT_RSA1, KT_DSA, or KT_RSA */ 98295367Sdes sig_atomic_t c_done; /* SSH2 done */ 9976259Sgreen char *c_namebase; /* Address to free for c_name and c_namelist */ 10076259Sgreen char *c_name; /* Hostname of connection for errors */ 10176259Sgreen char *c_namelist; /* Pointer to other possible addresses */ 10276259Sgreen char *c_output_name; /* Hostname of connection for output */ 10376259Sgreen char *c_data; /* Data read from this fd */ 104295367Sdes struct ssh *c_ssh; /* SSH-connection */ 10576259Sgreen struct timeval c_tv; /* Time at which connection gets aborted */ 10676259Sgreen TAILQ_ENTRY(Connection) c_link; /* List of connections in timeout order. */ 10776259Sgreen} con; 10876259Sgreen 10976259SgreenTAILQ_HEAD(conlist, Connection) tq; /* Timeout Queue */ 11076259Sgreencon *fdcon; 11176259Sgreen 112295367Sdesstatic void keyprint(con *c, struct sshkey *key); 113295367Sdes 11492555Sdesstatic int 11576259Sgreenfdlim_get(int hard) 11676259Sgreen{ 11798941Sdes#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) 11876259Sgreen struct rlimit rlfd; 11976259Sgreen 12076259Sgreen if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0) 12176259Sgreen return (-1); 12276259Sgreen if ((hard ? rlfd.rlim_max : rlfd.rlim_cur) == RLIM_INFINITY) 123126277Sdes return SSH_SYSFDMAX; 12476259Sgreen else 12576259Sgreen return hard ? rlfd.rlim_max : rlfd.rlim_cur; 12698941Sdes#else 127126277Sdes return SSH_SYSFDMAX; 12898941Sdes#endif 12976259Sgreen} 13076259Sgreen 13192555Sdesstatic int 13276259Sgreenfdlim_set(int lim) 13376259Sgreen{ 13498941Sdes#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) 13576259Sgreen struct rlimit rlfd; 13698941Sdes#endif 137106130Sdes 13876259Sgreen if (lim <= 0) 13976259Sgreen return (-1); 14098941Sdes#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) 14176259Sgreen if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0) 14276259Sgreen return (-1); 14376259Sgreen rlfd.rlim_cur = lim; 14476259Sgreen if (setrlimit(RLIMIT_NOFILE, &rlfd) < 0) 14576259Sgreen return (-1); 14698941Sdes#elif defined (HAVE_SETDTABLESIZE) 14798941Sdes setdtablesize(lim); 14898941Sdes#endif 14976259Sgreen return (0); 15076259Sgreen} 15176259Sgreen 15276259Sgreen/* 15376259Sgreen * This is an strsep function that returns a null field for adjacent 15476259Sgreen * separators. This is the same as the 4.4BSD strsep, but different from the 15576259Sgreen * one in the GNU libc. 15676259Sgreen */ 15792555Sdesstatic char * 15876259Sgreenxstrsep(char **str, const char *delim) 15976259Sgreen{ 16076259Sgreen char *s, *e; 16176259Sgreen 16276259Sgreen if (!**str) 16376259Sgreen return (NULL); 16476259Sgreen 16576259Sgreen s = *str; 16676259Sgreen e = s + strcspn(s, delim); 16776259Sgreen 16876259Sgreen if (*e != '\0') 16976259Sgreen *e++ = '\0'; 17076259Sgreen *str = e; 17176259Sgreen 17276259Sgreen return (s); 17376259Sgreen} 17476259Sgreen 17576259Sgreen/* 17676259Sgreen * Get the next non-null token (like GNU strsep). Strsep() will return a 17776259Sgreen * null token for two adjacent separators, so we may have to loop. 17876259Sgreen */ 17992555Sdesstatic char * 18076259Sgreenstrnnsep(char **stringp, char *delim) 18176259Sgreen{ 18276259Sgreen char *tok; 18376259Sgreen 18476259Sgreen do { 18576259Sgreen tok = xstrsep(stringp, delim); 18676259Sgreen } while (tok && *tok == '\0'); 18776259Sgreen return (tok); 18876259Sgreen} 18976259Sgreen 190295367Sdes#ifdef WITH_SSH1 191295367Sdesstatic struct sshkey * 19292555Sdeskeygrab_ssh1(con *c) 19376259Sgreen{ 194295367Sdes static struct sshkey *rsa; 195295367Sdes static struct sshbuf *msg; 196295367Sdes int r; 197295367Sdes u_char type; 19876259Sgreen 19976259Sgreen if (rsa == NULL) { 200295367Sdes if ((rsa = sshkey_new(KEY_RSA1)) == NULL) { 201295367Sdes error("%s: sshkey_new failed", __func__); 202295367Sdes return NULL; 203295367Sdes } 204295367Sdes if ((msg = sshbuf_new()) == NULL) 205295367Sdes fatal("%s: sshbuf_new failed", __func__); 20676259Sgreen } 207295367Sdes if ((r = sshbuf_put(msg, c->c_data, c->c_plen)) != 0 || 208295367Sdes (r = sshbuf_consume(msg, 8 - (c->c_plen & 7))) != 0 || /* padding */ 209295367Sdes (r = sshbuf_get_u8(msg, &type)) != 0) 210295367Sdes goto buf_err; 211295367Sdes if (type != (int) SSH_SMSG_PUBLIC_KEY) { 21292555Sdes error("%s: invalid packet type", c->c_name); 213295367Sdes sshbuf_reset(msg); 21492555Sdes return NULL; 21576259Sgreen } 216295367Sdes if ((r = sshbuf_consume(msg, 8)) != 0 || /* cookie */ 217295367Sdes /* server key */ 218295367Sdes (r = sshbuf_get_u32(msg, NULL)) != 0 || 219295367Sdes (r = sshbuf_get_bignum1(msg, NULL)) != 0 || 220295367Sdes (r = sshbuf_get_bignum1(msg, NULL)) != 0 || 221295367Sdes /* host key */ 222295367Sdes (r = sshbuf_get_u32(msg, NULL)) != 0 || 223295367Sdes (r = sshbuf_get_bignum1(msg, rsa->rsa->e)) != 0 || 224295367Sdes (r = sshbuf_get_bignum1(msg, rsa->rsa->n)) != 0) { 225295367Sdes buf_err: 226295367Sdes error("%s: buffer error: %s", __func__, ssh_err(r)); 227295367Sdes sshbuf_reset(msg); 228295367Sdes return NULL; 229295367Sdes } 23076259Sgreen 231295367Sdes sshbuf_reset(msg); 23276259Sgreen 23392555Sdes return (rsa); 23492555Sdes} 235295367Sdes#endif 23692555Sdes 23792555Sdesstatic int 238295367Sdeskey_print_wrapper(struct sshkey *hostkey, struct ssh *ssh) 23992555Sdes{ 240295367Sdes con *c; 241295367Sdes 242295367Sdes if ((c = ssh_get_app_data(ssh)) != NULL) 243295367Sdes keyprint(c, hostkey); 244295367Sdes /* always abort key exchange */ 245295367Sdes return -1; 24692555Sdes} 24792555Sdes 24892555Sdesstatic int 24992555Sdesssh2_capable(int remote_major, int remote_minor) 25092555Sdes{ 25192555Sdes switch (remote_major) { 25292555Sdes case 1: 25392555Sdes if (remote_minor == 99) 25492555Sdes return 1; 25592555Sdes break; 25692555Sdes case 2: 25792555Sdes return 1; 25892555Sdes default: 25992555Sdes break; 26092555Sdes } 26192555Sdes return 0; 26292555Sdes} 26392555Sdes 264295367Sdesstatic void 26592555Sdeskeygrab_ssh2(con *c) 26692555Sdes{ 267295367Sdes char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; 268295367Sdes int r; 26992555Sdes 27092555Sdes enable_compat20(); 271296781Sdes switch (c->c_keytype) { 272296781Sdes case KT_DSA: 273296781Sdes myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? 274296781Sdes "ssh-dss-cert-v01@openssh.com" : "ssh-dss"; 275296781Sdes break; 276296781Sdes case KT_RSA: 277296781Sdes myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? 278296781Sdes "ssh-rsa-cert-v01@openssh.com" : "ssh-rsa"; 279296781Sdes break; 280296781Sdes case KT_ED25519: 281296781Sdes myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? 282296781Sdes "ssh-ed25519-cert-v01@openssh.com" : "ssh-ed25519"; 283296781Sdes break; 284296781Sdes case KT_ECDSA: 285296781Sdes myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? 286296781Sdes "ecdsa-sha2-nistp256-cert-v01@openssh.com," 287296781Sdes "ecdsa-sha2-nistp384-cert-v01@openssh.com," 288296781Sdes "ecdsa-sha2-nistp521-cert-v01@openssh.com" : 289296781Sdes "ecdsa-sha2-nistp256," 290296781Sdes "ecdsa-sha2-nistp384," 291296781Sdes "ecdsa-sha2-nistp521"; 292296781Sdes break; 293296781Sdes default: 294296781Sdes fatal("unknown key type %d", c->c_keytype); 295296781Sdes break; 296296781Sdes } 297295367Sdes if ((r = kex_setup(c->c_ssh, myproposal)) != 0) { 298295367Sdes free(c->c_ssh); 299295367Sdes fprintf(stderr, "kex_setup: %s\n", ssh_err(r)); 30092555Sdes exit(1); 30192555Sdes } 302295367Sdes#ifdef WITH_OPENSSL 303295367Sdes c->c_ssh->kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; 304295367Sdes c->c_ssh->kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; 305323124Sdes c->c_ssh->kex->kex[KEX_DH_GRP14_SHA256] = kexdh_client; 306323124Sdes c->c_ssh->kex->kex[KEX_DH_GRP16_SHA512] = kexdh_client; 307323124Sdes c->c_ssh->kex->kex[KEX_DH_GRP18_SHA512] = kexdh_client; 308295367Sdes c->c_ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; 309295367Sdes c->c_ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; 310295367Sdes# ifdef OPENSSL_HAS_ECC 311295367Sdes c->c_ssh->kex->kex[KEX_ECDH_SHA2] = kexecdh_client; 312295367Sdes# endif 313295367Sdes#endif 314295367Sdes c->c_ssh->kex->kex[KEX_C25519_SHA256] = kexc25519_client; 315295367Sdes ssh_set_verify_host_key_callback(c->c_ssh, key_print_wrapper); 316295367Sdes /* 317295367Sdes * do the key-exchange until an error occurs or until 318295367Sdes * the key_print_wrapper() callback sets c_done. 319295367Sdes */ 320295367Sdes ssh_dispatch_run(c->c_ssh, DISPATCH_BLOCK, &c->c_done, c->c_ssh); 32192555Sdes} 32292555Sdes 32392555Sdesstatic void 324296781Sdeskeyprint_one(char *host, struct sshkey *key) 32592555Sdes{ 326296781Sdes char *hostport; 327147005Sdes 328147005Sdes if (hash_hosts && (host = host_hash(host, NULL, 0)) == NULL) 329147005Sdes fatal("host_hash failed"); 33092555Sdes 331295367Sdes hostport = put_host_port(host, ssh_port); 332296781Sdes if (!get_cert) 333296781Sdes fprintf(stdout, "%s ", hostport); 334295367Sdes sshkey_write(key, stdout); 33576259Sgreen fputs("\n", stdout); 336295367Sdes free(hostport); 33776259Sgreen} 33876259Sgreen 339296781Sdesstatic void 340296781Sdeskeyprint(con *c, struct sshkey *key) 341296781Sdes{ 342296781Sdes char *hosts = c->c_output_name ? c->c_output_name : c->c_name; 343296781Sdes char *host, *ohosts; 344296781Sdes 345296781Sdes if (key == NULL) 346296781Sdes return; 347296781Sdes if (get_cert || (!hash_hosts && ssh_port == SSH_DEFAULT_PORT)) { 348296781Sdes keyprint_one(hosts, key); 349296781Sdes return; 350296781Sdes } 351296781Sdes ohosts = hosts = xstrdup(hosts); 352296781Sdes while ((host = strsep(&hosts, ",")) != NULL) 353296781Sdes keyprint_one(host, key); 354296781Sdes free(ohosts); 355296781Sdes} 356296781Sdes 35792555Sdesstatic int 35876259Sgreentcpconnect(char *host) 35976259Sgreen{ 36076259Sgreen struct addrinfo hints, *ai, *aitop; 36176259Sgreen char strport[NI_MAXSERV]; 36276259Sgreen int gaierr, s = -1; 36376259Sgreen 36492555Sdes snprintf(strport, sizeof strport, "%d", ssh_port); 36576259Sgreen memset(&hints, 0, sizeof(hints)); 36692555Sdes hints.ai_family = IPv4or6; 36776259Sgreen hints.ai_socktype = SOCK_STREAM; 368295367Sdes if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) { 369295367Sdes error("getaddrinfo %s: %s", host, ssh_gai_strerror(gaierr)); 370295367Sdes return -1; 371295367Sdes } 37276259Sgreen for (ai = aitop; ai; ai = ai->ai_next) { 373124211Sdes s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 37476259Sgreen if (s < 0) { 37576259Sgreen error("socket: %s", strerror(errno)); 37676259Sgreen continue; 37776259Sgreen } 378137019Sdes if (set_nonblock(s) == -1) 379137019Sdes fatal("%s: set_nonblock(%d)", __func__, s); 38076259Sgreen if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0 && 38176259Sgreen errno != EINPROGRESS) 38276259Sgreen error("connect (`%s'): %s", host, strerror(errno)); 38376259Sgreen else 38476259Sgreen break; 38576259Sgreen close(s); 38676259Sgreen s = -1; 38776259Sgreen } 38876259Sgreen freeaddrinfo(aitop); 38976259Sgreen return s; 39076259Sgreen} 39176259Sgreen 39292555Sdesstatic int 39392555Sdesconalloc(char *iname, char *oname, int keytype) 39476259Sgreen{ 395106130Sdes char *namebase, *name, *namelist; 39676259Sgreen int s; 39776259Sgreen 39876259Sgreen namebase = namelist = xstrdup(iname); 39976259Sgreen 40076259Sgreen do { 40176259Sgreen name = xstrsep(&namelist, ","); 40276259Sgreen if (!name) { 403255767Sdes free(namebase); 40476259Sgreen return (-1); 40576259Sgreen } 40676259Sgreen } while ((s = tcpconnect(name)) < 0); 40776259Sgreen 40876259Sgreen if (s >= maxfd) 40976259Sgreen fatal("conalloc: fdno %d too high", s); 41076259Sgreen if (fdcon[s].c_status) 41176259Sgreen fatal("conalloc: attempt to reuse fdno %d", s); 41276259Sgreen 413296781Sdes debug3("%s: oname %s kt %d", __func__, oname, keytype); 41476259Sgreen fdcon[s].c_fd = s; 41576259Sgreen fdcon[s].c_status = CS_CON; 41676259Sgreen fdcon[s].c_namebase = namebase; 41776259Sgreen fdcon[s].c_name = name; 41876259Sgreen fdcon[s].c_namelist = namelist; 41976259Sgreen fdcon[s].c_output_name = xstrdup(oname); 42076259Sgreen fdcon[s].c_data = (char *) &fdcon[s].c_plen; 42176259Sgreen fdcon[s].c_len = 4; 42276259Sgreen fdcon[s].c_off = 0; 42392555Sdes fdcon[s].c_keytype = keytype; 42476259Sgreen gettimeofday(&fdcon[s].c_tv, NULL); 42576259Sgreen fdcon[s].c_tv.tv_sec += timeout; 42676259Sgreen TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); 42776259Sgreen FD_SET(s, read_wait); 42876259Sgreen ncon++; 42976259Sgreen return (s); 43076259Sgreen} 43176259Sgreen 43292555Sdesstatic void 43376259Sgreenconfree(int s) 43476259Sgreen{ 43576259Sgreen if (s >= maxfd || fdcon[s].c_status == CS_UNUSED) 43676259Sgreen fatal("confree: attempt to free bad fdno %d", s); 43776259Sgreen close(s); 438255767Sdes free(fdcon[s].c_namebase); 439255767Sdes free(fdcon[s].c_output_name); 44076259Sgreen if (fdcon[s].c_status == CS_KEYS) 441255767Sdes free(fdcon[s].c_data); 44276259Sgreen fdcon[s].c_status = CS_UNUSED; 44392555Sdes fdcon[s].c_keytype = 0; 444295367Sdes if (fdcon[s].c_ssh) { 445295367Sdes ssh_packet_close(fdcon[s].c_ssh); 446295367Sdes free(fdcon[s].c_ssh); 447295367Sdes fdcon[s].c_ssh = NULL; 448295367Sdes } 44976259Sgreen TAILQ_REMOVE(&tq, &fdcon[s], c_link); 45076259Sgreen FD_CLR(s, read_wait); 45176259Sgreen ncon--; 45276259Sgreen} 45376259Sgreen 45492555Sdesstatic void 45576259Sgreencontouch(int s) 45676259Sgreen{ 45776259Sgreen TAILQ_REMOVE(&tq, &fdcon[s], c_link); 45876259Sgreen gettimeofday(&fdcon[s].c_tv, NULL); 45976259Sgreen fdcon[s].c_tv.tv_sec += timeout; 46076259Sgreen TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); 46176259Sgreen} 46276259Sgreen 46392555Sdesstatic int 46476259Sgreenconrecycle(int s) 46576259Sgreen{ 466106130Sdes con *c = &fdcon[s]; 46776259Sgreen int ret; 46876259Sgreen 46992555Sdes ret = conalloc(c->c_namelist, c->c_output_name, c->c_keytype); 47076259Sgreen confree(s); 47176259Sgreen return (ret); 47276259Sgreen} 47376259Sgreen 47492555Sdesstatic void 47576259Sgreencongreet(int s) 47676259Sgreen{ 477149753Sdes int n = 0, remote_major = 0, remote_minor = 0; 47892555Sdes char buf[256], *cp; 47992555Sdes char remote_version[sizeof buf]; 48076259Sgreen size_t bufsiz; 48176259Sgreen con *c = &fdcon[s]; 48276259Sgreen 483157019Sdes for (;;) { 484157019Sdes memset(buf, '\0', sizeof(buf)); 485157019Sdes bufsiz = sizeof(buf); 486157019Sdes cp = buf; 487157019Sdes while (bufsiz-- && 488157019Sdes (n = atomicio(read, s, cp, 1)) == 1 && *cp != '\n') { 489157019Sdes if (*cp == '\r') 490157019Sdes *cp = '\n'; 491157019Sdes cp++; 492157019Sdes } 493157019Sdes if (n != 1 || strncmp(buf, "SSH-", 4) == 0) 494157019Sdes break; 49592555Sdes } 496149753Sdes if (n == 0) { 497149753Sdes switch (errno) { 498149753Sdes case EPIPE: 499149753Sdes error("%s: Connection closed by remote host", c->c_name); 500149753Sdes break; 501149753Sdes case ECONNREFUSED: 502149753Sdes break; 503149753Sdes default: 50476259Sgreen error("read (%s): %s", c->c_name, strerror(errno)); 505149753Sdes break; 506149753Sdes } 50776259Sgreen conrecycle(s); 50876259Sgreen return; 50976259Sgreen } 51076259Sgreen if (*cp != '\n' && *cp != '\r') { 51176259Sgreen error("%s: bad greeting", c->c_name); 51276259Sgreen confree(s); 51376259Sgreen return; 51476259Sgreen } 51576259Sgreen *cp = '\0'; 516295367Sdes if ((c->c_ssh = ssh_packet_set_connection(NULL, s, s)) == NULL) 517295367Sdes fatal("ssh_packet_set_connection failed"); 518295367Sdes ssh_packet_set_timeout(c->c_ssh, timeout, 1); 519295367Sdes ssh_set_app_data(c->c_ssh, c); /* back link */ 52092555Sdes if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", 52192555Sdes &remote_major, &remote_minor, remote_version) == 3) 522295367Sdes c->c_ssh->compat = compat_datafellows(remote_version); 52392555Sdes else 524295367Sdes c->c_ssh->compat = 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 } 536295367Sdes fprintf(stderr, "# %s:%d %s\n", c->c_name, ssh_port, 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); 540149753Sdes if (n < 0 || (size_t)n >= sizeof(buf)) { 541149753Sdes error("snprintf: buffer too small"); 542149753Sdes confree(s); 543149753Sdes return; 544149753Sdes } 545149753Sdes if (atomicio(vwrite, s, buf, n) != (size_t)n) { 54676259Sgreen error("write (%s): %s", c->c_name, strerror(errno)); 54776259Sgreen confree(s); 54876259Sgreen return; 54976259Sgreen } 55092555Sdes if (c->c_keytype != KT_RSA1) { 551295367Sdes keygrab_ssh2(c); 55292555Sdes confree(s); 55392555Sdes return; 55492555Sdes } 55576259Sgreen c->c_status = CS_SIZE; 55676259Sgreen contouch(s); 55776259Sgreen} 55876259Sgreen 55992555Sdesstatic void 56076259Sgreenconread(int s) 56176259Sgreen{ 562106130Sdes con *c = &fdcon[s]; 563149753Sdes size_t n; 56476259Sgreen 56576259Sgreen if (c->c_status == CS_CON) { 56676259Sgreen congreet(s); 56776259Sgreen return; 56876259Sgreen } 569137019Sdes n = atomicio(read, s, c->c_data + c->c_off, c->c_len - c->c_off); 570149753Sdes if (n == 0) { 57176259Sgreen error("read (%s): %s", c->c_name, strerror(errno)); 57276259Sgreen confree(s); 57376259Sgreen return; 57476259Sgreen } 57576259Sgreen c->c_off += n; 57676259Sgreen 57776259Sgreen if (c->c_off == c->c_len) 57876259Sgreen switch (c->c_status) { 57976259Sgreen case CS_SIZE: 58076259Sgreen c->c_plen = htonl(c->c_plen); 58176259Sgreen c->c_len = c->c_plen + 8 - (c->c_plen & 7); 58276259Sgreen c->c_off = 0; 58376259Sgreen c->c_data = xmalloc(c->c_len); 58476259Sgreen c->c_status = CS_KEYS; 58576259Sgreen break; 586295367Sdes#ifdef WITH_SSH1 58776259Sgreen case CS_KEYS: 58892555Sdes keyprint(c, keygrab_ssh1(c)); 58976259Sgreen confree(s); 59076259Sgreen return; 591295367Sdes#endif 59276259Sgreen default: 59376259Sgreen fatal("conread: invalid status %d", c->c_status); 59476259Sgreen break; 59576259Sgreen } 59676259Sgreen 59776259Sgreen contouch(s); 59876259Sgreen} 59976259Sgreen 60092555Sdesstatic void 60176259Sgreenconloop(void) 60276259Sgreen{ 603106130Sdes struct timeval seltime, now; 60476259Sgreen fd_set *r, *e; 605106130Sdes con *c; 60676259Sgreen int i; 60776259Sgreen 60876259Sgreen gettimeofday(&now, NULL); 60998675Sdes c = TAILQ_FIRST(&tq); 61076259Sgreen 61176259Sgreen if (c && (c->c_tv.tv_sec > now.tv_sec || 61276259Sgreen (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec > now.tv_usec))) { 61376259Sgreen seltime = c->c_tv; 61476259Sgreen seltime.tv_sec -= now.tv_sec; 61576259Sgreen seltime.tv_usec -= now.tv_usec; 61676259Sgreen if (seltime.tv_usec < 0) { 61776259Sgreen seltime.tv_usec += 1000000; 61876259Sgreen seltime.tv_sec--; 61976259Sgreen } 62076259Sgreen } else 621226046Sdes timerclear(&seltime); 62276259Sgreen 623162856Sdes r = xcalloc(read_wait_nfdset, sizeof(fd_mask)); 624162856Sdes e = xcalloc(read_wait_nfdset, sizeof(fd_mask)); 625162856Sdes memcpy(r, read_wait, read_wait_nfdset * sizeof(fd_mask)); 626162856Sdes memcpy(e, read_wait, read_wait_nfdset * sizeof(fd_mask)); 62776259Sgreen 62876259Sgreen while (select(maxfd, r, NULL, e, &seltime) == -1 && 629181111Sdes (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) 63076259Sgreen ; 63176259Sgreen 63276259Sgreen for (i = 0; i < maxfd; i++) { 63376259Sgreen if (FD_ISSET(i, e)) { 63476259Sgreen error("%s: exception!", fdcon[i].c_name); 63576259Sgreen confree(i); 63676259Sgreen } else if (FD_ISSET(i, r)) 63776259Sgreen conread(i); 63876259Sgreen } 639255767Sdes free(r); 640255767Sdes free(e); 64176259Sgreen 64298675Sdes c = TAILQ_FIRST(&tq); 64376259Sgreen while (c && (c->c_tv.tv_sec < now.tv_sec || 64476259Sgreen (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec < now.tv_usec))) { 64576259Sgreen int s = c->c_fd; 64676259Sgreen 64798675Sdes c = TAILQ_NEXT(c, c_link); 64876259Sgreen conrecycle(s); 64976259Sgreen } 65076259Sgreen} 65176259Sgreen 65292555Sdesstatic void 65392555Sdesdo_host(char *host) 65476259Sgreen{ 65592555Sdes char *name = strnnsep(&host, " \t\n"); 65692555Sdes int j; 65776259Sgreen 65892555Sdes if (name == NULL) 65992555Sdes return; 660262566Sdes for (j = KT_RSA1; j <= KT_ED25519; j *= 2) { 66192555Sdes if (get_keytypes & j) { 66292555Sdes while (ncon >= MAXCON) 66392555Sdes conloop(); 66492555Sdes conalloc(name, *host ? host : name, j); 66576259Sgreen } 66676259Sgreen } 66776259Sgreen} 66876259Sgreen 66976259Sgreenvoid 67092555Sdesfatal(const char *fmt,...) 67192555Sdes{ 67292555Sdes va_list args; 673106130Sdes 67492555Sdes va_start(args, fmt); 67592555Sdes do_log(SYSLOG_LEVEL_FATAL, fmt, args); 67692555Sdes va_end(args); 677295367Sdes exit(255); 67892555Sdes} 67992555Sdes 68092555Sdesstatic void 68176259Sgreenusage(void) 68276259Sgreen{ 683192595Sdes fprintf(stderr, 684296781Sdes "usage: %s [-46cHv] [-f file] [-p port] [-T timeout] [-t type]\n" 685192595Sdes "\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; 694215116Sdes int opt, fopt_count = 0, j; 695215116Sdes char *tname, *cp, line[NI_MAXHOST]; 696215116Sdes FILE *fp; 697215116Sdes u_long linenum; 69876259Sgreen 69992555Sdes extern int optind; 70092555Sdes extern char *optarg; 70192555Sdes 702296781Sdes ssh_malloc_init(); /* must be called before any mallocs */ 703124211Sdes __progname = ssh_get_progname(argv[0]); 70498941Sdes seed_rng(); 70576259Sgreen TAILQ_INIT(&tq); 70676259Sgreen 707157019Sdes /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 708157019Sdes sanitise_stdfd(); 709157019Sdes 71092555Sdes if (argc <= 1) 71176259Sgreen usage(); 71276259Sgreen 713296781Sdes while ((opt = getopt(argc, argv, "cHv46p:T:t:f:")) != -1) { 71492555Sdes switch (opt) { 715147005Sdes case 'H': 716147005Sdes hash_hosts = 1; 717147005Sdes break; 718296781Sdes case 'c': 719296781Sdes get_cert = 1; 720296781Sdes break; 72192555Sdes case 'p': 72292555Sdes ssh_port = a2port(optarg); 723192595Sdes if (ssh_port <= 0) { 72492555Sdes fprintf(stderr, "Bad port '%s'\n", optarg); 72592555Sdes exit(1); 72692555Sdes } 72792555Sdes break; 72892555Sdes case 'T': 729106130Sdes timeout = convtime(optarg); 730106130Sdes if (timeout == -1 || timeout == 0) { 731106130Sdes fprintf(stderr, "Bad timeout '%s'\n", optarg); 73276259Sgreen usage(); 733106130Sdes } 73492555Sdes break; 73592555Sdes case 'v': 73692555Sdes if (!debug_flag) { 73792555Sdes debug_flag = 1; 73892555Sdes log_level = SYSLOG_LEVEL_DEBUG1; 73992555Sdes } 74092555Sdes else if (log_level < SYSLOG_LEVEL_DEBUG3) 74192555Sdes log_level++; 74292555Sdes else 74392555Sdes fatal("Too high debugging level."); 74492555Sdes break; 74592555Sdes case 'f': 74692555Sdes if (strcmp(optarg, "-") == 0) 74792555Sdes optarg = NULL; 74892555Sdes argv[fopt_count++] = optarg; 74992555Sdes break; 75092555Sdes case 't': 75192555Sdes get_keytypes = 0; 75292555Sdes tname = strtok(optarg, ","); 75392555Sdes while (tname) { 754295367Sdes int type = sshkey_type_from_name(tname); 75592555Sdes switch (type) { 75692555Sdes case KEY_RSA1: 75792555Sdes get_keytypes |= KT_RSA1; 75892555Sdes break; 75992555Sdes case KEY_DSA: 76092555Sdes get_keytypes |= KT_DSA; 76192555Sdes break; 762221420Sdes case KEY_ECDSA: 763221420Sdes get_keytypes |= KT_ECDSA; 764221420Sdes break; 76592555Sdes case KEY_RSA: 76692555Sdes get_keytypes |= KT_RSA; 76792555Sdes break; 768262566Sdes case KEY_ED25519: 769262566Sdes get_keytypes |= KT_ED25519; 770262566Sdes break; 77192555Sdes case KEY_UNSPEC: 77292555Sdes fatal("unknown key type %s", tname); 77392555Sdes } 77492555Sdes tname = strtok(NULL, ","); 77592555Sdes } 77692555Sdes break; 77792555Sdes case '4': 77892555Sdes IPv4or6 = AF_INET; 77992555Sdes break; 78092555Sdes case '6': 78192555Sdes IPv4or6 = AF_INET6; 78292555Sdes break; 78392555Sdes case '?': 78492555Sdes default: 78592555Sdes usage(); 78676259Sgreen } 78776259Sgreen } 78892555Sdes if (optind == argc && !fopt_count) 78976259Sgreen usage(); 79076259Sgreen 79192555Sdes log_init("ssh-keyscan", log_level, SYSLOG_FACILITY_USER, 1); 79292555Sdes 79376259Sgreen maxfd = fdlim_get(1); 79476259Sgreen if (maxfd < 0) 79576259Sgreen fatal("%s: fdlim_get: bad value", __progname); 79676259Sgreen if (maxfd > MAXMAXFD) 79776259Sgreen maxfd = MAXMAXFD; 79876259Sgreen if (MAXCON <= 0) 79976259Sgreen fatal("%s: not enough file descriptors", __progname); 80076259Sgreen if (maxfd > fdlim_get(0)) 80176259Sgreen fdlim_set(maxfd); 802162856Sdes fdcon = xcalloc(maxfd, sizeof(con)); 80376259Sgreen 804162856Sdes read_wait_nfdset = howmany(maxfd, NFDBITS); 805162856Sdes read_wait = xcalloc(read_wait_nfdset, sizeof(fd_mask)); 80676259Sgreen 807215116Sdes for (j = 0; j < fopt_count; j++) { 808215116Sdes if (argv[j] == NULL) 809215116Sdes fp = stdin; 810215116Sdes else if ((fp = fopen(argv[j], "r")) == NULL) 811215116Sdes fatal("%s: %s: %s", __progname, argv[j], 812215116Sdes strerror(errno)); 813215116Sdes linenum = 0; 81476259Sgreen 815215116Sdes while (read_keyfile_line(fp, 816215116Sdes argv[j] == NULL ? "(stdin)" : argv[j], line, sizeof(line), 817215116Sdes &linenum) != -1) { 818215116Sdes /* Chomp off trailing whitespace and comments */ 819215116Sdes if ((cp = strchr(line, '#')) == NULL) 820215116Sdes cp = line + strlen(line) - 1; 821215116Sdes while (cp >= line) { 822215116Sdes if (*cp == ' ' || *cp == '\t' || 823215116Sdes *cp == '\n' || *cp == '#') 824215116Sdes *cp-- = '\0'; 825215116Sdes else 826215116Sdes break; 827215116Sdes } 828215116Sdes 829215116Sdes /* Skip empty lines */ 830215116Sdes if (*line == '\0') 83192555Sdes continue; 832215116Sdes 833215116Sdes do_host(line); 83476259Sgreen } 835215116Sdes 836215116Sdes if (ferror(fp)) 837215116Sdes fatal("%s: %s: %s", __progname, argv[j], 838215116Sdes strerror(errno)); 839215116Sdes 840215116Sdes fclose(fp); 84192555Sdes } 84292555Sdes 84392555Sdes while (optind < argc) 84492555Sdes do_host(argv[optind++]); 84592555Sdes 84676259Sgreen while (ncon > 0) 84776259Sgreen conloop(); 84876259Sgreen 84976259Sgreen return (0); 85076259Sgreen} 851