xref: /openbsd/sbin/isakmpd/conf.c (revision f8f1e192)
1*f8f1e192Sniklas /*	$OpenBSD: conf.c,v 1.10 1999/08/05 22:41:08 niklas Exp $	*/
2*f8f1e192Sniklas /*	$EOM: conf.c,v 1.19 1999/08/05 14:57:59 niklas Exp $	*/
32040585eSniklas 
42040585eSniklas /*
5a9753648Sniklas  * Copyright (c) 1998, 1999 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 
54*f8f1e192Sniklas struct conf_trans {
55*f8f1e192Sniklas   TAILQ_ENTRY (conf_trans) link;
56*f8f1e192Sniklas   int trans;
57*f8f1e192Sniklas   enum conf_op { CONF_SET, CONF_REMOVE, CONF_REMOVE_SECTION } op;
58*f8f1e192Sniklas   char *section;
59*f8f1e192Sniklas   char *tag;
60*f8f1e192Sniklas   char *value;
61*f8f1e192Sniklas   int override;
62*f8f1e192Sniklas };
63*f8f1e192Sniklas 
64*f8f1e192Sniklas TAILQ_HEAD (conf_trans_head, conf_trans) conf_trans_queue;
65*f8f1e192Sniklas 
662040585eSniklas /*
672040585eSniklas  * Radix-64 Encoding.
682040585eSniklas  */
692040585eSniklas const u_int8_t bin2asc[] =
702040585eSniklas         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
712040585eSniklas 
722040585eSniklas const u_int8_t asc2bin[] =
732040585eSniklas {
742040585eSniklas   255, 255, 255, 255, 255, 255, 255, 255,
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,  62, 255, 255, 255,  63,
802040585eSniklas    52,  53,  54,  55,  56,  57,  58,  59,
812040585eSniklas    60,  61, 255, 255, 255, 255, 255, 255,
822040585eSniklas   255,   0,   1,   2,   3,   4,   5,   6,
832040585eSniklas     7,   8,   9,  10,  11,  12,  13,  14,
842040585eSniklas    15,  16,  17,  18,  19,  20,  21,  22,
852040585eSniklas    23,  24,  25, 255, 255, 255, 255, 255,
862040585eSniklas   255,  26,  27,  28,  29,  30,  31,  32,
872040585eSniklas    33,  34,  35,  36,  37,  38,  39,  40,
882040585eSniklas    41,  42,  43,  44,  45,  46,  47,  48,
892040585eSniklas    49,  50,  51, 255, 255, 255, 255, 255
902040585eSniklas };
912040585eSniklas 
922040585eSniklas struct conf_binding {
932040585eSniklas   LIST_ENTRY (conf_binding) link;
942040585eSniklas   char *section;
952040585eSniklas   char *tag;
962040585eSniklas   char *value;
972040585eSniklas };
982040585eSniklas 
992040585eSniklas char *conf_path = CONFIG_FILE;
100*f8f1e192Sniklas LIST_HEAD (conf_bindings, conf_binding) conf_bindings[256];
1012040585eSniklas 
1022040585eSniklas static char *conf_addr;
1032040585eSniklas 
104*f8f1e192Sniklas static __inline__ u_int8_t
105*f8f1e192Sniklas conf_hash (char *s)
106*f8f1e192Sniklas {
107*f8f1e192Sniklas   u_int8_t hash = 0;
108*f8f1e192Sniklas 
109*f8f1e192Sniklas   while (*s)
110*f8f1e192Sniklas     {
111*f8f1e192Sniklas       hash = ((hash << 1) | (hash >> 7)) ^ tolower (*s);
112*f8f1e192Sniklas       s++;
113*f8f1e192Sniklas     }
114*f8f1e192Sniklas   return hash;
115*f8f1e192Sniklas }
116*f8f1e192Sniklas 
117*f8f1e192Sniklas /*
118*f8f1e192Sniklas  * Insert a tag-value combination from LINE (the equal sign is at POS)
119*f8f1e192Sniklas  */
120*f8f1e192Sniklas static int
121*f8f1e192Sniklas conf_remove_now (char *section, char *tag)
122*f8f1e192Sniklas {
123*f8f1e192Sniklas   struct conf_binding *cb, *next;
124*f8f1e192Sniklas 
125*f8f1e192Sniklas   for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb; cb = next)
126*f8f1e192Sniklas     {
127*f8f1e192Sniklas       next = LIST_NEXT (cb, link);
128*f8f1e192Sniklas       if (strcasecmp (cb->section, section) == 0
129*f8f1e192Sniklas 	  && strcasecmp (cb->tag, tag) == 0)
130*f8f1e192Sniklas 	{
131*f8f1e192Sniklas 	  LIST_REMOVE (cb, link);
132*f8f1e192Sniklas 	  log_debug (LOG_MISC, 70, "[%s]:%s->%s removed", section, tag,
133*f8f1e192Sniklas 		     cb->value);
134*f8f1e192Sniklas 	  free (cb->section);
135*f8f1e192Sniklas 	  free (cb->tag);
136*f8f1e192Sniklas 	  free (cb->value);
137*f8f1e192Sniklas 	  free (cb);
138*f8f1e192Sniklas 	  return 0;
139*f8f1e192Sniklas 	}
140*f8f1e192Sniklas     }
141*f8f1e192Sniklas   return 1;
142*f8f1e192Sniklas }
143*f8f1e192Sniklas 
144*f8f1e192Sniklas static int
145*f8f1e192Sniklas conf_remove_section_now (char *section)
146*f8f1e192Sniklas {
147*f8f1e192Sniklas   struct conf_binding *cb, *next;
148*f8f1e192Sniklas   int unseen = 1;
149*f8f1e192Sniklas 
150*f8f1e192Sniklas   for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb; cb = next)
151*f8f1e192Sniklas     {
152*f8f1e192Sniklas       next = LIST_NEXT (cb, link);
153*f8f1e192Sniklas       if (strcasecmp (cb->section, section) == 0)
154*f8f1e192Sniklas 	{
155*f8f1e192Sniklas 	  unseen = 0;
156*f8f1e192Sniklas 	  LIST_REMOVE (cb, link);
157*f8f1e192Sniklas 	  log_debug (LOG_MISC, 70, "[%s]:%s->%s removed", section, cb->tag,
158*f8f1e192Sniklas 		     cb->value);
159*f8f1e192Sniklas 	  free (cb->section);
160*f8f1e192Sniklas 	  free (cb->tag);
161*f8f1e192Sniklas 	  free (cb->value);
162*f8f1e192Sniklas 	  free (cb);
163*f8f1e192Sniklas 	}
164*f8f1e192Sniklas     }
165*f8f1e192Sniklas   return unseen;
166*f8f1e192Sniklas }
167*f8f1e192Sniklas 
1682040585eSniklas /*
1692040585eSniklas  * Insert a tag-value combination from LINE (the equal sign is at POS)
1702040585eSniklas  * into SECTION of our configuration database.
1712040585eSniklas  */
172*f8f1e192Sniklas static int
173*f8f1e192Sniklas conf_set_now (char *section, char *tag, char *value, int override)
1742040585eSniklas {
175*f8f1e192Sniklas   struct conf_binding *node = 0;
1762040585eSniklas 
177*f8f1e192Sniklas   if (override)
178*f8f1e192Sniklas     conf_remove_now (section, tag);
179*f8f1e192Sniklas   else if (conf_get_str (section, tag))
1802040585eSniklas     {
1812040585eSniklas       log_print ("conf_set: duplicate tag [%s]:%s, ignoring...\n", section,
182*f8f1e192Sniklas 		 tag);
183*f8f1e192Sniklas       return 1;
1842040585eSniklas     }
185*f8f1e192Sniklas 
186*f8f1e192Sniklas   node = calloc (1, sizeof *node);
187*f8f1e192Sniklas   if (!node)
188*f8f1e192Sniklas     {
189*f8f1e192Sniklas       log_error ("conf_set: calloc (1, %d) failed", sizeof *node);
190*f8f1e192Sniklas       return 1;
191*f8f1e192Sniklas     }
192*f8f1e192Sniklas   node->section = section;
193*f8f1e192Sniklas   node->tag = tag;
194*f8f1e192Sniklas   node->value = value;
195*f8f1e192Sniklas 
196*f8f1e192Sniklas   LIST_INSERT_HEAD (&conf_bindings[conf_hash (section)], node, link);
197*f8f1e192Sniklas   log_debug (LOG_MISC, 70, "[%s]:%s->%s", node->section, node->tag,
1982040585eSniklas 	     node->value);
199*f8f1e192Sniklas   return 0;
2002040585eSniklas }
2012040585eSniklas 
2022040585eSniklas /*
2032040585eSniklas  * Parse the line LINE of SZ bytes.  Skip Comments, recognize section
2042040585eSniklas  * headers and feed tag-value pairs into our configuration database.
2052040585eSniklas  */
2062040585eSniklas static void
207*f8f1e192Sniklas conf_parse_line (int trans, char *line, size_t sz)
2082040585eSniklas {
2092040585eSniklas   char *cp = line;
2102040585eSniklas   int i;
2112040585eSniklas   static char *section = 0;
2122040585eSniklas   static int ln = 0;
2132040585eSniklas 
2142040585eSniklas   ln++;
2152040585eSniklas 
2162040585eSniklas   /* Lines starting with '#' or ';' are comments.  */
2172040585eSniklas   if (*line == '#' || *line == ';')
2182040585eSniklas     return;
2192040585eSniklas 
2202040585eSniklas   /* '[section]' parsing...  */
2212040585eSniklas   if (*line == '[')
2222040585eSniklas     {
2232040585eSniklas       for (i = 1; i < sz; i++)
2242040585eSniklas 	if (line[i] == ']')
2252040585eSniklas 	  break;
2262040585eSniklas       if (i == sz)
2272040585eSniklas 	{
2282040585eSniklas 	  log_print ("conf_parse_line: %d:"
2292040585eSniklas 		     "non-matched ']', ignoring until next section", ln);
2302040585eSniklas 	  section = 0;
2312040585eSniklas 	  return;
2322040585eSniklas 	}
2332040585eSniklas       section = malloc (i);
2342040585eSniklas       strncpy (section, line + 1, i - 1);
2352040585eSniklas       section[i - 1] = '\0';
2362040585eSniklas       return;
2372040585eSniklas     }
2382040585eSniklas 
2392040585eSniklas   /* Deal with assignments.  */
2402040585eSniklas   for (i = 0; i < sz; i++)
2412040585eSniklas     if (cp[i] == '=')
2422040585eSniklas       {
2432040585eSniklas 	/* If no section, we are ignoring the lines.  */
2442040585eSniklas 	if (!section)
2452040585eSniklas 	  {
2462040585eSniklas 	    log_print ("conf_parse_line: %d: ignoring line due to no section",
2472040585eSniklas 		       ln);
2482040585eSniklas 	    return;
2492040585eSniklas 	  }
250*f8f1e192Sniklas 	line[strcspn (line, " \t=")] = '\0';
251*f8f1e192Sniklas 	/* XXX Perhaps should we not ignore errors?  */
252*f8f1e192Sniklas 	conf_set (trans, section, line,
253*f8f1e192Sniklas 		  line + i + 1 + strspn (line + i + 1, " \t"), 0);
2542040585eSniklas 	return;
2552040585eSniklas       }
2562040585eSniklas 
2572040585eSniklas   /* Other non-empty lines are wierd.  */
2582040585eSniklas   i = strspn (line, " \t");
2592040585eSniklas   if (line[i])
2602040585eSniklas     log_print ("conf_parse_line: %d: syntax error", ln);
2612040585eSniklas 
2622040585eSniklas   return;
2632040585eSniklas }
2642040585eSniklas 
2652040585eSniklas /* Parse the mapped configuration file.  */
2662040585eSniklas static void
267*f8f1e192Sniklas conf_parse (int trans, char *buf, size_t sz)
2682040585eSniklas {
269*f8f1e192Sniklas   char *cp = buf;
270*f8f1e192Sniklas   char *bufend = buf + sz;
2712040585eSniklas   char *line;
2722040585eSniklas 
2732040585eSniklas   line = cp;
274*f8f1e192Sniklas   while (cp < bufend)
2752040585eSniklas     {
2762040585eSniklas       if (*cp == '\n')
2772040585eSniklas 	{
2782040585eSniklas 	  /* Check for escaped newlines.  */
279*f8f1e192Sniklas 	  if (cp > buf && *(cp - 1) == '\\')
2802040585eSniklas 	    *(cp - 1) = *cp = ' ';
2812040585eSniklas 	  else
2822040585eSniklas 	    {
2832040585eSniklas 	      *cp = '\0';
284*f8f1e192Sniklas 	      conf_parse_line (trans, line, cp - line);
2852040585eSniklas 	      line = cp + 1;
2862040585eSniklas 	    }
2872040585eSniklas 	}
2882040585eSniklas       cp++;
2892040585eSniklas     }
2902040585eSniklas   if (cp != line)
2912040585eSniklas     log_print ("conf_parse: last line non-terminated, ignored.");
2922040585eSniklas }
2932040585eSniklas 
2942040585eSniklas void
2952040585eSniklas conf_init (void)
2962040585eSniklas {
297*f8f1e192Sniklas   int i;
2982040585eSniklas 
299*f8f1e192Sniklas   for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
300*f8f1e192Sniklas     LIST_INIT (&conf_bindings[i]);
301*f8f1e192Sniklas   TAILQ_INIT (&conf_trans_queue);
302*f8f1e192Sniklas   conf_reinit ();
3032040585eSniklas }
3042040585eSniklas 
305*f8f1e192Sniklas /* Open the config file and map it into our address space, then parse it.  */
306*f8f1e192Sniklas void
307*f8f1e192Sniklas conf_reinit (void)
308*f8f1e192Sniklas {
309*f8f1e192Sniklas   struct conf_binding *cb = 0;
310*f8f1e192Sniklas   int fd, i, trans;
311*f8f1e192Sniklas   struct stat st;
312*f8f1e192Sniklas   off_t sz;
313*f8f1e192Sniklas   char *new_conf_addr = 0;
314*f8f1e192Sniklas 
3152040585eSniklas   fd = open (conf_path, O_RDONLY);
3162040585eSniklas   if (fd == -1)
317*f8f1e192Sniklas     {
318*f8f1e192Sniklas       log_error ("open (\"%s\", O_RDONLY) failed", conf_path);
319*f8f1e192Sniklas       return;
320*f8f1e192Sniklas     }
3212040585eSniklas   if (fstat (fd, &st) == -1)
322*f8f1e192Sniklas     {
323*f8f1e192Sniklas       log_error ("fstat (%d, &st) failed", fd);
324*f8f1e192Sniklas       goto fail;
325*f8f1e192Sniklas     }
326*f8f1e192Sniklas   sz = st.st_size;
327*f8f1e192Sniklas   new_conf_addr = malloc (sz);
328*f8f1e192Sniklas   if (!new_conf_addr)
329*f8f1e192Sniklas     {
330*f8f1e192Sniklas       log_error ("malloc (%d) failed", sz);
331*f8f1e192Sniklas       goto fail;
332*f8f1e192Sniklas     }
3332040585eSniklas   /* XXX I assume short reads won't happen here.  */
334*f8f1e192Sniklas   if (read (fd, new_conf_addr, sz) != sz)
335*f8f1e192Sniklas     {
336*f8f1e192Sniklas       log_error ("read (%d, %p, %d) failed", fd, new_conf_addr, sz);
337*f8f1e192Sniklas       goto fail;
338*f8f1e192Sniklas     }
3392040585eSniklas   close (fd);
3402040585eSniklas 
341*f8f1e192Sniklas   trans = conf_begin ();
342*f8f1e192Sniklas 
343*f8f1e192Sniklas   /* XXX Should we not care about errors and rollback?  */
344*f8f1e192Sniklas   conf_parse (trans, new_conf_addr, sz);
345*f8f1e192Sniklas 
346*f8f1e192Sniklas   /* Free potential existing configuration.  */
347*f8f1e192Sniklas   if (conf_addr)
348*f8f1e192Sniklas     {
349*f8f1e192Sniklas       for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
350*f8f1e192Sniklas 	for (cb = LIST_FIRST (&conf_bindings[i]); cb;
351*f8f1e192Sniklas 	     cb = LIST_FIRST (&conf_bindings[i]))
352*f8f1e192Sniklas 	  conf_remove_now (cb->section, cb->tag);
353*f8f1e192Sniklas       free (conf_addr);
354*f8f1e192Sniklas     }
355*f8f1e192Sniklas 
356*f8f1e192Sniklas   conf_end (trans, 1);
357*f8f1e192Sniklas   conf_addr = new_conf_addr;
358*f8f1e192Sniklas   return;
359*f8f1e192Sniklas 
360*f8f1e192Sniklas  fail:
361*f8f1e192Sniklas   if (new_conf_addr)
362*f8f1e192Sniklas     free (new_conf_addr);
363*f8f1e192Sniklas   close (fd);
3642040585eSniklas }
3652040585eSniklas 
366a2d30fd1Sniklas /*
367a2d30fd1Sniklas  * Return the numeric value denoted by TAG in section SECTION or DEF
368a2d30fd1Sniklas  * if that tag does not exist.
369a2d30fd1Sniklas  */
3702040585eSniklas int
371a2d30fd1Sniklas conf_get_num (char *section, char *tag, int def)
3722040585eSniklas {
3732040585eSniklas   char *value = conf_get_str (section, tag);
3742040585eSniklas 
3752040585eSniklas   if (value)
3762040585eSniklas       return atoi (value);
377a2d30fd1Sniklas   return def;
3782040585eSniklas }
3792040585eSniklas 
38082d8fe06Sniklas /* Validate X according to the range denoted by TAG in section SECTION.  */
38182d8fe06Sniklas int
38282d8fe06Sniklas conf_match_num (char *section, char *tag, int x)
38382d8fe06Sniklas {
38482d8fe06Sniklas   char *value = conf_get_str (section, tag);
38582d8fe06Sniklas   int val, min, max, n;
38682d8fe06Sniklas 
38782d8fe06Sniklas   if (!value)
38882d8fe06Sniklas     return 0;
38982d8fe06Sniklas   n = sscanf (value, "%d,%d:%d", &val, &min, &max);
39082d8fe06Sniklas   switch (n)
39182d8fe06Sniklas     {
39282d8fe06Sniklas     case 1:
39382d8fe06Sniklas       log_debug (LOG_MISC, 90, "conf_match_num: %s:%s %d==%d?", section, tag,
39482d8fe06Sniklas 		 val, x);
39582d8fe06Sniklas       return x == val;
39682d8fe06Sniklas     case 3:
39782d8fe06Sniklas       log_debug (LOG_MISC, 90, "conf_match_num: %s:%s %d<=%d<=%d?", section,
39882d8fe06Sniklas 		 tag, min, x, max);
39982d8fe06Sniklas       return min <= x && max >= x;
40082d8fe06Sniklas     default:
40182d8fe06Sniklas       log_error ("conf_match_num: section %s tag %s: invalid number spec %s",
40282d8fe06Sniklas 		 section, tag, value);
40382d8fe06Sniklas     }
40482d8fe06Sniklas   return 0;
40582d8fe06Sniklas }
40682d8fe06Sniklas 
4072040585eSniklas /* Return the string value denoted by TAG in section SECTION.  */
4082040585eSniklas char *
4092040585eSniklas conf_get_str (char *section, char *tag)
4102040585eSniklas {
4112040585eSniklas   struct conf_binding *cb;
4122040585eSniklas 
413*f8f1e192Sniklas   for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb;
414*f8f1e192Sniklas        cb = LIST_NEXT (cb, link))
4152040585eSniklas     if (strcasecmp (section, cb->section) == 0
4162040585eSniklas 	&& strcasecmp (tag, cb->tag) == 0)
4172040585eSniklas       {
418*f8f1e192Sniklas 	log_debug (LOG_MISC, 60, "conf_get_str: [%s]:%s->%s", section,
4192040585eSniklas 		   tag, cb->value);
4202040585eSniklas 	return cb->value;
4212040585eSniklas       }
4222040585eSniklas   log_debug (LOG_MISC, 60,
423*f8f1e192Sniklas 	     "conf_get_str: configuration value not found [%s]:%s", section,
4242040585eSniklas 	     tag);
4252040585eSniklas   return 0;
4262040585eSniklas }
4272040585eSniklas 
428a9753648Sniklas /*
429a9753648Sniklas  * Build a list of string values out of the comma separated value denoted by
430a9753648Sniklas  * TAG in SECTION.
431a9753648Sniklas  */
4322040585eSniklas struct conf_list *
4332040585eSniklas conf_get_list (char *section, char *tag)
4342040585eSniklas {
4352040585eSniklas   char *liststr = 0, *p, *field;
4362040585eSniklas   struct conf_list *list = 0;
4372040585eSniklas   struct conf_list_node *node;
4382040585eSniklas 
4392040585eSniklas   list = malloc (sizeof *list);
4402040585eSniklas   if (!list)
4412040585eSniklas     goto cleanup;
4422040585eSniklas   TAILQ_INIT (&list->fields);
4432040585eSniklas   list->cnt = 0;
4442040585eSniklas   liststr = conf_get_str (section, tag);
4452040585eSniklas   if (!liststr)
4462040585eSniklas     goto cleanup;
4472040585eSniklas   liststr = strdup (liststr);
4482040585eSniklas   if (!liststr)
4492040585eSniklas     goto cleanup;
4502040585eSniklas   p = liststr;
4512040585eSniklas   while ((field = strsep (&p, ", \t")) != NULL)
4522040585eSniklas     {
4532040585eSniklas       if (*field == '\0')
4542040585eSniklas 	{
4552040585eSniklas 	  log_print ("conf_get_list: empty field, ignoring...");
4562040585eSniklas 	  continue;
4572040585eSniklas 	}
4582040585eSniklas       list->cnt++;
459a9753648Sniklas       node = calloc (1, sizeof *node);
4602040585eSniklas       if (!node)
4612040585eSniklas 	goto cleanup;
462a9753648Sniklas       node->field = strdup (field);
463a9753648Sniklas       if (!node->field)
464a9753648Sniklas 	goto cleanup;
4652040585eSniklas       TAILQ_INSERT_TAIL (&list->fields, node, link);
4662040585eSniklas     }
467a9753648Sniklas   free (liststr);
4682040585eSniklas   return list;
4692040585eSniklas 
4702040585eSniklas  cleanup:
4712040585eSniklas   if (list)
4722040585eSniklas     conf_free_list (list);
4732040585eSniklas   if (liststr)
4742040585eSniklas     free (liststr);
4752040585eSniklas   return 0;
4762040585eSniklas }
4772040585eSniklas 
47882d8fe06Sniklas struct conf_list *
47982d8fe06Sniklas conf_get_tag_list (char *section)
48082d8fe06Sniklas {
48182d8fe06Sniklas   struct conf_list *list = 0;
48282d8fe06Sniklas   struct conf_list_node *node;
48382d8fe06Sniklas   struct conf_binding *cb;
48482d8fe06Sniklas 
48582d8fe06Sniklas   list = malloc (sizeof *list);
48682d8fe06Sniklas   if (!list)
48782d8fe06Sniklas     goto cleanup;
48882d8fe06Sniklas   TAILQ_INIT (&list->fields);
48982d8fe06Sniklas   list->cnt = 0;
490*f8f1e192Sniklas   for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb;
491*f8f1e192Sniklas        cb = LIST_NEXT (cb, link))
49282d8fe06Sniklas     if (strcasecmp (section, cb->section) == 0)
49382d8fe06Sniklas       {
49482d8fe06Sniklas 	list->cnt++;
495a9753648Sniklas 	node = calloc (1, sizeof *node);
49682d8fe06Sniklas 	if (!node)
49782d8fe06Sniklas 	  goto cleanup;
498a9753648Sniklas 	node->field = strdup (cb->tag);
499a9753648Sniklas 	if (!node->field)
500a9753648Sniklas 	  goto cleanup;
50182d8fe06Sniklas 	TAILQ_INSERT_TAIL (&list->fields, node, link);
50282d8fe06Sniklas       }
50382d8fe06Sniklas   return list;
50482d8fe06Sniklas 
50582d8fe06Sniklas  cleanup:
50682d8fe06Sniklas   if (list)
50782d8fe06Sniklas     conf_free_list (list);
50882d8fe06Sniklas   return 0;
50982d8fe06Sniklas }
51082d8fe06Sniklas 
5112040585eSniklas /* Decode a PEM encoded buffer.  */
5122040585eSniklas int
5132040585eSniklas conf_decode_base64(u_int8_t *out, u_int32_t *len, u_char *buf)
5142040585eSniklas {
5152040585eSniklas   u_int32_t c = 0;
5162040585eSniklas   u_int8_t c1, c2, c3, c4;
5172040585eSniklas 
5182040585eSniklas   while (*buf)
5192040585eSniklas     {
5202040585eSniklas       if (*buf > 127 || (c1 = asc2bin[*buf]) == 255)
5212040585eSniklas 	return 0;
5222040585eSniklas       buf++;
5232040585eSniklas 
5242040585eSniklas       if (*buf > 127 || (c2 = asc2bin[*buf]) == 255)
5252040585eSniklas 	return 0;
5262040585eSniklas       buf++;
5272040585eSniklas 
5282040585eSniklas       if (*buf == '=')
5292040585eSniklas 	{
5302040585eSniklas 	  c3 = c4 = 0;
5312040585eSniklas 	  c++;
5322040585eSniklas 
5332040585eSniklas 	  /* Check last four bit */
5342040585eSniklas 	  if (c2 & 0xF)
5352040585eSniklas 	    return 0;
5362040585eSniklas 
5372040585eSniklas 	  if (!strcmp (buf, "=="))
5382040585eSniklas 	    buf++;
5392040585eSniklas 	  else
5402040585eSniklas 	    return 0;
5412040585eSniklas 	}
5422040585eSniklas       else if (*buf > 127 || (c3 = asc2bin[*buf]) == 255)
5432040585eSniklas 	return 0;
5442040585eSniklas       else
5452040585eSniklas 	{
5462040585eSniklas 	  if (*++buf == '=')
5472040585eSniklas 	    {
5482040585eSniklas 	      c4 = 0;
5492040585eSniklas 	      c += 2;
5502040585eSniklas 
5512040585eSniklas 	      /* Check last two bit */
5522040585eSniklas 	      if (c3 & 3)
5532040585eSniklas 		return 0;
5542040585eSniklas 
5552040585eSniklas 	      if (strcmp(buf, "="))
5562040585eSniklas 		return 0;
5572040585eSniklas 
5582040585eSniklas 	    }
5592040585eSniklas 	  else if (*buf > 127 || (c4 = asc2bin[*buf]) == 255)
5602040585eSniklas 	      return 0;
5612040585eSniklas 	  else
5622040585eSniklas 	      c += 3;
5632040585eSniklas 	}
5642040585eSniklas 
5652040585eSniklas       buf++;
5662040585eSniklas       *out++ = (c1 << 2) | (c2 >> 4);
5672040585eSniklas       *out++ = (c2 << 4) | (c3 >> 2);
5682040585eSniklas       *out++ = (c3 << 6) | c4;
5692040585eSniklas     }
5702040585eSniklas 
5712040585eSniklas   *len = c;
5722040585eSniklas   return 1;
5732040585eSniklas 
5742040585eSniklas }
5752040585eSniklas 
5762040585eSniklas /* Read a line from a stream to the buffer.  */
5772040585eSniklas int
5782040585eSniklas conf_get_line (FILE *stream, char *buf, u_int32_t len)
5792040585eSniklas {
5802040585eSniklas   char c;
5812040585eSniklas 
5822040585eSniklas   while (len-- > 1)
5832040585eSniklas     {
5842040585eSniklas       c = fgetc (stream);
5852040585eSniklas       if (c == '\n')
5862040585eSniklas 	{
5872040585eSniklas 	  *buf = 0;
5882040585eSniklas 	  return 1;
5892040585eSniklas 	}
5902040585eSniklas       else if (c == EOF)
5912040585eSniklas 	break;
5922040585eSniklas 
5932040585eSniklas       *buf++ = c;
5942040585eSniklas     }
5952040585eSniklas 
5962040585eSniklas   *buf = 0;
5972040585eSniklas   return 0;
5982040585eSniklas }
5992040585eSniklas 
6002040585eSniklas void
6012040585eSniklas conf_free_list (struct conf_list *list)
6022040585eSniklas {
603a9753648Sniklas   struct conf_list_node *node = TAILQ_FIRST (&list->fields);
604a9753648Sniklas 
605a9753648Sniklas   while (node)
606a9753648Sniklas     {
607a9753648Sniklas       TAILQ_REMOVE (&list->fields, node, link);
608a9753648Sniklas       if (node->field)
609a9753648Sniklas 	free (node->field);
610a9753648Sniklas       free (node);
611a9753648Sniklas       node = TAILQ_FIRST (&list->fields);
612a9753648Sniklas     }
6132040585eSniklas   free (list);
6142040585eSniklas }
615*f8f1e192Sniklas 
616*f8f1e192Sniklas int
617*f8f1e192Sniklas conf_begin (void)
618*f8f1e192Sniklas {
619*f8f1e192Sniklas   static int seq = 0;
620*f8f1e192Sniklas 
621*f8f1e192Sniklas   return ++seq;
622*f8f1e192Sniklas }
623*f8f1e192Sniklas 
624*f8f1e192Sniklas static struct conf_trans *
625*f8f1e192Sniklas conf_trans_node (int transaction, enum conf_op op)
626*f8f1e192Sniklas {
627*f8f1e192Sniklas   struct conf_trans *node;
628*f8f1e192Sniklas 
629*f8f1e192Sniklas   node = calloc (1, sizeof *node);
630*f8f1e192Sniklas   if (!node)
631*f8f1e192Sniklas     {
632*f8f1e192Sniklas       log_error ("conf_trans_node: calloc (1, %d) failed", sizeof *node);
633*f8f1e192Sniklas       return 0;
634*f8f1e192Sniklas     }
635*f8f1e192Sniklas   node->trans = transaction;
636*f8f1e192Sniklas   node->op = op;
637*f8f1e192Sniklas   TAILQ_INSERT_TAIL (&conf_trans_queue, node, link);
638*f8f1e192Sniklas   return node;
639*f8f1e192Sniklas }
640*f8f1e192Sniklas 
641*f8f1e192Sniklas /* Queue a set operation.  */
642*f8f1e192Sniklas int
643*f8f1e192Sniklas conf_set (int transaction, char *section, char *tag, char *value, int override)
644*f8f1e192Sniklas {
645*f8f1e192Sniklas   struct conf_trans *node;
646*f8f1e192Sniklas 
647*f8f1e192Sniklas   node = conf_trans_node (transaction, CONF_SET);
648*f8f1e192Sniklas   if (!node)
649*f8f1e192Sniklas     return 1;
650*f8f1e192Sniklas   node->section = strdup (section);
651*f8f1e192Sniklas   if (!node->section)
652*f8f1e192Sniklas     {
653*f8f1e192Sniklas       log_error ("conf_set: strdup (\"%s\") failed", section);
654*f8f1e192Sniklas       goto fail;
655*f8f1e192Sniklas     }
656*f8f1e192Sniklas   node->tag = strdup (tag);
657*f8f1e192Sniklas   if (!node->tag)
658*f8f1e192Sniklas     {
659*f8f1e192Sniklas       log_error ("conf_set: strdup (\"%s\") failed", tag);
660*f8f1e192Sniklas       goto fail;
661*f8f1e192Sniklas     }
662*f8f1e192Sniklas   node->value = strdup (value);
663*f8f1e192Sniklas   if (!node->value)
664*f8f1e192Sniklas     {
665*f8f1e192Sniklas       log_error ("conf_set: strdup (\"%s\") failed", value);
666*f8f1e192Sniklas       goto fail;
667*f8f1e192Sniklas     }
668*f8f1e192Sniklas   node->override = override;
669*f8f1e192Sniklas   return 0;
670*f8f1e192Sniklas 
671*f8f1e192Sniklas  fail:
672*f8f1e192Sniklas   if (node->tag)
673*f8f1e192Sniklas     free (node->tag);
674*f8f1e192Sniklas   if (node->section)
675*f8f1e192Sniklas     free (node->section);
676*f8f1e192Sniklas   if (node)
677*f8f1e192Sniklas     free (node);
678*f8f1e192Sniklas   return 1;
679*f8f1e192Sniklas }
680*f8f1e192Sniklas 
681*f8f1e192Sniklas /* Queue a remove operation.  */
682*f8f1e192Sniklas int
683*f8f1e192Sniklas conf_remove (int transaction, char *section, char *tag)
684*f8f1e192Sniklas {
685*f8f1e192Sniklas   struct conf_trans *node;
686*f8f1e192Sniklas 
687*f8f1e192Sniklas   node = conf_trans_node (transaction, CONF_REMOVE);
688*f8f1e192Sniklas   if (!node)
689*f8f1e192Sniklas     goto fail;
690*f8f1e192Sniklas   node->section = strdup (section);
691*f8f1e192Sniklas   if (!node->section)
692*f8f1e192Sniklas     {
693*f8f1e192Sniklas       log_error ("conf_remove: strdup (\"%s\") failed", section);
694*f8f1e192Sniklas       goto fail;
695*f8f1e192Sniklas     }
696*f8f1e192Sniklas   node->tag = strdup (tag);
697*f8f1e192Sniklas   if (!node->tag)
698*f8f1e192Sniklas     {
699*f8f1e192Sniklas       log_error ("conf_remove: strdup (\"%s\") failed", tag);
700*f8f1e192Sniklas       goto fail;
701*f8f1e192Sniklas     }
702*f8f1e192Sniklas   return 0;
703*f8f1e192Sniklas 
704*f8f1e192Sniklas  fail:
705*f8f1e192Sniklas   if (node->section)
706*f8f1e192Sniklas     free (node->section);
707*f8f1e192Sniklas   if (node)
708*f8f1e192Sniklas     free (node);
709*f8f1e192Sniklas   return 1;
710*f8f1e192Sniklas }
711*f8f1e192Sniklas 
712*f8f1e192Sniklas /* Queue a remove section operation.  */
713*f8f1e192Sniklas int
714*f8f1e192Sniklas conf_remove_section (int transaction, char *section)
715*f8f1e192Sniklas {
716*f8f1e192Sniklas   struct conf_trans *node;
717*f8f1e192Sniklas 
718*f8f1e192Sniklas   node = conf_trans_node (transaction, CONF_REMOVE_SECTION);
719*f8f1e192Sniklas   if (!node)
720*f8f1e192Sniklas     goto fail;
721*f8f1e192Sniklas   node->section = strdup (section);
722*f8f1e192Sniklas   if (!node->section)
723*f8f1e192Sniklas     {
724*f8f1e192Sniklas       log_error ("conf_remove_section: strdup (\"%s\") failed", section);
725*f8f1e192Sniklas       goto fail;
726*f8f1e192Sniklas     }
727*f8f1e192Sniklas   return 0;
728*f8f1e192Sniklas 
729*f8f1e192Sniklas  fail:
730*f8f1e192Sniklas   if (node)
731*f8f1e192Sniklas     free (node);
732*f8f1e192Sniklas   return 1;
733*f8f1e192Sniklas }
734*f8f1e192Sniklas 
735*f8f1e192Sniklas /* Execute all queued operations for this transaction.  Cleanup.  */
736*f8f1e192Sniklas int
737*f8f1e192Sniklas conf_end (int transaction, int commit)
738*f8f1e192Sniklas {
739*f8f1e192Sniklas   struct conf_trans *node, *next;
740*f8f1e192Sniklas 
741*f8f1e192Sniklas   for (node = TAILQ_FIRST (&conf_trans_queue); node; node = next)
742*f8f1e192Sniklas     {
743*f8f1e192Sniklas       next = TAILQ_NEXT (node, link);
744*f8f1e192Sniklas       if (node->trans == transaction)
745*f8f1e192Sniklas 	{
746*f8f1e192Sniklas 	  if (commit)
747*f8f1e192Sniklas 	    switch (node->op)
748*f8f1e192Sniklas 	      {
749*f8f1e192Sniklas 	      case CONF_SET:
750*f8f1e192Sniklas 		conf_set_now (node->section, node->tag, node->value,
751*f8f1e192Sniklas 			      node->override);
752*f8f1e192Sniklas 		break;
753*f8f1e192Sniklas 	      case CONF_REMOVE:
754*f8f1e192Sniklas 		conf_remove_now (node->section, node->tag);
755*f8f1e192Sniklas 		break;
756*f8f1e192Sniklas 	      case CONF_REMOVE_SECTION:
757*f8f1e192Sniklas 		conf_remove_section_now (node->section);
758*f8f1e192Sniklas 		break;
759*f8f1e192Sniklas 	      default:
760*f8f1e192Sniklas 		log_print ("conf_end: unknown operation: %d", node->op);
761*f8f1e192Sniklas 	      }
762*f8f1e192Sniklas 	  TAILQ_REMOVE (&conf_trans_queue, node, link);
763*f8f1e192Sniklas 	  free (node);
764*f8f1e192Sniklas 	}
765*f8f1e192Sniklas     }
766*f8f1e192Sniklas   return 0;
767*f8f1e192Sniklas }
768