1 /* $OpenBSD: parse.y,v 1.21 2018/04/26 14:12:19 krw 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 <sys/types.h> 28 #include <sys/queue.h> 29 #include <sys/stat.h> 30 #include <ctype.h> 31 #include <err.h> 32 #include <limits.h> 33 #include <stdarg.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <unistd.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 *); 66 void print_config(struct acme_conf *); 67 int conf_check_file(char *, int); 68 69 TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); 70 struct sym { 71 TAILQ_ENTRY(sym) entry; 72 int used; 73 int persist; 74 char *nam; 75 char *val; 76 }; 77 int symset(const char *, const char *, int); 78 char *symget(const char *); 79 80 static struct acme_conf *conf; 81 static struct authority_c *auth; 82 static struct domain_c *domain; 83 static int errors = 0; 84 85 typedef struct { 86 union { 87 int64_t number; 88 char *string; 89 } v; 90 int lineno; 91 } YYSTYPE; 92 93 %} 94 95 %token AUTHORITY URL API ACCOUNT 96 %token DOMAIN ALTERNATIVE NAMES CERT FULL CHAIN KEY SIGN WITH CHALLENGEDIR 97 %token YES NO 98 %token INCLUDE 99 %token ERROR 100 %token <v.string> STRING 101 %token <v.number> NUMBER 102 %type <v.string> string 103 104 %% 105 106 grammar : /* empty */ 107 | grammar include '\n' 108 | grammar varset '\n' 109 | grammar '\n' 110 | grammar authority '\n' 111 | grammar domain '\n' 112 | grammar error '\n' { file->errors++; } 113 ; 114 115 include : INCLUDE STRING { 116 struct file *nfile; 117 118 if ((nfile = pushfile($2)) == NULL) { 119 yyerror("failed to include file %s", $2); 120 free($2); 121 YYERROR; 122 } 123 free($2); 124 125 file = nfile; 126 lungetc('\n'); 127 } 128 ; 129 130 string : string STRING { 131 if (asprintf(&$$, "%s %s", $1, $2) == -1) { 132 free($1); 133 free($2); 134 yyerror("string: asprintf"); 135 YYERROR; 136 } 137 free($1); 138 free($2); 139 } 140 | STRING 141 ; 142 143 varset : STRING '=' string { 144 char *s = $1; 145 if (conf->opts & ACME_OPT_VERBOSE) 146 printf("%s = \"%s\"\n", $1, $3); 147 while (*s++) { 148 if (isspace((unsigned char)*s)) { 149 yyerror("macro name cannot contain " 150 "whitespace"); 151 free($1); 152 free($3); 153 YYERROR; 154 } 155 } 156 if (symset($1, $3, 0) == -1) 157 errx(EXIT_FAILURE, "cannot store variable"); 158 free($1); 159 free($3); 160 } 161 ; 162 163 optnl : '\n' optnl 164 | 165 ; 166 167 nl : '\n' optnl /* one newline or more */ 168 ; 169 170 comma : ',' 171 | /*empty*/ 172 ; 173 174 authority : AUTHORITY STRING { 175 char *s; 176 if ((s = strdup($2)) == NULL) 177 err(EXIT_FAILURE, "strdup"); 178 if ((auth = conf_new_authority(conf, s)) == NULL) { 179 free(s); 180 yyerror("authority already defined"); 181 YYERROR; 182 } 183 } '{' optnl authorityopts_l '}' { 184 /* XXX enforce minimum config here */ 185 auth = NULL; 186 } 187 ; 188 189 authorityopts_l : authorityopts_l authorityoptsl nl 190 | authorityoptsl optnl 191 ; 192 193 authorityoptsl : API URL STRING { 194 char *s; 195 if (auth->api != NULL) { 196 yyerror("duplicate api"); 197 YYERROR; 198 } 199 if ((s = strdup($3)) == NULL) 200 err(EXIT_FAILURE, "strdup"); 201 auth->api = s; 202 } 203 | ACCOUNT KEY STRING { 204 char *s; 205 if (auth->account != NULL) { 206 yyerror("duplicate account"); 207 YYERROR; 208 } 209 if ((s = strdup($3)) == NULL) 210 err(EXIT_FAILURE, "strdup"); 211 auth->account = s; 212 } 213 ; 214 215 domain : DOMAIN STRING { 216 char *s; 217 if ((s = strdup($2)) == NULL) 218 err(EXIT_FAILURE, "strdup"); 219 if (!domain_valid(s)) { 220 yyerror("%s: bad domain syntax", s); 221 free(s); 222 YYERROR; 223 } 224 if ((domain = conf_new_domain(conf, s)) == NULL) { 225 free(s); 226 yyerror("domain already defined"); 227 YYERROR; 228 } 229 } '{' optnl domainopts_l '}' { 230 /* enforce minimum config here */ 231 if (domain->key == NULL) { 232 yyerror("no domain key file specified for " 233 "domain %s", domain->domain); 234 YYERROR; 235 } 236 if (domain->cert == NULL && domain->fullchain == NULL) { 237 yyerror("at least certificate file or full " 238 "certificate chain file must be specified " 239 "for domain %s", domain->domain); 240 YYERROR; 241 } 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 (!conf_check_file(s, 260 (conf->opts & ACME_OPT_NEWDKEY))) { 261 free(s); 262 YYERROR; 263 } 264 if ((conf_new_keyfile(conf, s)) == NULL) { 265 free(s); 266 yyerror("domain key file already used"); 267 YYERROR; 268 } 269 domain->key = s; 270 } 271 | DOMAIN CERT STRING { 272 char *s; 273 if (domain->cert != NULL) { 274 yyerror("duplicate cert"); 275 YYERROR; 276 } 277 if ((s = strdup($3)) == NULL) 278 err(EXIT_FAILURE, "strdup"); 279 if (s[0] != '/') { 280 free(s); 281 yyerror("not an absolute path"); 282 YYERROR; 283 } 284 if ((conf_new_keyfile(conf, s)) == NULL) { 285 free(s); 286 yyerror("domain cert file already used"); 287 YYERROR; 288 } 289 domain->cert = s; 290 } 291 | DOMAIN CHAIN CERT STRING { 292 char *s; 293 if (domain->chain != NULL) { 294 yyerror("duplicate chain"); 295 YYERROR; 296 } 297 if ((s = strdup($4)) == NULL) 298 err(EXIT_FAILURE, "strdup"); 299 if ((conf_new_keyfile(conf, s)) == NULL) { 300 free(s); 301 yyerror("domain chain file already used"); 302 YYERROR; 303 } 304 domain->chain = s; 305 } 306 | DOMAIN FULL CHAIN CERT STRING { 307 char *s; 308 if (domain->fullchain != NULL) { 309 yyerror("duplicate chain"); 310 YYERROR; 311 } 312 if ((s = strdup($5)) == NULL) 313 err(EXIT_FAILURE, "strdup"); 314 if ((conf_new_keyfile(conf, s)) == NULL) { 315 free(s); 316 yyerror("domain full chain file already used"); 317 YYERROR; 318 } 319 domain->fullchain = s; 320 } 321 | SIGN WITH STRING { 322 char *s; 323 if (domain->auth != NULL) { 324 yyerror("duplicate use"); 325 YYERROR; 326 } 327 if ((s = strdup($3)) == NULL) 328 err(EXIT_FAILURE, "strdup"); 329 if (authority_find(conf, s) == NULL) { 330 yyerror("use: unknown authority"); 331 free(s); 332 YYERROR; 333 } 334 domain->auth = s; 335 } 336 | CHALLENGEDIR STRING { 337 char *s; 338 if (domain->challengedir != NULL) { 339 yyerror("duplicate challengedir"); 340 YYERROR; 341 } 342 if ((s = strdup($2)) == NULL) 343 err(EXIT_FAILURE, "strdup"); 344 domain->challengedir = s; 345 } 346 ; 347 348 altname_l : altname comma altname_l 349 | altname 350 ; 351 352 altname : STRING { 353 char *s; 354 struct altname_c *ac; 355 if (!domain_valid($1)) { 356 yyerror("bad domain syntax"); 357 YYERROR; 358 } 359 if ((ac = calloc(1, sizeof(struct altname_c))) == NULL) 360 err(EXIT_FAILURE, "calloc"); 361 if ((s = strdup($1)) == NULL) { 362 free(ac); 363 err(EXIT_FAILURE, "strdup"); 364 } 365 ac->domain = s; 366 TAILQ_INSERT_TAIL(&domain->altname_list, ac, entry); 367 domain->altname_count++; 368 /* 369 * XXX we could check if altname is duplicate 370 * or identical to domain->domain 371 */ 372 } 373 374 %% 375 376 struct keywords { 377 const char *k_name; 378 int k_val; 379 }; 380 381 int 382 yyerror(const char *fmt, ...) 383 { 384 va_list ap; 385 char *msg; 386 387 file->errors++; 388 va_start(ap, fmt); 389 if (vasprintf(&msg, fmt, ap) == -1) 390 err(EXIT_FAILURE, "yyerror vasprintf"); 391 va_end(ap); 392 fprintf(stderr, "%s:%d: %s\n", file->name, yylval.lineno, msg); 393 free(msg); 394 return (0); 395 } 396 397 int 398 kw_cmp(const void *k, const void *e) 399 { 400 return (strcmp(k, ((const struct keywords *)e)->k_name)); 401 } 402 403 int 404 lookup(char *s) 405 { 406 /* this has to be sorted always */ 407 static const struct keywords keywords[] = { 408 {"account", ACCOUNT}, 409 {"alternative", ALTERNATIVE}, 410 {"api", API}, 411 {"authority", AUTHORITY}, 412 {"certificate", CERT}, 413 {"chain", CHAIN}, 414 {"challengedir", CHALLENGEDIR}, 415 {"domain", DOMAIN}, 416 {"full", FULL}, 417 {"include", INCLUDE}, 418 {"key", KEY}, 419 {"names", NAMES}, 420 {"sign", SIGN}, 421 {"url", URL}, 422 {"with", WITH}, 423 }; 424 const struct keywords *p; 425 426 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 427 sizeof(keywords[0]), kw_cmp); 428 429 if (p) 430 return (p->k_val); 431 else 432 return (STRING); 433 } 434 435 #define MAXPUSHBACK 128 436 437 u_char *parsebuf; 438 int parseindex; 439 u_char pushback_buffer[MAXPUSHBACK]; 440 int pushback_index = 0; 441 442 int 443 lgetc(int quotec) 444 { 445 int c, next; 446 447 if (parsebuf) { 448 /* Read character from the parsebuffer instead of input. */ 449 if (parseindex >= 0) { 450 c = parsebuf[parseindex++]; 451 if (c != '\0') 452 return (c); 453 parsebuf = NULL; 454 } else 455 parseindex++; 456 } 457 458 if (pushback_index) 459 return (pushback_buffer[--pushback_index]); 460 461 if (quotec) { 462 if ((c = getc(file->stream)) == EOF) { 463 yyerror("reached end of file while parsing " 464 "quoted string"); 465 if (file == topfile || popfile() == EOF) 466 return (EOF); 467 return (quotec); 468 } 469 return (c); 470 } 471 472 while ((c = getc(file->stream)) == '\\') { 473 next = getc(file->stream); 474 if (next != '\n') { 475 c = next; 476 break; 477 } 478 yylval.lineno = file->lineno; 479 file->lineno++; 480 } 481 482 while (c == EOF) { 483 if (file == topfile || popfile() == EOF) 484 return (EOF); 485 c = getc(file->stream); 486 } 487 return (c); 488 } 489 490 int 491 lungetc(int c) 492 { 493 if (c == EOF) 494 return (EOF); 495 if (parsebuf) { 496 parseindex--; 497 if (parseindex >= 0) 498 return (c); 499 } 500 if (pushback_index < MAXPUSHBACK-1) 501 return (pushback_buffer[pushback_index++] = c); 502 else 503 return (EOF); 504 } 505 506 int 507 findeol(void) 508 { 509 int c; 510 511 parsebuf = NULL; 512 513 /* skip to either EOF or the first real EOL */ 514 while (1) { 515 if (pushback_index) 516 c = pushback_buffer[--pushback_index]; 517 else 518 c = lgetc(0); 519 if (c == '\n') { 520 file->lineno++; 521 break; 522 } 523 if (c == EOF) 524 break; 525 } 526 return (ERROR); 527 } 528 529 int 530 yylex(void) 531 { 532 u_char buf[8096]; 533 u_char *p, *val; 534 int quotec, next, c; 535 int token; 536 537 top: 538 p = buf; 539 while ((c = lgetc(0)) == ' ' || c == '\t') 540 ; /* nothing */ 541 542 yylval.lineno = file->lineno; 543 if (c == '#') 544 while ((c = lgetc(0)) != '\n' && c != EOF) 545 ; /* nothing */ 546 if (c == '$' && parsebuf == NULL) { 547 while (1) { 548 if ((c = lgetc(0)) == EOF) 549 return (0); 550 551 if (p + 1 >= buf + sizeof(buf) - 1) { 552 yyerror("string too long"); 553 return (findeol()); 554 } 555 if (isalnum(c) || c == '_') { 556 *p++ = c; 557 continue; 558 } 559 *p = '\0'; 560 lungetc(c); 561 break; 562 } 563 val = symget(buf); 564 if (val == NULL) { 565 yyerror("macro '%s' not defined", buf); 566 return (findeol()); 567 } 568 parsebuf = val; 569 parseindex = 0; 570 goto top; 571 } 572 573 switch (c) { 574 case '\'': 575 case '"': 576 quotec = c; 577 while (1) { 578 if ((c = lgetc(quotec)) == EOF) 579 return (0); 580 if (c == '\n') { 581 file->lineno++; 582 continue; 583 } else if (c == '\\') { 584 if ((next = lgetc(quotec)) == EOF) 585 return (0); 586 if (next == quotec || c == ' ' || c == '\t') 587 c = next; 588 else if (next == '\n') { 589 file->lineno++; 590 continue; 591 } else 592 lungetc(next); 593 } else if (c == quotec) { 594 *p = '\0'; 595 break; 596 } else if (c == '\0') { 597 yyerror("syntax error"); 598 return (findeol()); 599 } 600 if (p + 1 >= buf + sizeof(buf) - 1) { 601 yyerror("string too long"); 602 return (findeol()); 603 } 604 *p++ = c; 605 } 606 yylval.v.string = strdup(buf); 607 if (yylval.v.string == NULL) 608 err(EXIT_FAILURE, "yylex: strdup"); 609 return (STRING); 610 } 611 612 #define allowed_to_end_number(x) \ 613 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 614 615 if (c == '-' || isdigit(c)) { 616 do { 617 *p++ = c; 618 if ((unsigned)(p-buf) >= sizeof(buf)) { 619 yyerror("string too long"); 620 return (findeol()); 621 } 622 } while ((c = lgetc(0)) != EOF && isdigit(c)); 623 lungetc(c); 624 if (p == buf + 1 && buf[0] == '-') 625 goto nodigits; 626 if (c == EOF || allowed_to_end_number(c)) { 627 const char *errstr = NULL; 628 629 *p = '\0'; 630 yylval.v.number = strtonum(buf, LLONG_MIN, 631 LLONG_MAX, &errstr); 632 if (errstr) { 633 yyerror("\"%s\" invalid number: %s", 634 buf, errstr); 635 return (findeol()); 636 } 637 return (NUMBER); 638 } else { 639 nodigits: 640 while (p > buf + 1) 641 lungetc(*--p); 642 c = *--p; 643 if (c == '-') 644 return (c); 645 } 646 } 647 648 #define allowed_in_string(x) \ 649 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 650 x != '{' && x != '}' && \ 651 x != '!' && x != '=' && x != '#' && \ 652 x != ',')) 653 654 if (isalnum(c) || c == ':' || c == '_') { 655 do { 656 *p++ = c; 657 if ((unsigned)(p-buf) >= sizeof(buf)) { 658 yyerror("string too long"); 659 return (findeol()); 660 } 661 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 662 lungetc(c); 663 *p = '\0'; 664 if ((token = lookup(buf)) == STRING) { 665 if ((yylval.v.string = strdup(buf)) == NULL) 666 err(EXIT_FAILURE, "yylex: strdup"); 667 } 668 return (token); 669 } 670 if (c == '\n') { 671 yylval.lineno = file->lineno; 672 file->lineno++; 673 } 674 if (c == EOF) 675 return (0); 676 return (c); 677 } 678 679 struct file * 680 pushfile(const char *name) 681 { 682 struct file *nfile; 683 684 if ((nfile = calloc(1, sizeof(struct file))) == NULL) { 685 warn("malloc"); 686 return (NULL); 687 } 688 if ((nfile->name = strdup(name)) == NULL) { 689 warn("strdup"); 690 free(nfile); 691 return (NULL); 692 } 693 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 694 warn("%s", nfile->name); 695 free(nfile->name); 696 free(nfile); 697 return (NULL); 698 } 699 nfile->lineno = 1; 700 TAILQ_INSERT_TAIL(&files, nfile, entry); 701 return (nfile); 702 } 703 704 int 705 popfile(void) 706 { 707 struct file *prev; 708 709 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 710 prev->errors += file->errors; 711 712 TAILQ_REMOVE(&files, file, entry); 713 fclose(file->stream); 714 free(file->name); 715 free(file); 716 file = prev; 717 return (file ? 0 : EOF); 718 } 719 720 struct acme_conf * 721 parse_config(const char *filename, int opts) 722 { 723 struct sym *sym, *next; 724 725 if ((conf = calloc(1, sizeof(struct acme_conf))) == NULL) 726 err(EXIT_FAILURE, "parse_config"); 727 conf->opts = opts; 728 729 if ((file = pushfile(filename)) == NULL) { 730 free(conf); 731 return (NULL); 732 } 733 topfile = file; 734 735 TAILQ_INIT(&conf->authority_list); 736 TAILQ_INIT(&conf->domain_list); 737 738 yyparse(); 739 errors = file->errors; 740 popfile(); 741 742 /* Free macros and check which have not been used. */ 743 TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) { 744 if ((conf->opts & ACME_OPT_VERBOSE) && !sym->used) 745 fprintf(stderr, "warning: macro '%s' not " 746 "used\n", sym->nam); 747 if (!sym->persist) { 748 free(sym->nam); 749 free(sym->val); 750 TAILQ_REMOVE(&symhead, sym, entry); 751 free(sym); 752 } 753 } 754 755 if (errors) { 756 clear_config(conf); 757 return (NULL); 758 } 759 760 if (opts & ACME_OPT_CHECK) 761 print_config(conf); 762 763 return (conf); 764 } 765 766 int 767 symset(const char *nam, const char *val, int persist) 768 { 769 struct sym *sym; 770 771 TAILQ_FOREACH(sym, &symhead, entry) { 772 if (strcmp(nam, sym->nam) == 0) 773 break; 774 } 775 776 if (sym != NULL) { 777 if (sym->persist == 1) 778 return (0); 779 else { 780 free(sym->nam); 781 free(sym->val); 782 TAILQ_REMOVE(&symhead, sym, entry); 783 free(sym); 784 } 785 } 786 if ((sym = calloc(1, sizeof(*sym))) == NULL) 787 return (-1); 788 789 sym->nam = strdup(nam); 790 if (sym->nam == NULL) { 791 free(sym); 792 return (-1); 793 } 794 sym->val = strdup(val); 795 if (sym->val == NULL) { 796 free(sym->nam); 797 free(sym); 798 return (-1); 799 } 800 sym->used = 0; 801 sym->persist = persist; 802 TAILQ_INSERT_TAIL(&symhead, sym, entry); 803 return (0); 804 } 805 806 int 807 cmdline_symset(char *s) 808 { 809 char *sym, *val; 810 int ret; 811 size_t len; 812 813 if ((val = strrchr(s, '=')) == NULL) 814 return (-1); 815 816 len = strlen(s) - strlen(val) + 1; 817 if ((sym = malloc(len)) == NULL) 818 errx(EXIT_FAILURE, "cmdline_symset: malloc"); 819 820 strlcpy(sym, s, len); 821 822 ret = symset(sym, val + 1, 1); 823 free(sym); 824 825 return (ret); 826 } 827 828 char * 829 symget(const char *nam) 830 { 831 struct sym *sym; 832 833 TAILQ_FOREACH(sym, &symhead, entry) { 834 if (strcmp(nam, sym->nam) == 0) { 835 sym->used = 1; 836 return (sym->val); 837 } 838 } 839 return (NULL); 840 } 841 842 struct authority_c * 843 conf_new_authority(struct acme_conf *c, char *s) 844 { 845 struct authority_c *a; 846 847 a = authority_find(c, s); 848 if (a) 849 return (NULL); 850 if ((a = calloc(1, sizeof(struct authority_c))) == NULL) 851 err(EXIT_FAILURE, "calloc"); 852 TAILQ_INSERT_TAIL(&c->authority_list, a, entry); 853 854 a->name = s; 855 return (a); 856 } 857 858 struct authority_c * 859 authority_find(struct acme_conf *c, char *s) 860 { 861 struct authority_c *a; 862 863 TAILQ_FOREACH(a, &c->authority_list, entry) { 864 if (strncmp(a->name, s, AUTH_MAXLEN) == 0) { 865 return (a); 866 } 867 } 868 return (NULL); 869 } 870 871 struct authority_c * 872 authority_find0(struct acme_conf *c) 873 { 874 return (TAILQ_FIRST(&c->authority_list)); 875 } 876 877 struct domain_c * 878 conf_new_domain(struct acme_conf *c, char *s) 879 { 880 struct domain_c *d; 881 882 d = domain_find(c, s); 883 if (d) 884 return (NULL); 885 if ((d = calloc(1, sizeof(struct domain_c))) == NULL) 886 err(EXIT_FAILURE, "calloc"); 887 TAILQ_INSERT_TAIL(&c->domain_list, d, entry); 888 889 d->domain = s; 890 TAILQ_INIT(&d->altname_list); 891 892 return (d); 893 } 894 895 struct domain_c * 896 domain_find(struct acme_conf *c, char *s) 897 { 898 struct domain_c *d; 899 900 TAILQ_FOREACH(d, &c->domain_list, entry) { 901 if (strncmp(d->domain, s, DOMAIN_MAXLEN) == 0) { 902 return (d); 903 } 904 } 905 return (NULL); 906 } 907 908 struct keyfile * 909 conf_new_keyfile(struct acme_conf *c, char *s) 910 { 911 struct keyfile *k; 912 913 LIST_FOREACH(k, &c->used_key_list, entry) { 914 if (strncmp(k->name, s, PATH_MAX) == 0) { 915 return (NULL); 916 } 917 } 918 919 if ((k = calloc(1, sizeof(struct keyfile))) == NULL) 920 err(EXIT_FAILURE, "calloc"); 921 LIST_INSERT_HEAD(&c->used_key_list, k, entry); 922 923 k->name = s; 924 return (k); 925 } 926 927 void 928 clear_config(struct acme_conf *xconf) 929 { 930 struct authority_c *a; 931 struct domain_c *d; 932 struct altname_c *ac; 933 934 while ((a = TAILQ_FIRST(&xconf->authority_list)) != NULL) { 935 TAILQ_REMOVE(&xconf->authority_list, a, entry); 936 free(a); 937 } 938 while ((d = TAILQ_FIRST(&xconf->domain_list)) != NULL) { 939 while ((ac = TAILQ_FIRST(&d->altname_list)) != NULL) { 940 TAILQ_REMOVE(&d->altname_list, ac, entry); 941 free(ac); 942 } 943 TAILQ_REMOVE(&xconf->domain_list, d, entry); 944 free(d); 945 } 946 free(xconf); 947 } 948 949 void 950 print_config(struct acme_conf *xconf) 951 { 952 struct authority_c *a; 953 struct domain_c *d; 954 struct altname_c *ac; 955 int f; 956 957 TAILQ_FOREACH(a, &xconf->authority_list, entry) { 958 printf("authority %s {\n", a->name); 959 if (a->api != NULL) 960 printf("\tapi url \"%s\"\n", a->api); 961 if (a->account != NULL) 962 printf("\taccount key \"%s\"\n", a->account); 963 printf("}\n\n"); 964 } 965 TAILQ_FOREACH(d, &xconf->domain_list, entry) { 966 f = 0; 967 printf("domain %s {\n", d->domain); 968 TAILQ_FOREACH(ac, &d->altname_list, entry) { 969 if (!f) 970 printf("\talternative names { "); 971 if (ac->domain != NULL) { 972 printf("%s%s", f ? ", " : " ", ac->domain); 973 f = 1; 974 } 975 } 976 if (f) 977 printf("}\n"); 978 if (d->key != NULL) 979 printf("\tdomain key \"%s\"\n", d->key); 980 if (d->cert != NULL) 981 printf("\tdomain certificate \"%s\"\n", d->cert); 982 if (d->chain != NULL) 983 printf("\tdomain chain certificate \"%s\"\n", d->chain); 984 if (d->fullchain != NULL) 985 printf("\tdomain full chain certificate \"%s\"\n", 986 d->fullchain); 987 if (d->auth != NULL) 988 printf("\tsign with \"%s\"\n", d->auth); 989 if (d->challengedir != NULL) 990 printf("\tchallengedir \"%s\"\n", d->challengedir); 991 printf("}\n\n"); 992 } 993 } 994 995 /* 996 * This isn't RFC1035 compliant, but does the bare minimum in making 997 * sure that we don't get bogus domain names on the command line, which 998 * might otherwise screw up our directory structure. 999 * Returns zero on failure, non-zero on success. 1000 */ 1001 int 1002 domain_valid(const char *cp) 1003 { 1004 1005 for ( ; *cp != '\0'; cp++) 1006 if (!(*cp == '.' || *cp == '-' || 1007 *cp == '_' || isalnum((int)*cp))) 1008 return (0); 1009 return (1); 1010 } 1011 1012 int 1013 conf_check_file(char *s, int dontstat) 1014 { 1015 struct stat st; 1016 1017 if (s[0] != '/') { 1018 warnx("%s: not an absolute path", s); 1019 return (0); 1020 } 1021 if (dontstat) 1022 return (1); 1023 if (stat(s, &st)) { 1024 warn("cannot stat %s", s); 1025 return (0); 1026 } 1027 if (st.st_mode & (S_IRWXG | S_IRWXO)) { 1028 warnx("%s: group read/writable or world read/writable", s); 1029 return (0); 1030 } 1031 return (1); 1032 } 1033