2 * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 #include "openbsd-compat.h"
26 #include "opensmtpd.h"
40 void admd_conf(const char *, const char *);
41 void *admd_message_new(struct osmtpd_ctx *);
42 void admd_message_free(struct osmtpd_ctx *, void *);
43 void admd_dataline(struct osmtpd_ctx *, const char *);
44 void admd_commit(struct osmtpd_ctx *);
45 void admd_err(struct admd_message *, const char *);
46 void admd_cache(struct admd_message *, const char *);
47 const char *admd_authservid(struct admd_message *);
48 void admd_freecache(struct admd_message *);
55 main(int argc, char *argv[])
59 if (pledge("stdio", NULL) == -1)
60 osmtpd_err(1, "pledge");
62 while ((ch = getopt(argc, argv, "rv")) != -1) {
77 osmtpd_errx(1, "invalid authservid count");
81 osmtpd_local_message(admd_message_new, admd_message_free);
82 osmtpd_register_filter_dataline(admd_dataline);
83 osmtpd_register_filter_commit(admd_commit);
84 osmtpd_register_conf(admd_conf);
91 admd_conf(const char *key, const char *value)
94 if (authservid == NULL)
95 osmtpd_errx(1, "Didn't receive admd config option");
98 if (strcmp(key, "admd") == 0 && authservid == NULL) {
99 if ((authservid = strdup(value)) == NULL)
100 osmtpd_err(1, "malloc");
105 admd_message_new(struct osmtpd_ctx *ctx)
107 struct admd_message *msg;
109 if ((msg = malloc(sizeof(*msg))) == NULL)
110 osmtpd_err(1, "malloc");
115 msg->parsing_headers = 1;
124 admd_message_free(struct osmtpd_ctx *ctx, void *data)
126 struct admd_message *msg = data;
133 admd_dataline(struct osmtpd_ctx *ctx, const char *orig)
135 struct admd_message *msg = ctx->local_message;
136 const char *line = orig;
137 const char *msgauthid;
141 if (line[0] == '.' && line[1] =='\0')
142 osmtpd_filter_dataline(ctx, ".");
147 msg->parsing_headers = 0;
150 if (msg->parsing_headers) {
151 if (line[0] != ' ' && line[0] != '\t') {
153 msgauthid = admd_authservid(msg);
154 if (msgauthid == NULL && errno != EINVAL)
156 if (msgauthid != NULL &&
157 strcmp(msgauthid, authservid) == 0)
160 for (i = 0; i < msg->cachelen; i++)
161 osmtpd_filter_dataline(ctx,
162 "%s", msg->cache[i]);
168 if (strncasecmp(line, "Authentication-Results", 22) == 0) {
170 while (line[0] == ' ' || line[0] == '\t')
172 if (line++[0] == ':') {
174 admd_cache(msg, orig);
177 } else if (msg->inheader &&
178 (line[0] == ' ' || line[0] == '\t')) {
179 admd_cache(msg, orig);
184 osmtpd_filter_dataline(ctx, "%s", orig);
189 admd_commit(struct osmtpd_ctx *ctx)
191 struct admd_message *msg = ctx->local_message;
194 osmtpd_filter_disconnect(ctx, "Internal server error");
197 if (reject && msg->foundmatch) {
198 osmtpd_filter_disconnect(ctx, "Message contains "
199 "Authentication-Results header for authserv-id '%s'",
201 fprintf(stderr, "%016"PRIx64" Message contains "
202 "Authentication-Results header for authserv-id '%s': "
203 "rejected\n", ctx->reqid, authservid);
207 osmtpd_filter_proceed(ctx);
208 if (msg->foundmatch) {
209 fprintf(stderr, "%016"PRIx64" Message contains "
210 "Authentication-Results header for authserv-id '%s': "
211 "filtered\n", ctx->reqid, authservid);
213 fprintf(stderr, "%016"PRIx64" Message contains no "
214 "Authentication-Results header for authserv-id '%s'\n",
215 ctx->reqid, authservid);
219 admd_err(struct admd_message *message, const char *msg)
222 fprintf(stderr, "%s: %s\n", msg, strerror(errno));
226 admd_cache(struct admd_message *msg, const char *line)
230 if ((tcache = reallocarray(msg->cache, msg->cachelen + 1,
231 sizeof(*(msg->cache)))) == NULL) {
233 admd_err(msg, "malloc");
236 msg->cache[msg->cachelen] = strdup(line);
237 if (msg->cache[msg->cachelen] == NULL) {
239 admd_err(msg, "strdup");
242 msg->headerlen += strlen(line[0] == '.' ? line + 1 : line);
247 admd_authservid(struct admd_message *msg)
249 char *header0, *header, *line, *end;
253 headerlen = msg->headerlen + (msg->cachelen * 2) + 1;
254 header0 = header = malloc(headerlen);
255 if (header == NULL) {
256 admd_err(msg, "malloc");
260 for (i = 0; i < msg->cachelen; i++) {
261 line = msg->cache[i];
264 if (strlcat(header, line, headerlen) >= headerlen ||
265 strlcat(header, "\r\n", headerlen) >= headerlen) {
266 osmtpd_errx(1, "miscalculated header\n");
273 while (header[0] == ' ' || header[0] == '\t')
278 header = osmtpd_mheader_skip_cfws(header, 1);
280 if ((end = osmtpd_mheader_skip_value(header, 0)) == NULL) {
285 memmove(header0, header, end - header);
286 header0[end - header] = '\0';
292 admd_freecache(struct admd_message *msg)
294 while (msg->cachelen > 0) {
296 free(msg->cache[msg->cachelen]);
307 fprintf(stderr, "usage: filter-admdscrub [-rv] [-a authserv-id]\n");