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