From 2bc0f45ec0637be57e5d87b576a72cac71ccaf81 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Sat, 9 Jun 2018 21:39:44 +0100 Subject: [PATCH] DKIM: support timestamp and expiry tags in signing. Bug 2260 --- doc/doc-docbook/spec.xfpt | 21 +++++++++++++++++---- doc/doc-txt/OptionLists.txt | 1 + src/src/dkim.c | 11 ++++++++++- src/src/structs.h | 1 + src/src/transports/smtp.c | 3 +++ test/confs/4520 | 3 +++ test/log/4520 | 2 +- test/mail/4520.b | 6 ++---- test/runtest | 17 ++++++++++++++++- test/scripts/4500-DKIM/4520 | 4 ++-- 10 files changed, 56 insertions(+), 13 deletions(-) diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index 431d4560c..863a6b949 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -23976,14 +23976,15 @@ the message. As a result, the overall timeout for a message depends on the size of the message. Its value must not be zero. See also &%final_timeout%&. +.option dkim_canon smtp string&!! unset .option dkim_domain smtp string list&!! unset -.option dkim_selector smtp string&!! unset +.option dkim_hash smtp string&!! sha256 +.option dkim_identity smtp string&!! unset .option dkim_private_key smtp string&!! unset -.option dkim_canon smtp string&!! unset +.option dkim_selector smtp string&!! unset .option dkim_strict smtp string&!! unset .option dkim_sign_headers smtp string&!! "per RFC" -.option dkim_hash smtp string&!! sha256 -.option dkim_identity smtp string&!! unset +.option dkim_timestamps smtp string&!! unset DKIM signing options. For details see section &<>&. @@ -39102,6 +39103,18 @@ If a '+' prefix if used, all headers that are present with this name will be signed, and one signature added for a missing header with the name will be appended. +.new +.option dkim_timestamps smtp integer&!! unset +This option controls the inclusion of timestamp information in the signature. +If not set, no such information will be included. +Otherwise, must be an unsigned number giving an offset in seconds from the current time +for the expiry tag +(eg. 1209600 for two weeks); +both creation (t=) and expiry (x=) tags will be included. + +RFC 6376 lists these tags as RECOMMENDED. +.wen + .section "Verifying DKIM signatures in incoming mail" "SECDKIMVFY" .cindex "DKIM" "verification" diff --git a/doc/doc-txt/OptionLists.txt b/doc/doc-txt/OptionLists.txt index 91ce182fd..0a6a32073 100644 --- a/doc/doc-txt/OptionLists.txt +++ b/doc/doc-txt/OptionLists.txt @@ -169,6 +169,7 @@ dkim_private_key string* unset smtp dkim_selector string* unset smtp 4.70 dkim_sign_headers string* (RFC4871) smtp 4.70 dkim_strict string* unset smtp 4.70 +dkim_timestamps integer* unset smtp 4.92 dkim_verify_signers string* $dkim_signers main 4.70 directory string* unset appendfile directory_file string* + appendfile diff --git a/src/src/dkim.c b/src/src/dkim.c index 317f6e50d..edbeded5e 100644 --- a/src/src/dkim.c +++ b/src/src/dkim.c @@ -645,6 +645,8 @@ if (dkim_domain) uschar * dkim_private_key_expanded; uschar * dkim_hash_expanded; uschar * dkim_identity_expanded = NULL; + uschar * dkim_timestamps_expanded = NULL; + unsigned long tval = 0, xval = 0; /* Get canonicalization to use */ @@ -695,6 +697,13 @@ if (dkim_domain) else if (!*dkim_identity_expanded) dkim_identity_expanded = NULL; + if (dkim->dkim_timestamps) + if (!(dkim_timestamps_expanded = expand_string(dkim->dkim_timestamps))) + { errwhen = US"dkim_timestamps"; goto expand_bad; } + else + xval = (tval = (unsigned long) time(NULL)) + + strtoul(dkim_timestamps_expanded, NULL, 10); + if (!(sig = pdkim_init_sign(&dkim_sign_ctx, dkim_signing_domain, dkim_signing_selector, dkim_private_key_expanded, @@ -708,7 +717,7 @@ if (dkim_domain) CS dkim_sign_headers_expanded, CS dkim_identity_expanded, pdkim_canon, - pdkim_canon, -1, 0, 0); + pdkim_canon, -1, tval, xval); if (!pdkim_set_sig_bodyhash(&dkim_sign_ctx, sig)) goto bad; diff --git a/src/src/structs.h b/src/src/structs.h index 98c95010d..8384920ee 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -878,6 +878,7 @@ struct ob_dkim { uschar *dkim_sign_headers; uschar *dkim_strict; uschar *dkim_hash; + uschar *dkim_timestamps; BOOL dot_stuffed; BOOL force_bodyhash; #ifdef EXPERIMENTAL_ARC diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 2dfb5b73a..6d7085881 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -63,6 +63,8 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, dkim.dkim_sign_headers) }, { "dkim_strict", opt_stringptr, (void *)offsetof(smtp_transport_options_block, dkim.dkim_strict) }, + { "dkim_timestamps", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, dkim.dkim_timestamps) }, #endif { "dns_qualify_single", opt_bool, (void *)offsetof(smtp_transport_options_block, dns_qualify_single) }, @@ -295,6 +297,7 @@ smtp_transport_options_block smtp_transport_option_defaults = { .dkim_sign_headers = NULL, .dkim_strict = NULL, .dkim_hash = US"sha256", + .dkim_timestamps = NULL, .dot_stuffed = FALSE, .force_bodyhash = FALSE, # ifdef EXPERIMENTAL_ARC diff --git a/test/confs/4520 b/test/confs/4520 index 3b8d781ea..89769230f 100644 --- a/test/confs/4520 +++ b/test/confs/4520 @@ -67,6 +67,9 @@ send_to_server: .ifdef STRICT dkim_strict = STRICT .endif +.ifdef TIMES + dkim_timestamps = TIMES +.endif file: driver = appendfile diff --git a/test/log/4520 b/test/log/4520 index 2208b4f72..d58393310 100644 --- a/test/log/4520 +++ b/test/log/4520 @@ -34,7 +34,7 @@ 1999-03-02 09:44:33 10HmaY-0005vi-00 Completed 1999-03-02 09:44:33 rcpt acl: macro: From:Sender:Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive 1999-03-02 09:44:33 10HmbA-0005vi-00 dkim_acl: signer: test.ex bits: 1024 h=From:From -1999-03-02 09:44:33 10HmbA-0005vi-00 DKIM: d=test.ex s=sel c=relaxed/relaxed a=rsa-sha256 b=1024 [verification succeeded] +1999-03-02 09:44:33 10HmbA-0005vi-00 DKIM: d=test.ex s=sel c=relaxed/relaxed a=rsa-sha256 b=1024 t=T x=T+10 [verification succeeded] 1999-03-02 09:44:33 10HmbA-0005vi-00 data acl: dkim status pass 1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmaZ-0005vi-00@myhost.test.ex 1999-03-02 09:44:33 10HmbA-0005vi-00 => b R=server_store T=file diff --git a/test/mail/4520.b b/test/mail/4520.b index 14ef0ddf6..22b8c62ed 100644 --- a/test/mail/4520.b +++ b/test/mail/4520.b @@ -5,10 +5,8 @@ Received: from the.local.host.name ([ip4.ip4.ip4.ip4] helo=myhost.test.ex) id 10HmbA-0005vi-00 for b@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=test.ex; - s=sel; h=From:From; bh=/Ab0giHZitYQbDhFszoqQRUkgqueaX9zatJttIU/plc=; b=n8PsoE - fI7/q6r3UBtnd9bOynzqROT5SwDn/vctQeEYk79CsvhPmpFED/kb7+ojla6+ITBgvyRQMnBMxJwmk - RjvQsvnJD7z/kyB0xJJzWPvcwS8TTJZ0oBUEUJyDmRSzDDTyOGgTWLpxD7QLoWi8WS49/tWUGkub2 - hiGw06hIjc4=; + s=sel; h=From:From; bh=/Ab0giHZitYQbDhFszoqQRUkgqueaX9zatJttIU/plc=; + t=T; x=T+10; b=bbbb; Received: from CALLER by myhost.test.ex with local (Exim x.yz) (envelope-from ) id 10HmaZ-0005vi-00 diff --git a/test/runtest b/test/runtest index 63e6e11ea..0dc3cca73 100755 --- a/test/runtest +++ b/test/runtest @@ -1210,6 +1210,21 @@ RESET_AFTER_EXTRA_LINE_READ: s/(TLS error on connection [^:]*: error:)[0-9A-F]{8}(:system library):(?:fopen|func\(4095\)):(No such file or directory)$/$1xxxxxxxx$2:fopen:$3/; s/(DANE attempt failed.*error:)[0-9A-F]{8}(:SSL routines:)(ssl3_get_server_certificate|tls_process_server_certificate|CONNECT_CR_CERT)(?=:certificate verify failed$)/$1xxxxxxxx$2ssl3_get_server_certificate/; s/(DKIM: validation error: )error:[0-9A-F]{8}:rsa routines:(?:(?i)int_rsa_verify|CRYPTO_internal):(?:bad signature|algorithm mismatch)$/$1Public key signature verification has failed./; + + # DKIM timestamps + s/(DKIM: d=.*) t=([0-9]*) x=([0-9]*)(?{ return $3 - $2; }) /$1 t=T x=T+$^R /; + } + + # ======== mail ======== + + elsif ($is_mail) + { + # DKIM timestamps + if ( /^\s+t=[0-9]*; x=[0-9]*; b=[A-Za-z0-9+\/]+$/ ) { + s/^(\s+)t=([0-9]*); x=([0-9]*);(?{ return $3 - $2; }) b=[A-Za-z0-9+\/]+$/$1t=T; x=T+$^R; b=bbbb;/; + ; + ; + } } # ======== All files other than stderr ======== @@ -1569,7 +1584,7 @@ $munges = 'optional_config' => { 'stdout' => '/^( - dkim_(canon|domain|private_key|selector|sign_headers|strict|hash|identity) + dkim_(canon|domain|private_key|selector|sign_headers|strict|hash|identity|timestamps) |gnutls_require_(kx|mac|protocols) |hosts_(requ(est|ire)|try)_(dane|ocsp) |dane_require_tls_ciphers diff --git a/test/scripts/4500-DKIM/4520 b/test/scripts/4500-DKIM/4520 index f7fafb3e4..8e60f4bec 100644 --- a/test/scripts/4500-DKIM/4520 +++ b/test/scripts/4500-DKIM/4520 @@ -11,8 +11,8 @@ From: second@example.com content **** # -# single header, oversigned -exim -DOPT=From:From -odf b@test.ex +# single header, oversigned, with timestamps +exim -DOPT=From:From -DTIMES=10 -odf b@test.ex From: nobody@example.com content -- 2.25.1