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