Hi all.

In OpenSMTPD, the semi-unofficial filter-dkimsign filter provides basic
DKIM mail signing. However, the filter signs all mails it sees regardless
of user authentication. The standard configuration is to only enable this
filter on a Unix domain socket (or on a private submission port not used
for incoming mails, or requires authentication on a public port, etc).

However, often it's desirable to support DKIM signing for logged-in users
from the public Internet via a standard port, too. The following patches
introduce a new option "-u", when enabled, only mails from authenticated
users are signed, mails from unauthenticated users are passed unmodified,
making filter-dkimsign more useful simple option for servers with simple
needs.

Hopefully I did everything correctly, please review the patches attached
in the thread, one for for filter-dkimsign, another for libopensmtpd.
Although both codebases are not strictly an official part of OpenSMTPD, 
the patches may be of general interest, so I CCed both the mailing list
and the original author.

Note that these patch must be applied on top of the latest development
versions of libopensmtpd and filter-dkimsign at https://imperialat.at/
because it requires the field "username" in struct osmtpd_ctx, which has
not appeared in any stable releases to my best knowledge. Furthermore,
libopensmtpd itself must be patched first (see thread reply for patch)
due to a pre-existing bug.

I was unable to find the anonymous CVS access to the server, so for
reference my copy was obtained via:

wget --no-parent -r https://imperialat.at/dev/libopensmtpd/
wget --no-parent -r https://imperialat.at/dev/filter-dkimsign/

Cheers,
Tom Li

---
 filter-dkimsign.8 |  6 ++++++
 main.c            | 34 ++++++++++++++++++++++++++--------
 2 files changed, 32 insertions(+), 8 deletions(-)

diff --git a/filter-dkimsign.8 b/filter-dkimsign.8
index 024829c..cdffc10 100644
--- a/filter-dkimsign.8
+++ b/filter-dkimsign.8
@@ -88,6 +88,12 @@ The selector within the _domainkey subdomain of
 where the public key can be found.
 .It Fl t
 Add the time of signing to the dkim header.
+.It Fl u
+Only sign mails from authenticated users. This option should only
+be used with TCP/IP, not a Unix domain socket, since all submitted
+mails to the latter source are unauthenticated. If mail signing of
+both sources is desired, use two separate filter definitions for
+each.
 .It Fl x Ar seconds
 Add the amount of
 .Ar seconds
diff --git a/main.c b/main.c
index 2961793..d6d7211 100644
--- a/main.c
+++ b/main.c
@@ -86,6 +86,7 @@ static int canonbody = CANON_SIMPLE;
 static int addtime = 0;
 static long long addexpire = 0;
 static int addheaders = 0;
+static int authonly = 0;
 
 static char **domain = NULL;
 static size_t ndomains = 0;
@@ -110,6 +111,7 @@ void dkim_message_free(struct osmtpd_ctx *, void *);
 void dkim_parse_header(struct dkim_message *, char *, int);
 void dkim_parse_body(struct dkim_message *, char *);
 void dkim_sign(struct osmtpd_ctx *);
+void dkim_append_original(struct osmtpd_ctx *);
 int dkim_signature_printheader(struct dkim_message *, const char *);
 int dkim_signature_printf(struct dkim_message *, char *, ...)
        __attribute__((__format__ (printf, 2, 3)));
@@ -128,7 +130,7 @@ main(int argc, char *argv[])
        ssize_t linelen;
        const char *errstr;
 
-       while ((ch = getopt(argc, argv, "a:c:D:d:h:k:s:tx:z")) != -1) {
+       while ((ch = getopt(argc, argv, "a:c:D:d:h:k:s:tux:z")) != -1) {
                switch (ch) {
                case 'a':
                        if (strncmp(optarg, "rsa-", 4) == 0) {
@@ -208,6 +210,9 @@ main(int argc, char *argv[])
                case 't':
                        addtime = 1;
                        break;
+               case 'u':
+                       authonly = 1;
+                       break;
                case 'x':
                        addexpire = strtonum(optarg, 1, INT64_MAX, &errstr);
                        if (addexpire == 0)
@@ -238,6 +243,7 @@ main(int argc, char *argv[])
        osmtpd_register_filter_dataline(dkim_dataline);
        osmtpd_register_filter_commit(dkim_commit);
        osmtpd_local_message(dkim_message_new, dkim_message_free);
+       osmtpd_need(OSMTPD_NEED_USERNAME);
        osmtpd_run();
 
        return 0;
@@ -271,7 +277,10 @@ dkim_dataline(struct osmtpd_ctx *ctx, const char *line)
                dkim_errx(message, "Couldn't write to tempfile");
 
        if (line[0] == '.' && line[1] =='\0') {
-               dkim_sign(ctx);
+               if (ctx->username || !authonly)
+                       dkim_sign(ctx);
+               else
+                       dkim_append_original(ctx);
        } else if (linelen !=  0 && message->parsing_headers) {
                if (line[0] == '.')
                        line++;
@@ -750,17 +759,26 @@ dkim_sign(struct osmtpd_ctx *ctx)
                osmtpd_filter_dataline(ctx, "%s", tmp);
                tmp = tmp2 + 2;
        }
-       tmp = NULL;
-       linelen = 0;
+       dkim_append_original(ctx);
+       return;
+fail:
+       osmtpd_filter_dataline(ctx, ".");
+}
+
+void
+dkim_append_original(struct osmtpd_ctx *ctx)
+{
+       char *tmp = NULL;
+       size_t linelen = 0;
+       struct dkim_message *message = ctx->local_message;
+       ssize_t i;
+
        rewind(message->origf);
        while ((i = getline(&tmp, &linelen, message->origf)) != -1) {
                tmp[i - 1] = '\0';
                osmtpd_filter_dataline(ctx, "%s", tmp);
        }
        free(tmp);
-       return;
-fail:
-       osmtpd_filter_dataline(ctx, ".");
 }
 
 int
@@ -960,7 +978,7 @@ dkim_signature_need(struct dkim_message *message, size_t 
len)
 __dead void
 usage(void)
 {
-       fprintf(stderr, "usage: filter-dkimsign [-tz] [-a signalg] "
+       fprintf(stderr, "usage: filter-dkimsign [-tuz] [-a signalg] "
            "[-c canonicalization] \n    [-h headerfields]"
            "[-x seconds] -D file -d domain -k keyfile -s selector\n");
        exit(1);
-- 
2.46.1

Reply via email to