Commit Diff


commit - 7258013668ef07ebc2c942108e7b4851c327adaa
commit + 6609fda3c91445d79dbb58f5a57a620176caff76
blob - 8f324e536ea02ba338e434de491b4f778535a399
blob + 3ea6e58668009bcadf70739c6903f841027c7cda
--- main.c
+++ main.c
@@ -116,43 +116,39 @@ struct message {
 	int has_body;
 	struct header *header;
 	size_t nheaders;
-	int err;
 	int readdone;
 };
 
 void usage(void);
-void dkim_err(struct message *, char *);
-void dkim_errx(struct message *, char *);
 void dkim_conf(const char *, const char *);
-void dkim_dataline(struct osmtpd_ctx *, const char *);
-void dkim_commit(struct osmtpd_ctx *);
+int dkim_dataline(struct osmtpd_ctx *, const char *);
 void *dkim_message_new(struct osmtpd_ctx *);
 void dkim_message_free(struct osmtpd_ctx *, void *);
-void dkim_header_add(struct osmtpd_ctx *, const char *);
-void dkim_signature_parse(struct header *);
-void dkim_signature_parse_v(struct signature *, const char *, const char *);
-void dkim_signature_parse_a(struct signature *, const char *, const char *);
-void dkim_signature_parse_b(struct signature *, const char *, const char *);
-void dkim_signature_parse_bh(struct signature *, const char *, const char *);
-void dkim_signature_parse_c(struct signature *, const char *, const char *);
-void dkim_signature_parse_d(struct signature *, const char *, const char *);
-void dkim_signature_parse_h(struct signature *, const char *, const char *);
-void dkim_signature_parse_i(struct signature *, const char *, const char *);
-void dkim_signature_parse_l(struct signature *, const char *, const char *);
-void dkim_signature_parse_q(struct signature *, const char *, const char *);
-void dkim_signature_parse_s(struct signature *, const char *, const char *);
-void dkim_signature_parse_t(struct signature *, const char *, const char *);
-void dkim_signature_parse_x(struct signature *, const char *, const char *);
-void dkim_signature_parse_z(struct signature *, const char *, const char *);
-void dkim_signature_verify(struct signature *);
-void dkim_signature_header(EVP_MD_CTX *, struct signature *, struct header *);
+int dkim_header_add(struct osmtpd_ctx *, const char *);
+int dkim_signature_parse(struct header *);
+int dkim_signature_parse_v(struct signature *, const char *, const char *);
+int dkim_signature_parse_a(struct signature *, const char *, const char *);
+int dkim_signature_parse_b(struct signature *, const char *, const char *);
+int dkim_signature_parse_bh(struct signature *, const char *, const char *);
+int dkim_signature_parse_c(struct signature *, const char *, const char *);
+int dkim_signature_parse_d(struct signature *, const char *, const char *);
+int dkim_signature_parse_h(struct signature *, const char *, const char *);
+int dkim_signature_parse_i(struct signature *, const char *, const char *);
+int dkim_signature_parse_l(struct signature *, const char *, const char *);
+int dkim_signature_parse_q(struct signature *, const char *, const char *);
+int dkim_signature_parse_s(struct signature *, const char *, const char *);
+int dkim_signature_parse_t(struct signature *, const char *, const char *);
+int dkim_signature_parse_x(struct signature *, const char *, const char *);
+int dkim_signature_parse_z(struct signature *, const char *, const char *);
+int dkim_signature_verify(struct signature *);
+int dkim_signature_header(EVP_MD_CTX *, struct signature *, struct header *);
 void dkim_signature_state(struct signature *, enum state, const char *);
 const char *dkim_state2str(enum state);
-void dkim_header_cat(struct osmtpd_ctx *, const char *);
-void dkim_body_parse(struct message *, const char *);
-void dkim_body_verify(struct signature *);
+int dkim_header_cat(struct osmtpd_ctx *, const char *);
+int dkim_body_parse(struct message *, const char *);
+int dkim_body_verify(struct signature *);
 void dkim_rr_resolve(struct asr_result *, void *);
-void dkim_message_verify(struct message *);
+int dkim_message_verify(struct message *);
 ssize_t dkim_ar_cat(char **ar, size_t *n, size_t aroff, const char *fmt, ...)
     __attribute__((__format__ (printf, 4, 5)));
 void dkim_ar_print(struct osmtpd_ctx *, const char *);
@@ -177,7 +173,6 @@ main(int argc, char *argv[])
 
 	osmtpd_register_conf(dkim_conf);
 	osmtpd_register_filter_dataline(dkim_dataline);
-	osmtpd_register_filter_commit(dkim_commit);
 	osmtpd_local_message(dkim_message_new, dkim_message_free);
 	osmtpd_run();
 
@@ -203,23 +198,15 @@ dkim_conf(const char *key, const char *value)
 	}
 }
 
-void
+int
 dkim_dataline(struct osmtpd_ctx *ctx, const char *line)
 {
 	struct message *msg = ctx->local_message;
 	size_t i;
 
-	if (msg->err) {
-		if (line[0] == '.' && line[1] =='\0') {
-			msg->readdone = 1;
-			osmtpd_filter_dataline(ctx, ".");
-		}
-		return;
-	}
-
 	if (fprintf(msg->origf, "%s\n", line) < 0) {
-		dkim_err(msg, "Couldn't write to tempfile");
-		return;
+		osmtpd_warnx(ctx, "Couldn't write to tempfile");
+		return -1;
 	}
 	if (line[0] == '.') {
 		line++;
@@ -228,39 +215,30 @@ dkim_dataline(struct osmtpd_ctx *ctx, const char *line
 			for (i = 0; i < msg->nheaders; i++) {
 				if (msg->header[i].sig == NULL)
 					continue;
-				dkim_body_verify(msg->header[i].sig);
+				if (dkim_body_verify(msg->header[i].sig) == -1)
+					return -1;
 			}
-			dkim_message_verify(msg);
-			return;
+			return dkim_message_verify(msg);
 		}
 	}
 	if (msg->parsing_headers) {
-		dkim_header_add(ctx, line);
+		if (dkim_header_add(ctx, line) == -1)
+			return -1;
 		if (line[0] == '\0') {
 			msg->parsing_headers = 0;
 			for (i = 0; i < msg->nheaders; i++) {
 				if (msg->header[i].sig == NULL)
 					continue;
 				if (msg->header[i].sig->query == NULL)
-					dkim_signature_verify(
-					    msg->header[i].sig);
+					if (dkim_signature_verify(
+					    msg->header[i].sig) == -1)
+						return -1;
 			}
 		}
-		return;
-	} else {
-		dkim_body_parse(msg, line);
-	}
-}
+	} else
+		return dkim_body_parse(msg, line);
 
