1262266Sbapt/*
2262266Sbapt * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
3262266Sbapt *
4262266Sbapt * This code is derived from software contributed to The DragonFly Project
5262266Sbapt * by Matthias Schmidt <matthias@dragonflybsd.org>, University of Marburg,
6262266Sbapt * Germany.
7262266Sbapt *
8262266Sbapt * Redistribution and use in source and binary forms, with or without
9262266Sbapt * modification, are permitted provided that the following conditions
10262266Sbapt * are met:
11262266Sbapt *
12262266Sbapt * 1. Redistributions of source code must retain the above copyright
13262266Sbapt *    notice, this list of conditions and the following disclaimer.
14262266Sbapt * 2. Redistributions in binary form must reproduce the above copyright
15262266Sbapt *    notice, this list of conditions and the following disclaimer in
16262266Sbapt *    the documentation and/or other materials provided with the
17262266Sbapt *    distribution.
18262266Sbapt * 3. Neither the name of The DragonFly Project nor the names of its
19262266Sbapt *    contributors may be used to endorse or promote products derived
20262266Sbapt *    from this software without specific, prior written permission.
21262266Sbapt *
22262266Sbapt * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23262266Sbapt * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24262266Sbapt * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25262266Sbapt * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26262266Sbapt * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27262266Sbapt * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28262266Sbapt * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29262266Sbapt * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30262266Sbapt * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31262266Sbapt * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32262266Sbapt * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33262266Sbapt * SUCH DAMAGE.
34262266Sbapt */
35262266Sbapt
36262266Sbapt#include <err.h>
37262266Sbapt#include <errno.h>
38262266Sbapt#include <stdio.h>
39262266Sbapt#include <stdlib.h>
40262266Sbapt#include <string.h>
41262266Sbapt#include <syslog.h>
42262266Sbapt#include <stdarg.h>
43262266Sbapt
44262266Sbapt#include "dma.h"
45262266Sbapt
46262266Sbapt#define DP	": \t"
47262266Sbapt#define EQS	" \t"
48262266Sbapt
49262266Sbapt
50262266Sbapt/*
51262266Sbapt * Remove trailing \n's
52262266Sbapt */
53262266Sbaptvoid
54262266Sbapttrim_line(char *line)
55262266Sbapt{
56262266Sbapt	size_t linelen;
57262266Sbapt	char *p;
58262266Sbapt
59262266Sbapt	if ((p = strchr(line, '\n')))
60262266Sbapt		*p = (char)0;
61262266Sbapt
62262266Sbapt	/* Escape leading dot in every case */
63262266Sbapt	linelen = strlen(line);
64262266Sbapt	if (line[0] == '.') {
65262266Sbapt		if ((linelen + 2) > 1000) {
66262266Sbapt			syslog(LOG_CRIT, "Cannot escape leading dot.  Buffer overflow");
67289123Sbapt			exit(EX_DATAERR);
68262266Sbapt		}
69262266Sbapt		memmove((line + 1), line, (linelen + 1));
70262266Sbapt		line[0] = '.';
71262266Sbapt	}
72262266Sbapt}
73262266Sbapt
74262266Sbaptstatic void
75262266Sbaptchomp(char *str)
76262266Sbapt{
77262266Sbapt	size_t len = strlen(str);
78262266Sbapt
79262266Sbapt	if (len == 0)
80262266Sbapt		return;
81262266Sbapt	if (str[len - 1] == '\n')
82262266Sbapt		str[len - 1] = 0;
83262266Sbapt}
84262266Sbapt
85262266Sbapt/*
86262266Sbapt * Read the SMTP authentication config file
87262266Sbapt *
88262266Sbapt * file format is:
89262266Sbapt * user|host:password
90262266Sbapt *
91262266Sbapt * A line starting with # is treated as comment and ignored.
92262266Sbapt */
93262266Sbaptvoid
94262266Sbaptparse_authfile(const char *path)
95262266Sbapt{
96262266Sbapt	char line[2048];
97262266Sbapt	struct authuser *au;
98262266Sbapt	FILE *a;
99262266Sbapt	char *data;
100262266Sbapt	int lineno = 0;
101262266Sbapt
102262266Sbapt	a = fopen(path, "r");
103262266Sbapt	if (a == NULL) {
104289123Sbapt		errlog(EX_NOINPUT, "can not open auth file `%s'", path);
105262266Sbapt		/* NOTREACHED */
106262266Sbapt	}
107262266Sbapt
108262266Sbapt	while (!feof(a)) {
109262266Sbapt		if (fgets(line, sizeof(line), a) == NULL)
110262266Sbapt			break;
111262266Sbapt		lineno++;
112262266Sbapt
113262266Sbapt		chomp(line);
114262266Sbapt
115262266Sbapt		/* We hit a comment */
116262266Sbapt		if (*line == '#')
117262266Sbapt			continue;
118262266Sbapt		/* Ignore empty lines */
119262266Sbapt		if (*line == 0)
120262266Sbapt			continue;
121262266Sbapt
122262266Sbapt		au = calloc(1, sizeof(*au));
123262266Sbapt		if (au == NULL)
124289144Sbapt			errlog(EX_OSERR, "calloc()");
125262266Sbapt
126262266Sbapt		data = strdup(line);
127262266Sbapt		au->login = strsep(&data, "|");
128262266Sbapt		au->host = strsep(&data, DP);
129262266Sbapt		au->password = data;
130262266Sbapt
131262266Sbapt		if (au->login == NULL ||
132262266Sbapt		    au->host == NULL ||
133262266Sbapt		    au->password == NULL) {
134289123Sbapt			errlogx(EX_CONFIG, "syntax error in authfile %s:%d", path, lineno);
135262266Sbapt			/* NOTREACHED */
136262266Sbapt		}
137262266Sbapt
138262266Sbapt		SLIST_INSERT_HEAD(&authusers, au, next);
139262266Sbapt	}
140262266Sbapt
141262266Sbapt	fclose(a);
142262266Sbapt}
143262266Sbapt
144262266Sbapt/*
145262266Sbapt * XXX TODO
146262266Sbapt * Check for bad things[TM]
147262266Sbapt */
148262266Sbaptvoid
149262266Sbaptparse_conf(const char *config_path)
150262266Sbapt{
151262266Sbapt	char *word;
152262266Sbapt	char *data;
153262266Sbapt	FILE *conf;
154262266Sbapt	char line[2048];
155262266Sbapt	int lineno = 0;
156262266Sbapt
157262266Sbapt	conf = fopen(config_path, "r");
158262266Sbapt	if (conf == NULL) {
159262266Sbapt		/* Don't treat a non-existing config file as error */
160262266Sbapt		if (errno == ENOENT)
161262266Sbapt			return;
162289123Sbapt		errlog(EX_NOINPUT, "can not open config `%s'", config_path);
163262266Sbapt		/* NOTREACHED */
164262266Sbapt	}
165262266Sbapt
166262266Sbapt	while (!feof(conf)) {
167262266Sbapt		if (fgets(line, sizeof(line), conf) == NULL)
168262266Sbapt			break;
169262266Sbapt		lineno++;
170262266Sbapt
171262266Sbapt		chomp(line);
172262266Sbapt
173262266Sbapt		/* We hit a comment */
174262266Sbapt		if (strchr(line, '#'))
175262266Sbapt			*strchr(line, '#') = 0;
176262266Sbapt
177262266Sbapt		data = line;
178262266Sbapt		word = strsep(&data, EQS);
179262266Sbapt
180262266Sbapt		/* Ignore empty lines */
181262266Sbapt		if (word == NULL || *word == 0)
182262266Sbapt			continue;
183262266Sbapt
184262266Sbapt		if (data != NULL && *data != 0)
185262266Sbapt			data = strdup(data);
186262266Sbapt		else
187262266Sbapt			data = NULL;
188262266Sbapt
189262266Sbapt		if (strcmp(word, "SMARTHOST") == 0 && data != NULL)
190262266Sbapt			config.smarthost = data;
191262266Sbapt		else if (strcmp(word, "PORT") == 0 && data != NULL)
192262266Sbapt			config.port = atoi(data);
193262266Sbapt		else if (strcmp(word, "ALIASES") == 0 && data != NULL)
194262266Sbapt			config.aliases = data;
195262266Sbapt		else if (strcmp(word, "SPOOLDIR") == 0 && data != NULL)
196262266Sbapt			config.spooldir = data;
197262266Sbapt		else if (strcmp(word, "AUTHPATH") == 0 && data != NULL)
198262266Sbapt			config.authpath= data;
199262266Sbapt		else if (strcmp(word, "CERTFILE") == 0 && data != NULL)
200262266Sbapt			config.certfile = data;
201262266Sbapt		else if (strcmp(word, "MAILNAME") == 0 && data != NULL)
202262266Sbapt			config.mailname = data;
203262266Sbapt		else if (strcmp(word, "MASQUERADE") == 0 && data != NULL) {
204262266Sbapt			char *user = NULL, *host = NULL;
205262266Sbapt			if (strrchr(data, '@')) {
206262266Sbapt				host = strrchr(data, '@');
207262266Sbapt				*host = 0;
208262266Sbapt				host++;
209262266Sbapt				user = data;
210262266Sbapt			} else {
211262266Sbapt				host = data;
212262266Sbapt			}
213289123Sbapt			if (host && *host == 0)
214262266Sbapt				host = NULL;
215262266Sbapt                        if (user && *user == 0)
216262266Sbapt                                user = NULL;
217262266Sbapt			config.masquerade_host = host;
218262266Sbapt			config.masquerade_user = user;
219262266Sbapt		} else if (strcmp(word, "STARTTLS") == 0 && data == NULL)
220262266Sbapt			config.features |= STARTTLS;
221262266Sbapt		else if (strcmp(word, "OPPORTUNISTIC_TLS") == 0 && data == NULL)
222262266Sbapt			config.features |= TLS_OPP;
223262266Sbapt		else if (strcmp(word, "SECURETRANSFER") == 0 && data == NULL)
224262266Sbapt			config.features |= SECURETRANS;
225262266Sbapt		else if (strcmp(word, "DEFER") == 0 && data == NULL)
226262266Sbapt			config.features |= DEFER;
227262266Sbapt		else if (strcmp(word, "INSECURE") == 0 && data == NULL)
228262266Sbapt			config.features |= INSECURE;
229262266Sbapt		else if (strcmp(word, "FULLBOUNCE") == 0 && data == NULL)
230262266Sbapt			config.features |= FULLBOUNCE;
231262266Sbapt		else if (strcmp(word, "NULLCLIENT") == 0 && data == NULL)
232262266Sbapt			config.features |= NULLCLIENT;
233262266Sbapt		else {
234289123Sbapt			errlogx(EX_CONFIG, "syntax error in %s:%d", config_path, lineno);
235262266Sbapt			/* NOTREACHED */
236262266Sbapt		}
237262266Sbapt	}
238262266Sbapt
239262266Sbapt	if ((config.features & NULLCLIENT) && config.smarthost == NULL) {
240289123Sbapt		errlogx(EX_CONFIG, "%s: NULLCLIENT requires SMARTHOST", config_path);
241262266Sbapt		/* NOTREACHED */
242262266Sbapt	}
243262266Sbapt
244262266Sbapt	fclose(conf);
245262266Sbapt}
246