1c501d73cSMariusz Zaborski /*- 228b6f7c8SMariusz Zaborski * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 328b6f7c8SMariusz Zaborski * 4c501d73cSMariusz Zaborski * Copyright (c) 2012 The FreeBSD Foundation 5c501d73cSMariusz Zaborski * Copyright (c) 2015 Mariusz Zaborski <oshogbo@FreeBSD.org> 69612674fSRobert Watson * Copyright (c) 2017 Robert N. M. Watson 7c501d73cSMariusz Zaborski * All rights reserved. 8c501d73cSMariusz Zaborski * 9c501d73cSMariusz Zaborski * This software was developed by Pawel Jakub Dawidek under sponsorship from 10c501d73cSMariusz Zaborski * the FreeBSD Foundation. 11c501d73cSMariusz Zaborski * 129612674fSRobert Watson * This software was developed by SRI International and the University of 139612674fSRobert Watson * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) 149612674fSRobert Watson * ("CTSRD"), as part of the DARPA CRASH research programme. 159612674fSRobert Watson * 16c501d73cSMariusz Zaborski * Redistribution and use in source and binary forms, with or without 17c501d73cSMariusz Zaborski * modification, are permitted provided that the following conditions 18c501d73cSMariusz Zaborski * are met: 19c501d73cSMariusz Zaborski * 1. Redistributions of source code must retain the above copyright 20c501d73cSMariusz Zaborski * notice, this list of conditions and the following disclaimer. 21c501d73cSMariusz Zaborski * 2. Redistributions in binary form must reproduce the above copyright 22c501d73cSMariusz Zaborski * notice, this list of conditions and the following disclaimer in the 23c501d73cSMariusz Zaborski * documentation and/or other materials provided with the distribution. 24c501d73cSMariusz Zaborski * 25c501d73cSMariusz Zaborski * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 26c501d73cSMariusz Zaborski * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27c501d73cSMariusz Zaborski * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28c501d73cSMariusz Zaborski * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 29c501d73cSMariusz Zaborski * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30c501d73cSMariusz Zaborski * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31c501d73cSMariusz Zaborski * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32c501d73cSMariusz Zaborski * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33c501d73cSMariusz Zaborski * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34c501d73cSMariusz Zaborski * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35c501d73cSMariusz Zaborski * SUCH DAMAGE. 36c501d73cSMariusz Zaborski */ 37c501d73cSMariusz Zaborski 38c501d73cSMariusz Zaborski #include <sys/cdefs.h> 39c501d73cSMariusz Zaborski __FBSDID("$FreeBSD$"); 40c501d73cSMariusz Zaborski 41c501d73cSMariusz Zaborski #include <sys/types.h> 42c501d73cSMariusz Zaborski #include <sys/queue.h> 43c501d73cSMariusz Zaborski #include <sys/socket.h> 44c501d73cSMariusz Zaborski #include <sys/nv.h> 45c501d73cSMariusz Zaborski 46c501d73cSMariusz Zaborski #include <assert.h> 47c501d73cSMariusz Zaborski #include <errno.h> 48c501d73cSMariusz Zaborski #include <stdbool.h> 49c501d73cSMariusz Zaborski #include <stdio.h> 50c501d73cSMariusz Zaborski #include <stdlib.h> 51c501d73cSMariusz Zaborski #include <string.h> 52c501d73cSMariusz Zaborski #include <unistd.h> 53c501d73cSMariusz Zaborski 54c501d73cSMariusz Zaborski #include "libcasper_impl.h" 55c501d73cSMariusz Zaborski #include "zygote.h" 56c501d73cSMariusz Zaborski 57c501d73cSMariusz Zaborski struct casper_service { 58c501d73cSMariusz Zaborski struct service *cs_service; 59c501d73cSMariusz Zaborski TAILQ_ENTRY(casper_service) cs_next; 60c501d73cSMariusz Zaborski }; 61c501d73cSMariusz Zaborski 62c501d73cSMariusz Zaborski static TAILQ_HEAD(, casper_service) casper_services = 63c501d73cSMariusz Zaborski TAILQ_HEAD_INITIALIZER(casper_services); 64c501d73cSMariusz Zaborski 65c501d73cSMariusz Zaborski #define CORE_CASPER_NAME "core.casper" 66c501d73cSMariusz Zaborski #define CSERVICE_IS_CORE(service) \ 67c501d73cSMariusz Zaborski (strcmp(service_name(service->cs_service), CORE_CASPER_NAME) == 0) 68c501d73cSMariusz Zaborski 69c501d73cSMariusz Zaborski static struct casper_service * 70c501d73cSMariusz Zaborski service_find(const char *name) 71c501d73cSMariusz Zaborski { 72c501d73cSMariusz Zaborski struct casper_service *casserv; 73c501d73cSMariusz Zaborski 74c501d73cSMariusz Zaborski TAILQ_FOREACH(casserv, &casper_services, cs_next) { 75c501d73cSMariusz Zaborski if (strcmp(service_name(casserv->cs_service), name) == 0) 76c501d73cSMariusz Zaborski break; 77c501d73cSMariusz Zaborski } 78c501d73cSMariusz Zaborski return (casserv); 79c501d73cSMariusz Zaborski } 80c501d73cSMariusz Zaborski 81c501d73cSMariusz Zaborski struct casper_service * 82c501d73cSMariusz Zaborski service_register(const char *name, service_limit_func_t *limitfunc, 83920be817SMariusz Zaborski service_command_func_t *commandfunc, uint64_t flags) 84c501d73cSMariusz Zaborski { 85c501d73cSMariusz Zaborski struct casper_service *casserv; 86c501d73cSMariusz Zaborski 87c501d73cSMariusz Zaborski if (commandfunc == NULL) 88c501d73cSMariusz Zaborski return (NULL); 89c501d73cSMariusz Zaborski if (name == NULL || name[0] == '\0') 90c501d73cSMariusz Zaborski return (NULL); 91c501d73cSMariusz Zaborski if (service_find(name) != NULL) 92c501d73cSMariusz Zaborski return (NULL); 93c501d73cSMariusz Zaborski 94c501d73cSMariusz Zaborski casserv = malloc(sizeof(*casserv)); 95c501d73cSMariusz Zaborski if (casserv == NULL) 96c501d73cSMariusz Zaborski return (NULL); 97c501d73cSMariusz Zaborski 98920be817SMariusz Zaborski casserv->cs_service = service_alloc(name, limitfunc, commandfunc, 99920be817SMariusz Zaborski flags); 100c501d73cSMariusz Zaborski if (casserv->cs_service == NULL) { 101c501d73cSMariusz Zaborski free(casserv); 102c501d73cSMariusz Zaborski return (NULL); 103c501d73cSMariusz Zaborski } 104c501d73cSMariusz Zaborski TAILQ_INSERT_TAIL(&casper_services, casserv, cs_next); 105c501d73cSMariusz Zaborski 106c501d73cSMariusz Zaborski return (casserv); 107c501d73cSMariusz Zaborski } 108c501d73cSMariusz Zaborski 109c501d73cSMariusz Zaborski static bool 110c501d73cSMariusz Zaborski casper_allowed_service(const nvlist_t *limits, const char *service) 111c501d73cSMariusz Zaborski { 112c501d73cSMariusz Zaborski 113c501d73cSMariusz Zaborski if (limits == NULL) 114c501d73cSMariusz Zaborski return (true); 115c501d73cSMariusz Zaborski 116c501d73cSMariusz Zaborski if (nvlist_exists_null(limits, service)) 117c501d73cSMariusz Zaborski return (true); 118c501d73cSMariusz Zaborski 119c501d73cSMariusz Zaborski return (false); 120c501d73cSMariusz Zaborski } 121c501d73cSMariusz Zaborski 122c501d73cSMariusz Zaborski static int 123c501d73cSMariusz Zaborski casper_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits) 124c501d73cSMariusz Zaborski { 125c501d73cSMariusz Zaborski const char *name; 126c501d73cSMariusz Zaborski int type; 127c501d73cSMariusz Zaborski void *cookie; 128c501d73cSMariusz Zaborski 129c501d73cSMariusz Zaborski cookie = NULL; 130c501d73cSMariusz Zaborski while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) { 131c501d73cSMariusz Zaborski if (type != NV_TYPE_NULL) 132c501d73cSMariusz Zaborski return (EINVAL); 133c501d73cSMariusz Zaborski if (!casper_allowed_service(oldlimits, name)) 134c501d73cSMariusz Zaborski return (ENOTCAPABLE); 135c501d73cSMariusz Zaborski } 136c501d73cSMariusz Zaborski 137c501d73cSMariusz Zaborski return (0); 138c501d73cSMariusz Zaborski } 139c501d73cSMariusz Zaborski 1409612674fSRobert Watson void 141c501d73cSMariusz Zaborski service_execute(int chanfd) 142c501d73cSMariusz Zaborski { 1439612674fSRobert Watson struct casper_service *casserv; 144c501d73cSMariusz Zaborski struct service *service; 1459612674fSRobert Watson const char *servname; 146c501d73cSMariusz Zaborski nvlist_t *nvl; 147c501d73cSMariusz Zaborski int procfd; 148c501d73cSMariusz Zaborski 149c501d73cSMariusz Zaborski nvl = nvlist_recv(chanfd, 0); 150c501d73cSMariusz Zaborski if (nvl == NULL) 151c501d73cSMariusz Zaborski exit(1); 1529612674fSRobert Watson if (!nvlist_exists_string(nvl, "service")) 1539612674fSRobert Watson exit(1); 1549612674fSRobert Watson servname = nvlist_get_string(nvl, "service"); 1559612674fSRobert Watson casserv = service_find(servname); 1569612674fSRobert Watson if (casserv == NULL) 1579612674fSRobert Watson exit(1); 1589612674fSRobert Watson service = casserv->cs_service; 159c501d73cSMariusz Zaborski procfd = nvlist_take_descriptor(nvl, "procfd"); 160c501d73cSMariusz Zaborski nvlist_destroy(nvl); 161c501d73cSMariusz Zaborski 162920be817SMariusz Zaborski service_start(service, chanfd, procfd); 163c501d73cSMariusz Zaborski /* Not reached. */ 164c501d73cSMariusz Zaborski exit(1); 165c501d73cSMariusz Zaborski } 166c501d73cSMariusz Zaborski 167c501d73cSMariusz Zaborski static int 168c501d73cSMariusz Zaborski casper_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin, 169c501d73cSMariusz Zaborski nvlist_t *nvlout) 170c501d73cSMariusz Zaborski { 171c501d73cSMariusz Zaborski struct casper_service *casserv; 172c501d73cSMariusz Zaborski const char *servname; 173c501d73cSMariusz Zaborski nvlist_t *nvl; 174c501d73cSMariusz Zaborski int chanfd, procfd, error; 175c501d73cSMariusz Zaborski 176c501d73cSMariusz Zaborski if (strcmp(cmd, "open") != 0) 177c501d73cSMariusz Zaborski return (EINVAL); 178c501d73cSMariusz Zaborski if (!nvlist_exists_string(nvlin, "service")) 179c501d73cSMariusz Zaborski return (EINVAL); 180c501d73cSMariusz Zaborski 181c501d73cSMariusz Zaborski servname = nvlist_get_string(nvlin, "service"); 182c501d73cSMariusz Zaborski casserv = service_find(servname); 183c501d73cSMariusz Zaborski if (casserv == NULL) 184c501d73cSMariusz Zaborski return (ENOENT); 185c501d73cSMariusz Zaborski 186c501d73cSMariusz Zaborski if (!casper_allowed_service(limits, servname)) 187c501d73cSMariusz Zaborski return (ENOTCAPABLE); 188c501d73cSMariusz Zaborski 1899612674fSRobert Watson if (zygote_clone_service_execute(&chanfd, &procfd) == -1) 190c501d73cSMariusz Zaborski return (errno); 191c501d73cSMariusz Zaborski 192c501d73cSMariusz Zaborski nvl = nvlist_create(0); 1939612674fSRobert Watson nvlist_add_string(nvl, "service", servname); 194c501d73cSMariusz Zaborski nvlist_move_descriptor(nvl, "procfd", procfd); 195c501d73cSMariusz Zaborski if (nvlist_send(chanfd, nvl) == -1) { 196c501d73cSMariusz Zaborski error = errno; 197c501d73cSMariusz Zaborski nvlist_destroy(nvl); 198c501d73cSMariusz Zaborski close(chanfd); 199c501d73cSMariusz Zaborski return (error); 200c501d73cSMariusz Zaborski } 201c501d73cSMariusz Zaborski nvlist_destroy(nvl); 202c501d73cSMariusz Zaborski 203c501d73cSMariusz Zaborski nvlist_move_descriptor(nvlout, "chanfd", chanfd); 2044fc0a279SMariusz Zaborski nvlist_add_number(nvlout, "chanflags", 2054fc0a279SMariusz Zaborski service_get_channel_flags(casserv->cs_service)); 206c501d73cSMariusz Zaborski 207c501d73cSMariusz Zaborski return (0); 208c501d73cSMariusz Zaborski } 209c501d73cSMariusz Zaborski 210c501d73cSMariusz Zaborski static void 211c501d73cSMariusz Zaborski service_register_core(int fd) 212c501d73cSMariusz Zaborski { 213c501d73cSMariusz Zaborski struct casper_service *casserv; 214c501d73cSMariusz Zaborski struct service_connection *sconn; 215c501d73cSMariusz Zaborski 216c501d73cSMariusz Zaborski casserv = service_register(CORE_CASPER_NAME, casper_limit, 217920be817SMariusz Zaborski casper_command, 0); 218c501d73cSMariusz Zaborski sconn = service_connection_add(casserv->cs_service, fd, NULL); 219c501d73cSMariusz Zaborski if (sconn == NULL) { 220c501d73cSMariusz Zaborski close(fd); 221c501d73cSMariusz Zaborski abort(); 222c501d73cSMariusz Zaborski } 223c501d73cSMariusz Zaborski } 224c501d73cSMariusz Zaborski 225c501d73cSMariusz Zaborski void 226c501d73cSMariusz Zaborski casper_main_loop(int fd) 227c501d73cSMariusz Zaborski { 228c501d73cSMariusz Zaborski fd_set fds; 229c501d73cSMariusz Zaborski struct casper_service *casserv; 230c501d73cSMariusz Zaborski struct service_connection *sconn, *sconntmp; 231c501d73cSMariusz Zaborski int sock, maxfd, ret; 232c501d73cSMariusz Zaborski 233c501d73cSMariusz Zaborski if (zygote_init() < 0) 234c501d73cSMariusz Zaborski exit(1); 235c501d73cSMariusz Zaborski 236c501d73cSMariusz Zaborski /* 237c501d73cSMariusz Zaborski * Register core services. 238c501d73cSMariusz Zaborski */ 239c501d73cSMariusz Zaborski service_register_core(fd); 240c501d73cSMariusz Zaborski 241c501d73cSMariusz Zaborski for (;;) { 242c501d73cSMariusz Zaborski FD_ZERO(&fds); 243c501d73cSMariusz Zaborski FD_SET(fd, &fds); 244c501d73cSMariusz Zaborski maxfd = -1; 245c501d73cSMariusz Zaborski TAILQ_FOREACH(casserv, &casper_services, cs_next) { 246c501d73cSMariusz Zaborski /* We handle only core services. */ 247c501d73cSMariusz Zaborski if (!CSERVICE_IS_CORE(casserv)) 248c501d73cSMariusz Zaborski continue; 249c501d73cSMariusz Zaborski for (sconn = service_connection_first(casserv->cs_service); 250c501d73cSMariusz Zaborski sconn != NULL; 251c501d73cSMariusz Zaborski sconn = service_connection_next(sconn)) { 252c501d73cSMariusz Zaborski sock = service_connection_get_sock(sconn); 253c501d73cSMariusz Zaborski FD_SET(sock, &fds); 254c501d73cSMariusz Zaborski maxfd = sock > maxfd ? sock : maxfd; 255c501d73cSMariusz Zaborski } 256c501d73cSMariusz Zaborski } 257c501d73cSMariusz Zaborski if (maxfd == -1) { 258c501d73cSMariusz Zaborski /* Nothing to do. */ 259c501d73cSMariusz Zaborski exit(0); 260c501d73cSMariusz Zaborski } 261c501d73cSMariusz Zaborski maxfd++; 262c501d73cSMariusz Zaborski 263c501d73cSMariusz Zaborski 264c501d73cSMariusz Zaborski assert(maxfd <= (int)FD_SETSIZE); 265c501d73cSMariusz Zaborski ret = select(maxfd, &fds, NULL, NULL, NULL); 266c501d73cSMariusz Zaborski assert(ret == -1 || ret > 0); /* select() cannot timeout */ 267c501d73cSMariusz Zaborski if (ret == -1) { 268c501d73cSMariusz Zaborski if (errno == EINTR) 269c501d73cSMariusz Zaborski continue; 270c501d73cSMariusz Zaborski exit(1); 271c501d73cSMariusz Zaborski } 272c501d73cSMariusz Zaborski 273c501d73cSMariusz Zaborski TAILQ_FOREACH(casserv, &casper_services, cs_next) { 274c501d73cSMariusz Zaborski /* We handle only core services. */ 275c501d73cSMariusz Zaborski if (!CSERVICE_IS_CORE(casserv)) 276c501d73cSMariusz Zaborski continue; 277c501d73cSMariusz Zaborski for (sconn = service_connection_first(casserv->cs_service); 278c501d73cSMariusz Zaborski sconn != NULL; sconn = sconntmp) { 279c501d73cSMariusz Zaborski /* 280c501d73cSMariusz Zaborski * Prepare for connection to be removed from 281c501d73cSMariusz Zaborski * the list on failure. 282c501d73cSMariusz Zaborski */ 283c501d73cSMariusz Zaborski sconntmp = service_connection_next(sconn); 284c501d73cSMariusz Zaborski sock = service_connection_get_sock(sconn); 285c501d73cSMariusz Zaborski if (FD_ISSET(sock, &fds)) { 286c501d73cSMariusz Zaborski service_message(casserv->cs_service, 287c501d73cSMariusz Zaborski sconn); 288c501d73cSMariusz Zaborski } 289c501d73cSMariusz Zaborski } 290c501d73cSMariusz Zaborski } 291c501d73cSMariusz Zaborski } 292c501d73cSMariusz Zaborski } 293