-void
-dkim_commit(struct osmtpd_ctx *ctx)
-{
-	struct message *msg = ctx->local_message;
-
-	if (msg->err)
-		osmtpd_filter_disconnect(ctx, "Internal server error");
-	else
-		osmtpd_filter_proceed(ctx);
+	return 0;
 }
 
 void *
@@ -272,7 +250,8 @@ dkim_message_new(struct osmtpd_ctx *ctx)
 		osmtpd_err(1, NULL);
 
 	if ((msg->origf = tmpfile()) == NULL) {
-		dkim_err(msg, "Can't open tempfile");
+		osmtpd_warnx(ctx, "Can't open tempfile");
+		free(msg);
 		return NULL;
 	}
 	msg->ctx = ctx;
@@ -281,7 +260,6 @@ dkim_message_new(struct osmtpd_ctx *ctx)
 	msg->has_body = 0;
 	msg->header = NULL;
 	msg->nheaders = 0;
-	msg->err = 0;
 	msg->readdone = 0;
 
 	return msg;
@@ -311,7 +289,7 @@ dkim_message_free(struct osmtpd_ctx *ctx, void *data)
 	free(msg);
 }
 
-void
+int
 dkim_header_add(struct osmtpd_ctx *ctx, const char *line)
 {
 	struct message *msg = ctx->local_message;
@@ -331,21 +309,22 @@ dkim_header_add(struct osmtpd_ctx *ctx, const char *li
 			if (end != NULL &&
 			    strncasecmp(
 			    start, "DKIM-Signature", end - start) == 0 &&
-			    verify[0] == ':')
-				dkim_signature_parse(
-				    &msg->header[msg->nheaders - 1]);
+			    verify[0] == ':') {
+				if (dkim_signature_parse(
+				    &msg->header[msg->nheaders - 1]) == -1)
+					return -1;
+				}
 			if (line[0] == '\0')
-				return;
+				return 0;
 		} else {
-			dkim_header_cat(ctx, line);
-			return;
+			return dkim_header_cat(ctx, line);
 		}
 	}
 	if (msg->nheaders % 10 == 0) {
 		if ((headers = recallocarray(msg->header, msg->nheaders,
 		    msg->nheaders + 10, sizeof(*msg->header))) == NULL) {
-			dkim_err(msg, "malloc");
-			return;
+			osmtpd_warn(ctx, "malloc");
+			return -1;
 		}
 		msg->header = headers;
 		for (i = 0; i < msg->nheaders; i++) {
@@ -356,10 +335,10 @@ dkim_header_add(struct osmtpd_ctx *ctx, const char *li
 	}
 	msg->header[msg->nheaders].msg = msg;
 	msg->nheaders++;
-	dkim_header_cat(ctx, line);
+	return dkim_header_cat(ctx, line);
 }
 
-void
+int
 dkim_header_cat(struct osmtpd_ctx *ctx, const char *line)
 {
 	struct message *msg = ctx->local_message;
@@ -371,17 +350,18 @@ dkim_header_cat(struct osmtpd_ctx *ctx, const char *li
 	if (needed > (header->buflen / 1024) + 1) {
 		buf = reallocarray(header->buf, (needed / 1024) + 1, 1024);
 		if (buf == NULL) {
-			dkim_err(msg, "malloc");
-			return;
+			osmtpd_warn(ctx, "malloc");
+			return -1;
 		}
 		header->buf = buf;
 	}
 	header->buflen += snprintf(header->buf + header->buflen,
 	    (((needed / 1024) + 1) * 1024) - header->buflen, "%s%s",
 	    header->buflen == 0 ? "" : "\r\n", line);
+	return 0;
 }
 
-void
+int
 dkim_signature_parse(struct header *header)
 {
 	struct signature *sig;
@@ -390,14 +370,15 @@ dkim_signature_parse(struct header *header)
 	char tagname[3];
 	char subdomain[HOST_NAME_MAX + 1];
 	size_t ilen, dlen;
+	int r;
 
 	/* Format checked by dkim_header_add */
 	buf = osmtpd_ltok_skip_field_name(header->buf, 0);
 	buf = osmtpd_ltok_skip_wsp(buf, 1) + 1;
 
 	if ((header->sig = calloc(1, sizeof(*header->sig))) == NULL) {
-		dkim_err(header->msg, "malloc");
-		return;
+		osmtpd_warn(header->msg->ctx, "malloc");
+		return -1;
 	}
 	sig = header->sig;
 	sig->header = header;
@@ -408,7 +389,7 @@ dkim_signature_parse(struct header *header)
 	end = osmtpd_ltok_skip_tag_list(buf, 0);
 	if (end == NULL || end[0] != '\0') {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid tag-list");
-		return;
+		return 0;
 	}
 
 	while (buf[0] != '\0') {
@@ -419,39 +400,43 @@ dkim_signature_parse(struct header *header)
 		if ((size_t)(end - buf) >= sizeof(tagname))
 			tagname[0] = '\0';
 		else
-			strlcpy(tagname, buf, (end - buf) + 1);
+			strncpy(tagname, buf, (end - buf));
 		buf = osmtpd_ltok_skip_fws(end, 1);
 		/* '=' */
 		buf = osmtpd_ltok_skip_fws(buf + 1, 1);
 		end = osmtpd_ltok_skip_tag_value(buf, 1);
 		if (strcmp(tagname, "v") == 0)
-			dkim_signature_parse_v(sig, buf, end);
+			r = dkim_signature_parse_v(sig, buf, end);
 		else if (strcmp(tagname, "a") == 0)
-			dkim_signature_parse_a(sig, buf, end);
+			r = dkim_signature_parse_a(sig, buf, end);
 		else if (strcmp(tagname, "b") == 0)
-			dkim_signature_parse_b(sig, buf, end);
+			r = dkim_signature_parse_b(sig, buf, end);
 		else if (strcmp(tagname, "bh") == 0)
-			dkim_signature_parse_bh(sig, buf, end);
+			r = dkim_signature_parse_bh(sig, buf, end);
 		else if (strcmp(tagname, "c") == 0)
-			dkim_signature_parse_c(sig, buf, end);
+			r = dkim_signature_parse_c(sig, buf, end);
 		else if (strcmp(tagname, "d") == 0)
-			dkim_signature_parse_d(sig, buf, end);
+			r = dkim_signature_parse_d(sig, buf, end);
 		else if (strcmp(tagname, "h") == 0)
-			dkim_signature_parse_h(sig, buf, end);
+			r = dkim_signature_parse_h(sig, buf, end);
 		else if (strcmp(tagname, "i") == 0)
-			dkim_signature_parse_i(sig, buf, end);
+			r = dkim_signature_parse_i(sig, buf, end);
 		else if (strcmp(tagname, "l") == 0)
-			dkim_signature_parse_l(sig, buf, end);
+			r = dkim_signature_parse_l(sig, buf, end);
 		else if (strcmp(tagname, "q") == 0)
-			dkim_signature_parse_q(sig, buf, end);
+			r = dkim_signature_parse_q(sig, buf, end);
 		else if (strcmp(tagname, "s") == 0)
-			dkim_signature_parse_s(sig, buf, end);
+			r = dkim_signature_parse_s(sig, buf, end);
 		else if (strcmp(tagname, "t") == 0)
-			dkim_signature_parse_t(sig, buf, end);
+			r = dkim_signature_parse_t(sig, buf, end);
 		else if (strcmp(tagname, "x") == 0)
-			dkim_signature_parse_x(sig, buf, end);
+			r = dkim_signature_parse_x(sig, buf, end);
 		else if (strcmp(tagname, "z") == 0)
-			dkim_signature_parse_z(sig, buf, end);
+			r = dkim_signature_parse_z(sig, buf, end);
+		else
+			r = 0;
+		if (r == -1)
+			return -1;
 
 		buf = osmtpd_ltok_skip_fws(end, 1);
 		if (buf[0] == ';')
@@ -459,11 +444,11 @@ dkim_signature_parse(struct header *header)
 		else if (buf[0] != '\0') {
 			dkim_signature_state(sig, DKIM_PERMERROR,
 			    "Invalid tag-list");
-			return;
+			return 0;
 		}
 	}
 	if (sig->state != DKIM_UNKNOWN)
-		return;
+		return 0;
 
 	if (sig->v != 1)
 		dkim_signature_state(sig, DKIM_PERMERROR, "Missing v tag");
@@ -480,7 +465,7 @@ dkim_signature_parse(struct header *header)
 	else if (sig->s[0] == '\0')
 		dkim_signature_state(sig, DKIM_PERMERROR, "Missing s tag");
 	if (sig->state != DKIM_UNKNOWN)
-		return;
+		return 0;
 
 	if (sig->i != NULL) {
 		i = osmtpd_ltok_skip_local_part(sig->i, 1) + 1;
@@ -489,66 +474,70 @@ dkim_signature_parse(struct header *header)
 		if (ilen < dlen) {
 			dkim_signature_state(sig, DKIM_PERMERROR,
 			    "i tag not subdomain of d");
-			return;
+			return 0;
 		}
 		i += ilen - dlen;
 		if ((i[-1] != '.' && i[-1] != '@') ||
 		    strncasecmp(i, sig->d, dlen) != 0) {
 			dkim_signature_state(sig, DKIM_PERMERROR,
 			    "i tag not subdomain of d");
-			return;
+			return 0;
 		}
 	}
 	if (sig->t != -1 && sig->x != -1 && sig->t > sig->x) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "t tag after x tag");
-		return;
+		return 0;
 	}
 
 	if ((size_t)snprintf(subdomain, sizeof(subdomain), "%s._domainkey.%s",
 	    sig->s, sig->d) >= sizeof(subdomain)) {
 		dkim_signature_state(sig, DKIM_PERMERROR,
 		    "dns/txt query too long");
-		return;
+		return 0;
 	}
 
 	if ((query = res_query_async(subdomain, C_IN, T_TXT, NULL)) == NULL) {
-		dkim_err(header->msg, "res_query_async");
-		return;
+		osmtpd_warnx(header->msg->ctx, "res_query_async");
+		return -1;
 	}
 	if ((sig->query = event_asr_run(query, dkim_rr_resolve, sig)) == NULL) {
-		dkim_err(header->msg, "event_asr_run");
+		osmtpd_warnx(header->msg->ctx, "event_asr_run");
 		asr_abort(query);
-		return;
+		return -1;
 	}
+
+	return 0;
 }
 
