From owner-svn-src-head@FreeBSD.ORG Mon Dec 2 10:18:26 2013 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id D5C41ACE; Mon, 2 Dec 2013 10:18:26 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id C0B1315FC; Mon, 2 Dec 2013 10:18:26 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.7/8.14.7) with ESMTP id rB2AIQrV018815; Mon, 2 Dec 2013 10:18:26 GMT (envelope-from pjd@svn.freebsd.org) Received: (from pjd@localhost) by svn.freebsd.org (8.14.7/8.14.5/Submit) id rB2AIQ5l018810; Mon, 2 Dec 2013 10:18:26 GMT (envelope-from pjd@svn.freebsd.org) Message-Id: <201312021018.rB2AIQ5l018810@svn.freebsd.org> From: Pawel Jakub Dawidek Date: Mon, 2 Dec 2013 10:18:26 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r258843 - head/lib/libcasper X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.16 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 02 Dec 2013 10:18:26 -0000 Author: pjd Date: Mon Dec 2 10:18:25 2013 New Revision: 258843 URL: http://svnweb.freebsd.org/changeset/base/258843 Log: Forgot to 'svn add' lib/libcasper/ directory. Reported by: glebius Added: head/lib/libcasper/ head/lib/libcasper/Makefile (contents, props changed) head/lib/libcasper/libcasper.c (contents, props changed) head/lib/libcasper/libcasper.h (contents, props changed) head/lib/libcasper/libcasper_impl.h (contents, props changed) Added: head/lib/libcasper/Makefile ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/lib/libcasper/Makefile Mon Dec 2 10:18:25 2013 (r258843) @@ -0,0 +1,19 @@ +# $FreeBSD$ + +LIB= casper + +SHLIB_MAJOR= 0 + +SRCS= libcasper.c +INCS= libcasper.h + +DPADD= ${LIBCAPSICUM} ${LIBNV} ${LIBPJDLOG} +LDADD= -lcapsicum -lnv -lpjdlog + +CFLAGS+=-I${.CURDIR} +CFLAGS+=-I${.CURDIR}/../libpjdlog +CFLAGS+=-I${.CURDIR}/../../sbin/casper + +WARNS?= 6 + +.include Added: head/lib/libcasper/libcasper.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/lib/libcasper/libcasper.c Mon Dec 2 10:18:25 2013 (r258843) @@ -0,0 +1,441 @@ +/*- + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * 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; + 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) +{ + 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; + TAILQ_INIT(&service->s_connections); + service->s_magic = SERVICE_MAGIC; + + return (service); +} + +void +service_free(struct service *service) +{ + struct service_connection *sconn; + + PJDLOG_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; + + PJDLOG_ASSERT(service->s_magic = SERVICE_MAGIC); + + sconn = malloc(sizeof(*sconn)); + if (sconn == NULL) { + pjdlog_error("Unable to allocate memory for service connection."); + return (NULL); + } + sconn->sc_chan = cap_wrap(sock); + if (sconn->sc_chan == NULL) { + serrno = errno; + pjdlog_error("Unable to wrap communication channel."); + 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; + pjdlog_error("Unable to clone limits."); + (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) +{ + + PJDLOG_ASSERT(service->s_magic = SERVICE_MAGIC); + PJDLOG_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; + + PJDLOG_ASSERT(service->s_magic = SERVICE_MAGIC); + + sconn = TAILQ_FIRST(&service->s_connections); + PJDLOG_ASSERT(sconn == NULL || + sconn->sc_magic == SERVICE_CONNECTION_MAGIC); + return (sconn); +} + +struct service_connection * +service_connection_next(struct service_connection *sconn) +{ + + PJDLOG_ASSERT(sconn->sc_magic == SERVICE_CONNECTION_MAGIC); + + sconn = TAILQ_NEXT(sconn, sc_next); + PJDLOG_ASSERT(sconn == NULL || + sconn->sc_magic == SERVICE_CONNECTION_MAGIC); + return (sconn); +} + +cap_channel_t * +service_connection_get_chan(const struct service_connection *sconn) +{ + + PJDLOG_ASSERT(sconn->sc_magic == SERVICE_CONNECTION_MAGIC); + + return (sconn->sc_chan); +} + +int +service_connection_get_sock(const struct service_connection *sconn) +{ + + PJDLOG_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) +{ + + PJDLOG_ASSERT(sconn->sc_magic == SERVICE_CONNECTION_MAGIC); + + return (sconn->sc_limits); +} + +void +service_connection_set_limits(struct service_connection *sconn, + nvlist_t *limits) +{ + + PJDLOG_ASSERT(sconn->sc_magic == SERVICE_CONNECTION_MAGIC); + + nvlist_destroy(sconn->sc_limits); + sconn->sc_limits = limits; +} + +#if 0 +static void +casper_message_connection(struct service *service, const nvlist_t *nvl) +{ + + service_connection_add(&service->s_connections, + nvlist_get_descriptor(nvl, "sock")); +} + +static void +casper_message(const cap_channel_t *capcas, struct service *service) +{ + const char *cmd; + nvlist_t *nvl; + + nvl = cap_recv_nvlist(capcas); + if (nvl == NULL) + pjdlog_exit(1, "Unable to receive message from Casper"); + cmd = nvlist_get_string(nvl, "cmd"); + if (strcmp(cmd, "connection") == 0) + casper_message_connection(service, nvl); + else + PJDLOG_ABORT("Unknown command from Casper: %s.", cmd); +} +#endif + +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)); + if (nvlin == NULL) { + if (errno == ENOTCONN) { + pjdlog_debug(1, "Connection closed by the client."); + } else { + pjdlog_errno(LOG_ERR, + "Unable to receive message from client"); + } + service_connection_remove(service, sconn); + return; + } + + error = EDOOFUS; + nvlout = nvlist_create(0); + + cmd = nvlist_get_string(nvlin, "cmd"); + pjdlog_debug(1, "Command received from client: %s.", cmd); + if (pjdlog_debug_get() >= 2) + nvlist_fdump(nvlin, stderr); + if (strcmp(cmd, "limit_set") == 0) { + nvlist_t *nvllim; + + nvllim = nvlist_take_nvlist(nvlin, "limits"); + 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_add_descriptor(nvlout, "sock", sock); + error = 0; + } + } else { + nvlout = nvlist_create(0); + error = service->s_command(cmd, + service_connection_get_limits(sconn), nvlin, nvlout); + } + + nvlist_destroy(nvlin); + nvlist_add_number(nvlout, "error", (uint64_t)error); + pjdlog_debug(1, "Sending reply to client (error=%d).", error); + if (pjdlog_debug_get() >= 2) + nvlist_fdump(nvlout, stderr); + + if (cap_send_nvlist(service_connection_get_chan(sconn), nvlout) == -1) { + pjdlog_errno(LOG_ERR, "Unable to send message to client"); + service_connection_remove(service, sconn); + return; + } +} + +static int +fd_add(fd_set *fdsp, int maxfd, int fd) +{ + + FD_SET(fd, fdsp); + return (fd > maxfd ? fd : maxfd); +} + +int +service_start(const char *name, int sock, service_limit_func_t *limitfunc, + service_command_func_t *commandfunc, int argc, char *argv[]) +{ + struct service *service; + struct service_connection *sconn, *sconntmp; + fd_set fds; + int maxfd, nfds, serrno; + + assert(argc == 2); + + pjdlog_init(PJDLOG_MODE_STD); + pjdlog_debug_set(atoi(argv[1])); + + service = service_alloc(name, limitfunc, commandfunc); + if (service == NULL) + return (errno); + if (service_connection_add(service, sock, NULL) == NULL) { + serrno = errno; + service_free(service); + return (serrno); + } + + 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)); + } + + PJDLOG_ASSERT(maxfd >= 0); + PJDLOG_ASSERT(maxfd + 1 <= (int)FD_SETSIZE); + nfds = select(maxfd + 1, &fds, NULL, NULL, NULL); + if (nfds < 0) { + if (errno != EINTR) + pjdlog_errno(LOG_ERR, "select() failed"); + continue; + } else if (nfds == 0) { + /* Timeout. */ + PJDLOG_ABORT("select() timeout"); + continue; + } + + 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; + } + } + + return (0); +} Added: head/lib/libcasper/libcasper.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/lib/libcasper/libcasper.h Mon Dec 2 10:18:25 2013 (r258843) @@ -0,0 +1,70 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _LIBCASPER_H_ +#define _LIBCASPER_H_ + +#ifndef _NVLIST_T_DECLARED +#define _NVLIST_T_DECLARED +struct nvlist; + +typedef struct nvlist nvlist_t; +#endif + +#define PARENT_FILENO 3 +#define EXECUTABLE_FILENO 4 +#define PROC_FILENO 5 + +struct service; +struct service_connection; + +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 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); + +int service_start(const char *name, int sock, service_limit_func_t *limitfunc, + service_command_func_t *commandfunc, int argc, char *argv[]); + +#endif /* !_LIBCASPER_H_ */ Added: head/lib/libcasper/libcasper_impl.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/lib/libcasper/libcasper_impl.h Mon Dec 2 10:18:25 2013 (r258843) @@ -0,0 +1,46 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _LIBCASPER_IMPL_H_ +#define _LIBCASPER_IMPL_H_ + +#include "libcasper.h" + +struct service; +struct service_connection; + +struct service * service_alloc(const char *name, + service_limit_func_t *limitfunc, service_command_func_t *commandfunc); +void service_free(struct service *service); + +void service_message(struct service *service, struct service_connection *sconn); + +#endif /* !_LIBCASPER_IMPL_H_ */