New nbdkit_peer_pid, nbdkit_peer_uid and nbdkit_peer_gid calls can be
used on Linux (only) to read the peer PID, UID and GID from clients
connected over a Unix domain socket. This can be used in the
preconnect phase to add additional filtering.
One use for this is to add an extra layer of authentication for local
connections. A subsequent commit will enhance the now misnamed
nbdkit-ip-filter to allow filtering on these extra fields.
It appears as if it would be possible to implement this for FreeBSD
too (see comment in code).
---
docs/nbdkit-plugin.pod | 47 +++++++++++++++--
include/nbdkit-common.h | 3 ++
server/nbdkit.syms | 3 ++
server/public.c | 108 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 156 insertions(+), 5 deletions(-)
diff --git a/docs/nbdkit-plugin.pod b/docs/nbdkit-plugin.pod
index 62feae47..e1f10984 100644
--- a/docs/nbdkit-plugin.pod
+++ b/docs/nbdkit-plugin.pod
@@ -656,9 +656,10 @@ B<not> been negotiated at this point.
For security reasons (to avoid denial of service attacks) this
callback should be written to be as fast and take as few resources as
possible. If you use this callback, only use it to do basic access
-control, such as checking C<nbdkit_peer_name> against a list of
-permitted IP addresses (see L</PEER NAME> and L<nbdkit-ip-filter(1)>).
-It may be better to do access control outside the server, for example
+control, such as checking C<nbdkit_peer_name>, C<nbdkit_peer_pid>,
+C<nbdkit_peer_uid>, C<nbdkit_peer_gid> against a list of permitted
+source addresses (see L</PEER NAME> and L<nbdkit-ip-filter(1)>). It
+may be better to do access control outside the server, for example
using TCP wrappers or a firewall.
The C<readonly> flag informs the plugin that the server was started
@@ -1614,8 +1615,8 @@ C<.open>), C<nbdkit_error> is called and the call
returns C<-1>.
=head1 PEER NAME
-It is possible to get the address of the client when you are running
-in any connected callback.
+It is possible to get the source address of the client when you are
+running in any connected callback.
=head2 C<nbdkit_peer_name>
@@ -1635,6 +1636,42 @@ specify the current connection.
On success this returns C<0>. On error, C<nbdkit_error> is called and
this call returns C<-1>.
+=head2 C<nbdkit_peer_pid>
+
+(nbdkit E<ge> 1.24, Linux only)
+
+ int64_t nbdkit_peer_pid (void);
+
+Return the peer process ID. This is only available when the client
+connected over a Unix domain socket.
+
+On success this returns the peer process ID. On error,
+C<nbdkit_error> is called and this call returns C<-1>.
+
+=head2 C<nbdkit_peer_uid>
+
+(nbdkit E<ge> 1.24, Linux only)
+
+ int64_t nbdkit_peer_uid (void);
+
+Return the peer user ID. This is only available when the client
+connected over a Unix domain socket.
+
+On success this returns the user ID. On error, C<nbdkit_error> is
+called and this call returns C<-1>.
+
+=head2 C<nbdkit_peer_gid>
+
+(nbdkit E<ge> 1.24, Linux only)
+
+ int64_t nbdkit_peer_gid (void);
+
+Return the peer group ID. This is only available when the client
+connected over a Unix domain socket.
+
+On success this returns the user ID. On error, C<nbdkit_error> is
+called and this call returns C<-1>.
+
=head1 DEBUGGING
Run the server with I<-f> and I<-v> options so it doesn't fork and you
diff --git a/include/nbdkit-common.h b/include/nbdkit-common.h
index eb4416df..51a6264c 100644
--- a/include/nbdkit-common.h
+++ b/include/nbdkit-common.h
@@ -130,6 +130,9 @@ NBDKIT_EXTERN_DECL (char *, nbdkit_realpath, (const char *path));
NBDKIT_EXTERN_DECL (int, nbdkit_nanosleep, (unsigned sec, unsigned nsec));
NBDKIT_EXTERN_DECL (int, nbdkit_peer_name,
(struct sockaddr *addr, socklen_t *addrlen));
+NBDKIT_EXTERN_DECL (int64_t, nbdkit_peer_pid, (void));
+NBDKIT_EXTERN_DECL (int64_t, nbdkit_peer_uid, (void));
+NBDKIT_EXTERN_DECL (int64_t, nbdkit_peer_gid, (void));
NBDKIT_EXTERN_DECL (void, nbdkit_shutdown, (void));
NBDKIT_EXTERN_DECL (const char *, nbdkit_strdup_intern,
diff --git a/server/nbdkit.syms b/server/nbdkit.syms
index 1eb18bb0..3d6b2235 100644
--- a/server/nbdkit.syms
+++ b/server/nbdkit.syms
@@ -67,7 +67,10 @@
nbdkit_parse_uint32_t;
nbdkit_parse_uint64_t;
nbdkit_parse_unsigned;
+ nbdkit_peer_gid;
nbdkit_peer_name;
+ nbdkit_peer_pid;
+ nbdkit_peer_uid;
nbdkit_printf_intern;
nbdkit_read_password;
nbdkit_realpath;
diff --git a/server/public.c b/server/public.c
index 7636a16b..0ec33a17 100644
--- a/server/public.c
+++ b/server/public.c
@@ -47,6 +47,7 @@
#include <limits.h>
#include <errno.h>
#include <signal.h>
+#include <sys/types.h>
#ifdef HAVE_TERMIOS_H
#include <termios.h>
@@ -801,6 +802,113 @@ nbdkit_peer_name (struct sockaddr *addr, socklen_t *addrlen)
return 0;
}
+#ifdef SO_PEERCRED
+
+static int
+get_peercred (int s, int64_t *pid, int64_t *uid, int64_t *gid)
+{
+ struct ucred ucred;
+ socklen_t n = sizeof ucred;
+
+ if (getsockopt (s, SOL_SOCKET, SO_PEERCRED, &ucred, &n) == -1) {
+ nbdkit_error ("getsockopt: SO_PEERCRED: %m");
+ return -1;
+ }
+
+ if (pid && ucred.pid >= 1) {
+ if (ucred.pid <= INT64_MAX)
+ *pid = ucred.pid;
+ else
+ nbdkit_error ("pid out of range: cannot be mapped to int64_t");
+ }
+ if (uid && ucred.uid >= 0) {
+ if (ucred.uid <= INT64_MAX)
+ *uid = ucred.uid;
+ else
+ nbdkit_error ("uid out of range: cannot be mapped to int64_t");
+ }
+ if (gid && ucred.gid >= 0) {
+ if (ucred.gid <= INT64_MAX)
+ *gid = ucred.gid;
+ else
+ nbdkit_error ("gid out of range: cannot be mapped to int64_t");
+ }
+
+ return 0;
+}
+
+#else /* !SO_PEERCRED */
+
+/* Note this could be ported to FreeBSD, see LOCAL_PEERCRED in:
+ *
https://www.freebsd.org/cgi/man.cgi?query=unix&sektion=4
+ */
+static int
+get_peercred (int s, int64_t *pid, int64_t *uid, int64_t *gid)
+{
+ nbdkit_error ("nbdkit_peer_pid, nbdkit_peer_uid and nbdkit_peer_gid "
+ "are not supported on this platform");
+ return -1;
+}
+
+#endif /* !SO_PEERCRED */
+
+static int
+get_peercred_common (int64_t *pid, int64_t *uid, int64_t *gid)
+{
+ struct connection *conn = threadlocal_get_conn ();
+ int s;
+
+ if (pid) *pid = -1;
+ if (uid) *uid = -1;
+ if (gid) *gid = -1;
+
+ if (!conn) {
+ nbdkit_error ("no connection in this thread");
+ return -1;
+ }
+
+ s = conn->sockin;
+ if (s == -1) {
+ nbdkit_error ("socket not open");
+ return -1;
+ }
+
+ return get_peercred (s, pid, uid, gid);
+}
+
+int64_t
+nbdkit_peer_pid ()
+{
+ int64_t pid;
+
+ if (get_peercred_common (&pid, NULL, NULL) == -1)
+ return -1;
+
+ return pid;
+}
+
+int64_t
+nbdkit_peer_uid ()
+{
+ int64_t uid;
+
+ if (get_peercred_common (NULL, &uid, NULL) == -1)
+ return -1;
+
+ return uid;
+}
+
+int64_t
+nbdkit_peer_gid ()
+{
+ int64_t gid;
+
+ if (get_peercred_common (NULL, NULL, &gid) == -1)
+ return -1;
+
+ return gid;
+}
+
/* Functions for manipulating intern'd strings. */
static string_vector global_interns;
--
2.27.0