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 "opensmtpd.h"
37 void *admd_message_new(struct osmtpd_ctx *);
38 void admd_message_free(struct osmtpd_ctx *, void *);
39 void admd_dataline(struct osmtpd_ctx *, const char *);
40 void admd_commit(struct osmtpd_ctx *);
41 void admd_err(struct admd_message *, const char *);
42 void admd_cache(struct admd_message *, const char *);
43 const char *admd_authservid(struct admd_message *);
44 void admd_freecache(struct admd_message *);
46 char authservid[256] = "";
51 main(int argc, char *argv[])
55 while ((ch = getopt(argc, argv, "a:rv")) != -1) {
58 if (strlcpy(authservid, optarg, sizeof(authservid)) >=
60 osmtpd_errx(1, "authserv-id is too long");
73 if (pledge("stdio", NULL) == -1)
74 osmtpd_err(1, "pledge");
76 if (authservid[0] == '\0') {
77 if (gethostname(authservid, sizeof(authservid)) == -1)
78 osmtpd_err(1, "gethostname");
80 if (strchr(authservid, '\r') != NULL ||
81 strchr(authservid, '\n') != NULL)
82 osmtpd_errx(1, "ubsupported character in authserv-id");
84 osmtpd_local_message(admd_message_new, admd_message_free);
85 osmtpd_register_filter_dataline(admd_dataline);
86 osmtpd_register_filter_commit(admd_commit);
93 admd_message_new(struct osmtpd_ctx *ctx)
95 struct admd_message *msg;
97 if ((msg = malloc(sizeof(*msg))) == NULL)
98 osmtpd_err(1, "malloc");
103 msg->parsing_headers = 1;
111 admd_message_free(struct osmtpd_ctx *ctx, void *data)
113 struct admd_message *msg = data;
120 admd_dataline(struct osmtpd_ctx *ctx, const char *orig)
122 struct admd_message *msg = ctx->local_message;
123 const char *line = orig;
124 const char *msgauthid;
128 if (line[0] == '.' && line[1] =='\0')
129 osmtpd_filter_dataline(ctx, ".");
134 msg->parsing_headers = 0;
137 if (msg->parsing_headers) {
138 if (line[0] != ' ' && line[0] != '\t') {
140 msgauthid = admd_authservid(msg);
141 if (strcmp(msgauthid, authservid) == 0)
144 for (i = 0; i < msg->cachelen; i++)
145 osmtpd_filter_dataline(ctx,
146 "%s", msg->cache[i]);
152 if (strncmp(line, "Authentication-Results:", 23) == 0) {
154 admd_cache(msg, orig);
157 if (msg->inheader && (line[0] == ' ' || line[0] == '\t')) {
158 admd_cache(msg, orig);
163 osmtpd_filter_dataline(ctx, "%s", orig);
168 admd_commit(struct osmtpd_ctx *ctx)
170 struct admd_message *msg = ctx->local_message;
173 osmtpd_filter_disconnect(ctx, "Internal server error");
176 if (reject && msg->foundmatch) {
177 osmtpd_filter_disconnect(ctx, "Message contains "
178 "Authentication-Results header for authserv-id '%s'",
180 fprintf(stderr, "%016"PRIx64" Message contains "
181 "Authentication-Results header for authserv-id '%s': "
182 "rejected\n", ctx->reqid, authservid);
186 osmtpd_filter_proceed(ctx);
187 if (msg->foundmatch) {
188 fprintf(stderr, "%016"PRIx64" Message contains "
189 "Authentication-Results header for authserv-id '%s': "
190 "filtered\n", ctx->reqid, authservid);
192 fprintf(stderr, "%016"PRIx64" Message contains no "
193 "Authentication-Results header for authserv-id '%s'\n",
194 ctx->reqid, authservid);
198 admd_err(struct admd_message *message, const char *msg)
201 fprintf(stderr, "%s: %s\n", msg, strerror(errno));
205 admd_cache(struct admd_message *msg, const char *line)
209 if ((tcache = reallocarray(msg->cache, msg->cachelen + 1,
210 sizeof(*(msg->cache)))) == NULL) {
212 admd_err(msg, "malloc");
215 msg->cache[msg->cachelen] = strdup(line);
216 if (msg->cache[msg->cachelen] == NULL) {
218 admd_err(msg, "strdup");
225 admd_authservid(struct admd_message *msg)
227 static char msgauthid[sizeof(authservid)];
234 header = msg->cache[0];
236 if (header[0] == '.')
244 * Take the extremely loose approach with both FWS and comment so we
245 * might match a non fully complient comment and still get the right
249 while (header[0] == ' ' || header[0] == '\t')
251 if (header[0] == '\0') {
252 if (++i >= msg->cachelen)
254 header = msg->cache[i];
255 /* For leniency allow multiple consequtive FWS */
259 if (header[0] == '(') {
266 * consume a full quoted-pair, which may contain
269 if (header[0] == '"') {
271 while (header[0] != '"') {
272 if (header[0] == '\\')
274 if (header[0] == '\0') {
275 if (++i >= msg->cachelen) {
278 header = msg->cache[i];
284 } else if (header[0] == ')') {
288 } else if (header[0] == '(') {
291 } else if (header[0] == '\0') {
292 if (++i >= msg->cachelen)
294 header = msg->cache[i];
300 if (header[0] == '"') {
302 for (i = 0; header[0] != '"' && header[0] != '\0' &&
303 i < sizeof(msgauthid); i++, header++) {
304 if (header[0] == '\\')
306 /* Don't do Newline at all */
307 if (header[0] == '\0') {
311 msgauthid[i] = header[0];
316 * Be more lenient towards token to hit more
319 for (i = 0; header[i] != ' ' && header[i] != '\t' &&
320 header[i] != ';' && header[i] != '\0' &&
321 i < sizeof(msgauthid); i++)
322 msgauthid[i] = header[i];
324 /* If we overflow we simply don't match */
325 if (i == sizeof(msgauthid))
332 admd_freecache(struct admd_message *msg)
334 while (msg->cachelen > 0) {
336 free(msg->cache[msg->cachelen]);
346 fprintf(stderr, "usage: filter-admdscrub [-rv] [-a authserv-id]\n");