---
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