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