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