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