commit 4f29629bac17e2bc8fec298499946fd7518d249e from: Martijn van Duren date: Sun Aug 30 19:24:18 2020 UTC Use new osmtpd_mheader_skip_* API commit - e9e8d2076ab3dc31d76955739dc0afb9f32888c0 commit + 4f29629bac17e2bc8fec298499946fd7518d249e blob - eaa019c9175a22f22b80db9c87f1b9a4854d5407 blob + b1de9767ec30f0862521244246bb8070080eaf9c --- Makefile +++ Makefile @@ -5,7 +5,7 @@ MAN= filter-admdscrub.8 BINDIR= ${LOCALBASE}/libexec/smtpd/ MANDIR= ${LOCALBASE}/man/man -SRCS+= main.c +SRCS+= main.c mheader.c CFLAGS+=-I${LOCALBASE}/include CFLAGS+=-Wall -I${.CURDIR} blob - e419f50f5abc33db2762639e83baa6c79b509bd5 blob + 3bdfa0acc2e91c50c8f38f9078d9efa9a81251d9 --- filter-admdscrub.8 +++ filter-admdscrub.8 @@ -23,7 +23,7 @@ .Sh SYNOPSIS .Nm .Op Fl rv -.Op Fl a Ar authserv-id +.Op Ar authserv-id .Sh DESCRIPTION .Nm checks the mail for blob - 2bd64da3091101695ecc4fdf97d4c70f4546d557 blob + 1f776bcb78fb44859e4b8b7ad9b91cefa9e09c8a --- main.c +++ main.c @@ -23,6 +23,7 @@ #include #include "opensmtpd.h" +#include "mheader.h" struct admd_message { int foundmatch; @@ -31,6 +32,7 @@ struct admd_message { int parsing_headers; char **cache; size_t cachelen; + size_t headerlen; }; void usage(void); @@ -43,7 +45,7 @@ void admd_cache(struct admd_message *, const char *); const char *admd_authservid(struct admd_message *); void admd_freecache(struct admd_message *); -char authservid[256] = ""; +char authservid[256]; int reject = 0; int verbose = 0; @@ -52,13 +54,11 @@ main(int argc, char *argv[]) { int ch; - while ((ch = getopt(argc, argv, "a:rv")) != -1) { + if (pledge("stdio", NULL) == -1) + osmtpd_err(1, "pledge"); + + while ((ch = getopt(argc, argv, "rv")) != -1) { switch (ch) { - case 'a': - if (strlcpy(authservid, optarg, sizeof(authservid)) >= - sizeof(authservid)) - osmtpd_errx(1, "authserv-id is too long"); - break; case 'r': reject = 1; break; @@ -69,11 +69,15 @@ main(int argc, char *argv[]) usage(); } } - - if (pledge("stdio", NULL) == -1) - osmtpd_err(1, "pledge"); - - if (authservid[0] == '\0') { + argc -= optind; + argv += optind; + if (argc > 1) + osmtpd_errx(1, "invalid authservid count"); + if (argc == 1) { + if (strlcpy(authservid, argv[0], sizeof(authservid)) >= + sizeof(authservid)) + osmtpd_errx(1, "authserv-id is too long"); + } else { if (gethostname(authservid, sizeof(authservid)) == -1) osmtpd_err(1, "gethostname"); } @@ -103,6 +107,7 @@ admd_message_new(struct osmtpd_ctx *ctx) msg->parsing_headers = 1; msg->cache = NULL; msg->cachelen = 0; + msg->headerlen = 0; return msg; } @@ -138,7 +143,10 @@ admd_dataline(struct osmtpd_ctx *ctx, const char *orig if (line[0] != ' ' && line[0] != '\t') { if (msg->inheader) { msgauthid = admd_authservid(msg); - if (strcmp(msgauthid, authservid) == 0) + if (msgauthid == NULL && errno != EINVAL) + return; + if (msgauthid != NULL && + strcmp(msgauthid, authservid) == 0) msg->foundmatch = 1; else { for (i = 0; i < msg->cachelen; i++) @@ -149,12 +157,17 @@ admd_dataline(struct osmtpd_ctx *ctx, const char *orig } msg->inheader = 0; } - if (strncmp(line, "Authentication-Results:", 23) == 0) { - msg->inheader = 1; - admd_cache(msg, orig); - return; - } - if (msg->inheader && (line[0] == ' ' || line[0] == '\t')) { + if (strncasecmp(line, "Authentication-Results", 22) == 0) { + line += 22; + while (line[0] == ' ' || line[0] == '\t') + line++; + if (line++[0] == ':') { + msg->inheader = 1; + admd_cache(msg, orig); + return; + } + } else if (msg->inheader && + (line[0] == ' ' || line[0] == '\t')) { admd_cache(msg, orig); return; } @@ -218,114 +231,53 @@ admd_cache(struct admd_message *msg, const char *line) admd_err(msg, "strdup"); } msg->cachelen++; + msg->headerlen += strlen(line[0] == '.' ? line + 1 : line); return; } const char * admd_authservid(struct admd_message *msg) { - static char msgauthid[sizeof(authservid)]; - const char *header; + char *header0, *header, *line, *end; + size_t headerlen; size_t i = 0; - int depth = 0; - msgauthid[0] = '\0'; + headerlen = msg->headerlen + (msg->cachelen * 2) + 1; + header0 = header = malloc(headerlen); + if (header == NULL) { + admd_err(msg, "malloc"); + return NULL; + } + header[0] = '\0'; + for (i = 0; i < msg->cachelen; i++) { + line = msg->cache[i]; + if (line[0] == '.') + line++; + if (strlcat(header, line, headerlen) >= headerlen || + strlcat(header, "\r\n", headerlen) >= headerlen) { + osmtpd_errx(1, "miscalculated header\n"); + exit(1); + } + } - header = msg->cache[0]; - - if (header[0] == '.') - header++; - /* Skip key */ - header += 23; - - /* CFWS */ - /* - * Take the extremely loose approach with both FWS and comment so we - * might match a non fully complient comment and still get the right - * authserv-id - */ -fws: + header += 22; while (header[0] == ' ' || header[0] == '\t') header++; - if (header[0] == '\0') { - if (++i >= msg->cachelen) - return msgauthid; - header = msg->cache[i]; - /* For leniency allow multiple consequtive FWS */ - goto fws; + /* : */ + header++; + + header = osmtpd_mheader_skip_cfws(header, 1); + + if ((end = osmtpd_mheader_skip_value(header, 0)) == NULL) { + errno = EINVAL; + free(header0); + return NULL; } - /* comment */ - if (header[0] == '(') { - depth++; - header++; - } - if (depth > 0) { - while (1) { - /* - * consume a full quoted-pair, which may contain - * parentheses - */ - if (header[0] == '"') { - header++; - while (header[0] != '"') { - if (header[0] == '\\') - header++; - if (header[0] == '\0') { - if (++i >= msg->cachelen) { - return msgauthid; - } - header = msg->cache[i]; - } else - header++; - } - header++; - /* End of comment */ - } else if (header[0] == ')') { - header++; - if (--depth == 0) - goto fws; - } else if (header[0] == '(') { - header++; - depth++; - } else if (header[0] == '\0') { - if (++i >= msg->cachelen) - return msgauthid; - header = msg->cache[i]; - } else - header++; - } - } - /* Quoted-string */ - if (header[0] == '"') { - header++; - for (i = 0; header[0] != '"' && header[0] != '\0' && - i < sizeof(msgauthid); i++, header++) { - if (header[0] == '\\') - header++; - /* Don't do Newline at all */ - if (header[0] == '\0') { - i = 0; - break; - } - msgauthid[i] = header[0]; - } - /* token */ - } else { - /* - * Be more lenient towards token to hit more - * edgecases - */ - for (i = 0; header[i] != ' ' && header[i] != '\t' && - header[i] != ';' && header[i] != '\0' && - i < sizeof(msgauthid); i++) - msgauthid[i] = header[i]; - } - /* If we overflow we simply don't match */ - if (i == sizeof(msgauthid)) - i = 0; - msgauthid[i] = '\0'; - return msgauthid; + memmove(header0, header, end - header); + header0[end - header] = '\0'; + + return header0; } void @@ -338,6 +290,7 @@ admd_freecache(struct admd_message *msg) free(msg->cache); msg->cache = NULL; msg->cachelen = 0; + msg->headerlen = 0; } __dead void blob - /dev/null blob + 8831ab8037ae6aa8869bb840ff8f92295b0ee930 (mode 644) --- /dev/null +++ mheader.c @@ -0,0 +1,747 @@ +/* + * Copyright (c) 2020 Martijn van Duren + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "mheader.h" + +char * +osmtpd_mheader_skip_sp(char *ptr, int optional) +{ + if (ptr[0] == 0x20) + return ptr + 1; + return optional ? ptr : NULL; +} + +char * +osmtpd_mheader_skip_htab(char *ptr, int optional) +{ + if (ptr[0] == 0x9) + return ptr + 1; + return optional ? ptr : NULL; +} + +char * +osmtpd_mheader_skip_wsp(char *ptr, int optional) +{ + char *start = ptr; + + if ((ptr = osmtpd_mheader_skip_sp(start, 0)) != NULL || + (ptr = osmtpd_mheader_skip_htab(start, 0)) != NULL) + return ptr; + return optional ? ptr : NULL; +} + +char * +osmtpd_mheader_skip_crlf(char *ptr, int optional) +{ + if (ptr[0] == 13 && ptr[1] == 10) + return ptr + 2; + return optional ? ptr : NULL; +} + +char * +osmtpd_mheader_skip_vchar(char *ptr, int optional) +{ + if (ptr[0] >= 0x21 && ptr[0] <= 0x7e) + return ptr + 1; + return optional ? ptr : NULL; +} + +char * +osmtpd_mheader_skip_lf(char *ptr, int optional) +{ + if (ptr[0] == 0xa) + return ptr + 1; + return optional ? ptr : NULL; +} + +char * +osmtpd_mheader_skip_cr(char *ptr, int optional) +{ + if (ptr[0] == 0xd) + return ptr + 1; + return optional ? ptr : NULL; +} + +char * +osmtpd_mheader_skip_alpha(char *ptr, int optional) +{ + if ((ptr[0] >= 0x41 && ptr[0] <= 0x5a) || + (ptr[0] >= 0x61 && ptr[0] <= 0x7a)) + return ptr + 1; + return optional ? ptr : NULL; +} + +char * +osmtpd_mheader_skip_digit(char *ptr, int optional) +{ + if (ptr[0] >= 0x30 && ptr[0] <= 0x39) + return ptr + 1; + return optional ? ptr : NULL; +} + +char * +osmtpd_mheader_skip_dquote(char *ptr, int optional) +{ + if (ptr[0] == 0x22) + return ptr + 1; + return optional ? ptr : NULL; +} + +char * +osmtpd_mheader_skip_char(char *ptr, int optional) +{ + if (ptr[0] >= 0x1 && ptr[0] <= 0x7f) + return ptr + 1; + return optional ? ptr : NULL; +} + +char * +osmtpd_mheader_skip_ctl(char *ptr, int optional) +{ + if ((ptr[0] >= 0x0 && ptr[0] <= 0x1f) || ptr[0] == 0x7f) + return ptr + 1; + return optional ? ptr : NULL; +} + +char * +osmtpd_mheader_skip_obs_fws(char *ptr, int optional) +{ + char *start = ptr, *prev; + + if ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) == NULL) + return optional ? start : NULL; + prev = ptr; + while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL) + prev = ptr; + + ptr = prev; + while (1) { + if ((ptr = osmtpd_mheader_skip_crlf(ptr, 0)) == NULL) + return prev; + if ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) == NULL) + return prev; + prev = ptr; + while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL) + prev = ptr; + ptr = prev; + } +} + +char * +osmtpd_mheader_skip_fws(char *ptr, int optional) +{ + char *start = ptr, *prev = ptr; + + while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL) + prev = ptr; + if ((ptr = osmtpd_mheader_skip_crlf(prev, 1)) == prev) + ptr = start; + if ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) == NULL) + return osmtpd_mheader_skip_obs_fws(start, optional); + prev = ptr; + while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL) + prev = ptr; + return prev; +} + +char * +osmtpd_mheader_skip_obs_no_ws_ctl(char *ptr, int optional) +{ + if ((ptr[0] >= 1 && ptr[0] <= 8) || ptr[0] == 11 || ptr[0] == 12 || + (ptr[0] >= 14 && ptr[0] <= 31) || ptr[0] == 127) + return ptr + 1; + return optional ? ptr : NULL; +} + +char * +osmtpd_mheader_skip_obs_ctext(char *ptr, int optional) +{ + return osmtpd_mheader_skip_obs_no_ws_ctl(ptr, optional); +} + +char * +osmtpd_mheader_skip_ctext(char *ptr, int optional) +{ + char *start = ptr; + + if ((ptr[0] >= 33 && ptr[0] <= 39) || (ptr[0] >= 42 && ptr[0] <= 91) || + (ptr[0] >= 93 && ptr[0] <= 126)) + return ptr + 1; + if ((ptr = osmtpd_mheader_skip_obs_ctext(ptr, 0)) != NULL) + return ptr; + return optional ? start : NULL; +} + +char * +osmtpd_mheader_skip_obs_qp(char *ptr, int optional) +{ + char *start = ptr; + + if (ptr[0] == '\\' && ( + (ptr = osmtpd_mheader_skip_obs_no_ws_ctl(start + 1, 0)) != NULL || + (ptr = osmtpd_mheader_skip_lf(start + 1, 0)) != NULL || + (ptr = osmtpd_mheader_skip_cr(start + 1, 0)) != NULL)) + return ptr; + return optional ? start : NULL; +} + +char * +osmtpd_mheader_skip_quoted_pair(char *ptr, int optional) +{ + char *start = ptr; + + if (ptr[0] == '\\' && ( + (ptr = osmtpd_mheader_skip_vchar(start + 1, 0)) != NULL || + (ptr = osmtpd_mheader_skip_wsp(start + 1, 0)) != NULL)) + return ptr; + return osmtpd_mheader_skip_obs_qp(start, optional); +} + +char * +osmtpd_mheader_skip_ccontent(char *ptr, int optional) +{ + char *start = ptr; + + if ((ptr = osmtpd_mheader_skip_ctext(ptr, 0)) != NULL) + return ptr; + if ((ptr = osmtpd_mheader_skip_quoted_pair(start, 0)) != NULL) + return ptr; + if ((ptr = osmtpd_mheader_skip_comment(start, 0)) != NULL) + return ptr; + return optional ? start : NULL; +} + +char * +osmtpd_mheader_skip_comment(char *ptr, int optional) +{ + char *start = ptr; + + if (ptr++[0] != '(') + return optional ? start : NULL; + while (1) { + ptr = osmtpd_mheader_skip_fws(ptr, 1); + if (ptr[0] == ')') + return ptr + 1; + if ((ptr = osmtpd_mheader_skip_ccontent(ptr, 0)) == NULL) + return optional ? start : NULL; + } +} + +char * +osmtpd_mheader_skip_cfws(char *ptr, int optional) +{ + char *start = ptr, *prev; + + while (1) { + ptr = osmtpd_mheader_skip_fws(ptr, 1); + prev = ptr; + if ((ptr = osmtpd_mheader_skip_comment(ptr, 0)) == NULL) { + ptr = prev; + break; + } + } + return ptr == start && !optional ? NULL : ptr; +} + +char * +osmtpd_mheader_skip_atext(char *ptr, int optional) +{ + char *start = ptr; + + if ((ptr = osmtpd_mheader_skip_alpha(start, 0)) != NULL || + (ptr = osmtpd_mheader_skip_digit(start, 0)) != NULL) + return ptr; + ptr = start; + if (ptr[0] == '!' || ptr[0] == '#' || ptr[0] == '$' || ptr[0] == '%' || + ptr[0] == '&' || ptr[0] == '\'' || ptr[0] == '*' || ptr[0] == '+' || + ptr[0] == '-' || ptr[0] == '/' || ptr[0] == '=' || ptr[0] == '?' || + ptr[0] == '^' || ptr[0] == '_' || ptr[0] == '`' || ptr[0] == '{' || + ptr[0] == '|' || ptr[0] == '}' || ptr[0] == '~') + return ptr + 1; + return optional ? start : NULL; +} + +char * +osmtpd_mheader_skip_atom(char *ptr, int optional) +{ + char *start = ptr, *prev; + + ptr = osmtpd_mheader_skip_cfws(ptr, 1); + if ((ptr = osmtpd_mheader_skip_atext(ptr, 0)) == NULL) + return optional ? start : NULL; + do { + prev = ptr; + ptr = osmtpd_mheader_skip_atext(ptr, 1); + } while (prev != ptr); + return osmtpd_mheader_skip_cfws(ptr, 1); +} + +char * +osmtpd_mheader_skip_dot_atom_text(char *ptr, int optional) +{ + char *start = ptr, *prev; + + if ((ptr = osmtpd_mheader_skip_atext(ptr, 0)) == NULL) + return optional ? start : NULL; + do { + prev = ptr; + ptr = osmtpd_mheader_skip_atext(ptr, 1); + } while (ptr != prev); + + while (ptr[0] == '.') { + ptr++; + if ((ptr = osmtpd_mheader_skip_atext(ptr, 0)) == NULL) + return prev; + do { + prev = ptr; + ptr = osmtpd_mheader_skip_atext(ptr, 1); + } while (ptr != prev); + } + return ptr; +} + +char * +osmtpd_mheader_skip_dot_atom(char *ptr, int optional) +{ + char *start = ptr; + + ptr = osmtpd_mheader_skip_cfws(ptr, 1); + if ((ptr = osmtpd_mheader_skip_dot_atom_text(ptr, 0)) == NULL) + return optional ? start : NULL; + return osmtpd_mheader_skip_cfws(ptr, 1); +} + + +char * +osmtpd_mheader_skip_obs_qtext(char *ptr, int optional) +{ + return osmtpd_mheader_skip_obs_no_ws_ctl(ptr, optional); +} + +char * +osmtpd_mheader_skip_qtext(char *ptr, int optional) +{ + char *start = ptr; + + if (ptr[0] == 33 || (ptr[0] >= 35 && ptr[0] <= 91) || + (ptr[0] >= 93 && ptr[0] <= 126)) + return ptr + 1; + if ((ptr = osmtpd_mheader_skip_obs_qtext(ptr, 0)) != NULL) + return ptr; + return optional ? start : NULL; +} + +char * +osmtpd_mheader_skip_qcontent(char *ptr, int optional) +{ + char *start = ptr; + + if ((ptr = osmtpd_mheader_skip_qtext(ptr, 0)) != NULL) + return ptr; + return osmtpd_mheader_skip_quoted_pair(start, optional); +} + +char * +osmtpd_mheader_skip_quoted_string(char *ptr, int optional) +{ + char *start = ptr, *prev; + + ptr = osmtpd_mheader_skip_cfws(ptr, 1); + if ((ptr = osmtpd_mheader_skip_dquote(ptr, 0)) == NULL) + return optional ? start : NULL; + prev = ptr; + while (1) { + ptr = osmtpd_mheader_skip_fws(ptr, 1); + if ((ptr = osmtpd_mheader_skip_qcontent(ptr, 0)) == NULL) + break; + prev = ptr; + } + if ((ptr = osmtpd_mheader_skip_dquote(prev, 0)) == NULL) + return optional ? start : NULL; + return osmtpd_mheader_skip_cfws(ptr, 1); +} + +char * +osmtpd_mheader_skip_word(char *ptr, int optional) +{ + char *start = ptr; + + if ((ptr = osmtpd_mheader_skip_atom(ptr, 0)) != NULL) + return ptr; + return osmtpd_mheader_skip_quoted_string(start, optional); +} + +char * +osmtpd_mheader_skip_obs_phrase(char *ptr, int optional) +{ + char *start = ptr, *prev; + + if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL) + return optional ? start : NULL; + while (1) { + prev = ptr; + if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) != NULL) + continue; + ptr = prev; + if (ptr[0] == '.') + continue; + if ((ptr = osmtpd_mheader_skip_cfws(ptr, 0)) != NULL) + continue; + return prev; + } +} + +char * +osmtpd_mheader_skip_phrase(char *ptr, int optional) +{ + /* obs-phrase is a superset of phrae */ + return osmtpd_mheader_skip_obs_phrase(ptr, optional); +#if 0 + char *start = ptr, *prev; + + if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL) + return optional ? start : NULL; + while (1) { + prev = ptr; + if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL) + return prev; + } +#endif +} + +char * +osmtpd_mheader_skip_obs_local_part(char *ptr, int optional) +{ + char *start = ptr, *prev; + + if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL) + return optional ? start : NULL; + prev = ptr; + while (ptr[0] == '.') { + ptr++; + if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL) + return prev; + prev = ptr; + } + return ptr; +} + +char * +osmtpd_mheader_skip_local_part(char *ptr, int optional) +{ + char *start = ptr; + + if ((ptr = osmtpd_mheader_skip_dot_atom(ptr, 0)) != NULL) + return ptr; + ptr = start; + if ((ptr = osmtpd_mheader_skip_quoted_string(ptr, 0)) != NULL) + return ptr; + return osmtpd_mheader_skip_obs_local_part(start, optional); +} + +char * +osmtpd_mheader_skip_obs_dtext(char *ptr, int optional) +{ + char *start = ptr; + + if ((ptr = osmtpd_mheader_skip_obs_no_ws_ctl(ptr, 0)) != NULL) + return ptr; + return osmtpd_mheader_skip_quoted_pair(start, optional); +} + +char * +osmtpd_mheader_skip_dtext(char *ptr, int optional) +{ + if ((ptr[0] >= 33 && ptr[0] <= 90) || (ptr[0] >= 94 && ptr[0] <= 126)) + return ptr + 1; + return osmtpd_mheader_skip_obs_dtext(ptr, optional); + +} + +char * +osmtpd_mheader_skip_domain_literal(char *ptr, int optional) +{ + char *start = ptr, *prev; + + ptr = osmtpd_mheader_skip_cfws(ptr, 1); + if (ptr++[0] != '[') + return optional ? start : NULL; + while (1) { + ptr = osmtpd_mheader_skip_fws(ptr, 1); + prev = ptr; + if ((ptr = osmtpd_mheader_skip_dtext(ptr, 0)) == NULL) { + ptr = prev; + break; + } + } + if (ptr[0] != ']') + return optional ? start : NULL; + return osmtpd_mheader_skip_cfws(ptr, 1); +} + +char * +osmtpd_mheader_skip_obs_domain(char *ptr, int optional) +{ + char *start = ptr, *prev; + + if ((ptr = osmtpd_mheader_skip_atom(ptr, 0)) == NULL) + return optional ? start : NULL; + prev = ptr; + while (1) { + if (ptr++[0] != '.') + return prev; + if ((ptr = osmtpd_mheader_skip_atom(ptr, 0)) == NULL) + return prev; + prev = ptr; + } +} + +char * +osmtpd_mheader_skip_domain(char *ptr, int optional) +{ + char *start = ptr; + + if ((ptr = osmtpd_mheader_skip_dot_atom(start, 0)) != NULL) + return ptr; + if ((ptr = osmtpd_mheader_skip_domain_literal(start, 0)) != NULL) + return ptr; + return osmtpd_mheader_skip_obs_domain(start, optional); +} + +char * +osmtpd_mheader_skip_display_name(char *ptr, int optional) +{ + return osmtpd_mheader_skip_phrase(ptr, optional); +} + +char * +osmtpd_mheader_skip_obs_domain_list(char *ptr, int optional) +{ + char *start = ptr, *prev; + + while (1) { + if (ptr[0] == ',') { + ptr++; + prev = ptr; + continue; + } else if ((ptr = osmtpd_mheader_skip_cfws(ptr, 0)) != NULL) { + prev = ptr; + continue; + } + break; + } + ptr = prev; + + if (ptr++[0] != '@') + return optional ? start : NULL; + if ((ptr = osmtpd_mheader_skip_domain(ptr, 0)) == NULL) + return optional ? start : NULL; + while (1) { + if (ptr[0] != ',') + break; + ptr++; + ptr = osmtpd_mheader_skip_cfws(ptr, 1); + if (ptr[0] != '@') + continue; + prev = ptr; + if ((ptr = osmtpd_mheader_skip_domain(ptr + 1, 0)) == NULL) { + ptr = prev; + break; + } + } + return ptr; +} + +char * +osmtpd_mheader_skip_obs_route(char *ptr, int optional) +{ + char *start = ptr; + + if ((ptr = osmtpd_mheader_skip_obs_domain_list(ptr, 0)) == NULL) + return optional ? start : NULL; + if (ptr++[0] != ':') + return optional ? start : NULL; + return ptr; +} + +char * +osmtpd_mheader_skip_addr_spec(char *ptr, int optional) +{ + char *start = ptr; + + if ((ptr = osmtpd_mheader_skip_local_part(ptr, 0)) == NULL) + return optional ? start : NULL; + if (ptr++[0] != '@') + return optional ? start : NULL; + if ((ptr = osmtpd_mheader_skip_domain(ptr, 0)) == NULL) + return optional ? start : NULL; + return ptr; +} + +char * +osmtpd_mheader_skip_obs_angle_addr(char *ptr, int optional) +{ + char *start = ptr; + + ptr = osmtpd_mheader_skip_cfws(ptr, 1); + if (ptr++[0] != '<') + return optional ? start : NULL; + if ((ptr = osmtpd_mheader_skip_obs_route(ptr, 0)) == NULL) + return optional ? start : NULL; + if ((ptr = osmtpd_mheader_skip_addr_spec(ptr, 0)) == NULL) + return optional ? start : NULL; + if (ptr++[0] != '>') + return optional ? start : NULL; + return osmtpd_mheader_skip_cfws(ptr, 1); +} + +char * +osmtpd_mheader_skip_angle_addr(char *ptr, int optional) +{ + char *start = ptr; + + ptr = osmtpd_mheader_skip_cfws(ptr, 1); + if (ptr++[0] != '<') + return osmtpd_mheader_skip_obs_angle_addr(start, optional); + if ((ptr = osmtpd_mheader_skip_addr_spec(ptr, 0)) == NULL) + return osmtpd_mheader_skip_obs_angle_addr(start, optional); + if (ptr++[0] != '>') + return osmtpd_mheader_skip_obs_angle_addr(start, optional); + return osmtpd_mheader_skip_cfws(ptr, 1); +} + +char * +osmtpd_mheader_skip_name_addr(char *ptr, int optional) +{ + char *start = ptr; + + ptr = osmtpd_mheader_skip_display_name(ptr, 1); + if ((ptr = osmtpd_mheader_skip_angle_addr(ptr, 0)) == NULL) + return optional ? start : NULL; + return ptr; +} + +/* RFC 2045 */ +char * +osmtpd_mheader_skip_tspecials(char *ptr, int optional) +{ + if (ptr[0] == '(' || ptr[0] == ')' || ptr[0] == '<' || ptr[0] == '>' || + ptr[0] == '@' || ptr[0] == ',' || ptr[0] == ';' || ptr[0] == ':' || + ptr[0] == '\\' || ptr[0] == '\'' || ptr[0] == '/' || + ptr[0] == '[' || ptr[0] == ']' || ptr[0] == '?' || ptr[0] == '=') + return ptr + 1; + return optional ? ptr : NULL; + +} + +char * +osmtpd_mheader_skip_token(char *ptr, int optional) +{ + if (osmtpd_mheader_skip_char(ptr, 0) == NULL) + return optional ? ptr : NULL; + /* Can't find the official definition for SPACE, so use WSP */ + if (osmtpd_mheader_skip_wsp(ptr, 0) != NULL) + return optional ? ptr : NULL; + if (osmtpd_mheader_skip_ctl(ptr, 0) != NULL) + return optional ? ptr : NULL; + if (osmtpd_mheader_skip_tspecials(ptr, 0) != NULL) + return optional ? ptr : NULL; + ptr++; + while (1) { + if (osmtpd_mheader_skip_char(ptr, 0) == NULL) + return ptr; + if (osmtpd_mheader_skip_wsp(ptr, 0) != NULL) + return ptr; + if (osmtpd_mheader_skip_ctl(ptr, 0) != NULL) + return ptr; + if (osmtpd_mheader_skip_tspecials(ptr, 0) != NULL) + return ptr; + ptr++; + } +} + +char * +osmtpd_mheader_skip_value(char *ptr, int optional) +{ + char *start = ptr; + + if ((ptr = osmtpd_mheader_skip_token(ptr, 0)) == NULL) + return osmtpd_mheader_skip_quoted_string(start, optional); + return ptr; +} + +/* Return the domain component of the first mailbox */ +char * +osmtpd_mheader_from_domain(char *ptr) +{ + char *tmp; + + /* from */ + if (strncasecmp(ptr, "from:", 5) == 0) { + ptr += 5; + /* obs-from */ + } else if (strncasecmp(ptr, "from", 4) == 0) { + ptr += 4; + do { + tmp = ptr; + } while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL); + ptr = tmp; + if (ptr++[0] != ':') + return NULL; + } else { + errno = EINVAL; + return NULL; + } + + /* Both from and obs-from use Mailbox-list CRLF */ + /* obs-mbox-list has just a prefix compared to mailbox-list */ + while (1) { + tmp = ptr; + ptr = osmtpd_mheader_skip_cfws(ptr, 1); + if (ptr++[0] != ',') { + ptr = tmp; + break; + } + } + /* We're only interested in the first mailbox */ + if (osmtpd_mheader_skip_name_addr(ptr, 0) != NULL) { + ptr = osmtpd_mheader_skip_display_name(ptr, 1); + ptr = osmtpd_mheader_skip_cfws(ptr, 1); + /* < */ + ptr++; + /* addr-spec */ + ptr = osmtpd_mheader_skip_local_part(ptr, 0); + /* @ */ + ptr++; + tmp = osmtpd_mheader_skip_domain(ptr, 0); + return strndup(ptr, tmp - ptr); + } + if (osmtpd_mheader_skip_addr_spec(ptr, 0) != NULL) { + ptr = osmtpd_mheader_skip_local_part(ptr, 0); + /* @ */ + ptr++; + tmp = osmtpd_mheader_skip_domain(ptr, 0); + return strndup(ptr, tmp - ptr); + } + errno = EINVAL; + return NULL; +} blob - /dev/null blob + afc337958a16e8ba85c5144bf9edeacd225e18b5 (mode 644) --- /dev/null +++ mheader.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2020 Martijn van Duren + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +char *osmtpd_mheader_skip_sp(char *, int); +char *osmtpd_mheader_skip_htab(char *, int); +char *osmtpd_mheader_skip_wsp(char *, int); +char *osmtpd_mheader_skip_crlf(char *, int); +char *osmtpd_mheader_skip_vchar(char *, int); +char *osmtpd_mheader_skip_lf(char *, int); +char *osmtpd_mheader_skip_cr(char *, int); +char *osmtpd_mheader_skip_alpha(char *, int); +char *osmtpd_mheader_skip_digit(char *, int); +char *osmtpd_mheader_skip_dquote(char *, int); +char *osmtpd_mheader_skip_char(char *, int); +char *osmtpd_mheader_skip_ctl(char *, int); +char *osmtpd_mheader_skip_obs_fws(char *, int); +char *osmtpd_mheader_skip_fws(char *, int); +char *osmtpd_mheader_skip_obs_no_ws_ctl(char *, int); +char *osmtpd_mheader_skip_obs_ctext(char *, int); +char *osmtpd_mheader_skip_obs_qp(char *, int); +char *osmtpd_mheader_skip_quoted_pair(char *, int); +char *osmtpd_mheader_skip_ctext(char *, int); +char *osmtpd_mheader_skip_ccontent(char *, int); +char *osmtpd_mheader_skip_comment(char *, int); +char *osmtpd_mheader_skip_cfws(char *, int); +char *osmtpd_mheader_skip_atext(char *, int); +char *osmtpd_mheader_skip_atom(char *, int); +char *osmtpd_mheader_skip_dot_atom_text(char *, int); +char *osmtpd_mheader_skip_dot_atom(char *, int); +char *osmtpd_mheader_skip_obs_qtext(char *, int); +char *osmtpd_mheader_skip_qtext(char *, int); +char *osmtpd_mheader_skip_qcontent(char *, int); +char *osmtpd_mheader_skip_quoted_string(char *, int); +char *osmtpd_mheader_skip_word(char *, int); +char *osmtpd_mheader_skip_obs_phrase(char *, int); +char *osmtpd_mheader_skip_phrase(char *, int); +char *osmtpd_mheader_skip_obs_local_part(char *, int); +char *osmtpd_mheader_skip_local_part(char *, int); +char *osmtpd_mheader_skip_obs_dtext(char *, int); +char *osmtpd_mheader_skip_dtext(char *, int); +char *osmtpd_mheader_skip_domain_literal(char *, int); +char *osmtpd_mheader_skip_obs_domain(char *, int); +char *osmtpd_mheader_skip_domain(char *, int); +char *osmtpd_mheader_skip_display_name(char *, int); +char *osmtpd_mheader_skip_obs_domain_list(char *, int); +char *osmtpd_mheader_skip_obs_route(char *, int); +char *osmtpd_mheader_skip_addr_spec(char *, int); +char *osmtpd_mheader_skip_obs_angle_addr(char *, int); +char *osmtpd_mheader_skip_angle_addr(char *, int); +char *osmtpd_mheader_skip_name_addr(char *, int); +char *osmtpd_mheader_skip_tspecials(char *, int); +char *osmtpd_mheader_skip_token(char *, int); +char *osmtpd_mheader_skip_value(char *, int); + +char *osmtpd_mheader_from_domain(char *);