charon-nm: Set DPD/close action to restart and enable indefinite keying tries
[strongswan.git] / src / libcharon / plugins / unity / unity_narrow.c
1 /*
2 * Copyright (C) 2014 Tobias Brunner
3 * HSR Hochschule fuer Technik Rapperswil
4 *
5 * Copyright (C) 2012 Martin Willi
6 * Copyright (C) 2012 revosec AG
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * for more details.
17 */
18
19 #include "unity_narrow.h"
20
21 #include <daemon.h>
22 #include <encoding/payloads/id_payload.h>
23 #include <collections/hashtable.h>
24
25 typedef struct private_unity_narrow_t private_unity_narrow_t;
26
27 /**
28 * Private data of an unity_narrow_t object.
29 */
30 struct private_unity_narrow_t {
31
32 /**
33 * Public unity_narrow_t interface.
34 */
35 unity_narrow_t public;
36
37 /**
38 * Unity attribute handler
39 */
40 unity_handler_t *handler;
41
42 /**
43 * IKE_SAs for which we received 0.0.0.0/0 as remote traffic selector
44 */
45 hashtable_t *wildcard_ts;
46 };
47
48 /**
49 * Narrow the given received traffic selector with the child configuration and
50 * put them into the given list of TS
51 */
52 static void narrow_ts(child_cfg_t *cfg, traffic_selector_t *ts,
53 linked_list_t *list)
54 {
55 linked_list_t *received, *selected;
56
57 received = linked_list_create();
58 received->insert_last(received, ts);
59 selected = cfg->get_traffic_selectors(cfg, FALSE, received, NULL, FALSE);
60 while (selected->remove_first(selected, (void**)&ts) == SUCCESS)
61 {
62 list->insert_last(list, ts);
63 }
64 selected->destroy(selected);
65 received->destroy(received);
66 }
67
68 /**
69 * Narrow TS as initiator to Unity Split-Include/Local-LAN
70 */
71 static void narrow_initiator(private_unity_narrow_t *this, ike_sa_t *ike_sa,
72 child_cfg_t *cfg, linked_list_t *remote)
73 {
74 traffic_selector_t *current, *orig = NULL;
75 enumerator_t *enumerator;
76
77 enumerator = this->handler->create_include_enumerator(this->handler,
78 ike_sa->get_id(ike_sa));
79 while (enumerator->enumerate(enumerator, &current))
80 {
81 if (orig == NULL)
82 { /* got one, replace original TS */
83 if (remote->remove_first(remote, (void**)&orig) != SUCCESS)
84 {
85 break;
86 }
87 }
88 narrow_ts(cfg, current, remote);
89 }
90 enumerator->destroy(enumerator);
91 if (orig)
92 {
93 DBG1(DBG_CFG, "narrowed CHILD_SA to %N %#R",
94 configuration_attribute_type_names,
95 UNITY_SPLIT_INCLUDE, remote);
96 orig->destroy(orig);
97 }
98 else
99 { /* since we originally changed the traffic selector to 0.0.0.0/0 local
100 * narrowing is not applied if no Split-Include attrs are received */
101 if (remote->remove_first(remote, (void**)&orig) == SUCCESS)
102 {
103 narrow_ts(cfg, orig, remote);
104 orig->destroy(orig);
105 }
106 }
107 }
108
109 /**
110 * As initiator and responder, bump up TS to 0.0.0.0/0 for on-the-wire bits
111 */
112 static void narrow_pre(linked_list_t *list, char *side)
113 {
114 traffic_selector_t *ts;
115
116 while (list->remove_first(list, (void**)&ts) == SUCCESS)
117 {
118 ts->destroy(ts);
119 }
120 ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE,
121 "0.0.0.0", 0,
122 "255.255.255.255", 65535);
123 if (ts)
124 {
125 DBG2(DBG_CFG, "changing proposed traffic selectors for %s:", side);
126 DBG2(DBG_CFG, " %R", ts);
127 list->insert_last(list, ts);
128 }
129 }
130
131 /**
132 * As responder, narrow down TS to configuration for installation
133 */
134 static void narrow_responder_post(child_cfg_t *child_cfg, linked_list_t *local)
135 {
136 traffic_selector_t *ts;
137 linked_list_t *configured;
138
139 while (local->remove_first(local, (void**)&ts) == SUCCESS)
140 {
141 ts->destroy(ts);
142 }
143 configured = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, NULL,
144 FALSE);
145
146 while (configured->remove_first(configured, (void**)&ts) == SUCCESS)
147 {
148 local->insert_last(local, ts);
149 }
150 configured->destroy(configured);
151 }
152
153 /**
154 * Check if any Split-Include attributes are active on this IKE_SA
155 */
156 static bool has_split_includes(private_unity_narrow_t *this, ike_sa_t *ike_sa)
157 {
158 enumerator_t *enumerator;
159 traffic_selector_t *ts;
160 bool has;
161
162 enumerator = this->handler->create_include_enumerator(this->handler,
163 ike_sa->get_id(ike_sa));
164 has = enumerator->enumerate(enumerator, &ts);
165 enumerator->destroy(enumerator);
166
167 return has;
168 }
169
170 METHOD(listener_t, narrow, bool,
171 private_unity_narrow_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
172 narrow_hook_t type, linked_list_t *local, linked_list_t *remote)
173 {
174 if (ike_sa->get_version(ike_sa) == IKEV1 &&
175 ike_sa->supports_extension(ike_sa, EXT_CISCO_UNITY))
176 {
177 /* depending on who initiates a rekeying the hooks will not match the
178 * roles in the IKE_SA */
179 if (ike_sa->has_condition(ike_sa, COND_ORIGINAL_INITIATOR))
180 {
181 switch (type)
182 {
183 case NARROW_INITIATOR_PRE_AUTH:
184 case NARROW_RESPONDER:
185 if (has_split_includes(this, ike_sa))
186 {
187 narrow_pre(remote, "other");
188 }
189 break;
190 case NARROW_INITIATOR_POST_AUTH:
191 case NARROW_RESPONDER_POST:
192 narrow_initiator(this, ike_sa,
193 child_sa->get_config(child_sa), remote);
194 break;
195 default:
196 break;
197 }
198 }
199 else
200 {
201 switch (type)
202 {
203 case NARROW_INITIATOR_PRE_AUTH:
204 case NARROW_RESPONDER:
205 if (this->wildcard_ts->get(this->wildcard_ts, ike_sa))
206 {
207 narrow_pre(local, "us");
208
209 }
210 break;
211 case NARROW_INITIATOR_POST_AUTH:
212 case NARROW_RESPONDER_POST:
213 if (this->wildcard_ts->get(this->wildcard_ts, ike_sa))
214 {
215 narrow_responder_post(child_sa->get_config(child_sa),
216 local);
217 }
218 break;
219 default:
220 break;
221 }
222 }
223 }
224 return TRUE;
225 }
226
227 METHOD(listener_t, message, bool,
228 private_unity_narrow_t *this, ike_sa_t *ike_sa, message_t *message,
229 bool incoming, bool plain)
230 {
231 traffic_selector_t *tsr = NULL, *wildcard;
232 enumerator_t *enumerator;
233 id_payload_t *id_payload;
234 payload_t *payload;
235 bool first = TRUE;
236
237 if (!incoming || !plain ||
238 message->get_exchange_type(message) != QUICK_MODE ||
239 !ike_sa || !ike_sa->supports_extension(ike_sa, EXT_CISCO_UNITY))
240 {
241 return TRUE;
242 }
243 enumerator = message->create_payload_enumerator(message);
244 while (enumerator->enumerate(enumerator, &payload))
245 {
246 if (payload->get_type(payload) == PLV1_ID)
247 {
248 if (!first)
249 {
250 id_payload = (id_payload_t*)payload;
251 tsr = id_payload->get_ts(id_payload);
252 break;
253 }
254 first = FALSE;
255 }
256 }
257 enumerator->destroy(enumerator);
258 if (!tsr)
259 {
260 return TRUE;
261 }
262 wildcard = traffic_selector_create_from_cidr("0.0.0.0/0", 0, 0, 65535);
263 if (tsr->equals(tsr, wildcard))
264 {
265 this->wildcard_ts->put(this->wildcard_ts, ike_sa, ike_sa);
266 }
267 else
268 {
269 this->wildcard_ts->remove(this->wildcard_ts, ike_sa);
270 }
271 wildcard->destroy(wildcard);
272 tsr->destroy(tsr);
273 return TRUE;
274 }
275
276 METHOD(listener_t, ike_updown, bool,
277 private_unity_narrow_t *this, ike_sa_t *ike_sa, bool up)
278 {
279 if (!up)
280 {
281 this->wildcard_ts->remove(this->wildcard_ts, ike_sa);
282 }
283 return TRUE;
284 }
285
286 METHOD(unity_narrow_t, destroy, void,
287 private_unity_narrow_t *this)
288 {
289 this->wildcard_ts->destroy(this->wildcard_ts);
290 free(this);
291 }
292
293 /**
294 * See header
295 */
296 unity_narrow_t *unity_narrow_create(unity_handler_t *handler)
297 {
298 private_unity_narrow_t *this;
299
300 INIT(this,
301 .public = {
302 .listener = {
303 .narrow = _narrow,
304 .message = _message,
305 .ike_updown = _ike_updown,
306 },
307 .destroy = _destroy,
308 },
309 .handler = handler,
310 .wildcard_ts = hashtable_create(hashtable_hash_ptr,
311 hashtable_equals_ptr, 4),
312 );
313
314 return &this->public;
315 }