| File: | src/sbin/isakmpd/isakmp_cfg.c |
| Warning: | line 126, column 2 The left expression of the compound assignment is an uninitialized value. The computed value will also be garbage |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: isakmp_cfg.c,v 1.41 2018/01/15 09:54:48 mpi 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_ATTRIBUTE14, /* Initiator -> responder. */ | |||
| 60 | EXCHANGE_SCRIPT_SWITCH-3, | |||
| 61 | ISAKMP_PAYLOAD_ATTRIBUTE14, /* Responder -> initiator. */ | |||
| 62 | EXCHANGE_SCRIPT_END-4 | |||
| 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_MAX16 ISAKMP_CFG_ATTR_FUTURE_MIN16 /* XXX */ | |||
| 104 | bitstr_t bit_decl(attrbits, CFG_ATTR_BIT_MAX)((attrbits)[(((16) + 7) >> 3)]); | |||
| 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)do { register bitstr_t *__name = (attrbits); register int __start = (0), __stop = (16 - 1); while (__start <= __stop) { ((__name )[((__start) >> 3)] &= ~(1 << ((__start)& 0x7))); __start++; } } while(0); | |||
| ||||
| 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_SET3; | |||
| 132 | ||||
| 133 | LOG_DBG((LOG_NEGOTIATION, 10,log_debug (LOG_NEGOTIATION, 10, "cfg_initiator_send_ATTR: SET/ACK mode" ) | |||
| 134 | "cfg_initiator_send_ATTR: SET/ACK mode"))log_debug (LOG_NEGOTIATION, 10, "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((void *)0)) \ | |||
| 139 | switch (sa->sa_family) { \ | |||
| 140 | case AF_INET2: \ | |||
| 141 | bit_set (attrbits, ATTR4)((attrbits)[((ATTR4) >> 3)] |= (1 << ((ATTR4)& 0x7))); \ | |||
| 142 | attrlen += ISAKMP_ATTR_SZ4 + LEN4; \ | |||
| 143 | break; \ | |||
| 144 | case AF_INET624: \ | |||
| 145 | bit_set (attrbits, ATTR6)((attrbits)[((ATTR6) >> 3)] |= (1 << ((ATTR6)& 0x7))); \ | |||
| 146 | attrlen += ISAKMP_ATTR_SZ4 + 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_ADDRESS1, 4, | |||
| 159 | ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS8, 16); | |||
| 160 | ATTRFIND("Netmask", ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK2, 4, | |||
| 161 | ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK9, 16); | |||
| 162 | ATTRFIND("Nameserver", ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS3, 4, | |||
| 163 | ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS10, 16); | |||
| 164 | ATTRFIND("WINS-server", ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS4, 4, | |||
| 165 | ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS11, 16); | |||
| 166 | ATTRFIND("DHCP-server", ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP6, 4, | |||
| 167 | ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP12, 16); | |||
| 168 | #ifdef notyet | |||
| 169 | ATTRFIND("Network", ISAKMP_CFG_ATTR_INTERNAL_IP4_SUBNET13, 8, | |||
| 170 | ISAKMP_CFG_ATTR_INTERNAL_IP6_SUBNET15, 17); | |||
| 171 | #endif | |||
| 172 | #undef ATTRFIND | |||
| 173 | ||||
| 174 | if (conf_get_str(id_string, "Lifetime")) { | |||
| 175 | bit_set(attrbits,((attrbits)[((5) >> 3)] |= (1 << ((5)&0x7))) | |||
| 176 | ISAKMP_CFG_ATTR_INTERNAL_ADDRESS_EXPIRY)((attrbits)[((5) >> 3)] |= (1 << ((5)&0x7))); | |||
| 177 | attrlen += ISAKMP_ATTR_SZ4 + 4; | |||
| 178 | } | |||
| 179 | } else { | |||
| 180 | struct conf_list *alist; | |||
| 181 | struct conf_list_node *anode; | |||
| 182 | ||||
| 183 | ie->cfg_type = ISAKMP_CFG_REQUEST1; | |||
| 184 | ||||
| 185 | LOG_DBG((LOG_NEGOTIATION, 10,log_debug (LOG_NEGOTIATION, 10, "cfg_initiator_send_ATTR: REQ/REPLY mode" ) | |||
| 186 | "cfg_initiator_send_ATTR: REQ/REPLY mode"))log_debug (LOG_NEGOTIATION, 10, "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)((&alist->fields)->tqh_first); anode; | |||
| 191 | anode = TAILQ_NEXT(anode, link)((anode)->link.tqe_next)) { | |||
| 192 | if (strcasecmp(anode->field, "Address") == 0) { | |||
| 193 | bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS)((attrbits)[((1) >> 3)] |= (1 << ((1)&0x7))); | |||
| 194 | bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS)((attrbits)[((8) >> 3)] |= (1 << ((8)&0x7))); | |||
| 195 | attrlen += ISAKMP_ATTR_SZ4 * 2; | |||
| 196 | } else if (strcasecmp(anode->field, "Netmask") | |||
| 197 | == 0) { | |||
| 198 | bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK)((attrbits)[((2) >> 3)] |= (1 << ((2)&0x7))); | |||
| 199 | bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK)((attrbits)[((9) >> 3)] |= (1 << ((9)&0x7))); | |||
| 200 | attrlen += ISAKMP_ATTR_SZ4 * 2; | |||
| 201 | } else if (strcasecmp(anode->field, | |||
| 202 | "Nameserver") == 0) { | |||
| 203 | bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS)((attrbits)[((3) >> 3)] |= (1 << ((3)&0x7))); | |||
| 204 | bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS)((attrbits)[((10) >> 3)] |= (1 << ((10)&0x7)) ); | |||
| 205 | attrlen += ISAKMP_ATTR_SZ4 * 2; | |||
| 206 | } else if (strcasecmp(anode->field, | |||
| 207 | "WINS-server") == 0) { | |||
| 208 | bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS)((attrbits)[((4) >> 3)] |= (1 << ((4)&0x7))); | |||
| 209 | bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS)((attrbits)[((11) >> 3)] |= (1 << ((11)&0x7)) ); | |||
| 210 | attrlen += ISAKMP_ATTR_SZ4 * 2; | |||
| 211 | } else if (strcasecmp(anode->field, | |||
| 212 | "DHCP-server") == 0) { | |||
| 213 | bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP)((attrbits)[((6) >> 3)] |= (1 << ((6)&0x7))); | |||
| 214 | bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP)((attrbits)[((12) >> 3)] |= (1 << ((12)&0x7)) ); | |||
| 215 | attrlen += ISAKMP_ATTR_SZ4 * 2; | |||
| 216 | } else if (strcasecmp(anode->field, | |||
| 217 | "Lifetime") == 0) { | |||
| 218 | bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_ADDRESS_EXPIRY)((attrbits)[((5) >> 3)] |= (1 << ((5)&0x7))); | |||
| 219 | attrlen += ISAKMP_ATTR_SZ4; | |||
| 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_SZ8; | |||
| 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_ATTRIBUTE14, attrp, attrlen, | |||
| 252 | 1)) { | |||
| 253 | free(attrp); | |||
| 254 | goto fail; | |||
| 255 | } | |||
| 256 | SET_ISAKMP_ATTRIBUTE_TYPE(attrp, ie->cfg_type)field_set_num (isakmp_attribute_fld + 0, 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)field_set_num (isakmp_attribute_fld + 2, attrp, ie->cfg_id ); | |||
| 259 | ||||
| 260 | off = ISAKMP_ATTRIBUTE_SZ8; | |||
| 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_MAX16; bit++) | |||
| 267 | if (bit_test(attrbits, bit)((attrbits)[((bit) >> 3)] & (1 << ((bit)& 0x7)))) { | |||
| 268 | attr = attrp + off; | |||
| 269 | SET_ISAKMP_ATTR_TYPE(attr, bit)field_set_num (isakmp_attr_fld + 0, attr, bit); | |||
| 270 | ||||
| 271 | if (ie->cfg_type == ISAKMP_CFG_REQUEST1) { | |||
| 272 | off += ISAKMP_ATTR_SZ4; | |||
| 273 | continue; | |||
| 274 | } | |||
| 275 | /* All the other are similar, this is the odd one. */ | |||
| 276 | if (bit == ISAKMP_CFG_ATTR_INTERNAL_ADDRESS_EXPIRY5) { | |||
| 277 | life = conf_get_num(id_string, "Lifetime", | |||
| 278 | 1200); | |||
| 279 | SET_ISAKMP_ATTR_LENGTH_VALUE(attr, 4)field_set_num (isakmp_attr_fld + 1, attr, 4); | |||
| 280 | encode_32(attr + ISAKMP_ATTR_VALUE_OFF4, life); | |||
| 281 | off += ISAKMP_ATTR_SZ4 + 4; | |||
| 282 | continue; | |||
| 283 | } | |||
| 284 | switch (bit) { | |||
| 285 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS1: | |||
| 286 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK2: | |||
| 287 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS3: | |||
| 288 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP6: | |||
| 289 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS4: | |||
| 290 | length = 4; | |||
| 291 | break; | |||
| 292 | ||||
| 293 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS8: | |||
| 294 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK9: | |||
| 295 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS10: | |||
| 296 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP12: | |||
| 297 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS11: | |||
| 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_ADDRESS1: | |||
| 307 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS8: | |||
| 308 | field = "Address"; | |||
| 309 | break; | |||
| 310 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK2: | |||
| 311 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK9: | |||
| 312 | field = "Netmask"; | |||
| 313 | break; | |||
| 314 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS3: | |||
| 315 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS10: | |||
| 316 | field = "Nameserver"; | |||
| 317 | break; | |||
| 318 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP6: | |||
| 319 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP12: | |||
| 320 | field = "DHCP-server"; | |||
| 321 | break; | |||
| 322 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS4: | |||
| 323 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS11: | |||
| 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)field_set_num (isakmp_attr_fld + 1, attr, length); | |||
| 333 | memcpy(attr + ISAKMP_ATTR_VALUE_OFF4, | |||
| 334 | sockaddr_addrdata(sa), length); | |||
| 335 | ||||
| 336 | free(sa); | |||
| 337 | ||||
| 338 | off += ISAKMP_ATTR_SZ4 + 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_ATTRIBUTE14); | |||
| 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)field_get_num (isakmp_attribute_fld + 2, attrp->p)) { | |||
| 372 | log_print("cfg_initiator_recv_ATTR: " | |||
| 373 | "cfg packet ID does not match!"); | |||
| 374 | message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED16, 0, 1, 0); | |||
| 375 | return -1; | |||
| 376 | } | |||
| 377 | switch (attrp->p[ISAKMP_ATTRIBUTE_TYPE_OFF4]) { | |||
| 378 | case ISAKMP_CFG_ACK4: | |||
| 379 | if (ie->cfg_type != ISAKMP_CFG_SET3) { | |||
| 380 | log_print("cfg_initiator_recv_ATTR: " | |||
| 381 | "bad packet type ACK"); | |||
| 382 | message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED16, | |||
| 383 | 0, 1, 0); | |||
| 384 | return -1; | |||
| 385 | } | |||
| 386 | break; | |||
| 387 | case ISAKMP_CFG_REPLY2: | |||
| 388 | if (ie->cfg_type != ISAKMP_CFG_REQUEST1) { | |||
| 389 | log_print("cfg_initiator_recv_ATTR: " | |||
| 390 | "bad packet type REPLY"); | |||
| 391 | message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED16, | |||
| 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_OFF4]); | |||
| 400 | message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED16, 0, 1, 0); | |||
| 401 | return -1; | |||
| 402 | } | |||
| 403 | ||||
| 404 | attribute_map(attrp->p + ISAKMP_ATTRIBUTE_ATTRS_OFF8, | |||
| 405 | GET_ISAKMP_GEN_LENGTH(attrp->p)field_get_num (isakmp_gen_fld + 2, attrp->p) - ISAKMP_TRANSFORM_SA_ATTRS_OFF8, | |||
| 406 | cfg_decode_attribute, ie); | |||
| 407 | ||||
| 408 | switch (ie->cfg_type) { | |||
| 409 | case ISAKMP_CFG_ACK4: { | |||
| 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)((&ie->attrs)->lh_first); attr; | |||
| 417 | attr = LIST_NEXT(attr, link)((attr)->link.le_next)) | |||
| 418 | LOG_DBG((LOG_NEGOTIATION, 50,log_debug (LOG_NEGOTIATION, 50, "cfg_initiator_recv_ATTR: " "client %s ACKs attribute %s" , addr, constant_name(isakmp_cfg_attr_cst, attr->type)) | |||
| 419 | "cfg_initiator_recv_ATTR: "log_debug (LOG_NEGOTIATION, 50, "cfg_initiator_recv_ATTR: " "client %s ACKs attribute %s" , addr, constant_name(isakmp_cfg_attr_cst, attr->type)) | |||
| 420 | "client %s ACKs attribute %s", addr,log_debug (LOG_NEGOTIATION, 50, "cfg_initiator_recv_ATTR: " "client %s ACKs attribute %s" , addr, constant_name(isakmp_cfg_attr_cst, attr->type)) | |||
| 421 | constant_name(isakmp_cfg_attr_cst,log_debug (LOG_NEGOTIATION, 50, "cfg_initiator_recv_ATTR: " "client %s ACKs attribute %s" , addr, constant_name(isakmp_cfg_attr_cst, attr->type)) | |||
| 422 | attr->type)))log_debug (LOG_NEGOTIATION, 50, "cfg_initiator_recv_ATTR: " "client %s ACKs attribute %s" , addr, constant_name(isakmp_cfg_attr_cst, attr->type)); | |||
| 423 | ||||
| 424 | if (addr != uk_addr) | |||
| 425 | free(addr); | |||
| 426 | } | |||
| 427 | break; | |||
| 428 | ||||
| 429 | case ISAKMP_CFG_REPLY2: { | |||
| 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)((&ie->attrs)->lh_first); attr; | |||
| 440 | attr = LIST_NEXT(attr, link)((attr)->link.le_next)) | |||
| 441 | LOG_DBG((LOG_NEGOTIATION, 50,log_debug (LOG_NEGOTIATION, 50, "cfg_initiator_recv_ATTR: " "server %s replied with attribute %s" , addr, constant_name(isakmp_cfg_attr_cst, attr->type)) | |||
| 442 | "cfg_initiator_recv_ATTR: "log_debug (LOG_NEGOTIATION, 50, "cfg_initiator_recv_ATTR: " "server %s replied with attribute %s" , addr, constant_name(isakmp_cfg_attr_cst, attr->type)) | |||
| 443 | "server %s replied with attribute %s",log_debug (LOG_NEGOTIATION, 50, "cfg_initiator_recv_ATTR: " "server %s replied with attribute %s" , addr, constant_name(isakmp_cfg_attr_cst, attr->type)) | |||
| 444 | addr, constant_name(isakmp_cfg_attr_cst,log_debug (LOG_NEGOTIATION, 50, "cfg_initiator_recv_ATTR: " "server %s replied with attribute %s" , addr, constant_name(isakmp_cfg_attr_cst, attr->type)) | |||
| 445 | attr->type)))log_debug (LOG_NEGOTIATION, 50, "cfg_initiator_recv_ATTR: " "server %s replied with attribute %s" , addr, constant_name(isakmp_cfg_attr_cst, 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_MARK1; | |||
| 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_ATTRIBUTE14); | |||
| 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)field_get_num (isakmp_attribute_fld + 2, attrp->p); | |||
| 479 | ie->cfg_type = attrp->p[ISAKMP_ATTRIBUTE_TYPE_OFF4]; | |||
| 480 | ||||
| 481 | switch (ie->cfg_type) { | |||
| 482 | case ISAKMP_CFG_REQUEST1: | |||
| 483 | case ISAKMP_CFG_SET3: | |||
| 484 | break; | |||
| 485 | ||||
| 486 | default: | |||
| 487 | message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED16, 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_OFF8, | |||
| 494 | GET_ISAKMP_GEN_LENGTH(attrp->p)field_get_num (isakmp_gen_fld + 2, attrp->p) - ISAKMP_TRANSFORM_SA_ATTRS_OFF8, | |||
| 495 | cfg_decode_attribute, ie); | |||
| 496 | ||||
| 497 | switch (ie->cfg_type) { | |||
| 498 | case ISAKMP_CFG_REQUEST1: | |||
| 499 | /* We're done. */ | |||
| 500 | break; | |||
| 501 | ||||
| 502 | case ISAKMP_CFG_SET3: { | |||
| 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)((&ie->attrs)->lh_first); attr; | |||
| 512 | attr = LIST_NEXT(attr, link)((attr)->link.le_next)) | |||
| 513 | LOG_DBG((LOG_NEGOTIATION, 50,log_debug (LOG_NEGOTIATION, 50, "cfg_responder_recv_ATTR: " "server %s asks us to SET attribute %s" , addr, constant_name(isakmp_cfg_attr_cst, attr->type)) | |||
| 514 | "cfg_responder_recv_ATTR: "log_debug (LOG_NEGOTIATION, 50, "cfg_responder_recv_ATTR: " "server %s asks us to SET attribute %s" , addr, constant_name(isakmp_cfg_attr_cst, attr->type)) | |||
| 515 | "server %s asks us to SET attribute %s",log_debug (LOG_NEGOTIATION, 50, "cfg_responder_recv_ATTR: " "server %s asks us to SET attribute %s" , addr, constant_name(isakmp_cfg_attr_cst, attr->type)) | |||
| 516 | addr, constant_name(isakmp_cfg_attr_cst,log_debug (LOG_NEGOTIATION, 50, "cfg_responder_recv_ATTR: " "server %s asks us to SET attribute %s" , addr, constant_name(isakmp_cfg_attr_cst, attr->type)) | |||
| 517 | attr->type)))log_debug (LOG_NEGOTIATION, 50, "cfg_responder_recv_ATTR: " "server %s asks us to SET attribute %s" , addr, constant_name(isakmp_cfg_attr_cst, 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_MARK1; | |||
| 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_REQUEST1)) | |||
| 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_SET3 ? | |||
| 571 | ISAKMP_CFG_ACK4 : ISAKMP_CFG_REPLY2), 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_ATTRIBUTE14, 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_SZ4 + hash->hashsize); | |||
| 598 | if (!hashp) { | |||
| 599 | log_error("cfg_add_hash: malloc (%lu) failed", | |||
| 600 | ISAKMP_HASH_SZ4 + (unsigned long)hash->hashsize); | |||
| 601 | return 0; | |||
| 602 | } | |||
| 603 | if (message_add_payload(msg, ISAKMP_PAYLOAD_HASH8, hashp, | |||
| 604 | ISAKMP_HASH_SZ4 + 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_LEN4); | |||
| 626 | prf->Update(prf->prfctx, data, length); | |||
| 627 | prf->Final(hashp + ISAKMP_GEN_SZ4, 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_HASH8); | |||
| 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_INFORMATION23, | |||
| 644 | 0, 1, 0); | |||
| 645 | return -1; | |||
| 646 | } | |||
| 647 | hash = hashp->p; | |||
| 648 | hash_len = GET_ISAKMP_GEN_LENGTH(hash)field_get_num (isakmp_gen_fld + 2, hash); | |||
| 649 | comp_hash = malloc(hash_len - ISAKMP_GEN_SZ4); | |||
| 650 | if (!comp_hash) { | |||
| 651 | log_error("cfg_verify_hash: malloc (%lu) failed", | |||
| 652 | (unsigned long)hash_len - ISAKMP_GEN_SZ4); | |||
| 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_LEN4); | |||
| 665 | prf->Update(prf->prfctx, hash + hash_len, | |||
| 666 | msg->iov[0].iov_len - ISAKMP_HDR_SZ28 - hash_len); | |||
| 667 | prf->Final(comp_hash, prf->prfctx); | |||
| 668 | prf_free(prf); | |||
| 669 | ||||
| 670 | if (memcmp(hash + ISAKMP_GEN_SZ4, comp_hash, hash_len - ISAKMP_GEN_SZ4) | |||
| 671 | != 0) { | |||
| 672 | message_drop(msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION23, | |||
| 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_MARK1; | |||
| 681 | ||||
| 682 | /* Mark message authenticated. */ | |||
| 683 | msg->flags |= MSG_AUTHENTICATED0x10; | |||
| 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_MIN16384 && | |||
| 701 | type <= ISAKMP_CFG_ATTR_PRIVATE_MAX32767) | |||
| 702 | return 0; | |||
| 703 | if (type == 0 || type >= ISAKMP_CFG_ATTR_FUTURE_MIN16) { | |||
| 704 | LOG_DBG((LOG_NEGOTIATION, 30,log_debug (LOG_NEGOTIATION, 30, "cfg_decode_attribute: invalid attr type %u" , type) | |||
| 705 | "cfg_decode_attribute: invalid attr type %u", type))log_debug (LOG_NEGOTIATION, 30, "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)do { if (((attr)->link.le_next = (&ie->attrs)->lh_first ) != ((void *)0)) (&ie->attrs)->lh_first->link.le_prev = &(attr)->link.le_next; (&ie->attrs)->lh_first = (attr); (attr)->link.le_prev = &(&ie->attrs) ->lh_first; } while (0); | |||
| 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_SZ8; | |||
| 747 | for (attr = LIST_FIRST(attrs)((attrs)->lh_first); attr; attr = LIST_NEXT(attr, link)((attr)->link.le_next)) { | |||
| 748 | /* With ACK we only include the attrs we've actually used. */ | |||
| 749 | if (type == ISAKMP_CFG_ACK4 && attr->attr_used == 0) | |||
| 750 | continue; | |||
| 751 | ||||
| 752 | switch (attr->type) { | |||
| 753 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS1: | |||
| 754 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK2: | |||
| 755 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP6: | |||
| 756 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS3: | |||
| 757 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS4: | |||
| 758 | case ISAKMP_CFG_ATTR_INTERNAL_ADDRESS_EXPIRY5: | |||
| 759 | attr->length = 4; | |||
| 760 | break; | |||
| 761 | ||||
| 762 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_SUBNET13: | |||
| 763 | attr->length = 8; | |||
| 764 | break; | |||
| 765 | ||||
| 766 | case ISAKMP_CFG_ATTR_APPLICATION_VERSION7: | |||
| 767 | /* XXX So far no version identifier of isakmpd here. */ | |||
| 768 | attr->length = 0; | |||
| 769 | break; | |||
| 770 | ||||
| 771 | case ISAKMP_CFG_ATTR_SUPPORTED_ATTRIBUTES14: | |||
| 772 | attr->length = 2 * 15; | |||
| 773 | break; | |||
| 774 | ||||
| 775 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS8: | |||
| 776 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK9: | |||
| 777 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP12: | |||
| 778 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS10: | |||
| 779 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS11: | |||
| 780 | attr->length = 16; | |||
| 781 | break; | |||
| 782 | ||||
| 783 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_SUBNET15: | |||
| 784 | attr->length = 17; | |||
| 785 | break; | |||
| 786 | ||||
| 787 | default: | |||
| 788 | attr->ignore++; | |||
| 789 | /* XXX Log! */ | |||
| 790 | } | |||
| 791 | *len += ISAKMP_ATTR_SZ4 + 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)field_set_num (isakmp_attribute_fld + 0, *attrp, type); | |||
| 802 | SET_ISAKMP_ATTRIBUTE_ID(*attrp, cfg_id)field_set_num (isakmp_attribute_fld + 2, *attrp, cfg_id); | |||
| 803 | ||||
| 804 | off = ISAKMP_ATTRIBUTE_SZ8; | |||
| 805 | for (attr = LIST_FIRST(attrs)((attrs)->lh_first); attr; attr = LIST_NEXT(attr, link)((attr)->link.le_next)) { | |||
| 806 | /* With ACK we only include the attrs we've actually used. */ | |||
| 807 | if (type == ISAKMP_CFG_ACK4 && attr->attr_used == 0) | |||
| 808 | continue; | |||
| 809 | ||||
| 810 | switch (attr->type) { | |||
| 811 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS1: | |||
| 812 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK2: | |||
| 813 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_SUBNET13: | |||
| 814 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP6: | |||
| 815 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS3: | |||
| 816 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS4: | |||
| 817 | family = AF_INET2; | |||
| 818 | break; | |||
| 819 | ||||
| 820 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS8: | |||
| 821 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK9: | |||
| 822 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_SUBNET15: | |||
| 823 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP12: | |||
| 824 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS10: | |||
| 825 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS11: | |||
| 826 | family = AF_INET624; | |||
| 827 | break; | |||
| 828 | ||||
| 829 | default: | |||
| 830 | family = 0; | |||
| 831 | break; | |||
| 832 | } | |||
| 833 | ||||
| 834 | switch (attr->type) { | |||
| 835 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS1: | |||
| 836 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS8: | |||
| 837 | field = "Address"; | |||
| 838 | break; | |||
| 839 | ||||
| 840 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_SUBNET13: | |||
| 841 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_SUBNET15: | |||
| 842 | field = "Network"; /* XXX or just "Address" */ | |||
| 843 | break; | |||
| 844 | ||||
| 845 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK2: | |||
| 846 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK9: | |||
| 847 | field = "Netmask"; | |||
| 848 | break; | |||
| 849 | ||||
| 850 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP6: | |||
| 851 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP12: | |||
| 852 | field = "DHCP-server"; | |||
| 853 | break; | |||
| 854 | ||||
| 855 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS3: | |||
| 856 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS10: | |||
| 857 | field = "Nameserver"; | |||
| 858 | break; | |||
| 859 | ||||
| 860 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS4: | |||
| 861 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS11: | |||
| 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_ADDRESS1: | |||
| 871 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS8: | |||
| 872 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK2: | |||
| 873 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK9: | |||
| 874 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP6: | |||
| 875 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP12: | |||
| 876 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS3: | |||
| 877 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS10: | |||
| 878 | case ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS4: | |||
| 879 | case ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS11: | |||
| 880 | sa = conf_get_address(id_string, field); | |||
| 881 | if (!sa) { | |||
| 882 | LOG_DBG((LOG_NEGOTIATION, 10,log_debug (LOG_NEGOTIATION, 10, "cfg_responder_send_ATTR: " "attribute not found: %s" , field) | |||
| 883 | "cfg_responder_send_ATTR: "log_debug (LOG_NEGOTIATION, 10, "cfg_responder_send_ATTR: " "attribute not found: %s" , field) | |||
| 884 | "attribute not found: %s", field))log_debug (LOG_NEGOTIATION, 10, "cfg_responder_send_ATTR: " "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_INET2 ? "IPv4" : "IPv6"), | |||
| 893 | (sa->sa_family == | |||
| 894 | AF_INET2 ? "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_SUBNET13) | |||
| 901 | attr->length = 4; | |||
| 902 | else if (attr->type == | |||
| 903 | ISAKMP_CFG_ATTR_INTERNAL_IP6_SUBNET15) | |||
| 904 | attr->length = 16; | |||
| 905 | ||||
| 906 | memcpy(*attrp + off + ISAKMP_ATTR_VALUE_OFF4, | |||
| 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_SUBNET13) { | |||
| 913 | sa = conf_get_address(id_string, "Netmask"); | |||
| 914 | if (!sa) { | |||
| 915 | LOG_DBG((LOG_NEGOTIATION, 10,log_debug (LOG_NEGOTIATION, 10, "cfg_responder_send_ATTR: " "attribute not found: Netmask" ) | |||
| 916 | "cfg_responder_send_ATTR: "log_debug (LOG_NEGOTIATION, 10, "cfg_responder_send_ATTR: " "attribute not found: Netmask" ) | |||
| 917 | "attribute not found: Netmask"))log_debug (LOG_NEGOTIATION, 10, "cfg_responder_send_ATTR: " "attribute not found: Netmask" ); | |||
| 918 | attr->length = 0; | |||
| 919 | break; | |||
| 920 | } | |||
| 921 | if (sa->sa_family != AF_INET2) { | |||
| 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_OFF4 + | |||
| 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_SUBNET15) { | |||
| 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_OFF4 + 16) = | |||
| 952 | (u_int8_t)prefix; | |||
| 953 | attr->length = 17; | |||
| 954 | } | |||
| 955 | break; | |||
| 956 | ||||
| 957 | case ISAKMP_CFG_ATTR_INTERNAL_ADDRESS_EXPIRY5: | |||
| 958 | value = conf_get_num(id_string, "Lifetime", 1200); | |||
| 959 | encode_32(*attrp + off + ISAKMP_ATTR_VALUE_OFF4, value); | |||
| 960 | break; | |||
| 961 | ||||
| 962 | case ISAKMP_CFG_ATTR_APPLICATION_VERSION7: | |||
| 963 | /* XXX So far no version identifier of isakmpd here. */ | |||
| 964 | break; | |||
| 965 | ||||
| 966 | case ISAKMP_CFG_ATTR_SUPPORTED_ATTRIBUTES14: | |||
| 967 | break; | |||
| 968 | ||||
| 969 | default: | |||
| 970 | break; | |||
| 971 | } | |||
| 972 | ||||
| 973 | SET_ISAKMP_ATTR_TYPE(*attrp + off, attr->type)field_set_num (isakmp_attr_fld + 0, *attrp + off, attr->type ); | |||
| 974 | SET_ISAKMP_ATTR_LENGTH_VALUE(*attrp + off, attr->length)field_set_num (isakmp_attr_fld + 1, *attrp + off, attr->length ); | |||
| 975 | off += ISAKMP_ATTR_VALUE_OFF4 + attr->length; | |||
| 976 | } | |||
| 977 | ||||
| 978 | return 0; | |||
| 979 | } |