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