Blame


1 e9e8d207 2020-07-25 martijn /*
2 e9e8d207 2020-07-25 martijn * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
3 e9e8d207 2020-07-25 martijn *
4 e9e8d207 2020-07-25 martijn * Permission to use, copy, modify, and distribute this software for any
5 e9e8d207 2020-07-25 martijn * purpose with or without fee is hereby granted, provided that the above
6 e9e8d207 2020-07-25 martijn * copyright notice and this permission notice appear in all copies.
7 e9e8d207 2020-07-25 martijn *
8 e9e8d207 2020-07-25 martijn * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 e9e8d207 2020-07-25 martijn * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 e9e8d207 2020-07-25 martijn * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 e9e8d207 2020-07-25 martijn * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 e9e8d207 2020-07-25 martijn * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 e9e8d207 2020-07-25 martijn * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 e9e8d207 2020-07-25 martijn * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 e9e8d207 2020-07-25 martijn */
16 e9e8d207 2020-07-25 martijn
17 e9e8d207 2020-07-25 martijn #include <errno.h>
18 e9e8d207 2020-07-25 martijn #include <inttypes.h>
19 e9e8d207 2020-07-25 martijn #include <stdio.h>
20 e9e8d207 2020-07-25 martijn #include <stdlib.h>
21 e9e8d207 2020-07-25 martijn #include <string.h>
22 e9e8d207 2020-07-25 martijn #include <time.h>
23 e9e8d207 2020-07-25 martijn #include <unistd.h>
24 e9e8d207 2020-07-25 martijn
25 cb6053ee 2021-05-11 martijn #include "openbsd-compat.h"
26 e9e8d207 2020-07-25 martijn #include "opensmtpd.h"
27 4f29629b 2020-08-30 martijn #include "mheader.h"
28 e9e8d207 2020-07-25 martijn
29 e9e8d207 2020-07-25 martijn struct admd_message {
30 e9e8d207 2020-07-25 martijn int foundmatch;
31 e9e8d207 2020-07-25 martijn int err;
32 e9e8d207 2020-07-25 martijn int inheader;
33 e9e8d207 2020-07-25 martijn int parsing_headers;
34 e9e8d207 2020-07-25 martijn char **cache;
35 e9e8d207 2020-07-25 martijn size_t cachelen;
36 4f29629b 2020-08-30 martijn size_t headerlen;
37 e9e8d207 2020-07-25 martijn };
38 e9e8d207 2020-07-25 martijn
39 e9e8d207 2020-07-25 martijn void usage(void);
40 b7482b5c 2020-10-15 martijn void admd_conf(const char *, const char *);
41 e9e8d207 2020-07-25 martijn void *admd_message_new(struct osmtpd_ctx *);
42 e9e8d207 2020-07-25 martijn void admd_message_free(struct osmtpd_ctx *, void *);
43 e9e8d207 2020-07-25 martijn void admd_dataline(struct osmtpd_ctx *, const char *);
44 e9e8d207 2020-07-25 martijn void admd_commit(struct osmtpd_ctx *);
45 e9e8d207 2020-07-25 martijn void admd_err(struct admd_message *, const char *);
46 e9e8d207 2020-07-25 martijn void admd_cache(struct admd_message *, const char *);
47 e9e8d207 2020-07-25 martijn const char *admd_authservid(struct admd_message *);
48 e9e8d207 2020-07-25 martijn void admd_freecache(struct admd_message *);
49 e9e8d207 2020-07-25 martijn
50 b7482b5c 2020-10-15 martijn char *authservid;
51 e9e8d207 2020-07-25 martijn int reject = 0;
52 e9e8d207 2020-07-25 martijn int verbose = 0;
53 e9e8d207 2020-07-25 martijn
54 e9e8d207 2020-07-25 martijn int
55 e9e8d207 2020-07-25 martijn main(int argc, char *argv[])
56 e9e8d207 2020-07-25 martijn {
57 e9e8d207 2020-07-25 martijn int ch;
58 e9e8d207 2020-07-25 martijn
59 4f29629b 2020-08-30 martijn if (pledge("stdio", NULL) == -1)
60 4f29629b 2020-08-30 martijn osmtpd_err(1, "pledge");
61 4f29629b 2020-08-30 martijn
62 4f29629b 2020-08-30 martijn while ((ch = getopt(argc, argv, "rv")) != -1) {
63 e9e8d207 2020-07-25 martijn switch (ch) {
64 e9e8d207 2020-07-25 martijn case 'r':
65 e9e8d207 2020-07-25 martijn reject = 1;
66 e9e8d207 2020-07-25 martijn break;
67 e9e8d207 2020-07-25 martijn case 'v':
68 e9e8d207 2020-07-25 martijn verbose++;
69 e9e8d207 2020-07-25 martijn break;
70 e9e8d207 2020-07-25 martijn default:
71 e9e8d207 2020-07-25 martijn usage();
72 e9e8d207 2020-07-25 martijn }
73 e9e8d207 2020-07-25 martijn }
74 4f29629b 2020-08-30 martijn argc -= optind;
75 4f29629b 2020-08-30 martijn argv += optind;
76 4f29629b 2020-08-30 martijn if (argc > 1)
77 4f29629b 2020-08-30 martijn osmtpd_errx(1, "invalid authservid count");
78 b7482b5c 2020-10-15 martijn if (argc == 1)
79 b7482b5c 2020-10-15 martijn authservid = argv[0];
80 e9e8d207 2020-07-25 martijn
81 e9e8d207 2020-07-25 martijn osmtpd_local_message(admd_message_new, admd_message_free);
82 e9e8d207 2020-07-25 martijn osmtpd_register_filter_dataline(admd_dataline);
83 e9e8d207 2020-07-25 martijn osmtpd_register_filter_commit(admd_commit);
84 b7482b5c 2020-10-15 martijn osmtpd_register_conf(admd_conf);
85 e9e8d207 2020-07-25 martijn osmtpd_run();
86 e9e8d207 2020-07-25 martijn
87 e9e8d207 2020-07-25 martijn return 0;
88 e9e8d207 2020-07-25 martijn }
89 e9e8d207 2020-07-25 martijn
90 b7482b5c 2020-10-15 martijn void
91 b7482b5c 2020-10-15 martijn admd_conf(const char *key, const char *value)
92 b7482b5c 2020-10-15 martijn {
93 b7482b5c 2020-10-15 martijn if (key == NULL) {
94 b7482b5c 2020-10-15 martijn if (authservid == NULL)
95 08490891 2020-12-14 martijn osmtpd_errx(1, "Didn't receive admd config option");
96 b7482b5c 2020-10-15 martijn return;
97 b7482b5c 2020-10-15 martijn }
98 b7482b5c 2020-10-15 martijn if (strcmp(key, "admd") == 0 && authservid == NULL) {
99 b7482b5c 2020-10-15 martijn if ((authservid = strdup(value)) == NULL)
100 b7482b5c 2020-10-15 martijn osmtpd_err(1, "malloc");
101 b7482b5c 2020-10-15 martijn }
102 b7482b5c 2020-10-15 martijn }
103 b7482b5c 2020-10-15 martijn
104 e9e8d207 2020-07-25 martijn void *
105 e9e8d207 2020-07-25 martijn admd_message_new(struct osmtpd_ctx *ctx)
106 e9e8d207 2020-07-25 martijn {
107 e9e8d207 2020-07-25 martijn struct admd_message *msg;
108 e9e8d207 2020-07-25 martijn
109 e9e8d207 2020-07-25 martijn if ((msg = malloc(sizeof(*msg))) == NULL)
110 e9e8d207 2020-07-25 martijn osmtpd_err(1, "malloc");
111 e9e8d207 2020-07-25 martijn
112 e9e8d207 2020-07-25 martijn msg->foundmatch = 0;
113 e9e8d207 2020-07-25 martijn msg->err = 0;
114 e9e8d207 2020-07-25 martijn msg->inheader = 0;
115 e9e8d207 2020-07-25 martijn msg->parsing_headers = 1;
116 e9e8d207 2020-07-25 martijn msg->cache = NULL;
117 e9e8d207 2020-07-25 martijn msg->cachelen = 0;
118 4f29629b 2020-08-30 martijn msg->headerlen = 0;
119 e9e8d207 2020-07-25 martijn
120 e9e8d207 2020-07-25 martijn return msg;
121 e9e8d207 2020-07-25 martijn }
122 e9e8d207 2020-07-25 martijn
123 e9e8d207 2020-07-25 martijn void
124 e9e8d207 2020-07-25 martijn admd_message_free(struct osmtpd_ctx *ctx, void *data)
125 e9e8d207 2020-07-25 martijn {
126 e9e8d207 2020-07-25 martijn struct admd_message *msg = data;
127 e9e8d207 2020-07-25 martijn
128 e9e8d207 2020-07-25 martijn admd_freecache(msg);
129 e9e8d207 2020-07-25 martijn free(msg);
130 e9e8d207 2020-07-25 martijn }
131 e9e8d207 2020-07-25 martijn
132 e9e8d207 2020-07-25 martijn void
133 e9e8d207 2020-07-25 martijn admd_dataline(struct osmtpd_ctx *ctx, const char *orig)
134 e9e8d207 2020-07-25 martijn {
135 e9e8d207 2020-07-25 martijn struct admd_message *msg = ctx->local_message;
136 e9e8d207 2020-07-25 martijn const char *line = orig;
137 e9e8d207 2020-07-25 martijn const char *msgauthid;
138 e9e8d207 2020-07-25 martijn size_t i;
139 e9e8d207 2020-07-25 martijn
140 e9e8d207 2020-07-25 martijn if (msg->err) {
141 e9e8d207 2020-07-25 martijn if (line[0] == '.' && line[1] =='\0')
142 e9e8d207 2020-07-25 martijn osmtpd_filter_dataline(ctx, ".");
143 e9e8d207 2020-07-25 martijn return;
144 e9e8d207 2020-07-25 martijn }
145 e9e8d207 2020-07-25 martijn
146 e9e8d207 2020-07-25 martijn if (line[0] == '\0')
147 e9e8d207 2020-07-25 martijn msg->parsing_headers = 0;
148 e9e8d207 2020-07-25 martijn if (line[0] == '.')
149 e9e8d207 2020-07-25 martijn line++;
150 e9e8d207 2020-07-25 martijn if (msg->parsing_headers) {
151 e9e8d207 2020-07-25 martijn if (line[0] != ' ' && line[0] != '\t') {
152 e9e8d207 2020-07-25 martijn if (msg->inheader) {
153 e9e8d207 2020-07-25 martijn msgauthid = admd_authservid(msg);
154 4f29629b 2020-08-30 martijn if (msgauthid == NULL && errno != EINVAL)
155 4f29629b 2020-08-30 martijn return;
156 4f29629b 2020-08-30 martijn if (msgauthid != NULL &&
157 4f29629b 2020-08-30 martijn strcmp(msgauthid, authservid) == 0)
158 e9e8d207 2020-07-25 martijn msg->foundmatch = 1;
159 e9e8d207 2020-07-25 martijn else {
160 e9e8d207 2020-07-25 martijn for (i = 0; i < msg->cachelen; i++)
161 e9e8d207 2020-07-25 martijn osmtpd_filter_dataline(ctx,
162 e9e8d207 2020-07-25 martijn "%s", msg->cache[i]);
163 e9e8d207 2020-07-25 martijn }
164 e9e8d207 2020-07-25 martijn admd_freecache(msg);
165 e9e8d207 2020-07-25 martijn }
166 e9e8d207 2020-07-25 martijn msg->inheader = 0;
167 e9e8d207 2020-07-25 martijn }
168 4f29629b 2020-08-30 martijn if (strncasecmp(line, "Authentication-Results", 22) == 0) {
169 4f29629b 2020-08-30 martijn line += 22;
170 4f29629b 2020-08-30 martijn while (line[0] == ' ' || line[0] == '\t')
171 4f29629b 2020-08-30 martijn line++;
172 4f29629b 2020-08-30 martijn if (line++[0] == ':') {
173 4f29629b 2020-08-30 martijn msg->inheader = 1;
174 4f29629b 2020-08-30 martijn admd_cache(msg, orig);
175 4f29629b 2020-08-30 martijn return;
176 4f29629b 2020-08-30 martijn }
177 4f29629b 2020-08-30 martijn } else if (msg->inheader &&
178 4f29629b 2020-08-30 martijn (line[0] == ' ' || line[0] == '\t')) {
179 e9e8d207 2020-07-25 martijn admd_cache(msg, orig);
180 e9e8d207 2020-07-25 martijn return;
181 e9e8d207 2020-07-25 martijn }
182 e9e8d207 2020-07-25 martijn }
183 e9e8d207 2020-07-25 martijn
184 e9e8d207 2020-07-25 martijn osmtpd_filter_dataline(ctx, "%s", orig);
185 e9e8d207 2020-07-25 martijn return;
186 e9e8d207 2020-07-25 martijn }
187 e9e8d207 2020-07-25 martijn
188 e9e8d207 2020-07-25 martijn void
189 e9e8d207 2020-07-25 martijn admd_commit(struct osmtpd_ctx *ctx)
190 e9e8d207 2020-07-25 martijn {
191 e9e8d207 2020-07-25 martijn struct admd_message *msg = ctx->local_message;
192 e9e8d207 2020-07-25 martijn
193 e9e8d207 2020-07-25 martijn if (msg->err) {
194 e9e8d207 2020-07-25 martijn osmtpd_filter_disconnect(ctx, "Internal server error");
195 e9e8d207 2020-07-25 martijn return;
196 e9e8d207 2020-07-25 martijn }
197 e9e8d207 2020-07-25 martijn if (reject && msg->foundmatch) {
198 e9e8d207 2020-07-25 martijn osmtpd_filter_disconnect(ctx, "Message contains "
199 e9e8d207 2020-07-25 martijn "Authentication-Results header for authserv-id '%s'",
200 e9e8d207 2020-07-25 martijn authservid);
201 e9e8d207 2020-07-25 martijn fprintf(stderr, "%016"PRIx64" Message contains "
202 e9e8d207 2020-07-25 martijn "Authentication-Results header for authserv-id '%s': "
203 e9e8d207 2020-07-25 martijn "rejected\n", ctx->reqid, authservid);
204 e9e8d207 2020-07-25 martijn return;
205 e9e8d207 2020-07-25 martijn }
206 e9e8d207 2020-07-25 martijn
207 e9e8d207 2020-07-25 martijn osmtpd_filter_proceed(ctx);
208 e9e8d207 2020-07-25 martijn if (msg->foundmatch) {
209 e9e8d207 2020-07-25 martijn fprintf(stderr, "%016"PRIx64" Message contains "
210 e9e8d207 2020-07-25 martijn "Authentication-Results header for authserv-id '%s': "
211 e9e8d207 2020-07-25 martijn "filtered\n", ctx->reqid, authservid);
212 e9e8d207 2020-07-25 martijn } else if (verbose)
213 e9e8d207 2020-07-25 martijn fprintf(stderr, "%016"PRIx64" Message contains no "
214 e9e8d207 2020-07-25 martijn "Authentication-Results header for authserv-id '%s'\n",
215 e9e8d207 2020-07-25 martijn ctx->reqid, authservid);
216 e9e8d207 2020-07-25 martijn }
217 e9e8d207 2020-07-25 martijn
218 e9e8d207 2020-07-25 martijn void
219 e9e8d207 2020-07-25 martijn admd_err(struct admd_message *message, const char *msg)
220 e9e8d207 2020-07-25 martijn {
221 e9e8d207 2020-07-25 martijn message->err = 1;
222 e9e8d207 2020-07-25 martijn fprintf(stderr, "%s: %s\n", msg, strerror(errno));
223 e9e8d207 2020-07-25 martijn }
224 e9e8d207 2020-07-25 martijn
225 e9e8d207 2020-07-25 martijn void
226 e9e8d207 2020-07-25 martijn admd_cache(struct admd_message *msg, const char *line)
227 e9e8d207 2020-07-25 martijn {
228 e9e8d207 2020-07-25 martijn char **tcache;
229 e9e8d207 2020-07-25 martijn
230 e9e8d207 2020-07-25 martijn if ((tcache = reallocarray(msg->cache, msg->cachelen + 1,
231 e9e8d207 2020-07-25 martijn sizeof(*(msg->cache)))) == NULL) {
232 e9e8d207 2020-07-25 martijn admd_freecache(msg);
233 e9e8d207 2020-07-25 martijn admd_err(msg, "malloc");
234 e9e8d207 2020-07-25 martijn }
235 e9e8d207 2020-07-25 martijn msg->cache = tcache;
236 e9e8d207 2020-07-25 martijn msg->cache[msg->cachelen] = strdup(line);
237 e9e8d207 2020-07-25 martijn if (msg->cache[msg->cachelen] == NULL) {
238 e9e8d207 2020-07-25 martijn admd_freecache(msg);
239 e9e8d207 2020-07-25 martijn admd_err(msg, "strdup");
240 e9e8d207 2020-07-25 martijn }
241 e9e8d207 2020-07-25 martijn msg->cachelen++;
242 4f29629b 2020-08-30 martijn msg->headerlen += strlen(line[0] == '.' ? line + 1 : line);
243 e9e8d207 2020-07-25 martijn return;
244 e9e8d207 2020-07-25 martijn }
245 e9e8d207 2020-07-25 martijn
246 e9e8d207 2020-07-25 martijn const char *
247 e9e8d207 2020-07-25 martijn admd_authservid(struct admd_message *msg)
248 e9e8d207 2020-07-25 martijn {
249 4f29629b 2020-08-30 martijn char *header0, *header, *line, *end;
250 4f29629b 2020-08-30 martijn size_t headerlen;
251 e9e8d207 2020-07-25 martijn size_t i = 0;
252 e9e8d207 2020-07-25 martijn
253 4f29629b 2020-08-30 martijn headerlen = msg->headerlen + (msg->cachelen * 2) + 1;
254 4f29629b 2020-08-30 martijn header0 = header = malloc(headerlen);
255 4f29629b 2020-08-30 martijn if (header == NULL) {
256 4f29629b 2020-08-30 martijn admd_err(msg, "malloc");
257 4f29629b 2020-08-30 martijn return NULL;
258 4f29629b 2020-08-30 martijn }
259 4f29629b 2020-08-30 martijn header[0] = '\0';
260 4f29629b 2020-08-30 martijn for (i = 0; i < msg->cachelen; i++) {
261 4f29629b 2020-08-30 martijn line = msg->cache[i];
262 4f29629b 2020-08-30 martijn if (line[0] == '.')
263 4f29629b 2020-08-30 martijn line++;
264 4f29629b 2020-08-30 martijn if (strlcat(header, line, headerlen) >= headerlen ||
265 4f29629b 2020-08-30 martijn strlcat(header, "\r\n", headerlen) >= headerlen) {
266 4f29629b 2020-08-30 martijn osmtpd_errx(1, "miscalculated header\n");
267 4f29629b 2020-08-30 martijn exit(1);
268 4f29629b 2020-08-30 martijn }
269 4f29629b 2020-08-30 martijn }
270 e9e8d207 2020-07-25 martijn
271 e9e8d207 2020-07-25 martijn /* Skip key */
272 4f29629b 2020-08-30 martijn header += 22;
273 e9e8d207 2020-07-25 martijn while (header[0] == ' ' || header[0] == '\t')
274 e9e8d207 2020-07-25 martijn header++;
275 4f29629b 2020-08-30 martijn /* : */
276 4f29629b 2020-08-30 martijn header++;
277 4f29629b 2020-08-30 martijn
278 4f29629b 2020-08-30 martijn header = osmtpd_mheader_skip_cfws(header, 1);
279 4f29629b 2020-08-30 martijn
280 4f29629b 2020-08-30 martijn if ((end = osmtpd_mheader_skip_value(header, 0)) == NULL) {
281 4f29629b 2020-08-30 martijn errno = EINVAL;
282 4f29629b 2020-08-30 martijn free(header0);
283 4f29629b 2020-08-30 martijn return NULL;
284 e9e8d207 2020-07-25 martijn }
285 4f29629b 2020-08-30 martijn memmove(header0, header, end - header);
286 4f29629b 2020-08-30 martijn header0[end - header] = '\0';
287 4f29629b 2020-08-30 martijn
288 4f29629b 2020-08-30 martijn return header0;
289 e9e8d207 2020-07-25 martijn }
290 e9e8d207 2020-07-25 martijn
291 e9e8d207 2020-07-25 martijn void
292 e9e8d207 2020-07-25 martijn admd_freecache(struct admd_message *msg)
293 e9e8d207 2020-07-25 martijn {
294 e9e8d207 2020-07-25 martijn while (msg->cachelen > 0) {
295 e9e8d207 2020-07-25 martijn msg->cachelen--;
296 e9e8d207 2020-07-25 martijn free(msg->cache[msg->cachelen]);
297 e9e8d207 2020-07-25 martijn }
298 e9e8d207 2020-07-25 martijn free(msg->cache);
299 e9e8d207 2020-07-25 martijn msg->cache = NULL;
300 e9e8d207 2020-07-25 martijn msg->cachelen = 0;
301 4f29629b 2020-08-30 martijn msg->headerlen = 0;
302 e9e8d207 2020-07-25 martijn }
303 e9e8d207 2020-07-25 martijn
304 e9e8d207 2020-07-25 martijn __dead void
305 e9e8d207 2020-07-25 martijn usage(void)
306 e9e8d207 2020-07-25 martijn {
307 e9e8d207 2020-07-25 martijn fprintf(stderr, "usage: filter-admdscrub [-rv] [-a authserv-id]\n");
308 e9e8d207 2020-07-25 martijn exit(1);
309 e9e8d207 2020-07-25 martijn }