-void
+int
 dkim_signature_parse_v(struct signature *sig, const char *start, const char *end)
 {
 	if (sig->v != 0) {	/* Duplicate tag */
 		dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate v tag");
-		return;
+		return 0;
 	}
 	/* Unsupported version */
 	if (start[0] != '1' || start + 1 != end)
 		dkim_signature_state(sig, DKIM_NEUTRAL, "Unsupported v tag");
 	else
 		sig->v = 1;
+
+	return -1;
 }
 
-void
+int
 dkim_signature_parse_a(struct signature *sig, const char *start, const char *end)
 {
 	char ah[sizeof("sha256")];
 
 	if (sig->ah != NULL) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate a tag");
-		return;
+		return 0;
 	}
 
 	if (osmtpd_ltok_skip_sig_a_tag_alg(start, 0) != end) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid a tag");
-		return;
+		return 0;
 	}
 	sig->a = start;
 	sig->asz = (size_t)(end - start);
@@ -564,59 +553,62 @@ dkim_signature_parse_a(struct signature *sig, const ch
 #endif
 	} else {
 		dkim_signature_state(sig, DKIM_NEUTRAL, "Unsuppored a tag k");
-		return;
+		return 0;
 	}
 	if ((size_t)(end - start) >= sizeof(ah)) {
 		dkim_signature_state(sig, DKIM_NEUTRAL, "Unsuppored a tag h");
-		return;
+		return 0;
 	}
 	strlcpy(ah, start, sizeof(ah));
 	ah[end - start] = '\0';
 	if ((sig->ah = EVP_get_digestbyname(ah)) == NULL) {
 		dkim_signature_state(sig, DKIM_NEUTRAL, "Unsuppored a tag h");
-		return;
+		return 0;
 	}
 	if ((sig->bhctx = EVP_MD_CTX_new()) == NULL) {
-		dkim_err(sig->header->msg, "EVP_MD_CTX_new");
-		return;
+		osmtpd_warnx(sig->header->msg->ctx, "EVP_MD_CTX_new");
+		return -1;
 	}
 	if (EVP_DigestInit_ex(sig->bhctx, sig->ah, NULL) <= 0) {
-		dkim_err(sig->header->msg, "EVP_DigestInit_ex");
-		return;
+		osmtpd_warnx(sig->header->msg->ctx, "EVP_DigestInit_ex");
+		return -1;
 	}
