service.c revision 370004
1/*- 2 * Copyright (c) 2013 The FreeBSD Foundation 3 * Copyright (c) 2015 Mariusz Zaborski <oshogbo@FreeBSD.org> 4 * All rights reserved. 5 * 6 * This software was developed by Pawel Jakub Dawidek under sponsorship from 7 * the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31#include <sys/cdefs.h> 32__FBSDID("$FreeBSD: stable/11/lib/libcasper/libcasper/service.c 370004 2021-06-16 20:30:46Z git2svn $"); 33 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 mutiple 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 71struct 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 79struct 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 88struct service * 89service_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 111void 112service_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 125struct service_connection * 126service_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 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); 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 161void 162service_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 176int 177service_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 199struct service_connection * 200service_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 212struct service_connection * 213service_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 224cap_channel_t * 225service_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 233int 234service_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 242const nvlist_t * 243service_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 251void 252service_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 262void 263service_message(struct service *service, struct service_connection *sconn) 264{ 265 nvlist_t *nvlin, *nvlout; 266 const char *cmd; 267 int error; 268 269 nvlin = cap_recv_nvlist(service_connection_get_chan(sconn), 0); 270 if (nvlin == NULL) { 271 service_connection_remove(service, sconn); 272 return; 273 } 274 275 error = EDOOFUS; 276 nvlout = nvlist_create(0); 277 278 cmd = nvlist_get_string(nvlin, "cmd"); 279 if (strcmp(cmd, "limit_set") == 0) { 280 nvlist_t *nvllim; 281 282 nvllim = nvlist_take_nvlist(nvlin, "limits"); 283 if (service->s_limit == NULL) { 284 error = EOPNOTSUPP; 285 } else { 286 error = service->s_limit( 287 service_connection_get_limits(sconn), nvllim); 288 } 289 if (error == 0) { 290 service_connection_set_limits(sconn, nvllim); 291 /* Function consumes nvllim. */ 292 } else { 293 nvlist_destroy(nvllim); 294 } 295 } else if (strcmp(cmd, "limit_get") == 0) { 296 const nvlist_t *nvllim; 297 298 nvllim = service_connection_get_limits(sconn); 299 if (nvllim != NULL) 300 nvlist_add_nvlist(nvlout, "limits", nvllim); 301 else 302 nvlist_add_null(nvlout, "limits"); 303 error = 0; 304 } else if (strcmp(cmd, "clone") == 0) { 305 int sock; 306 307 sock = service_connection_clone(service, sconn); 308 if (sock == -1) { 309 error = errno; 310 } else { 311 nvlist_move_descriptor(nvlout, "sock", sock); 312 error = 0; 313 } 314 } else { 315 error = service->s_command(cmd, 316 service_connection_get_limits(sconn), nvlin, nvlout); 317 } 318 319 nvlist_destroy(nvlin); 320 nvlist_add_number(nvlout, "error", (uint64_t)error); 321 322 if (cap_send_nvlist(service_connection_get_chan(sconn), nvlout) == -1) 323 service_connection_remove(service, sconn); 324 325 nvlist_destroy(nvlout); 326} 327 328static int 329fd_add(fd_set *fdsp, int maxfd, int fd) 330{ 331 332 FD_SET(fd, fdsp); 333 return (fd > maxfd ? fd : maxfd); 334} 335 336const char * 337service_name(struct service *service) 338{ 339 340 assert(service->s_magic == SERVICE_MAGIC); 341 return (service->s_name); 342} 343 344static void 345stdnull(void) 346{ 347 int fd; 348 349 fd = open(_PATH_DEVNULL, O_RDWR); 350 if (fd == -1) 351 errx(1, "Unable to open %s", _PATH_DEVNULL); 352 353 if (setsid() == -1) 354 errx(1, "Unable to detach from session"); 355 356 if (dup2(fd, STDIN_FILENO) == -1) 357 errx(1, "Unable to cover stdin"); 358 if (dup2(fd, STDOUT_FILENO) == -1) 359 errx(1, "Unable to cover stdout"); 360 if (dup2(fd, STDERR_FILENO) == -1) 361 errx(1, "Unable to cover stderr"); 362 363 if (fd > STDERR_FILENO) 364 close(fd); 365} 366 367static void 368service_clean(int *sockp, int *procfdp, uint64_t flags) 369{ 370 int fd, maxfd, minfd; 371 372 fd_fix_environment(sockp); 373 fd_fix_environment(procfdp); 374 375 assert(*sockp > STDERR_FILENO); 376 assert(*procfdp > STDERR_FILENO); 377 assert(*sockp != *procfdp); 378 379 if ((flags & CASPER_SERVICE_STDIO) == 0) 380 stdnull(); 381 382 if ((flags & CASPER_SERVICE_FD) == 0) { 383 if (*procfdp > *sockp) { 384 maxfd = *procfdp; 385 minfd = *sockp; 386 } else { 387 maxfd = *sockp; 388 minfd = *procfdp; 389 } 390 391 for (fd = STDERR_FILENO + 1; fd < maxfd; fd++) { 392 if (fd != minfd) 393 close(fd); 394 } 395 closefrom(maxfd + 1); 396 } 397} 398 399void 400service_start(struct service *service, int sock, int procfd) 401{ 402 struct service_connection *sconn, *sconntmp; 403 fd_set fds; 404 int maxfd, nfds; 405 406 assert(service != NULL); 407 assert(service->s_magic == SERVICE_MAGIC); 408 setproctitle("%s", service->s_name); 409 service_clean(&sock, &procfd, service->s_flags); 410 411 if (service_connection_add(service, sock, NULL) == NULL) 412 exit(1); 413 414 for (;;) { 415 FD_ZERO(&fds); 416 maxfd = -1; 417 for (sconn = service_connection_first(service); sconn != NULL; 418 sconn = service_connection_next(sconn)) { 419 maxfd = fd_add(&fds, maxfd, 420 service_connection_get_sock(sconn)); 421 } 422 423 assert(maxfd >= 0); 424 assert(maxfd + 1 <= (int)FD_SETSIZE); 425 nfds = select(maxfd + 1, &fds, NULL, NULL, NULL); 426 if (nfds < 0) { 427 if (errno != EINTR) 428 exit(1); 429 continue; 430 } else if (nfds == 0) { 431 /* Timeout. */ 432 abort(); 433 } 434 435 for (sconn = service_connection_first(service); sconn != NULL; 436 sconn = sconntmp) { 437 /* 438 * Prepare for connection to be removed from the list 439 * on failure. 440 */ 441 sconntmp = service_connection_next(sconn); 442 if (FD_ISSET(service_connection_get_sock(sconn), &fds)) 443 service_message(service, sconn); 444 } 445 if (service_connection_first(service) == NULL) { 446 /* 447 * No connections left, exiting. 448 */ 449 break; 450 } 451 } 452 453 exit(0); 454} 455