ha: Delete passive IKE_SA on other node after half-open timeout
[strongswan.git] / src / libcharon / plugins / ha / ha_ike.c
1 /*
2 * Copyright (C) 2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "ha_ike.h"
17
18 #include <sa/ikev2/keymat_v2.h>
19 #include <sa/ikev1/keymat_v1.h>
20
21 typedef struct private_ha_ike_t private_ha_ike_t;
22
23 /**
24 * Private data of an ha_ike_t object.
25 */
26 struct private_ha_ike_t {
27
28 /**
29 * Public ha_ike_t interface.
30 */
31 ha_ike_t public;
32
33 /**
34 * socket we use for syncing
35 */
36 ha_socket_t *socket;
37
38 /**
39 * tunnel securing sync messages
40 */
41 ha_tunnel_t *tunnel;
42
43 /**
44 * message cache
45 */
46 ha_cache_t *cache;
47 };
48
49 /**
50 * Return condition if it is set on ike_sa
51 */
52 static ike_condition_t copy_condition(ike_sa_t *ike_sa, ike_condition_t cond)
53 {
54 if (ike_sa->has_condition(ike_sa, cond))
55 {
56 return cond;
57 }
58 return 0;
59 }
60
61 /**
62 * Return extension if it is supported by peers IKE_SA
63 */
64 static ike_extension_t copy_extension(ike_sa_t *ike_sa, ike_extension_t ext)
65 {
66 if (ike_sa->supports_extension(ike_sa, ext))
67 {
68 return ext;
69 }
70 return 0;
71 }
72
73 METHOD(listener_t, ike_keys, bool,
74 private_ha_ike_t *this, ike_sa_t *ike_sa, diffie_hellman_t *dh,
75 chunk_t dh_other, chunk_t nonce_i, chunk_t nonce_r, ike_sa_t *rekey,
76 shared_key_t *shared)
77 {
78 ha_message_t *m;
79 chunk_t secret;
80 proposal_t *proposal;
81 uint16_t alg, len;
82
83 if (this->tunnel && this->tunnel->is_sa(this->tunnel, ike_sa))
84 { /* do not sync SA between nodes */
85 return TRUE;
86 }
87 if (!dh->get_shared_secret(dh, &secret))
88 {
89 return TRUE;
90 }
91
92 m = ha_message_create(HA_IKE_ADD);
93 m->add_attribute(m, HA_IKE_VERSION, ike_sa->get_version(ike_sa));
94 m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa));
95
96 if (rekey && rekey->get_version(rekey) == IKEV2)
97 {
98 chunk_t skd;
99 keymat_v2_t *keymat;
100
101 keymat = (keymat_v2_t*)rekey->get_keymat(rekey);
102 m->add_attribute(m, HA_IKE_REKEY_ID, rekey->get_id(rekey));
103 m->add_attribute(m, HA_ALG_OLD_PRF, keymat->get_skd(keymat, &skd));
104 m->add_attribute(m, HA_OLD_SKD, skd);
105 }
106
107 proposal = ike_sa->get_proposal(ike_sa);
108 if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &alg, &len))
109 {
110 m->add_attribute(m, HA_ALG_ENCR, alg);
111 if (len)
112 {
113 m->add_attribute(m, HA_ALG_ENCR_LEN, len);
114 }
115 }
116 if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &alg, NULL))
117 {
118 m->add_attribute(m, HA_ALG_INTEG, alg);
119 }
120 if (proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &alg, NULL))
121 {
122 m->add_attribute(m, HA_ALG_PRF, alg);
123 }
124 if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, &alg, NULL))
125 {
126 m->add_attribute(m, HA_ALG_DH, alg);
127 }
128 m->add_attribute(m, HA_NONCE_I, nonce_i);
129 m->add_attribute(m, HA_NONCE_R, nonce_r);
130 m->add_attribute(m, HA_SECRET, secret);
131 chunk_clear(&secret);
132 if (ike_sa->get_version(ike_sa) == IKEV1)
133 {
134 if (dh->get_my_public_value(dh, &secret))
135 {
136 m->add_attribute(m, HA_LOCAL_DH, secret);
137 chunk_free(&secret);
138 }
139 m->add_attribute(m, HA_REMOTE_DH, dh_other);
140 if (shared)
141 {
142 m->add_attribute(m, HA_PSK, shared->get_key(shared));
143 }
144 }
145 m->add_attribute(m, HA_REMOTE_ADDR, ike_sa->get_other_host(ike_sa));
146
147 this->socket->push(this->socket, m);
148 this->cache->cache(this->cache, ike_sa, m);
149
150 return TRUE;
151 }
152
153 METHOD(listener_t, ike_updown, bool,
154 private_ha_ike_t *this, ike_sa_t *ike_sa, bool up)
155 {
156 ha_message_t *m;
157
158 if (ike_sa->get_state(ike_sa) == IKE_PASSIVE)
159 { /* only sync active IKE_SAs */
160 return TRUE;
161 }
162 if (this->tunnel && this->tunnel->is_sa(this->tunnel, ike_sa))
163 { /* do not sync SA between nodes */
164 return TRUE;
165 }
166
167 if (up)
168 {
169 enumerator_t *enumerator;
170 peer_cfg_t *peer_cfg;
171 uint32_t extension, condition;
172 host_t *addr;
173 ike_sa_id_t *id;
174 identification_t *eap_id;
175
176 peer_cfg = ike_sa->get_peer_cfg(ike_sa);
177
178 condition = copy_condition(ike_sa, COND_NAT_ANY)
179 | copy_condition(ike_sa, COND_NAT_HERE)
180 | copy_condition(ike_sa, COND_NAT_THERE)
181 | copy_condition(ike_sa, COND_NAT_FAKE)
182 | copy_condition(ike_sa, COND_EAP_AUTHENTICATED)
183 | copy_condition(ike_sa, COND_CERTREQ_SEEN)
184 | copy_condition(ike_sa, COND_ORIGINAL_INITIATOR)
185 | copy_condition(ike_sa, COND_STALE)
186 | copy_condition(ike_sa, COND_INIT_CONTACT_SEEN)
187 | copy_condition(ike_sa, COND_XAUTH_AUTHENTICATED);
188
189 extension = copy_extension(ike_sa, EXT_NATT)
190 | copy_extension(ike_sa, EXT_MOBIKE)
191 | copy_extension(ike_sa, EXT_HASH_AND_URL)
192 | copy_extension(ike_sa, EXT_MULTIPLE_AUTH)
193 | copy_extension(ike_sa, EXT_STRONGSWAN)
194 | copy_extension(ike_sa, EXT_EAP_ONLY_AUTHENTICATION)
195 | copy_extension(ike_sa, EXT_MS_WINDOWS)
196 | copy_extension(ike_sa, EXT_XAUTH)
197 | copy_extension(ike_sa, EXT_DPD);
198
199 id = ike_sa->get_id(ike_sa);
200
201 m = ha_message_create(HA_IKE_UPDATE);
202 m->add_attribute(m, HA_IKE_ID, id);
203 m->add_attribute(m, HA_LOCAL_ID, ike_sa->get_my_id(ike_sa));
204 m->add_attribute(m, HA_REMOTE_ID, ike_sa->get_other_id(ike_sa));
205 eap_id = ike_sa->get_other_eap_id(ike_sa);
206 if (!eap_id->equals(eap_id, ike_sa->get_other_id(ike_sa)))
207 {
208 m->add_attribute(m, HA_REMOTE_EAP_ID, eap_id);
209 }
210 m->add_attribute(m, HA_LOCAL_ADDR, ike_sa->get_my_host(ike_sa));
211 m->add_attribute(m, HA_REMOTE_ADDR, ike_sa->get_other_host(ike_sa));
212 m->add_attribute(m, HA_CONDITIONS, condition);
213 m->add_attribute(m, HA_EXTENSIONS, extension);
214 m->add_attribute(m, HA_CONFIG_NAME, peer_cfg->get_name(peer_cfg));
215 enumerator = ike_sa->create_peer_address_enumerator(ike_sa);
216 while (enumerator->enumerate(enumerator, (void**)&addr))
217 {
218 m->add_attribute(m, HA_PEER_ADDR, addr);
219 }
220 enumerator->destroy(enumerator);
221 }
222 else
223 {
224 m = ha_message_create(HA_IKE_DELETE);
225 m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa));
226 }
227 this->socket->push(this->socket, m);
228 this->cache->cache(this->cache, ike_sa, m);
229 return TRUE;
230 }
231
232 METHOD(listener_t, ike_rekey, bool,
233 private_ha_ike_t *this, ike_sa_t *old, ike_sa_t *new)
234 {
235 ike_updown(this, old, FALSE);
236 ike_updown(this, new, TRUE);
237 return TRUE;
238 }
239
240 METHOD(listener_t, alert, bool,
241 private_ha_ike_t *this, ike_sa_t *ike_sa, alert_t alert, va_list args)
242 {
243 switch (alert)
244 {
245 case ALERT_HALF_OPEN_TIMEOUT:
246 ike_updown(this, ike_sa, FALSE);
247 break;
248 default:
249 break;
250 }
251 return TRUE;
252 }
253
254 METHOD(listener_t, ike_state_change, bool,
255 private_ha_ike_t *this, ike_sa_t *ike_sa, ike_sa_state_t new)
256 {
257 /* delete any remaining cache entry if IKE_SA gets destroyed */
258 if (new == IKE_DESTROYING)
259 {
260 this->cache->delete(this->cache, ike_sa);
261 }
262 return TRUE;
263 }
264
265 /**
266 * Send a virtual IP sync message for remote VIPs
267 */
268 static void sync_vips(private_ha_ike_t *this, ike_sa_t *ike_sa)
269 {
270 ha_message_t *m = NULL;
271 enumerator_t *enumerator;
272 host_t *vip;
273
274 enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE);
275 while (enumerator->enumerate(enumerator, &vip))
276 {
277 if (!m)
278 {
279 m = ha_message_create(HA_IKE_UPDATE);
280 m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa));
281 }
282 m->add_attribute(m, HA_REMOTE_VIP, vip);
283 }
284 enumerator->destroy(enumerator);
285
286 if (m)
287 {
288 this->socket->push(this->socket, m);
289 this->cache->cache(this->cache, ike_sa, m);
290 }
291 }
292
293 METHOD(listener_t, message_hook, bool,
294 private_ha_ike_t *this, ike_sa_t *ike_sa, message_t *message,
295 bool incoming, bool plain)
296 {
297 if (this->tunnel && this->tunnel->is_sa(this->tunnel, ike_sa))
298 { /* do not sync SA between nodes */
299 return TRUE;
300 }
301
302 if (plain && ike_sa->get_version(ike_sa) == IKEV2)
303 {
304 if (message->get_exchange_type(message) != IKE_SA_INIT &&
305 message->get_request(message))
306 { /* we sync on requests, but skip it on IKE_SA_INIT */
307 ha_message_t *m;
308
309 if (incoming)
310 {
311 m = ha_message_create(HA_IKE_MID_RESPONDER);
312 }
313 else
314 {
315 m = ha_message_create(HA_IKE_MID_INITIATOR);
316 }
317 m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa));
318 m->add_attribute(m, HA_MID, message->get_message_id(message) + 1);
319 this->socket->push(this->socket, m);
320 this->cache->cache(this->cache, ike_sa, m);
321 }
322 if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
323 message->get_exchange_type(message) == IKE_AUTH &&
324 !message->get_request(message))
325 { /* After IKE_SA has been established, sync peers virtual IP.
326 * We cannot sync it in the state_change hook, it is installed later.
327 * TODO: where to sync local VIP? */
328 sync_vips(this, ike_sa);
329 }
330 }
331 if (ike_sa->get_version(ike_sa) == IKEV1)
332 {
333 ha_message_t *m;
334 keymat_v1_t *keymat;
335 chunk_t iv;
336
337 /* we need the last block (or expected next IV) of Phase 1, which gets
338 * upated after successful en-/decryption depending on direction */
339 if (incoming == plain)
340 {
341 if (message->get_message_id(message) == 0)
342 {
343 keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa);
344 if (keymat->get_iv(keymat, 0, &iv))
345 {
346 m = ha_message_create(HA_IKE_IV);
347 m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa));
348 m->add_attribute(m, HA_IV, iv);
349 this->socket->push(this->socket, m);
350 this->cache->cache(this->cache, ike_sa, m);
351 }
352 }
353 }
354 if (!plain && !incoming &&
355 message->get_exchange_type(message) == TRANSACTION)
356 {
357 sync_vips(this, ike_sa);
358 }
359 }
360 if (plain && ike_sa->get_version(ike_sa) == IKEV1 &&
361 message->get_exchange_type(message) == INFORMATIONAL_V1)
362 {
363 ha_message_t *m;
364 notify_payload_t *notify;
365 chunk_t data;
366 uint32_t seq;
367
368 notify = message->get_notify(message, DPD_R_U_THERE);
369 if (notify)
370 {
371 data = notify->get_notification_data(notify);
372 if (data.len == 4)
373 {
374 seq = untoh32(data.ptr);
375 if (incoming)
376 {
377 m = ha_message_create(HA_IKE_MID_RESPONDER);
378 }
379 else
380 {
381 m = ha_message_create(HA_IKE_MID_INITIATOR);
382 }
383 m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa));
384 m->add_attribute(m, HA_MID, seq + 1);
385 this->socket->push(this->socket, m);
386 this->cache->cache(this->cache, ike_sa, m);
387 }
388 }
389 }
390 return TRUE;
391 }
392
393 METHOD(ha_ike_t, destroy, void,
394 private_ha_ike_t *this)
395 {
396 free(this);
397 }
398
399 /**
400 * See header
401 */
402 ha_ike_t *ha_ike_create(ha_socket_t *socket, ha_tunnel_t *tunnel,
403 ha_cache_t *cache)
404 {
405 private_ha_ike_t *this;
406
407 INIT(this,
408 .public = {
409 .listener = {
410 .alert = _alert,
411 .ike_keys = _ike_keys,
412 .ike_updown = _ike_updown,
413 .ike_rekey = _ike_rekey,
414 .ike_state_change = _ike_state_change,
415 .message = _message_hook,
416 },
417 .destroy = _destroy,
418 },
419 .socket = socket,
420 .tunnel = tunnel,
421 .cache = cache,
422 );
423
424 return &this->public;
425 }
426