1 /* $OpenBSD: ssh-keyscan.c,v 1.164 2024/10/18 05:32:51 djm Exp $ */
3 * Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>.
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.
12 #include <sys/types.h>
13 #include "openbsd-compat/sys-queue.h"
14 #include <sys/resource.h>
15 #ifdef HAVE_SYS_TIME_H
16 # include <sys/time.h>
19 #include <netinet/in.h>
20 #include <arpa/inet.h>
23 #include <openssl/bn.h>
47 #include "myproposal.h"
59 /* Flag indicating whether IPv4 or IPv6. This can be set on the command line.
60 Default value is AF_UNSPEC means both IPv4 and IPv6. */
61 int IPv4or6
= AF_UNSPEC
;
63 int ssh_port
= SSH_DEFAULT_PORT
;
67 #define KT_ECDSA (1<<2)
68 #define KT_ED25519 (1<<3)
69 #define KT_XMSS (1<<4)
70 #define KT_ECDSA_SK (1<<5)
71 #define KT_ED25519_SK (1<<6)
74 #define KT_MAX KT_ED25519_SK
77 int get_keytypes
= KT_RSA
|KT_ECDSA
|KT_ED25519
|KT_ECDSA_SK
|KT_ED25519_SK
;
79 int hash_hosts
= 0; /* Hash hostname on output */
81 int print_sshfp
= 0; /* Print SSHFP records instead of known_hosts */
83 int found_one
= 0; /* Successfully found a key */
85 int hashalg
= -1; /* Hash for SSHFP records or -1 for all */
87 int quiet
= 0; /* Don't print key comment lines */
91 /* The number of seconds after which to give up on a TCP connection */
95 #define MAXCON (maxfd - 10)
97 extern char *__progname
;
98 struct pollfd
*read_wait
;
102 * Keep a connection structure for each file descriptor. The state
103 * associated with file descriptor n is held in fdcon[n].
105 typedef struct Connection
{
106 u_char c_status
; /* State of connection on this file desc. */
107 #define CS_UNUSED 0 /* File descriptor unused */
108 #define CS_CON 1 /* Waiting to connect/read greeting */
109 int c_fd
; /* Quick lookup: c->c_fd == c - fdcon */
110 int c_keytype
; /* Only one of KT_* */
111 sig_atomic_t c_done
; /* SSH2 done */
112 char *c_namebase
; /* Address to free for c_name and c_namelist */
113 char *c_name
; /* Hostname of connection for errors */
114 char *c_namelist
; /* Pointer to other possible addresses */
115 char *c_output_name
; /* Hostname of connection for output */
116 struct ssh
*c_ssh
; /* SSH-connection */
117 struct timespec c_ts
; /* Time at which connection gets aborted */
118 TAILQ_ENTRY(Connection
) c_link
; /* List of connections in timeout order. */
121 TAILQ_HEAD(conlist
, Connection
) tq
; /* Timeout Queue */
124 static void keyprint(con
*c
, struct sshkey
*key
);
129 #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)
133 if (getrlimit(RLIMIT_NOFILE
, &rlfd
) == -1)
135 lim
= hard
? rlfd
.rlim_max
: rlfd
.rlim_cur
;
138 if (lim
== RLIM_INFINITY
)
144 return (SSH_SYSFDMAX
<= 0) ? -1 :
145 ((SSH_SYSFDMAX
>= INT_MAX
) ? INT_MAX
: SSH_SYSFDMAX
);
152 #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
158 #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
159 if (getrlimit(RLIMIT_NOFILE
, &rlfd
) == -1)
162 if (setrlimit(RLIMIT_NOFILE
, &rlfd
) == -1)
164 #elif defined (HAVE_SETDTABLESIZE)
171 * This is an strsep function that returns a null field for adjacent
172 * separators. This is the same as the 4.4BSD strsep, but different from the
173 * one in the GNU libc.
176 xstrsep(char **str
, const char *delim
)
184 e
= s
+ strcspn(s
, delim
);
194 * Get the next non-null token (like GNU strsep). Strsep() will return a
195 * null token for two adjacent separators, so we may have to loop.
198 strnnsep(char **stringp
, char *delim
)
203 tok
= xstrsep(stringp
, delim
);
204 } while (tok
&& *tok
== '\0');
210 key_print_wrapper(struct sshkey
*hostkey
, struct ssh
*ssh
)
214 if ((c
= ssh_get_app_data(ssh
)) != NULL
)
215 keyprint(c
, hostkey
);
216 /* always abort key exchange */
221 ssh2_capable(int remote_major
, int remote_minor
)
223 switch (remote_major
) {
225 if (remote_minor
== 99)
239 char *myproposal
[PROPOSAL_MAX
] = { KEX_CLIENT
};
242 switch (c
->c_keytype
) {
244 myproposal
[PROPOSAL_SERVER_HOST_KEY_ALGS
] = get_cert
?
245 "ssh-dss-cert-v01@openssh.com" : "ssh-dss";
248 myproposal
[PROPOSAL_SERVER_HOST_KEY_ALGS
] = get_cert
?
249 "rsa-sha2-512-cert-v01@openssh.com,"
250 "rsa-sha2-256-cert-v01@openssh.com,"
251 "ssh-rsa-cert-v01@openssh.com" :
257 myproposal
[PROPOSAL_SERVER_HOST_KEY_ALGS
] = get_cert
?
258 "ssh-ed25519-cert-v01@openssh.com" : "ssh-ed25519";
261 myproposal
[PROPOSAL_SERVER_HOST_KEY_ALGS
] = get_cert
?
262 "ssh-xmss-cert-v01@openssh.com" : "ssh-xmss@openssh.com";
265 myproposal
[PROPOSAL_SERVER_HOST_KEY_ALGS
] = get_cert
?
266 "ecdsa-sha2-nistp256-cert-v01@openssh.com,"
267 "ecdsa-sha2-nistp384-cert-v01@openssh.com,"
268 "ecdsa-sha2-nistp521-cert-v01@openssh.com" :
269 "ecdsa-sha2-nistp256,"
270 "ecdsa-sha2-nistp384,"
271 "ecdsa-sha2-nistp521";
274 myproposal
[PROPOSAL_SERVER_HOST_KEY_ALGS
] = get_cert
?
275 "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com" :
276 "sk-ecdsa-sha2-nistp256@openssh.com";
279 myproposal
[PROPOSAL_SERVER_HOST_KEY_ALGS
] = get_cert
?
280 "sk-ssh-ed25519-cert-v01@openssh.com" :
281 "sk-ssh-ed25519@openssh.com";
284 fatal("unknown key type %d", c
->c_keytype
);
287 if ((r
= kex_setup(c
->c_ssh
, myproposal
)) != 0) {
289 fprintf(stderr
, "kex_setup: %s\n", ssh_err(r
));
293 c
->c_ssh
->kex
->kex
[KEX_DH_GRP1_SHA1
] = kex_gen_client
;
294 c
->c_ssh
->kex
->kex
[KEX_DH_GRP14_SHA1
] = kex_gen_client
;
295 c
->c_ssh
->kex
->kex
[KEX_DH_GRP14_SHA256
] = kex_gen_client
;
296 c
->c_ssh
->kex
->kex
[KEX_DH_GRP16_SHA512
] = kex_gen_client
;
297 c
->c_ssh
->kex
->kex
[KEX_DH_GRP18_SHA512
] = kex_gen_client
;
298 c
->c_ssh
->kex
->kex
[KEX_DH_GEX_SHA1
] = kexgex_client
;
299 c
->c_ssh
->kex
->kex
[KEX_DH_GEX_SHA256
] = kexgex_client
;
300 # ifdef OPENSSL_HAS_ECC
301 c
->c_ssh
->kex
->kex
[KEX_ECDH_SHA2
] = kex_gen_client
;
304 c
->c_ssh
->kex
->kex
[KEX_C25519_SHA256
] = kex_gen_client
;
305 c
->c_ssh
->kex
->kex
[KEX_KEM_SNTRUP761X25519_SHA512
] = kex_gen_client
;
306 c
->c_ssh
->kex
->kex
[KEX_KEM_MLKEM768X25519_SHA256
] = kex_gen_client
;
307 ssh_set_verify_host_key_callback(c
->c_ssh
, key_print_wrapper
);
309 * do the key-exchange until an error occurs or until
310 * the key_print_wrapper() callback sets c_done.
312 ssh_dispatch_run(c
->c_ssh
, DISPATCH_BLOCK
, &c
->c_done
);
316 keyprint_one(const char *host
, struct sshkey
*key
)
318 char *hostport
= NULL
, *hashed
= NULL
;
319 const char *known_host
;
325 export_dns_rr(host
, key
, stdout
, 0, hashalg
);
329 hostport
= put_host_port(host
, ssh_port
);
331 if (hash_hosts
&& (hashed
= host_hash(hostport
, NULL
, 0)) == NULL
)
332 fatal("host_hash failed");
333 known_host
= hash_hosts
? hashed
: hostport
;
335 r
= fprintf(stdout
, "%s ", known_host
);
336 if (r
>= 0 && sshkey_write(key
, stdout
) == 0)
337 (void)fputs("\n", stdout
);
343 keyprint(con
*c
, struct sshkey
*key
)
345 char *hosts
= c
->c_output_name
? c
->c_output_name
: c
->c_name
;
350 if (get_cert
|| (!hash_hosts
&& ssh_port
== SSH_DEFAULT_PORT
)) {
351 keyprint_one(hosts
, key
);
354 ohosts
= hosts
= xstrdup(hosts
);
355 while ((host
= strsep(&hosts
, ",")) != NULL
)
356 keyprint_one(host
, key
);
361 tcpconnect(char *host
)
363 struct addrinfo hints
, *ai
, *aitop
;
364 char strport
[NI_MAXSERV
];
367 snprintf(strport
, sizeof strport
, "%d", ssh_port
);
368 memset(&hints
, 0, sizeof(hints
));
369 hints
.ai_family
= IPv4or6
;
370 hints
.ai_socktype
= SOCK_STREAM
;
371 if ((gaierr
= getaddrinfo(host
, strport
, &hints
, &aitop
)) != 0) {
372 error("getaddrinfo %s: %s", host
, ssh_gai_strerror(gaierr
));
375 for (ai
= aitop
; ai
; ai
= ai
->ai_next
) {
376 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
378 error("socket: %s", strerror(errno
));
381 if (set_nonblock(s
) == -1)
382 fatal_f("set_nonblock(%d)", s
);
383 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) == -1 &&
384 errno
!= EINPROGRESS
)
385 error("connect (`%s'): %s", host
, strerror(errno
));
396 conalloc(const char *iname
, const char *oname
, int keytype
)
398 char *namebase
, *name
, *namelist
;
401 namebase
= namelist
= xstrdup(iname
);
404 name
= xstrsep(&namelist
, ",");
409 } while ((s
= tcpconnect(name
)) < 0);
412 fatal("conalloc: fdno %d too high", s
);
413 if (fdcon
[s
].c_status
)
414 fatal("conalloc: attempt to reuse fdno %d", s
);
416 debug3_f("oname %s kt %d", oname
, keytype
);
418 fdcon
[s
].c_status
= CS_CON
;
419 fdcon
[s
].c_namebase
= namebase
;
420 fdcon
[s
].c_name
= name
;
421 fdcon
[s
].c_namelist
= namelist
;
422 fdcon
[s
].c_output_name
= xstrdup(oname
);
423 fdcon
[s
].c_keytype
= keytype
;
424 monotime_ts(&fdcon
[s
].c_ts
);
425 fdcon
[s
].c_ts
.tv_sec
+= timeout
;
426 TAILQ_INSERT_TAIL(&tq
, &fdcon
[s
], c_link
);
428 read_wait
[s
].events
= POLLIN
;
436 if (s
>= maxfd
|| fdcon
[s
].c_status
== CS_UNUSED
)
437 fatal("confree: attempt to free bad fdno %d", s
);
438 free(fdcon
[s
].c_namebase
);
439 free(fdcon
[s
].c_output_name
);
440 fdcon
[s
].c_status
= CS_UNUSED
;
441 fdcon
[s
].c_keytype
= 0;
442 if (fdcon
[s
].c_ssh
) {
443 ssh_packet_close(fdcon
[s
].c_ssh
);
444 free(fdcon
[s
].c_ssh
);
445 fdcon
[s
].c_ssh
= NULL
;
448 TAILQ_REMOVE(&tq
, &fdcon
[s
], c_link
);
449 read_wait
[s
].fd
= -1;
450 read_wait
[s
].events
= 0;
460 ret
= conalloc(c
->c_namelist
, c
->c_output_name
, c
->c_keytype
);
468 int n
= 0, remote_major
= 0, remote_minor
= 0;
470 char remote_version
[sizeof buf
];
474 /* send client banner */
475 n
= snprintf(buf
, sizeof buf
, "SSH-%d.%d-OpenSSH-keyscan\r\n",
476 PROTOCOL_MAJOR_2
, PROTOCOL_MINOR_2
);
477 if (n
< 0 || (size_t)n
>= sizeof(buf
)) {
478 error("snprintf: buffer too small");
482 if (atomicio(vwrite
, s
, buf
, n
) != (size_t)n
) {
483 error("write (%s): %s", c
->c_name
, strerror(errno
));
489 * Read the server banner as per RFC4253 section 4.2. The "SSH-"
490 * protocol identification string may be preceded by an arbitrarily
491 * large banner which we must read and ignore. Loop while reading
492 * newline-terminated lines until we have one starting with "SSH-".
493 * The ID string cannot be longer than 255 characters although the
494 * preceding banner lines may (in which case they'll be discarded
495 * in multiple iterations of the outer loop).
498 memset(buf
, '\0', sizeof(buf
));
499 bufsiz
= sizeof(buf
);
502 (n
= atomicio(read
, s
, cp
, 1)) == 1 && *cp
!= '\n') {
507 if (n
!= 1 || strncmp(buf
, "SSH-", 4) == 0)
513 error("%s: Connection closed by remote host", c
->c_name
);
518 error("read (%s): %s", c
->c_name
, strerror(errno
));
524 if (cp
>= buf
+ sizeof(buf
)) {
525 error("%s: greeting exceeds allowable length", c
->c_name
);
529 if (*cp
!= '\n' && *cp
!= '\r') {
530 error("%s: bad greeting", c
->c_name
);
535 if ((c
->c_ssh
= ssh_packet_set_connection(NULL
, s
, s
)) == NULL
)
536 fatal("ssh_packet_set_connection failed");
537 ssh_packet_set_timeout(c
->c_ssh
, timeout
, 1);
538 ssh_set_app_data(c
->c_ssh
, c
); /* back link */
539 c
->c_ssh
->compat
= 0;
540 if (sscanf(buf
, "SSH-%d.%d-%[^\n]\n",
541 &remote_major
, &remote_minor
, remote_version
) == 3)
542 compat_banner(c
->c_ssh
, remote_version
);
543 if (!ssh2_capable(remote_major
, remote_minor
)) {
544 debug("%s doesn't support ssh2", c
->c_name
);
549 fprintf(stdout
, "%c %s:%d %s\n", print_sshfp
? ';' : '#',
550 c
->c_name
, ssh_port
, chop(buf
));
561 if (c
->c_status
!= CS_CON
)
562 fatal("conread: invalid status %d", c
->c_status
);
570 struct timespec seltime
, now
;
575 c
= TAILQ_FIRST(&tq
);
577 if (c
&& timespeccmp(&c
->c_ts
, &now
, >))
578 timespecsub(&c
->c_ts
, &now
, &seltime
);
580 timespecclear(&seltime
);
582 while (ppoll(read_wait
, maxfd
, &seltime
, NULL
) == -1) {
583 if (errno
== EAGAIN
|| errno
== EINTR
|| errno
== EWOULDBLOCK
)
588 for (i
= 0; i
< maxfd
; i
++) {
589 if (read_wait
[i
].revents
& (POLLHUP
|POLLERR
|POLLNVAL
))
591 else if (read_wait
[i
].revents
& (POLLIN
))
595 c
= TAILQ_FIRST(&tq
);
596 while (c
&& timespeccmp(&c
->c_ts
, &now
, <)) {
599 c
= TAILQ_NEXT(c
, c_link
);
605 do_one_host(char *host
)
607 char *name
= strnnsep(&host
, " \t\n");
612 for (j
= KT_MIN
; j
<= KT_MAX
; j
*= 2) {
613 if (get_keytypes
& j
) {
614 while (ncon
>= MAXCON
)
616 conalloc(name
, *host
? host
: name
, j
);
625 struct xaddr addr
, end_addr
;
630 if (addr_pton_cidr(host
, &addr
, &masklen
) != 0) {
631 /* Assume argument is a hostname */
634 /* Argument is a CIDR range */
635 debug("CIDR range %s", host
);
637 if (addr_host_to_all1s(&end_addr
, masklen
) != 0)
640 * Note: we deliberately include the all-zero/ones addresses.
643 if (addr_ntop(&addr
, daddr
, sizeof(daddr
)) != 0) {
645 error("Invalid address %s", host
);
648 debug("CIDR expand: address %s", daddr
);
650 if (addr_cmp(&addr
, &end_addr
) == 0)
652 addr_increment(&addr
);
661 "usage: ssh-keyscan [-46cDHqv] [-f file] [-O option] [-p port] [-T timeout]\n"
662 " [-t type] [host | addrlist namelist]\n");
667 main(int argc
, char **argv
)
669 int debug_flag
= 0, log_level
= SYSLOG_LEVEL_INFO
;
670 int opt
, fopt_count
= 0, j
;
671 char *tname
, *cp
, *line
= NULL
;
678 __progname
= ssh_get_progname(argv
[0]);
682 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
688 while ((opt
= getopt(argc
, argv
, "cDHqv46O:p:T:t:f:")) != -1) {
700 ssh_port
= a2port(optarg
);
702 fprintf(stderr
, "Bad port '%s'\n", optarg
);
707 timeout
= convtime(optarg
);
708 if (timeout
== -1 || timeout
== 0) {
709 fprintf(stderr
, "Bad timeout '%s'\n", optarg
);
716 log_level
= SYSLOG_LEVEL_DEBUG1
;
718 else if (log_level
< SYSLOG_LEVEL_DEBUG3
)
721 fatal("Too high debugging level.");
727 if (strcmp(optarg
, "-") == 0)
729 argv
[fopt_count
++] = optarg
;
732 /* Maybe other misc options in the future too */
733 if (strncmp(optarg
, "hashalg=", 8) != 0)
734 fatal("Unsupported -O option");
735 if ((hashalg
= ssh_digest_alg_by_name(
737 fatal("Unsupported hash algorithm");
741 tname
= strtok(optarg
, ",");
743 int type
= sshkey_type_from_shortname(tname
);
748 get_keytypes
|= KT_DSA
;
752 get_keytypes
|= KT_ECDSA
;
755 get_keytypes
|= KT_RSA
;
758 get_keytypes
|= KT_ED25519
;
761 get_keytypes
|= KT_XMSS
;
764 get_keytypes
|= KT_ED25519_SK
;
767 get_keytypes
|= KT_ECDSA_SK
;
771 fatal("Unknown key type \"%s\"", tname
);
773 tname
= strtok(NULL
, ",");
786 if (optind
== argc
&& !fopt_count
)
789 log_init("ssh-keyscan", log_level
, SYSLOG_FACILITY_USER
, 1);
791 maxfd
= fdlim_get(1);
793 fatal("%s: fdlim_get: bad value", __progname
);
794 if (maxfd
> MAXMAXFD
)
797 fatal("%s: not enough file descriptors", __progname
);
798 if (maxfd
> fdlim_get(0))
800 fdcon
= xcalloc(maxfd
, sizeof(con
));
801 read_wait
= xcalloc(maxfd
, sizeof(struct pollfd
));
802 for (j
= 0; j
< maxfd
; j
++)
803 read_wait
[j
].fd
= -1;
805 for (j
= 0; j
< fopt_count
; j
++) {
808 else if ((fp
= fopen(argv
[j
], "r")) == NULL
)
809 fatal("%s: %s: %s", __progname
,
810 fp
== stdin
? "<stdin>" : argv
[j
], strerror(errno
));
812 while (getline(&line
, &linesize
, fp
) != -1) {
813 /* Chomp off trailing whitespace and comments */
814 if ((cp
= strchr(line
, '#')) == NULL
)
815 cp
= line
+ strlen(line
) - 1;
817 if (*cp
== ' ' || *cp
== '\t' ||
818 *cp
== '\n' || *cp
== '#')
824 /* Skip empty lines */
832 fatal("%s: %s: %s", __progname
,
833 fp
== stdin
? "<stdin>" : argv
[j
], strerror(errno
));
840 while (optind
< argc
)
841 do_host(argv
[optind
++]);
846 return found_one
? 0 : 1;