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