+
+	return 0;
 }
 
-void
+int
 dkim_signature_parse_b(struct signature *sig, const char *start, const char *end)
 {
 	int decodesz;
 
 	if (sig->b != NULL) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate b tag");
-		return;
+		return 0;
 	}
 	sig->bheader = start;
 	if ((sig->b = malloc((((end - start) / 4) + 1) * 3)) == NULL) {
-		dkim_err(sig->header->msg, "malloc");
-		return;
+		osmtpd_warnx(sig->header->msg->ctx, "malloc");
+		return -1;
 	}
 	/* EVP_DecodeBlock doesn't handle internal whitespace */
 	EVP_DecodeInit(ectx);
 	if (EVP_DecodeUpdate(ectx, sig->b, &decodesz, start,
 	    (int)(end - start)) == -1) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid b tag");
-		return;
+		return 0;
 	}
 	sig->bsz = decodesz;
-	if (EVP_DecodeFinal(ectx, sig->b + sig->bsz,
-	    &decodesz) == -1) {
+	if (EVP_DecodeFinal(ectx, sig->b + sig->bsz, &decodesz) == -1) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid b tag");
-		return;
+		return 0;
 	}
 	sig->bsz += decodesz;
+
+	return 0;
 }
 
-void
+int
 dkim_signature_parse_bh(struct signature *sig, const char *start, const char *end)
 {
 	const char *b64;
@@ -625,7 +617,7 @@ dkim_signature_parse_bh(struct signature *sig, const c
 
 	if (sig->bhsz != 0) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate bh tag");
-		return;
+		return 0;
 	}
 	/*
 	 * EVP_Decode* expects sig->bh to be large enough,
@@ -651,7 +643,7 @@ dkim_signature_parse_bh(struct signature *sig, const c
 	/* Invalid tag value */
 	if (b64 != end || n % 4 != 0 || (n / 4) * 3 > sizeof(sig->bh)) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid bh tag");
-		return;
+		return 0;
 	}
 	/* EVP_DecodeBlock doesn't handle internal whitespace */
 	EVP_DecodeInit(ectx);
@@ -659,23 +651,25 @@ dkim_signature_parse_bh(struct signature *sig, const c
 	    (int)(end - start)) == -1) {
 		/* Paranoia check */
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid bh tag");
-		return;
+		return 0;
 	}
 	sig->bhsz = decodesz;
 	if (EVP_DecodeFinal(ectx, sig->bh + sig->bhsz, &decodesz) == -1) {
 		/* Paranoia check */
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid bh tag");
-		return;
+		return 0;
 	}
 	sig->bhsz += decodesz;
+
+	return 0;
 }
 
-void
+int
 dkim_signature_parse_c(struct signature *sig, const char *start, const char *end)
 {
 	if (sig->c != 0) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate c tag");
-		return;
+		return 0;
 	}
 	if (strncmp(start, "simple", 6) == 0) {
 		sig->c = CANON_HEADER_SIMPLE;
@@ -685,7 +679,7 @@ dkim_signature_parse_c(struct signature *sig, const ch
 		start += 7;
 	} else {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid c tag");
-		return;
+		return 0;
 	}
 	if (start[0] == '/') {
 		start++;
@@ -698,33 +692,36 @@ dkim_signature_parse_c(struct signature *sig, const ch
 		} else {
 			dkim_signature_state(sig, DKIM_PERMERROR,
 			    "Invalid c tag");
-			return;
+			return 0;
 		}
 	}
 
 	if (start != end) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid c tag");
-		return;
+		return 0;
 	}
 	sig->c |= CANON_DONE;
+
+	return 0;
 }
 
