mirror of
https://github.com/strongswan/strongswan.git
synced 2025-10-04 00:00:14 -04:00
ike: Track unprocessed initial IKE messages like half-open IKE_SAs
This should make the DoS limits (cookie_threshold[_ip] and block_threshold) more accurate so that it won't be possible to create lots of jobs from spoofed IP addresses before half-open IKE_SAs are actually created from these jobs to enforce those limits. Note that retransmits are tracked as half-open SAs until they are processed/dismissed as the check only happens in checkout_by_message(). Increasing the count in process_message_job_create() avoids issues with missing calls to track_init() before calling checkout_by_message() (e.g. when processing fragmented IKEv1 messages, which are reinjected via a process message job).
This commit is contained in:
parent
d8104b7c69
commit
b866ee88bf
@ -28,7 +28,8 @@ charon.accept_unencrypted_mainmode_messages = no
|
|||||||
example, some SonicWall boxes).
|
example, some SonicWall boxes).
|
||||||
|
|
||||||
charon.block_threshold = 5
|
charon.block_threshold = 5
|
||||||
Maximum number of half-open IKE_SAs for a single peer IP.
|
Maximum number of half-open IKE_SAs (including unprocessed IKE_SA_INITs)
|
||||||
|
for a single peer IP.
|
||||||
|
|
||||||
charon.cert_cache = yes
|
charon.cert_cache = yes
|
||||||
Whether relations in validated certificate chains should be cached in
|
Whether relations in validated certificate chains should be cached in
|
||||||
@ -70,11 +71,12 @@ charon.close_ike_on_child_failure = no
|
|||||||
Close the IKE_SA if setup of the CHILD_SA along with IKE_AUTH failed.
|
Close the IKE_SA if setup of the CHILD_SA along with IKE_AUTH failed.
|
||||||
|
|
||||||
charon.cookie_threshold = 30
|
charon.cookie_threshold = 30
|
||||||
Number of half-open IKE_SAs that activate the cookie mechanism.
|
Number of half-open IKE_SAs (including unprocessed IKE_SA_INITs) that
|
||||||
|
activate the cookie mechanism.
|
||||||
|
|
||||||
charon.cookie_threshold_ip = 3
|
charon.cookie_threshold_ip = 3
|
||||||
Number of half-open IKE_SAs for a single peer IP that activate the cookie
|
Number of half-open IKE_SAs (including unprocessed IKE_SA_INITs) for a
|
||||||
mechanism.
|
single peer IP that activate the cookie mechanism.
|
||||||
|
|
||||||
charon.crypto_test.bench = no
|
charon.crypto_test.bench = no
|
||||||
Benchmark crypto algorithms and order them by efficiency.
|
Benchmark crypto algorithms and order them by efficiency.
|
||||||
|
@ -133,5 +133,22 @@ process_message_job_t *process_message_job_create(message_t *message)
|
|||||||
.message = message,
|
.message = message,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (message->get_request(message) &&
|
||||||
|
message->get_exchange_type(message) == IKE_SA_INIT)
|
||||||
|
{
|
||||||
|
charon->ike_sa_manager->track_init(charon->ike_sa_manager,
|
||||||
|
message->get_source(message));
|
||||||
|
}
|
||||||
|
if (message->get_exchange_type(message) == ID_PROT ||
|
||||||
|
message->get_exchange_type(message) == AGGRESSIVE)
|
||||||
|
{
|
||||||
|
ike_sa_id_t *id = message->get_ike_sa_id(message);
|
||||||
|
|
||||||
|
if (id->get_responder_spi(id) == 0)
|
||||||
|
{
|
||||||
|
charon->ike_sa_manager->track_init(charon->ike_sa_manager,
|
||||||
|
message->get_source(message));
|
||||||
|
}
|
||||||
|
}
|
||||||
return &(this->public);
|
return &(this->public);
|
||||||
}
|
}
|
||||||
|
@ -786,17 +786,16 @@ static bool wait_for_entry(private_ike_sa_manager_t *this, entry_t *entry,
|
|||||||
/**
|
/**
|
||||||
* Put a half-open SA into the hash table.
|
* Put a half-open SA into the hash table.
|
||||||
*/
|
*/
|
||||||
static void put_half_open(private_ike_sa_manager_t *this, entry_t *entry)
|
static void put_half_open(private_ike_sa_manager_t *this, host_t *ip,
|
||||||
|
bool initiator)
|
||||||
{
|
{
|
||||||
table_item_t *item;
|
table_item_t *item;
|
||||||
u_int row, segment;
|
u_int row, segment;
|
||||||
rwlock_t *lock;
|
rwlock_t *lock;
|
||||||
ike_sa_id_t *ike_id;
|
|
||||||
half_open_t *half_open;
|
half_open_t *half_open;
|
||||||
chunk_t addr;
|
chunk_t addr;
|
||||||
|
|
||||||
ike_id = entry->ike_sa_id;
|
addr = ip->get_address(ip);
|
||||||
addr = entry->other->get_address(entry->other);
|
|
||||||
row = chunk_hash(addr) & this->table_mask;
|
row = chunk_hash(addr) & this->table_mask;
|
||||||
segment = row & this->segment_mask;
|
segment = row & this->segment_mask;
|
||||||
lock = this->half_open_segments[segment].lock;
|
lock = this->half_open_segments[segment].lock;
|
||||||
@ -826,7 +825,7 @@ static void put_half_open(private_ike_sa_manager_t *this, entry_t *entry)
|
|||||||
}
|
}
|
||||||
half_open->count++;
|
half_open->count++;
|
||||||
ref_get(&this->half_open_count);
|
ref_get(&this->half_open_count);
|
||||||
if (!ike_id->is_initiator(ike_id))
|
if (!initiator)
|
||||||
{
|
{
|
||||||
half_open->count_responder++;
|
half_open->count_responder++;
|
||||||
ref_get(&this->half_open_count_responder);
|
ref_get(&this->half_open_count_responder);
|
||||||
@ -838,16 +837,15 @@ static void put_half_open(private_ike_sa_manager_t *this, entry_t *entry)
|
|||||||
/**
|
/**
|
||||||
* Remove a half-open SA from the hash table.
|
* Remove a half-open SA from the hash table.
|
||||||
*/
|
*/
|
||||||
static void remove_half_open(private_ike_sa_manager_t *this, entry_t *entry)
|
static void remove_half_open(private_ike_sa_manager_t *this, host_t *ip,
|
||||||
|
bool initiator)
|
||||||
{
|
{
|
||||||
table_item_t *item, *prev = NULL;
|
table_item_t *item, *prev = NULL;
|
||||||
u_int row, segment;
|
u_int row, segment;
|
||||||
rwlock_t *lock;
|
rwlock_t *lock;
|
||||||
ike_sa_id_t *ike_id;
|
|
||||||
chunk_t addr;
|
chunk_t addr;
|
||||||
|
|
||||||
ike_id = entry->ike_sa_id;
|
addr = ip->get_address(ip);
|
||||||
addr = entry->other->get_address(entry->other);
|
|
||||||
row = chunk_hash(addr) & this->table_mask;
|
row = chunk_hash(addr) & this->table_mask;
|
||||||
segment = row & this->segment_mask;
|
segment = row & this->segment_mask;
|
||||||
lock = this->half_open_segments[segment].lock;
|
lock = this->half_open_segments[segment].lock;
|
||||||
@ -859,7 +857,7 @@ static void remove_half_open(private_ike_sa_manager_t *this, entry_t *entry)
|
|||||||
|
|
||||||
if (chunk_equals(addr, half_open->other))
|
if (chunk_equals(addr, half_open->other))
|
||||||
{
|
{
|
||||||
if (!ike_id->is_initiator(ike_id))
|
if (!initiator)
|
||||||
{
|
{
|
||||||
half_open->count_responder--;
|
half_open->count_responder--;
|
||||||
ignore_result(ref_put(&this->half_open_count_responder));
|
ignore_result(ref_put(&this->half_open_count_responder));
|
||||||
@ -905,7 +903,7 @@ static u_int create_and_put_entry(private_ike_sa_manager_t *this,
|
|||||||
{
|
{
|
||||||
(*entry)->half_open = TRUE;
|
(*entry)->half_open = TRUE;
|
||||||
(*entry)->other = other->clone(other);
|
(*entry)->other = other->clone(other);
|
||||||
put_half_open(this, *entry);
|
put_half_open(this, (*entry)->other, ike_sa_id->is_initiator(ike_sa_id));
|
||||||
}
|
}
|
||||||
return put_entry(this, *entry);
|
return put_entry(this, *entry);
|
||||||
}
|
}
|
||||||
@ -1314,7 +1312,7 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
|
|||||||
ike_sa_t *ike_sa = NULL;
|
ike_sa_t *ike_sa = NULL;
|
||||||
ike_sa_id_t *id;
|
ike_sa_id_t *id;
|
||||||
ike_version_t ike_version;
|
ike_version_t ike_version;
|
||||||
bool is_init = FALSE;
|
bool is_init = FALSE, untrack_half_open = FALSE;
|
||||||
|
|
||||||
id = message->get_ike_sa_id(message);
|
id = message->get_ike_sa_id(message);
|
||||||
/* clone the IKE_SA ID so we can modify the initiator flag */
|
/* clone the IKE_SA ID so we can modify the initiator flag */
|
||||||
@ -1359,6 +1357,7 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
|
|||||||
uint64_t our_spi;
|
uint64_t our_spi;
|
||||||
chunk_t hash;
|
chunk_t hash;
|
||||||
|
|
||||||
|
untrack_half_open = TRUE;
|
||||||
hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
|
hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
|
||||||
if (!hasher || !get_init_hash(hasher, message, &hash))
|
if (!hasher || !get_init_hash(hasher, message, &hash))
|
||||||
{
|
{
|
||||||
@ -1386,6 +1385,10 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
|
|||||||
entry->ike_sa_id = id;
|
entry->ike_sa_id = id;
|
||||||
entry->processing = get_message_id_or_hash(message);
|
entry->processing = get_message_id_or_hash(message);
|
||||||
entry->init_hash = hash;
|
entry->init_hash = hash;
|
||||||
|
entry->half_open = TRUE;
|
||||||
|
entry->other = message->get_source(message);
|
||||||
|
entry->other = entry->other->clone(entry->other);
|
||||||
|
untrack_half_open = FALSE;
|
||||||
|
|
||||||
segment = put_entry(this, entry);
|
segment = put_entry(this, entry);
|
||||||
entry->checked_out = thread_current();
|
entry->checked_out = thread_current();
|
||||||
@ -1426,6 +1429,9 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
|
|||||||
/* it looks like we already handled this init message to some degree */
|
/* it looks like we already handled this init message to some degree */
|
||||||
id->set_responder_spi(id, our_spi);
|
id->set_responder_spi(id, our_spi);
|
||||||
chunk_free(&hash);
|
chunk_free(&hash);
|
||||||
|
/* untrack the duplicate before waiting for the checkout */
|
||||||
|
remove_half_open(this, message->get_source(message), FALSE);
|
||||||
|
untrack_half_open = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_entry_by_id(this, id, &entry, &segment) == SUCCESS)
|
if (get_entry_by_id(this, id, &entry, &segment) == SUCCESS)
|
||||||
@ -1464,6 +1470,10 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
|
|||||||
id->destroy(id);
|
id->destroy(id);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
if (untrack_half_open)
|
||||||
|
{
|
||||||
|
remove_half_open(this, message->get_source(message), FALSE);
|
||||||
|
}
|
||||||
charon->bus->set_sa(charon->bus, ike_sa);
|
charon->bus->set_sa(charon->bus, ike_sa);
|
||||||
if (!ike_sa)
|
if (!ike_sa)
|
||||||
{
|
{
|
||||||
@ -1886,15 +1896,18 @@ METHOD(ike_sa_manager_t, checkin, void,
|
|||||||
{
|
{
|
||||||
/* not half open anymore */
|
/* not half open anymore */
|
||||||
entry->half_open = FALSE;
|
entry->half_open = FALSE;
|
||||||
remove_half_open(this, entry);
|
remove_half_open(this, entry->other,
|
||||||
|
entry->ike_sa_id->is_initiator(entry->ike_sa_id));
|
||||||
}
|
}
|
||||||
else if (entry->half_open && !other->ip_equals(other, entry->other))
|
else if (entry->half_open && !other->ip_equals(other, entry->other))
|
||||||
{
|
{
|
||||||
/* the other host's IP has changed, we must update the hash table */
|
/* the other host's IP has changed, we must update the hash table */
|
||||||
remove_half_open(this, entry);
|
remove_half_open(this, entry->other,
|
||||||
|
entry->ike_sa_id->is_initiator(entry->ike_sa_id));
|
||||||
DESTROY_IF(entry->other);
|
DESTROY_IF(entry->other);
|
||||||
entry->other = other->clone(other);
|
entry->other = other->clone(other);
|
||||||
put_half_open(this, entry);
|
put_half_open(this, entry->other,
|
||||||
|
entry->ike_sa_id->is_initiator(entry->ike_sa_id));
|
||||||
}
|
}
|
||||||
else if (!entry->half_open &&
|
else if (!entry->half_open &&
|
||||||
ike_sa->get_state(ike_sa) == IKE_CONNECTING)
|
ike_sa->get_state(ike_sa) == IKE_CONNECTING)
|
||||||
@ -1902,7 +1915,8 @@ METHOD(ike_sa_manager_t, checkin, void,
|
|||||||
/* this is a new half-open SA */
|
/* this is a new half-open SA */
|
||||||
entry->half_open = TRUE;
|
entry->half_open = TRUE;
|
||||||
entry->other = other->clone(other);
|
entry->other = other->clone(other);
|
||||||
put_half_open(this, entry);
|
put_half_open(this, entry->other,
|
||||||
|
entry->ike_sa_id->is_initiator(entry->ike_sa_id));
|
||||||
}
|
}
|
||||||
entry->condvar->signal(entry->condvar);
|
entry->condvar->signal(entry->condvar);
|
||||||
}
|
}
|
||||||
@ -2007,7 +2021,8 @@ METHOD(ike_sa_manager_t, checkin_and_destroy, void,
|
|||||||
|
|
||||||
if (entry->half_open)
|
if (entry->half_open)
|
||||||
{
|
{
|
||||||
remove_half_open(this, entry);
|
remove_half_open(this, entry->other,
|
||||||
|
entry->ike_sa_id->is_initiator(entry->ike_sa_id));
|
||||||
}
|
}
|
||||||
if (entry->my_id && entry->other_id)
|
if (entry->my_id && entry->other_id)
|
||||||
{
|
{
|
||||||
@ -2318,6 +2333,12 @@ METHOD(ike_sa_manager_t, get_half_open_count, u_int,
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
METHOD(ike_sa_manager_t, track_init, void,
|
||||||
|
private_ike_sa_manager_t *this, host_t *ip)
|
||||||
|
{
|
||||||
|
put_half_open(this, ip, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
METHOD(ike_sa_manager_t, set_spi_cb, void,
|
METHOD(ike_sa_manager_t, set_spi_cb, void,
|
||||||
private_ike_sa_manager_t *this, spi_cb_t callback, void *data)
|
private_ike_sa_manager_t *this, spi_cb_t callback, void *data)
|
||||||
{
|
{
|
||||||
@ -2342,7 +2363,8 @@ static void destroy_all_entries(private_ike_sa_manager_t *this)
|
|||||||
charon->bus->set_sa(charon->bus, entry->ike_sa);
|
charon->bus->set_sa(charon->bus, entry->ike_sa);
|
||||||
if (entry->half_open)
|
if (entry->half_open)
|
||||||
{
|
{
|
||||||
remove_half_open(this, entry);
|
remove_half_open(this, entry->other,
|
||||||
|
entry->ike_sa_id->is_initiator(entry->ike_sa_id));
|
||||||
}
|
}
|
||||||
if (entry->my_id && entry->other_id)
|
if (entry->my_id && entry->other_id)
|
||||||
{
|
{
|
||||||
@ -2494,6 +2516,7 @@ ike_sa_manager_t *ike_sa_manager_create()
|
|||||||
.checkin_and_destroy = _checkin_and_destroy,
|
.checkin_and_destroy = _checkin_and_destroy,
|
||||||
.get_count = _get_count,
|
.get_count = _get_count,
|
||||||
.get_half_open_count = _get_half_open_count,
|
.get_half_open_count = _get_half_open_count,
|
||||||
|
.track_init = _track_init,
|
||||||
.flush = _flush,
|
.flush = _flush,
|
||||||
.set_spi_cb = _set_spi_cb,
|
.set_spi_cb = _set_spi_cb,
|
||||||
.destroy = _destroy,
|
.destroy = _destroy,
|
||||||
|
@ -85,6 +85,16 @@ struct ike_sa_manager_t {
|
|||||||
*/
|
*/
|
||||||
ike_sa_t* (*checkout) (ike_sa_manager_t* this, ike_sa_id_t *sa_id);
|
ike_sa_t* (*checkout) (ike_sa_manager_t* this, ike_sa_id_t *sa_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Track an initial IKE message as responder by increasing the number of
|
||||||
|
* half-open IKE_SAs.
|
||||||
|
*
|
||||||
|
* @note It's expected that checkout_by_message() is called afterwards.
|
||||||
|
*
|
||||||
|
* @param ip IP of sender
|
||||||
|
*/
|
||||||
|
void (*track_init)(ike_sa_manager_t *this, host_t *ip);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checkout an IKE_SA by a message.
|
* Checkout an IKE_SA by a message.
|
||||||
*
|
*
|
||||||
@ -97,10 +107,13 @@ struct ike_sa_manager_t {
|
|||||||
* retransmission. If so, we have to drop the message, we would
|
* retransmission. If so, we have to drop the message, we would
|
||||||
* create another unneeded IKE_SA for each retransmitted packet.
|
* create another unneeded IKE_SA for each retransmitted packet.
|
||||||
*
|
*
|
||||||
* A call to checkout_by_message() returns a (maybe new created) IKE_SA.
|
* A call to checkout_by_message() returns a (maybe newly created) IKE_SA.
|
||||||
* If processing the message does not make sense (for the reasons above),
|
* If processing the message does not make sense (for the reasons above),
|
||||||
* NULL is returned.
|
* NULL is returned.
|
||||||
*
|
*
|
||||||
|
* @note For initial IKE messages, track_init() has to be called before
|
||||||
|
* calling this.
|
||||||
|
*
|
||||||
* @param ike_sa_id the SA identifier, will be updated
|
* @param ike_sa_id the SA identifier, will be updated
|
||||||
* @returns
|
* @returns
|
||||||
* - checked out/created IKE_SA
|
* - checked out/created IKE_SA
|
||||||
|
Loading…
x
Reference in New Issue
Block a user