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 /* 428 * Service functions. 429 */ 430 431 static const char *lastname; 432 static void *cacheposition; 433 static bool allcached; 434 static const cap_rights_t *caprightsp; 435 static int capflags; 436 static int allowed_operations; 437 static mode_t capmode; 438 439 static int 440 open_file(const char *name) 441 { 442 int fd, serrno; 443 444 if ((capflags & O_CREAT) == 0) 445 fd = open(name, capflags); 446 else 447 fd = open(name, capflags, capmode); 448 if (fd < 0) 449 return (-1); 450 451 if (caprightsp != NULL) { 452 if (cap_rights_limit(fd, caprightsp) < 0 && errno != ENOSYS) { 453 serrno = errno; 454 close(fd); 455 errno = serrno; 456 return (-1); 457 } 458 } 459 460 return (fd); 461 } 462 463 static void 464 fileargs_add_cache(nvlist_t *nvlout, const nvlist_t *limits, 465 const char *curent_name) 466 { 467 int type, i, fd; 468 void *cookie; 469 nvlist_t *new; 470 const char *fname; 471 struct stat sb; 472 473 if ((capflags & O_CREAT) != 0) { 474 allcached = true; 475 return; 476 } 477 478 cookie = cacheposition; 479 for (i = 0; i < CACHE_SIZE + 1; i++) { 480 fname = nvlist_next(limits, &type, &cookie); 481 if (fname == NULL) { 482 cacheposition = NULL; 483 lastname = NULL; 484 allcached = true; 485 return; 486 } 487 /* We doing that to catch next element name. */ 488 if (i == CACHE_SIZE) { 489 break; 490 } 491 492 if (type != NV_TYPE_NULL || 493 (curent_name != NULL && strcmp(fname, curent_name) == 0)) { 494 curent_name = NULL; 495 i--; 496 continue; 497 } 498 499 new = nvlist_create(NV_FLAG_NO_UNIQUE); 500 if ((allowed_operations & FA_OPEN) != 0) { 501 fd = open_file(fname); 502 if (fd < 0) { 503 i--; 504 nvlist_destroy(new); 505 continue; 506 } 507 nvlist_move_descriptor(new, "fd", fd); 508 } 509 if ((allowed_operations & FA_LSTAT) != 0) { 510 if (lstat(fname, &sb) < 0) { 511 i--; 512 nvlist_destroy(new); 513 continue; 514 } 515 nvlist_add_binary(new, "stat", &sb, sizeof(sb)); 516 } 517 518 nvlist_add_nvlist(nvlout, fname, new); 519 } 520 cacheposition = cookie; 521 lastname = fname; 522 } 523 524 static bool 525 fileargs_allowed(const nvlist_t *limits, const nvlist_t *request, int operation) 526 { 527 const char *name; 528 529 if ((allowed_operations & operation) == 0) 530 return (false); 531 532 name = dnvlist_get_string(request, "name", NULL); 533 if (name == NULL) 534 return (false); 535 536 /* Fast path. */ 537 if (lastname != NULL && strcmp(name, lastname) == 0) 538 return (true); 539 540 if (!nvlist_exists_null(limits, name)) 541 return (false); 542 543 return (true); 544 } 545 546 static int 547 fileargs_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits) 548 { 549 550 if (oldlimits != NULL) 551 return (ENOTCAPABLE); 552 553 capflags = (int)dnvlist_get_number(newlimits, "flags", 0); 554 allowed_operations = (int)dnvlist_get_number(newlimits, "operations", 0); 555 if ((capflags & O_CREAT) != 0) 556 capmode = (mode_t)nvlist_get_number(newlimits, "mode"); 557 else 558 capmode = 0; 559 560 caprightsp = dnvlist_get_binary(newlimits, "cap_rights", NULL, NULL, 0); 561 562 return (0); 563 } 564 565 static int 566 fileargs_command_lstat(const nvlist_t *limits, nvlist_t *nvlin, 567 nvlist_t *nvlout) 568 { 569 int error; 570 const char *name; 571 struct stat sb; 572 573 if (limits == NULL) 574 return (ENOTCAPABLE); 575 576 if (!fileargs_allowed(limits, nvlin, FA_LSTAT)) 577 return (ENOTCAPABLE); 578 579 name = nvlist_get_string(nvlin, "name"); 580 581 error = lstat(name, &sb); 582 if (error < 0) 583 return (errno); 584 585 if (!allcached && (lastname == NULL || 586 strcmp(name, lastname) == 0)) { 587 nvlist_add_string(nvlout, "cmd", "cache"); 588 fileargs_add_cache(nvlout, limits, name); 589 } else { 590 nvlist_add_string(nvlout, "cmd", "lstat"); 591 } 592 nvlist_add_binary(nvlout, "stat", &sb, sizeof(sb)); 593 return (0); 594 } 595 596 static int 597 fileargs_command_open(const nvlist_t *limits, nvlist_t *nvlin, 598 nvlist_t *nvlout) 599 { 600 int fd; 601 const char *name; 602 603 if (limits == NULL) 604 return (ENOTCAPABLE); 605 606 if (!fileargs_allowed(limits, nvlin, FA_OPEN)) 607 return (ENOTCAPABLE); 608 609 name = nvlist_get_string(nvlin, "name"); 610 611 fd = open_file(name); 612 if (fd < 0) 613 return (errno); 614 615 if (!allcached && (lastname == NULL || 616 strcmp(name, lastname) == 0)) { 617 nvlist_add_string(nvlout, "cmd", "cache"); 618 fileargs_add_cache(nvlout, limits, name); 619 } else { 620 nvlist_add_string(nvlout, "cmd", "open"); 621 } 622 nvlist_move_descriptor(nvlout, "fd", fd); 623 return (0); 624 } 625 626 static int 627 fileargs_command(const char *cmd, const nvlist_t *limits, 628 nvlist_t *nvlin, nvlist_t *nvlout) 629 { 630 631 if (strcmp(cmd, "open") == 0) 632 return (fileargs_command_open(limits, nvlin, nvlout)); 633 634 if (strcmp(cmd, "lstat") == 0) 635 return (fileargs_command_lstat(limits, nvlin, nvlout)); 636 637 return (EINVAL); 638 } 639 640 CREATE_SERVICE("system.fileargs", fileargs_limit, fileargs_command, 641 CASPER_SERVICE_FD | CASPER_SERVICE_STDIO | CASPER_SERVICE_NO_UNIQ_LIMITS); 642