From ca90a3a2ec250493d8082276649832390f3ecedc Mon Sep 17 00:00:00 2001 From: Emmanuel Dreyfus Date: Thu, 16 Mar 2023 16:27:35 +0100 Subject: [PATCH 1/3] OmitHeadersDB directive, for a per-recipient list of headers to not sign. The directive specify a tab-separated file with two columns: recipient (as user@domain or just domain) and comma-separated list of headers to not use in signature. For instance, to cope with a miling list that adds a tag in the subject: list@example.net Subject --- opendkim/opendkim.c | 219 ++++++++++++++++++++++++++++++++++++ opendkim/opendkim.conf.5.in | 9 ++ 2 files changed, 228 insertions(+) diff --git a/opendkim/opendkim.c b/opendkim/opendkim.c index d4229e8f..9f593a07 100644 --- a/opendkim/opendkim.c +++ b/opendkim/opendkim.c @@ -390,6 +390,7 @@ struct dkimf_config DKIMF_DB conf_domainsdb; /* domains to sign (DB) */ DKIMF_DB conf_omithdrdb; /* headers to omit (DB) */ char ** conf_omithdrs; /* headers to omit (array) */ + DKIMF_DB conf_omithdrkdb; /* headers to omit (keyed DB) */ DKIMF_DB conf_signhdrsdb; /* headers to sign (DB) */ char ** conf_signhdrs; /* headers to sign (array) */ DKIMF_DB conf_senderhdrsdb; /* sender headers (DB) */ @@ -519,6 +520,7 @@ struct msgctx /* primary domain */ unsigned char mctx_dkimar[DKIM_MAXHEADER + 1]; /* DKIM Auth-Results content */ + char ** mctx_omithdrs; /* headers to omit (array) */ }; /* @@ -1053,6 +1055,188 @@ dkimf_getsymval(SMFICTX *ctx, char *sym) return smfi_getsymval(ctx, sym); } + +/* +** DKIMF_GET_OMITHEADERS -- Get per-recipient headers to not sign +** +** Parameters: +** conf -- configuration handle +** rcpt -- recipient +** hdrs -- comma-separated list of headers (returned) +** hdrslen -- size of hdrs buffer +** +** Return value: +** 0 -- success +** !0 -- error +*/ + +static int +dkimf_get_omitheaders(struct dkimf_config *conf, const char *rcpt, + char *hdrs, size_t hdrslen) +{ + int status; + _Bool found; + unsigned char *user = NULL; + unsigned char *domain = NULL; + char tmp[MAXADDRESS + 1]; + char addr[MAXADDRESS + 1]; + struct dkimf_db_data req; + + assert(conf != NULL); + assert(conf->conf_omithdrkdb != NULL); + assert(rcpt != NULL); + assert(hdrs != NULL); + + strlcpy(tmp, rcpt, sizeof(tmp)); + status = dkim_mail_parse(tmp, &user, &domain); + if (status != 0) + { + return -1; + } + + memset(&req, '\0', sizeof req); + req.dbdata_buffer = hdrs; + req.dbdata_buflen = hdrslen; + + /* first try full "user@host" */ + found = FALSE; + (void)snprintf(addr, sizeof addr, "%s@%s", user, domain); + status = dkimf_db_get(conf->conf_omithdrkdb, addr, strlen(addr), + &req, 1, &found); + if (status != 0 || + (found && (req.dbdata_buflen == 0 || + req.dbdata_buflen == (size_t) -1))) + { + return -1; + } + else if (found) + { + req.dbdata_buffer[req.dbdata_buflen] = '\0'; + return 0; + } + + /* now just "host" */ + + memset(&req, '\0', sizeof req); + req.dbdata_buffer = hdrs; + req.dbdata_buflen = hdrslen; + + found = FALSE; + status = dkimf_db_get(conf->conf_omithdrkdb, domain, strlen(domain), + &req, 1, &found); + if (status != 0 || + (found && (req.dbdata_buflen == 0 || + req.dbdata_buflen == (size_t) -1))) + { + return -1; + } + else if (found) + { + req.dbdata_buffer[req.dbdata_buflen] = '\0'; + return 0; + } + + return -1; +} + +/* +** DKIMF_APPEND_ARRAY -- Append an item to an string array +** The item is only added if it is not +** alreay present. +** +** Parameters: +** arrayp -- pointer to array +** item -- new string to append +** init -- array of strings to insert if array is empty +** +** Return value: +** None +*/ + +static void +dkimf_append_array(char ***arrayp, char *item, const u_char **init) +{ + char **array; + int i; + + assert(arrayp != NULL); + assert(*arrayp != NULL || init != NULL); + + array = *arrayp; + + if (array == NULL) { + for (i = 0; init[i]; i++); + + array = calloc(i + 1, sizeof(*array)); + if (array == NULL) + { + syslog(LOG_ERR, "calloc failed"); + return; + } + + for (i = 0; init[i]; i++) + array[i] = strdup(init[i]); + } + + for (i = 0; array[i]; i++) { + if (strcasecmp(array[i], item) == 0) + return; + } + + if ((array = realloc(array, (i + 2) * sizeof(*array))) == NULL) + { + syslog(LOG_ERR, "realloc failed"); + return; + } + + array[i] = strdup(item); + array[i + 1] = NULL; + + *arrayp = array; + + return; +} + +/* +** DKIMF_CONFIGURE_OMITHEADERS -- Configure per-recipient header to not sign +** +** Parameters: +** conf -- configuration handle +** omithdrs -- comma-separated list of headers to not sign +** +** Return value: +** None +*/ + +static void +dkimf_configure_omitheaders(msgctx mctx, struct dkimf_config *conf, + char *omithdrs) +{ + size_t count = 0; + const char *delim = " ,"; + char *p; + char *last; + + assert(conf != NULL); + assert(conf->conf_libopendkim != NULL); + + if (omithdrs) { + for (p = strtok_r(omithdrs, delim, &last); + p != NULL; + p = strtok_r(NULL, delim, &last)) + dkimf_append_array(&mctx->mctx_omithdrs, p, + dkim_should_not_signhdrs); + } + + (void)dkim_options(conf->conf_libopendkim, + DKIM_OP_SETOPT, + DKIM_OPTS_SKIPHDRS, + mctx->mctx_omithdrs, sizeof(char **)); + + return; +} + + #ifdef USE_LUA /* ** LUA ACCESSOR FUNCTIONS @@ -5920,6 +6104,9 @@ dkimf_config_free(struct dkimf_config *conf) if (conf->conf_omithdrdb != NULL) dkimf_db_close(conf->conf_omithdrdb); + if (conf->conf_omithdrkdb != NULL) + dkimf_db_close(conf->conf_omithdrkdb); + if (conf->conf_thirdpartydb != NULL) dkimf_db_close(conf->conf_thirdpartydb); @@ -7293,6 +7480,29 @@ dkimf_config_load(struct config *data, struct dkimf_config *conf, } } + str = NULL; + if (data != NULL) + { + (void) config_get(data, "OmitHeadersDB", &str, sizeof str); + if (str != NULL) + { + int status; + char *dberr = NULL; + + status = dkimf_db_open(&conf->conf_omithdrkdb, str, + (dbflags | + DKIMF_DB_FLAG_ICASE | + DKIMF_DB_FLAG_READONLY), + NULL, &dberr); + if (status != 0) + { + snprintf(err, errlen, "%s: dkimf_db_open(): %s", + str, dberr); + return -1; + } + } + } + str = NULL; if (data != NULL) { @@ -11238,6 +11448,15 @@ mlfi_envrcpt(SMFICTX *ctx, char **envrcpt) dfc->mctx_rcptlist = a; } + if (conf->conf_omithdrkdb != NULL) { + char omithdrs[MAXBUFRSZ]; + if (dkimf_get_omitheaders(conf, envrcpt[0], + omithdrs, sizeof omithdrs) != -1) + dkimf_configure_omitheaders(dfc, conf, omithdrs); + else + dkimf_configure_omitheaders(dfc, conf, NULL); + } + return SMFIS_CONTINUE; } diff --git a/opendkim/opendkim.conf.5.in b/opendkim/opendkim.conf.5.in index 9046a1cd..5adff93e 100644 --- a/opendkim/opendkim.conf.5.in +++ b/opendkim/opendkim.conf.5.in @@ -619,6 +619,15 @@ default; for example, "*,+foobar" will use the entire default list plus the name "foobar", while "*,-Bcc" would use the entire default list except for the "Bcc" entry. +.TP +.I OmitHeadersDB (string) +Specify a file containing a per-recipient list of headers to omit +when generating signatures. Return-Path, Received, Comments, and +Keywoard are implicitely added to the list. The file should have +two columns : a recipient as user@domain.tld or domain.tld, and +a comma-separated lis of headers to omit. For example +"list@example.net Subject,Bcc" + .TP .I On-BadSignature (string) Selects the action to be taken when a signature fails to validate. From 263e96ad19d21db3318997ec7405abc901fddbcc Mon Sep 17 00:00:00 2001 From: Emmanuel Dreyfus Date: Thu, 23 Mar 2023 15:21:14 +0100 Subject: [PATCH 2/3] Add missing commit for OmitHeadersDB --- opendkim/opendkim-config.h | 1 + 1 file changed, 1 insertion(+) diff --git a/opendkim/opendkim-config.h b/opendkim/opendkim-config.h index 45d64939..fd14e2f9 100644 --- a/opendkim/opendkim-config.h +++ b/opendkim/opendkim-config.h @@ -115,6 +115,7 @@ struct configdef dkimf_config[] = { "Nameservers", CONFIG_TYPE_STRING, FALSE }, { "NoHeaderB", CONFIG_TYPE_BOOLEAN, FALSE }, { "OmitHeaders", CONFIG_TYPE_STRING, FALSE }, + { "OmitHeadersDB", CONFIG_TYPE_STRING, FALSE }, { "On-BadSignature", CONFIG_TYPE_STRING, FALSE }, { "On-Default", CONFIG_TYPE_STRING, FALSE }, { "On-DNSError", CONFIG_TYPE_STRING, FALSE }, From 6e9b5c37dc2c9a74b9e2b4504ad1ff189ab90eb3 Mon Sep 17 00:00:00 2001 From: Emmanuel Dreyfus Date: Tue, 30 Jul 2024 15:27:16 +0200 Subject: [PATCH 3/3] Check for NULL pointers from dkim_mail_parse() --- opendkim/opendkim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendkim/opendkim.c b/opendkim/opendkim.c index 9f593a07..d167dc80 100644 --- a/opendkim/opendkim.c +++ b/opendkim/opendkim.c @@ -1089,7 +1089,7 @@ dkimf_get_omitheaders(struct dkimf_config *conf, const char *rcpt, strlcpy(tmp, rcpt, sizeof(tmp)); status = dkim_mail_parse(tmp, &user, &domain); - if (status != 0) + if (status != 0 || user == NULL || domain == NULL) { return -1; }