Annotation of embedaddon/strongswan/src/libcharon/plugins/ha/ha_ike.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (C) 2008 Martin Willi
3: * HSR 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, auth_method_t method)
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: else
145: {
146: m->add_attribute(m, HA_AUTH_METHOD, method);
147: }
148: }
149: m->add_attribute(m, HA_REMOTE_ADDR, ike_sa->get_other_host(ike_sa));
150:
151: this->socket->push(this->socket, m);
152: this->cache->cache(this->cache, ike_sa, m);
153:
154: return TRUE;
155: }
156:
157: METHOD(listener_t, ike_updown, bool,
158: private_ha_ike_t *this, ike_sa_t *ike_sa, bool up)
159: {
160: ha_message_t *m;
161:
162: if (ike_sa->get_state(ike_sa) == IKE_PASSIVE)
163: { /* only sync active IKE_SAs */
164: return TRUE;
165: }
166: if (this->tunnel && this->tunnel->is_sa(this->tunnel, ike_sa))
167: { /* do not sync SA between nodes */
168: return TRUE;
169: }
170:
171: if (up)
172: {
173: enumerator_t *enumerator;
174: peer_cfg_t *peer_cfg;
175: uint32_t extension, condition;
176: host_t *addr;
177: ike_sa_id_t *id;
178: identification_t *eap_id;
179:
180: peer_cfg = ike_sa->get_peer_cfg(ike_sa);
181:
182: condition = copy_condition(ike_sa, COND_NAT_ANY)
183: | copy_condition(ike_sa, COND_NAT_HERE)
184: | copy_condition(ike_sa, COND_NAT_THERE)
185: | copy_condition(ike_sa, COND_NAT_FAKE)
186: | copy_condition(ike_sa, COND_EAP_AUTHENTICATED)
187: | copy_condition(ike_sa, COND_CERTREQ_SEEN)
188: | copy_condition(ike_sa, COND_ORIGINAL_INITIATOR)
189: | copy_condition(ike_sa, COND_STALE)
190: | copy_condition(ike_sa, COND_INIT_CONTACT_SEEN)
191: | copy_condition(ike_sa, COND_XAUTH_AUTHENTICATED);
192:
193: extension = copy_extension(ike_sa, EXT_NATT)
194: | copy_extension(ike_sa, EXT_MOBIKE)
195: | copy_extension(ike_sa, EXT_HASH_AND_URL)
196: | copy_extension(ike_sa, EXT_MULTIPLE_AUTH)
197: | copy_extension(ike_sa, EXT_STRONGSWAN)
198: | copy_extension(ike_sa, EXT_EAP_ONLY_AUTHENTICATION)
199: | copy_extension(ike_sa, EXT_MS_WINDOWS)
200: | copy_extension(ike_sa, EXT_XAUTH)
201: | copy_extension(ike_sa, EXT_DPD);
202:
203: id = ike_sa->get_id(ike_sa);
204:
205: m = ha_message_create(HA_IKE_UPDATE);
206: m->add_attribute(m, HA_IKE_ID, id);
207: m->add_attribute(m, HA_LOCAL_ID, ike_sa->get_my_id(ike_sa));
208: m->add_attribute(m, HA_REMOTE_ID, ike_sa->get_other_id(ike_sa));
209: eap_id = ike_sa->get_other_eap_id(ike_sa);
210: if (!eap_id->equals(eap_id, ike_sa->get_other_id(ike_sa)))
211: {
212: m->add_attribute(m, HA_REMOTE_EAP_ID, eap_id);
213: }
214: m->add_attribute(m, HA_LOCAL_ADDR, ike_sa->get_my_host(ike_sa));
215: m->add_attribute(m, HA_REMOTE_ADDR, ike_sa->get_other_host(ike_sa));
216: m->add_attribute(m, HA_CONDITIONS, condition);
217: m->add_attribute(m, HA_EXTENSIONS, extension);
218: m->add_attribute(m, HA_CONFIG_NAME, peer_cfg->get_name(peer_cfg));
219: enumerator = ike_sa->create_peer_address_enumerator(ike_sa);
220: while (enumerator->enumerate(enumerator, (void**)&addr))
221: {
222: m->add_attribute(m, HA_PEER_ADDR, addr);
223: }
224: enumerator->destroy(enumerator);
225: }
226: else
227: {
228: m = ha_message_create(HA_IKE_DELETE);
229: m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa));
230: }
231: this->socket->push(this->socket, m);
232: this->cache->cache(this->cache, ike_sa, m);
233: return TRUE;
234: }
235:
236: METHOD(listener_t, ike_rekey, bool,
237: private_ha_ike_t *this, ike_sa_t *old, ike_sa_t *new)
238: {
239: ike_updown(this, old, FALSE);
240: ike_updown(this, new, TRUE);
241: return TRUE;
242: }
243:
244: METHOD(listener_t, alert, bool,
245: private_ha_ike_t *this, ike_sa_t *ike_sa, alert_t alert, va_list args)
246: {
247: switch (alert)
248: {
249: case ALERT_HALF_OPEN_TIMEOUT:
250: ike_updown(this, ike_sa, FALSE);
251: break;
252: default:
253: break;
254: }
255: return TRUE;
256: }
257:
258: METHOD(listener_t, ike_state_change, bool,
259: private_ha_ike_t *this, ike_sa_t *ike_sa, ike_sa_state_t new)
260: {
261: /* delete any remaining cache entry if IKE_SA gets destroyed */
262: if (new == IKE_DESTROYING)
263: {
264: this->cache->delete(this->cache, ike_sa);
265: }
266: return TRUE;
267: }
268:
269: /**
270: * Send a virtual IP sync message for remote VIPs
271: */
272: static void sync_vips(private_ha_ike_t *this, ike_sa_t *ike_sa)
273: {
274: ha_message_t *m = NULL;
275: enumerator_t *enumerator;
276: host_t *vip;
277:
278: enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE);
279: while (enumerator->enumerate(enumerator, &vip))
280: {
281: if (!m)
282: {
283: m = ha_message_create(HA_IKE_UPDATE);
284: m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa));
285: }
286: m->add_attribute(m, HA_REMOTE_VIP, vip);
287: }
288: enumerator->destroy(enumerator);
289:
290: if (m)
291: {
292: this->socket->push(this->socket, m);
293: this->cache->cache(this->cache, ike_sa, m);
294: }
295: }
296:
297: METHOD(listener_t, message_hook, bool,
298: private_ha_ike_t *this, ike_sa_t *ike_sa, message_t *message,
299: bool incoming, bool plain)
300: {
301: if (this->tunnel && this->tunnel->is_sa(this->tunnel, ike_sa))
302: { /* do not sync SA between nodes */
303: return TRUE;
304: }
305:
306: if (plain && ike_sa->get_version(ike_sa) == IKEV2)
307: {
308: if (message->get_exchange_type(message) != IKE_SA_INIT &&
309: message->get_request(message))
310: { /* we sync on requests, but skip it on IKE_SA_INIT */
311: ha_message_t *m;
312:
313: if (incoming)
314: {
315: m = ha_message_create(HA_IKE_MID_RESPONDER);
316: }
317: else
318: {
319: m = ha_message_create(HA_IKE_MID_INITIATOR);
320: }
321: m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa));
322: m->add_attribute(m, HA_MID, message->get_message_id(message) + 1);
323: this->socket->push(this->socket, m);
324: this->cache->cache(this->cache, ike_sa, m);
325: }
326: if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
327: message->get_exchange_type(message) == IKE_AUTH &&
328: !message->get_request(message))
329: { /* After IKE_SA has been established, sync peers virtual IP.
330: * We cannot sync it in the state_change hook, it is installed later.
331: * TODO: where to sync local VIP? */
332: sync_vips(this, ike_sa);
333: }
334: }
335: if (ike_sa->get_version(ike_sa) == IKEV1)
336: {
337: ha_message_t *m;
338: keymat_v1_t *keymat;
339: chunk_t iv;
340:
341: /* we need the last block (or expected next IV) of Phase 1, which gets
342: * updated after successful en-/decryption depending on direction */
343: if (incoming == plain)
344: {
345: if (message->get_message_id(message) == 0)
346: {
347: keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa);
348: if (keymat->get_iv(keymat, 0, &iv))
349: {
350: m = ha_message_create(HA_IKE_IV);
351: m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa));
352: m->add_attribute(m, HA_IV, iv);
353: this->socket->push(this->socket, m);
354: this->cache->cache(this->cache, ike_sa, m);
355: }
356: }
357: }
358: if (!plain && !incoming &&
359: message->get_exchange_type(message) == TRANSACTION)
360: {
361: sync_vips(this, ike_sa);
362: }
363: }
364: if (plain && ike_sa->get_version(ike_sa) == IKEV1 &&
365: message->get_exchange_type(message) == INFORMATIONAL_V1)
366: {
367: ha_message_t *m;
368: notify_payload_t *notify;
369: chunk_t data;
370: uint32_t seq;
371:
372: notify = message->get_notify(message, DPD_R_U_THERE);
373: if (notify)
374: {
375: data = notify->get_notification_data(notify);
376: if (data.len == 4)
377: {
378: seq = untoh32(data.ptr);
379: if (incoming)
380: {
381: m = ha_message_create(HA_IKE_MID_RESPONDER);
382: }
383: else
384: {
385: m = ha_message_create(HA_IKE_MID_INITIATOR);
386: }
387: m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa));
388: m->add_attribute(m, HA_MID, seq + 1);
389: this->socket->push(this->socket, m);
390: this->cache->cache(this->cache, ike_sa, m);
391: }
392: }
393: }
394: return TRUE;
395: }
396:
397: METHOD(ha_ike_t, destroy, void,
398: private_ha_ike_t *this)
399: {
400: free(this);
401: }
402:
403: /**
404: * See header
405: */
406: ha_ike_t *ha_ike_create(ha_socket_t *socket, ha_tunnel_t *tunnel,
407: ha_cache_t *cache)
408: {
409: private_ha_ike_t *this;
410:
411: INIT(this,
412: .public = {
413: .listener = {
414: .alert = _alert,
415: .ike_keys = _ike_keys,
416: .ike_updown = _ike_updown,
417: .ike_rekey = _ike_rekey,
418: .ike_state_change = _ike_state_change,
419: .message = _message_hook,
420: },
421: .destroy = _destroy,
422: },
423: .socket = socket,
424: .tunnel = tunnel,
425: .cache = cache,
426: );
427:
428: return &this->public;
429: }
430:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>