1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/types.h> 27 #include <sys/systm.h> 28 #include <sys/kmem.h> 29 #include <sys/disp.h> 30 #include <sys/stream.h> 31 #include <sys/strsubr.h> 32 #include <sys/strsun.h> 33 #include <sys/policy.h> 34 #include <sys/tsol/label_macro.h> 35 #include <sys/tsol/tndb.h> 36 #include <sys/tsol/tnet.h> 37 #include <inet/ip.h> 38 #include <inet/ip6.h> 39 #include <inet/tcp.h> 40 #include <inet/ipclassifier.h> 41 #include <inet/ip_ire.h> 42 #include <inet/ip_ftable.h> 43 44 /* 45 * This routine takes a sensitivity label as input and creates a CIPSO 46 * option in the specified buffer. It returns the size of the CIPSO option. 47 * If the sensitivity label is too large for the CIPSO option, then 0 48 * is returned. 49 * 50 * tsol2cipso_tt1 returns 0 for failure and greater than 0 for success 51 * (more accurately, success means a return value between 10 and 40). 52 */ 53 54 static int 55 tsol2cipso_tt1(const bslabel_t *sl, unsigned char *cop, uint32_t doi) 56 { 57 struct cipso_tag_type_1 *tt1; 58 const _bslabel_impl_t *bsl; 59 const uchar_t *ucp; 60 int i; 61 62 if (doi == 0) 63 return (0); 64 65 /* check for Admin High sensitivity label */ 66 if (blequal(sl, label2bslabel(l_admin_high))) 67 return (0); 68 69 /* check whether classification will fit in one octet */ 70 bsl = (const _bslabel_impl_t *)sl; 71 if (LCLASS(bsl) & 0xFF00) 72 return (0); 73 74 /* 75 * Check whether compartments will fit in 30 octets. 76 * Compartments 241 - 256 are not allowed. 77 */ 78 if (ntohl(bsl->compartments.c8) & 0x0000FFFF) 79 return (0); 80 81 /* 82 * Compute option length and tag length. 83 * 'p' points to the last two bytes in the Sensitivity Label's 84 * compartments; these cannot be mapped into CIPSO compartments. 85 */ 86 ucp = (const uchar_t *)&bsl->compartments.c8 + 2; 87 while (--ucp >= (const uchar_t *)&bsl->compartments.c1) 88 if (*ucp != 0) 89 break; 90 91 i = ucp - (const uchar_t *)&bsl->compartments.c1 + 1; 92 93 if (cop == NULL) 94 return (10 + i); 95 96 doi = htonl(doi); 97 ucp = (const uchar_t *)&doi; 98 cop[IPOPT_OPTVAL] = IPOPT_COMSEC; 99 cop[IPOPT_OLEN] = 10 + i; 100 cop[IPOPT_OLEN+1] = ucp[0]; 101 cop[IPOPT_OLEN+2] = ucp[1]; 102 cop[IPOPT_OLEN+3] = ucp[2]; 103 cop[IPOPT_OLEN+4] = ucp[3]; 104 tt1 = (struct cipso_tag_type_1 *)&cop[IPOPT_OLEN + 5]; 105 tt1->tag_type = 1; 106 tt1->tag_align = 0; 107 tt1->tag_sl = LCLASS(bsl); 108 tt1->tag_length = 4 + i; 109 110 bcopy(&bsl->compartments.c1, tt1->tag_cat, i); 111 112 return (cop[IPOPT_OLEN]); 113 } 114 115 /* 116 * The following routine copies a datagram's option into the specified buffer 117 * (if buffer pointer is non-null), or returns a pointer to the label within 118 * the streams message (if buffer is null). In both cases, tsol_get_option 119 * returns the option's type. 120 * 121 * tsol_get_option assumes that the specified buffer is large enough to 122 * hold the largest valid CIPSO option. Since the total number of 123 * IP header options cannot exceed 40 bytes, a 40 byte buffer is a good choice. 124 */ 125 126 tsol_ip_label_t 127 tsol_get_option(mblk_t *mp, uchar_t **buffer) 128 { 129 ipha_t *ipha; 130 uchar_t *opt; 131 uint32_t totallen; 132 uint32_t optval; 133 uint32_t optlen; 134 135 ipha = (ipha_t *)mp->b_rptr; 136 137 /* 138 * Get length (in 4 byte octets) of IP header options. 139 * If header doesn't contain options, then return OPT_NONE. 140 */ 141 totallen = ipha->ipha_version_and_hdr_length - 142 (uint8_t)((IP_VERSION << 4) + IP_SIMPLE_HDR_LENGTH_IN_WORDS); 143 144 if (totallen == 0) 145 return (OPT_NONE); 146 147 totallen <<= 2; 148 149 /* 150 * Search for CIPSO option. 151 * If no such option is present, then return OPT_NONE. 152 */ 153 opt = (uchar_t *)&ipha[1]; 154 while (totallen != 0) { 155 switch (optval = opt[IPOPT_OPTVAL]) { 156 case IPOPT_EOL: 157 return (OPT_NONE); 158 case IPOPT_NOP: 159 optlen = 1; 160 break; 161 default: 162 if (totallen <= IPOPT_OLEN) 163 return (OPT_NONE); 164 optlen = opt[IPOPT_OLEN]; 165 if (optlen < 2) 166 return (OPT_NONE); 167 } 168 if (optlen > totallen) 169 return (OPT_NONE); 170 /* 171 * Copy pointer to option into '*buffer' and 172 * return the option type. 173 */ 174 switch (optval) { 175 case IPOPT_COMSEC: 176 *buffer = opt; 177 if (TSOL_CIPSO_TAG_OFFSET < optlen && 178 opt[TSOL_CIPSO_TAG_OFFSET] == 1) 179 return (OPT_CIPSO); 180 return (OPT_NONE); 181 } 182 totallen -= optlen; 183 opt += optlen; 184 } 185 return (OPT_NONE); 186 } 187 188 /* 189 * tsol_check_dest() 190 * 191 * This routine verifies if a destination is allowed to recieve messages 192 * based on the message cred's security label. If any adjustments to 193 * the cred are needed due to the connection's MAC-exempt status or 194 * the destination's ability to receive labels, an "effective cred" 195 * will be returned. 196 * 197 * On successful return, effective_cred will point to the new creds needed 198 * or will be NULL if new creds aren't needed. On error, effective_cred 199 * is NULL. 200 * 201 * Returns: 202 * 0 Have or constructed appropriate credentials 203 * EHOSTUNREACH The credentials failed the remote host accreditation 204 * ENOMEM Memory allocation failure 205 */ 206 int 207 tsol_check_dest(const cred_t *credp, const void *dst, uchar_t version, 208 boolean_t mac_exempt, cred_t **effective_cred) 209 { 210 ts_label_t *tsl, *newtsl = NULL; 211 tsol_tpc_t *dst_rhtp; 212 zoneid_t zoneid; 213 214 *effective_cred = NULL; 215 ASSERT(version == IPV4_VERSION || 216 (version == IPV6_VERSION && 217 !IN6_IS_ADDR_V4MAPPED((in6_addr_t *)dst))); 218 219 /* Always pass kernel level communication (NULL label) */ 220 if ((tsl = crgetlabel(credp)) == NULL) { 221 DTRACE_PROBE2(tx__tnopt__log__info__labeling__mac__allownull, 222 char *, "destination ip(1) with null cred was passed", 223 ipaddr_t, dst); 224 return (0); 225 } 226 227 /* Always pass multicast */ 228 if (version == IPV4_VERSION && 229 CLASSD(*(ipaddr_t *)dst)) { 230 DTRACE_PROBE2(tx__tnopt__log__info__labeling__mac__allowmult, 231 char *, "destination ip(1) with multicast dest was passed", 232 ipaddr_t, dst); 233 return (0); 234 } else if (version == IPV6_VERSION && 235 IN6_IS_ADDR_MULTICAST((in6_addr_t *)dst)) { 236 DTRACE_PROBE2(tx__tnopt__log__info__labeling__mac__allowmult_v6, 237 char *, "destination ip(1) with multicast dest was passed", 238 in6_addr_t *, dst); 239 return (0); 240 } 241 242 /* Never pass an undefined destination */ 243 if ((dst_rhtp = find_tpc(dst, version, B_FALSE)) == NULL) { 244 DTRACE_PROBE2(tx__tnopt__log__info__labeling__lookupdst, 245 char *, "destination ip(1) not in tn database.", 246 void *, dst); 247 return (EHOSTUNREACH); 248 } 249 250 switch (dst_rhtp->tpc_tp.host_type) { 251 case UNLABELED: 252 /* 253 * Can talk to unlabeled hosts if 254 * (1) zone's label matches the default label, or 255 * (2) SO_MAC_EXEMPT is on and we dominate the peer's label 256 * (3) SO_MAC_EXEMPT is on and this is the global zone 257 */ 258 if (dst_rhtp->tpc_tp.tp_doi != tsl->tsl_doi) { 259 DTRACE_PROBE4(tx__tnopt__log__info__labeling__doi, 260 char *, "unlabeled dest ip(1)/tpc(2) doi does " 261 "not match msg label(3) doi.", void *, dst, 262 tsol_tpc_t *, dst_rhtp, ts_label_t *, tsl); 263 TPC_RELE(dst_rhtp); 264 return (EHOSTUNREACH); 265 } 266 if (!blequal(&dst_rhtp->tpc_tp.tp_def_label, 267 &tsl->tsl_label)) { 268 zoneid = crgetzoneid(credp); 269 if (!mac_exempt || 270 !(zoneid == GLOBAL_ZONEID || 271 bldominates(&tsl->tsl_label, 272 &dst_rhtp->tpc_tp.tp_def_label))) { 273 DTRACE_PROBE4( 274 tx__tnopt__log__info__labeling__mac, 275 char *, "unlabeled dest ip(1)/tpc(2) does " 276 "not match msg label(3).", void *, dst, 277 tsol_tpc_t *, dst_rhtp, ts_label_t *, tsl); 278 TPC_RELE(dst_rhtp); 279 return (EHOSTUNREACH); 280 } 281 /* 282 * This is a downlabel MAC-exempt exchange. 283 * Use the remote destination's default label 284 * as the label of the message data. 285 */ 286 if ((newtsl = labelalloc(&dst_rhtp->tpc_tp.tp_def_label, 287 dst_rhtp->tpc_tp.tp_doi, KM_NOSLEEP)) == NULL) { 288 TPC_RELE(dst_rhtp); 289 return (ENOMEM); 290 } 291 newtsl->tsl_flags |= TSLF_UNLABELED; 292 293 } else if (!(tsl->tsl_flags & TSLF_UNLABELED)) { 294 /* 295 * The security labels are the same but we need 296 * to flag that the remote node is unlabeled. 297 */ 298 if ((newtsl = labeldup(tsl, KM_NOSLEEP)) == NULL) { 299 TPC_RELE(dst_rhtp); 300 return (ENOMEM); 301 } 302 newtsl->tsl_flags |= TSLF_UNLABELED; 303 } 304 break; 305 306 case SUN_CIPSO: 307 /* 308 * Can talk to labeled hosts if zone's label is within target's 309 * label range or set. 310 */ 311 if (dst_rhtp->tpc_tp.tp_cipso_doi_cipso != tsl->tsl_doi || 312 (!_blinrange(&tsl->tsl_label, 313 &dst_rhtp->tpc_tp.tp_sl_range_cipso) && 314 !blinlset(&tsl->tsl_label, 315 dst_rhtp->tpc_tp.tp_sl_set_cipso))) { 316 DTRACE_PROBE4(tx__tnopt__log__info__labeling__mac, 317 char *, "labeled dest ip(1)/tpc(2) does not " 318 "match msg label(3).", void *, dst, 319 tsol_tpc_t *, dst_rhtp, ts_label_t *, tsl); 320 TPC_RELE(dst_rhtp); 321 return (EHOSTUNREACH); 322 } 323 if (tsl->tsl_flags & TSLF_UNLABELED) { 324 /* 325 * The security label is a match but we need to 326 * clear the unlabeled flag for this remote node. 327 */ 328 if ((newtsl = labeldup(tsl, KM_NOSLEEP)) == NULL) { 329 TPC_RELE(dst_rhtp); 330 return (ENOMEM); 331 } 332 newtsl->tsl_flags ^= TSLF_UNLABELED; 333 } 334 break; 335 336 default: 337 TPC_RELE(dst_rhtp); 338 return (EHOSTUNREACH); 339 } 340 341 /* 342 * Generate a new cred if we modified the security label or 343 * label flags. 344 */ 345 if (newtsl != NULL) { 346 *effective_cred = copycred_from_tslabel(credp, 347 newtsl, KM_NOSLEEP); 348 label_rele(newtsl); 349 if (*effective_cred == NULL) { 350 TPC_RELE(dst_rhtp); 351 return (ENOMEM); 352 } 353 } 354 TPC_RELE(dst_rhtp); 355 return (0); 356 } 357 358 /* 359 * tsol_compute_label() 360 * 361 * This routine computes the IP label that should be on a packet based on the 362 * connection and destination information. 363 * 364 * Returns: 365 * 0 Fetched label 366 * EHOSTUNREACH No route to destination 367 * EINVAL Label cannot be computed 368 */ 369 int 370 tsol_compute_label(const cred_t *credp, ipaddr_t dst, uchar_t *opt_storage, 371 ip_stack_t *ipst) 372 { 373 uint_t sec_opt_len; 374 ts_label_t *tsl; 375 ire_t *ire, *sire = NULL; 376 tsol_ire_gw_secattr_t *attrp; 377 zoneid_t zoneid, ip_zoneid; 378 379 ASSERT(credp != NULL); 380 381 if (opt_storage != NULL) 382 opt_storage[IPOPT_OLEN] = 0; 383 384 if ((tsl = crgetlabel(credp)) == NULL) 385 return (0); 386 387 /* always pass multicast */ 388 if (CLASSD(dst)) 389 return (0); 390 391 if (tsl->tsl_flags & TSLF_UNLABELED) { 392 393 /* 394 * The destination is unlabeled. Only add a label if the 395 * destination is not a broadcast/local/loopback address, 396 * the destination is not on the same subnet, and the 397 * next-hop gateway is labeled. 398 * 399 * For exclusive stacks we set the zoneid to zero 400 * to operate as if we are in the global zone for 401 * IRE lookups. 402 */ 403 zoneid = crgetzoneid(credp); 404 if (ipst->ips_netstack->netstack_stackid != GLOBAL_NETSTACKID) 405 ip_zoneid = GLOBAL_ZONEID; 406 else 407 ip_zoneid = zoneid; 408 409 ire = ire_cache_lookup(dst, ip_zoneid, tsl, ipst); 410 411 if (ire != NULL && (ire->ire_type & (IRE_BROADCAST | IRE_LOCAL | 412 IRE_LOOPBACK | IRE_INTERFACE)) != 0) { 413 IRE_REFRELE(ire); 414 return (0); 415 } else if (ire == NULL) { 416 ire = ire_ftable_lookup(dst, 0, 0, 0, NULL, &sire, 417 ip_zoneid, 0, tsl, (MATCH_IRE_RECURSIVE | 418 MATCH_IRE_DEFAULT | MATCH_IRE_SECATTR), ipst); 419 } 420 421 /* no route to destination */ 422 if (ire == NULL) { 423 DTRACE_PROBE3( 424 tx__tnopt__log__info__labeling__routedst__v4, 425 char *, "No route to unlabeled dest ip(1) with " 426 "creds(2).", ipaddr_t, dst, cred_t *, credp); 427 return (EHOSTUNREACH); 428 } 429 430 /* 431 * Prefix IRE from f-table lookup means that the destination 432 * is not directly connected; check the next-hop attributes. 433 */ 434 if (sire != NULL) { 435 ASSERT(ire != NULL); 436 IRE_REFRELE(ire); 437 ire = sire; 438 } 439 440 /* 441 * Return now if next hop gateway is unlabeled. There is 442 * no need to generate a CIPSO option for this message. 443 */ 444 attrp = ire->ire_gw_secattr; 445 if (attrp == NULL || attrp->igsa_rhc == NULL || 446 attrp->igsa_rhc->rhc_tpc->tpc_tp.host_type == UNLABELED) { 447 IRE_REFRELE(ire); 448 return (0); 449 } 450 451 IRE_REFRELE(ire); 452 453 } 454 455 /* compute the CIPSO option */ 456 sec_opt_len = tsol2cipso_tt1(&tsl->tsl_label, opt_storage, 457 tsl->tsl_doi); 458 459 if (sec_opt_len == 0) { 460 DTRACE_PROBE3(tx__tnopt__log__error__labeling__lostops__v4, 461 char *, "options lack length for dest ip(1) with creds(2).", 462 ipaddr_t, dst, cred_t *, credp); 463 return (EINVAL); 464 } 465 466 return (0); 467 } 468 469 /* 470 * Remove any existing security option (CIPSO) from the given IP 471 * header, move the 'buflen' bytes back to fill the gap, and return the number 472 * of bytes removed (as zero or negative number). Assumes that the headers are 473 * sane. 474 */ 475 int 476 tsol_remove_secopt(ipha_t *ipha, int buflen) 477 { 478 int remlen, olen, oval, delta; 479 uchar_t *fptr, *tptr; 480 boolean_t noop_keep; 481 482 remlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH; 483 fptr = tptr = (uchar_t *)(ipha + 1); 484 noop_keep = B_TRUE; 485 while (remlen > 0) { 486 oval = fptr[IPOPT_OPTVAL]; 487 488 /* terminate on end of list */ 489 if (oval == IPOPT_EOL) 490 break; 491 492 /* 493 * Delete any no-ops following a deleted option, at least up 494 * to a 4 octet alignment; copy others. 495 */ 496 if (oval == IPOPT_NOP) { 497 if (((fptr - (uchar_t *)ipha) & 3) == 0) 498 noop_keep = B_TRUE; 499 if (noop_keep) 500 *tptr++ = oval; 501 fptr++; 502 remlen--; 503 continue; 504 } 505 506 /* stop on corrupted list; just do nothing. */ 507 if (remlen < 2) 508 return (0); 509 olen = fptr[IPOPT_OLEN]; 510 if (olen < 2 || olen > remlen) 511 return (0); 512 513 /* skip over security options to delete them */ 514 if (oval == IPOPT_COMSEC || oval == IPOPT_SECURITY) { 515 noop_keep = B_FALSE; 516 fptr += olen; 517 remlen -= olen; 518 continue; 519 } 520 521 /* copy the rest */ 522 noop_keep = B_TRUE; 523 if (tptr != fptr) 524 ovbcopy(fptr, tptr, olen); 525 fptr += olen; 526 tptr += olen; 527 remlen -= olen; 528 } 529 530 fptr += remlen; 531 532 /* figure how much padding we'll need for header alignment */ 533 olen = (tptr - (uchar_t *)ipha) & 3; 534 if (olen > 0) { 535 olen = 4 - olen; 536 /* pad with end-of-list */ 537 bzero(tptr, olen); 538 tptr += olen; 539 } 540 541 /* slide back the headers that follow and update the IP header */ 542 delta = fptr - tptr; 543 if (delta != 0) { 544 ovbcopy(fptr, tptr, ((uchar_t *)ipha + buflen) - fptr); 545 ipha->ipha_version_and_hdr_length -= delta / 4; 546 } 547 return (-delta); 548 } 549 550 /* 551 * Insert the option in 'optbuf' into the IP header pointed to by 'ipha', and 552 * move the data following the IP header (up to buflen) to accomodate the new 553 * option. Assumes that up to IP_MAX_OPT_LENGTH bytes are available (in total) 554 * for IP options. Returns the number of bytes actually inserted, or -1 if the 555 * option cannot be inserted. (Note that negative return values are possible 556 * when noops must be compressed, and that only -1 indicates error. Successful 557 * return value is always evenly divisible by 4, by definition.) 558 */ 559 int 560 tsol_prepend_option(uchar_t *optbuf, ipha_t *ipha, int buflen) 561 { 562 int remlen, padding, lastpad, totlen; 563 int oval, olen; 564 int delta; 565 uchar_t *optr; 566 uchar_t tempopt[IP_MAX_OPT_LENGTH], *toptr; 567 568 if (optbuf[IPOPT_OPTVAL] == IPOPT_EOL || 569 optbuf[IPOPT_OPTVAL] == IPOPT_NOP || 570 optbuf[IPOPT_OLEN] == 0) 571 return (0); 572 573 ASSERT(optbuf[IPOPT_OLEN] >= 2 && 574 optbuf[IPOPT_OLEN] <= IP_MAX_OPT_LENGTH); 575 576 /* first find the real (unpadded) length of the existing options */ 577 remlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH; 578 padding = totlen = lastpad = 0; 579 optr = (uchar_t *)(ipha + 1); 580 while (remlen > 0) { 581 oval = optr[IPOPT_OPTVAL]; 582 583 /* stop at end of list */ 584 if (oval == IPOPT_EOL) 585 break; 586 587 /* skip no-ops, noting that length byte isn't present */ 588 if (oval == IPOPT_NOP) { 589 optr++; 590 padding++; 591 lastpad++; 592 totlen++; 593 remlen--; 594 continue; 595 } 596 597 /* give up on a corrupted list; report failure */ 598 if (remlen < 2) 599 return (-1); 600 olen = optr[IPOPT_OLEN]; 601 if (olen < 2 || olen > remlen) 602 return (-1); 603 604 lastpad = 0; 605 optr += olen; 606 totlen += olen; 607 remlen -= olen; 608 } 609 610 /* completely ignore any trailing padding */ 611 totlen -= lastpad; 612 padding -= lastpad; 613 614 /* 615 * If some sort of inter-option alignment was present, try to preserve 616 * that alignment. If alignment pushes us out past the maximum, then 617 * discard it and try to compress to fit. (We just "assume" that any 618 * padding added was attempting to get 32 bit alignment. If that's 619 * wrong, that's just too bad.) 620 */ 621 if (padding > 0) { 622 olen = (optbuf[IPOPT_OLEN] + 3) & ~3; 623 if (olen + totlen > IP_MAX_OPT_LENGTH) { 624 totlen -= padding; 625 if (olen + totlen > IP_MAX_OPT_LENGTH) 626 return (-1); 627 padding = 0; 628 } 629 } 630 631 /* 632 * Since we may need to compress or expand the option list, we write to 633 * a temporary buffer and then copy the results back to the IP header. 634 */ 635 toptr = tempopt; 636 637 /* compute actual option to insert */ 638 olen = optbuf[IPOPT_OLEN]; 639 bcopy(optbuf, toptr, olen); 640 toptr += olen; 641 if (padding > 0) { 642 while ((olen & 3) != 0) { 643 *toptr++ = IPOPT_NOP; 644 olen++; 645 } 646 } 647 648 /* copy over the existing options */ 649 optr = (uchar_t *)(ipha + 1); 650 while (totlen > 0) { 651 oval = optr[IPOPT_OPTVAL]; 652 653 /* totlen doesn't include end-of-list marker */ 654 ASSERT(oval != IPOPT_EOL); 655 656 /* handle no-ops; copy if desired, ignore otherwise */ 657 if (oval == IPOPT_NOP) { 658 if (padding > 0) { 659 /* note: cannot overflow due to checks above */ 660 ASSERT(toptr < tempopt + IP_MAX_OPT_LENGTH); 661 *toptr++ = oval; 662 } 663 optr++; 664 totlen--; 665 continue; 666 } 667 668 /* list cannot be corrupt at this point */ 669 ASSERT(totlen >= 2); 670 olen = optr[IPOPT_OLEN]; 671 ASSERT(olen >= 2 && olen <= totlen); 672 673 /* cannot run out of room due to tests above */ 674 ASSERT(toptr + olen <= tempopt + IP_MAX_OPT_LENGTH); 675 676 bcopy(optr, toptr, olen); 677 optr += olen; 678 toptr += olen; 679 totlen -= olen; 680 } 681 682 /* figure how much padding we'll need for header alignment */ 683 olen = (toptr - tempopt) & 3; 684 if (olen > 0) { 685 olen = 4 - olen; 686 ASSERT(toptr + olen <= tempopt + IP_MAX_OPT_LENGTH); 687 /* pad with end-of-list value */ 688 bzero(toptr, olen); 689 toptr += olen; 690 } 691 692 /* move the headers as needed and update IP header */ 693 olen = (toptr - tempopt) + IP_SIMPLE_HDR_LENGTH; 694 remlen = IPH_HDR_LENGTH(ipha); 695 delta = olen - remlen; 696 if (delta != 0) { 697 ovbcopy((uchar_t *)ipha + remlen, (uchar_t *)ipha + olen, 698 buflen - remlen); 699 ipha->ipha_version_and_hdr_length += delta / 4; 700 } 701 702 /* slap in the new options */ 703 bcopy(tempopt, ipha + 1, olen - IP_SIMPLE_HDR_LENGTH); 704 705 return (delta); 706 } 707 708 /* 709 * tsol_check_label() 710 * 711 * This routine computes the IP label that should be on the packet based on the 712 * connection and destination information. If the label is there, it returns 713 * zero, so the caller knows that the label is syncronized, and further calls 714 * are not required. If the label isn't right, then the right one is inserted. 715 * 716 * The packet's header is clear before entering IPsec's engine. 717 * 718 * Returns: 719 * 0 Label on packet (was|is now) correct 720 * EACCES The packet failed the remote host accreditation. 721 * ENOMEM Memory allocation failure. 722 * EINVAL Label cannot be computed 723 */ 724 int 725 tsol_check_label(const cred_t *credp, mblk_t **mpp, boolean_t isexempt, 726 ip_stack_t *ipst, pid_t pid) 727 { 728 mblk_t *mp = *mpp; 729 ipha_t *ipha; 730 cred_t *effective_cred = NULL; 731 uchar_t opt_storage[IP_MAX_OPT_LENGTH]; 732 uint_t hlen; 733 uint_t sec_opt_len; 734 uchar_t *optr; 735 int delta_remove = 0, delta_add, adjust; 736 int retv; 737 738 opt_storage[IPOPT_OPTVAL] = 0; 739 740 ipha = (ipha_t *)mp->b_rptr; 741 742 /* 743 * Verify the destination is allowed to receive packets at 744 * the security label of the message data. check_dest() 745 * may create a new effective cred with a modified label 746 * or label flags. Apply any such cred to the message block 747 * for use in future routing decisions. 748 */ 749 retv = tsol_check_dest(credp, &ipha->ipha_dst, IPV4_VERSION, 750 isexempt, &effective_cred); 751 if (retv != 0) 752 return (retv); 753 754 /* 755 * Calculate the security label to be placed in the text 756 * of the message (if any). 757 */ 758 if (effective_cred != NULL) { 759 if ((retv = tsol_compute_label(effective_cred, 760 ipha->ipha_dst, opt_storage, ipst)) != 0) { 761 crfree(effective_cred); 762 return (retv); 763 } 764 mblk_setcred(mp, effective_cred, pid); 765 crfree(effective_cred); 766 } else { 767 if ((retv = tsol_compute_label(credp, 768 ipha->ipha_dst, opt_storage, ipst)) != 0) { 769 return (retv); 770 } 771 } 772 773 optr = (uchar_t *)(ipha + 1); 774 hlen = IPH_HDR_LENGTH(ipha) - IP_SIMPLE_HDR_LENGTH; 775 sec_opt_len = opt_storage[IPOPT_OLEN]; 776 777 if (hlen >= sec_opt_len) { 778 /* If no option is supposed to be there, make sure it's not */ 779 if (sec_opt_len == 0 && hlen > 0 && 780 optr[IPOPT_OPTVAL] != IPOPT_COMSEC && 781 optr[IPOPT_OPTVAL] != IPOPT_SECURITY) 782 return (0); 783 /* if the option is there, it's always first */ 784 if (sec_opt_len != 0 && 785 bcmp(opt_storage, optr, sec_opt_len) == 0) 786 return (0); 787 } 788 789 /* 790 * If there is an option there, then it must be the wrong one; delete. 791 */ 792 if (hlen > 0) { 793 delta_remove = tsol_remove_secopt(ipha, MBLKL(mp)); 794 mp->b_wptr += delta_remove; 795 } 796 797 /* Make sure we have room for the worst-case addition */ 798 hlen = IPH_HDR_LENGTH(ipha) + opt_storage[IPOPT_OLEN]; 799 hlen = (hlen + 3) & ~3; 800 if (hlen > IP_MAX_HDR_LENGTH) 801 hlen = IP_MAX_HDR_LENGTH; 802 hlen -= IPH_HDR_LENGTH(ipha); 803 if (mp->b_wptr + hlen > mp->b_datap->db_lim) { 804 int copylen; 805 mblk_t *new_mp; 806 807 /* allocate enough to be meaningful, but not *too* much */ 808 copylen = MBLKL(mp); 809 if (copylen > 256) 810 copylen = 256; 811 new_mp = allocb_tmpl(hlen + copylen + 812 (mp->b_rptr - mp->b_datap->db_base), mp); 813 if (new_mp == NULL) 814 return (ENOMEM); 815 816 /* keep the bias */ 817 new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base; 818 new_mp->b_wptr = new_mp->b_rptr + copylen; 819 bcopy(mp->b_rptr, new_mp->b_rptr, copylen); 820 new_mp->b_cont = mp; 821 if ((mp->b_rptr += copylen) >= mp->b_wptr) { 822 new_mp->b_cont = mp->b_cont; 823 freeb(mp); 824 } 825 *mpp = mp = new_mp; 826 ipha = (ipha_t *)mp->b_rptr; 827 } 828 829 delta_add = tsol_prepend_option(opt_storage, ipha, MBLKL(mp)); 830 if (delta_add == -1) 831 goto param_prob; 832 833 ASSERT((mp->b_wptr + delta_add) <= DB_LIM(mp)); 834 mp->b_wptr += delta_add; 835 836 adjust = delta_remove + delta_add; 837 adjust += ntohs(ipha->ipha_length); 838 ipha->ipha_length = htons(adjust); 839 840 return (0); 841 842 param_prob: 843 return (EINVAL); 844 } 845 846 /* 847 * IPv6 HopOpt extension header for the label option layout: 848 * - One octet giving the type of the 'next extension header' 849 * - Header extension length in 8-byte words, not including the 850 * 1st 8 bytes, but including any pad bytes at the end. 851 * Eg. A value of 2 means 16 bytes not including the 1st 8 bytes. 852 * - Followed by TLV encoded IPv6 label option. Option layout is 853 * * One octet, IP6OPT_LS 854 * * One octet option length in bytes of the option data following 855 * the length, but not including any pad bytes at the end. 856 * * Four-octet DOI (IP6LS_DOI_V4) 857 * * One octet suboption, IP6LS_TT_V4 858 * * One octet suboption length in bytes of the suboption 859 * following the suboption length, including the suboption 860 * header length, but not including any pad bytes at the end. 861 * - Pad to make the extension header a multiple of 8 bytes. 862 * 863 * This function returns the contents of 'IPv6 option structure' in the above. 864 * i.e starting from the IP6OPT_LS but not including the pad at the end. 865 * The user must prepend two octets (either padding or next header / length) 866 * and append padding out to the next 8 octet boundary. 867 */ 868 int 869 tsol_compute_label_v6(const cred_t *credp, const in6_addr_t *dst, 870 uchar_t *opt_storage, ip_stack_t *ipst) 871 { 872 ts_label_t *tsl; 873 uint_t sec_opt_len; 874 uint32_t doi; 875 zoneid_t zoneid, ip_zoneid; 876 ire_t *ire, *sire; 877 tsol_ire_gw_secattr_t *attrp; 878 879 ASSERT(credp != NULL); 880 881 if (ip6opt_ls == 0) 882 return (EINVAL); 883 884 if (opt_storage != NULL) 885 opt_storage[IPOPT_OLEN] = 0; 886 887 if ((tsl = crgetlabel(credp)) == NULL) 888 return (0); 889 890 /* Always pass multicast */ 891 if (IN6_IS_ADDR_MULTICAST(dst)) 892 return (0); 893 894 zoneid = crgetzoneid(credp); 895 896 /* 897 * Fill in a V6 label. If a new format is added here, make certain 898 * that the maximum size of this label is reflected in sys/tsol/tnet.h 899 * as TSOL_MAX_IPV6_OPTION. 900 */ 901 if (tsl->tsl_flags & TSLF_UNLABELED) { 902 /* 903 * The destination is unlabeled. Only add a label if the 904 * destination is not broadcast/local/loopback address, 905 * the destination is not on the same subnet, and the 906 * next-hop gateway is labeled. 907 * 908 * For exclusive stacks we set the zoneid to zero to 909 * operate as if we are in the global zone when 910 * performing IRE lookups and conn_t comparisons. 911 */ 912 if (ipst->ips_netstack->netstack_stackid != GLOBAL_NETSTACKID) 913 ip_zoneid = GLOBAL_ZONEID; 914 else 915 ip_zoneid = zoneid; 916 917 sire = NULL; 918 ire = ire_cache_lookup_v6(dst, ip_zoneid, tsl, ipst); 919 920 if (ire != NULL && (ire->ire_type & (IRE_LOCAL | 921 IRE_LOOPBACK | IRE_INTERFACE)) != 0) { 922 IRE_REFRELE(ire); 923 return (0); 924 } else if (ire == NULL) { 925 ire = ire_ftable_lookup_v6(dst, NULL, NULL, 0, NULL, 926 &sire, ip_zoneid, 0, tsl, (MATCH_IRE_RECURSIVE | 927 MATCH_IRE_DEFAULT | MATCH_IRE_SECATTR), ipst); 928 } 929 930 /* no route to destination */ 931 if (ire == NULL) { 932 DTRACE_PROBE3( 933 tx__tnopt__log__info__labeling__routedst__v6, 934 char *, "No route to unlabeled dest ip6(1) with " 935 "creds(2).", in6_addr_t *, dst, cred_t *, credp); 936 return (EHOSTUNREACH); 937 } 938 939 /* 940 * Prefix IRE from f-table lookup means that the destination 941 * is not directly connected; check the next-hop attributes. 942 */ 943 if (sire != NULL) { 944 ASSERT(ire != NULL); 945 IRE_REFRELE(ire); 946 ire = sire; 947 } 948 949 /* 950 * Return now if next hop gateway is unlabeled. There is 951 * no need to generate a CIPSO option for this message. 952 */ 953 attrp = ire->ire_gw_secattr; 954 if (attrp == NULL || attrp->igsa_rhc == NULL || 955 attrp->igsa_rhc->rhc_tpc->tpc_tp.host_type == UNLABELED) { 956 IRE_REFRELE(ire); 957 return (0); 958 } 959 IRE_REFRELE(ire); 960 } 961 962 /* compute the CIPSO option */ 963 if (opt_storage != NULL) 964 opt_storage += 8; 965 sec_opt_len = tsol2cipso_tt1(&tsl->tsl_label, opt_storage, 966 tsl->tsl_doi); 967 968 if (sec_opt_len == 0) { 969 DTRACE_PROBE3(tx__tnopt__log__error__labeling__lostops__v6, 970 char *, "options lack length for dest ip6(1) with " 971 "creds(2).", in6_addr_t *, dst, cred_t *, credp); 972 return (EINVAL); 973 } 974 975 if (opt_storage == NULL) 976 return (0); 977 978 if (sec_opt_len < IP_MAX_OPT_LENGTH) 979 opt_storage[sec_opt_len] = IPOPT_EOL; 980 981 /* 982 * Just in case the option length is odd, round it up to the next even 983 * multiple. The IPv6 option definition doesn't like odd numbers for 984 * some reason. 985 * 986 * Length in the overall option header (IP6OPT_LS) does not include the 987 * option header itself, but the length in the suboption does include 988 * the suboption header. Thus, when there's just one suboption, the 989 * length in the option header is the suboption length plus 4 (for the 990 * DOI value). 991 */ 992 opt_storage[-2] = IP6LS_TT_V4; 993 opt_storage[-1] = (sec_opt_len + 2 + 1) & ~1; 994 opt_storage[-8] = ip6opt_ls; 995 opt_storage[-7] = opt_storage[-1] + 4; 996 doi = htons(IP6LS_DOI_V4); 997 bcopy(&doi, opt_storage - 6, 4); 998 999 return (0); 1000 } 1001 1002 /* 1003 * Locate the start of the IP6OPT_LS label option and return it. 1004 * Also return the start of the next non-pad option in after_secoptp. 1005 * Usually the label option is the first option at least when packets 1006 * are generated, but for generality we don't assume that on received packets. 1007 */ 1008 uchar_t * 1009 tsol_find_secopt_v6( 1010 const uchar_t *ip6hbh, /* Start of the hop-by-hop extension header */ 1011 uint_t hbhlen, /* Length of the hop-by-hop extension header */ 1012 uchar_t **after_secoptp, /* Non-pad option following the label option */ 1013 boolean_t *hbh_needed) /* Is hop-by-hop hdr needed w/o label */ 1014 { 1015 uint_t optlen; 1016 uint_t optused; 1017 const uchar_t *optptr; 1018 uchar_t opt_type; 1019 const uchar_t *secopt = NULL; 1020 1021 *hbh_needed = B_FALSE; 1022 *after_secoptp = NULL; 1023 optlen = hbhlen - 2; 1024 optptr = ip6hbh + 2; 1025 while (optlen != 0) { 1026 opt_type = *optptr; 1027 if (opt_type == IP6OPT_PAD1) { 1028 optptr++; 1029 optlen--; 1030 continue; 1031 } 1032 if (optlen == 1) 1033 break; 1034 optused = 2 + optptr[1]; 1035 if (optused > optlen) 1036 break; 1037 /* 1038 * if we get here, ip6opt_ls can 1039 * not be 0 because it will always 1040 * match the IP6OPT_PAD1 above. 1041 * Therefore ip6opt_ls == 0 forces 1042 * this test to always fail here. 1043 */ 1044 if (opt_type == ip6opt_ls) 1045 secopt = optptr; 1046 else switch (opt_type) { 1047 case IP6OPT_PADN: 1048 break; 1049 default: 1050 /* 1051 * There is at least 1 option other than 1052 * the label option. So the hop-by-hop header is needed 1053 */ 1054 *hbh_needed = B_TRUE; 1055 if (secopt != NULL) { 1056 *after_secoptp = (uchar_t *)optptr; 1057 return ((uchar_t *)secopt); 1058 } 1059 break; 1060 } 1061 optlen -= optused; 1062 optptr += optused; 1063 } 1064 return ((uchar_t *)secopt); 1065 } 1066 1067 /* 1068 * Remove the label option from the hop-by-hop options header if it exists. 1069 * 'buflen' is the total length of the packet typically b_wptr - b_rptr. 1070 * Header and data following the label option that is deleted are copied 1071 * (i.e. slid backward) to the right position, and returns the number 1072 * of bytes removed (as zero or negative number.) 1073 */ 1074 int 1075 tsol_remove_secopt_v6(ip6_t *ip6h, int buflen) 1076 { 1077 uchar_t *ip6hbh; /* hop-by-hop header */ 1078 uint_t hbhlen; /* hop-by-hop extension header length */ 1079 uchar_t *secopt = NULL; 1080 uchar_t *after_secopt; 1081 uint_t pad; 1082 uint_t delta; 1083 boolean_t hbh_needed; 1084 1085 /* 1086 * hop-by-hop extension header must appear first, if it does not 1087 * exist, there is no label option. 1088 */ 1089 if (ip6h->ip6_nxt != IPPROTO_HOPOPTS) 1090 return (0); 1091 1092 ip6hbh = (uchar_t *)&ip6h[1]; 1093 hbhlen = (ip6hbh[1] + 1) << 3; 1094 /* 1095 * Locate the start of the label option if it exists and the end 1096 * of the label option including pads if any. 1097 */ 1098 secopt = tsol_find_secopt_v6(ip6hbh, hbhlen, &after_secopt, 1099 &hbh_needed); 1100 if (secopt == NULL) 1101 return (0); 1102 if (!hbh_needed) { 1103 uchar_t next_hdr; 1104 /* 1105 * The label option was the only option in the hop-by-hop 1106 * header. We don't need the hop-by-hop header itself any 1107 * longer. 1108 */ 1109 next_hdr = ip6hbh[0]; 1110 ovbcopy(ip6hbh + hbhlen, ip6hbh, 1111 buflen - (IPV6_HDR_LEN + hbhlen)); 1112 ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - hbhlen); 1113 ip6h->ip6_nxt = next_hdr; 1114 return (-hbhlen); 1115 } 1116 1117 if (after_secopt == NULL) { 1118 /* There is no option following the label option */ 1119 after_secopt = ip6hbh + hbhlen; 1120 } 1121 1122 /* 1123 * After deleting the label option, we need to slide the headers 1124 * and data back, while still maintaining the same alignment (module 8) 1125 * for the other options. So we slide the headers and data back only 1126 * by an integral multiple of 8 bytes, and fill the remaining bytes 1127 * with pads. 1128 */ 1129 delta = after_secopt - secopt; 1130 pad = delta % 8; 1131 if (pad == 1) { 1132 secopt[0] = IP6OPT_PAD1; 1133 } else if (pad > 1) { 1134 secopt[0] = IP6OPT_PADN; 1135 secopt[1] = pad - 2; 1136 if (pad > 2) 1137 bzero(&secopt[2], pad - 2); 1138 } 1139 secopt += pad; 1140 delta -= pad; 1141 ovbcopy(after_secopt, secopt, 1142 (uchar_t *)ip6h + buflen - after_secopt); 1143 ip6hbh[1] -= delta/8; 1144 ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - delta); 1145 1146 return (-delta); 1147 } 1148 1149 /* 1150 * 'optbuf' contains a CIPSO label embedded in an IPv6 hop-by-hop option, 1151 * starting with the IP6OPT_LS option type. The format of this hop-by-hop 1152 * option is described in the block comment above tsol_compute_label_v6. 1153 * This function prepends this hop-by-hop option before any other hop-by-hop 1154 * options in the hop-by-hop header if one already exists, else a new 1155 * hop-by-hop header is created and stuffed into the packet following 1156 * the IPv6 header. 'buflen' is the total length of the packet i.e. 1157 * b_wptr - b_rptr. The caller ensures that there is enough space for the 1158 * extra option being added. Header and data following the position where 1159 * the label option is inserted are copied (i.e. slid forward) to the right 1160 * position. 1161 */ 1162 int 1163 tsol_prepend_option_v6(uchar_t *optbuf, ip6_t *ip6h, int buflen) 1164 { 1165 /* 1166 * rawlen is the length of the label option in bytes, not including 1167 * any pads, starting from the IP6OPT_LS (option type) byte. 1168 */ 1169 uint_t rawlen; 1170 1171 uint_t optlen; /* rawlen rounded to an 8 byte multiple */ 1172 uchar_t *ip6hbh; /* start of the hop-by-hop extension header */ 1173 uint_t hbhlen; /* Length of the hop-by-hop extension header */ 1174 uint_t pad_len; 1175 uchar_t *pad_position; 1176 int delta; /* Actual number of bytes inserted */ 1177 1178 rawlen = optbuf[1] + 2; /* Add 2 for the option type, option length */ 1179 ip6hbh = (uchar_t *)&ip6h[1]; 1180 if (ip6h->ip6_nxt == IPPROTO_HOPOPTS) { 1181 /* 1182 * There is a hop-by-hop header present already. In order to 1183 * preserve the alignment of the other options at the existing 1184 * value (modulo 8) we need to pad the label option to a 1185 * multiple of 8 bytes before prepending it to the other 1186 * options. Slide the extension headers and data forward to 1187 * accomodate the label option at the start of the hop-by-hop 1188 * header 1189 */ 1190 delta = optlen = (rawlen + 7) & ~7; 1191 pad_len = optlen - rawlen; 1192 pad_position = ip6hbh + 2 + rawlen; 1193 ovbcopy(ip6hbh + 2, ip6hbh + 2 + optlen, 1194 buflen - (IPV6_HDR_LEN + 2)); 1195 /* 1196 * Bump up the hop-by-hop extension header length by 1197 * the number of 8-byte words added 1198 */ 1199 optlen >>= 3; 1200 if (ip6hbh[1] + optlen > 255) 1201 return (-1); 1202 ip6hbh[1] += optlen; 1203 } else { 1204 /* 1205 * There is no hop-by-hop header in the packet. Construct a 1206 * new Hop-by-hop extension header (a multiple of 8 bytes). 1207 * Slide any other extension headers and data forward to 1208 * accomodate this hop-by-hop header 1209 */ 1210 delta = hbhlen = (2 + rawlen + 7) & ~7; /* +2 for nxthdr, len */ 1211 pad_len = hbhlen - (2 + rawlen); 1212 pad_position = ip6hbh + 2 + rawlen; 1213 ovbcopy(ip6hbh, ip6hbh + hbhlen, buflen - IPV6_HDR_LEN); 1214 ip6hbh[0] = ip6h->ip6_nxt; 1215 /* 1216 * hop-by-hop extension header length in 8-byte words, not 1217 * including the 1st 8 bytes of the hop-by-hop header. 1218 */ 1219 ip6hbh[1] = (hbhlen >> 3) - 1; 1220 ip6h->ip6_nxt = IPPROTO_HOPOPTS; 1221 } 1222 /* 1223 * Copy the label option into the hop-by-hop header and insert any 1224 * needed pads 1225 */ 1226 bcopy(optbuf, ip6hbh + 2, rawlen); 1227 if (pad_len == 1) { 1228 pad_position[0] = IP6OPT_PAD1; 1229 } else if (pad_len > 1) { 1230 pad_position[0] = IP6OPT_PADN; 1231 pad_position[1] = pad_len - 2; 1232 if (pad_len > 2) 1233 bzero(pad_position + 2, pad_len - 2); 1234 } 1235 ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) + delta); 1236 return (delta); 1237 } 1238 1239 /* 1240 * tsol_check_label_v6() 1241 * 1242 * This routine computes the IP label that should be on the packet based on the 1243 * connection and destination information. It's called only by the IP 1244 * forwarding logic, because all internal modules atop IP know how to generate 1245 * their own labels. 1246 * 1247 * Returns: 1248 * 0 Label on packet was already correct 1249 * EACCES The packet failed the remote host accreditation. 1250 * ENOMEM Memory allocation failure. 1251 */ 1252 int 1253 tsol_check_label_v6(const cred_t *credp, mblk_t **mpp, boolean_t isexempt, 1254 ip_stack_t *ipst, pid_t pid) 1255 { 1256 mblk_t *mp = *mpp; 1257 ip6_t *ip6h; 1258 cred_t *effective_cred; 1259 /* 1260 * Label option length is limited to IP_MAX_OPT_LENGTH for 1261 * symmetry with IPv4. Can be relaxed if needed 1262 */ 1263 uchar_t opt_storage[TSOL_MAX_IPV6_OPTION]; 1264 uint_t hlen; 1265 uint_t sec_opt_len; /* label option length not including type, len */ 1266 int delta_remove = 0, delta_add; 1267 int retv; 1268 uchar_t *after_secopt; 1269 uchar_t *secopt = NULL; 1270 uchar_t *ip6hbh; 1271 uint_t hbhlen; 1272 boolean_t hbh_needed; 1273 1274 /* 1275 * Verify the destination is allowed to receive packets at 1276 * the security label of the message data. check_dest() 1277 * may create a new effective cred with a modified label 1278 * or label flags. Apply any such cred to the message block 1279 * for use in future routing decisions. 1280 */ 1281 ip6h = (ip6_t *)mp->b_rptr; 1282 retv = tsol_check_dest(credp, &ip6h->ip6_dst, IPV6_VERSION, 1283 isexempt, &effective_cred); 1284 if (retv != 0) 1285 return (retv); 1286 1287 /* 1288 * Calculate the security label to be placed in the text 1289 * of the message (if any). 1290 */ 1291 if (effective_cred != NULL) { 1292 if ((retv = tsol_compute_label_v6(effective_cred, 1293 &ip6h->ip6_dst, opt_storage, ipst)) != 0) { 1294 crfree(effective_cred); 1295 return (retv); 1296 } 1297 mblk_setcred(mp, effective_cred, pid); 1298 crfree(effective_cred); 1299 } else { 1300 if ((retv = tsol_compute_label_v6(credp, 1301 &ip6h->ip6_dst, opt_storage, ipst)) != 0) 1302 return (retv); 1303 } 1304 1305 sec_opt_len = opt_storage[1]; 1306 1307 if (ip6h->ip6_nxt == IPPROTO_HOPOPTS) { 1308 ip6hbh = (uchar_t *)&ip6h[1]; 1309 hbhlen = (ip6hbh[1] + 1) << 3; 1310 secopt = tsol_find_secopt_v6(ip6hbh, hbhlen, &after_secopt, 1311 &hbh_needed); 1312 } 1313 1314 if (sec_opt_len == 0 && secopt == NULL) { 1315 /* 1316 * The packet is not supposed to have a label, and it 1317 * does not have one currently 1318 */ 1319 return (0); 1320 } 1321 if (secopt != NULL && sec_opt_len != 0 && 1322 (bcmp(opt_storage, secopt, sec_opt_len + 2) == 0)) { 1323 /* The packet has the correct label already */ 1324 return (0); 1325 } 1326 1327 /* 1328 * If there is an option there, then it must be the wrong one; delete. 1329 */ 1330 if (secopt != NULL) { 1331 delta_remove = tsol_remove_secopt_v6(ip6h, MBLKL(mp)); 1332 mp->b_wptr += delta_remove; 1333 } 1334 1335 /* 1336 * Make sure we have room for the worst-case addition. Add 2 bytes for 1337 * the hop-by-hop ext header's next header and length fields. Add 1338 * another 2 bytes for the label option type, len and then round 1339 * up to the next 8-byte multiple. 1340 */ 1341 hlen = (4 + sec_opt_len + 7) & ~7; 1342 if (mp->b_wptr + hlen > mp->b_datap->db_lim) { 1343 int copylen; 1344 mblk_t *new_mp; 1345 uint16_t hdr_len; 1346 1347 hdr_len = ip_hdr_length_v6(mp, ip6h); 1348 /* 1349 * Allocate enough to be meaningful, but not *too* much. 1350 * Also all the IPv6 extension headers must be in the same mblk 1351 */ 1352 copylen = MBLKL(mp); 1353 if (copylen > 256) 1354 copylen = 256; 1355 if (copylen < hdr_len) 1356 copylen = hdr_len; 1357 new_mp = allocb_tmpl(hlen + copylen + 1358 (mp->b_rptr - mp->b_datap->db_base), mp); 1359 if (new_mp == NULL) 1360 return (ENOMEM); 1361 1362 /* keep the bias */ 1363 new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base; 1364 new_mp->b_wptr = new_mp->b_rptr + copylen; 1365 bcopy(mp->b_rptr, new_mp->b_rptr, copylen); 1366 new_mp->b_cont = mp; 1367 if ((mp->b_rptr += copylen) >= mp->b_wptr) { 1368 new_mp->b_cont = mp->b_cont; 1369 freeb(mp); 1370 } 1371 *mpp = mp = new_mp; 1372 ip6h = (ip6_t *)mp->b_rptr; 1373 } 1374 1375 delta_add = tsol_prepend_option_v6(opt_storage, ip6h, MBLKL(mp)); 1376 if (delta_add == -1) 1377 goto param_prob; 1378 1379 ASSERT(mp->b_wptr + delta_add <= DB_LIM(mp)); 1380 mp->b_wptr += delta_add; 1381 1382 return (0); 1383 1384 param_prob: 1385 return (EINVAL); 1386 } 1387 1388 /* 1389 * Update the given IPv6 "sticky options" structure to contain the provided 1390 * label, which is encoded as an IPv6 option. Existing label is removed if 1391 * necessary, and storage is allocated/freed/resized. 1392 * 1393 * Returns 0 on success, errno on failure. 1394 */ 1395 int 1396 tsol_update_sticky(ip6_pkt_t *ipp, uint_t *labellen, const uchar_t *labelopt) 1397 { 1398 int rawlen, optlen, newlen; 1399 uchar_t *newopts; 1400 1401 /* 1402 * rawlen is the size of the IPv6 label to be inserted from labelopt. 1403 * optlen is the total length of that option, including any necessary 1404 * headers and padding. newlen is the new size of the total hop-by-hop 1405 * options buffer, including user options. 1406 */ 1407 ASSERT(*labellen <= ipp->ipp_hopoptslen); 1408 ASSERT((ipp->ipp_hopopts == NULL && ipp->ipp_hopoptslen == 0) || 1409 (ipp->ipp_hopopts != NULL && ipp->ipp_hopoptslen != 0)); 1410 1411 if ((rawlen = labelopt[1]) != 0) { 1412 rawlen += 2; /* add in header size */ 1413 optlen = (2 + rawlen + 7) & ~7; 1414 } else { 1415 optlen = 0; 1416 } 1417 newlen = ipp->ipp_hopoptslen + optlen - *labellen; 1418 if (newlen == 0 && ipp->ipp_hopopts != NULL) { 1419 /* Deleting all existing hop-by-hop options */ 1420 kmem_free(ipp->ipp_hopopts, ipp->ipp_hopoptslen); 1421 ipp->ipp_hopopts = NULL; 1422 ipp->ipp_fields &= ~IPPF_HOPOPTS; 1423 } else if (optlen != *labellen) { 1424 /* If the label not same size as last time, then reallocate */ 1425 if (newlen > IP6_MAX_OPT_LENGTH) 1426 return (EHOSTUNREACH); 1427 newopts = kmem_alloc(newlen, KM_NOSLEEP); 1428 if (newopts == NULL) 1429 return (ENOMEM); 1430 /* 1431 * If the user has hop-by-hop stickyoptions set, then copy his 1432 * options in after the security label. 1433 */ 1434 if (ipp->ipp_hopoptslen > *labellen) { 1435 bcopy(ipp->ipp_hopopts + *labellen, newopts + optlen, 1436 ipp->ipp_hopoptslen - *labellen); 1437 /* 1438 * Stomp out any header gunk here - this was the 1439 * previous next-header and option length field. 1440 */ 1441 newopts[optlen] = IP6OPT_PADN; 1442 newopts[optlen + 1] = 0; 1443 } 1444 if (ipp->ipp_hopopts != NULL) 1445 kmem_free(ipp->ipp_hopopts, ipp->ipp_hopoptslen); 1446 ipp->ipp_hopopts = (ip6_hbh_t *)newopts; 1447 } 1448 ipp->ipp_hopoptslen = newlen; 1449 *labellen = optlen; 1450 1451 newopts = (uchar_t *)ipp->ipp_hopopts; 1452 1453 /* If there are any options, then fix up reported length */ 1454 if (newlen > 0) { 1455 newopts[1] = (newlen + 7) / 8 - 1; 1456 ipp->ipp_fields |= IPPF_HOPOPTS; 1457 } 1458 1459 /* If there's a label, then insert it now */ 1460 if (optlen > 0) { 1461 /* skip next-header and length fields */ 1462 newopts += 2; 1463 bcopy(labelopt, newopts, rawlen); 1464 newopts += rawlen; 1465 /* make sure padding comes out right */ 1466 optlen -= 2 + rawlen; 1467 if (optlen == 1) { 1468 newopts[0] = IP6OPT_PAD1; 1469 } else if (optlen > 1) { 1470 newopts[0] = IP6OPT_PADN; 1471 optlen -= 2; 1472 newopts[1] = optlen; 1473 if (optlen > 0) 1474 bzero(newopts + 2, optlen); 1475 } 1476 } 1477 return (0); 1478 } 1479 1480 int 1481 tsol_update_options(uchar_t **opts, uint_t *totlen, uint_t *labellen, 1482 const uchar_t *labelopt) 1483 { 1484 int optlen, newlen; 1485 uchar_t *newopts; 1486 1487 optlen = (labelopt[IPOPT_OLEN] + 3) & ~3; 1488 newlen = *totlen + optlen - *labellen; 1489 if (optlen > *labellen) { 1490 if (newlen > IP_MAX_OPT_LENGTH) 1491 return (EHOSTUNREACH); 1492 newopts = (uchar_t *)mi_alloc(newlen, BPRI_HI); 1493 if (newopts == NULL) 1494 return (ENOMEM); 1495 if (*totlen > *labellen) { 1496 bcopy(*opts + *labellen, newopts + optlen, 1497 *totlen - *labellen); 1498 } 1499 if (*opts != NULL) 1500 mi_free((char *)*opts); 1501 *opts = newopts; 1502 } else if (optlen < *labellen) { 1503 if (newlen == 0 && *opts != NULL) { 1504 mi_free((char *)*opts); 1505 *opts = NULL; 1506 } 1507 if (*totlen > *labellen) { 1508 ovbcopy(*opts + *labellen, *opts + optlen, 1509 *totlen - *labellen); 1510 } 1511 } 1512 *totlen = newlen; 1513 *labellen = optlen; 1514 if (optlen > 0) { 1515 newopts = *opts; 1516 bcopy(labelopt, newopts, optlen); 1517 /* check if there are user-supplied options that follow */ 1518 if (optlen < newlen) { 1519 /* compute amount of embedded alignment needed */ 1520 optlen -= newopts[IPOPT_OLEN]; 1521 newopts += newopts[IPOPT_OLEN]; 1522 while (--optlen >= 0) 1523 *newopts++ = IPOPT_NOP; 1524 } else if (optlen != newopts[IPOPT_OLEN]) { 1525 /* 1526 * The label option is the only option and it is 1527 * not a multiple of 4 bytes. 1528 */ 1529 optlen -= newopts[IPOPT_OLEN]; 1530 newopts += newopts[IPOPT_OLEN]; 1531 while (--optlen >= 0) 1532 *newopts++ = IPOPT_EOL; 1533 } 1534 } 1535 return (0); 1536 } 1537 1538 /* 1539 * This does the bulk of the processing for setting IPPROTO_IP {T_,}IP_OPTIONS. 1540 */ 1541 boolean_t 1542 tsol_option_set(uchar_t **opts, uint_t *optlen, uint_t labellen, 1543 const uchar_t *useropts, uint_t userlen) 1544 { 1545 int newlen; 1546 uchar_t *newopts; 1547 1548 newlen = userlen + labellen; 1549 if (newlen > *optlen) { 1550 /* need more room */ 1551 newopts = (uchar_t *)mi_alloc(newlen, BPRI_HI); 1552 if (newopts == NULL) 1553 return (B_FALSE); 1554 /* 1555 * The supplied *opts can't be NULL in this case, 1556 * since there's an existing label. 1557 */ 1558 if (labellen > 0) 1559 bcopy(*opts, newopts, labellen); 1560 if (*opts != NULL) 1561 mi_free((char *)*opts); 1562 *opts = newopts; 1563 } 1564 1565 if (newlen == 0) { 1566 /* special case -- no remaining IP options at all */ 1567 if (*opts != NULL) { 1568 mi_free((char *)*opts); 1569 *opts = NULL; 1570 } 1571 } else if (userlen > 0) { 1572 /* merge in the user's options */ 1573 newopts = *opts; 1574 if (labellen > 0) { 1575 int extra = labellen - newopts[IPOPT_OLEN]; 1576 1577 newopts += newopts[IPOPT_OLEN]; 1578 while (--extra >= 0) 1579 *newopts++ = IPOPT_NOP; 1580 } 1581 bcopy(useropts, newopts, userlen); 1582 } 1583 1584 *optlen = newlen; 1585 return (B_TRUE); 1586 } 1587