-void
+int
 dkim_signature_parse_d(struct signature *sig, const char *start, const char *end)
 {
 	if (sig->d[0] != '\0') {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate d tag");
-		return;
+		return 0;
 	}
 	if (osmtpd_ltok_skip_sig_d_tag_value(start, 0) != end ||
 	    (size_t)(end - start) >= sizeof(sig->d)) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid d tag");
-		return;
+		return 0;
 	}
 	strlcpy(sig->d, start, end - start + 1);
+	return 0;
 }
 
-void
+int
 dkim_signature_parse_h(struct signature *sig, const char *start, const char *end)
 {
 	const char *h;
@@ -732,18 +729,18 @@ dkim_signature_parse_h(struct signature *sig, const ch
 
 	if (sig->h != NULL) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate h tag");
-		return;
+		return 0;
 	}
 	if (osmtpd_ltok_skip_sig_h_tag_value(start, 0) < end) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid h tag");
-		return;
+		return 0;
 	}
 	h = start;
 	while (1) {
 		if ((h = osmtpd_ltok_skip_hdr_name(h, 0)) == NULL) {
 			dkim_signature_state(sig, DKIM_PERMERROR,
 			    "Invalid h tag");
-			return;
+			return 0;
 		}
 		n++;
 		/* ';' is part of hdr-name */
@@ -757,8 +754,8 @@ dkim_signature_parse_h(struct signature *sig, const ch
 		h = osmtpd_ltok_skip_fws(h + 1, 1);
 	}
 	if ((sig->h = calloc(n + 1, sizeof(*sig->h))) == NULL) {
-		dkim_err(sig->header->msg, "malloc");
-		return;
+		osmtpd_warn(sig->header->msg->ctx, "malloc");
+		return -1;
 	}
 	n = 0;
 	h = start;
@@ -770,32 +767,35 @@ dkim_signature_parse_h(struct signature *sig, const ch
 			break;
 		}
 		if ((sig->h[n++] = strndup(start, h - start)) == NULL) {
-			dkim_err(sig->header->msg, "malloc");
-			return;
+			osmtpd_warn(sig->header->msg->ctx, "malloc");
+			return -1;
 		}
 		start = osmtpd_ltok_skip_fws(h, 1);
 		if (start[0] != ':')
 			break;
 		start = osmtpd_ltok_skip_fws(start + 1, 1);
 	}
+	return 0;
 }
 
-void
+int
 dkim_signature_parse_i(struct signature *sig, const char *start, const char *end)
 {
 	if (sig->i != NULL) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate i tag");
-		return;
+		return 0;
 	}
 	if (osmtpd_ltok_skip_sig_i_tag_value(start, 0) != end) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid i tag");
-		return;
+		return 0;
 	}
 	sig->i = start;
 	sig->isz = (size_t)(end - start);
+
+	return 0;
 }
 
-void
+int
 dkim_signature_parse_l(struct signature *sig, const char *start, const char *end)
 {
 	long long l;
@@ -803,7 +803,7 @@ dkim_signature_parse_l(struct signature *sig, const ch
 
 	if (sig->l != -1) {	/* Duplicate tag */
 		dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate l tag");
-		return;
+		return 0;
 	}
 	errno = 0;
 	l = strtoll(start, &lend, 10);
@@ -811,23 +811,25 @@ dkim_signature_parse_l(struct signature *sig, const ch
 	if (osmtpd_ltok_skip_digit(start, 0) == NULL ||
 	    lend != end || errno != 0) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid l tag");
-		return;
+		return 0;
 	}
 	if (l > SSIZE_MAX) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "l tag too large");
-		return;
+		return 0;
 	}
 	sig->l = (ssize_t)l;
+
+	return 0;
 }
 
-void
+int
 dkim_signature_parse_q(struct signature *sig, const char *start, const char *end)
 {
 	const char *qend;
 
 	if (sig->q != 0) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate q tag");
-		return;
+		return 0;
 	}
 
 	while (1) {
@@ -835,7 +837,7 @@ dkim_signature_parse_q(struct signature *sig, const ch
 		qend = osmtpd_ltok_skip_sig_q_tag_method(start, 0);
 		if (qend == NULL) {
 			dkim_signature_state(sig, DKIM_PERMERROR, "Invalid q tag");
-			return;
+			return 0;
 		}
 		if (strncmp(start, "dns/txt", qend - start) == 0)
 			sig->q = 1;
@@ -845,81 +847,95 @@ dkim_signature_parse_q(struct signature *sig, const ch
 	}
 	if (start != end) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid q tag");
-		return;
+		return 0;
 	}
 	if (sig->q != 1) {
 		sig->q = 1;
 		dkim_signature_state(sig, DKIM_NEUTRAL, "No useable q found");
-		return;
+		return 0;
 	}
+
+	return 0;
 }
 
-void
+int
 dkim_signature_parse_s(struct signature *sig, const char *start, const char *end)
 {
 	if (sig->s[0] != '\0') {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate s tag");
-		return;
+		return 0;
 	}
 	if (osmtpd_ltok_skip_selector(start, 0) != end) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid s tag");
-		return;
+		return 0;
 	}
-	strlcpy(sig->s, start, end - start + 1);
+	if (sizeof(sig->s) >= (size_t)(end - start)) {
+		dkim_signature_state(sig, DKIM_PERMERROR, "s tag too long");
+		return 0;
+	}
+	strncpy(sig->s, start, end - start);
+
+	return 0;
 }
 
-void
+int
 dkim_signature_parse_t(struct signature *sig, const char *start, const char *end)
 {
 	char *tend;
 
 	if (sig->t != -1) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate t tag");
-		return;
+		return 0;
 	}
 	errno = 0;
 	sig->t = strtoll(start, &tend, 10);
 	if (osmtpd_ltok_skip_digit(start, 0) == NULL || tend != end ||
 	    tend - start > 12 || errno != 0) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid t tag");
-		return;
+		return 0;
 	}
