Please welcome casperd daemon. It (and its services) will be responsible for

giving access to functionality that is not available in capability mode
sandbox. The functionality can be precisely restricted.

Start with the following services:
- system.dns - provides API compatible to:
	- gethostbyname(3),
	- gethostbyname2(3),
	- gethostbyaddr(3),
	- getaddrinfo(3),
	- getnameinfo(3),
- system.grp - provides getgrent(3)-compatible API,
- system.pwd - provides getpwent(3)-compatible API,
- system.random - allows to obtain entropy from /dev/random,
- system.sysctl - provides sysctlbyname(3-compatible API.

Sponsored by:	The FreeBSD Foundation
This commit is contained in:
pjd 2013-12-02 08:21:28 +00:00
parent 9817dff3a7
commit 1073fb03ac
49 changed files with 5318 additions and 1 deletions

View File

@ -1499,7 +1499,8 @@ _prebuild_libs= ${_kerberos5_lib_libasn1} \
lib/libbz2 ${_libcom_err} lib/libcrypt \
lib/libelf lib/libexpat \
${_lib_libgssapi} ${_lib_libipx} \
lib/libkiconv lib/libkvm lib/liblzma lib/libmd \
lib/libkiconv lib/libkvm lib/liblzma lib/libmd lib/libnv \
${_lib_libcapsicum} \
lib/ncurses/ncurses lib/ncurses/ncursesw \
lib/libopie lib/libpam ${_lib_libthr} \
lib/libradius lib/libsbuf lib/libtacplus \
@ -1521,6 +1522,11 @@ _lib_libthr= lib/libthr
_ofed_lib= contrib/ofed/usr.lib/
.endif
.if ${MK_CASPER} != "no"
_lib_libcapsicum=lib/libcapsicum
.endif
lib/libcapsicum__L: lib/libnv__L
lib/libpjdlog__L: lib/libutil__L
_generic_libs= ${_cddl_lib} gnu/lib ${_kerberos5_lib} lib ${_secure_lib} usr.bin/lex/lib ${_ofed_lib}

View File

@ -32,6 +32,7 @@ LINE("libc", "Standard C\\~Library (libc, \\-lc)")
LINE("libc_r", "Reentrant C\\~Library (libc_r, \\-lc_r)")
LINE("libcalendar", "Calendar Arithmetic Library (libcalendar, \\-lcalendar)")
LINE("libcam", "Common Access Method User Library (libcam, \\-lcam)")
LINE("libcapsicum", "Capsicum Library (libcapsicum, \\-lcapsicum)")
LINE("libcdk", "Curses Development Kit Library (libcdk, \\-lcdk)")
LINE("libcipher", "FreeSec Crypt Library (libcipher, \\-lcipher)")
LINE("libcompat", "Compatibility Library (libcompat, \\-lcompat)")

View File

@ -220,6 +220,9 @@ distribution:
.endif
.if ${MK_BLUETOOTH} != "no"
${_+_}cd ${.CURDIR}/bluetooth; ${MAKE} install
.endif
.if ${MK_CASPER} != "no"
${_+_}cd ${.CURDIR}/casper; ${MAKE} install
.endif
${_+_}cd ${.CURDIR}/defaults; ${MAKE} install
${_+_}cd ${.CURDIR}/devd; ${MAKE} install

12
etc/casper/Makefile Normal file
View File

@ -0,0 +1,12 @@
# $FreeBSD$
FILES= system.dns
FILES+= system.grp
FILES+= system.pwd
FILES+= system.random
FILES+= system.sysctl
NO_OBJ=
FILESDIR= /etc/casper
.include <bsd.prog.mk>

1
etc/casper/system.dns Normal file
View File

@ -0,0 +1 @@
/libexec/casper/dns

1
etc/casper/system.grp Normal file
View File

@ -0,0 +1 @@
/libexec/casper/grp

1
etc/casper/system.pwd Normal file
View File

@ -0,0 +1 @@
/libexec/casper/pwd

1
etc/casper/system.random Normal file
View File

@ -0,0 +1 @@
/libexec/casper/random

1
etc/casper/system.sysctl Normal file
View File

@ -0,0 +1 @@
/libexec/casper/sysctl

View File

@ -26,6 +26,8 @@
..
bluetooth
..
casper
..
defaults
..
devd
@ -74,6 +76,8 @@
..
..
libexec
casper
..
resolvconf
..
..

View File

@ -37,6 +37,8 @@ SUBDIR_ORDERED= ${_csu} \
libauditd \
libutil \
libpjdlog \
libnv \
${_libcapsicum} \
libcompiler_rt \
libcrypt \
libelf \
@ -70,6 +72,7 @@ SUBDIR= ${SUBDIR_ORDERED} \
libbz2 \
libcalendar \
libcam \
${_libcasper} \
libcompat \
libdevinfo \
libdevstat \
@ -157,6 +160,11 @@ _libsdp= libsdp
_libbsnmp= libbsnmp
.endif
.if ${MK_CASPER} != "no"
_libcapsicum= libcapsicum
_libcasper= libcasper
.endif
.if ${MK_CLANG} != "no" && !defined(COMPAT_32BIT)
_clang= clang
.endif

46
lib/libcapsicum/Makefile Normal file
View File

@ -0,0 +1,46 @@
# $FreeBSD$
LIB= capsicum
SHLIB_MAJOR= 0
SRCS= libcapsicum.c
SRCS+= libcapsicum_dns.c
SRCS+= libcapsicum_grp.c
SRCS+= libcapsicum_pwd.c
SRCS+= libcapsicum_random.c
SRCS+= libcapsicum_service.c
SRCS+= libcapsicum_sysctl.c
INCS= libcapsicum.h
INCS+= libcapsicum_dns.h
INCS+= libcapsicum_grp.h
INCS+= libcapsicum_pwd.h
INCS+= libcapsicum_random.h
INCS+= libcapsicum_service.h
INCS+= libcapsicum_sysctl.h
DPADD= ${LIBNV}
LDADD= -lnv
CFLAGS+=-I${.CURDIR}
CFLAGS+=-I${.CURDIR}/../libnv
WARNS?= 6
MAN+= libcapsicum.3
MLINKS+=libcapsicum.3 cap_init.3
MLINKS+=libcapsicum.3 cap_wrap.3
MLINKS+=libcapsicum.3 cap_unwrap.3
MLINKS+=libcapsicum.3 cap_sock.3
MLINKS+=libcapsicum.3 cap_clone.3
MLINKS+=libcapsicum.3 cap_close.3
MLINKS+=libcapsicum.3 cap_limit_get.3
MLINKS+=libcapsicum.3 cap_limit_set.3
MLINKS+=libcapsicum.3 cap_send_nvlist.3
MLINKS+=libcapsicum.3 cap_recv_nvlist.3
MLINKS+=libcapsicum.3 cap_xfer_nvlist.3
MLINKS+=libcapsicum.3 cap_service_open.3
.include <bsd.lib.mk>

View File

@ -0,0 +1,288 @@
.\" Copyright (c) 2013 The FreeBSD Foundation
.\" All rights reserved.
.\"
.\" This documentation was written by Pawel Jakub Dawidek under sponsorship
.\" from the FreeBSD Foundation.
.\"
.\" 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 October 26, 2013
.Dt LIBCAPSICUM 3
.Os
.Sh NAME
.Nm cap_init ,
.Nm cap_wrap ,
.Nm cap_unwrap ,
.Nm cap_sock ,
.Nm cap_clone ,
.Nm cap_close ,
.Nm cap_limit_get ,
.Nm cap_limit_set ,
.Nm cap_send_nvlist ,
.Nm cap_recv_nvlist ,
.Nm cap_xfer_nvlist ,
.Nm cap_service_open
.Nd "library for handling application capabilities"
.Sh LIBRARY
.Lb libcapsicum
.Sh SYNOPSIS
.In libcapsicum.h
.In nv.h
.Ft "cap_channel_t *"
.Fn cap_init "void"
.Ft "cap_channel_t *"
.Fn cap_wrap "int sock"
.Ft "int"
.Fn cap_unwrap "cap_channel_t *chan"
.Ft "int"
.Fn cap_sock "const cap_channel_t *chan"
.Ft "cap_channel_t *"
.Fn cap_clone "const cap_channel_t *chan"
.Ft "void"
.Fn cap_close "cap_channel_t *chan"
.Ft "int"
.Fn cap_limit_get "const cap_channel_t *chan" "nvlist_t **limitsp"
.Ft "int"
.Fn cap_limit_set "const cap_channel_t *chan" "nvlist_t *limits"
.Ft "int"
.Fn cap_send_nvlist "const cap_channel_t *chan" "const nvlist_t *nvl"
.Ft "nvlist_t *"
.Fn cap_recv_nvlist "const cap_channel_t *chan"
.Ft "nvlist_t *"
.Fn cap_xfer_nvlist "const cap_channel_t *chan" "nvlist_t *nvl"
.In libcapsicum_service.h
.Ft "cap_channel_t *"
.Fn cap_service_open "const cap_channel_t *chan" "const char *name"
.Sh DESCRIPTION
The
.Nm libcapsicum
library allows to manage application capabilities through the
.Xr casperd 8
daemon.
.Pp
The application capability (represented by the
.Vt cap_channel_t
type) is a communication channel between the caller and the
.Xr casperd 8
daemon or an instance of one of its services.
A capability to the
.Xr casperd 8
daemon obtained with the
.Fn cap_init
function allows to create capabilities to casper's services via the
.Fn cap_service_open
function.
.Pp
The
.Fn cap_init
function opens capability to the
.Xr casperd 8
daemon.
.Pp
The
.Fn cap_wrap
function creates
.Vt cap_channel_t
based on the given socket.
The function is used when capability is inherited through
.Xr execve 2
or send over
.Xr unix 4
domain socket as a regular file descriptor and has to be represented as
.Vt cap_channel_t
again.
.Pp
The
.Fn cap_unwrap
function is the opposite of the
.Fn cap_wrap
function.
It frees the
.Vt cap_channel_t
structure and returns
.Xr unix 4
domain socket associated with it.
.Pp
The
.Fn cap_clone
function clones the given capability.
.Pp
The
.Fn cap_close
function closes the given capability.
.Pp
The
.Fn cap_sock
function returns
.Xr unix 4
domain socket descriptor associated with the given capability for use with
system calls like
.Xr kevent 2 ,
.Xr poll 2
and
.Xr select 2 .
.Pp
The
.Fn cap_limit_get
function stores current limits of the given capability in the
.Fa limitsp
argument.
If the function return
.Va 0
and
.Dv NULL
is stored in
.Fa limitsp
it means there are no limits set.
.Pp
The
.Fn cap_limit_set
function sets limits for the given capability.
The limits are provided as nvlist.
The exact format depends on the service the capability represents.
.Pp
The
.Fn cap_send_nvlist
function sends the given nvlist over the given capability.
This is low level interface to communicate with casper services.
Most services should provide higher level API.
.Pp
The
.Fn cap_recv_nvlist
function receives the given nvlist over the given capability.
.Pp
The
.Fn cap_xfer_nvlist
function sends the given nvlist, destroys it and receives new nvlist in
response over the given capability.
It does not matter if the function succeeds or fails, the nvlist given
for sending will always be destroyed once the function returns.
.Pp
The
.Fn cap_service_open
function opens casper service of the given name through casper capability
obtained via the
.Fn cap_init
function.
The function returns capability that provides access to opened service.
.Sh RETURN VALUES
The
.Fn cap_clone ,
.Fn cap_init ,
.Fn cap_recv_nvlist ,
.Fn cap_service_open ,
.Fn cap_wrap
and
.Fn cap_xfer_nvlist
functions return
.Dv NULL
and set the
.Va errno
variable on failure.
.Pp
The
.Fn cap_limit_get ,
.Fn cap_limit_set
and
.Fn cap_send_nvlist
functions return
.Dv -1
and set the
.Va errno
variable on failure.
.Pp
The
.Fn cap_close ,
.Fn cap_sock
and
.Fn cap_unwrap
functions always succeed.
.Sh EXAMPLES
The following example first opens capability to the
.Xr casperd 8
daemon, then using this capability creates new capability to the
.Nm system.dns
casper service and uses the latter capability to resolve IP address.
.Bd -literal
cap_channel_t *capcas, *capdns;
nvlist_t *limits;
const char *ipstr = "127.0.0.1";
struct in_addr ip;
struct hostent *hp;
/* Open capability to the Casper daemon. */
capcas = cap_init();
if (capcas == NULL)
err(1, "Unable to contact Casper daemon");
/* 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.dns service. */
capdns = cap_service_open(capcas, "system.dns");
if (capdns == NULL)
err(1, "Unable to open system.dns service");
/* Close Casper capability, we don't need it anymore. */
cap_close(capcas);
/* Limit system.dns to reverse DNS lookups and IPv4 addresses. */
limits = nvlist_create(0);
nvlist_add_string(limits, "type", "ADDR");
nvlist_add_number(limits, "family", (uint64_t)AF_INET);
if (cap_limit_set(capdns, limits) < 0)
err("Unable to limit access to the system.dns service");
/* Convert IP address in C-string to in_addr. */
if (!inet_aton(ipstr, &ip))
errx(1, "Unable to parse IP address %s.", ipstr);
/* Find hostname for the given IP address. */
hp = cap_gethostbyaddr(capdns, (const void *)&ip, sizeof(ip), AF_INET);
if (hp == NULL)
errx(1, "No name associated with %s.", ipstr);
printf("Name associated with %s is %s.\\n", ipstr, hp->h_name);
.Ed
.Sh SEE ALSO
.Xr cap_enter 2 ,
.Xr execve 2 ,
.Xr kevent 2 ,
.Xr poll 2 ,
.Xr select 2 ,
.Xr cap_gethostbyaddr 3 ,
.Xr err 3 ,
.Xr gethostbyaddr 3 ,
.Xr inet_aton 3 ,
.Xr nv 3 ,
.Xr capsicum 4 ,
.Xr unix 4 ,
.Xr casperd 8
.Sh AUTHORS
The
.Nm libcapsicum
library was implemented by
.An Pawel Jakub Dawidek Aq pawel@dawidek.net
under sponsorship from the FreeBSD Foundation.

View File

@ -0,0 +1,266 @@
/*-
* Copyright (c) 2012-2013 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "libcapsicum.h"
#include "libcapsicum_impl.h"
#include "nv.h"
/*
* Structure describing communication channel between two separated processes.
*/
#define CAP_CHANNEL_MAGIC 0xcac8a31
struct cap_channel {
/*
* Magic value helps to ensure that a pointer to the right structure is
* passed to our functions.
*/
int cch_magic;
/* Socket descriptor for IPC. */
int cch_sock;
};
bool
fd_is_valid(int fd)
{
return (fcntl(fd, F_GETFL) != -1 || errno != EBADF);
}
cap_channel_t *
cap_init(void)
{
cap_channel_t *chan;
struct sockaddr_un sun;
int serrno, sock;
bzero(&sun, sizeof(sun));
sun.sun_family = AF_UNIX;
strlcpy(sun.sun_path, CASPER_SOCKPATH, sizeof(sun.sun_path));
sun.sun_len = SUN_LEN(&sun);
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock == -1)
return (NULL);
if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
serrno = errno;
close(sock);
errno = serrno;
return (NULL);
}
chan = cap_wrap(sock);
if (chan == NULL) {
serrno = errno;
close(sock);
errno = serrno;
return (NULL);
}
return (chan);
}
cap_channel_t *
cap_wrap(int sock)
{
cap_channel_t *chan;
if (!fd_is_valid(sock))
return (NULL);
chan = malloc(sizeof(*chan));
if (chan != NULL) {
chan->cch_sock = sock;
chan->cch_magic = CAP_CHANNEL_MAGIC;
}
return (chan);
}
int
cap_unwrap(cap_channel_t *chan)
{
int sock;
assert(chan != NULL);
assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
sock = chan->cch_sock;
chan->cch_magic = 0;
free(chan);
return (sock);
}
cap_channel_t *
cap_clone(const cap_channel_t *chan)
{
cap_channel_t *newchan;
nvlist_t *nvl;
int newsock;
assert(chan != NULL);
assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
nvl = nvlist_create(0);
nvlist_add_string(nvl, "cmd", "clone");
nvl = cap_xfer_nvlist(chan, nvl);
if (nvl == NULL)
return (NULL);
if (nvlist_get_number(nvl, "error") != 0) {
errno = (int)nvlist_get_number(nvl, "error");
nvlist_destroy(nvl);
return (NULL);
}
newsock = nvlist_take_descriptor(nvl, "sock");
nvlist_destroy(nvl);
newchan = cap_wrap(newsock);
if (newchan == NULL) {
int serrno;
serrno = errno;
close(newsock);
errno = serrno;
}
return (newchan);
}
void
cap_close(cap_channel_t *chan)
{
assert(chan != NULL);
assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
chan->cch_magic = 0;
close(chan->cch_sock);
free(chan);
}
int
cap_sock(const cap_channel_t *chan)
{
assert(chan != NULL);
assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
return (chan->cch_sock);
}
int
cap_limit_set(const cap_channel_t *chan, nvlist_t *limits)
{
nvlist_t *nvlmsg;
int error;
nvlmsg = nvlist_create(0);
nvlist_add_string(nvlmsg, "cmd", "limit_set");
nvlist_add_nvlist(nvlmsg, "limits", limits);
nvlmsg = cap_xfer_nvlist(chan, nvlmsg);
if (nvlmsg == NULL) {
nvlist_destroy(limits);
return (-1);
}
error = (int)nvlist_get_number(nvlmsg, "error");
nvlist_destroy(nvlmsg);
nvlist_destroy(limits);
if (error != 0) {
errno = error;
return (-1);
}
return (0);
}
int
cap_limit_get(const cap_channel_t *chan, nvlist_t **limitsp)
{
nvlist_t *nvlmsg;
int error;
nvlmsg = nvlist_create(0);
nvlist_add_string(nvlmsg, "cmd", "limit_get");
nvlmsg = cap_xfer_nvlist(chan, nvlmsg);
if (nvlmsg == NULL)
return (-1);
error = (int)nvlist_get_number(nvlmsg, "error");
if (error != 0) {
nvlist_destroy(nvlmsg);
errno = error;
return (-1);
}
if (nvlist_exists_null(nvlmsg, "limits"))
*limitsp = NULL;
else
*limitsp = nvlist_take_nvlist(nvlmsg, "limits");
nvlist_destroy(nvlmsg);
return (0);
}
int
cap_send_nvlist(const cap_channel_t *chan, const nvlist_t *nvl)
{
assert(chan != NULL);
assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
return (nvlist_send(chan->cch_sock, nvl));
}
nvlist_t *
cap_recv_nvlist(const cap_channel_t *chan)
{
assert(chan != NULL);
assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
return (nvlist_recv(chan->cch_sock));
}
nvlist_t *
cap_xfer_nvlist(const cap_channel_t *chan, nvlist_t *nvl)
{
assert(chan != NULL);
assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
return (nvlist_xfer(chan->cch_sock, nvl));
}

