1 |
|
|
/* $OpenBSD: isakmp_cfg.c,v 1.39 2014/01/23 01:04:28 deraadt Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2001 Niklas Hallqvist. All rights reserved. |
5 |
|
|
* Copyright (c) 2002 Håkan Olsson. All rights reserved. |
6 |
|
|
* |
7 |
|
|
* Redistribution and use in source and binary forms, with or without |
8 |
|
|
* modification, are permitted provided that the following conditions |
9 |
|
|
* are met: |
10 |
|
|
* 1. Redistributions of source code must retain the above copyright |
11 |
|
|
* notice, this list of conditions and the following disclaimer. |
12 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
13 |
|
|
* notice, this list of conditions and the following disclaimer in the |
14 |
|
|
* documentation and/or other materials provided with the distribution. |
15 |
|
|
* |
16 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
17 |
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
18 |
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
19 |
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
20 |
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
21 |
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
22 |
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
23 |
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
24 |
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
25 |
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 |
|
|
*/ |
27 |
|
|
|
28 |
|
|
/* |
29 |
|
|
* This code was written under funding by Gatespace |
30 |
|
|
* (http://www.gatespace.com/). |
31 |
|
|
*/ |
32 |
|
|
|
33 |
|
|
#include <sys/types.h> |
34 |
|
|
#include <stdlib.h> |
35 |
|
|
#include <netinet/in.h> |
36 |
|
|
#include <arpa/inet.h> |
37 |
|
|
#include <string.h> |
38 |
|
|
#include <bitstring.h> |
39 |
|
|
|
40 |
|
|
#include "attribute.h" |
41 |
|
|
#include "conf.h" |
42 |
|
|
#include "exchange.h" |
43 |
|
|
#include "hash.h" |
44 |
|
|
#include "ipsec.h" |
45 |
|
|
#include "isakmp_fld.h" |
46 |
|
|
#include "isakmp_num.h" |
47 |
|
|
#include "log.h" |
48 |
|
|
#include "message.h" |
49 |
|
|
#include "prf.h" |
50 |
|
|
#include "sa.h" |
51 |
|
|
#include "transport.h" |
52 |
|
|
#include "util.h" |
53 |
|
|
|
54 |
|
|
/* |
55 |
|
|
* Validation script used to test messages for correct content of |
56 |
|
|
* payloads depending on the exchange type. |
57 |
|
|
*/ |
58 |
|
|
int16_t script_transaction[] = { |
59 |
|
|
ISAKMP_PAYLOAD_ATTRIBUTE, /* Initiator -> responder. */ |
60 |
|
|
EXCHANGE_SCRIPT_SWITCH, |
61 |
|
|
ISAKMP_PAYLOAD_ATTRIBUTE, /* Responder -> initiator. */ |
62 |
|
|
EXCHANGE_SCRIPT_END |
63 |
|
|
}; |
64 |
|
|
|
65 |
|
|
static int cfg_decode_attribute(u_int16_t, u_int8_t *, u_int16_t, void *); |
66 |
|
|
static int cfg_encode_attributes(struct isakmp_cfg_attr_head *, u_int32_t, |
67 |
|
|
u_int32_t, char *, u_int8_t **, u_int16_t *); |
68 |
|
|
static int cfg_initiator_send_ATTR(struct message *); |
69 |
|
|
static int cfg_initiator_recv_ATTR(struct message *); |
70 |
|
|
static int cfg_responder_recv_ATTR(struct message *); |
71 |
|
|
static int cfg_responder_send_ATTR(struct message *); |
72 |
|
|
|
73 |
|
|
u_int8_t *cfg_add_hash(struct message *); |
74 |
|
|
int cfg_finalize_hash(struct message *, u_int8_t *, u_int8_t *, |
75 |
|
|
u_int16_t); |
76 |
|
|
int cfg_verify_hash(struct message *); |
77 |
|
|
|
78 |
|
|
/* Server: SET/ACK Client; REQ/REPLY */ |
79 |
|
|
int (*isakmp_cfg_initiator[]) (struct message *) = { |
80 |
|
|
cfg_initiator_send_ATTR, |
81 |
|
|
cfg_initiator_recv_ATTR |
82 |
|
|
}; |
83 |
|
|
|
84 |
|
|
/* Server: REQ/REPLY Client: SET/ACK */ |
85 |
|
|
int (*isakmp_cfg_responder[]) (struct message *) = { |
86 |
|
|
cfg_responder_recv_ATTR, |
87 |
|
|
cfg_responder_send_ATTR |
88 |
|
|
}; |
89 |
|
|
|
90 |
|
|
/* |
91 |
|
|
* When we are "the server", this starts SET/ACK mode |
92 |
|
|
* When we are "the client", this starts REQ/REPLY mode |
93 |
|
|
*/ |
94 |
|
|
static int |
95 |
|
|
cfg_initiator_send_ATTR(struct message *msg) |
96 |
|
|
{ |
97 |
|
|
struct sa *isakmp_sa = msg->isakmp_sa; |
98 |
|
|
struct ipsec_exch *ie = msg->exchange->data; |
99 |
|
|
u_int8_t *hashp = 0, *attrp, *attr; |
100 |
|
|
size_t attrlen, off; |
101 |
|
|
char *id_string, *cfg_mode, *field; |
102 |
|
|
struct sockaddr *sa; |
103 |
|
|
#define CFG_ATTR_BIT_MAX ISAKMP_CFG_ATTR_FUTURE_MIN /* XXX */ |
104 |
|
|
bitstr_t bit_decl(attrbits, CFG_ATTR_BIT_MAX); |
105 |
|
|
u_int16_t bit, length; |
106 |
|
|
u_int32_t life; |
107 |
|
|
|
108 |
|
|
if (msg->exchange->phase == 2) { |
109 |
|
|
hashp = cfg_add_hash(msg); |
110 |
|
|
if (!hashp) |
111 |
|
|
return -1; |
112 |
|
|
} |
113 |
|
|
/* We initiated this exchange, check isakmp_sa for other side. */ |
114 |
|
|
if (isakmp_sa->initiator) |
115 |
|
|
id_string = ipsec_id_string(isakmp_sa->id_r, |
116 |
|
|
isakmp_sa->id_r_len); |
117 |
|
|
else |
118 |
|
|
id_string = ipsec_id_string(isakmp_sa->id_i, |
119 |
|
|
isakmp_sa->id_i_len); |
120 |
|
|
if (!id_string) { |
121 |
|
|
log_print("cfg_initiator_send_ATTR: cannot parse ID"); |
122 |
|
|
goto fail; |
123 |
|
|
} |
124 |
|
|
/* Check for attribute list to send to the other side */ |
125 |
|
|
attrlen = 0; |
126 |
|
|
bit_nclear(attrbits, 0, CFG_ATTR_BIT_MAX - 1); |
127 |
|
|
|
128 |
|
|
cfg_mode = conf_get_str(id_string, "Mode"); |
129 |
|
|
if (!cfg_mode || strcmp(cfg_mode, "SET") == 0) { |
130 |
|
|
/* SET/ACK mode */ |
131 |
|
|
ie->cfg_type = ISAKMP_CFG_SET; |
132 |
|
|
|
133 |
|
|
LOG_DBG((LOG_NEGOTIATION, 10, |
134 |
|
|
"cfg_initiator_send_ATTR: SET/ACK mode")); |
135 |
|
|
|
136 |
|
|
#define ATTRFIND(STR,ATTR4,LEN4,ATTR6,LEN6) do \ |
137 |
|
|
{ \ |
138 |
|
|
if ((sa = conf_get_address (id_string, STR)) != NULL) \ |
139 |
|
|
switch (sa->sa_family) { \ |
140 |
|
|
case AF_INET: \ |
141 |
|
|
bit_set (attrbits, ATTR4); \ |
142 |
|
|
attrlen += ISAKMP_ATTR_SZ + LEN4; \ |
143 |
|
|
break; \ |
144 |
|
|
case AF_INET6: \ |
145 |
|
|
bit_set (attrbits, ATTR6); \ |
146 |
|
|
attrlen += ISAKMP_ATTR_SZ + LEN6; \ |
147 |
|
|
break; \ |
148 |
|
|
default: \ |
149 |
|
|
break; \ |
150 |
|
|
} \ |
151 |
|
|
free (sa); \ |
152 |
|
|
} while (0) |
153 |
|
|
|
154 |
|
|
/* |
155 |
|
|
* XXX We don't simultaneously support IPv4 and IPv6 |
156 |
|
|
* addresses. |
157 |
|
|
*/ |
158 |
|
|
ATTRFIND("Address", ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS, 4, |
159 |
|
|
ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS, 16); |
160 |
|
|
ATTRFIND("Netmask", ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK, 4, |
161 |
|
|
ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK, 16); |
162 |
|
|
ATTRFIND("Nameserver", ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS, 4, |
163 |
|
|
ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS, 16); |
164 |
|
|
ATTRFIND("WINS-server", ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS, 4, |
165 |
|
|
ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS, 16); |
166 |
|
|
ATTRFIND("DHCP-server", ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP, 4, |
167 |
|
|
ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP, 16); |
168 |
|
|
#ifdef notyet |
169 |
|
|
ATTRFIND("Network", ISAKMP_CFG_ATTR_INTERNAL_IP4_SUBNET, 8, |
170 |
|
|
ISAKMP_CFG_ATTR_INTERNAL_IP6_SUBNET, 17); |
171 |
|
|
#endif |
172 |
|
|
#undef ATTRFIND |
173 |
|
|
|
174 |
|
|
if (conf_get_str(id_string, "Lifetime")) { |
175 |
|
|
bit_set(attrbits, |
176 |
|
|
ISAKMP_CFG_ATTR_INTERNAL_ADDRESS_EXPIRY); |
177 |
|
|
attrlen += ISAKMP_ATTR_SZ + 4; |
178 |
|
|
} |
179 |
|
|
} else { |
180 |
|
|
struct conf_list *alist; |
181 |
|
|
struct conf_list_node *anode; |
182 |
|
|
|
183 |
|
|
ie->cfg_type = ISAKMP_CFG_REQUEST; |
184 |
|
|
|
185 |
|
|
LOG_DBG((LOG_NEGOTIATION, 10, |
186 |
|
|
"cfg_initiator_send_ATTR: REQ/REPLY mode")); |
187 |
|
|
|
188 |
|
|
alist = conf_get_list(id_string, "Attributes"); |
189 |
|
|
if (alist) { |
190 |
|
|
for (anode = TAILQ_FIRST(&alist->fields); anode; |
191 |
|
|
anode = TAILQ_NEXT(anode, link)) { |
192 |
|
|
if (strcasecmp(anode->field, "Address") == 0) { |
193 |
|
|
bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS); |
194 |
|
|
bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS); |
195 |
|
|
attrlen += ISAKMP_ATTR_SZ * 2; |
196 |
|
|
} else if (strcasecmp(anode->field, "Netmask") |
197 |
|
|
== 0) { |
198 |
|
|
bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK); |
199 |
|
|
bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK); |
200 |
|
|
attrlen += ISAKMP_ATTR_SZ * 2; |
201 |
|
|
} else if (strcasecmp(anode->field, |
202 |
|
|
"Nameserver") == 0) { |
203 |
|
|
bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS); |
204 |
|
|
bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS); |
205 |
|
|
attrlen += ISAKMP_ATTR_SZ * 2; |
206 |
|
|
} else if (strcasecmp(anode->field, |
207 |
|
|
"WINS-server") == 0) { |
208 |
|
|
bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS); |
209 |
|
|
bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS); |
210 |
|
|
attrlen += ISAKMP_ATTR_SZ * 2; |
211 |
|
|
} else if (strcasecmp(anode->field, |
212 |
|
|
"DHCP-server") == 0) { |
213 |
|
|
bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP); |
214 |
|
|
bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP); |
215 |
|
|
attrlen += ISAKMP_ATTR_SZ * 2; |
216 |
|
|
} else if (strcasecmp(anode->field, |
217 |
|
|
"Lifetime") == 0) { |
218 |
|
|
bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_ADDRESS_EXPIRY); |
219 |
|
|
attrlen += ISAKMP_ATTR_SZ; |
220 |
|
|
} else { |
221 |
|
|
log_print("cfg_initiator_send_ATTR: " |
222 |
|
|
"unknown attribute %.20s in " |
223 |
|
|
"section [%s]", anode->field, |
224 |
|
|
id_string); |
225 |
|
|
} |
226 |
|
|
} |
227 |
|
|
|
228 |
|
|
conf_free_list(alist); |
229 |
|
|
} |
230 |
|
|
} |
231 |
|
|
|
232 |
|
|
if (attrlen == 0) { |
233 |
|
|
/* No data found. */ |
234 |
|
|
log_print("cfg_initiator_send_ATTR: no IKECFG attributes " |
235 |
|
|
"found for [%s]", id_string); |
236 |
|
|
|
237 |
|
|
/* |
238 |
|
|
* We can continue, but this indicates a configuration error |
239 |
|
|
* that the user probably will want to correct. |
240 |
|
|
*/ |
241 |
|
|
free(id_string); |
242 |
|
|
return 0; |
243 |
|
|
} |
244 |
|
|
attrlen += ISAKMP_ATTRIBUTE_SZ; |
245 |
|
|
attrp = calloc(1, attrlen); |
246 |
|
|
if (!attrp) { |
247 |
|
|
log_error("cfg_initiator_send_ATTR: calloc (1, %lu) failed", |
248 |
|
|
(unsigned long)attrlen); |
249 |
|
|
goto fail; |
250 |
|
|
} |
251 |
|
|
if (message_add_payload(msg, ISAKMP_PAYLOAD_ATTRIBUTE, attrp, attrlen, |
252 |
|
|
1)) { |
253 |
|
|
free(attrp); |
254 |
|
|
goto fail; |
255 |
|
|
} |
256 |
|
|
SET_ISAKMP_ATTRIBUTE_TYPE(attrp, ie->cfg_type); |
257 |
|
|
arc4random_buf((u_int8_t *) & ie->cfg_id, sizeof ie->cfg_id); |
258 |
|
|
SET_ISAKMP_ATTRIBUTE_ID(attrp, ie->cfg_id); |
259 |
|
|
|
260 |
|
|
off = ISAKMP_ATTRIBUTE_SZ; |
261 |
|
|
|
262 |
|
|
/* |
263 |
|
|
* Use the bitstring built previously to collect the right |
264 |
|
|
* parameters for attrp. |
265 |
|
|
*/ |
266 |
|
|
for (bit = 0; bit < CFG_ATTR_BIT_MAX; bit++) |
267 |
|
|
if (bit_test(attrbits, bit)) { |
268 |
|
|
attr = attrp + off; |
269 |
|
|
SET_ISAKMP_ATTR_TYPE(attr, bit); |
270 |
|
|
|
271 |
|
|
if (ie->cfg_type == ISAKMP_CFG_REQUEST) { |
272 |
|
|
off += ISAKMP_ATTR_SZ; |
273 |
|
|
continue; |
274 |
|
|
} |
275 |
|
|
/* All the other are similar, this is the odd one. */ |
276 |
|
|
if (bit == ISAKMP_CFG_ATTR_INTERNAL_ADDRESS_EXPIRY) { |
277 |
|
|
life = conf_get_num(id_string, "Lifetime", |
278 |
|
|
1200); |
279 |
|
|
SET_ISAKMP_ATTR_LENGTH_VALUE(attr, 4); |
280 |
|
|
encode_32(attr + ISAKMP_ATTR_VALUE_OFF, life); |
281 |
|
|
off += ISAKMP_ATTR_SZ + 4; |
282 |
|
|
continue; |
283 |
|
|
} |
284 |
|
|
switch (bit) { |
285 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS: |
286 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK: |
287 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS: |
288 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP: |
289 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS: |
290 |
|
|
length = 4; |
291 |
|
|
break; |
292 |
|
|
|
293 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS: |
294 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK: |
295 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS: |
296 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP: |
297 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS: |
298 |
|
|
length = 16; |
299 |
|
|
break; |
300 |
|
|
|
301 |
|
|
default: |
302 |
|
|
length = 0; /* Silence gcc. */ |
303 |
|
|
} |
304 |
|
|
|
305 |
|
|
switch (bit) { |
306 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS: |
307 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS: |
308 |
|
|
field = "Address"; |
309 |
|
|
break; |
310 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK: |
311 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK: |
312 |
|
|
field = "Netmask"; |
313 |
|
|
break; |
314 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS: |
315 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS: |
316 |
|
|
field = "Nameserver"; |
317 |
|
|
break; |
318 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP: |
319 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP: |
320 |
|
|
field = "DHCP-server"; |
321 |
|
|
break; |
322 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS: |
323 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS: |
324 |
|
|
field = "WINS-server"; |
325 |
|
|
break; |
326 |
|
|
default: |
327 |
|
|
field = 0; /* Silence gcc. */ |
328 |
|
|
} |
329 |
|
|
|
330 |
|
|
sa = conf_get_address(id_string, field); |
331 |
|
|
|
332 |
|
|
SET_ISAKMP_ATTR_LENGTH_VALUE(attr, length); |
333 |
|
|
memcpy(attr + ISAKMP_ATTR_VALUE_OFF, |
334 |
|
|
sockaddr_addrdata(sa), length); |
335 |
|
|
|
336 |
|
|
free(sa); |
337 |
|
|
|
338 |
|
|
off += ISAKMP_ATTR_SZ + length; |
339 |
|
|
} |
340 |
|
|
if (msg->exchange->phase == 2) |
341 |
|
|
if (cfg_finalize_hash(msg, hashp, attrp, attrlen)) |
342 |
|
|
goto fail; |
343 |
|
|
|
344 |
|
|
return 0; |
345 |
|
|
|
346 |
|
|
fail: |
347 |
|
|
free(id_string); |
348 |
|
|
return -1; |
349 |
|
|
} |
350 |
|
|
|
351 |
|
|
/* |
352 |
|
|
* As "the server", this ends SET/ACK. |
353 |
|
|
* As "the client", this ends REQ/REPLY. |
354 |
|
|
*/ |
355 |
|
|
static int |
356 |
|
|
cfg_initiator_recv_ATTR(struct message *msg) |
357 |
|
|
{ |
358 |
|
|
struct payload *attrp = payload_first(msg, ISAKMP_PAYLOAD_ATTRIBUTE); |
359 |
|
|
struct ipsec_exch *ie = msg->exchange->data; |
360 |
|
|
struct sa *isakmp_sa = msg->isakmp_sa; |
361 |
|
|
struct isakmp_cfg_attr *attr; |
362 |
|
|
struct sockaddr *sa; |
363 |
|
|
const char *uk_addr = "<unknown>"; |
364 |
|
|
char *addr; |
365 |
|
|
|
366 |
|
|
if (msg->exchange->phase == 2) |
367 |
|
|
if (cfg_verify_hash(msg)) |
368 |
|
|
return -1; |
369 |
|
|
|
370 |
|
|
/* Sanity. */ |
371 |
|
|
if (ie->cfg_id != GET_ISAKMP_ATTRIBUTE_ID(attrp->p)) { |
372 |
|
|
log_print("cfg_initiator_recv_ATTR: " |
373 |
|
|
"cfg packet ID does not match!"); |
374 |
|
|
message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); |
375 |
|
|
return -1; |
376 |
|
|
} |
377 |
|
|
switch (attrp->p[ISAKMP_ATTRIBUTE_TYPE_OFF]) { |
378 |
|
|
case ISAKMP_CFG_ACK: |
379 |
|
|
if (ie->cfg_type != ISAKMP_CFG_SET) { |
380 |
|
|
log_print("cfg_initiator_recv_ATTR: " |
381 |
|
|
"bad packet type ACK"); |
382 |
|
|
message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, |
383 |
|
|
0, 1, 0); |
384 |
|
|
return -1; |
385 |
|
|
} |
386 |
|
|
break; |
387 |
|
|
case ISAKMP_CFG_REPLY: |
388 |
|
|
if (ie->cfg_type != ISAKMP_CFG_REQUEST) { |
389 |
|
|
log_print("cfg_initiator_recv_ATTR: " |
390 |
|
|
"bad packet type REPLY"); |
391 |
|
|
message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, |
392 |
|
|
0, 1, 0); |
393 |
|
|
return -1; |
394 |
|
|
} |
395 |
|
|
break; |
396 |
|
|
|
397 |
|
|
default: |
398 |
|
|
log_print("cfg_initiator_recv_ATTR: unexpected configuration " |
399 |
|
|
"message type %d", attrp->p[ISAKMP_ATTRIBUTE_TYPE_OFF]); |
400 |
|
|
message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); |
401 |
|
|
return -1; |
402 |
|
|
} |
403 |
|
|
|
404 |
|
|
attribute_map(attrp->p + ISAKMP_ATTRIBUTE_ATTRS_OFF, |
405 |
|
|
GET_ISAKMP_GEN_LENGTH(attrp->p) - ISAKMP_TRANSFORM_SA_ATTRS_OFF, |
406 |
|
|
cfg_decode_attribute, ie); |
407 |
|
|
|
408 |
|
|
switch (ie->cfg_type) { |
409 |
|
|
case ISAKMP_CFG_ACK: { |
410 |
|
|
/* SET/ACK -- Server side (ACK from client) */ |
411 |
|
|
msg->transport->vtbl->get_src(isakmp_sa->transport, |
412 |
|
|
&sa); |
413 |
|
|
if (sockaddr2text(sa, &addr, 0) < 0) |
414 |
|
|
addr = (char *) uk_addr; |
415 |
|
|
|
416 |
|
|
for (attr = LIST_FIRST(&ie->attrs); attr; |
417 |
|
|
attr = LIST_NEXT(attr, link)) |
418 |
|
|
LOG_DBG((LOG_NEGOTIATION, 50, |
419 |
|
|
"cfg_initiator_recv_ATTR: " |
420 |
|
|
"client %s ACKs attribute %s", addr, |
421 |
|
|
constant_name(isakmp_cfg_attr_cst, |
422 |
|
|
attr->type))); |
423 |
|
|
|
424 |
|
|
if (addr != uk_addr) |
425 |
|
|
free(addr); |
426 |
|
|
} |
427 |
|
|
break; |
428 |
|
|
|
429 |
|
|
case ISAKMP_CFG_REPLY: { |
430 |
|
|
/* |
431 |
|
|
* REQ/REPLY: effect attributes we've gotten |
432 |
|
|
* responses on. |
433 |
|
|
*/ |
434 |
|
|
msg->transport->vtbl->get_src(isakmp_sa->transport, |
435 |
|
|
&sa); |
436 |
|
|
if (sockaddr2text(sa, &addr, 0) < 0) |
437 |
|
|
addr = (char *) uk_addr; |
438 |
|
|
|
439 |
|
|
for (attr = LIST_FIRST(&ie->attrs); attr; |
440 |
|
|
attr = LIST_NEXT(attr, link)) |
441 |
|
|
LOG_DBG((LOG_NEGOTIATION, 50, |
442 |
|
|
"cfg_initiator_recv_ATTR: " |
443 |
|
|
"server %s replied with attribute %s", |
444 |
|
|
addr, constant_name(isakmp_cfg_attr_cst, |
445 |
|
|
attr->type))); |
446 |
|
|
|
447 |
|
|
if (addr != uk_addr) |
448 |
|
|
free(addr); |
449 |
|
|
} |
450 |
|
|
break; |
451 |
|
|
|
452 |
|
|
default: |
453 |
|
|
break; |
454 |
|
|
} |
455 |
|
|
|
456 |
|
|
attrp->flags |= PL_MARK; |
457 |
|
|
return 0; |
458 |
|
|
} |
459 |
|
|
|
460 |
|
|
/* |
461 |
|
|
* As "the server", this starts REQ/REPLY (initiated by the client). |
462 |
|
|
* As "the client", this starts SET/ACK (initiated by the server). |
463 |
|
|
*/ |
464 |
|
|
static int |
465 |
|
|
cfg_responder_recv_ATTR(struct message *msg) |
466 |
|
|
{ |
467 |
|
|
struct payload *attrp = payload_first(msg, ISAKMP_PAYLOAD_ATTRIBUTE); |
468 |
|
|
struct ipsec_exch *ie = msg->exchange->data; |
469 |
|
|
struct sa *isakmp_sa = msg->isakmp_sa; |
470 |
|
|
struct isakmp_cfg_attr *attr; |
471 |
|
|
struct sockaddr *sa; |
472 |
|
|
char *addr; |
473 |
|
|
|
474 |
|
|
if (msg->exchange->phase == 2) |
475 |
|
|
if (cfg_verify_hash(msg)) |
476 |
|
|
return -1; |
477 |
|
|
|
478 |
|
|
ie->cfg_id = GET_ISAKMP_ATTRIBUTE_ID(attrp->p); |
479 |
|
|
ie->cfg_type = attrp->p[ISAKMP_ATTRIBUTE_TYPE_OFF]; |
480 |
|
|
|
481 |
|
|
switch (ie->cfg_type) { |
482 |
|
|
case ISAKMP_CFG_REQUEST: |
483 |
|
|
case ISAKMP_CFG_SET: |
484 |
|
|
break; |
485 |
|
|
|
486 |
|
|
default: |
487 |
|
|
message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); |
488 |
|
|
log_print("cfg_responder_recv_ATTR: " |
489 |
|
|
"unexpected configuration message type %d", ie->cfg_type); |
490 |
|
|
return -1; |
491 |
|
|
} |
492 |
|
|
|
493 |
|
|
attribute_map(attrp->p + ISAKMP_ATTRIBUTE_ATTRS_OFF, |
494 |
|
|
GET_ISAKMP_GEN_LENGTH(attrp->p) - ISAKMP_TRANSFORM_SA_ATTRS_OFF, |
495 |
|
|
cfg_decode_attribute, ie); |
496 |
|
|
|
497 |
|
|
switch (ie->cfg_type) { |
498 |
|
|
case ISAKMP_CFG_REQUEST: |
499 |
|
|
/* We're done. */ |
500 |
|
|
break; |
501 |
|
|
|
502 |
|
|
case ISAKMP_CFG_SET: { |
503 |
|
|
/* SET/ACK -- Client side (SET from server) */ |
504 |
|
|
const char *uk_addr = "<unknown>"; |
505 |
|
|
|
506 |
|
|
msg->transport->vtbl->get_dst(isakmp_sa->transport, |
507 |
|
|
&sa); |
508 |
|
|
if (sockaddr2text(sa, &addr, 0) < 0) |
509 |
|
|
addr = (char *) uk_addr; |
510 |
|
|
|
511 |
|
|
for (attr = LIST_FIRST(&ie->attrs); attr; |
512 |
|
|
attr = LIST_NEXT(attr, link)) |
513 |
|
|
LOG_DBG((LOG_NEGOTIATION, 50, |
514 |
|
|
"cfg_responder_recv_ATTR: " |
515 |
|
|
"server %s asks us to SET attribute %s", |
516 |
|
|
addr, constant_name(isakmp_cfg_attr_cst, |
517 |
|
|
attr->type))); |
518 |
|
|
|
519 |
|
|
/* |
520 |
|
|
* XXX Here's the place to add code to walk through |
521 |
|
|
* XXX each attribute and send them along to dhclient |
522 |
|
|
* XXX or whatever. Each attribute that we act upon |
523 |
|
|
* XXX (such as setting a netmask), should be marked |
524 |
|
|
* XXX like this for us to send the proper ACK |
525 |
|
|
* XXX response: attr->attr_used++; |
526 |
|
|
*/ |
527 |
|
|
|
528 |
|
|
if (addr != uk_addr) |
529 |
|
|
free(addr); |
530 |
|
|
} |
531 |
|
|
break; |
532 |
|
|
|
533 |
|
|
default: |
534 |
|
|
break; |
535 |
|
|
} |
536 |
|
|
|
537 |
|
|
attrp->flags |= PL_MARK; |
538 |
|
|
return 0; |
539 |
|
|
} |
540 |
|
|
|
541 |
|
|
/* |
542 |
|
|
* As "the server", this ends REQ/REPLY mode. |
543 |
|
|
* As "the client", this ends SET/ACK mode. |
544 |
|
|
*/ |
545 |
|
|
static int |
546 |
|
|
cfg_responder_send_ATTR(struct message *msg) |
547 |
|
|
{ |
548 |
|
|
struct ipsec_exch *ie = msg->exchange->data; |
549 |
|
|
struct sa *isakmp_sa = msg->isakmp_sa; |
550 |
|
|
u_int8_t *hashp = 0, *attrp; |
551 |
|
|
u_int16_t attrlen; |
552 |
|
|
char *id_string; |
553 |
|
|
|
554 |
|
|
if (msg->exchange->phase == 2) { |
555 |
|
|
hashp = cfg_add_hash(msg); |
556 |
|
|
if (!hashp) |
557 |
|
|
return -1; |
558 |
|
|
} |
559 |
|
|
/* We are responder, check isakmp_sa for other side. */ |
560 |
|
|
if (isakmp_sa->initiator ^ (ie->cfg_type == ISAKMP_CFG_REQUEST)) |
561 |
|
|
id_string = ipsec_id_string(isakmp_sa->id_i, |
562 |
|
|
isakmp_sa->id_i_len); |
563 |
|
|
else |
564 |
|
|
id_string = ipsec_id_string(isakmp_sa->id_r, |
565 |
|
|
isakmp_sa->id_r_len); |
566 |
|
|
if (!id_string) { |
567 |
|
|
log_print("cfg_responder_send_ATTR: cannot parse client's ID"); |
568 |
|
|
return -1; |
569 |
|
|
} |
570 |
|
|
if (cfg_encode_attributes(&ie->attrs, (ie->cfg_type == ISAKMP_CFG_SET ? |
571 |
|
|
ISAKMP_CFG_ACK : ISAKMP_CFG_REPLY), ie->cfg_id, id_string, &attrp, |
572 |
|
|
&attrlen)) { |
573 |
|
|
free(id_string); |
574 |
|
|
return -1; |
575 |
|
|
} |
576 |
|
|
free(id_string); |
577 |
|
|
|
578 |
|
|
if (message_add_payload(msg, ISAKMP_PAYLOAD_ATTRIBUTE, attrp, attrlen, |
579 |
|
|
1)) { |
580 |
|
|
free(attrp); |
581 |
|
|
return -1; |
582 |
|
|
} |
583 |
|
|
if (msg->exchange->phase == 2) |
584 |
|
|
if (cfg_finalize_hash(msg, hashp, attrp, attrlen)) |
585 |
|
|
return -1; |
586 |
|
|
|
587 |
|
|
return 0; |
588 |
|
|
} |
589 |
|
|
|
590 |
|
|
u_int8_t * |
591 |
|
|
cfg_add_hash(struct message *msg) |
592 |
|
|
{ |
593 |
|
|
struct ipsec_sa *isa = msg->isakmp_sa->data; |
594 |
|
|
struct hash *hash = hash_get(isa->hash); |
595 |
|
|
u_int8_t *hashp; |
596 |
|
|
|
597 |
|
|
hashp = malloc(ISAKMP_HASH_SZ + hash->hashsize); |
598 |
|
|
if (!hashp) { |
599 |
|
|
log_error("cfg_add_hash: malloc (%lu) failed", |
600 |
|
|
ISAKMP_HASH_SZ + (unsigned long)hash->hashsize); |
601 |
|
|
return 0; |
602 |
|
|
} |
603 |
|
|
if (message_add_payload(msg, ISAKMP_PAYLOAD_HASH, hashp, |
604 |
|
|
ISAKMP_HASH_SZ + hash->hashsize, 1)) { |
605 |
|
|
free(hashp); |
606 |
|
|
return 0; |
607 |
|
|
} |
608 |
|
|
return hashp; |
609 |
|
|
} |
610 |
|
|
|
611 |
|
|
int |
612 |
|
|
cfg_finalize_hash(struct message *msg, u_int8_t *hashp, u_int8_t *data, |
613 |
|
|
u_int16_t length) |
614 |
|
|
{ |
615 |
|
|
struct ipsec_sa *isa = msg->isakmp_sa->data; |
616 |
|
|
struct prf *prf; |
617 |
|
|
|
618 |
|
|
prf = prf_alloc(isa->prf_type, isa->hash, isa->skeyid_a, |
619 |
|
|
isa->skeyid_len); |
620 |
|
|
if (!prf) |
621 |
|
|
return -1; |
622 |
|
|
|
623 |
|
|
prf->Init(prf->prfctx); |
624 |
|
|
prf->Update(prf->prfctx, msg->exchange->message_id, |
625 |
|
|
ISAKMP_HDR_MESSAGE_ID_LEN); |
626 |
|
|
prf->Update(prf->prfctx, data, length); |
627 |
|
|
prf->Final(hashp + ISAKMP_GEN_SZ, prf->prfctx); |
628 |
|
|
prf_free(prf); |
629 |
|
|
return 0; |
630 |
|
|
} |
631 |
|
|
|
632 |
|
|
int |
633 |
|
|
cfg_verify_hash(struct message *msg) |
634 |
|
|
{ |
635 |
|
|
struct payload *hashp = payload_first(msg, ISAKMP_PAYLOAD_HASH); |
636 |
|
|
struct ipsec_sa *isa = msg->isakmp_sa->data; |
637 |
|
|
struct prf *prf; |
638 |
|
|
u_int8_t *hash, *comp_hash; |
639 |
|
|
size_t hash_len; |
640 |
|
|
|
641 |
|
|
if (!hashp) { |
642 |
|
|
log_print("cfg_verify_hash: phase 2 message missing HASH"); |
643 |
|
|
message_drop(msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION, |
644 |
|
|
0, 1, 0); |
645 |
|
|
return -1; |
646 |
|
|
} |
647 |
|
|
hash = hashp->p; |
648 |
|
|
hash_len = GET_ISAKMP_GEN_LENGTH(hash); |
649 |
|
|
comp_hash = malloc(hash_len - ISAKMP_GEN_SZ); |
650 |
|
|
if (!comp_hash) { |
651 |
|
|
log_error("cfg_verify_hash: malloc (%lu) failed", |
652 |
|
|
(unsigned long)hash_len - ISAKMP_GEN_SZ); |
653 |
|
|
return -1; |
654 |
|
|
} |
655 |
|
|
/* Verify hash. */ |
656 |
|
|
prf = prf_alloc(isa->prf_type, isa->hash, isa->skeyid_a, |
657 |
|
|
isa->skeyid_len); |
658 |
|
|
if (!prf) { |
659 |
|
|
free(comp_hash); |
660 |
|
|
return -1; |
661 |
|
|
} |
662 |
|
|
prf->Init(prf->prfctx); |
663 |
|
|
prf->Update(prf->prfctx, msg->exchange->message_id, |
664 |
|
|
ISAKMP_HDR_MESSAGE_ID_LEN); |
665 |
|
|
prf->Update(prf->prfctx, hash + hash_len, |
666 |
|
|
msg->iov[0].iov_len - ISAKMP_HDR_SZ - hash_len); |
667 |
|
|
prf->Final(comp_hash, prf->prfctx); |
668 |
|
|
prf_free(prf); |
669 |
|
|
|
670 |
|
|
if (memcmp(hash + ISAKMP_GEN_SZ, comp_hash, hash_len - ISAKMP_GEN_SZ) |
671 |
|
|
!= 0) { |
672 |
|
|
message_drop(msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION, |
673 |
|
|
0, 1, 0); |
674 |
|
|
free(comp_hash); |
675 |
|
|
return -1; |
676 |
|
|
} |
677 |
|
|
free(comp_hash); |
678 |
|
|
|
679 |
|
|
/* Mark the HASH as handled. */ |
680 |
|
|
hashp->flags |= PL_MARK; |
681 |
|
|
|
682 |
|
|
/* Mark message authenticated. */ |
683 |
|
|
msg->flags |= MSG_AUTHENTICATED; |
684 |
|
|
|
685 |
|
|
return 0; |
686 |
|
|
} |
687 |
|
|
|
688 |
|
|
/* |
689 |
|
|
* Decode the attribute of type TYPE with a LEN length value pointed to by |
690 |
|
|
* VALUE. VIE is a pointer to the IPsec exchange context holding the |
691 |
|
|
* attributes indexed by type for easy retrieval. |
692 |
|
|
*/ |
693 |
|
|
static int |
694 |
|
|
cfg_decode_attribute(u_int16_t type, u_int8_t * value, u_int16_t len, |
695 |
|
|
void *vie) |
696 |
|
|
{ |
697 |
|
|
struct ipsec_exch *ie = vie; |
698 |
|
|
struct isakmp_cfg_attr *attr; |
699 |
|
|
|
700 |
|
|
if (type >= ISAKMP_CFG_ATTR_PRIVATE_MIN && |
701 |
|
|
type <= ISAKMP_CFG_ATTR_PRIVATE_MAX) |
702 |
|
|
return 0; |
703 |
|
|
if (type == 0 || type >= ISAKMP_CFG_ATTR_FUTURE_MIN) { |
704 |
|
|
LOG_DBG((LOG_NEGOTIATION, 30, |
705 |
|
|
"cfg_decode_attribute: invalid attr type %u", type)); |
706 |
|
|
return -1; |
707 |
|
|
} |
708 |
|
|
attr = calloc(1, sizeof *attr); |
709 |
|
|
if (!attr) { |
710 |
|
|
log_error("cfg_decode_attribute: calloc (1, %lu) failed", |
711 |
|
|
(unsigned long)sizeof *attr); |
712 |
|
|
return -1; |
713 |
|
|
} |
714 |
|
|
attr->type = type; |
715 |
|
|
attr->length = len; |
716 |
|
|
if (len) { |
717 |
|
|
attr->value = malloc(len); |
718 |
|
|
if (!attr->value) { |
719 |
|
|
log_error("cfg_decode_attribute: malloc (%d) failed", |
720 |
|
|
len); |
721 |
|
|
free(attr); |
722 |
|
|
/* Should we also deallocate all other values? */ |
723 |
|
|
return -1; |
724 |
|
|
} |
725 |
|
|
memcpy(attr->value, value, len); |
726 |
|
|
} |
727 |
|
|
LIST_INSERT_HEAD(&ie->attrs, attr, link); |
728 |
|
|
return 0; |
729 |
|
|
} |
730 |
|
|
|
731 |
|
|
/* |
732 |
|
|
* Encode list of attributes from ie->attrs into a attribute payload. |
733 |
|
|
*/ |
734 |
|
|
static int |
735 |
|
|
cfg_encode_attributes(struct isakmp_cfg_attr_head *attrs, u_int32_t type, |
736 |
|
|
u_int32_t cfg_id, char *id_string, u_int8_t **attrp, u_int16_t *len) |
737 |
|
|
{ |
738 |
|
|
struct isakmp_cfg_attr *attr; |
739 |
|
|
struct sockaddr *sa; |
740 |
|
|
sa_family_t family; |
741 |
|
|
u_int32_t value; |
742 |
|
|
u_int16_t off; |
743 |
|
|
char *field; |
744 |
|
|
|
745 |
|
|
/* Compute length */ |
746 |
|
|
*len = ISAKMP_ATTRIBUTE_SZ; |
747 |
|
|
for (attr = LIST_FIRST(attrs); attr; attr = LIST_NEXT(attr, link)) { |
748 |
|
|
/* With ACK we only include the attrs we've actually used. */ |
749 |
|
|
if (type == ISAKMP_CFG_ACK && attr->attr_used == 0) |
750 |
|
|
continue; |
751 |
|
|
|
752 |
|
|
switch (attr->type) { |
753 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS: |
754 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK: |
755 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP: |
756 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS: |
757 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS: |
758 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_ADDRESS_EXPIRY: |
759 |
|
|
attr->length = 4; |
760 |
|
|
break; |
761 |
|
|
|
762 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_SUBNET: |
763 |
|
|
attr->length = 8; |
764 |
|
|
break; |
765 |
|
|
|
766 |
|
|
case ISAKMP_CFG_ATTR_APPLICATION_VERSION: |
767 |
|
|
/* XXX So far no version identifier of isakmpd here. */ |
768 |
|
|
attr->length = 0; |
769 |
|
|
break; |
770 |
|
|
|
771 |
|
|
case ISAKMP_CFG_ATTR_SUPPORTED_ATTRIBUTES: |
772 |
|
|
attr->length = 2 * 15; |
773 |
|
|
break; |
774 |
|
|
|
775 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS: |
776 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK: |
777 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP: |
778 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS: |
779 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS: |
780 |
|
|
attr->length = 16; |
781 |
|
|
break; |
782 |
|
|
|
783 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_SUBNET: |
784 |
|
|
attr->length = 17; |
785 |
|
|
break; |
786 |
|
|
|
787 |
|
|
default: |
788 |
|
|
attr->ignore++; |
789 |
|
|
/* XXX Log! */ |
790 |
|
|
} |
791 |
|
|
*len += ISAKMP_ATTR_SZ + attr->length; |
792 |
|
|
} |
793 |
|
|
|
794 |
|
|
/* Allocate enough space for the payload */ |
795 |
|
|
*attrp = calloc(1, *len); |
796 |
|
|
if (!*attrp) { |
797 |
|
|
log_error("cfg_encode_attributes: calloc (1, %lu) failed", |
798 |
|
|
(unsigned long)*len); |
799 |
|
|
return -1; |
800 |
|
|
} |
801 |
|
|
SET_ISAKMP_ATTRIBUTE_TYPE(*attrp, type); |
802 |
|
|
SET_ISAKMP_ATTRIBUTE_ID(*attrp, cfg_id); |
803 |
|
|
|
804 |
|
|
off = ISAKMP_ATTRIBUTE_SZ; |
805 |
|
|
for (attr = LIST_FIRST(attrs); attr; attr = LIST_NEXT(attr, link)) { |
806 |
|
|
/* With ACK we only include the attrs we've actually used. */ |
807 |
|
|
if (type == ISAKMP_CFG_ACK && attr->attr_used == 0) |
808 |
|
|
continue; |
809 |
|
|
|
810 |
|
|
switch (attr->type) { |
811 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS: |
812 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK: |
813 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_SUBNET: |
814 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP: |
815 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS: |
816 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS: |
817 |
|
|
family = AF_INET; |
818 |
|
|
break; |
819 |
|
|
|
820 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS: |
821 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK: |
822 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_SUBNET: |
823 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP: |
824 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS: |
825 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS: |
826 |
|
|
family = AF_INET6; |
827 |
|
|
break; |
828 |
|
|
|
829 |
|
|
default: |
830 |
|
|
family = 0; |
831 |
|
|
break; |
832 |
|
|
} |
833 |
|
|
|
834 |
|
|
switch (attr->type) { |
835 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS: |
836 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS: |
837 |
|
|
field = "Address"; |
838 |
|
|
break; |
839 |
|
|
|
840 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_SUBNET: |
841 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_SUBNET: |
842 |
|
|
field = "Network"; /* XXX or just "Address" */ |
843 |
|
|
break; |
844 |
|
|
|
845 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK: |
846 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK: |
847 |
|
|
field = "Netmask"; |
848 |
|
|
break; |
849 |
|
|
|
850 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP: |
851 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP: |
852 |
|
|
field = "DHCP-server"; |
853 |
|
|
break; |
854 |
|
|
|
855 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS: |
856 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS: |
857 |
|
|
field = "Nameserver"; |
858 |
|
|
break; |
859 |
|
|
|
860 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS: |
861 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS: |
862 |
|
|
field = "WINS-server"; |
863 |
|
|
break; |
864 |
|
|
|
865 |
|
|
default: |
866 |
|
|
field = 0; |
867 |
|
|
} |
868 |
|
|
|
869 |
|
|
switch (attr->type) { |
870 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS: |
871 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS: |
872 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK: |
873 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK: |
874 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP: |
875 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP: |
876 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS: |
877 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS: |
878 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS: |
879 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS: |
880 |
|
|
sa = conf_get_address(id_string, field); |
881 |
|
|
if (!sa) { |
882 |
|
|
LOG_DBG((LOG_NEGOTIATION, 10, |
883 |
|
|
"cfg_responder_send_ATTR: " |
884 |
|
|
"attribute not found: %s", field)); |
885 |
|
|
attr->length = 0; |
886 |
|
|
break; |
887 |
|
|
} |
888 |
|
|
if (sa->sa_family != family) { |
889 |
|
|
log_print("cfg_responder_send_ATTR: " |
890 |
|
|
"attribute %s - expected %s got %s data", |
891 |
|
|
field, |
892 |
|
|
(family == AF_INET ? "IPv4" : "IPv6"), |
893 |
|
|
(sa->sa_family == |
894 |
|
|
AF_INET ? "IPv4" : "IPv6")); |
895 |
|
|
free(sa); |
896 |
|
|
attr->length = 0; |
897 |
|
|
break; |
898 |
|
|
} |
899 |
|
|
/* Temporary limit length for the _SUBNET types. */ |
900 |
|
|
if (attr->type == ISAKMP_CFG_ATTR_INTERNAL_IP4_SUBNET) |
901 |
|
|
attr->length = 4; |
902 |
|
|
else if (attr->type == |
903 |
|
|
ISAKMP_CFG_ATTR_INTERNAL_IP6_SUBNET) |
904 |
|
|
attr->length = 16; |
905 |
|
|
|
906 |
|
|
memcpy(*attrp + off + ISAKMP_ATTR_VALUE_OFF, |
907 |
|
|
sockaddr_addrdata(sa), attr->length); |
908 |
|
|
free(sa); |
909 |
|
|
|
910 |
|
|
/* _SUBNET types need some extra work. */ |
911 |
|
|
if (attr->type == |
912 |
|
|
ISAKMP_CFG_ATTR_INTERNAL_IP4_SUBNET) { |
913 |
|
|
sa = conf_get_address(id_string, "Netmask"); |
914 |
|
|
if (!sa) { |
915 |
|
|
LOG_DBG((LOG_NEGOTIATION, 10, |
916 |
|
|
"cfg_responder_send_ATTR: " |
917 |
|
|
"attribute not found: Netmask")); |
918 |
|
|
attr->length = 0; |
919 |
|
|
break; |
920 |
|
|
} |
921 |
|
|
if (sa->sa_family != AF_INET) { |
922 |
|
|
log_print("cfg_responder_send_ATTR: " |
923 |
|
|
"attribute Netmask - expected " |
924 |
|
|
"IPv4 got IPv6 data"); |
925 |
|
|
free(sa); |
926 |
|
|
attr->length = 0; |
927 |
|
|
break; |
928 |
|
|
} |
929 |
|
|
memcpy(*attrp + off + ISAKMP_ATTR_VALUE_OFF + |
930 |
|
|
attr->length, sockaddr_addrdata(sa), |
931 |
|
|
attr->length); |
932 |
|
|
attr->length = 8; |
933 |
|
|
free(sa); |
934 |
|
|
} else if (attr->type == |
935 |
|
|
ISAKMP_CFG_ATTR_INTERNAL_IP6_SUBNET) { |
936 |
|
|
int prefix = conf_get_num(id_string, "Prefix", |
937 |
|
|
-1); |
938 |
|
|
|
939 |
|
|
if (prefix == -1) { |
940 |
|
|
log_print("cfg_responder_send_ATTR: " |
941 |
|
|
"attribute not found: Prefix"); |
942 |
|
|
attr->length = 0; |
943 |
|
|
break; |
944 |
|
|
} else if (prefix < -1 || prefix > 128) { |
945 |
|
|
log_print("cfg_responder_send_ATTR: " |
946 |
|
|
"attribute Prefix - invalid " |
947 |
|
|
"value %d", prefix); |
948 |
|
|
attr->length = 0; |
949 |
|
|
break; |
950 |
|
|
} |
951 |
|
|
*(*attrp + off + ISAKMP_ATTR_VALUE_OFF + 16) = |
952 |
|
|
(u_int8_t)prefix; |
953 |
|
|
attr->length = 17; |
954 |
|
|
} |
955 |
|
|
break; |
956 |
|
|
|
957 |
|
|
case ISAKMP_CFG_ATTR_INTERNAL_ADDRESS_EXPIRY: |
958 |
|
|
value = conf_get_num(id_string, "Lifetime", 1200); |
959 |
|
|
encode_32(*attrp + off + ISAKMP_ATTR_VALUE_OFF, value); |
960 |
|
|
break; |
961 |
|
|
|
962 |
|
|
case ISAKMP_CFG_ATTR_APPLICATION_VERSION: |
963 |
|
|
/* XXX So far no version identifier of isakmpd here. */ |
964 |
|
|
break; |
965 |
|
|
|
966 |
|
|
case ISAKMP_CFG_ATTR_SUPPORTED_ATTRIBUTES: |
967 |
|
|
break; |
968 |
|
|
|
969 |
|
|
default: |
970 |
|
|
break; |
971 |
|
|
} |
972 |
|
|
|
973 |
|
|
SET_ISAKMP_ATTR_TYPE(*attrp + off, attr->type); |
974 |
|
|
SET_ISAKMP_ATTR_LENGTH_VALUE(*attrp + off, attr->length); |
975 |
|
|
off += ISAKMP_ATTR_VALUE_OFF + attr->length; |
976 |
|
|
} |
977 |
|
|
|
978 |
|
|
return 0; |
979 |
|
|
} |