+
+	return 0;
 }
 
-void
+int
 dkim_signature_parse_x(struct signature *sig, const char *start, const char *end)
 {
 	char *xend;
 
 	if (sig->x != -1) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate x tag");
-		return;
+		return 0;
 	}
 	errno = 0;
 	sig->x = strtoll(start, &xend, 10);
 	if (osmtpd_ltok_skip_digit(start, 0) == NULL || xend != end ||
 	    xend - start > 12 || errno != 0) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid x tag");
-		return;
+		return 0;
 	}
+
+	return 0;
 }
 
-void
+int
 dkim_signature_parse_z(struct signature *sig, const char *start, const char *end)
 {
 	if (sig->z != 0) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate z tag");
-		return;
+		return 0;
 	}
 
 	sig->z = 1;
 	if (osmtpd_ltok_skip_sig_z_tag_value(start, 0) != end) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid z tag");
-		return;
+		return 0;
 	}
+
+	return 0;
 }
 
-void
+int
 dkim_signature_verify(struct signature *sig)
 {
 	struct message *msg = sig->header->msg;
@@ -930,25 +946,25 @@ dkim_signature_verify(struct signature *sig)
 	size_t i, header;
 
 	if (sig->state != DKIM_UNKNOWN)
-		return;
+		return 0;
 
 	if (bctx == NULL) {
 		if ((bctx = EVP_MD_CTX_new()) == NULL) {
-			dkim_errx(msg, "EVP_MD_CTX_new");
-			return;
+			osmtpd_warnx(msg->ctx, "EVP_MD_CTX_new");
+			return -1;
 		}
 	}
 	EVP_MD_CTX_reset(bctx);
 	if (!sig->sephash) {
 		if (EVP_DigestVerifyInit(bctx, NULL, sig->ah, NULL,
 		    sig->p) != 1) {
-			dkim_errx(msg, "EVP_DigestVerifyInit");
-			return;
+			osmtpd_warnx(msg->ctx, "EVP_DigestVerifyInit");
+			return -1;
 		}
 	} else {
 		if (EVP_DigestInit_ex(bctx, sig->ah, NULL) != 1) {
-			dkim_errx(msg, "EVP_DigestInit_ex");
-			return;
+			osmtpd_warnx(msg->ctx, "EVP_DigestInit_ex");
+			return -1;
 		}
 	}
 
@@ -967,22 +983,25 @@ dkim_signature_verify(struct signature *sig)
 			    msg->header[i].buf + strlen(sig->h[header]), 1);
 			if (end[0] != ':')
 				continue;
-			dkim_signature_header(bctx, sig, &(msg->header[i]));
+			if (dkim_signature_header(bctx, sig,
+			    &(msg->header[i])) == -1)
+				return -1;
 			msg->header[i].parsed = 1;
 		}
 	}
-	dkim_signature_header(bctx, sig, sig->header);
+	if (dkim_signature_header(bctx, sig, sig->header) == -1)
+		return -1;
 	if (!sig->sephash) {
 		if (EVP_DigestVerifyFinal(bctx, sig->b, sig->bsz) != 1)
 			dkim_signature_state(sig, DKIM_FAIL, "b mismatch");
 	} else {
 		if (EVP_DigestFinal_ex(bctx, digest, &digestsz) == 0) {
-			dkim_errx(msg, "EVP_DigestFinal_ex");
-			return;
+			osmtpd_warnx(msg->ctx, "EVP_DigestFinal_ex");
+			return -1;
 		}
 		if (EVP_DigestVerifyInit(bctx, NULL, NULL, NULL, sig->p) != 1) {
-			dkim_errx(msg, "EVP_DigestVerifyInit");
-			return;
+			osmtpd_warnx(msg->ctx, "EVP_DigestVerifyInit");
+			return -1;
 		}
 		switch (EVP_DigestVerify(bctx, sig->b, sig->bsz, digest,
 		    digestsz)) {
@@ -992,10 +1011,11 @@ dkim_signature_verify(struct signature *sig)
 			dkim_signature_state(sig, DKIM_FAIL, "b mismatch");
 			break;
 		default:
-			dkim_errx(msg, "EVP_DigestVerify");
-			return;
+			osmtpd_warnx(msg->ctx, "EVP_DigestVerify");
+			return -1;
 		}
 	}
+	return 0;
 }
 
 /* EVP_DigestVerifyUpdate is a macro, so we can't alias this on a variable */
@@ -1003,7 +1023,7 @@ dkim_signature_verify(struct signature *sig)
 	(sig->sephash ? EVP_DigestUpdate((a), (b), (c)) :\
 	    EVP_DigestVerifyUpdate((a), (b), (c)))
 
-void
+int
 dkim_signature_header(EVP_MD_CTX *bctx, struct signature *sig,
     struct header *header)
 {
@@ -1026,9 +1046,9 @@ dkim_signature_header(EVP_MD_CTX *bctx, struct signatu
 					    ptr + 1, 1) - 1;
 			}
 			if (dkim_b_digest_update(bctx, &c, 1) == 0) {
-				dkim_errx(sig->header->msg,
+				osmtpd_warnx(sig->header->msg->ctx,
 				    "dkim_b_digest_update");
-				return;
+				return -1;
 			}
 			continue;
 		}