View File

@ -0,0 +1,115 @@
/*-
* Copyright (c) 2012-2013 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* 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 _LIBCAPSICUM_H_
#define _LIBCAPSICUM_H_
#ifndef _NVLIST_T_DECLARED
#define _NVLIST_T_DECLARED
struct nvlist;
typedef struct nvlist nvlist_t;
#endif
#ifndef _CAP_CHANNEL_T_DECLARED
#define _CAP_CHANNEL_T_DECLARED
struct cap_channel;
typedef struct cap_channel cap_channel_t;
#endif
/*
* The function opens unrestricted communication channel to Casper.
*/
cap_channel_t *cap_init(void);
/*
* The function creates cap_channel_t based on the given socket.
*/
cap_channel_t *cap_wrap(int sock);
/*
* The function returns communication socket and frees cap_channel_t.
*/
int cap_unwrap(cap_channel_t *chan);
/*
* The function clones the given capability.
*/
cap_channel_t *cap_clone(const cap_channel_t *chan);
/*
* The function closes the given capability.
*/
void cap_close(cap_channel_t *chan);
/*
* The function returns socket descriptor associated with the given
* cap_channel_t for use with select(2)/kqueue(2)/etc.
*/
int cap_sock(const cap_channel_t *chan);
/*
* The function limits the given capability.
* It always destroys 'limits' on return.
*/
int cap_limit_set(const cap_channel_t *chan, nvlist_t *limits);
/*
* The function returns current limits of the given capability.
*/
int cap_limit_get(const cap_channel_t *chan, nvlist_t **limitsp);
#ifdef TODO
/*
* The function registers a service within provided Casper's capability.
* It will run with the same privileges the process has at the time of
* calling this function.
*/
int cap_service_register(cap_channel_t *chan, const char *name,
cap_func_t *func);
#endif
/*
* Function sends nvlist over the given capability.
*/
int cap_send_nvlist(const cap_channel_t *chan, const nvlist_t *nvl);
/*
* Function receives nvlist over the given capability.
*/
nvlist_t *cap_recv_nvlist(const cap_channel_t *chan);
/*
* Function sends the given nvlist, destroys it and receives new nvlist in
* response over the given capability.
*/
nvlist_t *cap_xfer_nvlist(const cap_channel_t *chan, nvlist_t *nvl);
#endif /* !_LIBCAPSICUM_H_ */

View File

@ -0,0 +1,346 @@
/*-
* Copyright (c) 2012-2013 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <nv.h>
#include "libcapsicum.h"
#include "libcapsicum_dns.h"
static struct hostent hent;
static void
hostent_free(struct hostent *hp)
{
unsigned int ii;
free(hp->h_name);
hp->h_name = NULL;
if (hp->h_aliases != NULL) {
for (ii = 0; hp->h_aliases[ii] != NULL; ii++)
free(hp->h_aliases[ii]);
free(hp->h_aliases);
hp->h_aliases = NULL;
}
if (hp->h_addr_list != NULL) {
for (ii = 0; hp->h_addr_list[ii] != NULL; ii++)
free(hp->h_addr_list[ii]);
free(hp->h_addr_list);
hp->h_addr_list = NULL;
}
}
static struct hostent *
hostent_unpack(const nvlist_t *nvl, struct hostent *hp)
{
unsigned int ii, nitems;
hostent_free(hp);
hp->h_name = strdup(nvlist_get_string(nvl, "name"));
if (hp->h_name == NULL)
goto fail;
hp->h_addrtype = (int)nvlist_get_number(nvl, "addrtype");
hp->h_length = (int)nvlist_get_number(nvl, "length");
nitems = (unsigned int)nvlist_get_number(nvl, "naliases");
hp->h_aliases = calloc(sizeof(hp->h_aliases[0]), nitems + 1);
if (hp->h_aliases == NULL)
goto fail;
for (ii = 0; ii < nitems; ii++) {
hp->h_aliases[ii] =
strdup(nvlist_getf_string(nvl, "alias%u", ii));
if (hp->h_aliases[ii] == NULL)
goto fail;
}
hp->h_aliases[ii] = NULL;
nitems = (unsigned int)nvlist_get_number(nvl, "naddrs");
hp->h_addr_list = calloc(sizeof(hp->h_addr_list[0]), nitems + 1);
if (hp->h_addr_list == NULL)
goto fail;
for (ii = 0; ii < nitems; ii++) {
hp->h_addr_list[ii] = malloc(hp->h_length);
if (hp->h_addr_list[ii] == NULL)
goto fail;
bcopy(nvlist_getf_binary(nvl, NULL, "addr%u", ii),
hp->h_addr_list[ii], hp->h_length);
}
hp->h_addr_list[ii] = NULL;
return (hp);
fail:
hostent_free(hp);
h_errno = NO_RECOVERY;
return (NULL);
}
struct hostent *
cap_gethostbyname(cap_channel_t *chan, const char *name)
{
return (cap_gethostbyname2(chan, name, AF_INET));
}
struct hostent *
cap_gethostbyname2(cap_channel_t *chan, const char *name, int type)
{
struct hostent *hp;
nvlist_t *nvl;
nvl = nvlist_create(0);
nvlist_add_string(nvl, "cmd", "gethostbyname");
nvlist_add_number(nvl, "family", (uint64_t)type);
nvlist_add_string(nvl, "name", name);
nvl = cap_xfer_nvlist(chan, nvl);
if (nvl == NULL) {
h_errno = NO_RECOVERY;
return (NULL);
}
if (nvlist_get_number(nvl, "error") != 0) {
h_errno = (int)nvlist_get_number(nvl, "error");
nvlist_destroy(nvl);
return (NULL);
}
hp = hostent_unpack(nvl, &hent);
nvlist_destroy(nvl);
return (hp);
}
struct hostent *
cap_gethostbyaddr(cap_channel_t *chan, const void *addr, socklen_t len,
int type)
{
struct hostent *hp;
nvlist_t *nvl;
nvl = nvlist_create(0);
nvlist_add_string(nvl, "cmd", "gethostbyaddr");
nvlist_add_binary(nvl, "addr", addr, (size_t)len);
nvlist_add_number(nvl, "family", (uint64_t)type);
nvl = cap_xfer_nvlist(chan, nvl);
if (nvl == NULL) {
h_errno = NO_RECOVERY;
return (NULL);
}
if (nvlist_get_number(nvl, "error") != 0) {
h_errno = (int)nvlist_get_number(nvl, "error");
nvlist_destroy(nvl);
return (NULL);
}
hp = hostent_unpack(nvl, &hent);
nvlist_destroy(nvl);
return (hp);
}
static struct addrinfo *
addrinfo_unpack(const nvlist_t *nvl)
{
struct addrinfo *ai;
const void *addr;
size_t addrlen;
const char *canonname;
addr = nvlist_get_binary(nvl, "ai_addr", &addrlen);
ai = malloc(sizeof(*ai) + addrlen);
if (ai == NULL)
return (NULL);
ai->ai_flags = (int)nvlist_get_number(nvl, "ai_flags");
ai->ai_family = (int)nvlist_get_number(nvl, "ai_family");
ai->ai_socktype = (int)nvlist_get_number(nvl, "ai_socktype");
ai->ai_protocol = (int)nvlist_get_number(nvl, "ai_protocol");
ai->ai_addrlen = (socklen_t)addrlen;
canonname = nvlist_get_string(nvl, "ai_canonname");
if (canonname != NULL) {
ai->ai_canonname = strdup(canonname);
if (ai->ai_canonname == NULL) {
free(ai);
return (NULL);
}
} else {
ai->ai_canonname = NULL;
}
ai->ai_addr = (void *)(ai + 1);
bcopy(addr, ai->ai_addr, addrlen);
ai->ai_next = NULL;
return (ai);
}
int
cap_getaddrinfo(cap_channel_t *chan, const char *hostname, const char *servname,
const struct addrinfo *hints, struct addrinfo **res)
{
struct addrinfo *firstai, *prevai, *curai;
unsigned int ii;
const nvlist_t *nvlai;
nvlist_t *nvl;
int error;
nvl = nvlist_create(0);
nvlist_add_string(nvl, "cmd", "getaddrinfo");
nvlist_add_string(nvl, "hostname", hostname);
nvlist_add_string(nvl, "servname", servname);
if (hints != NULL) {
nvlist_add_number(nvl, "hints.ai_flags",
(uint64_t)hints->ai_flags);
nvlist_add_number(nvl, "hints.ai_family",
(uint64_t)hints->ai_family);
nvlist_add_number(nvl, "hints.ai_socktype",
(uint64_t)hints->ai_socktype);
nvlist_add_number(nvl, "hints.ai_protocol",
(uint64_t)hints->ai_protocol);
}
nvl = cap_xfer_nvlist(chan, nvl);
if (nvl == NULL)
return (EAI_MEMORY);
if (nvlist_get_number(nvl, "error") != 0) {
error = (int)nvlist_get_number(nvl, "error");
nvlist_destroy(nvl);
return (error);
}
nvlai = NULL;
firstai = prevai = curai = NULL;
for (ii = 0; ; ii++) {
if (!nvlist_existsf_nvlist(nvl, "res%u", ii))
break;
nvlai = nvlist_getf_nvlist(nvl, "res%u", ii);
curai = addrinfo_unpack(nvlai);
if (curai == NULL)
break;
if (prevai != NULL)
prevai->ai_next = curai;
else if (firstai == NULL)
firstai = curai;
}
nvlist_destroy(nvl);
if (curai == NULL && nvlai != NULL) {
if (firstai == NULL)
freeaddrinfo(firstai);
return (EAI_MEMORY);
}
*res = firstai;
return (0);
}
int
cap_getnameinfo(cap_channel_t *chan, const struct sockaddr *sa, socklen_t salen,
char *host, size_t hostlen, char *serv, size_t servlen, int flags)
{
nvlist_t *nvl;
int error;
nvl = nvlist_create(0);
nvlist_add_string(nvl, "cmd", "getnameinfo");
nvlist_add_number(nvl, "hostlen", (uint64_t)hostlen);
nvlist_add_number(nvl, "servlen", (uint64_t)servlen);
nvlist_add_binary(nvl, "sa", sa, (size_t)salen);
nvlist_add_number(nvl, "flags", (uint64_t)flags);
nvl = cap_xfer_nvlist(chan, nvl);
if (nvl == NULL)
return (EAI_MEMORY);
if (nvlist_get_number(nvl, "error") != 0) {
error = (int)nvlist_get_number(nvl, "error");
nvlist_destroy(nvl);
return (error);
}
if (host != NULL)
strlcpy(host, nvlist_get_string(nvl, "host"), hostlen + 1);
if (serv != NULL)
strlcpy(serv, nvlist_get_string(nvl, "serv"), servlen + 1);
nvlist_destroy(nvl);
return (0);
}
static void
limit_remove(nvlist_t *limits, const char *prefix)
{
const char *name;
size_t prefixlen;
void *cookie;
prefixlen = strlen(prefix);
again:
cookie = NULL;
while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
if (strncmp(name, prefix, prefixlen) == 0) {
nvlist_free(limits, name);
goto again;
}
}
}
int
cap_dns_type_limit(cap_channel_t *chan, const char * const *types,
size_t ntypes)
{
nvlist_t *limits;
unsigned int i;
if (cap_limit_get(chan, &limits) < 0)
return (-1);
if (limits == NULL)
limits = nvlist_create(0);
else
limit_remove(limits, "type");
for (i = 0; i < ntypes; i++)
nvlist_addf_string(limits, types[i], "type%u", i);
return (cap_limit_set(chan, limits));
}
int
cap_dns_family_limit(cap_channel_t *chan, const int *families,
size_t nfamilies)
{
nvlist_t *limits;
unsigned int i;
if (cap_limit_get(chan, &limits) < 0)
return (-1);
if (limits == NULL)
limits = nvlist_create(0);
else
limit_remove(limits, "family");
for (i = 0; i < nfamilies; i++) {
nvlist_addf_number(limits, (uint64_t)families[i],
"family%u", i);
}
return (cap_limit_set(chan, limits));
}

View File

@ -0,0 +1,57 @@
/*-
* Copyright (c) 2012 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* 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 _LIBCAPSICUM_DNS_H_
#define _LIBCAPSICUM_DNS_H_
#include <sys/socket.h> /* socklen_t */
struct addrinfo;
struct hostent;
struct hostent *cap_gethostbyname(cap_channel_t *chan, const char *name);
struct hostent *cap_gethostbyname2(cap_channel_t *chan, const char *name,
int type);
struct hostent *cap_gethostbyaddr(cap_channel_t *chan, const void *addr,
socklen_t len, int type);
int cap_getaddrinfo(cap_channel_t *chan, const char *hostname,
const char *servname, const struct addrinfo *hints, struct addrinfo **res);
int cap_getnameinfo(cap_channel_t *chan, const struct sockaddr *sa,
socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen,
int flags);
int cap_dns_type_limit(cap_channel_t *chan, const char * const *types,
size_t ntypes);
int cap_dns_family_limit(cap_channel_t *chan, const int *families,
size_t nfamilies);
#endif /* !_LIBCAPSICUM_DNS_H_ */

View File

