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