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