@ -0,0 +1,426 @@
/*-
* Copyright (c) 2013 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <assert.h>
#include <errno.h>
#include <grp.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dnv.h>
#include <nv.h>
#include "libcapsicum.h"
#include "libcapsicum_grp.h"
static struct group ggrp;
static char *gbuffer;
static size_t gbufsize;
static int
group_resize(void)
{
char *buf;
if (gbufsize == 0)
gbufsize = 1024;
else
gbufsize *= 2;
buf = gbuffer;
gbuffer = realloc(buf, gbufsize);
if (gbuffer == NULL) {
free(buf);
gbufsize = 0;
return (ENOMEM);
}
memset(gbuffer, 0, gbufsize);
return (0);
}
static int
group_unpack_string(const nvlist_t *nvl, const char *fieldname, char **fieldp,
char **bufferp, size_t *bufsizep)
{
const char *str;
size_t len;
str = nvlist_get_string(nvl, fieldname);
len = strlcpy(*bufferp, str, *bufsizep);
if (len >= *bufsizep)
return (ERANGE);
*fieldp = *bufferp;
*bufferp += len + 1;
*bufsizep -= len + 1;
return (0);
}
static int
group_unpack_members(const nvlist_t *nvl, char ***fieldp, char **bufferp,
size_t *bufsizep)
{
const char *mem;
char **outstrs, *str;
size_t nmem, datasize, strsize;
unsigned int ii;
if (!nvlist_exists_number(nvl, "gr_nmem")) {
datasize = _ALIGNBYTES + sizeof(char *);
if (datasize >= *bufsizep)
return (ERANGE);
outstrs = (char **)_ALIGN(*bufferp);
outstrs[0] = NULL;
*fieldp = outstrs;
*bufferp += datasize;
*bufsizep -= datasize;
return (0);
}
nmem = (size_t)nvlist_get_number(nvl, "gr_nmem");
datasize = _ALIGNBYTES + sizeof(char *) * (nmem + 1);
for (ii = 0; ii < nmem; ii++) {
mem = dnvlist_getf_string(nvl, NULL, "gr_mem[%u]", ii);
if (mem == NULL)
return (EINVAL);
datasize += strlen(mem) + 1;
}
if (datasize >= *bufsizep)
return (ERANGE);
outstrs = (char **)_ALIGN(*bufferp);
str = (char *)outstrs + sizeof(char *) * (nmem + 1);
for (ii = 0; ii < nmem; ii++) {
mem = nvlist_getf_string(nvl, "gr_mem[%u]", ii);
strsize = strlen(mem) + 1;
memcpy(str, mem, strsize);
outstrs[ii] = str;
str += strsize;
}
assert(ii == nmem);
outstrs[ii] = NULL;
*fieldp = outstrs;
*bufferp += datasize;
*bufsizep -= datasize;
return (0);
}
static int
group_unpack(const nvlist_t *nvl, struct group *grp, char *buffer,
size_t bufsize)
{
int error;
if (!nvlist_exists_string(nvl, "gr_name"))
return (EINVAL);
memset(grp, 0, sizeof(*grp));
error = group_unpack_string(nvl, "gr_name", &grp->gr_name, &buffer,
&bufsize);
if (error != 0)
return (error);
error = group_unpack_string(nvl, "gr_passwd", &grp->gr_passwd, &buffer,
&bufsize);
if (error != 0)
return (error);
grp->gr_gid = (gid_t)nvlist_get_number(nvl, "gr_gid");
error = group_unpack_members(nvl, &grp->gr_mem, &buffer, &bufsize);
if (error != 0)
return (error);
return (0);
}
static int
cap_getgrcommon_r(cap_channel_t *chan, const char *cmd, const char *name,
gid_t gid, struct group *grp, char *buffer, size_t bufsize,
struct group **result)
{
nvlist_t *nvl;
bool getgr_r;
int error;
nvl = nvlist_create(0);
nvlist_add_string(nvl, "cmd", cmd);
if (strcmp(cmd, "getgrent") == 0 || strcmp(cmd, "getgrent_r") == 0) {
/* Add nothing. */
} else if (strcmp(cmd, "getgrnam") == 0 ||
strcmp(cmd, "getgrnam_r") == 0) {
nvlist_add_string(nvl, "name", name);
} else if (strcmp(cmd, "getgrgid") == 0 ||
strcmp(cmd, "getgrgid_r") == 0) {
nvlist_add_number(nvl, "gid", (uint64_t)gid);
} else {
abort();
}
nvl = cap_xfer_nvlist(chan, nvl);
if (nvl == NULL) {
assert(errno != 0);
*result = NULL;
return (errno);
}
error = (int)nvlist_get_number(nvl, "error");
if (error != 0) {
nvlist_destroy(nvl);
*result = NULL;
return (error);
}
if (!nvlist_exists_string(nvl, "gr_name")) {
/* Not found. */
nvlist_destroy(nvl);
*result = NULL;
return (0);
}
getgr_r = (strcmp(cmd, "getgrent_r") == 0 ||
strcmp(cmd, "getgrnam_r") == 0 || strcmp(cmd, "getgrgid_r") == 0);
for (;;) {
error = group_unpack(nvl, grp, buffer, bufsize);
if (getgr_r || error != ERANGE)
break;
assert(buffer == gbuffer);
assert(bufsize == gbufsize);
error = group_resize();
if (error != 0)
break;
/* Update pointers after resize. */
buffer = gbuffer;
bufsize = gbufsize;
}
nvlist_destroy(nvl);
if (error == 0)
*result = grp;
else
*result = NULL;
return (error);
}
static struct group *
cap_getgrcommon(cap_channel_t *chan, const char *cmd, const char *name,
gid_t gid)
{
struct group *result;
int error, serrno;
serrno = errno;
error = cap_getgrcommon_r(chan, cmd, name, gid, &ggrp, gbuffer,
gbufsize, &result);
if (error != 0) {
errno = error;
return (NULL);
}
errno = serrno;
return (result);
}
struct group *
cap_getgrent(cap_channel_t *chan)
{
return (cap_getgrcommon(chan, "getgrent", NULL, 0));
}
struct group *
cap_getgrnam(cap_channel_t *chan, const char *name)
{
return (cap_getgrcommon(chan, "getgrnam", name, 0));
}
struct group *
cap_getgrgid(cap_channel_t *chan, gid_t gid)
{
return (cap_getgrcommon(chan, "getgrgid", NULL, gid));
}
int
cap_getgrent_r(cap_channel_t *chan, struct group *grp, char *buffer,
size_t bufsize, struct group **result)
{
return (cap_getgrcommon_r(chan, "getgrent_r", NULL, 0, grp, buffer,
bufsize, result));
}
int
cap_getgrnam_r(cap_channel_t *chan, const char *name, struct group *grp,
char *buffer, size_t bufsize, struct group **result)
{
return (cap_getgrcommon_r(chan, "getgrnam_r", name, 0, grp, buffer,
bufsize, result));
}
int
cap_getgrgid_r(cap_channel_t *chan, gid_t gid, struct group *grp, char *buffer,
size_t bufsize, struct group **result)
{
return (cap_getgrcommon_r(chan, "getgrgid_r", NULL, gid, grp, buffer,
bufsize, result));
}
int
cap_setgroupent(cap_channel_t *chan, int stayopen)
{
nvlist_t *nvl;
nvl = nvlist_create(0);
nvlist_add_string(nvl, "cmd", "setgroupent");
nvlist_add_bool(nvl, "stayopen", stayopen != 0);
nvl = cap_xfer_nvlist(chan, nvl);
if (nvl == NULL)
return (0);
if (nvlist_get_number(nvl, "error") != 0) {
errno = nvlist_get_number(nvl, "error");
nvlist_destroy(nvl);
return (0);
}
nvlist_destroy(nvl);
return (1);
}
int
cap_setgrent(cap_channel_t *chan)
{
nvlist_t *nvl;
nvl = nvlist_create(0);
nvlist_add_string(nvl, "cmd", "setgrent");
nvl = cap_xfer_nvlist(chan, nvl);
if (nvl == NULL)
return (0);
if (nvlist_get_number(nvl, "error") != 0) {
errno = nvlist_get_number(nvl, "error");
nvlist_destroy(nvl);
return (0);
}
nvlist_destroy(nvl);
return (1);
}
void
cap_endgrent(cap_channel_t *chan)
{
nvlist_t *nvl;
nvl = nvlist_create(0);
nvlist_add_string(nvl, "cmd", "endgrent");
/* Ignore any errors, we have no way to report them. */
nvlist_destroy(cap_xfer_nvlist(chan, nvl));
}
int
cap_grp_limit_cmds(cap_channel_t *chan, const char * const *cmds, size_t ncmds)
{
nvlist_t *limits, *nvl;
unsigned int i;
if (cap_limit_get(chan, &limits) < 0)
return (-1);
if (limits == NULL) {
limits = nvlist_create(0);
} else {
if (nvlist_exists_nvlist(limits, "cmds"))
nvlist_free_nvlist(limits, "cmds");
}
nvl = nvlist_create(0);
for (i = 0; i < ncmds; i++)
nvlist_add_null(nvl, cmds[i]);
nvlist_move_nvlist(limits, "cmds", nvl);
return (cap_limit_set(chan, limits));
}
int
cap_grp_limit_fields(cap_channel_t *chan, const char * const *fields,
size_t nfields)
{
nvlist_t *limits, *nvl;
unsigned int i;
if (cap_limit_get(chan, &limits) < 0)
return (-1);
if (limits == NULL) {
limits = nvlist_create(0);
} else {
if (nvlist_exists_nvlist(limits, "fields"))
nvlist_free_nvlist(limits, "fields");
}
nvl = nvlist_create(0);
for (i = 0; i < nfields; i++)
nvlist_add_null(nvl, fields[i]);
nvlist_move_nvlist(limits, "fields", nvl);
return (cap_limit_set(chan, limits));
}
int
cap_grp_limit_groups(cap_channel_t *chan, const char * const *names,
size_t nnames, gid_t *gids, size_t ngids)
{
nvlist_t *limits, *groups;
unsigned int i;
if (cap_limit_get(chan, &limits) < 0)
return (-1);
if (limits == NULL) {
limits = nvlist_create(0);
} else {
if (nvlist_exists_nvlist(limits, "groups"))
nvlist_free_nvlist(limits, "groups");
}
groups = nvlist_create(0);
for (i = 0; i < ngids; i++)
nvlist_addf_number(groups, (uint64_t)gids[i], "gid%u", i);
for (i = 0; i < nnames; i++)
nvlist_addf_string(groups, names[i], "name%u", i);
nvlist_move_nvlist(limits, "groups", groups);
return (cap_limit_set(chan, limits));
}

View File

@ -0,0 +1,57 @@
/*-
* Copyright (c) 2013 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* 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 _LIBCAPSICUM_GRP_H_
#define _LIBCAPSICUM_GRP_H_
struct group *cap_getgrent(cap_channel_t *chan);
struct group *cap_getgrnam(cap_channel_t *chan, const char *name);
struct group *cap_getgrgid(cap_channel_t *chan, gid_t gid);
int cap_getgrent_r(cap_channel_t *chan, struct group *grp, char *buffer,
size_t bufsize, struct group **result);
int cap_getgrnam_r(cap_channel_t *chan, const char *name, struct group *grp,
char *buffer, size_t bufsize, struct group **result);
int cap_getgrgid_r(cap_channel_t *chan, gid_t gid, struct group *grp,
char *buffer, size_t bufsize, struct group **result);
int cap_setgroupent(cap_channel_t *chan, int stayopen);
int cap_setgrent(cap_channel_t *chan);
void cap_endgrent(cap_channel_t *chan);
int cap_grp_limit_cmds(cap_channel_t *chan, const char * const *cmds,
size_t ncmds);
int cap_grp_limit_fields(cap_channel_t *chan, const char * const *fields,
size_t nfields);
int cap_grp_limit_groups(cap_channel_t *chan, const char * const *names,
size_t nnames, gid_t *gids, size_t ngids);
#endif /* !_LIBCAPSICUM_GRP_H_ */

View File

@ -0,0 +1,39 @@
/*-
* Copyright (c) 2012-2013 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* 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 _LIBCAPSICUM_IMPL_H_
#define _LIBCAPSICUM_IMPL_H_
#define CASPER_SOCKPATH "/var/run/casper"
bool fd_is_valid(int fd);
#endif /* !_LIBCAPSICUM_IMPL_H_ */

View File

