]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - net/xfrm/xfrm_user.c
ipv6: updates to privacy addresses per RFC 4941
[linux-2.6.git] / net / xfrm / xfrm_user.c
index 468ab60d3dc0d7a9ef0afccf6ef4262531d1fcf3..0256b8a0a7cf36b3f7f37c9645c71d724398d930 100644 (file)
@@ -119,6 +119,25 @@ static inline int verify_sec_ctx_len(struct nlattr **attrs)
        return 0;
 }
 
+static inline int verify_replay(struct xfrm_usersa_info *p,
+                               struct nlattr **attrs)
+{
+       struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL];
+
+       if ((p->flags & XFRM_STATE_ESN) && !rt)
+               return -EINVAL;
+
+       if (!rt)
+               return 0;
+
+       if (p->id.proto != IPPROTO_ESP)
+               return -EINVAL;
+
+       if (p->replay_window != 0)
+               return -EINVAL;
+
+       return 0;
+}
 
 static int verify_newsa_info(struct xfrm_usersa_info *p,
                             struct nlattr **attrs)
@@ -214,6 +233,8 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
                goto out;
        if ((err = verify_sec_ctx_len(attrs)))
                goto out;
+       if ((err = verify_replay(p, attrs)))
+               goto out;
 
        err = -EINVAL;
        switch (p->mode) {
@@ -345,6 +366,50 @@ static int attach_aead(struct xfrm_algo_aead **algpp, u8 *props,
        return 0;
 }
 
+static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_esn,
+                                        struct nlattr *rp)
+{
+       struct xfrm_replay_state_esn *up;
+
+       if (!replay_esn || !rp)
+               return 0;
+
+       up = nla_data(rp);
+
+       if (xfrm_replay_state_esn_len(replay_esn) !=
+                       xfrm_replay_state_esn_len(up))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int xfrm_alloc_replay_state_esn(struct xfrm_replay_state_esn **replay_esn,
+                                      struct xfrm_replay_state_esn **preplay_esn,
+                                      struct nlattr *rta)
+{
+       struct xfrm_replay_state_esn *p, *pp, *up;
+
+       if (!rta)
+               return 0;
+
+       up = nla_data(rta);
+
+       p = kmemdup(up, xfrm_replay_state_esn_len(up), GFP_KERNEL);
+       if (!p)
+               return -ENOMEM;
+
+       pp = kmemdup(up, xfrm_replay_state_esn_len(up), GFP_KERNEL);
+       if (!pp) {
+               kfree(p);
+               return -ENOMEM;
+       }
+
+       *replay_esn = p;
+       *preplay_esn = pp;
+
+       return 0;
+}
+
 static inline int xfrm_user_sec_ctx_size(struct xfrm_sec_ctx *xfrm_ctx)
 {
        int len = 0;
@@ -380,10 +445,20 @@ static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *
 static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs)
 {
        struct nlattr *rp = attrs[XFRMA_REPLAY_VAL];
+       struct nlattr *re = attrs[XFRMA_REPLAY_ESN_VAL];
        struct nlattr *lt = attrs[XFRMA_LTIME_VAL];
        struct nlattr *et = attrs[XFRMA_ETIMER_THRESH];
        struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH];
 
+       if (re) {
+               struct xfrm_replay_state_esn *replay_esn;
+               replay_esn = nla_data(re);
+               memcpy(x->replay_esn, replay_esn,
+                      xfrm_replay_state_esn_len(replay_esn));
+               memcpy(x->preplay_esn, replay_esn,
+                      xfrm_replay_state_esn_len(replay_esn));
+       }
+
        if (rp) {
                struct xfrm_replay_state *replay;
                replay = nla_data(rp);
@@ -459,7 +534,7 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
 
        xfrm_mark_get(attrs, &x->mark);
 
-       err = xfrm_init_state(x);
+       err = __xfrm_init_state(x, false);
        if (err)
                goto error;
 
@@ -467,16 +542,19 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
            security_xfrm_state_alloc(x, nla_data(attrs[XFRMA_SEC_CTX])))
                goto error;
 
+       if ((err = xfrm_alloc_replay_state_esn(&x->replay_esn, &x->preplay_esn,
+                                              attrs[XFRMA_REPLAY_ESN_VAL])))
+               goto error;
+
        x->km.seq = p->seq;
        x->replay_maxdiff = net->xfrm.sysctl_aevent_rseqth;
        /* sysctl_xfrm_aevent_etime is in 100ms units */
        x->replay_maxage = (net->xfrm.sysctl_aevent_etime*HZ)/XFRM_AE_ETH_M;
-       x->preplay.bitmap = 0;
-       x->preplay.seq = x->replay.seq+x->replay_maxdiff;
-       x->preplay.oseq = x->replay.oseq +x->replay_maxdiff;
 
-       /* override default values from above */
+       if ((err = xfrm_init_replay(x)))
+               goto error;
 
+       /* override default values from above */
        xfrm_update_ae_params(x, attrs);
 
        return x;
@@ -707,6 +785,10 @@ static int copy_to_user_state_extra(struct xfrm_state *x,
        if (xfrm_mark_put(skb, &x->mark))
                goto nla_put_failure;
 
+       if (x->replay_esn)
+               NLA_PUT(skb, XFRMA_REPLAY_ESN_VAL,
+                       xfrm_replay_state_esn_len(x->replay_esn), x->replay_esn);
+
        if (x->security && copy_sec_ctx(x->security, skb) < 0)
                goto nla_put_failure;
 
@@ -815,7 +897,7 @@ static int build_spdinfo(struct sk_buff *skb, struct net *net,
        u32 *f;
 
        nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0);
-       if (nlh == NULL) /* shouldnt really happen ... */
+       if (nlh == NULL) /* shouldn't really happen ... */
                return -EMSGSIZE;
 
        f = nlmsg_data(nlh);
@@ -875,7 +957,7 @@ static int build_sadinfo(struct sk_buff *skb, struct net *net,
        u32 *f;
 
        nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSADINFO, sizeof(u32), 0);
-       if (nlh == NULL) /* shouldnt really happen ... */
+       if (nlh == NULL) /* shouldn't really happen ... */
                return -EMSGSIZE;
 
        f = nlmsg_data(nlh);
@@ -1282,7 +1364,7 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
        if (!xp)
                return err;
 
-       /* shouldnt excl be based on nlh flags??
+       /* shouldn't excl be based on nlh flags??
         * Aha! this is anti-netlink really i.e  more pfkey derived
         * in netlink excl is a flag and you wouldnt need
         * a type XFRM_MSG_UPDPOLICY - JHS */
@@ -1576,10 +1658,14 @@ static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
        return 0;
 }
 
-static inline size_t xfrm_aevent_msgsize(void)
+static inline size_t xfrm_aevent_msgsize(struct xfrm_state *x)
 {
+       size_t replay_size = x->replay_esn ?
+                             xfrm_replay_state_esn_len(x->replay_esn) :
+                             sizeof(struct xfrm_replay_state);
+
        return NLMSG_ALIGN(sizeof(struct xfrm_aevent_id))
-              + nla_total_size(sizeof(struct xfrm_replay_state))
+              + nla_total_size(replay_size)
               + nla_total_size(sizeof(struct xfrm_lifetime_cur))
               + nla_total_size(sizeof(struct xfrm_mark))
               + nla_total_size(4) /* XFRM_AE_RTHR */
@@ -1604,7 +1690,13 @@ static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct
        id->reqid = x->props.reqid;
        id->flags = c->data.aevent;
 
-       NLA_PUT(skb, XFRMA_REPLAY_VAL, sizeof(x->replay), &x->replay);
+       if (x->replay_esn)
+               NLA_PUT(skb, XFRMA_REPLAY_ESN_VAL,
+                       xfrm_replay_state_esn_len(x->replay_esn),
+                       x->replay_esn);
+       else
+               NLA_PUT(skb, XFRMA_REPLAY_VAL, sizeof(x->replay), &x->replay);
+
        NLA_PUT(skb, XFRMA_LTIME_VAL, sizeof(x->curlft), &x->curlft);
 
        if (id->flags & XFRM_AE_RTHR)
@@ -1637,16 +1729,16 @@ static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
        struct xfrm_aevent_id *p = nlmsg_data(nlh);
        struct xfrm_usersa_id *id = &p->sa_id;
 
-       r_skb = nlmsg_new(xfrm_aevent_msgsize(), GFP_ATOMIC);
-       if (r_skb == NULL)
-               return -ENOMEM;
-
        mark = xfrm_mark_get(attrs, &m);
 
        x = xfrm_state_lookup(net, mark, &id->daddr, id->spi, id->proto, id->family);
-       if (x == NULL) {
-               kfree_skb(r_skb);
+       if (x == NULL)
                return -ESRCH;
+
+       r_skb = nlmsg_new(xfrm_aevent_msgsize(x), GFP_ATOMIC);
+       if (r_skb == NULL) {
+               xfrm_state_put(x);
+               return -ENOMEM;
        }
 
        /*
@@ -1678,9 +1770,10 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
        struct xfrm_mark m;
        struct xfrm_aevent_id *p = nlmsg_data(nlh);
        struct nlattr *rp = attrs[XFRMA_REPLAY_VAL];
+       struct nlattr *re = attrs[XFRMA_REPLAY_ESN_VAL];
        struct nlattr *lt = attrs[XFRMA_LTIME_VAL];
 
-       if (!lt && !rp)
+       if (!lt && !rp && !re)
                return err;
 
        /* pedantic mode - thou shalt sayeth replaceth */
@@ -1696,6 +1789,10 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
        if (x->km.state != XFRM_STATE_VALID)
                goto out;
 
+       err = xfrm_replay_verify_len(x->replay_esn, rp);
+       if (err)
+               goto out;
+
        spin_lock_bh(&x->lock);
        xfrm_update_ae_params(x, attrs);
        spin_unlock_bh(&x->lock);
@@ -2145,6 +2242,7 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
        [XFRMA_KMADDRESS]       = { .len = sizeof(struct xfrm_user_kmaddress) },
        [XFRMA_MARK]            = { .len = sizeof(struct xfrm_mark) },
        [XFRMA_TFCPAD]          = { .type = NLA_U32 },
+       [XFRMA_REPLAY_ESN_VAL]  = { .len = sizeof(struct xfrm_replay_state_esn) },
 };
 
 static struct xfrm_link {
@@ -2201,7 +2299,8 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                if (link->dump == NULL)
                        return -EINVAL;
 
-               return netlink_dump_start(net->xfrm.nlsk, skb, nlh, link->dump, link->done);
+               return netlink_dump_start(net->xfrm.nlsk, skb, nlh,
+                                         link->dump, link->done, 0);
        }
 
        err = nlmsg_parse(nlh, xfrm_msg_min[type], attrs, XFRMA_MAX,
@@ -2272,7 +2371,7 @@ static int xfrm_aevent_state_notify(struct xfrm_state *x, const struct km_event
        struct net *net = xs_net(x);
        struct sk_buff *skb;
 
-       skb = nlmsg_new(xfrm_aevent_msgsize(), GFP_ATOMIC);
+       skb = nlmsg_new(xfrm_aevent_msgsize(x), GFP_ATOMIC);
        if (skb == NULL)
                return -ENOMEM;
 
@@ -2326,6 +2425,8 @@ static inline size_t xfrm_sa_len(struct xfrm_state *x)
                l += nla_total_size(sizeof(*x->encap));
        if (x->tfcpad)
                l += nla_total_size(sizeof(x->tfcpad));
+       if (x->replay_esn)
+               l += nla_total_size(xfrm_replay_state_esn_len(x->replay_esn));
        if (x->security)
                l += nla_total_size(sizeof(struct xfrm_user_sec_ctx) +
                                    x->security->ctx_len);