upstream: allow ssh-keyscan(1) to accept CIDR address ranges, e.g.

ssh-keyscan 192.168.0.0/24

If a CIDR range is passed, then it will be expanded to all possible
addresses in the range including the all-0s and all-1s addresses.

bz#976 feedback/ok markus@

OpenBSD-Commit-ID: ce6c5211f936ac0053fd4a2ddb415277931e6c4b
This commit is contained in:
djm@openbsd.org 2022-10-28 02:29:34 +00:00 committed by Damien Miller
parent 64af420930
commit 1192588546
No known key found for this signature in database
4 changed files with 136 additions and 8 deletions

73
addr.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: addr.c,v 1.5 2022/04/29 04:55:07 djm Exp $ */ /* $OpenBSD: addr.c,v 1.6 2022/10/28 02:29:34 djm Exp $ */
/* /*
* Copyright (c) 2004-2008 Damien Miller <djm@mindrot.org> * Copyright (c) 2004-2008 Damien Miller <djm@mindrot.org>
@ -227,6 +227,28 @@ addr_and(struct xaddr *dst, const struct xaddr *a, const struct xaddr *b)
} }
} }
int
addr_or(struct xaddr *dst, const struct xaddr *a, const struct xaddr *b)
{
int i;
if (dst == NULL || a == NULL || b == NULL || a->af != b->af)
return (-1);
memcpy(dst, a, sizeof(*dst));
switch (a->af) {
case AF_INET:
dst->v4.s_addr |= b->v4.s_addr;
return (0);
case AF_INET6:
for (i = 0; i < 4; i++)
dst->addr32[i] |= b->addr32[i];
return (0);
default:
return (-1);
}
}
int int
addr_cmp(const struct xaddr *a, const struct xaddr *b) addr_cmp(const struct xaddr *a, const struct xaddr *b)
{ {
@ -278,6 +300,29 @@ addr_is_all0s(const struct xaddr *a)
} }
} }
/* Increment the specified address. Note, does not do overflow checking */
void
addr_increment(struct xaddr *a)
{
int i;
uint32_t n;
switch (a->af) {
case AF_INET:
a->v4.s_addr = htonl(ntohl(a->v4.s_addr) + 1);
break;
case AF_INET6:
for (i = 0; i < 4; i++) {
/* Increment with carry */
n = ntohl(a->addr32[3 - i]) + 1;
a->addr32[3 - i] = htonl(n);
if (n != 0)
break;
}
break;
}
}
/* /*
* Test whether host portion of address 'a', as determined by 'masklen' * Test whether host portion of address 'a', as determined by 'masklen'
* is all zeros. * is all zeros.
@ -297,6 +342,32 @@ addr_host_is_all0s(const struct xaddr *a, u_int masklen)
return addr_is_all0s(&tmp_result); return addr_is_all0s(&tmp_result);
} }
#if 0
int
addr_host_to_all0s(struct xaddr *a, u_int masklen)
{
struct xaddr tmp_mask;
if (addr_netmask(a->af, masklen, &tmp_mask) == -1)
return (-1);
if (addr_and(a, a, &tmp_mask) == -1)
return (-1);
return (0);
}
#endif
int
addr_host_to_all1s(struct xaddr *a, u_int masklen)
{
struct xaddr tmp_mask;
if (addr_hostmask(a->af, masklen, &tmp_mask) == -1)
return (-1);
if (addr_or(a, a, &tmp_mask) == -1)
return (-1);
return (0);
}
/* /*
* Parse string address 'p' into 'n'. * Parse string address 'p' into 'n'.
* Returns 0 on success, -1 on failure. * Returns 0 on success, -1 on failure.

4
addr.h
View File

@ -52,9 +52,13 @@ int addr_sa_pton(const char *h, const char *s, struct sockaddr *sa,
int addr_pton_cidr(const char *p, struct xaddr *n, u_int *l); int addr_pton_cidr(const char *p, struct xaddr *n, u_int *l);
int addr_ntop(const struct xaddr *n, char *p, size_t len); int addr_ntop(const struct xaddr *n, char *p, size_t len);
int addr_and(struct xaddr *dst, const struct xaddr *a, const struct xaddr *b); int addr_and(struct xaddr *dst, const struct xaddr *a, const struct xaddr *b);
int addr_or(struct xaddr *dst, const struct xaddr *a, const struct xaddr *b);
int addr_cmp(const struct xaddr *a, const struct xaddr *b); int addr_cmp(const struct xaddr *a, const struct xaddr *b);
int addr_is_all0s(const struct xaddr *n); int addr_is_all0s(const struct xaddr *n);
int addr_host_is_all0s(const struct xaddr *n, u_int masklen); int addr_host_is_all0s(const struct xaddr *n, u_int masklen);
int addr_host_to_all0s(struct xaddr *a, u_int masklen);
int addr_host_to_all1s(struct xaddr *a, u_int masklen);
int addr_netmatch(const struct xaddr *host, const struct xaddr *net, int addr_netmatch(const struct xaddr *host, const struct xaddr *net,
u_int masklen); u_int masklen);
void addr_increment(struct xaddr *a);
#endif /* _ADDR_H */ #endif /* _ADDR_H */

View File

