[master] 94443fe Add VMOD unix.
Geoff Simmons
geoff at uplex.de
Mon Mar 12 10:38:08 UTC 2018
commit 94443fed07dc76d92afa868925b68fa5c1eb3f73
Author: Geoff Simmons <geoff at uplex.de>
Date: Mon Mar 12 11:32:35 2018 +0100
Add VMOD unix.
diff --git a/bin/varnishtest/tests/m00047.vtc b/bin/varnishtest/tests/m00047.vtc
new file mode 100644
index 0000000..bfc8f7c
--- /dev/null
+++ b/bin/varnishtest/tests/m00047.vtc
@@ -0,0 +1,130 @@
+varnishtest "VMOD unix"
+
+# This test requires some manual verification, by checking the log,
+# because support for peer credentials via UDS varies by platform, see
+# below.
+
+server s1 {
+ rxreq
+ txresp
+} -start
+
+varnish v1 -vcl+backend {
+ import unix;
+
+ sub vcl_backend_response {
+ set beresp.http.b-uid = unix.uid();
+ set beresp.http.b-gid = unix.gid();
+ set beresp.http.b-user = unix.user();
+ set beresp.http.b-group = unix.group();
+ }
+
+ sub vcl_deliver {
+ set resp.http.c-uid = unix.uid();
+ set resp.http.c-gid = unix.gid();
+ set resp.http.c-user = unix.user();
+ set resp.http.c-group = unix.group();
+ }
+} -start
+
+client c1 {
+ txreq
+ rxresp
+ expect resp.status == 200
+ expect resp.http.b-uid == -1
+ expect resp.http.b-gid == -1
+ expect resp.http.b-user == ""
+ expect resp.http.b-group == ""
+ expect resp.http.c-uid == -1
+ expect resp.http.c-gid == -1
+ expect resp.http.c-user == ""
+ expect resp.http.c-group == ""
+} -run
+
+logexpect l1 -v v1 -d 1 -b {
+ expect * 1002 Begin bereq
+ expect * = VCL_Error {^vmod unix error: not listening on a Unix domain socket$}
+ expect * = VCL_Error {^vmod unix error: not listening on a Unix domain socket$}
+ expect * = VCL_Error {^vmod unix error: not listening on a Unix domain socket$}
+ expect * = VCL_Error {^vmod unix error: not listening on a Unix domain socket$}
+} -run
+
+logexpect l1 -v v1 -d 1 -c {
+ expect * 1001 Begin req
+ expect * = VCL_Error {^vmod unix error: not listening on a Unix domain socket$}
+ expect * = VCL_Error {^vmod unix error: not listening on a Unix domain socket$}
+ expect * = VCL_Error {^vmod unix error: not listening on a Unix domain socket$}
+ expect * = VCL_Error {^vmod unix error: not listening on a Unix domain socket$}
+} -run
+
+
+varnish v1 -errvcl {vmod unix failure: may not be called in vcl_init or vcl_fini} {
+ import unix;
+ import std;
+ backend b { .host = "${bad_ip}"; }
+
+ sub vcl_init {
+ std.log(unix.uid());
+ }
+}
+
+varnish v1 -errvcl {vmod unix failure: may not be called in vcl_init or vcl_fini} {
+ import unix;
+ import std;
+ backend b { .host = "${bad_ip}"; }
+
+ sub vcl_init {
+ std.log(unix.gid());
+ }
+}
+
+varnish v1 -errvcl {vmod unix failure: may not be called in vcl_init or vcl_fini} {
+ import unix;
+ import std;
+ backend b { .host = "${bad_ip}"; }
+
+ sub vcl_init {
+ std.log(unix.user());
+ }
+}
+
+varnish v1 -errvcl {vmod unix failure: may not be called in vcl_init or vcl_fini} {
+ import unix;
+ import std;
+ backend b { .host = "${bad_ip}"; }
+
+ sub vcl_init {
+ std.log(unix.group());
+ }
+}
+
+varnish v1 -stop
+server s1 -wait
+server s1 -start
+
+varnish v2 -arg "-a ${tmpdir}/v2.sock" -vcl+backend {
+ import unix;
+
+ sub vcl_backend_response {
+ set beresp.http.b-uid = unix.uid();
+ set beresp.http.b-gid = unix.gid();
+ set beresp.http.b-user = unix.user();
+ set beresp.http.b-group = unix.group();
+ }
+
+ sub vcl_deliver {
+ set resp.http.c-uid = unix.uid();
+ set resp.http.c-gid = unix.gid();
+ set resp.http.c-user = unix.user();
+ set resp.http.c-group = unix.group();
+ }
+} -start
+
+# Check the log output for the response header values to see how this
+# worked on your platform.
+
+client c2 -connect "${v2_addr}" {
+ txreq
+ rxresp
+ expect resp.status ~ "^(200|503)$"
+} -run
diff --git a/bin/varnishtest/vmods.h b/bin/varnishtest/vmods.h
index e732468..fb8f68f 100644
--- a/bin/varnishtest/vmods.h
+++ b/bin/varnishtest/vmods.h
@@ -33,3 +33,4 @@ VTC_VMOD(directors)
VTC_VMOD(purge)
VTC_VMOD(vtc)
VTC_VMOD(blob)
+VTC_VMOD(unix)
diff --git a/configure.ac b/configure.ac
index cf14412..c0dc0f7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -216,6 +216,8 @@ AC_CHECK_FUNCS([setppriv])
AC_CHECK_FUNCS([fallocate])
AC_CHECK_FUNCS([closefrom])
AC_CHECK_FUNCS([sigaltstack])
+AC_CHECK_FUNCS([getpeereid])
+AC_CHECK_FUNCS([getpeerucred])
save_LIBS="${LIBS}"
LIBS="${PTHREAD_LIBS}"
@@ -758,6 +760,7 @@ AC_CONFIG_FILES([
lib/libvmod_purge/Makefile
lib/libvmod_vtc/Makefile
lib/libvmod_blob/Makefile
+ lib/libvmod_unix/Makefile
man/Makefile
varnishapi.pc
varnishapi-uninstalled.pc
diff --git a/doc/sphinx/Makefile.am b/doc/sphinx/Makefile.am
index 490ee50..810e903 100644
--- a/doc/sphinx/Makefile.am
+++ b/doc/sphinx/Makefile.am
@@ -218,6 +218,10 @@ reference/vmod_blob.generated.rst: reference $(top_builddir)/lib/libvmod_blob/vm
cp $(top_builddir)/lib/libvmod_blob/vmod_blob.rst $@ || true
BUILT_SOURCES += reference/vmod_blob.generated.rst
+reference/vmod_unix.generated.rst: reference $(top_builddir)/lib/libvmod_unix/vmod_unix.rst
+ cp $(top_builddir)/lib/libvmod_unix/vmod_unix.rst $@ || true
+BUILT_SOURCES += reference/vmod_unix.generated.rst
+
EXTRA_DIST += $(BUILT_SOURCES)
MAINTAINERCLEANFILES = $(EXTRA_DIST)
diff --git a/doc/sphinx/reference/index.rst b/doc/sphinx/reference/index.rst
index 39ccb81..9df60eb 100644
--- a/doc/sphinx/reference/index.rst
+++ b/doc/sphinx/reference/index.rst
@@ -25,6 +25,7 @@ The Varnish Reference Manual
vmod_vtc.generated.rst
vmod_purge.generated.rst
vmod_blob.generated.rst
+ vmod_unix.generated.rst
directors.rst
varnish-counters.rst
vsl.rst
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 4afd3d0..411bd80 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -10,4 +10,5 @@ SUBDIRS = \
libvmod_directors \
libvmod_purge \
libvmod_vtc \
- libvmod_blob
+ libvmod_blob \
+ libvmod_unix
diff --git a/lib/libvmod_unix/Makefile.am b/lib/libvmod_unix/Makefile.am
new file mode 100644
index 0000000..8dafeba
--- /dev/null
+++ b/lib/libvmod_unix/Makefile.am
@@ -0,0 +1,8 @@
+#
+
+libvmod_unix_la_SOURCES = \
+ vmod_unix.c \
+ cred_compat.h
+
+# Use vmodtool.py generated automake boilerplate
+include $(srcdir)/automake_boilerplate.am
diff --git a/lib/libvmod_unix/automake_boilerplate.am b/lib/libvmod_unix/automake_boilerplate.am
new file mode 100644
index 0000000..b71674a
--- /dev/null
+++ b/lib/libvmod_unix/automake_boilerplate.am
@@ -0,0 +1,39 @@
+
+# Boilerplate generated by vmodtool.py - changes will be overwritten
+
+AM_LDFLAGS = $(AM_LT_LDFLAGS)
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/bin/varnishd \
+ -I$(top_builddir)/include
+
+vmoddir = $(pkglibdir)/vmods
+vmodtool = $(top_srcdir)/lib/libvcc/vmodtool.py
+vmodtoolargs = --strict --boilerplate
+
+vmod_LTLIBRARIES = libvmod_unix.la
+
+libvmod_unix_la_CFLAGS = \
+ @SAN_CFLAGS@
+
+libvmod_unix_la_LDFLAGS = \
+ $(AM_LDFLAGS) \
+ $(VMOD_LDFLAGS) \
+ @SAN_LDFLAGS@
+
+nodist_libvmod_unix_la_SOURCES = vcc_if.c vcc_if.h
+
+$(libvmod_unix_la_OBJECTS): vcc_if.h
+
+vcc_if.h vmod_unix.rst vmod_unix.man.rst: vcc_if.c
+
+vcc_if.c: $(vmodtool) $(srcdir)/vmod.vcc
+ @PYTHON@ $(vmodtool) $(vmodtoolargs) $(srcdir)/vmod.vcc
+
+EXTRA_DIST = vmod.vcc automake_boilerplate.am
+
+CLEANFILES = $(builddir)/vcc_if.c $(builddir)/vcc_if.h \
+ $(builddir)/vmod_unix.rst \
+ $(builddir)/vmod_unix.man.rst
+
diff --git a/lib/libvmod_unix/cred_compat.h b/lib/libvmod_unix/cred_compat.h
new file mode 100644
index 0000000..5c2340b
--- /dev/null
+++ b/lib/libvmod_unix/cred_compat.h
@@ -0,0 +1,87 @@
+/*-
+ * Copyright 2018 UPLEX - Nils Goroll Systemoptimierung
+ * All rights reserved.
+ *
+ * Authors: Geoffrey Simmons <geoffrey.simmons at uplex.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+
+#if defined(HAVE_GETPEEREID)
+#include <unistd.h>
+#endif
+
+#if defined(HAVE_GETPEERUCRED)
+#include <ucred.h>
+#endif
+
+#define CREDS_FAIL -1
+#define NOT_SUPPORTED -2
+
+static int
+get_ids(int fd, uid_t *uid, gid_t *gid)
+{
+
+#if defined(SO_PEERCRED)
+
+ struct ucred ucred;
+ socklen_t l = sizeof(ucred);
+
+ errno = 0;
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, (void *) &ucred, &l) != 0)
+ return (CREDS_FAIL);
+ *uid = ucred.uid;
+ *gid = ucred.gid;
+ return (0);
+
+#elif defined(HAVE_GETPEEREID)
+
+ errno = 0;
+ if (getpeereid(fd, uid, gid) != 0)
+ return (CREDS_FAIL);
+ return (0);
+
+#elif defined(HAVE_GETPEERUCRED)
+
+ ucred_t *ucred;
+
+ errno = 0;
+ if (getpeerucred(fd, &ucred) != 0)
+ return (CREDS_FAIL);
+ *uid = ucred_geteuid(ucred);
+ *gid = ucred_getegid(ucred);
+ ucred_free(ucred);
+ return (0);
+
+#else
+ (void) fd;
+ (void) uid;
+ (void) gid;
+ return (NOT_SUPPORTED);
+#endif
+
+}
diff --git a/lib/libvmod_unix/vmod.vcc b/lib/libvmod_unix/vmod.vcc
new file mode 100644
index 0000000..1035df3
--- /dev/null
+++ b/lib/libvmod_unix/vmod.vcc
@@ -0,0 +1,110 @@
+#-
+# This document is licensed under the same conditions as Varnish itself.
+# See LICENSE for details.
+#
+# Authors: Geoffrey Simmons <geoffrey.simmons at uplex.de>
+#
+
+$Module unix 3 utilities for Unix domain sockets
+
+$ABI strict
+
+DESCRIPTION
+===========
+
+This VMOD provides information about the credentials of the peer
+process (user and group of the process owner) that is connected to
+Varnish via a Unix domain socket, if the platform supports it.
+
+Examples::
+
+ sub vcl_recv {
+ # Return "403 Forbidden" if the connected peer is
+ # not running as the user "trusteduser".
+ if (unix.user() != "trusteduser") {
+ return( synth(403) );
+ }
+
+ # Require the connected peer to run in the group
+ # "trustedgroup".
+ if (unix.group() != "trustedgroup") {
+ return( synth(403) );
+ }
+
+ # Require the connected peer to run under a specific numeric
+ # user id.
+ if (unix.uid() != 4711) {
+ return( synth(403) );
+ }
+
+ # Require the connected peer to run under a numeric group id.
+ if (unix.gid() != 815) {
+ return( synth(403) );
+ }
+ }
+
+Obtaining the peer credentials is possible on a platform that supports
+one of the following:
+
+* ``getpeereid(3)`` (such as FreeBSD and other BSD-derived systems)
+
+* ``getpeerucred(3)`` (SunOS and descendants)
+
+ * Reading peer credentials on such a system may require that the
+ Varnish child process runs with certain privileges (such as
+ ``PRIV_PROC_INFO``)
+
+* the socket option ``SO_PEERCRED`` for ``getsockopt(2)`` (Linux)
+
+On most platforms, the value returned is the effective user or group
+that was valid when the peer process initiated the connection.
+
+$Function STRING user()
+
+Return the user name of the peer process owner.
+
+$Function STRING group()
+
+Return the group name of the peer process owner.
+
+$Function INT uid()
+
+Return the numeric user id of the peer process owner.
+
+$Function INT gid()
+
+Return the numeric group id of the peer process owner.
+
+ERRORS
+======
+
+All functions in this VMOD are subject to the following constraints:
+
+* None of them may be called in ``vcl_init`` or ``vcl_fini``. If one
+ of them is called in ``vcl_init``, then the VCL program will fail to
+ load, with an error message from the VMOD.
+
+* If called on a platform that is not supported, then VCL failure is
+ invoked. An error message is written to the log (with the
+ ``VCL_Error`` tag), and for all VCL subroutines except for
+ ``vcl_synth``, control is directed immediately to ``vcl_synth``,
+ with the response status set to 503 and the reason string set to
+ "VCL failed".
+
+ If the failure occurs during ``vcl_synth``, then ``vcl_synth`` is
+ aborted, and the the response line "503 VCL failed" is sent.
+
+* If the current listener is not a Unix domain socket, or if the
+ attempt to read credentials fails, then a ``VCL_Error`` message is
+ written to the log. The STRING functions (``vmod_user`` and
+ ``vmod_group``) return NULL, while the INT functions (``vmod_user``
+ and ``vmod_gid``) return -1.
+
+SEE ALSO
+========
+
+* :ref:`varnishd(1)`
+* :ref:`vcl(7)`
+* ``getpeereid(3)``
+* ``getpeerucred(3)``
+* ``getsockopt(2)``
diff --git a/lib/libvmod_unix/vmod_unix.c b/lib/libvmod_unix/vmod_unix.c
new file mode 100644
index 0000000..ee1f3e0
--- /dev/null
+++ b/lib/libvmod_unix/vmod_unix.c
@@ -0,0 +1,142 @@
+/*-
+ * Copyright 2018 UPLEX - Nils Goroll Systemoptimierung
+ * All rights reserved.
+ *
+ * Authors: Geoffrey Simmons <geoffrey.simmons at uplex.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#include "cred_compat.h"
+
+#include <pwd.h>
+#include <grp.h>
+#include <string.h>
+
+#include "cache/cache.h"
+#include "vcl.h"
+#include "common/heritage.h"
+
+#include "vcc_if.h"
+
+#define FAIL(ctx, msg) \
+ VRT_fail((ctx), "vmod unix failure: " msg)
+
+#define ERR(ctx, msg) \
+ VSLb((ctx)->vsl, SLT_VCL_Error, "vmod unix error: " msg)
+
+#define VERR(ctx, fmt, ...) \
+ VSLb((ctx)->vsl, SLT_VCL_Error, "vmod unix error: " fmt, __VA_ARGS__)
+
+#define FAILNOINIT(ctx) \
+ FAIL((ctx), "may not be called in vcl_init or vcl_fini")
+
+#define ERRNOTUDS(ctx) \
+ ERR((ctx), "not listening on a Unix domain socket")
+
+#define FAIL_SUPPORT(ctx) \
+ FAIL((ctx), "not supported on this platform")
+
+#define ERRNOCREDS(ctx) \
+ VERR((ctx), "could not read peer credentials: %s", strerror(errno))
+
+#define ERRNOMEM(ctx) \
+ ERR((ctx), "out of space")
+
+static struct sess *
+get_sp(VRT_CTX)
+{
+ struct sess *sp;
+
+ if (VALID_OBJ(ctx->req, REQ_MAGIC))
+ sp = ctx->req->sp;
+ else {
+ CHECK_OBJ_NOTNULL(ctx->bo, BUSYOBJ_MAGIC);
+ sp = ctx->bo->sp;
+ }
+ CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+ CHECK_OBJ_NOTNULL(sp->listen_sock, LISTEN_SOCK_MAGIC);
+ return (sp);
+}
+
+#define NUM_FUNC(func) \
+VCL_INT \
+vmod_##func(VRT_CTX) \
+{ \
+ struct sess *sp; \
+ uid_t uid; \
+ gid_t gid; \
+ int ret; \
+ \
+ CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); \
+ if ((ctx->method & VCL_MET_TASK_H) != 0) { \
+ FAILNOINIT(ctx); \
+ return (-1); \
+ } \
+ \
+ sp = get_sp(ctx); \
+ if (! sp->listen_sock->uds) { \
+ ERRNOTUDS(ctx); \
+ return (-1); \
+ } \
+ \
+ ret = get_ids(sp->fd, &uid, &gid); \
+ if (ret == 0) \
+ return (func); \
+ \
+ if (ret == NOT_SUPPORTED) \
+ FAIL_SUPPORT(ctx); \
+ else if (ret == CREDS_FAIL) \
+ ERRNOCREDS(ctx); \
+ return (-1); \
+}
+
+NUM_FUNC(uid)
+NUM_FUNC(gid)
+
+#define NAME_FUNC(func, type, get, id, fld) \
+VCL_STRING \
+vmod_##func(VRT_CTX) \
+{ \
+ struct type *s; \
+ id##_t i; \
+ VCL_STRING name; \
+ \
+ CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); \
+ i = (id##_t) vmod_##id(ctx); \
+ if (i == -1) \
+ return (NULL); \
+ \
+ errno = 0; \
+ s = get(i); \
+ if (s == NULL) { \
+ ERRNOCREDS(ctx); \
+ return (NULL); \
+ } \
+ if ((name = WS_Copy(ctx->ws, s->fld, -1)) == NULL) { \
+ ERRNOMEM(ctx); \
+ return (NULL); \
+ } \
+ return (name); \
+}
+
+NAME_FUNC(user, passwd, getpwuid, uid, pw_name)
+NAME_FUNC(group, group, getgrgid, gid, gr_name)
diff --git a/man/Makefile.am b/man/Makefile.am
index 2187ff9..e7c00df 100644
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -19,7 +19,8 @@ dist_man_MANS = \
vmod_purge.3 \
vmod_std.3 \
vmod_vtc.3 \
- vmod_blob.3
+ vmod_blob.3 \
+ vmod_unix.3
CLEANFILES = $(dist_man_MANS)
@@ -101,4 +102,7 @@ vmod_vtc.3: $(top_builddir)/lib/libvmod_vtc/vmod_vtc.man.rst
vmod_blob.3: $(top_builddir)/lib/libvmod_blob/vmod_blob.man.rst
${RST2MAN} $(RST2ANY_FLAGS) $? $@
+vmod_unix.3: $(top_builddir)/lib/libvmod_unix/vmod_unix.man.rst
+ ${RST2MAN} $(RST2ANY_FLAGS) $? $@
+
.NOPATH: $(dist_man_MANS)
More information about the varnish-commit
mailing list