xref: /freebsd/lib/libcasper/libcasper/service.c (revision f126890a)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2013 The FreeBSD Foundation
5  * Copyright (c) 2015 Mariusz Zaborski <oshogbo@FreeBSD.org>
6  * All rights reserved.
7  *
8  * This software was developed by Pawel Jakub Dawidek under sponsorship from
9  * the FreeBSD Foundation.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/types.h>
34 #include <sys/queue.h>
35 #include <sys/socket.h>
36 #include <sys/nv.h>
37 
38 #include <assert.h>
39 #include <dirent.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <paths.h>
44 #include <stdbool.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <strings.h>
49 #include <unistd.h>
50 
51 #include "libcasper.h"
52 #include "libcasper_impl.h"
53 
54 /*
55  * Currently there is only one service_connection per service.
56  * In the future we may want multiple connections from multiple clients
57  * per one service instance, but it has to be carefully designed.
58  * The problem is that we may restrict/sandbox service instance according
59  * to the limits provided. When new connection comes in with different
60  * limits we won't be able to access requested resources.
61  * Not to mention one process will serve to multiple mutually untrusted
62  * clients and compromise of this service instance by one of its clients
63  * can lead to compromise of the other clients.
64  */
65 
66 /*
67  * Client connections to the given service.
68  */
69 #define	SERVICE_CONNECTION_MAGIC	0x5e91c0ec
70 struct service_connection {
71 	int		 sc_magic;
72 	cap_channel_t	*sc_chan;
73 	nvlist_t	*sc_limits;
74 	TAILQ_ENTRY(service_connection) sc_next;
75 };
76 
77 #define	SERVICE_MAGIC	0x5e91ce
78 struct service {
79 	int			 s_magic;
80 	char			*s_name;
81 	uint64_t		 s_flags;
82 	service_limit_func_t	*s_limit;
83 	service_command_func_t	*s_command;
84 	TAILQ_HEAD(, service_connection) s_connections;
85 };
86 
87 struct service *
88 service_alloc(const char *name, service_limit_func_t *limitfunc,
89     service_command_func_t *commandfunc, uint64_t flags)
90 {
91 	struct service *service;
92 
93 	service = malloc(sizeof(*service));
94 	if (service == NULL)
95 		return (NULL);
96 	service->s_name = strdup(name);
97 	if (service->s_name == NULL) {
98 		free(service);
99 		return (NULL);
100 	}
101 	service->s_limit = limitfunc;
102 	service->s_command = commandfunc;
103 	service->s_flags = flags;
104 	TAILQ_INIT(&service->s_connections);
105 	service->s_magic = SERVICE_MAGIC;
106 
107 	return (service);
108 }
109 
110 void
111 service_free(struct service *service)
112 {
113 	struct service_connection *sconn;
114 
115 	assert(service->s_magic == SERVICE_MAGIC);
116 
117 	service->s_magic = 0;
118 	while ((sconn = service_connection_first(service)) != NULL)
119 		service_connection_remove(service, sconn);
120 	free(service->s_name);
121 	free(service);
122 }
123 
124 struct service_connection *
125 service_connection_add(struct service *service, int sock,
126     const nvlist_t *limits)
127 {
128 	struct service_connection *sconn;
129 	int serrno;
130 
131 	assert(service->s_magic == SERVICE_MAGIC);
132 
133 	sconn = malloc(sizeof(*sconn));
134 	if (sconn == NULL)
135 		return (NULL);
136 	sconn->sc_chan = cap_wrap(sock,
137 	    service_get_channel_flags(service));
138 	if (sconn->sc_chan == NULL) {
139 		serrno = errno;
140 		free(sconn);
141 		errno = serrno;
142 		return (NULL);
143 	}
144 	if (limits == NULL) {
145 		sconn->sc_limits = NULL;
146 	} else {
147 		sconn->sc_limits = nvlist_clone(limits);
148 		if (sconn->sc_limits == NULL) {
149 			serrno = errno;
150 			(void)cap_unwrap(sconn->sc_chan, NULL);
151 			free(sconn);
152 			errno = serrno;
153 			return (NULL);
154 		}
155 	}
156 	sconn->sc_magic = SERVICE_CONNECTION_MAGIC;
157 	TAILQ_INSERT_TAIL(&service->s_connections, sconn, sc_next);
158 	return (sconn);
159 }
160 
161 void
162 service_connection_remove(struct service *service,
163     struct service_connection *sconn)
164 {
165 
166 	assert(service->s_magic == SERVICE_MAGIC);
167 	assert(sconn->sc_magic == SERVICE_CONNECTION_MAGIC);
168 
169 	TAILQ_REMOVE(&service->s_connections, sconn, sc_next);
170 	sconn->sc_magic = 0;
171 	nvlist_destroy(sconn->sc_limits);
172 	cap_close(sconn->sc_chan);
173 	free(sconn);
174 }
175 
176 int
177 service_connection_clone(struct service *service,
178     struct service_connection *sconn)
179 {
180 	struct service_connection *newsconn;
181 	int serrno, sock[2];
182 
183 	if (socketpair(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, sock) < 0)
184 		return (-1);
185 
186 	newsconn = service_connection_add(service, sock[0],
187 	    service_connection_get_limits(sconn));
188 	if (newsconn == NULL) {
189 		serrno = errno;
190 		close(sock[0]);
191 		close(sock[1]);
192 		errno = serrno;
193 		return (-1);
194 	}
195 
196 	return (sock[1]);
197 }
198 
199 struct service_connection *
200 service_connection_first(struct service *service)
201 {
202 	struct service_connection *sconn;
203 
204 	assert(service->s_magic == SERVICE_MAGIC);
205 
206 	sconn = TAILQ_FIRST(&service->s_connections);
207 	assert(sconn == NULL ||
208 	    sconn->sc_magic == SERVICE_CONNECTION_MAGIC);
209 	return (sconn);
210 }
211 
212 struct service_connection *
213 service_connection_next(struct service_connection *sconn)
214 {
215 
216 	assert(sconn->sc_magic == SERVICE_CONNECTION_MAGIC);
217 
218 	sconn = TAILQ_NEXT(sconn, sc_next);
219 	assert(sconn == NULL ||
220 	    sconn->sc_magic == SERVICE_CONNECTION_MAGIC);
221 	return (sconn);
222 }
223 
224 cap_channel_t *
225 service_connection_get_chan(const struct service_connection *sconn)
226 {
227 
228 	assert(sconn->sc_magic == SERVICE_CONNECTION_MAGIC);
229 
230 	return (sconn->sc_chan);
231 }
232 
233 int
234 service_connection_get_sock(const struct service_connection *sconn)
235 {
236 
237 	assert(sconn->sc_magic == SERVICE_CONNECTION_MAGIC);
238 
239 	return (cap_sock(sconn->sc_chan));
240 }
241 
242 const nvlist_t *
243 service_connection_get_limits(const struct service_connection *sconn)
244 {
245 
246 	assert(sconn->sc_magic == SERVICE_CONNECTION_MAGIC);
247 
248 	return (sconn->sc_limits);
249 }
250 
251 void
252 service_connection_set_limits(struct service_connection *sconn,
253     nvlist_t *limits)
254 {
255 
256 	assert(sconn->sc_magic == SERVICE_CONNECTION_MAGIC);
257 
258 	nvlist_destroy(sconn->sc_limits);
259 	sconn->sc_limits = limits;
260 }
261 
262 void
263 service_message(struct service *service, struct service_connection *sconn)
264 {
265 	nvlist_t *nvlin, *nvlout;
266 	const char *cmd;
267 	int error, flags;
268 
269 	flags = 0;
270 	if ((service->s_flags & CASPER_SERVICE_NO_UNIQ_LIMITS) != 0)
271 		flags = NV_FLAG_NO_UNIQUE;
272 
273 	nvlin = cap_recv_nvlist(service_connection_get_chan(sconn));
274 	if (nvlin == NULL) {
275 		service_connection_remove(service, sconn);
276 		return;
277 	}
278 
279 	error = EDOOFUS;
280 	nvlout = nvlist_create(flags);
281 
282 	cmd = nvlist_get_string(nvlin, "cmd");
283 	if (strcmp(cmd, "limit_set") == 0) {
284 		nvlist_t *nvllim;
285 
286 		nvllim = nvlist_take_nvlist(nvlin, "limits");
287 		if (service->s_limit == NULL) {
288 			error = EOPNOTSUPP;
289 		} else {
290 			error = service->s_limit(
291 			    service_connection_get_limits(sconn), nvllim);
292 		}
293 		if (error == 0) {
294 			service_connection_set_limits(sconn, nvllim);
295 			/* Function consumes nvllim. */
296 		} else {
297 			nvlist_destroy(nvllim);
298 		}
299 	} else if (strcmp(cmd, "limit_get") == 0) {
300 		const nvlist_t *nvllim;
301 
302 		nvllim = service_connection_get_limits(sconn);
303 		if (nvllim != NULL)
304 			nvlist_add_nvlist(nvlout, "limits", nvllim);
305 		else
306 			nvlist_add_null(nvlout, "limits");
307 		error = 0;
308 	} else if (strcmp(cmd, "clone") == 0) {
309 		int sock;
310 
311 		sock = service_connection_clone(service, sconn);
312 		if (sock == -1) {
313 			error = errno;
314 		} else {
315 			nvlist_move_descriptor(nvlout, "sock", sock);
316 			error = 0;
317 		}
318 	} else {
319 		error = service->s_command(cmd,
320 		    service_connection_get_limits(sconn), nvlin, nvlout);
321 	}
322 
323 	nvlist_destroy(nvlin);
324 	nvlist_add_number(nvlout, "error", (uint64_t)error);
325 
326 	if (cap_send_nvlist(service_connection_get_chan(sconn), nvlout) == -1)
327 		service_connection_remove(service, sconn);
328 
329 	nvlist_destroy(nvlout);
330 }
331 
332 static int
333 fd_add(fd_set *fdsp, int maxfd, int fd)
334 {
335 
336 	FD_SET(fd, fdsp);
337 	return (fd > maxfd ? fd : maxfd);
338 }
339 
340 const char *
341 service_name(struct service *service)
342 {
343 
344 	assert(service->s_magic == SERVICE_MAGIC);
345 	return (service->s_name);
346 }
347 
348 int
349 service_get_channel_flags(struct service *service)
350 {
351 	int flags;
352 
353 	assert(service->s_magic == SERVICE_MAGIC);
354 	flags = 0;
355 
356 	if ((service->s_flags & CASPER_SERVICE_NO_UNIQ_LIMITS) != 0)
357 		flags |= CASPER_NO_UNIQ;
358 
359 	return (flags);
360 }
361 
362 static void
363 stdnull(void)
364 {
365 	int fd;
366 
367 	fd = open(_PATH_DEVNULL, O_RDWR);
368 	if (fd == -1)
369 		errx(1, "Unable to open %s", _PATH_DEVNULL);
370 
371 	if (setsid() == -1)
372 		errx(1, "Unable to detach from session");
373 
374 	if (dup2(fd, STDIN_FILENO) == -1)
375 		errx(1, "Unable to cover stdin");
376 	if (dup2(fd, STDOUT_FILENO) == -1)
377 		errx(1, "Unable to cover stdout");
378 	if (dup2(fd, STDERR_FILENO) == -1)
379 		errx(1, "Unable to cover stderr");
380 
381 	if (fd > STDERR_FILENO)
382 		close(fd);
383 }
384 
385 static void
386 service_clean(int *sockp, int *procfdp, uint64_t flags)
387 {
388 	int fd, maxfd, minfd;
389 
390 	fd_fix_environment(sockp);
391 	fd_fix_environment(procfdp);
392 
393 	assert(*sockp > STDERR_FILENO);
394 	assert(*procfdp > STDERR_FILENO);
395 	assert(*sockp != *procfdp);
396 
397 	if ((flags & CASPER_SERVICE_STDIO) == 0)
398 		stdnull();
399 
400 	if ((flags & CASPER_SERVICE_FD) == 0) {
401 		if (*procfdp > *sockp) {
402 			maxfd = *procfdp;
403 			minfd = *sockp;
404 		} else {
405 			maxfd = *sockp;
406 			minfd = *procfdp;
407 		}
408 
409 		for (fd = STDERR_FILENO + 1; fd < maxfd; fd++) {
410 			if (fd != minfd)
411 				close(fd);
412 		}
413 		closefrom(maxfd + 1);
414 	}
415 }
416 
417 void
418 service_start(struct service *service, int sock, int procfd)
419 {
420 	struct service_connection *sconn, *sconntmp;
421 	fd_set fds;
422 	int maxfd, nfds;
423 
424 	assert(service != NULL);
425 	assert(service->s_magic == SERVICE_MAGIC);
426 	setproctitle("%s", service->s_name);
427 	service_clean(&sock, &procfd, service->s_flags);
428 
429 	if (service_connection_add(service, sock, NULL) == NULL)
430 		_exit(1);
431 
432 	for (;;) {
433 		FD_ZERO(&fds);
434 		maxfd = -1;
435 		for (sconn = service_connection_first(service); sconn != NULL;
436 		    sconn = service_connection_next(sconn)) {
437 			maxfd = fd_add(&fds, maxfd,
438 			    service_connection_get_sock(sconn));
439 		}
440 
441 		assert(maxfd >= 0);
442 		assert(maxfd + 1 <= (int)FD_SETSIZE);
443 		nfds = select(maxfd + 1, &fds, NULL, NULL, NULL);
444 		if (nfds < 0) {
445 			if (errno != EINTR)
446 				_exit(1);
447 			continue;
448 		} else if (nfds == 0) {
449 			/* Timeout. */
450 			abort();
451 		}
452 
453 		for (sconn = service_connection_first(service); sconn != NULL;
454 		    sconn = sconntmp) {
455 			/*
456 			 * Prepare for connection to be removed from the list
457 			 * on failure.
458 			 */
459 			sconntmp = service_connection_next(sconn);
460 			if (FD_ISSET(service_connection_get_sock(sconn), &fds))
461 				service_message(service, sconn);
462 		}
463 		if (service_connection_first(service) == NULL) {
464 			/*
465 			 * No connections left, exiting.
466 			 */
467 			break;
468 		}
469 	}
470 
471 	_exit(0);
472 }
473