@ -1,4 +1,4 @@
.\" $OpenBSD: ssh-keyscan.1,v 1.46 2022/06/03 04:00:15 dtucker Exp $ .\" $OpenBSD: ssh-keyscan.1,v 1.47 2022/10/28 02:29:34 djm Exp $
.\" .\"
.\" Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>. .\" Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>.
.\" .\"
@ -6,7 +6,7 @@
.\" permitted provided that due credit is given to the author and the .\" permitted provided that due credit is given to the author and the
.\" OpenBSD project by leaving this copyright notice intact. .\" OpenBSD project by leaving this copyright notice intact.
.\" .\"
.Dd $Mdocdate: June 3 2022 $ .Dd $Mdocdate: October 28 2022 $
.Dt SSH-KEYSCAN 1 .Dt SSH-KEYSCAN 1
.Os .Os
.Sh NAME .Sh NAME
@ -44,6 +44,11 @@ For scanning, one does not need
login access to the machines that are being scanned, nor does the login access to the machines that are being scanned, nor does the
scanning process involve any encryption. scanning process involve any encryption.
.Pp .Pp
Hosts to be scanned may be specified by hostname, address or by CIDR
network range (e.g. 192.168.16/28).
If a network range is specified, then all addresses in that range will
be scanned.
.Pp
The options are as follows: The options are as follows:
.Bl -tag -width Ds .Bl -tag -width Ds
.It Fl 4 .It Fl 4
@ -73,9 +78,16 @@ If
is supplied instead of a filename, is supplied instead of a filename,
.Nm .Nm
will read from the standard input. will read from the standard input.
Input is expected in the format: Names read from a file must start with an address, hostname or CIDR network
range to be scanned.
Addresses and hostnames may optionally be followed by comma-separated name
or address aliases that will be copied to the output.
For example:
.Bd -literal .Bd -literal
1.2.3.4,1.2.4.4 name.my.domain,name,n.my.domain,n,1.2.3.4,1.2.4.4 192.168.11.0/24
10.20.1.1
happy.example.org
10.0.0.1,sad.example.org
.Ed .Ed
.It Fl H .It Fl H
Hash all hostnames and addresses in the output. Hash all hostnames and addresses in the output.
@ -138,6 +150,10 @@ Print the RSA host key for machine
.Pp .Pp
.Dl $ ssh-keyscan -t rsa hostname .Dl $ ssh-keyscan -t rsa hostname
.Pp .Pp
Search a network range, printing all supported key types:
.Pp
.Dl $ ssh-keyscan 192.168.0.64/25
.Pp
Find all hosts from the file Find all hosts from the file
.Pa ssh_hosts .Pa ssh_hosts
which have new or different keys from those in the sorted file which have new or different keys from those in the sorted file

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-keyscan.c,v 1.146 2022/08/19 04:02:46 dtucker Exp $ */ /* $OpenBSD: ssh-keyscan.c,v 1.147 2022/10/28 02:29:34 djm Exp $ */
/* /*
* Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>. * Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>.
* *
@ -52,6 +52,7 @@
#include "ssherr.h" #include "ssherr.h"
#include "ssh_api.h" #include "ssh_api.h"
#include "dns.h" #include "dns.h"
#include "addr.h"
/* Flag indicating whether IPv4 or IPv6. This can be set on the command line. /* Flag indicating whether IPv4 or IPv6. This can be set on the command line.
Default value is AF_UNSPEC means both IPv4 and IPv6. */ Default value is AF_UNSPEC means both IPv4 and IPv6. */
@ -384,7 +385,7 @@ tcpconnect(char *host)
} }
static int static int
conalloc(char *iname, char *oname, int keytype) conalloc(const char *iname, const char *oname, int keytype)
{ {
char *namebase, *name, *namelist; char *namebase, *name, *namelist;
int s; int s;
@ -629,7 +630,7 @@ conloop(void)
} }
static void static void
do_host(char *host) do_one_host(char *host)
{ {
char *name = strnnsep(&host, " \t\n"); char *name = strnnsep(&host, " \t\n");
int j; int j;
@ -645,6 +646,42 @@ do_host(char *host)
} }
} }
static void
do_host(char *host)
{
char daddr[128];
struct xaddr addr, end_addr;
u_int masklen;
if (host == NULL)
return;
if (addr_pton_cidr(host, &addr, &masklen) != 0) {
/* Assume argument is a hostname */
do_one_host(host);
} else {
/* Argument is a CIDR range */
debug("CIDR range %s", host);
end_addr = addr;
if (addr_host_to_all1s(&end_addr, masklen) != 0)
goto badaddr;
/*
* Note: we deliberately include the all-zero/ones addresses.
*/
for (;;) {
if (addr_ntop(&addr, daddr, sizeof(daddr)) != 0) {
badaddr:
error("Invalid address %s", host);
return;
}
debug("CIDR expand: address %s", daddr);
do_one_host(daddr);
if (addr_cmp(&addr, &end_addr) == 0)
break;
addr_increment(&addr);
};
}
}
void void
sshfatal(const char *file, const char *func, int line, int showfunc, sshfatal(const char *file, const char *func, int line, int showfunc,
LogLevel level, const char *suffix, const char *fmt, ...) LogLevel level, const char *suffix, const char *fmt, ...)