[Libguestfs] [PATCH nbdkit v3 2/4] ip: Add filtering by process ID, user ID and group ID.

Richard W.M. Jones rjones at redhat.com
Mon Oct 5 14:58:39 UTC 2020


---
 filters/ip/nbdkit-ip-filter.pod | 63 ++++++++++++++++++++++++-----
 tests/Makefile.am               | 14 ++++++-
 filters/ip/ip.c                 | 69 +++++++++++++++++++++++++++++---
 tests/test-ip-filter-gid.sh     | 51 ++++++++++++++++++++++++
 tests/test-ip-filter-pid.sh     | 70 +++++++++++++++++++++++++++++++++
 tests/test-ip-filter-uid.sh     | 51 ++++++++++++++++++++++++
 6 files changed, 300 insertions(+), 18 deletions(-)

diff --git a/filters/ip/nbdkit-ip-filter.pod b/filters/ip/nbdkit-ip-filter.pod
index 17108617..d7e0666b 100644
--- a/filters/ip/nbdkit-ip-filter.pod
+++ b/filters/ip/nbdkit-ip-filter.pod
@@ -1,6 +1,7 @@
 =head1 NAME
 
-nbdkit-ip-filter - filter clients by IP address
+nbdkit-ip-filter - filter clients by IP address, process ID, user ID
+or group ID
 
 =head1 SYNOPSIS
 
@@ -14,6 +15,10 @@ address.  Usually it is better to control this outside nbdkit, for
 example using TCP wrappers or a firewall, but this filter can be used
 if these are not available.
 
+nbdkit E<ge> 1.24 added the ability to filter clients connecting over
+local Unix domain sockets by client process ID, user ID and group ID.
+This currently only works on Linux.
+
 =head1 EXAMPLES
 
  nbdkit --filter=ip [...] allow=127.0.0.1,::1 deny=all
@@ -28,13 +33,24 @@ network.
 
  nbdkit --filter=ip [...] allow=anyipv6 deny=all
 
-Allow IPv6 clients to connect from anywhere, deny all IPv4
-connections.
+Allow IPv6 clients to connect from anywhere, deny all other sources.
+
+ nbdkit -U $tmpdir/sock --filter=ip [...] allow=uid:`id -u` deny=all
+
+Only allow the current user (S<C<id -u>>) to connect over the socket.
+It is better to use this as an additional line of defense — also
+create a temporary directory, make sure it is only accessible by the
+user, and place the socket there.
+
+ nbdkit -U sock --filter=ip [...] allow=gid:`id -g` deny=all
+
+Allow anyone in the same group as the current user to connect to the
+Unix domain socket.
 
 =head1 RULES
 
-When a client connects, this filter checks its IP address against the
-allow and deny lists as follows:
+When a client connects, this filter checks its source address against
+the allow and deny lists as follows:
 
 =over 4
 
@@ -66,8 +82,7 @@ list of any of the following:
 
 =item B<any>
 
-These keywords (which both have the same meaning) match any IP
-address.
+These keywords (which both have the same meaning) match any source.
 
 =item B<allipv4>
 
@@ -100,6 +115,31 @@ address representations can be used (see S<RFC 5952>).
 
 This matches a range of IPv6 addresses C<A:B:.../NN>.
 
+=item B<pid:>PID
+
+(nbdkit E<ge> 1.24, Linux only)
+
+This matches the process ID C<PID>, if the client connects over a Unix
+domain socket.
+
+Note that process IDs are recycled so this alone is not secure enough
+to ensure that only a single desired process can connect.  However you
+could add it as an additional check.
+
+=item B<uid:>UID
+
+(nbdkit E<ge> 1.24, Linux only)
+
+This matches the numeric user ID C<UID>, if the client connects over a
+Unix domain socket.
+
+=item B<gid:>GID
+
+(nbdkit E<ge> 1.24, Linux only)
+
+This matches the numeric group ID C<GID>, if the client connects over
+a Unix domain socket.
+
 =back
 
 =head2 Not filtered
@@ -107,8 +147,11 @@ This matches a range of IPv6 addresses C<A:B:.../NN>.
 If neither the C<allow> nor the C<deny> parameter is given the filter
 does nothing.
 
-The filter permits non-IP connections, such as Unix domain sockets or
-AF_VSOCK.
+C<AF_VSOCK> connections are always unfiltered.
+
+Unix domain sockets were always unfiltered in S<nbdkit E<le> 1.22>.
+In S<nbdkit E<ge> 1.24> it is possible to apply filtering to them on
+Linux.
 
 =head1 PARAMETERS
 
@@ -155,4 +198,4 @@ Richard W.M. Jones
 
 =head1 COPYRIGHT
 
