Index: head/lib/libcasper/libcasper/libcasper.c =================================================================== --- head/lib/libcasper/libcasper/libcasper.c (revision 328472) +++ head/lib/libcasper/libcasper/libcasper.c (revision 328473) @@ -1,337 +1,339 @@ /*- + * 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" /* * 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; }; static bool cap_add_pd(cap_channel_t *chan, int pd) { if (!fd_is_valid(pd)) return (false); chan->cch_pd = pd; return (true); } 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]); 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_channel_t *chan; if (!fd_is_valid(sock)) return (NULL); chan = malloc(sizeof(*chan)); if (chan != NULL) { chan->cch_sock = sock; chan->cch_pd = -1; chan->cch_magic = CAP_CHANNEL_MAGIC; } return (chan); } int cap_unwrap(cap_channel_t *chan) { int sock; assert(chan != NULL); assert(chan->cch_magic == CAP_CHANNEL_MAGIC); sock = chan->cch_sock; if (chan->cch_pd != -1) close(chan->cch_pd); chan->cch_magic = 0; free(chan); return (sock); } cap_channel_t * cap_clone(const cap_channel_t *chan) { cap_channel_t *newchan; nvlist_t *nvl; int newsock; assert(chan != NULL); assert(chan->cch_magic == CAP_CHANNEL_MAGIC); nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", "clone"); nvl = cap_xfer_nvlist(chan, nvl, 0); if (nvl == NULL) return (NULL); if (nvlist_get_number(nvl, "error") != 0) { errno = (int)nvlist_get_number(nvl, "error"); nvlist_destroy(nvl); return (NULL); } newsock = nvlist_take_descriptor(nvl, "sock"); nvlist_destroy(nvl); newchan = cap_wrap(newsock); if (newchan == NULL) { int serrno; serrno = errno; close(newsock); errno = serrno; } return (newchan); } void cap_close(cap_channel_t *chan) { assert(chan != NULL); assert(chan->cch_magic == CAP_CHANNEL_MAGIC); chan->cch_magic = 0; 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); nvlist_add_string(nvlmsg, "cmd", "limit_set"); nvlist_add_nvlist(nvlmsg, "limits", limits); nvlmsg = cap_xfer_nvlist(chan, nvlmsg, 0); if (nvlmsg == NULL) { nvlist_destroy(limits); return (-1); } error = (int)nvlist_get_number(nvlmsg, "error"); nvlist_destroy(nvlmsg); nvlist_destroy(limits); if (error != 0) { errno = error; return (-1); } return (0); } int cap_limit_get(const cap_channel_t *chan, nvlist_t **limitsp) { nvlist_t *nvlmsg; int error; nvlmsg = nvlist_create(0); nvlist_add_string(nvlmsg, "cmd", "limit_get"); nvlmsg = cap_xfer_nvlist(chan, nvlmsg, 0); 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) { assert(chan != NULL); assert(chan->cch_magic == CAP_CHANNEL_MAGIC); return (nvlist_recv(chan->cch_sock, flags)); } nvlist_t * cap_xfer_nvlist(const cap_channel_t *chan, nvlist_t *nvl, int flags) { assert(chan != NULL); assert(chan->cch_magic == CAP_CHANNEL_MAGIC); return (nvlist_xfer(chan->cch_sock, nvl, flags)); } cap_channel_t * cap_service_open(const cap_channel_t *chan, const char *name) { cap_channel_t *newchan; nvlist_t *nvl; int sock, error; sock = -1; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", "open"); nvlist_add_string(nvl, "service", name); nvl = cap_xfer_nvlist(chan, nvl, 0); if (nvl == NULL) return (NULL); error = (int)nvlist_get_number(nvl, "error"); if (error != 0) { nvlist_destroy(nvl); errno = error; return (NULL); } sock = nvlist_take_descriptor(nvl, "chanfd"); assert(sock >= 0); nvlist_destroy(nvl); nvl = NULL; newchan = cap_wrap(sock); if (newchan == NULL) goto fail; return (newchan); fail: error = errno; close(sock); errno = error; return (NULL); } int cap_service_limit(const cap_channel_t *chan, const char * const *names, size_t nnames) { nvlist_t *limits; unsigned int i; limits = nvlist_create(0); for (i = 0; i < nnames; i++) nvlist_add_null(limits, names[i]); return (cap_limit_set(chan, limits)); } Index: head/lib/libcasper/libcasper/libcasper.h =================================================================== --- head/lib/libcasper/libcasper/libcasper.h (revision 328472) +++ head/lib/libcasper/libcasper/libcasper.h (revision 328473) @@ -1,252 +1,254 @@ /*- + * 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 #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; }; typedef struct cap_channel cap_channel_t; #define CASPER_SUPPORT (0) #endif /* ! WITH_CASPER */ #endif /* ! _CAP_CHANNEL_T_DECLARED */ /* * 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); #else static inline cap_channel_t * cap_wrap(int sock) { cap_channel_t *chan; chan = cap_init(); if (chan != NULL) { chan->cch_fd = sock; } return (chan); } #endif /* * The function returns communication socket and frees cap_channel_t. */ #ifdef WITH_CASPER int cap_unwrap(cap_channel_t *chan); #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; } } 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); 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); #else #define cap_recv_nvlist(chan, flags) (0) #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); #else static inline nvlist_t * cap_xfer_nvlist(const cap_channel_t *chan __unused, nvlist_t *nvl, int flags) { nvlist_destroy(nvl); return (nvlist_create(flags)); } #endif #endif /* !_LIBCASPER_H_ */ Index: head/lib/libcasper/libcasper/libcasper_impl.c =================================================================== --- head/lib/libcasper/libcasper/libcasper_impl.c (revision 328472) +++ head/lib/libcasper/libcasper/libcasper_impl.c (revision 328473) @@ -1,44 +1,46 @@ /*- + * 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$ */ #include #include #include #include "libcasper_impl.h" bool fd_is_valid(int fd) { return (fcntl(fd, F_GETFL) != -1 || errno != EBADF); } Index: head/lib/libcasper/libcasper/libcasper_impl.h =================================================================== --- head/lib/libcasper/libcasper/libcasper_impl.h (revision 328472) +++ head/lib/libcasper/libcasper/libcasper_impl.h (revision 328473) @@ -1,82 +1,84 @@ /*- + * 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); /* 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 328472) +++ head/lib/libcasper/libcasper/libcasper_service.c (revision 328473) @@ -1,288 +1,290 @@ /*- + * 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); 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 328472) +++ head/lib/libcasper/libcasper/libcasper_service.h (revision 328473) @@ -1,64 +1,66 @@ /*- + * 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 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 328472) +++ head/lib/libcasper/libcasper/service.c (revision 328473) @@ -1,451 +1,453 @@ /*- + * 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); 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); 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; nvlin = cap_recv_nvlist(service_connection_get_chan(sconn), 0); if (nvlin == NULL) { service_connection_remove(service, sconn); return; } error = EDOOFUS; nvlout = nvlist_create(0); 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); } 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/libcasper/zygote.c =================================================================== --- head/lib/libcasper/libcasper/zygote.c (revision 328472) +++ head/lib/libcasper/libcasper/zygote.c (revision 328473) @@ -1,217 +1,219 @@ /*- + * 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 * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * All rights reserved. * 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 #include "zygote.h" /* Zygote info. */ static int zygote_sock = -1; #define ZYGOTE_SERVICE_EXECUTE 1 int zygote_clone(uint64_t funcidx, int *chanfdp, int *procfdp) { nvlist_t *nvl; int error; if (zygote_sock == -1) { /* Zygote didn't start. */ errno = ENXIO; return (-1); } nvl = nvlist_create(0); nvlist_add_number(nvl, "funcidx", funcidx); nvl = nvlist_xfer(zygote_sock, nvl, 0); if (nvl == NULL) return (-1); if (nvlist_exists_number(nvl, "error")) { error = (int)nvlist_get_number(nvl, "error"); nvlist_destroy(nvl); errno = error; return (-1); } *chanfdp = nvlist_take_descriptor(nvl, "chanfd"); *procfdp = nvlist_take_descriptor(nvl, "procfd"); nvlist_destroy(nvl); return (0); } int zygote_clone_service_execute(int *chanfdp, int *procfdp) { return (zygote_clone(ZYGOTE_SERVICE_EXECUTE, chanfdp, procfdp)); } /* * This function creates sandboxes on-demand whoever has access to it via * 'sock' socket. Function sends two descriptors to the caller: process * descriptor of the sandbox and socket pair descriptor for communication * between sandbox and its owner. */ static void zygote_main(int sock) { int error, procfd; int chanfd[2]; nvlist_t *nvlin, *nvlout; uint64_t funcidx; zygote_func_t *func; pid_t pid; assert(sock > STDERR_FILENO); setproctitle("zygote"); for (;;) { nvlin = nvlist_recv(sock, 0); if (nvlin == NULL) { if (errno == ENOTCONN) { /* Casper exited. */ exit(0); } continue; } funcidx = nvlist_get_number(nvlin, "funcidx"); nvlist_destroy(nvlin); switch (funcidx) { case ZYGOTE_SERVICE_EXECUTE: func = service_execute; break; default: exit(0); } /* * Someone is requesting a new process, create one. */ procfd = -1; chanfd[0] = -1; chanfd[1] = -1; error = 0; if (socketpair(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, chanfd) == -1) { error = errno; goto send; } pid = pdfork(&procfd, 0); switch (pid) { case -1: /* Failure. */ error = errno; break; case 0: /* Child. */ close(sock); close(chanfd[0]); func(chanfd[1]); /* NOTREACHED */ exit(1); default: /* Parent. */ close(chanfd[1]); break; } send: nvlout = nvlist_create(0); if (error != 0) { nvlist_add_number(nvlout, "error", (uint64_t)error); if (chanfd[0] >= 0) close(chanfd[0]); if (procfd >= 0) close(procfd); } else { nvlist_move_descriptor(nvlout, "chanfd", chanfd[0]); nvlist_move_descriptor(nvlout, "procfd", procfd); } (void)nvlist_send(sock, nvlout); nvlist_destroy(nvlout); } /* NOTREACHED */ } int zygote_init(void) { int serrno, sp[2]; pid_t pid; if (socketpair(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, sp) == -1) return (-1); pid = fork(); switch (pid) { case -1: /* Failure. */ serrno = errno; close(sp[0]); close(sp[1]); errno = serrno; return (-1); case 0: /* Child. */ close(sp[0]); zygote_main(sp[1]); /* NOTREACHED */ abort(); default: /* Parent. */ zygote_sock = sp[0]; close(sp[1]); return (0); } /* NOTREACHED */ } Index: head/lib/libcasper/libcasper/zygote.h =================================================================== --- head/lib/libcasper/libcasper/zygote.h (revision 328472) +++ head/lib/libcasper/libcasper/zygote.h (revision 328473) @@ -1,47 +1,49 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2012 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 _ZYGOTE_H_ #define _ZYGOTE_H_ typedef void zygote_func_t(int); int zygote_init(void); int zygote_clone(uint64_t funcidx, int *chanfdp, int *procfdp); int zygote_clone_service_execute(int *chanfdp, int *procfdp); /* * Functions reachable via zygote_clone(). */ zygote_func_t service_execute; #endif /* !_ZYGOTE_H_ */ Index: head/lib/libcasper/services/cap_dns/cap_dns.c =================================================================== --- head/lib/libcasper/services/cap_dns/cap_dns.c (revision 328472) +++ head/lib/libcasper/services/cap_dns/cap_dns.c (revision 328473) @@ -1,762 +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); 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); 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); 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); 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_dns/tests/dns_test.c =================================================================== --- head/lib/libcasper/services/cap_dns/tests/dns_test.c (revision 328472) +++ head/lib/libcasper/services/cap_dns/tests/dns_test.c (revision 328473) @@ -1,700 +1,702 @@ /*- + * 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 #include #include #include static int ntest = 1; #define CHECK(expr) do { \ if ((expr)) \ printf("ok %d # %s:%u\n", ntest, __FILE__, __LINE__); \ else \ printf("not ok %d # %s:%u\n", ntest, __FILE__, __LINE__); \ fflush(stdout); \ ntest++; \ } while (0) #define CHECKX(expr) do { \ if ((expr)) { \ printf("ok %d # %s:%u\n", ntest, __FILE__, __LINE__); \ } else { \ printf("not ok %d # %s:%u\n", ntest, __FILE__, __LINE__); \ exit(1); \ } \ fflush(stdout); \ ntest++; \ } while (0) #define GETHOSTBYNAME 0x01 #define GETHOSTBYNAME2_AF_INET 0x02 #define GETHOSTBYNAME2_AF_INET6 0x04 #define GETHOSTBYADDR_AF_INET 0x08 #define GETHOSTBYADDR_AF_INET6 0x10 #define GETADDRINFO_AF_UNSPEC 0x20 #define GETADDRINFO_AF_INET 0x40 #define GETADDRINFO_AF_INET6 0x80 static bool addrinfo_compare(struct addrinfo *ai0, struct addrinfo *ai1) { struct addrinfo *at0, *at1; if (ai0 == NULL && ai1 == NULL) return (true); if (ai0 == NULL || ai1 == NULL) return (false); at0 = ai0; at1 = ai1; while (true) { if ((at0->ai_flags == at1->ai_flags) && (at0->ai_family == at1->ai_family) && (at0->ai_socktype == at1->ai_socktype) && (at0->ai_protocol == at1->ai_protocol) && (at0->ai_addrlen == at1->ai_addrlen) && (memcmp(at0->ai_addr, at1->ai_addr, at0->ai_addrlen) == 0)) { if (at0->ai_canonname != NULL && at1->ai_canonname != NULL) { if (strcmp(at0->ai_canonname, at1->ai_canonname) != 0) { return (false); } } if (at0->ai_canonname == NULL && at1->ai_canonname != NULL) { return (false); } if (at0->ai_canonname != NULL && at1->ai_canonname == NULL) { return (false); } if (at0->ai_next == NULL && at1->ai_next == NULL) return (true); if (at0->ai_next == NULL || at1->ai_next == NULL) return (false); at0 = at0->ai_next; at1 = at1->ai_next; } else { return (false); } } /* NOTREACHED */ fprintf(stderr, "Dead code reached in addrinfo_compare()\n"); exit(1); } static bool hostent_aliases_compare(char **aliases0, char **aliases1) { int i0, i1; if (aliases0 == NULL && aliases1 == NULL) return (true); if (aliases0 == NULL || aliases1 == NULL) return (false); for (i0 = 0; aliases0[i0] != NULL; i0++) { for (i1 = 0; aliases1[i1] != NULL; i1++) { if (strcmp(aliases0[i0], aliases1[i1]) == 0) break; } if (aliases1[i1] == NULL) return (false); } return (true); } static bool hostent_addr_list_compare(char **addr_list0, char **addr_list1, int length) { int i0, i1; if (addr_list0 == NULL && addr_list1 == NULL) return (true); if (addr_list0 == NULL || addr_list1 == NULL) return (false); for (i0 = 0; addr_list0[i0] != NULL; i0++) { for (i1 = 0; addr_list1[i1] != NULL; i1++) { if (memcmp(addr_list0[i0], addr_list1[i1], length) == 0) break; } if (addr_list1[i1] == NULL) return (false); } return (true); } static bool hostent_compare(const struct hostent *hp0, const struct hostent *hp1) { if (hp0 == NULL && hp1 != NULL) return (true); if (hp0 == NULL || hp1 == NULL) return (false); if (hp0->h_name != NULL || hp1->h_name != NULL) { if (hp0->h_name == NULL || hp1->h_name == NULL) return (false); if (strcmp(hp0->h_name, hp1->h_name) != 0) return (false); } if (!hostent_aliases_compare(hp0->h_aliases, hp1->h_aliases)) return (false); if (!hostent_aliases_compare(hp1->h_aliases, hp0->h_aliases)) return (false); if (hp0->h_addrtype != hp1->h_addrtype) return (false); if (hp0->h_length != hp1->h_length) return (false); if (!hostent_addr_list_compare(hp0->h_addr_list, hp1->h_addr_list, hp0->h_length)) { return (false); } if (!hostent_addr_list_compare(hp1->h_addr_list, hp0->h_addr_list, hp0->h_length)) { return (false); } return (true); } static unsigned int runtest(cap_channel_t *capdns) { unsigned int result; struct addrinfo *ais, *aic, hints, *hintsp; struct hostent *hps, *hpc; struct in_addr ip4; struct in6_addr ip6; result = 0; hps = gethostbyname("example.com"); if (hps == NULL) fprintf(stderr, "Unable to resolve %s IPv4.\n", "example.com"); hpc = cap_gethostbyname(capdns, "example.com"); if (hostent_compare(hps, hpc)) result |= GETHOSTBYNAME; hps = gethostbyname2("example.com", AF_INET); if (hps == NULL) fprintf(stderr, "Unable to resolve %s IPv4.\n", "example.com"); hpc = cap_gethostbyname2(capdns, "example.com", AF_INET); if (hostent_compare(hps, hpc)) result |= GETHOSTBYNAME2_AF_INET; hps = gethostbyname2("example.com", AF_INET6); if (hps == NULL) fprintf(stderr, "Unable to resolve %s IPv6.\n", "example.com"); hpc = cap_gethostbyname2(capdns, "example.com", AF_INET6); if (hostent_compare(hps, hpc)) result |= GETHOSTBYNAME2_AF_INET6; hints.ai_flags = 0; hints.ai_family = AF_UNSPEC; hints.ai_socktype = 0; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_addr = NULL; hints.ai_canonname = NULL; hints.ai_next = NULL; hintsp = &hints; if (getaddrinfo("freebsd.org", "25", hintsp, &ais) != 0) { fprintf(stderr, "Unable to issue [system] getaddrinfo() for AF_UNSPEC: %s\n", gai_strerror(errno)); } if (cap_getaddrinfo(capdns, "freebsd.org", "25", hintsp, &aic) == 0) { if (addrinfo_compare(ais, aic)) result |= GETADDRINFO_AF_UNSPEC; freeaddrinfo(ais); freeaddrinfo(aic); } hints.ai_family = AF_INET; if (getaddrinfo("freebsd.org", "25", hintsp, &ais) != 0) { fprintf(stderr, "Unable to issue [system] getaddrinfo() for AF_UNSPEC: %s\n", gai_strerror(errno)); } if (cap_getaddrinfo(capdns, "freebsd.org", "25", hintsp, &aic) == 0) { if (addrinfo_compare(ais, aic)) result |= GETADDRINFO_AF_INET; freeaddrinfo(ais); freeaddrinfo(aic); } hints.ai_family = AF_INET6; if (getaddrinfo("freebsd.org", "25", hintsp, &ais) != 0) { fprintf(stderr, "Unable to issue [system] getaddrinfo() for AF_UNSPEC: %s\n", gai_strerror(errno)); } if (cap_getaddrinfo(capdns, "freebsd.org", "25", hintsp, &aic) == 0) { if (addrinfo_compare(ais, aic)) result |= GETADDRINFO_AF_INET6; freeaddrinfo(ais); freeaddrinfo(aic); } /* XXX: hardcoded addresses for "google-public-dns-a.google.com". */ #define GOOGLE_DNS_IPV4 "8.8.8.8" #define GOOGLE_DNS_IPV6 "2001:4860:4860::8888" inet_pton(AF_INET, GOOGLE_DNS_IPV4, &ip4); hps = gethostbyaddr(&ip4, sizeof(ip4), AF_INET); if (hps == NULL) fprintf(stderr, "Unable to resolve %s.\n", GOOGLE_DNS_IPV4); hpc = cap_gethostbyaddr(capdns, &ip4, sizeof(ip4), AF_INET); if (hostent_compare(hps, hpc)) result |= GETHOSTBYADDR_AF_INET; inet_pton(AF_INET6, GOOGLE_DNS_IPV6, &ip6); hps = gethostbyaddr(&ip6, sizeof(ip6), AF_INET6); if (hps == NULL) { fprintf(stderr, "Unable to resolve %s.\n", GOOGLE_DNS_IPV6); } hpc = cap_gethostbyaddr(capdns, &ip6, sizeof(ip6), AF_INET6); if (hostent_compare(hps, hpc)) result |= GETHOSTBYADDR_AF_INET6; return (result); } int main(void) { cap_channel_t *capcas, *capdns, *origcapdns; const char *types[2]; int families[2]; printf("1..91\n"); fflush(stdout); capcas = cap_init(); CHECKX(capcas != NULL); origcapdns = capdns = cap_service_open(capcas, "system.dns"); CHECKX(capdns != NULL); cap_close(capcas); /* No limits set. */ CHECK(runtest(capdns) == (GETHOSTBYNAME | GETHOSTBYNAME2_AF_INET | GETHOSTBYNAME2_AF_INET6 | GETHOSTBYADDR_AF_INET | GETHOSTBYADDR_AF_INET6 | GETADDRINFO_AF_UNSPEC | GETADDRINFO_AF_INET | GETADDRINFO_AF_INET6)); /* * Allow: * type: NAME, ADDR * family: AF_INET, AF_INET6 */ capdns = cap_clone(origcapdns); CHECK(capdns != NULL); types[0] = "NAME"; types[1] = "ADDR"; CHECK(cap_dns_type_limit(capdns, types, 2) == 0); families[0] = AF_INET; families[1] = AF_INET6; CHECK(cap_dns_family_limit(capdns, families, 2) == 0); CHECK(runtest(capdns) == (GETHOSTBYNAME | GETHOSTBYNAME2_AF_INET | GETHOSTBYNAME2_AF_INET6 | GETHOSTBYADDR_AF_INET | GETHOSTBYADDR_AF_INET6 | GETADDRINFO_AF_INET | GETADDRINFO_AF_INET6)); cap_close(capdns); /* * Allow: * type: NAME * family: AF_INET, AF_INET6 */ capdns = cap_clone(origcapdns); CHECK(capdns != NULL); types[0] = "NAME"; CHECK(cap_dns_type_limit(capdns, types, 1) == 0); types[1] = "ADDR"; CHECK(cap_dns_type_limit(capdns, types, 2) == -1 && errno == ENOTCAPABLE); types[0] = "ADDR"; CHECK(cap_dns_type_limit(capdns, types, 1) == -1 && errno == ENOTCAPABLE); families[0] = AF_INET; families[1] = AF_INET6; CHECK(cap_dns_family_limit(capdns, families, 2) == 0); CHECK(runtest(capdns) == (GETHOSTBYNAME | GETHOSTBYNAME2_AF_INET | GETHOSTBYNAME2_AF_INET6)); cap_close(capdns); /* * Allow: * type: ADDR * family: AF_INET, AF_INET6 */ capdns = cap_clone(origcapdns); CHECK(capdns != NULL); types[0] = "ADDR"; CHECK(cap_dns_type_limit(capdns, types, 1) == 0); types[1] = "NAME"; CHECK(cap_dns_type_limit(capdns, types, 2) == -1 && errno == ENOTCAPABLE); types[0] = "NAME"; CHECK(cap_dns_type_limit(capdns, types, 1) == -1 && errno == ENOTCAPABLE); families[0] = AF_INET; families[1] = AF_INET6; CHECK(cap_dns_family_limit(capdns, families, 2) == 0); CHECK(runtest(capdns) == (GETHOSTBYADDR_AF_INET | GETHOSTBYADDR_AF_INET6 | GETADDRINFO_AF_INET | GETADDRINFO_AF_INET6)); cap_close(capdns); /* * Allow: * type: NAME, ADDR * family: AF_INET */ capdns = cap_clone(origcapdns); CHECK(capdns != NULL); types[0] = "NAME"; types[1] = "ADDR"; CHECK(cap_dns_type_limit(capdns, types, 2) == 0); families[0] = AF_INET; CHECK(cap_dns_family_limit(capdns, families, 1) == 0); families[1] = AF_INET6; CHECK(cap_dns_family_limit(capdns, families, 2) == -1 && errno == ENOTCAPABLE); families[0] = AF_INET6; CHECK(cap_dns_family_limit(capdns, families, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capdns) == (GETHOSTBYNAME | GETHOSTBYNAME2_AF_INET | GETHOSTBYADDR_AF_INET | GETADDRINFO_AF_INET)); cap_close(capdns); /* * Allow: * type: NAME, ADDR * family: AF_INET6 */ capdns = cap_clone(origcapdns); CHECK(capdns != NULL); types[0] = "NAME"; types[1] = "ADDR"; CHECK(cap_dns_type_limit(capdns, types, 2) == 0); families[0] = AF_INET6; CHECK(cap_dns_family_limit(capdns, families, 1) == 0); families[1] = AF_INET; CHECK(cap_dns_family_limit(capdns, families, 2) == -1 && errno == ENOTCAPABLE); families[0] = AF_INET; CHECK(cap_dns_family_limit(capdns, families, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capdns) == (GETHOSTBYNAME2_AF_INET6 | GETHOSTBYADDR_AF_INET6 | GETADDRINFO_AF_INET6)); cap_close(capdns); /* Below we also test further limiting capability. */ /* * Allow: * type: NAME * family: AF_INET */ capdns = cap_clone(origcapdns); CHECK(capdns != NULL); types[0] = "NAME"; types[1] = "ADDR"; CHECK(cap_dns_type_limit(capdns, types, 2) == 0); families[0] = AF_INET; families[1] = AF_INET6; CHECK(cap_dns_family_limit(capdns, families, 2) == 0); types[0] = "NAME"; CHECK(cap_dns_type_limit(capdns, types, 1) == 0); types[1] = "ADDR"; CHECK(cap_dns_type_limit(capdns, types, 2) == -1 && errno == ENOTCAPABLE); types[0] = "ADDR"; CHECK(cap_dns_type_limit(capdns, types, 1) == -1 && errno == ENOTCAPABLE); families[0] = AF_INET; CHECK(cap_dns_family_limit(capdns, families, 1) == 0); families[1] = AF_INET6; CHECK(cap_dns_family_limit(capdns, families, 2) == -1 && errno == ENOTCAPABLE); families[0] = AF_INET6; CHECK(cap_dns_family_limit(capdns, families, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capdns) == (GETHOSTBYNAME | GETHOSTBYNAME2_AF_INET)); cap_close(capdns); /* * Allow: * type: NAME * family: AF_INET6 */ capdns = cap_clone(origcapdns); CHECK(capdns != NULL); types[0] = "NAME"; types[1] = "ADDR"; CHECK(cap_dns_type_limit(capdns, types, 2) == 0); families[0] = AF_INET; families[1] = AF_INET6; CHECK(cap_dns_family_limit(capdns, families, 2) == 0); types[0] = "NAME"; CHECK(cap_dns_type_limit(capdns, types, 1) == 0); types[1] = "ADDR"; CHECK(cap_dns_type_limit(capdns, types, 2) == -1 && errno == ENOTCAPABLE); types[0] = "ADDR"; CHECK(cap_dns_type_limit(capdns, types, 1) == -1 && errno == ENOTCAPABLE); families[0] = AF_INET6; CHECK(cap_dns_family_limit(capdns, families, 1) == 0); families[1] = AF_INET; CHECK(cap_dns_family_limit(capdns, families, 2) == -1 && errno == ENOTCAPABLE); families[0] = AF_INET; CHECK(cap_dns_family_limit(capdns, families, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capdns) == GETHOSTBYNAME2_AF_INET6); cap_close(capdns); /* * Allow: * type: ADDR * family: AF_INET */ capdns = cap_clone(origcapdns); CHECK(capdns != NULL); types[0] = "NAME"; types[1] = "ADDR"; CHECK(cap_dns_type_limit(capdns, types, 2) == 0); families[0] = AF_INET; families[1] = AF_INET6; CHECK(cap_dns_family_limit(capdns, families, 2) == 0); types[0] = "ADDR"; CHECK(cap_dns_type_limit(capdns, types, 1) == 0); types[1] = "NAME"; CHECK(cap_dns_type_limit(capdns, types, 2) == -1 && errno == ENOTCAPABLE); types[0] = "NAME"; CHECK(cap_dns_type_limit(capdns, types, 1) == -1 && errno == ENOTCAPABLE); families[0] = AF_INET; CHECK(cap_dns_family_limit(capdns, families, 1) == 0); families[1] = AF_INET6; CHECK(cap_dns_family_limit(capdns, families, 2) == -1 && errno == ENOTCAPABLE); families[0] = AF_INET6; CHECK(cap_dns_family_limit(capdns, families, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capdns) == (GETHOSTBYADDR_AF_INET | GETADDRINFO_AF_INET)); cap_close(capdns); /* * Allow: * type: ADDR * family: AF_INET6 */ capdns = cap_clone(origcapdns); CHECK(capdns != NULL); types[0] = "NAME"; types[1] = "ADDR"; CHECK(cap_dns_type_limit(capdns, types, 2) == 0); families[0] = AF_INET; families[1] = AF_INET6; CHECK(cap_dns_family_limit(capdns, families, 2) == 0); types[0] = "ADDR"; CHECK(cap_dns_type_limit(capdns, types, 1) == 0); types[1] = "NAME"; CHECK(cap_dns_type_limit(capdns, types, 2) == -1 && errno == ENOTCAPABLE); types[0] = "NAME"; CHECK(cap_dns_type_limit(capdns, types, 1) == -1 && errno == ENOTCAPABLE); families[0] = AF_INET6; CHECK(cap_dns_family_limit(capdns, families, 1) == 0); families[1] = AF_INET; CHECK(cap_dns_family_limit(capdns, families, 2) == -1 && errno == ENOTCAPABLE); families[0] = AF_INET; CHECK(cap_dns_family_limit(capdns, families, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capdns) == (GETHOSTBYADDR_AF_INET6 | GETADDRINFO_AF_INET6)); cap_close(capdns); /* Trying to rise the limits. */ capdns = cap_clone(origcapdns); CHECK(capdns != NULL); types[0] = "NAME"; CHECK(cap_dns_type_limit(capdns, types, 1) == 0); families[0] = AF_INET; CHECK(cap_dns_family_limit(capdns, families, 1) == 0); types[0] = "NAME"; types[1] = "ADDR"; CHECK(cap_dns_type_limit(capdns, types, 2) == -1 && errno == ENOTCAPABLE); families[0] = AF_INET; families[1] = AF_INET6; CHECK(cap_dns_family_limit(capdns, families, 2) == -1 && errno == ENOTCAPABLE); types[0] = "ADDR"; CHECK(cap_dns_type_limit(capdns, types, 1) == -1 && errno == ENOTCAPABLE); families[0] = AF_INET6; CHECK(cap_dns_family_limit(capdns, families, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_dns_type_limit(capdns, NULL, 0) == -1 && errno == ENOTCAPABLE); CHECK(cap_dns_family_limit(capdns, NULL, 0) == -1 && errno == ENOTCAPABLE); /* Do the limits still hold? */ CHECK(runtest(capdns) == (GETHOSTBYNAME | GETHOSTBYNAME2_AF_INET)); cap_close(capdns); capdns = cap_clone(origcapdns); CHECK(capdns != NULL); types[0] = "ADDR"; CHECK(cap_dns_type_limit(capdns, types, 1) == 0); families[0] = AF_INET6; CHECK(cap_dns_family_limit(capdns, families, 1) == 0); types[0] = "NAME"; types[1] = "ADDR"; CHECK(cap_dns_type_limit(capdns, types, 2) == -1 && errno == ENOTCAPABLE); families[0] = AF_INET; families[1] = AF_INET6; CHECK(cap_dns_family_limit(capdns, families, 2) == -1 && errno == ENOTCAPABLE); types[0] = "NAME"; CHECK(cap_dns_type_limit(capdns, types, 1) == -1 && errno == ENOTCAPABLE); families[0] = AF_INET; CHECK(cap_dns_family_limit(capdns, families, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_dns_type_limit(capdns, NULL, 0) == -1 && errno == ENOTCAPABLE); CHECK(cap_dns_family_limit(capdns, NULL, 0) == -1 && errno == ENOTCAPABLE); /* Do the limits still hold? */ CHECK(runtest(capdns) == (GETHOSTBYADDR_AF_INET6 | GETADDRINFO_AF_INET6)); cap_close(capdns); cap_close(origcapdns); exit(0); } Index: head/lib/libcasper/services/cap_grp/cap_grp.c =================================================================== --- head/lib/libcasper/services/cap_grp/cap_grp.c (revision 328472) +++ head/lib/libcasper/services/cap_grp/cap_grp.c (revision 328473) @@ -1,789 +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); 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); 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); 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)); } 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_grp/tests/grp_test.c =================================================================== --- head/lib/libcasper/services/cap_grp/tests/grp_test.c (revision 328472) +++ head/lib/libcasper/services/cap_grp/tests/grp_test.c (revision 328473) @@ -1,1554 +1,1556 @@ /*- + * 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 #include static int ntest = 1; #define CHECK(expr) do { \ if ((expr)) \ printf("ok %d %s:%u\n", ntest, __FILE__, __LINE__); \ else \ printf("not ok %d %s:%u\n", ntest, __FILE__, __LINE__); \ fflush(stdout); \ ntest++; \ } while (0) #define CHECKX(expr) do { \ if ((expr)) { \ printf("ok %d %s:%u\n", ntest, __FILE__, __LINE__); \ } else { \ printf("not ok %d %s:%u\n", ntest, __FILE__, __LINE__); \ exit(1); \ } \ fflush(stdout); \ ntest++; \ } while (0) #define GID_WHEEL 0 #define GID_OPERATOR 5 #define GETGRENT0 0x0001 #define GETGRENT1 0x0002 #define GETGRENT2 0x0004 #define GETGRENT (GETGRENT0 | GETGRENT1 | GETGRENT2) #define GETGRENT_R0 0x0008 #define GETGRENT_R1 0x0010 #define GETGRENT_R2 0x0020 #define GETGRENT_R (GETGRENT_R0 | GETGRENT_R1 | GETGRENT_R2) #define GETGRNAM 0x0040 #define GETGRNAM_R 0x0080 #define GETGRGID 0x0100 #define GETGRGID_R 0x0200 #define SETGRENT 0x0400 static bool group_mem_compare(char **mem0, char **mem1) { int i0, i1; if (mem0 == NULL && mem1 == NULL) return (true); if (mem0 == NULL || mem1 == NULL) return (false); for (i0 = 0; mem0[i0] != NULL; i0++) { for (i1 = 0; mem1[i1] != NULL; i1++) { if (strcmp(mem0[i0], mem1[i1]) == 0) break; } if (mem1[i1] == NULL) return (false); } return (true); } static bool group_compare(const struct group *grp0, const struct group *grp1) { if (grp0 == NULL && grp1 == NULL) return (true); if (grp0 == NULL || grp1 == NULL) return (false); if (strcmp(grp0->gr_name, grp1->gr_name) != 0) return (false); if (grp0->gr_passwd != NULL || grp1->gr_passwd != NULL) { if (grp0->gr_passwd == NULL || grp1->gr_passwd == NULL) return (false); if (strcmp(grp0->gr_passwd, grp1->gr_passwd) != 0) return (false); } if (grp0->gr_gid != grp1->gr_gid) return (false); if (!group_mem_compare(grp0->gr_mem, grp1->gr_mem)) return (false); return (true); } static unsigned int runtest_cmds(cap_channel_t *capgrp) { char bufs[1024], bufc[1024]; unsigned int result; struct group *grps, *grpc; struct group sts, stc; result = 0; (void)setgrent(); if (cap_setgrent(capgrp) == 1) result |= SETGRENT; grps = getgrent(); grpc = cap_getgrent(capgrp); if (group_compare(grps, grpc)) { result |= GETGRENT0; grps = getgrent(); grpc = cap_getgrent(capgrp); if (group_compare(grps, grpc)) result |= GETGRENT1; } getgrent_r(&sts, bufs, sizeof(bufs), &grps); cap_getgrent_r(capgrp, &stc, bufc, sizeof(bufc), &grpc); if (group_compare(grps, grpc)) { result |= GETGRENT_R0; getgrent_r(&sts, bufs, sizeof(bufs), &grps); cap_getgrent_r(capgrp, &stc, bufc, sizeof(bufc), &grpc); if (group_compare(grps, grpc)) result |= GETGRENT_R1; } (void)setgrent(); if (cap_setgrent(capgrp) == 1) result |= SETGRENT; getgrent_r(&sts, bufs, sizeof(bufs), &grps); cap_getgrent_r(capgrp, &stc, bufc, sizeof(bufc), &grpc); if (group_compare(grps, grpc)) result |= GETGRENT_R2; grps = getgrent(); grpc = cap_getgrent(capgrp); if (group_compare(grps, grpc)) result |= GETGRENT2; grps = getgrnam("wheel"); grpc = cap_getgrnam(capgrp, "wheel"); if (group_compare(grps, grpc)) { grps = getgrnam("operator"); grpc = cap_getgrnam(capgrp, "operator"); if (group_compare(grps, grpc)) result |= GETGRNAM; } getgrnam_r("wheel", &sts, bufs, sizeof(bufs), &grps); cap_getgrnam_r(capgrp, "wheel", &stc, bufc, sizeof(bufc), &grpc); if (group_compare(grps, grpc)) { getgrnam_r("operator", &sts, bufs, sizeof(bufs), &grps); cap_getgrnam_r(capgrp, "operator", &stc, bufc, sizeof(bufc), &grpc); if (group_compare(grps, grpc)) result |= GETGRNAM_R; } grps = getgrgid(GID_WHEEL); grpc = cap_getgrgid(capgrp, GID_WHEEL); if (group_compare(grps, grpc)) { grps = getgrgid(GID_OPERATOR); grpc = cap_getgrgid(capgrp, GID_OPERATOR); if (group_compare(grps, grpc)) result |= GETGRGID; } getgrgid_r(GID_WHEEL, &sts, bufs, sizeof(bufs), &grps); cap_getgrgid_r(capgrp, GID_WHEEL, &stc, bufc, sizeof(bufc), &grpc); if (group_compare(grps, grpc)) { getgrgid_r(GID_OPERATOR, &sts, bufs, sizeof(bufs), &grps); cap_getgrgid_r(capgrp, GID_OPERATOR, &stc, bufc, sizeof(bufc), &grpc); if (group_compare(grps, grpc)) result |= GETGRGID_R; } return (result); } static void test_cmds(cap_channel_t *origcapgrp) { cap_channel_t *capgrp; const char *cmds[7], *fields[4], *names[5]; gid_t gids[5]; fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; fields[3] = "gr_mem"; names[0] = "wheel"; names[1] = "daemon"; names[2] = "kmem"; names[3] = "sys"; names[4] = "operator"; gids[0] = 0; gids[1] = 1; gids[2] = 2; gids[3] = 3; gids[4] = 5; /* * Allow: * cmds: setgrent, getgrent, getgrent_r, getgrnam, getgrnam_r, * getgrgid, getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: wheel, daemon, kmem, sys, operator * gids: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == 0); CHECK(cap_grp_limit_fields(capgrp, fields, 4) == 0); CHECK(cap_grp_limit_groups(capgrp, names, 5, NULL, 0) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT | GETGRENT_R | GETGRNAM | GETGRNAM_R | GETGRGID | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent, getgrent_r, getgrnam, getgrnam_r, * getgrgid, getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: * gids: 0, 1, 2, 3, 5 */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == 0); CHECK(cap_grp_limit_fields(capgrp, fields, 4) == 0); CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 5) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT | GETGRENT_R | GETGRNAM | GETGRNAM_R | GETGRGID | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: getgrent, getgrent_r, getgrnam, getgrnam_r, * getgrgid, getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: wheel, daemon, kmem, sys, operator * gids: * Disallow: * cmds: setgrent * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "getgrent"; cmds[1] = "getgrent_r"; cmds[2] = "getgrnam"; cmds[3] = "getgrnam_r"; cmds[4] = "getgrgid"; cmds[5] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "setgrent"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_groups(capgrp, names, 5, NULL, 0) == 0); CHECK(runtest_cmds(capgrp) == (GETGRENT0 | GETGRENT1 | GETGRENT_R0 | GETGRENT_R1 | GETGRNAM | GETGRNAM_R | GETGRGID | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: getgrent, getgrent_r, getgrnam, getgrnam_r, * getgrgid, getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: * gids: 0, 1, 2, 3, 5 * Disallow: * cmds: setgrent * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "getgrent"; cmds[1] = "getgrent_r"; cmds[2] = "getgrnam"; cmds[3] = "getgrnam_r"; cmds[4] = "getgrgid"; cmds[5] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "setgrent"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 5) == 0); CHECK(runtest_cmds(capgrp) == (GETGRENT0 | GETGRENT1 | GETGRENT_R0 | GETGRENT_R1 | GETGRNAM | GETGRNAM_R | GETGRGID | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent_r, getgrnam, getgrnam_r, * getgrgid, getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: wheel, daemon, kmem, sys, operator * gids: * Disallow: * cmds: getgrent * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent_r"; cmds[2] = "getgrnam"; cmds[3] = "getgrnam_r"; cmds[4] = "getgrgid"; cmds[5] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getgrent"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_fields(capgrp, fields, 4) == 0); CHECK(cap_grp_limit_groups(capgrp, names, 5, NULL, 0) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT_R2 | GETGRNAM | GETGRNAM_R | GETGRGID | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent_r, getgrnam, getgrnam_r, * getgrgid, getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: * gids: 0, 1, 2, 3, 5 * Disallow: * cmds: getgrent * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent_r"; cmds[2] = "getgrnam"; cmds[3] = "getgrnam_r"; cmds[4] = "getgrgid"; cmds[5] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getgrent"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_fields(capgrp, fields, 4) == 0); CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 5) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT_R2 | GETGRNAM | GETGRNAM_R | GETGRGID | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent, getgrnam, getgrnam_r, * getgrgid, getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: wheel, daemon, kmem, sys, operator * gids: * Disallow: * cmds: getgrent_r * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrnam"; cmds[3] = "getgrnam_r"; cmds[4] = "getgrgid"; cmds[5] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getgrent_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_groups(capgrp, names, 5, NULL, 0) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT0 | GETGRENT1 | GETGRNAM | GETGRNAM_R | GETGRGID | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent, getgrnam, getgrnam_r, * getgrgid, getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: * gids: 0, 1, 2, 3, 5 * Disallow: * cmds: getgrent_r * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrnam"; cmds[3] = "getgrnam_r"; cmds[4] = "getgrgid"; cmds[5] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getgrent_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 5) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT0 | GETGRENT1 | GETGRNAM | GETGRNAM_R | GETGRGID | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent, getgrent_r, getgrnam_r, * getgrgid, getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: wheel, daemon, kmem, sys, operator * gids: * Disallow: * cmds: getgrnam * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam_r"; cmds[4] = "getgrgid"; cmds[5] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getgrnam"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_fields(capgrp, fields, 4) == 0); CHECK(cap_grp_limit_groups(capgrp, names, 5, NULL, 0) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT | GETGRENT_R | GETGRNAM_R | GETGRGID | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent, getgrent_r, getgrnam_r, * getgrgid, getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: * gids: 0, 1, 2, 3, 5 * Disallow: * cmds: getgrnam * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam_r"; cmds[4] = "getgrgid"; cmds[5] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getgrnam"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_fields(capgrp, fields, 4) == 0); CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 5) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT | GETGRENT_R | GETGRNAM_R | GETGRGID | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent, getgrent_r, getgrnam, * getgrgid, getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: wheel, daemon, kmem, sys, operator * gids: * Disallow: * cmds: getgrnam_r * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrgid"; cmds[5] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getgrnam_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_groups(capgrp, names, 5, NULL, 0) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT | GETGRENT_R | GETGRNAM | GETGRGID | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent, getgrent_r, getgrnam, * getgrgid, getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: * gids: 0, 1, 2, 3, 5 * Disallow: * cmds: getgrnam_r * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrgid"; cmds[5] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getgrnam_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 5) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT | GETGRENT_R | GETGRNAM | GETGRGID | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent, getgrent_r, getgrnam, getgrnam_r, * getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: wheel, daemon, kmem, sys, operator * gids: * Disallow: * cmds: getgrgid * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getgrgid"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_fields(capgrp, fields, 4) == 0); CHECK(cap_grp_limit_groups(capgrp, names, 5, NULL, 0) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT | GETGRENT_R | GETGRNAM | GETGRNAM_R | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent, getgrent_r, getgrnam, getgrnam_r, * getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: * gids: 0, 1, 2, 3, 5 * Disallow: * cmds: getgrgid * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getgrgid"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_fields(capgrp, fields, 4) == 0); CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 5) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT | GETGRENT_R | GETGRNAM | GETGRNAM_R | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent, getgrent_r, getgrnam, getgrnam_r, * getgrgid * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: wheel, daemon, kmem, sys, operator * gids: * Disallow: * cmds: getgrgid_r * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_groups(capgrp, names, 5, NULL, 0) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT | GETGRENT_R | GETGRNAM | GETGRNAM_R | GETGRGID)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent, getgrent_r, getgrnam, getgrnam_r, * getgrgid * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: * gids: 0, 1, 2, 3, 5 * Disallow: * cmds: getgrgid_r * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 5) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT | GETGRENT_R | GETGRNAM | GETGRNAM_R | GETGRGID)); cap_close(capgrp); } #define GR_NAME 0x01 #define GR_PASSWD 0x02 #define GR_GID 0x04 #define GR_MEM 0x08 static unsigned int group_fields(const struct group *grp) { unsigned int result; result = 0; if (grp->gr_name != NULL && grp->gr_name[0] != '\0') result |= GR_NAME; if (grp->gr_passwd != NULL && grp->gr_passwd[0] != '\0') result |= GR_PASSWD; if (grp->gr_gid != (gid_t)-1) result |= GR_GID; if (grp->gr_mem != NULL && grp->gr_mem[0] != NULL) result |= GR_MEM; return (result); } static bool runtest_fields(cap_channel_t *capgrp, unsigned int expected) { char buf[1024]; struct group *grp; struct group st; (void)cap_setgrent(capgrp); grp = cap_getgrent(capgrp); if (group_fields(grp) != expected) return (false); (void)cap_setgrent(capgrp); cap_getgrent_r(capgrp, &st, buf, sizeof(buf), &grp); if (group_fields(grp) != expected) return (false); grp = cap_getgrnam(capgrp, "wheel"); if (group_fields(grp) != expected) return (false); cap_getgrnam_r(capgrp, "wheel", &st, buf, sizeof(buf), &grp); if (group_fields(grp) != expected) return (false); grp = cap_getgrgid(capgrp, GID_WHEEL); if (group_fields(grp) != expected) return (false); cap_getgrgid_r(capgrp, GID_WHEEL, &st, buf, sizeof(buf), &grp); if (group_fields(grp) != expected) return (false); return (true); } static void test_fields(cap_channel_t *origcapgrp) { cap_channel_t *capgrp; const char *fields[4]; /* No limits. */ CHECK(runtest_fields(origcapgrp, GR_NAME | GR_PASSWD | GR_GID | GR_MEM)); /* * Allow: * fields: gr_name, gr_passwd, gr_gid, gr_mem */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; fields[3] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 4) == 0); CHECK(runtest_fields(capgrp, GR_NAME | GR_PASSWD | GR_GID | GR_MEM)); cap_close(capgrp); /* * Allow: * fields: gr_passwd, gr_gid, gr_mem */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); fields[0] = "gr_passwd"; fields[1] = "gr_gid"; fields[2] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 3) == 0); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; fields[3] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 4) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(capgrp, GR_PASSWD | GR_GID | GR_MEM)); cap_close(capgrp); /* * Allow: * fields: gr_name, gr_gid, gr_mem */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); fields[0] = "gr_name"; fields[1] = "gr_gid"; fields[2] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 3) == 0); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; fields[3] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 4) == -1 && errno == ENOTCAPABLE); fields[0] = "gr_passwd"; CHECK(cap_grp_limit_fields(capgrp, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(capgrp, GR_NAME | GR_GID | GR_MEM)); cap_close(capgrp); /* * Allow: * fields: gr_name, gr_passwd, gr_mem */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 3) == 0); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; fields[3] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 4) == -1 && errno == ENOTCAPABLE); fields[0] = "gr_gid"; CHECK(cap_grp_limit_fields(capgrp, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(capgrp, GR_NAME | GR_PASSWD | GR_MEM)); cap_close(capgrp); /* * Allow: * fields: gr_name, gr_passwd, gr_gid */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; CHECK(cap_grp_limit_fields(capgrp, fields, 3) == 0); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; fields[3] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 4) == -1 && errno == ENOTCAPABLE); fields[0] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(capgrp, GR_NAME | GR_PASSWD | GR_GID)); cap_close(capgrp); /* * Allow: * fields: gr_name, gr_passwd */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); fields[0] = "gr_name"; fields[1] = "gr_passwd"; CHECK(cap_grp_limit_fields(capgrp, fields, 2) == 0); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; fields[3] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 4) == -1 && errno == ENOTCAPABLE); fields[0] = "gr_gid"; CHECK(cap_grp_limit_fields(capgrp, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(capgrp, GR_NAME | GR_PASSWD)); cap_close(capgrp); /* * Allow: * fields: gr_name, gr_gid */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); fields[0] = "gr_name"; fields[1] = "gr_gid"; CHECK(cap_grp_limit_fields(capgrp, fields, 2) == 0); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; fields[3] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 4) == -1 && errno == ENOTCAPABLE); fields[0] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(capgrp, GR_NAME | GR_GID)); cap_close(capgrp); /* * Allow: * fields: gr_name, gr_mem */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); fields[0] = "gr_name"; fields[1] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 2) == 0); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; fields[3] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 4) == -1 && errno == ENOTCAPABLE); fields[0] = "gr_passwd"; CHECK(cap_grp_limit_fields(capgrp, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(capgrp, GR_NAME | GR_MEM)); cap_close(capgrp); /* * Allow: * fields: gr_passwd, gr_gid */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); fields[0] = "gr_passwd"; fields[1] = "gr_gid"; CHECK(cap_grp_limit_fields(capgrp, fields, 2) == 0); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; fields[3] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 4) == -1 && errno == ENOTCAPABLE); fields[0] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(capgrp, GR_PASSWD | GR_GID)); cap_close(capgrp); /* * Allow: * fields: gr_passwd, gr_mem */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); fields[0] = "gr_passwd"; fields[1] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 2) == 0); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; fields[3] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 4) == -1 && errno == ENOTCAPABLE); fields[0] = "gr_gid"; CHECK(cap_grp_limit_fields(capgrp, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(capgrp, GR_PASSWD | GR_MEM)); cap_close(capgrp); /* * Allow: * fields: gr_gid, gr_mem */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); fields[0] = "gr_gid"; fields[1] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 2) == 0); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; fields[3] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 4) == -1 && errno == ENOTCAPABLE); fields[0] = "gr_passwd"; CHECK(cap_grp_limit_fields(capgrp, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(capgrp, GR_GID | GR_MEM)); cap_close(capgrp); } static bool runtest_groups(cap_channel_t *capgrp, const char **names, const gid_t *gids, size_t ngroups) { char buf[1024]; struct group *grp; struct group st; unsigned int i, got; (void)cap_setgrent(capgrp); got = 0; for (;;) { grp = cap_getgrent(capgrp); if (grp == NULL) break; got++; for (i = 0; i < ngroups; i++) { if (strcmp(names[i], grp->gr_name) == 0 && gids[i] == grp->gr_gid) { break; } } if (i == ngroups) return (false); } if (got != ngroups) return (false); (void)cap_setgrent(capgrp); got = 0; for (;;) { cap_getgrent_r(capgrp, &st, buf, sizeof(buf), &grp); if (grp == NULL) break; got++; for (i = 0; i < ngroups; i++) { if (strcmp(names[i], grp->gr_name) == 0 && gids[i] == grp->gr_gid) { break; } } if (i == ngroups) return (false); } if (got != ngroups) return (false); for (i = 0; i < ngroups; i++) { grp = cap_getgrnam(capgrp, names[i]); if (grp == NULL) return (false); } for (i = 0; i < ngroups; i++) { cap_getgrnam_r(capgrp, names[i], &st, buf, sizeof(buf), &grp); if (grp == NULL) return (false); } for (i = 0; i < ngroups; i++) { grp = cap_getgrgid(capgrp, gids[i]); if (grp == NULL) return (false); } for (i = 0; i < ngroups; i++) { cap_getgrgid_r(capgrp, gids[i], &st, buf, sizeof(buf), &grp); if (grp == NULL) return (false); } return (true); } static void test_groups(cap_channel_t *origcapgrp) { cap_channel_t *capgrp; const char *names[5]; gid_t gids[5]; /* * Allow: * groups: * names: wheel, daemon, kmem, sys, tty * gids: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); names[0] = "wheel"; names[1] = "daemon"; names[2] = "kmem"; names[3] = "sys"; names[4] = "tty"; CHECK(cap_grp_limit_groups(capgrp, names, 5, NULL, 0) == 0); gids[0] = 0; gids[1] = 1; gids[2] = 2; gids[3] = 3; gids[4] = 4; CHECK(runtest_groups(capgrp, names, gids, 5)); cap_close(capgrp); /* * Allow: * groups: * names: kmem, sys, tty * gids: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); names[0] = "kmem"; names[1] = "sys"; names[2] = "tty"; CHECK(cap_grp_limit_groups(capgrp, names, 3, NULL, 0) == 0); names[3] = "daemon"; CHECK(cap_grp_limit_groups(capgrp, names, 4, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "daemon"; CHECK(cap_grp_limit_groups(capgrp, names, 1, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "kmem"; gids[0] = 2; gids[1] = 3; gids[2] = 4; CHECK(runtest_groups(capgrp, names, gids, 3)); cap_close(capgrp); /* * Allow: * groups: * names: wheel, kmem, tty * gids: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); names[0] = "wheel"; names[1] = "kmem"; names[2] = "tty"; CHECK(cap_grp_limit_groups(capgrp, names, 3, NULL, 0) == 0); names[3] = "daemon"; CHECK(cap_grp_limit_groups(capgrp, names, 4, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "daemon"; CHECK(cap_grp_limit_groups(capgrp, names, 1, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "wheel"; gids[0] = 0; gids[1] = 2; gids[2] = 4; CHECK(runtest_groups(capgrp, names, gids, 3)); cap_close(capgrp); /* * Allow: * groups: * names: * gids: 2, 3, 4 */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); names[0] = "kmem"; names[1] = "sys"; names[2] = "tty"; gids[0] = 2; gids[1] = 3; gids[2] = 4; CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 3) == 0); gids[3] = 0; CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 4) == -1 && errno == ENOTCAPABLE); gids[0] = 0; CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 1) == -1 && errno == ENOTCAPABLE); gids[0] = 2; CHECK(runtest_groups(capgrp, names, gids, 3)); cap_close(capgrp); /* * Allow: * groups: * names: * gids: 0, 2, 4 */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); names[0] = "wheel"; names[1] = "kmem"; names[2] = "tty"; gids[0] = 0; gids[1] = 2; gids[2] = 4; CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 3) == 0); gids[3] = 1; CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 4) == -1 && errno == ENOTCAPABLE); gids[0] = 1; CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 1) == -1 && errno == ENOTCAPABLE); gids[0] = 0; CHECK(runtest_groups(capgrp, names, gids, 3)); cap_close(capgrp); /* * Allow: * groups: * names: kmem * gids: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); names[0] = "kmem"; CHECK(cap_grp_limit_groups(capgrp, names, 1, NULL, 0) == 0); names[1] = "daemon"; CHECK(cap_grp_limit_groups(capgrp, names, 2, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "daemon"; CHECK(cap_grp_limit_groups(capgrp, names, 1, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "kmem"; gids[0] = 2; CHECK(runtest_groups(capgrp, names, gids, 1)); cap_close(capgrp); /* * Allow: * groups: * names: wheel, tty * gids: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); names[0] = "wheel"; names[1] = "tty"; CHECK(cap_grp_limit_groups(capgrp, names, 2, NULL, 0) == 0); names[2] = "daemon"; CHECK(cap_grp_limit_groups(capgrp, names, 3, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "daemon"; CHECK(cap_grp_limit_groups(capgrp, names, 1, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "wheel"; gids[0] = 0; gids[1] = 4; CHECK(runtest_groups(capgrp, names, gids, 2)); cap_close(capgrp); /* * Allow: * groups: * names: * gids: 2 */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); names[0] = "kmem"; gids[0] = 2; CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 1) == 0); gids[1] = 1; CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 2) == -1 && errno == ENOTCAPABLE); gids[0] = 1; CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 1) == -1 && errno == ENOTCAPABLE); gids[0] = 2; CHECK(runtest_groups(capgrp, names, gids, 1)); cap_close(capgrp); /* * Allow: * groups: * names: * gids: 0, 4 */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); names[0] = "wheel"; names[1] = "tty"; gids[0] = 0; gids[1] = 4; CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 2) == 0); gids[2] = 1; CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 3) == -1 && errno == ENOTCAPABLE); gids[0] = 1; CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 1) == -1 && errno == ENOTCAPABLE); gids[0] = 0; CHECK(runtest_groups(capgrp, names, gids, 2)); cap_close(capgrp); } int main(void) { cap_channel_t *capcas, *capgrp; printf("1..199\n"); fflush(stdout); capcas = cap_init(); CHECKX(capcas != NULL); capgrp = cap_service_open(capcas, "system.grp"); CHECKX(capgrp != NULL); cap_close(capcas); /* No limits. */ CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT | GETGRENT_R | GETGRNAM | GETGRNAM_R | GETGRGID | GETGRGID_R)); test_cmds(capgrp); test_fields(capgrp); test_groups(capgrp); cap_close(capgrp); exit(0); } Index: head/lib/libcasper/services/cap_pwd/cap_pwd.c =================================================================== --- head/lib/libcasper/services/cap_pwd/cap_pwd.c (revision 328472) +++ head/lib/libcasper/services/cap_pwd/cap_pwd.c (revision 328473) @@ -1,783 +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); 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); 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)); } 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_pwd/tests/pwd_test.c =================================================================== --- head/lib/libcasper/services/cap_pwd/tests/pwd_test.c (revision 328472) +++ head/lib/libcasper/services/cap_pwd/tests/pwd_test.c (revision 328473) @@ -1,1540 +1,1542 @@ /*- + * 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 #include static int ntest = 1; #define CHECK(expr) do { \ if ((expr)) \ printf("ok %d # %s:%u\n", ntest, __FILE__, __LINE__); \ else \ printf("not ok %d # %s:%u\n", ntest, __FILE__, __LINE__); \ fflush(stdout); \ ntest++; \ } while (0) #define CHECKX(expr) do { \ if ((expr)) { \ printf("ok %d # %s:%u\n", ntest, __FILE__, __LINE__); \ } else { \ printf("not ok %d # %s:%u\n", ntest, __FILE__, __LINE__); \ exit(1); \ } \ fflush(stdout); \ ntest++; \ } while (0) #define UID_ROOT 0 #define UID_OPERATOR 2 #define GETPWENT0 0x0001 #define GETPWENT1 0x0002 #define GETPWENT2 0x0004 #define GETPWENT (GETPWENT0 | GETPWENT1 | GETPWENT2) #define GETPWENT_R0 0x0008 #define GETPWENT_R1 0x0010 #define GETPWENT_R2 0x0020 #define GETPWENT_R (GETPWENT_R0 | GETPWENT_R1 | GETPWENT_R2) #define GETPWNAM 0x0040 #define GETPWNAM_R 0x0080 #define GETPWUID 0x0100 #define GETPWUID_R 0x0200 static bool passwd_compare(const struct passwd *pwd0, const struct passwd *pwd1) { if (pwd0 == NULL && pwd1 == NULL) return (true); if (pwd0 == NULL || pwd1 == NULL) return (false); if (strcmp(pwd0->pw_name, pwd1->pw_name) != 0) return (false); if (pwd0->pw_passwd != NULL || pwd1->pw_passwd != NULL) { if (pwd0->pw_passwd == NULL || pwd1->pw_passwd == NULL) return (false); if (strcmp(pwd0->pw_passwd, pwd1->pw_passwd) != 0) return (false); } if (pwd0->pw_uid != pwd1->pw_uid) return (false); if (pwd0->pw_gid != pwd1->pw_gid) return (false); if (pwd0->pw_change != pwd1->pw_change) return (false); if (pwd0->pw_class != NULL || pwd1->pw_class != NULL) { if (pwd0->pw_class == NULL || pwd1->pw_class == NULL) return (false); if (strcmp(pwd0->pw_class, pwd1->pw_class) != 0) return (false); } if (pwd0->pw_gecos != NULL || pwd1->pw_gecos != NULL) { if (pwd0->pw_gecos == NULL || pwd1->pw_gecos == NULL) return (false); if (strcmp(pwd0->pw_gecos, pwd1->pw_gecos) != 0) return (false); } if (pwd0->pw_dir != NULL || pwd1->pw_dir != NULL) { if (pwd0->pw_dir == NULL || pwd1->pw_dir == NULL) return (false); if (strcmp(pwd0->pw_dir, pwd1->pw_dir) != 0) return (false); } if (pwd0->pw_shell != NULL || pwd1->pw_shell != NULL) { if (pwd0->pw_shell == NULL || pwd1->pw_shell == NULL) return (false); if (strcmp(pwd0->pw_shell, pwd1->pw_shell) != 0) return (false); } if (pwd0->pw_expire != pwd1->pw_expire) return (false); if (pwd0->pw_fields != pwd1->pw_fields) return (false); return (true); } static unsigned int runtest_cmds(cap_channel_t *cappwd) { char bufs[1024], bufc[1024]; unsigned int result; struct passwd *pwds, *pwdc; struct passwd sts, stc; result = 0; setpwent(); cap_setpwent(cappwd); pwds = getpwent(); pwdc = cap_getpwent(cappwd); if (passwd_compare(pwds, pwdc)) { result |= GETPWENT0; pwds = getpwent(); pwdc = cap_getpwent(cappwd); if (passwd_compare(pwds, pwdc)) result |= GETPWENT1; } getpwent_r(&sts, bufs, sizeof(bufs), &pwds); cap_getpwent_r(cappwd, &stc, bufc, sizeof(bufc), &pwdc); if (passwd_compare(pwds, pwdc)) { result |= GETPWENT_R0; getpwent_r(&sts, bufs, sizeof(bufs), &pwds); cap_getpwent_r(cappwd, &stc, bufc, sizeof(bufc), &pwdc); if (passwd_compare(pwds, pwdc)) result |= GETPWENT_R1; } setpwent(); cap_setpwent(cappwd); getpwent_r(&sts, bufs, sizeof(bufs), &pwds); cap_getpwent_r(cappwd, &stc, bufc, sizeof(bufc), &pwdc); if (passwd_compare(pwds, pwdc)) result |= GETPWENT_R2; pwds = getpwent(); pwdc = cap_getpwent(cappwd); if (passwd_compare(pwds, pwdc)) result |= GETPWENT2; pwds = getpwnam("root"); pwdc = cap_getpwnam(cappwd, "root"); if (passwd_compare(pwds, pwdc)) { pwds = getpwnam("operator"); pwdc = cap_getpwnam(cappwd, "operator"); if (passwd_compare(pwds, pwdc)) result |= GETPWNAM; } getpwnam_r("root", &sts, bufs, sizeof(bufs), &pwds); cap_getpwnam_r(cappwd, "root", &stc, bufc, sizeof(bufc), &pwdc); if (passwd_compare(pwds, pwdc)) { getpwnam_r("operator", &sts, bufs, sizeof(bufs), &pwds); cap_getpwnam_r(cappwd, "operator", &stc, bufc, sizeof(bufc), &pwdc); if (passwd_compare(pwds, pwdc)) result |= GETPWNAM_R; } pwds = getpwuid(UID_ROOT); pwdc = cap_getpwuid(cappwd, UID_ROOT); if (passwd_compare(pwds, pwdc)) { pwds = getpwuid(UID_OPERATOR); pwdc = cap_getpwuid(cappwd, UID_OPERATOR); if (passwd_compare(pwds, pwdc)) result |= GETPWUID; } getpwuid_r(UID_ROOT, &sts, bufs, sizeof(bufs), &pwds); cap_getpwuid_r(cappwd, UID_ROOT, &stc, bufc, sizeof(bufc), &pwdc); if (passwd_compare(pwds, pwdc)) { getpwuid_r(UID_OPERATOR, &sts, bufs, sizeof(bufs), &pwds); cap_getpwuid_r(cappwd, UID_OPERATOR, &stc, bufc, sizeof(bufc), &pwdc); if (passwd_compare(pwds, pwdc)) result |= GETPWUID_R; } return (result); } static void test_cmds(cap_channel_t *origcappwd) { cap_channel_t *cappwd; const char *cmds[7], *fields[10], *names[6]; uid_t uids[5]; fields[0] = "pw_name"; fields[1] = "pw_passwd"; fields[2] = "pw_uid"; fields[3] = "pw_gid"; fields[4] = "pw_change"; fields[5] = "pw_class"; fields[6] = "pw_gecos"; fields[7] = "pw_dir"; fields[8] = "pw_shell"; fields[9] = "pw_expire"; names[0] = "root"; names[1] = "toor"; names[2] = "daemon"; names[3] = "operator"; names[4] = "bin"; names[5] = "kmem"; uids[0] = 0; uids[1] = 1; uids[2] = 2; uids[3] = 3; uids[4] = 5; /* * Allow: * cmds: setpwent, getpwent, getpwent_r, getpwnam, getpwnam_r, * getpwuid, getpwuid_r * users: * names: root, toor, daemon, operator, bin, kmem * uids: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == 0); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R | GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent, getpwent_r, getpwnam, getpwnam_r, * getpwuid, getpwuid_r * users: * names: * uids: 0, 1, 2, 3, 5 */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == 0); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 5) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R | GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: getpwent, getpwent_r, getpwnam, getpwnam_r, * getpwuid, getpwuid_r * users: * names: root, toor, daemon, operator, bin, kmem * uids: * Disallow: * cmds: setpwent * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cap_setpwent(cappwd); cmds[0] = "getpwent"; cmds[1] = "getpwent_r"; cmds[2] = "getpwnam"; cmds[3] = "getpwnam_r"; cmds[4] = "getpwuid"; cmds[5] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "setpwent"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT0 | GETPWENT1 | GETPWENT_R0 | GETPWENT_R1 | GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: getpwent, getpwent_r, getpwnam, getpwnam_r, * getpwuid, getpwuid_r * users: * names: * uids: 0, 1, 2, 3, 5 * Disallow: * cmds: setpwent * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cap_setpwent(cappwd); cmds[0] = "getpwent"; cmds[1] = "getpwent_r"; cmds[2] = "getpwnam"; cmds[3] = "getpwnam_r"; cmds[4] = "getpwuid"; cmds[5] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "setpwent"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 5) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT0 | GETPWENT1 | GETPWENT_R0 | GETPWENT_R1 | GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent_r, getpwnam, getpwnam_r, * getpwuid, getpwuid_r * users: * names: root, toor, daemon, operator, bin, kmem * uids: * Disallow: * cmds: getpwent * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent_r"; cmds[2] = "getpwnam"; cmds[3] = "getpwnam_r"; cmds[4] = "getpwuid"; cmds[5] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getpwent"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT_R2 | GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent_r, getpwnam, getpwnam_r, * getpwuid, getpwuid_r * users: * names: * uids: 0, 1, 2, 3, 5 * Disallow: * cmds: getpwent * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent_r"; cmds[2] = "getpwnam"; cmds[3] = "getpwnam_r"; cmds[4] = "getpwuid"; cmds[5] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getpwent"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 5) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT_R2 | GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent, getpwnam, getpwnam_r, * getpwuid, getpwuid_r * users: * names: root, toor, daemon, operator, bin, kmem * uids: * Disallow: * cmds: getpwent_r * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwnam"; cmds[3] = "getpwnam_r"; cmds[4] = "getpwuid"; cmds[5] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getpwent_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT0 | GETPWENT1 | GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent, getpwnam, getpwnam_r, * getpwuid, getpwuid_r * users: * names: * uids: 0, 1, 2, 3, 5 * Disallow: * cmds: getpwent_r * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwnam"; cmds[3] = "getpwnam_r"; cmds[4] = "getpwuid"; cmds[5] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getpwent_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 5) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT0 | GETPWENT1 | GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent, getpwent_r, getpwnam_r, * getpwuid, getpwuid_r * users: * names: root, toor, daemon, operator, bin, kmem * uids: * Disallow: * cmds: getpwnam * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam_r"; cmds[4] = "getpwuid"; cmds[5] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getpwnam"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R | GETPWNAM_R | GETPWUID | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent, getpwent_r, getpwnam_r, * getpwuid, getpwuid_r * users: * names: * uids: 0, 1, 2, 3, 5 * Disallow: * cmds: getpwnam * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam_r"; cmds[4] = "getpwuid"; cmds[5] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getpwnam"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 5) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R | GETPWNAM_R | GETPWUID | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent, getpwent_r, getpwnam, * getpwuid, getpwuid_r * users: * names: root, toor, daemon, operator, bin, kmem * uids: * Disallow: * cmds: getpwnam_r * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwuid"; cmds[5] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getpwnam_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R | GETPWNAM | GETPWUID | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent, getpwent_r, getpwnam, * getpwuid, getpwuid_r * users: * names: * uids: 0, 1, 2, 3, 5 * Disallow: * cmds: getpwnam_r * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwuid"; cmds[5] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getpwnam_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 5) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R | GETPWNAM | GETPWUID | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent, getpwent_r, getpwnam, getpwnam_r, * getpwuid_r * users: * names: root, toor, daemon, operator, bin, kmem * uids: * Disallow: * cmds: getpwuid * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getpwuid"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R | GETPWNAM | GETPWNAM_R | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent, getpwent_r, getpwnam, getpwnam_r, * getpwuid_r * users: * names: * uids: 0, 1, 2, 3, 5 * Disallow: * cmds: getpwuid * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getpwuid"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 5) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R | GETPWNAM | GETPWNAM_R | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent, getpwent_r, getpwnam, getpwnam_r, * getpwuid * users: * names: root, toor, daemon, operator, bin, kmem * uids: * Disallow: * cmds: getpwuid_r * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R | GETPWNAM | GETPWNAM_R | GETPWUID)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent, getpwent_r, getpwnam, getpwnam_r, * getpwuid * users: * names: * uids: 0, 1, 2, 3, 5 * Disallow: * cmds: getpwuid_r * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 5) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R | GETPWNAM | GETPWNAM_R | GETPWUID)); cap_close(cappwd); } #define PW_NAME _PWF_NAME #define PW_PASSWD _PWF_PASSWD #define PW_UID _PWF_UID #define PW_GID _PWF_GID #define PW_CHANGE _PWF_CHANGE #define PW_CLASS _PWF_CLASS #define PW_GECOS _PWF_GECOS #define PW_DIR _PWF_DIR #define PW_SHELL _PWF_SHELL #define PW_EXPIRE _PWF_EXPIRE static unsigned int passwd_fields(const struct passwd *pwd) { unsigned int result; result = 0; if (pwd->pw_name != NULL && pwd->pw_name[0] != '\0') result |= PW_NAME; // else // printf("No pw_name\n"); if (pwd->pw_passwd != NULL && pwd->pw_passwd[0] != '\0') result |= PW_PASSWD; else if ((pwd->pw_fields & _PWF_PASSWD) != 0) result |= PW_PASSWD; // else // printf("No pw_passwd\n"); if (pwd->pw_uid != (uid_t)-1) result |= PW_UID; // else // printf("No pw_uid\n"); if (pwd->pw_gid != (gid_t)-1) result |= PW_GID; // else // printf("No pw_gid\n"); if (pwd->pw_change != 0 || (pwd->pw_fields & _PWF_CHANGE) != 0) result |= PW_CHANGE; // else // printf("No pw_change\n"); if (pwd->pw_class != NULL && pwd->pw_class[0] != '\0') result |= PW_CLASS; else if ((pwd->pw_fields & _PWF_CLASS) != 0) result |= PW_CLASS; // else // printf("No pw_class\n"); if (pwd->pw_gecos != NULL && pwd->pw_gecos[0] != '\0') result |= PW_GECOS; else if ((pwd->pw_fields & _PWF_GECOS) != 0) result |= PW_GECOS; // else // printf("No pw_gecos\n"); if (pwd->pw_dir != NULL && pwd->pw_dir[0] != '\0') result |= PW_DIR; else if ((pwd->pw_fields & _PWF_DIR) != 0) result |= PW_DIR; // else // printf("No pw_dir\n"); if (pwd->pw_shell != NULL && pwd->pw_shell[0] != '\0') result |= PW_SHELL; else if ((pwd->pw_fields & _PWF_SHELL) != 0) result |= PW_SHELL; // else // printf("No pw_shell\n"); if (pwd->pw_expire != 0 || (pwd->pw_fields & _PWF_EXPIRE) != 0) result |= PW_EXPIRE; // else // printf("No pw_expire\n"); if (false && pwd->pw_fields != (int)result) { printf("fields=0x%x != result=0x%x\n", (const unsigned int)pwd->pw_fields, result); printf(" fields result\n"); printf("PW_NAME %d %d\n", (pwd->pw_fields & PW_NAME) != 0, (result & PW_NAME) != 0); printf("PW_PASSWD %d %d\n", (pwd->pw_fields & PW_PASSWD) != 0, (result & PW_PASSWD) != 0); printf("PW_UID %d %d\n", (pwd->pw_fields & PW_UID) != 0, (result & PW_UID) != 0); printf("PW_GID %d %d\n", (pwd->pw_fields & PW_GID) != 0, (result & PW_GID) != 0); printf("PW_CHANGE %d %d\n", (pwd->pw_fields & PW_CHANGE) != 0, (result & PW_CHANGE) != 0); printf("PW_CLASS %d %d\n", (pwd->pw_fields & PW_CLASS) != 0, (result & PW_CLASS) != 0); printf("PW_GECOS %d %d\n", (pwd->pw_fields & PW_GECOS) != 0, (result & PW_GECOS) != 0); printf("PW_DIR %d %d\n", (pwd->pw_fields & PW_DIR) != 0, (result & PW_DIR) != 0); printf("PW_SHELL %d %d\n", (pwd->pw_fields & PW_SHELL) != 0, (result & PW_SHELL) != 0); printf("PW_EXPIRE %d %d\n", (pwd->pw_fields & PW_EXPIRE) != 0, (result & PW_EXPIRE) != 0); } //printf("result=0x%x\n", result); return (result); } static bool runtest_fields(cap_channel_t *cappwd, unsigned int expected) { char buf[1024]; struct passwd *pwd; struct passwd st; //printf("expected=0x%x\n", expected); cap_setpwent(cappwd); pwd = cap_getpwent(cappwd); if ((passwd_fields(pwd) & ~expected) != 0) return (false); cap_setpwent(cappwd); cap_getpwent_r(cappwd, &st, buf, sizeof(buf), &pwd); if ((passwd_fields(pwd) & ~expected) != 0) return (false); pwd = cap_getpwnam(cappwd, "root"); if ((passwd_fields(pwd) & ~expected) != 0) return (false); cap_getpwnam_r(cappwd, "root", &st, buf, sizeof(buf), &pwd); if ((passwd_fields(pwd) & ~expected) != 0) return (false); pwd = cap_getpwuid(cappwd, UID_ROOT); if ((passwd_fields(pwd) & ~expected) != 0) return (false); cap_getpwuid_r(cappwd, UID_ROOT, &st, buf, sizeof(buf), &pwd); if ((passwd_fields(pwd) & ~expected) != 0) return (false); return (true); } static void test_fields(cap_channel_t *origcappwd) { cap_channel_t *cappwd; const char *fields[10]; /* No limits. */ CHECK(runtest_fields(origcappwd, PW_NAME | PW_PASSWD | PW_UID | PW_GID | PW_CHANGE | PW_CLASS | PW_GECOS | PW_DIR | PW_SHELL | PW_EXPIRE)); /* * Allow: * fields: pw_name, pw_passwd, pw_uid, pw_gid, pw_change, pw_class, * pw_gecos, pw_dir, pw_shell, pw_expire */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); fields[0] = "pw_name"; fields[1] = "pw_passwd"; fields[2] = "pw_uid"; fields[3] = "pw_gid"; fields[4] = "pw_change"; fields[5] = "pw_class"; fields[6] = "pw_gecos"; fields[7] = "pw_dir"; fields[8] = "pw_shell"; fields[9] = "pw_expire"; CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(runtest_fields(origcappwd, PW_NAME | PW_PASSWD | PW_UID | PW_GID | PW_CHANGE | PW_CLASS | PW_GECOS | PW_DIR | PW_SHELL | PW_EXPIRE)); cap_close(cappwd); /* * Allow: * fields: pw_name, pw_passwd, pw_uid, pw_gid, pw_change */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); fields[0] = "pw_name"; fields[1] = "pw_passwd"; fields[2] = "pw_uid"; fields[3] = "pw_gid"; fields[4] = "pw_change"; CHECK(cap_pwd_limit_fields(cappwd, fields, 5) == 0); fields[5] = "pw_class"; CHECK(cap_pwd_limit_fields(cappwd, fields, 6) == -1 && errno == ENOTCAPABLE); fields[0] = "pw_class"; CHECK(cap_pwd_limit_fields(cappwd, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(cappwd, PW_NAME | PW_PASSWD | PW_UID | PW_GID | PW_CHANGE)); cap_close(cappwd); /* * Allow: * fields: pw_class, pw_gecos, pw_dir, pw_shell, pw_expire */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); fields[0] = "pw_class"; fields[1] = "pw_gecos"; fields[2] = "pw_dir"; fields[3] = "pw_shell"; fields[4] = "pw_expire"; CHECK(cap_pwd_limit_fields(cappwd, fields, 5) == 0); fields[5] = "pw_uid"; CHECK(cap_pwd_limit_fields(cappwd, fields, 6) == -1 && errno == ENOTCAPABLE); fields[0] = "pw_uid"; CHECK(cap_pwd_limit_fields(cappwd, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(cappwd, PW_CLASS | PW_GECOS | PW_DIR | PW_SHELL | PW_EXPIRE)); cap_close(cappwd); /* * Allow: * fields: pw_name, pw_uid, pw_change, pw_gecos, pw_shell */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); fields[0] = "pw_name"; fields[1] = "pw_uid"; fields[2] = "pw_change"; fields[3] = "pw_gecos"; fields[4] = "pw_shell"; CHECK(cap_pwd_limit_fields(cappwd, fields, 5) == 0); fields[5] = "pw_class"; CHECK(cap_pwd_limit_fields(cappwd, fields, 6) == -1 && errno == ENOTCAPABLE); fields[0] = "pw_class"; CHECK(cap_pwd_limit_fields(cappwd, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(cappwd, PW_NAME | PW_UID | PW_CHANGE | PW_GECOS | PW_SHELL)); cap_close(cappwd); /* * Allow: * fields: pw_passwd, pw_gid, pw_class, pw_dir, pw_expire */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); fields[0] = "pw_passwd"; fields[1] = "pw_gid"; fields[2] = "pw_class"; fields[3] = "pw_dir"; fields[4] = "pw_expire"; CHECK(cap_pwd_limit_fields(cappwd, fields, 5) == 0); fields[5] = "pw_uid"; CHECK(cap_pwd_limit_fields(cappwd, fields, 6) == -1 && errno == ENOTCAPABLE); fields[0] = "pw_uid"; CHECK(cap_pwd_limit_fields(cappwd, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(cappwd, PW_PASSWD | PW_GID | PW_CLASS | PW_DIR | PW_EXPIRE)); cap_close(cappwd); /* * Allow: * fields: pw_uid, pw_class, pw_shell */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); fields[0] = "pw_uid"; fields[1] = "pw_class"; fields[2] = "pw_shell"; CHECK(cap_pwd_limit_fields(cappwd, fields, 3) == 0); fields[3] = "pw_change"; CHECK(cap_pwd_limit_fields(cappwd, fields, 4) == -1 && errno == ENOTCAPABLE); fields[0] = "pw_change"; CHECK(cap_pwd_limit_fields(cappwd, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(cappwd, PW_UID | PW_CLASS | PW_SHELL)); cap_close(cappwd); /* * Allow: * fields: pw_change */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); fields[0] = "pw_change"; CHECK(cap_pwd_limit_fields(cappwd, fields, 1) == 0); fields[1] = "pw_uid"; CHECK(cap_pwd_limit_fields(cappwd, fields, 2) == -1 && errno == ENOTCAPABLE); fields[0] = "pw_uid"; CHECK(cap_pwd_limit_fields(cappwd, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(cappwd, PW_CHANGE)); cap_close(cappwd); } static bool runtest_users(cap_channel_t *cappwd, const char **names, const uid_t *uids, size_t nusers) { char buf[1024]; struct passwd *pwd; struct passwd st; unsigned int i, got; cap_setpwent(cappwd); got = 0; for (;;) { pwd = cap_getpwent(cappwd); if (pwd == NULL) break; got++; for (i = 0; i < nusers; i++) { if (strcmp(names[i], pwd->pw_name) == 0 && uids[i] == pwd->pw_uid) { break; } } if (i == nusers) return (false); } if (got != nusers) return (false); cap_setpwent(cappwd); got = 0; for (;;) { cap_getpwent_r(cappwd, &st, buf, sizeof(buf), &pwd); if (pwd == NULL) break; got++; for (i = 0; i < nusers; i++) { if (strcmp(names[i], pwd->pw_name) == 0 && uids[i] == pwd->pw_uid) { break; } } if (i == nusers) return (false); } if (got != nusers) return (false); for (i = 0; i < nusers; i++) { pwd = cap_getpwnam(cappwd, names[i]); if (pwd == NULL) return (false); } for (i = 0; i < nusers; i++) { cap_getpwnam_r(cappwd, names[i], &st, buf, sizeof(buf), &pwd); if (pwd == NULL) return (false); } for (i = 0; i < nusers; i++) { pwd = cap_getpwuid(cappwd, uids[i]); if (pwd == NULL) return (false); } for (i = 0; i < nusers; i++) { cap_getpwuid_r(cappwd, uids[i], &st, buf, sizeof(buf), &pwd); if (pwd == NULL) return (false); } return (true); } static void test_users(cap_channel_t *origcappwd) { cap_channel_t *cappwd; const char *names[6]; uid_t uids[6]; /* * Allow: * users: * names: root, toor, daemon, operator, bin, tty * uids: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); names[0] = "root"; names[1] = "toor"; names[2] = "daemon"; names[3] = "operator"; names[4] = "bin"; names[5] = "tty"; CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0); uids[0] = 0; uids[1] = 0; uids[2] = 1; uids[3] = 2; uids[4] = 3; uids[5] = 4; CHECK(runtest_users(cappwd, names, uids, 6)); cap_close(cappwd); /* * Allow: * users: * names: daemon, operator, bin * uids: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); names[0] = "daemon"; names[1] = "operator"; names[2] = "bin"; CHECK(cap_pwd_limit_users(cappwd, names, 3, NULL, 0) == 0); names[3] = "tty"; CHECK(cap_pwd_limit_users(cappwd, names, 4, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "tty"; CHECK(cap_pwd_limit_users(cappwd, names, 1, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "daemon"; uids[0] = 1; uids[1] = 2; uids[2] = 3; CHECK(runtest_users(cappwd, names, uids, 3)); cap_close(cappwd); /* * Allow: * users: * names: daemon, bin, tty * uids: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); names[0] = "daemon"; names[1] = "bin"; names[2] = "tty"; CHECK(cap_pwd_limit_users(cappwd, names, 3, NULL, 0) == 0); names[3] = "operator"; CHECK(cap_pwd_limit_users(cappwd, names, 4, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "operator"; CHECK(cap_pwd_limit_users(cappwd, names, 1, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "daemon"; uids[0] = 1; uids[1] = 3; uids[2] = 4; CHECK(runtest_users(cappwd, names, uids, 3)); cap_close(cappwd); /* * Allow: * users: * names: * uids: 1, 2, 3 */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); names[0] = "daemon"; names[1] = "operator"; names[2] = "bin"; uids[0] = 1; uids[1] = 2; uids[2] = 3; CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 3) == 0); uids[3] = 4; CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 4) == -1 && errno == ENOTCAPABLE); uids[0] = 4; CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 1) == -1 && errno == ENOTCAPABLE); uids[0] = 1; CHECK(runtest_users(cappwd, names, uids, 3)); cap_close(cappwd); /* * Allow: * users: * names: * uids: 1, 3, 4 */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); names[0] = "daemon"; names[1] = "bin"; names[2] = "tty"; uids[0] = 1; uids[1] = 3; uids[2] = 4; CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 3) == 0); uids[3] = 5; CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 4) == -1 && errno == ENOTCAPABLE); uids[0] = 5; CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 1) == -1 && errno == ENOTCAPABLE); uids[0] = 1; CHECK(runtest_users(cappwd, names, uids, 3)); cap_close(cappwd); /* * Allow: * users: * names: bin * uids: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); names[0] = "bin"; CHECK(cap_pwd_limit_users(cappwd, names, 1, NULL, 0) == 0); names[1] = "operator"; CHECK(cap_pwd_limit_users(cappwd, names, 2, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "operator"; CHECK(cap_pwd_limit_users(cappwd, names, 1, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "bin"; uids[0] = 3; CHECK(runtest_users(cappwd, names, uids, 1)); cap_close(cappwd); /* * Allow: * users: * names: daemon, tty * uids: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); names[0] = "daemon"; names[1] = "tty"; CHECK(cap_pwd_limit_users(cappwd, names, 2, NULL, 0) == 0); names[2] = "operator"; CHECK(cap_pwd_limit_users(cappwd, names, 3, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "operator"; CHECK(cap_pwd_limit_users(cappwd, names, 1, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "daemon"; uids[0] = 1; uids[1] = 4; CHECK(runtest_users(cappwd, names, uids, 2)); cap_close(cappwd); /* * Allow: * users: * names: * uids: 3 */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); names[0] = "bin"; uids[0] = 3; CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 1) == 0); uids[1] = 4; CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 2) == -1 && errno == ENOTCAPABLE); uids[0] = 4; CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 1) == -1 && errno == ENOTCAPABLE); uids[0] = 3; CHECK(runtest_users(cappwd, names, uids, 1)); cap_close(cappwd); /* * Allow: * users: * names: * uids: 1, 4 */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); names[0] = "daemon"; names[1] = "tty"; uids[0] = 1; uids[1] = 4; CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 2) == 0); uids[2] = 3; CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 3) == -1 && errno == ENOTCAPABLE); uids[0] = 3; CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 1) == -1 && errno == ENOTCAPABLE); uids[0] = 1; CHECK(runtest_users(cappwd, names, uids, 2)); cap_close(cappwd); } int main(void) { cap_channel_t *capcas, *cappwd; printf("1..188\n"); fflush(stdout); capcas = cap_init(); CHECKX(capcas != NULL); cappwd = cap_service_open(capcas, "system.pwd"); CHECKX(cappwd != NULL); cap_close(capcas); /* No limits. */ CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R | GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R)); test_cmds(cappwd); test_fields(cappwd); test_users(cappwd); cap_close(cappwd); exit(0); } Index: head/lib/libcasper/services/cap_random/cap_random.c =================================================================== --- head/lib/libcasper/services/cap_random/cap_random.c (revision 328472) +++ head/lib/libcasper/services/cap_random/cap_random.c (revision 328473) @@ -1,117 +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); 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/cap_sysctl.c =================================================================== --- head/lib/libcasper/services/cap_sysctl/cap_sysctl.c (revision 328472) +++ head/lib/libcasper/services/cap_sysctl/cap_sysctl.c (revision 328473) @@ -1,293 +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); 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_sysctl/tests/sysctl_test.c =================================================================== --- head/lib/libcasper/services/cap_sysctl/tests/sysctl_test.c (revision 328472) +++ head/lib/libcasper/services/cap_sysctl/tests/sysctl_test.c (revision 328473) @@ -1,1513 +1,1515 @@ /*- + * 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 #include #include #include /* * We need some sysctls to perform the tests on. * We remember their values and restore them afer the test is done. */ #define SYSCTL0_PARENT "kern" #define SYSCTL0_NAME "kern.sync_on_panic" #define SYSCTL1_PARENT "debug" #define SYSCTL1_NAME "debug.minidump" static int ntest = 1; #define CHECK(expr) do { \ if ((expr)) \ printf("ok %d # %s:%u\n", ntest, __FILE__, __LINE__); \ else \ printf("not ok %d # %s:%u\n", ntest, __FILE__, __LINE__); \ fflush(stdout); \ ntest++; \ } while (0) #define CHECKX(expr) do { \ if ((expr)) { \ printf("ok %d # %s:%u\n", ntest, __FILE__, __LINE__); \ } else { \ printf("not ok %d # %s:%u\n", ntest, __FILE__, __LINE__); \ exit(1); \ } \ fflush(stdout); \ ntest++; \ } while (0) #define SYSCTL0_READ0 0x0001 #define SYSCTL0_READ1 0x0002 #define SYSCTL0_READ2 0x0004 #define SYSCTL0_WRITE 0x0008 #define SYSCTL0_READ_WRITE 0x0010 #define SYSCTL1_READ0 0x0020 #define SYSCTL1_READ1 0x0040 #define SYSCTL1_READ2 0x0080 #define SYSCTL1_WRITE 0x0100 #define SYSCTL1_READ_WRITE 0x0200 static unsigned int runtest(cap_channel_t *capsysctl) { unsigned int result; int oldvalue, newvalue; size_t oldsize; result = 0; oldsize = sizeof(oldvalue); if (cap_sysctlbyname(capsysctl, SYSCTL0_NAME, &oldvalue, &oldsize, NULL, 0) == 0) { if (oldsize == sizeof(oldvalue)) result |= SYSCTL0_READ0; } newvalue = 123; if (cap_sysctlbyname(capsysctl, SYSCTL0_NAME, NULL, NULL, &newvalue, sizeof(newvalue)) == 0) { result |= SYSCTL0_WRITE; } if ((result & SYSCTL0_WRITE) != 0) { oldsize = sizeof(oldvalue); if (cap_sysctlbyname(capsysctl, SYSCTL0_NAME, &oldvalue, &oldsize, NULL, 0) == 0) { if (oldsize == sizeof(oldvalue) && oldvalue == 123) result |= SYSCTL0_READ1; } } oldsize = sizeof(oldvalue); newvalue = 4567; if (cap_sysctlbyname(capsysctl, SYSCTL0_NAME, &oldvalue, &oldsize, &newvalue, sizeof(newvalue)) == 0) { if (oldsize == sizeof(oldvalue) && oldvalue == 123) result |= SYSCTL0_READ_WRITE; } if ((result & SYSCTL0_READ_WRITE) != 0) { oldsize = sizeof(oldvalue); if (cap_sysctlbyname(capsysctl, SYSCTL0_NAME, &oldvalue, &oldsize, NULL, 0) == 0) { if (oldsize == sizeof(oldvalue) && oldvalue == 4567) result |= SYSCTL0_READ2; } } oldsize = sizeof(oldvalue); if (cap_sysctlbyname(capsysctl, SYSCTL1_NAME, &oldvalue, &oldsize, NULL, 0) == 0) { if (oldsize == sizeof(oldvalue)) result |= SYSCTL1_READ0; } newvalue = 506; if (cap_sysctlbyname(capsysctl, SYSCTL1_NAME, NULL, NULL, &newvalue, sizeof(newvalue)) == 0) { result |= SYSCTL1_WRITE; } if ((result & SYSCTL1_WRITE) != 0) { oldsize = sizeof(oldvalue); if (cap_sysctlbyname(capsysctl, SYSCTL1_NAME, &oldvalue, &oldsize, NULL, 0) == 0) { if (oldsize == sizeof(oldvalue) && oldvalue == 506) result |= SYSCTL1_READ1; } } oldsize = sizeof(oldvalue); newvalue = 7008; if (cap_sysctlbyname(capsysctl, SYSCTL1_NAME, &oldvalue, &oldsize, &newvalue, sizeof(newvalue)) == 0) { if (oldsize == sizeof(oldvalue) && oldvalue == 506) result |= SYSCTL1_READ_WRITE; } if ((result & SYSCTL1_READ_WRITE) != 0) { oldsize = sizeof(oldvalue); if (cap_sysctlbyname(capsysctl, SYSCTL1_NAME, &oldvalue, &oldsize, NULL, 0) == 0) { if (oldsize == sizeof(oldvalue) && oldvalue == 7008) result |= SYSCTL1_READ2; } } return (result); } static void test_operation(cap_channel_t *origcapsysctl) { cap_channel_t *capsysctl; nvlist_t *limits; /* * Allow: * SYSCTL0_PARENT/RDWR/RECURSIVE * SYSCTL1_PARENT/RDWR/RECURSIVE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, "foo.bar", CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, "foo.bar", CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == (SYSCTL0_READ0 | SYSCTL0_READ1 | SYSCTL0_READ2 | SYSCTL0_WRITE | SYSCTL0_READ_WRITE | SYSCTL1_READ0 | SYSCTL1_READ1 | SYSCTL1_READ2 | SYSCTL1_WRITE | SYSCTL1_READ_WRITE)); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == 0); CHECK(runtest(capsysctl) == (SYSCTL0_READ0 | SYSCTL0_READ1 | SYSCTL0_READ2 | SYSCTL0_WRITE | SYSCTL0_READ_WRITE | SYSCTL1_READ0 | SYSCTL1_READ1 | SYSCTL1_READ2 | SYSCTL1_WRITE | SYSCTL1_READ_WRITE)); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == 0); CHECK(runtest(capsysctl) == (SYSCTL0_READ0 | SYSCTL1_WRITE)); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == 0); CHECK(runtest(capsysctl) == (SYSCTL0_READ0 | SYSCTL1_WRITE)); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == 0); CHECK(runtest(capsysctl) == SYSCTL0_READ0); cap_close(capsysctl); /* * Allow: * SYSCTL0_NAME/RDWR/RECURSIVE * SYSCTL1_NAME/RDWR/RECURSIVE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == (SYSCTL0_READ0 | SYSCTL0_READ1 | SYSCTL0_READ2 | SYSCTL0_WRITE | SYSCTL0_READ_WRITE | SYSCTL1_READ0 | SYSCTL1_READ1 | SYSCTL1_READ2 | SYSCTL1_WRITE | SYSCTL1_READ_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/RDWR * SYSCTL1_PARENT/RDWR */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == 0); cap_close(capsysctl); /* * Allow: * SYSCTL0_NAME/RDWR * SYSCTL1_NAME/RDWR */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_RDWR); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == (SYSCTL0_READ0 | SYSCTL0_READ1 | SYSCTL0_READ2 | SYSCTL0_WRITE | SYSCTL0_READ_WRITE | SYSCTL1_READ0 | SYSCTL1_READ1 | SYSCTL1_READ2 | SYSCTL1_WRITE | SYSCTL1_READ_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/RDWR * SYSCTL1_PARENT/RDWR/RECURSIVE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == (SYSCTL1_READ0 | SYSCTL1_READ1 | SYSCTL1_READ2 | SYSCTL1_WRITE | SYSCTL1_READ_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL0_NAME/RDWR * SYSCTL1_NAME/RDWR/RECURSIVE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_RDWR); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == (SYSCTL0_READ0 | SYSCTL0_READ1 | SYSCTL0_READ2 | SYSCTL0_WRITE | SYSCTL0_READ_WRITE | SYSCTL1_READ0 | SYSCTL1_READ1 | SYSCTL1_READ2 | SYSCTL1_WRITE | SYSCTL1_READ_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/READ/RECURSIVE * SYSCTL1_PARENT/READ/RECURSIVE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_WRITE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == (SYSCTL0_READ0 | SYSCTL1_READ0)); cap_close(capsysctl); /* * Allow: * SYSCTL0_NAME/READ/RECURSIVE * SYSCTL1_NAME/READ/RECURSIVE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_RDWR); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_WRITE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == (SYSCTL0_READ0 | SYSCTL1_READ0)); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/READ * SYSCTL1_PARENT/READ */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_WRITE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == 0); cap_close(capsysctl); /* * Allow: * SYSCTL0_NAME/READ * SYSCTL1_NAME/READ */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_RDWR); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_WRITE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == (SYSCTL0_READ0 | SYSCTL1_READ0)); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/READ * SYSCTL1_PARENT/READ/RECURSIVE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == SYSCTL1_READ0); cap_close(capsysctl); /* * Allow: * SYSCTL0_NAME/READ * SYSCTL1_NAME/READ/RECURSIVE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == (SYSCTL0_READ0 | SYSCTL1_READ0)); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/WRITE/RECURSIVE * SYSCTL1_PARENT/WRITE/RECURSIVE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == (SYSCTL0_WRITE | SYSCTL1_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL0_NAME/WRITE/RECURSIVE * SYSCTL1_NAME/WRITE/RECURSIVE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_RDWR); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == (SYSCTL0_WRITE | SYSCTL1_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/WRITE * SYSCTL1_PARENT/WRITE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_WRITE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == 0); cap_close(capsysctl); /* * Allow: * SYSCTL0_NAME/WRITE * SYSCTL1_NAME/WRITE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_WRITE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_RDWR); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == (SYSCTL0_WRITE | SYSCTL1_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/WRITE * SYSCTL1_PARENT/WRITE/RECURSIVE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_WRITE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == SYSCTL1_WRITE); cap_close(capsysctl); /* * Allow: * SYSCTL0_NAME/WRITE * SYSCTL1_NAME/WRITE/RECURSIVE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_WRITE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == (SYSCTL0_WRITE | SYSCTL1_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/READ/RECURSIVE * SYSCTL1_PARENT/WRITE/RECURSIVE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == 0); CHECK(runtest(capsysctl) == (SYSCTL0_READ0 | SYSCTL1_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL0_NAME/READ/RECURSIVE * SYSCTL1_NAME/WRITE/RECURSIVE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == 0); CHECK(runtest(capsysctl) == (SYSCTL0_READ0 | SYSCTL1_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/READ * SYSCTL1_PARENT/WRITE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == 0); CHECK(runtest(capsysctl) == 0); cap_close(capsysctl); /* * Allow: * SYSCTL0_NAME/READ * SYSCTL1_NAME/WRITE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == 0); CHECK(runtest(capsysctl) == (SYSCTL0_READ0 | SYSCTL1_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/READ * SYSCTL1_PARENT/WRITE/RECURSIVE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == 0); CHECK(runtest(capsysctl) == SYSCTL1_WRITE); cap_close(capsysctl); /* * Allow: * SYSCTL0_NAME/READ * SYSCTL1_NAME/WRITE/RECURSIVE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == 0); CHECK(runtest(capsysctl) == (SYSCTL0_READ0 | SYSCTL1_WRITE)); cap_close(capsysctl); } static void test_names(cap_channel_t *origcapsysctl) { cap_channel_t *capsysctl; nvlist_t *limits; /* * Allow: * SYSCTL0_PARENT/READ/RECURSIVE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == SYSCTL0_READ0); cap_close(capsysctl); /* * Allow: * SYSCTL1_NAME/READ/RECURSIVE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == SYSCTL1_READ0); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/WRITE/RECURSIVE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_WRITE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == SYSCTL0_WRITE); cap_close(capsysctl); /* * Allow: * SYSCTL1_NAME/WRITE/RECURSIVE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_WRITE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == SYSCTL1_WRITE); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/RDWR/RECURSIVE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == (SYSCTL0_READ0 | SYSCTL0_READ1 | SYSCTL0_READ2 | SYSCTL0_WRITE | SYSCTL0_READ_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL1_NAME/RDWR/RECURSIVE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == (SYSCTL1_READ0 | SYSCTL1_READ1 | SYSCTL1_READ2 | SYSCTL1_WRITE | SYSCTL1_READ_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/READ */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_READ); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == 0); cap_close(capsysctl); /* * Allow: * SYSCTL1_NAME/READ */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_READ); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == SYSCTL1_READ0); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/WRITE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_WRITE); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == 0); cap_close(capsysctl); /* * Allow: * SYSCTL1_NAME/WRITE */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_WRITE); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_WRITE); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == SYSCTL1_WRITE); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/RDWR */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_PARENT, CAP_SYSCTL_RDWR); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_PARENT, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == 0); cap_close(capsysctl); /* * Allow: * SYSCTL1_NAME/RDWR */ capsysctl = cap_clone(origcapsysctl); CHECK(capsysctl != NULL); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == 0); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_RDWR); nvlist_add_number(limits, SYSCTL1_NAME, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); limits = nvlist_create(0); nvlist_add_number(limits, SYSCTL0_NAME, CAP_SYSCTL_RDWR); CHECK(cap_limit_set(capsysctl, limits) == -1 && errno == ENOTCAPABLE); CHECK(runtest(capsysctl) == (SYSCTL1_READ0 | SYSCTL1_READ1 | SYSCTL1_READ2 | SYSCTL1_WRITE | SYSCTL1_READ_WRITE)); cap_close(capsysctl); } int main(void) { cap_channel_t *capcas, *capsysctl; int scvalue0, scvalue1; size_t scsize; printf("1..256\n"); fflush(stdout); scsize = sizeof(scvalue0); CHECKX(sysctlbyname(SYSCTL0_NAME, &scvalue0, &scsize, NULL, 0) == 0); CHECKX(scsize == sizeof(scvalue0)); scsize = sizeof(scvalue1); CHECKX(sysctlbyname(SYSCTL1_NAME, &scvalue1, &scsize, NULL, 0) == 0); CHECKX(scsize == sizeof(scvalue1)); capcas = cap_init(); CHECKX(capcas != NULL); capsysctl = cap_service_open(capcas, "system.sysctl"); CHECKX(capsysctl != NULL); cap_close(capcas); /* No limits set. */ CHECK(runtest(capsysctl) == (SYSCTL0_READ0 | SYSCTL0_READ1 | SYSCTL0_READ2 | SYSCTL0_WRITE | SYSCTL0_READ_WRITE | SYSCTL1_READ0 | SYSCTL1_READ1 | SYSCTL1_READ2 | SYSCTL1_WRITE | SYSCTL1_READ_WRITE)); test_operation(capsysctl); test_names(capsysctl); cap_close(capsysctl); CHECK(sysctlbyname(SYSCTL0_NAME, NULL, NULL, &scvalue0, sizeof(scvalue0)) == 0); CHECK(sysctlbyname(SYSCTL1_NAME, NULL, NULL, &scvalue1, sizeof(scvalue1)) == 0); exit(0); } Index: head/lib/libcasper/services/cap_syslog/cap_syslog.c =================================================================== --- head/lib/libcasper/services/cap_syslog/cap_syslog.c (revision 328472) +++ head/lib/libcasper/services/cap_syslog/cap_syslog.c (revision 328473) @@ -1,199 +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); 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); 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); 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); 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);