1323136Sdes/* $OpenBSD: ssh-keyscan.c,v 1.109 2017/03/10 04:26:06 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 12294332Sdes#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" 36294332Sdes#include "sshbuf.h" 37294332Sdes#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" 48294332Sdes#include "ssherr.h" 49294332Sdes#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 61261320Sdes#define KT_ED25519 16 6292555Sdes 63296633Sdesint get_cert = 0; 64294328Sdesint 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 81294332Sdesstruct ssh *active_state = NULL; /* XXX needed for linking */ 82294332Sdes 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 */ 98294336Sdes 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 */ 104294332Sdes 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 112294332Sdesstatic void keyprint(con *c, struct sshkey *key); 113294332Sdes 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 190294328Sdes#ifdef WITH_SSH1 191294332Sdesstatic struct sshkey * 19292555Sdeskeygrab_ssh1(con *c) 19376259Sgreen{ 194294332Sdes static struct sshkey *rsa; 195294332Sdes static struct sshbuf *msg; 196294332Sdes int r; 197294332Sdes u_char type; 19876259Sgreen 19976259Sgreen if (rsa == NULL) { 200294332Sdes if ((rsa = sshkey_new(KEY_RSA1)) == NULL) { 201294332Sdes error("%s: sshkey_new failed", __func__); 202294332Sdes return NULL; 203294332Sdes } 204294332Sdes if ((msg = sshbuf_new()) == NULL) 205294332Sdes fatal("%s: sshbuf_new failed", __func__); 20676259Sgreen } 207294332Sdes if ((r = sshbuf_put(msg, c->c_data, c->c_plen)) != 0 || 208294332Sdes (r = sshbuf_consume(msg, 8 - (c->c_plen & 7))) != 0 || /* padding */ 209294332Sdes (r = sshbuf_get_u8(msg, &type)) != 0) 210294332Sdes goto buf_err; 211294332Sdes if (type != (int) SSH_SMSG_PUBLIC_KEY) { 21292555Sdes error("%s: invalid packet type", c->c_name); 213294332Sdes sshbuf_reset(msg); 21492555Sdes return NULL; 21576259Sgreen } 216294332Sdes if ((r = sshbuf_consume(msg, 8)) != 0 || /* cookie */ 217294332Sdes /* server key */ 218294332Sdes (r = sshbuf_get_u32(msg, NULL)) != 0 || 219294332Sdes (r = sshbuf_get_bignum1(msg, NULL)) != 0 || 220294332Sdes (r = sshbuf_get_bignum1(msg, NULL)) != 0 || 221294332Sdes /* host key */ 222294332Sdes (r = sshbuf_get_u32(msg, NULL)) != 0 || 223294332Sdes (r = sshbuf_get_bignum1(msg, rsa->rsa->e)) != 0 || 224294332Sdes (r = sshbuf_get_bignum1(msg, rsa->rsa->n)) != 0) { 225294332Sdes buf_err: 226294332Sdes error("%s: buffer error: %s", __func__, ssh_err(r)); 227294332Sdes sshbuf_reset(msg); 228294332Sdes return NULL; 229294332Sdes } 23076259Sgreen 231294332Sdes sshbuf_reset(msg); 23276259Sgreen 23392555Sdes return (rsa); 23492555Sdes} 235294328Sdes#endif 23692555Sdes 23792555Sdesstatic int 238294332Sdeskey_print_wrapper(struct sshkey *hostkey, struct ssh *ssh) 23992555Sdes{ 240294332Sdes con *c; 241294332Sdes 242294332Sdes if ((c = ssh_get_app_data(ssh)) != NULL) 243294332Sdes keyprint(c, hostkey); 244294332Sdes /* always abort key exchange */ 245294332Sdes 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 264294332Sdesstatic void 26592555Sdeskeygrab_ssh2(con *c) 26692555Sdes{ 267294328Sdes char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; 268294332Sdes int r; 26992555Sdes 27092555Sdes enable_compat20(); 271296633Sdes switch (c->c_keytype) { 272296633Sdes case KT_DSA: 273296633Sdes myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? 274296633Sdes "ssh-dss-cert-v01@openssh.com" : "ssh-dss"; 275296633Sdes break; 276296633Sdes case KT_RSA: 277296633Sdes myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? 278296633Sdes "ssh-rsa-cert-v01@openssh.com" : "ssh-rsa"; 279296633Sdes break; 280296633Sdes case KT_ED25519: 281296633Sdes myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? 282296633Sdes "ssh-ed25519-cert-v01@openssh.com" : "ssh-ed25519"; 283296633Sdes break; 284296633Sdes case KT_ECDSA: 285296633Sdes myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? 286296633Sdes "ecdsa-sha2-nistp256-cert-v01@openssh.com," 287296633Sdes "ecdsa-sha2-nistp384-cert-v01@openssh.com," 288296633Sdes "ecdsa-sha2-nistp521-cert-v01@openssh.com" : 289296633Sdes "ecdsa-sha2-nistp256," 290296633Sdes "ecdsa-sha2-nistp384," 291296633Sdes "ecdsa-sha2-nistp521"; 292296633Sdes break; 293296633Sdes default: 294296633Sdes fatal("unknown key type %d", c->c_keytype); 295296633Sdes break; 296296633Sdes } 297294332Sdes if ((r = kex_setup(c->c_ssh, myproposal)) != 0) { 298294332Sdes free(c->c_ssh); 299294332Sdes fprintf(stderr, "kex_setup: %s\n", ssh_err(r)); 300294332Sdes exit(1); 301294332Sdes } 302294328Sdes#ifdef WITH_OPENSSL 303294332Sdes c->c_ssh->kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; 304294332Sdes c->c_ssh->kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; 305323129Sdes c->c_ssh->kex->kex[KEX_DH_GRP14_SHA256] = kexdh_client; 306323129Sdes c->c_ssh->kex->kex[KEX_DH_GRP16_SHA512] = kexdh_client; 307323129Sdes c->c_ssh->kex->kex[KEX_DH_GRP18_SHA512] = kexdh_client; 308294332Sdes c->c_ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; 309294332Sdes c->c_ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; 310294332Sdes# ifdef OPENSSL_HAS_ECC 311294332Sdes c->c_ssh->kex->kex[KEX_ECDH_SHA2] = kexecdh_client; 312294332Sdes# endif 313294328Sdes#endif 314294332Sdes c->c_ssh->kex->kex[KEX_C25519_SHA256] = kexc25519_client; 315294332Sdes ssh_set_verify_host_key_callback(c->c_ssh, key_print_wrapper); 316294332Sdes /* 317294332Sdes * do the key-exchange until an error occurs or until 318294332Sdes * the key_print_wrapper() callback sets c_done. 319294332Sdes */ 320294332Sdes ssh_dispatch_run(c->c_ssh, DISPATCH_BLOCK, &c->c_done, c->c_ssh); 32192555Sdes} 32292555Sdes 32392555Sdesstatic void 324323136Sdeskeyprint_one(const char *host, struct sshkey *key) 32592555Sdes{ 326296633Sdes char *hostport; 327323136Sdes const char *known_host, *hashed; 328147005Sdes 329323136Sdes hostport = put_host_port(host, ssh_port); 330323136Sdes lowercase(hostport); 331323136Sdes if (hash_hosts && (hashed = host_hash(host, NULL, 0)) == NULL) 332147005Sdes fatal("host_hash failed"); 333323136Sdes known_host = hash_hosts ? hashed : hostport; 334296633Sdes if (!get_cert) 335323136Sdes fprintf(stdout, "%s ", known_host); 336294332Sdes sshkey_write(key, stdout); 33776259Sgreen fputs("\n", stdout); 338294336Sdes free(hostport); 33976259Sgreen} 34076259Sgreen 341296633Sdesstatic void 342296633Sdeskeyprint(con *c, struct sshkey *key) 343296633Sdes{ 344296633Sdes char *hosts = c->c_output_name ? c->c_output_name : c->c_name; 345296633Sdes char *host, *ohosts; 346296633Sdes 347296633Sdes if (key == NULL) 348296633Sdes return; 349296633Sdes if (get_cert || (!hash_hosts && ssh_port == SSH_DEFAULT_PORT)) { 350296633Sdes keyprint_one(hosts, key); 351296633Sdes return; 352296633Sdes } 353296633Sdes ohosts = hosts = xstrdup(hosts); 354296633Sdes while ((host = strsep(&hosts, ",")) != NULL) 355296633Sdes keyprint_one(host, key); 356296633Sdes free(ohosts); 357296633Sdes} 358296633Sdes 35992555Sdesstatic int 36076259Sgreentcpconnect(char *host) 36176259Sgreen{ 36276259Sgreen struct addrinfo hints, *ai, *aitop; 36376259Sgreen char strport[NI_MAXSERV]; 36476259Sgreen int gaierr, s = -1; 36576259Sgreen 36692555Sdes snprintf(strport, sizeof strport, "%d", ssh_port); 36776259Sgreen memset(&hints, 0, sizeof(hints)); 36892555Sdes hints.ai_family = IPv4or6; 36976259Sgreen hints.ai_socktype = SOCK_STREAM; 370294332Sdes if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) { 371294332Sdes error("getaddrinfo %s: %s", host, ssh_gai_strerror(gaierr)); 372294332Sdes return -1; 373294332Sdes } 37476259Sgreen for (ai = aitop; ai; ai = ai->ai_next) { 375124211Sdes s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 37676259Sgreen if (s < 0) { 37776259Sgreen error("socket: %s", strerror(errno)); 37876259Sgreen continue; 37976259Sgreen } 380137019Sdes if (set_nonblock(s) == -1) 381137019Sdes fatal("%s: set_nonblock(%d)", __func__, s); 38276259Sgreen if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0 && 38376259Sgreen errno != EINPROGRESS) 38476259Sgreen error("connect (`%s'): %s", host, strerror(errno)); 38576259Sgreen else 38676259Sgreen break; 38776259Sgreen close(s); 38876259Sgreen s = -1; 38976259Sgreen } 39076259Sgreen freeaddrinfo(aitop); 39176259Sgreen return s; 39276259Sgreen} 39376259Sgreen 39492555Sdesstatic int 39592555Sdesconalloc(char *iname, char *oname, int keytype) 39676259Sgreen{ 397106130Sdes char *namebase, *name, *namelist; 39876259Sgreen int s; 39976259Sgreen 40076259Sgreen namebase = namelist = xstrdup(iname); 40176259Sgreen 40276259Sgreen do { 40376259Sgreen name = xstrsep(&namelist, ","); 40476259Sgreen if (!name) { 405255767Sdes free(namebase); 40676259Sgreen return (-1); 40776259Sgreen } 40876259Sgreen } while ((s = tcpconnect(name)) < 0); 40976259Sgreen 41076259Sgreen if (s >= maxfd) 41176259Sgreen fatal("conalloc: fdno %d too high", s); 41276259Sgreen if (fdcon[s].c_status) 41376259Sgreen fatal("conalloc: attempt to reuse fdno %d", s); 41476259Sgreen 415296633Sdes debug3("%s: oname %s kt %d", __func__, oname, keytype); 41676259Sgreen fdcon[s].c_fd = s; 41776259Sgreen fdcon[s].c_status = CS_CON; 41876259Sgreen fdcon[s].c_namebase = namebase; 41976259Sgreen fdcon[s].c_name = name; 42076259Sgreen fdcon[s].c_namelist = namelist; 42176259Sgreen fdcon[s].c_output_name = xstrdup(oname); 42276259Sgreen fdcon[s].c_data = (char *) &fdcon[s].c_plen; 42376259Sgreen fdcon[s].c_len = 4; 42476259Sgreen fdcon[s].c_off = 0; 42592555Sdes fdcon[s].c_keytype = keytype; 42676259Sgreen gettimeofday(&fdcon[s].c_tv, NULL); 42776259Sgreen fdcon[s].c_tv.tv_sec += timeout; 42876259Sgreen TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); 42976259Sgreen FD_SET(s, read_wait); 43076259Sgreen ncon++; 43176259Sgreen return (s); 43276259Sgreen} 43376259Sgreen 43492555Sdesstatic void 43576259Sgreenconfree(int s) 43676259Sgreen{ 43776259Sgreen if (s >= maxfd || fdcon[s].c_status == CS_UNUSED) 43876259Sgreen fatal("confree: attempt to free bad fdno %d", s); 43976259Sgreen close(s); 440255767Sdes free(fdcon[s].c_namebase); 441255767Sdes free(fdcon[s].c_output_name); 44276259Sgreen if (fdcon[s].c_status == CS_KEYS) 443255767Sdes free(fdcon[s].c_data); 44476259Sgreen fdcon[s].c_status = CS_UNUSED; 44592555Sdes fdcon[s].c_keytype = 0; 446294332Sdes if (fdcon[s].c_ssh) { 447294332Sdes ssh_packet_close(fdcon[s].c_ssh); 448294332Sdes free(fdcon[s].c_ssh); 449294332Sdes fdcon[s].c_ssh = NULL; 450294332Sdes } 45176259Sgreen TAILQ_REMOVE(&tq, &fdcon[s], c_link); 45276259Sgreen FD_CLR(s, read_wait); 45376259Sgreen ncon--; 45476259Sgreen} 45576259Sgreen 45692555Sdesstatic void 45776259Sgreencontouch(int s) 45876259Sgreen{ 45976259Sgreen TAILQ_REMOVE(&tq, &fdcon[s], c_link); 46076259Sgreen gettimeofday(&fdcon[s].c_tv, NULL); 46176259Sgreen fdcon[s].c_tv.tv_sec += timeout; 46276259Sgreen TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); 46376259Sgreen} 46476259Sgreen 46592555Sdesstatic int 46676259Sgreenconrecycle(int s) 46776259Sgreen{ 468106130Sdes con *c = &fdcon[s]; 46976259Sgreen int ret; 47076259Sgreen 47192555Sdes ret = conalloc(c->c_namelist, c->c_output_name, c->c_keytype); 47276259Sgreen confree(s); 47376259Sgreen return (ret); 47476259Sgreen} 47576259Sgreen 47692555Sdesstatic void 47776259Sgreencongreet(int s) 47876259Sgreen{ 479149753Sdes int n = 0, remote_major = 0, remote_minor = 0; 48092555Sdes char buf[256], *cp; 48192555Sdes char remote_version[sizeof buf]; 48276259Sgreen size_t bufsiz; 48376259Sgreen con *c = &fdcon[s]; 48476259Sgreen 485157019Sdes for (;;) { 486157019Sdes memset(buf, '\0', sizeof(buf)); 487157019Sdes bufsiz = sizeof(buf); 488157019Sdes cp = buf; 489157019Sdes while (bufsiz-- && 490157019Sdes (n = atomicio(read, s, cp, 1)) == 1 && *cp != '\n') { 491157019Sdes if (*cp == '\r') 492157019Sdes *cp = '\n'; 493157019Sdes cp++; 494157019Sdes } 495157019Sdes if (n != 1 || strncmp(buf, "SSH-", 4) == 0) 496157019Sdes break; 49792555Sdes } 498149753Sdes if (n == 0) { 499149753Sdes switch (errno) { 500149753Sdes case EPIPE: 501149753Sdes error("%s: Connection closed by remote host", c->c_name); 502149753Sdes break; 503149753Sdes case ECONNREFUSED: 504149753Sdes break; 505149753Sdes default: 50676259Sgreen error("read (%s): %s", c->c_name, strerror(errno)); 507149753Sdes break; 508149753Sdes } 50976259Sgreen conrecycle(s); 51076259Sgreen return; 51176259Sgreen } 51276259Sgreen if (*cp != '\n' && *cp != '\r') { 51376259Sgreen error("%s: bad greeting", c->c_name); 51476259Sgreen confree(s); 51576259Sgreen return; 51676259Sgreen } 51776259Sgreen *cp = '\0'; 518294332Sdes if ((c->c_ssh = ssh_packet_set_connection(NULL, s, s)) == NULL) 519294332Sdes fatal("ssh_packet_set_connection failed"); 520294332Sdes ssh_packet_set_timeout(c->c_ssh, timeout, 1); 521294332Sdes ssh_set_app_data(c->c_ssh, c); /* back link */ 52292555Sdes if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", 52392555Sdes &remote_major, &remote_minor, remote_version) == 3) 524294332Sdes c->c_ssh->compat = compat_datafellows(remote_version); 52592555Sdes else 526294332Sdes c->c_ssh->compat = 0; 52792555Sdes if (c->c_keytype != KT_RSA1) { 52892555Sdes if (!ssh2_capable(remote_major, remote_minor)) { 52992555Sdes debug("%s doesn't support ssh2", c->c_name); 53092555Sdes confree(s); 53192555Sdes return; 53292555Sdes } 53392555Sdes } else if (remote_major != 1) { 53492555Sdes debug("%s doesn't support ssh1", c->c_name); 53592555Sdes confree(s); 53692555Sdes return; 53792555Sdes } 538294336Sdes fprintf(stderr, "# %s:%d %s\n", c->c_name, ssh_port, chop(buf)); 53992555Sdes n = snprintf(buf, sizeof buf, "SSH-%d.%d-OpenSSH-keyscan\r\n", 54092555Sdes c->c_keytype == KT_RSA1? PROTOCOL_MAJOR_1 : PROTOCOL_MAJOR_2, 54192555Sdes c->c_keytype == KT_RSA1? PROTOCOL_MINOR_1 : PROTOCOL_MINOR_2); 542149753Sdes if (n < 0 || (size_t)n >= sizeof(buf)) { 543149753Sdes error("snprintf: buffer too small"); 544149753Sdes confree(s); 545149753Sdes return; 546149753Sdes } 547149753Sdes if (atomicio(vwrite, s, buf, n) != (size_t)n) { 54876259Sgreen error("write (%s): %s", c->c_name, strerror(errno)); 54976259Sgreen confree(s); 55076259Sgreen return; 55176259Sgreen } 55292555Sdes if (c->c_keytype != KT_RSA1) { 553294332Sdes keygrab_ssh2(c); 55492555Sdes confree(s); 55592555Sdes return; 55692555Sdes } 55776259Sgreen c->c_status = CS_SIZE; 55876259Sgreen contouch(s); 55976259Sgreen} 56076259Sgreen 56192555Sdesstatic void 56276259Sgreenconread(int s) 56376259Sgreen{ 564106130Sdes con *c = &fdcon[s]; 565149753Sdes size_t n; 56676259Sgreen 56776259Sgreen if (c->c_status == CS_CON) { 56876259Sgreen congreet(s); 56976259Sgreen return; 57076259Sgreen } 571137019Sdes n = atomicio(read, s, c->c_data + c->c_off, c->c_len - c->c_off); 572149753Sdes if (n == 0) { 57376259Sgreen error("read (%s): %s", c->c_name, strerror(errno)); 57476259Sgreen confree(s); 57576259Sgreen return; 57676259Sgreen } 57776259Sgreen c->c_off += n; 57876259Sgreen 57976259Sgreen if (c->c_off == c->c_len) 58076259Sgreen switch (c->c_status) { 58176259Sgreen case CS_SIZE: 58276259Sgreen c->c_plen = htonl(c->c_plen); 58376259Sgreen c->c_len = c->c_plen + 8 - (c->c_plen & 7); 58476259Sgreen c->c_off = 0; 58576259Sgreen c->c_data = xmalloc(c->c_len); 58676259Sgreen c->c_status = CS_KEYS; 58776259Sgreen break; 588294328Sdes#ifdef WITH_SSH1 58976259Sgreen case CS_KEYS: 59092555Sdes keyprint(c, keygrab_ssh1(c)); 59176259Sgreen confree(s); 59276259Sgreen return; 593294328Sdes#endif 59476259Sgreen default: 59576259Sgreen fatal("conread: invalid status %d", c->c_status); 59676259Sgreen break; 59776259Sgreen } 59876259Sgreen 59976259Sgreen contouch(s); 60076259Sgreen} 60176259Sgreen 60292555Sdesstatic void 60376259Sgreenconloop(void) 60476259Sgreen{ 605106130Sdes struct timeval seltime, now; 60676259Sgreen fd_set *r, *e; 607106130Sdes con *c; 60876259Sgreen int i; 60976259Sgreen 61076259Sgreen gettimeofday(&now, NULL); 61198675Sdes c = TAILQ_FIRST(&tq); 61276259Sgreen 61376259Sgreen if (c && (c->c_tv.tv_sec > now.tv_sec || 61476259Sgreen (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec > now.tv_usec))) { 61576259Sgreen seltime = c->c_tv; 61676259Sgreen seltime.tv_sec -= now.tv_sec; 61776259Sgreen seltime.tv_usec -= now.tv_usec; 61876259Sgreen if (seltime.tv_usec < 0) { 61976259Sgreen seltime.tv_usec += 1000000; 62076259Sgreen seltime.tv_sec--; 62176259Sgreen } 62276259Sgreen } else 623226046Sdes timerclear(&seltime); 62476259Sgreen 625162856Sdes r = xcalloc(read_wait_nfdset, sizeof(fd_mask)); 626162856Sdes e = xcalloc(read_wait_nfdset, sizeof(fd_mask)); 627162856Sdes memcpy(r, read_wait, read_wait_nfdset * sizeof(fd_mask)); 628162856Sdes memcpy(e, read_wait, read_wait_nfdset * sizeof(fd_mask)); 62976259Sgreen 63076259Sgreen while (select(maxfd, r, NULL, e, &seltime) == -1 && 631181111Sdes (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) 63276259Sgreen ; 63376259Sgreen 63476259Sgreen for (i = 0; i < maxfd; i++) { 63576259Sgreen if (FD_ISSET(i, e)) { 63676259Sgreen error("%s: exception!", fdcon[i].c_name); 63776259Sgreen confree(i); 63876259Sgreen } else if (FD_ISSET(i, r)) 63976259Sgreen conread(i); 64076259Sgreen } 641255767Sdes free(r); 642255767Sdes free(e); 64376259Sgreen 64498675Sdes c = TAILQ_FIRST(&tq); 64576259Sgreen while (c && (c->c_tv.tv_sec < now.tv_sec || 64676259Sgreen (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec < now.tv_usec))) { 64776259Sgreen int s = c->c_fd; 64876259Sgreen 64998675Sdes c = TAILQ_NEXT(c, c_link); 65076259Sgreen conrecycle(s); 65176259Sgreen } 65276259Sgreen} 65376259Sgreen 65492555Sdesstatic void 65592555Sdesdo_host(char *host) 65676259Sgreen{ 65792555Sdes char *name = strnnsep(&host, " \t\n"); 65892555Sdes int j; 65976259Sgreen 66092555Sdes if (name == NULL) 66192555Sdes return; 662261320Sdes for (j = KT_RSA1; j <= KT_ED25519; j *= 2) { 66392555Sdes if (get_keytypes & j) { 66492555Sdes while (ncon >= MAXCON) 66592555Sdes conloop(); 66692555Sdes conalloc(name, *host ? host : name, j); 66776259Sgreen } 66876259Sgreen } 66976259Sgreen} 67076259Sgreen 67176259Sgreenvoid 67292555Sdesfatal(const char *fmt,...) 67392555Sdes{ 67492555Sdes va_list args; 675106130Sdes 67692555Sdes va_start(args, fmt); 67792555Sdes do_log(SYSLOG_LEVEL_FATAL, fmt, args); 67892555Sdes va_end(args); 679294332Sdes exit(255); 68092555Sdes} 68192555Sdes 68292555Sdesstatic void 68376259Sgreenusage(void) 68476259Sgreen{ 685192595Sdes fprintf(stderr, 686296633Sdes "usage: %s [-46cHv] [-f file] [-p port] [-T timeout] [-t type]\n" 687192595Sdes "\t\t [host | addrlist namelist] ...\n", 68892555Sdes __progname); 68992555Sdes exit(1); 69076259Sgreen} 69176259Sgreen 69276259Sgreenint 69376259Sgreenmain(int argc, char **argv) 69476259Sgreen{ 69592555Sdes int debug_flag = 0, log_level = SYSLOG_LEVEL_INFO; 696215116Sdes int opt, fopt_count = 0, j; 697215116Sdes char *tname, *cp, line[NI_MAXHOST]; 698215116Sdes FILE *fp; 699215116Sdes u_long linenum; 70076259Sgreen 70192555Sdes extern int optind; 70292555Sdes extern char *optarg; 70392555Sdes 704296633Sdes ssh_malloc_init(); /* must be called before any mallocs */ 705124211Sdes __progname = ssh_get_progname(argv[0]); 70698941Sdes seed_rng(); 70776259Sgreen TAILQ_INIT(&tq); 70876259Sgreen 709157019Sdes /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 710157019Sdes sanitise_stdfd(); 711157019Sdes 71292555Sdes if (argc <= 1) 71376259Sgreen usage(); 71476259Sgreen 715296633Sdes while ((opt = getopt(argc, argv, "cHv46p:T:t:f:")) != -1) { 71692555Sdes switch (opt) { 717147005Sdes case 'H': 718147005Sdes hash_hosts = 1; 719147005Sdes break; 720296633Sdes case 'c': 721296633Sdes get_cert = 1; 722296633Sdes break; 72392555Sdes case 'p': 72492555Sdes ssh_port = a2port(optarg); 725192595Sdes if (ssh_port <= 0) { 72692555Sdes fprintf(stderr, "Bad port '%s'\n", optarg); 72792555Sdes exit(1); 72892555Sdes } 72992555Sdes break; 73092555Sdes case 'T': 731106130Sdes timeout = convtime(optarg); 732106130Sdes if (timeout == -1 || timeout == 0) { 733106130Sdes fprintf(stderr, "Bad timeout '%s'\n", optarg); 73476259Sgreen usage(); 735106130Sdes } 73692555Sdes break; 73792555Sdes case 'v': 73892555Sdes if (!debug_flag) { 73992555Sdes debug_flag = 1; 74092555Sdes log_level = SYSLOG_LEVEL_DEBUG1; 74192555Sdes } 74292555Sdes else if (log_level < SYSLOG_LEVEL_DEBUG3) 74392555Sdes log_level++; 74492555Sdes else 74592555Sdes fatal("Too high debugging level."); 74692555Sdes break; 74792555Sdes case 'f': 74892555Sdes if (strcmp(optarg, "-") == 0) 74992555Sdes optarg = NULL; 75092555Sdes argv[fopt_count++] = optarg; 75192555Sdes break; 75292555Sdes case 't': 75392555Sdes get_keytypes = 0; 75492555Sdes tname = strtok(optarg, ","); 75592555Sdes while (tname) { 756294332Sdes int type = sshkey_type_from_name(tname); 757323136Sdes 75892555Sdes switch (type) { 759323136Sdes#ifdef WITH_SSH1 76092555Sdes case KEY_RSA1: 76192555Sdes get_keytypes |= KT_RSA1; 76292555Sdes break; 763323136Sdes#endif 76492555Sdes case KEY_DSA: 76592555Sdes get_keytypes |= KT_DSA; 76692555Sdes break; 767221420Sdes case KEY_ECDSA: 768221420Sdes get_keytypes |= KT_ECDSA; 769221420Sdes break; 77092555Sdes case KEY_RSA: 77192555Sdes get_keytypes |= KT_RSA; 77292555Sdes break; 773261320Sdes case KEY_ED25519: 774261320Sdes get_keytypes |= KT_ED25519; 775261320Sdes break; 77692555Sdes case KEY_UNSPEC: 777323136Sdes default: 778323136Sdes fatal("Unknown key type \"%s\"", tname); 77992555Sdes } 78092555Sdes tname = strtok(NULL, ","); 78192555Sdes } 78292555Sdes break; 78392555Sdes case '4': 78492555Sdes IPv4or6 = AF_INET; 78592555Sdes break; 78692555Sdes case '6': 78792555Sdes IPv4or6 = AF_INET6; 78892555Sdes break; 78992555Sdes case '?': 79092555Sdes default: 79192555Sdes usage(); 79276259Sgreen } 79376259Sgreen } 79492555Sdes if (optind == argc && !fopt_count) 79576259Sgreen usage(); 79676259Sgreen 79792555Sdes log_init("ssh-keyscan", log_level, SYSLOG_FACILITY_USER, 1); 79892555Sdes 79976259Sgreen maxfd = fdlim_get(1); 80076259Sgreen if (maxfd < 0) 80176259Sgreen fatal("%s: fdlim_get: bad value", __progname); 80276259Sgreen if (maxfd > MAXMAXFD) 80376259Sgreen maxfd = MAXMAXFD; 80476259Sgreen if (MAXCON <= 0) 80576259Sgreen fatal("%s: not enough file descriptors", __progname); 80676259Sgreen if (maxfd > fdlim_get(0)) 80776259Sgreen fdlim_set(maxfd); 808162856Sdes fdcon = xcalloc(maxfd, sizeof(con)); 80976259Sgreen 810162856Sdes read_wait_nfdset = howmany(maxfd, NFDBITS); 811162856Sdes read_wait = xcalloc(read_wait_nfdset, sizeof(fd_mask)); 81276259Sgreen 813215116Sdes for (j = 0; j < fopt_count; j++) { 814215116Sdes if (argv[j] == NULL) 815215116Sdes fp = stdin; 816215116Sdes else if ((fp = fopen(argv[j], "r")) == NULL) 817215116Sdes fatal("%s: %s: %s", __progname, argv[j], 818215116Sdes strerror(errno)); 819215116Sdes linenum = 0; 82076259Sgreen 821215116Sdes while (read_keyfile_line(fp, 822215116Sdes argv[j] == NULL ? "(stdin)" : argv[j], line, sizeof(line), 823215116Sdes &linenum) != -1) { 824215116Sdes /* Chomp off trailing whitespace and comments */ 825215116Sdes if ((cp = strchr(line, '#')) == NULL) 826215116Sdes cp = line + strlen(line) - 1; 827215116Sdes while (cp >= line) { 828215116Sdes if (*cp == ' ' || *cp == '\t' || 829215116Sdes *cp == '\n' || *cp == '#') 830215116Sdes *cp-- = '\0'; 831215116Sdes else 832215116Sdes break; 833215116Sdes } 834215116Sdes 835215116Sdes /* Skip empty lines */ 836215116Sdes if (*line == '\0') 83792555Sdes continue; 838215116Sdes 839215116Sdes do_host(line); 84076259Sgreen } 841215116Sdes 842215116Sdes if (ferror(fp)) 843215116Sdes fatal("%s: %s: %s", __progname, argv[j], 844215116Sdes strerror(errno)); 845215116Sdes 846215116Sdes fclose(fp); 84792555Sdes } 84892555Sdes 84992555Sdes while (optind < argc) 85092555Sdes do_host(argv[optind++]); 85192555Sdes 85276259Sgreen while (ncon > 0) 85376259Sgreen conloop(); 85476259Sgreen 85576259Sgreen return (0); 85676259Sgreen} 857