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

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