|
@@ -119,6 +119,19 @@ 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 (!rt)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (p->replay_window != 0)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
static int verify_newsa_info(struct xfrm_usersa_info *p,
|
|
|
struct nlattr **attrs)
|
|
@@ -214,6 +227,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 +360,33 @@ static int attach_aead(struct xfrm_algo_aead **algpp, u8 *props,
|
|
|
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 +422,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);
|
|
@@ -467,13 +519,14 @@ 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;
|
|
|
|
|
|
if ((err = xfrm_init_replay(x)))
|
|
|
goto error;
|
|
@@ -709,6 +762,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;
|
|
|
|
|
@@ -1578,10 +1635,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 */
|
|
@@ -1606,7 +1667,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)
|
|
@@ -1639,16 +1706,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;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -1680,9 +1747,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 */
|
|
@@ -2147,6 +2215,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 {
|
|
@@ -2274,7 +2343,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;
|
|
|
|
|
@@ -2328,6 +2397,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);
|