@ -0,0 +1,384 @@
/*-
* Copyright (c) 2013 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <assert.h>
#include <errno.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <nv.h>
#include "libcapsicum.h"
#include "libcapsicum_pwd.h"
static struct passwd gpwd;
static char *gbuffer;
static size_t gbufsize;
static int
passwd_resize(void)
{
char *buf;
if (gbufsize == 0)
gbufsize = 1024;
else
gbufsize *= 2;
buf = gbuffer;
gbuffer = realloc(buf, gbufsize);
if (gbuffer == NULL) {
free(buf);
gbufsize = 0;
return (ENOMEM);
}
memset(gbuffer, 0, gbufsize);
return (0);
}
static int
passwd_unpack_string(const nvlist_t *nvl, const char *fieldname, char **fieldp,
char **bufferp, size_t *bufsizep)
{
const char *str;
size_t len;
str = nvlist_get_string(nvl, fieldname);
len = strlcpy(*bufferp, str, *bufsizep);
if (len >= *bufsizep)
return (ERANGE);
*fieldp = *bufferp;
*bufferp += len + 1;
*bufsizep -= len + 1;
return (0);
}
static int
passwd_unpack(const nvlist_t *nvl, struct passwd *pwd, char *buffer,
size_t bufsize)
{
int error;
if (!nvlist_exists_string(nvl, "pw_name"))
return (EINVAL);
memset(pwd, 0, sizeof(*pwd));
error = passwd_unpack_string(nvl, "pw_name", &pwd->pw_name, &buffer,
&bufsize);
if (error != 0)
return (error);
pwd->pw_uid = (uid_t)nvlist_get_number(nvl, "pw_uid");
pwd->pw_gid = (gid_t)nvlist_get_number(nvl, "pw_gid");
pwd->pw_change = (time_t)nvlist_get_number(nvl, "pw_change");
error = passwd_unpack_string(nvl, "pw_passwd", &pwd->pw_passwd, &buffer,
&bufsize);
if (error != 0)
return (error);
error = passwd_unpack_string(nvl, "pw_class", &pwd->pw_class, &buffer,
&bufsize);
if (error != 0)
return (error);
error = passwd_unpack_string(nvl, "pw_gecos", &pwd->pw_gecos, &buffer,
&bufsize);
if (error != 0)
return (error);
error = passwd_unpack_string(nvl, "pw_dir", &pwd->pw_dir, &buffer,
&bufsize);
if (error != 0)
return (error);
error = passwd_unpack_string(nvl, "pw_shell", &pwd->pw_shell, &buffer,
&bufsize);
if (error != 0)
return (error);
pwd->pw_expire = (time_t)nvlist_get_number(nvl, "pw_expire");
pwd->pw_fields = (int)nvlist_get_number(nvl, "pw_fields");
return (0);
}
static int
cap_getpwcommon_r(cap_channel_t *chan, const char *cmd, const char *login,
uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize,
struct passwd **result)
{
nvlist_t *nvl;
bool getpw_r;
int error;
nvl = nvlist_create(0);
nvlist_add_string(nvl, "cmd", cmd);
if (strcmp(cmd, "getpwent") == 0 || strcmp(cmd, "getpwent_r") == 0) {
/* Add nothing. */
} else if (strcmp(cmd, "getpwnam") == 0 ||
strcmp(cmd, "getpwnam_r") == 0) {
nvlist_add_string(nvl, "name", login);
} else if (strcmp(cmd, "getpwuid") == 0 ||
strcmp(cmd, "getpwuid_r") == 0) {
nvlist_add_number(nvl, "uid", (uint64_t)uid);
} else {
abort();
}
nvl = cap_xfer_nvlist(chan, nvl);
if (nvl == NULL) {
assert(errno != 0);
*result = NULL;
return (errno);
}
error = (int)nvlist_get_number(nvl, "error");
if (error != 0) {
nvlist_destroy(nvl);
*result = NULL;
return (error);
}
if (!nvlist_exists_string(nvl, "pw_name")) {
/* Not found. */
nvlist_destroy(nvl);
*result = NULL;
return (0);
}
getpw_r = (strcmp(cmd, "getpwent_r") == 0 ||
strcmp(cmd, "getpwnam_r") == 0 || strcmp(cmd, "getpwuid_r") == 0);
for (;;) {
error = passwd_unpack(nvl, pwd, buffer, bufsize);
if (getpw_r || error != ERANGE)
break;
assert(buffer == gbuffer);
assert(bufsize == gbufsize);
error = passwd_resize();
if (error != 0)
break;
/* Update pointers after resize. */
buffer = gbuffer;
bufsize = gbufsize;
}
nvlist_destroy(nvl);
if (error == 0)
*result = pwd;
else
*result = NULL;
return (error);
}
static struct passwd *
cap_getpwcommon(cap_channel_t *chan, const char *cmd, const char *login,
uid_t uid)
{
struct passwd *result;
int error, serrno;
serrno = errno;
error = cap_getpwcommon_r(chan, cmd, login, uid, &gpwd, gbuffer,
gbufsize, &result);
if (error != 0) {
errno = error;
return (NULL);
}
errno = serrno;
return (result);
}
struct passwd *
cap_getpwent(cap_channel_t *chan)
{
return (cap_getpwcommon(chan, "getpwent", NULL, 0));
}
struct passwd *
cap_getpwnam(cap_channel_t *chan, const char *login)
{
return (cap_getpwcommon(chan, "getpwnam", login, 0));
}
struct passwd *
cap_getpwuid(cap_channel_t *chan, uid_t uid)
{
return (cap_getpwcommon(chan, "getpwuid", NULL, uid));
}
int
cap_getpwent_r(cap_channel_t *chan, struct passwd *pwd, char *buffer,
size_t bufsize, struct passwd **result)
{
return (cap_getpwcommon_r(chan, "getpwent_r", NULL, 0, pwd, buffer,
bufsize, result));
}
int
cap_getpwnam_r(cap_channel_t *chan, const char *name, struct passwd *pwd,
char *buffer, size_t bufsize, struct passwd **result)
{
return (cap_getpwcommon_r(chan, "getpwnam_r", name, 0, pwd, buffer,
bufsize, result));
}
int
cap_getpwuid_r(cap_channel_t *chan, uid_t uid, struct passwd *pwd, char *buffer,
size_t bufsize, struct passwd **result)
{
return (cap_getpwcommon_r(chan, "getpwuid_r", NULL, uid, pwd, buffer,
bufsize, result));
}
int
cap_setpassent(cap_channel_t *chan, int stayopen)
{
nvlist_t *nvl;
nvl = nvlist_create(0);
nvlist_add_string(nvl, "cmd", "setpassent");
nvlist_add_bool(nvl, "stayopen", stayopen != 0);
nvl = cap_xfer_nvlist(chan, nvl);
if (nvl == NULL)
return (0);
if (nvlist_get_number(nvl, "error") != 0) {
errno = nvlist_get_number(nvl, "error");
nvlist_destroy(nvl);
return (0);
}
nvlist_destroy(nvl);
return (1);
}
static void
cap_set_end_pwent(cap_channel_t *chan, const char *cmd)
{
nvlist_t *nvl;
nvl = nvlist_create(0);
nvlist_add_string(nvl, "cmd", cmd);
/* Ignore any errors, we have no way to report them. */
nvlist_destroy(cap_xfer_nvlist(chan, nvl));
}
void
cap_setpwent(cap_channel_t *chan)
{
cap_set_end_pwent(chan, "setpwent");
}
void
cap_endpwent(cap_channel_t *chan)
{
cap_set_end_pwent(chan, "endpwent");
}
int
cap_pwd_limit_cmds(cap_channel_t *chan, const char * const *cmds, size_t ncmds)
{
nvlist_t *limits, *nvl;
unsigned int i;
if (cap_limit_get(chan, &limits) < 0)
return (-1);
if (limits == NULL) {
limits = nvlist_create(0);
} else {
if (nvlist_exists_nvlist(limits, "cmds"))
nvlist_free_nvlist(limits, "cmds");
}
nvl = nvlist_create(0);
for (i = 0; i < ncmds; i++)
nvlist_add_null(nvl, cmds[i]);
nvlist_move_nvlist(limits, "cmds", nvl);
return (cap_limit_set(chan, limits));
}
int
cap_pwd_limit_fields(cap_channel_t *chan, const char * const *fields,
size_t nfields)
{
nvlist_t *limits, *nvl;
unsigned int i;
if (cap_limit_get(chan, &limits) < 0)
return (-1);
if (limits == NULL) {
limits = nvlist_create(0);
} else {
if (nvlist_exists_nvlist(limits, "fields"))
nvlist_free_nvlist(limits, "fields");
}
nvl = nvlist_create(0);
for (i = 0; i < nfields; i++)
nvlist_add_null(nvl, fields[i]);
nvlist_move_nvlist(limits, "fields", nvl);
return (cap_limit_set(chan, limits));
}
int
cap_pwd_limit_users(cap_channel_t *chan, const char * const *names,
size_t nnames, uid_t *uids, size_t nuids)
{
nvlist_t *limits, *users;
unsigned int i;
if (cap_limit_get(chan, &limits) < 0)
return (-1);
if (limits == NULL) {
limits = nvlist_create(0);
} else {
if (nvlist_exists_nvlist(limits, "users"))
nvlist_free_nvlist(limits, "users");
}
users = nvlist_create(0);
for (i = 0; i < nuids; i++)
nvlist_addf_number(users, (uint64_t)uids[i], "uid%u", i);
for (i = 0; i < nnames; i++)
nvlist_addf_string(users, names[i], "name%u", i);
nvlist_move_nvlist(limits, "users", users);
return (cap_limit_set(chan, limits));
}

View File

@ -0,0 +1,57 @@
/*-
* Copyright (c) 2013 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* 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 _LIBCAPSICUM_PWD_H_
#define _LIBCAPSICUM_PWD_H_
struct passwd *cap_getpwent(cap_channel_t *chan);
struct passwd *cap_getpwnam(cap_channel_t *chan, const char *login);
struct passwd *cap_getpwuid(cap_channel_t *chan, uid_t uid);
int cap_getpwent_r(cap_channel_t *chan, struct passwd *pwd, char *buffer,
size_t bufsize, struct passwd **result);
int cap_getpwnam_r(cap_channel_t *chan, const char *name, struct passwd *pwd,
char *buffer, size_t bufsize, struct passwd **result);
int cap_getpwuid_r(cap_channel_t *chan, uid_t uid, struct passwd *pwd,
char *buffer, size_t bufsize, struct passwd **result);
int cap_setpassent(cap_channel_t *chan, int stayopen);
void cap_setpwent(cap_channel_t *chan);
void cap_endpwent(cap_channel_t *chan);
int cap_pwd_limit_cmds(cap_channel_t *chan, const char * const *cmds,
size_t ncmds);
int cap_pwd_limit_fields(cap_channel_t *chan, const char * const *fields,
size_t nfields);
int cap_pwd_limit_users(cap_channel_t *chan, const char * const *names,
size_t nnames, uid_t *uids, size_t nuids);
#endif /* !_LIBCAPSICUM_PWD_H_ */

View File

@ -0,0 +1,80 @@
/*-
* Copyright (c) 2013 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <nv.h>
#include "libcapsicum.h"
#include "libcapsicum_random.h"
#define MAXSIZE (1024 * 1024)
int
cap_random_buf(cap_channel_t *chan, void *buf, size_t nbytes)
{
nvlist_t *nvl;
const void *randbuf;
uint8_t *ptr;
size_t left, randbufsize;
left = nbytes;
ptr = buf;
while (left > 0) {
nvl = nvlist_create(0);
nvlist_add_string(nvl, "cmd", "generate");
nvlist_add_number(nvl, "size",
(uint64_t)(left > MAXSIZE ? MAXSIZE : left));
nvl = cap_xfer_nvlist(chan, nvl);
if (nvl == NULL)
return (-1);
if (nvlist_get_number(nvl, "error") != 0) {
errno = (int)nvlist_get_number(nvl, "error");
nvlist_destroy(nvl);
return (-1);
}
randbuf = nvlist_get_binary(nvl, "data", &randbufsize);
memcpy(ptr, randbuf, randbufsize);
nvlist_destroy(nvl);
ptr += randbufsize;
assert(left >= randbufsize);
left -= randbufsize;
}
return (0);
}

View File

@ -0,0 +1,37 @@
/*-
* Copyright (c) 2013 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* 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 _LIBCAPSICUM_RANDOM_H_
#define _LIBCAPSICUM_RANDOM_H_
int cap_random_buf(cap_channel_t *chan, void *buf, size_t nbytes);
#endif /* !_LIBCAPSICUM_RANDOM_H_ */

View File

@ -0,0 +1,96 @@
/*-
* Copyright (c) 2013 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <nv.h>
#include "msgio.h"
#include "libcapsicum.h"
#include "libcapsicum_impl.h"
#include "libcapsicum_service.h"
cap_channel_t *
cap_service_open(const cap_channel_t *chan, const char *name)
{
cap_channel_t *newchan;
nvlist_t *nvl;
int sock, error;
sock = -1;
nvl = nvlist_create(0);
nvlist_add_string(nvl, "cmd", "open");
nvlist_add_string(nvl, "service", name);
if (fd_is_valid(STDERR_FILENO))
nvlist_add_descriptor(nvl, "stderrfd", STDERR_FILENO);
nvl = cap_xfer_nvlist(chan, nvl);
if (nvl == NULL)
return (NULL);
error = (int)nvlist_get_number(nvl, "error");
if (error != 0) {
nvlist_destroy(nvl);
errno = error;
return (NULL);
}
sock = nvlist_take_descriptor(nvl, "chanfd");
assert(sock >= 0);
nvlist_destroy(nvl);
nvl = NULL;
if (cred_send(sock) == -1)
goto fail;
newchan = cap_wrap(sock);
if (newchan == NULL)
goto fail;
return (newchan);
fail:
error = errno;
close(sock);
errno = error;
return (NULL);
}
int
cap_service_limit(const cap_channel_t *chan, const char * const *names,
size_t nnames)
{
nvlist_t *limits;
unsigned int i;
limits = nvlist_create(0);
for (i = 0; i < nnames; i++)
nvlist_add_null(limits, names[i]);
return (cap_limit_set(chan, limits));
}

View File

@ -0,0 +1,40 @@
/*-
* Copyright (c) 2013 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* 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 _LIBCAPSICUM_SERVICE_H_
#define _LIBCAPSICUM_SERVICE_H_
cap_channel_t *cap_service_open(const cap_channel_t *chan, const char *name);
int cap_service_limit(const cap_channel_t *chan, const char * const *names,
size_t nnames);
#endif /* !_LIBCAPSICUM_SERVICE_H_ */

View File

@ -0,0 +1,86 @@
/*-
* Copyright (c) 2013 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <errno.h>
#include <string.h>
#include <nv.h>
#include "libcapsicum.h"
#include "libcapsicum_sysctl.h"
int
cap_sysctlbyname(cap_channel_t *chan, const char *name, void *oldp,
size_t *oldlenp, const void *newp, size_t newlen)
{
nvlist_t *nvl;
const uint8_t *retoldp;
uint8_t operation;
size_t oldlen;
operation = 0;
if (oldp != NULL)
operation |= CAP_SYSCTL_READ;
if (newp != NULL)
operation |= CAP_SYSCTL_WRITE;
nvl = nvlist_create(0);
nvlist_add_string(nvl, "cmd", "sysctl");
nvlist_add_string(nvl, "name", name);
nvlist_add_number(nvl, "operation", (uint64_t)operation);
if (oldp == NULL && oldlenp != NULL)
nvlist_add_null(nvl, "justsize");
else if (oldlenp != NULL)
nvlist_add_number(nvl, "oldlen", (uint64_t)*oldlenp);
if (newp != NULL)
nvlist_add_binary(nvl, "newp", newp, newlen);
nvl = cap_xfer_nvlist(chan, nvl);
if (nvl == NULL)
return (-1);
if (nvlist_get_number(nvl, "error") != 0) {
errno = (int)nvlist_get_number(nvl, "error");
nvlist_destroy(nvl);
return (-1);
}
if (oldp == NULL && oldlenp != NULL) {
*oldlenp = (size_t)nvlist_get_number(nvl, "oldlen");
} else if (oldp != NULL) {
retoldp = nvlist_get_binary(nvl, "oldp", &oldlen);
memcpy(oldp, retoldp, oldlen);
if (oldlenp != NULL)
*oldlenp = oldlen;
}
nvlist_destroy(nvl);
return (0);
}

View File

@ -0,0 +1,43 @@
/*-
* Copyright (c) 2013 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* 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 _LIBCAPSICUM_SYSCTL_H_
#define _LIBCAPSICUM_SYSCTL_H_
#define CAP_SYSCTL_READ 0x01
#define CAP_SYSCTL_WRITE 0x02
#define CAP_SYSCTL_RDWR (CAP_SYSCTL_READ | CAP_SYSCTL_WRITE)
#define CAP_SYSCTL_RECURSIVE 0x04
int cap_sysctlbyname(cap_channel_t *chan, const char *name, void *oldp,
size_t *oldlenp, const void *newp, size_t newlen);
#endif /* !_LIBCAPSICUM_SYSCTL_H_ */

View File

@ -6,6 +6,7 @@
SUBDIR= ${_atf} \
${_atrun} \
bootpd \
${_casper} \
${_comsat} \
fingerd \
ftpd \
@ -38,6 +39,10 @@ SUBDIR= ${_atf} \
_atrun= atrun
.endif
.if ${MK_CASPER} != "no"
_casper= casper
.endif
.if ${MK_MAIL} != "no"
_comsat= comsat
.endif

11
libexec/casper/Makefile Normal file
View File

@ -0,0 +1,11 @@
# $FreeBSD$
.include <bsd.own.mk>
SUBDIR= dns
SUBDIR+=grp
SUBDIR+=pwd
SUBDIR+=random
SUBDIR+=sysctl
.include <bsd.subdir.mk>

View File

@ -0,0 +1,22 @@
# $FreeBSD$
.PATH: ${.CURDIR} ${.CURDIR}/../../../sbin/casper
PROG= dns
SRCS= dns.c
DPADD= ${LIBCAPSICUM} ${LIBCASPER} ${LIBNV} ${LIBPJDLOG} ${LIBUTIL}
LDADD= -lcapsicum -lcasper -lnv -lpjdlog -lutil
BINDIR= /libexec/casper
CFLAGS+=-I${.CURDIR}
CFLAGS+=-I${.CURDIR}/../../../lib/libcapsicum
CFLAGS+=-I${.CURDIR}/../../../lib/libcasper
CFLAGS+=-I${.CURDIR}/../../../lib/libpjdlog
CFLAGS+=-I${.CURDIR}/../../../sbin/casper
NO_MAN=
.include <bsd.prog.mk>

425
libexec/casper/dns/dns.c Normal file
View File

