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 *
service_find(const char * name)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 *
service_register(const char * name,service_limit_func_t * limitfunc,service_command_func_t * commandfunc,uint64_t flags)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
casper_allowed_service(const nvlist_t * limits,const char * service)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
casper_limit(const nvlist_t * oldlimits,const nvlist_t * newlimits)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
service_execute(int chanfd)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
casper_command(const char * cmd,const nvlist_t * limits,nvlist_t * nvlin,nvlist_t * nvlout)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
service_register_core(int fd)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
casper_main_loop(int fd)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