conf.c revision 1.36
1/*	$OpenBSD: conf.c,v 1.36 2002/01/23 18:44:47 ho Exp $	*/
2/*	$EOM: conf.c,v 1.48 2000/12/04 02:04:29 angelos Exp $	*/
3
4/*
5 * Copyright (c) 1998, 1999, 2000, 2001 Niklas Hallqvist.  All rights reserved.
6 * Copyright (c) 2000, 2001 H�kan Olsson.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by Ericsson Radio Systems.
19 * 4. The name of the author may not be used to endorse or promote products
20 *    derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34/*
35 * This code was written under funding by Ericsson Radio Systems.
36 */
37
38#include <sys/param.h>
39#include <sys/mman.h>
40#include <sys/queue.h>
41#include <sys/socket.h>
42#include <sys/stat.h>
43#include <netinet/in.h>
44#include <arpa/inet.h>
45#include <ctype.h>
46#include <fcntl.h>
47#include <stdio.h>
48#include <stdlib.h>
49#include <string.h>
50#include <unistd.h>
51#include <errno.h>
52
53#include "sysdep.h"
54
55#include "app.h"
56#include "conf.h"
57#include "log.h"
58#include "util.h"
59
60static char *conf_get_trans_str (int, char *, char *);
61static void conf_load_defaults (int);
62#if 0
63static int conf_find_trans_xf (int, char *);
64#endif
65
66struct conf_trans {
67  TAILQ_ENTRY (conf_trans) link;
68  int trans;
69  enum conf_op { CONF_SET, CONF_REMOVE, CONF_REMOVE_SECTION } op;
70  char *section;
71  char *tag;
72  char *value;
73  int override;
74  int is_default;
75};
76
77TAILQ_HEAD (conf_trans_head, conf_trans) conf_trans_queue;
78
79/*
80 * Radix-64 Encoding.
81 */
82const u_int8_t bin2asc[]
83  = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
84
85const u_int8_t asc2bin[] =
86{
87  255, 255, 255, 255, 255, 255, 255, 255,
88  255, 255, 255, 255, 255, 255, 255, 255,
89  255, 255, 255, 255, 255, 255, 255, 255,
90  255, 255, 255, 255, 255, 255, 255, 255,
91  255, 255, 255, 255, 255, 255, 255, 255,
92  255, 255, 255,  62, 255, 255, 255,  63,
93   52,  53,  54,  55,  56,  57,  58,  59,
94   60,  61, 255, 255, 255, 255, 255, 255,
95  255,   0,   1,   2,   3,   4,   5,   6,
96    7,   8,   9,  10,  11,  12,  13,  14,
97   15,  16,  17,  18,  19,  20,  21,  22,
98   23,  24,  25, 255, 255, 255, 255, 255,
99  255,  26,  27,  28,  29,  30,  31,  32,
100   33,  34,  35,  36,  37,  38,  39,  40,
101   41,  42,  43,  44,  45,  46,  47,  48,
102   49,  50,  51, 255, 255, 255, 255, 255
103};
104
105struct conf_binding {
106  LIST_ENTRY (conf_binding) link;
107  char *section;
108  char *tag;
109  char *value;
110  int is_default;
111};
112
113char *conf_path = CONFIG_FILE;
114LIST_HEAD (conf_bindings, conf_binding) conf_bindings[256];
115
116static char *conf_addr;
117
118static __inline__ u_int8_t
119conf_hash (char *s)
120{
121  u_int8_t hash = 0;
122
123  while (*s)
124    {
125      hash = ((hash << 1) | (hash >> 7)) ^ tolower (*s);
126      s++;
127    }
128  return hash;
129}
130
131/*
132 * Insert a tag-value combination from LINE (the equal sign is at POS)
133 */
134static int
135conf_remove_now (char *section, char *tag)
136{
137  struct conf_binding *cb, *next;
138
139  for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb; cb = next)
140    {
141      next = LIST_NEXT (cb, link);
142      if (strcasecmp (cb->section, section) == 0
143	  && strcasecmp (cb->tag, tag) == 0)
144	{
145	  LIST_REMOVE (cb, link);
146	  LOG_DBG ((LOG_MISC, 95, "[%s]:%s->%s removed", section, tag,
147		    cb->value));
148	  free (cb->section);
149	  free (cb->tag);
150	  free (cb->value);
151	  free (cb);
152	  return 0;
153	}
154    }
155  return 1;
156}
157
158static int
159conf_remove_section_now (char *section)
160{
161  struct conf_binding *cb, *next;
162  int unseen = 1;
163
164  for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb; cb = next)
165    {
166      next = LIST_NEXT (cb, link);
167      if (strcasecmp (cb->section, section) == 0)
168	{
169	  unseen = 0;
170	  LIST_REMOVE (cb, link);
171	  LOG_DBG ((LOG_MISC, 95, "[%s]:%s->%s removed", section, cb->tag,
172		    cb->value));
173	  free (cb->section);
174	  free (cb->tag);
175	  free (cb->value);
176	  free (cb);
177	}
178    }
179  return unseen;
180}
181
182/*
183 * Insert a tag-value combination from LINE (the equal sign is at POS)
184 * into SECTION of our configuration database.
185 */
186static int
187conf_set_now (char *section, char *tag, char *value, int override,
188	      int is_default)
189{
190  struct conf_binding *node = 0;
191
192  if (override)
193    conf_remove_now (section, tag);
194  else if (conf_get_str (section, tag))
195    {
196      if (!is_default)
197	log_print ("conf_set: duplicate tag [%s]:%s, ignoring...\n", section,
198		   tag);
199      return 1;
200    }
201
202  node = calloc (1, sizeof *node);
203  if (!node)
204    {
205      log_error ("conf_set: calloc (1, %d) failed", sizeof *node);
206      return 1;
207    }
208  node->section = strdup (section);
209  node->tag = strdup (tag);
210  node->value = strdup (value);
211  node->is_default = is_default;
212
213  LIST_INSERT_HEAD (&conf_bindings[conf_hash (section)], node, link);
214  LOG_DBG ((LOG_MISC, 95, "conf_set: [%s]:%s->%s", node->section, node->tag,
215	    node->value));
216  return 0;
217}
218
219/*
220 * Parse the line LINE of SZ bytes.  Skip Comments, recognize section
221 * headers and feed tag-value pairs into our configuration database.
222 */
223static void
224conf_parse_line (int trans, char *line, size_t sz)
225{
226  char *cp = line;
227  int i;
228  static char *section = 0;
229  static int ln = 0;
230
231  ln++;
232
233  /* Lines starting with '#' or ';' are comments.  */
234  if (*line == '#' || *line == ';')
235    return;
236
237  /* '[section]' parsing...  */
238  if (*line == '[')
239    {
240      for (i = 1; i < sz; i++)
241	if (line[i] == ']')
242	  break;
243      if (i == sz)
244	{
245	  log_print ("conf_parse_line: %d:"
246		     "non-matched ']', ignoring until next section", ln);
247	  section = 0;
248	  return;
249	}
250      if (section)
251	free (section);
252      section = malloc (i);
253      strlcpy (section, line + 1, i);
254      return;
255    }
256
257  /* Deal with assignments.  */
258  for (i = 0; i < sz; i++)
259    if (cp[i] == '=')
260      {
261	/* If no section, we are ignoring the lines.  */
262	if (!section)
263	  {
264	    log_print ("conf_parse_line: %d: ignoring line due to no section",
265		       ln);
266	    return;
267	  }
268	line[strcspn (line, " \t=")] = '\0';
269	/* XXX Perhaps should we not ignore errors?  */
270	conf_set (trans, section, line,
271		  line + i + 1 + strspn (line + i + 1, " \t"), 0, 0);
272	return;
273      }
274
275  /* Other non-empty lines are wierd.  */
276  i = strspn (line, " \t");
277  if (line[i])
278    log_print ("conf_parse_line: %d: syntax error", ln);
279
280  return;
281}
282
283/* Parse the mapped configuration file.  */
284static void
285conf_parse (int trans, char *buf, size_t sz)
286{
287  char *cp = buf;
288  char *bufend = buf + sz;
289  char *line;
290
291  line = cp;
292  while (cp < bufend)
293    {
294      if (*cp == '\n')
295	{
296	  /* Check for escaped newlines.  */
297	  if (cp > buf && *(cp - 1) == '\\')
298	    *(cp - 1) = *cp = ' ';
299	  else
300	    {
301	      *cp = '\0';
302	      conf_parse_line (trans, line, cp - line);
303	      line = cp + 1;
304	    }
305	}
306      cp++;
307    }
308  if (cp != line)
309    log_print ("conf_parse: last line non-terminated, ignored.");
310}
311
312/*
313 * Auto-generate default configuration values for the transforms and
314 * suites the user wants.
315 *
316 * Resulting section names can be:
317 *  For main mode:
318 *     {DES,BLF,3DES,CAST}-{MD5,SHA}[-{DSS,RSA_SIG}]
319 *  For quick mode:
320 *     QM-{ESP,AH}[-TRP]-{DES,3DES,CAST,BLF,AES}[-{MD5,SHA,RIPEMD}][-PFS]-SUITE
321 * DH groups; currently always MODP_768 for MD5, and MODP_1024 for SHA.
322 *
323 * XXX We may want to support USE_BLOWFISH, USE_TRIPLEDES, etc...
324 * XXX No EC2N DH support here yet.
325 */
326
327/* Find the value for a section+tag in the transaction list.  */
328static char *
329conf_get_trans_str (int trans, char *section, char *tag)
330{
331  struct conf_trans *node, *nf = 0;
332
333  for (node = TAILQ_FIRST (&conf_trans_queue); node;
334       node = TAILQ_NEXT (node, link))
335    if (node->trans == trans && strcmp (section, node->section) == 0
336	&& strcmp (tag, node->tag) == 0)
337      {
338	if (!nf)
339	  nf = node;
340	else if (node->override)
341	  nf = node;
342      }
343  return nf ? nf->value : 0;
344}
345
346#if 0
347/* XXX Currently unused.  */
348static int
349conf_find_trans_xf (int phase, char *xf)
350{
351  struct conf_trans *node;
352  char *p;
353
354  /* Find the relevant transforms and suites, if any.  */
355  for (node = TAILQ_FIRST (&conf_trans_queue); node;
356       node = TAILQ_NEXT (node, link))
357    if ((phase == 1 && strcmp ("Transforms", node->tag) == 0) ||
358	(phase == 2 && strcmp ("Suites", node->tag) == 0))
359      {
360	p = node->value;
361	while ((p = strstr (p, xf)) != NULL)
362	  if (*(p + strlen (p)) && *(p + strlen (p)) != ',')
363	    p += strlen (p);
364	  else
365	    return 1;
366      }
367  return 0;
368}
369#endif
370
371static void
372conf_load_defaults (int tr)
373{
374#define CONF_MAX 256
375  int enc, auth, hash, proto, mode, pfs;
376  char sect[CONF_MAX], *dflt;
377
378  char *mm_auth[]   = { "PRE_SHARED", "DSS", "RSA_SIG", 0 };
379  char *mm_hash[]   = { "MD5", "SHA", 0 };
380  char *mm_enc[]    = { "DES_CBC", "BLOWFISH_CBC", "3DES_CBC",
381			"CAST_CBC", 0 };
382  char *dh_group[]  = { "MODP_768", "MODP_1024", "MODP_1536", 0 };
383  char *qm_enc[]    = { "DES", "3DES", "CAST", "BLOWFISH", "AES", 0 };
384  char *qm_hash[]   = { "HMAC_MD5", "HMAC_SHA", "HMAC_RIPEMD", "NONE", 0 };
385
386  /* Abbreviations to make section names a bit shorter.  */
387  char *mm_auth_p[] = { "", "-DSS", "-RSA_SIG", 0 };
388  char *mm_enc_p[]  = { "DES", "BLF", "3DES", "CAST", 0 };
389  char *qm_enc_p[]  = { "-DES", "-3DES", "-CAST", "-BLF", "-AES", 0 };
390  char *qm_hash_p[] = { "-MD5", "-SHA", "-RIPEMD", "", 0 };
391
392  /* Helper #defines, incl abbreviations.  */
393#define PROTO(x)  ((x) ? "AH" : "ESP")
394#define PFS(x)    ((x) ? "-PFS" : "")
395#define MODE(x)   ((x) ? "TRANSPORT" : "TUNNEL")
396#define MODE_p(x) ((x) ? "-TRP" : "")
397
398  /* General and X509 defaults */
399  conf_set (tr, "General", "Retransmits", CONF_DFLT_RETRANSMITS, 0, 1);
400  conf_set (tr, "General", "Exchange-max-time", CONF_DFLT_EXCH_MAX_TIME, 0, 1);
401  conf_set (tr, "General", "Policy-file", CONF_DFLT_POLICY_FILE, 0, 1);
402
403#ifdef USE_X509
404  conf_set (tr, "X509-certificates", "CA-directory", CONF_DFLT_X509_CA_DIR, 0,
405	    1);
406  conf_set (tr, "X509-certificates", "Cert-directory", CONF_DFLT_X509_CERT_DIR,
407	    0, 1);
408  conf_set (tr, "X509-certificates", "Private-key", CONF_DFLT_X509_PRIVATE_KEY,
409	    0, 1);
410#endif
411
412#ifdef USE_KEYNOTE
413  conf_set (tr, "KeyNote", "Credential-directory", CONF_DFLT_KEYNOTE_CRED_DIR,
414	    0, 1);
415#endif
416
417  /* Lifetimes. XXX p1/p2 vs main/quick mode may be unclear.  */
418  dflt = conf_get_trans_str (tr, "General", "Default-phase-1-lifetime");
419  conf_set (tr, CONF_DFLT_TAG_LIFE_MAIN_MODE, "LIFE_TYPE",
420	    CONF_DFLT_TYPE_LIFE_MAIN_MODE, 0, 1);
421  conf_set (tr, CONF_DFLT_TAG_LIFE_MAIN_MODE, "LIFE_DURATION",
422	    (dflt ? dflt : CONF_DFLT_VAL_LIFE_MAIN_MODE), 0, 1);
423
424  dflt = conf_get_trans_str (tr, "General", "Default-phase-2-lifetime");
425  conf_set (tr, CONF_DFLT_TAG_LIFE_QUICK_MODE, "LIFE_TYPE",
426	    CONF_DFLT_TYPE_LIFE_QUICK_MODE, 0, 1);
427  conf_set (tr, CONF_DFLT_TAG_LIFE_QUICK_MODE, "LIFE_DURATION",
428	    (dflt ? dflt : CONF_DFLT_VAL_LIFE_QUICK_MODE), 0, 1);
429
430  /* Main modes */
431  for (enc = 0; mm_enc[enc]; enc ++)
432    for (hash = 0; mm_hash[hash]; hash ++)
433      for (auth = 0; mm_auth[auth]; auth ++)
434	{
435	  snprintf (sect, CONF_MAX, "%s-%s%s", mm_enc_p[enc], mm_hash[hash],
436		    mm_auth_p[auth]);
437
438#if 0
439	  if (!conf_find_trans_xf (1, sect))
440	    continue;
441#endif
442
443	  LOG_DBG ((LOG_MISC, 90, "conf_load_defaults : main mode %s", sect));
444
445	  conf_set (tr, sect, "ENCRYPTION_ALGORITHM", mm_enc[enc], 0, 1);
446	  if (strcmp (mm_enc[enc], "BLOWFISH_CBC") == 0)
447	    conf_set (tr, sect, "KEY_LENGTH", CONF_DFLT_VAL_BLF_KEYLEN, 0, 1);
448
449	  conf_set (tr, sect, "HASH_ALGORITHM", mm_hash[hash], 0, 1);
450	  conf_set (tr, sect, "AUTHENTICATION_METHOD", mm_auth[auth], 0, 1);
451
452	  /* XXX Assumes md5 -> modp768 and sha -> modp1024 */
453	  conf_set (tr, sect, "GROUP_DESCRIPTION", dh_group[hash], 0, 1);
454
455	  conf_set (tr, sect, "Life", CONF_DFLT_TAG_LIFE_MAIN_MODE, 0, 1);
456	}
457
458  /* Setup a default Phase 1 entry */
459  conf_set (tr, "Phase 1", "Default", "Default-phase-1", 0, 1);
460
461  conf_set (tr, "Default-phase-1", "Phase", "1", 0, 1);
462  conf_set (tr, "Default-phase-1", "Configuration",
463            "Default-phase-1-configuration", 0, 1);
464  dflt = conf_get_trans_str (tr, "General", "Default-phase-1-ID");
465  if (dflt)
466    conf_set (tr, "Default-phase-1", "ID", dflt, 0, 1);
467
468  conf_set (tr, "Default-phase-1-configuration",
469            "EXCHANGE_TYPE", "ID_PROT", 0, 1);
470  conf_set (tr, "Default-phase-1-configuration", "Transforms",
471            "3DES-SHA-RSA_SIG", 0, 1);
472
473   /* Quick modes */
474  for (enc = 0; qm_enc[enc]; enc ++)
475    for (proto = 0; proto < 2; proto ++)
476      for (mode = 0; mode < 2; mode ++)
477	for (pfs = 0; pfs < 2; pfs ++)
478	  for (hash = 0; qm_hash[hash]; hash ++)
479	    if ((proto == 1 && strcmp (qm_hash[hash], "NONE") == 0)) /* AH */
480	      continue;
481	    else
482	      {
483		char tmp[CONF_MAX];
484
485		snprintf (tmp, CONF_MAX, "QM-%s%s%s%s%s", PROTO (proto),
486			  MODE_p (mode), qm_enc_p[enc], qm_hash_p[hash],
487			  PFS (pfs));
488
489		strlcpy (sect, tmp, CONF_MAX);
490		strlcat (sect, "-SUITE", CONF_MAX);
491
492#if 0
493		if (!conf_find_trans_xf (2, sect))
494		  continue;
495#endif
496
497		LOG_DBG ((LOG_MISC, 90, "conf_load_defaults : quick mode %s",
498			  sect));
499
500		conf_set (tr, sect, "Protocols", tmp, 0, 1);
501
502		snprintf (sect, CONF_MAX, "IPSEC_%s", PROTO (proto));
503		conf_set (tr, tmp, "PROTOCOL_ID", sect, 0, 1);
504
505		strlcpy (sect, tmp, CONF_MAX);
506		strlcat (sect, "-XF", CONF_MAX);
507		conf_set (tr, tmp, "Transforms", sect, 0, 1);
508
509                /* XXX For now, defaults contain just one xf per protocol.  */
510
511		conf_set (tr, sect, "TRANSFORM_ID", qm_enc[enc], 0, 1);
512
513                if (strcmp (qm_enc[enc], "BLOWFISH") == 0)
514		  conf_set (tr, sect, "KEY_LENGTH", CONF_DFLT_VAL_BLF_KEYLEN,
515			    0, 1);
516
517		conf_set (tr, sect, "ENCAPSULATION_MODE", MODE (mode), 0, 1);
518
519                if (strcmp (qm_hash[hash], "NONE"))
520                {
521		  conf_set (tr, sect, "AUTHENTICATION_ALGORITHM",
522			    qm_hash[hash], 0, 1);
523
524                  /* XXX Another shortcut -- to keep length down.  */
525                  if (pfs)
526		    conf_set (tr, sect, "GROUP_DESCRIPTION",
527			      dh_group[ ((hash<2) ? hash : 1) ], 0, 1);
528                }
529
530                /* XXX Lifetimes depending on enc/auth strength?  */
531		conf_set (tr, sect, "Life", CONF_DFLT_TAG_LIFE_QUICK_MODE, 0,
532			  1);
533	      }
534  return;
535}
536
537void
538conf_init (void)
539{
540  int i;
541
542  for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
543    LIST_INIT (&conf_bindings[i]);
544  TAILQ_INIT (&conf_trans_queue);
545  conf_reinit ();
546}
547
548/* Open the config file and map it into our address space, then parse it.  */
549void
550conf_reinit (void)
551{
552  struct conf_binding *cb = 0;
553  int fd, i, trans;
554  off_t sz;
555  char *new_conf_addr = 0;
556  struct stat sb;
557
558  if ((stat (conf_path, &sb) == 0) || (errno != ENOENT))
559    {
560      if (check_file_secrecy (conf_path, &sz))
561	return;
562
563      fd = open (conf_path, O_RDONLY);
564      if (fd == -1)
565        {
566	  log_error ("conf_reinit: open (\"%s\", O_RDONLY) failed", conf_path);
567	  return;
568	}
569
570      new_conf_addr = malloc (sz);
571      if (!new_conf_addr)
572        {
573	  log_error ("conf_reinit: malloc (%d) failed", sz);
574	  goto fail;
575	}
576
577      /* XXX I assume short reads won't happen here.  */
578      if (read (fd, new_conf_addr, sz) != sz)
579        {
580	    log_error ("conf_reinit: read (%d, %p, %d) failed",
581		       fd, new_conf_addr, sz);
582	    goto fail;
583	}
584      close (fd);
585
586      trans = conf_begin ();
587
588      /* XXX Should we not care about errors and rollback?  */
589      conf_parse (trans, new_conf_addr, sz);
590    }
591  else
592    trans = conf_begin ();
593
594  /* Load default configuration values.  */
595  conf_load_defaults (trans);
596
597  /* Free potential existing configuration.  */
598  if (conf_addr)
599    {
600      for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
601	for (cb = LIST_FIRST (&conf_bindings[i]); cb;
602	     cb = LIST_FIRST (&conf_bindings[i]))
603	  conf_remove_now (cb->section, cb->tag);
604      free (conf_addr);
605    }
606
607  conf_end (trans, 1);
608  conf_addr = new_conf_addr;
609  return;
610
611 fail:
612  if (new_conf_addr)
613    free (new_conf_addr);
614  close (fd);
615}
616
617/*
618 * Return the numeric value denoted by TAG in section SECTION or DEF
619 * if that tag does not exist.
620 */
621int
622conf_get_num (char *section, char *tag, int def)
623{
624  char *value = conf_get_str (section, tag);
625
626  if (value)
627      return atoi (value);
628  return def;
629}
630
631/*
632 * Return the socket endpoint address denoted by TAG in SECTION as a
633 * struct sockaddr.  It is the callers responsibility to deallocate
634 * this structure when it is finished with it.
635 */
636struct sockaddr *
637conf_get_address (char *section, char *tag)
638{
639  char *value = conf_get_str (section, tag);
640  struct sockaddr *sa;
641
642  if (!value)
643    return 0;
644  if (text2sockaddr (value, 0, &sa) == -1)
645    return 0;
646  return sa;
647}
648
649/* Validate X according to the range denoted by TAG in section SECTION.  */
650int
651conf_match_num (char *section, char *tag, int x)
652{
653  char *value = conf_get_str (section, tag);
654  int val, min, max, n;
655
656  if (!value)
657    return 0;
658  n = sscanf (value, "%d,%d:%d", &val, &min, &max);
659  switch (n)
660    {
661    case 1:
662      LOG_DBG ((LOG_MISC, 90, "conf_match_num: %s:%s %d==%d?", section, tag,
663		val, x));
664      return x == val;
665    case 3:
666      LOG_DBG ((LOG_MISC, 90, "conf_match_num: %s:%s %d<=%d<=%d?", section,
667		tag, min, x, max));
668      return min <= x && max >= x;
669    default:
670      log_error ("conf_match_num: section %s tag %s: invalid number spec %s",
671		 section, tag, value);
672    }
673  return 0;
674}
675
676/* Return the string value denoted by TAG in section SECTION.  */
677char *
678conf_get_str (char *section, char *tag)
679{
680  struct conf_binding *cb;
681
682  for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb;
683       cb = LIST_NEXT (cb, link))
684    if (strcasecmp (section, cb->section) == 0
685	&& strcasecmp (tag, cb->tag) == 0)
686      {
687	LOG_DBG ((LOG_MISC, 95, "conf_get_str: [%s]:%s->%s", section,
688		  tag, cb->value));
689	return cb->value;
690      }
691  LOG_DBG ((LOG_MISC, 95,
692	    "conf_get_str: configuration value not found [%s]:%s", section,
693	    tag));
694  return 0;
695}
696
697/*
698 * Build a list of string values out of the comma separated value denoted by
699 * TAG in SECTION.
700 */
701struct conf_list *
702conf_get_list (char *section, char *tag)
703{
704  char *liststr = 0, *p, *field;
705  struct conf_list *list = 0;
706  struct conf_list_node *node;
707
708  list = malloc (sizeof *list);
709  if (!list)
710    goto cleanup;
711  TAILQ_INIT (&list->fields);
712  list->cnt = 0;
713  liststr = conf_get_str (section, tag);
714  if (!liststr)
715    goto cleanup;
716  liststr = strdup (liststr);
717  if (!liststr)
718    goto cleanup;
719  p = liststr;
720  while ((field = strsep (&p, ", \t")) != NULL)
721    {
722      if (*field == '\0')
723	{
724	  log_print ("conf_get_list: empty field, ignoring...");
725	  continue;
726	}
727      list->cnt++;
728      node = calloc (1, sizeof *node);
729      if (!node)
730	goto cleanup;
731      node->field = strdup (field);
732      if (!node->field)
733	goto cleanup;
734      TAILQ_INSERT_TAIL (&list->fields, node, link);
735    }
736  free (liststr);
737  return list;
738
739 cleanup:
740  if (list)
741    conf_free_list (list);
742  if (liststr)
743    free (liststr);
744  return 0;
745}
746
747struct conf_list *
748conf_get_tag_list (char *section)
749{
750  struct conf_list *list = 0;
751  struct conf_list_node *node;
752  struct conf_binding *cb;
753
754  list = malloc (sizeof *list);
755  if (!list)
756    goto cleanup;
757  TAILQ_INIT (&list->fields);
758  list->cnt = 0;
759  for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb;
760       cb = LIST_NEXT (cb, link))
761    if (strcasecmp (section, cb->section) == 0)
762      {
763	list->cnt++;
764	node = calloc (1, sizeof *node);
765	if (!node)
766	  goto cleanup;
767	node->field = strdup (cb->tag);
768	if (!node->field)
769	  goto cleanup;
770	TAILQ_INSERT_TAIL (&list->fields, node, link);
771      }
772  return list;
773
774 cleanup:
775  if (list)
776    conf_free_list (list);
777  return 0;
778}
779
780/* Decode a PEM encoded buffer.  */
781int
782conf_decode_base64 (u_int8_t *out, u_int32_t *len, u_char *buf)
783{
784  u_int32_t c = 0;
785  u_int8_t c1, c2, c3, c4;
786
787  while (*buf)
788    {
789      if (*buf > 127 || (c1 = asc2bin[*buf]) == 255)
790	return 0;
791      buf++;
792
793      if (*buf > 127 || (c2 = asc2bin[*buf]) == 255)
794	return 0;
795      buf++;
796
797      if (*buf == '=')
798	{
799	  c3 = c4 = 0;
800	  c++;
801
802	  /* Check last four bit */
803	  if (c2 & 0xF)
804	    return 0;
805
806	  if (strcmp (buf, "==") == 0)
807	    buf++;
808	  else
809	    return 0;
810	}
811      else if (*buf > 127 || (c3 = asc2bin[*buf]) == 255)
812	return 0;
813      else
814	{
815	  if (*++buf == '=')
816	    {
817	      c4 = 0;
818	      c += 2;
819
820	      /* Check last two bit */
821	      if (c3 & 3)
822		return 0;
823
824	      if (strcmp (buf, "="))
825		return 0;
826
827	    }
828	  else if (*buf > 127 || (c4 = asc2bin[*buf]) == 255)
829	      return 0;
830	  else
831	      c += 3;
832	}
833
834      buf++;
835      *out++ = (c1 << 2) | (c2 >> 4);
836      *out++ = (c2 << 4) | (c3 >> 2);
837      *out++ = (c3 << 6) | c4;
838    }
839
840  *len = c;
841  return 1;
842
843}
844
845/* Read a line from a stream to the buffer.  */
846int
847conf_get_line (FILE *stream, char *buf, u_int32_t len)
848{
849  int c;
850
851  while (len-- > 1)
852    {
853      c = fgetc (stream);
854      if (c == '\n')
855	{
856	  *buf = 0;
857	  return 1;
858	}
859      else if (c == EOF)
860	break;
861
862      *buf++ = c;
863    }
864
865  *buf = 0;
866  return 0;
867}
868
869void
870conf_free_list (struct conf_list *list)
871{
872  struct conf_list_node *node = TAILQ_FIRST (&list->fields);
873
874  while (node)
875    {
876      TAILQ_REMOVE (&list->fields, node, link);
877      if (node->field)
878	free (node->field);
879      free (node);
880      node = TAILQ_FIRST (&list->fields);
881    }
882  free (list);
883}
884
885int
886conf_begin (void)
887{
888  static int seq = 0;
889
890  return ++seq;
891}
892
893static struct conf_trans *
894conf_trans_node (int transaction, enum conf_op op)
895{
896  struct conf_trans *node;
897
898  node = calloc (1, sizeof *node);
899  if (!node)
900    {
901      log_error ("conf_trans_node: calloc (1, %d) failed", sizeof *node);
902      return 0;
903    }
904  node->trans = transaction;
905  node->op = op;
906  TAILQ_INSERT_TAIL (&conf_trans_queue, node, link);
907  return node;
908}
909
910/* Queue a set operation.  */
911int
912conf_set (int transaction, char *section, char *tag, char *value, int override,
913	  int is_default)
914{
915  struct conf_trans *node;
916
917  node = conf_trans_node (transaction, CONF_SET);
918  if (!node)
919    return 1;
920  node->section = strdup (section);
921  if (!node->section)
922    {
923      log_error ("conf_set: strdup (\"%s\") failed", section);
924      goto fail;
925    }
926  node->tag = strdup (tag);
927  if (!node->tag)
928    {
929      log_error ("conf_set: strdup (\"%s\") failed", tag);
930      goto fail;
931    }
932  node->value = strdup (value);
933  if (!node->value)
934    {
935      log_error ("conf_set: strdup (\"%s\") failed", value);
936      goto fail;
937    }
938  node->override = override;
939  node->is_default = is_default;
940  return 0;
941
942 fail:
943  if (node->tag)
944    free (node->tag);
945  if (node->section)
946    free (node->section);
947  if (node)
948    free (node);
949  return 1;
950}
951
952/* Queue a remove operation.  */
953int
954conf_remove (int transaction, char *section, char *tag)
955{
956  struct conf_trans *node;
957
958  node = conf_trans_node (transaction, CONF_REMOVE);
959  if (!node)
960    goto fail;
961  node->section = strdup (section);
962  if (!node->section)
963    {
964      log_error ("conf_remove: strdup (\"%s\") failed", section);
965      goto fail;
966    }
967  node->tag = strdup (tag);
968  if (!node->tag)
969    {
970      log_error ("conf_remove: strdup (\"%s\") failed", tag);
971      goto fail;
972    }
973  return 0;
974
975 fail:
976  if (node->section)
977    free (node->section);
978  if (node)
979    free (node);
980  return 1;
981}
982
983/* Queue a remove section operation.  */
984int
985conf_remove_section (int transaction, char *section)
986{
987  struct conf_trans *node;
988
989  node = conf_trans_node (transaction, CONF_REMOVE_SECTION);
990  if (!node)
991    goto fail;
992  node->section = strdup (section);
993  if (!node->section)
994    {
995      log_error ("conf_remove_section: strdup (\"%s\") failed", section);
996      goto fail;
997    }
998  return 0;
999
1000 fail:
1001  if (node)
1002    free (node);
1003  return 1;
1004}
1005
1006/* Execute all queued operations for this transaction.  Cleanup.  */
1007int
1008conf_end (int transaction, int commit)
1009{
1010  struct conf_trans *node, *next;
1011
1012  for (node = TAILQ_FIRST (&conf_trans_queue); node; node = next)
1013    {
1014      next = TAILQ_NEXT (node, link);
1015      if (node->trans == transaction)
1016	{
1017	  if (commit)
1018	    switch (node->op)
1019	      {
1020	      case CONF_SET:
1021		conf_set_now (node->section, node->tag, node->value,
1022			      node->override, node->is_default);
1023		break;
1024	      case CONF_REMOVE:
1025		conf_remove_now (node->section, node->tag);
1026		break;
1027	      case CONF_REMOVE_SECTION:
1028		conf_remove_section_now (node->section);
1029		break;
1030	      default:
1031		log_print ("conf_end: unknown operation: %d", node->op);
1032	      }
1033	  TAILQ_REMOVE (&conf_trans_queue, node, link);
1034	  if (node->section)
1035	    free (node->section);
1036	  if (node->tag)
1037	    free (node->tag);
1038	  if (node->value)
1039	    free (node->value);
1040	  free (node);
1041	}
1042    }
1043  return 0;
1044}
1045
1046/*
1047 * Dump running configuration upon SIGUSR1.
1048 * Configuration is "stored in reverse order", so reverse it again.
1049 */
1050struct dumper {
1051  char *s, *v;
1052  struct dumper *next;
1053};
1054
1055static void
1056conf_report_dump (struct dumper *node)
1057{
1058  /* Recursive, cleanup when we're done.  */
1059
1060  if (node->next)
1061    conf_report_dump (node->next);
1062
1063  if (node->v)
1064    LOG_DBG ((LOG_REPORT, 0, "%s=\t%s", node->s, node->v));
1065  else if (node->s)
1066    {
1067      LOG_DBG ((LOG_REPORT, 0, "%s", node->s));
1068      if (strlen (node->s) > 0)
1069	free (node->s);
1070    }
1071
1072  free (node);
1073}
1074
1075void
1076conf_report (void)
1077{
1078  struct conf_binding *cb, *last = 0;
1079  int i, len;
1080  char *current_section = (char *)0;
1081  struct dumper *dumper, *dnode;
1082
1083  dumper = dnode = (struct dumper *)calloc (1, sizeof *dumper);
1084  if (!dumper)
1085    goto mem_fail;
1086
1087  LOG_DBG ((LOG_REPORT, 0, "conf_report: dumping running configuration"));
1088
1089  for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
1090    for (cb = LIST_FIRST (&conf_bindings[i]); cb;
1091	 cb = LIST_NEXT (cb, link))
1092      {
1093	if (!cb->is_default)
1094	  {
1095	    /* Dump this entry.  */
1096	    if (!current_section || strcmp (cb->section, current_section))
1097	      {
1098		if (current_section)
1099		  {
1100		    len = strlen (current_section) + 3;
1101		    dnode->s = malloc (len);
1102		    if (!dnode->s)
1103		      goto mem_fail;
1104
1105		    snprintf (dnode->s, len, "[%s]", current_section);
1106		    dnode->next
1107		      = (struct dumper *)calloc (1, sizeof (struct dumper));
1108		    dnode = dnode->next;
1109		    if (!dnode)
1110		      goto mem_fail;
1111
1112		    dnode->s = "";
1113		    dnode->next
1114		      = (struct dumper *)calloc (1, sizeof (struct dumper));
1115		    dnode = dnode->next;
1116		    if (!dnode)
1117		      goto mem_fail;
1118		  }
1119		current_section = cb->section;
1120	      }
1121	    dnode->s = cb->tag;
1122	    dnode->v = cb->value;
1123	    dnode->next = (struct dumper *)calloc (1, sizeof (struct dumper));
1124	    dnode = dnode->next;
1125	    if (!dnode)
1126	      goto mem_fail;
1127	    last = cb;
1128	  }
1129      }
1130
1131  if (last)
1132    {
1133      len = strlen (last->section) + 3;
1134      dnode->s = malloc (len);
1135      if (!dnode->s)
1136	goto mem_fail;
1137      snprintf (dnode->s, len, "[%s]", last->section);
1138    }
1139
1140  conf_report_dump (dumper);
1141
1142  return;
1143
1144 mem_fail:
1145  log_error ("conf_report: malloc/calloc failed");
1146  while ((dnode = dumper) != 0)
1147    {
1148      dumper = dumper->next;
1149      if (dnode->s)
1150	free (dnode->s);
1151      free (dnode);
1152    }
1153  return;
1154}
1155