summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.inc18
-rw-r--r--contrib/mdocml/lib.in1
-rw-r--r--etc/Makefile3
-rw-r--r--etc/casper/Makefile12
-rw-r--r--etc/casper/system.dns1
-rw-r--r--etc/casper/system.grp1
-rw-r--r--etc/casper/system.pwd1
-rw-r--r--etc/casper/system.random1
-rw-r--r--etc/casper/system.sysctl1
-rw-r--r--etc/mtree/BSD.root.dist4
-rw-r--r--lib/Makefile8
-rw-r--r--lib/libcapsicum/Makefile46
-rw-r--r--lib/libcapsicum/libcapsicum.3288
-rw-r--r--lib/libcapsicum/libcapsicum.c266
-rw-r--r--lib/libcapsicum/libcapsicum.h115
-rw-r--r--lib/libcapsicum/libcapsicum_dns.c346
-rw-r--r--lib/libcapsicum/libcapsicum_dns.h57
-rw-r--r--lib/libcapsicum/libcapsicum_grp.c426
-rw-r--r--lib/libcapsicum/libcapsicum_grp.h57
-rw-r--r--lib/libcapsicum/libcapsicum_impl.h39
-rw-r--r--lib/libcapsicum/libcapsicum_pwd.c384
-rw-r--r--lib/libcapsicum/libcapsicum_pwd.h57
-rw-r--r--lib/libcapsicum/libcapsicum_random.c80
-rw-r--r--lib/libcapsicum/libcapsicum_random.h37
-rw-r--r--lib/libcapsicum/libcapsicum_service.c96
-rw-r--r--lib/libcapsicum/libcapsicum_service.h40
-rw-r--r--lib/libcapsicum/libcapsicum_sysctl.c86
-rw-r--r--lib/libcapsicum/libcapsicum_sysctl.h43
-rw-r--r--libexec/Makefile5
-rw-r--r--libexec/casper/Makefile11
-rw-r--r--libexec/casper/dns/Makefile22
-rw-r--r--libexec/casper/dns/dns.c425
-rw-r--r--libexec/casper/grp/Makefile22
-rw-r--r--libexec/casper/grp/grp.c384
-rw-r--r--libexec/casper/pwd/Makefile22
-rw-r--r--libexec/casper/pwd/pwd.c429
-rw-r--r--libexec/casper/random/Makefile22
-rw-r--r--libexec/casper/random/random.c81
-rw-r--r--libexec/casper/sysctl/Makefile22
-rw-r--r--libexec/casper/sysctl/sysctl.c249
-rw-r--r--sbin/Makefile4
-rw-r--r--sbin/casperd/Makefile18
-rw-r--r--sbin/casperd/casperd.8124
-rw-r--r--sbin/casperd/casperd.c711
-rw-r--r--sbin/casperd/zygote.c219
-rw-r--r--sbin/casperd/zygote.h40
-rw-r--r--share/mk/bsd.libnames.mk2
-rw-r--r--share/mk/bsd.own.mk1
-rw-r--r--tools/build/options/WITHOUT_CASPER2
49 files changed, 5318 insertions, 1 deletions
diff --git a/Makefile.inc1 b/Makefile.inc1
index 1fd5aa3..cf39edf 100644
--- a/Makefile.inc1
+++ b/Makefile.inc1
@@ -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}
diff --git a/contrib/mdocml/lib.in b/contrib/mdocml/lib.in
index 0e3162f..6af9110 100644
--- a/contrib/mdocml/lib.in
+++ b/contrib/mdocml/lib.in
@@ -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)")
diff --git a/etc/Makefile b/etc/Makefile
index 70a1e2a..bcccf82 100644
--- a/etc/Makefile
+++ b/etc/Makefile
@@ -221,6 +221,9 @@ distribution:
.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
${_+_}cd ${.CURDIR}/gss; ${MAKE} install
diff --git a/etc/casper/Makefile b/etc/casper/Makefile
new file mode 100644
index 0000000..d1cd9f4
--- /dev/null
+++ b/etc/casper/Makefile
@@ -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>
diff --git a/etc/casper/system.dns b/etc/casper/system.dns
new file mode 100644
index 0000000..b3c1ca0
--- /dev/null
+++ b/etc/casper/system.dns
@@ -0,0 +1 @@
+/libexec/casper/dns
diff --git a/etc/casper/system.grp b/etc/casper/system.grp
new file mode 100644
index 0000000..32eea2c
--- /dev/null
+++ b/etc/casper/system.grp
@@ -0,0 +1 @@
+/libexec/casper/grp
diff --git a/etc/casper/system.pwd b/etc/casper/system.pwd
new file mode 100644
index 0000000..a5f7c25
--- /dev/null
+++ b/etc/casper/system.pwd
@@ -0,0 +1 @@
+/libexec/casper/pwd
diff --git a/etc/casper/system.random b/etc/casper/system.random
new file mode 100644
index 0000000..ac98b35
--- /dev/null
+++ b/etc/casper/system.random
@@ -0,0 +1 @@
+/libexec/casper/random
diff --git a/etc/casper/system.sysctl b/etc/casper/system.sysctl
new file mode 100644
index 0000000..9f80c5d
--- /dev/null
+++ b/etc/casper/system.sysctl
@@ -0,0 +1 @@
+/libexec/casper/sysctl
diff --git a/etc/mtree/BSD.root.dist b/etc/mtree/BSD.root.dist
index 86b93e1..5acd4b2 100644
--- a/etc/mtree/BSD.root.dist
+++ b/etc/mtree/BSD.root.dist
@@ -26,6 +26,8 @@
..
bluetooth
..
+ casper
+ ..
defaults
..
devd
@@ -74,6 +76,8 @@
..
..
libexec
+ casper
+ ..
resolvconf
..
..
diff --git a/lib/Makefile b/lib/Makefile
index 4f7ad6d..4912259 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -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
diff --git a/lib/libcapsicum/Makefile b/lib/libcapsicum/Makefile
new file mode 100644
index 0000000..54d8dfb
--- /dev/null
+++ b/lib/libcapsicum/Makefile
@@ -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>
diff --git a/lib/libcapsicum/libcapsicum.3 b/lib/libcapsicum/libcapsicum.3
new file mode 100644
index 0000000..6204a0e
--- /dev/null
+++ b/lib/libcapsicum/libcapsicum.3
@@ -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.
diff --git a/lib/libcapsicum/libcapsicum.c b/lib/libcapsicum/libcapsicum.c
new file mode 100644
index 0000000..79ca871
--- /dev/null
+++ b/lib/libcapsicum/libcapsicum.c
@@ -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));
+}
diff --git a/lib/libcapsicum/libcapsicum.h b/lib/libcapsicum/libcapsicum.h
new file mode 100644
index 0000000..4f8c597
--- /dev/null
+++ b/lib/libcapsicum/libcapsicum.h
@@ -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_ */
diff --git a/lib/libcapsicum/libcapsicum_dns.c b/lib/libcapsicum/libcapsicum_dns.c
new file mode 100644
index 0000000..170e0d0
--- /dev/null
+++ b/lib/libcapsicum/libcapsicum_dns.c
@@ -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));
+}
diff --git a/lib/libcapsicum/libcapsicum_dns.h b/lib/libcapsicum/libcapsicum_dns.h
new file mode 100644
index 0000000..0223510
--- /dev/null
+++ b/lib/libcapsicum/libcapsicum_dns.h
@@ -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_ */
diff --git a/lib/libcapsicum/libcapsicum_grp.c b/lib/libcapsicum/libcapsicum_grp.c
new file mode 100644
index 0000000..c679ce9
--- /dev/null
+++ b/lib/libcapsicum/libcapsicum_grp.c
@@ -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));
+}
diff --git a/lib/libcapsicum/libcapsicum_grp.h b/lib/libcapsicum/libcapsicum_grp.h
new file mode 100644
index 0000000..e0b44f0
--- /dev/null
+++ b/lib/libcapsicum/libcapsicum_grp.h
@@ -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_ */
diff --git a/lib/libcapsicum/libcapsicum_impl.h b/lib/libcapsicum/libcapsicum_impl.h
new file mode 100644
index 0000000..ce6f49f
--- /dev/null
+++ b/lib/libcapsicum/libcapsicum_impl.h
@@ -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_ */
diff --git a/lib/libcapsicum/libcapsicum_pwd.c b/lib/libcapsicum/libcapsicum_pwd.c
new file mode 100644
index 0000000..792fb66
--- /dev/null
+++ b/lib/libcapsicum/libcapsicum_pwd.c
@@ -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));
+}
diff --git a/lib/libcapsicum/libcapsicum_pwd.h b/lib/libcapsicum/libcapsicum_pwd.h
new file mode 100644
index 0000000..960a490
--- /dev/null
+++ b/lib/libcapsicum/libcapsicum_pwd.h
@@ -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_ */
diff --git a/lib/libcapsicum/libcapsicum_random.c b/lib/libcapsicum/libcapsicum_random.c
new file mode 100644
index 0000000..eed97e2
--- /dev/null
+++ b/lib/libcapsicum/libcapsicum_random.c
@@ -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);
+}
diff --git a/lib/libcapsicum/libcapsicum_random.h b/lib/libcapsicum/libcapsicum_random.h
new file mode 100644
index 0000000..672afa0
--- /dev/null
+++ b/lib/libcapsicum/libcapsicum_random.h
@@ -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_ */
diff --git a/lib/libcapsicum/libcapsicum_service.c b/lib/libcapsicum/libcapsicum_service.c
new file mode 100644
index 0000000..4127668
--- /dev/null
+++ b/lib/libcapsicum/libcapsicum_service.c
@@ -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));
+}
diff --git a/lib/libcapsicum/libcapsicum_service.h b/lib/libcapsicum/libcapsicum_service.h
new file mode 100644
index 0000000..05c654f
--- /dev/null
+++ b/lib/libcapsicum/libcapsicum_service.h
@@ -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_ */
diff --git a/lib/libcapsicum/libcapsicum_sysctl.c b/lib/libcapsicum/libcapsicum_sysctl.c
new file mode 100644
index 0000000..6ea951b
--- /dev/null
+++ b/lib/libcapsicum/libcapsicum_sysctl.c
@@ -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);
+}
diff --git a/lib/libcapsicum/libcapsicum_sysctl.h b/lib/libcapsicum/libcapsicum_sysctl.h
new file mode 100644
index 0000000..d0df143
--- /dev/null
+++ b/lib/libcapsicum/libcapsicum_sysctl.h
@@ -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_ */
diff --git a/libexec/Makefile b/libexec/Makefile
index cf5c120..e4781b8 100644
--- a/libexec/Makefile
+++ b/libexec/Makefile
@@ -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
diff --git a/libexec/casper/Makefile b/libexec/casper/Makefile
new file mode 100644
index 0000000..ed6bd7b
--- /dev/null
+++ b/libexec/casper/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+SUBDIR= dns
+SUBDIR+=grp
+SUBDIR+=pwd
+SUBDIR+=random
+SUBDIR+=sysctl
+
+.include <bsd.subdir.mk>
diff --git a/libexec/casper/dns/Makefile b/libexec/casper/dns/Makefile
new file mode 100644
index 0000000..82bd4c0
--- /dev/null
+++ b/libexec/casper/dns/Makefile
@@ -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>
diff --git a/libexec/casper/dns/dns.c b/libexec/casper/dns/dns.c
new file mode 100644
index 0000000..6be022a
--- /dev/null
+++ b/libexec/casper/dns/dns.c
@@ -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));
+}
diff --git a/libexec/casper/grp/Makefile b/libexec/casper/grp/Makefile
new file mode 100644
index 0000000..6f166b8
--- /dev/null
+++ b/libexec/casper/grp/Makefile
@@ -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>
diff --git a/libexec/casper/grp/grp.c b/libexec/casper/grp/grp.c
new file mode 100644
index 0000000..ba22f62
--- /dev/null
+++ b/libexec/casper/grp/grp.c
@@ -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));
+}
diff --git a/libexec/casper/pwd/Makefile b/libexec/casper/pwd/Makefile
new file mode 100644
index 0000000..b2dcfec
--- /dev/null
+++ b/libexec/casper/pwd/Makefile
@@ -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>
diff --git a/libexec/casper/pwd/pwd.c b/libexec/casper/pwd/pwd.c
new file mode 100644
index 0000000..be51ad0
--- /dev/null
+++ b/libexec/casper/pwd/pwd.c
@@ -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));
+}
diff --git a/libexec/casper/random/Makefile b/libexec/casper/random/Makefile
new file mode 100644
index 0000000..87efe21
--- /dev/null
+++ b/libexec/casper/random/Makefile
@@ -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>
diff --git a/libexec/casper/random/random.c b/libexec/casper/random/random.c
new file mode 100644
index 0000000..56f1afb
--- /dev/null
+++ b/libexec/casper/random/random.c
@@ -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));
+}
diff --git a/libexec/casper/sysctl/Makefile b/libexec/casper/sysctl/Makefile
new file mode 100644
index 0000000..dc15099
--- /dev/null
+++ b/libexec/casper/sysctl/Makefile
@@ -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>
diff --git a/libexec/casper/sysctl/sysctl.c b/libexec/casper/sysctl/sysctl.c
new file mode 100644
index 0000000..8bb43b6
--- /dev/null
+++ b/libexec/casper/sysctl/sysctl.c
@@ -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));
+}
diff --git a/sbin/Makefile b/sbin/Makefile
index 7411493..9ee8ea3 100644
--- a/sbin/Makefile
+++ b/sbin/Makefile
@@ -78,6 +78,10 @@ SUBDIR=adjkerntz \
SUBDIR+= atm
.endif
+.if ${MK_CASPER} != "no"
+SUBDIR+= casperd
+.endif
+
.if ${MK_CXX} != "no"
SUBDIR+= devd
.endif
diff --git a/sbin/casperd/Makefile b/sbin/casperd/Makefile
new file mode 100644
index 0000000..d436ce8
--- /dev/null
+++ b/sbin/casperd/Makefile
@@ -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>
diff --git a/sbin/casperd/casperd.8 b/sbin/casperd/casperd.8
new file mode 100644
index 0000000..50f8ebe
--- /dev/null
+++ b/sbin/casperd/casperd.8
@@ -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.
diff --git a/sbin/casperd/casperd.c b/sbin/casperd/casperd.c
new file mode 100644
index 0000000..e918534
--- /dev/null
+++ b/sbin/casperd/casperd.c
@@ -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);
+}
diff --git a/sbin/casperd/zygote.c b/sbin/casperd/zygote.c
new file mode 100644
index 0000000..70594ce
--- /dev/null
+++ b/sbin/casperd/zygote.c
@@ -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 */
+}
diff --git a/sbin/casperd/zygote.h b/sbin/casperd/zygote.h
new file mode 100644
index 0000000..75ef2ef
--- /dev/null
+++ b/sbin/casperd/zygote.h
@@ -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_ */
diff --git a/share/mk/bsd.libnames.mk b/share/mk/bsd.libnames.mk
index 3b9abca..4f32e59 100644
--- a/share/mk/bsd.libnames.mk
+++ b/share/mk/bsd.libnames.mk
@@ -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
diff --git a/share/mk/bsd.own.mk b/share/mk/bsd.own.mk
index a71e442..c99ab84 100644
--- a/share/mk/bsd.own.mk
+++ b/share/mk/bsd.own.mk
@@ -264,6 +264,7 @@ __DEFAULT_YES_OPTIONS = \
BZIP2 \
CALENDAR \
CAPSICUM \
+ CASPER \
CDDL \
CPP \
CROSS_COMPILER \
diff --git a/tools/build/options/WITHOUT_CASPER b/tools/build/options/WITHOUT_CASPER
new file mode 100644
index 0000000..d364305
--- /dev/null
+++ b/tools/build/options/WITHOUT_CASPER
@@ -0,0 +1,2 @@
+.\" $FreeBSD$
+Set to not build Casper program and related libraries.
OpenPOWER on IntegriCloud