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