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