xref: /openbsd-src/sbin/isakmpd/isakmp_cfg.c (revision 2f1aa25b0f696ec888d7fe80b22e760eed552d21)
1*2f1aa25bSmpi /* $OpenBSD: isakmp_cfg.c,v 1.41 2018/01/15 09:54:48 mpi Exp $	 */
23635a927Sniklas 
33635a927Sniklas /*
43635a927Sniklas  * Copyright (c) 2001 Niklas Hallqvist.  All rights reserved.
5d6351a66Sho  * Copyright (c) 2002 H�kan Olsson.  All rights reserved.
63635a927Sniklas  *
73635a927Sniklas  * Redistribution and use in source and binary forms, with or without
83635a927Sniklas  * modification, are permitted provided that the following conditions
93635a927Sniklas  * are met:
103635a927Sniklas  * 1. Redistributions of source code must retain the above copyright
113635a927Sniklas  *    notice, this list of conditions and the following disclaimer.
123635a927Sniklas  * 2. Redistributions in binary form must reproduce the above copyright
133635a927Sniklas  *    notice, this list of conditions and the following disclaimer in the
143635a927Sniklas  *    documentation and/or other materials provided with the distribution.
153635a927Sniklas  *
163635a927Sniklas  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
173635a927Sniklas  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
183635a927Sniklas  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
193635a927Sniklas  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
203635a927Sniklas  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
213635a927Sniklas  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
223635a927Sniklas  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
233635a927Sniklas  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
243635a927Sniklas  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
253635a927Sniklas  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
263635a927Sniklas  */
273635a927Sniklas 
283635a927Sniklas /*
293635a927Sniklas  * This code was written under funding by Gatespace
303635a927Sniklas  * (http://www.gatespace.com/).
313635a927Sniklas  */
323635a927Sniklas 
333635a927Sniklas #include <sys/types.h>
34bf2962d5Sderaadt #include <stdlib.h>
353635a927Sniklas #include <netinet/in.h>
363635a927Sniklas #include <arpa/inet.h>
37bf2962d5Sderaadt #include <string.h>
38d6351a66Sho #include <bitstring.h>
393635a927Sniklas 
403635a927Sniklas #include "attribute.h"
413635a927Sniklas #include "conf.h"
423635a927Sniklas #include "exchange.h"
433635a927Sniklas #include "hash.h"
443635a927Sniklas #include "ipsec.h"
453635a927Sniklas #include "isakmp_fld.h"
463635a927Sniklas #include "isakmp_num.h"
473635a927Sniklas #include "log.h"
483635a927Sniklas #include "message.h"
493635a927Sniklas #include "prf.h"
503635a927Sniklas #include "sa.h"
51d6351a66Sho #include "transport.h"
523635a927Sniklas #include "util.h"
533635a927Sniklas 
543635a927Sniklas /*
553635a927Sniklas  * Validation script used to test messages for correct content of
563635a927Sniklas  * payloads depending on the exchange type.
573635a927Sniklas  */
583635a927Sniklas int16_t script_transaction[] = {
593635a927Sniklas 	ISAKMP_PAYLOAD_ATTRIBUTE,	/* Initiator -> responder.  */
603635a927Sniklas 	EXCHANGE_SCRIPT_SWITCH,
613635a927Sniklas 	ISAKMP_PAYLOAD_ATTRIBUTE,	/* Responder -> initiator.  */
623635a927Sniklas 	EXCHANGE_SCRIPT_END
633635a927Sniklas };
643635a927Sniklas 
65d6351a66Sho static int      cfg_decode_attribute(u_int16_t, u_int8_t *, u_int16_t, void *);
6612f43dabShshoexer static int	cfg_encode_attributes(struct isakmp_cfg_attr_head *, u_int32_t,
679c59062cSho     u_int32_t, char *, u_int8_t **, u_int16_t *);
68d6351a66Sho static int      cfg_initiator_send_ATTR(struct message *);
69d6351a66Sho static int      cfg_initiator_recv_ATTR(struct message *);
70d6351a66Sho static int      cfg_responder_recv_ATTR(struct message *);
71d6351a66Sho static int      cfg_responder_send_ATTR(struct message *);
723635a927Sniklas 
739c59062cSho u_int8_t       *cfg_add_hash(struct message *);
7412f43dabShshoexer int		cfg_finalize_hash(struct message *, u_int8_t *, u_int8_t *,
759c59062cSho     u_int16_t);
76f0a8bf13Sho int             cfg_verify_hash(struct message *);
779c59062cSho 
78d6351a66Sho /* Server: SET/ACK    Client; REQ/REPLY */
793635a927Sniklas int (*isakmp_cfg_initiator[]) (struct message *) = {
80d6351a66Sho 	cfg_initiator_send_ATTR,
81d6351a66Sho 	cfg_initiator_recv_ATTR
823635a927Sniklas };
833635a927Sniklas 
84d6351a66Sho /* Server: REQ/REPLY  Client: SET/ACK */
853635a927Sniklas int (*isakmp_cfg_responder[]) (struct message *) = {
86d6351a66Sho 	cfg_responder_recv_ATTR,
87d6351a66Sho 	cfg_responder_send_ATTR
883635a927Sniklas };
893635a927Sniklas 
90d6351a66Sho /*
9162e85ae0Sho  * When we are "the server", this starts SET/ACK mode
9262e85ae0Sho  * When we are "the client", this starts REQ/REPLY mode
93d6351a66Sho  */
943635a927Sniklas static int
cfg_initiator_send_ATTR(struct message * msg)95d6351a66Sho cfg_initiator_send_ATTR(struct message *msg)
963635a927Sniklas {
973635a927Sniklas 	struct sa      *isakmp_sa = msg->isakmp_sa;
989c59062cSho 	struct ipsec_exch *ie = msg->exchange->data;
99d6351a66Sho 	u_int8_t       *hashp = 0, *attrp, *attr;
100d6351a66Sho 	size_t          attrlen, off;
101d6351a66Sho 	char           *id_string, *cfg_mode, *field;
102d6351a66Sho 	struct sockaddr *sa;
103d6351a66Sho #define CFG_ATTR_BIT_MAX ISAKMP_CFG_ATTR_FUTURE_MIN	/* XXX */
104d6351a66Sho 	bitstr_t        bit_decl(attrbits, CFG_ATTR_BIT_MAX);
1054292de0eSho 	u_int16_t       bit, length;
1064292de0eSho 	u_int32_t       life;
1073635a927Sniklas 
108fb9475d6Sderaadt 	if (msg->exchange->phase == 2) {
1099c59062cSho 		hashp = cfg_add_hash(msg);
1103635a927Sniklas 		if (!hashp)
1113635a927Sniklas 			return -1;
1123635a927Sniklas 	}
113cd659179Smiod 	/* We initiated this exchange, check isakmp_sa for other side.  */
1149c59062cSho 	if (isakmp_sa->initiator)
11512f43dabShshoexer 		id_string = ipsec_id_string(isakmp_sa->id_r,
11612f43dabShshoexer 		    isakmp_sa->id_r_len);
1179c59062cSho 	else
11812f43dabShshoexer 		id_string = ipsec_id_string(isakmp_sa->id_i,
11912f43dabShshoexer 		    isakmp_sa->id_i_len);
120fb9475d6Sderaadt 	if (!id_string) {
121d6351a66Sho 		log_print("cfg_initiator_send_ATTR: cannot parse ID");
122d6351a66Sho 		goto fail;
123d6351a66Sho 	}
124d6351a66Sho 	/* Check for attribute list to send to the other side */
1253635a927Sniklas 	attrlen = 0;
126d6351a66Sho 	bit_nclear(attrbits, 0, CFG_ATTR_BIT_MAX - 1);
127d6351a66Sho 
128d6351a66Sho 	cfg_mode = conf_get_str(id_string, "Mode");
129fb9475d6Sderaadt 	if (!cfg_mode || strcmp(cfg_mode, "SET") == 0) {
130d6351a66Sho 		/* SET/ACK mode */
1314292de0eSho 		ie->cfg_type = ISAKMP_CFG_SET;
1324292de0eSho 
13312f43dabShshoexer 		LOG_DBG((LOG_NEGOTIATION, 10,
13412f43dabShshoexer 		    "cfg_initiator_send_ATTR: SET/ACK mode"));
135d6351a66Sho 
136d6351a66Sho #define ATTRFIND(STR,ATTR4,LEN4,ATTR6,LEN6) do				\
137d6351a66Sho 	{								\
138d6351a66Sho 		if ((sa = conf_get_address (id_string, STR)) != NULL)	\
13912f43dabShshoexer 			switch (sa->sa_family) {			\
140d6351a66Sho 			case AF_INET:					\
141d6351a66Sho 				bit_set (attrbits, ATTR4);		\
142d6351a66Sho 				attrlen += ISAKMP_ATTR_SZ + LEN4;	\
143d6351a66Sho 				break;					\
144d6351a66Sho 			case AF_INET6:					\
145d6351a66Sho 				bit_set (attrbits, ATTR6);		\
146d6351a66Sho 				attrlen += ISAKMP_ATTR_SZ + LEN6;	\
147d6351a66Sho 				break;					\
148d6351a66Sho 			default:					\
1491ae0ca71Sho 				break;					\
150d6351a66Sho 			}						\
151d6351a66Sho 		free (sa);						\
152d6351a66Sho 	} while (0)
153d6351a66Sho 
154fb9475d6Sderaadt 		/*
155fb9475d6Sderaadt 		 * XXX We don't simultaneously support IPv4 and IPv6
156fb9475d6Sderaadt 		 * addresses.
157fb9475d6Sderaadt 		 */
158d6351a66Sho 		ATTRFIND("Address", ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS, 4,
159d6351a66Sho 		    ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS, 16);
160d6351a66Sho 		ATTRFIND("Netmask", ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK, 4,
161d6351a66Sho 		    ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK, 16);
162d6351a66Sho 		ATTRFIND("Nameserver", ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS, 4,
163d6351a66Sho 		    ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS, 16);
164d6351a66Sho 		ATTRFIND("WINS-server", ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS, 4,
165d6351a66Sho 		    ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS, 16);
166d6351a66Sho 		ATTRFIND("DHCP-server", ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP, 4,
167d6351a66Sho 		    ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP, 16);
168d6351a66Sho #ifdef notyet
169d6351a66Sho 		ATTRFIND("Network", ISAKMP_CFG_ATTR_INTERNAL_IP4_SUBNET, 8,
170d6351a66Sho 		    ISAKMP_CFG_ATTR_INTERNAL_IP6_SUBNET, 17);
1713635a927Sniklas #endif
172d6351a66Sho #undef ATTRFIND
173d6351a66Sho 
174fb9475d6Sderaadt 		if (conf_get_str(id_string, "Lifetime")) {
17512f43dabShshoexer 			bit_set(attrbits,
17612f43dabShshoexer 			    ISAKMP_CFG_ATTR_INTERNAL_ADDRESS_EXPIRY);
177d6351a66Sho 			attrlen += ISAKMP_ATTR_SZ + 4;
178d6351a66Sho 		}
179fb9475d6Sderaadt 	} else {
18062e85ae0Sho 		struct conf_list *alist;
18162e85ae0Sho 		struct conf_list_node *anode;
18262e85ae0Sho 
1834292de0eSho 		ie->cfg_type = ISAKMP_CFG_REQUEST;
1844292de0eSho 
185d6351a66Sho 		LOG_DBG((LOG_NEGOTIATION, 10,
186d6351a66Sho 		    "cfg_initiator_send_ATTR: REQ/REPLY mode"));
18762e85ae0Sho 
18862e85ae0Sho 		alist = conf_get_list(id_string, "Attributes");
189fb9475d6Sderaadt 		if (alist) {
19062e85ae0Sho 			for (anode = TAILQ_FIRST(&alist->fields); anode;
191fb9475d6Sderaadt 			    anode = TAILQ_NEXT(anode, link)) {
192fb9475d6Sderaadt 				if (strcasecmp(anode->field, "Address") == 0) {
19362e85ae0Sho 					bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS);
19462e85ae0Sho 					bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS);
19562e85ae0Sho 					attrlen += ISAKMP_ATTR_SZ * 2;
19612f43dabShshoexer 				} else if (strcasecmp(anode->field, "Netmask")
19712f43dabShshoexer 				    == 0) {
19862e85ae0Sho 					bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK);
19962e85ae0Sho 					bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK);
20062e85ae0Sho 					attrlen += ISAKMP_ATTR_SZ * 2;
20112f43dabShshoexer 				} else if (strcasecmp(anode->field,
20212f43dabShshoexer 				    "Nameserver") == 0) {
20362e85ae0Sho 					bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS);
20462e85ae0Sho 					bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS);
20562e85ae0Sho 					attrlen += ISAKMP_ATTR_SZ * 2;
20612f43dabShshoexer 				} else if (strcasecmp(anode->field,
20712f43dabShshoexer 				    "WINS-server") == 0) {
20862e85ae0Sho 					bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS);
20962e85ae0Sho 					bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS);
21062e85ae0Sho 					attrlen += ISAKMP_ATTR_SZ * 2;
21112f43dabShshoexer 				} else if (strcasecmp(anode->field,
21212f43dabShshoexer 				    "DHCP-server") == 0) {
21362e85ae0Sho 					bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP);
21462e85ae0Sho 					bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP);
21562e85ae0Sho 					attrlen += ISAKMP_ATTR_SZ * 2;
21612f43dabShshoexer 				} else if (strcasecmp(anode->field,
21712f43dabShshoexer 				    "Lifetime") == 0) {
21862e85ae0Sho 					bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_ADDRESS_EXPIRY);
21962e85ae0Sho 					attrlen += ISAKMP_ATTR_SZ;
220fb9475d6Sderaadt 				} else {
22112f43dabShshoexer 					log_print("cfg_initiator_send_ATTR: "
22212f43dabShshoexer 					    "unknown attribute %.20s in "
22312f43dabShshoexer 					    "section [%s]", anode->field,
22412f43dabShshoexer 					    id_string);
22562e85ae0Sho 				}
22662e85ae0Sho 			}
22762e85ae0Sho 
22862e85ae0Sho 			conf_free_list(alist);
229d6351a66Sho 		}
2304120e4d1Sho 	}
231d6351a66Sho 
232fb9475d6Sderaadt 	if (attrlen == 0) {
233d6351a66Sho 		/* No data found.  */
23462e85ae0Sho 		log_print("cfg_initiator_send_ATTR: no IKECFG attributes "
23562e85ae0Sho 		    "found for [%s]", id_string);
23662e85ae0Sho 
23762e85ae0Sho 		/*
23812f43dabShshoexer 		 * We can continue, but this indicates a configuration error
23912f43dabShshoexer 		 * that the user probably will want to correct.
24062e85ae0Sho 		 */
241d6351a66Sho 		free(id_string);
242d6351a66Sho 		return 0;
243d6351a66Sho 	}
2446200cee4Sho 	attrlen += ISAKMP_ATTRIBUTE_SZ;
245d6351a66Sho 	attrp = calloc(1, attrlen);
246fb9475d6Sderaadt 	if (!attrp) {
247d6351a66Sho 		log_error("cfg_initiator_send_ATTR: calloc (1, %lu) failed",
248d6351a66Sho 		    (unsigned long)attrlen);
249d6351a66Sho 		goto fail;
250d6351a66Sho 	}
25112f43dabShshoexer 	if (message_add_payload(msg, ISAKMP_PAYLOAD_ATTRIBUTE, attrp, attrlen,
25212f43dabShshoexer 	    1)) {
253d6351a66Sho 		free(attrp);
254d6351a66Sho 		goto fail;
255d6351a66Sho 	}
2564292de0eSho 	SET_ISAKMP_ATTRIBUTE_TYPE(attrp, ie->cfg_type);
257baf9c2dbSderaadt 	arc4random_buf((u_int8_t *) & ie->cfg_id, sizeof ie->cfg_id);
258d6351a66Sho 	SET_ISAKMP_ATTRIBUTE_ID(attrp, ie->cfg_id);
259d6351a66Sho 
260d6351a66Sho 	off = ISAKMP_ATTRIBUTE_SZ;
2614292de0eSho 
2624292de0eSho 	/*
2634292de0eSho 	 * Use the bitstring built previously to collect the right
2644292de0eSho 	 * parameters for attrp.
2654292de0eSho 	 */
266d6351a66Sho 	for (bit = 0; bit < CFG_ATTR_BIT_MAX; bit++)
267fb9475d6Sderaadt 		if (bit_test(attrbits, bit)) {
268d6351a66Sho 			attr = attrp + off;
2694292de0eSho 			SET_ISAKMP_ATTR_TYPE(attr, bit);
2704292de0eSho 
271fb9475d6Sderaadt 			if (ie->cfg_type == ISAKMP_CFG_REQUEST) {
2724292de0eSho 				off += ISAKMP_ATTR_SZ;
2734292de0eSho 				continue;
2744292de0eSho 			}
275d6351a66Sho 			/* All the other are similar, this is the odd one.  */
276fb9475d6Sderaadt 			if (bit == ISAKMP_CFG_ATTR_INTERNAL_ADDRESS_EXPIRY) {
27750eea14cSho 				life = conf_get_num(id_string, "Lifetime",
27850eea14cSho 				    1200);
279d6351a66Sho 				SET_ISAKMP_ATTR_LENGTH_VALUE(attr, 4);
280d6351a66Sho 				encode_32(attr + ISAKMP_ATTR_VALUE_OFF, life);
281d6351a66Sho 				off += ISAKMP_ATTR_SZ + 4;
282d6351a66Sho 				continue;
283d6351a66Sho 			}
284fb9475d6Sderaadt 			switch (bit) {
285d6351a66Sho 			case ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS:
286d6351a66Sho 			case ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK:
287d6351a66Sho 			case ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS:
288d6351a66Sho 			case ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP:
289d6351a66Sho 			case ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS:
290d6351a66Sho 				length = 4;
291d6351a66Sho 				break;
292d6351a66Sho 
293d6351a66Sho 			case ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS:
294d6351a66Sho 			case ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK:
295d6351a66Sho 			case ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS:
296d6351a66Sho 			case ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP:
297d6351a66Sho 			case ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS:
298d6351a66Sho 				length = 16;
299d6351a66Sho 				break;
300d6351a66Sho 
301d6351a66Sho 			default:
302d6351a66Sho 				length = 0;	/* Silence gcc.  */
303d6351a66Sho 			}
304d6351a66Sho 
305fb9475d6Sderaadt 			switch (bit) {
306d6351a66Sho 			case ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS:
307d6351a66Sho 			case ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS:
308d6351a66Sho 				field = "Address";
309d6351a66Sho 				break;
310d6351a66Sho 			case ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK:
311d6351a66Sho 			case ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK:
312d6351a66Sho 				field = "Netmask";
313d6351a66Sho 				break;
314d6351a66Sho 			case ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS:
315d6351a66Sho 			case ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS:
316d6351a66Sho 				field = "Nameserver";
317d6351a66Sho 				break;
318d6351a66Sho 			case ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP:
319d6351a66Sho 			case ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP:
320d6351a66Sho 				field = "DHCP-server";
321d6351a66Sho 				break;
322d6351a66Sho 			case ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS:
323d6351a66Sho 			case ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS:
324d6351a66Sho 				field = "WINS-server";
325d6351a66Sho 				break;
326d6351a66Sho 			default:
327d6351a66Sho 				field = 0;	/* Silence gcc.  */
328d6351a66Sho 			}
329d6351a66Sho 
330d6351a66Sho 			sa = conf_get_address(id_string, field);
3319c59062cSho 
332d6351a66Sho 			SET_ISAKMP_ATTR_LENGTH_VALUE(attr, length);
33312f43dabShshoexer 			memcpy(attr + ISAKMP_ATTR_VALUE_OFF,
33412f43dabShshoexer 			    sockaddr_addrdata(sa), length);
335d6351a66Sho 
336c482b2d8Shshoexer 			free(sa);
337c482b2d8Shshoexer 
3384292de0eSho 			off += ISAKMP_ATTR_SZ + length;
339d6351a66Sho 		}
3409c59062cSho 	if (msg->exchange->phase == 2)
3419c59062cSho 		if (cfg_finalize_hash(msg, hashp, attrp, attrlen))
342d6351a66Sho 			goto fail;
3439c59062cSho 
3443635a927Sniklas 	return 0;
345d6351a66Sho 
346d6351a66Sho fail:
347d6351a66Sho 	free(id_string);
348d6351a66Sho 	return -1;
3493635a927Sniklas }
3503635a927Sniklas 
351d6351a66Sho /*
352d6351a66Sho  * As "the server", this ends SET/ACK.
353d6351a66Sho  * As "the client", this ends REQ/REPLY.
354d6351a66Sho  */
3553635a927Sniklas static int
cfg_initiator_recv_ATTR(struct message * msg)356d6351a66Sho cfg_initiator_recv_ATTR(struct message *msg)
3573635a927Sniklas {
35877fa3de5Sho 	struct payload *attrp = payload_first(msg, ISAKMP_PAYLOAD_ATTRIBUTE);
3594292de0eSho 	struct ipsec_exch *ie = msg->exchange->data;
360d6351a66Sho 	struct sa      *isakmp_sa = msg->isakmp_sa;
361d6351a66Sho 	struct isakmp_cfg_attr *attr;
362d6351a66Sho 	struct sockaddr *sa;
3634292de0eSho 	const char     *uk_addr = "<unknown>";
364d6351a66Sho 	char           *addr;
3653635a927Sniklas 
3664292de0eSho 	if (msg->exchange->phase == 2)
3679c59062cSho 		if (cfg_verify_hash(msg))
3683635a927Sniklas 			return -1;
3693635a927Sniklas 
3704292de0eSho 	/* Sanity.  */
371fb9475d6Sderaadt 	if (ie->cfg_id != GET_ISAKMP_ATTRIBUTE_ID(attrp->p)) {
37250eea14cSho 		log_print("cfg_initiator_recv_ATTR: "
37350eea14cSho 		    "cfg packet ID does not match!");
3744292de0eSho 		message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0);
3754292de0eSho 		return -1;
3764292de0eSho 	}
377fb9475d6Sderaadt 	switch (attrp->p[ISAKMP_ATTRIBUTE_TYPE_OFF]) {
378d6351a66Sho 	case ISAKMP_CFG_ACK:
379fb9475d6Sderaadt 		if (ie->cfg_type != ISAKMP_CFG_SET) {
38050eea14cSho 			log_print("cfg_initiator_recv_ATTR: "
38150eea14cSho 			    "bad packet type ACK");
38212f43dabShshoexer 			message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED,
38312f43dabShshoexer 			    0, 1, 0);
3844292de0eSho 			return -1;
3854292de0eSho 		}
3864292de0eSho 		break;
387d6351a66Sho 	case ISAKMP_CFG_REPLY:
388fb9475d6Sderaadt 		if (ie->cfg_type != ISAKMP_CFG_REQUEST) {
38950eea14cSho 			log_print("cfg_initiator_recv_ATTR: "
39050eea14cSho 			    "bad packet type REPLY");
39112f43dabShshoexer 			message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED,
39212f43dabShshoexer 			    0, 1, 0);
3934292de0eSho 			return -1;
3944292de0eSho 		}
395d6351a66Sho 		break;
396d6351a66Sho 
397d6351a66Sho 	default:
39812f43dabShshoexer 		log_print("cfg_initiator_recv_ATTR: unexpected configuration "
39912f43dabShshoexer 		    "message type %d", attrp->p[ISAKMP_ATTRIBUTE_TYPE_OFF]);
400213c41a9Sho 		message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0);
401d6351a66Sho 		return -1;
402d6351a66Sho 	}
403d6351a66Sho 
404d6351a66Sho 	attribute_map(attrp->p + ISAKMP_ATTRIBUTE_ATTRS_OFF,
40512f43dabShshoexer 	    GET_ISAKMP_GEN_LENGTH(attrp->p) - ISAKMP_TRANSFORM_SA_ATTRS_OFF,
40612f43dabShshoexer 	    cfg_decode_attribute, ie);
407d6351a66Sho 
408fb9475d6Sderaadt 	switch (ie->cfg_type) {
40912f43dabShshoexer 	case ISAKMP_CFG_ACK: {
410213c41a9Sho 			/* SET/ACK -- Server side (ACK from client) */
41150eea14cSho 			msg->transport->vtbl->get_src(isakmp_sa->transport,
41250eea14cSho 			    &sa);
413d6351a66Sho 			if (sockaddr2text(sa, &addr, 0) < 0)
414d6351a66Sho 				addr = (char *) uk_addr;
415d6351a66Sho 
416213c41a9Sho 			for (attr = LIST_FIRST(&ie->attrs); attr;
417213c41a9Sho 			    attr = LIST_NEXT(attr, link))
41812f43dabShshoexer 				LOG_DBG((LOG_NEGOTIATION, 50,
41912f43dabShshoexer 				    "cfg_initiator_recv_ATTR: "
420d6351a66Sho 				    "client %s ACKs attribute %s", addr,
42112f43dabShshoexer 				    constant_name(isakmp_cfg_attr_cst,
42212f43dabShshoexer 					attr->type)));
423d6351a66Sho 
424d6351a66Sho 			if (addr != uk_addr)
425d6351a66Sho 				free(addr);
426d6351a66Sho 		}
427213c41a9Sho 		break;
428213c41a9Sho 
42912f43dabShshoexer 	case ISAKMP_CFG_REPLY: {
430fb9475d6Sderaadt 			/*
431fb9475d6Sderaadt 			 * REQ/REPLY: effect attributes we've gotten
432fb9475d6Sderaadt 			 * responses on.
433fb9475d6Sderaadt 			 */
43450eea14cSho 			msg->transport->vtbl->get_src(isakmp_sa->transport,
43550eea14cSho 			    &sa);
4364292de0eSho 			if (sockaddr2text(sa, &addr, 0) < 0)
4374292de0eSho 				addr = (char *) uk_addr;
4384292de0eSho 
4394292de0eSho 			for (attr = LIST_FIRST(&ie->attrs); attr;
4404292de0eSho 			    attr = LIST_NEXT(attr, link))
44112f43dabShshoexer 				LOG_DBG((LOG_NEGOTIATION, 50,
44212f43dabShshoexer 				    "cfg_initiator_recv_ATTR: "
44312f43dabShshoexer 				    "server %s replied with attribute %s",
44412f43dabShshoexer 				    addr, constant_name(isakmp_cfg_attr_cst,
44512f43dabShshoexer 					attr->type)));
4464292de0eSho 
4474292de0eSho 			if (addr != uk_addr)
4484292de0eSho 				free(addr);
449d6351a66Sho 		}
450213c41a9Sho 		break;
451213c41a9Sho 
452213c41a9Sho 	default:
453213c41a9Sho 		break;
454213c41a9Sho 	}
455d6351a66Sho 
4561b3b7699Sho 	attrp->flags |= PL_MARK;
4573635a927Sniklas 	return 0;
4583635a927Sniklas }
4593635a927Sniklas 
460d6351a66Sho /*
461d6351a66Sho  * As "the server", this starts REQ/REPLY (initiated by the client).
462d6351a66Sho  * As "the client", this starts SET/ACK (initiated by the server).
463d6351a66Sho  */
4643635a927Sniklas static int
cfg_responder_recv_ATTR(struct message * msg)465d6351a66Sho cfg_responder_recv_ATTR(struct message *msg)
4663635a927Sniklas {
46777fa3de5Sho 	struct payload *attrp = payload_first(msg, ISAKMP_PAYLOAD_ATTRIBUTE);
4684292de0eSho 	struct ipsec_exch *ie = msg->exchange->data;
4693635a927Sniklas 	struct sa      *isakmp_sa = msg->isakmp_sa;
470213c41a9Sho 	struct isakmp_cfg_attr *attr;
471213c41a9Sho 	struct sockaddr *sa;
472213c41a9Sho 	char           *addr;
4733635a927Sniklas 
4744292de0eSho 	if (msg->exchange->phase == 2)
4759c59062cSho 		if (cfg_verify_hash(msg))
4763635a927Sniklas 			return -1;
4773635a927Sniklas 
4783635a927Sniklas 	ie->cfg_id = GET_ISAKMP_ATTRIBUTE_ID(attrp->p);
4799c59062cSho 	ie->cfg_type = attrp->p[ISAKMP_ATTRIBUTE_TYPE_OFF];
4803635a927Sniklas 
481fb9475d6Sderaadt 	switch (ie->cfg_type) {
4823635a927Sniklas 	case ISAKMP_CFG_REQUEST:
4833635a927Sniklas 	case ISAKMP_CFG_SET:
4843635a927Sniklas 		break;
4853635a927Sniklas 
4863635a927Sniklas 	default:
4873635a927Sniklas 		message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0);
488d6351a66Sho 		log_print("cfg_responder_recv_ATTR: "
4899c59062cSho 		    "unexpected configuration message type %d", ie->cfg_type);
4903635a927Sniklas 		return -1;
4913635a927Sniklas 	}
4923635a927Sniklas 
493213c41a9Sho 	attribute_map(attrp->p + ISAKMP_ATTRIBUTE_ATTRS_OFF,
49412f43dabShshoexer 	    GET_ISAKMP_GEN_LENGTH(attrp->p) - ISAKMP_TRANSFORM_SA_ATTRS_OFF,
49512f43dabShshoexer 	    cfg_decode_attribute, ie);
496213c41a9Sho 
497fb9475d6Sderaadt 	switch (ie->cfg_type) {
498213c41a9Sho 	case ISAKMP_CFG_REQUEST:
4994292de0eSho 		/* We're done.  */
500213c41a9Sho 		break;
501213c41a9Sho 
50212f43dabShshoexer 	case ISAKMP_CFG_SET: {
503213c41a9Sho 			/* SET/ACK -- Client side (SET from server) */
504213c41a9Sho 			const char     *uk_addr = "<unknown>";
505213c41a9Sho 
50650eea14cSho 			msg->transport->vtbl->get_dst(isakmp_sa->transport,
50750eea14cSho 			    &sa);
508213c41a9Sho 			if (sockaddr2text(sa, &addr, 0) < 0)
509213c41a9Sho 				addr = (char *) uk_addr;
510213c41a9Sho 
511213c41a9Sho 			for (attr = LIST_FIRST(&ie->attrs); attr;
512213c41a9Sho 			    attr = LIST_NEXT(attr, link))
51312f43dabShshoexer 				LOG_DBG((LOG_NEGOTIATION, 50,
51412f43dabShshoexer 				    "cfg_responder_recv_ATTR: "
51512f43dabShshoexer 				    "server %s asks us to SET attribute %s",
51612f43dabShshoexer 				    addr, constant_name(isakmp_cfg_attr_cst,
51712f43dabShshoexer 					attr->type)));
518213c41a9Sho 
5194292de0eSho 			/*
52012f43dabShshoexer 			 * XXX Here's the place to add code to walk through
52112f43dabShshoexer 			 * XXX each attribute and send them along to dhclient
52212f43dabShshoexer 			 * XXX or whatever. Each attribute that we act upon
52312f43dabShshoexer 			 * XXX (such as setting a netmask), should be marked
52412f43dabShshoexer 			 * XXX like this for us to send the proper ACK
52512f43dabShshoexer 			 * XXX response: attr->attr_used++;
5264292de0eSho 			 */
5274292de0eSho 
528213c41a9Sho 			if (addr != uk_addr)
529213c41a9Sho 				free(addr);
530213c41a9Sho 		}
531213c41a9Sho 		break;
532213c41a9Sho 
533213c41a9Sho 	default:
534213c41a9Sho 		break;
535213c41a9Sho 	}
536213c41a9Sho 
5374292de0eSho 	attrp->flags |= PL_MARK;
5383635a927Sniklas 	return 0;
5393635a927Sniklas }
5403635a927Sniklas 
541d6351a66Sho /*
542d6351a66Sho  * As "the server", this ends REQ/REPLY mode.
543d6351a66Sho  * As "the client", this ends SET/ACK mode.
544d6351a66Sho  */
5453635a927Sniklas static int
cfg_responder_send_ATTR(struct message * msg)546d6351a66Sho cfg_responder_send_ATTR(struct message *msg)
5473635a927Sniklas {
5489c59062cSho 	struct ipsec_exch *ie = msg->exchange->data;
5493635a927Sniklas 	struct sa      *isakmp_sa = msg->isakmp_sa;
5503635a927Sniklas 	u_int8_t       *hashp = 0, *attrp;
5519c59062cSho 	u_int16_t       attrlen;
5529c59062cSho 	char           *id_string;
553d0aff4c1Sniklas 
554fb9475d6Sderaadt 	if (msg->exchange->phase == 2) {
5559c59062cSho 		hashp = cfg_add_hash(msg);
5569c59062cSho 		if (!hashp)
5579c59062cSho 			return -1;
5589c59062cSho 	}
5599c59062cSho 	/* We are responder, check isakmp_sa for other side.  */
560be6dd78aSho 	if (isakmp_sa->initiator ^ (ie->cfg_type == ISAKMP_CFG_REQUEST))
56112f43dabShshoexer 		id_string = ipsec_id_string(isakmp_sa->id_i,
56212f43dabShshoexer 		    isakmp_sa->id_i_len);
5639c59062cSho 	else
56412f43dabShshoexer 		id_string = ipsec_id_string(isakmp_sa->id_r,
56512f43dabShshoexer 		    isakmp_sa->id_r_len);
566fb9475d6Sderaadt 	if (!id_string) {
567d6351a66Sho 		log_print("cfg_responder_send_ATTR: cannot parse client's ID");
5689c59062cSho 		return -1;
569d0aff4c1Sniklas 	}
5709c59062cSho 	if (cfg_encode_attributes(&ie->attrs, (ie->cfg_type == ISAKMP_CFG_SET ?
57112f43dabShshoexer 	    ISAKMP_CFG_ACK : ISAKMP_CFG_REPLY), ie->cfg_id, id_string, &attrp,
57212f43dabShshoexer 	    &attrlen)) {
5739c59062cSho 		free(id_string);
5749c59062cSho 		return -1;
5759c59062cSho 	}
5769c59062cSho 	free(id_string);
5779c59062cSho 
57812f43dabShshoexer 	if (message_add_payload(msg, ISAKMP_PAYLOAD_ATTRIBUTE, attrp, attrlen,
57912f43dabShshoexer 	    1)) {
5809c59062cSho 		free(attrp);
5819c59062cSho 		return -1;
5829c59062cSho 	}
5839c59062cSho 	if (msg->exchange->phase == 2)
5849c59062cSho 		if (cfg_finalize_hash(msg, hashp, attrp, attrlen))
5859c59062cSho 			return -1;
5869c59062cSho 
5879c59062cSho 	return 0;
5889c59062cSho }
5899c59062cSho 
5909c59062cSho u_int8_t *
cfg_add_hash(struct message * msg)5919c59062cSho cfg_add_hash(struct message *msg)
5929c59062cSho {
5939c59062cSho 	struct ipsec_sa *isa = msg->isakmp_sa->data;
5949c59062cSho 	struct hash    *hash = hash_get(isa->hash);
5959c59062cSho 	u_int8_t       *hashp;
5969c59062cSho 
5979c59062cSho 	hashp = malloc(ISAKMP_HASH_SZ + hash->hashsize);
598fb9475d6Sderaadt 	if (!hashp) {
5999c59062cSho 		log_error("cfg_add_hash: malloc (%lu) failed",
6009c59062cSho 		    ISAKMP_HASH_SZ + (unsigned long)hash->hashsize);
6019c59062cSho 		return 0;
6023635a927Sniklas 	}
6033635a927Sniklas 	if (message_add_payload(msg, ISAKMP_PAYLOAD_HASH, hashp,
604fb9475d6Sderaadt 	    ISAKMP_HASH_SZ + hash->hashsize, 1)) {
6053635a927Sniklas 		free(hashp);
6069c59062cSho 		return 0;
6073635a927Sniklas 	}
6089c59062cSho 	return hashp;
6093635a927Sniklas }
6103635a927Sniklas 
6119c59062cSho int
cfg_finalize_hash(struct message * msg,u_int8_t * hashp,u_int8_t * data,u_int16_t length)6129c59062cSho cfg_finalize_hash(struct message *msg, u_int8_t *hashp, u_int8_t *data,
6139c59062cSho     u_int16_t length)
6149c59062cSho {
6159c59062cSho 	struct ipsec_sa *isa = msg->isakmp_sa->data;
6169c59062cSho 	struct prf     *prf;
6179c59062cSho 
61812f43dabShshoexer 	prf = prf_alloc(isa->prf_type, isa->hash, isa->skeyid_a,
61912f43dabShshoexer 	    isa->skeyid_len);
6209c59062cSho 	if (!prf)
6219c59062cSho 		return -1;
6229c59062cSho 
6239c59062cSho 	prf->Init(prf->prfctx);
6249c59062cSho 	prf->Update(prf->prfctx, msg->exchange->message_id,
6259c59062cSho 	    ISAKMP_HDR_MESSAGE_ID_LEN);
6269c59062cSho 	prf->Update(prf->prfctx, data, length);
6279c59062cSho 	prf->Final(hashp + ISAKMP_GEN_SZ, prf->prfctx);
6289c59062cSho 	prf_free(prf);
6299c59062cSho 	return 0;
6309c59062cSho }
6319c59062cSho 
6329c59062cSho int
cfg_verify_hash(struct message * msg)6339c59062cSho cfg_verify_hash(struct message *msg)
6349c59062cSho {
63577fa3de5Sho 	struct payload *hashp = payload_first(msg, ISAKMP_PAYLOAD_HASH);
6369c59062cSho 	struct ipsec_sa *isa = msg->isakmp_sa->data;
6379c59062cSho 	struct prf     *prf;
6389c59062cSho 	u_int8_t       *hash, *comp_hash;
6399c59062cSho 	size_t          hash_len;
6409c59062cSho 
641fb9475d6Sderaadt 	if (!hashp) {
6429c59062cSho 		log_print("cfg_verify_hash: phase 2 message missing HASH");
64312f43dabShshoexer 		message_drop(msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION,
64412f43dabShshoexer 		    0, 1, 0);
6459c59062cSho 		return -1;
6469c59062cSho 	}
6479c59062cSho 	hash = hashp->p;
6489c59062cSho 	hash_len = GET_ISAKMP_GEN_LENGTH(hash);
6499c59062cSho 	comp_hash = malloc(hash_len - ISAKMP_GEN_SZ);
650fb9475d6Sderaadt 	if (!comp_hash) {
6519c59062cSho 		log_error("cfg_verify_hash: malloc (%lu) failed",
6529c59062cSho 		    (unsigned long)hash_len - ISAKMP_GEN_SZ);
6539c59062cSho 		return -1;
6549c59062cSho 	}
6559c59062cSho 	/* Verify hash.  */
6569c59062cSho 	prf = prf_alloc(isa->prf_type, isa->hash, isa->skeyid_a,
6579c59062cSho 	    isa->skeyid_len);
658fb9475d6Sderaadt 	if (!prf) {
6599c59062cSho 		free(comp_hash);
6609c59062cSho 		return -1;
6619c59062cSho 	}
6629c59062cSho 	prf->Init(prf->prfctx);
6634292de0eSho 	prf->Update(prf->prfctx, msg->exchange->message_id,
6644292de0eSho 	    ISAKMP_HDR_MESSAGE_ID_LEN);
6659c59062cSho 	prf->Update(prf->prfctx, hash + hash_len,
6669c59062cSho 	    msg->iov[0].iov_len - ISAKMP_HDR_SZ - hash_len);
6679c59062cSho 	prf->Final(comp_hash, prf->prfctx);
6689c59062cSho 	prf_free(prf);
6699c59062cSho 
67012f43dabShshoexer 	if (memcmp(hash + ISAKMP_GEN_SZ, comp_hash, hash_len - ISAKMP_GEN_SZ)
67112f43dabShshoexer 	    != 0) {
67212f43dabShshoexer 		message_drop(msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION,
67312f43dabShshoexer 		    0, 1, 0);
6749c59062cSho 		free(comp_hash);
6759c59062cSho 		return -1;
6769c59062cSho 	}
6779c59062cSho 	free(comp_hash);
6789c59062cSho 
6799c59062cSho 	/* Mark the HASH as handled.  */
6809c59062cSho 	hashp->flags |= PL_MARK;
6819c59062cSho 
68206f1661bShshoexer 	/* Mark message authenticated. */
68306f1661bShshoexer 	msg->flags |= MSG_AUTHENTICATED;
68406f1661bShshoexer 
6859c59062cSho 	return 0;
6869c59062cSho }
6879c59062cSho 
6889c59062cSho /*
6899c59062cSho  * Decode the attribute of type TYPE with a LEN length value pointed to by
6909c59062cSho  * VALUE.  VIE is a pointer to the IPsec exchange context holding the
6919c59062cSho  * attributes indexed by type for easy retrieval.
6929c59062cSho  */
6939c59062cSho static int
cfg_decode_attribute(u_int16_t type,u_int8_t * value,u_int16_t len,void * vie)6949c59062cSho cfg_decode_attribute(u_int16_t type, u_int8_t * value, u_int16_t len,
6959c59062cSho     void *vie)
6969c59062cSho {
6979c59062cSho 	struct ipsec_exch *ie = vie;
6989c59062cSho 	struct isakmp_cfg_attr *attr;
6999c59062cSho 
7009d6bd3cfSderaadt 	if (type >= ISAKMP_CFG_ATTR_PRIVATE_MIN &&
7019d6bd3cfSderaadt 	    type <= ISAKMP_CFG_ATTR_PRIVATE_MAX)
7029c59062cSho 		return 0;
703fb9475d6Sderaadt 	if (type == 0 || type >= ISAKMP_CFG_ATTR_FUTURE_MIN) {
7049c59062cSho 		LOG_DBG((LOG_NEGOTIATION, 30,
7059c59062cSho 		    "cfg_decode_attribute: invalid attr type %u", type));
7069c59062cSho 		return -1;
7079c59062cSho 	}
7089c59062cSho 	attr = calloc(1, sizeof *attr);
709fb9475d6Sderaadt 	if (!attr) {
7109c59062cSho 		log_error("cfg_decode_attribute: calloc (1, %lu) failed",
7119c59062cSho 		    (unsigned long)sizeof *attr);
7129c59062cSho 		return -1;
7139c59062cSho 	}
7149c59062cSho 	attr->type = type;
7159c59062cSho 	attr->length = len;
716fb9475d6Sderaadt 	if (len) {
7179c59062cSho 		attr->value = malloc(len);
718fb9475d6Sderaadt 		if (!attr->value) {
71912f43dabShshoexer 			log_error("cfg_decode_attribute: malloc (%d) failed",
72012f43dabShshoexer 			    len);
7219c59062cSho 			free(attr);
7229c59062cSho 			/* Should we also deallocate all other values?  */
7239c59062cSho 			return -1;
7249c59062cSho 		}
7259c59062cSho 		memcpy(attr->value, value, len);
7269c59062cSho 	}
7279c59062cSho 	LIST_INSERT_HEAD(&ie->attrs, attr, link);
7289c59062cSho 	return 0;
7299c59062cSho }
7309c59062cSho 
7319c59062cSho /*
7329c59062cSho  * Encode list of attributes from ie->attrs into a attribute payload.
7339c59062cSho  */
7349c59062cSho static int
cfg_encode_attributes(struct isakmp_cfg_attr_head * attrs,u_int32_t type,u_int32_t cfg_id,char * id_string,u_int8_t ** attrp,u_int16_t * len)7359c59062cSho cfg_encode_attributes(struct isakmp_cfg_attr_head *attrs, u_int32_t type,
73612f43dabShshoexer     u_int32_t cfg_id, char *id_string, u_int8_t **attrp, u_int16_t *len)
7379c59062cSho {
7389c59062cSho 	struct isakmp_cfg_attr *attr;
7399c59062cSho 	struct sockaddr *sa;
7409c59062cSho 	sa_family_t     family;
7419c59062cSho 	u_int32_t       value;
7421b3b7699Sho 	u_int16_t       off;
7439c59062cSho 	char           *field;
7449c59062cSho 
7459c59062cSho 	/* Compute length */
7461b3b7699Sho 	*len = ISAKMP_ATTRIBUTE_SZ;
747fb9475d6Sderaadt 	for (attr = LIST_FIRST(attrs); attr; attr = LIST_NEXT(attr, link)) {
7484292de0eSho 		/* With ACK we only include the attrs we've actually used.  */
7494292de0eSho 		if (type == ISAKMP_CFG_ACK && attr->attr_used == 0)
7504292de0eSho 			continue;
7514292de0eSho 
752fb9475d6Sderaadt 		switch (attr->type) {
7533635a927Sniklas 		case ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS:
7543635a927Sniklas 		case ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK:
7553635a927Sniklas 		case ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP:
7563635a927Sniklas 		case ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS:
7573635a927Sniklas 		case ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS:
7583635a927Sniklas 		case ISAKMP_CFG_ATTR_INTERNAL_ADDRESS_EXPIRY:
7593635a927Sniklas 			attr->length = 4;
7603635a927Sniklas 			break;
7613635a927Sniklas 
7623635a927Sniklas 		case ISAKMP_CFG_ATTR_INTERNAL_IP4_SUBNET:
7633635a927Sniklas 			attr->length = 8;
7643635a927Sniklas 			break;
7653635a927Sniklas 
7663635a927Sniklas 		case ISAKMP_CFG_ATTR_APPLICATION_VERSION:
7673635a927Sniklas 			/* XXX So far no version identifier of isakmpd here. */
7683635a927Sniklas 			attr->length = 0;
7693635a927Sniklas 			break;
7703635a927Sniklas 
7713635a927Sniklas 		case ISAKMP_CFG_ATTR_SUPPORTED_ATTRIBUTES:
772dbb3e331Sniklas 			attr->length = 2 * 15;
7733635a927Sniklas 			break;
7743635a927Sniklas 
7753635a927Sniklas 		case ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS:
7763635a927Sniklas 		case ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK:
7773635a927Sniklas 		case ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP:
7783635a927Sniklas 		case ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS:
7793635a927Sniklas 		case ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS:
7803635a927Sniklas 			attr->length = 16;
7813635a927Sniklas 			break;
7823635a927Sniklas 
7833635a927Sniklas 		case ISAKMP_CFG_ATTR_INTERNAL_IP6_SUBNET:
7843635a927Sniklas 			attr->length = 17;
7853635a927Sniklas 			break;
7863635a927Sniklas 
7873635a927Sniklas 		default:
7883635a927Sniklas 			attr->ignore++;
7893635a927Sniklas 			/* XXX Log!  */
7903635a927Sniklas 		}
7911b3b7699Sho 		*len += ISAKMP_ATTR_SZ + attr->length;
7923635a927Sniklas 	}
7933635a927Sniklas 
7949c59062cSho 	/* Allocate enough space for the payload */
7951b3b7699Sho 	*attrp = calloc(1, *len);
796fb9475d6Sderaadt 	if (!*attrp) {
7979c59062cSho 		log_error("cfg_encode_attributes: calloc (1, %lu) failed",
7981b3b7699Sho 		    (unsigned long)*len);
7999c59062cSho 		return -1;
8003635a927Sniklas 	}
8019c59062cSho 	SET_ISAKMP_ATTRIBUTE_TYPE(*attrp, type);
8029c59062cSho 	SET_ISAKMP_ATTRIBUTE_ID(*attrp, cfg_id);
8033635a927Sniklas 
8043635a927Sniklas 	off = ISAKMP_ATTRIBUTE_SZ;
805fb9475d6Sderaadt 	for (attr = LIST_FIRST(attrs); attr; attr = LIST_NEXT(attr, link)) {
8064292de0eSho 		/* With ACK we only include the attrs we've actually used.  */
8074292de0eSho 		if (type == ISAKMP_CFG_ACK && attr->attr_used == 0)
8084292de0eSho 			continue;
8094292de0eSho 
810fb9475d6Sderaadt 		switch (attr->type) {
8113635a927Sniklas 		case ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS:
8123635a927Sniklas 		case ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK:
8132af22032Sho 		case ISAKMP_CFG_ATTR_INTERNAL_IP4_SUBNET:
8142af22032Sho 		case ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP:
8152af22032Sho 		case ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS:
8162af22032Sho 		case ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS:
8172af22032Sho 			family = AF_INET;
8182af22032Sho 			break;
8192af22032Sho 
8202af22032Sho 		case ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS:
821d38e1a4dSniklas 		case ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK:
8222af22032Sho 		case ISAKMP_CFG_ATTR_INTERNAL_IP6_SUBNET:
8232af22032Sho 		case ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP:
8242af22032Sho 		case ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS:
8252af22032Sho 		case ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS:
8262af22032Sho 			family = AF_INET6;
8272af22032Sho 			break;
8282af22032Sho 
8292af22032Sho 		default:
8302af22032Sho 			family = 0;
8312af22032Sho 			break;
8322af22032Sho 		}
8332af22032Sho 
834fb9475d6Sderaadt 		switch (attr->type) {
8352af22032Sho 		case ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS:
8362af22032Sho 		case ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS:
8372af22032Sho 			field = "Address";
8383635a927Sniklas 			break;
8393635a927Sniklas 
8403635a927Sniklas 		case ISAKMP_CFG_ATTR_INTERNAL_IP4_SUBNET:
841d38e1a4dSniklas 		case ISAKMP_CFG_ATTR_INTERNAL_IP6_SUBNET:
842d6351a66Sho 			field = "Network";	/* XXX or just "Address" */
8432af22032Sho 			break;
8442af22032Sho 
8452af22032Sho 		case ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK:
8462af22032Sho 		case ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK:
8472af22032Sho 			field = "Netmask";
8483635a927Sniklas 			break;
8493635a927Sniklas 
8503635a927Sniklas 		case ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP:
851d38e1a4dSniklas 		case ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP:
8522af22032Sho 			field = "DHCP-server";
8533635a927Sniklas 			break;
8543635a927Sniklas 
8553635a927Sniklas 		case ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS:
856d38e1a4dSniklas 		case ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS:
8572af22032Sho 			field = "Nameserver";
8583635a927Sniklas 			break;
8593635a927Sniklas 
8603635a927Sniklas 		case ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS:
861d38e1a4dSniklas 		case ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS:
8622af22032Sho 			field = "WINS-server";
8632af22032Sho 			break;
8642af22032Sho 
8652af22032Sho 		default:
8662af22032Sho 			field = 0;
8672af22032Sho 		}
8682af22032Sho 
869fb9475d6Sderaadt 		switch (attr->type) {
8702af22032Sho 		case ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS:
8712af22032Sho 		case ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS:
8722af22032Sho 		case ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK:
8732af22032Sho 		case ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK:
8742af22032Sho 		case ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP:
8752af22032Sho 		case ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP:
8762af22032Sho 		case ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS:
8772af22032Sho 		case ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS:
8782af22032Sho 		case ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS:
8792af22032Sho 		case ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS:
8802af22032Sho 			sa = conf_get_address(id_string, field);
881fb9475d6Sderaadt 			if (!sa) {
88212f43dabShshoexer 				LOG_DBG((LOG_NEGOTIATION, 10,
88312f43dabShshoexer 				    "cfg_responder_send_ATTR: "
8842af22032Sho 				    "attribute not found: %s", field));
885341363bfSmarkus 				attr->length = 0;
886341363bfSmarkus 				break;
887341363bfSmarkus 			}
888fb9475d6Sderaadt 			if (sa->sa_family != family) {
88912f43dabShshoexer 				log_print("cfg_responder_send_ATTR: "
89012f43dabShshoexer 				    "attribute %s - expected %s got %s data",
89112f43dabShshoexer 				    field,
8922af22032Sho 				    (family == AF_INET ? "IPv4" : "IPv6"),
89350eea14cSho 				    (sa->sa_family ==
89450eea14cSho 					AF_INET ? "IPv4" : "IPv6"));
8953635a927Sniklas 				free(sa);
8962af22032Sho 				attr->length = 0;
897341363bfSmarkus 				break;
898341363bfSmarkus 			}
8992af22032Sho 			/* Temporary limit length for the _SUBNET types. */
9002af22032Sho 			if (attr->type == ISAKMP_CFG_ATTR_INTERNAL_IP4_SUBNET)
9012af22032Sho 				attr->length = 4;
90212f43dabShshoexer 			else if (attr->type ==
90312f43dabShshoexer 			    ISAKMP_CFG_ATTR_INTERNAL_IP6_SUBNET)
9042af22032Sho 				attr->length = 16;
9052af22032Sho 
90612f43dabShshoexer 			memcpy(*attrp + off + ISAKMP_ATTR_VALUE_OFF,
90712f43dabShshoexer 			    sockaddr_addrdata(sa), attr->length);
9083635a927Sniklas 			free(sa);
9092af22032Sho 
9102af22032Sho 			/* _SUBNET types need some extra work. */
91150eea14cSho 			if (attr->type ==
91250eea14cSho 			    ISAKMP_CFG_ATTR_INTERNAL_IP4_SUBNET) {
9132af22032Sho 				sa = conf_get_address(id_string, "Netmask");
914fb9475d6Sderaadt 				if (!sa) {
91512f43dabShshoexer 					LOG_DBG((LOG_NEGOTIATION, 10,
91612f43dabShshoexer 					    "cfg_responder_send_ATTR: "
9172af22032Sho 					   "attribute not found: Netmask"));
9182af22032Sho 					attr->length = 0;
9192af22032Sho 					break;
9202af22032Sho 				}
921fb9475d6Sderaadt 				if (sa->sa_family != AF_INET) {
92212f43dabShshoexer 					log_print("cfg_responder_send_ATTR: "
92312f43dabShshoexer 					    "attribute Netmask - expected "
92412f43dabShshoexer 					    "IPv4 got IPv6 data");
9252af22032Sho 					free(sa);
9262af22032Sho 					attr->length = 0;
9272af22032Sho 					break;
9282af22032Sho 				}
92912f43dabShshoexer 				memcpy(*attrp + off + ISAKMP_ATTR_VALUE_OFF +
93012f43dabShshoexer 				    attr->length, sockaddr_addrdata(sa),
93112f43dabShshoexer 				    attr->length);
9322af22032Sho 				attr->length = 8;
9332af22032Sho 				free(sa);
93412f43dabShshoexer 			} else if (attr->type ==
93512f43dabShshoexer 			    ISAKMP_CFG_ATTR_INTERNAL_IP6_SUBNET) {
93612f43dabShshoexer 				int prefix = conf_get_num(id_string, "Prefix",
93712f43dabShshoexer 				    -1);
9382af22032Sho 
939fb9475d6Sderaadt 				if (prefix == -1) {
940d6351a66Sho 					log_print("cfg_responder_send_ATTR: "
9412af22032Sho 					    "attribute not found: Prefix");
9422af22032Sho 					attr->length = 0;
9432af22032Sho 					break;
944fb9475d6Sderaadt 				} else if (prefix < -1 || prefix > 128) {
94512f43dabShshoexer 					log_print("cfg_responder_send_ATTR: "
94650eea14cSho 					    "attribute Prefix - invalid "
94750eea14cSho 					    "value %d", prefix);
9482af22032Sho 					attr->length = 0;
9492af22032Sho 					break;
9502af22032Sho 				}
95112f43dabShshoexer 				*(*attrp + off + ISAKMP_ATTR_VALUE_OFF + 16) =
95212f43dabShshoexer 				    (u_int8_t)prefix;
9532af22032Sho 				attr->length = 17;
9542af22032Sho 			}
9553635a927Sniklas 			break;
9563635a927Sniklas 
9573635a927Sniklas 		case ISAKMP_CFG_ATTR_INTERNAL_ADDRESS_EXPIRY:
958d0aff4c1Sniklas 			value = conf_get_num(id_string, "Lifetime", 1200);
9599c59062cSho 			encode_32(*attrp + off + ISAKMP_ATTR_VALUE_OFF, value);
9603635a927Sniklas 			break;
9613635a927Sniklas 
9623635a927Sniklas 		case ISAKMP_CFG_ATTR_APPLICATION_VERSION:
9633635a927Sniklas 			/* XXX So far no version identifier of isakmpd here. */
9643635a927Sniklas 			break;
9653635a927Sniklas 
9663635a927Sniklas 		case ISAKMP_CFG_ATTR_SUPPORTED_ATTRIBUTES:
9673635a927Sniklas 			break;
9683635a927Sniklas 
9693635a927Sniklas 		default:
970349fdaddSho 			break;
9713635a927Sniklas 		}
9729c59062cSho 
973be6dd78aSho 		SET_ISAKMP_ATTR_TYPE(*attrp + off, attr->type);
9749c59062cSho 		SET_ISAKMP_ATTR_LENGTH_VALUE(*attrp + off, attr->length);
975be6dd78aSho 		off += ISAKMP_ATTR_VALUE_OFF + attr->length;
9763635a927Sniklas 	}
9773635a927Sniklas 
9783635a927Sniklas 	return 0;
9793635a927Sniklas }
980