xref: /openbsd/sbin/isakmpd/conf.c (revision d6fd0492)
1*d6fd0492Spvalchev /*	$OpenBSD: conf.c,v 1.39 2002/04/29 06:26:50 pvalchev 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  * 3. All advertising materials mentioning features or use of this software
172040585eSniklas  *    must display the following acknowledgement:
182040585eSniklas  *	This product includes software developed by Ericsson Radio Systems.
192040585eSniklas  * 4. The name of the author may not be used to endorse or promote products
202040585eSniklas  *    derived from this software without specific prior written permission.
212040585eSniklas  *
222040585eSniklas  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
232040585eSniklas  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
242040585eSniklas  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
252040585eSniklas  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
262040585eSniklas  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
272040585eSniklas  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
282040585eSniklas  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
292040585eSniklas  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
302040585eSniklas  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
312040585eSniklas  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
322040585eSniklas  */
332040585eSniklas 
342040585eSniklas /*
352040585eSniklas  * This code was written under funding by Ericsson Radio Systems.
362040585eSniklas  */
372040585eSniklas 
382040585eSniklas #include <sys/param.h>
392040585eSniklas #include <sys/mman.h>
402040585eSniklas #include <sys/queue.h>
4181c21331Sniklas #include <sys/socket.h>
422040585eSniklas #include <sys/stat.h>
4381c21331Sniklas #include <netinet/in.h>
4481c21331Sniklas #include <arpa/inet.h>
452040585eSniklas #include <ctype.h>
462040585eSniklas #include <fcntl.h>
472040585eSniklas #include <stdio.h>
482040585eSniklas #include <stdlib.h>
492040585eSniklas #include <string.h>
502040585eSniklas #include <unistd.h>
513eed80ffSniklas #include <errno.h>
522040585eSniklas 
53a2d30fd1Sniklas #include "sysdep.h"
54a2d30fd1Sniklas 
55a2d30fd1Sniklas #include "app.h"
562040585eSniklas #include "conf.h"
572040585eSniklas #include "log.h"
58bda02003Sniklas #include "util.h"
592040585eSniklas 
604c8c122bSho static char *conf_get_trans_str (int, char *, char *);
614c8c122bSho static void conf_load_defaults (int);
624c8c122bSho #if 0
634c8c122bSho static int conf_find_trans_xf (int, char *);
644c8c122bSho #endif
654c8c122bSho 
66f8f1e192Sniklas struct conf_trans {
67f8f1e192Sniklas   TAILQ_ENTRY (conf_trans) link;
68f8f1e192Sniklas   int trans;
69f8f1e192Sniklas   enum conf_op { CONF_SET, CONF_REMOVE, CONF_REMOVE_SECTION } op;
70f8f1e192Sniklas   char *section;
71f8f1e192Sniklas   char *tag;
72f8f1e192Sniklas   char *value;
73f8f1e192Sniklas   int override;
74510d8b0cSniklas   int is_default;
75f8f1e192Sniklas };
76f8f1e192Sniklas 
77f8f1e192Sniklas TAILQ_HEAD (conf_trans_head, conf_trans) conf_trans_queue;
78f8f1e192Sniklas 
792040585eSniklas /*
802040585eSniklas  * Radix-64 Encoding.
812040585eSniklas  */
82510d8b0cSniklas const u_int8_t bin2asc[]
83510d8b0cSniklas   = "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;
1172040585eSniklas 
118f8f1e192Sniklas static __inline__ u_int8_t
119f8f1e192Sniklas conf_hash (char *s)
120f8f1e192Sniklas {
121f8f1e192Sniklas   u_int8_t hash = 0;
122f8f1e192Sniklas 
123f8f1e192Sniklas   while (*s)
124f8f1e192Sniklas     {
125f8f1e192Sniklas       hash = ((hash << 1) | (hash >> 7)) ^ tolower (*s);
126f8f1e192Sniklas       s++;
127f8f1e192Sniklas     }
128f8f1e192Sniklas   return hash;
129f8f1e192Sniklas }
130f8f1e192Sniklas 
131f8f1e192Sniklas /*
132f8f1e192Sniklas  * Insert a tag-value combination from LINE (the equal sign is at POS)
133f8f1e192Sniklas  */
134f8f1e192Sniklas static int
135f8f1e192Sniklas conf_remove_now (char *section, char *tag)
136f8f1e192Sniklas {
137f8f1e192Sniklas   struct conf_binding *cb, *next;
138f8f1e192Sniklas 
139f8f1e192Sniklas   for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb; cb = next)
140f8f1e192Sniklas     {
141f8f1e192Sniklas       next = LIST_NEXT (cb, link);
142f8f1e192Sniklas       if (strcasecmp (cb->section, section) == 0
143f8f1e192Sniklas 	  && strcasecmp (cb->tag, tag) == 0)
144f8f1e192Sniklas 	{
145f8f1e192Sniklas 	  LIST_REMOVE (cb, link);
146395a452cSho 	  LOG_DBG ((LOG_MISC, 95, "[%s]:%s->%s removed", section, tag,
14751ca15aeSniklas 		    cb->value));
148f8f1e192Sniklas 	  free (cb->section);
149f8f1e192Sniklas 	  free (cb->tag);
150f8f1e192Sniklas 	  free (cb->value);
151f8f1e192Sniklas 	  free (cb);
152f8f1e192Sniklas 	  return 0;
153f8f1e192Sniklas 	}
154f8f1e192Sniklas     }
155f8f1e192Sniklas   return 1;
156f8f1e192Sniklas }
157f8f1e192Sniklas 
158f8f1e192Sniklas static int
159f8f1e192Sniklas conf_remove_section_now (char *section)
160f8f1e192Sniklas {
161f8f1e192Sniklas   struct conf_binding *cb, *next;
162f8f1e192Sniklas   int unseen = 1;
163f8f1e192Sniklas 
164f8f1e192Sniklas   for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb; cb = next)
165f8f1e192Sniklas     {
166f8f1e192Sniklas       next = LIST_NEXT (cb, link);
167f8f1e192Sniklas       if (strcasecmp (cb->section, section) == 0)
168f8f1e192Sniklas 	{
169f8f1e192Sniklas 	  unseen = 0;
170f8f1e192Sniklas 	  LIST_REMOVE (cb, link);
171395a452cSho 	  LOG_DBG ((LOG_MISC, 95, "[%s]:%s->%s removed", section, cb->tag,
17251ca15aeSniklas 		    cb->value));
173f8f1e192Sniklas 	  free (cb->section);
174f8f1e192Sniklas 	  free (cb->tag);
175f8f1e192Sniklas 	  free (cb->value);
176f8f1e192Sniklas 	  free (cb);
177f8f1e192Sniklas 	}
178f8f1e192Sniklas     }
179f8f1e192Sniklas   return unseen;
180f8f1e192Sniklas }
181f8f1e192Sniklas 
1822040585eSniklas /*
1832040585eSniklas  * Insert a tag-value combination from LINE (the equal sign is at POS)
1842040585eSniklas  * into SECTION of our configuration database.
1852040585eSniklas  */
186f8f1e192Sniklas static int
187510d8b0cSniklas conf_set_now (char *section, char *tag, char *value, int override,
188510d8b0cSniklas 	      int is_default)
1892040585eSniklas {
190f8f1e192Sniklas   struct conf_binding *node = 0;
1912040585eSniklas 
192f8f1e192Sniklas   if (override)
193f8f1e192Sniklas     conf_remove_now (section, tag);
194f8f1e192Sniklas   else if (conf_get_str (section, tag))
1952040585eSniklas     {
19613e19299Sniklas       if (!is_default)
1972040585eSniklas 	log_print ("conf_set: duplicate tag [%s]:%s, ignoring...\n", section,
198f8f1e192Sniklas 		   tag);
199f8f1e192Sniklas       return 1;
2002040585eSniklas     }
201f8f1e192Sniklas 
202f8f1e192Sniklas   node = calloc (1, sizeof *node);
203f8f1e192Sniklas   if (!node)
204f8f1e192Sniklas     {
205f8f1e192Sniklas       log_error ("conf_set: calloc (1, %d) failed", sizeof *node);
206f8f1e192Sniklas       return 1;
207f8f1e192Sniklas     }
208074d67afSniklas   node->section = strdup (section);
209074d67afSniklas   node->tag = strdup (tag);
210074d67afSniklas   node->value = strdup (value);
211510d8b0cSniklas   node->is_default = is_default;
212f8f1e192Sniklas 
213f8f1e192Sniklas   LIST_INSERT_HEAD (&conf_bindings[conf_hash (section)], node, link);
214395a452cSho   LOG_DBG ((LOG_MISC, 95, "conf_set: [%s]:%s->%s", node->section, node->tag,
21551ca15aeSniklas 	    node->value));
216f8f1e192Sniklas   return 0;
2172040585eSniklas }
2182040585eSniklas 
2192040585eSniklas /*
2202040585eSniklas  * Parse the line LINE of SZ bytes.  Skip Comments, recognize section
2212040585eSniklas  * headers and feed tag-value pairs into our configuration database.
2222040585eSniklas  */
2232040585eSniklas static void
224f8f1e192Sniklas conf_parse_line (int trans, char *line, size_t sz)
2252040585eSniklas {
22690d8b2b0Sho   char *val;
2272040585eSniklas   int i;
2282040585eSniklas   static char *section = 0;
2292040585eSniklas   static int ln = 0;
2302040585eSniklas 
2312040585eSniklas   ln++;
2322040585eSniklas 
2332040585eSniklas   /* Lines starting with '#' or ';' are comments.  */
2342040585eSniklas   if (*line == '#' || *line == ';')
2352040585eSniklas     return;
2362040585eSniklas 
2372040585eSniklas   /* '[section]' parsing...  */
2382040585eSniklas   if (*line == '[')
2392040585eSniklas     {
2402040585eSniklas       for (i = 1; i < sz; i++)
2412040585eSniklas 	if (line[i] == ']')
2422040585eSniklas 	  break;
24390d8b2b0Sho       if (section)
24490d8b2b0Sho 	free (section);
2452040585eSniklas       if (i == sz)
2462040585eSniklas 	{
2472040585eSniklas 	  log_print ("conf_parse_line: %d:"
2482040585eSniklas 		     "non-matched ']', ignoring until next section", ln);
2492040585eSniklas 	  section = 0;
2502040585eSniklas 	  return;
2512040585eSniklas 	}
2522040585eSniklas       section = malloc (i);
25390d8b2b0Sho       if (!section)
25490d8b2b0Sho 	{
25590d8b2b0Sho 	  log_print ("conf_parse_line: %d: malloc (%d) failed", ln, i);
25690d8b2b0Sho 	  return;
25790d8b2b0Sho 	}
258b8380d91Sho       strlcpy (section, line + 1, i);
2592040585eSniklas       return;
2602040585eSniklas     }
2612040585eSniklas 
2622040585eSniklas   /* Deal with assignments.  */
2632040585eSniklas   for (i = 0; i < sz; i++)
26490d8b2b0Sho     if (line[i] == '=')
2652040585eSniklas       {
2662040585eSniklas 	/* If no section, we are ignoring the lines.  */
2672040585eSniklas 	if (!section)
2682040585eSniklas 	  {
2692040585eSniklas 	    log_print ("conf_parse_line: %d: ignoring line due to no section",
2702040585eSniklas 		       ln);
2712040585eSniklas 	    return;
2722040585eSniklas 	  }
273f8f1e192Sniklas 	line[strcspn (line, " \t=")] = '\0';
27490d8b2b0Sho 	val = line + i + 1 + strspn (line + i + 1, " \t");
27590d8b2b0Sho 	/* Skip trailing whitespace, if any */
27690d8b2b0Sho 	i = strcspn (val, " \t\r");
27790d8b2b0Sho 	if (i)
27890d8b2b0Sho 	  val[i] = '\0';
279f8f1e192Sniklas 	/* XXX Perhaps should we not ignore errors?  */
28090d8b2b0Sho 	conf_set (trans, section, line, val, 0, 0);
2812040585eSniklas 	return;
2822040585eSniklas       }
2832040585eSniklas 
284*d6fd0492Spvalchev   /* Other non-empty lines are weird.  */
2852040585eSniklas   i = strspn (line, " \t");
2862040585eSniklas   if (line[i])
2872040585eSniklas     log_print ("conf_parse_line: %d: syntax error", ln);
2882040585eSniklas 
2892040585eSniklas   return;
2902040585eSniklas }
2912040585eSniklas 
2922040585eSniklas /* Parse the mapped configuration file.  */
2932040585eSniklas static void
294f8f1e192Sniklas conf_parse (int trans, char *buf, size_t sz)
2952040585eSniklas {
296f8f1e192Sniklas   char *cp = buf;
297f8f1e192Sniklas   char *bufend = buf + sz;
2982040585eSniklas   char *line;
2992040585eSniklas 
3002040585eSniklas   line = cp;
301f8f1e192Sniklas   while (cp < bufend)
3022040585eSniklas     {
3032040585eSniklas       if (*cp == '\n')
3042040585eSniklas 	{
3052040585eSniklas 	  /* Check for escaped newlines.  */
306f8f1e192Sniklas 	  if (cp > buf && *(cp - 1) == '\\')
3072040585eSniklas 	    *(cp - 1) = *cp = ' ';
3082040585eSniklas 	  else
3092040585eSniklas 	    {
3102040585eSniklas 	      *cp = '\0';
311f8f1e192Sniklas 	      conf_parse_line (trans, line, cp - line);
3122040585eSniklas 	      line = cp + 1;
3132040585eSniklas 	    }
3142040585eSniklas 	}
3152040585eSniklas       cp++;
3162040585eSniklas     }
3172040585eSniklas   if (cp != line)
3182040585eSniklas     log_print ("conf_parse: last line non-terminated, ignored.");
3192040585eSniklas }
3202040585eSniklas 
321510d8b0cSniklas /*
322510d8b0cSniklas  * Auto-generate default configuration values for the transforms and
323510d8b0cSniklas  * suites the user wants.
324510d8b0cSniklas  *
325510d8b0cSniklas  * Resulting section names can be:
326510d8b0cSniklas  *  For main mode:
327e33f6eeeSho  *     {DES,BLF,3DES,CAST}-{MD5,SHA}[-GRP{1,2,5}][-{DSS,RSA_SIG}]
328510d8b0cSniklas  *  For quick mode:
329e33f6eeeSho  *     QM-{proto}[-TRP]-{cipher}[-{hash}][-PFS[-{group}]]-SUITE
330e33f6eeeSho  *     where
331e33f6eeeSho  *       {proto}  = ESP, AH
332e33f6eeeSho  *       {cipher} = DES, 3DES, CAST, BLF, AES
333e33f6eeeSho  *       {hash}   = MD5, SHA, RIPEMD
334e33f6eeeSho  *       {group}  = GRP1, GRP2, GRP5
335e33f6eeeSho  *
336e33f6eeeSho  * DH group defaults to MODP_1024.
337510d8b0cSniklas  *
338510d8b0cSniklas  * XXX We may want to support USE_BLOWFISH, USE_TRIPLEDES, etc...
339510d8b0cSniklas  * XXX No EC2N DH support here yet.
340510d8b0cSniklas  */
341510d8b0cSniklas 
3420eb823c5Sniklas /* Find the value for a section+tag in the transaction list.  */
3434c8c122bSho static char *
344eee423ceSho conf_get_trans_str (int trans, char *section, char *tag)
345eee423ceSho {
346eee423ceSho   struct conf_trans *node, *nf = 0;
347eee423ceSho 
348eee423ceSho   for (node = TAILQ_FIRST (&conf_trans_queue); node;
349eee423ceSho        node = TAILQ_NEXT (node, link))
3500eb823c5Sniklas     if (node->trans == trans && strcmp (section, node->section) == 0
3510eb823c5Sniklas 	&& strcmp (tag, node->tag) == 0)
352eee423ceSho       {
353eee423ceSho 	if (!nf)
354eee423ceSho 	  nf = node;
355eee423ceSho 	else if (node->override)
356eee423ceSho 	  nf = node;
357eee423ceSho       }
3580eb823c5Sniklas   return nf ? nf->value : 0;
359eee423ceSho }
360eee423ceSho 
3614c8c122bSho #if 0
3624c8c122bSho /* XXX Currently unused.  */
3634c8c122bSho static int
364510d8b0cSniklas conf_find_trans_xf (int phase, char *xf)
365510d8b0cSniklas {
366510d8b0cSniklas   struct conf_trans *node;
367510d8b0cSniklas   char *p;
368510d8b0cSniklas 
369510d8b0cSniklas   /* Find the relevant transforms and suites, if any.  */
370510d8b0cSniklas   for (node = TAILQ_FIRST (&conf_trans_queue); node;
371510d8b0cSniklas        node = TAILQ_NEXT (node, link))
372eee423ceSho     if ((phase == 1 && strcmp ("Transforms", node->tag) == 0) ||
373eee423ceSho 	(phase == 2 && strcmp ("Suites", node->tag) == 0))
374510d8b0cSniklas       {
375510d8b0cSniklas 	p = node->value;
376510d8b0cSniklas 	while ((p = strstr (p, xf)) != NULL)
377510d8b0cSniklas 	  if (*(p + strlen (p)) && *(p + strlen (p)) != ',')
378510d8b0cSniklas 	    p += strlen (p);
379510d8b0cSniklas 	  else
380510d8b0cSniklas 	    return 1;
381510d8b0cSniklas       }
382510d8b0cSniklas   return 0;
383510d8b0cSniklas }
3844c8c122bSho #endif
385510d8b0cSniklas 
3864c8c122bSho static void
387510d8b0cSniklas conf_load_defaults (int tr)
388510d8b0cSniklas {
389b8380d91Sho #define CONF_MAX 256
390e33f6eeeSho   int enc, auth, hash, group, group_max, proto, mode, pfs;
391b8380d91Sho   char sect[CONF_MAX], *dflt;
392510d8b0cSniklas 
3930eb823c5Sniklas   char *mm_auth[]   = { "PRE_SHARED", "DSS", "RSA_SIG", 0 };
3940eb823c5Sniklas   char *mm_hash[]   = { "MD5", "SHA", 0 };
395510d8b0cSniklas   char *mm_enc[]    = { "DES_CBC", "BLOWFISH_CBC", "3DES_CBC",
3960eb823c5Sniklas 			"CAST_CBC", 0 };
3970eb823c5Sniklas   char *dh_group[]  = { "MODP_768", "MODP_1024", "MODP_1536", 0 };
3980eb823c5Sniklas   char *qm_enc[]    = { "DES", "3DES", "CAST", "BLOWFISH", "AES", 0 };
3990eb823c5Sniklas   char *qm_hash[]   = { "HMAC_MD5", "HMAC_SHA", "HMAC_RIPEMD", "NONE", 0 };
400510d8b0cSniklas 
401510d8b0cSniklas   /* Abbreviations to make section names a bit shorter.  */
4020eb823c5Sniklas   char *mm_auth_p[] = { "", "-DSS", "-RSA_SIG", 0 };
4030eb823c5Sniklas   char *mm_enc_p[]  = { "DES", "BLF", "3DES", "CAST", 0 };
404e33f6eeeSho   char *dh_group_p[]= { "-GRP1", "-GRP2", "-GRP5", "", 0 };
4050eb823c5Sniklas   char *qm_enc_p[]  = { "-DES", "-3DES", "-CAST", "-BLF", "-AES", 0 };
4060eb823c5Sniklas   char *qm_hash_p[] = { "-MD5", "-SHA", "-RIPEMD", "", 0 };
407510d8b0cSniklas 
408510d8b0cSniklas   /* Helper #defines, incl abbreviations.  */
409510d8b0cSniklas #define PROTO(x)  ((x) ? "AH" : "ESP")
410510d8b0cSniklas #define PFS(x)    ((x) ? "-PFS" : "")
411510d8b0cSniklas #define MODE(x)   ((x) ? "TRANSPORT" : "TUNNEL")
412510d8b0cSniklas #define MODE_p(x) ((x) ? "-TRP" : "")
413e33f6eeeSho   group_max = sizeof dh_group / sizeof *dh_group - 1;
414510d8b0cSniklas 
415510d8b0cSniklas   /* General and X509 defaults */
416510d8b0cSniklas   conf_set (tr, "General", "Retransmits", CONF_DFLT_RETRANSMITS, 0, 1);
417510d8b0cSniklas   conf_set (tr, "General", "Exchange-max-time", CONF_DFLT_EXCH_MAX_TIME, 0, 1);
418510d8b0cSniklas   conf_set (tr, "General", "Policy-file", CONF_DFLT_POLICY_FILE, 0, 1);
419510d8b0cSniklas 
420510d8b0cSniklas #ifdef USE_X509
421510d8b0cSniklas   conf_set (tr, "X509-certificates", "CA-directory", CONF_DFLT_X509_CA_DIR, 0,
422510d8b0cSniklas 	    1);
423510d8b0cSniklas   conf_set (tr, "X509-certificates", "Cert-directory", CONF_DFLT_X509_CERT_DIR,
424510d8b0cSniklas 	    0, 1);
425510d8b0cSniklas   conf_set (tr, "X509-certificates", "Private-key", CONF_DFLT_X509_PRIVATE_KEY,
426510d8b0cSniklas 	    0, 1);
427510d8b0cSniklas #endif
428510d8b0cSniklas 
42913e19299Sniklas #ifdef USE_KEYNOTE
43013e19299Sniklas   conf_set (tr, "KeyNote", "Credential-directory", CONF_DFLT_KEYNOTE_CRED_DIR,
43113e19299Sniklas 	    0, 1);
43213e19299Sniklas #endif
43313e19299Sniklas 
43428d27e6cSangelos   /* Lifetimes. XXX p1/p2 vs main/quick mode may be unclear.  */
435eee423ceSho   dflt = conf_get_trans_str (tr, "General", "Default-phase-1-lifetime");
43628d27e6cSangelos   conf_set (tr, CONF_DFLT_TAG_LIFE_MAIN_MODE, "LIFE_TYPE",
43728d27e6cSangelos 	    CONF_DFLT_TYPE_LIFE_MAIN_MODE, 0, 1);
43828d27e6cSangelos   conf_set (tr, CONF_DFLT_TAG_LIFE_MAIN_MODE, "LIFE_DURATION",
43928d27e6cSangelos 	    (dflt ? dflt : CONF_DFLT_VAL_LIFE_MAIN_MODE), 0, 1);
44028d27e6cSangelos 
441eee423ceSho   dflt = conf_get_trans_str (tr, "General", "Default-phase-2-lifetime");
44228d27e6cSangelos   conf_set (tr, CONF_DFLT_TAG_LIFE_QUICK_MODE, "LIFE_TYPE",
44328d27e6cSangelos 	    CONF_DFLT_TYPE_LIFE_QUICK_MODE, 0, 1);
44428d27e6cSangelos   conf_set (tr, CONF_DFLT_TAG_LIFE_QUICK_MODE, "LIFE_DURATION",
44528d27e6cSangelos 	    (dflt ? dflt : CONF_DFLT_VAL_LIFE_QUICK_MODE), 0, 1);
44628d27e6cSangelos 
447510d8b0cSniklas   /* Main modes */
448510d8b0cSniklas   for (enc = 0; mm_enc[enc]; enc ++)
449510d8b0cSniklas     for (hash = 0; mm_hash[hash]; hash ++)
450510d8b0cSniklas       for (auth = 0; mm_auth[auth]; auth ++)
451e33f6eeeSho 	for (group = 0; dh_group_p[group]; group ++) /* special */
452510d8b0cSniklas 	  {
453e33f6eeeSho 	    snprintf (sect, CONF_MAX, "%s-%s%s%s", mm_enc_p[enc],
454e33f6eeeSho 		      mm_hash[hash], dh_group_p[group], mm_auth_p[auth]);
455510d8b0cSniklas 
4563eed80ffSniklas #if 0
457510d8b0cSniklas 	    if (!conf_find_trans_xf (1, sect))
458510d8b0cSniklas 	      continue;
4593eed80ffSniklas #endif
460510d8b0cSniklas 
461e33f6eeeSho 	    LOG_DBG ((LOG_MISC, 90, "conf_load_defaults : main mode %s",
462e33f6eeeSho 		      sect));
463510d8b0cSniklas 
464510d8b0cSniklas 	    conf_set (tr, sect, "ENCRYPTION_ALGORITHM", mm_enc[enc], 0, 1);
465eee423ceSho 	    if (strcmp (mm_enc[enc], "BLOWFISH_CBC") == 0)
466e33f6eeeSho 	      conf_set (tr, sect, "KEY_LENGTH", CONF_DFLT_VAL_BLF_KEYLEN, 0,
467e33f6eeeSho 			1);
468510d8b0cSniklas 
469510d8b0cSniklas 	    conf_set (tr, sect, "HASH_ALGORITHM", mm_hash[hash], 0, 1);
470510d8b0cSniklas 	    conf_set (tr, sect, "AUTHENTICATION_METHOD", mm_auth[auth], 0, 1);
471510d8b0cSniklas 
472e33f6eeeSho 	    /* XXX Always DH group 2 (MODP_1024) */
473e33f6eeeSho 	    conf_set (tr, sect, "GROUP_DESCRIPTION",
474e33f6eeeSho 		      dh_group[group < group_max ? group : 1], 0, 1);
475510d8b0cSniklas 
476510d8b0cSniklas 	    conf_set (tr, sect, "Life", CONF_DFLT_TAG_LIFE_MAIN_MODE, 0, 1);
477510d8b0cSniklas 	}
478510d8b0cSniklas 
479cc475db6Sniklas   /* Setup a default Phase 1 entry */
480cc475db6Sniklas   conf_set (tr, "Phase 1", "Default", "Default-phase-1", 0, 1);
481cc475db6Sniklas 
482cc475db6Sniklas   conf_set (tr, "Default-phase-1", "Phase", "1", 0, 1);
483cc475db6Sniklas   conf_set (tr, "Default-phase-1", "Configuration",
484cc475db6Sniklas             "Default-phase-1-configuration", 0, 1);
485eee423ceSho   dflt = conf_get_trans_str (tr, "General", "Default-phase-1-ID");
486cc475db6Sniklas   if (dflt)
487cc475db6Sniklas     conf_set (tr, "Default-phase-1", "ID", dflt, 0, 1);
488cc475db6Sniklas 
489cc475db6Sniklas   conf_set (tr, "Default-phase-1-configuration",
490cc475db6Sniklas             "EXCHANGE_TYPE", "ID_PROT", 0, 1);
491cc475db6Sniklas   conf_set (tr, "Default-phase-1-configuration", "Transforms",
492cc475db6Sniklas             "3DES-SHA-RSA_SIG", 0, 1);
493cc475db6Sniklas 
494510d8b0cSniklas    /* Quick modes */
495510d8b0cSniklas   for (enc = 0; qm_enc[enc]; enc ++)
496510d8b0cSniklas     for (proto = 0; proto < 2; proto ++)
497510d8b0cSniklas       for (mode = 0; mode < 2; mode ++)
498510d8b0cSniklas 	for (pfs = 0; pfs < 2; pfs ++)
499510d8b0cSniklas 	  for (hash = 0; qm_hash[hash]; hash ++)
500e33f6eeeSho 	    for (group = 0; dh_group_p[group]; group ++)
501eee423ceSho 	      if ((proto == 1 && strcmp (qm_hash[hash], "NONE") == 0)) /* AH */
502510d8b0cSniklas 		continue;
503510d8b0cSniklas 	      else
504510d8b0cSniklas 		{
505b8380d91Sho 		  char tmp[CONF_MAX];
506510d8b0cSniklas 
507e33f6eeeSho 		  snprintf (tmp, CONF_MAX, "QM-%s%s%s%s%s%s", PROTO (proto),
508b8380d91Sho 			    MODE_p (mode), qm_enc_p[enc], qm_hash_p[hash],
509e33f6eeeSho 			    PFS (pfs), dh_group_p[group]);
510510d8b0cSniklas 
511b8380d91Sho 		  strlcpy (sect, tmp, CONF_MAX);
512b8380d91Sho 		  strlcat (sect, "-SUITE", CONF_MAX);
513510d8b0cSniklas 
5143eed80ffSniklas #if 0
515510d8b0cSniklas 		  if (!conf_find_trans_xf (2, sect))
516510d8b0cSniklas 		    continue;
5173eed80ffSniklas #endif
518510d8b0cSniklas 
519395a452cSho 		  LOG_DBG ((LOG_MISC, 90, "conf_load_defaults : quick mode %s",
520510d8b0cSniklas 			    sect));
521510d8b0cSniklas 
522510d8b0cSniklas 		  conf_set (tr, sect, "Protocols", tmp, 0, 1);
523510d8b0cSniklas 
524b8380d91Sho 		  snprintf (sect, CONF_MAX, "IPSEC_%s", PROTO (proto));
525510d8b0cSniklas 		  conf_set (tr, tmp, "PROTOCOL_ID", sect, 0, 1);
526510d8b0cSniklas 
527b8380d91Sho 		  strlcpy (sect, tmp, CONF_MAX);
528b8380d91Sho 		  strlcat (sect, "-XF", CONF_MAX);
529510d8b0cSniklas 		  conf_set (tr, tmp, "Transforms", sect, 0, 1);
530510d8b0cSniklas 
531e33f6eeeSho 		  /* XXX For now, defaults contain one xf per protocol.  */
532510d8b0cSniklas 
533510d8b0cSniklas 		  conf_set (tr, sect, "TRANSFORM_ID", qm_enc[enc], 0, 1);
534510d8b0cSniklas 
535eee423ceSho 		  if (strcmp (qm_enc[enc], "BLOWFISH") == 0)
536510d8b0cSniklas 		    conf_set (tr, sect, "KEY_LENGTH", CONF_DFLT_VAL_BLF_KEYLEN,
537510d8b0cSniklas 			      0, 1);
538510d8b0cSniklas 
539510d8b0cSniklas 		  conf_set (tr, sect, "ENCAPSULATION_MODE", MODE (mode), 0, 1);
540510d8b0cSniklas 
541510d8b0cSniklas 		  if (strcmp (qm_hash[hash], "NONE"))
542510d8b0cSniklas 		    {
543510d8b0cSniklas 		      conf_set (tr, sect, "AUTHENTICATION_ALGORITHM",
544510d8b0cSniklas 				qm_hash[hash], 0, 1);
545510d8b0cSniklas 
546510d8b0cSniklas 		      /* XXX Another shortcut -- to keep length down.  */
547510d8b0cSniklas 		      if (pfs)
548510d8b0cSniklas 			conf_set (tr, sect, "GROUP_DESCRIPTION",
549e33f6eeeSho 				  dh_group[group < group_max ? group : 1], 0,
550e33f6eeeSho 				  1);
551510d8b0cSniklas 		    }
552510d8b0cSniklas 
553510d8b0cSniklas 		  /* XXX Lifetimes depending on enc/auth strength?  */
554510d8b0cSniklas 		  conf_set (tr, sect, "Life", CONF_DFLT_TAG_LIFE_QUICK_MODE, 0,
555510d8b0cSniklas 			    1);
556510d8b0cSniklas 	      }
557510d8b0cSniklas   return;
558510d8b0cSniklas }
559510d8b0cSniklas 
5602040585eSniklas void
5612040585eSniklas conf_init (void)
5622040585eSniklas {
563f8f1e192Sniklas   int i;
5642040585eSniklas 
565f8f1e192Sniklas   for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
566f8f1e192Sniklas     LIST_INIT (&conf_bindings[i]);
567f8f1e192Sniklas   TAILQ_INIT (&conf_trans_queue);
568f8f1e192Sniklas   conf_reinit ();
5692040585eSniklas }
5702040585eSniklas 
571f8f1e192Sniklas /* Open the config file and map it into our address space, then parse it.  */
572f8f1e192Sniklas void
573f8f1e192Sniklas conf_reinit (void)
574f8f1e192Sniklas {
575f8f1e192Sniklas   struct conf_binding *cb = 0;
576f8f1e192Sniklas   int fd, i, trans;
577f8f1e192Sniklas   off_t sz;
578f8f1e192Sniklas   char *new_conf_addr = 0;
5793eed80ffSniklas   struct stat sb;
580f8f1e192Sniklas 
58153d0d28bSniklas   if ((stat (conf_path, &sb) == 0) || (errno != ENOENT))
5823eed80ffSniklas     {
583bda02003Sniklas       if (check_file_secrecy (conf_path, &sz))
584bda02003Sniklas 	return;
585bda02003Sniklas 
5862040585eSniklas       fd = open (conf_path, O_RDONLY);
5872040585eSniklas       if (fd == -1)
588f8f1e192Sniklas         {
589757a19b2Sniklas 	  log_error ("conf_reinit: open (\"%s\", O_RDONLY) failed", conf_path);
590f8f1e192Sniklas 	  return;
591f8f1e192Sniklas 	}
592bda02003Sniklas 
593f8f1e192Sniklas       new_conf_addr = malloc (sz);
594f8f1e192Sniklas       if (!new_conf_addr)
595f8f1e192Sniklas         {
596757a19b2Sniklas 	  log_error ("conf_reinit: malloc (%d) failed", sz);
597f8f1e192Sniklas 	  goto fail;
598f8f1e192Sniklas 	}
5993eed80ffSniklas 
6002040585eSniklas       /* XXX I assume short reads won't happen here.  */
601f8f1e192Sniklas       if (read (fd, new_conf_addr, sz) != sz)
602f8f1e192Sniklas         {
6033eed80ffSniklas 	    log_error ("conf_reinit: read (%d, %p, %d) failed",
6043eed80ffSniklas 		       fd, new_conf_addr, sz);
605f8f1e192Sniklas 	    goto fail;
606f8f1e192Sniklas 	}
6072040585eSniklas       close (fd);
6082040585eSniklas 
609f8f1e192Sniklas       trans = conf_begin ();
610f8f1e192Sniklas 
611f8f1e192Sniklas       /* XXX Should we not care about errors and rollback?  */
612f8f1e192Sniklas       conf_parse (trans, new_conf_addr, sz);
6133eed80ffSniklas     }
6143eed80ffSniklas   else
6153eed80ffSniklas     trans = conf_begin ();
616f8f1e192Sniklas 
617510d8b0cSniklas   /* Load default configuration values.  */
618510d8b0cSniklas   conf_load_defaults (trans);
619510d8b0cSniklas 
620f8f1e192Sniklas   /* Free potential existing configuration.  */
621f8f1e192Sniklas   if (conf_addr)
622f8f1e192Sniklas     {
623f8f1e192Sniklas       for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
624f8f1e192Sniklas 	for (cb = LIST_FIRST (&conf_bindings[i]); cb;
625f8f1e192Sniklas 	     cb = LIST_FIRST (&conf_bindings[i]))
626f8f1e192Sniklas 	  conf_remove_now (cb->section, cb->tag);
627f8f1e192Sniklas       free (conf_addr);
628f8f1e192Sniklas     }
629f8f1e192Sniklas 
630f8f1e192Sniklas   conf_end (trans, 1);
631f8f1e192Sniklas   conf_addr = new_conf_addr;
632f8f1e192Sniklas   return;
633f8f1e192Sniklas 
634f8f1e192Sniklas  fail:
635f8f1e192Sniklas   if (new_conf_addr)
636f8f1e192Sniklas     free (new_conf_addr);
637f8f1e192Sniklas   close (fd);
6382040585eSniklas }
6392040585eSniklas 
640a2d30fd1Sniklas /*
641a2d30fd1Sniklas  * Return the numeric value denoted by TAG in section SECTION or DEF
642a2d30fd1Sniklas  * if that tag does not exist.
643a2d30fd1Sniklas  */
6442040585eSniklas int
645a2d30fd1Sniklas conf_get_num (char *section, char *tag, int def)
6462040585eSniklas {
6472040585eSniklas   char *value = conf_get_str (section, tag);
6482040585eSniklas 
6492040585eSniklas   if (value)
6502040585eSniklas       return atoi (value);
651a2d30fd1Sniklas   return def;
6522040585eSniklas }
6532040585eSniklas 
65481c21331Sniklas /*
65581c21331Sniklas  * Return the socket endpoint address denoted by TAG in SECTION as a
65681c21331Sniklas  * struct sockaddr.  It is the callers responsibility to deallocate
65781c21331Sniklas  * this structure when it is finished with it.
65881c21331Sniklas  */
65981c21331Sniklas struct sockaddr *
66081c21331Sniklas conf_get_address (char *section, char *tag)
66181c21331Sniklas {
66281c21331Sniklas   char *value = conf_get_str (section, tag);
66381c21331Sniklas   struct sockaddr *sa;
66481c21331Sniklas 
66581c21331Sniklas   if (!value)
66681c21331Sniklas     return 0;
66781c21331Sniklas   if (text2sockaddr (value, 0, &sa) == -1)
66881c21331Sniklas     return 0;
66981c21331Sniklas   return sa;
67081c21331Sniklas }
67181c21331Sniklas 
67282d8fe06Sniklas /* Validate X according to the range denoted by TAG in section SECTION.  */
67382d8fe06Sniklas int
67482d8fe06Sniklas conf_match_num (char *section, char *tag, int x)
67582d8fe06Sniklas {
67682d8fe06Sniklas   char *value = conf_get_str (section, tag);
67782d8fe06Sniklas   int val, min, max, n;
67882d8fe06Sniklas 
67982d8fe06Sniklas   if (!value)
68082d8fe06Sniklas     return 0;
68182d8fe06Sniklas   n = sscanf (value, "%d,%d:%d", &val, &min, &max);
68282d8fe06Sniklas   switch (n)
68382d8fe06Sniklas     {
68482d8fe06Sniklas     case 1:
68551ca15aeSniklas       LOG_DBG ((LOG_MISC, 90, "conf_match_num: %s:%s %d==%d?", section, tag,
68651ca15aeSniklas 		val, x));
68782d8fe06Sniklas       return x == val;
68882d8fe06Sniklas     case 3:
68951ca15aeSniklas       LOG_DBG ((LOG_MISC, 90, "conf_match_num: %s:%s %d<=%d<=%d?", section,
69051ca15aeSniklas 		tag, min, x, max));
69182d8fe06Sniklas       return min <= x && max >= x;
69282d8fe06Sniklas     default:
69382d8fe06Sniklas       log_error ("conf_match_num: section %s tag %s: invalid number spec %s",
69482d8fe06Sniklas 		 section, tag, value);
69582d8fe06Sniklas     }
69682d8fe06Sniklas   return 0;
69782d8fe06Sniklas }
69882d8fe06Sniklas 
6992040585eSniklas /* Return the string value denoted by TAG in section SECTION.  */
7002040585eSniklas char *
7012040585eSniklas conf_get_str (char *section, char *tag)
7022040585eSniklas {
7032040585eSniklas   struct conf_binding *cb;
7042040585eSniklas 
705f8f1e192Sniklas   for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb;
706f8f1e192Sniklas        cb = LIST_NEXT (cb, link))
7072040585eSniklas     if (strcasecmp (section, cb->section) == 0
7082040585eSniklas 	&& strcasecmp (tag, cb->tag) == 0)
7092040585eSniklas       {
710395a452cSho 	LOG_DBG ((LOG_MISC, 95, "conf_get_str: [%s]:%s->%s", section,
71151ca15aeSniklas 		  tag, cb->value));
7122040585eSniklas 	return cb->value;
7132040585eSniklas       }
714395a452cSho   LOG_DBG ((LOG_MISC, 95,
715f8f1e192Sniklas 	    "conf_get_str: configuration value not found [%s]:%s", section,
71651ca15aeSniklas 	    tag));
7172040585eSniklas   return 0;
7182040585eSniklas }
7192040585eSniklas 
720a9753648Sniklas /*
721a9753648Sniklas  * Build a list of string values out of the comma separated value denoted by
722a9753648Sniklas  * TAG in SECTION.
723a9753648Sniklas  */
7242040585eSniklas struct conf_list *
7252040585eSniklas conf_get_list (char *section, char *tag)
7262040585eSniklas {
7272040585eSniklas   char *liststr = 0, *p, *field;
7282040585eSniklas   struct conf_list *list = 0;
7292040585eSniklas   struct conf_list_node *node;
7302040585eSniklas 
7312040585eSniklas   list = malloc (sizeof *list);
7322040585eSniklas   if (!list)
7332040585eSniklas     goto cleanup;
7342040585eSniklas   TAILQ_INIT (&list->fields);
7352040585eSniklas   list->cnt = 0;
7362040585eSniklas   liststr = conf_get_str (section, tag);
7372040585eSniklas   if (!liststr)
7382040585eSniklas     goto cleanup;
7392040585eSniklas   liststr = strdup (liststr);
7402040585eSniklas   if (!liststr)
7412040585eSniklas     goto cleanup;
7422040585eSniklas   p = liststr;
7432040585eSniklas   while ((field = strsep (&p, ", \t")) != NULL)
7442040585eSniklas     {
7452040585eSniklas       if (*field == '\0')
7462040585eSniklas 	{
7472040585eSniklas 	  log_print ("conf_get_list: empty field, ignoring...");
7482040585eSniklas 	  continue;
7492040585eSniklas 	}
7502040585eSniklas       list->cnt++;
751a9753648Sniklas       node = calloc (1, sizeof *node);
7522040585eSniklas       if (!node)
7532040585eSniklas 	goto cleanup;
754a9753648Sniklas       node->field = strdup (field);
755a9753648Sniklas       if (!node->field)
756a9753648Sniklas 	goto cleanup;
7572040585eSniklas       TAILQ_INSERT_TAIL (&list->fields, node, link);
7582040585eSniklas     }
759a9753648Sniklas   free (liststr);
7602040585eSniklas   return list;
7612040585eSniklas 
7622040585eSniklas  cleanup:
7632040585eSniklas   if (list)
7642040585eSniklas     conf_free_list (list);
7652040585eSniklas   if (liststr)
7662040585eSniklas     free (liststr);
7672040585eSniklas   return 0;
7682040585eSniklas }
7692040585eSniklas 
77082d8fe06Sniklas struct conf_list *
77182d8fe06Sniklas conf_get_tag_list (char *section)
77282d8fe06Sniklas {
77382d8fe06Sniklas   struct conf_list *list = 0;
77482d8fe06Sniklas   struct conf_list_node *node;
77582d8fe06Sniklas   struct conf_binding *cb;
77682d8fe06Sniklas 
77782d8fe06Sniklas   list = malloc (sizeof *list);
77882d8fe06Sniklas   if (!list)
77982d8fe06Sniklas     goto cleanup;
78082d8fe06Sniklas   TAILQ_INIT (&list->fields);
78182d8fe06Sniklas   list->cnt = 0;
782f8f1e192Sniklas   for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb;
783f8f1e192Sniklas        cb = LIST_NEXT (cb, link))
78482d8fe06Sniklas     if (strcasecmp (section, cb->section) == 0)
78582d8fe06Sniklas       {
78682d8fe06Sniklas 	list->cnt++;
787a9753648Sniklas 	node = calloc (1, sizeof *node);
78882d8fe06Sniklas 	if (!node)
78982d8fe06Sniklas 	  goto cleanup;
790a9753648Sniklas 	node->field = strdup (cb->tag);
791a9753648Sniklas 	if (!node->field)
792a9753648Sniklas 	  goto cleanup;
79382d8fe06Sniklas 	TAILQ_INSERT_TAIL (&list->fields, node, link);
79482d8fe06Sniklas       }
79582d8fe06Sniklas   return list;
79682d8fe06Sniklas 
79782d8fe06Sniklas  cleanup:
79882d8fe06Sniklas   if (list)
79982d8fe06Sniklas     conf_free_list (list);
80082d8fe06Sniklas   return 0;
80182d8fe06Sniklas }
80282d8fe06Sniklas 
8032040585eSniklas /* Decode a PEM encoded buffer.  */
8042040585eSniklas int
8052040585eSniklas conf_decode_base64 (u_int8_t *out, u_int32_t *len, u_char *buf)
8062040585eSniklas {
8072040585eSniklas   u_int32_t c = 0;
8082040585eSniklas   u_int8_t c1, c2, c3, c4;
8092040585eSniklas 
8102040585eSniklas   while (*buf)
8112040585eSniklas     {
8122040585eSniklas       if (*buf > 127 || (c1 = asc2bin[*buf]) == 255)
8132040585eSniklas 	return 0;
8142040585eSniklas       buf++;
8152040585eSniklas 
8162040585eSniklas       if (*buf > 127 || (c2 = asc2bin[*buf]) == 255)
8172040585eSniklas 	return 0;
8182040585eSniklas       buf++;
8192040585eSniklas 
8202040585eSniklas       if (*buf == '=')
8212040585eSniklas 	{
8222040585eSniklas 	  c3 = c4 = 0;
8232040585eSniklas 	  c++;
8242040585eSniklas 
8252040585eSniklas 	  /* Check last four bit */
8262040585eSniklas 	  if (c2 & 0xF)
8272040585eSniklas 	    return 0;
8282040585eSniklas 
829eee423ceSho 	  if (strcmp (buf, "==") == 0)
8302040585eSniklas 	    buf++;
8312040585eSniklas 	  else
8322040585eSniklas 	    return 0;
8332040585eSniklas 	}
8342040585eSniklas       else if (*buf > 127 || (c3 = asc2bin[*buf]) == 255)
8352040585eSniklas 	return 0;
8362040585eSniklas       else
8372040585eSniklas 	{
8382040585eSniklas 	  if (*++buf == '=')
8392040585eSniklas 	    {
8402040585eSniklas 	      c4 = 0;
8412040585eSniklas 	      c += 2;
8422040585eSniklas 
8432040585eSniklas 	      /* Check last two bit */
8442040585eSniklas 	      if (c3 & 3)
8452040585eSniklas 		return 0;
8462040585eSniklas 
8472040585eSniklas 	      if (strcmp (buf, "="))
8482040585eSniklas 		return 0;
8492040585eSniklas 
8502040585eSniklas 	    }
8512040585eSniklas 	  else if (*buf > 127 || (c4 = asc2bin[*buf]) == 255)
8522040585eSniklas 	      return 0;
8532040585eSniklas 	  else
8542040585eSniklas 	      c += 3;
8552040585eSniklas 	}
8562040585eSniklas 
8572040585eSniklas       buf++;
8582040585eSniklas       *out++ = (c1 << 2) | (c2 >> 4);
8592040585eSniklas       *out++ = (c2 << 4) | (c3 >> 2);
8602040585eSniklas       *out++ = (c3 << 6) | c4;
8612040585eSniklas     }
8622040585eSniklas 
8632040585eSniklas   *len = c;
8642040585eSniklas   return 1;
8652040585eSniklas 
8662040585eSniklas }
8672040585eSniklas 
8682040585eSniklas /* Read a line from a stream to the buffer.  */
8692040585eSniklas int
8702040585eSniklas conf_get_line (FILE *stream, char *buf, u_int32_t len)
8712040585eSniklas {
872feb24fb3Sniklas   int c;
8732040585eSniklas 
8742040585eSniklas   while (len-- > 1)
8752040585eSniklas     {
8762040585eSniklas       c = fgetc (stream);
8772040585eSniklas       if (c == '\n')
8782040585eSniklas 	{
8792040585eSniklas 	  *buf = 0;
8802040585eSniklas 	  return 1;
8812040585eSniklas 	}
8822040585eSniklas       else if (c == EOF)
8832040585eSniklas 	break;
8842040585eSniklas 
8852040585eSniklas       *buf++ = c;
8862040585eSniklas     }
8872040585eSniklas 
8882040585eSniklas   *buf = 0;
8892040585eSniklas   return 0;
8902040585eSniklas }
8912040585eSniklas 
8922040585eSniklas void
8932040585eSniklas conf_free_list (struct conf_list *list)
8942040585eSniklas {
895a9753648Sniklas   struct conf_list_node *node = TAILQ_FIRST (&list->fields);
896a9753648Sniklas 
897a9753648Sniklas   while (node)
898a9753648Sniklas     {
899a9753648Sniklas       TAILQ_REMOVE (&list->fields, node, link);
900a9753648Sniklas       if (node->field)
901a9753648Sniklas 	free (node->field);
902a9753648Sniklas       free (node);
903a9753648Sniklas       node = TAILQ_FIRST (&list->fields);
904a9753648Sniklas     }
9052040585eSniklas   free (list);
9062040585eSniklas }
907f8f1e192Sniklas 
908f8f1e192Sniklas int
909f8f1e192Sniklas conf_begin (void)
910f8f1e192Sniklas {
911f8f1e192Sniklas   static int seq = 0;
912f8f1e192Sniklas 
913f8f1e192Sniklas   return ++seq;
914f8f1e192Sniklas }
915f8f1e192Sniklas 
916f8f1e192Sniklas static struct conf_trans *
917f8f1e192Sniklas conf_trans_node (int transaction, enum conf_op op)
918f8f1e192Sniklas {
919f8f1e192Sniklas   struct conf_trans *node;
920f8f1e192Sniklas 
921f8f1e192Sniklas   node = calloc (1, sizeof *node);
922f8f1e192Sniklas   if (!node)
923f8f1e192Sniklas     {
924f8f1e192Sniklas       log_error ("conf_trans_node: calloc (1, %d) failed", sizeof *node);
925f8f1e192Sniklas       return 0;
926f8f1e192Sniklas     }
927f8f1e192Sniklas   node->trans = transaction;
928f8f1e192Sniklas   node->op = op;
929f8f1e192Sniklas   TAILQ_INSERT_TAIL (&conf_trans_queue, node, link);
930f8f1e192Sniklas   return node;
931f8f1e192Sniklas }
932f8f1e192Sniklas 
933f8f1e192Sniklas /* Queue a set operation.  */
934f8f1e192Sniklas int
935510d8b0cSniklas conf_set (int transaction, char *section, char *tag, char *value, int override,
936510d8b0cSniklas 	  int is_default)
937f8f1e192Sniklas {
938f8f1e192Sniklas   struct conf_trans *node;
939f8f1e192Sniklas 
940f8f1e192Sniklas   node = conf_trans_node (transaction, CONF_SET);
941f8f1e192Sniklas   if (!node)
942f8f1e192Sniklas     return 1;
943f8f1e192Sniklas   node->section = strdup (section);
944f8f1e192Sniklas   if (!node->section)
945f8f1e192Sniklas     {
946f8f1e192Sniklas       log_error ("conf_set: strdup (\"%s\") failed", section);
947f8f1e192Sniklas       goto fail;
948f8f1e192Sniklas     }
949f8f1e192Sniklas   node->tag = strdup (tag);
950f8f1e192Sniklas   if (!node->tag)
951f8f1e192Sniklas     {
952f8f1e192Sniklas       log_error ("conf_set: strdup (\"%s\") failed", tag);
953f8f1e192Sniklas       goto fail;
954f8f1e192Sniklas     }
955f8f1e192Sniklas   node->value = strdup (value);
956f8f1e192Sniklas   if (!node->value)
957f8f1e192Sniklas     {
958f8f1e192Sniklas       log_error ("conf_set: strdup (\"%s\") failed", value);
959f8f1e192Sniklas       goto fail;
960f8f1e192Sniklas     }
961f8f1e192Sniklas   node->override = override;
962510d8b0cSniklas   node->is_default = is_default;
963f8f1e192Sniklas   return 0;
964f8f1e192Sniklas 
965f8f1e192Sniklas  fail:
966f8f1e192Sniklas   if (node->tag)
967f8f1e192Sniklas     free (node->tag);
968f8f1e192Sniklas   if (node->section)
969f8f1e192Sniklas     free (node->section);
970f8f1e192Sniklas   if (node)
971f8f1e192Sniklas     free (node);
972f8f1e192Sniklas   return 1;
973f8f1e192Sniklas }
974f8f1e192Sniklas 
975f8f1e192Sniklas /* Queue a remove operation.  */
976f8f1e192Sniklas int
977f8f1e192Sniklas conf_remove (int transaction, char *section, char *tag)
978f8f1e192Sniklas {
979f8f1e192Sniklas   struct conf_trans *node;
980f8f1e192Sniklas 
981f8f1e192Sniklas   node = conf_trans_node (transaction, CONF_REMOVE);
982f8f1e192Sniklas   if (!node)
983f8f1e192Sniklas     goto fail;
984f8f1e192Sniklas   node->section = strdup (section);
985f8f1e192Sniklas   if (!node->section)
986f8f1e192Sniklas     {
987f8f1e192Sniklas       log_error ("conf_remove: strdup (\"%s\") failed", section);
988f8f1e192Sniklas       goto fail;
989f8f1e192Sniklas     }
990f8f1e192Sniklas   node->tag = strdup (tag);
991f8f1e192Sniklas   if (!node->tag)
992f8f1e192Sniklas     {
993f8f1e192Sniklas       log_error ("conf_remove: strdup (\"%s\") failed", tag);
994f8f1e192Sniklas       goto fail;
995f8f1e192Sniklas     }
996f8f1e192Sniklas   return 0;
997f8f1e192Sniklas 
998f8f1e192Sniklas  fail:
999f8f1e192Sniklas   if (node->section)
1000f8f1e192Sniklas     free (node->section);
1001f8f1e192Sniklas   if (node)
1002f8f1e192Sniklas     free (node);
1003f8f1e192Sniklas   return 1;
1004f8f1e192Sniklas }
1005f8f1e192Sniklas 
1006f8f1e192Sniklas /* Queue a remove section operation.  */
1007f8f1e192Sniklas int
1008f8f1e192Sniklas conf_remove_section (int transaction, char *section)
1009f8f1e192Sniklas {
1010f8f1e192Sniklas   struct conf_trans *node;
1011f8f1e192Sniklas 
1012f8f1e192Sniklas   node = conf_trans_node (transaction, CONF_REMOVE_SECTION);
1013f8f1e192Sniklas   if (!node)
1014f8f1e192Sniklas     goto fail;
1015f8f1e192Sniklas   node->section = strdup (section);
1016f8f1e192Sniklas   if (!node->section)
1017f8f1e192Sniklas     {
1018f8f1e192Sniklas       log_error ("conf_remove_section: strdup (\"%s\") failed", section);
1019f8f1e192Sniklas       goto fail;
1020f8f1e192Sniklas     }
1021f8f1e192Sniklas   return 0;
1022f8f1e192Sniklas 
1023f8f1e192Sniklas  fail:
1024f8f1e192Sniklas   if (node)
1025f8f1e192Sniklas     free (node);
1026f8f1e192Sniklas   return 1;
1027f8f1e192Sniklas }
1028f8f1e192Sniklas 
1029f8f1e192Sniklas /* Execute all queued operations for this transaction.  Cleanup.  */
1030f8f1e192Sniklas int
1031f8f1e192Sniklas conf_end (int transaction, int commit)
1032f8f1e192Sniklas {
1033f8f1e192Sniklas   struct conf_trans *node, *next;
1034f8f1e192Sniklas 
1035f8f1e192Sniklas   for (node = TAILQ_FIRST (&conf_trans_queue); node; node = next)
1036f8f1e192Sniklas     {
1037f8f1e192Sniklas       next = TAILQ_NEXT (node, link);
1038f8f1e192Sniklas       if (node->trans == transaction)
1039f8f1e192Sniklas 	{
1040f8f1e192Sniklas 	  if (commit)
1041f8f1e192Sniklas 	    switch (node->op)
1042f8f1e192Sniklas 	      {
1043f8f1e192Sniklas 	      case CONF_SET:
1044f8f1e192Sniklas 		conf_set_now (node->section, node->tag, node->value,
1045510d8b0cSniklas 			      node->override, node->is_default);
1046f8f1e192Sniklas 		break;
1047f8f1e192Sniklas 	      case CONF_REMOVE:
1048f8f1e192Sniklas 		conf_remove_now (node->section, node->tag);
1049f8f1e192Sniklas 		break;
1050f8f1e192Sniklas 	      case CONF_REMOVE_SECTION:
1051f8f1e192Sniklas 		conf_remove_section_now (node->section);
1052f8f1e192Sniklas 		break;
1053f8f1e192Sniklas 	      default:
1054f8f1e192Sniklas 		log_print ("conf_end: unknown operation: %d", node->op);
1055f8f1e192Sniklas 	      }
1056f8f1e192Sniklas 	  TAILQ_REMOVE (&conf_trans_queue, node, link);
1057074d67afSniklas 	  if (node->section)
1058074d67afSniklas 	    free (node->section);
1059074d67afSniklas 	  if (node->tag)
1060074d67afSniklas 	    free (node->tag);
1061074d67afSniklas 	  if (node->value)
1062074d67afSniklas 	    free (node->value);
1063f8f1e192Sniklas 	  free (node);
1064f8f1e192Sniklas 	}
1065f8f1e192Sniklas     }
1066f8f1e192Sniklas   return 0;
1067f8f1e192Sniklas }
1068510d8b0cSniklas 
106994de5165Sniklas /*
107094de5165Sniklas  * Dump running configuration upon SIGUSR1.
1071395a452cSho  * Configuration is "stored in reverse order", so reverse it again.
107294de5165Sniklas  */
1073510d8b0cSniklas struct dumper {
1074510d8b0cSniklas   char *s, *v;
1075510d8b0cSniklas   struct dumper *next;
1076510d8b0cSniklas };
1077510d8b0cSniklas 
1078510d8b0cSniklas static void
1079510d8b0cSniklas conf_report_dump (struct dumper *node)
1080510d8b0cSniklas {
1081510d8b0cSniklas   /* Recursive, cleanup when we're done.  */
1082510d8b0cSniklas 
1083510d8b0cSniklas   if (node->next)
1084510d8b0cSniklas     conf_report_dump (node->next);
1085510d8b0cSniklas 
1086510d8b0cSniklas   if (node->v)
1087510d8b0cSniklas     LOG_DBG ((LOG_REPORT, 0, "%s=\t%s", node->s, node->v));
1088074d67afSniklas   else if (node->s)
1089510d8b0cSniklas     {
1090510d8b0cSniklas       LOG_DBG ((LOG_REPORT, 0, "%s", node->s));
1091510d8b0cSniklas       if (strlen (node->s) > 0)
1092510d8b0cSniklas 	free (node->s);
1093510d8b0cSniklas     }
1094510d8b0cSniklas 
1095510d8b0cSniklas   free (node);
1096510d8b0cSniklas }
1097510d8b0cSniklas 
1098510d8b0cSniklas void
1099510d8b0cSniklas conf_report (void)
1100510d8b0cSniklas {
11010eb823c5Sniklas   struct conf_binding *cb, *last = 0;
110253922df5Sho   int i, len;
1103510d8b0cSniklas   char *current_section = (char *)0;
1104510d8b0cSniklas   struct dumper *dumper, *dnode;
1105510d8b0cSniklas 
1106592a196eSniklas   dumper = dnode = (struct dumper *)calloc (1, sizeof *dumper);
1107510d8b0cSniklas   if (!dumper)
1108510d8b0cSniklas     goto mem_fail;
1109510d8b0cSniklas 
1110510d8b0cSniklas   LOG_DBG ((LOG_REPORT, 0, "conf_report: dumping running configuration"));
1111510d8b0cSniklas 
1112510d8b0cSniklas   for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
1113510d8b0cSniklas     for (cb = LIST_FIRST (&conf_bindings[i]); cb;
1114510d8b0cSniklas 	 cb = LIST_NEXT (cb, link))
1115510d8b0cSniklas       {
1116510d8b0cSniklas 	if (!cb->is_default)
1117510d8b0cSniklas 	  {
11180eb823c5Sniklas 	    /* Dump this entry.  */
1119510d8b0cSniklas 	    if (!current_section || strcmp (cb->section, current_section))
1120510d8b0cSniklas 	      {
1121510d8b0cSniklas 		if (current_section)
1122510d8b0cSniklas 		  {
112353922df5Sho 		    len = strlen (current_section) + 3;
112453922df5Sho 		    dnode->s = malloc (len);
1125510d8b0cSniklas 		    if (!dnode->s)
1126510d8b0cSniklas 		      goto mem_fail;
1127510d8b0cSniklas 
112853922df5Sho 		    snprintf (dnode->s, len, "[%s]", current_section);
1129592a196eSniklas 		    dnode->next
1130592a196eSniklas 		      = (struct dumper *)calloc (1, sizeof (struct dumper));
1131510d8b0cSniklas 		    dnode = dnode->next;
1132510d8b0cSniklas 		    if (!dnode)
1133510d8b0cSniklas 		      goto mem_fail;
1134510d8b0cSniklas 
1135510d8b0cSniklas 		    dnode->s = "";
1136592a196eSniklas 		    dnode->next
1137592a196eSniklas 		      = (struct dumper *)calloc (1, sizeof (struct dumper));
1138510d8b0cSniklas 		    dnode = dnode->next;
1139510d8b0cSniklas 		    if (!dnode)
1140510d8b0cSniklas 		      goto mem_fail;
1141510d8b0cSniklas 		  }
1142510d8b0cSniklas 		current_section = cb->section;
1143510d8b0cSniklas 	      }
1144510d8b0cSniklas 	    dnode->s = cb->tag;
1145510d8b0cSniklas 	    dnode->v = cb->value;
1146510d8b0cSniklas 	    dnode->next = (struct dumper *)calloc (1, sizeof (struct dumper));
1147510d8b0cSniklas 	    dnode = dnode->next;
1148510d8b0cSniklas 	    if (!dnode)
1149510d8b0cSniklas 	      goto mem_fail;
1150510d8b0cSniklas 	    last = cb;
1151510d8b0cSniklas 	  }
1152510d8b0cSniklas       }
1153510d8b0cSniklas 
1154510d8b0cSniklas   if (last)
1155510d8b0cSniklas     {
115653922df5Sho       len = strlen (last->section) + 3;
115753922df5Sho       dnode->s = malloc (len);
1158510d8b0cSniklas       if (!dnode->s)
1159510d8b0cSniklas 	goto mem_fail;
116053922df5Sho       snprintf (dnode->s, len, "[%s]", last->section);
1161510d8b0cSniklas     }
1162510d8b0cSniklas 
1163510d8b0cSniklas   conf_report_dump (dumper);
1164510d8b0cSniklas 
1165510d8b0cSniklas   return;
1166510d8b0cSniklas 
1167510d8b0cSniklas  mem_fail:
11680eb823c5Sniklas   log_error ("conf_report: malloc/calloc failed");
11690eb823c5Sniklas   while ((dnode = dumper) != 0)
1170510d8b0cSniklas     {
1171510d8b0cSniklas       dumper = dumper->next;
1172510d8b0cSniklas       if (dnode->s)
1173510d8b0cSniklas 	free (dnode->s);
1174510d8b0cSniklas       free (dnode);
1175510d8b0cSniklas     }
1176510d8b0cSniklas   return;
1177510d8b0cSniklas }
1178