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