@ -0,0 +1,425 @@
/*-
* Copyright (c) 2012-2013 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <netinet/in.h>
#include <errno.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <libcapsicum.h>
#include <libcapsicum_dns.h>
#include <libcasper.h>
#include <nv.h>
#include <pjdlog.h>
static bool
dns_allowed_type(const nvlist_t *limits, const char *type)
{
const char *name;
bool notypes;
void *cookie;
if (limits == NULL)
return (true);
notypes = true;
cookie = NULL;
while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
if (strncmp(name, "type", sizeof("type") - 1) != 0)
continue;
notypes = false;
if (strcmp(nvlist_get_string(limits, name), type) == 0)
return (true);
}
/* If there are no types at all, allow any type. */
if (notypes)
return (true);
return (false);
}
static bool
dns_allowed_family(const nvlist_t *limits, int family)
{
const char *name;
bool nofamilies;
void *cookie;
if (limits == NULL)
return (true);
nofamilies = true;
cookie = NULL;
while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
if (strncmp(name, "family", sizeof("family") - 1) != 0)
continue;
nofamilies = false;
if (family == AF_UNSPEC)
continue;
if (nvlist_get_number(limits, name) == (uint64_t)family)
return (true);
}
/* If there are no families at all, allow any family. */
if (nofamilies)
return (true);
return (false);
}
static void
hostent_pack(const struct hostent *hp, nvlist_t *nvl)
{
unsigned int ii;
nvlist_add_string(nvl, "name", hp->h_name);
nvlist_add_number(nvl, "addrtype", (uint64_t)hp->h_addrtype);
nvlist_add_number(nvl, "length", (uint64_t)hp->h_length);
if (hp->h_aliases == NULL) {
nvlist_add_number(nvl, "naliases", 0);
} else {
for (ii = 0; hp->h_aliases[ii] != NULL; ii++) {
nvlist_addf_string(nvl, hp->h_aliases[ii], "alias%u",
ii);
}
nvlist_add_number(nvl, "naliases", (uint64_t)ii);
}
if (hp->h_addr_list == NULL) {
nvlist_add_number(nvl, "naddrs", 0);
} else {
for (ii = 0; hp->h_addr_list[ii] != NULL; ii++) {
nvlist_addf_binary(nvl, hp->h_addr_list[ii],
(size_t)hp->h_length, "addr%u", ii);
}
nvlist_add_number(nvl, "naddrs", (uint64_t)ii);
}
}
static int
dns_gethostbyname(const nvlist_t *limits, const nvlist_t *nvlin,
nvlist_t *nvlout)
{
struct hostent *hp;
int family;
if (!dns_allowed_type(limits, "NAME"))
return (NO_RECOVERY);
family = (int)nvlist_get_number(nvlin, "family");
if (!dns_allowed_family(limits, family))
return (NO_RECOVERY);
hp = gethostbyname2(nvlist_get_string(nvlin, "name"), family);
if (hp == NULL)
return (h_errno);
hostent_pack(hp, nvlout);
return (0);
}
static int
dns_gethostbyaddr(const nvlist_t *limits, const nvlist_t *nvlin,
nvlist_t *nvlout)
{
struct hostent *hp;
const void *addr;
size_t addrsize;
int family;
if (!dns_allowed_type(limits, "ADDR"))
return (NO_RECOVERY);
family = (int)nvlist_get_number(nvlin, "family");
if (!dns_allowed_family(limits, family))
return (NO_RECOVERY);
addr = nvlist_get_binary(nvlin, "addr", &addrsize);
hp = gethostbyaddr(addr, (socklen_t)addrsize, family);
if (hp == NULL)
return (h_errno);
hostent_pack(hp, nvlout);
return (0);
}
static int
dns_getnameinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
{
struct sockaddr_storage sast;
const void *sabin;
char *host, *serv;
size_t sabinsize, hostlen, servlen;
socklen_t salen;
int error, flags;
if (!dns_allowed_type(limits, "NAME"))
return (NO_RECOVERY);
error = 0;
host = serv = NULL;
memset(&sast, 0, sizeof(sast));
hostlen = (size_t)nvlist_get_number(nvlin, "hostlen");
servlen = (size_t)nvlist_get_number(nvlin, "servlen");
if (hostlen > 0) {
host = calloc(1, hostlen + 1);
if (host == NULL) {
error = EAI_MEMORY;
goto out;
}
}
if (servlen > 0) {
serv = calloc(1, servlen + 1);
if (serv == NULL) {
error = EAI_MEMORY;
goto out;
}
}
sabin = nvlist_get_binary(nvlin, "sa", &sabinsize);
if (sabinsize > sizeof(sast)) {
error = EAI_FAIL;
goto out;
}
memcpy(&sast, sabin, sabinsize);
salen = (socklen_t)sabinsize;
if ((sast.ss_family != AF_INET ||
salen != sizeof(struct sockaddr_in)) &&
(sast.ss_family != AF_INET6 ||
salen != sizeof(struct sockaddr_in6))) {
error = EAI_FAIL;
goto out;
}
if (!dns_allowed_family(limits, (int)sast.ss_family))
return (NO_RECOVERY);
flags = (int)nvlist_get_number(nvlin, "flags");
error = getnameinfo((struct sockaddr *)&sast, salen, host, hostlen,
serv, servlen, flags);
if (error != 0)
goto out;
nvlist_move_string(nvlout, "host", host);
nvlist_move_string(nvlout, "serv", serv);
out:
if (error != 0) {
free(host);
free(serv);
}
return (error);
}
static nvlist_t *
addrinfo_pack(const struct addrinfo *ai)
{
nvlist_t *nvl;
nvl = nvlist_create(0);
nvlist_add_number(nvl, "ai_flags", (uint64_t)ai->ai_flags);
nvlist_add_number(nvl, "ai_family", (uint64_t)ai->ai_family);
nvlist_add_number(nvl, "ai_socktype", (uint64_t)ai->ai_socktype);
nvlist_add_number(nvl, "ai_protocol", (uint64_t)ai->ai_protocol);
nvlist_add_binary(nvl, "ai_addr", ai->ai_addr, (size_t)ai->ai_addrlen);
nvlist_add_string(nvl, "ai_canonname", ai->ai_canonname);
return (nvl);
}
static int
dns_getaddrinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
{
struct addrinfo hints, *hintsp, *res, *cur;
const char *hostname, *servname;
nvlist_t *elem;
unsigned int ii;
int error, family;
if (!dns_allowed_type(limits, "ADDR"))
return (NO_RECOVERY);
hostname = nvlist_get_string(nvlin, "hostname");
servname = nvlist_get_string(nvlin, "servname");
if (nvlist_exists_number(nvlin, "hints.ai_flags")) {
size_t addrlen;
hints.ai_flags = (int)nvlist_get_number(nvlin,
"hints.ai_flags");
hints.ai_family = (int)nvlist_get_number(nvlin,
"hints.ai_family");
hints.ai_socktype = (int)nvlist_get_number(nvlin,
"hints.ai_socktype");
hints.ai_protocol = (int)nvlist_get_number(nvlin,
"hints.ai_protocol");
hints.ai_addrlen = 0;
hints.ai_addr = NULL;
hints.ai_canonname = NULL;
hintsp = &hints;
family = hints.ai_family;
} else {
hintsp = NULL;
family = AF_UNSPEC;
}
if (!dns_allowed_family(limits, family))
return (NO_RECOVERY);
error = getaddrinfo(hostname, servname, hintsp, &res);
if (error != 0)
goto out;
for (cur = res, ii = 0; cur != NULL; cur = cur->ai_next, ii++) {
elem = addrinfo_pack(cur);
nvlist_movef_nvlist(nvlout, elem, "res%u", ii);
}
freeaddrinfo(res);
error = 0;
out:
return (error);
}
static bool
limit_has_entry(const nvlist_t *limits, const char *prefix)
{
const char *name;
size_t prefixlen;
void *cookie;
if (limits == NULL)
return (false);
prefixlen = strlen(prefix);
cookie = NULL;
while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
if (strncmp(name, prefix, prefixlen) == 0)
return (true);
}
return (false);
}
static int
dns_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
{
const char *name;
void *cookie;
int nvtype;
bool hastype, hasfamily;
hastype = false;
hasfamily = false;
cookie = NULL;
while ((name = nvlist_next(newlimits, &nvtype, &cookie)) != NULL) {
if (nvtype == NV_TYPE_STRING) {
const char *type;
if (strncmp(name, "type", sizeof("type") - 1) != 0)
return (EINVAL);
type = nvlist_get_string(newlimits, name);
if (strcmp(type, "ADDR") != 0 &&
strcmp(type, "NAME") != 0) {
return (EINVAL);
}
if (!dns_allowed_type(oldlimits, type))
return (ENOTCAPABLE);
hastype = true;
} else if (nvtype == NV_TYPE_NUMBER) {
int family;
if (strncmp(name, "family", sizeof("family") - 1) != 0)
return (EINVAL);
family = (int)nvlist_get_number(newlimits, name);
if (!dns_allowed_family(oldlimits, family))
return (ENOTCAPABLE);
hasfamily = true;
} else {
return (EINVAL);
}
}
/*
* If the new limit doesn't mention type or family we have to
* check if the current limit does have those. Missing type or
* family in the limit means that all types or families are
* allowed.
*/
if (!hastype) {
if (limit_has_entry(oldlimits, "type"))
return (ENOTCAPABLE);
}
if (!hasfamily) {
if (limit_has_entry(oldlimits, "family"))
return (ENOTCAPABLE);
}
return (0);
}
static int
dns_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
nvlist_t *nvlout)
{
int error;
if (strcmp(cmd, "gethostbyname") == 0)
error = dns_gethostbyname(limits, nvlin, nvlout);
else if (strcmp(cmd, "gethostbyaddr") == 0)
error = dns_gethostbyaddr(limits, nvlin, nvlout);
else if (strcmp(cmd, "getnameinfo") == 0)
error = dns_getnameinfo(limits, nvlin, nvlout);
else if (strcmp(cmd, "getaddrinfo") == 0)
error = dns_getaddrinfo(limits, nvlin, nvlout);
else
error = NO_RECOVERY;
return (error);
}
int
main(int argc, char *argv[])
{
return (service_start("system.dns", PARENT_FILENO, dns_limit,
dns_command, argc, argv));
}

View File

@ -0,0 +1,22 @@
# $FreeBSD$
.PATH: ${.CURDIR} ${.CURDIR}/../../../sbin/casper
PROG= grp
SRCS= grp.c
DPADD= ${LIBCAPSICUM} ${LIBCASPER} ${LIBNV} ${LIBPJDLOG} ${LIBUTIL}
LDADD= -lcapsicum -lcasper -lnv -lpjdlog -lutil
BINDIR= /libexec/casper
CFLAGS+=-I${.CURDIR}
CFLAGS+=-I${.CURDIR}/../../../lib/libcapsicum
CFLAGS+=-I${.CURDIR}/../../../lib/libcasper
CFLAGS+=-I${.CURDIR}/../../../lib/libpjdlog
CFLAGS+=-I${.CURDIR}/../../../sbin/casper
NO_MAN=
.include <bsd.prog.mk>

384
libexec/casper/grp/grp.c Normal file
View File

@ -0,0 +1,384 @@
/*-
* Copyright (c) 2013 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <errno.h>
#include <grp.h>
#include <stdlib.h>
#include <string.h>
#include <libcapsicum.h>
#include <libcasper.h>
#include <nv.h>
#include <pjdlog.h>
static bool
grp_allowed_cmd(const nvlist_t *limits, const char *cmd)
{
if (limits == NULL)
return (true);
/*
* If no limit was set on allowed commands, then all commands
* are allowed.
*/
if (!nvlist_exists_nvlist(limits, "cmds"))
return (true);
limits = nvlist_get_nvlist(limits, "cmds");
return (nvlist_exists_null(limits, cmd));
}
static int
grp_allowed_cmds(const nvlist_t *oldlimits, const nvlist_t *newlimits)
{
const char *name;
void *cookie;
int type;
cookie = NULL;
while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
if (type != NV_TYPE_NULL)
return (EINVAL);
if (!grp_allowed_cmd(oldlimits, name))
return (ENOTCAPABLE);
}
return (0);
}
static bool
grp_allowed_group(const nvlist_t *limits, const char *gname, gid_t gid)
{
const char *name;
void *cookie;
int type;
if (limits == NULL)
return (true);
/*
* If no limit was set on allowed groups, then all groups are allowed.
*/
if (!nvlist_exists_nvlist(limits, "groups"))
return (true);
limits = nvlist_get_nvlist(limits, "groups");
cookie = NULL;
while ((name = nvlist_next(limits, &type, &cookie)) != NULL) {
switch (type) {
case NV_TYPE_NUMBER:
if (gid != (gid_t)-1 &&
nvlist_get_number(limits, name) == (uint64_t)gid) {
return (true);
}
break;
case NV_TYPE_STRING:
if (gname != NULL &&
strcmp(nvlist_get_string(limits, name),
gname) == 0) {
return (true);
}
break;
default:
PJDLOG_ABORT("Unexpected type %d.", type);
}
}
return (false);
}
static int
grp_allowed_groups(const nvlist_t *oldlimits, const nvlist_t *newlimits)
{
const char *name, *gname;
void *cookie;
gid_t gid;
int type;
cookie = NULL;
while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
switch (type) {
case NV_TYPE_NUMBER:
gid = (gid_t)nvlist_get_number(newlimits, name);
gname = NULL;
break;
case NV_TYPE_STRING:
gid = (gid_t)-1;
gname = nvlist_get_string(newlimits, name);
break;
default:
return (EINVAL);
}
if (!grp_allowed_group(oldlimits, gname, gid))
return (ENOTCAPABLE);
}
return (0);
}
static bool
grp_allowed_field(const nvlist_t *limits, const char *field)
{
if (limits == NULL)
return (true);
/*
* If no limit was set on allowed fields, then all fields are allowed.
*/
if (!nvlist_exists_nvlist(limits, "fields"))
return (true);
limits = nvlist_get_nvlist(limits, "fields");
return (nvlist_exists_null(limits, field));
}
static int
grp_allowed_fields(const nvlist_t *oldlimits, const nvlist_t *newlimits)
{
const char *name;
void *cookie;
int type;
cookie = NULL;
while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
if (type != NV_TYPE_NULL)
return (EINVAL);
if (!grp_allowed_field(oldlimits, name))
return (ENOTCAPABLE);
}
return (0);
}
static bool
grp_pack(const nvlist_t *limits, const struct group *grp, nvlist_t *nvl)
{
if (grp == NULL)
return (true);
/*
* If either name or GID is allowed, we allow it.
*/
if (!grp_allowed_group(limits, grp->gr_name, grp->gr_gid))
return (false);
if (grp_allowed_field(limits, "gr_name"))
nvlist_add_string(nvl, "gr_name", grp->gr_name);
else
nvlist_add_string(nvl, "gr_name", "");
if (grp_allowed_field(limits, "gr_passwd"))
nvlist_add_string(nvl, "gr_passwd", grp->gr_passwd);
else
nvlist_add_string(nvl, "gr_passwd", "");
if (grp_allowed_field(limits, "gr_gid"))
nvlist_add_number(nvl, "gr_gid", (uint64_t)grp->gr_gid);
else
nvlist_add_number(nvl, "gr_gid", (uint64_t)-1);
if (grp_allowed_field(limits, "gr_mem") && grp->gr_mem[0] != NULL) {
unsigned int ngroups;
for (ngroups = 0; grp->gr_mem[ngroups] != NULL; ngroups++) {
nvlist_addf_string(nvl, grp->gr_mem[ngroups],
"gr_mem[%u]", ngroups);
}
nvlist_add_number(nvl, "gr_nmem", (uint64_t)ngroups);
}
return (true);
}
static int
grp_getgrent(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
{
struct group *grp;
for (;;) {
errno = 0;
grp = getgrent();
if (errno != 0)
return (errno);
if (grp_pack(limits, grp, nvlout))
return (0);
}
/* NOTREACHED */
}
static int
grp_getgrnam(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
{
struct group *grp;
const char *name;
if (!nvlist_exists_string(nvlin, "name"))
return (EINVAL);
name = nvlist_get_string(nvlin, "name");
PJDLOG_ASSERT(name != NULL);
errno = 0;
grp = getgrnam(name);
if (errno != 0)
return (errno);
(void)grp_pack(limits, grp, nvlout);
return (0);
}
static int
grp_getgrgid(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
{
struct group *grp;
gid_t gid;
if (!nvlist_exists_number(nvlin, "gid"))
return (EINVAL);
gid = (gid_t)nvlist_get_number(nvlin, "gid");
errno = 0;
grp = getgrgid(gid);
if (errno != 0)
return (errno);
(void)grp_pack(limits, grp, nvlout);
return (0);
}
static int
grp_setgroupent(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
{
int stayopen;
if (!nvlist_exists_bool(nvlin, "stayopen"))
return (EINVAL);
stayopen = nvlist_get_bool(nvlin, "stayopen") ? 1 : 0;
return (setgroupent(stayopen) == 0 ? EFAULT : 0);
}
static int
grp_setgrent(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
{
return (setgrent() == 0 ? EFAULT : 0);
}
static int
grp_endgrent(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
{
endgrent();
return (0);
}
static int
grp_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
{
const nvlist_t *limits;
const char *name;
void *cookie;
int error, type;
if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "cmds") &&
!nvlist_exists_nvlist(newlimits, "cmds")) {
return (ENOTCAPABLE);
}
if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "fields") &&
!nvlist_exists_nvlist(newlimits, "fields")) {
return (ENOTCAPABLE);
}
if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "groups") &&
!nvlist_exists_nvlist(newlimits, "groups")) {
return (ENOTCAPABLE);
}
cookie = NULL;
while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
if (type != NV_TYPE_NVLIST)
return (EINVAL);
limits = nvlist_get_nvlist(newlimits, name);
if (strcmp(name, "cmds") == 0)
error = grp_allowed_cmds(oldlimits, limits);
else if (strcmp(name, "fields") == 0)
error = grp_allowed_fields(oldlimits, limits);
else if (strcmp(name, "groups") == 0)
error = grp_allowed_groups(oldlimits, limits);
else
error = EINVAL;
if (error != 0)
return (error);
}
return (0);
}
static int
grp_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
nvlist_t *nvlout)
{
int error;
if (!grp_allowed_cmd(limits, cmd))
return (ENOTCAPABLE);
if (strcmp(cmd, "getgrent") == 0 || strcmp(cmd, "getgrent_r") == 0)
error = grp_getgrent(limits, nvlin, nvlout);
else if (strcmp(cmd, "getgrnam") == 0 || strcmp(cmd, "getgrnam_r") == 0)
error = grp_getgrnam(limits, nvlin, nvlout);
else if (strcmp(cmd, "getgrgid") == 0 || strcmp(cmd, "getgrgid_r") == 0)
error = grp_getgrgid(limits, nvlin, nvlout);
else if (strcmp(cmd, "setgroupent") == 0)
error = grp_setgroupent(limits, nvlin, nvlout);
else if (strcmp(cmd, "setgrent") == 0)
error = grp_setgrent(limits, nvlin, nvlout);
else if (strcmp(cmd, "endgrent") == 0)
error = grp_endgrent(limits, nvlin, nvlout);
else
error = EINVAL;
return (error);
}
int
main(int argc, char *argv[])
{
return (service_start("system.grp", PARENT_FILENO, grp_limit,
grp_command, argc, argv));
}

