File: | programs/pluto/ikev2_ts.c |
Warning: | line 1405, column 10 Null pointer passed to 2nd parameter expecting 'nonnull' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* IKEv2 Traffic Selectors, for libreswan | |||
2 | * | |||
3 | * Copyright (C) 2007-2008 Michael Richardson <mcr@xelerance.com> | |||
4 | * Copyright (C) 2009-2010 Paul Wouters <paul@xelerance.com> | |||
5 | * Copyright (C) 2010 Tuomo Soini <tis@foobar.fi> | |||
6 | * Copyright (C) 2011-2012 Avesh Agarwal <avagarwa@redhat.com> | |||
7 | * Copyright (C) 2012-2018 Paul Wouters <pwouters@redhat.com> | |||
8 | * Copyright (C) 2012,2016-2017 Antony Antony <appu@phenome.org> | |||
9 | * Copyright (C) 2013 D. Hugh Redelmeier <hugh@mimosa.com> | |||
10 | * Copyright (C) 2014-2015, 2018 Andrew cagney <cagney@gnu.org> | |||
11 | * Copyright (C) 2017 Antony Antony <antony@phenome.org> | |||
12 | * Copyright (C) 2019 Andrew Cagney <cagney@gnu.org> | |||
13 | * Copyright (C) 2021 Paul Wouters <paul.wouters@aiven.io> | |||
14 | * | |||
15 | * This program is free software; you can redistribute it and/or modify it | |||
16 | * under the terms of the GNU General Public License as published by the | |||
17 | * Free Software Foundation; either version 2 of the License, or (at your | |||
18 | * option) any later version. See <https://www.gnu.org/licenses/gpl2.txt>. | |||
19 | * | |||
20 | * This program is distributed in the hope that it will be useful, but | |||
21 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |||
22 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |||
23 | * for more details. | |||
24 | * | |||
25 | */ | |||
26 | ||||
27 | #include "defs.h" | |||
28 | ||||
29 | #include "log.h" | |||
30 | #include "ikev2_ts.h" | |||
31 | #include "connections.h" /* for struct end */ | |||
32 | #include "demux.h" | |||
33 | #include "virtual_ip.h" | |||
34 | #include "host_pair.h" | |||
35 | #include "ip_info.h" | |||
36 | #include "ip_selector.h" | |||
37 | #include "labeled_ipsec.h" | |||
38 | #include "ip_range.h" | |||
39 | #include "iface.h" | |||
40 | #include "pending.h" /* for connection_is_pending() */ | |||
41 | #include "state_db.h" /* for FOR_EACH_STATE_NEW2OLD() */ | |||
42 | ||||
43 | /* | |||
44 | * While the RFC seems to suggest that the traffic selectors come in | |||
45 | * pairs, strongswan, at least, doesn't. | |||
46 | */ | |||
47 | ||||
48 | struct traffic_selectors { | |||
49 | const char *name; | |||
50 | bool_Bool contains_sec_label; | |||
51 | unsigned nr; | |||
52 | /* ??? is 16 an undocumented limit - IKEv2 has no limit */ | |||
53 | struct traffic_selector ts[16]; | |||
54 | }; | |||
55 | ||||
56 | struct traffic_selector_payloads { | |||
57 | struct traffic_selectors i; | |||
58 | struct traffic_selectors r; | |||
59 | }; | |||
60 | ||||
61 | static const struct traffic_selector_payloads empty_traffic_selectors = { | |||
62 | .i = { | |||
63 | .name = "TSi", | |||
64 | }, | |||
65 | .r = { | |||
66 | .name = "TSr", | |||
67 | }, | |||
68 | }; | |||
69 | ||||
70 | struct ends { | |||
71 | const struct end *i; | |||
72 | const struct end *r; | |||
73 | }; | |||
74 | ||||
75 | enum fit { | |||
76 | END_EQUALS_TS = 1, | |||
77 | END_NARROWER_THAN_TS, | |||
78 | END_WIDER_THAN_TS, | |||
79 | }; | |||
80 | ||||
81 | ||||
82 | static const char *fit_string(enum fit fit) | |||
83 | { | |||
84 | switch (fit) { | |||
85 | case END_EQUALS_TS: return "=="; | |||
86 | case END_NARROWER_THAN_TS: return "<="; | |||
87 | case END_WIDER_THAN_TS: return ">="; | |||
88 | default: bad_case(fit)libreswan_bad_case("fit", (fit), ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c" , .line = 88, }; &here; })); | |||
89 | } | |||
90 | } | |||
91 | ||||
92 | void dbg_v2_ts(const struct traffic_selector *ts, const char *prefix, ...) | |||
93 | { | |||
94 | if (DBGP(DBG_BASE)(cur_debugging & (((lset_t)1 << (DBG_BASE_IX))))) { | |||
95 | va_list ap; | |||
96 | va_start(ap, prefix)__builtin_va_start(ap, prefix); | |||
97 | DBG_va_list(prefix, ap); | |||
98 | va_end(ap)__builtin_va_end(ap); | |||
99 | DBG_log(" ts_type: %s", enum_name(&ikev2_ts_type_names, ts->ts_type)); | |||
100 | DBG_log(" ipprotoid: %d", ts->ipprotoid); | |||
101 | DBG_log(" port range: %d-%d", ts->startport, ts->endport); | |||
102 | range_buf b; | |||
103 | DBG_log(" ip range: %s", str_range(&ts->net, &b)); | |||
104 | DBG_log(" sec_label: "PRI_SHUNK"%.*s", pri_shunk(ts->sec_label)((int) (ts->sec_label).len), (const char *) ((ts->sec_label ).ptr)); | |||
105 | } | |||
106 | } | |||
107 | ||||
108 | static void traffic_selector_to_end(const struct traffic_selector *ts, struct end *end, | |||
109 | const char *story) | |||
110 | { | |||
111 | dbg_v2_ts(ts, "%s() %s", __func__, story); | |||
112 | ip_subnet subnet; | |||
113 | happy(range_to_subnet(ts->net, &subnet)){ err_t ugh = range_to_subnet(ts->net, &subnet); if (ugh != ((void*)0)) { llog_passert(&failsafe_logger, ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c" , .line = 113, }; &here; }), "%s", ugh); } }; | |||
114 | const ip_protocol *protocol = protocol_by_ipproto(ts->ipprotoid); | |||
115 | /* XXX: check port range valid */ | |||
116 | ip_port port = ip_hport(ts->startport); | |||
117 | end->client = selector_from_subnet_protocol_port(subnet, protocol, port); | |||
118 | /* redundant */ | |||
119 | end->port = ts->startport; | |||
120 | end->protocol = ts->ipprotoid; | |||
121 | end->has_client = !selector_eq_address(end->client, end->host_addr); | |||
122 | } | |||
123 | ||||
124 | /* rewrite me with address_as_{chunk,shunk}()? */ | |||
125 | /* For now, note the struct traffic_selector can contain | |||
126 | * two selectors - an IPvX range and a sec_label | |||
127 | */ | |||
128 | struct traffic_selector traffic_selector_from_end(const struct end *e, const char *what) | |||
129 | { | |||
130 | struct traffic_selector ts = { | |||
131 | /* | |||
132 | * Setting ts_type IKEv2_TS_FC_ADDR_RANGE (RFC-4595) | |||
133 | * not yet supported. | |||
134 | */ | |||
135 | .ts_type = selector_type(&e->client)->ikev2_ts_addr_range_type, | |||
136 | /* subnet => range */ | |||
137 | .net = selector_range(e->client), | |||
138 | .ipprotoid = e->protocol, | |||
139 | /* | |||
140 | * Use the 'instance/narrowed' label from the ACQUIRE | |||
141 | * and stored in the connection instance's sends, if | |||
142 | * present. | |||
143 | */ | |||
144 | .sec_label = HUNK_AS_SHUNK(e->sec_label)({ typeof(e->sec_label) h_ = (e->sec_label); shunk2(h_. ptr, h_.len); }), | |||
145 | }; | |||
146 | ||||
147 | /* | |||
148 | * if port is %any or 0 we mean all ports (or all | |||
149 | * iccmp/icmpv6). | |||
150 | * | |||
151 | * See RFC-5996 Section 3.13.1 handling for ICMP(1) and | |||
152 | * ICMPv6(58) we only support providing Type, not Code, eg | |||
153 | * protoport=1/1 | |||
154 | */ | |||
155 | if (e->port == 0 || e->has_port_wildcard) { | |||
156 | ts.startport = 0; | |||
157 | ts.endport = 65535; | |||
158 | } else { | |||
159 | ts.startport = e->port; | |||
160 | ts.endport = e->port; | |||
161 | } | |||
162 | ||||
163 | dbg_v2_ts(&ts, "%s TS", what); | |||
164 | return ts; | |||
165 | } | |||
166 | ||||
167 | /* | |||
168 | * A struct end is converted to a struct traffic_selector. | |||
169 | * | |||
170 | * This (currently) can contain both an IP range AND a SEC_LABEL, | |||
171 | * which will get output here as two Traffic Selectors. The label is | |||
172 | * optional, the IP range is mandatory. | |||
173 | */ | |||
174 | static stf_status emit_v2TS(struct pbs_outpacket_byte_stream *outpbs, | |||
175 | const struct_desc *ts_desc, | |||
176 | const struct traffic_selector *ts) | |||
177 | { | |||
178 | struct pbs_outpacket_byte_stream ts_pbs; | |||
179 | bool_Bool with_label = (ts->sec_label.len > 0); | |||
180 | ||||
181 | if (ts->ts_type != IKEv2_TS_IPV4_ADDR_RANGE && | |||
182 | ts->ts_type != IKEv2_TS_IPV6_ADDR_RANGE) { | |||
183 | return STF_INTERNAL_ERROR; | |||
184 | } | |||
185 | ||||
186 | { | |||
187 | struct ikev2_ts its = { | |||
188 | .isat_critical = ISAKMP_PAYLOAD_NONCRITICAL0x00, | |||
189 | /* | |||
190 | * If there is a security label in the Traffic | |||
191 | * Selector, then we must send a TS_SECLABEL | |||
192 | * substructure as part of the Traffic | |||
193 | * Selector (TS) Payload. | |||
194 | * | |||
195 | * That means the TS Payload contains two TS | |||
196 | * substructures: | |||
197 | * - One for the address/port range | |||
198 | * - One for the TS_SECLABEL | |||
199 | */ | |||
200 | .isat_num = with_label ? 2 : 1, | |||
201 | }; | |||
202 | ||||
203 | if (!out_struct(&its, ts_desc, outpbs, &ts_pbs)) | |||
204 | return STF_INTERNAL_ERROR; | |||
205 | } | |||
206 | ||||
207 | { | |||
208 | pb_stream ts_range_pbs; | |||
209 | struct ikev2_ts_header ts_header = { | |||
210 | .isath_ipprotoid = ts->ipprotoid | |||
211 | }; | |||
212 | ||||
213 | switch (ts->ts_type) { | |||
214 | case IKEv2_TS_IPV4_ADDR_RANGE: | |||
215 | ts_header.isath_type = IKEv2_TS_IPV4_ADDR_RANGE; | |||
216 | break; | |||
217 | case IKEv2_TS_IPV6_ADDR_RANGE: | |||
218 | ts_header.isath_type = IKEv2_TS_IPV6_ADDR_RANGE; | |||
219 | break; | |||
220 | } | |||
221 | ||||
222 | if (!out_struct(&ts_header, &ikev2_ts_header_desc, &ts_pbs, &ts_range_pbs)) | |||
223 | return STF_INTERNAL_ERROR; | |||
224 | ||||
225 | ||||
226 | struct ikev2_ts_portrange ts_ports = { | |||
227 | .isatpr_startport = ts->startport, | |||
228 | .isatpr_endport = ts->endport | |||
229 | }; | |||
230 | ||||
231 | if (!out_struct(&ts_ports, &ikev2_ts_portrange_desc, &ts_range_pbs, NULL((void*)0))) | |||
232 | return STF_INTERNAL_ERROR; | |||
233 | ||||
234 | diag_t d; | |||
235 | d = pbs_out_address(&ts_range_pbs, range_start(ts->net), "IP start"); | |||
236 | if (d != NULL((void*)0)) { | |||
237 | llog_diag(RC_LOG_SERIOUS, outpbs->outs_logger, &d, "%s", ""); | |||
238 | return STF_INTERNAL_ERROR; | |||
239 | } | |||
240 | d = pbs_out_address(&ts_range_pbs, range_end(ts->net), "IP end"); | |||
241 | if (d != NULL((void*)0)) { | |||
242 | llog_diag(RC_LOG_SERIOUS, outpbs->outs_logger, &d, "%s", ""); | |||
243 | return STF_INTERNAL_ERROR; | |||
244 | } | |||
245 | close_output_pbs(&ts_range_pbs); | |||
246 | } | |||
247 | ||||
248 | /* | |||
249 | * Emit the security label, if known. | |||
250 | */ | |||
251 | if (with_label) { | |||
252 | ||||
253 | struct ikev2_ts_header ts_header = { | |||
254 | .isath_type = IKEv2_TS_SECLABEL, | |||
255 | .isath_ipprotoid = 0 /* really RESERVED, not iprotoid */ | |||
256 | }; | |||
257 | /* Output the header of the TS_SECLABEL substructure payload. */ | |||
258 | struct pbs_outpacket_byte_stream ts_label_pbs; | |||
259 | if (!out_struct(&ts_header, &ikev2_ts_header_desc, &ts_pbs, &ts_label_pbs)) { | |||
260 | return STF_INTERNAL_ERROR; | |||
261 | } | |||
262 | ||||
263 | /* | |||
264 | * Output the security label value of the TS_SECLABEL | |||
265 | * substructure payload. | |||
266 | * | |||
267 | * If we got ACQUIRE, or received a subset TS_LABEL, | |||
268 | * use that one - it is subset of connection policy | |||
269 | * one | |||
270 | */ | |||
271 | ||||
272 | dbg("emitting sec_label="PRI_SHUNK, pri_shunk(ts->sec_label)){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("emitting sec_label=""%.*s", ((int) (ts->sec_label ).len), (const char *) ((ts->sec_label).ptr)); } }; | |||
273 | ||||
274 | diag_t d = pbs_out_hunk(&ts_label_pbs, ts->sec_label, "output Security label")({ typeof(ts->sec_label) hunk_ = ts->sec_label; struct packet_byte_stream *outs_ = &ts_label_pbs; pbs_out_raw(outs_, hunk_.ptr, hunk_ .len, ("output Security label")); }); | |||
275 | if (d != NULL((void*)0)) { | |||
276 | llog_diag(RC_LOG_SERIOUS, outpbs->outs_logger, &d, "%s", ""); | |||
277 | return STF_INTERNAL_ERROR; | |||
278 | } | |||
279 | ||||
280 | close_output_pbs(&ts_label_pbs); | |||
281 | } | |||
282 | ||||
283 | close_output_pbs(&ts_pbs); | |||
284 | return STF_OK; | |||
285 | } | |||
286 | ||||
287 | static struct traffic_selector impair_ts_to_subnet(const struct traffic_selector ts) | |||
288 | { | |||
289 | struct traffic_selector ts_ret = ts; | |||
290 | ||||
291 | ts_ret.net.end = ts_ret.net.start; | |||
292 | ts_ret.net.is_subnet = true1; | |||
293 | ||||
294 | return ts_ret; | |||
295 | } | |||
296 | ||||
297 | ||||
298 | static struct traffic_selector impair_ts_to_supernet(const struct traffic_selector ts) | |||
299 | { | |||
300 | struct traffic_selector ts_ret = ts; | |||
301 | ||||
302 | if (ts_ret.ts_type == IKEv2_TS_IPV4_ADDR_RANGE) | |||
303 | ts_ret.net = range_from_subnet(ipv4_info.subnet.all); | |||
304 | else if (ts_ret.ts_type == IKEv2_TS_IPV6_ADDR_RANGE) | |||
305 | ts_ret.net = range_from_subnet(ipv6_info.subnet.all); | |||
306 | ||||
307 | ts_ret.net.is_subnet = true1; | |||
308 | ||||
309 | ts_ret.sec_label = ts.sec_label; | |||
310 | ||||
311 | return ts_ret; | |||
312 | } | |||
313 | ||||
314 | stf_status emit_v2TS_payloads(struct pbs_outpacket_byte_stream *outpbs, const struct child_sa *child) | |||
315 | { | |||
316 | stf_status ret; | |||
317 | struct traffic_selector ts_i, ts_r; | |||
318 | ||||
319 | switch (child->sa.st_sa_role) { | |||
320 | case SA_INITIATOR: | |||
321 | ts_i = traffic_selector_from_end(&child->sa.st_connection->spd.this, "this TSi"); | |||
322 | ts_r = traffic_selector_from_end(&child->sa.st_connection->spd.that, "that TSr"); | |||
323 | if (child->sa.st_state->kind == STATE_V2_REKEY_CHILD_I0 && | |||
324 | impair.rekey_initiate_supernet) { | |||
325 | ts_i = ts_r = impair_ts_to_supernet(ts_i); | |||
326 | range_buf tsi_buf; | |||
327 | range_buf tsr_buf; | |||
328 | dbg("rekey-initiate-supernet TSi and TSr set to %s %s",{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("rekey-initiate-supernet TSi and TSr set to %s %s" , str_range(&ts_i.net, &tsi_buf), str_range(&ts_r .net, &tsr_buf)); } } | |||
329 | str_range(&ts_i.net, &tsi_buf),{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("rekey-initiate-supernet TSi and TSr set to %s %s" , str_range(&ts_i.net, &tsi_buf), str_range(&ts_r .net, &tsr_buf)); } } | |||
330 | str_range(&ts_r.net, &tsr_buf)){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("rekey-initiate-supernet TSi and TSr set to %s %s" , str_range(&ts_i.net, &tsi_buf), str_range(&ts_r .net, &tsr_buf)); } }; | |||
331 | ||||
332 | } else if (child->sa.st_state->kind == STATE_V2_REKEY_CHILD_I0 && | |||
333 | impair.rekey_initiate_subnet) { | |||
334 | ts_i = impair_ts_to_subnet(ts_i); | |||
335 | ts_r = impair_ts_to_subnet(ts_r); | |||
336 | range_buf tsi_buf; | |||
337 | range_buf tsr_buf; | |||
338 | dbg("rekey-initiate-subnet TSi and TSr set to %s %s",{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("rekey-initiate-subnet TSi and TSr set to %s %s" , str_range(&ts_i.net, &tsi_buf), str_range(&ts_r .net, &tsr_buf)); } } | |||
339 | str_range(&ts_i.net, &tsi_buf),{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("rekey-initiate-subnet TSi and TSr set to %s %s" , str_range(&ts_i.net, &tsi_buf), str_range(&ts_r .net, &tsr_buf)); } } | |||
340 | str_range(&ts_r.net, &tsr_buf)){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("rekey-initiate-subnet TSi and TSr set to %s %s" , str_range(&ts_i.net, &tsi_buf), str_range(&ts_r .net, &tsr_buf)); } }; | |||
341 | ||||
342 | } | |||
343 | ||||
344 | break; | |||
345 | case SA_RESPONDER: | |||
346 | ts_i = traffic_selector_from_end(&child->sa.st_connection->spd.that, "that TSi"); | |||
347 | ts_r = traffic_selector_from_end(&child->sa.st_connection->spd.this, "this TSr"); | |||
348 | if (child->sa.st_state->kind == STATE_V2_REKEY_CHILD_R0 && | |||
349 | impair.rekey_respond_subnet) { | |||
350 | ts_i = impair_ts_to_subnet(ts_i); | |||
351 | ts_r = impair_ts_to_subnet(ts_r); | |||
352 | range_buf tsi_buf; | |||
353 | range_buf tsr_buf; | |||
354 | dbg("rekey-respond-subnet TSi and TSr set to %s %s",{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("rekey-respond-subnet TSi and TSr set to %s %s" , str_range(&ts_i.net, &tsi_buf), str_range(&ts_r .net, &tsr_buf)); } } | |||
355 | str_range(&ts_i.net, &tsi_buf),{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("rekey-respond-subnet TSi and TSr set to %s %s" , str_range(&ts_i.net, &tsi_buf), str_range(&ts_r .net, &tsr_buf)); } } | |||
356 | str_range(&ts_r.net, &tsr_buf)){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("rekey-respond-subnet TSi and TSr set to %s %s" , str_range(&ts_i.net, &tsi_buf), str_range(&ts_r .net, &tsr_buf)); } }; | |||
357 | } | |||
358 | if (child->sa.st_state->kind == STATE_V2_REKEY_CHILD_R0 && | |||
359 | impair.rekey_respond_supernet) { | |||
360 | ts_i = ts_r = impair_ts_to_supernet(ts_i); | |||
361 | range_buf tsi_buf; | |||
362 | range_buf tsr_buf; | |||
363 | dbg("rekey-respond-supernet TSi and TSr set to %s %s",{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("rekey-respond-supernet TSi and TSr set to %s %s" , str_range(&ts_i.net, &tsi_buf), str_range(&ts_r .net, &tsr_buf)); } } | |||
364 | str_range(&ts_i.net, &tsi_buf),{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("rekey-respond-supernet TSi and TSr set to %s %s" , str_range(&ts_i.net, &tsi_buf), str_range(&ts_r .net, &tsr_buf)); } } | |||
365 | str_range(&ts_r.net, &tsr_buf)){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("rekey-respond-supernet TSi and TSr set to %s %s" , str_range(&ts_i.net, &tsi_buf), str_range(&ts_r .net, &tsr_buf)); } }; | |||
366 | } | |||
367 | break; | |||
368 | default: | |||
369 | bad_case(child->sa.st_sa_role)libreswan_bad_case("child->sa.st_sa_role", (child->sa.st_sa_role ), ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c", .line = 369, }; &here; }) ); | |||
370 | } | |||
371 | ||||
372 | ret = emit_v2TS(outpbs, &ikev2_ts_i_desc, &ts_i); | |||
373 | if (ret != STF_OK) | |||
374 | return ret; | |||
375 | ||||
376 | ret = emit_v2TS(outpbs, &ikev2_ts_r_desc, &ts_r); | |||
377 | if (ret != STF_OK) | |||
378 | return ret; | |||
379 | ||||
380 | return STF_OK; | |||
381 | } | |||
382 | ||||
383 | /* return success */ | |||
384 | static bool_Bool v2_parse_tss(struct payload_digest *const ts_pd, | |||
385 | struct traffic_selectors *tss, | |||
386 | struct logger *logger) | |||
387 | { | |||
388 | dbg("%s: parsing %u traffic selectors",{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("%s: parsing %u traffic selectors", tss->name , ts_pd->payload.v2ts.isat_num); } } | |||
389 | tss->name, ts_pd->payload.v2ts.isat_num){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("%s: parsing %u traffic selectors", tss->name , ts_pd->payload.v2ts.isat_num); } }; | |||
390 | ||||
391 | if (ts_pd->payload.v2ts.isat_num == 0) { | |||
392 | llog(RC_LOG, logger, | |||
393 | "%s payload contains no entries when at least one is expected", | |||
394 | tss->name); | |||
395 | return false0; | |||
396 | } | |||
397 | ||||
398 | if (ts_pd->payload.v2ts.isat_num >= elemsof(tss->ts)(sizeof(tss->ts) / sizeof(*(tss->ts)))) { | |||
399 | llog(RC_LOG, logger, | |||
400 | "%s contains %d entries which exceeds hardwired max of %zu", | |||
401 | tss->name, ts_pd->payload.v2ts.isat_num, elemsof(tss->ts)(sizeof(tss->ts) / sizeof(*(tss->ts)))); | |||
402 | return false0; /* won't fit in array */ | |||
403 | } | |||
404 | ||||
405 | for (tss->nr = 0; tss->nr < ts_pd->payload.v2ts.isat_num; ) { | |||
406 | diag_t d; | |||
407 | struct traffic_selector *ts = &tss->ts[tss->nr]; | |||
408 | ||||
409 | *ts = (struct traffic_selector){0}; | |||
410 | ||||
411 | struct ikev2_ts_header ts_h; | |||
412 | struct pbs_inpacket_byte_stream ts_body_pbs; | |||
413 | ||||
414 | d = pbs_in_struct(&ts_pd->pbs, &ikev2_ts_header_desc, | |||
415 | &ts_h, sizeof(ts_h), &ts_body_pbs); | |||
416 | ||||
417 | switch (ts_h.isath_type) { | |||
418 | case IKEv2_TS_IPV4_ADDR_RANGE: | |||
419 | case IKEv2_TS_IPV6_ADDR_RANGE: | |||
420 | { | |||
421 | ts->ipprotoid = ts_h.isath_ipprotoid; | |||
422 | ||||
423 | /* read and fill in port range */ | |||
424 | struct ikev2_ts_portrange pr; | |||
425 | ||||
426 | d = pbs_in_struct(&ts_body_pbs, &ikev2_ts_portrange_desc, | |||
427 | &pr, sizeof(pr), NULL((void*)0)); | |||
428 | if (d != NULL((void*)0)) { | |||
429 | llog_diag(RC_LOG, logger, &d, "%s", ""); | |||
430 | return false0; | |||
431 | } | |||
432 | ||||
433 | ts->startport = pr.isatpr_startport; | |||
434 | ts->endport = pr.isatpr_endport; | |||
435 | ||||
436 | if (ts->startport > ts->endport) { | |||
437 | llog(RC_LOG, logger, | |||
438 | "%s traffic selector %d has an invalid port range - ignored", | |||
439 | tss->name, tss->nr); | |||
440 | continue; | |||
441 | } | |||
442 | ||||
443 | /* read and fill in IP address range */ | |||
444 | const struct ip_info *ipv; | |||
445 | switch (ts_h.isath_type) { | |||
446 | case IKEv2_TS_IPV4_ADDR_RANGE: | |||
447 | ipv = &ipv4_info; | |||
448 | break; | |||
449 | case IKEv2_TS_IPV6_ADDR_RANGE: | |||
450 | ipv = &ipv6_info; | |||
451 | break; | |||
452 | default: | |||
453 | bad_case(ts_h.isath_type)libreswan_bad_case("ts_h.isath_type", (ts_h.isath_type), ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c" , .line = 453, }; &here; })); /* make compiler happy */ | |||
454 | } | |||
455 | ||||
456 | ip_address start; | |||
457 | d = pbs_in_address(&ts_body_pbs, &start, ipv, "TS IP start"); | |||
458 | if (d != NULL((void*)0)) { | |||
459 | llog_diag(RC_LOG, logger, &d, "%s", ""); | |||
460 | return false0; | |||
461 | } | |||
462 | ||||
463 | ip_address end; | |||
464 | d = pbs_in_address(&ts_body_pbs, &end, ipv, "TS IP end"); | |||
465 | if (d != NULL((void*)0)) { | |||
466 | llog_diag(RC_LOG, logger, &d, "%s", ""); | |||
467 | return false0; | |||
468 | } | |||
469 | ||||
470 | /* XXX: does this matter? */ | |||
471 | if (pbs_left(&ts_body_pbs)((size_t)((&ts_body_pbs)->roof - (&ts_body_pbs)-> cur)) != 0) | |||
472 | return false0; | |||
473 | ||||
474 | err_t err = addresses_to_nonzero_range(start, end, &ts->net); | |||
475 | ||||
476 | /* pluto doesn't yet do full ranges; check for subnet */ | |||
477 | ip_subnet ignore; | |||
478 | err = err == NULL((void*)0) ? range_to_subnet(ts->net, &ignore) : err; | |||
479 | ||||
480 | if (err != NULL((void*)0)) { | |||
481 | address_buf sb, eb; | |||
482 | llog(RC_LOG, logger, "Traffic Selector range %s-%s invalid: %s", | |||
483 | str_address_sensitive(&start, &sb), | |||
484 | str_address_sensitive(&end, &eb), | |||
485 | err); | |||
486 | return false0; | |||
487 | } | |||
488 | ||||
489 | ts->ts_type = ts_h.isath_type; | |||
490 | break; | |||
491 | } | |||
492 | ||||
493 | case IKEv2_TS_SECLABEL: | |||
494 | { | |||
495 | if (ts_h.isath_ipprotoid != 0) { | |||
496 | llog(RC_LOG, logger, "Traffic Selector of type Security Label should not have non-zero IP protocol '%u' - ignored", | |||
497 | ts_h.isath_ipprotoid); | |||
498 | } | |||
499 | ||||
500 | shunk_t sec_label = pbs_in_left_as_shunk(&ts_body_pbs); | |||
501 | err_t ugh = vet_seclabel(sec_label); | |||
502 | if (ugh != NULL((void*)0)) { | |||
503 | llog(RC_LOG, logger, "Traffic Selector %s", ugh); | |||
504 | /* ??? should we just ignore? If so, use continue */ | |||
505 | return false0; | |||
506 | } | |||
507 | ||||
508 | ts->sec_label = sec_label; | |||
509 | ts->ts_type = ts_h.isath_type; | |||
510 | tss->contains_sec_label = true1; | |||
511 | break; | |||
512 | } | |||
513 | ||||
514 | case IKEv2_TS_FC_ADDR_RANGE: | |||
515 | llog(RC_LOG, logger, "Encountered Traffic Selector Type FC_ADDR_RANGE not supported"); | |||
516 | return false0; | |||
517 | ||||
518 | default: | |||
519 | llog(RC_LOG, logger, "Encountered Traffic Selector of unknown Type"); | |||
520 | return false0; | |||
521 | } | |||
522 | tss->nr++; | |||
523 | } | |||
524 | ||||
525 | dbg("%s: parsed %d traffic selectors", tss->name, tss->nr){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("%s: parsed %d traffic selectors", tss->name , tss->nr); } }; | |||
526 | return true1; | |||
527 | } | |||
528 | ||||
529 | static bool_Bool v2_parse_tsp(const struct msg_digest *md, | |||
530 | struct traffic_selector_payloads *tsp, | |||
531 | struct logger *logger) | |||
532 | { | |||
533 | if (!v2_parse_tss(md->chain[ISAKMP_NEXT_v2TSi], &tsp->i, logger)) { | |||
534 | return false0; | |||
535 | } | |||
536 | ||||
537 | if (!v2_parse_tss(md->chain[ISAKMP_NEXT_v2TSr], &tsp->r, logger)) { | |||
538 | return false0; | |||
539 | } | |||
540 | ||||
541 | return true1; | |||
542 | } | |||
543 | ||||
544 | #define MATCH_PREFIX" " " " | |||
545 | ||||
546 | /* | |||
547 | * Check if our policy's protocol (proto) matches the Traffic Selector | |||
548 | * protocol (ts_proto). | |||
549 | */ | |||
550 | ||||
551 | static int narrow_protocol(const struct end *end, | |||
552 | const struct traffic_selectors *tss, | |||
553 | enum fit fit, unsigned index) | |||
554 | { | |||
555 | const struct traffic_selector *ts = &tss->ts[index]; | |||
556 | int protocol = -1; | |||
557 | ||||
558 | switch (fit) { | |||
559 | case END_EQUALS_TS: | |||
560 | if (end->protocol == ts->ipprotoid) { | |||
561 | protocol = end->protocol; | |||
562 | } | |||
563 | break; | |||
564 | case END_NARROWER_THAN_TS: | |||
565 | if (ts->ipprotoid == 0 /* wild-card */ || | |||
566 | ts->ipprotoid == end->protocol) { | |||
567 | protocol = end->protocol; | |||
568 | } | |||
569 | break; | |||
570 | case END_WIDER_THAN_TS: | |||
571 | if (end->protocol == 0 /* wild-card */ || | |||
572 | end->protocol == ts->ipprotoid) { | |||
573 | protocol = ts->ipprotoid; | |||
574 | } | |||
575 | break; | |||
576 | default: | |||
577 | bad_case(fit)libreswan_bad_case("fit", (fit), ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c" , .line = 577, }; &here; })); | |||
578 | } | |||
579 | dbg(MATCH_PREFIX "narrow protocol end=%s%d %s %s[%u]=%s%d: %d",{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" " "narrow protocol end=%s%d %s %s[%u]=%s%d: %d" , end->protocol == 0 ? "*" : "", end->protocol, fit_string (fit), tss->name, index, ts->ipprotoid == 0 ? "*" : "", ts->ipprotoid, protocol); } } | |||
580 | end->protocol == 0 ? "*" : "", end->protocol,{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" " "narrow protocol end=%s%d %s %s[%u]=%s%d: %d" , end->protocol == 0 ? "*" : "", end->protocol, fit_string (fit), tss->name, index, ts->ipprotoid == 0 ? "*" : "", ts->ipprotoid, protocol); } } | |||
581 | fit_string(fit),{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" " "narrow protocol end=%s%d %s %s[%u]=%s%d: %d" , end->protocol == 0 ? "*" : "", end->protocol, fit_string (fit), tss->name, index, ts->ipprotoid == 0 ? "*" : "", ts->ipprotoid, protocol); } } | |||
582 | tss->name, index, ts->ipprotoid == 0 ? "*" : "", ts->ipprotoid,{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" " "narrow protocol end=%s%d %s %s[%u]=%s%d: %d" , end->protocol == 0 ? "*" : "", end->protocol, fit_string (fit), tss->name, index, ts->ipprotoid == 0 ? "*" : "", ts->ipprotoid, protocol); } } | |||
583 | protocol){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" " "narrow protocol end=%s%d %s %s[%u]=%s%d: %d" , end->protocol == 0 ? "*" : "", end->protocol, fit_string (fit), tss->name, index, ts->ipprotoid == 0 ? "*" : "", ts->ipprotoid, protocol); } }; | |||
584 | return protocol; | |||
585 | } | |||
586 | ||||
587 | static int score_narrow_protocol(const struct end *end, | |||
588 | const struct traffic_selectors *tss, | |||
589 | enum fit fit, unsigned index) | |||
590 | { | |||
591 | int f; /* strength of match */ | |||
592 | ||||
593 | int protocol = narrow_protocol(end, tss, fit, index); | |||
594 | if (protocol == 0) { | |||
595 | f = 255; /* ??? odd value */ | |||
596 | } else if (protocol > 0) { | |||
597 | f = 1; | |||
598 | } else { | |||
599 | f = 0; | |||
600 | } | |||
601 | LSWDBGP(DBG_BASE, buf)for (_Bool lswlog_p = (cur_debugging & (((lset_t)1 << (DBG_BASE_IX)))); lswlog_p; lswlog_p = 0) for (char lswbuf[( (size_t)1024)], *lswbuf_ = lswbuf; lswbuf_ != ((void*)0); lswbuf_ = ((void*)0)) for (struct jambuf jambuf = array_as_jambuf((lswbuf ), sizeof(lswbuf)), *buf = &jambuf; buf != ((void*)0); buf = ((void*)0)) for (; buf != ((void*)0); jambuf_to_logger(buf , &failsafe_logger, DEBUG_STREAM), buf = ((void*)0)) { | |||
602 | const struct traffic_selector *ts = &tss->ts[index]; | |||
603 | jam(buf, MATCH_PREFIX" " "match end->protocol=%s%d %s %s[%u].ipprotoid=%s%d: ", | |||
604 | end->protocol == 0 ? "*" : "", end->protocol, | |||
605 | fit_string(fit), | |||
606 | tss->name, index, ts->ipprotoid == 0 ? "*" : "", ts->ipprotoid); | |||
607 | if (f > 0) { | |||
608 | jam(buf, "YES fitness %d", f); | |||
609 | } else { | |||
610 | jam(buf, "NO"); | |||
611 | } | |||
612 | } | |||
613 | return f; | |||
614 | } | |||
615 | ||||
616 | /* | |||
617 | * Narrow the END/TS ports according to FIT. | |||
618 | * | |||
619 | * Returns 0 (all ports), a specific port number, or -1 (no luck). | |||
620 | * | |||
621 | * Since 'struct end' only describes all-ports or a single port; only | |||
622 | * narrow to that. | |||
623 | */ | |||
624 | ||||
625 | static int narrow_port(const struct end *end, | |||
626 | const struct traffic_selectors *tss, | |||
627 | enum fit fit, unsigned index) | |||
628 | { | |||
629 | passert(index < tss->nr)({ _Bool assertion__ = index < tss->nr; if (!assertion__ ) { where_t here = ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c", .line = 629 , }; &here; }); const struct logger *logger_ = &failsafe_logger ; llog_passert(logger_, here, "%s", "index < tss->nr"); } (void) 1; }); | |||
630 | const struct traffic_selector *ts = &tss->ts[index]; | |||
631 | ||||
632 | int end_low = end->port; | |||
633 | int end_high = end->port == 0 ? 65535 : end->port; | |||
634 | int port = -1; | |||
635 | ||||
636 | switch (fit) { | |||
637 | case END_EQUALS_TS: | |||
638 | if (end_low == ts->startport && ts->endport == end_high) { | |||
639 | /* end=ts=0-65535 || end=ts=N-N */ | |||
640 | port = end_low; | |||
641 | } | |||
642 | break; | |||
643 | case END_NARROWER_THAN_TS: | |||
644 | if (ts->startport <= end_low && end_high <= ts->endport) { | |||
645 | /* end=ts=0-65535 || ts=N<=end<=M */ | |||
646 | port = end_low; | |||
647 | } | |||
648 | break; | |||
649 | case END_WIDER_THAN_TS: | |||
650 | if (end_low < ts->startport && ts->endport < end_high && | |||
651 | ts->startport == ts->endport) { | |||
652 | /*ts=0<N-N<65535*/ | |||
653 | port = ts->startport; | |||
654 | } else if (end_low == ts->startport && ts->endport == end_high) { | |||
655 | /* end=ts=0-65535 || end=ts=N-N */ | |||
656 | port = ts->startport; | |||
657 | } | |||
658 | break; | |||
659 | default: | |||
660 | bad_case(fit)libreswan_bad_case("fit", (fit), ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c" , .line = 660, }; &here; })); | |||
661 | } | |||
662 | dbg(MATCH_PREFIX "narrow port end=%u..%u %s %s[%u]=%u..%u: %d",{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" " "narrow port end=%u..%u %s %s[%u]=%u..%u: %d" , end_low, end_high, fit_string(fit), tss->name, index, ts ->startport, ts->endport, port); } } | |||
663 | end_low, end_high,{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" " "narrow port end=%u..%u %s %s[%u]=%u..%u: %d" , end_low, end_high, fit_string(fit), tss->name, index, ts ->startport, ts->endport, port); } } | |||
664 | fit_string(fit),{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" " "narrow port end=%u..%u %s %s[%u]=%u..%u: %d" , end_low, end_high, fit_string(fit), tss->name, index, ts ->startport, ts->endport, port); } } | |||
665 | tss->name, index, ts->startport, ts->endport,{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" " "narrow port end=%u..%u %s %s[%u]=%u..%u: %d" , end_low, end_high, fit_string(fit), tss->name, index, ts ->startport, ts->endport, port); } } | |||
666 | port){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" " "narrow port end=%u..%u %s %s[%u]=%u..%u: %d" , end_low, end_high, fit_string(fit), tss->name, index, ts ->startport, ts->endport, port); } }; | |||
667 | return port; | |||
668 | } | |||
669 | ||||
670 | /* | |||
671 | * Assign a score to the narrowed port, rationale for score lost in | |||
672 | * time? | |||
673 | */ | |||
674 | ||||
675 | static int score_narrow_port(const struct end *end, | |||
676 | const struct traffic_selectors *tss, | |||
677 | enum fit fit, unsigned index) | |||
678 | { | |||
679 | int f; /* strength of match */ | |||
680 | ||||
681 | int port = narrow_port(end, tss, fit, index); | |||
682 | if (port > 0) { | |||
683 | f = 1; | |||
684 | } else if (port == 0) { | |||
685 | f = 65536; /* from 1 + 65535-0 */ | |||
686 | } else { | |||
687 | f = 0; | |||
688 | } | |||
689 | if (f > 0) { | |||
690 | dbg(MATCH_PREFIX " %s[%u] port match: YES fitness %d",{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" " " %s[%u] port match: YES fitness %d" , tss->name, index, f); } } | |||
691 | tss->name, index, f){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" " " %s[%u] port match: YES fitness %d" , tss->name, index, f); } }; | |||
692 | } else { | |||
693 | dbg(MATCH_PREFIX " %s[%u] port match: NO",{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" " " %s[%u] port match: NO", tss-> name, index); } } | |||
694 | tss->name, index){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" " " %s[%u] port match: NO", tss-> name, index); } }; | |||
695 | } | |||
696 | return f; | |||
697 | } | |||
698 | ||||
699 | ||||
700 | /* | |||
701 | * Does TS fit inside of END? | |||
702 | * | |||
703 | * Given other code flips the comparison depending initiator or | |||
704 | * responder, is this right? | |||
705 | * | |||
706 | * NOTE: Our parser/config only allows 1 CIDR, however IKEv2 ranges | |||
707 | * can be non-CIDR for now we really support/limit ourselves to | |||
708 | * a single CIDR | |||
709 | */ | |||
710 | ||||
711 | static int score_address_range(const struct end *end, | |||
712 | const struct traffic_selectors *tss, | |||
713 | enum fit fit, unsigned index) | |||
714 | { | |||
715 | const struct traffic_selector *ts = &tss->ts[index]; | |||
716 | /* | |||
717 | * Pre-compute possible fit --- sum of bits gives how good a | |||
718 | * fit this is. | |||
719 | */ | |||
720 | int ts_range = range_host_bits(ts->net); | |||
721 | int maskbits = end->client.maskbits; | |||
722 | int fitbits = maskbits + ts_range; | |||
723 | ||||
724 | int f = 0; | |||
725 | ||||
726 | /* | |||
727 | * NOTE: Our parser/config only allows 1 CIDR, however IKEv2 | |||
728 | * ranges can be non-CIDR for now we really | |||
729 | * support/limit ourselves to a single CIDR | |||
730 | * | |||
731 | * XXX: so what is CIDR? | |||
732 | */ | |||
733 | ip_range range = selector_range(end->client); | |||
734 | switch (fit) { | |||
735 | case END_EQUALS_TS: | |||
736 | if (range_eq_range(range, ts->net)) { | |||
737 | f = fitbits; | |||
738 | } | |||
739 | break; | |||
740 | case END_NARROWER_THAN_TS: | |||
741 | if (range_in_range(range, ts->net)) { | |||
742 | f = fitbits; | |||
743 | } | |||
744 | break; | |||
745 | case END_WIDER_THAN_TS: | |||
746 | if (range_in_range(ts->net, range)) { | |||
747 | f = fitbits; | |||
748 | } | |||
749 | break; | |||
750 | default: | |||
751 | bad_case(fit)libreswan_bad_case("fit", (fit), ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c" , .line = 751, }; &here; })); | |||
752 | } | |||
753 | ||||
754 | /* | |||
755 | * comparing for ports for finding better local policy | |||
756 | * | |||
757 | * XXX: why do this? | |||
758 | */ | |||
759 | /* ??? arbitrary modification to objective function */ | |||
760 | if (end->port != 0 && | |||
761 | ts->startport == end->port && | |||
762 | ts->endport == end->port) | |||
763 | f = f << 1; | |||
764 | ||||
765 | LSWDBGP(DBG_BASE, buf)for (_Bool lswlog_p = (cur_debugging & (((lset_t)1 << (DBG_BASE_IX)))); lswlog_p; lswlog_p = 0) for (char lswbuf[( (size_t)1024)], *lswbuf_ = lswbuf; lswbuf_ != ((void*)0); lswbuf_ = ((void*)0)) for (struct jambuf jambuf = array_as_jambuf((lswbuf ), sizeof(lswbuf)), *buf = &jambuf; buf != ((void*)0); buf = ((void*)0)) for (; buf != ((void*)0); jambuf_to_logger(buf , &failsafe_logger, DEBUG_STREAM), buf = ((void*)0)) { | |||
766 | jam(buf, MATCH_PREFIX" " "match address end->client="); | |||
767 | jam_selector(buf, &end->client); | |||
768 | jam(buf, " %s %s[%u]net=", fit_string(fit), tss->name, index); | |||
769 | jam_range(buf, &ts->net); | |||
770 | jam(buf, ": "); | |||
771 | if (f > 0) { | |||
772 | jam(buf, "YES fitness %d", f); | |||
773 | } else { | |||
774 | jam(buf, "NO"); | |||
775 | } | |||
776 | } | |||
777 | return f; | |||
778 | } | |||
779 | ||||
780 | struct score { | |||
781 | bool_Bool ok; | |||
782 | int address; | |||
783 | int port; | |||
784 | int protocol; | |||
785 | }; | |||
786 | ||||
787 | static struct score score_end(const struct end *end, | |||
788 | const struct traffic_selectors *tss, | |||
789 | enum fit fit, unsigned index) | |||
790 | { | |||
791 | const struct traffic_selector *ts = &tss->ts[index]; | |||
792 | ||||
793 | LSWDBGP(DBG_BASE, buf)for (_Bool lswlog_p = (cur_debugging & (((lset_t)1 << (DBG_BASE_IX)))); lswlog_p; lswlog_p = 0) for (char lswbuf[( (size_t)1024)], *lswbuf_ = lswbuf; lswbuf_ != ((void*)0); lswbuf_ = ((void*)0)) for (struct jambuf jambuf = array_as_jambuf((lswbuf ), sizeof(lswbuf)), *buf = &jambuf; buf != ((void*)0); buf = ((void*)0)) for (; buf != ((void*)0); jambuf_to_logger(buf , &failsafe_logger, DEBUG_STREAM), buf = ((void*)0)) { | |||
794 | jam(buf, " %s[%u]", tss->name, index); | |||
795 | if (ts->sec_label.len > 0) { | |||
796 | jam(buf, " sec_label="); | |||
797 | jam_sanitized_hunk(buf, ts->sec_label)({ typeof(ts->sec_label) hunk_ = (ts->sec_label); jam_sanitized_bytes (buf, hunk_.ptr, hunk_.len); }); | |||
798 | } else { | |||
799 | range_buf ts_net; | |||
800 | jam(buf, " net=%s iporotoid=%d {start,end}port=%d..%d", | |||
801 | str_range(&ts->net, &ts_net), | |||
802 | ts->ipprotoid, | |||
803 | ts->startport, | |||
804 | ts->endport); | |||
805 | } | |||
806 | } | |||
807 | ||||
808 | struct score score = { | |||
809 | .ok = false0, | |||
810 | }; | |||
811 | ||||
812 | switch(ts->ts_type) { | |||
813 | case IKEv2_TS_IPV4_ADDR_RANGE: | |||
814 | case IKEv2_TS_IPV6_ADDR_RANGE: | |||
815 | score.address = score_address_range(end, tss, fit, index); | |||
816 | if (score.address <= 0) { | |||
817 | return score; | |||
818 | } | |||
819 | score.port = score_narrow_port(end, tss, fit, index); | |||
820 | if (score.port <= 0) { | |||
821 | return score; | |||
822 | } | |||
823 | score.protocol = score_narrow_protocol(end, tss, fit, index); | |||
824 | if (score.protocol <= 0) { | |||
825 | return score; | |||
826 | } | |||
827 | score.ok = true1; | |||
828 | return score; | |||
829 | case IKEv2_TS_SECLABEL: | |||
830 | default: | |||
831 | return score; | |||
832 | } | |||
833 | } | |||
834 | ||||
835 | struct best_score { | |||
836 | bool_Bool ok; | |||
837 | int address; | |||
838 | int port; | |||
839 | int protocol; | |||
840 | const struct traffic_selector *tsi; | |||
841 | const struct traffic_selector *tsr; | |||
842 | }; | |||
843 | #define NO_SCORE{ .ok = 0, .address = -1, .port = -1, .protocol = -1, } { .ok = false0, .address = -1, .port = -1, .protocol = -1, } | |||
844 | ||||
845 | static bool_Bool score_gt(const struct best_score *score, const struct best_score *best) | |||
846 | { | |||
847 | return (score->address > best->address || | |||
848 | (score->address == best->address && | |||
849 | score->port > best->port) || | |||
850 | (score->address == best->address && | |||
851 | score->port == best->port && | |||
852 | score->protocol > best->protocol)); | |||
853 | } | |||
854 | ||||
855 | /* | |||
856 | * Return true for sec_label expected and good XOR not expected and | |||
857 | * not present. | |||
858 | * | |||
859 | * Return false when required sec_label is missing or bad; or | |||
860 | * sec_label encountered when it wasn't expected. | |||
861 | * | |||
862 | * The code calls sec_label_within_range() to check that the "source | |||
863 | * context has the access permission for the specified class on the | |||
864 | * "target context" (see selinux_check_access()): | |||
865 | * | |||
866 | * - for the initiator searching for a matching template connection to | |||
867 | * instantiate, the "source context" is the sec_label from acquire, | |||
868 | * and the "target context" is the sec_label in the template | |||
869 | * connection. | |||
870 | * | |||
871 | * - for the responder searching for a template connection to match | |||
872 | * the on-wire TS, the "source context" is the sec_label included in | |||
873 | * the traffic selector, and the "target context" is (again) the | |||
874 | * sec_label in the template connection. | |||
875 | * | |||
876 | * However, when the initiator gets back the responder's accepted TS | |||
877 | * containing a sec_label, the initiator only checks it is identical | |||
878 | * to what was sent out in the initiator's TS: | |||
879 | * | |||
880 | * - the RFC makes vague references to narrowing; but what that means | |||
881 | * for sec_labels isn't clear | |||
882 | * | |||
883 | * - one interpretation, that the responder's "source context" has | |||
884 | * "access permission" for the initiator's "source context" seems to | |||
885 | * always fail when enforcing is enabled (suspect | |||
886 | * selinux_check_access() requires a "ptarget context"). | |||
887 | */ | |||
888 | ||||
889 | static bool_Bool check_tss_sec_label(const struct traffic_selectors *tss, | |||
890 | chunk_t config_sec_label, | |||
891 | shunk_t *selected_sec_label, | |||
892 | struct logger *logger) | |||
893 | { | |||
894 | passert(tss->contains_sec_label)({ _Bool assertion__ = tss->contains_sec_label; if (!assertion__ ) { where_t here = ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c", .line = 894 , }; &here; }); const struct logger *logger_ = &failsafe_logger ; llog_passert(logger_, here, "%s", "tss->contains_sec_label" ); } (void) 1; }); | |||
895 | ||||
896 | *selected_sec_label = null_shunk; | |||
897 | for (unsigned i = 0; i < tss->nr; i++) { | |||
898 | const struct traffic_selector *ts = &tss->ts[i]; | |||
899 | if (ts->ts_type != IKEv2_TS_SECLABEL) { | |||
900 | continue; | |||
901 | } | |||
902 | ||||
903 | passert(vet_seclabel(ts->sec_label) == NULL)({ _Bool assertion__ = vet_seclabel(ts->sec_label) == ((void *)0); if (!assertion__) { where_t here = ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c" , .line = 903, }; &here; }); const struct logger *logger_ = &failsafe_logger; llog_passert(logger_, here, "%s", "vet_seclabel(ts->sec_label) == ((void*)0)" ); } (void) 1; }); | |||
904 | ||||
905 | if (!sec_label_within_range(ts->sec_label, config_sec_label, logger)) { | |||
906 | dbg("ikev2ts: %s sec_label="PRI_SHUNK" is not within range connection sec_label="PRI_SHUNK,{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("ikev2ts: %s sec_label=""%.*s"" is not within range connection sec_label=" "%.*s", tss->name, ((int) (ts->sec_label).len), (const char *) ((ts->sec_label).ptr), ((int) (config_sec_label).len), (const char *) ((config_sec_label).ptr)); } } | |||
907 | tss->name, pri_shunk(ts->sec_label), pri_shunk(config_sec_label)){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("ikev2ts: %s sec_label=""%.*s"" is not within range connection sec_label=" "%.*s", tss->name, ((int) (ts->sec_label).len), (const char *) ((ts->sec_label).ptr), ((int) (config_sec_label).len), (const char *) ((config_sec_label).ptr)); } }; | |||
908 | continue; | |||
909 | } | |||
910 | ||||
911 | dbg("ikev2ts: received %s label within range of our security label",{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("ikev2ts: received %s label within range of our security label" , tss->name); } } | |||
912 | tss->name){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("ikev2ts: received %s label within range of our security label" , tss->name); } }; | |||
913 | ||||
914 | /* XXX we return the first match. Should we return the best? */ | |||
915 | *selected_sec_label = ts->sec_label; /* first match */ | |||
916 | return true1; | |||
917 | } | |||
918 | ||||
919 | return false0; | |||
920 | } | |||
921 | ||||
922 | static bool_Bool score_tsp_sec_label(const struct traffic_selector_payloads *tsp, | |||
923 | chunk_t config_sec_label, | |||
924 | shunk_t *selected_sec_label, | |||
925 | struct logger *logger) | |||
926 | { | |||
927 | if (config_sec_label.len == 0) { | |||
928 | /* This endpoint is not configured to use labeled IPsec. */ | |||
929 | if (tsp->i.contains_sec_label || tsp->r.contains_sec_label) { | |||
930 | dbg("error: received sec_label but this end is *not* configured to use sec_label"){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("error: received sec_label but this end is *not* configured to use sec_label" ); } }; | |||
931 | return false0; | |||
932 | } | |||
933 | /* No sec_label was found and none was expected */ | |||
934 | return true1; /* success: no label, as expected */ | |||
935 | } | |||
936 | ||||
937 | /* This endpoint is configured to use labeled IPsec. */ | |||
938 | passert(vet_seclabel(HUNK_AS_SHUNK(config_sec_label)) == NULL)({ _Bool assertion__ = vet_seclabel(({ typeof(config_sec_label ) h_ = (config_sec_label); shunk2(h_.ptr, h_.len); })) == ((void *)0); if (!assertion__) { where_t here = ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c" , .line = 938, }; &here; }); const struct logger *logger_ = &failsafe_logger; llog_passert(logger_, here, "%s", "vet_seclabel(({ typeof(config_sec_label) h_ = (config_sec_label); shunk2(h_.ptr, h_.len); })) == ((void*)0)" ); } (void) 1; }); | |||
939 | ||||
940 | if (!tsp->i.contains_sec_label || !tsp->r.contains_sec_label) { | |||
941 | dbg("error: connection requires sec_label but not received TSi/TSr with sec_label"){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("error: connection requires sec_label but not received TSi/TSr with sec_label" ); } }; | |||
942 | return false0; | |||
943 | } | |||
944 | ||||
945 | if (!check_tss_sec_label(&tsp->i, config_sec_label, selected_sec_label, logger) || | |||
946 | !check_tss_sec_label(&tsp->r, config_sec_label, selected_sec_label, logger)) { | |||
947 | return false0; | |||
948 | } | |||
949 | ||||
950 | /* security label required and matched */ | |||
951 | return true1; | |||
952 | } | |||
953 | ||||
954 | static struct best_score score_ends_iprange(enum fit fit, | |||
955 | const struct connection *d, | |||
956 | const struct ends *ends, | |||
957 | const struct traffic_selector_payloads *tsp) | |||
958 | { | |||
959 | if (DBGP(DBG_BASE)(cur_debugging & (((lset_t)1 << (DBG_BASE_IX))))) { | |||
960 | selector_buf ei3; | |||
961 | selector_buf er3; | |||
962 | connection_buf cib; | |||
963 | DBG_log("evaluating our conn="PRI_CONNECTION"\"%s\"%s"" I=%s:%d/%d R=%s:%d/%d%s to their:", | |||
964 | pri_connection(d, &cib)(d)->name, str_connection_instance(d, &cib), | |||
965 | str_selector(&ends->i->client, &ei3), ends->i->protocol, ends->i->port, | |||
966 | str_selector(&ends->r->client, &er3), ends->r->protocol, ends->r->port, | |||
967 | is_virtual_connection(d) ? " (virt)" : ""); | |||
968 | } | |||
969 | ||||
970 | struct best_score best_score = NO_SCORE{ .ok = 0, .address = -1, .port = -1, .protocol = -1, }; | |||
971 | ||||
972 | /* compare tsi/r array to this/that, evaluating how well it fits */ | |||
973 | for (unsigned tsi_n = 0; tsi_n < tsp->i.nr; tsi_n++) { | |||
974 | const struct traffic_selector *tni = &tsp->i.ts[tsi_n]; | |||
975 | ||||
976 | /* choice hardwired for IPrange and sec_label */ | |||
977 | struct score score_i = score_end(ends->i, &tsp->i, fit, tsi_n); | |||
978 | if (!score_i.ok) { | |||
979 | continue; | |||
980 | } | |||
981 | ||||
982 | for (unsigned tsr_n = 0; tsr_n < tsp->r.nr; tsr_n++) { | |||
983 | const struct traffic_selector *tnr = &tsp->r.ts[tsr_n]; | |||
984 | ||||
985 | struct score score_r = score_end(ends->r, &tsp->r, fit, tsr_n); | |||
986 | if (!score_r.ok) { | |||
987 | continue; | |||
988 | } | |||
989 | struct best_score score = { | |||
990 | .ok = true1, | |||
991 | /* ??? this objective function is odd and arbitrary */ | |||
992 | .address = (score_i.address << 8) + score_r.address, | |||
993 | /* ??? arbitrary objective function */ | |||
994 | .port = score_i.port + score_r.port, | |||
995 | /* ??? arbitrary objective function */ | |||
996 | .protocol = score_i.protocol + score_r.protocol, | |||
997 | /* which one */ | |||
998 | .tsi = tni, .tsr = tnr, | |||
999 | }; | |||
1000 | ||||
1001 | /* score >= best_score? */ | |||
1002 | if (score_gt(&score, &best_score)) { | |||
1003 | best_score = score; | |||
1004 | dbg("best fit so far: TSi[%d] TSr[%d]",{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("best fit so far: TSi[%d] TSr[%d]", tsi_n, tsr_n ); } } | |||
1005 | tsi_n, tsr_n){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("best fit so far: TSi[%d] TSr[%d]", tsi_n, tsr_n ); } }; | |||
1006 | } | |||
1007 | } | |||
1008 | } | |||
1009 | ||||
1010 | return best_score; | |||
1011 | } | |||
1012 | ||||
1013 | static bool_Bool v2_child_connection_probably_shared(struct child_sa *child) | |||
1014 | { | |||
1015 | struct connection *c = child->sa.st_connection; | |||
1016 | ||||
1017 | if (connection_is_pending(c)) { | |||
1018 | dbg("#%lu connection is also pending; but what about pending for this state???",{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("#%lu connection is also pending; but what about pending for this state???" , child->sa.st_serialno); } } | |||
1019 | child->sa.st_serialno){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("#%lu connection is also pending; but what about pending for this state???" , child->sa.st_serialno); } }; | |||
1020 | return true1; | |||
1021 | } | |||
1022 | ||||
1023 | dbg("FOR_EACH_STATE_... in %s", __func__){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("FOR_EACH_STATE_... in %s", __func__); } }; | |||
1024 | struct ike_sa *ike = ike_sa(&child->sa, HERE({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c", .line = 1024, }; &here; } )); | |||
1025 | struct state *st = NULL((void*)0); | |||
1026 | FOR_EACH_STATE_NEW2OLD(st)for (struct list_entry *entry_ = (&state_serialno_list_head )->head.older; entry_ != ((void*)0); entry_ = ((void*)0)) for (st = (typeof(st))entry_->data, entry_ = entry_->older ; st != ((void*)0); st = (typeof(st))entry_->data, entry_ = entry_->older) { | |||
1027 | if (st->st_connection != c) { | |||
1028 | continue; | |||
1029 | } | |||
1030 | if (st == &child->sa) { | |||
1031 | dbg("ignoring ourselves #%lu sharing connection %s",{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("ignoring ourselves #%lu sharing connection %s" , st->st_serialno, c->name); } } | |||
1032 | st->st_serialno, c->name){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("ignoring ourselves #%lu sharing connection %s" , st->st_serialno, c->name); } }; | |||
1033 | continue; | |||
1034 | } | |||
1035 | if (st == &ike->sa) { | |||
1036 | dbg("ignoring IKE SA #%lu sharing connection %s with #%lu",{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("ignoring IKE SA #%lu sharing connection %s with #%lu" , st->st_serialno, c->name, child->sa.st_serialno); } } | |||
1037 | st->st_serialno, c->name, child->sa.st_serialno){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("ignoring IKE SA #%lu sharing connection %s with #%lu" , st->st_serialno, c->name, child->sa.st_serialno); } }; | |||
1038 | continue; | |||
1039 | } | |||
1040 | dbg("#%lu and #%lu share connection %s",{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("#%lu and #%lu share connection %s", child-> sa.st_serialno, st->st_serialno, c->name); } } | |||
1041 | child->sa.st_serialno, st->st_serialno,{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("#%lu and #%lu share connection %s", child-> sa.st_serialno, st->st_serialno, c->name); } } | |||
1042 | c->name){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("#%lu and #%lu share connection %s", child-> sa.st_serialno, st->st_serialno, c->name); } }; | |||
1043 | return true1; | |||
1044 | } | |||
1045 | ||||
1046 | return false0; | |||
1047 | } | |||
1048 | ||||
1049 | struct narrowed_ts { | |||
1050 | bool_Bool ok; | |||
1051 | int tsi_port; | |||
1052 | int tsi_protocol; | |||
1053 | int tsr_port; | |||
1054 | int tsr_protocol; | |||
1055 | }; | |||
1056 | ||||
1057 | static struct narrowed_ts narrow_request_ts(struct connection *c, | |||
1058 | const struct traffic_selector_payloads *tsp, | |||
1059 | enum fit fit) | |||
1060 | { | |||
1061 | struct narrowed_ts n = { | |||
1062 | .ok = false0, /* until proven */ | |||
1063 | }; | |||
1064 | ||||
1065 | passert(tsp->i.nr >= 1)({ _Bool assertion__ = tsp->i.nr >= 1; if (!assertion__ ) { where_t here = ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c", .line = 1065 , }; &here; }); const struct logger *logger_ = &failsafe_logger ; llog_passert(logger_, here, "%s", "tsp->i.nr >= 1"); } (void) 1; }); | |||
1066 | n.tsi_port = narrow_port(&c->spd.that, &tsp->i, fit, 0); | |||
1067 | if (n.tsi_port < 0) { | |||
1068 | dbg(" skipping; TSi port too wide"){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" skipping; TSi port too wide"); } }; | |||
1069 | return n; | |||
1070 | } | |||
1071 | ||||
1072 | n.tsi_protocol = narrow_protocol(&c->spd.that, &tsp->r, fit, 0); | |||
1073 | if (n.tsi_protocol < 0) { | |||
1074 | dbg(" skipping; TSi protocol too wide"){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" skipping; TSi protocol too wide"); } }; | |||
1075 | return n; | |||
1076 | } | |||
1077 | ||||
1078 | passert(tsp->r.nr >= 1)({ _Bool assertion__ = tsp->r.nr >= 1; if (!assertion__ ) { where_t here = ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c", .line = 1078 , }; &here; }); const struct logger *logger_ = &failsafe_logger ; llog_passert(logger_, here, "%s", "tsp->r.nr >= 1"); } (void) 1; }); | |||
1079 | n.tsr_port = narrow_port(&c->spd.this, &tsp->r, fit, 0); | |||
1080 | if (n.tsr_port < 0) { | |||
1081 | dbg(" skipping; TSr port too wide"){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" skipping; TSr port too wide"); } }; | |||
1082 | return n; | |||
1083 | } | |||
1084 | ||||
1085 | n.tsr_protocol = narrow_protocol(&c->spd.this, &tsp->r, fit, 0); | |||
1086 | if (n.tsr_protocol < 0) { | |||
1087 | dbg(" skipping; TSr protocol too wide"){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" skipping; TSr protocol too wide"); } }; | |||
1088 | return n; | |||
1089 | } | |||
1090 | ||||
1091 | n.ok = true1; | |||
1092 | return n; | |||
1093 | } | |||
1094 | ||||
1095 | static void scribble_request_ts_on_connection(struct child_sa *child, | |||
1096 | struct connection *c, | |||
1097 | struct narrowed_ts n) | |||
1098 | { | |||
1099 | if (c != child->sa.st_connection) { | |||
1100 | connection_buf from, to; | |||
1101 | dbg(" switching #%lu from "PRI_CONNECTION" to just-instantiated "PRI_CONNECTION,{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" switching #%lu from ""\"%s\"%s"" to just-instantiated " "\"%s\"%s", child->sa.st_serialno, (child->sa.st_connection )->name, str_connection_instance(child->sa.st_connection , &from), (c)->name, str_connection_instance(c, &to )); } } | |||
1102 | child->sa.st_serialno,{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" switching #%lu from ""\"%s\"%s"" to just-instantiated " "\"%s\"%s", child->sa.st_serialno, (child->sa.st_connection )->name, str_connection_instance(child->sa.st_connection , &from), (c)->name, str_connection_instance(c, &to )); } } | |||
1103 | pri_connection(child->sa.st_connection, &from),{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" switching #%lu from ""\"%s\"%s"" to just-instantiated " "\"%s\"%s", child->sa.st_serialno, (child->sa.st_connection )->name, str_connection_instance(child->sa.st_connection , &from), (c)->name, str_connection_instance(c, &to )); } } | |||
1104 | pri_connection(c, &to)){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" switching #%lu from ""\"%s\"%s"" to just-instantiated " "\"%s\"%s", child->sa.st_serialno, (child->sa.st_connection )->name, str_connection_instance(child->sa.st_connection , &from), (c)->name, str_connection_instance(c, &to )); } }; | |||
1105 | } else { | |||
1106 | connection_buf cib; | |||
1107 | dbg(" overwrote #%lu connection "PRI_CONNECTION,{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" overwrote #%lu connection ""\"%s\"%s", child ->sa.st_serialno, (c)->name, str_connection_instance(c, &cib)); } } | |||
1108 | child->sa.st_serialno, pri_connection(c, &cib)){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" overwrote #%lu connection ""\"%s\"%s", child ->sa.st_serialno, (c)->name, str_connection_instance(c, &cib)); } }; | |||
1109 | } | |||
1110 | ||||
1111 | /* "this" == responder; see function name */ | |||
1112 | c->spd.this.port = n.tsr_port; | |||
1113 | c->spd.that.port = n.tsi_port; | |||
1114 | c->spd.this.protocol = n.tsr_protocol; | |||
1115 | c->spd.that.protocol = n.tsi_protocol; | |||
1116 | /* hack */ | |||
1117 | dbg("XXX: updating best connection's ports/protocols"){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("XXX: updating best connection's ports/protocols" ); } }; | |||
1118 | update_selector_hport(&c->spd.this.client, n.tsr_port){ (&c->spd.this.client)->hport = (n.tsr_port); }; | |||
1119 | update_selector_hport(&c->spd.that.client, n.tsi_port){ (&c->spd.that.client)->hport = (n.tsi_port); }; | |||
1120 | update_selector_ipproto(&c->spd.this.client, n.tsr_protocol){ (&c->spd.this.client)->ipproto = (n.tsr_protocol) ; }; | |||
1121 | update_selector_ipproto(&c->spd.that.client, n.tsi_protocol){ (&c->spd.that.client)->ipproto = (n.tsi_protocol) ; }; | |||
1122 | } | |||
1123 | ||||
1124 | /* | |||
1125 | * Find the best connection, possibly updating child. | |||
1126 | */ | |||
1127 | bool_Bool v2_process_request_ts_payloads(struct child_sa *child, | |||
1128 | const struct msg_digest *md) | |||
1129 | { | |||
1130 | passert(v2_msg_role(md) == MESSAGE_REQUEST)({ _Bool assertion__ = v2_msg_role(md) == MESSAGE_REQUEST; if (!assertion__) { where_t here = ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c" , .line = 1130, }; &here; }); const struct logger *logger_ = &failsafe_logger; llog_passert(logger_, here, "%s", "v2_msg_role(md) == MESSAGE_REQUEST" ); } (void) 1; }); | |||
| ||||
1131 | passert(child->sa.st_sa_role == SA_RESPONDER)({ _Bool assertion__ = child->sa.st_sa_role == SA_RESPONDER ; if (!assertion__) { where_t here = ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c" , .line = 1131, }; &here; }); const struct logger *logger_ = &failsafe_logger; llog_passert(logger_, here, "%s", "child->sa.st_sa_role == SA_RESPONDER" ); } (void) 1; }); | |||
1132 | ||||
1133 | struct traffic_selector_payloads tsp = empty_traffic_selectors; | |||
1134 | if (!v2_parse_tsp(md, &tsp, child->sa.st_logger)) { | |||
1135 | return false0; | |||
1136 | } | |||
1137 | ||||
1138 | /* best so far; start with state's connection */ | |||
1139 | struct best_score best_score = NO_SCORE{ .ok = 0, .address = -1, .port = -1, .protocol = -1, }; | |||
1140 | const struct spd_route *best_spd_route = NULL((void*)0); | |||
1141 | struct connection *const c = child->sa.st_connection; | |||
1142 | struct connection *best_connection = c; | |||
1143 | shunk_t best_sec_label = null_shunk; | |||
1144 | ||||
1145 | /* find best spd in c */ | |||
1146 | ||||
1147 | connection_buf ccb; | |||
1148 | dbg("responder looking for best SPD in current connection "PRI_CONNECTION,{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("responder looking for best SPD in current connection " "\"%s\"%s", (c)->name, str_connection_instance(c, &ccb )); } } | |||
1149 | pri_connection(c, &ccb)){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("responder looking for best SPD in current connection " "\"%s\"%s", (c)->name, str_connection_instance(c, &ccb )); } }; | |||
1150 | for (const struct spd_route *sra = &c->spd; sra != NULL((void*)0); sra = sra->spd_next) { | |||
1151 | ||||
1152 | /* responder */ | |||
1153 | const struct ends ends = { | |||
1154 | .i = &sra->that, | |||
1155 | .r = &sra->this, | |||
1156 | }; | |||
1157 | ||||
1158 | shunk_t selected_sec_label = null_shunk; | |||
1159 | if (!score_tsp_sec_label(&tsp, c->config->sec_label, | |||
1160 | &selected_sec_label, | |||
1161 | child->sa.st_logger)) { | |||
1162 | /* | |||
1163 | * Either: | |||
1164 | * - Security label required, but not found. | |||
1165 | * OR | |||
1166 | * - Security label *not* required, but found. | |||
1167 | */ | |||
1168 | continue; | |||
1169 | } | |||
1170 | ||||
1171 | enum fit responder_fit = | |||
1172 | (c->policy & POLICY_IKEV2_ALLOW_NARROWING((lset_t)1 << (POLICY_IKEV2_ALLOW_NARROWING_IX))) | |||
1173 | ? END_NARROWER_THAN_TS | |||
1174 | : END_EQUALS_TS; | |||
1175 | struct best_score score = score_ends_iprange(responder_fit, c, &ends, &tsp); | |||
1176 | if (!score.ok) { | |||
1177 | continue; | |||
1178 | } | |||
1179 | ||||
1180 | if (score_gt(&score, &best_score)) { | |||
1181 | dbg(" found better spd route for TSi[%td],TSr[%td]",{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" found better spd route for TSi[%td],TSr[%td]" , score.tsi - tsp.i.ts, score.tsr - tsp.r.ts); } } | |||
1182 | score.tsi - tsp.i.ts, score.tsr - tsp.r.ts){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" found better spd route for TSi[%td],TSr[%td]" , score.tsi - tsp.i.ts, score.tsr - tsp.r.ts); } }; | |||
1183 | best_score = score; | |||
1184 | best_spd_route = sra; | |||
1185 | best_sec_label = selected_sec_label; | |||
1186 | /* better SPD still within current connection */ | |||
1187 | passert(best_connection == c)({ _Bool assertion__ = best_connection == c; if (!assertion__ ) { where_t here = ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c", .line = 1187 , }; &here; }); const struct logger *logger_ = &failsafe_logger ; llog_passert(logger_, here, "%s", "best_connection == c"); } (void) 1; }); | |||
1188 | } | |||
1189 | } | |||
1190 | ||||
1191 | /* | |||
1192 | * ??? the use of hp looks nonsensical. | |||
1193 | * Either the first non-empty host_pair should be used | |||
1194 | * (like the current code) and the following should | |||
1195 | * be broken into two loops: first find the non-empty | |||
1196 | * host_pair list, second look through the host_pair list. | |||
1197 | * OR | |||
1198 | * what's really meant is look at the host_pair for | |||
1199 | * each sra, something that matches the current | |||
1200 | * nested loop structure but not what it actually does. | |||
1201 | */ | |||
1202 | ||||
1203 | dbg("looking for better host pair"){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("looking for better host pair"); } }; | |||
1204 | ||||
1205 | const ip_address local = md->iface->ip_dev->id_address; | |||
1206 | FOR_EACH_THING(remote, endpoint_address(md->sender), unset_address)for (typeof(endpoint_address(md->sender)) things_[] = { endpoint_address (md->sender), unset_address }, *thingp_ = things_, remote; thingp_ < things_ + (sizeof(things_) / sizeof(*(things_)) ) ? (remote = *thingp_, 1) : 0; thingp_++) { | |||
1207 | ||||
1208 | FOR_EACH_HOST_PAIR_CONNECTION(local, remote, d)for (struct connection *next_ = ((void*)0), *d = next_host_pair_connection (local, remote, &next_, 1, ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c", . line = 1208, }; &here; })); d != ((void*)0); d = next_host_pair_connection (local, remote, &next_, 0, ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c", . line = 1208, }; &here; }))) { | |||
1209 | ||||
1210 | /* groups are templates instantiated as GROUPINSTANCE */ | |||
1211 | if (d->policy & POLICY_GROUP((lset_t)1 << (POLICY_GROUP_IX))) { | |||
1212 | continue; | |||
1213 | } | |||
1214 | ||||
1215 | /* | |||
1216 | * For labeled IPsec, always start with the | |||
1217 | * template. Who are we to argue if the | |||
1218 | * kernel asks for a new SA with, seemingly, a | |||
1219 | * security label that matches an existing | |||
1220 | * connection instance. | |||
1221 | */ | |||
1222 | if (c->ike_version == IKEv2 && | |||
1223 | c->config->sec_label.len > 0 && | |||
1224 | c->kind != CK_TEMPLATE) { | |||
1225 | connection_buf cb; | |||
1226 | dbg("skipping non-template IKEv2 "PRI_CONNECTION" with a security label",{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("skipping non-template IKEv2 ""\"%s\"%s"" with a security label" , (c)->name, str_connection_instance(c, &cb)); } } | |||
1227 | pri_connection(c, &cb)){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("skipping non-template IKEv2 ""\"%s\"%s"" with a security label" , (c)->name, str_connection_instance(c, &cb)); } }; | |||
1228 | continue; | |||
1229 | } | |||
1230 | ||||
1231 | dbg(" investigating connection \"%s\" as a better match", d->name){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" investigating connection \"%s\" as a better match" , d->name); } }; | |||
1232 | ||||
1233 | /* | |||
1234 | * ??? same_id && match_id seems redundant. | |||
1235 | * if d->spd.this.id.kind == ID_NONE, both TRUE | |||
1236 | * else if c->spd.this.id.kind == ID_NONE, | |||
1237 | * same_id treats it as a wildcard and match_id | |||
1238 | * does not. Odd. | |||
1239 | * else if kinds differ, match_id FALSE | |||
1240 | * else if kind ID_DER_ASN1_DN, wildcards are forbidden by same_id | |||
1241 | * else match_id just calls same_id. | |||
1242 | * So: if wildcards are desired, just use match_id. | |||
1243 | * If they are not, just use same_id | |||
1244 | */ | |||
1245 | int wildcards; /* value ignored */ | |||
1246 | int pathlen; /* value ignored */ | |||
1247 | ||||
1248 | /* conns created as aliases from the same source have identical ID/CA */ | |||
1249 | if (!(c->connalias != NULL((void*)0) && d->connalias != NULL((void*)0) && streq(c->connalias, d->connalias)(strcmp((c->connalias), (d->connalias)) == 0))) { | |||
1250 | if (!(same_id(&c->spd.this.id, &d->spd.this.id) && | |||
1251 | match_id(&c->spd.that.id, &d->spd.that.id, &wildcards) && | |||
1252 | trusted_ca_nss(c->spd.that.ca, d->spd.that.ca, &pathlen))) | |||
1253 | { | |||
1254 | dbg(" connection \"%s\" does not match IDs or CA of current connection \"%s\"",{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" connection \"%s\" does not match IDs or CA of current connection \"%s\"" , d->name, c->name); } } | |||
1255 | d->name, c->name){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" connection \"%s\" does not match IDs or CA of current connection \"%s\"" , d->name, c->name); } }; | |||
1256 | continue; | |||
1257 | } | |||
1258 | } | |||
1259 | ||||
1260 | const struct spd_route *sr; | |||
1261 | ||||
1262 | for (sr = &d->spd; sr != NULL((void*)0); sr = sr->spd_next) { | |||
1263 | ||||
1264 | /* responder */ | |||
1265 | const struct ends ends = { | |||
1266 | .i = &sr->that, | |||
1267 | .r = &sr->this, | |||
1268 | }; | |||
1269 | /* responder -- note D! */ | |||
1270 | enum fit responder_fit = | |||
1271 | (d->policy & POLICY_IKEV2_ALLOW_NARROWING((lset_t)1 << (POLICY_IKEV2_ALLOW_NARROWING_IX))) | |||
1272 | ? END_NARROWER_THAN_TS | |||
1273 | : END_EQUALS_TS; | |||
1274 | ||||
1275 | /* Returns NULL(ok), &null_shunk(skip), memory(ok). */ | |||
1276 | shunk_t selected_sec_label = null_shunk; | |||
1277 | if (!score_tsp_sec_label(&tsp, d->config->sec_label, | |||
1278 | &selected_sec_label, child->sa.st_logger)) { | |||
1279 | /* | |||
1280 | * Either: | |||
1281 | * - Security label required, but not found. | |||
1282 | * OR | |||
1283 | * - Security label *not* required, but found. | |||
1284 | */ | |||
1285 | continue; | |||
1286 | } | |||
1287 | ||||
1288 | struct best_score score = score_ends_iprange(responder_fit, d/*note D*/, | |||
1289 | &ends, &tsp); | |||
1290 | if (!score.ok) { | |||
1291 | continue; | |||
1292 | } | |||
1293 | if (score_gt(&score, &best_score)) { | |||
1294 | dbg(" protocol fitness found better match d %s, TSi[%td],TSr[%td]",{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" protocol fitness found better match d %s, TSi[%td],TSr[%td]" , d->name, score.tsi - tsp.i.ts, score.tsr - tsp.r.ts); } } | |||
1295 | d->name,{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" protocol fitness found better match d %s, TSi[%td],TSr[%td]" , d->name, score.tsi - tsp.i.ts, score.tsr - tsp.r.ts); } } | |||
1296 | score.tsi - tsp.i.ts, score.tsr - tsp.r.ts){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" protocol fitness found better match d %s, TSi[%td],TSr[%td]" , d->name, score.tsi - tsp.i.ts, score.tsr - tsp.r.ts); } }; | |||
1297 | best_connection = d; | |||
1298 | best_score = score; | |||
1299 | best_spd_route = sr; | |||
1300 | best_sec_label = selected_sec_label; | |||
1301 | } | |||
1302 | } | |||
1303 | } | |||
1304 | } | |||
1305 | ||||
1306 | if (best_connection
| |||
1307 | dbg(" did not find a better connection using host pair"){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" did not find a better connection using host pair" ); } }; | |||
1308 | } | |||
1309 | ||||
1310 | #define CONNECTION_POLICIES(((lset_t)1 << (POLICY_NEGO_PASS_IX)) | ((lset_t)1 << (POLICY_DONT_REKEY_IX)) | ((lset_t)1 << (POLICY_REAUTH_IX )) | ((lset_t)1 << (POLICY_OPPORTUNISTIC_IX)) | ((lset_t )1 << (POLICY_GROUP_IX)) | ((lset_t)1 << (POLICY_GROUTED_IX )) | ((lset_t)1 << (POLICY_GROUPINSTANCE_IX)) | ((lset_t )1 << (POLICY_UP_IX)) | ((lset_t)1 << (POLICY_XAUTH_IX )) | ((lset_t)1 << (POLICY_MODECFG_PULL_IX)) | ((lset_t )1 << (POLICY_AGGRESSIVE_IX)) | ((lset_t)1 << (POLICY_OVERLAPIP_IX )) | ((lset_t)1 << (POLICY_IKEV2_ALLOW_NARROWING_IX))) (POLICY_NEGO_PASS((lset_t)1 << (POLICY_NEGO_PASS_IX)) | \ | |||
1311 | POLICY_DONT_REKEY((lset_t)1 << (POLICY_DONT_REKEY_IX)) | \ | |||
1312 | POLICY_REAUTH((lset_t)1 << (POLICY_REAUTH_IX)) | \ | |||
1313 | POLICY_OPPORTUNISTIC((lset_t)1 << (POLICY_OPPORTUNISTIC_IX)) | \ | |||
1314 | POLICY_GROUP((lset_t)1 << (POLICY_GROUP_IX)) | \ | |||
1315 | POLICY_GROUTED((lset_t)1 << (POLICY_GROUTED_IX)) | \ | |||
1316 | POLICY_GROUPINSTANCE((lset_t)1 << (POLICY_GROUPINSTANCE_IX)) | \ | |||
1317 | POLICY_UP((lset_t)1 << (POLICY_UP_IX)) | \ | |||
1318 | POLICY_XAUTH((lset_t)1 << (POLICY_XAUTH_IX)) | \ | |||
1319 | POLICY_MODECFG_PULL((lset_t)1 << (POLICY_MODECFG_PULL_IX)) | \ | |||
1320 | POLICY_AGGRESSIVE((lset_t)1 << (POLICY_AGGRESSIVE_IX)) | \ | |||
1321 | POLICY_OVERLAPIP((lset_t)1 << (POLICY_OVERLAPIP_IX)) | \ | |||
1322 | POLICY_IKEV2_ALLOW_NARROWING((lset_t)1 << (POLICY_IKEV2_ALLOW_NARROWING_IX))) | |||
1323 | ||||
1324 | /* | |||
1325 | * Try instantiating something better. | |||
1326 | */ | |||
1327 | if (best_spd_route
| |||
1328 | /* | |||
1329 | * Don't try to look for something else to | |||
1330 | * 'instantiate' when the current connection is | |||
1331 | * permanent. | |||
1332 | * | |||
1333 | * XXX: but c->kind could be CK_TEMPLATE? | |||
1334 | * | |||
1335 | * XXX: Is this missing an opportunity? Could there | |||
1336 | * be a better connection to instantiate when the | |||
1337 | * current one is permanent? | |||
1338 | * | |||
1339 | * XXX: 'instantiate', not really? The code below | |||
1340 | * sometimes blats the current instance with new | |||
1341 | * values - something that should not be done to a | |||
1342 | * permanent connection. | |||
1343 | */ | |||
1344 | pexpect((c->kind == CK_PERMANENT) ||({ _Bool assertion__ = (c->kind == CK_PERMANENT) || (c-> kind == CK_TEMPLATE && c->config->sec_label.len > 0); if (!assertion__) { where_t here_ = ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c" , .line = 1345, }; &here; }); const struct logger *logger_ = &failsafe_logger; llog_pexpect(logger_, here_, "%s", "(c->kind == CK_PERMANENT) || (c->kind == CK_TEMPLATE && c->config->sec_label.len > 0)" ); } assertion__; }) | |||
1345 | (c->kind == CK_TEMPLATE && c->config->sec_label.len > 0))({ _Bool assertion__ = (c->kind == CK_PERMANENT) || (c-> kind == CK_TEMPLATE && c->config->sec_label.len > 0); if (!assertion__) { where_t here_ = ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c" , .line = 1345, }; &here; }); const struct logger *logger_ = &failsafe_logger; llog_pexpect(logger_, here_, "%s", "(c->kind == CK_PERMANENT) || (c->kind == CK_TEMPLATE && c->config->sec_label.len > 0)" ); } assertion__; }); | |||
1346 | dbg("no best spd route; but the current %s connection \"%s\" is not a CK_INSTANCE; giving up",{ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("no best spd route; but the current %s connection \"%s\" is not a CK_INSTANCE; giving up" , enum_name(&connection_kind_names, c->kind), c->name ); } } | |||
1347 | enum_name(&connection_kind_names, c->kind), c->name){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("no best spd route; but the current %s connection \"%s\" is not a CK_INSTANCE; giving up" , enum_name(&connection_kind_names, c->kind), c->name ); } }; | |||
1348 | llog_sa(RC_LOG_SERIOUS, child, "No IKEv2 connection found with compatible Traffic Selectors")llog(RC_LOG_SERIOUS, (child)->sa.st_logger, "No IKEv2 connection found with compatible Traffic Selectors" ); | |||
1349 | return false0; | |||
1350 | } | |||
1351 | ||||
1352 | if (best_spd_route
| |||
1353 | (c->policy & POLICY_IKEV2_ALLOW_NARROWING((lset_t)1 << (POLICY_IKEV2_ALLOW_NARROWING_IX))))) { | |||
1354 | /* | |||
1355 | * Is there something better than the current | |||
1356 | * connection? | |||
1357 | * | |||
1358 | * Rather than overwrite the current INSTANCE; would | |||
1359 | * it be better to instantiate a new instance, and | |||
1360 | * then replace it? | |||
1361 | * | |||
1362 | * Would also address the above. | |||
1363 | * | |||
1364 | * If the connection seems to be shared, this happens. | |||
1365 | */ | |||
1366 | pexpect(c->kind == CK_INSTANCE)({ _Bool assertion__ = c->kind == CK_INSTANCE; if (!assertion__ ) { where_t here_ = ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c", .line = 1366 , }; &here; }); const struct logger *logger_ = &failsafe_logger ; llog_pexpect(logger_, here_, "%s", "c->kind == CK_INSTANCE" ); } assertion__; }); | |||
1367 | /* since an SPD_ROUTE wasn't found */ | |||
1368 | passert(best_connection == c)({ _Bool assertion__ = best_connection == c; if (!assertion__ ) { where_t here = ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c", .line = 1368 , }; &here; }); const struct logger *logger_ = &failsafe_logger ; llog_passert(logger_, here, "%s", "best_connection == c"); } (void) 1; }); | |||
1369 | dbg("no best spd route; looking for a better template connection to instantiate"){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("no best spd route; looking for a better template connection to instantiate" ); } }; | |||
1370 | ||||
1371 | struct connection_query cq = { .where = HERE({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c", .line = 1371, }; &here; } ), .c = NULL((void*)0), }; | |||
1372 | while (new2old_connection(&cq)) { | |||
1373 | struct connection *t = cq.c; | |||
1374 | /* require a template */ | |||
1375 | if (t->kind != CK_TEMPLATE) { | |||
1376 | continue; | |||
1377 | } | |||
1378 | LSWDBGP(DBG_BASE, buf)for (_Bool lswlog_p = (cur_debugging & (((lset_t)1 << (DBG_BASE_IX)))); lswlog_p; lswlog_p = 0) for (char lswbuf[( (size_t)1024)], *lswbuf_ = lswbuf; lswbuf_ != ((void*)0); lswbuf_ = ((void*)0)) for (struct jambuf jambuf = array_as_jambuf((lswbuf ), sizeof(lswbuf)), *buf = &jambuf; buf != ((void*)0); buf = ((void*)0)) for (; buf != ((void*)0); jambuf_to_logger(buf , &failsafe_logger, DEBUG_STREAM), buf = ((void*)0)) { | |||
1379 | jam(buf, " investigating template \"%s\";", | |||
1380 | t->name); | |||
1381 | if (t->foodgroup != NULL((void*)0)) { | |||
1382 | jam(buf, " food-group=\"%s\"", t->foodgroup); | |||
1383 | } | |||
1384 | jam(buf, " policy="); | |||
1385 | jam_policy(buf, t->policy & CONNECTION_POLICIES(((lset_t)1 << (POLICY_NEGO_PASS_IX)) | ((lset_t)1 << (POLICY_DONT_REKEY_IX)) | ((lset_t)1 << (POLICY_REAUTH_IX )) | ((lset_t)1 << (POLICY_OPPORTUNISTIC_IX)) | ((lset_t )1 << (POLICY_GROUP_IX)) | ((lset_t)1 << (POLICY_GROUTED_IX )) | ((lset_t)1 << (POLICY_GROUPINSTANCE_IX)) | ((lset_t )1 << (POLICY_UP_IX)) | ((lset_t)1 << (POLICY_XAUTH_IX )) | ((lset_t)1 << (POLICY_MODECFG_PULL_IX)) | ((lset_t )1 << (POLICY_AGGRESSIVE_IX)) | ((lset_t)1 << (POLICY_OVERLAPIP_IX )) | ((lset_t)1 << (POLICY_IKEV2_ALLOW_NARROWING_IX)))); | |||
1386 | } | |||
1387 | ||||
1388 | /* | |||
1389 | * Is it worth looking at the template. | |||
1390 | * | |||
1391 | * XXX: treat the combination the same as | |||
1392 | * group instance, like the old code did; is | |||
1393 | * this valid? | |||
1394 | */ | |||
1395 | switch (c->policy & (POLICY_GROUPINSTANCE((lset_t)1 << (POLICY_GROUPINSTANCE_IX)) | | |||
1396 | POLICY_IKEV2_ALLOW_NARROWING((lset_t)1 << (POLICY_IKEV2_ALLOW_NARROWING_IX)))) { | |||
1397 | case POLICY_GROUPINSTANCE((lset_t)1 << (POLICY_GROUPINSTANCE_IX)): | |||
1398 | case POLICY_GROUPINSTANCE((lset_t)1 << (POLICY_GROUPINSTANCE_IX)) | POLICY_IKEV2_ALLOW_NARROWING((lset_t)1 << (POLICY_IKEV2_ALLOW_NARROWING_IX)): /* XXX: true */ | |||
1399 | /* XXX: why does this matter; does it imply t->foodgroup != NULL? */ | |||
1400 | if (!LIN(POLICY_GROUPINSTANCE, t->policy)(((((lset_t)1 << (POLICY_GROUPINSTANCE_IX))) & (t-> policy)) == (((lset_t)1 << (POLICY_GROUPINSTANCE_IX))))) { | |||
1401 | dbg(" skipping; not a group instance"){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" skipping; not a group instance"); } }; | |||
1402 | continue; | |||
1403 | } | |||
1404 | /* when OE, don't change food groups? */ | |||
1405 | if (!streq(c->foodgroup, t->foodgroup)(strcmp((c->foodgroup), (t->foodgroup)) == 0)) { | |||
| ||||
1406 | dbg(" skipping; wrong foodgroup name"){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" skipping; wrong foodgroup name"); } }; | |||
1407 | continue; | |||
1408 | } | |||
1409 | /* ??? why require current connection->name and t->name to be different */ | |||
1410 | /* XXX: don't re-instantiate the same connection template???? */ | |||
1411 | if (streq(c->name, t->name)(strcmp((c->name), (t->name)) == 0)) { | |||
1412 | dbg(" skipping; name same as current connection"){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" skipping; name same as current connection" ); } }; | |||
1413 | continue; | |||
1414 | } | |||
1415 | break; | |||
1416 | case POLICY_IKEV2_ALLOW_NARROWING((lset_t)1 << (POLICY_IKEV2_ALLOW_NARROWING_IX)): | |||
1417 | if (!LIN(POLICY_IKEV2_ALLOW_NARROWING, t->policy)(((((lset_t)1 << (POLICY_IKEV2_ALLOW_NARROWING_IX))) & (t->policy)) == (((lset_t)1 << (POLICY_IKEV2_ALLOW_NARROWING_IX ))))) { | |||
1418 | dbg(" skipping; cannot narrow"){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" skipping; cannot narrow"); } }; | |||
1419 | continue; | |||
1420 | } | |||
1421 | break; | |||
1422 | default: | |||
1423 | bad_case(c->policy)libreswan_bad_case("c->policy", (c->policy), ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c" , .line = 1423, }; &here; })); /* not quite true */ | |||
1424 | } | |||
1425 | ||||
1426 | /* require initiator's subnet <= T; why? */ | |||
1427 | if (!selector_in_selector(c->spd.that.client, t->spd.that.client)) { | |||
1428 | dbg(" skipping; current connection's initiator subnet is not <= template"){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" skipping; current connection's initiator subnet is not <= template" ); } }; | |||
1429 | continue; | |||
1430 | } | |||
1431 | /* require responder address match; why? */ | |||
1432 | ip_address c_this_client_address = selector_prefix(c->spd.this.client); | |||
1433 | ip_address t_this_client_address = selector_prefix(t->spd.this.client); | |||
1434 | if (!address_eq_address(c_this_client_address, t_this_client_address)) { | |||
1435 | dbg(" skipping; responder addresses don't match"){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" skipping; responder addresses don't match" ); } }; | |||
1436 | continue; | |||
1437 | } | |||
1438 | ||||
1439 | /* require a valid narrowed port? */ | |||
1440 | enum fit fit; | |||
1441 | switch (c->policy & (POLICY_GROUPINSTANCE((lset_t)1 << (POLICY_GROUPINSTANCE_IX)) | | |||
1442 | POLICY_IKEV2_ALLOW_NARROWING((lset_t)1 << (POLICY_IKEV2_ALLOW_NARROWING_IX)))) { | |||
1443 | case POLICY_GROUPINSTANCE((lset_t)1 << (POLICY_GROUPINSTANCE_IX)): | |||
1444 | case POLICY_GROUPINSTANCE((lset_t)1 << (POLICY_GROUPINSTANCE_IX)) | POLICY_IKEV2_ALLOW_NARROWING((lset_t)1 << (POLICY_IKEV2_ALLOW_NARROWING_IX)): /* XXX: true */ | |||
1445 | /* exact match; XXX: 'cos that is what old code did */ | |||
1446 | fit = END_EQUALS_TS; | |||
1447 | break; | |||
1448 | case POLICY_IKEV2_ALLOW_NARROWING((lset_t)1 << (POLICY_IKEV2_ALLOW_NARROWING_IX)): | |||
1449 | /* narrow END's port to TS port */ | |||
1450 | fit = END_WIDER_THAN_TS; | |||
1451 | break; | |||
1452 | default: | |||
1453 | bad_case(c->policy)libreswan_bad_case("c->policy", (c->policy), ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c" , .line = 1453, }; &here; })); | |||
1454 | } | |||
1455 | ||||
1456 | passert(best_connection == c)({ _Bool assertion__ = best_connection == c; if (!assertion__ ) { where_t here = ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c", .line = 1456 , }; &here; }); const struct logger *logger_ = &failsafe_logger ; llog_passert(logger_, here, "%s", "best_connection == c"); } (void) 1; }); /* aka st->st_connection, no leak */ | |||
1457 | pexpect(best_connection == child->sa.st_connection)({ _Bool assertion__ = best_connection == child->sa.st_connection ; if (!assertion__) { where_t here_ = ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c" , .line = 1457, }; &here; }); const struct logger *logger_ = &failsafe_logger; llog_pexpect(logger_, here_, "%s", "best_connection == child->sa.st_connection" ); } assertion__; }); | |||
1458 | struct narrowed_ts n = narrow_request_ts(t, &tsp, fit); | |||
1459 | if (!n.ok) { | |||
1460 | continue; | |||
1461 | } | |||
1462 | ||||
1463 | struct connection *s; | |||
1464 | if (v2_child_connection_probably_shared(child)) { | |||
1465 | /* instantiate it, filling in peer's ID */ | |||
1466 | /* XXX: is best_sec_label ever non-NULL? */ | |||
1467 | s = instantiate(t, &child->sa.st_connection->spd.that.host_addr, | |||
1468 | NULL((void*)0), best_sec_label); | |||
1469 | } else { | |||
1470 | s = child->sa.st_connection; | |||
1471 | } | |||
1472 | scribble_request_ts_on_connection(child, s, n); | |||
1473 | ||||
1474 | /* switch */ | |||
1475 | best_connection = s; | |||
1476 | best_spd_route = &best_connection->spd; | |||
1477 | break; | |||
1478 | } | |||
1479 | } else if (best_connection == c && | |||
1480 | c->kind == CK_TEMPLATE && | |||
1481 | c->config->sec_label.len > 0) { | |||
1482 | dbg(" instantiating template security label connection"){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log(" instantiating template security label connection" ); } }; | |||
1483 | /* sure looks like a sec-label template */ | |||
1484 | struct narrowed_ts n = narrow_request_ts(c, &tsp, END_WIDER_THAN_TS); | |||
1485 | if (!n.ok) { | |||
1486 | return false0; | |||
1487 | } | |||
1488 | ||||
1489 | struct connection *s = instantiate(c, &child->sa.st_connection->spd.that.host_addr, | |||
1490 | NULL((void*)0), best_sec_label); | |||
1491 | scribble_request_ts_on_connection(child, s, n); | |||
1492 | ||||
1493 | /* switch */ | |||
1494 | best_connection = s; | |||
1495 | best_spd_route = &best_connection->spd; | |||
1496 | } | |||
1497 | ||||
1498 | if (best_spd_route == NULL((void*)0)) { | |||
1499 | dbg("giving up"){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("giving up"); } }; | |||
1500 | return false0; | |||
1501 | } | |||
1502 | ||||
1503 | /* | |||
1504 | * this both replaces the child's connection, and flips any | |||
1505 | * underlying current-connection | |||
1506 | * | |||
1507 | * XXX: but this is responder code, there probably isn't a | |||
1508 | * current-connection - it would have gone straight to current | |||
1509 | * state. | |||
1510 | * | |||
1511 | * XXX: ah, but the state code does: set-state; set-connection | |||
1512 | * (yes order is wrong). Why does it bother? | |||
1513 | * | |||
1514 | * update_state_connection(), if the connection changes, | |||
1515 | * de-references the old connection; which is what really | |||
1516 | * matters | |||
1517 | */ | |||
1518 | update_state_connection(&child->sa, best_connection); | |||
1519 | ||||
1520 | return true1; | |||
1521 | } | |||
1522 | ||||
1523 | /* check TS payloads, response */ | |||
1524 | bool_Bool v2_process_ts_response(struct child_sa *child, | |||
1525 | struct msg_digest *md) | |||
1526 | { | |||
1527 | passert(child->sa.st_sa_role == SA_INITIATOR)({ _Bool assertion__ = child->sa.st_sa_role == SA_INITIATOR ; if (!assertion__) { where_t here = ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c" , .line = 1527, }; &here; }); const struct logger *logger_ = &failsafe_logger; llog_passert(logger_, here, "%s", "child->sa.st_sa_role == SA_INITIATOR" ); } (void) 1; }); | |||
1528 | passert(v2_msg_role(md) == MESSAGE_RESPONSE)({ _Bool assertion__ = v2_msg_role(md) == MESSAGE_RESPONSE; if (!assertion__) { where_t here = ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c" , .line = 1528, }; &here; }); const struct logger *logger_ = &failsafe_logger; llog_passert(logger_, here, "%s", "v2_msg_role(md) == MESSAGE_RESPONSE" ); } (void) 1; }); | |||
1529 | ||||
1530 | struct connection *c = child->sa.st_connection; | |||
1531 | ||||
1532 | struct traffic_selector_payloads tsp = empty_traffic_selectors; | |||
1533 | if (!v2_parse_tsp(md, &tsp, child->sa.st_logger)) { | |||
1534 | return false0; | |||
1535 | } | |||
1536 | ||||
1537 | /* initiator */ | |||
1538 | const struct spd_route *sra = &c->spd; | |||
1539 | const struct ends e = { | |||
1540 | .i = &sra->this, | |||
1541 | .r = &sra->that, | |||
1542 | }; | |||
1543 | enum fit initiator_widening = | |||
1544 | (c->policy & POLICY_IKEV2_ALLOW_NARROWING((lset_t)1 << (POLICY_IKEV2_ALLOW_NARROWING_IX))) | |||
1545 | ? END_WIDER_THAN_TS | |||
1546 | : END_EQUALS_TS; | |||
1547 | ||||
1548 | /* Returns NULL(ok), &null_shunk(skip), memory(ok). */ | |||
1549 | shunk_t selected_sec_label = null_shunk; | |||
1550 | if (!score_tsp_sec_label(&tsp, c->config->sec_label, | |||
1551 | &selected_sec_label, child->sa.st_logger)) { | |||
1552 | /* | |||
1553 | * Either: | |||
1554 | * - Security label required, but not found. | |||
1555 | * OR | |||
1556 | * - Security label *not* required, but found. | |||
1557 | */ | |||
1558 | return false0; | |||
1559 | } | |||
1560 | ||||
1561 | struct best_score best = score_ends_iprange(initiator_widening, c, &e, &tsp); | |||
1562 | ||||
1563 | if (!best.ok) { | |||
1564 | dbg("reject responder TSi/TSr Traffic Selector"){ if ((cur_debugging & (((lset_t)1 << (DBG_BASE_IX) )))) { DBG_log("reject responder TSi/TSr Traffic Selector"); } }; | |||
1565 | /* prevents parent from going to I3 */ | |||
1566 | return false0; | |||
1567 | } | |||
1568 | ||||
1569 | traffic_selector_to_end(best.tsi, &c->spd.this, | |||
1570 | "scribble accepted TSi response on initiator's this"); | |||
1571 | traffic_selector_to_end(best.tsr, &c->spd.that, | |||
1572 | "scribble accepted TSr response on initiator's that"); | |||
1573 | ||||
1574 | return true1; | |||
1575 | } | |||
1576 | ||||
1577 | /* | |||
1578 | * RFC 7296 https://tools.ietf.org/html/rfc7296#section-2.8 | |||
1579 | * "when rekeying, the new Child SA SHOULD NOT have different Traffic | |||
1580 | * Selectors and algorithms than the old one." | |||
1581 | * | |||
1582 | * However, when narrowed down, the original TSi/TSr is wider than the | |||
1583 | * returned narrowed TSi/TSr. Windows 10 is known to use the original | |||
1584 | * and not the narrowed TSi/TSr. | |||
1585 | * | |||
1586 | * RFC 7296 #1.3.3 "The Traffic Selectors for traffic to be sent | |||
1587 | * on that SA are specified in the TS payloads in the response, | |||
1588 | * which may be a subset of what the initiator of the Child SA proposed." | |||
1589 | * | |||
1590 | * However, the rekey initiator, when it is the original initiator of | |||
1591 | * the Child SA, may request a super set. And responder should | |||
1592 | * respond with same set as initially negotiated, ie RFC 7296 #2.8 | |||
1593 | * | |||
1594 | * See RFC 7296 Section 1.7. for the above change. | |||
1595 | * Significant Differences between RFC 4306 and RFC 5996 | |||
1596 | * | |||
1597 | * We already matched the right connection by the SPI of v2N_REKEY_SA | |||
1598 | */ | |||
1599 | bool_Bool child_rekey_responder_ts_verify(struct child_sa *child, struct msg_digest *md) | |||
1600 | { | |||
1601 | if (!pexpect(child->sa.st_state->kind == STATE_V2_REKEY_CHILD_R0)({ _Bool assertion__ = child->sa.st_state->kind == STATE_V2_REKEY_CHILD_R0 ; if (!assertion__) { where_t here_ = ({ static const struct where here = { .func = __func__, .file = "programs/pluto/ikev2_ts.c" , .line = 1601, }; &here; }); const struct logger *logger_ = &failsafe_logger; llog_pexpect(logger_, here_, "%s", "child->sa.st_state->kind == STATE_V2_REKEY_CHILD_R0" ); } assertion__; })) | |||
1602 | return false0; | |||
1603 | ||||
1604 | const struct connection *c = child->sa.st_connection; | |||
1605 | struct traffic_selector_payloads their_tsp = empty_traffic_selectors; | |||
1606 | ||||
1607 | if (!v2_parse_tsp(md, &their_tsp, child->sa.st_logger)) { | |||
1608 | log_state(RC_LOG_SERIOUS, &child->sa, | |||
1609 | "received malformed TSi/TSr payload(s)"); | |||
1610 | return false0; | |||
1611 | } | |||
1612 | ||||
1613 | const struct ends ends = { | |||
1614 | .i = &c->spd.that, | |||
1615 | .r = &c->spd.this, | |||
1616 | }; | |||
1617 | ||||
1618 | enum fit fitness = END_NARROWER_THAN_TS; | |||
1619 | ||||
1620 | /* Returns NULL(ok), &null_shunk(skip), memory(ok). */ | |||
1621 | shunk_t selected_sec_label = null_shunk; | |||
1622 | if (!score_tsp_sec_label(&their_tsp, c->config->sec_label, | |||
1623 | &selected_sec_label, | |||
1624 | child->sa.st_logger)) { | |||
1625 | /* | |||
1626 | * Either: | |||
1627 | * - Security label required, but not found. | |||
1628 | * OR | |||
1629 | * - Security label *not* required, but found. | |||
1630 | */ | |||
1631 | log_state(RC_LOG_SERIOUS, &child->sa, | |||
1632 | "rekey: received Traffic Selectors mismatch configured selectors for Security Label"); | |||
1633 | return false0; | |||
1634 | } | |||
1635 | ||||
1636 | struct best_score score = score_ends_iprange(fitness, c, &ends, &their_tsp); | |||
1637 | ||||
1638 | if (!score.ok) { | |||
1639 | log_state(RC_LOG_SERIOUS, &child->sa, | |||
1640 | "rekey: received Traffic Selectors does not contain existing IPsec SA Traffic Selectors"); | |||
1641 | return false0; | |||
1642 | } | |||
1643 | ||||
1644 | return true1; | |||
1645 | } |