xref: /dflybsd-src/libexec/dma/conf.c (revision 92fe556d1644256324e534f2cbaef0e73e2d85bc)
1f67bedddSMatthias Schmidt /*
2f67bedddSMatthias Schmidt  * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
3f67bedddSMatthias Schmidt  *
4f67bedddSMatthias Schmidt  * This code is derived from software contributed to The DragonFly Project
5f67bedddSMatthias Schmidt  * by Matthias Schmidt <matthias@dragonflybsd.org>, University of Marburg,
6f67bedddSMatthias Schmidt  * Germany.
7f67bedddSMatthias Schmidt  *
8f67bedddSMatthias Schmidt  * Redistribution and use in source and binary forms, with or without
9f67bedddSMatthias Schmidt  * modification, are permitted provided that the following conditions
10f67bedddSMatthias Schmidt  * are met:
11f67bedddSMatthias Schmidt  *
12f67bedddSMatthias Schmidt  * 1. Redistributions of source code must retain the above copyright
13f67bedddSMatthias Schmidt  *    notice, this list of conditions and the following disclaimer.
14f67bedddSMatthias Schmidt  * 2. Redistributions in binary form must reproduce the above copyright
15f67bedddSMatthias Schmidt  *    notice, this list of conditions and the following disclaimer in
16f67bedddSMatthias Schmidt  *    the documentation and/or other materials provided with the
17f67bedddSMatthias Schmidt  *    distribution.
18f67bedddSMatthias Schmidt  * 3. Neither the name of The DragonFly Project nor the names of its
19f67bedddSMatthias Schmidt  *    contributors may be used to endorse or promote products derived
20f67bedddSMatthias Schmidt  *    from this software without specific, prior written permission.
21f67bedddSMatthias Schmidt  *
22f67bedddSMatthias Schmidt  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23f67bedddSMatthias Schmidt  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24f67bedddSMatthias Schmidt  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25f67bedddSMatthias Schmidt  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26f67bedddSMatthias Schmidt  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27f67bedddSMatthias Schmidt  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28f67bedddSMatthias Schmidt  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29f67bedddSMatthias Schmidt  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30f67bedddSMatthias Schmidt  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31f67bedddSMatthias Schmidt  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32f67bedddSMatthias Schmidt  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33f67bedddSMatthias Schmidt  * SUCH DAMAGE.
34f67bedddSMatthias Schmidt  */
35f67bedddSMatthias Schmidt 
36f67bedddSMatthias Schmidt #include <err.h>
37e2c88018SSimon Schubert #include <errno.h>
38f67bedddSMatthias Schmidt #include <stdio.h>
39f67bedddSMatthias Schmidt #include <stdlib.h>
40f67bedddSMatthias Schmidt #include <string.h>
41f67bedddSMatthias Schmidt #include <syslog.h>
42f67bedddSMatthias Schmidt #include <stdarg.h>
43f67bedddSMatthias Schmidt 
44f67bedddSMatthias Schmidt #include "dma.h"
45f67bedddSMatthias Schmidt 
46ca259d14SSimon Schubert #define DP	": \t"
47ca259d14SSimon Schubert #define EQS	" \t"
48f67bedddSMatthias Schmidt 
49f67bedddSMatthias Schmidt 
50f67bedddSMatthias Schmidt /*
51f67bedddSMatthias Schmidt  * Remove trailing \n's
52f67bedddSMatthias Schmidt  */
53f67bedddSMatthias Schmidt void
trim_line(char * line)54f67bedddSMatthias Schmidt trim_line(char *line)
55f67bedddSMatthias Schmidt {
56f67bedddSMatthias Schmidt 	size_t linelen;
57f67bedddSMatthias Schmidt 	char *p;
58f67bedddSMatthias Schmidt 
59f67bedddSMatthias Schmidt 	if ((p = strchr(line, '\n')))
60f67bedddSMatthias Schmidt 		*p = (char)0;
61f67bedddSMatthias Schmidt 
62f67bedddSMatthias Schmidt 	/* Escape leading dot in every case */
63f67bedddSMatthias Schmidt 	linelen = strlen(line);
64f67bedddSMatthias Schmidt 	if (line[0] == '.') {
65f67bedddSMatthias Schmidt 		if ((linelen + 2) > 1000) {
66f67bedddSMatthias Schmidt 			syslog(LOG_CRIT, "Cannot escape leading dot.  Buffer overflow");
67*92fe556dSDaniel Fojt 			exit(EX_DATAERR);
68f67bedddSMatthias Schmidt 		}
69f67bedddSMatthias Schmidt 		memmove((line + 1), line, (linelen + 1));
70f67bedddSMatthias Schmidt 		line[0] = '.';
71f67bedddSMatthias Schmidt 	}
72f67bedddSMatthias Schmidt }
73f67bedddSMatthias Schmidt 
74f67bedddSMatthias Schmidt static void
chomp(char * str)75ca259d14SSimon Schubert chomp(char *str)
76f67bedddSMatthias Schmidt {
77ca259d14SSimon Schubert 	size_t len = strlen(str);
78f67bedddSMatthias Schmidt 
79ca259d14SSimon Schubert 	if (len == 0)
80ca259d14SSimon Schubert 		return;
81ca259d14SSimon Schubert 	if (str[len - 1] == '\n')
82ca259d14SSimon Schubert 		str[len - 1] = 0;
83f67bedddSMatthias Schmidt }
84f67bedddSMatthias Schmidt 
85f67bedddSMatthias Schmidt /*
86f67bedddSMatthias Schmidt  * Read the SMTP authentication config file
87ca259d14SSimon Schubert  *
88ca259d14SSimon Schubert  * file format is:
89ca259d14SSimon Schubert  * user|host:password
90ca259d14SSimon Schubert  *
91ca259d14SSimon Schubert  * A line starting with # is treated as comment and ignored.
92f67bedddSMatthias Schmidt  */
93ca259d14SSimon Schubert void
parse_authfile(const char * path)94f67bedddSMatthias Schmidt parse_authfile(const char *path)
95f67bedddSMatthias Schmidt {
96f67bedddSMatthias Schmidt 	char line[2048];
97ca259d14SSimon Schubert 	struct authuser *au;
98ca259d14SSimon Schubert 	FILE *a;
99ca259d14SSimon Schubert 	char *data;
100ca259d14SSimon Schubert 	int lineno = 0;
101f67bedddSMatthias Schmidt 
102f67bedddSMatthias Schmidt 	a = fopen(path, "r");
103ca259d14SSimon Schubert 	if (a == NULL) {
104*92fe556dSDaniel Fojt 		errlog(EX_NOINPUT, "can not open auth file `%s'", path);
105ca259d14SSimon Schubert 		/* NOTREACHED */
106ca259d14SSimon Schubert 	}
107f67bedddSMatthias Schmidt 
108f67bedddSMatthias Schmidt 	while (!feof(a)) {
1092922fd2bSSimon Schubert 		if (fgets(line, sizeof(line), a) == NULL)
1102922fd2bSSimon Schubert 			break;
111ca259d14SSimon Schubert 		lineno++;
112ca259d14SSimon Schubert 
113ca259d14SSimon Schubert 		chomp(line);
114ca259d14SSimon Schubert 
115f67bedddSMatthias Schmidt 		/* We hit a comment */
116ca259d14SSimon Schubert 		if (*line == '#')
117ca259d14SSimon Schubert 			continue;
118ca259d14SSimon Schubert 		/* Ignore empty lines */
119ca259d14SSimon Schubert 		if (*line == 0)
120ca259d14SSimon Schubert 			continue;
121ca259d14SSimon Schubert 
122ca259d14SSimon Schubert 		au = calloc(1, sizeof(*au));
123ca259d14SSimon Schubert 		if (au == NULL)
124*92fe556dSDaniel Fojt 			errlog(EX_OSERR, NULL);
125ca259d14SSimon Schubert 
126ca259d14SSimon Schubert 		data = strdup(line);
127ca259d14SSimon Schubert 		au->login = strsep(&data, "|");
128ca259d14SSimon Schubert 		au->host = strsep(&data, DP);
129ca259d14SSimon Schubert 		au->password = data;
130ca259d14SSimon Schubert 
131ca259d14SSimon Schubert 		if (au->login == NULL ||
132ca259d14SSimon Schubert 		    au->host == NULL ||
133ca259d14SSimon Schubert 		    au->password == NULL) {
134*92fe556dSDaniel Fojt 			errlogx(EX_CONFIG, "syntax error in authfile %s:%d", path, lineno);
135ca259d14SSimon Schubert 			/* NOTREACHED */
136f67bedddSMatthias Schmidt 		}
137ca259d14SSimon Schubert 
138ca259d14SSimon Schubert 		SLIST_INSERT_HEAD(&authusers, au, next);
139f67bedddSMatthias Schmidt 	}
140f67bedddSMatthias Schmidt 
141f67bedddSMatthias Schmidt 	fclose(a);
142f67bedddSMatthias Schmidt }
143f67bedddSMatthias Schmidt 
144f67bedddSMatthias Schmidt /*
145f67bedddSMatthias Schmidt  * XXX TODO
146f67bedddSMatthias Schmidt  * Check for bad things[TM]
147f67bedddSMatthias Schmidt  */
148ca259d14SSimon Schubert void
parse_conf(const char * config_path)149f4e61a9fSSimon 'corecode' Schubert parse_conf(const char *config_path)
150f67bedddSMatthias Schmidt {
151f67bedddSMatthias Schmidt 	char *word;
152f67bedddSMatthias Schmidt 	char *data;
153f67bedddSMatthias Schmidt 	FILE *conf;
154f67bedddSMatthias Schmidt 	char line[2048];
155ca259d14SSimon Schubert 	int lineno = 0;
156f67bedddSMatthias Schmidt 
157f67bedddSMatthias Schmidt 	conf = fopen(config_path, "r");
158ca259d14SSimon Schubert 	if (conf == NULL) {
159ca259d14SSimon Schubert 		/* Don't treat a non-existing config file as error */
160ca259d14SSimon Schubert 		if (errno == ENOENT)
161ca259d14SSimon Schubert 			return;
162*92fe556dSDaniel Fojt 		errlog(EX_NOINPUT, "can not open config `%s'", config_path);
163ca259d14SSimon Schubert 		/* NOTREACHED */
164ca259d14SSimon Schubert 	}
165f67bedddSMatthias Schmidt 
166f67bedddSMatthias Schmidt 	while (!feof(conf)) {
1672922fd2bSSimon Schubert 		if (fgets(line, sizeof(line), conf) == NULL)
1682922fd2bSSimon Schubert 			break;
169ca259d14SSimon Schubert 		lineno++;
170ca259d14SSimon Schubert 
171ca259d14SSimon Schubert 		chomp(line);
172ca259d14SSimon Schubert 
173f67bedddSMatthias Schmidt 		/* We hit a comment */
174f67bedddSMatthias Schmidt 		if (strchr(line, '#'))
175f67bedddSMatthias Schmidt 			*strchr(line, '#') = 0;
176ca259d14SSimon Schubert 
177ca259d14SSimon Schubert 		data = line;
178ca259d14SSimon Schubert 		word = strsep(&data, EQS);
179ca259d14SSimon Schubert 
180ca259d14SSimon Schubert 		/* Ignore empty lines */
181ca259d14SSimon Schubert 		if (word == NULL || *word == 0)
182ca259d14SSimon Schubert 			continue;
183ca259d14SSimon Schubert 
184ca259d14SSimon Schubert 		if (data != NULL && *data != 0)
185ca259d14SSimon Schubert 			data = strdup(data);
186ca259d14SSimon Schubert 		else
187ca259d14SSimon Schubert 			data = NULL;
188ca259d14SSimon Schubert 
189ca259d14SSimon Schubert 		if (strcmp(word, "SMARTHOST") == 0 && data != NULL)
190ca259d14SSimon Schubert 			config.smarthost = data;
191ca259d14SSimon Schubert 		else if (strcmp(word, "PORT") == 0 && data != NULL)
192ca259d14SSimon Schubert 			config.port = atoi(data);
193ca259d14SSimon Schubert 		else if (strcmp(word, "ALIASES") == 0 && data != NULL)
194ca259d14SSimon Schubert 			config.aliases = data;
195ca259d14SSimon Schubert 		else if (strcmp(word, "SPOOLDIR") == 0 && data != NULL)
196ca259d14SSimon Schubert 			config.spooldir = data;
197ca259d14SSimon Schubert 		else if (strcmp(word, "AUTHPATH") == 0 && data != NULL)
198ca259d14SSimon Schubert 			config.authpath= data;
199ca259d14SSimon Schubert 		else if (strcmp(word, "CERTFILE") == 0 && data != NULL)
200ca259d14SSimon Schubert 			config.certfile = data;
201ca259d14SSimon Schubert 		else if (strcmp(word, "MAILNAME") == 0 && data != NULL)
202ca259d14SSimon Schubert 			config.mailname = data;
203c8b07ee5SSascha Wildner 		else if (strcmp(word, "MASQUERADE") == 0 && data != NULL) {
204c8b07ee5SSascha Wildner 			char *user = NULL, *host = NULL;
205c8b07ee5SSascha Wildner 			if (strrchr(data, '@')) {
206c8b07ee5SSascha Wildner 				host = strrchr(data, '@');
207c8b07ee5SSascha Wildner 				*host = 0;
208c8b07ee5SSascha Wildner 				host++;
209c8b07ee5SSascha Wildner 				user = data;
210c8b07ee5SSascha Wildner 			} else {
211c8b07ee5SSascha Wildner 				host = data;
212c8b07ee5SSascha Wildner 			}
213c8b07ee5SSascha Wildner 			if (host && *host == 0)
214c8b07ee5SSascha Wildner 				host = NULL;
215c8b07ee5SSascha Wildner                         if (user && *user == 0)
216c8b07ee5SSascha Wildner                                 user = NULL;
217c8b07ee5SSascha Wildner 			config.masquerade_host = host;
218c8b07ee5SSascha Wildner 			config.masquerade_user = user;
219c8b07ee5SSascha Wildner 		} else if (strcmp(word, "STARTTLS") == 0 && data == NULL)
220ca259d14SSimon Schubert 			config.features |= STARTTLS;
221*92fe556dSDaniel Fojt 		else if (strcmp(word, "FINGERPRINT") == 0) {
222*92fe556dSDaniel Fojt 			if (strlen(data) != SHA256_DIGEST_LENGTH * 2) {
223*92fe556dSDaniel Fojt 				errlogx(EX_CONFIG, "invalid sha256 fingerprint length");
224*92fe556dSDaniel Fojt 			}
225*92fe556dSDaniel Fojt 			unsigned char *fingerprint = malloc(SHA256_DIGEST_LENGTH);
226*92fe556dSDaniel Fojt 			if (fingerprint == NULL) {
227*92fe556dSDaniel Fojt 				errlogx(EX_CONFIG, "fingerprint allocation failed");
228*92fe556dSDaniel Fojt 			}
229*92fe556dSDaniel Fojt 			for (unsigned int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
230*92fe556dSDaniel Fojt 				if(sscanf(data + 2 * i, "%02hhx", &fingerprint[i]) != 1) {
231*92fe556dSDaniel Fojt 					errlogx(EX_CONFIG, "failed to read fingerprint");
232*92fe556dSDaniel Fojt 				}
233*92fe556dSDaniel Fojt 			}
234*92fe556dSDaniel Fojt 			free(data);
235*92fe556dSDaniel Fojt 			config.fingerprint = fingerprint;
236*92fe556dSDaniel Fojt 		} else if (strcmp(word, "OPPORTUNISTIC_TLS") == 0 && data == NULL)
237c8b07ee5SSascha Wildner 			config.features |= TLS_OPP;
238ca259d14SSimon Schubert 		else if (strcmp(word, "SECURETRANSFER") == 0 && data == NULL)
239*92fe556dSDaniel Fojt 			config.features |= SECURETRANSFER;
240ca259d14SSimon Schubert 		else if (strcmp(word, "DEFER") == 0 && data == NULL)
241ca259d14SSimon Schubert 			config.features |= DEFER;
242ca259d14SSimon Schubert 		else if (strcmp(word, "INSECURE") == 0 && data == NULL)
243ca259d14SSimon Schubert 			config.features |= INSECURE;
244ca259d14SSimon Schubert 		else if (strcmp(word, "FULLBOUNCE") == 0 && data == NULL)
245ca259d14SSimon Schubert 			config.features |= FULLBOUNCE;
24614dfb991SJoris Giovannangeli 		else if (strcmp(word, "NULLCLIENT") == 0 && data == NULL)
24714dfb991SJoris Giovannangeli 			config.features |= NULLCLIENT;
248e2c88018SSimon Schubert 		else {
249*92fe556dSDaniel Fojt 			errlogx(EX_CONFIG, "syntax error in %s:%d", config_path, lineno);
250ca259d14SSimon Schubert 			/* NOTREACHED */
251f67bedddSMatthias Schmidt 		}
252f67bedddSMatthias Schmidt 	}
253f67bedddSMatthias Schmidt 
25414dfb991SJoris Giovannangeli 	if ((config.features & NULLCLIENT) && config.smarthost == NULL) {
255*92fe556dSDaniel Fojt 		errlogx(EX_CONFIG, "%s: NULLCLIENT requires SMARTHOST", config_path);
25614dfb991SJoris Giovannangeli 		/* NOTREACHED */
25714dfb991SJoris Giovannangeli 	}
25814dfb991SJoris Giovannangeli 
259f67bedddSMatthias Schmidt 	fclose(conf);
260f67bedddSMatthias Schmidt }
261