View File

@ -0,0 +1,22 @@
# $FreeBSD$
.PATH: ${.CURDIR} ${.CURDIR}/../../../sbin/casper
PROG= pwd
SRCS= pwd.c
DPADD= ${LIBCAPSICUM} ${LIBCASPER} ${LIBNV} ${LIBPJDLOG} ${LIBUTIL}
LDADD= -lcapsicum -lcasper -lnv -lpjdlog -lutil
BINDIR= /libexec/casper
CFLAGS+=-I${.CURDIR}
CFLAGS+=-I${.CURDIR}/../../../lib/libcapsicum
CFLAGS+=-I${.CURDIR}/../../../lib/libcasper
CFLAGS+=-I${.CURDIR}/../../../lib/libpjdlog
CFLAGS+=-I${.CURDIR}/../../../sbin/casper
NO_MAN=
.include <bsd.prog.mk>

429
libexec/casper/pwd/pwd.c Normal file
View File

@ -0,0 +1,429 @@
/*-
* Copyright (c) 2013 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <errno.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include <libcapsicum.h>
#include <libcasper.h>
#include <nv.h>
#include <pjdlog.h>
static bool
pwd_allowed_cmd(const nvlist_t *limits, const char *cmd)
{
if (limits == NULL)
return (true);
/*
* If no limit was set on allowed commands, then all commands
* are allowed.
*/
if (!nvlist_exists_nvlist(limits, "cmds"))
return (true);
limits = nvlist_get_nvlist(limits, "cmds");
return (nvlist_exists_null(limits, cmd));
}
static int
pwd_allowed_cmds(const nvlist_t *oldlimits, const nvlist_t *newlimits)
{
const char *name;
void *cookie;
int type;
cookie = NULL;
while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
if (type != NV_TYPE_NULL)
return (EINVAL);
if (!pwd_allowed_cmd(oldlimits, name))
return (ENOTCAPABLE);
}
return (0);
}
static bool
pwd_allowed_user(const nvlist_t *limits, const char *uname, uid_t uid)
{
const char *name;
void *cookie;
int type;
if (limits == NULL)
return (true);
/*
* If no limit was set on allowed users, then all users are allowed.
*/
if (!nvlist_exists_nvlist(limits, "users"))
return (true);
limits = nvlist_get_nvlist(limits, "users");
cookie = NULL;
while ((name = nvlist_next(limits, &type, &cookie)) != NULL) {
switch (type) {
case NV_TYPE_NUMBER:
if (uid != (uid_t)-1 &&
nvlist_get_number(limits, name) == (uint64_t)uid) {
return (true);
}
break;
case NV_TYPE_STRING:
if (uname != NULL &&
strcmp(nvlist_get_string(limits, name),
uname) == 0) {
return (true);
}
break;
default:
PJDLOG_ABORT("Unexpected type %d.", type);
}
}
return (false);
}
static int
pwd_allowed_users(const nvlist_t *oldlimits, const nvlist_t *newlimits)
{
const char *name, *uname;
void *cookie;
uid_t uid;
int type;
cookie = NULL;
while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
switch (type) {
case NV_TYPE_NUMBER:
uid = (uid_t)nvlist_get_number(newlimits, name);
uname = NULL;
break;
case NV_TYPE_STRING:
uid = (uid_t)-1;
uname = nvlist_get_string(newlimits, name);
break;
default:
return (EINVAL);
}
if (!pwd_allowed_user(oldlimits, uname, uid))
return (ENOTCAPABLE);
}
return (0);
}
static bool
pwd_allowed_field(const nvlist_t *limits, const char *field)
{
if (limits == NULL)
return (true);
/*
* If no limit was set on allowed fields, then all fields are allowed.
*/
if (!nvlist_exists_nvlist(limits, "fields"))
return (true);
limits = nvlist_get_nvlist(limits, "fields");
return (nvlist_exists_null(limits, field));
}
static int
pwd_allowed_fields(const nvlist_t *oldlimits, const nvlist_t *newlimits)
{
const char *name;
void *cookie;
int type;
cookie = NULL;
while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
if (type != NV_TYPE_NULL)
return (EINVAL);
if (!pwd_allowed_field(oldlimits, name))
return (ENOTCAPABLE);
}
return (0);
}
static bool
pwd_pack(const nvlist_t *limits, const struct passwd *pwd, nvlist_t *nvl)
{
int fields;
if (pwd == NULL)
return (true);
/*
* If either name or UID is allowed, we allow it.
*/
if (!pwd_allowed_user(limits, pwd->pw_name, pwd->pw_uid))
return (false);
fields = pwd->pw_fields;
if (pwd_allowed_field(limits, "pw_name")) {
nvlist_add_string(nvl, "pw_name", pwd->pw_name);
} else {
nvlist_add_string(nvl, "pw_name", "");
fields &= ~_PWF_NAME;
}
if (pwd_allowed_field(limits, "pw_uid")) {
nvlist_add_number(nvl, "pw_uid", (uint64_t)pwd->pw_uid);
} else {
nvlist_add_number(nvl, "pw_uid", (uint64_t)-1);
fields &= ~_PWF_UID;
}
if (pwd_allowed_field(limits, "pw_gid")) {
nvlist_add_number(nvl, "pw_gid", (uint64_t)pwd->pw_gid);
} else {
nvlist_add_number(nvl, "pw_gid", (uint64_t)-1);
fields &= ~_PWF_GID;
}
if (pwd_allowed_field(limits, "pw_change")) {
nvlist_add_number(nvl, "pw_change", (uint64_t)pwd->pw_change);
} else {
nvlist_add_number(nvl, "pw_change", (uint64_t)0);
fields &= ~_PWF_CHANGE;
}
if (pwd_allowed_field(limits, "pw_passwd")) {
nvlist_add_string(nvl, "pw_passwd", pwd->pw_passwd);
} else {
nvlist_add_string(nvl, "pw_passwd", "");
fields &= ~_PWF_PASSWD;
}
if (pwd_allowed_field(limits, "pw_class")) {
nvlist_add_string(nvl, "pw_class", pwd->pw_class);
} else {
nvlist_add_string(nvl, "pw_class", "");
fields &= ~_PWF_CLASS;
}
if (pwd_allowed_field(limits, "pw_gecos")) {
nvlist_add_string(nvl, "pw_gecos", pwd->pw_gecos);
} else {
nvlist_add_string(nvl, "pw_gecos", "");
fields &= ~_PWF_GECOS;
}
if (pwd_allowed_field(limits, "pw_dir")) {
nvlist_add_string(nvl, "pw_dir", pwd->pw_dir);
} else {
nvlist_add_string(nvl, "pw_dir", "");
fields &= ~_PWF_DIR;
}
if (pwd_allowed_field(limits, "pw_shell")) {
nvlist_add_string(nvl, "pw_shell", pwd->pw_shell);
} else {
nvlist_add_string(nvl, "pw_shell", "");
fields &= ~_PWF_SHELL;
}
if (pwd_allowed_field(limits, "pw_expire")) {
nvlist_add_number(nvl, "pw_expire", (uint64_t)pwd->pw_expire);
} else {
nvlist_add_number(nvl, "pw_expire", (uint64_t)0);
fields &= ~_PWF_EXPIRE;
}
nvlist_add_number(nvl, "pw_fields", (uint64_t)fields);
return (true);
}
static int
pwd_getpwent(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
{
struct passwd *pwd;
for (;;) {
errno = 0;
pwd = getpwent();
if (errno != 0)
return (errno);
if (pwd_pack(limits, pwd, nvlout))
return (0);
}
/* NOTREACHED */
}
static int
pwd_getpwnam(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
{
struct passwd *pwd;
const char *name;
if (!nvlist_exists_string(nvlin, "name"))
return (EINVAL);
name = nvlist_get_string(nvlin, "name");
PJDLOG_ASSERT(name != NULL);
errno = 0;
pwd = getpwnam(name);
if (errno != 0)
return (errno);
(void)pwd_pack(limits, pwd, nvlout);
return (0);
}
static int
pwd_getpwuid(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
{
struct passwd *pwd;
uid_t uid;
if (!nvlist_exists_number(nvlin, "uid"))
return (EINVAL);
uid = (uid_t)nvlist_get_number(nvlin, "uid");
errno = 0;
pwd = getpwuid(uid);
if (errno != 0)
return (errno);
(void)pwd_pack(limits, pwd, nvlout);
return (0);
}
static int
pwd_setpassent(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
{
int stayopen;
if (!nvlist_exists_bool(nvlin, "stayopen"))
return (EINVAL);
stayopen = nvlist_get_bool(nvlin, "stayopen") ? 1 : 0;
return (setpassent(stayopen) == 0 ? EFAULT : 0);
}
static int
pwd_setpwent(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
{
setpwent();
return (0);
}
static int
pwd_endpwent(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
{
endpwent();
return (0);
}
static int
pwd_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
{
const nvlist_t *limits;
const char *name;
void *cookie;
int error, type;
if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "cmds") &&
!nvlist_exists_nvlist(newlimits, "cmds")) {
return (ENOTCAPABLE);
}
if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "fields") &&
!nvlist_exists_nvlist(newlimits, "fields")) {
return (ENOTCAPABLE);
}
if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "users") &&
!nvlist_exists_nvlist(newlimits, "users")) {
return (ENOTCAPABLE);
}
cookie = NULL;
while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
if (type != NV_TYPE_NVLIST)
return (EINVAL);
limits = nvlist_get_nvlist(newlimits, name);
if (strcmp(name, "cmds") == 0)
error = pwd_allowed_cmds(oldlimits, limits);
else if (strcmp(name, "fields") == 0)
error = pwd_allowed_fields(oldlimits, limits);
else if (strcmp(name, "users") == 0)
error = pwd_allowed_users(oldlimits, limits);
else
error = EINVAL;
if (error != 0)
return (error);
}
return (0);
}
static int
pwd_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
nvlist_t *nvlout)
{
int error;
if (!pwd_allowed_cmd(limits, cmd))
return (ENOTCAPABLE);
if (strcmp(cmd, "getpwent") == 0 || strcmp(cmd, "getpwent_r") == 0)
error = pwd_getpwent(limits, nvlin, nvlout);
else if (strcmp(cmd, "getpwnam") == 0 || strcmp(cmd, "getpwnam_r") == 0)
error = pwd_getpwnam(limits, nvlin, nvlout);
else if (strcmp(cmd, "getpwuid") == 0 || strcmp(cmd, "getpwuid_r") == 0)
error = pwd_getpwuid(limits, nvlin, nvlout);
else if (strcmp(cmd, "setpassent") == 0)
error = pwd_setpassent(limits, nvlin, nvlout);
else if (strcmp(cmd, "setpwent") == 0)
error = pwd_setpwent(limits, nvlin, nvlout);
else if (strcmp(cmd, "endpwent") == 0)
error = pwd_endpwent(limits, nvlin, nvlout);
else
error = EINVAL;
return (error);
}
int
main(int argc, char *argv[])
{
return (service_start("system.pwd", PARENT_FILENO, pwd_limit,
pwd_command, argc, argv));
}

View File

@ -0,0 +1,22 @@
# $FreeBSD$
.PATH: ${.CURDIR} ${.CURDIR}/../../../sbin/casper
PROG= random
SRCS= random.c
DPADD= ${LIBCAPSICUM} ${LIBCASPER} ${LIBNV} ${LIBPJDLOG} ${LIBUTIL}
LDADD= -lcapsicum -lcasper -lnv -lpjdlog -lutil
BINDIR= /libexec/casper
CFLAGS+=-I${.CURDIR}
CFLAGS+=-I${.CURDIR}/../../../lib/libcapsicum
CFLAGS+=-I${.CURDIR}/../../../lib/libcasper
CFLAGS+=-I${.CURDIR}/../../../lib/libpjdlog
CFLAGS+=-I${.CURDIR}/../../../sbin/casper
NO_MAN=
.include <bsd.prog.mk>

View File

@ -0,0 +1,81 @@
/*-
* Copyright (c) 2012-2013 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <libcapsicum.h>
#include <libcasper.h>
#include <nv.h>
#include <pjdlog.h>
#define MAXSIZE (1024 * 1024)
static int
random_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
nvlist_t *nvlout)
{
void *data;
size_t size;
if (strcmp(cmd, "generate") != 0)
return (EINVAL);
if (!nvlist_exists_number(nvlin, "size"))
return (EINVAL);
size = (size_t)nvlist_get_number(nvlin, "size");
if (size == 0 || size > MAXSIZE)
return (EINVAL);
data = malloc(size);
if (data == NULL)
return (ENOMEM);
arc4random_buf(data, size);
nvlist_move_binary(nvlout, "data", data, size);
return (0);
}
int
main(int argc, char *argv[])
{
/*
* TODO: Sandbox this.
*/
return (service_start("system.random", PARENT_FILENO, NULL,
random_command, argc, argv));
}

View File

@ -0,0 +1,22 @@
# $FreeBSD$
.PATH: ${.CURDIR} ${.CURDIR}/../../../sbin/casper
PROG= sysctl
SRCS= sysctl.c
DPADD= ${LIBCAPSICUM} ${LIBCASPER} ${LIBNV} ${LIBPJDLOG} ${LIBUTIL}
LDADD= -lcapsicum -lcasper -lnv -lpjdlog -lutil
BINDIR= /libexec/casper
CFLAGS+=-I${.CURDIR}
CFLAGS+=-I${.CURDIR}/../../../lib/libcapsicum
CFLAGS+=-I${.CURDIR}/../../../lib/libcasper
CFLAGS+=-I${.CURDIR}/../../../lib/libpjdlog
CFLAGS+=-I${.CURDIR}/../../../sbin/casper
NO_MAN=
.include <bsd.prog.mk>

View File

