ssh-keyscan.c revision 237568
1193323Sed/* $OpenBSD: ssh-keyscan.c,v 1.86 2012/04/11 13:34:17 djm Exp $ */ 2193323Sed/* 3193323Sed * Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>. 4193323Sed * 5193323Sed * Modification and redistribution in source and binary forms is 6193323Sed * permitted provided that due credit is given to the author and the 7193323Sed * OpenBSD project by leaving this copyright notice intact. 8193323Sed */ 9193323Sed 10193323Sed#include "includes.h" 11193323Sed 12193323Sed#include "openbsd-compat/sys-queue.h" 13193323Sed#include <sys/resource.h> 14193323Sed#ifdef HAVE_SYS_TIME_H 15193323Sed# include <sys/time.h> 16193323Sed#endif 17193323Sed 18193323Sed#include <netinet/in.h> 19193323Sed#include <arpa/inet.h> 20249423Sdim 21249423Sdim#include <openssl/bn.h> 22193323Sed 23198090Srdivacky#include <netdb.h> 24193323Sed#include <errno.h> 25193323Sed#include <setjmp.h> 26193323Sed#include <stdarg.h> 27193323Sed#include <stdio.h> 28249423Sdim#include <stdlib.h> 29193323Sed#include <signal.h> 30193323Sed#include <string.h> 31193323Sed#include <unistd.h> 32193323Sed 33201360Srdivacky#include "xmalloc.h" 34193323Sed#include "ssh.h" 35206083Srdivacky#include "ssh1.h" 36201360Srdivacky#include "buffer.h" 37205218Srdivacky#include "key.h" 38201360Srdivacky#include "cipher.h" 39208599Srdivacky#include "kex.h" 40249423Sdim#include "compat.h" 41193323Sed#include "myproposal.h" 42193323Sed#include "packet.h" 43193323Sed#include "dispatch.h" 44198090Srdivacky#include "log.h" 45193323Sed#include "atomicio.h" 46193323Sed#include "misc.h" 47193323Sed#include "hostfile.h" 48193323Sed 49193323Sed/* Flag indicating whether IPv4 or IPv6. This can be set on the command line. 50193323Sed Default value is AF_UNSPEC means both IPv4 and IPv6. */ 51193323Sedint IPv4or6 = AF_UNSPEC; 52193323Sed 53193323Sedint ssh_port = SSH_DEFAULT_PORT; 54193323Sed 55193323Sed#define KT_RSA1 1 56234353Sdim#define KT_DSA 2 57193323Sed#define KT_RSA 4 58193323Sed#define KT_ECDSA 8 59193323Sed 60193323Sedint get_keytypes = KT_RSA|KT_ECDSA;/* Get RSA and ECDSA keys by default */ 61193323Sed 62205218Srdivackyint hash_hosts = 0; /* Hash hostname on output */ 63205218Srdivacky 64206083Srdivacky#define MAXMAXFD 256 65206083Srdivacky 66206083Srdivacky/* The number of seconds after which to give up on a TCP connection */ 67207618Srdivackyint timeout = 5; 68207618Srdivacky 69207618Srdivackyint maxfd; 70207618Srdivacky#define MAXCON (maxfd - 10) 71207618Srdivacky 72207618Srdivackyextern char *__progname; 73205218Srdivackyfd_set *read_wait; 74206083Srdivackysize_t read_wait_nfdset; 75207618Srdivackyint ncon; 76206083Srdivackyint nonfatal_fatal = 0; 77205218Srdivackyjmp_buf kexjmp; 78243830SdimKey *kexjmp_key; 79243830Sdim 80205218Srdivacky/* 81205218Srdivacky * Keep a connection structure for each file descriptor. The state 82205218Srdivacky * associated with file descriptor n is held in fdcon[n]. 83207618Srdivacky */ 84207618Srdivackytypedef struct Connection { 85207618Srdivacky u_char c_status; /* State of connection on this file desc. */ 86207618Srdivacky#define CS_UNUSED 0 /* File descriptor unused */ 87206083Srdivacky#define CS_CON 1 /* Waiting to connect/read greeting */ 88206083Srdivacky#define CS_SIZE 2 /* Waiting to read initial packet size */ 89205218Srdivacky#define CS_KEYS 3 /* Waiting to read public key packet */ 90206083Srdivacky int c_fd; /* Quick lookup: c->c_fd == c - fdcon */ 91205218Srdivacky int c_plen; /* Packet length field for ssh packet */ 92206083Srdivacky int c_len; /* Total bytes which must be read. */ 93206083Srdivacky int c_off; /* Length of data read so far. */ 94207618Srdivacky int c_keytype; /* Only one of KT_RSA1, KT_DSA, or KT_RSA */ 95205218Srdivacky char *c_namebase; /* Address to free for c_name and c_namelist */ 96206083Srdivacky char *c_name; /* Hostname of connection for errors */ 97206083Srdivacky char *c_namelist; /* Pointer to other possible addresses */ 98207618Srdivacky char *c_output_name; /* Hostname of connection for output */ 99205218Srdivacky char *c_data; /* Data read from this fd */ 100206083Srdivacky Kex *c_kex; /* The key-exchange struct for ssh2 */ 101224145Sdim struct timeval c_tv; /* Time at which connection gets aborted */ 102224145Sdim TAILQ_ENTRY(Connection) c_link; /* List of connections in timeout order. */ 103224145Sdim} con; 104224145Sdim 105224145SdimTAILQ_HEAD(conlist, Connection) tq; /* Timeout Queue */ 106224145Sdimcon *fdcon; 107206083Srdivacky 108206083Srdivackystatic int 109206083Srdivackyfdlim_get(int hard) 110206083Srdivacky{ 111206083Srdivacky#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) 112207618Srdivacky struct rlimit rlfd; 113207618Srdivacky 114205218Srdivacky if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0) 115205218Srdivacky return (-1); 116202878Srdivacky if ((hard ? rlfd.rlim_max : rlfd.rlim_cur) == RLIM_INFINITY) 117202878Srdivacky return SSH_SYSFDMAX; 118202878Srdivacky else 119202878Srdivacky return hard ? rlfd.rlim_max : rlfd.rlim_cur; 120193323Sed#else 121193323Sed return SSH_SYSFDMAX; 122193323Sed#endif 123193323Sed} 124193323Sed 125193323Sedstatic int 126193323Sedfdlim_set(int lim) 127193323Sed{ 128193323Sed#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) 129193323Sed struct rlimit rlfd; 130193323Sed#endif 131193323Sed 132207618Srdivacky if (lim <= 0) 133207618Srdivacky return (-1); 134208599Srdivacky#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) 135249423Sdim if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0) 136193323Sed return (-1); 137206274Srdivacky rlfd.rlim_cur = lim; 138234353Sdim if (setrlimit(RLIMIT_NOFILE, &rlfd) < 0) 139193323Sed return (-1); 140193323Sed#elif defined (HAVE_SETDTABLESIZE) 141193323Sed setdtablesize(lim); 142193323Sed#endif 143193323Sed return (0); 144193323Sed} 145193323Sed 146193323Sed/* 147193323Sed * This is an strsep function that returns a null field for adjacent 148193323Sed * separators. This is the same as the 4.4BSD strsep, but different from the 149193323Sed * one in the GNU libc. 150193323Sed */ 151193323Sedstatic char * 152193323Sedxstrsep(char **str, const char *delim) 153193323Sed{ 154193323Sed char *s, *e; 155193323Sed 156193323Sed if (!**str) 157193323Sed return (NULL); 158193323Sed 159198090Srdivacky s = *str; 160193323Sed e = s + strcspn(s, delim); 161193323Sed 162193323Sed if (*e != '\0') 163193323Sed *e++ = '\0'; 164193323Sed *str = e; 165193323Sed 166193323Sed return (s); 167193323Sed} 168193323Sed 169201360Srdivacky/* 170201360Srdivacky * Get the next non-null token (like GNU strsep). Strsep() will return a 171201360Srdivacky * null token for two adjacent separators, so we may have to loop. 172200581Srdivacky */ 173205218Srdivackystatic char * 174205218Srdivackystrnnsep(char **stringp, char *delim) 175205218Srdivacky{ 176239462Sdim char *tok; 177239462Sdim 178239462Sdim do { 179239462Sdim tok = xstrsep(stringp, delim); 180239462Sdim } while (tok && *tok == '\0'); 181239462Sdim return (tok); 182239462Sdim} 183239462Sdim 184239462Sdimstatic Key * 185239462Sdimkeygrab_ssh1(con *c) 186239462Sdim{ 187239462Sdim static Key *rsa; 188239462Sdim static Buffer msg; 189239462Sdim 190239462Sdim if (rsa == NULL) { 191239462Sdim buffer_init(&msg); 192239462Sdim rsa = key_new(KEY_RSA1); 193239462Sdim } 194239462Sdim buffer_append(&msg, c->c_data, c->c_plen); 195239462Sdim buffer_consume(&msg, 8 - (c->c_plen & 7)); /* padding */ 196239462Sdim if (buffer_get_char(&msg) != (int) SSH_SMSG_PUBLIC_KEY) { 197239462Sdim error("%s: invalid packet type", c->c_name); 198239462Sdim buffer_clear(&msg); 199239462Sdim return NULL; 200239462Sdim } 201239462Sdim buffer_consume(&msg, 8); /* cookie */ 202239462Sdim 203239462Sdim /* server key */ 204239462Sdim (void) buffer_get_int(&msg); 205239462Sdim buffer_get_bignum(&msg, rsa->rsa->e); 206239462Sdim buffer_get_bignum(&msg, rsa->rsa->n); 207239462Sdim 208239462Sdim /* host key */ 209239462Sdim (void) buffer_get_int(&msg); 210239462Sdim buffer_get_bignum(&msg, rsa->rsa->e); 211239462Sdim buffer_get_bignum(&msg, rsa->rsa->n); 212239462Sdim 213239462Sdim buffer_clear(&msg); 214193323Sed 215193323Sed return (rsa); 216193323Sed} 217193323Sed 218193323Sedstatic int 219193323Sedhostjump(Key *hostkey) 220193323Sed{ 221243830Sdim kexjmp_key = hostkey; 222243830Sdim longjmp(kexjmp, 1); 223200581Srdivacky} 224193323Sed 225234353Sdimstatic int 226193323Sedssh2_capable(int remote_major, int remote_minor) 227193323Sed{ 228193323Sed switch (remote_major) { 229193323Sed case 1: 230193323Sed if (remote_minor == 99) 231249423Sdim return 1; 232193323Sed break; 233193323Sed case 2: 234193323Sed return 1; 235193323Sed default: 236193323Sed break; 237193323Sed } 238193323Sed return 0; 239207618Srdivacky} 240207618Srdivacky 241208599Srdivackystatic Key * 242249423Sdimkeygrab_ssh2(con *c) 243198090Srdivacky{ 244193323Sed int j; 245193323Sed 246193323Sed packet_set_connection(c->c_fd, c->c_fd); 247193323Sed enable_compat20(); 248193323Sed myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = c->c_keytype == KT_DSA? 249193323Sed "ssh-dss" : (c->c_keytype == KT_RSA ? "ssh-rsa" : 250193323Sed "ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521"); 251193323Sed c->c_kex = kex_setup(myproposal); 252193323Sed c->c_kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; 253193323Sed c->c_kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; 254193323Sed c->c_kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; 255193323Sed c->c_kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; 256193323Sed c->c_kex->kex[KEX_ECDH_SHA2] = kexecdh_client; 257193323Sed c->c_kex->verify_host_key = hostjump; 258193323Sed 259193323Sed if (!(j = setjmp(kexjmp))) { 260193323Sed nonfatal_fatal = 1; 261193323Sed dispatch_run(DISPATCH_BLOCK, &c->c_kex->done, c->c_kex); 262193323Sed fprintf(stderr, "Impossible! dispatch_run() returned!\n"); 263193323Sed exit(1); 264193323Sed } 265193323Sed nonfatal_fatal = 0; 266193323Sed xfree(c->c_kex); 267193323Sed c->c_kex = NULL; 268193323Sed packet_close(); 269193323Sed 270193323Sed return j < 0? NULL : kexjmp_key; 271193323Sed} 272193323Sed 273193323Sedstatic void 274193323Sedkeyprint(con *c, Key *key) 275193323Sed{ 276193323Sed char *host = c->c_output_name ? c->c_output_name : c->c_name; 277193323Sed 278193323Sed if (!key) 279193323Sed return; 280193323Sed if (hash_hosts && (host = host_hash(host, NULL, 0)) == NULL) 281193323Sed fatal("host_hash failed"); 282193323Sed 283193323Sed fprintf(stdout, "%s ", host); 284193323Sed key_write(key, stdout); 285193323Sed fputs("\n", stdout); 286193323Sed} 287193323Sed 288193323Sedstatic int 289193323Sedtcpconnect(char *host) 290193323Sed{ 291193323Sed struct addrinfo hints, *ai, *aitop; 292193323Sed char strport[NI_MAXSERV]; 293193323Sed int gaierr, s = -1; 294193323Sed 295193323Sed snprintf(strport, sizeof strport, "%d", ssh_port); 296193323Sed memset(&hints, 0, sizeof(hints)); 297193323Sed hints.ai_family = IPv4or6; 298193323Sed hints.ai_socktype = SOCK_STREAM; 299202878Srdivacky if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) 300202878Srdivacky fatal("getaddrinfo %s: %s", host, ssh_gai_strerror(gaierr)); 301202878Srdivacky for (ai = aitop; ai; ai = ai->ai_next) { 302202878Srdivacky s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 303202878Srdivacky if (s < 0) { 304202878Srdivacky error("socket: %s", strerror(errno)); 305193323Sed continue; 306193323Sed } 307193323Sed if (set_nonblock(s) == -1) 308193323Sed fatal("%s: set_nonblock(%d)", __func__, s); 309193323Sed if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0 && 310193323Sed errno != EINPROGRESS) 311193323Sed error("connect (`%s'): %s", host, strerror(errno)); 312193323Sed else 313193323Sed break; 314193323Sed close(s); 315193323Sed s = -1; 316193323Sed } 317193323Sed freeaddrinfo(aitop); 318193323Sed return s; 319193323Sed} 320193323Sed 321193323Sedstatic int 322193323Sedconalloc(char *iname, char *oname, int keytype) 323193323Sed{ 324193323Sed char *namebase, *name, *namelist; 325193323Sed int s; 326193323Sed 327193323Sed namebase = namelist = xstrdup(iname); 328223017Sdim 329193323Sed do { 330193323Sed name = xstrsep(&namelist, ","); 331193323Sed if (!name) { 332193323Sed xfree(namebase); 333193323Sed return (-1); 334193323Sed } 335193323Sed } while ((s = tcpconnect(name)) < 0); 336193323Sed 337193323Sed if (s >= maxfd) 338193323Sed fatal("conalloc: fdno %d too high", s); 339193323Sed if (fdcon[s].c_status) 340193323Sed fatal("conalloc: attempt to reuse fdno %d", s); 341193323Sed 342193323Sed fdcon[s].c_fd = s; 343193323Sed fdcon[s].c_status = CS_CON; 344193323Sed fdcon[s].c_namebase = namebase; 345193323Sed fdcon[s].c_name = name; 346193323Sed fdcon[s].c_namelist = namelist; 347193323Sed fdcon[s].c_output_name = xstrdup(oname); 348193323Sed fdcon[s].c_data = (char *) &fdcon[s].c_plen; 349193323Sed fdcon[s].c_len = 4; 350193323Sed fdcon[s].c_off = 0; 351193323Sed fdcon[s].c_keytype = keytype; 352193323Sed gettimeofday(&fdcon[s].c_tv, NULL); 353198090Srdivacky fdcon[s].c_tv.tv_sec += timeout; 354198090Srdivacky TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); 355198090Srdivacky FD_SET(s, read_wait); 356198090Srdivacky ncon++; 357198090Srdivacky return (s); 358193323Sed} 359193323Sed 360193323Sedstatic void 361193323Sedconfree(int s) 362198090Srdivacky{ 363198090Srdivacky if (s >= maxfd || fdcon[s].c_status == CS_UNUSED) 364198090Srdivacky fatal("confree: attempt to free bad fdno %d", s); 365193323Sed close(s); 366198090Srdivacky xfree(fdcon[s].c_namebase); 367193323Sed xfree(fdcon[s].c_output_name); 368193323Sed if (fdcon[s].c_status == CS_KEYS) 369198090Srdivacky xfree(fdcon[s].c_data); 370193323Sed fdcon[s].c_status = CS_UNUSED; 371193323Sed fdcon[s].c_keytype = 0; 372198090Srdivacky TAILQ_REMOVE(&tq, &fdcon[s], c_link); 373193323Sed FD_CLR(s, read_wait); 374193323Sed ncon--; 375208599Srdivacky} 376208599Srdivacky 377198090Srdivackystatic void 378198090Srdivackycontouch(int s) 379198090Srdivacky{ 380198090Srdivacky TAILQ_REMOVE(&tq, &fdcon[s], c_link); 381193323Sed gettimeofday(&fdcon[s].c_tv, NULL); 382193323Sed fdcon[s].c_tv.tv_sec += timeout; 383198090Srdivacky TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); 384193323Sed} 385193323Sed 386198090Srdivackystatic int 387193323Sedconrecycle(int s) 388193323Sed{ 389210299Sed con *c = &fdcon[s]; 390195098Sed int ret; 391195098Sed 392210299Sed ret = conalloc(c->c_namelist, c->c_output_name, c->c_keytype); 393195098Sed confree(s); 394195098Sed return (ret); 395210299Sed} 396193323Sed 397198090Srdivackystatic void 398198090Srdivackycongreet(int s) 399193323Sed{ 400193323Sed int n = 0, remote_major = 0, remote_minor = 0; 401198090Srdivacky char buf[256], *cp; 402195098Sed char remote_version[sizeof buf]; 403198090Srdivacky size_t bufsiz; 404195098Sed con *c = &fdcon[s]; 405193323Sed 406207618Srdivacky for (;;) { 407195098Sed memset(buf, '\0', sizeof(buf)); 408195098Sed bufsiz = sizeof(buf); 409207618Srdivacky cp = buf; 410195098Sed while (bufsiz-- && 411195098Sed (n = atomicio(read, s, cp, 1)) == 1 && *cp != '\n') { 412195098Sed if (*cp == '\r') 413193323Sed *cp = '\n'; 414198090Srdivacky cp++; 415195098Sed } 416195098Sed if (n != 1 || strncmp(buf, "SSH-", 4) == 0) 417193323Sed break; 418198090Srdivacky } 419195098Sed if (n == 0) { 420195098Sed switch (errno) { 421193323Sed case EPIPE: 422239462Sdim error("%s: Connection closed by remote host", c->c_name); 423239462Sdim break; 424193323Sed case ECONNREFUSED: 425193323Sed break; 426193323Sed default: 427193323Sed error("read (%s): %s", c->c_name, strerror(errno)); 428198090Srdivacky break; 429198090Srdivacky } 430198090Srdivacky conrecycle(s); 431195098Sed return; 432198090Srdivacky } 433198090Srdivacky if (*cp != '\n' && *cp != '\r') { 434234353Sdim error("%s: bad greeting", c->c_name); 435205218Srdivacky confree(s); 436207618Srdivacky return; 437243830Sdim } 438243830Sdim *cp = '\0'; 439243830Sdim if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", 440243830Sdim &remote_major, &remote_minor, remote_version) == 3) 441243830Sdim compat_datafellows(remote_version); 442243830Sdim else 443243830Sdim datafellows = 0; 444193323Sed if (c->c_keytype != KT_RSA1) { 445193323Sed if (!ssh2_capable(remote_major, remote_minor)) { 446193323Sed debug("%s doesn't support ssh2", c->c_name); 447193323Sed confree(s); 448193323Sed return; 449193323Sed } 450193323Sed } else if (remote_major != 1) { 451218893Sdim debug("%s doesn't support ssh1", c->c_name); 452218893Sdim confree(s); 453193323Sed return; 454218893Sdim } 455218893Sdim fprintf(stderr, "# %s %s\n", c->c_name, chop(buf)); 456218893Sdim n = snprintf(buf, sizeof buf, "SSH-%d.%d-OpenSSH-keyscan\r\n", 457218893Sdim c->c_keytype == KT_RSA1? PROTOCOL_MAJOR_1 : PROTOCOL_MAJOR_2, 458193323Sed c->c_keytype == KT_RSA1? PROTOCOL_MINOR_1 : PROTOCOL_MINOR_2); 459193323Sed if (n < 0 || (size_t)n >= sizeof(buf)) { 460193323Sed error("snprintf: buffer too small"); 461193323Sed confree(s); 462218893Sdim return; 463218893Sdim } 464218893Sdim if (atomicio(vwrite, s, buf, n) != (size_t)n) { 465218893Sdim error("write (%s): %s", c->c_name, strerror(errno)); 466193323Sed confree(s); 467193323Sed return; 468198090Srdivacky } 469193323Sed if (c->c_keytype != KT_RSA1) { 470193323Sed keyprint(c, keygrab_ssh2(c)); 471193323Sed confree(s); 472193323Sed return; 473193323Sed } 474193323Sed c->c_status = CS_SIZE; 475218893Sdim contouch(s); 476218893Sdim} 477198090Srdivacky 478218893Sdimstatic void 479218893Sdimconread(int s) 480218893Sdim{ 481218893Sdim con *c = &fdcon[s]; 482193323Sed size_t n; 483193323Sed 484193323Sed if (c->c_status == CS_CON) { 485193323Sed congreet(s); 486193323Sed return; 487193323Sed } 488198090Srdivacky n = atomicio(read, s, c->c_data + c->c_off, c->c_len - c->c_off); 489193323Sed if (n == 0) { 490193323Sed error("read (%s): %s", c->c_name, strerror(errno)); 491221345Sdim confree(s); 492193323Sed return; 493193323Sed } 494193323Sed c->c_off += n; 495193323Sed 496221345Sdim if (c->c_off == c->c_len) 497193323Sed switch (c->c_status) { 498193323Sed case CS_SIZE: 499226633Sdim c->c_plen = htonl(c->c_plen); 500226633Sdim c->c_len = c->c_plen + 8 - (c->c_plen & 7); 501226633Sdim c->c_off = 0; 502226633Sdim c->c_data = xmalloc(c->c_len); 503198090Srdivacky c->c_status = CS_KEYS; 504198090Srdivacky break; 505198090Srdivacky case CS_KEYS: 506198090Srdivacky keyprint(c, keygrab_ssh1(c)); 507198090Srdivacky confree(s); 508198090Srdivacky return; 509198090Srdivacky default: 510198090Srdivacky fatal("conread: invalid status %d", c->c_status); 511193323Sed break; 512193323Sed } 513198090Srdivacky 514193323Sed contouch(s); 515193323Sed} 516198090Srdivacky 517193323Sedstatic void 518193323Sedconloop(void) 519218893Sdim{ 520193323Sed struct timeval seltime, now; 521193323Sed fd_set *r, *e; 522218893Sdim con *c; 523193323Sed int i; 524206124Srdivacky 525193323Sed gettimeofday(&now, NULL); 526193323Sed c = TAILQ_FIRST(&tq); 527193323Sed 528218893Sdim if (c && (c->c_tv.tv_sec > now.tv_sec || 529193323Sed (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec > now.tv_usec))) { 530193323Sed seltime = c->c_tv; 531218893Sdim seltime.tv_sec -= now.tv_sec; 532218893Sdim seltime.tv_usec -= now.tv_usec; 533193323Sed if (seltime.tv_usec < 0) { 534193323Sed seltime.tv_usec += 1000000; 535193323Sed seltime.tv_sec--; 536193323Sed } 537218893Sdim } else 538206124Srdivacky timerclear(&seltime); 539218893Sdim 540193323Sed r = xcalloc(read_wait_nfdset, sizeof(fd_mask)); 541193323Sed e = xcalloc(read_wait_nfdset, sizeof(fd_mask)); 542193323Sed memcpy(r, read_wait, read_wait_nfdset * sizeof(fd_mask)); 543198090Srdivacky memcpy(e, read_wait, read_wait_nfdset * sizeof(fd_mask)); 544206124Srdivacky 545193323Sed while (select(maxfd, r, NULL, e, &seltime) == -1 && 546193323Sed (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) 547193323Sed ; 548193323Sed 549198090Srdivacky for (i = 0; i < maxfd; i++) { 550206124Srdivacky if (FD_ISSET(i, e)) { 551193323Sed error("%s: exception!", fdcon[i].c_name); 552193323Sed confree(i); 553193323Sed } else if (FD_ISSET(i, r)) 554193323Sed conread(i); 555198090Srdivacky } 556198090Srdivacky xfree(r); 557198090Srdivacky xfree(e); 558198090Srdivacky 559193323Sed c = TAILQ_FIRST(&tq); 560198090Srdivacky while (c && (c->c_tv.tv_sec < now.tv_sec || 561193323Sed (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec < now.tv_usec))) { 562198090Srdivacky int s = c->c_fd; 563193323Sed 564193323Sed c = TAILQ_NEXT(c, c_link); 565198090Srdivacky conrecycle(s); 566193323Sed } 567198090Srdivacky} 568193323Sed 569193323Sedstatic void 570249423Sdimdo_host(char *host) 571193323Sed{ 572198090Srdivacky char *name = strnnsep(&host, " \t\n"); 573193323Sed int j; 574193323Sed 575193323Sed if (name == NULL) 576193323Sed return; 577193323Sed for (j = KT_RSA1; j <= KT_ECDSA; j *= 2) { 578193323Sed if (get_keytypes & j) { 579193323Sed while (ncon >= MAXCON) 580193323Sed conloop(); 581193323Sed conalloc(name, *host ? host : name, j); 582193323Sed } 583193323Sed } 584193323Sed} 585193323Sed 586193323Sedvoid 587193323Sedfatal(const char *fmt,...) 588198090Srdivacky{ 589198090Srdivacky va_list args; 590198090Srdivacky 591198090Srdivacky va_start(args, fmt); 592198090Srdivacky do_log(SYSLOG_LEVEL_FATAL, fmt, args); 593198090Srdivacky va_end(args); 594193323Sed if (nonfatal_fatal) 595206274Srdivacky longjmp(kexjmp, -1); 596218893Sdim else 597218893Sdim exit(255); 598193323Sed} 599193323Sed 600206274Srdivackystatic void 601218893Sdimusage(void) 602218893Sdim{ 603193323Sed fprintf(stderr, 604193323Sed "usage: %s [-46Hv] [-f file] [-p port] [-T timeout] [-t type]\n" 605206274Srdivacky "\t\t [host | addrlist namelist] ...\n", 606218893Sdim __progname); 607193323Sed exit(1); 608193323Sed} 609193323Sed 610193323Sedint 611198090Srdivackymain(int argc, char **argv) 612193323Sed{ 613226633Sdim int debug_flag = 0, log_level = SYSLOG_LEVEL_INFO; 614226633Sdim int opt, fopt_count = 0, j; 615226633Sdim char *tname, *cp, line[NI_MAXHOST]; 616226633Sdim FILE *fp; 617193323Sed u_long linenum; 618193323Sed 619193323Sed extern int optind; 620193323Sed extern char *optarg; 621193323Sed 622193323Sed __progname = ssh_get_progname(argv[0]); 623193323Sed seed_rng(); 624193323Sed TAILQ_INIT(&tq); 625193323Sed 626193323Sed /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 627193323Sed sanitise_stdfd(); 628193323Sed 629193323Sed if (argc <= 1) 630193323Sed usage(); 631198090Srdivacky 632210299Sed while ((opt = getopt(argc, argv, "Hv46p:T:t:f:")) != -1) { 633193323Sed switch (opt) { 634193323Sed case 'H': 635193323Sed hash_hosts = 1; 636198090Srdivacky break; 637218893Sdim case 'p': 638226633Sdim ssh_port = a2port(optarg); 639226633Sdim if (ssh_port <= 0) { 640226633Sdim fprintf(stderr, "Bad port '%s'\n", optarg); 641198090Srdivacky exit(1); 642198090Srdivacky } 643226633Sdim break; 644226633Sdim case 'T': 645226633Sdim timeout = convtime(optarg); 646193323Sed if (timeout == -1 || timeout == 0) { 647226633Sdim fprintf(stderr, "Bad timeout '%s'\n", optarg); 648226633Sdim usage(); 649198090Srdivacky } 650193323Sed break; 651226633Sdim case 'v': 652226633Sdim if (!debug_flag) { 653198090Srdivacky debug_flag = 1; 654226633Sdim log_level = SYSLOG_LEVEL_DEBUG1; 655226633Sdim } 656226633Sdim else if (log_level < SYSLOG_LEVEL_DEBUG3) 657193323Sed log_level++; 658226633Sdim else 659226633Sdim fatal("Too high debugging level."); 660226633Sdim break; 661226633Sdim case 'f': 662226633Sdim if (strcmp(optarg, "-") == 0) 663226633Sdim optarg = NULL; 664226633Sdim argv[fopt_count++] = optarg; 665226633Sdim break; 666226633Sdim case 't': 667226633Sdim get_keytypes = 0; 668226633Sdim tname = strtok(optarg, ","); 669226633Sdim while (tname) { 670193323Sed int type = key_type_from_name(tname); 671198090Srdivacky switch (type) { 672198090Srdivacky case KEY_RSA1: 673198090Srdivacky get_keytypes |= KT_RSA1; 674193323Sed break; 675198090Srdivacky case KEY_DSA: 676193323Sed get_keytypes |= KT_DSA; 677218893Sdim break; 678193323Sed case KEY_ECDSA: 679193323Sed get_keytypes |= KT_ECDSA; 680193323Sed break; 681193323Sed case KEY_RSA: 682193323Sed get_keytypes |= KT_RSA; 683218893Sdim break; 684193323Sed case KEY_UNSPEC: 685193323Sed fatal("unknown key type %s", tname); 686193323Sed } 687198090Srdivacky tname = strtok(NULL, ","); 688198090Srdivacky } 689198090Srdivacky break; 690198090Srdivacky case '4': 691193323Sed IPv4or6 = AF_INET; 692193323Sed break; 693193323Sed case '6': 694193323Sed IPv4or6 = AF_INET6; 695193323Sed break; 696193323Sed case '?': 697198090Srdivacky default: 698218893Sdim usage(); 699234353Sdim } 700234353Sdim } 701218893Sdim if (optind == argc && !fopt_count) 702218893Sdim usage(); 703218893Sdim 704218893Sdim log_init("ssh-keyscan", log_level, SYSLOG_FACILITY_USER, 1); 705218893Sdim 706193323Sed maxfd = fdlim_get(1); 707210299Sed if (maxfd < 0) 708210299Sed fatal("%s: fdlim_get: bad value", __progname); 709210299Sed if (maxfd > MAXMAXFD) 710210299Sed maxfd = MAXMAXFD; 711218893Sdim if (MAXCON <= 0) 712234353Sdim fatal("%s: not enough file descriptors", __progname); 713234353Sdim if (maxfd > fdlim_get(0)) 714234353Sdim fdlim_set(maxfd); 715210299Sed fdcon = xcalloc(maxfd, sizeof(con)); 716210299Sed 717210299Sed read_wait_nfdset = howmany(maxfd, NFDBITS); 718198090Srdivacky read_wait = xcalloc(read_wait_nfdset, sizeof(fd_mask)); 719193323Sed 720193323Sed for (j = 0; j < fopt_count; j++) { 721193323Sed if (argv[j] == NULL) 722193323Sed fp = stdin; 723218893Sdim else if ((fp = fopen(argv[j], "r")) == NULL) 724218893Sdim fatal("%s: %s: %s", __progname, argv[j], 725218893Sdim strerror(errno)); 726198090Srdivacky linenum = 0; 727198090Srdivacky 728193323Sed while (read_keyfile_line(fp, 729218893Sdim argv[j] == NULL ? "(stdin)" : argv[j], line, sizeof(line), 730203954Srdivacky &linenum) != -1) { 731218893Sdim /* Chomp off trailing whitespace and comments */ 732218893Sdim if ((cp = strchr(line, '#')) == NULL) 733198090Srdivacky cp = line + strlen(line) - 1; 734198090Srdivacky while (cp >= line) { 735193323Sed if (*cp == ' ' || *cp == '\t' || 736193323Sed *cp == '\n' || *cp == '#') 737193323Sed *cp-- = '\0'; 738193323Sed else 739193323Sed break; 740193323Sed } 741207618Srdivacky 742207618Srdivacky /* Skip empty lines */ 743221345Sdim if (*line == '\0') 744193323Sed continue; 745193323Sed 746221345Sdim do_host(line); 747193323Sed } 748193323Sed 749193323Sed if (ferror(fp)) 750193323Sed fatal("%s: %s: %s", __progname, argv[j], 751193323Sed strerror(errno)); 752193323Sed 753193323Sed fclose(fp); 754210299Sed } 755210299Sed 756210299Sed while (optind < argc) 757193323Sed do_host(argv[optind++]); 758210299Sed 759193323Sed while (ncon > 0) 760210299Sed conloop(); 761193323Sed 762210299Sed return (0); 763193323Sed} 764193323Sed