libipsec: Make sure to expire the right SA
authorTobias Brunner <tobias@strongswan.org>
Mon, 14 Aug 2017 14:03:54 +0000 (16:03 +0200)
committerTobias Brunner <tobias@strongswan.org>
Mon, 18 Sep 2017 08:51:39 +0000 (10:51 +0200)
If an IPsec SA is actually replaced with a rekeying its entry in the
manager is freed. That means that when the hard expire is triggered a
new entry might be found at the cached pointer location.  So we have
to make sure we trigger the expire only if we found the right SA.

We could use SPI and addresses for the lookup, but this here requires
a bit less memory and is just a small change. Another option would be to
somehow cancel the queued job, but our scheduler doesn't allow that at
the moment.

Fixes #2399.

src/libipsec/ipsec_sa_mgr.c

index 957d930..44d3524 100644 (file)
@@ -107,6 +107,11 @@ typedef struct {
        ipsec_sa_entry_t *entry;
 
        /**
+        * SPI of the expired entry
+        */
+       uint32_t spi;
+
+       /**
         * 0 if this is a hard expire, otherwise the offset in s (soft->hard)
         */
        uint32_t hard_offset;
@@ -314,8 +319,9 @@ static job_requeue_t sa_expired(ipsec_sa_expired_t *expired)
        private_ipsec_sa_mgr_t *this = expired->manager;
 
        this->mutex->lock(this->mutex);
-       if (this->sas->find_first(this->sas, NULL, (void**)&expired->entry))
-       {
+       if (this->sas->find_first(this->sas, NULL, (void**)&expired->entry) &&
+               expired->spi == expired->entry->sa->get_spi(expired->entry->sa))
+       {       /* only if we find the right SA at this pointer location */
                uint32_t hard_offset;
 
                hard_offset = expired->hard_offset;
@@ -355,6 +361,7 @@ static void schedule_expiration(private_ipsec_sa_mgr_t *this,
        INIT(expired,
                .manager = this,
                .entry = entry,
+               .spi = entry->sa->get_spi(entry->sa),
        );
 
        /* schedule a rekey first, a hard timeout will be scheduled then, if any */