1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 __FBSDID("$FreeBSD$"); 40 41 #include <sys/types.h> 42 #include <sys/queue.h> 43 #include <sys/socket.h> 44 #include <sys/nv.h> 45 46 #include <assert.h> 47 #include <errno.h> 48 #include <stdbool.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <unistd.h> 53 54 #include "libcasper_impl.h" 55 #include "zygote.h" 56 57 struct casper_service { 58 struct service *cs_service; 59 TAILQ_ENTRY(casper_service) cs_next; 60 }; 61 62 static TAILQ_HEAD(, casper_service) casper_services = 63 TAILQ_HEAD_INITIALIZER(casper_services); 64 65 #define CORE_CASPER_NAME "core.casper" 66 #define CSERVICE_IS_CORE(service) \ 67 (strcmp(service_name(service->cs_service), CORE_CASPER_NAME) == 0) 68 69 static struct casper_service * 70 service_find(const char *name) 71 { 72 struct casper_service *casserv; 73 74 TAILQ_FOREACH(casserv, &casper_services, cs_next) { 75 if (strcmp(service_name(casserv->cs_service), name) == 0) 76 break; 77 } 78 return (casserv); 79 } 80 81 struct casper_service * 82 service_register(const char *name, service_limit_func_t *limitfunc, 83 service_command_func_t *commandfunc, uint64_t flags) 84 { 85 struct casper_service *casserv; 86 87 if (commandfunc == NULL) 88 return (NULL); 89 if (name == NULL || name[0] == '\0') 90 return (NULL); 91 if (service_find(name) != NULL) 92 return (NULL); 93 94 casserv = malloc(sizeof(*casserv)); 95 if (casserv == NULL) 96 return (NULL); 97 98 casserv->cs_service = service_alloc(name, limitfunc, commandfunc, 99 flags); 100 if (casserv->cs_service == NULL) { 101 free(casserv); 102 return (NULL); 103 } 104 TAILQ_INSERT_TAIL(&casper_services, casserv, cs_next); 105 106 return (casserv); 107 } 108 109 static bool 110 casper_allowed_service(const nvlist_t *limits, const char *service) 111 { 112 113 if (limits == NULL) 114 return (true); 115 116 if (nvlist_exists_null(limits, service)) 117 return (true); 118 119 return (false); 120 } 121 122 static int 123 casper_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits) 124 { 125 const char *name; 126 int type; 127 void *cookie; 128 129 cookie = NULL; 130 while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) { 131 if (type != NV_TYPE_NULL) 132 return (EINVAL); 133 if (!casper_allowed_service(oldlimits, name)) 134 return (ENOTCAPABLE); 135 } 136 137 return (0); 138 } 139 140 void 141 service_execute(int chanfd) 142 { 143 struct casper_service *casserv; 144 struct service *service; 145 const char *servname; 146 nvlist_t *nvl; 147 int procfd; 148 149 nvl = nvlist_recv(chanfd, 0); 150 if (nvl == NULL) 151 _exit(1); 152 if (!nvlist_exists_string(nvl, "service")) 153 _exit(1); 154 servname = nvlist_get_string(nvl, "service"); 155 casserv = service_find(servname); 156 if (casserv == NULL) 157 _exit(1); 158 service = casserv->cs_service; 159 procfd = nvlist_take_descriptor(nvl, "procfd"); 160 nvlist_destroy(nvl); 161 162 service_start(service, chanfd, procfd); 163 /* Not reached. */ 164 _exit(1); 165 } 166 167 static int 168 casper_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin, 169 nvlist_t *nvlout) 170 { 171 struct casper_service *casserv; 172 const char *servname; 173 nvlist_t *nvl; 174 int chanfd, procfd, error; 175 176 if (strcmp(cmd, "open") != 0) 177 return (EINVAL); 178 if (!nvlist_exists_string(nvlin, "service")) 179 return (EINVAL); 180 181 servname = nvlist_get_string(nvlin, "service"); 182 casserv = service_find(servname); 183 if (casserv == NULL) 184 return (ENOENT); 185 186 if (!casper_allowed_service(limits, servname)) 187 return (ENOTCAPABLE); 188 189 if (zygote_clone_service_execute(&chanfd, &procfd) == -1) 190 return (errno); 191 192 nvl = nvlist_create(0); 193 nvlist_add_string(nvl, "service", servname); 194 nvlist_move_descriptor(nvl, "procfd", procfd); 195 if (nvlist_send(chanfd, nvl) == -1) { 196 error = errno; 197 nvlist_destroy(nvl); 198 close(chanfd); 199 return (error); 200 } 201 nvlist_destroy(nvl); 202 203 nvlist_move_descriptor(nvlout, "chanfd", chanfd); 204 nvlist_add_number(nvlout, "chanflags", 205 service_get_channel_flags(casserv->cs_service)); 206 207 return (0); 208 } 209 210 static void 211 service_register_core(int fd) 212 { 213 struct casper_service *casserv; 214 struct service_connection *sconn; 215 216 casserv = service_register(CORE_CASPER_NAME, casper_limit, 217 casper_command, 0); 218 sconn = service_connection_add(casserv->cs_service, fd, NULL); 219 if (sconn == NULL) { 220 close(fd); 221 abort(); 222 } 223 } 224 225 void 226 casper_main_loop(int fd) 227 { 228 fd_set fds; 229 struct casper_service *casserv; 230 struct service_connection *sconn, *sconntmp; 231 int sock, maxfd, ret; 232 233 if (zygote_init() < 0) 234 _exit(1); 235 236 /* 237 * Register core services. 238 */ 239 service_register_core(fd); 240 241 for (;;) { 242 FD_ZERO(&fds); 243 FD_SET(fd, &fds); 244 maxfd = -1; 245 TAILQ_FOREACH(casserv, &casper_services, cs_next) { 246 /* We handle only core services. */ 247 if (!CSERVICE_IS_CORE(casserv)) 248 continue; 249 for (sconn = service_connection_first(casserv->cs_service); 250 sconn != NULL; 251 sconn = service_connection_next(sconn)) { 252 sock = service_connection_get_sock(sconn); 253 FD_SET(sock, &fds); 254 maxfd = sock > maxfd ? sock : maxfd; 255 } 256 } 257 if (maxfd == -1) { 258 /* Nothing to do. */ 259 _exit(0); 260 } 261 maxfd++; 262 263 264 assert(maxfd <= (int)FD_SETSIZE); 265 ret = select(maxfd, &fds, NULL, NULL, NULL); 266 assert(ret == -1 || ret > 0); /* select() cannot timeout */ 267 if (ret == -1) { 268 if (errno == EINTR) 269 continue; 270 _exit(1); 271 } 272 273 TAILQ_FOREACH(casserv, &casper_services, cs_next) { 274 /* We handle only core services. */ 275 if (!CSERVICE_IS_CORE(casserv)) 276 continue; 277 for (sconn = service_connection_first(casserv->cs_service); 278 sconn != NULL; sconn = sconntmp) { 279 /* 280 * Prepare for connection to be removed from 281 * the list on failure. 282 */ 283 sconntmp = service_connection_next(sconn); 284 sock = service_connection_get_sock(sconn); 285 if (FD_ISSET(sock, &fds)) { 286 service_message(casserv->cs_service, 287 sconn); 288 } 289 } 290 } 291 } 292 } 293