1 /*- 2 * Copyright (c) 2009 James Gritton. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/types.h> 32 #include <sys/jail.h> 33 #include <sys/socket.h> 34 #include <sys/sysctl.h> 35 36 #include <arpa/inet.h> 37 #include <netinet/in.h> 38 39 #include <errno.h> 40 #include <inttypes.h> 41 #include <stdio.h> 42 #include <stdarg.h> 43 #include <stdlib.h> 44 #include <string.h> 45 46 #include "jail.h" 47 48 #define SJPARAM "security.jail.param" 49 50 #define JPS_IN_ADDR 1 51 #define JPS_IN6_ADDR 2 52 53 #define ARRAY_SANITY 5 54 #define ARRAY_SLOP 5 55 56 57 static int jailparam_import_enum(const char **values, int nvalues, 58 const char *valstr, size_t valsize, int *value); 59 static int jailparam_type(struct jailparam *jp); 60 static char *noname(const char *name); 61 static char *nononame(const char *name); 62 63 char jail_errmsg[JAIL_ERRMSGLEN]; 64 65 static const char *bool_values[] = { "false", "true" }; 66 static const char *jailsys_values[] = { "disable", "new", "inherit" }; 67 68 69 /* 70 * Import a null-terminated parameter list and set a jail with the flags 71 * and parameters. 72 */ 73 int 74 jail_setv(int flags, ...) 75 { 76 va_list ap, tap; 77 struct jailparam *jp; 78 const char *name, *value; 79 int njp, jid; 80 81 /* Create the parameter list and import the parameters. */ 82 va_start(ap, flags); 83 va_copy(tap, ap); 84 for (njp = 0; va_arg(tap, char *) != NULL; njp++) 85 (void)va_arg(tap, char *); 86 va_end(tap); 87 jp = alloca(njp * sizeof(struct jailparam)); 88 for (njp = 0; (name = va_arg(ap, char *)) != NULL;) { 89 value = va_arg(ap, char *); 90 if (jailparam_init(jp + njp, name) < 0) 91 goto error; 92 if (jailparam_import(jp + njp++, value) < 0) 93 goto error; 94 } 95 va_end(ap); 96 jid = jailparam_set(jp, njp, flags); 97 jailparam_free(jp, njp); 98 return (jid); 99 100 error: 101 jailparam_free(jp, njp); 102 va_end(ap); 103 return (-1); 104 } 105 106 /* 107 * Read a null-terminated parameter list, get the referenced jail, and export 108 * the parameters to the list. 109 */ 110 int 111 jail_getv(int flags, ...) 112 { 113 va_list ap, tap; 114 struct jailparam *jp, *jp_lastjid, *jp_jid, *jp_name, *jp_key; 115 char *valarg, *value; 116 const char *name, *key_value, *lastjid_value, *jid_value, *name_value; 117 int njp, i, jid; 118 119 /* Create the parameter list and find the key. */ 120 va_start(ap, flags); 121 va_copy(tap, ap); 122 for (njp = 0; va_arg(tap, char *) != NULL; njp++) 123 (void)va_arg(tap, char *); 124 va_end(tap); 125 126 jp = alloca(njp * sizeof(struct jailparam)); 127 va_copy(tap, ap); 128 jp_lastjid = jp_jid = jp_name = NULL; 129 lastjid_value = jid_value = name_value = NULL; 130 for (njp = 0; (name = va_arg(tap, char *)) != NULL; njp++) { 131 value = va_arg(tap, char *); 132 if (jailparam_init(jp + njp, name) < 0) { 133 va_end(tap); 134 goto error; 135 } 136 if (!strcmp(jp[njp].jp_name, "lastjid")) { 137 jp_lastjid = jp + njp; 138 lastjid_value = value; 139 } else if (!strcmp(jp[njp].jp_name, "jid")) { 140 jp_jid = jp + njp; 141 jid_value = value; 142 } if (!strcmp(jp[njp].jp_name, "name")) { 143 jp_name = jp + njp; 144 name_value = value; 145 } 146 } 147 va_end(tap); 148 /* Import the key parameter. */ 149 if (jp_lastjid != NULL) { 150 jp_key = jp_lastjid; 151 key_value = lastjid_value; 152 } else if (jp_jid != NULL && strtol(jid_value, NULL, 10) != 0) { 153 jp_key = jp_jid; 154 key_value = jid_value; 155 } else if (jp_name != NULL) { 156 jp_key = jp_name; 157 key_value = name_value; 158 } else { 159 strlcpy(jail_errmsg, "no jail specified", JAIL_ERRMSGLEN); 160 errno = ENOENT; 161 goto error; 162 } 163 if (jailparam_import(jp_key, key_value) < 0) 164 goto error; 165 /* Get the jail and export the parameters. */ 166 jid = jailparam_get(jp, njp, flags); 167 if (jid < 0) 168 goto error; 169 for (i = 0; i < njp; i++) { 170 (void)va_arg(ap, char *); 171 valarg = va_arg(ap, char *); 172 if (jp + i != jp_key) { 173 /* It's up to the caller to ensure there's room. */ 174 if ((jp[i].jp_ctltype & CTLTYPE) == CTLTYPE_STRING) 175 strcpy(valarg, jp[i].jp_value); 176 else { 177 value = jailparam_export(jp + i); 178 if (value == NULL) 179 goto error; 180 strcpy(valarg, value); 181 free(value); 182 } 183 } 184 } 185 jailparam_free(jp, njp); 186 va_end(ap); 187 return (jid); 188 189 error: 190 jailparam_free(jp, njp); 191 va_end(ap); 192 return (-1); 193 } 194 195 /* 196 * Return a list of all known parameters. 197 */ 198 int 199 jailparam_all(struct jailparam **jpp) 200 { 201 struct jailparam *jp, *tjp; 202 size_t mlen1, mlen2, buflen; 203 int njp, nlist; 204 int mib1[CTL_MAXNAME], mib2[CTL_MAXNAME - 2]; 205 char buf[MAXPATHLEN]; 206 207 njp = 0; 208 nlist = 32; 209 jp = malloc(nlist * sizeof(*jp)); 210 if (jp == NULL) { 211 strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); 212 return (-1); 213 } 214 mib1[0] = 0; 215 mib1[1] = 2; 216 mlen1 = CTL_MAXNAME - 2; 217 if (sysctlnametomib(SJPARAM, mib1 + 2, &mlen1) < 0) { 218 snprintf(jail_errmsg, JAIL_ERRMSGLEN, 219 "sysctlnametomib(" SJPARAM "): %s", strerror(errno)); 220 goto error; 221 } 222 for (;; njp++) { 223 /* Get the next parameter. */ 224 mlen2 = sizeof(mib2); 225 if (sysctl(mib1, mlen1 + 2, mib2, &mlen2, NULL, 0) < 0) { 226 snprintf(jail_errmsg, JAIL_ERRMSGLEN, 227 "sysctl(0.2): %s", strerror(errno)); 228 goto error; 229 } 230 if (mib2[0] != mib1[2] || mib2[1] != mib1[3] || 231 mib2[2] != mib1[4]) 232 break; 233 /* Convert it to an ascii name. */ 234 memcpy(mib1 + 2, mib2, mlen2); 235 mlen1 = mlen2 / sizeof(int); 236 mib1[1] = 1; 237 buflen = sizeof(buf); 238 if (sysctl(mib1, mlen1 + 2, buf, &buflen, NULL, 0) < 0) { 239 snprintf(jail_errmsg, JAIL_ERRMSGLEN, 240 "sysctl(0.1): %s", strerror(errno)); 241 goto error; 242 } 243 if (buf[buflen - 2] == '.') 244 buf[buflen - 2] = '\0'; 245 /* Add the parameter to the list */ 246 if (njp >= nlist) { 247 nlist *= 2; 248 tjp = realloc(jp, nlist * sizeof(*jp)); 249 if (tjp == NULL) 250 goto error; 251 jp = tjp; 252 } 253 if (jailparam_init(jp + njp, buf + sizeof(SJPARAM)) < 0) 254 goto error; 255 mib1[1] = 2; 256 } 257 jp = realloc(jp, njp * sizeof(*jp)); 258 *jpp = jp; 259 return (njp); 260 261 error: 262 jailparam_free(jp, njp); 263 free(jp); 264 return (-1); 265 } 266 267 /* 268 * Clear a jail parameter and copy in its name. 269 */ 270 int 271 jailparam_init(struct jailparam *jp, const char *name) 272 { 273 274 memset(jp, 0, sizeof(*jp)); 275 jp->jp_name = strdup(name); 276 if (jp->jp_name == NULL) { 277 strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); 278 return (-1); 279 } 280 if (jailparam_type(jp) < 0) { 281 jailparam_free(jp, 1); 282 jp->jp_name = NULL; 283 jp->jp_value = NULL; 284 return (-1); 285 } 286 return (0); 287 } 288 289 /* 290 * Put a name and value into a jail parameter element, converting the value 291 * to internal form. 292 */ 293 int 294 jailparam_import(struct jailparam *jp, const char *value) 295 { 296 char *p, *ep, *tvalue; 297 const char *avalue; 298 int i, nval, fw; 299 300 if (value == NULL) 301 return (0); 302 if ((jp->jp_ctltype & CTLTYPE) == CTLTYPE_STRING) { 303 jp->jp_value = strdup(value); 304 if (jp->jp_value == NULL) { 305 strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); 306 return (-1); 307 } 308 return (0); 309 } 310 nval = 1; 311 if (jp->jp_elemlen) { 312 if (value[0] == '\0' || (value[0] == '-' && value[1] == '\0')) { 313 jp->jp_value = strdup(""); 314 if (jp->jp_value == NULL) { 315 strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); 316 return (-1); 317 } 318 jp->jp_valuelen = 0; 319 return (0); 320 } 321 for (p = strchr(value, ','); p; p = strchr(p + 1, ',')) 322 nval++; 323 jp->jp_valuelen = jp->jp_elemlen * nval; 324 } 325 jp->jp_value = malloc(jp->jp_valuelen); 326 if (jp->jp_value == NULL) { 327 strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); 328 return (-1); 329 } 330 avalue = value; 331 for (i = 0; i < nval; i++) { 332 fw = nval == 1 ? strlen(avalue) : strcspn(avalue, ","); 333 switch (jp->jp_ctltype & CTLTYPE) { 334 case CTLTYPE_INT: 335 if (jp->jp_flags & (JP_BOOL | JP_NOBOOL)) { 336 if (!jailparam_import_enum(bool_values, 2, 337 avalue, fw, &((int *)jp->jp_value)[i])) { 338 snprintf(jail_errmsg, 339 JAIL_ERRMSGLEN, "%s: " 340 "unknown boolean value \"%.*s\"", 341 jp->jp_name, fw, avalue); 342 errno = EINVAL; 343 goto error; 344 } 345 break; 346 } 347 if (jp->jp_flags & JP_JAILSYS) { 348 /* 349 * Allow setting a jailsys parameter to "new" 350 * in a booleanesque fashion. 351 */ 352 if (value[0] == '\0') 353 ((int *)jp->jp_value)[i] = JAIL_SYS_NEW; 354 else if (!jailparam_import_enum(jailsys_values, 355 sizeof(jailsys_values) / 356 sizeof(jailsys_values[0]), avalue, fw, 357 &((int *)jp->jp_value)[i])) { 358 snprintf(jail_errmsg, 359 JAIL_ERRMSGLEN, "%s: " 360 "unknown jailsys value \"%.*s\"", 361 jp->jp_name, fw, avalue); 362 errno = EINVAL; 363 goto error; 364 } 365 break; 366 } 367 ((int *)jp->jp_value)[i] = strtol(avalue, &ep, 10); 368 integer_test: 369 if (ep != avalue + fw) { 370 snprintf(jail_errmsg, JAIL_ERRMSGLEN, 371 "%s: non-integer value \"%.*s\"", 372 jp->jp_name, fw, avalue); 373 errno = EINVAL; 374 goto error; 375 } 376 break; 377 case CTLTYPE_UINT: 378 ((unsigned *)jp->jp_value)[i] = 379 strtoul(avalue, &ep, 10); 380 goto integer_test; 381 case CTLTYPE_LONG: 382 ((long *)jp->jp_value)[i] = strtol(avalue, &ep, 10); 383 goto integer_test; 384 case CTLTYPE_ULONG: 385 ((unsigned long *)jp->jp_value)[i] = 386 strtoul(avalue, &ep, 10); 387 goto integer_test; 388 case CTLTYPE_S64: 389 ((int64_t *)jp->jp_value)[i] = 390 strtoimax(avalue, &ep, 10); 391 goto integer_test; 392 case CTLTYPE_U64: 393 ((uint64_t *)jp->jp_value)[i] = 394 strtoumax(avalue, &ep, 10); 395 goto integer_test; 396 case CTLTYPE_STRUCT: 397 tvalue = alloca(fw + 1); 398 strlcpy(tvalue, avalue, fw + 1); 399 switch (jp->jp_structtype) { 400 case JPS_IN_ADDR: 401 if (inet_pton(AF_INET, tvalue, 402 &((struct in_addr *)jp->jp_value)[i]) != 1) 403 { 404 snprintf(jail_errmsg, 405 JAIL_ERRMSGLEN, 406 "%s: not an IPv4 address: %s", 407 jp->jp_name, tvalue); 408 errno = EINVAL; 409 goto error; 410 } 411 break; 412 case JPS_IN6_ADDR: 413 if (inet_pton(AF_INET6, tvalue, 414 &((struct in6_addr *)jp->jp_value)[i]) != 1) 415 { 416 snprintf(jail_errmsg, 417 JAIL_ERRMSGLEN, 418 "%s: not an IPv6 address: %s", 419 jp->jp_name, tvalue); 420 errno = EINVAL; 421 goto error; 422 } 423 break; 424 default: 425 goto unknown_type; 426 } 427 break; 428 default: 429 unknown_type: 430 snprintf(jail_errmsg, JAIL_ERRMSGLEN, 431 "unknown type for %s", jp->jp_name); 432 errno = ENOENT; 433 goto error; 434 } 435 avalue += fw + 1; 436 } 437 return (0); 438 439 error: 440 free(jp->jp_value); 441 jp->jp_value = NULL; 442 return (-1); 443 } 444 445 static int 446 jailparam_import_enum(const char **values, int nvalues, const char *valstr, 447 size_t valsize, int *value) 448 { 449 char *ep; 450 int i; 451 452 for (i = 0; i < nvalues; i++) 453 if (valsize == strlen(values[i]) && 454 !strncasecmp(valstr, values[i], valsize)) { 455 *value = i; 456 return 1; 457 } 458 *value = strtol(valstr, &ep, 10); 459 return (ep == valstr + valsize); 460 } 461 462 /* 463 * Put a name and value into a jail parameter element, copying the value 464 * but not altering it. 465 */ 466 int 467 jailparam_import_raw(struct jailparam *jp, void *value, size_t valuelen) 468 { 469 470 jp->jp_value = value; 471 jp->jp_valuelen = valuelen; 472 jp->jp_flags |= JP_RAWVALUE; 473 return (0); 474 } 475 476 /* 477 * Run the jail_set and jail_get system calls on a parameter list. 478 */ 479 int 480 jailparam_set(struct jailparam *jp, unsigned njp, int flags) 481 { 482 struct iovec *jiov; 483 char *nname; 484 int i, jid, bool0; 485 unsigned j; 486 487 jiov = alloca(sizeof(struct iovec) * 2 * (njp + 1)); 488 bool0 = 0; 489 for (i = j = 0; j < njp; j++) { 490 jiov[i].iov_base = jp[j].jp_name; 491 jiov[i].iov_len = strlen(jp[j].jp_name) + 1; 492 i++; 493 if (jp[j].jp_flags & (JP_BOOL | JP_NOBOOL)) { 494 /* 495 * Set booleans without values. If one has a value of 496 * zero, change it to (or from) its "no" counterpart. 497 */ 498 jiov[i].iov_base = NULL; 499 jiov[i].iov_len = 0; 500 if (jp[j].jp_value != NULL && 501 jp[j].jp_valuelen == sizeof(int) && 502 !*(int *)jp[j].jp_value) { 503 bool0 = 1; 504 nname = jp[j].jp_flags & JP_BOOL 505 ? noname(jp[j].jp_name) 506 : nononame(jp[j].jp_name); 507 if (nname == NULL) { 508 njp = j; 509 jid = -1; 510 goto done; 511 } 512 jiov[i - 1].iov_base = nname; 513 jiov[i - 1].iov_len = strlen(nname) + 1; 514 515 } 516 } else { 517 /* 518 * Try to fill in missing values with an empty string. 519 */ 520 if (jp[j].jp_value == NULL && jp[j].jp_valuelen > 0 && 521 jailparam_import(jp + j, "") < 0) { 522 njp = j; 523 jid = -1; 524 goto done; 525 } 526 jiov[i].iov_base = jp[j].jp_value; 527 jiov[i].iov_len = 528 (jp[j].jp_ctltype & CTLTYPE) == CTLTYPE_STRING 529 ? strlen(jp[j].jp_value) + 1 530 : jp[j].jp_valuelen; 531 } 532 i++; 533 } 534 jiov[i].iov_base = __DECONST(char *, "errmsg"); 535 jiov[i].iov_len = sizeof("errmsg"); 536 i++; 537 jiov[i].iov_base = jail_errmsg; 538 jiov[i].iov_len = JAIL_ERRMSGLEN; 539 i++; 540 jail_errmsg[0] = 0; 541 jid = jail_set(jiov, i, flags); 542 if (jid < 0 && !jail_errmsg[0]) 543 snprintf(jail_errmsg, sizeof(jail_errmsg), "jail_set: %s", 544 strerror(errno)); 545 done: 546 if (bool0) 547 for (j = 0; j < njp; j++) 548 if ((jp[j].jp_flags & (JP_BOOL | JP_NOBOOL)) && 549 jp[j].jp_value != NULL && 550 jp[j].jp_valuelen == sizeof(int) && 551 !*(int *)jp[j].jp_value) 552 free(jiov[j * 2].iov_base); 553 return (jid); 554 } 555 556 int 557 jailparam_get(struct jailparam *jp, unsigned njp, int flags) 558 { 559 struct iovec *jiov; 560 struct jailparam *jp_lastjid, *jp_jid, *jp_name, *jp_key; 561 int i, ai, ki, jid, arrays, sanity; 562 unsigned j; 563 564 /* 565 * Get the types for all parameters. 566 * Find the key and any array parameters. 567 */ 568 jiov = alloca(sizeof(struct iovec) * 2 * (njp + 1)); 569 jp_lastjid = jp_jid = jp_name = NULL; 570 arrays = 0; 571 for (ai = j = 0; j < njp; j++) { 572 if (!strcmp(jp[j].jp_name, "lastjid")) 573 jp_lastjid = jp + j; 574 else if (!strcmp(jp[j].jp_name, "jid")) 575 jp_jid = jp + j; 576 else if (!strcmp(jp[j].jp_name, "name")) 577 jp_name = jp + j; 578 else if (jp[j].jp_elemlen && !(jp[j].jp_flags & JP_RAWVALUE)) { 579 arrays = 1; 580 jiov[ai].iov_base = jp[j].jp_name; 581 jiov[ai].iov_len = strlen(jp[j].jp_name) + 1; 582 ai++; 583 jiov[ai].iov_base = NULL; 584 jiov[ai].iov_len = 0; 585 ai++; 586 } 587 } 588 jp_key = jp_lastjid ? jp_lastjid : 589 jp_jid && jp_jid->jp_valuelen == sizeof(int) && 590 jp_jid->jp_value && *(int *)jp_jid->jp_value ? jp_jid : jp_name; 591 if (jp_key == NULL || jp_key->jp_value == NULL) { 592 strlcpy(jail_errmsg, "no jail specified", JAIL_ERRMSGLEN); 593 errno = ENOENT; 594 return (-1); 595 } 596 ki = ai; 597 jiov[ki].iov_base = jp_key->jp_name; 598 jiov[ki].iov_len = strlen(jp_key->jp_name) + 1; 599 ki++; 600 jiov[ki].iov_base = jp_key->jp_value; 601 jiov[ki].iov_len = (jp_key->jp_ctltype & CTLTYPE) == CTLTYPE_STRING 602 ? strlen(jp_key->jp_value) + 1 : jp_key->jp_valuelen; 603 ki++; 604 jiov[ki].iov_base = __DECONST(char *, "errmsg"); 605 jiov[ki].iov_len = sizeof("errmsg"); 606 ki++; 607 jiov[ki].iov_base = jail_errmsg; 608 jiov[ki].iov_len = JAIL_ERRMSGLEN; 609 ki++; 610 jail_errmsg[0] = 0; 611 if (arrays && jail_get(jiov, ki, flags) < 0) { 612 if (!jail_errmsg[0]) 613 snprintf(jail_errmsg, sizeof(jail_errmsg), 614 "jail_get: %s", strerror(errno)); 615 return (-1); 616 } 617 /* Allocate storage for all parameters. */ 618 for (ai = j = 0, i = ki; j < njp; j++) { 619 if (jp[j].jp_elemlen && !(jp[j].jp_flags & JP_RAWVALUE)) { 620 ai++; 621 jiov[ai].iov_len += jp[j].jp_elemlen * ARRAY_SLOP; 622 if (jp[j].jp_valuelen >= jiov[ai].iov_len) 623 jiov[ai].iov_len = jp[j].jp_valuelen; 624 else { 625 jp[j].jp_valuelen = jiov[ai].iov_len; 626 if (jp[j].jp_value != NULL) 627 free(jp[j].jp_value); 628 jp[j].jp_value = malloc(jp[j].jp_valuelen); 629 if (jp[j].jp_value == NULL) { 630 strerror_r(errno, jail_errmsg, 631 JAIL_ERRMSGLEN); 632 return (-1); 633 } 634 } 635 jiov[ai].iov_base = jp[j].jp_value; 636 memset(jiov[ai].iov_base, 0, jiov[ai].iov_len); 637 ai++; 638 } else if (jp + j != jp_key) { 639 jiov[i].iov_base = jp[j].jp_name; 640 jiov[i].iov_len = strlen(jp[j].jp_name) + 1; 641 i++; 642 if (jp[j].jp_value == NULL && 643 !(jp[j].jp_flags & JP_RAWVALUE)) { 644 jp[j].jp_value = malloc(jp[j].jp_valuelen); 645 if (jp[j].jp_value == NULL) { 646 strerror_r(errno, jail_errmsg, 647 JAIL_ERRMSGLEN); 648 return (-1); 649 } 650 } 651 jiov[i].iov_base = jp[j].jp_value; 652 jiov[i].iov_len = jp[j].jp_valuelen; 653 memset(jiov[i].iov_base, 0, jiov[i].iov_len); 654 i++; 655 } 656 } 657 /* 658 * Get the prison. If there are array elements, retry a few times 659 * in case their sizes changed from under us. 660 */ 661 for (sanity = 0;; sanity++) { 662 jid = jail_get(jiov, i, flags); 663 if (jid >= 0 || !arrays || sanity == ARRAY_SANITY || 664 errno != EINVAL || jail_errmsg[0]) 665 break; 666 for (ai = j = 0; j < njp; j++) { 667 if (jp[j].jp_elemlen && 668 !(jp[j].jp_flags & JP_RAWVALUE)) { 669 ai++; 670 jiov[ai].iov_base = NULL; 671 jiov[ai].iov_len = 0; 672 ai++; 673 } 674 } 675 if (jail_get(jiov, ki, flags) < 0) 676 break; 677 for (ai = j = 0; j < njp; j++) { 678 if (jp[j].jp_elemlen && 679 !(jp[j].jp_flags & JP_RAWVALUE)) { 680 ai++; 681 jiov[ai].iov_len += 682 jp[j].jp_elemlen * ARRAY_SLOP; 683 if (jp[j].jp_valuelen >= jiov[ai].iov_len) 684 jiov[ai].iov_len = jp[j].jp_valuelen; 685 else { 686 jp[j].jp_valuelen = jiov[ai].iov_len; 687 if (jp[j].jp_value != NULL) 688 free(jp[j].jp_value); 689 jp[j].jp_value = 690 malloc(jiov[ai].iov_len); 691 if (jp[j].jp_value == NULL) { 692 strerror_r(errno, jail_errmsg, 693 JAIL_ERRMSGLEN); 694 return (-1); 695 } 696 } 697 jiov[ai].iov_base = jp[j].jp_value; 698 memset(jiov[ai].iov_base, 0, jiov[ai].iov_len); 699 ai++; 700 } 701 } 702 } 703 if (jid < 0 && !jail_errmsg[0]) 704 snprintf(jail_errmsg, sizeof(jail_errmsg), 705 "jail_get: %s", strerror(errno)); 706 for (ai = j = 0, i = ki; j < njp; j++) { 707 if (jp[j].jp_elemlen && !(jp[j].jp_flags & JP_RAWVALUE)) { 708 ai++; 709 jp[j].jp_valuelen = jiov[ai].iov_len; 710 ai++; 711 } else if (jp + j != jp_key) { 712 i++; 713 jp[j].jp_valuelen = jiov[i].iov_len; 714 i++; 715 } 716 } 717 return (jid); 718 } 719 720 /* 721 * Convert a jail parameter's value to external form. 722 */ 723 char * 724 jailparam_export(struct jailparam *jp) 725 { 726 size_t *valuelens; 727 char *value, *tvalue, **values; 728 size_t valuelen; 729 int i, nval, ival; 730 char valbuf[INET6_ADDRSTRLEN]; 731 732 if ((jp->jp_ctltype & CTLTYPE) == CTLTYPE_STRING) { 733 value = strdup(jp->jp_value); 734 if (value == NULL) 735 strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); 736 return (value); 737 } 738 nval = jp->jp_elemlen ? jp->jp_valuelen / jp->jp_elemlen : 1; 739 if (nval == 0) { 740 value = strdup(""); 741 if (value == NULL) 742 strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); 743 return (value); 744 } 745 values = alloca(nval * sizeof(char *)); 746 valuelens = alloca(nval * sizeof(size_t)); 747 valuelen = 0; 748 for (i = 0; i < nval; i++) { 749 switch (jp->jp_ctltype & CTLTYPE) { 750 case CTLTYPE_INT: 751 ival = ((int *)jp->jp_value)[i]; 752 if ((jp->jp_flags & (JP_BOOL | JP_NOBOOL)) && 753 (unsigned)ival < 2) { 754 strlcpy(valbuf, bool_values[ival], 755 sizeof(valbuf)); 756 break; 757 } 758 if ((jp->jp_flags & JP_JAILSYS) && 759 (unsigned)ival < sizeof(jailsys_values) / 760 sizeof(jailsys_values[0])) { 761 strlcpy(valbuf, jailsys_values[ival], 762 sizeof(valbuf)); 763 break; 764 } 765 snprintf(valbuf, sizeof(valbuf), "%d", ival); 766 break; 767 case CTLTYPE_UINT: 768 snprintf(valbuf, sizeof(valbuf), "%u", 769 ((unsigned *)jp->jp_value)[i]); 770 break; 771 case CTLTYPE_LONG: 772 snprintf(valbuf, sizeof(valbuf), "%ld", 773 ((long *)jp->jp_value)[i]); 774 break; 775 case CTLTYPE_ULONG: 776 snprintf(valbuf, sizeof(valbuf), "%lu", 777 ((unsigned long *)jp->jp_value)[i]); 778 break; 779 case CTLTYPE_S64: 780 snprintf(valbuf, sizeof(valbuf), "%jd", 781 (intmax_t)((int64_t *)jp->jp_value)[i]); 782 break; 783 case CTLTYPE_U64: 784 snprintf(valbuf, sizeof(valbuf), "%ju", 785 (uintmax_t)((uint64_t *)jp->jp_value)[i]); 786 break; 787 case CTLTYPE_STRUCT: 788 switch (jp->jp_structtype) { 789 case JPS_IN_ADDR: 790 if (inet_ntop(AF_INET, 791 &((struct in_addr *)jp->jp_value)[i], 792 valbuf, sizeof(valbuf)) == NULL) { 793 strerror_r(errno, jail_errmsg, 794 JAIL_ERRMSGLEN); 795 return (NULL); 796 } 797 break; 798 case JPS_IN6_ADDR: 799 if (inet_ntop(AF_INET6, 800 &((struct in6_addr *)jp->jp_value)[i], 801 valbuf, sizeof(valbuf)) == NULL) { 802 strerror_r(errno, jail_errmsg, 803 JAIL_ERRMSGLEN); 804 return (NULL); 805 } 806 break; 807 default: 808 goto unknown_type; 809 } 810 break; 811 default: 812 unknown_type: 813 snprintf(jail_errmsg, JAIL_ERRMSGLEN, 814 "unknown type for %s", jp->jp_name); 815 errno = ENOENT; 816 return (NULL); 817 } 818 valuelens[i] = strlen(valbuf) + 1; 819 valuelen += valuelens[i]; 820 values[i] = alloca(valuelens[i]); 821 strcpy(values[i], valbuf); 822 } 823 value = malloc(valuelen); 824 if (value == NULL) 825 strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); 826 else { 827 tvalue = value; 828 for (i = 0; i < nval; i++) { 829 strcpy(tvalue, values[i]); 830 if (i < nval - 1) { 831 tvalue += valuelens[i]; 832 tvalue[-1] = ','; 833 } 834 } 835 } 836 return (value); 837 } 838 839 /* 840 * Free the contents of a jail parameter list (but not the list itself). 841 */ 842 void 843 jailparam_free(struct jailparam *jp, unsigned njp) 844 { 845 unsigned j; 846 847 for (j = 0; j < njp; j++) { 848 free(jp[j].jp_name); 849 if (!(jp[j].jp_flags & JP_RAWVALUE)) 850 free(jp[j].jp_value); 851 } 852 } 853 854 /* 855 * Find a parameter's type and size from its MIB. 856 */ 857 static int 858 jailparam_type(struct jailparam *jp) 859 { 860 char *p, *name, *nname; 861 size_t miblen, desclen; 862 int i, isarray; 863 struct { 864 int i; 865 char s[MAXPATHLEN]; 866 } desc; 867 int mib[CTL_MAXNAME]; 868 869 /* The "lastjid" parameter isn't real. */ 870 name = jp->jp_name; 871 if (!strcmp(name, "lastjid")) { 872 jp->jp_valuelen = sizeof(int); 873 jp->jp_ctltype = CTLTYPE_INT | CTLFLAG_WR; 874 return (0); 875 } 876 877 /* Find the sysctl that describes the parameter. */ 878 mib[0] = 0; 879 mib[1] = 3; 880 snprintf(desc.s, sizeof(desc.s), SJPARAM ".%s", name); 881 miblen = sizeof(mib) - 2 * sizeof(int); 882 if (sysctl(mib, 2, mib + 2, &miblen, desc.s, strlen(desc.s)) < 0) { 883 if (errno != ENOENT) { 884 snprintf(jail_errmsg, JAIL_ERRMSGLEN, 885 "sysctl(0.3.%s): %s", name, strerror(errno)); 886 return (-1); 887 } 888 /* 889 * The parameter probably doesn't exist. But it might be 890 * the "no" counterpart to a boolean. 891 */ 892 nname = nononame(name); 893 if (nname == NULL) { 894 unknown_parameter: 895 snprintf(jail_errmsg, JAIL_ERRMSGLEN, 896 "unknown parameter: %s", jp->jp_name); 897 errno = ENOENT; 898 return (-1); 899 } 900 name = alloca(strlen(nname) + 1); 901 strcpy(name, nname); 902 free(nname); 903 snprintf(desc.s, sizeof(desc.s), SJPARAM ".%s", name); 904 miblen = sizeof(mib) - 2 * sizeof(int); 905 if (sysctl(mib, 2, mib + 2, &miblen, desc.s, 906 strlen(desc.s)) < 0) 907 goto unknown_parameter; 908 jp->jp_flags |= JP_NOBOOL; 909 } 910 mib_desc: 911 mib[1] = 4; 912 desclen = sizeof(desc); 913 if (sysctl(mib, (miblen / sizeof(int)) + 2, &desc, &desclen, 914 NULL, 0) < 0) { 915 snprintf(jail_errmsg, JAIL_ERRMSGLEN, 916 "sysctl(0.4.%s): %s", name, strerror(errno)); 917 return (-1); 918 } 919 jp->jp_ctltype = desc.i; 920 /* If this came from removing a "no", it better be a boolean. */ 921 if (jp->jp_flags & JP_NOBOOL) { 922 if ((desc.i & CTLTYPE) == CTLTYPE_INT && desc.s[0] == 'B') { 923 jp->jp_valuelen = sizeof(int); 924 return (0); 925 } 926 else if ((desc.i & CTLTYPE) != CTLTYPE_NODE) 927 goto unknown_parameter; 928 } 929 /* See if this is an array type. */ 930 p = strchr(desc.s, '\0'); 931 isarray = 0; 932 if (p - 2 < desc.s || strcmp(p - 2, ",a")) 933 isarray = 0; 934 else { 935 isarray = 1; 936 p[-2] = 0; 937 } 938 /* Look for types we understand. */ 939 switch (desc.i & CTLTYPE) { 940 case CTLTYPE_INT: 941 if (desc.s[0] == 'B') 942 jp->jp_flags |= JP_BOOL; 943 else if (!strcmp(desc.s, "E,jailsys")) 944 jp->jp_flags |= JP_JAILSYS; 945 case CTLTYPE_UINT: 946 jp->jp_valuelen = sizeof(int); 947 break; 948 case CTLTYPE_LONG: 949 case CTLTYPE_ULONG: 950 jp->jp_valuelen = sizeof(long); 951 break; 952 case CTLTYPE_S64: 953 case CTLTYPE_U64: 954 jp->jp_valuelen = sizeof(int64_t); 955 break; 956 case CTLTYPE_STRING: 957 desc.s[0] = 0; 958 desclen = sizeof(desc.s); 959 if (sysctl(mib + 2, miblen / sizeof(int), desc.s, &desclen, 960 NULL, 0) < 0) { 961 snprintf(jail_errmsg, JAIL_ERRMSGLEN, 962 "sysctl(" SJPARAM ".%s): %s", name, 963 strerror(errno)); 964 return (-1); 965 } 966 jp->jp_valuelen = strtoul(desc.s, NULL, 10); 967 break; 968 case CTLTYPE_STRUCT: 969 if (!strcmp(desc.s, "S,in_addr")) { 970 jp->jp_structtype = JPS_IN_ADDR; 971 jp->jp_valuelen = sizeof(struct in_addr); 972 } else if (!strcmp(desc.s, "S,in6_addr")) { 973 jp->jp_structtype = JPS_IN6_ADDR; 974 jp->jp_valuelen = sizeof(struct in6_addr); 975 } else { 976 desclen = 0; 977 if (sysctl(mib + 2, miblen / sizeof(int), 978 NULL, &jp->jp_valuelen, NULL, 0) < 0) { 979 snprintf(jail_errmsg, JAIL_ERRMSGLEN, 980 "sysctl(" SJPARAM ".%s): %s", name, 981 strerror(errno)); 982 return (-1); 983 } 984 } 985 break; 986 case CTLTYPE_NODE: 987 /* 988 * A node might be described by an empty-named child, 989 * which would be immediately before or after the node itself. 990 */ 991 mib[1] = 1; 992 miblen += sizeof(int); 993 for (i = -1; i <= 1; i += 2) { 994 mib[(miblen / sizeof(int)) + 1] = 995 mib[(miblen / sizeof(int))] + i; 996 desclen = sizeof(desc.s); 997 if (sysctl(mib, (miblen / sizeof(int)) + 2, desc.s, 998 &desclen, NULL, 0) < 0) { 999 if (errno == ENOENT) 1000 continue; 1001 snprintf(jail_errmsg, JAIL_ERRMSGLEN, 1002 "sysctl(0.1): %s", strerror(errno)); 1003 return (-1); 1004 } 1005 if (desclen == sizeof(SJPARAM) + strlen(name) + 2 && 1006 memcmp(SJPARAM ".", desc.s, sizeof(SJPARAM)) == 0 && 1007 memcmp(name, desc.s + sizeof(SJPARAM), 1008 desclen - sizeof(SJPARAM) - 2) == 0 && 1009 desc.s[desclen - 2] == '.') 1010 goto mib_desc; 1011 } 1012 goto unknown_parameter; 1013 default: 1014 snprintf(jail_errmsg, JAIL_ERRMSGLEN, 1015 "unknown type for %s", jp->jp_name); 1016 errno = ENOENT; 1017 return (-1); 1018 } 1019 if (isarray) { 1020 jp->jp_elemlen = jp->jp_valuelen; 1021 jp->jp_valuelen = 0; 1022 } 1023 return (0); 1024 } 1025 1026 /* 1027 * Change a boolean parameter name into its "no" counterpart or vice versa. 1028 */ 1029 static char * 1030 noname(const char *name) 1031 { 1032 char *nname, *p; 1033 1034 nname = malloc(strlen(name) + 3); 1035 if (nname == NULL) { 1036 strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); 1037 return (NULL); 1038 } 1039 p = strrchr(name, '.'); 1040 if (p != NULL) 1041 sprintf(nname, "%.*s.no%s", (int)(p - name), name, p + 1); 1042 else 1043 sprintf(nname, "no%s", name); 1044 return (nname); 1045 } 1046 1047 static char * 1048 nononame(const char *name) 1049 { 1050 char *p, *nname; 1051 1052 p = strrchr(name, '.'); 1053 if (strncmp(p ? p + 1 : name, "no", 2)) { 1054 snprintf(jail_errmsg, sizeof(jail_errmsg), 1055 "mismatched boolean: %s", name); 1056 errno = EINVAL; 1057 return (NULL); 1058 } 1059 nname = malloc(strlen(name) - 1); 1060 if (nname == NULL) { 1061 strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN); 1062 return (NULL); 1063 } 1064 if (p != NULL) 1065 sprintf(nname, "%.*s.%s", (int)(p - name), name, p + 3); 1066 else 1067 strcpy(nname, name + 2); 1068 return (nname); 1069 } 1070