1 /* $OpenBSD: ssh-keyscan.c,v 1.157 2024/05/06 19:26:17 tobias Exp $ */ 2 /* 3 * Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>. 4 * 5 * Modification and redistribution in source and binary forms is 6 * permitted provided that due credit is given to the author and the 7 * OpenBSD project by leaving this copyright notice intact. 8 */ 9 10 #include <sys/types.h> 11 #include <sys/socket.h> 12 #include <sys/queue.h> 13 #include <sys/time.h> 14 #include <sys/resource.h> 15 16 #ifdef WITH_OPENSSL 17 #include <openssl/bn.h> 18 #endif 19 20 #include <errno.h> 21 #include <limits.h> 22 #include <netdb.h> 23 #include <stdarg.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <poll.h> 27 #include <signal.h> 28 #include <string.h> 29 #include <unistd.h> 30 31 #include "xmalloc.h" 32 #include "ssh.h" 33 #include "sshbuf.h" 34 #include "sshkey.h" 35 #include "cipher.h" 36 #include "digest.h" 37 #include "kex.h" 38 #include "compat.h" 39 #include "myproposal.h" 40 #include "packet.h" 41 #include "dispatch.h" 42 #include "log.h" 43 #include "atomicio.h" 44 #include "misc.h" 45 #include "hostfile.h" 46 #include "ssherr.h" 47 #include "ssh_api.h" 48 #include "dns.h" 49 #include "addr.h" 50 51 /* Flag indicating whether IPv4 or IPv6. This can be set on the command line. 52 Default value is AF_UNSPEC means both IPv4 and IPv6. */ 53 int IPv4or6 = AF_UNSPEC; 54 55 int ssh_port = SSH_DEFAULT_PORT; 56 57 #define KT_DSA (1) 58 #define KT_RSA (1<<1) 59 #define KT_ECDSA (1<<2) 60 #define KT_ED25519 (1<<3) 61 #define KT_XMSS (1<<4) 62 #define KT_ECDSA_SK (1<<5) 63 #define KT_ED25519_SK (1<<6) 64 65 #define KT_MIN KT_DSA 66 #define KT_MAX KT_ED25519_SK 67 68 int get_cert = 0; 69 int get_keytypes = KT_RSA|KT_ECDSA|KT_ED25519|KT_ECDSA_SK|KT_ED25519_SK; 70 71 int hash_hosts = 0; /* Hash hostname on output */ 72 73 int print_sshfp = 0; /* Print SSHFP records instead of known_hosts */ 74 75 int found_one = 0; /* Successfully found a key */ 76 77 int hashalg = -1; /* Hash for SSHFP records or -1 for all */ 78 79 #define MAXMAXFD 256 80 81 /* The number of seconds after which to give up on a TCP connection */ 82 int timeout = 5; 83 84 int maxfd; 85 #define MAXCON (maxfd - 10) 86 87 extern char *__progname; 88 struct pollfd *read_wait; 89 int ncon; 90 91 /* 92 * Keep a connection structure for each file descriptor. The state 93 * associated with file descriptor n is held in fdcon[n]. 94 */ 95 typedef struct Connection { 96 u_char c_status; /* State of connection on this file desc. */ 97 #define CS_UNUSED 0 /* File descriptor unused */ 98 #define CS_CON 1 /* Waiting to connect/read greeting */ 99 int c_fd; /* Quick lookup: c->c_fd == c - fdcon */ 100 int c_keytype; /* Only one of KT_* */ 101 sig_atomic_t c_done; /* SSH2 done */ 102 char *c_namebase; /* Address to free for c_name and c_namelist */ 103 char *c_name; /* Hostname of connection for errors */ 104 char *c_namelist; /* Pointer to other possible addresses */ 105 char *c_output_name; /* Hostname of connection for output */ 106 struct ssh *c_ssh; /* SSH-connection */ 107 struct timespec c_ts; /* Time at which connection gets aborted */ 108 TAILQ_ENTRY(Connection) c_link; /* List of connections in timeout order. */ 109 } con; 110 111 TAILQ_HEAD(conlist, Connection) tq; /* Timeout Queue */ 112 con *fdcon; 113 114 static void keyprint(con *c, struct sshkey *key); 115 116 static int 117 fdlim_get(int hard) 118 { 119 struct rlimit rlfd; 120 121 if (getrlimit(RLIMIT_NOFILE, &rlfd) == -1) 122 return (-1); 123 if ((hard ? rlfd.rlim_max : rlfd.rlim_cur) == RLIM_INFINITY || 124 (hard ? rlfd.rlim_max : rlfd.rlim_cur) > INT_MAX) 125 return sysconf(_SC_OPEN_MAX); 126 return hard ? rlfd.rlim_max : rlfd.rlim_cur; 127 } 128 129 static int 130 fdlim_set(int lim) 131 { 132 struct rlimit rlfd; 133 134 if (lim <= 0) 135 return (-1); 136 if (getrlimit(RLIMIT_NOFILE, &rlfd) == -1) 137 return (-1); 138 rlfd.rlim_cur = lim; 139 if (setrlimit(RLIMIT_NOFILE, &rlfd) == -1) 140 return (-1); 141 return (0); 142 } 143 144 /* 145 * This is an strsep function that returns a null field for adjacent 146 * separators. This is the same as the 4.4BSD strsep, but different from the 147 * one in the GNU libc. 148 */ 149 static char * 150 xstrsep(char **str, const char *delim) 151 { 152 char *s, *e; 153 154 if (!**str) 155 return (NULL); 156 157 s = *str; 158 e = s + strcspn(s, delim); 159 160 if (*e != '\0') 161 *e++ = '\0'; 162 *str = e; 163 164 return (s); 165 } 166 167 /* 168 * Get the next non-null token (like GNU strsep). Strsep() will return a 169 * null token for two adjacent separators, so we may have to loop. 170 */ 171 static char * 172 strnnsep(char **stringp, char *delim) 173 { 174 char *tok; 175 176 do { 177 tok = xstrsep(stringp, delim); 178 } while (tok && *tok == '\0'); 179 return (tok); 180 } 181 182 183 static int 184 key_print_wrapper(struct sshkey *hostkey, struct ssh *ssh) 185 { 186 con *c; 187 188 if ((c = ssh_get_app_data(ssh)) != NULL) 189 keyprint(c, hostkey); 190 /* always abort key exchange */ 191 return -1; 192 } 193 194 static int 195 ssh2_capable(int remote_major, int remote_minor) 196 { 197 switch (remote_major) { 198 case 1: 199 if (remote_minor == 99) 200 return 1; 201 break; 202 case 2: 203 return 1; 204 default: 205 break; 206 } 207 return 0; 208 } 209 210 static void 211 keygrab_ssh2(con *c) 212 { 213 char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; 214 int r; 215 216 switch (c->c_keytype) { 217 case KT_DSA: 218 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? 219 "ssh-dss-cert-v01@openssh.com" : "ssh-dss"; 220 break; 221 case KT_RSA: 222 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? 223 "rsa-sha2-512-cert-v01@openssh.com," 224 "rsa-sha2-256-cert-v01@openssh.com," 225 "ssh-rsa-cert-v01@openssh.com" : 226 "rsa-sha2-512," 227 "rsa-sha2-256," 228 "ssh-rsa"; 229 break; 230 case KT_ED25519: 231 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? 232 "ssh-ed25519-cert-v01@openssh.com" : "ssh-ed25519"; 233 break; 234 case KT_XMSS: 235 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? 236 "ssh-xmss-cert-v01@openssh.com" : "ssh-xmss@openssh.com"; 237 break; 238 case KT_ECDSA: 239 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? 240 "ecdsa-sha2-nistp256-cert-v01@openssh.com," 241 "ecdsa-sha2-nistp384-cert-v01@openssh.com," 242 "ecdsa-sha2-nistp521-cert-v01@openssh.com" : 243 "ecdsa-sha2-nistp256," 244 "ecdsa-sha2-nistp384," 245 "ecdsa-sha2-nistp521"; 246 break; 247 case KT_ECDSA_SK: 248 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? 249 "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com" : 250 "sk-ecdsa-sha2-nistp256@openssh.com"; 251 break; 252 case KT_ED25519_SK: 253 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? 254 "sk-ssh-ed25519-cert-v01@openssh.com" : 255 "sk-ssh-ed25519@openssh.com"; 256 break; 257 default: 258 fatal("unknown key type %d", c->c_keytype); 259 break; 260 } 261 if ((r = kex_setup(c->c_ssh, myproposal)) != 0) { 262 free(c->c_ssh); 263 fprintf(stderr, "kex_setup: %s\n", ssh_err(r)); 264 exit(1); 265 } 266 #ifdef WITH_OPENSSL 267 c->c_ssh->kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_client; 268 c->c_ssh->kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_client; 269 c->c_ssh->kex->kex[KEX_DH_GRP14_SHA256] = kex_gen_client; 270 c->c_ssh->kex->kex[KEX_DH_GRP16_SHA512] = kex_gen_client; 271 c->c_ssh->kex->kex[KEX_DH_GRP18_SHA512] = kex_gen_client; 272 c->c_ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; 273 c->c_ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; 274 c->c_ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client; 275 #endif 276 c->c_ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client; 277 c->c_ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_client; 278 ssh_set_verify_host_key_callback(c->c_ssh, key_print_wrapper); 279 /* 280 * do the key-exchange until an error occurs or until 281 * the key_print_wrapper() callback sets c_done. 282 */ 283 ssh_dispatch_run(c->c_ssh, DISPATCH_BLOCK, &c->c_done); 284 } 285 286 static void 287 keyprint_one(const char *host, struct sshkey *key) 288 { 289 char *hostport = NULL, *hashed = NULL; 290 const char *known_host; 291 int r = 0; 292 293 found_one = 1; 294 295 if (print_sshfp) { 296 export_dns_rr(host, key, stdout, 0, hashalg); 297 return; 298 } 299 300 hostport = put_host_port(host, ssh_port); 301 lowercase(hostport); 302 if (hash_hosts && (hashed = host_hash(hostport, NULL, 0)) == NULL) 303 fatal("host_hash failed"); 304 known_host = hash_hosts ? hashed : hostport; 305 if (!get_cert) 306 r = fprintf(stdout, "%s ", known_host); 307 if (r >= 0 && sshkey_write(key, stdout) == 0) 308 (void)fputs("\n", stdout); 309 free(hashed); 310 free(hostport); 311 } 312 313 static void 314 keyprint(con *c, struct sshkey *key) 315 { 316 char *hosts = c->c_output_name ? c->c_output_name : c->c_name; 317 char *host, *ohosts; 318 319 if (key == NULL) 320 return; 321 if (get_cert || (!hash_hosts && ssh_port == SSH_DEFAULT_PORT)) { 322 keyprint_one(hosts, key); 323 return; 324 } 325 ohosts = hosts = xstrdup(hosts); 326 while ((host = strsep(&hosts, ",")) != NULL) 327 keyprint_one(host, key); 328 free(ohosts); 329 } 330 331 static int 332 tcpconnect(char *host) 333 { 334 struct addrinfo hints, *ai, *aitop; 335 char strport[NI_MAXSERV]; 336 int gaierr, s = -1; 337 338 snprintf(strport, sizeof strport, "%d", ssh_port); 339 memset(&hints, 0, sizeof(hints)); 340 hints.ai_family = IPv4or6; 341 hints.ai_socktype = SOCK_STREAM; 342 if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) { 343 error("getaddrinfo %s: %s", host, ssh_gai_strerror(gaierr)); 344 return -1; 345 } 346 for (ai = aitop; ai; ai = ai->ai_next) { 347 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 348 if (s == -1) { 349 error("socket: %s", strerror(errno)); 350 continue; 351 } 352 if (set_nonblock(s) == -1) 353 fatal_f("set_nonblock(%d)", s); 354 if (connect(s, ai->ai_addr, ai->ai_addrlen) == -1 && 355 errno != EINPROGRESS) 356 error("connect (`%s'): %s", host, strerror(errno)); 357 else 358 break; 359 close(s); 360 s = -1; 361 } 362 freeaddrinfo(aitop); 363 return s; 364 } 365 366 static int 367 conalloc(const char *iname, const char *oname, int keytype) 368 { 369 char *namebase, *name, *namelist; 370 int s; 371 372 namebase = namelist = xstrdup(iname); 373 374 do { 375 name = xstrsep(&namelist, ","); 376 if (!name) { 377 free(namebase); 378 return (-1); 379 } 380 } while ((s = tcpconnect(name)) < 0); 381 382 if (s >= maxfd) 383 fatal("conalloc: fdno %d too high", s); 384 if (fdcon[s].c_status) 385 fatal("conalloc: attempt to reuse fdno %d", s); 386 387 debug3_f("oname %s kt %d", oname, keytype); 388 fdcon[s].c_fd = s; 389 fdcon[s].c_status = CS_CON; 390 fdcon[s].c_namebase = namebase; 391 fdcon[s].c_name = name; 392 fdcon[s].c_namelist = namelist; 393 fdcon[s].c_output_name = xstrdup(oname); 394 fdcon[s].c_keytype = keytype; 395 monotime_ts(&fdcon[s].c_ts); 396 fdcon[s].c_ts.tv_sec += timeout; 397 TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); 398 read_wait[s].fd = s; 399 read_wait[s].events = POLLIN; 400 ncon++; 401 return (s); 402 } 403 404 static void 405 confree(int s) 406 { 407 if (s >= maxfd || fdcon[s].c_status == CS_UNUSED) 408 fatal("confree: attempt to free bad fdno %d", s); 409 free(fdcon[s].c_namebase); 410 free(fdcon[s].c_output_name); 411 fdcon[s].c_status = CS_UNUSED; 412 fdcon[s].c_keytype = 0; 413 if (fdcon[s].c_ssh) { 414 ssh_packet_close(fdcon[s].c_ssh); 415 free(fdcon[s].c_ssh); 416 fdcon[s].c_ssh = NULL; 417 } else 418 close(s); 419 TAILQ_REMOVE(&tq, &fdcon[s], c_link); 420 read_wait[s].fd = -1; 421 read_wait[s].events = 0; 422 ncon--; 423 } 424 425 static int 426 conrecycle(int s) 427 { 428 con *c = &fdcon[s]; 429 int ret; 430 431 ret = conalloc(c->c_namelist, c->c_output_name, c->c_keytype); 432 confree(s); 433 return (ret); 434 } 435 436 static void 437 congreet(int s) 438 { 439 int n = 0, remote_major = 0, remote_minor = 0; 440 char buf[256], *cp; 441 char remote_version[sizeof buf]; 442 size_t bufsiz; 443 con *c = &fdcon[s]; 444 445 /* send client banner */ 446 n = snprintf(buf, sizeof buf, "SSH-%d.%d-OpenSSH-keyscan\r\n", 447 PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2); 448 if (n < 0 || (size_t)n >= sizeof(buf)) { 449 error("snprintf: buffer too small"); 450 confree(s); 451 return; 452 } 453 if (atomicio(vwrite, s, buf, n) != (size_t)n) { 454 error("write (%s): %s", c->c_name, strerror(errno)); 455 confree(s); 456 return; 457 } 458 459 /* 460 * Read the server banner as per RFC4253 section 4.2. The "SSH-" 461 * protocol identification string may be preceded by an arbitrarily 462 * large banner which we must read and ignore. Loop while reading 463 * newline-terminated lines until we have one starting with "SSH-". 464 * The ID string cannot be longer than 255 characters although the 465 * preceding banner lines may (in which case they'll be discarded 466 * in multiple iterations of the outer loop). 467 */ 468 for (;;) { 469 memset(buf, '\0', sizeof(buf)); 470 bufsiz = sizeof(buf); 471 cp = buf; 472 while (bufsiz-- && 473 (n = atomicio(read, s, cp, 1)) == 1 && *cp != '\n') { 474 if (*cp == '\r') 475 *cp = '\n'; 476 cp++; 477 } 478 if (n != 1 || strncmp(buf, "SSH-", 4) == 0) 479 break; 480 } 481 if (n == 0) { 482 switch (errno) { 483 case EPIPE: 484 error("%s: Connection closed by remote host", c->c_name); 485 break; 486 case ECONNREFUSED: 487 break; 488 default: 489 error("read (%s): %s", c->c_name, strerror(errno)); 490 break; 491 } 492 conrecycle(s); 493 return; 494 } 495 if (cp >= buf + sizeof(buf)) { 496 error("%s: greeting exceeds allowable length", c->c_name); 497 confree(s); 498 return; 499 } 500 if (*cp != '\n' && *cp != '\r') { 501 error("%s: bad greeting", c->c_name); 502 confree(s); 503 return; 504 } 505 *cp = '\0'; 506 if ((c->c_ssh = ssh_packet_set_connection(NULL, s, s)) == NULL) 507 fatal("ssh_packet_set_connection failed"); 508 ssh_packet_set_timeout(c->c_ssh, timeout, 1); 509 ssh_set_app_data(c->c_ssh, c); /* back link */ 510 c->c_ssh->compat = 0; 511 if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", 512 &remote_major, &remote_minor, remote_version) == 3) 513 compat_banner(c->c_ssh, remote_version); 514 if (!ssh2_capable(remote_major, remote_minor)) { 515 debug("%s doesn't support ssh2", c->c_name); 516 confree(s); 517 return; 518 } 519 fprintf(stderr, "%c %s:%d %s\n", print_sshfp ? ';' : '#', 520 c->c_name, ssh_port, chop(buf)); 521 keygrab_ssh2(c); 522 confree(s); 523 } 524 525 static void 526 conread(int s) 527 { 528 con *c = &fdcon[s]; 529 530 if (c->c_status != CS_CON) 531 fatal("conread: invalid status %d", c->c_status); 532 533 congreet(s); 534 } 535 536 static void 537 conloop(void) 538 { 539 struct timespec seltime, now; 540 con *c; 541 int i; 542 543 monotime_ts(&now); 544 c = TAILQ_FIRST(&tq); 545 546 if (c && timespeccmp(&c->c_ts, &now, >)) 547 timespecsub(&c->c_ts, &now, &seltime); 548 else 549 timespecclear(&seltime); 550 551 while (ppoll(read_wait, maxfd, &seltime, NULL) == -1) { 552 if (errno == EAGAIN || errno == EINTR) 553 continue; 554 error("poll error"); 555 } 556 557 for (i = 0; i < maxfd; i++) { 558 if (read_wait[i].revents & (POLLHUP|POLLERR|POLLNVAL)) 559 confree(i); 560 else if (read_wait[i].revents & (POLLIN|POLLHUP)) 561 conread(i); 562 } 563 564 c = TAILQ_FIRST(&tq); 565 while (c && timespeccmp(&c->c_ts, &now, <)) { 566 int s = c->c_fd; 567 568 c = TAILQ_NEXT(c, c_link); 569 conrecycle(s); 570 } 571 } 572 573 static void 574 do_one_host(char *host) 575 { 576 char *name = strnnsep(&host, " \t\n"); 577 int j; 578 579 if (name == NULL) 580 return; 581 for (j = KT_MIN; j <= KT_MAX; j *= 2) { 582 if (get_keytypes & j) { 583 while (ncon >= MAXCON) 584 conloop(); 585 conalloc(name, *host ? host : name, j); 586 } 587 } 588 } 589 590 static void 591 do_host(char *host) 592 { 593 char daddr[128]; 594 struct xaddr addr, end_addr; 595 u_int masklen; 596 597 if (host == NULL) 598 return; 599 if (addr_pton_cidr(host, &addr, &masklen) != 0) { 600 /* Assume argument is a hostname */ 601 do_one_host(host); 602 } else { 603 /* Argument is a CIDR range */ 604 debug("CIDR range %s", host); 605 end_addr = addr; 606 if (addr_host_to_all1s(&end_addr, masklen) != 0) 607 goto badaddr; 608 /* 609 * Note: we deliberately include the all-zero/ones addresses. 610 */ 611 for (;;) { 612 if (addr_ntop(&addr, daddr, sizeof(daddr)) != 0) { 613 badaddr: 614 error("Invalid address %s", host); 615 return; 616 } 617 debug("CIDR expand: address %s", daddr); 618 do_one_host(daddr); 619 if (addr_cmp(&addr, &end_addr) == 0) 620 break; 621 addr_increment(&addr); 622 }; 623 } 624 } 625 626 void 627 sshfatal(const char *file, const char *func, int line, int showfunc, 628 LogLevel level, const char *suffix, const char *fmt, ...) 629 { 630 va_list args; 631 632 va_start(args, fmt); 633 sshlogv(file, func, line, showfunc, level, suffix, fmt, args); 634 va_end(args); 635 cleanup_exit(255); 636 } 637 638 static void 639 usage(void) 640 { 641 fprintf(stderr, 642 "usage: ssh-keyscan [-46cDHv] [-f file] [-O option] [-p port] [-T timeout]\n" 643 " [-t type] [host | addrlist namelist]\n"); 644 exit(1); 645 } 646 647 int 648 main(int argc, char **argv) 649 { 650 int debug_flag = 0, log_level = SYSLOG_LEVEL_INFO; 651 int opt, fopt_count = 0, j; 652 char *tname, *cp, *line = NULL; 653 size_t linesize = 0; 654 FILE *fp; 655 656 extern int optind; 657 extern char *optarg; 658 659 TAILQ_INIT(&tq); 660 661 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 662 sanitise_stdfd(); 663 664 if (argc <= 1) 665 usage(); 666 667 while ((opt = getopt(argc, argv, "cDHv46O:p:T:t:f:")) != -1) { 668 switch (opt) { 669 case 'H': 670 hash_hosts = 1; 671 break; 672 case 'c': 673 get_cert = 1; 674 break; 675 case 'D': 676 print_sshfp = 1; 677 break; 678 case 'p': 679 ssh_port = a2port(optarg); 680 if (ssh_port <= 0) { 681 fprintf(stderr, "Bad port '%s'\n", optarg); 682 exit(1); 683 } 684 break; 685 case 'T': 686 timeout = convtime(optarg); 687 if (timeout == -1 || timeout == 0) { 688 fprintf(stderr, "Bad timeout '%s'\n", optarg); 689 usage(); 690 } 691 break; 692 case 'v': 693 if (!debug_flag) { 694 debug_flag = 1; 695 log_level = SYSLOG_LEVEL_DEBUG1; 696 } 697 else if (log_level < SYSLOG_LEVEL_DEBUG3) 698 log_level++; 699 else 700 fatal("Too high debugging level."); 701 break; 702 case 'f': 703 if (strcmp(optarg, "-") == 0) 704 optarg = NULL; 705 argv[fopt_count++] = optarg; 706 break; 707 case 'O': 708 /* Maybe other misc options in the future too */ 709 if (strncmp(optarg, "hashalg=", 8) != 0) 710 fatal("Unsupported -O option"); 711 if ((hashalg = ssh_digest_alg_by_name( 712 optarg + 8)) == -1) 713 fatal("Unsupported hash algorithm"); 714 break; 715 case 't': 716 get_keytypes = 0; 717 tname = strtok(optarg, ","); 718 while (tname) { 719 int type = sshkey_type_from_name(tname); 720 721 switch (type) { 722 #ifdef WITH_DSA 723 case KEY_DSA: 724 get_keytypes |= KT_DSA; 725 break; 726 #endif 727 case KEY_ECDSA: 728 get_keytypes |= KT_ECDSA; 729 break; 730 case KEY_RSA: 731 get_keytypes |= KT_RSA; 732 break; 733 case KEY_ED25519: 734 get_keytypes |= KT_ED25519; 735 break; 736 case KEY_XMSS: 737 get_keytypes |= KT_XMSS; 738 break; 739 case KEY_ED25519_SK: 740 get_keytypes |= KT_ED25519_SK; 741 break; 742 case KEY_ECDSA_SK: 743 get_keytypes |= KT_ECDSA_SK; 744 break; 745 case KEY_UNSPEC: 746 default: 747 fatal("Unknown key type \"%s\"", tname); 748 } 749 tname = strtok(NULL, ","); 750 } 751 break; 752 case '4': 753 IPv4or6 = AF_INET; 754 break; 755 case '6': 756 IPv4or6 = AF_INET6; 757 break; 758 default: 759 usage(); 760 } 761 } 762 if (optind == argc && !fopt_count) 763 usage(); 764 765 log_init("ssh-keyscan", log_level, SYSLOG_FACILITY_USER, 1); 766 767 maxfd = fdlim_get(1); 768 if (maxfd < 0) 769 fatal("%s: fdlim_get: bad value", __progname); 770 if (maxfd > MAXMAXFD) 771 maxfd = MAXMAXFD; 772 if (MAXCON <= 0) 773 fatal("%s: not enough file descriptors", __progname); 774 if (maxfd > fdlim_get(0)) 775 fdlim_set(maxfd); 776 fdcon = xcalloc(maxfd, sizeof(con)); 777 read_wait = xcalloc(maxfd, sizeof(struct pollfd)); 778 for (j = 0; j < maxfd; j++) 779 read_wait[j].fd = -1; 780 781 for (j = 0; j < fopt_count; j++) { 782 if (argv[j] == NULL) 783 fp = stdin; 784 else if ((fp = fopen(argv[j], "r")) == NULL) 785 fatal("%s: %s: %s", __progname, 786 fp == stdin ? "<stdin>" : argv[j], strerror(errno)); 787 788 while (getline(&line, &linesize, fp) != -1) { 789 /* Chomp off trailing whitespace and comments */ 790 if ((cp = strchr(line, '#')) == NULL) 791 cp = line + strlen(line) - 1; 792 while (cp >= line) { 793 if (*cp == ' ' || *cp == '\t' || 794 *cp == '\n' || *cp == '#') 795 *cp-- = '\0'; 796 else 797 break; 798 } 799 800 /* Skip empty lines */ 801 if (*line == '\0') 802 continue; 803 804 do_host(line); 805 } 806 807 if (ferror(fp)) 808 fatal("%s: %s: %s", __progname, 809 fp == stdin ? "<stdin>" : argv[j], strerror(errno)); 810 811 if (fp != stdin) 812 fclose(fp); 813 } 814 free(line); 815 816 while (optind < argc) 817 do_host(argv[optind++]); 818 819 while (ncon > 0) 820 conloop(); 821 822 return found_one ? 0 : 1; 823 } 824