Commit 89aa3619 authored by Ondrej Mosnacek's avatar Ondrej Mosnacek Committed by David S. Miller

cipso: make cipso_v4_skbuff_delattr() fully remove the CIPSO options

As the comment in this function says, the code currently just clears the
CIPSO part with IPOPT_NOP, rather than removing it completely and
trimming the packet. The other cipso_v4_*_delattr() functions, however,
do the proper removal and also calipso_skbuff_delattr() makes an effort
to remove the CALIPSO options instead of replacing them with padding.

Some routers treat IPv4 packets with anything (even NOPs) in the option
header as a special case and take them through a slower processing path.
Consequently, hardening guides such as STIG recommend to configure such
routers to drop packets with non-empty IP option headers [1][2]. Thus,
users might expect NetLabel to produce packets with minimal padding (or
at least with no padding when no actual options are present).

Implement the proper option removal to address this and to be closer to
what the peer functions do.

[1] https://www.stigviewer.com/stig/juniper_router_rtr/2019-09-27/finding/V-90937
[2] https://www.stigviewer.com/stig/cisco_ios_xe_router_rtr/2021-03-26/finding/V-217001Signed-off-by: default avatarOndrej Mosnacek <omosnace@redhat.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 9f361699
...@@ -1810,6 +1810,29 @@ static int cipso_v4_genopt(unsigned char *buf, u32 buf_len, ...@@ -1810,6 +1810,29 @@ static int cipso_v4_genopt(unsigned char *buf, u32 buf_len,
return CIPSO_V4_HDR_LEN + ret_val; return CIPSO_V4_HDR_LEN + ret_val;
} }
static int cipso_v4_get_actual_opt_len(const unsigned char *data, int len)
{
int iter = 0, optlen = 0;
/* determining the new total option length is tricky because of
* the padding necessary, the only thing i can think to do at
* this point is walk the options one-by-one, skipping the
* padding at the end to determine the actual option size and
* from there we can determine the new total option length
*/
while (iter < len) {
if (data[iter] == IPOPT_END) {
break;
} else if (data[iter] == IPOPT_NOP) {
iter++;
} else {
iter += data[iter + 1];
optlen = iter;
}
}
return optlen;
}
/** /**
* cipso_v4_sock_setattr - Add a CIPSO option to a socket * cipso_v4_sock_setattr - Add a CIPSO option to a socket
* @sk: the socket * @sk: the socket
...@@ -1986,7 +2009,6 @@ static int cipso_v4_delopt(struct ip_options_rcu __rcu **opt_ptr) ...@@ -1986,7 +2009,6 @@ static int cipso_v4_delopt(struct ip_options_rcu __rcu **opt_ptr)
u8 cipso_len; u8 cipso_len;
u8 cipso_off; u8 cipso_off;
unsigned char *cipso_ptr; unsigned char *cipso_ptr;
int iter;
int optlen_new; int optlen_new;
cipso_off = opt->opt.cipso - sizeof(struct iphdr); cipso_off = opt->opt.cipso - sizeof(struct iphdr);
...@@ -2006,23 +2028,8 @@ static int cipso_v4_delopt(struct ip_options_rcu __rcu **opt_ptr) ...@@ -2006,23 +2028,8 @@ static int cipso_v4_delopt(struct ip_options_rcu __rcu **opt_ptr)
memmove(cipso_ptr, cipso_ptr + cipso_len, memmove(cipso_ptr, cipso_ptr + cipso_len,
opt->opt.optlen - cipso_off - cipso_len); opt->opt.optlen - cipso_off - cipso_len);
/* determining the new total option length is tricky because of optlen_new = cipso_v4_get_actual_opt_len(opt->opt.__data,
* the padding necessary, the only thing i can think to do at opt->opt.optlen);
* this point is walk the options one-by-one, skipping the
* padding at the end to determine the actual option size and
* from there we can determine the new total option length */
iter = 0;
optlen_new = 0;
while (iter < opt->opt.optlen) {
if (opt->opt.__data[iter] == IPOPT_END) {
break;
} else if (opt->opt.__data[iter] == IPOPT_NOP) {
iter++;
} else {
iter += opt->opt.__data[iter + 1];
optlen_new = iter;
}
}
hdr_delta = opt->opt.optlen; hdr_delta = opt->opt.optlen;
opt->opt.optlen = (optlen_new + 3) & ~3; opt->opt.optlen = (optlen_new + 3) & ~3;
hdr_delta -= opt->opt.optlen; hdr_delta -= opt->opt.optlen;
...@@ -2242,7 +2249,8 @@ int cipso_v4_skbuff_setattr(struct sk_buff *skb, ...@@ -2242,7 +2249,8 @@ int cipso_v4_skbuff_setattr(struct sk_buff *skb,
*/ */
int cipso_v4_skbuff_delattr(struct sk_buff *skb) int cipso_v4_skbuff_delattr(struct sk_buff *skb)
{ {
int ret_val; int ret_val, cipso_len, hdr_len_actual, new_hdr_len_actual, new_hdr_len,
hdr_len_delta;
struct iphdr *iph; struct iphdr *iph;
struct ip_options *opt = &IPCB(skb)->opt; struct ip_options *opt = &IPCB(skb)->opt;
unsigned char *cipso_ptr; unsigned char *cipso_ptr;
...@@ -2255,16 +2263,37 @@ int cipso_v4_skbuff_delattr(struct sk_buff *skb) ...@@ -2255,16 +2263,37 @@ int cipso_v4_skbuff_delattr(struct sk_buff *skb)
if (ret_val < 0) if (ret_val < 0)
return ret_val; return ret_val;
/* the easiest thing to do is just replace the cipso option with noop
* options since we don't change the size of the packet, although we
* still need to recalculate the checksum */
iph = ip_hdr(skb); iph = ip_hdr(skb);
cipso_ptr = (unsigned char *)iph + opt->cipso; cipso_ptr = (unsigned char *)iph + opt->cipso;
memset(cipso_ptr, IPOPT_NOOP, cipso_ptr[1]); cipso_len = cipso_ptr[1];
hdr_len_actual = sizeof(struct iphdr) +
cipso_v4_get_actual_opt_len((unsigned char *)(iph + 1),
opt->optlen);
new_hdr_len_actual = hdr_len_actual - cipso_len;
new_hdr_len = (new_hdr_len_actual + 3) & ~3;
hdr_len_delta = (iph->ihl << 2) - new_hdr_len;
/* 1. shift any options after CIPSO to the left */
memmove(cipso_ptr, cipso_ptr + cipso_len,
new_hdr_len_actual - opt->cipso);
/* 2. move the whole IP header to its new place */
memmove((unsigned char *)iph + hdr_len_delta, iph, new_hdr_len_actual);
/* 3. adjust the skb layout */
skb_pull(skb, hdr_len_delta);
skb_reset_network_header(skb);
iph = ip_hdr(skb);
/* 4. re-fill new padding with IPOPT_END (may now be longer) */
memset((unsigned char *)iph + new_hdr_len_actual, IPOPT_END,
new_hdr_len - new_hdr_len_actual);
opt->optlen -= hdr_len_delta;
opt->cipso = 0; opt->cipso = 0;
opt->is_changed = 1; opt->is_changed = 1;
if (hdr_len_delta != 0) {
iph->ihl = new_hdr_len >> 2;
iph_set_totlen(iph, skb->len);
}
ip_send_check(iph); ip_send_check(iph);
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment