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