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