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