[XFRM]: RFC4303 compliant auditing
authorPaul Moore <paul.moore@hp.com>
Fri, 21 Dec 2007 22:58:11 +0000 (14:58 -0800)
committerDavid S. Miller <davem@davemloft.net>
Mon, 28 Jan 2008 23:00:01 +0000 (15:00 -0800)
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 <paul.moore@hp.com>
Acked-by: James Morris <jmorris@namei.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/xfrm.h
net/ipv4/ah4.c
net/ipv4/esp4.c
net/ipv6/ah6.c
net/ipv6/esp6.c
net/ipv6/xfrm6_input.c
net/xfrm/xfrm_input.c
net/xfrm/xfrm_output.c
net/xfrm/xfrm_policy.c
net/xfrm/xfrm_state.c

index f333c95c418986b2c42cdcbb9fffc48ce8fc4b86..5d5580ac0101c718befe0761fc6e6e2b164a8483 100644 (file)
@@ -565,26 +565,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,
@@ -595,11 +602,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)
@@ -1214,7 +1232,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);
index d76803a3dcae843f1ebf07794fdd0da6c34b9498..ec8de0aa20ecf8d56f8fe67dce2a2939090b8990 100644 (file)
@@ -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);
index 28ea5c77ca238a72a424ae292c2decc622c2d70e..b334c7619c08bf8903cb2b86d7f9c6493d2aee60 100644 (file)
@@ -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;
                }
index 1b51d1eedbded106ce2adf60490636b1e23e0cb3..2d32772c87c3f9913a7a7f5bd8ebd8f63f820f66 100644 (file)
@@ -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;
                }
        }
index 5bd5292ad9fa9021cf31899355db8cd60857a165..e10f10bfe2c97bd47239be7c061700d83bf8e7e6 100644 (file)
@@ -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;
                }
index 6644fc6d542797aba6880d969c0f4d40c9b408cb..063ce6ed1bd095797262bb915c4d16d9175802c6 100644 (file)
@@ -152,6 +152,7 @@ int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr,
 
        if (!x) {
                XFRM_INC_STATS(LINUX_MIB_XFRMINNOSTATES);
+               xfrm_audit_state_notfound_simple(skb, AF_INET6);
                goto drop;
        }
 
index 493243fc5fe5d8db1efbba92991d98493676e4a7..1b250f33ad5b38962080cdc1d46db566deb4b51e 100644 (file)
@@ -147,6 +147,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
                x = xfrm_state_lookup(daddr, spi, nexthdr, family);
                if (x == NULL) {
                        XFRM_INC_STATS(LINUX_MIB_XFRMINNOSTATES);
+                       xfrm_audit_state_notfound(skb, family, spi, seq);
                        goto drop;
                }
 
@@ -163,7 +164,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int 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)) {
                        XFRM_INC_STATS(LINUX_MIB_XFRMINSEQOUTOFWINDOW);
                        goto drop_unlock;
                }
index 867484a046af15ab4ccb7a32f7fa100cc2ceb0e5..09514449fe8a3bf7aeb791b4fe98b7604a8d454d 100644 (file)
@@ -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);
                }
index abc3e39b115b8cfb7489ca9802022462fc76d37e..280f8ded975ca79826f5db857f72d91d1eae5d2d 100644 (file)
@@ -2407,12 +2407,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);
 }
@@ -2423,12 +2422,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);
 }
index 9e57378c51dfaa80bc3ee7e187d3e1bb472f2866..6bf876c866dfec49be76391c2cfa22d4430e8a74 100644 (file)
@@ -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);
 
@@ -1996,8 +2008,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);
@@ -2024,18 +2036,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);
@@ -2045,14 +2084,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 */