GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/acme-client/parse.y Lines: 0 339 0.0 %
Date: 2017-11-13 Branches: 0 277 0.0 %

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