xref: /openbsd/sbin/isakmpd/conf.c (revision e157c6af)
1*e157c6afSmoritz /* $OpenBSD: conf.c,v 1.76 2005/03/15 20:33:07 moritz Exp $	 */
2cc475db6Sniklas /* $EOM: conf.c,v 1.48 2000/12/04 02:04:29 angelos Exp $	 */
32040585eSniklas 
42040585eSniklas /*
542af7185Sniklas  * Copyright (c) 1998, 1999, 2000, 2001 Niklas Hallqvist.  All rights reserved.
690d8b2b0Sho  * Copyright (c) 2000, 2001, 2002 H�kan Olsson.  All rights reserved.
72040585eSniklas  *
82040585eSniklas  * Redistribution and use in source and binary forms, with or without
92040585eSniklas  * modification, are permitted provided that the following conditions
102040585eSniklas  * are met:
112040585eSniklas  * 1. Redistributions of source code must retain the above copyright
122040585eSniklas  *    notice, this list of conditions and the following disclaimer.
132040585eSniklas  * 2. Redistributions in binary form must reproduce the above copyright
142040585eSniklas  *    notice, this list of conditions and the following disclaimer in the
152040585eSniklas  *    documentation and/or other materials provided with the distribution.
162040585eSniklas  *
172040585eSniklas  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
182040585eSniklas  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
192040585eSniklas  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
202040585eSniklas  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
212040585eSniklas  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
222040585eSniklas  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
232040585eSniklas  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
242040585eSniklas  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
252040585eSniklas  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
262040585eSniklas  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
272040585eSniklas  */
282040585eSniklas 
292040585eSniklas /*
302040585eSniklas  * This code was written under funding by Ericsson Radio Systems.
312040585eSniklas  */
322040585eSniklas 
332040585eSniklas #include <sys/param.h>
342040585eSniklas #include <sys/mman.h>
352040585eSniklas #include <sys/queue.h>
3681c21331Sniklas #include <sys/socket.h>
372040585eSniklas #include <sys/stat.h>
3881c21331Sniklas #include <netinet/in.h>
3981c21331Sniklas #include <arpa/inet.h>
402040585eSniklas #include <ctype.h>
412040585eSniklas #include <fcntl.h>
422040585eSniklas #include <stdio.h>
432040585eSniklas #include <stdlib.h>
442040585eSniklas #include <string.h>
452040585eSniklas #include <unistd.h>
463eed80ffSniklas #include <errno.h>
472040585eSniklas 
48a2d30fd1Sniklas #include "sysdep.h"
49a2d30fd1Sniklas 
50a2d30fd1Sniklas #include "app.h"
512040585eSniklas #include "conf.h"
522040585eSniklas #include "log.h"
53da35d433Sho #include "monitor.h"
54bda02003Sniklas #include "util.h"
552040585eSniklas 
564c8c122bSho static char    *conf_get_trans_str(int, char *, char *);
574c8c122bSho static void     conf_load_defaults(int);
584c8c122bSho #if 0
594c8c122bSho static int      conf_find_trans_xf(int, char *);
604c8c122bSho #endif
614c8c122bSho 
62f8f1e192Sniklas struct conf_trans {
63f8f1e192Sniklas 	TAILQ_ENTRY(conf_trans) link;
64f8f1e192Sniklas 	int	 trans;
65fb9475d6Sderaadt 	enum conf_op {
66fb9475d6Sderaadt 		CONF_SET, CONF_REMOVE, CONF_REMOVE_SECTION
67fb9475d6Sderaadt 	}	 op;
68f8f1e192Sniklas 	char	*section;
69f8f1e192Sniklas 	char	*tag;
70f8f1e192Sniklas 	char	*value;
71f8f1e192Sniklas 	int	 override;
72510d8b0cSniklas 	int	 is_default;
73f8f1e192Sniklas };
74f8f1e192Sniklas 
75d865f642Sho #define CONF_SECT_MAX 256
76d865f642Sho 
77f8f1e192Sniklas TAILQ_HEAD(conf_trans_head, conf_trans) conf_trans_queue;
78f8f1e192Sniklas 
792040585eSniklas /*
802040585eSniklas  * Radix-64 Encoding.
812040585eSniklas  */
8299cdfc90Sderaadt const u_int8_t  bin2asc[] =
8399cdfc90Sderaadt     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
842040585eSniklas 
852040585eSniklas const u_int8_t  asc2bin[] =
862040585eSniklas {
872040585eSniklas 	255, 255, 255, 255, 255, 255, 255, 255,
882040585eSniklas 	255, 255, 255, 255, 255, 255, 255, 255,
892040585eSniklas 	255, 255, 255, 255, 255, 255, 255, 255,
902040585eSniklas 	255, 255, 255, 255, 255, 255, 255, 255,
912040585eSniklas 	255, 255, 255, 255, 255, 255, 255, 255,
922040585eSniklas 	255, 255, 255, 62, 255, 255, 255, 63,
932040585eSniklas 	52, 53, 54, 55, 56, 57, 58, 59,
942040585eSniklas 	60, 61, 255, 255, 255, 255, 255, 255,
952040585eSniklas 	255, 0, 1, 2, 3, 4, 5, 6,
962040585eSniklas 	7, 8, 9, 10, 11, 12, 13, 14,
972040585eSniklas 	15, 16, 17, 18, 19, 20, 21, 22,
982040585eSniklas 	23, 24, 25, 255, 255, 255, 255, 255,
992040585eSniklas 	255, 26, 27, 28, 29, 30, 31, 32,
1002040585eSniklas 	33, 34, 35, 36, 37, 38, 39, 40,
1012040585eSniklas 	41, 42, 43, 44, 45, 46, 47, 48,
1022040585eSniklas 	49, 50, 51, 255, 255, 255, 255, 255
1032040585eSniklas };
1042040585eSniklas 
1052040585eSniklas struct conf_binding {
1062040585eSniklas 	LIST_ENTRY(conf_binding) link;
1072040585eSniklas 	char	*section;
1082040585eSniklas 	char	*tag;
1092040585eSniklas 	char	*value;
110510d8b0cSniklas 	int	 is_default;
1112040585eSniklas };
1122040585eSniklas 
1132040585eSniklas char	*conf_path = CONFIG_FILE;
114f8f1e192Sniklas LIST_HEAD(conf_bindings, conf_binding) conf_bindings[256];
1152040585eSniklas 
1162040585eSniklas static char	*conf_addr;
117f8f1e192Sniklas static __inline__ u_int8_t
118f8f1e192Sniklas conf_hash(char *s)
119f8f1e192Sniklas {
120f8f1e192Sniklas 	u_int8_t hash = 0;
121f8f1e192Sniklas 
122fb9475d6Sderaadt 	while (*s) {
123f8f1e192Sniklas 		hash = ((hash << 1) | (hash >> 7)) ^ tolower(*s);
124f8f1e192Sniklas 		s++;
125f8f1e192Sniklas 	}
126f8f1e192Sniklas 	return hash;
127f8f1e192Sniklas }
128f8f1e192Sniklas 
129f8f1e192Sniklas /*
130f8f1e192Sniklas  * Insert a tag-value combination from LINE (the equal sign is at POS)
131f8f1e192Sniklas  */
132f8f1e192Sniklas static int
133f8f1e192Sniklas conf_remove_now(char *section, char *tag)
134f8f1e192Sniklas {
135f8f1e192Sniklas 	struct conf_binding *cb, *next;
136f8f1e192Sniklas 
137df915834Shshoexer 	for (cb = LIST_FIRST(&conf_bindings[conf_hash(section)]); cb;
138df915834Shshoexer 	    cb = next) {
139f8f1e192Sniklas 		next = LIST_NEXT(cb, link);
140f8f1e192Sniklas 		if (strcasecmp(cb->section, section) == 0
141fb9475d6Sderaadt 		    && strcasecmp(cb->tag, tag) == 0) {
142f8f1e192Sniklas 			LIST_REMOVE(cb, link);
143df915834Shshoexer 			LOG_DBG((LOG_MISC, 95, "[%s]:%s->%s removed", section,
144df915834Shshoexer 			    tag, cb->value));
145f8f1e192Sniklas 			free(cb->section);
146f8f1e192Sniklas 			free(cb->tag);
147f8f1e192Sniklas 			free(cb->value);
148f8f1e192Sniklas 			free(cb);
149f8f1e192Sniklas 			return 0;
150f8f1e192Sniklas 		}
151f8f1e192Sniklas 	}
152f8f1e192Sniklas 	return 1;
153f8f1e192Sniklas }
154f8f1e192Sniklas 
155f8f1e192Sniklas static int
156f8f1e192Sniklas conf_remove_section_now(char *section)
157f8f1e192Sniklas {
158f8f1e192Sniklas 	struct conf_binding *cb, *next;
159f8f1e192Sniklas 	int	unseen = 1;
160f8f1e192Sniklas 
161df915834Shshoexer 	for (cb = LIST_FIRST(&conf_bindings[conf_hash(section)]); cb;
162df915834Shshoexer 	    cb = next) {
163f8f1e192Sniklas 		next = LIST_NEXT(cb, link);
164fb9475d6Sderaadt 		if (strcasecmp(cb->section, section) == 0) {
165f8f1e192Sniklas 			unseen = 0;
166f8f1e192Sniklas 			LIST_REMOVE(cb, link);
167df915834Shshoexer 			LOG_DBG((LOG_MISC, 95, "[%s]:%s->%s removed", section,
168df915834Shshoexer 			    cb->tag, cb->value));
169f8f1e192Sniklas 			free(cb->section);
170f8f1e192Sniklas 			free(cb->tag);
171f8f1e192Sniklas 			free(cb->value);
172f8f1e192Sniklas 			free(cb);
173f8f1e192Sniklas 		}
174f8f1e192Sniklas 	}
175f8f1e192Sniklas 	return unseen;
176f8f1e192Sniklas }
177f8f1e192Sniklas 
1782040585eSniklas /*
1792040585eSniklas  * Insert a tag-value combination from LINE (the equal sign is at POS)
1802040585eSniklas  * into SECTION of our configuration database.
1812040585eSniklas  */
182f8f1e192Sniklas static int
183510d8b0cSniklas conf_set_now(char *section, char *tag, char *value, int override,
184510d8b0cSniklas     int is_default)
1852040585eSniklas {
186f8f1e192Sniklas 	struct conf_binding *node = 0;
1872040585eSniklas 
188f8f1e192Sniklas 	if (override)
189f8f1e192Sniklas 		conf_remove_now(section, tag);
190fb9475d6Sderaadt 	else if (conf_get_str(section, tag)) {
19113e19299Sniklas 		if (!is_default)
192df915834Shshoexer 			log_print("conf_set_now: duplicate tag [%s]:%s, "
193df915834Shshoexer 			    "ignoring...\n", section, tag);
194f8f1e192Sniklas 		return 1;
1952040585eSniklas 	}
196f8f1e192Sniklas 	node = calloc(1, sizeof *node);
197fb9475d6Sderaadt 	if (!node) {
198df915834Shshoexer 		log_error("conf_set_now: calloc (1, %lu) failed",
199df915834Shshoexer 		    (unsigned long)sizeof *node);
200f8f1e192Sniklas 		return 1;
201f8f1e192Sniklas 	}
202074d67afSniklas 	node->section = strdup(section);
203074d67afSniklas 	node->tag = strdup(tag);
204074d67afSniklas 	node->value = strdup(value);
205510d8b0cSniklas 	node->is_default = is_default;
206f8f1e192Sniklas 
207f8f1e192Sniklas 	LIST_INSERT_HEAD(&conf_bindings[conf_hash(section)], node, link);
208df915834Shshoexer 	LOG_DBG((LOG_MISC, 95, "conf_set_now: [%s]:%s->%s", node->section,
209df915834Shshoexer 	    node->tag, node->value));
210f8f1e192Sniklas 	return 0;
2112040585eSniklas }
2122040585eSniklas 
2132040585eSniklas /*
2142040585eSniklas  * Parse the line LINE of SZ bytes.  Skip Comments, recognize section
2152040585eSniklas  * headers and feed tag-value pairs into our configuration database.
2162040585eSniklas  */
2172040585eSniklas static void
218*e157c6afSmoritz conf_parse_line(int trans, char *line, int ln, size_t sz)
2192040585eSniklas {
22090d8b2b0Sho 	char	*val;
221cde22268Sho 	size_t	 i;
222cde22268Sho 	int	 j;
2232040585eSniklas 	static char *section = 0;
2242040585eSniklas 
2252040585eSniklas 	/* Lines starting with '#' or ';' are comments.  */
2262040585eSniklas 	if (*line == '#' || *line == ';')
2272040585eSniklas 		return;
2282040585eSniklas 
2292040585eSniklas 	/* '[section]' parsing...  */
230fb9475d6Sderaadt 	if (*line == '[') {
2312040585eSniklas 		for (i = 1; i < sz; i++)
2322040585eSniklas 			if (line[i] == ']')
2332040585eSniklas 				break;
23490d8b2b0Sho 		if (section)
23590d8b2b0Sho 			free(section);
236fb9475d6Sderaadt 		if (i == sz) {
2372040585eSniklas 			log_print("conf_parse_line: %d:"
23850eea14cSho 			    "unmatched ']', ignoring until next section", ln);
2392040585eSniklas 			section = 0;
2402040585eSniklas 			return;
2412040585eSniklas 		}
2422040585eSniklas 		section = malloc(i);
243fb9475d6Sderaadt 		if (!section) {
244df915834Shshoexer 			log_print("conf_parse_line: %d: malloc (%lu) failed",
245df915834Shshoexer 			    ln, (unsigned long)i);
24690d8b2b0Sho 			return;
24790d8b2b0Sho 		}
248b8380d91Sho 		strlcpy(section, line + 1, i);
2492040585eSniklas 		return;
2502040585eSniklas 	}
2512040585eSniklas 	/* Deal with assignments.  */
2522040585eSniklas 	for (i = 0; i < sz; i++)
253fb9475d6Sderaadt 		if (line[i] == '=') {
2542040585eSniklas 			/* If no section, we are ignoring the lines.  */
255fb9475d6Sderaadt 			if (!section) {
25680cd8be9Sderaadt 				log_print("conf_parse_line: %d: ignoring line "
25780cd8be9Sderaadt 				    "due to no section", ln);
2582040585eSniklas 				return;
2592040585eSniklas 			}
260f8f1e192Sniklas 			line[strcspn(line, " \t=")] = '\0';
26190d8b2b0Sho 			val = line + i + 1 + strspn(line + i + 1, " \t");
26290d8b2b0Sho 			/* Skip trailing whitespace, if any */
263df915834Shshoexer 			for (j = sz - (val - line) - 1; j > 0 &&
264df915834Shshoexer 			    isspace(val[j]); j--)
265cde22268Sho 				val[j] = '\0';
266f8f1e192Sniklas 			/* XXX Perhaps should we not ignore errors?  */
26790d8b2b0Sho 			conf_set(trans, section, line, val, 0, 0);
2682040585eSniklas 			return;
2692040585eSniklas 		}
270d6fd0492Spvalchev 	/* Other non-empty lines are weird.  */
2712040585eSniklas 	i = strspn(line, " \t");
2722040585eSniklas 	if (line[i])
2732040585eSniklas 		log_print("conf_parse_line: %d: syntax error", ln);
2742040585eSniklas }
2752040585eSniklas 
2762040585eSniklas /* Parse the mapped configuration file.  */
2772040585eSniklas static void
278f8f1e192Sniklas conf_parse(int trans, char *buf, size_t sz)
2792040585eSniklas {
280f8f1e192Sniklas 	char	*cp = buf;
281f8f1e192Sniklas 	char	*bufend = buf + sz;
2822040585eSniklas 	char	*line;
283*e157c6afSmoritz 	int	ln = 1;
2842040585eSniklas 
2852040585eSniklas 	line = cp;
286fb9475d6Sderaadt 	while (cp < bufend) {
287fb9475d6Sderaadt 		if (*cp == '\n') {
2882040585eSniklas 			/* Check for escaped newlines.  */
289f8f1e192Sniklas 			if (cp > buf && *(cp - 1) == '\\')
2902040585eSniklas 				*(cp - 1) = *cp = ' ';
291fb9475d6Sderaadt 			else {
2922040585eSniklas 				*cp = '\0';
293*e157c6afSmoritz 				conf_parse_line(trans, line, ln, cp - line);
2942040585eSniklas 				line = cp + 1;
2952040585eSniklas 			}
296*e157c6afSmoritz 			ln++;
2972040585eSniklas 		}
2982040585eSniklas 		cp++;
2992040585eSniklas 	}
3002040585eSniklas 	if (cp != line)
30150eea14cSho 		log_print("conf_parse: last line unterminated, ignored.");
3022040585eSniklas }
3032040585eSniklas 
304510d8b0cSniklas /*
305510d8b0cSniklas  * Auto-generate default configuration values for the transforms and
306510d8b0cSniklas  * suites the user wants.
307510d8b0cSniklas  *
308510d8b0cSniklas  * Resulting section names can be:
309510d8b0cSniklas  *  For main mode:
31087ab220eShshoexer  *     {DES,BLF,3DES,CAST,AES}-{MD5,SHA}[-GRP{1,2,5,14}][-{DSS,RSA_SIG}]
311510d8b0cSniklas  *  For quick mode:
312e33f6eeeSho  *     QM-{proto}[-TRP]-{cipher}[-{hash}][-PFS[-{group}]]-SUITE
313e33f6eeeSho  *     where
314e33f6eeeSho  *       {proto}  = ESP, AH
315e33f6eeeSho  *       {cipher} = DES, 3DES, CAST, BLF, AES
316daa7789bSmarkus  *       {hash}   = MD5, SHA, RIPEMD, SHA2-{-256,384,512}
31787ab220eShshoexer  *       {group}  = GRP1, GRP2, GRP5, GRP14
318e33f6eeeSho  *
319e33f6eeeSho  * DH group defaults to MODP_1024.
320510d8b0cSniklas  *
321510d8b0cSniklas  * XXX We may want to support USE_BLOWFISH, USE_TRIPLEDES, etc...
322510d8b0cSniklas  * XXX No EC2N DH support here yet.
323510d8b0cSniklas  */
324510d8b0cSniklas 
3250eb823c5Sniklas /* Find the value for a section+tag in the transaction list.  */
3264c8c122bSho static char *
327eee423ceSho conf_get_trans_str(int trans, char *section, char *tag)
328eee423ceSho {
329eee423ceSho 	struct conf_trans *node, *nf = 0;
330eee423ceSho 
331eee423ceSho 	for (node = TAILQ_FIRST(&conf_trans_queue); node;
332eee423ceSho 	    node = TAILQ_NEXT(node, link))
333df915834Shshoexer 		if (node->trans == trans && strcasecmp(section, node->section)
334df915834Shshoexer 		    == 0 && strcasecmp(tag, node->tag) == 0) {
335eee423ceSho 			if (!nf)
336eee423ceSho 				nf = node;
337eee423ceSho 			else if (node->override)
338eee423ceSho 				nf = node;
339eee423ceSho 		}
3400eb823c5Sniklas 	return nf ? nf->value : 0;
341eee423ceSho }
342eee423ceSho 
3434c8c122bSho #if 0
3444c8c122bSho /* XXX Currently unused.  */
3454c8c122bSho static int
346510d8b0cSniklas conf_find_trans_xf(int phase, char *xf)
347510d8b0cSniklas {
348510d8b0cSniklas 	struct conf_trans *node;
349510d8b0cSniklas 	char	*p;
350510d8b0cSniklas 
351510d8b0cSniklas 	/* Find the relevant transforms and suites, if any.  */
352510d8b0cSniklas 	for (node = TAILQ_FIRST(&conf_trans_queue); node;
353510d8b0cSniklas 	    node = TAILQ_NEXT(node, link))
354eee423ceSho 		if ((phase == 1 && strcmp("Transforms", node->tag) == 0) ||
355fb9475d6Sderaadt 		    (phase == 2 && strcmp("Suites", node->tag) == 0)) {
356510d8b0cSniklas 			p = node->value;
357510d8b0cSniklas 			while ((p = strstr(p, xf)) != NULL)
35850eea14cSho 				if (*(p + strlen(p)) &&
35950eea14cSho 				    *(p + strlen(p)) != ',')
360510d8b0cSniklas 					p += strlen(p);
361510d8b0cSniklas 				else
362510d8b0cSniklas 					return 1;
363510d8b0cSniklas 		}
364510d8b0cSniklas 	return 0;
365510d8b0cSniklas }
3664c8c122bSho #endif
367510d8b0cSniklas 
3684c8c122bSho static void
369d865f642Sho conf_load_defaults_mm(int tr, char *mme, char *mmh, char *mma, char *dhg,
370d865f642Sho     char *mme_p, char *mma_p, char *dhg_p)
371510d8b0cSniklas {
372d865f642Sho 	char sect[CONF_SECT_MAX];
373510d8b0cSniklas 
374d865f642Sho 	snprintf(sect, sizeof sect, "%s-%s%s%s", mme_p, mmh, dhg_p, mma_p);
375510d8b0cSniklas 
37655665484Sho 	LOG_DBG((LOG_MISC, 95, "conf_load_defaults_mm: main mode %s", sect));
377d865f642Sho 
378d865f642Sho 	conf_set(tr, sect, "ENCRYPTION_ALGORITHM", mme, 0, 1);
379d865f642Sho 	if (strcmp(mme, "BLOWFISH_CBC") == 0)
380d865f642Sho 		conf_set(tr, sect, "KEY_LENGTH", CONF_DFLT_VAL_BLF_KEYLEN, 0,
381d865f642Sho 		    1);
382d865f642Sho 	else if (strcmp(mme, "AES_CBC") == 0)
383d865f642Sho 		conf_set(tr, sect, "KEY_LENGTH", CONF_DFLT_VAL_AES_KEYLEN, 0,
384d865f642Sho 		    1);
385d865f642Sho 
386d865f642Sho 	conf_set(tr, sect, "HASH_ALGORITHM", mmh, 0, 1);
387d865f642Sho 	conf_set(tr, sect, "AUTHENTICATION_METHOD", mma, 0, 1);
388d865f642Sho 	conf_set(tr, sect, "GROUP_DESCRIPTION", dhg, 0, 1);
389d865f642Sho 	conf_set(tr, sect, "Life", CONF_DFLT_TAG_LIFE_MAIN_MODE, 0, 1);
390d865f642Sho }
391d865f642Sho 
392d865f642Sho static void
393d865f642Sho conf_load_defaults_qm(int tr, char *qme, char *qmh, char *dhg, char *qme_p,
394d865f642Sho     char *qmh_p, char *dhg_p, int proto, int mode, int pfs)
395d865f642Sho {
396d865f642Sho 	char sect[CONF_SECT_MAX], tmp[CONF_SECT_MAX];
397510d8b0cSniklas 
398510d8b0cSniklas 	/* Helper #defines, incl abbreviations.  */
399510d8b0cSniklas #define PROTO(x)  ((x) ? "AH" : "ESP")
400510d8b0cSniklas #define PFS(x)    ((x) ? "-PFS" : "")
401510d8b0cSniklas #define MODE(x)   ((x) ? "TRANSPORT" : "TUNNEL")
402510d8b0cSniklas #define MODE_p(x) ((x) ? "-TRP" : "")
403d865f642Sho 
404d865f642Sho 	if (proto == 1 && strcmp(qmh, "NONE") == 0) /* AH */
405d865f642Sho 		return;
406d865f642Sho 
407d865f642Sho 	snprintf(tmp, sizeof tmp, "QM-%s%s%s%s%s%s", PROTO(proto),
408d865f642Sho 	    MODE_p(mode), qme_p, qmh_p, PFS(pfs), dhg_p);
409d865f642Sho 
410d865f642Sho 	strlcpy(sect, tmp, CONF_SECT_MAX);
411d865f642Sho 	strlcat(sect, "-SUITE",	CONF_SECT_MAX);
412d865f642Sho 
41355665484Sho 	LOG_DBG((LOG_MISC, 95, "conf_load_defaults_qm: quick mode %s", sect));
414d865f642Sho 
415d865f642Sho 	conf_set(tr, sect, "Protocols", tmp, 0, 1);
416d865f642Sho 	snprintf(sect, sizeof sect, "IPSEC_%s", PROTO(proto));
417d865f642Sho 	conf_set(tr, tmp, "PROTOCOL_ID", sect, 0, 1);
418d865f642Sho 	strlcpy(sect, tmp, CONF_SECT_MAX);
419d865f642Sho 	strlcat(sect, "-XF", CONF_SECT_MAX);
420d865f642Sho 	conf_set(tr, tmp, "Transforms", sect, 0, 1);
421d865f642Sho 
422d865f642Sho 	/*
423d865f642Sho 	 * XXX For now, defaults
424d865f642Sho 	 * contain one xf per protocol.
425d865f642Sho 	 */
426d865f642Sho 	conf_set(tr, sect, "TRANSFORM_ID", qme, 0, 1);
427d865f642Sho 	if (strcmp(qme ,"BLOWFISH") == 0)
428d865f642Sho 		conf_set(tr, sect, "KEY_LENGTH", CONF_DFLT_VAL_BLF_KEYLEN, 0,
429d865f642Sho 			 1);
430d865f642Sho 	else if (strcmp(qme ,"AES") == 0)
431d865f642Sho 		conf_set(tr, sect, "KEY_LENGTH", CONF_DFLT_VAL_AES_KEYLEN, 0,
432d865f642Sho 			 1);
433d865f642Sho 	conf_set(tr, sect, "ENCAPSULATION_MODE", MODE(mode), 0, 1);
434d865f642Sho 	if (strcmp(qmh, "NONE")) {
435d865f642Sho 		conf_set(tr, sect, "AUTHENTICATION_ALGORITHM", qmh, 0, 1);
436d865f642Sho 
437d865f642Sho 		/* XXX Another shortcut to keep length down */
438d865f642Sho 		if (pfs)
439d865f642Sho 			conf_set(tr, sect, "GROUP_DESCRIPTION", dhg, 0, 1);
440d865f642Sho 	}
441d865f642Sho 
442d865f642Sho 	/* XXX Lifetimes depending on enc/auth strength? */
443d865f642Sho 	conf_set(tr, sect, "Life", CONF_DFLT_TAG_LIFE_QUICK_MODE, 0, 1);
444d865f642Sho }
445d865f642Sho 
446d865f642Sho static void
447d865f642Sho conf_load_defaults(int tr)
448d865f642Sho {
449d865f642Sho 	int	 enc, auth, hash, group, proto, mode, pfs;
450d865f642Sho 	char	*dflt;
451d865f642Sho 
452d865f642Sho 	char	*mm_auth[] = {"PRE_SHARED", "DSS", "RSA_SIG", 0};
453d865f642Sho 	char	*mm_auth_p[] = {"", "-DSS", "-RSA_SIG", 0};
454d865f642Sho 	char	*mm_hash[] = {"MD5", "SHA", 0};
455df915834Shshoexer 	char	*mm_enc[] = {"DES_CBC", "BLOWFISH_CBC", "3DES_CBC", "CAST_CBC",
456df915834Shshoexer 		    "AES_CBC", 0};
457d865f642Sho 	char	*mm_enc_p[] = {"DES", "BLF", "3DES", "CAST", "AES", 0};
458d865f642Sho 	char	*dhgroup[] = {"MODP_1024", "MODP_768", "MODP_1024",
459d865f642Sho 		    "MODP_1536", "MODP_2048", 0};
460df915834Shshoexer 	char	*dhgroup_p[] = {"", "-GRP1", "-GRP2", "-GRP5", "-GRP14", 0};
461df915834Shshoexer 	char	*qm_enc[] = {"DES", "3DES", "CAST", "BLOWFISH", "AES", 0};
462df915834Shshoexer 	char	*qm_enc_p[] = {"-DES", "-3DES", "-CAST", "-BLF", "-AES", 0};
463d865f642Sho 	char	*qm_hash[] = {"HMAC_MD5", "HMAC_SHA", "HMAC_RIPEMD",
464df915834Shshoexer 		    "HMAC_SHA2_256", "HMAC_SHA2_384", "HMAC_SHA2_512", "NONE",
465df915834Shshoexer 		    0};
466d865f642Sho 	char	*qm_hash_p[] = {"-MD5", "-SHA", "-RIPEMD", "-SHA2-256",
467d865f642Sho 		    "-SHA2-384", "-SHA2-512", "", 0};
468510d8b0cSniklas 
469510d8b0cSniklas 	/* General and X509 defaults */
470510d8b0cSniklas 	conf_set(tr, "General", "Retransmits", CONF_DFLT_RETRANSMITS, 0, 1);
471d865f642Sho 	conf_set(tr, "General", "Exchange-max-time", CONF_DFLT_EXCH_MAX_TIME,
472d865f642Sho 	    0, 1);
473b6e0b5cbShshoexer 	conf_set(tr, "General", "Use-Keynote", CONF_DFLT_USE_KEYNOTE, 0, 1);
474510d8b0cSniklas 	conf_set(tr, "General", "Policy-file", CONF_DFLT_POLICY_FILE, 0, 1);
475d865f642Sho 	conf_set(tr, "General", "Pubkey-directory", CONF_DFLT_PUBKEY_DIR, 0,
476d865f642Sho 	    1);
477510d8b0cSniklas 
478510d8b0cSniklas #ifdef USE_X509
479d865f642Sho 	conf_set(tr, "X509-certificates", "CA-directory",
480d865f642Sho 	    CONF_DFLT_X509_CA_DIR, 0, 1);
481d865f642Sho 	conf_set(tr, "X509-certificates", "Cert-directory",
482d865f642Sho 	    CONF_DFLT_X509_CERT_DIR, 0, 1);
483d865f642Sho 	conf_set(tr, "X509-certificates", "Private-key",
484d865f642Sho 	    CONF_DFLT_X509_PRIVATE_KEY, 0, 1);
485d865f642Sho 	conf_set(tr, "X509-certificates", "CRL-directory",
486d865f642Sho 	    CONF_DFLT_X509_CRL_DIR, 0, 1);
487510d8b0cSniklas #endif
488510d8b0cSniklas 
48913e19299Sniklas #ifdef USE_KEYNOTE
490df915834Shshoexer 	conf_set(tr, "KeyNote", "Credential-directory",
491df915834Shshoexer 	    CONF_DFLT_KEYNOTE_CRED_DIR, 0, 1);
49213e19299Sniklas #endif
49313e19299Sniklas 
49428d27e6cSangelos 	/* Lifetimes. XXX p1/p2 vs main/quick mode may be unclear.  */
495eee423ceSho 	dflt = conf_get_trans_str(tr, "General", "Default-phase-1-lifetime");
49628d27e6cSangelos 	conf_set(tr, CONF_DFLT_TAG_LIFE_MAIN_MODE, "LIFE_TYPE",
49728d27e6cSangelos 	    CONF_DFLT_TYPE_LIFE_MAIN_MODE, 0, 1);
49828d27e6cSangelos 	conf_set(tr, CONF_DFLT_TAG_LIFE_MAIN_MODE, "LIFE_DURATION",
49928d27e6cSangelos 	    (dflt ? dflt : CONF_DFLT_VAL_LIFE_MAIN_MODE), 0, 1);
50028d27e6cSangelos 
501eee423ceSho 	dflt = conf_get_trans_str(tr, "General", "Default-phase-2-lifetime");
50228d27e6cSangelos 	conf_set(tr, CONF_DFLT_TAG_LIFE_QUICK_MODE, "LIFE_TYPE",
50328d27e6cSangelos 	    CONF_DFLT_TYPE_LIFE_QUICK_MODE, 0, 1);
50428d27e6cSangelos 	conf_set(tr, CONF_DFLT_TAG_LIFE_QUICK_MODE, "LIFE_DURATION",
50528d27e6cSangelos 	    (dflt ? dflt : CONF_DFLT_VAL_LIFE_QUICK_MODE), 0, 1);
50628d27e6cSangelos 
507419caefeSho 	/* Default Phase-1 Configuration section */
508419caefeSho 	conf_set(tr, CONF_DFLT_TAG_PHASE1_CONFIG, "EXCHANGE_TYPE",
509419caefeSho 	    CONF_DFLT_PHASE1_EXCH_TYPE, 0, 1);
510419caefeSho 	conf_set(tr, CONF_DFLT_TAG_PHASE1_CONFIG, "Transforms",
511419caefeSho 	    CONF_DFLT_PHASE1_TRANSFORMS, 0, 1);
512419caefeSho 
513510d8b0cSniklas 	/* Main modes */
514d865f642Sho 	for (enc = 0; mm_enc[enc]; enc++)
515d865f642Sho 		for (hash = 0; mm_hash[hash]; hash++)
516d865f642Sho 			for (auth = 0; mm_auth[auth]; auth++)
517d865f642Sho 				for (group = 0; dhgroup_p[group]; group++)
518d865f642Sho 					conf_load_defaults_mm (tr, mm_enc[enc],
519d865f642Sho 					    mm_hash[hash], mm_auth[auth],
520d865f642Sho 					    dhgroup[group], mm_enc_p[enc],
521d865f642Sho 					    mm_auth_p[auth], dhgroup_p[group]);
522510d8b0cSniklas 
523cc475db6Sniklas 	/* Setup a default Phase 1 entry */
524cc475db6Sniklas 	conf_set(tr, "Phase 1", "Default", "Default-phase-1", 0, 1);
525cc475db6Sniklas 	conf_set(tr, "Default-phase-1", "Phase", "1", 0, 1);
526cc475db6Sniklas 	conf_set(tr, "Default-phase-1", "Configuration",
527cc475db6Sniklas 	    "Default-phase-1-configuration", 0, 1);
528eee423ceSho 	dflt = conf_get_trans_str(tr, "General", "Default-phase-1-ID");
529cc475db6Sniklas 	if (dflt)
530cc475db6Sniklas 		conf_set(tr, "Default-phase-1", "ID", dflt, 0, 1);
531cc475db6Sniklas 
532510d8b0cSniklas 	/* Quick modes */
533d865f642Sho 	for (enc = 0; qm_enc[enc]; enc++)
534d865f642Sho 		for (proto = 0; proto < 2; proto++)
535d865f642Sho 			for (mode = 0; mode < 2; mode++)
536d865f642Sho 				for (pfs = 0; pfs < 2; pfs++)
537d865f642Sho 					for (hash = 0; qm_hash[hash]; hash++)
538d865f642Sho 						for (group = 0;
539d865f642Sho 						    dhgroup_p[group]; group++)
540d865f642Sho 							conf_load_defaults_qm(
541d865f642Sho 							    tr, qm_enc[enc],
542d865f642Sho 							    qm_hash[hash],
543d865f642Sho 							    dhgroup[group],
54480cd8be9Sderaadt 							    qm_enc_p[enc],
54580cd8be9Sderaadt 							    qm_hash_p[hash],
546d865f642Sho 							    dhgroup_p[group],
547d865f642Sho 							    proto, mode, pfs);
548510d8b0cSniklas }
549510d8b0cSniklas 
5502040585eSniklas void
5512040585eSniklas conf_init(void)
5522040585eSniklas {
553cde22268Sho 	unsigned int i;
5542040585eSniklas 
555f8f1e192Sniklas 	for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
556f8f1e192Sniklas 		LIST_INIT(&conf_bindings[i]);
557f8f1e192Sniklas 	TAILQ_INIT(&conf_trans_queue);
558f8f1e192Sniklas 	conf_reinit();
5592040585eSniklas }
5602040585eSniklas 
561f8f1e192Sniklas /* Open the config file and map it into our address space, then parse it.  */
562f8f1e192Sniklas void
563f8f1e192Sniklas conf_reinit(void)
564f8f1e192Sniklas {
565f8f1e192Sniklas 	struct conf_binding *cb = 0;
566cde22268Sho 	int	 fd, trans;
567cde22268Sho 	unsigned int i;
56852e9f6e6Sho 	size_t	 sz;
569f8f1e192Sniklas 	char	*new_conf_addr = 0;
570f8f1e192Sniklas 
5712872008fShshoexer 	if ((fd = monitor_open(conf_path, O_RDONLY, 0)) != -1) {
5722872008fShshoexer 		if (check_file_secrecy_fd(fd, conf_path, &sz))
5732872008fShshoexer 			goto fail;
574bda02003Sniklas 
575f8f1e192Sniklas 		new_conf_addr = malloc(sz);
576fb9475d6Sderaadt 		if (!new_conf_addr) {
577df915834Shshoexer 			log_error("conf_reinit: malloc (%lu) failed",
578df915834Shshoexer 			    (unsigned long)sz);
579f8f1e192Sniklas 			goto fail;
580f8f1e192Sniklas 		}
5812040585eSniklas 		/* XXX I assume short reads won't happen here.  */
582fb9475d6Sderaadt 		if (read(fd, new_conf_addr, sz) != (int)sz) {
5837eb3b581Sderaadt 			log_error("conf_reinit: read (%d, %p, %lu) failed",
5847eb3b581Sderaadt 			    fd, new_conf_addr, (unsigned long)sz);
585f8f1e192Sniklas 			goto fail;
586f8f1e192Sniklas 		}
587ea1948caSho 		close(fd);
5882040585eSniklas 
589f8f1e192Sniklas 		trans = conf_begin();
590f8f1e192Sniklas 
591f8f1e192Sniklas 		/* XXX Should we not care about errors and rollback?  */
592f8f1e192Sniklas 		conf_parse(trans, new_conf_addr, sz);
5932872008fShshoexer 	} else {
5942872008fShshoexer 		if (errno != ENOENT)
5952872008fShshoexer 			log_error("conf_reinit: open(\"%s\", O_RDONLY, 0) "
5962872008fShshoexer 			    "failed", conf_path);
5972872008fShshoexer 
5983eed80ffSniklas 		trans = conf_begin();
5992872008fShshoexer 	}
600f8f1e192Sniklas 
601510d8b0cSniklas 	/* Load default configuration values.  */
602510d8b0cSniklas 	conf_load_defaults(trans);
603510d8b0cSniklas 
604f8f1e192Sniklas 	/* Free potential existing configuration.  */
605fb9475d6Sderaadt 	if (conf_addr) {
606df915834Shshoexer 		for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0];
607df915834Shshoexer 		    i++)
608f8f1e192Sniklas 			for (cb = LIST_FIRST(&conf_bindings[i]); cb;
609f8f1e192Sniklas 			    cb = LIST_FIRST(&conf_bindings[i]))
610f8f1e192Sniklas 				conf_remove_now(cb->section, cb->tag);
611f8f1e192Sniklas 		free(conf_addr);
612f8f1e192Sniklas 	}
613f8f1e192Sniklas 	conf_end(trans, 1);
614f8f1e192Sniklas 	conf_addr = new_conf_addr;
615f8f1e192Sniklas 	return;
616f8f1e192Sniklas 
617f8f1e192Sniklas fail:
618f8f1e192Sniklas 	if (new_conf_addr)
619f8f1e192Sniklas 		free(new_conf_addr);
620f8f1e192Sniklas 	close(fd);
6212040585eSniklas }
6222040585eSniklas 
623a2d30fd1Sniklas /*
624a2d30fd1Sniklas  * Return the numeric value denoted by TAG in section SECTION or DEF
625a2d30fd1Sniklas  * if that tag does not exist.
626a2d30fd1Sniklas  */
6272040585eSniklas int
628a2d30fd1Sniklas conf_get_num(char *section, char *tag, int def)
6292040585eSniklas {
6302040585eSniklas 	char	*value = conf_get_str(section, tag);
6312040585eSniklas 
6322040585eSniklas 	if (value)
6332040585eSniklas 		return atoi(value);
634a2d30fd1Sniklas 	return def;
6352040585eSniklas }
6362040585eSniklas 
63781c21331Sniklas /*
63881c21331Sniklas  * Return the socket endpoint address denoted by TAG in SECTION as a
63981c21331Sniklas  * struct sockaddr.  It is the callers responsibility to deallocate
64081c21331Sniklas  * this structure when it is finished with it.
64181c21331Sniklas  */
64281c21331Sniklas struct sockaddr *
64381c21331Sniklas conf_get_address(char *section, char *tag)
64481c21331Sniklas {
64581c21331Sniklas 	char	*value = conf_get_str(section, tag);
64681c21331Sniklas 	struct sockaddr *sa;
64781c21331Sniklas 
64881c21331Sniklas 	if (!value)
64981c21331Sniklas 		return 0;
650e3283cbfSmcbride 	if (text2sockaddr(value, 0, &sa, 0, 0) == -1)
65181c21331Sniklas 		return 0;
65281c21331Sniklas 	return sa;
65381c21331Sniklas }
65481c21331Sniklas 
65582d8fe06Sniklas /* Validate X according to the range denoted by TAG in section SECTION.  */
65682d8fe06Sniklas int
65782d8fe06Sniklas conf_match_num(char *section, char *tag, int x)
65882d8fe06Sniklas {
65982d8fe06Sniklas 	char	*value = conf_get_str(section, tag);
66082d8fe06Sniklas 	int	 val, min, max, n;
66182d8fe06Sniklas 
66282d8fe06Sniklas 	if (!value)
66382d8fe06Sniklas 		return 0;
66482d8fe06Sniklas 	n = sscanf(value, "%d,%d:%d", &val, &min, &max);
665fb9475d6Sderaadt 	switch (n) {
66682d8fe06Sniklas 	case 1:
66755665484Sho 		LOG_DBG((LOG_MISC, 95, "conf_match_num: %s:%s %d==%d?",
668df915834Shshoexer 		    section, tag, val, x));
66982d8fe06Sniklas 		return x == val;
67082d8fe06Sniklas 	case 3:
67155665484Sho 		LOG_DBG((LOG_MISC, 95, "conf_match_num: %s:%s %d<=%d<=%d?",
672df915834Shshoexer 		    section, tag, min, x, max));
67382d8fe06Sniklas 		return min <= x && max >= x;
67482d8fe06Sniklas 	default:
675df915834Shshoexer 		log_error("conf_match_num: section %s tag %s: invalid number "
676df915834Shshoexer 		    "spec %s", section, tag, value);
67782d8fe06Sniklas 	}
67882d8fe06Sniklas 	return 0;
67982d8fe06Sniklas }
68082d8fe06Sniklas 
6812040585eSniklas /* Return the string value denoted by TAG in section SECTION.  */
6822040585eSniklas char *
6832040585eSniklas conf_get_str(char *section, char *tag)
6842040585eSniklas {
6852040585eSniklas 	struct conf_binding *cb;
6862040585eSniklas 
687f8f1e192Sniklas 	for (cb = LIST_FIRST(&conf_bindings[conf_hash(section)]); cb;
688f8f1e192Sniklas 	    cb = LIST_NEXT(cb, link))
689df915834Shshoexer 		if (strcasecmp(section, cb->section) == 0 &&
690df915834Shshoexer 		    strcasecmp(tag, cb->tag) == 0) {
691df915834Shshoexer 			LOG_DBG((LOG_MISC, 95, "conf_get_str: [%s]:%s->%s",
692df915834Shshoexer 			    section, tag, cb->value));
6932040585eSniklas 			return cb->value;
6942040585eSniklas 		}
695395a452cSho 	LOG_DBG((LOG_MISC, 95,
696f8f1e192Sniklas 	     "conf_get_str: configuration value not found [%s]:%s", section,
69751ca15aeSniklas 	     tag));
6982040585eSniklas 	return 0;
6992040585eSniklas }
7002040585eSniklas 
701a9753648Sniklas /*
702a9753648Sniklas  * Build a list of string values out of the comma separated value denoted by
703a9753648Sniklas  * TAG in SECTION.
704a9753648Sniklas  */
7052040585eSniklas struct conf_list *
7062040585eSniklas conf_get_list(char *section, char *tag)
7072040585eSniklas {
708cde22268Sho 	char	*liststr = 0, *p, *field, *t;
7092040585eSniklas 	struct conf_list *list = 0;
71078ef4cbaScloder 	struct conf_list_node *node = 0;
7112040585eSniklas 
7122040585eSniklas 	list = malloc(sizeof *list);
7132040585eSniklas 	if (!list)
7142040585eSniklas 		goto cleanup;
7152040585eSniklas 	TAILQ_INIT(&list->fields);
7162040585eSniklas 	list->cnt = 0;
7172040585eSniklas 	liststr = conf_get_str(section, tag);
7182040585eSniklas 	if (!liststr)
7192040585eSniklas 		goto cleanup;
7202040585eSniklas 	liststr = strdup(liststr);
7212040585eSniklas 	if (!liststr)
7222040585eSniklas 		goto cleanup;
7232040585eSniklas 	p = liststr;
724fb9475d6Sderaadt 	while ((field = strsep(&p, ",")) != NULL) {
725cde22268Sho 		/* Skip leading whitespace */
726cde22268Sho 		while (isspace(*field))
727cde22268Sho 			field++;
728cde22268Sho 		/* Skip trailing whitespace */
729cde22268Sho 		if (p)
730cde22268Sho 			for (t = p - 1; t > field && isspace(*t); t--)
731cde22268Sho 				*t = '\0';
732fb9475d6Sderaadt 		if (*field == '\0') {
7332040585eSniklas 			log_print("conf_get_list: empty field, ignoring...");
7342040585eSniklas 			continue;
7352040585eSniklas 		}
7362040585eSniklas 		list->cnt++;
737a9753648Sniklas 		node = calloc(1, sizeof *node);
7382040585eSniklas 		if (!node)
7392040585eSniklas 			goto cleanup;
740a9753648Sniklas 		node->field = strdup(field);
741a9753648Sniklas 		if (!node->field)
742a9753648Sniklas 			goto cleanup;
7432040585eSniklas 		TAILQ_INSERT_TAIL(&list->fields, node, link);
7442040585eSniklas 	}
745a9753648Sniklas 	free(liststr);
7462040585eSniklas 	return list;
7472040585eSniklas 
7482040585eSniklas cleanup:
74978ef4cbaScloder 	if (node)
75078ef4cbaScloder 		free(node);
7512040585eSniklas 	if (list)
7522040585eSniklas 		conf_free_list(list);
7532040585eSniklas 	if (liststr)
7542040585eSniklas 		free(liststr);
7552040585eSniklas 	return 0;
7562040585eSniklas }
7572040585eSniklas 
75882d8fe06Sniklas struct conf_list *
75982d8fe06Sniklas conf_get_tag_list(char *section)
76082d8fe06Sniklas {
76182d8fe06Sniklas 	struct conf_list *list = 0;
76278ef4cbaScloder 	struct conf_list_node *node = 0;
76382d8fe06Sniklas 	struct conf_binding *cb;
76482d8fe06Sniklas 
76582d8fe06Sniklas 	list = malloc(sizeof *list);
76682d8fe06Sniklas 	if (!list)
76782d8fe06Sniklas 		goto cleanup;
76882d8fe06Sniklas 	TAILQ_INIT(&list->fields);
76982d8fe06Sniklas 	list->cnt = 0;
770f8f1e192Sniklas 	for (cb = LIST_FIRST(&conf_bindings[conf_hash(section)]); cb;
771f8f1e192Sniklas 	    cb = LIST_NEXT(cb, link))
772fb9475d6Sderaadt 		if (strcasecmp(section, cb->section) == 0) {
77382d8fe06Sniklas 			list->cnt++;
774a9753648Sniklas 			node = calloc(1, sizeof *node);
77582d8fe06Sniklas 			if (!node)
77682d8fe06Sniklas 				goto cleanup;
777a9753648Sniklas 			node->field = strdup(cb->tag);
778a9753648Sniklas 			if (!node->field)
779a9753648Sniklas 				goto cleanup;
78082d8fe06Sniklas 			TAILQ_INSERT_TAIL(&list->fields, node, link);
78182d8fe06Sniklas 		}
78282d8fe06Sniklas 	return list;
78382d8fe06Sniklas 
78482d8fe06Sniklas cleanup:
78578ef4cbaScloder 	if (node)
78678ef4cbaScloder 		free(node);
78782d8fe06Sniklas 	if (list)
78882d8fe06Sniklas 		conf_free_list(list);
78982d8fe06Sniklas 	return 0;
79082d8fe06Sniklas }
79182d8fe06Sniklas 
7922040585eSniklas /* Decode a PEM encoded buffer.  */
7932040585eSniklas int
7942040585eSniklas conf_decode_base64(u_int8_t *out, u_int32_t *len, u_char *buf)
7952040585eSniklas {
7962040585eSniklas 	u_int32_t	c = 0;
7972040585eSniklas 	u_int8_t	c1, c2, c3, c4;
7982040585eSniklas 
799fb9475d6Sderaadt 	while (*buf) {
8002040585eSniklas 		if (*buf > 127 || (c1 = asc2bin[*buf]) == 255)
8012040585eSniklas 			return 0;
8022040585eSniklas 		buf++;
8032040585eSniklas 
8042040585eSniklas 		if (*buf > 127 || (c2 = asc2bin[*buf]) == 255)
8052040585eSniklas 			return 0;
8062040585eSniklas 		buf++;
8072040585eSniklas 
808fb9475d6Sderaadt 		if (*buf == '=') {
8092040585eSniklas 			c3 = c4 = 0;
8102040585eSniklas 			c++;
8112040585eSniklas 
8122040585eSniklas 			/* Check last four bit */
8132040585eSniklas 			if (c2 & 0xF)
8142040585eSniklas 				return 0;
8152040585eSniklas 
816b26670e8Sho 			if (strcmp((char *)buf, "==") == 0)
8172040585eSniklas 				buf++;
8182040585eSniklas 			else
8192040585eSniklas 				return 0;
820fb9475d6Sderaadt 		} else if (*buf > 127 || (c3 = asc2bin[*buf]) == 255)
8212040585eSniklas 			return 0;
822fb9475d6Sderaadt 		else {
823fb9475d6Sderaadt 			if (*++buf == '=') {
8242040585eSniklas 				c4 = 0;
8252040585eSniklas 				c += 2;
8262040585eSniklas 
8272040585eSniklas 				/* Check last two bit */
8282040585eSniklas 				if (c3 & 3)
8292040585eSniklas 					return 0;
8302040585eSniklas 
831b26670e8Sho 				if (strcmp((char *)buf, "="))
8322040585eSniklas 					return 0;
8332040585eSniklas 
834fb9475d6Sderaadt 			} else if (*buf > 127 || (c4 = asc2bin[*buf]) == 255)
8352040585eSniklas 				return 0;
8362040585eSniklas 			else
8372040585eSniklas 				c += 3;
8382040585eSniklas 		}
8392040585eSniklas 
8402040585eSniklas 		buf++;
8412040585eSniklas 		*out++ = (c1 << 2) | (c2 >> 4);
8422040585eSniklas 		*out++ = (c2 << 4) | (c3 >> 2);
8432040585eSniklas 		*out++ = (c3 << 6) | c4;
8442040585eSniklas 	}
8452040585eSniklas 
8462040585eSniklas 	*len = c;
8472040585eSniklas 	return 1;
8482040585eSniklas 
8492040585eSniklas }
8502040585eSniklas 
8512040585eSniklas void
8522040585eSniklas conf_free_list(struct conf_list *list)
8532040585eSniklas {
854a9753648Sniklas 	struct conf_list_node *node = TAILQ_FIRST(&list->fields);
855a9753648Sniklas 
856fb9475d6Sderaadt 	while (node) {
857a9753648Sniklas 		TAILQ_REMOVE(&list->fields, node, link);
858a9753648Sniklas 		if (node->field)
859a9753648Sniklas 			free(node->field);
860a9753648Sniklas 		free(node);
861a9753648Sniklas 		node = TAILQ_FIRST(&list->fields);
862a9753648Sniklas 	}
8632040585eSniklas 	free(list);
8642040585eSniklas }
865f8f1e192Sniklas 
866f8f1e192Sniklas int
867f8f1e192Sniklas conf_begin(void)
868f8f1e192Sniklas {
869f8f1e192Sniklas 	static int	seq = 0;
870f8f1e192Sniklas 
871f8f1e192Sniklas 	return ++seq;
872f8f1e192Sniklas }
873f8f1e192Sniklas 
874f8f1e192Sniklas static struct conf_trans *
875f8f1e192Sniklas conf_trans_node(int transaction, enum conf_op op)
876f8f1e192Sniklas {
877f8f1e192Sniklas 	struct conf_trans *node;
878f8f1e192Sniklas 
879f8f1e192Sniklas 	node = calloc(1, sizeof *node);
880fb9475d6Sderaadt 	if (!node) {
8817eb3b581Sderaadt 		log_error("conf_trans_node: calloc (1, %lu) failed",
8827eb3b581Sderaadt 		    (unsigned long)sizeof *node);
883f8f1e192Sniklas 		return 0;
884f8f1e192Sniklas 	}
885f8f1e192Sniklas 	node->trans = transaction;
886f8f1e192Sniklas 	node->op = op;
887f8f1e192Sniklas 	TAILQ_INSERT_TAIL(&conf_trans_queue, node, link);
888f8f1e192Sniklas 	return node;
889f8f1e192Sniklas }
890f8f1e192Sniklas 
891f8f1e192Sniklas /* Queue a set operation.  */
892f8f1e192Sniklas int
893510d8b0cSniklas conf_set(int transaction, char *section, char *tag, char *value, int override,
894510d8b0cSniklas     int is_default)
895f8f1e192Sniklas {
896f8f1e192Sniklas 	struct conf_trans *node;
897f8f1e192Sniklas 
898f8f1e192Sniklas 	node = conf_trans_node(transaction, CONF_SET);
899f8f1e192Sniklas 	if (!node)
900f8f1e192Sniklas 		return 1;
901f8f1e192Sniklas 	node->section = strdup(section);
902fb9475d6Sderaadt 	if (!node->section) {
903f8f1e192Sniklas 		log_error("conf_set: strdup (\"%s\") failed", section);
904f8f1e192Sniklas 		goto fail;
905f8f1e192Sniklas 	}
906f8f1e192Sniklas 	node->tag = strdup(tag);
907fb9475d6Sderaadt 	if (!node->tag) {
908f8f1e192Sniklas 		log_error("conf_set: strdup (\"%s\") failed", tag);
909f8f1e192Sniklas 		goto fail;
910f8f1e192Sniklas 	}
911f8f1e192Sniklas 	node->value = strdup(value);
912fb9475d6Sderaadt 	if (!node->value) {
913f8f1e192Sniklas 		log_error("conf_set: strdup (\"%s\") failed", value);
914f8f1e192Sniklas 		goto fail;
915f8f1e192Sniklas 	}
916f8f1e192Sniklas 	node->override = override;
917510d8b0cSniklas 	node->is_default = is_default;
918f8f1e192Sniklas 	return 0;
919f8f1e192Sniklas 
920f8f1e192Sniklas fail:
921f8f1e192Sniklas 	if (node->tag)
922f8f1e192Sniklas 		free(node->tag);
923f8f1e192Sniklas 	if (node->section)
924f8f1e192Sniklas 		free(node->section);
925f8f1e192Sniklas 	if (node)
926f8f1e192Sniklas 		free(node);
927f8f1e192Sniklas 	return 1;
928f8f1e192Sniklas }
929f8f1e192Sniklas 
930f8f1e192Sniklas /* Queue a remove operation.  */
931f8f1e192Sniklas int
932f8f1e192Sniklas conf_remove(int transaction, char *section, char *tag)
933f8f1e192Sniklas {
934f8f1e192Sniklas 	struct conf_trans *node;
935f8f1e192Sniklas 
936f8f1e192Sniklas 	node = conf_trans_node(transaction, CONF_REMOVE);
937f8f1e192Sniklas 	if (!node)
938f8f1e192Sniklas 		goto fail;
939f8f1e192Sniklas 	node->section = strdup(section);
940fb9475d6Sderaadt 	if (!node->section) {
941f8f1e192Sniklas 		log_error("conf_remove: strdup (\"%s\") failed", section);
942f8f1e192Sniklas 		goto fail;
943f8f1e192Sniklas 	}
944f8f1e192Sniklas 	node->tag = strdup(tag);
945fb9475d6Sderaadt 	if (!node->tag) {
946f8f1e192Sniklas 		log_error("conf_remove: strdup (\"%s\") failed", tag);
947f8f1e192Sniklas 		goto fail;
948f8f1e192Sniklas 	}
949f8f1e192Sniklas 	return 0;
950f8f1e192Sniklas 
951f8f1e192Sniklas fail:
952f8f1e192Sniklas 	if (node->section)
953f8f1e192Sniklas 		free(node->section);
954f8f1e192Sniklas 	if (node)
955f8f1e192Sniklas 		free(node);
956f8f1e192Sniklas 	return 1;
957f8f1e192Sniklas }
958f8f1e192Sniklas 
959f8f1e192Sniklas /* Queue a remove section operation.  */
960f8f1e192Sniklas int
961f8f1e192Sniklas conf_remove_section(int transaction, char *section)
962f8f1e192Sniklas {
963f8f1e192Sniklas 	struct conf_trans *node;
964f8f1e192Sniklas 
965f8f1e192Sniklas 	node = conf_trans_node(transaction, CONF_REMOVE_SECTION);
966f8f1e192Sniklas 	if (!node)
967f8f1e192Sniklas 		goto fail;
968f8f1e192Sniklas 	node->section = strdup(section);
969fb9475d6Sderaadt 	if (!node->section) {
970df915834Shshoexer 		log_error("conf_remove_section: strdup (\"%s\") failed",
971df915834Shshoexer 		    section);
972f8f1e192Sniklas 		goto fail;
973f8f1e192Sniklas 	}
974f8f1e192Sniklas 	return 0;
975f8f1e192Sniklas 
976f8f1e192Sniklas fail:
977f8f1e192Sniklas 	if (node)
978f8f1e192Sniklas 		free(node);
979f8f1e192Sniklas 	return 1;
980f8f1e192Sniklas }
981f8f1e192Sniklas 
982f8f1e192Sniklas /* Execute all queued operations for this transaction.  Cleanup.  */
983f8f1e192Sniklas int
984f8f1e192Sniklas conf_end(int transaction, int commit)
985f8f1e192Sniklas {
986f8f1e192Sniklas 	struct conf_trans *node, *next;
987f8f1e192Sniklas 
988fb9475d6Sderaadt 	for (node = TAILQ_FIRST(&conf_trans_queue); node; node = next) {
989f8f1e192Sniklas 		next = TAILQ_NEXT(node, link);
990fb9475d6Sderaadt 		if (node->trans == transaction) {
991f8f1e192Sniklas 			if (commit)
992fb9475d6Sderaadt 				switch (node->op) {
993f8f1e192Sniklas 				case CONF_SET:
99499cdfc90Sderaadt 					conf_set_now(node->section, node->tag,
99599cdfc90Sderaadt 					    node->value, node->override,
99699cdfc90Sderaadt 					    node->is_default);
997f8f1e192Sniklas 					break;
998f8f1e192Sniklas 				case CONF_REMOVE:
999df915834Shshoexer 					conf_remove_now(node->section,
1000df915834Shshoexer 					    node->tag);
1001f8f1e192Sniklas 					break;
1002f8f1e192Sniklas 				case CONF_REMOVE_SECTION:
1003f8f1e192Sniklas 					conf_remove_section_now(node->section);
1004f8f1e192Sniklas 					break;
1005f8f1e192Sniklas 				default:
1006df915834Shshoexer 					log_print("conf_end: unknown "
1007df915834Shshoexer 					    "operation: %d", node->op);
1008f8f1e192Sniklas 				}
1009f8f1e192Sniklas 			TAILQ_REMOVE(&conf_trans_queue, node, link);
1010074d67afSniklas 			if (node->section)
1011074d67afSniklas 				free(node->section);
1012074d67afSniklas 			if (node->tag)
1013074d67afSniklas 				free(node->tag);
1014074d67afSniklas 			if (node->value)
1015074d67afSniklas 				free(node->value);
1016f8f1e192Sniklas 			free(node);
1017f8f1e192Sniklas 		}
1018f8f1e192Sniklas 	}
1019f8f1e192Sniklas 	return 0;
1020f8f1e192Sniklas }
1021510d8b0cSniklas 
102294de5165Sniklas /*
102394de5165Sniklas  * Dump running configuration upon SIGUSR1.
1024395a452cSho  * Configuration is "stored in reverse order", so reverse it again.
102594de5165Sniklas  */
1026510d8b0cSniklas struct dumper {
1027510d8b0cSniklas 	char	*s, *v;
1028510d8b0cSniklas 	struct dumper *next;
1029510d8b0cSniklas };
1030510d8b0cSniklas 
1031510d8b0cSniklas static void
1032510d8b0cSniklas conf_report_dump(struct dumper *node)
1033510d8b0cSniklas {
1034510d8b0cSniklas 	/* Recursive, cleanup when we're done.  */
1035510d8b0cSniklas 
1036510d8b0cSniklas 	if (node->next)
1037510d8b0cSniklas 		conf_report_dump(node->next);
1038510d8b0cSniklas 
1039510d8b0cSniklas 	if (node->v)
1040510d8b0cSniklas 		LOG_DBG((LOG_REPORT, 0, "%s=\t%s", node->s, node->v));
1041fb9475d6Sderaadt 	else if (node->s) {
1042510d8b0cSniklas 		LOG_DBG((LOG_REPORT, 0, "%s", node->s));
1043510d8b0cSniklas 		if (strlen(node->s) > 0)
1044510d8b0cSniklas 			free(node->s);
1045510d8b0cSniklas 	}
1046510d8b0cSniklas 	free(node);
1047510d8b0cSniklas }
1048510d8b0cSniklas 
1049510d8b0cSniklas void
1050510d8b0cSniklas conf_report(void)
1051510d8b0cSniklas {
10520eb823c5Sniklas 	struct conf_binding *cb, *last = 0;
1053cde22268Sho 	unsigned int	i, len;
1054510d8b0cSniklas 	char           *current_section = (char *)0;
1055510d8b0cSniklas 	struct dumper  *dumper, *dnode;
1056510d8b0cSniklas 
1057592a196eSniklas 	dumper = dnode = (struct dumper *)calloc(1, sizeof *dumper);
1058510d8b0cSniklas 	if (!dumper)
1059510d8b0cSniklas 		goto mem_fail;
1060510d8b0cSniklas 
1061510d8b0cSniklas 	LOG_DBG((LOG_REPORT, 0, "conf_report: dumping running configuration"));
1062510d8b0cSniklas 
1063510d8b0cSniklas 	for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
1064510d8b0cSniklas 		for (cb = LIST_FIRST(&conf_bindings[i]); cb;
1065fb9475d6Sderaadt 		    cb = LIST_NEXT(cb, link)) {
1066fb9475d6Sderaadt 			if (!cb->is_default) {
10670eb823c5Sniklas 				/* Dump this entry.  */
1068df915834Shshoexer 				if (!current_section || strcmp(cb->section,
1069df915834Shshoexer 				    current_section)) {
1070fb9475d6Sderaadt 					if (current_section) {
1071df915834Shshoexer 						len = strlen(current_section)
1072df915834Shshoexer 						    + 3;
107353922df5Sho 						dnode->s = malloc(len);
1074510d8b0cSniklas 						if (!dnode->s)
1075510d8b0cSniklas 							goto mem_fail;
1076510d8b0cSniklas 
107799cdfc90Sderaadt 						snprintf(dnode->s, len, "[%s]",
107899cdfc90Sderaadt 						    current_section);
107999cdfc90Sderaadt 						dnode->next = (struct dumper *)
108050eea14cSho 						    calloc(1,
108150eea14cSho 							sizeof(struct dumper));
1082510d8b0cSniklas 						dnode = dnode->next;
1083510d8b0cSniklas 						if (!dnode)
1084510d8b0cSniklas 							goto mem_fail;
1085510d8b0cSniklas 
1086510d8b0cSniklas 						dnode->s = "";
108799cdfc90Sderaadt 						dnode->next = (struct dumper *)
108850eea14cSho 						    calloc(1,
108950eea14cSho 							sizeof(struct dumper));
1090510d8b0cSniklas 						dnode = dnode->next;
1091510d8b0cSniklas 						if (!dnode)
1092510d8b0cSniklas 							goto mem_fail;
1093510d8b0cSniklas 					}
1094510d8b0cSniklas 					current_section = cb->section;
1095510d8b0cSniklas 				}
1096510d8b0cSniklas 				dnode->s = cb->tag;
1097510d8b0cSniklas 				dnode->v = cb->value;
109899cdfc90Sderaadt 				dnode->next = (struct dumper *)
109999cdfc90Sderaadt 				    calloc(1, sizeof(struct dumper));
1100510d8b0cSniklas 				dnode = dnode->next;
1101510d8b0cSniklas 				if (!dnode)
1102510d8b0cSniklas 					goto mem_fail;
1103510d8b0cSniklas 				last = cb;
1104510d8b0cSniklas 			}
1105510d8b0cSniklas 		}
1106510d8b0cSniklas 
1107fb9475d6Sderaadt 	if (last) {
110853922df5Sho 		len = strlen(last->section) + 3;
110953922df5Sho 		dnode->s = malloc(len);
1110510d8b0cSniklas 		if (!dnode->s)
1111510d8b0cSniklas 			goto mem_fail;
111253922df5Sho 		snprintf(dnode->s, len, "[%s]", last->section);
1113510d8b0cSniklas 	}
1114510d8b0cSniklas 	conf_report_dump(dumper);
1115510d8b0cSniklas 
1116510d8b0cSniklas 	return;
1117510d8b0cSniklas 
1118510d8b0cSniklas mem_fail:
11190eb823c5Sniklas 	log_error("conf_report: malloc/calloc failed");
1120fb9475d6Sderaadt 	while ((dnode = dumper) != 0) {
1121510d8b0cSniklas 		dumper = dumper->next;
1122510d8b0cSniklas 		if (dnode->s)
1123510d8b0cSniklas 			free(dnode->s);
1124510d8b0cSniklas 		free(dnode);
1125510d8b0cSniklas 	}
1126510d8b0cSniklas }
1127