@ -0,0 +1,249 @@
/*-
* Copyright (c) 2013 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/sysctl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <libcapsicum.h>
#include <libcapsicum_sysctl.h>
#include <libcasper.h>
#include <nv.h>
#include <pjdlog.h>
static int
sysctl_check_one(const nvlist_t *nvl, bool islimit)
{
const char *name;
void *cookie;
int type;
unsigned int fields;
/* NULL nvl is of course invalid. */
if (nvl == NULL)
return (EINVAL);
if (nvlist_error(nvl) != 0)
return (nvlist_error(nvl));
#define HAS_NAME 0x01
#define HAS_OPERATION 0x02
fields = 0;
cookie = NULL;
while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) {
/* We accept only one 'name' and one 'operation' in nvl. */
if (strcmp(name, "name") == 0) {
if (type != NV_TYPE_STRING)
return (EINVAL);
/* Only one 'name' can be present. */
if ((fields & HAS_NAME) != 0)
return (EINVAL);
fields |= HAS_NAME;
} else if (strcmp(name, "operation") == 0) {
uint64_t operation;
if (type != NV_TYPE_NUMBER)
return (EINVAL);
/*
* We accept only CAP_SYSCTL_READ and
* CAP_SYSCTL_WRITE flags.
*/
operation = nvlist_get_number(nvl, name);
if ((operation & ~(CAP_SYSCTL_RDWR)) != 0)
return (EINVAL);
/* ...but there has to be at least one of them. */
if ((operation & (CAP_SYSCTL_RDWR)) == 0)
return (EINVAL);
/* Only one 'operation' can be present. */
if ((fields & HAS_OPERATION) != 0)
return (EINVAL);
fields |= HAS_OPERATION;
} else if (islimit) {
/* If this is limit, there can be no other fields. */
return (EINVAL);
}
}
/* Both fields has to be there. */
if (fields != (HAS_NAME | HAS_OPERATION))
return (EINVAL);
#undef HAS_OPERATION
#undef HAS_NAME
return (0);
}
static bool
sysctl_allowed(const nvlist_t *limits, const char *chname, uint64_t choperation)
{
uint64_t operation;
const char *name;
void *cookie;
int type;
if (limits == NULL)
return (true);
cookie = NULL;
while ((name = nvlist_next(limits, &type, &cookie)) != NULL) {
PJDLOG_ASSERT(type == NV_TYPE_NUMBER);
operation = nvlist_get_number(limits, name);
if ((operation & choperation) != choperation)
continue;
if ((operation & CAP_SYSCTL_RECURSIVE) == 0) {
if (strcmp(name, chname) != 0)
continue;
} else {
size_t namelen;
namelen = strlen(name);
if (strncmp(name, chname, namelen) != 0)
continue;
if (chname[namelen] != '.' && chname[namelen] != '\0')
continue;
}
return (true);
}
return (false);
}
static int
sysctl_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
{
const nvlist_t *nvl;
const char *name;
void *cookie;
uint64_t operation;
int error, type;
cookie = NULL;
while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
if (type != NV_TYPE_NUMBER)
return (EINVAL);
operation = nvlist_get_number(newlimits, name);
if ((operation & ~(CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE)) != 0)
return (EINVAL);
if ((operation & (CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE)) == 0)
return (EINVAL);
if (!sysctl_allowed(oldlimits, name, operation))
return (ENOTCAPABLE);
}
return (0);
}
static int
sysctl_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
nvlist_t *nvlout)
{
const char *name;
const void *newp;
void *oldp;
uint64_t operation;
size_t oldlen, newlen;
size_t *oldlenp;
int error;
if (strcmp(cmd, "sysctl") != 0)
return (EINVAL);
error = sysctl_check_one(nvlin, false);
if (error != 0)
return (error);
name = nvlist_get_string(nvlin, "name");
operation = nvlist_get_number(nvlin, "operation");
if (!sysctl_allowed(limits, name, operation))
return (ENOTCAPABLE);
if ((operation & CAP_SYSCTL_WRITE) != 0) {
if (!nvlist_exists_binary(nvlin, "newp"))
return (EINVAL);
newp = nvlist_get_binary(nvlin, "newp", &newlen);
PJDLOG_ASSERT(newp != NULL && newlen > 0);
} else {
newp = NULL;
newlen = 0;
}
if ((operation & CAP_SYSCTL_READ) != 0) {
if (nvlist_exists_null(nvlin, "justsize")) {
oldp = NULL;
oldlen = 0;
oldlenp = &oldlen;
} else {
if (!nvlist_exists_number(nvlin, "oldlen"))
return (EINVAL);
oldlen = (size_t)nvlist_get_number(nvlin, "oldlen");
if (oldlen == 0)
return (EINVAL);
oldp = calloc(1, oldlen);
if (oldp == NULL)
return (ENOMEM);
oldlenp = &oldlen;
}
} else {
oldp = NULL;
oldlen = 0;
oldlenp = NULL;
}
if (sysctlbyname(name, oldp, oldlenp, newp, newlen) == -1) {
error = errno;
free(oldp);
return (error);
}
if ((operation & CAP_SYSCTL_READ) != 0) {
if (nvlist_exists_null(nvlin, "justsize"))
nvlist_add_number(nvlout, "oldlen", (uint64_t)oldlen);
else
nvlist_move_binary(nvlout, "oldp", oldp, oldlen);
}
return (0);
}
int
main(int argc, char *argv[])
{
return (service_start("system.sysctl", PARENT_FILENO, sysctl_limit,
sysctl_command, argc, argv));
}

View File

@ -78,6 +78,10 @@ SUBDIR=adjkerntz \
SUBDIR+= atm
.endif
.if ${MK_CASPER} != "no"
SUBDIR+= casperd
.endif
.if ${MK_CXX} != "no"
SUBDIR+= devd
.endif

18
sbin/casperd/Makefile Normal file
View File

@ -0,0 +1,18 @@
# $FreeBSD$
PROG= casperd
SRCS= casperd.c zygote.c
DPADD= ${LIBCAPSICUM} ${LIBCASPER} ${LIBNV} ${LIBPJDLOG} ${LIBUTIL}
LDADD= -lcapsicum -lcasper -lnv -lpjdlog -lutil
MAN= casperd.8
CFLAGS+=-I${.CURDIR}
CFLAGS+=-I${.CURDIR}/../../lib/libcapsicum
CFLAGS+=-I${.CURDIR}/../../lib/libcasper
CFLAGS+=-I${.CURDIR}/../../lib/libnv
CFLAGS+=-I${.CURDIR}/../../lib/libpjdlog
.include <bsd.prog.mk>

124
sbin/casperd/casperd.8 Normal file
View File

@ -0,0 +1,124 @@
.\" Copyright (c) 2013 The FreeBSD Foundation
.\" All rights reserved.
.\"
.\" This documentation was written by Pawel Jakub Dawidek under sponsorship
.\" from the FreeBSD Foundation.
.\"
.\" 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 October 26, 2013
.Dt CASPERD 8
.Os
.Sh NAME
.Nm casperd
.Nd "Capability Services friendly daemon"
.Sh SYNOPSIS
.Nm
[-Fhv] [-D servconfdir] [-P pidfile] [-S sockpath]
.Op Fl Fhv
.Op Fl D Ar servconfdir
.Op Fl P Ar pidfile
.Op Fl S Ar sockpath
.Sh DESCRIPTION
The
.Nm
daemon provides various services in capability-like fashion to programs
running in capability mode sandbox.
For example it is prohibited to send UDP packets to arbitrary destinations
when operating in capability mode, which makes DNS resolution impossible.
To make it possible the
.Nm
daemon provides
.Nm system.dns
service that allows to proxy DNS resolution requests through dedicated,
non-sandboxed process provided by
.Nm .
.Pp
The
.Nm
daemon can be started with the following command line arguments:
.Bl -tag -width ".Fl D Ar servconfdir"
.It Fl D Ar servconfdir
Specify alternative location of the service configuration directory.
The default location is
.Pa /etc/casper/ .
.It Fl F
Start the
.Nm
daemon in the foreground.
By default
.Nm
starts in the background.
.It Fl h
Print the
.Nm
usage message.
.It Fl P Ar pidfile
Specify alternative location of a file where main process PID will be
stored.
The default location is
.Pa /var/run/casperd.pid .
.It Fl S Ar sockpath
Specify alternative location of a
.Xr unix 4
domain socket that can be used to connect to the
.Nm
daemon.
The default location is
.Pa /var/run/casper .
.It Fl v
Print or log verbose/debugging informations.
This option can be specified multiple times to raise the verbosity
level.
.El
.Sh FILES
.Bl -tag -width ".Pa /var/run/casperd.pid" -compact
.It Pa /etc/casper/
The configuration directory for
.Nm
services.
.It Pa /var/run/casper
.Xr unix 4
domain socket used to connect to the
.Nm
daemon.
.It Pa /var/run/casperd.pid
The default location of the
.Nm
PID file.
.El
.Sh EXIT STATUS
Exit status is 0 on success, and > 0 of an error occurs.
.Sh SEE ALSO
.Xr cap_enter 2 ,
.Xr libcapsicum 3 ,
.Xr pidfile 3 ,
.Xr capsicum 4 ,
.Xr unix 4
.Sh AUTHORS
The
.Nm
was implemented by
.An Pawel Jakub Dawidek Aq pawel@dawidek.net
under sponsorship from the FreeBSD Foundation.

711
sbin/casperd/casperd.c Normal file
View File

