1 |
|
|
/* $OpenBSD: ikev2_msg.c,v 1.45 2015/10/19 11:25:35 reyk Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org> |
5 |
|
|
* |
6 |
|
|
* Permission to use, copy, modify, and distribute this software for any |
7 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
8 |
|
|
* copyright notice and this permission notice appear in all copies. |
9 |
|
|
* |
10 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 |
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 |
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 |
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
15 |
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
16 |
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 |
|
|
*/ |
18 |
|
|
|
19 |
|
|
#include <sys/param.h> /* roundup */ |
20 |
|
|
#include <sys/queue.h> |
21 |
|
|
#include <sys/socket.h> |
22 |
|
|
#include <sys/wait.h> |
23 |
|
|
#include <sys/uio.h> |
24 |
|
|
|
25 |
|
|
#include <netinet/in.h> |
26 |
|
|
#include <arpa/inet.h> |
27 |
|
|
|
28 |
|
|
#include <stdlib.h> |
29 |
|
|
#include <stdio.h> |
30 |
|
|
#include <unistd.h> |
31 |
|
|
#include <string.h> |
32 |
|
|
#include <signal.h> |
33 |
|
|
#include <errno.h> |
34 |
|
|
#include <err.h> |
35 |
|
|
#include <pwd.h> |
36 |
|
|
#include <event.h> |
37 |
|
|
|
38 |
|
|
#include <openssl/sha.h> |
39 |
|
|
#include <openssl/evp.h> |
40 |
|
|
|
41 |
|
|
#include "iked.h" |
42 |
|
|
#include "ikev2.h" |
43 |
|
|
#include "eap.h" |
44 |
|
|
#include "dh.h" |
45 |
|
|
|
46 |
|
|
void ikev1_recv(struct iked *, struct iked_message *); |
47 |
|
|
void ikev2_msg_response_timeout(struct iked *, void *); |
48 |
|
|
void ikev2_msg_retransmit_timeout(struct iked *, void *); |
49 |
|
|
|
50 |
|
|
void |
51 |
|
|
ikev2_msg_cb(int fd, short event, void *arg) |
52 |
|
|
{ |
53 |
|
|
struct iked_socket *sock = arg; |
54 |
|
|
struct iked *env = sock->sock_env; |
55 |
|
|
struct iked_message msg; |
56 |
|
|
struct ike_header hdr; |
57 |
|
|
uint32_t natt = 0x00000000; |
58 |
|
|
uint8_t buf[IKED_MSGBUF_MAX]; |
59 |
|
|
ssize_t len; |
60 |
|
|
off_t off; |
61 |
|
|
|
62 |
|
|
bzero(&msg, sizeof(msg)); |
63 |
|
|
bzero(buf, sizeof(buf)); |
64 |
|
|
|
65 |
|
|
msg.msg_peerlen = sizeof(msg.msg_peer); |
66 |
|
|
msg.msg_locallen = sizeof(msg.msg_local); |
67 |
|
|
msg.msg_parent = &msg; |
68 |
|
|
memcpy(&msg.msg_local, &sock->sock_addr, sizeof(sock->sock_addr)); |
69 |
|
|
|
70 |
|
|
if ((len = recvfromto(fd, buf, sizeof(buf), 0, |
71 |
|
|
(struct sockaddr *)&msg.msg_peer, &msg.msg_peerlen, |
72 |
|
|
(struct sockaddr *)&msg.msg_local, &msg.msg_locallen)) < |
73 |
|
|
(ssize_t)sizeof(natt)) |
74 |
|
|
return; |
75 |
|
|
|
76 |
|
|
if (socket_getport((struct sockaddr *)&msg.msg_local) == |
77 |
|
|
IKED_NATT_PORT) { |
78 |
|
|
if (memcmp(&natt, buf, sizeof(natt)) != 0) |
79 |
|
|
return; |
80 |
|
|
msg.msg_natt = 1; |
81 |
|
|
off = sizeof(natt); |
82 |
|
|
} else |
83 |
|
|
off = 0; |
84 |
|
|
|
85 |
|
|
if ((size_t)(len - off) <= sizeof(hdr)) |
86 |
|
|
return; |
87 |
|
|
memcpy(&hdr, buf + off, sizeof(hdr)); |
88 |
|
|
|
89 |
|
|
if ((msg.msg_data = ibuf_new(buf + off, len - off)) == NULL) |
90 |
|
|
return; |
91 |
|
|
|
92 |
|
|
TAILQ_INIT(&msg.msg_proposals); |
93 |
|
|
msg.msg_fd = fd; |
94 |
|
|
|
95 |
|
|
if (hdr.ike_version == IKEV1_VERSION) |
96 |
|
|
ikev1_recv(env, &msg); |
97 |
|
|
else |
98 |
|
|
ikev2_recv(env, &msg); |
99 |
|
|
|
100 |
|
|
ikev2_msg_cleanup(env, &msg); |
101 |
|
|
} |
102 |
|
|
|
103 |
|
|
void |
104 |
|
|
ikev1_recv(struct iked *env, struct iked_message *msg) |
105 |
|
|
{ |
106 |
|
|
struct ike_header *hdr; |
107 |
|
|
|
108 |
|
|
if (ibuf_size(msg->msg_data) <= sizeof(*hdr)) { |
109 |
|
|
log_debug("%s: short message", __func__); |
110 |
|
|
return; |
111 |
|
|
} |
112 |
|
|
|
113 |
|
|
hdr = (struct ike_header *)ibuf_data(msg->msg_data); |
114 |
|
|
|
115 |
|
|
log_debug("%s: header ispi %s rspi %s" |
116 |
|
|
" nextpayload %u version 0x%02x exchange %u flags 0x%02x" |
117 |
|
|
" msgid %u length %u", __func__, |
118 |
|
|
print_spi(betoh64(hdr->ike_ispi), 8), |
119 |
|
|
print_spi(betoh64(hdr->ike_rspi), 8), |
120 |
|
|
hdr->ike_nextpayload, |
121 |
|
|
hdr->ike_version, |
122 |
|
|
hdr->ike_exchange, |
123 |
|
|
hdr->ike_flags, |
124 |
|
|
betoh32(hdr->ike_msgid), |
125 |
|
|
betoh32(hdr->ike_length)); |
126 |
|
|
|
127 |
|
|
log_debug("%s: IKEv1 not supported", __func__); |
128 |
|
|
} |
129 |
|
|
|
130 |
|
|
struct ibuf * |
131 |
|
|
ikev2_msg_init(struct iked *env, struct iked_message *msg, |
132 |
|
|
struct sockaddr_storage *peer, socklen_t peerlen, |
133 |
|
|
struct sockaddr_storage *local, socklen_t locallen, int response) |
134 |
|
|
{ |
135 |
|
|
bzero(msg, sizeof(*msg)); |
136 |
|
|
memcpy(&msg->msg_peer, peer, peerlen); |
137 |
|
|
msg->msg_peerlen = peerlen; |
138 |
|
|
memcpy(&msg->msg_local, local, locallen); |
139 |
|
|
msg->msg_locallen = locallen; |
140 |
|
|
msg->msg_response = response ? 1 : 0; |
141 |
|
|
msg->msg_fd = -1; |
142 |
|
|
msg->msg_data = ibuf_static(); |
143 |
|
|
msg->msg_e = 0; |
144 |
|
|
msg->msg_parent = msg; /* has to be set */ |
145 |
|
|
TAILQ_INIT(&msg->msg_proposals); |
146 |
|
|
|
147 |
|
|
return (msg->msg_data); |
148 |
|
|
} |
149 |
|
|
|
150 |
|
|
struct iked_message * |
151 |
|
|
ikev2_msg_copy(struct iked *env, struct iked_message *msg) |
152 |
|
|
{ |
153 |
|
|
struct iked_message *m = NULL; |
154 |
|
|
struct ibuf *buf; |
155 |
|
|
size_t len; |
156 |
|
|
void *ptr; |
157 |
|
|
|
158 |
|
|
if (ibuf_size(msg->msg_data) < msg->msg_offset) |
159 |
|
|
return (NULL); |
160 |
|
|
len = ibuf_size(msg->msg_data) - msg->msg_offset; |
161 |
|
|
|
162 |
|
|
if ((ptr = ibuf_seek(msg->msg_data, msg->msg_offset, len)) == NULL || |
163 |
|
|
(m = malloc(sizeof(*m))) == NULL || |
164 |
|
|
(buf = ikev2_msg_init(env, m, &msg->msg_peer, msg->msg_peerlen, |
165 |
|
|
&msg->msg_local, msg->msg_locallen, msg->msg_response)) == NULL || |
166 |
|
|
ibuf_add(buf, ptr, len)) |
167 |
|
|
return (NULL); |
168 |
|
|
|
169 |
|
|
m->msg_fd = msg->msg_fd; |
170 |
|
|
m->msg_msgid = msg->msg_msgid; |
171 |
|
|
m->msg_offset = msg->msg_offset; |
172 |
|
|
m->msg_sa = msg->msg_sa; |
173 |
|
|
|
174 |
|
|
return (m); |
175 |
|
|
} |
176 |
|
|
|
177 |
|
|
void |
178 |
|
|
ikev2_msg_cleanup(struct iked *env, struct iked_message *msg) |
179 |
|
|
{ |
180 |
|
|
if (msg == msg->msg_parent) { |
181 |
|
|
ibuf_release(msg->msg_nonce); |
182 |
|
|
ibuf_release(msg->msg_ke); |
183 |
|
|
ibuf_release(msg->msg_auth.id_buf); |
184 |
|
|
ibuf_release(msg->msg_id.id_buf); |
185 |
|
|
ibuf_release(msg->msg_cert.id_buf); |
186 |
|
|
|
187 |
|
|
config_free_proposals(&msg->msg_proposals, 0); |
188 |
|
|
} |
189 |
|
|
|
190 |
|
|
if (msg->msg_data != NULL) { |
191 |
|
|
ibuf_release(msg->msg_data); |
192 |
|
|
msg->msg_data = NULL; |
193 |
|
|
} |
194 |
|
|
} |
195 |
|
|
|
196 |
|
|
int |
197 |
|
|
ikev2_msg_valid_ike_sa(struct iked *env, struct ike_header *oldhdr, |
198 |
|
|
struct iked_message *msg) |
199 |
|
|
{ |
200 |
|
|
#if 0 |
201 |
|
|
/* XXX Disabled, see comment below */ |
202 |
|
|
struct iked_message resp; |
203 |
|
|
struct ike_header *hdr; |
204 |
|
|
struct ikev2_payload *pld; |
205 |
|
|
struct ikev2_notify *n; |
206 |
|
|
struct ibuf *buf; |
207 |
|
|
struct iked_sa sa; |
208 |
|
|
#endif |
209 |
|
|
|
210 |
|
|
if (msg->msg_sa != NULL && msg->msg_policy != NULL) { |
211 |
|
|
/* |
212 |
|
|
* Only permit informational requests from initiator |
213 |
|
|
* on closing SAs (for DELETE). |
214 |
|
|
*/ |
215 |
|
|
if (msg->msg_sa->sa_state == IKEV2_STATE_CLOSING) { |
216 |
|
|
if (((oldhdr->ike_flags & |
217 |
|
|
(IKEV2_FLAG_INITIATOR|IKEV2_FLAG_RESPONSE)) == |
218 |
|
|
IKEV2_FLAG_INITIATOR) && |
219 |
|
|
(oldhdr->ike_exchange == |
220 |
|
|
IKEV2_EXCHANGE_INFORMATIONAL)) |
221 |
|
|
return (0); |
222 |
|
|
return (-1); |
223 |
|
|
} |
224 |
|
|
return (0); |
225 |
|
|
} |
226 |
|
|
|
227 |
|
|
#if 0 |
228 |
|
|
/* |
229 |
|
|
* XXX Sending INVALID_IKE_SPIs notifications is disabled |
230 |
|
|
* XXX because it is not mandatory and ignored by most |
231 |
|
|
* XXX implementations. We might want to enable it in |
232 |
|
|
* XXX combination with a rate-limitation to avoid DoS situations. |
233 |
|
|
*/ |
234 |
|
|
|
235 |
|
|
/* Fail without error message */ |
236 |
|
|
if (msg->msg_response || msg->msg_policy == NULL) |
237 |
|
|
return (-1); |
238 |
|
|
|
239 |
|
|
/* Invalid IKE SA, return notification */ |
240 |
|
|
if ((buf = ikev2_msg_init(env, &resp, |
241 |
|
|
&msg->msg_peer, msg->msg_peerlen, |
242 |
|
|
&msg->msg_local, msg->msg_locallen, 1)) == NULL) |
243 |
|
|
goto done; |
244 |
|
|
|
245 |
|
|
resp.msg_fd = msg->msg_fd; |
246 |
|
|
|
247 |
|
|
bzero(&sa, sizeof(sa)); |
248 |
|
|
if ((oldhdr->ike_flags & IKEV2_FLAG_INITIATOR) == 0) |
249 |
|
|
sa.sa_hdr.sh_initiator = 1; |
250 |
|
|
sa.sa_hdr.sh_ispi = betoh64(oldhdr->ike_ispi); |
251 |
|
|
sa.sa_hdr.sh_rspi = betoh64(oldhdr->ike_rspi); |
252 |
|
|
|
253 |
|
|
resp.msg_msgid = betoh32(oldhdr->ike_msgid); |
254 |
|
|
|
255 |
|
|
/* IKE header */ |
256 |
|
|
if ((hdr = ikev2_add_header(buf, &sa, resp.msg_msgid, |
257 |
|
|
IKEV2_PAYLOAD_NOTIFY, IKEV2_EXCHANGE_INFORMATIONAL, |
258 |
|
|
IKEV2_FLAG_RESPONSE)) == NULL) |
259 |
|
|
goto done; |
260 |
|
|
|
261 |
|
|
/* SA payload */ |
262 |
|
|
if ((pld = ikev2_add_payload(buf)) == NULL) |
263 |
|
|
goto done; |
264 |
|
|
if ((n = ibuf_advance(buf, sizeof(*n))) == NULL) |
265 |
|
|
goto done; |
266 |
|
|
n->n_protoid = IKEV2_SAPROTO_IKE; |
267 |
|
|
n->n_spisize = 0; |
268 |
|
|
n->n_type = htobe16(IKEV2_N_INVALID_IKE_SPI); |
269 |
|
|
|
270 |
|
|
if (ikev2_next_payload(pld, sizeof(*n), IKEV2_PAYLOAD_NONE) == -1) |
271 |
|
|
goto done; |
272 |
|
|
|
273 |
|
|
if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1) |
274 |
|
|
goto done; |
275 |
|
|
|
276 |
|
|
(void)ikev2_pld_parse(env, hdr, &resp, 0); |
277 |
|
|
(void)ikev2_msg_send(env, &resp); |
278 |
|
|
|
279 |
|
|
done: |
280 |
|
|
ikev2_msg_cleanup(env, &resp); |
281 |
|
|
#endif |
282 |
|
|
|
283 |
|
|
/* Always fail */ |
284 |
|
|
return (-1); |
285 |
|
|
} |
286 |
|
|
|
287 |
|
|
int |
288 |
|
|
ikev2_msg_send(struct iked *env, struct iked_message *msg) |
289 |
|
|
{ |
290 |
|
|
struct iked_sa *sa = msg->msg_sa; |
291 |
|
|
struct ibuf *buf = msg->msg_data; |
292 |
|
|
uint32_t natt = 0x00000000; |
293 |
|
|
int isnatt = 0; |
294 |
|
|
uint8_t exchange, flags; |
295 |
|
|
struct ike_header *hdr; |
296 |
|
|
struct iked_message *m; |
297 |
|
|
|
298 |
|
|
if (buf == NULL || (hdr = ibuf_seek(msg->msg_data, |
299 |
|
|
msg->msg_offset, sizeof(*hdr))) == NULL) |
300 |
|
|
return (-1); |
301 |
|
|
|
302 |
|
|
isnatt = (msg->msg_natt || (msg->msg_sa && msg->msg_sa->sa_natt)); |
303 |
|
|
|
304 |
|
|
exchange = hdr->ike_exchange; |
305 |
|
|
flags = hdr->ike_flags; |
306 |
|
|
log_info("%s: %s %s from %s to %s msgid %u, %ld bytes%s", __func__, |
307 |
|
|
print_map(exchange, ikev2_exchange_map), |
308 |
|
|
(flags & IKEV2_FLAG_RESPONSE) ? "response" : "request", |
309 |
|
|
print_host((struct sockaddr *)&msg->msg_local, NULL, 0), |
310 |
|
|
print_host((struct sockaddr *)&msg->msg_peer, NULL, 0), |
311 |
|
|
betoh32(hdr->ike_msgid), |
312 |
|
|
ibuf_length(buf), isnatt ? ", NAT-T" : ""); |
313 |
|
|
|
314 |
|
|
if (isnatt) { |
315 |
|
|
if (ibuf_prepend(buf, &natt, sizeof(natt)) == -1) { |
316 |
|
|
log_debug("%s: failed to set NAT-T", __func__); |
317 |
|
|
return (-1); |
318 |
|
|
} |
319 |
|
|
msg->msg_offset += sizeof(natt); |
320 |
|
|
} |
321 |
|
|
|
322 |
|
|
if ((sendto(msg->msg_fd, ibuf_data(buf), ibuf_size(buf), 0, |
323 |
|
|
(struct sockaddr *)&msg->msg_peer, msg->msg_peerlen)) == -1) { |
324 |
|
|
log_warn("%s: sendto", __func__); |
325 |
|
|
return (-1); |
326 |
|
|
} |
327 |
|
|
|
328 |
|
|
if (!sa) |
329 |
|
|
return (0); |
330 |
|
|
|
331 |
|
|
if ((m = ikev2_msg_copy(env, msg)) == NULL) { |
332 |
|
|
log_debug("%s: failed to copy a message", __func__); |
333 |
|
|
return (-1); |
334 |
|
|
} |
335 |
|
|
m->msg_exchange = exchange; |
336 |
|
|
|
337 |
|
|
if (flags & IKEV2_FLAG_RESPONSE) { |
338 |
|
|
TAILQ_INSERT_TAIL(&sa->sa_responses, m, msg_entry); |
339 |
|
|
timer_set(env, &m->msg_timer, ikev2_msg_response_timeout, m); |
340 |
|
|
timer_add(env, &m->msg_timer, IKED_RESPONSE_TIMEOUT); |
341 |
|
|
} else { |
342 |
|
|
TAILQ_INSERT_TAIL(&sa->sa_requests, m, msg_entry); |
343 |
|
|
timer_set(env, &m->msg_timer, ikev2_msg_retransmit_timeout, m); |
344 |
|
|
timer_add(env, &m->msg_timer, IKED_RETRANSMIT_TIMEOUT); |
345 |
|
|
} |
346 |
|
|
|
347 |
|
|
return (0); |
348 |
|
|
} |
349 |
|
|
|
350 |
|
|
uint32_t |
351 |
|
|
ikev2_msg_id(struct iked *env, struct iked_sa *sa) |
352 |
|
|
{ |
353 |
|
|
uint32_t id = sa->sa_reqid; |
354 |
|
|
|
355 |
|
|
if (++sa->sa_reqid == UINT32_MAX) { |
356 |
|
|
/* XXX we should close and renegotiate the connection now */ |
357 |
|
|
log_debug("%s: IKEv2 message sequence overflow", __func__); |
358 |
|
|
} |
359 |
|
|
return (id); |
360 |
|
|
} |
361 |
|
|
|
362 |
|
|
struct ibuf * |
363 |
|
|
ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src) |
364 |
|
|
{ |
365 |
|
|
size_t len, ivlen, encrlen, integrlen, blocklen, |
366 |
|
|
outlen; |
367 |
|
|
uint8_t *buf, pad = 0, *ptr; |
368 |
|
|
struct ibuf *encr, *dst = NULL, *out = NULL; |
369 |
|
|
|
370 |
|
|
buf = ibuf_data(src); |
371 |
|
|
len = ibuf_size(src); |
372 |
|
|
|
373 |
|
|
log_debug("%s: decrypted length %zu", __func__, len); |
374 |
|
|
print_hex(buf, 0, len); |
375 |
|
|
|
376 |
|
|
if (sa == NULL || |
377 |
|
|
sa->sa_encr == NULL || |
378 |
|
|
sa->sa_integr == NULL) { |
379 |
|
|
log_debug("%s: invalid SA", __func__); |
380 |
|
|
goto done; |
381 |
|
|
} |
382 |
|
|
|
383 |
|
|
if (sa->sa_hdr.sh_initiator) |
384 |
|
|
encr = sa->sa_key_iencr; |
385 |
|
|
else |
386 |
|
|
encr = sa->sa_key_rencr; |
387 |
|
|
|
388 |
|
|
blocklen = cipher_length(sa->sa_encr); |
389 |
|
|
ivlen = cipher_ivlength(sa->sa_encr); |
390 |
|
|
integrlen = hash_length(sa->sa_integr); |
391 |
|
|
encrlen = roundup(len + sizeof(pad), blocklen); |
392 |
|
|
pad = encrlen - (len + sizeof(pad)); |
393 |
|
|
|
394 |
|
|
/* |
395 |
|
|
* Pad the payload and encrypt it |
396 |
|
|
*/ |
397 |
|
|
if (pad) { |
398 |
|
|
if ((ptr = ibuf_advance(src, pad)) == NULL) |
399 |
|
|
goto done; |
400 |
|
|
arc4random_buf(ptr, pad); |
401 |
|
|
} |
402 |
|
|
if (ibuf_add(src, &pad, sizeof(pad)) != 0) |
403 |
|
|
goto done; |
404 |
|
|
|
405 |
|
|
log_debug("%s: padded length %zu", __func__, ibuf_size(src)); |
406 |
|
|
print_hex(ibuf_data(src), 0, ibuf_size(src)); |
407 |
|
|
|
408 |
|
|
cipher_setkey(sa->sa_encr, encr->buf, ibuf_length(encr)); |
409 |
|
|
cipher_setiv(sa->sa_encr, NULL, 0); /* XXX ivlen */ |
410 |
|
|
cipher_init_encrypt(sa->sa_encr); |
411 |
|
|
|
412 |
|
|
if ((dst = ibuf_dup(sa->sa_encr->encr_iv)) == NULL) |
413 |
|
|
goto done; |
414 |
|
|
|
415 |
|
|
if ((out = ibuf_new(NULL, |
416 |
|
|
cipher_outlength(sa->sa_encr, encrlen))) == NULL) |
417 |
|
|
goto done; |
418 |
|
|
|
419 |
|
|
outlen = ibuf_size(out); |
420 |
|
|
cipher_update(sa->sa_encr, |
421 |
|
|
ibuf_data(src), encrlen, ibuf_data(out), &outlen); |
422 |
|
|
|
423 |
|
|
if (outlen && ibuf_add(dst, ibuf_data(out), outlen) != 0) |
424 |
|
|
goto done; |
425 |
|
|
|
426 |
|
|
if ((ptr = ibuf_advance(dst, integrlen)) == NULL) |
427 |
|
|
goto done; |
428 |
|
|
explicit_bzero(ptr, integrlen); |
429 |
|
|
|
430 |
|
|
log_debug("%s: length %zu, padding %d, output length %zu", |
431 |
|
|
__func__, len + sizeof(pad), pad, ibuf_size(dst)); |
432 |
|
|
print_hex(ibuf_data(dst), 0, ibuf_size(dst)); |
433 |
|
|
|
434 |
|
|
ibuf_release(src); |
435 |
|
|
ibuf_release(out); |
436 |
|
|
return (dst); |
437 |
|
|
done: |
438 |
|
|
ibuf_release(src); |
439 |
|
|
ibuf_release(out); |
440 |
|
|
ibuf_release(dst); |
441 |
|
|
return (NULL); |
442 |
|
|
} |
443 |
|
|
|
444 |
|
|
int |
445 |
|
|
ikev2_msg_integr(struct iked *env, struct iked_sa *sa, struct ibuf *src) |
446 |
|
|
{ |
447 |
|
|
int ret = -1; |
448 |
|
|
size_t integrlen, tmplen; |
449 |
|
|
struct ibuf *integr, *tmp = NULL; |
450 |
|
|
uint8_t *ptr; |
451 |
|
|
|
452 |
|
|
log_debug("%s: message length %zu", __func__, ibuf_size(src)); |
453 |
|
|
print_hex(ibuf_data(src), 0, ibuf_size(src)); |
454 |
|
|
|
455 |
|
|
if (sa == NULL || |
456 |
|
|
sa->sa_integr == NULL) { |
457 |
|
|
log_debug("%s: invalid SA", __func__); |
458 |
|
|
return (-1); |
459 |
|
|
} |
460 |
|
|
|
461 |
|
|
if (sa->sa_hdr.sh_initiator) |
462 |
|
|
integr = sa->sa_key_iauth; |
463 |
|
|
else |
464 |
|
|
integr = sa->sa_key_rauth; |
465 |
|
|
|
466 |
|
|
integrlen = hash_length(sa->sa_integr); |
467 |
|
|
|
468 |
|
|
log_debug("%s: integrity checksum length %zu", __func__, |
469 |
|
|
integrlen); |
470 |
|
|
|
471 |
|
|
/* |
472 |
|
|
* Validate packet checksum |
473 |
|
|
*/ |
474 |
|
|
if ((tmp = ibuf_new(NULL, hash_keylength(sa->sa_integr))) == NULL) |
475 |
|
|
goto done; |
476 |
|
|
|
477 |
|
|
hash_setkey(sa->sa_integr, ibuf_data(integr), ibuf_size(integr)); |
478 |
|
|
hash_init(sa->sa_integr); |
479 |
|
|
hash_update(sa->sa_integr, ibuf_data(src), |
480 |
|
|
ibuf_size(src) - integrlen); |
481 |
|
|
hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen); |
482 |
|
|
|
483 |
|
|
if (tmplen != integrlen) { |
484 |
|
|
log_debug("%s: hash failure", __func__); |
485 |
|
|
goto done; |
486 |
|
|
} |
487 |
|
|
|
488 |
|
|
if ((ptr = ibuf_seek(src, |
489 |
|
|
ibuf_size(src) - integrlen, integrlen)) == NULL) |
490 |
|
|
goto done; |
491 |
|
|
memcpy(ptr, ibuf_data(tmp), tmplen); |
492 |
|
|
|
493 |
|
|
print_hex(ibuf_data(tmp), 0, ibuf_size(tmp)); |
494 |
|
|
|
495 |
|
|
ret = 0; |
496 |
|
|
done: |
497 |
|
|
ibuf_release(tmp); |
498 |
|
|
|
499 |
|
|
return (ret); |
500 |
|
|
} |
501 |
|
|
|
502 |
|
|
struct ibuf * |
503 |
|
|
ikev2_msg_decrypt(struct iked *env, struct iked_sa *sa, |
504 |
|
|
struct ibuf *msg, struct ibuf *src) |
505 |
|
|
{ |
506 |
|
|
ssize_t ivlen, encrlen, integrlen, blocklen, |
507 |
|
|
outlen, tmplen; |
508 |
|
|
uint8_t pad = 0, *ptr; |
509 |
|
|
struct ibuf *integr, *encr, *tmp = NULL, *out = NULL; |
510 |
|
|
off_t ivoff, encroff, integroff; |
511 |
|
|
|
512 |
|
|
if (sa == NULL || |
513 |
|
|
sa->sa_encr == NULL || |
514 |
|
|
sa->sa_integr == NULL) { |
515 |
|
|
log_debug("%s: invalid SA", __func__); |
516 |
|
|
print_hex(ibuf_data(src), 0, ibuf_size(src)); |
517 |
|
|
goto done; |
518 |
|
|
} |
519 |
|
|
|
520 |
|
|
if (!sa->sa_hdr.sh_initiator) { |
521 |
|
|
encr = sa->sa_key_iencr; |
522 |
|
|
integr = sa->sa_key_iauth; |
523 |
|
|
} else { |
524 |
|
|
encr = sa->sa_key_rencr; |
525 |
|
|
integr = sa->sa_key_rauth; |
526 |
|
|
} |
527 |
|
|
|
528 |
|
|
blocklen = cipher_length(sa->sa_encr); |
529 |
|
|
ivlen = cipher_ivlength(sa->sa_encr); |
530 |
|
|
ivoff = 0; |
531 |
|
|
integrlen = hash_length(sa->sa_integr); |
532 |
|
|
integroff = ibuf_size(src) - integrlen; |
533 |
|
|
encroff = ivlen; |
534 |
|
|
encrlen = ibuf_size(src) - integrlen - ivlen; |
535 |
|
|
|
536 |
|
|
if (encrlen < 0 || integroff < 0) { |
537 |
|
|
log_debug("%s: invalid integrity value", __func__); |
538 |
|
|
goto done; |
539 |
|
|
} |
540 |
|
|
|
541 |
|
|
log_debug("%s: IV length %zd", __func__, ivlen); |
542 |
|
|
print_hex(ibuf_data(src), 0, ivlen); |
543 |
|
|
log_debug("%s: encrypted payload length %zd", __func__, encrlen); |
544 |
|
|
print_hex(ibuf_data(src), encroff, encrlen); |
545 |
|
|
log_debug("%s: integrity checksum length %zd", __func__, integrlen); |
546 |
|
|
print_hex(ibuf_data(src), integroff, integrlen); |
547 |
|
|
|
548 |
|
|
/* |
549 |
|
|
* Validate packet checksum |
550 |
|
|
*/ |
551 |
|
|
if ((tmp = ibuf_new(NULL, ibuf_length(integr))) == NULL) |
552 |
|
|
goto done; |
553 |
|
|
|
554 |
|
|
hash_setkey(sa->sa_integr, integr->buf, ibuf_length(integr)); |
555 |
|
|
hash_init(sa->sa_integr); |
556 |
|
|
hash_update(sa->sa_integr, ibuf_data(msg), |
557 |
|
|
ibuf_size(msg) - integrlen); |
558 |
|
|
hash_final(sa->sa_integr, tmp->buf, &tmplen); |
559 |
|
|
|
560 |
|
|
if (memcmp(tmp->buf, ibuf_data(src) + integroff, integrlen) != 0) { |
561 |
|
|
log_debug("%s: integrity check failed", __func__); |
562 |
|
|
goto done; |
563 |
|
|
} |
564 |
|
|
|
565 |
|
|
log_debug("%s: integrity check succeeded", __func__); |
566 |
|
|
print_hex(tmp->buf, 0, tmplen); |
567 |
|
|
|
568 |
|
|
ibuf_release(tmp); |
569 |
|
|
tmp = NULL; |
570 |
|
|
|
571 |
|
|
/* |
572 |
|
|
* Decrypt the payload and strip any padding |
573 |
|
|
*/ |
574 |
|
|
if ((encrlen % blocklen) != 0) { |
575 |
|
|
log_debug("%s: unaligned encrypted payload", __func__); |
576 |
|
|
goto done; |
577 |
|
|
} |
578 |
|
|
|
579 |
|
|
cipher_setkey(sa->sa_encr, encr->buf, ibuf_length(encr)); |
580 |
|
|
cipher_setiv(sa->sa_encr, ibuf_data(src) + ivoff, ivlen); |
581 |
|
|
cipher_init_decrypt(sa->sa_encr); |
582 |
|
|
|
583 |
|
|
if ((out = ibuf_new(NULL, cipher_outlength(sa->sa_encr, |
584 |
|
|
encrlen))) == NULL) |
585 |
|
|
goto done; |
586 |
|
|
|
587 |
|
|
if ((outlen = ibuf_length(out)) != 0) { |
588 |
|
|
cipher_update(sa->sa_encr, ibuf_data(src) + encroff, encrlen, |
589 |
|
|
ibuf_data(out), &outlen); |
590 |
|
|
|
591 |
|
|
ptr = ibuf_seek(out, outlen - 1, 1); |
592 |
|
|
pad = *ptr; |
593 |
|
|
} |
594 |
|
|
|
595 |
|
|
log_debug("%s: decrypted payload length %zd/%zd padding %d", |
596 |
|
|
__func__, outlen, encrlen, pad); |
597 |
|
|
print_hex(ibuf_data(out), 0, ibuf_size(out)); |
598 |
|
|
|
599 |
|
|
if (ibuf_setsize(out, outlen) != 0) |
600 |
|
|
goto done; |
601 |
|
|
|
602 |
|
|
ibuf_release(src); |
603 |
|
|
return (out); |
604 |
|
|
done: |
605 |
|
|
ibuf_release(tmp); |
606 |
|
|
ibuf_release(out); |
607 |
|
|
ibuf_release(src); |
608 |
|
|
return (NULL); |
609 |
|
|
} |
610 |
|
|
|
611 |
|
|
int |
612 |
|
|
ikev2_msg_send_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf **ep, |
613 |
|
|
uint8_t exchange, uint8_t firstpayload, int response) |
614 |
|
|
{ |
615 |
|
|
struct iked_message resp; |
616 |
|
|
struct ike_header *hdr; |
617 |
|
|
struct ikev2_payload *pld; |
618 |
|
|
struct ibuf *buf, *e = *ep; |
619 |
|
|
int ret = -1; |
620 |
|
|
|
621 |
|
|
if ((buf = ikev2_msg_init(env, &resp, &sa->sa_peer.addr, |
622 |
|
|
sa->sa_peer.addr.ss_len, &sa->sa_local.addr, |
623 |
|
|
sa->sa_local.addr.ss_len, response)) == NULL) |
624 |
|
|
goto done; |
625 |
|
|
|
626 |
|
|
resp.msg_msgid = response ? sa->sa_msgid : ikev2_msg_id(env, sa); |
627 |
|
|
|
628 |
|
|
/* IKE header */ |
629 |
|
|
if ((hdr = ikev2_add_header(buf, sa, resp.msg_msgid, IKEV2_PAYLOAD_SK, |
630 |
|
|
exchange, response ? IKEV2_FLAG_RESPONSE : 0)) == NULL) |
631 |
|
|
goto done; |
632 |
|
|
|
633 |
|
|
if ((pld = ikev2_add_payload(buf)) == NULL) |
634 |
|
|
goto done; |
635 |
|
|
|
636 |
|
|
/* Encrypt message and add as an E payload */ |
637 |
|
|
if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) { |
638 |
|
|
log_debug("%s: encryption failed", __func__); |
639 |
|
|
goto done; |
640 |
|
|
} |
641 |
|
|
if (ibuf_cat(buf, e) != 0) |
642 |
|
|
goto done; |
643 |
|
|
if (ikev2_next_payload(pld, ibuf_size(e), firstpayload) == -1) |
644 |
|
|
goto done; |
645 |
|
|
|
646 |
|
|
if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1) |
647 |
|
|
goto done; |
648 |
|
|
|
649 |
|
|
/* Add integrity checksum (HMAC) */ |
650 |
|
|
if (ikev2_msg_integr(env, sa, buf) != 0) { |
651 |
|
|
log_debug("%s: integrity checksum failed", __func__); |
652 |
|
|
goto done; |
653 |
|
|
} |
654 |
|
|
|
655 |
|
|
resp.msg_data = buf; |
656 |
|
|
resp.msg_sa = sa; |
657 |
|
|
resp.msg_fd = sa->sa_fd; |
658 |
|
|
TAILQ_INIT(&resp.msg_proposals); |
659 |
|
|
|
660 |
|
|
(void)ikev2_pld_parse(env, hdr, &resp, 0); |
661 |
|
|
|
662 |
|
|
ret = ikev2_msg_send(env, &resp); |
663 |
|
|
|
664 |
|
|
done: |
665 |
|
|
/* e is cleaned up by the calling function */ |
666 |
|
|
*ep = e; |
667 |
|
|
ikev2_msg_cleanup(env, &resp); |
668 |
|
|
|
669 |
|
|
return (ret); |
670 |
|
|
} |
671 |
|
|
|
672 |
|
|
struct ibuf * |
673 |
|
|
ikev2_msg_auth(struct iked *env, struct iked_sa *sa, int response) |
674 |
|
|
{ |
675 |
|
|
struct ibuf *authmsg = NULL, *nonce, *prfkey, *buf; |
676 |
|
|
uint8_t *ptr; |
677 |
|
|
struct iked_id *id; |
678 |
|
|
size_t tmplen; |
679 |
|
|
|
680 |
|
|
/* |
681 |
|
|
* Create the payload to be signed/MAC'ed for AUTH |
682 |
|
|
*/ |
683 |
|
|
|
684 |
|
|
if (!response) { |
685 |
|
|
if ((nonce = sa->sa_rnonce) == NULL || |
686 |
|
|
(sa->sa_iid.id_type == 0) || |
687 |
|
|
(prfkey = sa->sa_key_iprf) == NULL || |
688 |
|
|
(buf = sa->sa_1stmsg) == NULL) |
689 |
|
|
return (NULL); |
690 |
|
|
id = &sa->sa_iid; |
691 |
|
|
} else { |
692 |
|
|
if ((nonce = sa->sa_inonce) == NULL || |
693 |
|
|
(sa->sa_rid.id_type == 0) || |
694 |
|
|
(prfkey = sa->sa_key_rprf) == NULL || |
695 |
|
|
(buf = sa->sa_2ndmsg) == NULL) |
696 |
|
|
return (NULL); |
697 |
|
|
id = &sa->sa_rid; |
698 |
|
|
} |
699 |
|
|
|
700 |
|
|
if ((authmsg = ibuf_dup(buf)) == NULL) |
701 |
|
|
return (NULL); |
702 |
|
|
if (ibuf_cat(authmsg, nonce) != 0) |
703 |
|
|
goto fail; |
704 |
|
|
|
705 |
|
|
if ((hash_setkey(sa->sa_prf, ibuf_data(prfkey), |
706 |
|
|
ibuf_size(prfkey))) == NULL) |
707 |
|
|
goto fail; |
708 |
|
|
|
709 |
|
|
if ((ptr = ibuf_advance(authmsg, |
710 |
|
|
hash_length(sa->sa_prf))) == NULL) |
711 |
|
|
goto fail; |
712 |
|
|
|
713 |
|
|
hash_init(sa->sa_prf); |
714 |
|
|
hash_update(sa->sa_prf, ibuf_data(id->id_buf), ibuf_size(id->id_buf)); |
715 |
|
|
hash_final(sa->sa_prf, ptr, &tmplen); |
716 |
|
|
|
717 |
|
|
if (tmplen != hash_length(sa->sa_prf)) |
718 |
|
|
goto fail; |
719 |
|
|
|
720 |
|
|
log_debug("%s: %s auth data length %zu", |
721 |
|
|
__func__, response ? "responder" : "initiator", |
722 |
|
|
ibuf_size(authmsg)); |
723 |
|
|
print_hex(ibuf_data(authmsg), 0, ibuf_size(authmsg)); |
724 |
|
|
|
725 |
|
|
return (authmsg); |
726 |
|
|
|
727 |
|
|
fail: |
728 |
|
|
ibuf_release(authmsg); |
729 |
|
|
return (NULL); |
730 |
|
|
} |
731 |
|
|
|
732 |
|
|
int |
733 |
|
|
ikev2_msg_authverify(struct iked *env, struct iked_sa *sa, |
734 |
|
|
struct iked_auth *auth, uint8_t *buf, size_t len, struct ibuf *authmsg) |
735 |
|
|
{ |
736 |
|
|
uint8_t *key, *psk = NULL; |
737 |
|
|
ssize_t keylen; |
738 |
|
|
struct iked_id *id; |
739 |
|
|
struct iked_dsa *dsa = NULL; |
740 |
|
|
int ret = -1; |
741 |
|
|
uint8_t keytype; |
742 |
|
|
|
743 |
|
|
if (sa->sa_hdr.sh_initiator) |
744 |
|
|
id = &sa->sa_rcert; |
745 |
|
|
else |
746 |
|
|
id = &sa->sa_icert; |
747 |
|
|
|
748 |
|
|
if ((dsa = dsa_verify_new(auth->auth_method, sa->sa_prf)) == NULL) { |
749 |
|
|
log_debug("%s: invalid auth method", __func__); |
750 |
|
|
return (-1); |
751 |
|
|
} |
752 |
|
|
|
753 |
|
|
switch (auth->auth_method) { |
754 |
|
|
case IKEV2_AUTH_SHARED_KEY_MIC: |
755 |
|
|
if (!auth->auth_length) { |
756 |
|
|
log_debug("%s: no pre-shared key found", __func__); |
757 |
|
|
goto done; |
758 |
|
|
} |
759 |
|
|
if ((keylen = ikev2_psk(sa, auth->auth_data, |
760 |
|
|
auth->auth_length, &psk)) == -1) { |
761 |
|
|
log_debug("%s: failed to get PSK", __func__); |
762 |
|
|
goto done; |
763 |
|
|
} |
764 |
|
|
key = psk; |
765 |
|
|
keytype = 0; |
766 |
|
|
break; |
767 |
|
|
default: |
768 |
|
|
if (!id->id_type || !ibuf_length(id->id_buf)) { |
769 |
|
|
log_debug("%s: no cert found", __func__); |
770 |
|
|
goto done; |
771 |
|
|
} |
772 |
|
|
key = ibuf_data(id->id_buf); |
773 |
|
|
keylen = ibuf_size(id->id_buf); |
774 |
|
|
keytype = id->id_type; |
775 |
|
|
break; |
776 |
|
|
} |
777 |
|
|
|
778 |
|
|
log_debug("%s: method %s keylen %zd type %s", __func__, |
779 |
|
|
print_map(auth->auth_method, ikev2_auth_map), keylen, |
780 |
|
|
print_map(id->id_type, ikev2_cert_map)); |
781 |
|
|
|
782 |
|
|
if (dsa_setkey(dsa, key, keylen, keytype) == NULL || |
783 |
|
|
dsa_init(dsa, buf, len) != 0 || |
784 |
|
|
dsa_update(dsa, ibuf_data(authmsg), ibuf_size(authmsg))) { |
785 |
|
|
log_debug("%s: failed to compute digital signature", __func__); |
786 |
|
|
goto done; |
787 |
|
|
} |
788 |
|
|
|
789 |
|
|
if ((ret = dsa_verify_final(dsa, buf, len)) == 0) { |
790 |
|
|
log_debug("%s: authentication successful", __func__); |
791 |
|
|
sa_state(env, sa, IKEV2_STATE_AUTH_SUCCESS); |
792 |
|
|
sa_stateflags(sa, IKED_REQ_AUTHVALID); |
793 |
|
|
} else { |
794 |
|
|
log_debug("%s: authentication failed", __func__); |
795 |
|
|
sa_state(env, sa, IKEV2_STATE_AUTH_REQUEST); |
796 |
|
|
} |
797 |
|
|
|
798 |
|
|
done: |
799 |
|
|
free(psk); |
800 |
|
|
dsa_free(dsa); |
801 |
|
|
|
802 |
|
|
return (ret); |
803 |
|
|
} |
804 |
|
|
|
805 |
|
|
int |
806 |
|
|
ikev2_msg_authsign(struct iked *env, struct iked_sa *sa, |
807 |
|
|
struct iked_auth *auth, struct ibuf *authmsg) |
808 |
|
|
{ |
809 |
|
|
uint8_t *key, *psk = NULL; |
810 |
|
|
ssize_t keylen; |
811 |
|
|
struct iked_hash *prf = sa->sa_prf; |
812 |
|
|
struct iked_id *id; |
813 |
|
|
struct iked_dsa *dsa = NULL; |
814 |
|
|
struct ibuf *buf; |
815 |
|
|
int ret = -1; |
816 |
|
|
uint8_t keytype; |
817 |
|
|
|
818 |
|
|
if (sa->sa_hdr.sh_initiator) |
819 |
|
|
id = &sa->sa_icert; |
820 |
|
|
else |
821 |
|
|
id = &sa->sa_rcert; |
822 |
|
|
|
823 |
|
|
if ((dsa = dsa_sign_new(auth->auth_method, prf)) == NULL) { |
824 |
|
|
log_debug("%s: invalid auth method", __func__); |
825 |
|
|
return (-1); |
826 |
|
|
} |
827 |
|
|
|
828 |
|
|
switch (auth->auth_method) { |
829 |
|
|
case IKEV2_AUTH_SHARED_KEY_MIC: |
830 |
|
|
if (!auth->auth_length) { |
831 |
|
|
log_debug("%s: no pre-shared key found", __func__); |
832 |
|
|
goto done; |
833 |
|
|
} |
834 |
|
|
if ((keylen = ikev2_psk(sa, auth->auth_data, |
835 |
|
|
auth->auth_length, &psk)) == -1) { |
836 |
|
|
log_debug("%s: failed to get PSK", __func__); |
837 |
|
|
goto done; |
838 |
|
|
} |
839 |
|
|
key = psk; |
840 |
|
|
keytype = 0; |
841 |
|
|
break; |
842 |
|
|
default: |
843 |
|
|
if (id == NULL) { |
844 |
|
|
log_debug("%s: no cert found", __func__); |
845 |
|
|
goto done; |
846 |
|
|
} |
847 |
|
|
key = ibuf_data(id->id_buf); |
848 |
|
|
keylen = ibuf_size(id->id_buf); |
849 |
|
|
keytype = id->id_type; |
850 |
|
|
break; |
851 |
|
|
} |
852 |
|
|
|
853 |
|
|
if (dsa_setkey(dsa, key, keylen, keytype) == NULL || |
854 |
|
|
dsa_init(dsa, NULL, 0) != 0 || |
855 |
|
|
dsa_update(dsa, ibuf_data(authmsg), ibuf_size(authmsg))) { |
856 |
|
|
log_debug("%s: failed to compute digital signature", __func__); |
857 |
|
|
goto done; |
858 |
|
|
} |
859 |
|
|
|
860 |
|
|
ibuf_release(sa->sa_localauth.id_buf); |
861 |
|
|
sa->sa_localauth.id_buf = NULL; |
862 |
|
|
|
863 |
|
|
if ((buf = ibuf_new(NULL, dsa_length(dsa))) == NULL) { |
864 |
|
|
log_debug("%s: failed to get auth buffer", __func__); |
865 |
|
|
goto done; |
866 |
|
|
} |
867 |
|
|
|
868 |
|
|
if ((ret = dsa_sign_final(dsa, |
869 |
|
|
ibuf_data(buf), ibuf_size(buf))) == -1) { |
870 |
|
|
log_debug("%s: failed to create auth signature", __func__); |
871 |
|
|
ibuf_release(buf); |
872 |
|
|
goto done; |
873 |
|
|
} |
874 |
|
|
|
875 |
|
|
sa->sa_localauth.id_type = auth->auth_method; |
876 |
|
|
sa->sa_localauth.id_buf = buf; |
877 |
|
|
|
878 |
|
|
ret = 0; |
879 |
|
|
done: |
880 |
|
|
free(psk); |
881 |
|
|
dsa_free(dsa); |
882 |
|
|
|
883 |
|
|
return (ret); |
884 |
|
|
} |
885 |
|
|
|
886 |
|
|
int |
887 |
|
|
ikev2_msg_frompeer(struct iked_message *msg) |
888 |
|
|
{ |
889 |
|
|
struct iked_sa *sa = msg->msg_sa; |
890 |
|
|
struct ike_header *hdr; |
891 |
|
|
|
892 |
|
|
msg = msg->msg_parent; |
893 |
|
|
|
894 |
|
|
if (sa == NULL || |
895 |
|
|
(hdr = ibuf_seek(msg->msg_data, 0, sizeof(*hdr))) == NULL) |
896 |
|
|
return (0); |
897 |
|
|
|
898 |
|
|
if (!sa->sa_hdr.sh_initiator && |
899 |
|
|
(hdr->ike_flags & IKEV2_FLAG_INITIATOR)) |
900 |
|
|
return (1); |
901 |
|
|
else if (sa->sa_hdr.sh_initiator && |
902 |
|
|
(hdr->ike_flags & IKEV2_FLAG_INITIATOR) == 0) |
903 |
|
|
return (1); |
904 |
|
|
|
905 |
|
|
return (0); |
906 |
|
|
} |
907 |
|
|
|
908 |
|
|
struct iked_socket * |
909 |
|
|
ikev2_msg_getsocket(struct iked *env, int af, int natt) |
910 |
|
|
{ |
911 |
|
|
switch (af) { |
912 |
|
|
case AF_INET: |
913 |
|
|
return (env->sc_sock4[natt ? 1 : 0]); |
914 |
|
|
case AF_INET6: |
915 |
|
|
return (env->sc_sock6[natt ? 1 : 0]); |
916 |
|
|
} |
917 |
|
|
|
918 |
|
|
log_debug("%s: af socket %d not available", __func__, af); |
919 |
|
|
return (NULL); |
920 |
|
|
} |
921 |
|
|
|
922 |
|
|
void |
923 |
|
|
ikev2_msg_prevail(struct iked *env, struct iked_msgqueue *queue, |
924 |
|
|
struct iked_message *msg) |
925 |
|
|
{ |
926 |
|
|
struct iked_message *m, *mtmp; |
927 |
|
|
|
928 |
|
|
TAILQ_FOREACH_SAFE(m, queue, msg_entry, mtmp) { |
929 |
|
|
if (m->msg_msgid < msg->msg_msgid) |
930 |
|
|
ikev2_msg_dispose(env, queue, m); |
931 |
|
|
} |
932 |
|
|
} |
933 |
|
|
|
934 |
|
|
void |
935 |
|
|
ikev2_msg_dispose(struct iked *env, struct iked_msgqueue *queue, |
936 |
|
|
struct iked_message *msg) |
937 |
|
|
{ |
938 |
|
|
TAILQ_REMOVE(queue, msg, msg_entry); |
939 |
|
|
timer_del(env, &msg->msg_timer); |
940 |
|
|
ikev2_msg_cleanup(env, msg); |
941 |
|
|
free(msg); |
942 |
|
|
} |
943 |
|
|
|
944 |
|
|
void |
945 |
|
|
ikev2_msg_flushqueue(struct iked *env, struct iked_msgqueue *queue) |
946 |
|
|
{ |
947 |
|
|
struct iked_message *m = NULL; |
948 |
|
|
|
949 |
|
|
while ((m = TAILQ_FIRST(queue)) != NULL) |
950 |
|
|
ikev2_msg_dispose(env, queue, m); |
951 |
|
|
} |
952 |
|
|
|
953 |
|
|
struct iked_message * |
954 |
|
|
ikev2_msg_lookup(struct iked *env, struct iked_msgqueue *queue, |
955 |
|
|
struct iked_message *msg, struct ike_header *hdr) |
956 |
|
|
{ |
957 |
|
|
struct iked_message *m = NULL; |
958 |
|
|
|
959 |
|
|
TAILQ_FOREACH(m, queue, msg_entry) { |
960 |
|
|
if (m->msg_msgid == msg->msg_msgid && |
961 |
|
|
m->msg_exchange == hdr->ike_exchange) |
962 |
|
|
break; |
963 |
|
|
} |
964 |
|
|
|
965 |
|
|
return (m); |
966 |
|
|
} |
967 |
|
|
|
968 |
|
|
int |
969 |
|
|
ikev2_msg_retransmit_response(struct iked *env, struct iked_sa *sa, |
970 |
|
|
struct iked_message *msg) |
971 |
|
|
{ |
972 |
|
|
if ((sendto(msg->msg_fd, ibuf_data(msg->msg_data), |
973 |
|
|
ibuf_size(msg->msg_data), 0, (struct sockaddr *)&msg->msg_peer, |
974 |
|
|
msg->msg_peerlen)) == -1) { |
975 |
|
|
log_warn("%s: sendto", __func__); |
976 |
|
|
return (-1); |
977 |
|
|
} |
978 |
|
|
|
979 |
|
|
timer_add(env, &msg->msg_timer, IKED_RESPONSE_TIMEOUT); |
980 |
|
|
return (0); |
981 |
|
|
} |
982 |
|
|
|
983 |
|
|
void |
984 |
|
|
ikev2_msg_response_timeout(struct iked *env, void *arg) |
985 |
|
|
{ |
986 |
|
|
struct iked_message *msg = arg; |
987 |
|
|
struct iked_sa *sa = msg->msg_sa; |
988 |
|
|
|
989 |
|
|
ikev2_msg_dispose(env, &sa->sa_responses, msg); |
990 |
|
|
} |
991 |
|
|
|
992 |
|
|
void |
993 |
|
|
ikev2_msg_retransmit_timeout(struct iked *env, void *arg) |
994 |
|
|
{ |
995 |
|
|
struct iked_message *msg = arg; |
996 |
|
|
struct iked_sa *sa = msg->msg_sa; |
997 |
|
|
|
998 |
|
|
if (msg->msg_tries < IKED_RETRANSMIT_TRIES) { |
999 |
|
|
if ((sendto(msg->msg_fd, ibuf_data(msg->msg_data), |
1000 |
|
|
ibuf_size(msg->msg_data), 0, |
1001 |
|
|
(struct sockaddr *)&msg->msg_peer, |
1002 |
|
|
msg->msg_peerlen)) == -1) { |
1003 |
|
|
log_warn("%s: sendto", __func__); |
1004 |
|
|
sa_free(env, sa); |
1005 |
|
|
return; |
1006 |
|
|
} |
1007 |
|
|
/* Exponential timeout */ |
1008 |
|
|
timer_add(env, &msg->msg_timer, |
1009 |
|
|
IKED_RETRANSMIT_TIMEOUT * (2 << (msg->msg_tries++))); |
1010 |
|
|
} else { |
1011 |
|
|
log_debug("%s: retransmit limit reached for msgid %u", |
1012 |
|
|
__func__, msg->msg_msgid); |
1013 |
|
|
sa_free(env, sa); |
1014 |
|
|
} |
1015 |
|
|
} |