1 /* $OpenBSD: parse.y,v 1.1 2016/09/18 20:18:25 benno Exp $ */ 2 3 /* 4 * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv> 5 * Copyright (c) 2016 Sebastian Benoit <benno@openbsd.org> 6 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> 7 * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org> 8 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> 9 * Copyright (c) 2001 Markus Friedl. All rights reserved. 10 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. 11 * Copyright (c) 2001 Theo de Raadt. All rights reserved. 12 * 13 * Permission to use, copy, modify, and distribute this software for any 14 * purpose with or without fee is hereby granted, provided that the above 15 * copyright notice and this permission notice appear in all copies. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 18 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 19 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 20 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 21 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 22 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 23 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 24 */ 25 26 %{ 27 #include <ctype.h> 28 #include <err.h> 29 #include <limits.h> 30 #include <sys/queue.h> 31 #include <stdarg.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <unistd.h> 36 #include <sys/stat.h> 37 #include <sys/types.h> 38 39 #include "parse.h" 40 41 TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); 42 static struct file { 43 TAILQ_ENTRY(file) entry; 44 FILE *stream; 45 char *name; 46 int lineno; 47 int errors; 48 } *file, *topfile; 49 struct file *pushfile(const char *); 50 int popfile(void); 51 int yyparse(void); 52 int yylex(void); 53 int yyerror(const char *, ...) 54 __attribute__((__format__ (printf, 1, 2))) 55 __attribute__((__nonnull__ (1))); 56 int kw_cmp(const void *, const void *); 57 int lookup(char *); 58 int lgetc(int); 59 int lungetc(int); 60 int findeol(void); 61 62 struct authority_c *conf_new_authority(struct acme_conf *, char *); 63 struct domain_c *conf_new_domain(struct acme_conf *, char *); 64 struct keyfile *conf_new_keyfile(struct acme_conf *, char *); 65 void clear_config(struct acme_conf *xconf); 66 int conf_check_file(char *); 67 68 TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); 69 struct sym { 70 TAILQ_ENTRY(sym) entry; 71 int used; 72 int persist; 73 char *nam; 74 char *val; 75 }; 76 int symset(const char *, const char *, int); 77 char *symget(const char *); 78 79 static struct acme_conf *conf; 80 static struct authority_c *auth; 81 static struct domain_c *domain; 82 static int errors = 0; 83 84 typedef struct { 85 union { 86 int64_t number; 87 char *string; 88 } v; 89 int lineno; 90 } YYSTYPE; 91 92 %} 93 94 %token AUTHORITY AGREEMENT URL API ACCOUNT 95 %token DOMAIN ALTERNATIVE NAMES CERT KEY SIGN WITH 96 %token YES NO 97 %token INCLUDE 98 %token ERROR 99 %token <v.string> STRING 100 %token <v.number> NUMBER 101 %type <v.string> string 102 103 %% 104 105 grammar : /* empty */ 106 | grammar include '\n' 107 | grammar varset '\n' 108 | grammar '\n' 109 | grammar authority '\n' 110 | grammar domain '\n' 111 | grammar error '\n' { file->errors++; } 112 ; 113 114 include : INCLUDE STRING { 115 struct file *nfile; 116 117 if ((nfile = pushfile($2)) == NULL) { 118 yyerror("failed to include file %s", $2); 119 free($2); 120 YYERROR; 121 } 122 free($2); 123 124 file = nfile; 125 lungetc('\n'); 126 } 127 ; 128 129 string : string STRING { 130 if (asprintf(&$$, "%s %s", $1, $2) == -1) { 131 free($1); 132 free($2); 133 yyerror("string: asprintf"); 134 YYERROR; 135 } 136 free($1); 137 free($2); 138 } 139 | STRING 140 ; 141 142 varset : STRING '=' string { 143 char *s = $1; 144 if (conf->opts & ACME_OPT_VERBOSE) 145 printf("%s = \"%s\"\n", $1, $3); 146 while (*s++) { 147 if (isspace((unsigned char)*s)) { 148 yyerror("macro name cannot contain " 149 "whitespace"); 150 YYERROR; 151 } 152 } 153 if (symset($1, $3, 0) == -1) 154 errx(EXIT_FAILURE, "cannot store variable"); 155 free($1); 156 free($3); 157 } 158 ; 159 160 optnl : '\n' optnl 161 | 162 ; 163 164 nl : '\n' optnl /* one newline or more */ 165 ; 166 167 comma : ',' 168 | /*empty*/ 169 ; 170 171 authority : AUTHORITY STRING { 172 char *s; 173 if ((s = strdup($2)) == NULL) 174 err(EXIT_FAILURE, "strdup"); 175 if ((auth = conf_new_authority(conf, s)) == NULL) { 176 free(s); 177 yyerror("authority already defined"); 178 YYERROR; 179 } 180 } '{' optnl authorityopts_l '}' { 181 /* XXX enforce minimum config here */ 182 auth = NULL; 183 } 184 ; 185 186 authorityopts_l : authorityopts_l authorityoptsl nl 187 | authorityoptsl optnl 188 ; 189 190 authorityoptsl : AGREEMENT URL STRING { 191 char *s; 192 if (auth->agreement != NULL) { 193 yyerror("duplicate agreement"); 194 YYERROR; 195 } 196 if ((s = strdup($3)) == NULL) 197 err(EXIT_FAILURE, "strdup"); 198 auth->agreement = s; 199 } 200 | API URL STRING { 201 char *s; 202 if (auth->api != NULL) { 203 yyerror("duplicate api"); 204 YYERROR; 205 } 206 if ((s = strdup($3)) == NULL) 207 err(EXIT_FAILURE, "strdup"); 208 auth->api = s; 209 } 210 | ACCOUNT KEY STRING { 211 char *s; 212 if (auth->account != NULL) { 213 yyerror("duplicate account"); 214 YYERROR; 215 } 216 if ((s = strdup($3)) == NULL) 217 err(EXIT_FAILURE, "strdup"); 218 if (!conf_check_file(s)) { 219 free(s); 220 YYERROR; 221 } 222 auth->account = s; 223 } 224 ; 225 226 domain : DOMAIN STRING { 227 char *s; 228 if ((s = strdup($2)) == NULL) 229 err(EXIT_FAILURE, "strdup"); 230 if (!domain_valid(s)) { 231 free(s); 232 yyerror("%s: bad domain syntax", s); 233 YYERROR; 234 } 235 if ((domain = conf_new_domain(conf, s)) == NULL) { 236 free(s); 237 yyerror("domain already defined"); 238 YYERROR; 239 } 240 } '{' optnl domainopts_l '}' { 241 /* XXX enforce minimum config here */ 242 domain = NULL; 243 } 244 ; 245 246 domainopts_l : domainopts_l domainoptsl nl 247 | domainoptsl optnl 248 ; 249 250 domainoptsl : ALTERNATIVE NAMES '{' altname_l '}' 251 | DOMAIN KEY STRING { 252 char *s; 253 if (domain->key != NULL) { 254 yyerror("duplicate key"); 255 YYERROR; 256 } 257 if ((s = strdup($3)) == NULL) 258 err(EXIT_FAILURE, "strdup"); 259 if (((void *)conf_new_keyfile(conf, s)) == NULL) { 260 free(s); 261 yyerror("domain key file already used"); 262 YYERROR; 263 } 264 domain->key = s; 265 } 266 | DOMAIN CERT STRING { 267 char *s; 268 if (domain->cert != NULL) { 269 yyerror("duplicate cert"); 270 YYERROR; 271 } 272 if ((s = strdup($3)) == NULL) 273 err(EXIT_FAILURE, "strdup"); 274 if (s[0] != '/') { 275 free(s); 276 yyerror("not an absolute path"); 277 YYERROR; 278 } 279 if (((void *)conf_new_keyfile(conf, s)) == NULL) { 280 free(s); 281 yyerror("domain cert file already used"); 282 YYERROR; 283 } 284 domain->cert = s; 285 } 286 | SIGN WITH STRING { 287 char *s; 288 if (domain->auth != NULL) { 289 yyerror("duplicate use"); 290 YYERROR; 291 } 292 if ((s = strdup($3)) == NULL) 293 err(EXIT_FAILURE, "strdup"); 294 if (authority_find(conf, s) == NULL) { 295 yyerror("use: unknown authority"); 296 YYERROR; 297 } 298 domain->auth = s; 299 } 300 ; 301 302 altname_l : altname comma altname_l 303 | altname 304 ; 305 306 altname : STRING { 307 char *s; 308 struct altname_c *ac; 309 if (!domain_valid($1)) { 310 yyerror("bad domain syntax"); 311 YYERROR; 312 } 313 if ((ac = calloc(1, sizeof(struct altname_c))) == NULL) 314 err(EXIT_FAILURE, "calloc"); 315 if ((s = strdup($1)) == NULL) { 316 free(ac); 317 err(EXIT_FAILURE, "strdup"); 318 } 319 ac->domain = s; 320 LIST_INSERT_HEAD(&domain->altname_list, ac, entry); 321 /* 322 * XXX we could check if altname is duplicate 323 * or identical to domain->domain 324 */ 325 } 326 327 %% 328 329 struct keywords { 330 const char *k_name; 331 int k_val; 332 }; 333 334 int 335 yyerror(const char *fmt, ...) 336 { 337 va_list ap; 338 char *msg; 339 340 file->errors++; 341 va_start(ap, fmt); 342 if (vasprintf(&msg, fmt, ap) == -1) 343 err(EXIT_FAILURE, "yyerror vasprintf"); 344 va_end(ap); 345 fprintf(stderr, "%s:%d: %s\n", file->name, yylval.lineno, msg); 346 free(msg); 347 return (0); 348 } 349 350 int 351 kw_cmp(const void *k, const void *e) 352 { 353 return (strcmp(k, ((const struct keywords *)e)->k_name)); 354 } 355 356 int 357 lookup(char *s) 358 { 359 /* this has to be sorted always */ 360 static const struct keywords keywords[] = { 361 {"account", ACCOUNT}, 362 {"agreement", AGREEMENT}, 363 {"alternative", ALTERNATIVE}, 364 {"api", API}, 365 {"authority", AUTHORITY}, 366 {"certificate", CERT}, 367 {"domain", DOMAIN}, 368 {"include", INCLUDE}, 369 {"key", KEY}, 370 {"names", NAMES}, 371 {"sign", SIGN}, 372 {"url", URL}, 373 {"with", WITH}, 374 }; 375 const struct keywords *p; 376 377 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 378 sizeof(keywords[0]), kw_cmp); 379 380 if (p) 381 return (p->k_val); 382 else 383 return (STRING); 384 } 385 386 #define MAXPUSHBACK 128 387 388 u_char *parsebuf; 389 int parseindex; 390 u_char pushback_buffer[MAXPUSHBACK]; 391 int pushback_index = 0; 392 393 int 394 lgetc(int quotec) 395 { 396 int c, next; 397 398 if (parsebuf) { 399 /* Read character from the parsebuffer instead of input. */ 400 if (parseindex >= 0) { 401 c = parsebuf[parseindex++]; 402 if (c != '\0') 403 return (c); 404 parsebuf = NULL; 405 } else 406 parseindex++; 407 } 408 409 if (pushback_index) 410 return (pushback_buffer[--pushback_index]); 411 412 if (quotec) { 413 if ((c = getc(file->stream)) == EOF) { 414 yyerror("reached end of file while parsing " 415 "quoted string"); 416 if (file == topfile || popfile() == EOF) 417 return (EOF); 418 return (quotec); 419 } 420 return (c); 421 } 422 423 while ((c = getc(file->stream)) == '\\') { 424 next = getc(file->stream); 425 if (next != '\n') { 426 c = next; 427 break; 428 } 429 yylval.lineno = file->lineno; 430 file->lineno++; 431 } 432 433 while (c == EOF) { 434 if (file == topfile || popfile() == EOF) 435 return (EOF); 436 c = getc(file->stream); 437 } 438 return (c); 439 } 440 441 int 442 lungetc(int c) 443 { 444 if (c == EOF) 445 return (EOF); 446 if (parsebuf) { 447 parseindex--; 448 if (parseindex >= 0) 449 return (c); 450 } 451 if (pushback_index < MAXPUSHBACK-1) 452 return (pushback_buffer[pushback_index++] = c); 453 else 454 return (EOF); 455 } 456 457 int 458 findeol(void) 459 { 460 int c; 461 462 parsebuf = NULL; 463 464 /* skip to either EOF or the first real EOL */ 465 while (1) { 466 if (pushback_index) 467 c = pushback_buffer[--pushback_index]; 468 else 469 c = lgetc(0); 470 if (c == '\n') { 471 file->lineno++; 472 break; 473 } 474 if (c == EOF) 475 break; 476 } 477 return (ERROR); 478 } 479 480 int 481 yylex(void) 482 { 483 u_char buf[8096]; 484 u_char *p, *val; 485 int quotec, next, c; 486 int token; 487 488 top: 489 p = buf; 490 while ((c = lgetc(0)) == ' ' || c == '\t') 491 ; /* nothing */ 492 493 yylval.lineno = file->lineno; 494 if (c == '#') 495 while ((c = lgetc(0)) != '\n' && c != EOF) 496 ; /* nothing */ 497 if (c == '$' && parsebuf == NULL) { 498 while (1) { 499 if ((c = lgetc(0)) == EOF) 500 return (0); 501 502 if (p + 1 >= buf + sizeof(buf) - 1) { 503 yyerror("string too long"); 504 return (findeol()); 505 } 506 if (isalnum(c) || c == '_') { 507 *p++ = c; 508 continue; 509 } 510 *p = '\0'; 511 lungetc(c); 512 break; 513 } 514 val = symget(buf); 515 if (val == NULL) { 516 yyerror("macro '%s' not defined", buf); 517 return (findeol()); 518 } 519 parsebuf = val; 520 parseindex = 0; 521 goto top; 522 } 523 524 switch (c) { 525 case '\'': 526 case '"': 527 quotec = c; 528 while (1) { 529 if ((c = lgetc(quotec)) == EOF) 530 return (0); 531 if (c == '\n') { 532 file->lineno++; 533 continue; 534 } else if (c == '\\') { 535 if ((next = lgetc(quotec)) == EOF) 536 return (0); 537 if (next == quotec || c == ' ' || c == '\t') 538 c = next; 539 else if (next == '\n') { 540 file->lineno++; 541 continue; 542 } else 543 lungetc(next); 544 } else if (c == quotec) { 545 *p = '\0'; 546 break; 547 } else if (c == '\0') { 548 yyerror("syntax error"); 549 return (findeol()); 550 } 551 if (p + 1 >= buf + sizeof(buf) - 1) { 552 yyerror("string too long"); 553 return (findeol()); 554 } 555 *p++ = c; 556 } 557 yylval.v.string = strdup(buf); 558 if (yylval.v.string == NULL) 559 err(EXIT_FAILURE, "yylex: strdup"); 560 return (STRING); 561 } 562 563 #define allowed_to_end_number(x) \ 564 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 565 566 if (c == '-' || isdigit(c)) { 567 do { 568 *p++ = c; 569 if ((unsigned)(p-buf) >= sizeof(buf)) { 570 yyerror("string too long"); 571 return (findeol()); 572 } 573 } while ((c = lgetc(0)) != EOF && isdigit(c)); 574 lungetc(c); 575 if (p == buf + 1 && buf[0] == '-') 576 goto nodigits; 577 if (c == EOF || allowed_to_end_number(c)) { 578 const char *errstr = NULL; 579 580 *p = '\0'; 581 yylval.v.number = strtonum(buf, LLONG_MIN, 582 LLONG_MAX, &errstr); 583 if (errstr) { 584 yyerror("\"%s\" invalid number: %s", 585 buf, errstr); 586 return (findeol()); 587 } 588 return (NUMBER); 589 } else { 590 nodigits: 591 while (p > buf + 1) 592 lungetc(*--p); 593 c = *--p; 594 if (c == '-') 595 return (c); 596 } 597 } 598 599 #define allowed_in_string(x) \ 600 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 601 x != '{' && x != '}' && \ 602 x != '!' && x != '=' && x != '#' && \ 603 x != ',')) 604 605 if (isalnum(c) || c == ':' || c == '_' || c == '/') { 606 do { 607 *p++ = c; 608 if ((unsigned)(p-buf) >= sizeof(buf)) { 609 yyerror("string too long"); 610 return (findeol()); 611 } 612 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 613 lungetc(c); 614 *p = '\0'; 615 if ((token = lookup(buf)) == STRING) { 616 if ((yylval.v.string = strdup(buf)) == NULL) 617 err(EXIT_FAILURE, "yylex: strdup"); 618 } 619 return (token); 620 } 621 if (c == '\n') { 622 yylval.lineno = file->lineno; 623 file->lineno++; 624 } 625 if (c == EOF) 626 return (0); 627 return (c); 628 } 629 630 struct file * 631 pushfile(const char *name) 632 { 633 struct file *nfile; 634 635 if ((nfile = calloc(1, sizeof(struct file))) == NULL) { 636 warn("malloc"); 637 return (NULL); 638 } 639 if ((nfile->name = strdup(name)) == NULL) { 640 warn("strdup"); 641 free(nfile); 642 return (NULL); 643 } 644 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 645 warn("%s", nfile->name); 646 free(nfile->name); 647 free(nfile); 648 return (NULL); 649 } 650 nfile->lineno = 1; 651 TAILQ_INSERT_TAIL(&files, nfile, entry); 652 return (nfile); 653 } 654 655 int 656 popfile(void) 657 { 658 struct file *prev; 659 660 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 661 prev->errors += file->errors; 662 663 TAILQ_REMOVE(&files, file, entry); 664 fclose(file->stream); 665 free(file->name); 666 free(file); 667 file = prev; 668 return (file ? 0 : EOF); 669 } 670 671 struct acme_conf * 672 parse_config(const char *filename, int opts) 673 { 674 struct sym *sym, *next; 675 676 if ((conf = calloc(1, sizeof(struct acme_conf))) == NULL) 677 err(EXIT_FAILURE, "parse_config"); 678 conf->opts = opts; 679 680 if ((file = pushfile(filename)) == NULL) { 681 free(conf); 682 return (NULL); 683 } 684 topfile = file; 685 686 LIST_INIT(&conf->authority_list); 687 LIST_INIT(&conf->domain_list); 688 689 yyparse(); 690 errors = file->errors; 691 popfile(); 692 693 /* Free macros and check which have not been used. */ 694 for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) { 695 next = TAILQ_NEXT(sym, entry); 696 if ((conf->opts & ACME_OPT_VERBOSE) && !sym->used) 697 fprintf(stderr, "warning: macro '%s' not " 698 "used\n", sym->nam); 699 if (!sym->persist) { 700 free(sym->nam); 701 free(sym->val); 702 TAILQ_REMOVE(&symhead, sym, entry); 703 free(sym); 704 } 705 } 706 707 if (errors) { 708 clear_config(conf); 709 return (NULL); 710 } 711 712 return (conf); 713 } 714 715 int 716 symset(const char *nam, const char *val, int persist) 717 { 718 struct sym *sym; 719 720 for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam); 721 sym = TAILQ_NEXT(sym, entry)) 722 ; /* nothing */ 723 724 if (sym != NULL) { 725 if (sym->persist == 1) 726 return (0); 727 else { 728 free(sym->nam); 729 free(sym->val); 730 TAILQ_REMOVE(&symhead, sym, entry); 731 free(sym); 732 } 733 } 734 if ((sym = calloc(1, sizeof(*sym))) == NULL) 735 return (-1); 736 737 sym->nam = strdup(nam); 738 if (sym->nam == NULL) { 739 free(sym); 740 return (-1); 741 } 742 sym->val = strdup(val); 743 if (sym->val == NULL) { 744 free(sym->nam); 745 free(sym); 746 return (-1); 747 } 748 sym->used = 0; 749 sym->persist = persist; 750 TAILQ_INSERT_TAIL(&symhead, sym, entry); 751 return (0); 752 } 753 754 int 755 cmdline_symset(char *s) 756 { 757 char *sym, *val; 758 int ret; 759 size_t len; 760 761 if ((val = strrchr(s, '=')) == NULL) 762 return (-1); 763 764 len = strlen(s) - strlen(val) + 1; 765 if ((sym = malloc(len)) == NULL) 766 errx(EXIT_FAILURE, "cmdline_symset: malloc"); 767 768 strlcpy(sym, s, len); 769 770 ret = symset(sym, val + 1, 1); 771 free(sym); 772 773 return (ret); 774 } 775 776 char * 777 symget(const char *nam) 778 { 779 struct sym *sym; 780 781 TAILQ_FOREACH(sym, &symhead, entry) 782 if (strcmp(nam, sym->nam) == 0) { 783 sym->used = 1; 784 return (sym->val); 785 } 786 return (NULL); 787 } 788 789 struct authority_c * 790 conf_new_authority(struct acme_conf *c, char *s) 791 { 792 struct authority_c *a; 793 794 a = authority_find(c, s); 795 if (a) 796 return (NULL); 797 if ((a = calloc(1, sizeof(struct authority_c))) == NULL) 798 err(EXIT_FAILURE, "calloc"); 799 LIST_INSERT_HEAD(&c->authority_list, a, entry); 800 801 a->name = s; 802 return (a); 803 } 804 805 struct authority_c * 806 authority_find(struct acme_conf *c, char *s) 807 { 808 struct authority_c *a; 809 810 LIST_FOREACH(a, &c->authority_list, entry) { 811 if (strncmp(a->name, s, AUTH_MAXLEN) == 0) { 812 return (a); 813 } 814 } 815 return (NULL); 816 } 817 818 struct authority_c * 819 authority_find0(struct acme_conf *c) 820 { 821 struct authority_c *a, *b; 822 a = b = NULL; 823 824 LIST_FOREACH(a, &c->authority_list, entry) 825 b = a; 826 827 return (b); 828 } 829 830 struct domain_c * 831 conf_new_domain(struct acme_conf *c, char *s) 832 { 833 struct domain_c *d; 834 835 d = domain_find(c, s); 836 if (d) 837 return (NULL); 838 if ((d = calloc(1, sizeof(struct domain_c))) == NULL) 839 err(EXIT_FAILURE, "calloc"); 840 LIST_INSERT_HEAD(&c->domain_list, d, entry); 841 842 d->domain = s; 843 LIST_INIT(&d->altname_list); 844 845 return (d); 846 } 847 848 struct domain_c * 849 domain_find(struct acme_conf *c, char *s) 850 { 851 struct domain_c *d; 852 853 LIST_FOREACH(d, &c->domain_list, entry) { 854 if (strncmp(d->domain, s, DOMAIN_MAXLEN) == 0) { 855 return (d); 856 } 857 } 858 return (NULL); 859 } 860 861 struct keyfile * 862 conf_new_keyfile(struct acme_conf *c, char *s) 863 { 864 struct keyfile *k; 865 866 LIST_FOREACH(k, &c->used_key_list, entry) { 867 if (strncmp(k->name, s, PATH_MAX) == 0) { 868 return (NULL); 869 } 870 } 871 872 if ((k = calloc(1, sizeof(struct keyfile))) == NULL) 873 err(EXIT_FAILURE, "calloc"); 874 LIST_INSERT_HEAD(&c->used_key_list, k, entry); 875 876 k->name = s; 877 return (k); 878 } 879 880 void 881 clear_config(struct acme_conf *xconf) 882 { 883 struct authority_c *a; 884 struct domain_c *d; 885 struct altname_c *ac; 886 887 while ((a = LIST_FIRST(&xconf->authority_list)) != NULL) { 888 LIST_REMOVE(a, entry); 889 free(a); 890 } 891 while ((d = LIST_FIRST(&xconf->domain_list)) != NULL) { 892 while ((ac = LIST_FIRST(&d->altname_list)) != NULL) { 893 LIST_REMOVE(ac, entry); 894 free(ac); 895 } 896 LIST_REMOVE(d, entry); 897 free(d); 898 } 899 free(xconf); 900 } 901 902 /* 903 * This isn't RFC1035 compliant, but does the bare minimum in making 904 * sure that we don't get bogus domain names on the command line, which 905 * might otherwise screw up our directory structure. 906 * Returns zero on failure, non-zero on success. 907 */ 908 int 909 domain_valid(const char *cp) 910 { 911 912 for ( ; '\0' != *cp; cp++) 913 if (!('.' == *cp || '-' == *cp || 914 '_' == *cp || isalnum((int)*cp))) 915 return (0); 916 return (1); 917 } 918 919 int 920 conf_check_file(char *s) 921 { 922 struct stat st; 923 924 if (s[0] != '/') { 925 warnx("%s: not an absolute path", s); 926 return (0); 927 } 928 if (stat(s, &st)) { 929 warn("cannot stat %s", s); 930 return (0); 931 } 932 if (st.st_uid != 0 && st.st_uid != getuid()) { 933 warnx("%s: owner not root or current user", s); 934 return (0); 935 } 936 if (st.st_mode & (S_IRWXG | S_IRWXO)) { 937 warnx("%s: group read/writable or world read/writable", s); 938 return (0); 939 } 940 return (1); 941 } 942