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