/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <inet/ipclassifier.h>
#include <inet/ip_ftable.h>
/*
* This routine takes a sensitivity label as input and creates a CIPSO
* option in the specified buffer. It returns the size of the CIPSO option.
* If the sensitivity label is too large for the CIPSO option, then 0
* is returned.
*
* tsol2cipso_tt1 returns 0 for failure and greater than 0 for success
* (more accurately, success means a return value between 10 and 40).
*/
static int
{
int i;
if (doi == 0)
return (0);
/* check for Admin High sensitivity label */
return (0);
/* check whether classification will fit in one octet */
return (0);
/*
* Check whether compartments will fit in 30 octets.
* Compartments 241 - 256 are not allowed.
*/
return (0);
/*
* Compute option length and tag length.
* 'p' points to the last two bytes in the Sensitivity Label's
* compartments; these cannot be mapped into CIPSO compartments.
*/
if (*ucp != 0)
break;
return (10 + i);
return (cop[IPOPT_OLEN]);
}
/*
* The following routine searches for a security label in an IPv4 datagram.
* It returns label_type of:
* OPT_CIPSO if a CIPSO IP option is found.
* OPT_NONE if no security label is found.
*
* If OPT_CIPSO, a pointer to the CIPSO IP option will be returned in
* the buffer parameter.
*
* The function will return with B_FALSE if an IP format error
* is encountered.
*/
{
*label_type = OPT_NONE;
/*
* Get length (in 4 byte octets) of IP header options.
* If header doesn't contain options, then return a label_type
* of OPT_NONE.
*/
totallen <<= 2;
return (B_FALSE);
if (totallen == 0)
return (B_TRUE);
/*
* Search for CIPSO option.
* If no such option is present, then return OPT_NONE.
*/
while (totallen != 0) {
case IPOPT_EOL:
return (B_TRUE);
case IPOPT_NOP:
optlen = 1;
break;
default:
if (totallen <= IPOPT_OLEN)
return (B_FALSE);
if (optlen < 2)
return (B_FALSE);
}
return (B_FALSE);
/*
* Copy pointer to option into '*buffer' and
* return the option type.
*/
switch (optval) {
case IPOPT_COMSEC:
if (TSOL_CIPSO_TAG_OFFSET < optlen &&
*label_type = OPT_CIPSO;
return (B_TRUE);
}
return (B_FALSE);
}
}
return (B_TRUE);
}
/*
* The following routine searches for a security label in an IPv6 datagram.
* It returns label_type of:
* OPT_CIPSO if a CIPSO IP option is found.
* OPT_NONE if no security label is found.
*
* If OPT_CIPSO, a pointer to the IPv4 portion of the CIPSO IP option will
* be returned in the buffer parameter.
*
* The function will return with B_FALSE if an IP format error
* or an unexpected label content error is encountered.
*/
{
*label_type = OPT_NONE;
return (B_TRUE);
return (B_FALSE);
return (B_FALSE);
return (B_FALSE);
/* tsol_find_secopt_v6 guarantees some sanity */
/*
* IPv6 Option
* opt_ptr[0]: Option type
* opt_ptr[1]: Length of option data in bytes
* opt_ptr[2]: First byte of option data
*/
return (B_FALSE);
opt_ptr += 2;
/*
* From "Generalized Labeled Security Option for IPv6" draft
* opt_ptr[0] - opt_ptr[4]: DOI = IP6LS_DOI_V4
* opt_ptr[4]: Tag type = IP6LS_TT_V4
* opt_ptr[5]: Tag length in bytes starting at Tag type field
* IPv4 CIPSO Option
* opt_ptr[6]: option type
* opt_ptr[7]: option length in bytes starting at type field
*/
if (doi == IP6LS_DOI_V4 &&
*label_type = OPT_CIPSO;
return (B_TRUE);
}
return (B_FALSE);
}
return (B_TRUE);
}
/*
* tsol_check_dest()
*
* This routine verifies if a destination is allowed to recieve messages
* based on the security label. If any adjustments to the label are needed
* due to the connection's MAC mode or the destination's ability
* to receive labels, an "effective label" will be returned.
*
* zone_is_global is set if the actual zoneid is global. That is, it is
* not set for an exclusive-IP zone.
*
* On successful return, effective_tsl will point to the new label needed
* or will be NULL if a new label isn't needed. On error, effective_tsl will
* point to NULL.
*
* Returns:
* 0 Label (was|is now) correct
* EHOSTUNREACH The label failed the remote host accreditation
* ENOMEM Memory allocation failure
*/
int
{
if (effective_tsl != NULL)
*effective_tsl = NULL;
(version == IPV6_VERSION &&
/* Always pass kernel level communication (NULL label) */
char *, "destination ip(1) with null label was passed",
return (0);
}
char *,
"implicit-in packet to ip(1) reached tsol_check_dest "
"with implied security label sl(2)",
}
/* Always pass multicast */
if (version == IPV4_VERSION &&
char *, "destination ip(1) with multicast dest was passed",
return (0);
} else if (version == IPV6_VERSION &&
char *, "destination ip(1) with multicast dest was passed",
in6_addr_t *, dst);
return (0);
}
/* Never pass an undefined destination */
char *, "destination ip(1) not in tn database.",
void *, dst);
return (EHOSTUNREACH);
}
case UNLABELED:
/*
* Can talk to unlabeled hosts if
* (1) zone's label matches the default label, or
* (2) SO_MAC_EXEMPT is on and we
* dominate the peer's label, or
* (3) SO_MAC_EXEMPT is on and
* this is the global zone
*/
char *, "unlabeled dest ip(1)/tpc(2) doi does "
"not match msg label(3) doi.", void *, dst,
return (EHOSTUNREACH);
}
if (mac_mode != CONN_MAC_AWARE ||
!(zone_is_global ||
char *, "unlabeled dest ip(1)/tpc(2) does "
"not match msg label(3).", void *, dst,
return (EHOSTUNREACH);
}
/*
* This is a downlabel MAC-exempt exchange.
* Use the remote destination's default label
* as the label of the message data.
*/
return (ENOMEM);
}
/*
* The security labels are the same but we need
* to flag that the remote node is unlabeled.
*/
return (ENOMEM);
}
}
break;
case SUN_CIPSO:
/*
* Can talk to labeled hosts if zone's label is within target's
* label range or set.
*/
char *, "labeled dest ip(1)/tpc(2) does not "
"match msg label(3).", void *, dst,
return (EHOSTUNREACH);
}
(mac_mode == CONN_MAC_IMPLICIT)) {
/*
* Copy label so we can modify the flags
*/
return (ENOMEM);
}
/*
* The security label is a match but we need to
* clear the unlabeled flag for this remote node.
*/
if (mac_mode == CONN_MAC_IMPLICIT)
}
break;
default:
return (EHOSTUNREACH);
}
/*
* Return the new label.
*/
if (effective_tsl != NULL)
*effective_tsl = newtsl;
else
}
return (0);
}
/*
* tsol_compute_label_v4()
*
* This routine computes the IP label that should be on a packet based on the
* connection and destination information.
*
* The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones).
*
* Returns:
* 0 Fetched label
* EHOSTUNREACH No route to destination
* EINVAL Label cannot be computed
*/
int
{
if (opt_storage != NULL)
opt_storage[IPOPT_OLEN] = 0;
return (0);
/* always pass multicast */
return (0);
return (0);
/*
* The destination is unlabeled. Only add a label if the
* the destination is not on the same subnet, and the
* next-hop gateway is labeled.
*/
NULL);
/* no route to destination */
char *, "No route to unlabeled dest ip(1) with "
return (EHOSTUNREACH);
}
IRE_INTERFACE)) {
return (0);
}
/*
* ire_route_recursive gives us the first attrp it finds
* in the recursive lookup.
*/
/*
* Return now if next hop gateway is unlabeled. There is
* no need to generate a CIPSO option for this message.
*/
return (0);
}
}
/* compute the CIPSO option */
if (sec_opt_len == 0) {
char *, "options lack length for dest ip(1) with label(2).",
return (EINVAL);
}
return (0);
}
/*
* Remove any existing security option (CIPSO) from the given IP
* header, move the 'buflen' bytes back to fill the gap, and return the number
* of bytes removed (as zero or negative number). Assumes that the headers are
* sane.
*
* Note that tsol_remove_secopt does not adjust ipha_length but
* tsol_remove_secopt_v6 does adjust ip6_plen.
*/
int
{
while (remlen > 0) {
/* terminate on end of list */
break;
/*
* Delete any no-ops following a deleted option, at least up
* to a 4 octet alignment; copy others.
*/
if (noop_keep)
fptr++;
remlen--;
continue;
}
/* stop on corrupted list; just do nothing. */
if (remlen < 2)
return (0);
return (0);
/* skip over security options to delete them */
continue;
}
/* copy the rest */
}
/* figure how much padding we'll need for header alignment */
if (olen > 0) {
/* pad with end-of-list */
}
/* slide back the headers that follow and update the IP header */
if (delta != 0) {
}
return (-delta);
}
/*
* Insert the option in 'optbuf' into the IP header pointed to by 'ipha', and
* move the data following the IP header (up to buflen) to accomodate the new
* option. Assumes that up to IP_MAX_OPT_LENGTH bytes are available (in total)
* for IP options. Returns the number of bytes actually inserted, or -1 if the
* option cannot be inserted. (Note that negative return values are possible
* when noops must be compressed, and that only -1 indicates error. Successful
* return value is always evenly divisible by 4, by definition.)
*
* Note that tsol_prepend_option does not adjust ipha_length but
* tsol_prepend_option_v6 does adjust ip6_plen.
*/
int
{
int delta;
optbuf[IPOPT_OLEN] == 0)
return (0);
/* first find the real (unpadded) length of the existing options */
while (remlen > 0) {
/* stop at end of list */
break;
/* skip no-ops, noting that length byte isn't present */
optr++;
padding++;
lastpad++;
totlen++;
remlen--;
continue;
}
/* give up on a corrupted list; report failure */
if (remlen < 2)
return (-1);
return (-1);
lastpad = 0;
}
/* completely ignore any trailing padding */
/*
* If some sort of inter-option alignment was present, try to preserve
* that alignment. If alignment pushes us out past the maximum, then
* discard it and try to compress to fit. (We just "assume" that any
* padding added was attempting to get 32 bit alignment. If that's
* wrong, that's just too bad.)
*/
if (padding > 0) {
return (-1);
padding = 0;
}
}
/*
* Since we may need to compress or expand the option list, we write to
* a temporary buffer and then copy the results back to the IP header.
*/
/* compute actual option to insert */
if (padding > 0) {
while ((olen & 3) != 0) {
olen++;
}
}
/* copy over the existing options */
while (totlen > 0) {
/* totlen doesn't include end-of-list marker */
/* handle no-ops; copy if desired, ignore otherwise */
if (padding > 0) {
/* note: cannot overflow due to checks above */
}
optr++;
totlen--;
continue;
}
/* list cannot be corrupt at this point */
/* cannot run out of room due to tests above */
}
/* figure how much padding we'll need for header alignment */
if (olen > 0) {
/* pad with end-of-list value */
}
/* move the headers as needed and update IP header */
if (delta != 0) {
}
/* slap in the new options */
return (delta);
}
/*
* tsol_check_label_v4()
*
* This routine computes the IP label that should be on the packet based on the
* connection and destination information. It's called by the IP forwarding
* logic and by ip_output_simple. The ULPs generate the labels before calling
* conn_ip_output. If any adjustments to
* the label are needed due to the connection's MAC-exempt status or
* the destination's ability to receive labels, an "effective label"
* will be returned.
*
* The packet's header is clear before entering IPsec's engine.
*
* The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones).
* zone_is_global is set if the actual zoneid is global.
*
* On successful return, effective_tslp will point to the new label needed
* or will be NULL if a new label isn't needed. On error, effective_tsl will
* point to NULL.
*
* Returns:
* 0 Label (was|is now) correct
* EACCES The packet failed the remote host accreditation.
* ENOMEM Memory allocation failure.
* EINVAL Label cannot be computed
*/
int
{
int retv;
*effective_tslp = NULL;
opt_storage[IPOPT_OPTVAL] = 0;
/*
* Verify the destination is allowed to receive packets at
* the security label of the message data. tsol_check_dest()
* may create a new effective label or label flags.
*/
if (retv != 0)
return (retv);
/*
* Calculate the security label to be placed in the text
* of the message (if any).
*/
if (effective_tsl != NULL) {
return (retv);
}
} else {
return (retv);
}
}
if (hlen >= sec_opt_len) {
/* If no option is supposed to be there, make sure it's not */
if (sec_opt_len == 0 && hlen > 0 &&
return (0);
/* if the option is there, it's always first */
if (sec_opt_len != 0 &&
return (0);
}
/*
* If there is an option there, then it must be the wrong one; delete.
*/
if (hlen > 0) {
}
/* Make sure we have room for the worst-case addition */
if (hlen > IP_MAX_HDR_LENGTH)
int copylen;
/* allocate enough to be meaningful, but not *too* much */
if (copylen > 256)
copylen = 256;
if (effective_tsl != NULL) {
*effective_tslp = NULL;
}
return (ENOMEM);
}
/* keep the bias */
}
}
if (delta_add == -1)
goto param_prob;
return (0);
if (effective_tsl != NULL) {
*effective_tslp = NULL;
}
return (EINVAL);
}
/*
* IPv6 HopOpt extension header for the label option layout:
* - One octet giving the type of the 'next extension header'
* - Header extension length in 8-byte words, not including the
* 1st 8 bytes, but including any pad bytes at the end.
* Eg. A value of 2 means 16 bytes not including the 1st 8 bytes.
* - Followed by TLV encoded IPv6 label option. Option layout is
* * One octet, IP6OPT_LS
* * One octet option length in bytes of the option data following
* the length, but not including any pad bytes at the end.
* * Four-octet DOI (IP6LS_DOI_V4)
* * One octet suboption, IP6LS_TT_V4
* * One octet suboption length in bytes of the suboption
* following the suboption length, including the suboption
* header length, but not including any pad bytes at the end.
* - Pad to make the extension header a multiple of 8 bytes.
*
* This function returns the contents of 'IPv6 option structure' in the above.
* i.e starting from the IP6OPT_LS but not including the pad at the end.
* The user must prepend two octets (either padding or next header / length)
* and append padding out to the next 8 octet boundary.
*
* The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones).
*/
int
{
if (ip6opt_ls == 0)
return (EINVAL);
if (opt_storage != NULL)
opt_storage[IPOPT_OLEN] = 0;
return (0);
/* Always pass multicast */
if (IN6_IS_ADDR_MULTICAST(dst))
return (0);
/*
* Fill in a V6 label. If a new format is added here, make certain
* as TSOL_MAX_IPV6_OPTION.
*/
return (0);
/*
* The destination is unlabeled. Only add a label if the
* the destination is not on the same subnet, and the
* next-hop gateway is labeled.
*/
NULL);
/* no route to destination */
char *, "No route to unlabeled dest ip6(1) with "
return (EHOSTUNREACH);
}
IRE_INTERFACE)) {
return (0);
}
/*
* ire_route_recursive gives us the first attrp it finds
* in the recursive lookup.
*/
/*
* Return now if next hop gateway is unlabeled. There is
* no need to generate a CIPSO option for this message.
*/
return (0);
}
}
/* compute the CIPSO option */
if (opt_storage != NULL)
opt_storage += 8;
if (sec_opt_len == 0) {
char *, "options lack length for dest ip6(1) with "
return (EINVAL);
}
if (opt_storage == NULL)
return (0);
if (sec_opt_len < IP_MAX_OPT_LENGTH)
/*
* Just in case the option length is odd, round it up to the next even
* multiple. The IPv6 option definition doesn't like odd numbers for
* some reason.
*
* Length in the overall option header (IP6OPT_LS) does not include the
* option header itself, but the length in the suboption does include
* the suboption header. Thus, when there's just one suboption, the
* length in the option header is the suboption length plus 4 (for the
* DOI value).
*/
return (0);
}
/*
* Locate the start of the IP6OPT_LS label option and return it.
* Also return the start of the next non-pad option in after_secoptp.
* Usually the label option is the first option at least when packets
* are generated, but for generality we don't assume that on received packets.
*
* The function will return with B_FALSE if an IP format error
* or an unexpected label content error is encountered.
*/
{
*hbh_needed = B_FALSE;
*after_secoptp = NULL;
while (optlen != 0) {
if (opt_type == IP6OPT_PAD1) {
optptr++;
optlen--;
continue;
}
if (optlen == 1)
return (B_FALSE);
return (B_FALSE);
/*
* if we get here, ip6opt_ls can
* not be 0 because it will always
* match the IP6OPT_PAD1 above.
* Therefore ip6opt_ls == 0 forces
* this test to always fail here.
*/
/* More than one security option found */
return (B_FALSE);
} else switch (opt_type) {
case IP6OPT_PADN:
break;
default:
/*
* There is at least 1 option other than
* the label option. So the hop-by-hop header is needed
*/
*hbh_needed = B_TRUE;
return (B_TRUE);
}
break;
}
}
return (B_TRUE);
}
/*
* Remove the label option from the hop-by-hop options header if it exists.
* 'buflen' is the total length of the packet typically b_wptr - b_rptr.
* Header and data following the label option that is deleted are copied
* (i.e. slid backward) to the right position, and returns the number
* of bytes removed (as zero or negative number.)
*
* Note that tsol_remove_secopt does not adjust ipha_length but
* tsol_remove_secopt_v6 does adjust ip6_plen.
*/
int
{
/*
* hop-by-hop extension header must appear first, if it does not
* exist, there is no label option.
*/
return (0);
/*
* Locate the start of the label option if it exists and the end
* of the label option including pads if any.
*/
&hbh_needed)) {
/*
* This function should not see invalid messages.
* If one occurs, it would indicate either an
* option previously verified in the forwarding
* path has been corrupted or an option was
* incorrectly generated locally.
*/
ASSERT(0);
return (0);
}
return (0);
if (!hbh_needed) {
/*
* The label option was the only option in the hop-by-hop
* header. We don't need the hop-by-hop header itself any
* longer.
*/
return (-hbhlen);
}
if (after_secopt == NULL) {
/* There is no option following the label option */
}
/*
* After deleting the label option, we need to slide the headers
* and data back, while still maintaining the same alignment (module 8)
* for the other options. So we slide the headers and data back only
* by an integral multiple of 8 bytes, and fill the remaining bytes
* with pads.
*/
if (pad == 1) {
secopt[0] = IP6OPT_PAD1;
} else if (pad > 1) {
secopt[0] = IP6OPT_PADN;
if (pad > 2)
}
return (-delta);
}
/*
* 'optbuf' contains a CIPSO label embedded in an IPv6 hop-by-hop option,
* starting with the IP6OPT_LS option type. The format of this hop-by-hop
* option is described in the block comment above tsol_compute_label_v6.
* This function prepends this hop-by-hop option before any other hop-by-hop
* options in the hop-by-hop header if one already exists, else a new
* hop-by-hop header is created and stuffed into the packet following
* the IPv6 header. 'buflen' is the total length of the packet i.e.
* b_wptr - b_rptr. The caller ensures that there is enough space for the
* extra option being added. Header and data following the position where
* the label option is inserted are copied (i.e. slid forward) to the right
* position.
*
* Note that tsol_prepend_option does not adjust ipha_length but
* tsol_prepend_option_v6 does adjust ip6_plen.
*/
int
{
/*
* rawlen is the length of the label option in bytes, not including
* any pads, starting from the IP6OPT_LS (option type) byte.
*/
/*
* There is a hop-by-hop header present already. In order to
* preserve the alignment of the other options at the existing
* value (modulo 8) we need to pad the label option to a
* multiple of 8 bytes before prepending it to the other
* options. Slide the extension headers and data forward to
* accomodate the label option at the start of the hop-by-hop
* header
*/
/*
* Bump up the hop-by-hop extension header length by
* the number of 8-byte words added
*/
optlen >>= 3;
return (-1);
} else {
/*
* There is no hop-by-hop header in the packet. Construct a
* new Hop-by-hop extension header (a multiple of 8 bytes).
* Slide any other extension headers and data forward to
* accomodate this hop-by-hop header
*/
/*
* hop-by-hop extension header length in 8-byte words, not
* including the 1st 8 bytes of the hop-by-hop header.
*/
}
/*
* Copy the label option into the hop-by-hop header and insert any
* needed pads
*/
if (pad_len == 1) {
pad_position[0] = IP6OPT_PAD1;
} else if (pad_len > 1) {
pad_position[0] = IP6OPT_PADN;
if (pad_len > 2)
}
return (delta);
}
/*
* tsol_check_label_v6()
*
* This routine computes the IP label that should be on the packet based on the
* connection and destination information. It's called by the IP forwarding
* logic and by ip_output_simple. The ULPs generate the labels before calling
* conn_ip_output. If any adjustments to
* the label are needed due to the connection's MAC-exempt status or
* the destination's ability to receive labels, an "effective label"
* will be returned.
*
* The packet's header is clear before entering IPsec's engine.
*
* The zoneid is the IP zoneid (i.e., GLOBAL_ZONEID for exlusive-IP zones).
* zone_is_global is set if the actual zoneid is global.
*
* On successful return, effective_tslp will point to the new label needed
* or will be NULL if a new label isn't needed. On error, effective_tsl will
* point to NULL.
*
* Returns:
* 0 Label (was|is now) correct
* EACCES The packet failed the remote host accreditation.
* ENOMEM Memory allocation failure.
* EINVAL Label cannot be computed
*/
int
{
/*
* Label option length is limited to IP_MAX_OPT_LENGTH for
* symmetry with IPv4. Can be relaxed if needed
*/
int retv;
*effective_tslp = NULL;
/*
* Verify the destination is allowed to receive packets at
* the security label of the message data. tsol_check_dest()
* may create a new effective label or label flags.
*/
if (retv != 0)
return (retv);
/*
* Calculate the security label to be placed in the text
* of the message (if any).
*/
if (effective_tsl != NULL) {
return (retv);
}
} else {
return (retv);
}
&after_secopt, &hbh_needed)) {
/*
* This function should not see invalid messages.
* If one occurs, it would indicate either an
* option previously verified in the forwarding
* path has been corrupted or an option was
* incorrectly generated locally.
*/
ASSERT(0);
return (EACCES);
}
}
/*
* The packet is not supposed to have a label, and it
* does not have one currently
*/
return (0);
}
/* The packet has the correct label already */
return (0);
}
/*
* If there is an option there, then it must be the wrong one; delete.
*/
}
/*
* Make sure we have room for the worst-case addition. Add 2 bytes for
* the hop-by-hop ext header's next header and length fields. Add
* another 2 bytes for the label option type, len and then round
* up to the next 8-byte multiple.
*/
int copylen;
/*
* Allocate enough to be meaningful, but not *too* much.
* Also all the IPv6 extension headers must be in the same mblk
*/
if (copylen > 256)
copylen = 256;
if (effective_tsl != NULL) {
*effective_tslp = NULL;
}
return (ENOMEM);
}
/* keep the bias */
}
}
if (delta_add == -1)
goto param_prob;
/* tsol_prepend_option_v6 has adjusted ip6_plen */
return (0);
if (effective_tsl != NULL) {
*effective_tslp = NULL;
}
return (EINVAL);
}