Commit 94443fed authored by Geoff Simmons's avatar Geoff Simmons

Add VMOD unix.

parent 63d19da4
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
......@@ -33,3 +33,4 @@ VTC_VMOD(directors)
VTC_VMOD(purge)
VTC_VMOD(vtc)
VTC_VMOD(blob)
VTC_VMOD(unix)
......@@ -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
......
......@@ -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)
......
......@@ -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
......
......@@ -10,4 +10,5 @@ SUBDIRS = \
libvmod_directors \
libvmod_purge \
libvmod_vtc \
libvmod_blob
libvmod_blob \
libvmod_unix
#
libvmod_unix_la_SOURCES = \
vmod_unix.c \
cred_compat.h
# Use vmodtool.py generated automake boilerplate
include $(srcdir)/automake_boilerplate.am
# 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
/*-
* Copyright 2018 UPLEX - Nils Goroll Systemoptimierung
* All rights reserved.
*
* Authors: Geoffrey Simmons <geoffrey.simmons@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
}
#-
# This document is licensed under the same conditions as Varnish itself.
# See LICENSE for details.
#
# Authors: Geoffrey Simmons <geoffrey.simmons@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)``
/*-
* Copyright 2018 UPLEX - Nils Goroll Systemoptimierung
* All rights reserved.
*
* Authors: Geoffrey Simmons <geoffrey.simmons@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)
......@@ -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)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment