On Thu, 20 Dec 2007, Paul Moore wrote: > This patch adds a number of new IPsec audit events to meet the auditing > requirements of RFC4303. This includes audit hooks for the following events: > > * Could not find a valid SA [sections 2.1, 3.4.2] > . xfrm_audit_state_notfound() > . xfrm_audit_state_notfound_simple() > > * Sequence number overflow [section 3.3.3] > . xfrm_audit_state_replay_overflow() > > * Replayed packet [section 3.4.3] > . xfrm_audit_state_replay() > > * Integrity check failure [sections 3.4.4.1, 3.4.4.2] > . xfrm_audit_state_icvfail() > > While RFC4304 deals only with ESP most of the changes in this patch apply to > IPsec in general, i.e. both AH and ESP. The one case, integrity check > failure, where ESP specific code had to be modified the same was done to the > AH code for the sake of consistency. > > Signed-off-by: Paul Moore <[EMAIL PROTECTED]>
Acked-by: James Morris <[EMAIL PROTECTED]> > --- > > include/net/xfrm.h | 33 ++++++++-- > net/ipv4/ah4.c | 4 + > net/ipv4/esp4.c | 1 > net/ipv6/ah6.c | 2 - > net/ipv6/esp6.c | 1 > net/ipv6/xfrm6_input.c | 4 + > net/xfrm/xfrm_input.c | 6 +- > net/xfrm/xfrm_output.c | 2 + > net/xfrm/xfrm_policy.c | 14 ++-- > net/xfrm/xfrm_state.c | 153 > +++++++++++++++++++++++++++++++++++++++++++----- > 10 files changed, 184 insertions(+), 36 deletions(-) > > diff --git a/include/net/xfrm.h b/include/net/xfrm.h > index ac6cf09..941d5cd 100644 > --- a/include/net/xfrm.h > +++ b/include/net/xfrm.h > @@ -548,26 +548,33 @@ struct xfrm_audit > }; > > #ifdef CONFIG_AUDITSYSCALL > -static inline struct audit_buffer *xfrm_audit_start(u32 auid, u32 secid) > +static inline struct audit_buffer *xfrm_audit_start(const char *op) > { > struct audit_buffer *audit_buf = NULL; > - char *secctx; > - u32 secctx_len; > > + if (audit_enabled == 0) > + return NULL; > audit_buf = audit_log_start(current->audit_context, GFP_ATOMIC, > - AUDIT_MAC_IPSEC_EVENT); > + AUDIT_MAC_IPSEC_EVENT); > if (audit_buf == NULL) > return NULL; > + audit_log_format(audit_buf, "op=%s", op); > + return audit_buf; > +} > > - audit_log_format(audit_buf, "auid=%u", auid); > +static inline void xfrm_audit_helper_usrinfo(u32 auid, u32 secid, > + struct audit_buffer *audit_buf) > +{ > + char *secctx; > + u32 secctx_len; > > + audit_log_format(audit_buf, " auid=%u", auid); > if (secid != 0 && > security_secid_to_secctx(secid, &secctx, &secctx_len) == 0) { > audit_log_format(audit_buf, " subj=%s", secctx); > security_release_secctx(secctx, secctx_len); > } else > audit_log_task_context(audit_buf); > - return audit_buf; > } > > extern void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, > @@ -578,11 +585,22 @@ extern void xfrm_audit_state_add(struct xfrm_state *x, > int result, > u32 auid, u32 secid); > extern void xfrm_audit_state_delete(struct xfrm_state *x, int result, > u32 auid, u32 secid); > +extern void xfrm_audit_state_replay_overflow(struct xfrm_state *x, > + struct sk_buff *skb); > +extern void xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 > family); > +extern void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family, > + __be32 net_spi, __be32 net_seq); > +extern void xfrm_audit_state_icvfail(struct xfrm_state *x, > + struct sk_buff *skb, u8 proto); > #else > #define xfrm_audit_policy_add(x, r, a, s) do { ; } while (0) > #define xfrm_audit_policy_delete(x, r, a, s) do { ; } while (0) > #define xfrm_audit_state_add(x, r, a, s) do { ; } while (0) > #define xfrm_audit_state_delete(x, r, a, s) do { ; } while (0) > +#define xfrm_audit_state_replay_overflow(x, s) do { ; } while (0) > +#define xfrm_audit_state_notfound_simple(s, f) do { ; } while (0) > +#define xfrm_audit_state_notfound(s, f, sp, sq) do { ; } while (0) > +#define xfrm_audit_state_icvfail(x, s, p) do { ; } while (0) > #endif /* CONFIG_AUDITSYSCALL */ > > static inline void xfrm_pol_hold(struct xfrm_policy *policy) > @@ -1193,7 +1211,8 @@ extern int xfrm_state_delete(struct xfrm_state *x); > extern int xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info); > extern void xfrm_sad_getinfo(struct xfrmk_sadinfo *si); > extern void xfrm_spd_getinfo(struct xfrmk_spdinfo *si); > -extern int xfrm_replay_check(struct xfrm_state *x, __be32 seq); > +extern int xfrm_replay_check(struct xfrm_state *x, > + struct sk_buff *skb, __be32 seq); > extern void xfrm_replay_advance(struct xfrm_state *x, __be32 seq); > extern void xfrm_replay_notify(struct xfrm_state *x, int event); > extern int xfrm_state_mtu(struct xfrm_state *x, int mtu); > diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c > index d76803a..ec8de0a 100644 > --- a/net/ipv4/ah4.c > +++ b/net/ipv4/ah4.c > @@ -179,8 +179,10 @@ static int ah_input(struct xfrm_state *x, struct sk_buff > *skb) > err = ah_mac_digest(ahp, skb, ah->auth_data); > if (err) > goto unlock; > - if (memcmp(ahp->work_icv, auth_data, ahp->icv_trunc_len)) > + if (memcmp(ahp->work_icv, auth_data, ahp->icv_trunc_len)) { > + xfrm_audit_state_icvfail(x, skb, IPPROTO_AH); > err = -EBADMSG; > + } > } > unlock: > spin_unlock(&x->lock); > diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c > index 28ea5c7..b334c76 100644 > --- a/net/ipv4/esp4.c > +++ b/net/ipv4/esp4.c > @@ -191,6 +191,7 @@ static int esp_input(struct xfrm_state *x, struct sk_buff > *skb) > BUG(); > > if (unlikely(memcmp(esp->auth.work_icv, sum, alen))) { > + xfrm_audit_state_icvfail(x, skb, IPPROTO_ESP); > err = -EBADMSG; > goto unlock; > } > diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c > index 1b51d1e..2d32772 100644 > --- a/net/ipv6/ah6.c > +++ b/net/ipv6/ah6.c > @@ -381,7 +381,7 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff > *skb) > if (err) > goto unlock; > if (memcmp(ahp->work_icv, auth_data, ahp->icv_trunc_len)) { > - LIMIT_NETDEBUG(KERN_WARNING "ipsec ah authentication > error\n"); > + xfrm_audit_state_icvfail(x, skb, IPPROTO_AH); > err = -EBADMSG; > } > } > diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c > index 5bd5292..e10f10b 100644 > --- a/net/ipv6/esp6.c > +++ b/net/ipv6/esp6.c > @@ -186,6 +186,7 @@ static int esp6_input(struct xfrm_state *x, struct > sk_buff *skb) > BUG(); > > if (unlikely(memcmp(esp->auth.work_icv, sum, alen))) { > + xfrm_audit_state_icvfail(x, skb, IPPROTO_ESP); > ret = -EBADMSG; > goto unlock; > } > diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c > index 74f3aac..08b850a 100644 > --- a/net/ipv6/xfrm6_input.c > +++ b/net/ipv6/xfrm6_input.c > @@ -136,8 +136,10 @@ int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t > *daddr, > break; > } > > - if (!xfrm_vec_one) > + if (!xfrm_vec_one) { > + xfrm_audit_state_notfound_simple(skb, AF_INET6); > goto drop; > + } > > /* Allocate new secpath or COW existing one. */ > if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) { > diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c > index 8624cbd..87e1aac 100644 > --- a/net/xfrm/xfrm_input.c > +++ b/net/xfrm/xfrm_input.c > @@ -139,8 +139,10 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 > spi, int encap_type) > goto drop; > > x = xfrm_state_lookup(daddr, spi, nexthdr, family); > - if (x == NULL) > + if (x == NULL) { > + xfrm_audit_state_notfound(skb, family, spi, seq); > goto drop; > + } > > skb->sp->xvec[skb->sp->len++] = x; > > @@ -151,7 +153,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 > spi, int encap_type) > if ((x->encap ? x->encap->encap_type : 0) != encap_type) > goto drop_unlock; > > - if (x->props.replay_window && xfrm_replay_check(x, seq)) > + if (x->props.replay_window && xfrm_replay_check(x, skb, seq)) > goto drop_unlock; > > if (xfrm_state_check_expire(x)) > diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c > index 26fa0cb..eb3333b 100644 > --- a/net/xfrm/xfrm_output.c > +++ b/net/xfrm/xfrm_output.c > @@ -57,6 +57,8 @@ static int xfrm_output_one(struct sk_buff *skb, int err) > > if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { > XFRM_SKB_CB(skb)->seq = ++x->replay.oseq; > + if (unlikely(x->replay.oseq == 0)) > + xfrm_audit_state_replay_overflow(x, skb); > if (xfrm_aevent_is_on()) > xfrm_replay_notify(x, XFRM_REPLAY_UPDATE); > } > diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c > index c8f0656..97cebfe 100644 > --- a/net/xfrm/xfrm_policy.c > +++ b/net/xfrm/xfrm_policy.c > @@ -2323,12 +2323,11 @@ void xfrm_audit_policy_add(struct xfrm_policy *xp, > int result, > { > struct audit_buffer *audit_buf; > > - if (audit_enabled == 0) > - return; > - audit_buf = xfrm_audit_start(auid, secid); > + audit_buf = xfrm_audit_start("SPD-add"); > if (audit_buf == NULL) > return; > - audit_log_format(audit_buf, " op=SPD-add res=%u", result); > + xfrm_audit_helper_usrinfo(auid, secid, audit_buf); > + audit_log_format(audit_buf, " res=%u", result); > xfrm_audit_common_policyinfo(xp, audit_buf); > audit_log_end(audit_buf); > } > @@ -2339,12 +2338,11 @@ void xfrm_audit_policy_delete(struct xfrm_policy *xp, > int result, > { > struct audit_buffer *audit_buf; > > - if (audit_enabled == 0) > - return; > - audit_buf = xfrm_audit_start(auid, secid); > + audit_buf = xfrm_audit_start("SPD-delete"); > if (audit_buf == NULL) > return; > - audit_log_format(audit_buf, " op=SPD-delete res=%u", result); > + xfrm_audit_helper_usrinfo(auid, secid, audit_buf); > + audit_log_format(audit_buf, " res=%u", result); > xfrm_audit_common_policyinfo(xp, audit_buf); > audit_log_end(audit_buf); > } > diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c > index dd38e6f..1f00aeb 100644 > --- a/net/xfrm/xfrm_state.c > +++ b/net/xfrm/xfrm_state.c > @@ -61,6 +61,13 @@ static unsigned int xfrm_state_genid; > static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family); > static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo); > > +#ifdef CONFIG_AUDITSYSCALL > +static void xfrm_audit_state_replay(struct xfrm_state *x, > + struct sk_buff *skb, __be32 net_seq); > +#else > +#define xfrm_audit_state_replay(x, s, sq) do { ; } while (0) > +#endif /* CONFIG_AUDITSYSCALL */ > + > static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr, > xfrm_address_t *saddr, > u32 reqid, > @@ -1609,13 +1616,14 @@ static void xfrm_replay_timer_handler(unsigned long > data) > spin_unlock(&x->lock); > } > > -int xfrm_replay_check(struct xfrm_state *x, __be32 net_seq) > +int xfrm_replay_check(struct xfrm_state *x, > + struct sk_buff *skb, __be32 net_seq) > { > u32 diff; > u32 seq = ntohl(net_seq); > > if (unlikely(seq == 0)) > - return -EINVAL; > + goto err; > > if (likely(seq > x->replay.seq)) > return 0; > @@ -1624,14 +1632,18 @@ int xfrm_replay_check(struct xfrm_state *x, __be32 > net_seq) > if (diff >= min_t(unsigned int, x->props.replay_window, > sizeof(x->replay.bitmap) * 8)) { > x->stats.replay_window++; > - return -EINVAL; > + goto err; > } > > if (x->replay.bitmap & (1U << diff)) { > x->stats.replay++; > - return -EINVAL; > + goto err; > } > return 0; > + > +err: > + xfrm_audit_state_replay(x, skb, net_seq); > + return -EINVAL; > } > EXPORT_SYMBOL(xfrm_replay_check); > > @@ -1994,8 +2006,8 @@ void __init xfrm_state_init(void) > } > > #ifdef CONFIG_AUDITSYSCALL > -static inline void xfrm_audit_common_stateinfo(struct xfrm_state *x, > - struct audit_buffer *audit_buf) > +static inline void xfrm_audit_helper_sainfo(struct xfrm_state *x, > + struct audit_buffer *audit_buf) > { > struct xfrm_sec_ctx *ctx = x->security; > u32 spi = ntohl(x->id.spi); > @@ -2022,18 +2034,45 @@ static inline void xfrm_audit_common_stateinfo(struct > xfrm_state *x, > audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi); > } > > +static inline void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family, > + struct audit_buffer *audit_buf) > +{ > + struct iphdr *iph4; > + struct ipv6hdr *iph6; > + > + switch (family) { > + case AF_INET: > + iph4 = ip_hdr(skb); > + audit_log_format(audit_buf, > + " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT, > + NIPQUAD(iph4->saddr), > + NIPQUAD(iph4->daddr)); > + break; > + case AF_INET6: > + iph6 = ipv6_hdr(skb); > + audit_log_format(audit_buf, > + " src=" NIP6_FMT " dst=" NIP6_FMT > + " flowlbl=0x%x%x%x", > + NIP6(iph6->saddr), > + NIP6(iph6->daddr), > + iph6->flow_lbl[0] & 0x0f, > + iph6->flow_lbl[1], > + iph6->flow_lbl[2]); > + break; > + } > +} > + > void xfrm_audit_state_add(struct xfrm_state *x, int result, > u32 auid, u32 secid) > { > struct audit_buffer *audit_buf; > > - if (audit_enabled == 0) > - return; > - audit_buf = xfrm_audit_start(auid, secid); > + audit_buf = xfrm_audit_start("SAD-add"); > if (audit_buf == NULL) > return; > - audit_log_format(audit_buf, " op=SAD-add res=%u", result); > - xfrm_audit_common_stateinfo(x, audit_buf); > + xfrm_audit_helper_usrinfo(auid, secid, audit_buf); > + xfrm_audit_helper_sainfo(x, audit_buf); > + audit_log_format(audit_buf, " res=%u", result); > audit_log_end(audit_buf); > } > EXPORT_SYMBOL_GPL(xfrm_audit_state_add); > @@ -2043,14 +2082,96 @@ void xfrm_audit_state_delete(struct xfrm_state *x, > int result, > { > struct audit_buffer *audit_buf; > > - if (audit_enabled == 0) > - return; > - audit_buf = xfrm_audit_start(auid, secid); > + audit_buf = xfrm_audit_start("SAD-delete"); > if (audit_buf == NULL) > return; > - audit_log_format(audit_buf, " op=SAD-delete res=%u", result); > - xfrm_audit_common_stateinfo(x, audit_buf); > + xfrm_audit_helper_usrinfo(auid, secid, audit_buf); > + xfrm_audit_helper_sainfo(x, audit_buf); > + audit_log_format(audit_buf, " res=%u", result); > audit_log_end(audit_buf); > } > EXPORT_SYMBOL_GPL(xfrm_audit_state_delete); > + > +void xfrm_audit_state_replay_overflow(struct xfrm_state *x, > + struct sk_buff *skb) > +{ > + struct audit_buffer *audit_buf; > + u32 spi; > + > + audit_buf = xfrm_audit_start("SA-replay-overflow"); > + if (audit_buf == NULL) > + return; > + xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf); > + /* don't record the sequence number because it's inherent in this kind > + * of audit message */ > + spi = ntohl(x->id.spi); > + audit_log_format(audit_buf, " spi=%u(0x%x)", spi, spi); > + audit_log_end(audit_buf); > +} > +EXPORT_SYMBOL_GPL(xfrm_audit_state_replay_overflow); > + > +static void xfrm_audit_state_replay(struct xfrm_state *x, > + struct sk_buff *skb, __be32 net_seq) > +{ > + struct audit_buffer *audit_buf; > + u32 spi; > + > + audit_buf = xfrm_audit_start("SA-replayed-pkt"); > + if (audit_buf == NULL) > + return; > + xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf); > + spi = ntohl(x->id.spi); > + audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u", > + spi, spi, ntohl(net_seq)); > + audit_log_end(audit_buf); > +} > + > +void xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 family) > +{ > + struct audit_buffer *audit_buf; > + > + audit_buf = xfrm_audit_start("SA-notfound"); > + if (audit_buf == NULL) > + return; > + xfrm_audit_helper_pktinfo(skb, family, audit_buf); > + audit_log_end(audit_buf); > +} > +EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound_simple); > + > +void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family, > + __be32 net_spi, __be32 net_seq) > +{ > + struct audit_buffer *audit_buf; > + u32 spi; > + > + audit_buf = xfrm_audit_start("SA-notfound"); > + if (audit_buf == NULL) > + return; > + xfrm_audit_helper_pktinfo(skb, family, audit_buf); > + spi = ntohl(net_spi); > + audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u", > + spi, spi, ntohl(net_seq)); > + audit_log_end(audit_buf); > +} > +EXPORT_SYMBOL_GPL(xfrm_audit_state_notfound); > + > +void xfrm_audit_state_icvfail(struct xfrm_state *x, > + struct sk_buff *skb, u8 proto) > +{ > + struct audit_buffer *audit_buf; > + __be32 net_spi; > + __be32 net_seq; > + > + audit_buf = xfrm_audit_start("SA-icv-failure"); > + if (audit_buf == NULL) > + return; > + xfrm_audit_helper_pktinfo(skb, x->props.family, audit_buf); > + if (xfrm_parse_spi(skb, proto, &net_spi, &net_seq) == 0) { > + u32 spi = ntohl(net_spi); > + audit_log_format(audit_buf, " spi=%u(0x%x) seqno=%u", > + spi, spi, ntohl(net_seq)); > + } > + audit_log_end(audit_buf); > +} > +EXPORT_SYMBOL_GPL(xfrm_audit_state_icvfail); > #endif /* CONFIG_AUDITSYSCALL */ > > -- > To unsubscribe from this list: send the line "unsubscribe netdev" in > the body of a message to [EMAIL PROTECTED] > More majordomo info at http://vger.kernel.org/majordomo-info.html > -- James Morris <[EMAIL PROTECTED]> -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html