conf.c revision 1.6
1/* $OpenBSD: conf.c,v 1.6 1999/02/26 03:34:26 niklas Exp $ */ 2/* $EOM: conf.c,v 1.14 1999/02/25 11:38:47 niklas Exp $ */ 3 4/* 5 * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Ericsson Radio Systems. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33/* 34 * This code was written under funding by Ericsson Radio Systems. 35 */ 36 37#include <sys/param.h> 38#include <sys/types.h> 39#include <sys/mman.h> 40#include <sys/queue.h> 41#include <sys/stat.h> 42#include <ctype.h> 43#include <fcntl.h> 44#include <stdio.h> 45#include <stdlib.h> 46#include <string.h> 47#include <unistd.h> 48 49#include "sysdep.h" 50 51#include "app.h" 52#include "conf.h" 53#include "log.h" 54 55/* 56 * Radix-64 Encoding. 57 */ 58 59const u_int8_t bin2asc[] = 60 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 61 62const u_int8_t asc2bin[] = 63{ 64 255, 255, 255, 255, 255, 255, 255, 255, 65 255, 255, 255, 255, 255, 255, 255, 255, 66 255, 255, 255, 255, 255, 255, 255, 255, 67 255, 255, 255, 255, 255, 255, 255, 255, 68 255, 255, 255, 255, 255, 255, 255, 255, 69 255, 255, 255, 62, 255, 255, 255, 63, 70 52, 53, 54, 55, 56, 57, 58, 59, 71 60, 61, 255, 255, 255, 255, 255, 255, 72 255, 0, 1, 2, 3, 4, 5, 6, 73 7, 8, 9, 10, 11, 12, 13, 14, 74 15, 16, 17, 18, 19, 20, 21, 22, 75 23, 24, 25, 255, 255, 255, 255, 255, 76 255, 26, 27, 28, 29, 30, 31, 32, 77 33, 34, 35, 36, 37, 38, 39, 40, 78 41, 42, 43, 44, 45, 46, 47, 48, 79 49, 50, 51, 255, 255, 255, 255, 255 80}; 81 82struct conf_binding { 83 LIST_ENTRY (conf_binding) link; 84 char *section; 85 char *tag; 86 char *value; 87}; 88 89char *conf_path = CONFIG_FILE; 90LIST_HEAD (conf_bindings, conf_binding) conf_bindings; 91 92static off_t conf_sz; 93static char *conf_addr; 94 95/* 96 * Insert a tag-value combination from LINE (the equal sign is at POS) 97 * into SECTION of our configuration database. 98 * XXX Should really be a hash table implementation. 99 */ 100static void 101conf_set (char *section, char *line, int pos) 102{ 103 struct conf_binding *node; 104 int i; 105 106 node = malloc (sizeof *node); 107 if (!node) 108 log_fatal ("conf_set: out of memory"); 109 node->section = section; 110 node->tag = line; 111 for (i = 0; line[i] && i < pos; i++) 112 ; 113 line[i] = '\0'; 114 if (conf_get_str (section, line)) 115 { 116 log_print ("conf_set: duplicate tag [%s]:%s, ignoring...\n", section, 117 line); 118 return; 119 } 120 node->value = line + pos + 1 + strspn (line + pos + 1, " \t"); 121 LIST_INSERT_HEAD (&conf_bindings, node, link); 122 log_debug (LOG_MISC, 70, "(%s,%s)->%s", node->section, node->tag, 123 node->value); 124} 125 126/* 127 * Parse the line LINE of SZ bytes. Skip Comments, recognize section 128 * headers and feed tag-value pairs into our configuration database. 129 */ 130static void 131conf_parse_line (char *line, size_t sz) 132{ 133 char *cp = line; 134 int i; 135 static char *section = 0; 136 static int ln = 0; 137 138 ln++; 139 140 /* Lines starting with '#' or ';' are comments. */ 141 if (*line == '#' || *line == ';') 142 return; 143 144 /* '[section]' parsing... */ 145 if (*line == '[') 146 { 147 for (i = 1; i < sz; i++) 148 if (line[i] == ']') 149 break; 150 if (i == sz) 151 { 152 log_print ("conf_parse_line: %d:" 153 "non-matched ']', ignoring until next section", ln); 154 section = 0; 155 return; 156 } 157 section = malloc (i); 158 strncpy (section, line + 1, i - 1); 159 section[i - 1] = '\0'; 160 return; 161 } 162 163 /* Deal with assignments. */ 164 for (i = 0; i < sz; i++) 165 if (cp[i] == '=') 166 { 167 /* If no section, we are ignoring the lines. */ 168 if (!section) 169 { 170 log_print ("conf_parse_line: %d: ignoring line due to no section", 171 ln); 172 return; 173 } 174 conf_set (section, line, i); 175 return; 176 } 177 178 /* Other non-empty lines are wierd. */ 179 i = strspn (line, " \t"); 180 if (line[i]) 181 log_print ("conf_parse_line: %d: syntax error", ln); 182 183 return; 184} 185 186/* Parse the mapped configuration file. */ 187static void 188conf_parse (void) 189{ 190 char *cp = conf_addr; 191 char *conf_end = conf_addr + conf_sz; 192 char *line; 193 194 line = cp; 195 while (cp < conf_end) 196 { 197 if (*cp == '\n') 198 { 199 /* Check for escaped newlines. */ 200 if (cp > conf_addr && *(cp - 1) == '\\') 201 *(cp - 1) = *cp = ' '; 202 else 203 { 204 *cp = '\0'; 205 conf_parse_line (line, cp - line); 206 line = cp + 1; 207 } 208 } 209 cp++; 210 } 211 if (cp != line) 212 log_print ("conf_parse: last line non-terminated, ignored."); 213} 214 215/* Open the config file and map it into our address space, then parse it. */ 216void 217conf_init (void) 218{ 219 int fd; 220 struct stat st; 221 222 /* 223 * Start by freeing potential existing configuration. 224 * 225 * XXX One could envision doing this late, surviving failures with just 226 * a warning log message that the new configuration did not get read 227 * and that the former one persists. 228 */ 229 if (conf_addr) 230 { 231 while (LIST_FIRST (&conf_bindings)) 232 LIST_REMOVE (LIST_FIRST (&conf_bindings), link); 233 free (conf_addr); 234 } 235 236 fd = open (conf_path, O_RDONLY); 237 if (fd == -1) 238 log_fatal ("open (\"%s\", O_RDONLY)", conf_path); 239 if (fstat (fd, &st) == -1) 240 log_fatal ("fstat (%d, &st)", fd); 241 conf_sz = st.st_size; 242 conf_addr = malloc (conf_sz); 243 if (!conf_addr) 244 log_fatal ("malloc (%d)", conf_sz); 245 /* XXX I assume short reads won't happen here. */ 246 if (read (fd, conf_addr, conf_sz) != conf_sz) 247 log_fatal ("read (%d, %p, %d)", fd, conf_addr, conf_sz); 248 close (fd); 249 250 LIST_INIT (&conf_bindings); 251 conf_parse (); 252 253#ifdef NEED_SYSDEP_APP 254 /* Let the application layer record on-demand keyed connections. */ 255 app_conf_init_hook (); 256#endif 257} 258 259/* 260 * Return the numeric value denoted by TAG in section SECTION or DEF 261 * if that tag does not exist. 262 */ 263int 264conf_get_num (char *section, char *tag, int def) 265{ 266 char *value = conf_get_str (section, tag); 267 268 if (value) 269 return atoi (value); 270 return def; 271} 272 273/* Validate X according to the range denoted by TAG in section SECTION. */ 274int 275conf_match_num (char *section, char *tag, int x) 276{ 277 char *value = conf_get_str (section, tag); 278 int val, min, max, n; 279 280 if (!value) 281 return 0; 282 n = sscanf (value, "%d,%d:%d", &val, &min, &max); 283 switch (n) 284 { 285 case 1: 286 log_debug (LOG_MISC, 90, "conf_match_num: %s:%s %d==%d?", section, tag, 287 val, x); 288 return x == val; 289 case 3: 290 log_debug (LOG_MISC, 90, "conf_match_num: %s:%s %d<=%d<=%d?", section, 291 tag, min, x, max); 292 return min <= x && max >= x; 293 default: 294 log_error ("conf_match_num: section %s tag %s: invalid number spec %s", 295 section, tag, value); 296 } 297 return 0; 298} 299 300/* Return the string value denoted by TAG in section SECTION. */ 301char * 302conf_get_str (char *section, char *tag) 303{ 304 struct conf_binding *cb; 305 306 for (cb = LIST_FIRST (&conf_bindings); cb; cb = LIST_NEXT (cb, link)) 307 if (strcasecmp (section, cb->section) == 0 308 && strcasecmp (tag, cb->tag) == 0) 309 { 310 log_debug (LOG_MISC, 60, "conf_get_str: (%s, %s) -> %s", section, 311 tag, cb->value); 312 return cb->value; 313 } 314 log_debug (LOG_MISC, 60, 315 "conf_get_str: configuration value not found (%s, %s)", section, 316 tag); 317 return 0; 318} 319 320struct conf_list * 321conf_get_list (char *section, char *tag) 322{ 323 char *liststr = 0, *p, *field; 324 struct conf_list *list = 0; 325 struct conf_list_node *node; 326 327 list = malloc (sizeof *list); 328 if (!list) 329 goto cleanup; 330 TAILQ_INIT (&list->fields); 331 list->cnt = 0; 332 liststr = conf_get_str (section, tag); 333 if (!liststr) 334 goto cleanup; 335 liststr = strdup (liststr); 336 if (!liststr) 337 goto cleanup; 338 p = liststr; 339 while ((field = strsep (&p, ", \t")) != NULL) 340 { 341 if (*field == '\0') 342 { 343 log_print ("conf_get_list: empty field, ignoring..."); 344 continue; 345 } 346 list->cnt++; 347 node = malloc (sizeof *node); 348 if (!node) 349 goto cleanup; 350 node->field = field; 351 TAILQ_INSERT_TAIL (&list->fields, node, link); 352 } 353 return list; 354 355 cleanup: 356 if (list) 357 conf_free_list (list); 358 if (liststr) 359 free (liststr); 360 return 0; 361} 362 363struct conf_list * 364conf_get_tag_list (char *section) 365{ 366 struct conf_list *list = 0; 367 struct conf_list_node *node; 368 struct conf_binding *cb; 369 370 list = malloc (sizeof *list); 371 if (!list) 372 goto cleanup; 373 TAILQ_INIT (&list->fields); 374 list->cnt = 0; 375 for (cb = LIST_FIRST (&conf_bindings); cb; cb = LIST_NEXT (cb, link)) 376 if (strcasecmp (section, cb->section) == 0) 377 { 378 list->cnt++; 379 node = malloc (sizeof *node); 380 if (!node) 381 goto cleanup; 382 node->field = cb->tag; 383 TAILQ_INSERT_TAIL (&list->fields, node, link); 384 } 385 return list; 386 387 cleanup: 388 if (list) 389 conf_free_list (list); 390 return 0; 391} 392 393/* Decode a PEM encoded buffer. */ 394int 395conf_decode_base64(u_int8_t *out, u_int32_t *len, u_char *buf) 396{ 397 u_int32_t c = 0; 398 u_int8_t c1, c2, c3, c4; 399 400 while (*buf) 401 { 402 if (*buf > 127 || (c1 = asc2bin[*buf]) == 255) 403 return 0; 404 buf++; 405 406 if (*buf > 127 || (c2 = asc2bin[*buf]) == 255) 407 return 0; 408 buf++; 409 410 if (*buf == '=') 411 { 412 c3 = c4 = 0; 413 c++; 414 415 /* Check last four bit */ 416 if (c2 & 0xF) 417 return 0; 418 419 if (!strcmp (buf, "==")) 420 buf++; 421 else 422 return 0; 423 } 424 else if (*buf > 127 || (c3 = asc2bin[*buf]) == 255) 425 return 0; 426 else 427 { 428 if (*++buf == '=') 429 { 430 c4 = 0; 431 c += 2; 432 433 /* Check last two bit */ 434 if (c3 & 3) 435 return 0; 436 437 if (strcmp(buf, "=")) 438 return 0; 439 440 } 441 else if (*buf > 127 || (c4 = asc2bin[*buf]) == 255) 442 return 0; 443 else 444 c += 3; 445 } 446 447 buf++; 448 *out++ = (c1 << 2) | (c2 >> 4); 449 *out++ = (c2 << 4) | (c3 >> 2); 450 *out++ = (c3 << 6) | c4; 451 } 452 453 *len = c; 454 return 1; 455 456} 457 458/* Read a line from a stream to the buffer. */ 459int 460conf_get_line (FILE *stream, char *buf, u_int32_t len) 461{ 462 char c; 463 464 while (len-- > 1) 465 { 466 c = fgetc (stream); 467 if (c == '\n') 468 { 469 *buf = 0; 470 return 1; 471 } 472 else if (c == EOF) 473 break; 474 475 *buf++ = c; 476 } 477 478 *buf = 0; 479 return 0; 480} 481 482void 483conf_free_list (struct conf_list *list) 484{ 485 while (TAILQ_FIRST (&list->fields)) 486 TAILQ_REMOVE (&list->fields, TAILQ_FIRST (&list->fields), link); 487 free (list); 488} 489