1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2018 Mariusz Zaborski <oshogbo@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/types.h> 33 #include <sys/capsicum.h> 34 #include <sys/sysctl.h> 35 #include <sys/cnv.h> 36 #include <sys/dnv.h> 37 #include <sys/nv.h> 38 #include <sys/stat.h> 39 40 #include <assert.h> 41 #include <errno.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <unistd.h> 45 46 #include <libcasper.h> 47 #include <libcasper_service.h> 48 49 #include "cap_fileargs.h" 50 51 #define CACHE_SIZE 128 52 53 #define FILEARGS_MAGIC 0xFA00FA00 54 55 struct fileargs { 56 uint32_t fa_magic; 57 nvlist_t *fa_cache; 58 cap_channel_t *fa_chann; 59 int fa_fdflags; 60 }; 61 62 static int 63 fileargs_get_lstat_cache(fileargs_t *fa, const char *name, struct stat *sb) 64 { 65 const nvlist_t *nvl; 66 size_t size; 67 const void *buf; 68 69 assert(fa != NULL); 70 assert(fa->fa_magic == FILEARGS_MAGIC); 71 assert(name != NULL); 72 73 if (fa->fa_cache == NULL) 74 return (-1); 75 76 nvl = dnvlist_get_nvlist(fa->fa_cache, name, NULL); 77 if (nvl == NULL) 78 return (-1); 79 80 if (!nvlist_exists_binary(nvl, "stat")) { 81 return (-1); 82 } 83 84 buf = nvlist_get_binary(nvl, "stat", &size); 85 assert(size == sizeof(*sb)); 86 memcpy(sb, buf, size); 87 88 return (0); 89 } 90 91 static int 92 fileargs_get_fd_cache(fileargs_t *fa, const char *name) 93 { 94 int fd; 95 const nvlist_t *nvl; 96 nvlist_t *tnvl; 97 98 assert(fa != NULL); 99 assert(fa->fa_magic == FILEARGS_MAGIC); 100 assert(name != NULL); 101 102 if (fa->fa_cache == NULL) 103 return (-1); 104 105 if ((fa->fa_fdflags & O_CREAT) != 0) 106 return (-1); 107 108 nvl = dnvlist_get_nvlist(fa->fa_cache, name, NULL); 109 if (nvl == NULL) 110 return (-1); 111 112 tnvl = nvlist_take_nvlist(fa->fa_cache, name); 113 114 if (!nvlist_exists_descriptor(tnvl, "fd")) { 115 nvlist_destroy(tnvl); 116 return (-1); 117 } 118 119 fd = nvlist_take_descriptor(tnvl, "fd"); 120 nvlist_destroy(tnvl); 121 122 if ((fa->fa_fdflags & O_CLOEXEC) != O_CLOEXEC) { 123 if (fcntl(fd, F_SETFD, fa->fa_fdflags) == -1) { 124 close(fd); 125 return (-1); 126 } 127 } 128 129 return (fd); 130 } 131 132 static void 133 fileargs_set_cache(fileargs_t *fa, nvlist_t *nvl) 134 { 135 136 nvlist_destroy(fa->fa_cache); 137 fa->fa_cache = nvl; 138 } 139 140 static nvlist_t* 141 fileargs_fetch(fileargs_t *fa, const char *name, const char *cmd) 142 { 143 nvlist_t *nvl; 144 int serrno; 145 146 assert(fa != NULL); 147 assert(name != NULL); 148 149 nvl = nvlist_create(NV_FLAG_NO_UNIQUE); 150 nvlist_add_string(nvl, "cmd", cmd); 151 nvlist_add_string(nvl, "name", name); 152 153 nvl = cap_xfer_nvlist(fa->fa_chann, nvl); 154 if (nvl == NULL) 155 return (NULL); 156 157 if (nvlist_get_number(nvl, "error") != 0) { 158 serrno = (int)nvlist_get_number(nvl, "error"); 159 nvlist_destroy(nvl); 160 errno = serrno; 161 return (NULL); 162 } 163 164 return (nvl); 165 } 166 167 static nvlist_t * 168 fileargs_create_limit(int argc, const char * const *argv, int flags, 169 mode_t mode, cap_rights_t *rightsp, int operations) 170 { 171 nvlist_t *limits; 172 int i; 173 174 limits = nvlist_create(NV_FLAG_NO_UNIQUE); 175 if (limits == NULL) 176 return (NULL); 177 178 nvlist_add_number(limits, "flags", flags); 179 nvlist_add_number(limits, "operations", operations); 180 if (rightsp != NULL) { 181 nvlist_add_binary(limits, "cap_rights", rightsp, 182 sizeof(*rightsp)); 183 } 184 if ((flags & O_CREAT) != 0) 185 nvlist_add_number(limits, "mode", (uint64_t)mode); 186 187 for (i = 0; i < argc; i++) { 188 nvlist_add_null(limits, argv[i]); 189 } 190 191 return (limits); 192 } 193 194 static fileargs_t * 195 fileargs_create(cap_channel_t *chan, int fdflags) 196 { 197 fileargs_t *fa; 198 199 fa = malloc(sizeof(*fa)); 200 if (fa != NULL) { 201 fa->fa_cache = NULL; 202 fa->fa_chann = chan; 203 fa->fa_fdflags = fdflags; 204 fa->fa_magic = FILEARGS_MAGIC; 205 } 206 207 return (fa); 208 } 209 210 fileargs_t * 211 fileargs_init(int argc, char *argv[], int flags, mode_t mode, 212 cap_rights_t *rightsp, int operations) 213 { 214 nvlist_t *limits; 215 216 if (argc <= 0 || argv == NULL) { 217 return (fileargs_create(NULL, 0)); 218 } 219 220 limits = fileargs_create_limit(argc, (const char * const *)argv, flags, 221 mode, rightsp, operations); 222 if (limits == NULL) 223 return (NULL); 224 225 return (fileargs_initnv(limits)); 226 } 227 228 fileargs_t * 229 fileargs_cinit(cap_channel_t *cas, int argc, char *argv[], int flags, 230 mode_t mode, cap_rights_t *rightsp, int operations) 231 { 232 nvlist_t *limits; 233 234 if (argc <= 0 || argv == NULL) { 235 return (fileargs_create(NULL, 0)); 236 } 237 238 limits = fileargs_create_limit(argc, (const char * const *)argv, flags, 239 mode, rightsp, operations); 240 if (limits == NULL) 241 return (NULL); 242 243 return (fileargs_cinitnv(cas, limits)); 244 } 245 246 fileargs_t * 247 fileargs_initnv(nvlist_t *limits) 248 { 249 cap_channel_t *cas; 250 fileargs_t *fa; 251 252 if (limits == NULL) { 253 return (fileargs_create(NULL, 0)); 254 } 255 256 cas = cap_init(); 257 if (cas == NULL) { 258 nvlist_destroy(limits); 259 return (NULL); 260 } 261 262 fa = fileargs_cinitnv(cas, limits); 263 cap_close(cas); 264 265 return (fa); 266 } 267 268 fileargs_t * 269 fileargs_cinitnv(cap_channel_t *cas, nvlist_t *limits) 270 { 271 cap_channel_t *chann; 272 fileargs_t *fa; 273 int serrno, ret; 274 int flags, operations; 275 276 assert(cas != NULL); 277 278 if (limits == NULL) { 279 return (fileargs_create(NULL, 0)); 280 } 281 282 chann = NULL; 283 fa = NULL; 284 285 chann = cap_service_open(cas, "system.fileargs"); 286 if (chann == NULL) { 287 nvlist_destroy(limits); 288 return (NULL); 289 } 290 291 flags = nvlist_get_number(limits, "flags"); 292 operations = nvlist_get_number(limits, "operations"); 293 294 /* Limits are consumed no need to free them. */ 295 ret = cap_limit_set(chann, limits); 296 if (ret < 0) 297 goto out; 298 299 fa = fileargs_create(chann, flags); 300 if (fa == NULL) 301 goto out; 302 303 return (fa); 304 out: 305 serrno = errno; 306 if (chann != NULL) 307 cap_close(chann); 308 errno = serrno; 309 return (NULL); 310 } 311 312 int 313 fileargs_open(fileargs_t *fa, const char *name) 314 { 315 int fd; 316 nvlist_t *nvl; 317 char *cmd; 318 319 assert(fa != NULL); 320 assert(fa->fa_magic == FILEARGS_MAGIC); 321 322 if (name == NULL) { 323 errno = EINVAL; 324 return (-1); 325 } 326 327 if (fa->fa_chann == NULL) { 328 errno = ENOTCAPABLE; 329 return (-1); 330 } 331 332 fd = fileargs_get_fd_cache(fa, name); 333 if (fd != -1) 334 return (fd); 335 336 nvl = fileargs_fetch(fa, name, "open"); 337 if (nvl == NULL) 338 return (-1); 339 340 fd = nvlist_take_descriptor(nvl, "fd"); 341 cmd = nvlist_take_string(nvl, "cmd"); 342 if (strcmp(cmd, "cache") == 0) 343 fileargs_set_cache(fa, nvl); 344 else 345 nvlist_destroy(nvl); 346 free(cmd); 347 348 return (fd); 349 } 350 351 FILE * 352 fileargs_fopen(fileargs_t *fa, const char *name, const char *mode) 353 { 354 int fd; 355 356 if ((fd = fileargs_open(fa, name)) < 0) { 357 return (NULL); 358 } 359 360 return (fdopen(fd, mode)); 361 } 362 363 int 364 fileargs_lstat(fileargs_t *fa, const char *name, struct stat *sb) 365 { 366 nvlist_t *nvl; 367 const void *buf; 368 size_t size; 369 char *cmd; 370 371 assert(fa != NULL); 372 assert(fa->fa_magic == FILEARGS_MAGIC); 373 374 if (name == NULL) { 375 errno = EINVAL; 376 return (-1); 377 } 378 379 if (sb == NULL) { 380 errno = EFAULT; 381 return (-1); 382 } 383 384 if (fa->fa_chann == NULL) { 385 errno = ENOTCAPABLE; 386 return (-1); 387 } 388 389 if (fileargs_get_lstat_cache(fa, name, sb) != -1) 390 return (0); 391 392 nvl = fileargs_fetch(fa, name, "lstat"); 393 if (nvl == NULL) 394 return (-1); 395 396 buf = nvlist_get_binary(nvl, "stat", &size); 397 assert(size == sizeof(*sb)); 398 memcpy(sb, buf, size); 399 400 cmd = nvlist_take_string(nvl, "cmd"); 401 if (strcmp(cmd, "cache") == 0) 402 fileargs_set_cache(fa, nvl); 403 else 404 nvlist_destroy(nvl); 405 free(cmd); 406 407 return (0); 408 } 409 410 void 411 fileargs_free(fileargs_t *fa) 412 { 413 414 if (fa == NULL) 415 return; 416 417 assert(fa->fa_magic == FILEARGS_MAGIC); 418 419 nvlist_destroy(fa->fa_cache); 420 if (fa->fa_chann != NULL) { 421 cap_close(fa->fa_chann); 422 } 423 explicit_bzero(&fa->fa_magic, sizeof(fa->fa_magic)); 424 free(fa); 425 } 426 427 cap_channel_t * 428 fileargs_unwrap(fileargs_t *fa, int *flags) 429 { 430 cap_channel_t *chan; 431 432 if (fa == NULL) 433 return (NULL); 434 435 assert(fa->fa_magic == FILEARGS_MAGIC); 436 437 chan = fa->fa_chann; 438 if (flags != NULL) { 439 *flags = fa->fa_fdflags; 440 } 441 442 nvlist_destroy(fa->fa_cache); 443 explicit_bzero(&fa->fa_magic, sizeof(fa->fa_magic)); 444 free(fa); 445 446 return (chan); 447 } 448 449 fileargs_t * 450 fileargs_wrap(cap_channel_t *chan, int fdflags) 451 { 452 453 if (chan == NULL) { 454 return (NULL); 455 } 456 457 return (fileargs_create(chan, fdflags)); 458 } 459 460 /* 461 * Service functions. 462 */ 463 464 static const char *lastname; 465 static void *cacheposition; 466 static bool allcached; 467 static const cap_rights_t *caprightsp; 468 static int capflags; 469 static int allowed_operations; 470 static mode_t capmode; 471 472 static int 473 open_file(const char *name) 474 { 475 int fd, serrno; 476 477 if ((capflags & O_CREAT) == 0) 478 fd = open(name, capflags); 479 else 480 fd = open(name, capflags, capmode); 481 if (fd < 0) 482 return (-1); 483 484 if (caprightsp != NULL) { 485 if (cap_rights_limit(fd, caprightsp) < 0 && errno != ENOSYS) { 486 serrno = errno; 487 close(fd); 488 errno = serrno; 489 return (-1); 490 } 491 } 492 493 return (fd); 494 } 495 496 static void 497 fileargs_add_cache(nvlist_t *nvlout, const nvlist_t *limits, 498 const char *curent_name) 499 { 500 int type, i, fd; 501 void *cookie; 502 nvlist_t *new; 503 const char *fname; 504 struct stat sb; 505 506 if ((capflags & O_CREAT) != 0) { 507 allcached = true; 508 return; 509 } 510 511 cookie = cacheposition; 512 for (i = 0; i < CACHE_SIZE + 1; i++) { 513 fname = nvlist_next(limits, &type, &cookie); 514 if (fname == NULL) { 515 cacheposition = NULL; 516 lastname = NULL; 517 allcached = true; 518 return; 519 } 520 /* We doing that to catch next element name. */ 521 if (i == CACHE_SIZE) { 522 break; 523 } 524 525 if (type != NV_TYPE_NULL || 526 (curent_name != NULL && strcmp(fname, curent_name) == 0)) { 527 curent_name = NULL; 528 i--; 529 continue; 530 } 531 532 new = nvlist_create(NV_FLAG_NO_UNIQUE); 533 if ((allowed_operations & FA_OPEN) != 0) { 534 fd = open_file(fname); 535 if (fd < 0) { 536 i--; 537 nvlist_destroy(new); 538 continue; 539 } 540 nvlist_move_descriptor(new, "fd", fd); 541 } 542 if ((allowed_operations & FA_LSTAT) != 0) { 543 if (lstat(fname, &sb) < 0) { 544 i--; 545 nvlist_destroy(new); 546 continue; 547 } 548 nvlist_add_binary(new, "stat", &sb, sizeof(sb)); 549 } 550 551 nvlist_add_nvlist(nvlout, fname, new); 552 } 553 cacheposition = cookie; 554 lastname = fname; 555 } 556 557 static bool 558 fileargs_allowed(const nvlist_t *limits, const nvlist_t *request, int operation) 559 { 560 const char *name; 561 562 if ((allowed_operations & operation) == 0) 563 return (false); 564 565 name = dnvlist_get_string(request, "name", NULL); 566 if (name == NULL) 567 return (false); 568 569 /* Fast path. */ 570 if (lastname != NULL && strcmp(name, lastname) == 0) 571 return (true); 572 573 if (!nvlist_exists_null(limits, name)) 574 return (false); 575 576 return (true); 577 } 578 579 static int 580 fileargs_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits) 581 { 582 583 if (oldlimits != NULL) 584 return (ENOTCAPABLE); 585 586 capflags = (int)dnvlist_get_number(newlimits, "flags", 0); 587 allowed_operations = (int)dnvlist_get_number(newlimits, "operations", 0); 588 if ((capflags & O_CREAT) != 0) 589 capmode = (mode_t)nvlist_get_number(newlimits, "mode"); 590 else 591 capmode = 0; 592 593 caprightsp = dnvlist_get_binary(newlimits, "cap_rights", NULL, NULL, 0); 594 595 return (0); 596 } 597 598 static int 599 fileargs_command_lstat(const nvlist_t *limits, nvlist_t *nvlin, 600 nvlist_t *nvlout) 601 { 602 int error; 603 const char *name; 604 struct stat sb; 605 606 if (limits == NULL) 607 return (ENOTCAPABLE); 608 609 if (!fileargs_allowed(limits, nvlin, FA_LSTAT)) 610 return (ENOTCAPABLE); 611 612 name = nvlist_get_string(nvlin, "name"); 613 614 error = lstat(name, &sb); 615 if (error < 0) 616 return (errno); 617 618 if (!allcached && (lastname == NULL || 619 strcmp(name, lastname) == 0)) { 620 nvlist_add_string(nvlout, "cmd", "cache"); 621 fileargs_add_cache(nvlout, limits, name); 622 } else { 623 nvlist_add_string(nvlout, "cmd", "lstat"); 624 } 625 nvlist_add_binary(nvlout, "stat", &sb, sizeof(sb)); 626 return (0); 627 } 628 629 static int 630 fileargs_command_open(const nvlist_t *limits, nvlist_t *nvlin, 631 nvlist_t *nvlout) 632 { 633 int fd; 634 const char *name; 635 636 if (limits == NULL) 637 return (ENOTCAPABLE); 638 639 if (!fileargs_allowed(limits, nvlin, FA_OPEN)) 640 return (ENOTCAPABLE); 641 642 name = nvlist_get_string(nvlin, "name"); 643 644 fd = open_file(name); 645 if (fd < 0) 646 return (errno); 647 648 if (!allcached && (lastname == NULL || 649 strcmp(name, lastname) == 0)) { 650 nvlist_add_string(nvlout, "cmd", "cache"); 651 fileargs_add_cache(nvlout, limits, name); 652 } else { 653 nvlist_add_string(nvlout, "cmd", "open"); 654 } 655 nvlist_move_descriptor(nvlout, "fd", fd); 656 return (0); 657 } 658 659 static int 660 fileargs_command(const char *cmd, const nvlist_t *limits, 661 nvlist_t *nvlin, nvlist_t *nvlout) 662 { 663 664 if (strcmp(cmd, "open") == 0) 665 return (fileargs_command_open(limits, nvlin, nvlout)); 666 667 if (strcmp(cmd, "lstat") == 0) 668 return (fileargs_command_lstat(limits, nvlin, nvlout)); 669 670 return (EINVAL); 671 } 672 673 CREATE_SERVICE("system.fileargs", fileargs_limit, fileargs_command, 674 CASPER_SERVICE_FD | CASPER_SERVICE_STDIO | CASPER_SERVICE_NO_UNIQ_LIMITS); 675