allow ssh-keyscan(1) to accept CIDR address ranges, e.g.
authordjm <djm@openbsd.org>
Fri, 28 Oct 2022 02:29:34 +0000 (02:29 +0000)
committerdjm <djm@openbsd.org>
Fri, 28 Oct 2022 02:29:34 +0000 (02:29 +0000)
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@

usr.bin/ssh/addr.c
usr.bin/ssh/addr.h
usr.bin/ssh/ssh-keyscan.1
usr.bin/ssh/ssh-keyscan.c

index 8774764..056f159 100644 (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>
@@ -223,6 +223,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
 addr_cmp(const struct xaddr *a, const struct xaddr *b)
 {
@@ -274,6 +296,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'
  * is all zeros.
@@ -293,6 +338,32 @@ addr_host_is_all0s(const struct xaddr *a, u_int masklen)
        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'.
  * Returns 0 on success, -1 on failure.
index 5eff026..180e9fd 100644 (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_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_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_is_all0s(const struct xaddr *n);
 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,
     u_int masklen);
+void addr_increment(struct xaddr *a);
 #endif /* _ADDR_H */
index 4eb0bea..ca4feea 100644 (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>.
 .\"
@@ -6,7 +6,7 @@
 .\" permitted provided that due credit is given to the author and the
 .\" OpenBSD project by leaving this copyright notice intact.
 .\"
-.Dd $Mdocdate: June 3 2022 $
+.Dd $Mdocdate: October 28 2022 $
 .Dt SSH-KEYSCAN 1
 .Os
 .Sh NAME
@@ -44,6 +44,11 @@ For scanning, one does not need
 login access to the machines that are being scanned, nor does the
 scanning process involve any encryption.
 .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:
 .Bl -tag -width Ds
 .It Fl 4
@@ -73,9 +78,16 @@ If
 is supplied instead of a filename,
 .Nm
 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
-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
 .It Fl H
 Hash all hostnames and addresses in the output.
@@ -138,6 +150,10 @@ Print the RSA host key for machine
 .Pp
 .Dl $ ssh-keyscan -t rsa hostname
 .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
 .Pa ssh_hosts
 which have new or different keys from those in the sorted file
index 225884e..6794b15 100644 (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>.
  *
@@ -44,6 +44,7 @@
 #include "ssherr.h"
 #include "ssh_api.h"
 #include "dns.h"
+#include "addr.h"
 
 /* Flag indicating whether IPv4 or IPv6.  This can be set on the command line.
    Default value is AF_UNSPEC means both IPv4 and IPv6. */
@@ -364,7 +365,7 @@ tcpconnect(char *host)
 }
 
 static int
-conalloc(char *iname, char *oname, int keytype)
+conalloc(const char *iname, const char *oname, int keytype)
 {
        char *namebase, *name, *namelist;
        int s;
@@ -609,7 +610,7 @@ conloop(void)
 }
 
 static void
-do_host(char *host)
+do_one_host(char *host)
 {
        char *name = strnnsep(&host, " \t\n");
        int j;
@@ -625,6 +626,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
 sshfatal(const char *file, const char *func, int line, int showfunc,
     LogLevel level, const char *suffix, const char *fmt, ...)