From 4479ff76c43607b680f9349128d8493228b49dce Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Mon, 9 Sep 2013 09:39:01 +0200 Subject: xfrm: Fix replay size checking on async events We pass the wrong netlink attribute to xfrm_replay_verify_len(). It should be XFRMA_REPLAY_ESN_VAL and not XFRMA_REPLAY_VAL as we currently doing. This causes memory corruptions if the replay esn attribute has incorrect length. Fix this by passing the right attribute to xfrm_replay_verify_len(). Reported-by: Michael Rossberg Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/xfrm') diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 3f565e495ac6..4b26ceedff26 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -1856,7 +1856,7 @@ 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); + err = xfrm_replay_verify_len(x->replay_esn, re); if (err) goto out; -- cgit v1.2.3 From 33fce60d6a6e137035f8e23a89d7fd55f3a24cda Mon Sep 17 00:00:00 2001 From: Fan Du Date: Tue, 17 Sep 2013 15:14:13 +0800 Subject: xfrm: Guard IPsec anti replay window against replay bitmap For legacy IPsec anti replay mechanism: bitmap in struct xfrm_replay_state could only provide a 32 bits window size limit in current design, thus user level parameter sadb_sa_replay should honor this limit, otherwise misleading outputs("replay=244") by setkey -D will be: 192.168.25.2 192.168.22.2 esp mode=transport spi=147561170(0x08cb9ad2) reqid=0(0x00000000) E: aes-cbc 9a8d7468 7655cf0b 719d27be b0ddaac2 A: hmac-sha1 2d2115c2 ebf7c126 1c54f186 3b139b58 264a7331 seq=0x00000000 replay=244 flags=0x00000000 state=mature created: Sep 17 14:00:00 2013 current: Sep 17 14:00:22 2013 diff: 22(s) hard: 30(s) soft: 26(s) last: Sep 17 14:00:00 2013 hard: 0(s) soft: 0(s) current: 1408(bytes) hard: 0(bytes) soft: 0(bytes) allocated: 22 hard: 0 soft: 0 sadb_seq=1 pid=4854 refcnt=0 192.168.22.2 192.168.25.2 esp mode=transport spi=255302123(0x0f3799eb) reqid=0(0x00000000) E: aes-cbc 6485d990 f61a6bd5 e5660252 608ad282 A: hmac-sha1 0cca811a eb4fa893 c47ae56c 98f6e413 87379a88 seq=0x00000000 replay=244 flags=0x00000000 state=mature created: Sep 17 14:00:00 2013 current: Sep 17 14:00:22 2013 diff: 22(s) hard: 30(s) soft: 26(s) last: Sep 17 14:00:00 2013 hard: 0(s) soft: 0(s) current: 1408(bytes) hard: 0(bytes) soft: 0(bytes) allocated: 22 hard: 0 soft: 0 sadb_seq=0 pid=4854 refcnt=0 And also, optimizing xfrm_replay_check window checking by setting the desirable x->props.replay_window with only doing the comparison once for all when xfrm_state is first born. Signed-off-by: Fan Du Signed-off-by: Steffen Klassert --- net/key/af_key.c | 3 ++- net/xfrm/xfrm_replay.c | 3 +-- net/xfrm/xfrm_user.c | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) (limited to 'net/xfrm') diff --git a/net/key/af_key.c b/net/key/af_key.c index 9d585370c5b4..911ef03bf8fb 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -1098,7 +1098,8 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net, x->id.proto = proto; x->id.spi = sa->sadb_sa_spi; - x->props.replay_window = sa->sadb_sa_replay; + x->props.replay_window = min_t(unsigned int, sa->sadb_sa_replay, + (sizeof(x->replay.bitmap) * 8)); if (sa->sadb_sa_flags & SADB_SAFLAGS_NOECN) x->props.flags |= XFRM_STATE_NOECN; if (sa->sadb_sa_flags & SADB_SAFLAGS_DECAP_DSCP) diff --git a/net/xfrm/xfrm_replay.c b/net/xfrm/xfrm_replay.c index 8dafe6d3c6e4..eeca388effc7 100644 --- a/net/xfrm/xfrm_replay.c +++ b/net/xfrm/xfrm_replay.c @@ -129,8 +129,7 @@ static int xfrm_replay_check(struct xfrm_state *x, return 0; diff = x->replay.seq - seq; - if (diff >= min_t(unsigned int, x->props.replay_window, - sizeof(x->replay.bitmap) * 8)) { + if (diff >= x->props.replay_window) { x->stats.replay_window++; goto err; } diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 4b26ceedff26..f964d4c00ffb 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -446,7 +446,8 @@ static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info * memcpy(&x->sel, &p->sel, sizeof(x->sel)); memcpy(&x->lft, &p->lft, sizeof(x->lft)); x->props.mode = p->mode; - x->props.replay_window = p->replay_window; + x->props.replay_window = min_t(unsigned int, p->replay_window, + sizeof(x->replay.bitmap) * 8); x->props.reqid = p->reqid; x->props.family = p->family; memcpy(&x->props.saddr, &p->saddr, sizeof(x->props.saddr)); -- cgit v1.2.3 From cd808fc9a6c7cd3a4311d9d2cffc4adbeaef5f6c Mon Sep 17 00:00:00 2001 From: Thomas Egerer Date: Thu, 19 Sep 2013 13:19:19 +0200 Subject: xfrm: Fix aevent generation for each received packet If asynchronous events are enabled for a particular netlink socket, the notify function is called by the advance function. The notify function creates and dispatches a km_event if a replay timeout occurred, or at least replay_maxdiff packets have been received since the last asynchronous event has been sent. The function is supposed to return if neither of the two events were detected for a state, or replay_maxdiff is equal to zero. Replay_maxdiff is initialized in xfrm_state_construct to the value of the xfrm.sysctl_aevent_rseqth (2 by default), and updated if for a state if the netlink attribute XFRMA_REPLAY_THRESH is set. If, however, replay_maxdiff is set to zero, then all of the three notify implementations perform a break from the switch statement instead of checking whether a timeout occurred, and -- if not -- return. As a result an asynchronous event is generated for every replay update of a state that has a zero replay_maxdiff value. This patch modifies the notify functions such that they immediately return if replay_maxdiff has the value zero, unless a timeout occurred. Signed-off-by: Thomas Egerer Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_replay.c | 51 ++++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 24 deletions(-) (limited to 'net/xfrm') diff --git a/net/xfrm/xfrm_replay.c b/net/xfrm/xfrm_replay.c index eeca388effc7..dab57daae408 100644 --- a/net/xfrm/xfrm_replay.c +++ b/net/xfrm/xfrm_replay.c @@ -61,9 +61,9 @@ static void xfrm_replay_notify(struct xfrm_state *x, int event) switch (event) { case XFRM_REPLAY_UPDATE: - if (x->replay_maxdiff && - (x->replay.seq - x->preplay.seq < x->replay_maxdiff) && - (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff)) { + if (!x->replay_maxdiff || + ((x->replay.seq - x->preplay.seq < x->replay_maxdiff) && + (x->replay.oseq - x->preplay.oseq < x->replay_maxdiff))) { if (x->xflags & XFRM_TIME_DEFER) event = XFRM_REPLAY_TIMEOUT; else @@ -301,9 +301,10 @@ static void xfrm_replay_notify_bmp(struct xfrm_state *x, int event) switch (event) { case XFRM_REPLAY_UPDATE: - if (x->replay_maxdiff && - (replay_esn->seq - preplay_esn->seq < x->replay_maxdiff) && - (replay_esn->oseq - preplay_esn->oseq < x->replay_maxdiff)) { + if (!x->replay_maxdiff || + ((replay_esn->seq - preplay_esn->seq < x->replay_maxdiff) && + (replay_esn->oseq - preplay_esn->oseq + < x->replay_maxdiff))) { if (x->xflags & XFRM_TIME_DEFER) event = XFRM_REPLAY_TIMEOUT; else @@ -352,28 +353,30 @@ static void xfrm_replay_notify_esn(struct xfrm_state *x, int event) switch (event) { case XFRM_REPLAY_UPDATE: - if (!x->replay_maxdiff) - break; - - if (replay_esn->seq_hi == preplay_esn->seq_hi) - seq_diff = replay_esn->seq - preplay_esn->seq; - else - seq_diff = ~preplay_esn->seq + replay_esn->seq + 1; - - if (replay_esn->oseq_hi == preplay_esn->oseq_hi) - oseq_diff = replay_esn->oseq - preplay_esn->oseq; - else - oseq_diff = ~preplay_esn->oseq + replay_esn->oseq + 1; - - if (seq_diff < x->replay_maxdiff && - oseq_diff < x->replay_maxdiff) { + if (x->replay_maxdiff) { + if (replay_esn->seq_hi == preplay_esn->seq_hi) + seq_diff = replay_esn->seq - preplay_esn->seq; + else + seq_diff = ~preplay_esn->seq + replay_esn->seq + + 1; - if (x->xflags & XFRM_TIME_DEFER) - event = XFRM_REPLAY_TIMEOUT; + if (replay_esn->oseq_hi == preplay_esn->oseq_hi) + oseq_diff = replay_esn->oseq + - preplay_esn->oseq; else - return; + oseq_diff = ~preplay_esn->oseq + + replay_esn->oseq + 1; + + if (seq_diff >= x->replay_maxdiff || + oseq_diff >= x->replay_maxdiff) + break; } + if (x->xflags & XFRM_TIME_DEFER) + event = XFRM_REPLAY_TIMEOUT; + else + return; + break; case XFRM_REPLAY_TIMEOUT: -- cgit v1.2.3 From e7d8f6cb2f8735693396872f4608bbe305e8baee Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Tue, 8 Oct 2013 10:49:45 +0200 Subject: xfrm: Add refcount handling to queued policies We need to ensure that policies can't go away as long as the hold timer is armed, so take a refcont when we arm the timer and drop one if we delete it. Bug was introduced with git commit a0073fe18 ("xfrm: Add a state resolution packet queue") Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_policy.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) (limited to 'net/xfrm') diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index ed38d5d81f9e..5f9be976770e 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -334,7 +334,8 @@ static void xfrm_policy_kill(struct xfrm_policy *policy) atomic_inc(&policy->genid); - del_timer(&policy->polq.hold_timer); + if (del_timer(&policy->polq.hold_timer)) + xfrm_pol_put(policy); xfrm_queue_purge(&policy->polq.hold_queue); if (del_timer(&policy->timer)) @@ -589,7 +590,8 @@ static void xfrm_policy_requeue(struct xfrm_policy *old, spin_lock_bh(&pq->hold_queue.lock); skb_queue_splice_init(&pq->hold_queue, &list); - del_timer(&pq->hold_timer); + if (del_timer(&pq->hold_timer)) + xfrm_pol_put(old); spin_unlock_bh(&pq->hold_queue.lock); if (skb_queue_empty(&list)) @@ -600,7 +602,8 @@ static void xfrm_policy_requeue(struct xfrm_policy *old, spin_lock_bh(&pq->hold_queue.lock); skb_queue_splice(&list, &pq->hold_queue); pq->timeout = XFRM_QUEUE_TMO_MIN; - mod_timer(&pq->hold_timer, jiffies); + if (!mod_timer(&pq->hold_timer, jiffies)) + xfrm_pol_hold(new); spin_unlock_bh(&pq->hold_queue.lock); } @@ -1787,8 +1790,9 @@ static void xfrm_policy_queue_process(unsigned long arg) goto purge_queue; pq->timeout = pq->timeout << 1; - mod_timer(&pq->hold_timer, jiffies + pq->timeout); - return; + if (!mod_timer(&pq->hold_timer, jiffies + pq->timeout)) + xfrm_pol_hold(pol); + goto out; } dst_release(dst); @@ -1819,11 +1823,14 @@ static void xfrm_policy_queue_process(unsigned long arg) err = dst_output(skb); } +out: + xfrm_pol_put(pol); return; purge_queue: pq->timeout = 0; xfrm_queue_purge(&pq->hold_queue); + xfrm_pol_put(pol); } static int xdst_queue_output(struct sk_buff *skb) @@ -1831,7 +1838,8 @@ static int xdst_queue_output(struct sk_buff *skb) unsigned long sched_next; struct dst_entry *dst = skb_dst(skb); struct xfrm_dst *xdst = (struct xfrm_dst *) dst; - struct xfrm_policy_queue *pq = &xdst->pols[0]->polq; + struct xfrm_policy *pol = xdst->pols[0]; + struct xfrm_policy_queue *pq = &pol->polq; if (pq->hold_queue.qlen > XFRM_MAX_QUEUE_LEN) { kfree_skb(skb); @@ -1850,10 +1858,12 @@ static int xdst_queue_output(struct sk_buff *skb) if (del_timer(&pq->hold_timer)) { if (time_before(pq->hold_timer.expires, sched_next)) sched_next = pq->hold_timer.expires; + xfrm_pol_put(pol); } __skb_queue_tail(&pq->hold_queue, skb); - mod_timer(&pq->hold_timer, sched_next); + if (!mod_timer(&pq->hold_timer, sched_next)) + xfrm_pol_hold(pol); spin_unlock_bh(&pq->hold_queue.lock); -- cgit v1.2.3 From 2bb53e2557964c2c5368a0392cf3b3b63a288cd0 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Tue, 8 Oct 2013 10:49:51 +0200 Subject: xfrm: check for a vaild skb in xfrm_policy_queue_process We might dreference a NULL pointer if the hold_queue is empty, so add a check to avoid this. Bug was introduced with git commit a0073fe18 ("xfrm: Add a state resolution packet queue") Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_policy.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net/xfrm') diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 5f9be976770e..76e1873811d4 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1772,6 +1772,10 @@ static void xfrm_policy_queue_process(unsigned long arg) spin_lock(&pq->hold_queue.lock); skb = skb_peek(&pq->hold_queue); + if (!skb) { + spin_unlock(&pq->hold_queue.lock); + goto out; + } dst = skb_dst(skb); sk = skb->sk; xfrm_decode_session(skb, &fl, dst->ops->family); -- cgit v1.2.3