xref: /openbsd-src/usr.sbin/acme-client/parse.y (revision 4a50067c81ccb6fd6654540923abf553683ef01d)
1*4a50067cSflorian /*	$OpenBSD: parse.y,v 1.45 2022/12/15 08:06:13 florian Exp $ */
23943d840Sbenno 
33943d840Sbenno /*
43943d840Sbenno  * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
53943d840Sbenno  * Copyright (c) 2016 Sebastian Benoit <benno@openbsd.org>
63943d840Sbenno  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
73943d840Sbenno  * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
83943d840Sbenno  * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
93943d840Sbenno  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
103943d840Sbenno  * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
113943d840Sbenno  * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
123943d840Sbenno  *
133943d840Sbenno  * Permission to use, copy, modify, and distribute this software for any
143943d840Sbenno  * purpose with or without fee is hereby granted, provided that the above
153943d840Sbenno  * copyright notice and this permission notice appear in all copies.
163943d840Sbenno  *
173943d840Sbenno  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
183943d840Sbenno  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
193943d840Sbenno  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
203943d840Sbenno  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
213943d840Sbenno  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
223943d840Sbenno  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
233943d840Sbenno  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
243943d840Sbenno  */
253943d840Sbenno 
263943d840Sbenno %{
27f9e1cc5fSbenno #include <sys/types.h>
28f9e1cc5fSbenno #include <sys/queue.h>
29f9e1cc5fSbenno #include <sys/stat.h>
303943d840Sbenno #include <ctype.h>
313943d840Sbenno #include <err.h>
322570ecd0Sflorian #include <errno.h>
333943d840Sbenno #include <limits.h>
343943d840Sbenno #include <stdarg.h>
353943d840Sbenno #include <stdio.h>
363943d840Sbenno #include <stdlib.h>
373943d840Sbenno #include <string.h>
383943d840Sbenno #include <unistd.h>
393943d840Sbenno 
403943d840Sbenno #include "parse.h"
413e86e78bSgilles #include "extern.h"
423943d840Sbenno 
433943d840Sbenno TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
443943d840Sbenno static struct file {
453943d840Sbenno 	TAILQ_ENTRY(file)	 entry;
463943d840Sbenno 	FILE			*stream;
473943d840Sbenno 	char			*name;
48f3e9965dSdenis 	size_t			 ungetpos;
49f3e9965dSdenis 	size_t			 ungetsize;
50f3e9965dSdenis 	u_char			*ungetbuf;
51f3e9965dSdenis 	int			 eof_reached;
523943d840Sbenno 	int			 lineno;
533943d840Sbenno 	int			 errors;
543943d840Sbenno } *file, *topfile;
553943d840Sbenno struct file	*pushfile(const char *);
563943d840Sbenno int		 popfile(void);
573943d840Sbenno int		 yyparse(void);
583943d840Sbenno int		 yylex(void);
593943d840Sbenno int		 yyerror(const char *, ...)
603943d840Sbenno     __attribute__((__format__ (printf, 1, 2)))
613943d840Sbenno     __attribute__((__nonnull__ (1)));
623943d840Sbenno int		 kw_cmp(const void *, const void *);
633943d840Sbenno int		 lookup(char *);
64f3e9965dSdenis int		 igetc(void);
653943d840Sbenno int		 lgetc(int);
66f3e9965dSdenis void		 lungetc(int);
673943d840Sbenno int		 findeol(void);
683943d840Sbenno 
693943d840Sbenno struct authority_c	*conf_new_authority(struct acme_conf *, char *);
703943d840Sbenno struct domain_c		*conf_new_domain(struct acme_conf *, char *);
713943d840Sbenno struct keyfile		*conf_new_keyfile(struct acme_conf *, char *);
723298b855Sbenno void			 clear_config(struct acme_conf *);
73bc812290Sflorian const char*		 kt2txt(enum keytype);
743298b855Sbenno void			 print_config(struct acme_conf *);
752570ecd0Sflorian int			 conf_check_file(char *);
763943d840Sbenno 
773943d840Sbenno TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
783943d840Sbenno struct sym {
793943d840Sbenno 	TAILQ_ENTRY(sym)	 entry;
803943d840Sbenno 	int			 used;
813943d840Sbenno 	int			 persist;
823943d840Sbenno 	char			*nam;
833943d840Sbenno 	char			*val;
843943d840Sbenno };
853943d840Sbenno int		 symset(const char *, const char *, int);
863943d840Sbenno char		*symget(const char *);
873943d840Sbenno 
883943d840Sbenno static struct acme_conf		*conf;
893943d840Sbenno static struct authority_c	*auth;
903943d840Sbenno static struct domain_c		*domain;
913943d840Sbenno static int			 errors = 0;
923943d840Sbenno 
933943d840Sbenno typedef struct {
943943d840Sbenno 	union {
953943d840Sbenno 		int64_t		 number;
963943d840Sbenno 		char		*string;
973943d840Sbenno 	} v;
983943d840Sbenno 	int lineno;
993943d840Sbenno } YYSTYPE;
1003943d840Sbenno 
1013943d840Sbenno %}
1023943d840Sbenno 
1036736ff2bSflorian %token	AUTHORITY URL API ACCOUNT CONTACT
104d0ea1cfcSsthen %token	DOMAIN ALTERNATIVE NAME NAMES CERT FULL CHAIN KEY SIGN WITH CHALLENGEDIR
1053943d840Sbenno %token	YES NO
1063943d840Sbenno %token	INCLUDE
1073943d840Sbenno %token	ERROR
1083e86e78bSgilles %token	RSA ECDSA
1093943d840Sbenno %token	<v.string>	STRING
1103943d840Sbenno %token	<v.number>	NUMBER
1113943d840Sbenno %type	<v.string>	string
11265a104faSflorian %type	<v.number>	keytype
1133943d840Sbenno 
1143943d840Sbenno %%
1153943d840Sbenno 
1163943d840Sbenno grammar		: /* empty */
1173943d840Sbenno 		| grammar include '\n'
1183943d840Sbenno 		| grammar varset '\n'
1193943d840Sbenno 		| grammar '\n'
1203943d840Sbenno 		| grammar authority '\n'
1213943d840Sbenno 		| grammar domain '\n'
1223943d840Sbenno 		| grammar error '\n'		{ file->errors++; }
1233943d840Sbenno 		;
1243943d840Sbenno 
1253943d840Sbenno include		: INCLUDE STRING		{
1263943d840Sbenno 			struct file	*nfile;
1273943d840Sbenno 
1283943d840Sbenno 			if ((nfile = pushfile($2)) == NULL) {
1293943d840Sbenno 				yyerror("failed to include file %s", $2);
1303943d840Sbenno 				free($2);
1313943d840Sbenno 				YYERROR;
1323943d840Sbenno 			}
1333943d840Sbenno 			free($2);
1343943d840Sbenno 
1353943d840Sbenno 			file = nfile;
1363943d840Sbenno 			lungetc('\n');
1373943d840Sbenno 		}
1383943d840Sbenno 		;
1393943d840Sbenno 
1403943d840Sbenno string		: string STRING	{
1413943d840Sbenno 			if (asprintf(&$$, "%s %s", $1, $2) == -1) {
1423943d840Sbenno 				free($1);
1433943d840Sbenno 				free($2);
1443943d840Sbenno 				yyerror("string: asprintf");
1453943d840Sbenno 				YYERROR;
1463943d840Sbenno 			}
1473943d840Sbenno 			free($1);
1483943d840Sbenno 			free($2);
1493943d840Sbenno 		}
1503943d840Sbenno 		| STRING
1513943d840Sbenno 		;
1523943d840Sbenno 
1533943d840Sbenno varset		: STRING '=' string		{
1543943d840Sbenno 			char *s = $1;
1553943d840Sbenno 			if (conf->opts & ACME_OPT_VERBOSE)
1563943d840Sbenno 				printf("%s = \"%s\"\n", $1, $3);
1573943d840Sbenno 			while (*s++) {
1583943d840Sbenno 				if (isspace((unsigned char)*s)) {
1593943d840Sbenno 					yyerror("macro name cannot contain "
1603943d840Sbenno 					    "whitespace");
16116a0a906Skrw 					free($1);
16216a0a906Skrw 					free($3);
1633943d840Sbenno 					YYERROR;
1643943d840Sbenno 				}
1653943d840Sbenno 			}
1663943d840Sbenno 			if (symset($1, $3, 0) == -1)
1673943d840Sbenno 				errx(EXIT_FAILURE, "cannot store variable");
1683943d840Sbenno 			free($1);
1693943d840Sbenno 			free($3);
1703943d840Sbenno 		}
1713943d840Sbenno 		;
1723943d840Sbenno 
1733943d840Sbenno optnl		: '\n' optnl
1743943d840Sbenno 		|
1753943d840Sbenno 		;
1763943d840Sbenno 
1773943d840Sbenno nl		: '\n' optnl		/* one newline or more */
1783943d840Sbenno 		;
1793943d840Sbenno 
180e6cfdc77Sop optcommanl	: ',' optnl
181e6cfdc77Sop 		| optnl
1823943d840Sbenno 		;
1833943d840Sbenno 
1843943d840Sbenno authority	: AUTHORITY STRING {
1853943d840Sbenno 			char *s;
1863943d840Sbenno 			if ((s = strdup($2)) == NULL)
1873943d840Sbenno 				err(EXIT_FAILURE, "strdup");
1883943d840Sbenno 			if ((auth = conf_new_authority(conf, s)) == NULL) {
1893943d840Sbenno 				free(s);
1903943d840Sbenno 				yyerror("authority already defined");
1913943d840Sbenno 				YYERROR;
1923943d840Sbenno 			}
1933943d840Sbenno 		} '{' optnl authorityopts_l '}' {
1941e4d2958Sbenno 			if (auth->api == NULL) {
1951e4d2958Sbenno 				yyerror("authority %s: no api URL specified",
1961e4d2958Sbenno 				    auth->name);
1971e4d2958Sbenno 				YYERROR;
1981e4d2958Sbenno 			}
1991e4d2958Sbenno 			if (auth->account == NULL) {
2001e4d2958Sbenno 				yyerror("authority %s: no account key file "
2011e4d2958Sbenno 				    "specified", auth->name);
2021e4d2958Sbenno 				YYERROR;
2031e4d2958Sbenno 			}
2043943d840Sbenno 			auth = NULL;
2053943d840Sbenno 		}
2063943d840Sbenno 		;
2073943d840Sbenno 
2083943d840Sbenno authorityopts_l	: authorityopts_l authorityoptsl nl
2093943d840Sbenno 		| authorityoptsl optnl
2103943d840Sbenno 		;
2113943d840Sbenno 
212179fe9a6Sflorian authorityoptsl	: API URL STRING {
2133943d840Sbenno 			char *s;
2143943d840Sbenno 			if (auth->api != NULL) {
2153943d840Sbenno 				yyerror("duplicate api");
2163943d840Sbenno 				YYERROR;
2173943d840Sbenno 			}
2183943d840Sbenno 			if ((s = strdup($3)) == NULL)
2193943d840Sbenno 				err(EXIT_FAILURE, "strdup");
2203943d840Sbenno 			auth->api = s;
2213943d840Sbenno 		}
2224f8b772fSflorian 		| ACCOUNT KEY STRING keytype{
2233943d840Sbenno 			char *s;
2243943d840Sbenno 			if (auth->account != NULL) {
2253943d840Sbenno 				yyerror("duplicate account");
2263943d840Sbenno 				YYERROR;
2273943d840Sbenno 			}
2283943d840Sbenno 			if ((s = strdup($3)) == NULL)
2293943d840Sbenno 				err(EXIT_FAILURE, "strdup");
2303943d840Sbenno 			auth->account = s;
2314f8b772fSflorian 			auth->keytype = $4;
2323943d840Sbenno 		}
2336736ff2bSflorian 		| CONTACT STRING {
2346736ff2bSflorian 			char *s;
2356736ff2bSflorian 			if (auth->contact != NULL) {
2366736ff2bSflorian 				yyerror("duplicate contact");
2376736ff2bSflorian 				YYERROR;
2386736ff2bSflorian 			}
2396736ff2bSflorian 			if ((s = strdup($2)) == NULL)
2406736ff2bSflorian 				err(EXIT_FAILURE, "strdup");
2416736ff2bSflorian 			auth->contact = s;
2426736ff2bSflorian 		}
2433943d840Sbenno 		;
2443943d840Sbenno 
2453943d840Sbenno domain		: DOMAIN STRING {
2463943d840Sbenno 			char *s;
2473943d840Sbenno 			if ((s = strdup($2)) == NULL)
2483943d840Sbenno 				err(EXIT_FAILURE, "strdup");
2493943d840Sbenno 			if (!domain_valid(s)) {
2503943d840Sbenno 				yyerror("%s: bad domain syntax", s);
2519d58ffeeSjsg 				free(s);
2523943d840Sbenno 				YYERROR;
2533943d840Sbenno 			}
2543943d840Sbenno 			if ((domain = conf_new_domain(conf, s)) == NULL) {
2553943d840Sbenno 				free(s);
2563943d840Sbenno 				yyerror("domain already defined");
2573943d840Sbenno 				YYERROR;
2583943d840Sbenno 			}
2593943d840Sbenno 		} '{' optnl domainopts_l '}' {
26087f5451dSbenno 			if (domain->domain == NULL) {
26187f5451dSbenno 				if ((domain->domain = strdup(domain->handle))
26287f5451dSbenno 				    == NULL)
26387f5451dSbenno 					err(EXIT_FAILURE, "strdup");
26487f5451dSbenno 			}
2651b6b7d02Sflorian 			/* enforce minimum config here */
2661b6b7d02Sflorian 			if (domain->key == NULL) {
2671b6b7d02Sflorian 				yyerror("no domain key file specified for "
2681b6b7d02Sflorian 				    "domain %s", domain->domain);
2691b6b7d02Sflorian 				YYERROR;
2701b6b7d02Sflorian 			}
2711b6b7d02Sflorian 			if (domain->cert == NULL && domain->fullchain == NULL) {
2721b6b7d02Sflorian 				yyerror("at least certificate file or full "
2731b6b7d02Sflorian 				    "certificate chain file must be specified "
2741b6b7d02Sflorian 				    "for domain %s", domain->domain);
2751b6b7d02Sflorian 				YYERROR;
2761b6b7d02Sflorian 			}
2773943d840Sbenno 			domain = NULL;
2783943d840Sbenno 		}
2793943d840Sbenno 		;
2803943d840Sbenno 
28165a104faSflorian keytype		: RSA	{ $$ = KT_RSA; }
28265a104faSflorian 		| ECDSA	{ $$ = KT_ECDSA; }
28365a104faSflorian 		|	{ $$ = KT_RSA; }
2843e86e78bSgilles 		;
2853e86e78bSgilles 
2863943d840Sbenno domainopts_l	: domainopts_l domainoptsl nl
2873943d840Sbenno 		| domainoptsl optnl
2883943d840Sbenno 		;
2893943d840Sbenno 
290e6cfdc77Sop domainoptsl	: ALTERNATIVE NAMES '{' optnl altname_l '}'
29187f5451dSbenno 		| DOMAIN NAME STRING {
29287f5451dSbenno 			char *s;
29387f5451dSbenno 			if (domain->domain != NULL) {
29487f5451dSbenno 				yyerror("duplicate domain name");
29587f5451dSbenno 				YYERROR;
29687f5451dSbenno 			}
29787f5451dSbenno 			if ((s = strdup($3)) == NULL)
29887f5451dSbenno 				err(EXIT_FAILURE, "strdup");
29987f5451dSbenno 			domain->domain = s;
30087f5451dSbenno 		}
3013e86e78bSgilles 		| DOMAIN KEY STRING keytype {
3023943d840Sbenno 			char *s;
3033943d840Sbenno 			if (domain->key != NULL) {
3043943d840Sbenno 				yyerror("duplicate key");
3053943d840Sbenno 				YYERROR;
3063943d840Sbenno 			}
3073943d840Sbenno 			if ((s = strdup($3)) == NULL)
3083943d840Sbenno 				err(EXIT_FAILURE, "strdup");
3092570ecd0Sflorian 			if (!conf_check_file(s)) {
310383e31e9Sbenno 				free(s);
311383e31e9Sbenno 				YYERROR;
312383e31e9Sbenno 			}
31333febeb9Sflorian 			if ((conf_new_keyfile(conf, s)) == NULL) {
3143943d840Sbenno 				free(s);
3153943d840Sbenno 				yyerror("domain key file already used");
3163943d840Sbenno 				YYERROR;
3173943d840Sbenno 			}
3183943d840Sbenno 			domain->key = s;
31965a104faSflorian 			domain->keytype = $4;
3203943d840Sbenno 		}
3213943d840Sbenno 		| DOMAIN CERT STRING {
3223943d840Sbenno 			char *s;
3233943d840Sbenno 			if (domain->cert != NULL) {
3243943d840Sbenno 				yyerror("duplicate cert");
3253943d840Sbenno 				YYERROR;
3263943d840Sbenno 			}
3273943d840Sbenno 			if ((s = strdup($3)) == NULL)
3283943d840Sbenno 				err(EXIT_FAILURE, "strdup");
3293943d840Sbenno 			if (s[0] != '/') {
3303943d840Sbenno 				free(s);
3313943d840Sbenno 				yyerror("not an absolute path");
3323943d840Sbenno 				YYERROR;
3333943d840Sbenno 			}
33433febeb9Sflorian 			if ((conf_new_keyfile(conf, s)) == NULL) {
3353943d840Sbenno 				free(s);
3363943d840Sbenno 				yyerror("domain cert file already used");
3373943d840Sbenno 				YYERROR;
3383943d840Sbenno 			}
3393943d840Sbenno 			domain->cert = s;
3403943d840Sbenno 		}
34133febeb9Sflorian 		| DOMAIN CHAIN CERT STRING {
34233febeb9Sflorian 			char *s;
34333febeb9Sflorian 			if (domain->chain != NULL) {
34433febeb9Sflorian 				yyerror("duplicate chain");
34533febeb9Sflorian 				YYERROR;
34633febeb9Sflorian 			}
34733febeb9Sflorian 			if ((s = strdup($4)) == NULL)
34833febeb9Sflorian 				err(EXIT_FAILURE, "strdup");
34933febeb9Sflorian 			if ((conf_new_keyfile(conf, s)) == NULL) {
35033febeb9Sflorian 				free(s);
35133febeb9Sflorian 				yyerror("domain chain file already used");
35233febeb9Sflorian 				YYERROR;
35333febeb9Sflorian 			}
35433febeb9Sflorian 			domain->chain = s;
35533febeb9Sflorian 		}
35670bcb874Sbenno 		| DOMAIN FULL CHAIN CERT STRING {
35770bcb874Sbenno 			char *s;
35870bcb874Sbenno 			if (domain->fullchain != NULL) {
3594adf5313Sbenno 				yyerror("duplicate full chain");
36070bcb874Sbenno 				YYERROR;
36170bcb874Sbenno 			}
36270bcb874Sbenno 			if ((s = strdup($5)) == NULL)
36370bcb874Sbenno 				err(EXIT_FAILURE, "strdup");
36470bcb874Sbenno 			if ((conf_new_keyfile(conf, s)) == NULL) {
36570bcb874Sbenno 				free(s);
36670bcb874Sbenno 				yyerror("domain full chain file already used");
36770bcb874Sbenno 				YYERROR;
36870bcb874Sbenno 			}
36970bcb874Sbenno 			domain->fullchain = s;
37070bcb874Sbenno 		}
3713943d840Sbenno 		| SIGN WITH STRING {
3723943d840Sbenno 			char *s;
3733943d840Sbenno 			if (domain->auth != NULL) {
37446203dadSbenno 				yyerror("duplicate sign with");
3753943d840Sbenno 				YYERROR;
3763943d840Sbenno 			}
3773943d840Sbenno 			if ((s = strdup($3)) == NULL)
3783943d840Sbenno 				err(EXIT_FAILURE, "strdup");
3793943d840Sbenno 			if (authority_find(conf, s) == NULL) {
38046203dadSbenno 				yyerror("sign with: unknown authority");
3819d58ffeeSjsg 				free(s);
3823943d840Sbenno 				YYERROR;
3833943d840Sbenno 			}
3843943d840Sbenno 			domain->auth = s;
3853943d840Sbenno 		}
3866c0ff37dSbenno 		| CHALLENGEDIR STRING {
3876c0ff37dSbenno 			char *s;
3886c0ff37dSbenno 			if (domain->challengedir != NULL) {
3896c0ff37dSbenno 				yyerror("duplicate challengedir");
3906c0ff37dSbenno 				YYERROR;
3916c0ff37dSbenno 			}
3926c0ff37dSbenno 			if ((s = strdup($2)) == NULL)
3936c0ff37dSbenno 				err(EXIT_FAILURE, "strdup");
3946c0ff37dSbenno 			domain->challengedir = s;
3956c0ff37dSbenno 		}
3963943d840Sbenno 		;
3973943d840Sbenno 
398e6cfdc77Sop altname_l	: altname optcommanl altname_l
399e6cfdc77Sop 		| altname optnl
4003943d840Sbenno 		;
4013943d840Sbenno 
4023943d840Sbenno altname		: STRING {
4033943d840Sbenno 			char			*s;
4043943d840Sbenno 			struct altname_c	*ac;
4053943d840Sbenno 			if (!domain_valid($1)) {
4063943d840Sbenno 				yyerror("bad domain syntax");
4073943d840Sbenno 				YYERROR;
4083943d840Sbenno 			}
4093943d840Sbenno 			if ((ac = calloc(1, sizeof(struct altname_c))) == NULL)
4103943d840Sbenno 				err(EXIT_FAILURE, "calloc");
4113943d840Sbenno 			if ((s = strdup($1)) == NULL) {
4123943d840Sbenno 				free(ac);
4133943d840Sbenno 				err(EXIT_FAILURE, "strdup");
4143943d840Sbenno 			}
4153943d840Sbenno 			ac->domain = s;
416221ac2aaSbenno 			TAILQ_INSERT_TAIL(&domain->altname_list, ac, entry);
417383e31e9Sbenno 			domain->altname_count++;
4183943d840Sbenno 			/*
4193943d840Sbenno 			 * XXX we could check if altname is duplicate
4203943d840Sbenno 			 * or identical to domain->domain
4213943d840Sbenno 			*/
4223943d840Sbenno 		}
4233943d840Sbenno 
4243943d840Sbenno %%
4253943d840Sbenno 
4263943d840Sbenno struct keywords {
4273943d840Sbenno 	const char	*k_name;
4283943d840Sbenno 	int		 k_val;
4293943d840Sbenno };
4303943d840Sbenno 
4313943d840Sbenno int
yyerror(const char * fmt,...)4323943d840Sbenno yyerror(const char *fmt, ...)
4333943d840Sbenno {
4343943d840Sbenno 	va_list		 ap;
4353943d840Sbenno 	char		*msg;
4363943d840Sbenno 
4373943d840Sbenno 	file->errors++;
4383943d840Sbenno 	va_start(ap, fmt);
4393943d840Sbenno 	if (vasprintf(&msg, fmt, ap) == -1)
4403943d840Sbenno 		err(EXIT_FAILURE, "yyerror vasprintf");
4413943d840Sbenno 	va_end(ap);
4423943d840Sbenno 	fprintf(stderr, "%s:%d: %s\n", file->name, yylval.lineno, msg);
4433943d840Sbenno 	free(msg);
4443943d840Sbenno 	return (0);
4453943d840Sbenno }
4463943d840Sbenno 
4473943d840Sbenno int
kw_cmp(const void * k,const void * e)4483943d840Sbenno kw_cmp(const void *k, const void *e)
4493943d840Sbenno {
4507d83751cSbenno 	return strcmp(k, ((const struct keywords *)e)->k_name);
4513943d840Sbenno }
4523943d840Sbenno 
4533943d840Sbenno int
lookup(char * s)4543943d840Sbenno lookup(char *s)
4553943d840Sbenno {
4563943d840Sbenno 	/* this has to be sorted always */
4573943d840Sbenno 	static const struct keywords keywords[] = {
4583943d840Sbenno 		{"account",		ACCOUNT},
4593943d840Sbenno 		{"alternative",		ALTERNATIVE},
4603943d840Sbenno 		{"api",			API},
4613943d840Sbenno 		{"authority",		AUTHORITY},
4623943d840Sbenno 		{"certificate",		CERT},
46333febeb9Sflorian 		{"chain",		CHAIN},
4646c0ff37dSbenno 		{"challengedir",	CHALLENGEDIR},
4656736ff2bSflorian 		{"contact",		CONTACT},
4663943d840Sbenno 		{"domain",		DOMAIN},
4673e86e78bSgilles 		{"ecdsa",		ECDSA},
46870bcb874Sbenno 		{"full",		FULL},
4693943d840Sbenno 		{"include",		INCLUDE},
4703943d840Sbenno 		{"key",			KEY},
471d0ea1cfcSsthen 		{"name",		NAME},
4723943d840Sbenno 		{"names",		NAMES},
4733e86e78bSgilles 		{"rsa",			RSA},
4743943d840Sbenno 		{"sign",		SIGN},
4753943d840Sbenno 		{"url",			URL},
4763943d840Sbenno 		{"with",		WITH},
4773943d840Sbenno 	};
4783943d840Sbenno 	const struct keywords	*p;
4793943d840Sbenno 
4803943d840Sbenno 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
4813943d840Sbenno 	    sizeof(keywords[0]), kw_cmp);
4823943d840Sbenno 
4837d83751cSbenno 	if (p != NULL)
4847d83751cSbenno 		return p->k_val;
4853943d840Sbenno 	else
4867d83751cSbenno 		return STRING;
4873943d840Sbenno }
4883943d840Sbenno 
489f3e9965dSdenis #define	START_EXPAND	1
490f3e9965dSdenis #define	DONE_EXPAND	2
4913943d840Sbenno 
492f3e9965dSdenis static int	expanding;
493f3e9965dSdenis 
494f3e9965dSdenis int
igetc(void)495f3e9965dSdenis igetc(void)
496f3e9965dSdenis {
497f3e9965dSdenis 	int	c;
498f3e9965dSdenis 
499f3e9965dSdenis 	while (1) {
500f3e9965dSdenis 		if (file->ungetpos > 0)
501f3e9965dSdenis 			c = file->ungetbuf[--file->ungetpos];
502f3e9965dSdenis 		else
503f3e9965dSdenis 			c = getc(file->stream);
504f3e9965dSdenis 
505f3e9965dSdenis 		if (c == START_EXPAND)
506f3e9965dSdenis 			expanding = 1;
507f3e9965dSdenis 		else if (c == DONE_EXPAND)
508f3e9965dSdenis 			expanding = 0;
509f3e9965dSdenis 		else
510f3e9965dSdenis 			break;
511f3e9965dSdenis 	}
5127d83751cSbenno 	return c;
513f3e9965dSdenis }
5143943d840Sbenno 
5153943d840Sbenno int
lgetc(int quotec)5163943d840Sbenno lgetc(int quotec)
5173943d840Sbenno {
5183943d840Sbenno 	int		c, next;
5193943d840Sbenno 
5203943d840Sbenno 	if (quotec) {
521f3e9965dSdenis 		if ((c = igetc()) == EOF) {
5223943d840Sbenno 			yyerror("reached end of file while parsing "
5233943d840Sbenno 			    "quoted string");
5243943d840Sbenno 			if (file == topfile || popfile() == EOF)
5253943d840Sbenno 				return (EOF);
5267d83751cSbenno 			return quotec;
5273943d840Sbenno 		}
5287d83751cSbenno 		return c;
5293943d840Sbenno 	}
5303943d840Sbenno 
531f3e9965dSdenis 	while ((c = igetc()) == '\\') {
532f3e9965dSdenis 		next = igetc();
5333943d840Sbenno 		if (next != '\n') {
5343943d840Sbenno 			c = next;
5353943d840Sbenno 			break;
5363943d840Sbenno 		}
5373943d840Sbenno 		yylval.lineno = file->lineno;
5383943d840Sbenno 		file->lineno++;
5393943d840Sbenno 	}
5403943d840Sbenno 
541f3e9965dSdenis 	if (c == EOF) {
542f3e9965dSdenis 		/*
543f3e9965dSdenis 		 * Fake EOL when hit EOF for the first time. This gets line
544f3e9965dSdenis 		 * count right if last line in included file is syntactically
545f3e9965dSdenis 		 * invalid and has no newline.
546f3e9965dSdenis 		 */
547f3e9965dSdenis 		if (file->eof_reached == 0) {
548f3e9965dSdenis 			file->eof_reached = 1;
5497d83751cSbenno 			return '\n';
550f3e9965dSdenis 		}
5513943d840Sbenno 		while (c == EOF) {
5523943d840Sbenno 			if (file == topfile || popfile() == EOF)
5533943d840Sbenno 				return (EOF);
554f3e9965dSdenis 			c = igetc();
555f3e9965dSdenis 		}
5563943d840Sbenno 	}
5577d83751cSbenno 	return c;
5583943d840Sbenno }
5593943d840Sbenno 
560f3e9965dSdenis void
lungetc(int c)5613943d840Sbenno lungetc(int c)
5623943d840Sbenno {
5633943d840Sbenno 	if (c == EOF)
564f3e9965dSdenis 		return;
565f3e9965dSdenis 
566f3e9965dSdenis 	if (file->ungetpos >= file->ungetsize) {
567f3e9965dSdenis 		void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
568f3e9965dSdenis 		if (p == NULL)
569a062aa9dSkrw 			err(1, "%s", __func__);
570f3e9965dSdenis 		file->ungetbuf = p;
571f3e9965dSdenis 		file->ungetsize *= 2;
5723943d840Sbenno 	}
573f3e9965dSdenis 	file->ungetbuf[file->ungetpos++] = c;
5743943d840Sbenno }
5753943d840Sbenno 
5763943d840Sbenno int
findeol(void)5773943d840Sbenno findeol(void)
5783943d840Sbenno {
5793943d840Sbenno 	int	c;
5803943d840Sbenno 
5813943d840Sbenno 	/* skip to either EOF or the first real EOL */
5823943d840Sbenno 	while (1) {
5833943d840Sbenno 		c = lgetc(0);
5843943d840Sbenno 		if (c == '\n') {
5853943d840Sbenno 			file->lineno++;
5863943d840Sbenno 			break;
5873943d840Sbenno 		}
5883943d840Sbenno 		if (c == EOF)
5893943d840Sbenno 			break;
5903943d840Sbenno 	}
5917d83751cSbenno 	return ERROR;
5923943d840Sbenno }
5933943d840Sbenno 
5943943d840Sbenno int
yylex(void)5953943d840Sbenno yylex(void)
5963943d840Sbenno {
59708f6ba19Snaddy 	char	 buf[8096];
59808f6ba19Snaddy 	char	*p, *val;
5993943d840Sbenno 	int	 quotec, next, c;
6003943d840Sbenno 	int	 token;
6013943d840Sbenno 
6023943d840Sbenno top:
6033943d840Sbenno 	p = buf;
6043943d840Sbenno 	while ((c = lgetc(0)) == ' ' || c == '\t')
6053943d840Sbenno 		; /* nothing */
6063943d840Sbenno 
6073943d840Sbenno 	yylval.lineno = file->lineno;
6083943d840Sbenno 	if (c == '#')
6093943d840Sbenno 		while ((c = lgetc(0)) != '\n' && c != EOF)
6103943d840Sbenno 			; /* nothing */
611f3e9965dSdenis 	if (c == '$' && !expanding) {
6123943d840Sbenno 		while (1) {
6133943d840Sbenno 			if ((c = lgetc(0)) == EOF)
6147d83751cSbenno 				return 0;
6153943d840Sbenno 
6163943d840Sbenno 			if (p + 1 >= buf + sizeof(buf) - 1) {
6173943d840Sbenno 				yyerror("string too long");
6187d83751cSbenno 				return findeol();
6193943d840Sbenno 			}
6203943d840Sbenno 			if (isalnum(c) || c == '_') {
6213943d840Sbenno 				*p++ = c;
6223943d840Sbenno 				continue;
6233943d840Sbenno 			}
6243943d840Sbenno 			*p = '\0';
6253943d840Sbenno 			lungetc(c);
6263943d840Sbenno 			break;
6273943d840Sbenno 		}
6283943d840Sbenno 		val = symget(buf);
6293943d840Sbenno 		if (val == NULL) {
6303943d840Sbenno 			yyerror("macro '%s' not defined", buf);
6317d83751cSbenno 			return findeol();
6323943d840Sbenno 		}
633f3e9965dSdenis 		p = val + strlen(val) - 1;
634f3e9965dSdenis 		lungetc(DONE_EXPAND);
635f3e9965dSdenis 		while (p >= val) {
63608f6ba19Snaddy 			lungetc((unsigned char)*p);
637f3e9965dSdenis 			p--;
638f3e9965dSdenis 		}
639f3e9965dSdenis 		lungetc(START_EXPAND);
6403943d840Sbenno 		goto top;
6413943d840Sbenno 	}
6423943d840Sbenno 
6433943d840Sbenno 	switch (c) {
6443943d840Sbenno 	case '\'':
6453943d840Sbenno 	case '"':
6463943d840Sbenno 		quotec = c;
6473943d840Sbenno 		while (1) {
6483943d840Sbenno 			if ((c = lgetc(quotec)) == EOF)
6497d83751cSbenno 				return 0;
6503943d840Sbenno 			if (c == '\n') {
6513943d840Sbenno 				file->lineno++;
6523943d840Sbenno 				continue;
6533943d840Sbenno 			} else if (c == '\\') {
6543943d840Sbenno 				if ((next = lgetc(quotec)) == EOF)
6557d83751cSbenno 					return 0;
656a1533359Ssashan 				if (next == quotec || next == ' ' ||
657a1533359Ssashan 				    next == '\t')
6583943d840Sbenno 					c = next;
6593943d840Sbenno 				else if (next == '\n') {
6603943d840Sbenno 					file->lineno++;
6613943d840Sbenno 					continue;
6623943d840Sbenno 				} else
6633943d840Sbenno 					lungetc(next);
6643943d840Sbenno 			} else if (c == quotec) {
6653943d840Sbenno 				*p = '\0';
6663943d840Sbenno 				break;
6673943d840Sbenno 			} else if (c == '\0') {
6683943d840Sbenno 				yyerror("syntax error");
6697d83751cSbenno 				return findeol();
6703943d840Sbenno 			}
6713943d840Sbenno 			if (p + 1 >= buf + sizeof(buf) - 1) {
6723943d840Sbenno 				yyerror("string too long");
6737d83751cSbenno 				return findeol();
6743943d840Sbenno 			}
6753943d840Sbenno 			*p++ = c;
6763943d840Sbenno 		}
6773943d840Sbenno 		yylval.v.string = strdup(buf);
6783943d840Sbenno 		if (yylval.v.string == NULL)
679a062aa9dSkrw 			err(EXIT_FAILURE, "%s", __func__);
6807d83751cSbenno 		return STRING;
6813943d840Sbenno 	}
6823943d840Sbenno 
6833943d840Sbenno #define allowed_to_end_number(x) \
6843943d840Sbenno 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
6853943d840Sbenno 
6863943d840Sbenno 	if (c == '-' || isdigit(c)) {
6873943d840Sbenno 		do {
6883943d840Sbenno 			*p++ = c;
689915c3f33Sderaadt 			if ((size_t)(p-buf) >= sizeof(buf)) {
6903943d840Sbenno 				yyerror("string too long");
6917d83751cSbenno 				return findeol();
6923943d840Sbenno 			}
6933943d840Sbenno 		} while ((c = lgetc(0)) != EOF && isdigit(c));
6943943d840Sbenno 		lungetc(c);
6953943d840Sbenno 		if (p == buf + 1 && buf[0] == '-')
6963943d840Sbenno 			goto nodigits;
6973943d840Sbenno 		if (c == EOF || allowed_to_end_number(c)) {
6983943d840Sbenno 			const char *errstr = NULL;
6993943d840Sbenno 
7003943d840Sbenno 			*p = '\0';
7013943d840Sbenno 			yylval.v.number = strtonum(buf, LLONG_MIN,
7023943d840Sbenno 			    LLONG_MAX, &errstr);
7037d83751cSbenno 			if (errstr != NULL) {
7043943d840Sbenno 				yyerror("\"%s\" invalid number: %s",
7053943d840Sbenno 				    buf, errstr);
7063943d840Sbenno 				return (findeol());
7073943d840Sbenno 			}
7087d83751cSbenno 			return NUMBER;
7093943d840Sbenno 		} else {
7103943d840Sbenno nodigits:
7113943d840Sbenno 			while (p > buf + 1)
71208f6ba19Snaddy 				lungetc((unsigned char)*--p);
71308f6ba19Snaddy 			c = (unsigned char)*--p;
7143943d840Sbenno 			if (c == '-')
7157d83751cSbenno 				return c;
7163943d840Sbenno 		}
7173943d840Sbenno 	}
7183943d840Sbenno 
7193943d840Sbenno #define allowed_in_string(x) \
7203943d840Sbenno 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
7213943d840Sbenno 	x != '{' && x != '}' && \
7223943d840Sbenno 	x != '!' && x != '=' && x != '#' && \
7233943d840Sbenno 	x != ','))
7243943d840Sbenno 
725383e31e9Sbenno 	if (isalnum(c) || c == ':' || c == '_') {
7263943d840Sbenno 		do {
7273943d840Sbenno 			*p++ = c;
728915c3f33Sderaadt 			if ((size_t)(p-buf) >= sizeof(buf)) {
7293943d840Sbenno 				yyerror("string too long");
7303943d840Sbenno 				return (findeol());
7313943d840Sbenno 			}
7323943d840Sbenno 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
7333943d840Sbenno 		lungetc(c);
7343943d840Sbenno 		*p = '\0';
7353943d840Sbenno 		if ((token = lookup(buf)) == STRING) {
7363943d840Sbenno 			if ((yylval.v.string = strdup(buf)) == NULL)
737a062aa9dSkrw 				err(EXIT_FAILURE, "%s", __func__);
7383943d840Sbenno 		}
7397d83751cSbenno 		return token;
7403943d840Sbenno 	}
7413943d840Sbenno 	if (c == '\n') {
7423943d840Sbenno 		yylval.lineno = file->lineno;
7433943d840Sbenno 		file->lineno++;
7443943d840Sbenno 	}
7453943d840Sbenno 	if (c == EOF)
7467d83751cSbenno 		return 0;
7477d83751cSbenno 	return c;
7483943d840Sbenno }
7493943d840Sbenno 
7503943d840Sbenno struct file *
pushfile(const char * name)7513943d840Sbenno pushfile(const char *name)
7523943d840Sbenno {
7533943d840Sbenno 	struct file	*nfile;
7543943d840Sbenno 
7553943d840Sbenno 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
7566a3d55f9Skrw 		warn("%s", __func__);
7577d83751cSbenno 		return NULL;
7583943d840Sbenno 	}
7593943d840Sbenno 	if ((nfile->name = strdup(name)) == NULL) {
7606a3d55f9Skrw 		warn("%s", __func__);
7613943d840Sbenno 		free(nfile);
7627d83751cSbenno 		return NULL;
7633943d840Sbenno 	}
7643943d840Sbenno 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
7656a3d55f9Skrw 		warn("%s: %s", __func__, nfile->name);
7663943d840Sbenno 		free(nfile->name);
7673943d840Sbenno 		free(nfile);
7687d83751cSbenno 		return NULL;
7693943d840Sbenno 	}
770f3e9965dSdenis 	nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
771f3e9965dSdenis 	nfile->ungetsize = 16;
772f3e9965dSdenis 	nfile->ungetbuf = malloc(nfile->ungetsize);
773f3e9965dSdenis 	if (nfile->ungetbuf == NULL) {
7746a3d55f9Skrw 		warn("%s", __func__);
775f3e9965dSdenis 		fclose(nfile->stream);
776f3e9965dSdenis 		free(nfile->name);
777f3e9965dSdenis 		free(nfile);
7787d83751cSbenno 		return NULL;
779f3e9965dSdenis 	}
7803943d840Sbenno 	TAILQ_INSERT_TAIL(&files, nfile, entry);
7817d83751cSbenno 	return nfile;
7823943d840Sbenno }
7833943d840Sbenno 
7843943d840Sbenno int
popfile(void)7853943d840Sbenno popfile(void)
7863943d840Sbenno {
7873943d840Sbenno 	struct file	*prev;
7883943d840Sbenno 
7893943d840Sbenno 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
7903943d840Sbenno 		prev->errors += file->errors;
7913943d840Sbenno 
7923943d840Sbenno 	TAILQ_REMOVE(&files, file, entry);
7933943d840Sbenno 	fclose(file->stream);
7943943d840Sbenno 	free(file->name);
795f3e9965dSdenis 	free(file->ungetbuf);
7963943d840Sbenno 	free(file);
7973943d840Sbenno 	file = prev;
7983943d840Sbenno 	return (file ? 0 : EOF);
7993943d840Sbenno }
8003943d840Sbenno 
8013943d840Sbenno struct acme_conf *
parse_config(const char * filename,int opts)8023943d840Sbenno parse_config(const char *filename, int opts)
8033943d840Sbenno {
8043943d840Sbenno 	struct sym	*sym, *next;
8053943d840Sbenno 
8063943d840Sbenno 	if ((conf = calloc(1, sizeof(struct acme_conf))) == NULL)
807a062aa9dSkrw 		err(EXIT_FAILURE, "%s", __func__);
8083943d840Sbenno 	conf->opts = opts;
8093943d840Sbenno 
8103943d840Sbenno 	if ((file = pushfile(filename)) == NULL) {
8113943d840Sbenno 		free(conf);
8127d83751cSbenno 		return NULL;
8133943d840Sbenno 	}
8143943d840Sbenno 	topfile = file;
8153943d840Sbenno 
816221ac2aaSbenno 	TAILQ_INIT(&conf->authority_list);
817221ac2aaSbenno 	TAILQ_INIT(&conf->domain_list);
8183943d840Sbenno 
8193943d840Sbenno 	yyparse();
8203943d840Sbenno 	errors = file->errors;
8213943d840Sbenno 	popfile();
8223943d840Sbenno 
8233943d840Sbenno 	/* Free macros and check which have not been used. */
82446bca67bSkrw 	TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
8253943d840Sbenno 		if ((conf->opts & ACME_OPT_VERBOSE) && !sym->used)
8263943d840Sbenno 			fprintf(stderr, "warning: macro '%s' not "
8273943d840Sbenno 			    "used\n", sym->nam);
8283943d840Sbenno 		if (!sym->persist) {
8293943d840Sbenno 			free(sym->nam);
8303943d840Sbenno 			free(sym->val);
8313943d840Sbenno 			TAILQ_REMOVE(&symhead, sym, entry);
8323943d840Sbenno 			free(sym);
8333943d840Sbenno 		}
8343943d840Sbenno 	}
8353943d840Sbenno 
8367d83751cSbenno 	if (errors != 0) {
8373943d840Sbenno 		clear_config(conf);
8387d83751cSbenno 		return NULL;
8393943d840Sbenno 	}
8403943d840Sbenno 
841f142a2feSbenno 	if (opts & ACME_OPT_CHECK) {
842f142a2feSbenno 		if (opts & ACME_OPT_VERBOSE)
84309263938Sbenno 			print_config(conf);
844f142a2feSbenno 		exit(0);
845f142a2feSbenno 	}
846f142a2feSbenno 
84709263938Sbenno 
8487d83751cSbenno 	return conf;
8493943d840Sbenno }
8503943d840Sbenno 
8513943d840Sbenno int
symset(const char * nam,const char * val,int persist)8523943d840Sbenno symset(const char *nam, const char *val, int persist)
8533943d840Sbenno {
8543943d840Sbenno 	struct sym	*sym;
8553943d840Sbenno 
85654c95b7aSkrw 	TAILQ_FOREACH(sym, &symhead, entry) {
85754c95b7aSkrw 		if (strcmp(nam, sym->nam) == 0)
85854c95b7aSkrw 			break;
85954c95b7aSkrw 	}
8603943d840Sbenno 
8613943d840Sbenno 	if (sym != NULL) {
8623943d840Sbenno 		if (sym->persist == 1)
8633943d840Sbenno 			return (0);
8643943d840Sbenno 		else {
8653943d840Sbenno 			free(sym->nam);
8663943d840Sbenno 			free(sym->val);
8673943d840Sbenno 			TAILQ_REMOVE(&symhead, sym, entry);
8683943d840Sbenno 			free(sym);
8693943d840Sbenno 		}
8703943d840Sbenno 	}
8713943d840Sbenno 	if ((sym = calloc(1, sizeof(*sym))) == NULL)
8727d83751cSbenno 		return -1;
8733943d840Sbenno 
8743943d840Sbenno 	sym->nam = strdup(nam);
8753943d840Sbenno 	if (sym->nam == NULL) {
8763943d840Sbenno 		free(sym);
8777d83751cSbenno 		return -1;
8783943d840Sbenno 	}
8793943d840Sbenno 	sym->val = strdup(val);
8803943d840Sbenno 	if (sym->val == NULL) {
8813943d840Sbenno 		free(sym->nam);
8823943d840Sbenno 		free(sym);
8837d83751cSbenno 		return -1;
8843943d840Sbenno 	}
8853943d840Sbenno 	sym->used = 0;
8863943d840Sbenno 	sym->persist = persist;
8873943d840Sbenno 	TAILQ_INSERT_TAIL(&symhead, sym, entry);
8887d83751cSbenno 	return 0;
8893943d840Sbenno }
8903943d840Sbenno 
8913943d840Sbenno int
cmdline_symset(char * s)8923943d840Sbenno cmdline_symset(char *s)
8933943d840Sbenno {
8943943d840Sbenno 	char	*sym, *val;
8953943d840Sbenno 	int	ret;
8963943d840Sbenno 
8973943d840Sbenno 	if ((val = strrchr(s, '=')) == NULL)
8987d83751cSbenno 		return -1;
899ed1b9eb8Smiko 	sym = strndup(s, val - s);
900ed1b9eb8Smiko 	if (sym == NULL)
901ed1b9eb8Smiko 		errx(EXIT_FAILURE, "%s: strndup", __func__);
9023943d840Sbenno 	ret = symset(sym, val + 1, 1);
9033943d840Sbenno 	free(sym);
9043943d840Sbenno 
9057d83751cSbenno 	return ret;
9063943d840Sbenno }
9073943d840Sbenno 
9083943d840Sbenno char *
symget(const char * nam)9093943d840Sbenno symget(const char *nam)
9103943d840Sbenno {
9113943d840Sbenno 	struct sym	*sym;
9123943d840Sbenno 
91354c95b7aSkrw 	TAILQ_FOREACH(sym, &symhead, entry) {
9143943d840Sbenno 		if (strcmp(nam, sym->nam) == 0) {
9153943d840Sbenno 			sym->used = 1;
9167d83751cSbenno 			return sym->val;
9173943d840Sbenno 		}
91854c95b7aSkrw 	}
9197d83751cSbenno 	return NULL;
9203943d840Sbenno }
9213943d840Sbenno 
9223943d840Sbenno struct authority_c *
conf_new_authority(struct acme_conf * c,char * s)9233943d840Sbenno conf_new_authority(struct acme_conf *c, char *s)
9243943d840Sbenno {
9253943d840Sbenno 	struct authority_c *a;
9263943d840Sbenno 
9273943d840Sbenno 	a = authority_find(c, s);
9287d83751cSbenno 	if (a != NULL)
9297d83751cSbenno 		return NULL;
9303943d840Sbenno 	if ((a = calloc(1, sizeof(struct authority_c))) == NULL)
931a062aa9dSkrw 		err(EXIT_FAILURE, "%s", __func__);
932221ac2aaSbenno 	TAILQ_INSERT_TAIL(&c->authority_list, a, entry);
9333943d840Sbenno 
9343943d840Sbenno 	a->name = s;
9357d83751cSbenno 	return a;
9363943d840Sbenno }
9373943d840Sbenno 
9383943d840Sbenno struct authority_c *
authority_find(struct acme_conf * c,char * s)9393943d840Sbenno authority_find(struct acme_conf *c, char *s)
9403943d840Sbenno {
9413943d840Sbenno 	struct authority_c	*a;
9423943d840Sbenno 
943221ac2aaSbenno 	TAILQ_FOREACH(a, &c->authority_list, entry) {
9443943d840Sbenno 		if (strncmp(a->name, s, AUTH_MAXLEN) == 0) {
9457d83751cSbenno 			return a;
9463943d840Sbenno 		}
9473943d840Sbenno 	}
9487d83751cSbenno 	return NULL;
9493943d840Sbenno }
9503943d840Sbenno 
9513943d840Sbenno struct authority_c *
authority_find0(struct acme_conf * c)9523943d840Sbenno authority_find0(struct acme_conf *c)
9533943d840Sbenno {
9547d307612Sbenno 	return (TAILQ_FIRST(&c->authority_list));
9553943d840Sbenno }
9563943d840Sbenno 
9573943d840Sbenno struct domain_c *
conf_new_domain(struct acme_conf * c,char * s)9583943d840Sbenno conf_new_domain(struct acme_conf *c, char *s)
9593943d840Sbenno {
9603943d840Sbenno 	struct domain_c *d;
9613943d840Sbenno 
96287f5451dSbenno 	d = domain_find_handle(c, s);
9637d83751cSbenno 	if (d != NULL)
9643943d840Sbenno 		return (NULL);
9653943d840Sbenno 	if ((d = calloc(1, sizeof(struct domain_c))) == NULL)
966a062aa9dSkrw 		err(EXIT_FAILURE, "%s", __func__);
967221ac2aaSbenno 	TAILQ_INSERT_TAIL(&c->domain_list, d, entry);
9683943d840Sbenno 
96987f5451dSbenno 	d->handle = s;
970221ac2aaSbenno 	TAILQ_INIT(&d->altname_list);
9713943d840Sbenno 
9727d83751cSbenno 	return d;
9733943d840Sbenno }
9743943d840Sbenno 
9753943d840Sbenno struct domain_c *
domain_find_handle(struct acme_conf * c,char * s)97687f5451dSbenno domain_find_handle(struct acme_conf *c, char *s)
9773943d840Sbenno {
9783943d840Sbenno 	struct domain_c	*d;
9793943d840Sbenno 
980221ac2aaSbenno 	TAILQ_FOREACH(d, &c->domain_list, entry) {
98187f5451dSbenno 		if (strncmp(d->handle, s, DOMAIN_MAXLEN) == 0) {
9827d83751cSbenno 			return d;
9833943d840Sbenno 		}
9843943d840Sbenno 	}
9857d83751cSbenno 	return NULL;
9863943d840Sbenno }
9873943d840Sbenno 
9883943d840Sbenno struct keyfile *
conf_new_keyfile(struct acme_conf * c,char * s)9893943d840Sbenno conf_new_keyfile(struct acme_conf *c, char *s)
9903943d840Sbenno {
9913943d840Sbenno 	struct keyfile *k;
9923943d840Sbenno 
9933943d840Sbenno 	LIST_FOREACH(k, &c->used_key_list, entry) {
9943943d840Sbenno 		if (strncmp(k->name, s, PATH_MAX) == 0) {
9957d83751cSbenno 			return NULL;
9963943d840Sbenno 		}
9973943d840Sbenno 	}
9983943d840Sbenno 
9993943d840Sbenno 	if ((k = calloc(1, sizeof(struct keyfile))) == NULL)
1000a062aa9dSkrw 		err(EXIT_FAILURE, "%s", __func__);
10013943d840Sbenno 	LIST_INSERT_HEAD(&c->used_key_list, k, entry);
10023943d840Sbenno 
10033943d840Sbenno 	k->name = s;
10047d83751cSbenno 	return k;
10053943d840Sbenno }
10063943d840Sbenno 
10073943d840Sbenno void
clear_config(struct acme_conf * xconf)10083943d840Sbenno clear_config(struct acme_conf *xconf)
10093943d840Sbenno {
10103943d840Sbenno 	struct authority_c	*a;
10113943d840Sbenno 	struct domain_c		*d;
10123943d840Sbenno 	struct altname_c	*ac;
10133943d840Sbenno 
1014221ac2aaSbenno 	while ((a = TAILQ_FIRST(&xconf->authority_list)) != NULL) {
1015221ac2aaSbenno 		TAILQ_REMOVE(&xconf->authority_list, a, entry);
10163943d840Sbenno 		free(a);
10173943d840Sbenno 	}
1018221ac2aaSbenno 	while ((d = TAILQ_FIRST(&xconf->domain_list)) != NULL) {
1019221ac2aaSbenno 		while ((ac = TAILQ_FIRST(&d->altname_list)) != NULL) {
1020221ac2aaSbenno 			TAILQ_REMOVE(&d->altname_list, ac, entry);
10213943d840Sbenno 			free(ac);
10223943d840Sbenno 		}
1023221ac2aaSbenno 		TAILQ_REMOVE(&xconf->domain_list, d, entry);
10243943d840Sbenno 		free(d);
10253943d840Sbenno 	}
10263943d840Sbenno 	free(xconf);
10273943d840Sbenno }
10283943d840Sbenno 
1029bc812290Sflorian const char*
kt2txt(enum keytype kt)1030bc812290Sflorian kt2txt(enum keytype kt)
1031bc812290Sflorian {
1032bc812290Sflorian 	switch (kt) {
1033bc812290Sflorian 	case KT_RSA:
1034bc812290Sflorian 		return "rsa";
1035bc812290Sflorian 	case KT_ECDSA:
1036bc812290Sflorian 		return "ecdsa";
1037bc812290Sflorian 	default:
1038bc812290Sflorian 		return "<unknown>";
1039bc812290Sflorian 	}
1040bc812290Sflorian }
1041bc812290Sflorian 
10423298b855Sbenno void
print_config(struct acme_conf * xconf)10433298b855Sbenno print_config(struct acme_conf *xconf)
10443298b855Sbenno {
10453298b855Sbenno 	struct authority_c	*a;
10463298b855Sbenno 	struct domain_c		*d;
10473298b855Sbenno 	struct altname_c	*ac;
10483298b855Sbenno 	int			 f;
10493298b855Sbenno 
1050221ac2aaSbenno 	TAILQ_FOREACH(a, &xconf->authority_list, entry) {
10513298b855Sbenno 		printf("authority %s {\n", a->name);
10523298b855Sbenno 		if (a->api != NULL)
10533298b855Sbenno 			printf("\tapi url \"%s\"\n", a->api);
10543298b855Sbenno 		if (a->account != NULL)
10554f8b772fSflorian 			printf("\taccount key \"%s\" %s\n", a->account,
10564f8b772fSflorian 			    kt2txt(a->keytype));
10573298b855Sbenno 		printf("}\n\n");
10583298b855Sbenno 	}
1059221ac2aaSbenno 	TAILQ_FOREACH(d, &xconf->domain_list, entry) {
10603298b855Sbenno 		f = 0;
106187f5451dSbenno 		printf("domain %s {\n", d->handle);
106287f5451dSbenno 		if (d->domain != NULL)
106387f5451dSbenno 			printf("\tdomain name \"%s\"\n", d->domain);
1064221ac2aaSbenno 		TAILQ_FOREACH(ac, &d->altname_list, entry) {
10653298b855Sbenno 			if (!f)
10663298b855Sbenno 				printf("\talternative names {");
10673298b855Sbenno 			if (ac->domain != NULL) {
10683298b855Sbenno 				printf("%s%s", f ? ", " : " ", ac->domain);
10693298b855Sbenno 				f = 1;
10703298b855Sbenno 			}
10713298b855Sbenno 		}
10723298b855Sbenno 		if (f)
10733298b855Sbenno 			printf(" }\n");
10743298b855Sbenno 		if (d->key != NULL)
1075bc812290Sflorian 			printf("\tdomain key \"%s\" %s\n", d->key, kt2txt(
1076bc812290Sflorian 			    d->keytype));
10773298b855Sbenno 		if (d->cert != NULL)
10783298b855Sbenno 			printf("\tdomain certificate \"%s\"\n", d->cert);
107933febeb9Sflorian 		if (d->chain != NULL)
10805c258182Sflorian 			printf("\tdomain chain certificate \"%s\"\n", d->chain);
108170bcb874Sbenno 		if (d->fullchain != NULL)
10825c258182Sflorian 			printf("\tdomain full chain certificate \"%s\"\n",
108370bcb874Sbenno 			    d->fullchain);
10843298b855Sbenno 		if (d->auth != NULL)
10853298b855Sbenno 			printf("\tsign with \"%s\"\n", d->auth);
10863298b855Sbenno 		if (d->challengedir != NULL)
10873298b855Sbenno 			printf("\tchallengedir \"%s\"\n", d->challengedir);
10883298b855Sbenno 		printf("}\n\n");
10893298b855Sbenno 	}
10903298b855Sbenno }
10913298b855Sbenno 
10923943d840Sbenno /*
10933943d840Sbenno  * This isn't RFC1035 compliant, but does the bare minimum in making
10943943d840Sbenno  * sure that we don't get bogus domain names on the command line, which
10953943d840Sbenno  * might otherwise screw up our directory structure.
10963943d840Sbenno  * Returns zero on failure, non-zero on success.
10973943d840Sbenno  */
10983943d840Sbenno int
domain_valid(const char * cp)10993943d840Sbenno domain_valid(const char *cp)
11003943d840Sbenno {
11013943d840Sbenno 
11024de82fa3Sderaadt 	for ( ; *cp != '\0'; cp++)
11034de82fa3Sderaadt 		if (!(*cp == '.' || *cp == '-' ||
1104*4a50067cSflorian 		    *cp == '_' || isalnum((unsigned char)*cp)))
11057d83751cSbenno 			return 0;
11067d83751cSbenno 	return 1;
11073943d840Sbenno }
11083943d840Sbenno 
11093943d840Sbenno int
conf_check_file(char * s)11102570ecd0Sflorian conf_check_file(char *s)
11113943d840Sbenno {
11123943d840Sbenno 	struct stat st;
11133943d840Sbenno 
11143943d840Sbenno 	if (s[0] != '/') {
11153943d840Sbenno 		warnx("%s: not an absolute path", s);
11167d83751cSbenno 		return 0;
11173943d840Sbenno 	}
11183943d840Sbenno 	if (stat(s, &st)) {
11192570ecd0Sflorian 		if (errno == ENOENT)
11202570ecd0Sflorian 			return 1;
11213943d840Sbenno 		warn("cannot stat %s", s);
11227d83751cSbenno 		return 0;
11233943d840Sbenno 	}
11243943d840Sbenno 	if (st.st_mode & (S_IRWXG | S_IRWXO)) {
11253943d840Sbenno 		warnx("%s: group read/writable or world read/writable", s);
11267d83751cSbenno 		return 0;
11273943d840Sbenno 	}
11287d83751cSbenno 	return 1;
11293943d840Sbenno }
1130