@@ -1040,25 +1060,25 @@ dkim_signature_header(EVP_MD_CTX *bctx, struct signatu
 				continue;
 			}
 			if (dkim_b_digest_update(bctx, ptr, 1) == 0) {
-				dkim_errx(sig->header->msg,
+				osmtpd_warnx(sig->header->msg->ctx,
 				    "dkim_b_digest_update");
-				return;
+				return -1;
 			}
 		} else {
 			if (canon == CANON_HEADER_RELAXED) {
 				if (end[0] == '\0')
 					continue;
 				if (dkim_b_digest_update(bctx, " ", 1) == 0) {
-					dkim_errx(sig->header->msg,
+					osmtpd_warnx(sig->header->msg->ctx,
 					    "dkim_b_digest_update");
-					return;
+					return -1;
 				}
 			} else {
 				if (dkim_b_digest_update(bctx, ptr,
 				    end - ptr) == 0) {
-					dkim_errx(sig->header->msg,
+					osmtpd_warnx(sig->header->msg->ctx,
 					    "dkim_b_digest_update");
-					return;
+					return -1;
 				}
 			}
 			ptr = end - 1;
@@ -1067,10 +1087,13 @@ dkim_signature_header(EVP_MD_CTX *bctx, struct signatu
 	}
 	if (sig->header != header) {
 		if (dkim_b_digest_update(bctx, "\r\n", 2) == 0) {
-			dkim_errx(sig->header->msg, "dkim_b_digest_update");
-			return;
+			osmtpd_warnx(sig->header->msg->ctx,
+			    "dkim_b_digest_update");
+			return -1;
 		}
 	}
+
+	return 0;
 }
 
 void
@@ -1194,19 +1217,23 @@ dkim_rr_resolve(struct asr_result *ar, void *arg)
 		dkim_signature_state(sig, DKIM_PERMERROR,
 		    "No matching key found");
 	} else {
+		if (sig->p == NULL)
+			osmtpd_errx(1, "%s:%d TODO", __func__, __LINE__);
 		/* Only verify if all headers have been read */
 		if (!sig->header->msg->parsing_headers)
-			dkim_signature_verify(sig);
+			if (dkim_signature_verify(sig) == -1)
+				osmtpd_errx(1, "%s:%d TODO", __func__, __LINE__);
 	}
  verify:
 	free(ar->ar_data);
-	dkim_message_verify(sig->header->msg);
+	if (dkim_message_verify(sig->header->msg) == -1)
+		osmtpd_errx(1, "%s:%d TODO", __func__, __LINE__);
 }
 
 int
 dkim_key_text_parse(struct signature *sig, const char *key)
 {
-	char tagname, *hashname;
+	char tagname, hashname[100];
 	const char *end, *tagvend;
 	char pkraw[UINT16_MAX] = "", pkimp[UINT16_MAX];
 	size_t pkrawlen = 0, pkoff, linelen;
@@ -1260,17 +1287,13 @@ dkim_key_text_parse(struct signature *sig, const char 
 				if ((tagvend = osmtpd_ltok_skip_key_h_tag_alg(
 				    key, 0)) == NULL)
 					break;
-				hashname = strndup(key, tagvend - key);
-				if (hashname == NULL) {
-					dkim_err(sig->header->msg, "malloc");
+				if (sizeof(hashname) <= (size_t)(tagvend - key))
 					return 0;
-				}
+				strncpy(hashname, key, tagvend - key);
 				if (EVP_get_digestbyname(hashname) == sig->ah) {
-					free(hashname);
 					h = 1;
 					break;
 				}
-				free(hashname);
 				key = osmtpd_ltok_skip_fws(tagvend, 1);
 				if (key[0] != ':')
 					break;
@@ -1422,7 +1445,7 @@ dkim_key_text_parse(struct signature *sig, const char 
 		pkoff += strlcpy(pkimp + pkoff, "-----END PUBLIC KEY-----\n",
 		    sizeof(pkimp) - pkoff);
 		if ((bio = BIO_new_mem_buf(pkimp, pkoff)) == NULL) {
-			dkim_err(sig->header->msg, "BIO_new_mem_buf");
+			osmtpd_warnx(sig->header->msg->ctx, "BIO_new_mem_buf");
 			return 1;
 		}
 		sig->p = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
@@ -1455,7 +1478,7 @@ dkim_key_text_parse(struct signature *sig, const char 
 	return 1;
 }
 
-void
+int
 dkim_body_parse(struct message *msg, const char *line)
 {
 	struct signature *sig;
@@ -1465,7 +1488,7 @@ dkim_body_parse(struct message *msg, const char *line)
 
 	if (line[0] == '\0') {
 		msg->body_whitelines++;
-		return;
+		return 0;
 	}
 
 	while (msg->body_whitelines-- > 0) {
@@ -1476,8 +1499,8 @@ dkim_body_parse(struct message *msg, const char *line)
 			hashn = sig->l == -1 ? 2 : MIN(2, sig->l);
 			sig->l -= sig->l == -1 ? 0 : hashn;
 			if (EVP_DigestUpdate(sig->bhctx, "\r\n", hashn) == 0) {
-				dkim_errx(msg, "EVP_DigestUpdate");
-				return;
+				osmtpd_warnx(msg->ctx, "EVP_DigestUpdate");
+				return -1;
 			}
 		}
 	}
@@ -1513,8 +1536,8 @@ dkim_body_parse(struct message *msg, const char *line)
 			sig->l -= sig->l == -1 ? 0 : hashn;
 			ret = EVP_DigestUpdate(sig->bhctx, hash, hashn);
 			if (ret == 0) {
-				dkim_errx(msg, "EVP_DigestUpdate");
-				return;
+				osmtpd_warnx(msg->ctx, "EVP_DigestUpdate");
+				return -1;
 			}
 		}
 		line = end;
@@ -1527,46 +1550,50 @@ dkim_body_parse(struct message *msg, const char *line)
 		sig->l -= sig->l == -1 ? 0 : hashn;
 		ret = EVP_DigestUpdate(sig->bhctx, "\r\n", hashn);
 		if (ret == 0) {
-			dkim_errx(msg, "EVP_DigestUpdate");
-			return;
+			osmtpd_warnx(msg->ctx, "EVP_DigestUpdate");
+			return -1;
 		}
 	}
+
+	return 0;
 }
 
