Index: head/lib/libcasper/libcasper/Makefile =================================================================== --- head/lib/libcasper/libcasper/Makefile (revision 329451) +++ head/lib/libcasper/libcasper/Makefile (revision 329452) @@ -1,42 +1,42 @@ # $FreeBSD$ SHLIBDIR?= /lib .include PACKAGE=casper .if ${MK_CASPER} != "no" SHLIB= casper -SHLIB_MAJOR= 0 +SHLIB_MAJOR= 1 SRCS= libcasper.c SRCS+= libcasper_impl.c SRCS+= libcasper_service.c SRCS+= service.c SRCS+= zygote.c .endif INCS= libcasper.h INCS+= libcasper_service.h LIBADD= nv CFLAGS+=-I${.CURDIR} MAN+= libcasper.3 MLINKS+=libcasper.3 cap_init.3 MLINKS+=libcasper.3 cap_wrap.3 MLINKS+=libcasper.3 cap_unwrap.3 MLINKS+=libcasper.3 cap_sock.3 MLINKS+=libcasper.3 cap_clone.3 MLINKS+=libcasper.3 cap_close.3 MLINKS+=libcasper.3 cap_limit_get.3 MLINKS+=libcasper.3 cap_limit_set.3 MLINKS+=libcasper.3 cap_send_nvlist.3 MLINKS+=libcasper.3 cap_recv_nvlist.3 MLINKS+=libcasper.3 cap_xfer_nvlist.3 MLINKS+=libcasper.3 cap_service_open.3 .include Index: head/lib/libcasper/libcasper/libcasper.3 =================================================================== --- head/lib/libcasper/libcasper/libcasper.3 (revision 329451) +++ head/lib/libcasper/libcasper/libcasper.3 (revision 329452) @@ -1,320 +1,316 @@ .\" 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 February 5, 2018 +.Dd February 17, 2018 .Dt LIBCASPER 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 libcasper .Sh SYNOPSIS .Fd #define WITH_CASPER .In sys/nv.h .In libcasper.h .Ft "cap_channel_t *" .Fn cap_init "void" .Ft "cap_channel_t *" -.Fn cap_wrap "int sock" +.Fn cap_wrap "int sock" "int flags" .Ft "int" -.Fn cap_unwrap "cap_channel_t *chan" +.Fn cap_unwrap "cap_channel_t *chan" "int *flags" .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" "int flags" +.Fn cap_recv_nvlist "const cap_channel_t *chan" .Ft "nvlist_t *" -.Fn cap_xfer_nvlist "const cap_channel_t *chan" "nvlist_t *nvl" "int flags" +.Fn cap_xfer_nvlist "const cap_channel_t *chan" "nvlist_t *nvl" .Ft "cap_channel_t *" .Fn cap_service_open "const cap_channel_t *chan" "const char *name" .Sh DESCRIPTION The .Nm libcasper library allows to manage application capabilities through the casper process. .Pp The application capability (represented by the .Vt cap_channel_t type) is a communication channel between the caller and the casper process daemon or an instance of one of its services. A capability to the casper process 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 casper process. .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. +The +.Fa flags +argument defines the channel behavior. +The supported flags are: +.Bl -ohang -offset indent +.It CASPER_NO_UNIQ +The communication between process and casper uses no unique version of nvlist. +.El .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. -The -.Fa flags -argument defines what type the top nvlist is expected to be. -If the nvlist flags do not match the flags passed to -.Fn cap_recv_nvlist , -the nvlist will not be returned. .Pp The .Fn cap_xfer_nvlist function sends the given nvlist, destroys it and receives new nvlist in response over the given capability. -The -.Fa flags -argument defines what type the top nvlist is expected to be. -If the nvlist flags do not match the flags passed to -.Fn cap_xfer_nvlist , -the nvlist will not be returned. 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. Casper supports the following services in the base system: .Bl -tag -width "system.random" -compact -offset indent .Pp .It system.dns provides DNS libc compatible API .It system.grp provides .Xr getgrent 3 compatible API .It system.pwd provides .Xr getpwent 3 compatible API .It system.random allows to obtain entropy from .Pa /dev/random .It system.sysctl provides .Xr sysctlbyname 3 compatible API .It system.syslog provides .Xr syslog 3 compatible API .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 casper 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. */ capcas = cap_init(); if (capcas == NULL) err(1, "Unable to contact Casper"); /* Enter capability mode sandbox. */ if (cap_enter() < 0 && errno != ENOSYS) err(1, "Unable to enter capability mode"); /* Use Casper capability to create capability to the system.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(1, "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 .Sh AUTHORS The .Nm libcasper library was implemented by .An Pawel Jakub Dawidek Aq Mt pawel@dawidek.net under sponsorship from the FreeBSD Foundation. The .Nm libcasper new architecture was implemented by .An Mariusz Zaborski Aq Mt oshogbo@FreeBSD.org . Index: head/lib/libcasper/libcasper/libcasper.c =================================================================== --- head/lib/libcasper/libcasper/libcasper.c (revision 329451) +++ head/lib/libcasper/libcasper/libcasper.c (revision 329452) @@ -1,339 +1,360 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012-2013 The FreeBSD Foundation * Copyright (c) 2015 Mariusz Zaborski * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include "libcasper.h" #include "libcasper_impl.h" +#define CASPER_VALID_FLAGS (CASPER_NO_UNIQ) + /* * 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; /* Process descriptor for casper. */ int cch_pd; + /* Flags to communicate with casper. */ + int cch_flags; }; static bool cap_add_pd(cap_channel_t *chan, int pd) { if (!fd_is_valid(pd)) return (false); chan->cch_pd = pd; return (true); } +int +cap_channel_flags(const cap_channel_t *chan) +{ + + return (chan->cch_flags); +} + cap_channel_t * cap_init(void) { pid_t pid; int sock[2], serrno, pfd; bool ret; cap_channel_t *chan; if (socketpair(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, sock) == -1) { return (NULL); } pid = pdfork(&pfd, 0); if (pid == 0) { /* Parent. */ close(sock[0]); casper_main_loop(sock[1]); /* NOTREACHED. */ } else if (pid > 0) { /* Child. */ close(sock[1]); - chan = cap_wrap(sock[0]); + chan = cap_wrap(sock[0], 0); if (chan == NULL) { serrno = errno; close(sock[0]); close(pfd); errno = serrno; return (NULL); } ret = cap_add_pd(chan, pfd); assert(ret); return (chan); } /* Error. */ serrno = errno; close(sock[0]); close(sock[1]); errno = serrno; return (NULL); } cap_channel_t * -cap_wrap(int sock) +cap_wrap(int sock, int flags) { cap_channel_t *chan; if (!fd_is_valid(sock)) return (NULL); + if ((flags & CASPER_VALID_FLAGS) != flags) + return (NULL); + chan = malloc(sizeof(*chan)); if (chan != NULL) { chan->cch_sock = sock; chan->cch_pd = -1; + chan->cch_flags = flags; chan->cch_magic = CAP_CHANNEL_MAGIC; } return (chan); } int -cap_unwrap(cap_channel_t *chan) +cap_unwrap(cap_channel_t *chan, int *flags) { int sock; assert(chan != NULL); assert(chan->cch_magic == CAP_CHANNEL_MAGIC); sock = chan->cch_sock; if (chan->cch_pd != -1) close(chan->cch_pd); + if (flags != NULL) + *flags = chan->cch_flags; 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); + nvl = nvlist_create(channel_nvlist_flags(chan)); nvlist_add_string(nvl, "cmd", "clone"); - nvl = cap_xfer_nvlist(chan, nvl, 0); + 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); + newchan = cap_wrap(newsock, chan->cch_flags); 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; if (chan->cch_pd != -1) close(chan->cch_pd); 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); + nvlmsg = nvlist_create(channel_nvlist_flags(chan)); nvlist_add_string(nvlmsg, "cmd", "limit_set"); nvlist_add_nvlist(nvlmsg, "limits", limits); - nvlmsg = cap_xfer_nvlist(chan, nvlmsg, 0); + 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); + nvlmsg = nvlist_create(channel_nvlist_flags(chan)); nvlist_add_string(nvlmsg, "cmd", "limit_get"); - nvlmsg = cap_xfer_nvlist(chan, nvlmsg, 0); + 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, int flags) +cap_recv_nvlist(const cap_channel_t *chan) { assert(chan != NULL); assert(chan->cch_magic == CAP_CHANNEL_MAGIC); - return (nvlist_recv(chan->cch_sock, flags)); + return (nvlist_recv(chan->cch_sock, + channel_nvlist_flags(chan))); } nvlist_t * -cap_xfer_nvlist(const cap_channel_t *chan, nvlist_t *nvl, int flags) +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, flags)); + return (nvlist_xfer(chan->cch_sock, nvl, + channel_nvlist_flags(chan))); } cap_channel_t * cap_service_open(const cap_channel_t *chan, const char *name) { cap_channel_t *newchan; nvlist_t *nvl; int sock, error; + int flags; sock = -1; - nvl = nvlist_create(0); + nvl = nvlist_create(channel_nvlist_flags(chan)); nvlist_add_string(nvl, "cmd", "open"); nvlist_add_string(nvl, "service", name); - nvl = cap_xfer_nvlist(chan, nvl, 0); + 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"); + flags = nvlist_take_number(nvl, "chanflags"); assert(sock >= 0); nvlist_destroy(nvl); nvl = NULL; - newchan = cap_wrap(sock); + newchan = cap_wrap(sock, flags); 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); + limits = nvlist_create(channel_nvlist_flags(chan)); for (i = 0; i < nnames; i++) nvlist_add_null(limits, names[i]); return (cap_limit_set(chan, limits)); } Index: head/lib/libcasper/libcasper/libcasper.h =================================================================== --- head/lib/libcasper/libcasper/libcasper.h (revision 329451) +++ head/lib/libcasper/libcasper/libcasper.h (revision 329452) @@ -1,254 +1,282 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012-2013 The FreeBSD Foundation * Copyright (c) 2015-2017 Mariusz Zaborski * 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 _LIBCASPER_H_ #define _LIBCASPER_H_ #ifdef HAVE_CASPER #define WITH_CASPER #endif #include #include #include #include +#define CASPER_NO_UNIQ 0x00000001 + #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 #ifdef WITH_CASPER struct cap_channel; typedef struct cap_channel cap_channel_t; #define CASPER_SUPPORT (1) #else struct cap_channel { int cch_fd; + int cch_flags; }; typedef struct cap_channel cap_channel_t; #define CASPER_SUPPORT (0) #endif /* ! WITH_CASPER */ #endif /* ! _CAP_CHANNEL_T_DECLARED */ +#ifdef WITH_CASPER +int cap_channel_flags(const cap_channel_t *chan); +#else +static inline int +cap_channel_flags(const cap_channel_t *chan) +{ + + return (chan->cch_flags); +} +#endif + +static inline int +channel_nvlist_flags(const cap_channel_t *chan) +{ + int flags; + + flags = 0; + if ((cap_channel_flags(chan) & CASPER_NO_UNIQ) != 0) + flags |= NV_FLAG_NO_UNIQUE; + + return (flags); +} + /* * The functions opens unrestricted communication channel to Casper. */ #ifdef WITH_CASPER cap_channel_t *cap_init(void); #else static inline cap_channel_t * cap_init(void) { cap_channel_t *chan; chan = malloc(sizeof(*chan)); if (chan != NULL) { chan->cch_fd = -1; } return (chan); } #endif /* * The functions to communicate with service. */ #ifdef WITH_CASPER 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); #else #define cap_service_open(chan, name) (cap_init()) #define cap_service_limit(chan, names, nnames) (0) #endif /* * The function creates cap_channel_t based on the given socket. */ #ifdef WITH_CASPER -cap_channel_t *cap_wrap(int sock); +cap_channel_t *cap_wrap(int sock, int flags); #else static inline cap_channel_t * -cap_wrap(int sock) +cap_wrap(int sock, int flags) { cap_channel_t *chan; chan = cap_init(); if (chan != NULL) { chan->cch_fd = sock; + chan->cch_flags = flags; } return (chan); } #endif /* * The function returns communication socket and frees cap_channel_t. */ #ifdef WITH_CASPER -int cap_unwrap(cap_channel_t *chan); +int cap_unwrap(cap_channel_t *chan, int *flags); #else static inline int cap_unwrap(cap_channel_t *chan) { int fd; fd = chan->cch_fd; free(chan); return (fd); } #endif /* * The function clones the given capability. */ #ifdef WITH_CASPER cap_channel_t *cap_clone(const cap_channel_t *chan); #else static inline cap_channel_t * cap_clone(const cap_channel_t *chan) { cap_channel_t *newchan; newchan = cap_init(); if (newchan == NULL) { return (NULL); } if (chan->cch_fd == -1) { newchan->cch_fd = -1; } else { newchan->cch_fd = dup(chan->cch_fd); if (newchan->cch_fd < 0) { free(newchan); newchan = NULL; } } + newchan->cch_flags = chan->cch_flags; return (newchan); } #endif /* * The function closes the given capability. */ #ifdef WITH_CASPER void cap_close(cap_channel_t *chan); #else static inline void cap_close(cap_channel_t *chan) { if (chan->cch_fd >= 0) { close(chan->cch_fd); } free(chan); } #endif /* * The function returns socket descriptor associated with the given * cap_channel_t for use with select(2)/kqueue(2)/etc. */ #ifdef WITH_CASPER int cap_sock(const cap_channel_t *chan); #else #define cap_sock(chan) (chan->cch_fd) #endif /* * The function limits the given capability. * It always destroys 'limits' on return. */ #ifdef WITH_CASPER int cap_limit_set(const cap_channel_t *chan, nvlist_t *limits); #else #define cap_limit_set(chan, limits) (0) #endif /* * The function returns current limits of the given capability. */ #ifdef WITH_CASPER int cap_limit_get(const cap_channel_t *chan, nvlist_t **limitsp); #else static inline int cap_limit_get(const cap_channel_t *chan __unused, nvlist_t **limitsp) { - *limitsp = nvlist_create(0); + *limitsp = nvlist_create(channel_nvlist_flags(chan)); return (0); } #endif /* * Function sends nvlist over the given capability. */ #ifdef WITH_CASPER int cap_send_nvlist(const cap_channel_t *chan, const nvlist_t *nvl); #else #define cap_send_nvlist(chan, nvl) (0) #endif /* * Function receives nvlist over the given capability. */ #ifdef WITH_CASPER -nvlist_t *cap_recv_nvlist(const cap_channel_t *chan, int flags); +nvlist_t *cap_recv_nvlist(const cap_channel_t *chan); #else -#define cap_recv_nvlist(chan, flags) (nvlist_create(flags)) +#define cap_recv_nvlist(chan) (nvlist_create(chan->cch_flags)) #endif /* * Function sends the given nvlist, destroys it and receives new nvlist in * response over the given capability. */ #ifdef WITH_CASPER -nvlist_t *cap_xfer_nvlist(const cap_channel_t *chan, nvlist_t *nvl, int flags); +nvlist_t *cap_xfer_nvlist(const cap_channel_t *chan, nvlist_t *nvl); #else static inline nvlist_t * -cap_xfer_nvlist(const cap_channel_t *chan __unused, nvlist_t *nvl, int flags) +cap_xfer_nvlist(const cap_channel_t *chan, nvlist_t *nvl) { nvlist_destroy(nvl); - return (nvlist_create(flags)); + return (nvlist_create(channel_nvlist_flags(chan))); } #endif #endif /* !_LIBCASPER_H_ */ Index: head/lib/libcasper/libcasper/libcasper_impl.h =================================================================== --- head/lib/libcasper/libcasper/libcasper_impl.h (revision 329451) +++ head/lib/libcasper/libcasper/libcasper_impl.h (revision 329452) @@ -1,84 +1,85 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2013 The FreeBSD Foundation * Copyright (c) 2015 Mariusz Zaborski * 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 _LIBCASPER_IMPL_H_ #define _LIBCASPER_IMPL_H_ #include #include "libcasper.h" #include "libcasper_service.h" struct service; struct service_connection; bool fd_is_valid(int fd); /* Private service functions. */ struct service *service_alloc(const char *name, service_limit_func_t *limitfunc, service_command_func_t *commandfunc, uint64_t flags); void service_free(struct service *service); void service_message(struct service *service, struct service_connection *sconn); void service_start(struct service *service, int sock, int procfd); const char *service_name(struct service *service); +int service_get_channel_flags(struct service *service); /* Private service connection functions. */ struct service_connection *service_connection_add(struct service *service, int sock, const nvlist_t *limits); void service_connection_remove( struct service *service, struct service_connection *sconn); int service_connection_clone( struct service *service, struct service_connection *sconn); struct service_connection *service_connection_first( struct service *service); struct service_connection *service_connection_next( struct service_connection *sconn); cap_channel_t *service_connection_get_chan( const struct service_connection *sconn); int service_connection_get_sock( const struct service_connection *sconn); const nvlist_t *service_connection_get_limits( const struct service_connection *sconn); void service_connection_set_limits( struct service_connection *sconn, nvlist_t *limits); /* Private libcasper functions. */ void casper_main_loop(int fd); #endif /* !_LIBCASPER_IMPL_H_ */ Index: head/lib/libcasper/libcasper/libcasper_service.c =================================================================== --- head/lib/libcasper/libcasper/libcasper_service.c (revision 329451) +++ head/lib/libcasper/libcasper/libcasper_service.c (revision 329452) @@ -1,290 +1,292 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 The FreeBSD Foundation * Copyright (c) 2015 Mariusz Zaborski * Copyright (c) 2017 Robert N. M. Watson * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include "libcasper_impl.h" #include "zygote.h" struct casper_service { struct service *cs_service; TAILQ_ENTRY(casper_service) cs_next; }; static TAILQ_HEAD(, casper_service) casper_services = TAILQ_HEAD_INITIALIZER(casper_services); #define CORE_CASPER_NAME "core.casper" #define CSERVICE_IS_CORE(service) \ (strcmp(service_name(service->cs_service), CORE_CASPER_NAME) == 0) static struct casper_service * service_find(const char *name) { struct casper_service *casserv; TAILQ_FOREACH(casserv, &casper_services, cs_next) { if (strcmp(service_name(casserv->cs_service), name) == 0) break; } return (casserv); } struct casper_service * service_register(const char *name, service_limit_func_t *limitfunc, service_command_func_t *commandfunc, uint64_t flags) { struct casper_service *casserv; if (commandfunc == NULL) return (NULL); if (name == NULL || name[0] == '\0') return (NULL); if (service_find(name) != NULL) return (NULL); casserv = malloc(sizeof(*casserv)); if (casserv == NULL) return (NULL); casserv->cs_service = service_alloc(name, limitfunc, commandfunc, flags); if (casserv->cs_service == NULL) { free(casserv); return (NULL); } TAILQ_INSERT_TAIL(&casper_services, casserv, cs_next); return (casserv); } 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; cookie = NULL; 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); } void service_execute(int chanfd) { struct casper_service *casserv; struct service *service; const char *servname; nvlist_t *nvl; int procfd; nvl = nvlist_recv(chanfd, 0); if (nvl == NULL) exit(1); if (!nvlist_exists_string(nvl, "service")) exit(1); servname = nvlist_get_string(nvl, "service"); casserv = service_find(servname); if (casserv == NULL) exit(1); service = casserv->cs_service; procfd = nvlist_take_descriptor(nvl, "procfd"); nvlist_destroy(nvl); service_start(service, chanfd, procfd); /* Not reached. */ exit(1); } 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, 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); if (zygote_clone_service_execute(&chanfd, &procfd) == -1) return (errno); nvl = nvlist_create(0); nvlist_add_string(nvl, "service", servname); nvlist_move_descriptor(nvl, "procfd", procfd); if (nvlist_send(chanfd, nvl) == -1) { error = errno; nvlist_destroy(nvl); close(chanfd); return (error); } nvlist_destroy(nvl); nvlist_move_descriptor(nvlout, "chanfd", chanfd); + nvlist_add_number(nvlout, "chanflags", + service_get_channel_flags(casserv->cs_service)); return (0); } static void service_register_core(int fd) { struct casper_service *casserv; struct service_connection *sconn; casserv = service_register(CORE_CASPER_NAME, casper_limit, casper_command, 0); sconn = service_connection_add(casserv->cs_service, fd, NULL); if (sconn == NULL) { close(fd); abort(); } } void casper_main_loop(int fd) { fd_set fds; struct casper_service *casserv; struct service_connection *sconn, *sconntmp; int sock, maxfd, ret; if (zygote_init() < 0) exit(1); /* * Register core services. */ service_register_core(fd); for (;;) { FD_ZERO(&fds); FD_SET(fd, &fds); maxfd = -1; TAILQ_FOREACH(casserv, &casper_services, cs_next) { /* We handle only core services. */ if (!CSERVICE_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; } } if (maxfd == -1) { /* Nothing to do. */ exit(0); } maxfd++; assert(maxfd <= (int)FD_SETSIZE); ret = select(maxfd, &fds, NULL, NULL, NULL); assert(ret == -1 || ret > 0); /* select() cannot timeout */ if (ret == -1) { if (errno == EINTR) continue; exit(1); } TAILQ_FOREACH(casserv, &casper_services, cs_next) { /* We handle only core services. */ if (!CSERVICE_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); } } } } } Index: head/lib/libcasper/libcasper/libcasper_service.h =================================================================== --- head/lib/libcasper/libcasper/libcasper_service.h (revision 329451) +++ head/lib/libcasper/libcasper/libcasper_service.h (revision 329452) @@ -1,66 +1,67 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2013 The FreeBSD Foundation * Copyright (c) 2015 Mariusz Zaborski * 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 _LIBCASPER_SERVICE_H_ #define _LIBCASPER_SERVICE_H_ #ifndef _NVLIST_T_DECLARED #define _NVLIST_T_DECLARED struct nvlist; typedef struct nvlist nvlist_t; #endif -#define CASPER_SERVICE_STDIO 0x00000001 -#define CASPER_SERVICE_FD 0x00000002 +#define CASPER_SERVICE_STDIO 0x00000001 +#define CASPER_SERVICE_FD 0x00000002 +#define CASPER_SERVICE_NO_UNIQ_LIMITS 0x00000004 typedef int service_limit_func_t(const nvlist_t *, const nvlist_t *); typedef int service_command_func_t(const char *cmd, const nvlist_t *, nvlist_t *, nvlist_t *); struct casper_service *service_register(const char *name, service_limit_func_t *limitfunc, service_command_func_t *commandfunc, uint64_t flags); #define __constructor __attribute__((constructor)) #define CREATE_SERVICE(name, limit_func, command_func, flags) \ static __constructor void \ init_casper_service(void) \ { \ \ (void)service_register(name, limit_func, command_func, \ flags); \ } #endif /* !_LIBCASPER_SERVICE_H_ */ Index: head/lib/libcasper/libcasper/service.c =================================================================== --- head/lib/libcasper/libcasper/service.c (revision 329451) +++ head/lib/libcasper/libcasper/service.c (revision 329452) @@ -1,453 +1,472 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2013 The FreeBSD Foundation * Copyright (c) 2015 Mariusz Zaborski * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libcasper.h" #include "libcasper_impl.h" /* * Currently there is only one service_connection per service. * In the future we may want multiple connections from multiple clients * per one service instance, but it has to be carefully designed. * The problem is that we may restrict/sandbox service instance according * to the limits provided. When new connection comes in with different * limits we won't be able to access requested resources. * Not to mention one process will serve to mutiple mutually untrusted * clients and compromise of this service instance by one of its clients * can lead to compromise of the other clients. */ /* * Client connections to the given service. */ #define SERVICE_CONNECTION_MAGIC 0x5e91c0ec struct service_connection { int sc_magic; cap_channel_t *sc_chan; nvlist_t *sc_limits; TAILQ_ENTRY(service_connection) sc_next; }; #define SERVICE_MAGIC 0x5e91ce struct service { int s_magic; char *s_name; uint64_t s_flags; service_limit_func_t *s_limit; service_command_func_t *s_command; TAILQ_HEAD(, service_connection) s_connections; }; struct service * service_alloc(const char *name, service_limit_func_t *limitfunc, service_command_func_t *commandfunc, uint64_t flags) { struct service *service; service = malloc(sizeof(*service)); if (service == NULL) return (NULL); service->s_name = strdup(name); if (service->s_name == NULL) { free(service); return (NULL); } service->s_limit = limitfunc; service->s_command = commandfunc; service->s_flags = flags; TAILQ_INIT(&service->s_connections); service->s_magic = SERVICE_MAGIC; return (service); } void service_free(struct service *service) { struct service_connection *sconn; assert(service->s_magic == SERVICE_MAGIC); service->s_magic = 0; while ((sconn = service_connection_first(service)) != NULL) service_connection_remove(service, sconn); free(service->s_name); free(service); } struct service_connection * service_connection_add(struct service *service, int sock, const nvlist_t *limits) { struct service_connection *sconn; int serrno; assert(service->s_magic == SERVICE_MAGIC); sconn = malloc(sizeof(*sconn)); if (sconn == NULL) return (NULL); - sconn->sc_chan = cap_wrap(sock); + sconn->sc_chan = cap_wrap(sock, + service_get_channel_flags(service)); if (sconn->sc_chan == NULL) { serrno = errno; free(sconn); errno = serrno; return (NULL); } if (limits == NULL) { sconn->sc_limits = NULL; } else { sconn->sc_limits = nvlist_clone(limits); if (sconn->sc_limits == NULL) { serrno = errno; - (void)cap_unwrap(sconn->sc_chan); + (void)cap_unwrap(sconn->sc_chan, NULL); free(sconn); errno = serrno; return (NULL); } } sconn->sc_magic = SERVICE_CONNECTION_MAGIC; TAILQ_INSERT_TAIL(&service->s_connections, sconn, sc_next); return (sconn); } void service_connection_remove(struct service *service, struct service_connection *sconn) { assert(service->s_magic == SERVICE_MAGIC); assert(sconn->sc_magic == SERVICE_CONNECTION_MAGIC); TAILQ_REMOVE(&service->s_connections, sconn, sc_next); sconn->sc_magic = 0; nvlist_destroy(sconn->sc_limits); cap_close(sconn->sc_chan); free(sconn); } int service_connection_clone(struct service *service, struct service_connection *sconn) { struct service_connection *newsconn; int serrno, sock[2]; if (socketpair(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, sock) < 0) return (-1); newsconn = service_connection_add(service, sock[0], service_connection_get_limits(sconn)); if (newsconn == NULL) { serrno = errno; close(sock[0]); close(sock[1]); errno = serrno; return (-1); } return (sock[1]); } struct service_connection * service_connection_first(struct service *service) { struct service_connection *sconn; assert(service->s_magic == SERVICE_MAGIC); sconn = TAILQ_FIRST(&service->s_connections); assert(sconn == NULL || sconn->sc_magic == SERVICE_CONNECTION_MAGIC); return (sconn); } struct service_connection * service_connection_next(struct service_connection *sconn) { assert(sconn->sc_magic == SERVICE_CONNECTION_MAGIC); sconn = TAILQ_NEXT(sconn, sc_next); assert(sconn == NULL || sconn->sc_magic == SERVICE_CONNECTION_MAGIC); return (sconn); } cap_channel_t * service_connection_get_chan(const struct service_connection *sconn) { assert(sconn->sc_magic == SERVICE_CONNECTION_MAGIC); return (sconn->sc_chan); } int service_connection_get_sock(const struct service_connection *sconn) { assert(sconn->sc_magic == SERVICE_CONNECTION_MAGIC); return (cap_sock(sconn->sc_chan)); } const nvlist_t * service_connection_get_limits(const struct service_connection *sconn) { assert(sconn->sc_magic == SERVICE_CONNECTION_MAGIC); return (sconn->sc_limits); } void service_connection_set_limits(struct service_connection *sconn, nvlist_t *limits) { assert(sconn->sc_magic == SERVICE_CONNECTION_MAGIC); nvlist_destroy(sconn->sc_limits); sconn->sc_limits = limits; } void service_message(struct service *service, struct service_connection *sconn) { nvlist_t *nvlin, *nvlout; const char *cmd; - int error; + int error, flags; - nvlin = cap_recv_nvlist(service_connection_get_chan(sconn), 0); + flags = 0; + if ((service->s_flags & CASPER_SERVICE_NO_UNIQ_LIMITS) != 0) + flags = NV_FLAG_NO_UNIQUE; + + nvlin = cap_recv_nvlist(service_connection_get_chan(sconn)); if (nvlin == NULL) { service_connection_remove(service, sconn); return; } error = EDOOFUS; - nvlout = nvlist_create(0); + nvlout = nvlist_create(flags); cmd = nvlist_get_string(nvlin, "cmd"); if (strcmp(cmd, "limit_set") == 0) { nvlist_t *nvllim; nvllim = nvlist_take_nvlist(nvlin, "limits"); if (service->s_limit == NULL) { error = EOPNOTSUPP; } else { error = service->s_limit( service_connection_get_limits(sconn), nvllim); } if (error == 0) { service_connection_set_limits(sconn, nvllim); /* Function consumes nvllim. */ } else { nvlist_destroy(nvllim); } } else if (strcmp(cmd, "limit_get") == 0) { const nvlist_t *nvllim; nvllim = service_connection_get_limits(sconn); if (nvllim != NULL) nvlist_add_nvlist(nvlout, "limits", nvllim); else nvlist_add_null(nvlout, "limits"); error = 0; } else if (strcmp(cmd, "clone") == 0) { int sock; sock = service_connection_clone(service, sconn); if (sock == -1) { error = errno; } else { nvlist_move_descriptor(nvlout, "sock", sock); error = 0; } } else { error = service->s_command(cmd, service_connection_get_limits(sconn), nvlin, nvlout); } nvlist_destroy(nvlin); nvlist_add_number(nvlout, "error", (uint64_t)error); if (cap_send_nvlist(service_connection_get_chan(sconn), nvlout) == -1) service_connection_remove(service, sconn); nvlist_destroy(nvlout); } static int fd_add(fd_set *fdsp, int maxfd, int fd) { FD_SET(fd, fdsp); return (fd > maxfd ? fd : maxfd); } const char * service_name(struct service *service) { assert(service->s_magic == SERVICE_MAGIC); return (service->s_name); +} + +int +service_get_channel_flags(struct service *service) +{ + int flags; + + assert(service->s_magic == SERVICE_MAGIC); + flags = 0; + + if ((service->s_flags & CASPER_SERVICE_NO_UNIQ_LIMITS) != 0) + flags |= CASPER_NO_UNIQ; + + return (flags); } static void stdnull(void) { int fd; fd = open(_PATH_DEVNULL, O_RDWR); if (fd == -1) errx(1, "Unable to open %s", _PATH_DEVNULL); if (setsid() == -1) errx(1, "Unable to detach from session"); 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"); if (fd > STDERR_FILENO) close(fd); } static void service_clean(int sock, int procfd, uint64_t flags) { int fd, maxfd, minfd; assert(sock > STDERR_FILENO); assert(procfd > STDERR_FILENO); assert(sock != procfd); if ((flags & CASPER_SERVICE_STDIO) == 0) stdnull(); if ((flags & CASPER_SERVICE_FD) == 0) { if (procfd > sock) { maxfd = procfd; minfd = sock; } else { maxfd = sock; minfd = procfd; } for (fd = STDERR_FILENO + 1; fd < maxfd; fd++) { if (fd != minfd) close(fd); } closefrom(maxfd + 1); } } void service_start(struct service *service, int sock, int procfd) { struct service_connection *sconn, *sconntmp; fd_set fds; int maxfd, nfds; assert(service != NULL); assert(service->s_magic == SERVICE_MAGIC); setproctitle("%s", service->s_name); service_clean(sock, procfd, service->s_flags); if (service_connection_add(service, sock, NULL) == NULL) exit(1); for (;;) { FD_ZERO(&fds); maxfd = -1; for (sconn = service_connection_first(service); sconn != NULL; sconn = service_connection_next(sconn)) { maxfd = fd_add(&fds, maxfd, service_connection_get_sock(sconn)); } assert(maxfd >= 0); assert(maxfd + 1 <= (int)FD_SETSIZE); nfds = select(maxfd + 1, &fds, NULL, NULL, NULL); if (nfds < 0) { if (errno != EINTR) exit(1); continue; } else if (nfds == 0) { /* Timeout. */ abort(); } for (sconn = service_connection_first(service); sconn != NULL; sconn = sconntmp) { /* * Prepare for connection to be removed from the list * on failure. */ sconntmp = service_connection_next(sconn); if (FD_ISSET(service_connection_get_sock(sconn), &fds)) service_message(service, sconn); } if (service_connection_first(service) == NULL) { /* * No connections left, exiting. */ break; } } exit(0); } Index: head/lib/libcasper/services/cap_dns/Makefile =================================================================== --- head/lib/libcasper/services/cap_dns/Makefile (revision 329451) +++ head/lib/libcasper/services/cap_dns/Makefile (revision 329452) @@ -1,37 +1,37 @@ # $FreeBSD$ SHLIBDIR?= /lib/casper .include PACKAGE=libcasper -SHLIB_MAJOR= 0 +SHLIB_MAJOR= 1 INCSDIR?= ${INCLUDEDIR}/casper .if ${MK_CASPER} != "no" SHLIB= cap_dns SRCS= cap_dns.c .endif INCS= cap_dns.h LIBADD= nv CFLAGS+=-I${.CURDIR} HAS_TESTS= SUBDIR.${MK_TESTS}+= tests MAN+= cap_dns.3 MLINKS+=cap_dns.3 libcap_dns.3 MLINKS+=cap_dns.3 cap_gethostbyname.3 MLINKS+=cap_dns.3 cap_gethostbyname2.3 MLINKS+=cap_dns.3 cap_gethostbyaddr.3 MLINKS+=cap_dns.3 cap_getnameinfo.3 MLINKS+=cap_dns.3 cap_dns_type_limit.3 MLINKS+=cap_dns.3 cap_dns_family_limit.3 .include Index: head/lib/libcasper/services/cap_dns/cap_dns.c =================================================================== --- head/lib/libcasper/services/cap_dns/cap_dns.c (revision 329451) +++ head/lib/libcasper/services/cap_dns/cap_dns.c (revision 329452) @@ -1,764 +1,764 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include "cap_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; char nvlname[64]; int n; 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++) { n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii); assert(n > 0 && n < (int)sizeof(nvlname)); hp->h_aliases[ii] = strdup(nvlist_get_string(nvl, nvlname)); 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; n = snprintf(nvlname, sizeof(nvlname), "addr%u", ii); assert(n > 0 && n < (int)sizeof(nvlname)); bcopy(nvlist_get_binary(nvl, nvlname, NULL), 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, 0); + 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, 0); + 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 = dnvlist_get_string(nvl, "ai_canonname", NULL); 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; char nvlname[64]; nvlist_t *nvl; int error, n; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", "getaddrinfo"); if (hostname != NULL) nvlist_add_string(nvl, "hostname", hostname); if (servname != NULL) 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, 0); + 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++) { n = snprintf(nvlname, sizeof(nvlname), "res%u", ii); assert(n > 0 && n < (int)sizeof(nvlname)); if (!nvlist_exists_nvlist(nvl, nvlname)) break; nvlai = nvlist_get_nvlist(nvl, nvlname); curai = addrinfo_unpack(nvlai); if (curai == NULL) break; if (prevai != NULL) prevai->ai_next = curai; else if (firstai == NULL) firstai = curai; prevai = 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, 0); + 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 && nvlist_exists_string(nvl, "host")) strlcpy(host, nvlist_get_string(nvl, "host"), hostlen + 1); if (serv != NULL && nvlist_exists_string(nvl, "serv")) 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; char nvlname[64]; int n; 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++) { n = snprintf(nvlname, sizeof(nvlname), "type%u", i); assert(n > 0 && n < (int)sizeof(nvlname)); nvlist_add_string(limits, nvlname, types[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; char nvlname[64]; int n; 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++) { n = snprintf(nvlname, sizeof(nvlname), "family%u", i); assert(n > 0 && n < (int)sizeof(nvlname)); nvlist_add_number(limits, nvlname, (uint64_t)families[i]); } return (cap_limit_set(chan, limits)); } /* * Service functions. */ 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; char nvlname[64]; int n; 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++) { n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii); assert(n > 0 && n < (int)sizeof(nvlname)); nvlist_add_string(nvl, nvlname, hp->h_aliases[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++) { n = snprintf(nvlname, sizeof(nvlname), "addr%u", ii); assert(n > 0 && n < (int)sizeof(nvlname)); nvlist_add_binary(nvl, nvlname, hp->h_addr_list[ii], (size_t)hp->h_length); } 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)) { error = NO_RECOVERY; goto out; } flags = (int)nvlist_get_number(nvlin, "flags"); error = getnameinfo((struct sockaddr *)&sast, salen, host, hostlen, serv, servlen, flags); if (error != 0) goto out; if (host != NULL) nvlist_move_string(nvlout, "host", host); if (serv != NULL) 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); if (ai->ai_canonname != NULL) 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; char nvlname[64]; nvlist_t *elem; unsigned int ii; int error, family, n; if (!dns_allowed_type(limits, "ADDR")) return (NO_RECOVERY); hostname = dnvlist_get_string(nvlin, "hostname", NULL); servname = dnvlist_get_string(nvlin, "servname", NULL); if (nvlist_exists_number(nvlin, "hints.ai_flags")) { 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; hints.ai_next = 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); n = snprintf(nvlname, sizeof(nvlname), "res%u", ii); assert(n > 0 && n < (int)sizeof(nvlname)); nvlist_move_nvlist(nvlout, nvlname, elem); } 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); } CREATE_SERVICE("system.dns", dns_limit, dns_command, 0); Index: head/lib/libcasper/services/cap_grp/Makefile =================================================================== --- head/lib/libcasper/services/cap_grp/Makefile (revision 329451) +++ head/lib/libcasper/services/cap_grp/Makefile (revision 329452) @@ -1,43 +1,43 @@ # $FreeBSD$ SHLIBDIR?= /lib/casper .include PACKAGE=libcasper -SHLIB_MAJOR= 0 +SHLIB_MAJOR= 1 INCSDIR?= ${INCLUDEDIR}/casper .if ${MK_CASPER} != "no" SHLIB= cap_grp SRCS= cap_grp.c .endif INCS= cap_grp.h LIBADD= nv CFLAGS+=-I${.CURDIR} HAS_TESTS= SUBDIR.${MK_TESTS}+= tests MAN+= cap_grp.3 MLINKS+=cap_grp.3 libcap_grp.3 MLINKS+=cap_grp.3 cap_getgrent.3 MLINKS+=cap_grp.3 cap_getgrnam.3 MLINKS+=cap_grp.3 cap_getgrgid.3 MLINKS+=cap_grp.3 cap_getgrent_r.3 MLINKS+=cap_grp.3 cap_getgrnam_r.3 MLINKS+=cap_grp.3 cap_getgrgid_r.3 MLINKS+=cap_grp.3 cap_setgroupent.3 MLINKS+=cap_grp.3 cap_setgrent.3 MLINKS+=cap_grp.3 cap_endgrent.3 MLINKS+=cap_grp.3 cap_grp_limit_cmds.3 MLINKS+=cap_grp.3 cap_grp_limit_fields.3 MLINKS+=cap_grp.3 cap_grp_limit_groups.3 .include Index: head/lib/libcasper/services/cap_grp/cap_grp.c =================================================================== --- head/lib/libcasper/services/cap_grp/cap_grp.c (revision 329451) +++ head/lib/libcasper/services/cap_grp/cap_grp.c (revision 329452) @@ -1,791 +1,791 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include "cap_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, nvlname[64]; size_t nmem, datasize, strsize; unsigned int ii; int n; 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++) { n = snprintf(nvlname, sizeof(nvlname), "gr_mem[%u]", ii); assert(n > 0 && n < (int)sizeof(nvlname)); mem = dnvlist_get_string(nvl, nvlname, NULL); 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++) { n = snprintf(nvlname, sizeof(nvlname), "gr_mem[%u]", ii); assert(n > 0 && n < (int)sizeof(nvlname)); mem = nvlist_get_string(nvl, nvlname); 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, 0); + 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, 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, 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); } 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, 0)); + 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; char nvlname[64]; int n; 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++) { n = snprintf(nvlname, sizeof(nvlname), "gid%u", i); assert(n > 0 && n < (int)sizeof(nvlname)); nvlist_add_number(groups, nvlname, (uint64_t)gids[i]); } for (i = 0; i < nnames; i++) { n = snprintf(nvlname, sizeof(nvlname), "gid%u", i); assert(n > 0 && n < (int)sizeof(nvlname)); nvlist_add_string(groups, nvlname, names[i]); } nvlist_move_nvlist(limits, "groups", groups); return (cap_limit_set(chan, limits)); } /* * Service functions. */ 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: abort(); } } 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) { char nvlname[64]; int n; 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++) { n = snprintf(nvlname, sizeof(nvlname), "gr_mem[%u]", ngroups); assert(n > 0 && n < (ssize_t)sizeof(nvlname)); nvlist_add_string(nvl, nvlname, grp->gr_mem[ngroups]); } nvlist_add_number(nvl, "gr_nmem", (uint64_t)ngroups); } return (true); } static int grp_getgrent(const nvlist_t *limits, const nvlist_t *nvlin __unused, 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"); 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 __unused, const nvlist_t *nvlin, nvlist_t *nvlout __unused) { 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 __unused, const nvlist_t *nvlin __unused, nvlist_t *nvlout __unused) { setgrent(); return (0); } static int grp_endgrent(const nvlist_t *limits __unused, const nvlist_t *nvlin __unused, nvlist_t *nvlout __unused) { 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); } CREATE_SERVICE("system.grp", grp_limit, grp_command, 0); Index: head/lib/libcasper/services/cap_pwd/Makefile =================================================================== --- head/lib/libcasper/services/cap_pwd/Makefile (revision 329451) +++ head/lib/libcasper/services/cap_pwd/Makefile (revision 329452) @@ -1,27 +1,27 @@ # $FreeBSD$ SHLIBDIR?= /lib/casper .include PACKAGE=libcasper -SHLIB_MAJOR= 0 +SHLIB_MAJOR= 1 INCSDIR?= ${INCLUDEDIR}/casper .if ${MK_CASPER} != "no" SHLIB= cap_pwd SRCS= cap_pwd.c .endif INCS= cap_pwd.h LIBADD= nv CFLAGS+=-I${.CURDIR} HAS_TESTS= SUBDIR.${MK_TESTS}+= tests .include Index: head/lib/libcasper/services/cap_pwd/cap_pwd.c =================================================================== --- head/lib/libcasper/services/cap_pwd/cap_pwd.c (revision 329451) +++ head/lib/libcasper/services/cap_pwd/cap_pwd.c (revision 329452) @@ -1,785 +1,785 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include "cap_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, 0); + 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, 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, 0)); + 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; char nvlname[64]; unsigned int i; int n; 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++) { n = snprintf(nvlname, sizeof(nvlname), "uid%u", i); assert(n > 0 && n < (int)sizeof(nvlname)); nvlist_add_number(users, nvlname, (uint64_t)uids[i]); } for (i = 0; i < nnames; i++) { n = snprintf(nvlname, sizeof(nvlname), "name%u", i); assert(n > 0 && n < (int)sizeof(nvlname)); nvlist_add_string(users, nvlname, names[i]); } nvlist_move_nvlist(limits, "users", users); return (cap_limit_set(chan, limits)); } /* * Service functions. */ 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: abort(); } } 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 __unused, 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"); 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 __unused, const nvlist_t *nvlin, nvlist_t *nvlout __unused) { 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 __unused, const nvlist_t *nvlin __unused, nvlist_t *nvlout __unused) { setpwent(); return (0); } static int pwd_endpwent(const nvlist_t *limits __unused, const nvlist_t *nvlin __unused, nvlist_t *nvlout __unused) { 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); } CREATE_SERVICE("system.pwd", pwd_limit, pwd_command, 0); Index: head/lib/libcasper/services/cap_random/Makefile =================================================================== --- head/lib/libcasper/services/cap_random/Makefile (revision 329451) +++ head/lib/libcasper/services/cap_random/Makefile (revision 329452) @@ -1,29 +1,29 @@ # $FreeBSD$ SHLIBDIR?= /lib/casper .include PACKAGE=libcasper -SHLIB_MAJOR= 0 +SHLIB_MAJOR= 1 INCSDIR?= ${INCLUDEDIR}/casper .if ${MK_CASPER} != "no" SHLIB= cap_random SRCS= cap_random.c .endif INCS= cap_random.h LIBADD= nv CFLAGS+=-I${.CURDIR} MAN+= cap_random.3 MLINKS+=cap_random.3 libcap_random.3 MLINKS+=cap_random.3 cap_random_buf.3 .include Index: head/lib/libcasper/services/cap_random/cap_random.c =================================================================== --- head/lib/libcasper/services/cap_random/cap_random.c (revision 329451) +++ head/lib/libcasper/services/cap_random/cap_random.c (revision 329452) @@ -1,119 +1,119 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "cap_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, 0); + 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); } /* * Service functions. */ static int random_command(const char *cmd, const nvlist_t *limits __unused, 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); } CREATE_SERVICE("system.random", NULL, random_command, 0); Index: head/lib/libcasper/services/cap_sysctl/Makefile =================================================================== --- head/lib/libcasper/services/cap_sysctl/Makefile (revision 329451) +++ head/lib/libcasper/services/cap_sysctl/Makefile (revision 329452) @@ -1,27 +1,27 @@ # $FreeBSD$ SHLIBDIR?= /lib/casper .include PACKAGE=libcasper -SHLIB_MAJOR= 0 +SHLIB_MAJOR= 1 INCSDIR?= ${INCLUDEDIR}/casper .if ${MK_CASPER} != "no" SHLIB= cap_sysctl SRCS= cap_sysctl.c .endif INCS= cap_sysctl.h LIBADD= nv CFLAGS+=-I${.CURDIR} HAS_TESTS= SUBDIR.${MK_TESTS}+= tests .include Index: head/lib/libcasper/services/cap_sysctl/cap_sysctl.c =================================================================== --- head/lib/libcasper/services/cap_sysctl/cap_sysctl.c (revision 329451) +++ head/lib/libcasper/services/cap_sysctl/cap_sysctl.c (revision 329452) @@ -1,295 +1,295 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include "cap_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, 0); + 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); } /* * Service functions. */ 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) { 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 char *name; void *cookie; uint64_t operation; int 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); 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); } CREATE_SERVICE("system.sysctl", sysctl_limit, sysctl_command, 0); Index: head/lib/libcasper/services/cap_syslog/Makefile =================================================================== --- head/lib/libcasper/services/cap_syslog/Makefile (revision 329451) +++ head/lib/libcasper/services/cap_syslog/Makefile (revision 329452) @@ -1,32 +1,32 @@ # $FreeBSD$ SHLIBDIR?= /lib/casper .include PACKAGE=libcasper -SHLIB_MAJOR= 0 +SHLIB_MAJOR= 1 INCSDIR?= ${INCLUDEDIR}/casper .if ${MK_CASPER} != "no" SHLIB= cap_syslog SRCS= cap_syslog.c .endif INCS= cap_syslog.h LIBADD= nv CFLAGS+=-I${.CURDIR} MAN+= cap_syslog.3 MLINKS+= cap_syslog.3 libcap_syslog.3 MLINKS+= cap_syslog.3 cap_vsyslog.3 MLINKS+= cap_syslog.3 cap_openlog.3 MLINKS+= cap_syslog.3 cap_closelog.3 MLINKS+= cap_syslog.3 cap_setlogmask.3 .include Index: head/lib/libcasper/services/cap_syslog/cap_syslog.c =================================================================== --- head/lib/libcasper/services/cap_syslog/cap_syslog.c (revision 329451) +++ head/lib/libcasper/services/cap_syslog/cap_syslog.c (revision 329452) @@ -1,201 +1,201 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2017 Mariusz Zaborski * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include "cap_syslog.h" #define CAP_SYSLOG_LIMIT 2048 void cap_syslog(cap_channel_t *chan, int pri, const char *fmt, ...) { va_list ap; va_start(ap, fmt); cap_vsyslog(chan, pri, fmt, ap); va_end(ap); } void cap_vsyslog(cap_channel_t *chan, int priority, const char *fmt, va_list ap) { nvlist_t *nvl; char message[CAP_SYSLOG_LIMIT]; (void)vsnprintf(message, sizeof(message), fmt, ap); nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", "vsyslog"); nvlist_add_number(nvl, "priority", priority); nvlist_add_string(nvl, "message", message); - nvl = cap_xfer_nvlist(chan, nvl, 0); + nvl = cap_xfer_nvlist(chan, nvl); if (nvl == NULL) { return; } nvlist_destroy(nvl); } void cap_openlog(cap_channel_t *chan, const char *ident, int logopt, int facility) { nvlist_t *nvl; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", "openlog"); if (ident != NULL) { nvlist_add_string(nvl, "ident", ident); } nvlist_add_number(nvl, "logopt", logopt); nvlist_add_number(nvl, "facility", facility); - nvl = cap_xfer_nvlist(chan, nvl, 0); + nvl = cap_xfer_nvlist(chan, nvl); if (nvl == NULL) { return; } nvlist_destroy(nvl); } void cap_closelog(cap_channel_t *chan) { nvlist_t *nvl; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", "closelog"); - nvl = cap_xfer_nvlist(chan, nvl, 0); + nvl = cap_xfer_nvlist(chan, nvl); if (nvl == NULL) { return; } nvlist_destroy(nvl); } int cap_setlogmask(cap_channel_t *chan, int maskpri) { nvlist_t *nvl; int omask; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", "setlogmask"); nvlist_add_number(nvl, "maskpri", maskpri); - nvl = cap_xfer_nvlist(chan, nvl, 0); + nvl = cap_xfer_nvlist(chan, nvl); omask = nvlist_get_number(nvl, "omask"); nvlist_destroy(nvl); return (omask); } /* * Service functions. */ static char *LogTag; static void slog_vsyslog(const nvlist_t *limits __unused, const nvlist_t *nvlin, nvlist_t *nvlout __unused) { syslog(nvlist_get_number(nvlin, "priority"), "%s", nvlist_get_string(nvlin, "message")); } static void slog_openlog(const nvlist_t *limits __unused, const nvlist_t *nvlin, nvlist_t *nvlout __unused) { const char *ident; ident = dnvlist_get_string(nvlin, "ident", NULL); if (ident != NULL) { free(LogTag); LogTag = strdup(ident); } openlog(LogTag, nvlist_get_number(nvlin, "logopt"), nvlist_get_number(nvlin, "facility")); } static void slog_closelog(const nvlist_t *limits __unused, const nvlist_t *nvlin __unused, nvlist_t *nvlout __unused) { closelog(); free(LogTag); LogTag = NULL; } static void slog_setlogmask(const nvlist_t *limits __unused, const nvlist_t *nvlin, nvlist_t *nvlout) { int omask; omask = setlogmask(nvlist_get_number(nvlin, "maskpri")); nvlist_add_number(nvlout, "omask", omask); } static int syslog_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin, nvlist_t *nvlout) { if (strcmp(cmd, "vsyslog") == 0) { slog_vsyslog(limits, nvlin, nvlout); } else if (strcmp(cmd, "openlog") == 0) { slog_openlog(limits, nvlin, nvlout); } else if (strcmp(cmd, "closelog") == 0) { slog_closelog(limits, nvlin, nvlout); } else if (strcmp(cmd, "setlogmask") == 0) { slog_setlogmask(limits, nvlin, nvlout); } else { return (EINVAL); } return (0); } CREATE_SERVICE("system.syslog", NULL, syslog_command, 0);