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