1 |
|
|
/* $OpenBSD: ike_phase_1.c,v 1.77 2017/11/08 13:33:49 patrick Exp $ */ |
2 |
|
|
/* $EOM: ike_phase_1.c,v 1.31 2000/12/11 23:47:56 niklas Exp $ */ |
3 |
|
|
|
4 |
|
|
/* |
5 |
|
|
* Copyright (c) 1999, 2000 Niklas Hallqvist. All rights reserved. |
6 |
|
|
* Copyright (c) 1999, 2000 Angelos D. Keromytis. All rights reserved. |
7 |
|
|
* Copyright (c) 2001, 2004 Håkan Olsson. All rights reserved. |
8 |
|
|
* |
9 |
|
|
* Redistribution and use in source and binary forms, with or without |
10 |
|
|
* modification, are permitted provided that the following conditions |
11 |
|
|
* are met: |
12 |
|
|
* 1. Redistributions of source code must retain the above copyright |
13 |
|
|
* notice, this list of conditions and the following disclaimer. |
14 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
15 |
|
|
* notice, this list of conditions and the following disclaimer in the |
16 |
|
|
* documentation and/or other materials provided with the distribution. |
17 |
|
|
* |
18 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
19 |
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
20 |
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
21 |
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
22 |
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
23 |
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
24 |
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
25 |
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
26 |
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
27 |
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 |
|
|
*/ |
29 |
|
|
|
30 |
|
|
/* |
31 |
|
|
* This code was written under funding by Ericsson Radio Systems. |
32 |
|
|
*/ |
33 |
|
|
|
34 |
|
|
#include <sys/types.h> |
35 |
|
|
#include <netinet/in.h> |
36 |
|
|
#include <arpa/inet.h> |
37 |
|
|
#include <stdlib.h> |
38 |
|
|
#include <string.h> |
39 |
|
|
|
40 |
|
|
#include "attribute.h" |
41 |
|
|
#include "conf.h" |
42 |
|
|
#include "constants.h" |
43 |
|
|
#include "crypto.h" |
44 |
|
|
#include "dh.h" |
45 |
|
|
#include "doi.h" |
46 |
|
|
#include "dpd.h" |
47 |
|
|
#include "exchange.h" |
48 |
|
|
#include "hash.h" |
49 |
|
|
#include "ike_auth.h" |
50 |
|
|
#include "ike_phase_1.h" |
51 |
|
|
#include "ipsec.h" |
52 |
|
|
#include "ipsec_doi.h" |
53 |
|
|
#include "isakmp.h" |
54 |
|
|
#include "log.h" |
55 |
|
|
#include "message.h" |
56 |
|
|
#include "nat_traversal.h" |
57 |
|
|
#include "prf.h" |
58 |
|
|
#include "sa.h" |
59 |
|
|
#include "transport.h" |
60 |
|
|
#include "util.h" |
61 |
|
|
#include "vendor.h" |
62 |
|
|
|
63 |
|
|
static int attribute_unacceptable(u_int16_t, u_int8_t *, u_int16_t, |
64 |
|
|
void *); |
65 |
|
|
static int ike_phase_1_validate_prop(struct exchange *, struct sa *, |
66 |
|
|
struct sa *); |
67 |
|
|
|
68 |
|
|
/* Offer a set of transforms to the responder in the MSG message. */ |
69 |
|
|
int |
70 |
|
|
ike_phase_1_initiator_send_SA(struct message *msg) |
71 |
|
|
{ |
72 |
|
|
struct exchange *exchange = msg->exchange; |
73 |
|
|
struct ipsec_exch *ie = exchange->data; |
74 |
|
|
u_int8_t *proposal = 0, *sa_buf = 0, *saved_nextp, *attr; |
75 |
|
|
u_int8_t **transform = 0; |
76 |
|
|
size_t transforms_len = 0, proposal_len, sa_len; |
77 |
|
|
size_t *transform_len = 0; |
78 |
|
|
struct conf_list *conf, *life_conf; |
79 |
|
|
struct conf_list_node *xf, *life; |
80 |
|
|
int value, update_nextp; |
81 |
|
|
size_t i; |
82 |
|
|
struct payload *p; |
83 |
|
|
struct proto *proto; |
84 |
|
|
struct proto_attr *pa; |
85 |
|
|
int group_desc = -1, new_group_desc; |
86 |
|
|
|
87 |
|
|
/* Get the list of transforms. */ |
88 |
|
|
conf = conf_get_list(exchange->policy, "Transforms"); |
89 |
|
|
if (!conf) |
90 |
|
|
return -1; |
91 |
|
|
|
92 |
|
|
transform = calloc(conf->cnt, sizeof *transform); |
93 |
|
|
if (!transform) { |
94 |
|
|
log_error("ike_phase_1_initiator_send_SA: calloc (%lu, %lu) " |
95 |
|
|
"failed", (u_long)conf->cnt, (u_long)sizeof *transform); |
96 |
|
|
goto bail_out; |
97 |
|
|
} |
98 |
|
|
transform_len = calloc(conf->cnt, sizeof *transform_len); |
99 |
|
|
if (!transform_len) { |
100 |
|
|
log_error("ike_phase_1_initiator_send_SA: calloc (%lu, %lu) " |
101 |
|
|
"failed", (u_long)conf->cnt, |
102 |
|
|
(u_long)sizeof *transform_len); |
103 |
|
|
goto bail_out; |
104 |
|
|
} |
105 |
|
|
for (xf = TAILQ_FIRST(&conf->fields), i = 0; i < conf->cnt; |
106 |
|
|
i++, xf = TAILQ_NEXT(xf, link)) { |
107 |
|
|
/* XXX The sizing needs to be dynamic. */ |
108 |
|
|
transform[i] = malloc(ISAKMP_TRANSFORM_SA_ATTRS_OFF + |
109 |
|
|
16 * ISAKMP_ATTR_VALUE_OFF); |
110 |
|
|
if (!transform[i]) { |
111 |
|
|
log_error("ike_phase_1_initiator_send_SA: malloc (%d) " |
112 |
|
|
"failed", ISAKMP_TRANSFORM_SA_ATTRS_OFF + |
113 |
|
|
16 * ISAKMP_ATTR_VALUE_OFF); |
114 |
|
|
goto bail_out; |
115 |
|
|
} |
116 |
|
|
SET_ISAKMP_TRANSFORM_NO(transform[i], i); |
117 |
|
|
SET_ISAKMP_TRANSFORM_ID(transform[i], IPSEC_TRANSFORM_KEY_IKE); |
118 |
|
|
SET_ISAKMP_TRANSFORM_RESERVED(transform[i], 0); |
119 |
|
|
|
120 |
|
|
attr = transform[i] + ISAKMP_TRANSFORM_SA_ATTRS_OFF; |
121 |
|
|
|
122 |
|
|
if (attribute_set_constant(xf->field, "ENCRYPTION_ALGORITHM", |
123 |
|
|
ike_encrypt_cst, IKE_ATTR_ENCRYPTION_ALGORITHM, &attr)) |
124 |
|
|
goto bail_out; |
125 |
|
|
|
126 |
|
|
if (attribute_set_constant(xf->field, "HASH_ALGORITHM", |
127 |
|
|
ike_hash_cst, IKE_ATTR_HASH_ALGORITHM, &attr)) |
128 |
|
|
goto bail_out; |
129 |
|
|
|
130 |
|
|
if (attribute_set_constant(xf->field, "AUTHENTICATION_METHOD", |
131 |
|
|
ike_auth_cst, IKE_ATTR_AUTHENTICATION_METHOD, &attr)) |
132 |
|
|
goto bail_out; |
133 |
|
|
|
134 |
|
|
if (attribute_set_constant(xf->field, "GROUP_DESCRIPTION", |
135 |
|
|
ike_group_desc_cst, IKE_ATTR_GROUP_DESCRIPTION, &attr)) { |
136 |
|
|
/* |
137 |
|
|
* If no group description exists, try looking for |
138 |
|
|
* a user-defined one. |
139 |
|
|
*/ |
140 |
|
|
if (attribute_set_constant(xf->field, "GROUP_TYPE", |
141 |
|
|
ike_group_cst, IKE_ATTR_GROUP_TYPE, &attr)) |
142 |
|
|
goto bail_out; |
143 |
|
|
|
144 |
|
|
#if 0 |
145 |
|
|
if (attribute_set_bignum(xf->field, "GROUP_PRIME", |
146 |
|
|
IKE_ATTR_GROUP_PRIME, &attr)) |
147 |
|
|
goto bail_out; |
148 |
|
|
|
149 |
|
|
if (attribute_set_bignum(xf->field, |
150 |
|
|
"GROUP_GENERATOR_2", IKE_ATTR_GROUP_GENERATOR_2, |
151 |
|
|
&attr)) |
152 |
|
|
goto bail_out; |
153 |
|
|
|
154 |
|
|
if (attribute_set_bignum(xf->field, |
155 |
|
|
"GROUP_GENERATOR_2", IKE_ATTR_GROUP_GENERATOR_2, |
156 |
|
|
&attr)) |
157 |
|
|
goto bail_out; |
158 |
|
|
|
159 |
|
|
if (attribute_set_bignum(xf->field, "GROUP_CURVE_A", |
160 |
|
|
IKE_ATTR_GROUP_CURVE_A, &attr)) |
161 |
|
|
goto bail_out; |
162 |
|
|
|
163 |
|
|
if (attribute_set_bignum(xf->field, "GROUP_CURVE_B", |
164 |
|
|
IKE_ATTR_GROUP_CURVE_B, &attr)) |
165 |
|
|
goto bail_out; |
166 |
|
|
#endif |
167 |
|
|
} |
168 |
|
|
/* |
169 |
|
|
* Life durations are special, we should be able to specify |
170 |
|
|
* several, one per type. |
171 |
|
|
*/ |
172 |
|
|
life_conf = conf_get_list(xf->field, "Life"); |
173 |
|
|
if (life_conf) { |
174 |
|
|
for (life = TAILQ_FIRST(&life_conf->fields); life; |
175 |
|
|
life = TAILQ_NEXT(life, link)) { |
176 |
|
|
attribute_set_constant(life->field, |
177 |
|
|
"LIFE_TYPE", ike_duration_cst, |
178 |
|
|
IKE_ATTR_LIFE_TYPE, &attr); |
179 |
|
|
|
180 |
|
|
/* |
181 |
|
|
* XXX Deals with 16 and 32 bit lifetimes |
182 |
|
|
* only |
183 |
|
|
*/ |
184 |
|
|
value = conf_get_num(life->field, |
185 |
|
|
"LIFE_DURATION", 0); |
186 |
|
|
if (value) { |
187 |
|
|
if (value <= 0xffff) |
188 |
|
|
attr = attribute_set_basic( |
189 |
|
|
attr, |
190 |
|
|
IKE_ATTR_LIFE_DURATION, |
191 |
|
|
value); |
192 |
|
|
else { |
193 |
|
|
value = htonl(value); |
194 |
|
|
attr = attribute_set_var(attr, |
195 |
|
|
IKE_ATTR_LIFE_DURATION, |
196 |
|
|
(u_int8_t *)&value, |
197 |
|
|
sizeof value); |
198 |
|
|
} |
199 |
|
|
} |
200 |
|
|
} |
201 |
|
|
conf_free_list(life_conf); |
202 |
|
|
} |
203 |
|
|
attribute_set_constant(xf->field, "PRF", ike_prf_cst, |
204 |
|
|
IKE_ATTR_PRF, &attr); |
205 |
|
|
|
206 |
|
|
value = conf_get_num(xf->field, "KEY_LENGTH", 0); |
207 |
|
|
if (value) |
208 |
|
|
attr = attribute_set_basic(attr, IKE_ATTR_KEY_LENGTH, |
209 |
|
|
value); |
210 |
|
|
|
211 |
|
|
value = conf_get_num(xf->field, "FIELD_SIZE", 0); |
212 |
|
|
if (value) |
213 |
|
|
attr = attribute_set_basic(attr, IKE_ATTR_FIELD_SIZE, |
214 |
|
|
value); |
215 |
|
|
|
216 |
|
|
value = conf_get_num(xf->field, "GROUP_ORDER", 0); |
217 |
|
|
if (value) |
218 |
|
|
attr = attribute_set_basic(attr, IKE_ATTR_GROUP_ORDER, |
219 |
|
|
value); |
220 |
|
|
|
221 |
|
|
/* Record the real transform size. */ |
222 |
|
|
transforms_len += transform_len[i] = attr - transform[i]; |
223 |
|
|
|
224 |
|
|
/* XXX I don't like exchange-specific stuff in here. */ |
225 |
|
|
if (exchange->type == ISAKMP_EXCH_AGGRESSIVE) { |
226 |
|
|
/* |
227 |
|
|
* Make sure that if a group description is specified, |
228 |
|
|
* it is specified for all transforms equally. |
229 |
|
|
*/ |
230 |
|
|
attr = (u_int8_t *)conf_get_str(xf->field, |
231 |
|
|
"GROUP_DESCRIPTION"); |
232 |
|
|
new_group_desc = |
233 |
|
|
attr ? constant_value(ike_group_desc_cst, |
234 |
|
|
(char *)attr) : 0; |
235 |
|
|
if (group_desc == -1) |
236 |
|
|
group_desc = new_group_desc; |
237 |
|
|
else if (group_desc != new_group_desc) { |
238 |
|
|
log_print("ike_phase_1_initiator_send_SA: " |
239 |
|
|
"differing group descriptions in a " |
240 |
|
|
"proposal"); |
241 |
|
|
goto bail_out; |
242 |
|
|
} |
243 |
|
|
} |
244 |
|
|
/* |
245 |
|
|
* We need to check that we actually support our |
246 |
|
|
* configuration. |
247 |
|
|
*/ |
248 |
|
|
if (attribute_map(transform[i] + ISAKMP_TRANSFORM_SA_ATTRS_OFF, |
249 |
|
|
transform_len[i] - ISAKMP_TRANSFORM_SA_ATTRS_OFF, |
250 |
|
|
exchange->doi->is_attribute_incompatible, msg)) { |
251 |
|
|
log_print("ike_phase_1_initiator_send_SA: " |
252 |
|
|
"section [%s] has unsupported attribute(s)", |
253 |
|
|
xf->field); |
254 |
|
|
goto bail_out; |
255 |
|
|
} |
256 |
|
|
} |
257 |
|
|
|
258 |
|
|
/* XXX I don't like exchange-specific stuff in here. */ |
259 |
|
|
if (exchange->type == ISAKMP_EXCH_AGGRESSIVE) |
260 |
|
|
ie->group = group_get(group_desc); |
261 |
|
|
|
262 |
|
|
proposal_len = ISAKMP_PROP_SPI_OFF; |
263 |
|
|
proposal = malloc(proposal_len); |
264 |
|
|
if (!proposal) { |
265 |
|
|
log_error("ike_phase_1_initiator_send_SA: malloc (%lu) failed", |
266 |
|
|
(unsigned long)proposal_len); |
267 |
|
|
goto bail_out; |
268 |
|
|
} |
269 |
|
|
SET_ISAKMP_PROP_NO(proposal, 1); |
270 |
|
|
SET_ISAKMP_PROP_PROTO(proposal, ISAKMP_PROTO_ISAKMP); |
271 |
|
|
SET_ISAKMP_PROP_SPI_SZ(proposal, 0); |
272 |
|
|
SET_ISAKMP_PROP_NTRANSFORMS(proposal, conf->cnt); |
273 |
|
|
|
274 |
|
|
/* XXX I would like to see this factored out. */ |
275 |
|
|
proto = calloc(1, sizeof *proto); |
276 |
|
|
if (!proto) { |
277 |
|
|
log_error("ike_phase_1_initiator_send_SA: " |
278 |
|
|
"calloc (1, %lu) failed", (unsigned long)sizeof *proto); |
279 |
|
|
goto bail_out; |
280 |
|
|
} |
281 |
|
|
proto->no = 1; |
282 |
|
|
proto->proto = ISAKMP_PROTO_ISAKMP; |
283 |
|
|
proto->sa = TAILQ_FIRST(&exchange->sa_list); |
284 |
|
|
proto->xf_cnt = conf->cnt; |
285 |
|
|
TAILQ_INIT(&proto->xfs); |
286 |
|
|
for (i = 0; i < proto->xf_cnt; i++) { |
287 |
|
|
pa = calloc(1, sizeof *pa); |
288 |
|
|
if (!pa) |
289 |
|
|
goto bail_out; |
290 |
|
|
pa->len = transform_len[i]; |
291 |
|
|
pa->attrs = malloc(pa->len); |
292 |
|
|
if (!pa->attrs) { |
293 |
|
|
free(pa); |
294 |
|
|
goto bail_out; |
295 |
|
|
} |
296 |
|
|
memcpy(pa->attrs, transform[i], pa->len); |
297 |
|
|
TAILQ_INSERT_TAIL(&proto->xfs, pa, next); |
298 |
|
|
} |
299 |
|
|
TAILQ_INSERT_TAIL(&TAILQ_FIRST(&exchange->sa_list)->protos, proto, |
300 |
|
|
link); |
301 |
|
|
|
302 |
|
|
sa_len = ISAKMP_SA_SIT_OFF + IPSEC_SIT_SIT_LEN; |
303 |
|
|
sa_buf = malloc(sa_len); |
304 |
|
|
if (!sa_buf) { |
305 |
|
|
log_error("ike_phase_1_initiator_send_SA: malloc (%lu) failed", |
306 |
|
|
(unsigned long)sa_len); |
307 |
|
|
goto bail_out; |
308 |
|
|
} |
309 |
|
|
SET_ISAKMP_SA_DOI(sa_buf, IPSEC_DOI_IPSEC); |
310 |
|
|
SET_IPSEC_SIT_SIT(sa_buf + ISAKMP_SA_SIT_OFF, IPSEC_SIT_IDENTITY_ONLY); |
311 |
|
|
|
312 |
|
|
/* |
313 |
|
|
* Add the payloads. As this is a SA, we need to recompute the |
314 |
|
|
* lengths of the payloads containing others. |
315 |
|
|
*/ |
316 |
|
|
if (message_add_payload(msg, ISAKMP_PAYLOAD_SA, sa_buf, sa_len, 1)) |
317 |
|
|
goto bail_out; |
318 |
|
|
SET_ISAKMP_GEN_LENGTH(sa_buf, |
319 |
|
|
sa_len + proposal_len + transforms_len); |
320 |
|
|
sa_buf = 0; |
321 |
|
|
|
322 |
|
|
saved_nextp = msg->nextp; |
323 |
|
|
if (message_add_payload(msg, ISAKMP_PAYLOAD_PROPOSAL, proposal, |
324 |
|
|
proposal_len, 0)) |
325 |
|
|
goto bail_out; |
326 |
|
|
SET_ISAKMP_GEN_LENGTH(proposal, proposal_len + transforms_len); |
327 |
|
|
proposal = 0; |
328 |
|
|
|
329 |
|
|
update_nextp = 0; |
330 |
|
|
for (i = 0; i < conf->cnt; i++) { |
331 |
|
|
if (message_add_payload(msg, ISAKMP_PAYLOAD_TRANSFORM, |
332 |
|
|
transform[i], transform_len[i], update_nextp)) |
333 |
|
|
goto bail_out; |
334 |
|
|
update_nextp = 1; |
335 |
|
|
transform[i] = 0; |
336 |
|
|
} |
337 |
|
|
msg->nextp = saved_nextp; |
338 |
|
|
|
339 |
|
|
/* Save SA payload body in ie->sa_i_b, length ie->sa_i_b_len. */ |
340 |
|
|
ie->sa_i_b_len = sa_len + proposal_len + transforms_len - |
341 |
|
|
ISAKMP_GEN_SZ; |
342 |
|
|
ie->sa_i_b = malloc(ie->sa_i_b_len); |
343 |
|
|
if (!ie->sa_i_b) { |
344 |
|
|
log_error("ike_phase_1_initiator_send_SA: malloc (%lu) failed", |
345 |
|
|
(unsigned long)ie->sa_i_b_len); |
346 |
|
|
goto bail_out; |
347 |
|
|
} |
348 |
|
|
memcpy(ie->sa_i_b, |
349 |
|
|
payload_first(msg, ISAKMP_PAYLOAD_SA)->p + ISAKMP_GEN_SZ, |
350 |
|
|
sa_len - ISAKMP_GEN_SZ); |
351 |
|
|
memcpy(ie->sa_i_b + sa_len - ISAKMP_GEN_SZ, |
352 |
|
|
payload_first(msg, ISAKMP_PAYLOAD_PROPOSAL)->p, proposal_len); |
353 |
|
|
transforms_len = 0; |
354 |
|
|
for (i = 0, p = TAILQ_FIRST(&msg->payload[ISAKMP_PAYLOAD_TRANSFORM]); |
355 |
|
|
i < conf->cnt; i++, p = TAILQ_NEXT(p, link)) { |
356 |
|
|
memcpy(ie->sa_i_b + sa_len + proposal_len + transforms_len - |
357 |
|
|
ISAKMP_GEN_SZ, p->p, transform_len[i]); |
358 |
|
|
transforms_len += transform_len[i]; |
359 |
|
|
} |
360 |
|
|
|
361 |
|
|
/* Advertise OpenBSD isakmpd. */ |
362 |
|
|
if (add_vendor_openbsd(msg)) |
363 |
|
|
goto bail_out; |
364 |
|
|
|
365 |
|
|
/* Advertise NAT-T capability. */ |
366 |
|
|
if (nat_t_add_vendor_payloads(msg)) |
367 |
|
|
goto bail_out; |
368 |
|
|
|
369 |
|
|
/* Advertise DPD capability. */ |
370 |
|
|
if (dpd_add_vendor_payload(msg)) |
371 |
|
|
goto bail_out; |
372 |
|
|
|
373 |
|
|
conf_free_list(conf); |
374 |
|
|
free(transform); |
375 |
|
|
free(transform_len); |
376 |
|
|
return 0; |
377 |
|
|
|
378 |
|
|
bail_out: |
379 |
|
|
free(sa_buf); |
380 |
|
|
free(proposal); |
381 |
|
|
if (transform) { |
382 |
|
|
for (i = 0; i < conf->cnt; i++) |
383 |
|
|
free(transform[i]); |
384 |
|
|
free(transform); |
385 |
|
|
} |
386 |
|
|
free(transform_len); |
387 |
|
|
conf_free_list(conf); |
388 |
|
|
return -1; |
389 |
|
|
} |
390 |
|
|
|
391 |
|
|
/* Figure out what transform the responder chose. */ |
392 |
|
|
int |
393 |
|
|
ike_phase_1_initiator_recv_SA(struct message *msg) |
394 |
|
|
{ |
395 |
|
|
struct exchange *exchange = msg->exchange; |
396 |
|
|
struct sa *sa = TAILQ_FIRST(&exchange->sa_list); |
397 |
|
|
struct ipsec_exch *ie = exchange->data; |
398 |
|
|
struct ipsec_sa *isa = sa->data; |
399 |
|
|
struct payload *sa_p = payload_first(msg, ISAKMP_PAYLOAD_SA); |
400 |
|
|
struct payload *prop = payload_first(msg, ISAKMP_PAYLOAD_PROPOSAL); |
401 |
|
|
struct payload *xf = payload_first(msg, ISAKMP_PAYLOAD_TRANSFORM); |
402 |
|
|
|
403 |
|
|
/* |
404 |
|
|
* IKE requires that only one SA with only one proposal exists and |
405 |
|
|
* since we are getting an answer on our transform offer, only one |
406 |
|
|
* transform. |
407 |
|
|
*/ |
408 |
|
|
if (TAILQ_NEXT(sa_p, link) || TAILQ_NEXT(prop, link) || |
409 |
|
|
TAILQ_NEXT(xf, link)) { |
410 |
|
|
log_print("ike_phase_1_initiator_recv_SA: " |
411 |
|
|
"multiple SA, proposal or transform payloads in phase 1"); |
412 |
|
|
/* XXX Is there a better notification type? */ |
413 |
|
|
message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); |
414 |
|
|
return -1; |
415 |
|
|
} |
416 |
|
|
/* Check that the chosen transform matches an offer. */ |
417 |
|
|
if (message_negotiate_sa(msg, ike_phase_1_validate_prop) || |
418 |
|
|
!TAILQ_FIRST(&sa->protos)) |
419 |
|
|
return -1; |
420 |
|
|
|
421 |
|
|
ipsec_decode_transform(msg, sa, TAILQ_FIRST(&sa->protos), xf->p); |
422 |
|
|
|
423 |
|
|
/* XXX I don't like exchange-specific stuff in here. */ |
424 |
|
|
if (exchange->type != ISAKMP_EXCH_AGGRESSIVE) |
425 |
|
|
ie->group = group_get(isa->group_desc); |
426 |
|
|
|
427 |
|
|
/* Mark the SA as handled. */ |
428 |
|
|
sa_p->flags |= PL_MARK; |
429 |
|
|
|
430 |
|
|
return 0; |
431 |
|
|
} |
432 |
|
|
|
433 |
|
|
/* Send our public DH value and a nonce to the responder. */ |
434 |
|
|
int |
435 |
|
|
ike_phase_1_initiator_send_KE_NONCE(struct message *msg) |
436 |
|
|
{ |
437 |
|
|
struct ipsec_exch *ie = msg->exchange->data; |
438 |
|
|
|
439 |
|
|
ie->g_x_len = dh_getlen(ie->group); |
440 |
|
|
|
441 |
|
|
/* XXX I want a better way to specify the nonce's size. */ |
442 |
|
|
return ike_phase_1_send_KE_NONCE(msg, 16); |
443 |
|
|
} |
444 |
|
|
|
445 |
|
|
/* Accept responder's public DH value and nonce. */ |
446 |
|
|
int |
447 |
|
|
ike_phase_1_initiator_recv_KE_NONCE(struct message *msg) |
448 |
|
|
{ |
449 |
|
|
if (ike_phase_1_recv_KE_NONCE(msg)) |
450 |
|
|
return -1; |
451 |
|
|
|
452 |
|
|
return ike_phase_1_post_exchange_KE_NONCE(msg); |
453 |
|
|
} |
454 |
|
|
|
455 |
|
|
/* |
456 |
|
|
* Accept a set of transforms offered by the initiator and chose one we can |
457 |
|
|
* handle. |
458 |
|
|
*/ |
459 |
|
|
int |
460 |
|
|
ike_phase_1_responder_recv_SA(struct message *msg) |
461 |
|
|
{ |
462 |
|
|
struct exchange *exchange = msg->exchange; |
463 |
|
|
struct sa *sa = TAILQ_FIRST(&exchange->sa_list); |
464 |
|
|
struct ipsec_sa *isa = sa->data; |
465 |
|
|
struct payload *sa_p = payload_first(msg, ISAKMP_PAYLOAD_SA); |
466 |
|
|
struct payload *prop = payload_first(msg, ISAKMP_PAYLOAD_PROPOSAL); |
467 |
|
|
struct ipsec_exch *ie = exchange->data; |
468 |
|
|
|
469 |
|
|
/* Mark the SA as handled. */ |
470 |
|
|
sa_p->flags |= PL_MARK; |
471 |
|
|
|
472 |
|
|
/* IKE requires that only one SA with only one proposal exists. */ |
473 |
|
|
if (TAILQ_NEXT(sa_p, link) || TAILQ_NEXT(prop, link)) { |
474 |
|
|
log_print("ike_phase_1_responder_recv_SA: " |
475 |
|
|
"multiple SA or proposal payloads in phase 1"); |
476 |
|
|
/* XXX Is there a better notification type? */ |
477 |
|
|
message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); |
478 |
|
|
return -1; |
479 |
|
|
} |
480 |
|
|
/* Chose a transform from the SA. */ |
481 |
|
|
if (message_negotiate_sa(msg, ike_phase_1_validate_prop) || |
482 |
|
|
!TAILQ_FIRST(&sa->protos)) |
483 |
|
|
return -1; |
484 |
|
|
|
485 |
|
|
/* XXX Move into message_negotiate_sa? */ |
486 |
|
|
ipsec_decode_transform(msg, sa, TAILQ_FIRST(&sa->protos), |
487 |
|
|
TAILQ_FIRST(&sa->protos)->chosen->p); |
488 |
|
|
|
489 |
|
|
ie->group = group_get(isa->group_desc); |
490 |
|
|
|
491 |
|
|
/* |
492 |
|
|
* Check that the mandatory attributes: encryption, hash, |
493 |
|
|
* authentication method and Diffie-Hellman group description, has |
494 |
|
|
* been supplied. |
495 |
|
|
*/ |
496 |
|
|
if (!exchange->crypto || !ie->hash || !ie->ike_auth || !ie->group) { |
497 |
|
|
message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); |
498 |
|
|
return -1; |
499 |
|
|
} |
500 |
|
|
/* Save the body for later hash computation. */ |
501 |
|
|
ie->sa_i_b_len = GET_ISAKMP_GEN_LENGTH(sa_p->p) - ISAKMP_GEN_SZ; |
502 |
|
|
ie->sa_i_b = malloc(ie->sa_i_b_len); |
503 |
|
|
if (!ie->sa_i_b) { |
504 |
|
|
/* XXX How to notify peer? */ |
505 |
|
|
log_error("ike_phase_1_responder_recv_SA: malloc (%lu) failed", |
506 |
|
|
(unsigned long)ie->sa_i_b_len); |
507 |
|
|
return -1; |
508 |
|
|
} |
509 |
|
|
memcpy(ie->sa_i_b, sa_p->p + ISAKMP_GEN_SZ, ie->sa_i_b_len); |
510 |
|
|
return 0; |
511 |
|
|
} |
512 |
|
|
|
513 |
|
|
/* Reply with the transform we chose. */ |
514 |
|
|
int |
515 |
|
|
ike_phase_1_responder_send_SA(struct message *msg) |
516 |
|
|
{ |
517 |
|
|
/* Add the SA payload with the transform that was chosen. */ |
518 |
|
|
if (message_add_sa_payload(msg)) |
519 |
|
|
return -1; |
520 |
|
|
|
521 |
|
|
/* Advertise OpenBSD isakmpd. */ |
522 |
|
|
if (add_vendor_openbsd(msg)) |
523 |
|
|
return -1; |
524 |
|
|
|
525 |
|
|
/* Advertise NAT-T capability. */ |
526 |
|
|
if (nat_t_add_vendor_payloads(msg)) |
527 |
|
|
return -1; |
528 |
|
|
|
529 |
|
|
/* Advertise DPD capability. */ |
530 |
|
|
if (dpd_add_vendor_payload(msg)) |
531 |
|
|
return -1; |
532 |
|
|
return 0; |
533 |
|
|
} |
534 |
|
|
|
535 |
|
|
/* Send our public DH value and a nonce to the peer. */ |
536 |
|
|
int |
537 |
|
|
ike_phase_1_send_KE_NONCE(struct message *msg, size_t nonce_sz) |
538 |
|
|
{ |
539 |
|
|
/* Public DH key. */ |
540 |
|
|
if (ipsec_gen_g_x(msg)) { |
541 |
|
|
/* XXX How to log and notify peer? */ |
542 |
|
|
return -1; |
543 |
|
|
} |
544 |
|
|
/* Generate a nonce, and add it to the message. */ |
545 |
|
|
if (exchange_gen_nonce(msg, nonce_sz)) { |
546 |
|
|
/* XXX Log? */ |
547 |
|
|
return -1; |
548 |
|
|
} |
549 |
|
|
/* Are there any CERTREQs to send? */ |
550 |
|
|
if (exchange_add_certreqs(msg)) { |
551 |
|
|
/* XXX Log? */ |
552 |
|
|
return -1; |
553 |
|
|
} |
554 |
|
|
/* Try to add certificates which are acceptable for the CERTREQs */ |
555 |
|
|
if (exchange_add_certs(msg)) { |
556 |
|
|
/* XXX Log? */ |
557 |
|
|
return -1; |
558 |
|
|
} |
559 |
|
|
/* If this exchange uses NAT-Traversal, add NAT-D payloads now. */ |
560 |
|
|
if (msg->exchange->flags & EXCHANGE_FLAG_NAT_T_CAP_PEER) |
561 |
|
|
if (nat_t_exchange_add_nat_d(msg)) { |
562 |
|
|
/* XXX Log? */ |
563 |
|
|
return -1; |
564 |
|
|
} |
565 |
|
|
return 0; |
566 |
|
|
} |
567 |
|
|
|
568 |
|
|
/* Receive our peer's public DH value and nonce. */ |
569 |
|
|
int |
570 |
|
|
ike_phase_1_recv_KE_NONCE(struct message *msg) |
571 |
|
|
{ |
572 |
|
|
/* Copy out the initiator's DH public value. */ |
573 |
|
|
if (ipsec_save_g_x(msg)) { |
574 |
|
|
/* XXX How to log and notify peer? */ |
575 |
|
|
return -1; |
576 |
|
|
} |
577 |
|
|
/* Copy out the initiator's nonce. */ |
578 |
|
|
if (exchange_save_nonce(msg)) { |
579 |
|
|
/* XXX How to log and notify peer? */ |
580 |
|
|
return -1; |
581 |
|
|
} |
582 |
|
|
/* Copy out the initiator's cert requests. */ |
583 |
|
|
if (exchange_save_certreq(msg)) { |
584 |
|
|
/* XXX How to log and notify peer? */ |
585 |
|
|
return -1; |
586 |
|
|
} |
587 |
|
|
/* MainMode: Check for NAT-D payloads and contents. */ |
588 |
|
|
if (msg->exchange->type == ISAKMP_EXCH_ID_PROT && |
589 |
|
|
msg->exchange->flags & EXCHANGE_FLAG_NAT_T_CAP_PEER) |
590 |
|
|
(void)nat_t_exchange_check_nat_d(msg); |
591 |
|
|
return 0; |
592 |
|
|
} |
593 |
|
|
|
594 |
|
|
/* |
595 |
|
|
* Compute DH values and key material. This is done in a post-send function |
596 |
|
|
* as that means we can do parallel work in both the initiator and responder |
597 |
|
|
* thus speeding up exchanges. |
598 |
|
|
*/ |
599 |
|
|
int |
600 |
|
|
ike_phase_1_post_exchange_KE_NONCE(struct message *msg) |
601 |
|
|
{ |
602 |
|
|
struct exchange *exchange = msg->exchange; |
603 |
|
|
struct ipsec_exch *ie = exchange->data; |
604 |
|
|
struct prf *prf; |
605 |
|
|
struct hash *hash = ie->hash; |
606 |
|
|
enum cryptoerr err; |
607 |
|
|
|
608 |
|
|
/* Compute Diffie-Hellman shared value. */ |
609 |
|
|
ie->g_xy_len = dh_secretlen(ie->group); |
610 |
|
|
ie->g_xy = malloc(ie->g_xy_len); |
611 |
|
|
if (!ie->g_xy) { |
612 |
|
|
/* XXX How to notify peer? */ |
613 |
|
|
log_error("ike_phase_1_post_exchange_KE_NONCE: " |
614 |
|
|
"malloc (%lu) failed", (unsigned long)ie->g_xy_len); |
615 |
|
|
return -1; |
616 |
|
|
} |
617 |
|
|
if (dh_create_shared(ie->group, ie->g_xy, |
618 |
|
|
exchange->initiator ? ie->g_xr : ie->g_xi)) { |
619 |
|
|
log_print("ike_phase_1_post_exchange_KE_NONCE: " |
620 |
|
|
"dh_create_shared failed"); |
621 |
|
|
return -1; |
622 |
|
|
} |
623 |
|
|
LOG_DBG_BUF((LOG_NEGOTIATION, 80, |
624 |
|
|
"ike_phase_1_post_exchange_KE_NONCE: g^xy", ie->g_xy, |
625 |
|
|
ie->g_xy_len)); |
626 |
|
|
|
627 |
|
|
/* Compute the SKEYID depending on the authentication method. */ |
628 |
|
|
ie->skeyid = ie->ike_auth->gen_skeyid(exchange, &ie->skeyid_len); |
629 |
|
|
if (!ie->skeyid) { |
630 |
|
|
/* XXX Log and teardown? */ |
631 |
|
|
return -1; |
632 |
|
|
} |
633 |
|
|
LOG_DBG_BUF((LOG_NEGOTIATION, 80, |
634 |
|
|
"ike_phase_1_post_exchange_KE_NONCE: SKEYID", ie->skeyid, |
635 |
|
|
ie->skeyid_len)); |
636 |
|
|
|
637 |
|
|
/* SKEYID_d. */ |
638 |
|
|
ie->skeyid_d = malloc(ie->skeyid_len); |
639 |
|
|
if (!ie->skeyid_d) { |
640 |
|
|
/* XXX How to notify peer? */ |
641 |
|
|
log_error("ike_phase_1_post_exchange_KE_NONCE: " |
642 |
|
|
"malloc (%lu) failed", (unsigned long)ie->skeyid_len); |
643 |
|
|
return -1; |
644 |
|
|
} |
645 |
|
|
prf = prf_alloc(ie->prf_type, hash->type, ie->skeyid, ie->skeyid_len); |
646 |
|
|
if (!prf) { |
647 |
|
|
/* XXX Log and teardown? */ |
648 |
|
|
return -1; |
649 |
|
|
} |
650 |
|
|
prf->Init(prf->prfctx); |
651 |
|
|
prf->Update(prf->prfctx, ie->g_xy, ie->g_xy_len); |
652 |
|
|
prf->Update(prf->prfctx, exchange->cookies, ISAKMP_HDR_COOKIES_LEN); |
653 |
|
|
prf->Update(prf->prfctx, (unsigned char *)"\0", 1); |
654 |
|
|
prf->Final(ie->skeyid_d, prf->prfctx); |
655 |
|
|
LOG_DBG_BUF((LOG_NEGOTIATION, 80, |
656 |
|
|
"ike_phase_1_post_exchange_KE_NONCE: SKEYID_d", ie->skeyid_d, |
657 |
|
|
ie->skeyid_len)); |
658 |
|
|
|
659 |
|
|
/* SKEYID_a. */ |
660 |
|
|
ie->skeyid_a = malloc(ie->skeyid_len); |
661 |
|
|
if (!ie->skeyid_a) { |
662 |
|
|
log_error("ike_phase_1_post_exchange_KE_NONCE: " |
663 |
|
|
"malloc (%lu) failed", (unsigned long)ie->skeyid_len); |
664 |
|
|
prf_free(prf); |
665 |
|
|
return -1; |
666 |
|
|
} |
667 |
|
|
prf->Init(prf->prfctx); |
668 |
|
|
prf->Update(prf->prfctx, ie->skeyid_d, ie->skeyid_len); |
669 |
|
|
prf->Update(prf->prfctx, ie->g_xy, ie->g_xy_len); |
670 |
|
|
prf->Update(prf->prfctx, exchange->cookies, ISAKMP_HDR_COOKIES_LEN); |
671 |
|
|
prf->Update(prf->prfctx, (unsigned char *)"\1", 1); |
672 |
|
|
prf->Final(ie->skeyid_a, prf->prfctx); |
673 |
|
|
LOG_DBG_BUF((LOG_NEGOTIATION, 80, |
674 |
|
|
"ike_phase_1_post_exchange_KE_NONCE: SKEYID_a", ie->skeyid_a, |
675 |
|
|
ie->skeyid_len)); |
676 |
|
|
|
677 |
|
|
/* SKEYID_e. */ |
678 |
|
|
ie->skeyid_e = malloc(ie->skeyid_len); |
679 |
|
|
if (!ie->skeyid_e) { |
680 |
|
|
/* XXX How to notify peer? */ |
681 |
|
|
log_error("ike_phase_1_post_exchange_KE_NONCE: " |
682 |
|
|
"malloc (%lu) failed", (unsigned long)ie->skeyid_len); |
683 |
|
|
prf_free(prf); |
684 |
|
|
return -1; |
685 |
|
|
} |
686 |
|
|
prf->Init(prf->prfctx); |
687 |
|
|
prf->Update(prf->prfctx, ie->skeyid_a, ie->skeyid_len); |
688 |
|
|
prf->Update(prf->prfctx, ie->g_xy, ie->g_xy_len); |
689 |
|
|
prf->Update(prf->prfctx, exchange->cookies, ISAKMP_HDR_COOKIES_LEN); |
690 |
|
|
prf->Update(prf->prfctx, (unsigned char *)"\2", 1); |
691 |
|
|
prf->Final(ie->skeyid_e, prf->prfctx); |
692 |
|
|
prf_free(prf); |
693 |
|
|
LOG_DBG_BUF((LOG_NEGOTIATION, 80, |
694 |
|
|
"ike_phase_1_post_exchange_KE_NONCE: SKEYID_e", ie->skeyid_e, |
695 |
|
|
ie->skeyid_len)); |
696 |
|
|
|
697 |
|
|
/* Key length determination. */ |
698 |
|
|
if (!exchange->key_length) |
699 |
|
|
exchange->key_length = exchange->crypto->keymax; |
700 |
|
|
|
701 |
|
|
/* Derive a longer key from skeyid_e */ |
702 |
|
|
if (ie->skeyid_len < exchange->key_length) { |
703 |
|
|
u_int16_t len, keylen; |
704 |
|
|
u_int8_t *key, *p; |
705 |
|
|
|
706 |
|
|
prf = prf_alloc(ie->prf_type, hash->type, ie->skeyid_e, |
707 |
|
|
ie->skeyid_len); |
708 |
|
|
if (!prf) { |
709 |
|
|
/* XXX - notify peer */ |
710 |
|
|
return -1; |
711 |
|
|
} |
712 |
|
|
/* Make keylen a multiple of prf->blocksize */ |
713 |
|
|
keylen = exchange->key_length; |
714 |
|
|
if (keylen % prf->blocksize) |
715 |
|
|
keylen += prf->blocksize - (keylen % prf->blocksize); |
716 |
|
|
|
717 |
|
|
key = malloc(keylen); |
718 |
|
|
if (!key) { |
719 |
|
|
/* XXX - Notify peer. */ |
720 |
|
|
prf_free(prf); |
721 |
|
|
log_error("ike_phase_1_post_exchange_KE_NONCE: " |
722 |
|
|
"malloc (%d) failed", keylen); |
723 |
|
|
return -1; |
724 |
|
|
} |
725 |
|
|
prf->Init(prf->prfctx); |
726 |
|
|
prf->Update(prf->prfctx, (unsigned char *)"\0", 1); |
727 |
|
|
prf->Final(key, prf->prfctx); |
728 |
|
|
|
729 |
|
|
for (len = prf->blocksize, p = key; len < exchange->key_length; |
730 |
|
|
len += prf->blocksize, p += prf->blocksize) { |
731 |
|
|
prf->Init(prf->prfctx); |
732 |
|
|
prf->Update(prf->prfctx, p, prf->blocksize); |
733 |
|
|
prf->Final(p + prf->blocksize, prf->prfctx); |
734 |
|
|
} |
735 |
|
|
prf_free(prf); |
736 |
|
|
|
737 |
|
|
/* Setup our keystate using the derived encryption key. */ |
738 |
|
|
exchange->keystate = crypto_init(exchange->crypto, key, |
739 |
|
|
exchange->key_length, &err); |
740 |
|
|
|
741 |
|
|
free(key); |
742 |
|
|
} else |
743 |
|
|
/* Setup our keystate using the raw skeyid_e. */ |
744 |
|
|
exchange->keystate = crypto_init(exchange->crypto, |
745 |
|
|
ie->skeyid_e, exchange->key_length, &err); |
746 |
|
|
|
747 |
|
|
/* Special handling for DES weak keys. */ |
748 |
|
|
if (!exchange->keystate && err == EWEAKKEY && |
749 |
|
|
(exchange->key_length << 1) <= ie->skeyid_len) { |
750 |
|
|
log_print("ike_phase_1_post_exchange_KE_NONCE: " |
751 |
|
|
"weak key, trying subseq. skeyid_e"); |
752 |
|
|
exchange->keystate = crypto_init(exchange->crypto, |
753 |
|
|
ie->skeyid_e + exchange->key_length, |
754 |
|
|
exchange->key_length, &err); |
755 |
|
|
} |
756 |
|
|
if (!exchange->keystate) { |
757 |
|
|
log_print("ike_phase_1_post_exchange_KE_NONCE: " |
758 |
|
|
"exchange->crypto->init () failed: %d", err); |
759 |
|
|
|
760 |
|
|
/* |
761 |
|
|
* XXX We really need to know if problems are of transient |
762 |
|
|
* nature or fatal (like failed assertions etc.) |
763 |
|
|
*/ |
764 |
|
|
return -1; |
765 |
|
|
} |
766 |
|
|
/* Setup IV. XXX Only for CBC transforms, no? */ |
767 |
|
|
hash->Init(hash->ctx); |
768 |
|
|
hash->Update(hash->ctx, ie->g_xi, ie->g_x_len); |
769 |
|
|
hash->Update(hash->ctx, ie->g_xr, ie->g_x_len); |
770 |
|
|
hash->Final(hash->digest, hash->ctx); |
771 |
|
|
crypto_init_iv(exchange->keystate, hash->digest, |
772 |
|
|
exchange->crypto->blocksize); |
773 |
|
|
return 0; |
774 |
|
|
} |
775 |
|
|
|
776 |
|
|
int |
777 |
|
|
ike_phase_1_responder_send_ID_AUTH(struct message *msg) |
778 |
|
|
{ |
779 |
|
|
if (ike_phase_1_send_ID(msg)) |
780 |
|
|
return -1; |
781 |
|
|
|
782 |
|
|
return ike_phase_1_send_AUTH(msg); |
783 |
|
|
} |
784 |
|
|
|
785 |
|
|
int |
786 |
|
|
ike_phase_1_send_ID(struct message *msg) |
787 |
|
|
{ |
788 |
|
|
struct exchange *exchange = msg->exchange; |
789 |
|
|
u_int8_t *buf; |
790 |
|
|
char header[80]; |
791 |
|
|
ssize_t sz; |
792 |
|
|
struct sockaddr *src; |
793 |
|
|
int initiator = exchange->initiator; |
794 |
|
|
u_int8_t **id; |
795 |
|
|
size_t *id_len; |
796 |
|
|
char *my_id = 0, *data; |
797 |
|
|
u_int8_t id_type; |
798 |
|
|
sa_family_t af = 0; |
799 |
|
|
|
800 |
|
|
/* Choose the right fields to fill-in. */ |
801 |
|
|
id = initiator ? &exchange->id_i : &exchange->id_r; |
802 |
|
|
id_len = initiator ? &exchange->id_i_len : &exchange->id_r_len; |
803 |
|
|
|
804 |
|
|
if (exchange->name) |
805 |
|
|
my_id = conf_get_str(exchange->name, "ID"); |
806 |
|
|
|
807 |
|
|
if (!my_id) |
808 |
|
|
my_id = conf_get_str("General", "Default-phase-1-ID"); |
809 |
|
|
|
810 |
|
|
msg->transport->vtbl->get_src(msg->transport, &src); |
811 |
|
|
sz = my_id ? ipsec_id_size(my_id, &id_type) : sockaddr_addrlen(src); |
812 |
|
|
if (sz == -1) |
813 |
|
|
return -1; |
814 |
|
|
|
815 |
|
|
sz += ISAKMP_ID_DATA_OFF; |
816 |
|
|
buf = malloc(sz); |
817 |
|
|
if (!buf) { |
818 |
|
|
log_error("ike_phase_1_send_ID: malloc (%lu) failed", |
819 |
|
|
(unsigned long)sz); |
820 |
|
|
return -1; |
821 |
|
|
} |
822 |
|
|
SET_IPSEC_ID_PROTO(buf + ISAKMP_ID_DOI_DATA_OFF, 0); |
823 |
|
|
SET_IPSEC_ID_PORT(buf + ISAKMP_ID_DOI_DATA_OFF, 0); |
824 |
|
|
if (my_id) { |
825 |
|
|
SET_ISAKMP_ID_TYPE(buf, id_type); |
826 |
|
|
switch (id_type) { |
827 |
|
|
case IPSEC_ID_IPV4_ADDR: |
828 |
|
|
case IPSEC_ID_IPV4_ADDR_SUBNET: |
829 |
|
|
af = AF_INET; |
830 |
|
|
break; |
831 |
|
|
case IPSEC_ID_IPV6_ADDR: |
832 |
|
|
case IPSEC_ID_IPV6_ADDR_SUBNET: |
833 |
|
|
af = AF_INET6; |
834 |
|
|
break; |
835 |
|
|
} |
836 |
|
|
switch (id_type) { |
837 |
|
|
case IPSEC_ID_IPV4_ADDR: |
838 |
|
|
case IPSEC_ID_IPV6_ADDR: |
839 |
|
|
data = conf_get_str(my_id, "Address"); |
840 |
|
|
if (!data) { |
841 |
|
|
log_print("ike_phase_1_send_ID: section %s " |
842 |
|
|
"has no \"Address\" tag", my_id); |
843 |
|
|
free(buf); |
844 |
|
|
return -1; |
845 |
|
|
} |
846 |
|
|
if (text2sockaddr(data, NULL, &src, af, 0)) { |
847 |
|
|
log_error("ike_phase_1_send_ID: " |
848 |
|
|
"text2sockaddr() failed"); |
849 |
|
|
free(buf); |
850 |
|
|
return -1; |
851 |
|
|
} |
852 |
|
|
memcpy(buf + ISAKMP_ID_DATA_OFF, |
853 |
|
|
sockaddr_addrdata(src), sockaddr_addrlen(src)); |
854 |
|
|
free(src); |
855 |
|
|
break; |
856 |
|
|
|
857 |
|
|
case IPSEC_ID_IPV4_ADDR_SUBNET: |
858 |
|
|
case IPSEC_ID_IPV6_ADDR_SUBNET: |
859 |
|
|
/* Network */ |
860 |
|
|
data = conf_get_str(my_id, "Network"); |
861 |
|
|
if (!data) { |
862 |
|
|
log_print("ike_phase_1_send_ID: section %s " |
863 |
|
|
"has no \"Network\" tag", my_id); |
864 |
|
|
free(buf); |
865 |
|
|
return -1; |
866 |
|
|
} |
867 |
|
|
if (text2sockaddr(data, NULL, &src, af, 0)) { |
868 |
|
|
log_error("ike_phase_1_send_ID: " |
869 |
|
|
"text2sockaddr() failed"); |
870 |
|
|
free(buf); |
871 |
|
|
return -1; |
872 |
|
|
} |
873 |
|
|
memcpy(buf + ISAKMP_ID_DATA_OFF, |
874 |
|
|
sockaddr_addrdata(src), sockaddr_addrlen(src)); |
875 |
|
|
free(src); |
876 |
|
|
/* Netmask */ |
877 |
|
|
data = conf_get_str(my_id, "Netmask"); |
878 |
|
|
if (!data) { |
879 |
|
|
log_print("ike_phase_1_send_ID: section %s " |
880 |
|
|
"has no \"Netmask\" tag", my_id); |
881 |
|
|
free(buf); |
882 |
|
|
return -1; |
883 |
|
|
} |
884 |
|
|
if (text2sockaddr(data, NULL, &src, af, 1)) { |
885 |
|
|
log_error("ike_phase_1_send_ID: " |
886 |
|
|
"text2sockaddr() failed"); |
887 |
|
|
free(buf); |
888 |
|
|
return -1; |
889 |
|
|
} |
890 |
|
|
memcpy(buf + ISAKMP_ID_DATA_OFF + |
891 |
|
|
sockaddr_addrlen(src), sockaddr_addrdata(src), |
892 |
|
|
sockaddr_addrlen(src)); |
893 |
|
|
free(src); |
894 |
|
|
break; |
895 |
|
|
|
896 |
|
|
case IPSEC_ID_FQDN: |
897 |
|
|
case IPSEC_ID_USER_FQDN: |
898 |
|
|
case IPSEC_ID_KEY_ID: |
899 |
|
|
data = conf_get_str(my_id, "Name"); |
900 |
|
|
if (!data) { |
901 |
|
|
log_print("ike_phase_1_send_ID: section %s " |
902 |
|
|
"has no \"Name\" tag", my_id); |
903 |
|
|
free(buf); |
904 |
|
|
return -1; |
905 |
|
|
} |
906 |
|
|
memcpy(buf + ISAKMP_ID_DATA_OFF, data, |
907 |
|
|
sz - ISAKMP_ID_DATA_OFF); |
908 |
|
|
break; |
909 |
|
|
|
910 |
|
|
default: |
911 |
|
|
log_print("ike_phase_1_send_ID: " |
912 |
|
|
"unsupported ID type %d", id_type); |
913 |
|
|
free(buf); |
914 |
|
|
return -1; |
915 |
|
|
} |
916 |
|
|
} else { |
917 |
|
|
switch (src->sa_family) { |
918 |
|
|
case AF_INET: |
919 |
|
|
SET_ISAKMP_ID_TYPE(buf, IPSEC_ID_IPV4_ADDR); |
920 |
|
|
break; |
921 |
|
|
case AF_INET6: |
922 |
|
|
SET_ISAKMP_ID_TYPE(buf, IPSEC_ID_IPV6_ADDR); |
923 |
|
|
break; |
924 |
|
|
} |
925 |
|
|
/* Already in network byteorder. */ |
926 |
|
|
memcpy(buf + ISAKMP_ID_DATA_OFF, sockaddr_addrdata(src), |
927 |
|
|
sockaddr_addrlen(src)); |
928 |
|
|
} |
929 |
|
|
|
930 |
|
|
if (message_add_payload(msg, ISAKMP_PAYLOAD_ID, buf, sz, 1)) { |
931 |
|
|
free(buf); |
932 |
|
|
return -1; |
933 |
|
|
} |
934 |
|
|
*id_len = sz - ISAKMP_GEN_SZ; |
935 |
|
|
*id = malloc(*id_len); |
936 |
|
|
if (!*id) { |
937 |
|
|
log_error("ike_phase_1_send_ID: malloc (%lu) failed", |
938 |
|
|
(unsigned long)*id_len); |
939 |
|
|
return -1; |
940 |
|
|
} |
941 |
|
|
memcpy(*id, buf + ISAKMP_GEN_SZ, *id_len); |
942 |
|
|
snprintf(header, sizeof header, "ike_phase_1_send_ID: %s", |
943 |
|
|
constant_name(ipsec_id_cst, GET_ISAKMP_ID_TYPE(buf))); |
944 |
|
|
LOG_DBG_BUF((LOG_NEGOTIATION, 40, header, buf + ISAKMP_ID_DATA_OFF, |
945 |
|
|
sz - ISAKMP_ID_DATA_OFF)); |
946 |
|
|
return 0; |
947 |
|
|
} |
948 |
|
|
|
949 |
|
|
int |
950 |
|
|
ike_phase_1_send_AUTH(struct message *msg) |
951 |
|
|
{ |
952 |
|
|
struct exchange *exchange = msg->exchange; |
953 |
|
|
struct ipsec_exch *ie = exchange->data; |
954 |
|
|
|
955 |
|
|
if (ie->ike_auth->encode_hash(msg)) { |
956 |
|
|
/* XXX Log? */ |
957 |
|
|
return -1; |
958 |
|
|
} |
959 |
|
|
/* |
960 |
|
|
* XXX Many people say the COMMIT flag is just junk, especially in |
961 |
|
|
* Phase 1. |
962 |
|
|
*/ |
963 |
|
|
#ifdef notyet |
964 |
|
|
if ((exchange->flags & EXCHANGE_FLAG_COMMITTED) == 0) |
965 |
|
|
exchange->flags |= EXCHANGE_FLAG_I_COMMITTED; |
966 |
|
|
#endif |
967 |
|
|
|
968 |
|
|
return 0; |
969 |
|
|
} |
970 |
|
|
|
971 |
|
|
/* Receive ID and HASH and check that the exchange has been consistent. */ |
972 |
|
|
int |
973 |
|
|
ike_phase_1_recv_ID_AUTH(struct message *msg) |
974 |
|
|
{ |
975 |
|
|
if (ike_phase_1_recv_ID(msg)) |
976 |
|
|
return -1; |
977 |
|
|
|
978 |
|
|
return ike_phase_1_recv_AUTH(msg); |
979 |
|
|
} |
980 |
|
|
|
981 |
|
|
/* Receive ID. */ |
982 |
|
|
int |
983 |
|
|
ike_phase_1_recv_ID(struct message *msg) |
984 |
|
|
{ |
985 |
|
|
struct exchange *exchange = msg->exchange; |
986 |
|
|
struct payload *payload; |
987 |
|
|
char header[80], *rs = 0, *rid = 0, *p; |
988 |
|
|
int initiator = exchange->initiator; |
989 |
|
|
u_int8_t **id, id_type; |
990 |
|
|
size_t *id_len; |
991 |
|
|
ssize_t sz; |
992 |
|
|
struct sockaddr *sa; |
993 |
|
|
sa_family_t af = 0; |
994 |
|
|
|
995 |
|
|
payload = payload_first(msg, ISAKMP_PAYLOAD_ID); |
996 |
|
|
|
997 |
|
|
if (exchange->name) |
998 |
|
|
rs = conf_get_str(exchange->name, "Remote-ID"); |
999 |
|
|
|
1000 |
|
|
if (rs) { |
1001 |
|
|
sz = ipsec_id_size(rs, &id_type); |
1002 |
|
|
if (sz == -1) { |
1003 |
|
|
log_print("ike_phase_1_recv_ID: could not handle " |
1004 |
|
|
"specified Remote-ID [%s]", rs); |
1005 |
|
|
return -1; |
1006 |
|
|
} |
1007 |
|
|
rid = malloc(sz); |
1008 |
|
|
if (!rid) { |
1009 |
|
|
log_error("ike_phase_1_recv_ID: malloc (%lu) failed", |
1010 |
|
|
(unsigned long)sz); |
1011 |
|
|
return -1; |
1012 |
|
|
} |
1013 |
|
|
switch (id_type) { |
1014 |
|
|
case IPSEC_ID_IPV4_ADDR: |
1015 |
|
|
af = AF_INET; |
1016 |
|
|
break; |
1017 |
|
|
case IPSEC_ID_IPV6_ADDR: |
1018 |
|
|
af = AF_INET6; |
1019 |
|
|
break; |
1020 |
|
|
} |
1021 |
|
|
switch (id_type) { |
1022 |
|
|
case IPSEC_ID_IPV4_ADDR: |
1023 |
|
|
case IPSEC_ID_IPV6_ADDR: |
1024 |
|
|
p = conf_get_str(rs, "Address"); |
1025 |
|
|
if (!p) { |
1026 |
|
|
log_print("ike_phase_1_recv_ID: failed to get " |
1027 |
|
|
"Address in Remote-ID section [%s]", rs); |
1028 |
|
|
free(rid); |
1029 |
|
|
return -1; |
1030 |
|
|
} |
1031 |
|
|
if (text2sockaddr(p, 0, &sa, af, 0) == -1) { |
1032 |
|
|
log_print("ike_phase_1_recv_ID: " |
1033 |
|
|
"failed to parse address %s", p); |
1034 |
|
|
free(rid); |
1035 |
|
|
return -1; |
1036 |
|
|
} |
1037 |
|
|
if ((id_type == IPSEC_ID_IPV4_ADDR && |
1038 |
|
|
sa->sa_family != AF_INET) || |
1039 |
|
|
(id_type == IPSEC_ID_IPV6_ADDR && |
1040 |
|
|
sa->sa_family != AF_INET6)) { |
1041 |
|
|
log_print("ike_phase_1_recv_ID: " |
1042 |
|
|
"address %s not of expected family", p); |
1043 |
|
|
free(rid); |
1044 |
|
|
free(sa); |
1045 |
|
|
return -1; |
1046 |
|
|
} |
1047 |
|
|
memcpy(rid, sockaddr_addrdata(sa), |
1048 |
|
|
sockaddr_addrlen(sa)); |
1049 |
|
|
free(sa); |
1050 |
|
|
break; |
1051 |
|
|
|
1052 |
|
|
case IPSEC_ID_FQDN: |
1053 |
|
|
case IPSEC_ID_USER_FQDN: |
1054 |
|
|
case IPSEC_ID_KEY_ID: |
1055 |
|
|
p = conf_get_str(rs, "Name"); |
1056 |
|
|
if (!p) { |
1057 |
|
|
log_print("ike_phase_1_recv_ID: failed to " |
1058 |
|
|
"get Name in Remote-ID section [%s]", rs); |
1059 |
|
|
free(rid); |
1060 |
|
|
return -1; |
1061 |
|
|
} |
1062 |
|
|
memcpy(rid, p, sz); |
1063 |
|
|
break; |
1064 |
|
|
|
1065 |
|
|
default: |
1066 |
|
|
log_print("ike_phase_1_recv_ID: " |
1067 |
|
|
"unsupported ID type %d", id_type); |
1068 |
|
|
free(rid); |
1069 |
|
|
return -1; |
1070 |
|
|
} |
1071 |
|
|
|
1072 |
|
|
/* Compare expected/desired and received remote ID */ |
1073 |
|
|
if (memcmp(rid, payload->p + ISAKMP_ID_DATA_OFF, sz) != 0) { |
1074 |
|
|
free(rid); |
1075 |
|
|
log_print("ike_phase_1_recv_ID: " |
1076 |
|
|
"received remote ID other than expected %s", p); |
1077 |
|
|
return -1; |
1078 |
|
|
} |
1079 |
|
|
free(rid); |
1080 |
|
|
} |
1081 |
|
|
/* Choose the right fields to fill in */ |
1082 |
|
|
id = initiator ? &exchange->id_r : &exchange->id_i; |
1083 |
|
|
id_len = initiator ? &exchange->id_r_len : &exchange->id_i_len; |
1084 |
|
|
|
1085 |
|
|
*id_len = GET_ISAKMP_GEN_LENGTH(payload->p) - ISAKMP_GEN_SZ; |
1086 |
|
|
*id = malloc(*id_len); |
1087 |
|
|
if (!*id) { |
1088 |
|
|
log_error("ike_phase_1_recv_ID: malloc (%lu) failed", |
1089 |
|
|
(unsigned long)*id_len); |
1090 |
|
|
return -1; |
1091 |
|
|
} |
1092 |
|
|
memcpy(*id, payload->p + ISAKMP_GEN_SZ, *id_len); |
1093 |
|
|
snprintf(header, sizeof header, "ike_phase_1_recv_ID: %s", |
1094 |
|
|
constant_name(ipsec_id_cst, GET_ISAKMP_ID_TYPE(payload->p))); |
1095 |
|
|
LOG_DBG_BUF((LOG_NEGOTIATION, 40, header, |
1096 |
|
|
payload->p + ISAKMP_ID_DATA_OFF, |
1097 |
|
|
*id_len + ISAKMP_GEN_SZ - ISAKMP_ID_DATA_OFF)); |
1098 |
|
|
payload->flags |= PL_MARK; |
1099 |
|
|
return 0; |
1100 |
|
|
} |
1101 |
|
|
|
1102 |
|
|
/* Receive HASH and check that the exchange has been consistent. */ |
1103 |
|
|
int |
1104 |
|
|
ike_phase_1_recv_AUTH(struct message *msg) |
1105 |
|
|
{ |
1106 |
|
|
struct exchange *exchange = msg->exchange; |
1107 |
|
|
struct ipsec_exch *ie = exchange->data; |
1108 |
|
|
struct prf *prf; |
1109 |
|
|
struct hash *hash = ie->hash; |
1110 |
|
|
char header[80]; |
1111 |
|
|
size_t hashsize = hash->hashsize; |
1112 |
|
|
int initiator = exchange->initiator; |
1113 |
|
|
u_int8_t **hash_p, *id; |
1114 |
|
|
size_t id_len; |
1115 |
|
|
|
1116 |
|
|
/* Choose the right fields to fill in */ |
1117 |
|
|
hash_p = initiator ? &ie->hash_r : &ie->hash_i; |
1118 |
|
|
id = initiator ? exchange->id_r : exchange->id_i; |
1119 |
|
|
id_len = initiator ? exchange->id_r_len : exchange->id_i_len; |
1120 |
|
|
|
1121 |
|
|
/* The decoded hash will be in ie->hash_r or ie->hash_i */ |
1122 |
|
|
if (ie->ike_auth->decode_hash(msg)) { |
1123 |
|
|
message_drop(msg, ISAKMP_NOTIFY_INVALID_ID_INFORMATION, 0, 1, |
1124 |
|
|
0); |
1125 |
|
|
return -1; |
1126 |
|
|
} |
1127 |
|
|
/* Allocate the prf and start calculating his HASH. */ |
1128 |
|
|
prf = prf_alloc(ie->prf_type, hash->type, ie->skeyid, ie->skeyid_len); |
1129 |
|
|
if (!prf) { |
1130 |
|
|
/* XXX Log? */ |
1131 |
|
|
return -1; |
1132 |
|
|
} |
1133 |
|
|
prf->Init(prf->prfctx); |
1134 |
|
|
prf->Update(prf->prfctx, initiator ? ie->g_xr : ie->g_xi, ie->g_x_len); |
1135 |
|
|
prf->Update(prf->prfctx, initiator ? ie->g_xi : ie->g_xr, ie->g_x_len); |
1136 |
|
|
prf->Update(prf->prfctx, exchange->cookies + |
1137 |
|
|
(initiator ? ISAKMP_HDR_RCOOKIE_OFF : ISAKMP_HDR_ICOOKIE_OFF), |
1138 |
|
|
ISAKMP_HDR_ICOOKIE_LEN); |
1139 |
|
|
prf->Update(prf->prfctx, exchange->cookies + |
1140 |
|
|
(initiator ? ISAKMP_HDR_ICOOKIE_OFF : ISAKMP_HDR_RCOOKIE_OFF), |
1141 |
|
|
ISAKMP_HDR_ICOOKIE_LEN); |
1142 |
|
|
prf->Update(prf->prfctx, ie->sa_i_b, ie->sa_i_b_len); |
1143 |
|
|
prf->Update(prf->prfctx, id, id_len); |
1144 |
|
|
prf->Final(hash->digest, prf->prfctx); |
1145 |
|
|
prf_free(prf); |
1146 |
|
|
snprintf(header, sizeof header, "ike_phase_1_recv_AUTH: " |
1147 |
|
|
"computed HASH_%c", initiator ? 'R' : 'I'); |
1148 |
|
|
LOG_DBG_BUF((LOG_NEGOTIATION, 80, header, hash->digest, hashsize)); |
1149 |
|
|
|
1150 |
|
|
/* Check that the hash we got matches the one we computed. */ |
1151 |
|
|
if (memcmp(*hash_p, hash->digest, hashsize) != 0) { |
1152 |
|
|
/* XXX Log? */ |
1153 |
|
|
return -1; |
1154 |
|
|
} |
1155 |
|
|
|
1156 |
|
|
/* Mark message as authenticated. */ |
1157 |
|
|
msg->flags |= MSG_AUTHENTICATED; |
1158 |
|
|
|
1159 |
|
|
return 0; |
1160 |
|
|
} |
1161 |
|
|
|
1162 |
|
|
struct attr_node { |
1163 |
|
|
LIST_ENTRY(attr_node) link; |
1164 |
|
|
u_int16_t type; |
1165 |
|
|
}; |
1166 |
|
|
|
1167 |
|
|
struct validation_state { |
1168 |
|
|
struct conf_list_node *xf; |
1169 |
|
|
LIST_HEAD(attr_head, attr_node) attrs; |
1170 |
|
|
char *life; |
1171 |
|
|
}; |
1172 |
|
|
|
1173 |
|
|
/* Validate a proposal inside SA according to EXCHANGE's policy. */ |
1174 |
|
|
static int |
1175 |
|
|
ike_phase_1_validate_prop(struct exchange *exchange, struct sa *sa, |
1176 |
|
|
struct sa *isakmp_sa) |
1177 |
|
|
{ |
1178 |
|
|
struct conf_list *conf, *tags; |
1179 |
|
|
struct conf_list_node *xf, *tag; |
1180 |
|
|
struct proto *proto; |
1181 |
|
|
struct validation_state vs; |
1182 |
|
|
struct attr_node *node, *next_node; |
1183 |
|
|
|
1184 |
|
|
/* Get the list of transforms. */ |
1185 |
|
|
conf = conf_get_list(exchange->policy, "Transforms"); |
1186 |
|
|
if (!conf) |
1187 |
|
|
return 0; |
1188 |
|
|
|
1189 |
|
|
for (xf = TAILQ_FIRST(&conf->fields); xf; xf = TAILQ_NEXT(xf, link)) { |
1190 |
|
|
for (proto = TAILQ_FIRST(&sa->protos); proto; |
1191 |
|
|
proto = TAILQ_NEXT(proto, link)) { |
1192 |
|
|
/* Mark all attributes in our policy as unseen. */ |
1193 |
|
|
LIST_INIT(&vs.attrs); |
1194 |
|
|
vs.xf = xf; |
1195 |
|
|
vs.life = 0; |
1196 |
|
|
if (attribute_map(proto->chosen->p + |
1197 |
|
|
ISAKMP_TRANSFORM_SA_ATTRS_OFF, |
1198 |
|
|
GET_ISAKMP_GEN_LENGTH(proto->chosen->p) - |
1199 |
|
|
ISAKMP_TRANSFORM_SA_ATTRS_OFF, |
1200 |
|
|
attribute_unacceptable, &vs)) |
1201 |
|
|
goto try_next; |
1202 |
|
|
|
1203 |
|
|
/* Sweep over unseen tags in this section. */ |
1204 |
|
|
tags = conf_get_tag_list(xf->field); |
1205 |
|
|
if (tags) { |
1206 |
|
|
for (tag = TAILQ_FIRST(&tags->fields); tag; |
1207 |
|
|
tag = TAILQ_NEXT(tag, link)) |
1208 |
|
|
/* |
1209 |
|
|
* XXX Should we care about attributes |
1210 |
|
|
* we have, they do not provide? |
1211 |
|
|
*/ |
1212 |
|
|
for (node = LIST_FIRST(&vs.attrs); |
1213 |
|
|
node; node = next_node) { |
1214 |
|
|
next_node = |
1215 |
|
|
LIST_NEXT(node, link); |
1216 |
|
|
if (node->type == |
1217 |
|
|
constant_value(ike_attr_cst, |
1218 |
|
|
tag->field)) { |
1219 |
|
|
LIST_REMOVE(node, link); |
1220 |
|
|
free(node); |
1221 |
|
|
} |
1222 |
|
|
} |
1223 |
|
|
conf_free_list(tags); |
1224 |
|
|
} |
1225 |
|
|
/* Are there leftover tags in this section? */ |
1226 |
|
|
node = LIST_FIRST(&vs.attrs); |
1227 |
|
|
if (node) |
1228 |
|
|
goto try_next; |
1229 |
|
|
} |
1230 |
|
|
|
1231 |
|
|
/* All protocols were OK, we succeeded. */ |
1232 |
|
|
LOG_DBG((LOG_NEGOTIATION, 20, "ike_phase_1_validate_prop: " |
1233 |
|
|
"success")); |
1234 |
|
|
conf_free_list(conf); |
1235 |
|
|
free(vs.life); |
1236 |
|
|
return 1; |
1237 |
|
|
|
1238 |
|
|
try_next: |
1239 |
|
|
/* Are there leftover tags in this section? */ |
1240 |
|
|
node = LIST_FIRST(&vs.attrs); |
1241 |
|
|
while (node) { |
1242 |
|
|
LIST_REMOVE(node, link); |
1243 |
|
|
free(node); |
1244 |
|
|
node = LIST_FIRST(&vs.attrs); |
1245 |
|
|
} |
1246 |
|
|
free(vs.life); |
1247 |
|
|
} |
1248 |
|
|
|
1249 |
|
|
LOG_DBG((LOG_NEGOTIATION, 20, "ike_phase_1_validate_prop: failure")); |
1250 |
|
|
conf_free_list(conf); |
1251 |
|
|
return 0; |
1252 |
|
|
} |
1253 |
|
|
|
1254 |
|
|
/* |
1255 |
|
|
* Look at the attribute of type TYPE, located at VALUE for LEN bytes forward. |
1256 |
|
|
* The VVS argument holds a validation state kept across invocations. |
1257 |
|
|
* If the attribute is unacceptable to use, return non-zero, otherwise zero. |
1258 |
|
|
*/ |
1259 |
|
|
static int |
1260 |
|
|
attribute_unacceptable(u_int16_t type, u_int8_t *value, u_int16_t len, |
1261 |
|
|
void *vvs) |
1262 |
|
|
{ |
1263 |
|
|
struct validation_state *vs = vvs; |
1264 |
|
|
struct conf_list *life_conf; |
1265 |
|
|
struct conf_list_node *xf = vs->xf, *life; |
1266 |
|
|
char *tag = constant_lookup(ike_attr_cst, type); |
1267 |
|
|
char *str; |
1268 |
|
|
struct constant_map *map; |
1269 |
|
|
struct attr_node *node; |
1270 |
|
|
int rv, dur = 0; |
1271 |
|
|
|
1272 |
|
|
if (!tag) { |
1273 |
|
|
log_print("attribute_unacceptable: " |
1274 |
|
|
"attribute type %d not known", type); |
1275 |
|
|
return 1; |
1276 |
|
|
} |
1277 |
|
|
switch (type) { |
1278 |
|
|
case IKE_ATTR_ENCRYPTION_ALGORITHM: |
1279 |
|
|
case IKE_ATTR_HASH_ALGORITHM: |
1280 |
|
|
case IKE_ATTR_AUTHENTICATION_METHOD: |
1281 |
|
|
case IKE_ATTR_GROUP_DESCRIPTION: |
1282 |
|
|
case IKE_ATTR_GROUP_TYPE: |
1283 |
|
|
case IKE_ATTR_PRF: |
1284 |
|
|
str = conf_get_str(xf->field, tag); |
1285 |
|
|
if (!str) { |
1286 |
|
|
/* This attribute does not exist in this policy. */ |
1287 |
|
|
log_print("attribute_unacceptable: " |
1288 |
|
|
"attr %s does not exist in %s", tag, xf->field); |
1289 |
|
|
return 1; |
1290 |
|
|
} |
1291 |
|
|
map = constant_link_lookup(ike_attr_cst, type); |
1292 |
|
|
if (!map) |
1293 |
|
|
return 1; |
1294 |
|
|
|
1295 |
|
|
if ((constant_value(map, str) == decode_16(value)) || |
1296 |
|
|
(!strcmp(str, "ANY"))) { |
1297 |
|
|
/* Mark this attribute as seen. */ |
1298 |
|
|
node = malloc(sizeof *node); |
1299 |
|
|
if (!node) { |
1300 |
|
|
log_error("attribute_unacceptable: " |
1301 |
|
|
"malloc (%lu) failed", |
1302 |
|
|
(unsigned long)sizeof *node); |
1303 |
|
|
return 1; |
1304 |
|
|
} |
1305 |
|
|
node->type = type; |
1306 |
|
|
LIST_INSERT_HEAD(&vs->attrs, node, link); |
1307 |
|
|
return 0; |
1308 |
|
|
} |
1309 |
|
|
log_print("attribute_unacceptable: %s: got %s, expected %s", |
1310 |
|
|
tag, constant_name(map, decode_16(value)), str); |
1311 |
|
|
return 1; |
1312 |
|
|
|
1313 |
|
|
case IKE_ATTR_GROUP_PRIME: |
1314 |
|
|
case IKE_ATTR_GROUP_GENERATOR_1: |
1315 |
|
|
case IKE_ATTR_GROUP_GENERATOR_2: |
1316 |
|
|
case IKE_ATTR_GROUP_CURVE_A: |
1317 |
|
|
case IKE_ATTR_GROUP_CURVE_B: |
1318 |
|
|
/* XXX Bignums not handled yet. */ |
1319 |
|
|
log_print("attribute_unacceptable: " |
1320 |
|
|
"bignum type %d not supported", type); |
1321 |
|
|
return 1; |
1322 |
|
|
|
1323 |
|
|
case IKE_ATTR_LIFE_TYPE: |
1324 |
|
|
case IKE_ATTR_LIFE_DURATION: |
1325 |
|
|
life_conf = conf_get_list(xf->field, "Life"); |
1326 |
|
|
if (life_conf && |
1327 |
|
|
!strcmp(conf_get_str(xf->field, "Life"), "ANY")) { |
1328 |
|
|
conf_free_list(life_conf); |
1329 |
|
|
return 0; |
1330 |
|
|
} |
1331 |
|
|
|
1332 |
|
|
rv = 1; |
1333 |
|
|
if (!life_conf) { |
1334 |
|
|
/* Life attributes given, but not in our policy. */ |
1335 |
|
|
log_print("attribute_unacceptable: " |
1336 |
|
|
"life attribute received, none in policy"); |
1337 |
|
|
return 1; |
1338 |
|
|
} |
1339 |
|
|
/* |
1340 |
|
|
* Each lifetime type must match, otherwise we turn the |
1341 |
|
|
* proposal down. In order to do this we need to find the |
1342 |
|
|
* specific section of our policy's "Life" list and match |
1343 |
|
|
* its duration. |
1344 |
|
|
*/ |
1345 |
|
|
switch (type) { |
1346 |
|
|
case IKE_ATTR_LIFE_TYPE: |
1347 |
|
|
for (life = TAILQ_FIRST(&life_conf->fields); life; |
1348 |
|
|
life = TAILQ_NEXT(life, link)) { |
1349 |
|
|
str = conf_get_str(life->field, "LIFE_TYPE"); |
1350 |
|
|
if (!str) { |
1351 |
|
|
log_print("attribute_unacceptable: " |
1352 |
|
|
"section [%s] has no LIFE_TYPE", |
1353 |
|
|
life->field); |
1354 |
|
|
continue; |
1355 |
|
|
} |
1356 |
|
|
|
1357 |
|
|
/* |
1358 |
|
|
* If this is the type we are looking at, |
1359 |
|
|
* save a pointer to this section in vs->life. |
1360 |
|
|
*/ |
1361 |
|
|
if (constant_value(ike_duration_cst, str) == |
1362 |
|
|
decode_16(value)) { |
1363 |
|
|
vs->life = strdup(life->field); |
1364 |
|
|
rv = 0; |
1365 |
|
|
goto bail_out; |
1366 |
|
|
} |
1367 |
|
|
} |
1368 |
|
|
log_print("attribute_unacceptable: " |
1369 |
|
|
"unrecognized LIFE_TYPE %d", decode_16(value)); |
1370 |
|
|
vs->life = 0; |
1371 |
|
|
break; |
1372 |
|
|
|
1373 |
|
|
case IKE_ATTR_LIFE_DURATION: |
1374 |
|
|
if (!vs->life) { |
1375 |
|
|
log_print("attribute_unacceptable: " |
1376 |
|
|
"LIFE_DURATION without LIFE_TYPE"); |
1377 |
|
|
rv = 1; |
1378 |
|
|
goto bail_out; |
1379 |
|
|
} |
1380 |
|
|
str = conf_get_str(vs->life, "LIFE_DURATION"); |
1381 |
|
|
if (str) { |
1382 |
|
|
if (!strcmp(str, "ANY")) |
1383 |
|
|
rv = 0; |
1384 |
|
|
else |
1385 |
|
|
dur = (len == 4) ? decode_32(value) : |
1386 |
|
|
decode_16(value); |
1387 |
|
|
if ((rv = !conf_match_num(vs->life, |
1388 |
|
|
"LIFE_DURATION", dur))) { |
1389 |
|
|
log_print( |
1390 |
|
|
"attribute_unacceptable: " |
1391 |
|
|
"LIFE_DURATION: got %d, " |
1392 |
|
|
" expected %s", dur, str); |
1393 |
|
|
} |
1394 |
|
|
} else { |
1395 |
|
|
log_print("attribute_unacceptable: " |
1396 |
|
|
"section [%s] has no LIFE_DURATION", |
1397 |
|
|
vs->life); |
1398 |
|
|
rv = 1; |
1399 |
|
|
} |
1400 |
|
|
|
1401 |
|
|
free(vs->life); |
1402 |
|
|
vs->life = 0; |
1403 |
|
|
break; |
1404 |
|
|
} |
1405 |
|
|
|
1406 |
|
|
bail_out: |
1407 |
|
|
conf_free_list(life_conf); |
1408 |
|
|
return rv; |
1409 |
|
|
|
1410 |
|
|
case IKE_ATTR_KEY_LENGTH: |
1411 |
|
|
case IKE_ATTR_FIELD_SIZE: |
1412 |
|
|
case IKE_ATTR_GROUP_ORDER: |
1413 |
|
|
if (conf_match_num(xf->field, tag, decode_16(value))) { |
1414 |
|
|
/* Mark this attribute as seen. */ |
1415 |
|
|
node = malloc(sizeof *node); |
1416 |
|
|
if (!node) { |
1417 |
|
|
log_error("attribute_unacceptable: " |
1418 |
|
|
"malloc (%lu) failed", |
1419 |
|
|
(unsigned long)sizeof *node); |
1420 |
|
|
return 1; |
1421 |
|
|
} |
1422 |
|
|
node->type = type; |
1423 |
|
|
LIST_INSERT_HEAD(&vs->attrs, node, link); |
1424 |
|
|
return 0; |
1425 |
|
|
} |
1426 |
|
|
return 1; |
1427 |
|
|
default: |
1428 |
|
|
log_print("attribute_unacceptable: unexpected type %d", |
1429 |
|
|
type); |
1430 |
|
|
} |
1431 |
|
|
return 1; |
1432 |
|
|
} |