From 4bee1f7372c231a068023826a95fed734003c26e Mon Sep 17 00:00:00 2001 From: FUTATSUKI Yasuhito Date: Fri, 1 Mar 2024 04:24:14 +0900 Subject: [PATCH 1/4] Extend structure signreq to hold signing algorithm per request --- opendkim/opendkim.c | 6 +++++- opendkim/opendkim.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/opendkim/opendkim.c b/opendkim/opendkim.c index 803f37b0..dedffda7 100644 --- a/opendkim/opendkim.c +++ b/opendkim/opendkim.c @@ -5091,6 +5091,7 @@ dkimf_add_signrequest(struct msgctx *dfc, DKIMF_DB keytable, char *keyname, new->srq_selector = NULL; new->srq_keydata = NULL; new->srq_signlen = signlen; + new->srq_signalg = DKIM_SIGN_DEFAULT; if (signer != NULL && signer[0] != '\0') new->srq_signer = (u_char *) strdup(signer); else @@ -12619,6 +12620,7 @@ mlfi_eoh(SMFICTX *ctx) for (sr = dfc->mctx_srhead; sr != NULL; sr = sr->srq_next) { + dkim_alg_t signalg; #ifdef _FFR_CONDITIONAL if (sr->srq_dkim != NULL) continue; @@ -12645,13 +12647,15 @@ mlfi_eoh(SMFICTX *ctx) selector = conf->conf_selector; } + signalg = (sr->srq_signalg == DKIM_SIGN_DEFAULT)? + dfc->mctx_signalg:sr->srq_signalg; sr->srq_dkim = dkim_sign(conf->conf_libopendkim, dfc->mctx_jobid, NULL, keydata, selector, sdomain, dfc->mctx_hdrcanon, dfc->mctx_bodycanon, - dfc->mctx_signalg, + signalg, signlen, &status); if (sr->srq_dkim == NULL || status != DKIM_STAT_OK) diff --git a/opendkim/opendkim.h b/opendkim/opendkim.h index e3637b10..437ec946 100644 --- a/opendkim/opendkim.h +++ b/opendkim/opendkim.h @@ -120,6 +120,7 @@ struct signreq u_char * srq_domain; u_char * srq_selector; u_char * srq_signer; + dkim_alg_t srq_signalg; DKIM * srq_dkim; struct signreq * srq_next; }; From 2b84a15f28a433a74f91e625d785b5014778c627 Mon Sep 17 00:00:00 2001 From: FUTATSUKI Yasuhito Date: Fri, 1 Mar 2024 19:00:06 +0900 Subject: [PATCH 2/4] Extend KeyTable value field so that store sign algorithm in the 4th field * opendkim/opendkim.c (): Add forward declaration for dkimf_lookup_strtoint (dkimf_add_signrequest): Extract sign algorithm from 4th field in key table and set it in the sign request. (dkimf_config_load): Allow 4th field in key table value. --- opendkim/opendkim.c | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/opendkim/opendkim.c b/opendkim/opendkim.c index dedffda7..4fae42ff 100644 --- a/opendkim/opendkim.c +++ b/opendkim/opendkim.c @@ -4934,10 +4934,12 @@ dkimf_add_signrequest(struct msgctx *dfc, DKIMF_DB keytable, char *keyname, _Bool found = FALSE; size_t keydatasz = 0; struct signreq *new; - struct dkimf_db_data dbd[3]; + struct dkimf_db_data dbd[4]; char keydata[MAXBUFRSZ + 1]; char domain[DKIM_MAXHOSTNAMELEN + 1]; char selector[BUFRSZ + 1]; + char signalgstr[BUFRSZ + 1]; + dkim_alg_t signalg = DKIM_SIGN_DEFAULT; char err[BUFRSZ + 1]; assert(dfc != NULL); @@ -4963,6 +4965,7 @@ dkimf_add_signrequest(struct msgctx *dfc, DKIMF_DB keytable, char *keyname, memset(domain, '\0', sizeof domain); memset(selector, '\0', sizeof selector); memset(keydata, '\0', sizeof keydata); + memset(signalgstr, '\0', sizeof signalgstr); dbd[0].dbdata_buffer = domain; dbd[0].dbdata_buflen = sizeof domain - 1; @@ -4973,9 +4976,12 @@ dkimf_add_signrequest(struct msgctx *dfc, DKIMF_DB keytable, char *keyname, dbd[2].dbdata_buffer = keydata; dbd[2].dbdata_buflen = sizeof keydata - 1; dbd[2].dbdata_flags = DKIMF_DB_DATA_OPTIONAL; + dbd[3].dbdata_buffer = signalgstr; + dbd[3].dbdata_buflen = sizeof signalgstr - 1; + dbd[3].dbdata_flags = DKIMF_DB_DATA_OPTIONAL; if (dkimf_db_get(keytable, keyname, strlen(keyname), - dbd, 3, &found) != 0) + dbd, 4, &found) != 0) { memset(err, '\0', sizeof err); (void) dkimf_db_strerror(keytable, err, sizeof err); @@ -5079,6 +5085,24 @@ dkimf_add_signrequest(struct msgctx *dfc, DKIMF_DB keytable, char *keyname, if (curconf->conf_safekeys) return 2; } + + if (dbd[3].dbdata_buflen > 0 && signalgstr[0] != '\0') + { + signalg = (dkim_alg_t)dkim_name_to_code(dkim_table_algorithms, + signalgstr); + if (signalg == -1) + { + if (dolog) + { + syslog(LOG_ERR, + "KeyTable entry for '%s' corrupt:" + " invalid sign algorithm %s", + keyname, signalgstr); + } + + return 2; + } + } } new = malloc(sizeof *new); @@ -5091,7 +5115,7 @@ dkimf_add_signrequest(struct msgctx *dfc, DKIMF_DB keytable, char *keyname, new->srq_selector = NULL; new->srq_keydata = NULL; new->srq_signlen = signlen; - new->srq_signalg = DKIM_SIGN_DEFAULT; + new->srq_signalg = signalg; if (signer != NULL && signer[0] != '\0') new->srq_signer = (u_char *) strdup(signer); else @@ -8328,11 +8352,12 @@ dkimf_config_load(struct config *data, struct dkimf_config *conf, { _Bool first = TRUE; _Bool found; - struct dkimf_db_data dbd[3]; + struct dkimf_db_data dbd[4]; char keyname[BUFRSZ + 1]; char domain[BUFRSZ + 1]; char selector[BUFRSZ + 1]; char keydata[BUFRSZ + 1]; + char signalgstr[BUFRSZ + 1]; char signer[BUFRSZ + 1]; dbd[0].dbdata_flags = 0; @@ -8360,10 +8385,13 @@ dkimf_config_load(struct config *data, struct dkimf_config *conf, dbd[2].dbdata_buffer = keydata; dbd[2].dbdata_buflen = sizeof keydata - 1; dbd[2].dbdata_flags = DKIMF_DB_DATA_BINARY; + dbd[3].dbdata_buffer = signalgstr; + dbd[3].dbdata_buflen = sizeof signalgstr - 1; + dbd[3].dbdata_flags = DKIMF_DB_DATA_OPTIONAL; if (dkimf_db_get(conf->conf_keytabledb, keyname, strlen(keyname), - dbd, 3, &found) != 0 || + dbd, 4, &found) != 0 || !found || dbd[0].dbdata_buflen == 0 || dbd[1].dbdata_buflen == 0 || From 6e75699c3ae812ab7361d1cfa4edc0a534c47012 Mon Sep 17 00:00:00 2001 From: FUTATSUKI Yasuhito Date: Sat, 23 Mar 2024 11:55:13 +0900 Subject: [PATCH 3/4] opendkim-testkey: Support sign algorithm field in KeyTable --- opendkim/opendkim-testkey.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/opendkim/opendkim-testkey.c b/opendkim/opendkim-testkey.c index fc1df4af..dc9667d8 100644 --- a/opendkim/opendkim-testkey.c +++ b/opendkim/opendkim-testkey.c @@ -236,6 +236,8 @@ main(int argc, char **argv) char domain[BUFRSZ]; char selector[BUFRSZ]; char keypath[MAXBUFRSZ]; + char signalgstr[BUFRSZ]; + dkim_alg_t signalg; progname = (p = strrchr(argv[0], '/')) == NULL ? argv[0] : p + 1; @@ -464,7 +466,7 @@ main(int argc, char **argv) size_t keylen; DKIMF_DB db; char keyname[BUFRSZ + 1]; - struct dkimf_db_data dbd[3]; + struct dkimf_db_data dbd[4]; memset(dbd, '\0', sizeof dbd); @@ -491,6 +493,7 @@ main(int argc, char **argv) memset(domain, '\0', sizeof domain); memset(selector, '\0', sizeof selector); memset(keypath, '\0', sizeof keypath); + memset(signalgstr, '\0', sizeof signalgstr); dbd[0].dbdata_buffer = domain; dbd[0].dbdata_buflen = sizeof domain; @@ -498,11 +501,14 @@ main(int argc, char **argv) dbd[1].dbdata_buflen = sizeof selector; dbd[2].dbdata_buffer = keypath; dbd[2].dbdata_buflen = sizeof keypath; + dbd[3].dbdata_buffer = signalgstr; + dbd[3].dbdata_buflen = sizeof signalgstr; + dbd[3].dbdata_flags = DKIMF_DB_DATA_OPTIONAL; keylen = sizeof keyname; status = dkimf_db_walk(db, c == 0, keyname, &keylen, - dbd, 3); + dbd, 4); if (status == -1) { fprintf(stderr, @@ -524,6 +530,27 @@ main(int argc, char **argv) progname, c, keyname); } + if (signalgstr[0] != '\0') + { + signalg = (dkim_alg_t)dkim_name_to_code(dkim_table_algorithms, + signalgstr); + if (signalg == -1) + { + fprintf(stderr, + "%s: unknown sign algorithm " + "'%s' for key '%s'\n", + progname, signalgstr, keyname); + return 1; + } + + if (verbose > 1) + { + fprintf(stderr, + "%s: key '%s': sign algorithm is '%s'\n", + progname, keyname, signalgstr); + } + } + if (keypath[0] == '/' || strncmp(keypath, "./", 2) == 0 || strncmp(keypath, "../", 3) == 0) @@ -572,6 +599,9 @@ main(int argc, char **argv) progname, keyname); } + /* To do: check consistency of the key and algorithm. + It is needed to extend dkim_test_key() for it */ + dnssec = DKIM_DNSSEC_UNKNOWN; status = dkim_test_key(lib, selector, domain, From a05fead21a1923d28a738723c3f66e813648146d Mon Sep 17 00:00:00 2001 From: FUTATSUKI Yasuhito Date: Sat, 16 Mar 2024 05:21:34 +0900 Subject: [PATCH 4/4] Update manual of opendkim.conf and RELEASE_NOTE for KeyTable extension --- RELEASE_NOTES | 2 ++ opendkim/opendkim.conf.5.in | 11 +++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index b8cc446a..a3f5d07c 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -13,6 +13,8 @@ release, and a summary of the changes in that release. Make odkim.internal_ip() available to all Lua hooks. Problem noted by Iosif Fettich. Make bind code DNSSEC-aware. Patch from Jack Bates. + Extend KeyTable to specify signing algorithm. + Patch from Yasuhito Futatsuki. Fix dkimf_db_nextpunct() so it doesn't incorrectly identify an encoded hex digit as a value delimiter. Fix issue #8: The password file critical section isn't big enough. diff --git a/opendkim/opendkim.conf.5.in b/opendkim/opendkim.conf.5.in index 21da18f5..0bd6f0f1 100644 --- a/opendkim/opendkim.conf.5.in +++ b/opendkim/opendkim.conf.5.in @@ -349,7 +349,8 @@ If present, overrides any setting in the configuration file. The data set named here maps each key name to three values: (a) the name of the domain to use in the signature's "d=" value; (b) the name of the selector to use in the signature's "s=" value; -and (c) either a private key or a path to a file containing a private key. +and (c) either a private key or a path to a file containing a private key; +(d) (optional) signing algorithm to use with this key. If the first value consists solely of a percent sign ("%") character, it will be replaced by the apparent domain of the sender when generating a signature. @@ -357,7 +358,13 @@ If the third value starts with a slash ("/") character, or "./" or "../", then it is presumed to refer to a file from which the private key should be read, otherwise it is itself a PEM-encoded private key or a base64-encoded DER private key; a "%" in the third value in this case will be replaced by -the apparent domain name of the sender. The +the apparent domain name of the sender. The fourth field should be one +of supported siginig algorithms (see +.I SignatureAlgorithm +below). +If it is omited, the algorithm specified in +.I SignatureAlgorithm +is used for the key. .I SigningTable (see below) is used to select records from this table to be used to add signatures based on the message sender.