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