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