-void
+int
 dkim_body_verify(struct signature *sig)
 {
 	unsigned char digest[EVP_MAX_MD_SIZE];
 	unsigned int digestsz;
 
 	if (sig->state != DKIM_UNKNOWN)
-		return;
+		return 0;
 
 	if ((sig->c & CANON_BODY) == CANON_BODY_SIMPLE &&
 	    !sig->header->msg->has_body) {
 		if (EVP_DigestUpdate(sig->bhctx, "\r\n",
 		    sig->l == -1 ? 2 : MIN(2, sig->l)) <= 0) {
-			dkim_errx(sig->header->msg,
+			osmtpd_warnx(sig->header->msg->ctx,
 			    "Can't update hash context");
-			return;
+			return -1;
 		}
 	}
 	if (sig->l > 0) {
 		dkim_signature_state(sig, DKIM_PERMERROR,
 		    "l tag larger than body");
-		return;
+		return 0;
 	}
 
 	if (EVP_DigestFinal_ex(sig->bhctx, digest, &digestsz) == 0) {
-		dkim_errx(sig->header->msg, "EVP_DigestFinal_ex");
-		return;
+		osmtpd_warnx(sig->header->msg->ctx, "EVP_DigestFinal_ex");
+		return -1;
 	}
 
 	if (digestsz != sig->bhsz || memcmp(digest, sig->bh, digestsz) != 0)
 		dkim_signature_state(sig, DKIM_FAIL, "bh mismatch");
+
+	return 0;
 }
 
-void
+int
 dkim_message_verify(struct message *msg)
 {
 	struct signature *sig;
@@ -1575,15 +1602,16 @@ dkim_message_verify(struct message *msg)
 	int found = 0;
 	char *line = NULL;
 	size_t linelen = 0;
+	int r = -1;
 
 	if (!msg->readdone)
-		return;
+		return 0;
 
 	for (i = 0; i < msg->nheaders; i++) {
 		if (msg->header[i].sig == NULL)
 			continue;
 		if (msg->header[i].sig->query != NULL)
-			return;
+			return 0;
 		if (msg->header[i].sig->state != DKIM_UNKNOWN)
 			continue;
 		dkim_signature_state(msg->header[i].sig, DKIM_PASS, NULL);
@@ -1591,7 +1619,7 @@ dkim_message_verify(struct message *msg)
 	
 	if ((aroff = dkim_ar_cat(&line, &linelen, aroff,
 	    "Authentication-Results: %s", authservid)) == -1) {
-		dkim_err(msg, "malloc");
+		osmtpd_warn(msg->ctx, "malloc");
 		goto fail;
 	}
 	for (i = 0; i < msg->nheaders; i++) {
@@ -1601,27 +1629,27 @@ dkim_message_verify(struct message *msg)
 		found = 1;
 		if ((aroff = dkim_ar_cat(&line, &linelen, aroff, "; dkim=%s",
 		    dkim_state2str(sig->state))) == -1) {
-			dkim_err(msg, "malloc");
+			osmtpd_warn(msg->ctx, "malloc");
 			goto fail;
 		}
 		if (sig->state_reason != NULL) {
 			if ((aroff = dkim_ar_cat(&line, &linelen, aroff,
 			    " reason=\"%s\"", sig->state_reason)) == -1) {
-				dkim_err(msg, "malloc");
+				osmtpd_warn(msg->ctx, "malloc");
 				goto fail;
 			}
 		}
 		if (sig->s[0] != '\0') {
 			if ((aroff = dkim_ar_cat(&line, &linelen, aroff,
 			    " header.s=%s", sig->s)) == -1) {
-				dkim_err(msg, "malloc");
+				osmtpd_warn(msg->ctx, "malloc");
 				goto fail;
 			}
 		}
 		if (sig->d[0] != '\0') {
 			if ((aroff = dkim_ar_cat(&line, &linelen, aroff,
 			    " header.d=%s", sig->d)) == -1) {
-				dkim_err(msg, "malloc");
+				osmtpd_warn(msg->ctx, "malloc");
 				goto fail;
 			}
 		}
@@ -1632,7 +1660,7 @@ dkim_message_verify(struct message *msg)
 		if (sig->a != NULL) {
 			if ((aroff = dkim_ar_cat(&line, &linelen, aroff,
 			    " header.a=%.*s", (int)sig->asz, sig->a)) == -1) {
-				dkim_err(msg, "malloc");
+				osmtpd_warn(msg->ctx, "malloc");
 				goto fail;
 			}
 		}
@@ -1640,7 +1668,7 @@ dkim_message_verify(struct message *msg)
 	if (!found) {
 		aroff = dkim_ar_cat(&line, &linelen, aroff, "; dkim=none");
 		if (aroff == -1) {
-			dkim_err(msg, "malloc");
+			osmtpd_warn(msg->ctx, "malloc");
 			goto fail;
 		}
 	}
@@ -1651,11 +1679,14 @@ dkim_message_verify(struct message *msg)
 		line[n - 1] = '\0';
 		osmtpd_filter_dataline(msg->ctx, "%s", line);
 	}
-	if (ferror(msg->origf))
-		dkim_err(msg, "getline");
+	if (ferror(msg->origf)) {
+		osmtpd_warnx(msg->ctx, "getline");
+		goto fail;
+	}
+	r = 0;
  fail:
 	free(line);
-	return;
+	return r;
 }
 
 void
@@ -1737,20 +1768,6 @@ dkim_ar_cat(char **ar, size_t *n, size_t aroff, const 
 	return (ssize_t)size + aroff;
 }
 
-void
-dkim_err(struct message *msg, char *text)
-{
-	msg->err = 1;
-	fprintf(stderr, "%s: %s\n", text, strerror(errno));
-}
-
-void
-dkim_errx(struct message *msg, char *text)
-{
-	msg->err = 1;
-	fprintf(stderr, "%s\n", text);
-}
-
 __dead void
 usage(void)
 {