Index: lib/libcasper/libcasper/libcasper.3 =================================================================== --- lib/libcasper/libcasper/libcasper.3 +++ lib/libcasper/libcasper/libcasper.3 @@ -28,7 +28,7 @@ .\" .\" $FreeBSD$ .\" -.Dd March 6, 2018 +.Dd June 20, 2018 .Dt LIBCASPER 3 .Os .Sh NAME @@ -263,6 +263,7 @@ .Xr cap_grp 3 , .Xr cap_pwd 3 , .Xr cap_ranodm 3 , +.Xr cap_signal 3 , .Xr cap_sysctl 3 , .Xr cap_syslog 3 , .Xr capsicum 4 , Index: lib/libcasper/services/Makefile =================================================================== --- lib/libcasper/services/Makefile +++ lib/libcasper/services/Makefile @@ -6,6 +6,7 @@ SUBDIR+= cap_grp SUBDIR+= cap_pwd SUBDIR+= cap_random +SUBDIR+= cap_signal SUBDIR+= cap_sysctl SUBDIR+= cap_syslog Index: lib/libcasper/services/cap_signal/Makefile =================================================================== --- /dev/null +++ lib/libcasper/services/cap_signal/Makefile @@ -0,0 +1,31 @@ +# $FreeBSD$ + +SHLIBDIR?= /lib/casper + +.include + +PACKAGE=libcasper + +SHLIB_MAJOR= 1 +INCSDIR?= ${INCLUDEDIR}/casper + +.if ${MK_CASPER} != "no" +SHLIB= cap_signal + +SRCS= cap_signal.c +.endif + +INCS= cap_signal.h + +LIBADD= nv + +CFLAGS+=-I${.CURDIR} + +MAN+= cap_signal.3 + +MLINKS+=cap_signal.3 libcap_signal.3 +MLINKS+=cap_signal.3 cap_kill.3 +MLINKS+=cap_signal.3 cap_signal_limit_pid.3 +MLINKS+=cap_signal.3 cap_signal_limit_sig.3 + +.include Index: lib/libcasper/services/cap_signal/cap_signal.h =================================================================== --- /dev/null +++ lib/libcasper/services/cap_signal/cap_signal.h @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 2018 Mariusz Zaborski + * All rights reserved. + * + * 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 AUTHORS 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 THE AUTHORS 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. + * + * $FreeBSD$ + */ + +#ifndef _CAP_SIGNAL_H_ +#define _CAP_SIGNAL_H_ + +#include + +#include + +#ifdef HAVE_CASPER +#define WITH_CASPER +#endif + +#ifdef WITH_CASPER +int cap_kill(cap_channel_t *chan, pid_t pid, int sig); + +int cap_signal_limit_pid(cap_channel_t *chan, const pid_t *pids, size_t npids); +int cap_signal_limit_signals(cap_channel_t *chan, const int *sigs, size_t nsig); +#else +#define cap_kill(chan, pid, sig) kill(pid, sig) + +#define cap_signal_limit_pid(chan, pids, npids) (0) +#define cap_signal_limit_signals(chan, sigs, nsig) (0) +#endif + +#endif /* !_CAP_SIGNAL_H_ */ Index: lib/libcasper/services/cap_signal/cap_signal.3 =================================================================== --- /dev/null +++ lib/libcasper/services/cap_signal/cap_signal.3 @@ -0,0 +1,125 @@ +.\" Copyright (c) 2018 Mariusz Zaborski +.\" All rights reserved. +.\" +.\" 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 AUTHORS 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 THE AUTHORS 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. +.\" +.\" $FreeBSD$ +.\" +.Dd June 20, 2018 +.Dt CAP_SIGNAL 3 +.Os +.Sh NAME +.Nm cap_kill , +.Nm cap_signal_limit_pid , +.Nm cap_signal_limit_signals +.Nd "library for signal operations in capability mode" +.Sh LIBRARY +.Lb libcap_signal +.Sh SYNOPSIS +.In libcasper.h +.In casper/cap_signal.h +.Ft int +.Fn cap_kill "cap_channel_t *chan" "pid_t pid" "int sig" +.Ft int +.Fn cap_signal_limit_pid "cap_channel_t *chan" "const pid_t *pids" "size_t npids" +.Ft int +.Fn cap_signal_limit_signals "cap_channel_t *chan" "const int *sigs" "size_t nsig" +.Sh DESCRIPTION +The function +.Fn cap_kill +is equivalent to +.Xr kill 2 +except that the connection to the +.Nm system.signal +service needs to be provided. +.Pp +The +.Fn cap_signal_limit_pid +function limits a list of process to which signals can be sent. +.Pp +The +.Fn cap_signal_limit_signals +function limits signals which can be sent. +.Sh EXAMPLES +The following example first create a process, opens a capability to Casper and +then uses this capability to create the +.Nm system.signal +casper service and uses it to send a signal SIGABRT to the process. +.Bd -literal +cap_channel_t *capcas, *capsignal; +pid_t pid; +int signals[] = { SIGABRT }; + +/* Spawn a child to which we will send a signal */ +pid = fork(); +if (pid == -1) { + return (1); +} else if (pid == 0) { + /* Wait for a moment. */ + for (;;) { + sleep(1); + } +} + +/* Open capability to Casper. */ +capcas = cap_init(); +if (capcas == NULL) + err(1, "Unable to contact Casper"); + +/* Enter capability mode sandbox. */ +if (cap_enter() < 0 && errno != ENOSYS) + err(1, "Unable to enter capability mode"); + +/* Use Casper capability to create capability to the system.signal service. */ +capsignal = cap_service_open(capcas, "system.signal"); +if (capsignal == NULL) + err(1, "Unable to open system.signal service"); + +/* Close Casper capability, we don't need it anymore. */ +cap_close(capcas); + +/* Limit service to one single process. */ +if (cap_signal_limit_pid(capsignal, &pid, 1) < 0) + err(1, "Unable to limit access to system.signal service"); + +/* Limit service to one field as we only need name of the user. */ +if (cap_signal_limit_signals(capsignal, signals, nitems(signals)) < 0) + err(1, "Unable to limit access to system.signal service"); + +if (cap_kill(capsignal, pid, SIGABRT) < 0) + err(1, "Unable to send signal"); + +printf("The process %d recived SIGABORT.\n", pid); +.Ed +.Sh SEE ALSO +.Xr cap_enter 2 , +.Xr errno 2 , +.Xr fork 2, +.Xr kill 2 , +.Xr err 3 , +.Xr capsicum 4 , +.Xr nv 9 +.Sh AUTHORS +The +.Nm cap_signal +service was implemented by +.An Mariusz Zaborski Aq Mt oshogbo@FreeBSD.org . Index: lib/libcasper/services/cap_signal/cap_signal.c =================================================================== --- /dev/null +++ lib/libcasper/services/cap_signal/cap_signal.c @@ -0,0 +1,215 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Mariusz Zaborski + * All rights reserved. + * + * 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 AUTHORS 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 THE AUTHORS 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "cap_signal.h" + +int +cap_kill(cap_channel_t *chan, pid_t pid, int sig) +{ + nvlist_t *nvl; + int err; + + nvl = nvlist_create(0); + nvlist_add_string(nvl, "cmd", "kill"); + nvlist_add_number(nvl, "pid", pid); + nvlist_add_number(nvl, "sig", sig); + + nvl = cap_xfer_nvlist(chan, nvl); + if (nvl == NULL) + return (-1); + + err = nvlist_get_number(nvl, "error"); + nvlist_destroy(nvl); + if (err != 0) { + errno = err; + return (-1); + } + + return (0); +} + +int +cap_signal_limit_pids(cap_channel_t *chan, const pid_t *pids, size_t npids) +{ + nvlist_t *limits; + size_t i; + + if (cap_limit_get(chan, &limits) < 0) + return (-1); + + if (limits == NULL) + limits = nvlist_create(0); + else if (nvlist_exists_nvlist(limits, "pids")) + nvlist_free_nvlist(limits, "pids"); + + for (i = 0; i < npids; i++) + nvlist_append_number_array(limits, "pids", pids[i]); + + return (cap_limit_set(chan, limits)); +} + +int +cap_signal_limit_signals(cap_channel_t *chan, const int *sigs, size_t nsig) +{ + nvlist_t *limits; + size_t i; + + if (cap_limit_get(chan, &limits) < 0) + return (-1); + + if (limits == NULL) + limits = nvlist_create(0); + else if (nvlist_exists_nvlist(limits, "sigs")) + nvlist_free_nvlist(limits, "sigs"); + + for (i = 0; i < nsig; i++) + nvlist_append_number_array(limits, "sigs", sigs[i]); + + return (cap_limit_set(chan, limits)); +} + +/* + * Service functions. + */ +static bool +signal_allowed(const nvlist_t *limits, const char *name, int64_t number) +{ + const int64_t *numbers; + size_t nitems, i; + + if (limits == NULL) + return (true); + + if (!nvlist_exists_number_array(limits, name)) + return (true); + + numbers = nvlist_get_number_array(limits, name, &nitems); + for (i = 0; i < nitems; i++) { + if (number == numbers[i]) + return (true); + } + + return (false); +} + +static bool +signal_allowed_pid(const nvlist_t *limits, pid_t pid) +{ + + return (signal_allowed(limits, "pids", pid)); +} + +static bool +signal_allowed_sig(const nvlist_t *limits, int sig) +{ + + return (signal_allowed(limits, "sigs", sig)); +} + +static int +signal_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits) +{ + const char *name; + const int64_t *values; + size_t nitems, i; + int nvtype; + void *cookie; + + /* XXX: If needed we can optimize the limits using qsort. */ + if (oldlimits == NULL) { + return (0); + } + + cookie = NULL; + while ((name = nvlist_next(newlimits, &nvtype, &cookie)) != NULL) { + if (nvtype != NV_TYPE_NUMBER_ARRAY) + return (EINVAL); + + if (strcmp(name, "pids") != 0 && strcmp(name, "sigs") != 0) + return (EINVAL); + + values = cnvlist_get_number_array(cookie, &nitems); + for (i = 0; i < nitems; i++) { + if (!signal_allowed(oldlimits, name, values[i])) { + return (ENOTCAPABLE); + } + } + } + + return (0); +} + +static int +signal_kill(const nvlist_t *limits, const nvlist_t *nvlin) +{ + pid_t pid; + int sig; + + pid = nvlist_get_number(nvlin, "pid"); + sig = nvlist_get_number(nvlin, "sig"); + + if (!signal_allowed_pid(limits, pid)) + return (ENOTCAPABLE); + if (!signal_allowed_sig(limits, sig)) + return (ENOTCAPABLE); + + if (kill(pid, sig) != 0) + return (errno); + + return (0); +} + +static int +signal_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin, + nvlist_t *nvlout __unused) +{ + + if (strcmp(cmd, "kill") != 0) + return (ENOTCAPABLE); + + return (signal_kill(limits, nvlin)); +} + +CREATE_SERVICE("system.signal", signal_limit, signal_command, 0) Index: share/mk/src.libnames.mk =================================================================== --- share/mk/src.libnames.mk +++ share/mk/src.libnames.mk @@ -77,6 +77,7 @@ cap_grp \ cap_pwd \ cap_random \ + cap_signal \ cap_sysctl \ cap_syslog \ com_err \ @@ -239,6 +240,7 @@ _DP_cap_grp= nv _DP_cap_pwd= nv _DP_cap_random= nv +_DP_cap_signal= nv _DP_cap_sysctl= nv _DP_cap_syslog= nv .if ${MK_OFED} != "no" @@ -540,6 +542,7 @@ LIBCAP_GRPDIR= ${OBJTOP}/lib/libcasper/services/cap_grp LIBCAP_PWDDIR= ${OBJTOP}/lib/libcasper/services/cap_pwd LIBCAP_RANDOMDIR= ${OBJTOP}/lib/libcasper/services/cap_random +LIBCAP_SINGALDIR= ${OBJTOP}/lib/libcasper/services/cap_signal LIBCAP_SYSCTLDIR= ${OBJTOP}/lib/libcasper/services/cap_sysctl LIBCAP_SYSLOGDIR= ${OBJTOP}/lib/libcasper/services/cap_syslog LIBBSDXMLDIR= ${OBJTOP}/lib/libexpat