-Copyright (C) 2019 Red Hat Inc.
+Copyright (C) 2019-2020 Red Hat Inc.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index b5b06810..cacbbce9 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1437,8 +1437,18 @@ test_gzip_CFLAGS = $(WARNINGS_CFLAGS) $(LIBGUESTFS_CFLAGS)
 test_gzip_LDADD = libtest.la $(LIBGUESTFS_LIBS)
 
 # ip filter test.
-TESTS += test-ip-filter.sh
-EXTRA_DIST += test-ip-filter.sh
+TESTS += \
+	test-ip-filter.sh \
+	test-ip-filter-pid.sh \
+	test-ip-filter-uid.sh \
+	test-ip-filter-gid.sh \
+	$(NULL)
+EXTRA_DIST += \
+	test-ip-filter.sh \
+	test-ip-filter-pid.sh \
+	test-ip-filter-uid.sh \
+	test-ip-filter-gid.sh \
+	$(NULL)
 
 # limit filter test.
 TESTS += test-limit.sh
diff --git a/filters/ip/ip.c b/filters/ip/ip.c
index b16ec55c..2f702c01 100644
--- a/filters/ip/ip.c
+++ b/filters/ip/ip.c
@@ -62,12 +62,13 @@ int ip_debug_rules;
 
 struct rule {
   struct rule *next;
-  enum { BAD = 0, ANY, ANYV4, ANYV6, IPV4, IPV6 } type;
+  enum { BAD = 0, ANY, ANYV4, ANYV6, IPV4, IPV6, PID, UID, GID } type;
   union {
-    struct in_addr ipv4;
+    struct in_addr ipv4;        /* for IPV4, IPV6 */
     struct in6_addr ipv6;
+    int64_t id;                 /* for PID, UID and GID */
   } u;
-  unsigned prefixlen;
+  unsigned prefixlen;           /* for IPV4, IPV6 */
 };
 
 static struct rule *allow_rules, *allow_rules_last;
@@ -100,6 +101,16 @@ print_rule (const char *name, const struct rule *rule, const char *suffix)
     nbdkit_debug ("%s=ipv6:[%s]/%u%s", name, u.addr6, rule->prefixlen, suffix);
     break;
 
+  case PID:
+    nbdkit_debug ("%s=pid:%" PRIi64 "%s", name, rule->u.id, suffix);
+    break;
+  case UID:
+    nbdkit_debug ("%s=uid:%" PRIi64 "%s", name, rule->u.id, suffix);
+    break;
+  case GID:
+    nbdkit_debug ("%s=gid:%" PRIi64 "%s", name, rule->u.id, suffix);
+    break;
+
   case BAD:
     nbdkit_debug ("%s=BAD(!)%s", name, suffix);
     break;
@@ -227,6 +238,37 @@ parse_rule (const char *paramname,
     return 0;
   }
 
+  if (n >= 4 && ascii_strncasecmp (value, "pid:", 4) == 0) {
+    new_rule->type = PID;
+    if (nbdkit_parse_int64_t ("pid:", &value[4], &new_rule->u.id) == -1)
+      return -1;
+    if (new_rule->u.id <= 0) {
+      nbdkit_error ("pid: parameter out of range");
+      return -1;
+    }
+    return 0;
+  }
+  if (n >= 4 && ascii_strncasecmp (value, "uid:", 4) == 0) {
+    new_rule->type = UID;
+    if (nbdkit_parse_int64_t ("uid:", &value[4], &new_rule->u.id) == -1)
+      return -1;
+    if (new_rule->u.id < 0) {
+      nbdkit_error ("uid: parameter out of range");
+      return -1;
+    }
+    return 0;
+  }
+  if (n >= 4 && ascii_strncasecmp (value, "gid:", 4) == 0) {
+    new_rule->type = GID;
+    if (nbdkit_parse_int64_t ("gid:", &value[4], &new_rule->u.id) == -1)
+      return -1;
+    if (new_rule->u.id < 0) {
+      nbdkit_error ("gid: parameter out of range");
+      return -1;
+    }
+    return 0;
+  }
+
   /* Address with prefixlen. */
   if ((p = strchr (value, '/')) != NULL) {
     size_t pllen = &value[n] - &p[1];
@@ -401,6 +443,19 @@ matches_rule (const struct rule *rule,
     sin6 = (struct sockaddr_in6 *) addr;
     return ipv6_equal (sin6->sin6_addr, rule->u.ipv6, rule->prefixlen);
 
+    /* Note these work even if the underlying nbdkit_peer_* call fails. */
+  case PID:
+    if (family != AF_UNIX) return false;
+    return nbdkit_peer_pid () == rule->u.id;
+
+  case UID:
+    if (family != AF_UNIX) return false;
+    return nbdkit_peer_uid () == rule->u.id;
+
+  case GID:
+    if (family != AF_UNIX) return false;
+    return nbdkit_peer_gid () == rule->u.id;
+
   case BAD:
   default:
     abort ();
@@ -430,8 +485,10 @@ check_if_allowed (const struct sockaddr *addr)
 {
   int family = ((struct sockaddr_in *)addr)->sin_family;
 
-  /* There's an implicit allow all for non-IP sockets, see the manual. */
-  if (family != AF_INET && family != AF_INET6)
+  /* There's an implicit allow all for non-IP, non-Unix sockets,
+   * see the manual.
+   */
+  if (family != AF_INET && family != AF_INET6 && family != AF_UNIX)
     return true;
 
   if (matches_rules_list ("ip: match source with allow",
@@ -457,7 +514,7 @@ ip_preconnect (nbdkit_next_preconnect *next, void *nxdata, int readonly)
   /* Follow the rules. */
   if (check_if_allowed ((struct sockaddr *) &addr) == false) {
     nbdkit_error ("client not permitted to connect "
-                  "because of IP address restriction");
+                  "because of source address restriction");
     return -1;
   }
 
diff --git a/tests/test-ip-filter-gid.sh b/tests/test-ip-filter-gid.sh
new file mode 100755
index 00000000..d02407f3
--- /dev/null
+++ b/tests/test-ip-filter-gid.sh
@@ -0,0 +1,51 @@
+#!/usr/bin/env bash
+# nbdkit
+# Copyright (C) 2020 Red Hat Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the name of Red Hat nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+# Test the ip filter with gid: parameter.
+
+source ./functions.sh
+set -e
+set -x
+
+requires qemu-img --version
+# This requires Linux.
+requires_linux_kernel_version 2.6
+
+nbdkit -U - -v -D ip.rules=1 --filter=ip null allow=gid:`id -g` deny=all \
+       --run 'qemu-img info $nbd'
+
+# This is expected to fail.
+if nbdkit -U - -v -D ip.rules=1 --filter=ip null deny=gid:`id -g` \
+          --run 'qemu-img info $nbd'; then
+    echo "$0: expected test to fail"
+    exit 1
+fi
diff --git a/tests/test-ip-filter-pid.sh b/tests/test-ip-filter-pid.sh
new file mode 100755
index 00000000..4a7f55a3
--- /dev/null
+++ b/tests/test-ip-filter-pid.sh
@@ -0,0 +1,70 @@
+#!/usr/bin/env bash
+# nbdkit
+# Copyright (C) 2020 Red Hat Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the name of Red Hat nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+# Test the ip filter with pid: parameter.
+
+source ./functions.sh
+set -e
+set -x
+
+requires nbdinfo --version
+requires nbdsh --version
+requires_nbdsh_uri
+# This requires Linux.
+requires_linux_kernel_version 2.6
+
+# This is expected to fail because the shell ($$) is not connecting to
+# the server.
+if nbdkit -U - -v -D ip.rules=1 --filter=ip null allow=pid:$$ deny=all \
+          --run 'nbdinfo --size "$uri"'; then
+    echo "$0: expected test to fail"
+    exit 1
+fi
+
+# This is expected to work because we can deny the shell.
+nbdkit -U - -v -D ip.rules=1 --filter=ip null deny=pid:$$ \
+       --run 'nbdinfo --size "$uri"'
+
+# This is a better test using nbdsh and passing the PID of nbdsh
+# itself to nbdkit.  Note this only works because nbd_connect_command
+# uses a socketpair which is a kind of nameless Unix domain socket.
+nbdsh -c - <<'EOF'
+import os
+
+h.connect_command(["nbdkit", "-s",
+                   "-v", "-D", "ip.rules=1",
+                   "null", "size=512",
+                   "--filter=ip",
+                   "allow=pid:" + str(os.getpid()),
+                   "deny=all"])
+assert h.get_size() == 512
+EOF
diff --git a/tests/test-ip-filter-uid.sh b/tests/test-ip-filter-uid.sh
new file mode 100755
index 00000000..dc6ab679
--- /dev/null
+++ b/tests/test-ip-filter-uid.sh
@@ -0,0 +1,51 @@
+#!/usr/bin/env bash
+# nbdkit
+# Copyright (C) 2020 Red Hat Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the name of Red Hat nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+# Test the ip filter with uid: parameter.
+
+source ./functions.sh
+set -e
+set -x
+
+requires qemu-img --version
+# This requires Linux.
+requires_linux_kernel_version 2.6
+
+nbdkit -U - -v -D ip.rules=1 --filter=ip null allow=uid:`id -u` deny=all \
+       --run 'qemu-img info $nbd'
+
+# This is expected to fail.
+if nbdkit -U - -v -D ip.rules=1 --filter=ip null deny=uid:`id -u` \
+          --run 'qemu-img info $nbd'; then
+    echo "$0: expected test to fail"
+    exit 1
+fi
-- 
2.27.0




More information about the Libguestfs mailing list