xref: /openbsd/sbin/isakmpd/conf.c (revision 592a196e)
1*592a196eSniklas /*	$OpenBSD: conf.c,v 1.15 2000/05/03 13:47:15 niklas Exp $	*/
2*592a196eSniklas /*	$EOM: conf.c,v 1.28 2000/05/03 13:24:45 niklas Exp $	*/
32040585eSniklas 
42040585eSniklas /*
598ef4a55Sniklas  * Copyright (c) 1998, 1999, 2000 Niklas Hallqvist.  All rights reserved.
62040585eSniklas  *
72040585eSniklas  * Redistribution and use in source and binary forms, with or without
82040585eSniklas  * modification, are permitted provided that the following conditions
92040585eSniklas  * are met:
102040585eSniklas  * 1. Redistributions of source code must retain the above copyright
112040585eSniklas  *    notice, this list of conditions and the following disclaimer.
122040585eSniklas  * 2. Redistributions in binary form must reproduce the above copyright
132040585eSniklas  *    notice, this list of conditions and the following disclaimer in the
142040585eSniklas  *    documentation and/or other materials provided with the distribution.
152040585eSniklas  * 3. All advertising materials mentioning features or use of this software
162040585eSniklas  *    must display the following acknowledgement:
172040585eSniklas  *	This product includes software developed by Ericsson Radio Systems.
182040585eSniklas  * 4. The name of the author may not be used to endorse or promote products
192040585eSniklas  *    derived from this software without specific prior written permission.
202040585eSniklas  *
212040585eSniklas  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
222040585eSniklas  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
232040585eSniklas  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
242040585eSniklas  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
252040585eSniklas  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
262040585eSniklas  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
272040585eSniklas  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
282040585eSniklas  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
292040585eSniklas  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
302040585eSniklas  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
312040585eSniklas  */
322040585eSniklas 
332040585eSniklas /*
342040585eSniklas  * This code was written under funding by Ericsson Radio Systems.
352040585eSniklas  */
362040585eSniklas 
372040585eSniklas #include <sys/param.h>
382040585eSniklas #include <sys/mman.h>
392040585eSniklas #include <sys/queue.h>
402040585eSniklas #include <sys/stat.h>
412040585eSniklas #include <ctype.h>
422040585eSniklas #include <fcntl.h>
432040585eSniklas #include <stdio.h>
442040585eSniklas #include <stdlib.h>
452040585eSniklas #include <string.h>
462040585eSniklas #include <unistd.h>
472040585eSniklas 
48a2d30fd1Sniklas #include "sysdep.h"
49a2d30fd1Sniklas 
50a2d30fd1Sniklas #include "app.h"
512040585eSniklas #include "conf.h"
522040585eSniklas #include "log.h"
532040585eSniklas 
54f8f1e192Sniklas struct conf_trans {
55f8f1e192Sniklas   TAILQ_ENTRY (conf_trans) link;
56f8f1e192Sniklas   int trans;
57f8f1e192Sniklas   enum conf_op { CONF_SET, CONF_REMOVE, CONF_REMOVE_SECTION } op;
58f8f1e192Sniklas   char *section;
59f8f1e192Sniklas   char *tag;
60f8f1e192Sniklas   char *value;
61f8f1e192Sniklas   int override;
62510d8b0cSniklas   int is_default;
63f8f1e192Sniklas };
64f8f1e192Sniklas 
65f8f1e192Sniklas TAILQ_HEAD (conf_trans_head, conf_trans) conf_trans_queue;
66f8f1e192Sniklas 
672040585eSniklas /*
682040585eSniklas  * Radix-64 Encoding.
692040585eSniklas  */
70510d8b0cSniklas const u_int8_t bin2asc[]
71510d8b0cSniklas   = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
722040585eSniklas 
732040585eSniklas const u_int8_t asc2bin[] =
742040585eSniklas {
752040585eSniklas   255, 255, 255, 255, 255, 255, 255, 255,
762040585eSniklas   255, 255, 255, 255, 255, 255, 255, 255,
772040585eSniklas   255, 255, 255, 255, 255, 255, 255, 255,
782040585eSniklas   255, 255, 255, 255, 255, 255, 255, 255,
792040585eSniklas   255, 255, 255, 255, 255, 255, 255, 255,
802040585eSniklas   255, 255, 255,  62, 255, 255, 255,  63,
812040585eSniklas    52,  53,  54,  55,  56,  57,  58,  59,
822040585eSniklas    60,  61, 255, 255, 255, 255, 255, 255,
832040585eSniklas   255,   0,   1,   2,   3,   4,   5,   6,
842040585eSniklas     7,   8,   9,  10,  11,  12,  13,  14,
852040585eSniklas    15,  16,  17,  18,  19,  20,  21,  22,
862040585eSniklas    23,  24,  25, 255, 255, 255, 255, 255,
872040585eSniklas   255,  26,  27,  28,  29,  30,  31,  32,
882040585eSniklas    33,  34,  35,  36,  37,  38,  39,  40,
892040585eSniklas    41,  42,  43,  44,  45,  46,  47,  48,
902040585eSniklas    49,  50,  51, 255, 255, 255, 255, 255
912040585eSniklas };
922040585eSniklas 
932040585eSniklas struct conf_binding {
942040585eSniklas   LIST_ENTRY (conf_binding) link;
952040585eSniklas   char *section;
962040585eSniklas   char *tag;
972040585eSniklas   char *value;
98510d8b0cSniklas   int is_default;
992040585eSniklas };
1002040585eSniklas 
1012040585eSniklas char *conf_path = CONFIG_FILE;
102f8f1e192Sniklas LIST_HEAD (conf_bindings, conf_binding) conf_bindings[256];
1032040585eSniklas 
1042040585eSniklas static char *conf_addr;
1052040585eSniklas 
106f8f1e192Sniklas static __inline__ u_int8_t
107f8f1e192Sniklas conf_hash (char *s)
108f8f1e192Sniklas {
109f8f1e192Sniklas   u_int8_t hash = 0;
110f8f1e192Sniklas 
111f8f1e192Sniklas   while (*s)
112f8f1e192Sniklas     {
113f8f1e192Sniklas       hash = ((hash << 1) | (hash >> 7)) ^ tolower (*s);
114f8f1e192Sniklas       s++;
115f8f1e192Sniklas     }
116f8f1e192Sniklas   return hash;
117f8f1e192Sniklas }
118f8f1e192Sniklas 
119f8f1e192Sniklas /*
120f8f1e192Sniklas  * Insert a tag-value combination from LINE (the equal sign is at POS)
121f8f1e192Sniklas  */
122f8f1e192Sniklas static int
123f8f1e192Sniklas conf_remove_now (char *section, char *tag)
124f8f1e192Sniklas {
125f8f1e192Sniklas   struct conf_binding *cb, *next;
126f8f1e192Sniklas 
127f8f1e192Sniklas   for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb; cb = next)
128f8f1e192Sniklas     {
129f8f1e192Sniklas       next = LIST_NEXT (cb, link);
130f8f1e192Sniklas       if (strcasecmp (cb->section, section) == 0
131f8f1e192Sniklas 	  && strcasecmp (cb->tag, tag) == 0)
132f8f1e192Sniklas 	{
133f8f1e192Sniklas 	  LIST_REMOVE (cb, link);
13451ca15aeSniklas 	  LOG_DBG ((LOG_MISC, 70, "[%s]:%s->%s removed", section, tag,
13551ca15aeSniklas 		    cb->value));
136f8f1e192Sniklas 	  free (cb->section);
137f8f1e192Sniklas 	  free (cb->tag);
138f8f1e192Sniklas 	  free (cb->value);
139f8f1e192Sniklas 	  free (cb);
140f8f1e192Sniklas 	  return 0;
141f8f1e192Sniklas 	}
142f8f1e192Sniklas     }
143f8f1e192Sniklas   return 1;
144f8f1e192Sniklas }
145f8f1e192Sniklas 
146f8f1e192Sniklas static int
147f8f1e192Sniklas conf_remove_section_now (char *section)
148f8f1e192Sniklas {
149f8f1e192Sniklas   struct conf_binding *cb, *next;
150f8f1e192Sniklas   int unseen = 1;
151f8f1e192Sniklas 
152f8f1e192Sniklas   for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb; cb = next)
153f8f1e192Sniklas     {
154f8f1e192Sniklas       next = LIST_NEXT (cb, link);
155f8f1e192Sniklas       if (strcasecmp (cb->section, section) == 0)
156f8f1e192Sniklas 	{
157f8f1e192Sniklas 	  unseen = 0;
158f8f1e192Sniklas 	  LIST_REMOVE (cb, link);
15951ca15aeSniklas 	  LOG_DBG ((LOG_MISC, 70, "[%s]:%s->%s removed", section, cb->tag,
16051ca15aeSniklas 		    cb->value));
161f8f1e192Sniklas 	  free (cb->section);
162f8f1e192Sniklas 	  free (cb->tag);
163f8f1e192Sniklas 	  free (cb->value);
164f8f1e192Sniklas 	  free (cb);
165f8f1e192Sniklas 	}
166f8f1e192Sniklas     }
167f8f1e192Sniklas   return unseen;
168f8f1e192Sniklas }
169f8f1e192Sniklas 
1702040585eSniklas /*
1712040585eSniklas  * Insert a tag-value combination from LINE (the equal sign is at POS)
1722040585eSniklas  * into SECTION of our configuration database.
1732040585eSniklas  */
174f8f1e192Sniklas static int
175510d8b0cSniklas conf_set_now (char *section, char *tag, char *value, int override,
176510d8b0cSniklas 	      int is_default)
1772040585eSniklas {
178f8f1e192Sniklas   struct conf_binding *node = 0;
1792040585eSniklas 
180f8f1e192Sniklas   if (override)
181f8f1e192Sniklas     conf_remove_now (section, tag);
182f8f1e192Sniklas   else if (conf_get_str (section, tag))
1832040585eSniklas     {
184510d8b0cSniklas       if (is_default == 0)
1852040585eSniklas 	log_print ("conf_set: duplicate tag [%s]:%s, ignoring...\n", section,
186f8f1e192Sniklas 		   tag);
187f8f1e192Sniklas       return 1;
1882040585eSniklas     }
189f8f1e192Sniklas 
190f8f1e192Sniklas   node = calloc (1, sizeof *node);
191f8f1e192Sniklas   if (!node)
192f8f1e192Sniklas     {
193f8f1e192Sniklas       log_error ("conf_set: calloc (1, %d) failed", sizeof *node);
194f8f1e192Sniklas       return 1;
195f8f1e192Sniklas     }
196f8f1e192Sniklas   node->section = section;
197f8f1e192Sniklas   node->tag = tag;
198f8f1e192Sniklas   node->value = value;
199510d8b0cSniklas   node->is_default = is_default;
200f8f1e192Sniklas 
201f8f1e192Sniklas   LIST_INSERT_HEAD (&conf_bindings[conf_hash (section)], node, link);
20251ca15aeSniklas   LOG_DBG ((LOG_MISC, 70, "[%s]:%s->%s", node->section, node->tag,
20351ca15aeSniklas 	    node->value));
204f8f1e192Sniklas   return 0;
2052040585eSniklas }
2062040585eSniklas 
2072040585eSniklas /*
2082040585eSniklas  * Parse the line LINE of SZ bytes.  Skip Comments, recognize section
2092040585eSniklas  * headers and feed tag-value pairs into our configuration database.
2102040585eSniklas  */
2112040585eSniklas static void
212f8f1e192Sniklas conf_parse_line (int trans, char *line, size_t sz)
2132040585eSniklas {
2142040585eSniklas   char *cp = line;
2152040585eSniklas   int i;
2162040585eSniklas   static char *section = 0;
2172040585eSniklas   static int ln = 0;
2182040585eSniklas 
2192040585eSniklas   ln++;
2202040585eSniklas 
2212040585eSniklas   /* Lines starting with '#' or ';' are comments.  */
2222040585eSniklas   if (*line == '#' || *line == ';')
2232040585eSniklas     return;
2242040585eSniklas 
2252040585eSniklas   /* '[section]' parsing...  */
2262040585eSniklas   if (*line == '[')
2272040585eSniklas     {
2282040585eSniklas       for (i = 1; i < sz; i++)
2292040585eSniklas 	if (line[i] == ']')
2302040585eSniklas 	  break;
2312040585eSniklas       if (i == sz)
2322040585eSniklas 	{
2332040585eSniklas 	  log_print ("conf_parse_line: %d:"
2342040585eSniklas 		     "non-matched ']', ignoring until next section", ln);
2352040585eSniklas 	  section = 0;
2362040585eSniklas 	  return;
2372040585eSniklas 	}
2382040585eSniklas       section = malloc (i);
2392040585eSniklas       strncpy (section, line + 1, i - 1);
2402040585eSniklas       section[i - 1] = '\0';
2412040585eSniklas       return;
2422040585eSniklas     }
2432040585eSniklas 
2442040585eSniklas   /* Deal with assignments.  */
2452040585eSniklas   for (i = 0; i < sz; i++)
2462040585eSniklas     if (cp[i] == '=')
2472040585eSniklas       {
2482040585eSniklas 	/* If no section, we are ignoring the lines.  */
2492040585eSniklas 	if (!section)
2502040585eSniklas 	  {
2512040585eSniklas 	    log_print ("conf_parse_line: %d: ignoring line due to no section",
2522040585eSniklas 		       ln);
2532040585eSniklas 	    return;
2542040585eSniklas 	  }
255f8f1e192Sniklas 	line[strcspn (line, " \t=")] = '\0';
256f8f1e192Sniklas 	/* XXX Perhaps should we not ignore errors?  */
257f8f1e192Sniklas 	conf_set (trans, section, line,
258510d8b0cSniklas 		  line + i + 1 + strspn (line + i + 1, " \t"), 0, 0);
2592040585eSniklas 	return;
2602040585eSniklas       }
2612040585eSniklas 
2622040585eSniklas   /* Other non-empty lines are wierd.  */
2632040585eSniklas   i = strspn (line, " \t");
2642040585eSniklas   if (line[i])
2652040585eSniklas     log_print ("conf_parse_line: %d: syntax error", ln);
2662040585eSniklas 
2672040585eSniklas   return;
2682040585eSniklas }
2692040585eSniklas 
2702040585eSniklas /* Parse the mapped configuration file.  */
2712040585eSniklas static void
272f8f1e192Sniklas conf_parse (int trans, char *buf, size_t sz)
2732040585eSniklas {
274f8f1e192Sniklas   char *cp = buf;
275f8f1e192Sniklas   char *bufend = buf + sz;
2762040585eSniklas   char *line;
2772040585eSniklas 
2782040585eSniklas   line = cp;
279f8f1e192Sniklas   while (cp < bufend)
2802040585eSniklas     {
2812040585eSniklas       if (*cp == '\n')
2822040585eSniklas 	{
2832040585eSniklas 	  /* Check for escaped newlines.  */
284f8f1e192Sniklas 	  if (cp > buf && *(cp - 1) == '\\')
2852040585eSniklas 	    *(cp - 1) = *cp = ' ';
2862040585eSniklas 	  else
2872040585eSniklas 	    {
2882040585eSniklas 	      *cp = '\0';
289f8f1e192Sniklas 	      conf_parse_line (trans, line, cp - line);
2902040585eSniklas 	      line = cp + 1;
2912040585eSniklas 	    }
2922040585eSniklas 	}
2932040585eSniklas       cp++;
2942040585eSniklas     }
2952040585eSniklas   if (cp != line)
2962040585eSniklas     log_print ("conf_parse: last line non-terminated, ignored.");
2972040585eSniklas }
2982040585eSniklas 
299510d8b0cSniklas /*
300510d8b0cSniklas  * Auto-generate default configuration values for the transforms and
301510d8b0cSniklas  * suites the user wants.
302510d8b0cSniklas  *
303510d8b0cSniklas  * Resulting section names can be:
304510d8b0cSniklas  *  For main mode:
305510d8b0cSniklas  *     {DES,BLF,3DES,CAST}-{MD5,SHA}[-{DSS,RSA_SIG}]
306510d8b0cSniklas  *  For quick mode:
307510d8b0cSniklas  *     QM-{ESP,AH}[-TRP]-{DES,3DES,CAST,BLF}[-{MD5,SHA}][-PFS]-SUITE
308510d8b0cSniklas  * DH groups; currently always MODP_768 for MD5, and MODP_1024 for SHA.
309510d8b0cSniklas  *
310510d8b0cSniklas  * XXX We may want to support USE_BLOWFISH, USE_TRIPLEDES, etc...
311510d8b0cSniklas  * XXX No EC2N DH support here yet.
312510d8b0cSniklas  */
313510d8b0cSniklas 
314510d8b0cSniklas int
315510d8b0cSniklas conf_find_trans_xf (int phase, char *xf)
316510d8b0cSniklas {
317510d8b0cSniklas   struct conf_trans *node;
318510d8b0cSniklas   char *p;
319510d8b0cSniklas 
320510d8b0cSniklas   /* Find the relevant transforms and suites, if any. */
321510d8b0cSniklas   for (node = TAILQ_FIRST (&conf_trans_queue); node;
322510d8b0cSniklas        node = TAILQ_NEXT (node, link))
323510d8b0cSniklas     if (( phase == 1 && !strcmp ("Transforms", node->tag)) ||
324510d8b0cSniklas 	( phase == 2 && !strcmp ("Suites", node->tag)))
325510d8b0cSniklas       {
326510d8b0cSniklas 	p = node->value;
327510d8b0cSniklas 	while ((p = strstr (p, xf)) != NULL)
328510d8b0cSniklas 	  if ( *(p + strlen (p)) && *(p + strlen(p)) != ',')
329510d8b0cSniklas 	    p += strlen(p);
330510d8b0cSniklas 	  else
331510d8b0cSniklas 	    return 1;
332510d8b0cSniklas       }
333510d8b0cSniklas   return 0;
334510d8b0cSniklas }
335510d8b0cSniklas 
336510d8b0cSniklas void
337510d8b0cSniklas conf_load_defaults (int tr)
338510d8b0cSniklas {
339510d8b0cSniklas   int enc, auth, hash, proto, mode, pfs;
340510d8b0cSniklas   char sect[256];
341510d8b0cSniklas 
342510d8b0cSniklas   char *mm_auth[]   = { "PRE_SHARED", "DSS", "RSA_SIG", NULL };
343510d8b0cSniklas   char *mm_hash[]   = { "MD5", "SHA", NULL };
344510d8b0cSniklas   char *mm_enc[]    = { "DES_CBC", "BLOWFISH_CBC", "3DES_CBC",
345510d8b0cSniklas 			"CAST_CBC", NULL };
346510d8b0cSniklas   char *dh_group[]  = { "MODP_768", "MODP_1024", "MODP_1536", NULL };
347510d8b0cSniklas   char *qm_enc[]    = { "DES", "3DES", "CAST", "BLOWFISH", NULL };
348510d8b0cSniklas   char *qm_hash[]   = { "HMAC_MD5", "HMAC_SHA", "NONE", NULL };
349510d8b0cSniklas 
350510d8b0cSniklas   /* Abbreviations to make section names a bit shorter.  */
351510d8b0cSniklas   char *mm_auth_p[] = { "", "-DSS", "-RSA_SIG", NULL };
352510d8b0cSniklas   char *mm_enc_p[]  = { "DES", "BLF", "3DES", "CAST", NULL };
353510d8b0cSniklas   char *qm_enc_p[]  = { "-DES", "-3DES", "-CAST", "-BLF", NULL };
354510d8b0cSniklas   char *qm_hash_p[] = { "-MD5", "-SHA", "", NULL };
355510d8b0cSniklas 
356510d8b0cSniklas   /* Helper #defines, incl abbreviations.  */
357510d8b0cSniklas #define PROTO(x)  ((x) ? "AH" : "ESP")
358510d8b0cSniklas #define PFS(x)    ((x) ? "-PFS" : "")
359510d8b0cSniklas #define MODE(x)   ((x) ? "TRANSPORT" : "TUNNEL")
360510d8b0cSniklas #define MODE_p(x) ((x) ? "-TRP" : "")
361510d8b0cSniklas 
362510d8b0cSniklas   /* General and X509 defaults */
363510d8b0cSniklas   conf_set (tr, "General", "Retransmits", CONF_DFLT_RETRANSMITS, 0, 1);
364510d8b0cSniklas   conf_set (tr, "General", "Exchange-max-time", CONF_DFLT_EXCH_MAX_TIME, 0, 1);
365510d8b0cSniklas   conf_set (tr, "General", "Policy-file", CONF_DFLT_POLICY_FILE, 0, 1);
366510d8b0cSniklas 
367510d8b0cSniklas #ifdef USE_X509
368510d8b0cSniklas   conf_set (tr, "X509-certificates", "CA-directory", CONF_DFLT_X509_CA_DIR, 0,
369510d8b0cSniklas 	    1);
370510d8b0cSniklas   conf_set (tr, "X509-certificates", "Cert-directory", CONF_DFLT_X509_CERT_DIR,
371510d8b0cSniklas 	    0, 1);
372510d8b0cSniklas   conf_set (tr, "X509-certificates", "Private-key", CONF_DFLT_X509_PRIVATE_KEY,
373510d8b0cSniklas 	    0, 1);
374510d8b0cSniklas #endif
375510d8b0cSniklas 
376510d8b0cSniklas   /* Main modes */
377510d8b0cSniklas   for (enc = 0; mm_enc[enc]; enc ++)
378510d8b0cSniklas     for (hash = 0; mm_hash[hash]; hash ++)
379510d8b0cSniklas       for (auth = 0; mm_auth[auth]; auth ++)
380510d8b0cSniklas 	{
381510d8b0cSniklas 	  sprintf (sect, "%s-%s%s", mm_enc_p[enc], mm_hash[hash],
382510d8b0cSniklas 		   mm_auth_p[auth]);
383510d8b0cSniklas 
384510d8b0cSniklas 	  if (!conf_find_trans_xf (1, sect))
385510d8b0cSniklas 	    continue;
386510d8b0cSniklas 
387510d8b0cSniklas 	  LOG_DBG ((LOG_MISC, 40, "conf_load_defaults : main mode %s", sect));
388510d8b0cSniklas 
389510d8b0cSniklas 	  conf_set (tr, sect, "ENCRYPTION_ALGORITHM", mm_enc[enc], 0, 1);
390510d8b0cSniklas 	  if (!strcmp (mm_enc[enc], "BLOWFISH_CBC"))
391510d8b0cSniklas 	    conf_set (tr, sect, "KEY_LENGTH", CONF_DFLT_VAL_BLF_KEYLEN, 0, 1);
392510d8b0cSniklas 
393510d8b0cSniklas 	  conf_set (tr, sect, "HASH_ALGORITHM", mm_hash[hash], 0, 1);
394510d8b0cSniklas 	  conf_set (tr, sect, "AUTHENTICATION_METHOD", mm_auth[auth], 0, 1);
395510d8b0cSniklas 
396510d8b0cSniklas 	  /* XXX Assumes md5 -> modp768 and sha -> modp1024 */
397510d8b0cSniklas 	  conf_set (tr, sect, "GROUP_DESCRIPTION", dh_group[hash], 0, 1);
398510d8b0cSniklas 
399510d8b0cSniklas 	  conf_set (tr, sect, "Life", CONF_DFLT_TAG_LIFE_MAIN_MODE, 0, 1);
400510d8b0cSniklas 	}
401510d8b0cSniklas 
402510d8b0cSniklas   /* Quick modes */
403510d8b0cSniklas   for (enc = 0; qm_enc[enc]; enc ++)
404510d8b0cSniklas     for (proto = 0; proto < 2; proto ++)
405510d8b0cSniklas       for (mode = 0; mode < 2; mode ++)
406510d8b0cSniklas 	for (pfs = 0; pfs < 2; pfs ++)
407510d8b0cSniklas 	  for (hash = 0; qm_hash[hash]; hash ++)
408510d8b0cSniklas 	    if ((proto == 1 && /* AH */
409510d8b0cSniklas 		 !strcmp (qm_hash[hash], "NONE")))
410510d8b0cSniklas 	      continue;
411510d8b0cSniklas 	    else
412510d8b0cSniklas 	      {
413510d8b0cSniklas 		char tmp[256];
414510d8b0cSniklas 
415510d8b0cSniklas 		sprintf (tmp, "QM-%s%s%s%s%s", PROTO (proto), MODE_p (mode),
416510d8b0cSniklas 			 qm_enc_p[enc], qm_hash_p[hash], PFS (pfs));
417510d8b0cSniklas 
418510d8b0cSniklas 		strcpy (sect, tmp);
419510d8b0cSniklas 		strcat (sect, "-SUITE");
420510d8b0cSniklas 
421510d8b0cSniklas 		if (!conf_find_trans_xf (2, sect))
422510d8b0cSniklas 		  continue;
423510d8b0cSniklas 
424510d8b0cSniklas 		LOG_DBG ((LOG_MISC, 40, "conf_load_defaults : quick mode %s",
425510d8b0cSniklas 			  sect));
426510d8b0cSniklas 
427510d8b0cSniklas 		conf_set (tr, sect, "Protocols", tmp, 0, 1);
428510d8b0cSniklas 
429510d8b0cSniklas 		sprintf (sect, "IPSEC_%s", PROTO (proto));
430510d8b0cSniklas 		conf_set (tr, tmp, "PROTOCOL_ID", sect, 0, 1);
431510d8b0cSniklas 
432510d8b0cSniklas 		strcpy (sect, tmp);
433510d8b0cSniklas 		strcat (sect, "-XF");
434510d8b0cSniklas 		conf_set (tr, tmp, "Transforms", sect, 0, 1);
435510d8b0cSniklas 
436510d8b0cSniklas                 /* XXX For now, defaults contain just one xf per protocol.  */
437510d8b0cSniklas 
438510d8b0cSniklas 		conf_set (tr, sect, "TRANSFORM_ID", qm_enc[enc], 0, 1);
439510d8b0cSniklas 
440510d8b0cSniklas                 if (!strcmp (qm_enc[enc], "BLOWFISH"))
441510d8b0cSniklas 		  conf_set (tr, sect, "KEY_LENGTH", CONF_DFLT_VAL_BLF_KEYLEN,
442510d8b0cSniklas 			    0, 1);
443510d8b0cSniklas 
444510d8b0cSniklas 		conf_set (tr, sect, "ENCAPSULATION_MODE", MODE (mode), 0, 1);
445510d8b0cSniklas 
446510d8b0cSniklas                 if (strcmp (qm_hash[hash], "NONE"))
447510d8b0cSniklas                 {
448510d8b0cSniklas 		  conf_set (tr, sect, "AUTHENTICATION_ALGORITHM",
449510d8b0cSniklas 			    qm_hash[hash], 0, 1);
450510d8b0cSniklas 
451510d8b0cSniklas                   /* XXX Another shortcut -- to keep length down.  */
452510d8b0cSniklas                   if (pfs)
453510d8b0cSniklas 		    conf_set (tr, sect, "GROUP_DESCRIPTION",
454510d8b0cSniklas 			      dh_group[ ((hash<2) ? hash : 1) ], 0, 1);
455510d8b0cSniklas                 }
456510d8b0cSniklas 
457510d8b0cSniklas                 /* XXX Lifetimes depending on enc/auth strength?  */
458510d8b0cSniklas 		conf_set (tr, sect, "Life", CONF_DFLT_TAG_LIFE_QUICK_MODE, 0,
459510d8b0cSniklas 			  1);
460510d8b0cSniklas 	      }
461510d8b0cSniklas 
462510d8b0cSniklas   /* Lifetimes */
463510d8b0cSniklas   conf_set (tr, CONF_DFLT_TAG_LIFE_MAIN_MODE, "LIFE_TYPE",
464510d8b0cSniklas 	    CONF_DFLT_TYPE_LIFE_MAIN_MODE, 0, 1);
465510d8b0cSniklas   conf_set (tr, CONF_DFLT_TAG_LIFE_MAIN_MODE, "LIFE_DURATION",
466510d8b0cSniklas 	    CONF_DFLT_VAL_LIFE_MAIN_MODE, 0, 1);
467510d8b0cSniklas 
468510d8b0cSniklas   conf_set (tr, CONF_DFLT_TAG_LIFE_QUICK_MODE, "LIFE_TYPE",
469510d8b0cSniklas 	    CONF_DFLT_TYPE_LIFE_QUICK_MODE, 0, 1);
470510d8b0cSniklas   conf_set (tr, CONF_DFLT_TAG_LIFE_QUICK_MODE, "LIFE_DURATION",
471510d8b0cSniklas 	    CONF_DFLT_VAL_LIFE_QUICK_MODE, 0, 1);
472510d8b0cSniklas 
473510d8b0cSniklas   return;
474510d8b0cSniklas }
475510d8b0cSniklas 
4762040585eSniklas void
4772040585eSniklas conf_init (void)
4782040585eSniklas {
479f8f1e192Sniklas   int i;
4802040585eSniklas 
481f8f1e192Sniklas   for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
482f8f1e192Sniklas     LIST_INIT (&conf_bindings[i]);
483f8f1e192Sniklas   TAILQ_INIT (&conf_trans_queue);
484f8f1e192Sniklas   conf_reinit ();
4852040585eSniklas }
4862040585eSniklas 
487f8f1e192Sniklas /* Open the config file and map it into our address space, then parse it.  */
488f8f1e192Sniklas void
489f8f1e192Sniklas conf_reinit (void)
490f8f1e192Sniklas {
491f8f1e192Sniklas   struct conf_binding *cb = 0;
492f8f1e192Sniklas   int fd, i, trans;
493f8f1e192Sniklas   struct stat st;
494f8f1e192Sniklas   off_t sz;
495f8f1e192Sniklas   char *new_conf_addr = 0;
496f8f1e192Sniklas 
4972040585eSniklas   fd = open (conf_path, O_RDONLY);
4982040585eSniklas   if (fd == -1)
499f8f1e192Sniklas     {
500757a19b2Sniklas       log_error ("conf_reinit: open (\"%s\", O_RDONLY) failed", conf_path);
501f8f1e192Sniklas       return;
502f8f1e192Sniklas     }
5032040585eSniklas   if (fstat (fd, &st) == -1)
504f8f1e192Sniklas     {
505757a19b2Sniklas       log_error ("conf_reinit: fstat (%d, &st) failed", fd);
506f8f1e192Sniklas       goto fail;
507f8f1e192Sniklas     }
508510d8b0cSniklas   if (st.st_uid != geteuid () && st.st_uid != getuid ())
509510d8b0cSniklas     {
510510d8b0cSniklas       log_print ("conf_reinit: not loading %s - file owner is not process "
511510d8b0cSniklas 		 "user", conf_path);
512510d8b0cSniklas       close (fd);
513510d8b0cSniklas       return;
514510d8b0cSniklas     }
515510d8b0cSniklas   if ((st.st_mode & (S_IRWXG | S_IRWXO)) != 0)
516510d8b0cSniklas     {
517510d8b0cSniklas       log_print ("conf_reinit: not loading %s - too open permissions",
518510d8b0cSniklas 		 conf_path);
519510d8b0cSniklas       close (fd);
520510d8b0cSniklas       return;
521510d8b0cSniklas     }
522f8f1e192Sniklas   sz = st.st_size;
523f8f1e192Sniklas   new_conf_addr = malloc (sz);
524f8f1e192Sniklas   if (!new_conf_addr)
525f8f1e192Sniklas     {
526757a19b2Sniklas       log_error ("conf_reinit: malloc (%d) failed", sz);
527f8f1e192Sniklas       goto fail;
528f8f1e192Sniklas     }
5292040585eSniklas   /* XXX I assume short reads won't happen here.  */
530f8f1e192Sniklas   if (read (fd, new_conf_addr, sz) != sz)
531f8f1e192Sniklas     {
532757a19b2Sniklas       log_error ("conf_reinit: read (%d, %p, %d) failed", fd, new_conf_addr,
533757a19b2Sniklas 		 sz);
534f8f1e192Sniklas       goto fail;
535f8f1e192Sniklas     }
5362040585eSniklas   close (fd);
5372040585eSniklas 
538f8f1e192Sniklas   trans = conf_begin ();
539f8f1e192Sniklas 
540f8f1e192Sniklas   /* XXX Should we not care about errors and rollback?  */
541f8f1e192Sniklas   conf_parse (trans, new_conf_addr, sz);
542f8f1e192Sniklas 
543510d8b0cSniklas   /* Load default configuration values.  */
544510d8b0cSniklas   conf_load_defaults (trans);
545510d8b0cSniklas 
546f8f1e192Sniklas   /* Free potential existing configuration.  */
547f8f1e192Sniklas   if (conf_addr)
548f8f1e192Sniklas     {
549f8f1e192Sniklas       for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
550f8f1e192Sniklas 	for (cb = LIST_FIRST (&conf_bindings[i]); cb;
551f8f1e192Sniklas 	     cb = LIST_FIRST (&conf_bindings[i]))
552f8f1e192Sniklas 	  conf_remove_now (cb->section, cb->tag);
553f8f1e192Sniklas       free (conf_addr);
554f8f1e192Sniklas     }
555f8f1e192Sniklas 
556f8f1e192Sniklas   conf_end (trans, 1);
557f8f1e192Sniklas   conf_addr = new_conf_addr;
558f8f1e192Sniklas   return;
559f8f1e192Sniklas 
560f8f1e192Sniklas  fail:
561f8f1e192Sniklas   if (new_conf_addr)
562f8f1e192Sniklas     free (new_conf_addr);
563f8f1e192Sniklas   close (fd);
5642040585eSniklas }
5652040585eSniklas 
566a2d30fd1Sniklas /*
567a2d30fd1Sniklas  * Return the numeric value denoted by TAG in section SECTION or DEF
568a2d30fd1Sniklas  * if that tag does not exist.
569a2d30fd1Sniklas  */
5702040585eSniklas int
571a2d30fd1Sniklas conf_get_num (char *section, char *tag, int def)
5722040585eSniklas {
5732040585eSniklas   char *value = conf_get_str (section, tag);
5742040585eSniklas 
5752040585eSniklas   if (value)
5762040585eSniklas       return atoi (value);
577a2d30fd1Sniklas   return def;
5782040585eSniklas }
5792040585eSniklas 
58082d8fe06Sniklas /* Validate X according to the range denoted by TAG in section SECTION.  */
58182d8fe06Sniklas int
58282d8fe06Sniklas conf_match_num (char *section, char *tag, int x)
58382d8fe06Sniklas {
58482d8fe06Sniklas   char *value = conf_get_str (section, tag);
58582d8fe06Sniklas   int val, min, max, n;
58682d8fe06Sniklas 
58782d8fe06Sniklas   if (!value)
58882d8fe06Sniklas     return 0;
58982d8fe06Sniklas   n = sscanf (value, "%d,%d:%d", &val, &min, &max);
59082d8fe06Sniklas   switch (n)
59182d8fe06Sniklas     {
59282d8fe06Sniklas     case 1:
59351ca15aeSniklas       LOG_DBG ((LOG_MISC, 90, "conf_match_num: %s:%s %d==%d?", section, tag,
59451ca15aeSniklas 		val, x));
59582d8fe06Sniklas       return x == val;
59682d8fe06Sniklas     case 3:
59751ca15aeSniklas       LOG_DBG ((LOG_MISC, 90, "conf_match_num: %s:%s %d<=%d<=%d?", section,
59851ca15aeSniklas 		tag, min, x, max));
59982d8fe06Sniklas       return min <= x && max >= x;
60082d8fe06Sniklas     default:
60182d8fe06Sniklas       log_error ("conf_match_num: section %s tag %s: invalid number spec %s",
60282d8fe06Sniklas 		 section, tag, value);
60382d8fe06Sniklas     }
60482d8fe06Sniklas   return 0;
60582d8fe06Sniklas }
60682d8fe06Sniklas 
6072040585eSniklas /* Return the string value denoted by TAG in section SECTION.  */
6082040585eSniklas char *
6092040585eSniklas conf_get_str (char *section, char *tag)
6102040585eSniklas {
6112040585eSniklas   struct conf_binding *cb;
6122040585eSniklas 
613f8f1e192Sniklas   for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb;
614f8f1e192Sniklas        cb = LIST_NEXT (cb, link))
6152040585eSniklas     if (strcasecmp (section, cb->section) == 0
6162040585eSniklas 	&& strcasecmp (tag, cb->tag) == 0)
6172040585eSniklas       {
61851ca15aeSniklas 	LOG_DBG ((LOG_MISC, 60, "conf_get_str: [%s]:%s->%s", section,
61951ca15aeSniklas 		  tag, cb->value));
6202040585eSniklas 	return cb->value;
6212040585eSniklas       }
62251ca15aeSniklas   LOG_DBG ((LOG_MISC, 60,
623f8f1e192Sniklas 	    "conf_get_str: configuration value not found [%s]:%s", section,
62451ca15aeSniklas 	    tag));
6252040585eSniklas   return 0;
6262040585eSniklas }
6272040585eSniklas 
628a9753648Sniklas /*
629a9753648Sniklas  * Build a list of string values out of the comma separated value denoted by
630a9753648Sniklas  * TAG in SECTION.
631a9753648Sniklas  */
6322040585eSniklas struct conf_list *
6332040585eSniklas conf_get_list (char *section, char *tag)
6342040585eSniklas {
6352040585eSniklas   char *liststr = 0, *p, *field;
6362040585eSniklas   struct conf_list *list = 0;
6372040585eSniklas   struct conf_list_node *node;
6382040585eSniklas 
6392040585eSniklas   list = malloc (sizeof *list);
6402040585eSniklas   if (!list)
6412040585eSniklas     goto cleanup;
6422040585eSniklas   TAILQ_INIT (&list->fields);
6432040585eSniklas   list->cnt = 0;
6442040585eSniklas   liststr = conf_get_str (section, tag);
6452040585eSniklas   if (!liststr)
6462040585eSniklas     goto cleanup;
6472040585eSniklas   liststr = strdup (liststr);
6482040585eSniklas   if (!liststr)
6492040585eSniklas     goto cleanup;
6502040585eSniklas   p = liststr;
6512040585eSniklas   while ((field = strsep (&p, ", \t")) != NULL)
6522040585eSniklas     {
6532040585eSniklas       if (*field == '\0')
6542040585eSniklas 	{
6552040585eSniklas 	  log_print ("conf_get_list: empty field, ignoring...");
6562040585eSniklas 	  continue;
6572040585eSniklas 	}
6582040585eSniklas       list->cnt++;
659a9753648Sniklas       node = calloc (1, sizeof *node);
6602040585eSniklas       if (!node)
6612040585eSniklas 	goto cleanup;
662a9753648Sniklas       node->field = strdup (field);
663a9753648Sniklas       if (!node->field)
664a9753648Sniklas 	goto cleanup;
6652040585eSniklas       TAILQ_INSERT_TAIL (&list->fields, node, link);
6662040585eSniklas     }
667a9753648Sniklas   free (liststr);
6682040585eSniklas   return list;
6692040585eSniklas 
6702040585eSniklas  cleanup:
6712040585eSniklas   if (list)
6722040585eSniklas     conf_free_list (list);
6732040585eSniklas   if (liststr)
6742040585eSniklas     free (liststr);
6752040585eSniklas   return 0;
6762040585eSniklas }
6772040585eSniklas 
67882d8fe06Sniklas struct conf_list *
67982d8fe06Sniklas conf_get_tag_list (char *section)
68082d8fe06Sniklas {
68182d8fe06Sniklas   struct conf_list *list = 0;
68282d8fe06Sniklas   struct conf_list_node *node;
68382d8fe06Sniklas   struct conf_binding *cb;
68482d8fe06Sniklas 
68582d8fe06Sniklas   list = malloc (sizeof *list);
68682d8fe06Sniklas   if (!list)
68782d8fe06Sniklas     goto cleanup;
68882d8fe06Sniklas   TAILQ_INIT (&list->fields);
68982d8fe06Sniklas   list->cnt = 0;
690f8f1e192Sniklas   for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb;
691f8f1e192Sniklas        cb = LIST_NEXT (cb, link))
69282d8fe06Sniklas     if (strcasecmp (section, cb->section) == 0)
69382d8fe06Sniklas       {
69482d8fe06Sniklas 	list->cnt++;
695a9753648Sniklas 	node = calloc (1, sizeof *node);
69682d8fe06Sniklas 	if (!node)
69782d8fe06Sniklas 	  goto cleanup;
698a9753648Sniklas 	node->field = strdup (cb->tag);
699a9753648Sniklas 	if (!node->field)
700a9753648Sniklas 	  goto cleanup;
70182d8fe06Sniklas 	TAILQ_INSERT_TAIL (&list->fields, node, link);
70282d8fe06Sniklas       }
70382d8fe06Sniklas   return list;
70482d8fe06Sniklas 
70582d8fe06Sniklas  cleanup:
70682d8fe06Sniklas   if (list)
70782d8fe06Sniklas     conf_free_list (list);
70882d8fe06Sniklas   return 0;
70982d8fe06Sniklas }
71082d8fe06Sniklas 
7112040585eSniklas /* Decode a PEM encoded buffer.  */
7122040585eSniklas int
7132040585eSniklas conf_decode_base64 (u_int8_t *out, u_int32_t *len, u_char *buf)
7142040585eSniklas {
7152040585eSniklas   u_int32_t c = 0;
7162040585eSniklas   u_int8_t c1, c2, c3, c4;
7172040585eSniklas 
7182040585eSniklas   while (*buf)
7192040585eSniklas     {
7202040585eSniklas       if (*buf > 127 || (c1 = asc2bin[*buf]) == 255)
7212040585eSniklas 	return 0;
7222040585eSniklas       buf++;
7232040585eSniklas 
7242040585eSniklas       if (*buf > 127 || (c2 = asc2bin[*buf]) == 255)
7252040585eSniklas 	return 0;
7262040585eSniklas       buf++;
7272040585eSniklas 
7282040585eSniklas       if (*buf == '=')
7292040585eSniklas 	{
7302040585eSniklas 	  c3 = c4 = 0;
7312040585eSniklas 	  c++;
7322040585eSniklas 
7332040585eSniklas 	  /* Check last four bit */
7342040585eSniklas 	  if (c2 & 0xF)
7352040585eSniklas 	    return 0;
7362040585eSniklas 
7372040585eSniklas 	  if (!strcmp (buf, "=="))
7382040585eSniklas 	    buf++;
7392040585eSniklas 	  else
7402040585eSniklas 	    return 0;
7412040585eSniklas 	}
7422040585eSniklas       else if (*buf > 127 || (c3 = asc2bin[*buf]) == 255)
7432040585eSniklas 	return 0;
7442040585eSniklas       else
7452040585eSniklas 	{
7462040585eSniklas 	  if (*++buf == '=')
7472040585eSniklas 	    {
7482040585eSniklas 	      c4 = 0;
7492040585eSniklas 	      c += 2;
7502040585eSniklas 
7512040585eSniklas 	      /* Check last two bit */
7522040585eSniklas 	      if (c3 & 3)
7532040585eSniklas 		return 0;
7542040585eSniklas 
7552040585eSniklas 	      if (strcmp (buf, "="))
7562040585eSniklas 		return 0;
7572040585eSniklas 
7582040585eSniklas 	    }
7592040585eSniklas 	  else if (*buf > 127 || (c4 = asc2bin[*buf]) == 255)
7602040585eSniklas 	      return 0;
7612040585eSniklas 	  else
7622040585eSniklas 	      c += 3;
7632040585eSniklas 	}
7642040585eSniklas 
7652040585eSniklas       buf++;
7662040585eSniklas       *out++ = (c1 << 2) | (c2 >> 4);
7672040585eSniklas       *out++ = (c2 << 4) | (c3 >> 2);
7682040585eSniklas       *out++ = (c3 << 6) | c4;
7692040585eSniklas     }
7702040585eSniklas 
7712040585eSniklas   *len = c;
7722040585eSniklas   return 1;
7732040585eSniklas 
7742040585eSniklas }
7752040585eSniklas 
7762040585eSniklas /* Read a line from a stream to the buffer.  */
7772040585eSniklas int
7782040585eSniklas conf_get_line (FILE *stream, char *buf, u_int32_t len)
7792040585eSniklas {
7802040585eSniklas   char c;
7812040585eSniklas 
7822040585eSniklas   while (len-- > 1)
7832040585eSniklas     {
7842040585eSniklas       c = fgetc (stream);
7852040585eSniklas       if (c == '\n')
7862040585eSniklas 	{
7872040585eSniklas 	  *buf = 0;
7882040585eSniklas 	  return 1;
7892040585eSniklas 	}
7902040585eSniklas       else if (c == EOF)
7912040585eSniklas 	break;
7922040585eSniklas 
7932040585eSniklas       *buf++ = c;
7942040585eSniklas     }
7952040585eSniklas 
7962040585eSniklas   *buf = 0;
7972040585eSniklas   return 0;
7982040585eSniklas }
7992040585eSniklas 
8002040585eSniklas void
8012040585eSniklas conf_free_list (struct conf_list *list)
8022040585eSniklas {
803a9753648Sniklas   struct conf_list_node *node = TAILQ_FIRST (&list->fields);
804a9753648Sniklas 
805a9753648Sniklas   while (node)
806a9753648Sniklas     {
807a9753648Sniklas       TAILQ_REMOVE (&list->fields, node, link);
808a9753648Sniklas       if (node->field)
809a9753648Sniklas 	free (node->field);
810a9753648Sniklas       free (node);
811a9753648Sniklas       node = TAILQ_FIRST (&list->fields);
812a9753648Sniklas     }
8132040585eSniklas   free (list);
8142040585eSniklas }
815f8f1e192Sniklas 
816f8f1e192Sniklas int
817f8f1e192Sniklas conf_begin (void)
818f8f1e192Sniklas {
819f8f1e192Sniklas   static int seq = 0;
820f8f1e192Sniklas 
821f8f1e192Sniklas   return ++seq;
822f8f1e192Sniklas }
823f8f1e192Sniklas 
824f8f1e192Sniklas static struct conf_trans *
825f8f1e192Sniklas conf_trans_node (int transaction, enum conf_op op)
826f8f1e192Sniklas {
827f8f1e192Sniklas   struct conf_trans *node;
828f8f1e192Sniklas 
829f8f1e192Sniklas   node = calloc (1, sizeof *node);
830f8f1e192Sniklas   if (!node)
831f8f1e192Sniklas     {
832f8f1e192Sniklas       log_error ("conf_trans_node: calloc (1, %d) failed", sizeof *node);
833f8f1e192Sniklas       return 0;
834f8f1e192Sniklas     }
835f8f1e192Sniklas   node->trans = transaction;
836f8f1e192Sniklas   node->op = op;
837f8f1e192Sniklas   TAILQ_INSERT_TAIL (&conf_trans_queue, node, link);
838f8f1e192Sniklas   return node;
839f8f1e192Sniklas }
840f8f1e192Sniklas 
841f8f1e192Sniklas /* Queue a set operation.  */
842f8f1e192Sniklas int
843510d8b0cSniklas conf_set (int transaction, char *section, char *tag, char *value, int override,
844510d8b0cSniklas 	  int is_default)
845f8f1e192Sniklas {
846f8f1e192Sniklas   struct conf_trans *node;
847f8f1e192Sniklas 
848f8f1e192Sniklas   node = conf_trans_node (transaction, CONF_SET);
849f8f1e192Sniklas   if (!node)
850f8f1e192Sniklas     return 1;
851f8f1e192Sniklas   node->section = strdup (section);
852f8f1e192Sniklas   if (!node->section)
853f8f1e192Sniklas     {
854f8f1e192Sniklas       log_error ("conf_set: strdup (\"%s\") failed", section);
855f8f1e192Sniklas       goto fail;
856f8f1e192Sniklas     }
857f8f1e192Sniklas   node->tag = strdup (tag);
858f8f1e192Sniklas   if (!node->tag)
859f8f1e192Sniklas     {
860f8f1e192Sniklas       log_error ("conf_set: strdup (\"%s\") failed", tag);
861f8f1e192Sniklas       goto fail;
862f8f1e192Sniklas     }
863f8f1e192Sniklas   node->value = strdup (value);
864f8f1e192Sniklas   if (!node->value)
865f8f1e192Sniklas     {
866f8f1e192Sniklas       log_error ("conf_set: strdup (\"%s\") failed", value);
867f8f1e192Sniklas       goto fail;
868f8f1e192Sniklas     }
869f8f1e192Sniklas   node->override = override;
870510d8b0cSniklas   node->is_default = is_default;
871f8f1e192Sniklas   return 0;
872f8f1e192Sniklas 
873f8f1e192Sniklas  fail:
874f8f1e192Sniklas   if (node->tag)
875f8f1e192Sniklas     free (node->tag);
876f8f1e192Sniklas   if (node->section)
877f8f1e192Sniklas     free (node->section);
878f8f1e192Sniklas   if (node)
879f8f1e192Sniklas     free (node);
880f8f1e192Sniklas   return 1;
881f8f1e192Sniklas }
882f8f1e192Sniklas 
883f8f1e192Sniklas /* Queue a remove operation.  */
884f8f1e192Sniklas int
885f8f1e192Sniklas conf_remove (int transaction, char *section, char *tag)
886f8f1e192Sniklas {
887f8f1e192Sniklas   struct conf_trans *node;
888f8f1e192Sniklas 
889f8f1e192Sniklas   node = conf_trans_node (transaction, CONF_REMOVE);
890f8f1e192Sniklas   if (!node)
891f8f1e192Sniklas     goto fail;
892f8f1e192Sniklas   node->section = strdup (section);
893f8f1e192Sniklas   if (!node->section)
894f8f1e192Sniklas     {
895f8f1e192Sniklas       log_error ("conf_remove: strdup (\"%s\") failed", section);
896f8f1e192Sniklas       goto fail;
897f8f1e192Sniklas     }
898f8f1e192Sniklas   node->tag = strdup (tag);
899f8f1e192Sniklas   if (!node->tag)
900f8f1e192Sniklas     {
901f8f1e192Sniklas       log_error ("conf_remove: strdup (\"%s\") failed", tag);
902f8f1e192Sniklas       goto fail;
903f8f1e192Sniklas     }
904f8f1e192Sniklas   return 0;
905f8f1e192Sniklas 
906f8f1e192Sniklas  fail:
907f8f1e192Sniklas   if (node->section)
908f8f1e192Sniklas     free (node->section);
909f8f1e192Sniklas   if (node)
910f8f1e192Sniklas     free (node);
911f8f1e192Sniklas   return 1;
912f8f1e192Sniklas }
913f8f1e192Sniklas 
914f8f1e192Sniklas /* Queue a remove section operation.  */
915f8f1e192Sniklas int
916f8f1e192Sniklas conf_remove_section (int transaction, char *section)
917f8f1e192Sniklas {
918f8f1e192Sniklas   struct conf_trans *node;
919f8f1e192Sniklas 
920f8f1e192Sniklas   node = conf_trans_node (transaction, CONF_REMOVE_SECTION);
921f8f1e192Sniklas   if (!node)
922f8f1e192Sniklas     goto fail;
923f8f1e192Sniklas   node->section = strdup (section);
924f8f1e192Sniklas   if (!node->section)
925f8f1e192Sniklas     {
926f8f1e192Sniklas       log_error ("conf_remove_section: strdup (\"%s\") failed", section);
927f8f1e192Sniklas       goto fail;
928f8f1e192Sniklas     }
929f8f1e192Sniklas   return 0;
930f8f1e192Sniklas 
931f8f1e192Sniklas  fail:
932f8f1e192Sniklas   if (node)
933f8f1e192Sniklas     free (node);
934f8f1e192Sniklas   return 1;
935f8f1e192Sniklas }
936f8f1e192Sniklas 
937f8f1e192Sniklas /* Execute all queued operations for this transaction.  Cleanup.  */
938f8f1e192Sniklas int
939f8f1e192Sniklas conf_end (int transaction, int commit)
940f8f1e192Sniklas {
941f8f1e192Sniklas   struct conf_trans *node, *next;
942f8f1e192Sniklas 
943f8f1e192Sniklas   for (node = TAILQ_FIRST (&conf_trans_queue); node; node = next)
944f8f1e192Sniklas     {
945f8f1e192Sniklas       next = TAILQ_NEXT (node, link);
946f8f1e192Sniklas       if (node->trans == transaction)
947f8f1e192Sniklas 	{
948f8f1e192Sniklas 	  if (commit)
949f8f1e192Sniklas 	    switch (node->op)
950f8f1e192Sniklas 	      {
951f8f1e192Sniklas 	      case CONF_SET:
952f8f1e192Sniklas 		conf_set_now (node->section, node->tag, node->value,
953510d8b0cSniklas 			      node->override, node->is_default);
954f8f1e192Sniklas 		break;
955f8f1e192Sniklas 	      case CONF_REMOVE:
956f8f1e192Sniklas 		conf_remove_now (node->section, node->tag);
957f8f1e192Sniklas 		break;
958f8f1e192Sniklas 	      case CONF_REMOVE_SECTION:
959f8f1e192Sniklas 		conf_remove_section_now (node->section);
960f8f1e192Sniklas 		break;
961f8f1e192Sniklas 	      default:
962f8f1e192Sniklas 		log_print ("conf_end: unknown operation: %d", node->op);
963f8f1e192Sniklas 	      }
964f8f1e192Sniklas 	  TAILQ_REMOVE (&conf_trans_queue, node, link);
965f8f1e192Sniklas 	  free (node);
966f8f1e192Sniklas 	}
967f8f1e192Sniklas     }
968f8f1e192Sniklas   return 0;
969f8f1e192Sniklas }
970510d8b0cSniklas 
971510d8b0cSniklas /* Dump running configuration upon SIGUSR1. */
972510d8b0cSniklas /* XXX Configuration is "stored in reverse order", so reverse it. */
973510d8b0cSniklas struct dumper {
974510d8b0cSniklas   char *s, *v;
975510d8b0cSniklas   struct dumper *next;
976510d8b0cSniklas };
977510d8b0cSniklas 
978510d8b0cSniklas static void
979510d8b0cSniklas conf_report_dump (struct dumper *node)
980510d8b0cSniklas {
981510d8b0cSniklas   /* Recursive, cleanup when we're done. */
982510d8b0cSniklas 
983510d8b0cSniklas   if (node->next)
984510d8b0cSniklas     conf_report_dump (node->next);
985510d8b0cSniklas 
986510d8b0cSniklas   if (node->v)
987510d8b0cSniklas     LOG_DBG ((LOG_REPORT, 0, "%s=\t%s", node->s, node->v));
988510d8b0cSniklas   else
989510d8b0cSniklas     {
990510d8b0cSniklas       LOG_DBG ((LOG_REPORT, 0, "%s", node->s));
991510d8b0cSniklas       if (strlen (node->s) > 0)
992510d8b0cSniklas 	free (node->s);
993510d8b0cSniklas     }
994510d8b0cSniklas 
995510d8b0cSniklas   free (node);
996510d8b0cSniklas }
997510d8b0cSniklas 
998510d8b0cSniklas void
999510d8b0cSniklas conf_report (void)
1000510d8b0cSniklas {
1001510d8b0cSniklas   struct conf_binding *cb, *last = NULL;
1002510d8b0cSniklas   int i;
1003510d8b0cSniklas   char *current_section = (char *)0;
1004510d8b0cSniklas   struct dumper *dumper, *dnode;
1005510d8b0cSniklas 
1006*592a196eSniklas   dumper = dnode = (struct dumper *)calloc (1, sizeof *dumper);
1007510d8b0cSniklas   if (!dumper)
1008510d8b0cSniklas     goto mem_fail;
1009510d8b0cSniklas 
1010510d8b0cSniklas   LOG_DBG ((LOG_REPORT, 0, "conf_report: dumping running configuration"));
1011510d8b0cSniklas 
1012510d8b0cSniklas   for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
1013510d8b0cSniklas     for (cb = LIST_FIRST (&conf_bindings[i]); cb;
1014510d8b0cSniklas 	 cb = LIST_NEXT (cb, link))
1015510d8b0cSniklas       {
1016510d8b0cSniklas 	if (!cb->is_default)
1017510d8b0cSniklas 	  {
1018510d8b0cSniklas 	    /* Dump this entry */
1019510d8b0cSniklas 	    if (!current_section || strcmp (cb->section, current_section))
1020510d8b0cSniklas 	      {
1021510d8b0cSniklas 		if (current_section)
1022510d8b0cSniklas 		  {
1023510d8b0cSniklas 		    dnode->s = malloc (strlen (current_section) + 3);
1024510d8b0cSniklas 		    if (!dnode->s)
1025510d8b0cSniklas 		      goto mem_fail;
1026510d8b0cSniklas 
1027510d8b0cSniklas 		    sprintf (dnode->s, "[%s]", current_section);
1028*592a196eSniklas 		    dnode->next
1029*592a196eSniklas 		      = (struct dumper *)calloc (1, sizeof (struct dumper));
1030510d8b0cSniklas 		    dnode = dnode->next;
1031510d8b0cSniklas 		    if (!dnode)
1032510d8b0cSniklas 		      goto mem_fail;
1033510d8b0cSniklas 
1034510d8b0cSniklas 		    dnode->s = "";
1035*592a196eSniklas 		    dnode->next
1036*592a196eSniklas 		      = (struct dumper *)calloc (1, sizeof (struct dumper));
1037510d8b0cSniklas 		    dnode = dnode->next;
1038510d8b0cSniklas 		    if (!dnode)
1039510d8b0cSniklas 		      goto mem_fail;
1040510d8b0cSniklas 		  }
1041510d8b0cSniklas 		current_section = cb->section;
1042510d8b0cSniklas 	      }
1043510d8b0cSniklas 	    dnode->s = cb->tag;
1044510d8b0cSniklas 	    dnode->v = cb->value;
1045510d8b0cSniklas 	    dnode->next = (struct dumper *)calloc (1, sizeof (struct dumper));
1046510d8b0cSniklas 	    dnode = dnode->next;
1047510d8b0cSniklas 	    if (!dnode)
1048510d8b0cSniklas 	      goto mem_fail;
1049510d8b0cSniklas 	    last = cb;
1050510d8b0cSniklas 	  }
1051510d8b0cSniklas       }
1052510d8b0cSniklas 
1053510d8b0cSniklas   if (last)
1054510d8b0cSniklas     {
1055510d8b0cSniklas       dnode->s = malloc (strlen (last->section) + 3);
1056510d8b0cSniklas       if (!dnode->s)
1057510d8b0cSniklas 	goto mem_fail;
1058510d8b0cSniklas       sprintf (dnode->s, "[%s]", last->section);
1059510d8b0cSniklas     }
1060510d8b0cSniklas 
1061510d8b0cSniklas   conf_report_dump (dumper);
1062510d8b0cSniklas 
1063510d8b0cSniklas   return;
1064510d8b0cSniklas 
1065510d8b0cSniklas  mem_fail:
1066510d8b0cSniklas   LOG_DBG ((LOG_REPORT, 0, "conf_report: memory allocation failure."));
1067510d8b0cSniklas   while ((dnode = dumper) != NULL)
1068510d8b0cSniklas     {
1069510d8b0cSniklas       dumper = dumper->next;
1070510d8b0cSniklas       if (dnode->s)
1071510d8b0cSniklas 	free (dnode->s);
1072510d8b0cSniklas       free (dnode);
1073510d8b0cSniklas     }
1074510d8b0cSniklas   return;
1075510d8b0cSniklas }
1076