@ -0,0 +1,711 @@
/*-
* Copyright (c) 2012 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/capability.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <assert.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <libutil.h>
#include <paths.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <libcapsicum.h>
#include <libcapsicum_impl.h>
#include <libcasper.h>
#include <libcasper_impl.h>
#include <msgio.h>
#include <nv.h>
#include <pjdlog.h>
#include "msgio.h"
#include "zygote.h"
#define CASPERD_PIDFILE "/var/run/casperd.pid"
#define CASPERD_SERVCONFDIR "/etc/casper"
#define CASPERD_SOCKPATH "/var/run/casper"
typedef void service_function_t(struct service_connection *, const nvlist_t *,
nvlist_t *);
struct casper_service {
const char *cs_name;
const char *cs_execpath;
struct service *cs_service;
nvlist_t *cs_attrs;
TAILQ_ENTRY(casper_service) cs_next;
};
static TAILQ_HEAD(, casper_service) casper_services =
TAILQ_HEAD_INITIALIZER(casper_services);
#define SERVICE_IS_CORE(service) ((service)->cs_execpath == NULL)
static void service_external_execute(int chanfd);
#define KEEP_ERRNO(work) do { \
int _serrno; \
\
_serrno = errno; \
work; \
errno = _serrno; \
} while (0)
static struct casper_service *
service_find(const char *name)
{
struct casper_service *casserv;
TAILQ_FOREACH(casserv, &casper_services, cs_next) {
if (strcmp(casserv->cs_name, name) == 0)
break;
}
return (casserv);
}
/*
* Function always consumes the given attrs.
*/
static void
service_register(nvlist_t *attrs)
{
struct casper_service *casserv;
const char *name;
PJDLOG_ASSERT(nvlist_exists_string(attrs, "name"));
PJDLOG_ASSERT(nvlist_exists_string(attrs, "execpath") ||
(nvlist_exists_number(attrs, "commandfunc") &&
nvlist_exists_number(attrs, "limitfunc")));
name = nvlist_get_string(attrs, "name");
PJDLOG_ASSERT(name != NULL);
if (name[0] == '\0') {
pjdlog_error("Unable to register service with an empty name.");
nvlist_destroy(attrs);
return;
}
if (service_find(name) != NULL) {
pjdlog_error("Service \"%s\" is already registered.", name);
nvlist_destroy(attrs);
return;
}
casserv = malloc(sizeof(*casserv));
if (casserv == NULL) {
pjdlog_errno(LOG_ERR, "Unable to register service \"%s\"",
name);
nvlist_destroy(attrs);
return;
}
casserv->cs_name = name;
if (nvlist_exists_string(attrs, "execpath")) {
struct stat sb;
PJDLOG_ASSERT(!nvlist_exists_number(attrs, "commandfunc"));
PJDLOG_ASSERT(!nvlist_exists_number(attrs, "limitfunc"));
casserv->cs_service = NULL;
casserv->cs_execpath = nvlist_get_string(attrs, "execpath");
if (casserv->cs_execpath == NULL ||
casserv->cs_execpath[0] == '\0') {
pjdlog_error("Unable to register service with an empty execpath.");
free(casserv);
nvlist_destroy(attrs);
return;
}
if (stat(casserv->cs_execpath, &sb) == -1) {
pjdlog_errno(LOG_ERR,
"Unable to register service \"%s\", problem with executable \"%s\"",
name, casserv->cs_execpath);
free(casserv);
nvlist_destroy(attrs);
return;
}
} else /* if (nvlist_exists_number(attrs, "commandfunc")) */ {
PJDLOG_ASSERT(!nvlist_exists_string(attrs, "execpath"));
casserv->cs_execpath = NULL;
casserv->cs_service = service_alloc(name,
(void *)(uintptr_t)nvlist_get_number(attrs, "limitfunc"),
(void *)(uintptr_t)nvlist_get_number(attrs, "commandfunc"));
if (casserv->cs_service == NULL) {
pjdlog_errno(LOG_ERR,
"Unable to register service \"%s\"", name);
free(casserv);
nvlist_destroy(attrs);
return;
}
}
casserv->cs_attrs = attrs;
TAILQ_INSERT_TAIL(&casper_services, casserv, cs_next);
pjdlog_debug(1, "Service %s successfully registered.",
casserv->cs_name);
}
static bool
casper_allowed_service(const nvlist_t *limits, const char *service)
{
if (limits == NULL)
return (true);
if (nvlist_exists_null(limits, service))
return (true);
return (false);
}
static int
casper_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
{
const char *name;
int type;
void *cookie;
while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
if (type != NV_TYPE_NULL)
return (EINVAL);
if (!casper_allowed_service(oldlimits, name))
return (ENOTCAPABLE);
}
return (0);
}
static int
casper_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
nvlist_t *nvlout)
{
struct casper_service *casserv;
const char *servname;
nvlist_t *nvl;
int chanfd, execfd, procfd, error;
if (strcmp(cmd, "open") != 0)
return (EINVAL);
if (!nvlist_exists_string(nvlin, "service"))
return (EINVAL);
servname = nvlist_get_string(nvlin, "service");
casserv = service_find(servname);
if (casserv == NULL)
return (ENOENT);
if (!casper_allowed_service(limits, servname))
return (ENOTCAPABLE);
#ifdef O_EXEC_WORKING
execfd = open(casserv->cs_execpath, O_EXEC);
#else
execfd = open(casserv->cs_execpath, O_RDONLY);
#endif
if (execfd < -1) {
error = errno;
pjdlog_errno(LOG_ERR,
"Unable to open executable '%s' of service '%s'",
casserv->cs_execpath, casserv->cs_name);
return (error);
}
if (zygote_clone(service_external_execute, 0, &chanfd, &procfd) == -1) {
error = errno;
close(execfd);
return (error);
}
nvl = nvlist_create(0);
nvlist_add_string(nvl, "service", casserv->cs_name);
if (nvlist_exists_descriptor(nvlin, "stderrfd")) {
nvlist_move_descriptor(nvl, "stderrfd",
nvlist_take_descriptor(nvlin, "stderrfd"));
}
nvlist_move_descriptor(nvl, "execfd", execfd);
nvlist_move_descriptor(nvl, "procfd", procfd);
if (nvlist_send(chanfd, nvl) == -1) {
error = errno;
pjdlog_errno(LOG_ERR, "Unable to send nvlist");
nvlist_destroy(nvl);
close(chanfd);
return (error);
}
nvlist_destroy(nvl);
nvlist_move_descriptor(nvlout, "chanfd", chanfd);
return (0);
}
static void
fdswap(int *fd0, int *fd1)
{
int tmpfd;
PJDLOG_VERIFY((tmpfd = dup(*fd0)) != -1);
PJDLOG_VERIFY(dup2(*fd1, *fd0) != -1);
PJDLOG_VERIFY(dup2(tmpfd, *fd1) != -1);
close(tmpfd);
tmpfd = *fd0;
*fd0 = *fd1;
*fd1 = tmpfd;
}
static void
fdmove(int *oldfdp, int newfd)
{
if (*oldfdp != newfd) {
PJDLOG_VERIFY(dup2(*oldfdp, newfd) != -1);
close(*oldfdp);
*oldfdp = newfd;
}
}
static void
fdcloexec(int fd)
{
int flags;
flags = fcntl(fd, F_GETFD);
PJDLOG_ASSERT(flags != -1);
if ((flags & FD_CLOEXEC) != 0)
PJDLOG_VERIFY(fcntl(fd, F_SETFD, flags & ~FD_CLOEXEC) != -1);
}
static void
service_register_core(void)
{
nvlist_t *nvl;
nvl = nvlist_create(0);
nvlist_add_string(nvl, "name", "core.casper");
nvlist_add_number(nvl, "limitfunc", (uint64_t)(uintptr_t)casper_limit);
nvlist_add_number(nvl, "commandfunc",
(uint64_t)(uintptr_t)casper_command);
service_register(nvl);
}
static int
setup_creds(int sock)
{
struct cmsgcred cred;
if (cred_recv(sock, &cred) == -1)
return (-1);
if (setgroups((int)cred.cmcred_ngroups, cred.cmcred_groups) == -1)
return (-1);
if (setgid(cred.cmcred_groups[0]) == -1)
return (-1);
if (setuid(cred.cmcred_euid) == -1)
return (-1);
return (0);
}
static void
service_external_execute(int chanfd)
{
char *service, *argv[3];
int stderrfd, execfd, procfd;
nvlist_t *nvl;
nvl = nvlist_recv(chanfd);
if (nvl == NULL)
pjdlog_exit(1, "Unable to receive nvlist");
service = nvlist_take_string(nvl, "service");
PJDLOG_ASSERT(service != NULL);
if (nvlist_exists_descriptor(nvl, "stderrfd")) {
stderrfd = nvlist_take_descriptor(nvl, "stderrfd");
} else {
stderrfd = open(_PATH_DEVNULL, O_RDWR);
if (stderrfd < 0)
pjdlog_exit(1, "Unable to open %s", _PATH_DEVNULL);
}
execfd = nvlist_take_descriptor(nvl, "execfd");
procfd = nvlist_take_descriptor(nvl, "procfd");
nvlist_destroy(nvl);
/*
* Move all descriptors into right slots.
*/
if (stderrfd != STDERR_FILENO) {
if (chanfd == STDERR_FILENO)
fdswap(&stderrfd, &chanfd);
else if (execfd == STDERR_FILENO)
fdswap(&stderrfd, &execfd);
else if (procfd == STDERR_FILENO)
fdswap(&stderrfd, &procfd);
fdmove(&stderrfd, STDERR_FILENO);
}
fdcloexec(stderrfd);
if (chanfd != PARENT_FILENO) {
if (execfd == PARENT_FILENO)
fdswap(&chanfd, &execfd);
else if (procfd == PARENT_FILENO)
fdswap(&chanfd, &procfd);
fdmove(&chanfd, PARENT_FILENO);
}
fdcloexec(chanfd);
if (execfd != EXECUTABLE_FILENO) {
if (procfd == EXECUTABLE_FILENO)
fdswap(&execfd, &procfd);
fdmove(&execfd, EXECUTABLE_FILENO);
}
fdcloexec(execfd);
if (procfd != PROC_FILENO)
fdmove(&procfd, PROC_FILENO);
fdcloexec(procfd);
/*
* Use credentials of the caller process.
*/
setup_creds(chanfd);
argv[0] = service;
asprintf(&argv[1], "%d", pjdlog_debug_get());
argv[2] = NULL;
fexecve(execfd, argv, NULL);
pjdlog_exit(1, "Unable to execute service %s", service);
}
static void
service_register_external_one(const char *dirpath, int dfd,
const char *filename)
{
char execpath[FILENAME_MAX];
nvlist_t *nvl;
ssize_t done;
int fd;
fd = openat(dfd, filename, O_RDONLY);
if (fd == -1) {
pjdlog_errno(LOG_ERR, "Unable to open \"%s/%s\"", dirpath,
filename);
return;
}
done = read(fd, execpath, sizeof(execpath));
if (done == -1) {
pjdlog_errno(LOG_ERR, "Unable to read content of \"%s/%s\"",
dirpath, filename);
close(fd);
return;
}
close(fd);
if (done == sizeof(execpath)) {
pjdlog_error("Executable path too long in \"%s/%s\".", dirpath,
filename);
return;
}
execpath[done] = '\0';
while (done > 0) {
if (execpath[--done] == '\n')
execpath[done] = '\0';
}
nvl = nvlist_create(0);
nvlist_add_string(nvl, "name", filename);
nvlist_add_string(nvl, "execpath", execpath);
if (nvlist_error(nvl) != 0) {
pjdlog_common(LOG_ERR, 0, nvlist_error(nvl),
"Unable to allocate attributes for service \"%s/%s\"",
dirpath, filename);
nvlist_destroy(nvl);
return;
}
service_register(nvl);
/* service_register() consumed nvl. */
}
static uint8_t
file_type(int dfd, const char *filename)
{
struct stat sb;
if (fstatat(dfd, filename, &sb, AT_SYMLINK_NOFOLLOW) == -1) {
pjdlog_errno(LOG_ERR, "Unable to stat \"%s\"", filename);
return (DT_UNKNOWN);
}
return (IFTODT(sb.st_mode));
}
static void
service_register_external(const char *dirpath)
{
DIR *dirp;
struct dirent *dp;
int dfd;
dirp = opendir(dirpath);
if (dirp == NULL) {
pjdlog_errno(LOG_WARNING, "Unable to open \"%s\"", dirpath);
return;
}
dfd = dirfd(dirp);
PJDLOG_ASSERT(dfd >= 0);
while ((dp = readdir(dirp)) != NULL) {
dp->d_type = file_type(dfd, dp->d_name);
/* We are only interested in regular files, skip the rest. */
if (dp->d_type != DT_REG) {
pjdlog_debug(1,
"File \"%s/%s\" is not a regular file, skipping.",
dirpath, dp->d_name);
continue;
}
service_register_external_one(dirpath, dfd, dp->d_name);
}
closedir(dirp);
}
static void
casper_accept(int lsock)
{
struct casper_service *casserv;
struct service_connection *sconn;
int sock;
sock = accept(lsock, NULL, NULL);
if (sock == -1) {
pjdlog_errno(LOG_ERR, "Unable to accept casper connection");
return;
}
casserv = service_find("core.casper");
PJDLOG_ASSERT(casserv != NULL);
sconn = service_connection_add(casserv->cs_service, sock, NULL);
if (sconn == NULL) {
close(sock);
return;
}
}
static void
main_loop(const char *sockpath, struct pidfh *pfh)
{
fd_set fds;
struct sockaddr_un sun;
struct casper_service *casserv;
struct service_connection *sconn, *sconntmp;
int lsock, sock, maxfd, ret;
lsock = socket(AF_UNIX, SOCK_STREAM, 0);
if (lsock == -1)
pjdlog_exit(1, "Unable to create socket");
(void)unlink(sockpath);
bzero(&sun, sizeof(sun));
sun.sun_family = AF_UNIX;
PJDLOG_VERIFY(strlcpy(sun.sun_path, sockpath, sizeof(sun.sun_path)) <
sizeof(sun.sun_path));
sun.sun_len = SUN_LEN(&sun);
if (bind(lsock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
pjdlog_exit(1, "Unable to bind to %s", sockpath);
if (listen(lsock, 8) == -1)
pjdlog_exit(1, "Unable to listen on %s", sockpath);
for (;;) {
FD_ZERO(&fds);
FD_SET(lsock, &fds);
maxfd = lsock;
TAILQ_FOREACH(casserv, &casper_services, cs_next) {
/* We handle only core services. */
if (!SERVICE_IS_CORE(casserv))
continue;
for (sconn = service_connection_first(casserv->cs_service);
sconn != NULL;
sconn = service_connection_next(sconn)) {
sock = service_connection_get_sock(sconn);
FD_SET(sock, &fds);
maxfd = sock > maxfd ? sock : maxfd;
}
}
maxfd++;
PJDLOG_ASSERT(maxfd <= (int)FD_SETSIZE);
ret = select(maxfd, &fds, NULL, NULL, NULL);
PJDLOG_ASSERT(ret == -1 || ret > 0); /* select() cannot timeout */
if (ret == -1) {
if (errno == EINTR)
continue;
KEEP_ERRNO((void)pidfile_remove(pfh));
pjdlog_exit(1, "select() failed");
}
if (FD_ISSET(lsock, &fds))
casper_accept(lsock);
TAILQ_FOREACH(casserv, &casper_services, cs_next) {
/* We handle only core services. */
if (!SERVICE_IS_CORE(casserv))
continue;
for (sconn = service_connection_first(casserv->cs_service);
sconn != NULL; sconn = sconntmp) {
/*
* Prepare for connection to be removed from
* the list on failure.
*/
sconntmp = service_connection_next(sconn);
sock = service_connection_get_sock(sconn);
if (FD_ISSET(sock, &fds)) {
service_message(casserv->cs_service,
sconn);
}
}
}
}
}
static void
usage(void)
{
pjdlog_exitx(1,
"usage: casperd [-Fhv] [-D servconfdir] [-P pidfile] [-S sockpath]");
}
int
main(int argc, char *argv[])
{
struct pidfh *pfh;
const char *pidfile, *servconfdir, *sockpath;
pid_t otherpid;
int ch, debug;
bool foreground;
pjdlog_init(PJDLOG_MODE_STD);
debug = 0;
foreground = false;
pidfile = CASPERD_PIDFILE;
servconfdir = CASPERD_SERVCONFDIR;
sockpath = CASPERD_SOCKPATH;
while ((ch = getopt(argc, argv, "D:FhP:S:v")) != -1) {
switch (ch) {
case 'D':
servconfdir = optarg;
break;
case 'F':
foreground = true;
break;
case 'P':
pidfile = optarg;
break;
case 'S':
sockpath = optarg;
break;
case 'v':
debug++;
break;
case 'h':
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc != 0)
usage();
if (!foreground)
pjdlog_mode_set(PJDLOG_MODE_SYSLOG);
pjdlog_prefix_set("(casperd) ");
pjdlog_debug_set(debug);
if (zygote_init() < 0)
pjdlog_exit(1, "Unable to create zygote process");
pfh = pidfile_open(pidfile, 0600, &otherpid);
if (pfh == NULL) {
if (errno == EEXIST) {
pjdlog_exitx(1, "casperd already running, pid: %jd.",
(intmax_t)otherpid);
}
pjdlog_errno(LOG_WARNING, "Cannot open or create pidfile %s",
pidfile);
}
if (!foreground) {
if (daemon(0, 0) == -1) {
KEEP_ERRNO((void)pidfile_remove(pfh));
pjdlog_exit(1, "Unable to go into background");
}
}
/* Write PID to a file. */
if (pidfile_write(pfh) == -1) {
pjdlog_errno(LOG_WARNING, "Unable to write to pidfile %s",
pidfile);
} else {
pjdlog_debug(1, "PID stored in %s.", pidfile);
}
/*
* Register core services.
*/
service_register_core();
/*
* Register external services.
*/
service_register_external(servconfdir);
/*
* Wait for connections.
*/
main_loop(sockpath, pfh);
}

219
sbin/casperd/zygote.c Normal file
View File

@ -0,0 +1,219 @@
/*-
* Copyright (c) 2012 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/capability.h>
#include <sys/procdesc.h>
#include <sys/socket.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <paths.h>
#include <stdbool.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <libcapsicum.h>
#include <libcapsicum_impl.h>
#include <nv.h>
#include <pjdlog.h>
#include "zygote.h"
/* Zygote info. */
static int zygote_sock = -1;
static void
stdnull(void)
{
int fd;
fd = open(_PATH_DEVNULL, O_RDWR);
if (fd == -1)
errx(1, "Unable to open %s", _PATH_DEVNULL);
if (dup2(fd, STDIN_FILENO) == -1)
errx(1, "Unable to cover stdin");
if (dup2(fd, STDOUT_FILENO) == -1)
errx(1, "Unable to cover stdout");
if (dup2(fd, STDERR_FILENO) == -1)
errx(1, "Unable to cover stderr");
close(fd);
}
int
zygote_clone(zygote_func_t *func, int flags, int *chanfdp, int *procfdp)
{
nvlist_t *nvl;
int error;
if (zygote_sock == -1) {
/* Zygote didn't start. */
errno = ENXIO;
return (-1);
}
nvl = nvlist_create(0);
nvlist_add_number(nvl, "func", (uint64_t)(uintptr_t)func);
nvlist_add_number(nvl, "flags", (uint64_t)flags);
nvl = nvlist_xfer(zygote_sock, nvl);
if (nvl == NULL)
return (-1);
if (nvlist_exists_number(nvl, "error")) {
error = (int)nvlist_get_number(nvl, "error");
nvlist_destroy(nvl);
errno = error;
return (-1);
}
*chanfdp = nvlist_take_descriptor(nvl, "chanfd");
*procfdp = nvlist_take_descriptor(nvl, "procfd");
nvlist_destroy(nvl);
return (0);
}
/*
* This function creates sandboxes on-demand whoever has access to it via
* 'sock' socket. Function sends two descriptors to the caller: process
* descriptor of the sandbox and socket pair descriptor for communication
* between sandbox and its owner.
*/
static void
zygote_main(int sock)
{
int error, fd, flags, procfd;
int chanfd[2];
nvlist_t *nvlin, *nvlout;
zygote_func_t *func;
pid_t pid;
assert(sock > STDERR_FILENO);
if (pjdlog_mode_get() != PJDLOG_MODE_STD)
stdnull();
for (fd = STDERR_FILENO + 1; fd < sock; fd++)
close(fd);
closefrom(sock + 1);
for (;;) {
nvlin = nvlist_recv(sock);
if (nvlin == NULL)
continue;
func = (zygote_func_t *)(uintptr_t)nvlist_get_number(nvlin,
"func");
flags = (int)nvlist_get_number(nvlin, "flags");
nvlist_destroy(nvlin);
/*
* Someone is requesting a new process, create one.
*/
procfd = -1;
chanfd[0] = -1;
chanfd[1] = -1;
error = 0;
if (socketpair(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0,
chanfd) == -1) {
error = errno;
goto send;
}
pid = pdfork(&procfd, 0);
switch (pid) {
case -1:
/* Failure. */
error = errno;
break;
case 0:
/* Child. */
close(sock);
close(chanfd[0]);
func(chanfd[1]);
/* NOTREACHED */
exit(1);
default:
/* Parent. */
close(chanfd[1]);
break;
}
send:
nvlout = nvlist_create(0);
if (error != 0) {
nvlist_add_number(nvlout, "error", (uint64_t)error);
if (chanfd[0] >= 0)
close(chanfd[0]);
if (procfd >= 0)
close(procfd);
} else {
nvlist_move_descriptor(nvlout, "chanfd", chanfd[0]);
nvlist_move_descriptor(nvlout, "procfd", procfd);
}
(void)nvlist_send(sock, nvlout);
nvlist_destroy(nvlout);
}
/* NOTREACHED */
}
int
zygote_init(void)
{
int serrno, sp[2];
pid_t pid;
if (socketpair(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, sp) == -1)
return (-1);
pid = fork();
switch (pid) {
case -1:
/* Failure. */
serrno = errno;
close(sp[0]);
close(sp[1]);
errno = serrno;
return (-1);
case 0:
/* Child. */
close(sp[0]);
zygote_main(sp[1]);
/* NOTREACHED */
abort();
default:
/* Parent. */
zygote_sock = sp[0];
close(sp[1]);
return (0);
}
/* NOTREACHED */
}

40
sbin/casperd/zygote.h Normal file
View File

@ -0,0 +1,40 @@
/*-
* Copyright (c) 2012 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* 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 _ZYGOTE_H_
#define _ZYGOTE_H_
typedef void zygote_func_t(int);
int zygote_init(void);
int zygote_clone(zygote_func_t *func, int flags, int *chanfdp, int *procfdp);
#endif /* !_ZYGOTE_H_ */

View File

@ -32,6 +32,8 @@ LIBC?= ${DESTDIR}${LIBDIR}/libc.a
LIBC_PIC?= ${DESTDIR}${LIBDIR}/libc_pic.a
LIBCALENDAR?= ${DESTDIR}${LIBDIR}/libcalendar.a
LIBCAM?= ${DESTDIR}${LIBDIR}/libcam.a
LIBCAPSICUM?= ${DESTDIR}${LIBDIR}/libcapsicum.a
LIBCASPER?= ${DESTDIR}${LIBDIR}/libcasper.a
LIBCOM_ERR?= ${DESTDIR}${LIBDIR}/libcom_err.a
LIBCOMPAT?= ${DESTDIR}${LIBDIR}/libcompat.a
LIBCRYPT?= ${DESTDIR}${LIBDIR}/libcrypt.a

View File

@ -264,6 +264,7 @@ __DEFAULT_YES_OPTIONS = \
BZIP2 \
CALENDAR \
CAPSICUM \
CASPER \
CDDL \
CPP \
CROSS_COMPILER \

View File

@ -0,0 +1,2 @@
.\" $FreeBSD$
Set to not build Casper program and related libraries.