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..c2b9fb12 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)
+
+ int nbdkit_peer_pid (void);
+
+Return the peer process ID. This is only available when the client
+connected over a Unix domain socket, and only works for Linux.
+
+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)
+
+ int nbdkit_peer_uid (void);
+
+Return the peer user ID. This is only available when the client
+connected over a Unix domain socket, and only works for Linux.
+
+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)
+
+ int nbdkit_peer_gid (void);
+
+Return the peer group ID. This is only available when the client
+connected over a Unix domain socket, and only works for Linux.
+
+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..7690b57e 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 (int, nbdkit_peer_pid, (void));
+NBDKIT_EXTERN_DECL (int, nbdkit_peer_uid, (void));
+NBDKIT_EXTERN_DECL (int, 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..8b7ca437 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, int *pid, int *uid, int *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 <= INT_MAX)
+ *pid = ucred.pid;
+ else
+ nbdkit_error ("pid out of range: cannot be mapped to int");
+ }
+ if (uid && ucred.uid >= 0) {
+ if (ucred.uid <= INT_MAX)
+ *uid = ucred.uid;
+ else
+ nbdkit_error ("uid out of range: cannot be mapped to int");
+ }
+ if (gid && ucred.gid >= 0) {
+ if (ucred.gid <= INT_MAX)
+ *gid = ucred.gid;
+ else
+ nbdkit_error ("gid out of range: cannot be mapped to int");
+ }
+
+ 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, int *pid, int *uid, int *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 (int *pid, int *uid, int *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);
+}
+
+int
+nbdkit_peer_pid ()
+{
+ int pid;
+
+ if (get_peercred_common (&pid, NULL, NULL) == -1)
+ return -1;
+
+ return pid;
+}
+
+int
+nbdkit_peer_uid ()
+{
+ int uid;
+
+ if (get_peercred_common (NULL, &uid, NULL) == -1)
+ return -1;
+
+ return uid;
+}
+
+int
+nbdkit_peer_gid ()
+{
+ int 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