EXTERNAL authenticator
authorJeremy Harris <jgh146exb@wizmail.org>
Sat, 5 Jan 2019 20:40:08 +0000 (20:40 +0000)
committerJeremy Harris <jgh146exb@wizmail.org>
Sat, 5 Jan 2019 20:46:13 +0000 (20:46 +0000)
21 files changed:
doc/doc-docbook/spec.xfpt
doc/doc-txt/NewStuff
doc/doc-txt/OptionLists.txt
src/OS/Makefile-Base
src/scripts/MakeLinks
src/src/EDITME
src/src/auths/Makefile
src/src/auths/external.c [new file with mode: 0644]
src/src/auths/external.h [new file with mode: 0644]
src/src/config.h.defaults
src/src/drtables.c
test/confs/3720 [new file with mode: 0644]
test/confs/3721 [new symlink]
test/log/3720 [new file with mode: 0644]
test/log/3721 [new file with mode: 0644]
test/scripts/3720-external-auth-GnuTLS/3720 [new file with mode: 0644]
test/scripts/3720-external-auth-GnuTLS/REQUIRES [new file with mode: 0644]
test/scripts/3721-external-auth-OpenSSL/3721 [new file with mode: 0644]
test/scripts/3721-external-auth-OpenSSL/REQUIRES [new file with mode: 0644]
test/stdout/3720 [new file with mode: 0644]
test/stdout/3721 [new file with mode: 0644]

index c8f5a600b6f1aee05903626e123ad456cde00daf..f2902dc959863fbbbfc30fcf08af06204e5b1ba4 100644 (file)
@@ -26009,6 +26009,7 @@ included by setting
 AUTH_CRAM_MD5=yes
 AUTH_CYRUS_SASL=yes
 AUTH_DOVECOT=yes
 AUTH_CRAM_MD5=yes
 AUTH_CYRUS_SASL=yes
 AUTH_DOVECOT=yes
+AUTH_EXTERNAL=yes
 AUTH_GSASL=yes
 AUTH_HEIMDAL_GSSAPI=yes
 AUTH_PLAINTEXT=yes
 AUTH_GSASL=yes
 AUTH_HEIMDAL_GSSAPI=yes
 AUTH_PLAINTEXT=yes
@@ -26020,15 +26021,20 @@ authentication mechanism (RFC 2195), and the second provides an interface to
 the Cyrus SASL authentication library.
 The third is an interface to Dovecot's authentication system, delegating the
 work via a socket interface.
 the Cyrus SASL authentication library.
 The third is an interface to Dovecot's authentication system, delegating the
 work via a socket interface.
-The fourth provides an interface to the GNU SASL authentication library, which
+.new
+The fourth provides for negotiation of authentication done via non-SMTP means,
+as defined by RFC 4422 Appendix A.
+.wen
+The fifth provides an interface to the GNU SASL authentication library, which
 provides mechanisms but typically not data sources.
 provides mechanisms but typically not data sources.
-The fifth provides direct access to Heimdal GSSAPI, geared for Kerberos, but
+The sixth provides direct access to Heimdal GSSAPI, geared for Kerberos, but
 supporting setting a server keytab.
 supporting setting a server keytab.
-The sixth can be configured to support
+The seventh can be configured to support
 the PLAIN authentication mechanism (RFC 2595) or the LOGIN mechanism, which is
 the PLAIN authentication mechanism (RFC 2595) or the LOGIN mechanism, which is
-not formally documented, but used by several MUAs. The seventh authenticator
+not formally documented, but used by several MUAs.
+The eighth authenticator
 supports Microsoft's &'Secure Password Authentication'& mechanism.
 supports Microsoft's &'Secure Password Authentication'& mechanism.
-The eighth is an Exim authenticator but not an SMTP one;
+The last is an Exim authenticator but not an SMTP one;
 instead it can use information from a TLS negotiation.
 
 The authenticators are configured using the same syntax as other drivers (see
 instead it can use information from a TLS negotiation.
 
 The authenticators are configured using the same syntax as other drivers (see
@@ -27260,6 +27266,143 @@ msn:
 
 
 
 
 
 
+. ////////////////////////////////////////////////////////////////////////////
+. ////////////////////////////////////////////////////////////////////////////
+
+.chapter "The external authenticator" "CHAPexternauth"
+.scindex IIDexternauth1 "&(external)& authenticator"
+.scindex IIDexternauth2 "authenticators" "&(external)&"
+.cindex "authentication" "Client Certificate"
+.cindex "authentication" "X509"
+.cindex "Certificate-based authentication"
+The &(external)& authenticator provides support for
+authentication based on non-SMTP information.
+The specification is in RFC 4422 Appendix A
+(&url(https://tools.ietf.org/html/rfc4422)).
+It is only a transport and negotiation mechanism;
+the process of authentication is entirely controlled
+by the server configuration.
+
+The client presents an identity in-clear.
+It is probably wise for a server to only advertise,
+and for clients to only attempt,
+this authentication method on a secure (eg. under TLS) connection.
+
+One possible use, compatible with the
+K-9 Mail Andoid client (&url(https://k9mail.github.io/)),
+is for using X509 client certificates.
+
+It thus overlaps in function with the TLS authenticator
+(see &<<CHAPtlsauth>>&)
+but is a full SMTP SASL authenticator
+rather than being implicit for TLS-connection carried
+client certificates only.
+
+The examples and discussion in this chapter assume that 
+client-certificate authentication is being done.
+
+The client must present a certificate,
+for which it must have been requested via the
+&%tls_verify_hosts%& or &%tls_try_verify_hosts%& main options
+(see &<<CHAPTLS>>&).
+For authentication to be effective the certificate should be
+verifiable against a trust-anchor certificate known to the server.
+
+.section "External options" "SECTexternsoptions"
+.cindex "options" "&(external)& authenticator (server)"
+The &(external)& authenticator has two server options:
+
+.option server_param2 external string&!! unset
+.option server_param3 external string&!! unset
+.cindex "variables (&$auth1$& &$auth2$& etc)" "in &(external)& authenticator"
+These options are expanded before the &%server_condition%& option
+and the result are placed in &$auth2$& and &$auth3$& resectively.
+If the expansion is forced to fail, authentication fails. Any other expansion
+failure causes a temporary error code to be returned.
+
+They can be used to clarify the coding of a complex &%server_condition%&.
+
+.section "Using external in a server" "SECTexternserver"
+.cindex "AUTH" "in &(external)& authenticator"
+.cindex "numerical variables (&$1$& &$2$& etc)" &&&
+        "in &(external)& authenticator"
+.vindex "&$auth1$&, &$auth2$&, etc"
+.cindex "base64 encoding" "in &(external)& authenticator"
+
+When running as a server, &(external)& performs the authentication test by
+expanding a string. The data sent by the client with the AUTH command, or in
+response to subsequent prompts, is base64 encoded, and so may contain any byte
+values when decoded. The decoded value is treated as
+an identity for authentication and
+placed in the expansion variable &$auth1$&.
+
+For compatibility with previous releases of Exim, the value is also placed in
+the expansion variable &$1$&. However, the use of this
+variable for this purpose is now deprecated, as it can lead to confusion in
+string expansions that also use them for other things.
+
+.vindex "&$authenticated_id$&"
+Once an identity has been received,
+&%server_condition%& is expanded. If the expansion is forced to fail,
+authentication fails. Any other expansion failure causes a temporary error code
+to be returned. If the result of a successful expansion is an empty string,
+&"0"&, &"no"&, or &"false"&, authentication fails. If the result of the
+expansion is &"1"&, &"yes"&, or &"true"&, authentication succeeds and the
+generic &%server_set_id%& option is expanded and saved in &$authenticated_id$&.
+For any other result, a temporary error code is returned, with the expanded
+string as the error text.
+
+Example:
+.code
+ext_ccert_san_mail:
+  driver =            external
+  public_name =       EXTERNAL
+
+  server_advertise_condition = $tls_in_certificate_verified
+  server_param2 =     ${certextract {subj_altname,mail,>:} \
+                                    {$tls_in_peercert}}
+  server_condition =  ${if forany {$auth2} \
+                            {eq {$item}{$auth1}}}
+  server_set_id =     $auth1
+.endd
+This accepts a client certificate that is verifiable against any
+of your configured trust-anchors
+(which usually means the full set of public CAs)
+and which has a mail-SAN matching the claimed identity sent by the client.
+
+Note that, up to TLS1.2, the client cert is on the wire in-clear, including the SAN,
+The account name is therefore guessable by an opponent.
+TLS 1.3 protects both server and client certificates, and is not vulnerable
+in this way.
+Likewise, a traditional plaintext SMTP AUTH done inside TLS is not.
+
+
+.section "Using external in a client" "SECTexternclient"
+.cindex "options" "&(external)& authenticator (client)"
+The &(external)& authenticator has one client option:
+
+.option client_send external string&!! unset
+This option is expanded and sent with the AUTH command as the
+identity being asserted.
+
+Example:
+.code
+ext_ccert:
+  driver =      external
+  public_name = EXTERNAL
+
+  client_condition = ${if !eq{$tls_out_cipher}{}}
+  client_send = myaccount@smarthost.example.net
+.endd
+
+
+.ecindex IIDexternauth1
+.ecindex IIDexternauth2
+
+
+
+
+
 . ////////////////////////////////////////////////////////////////////////////
 . ////////////////////////////////////////////////////////////////////////////
 
 . ////////////////////////////////////////////////////////////////////////////
 . ////////////////////////////////////////////////////////////////////////////
 
index fb336b8af2413fa056d587d7e644986dc27cc507..b2a61d43cea161d4f4e9346eeca366d4911acefc 100644 (file)
@@ -6,6 +6,11 @@ Before a formal release, there may be quite a lot of detail so that people can
 test from the snapshots or the Git before the documentation is updated. Once
 the documentation is updated, this file is reduced to a short list.
 
 test from the snapshots or the Git before the documentation is updated. Once
 the documentation is updated, this file is reduced to a short list.
 
+Version 4.92
+--------------
+
+ 1. An "external" authenticator, per RFC 4422 Appendix A.
+
 Version 4.92
 --------------
 
 Version 4.92
 --------------
 
index fec47946a384d288fc0d60f347186e183d9c9848..5586e7d347a699ddbfa096aafe558850e49fafc7 100644 (file)
@@ -132,6 +132,7 @@ client_ignore_invalid_base64         boolean         false         plaintext
 client_name                          string*         +             cram_md5          3.10
 client_secret                        string*         unset         cram_md5          3.10
 client_send                          string*         unset         plaintext         3.10
 client_name                          string*         +             cram_md5          3.10
 client_secret                        string*         unset         cram_md5          3.10
 client_send                          string*         unset         plaintext         3.10
+client_send                          string*         unset         external (auth)   4.93
 command                              string*         unset         lmtp              3.20
                                                      unset         pipe
                                                      unset         queryprogram      4.00
 command                              string*         unset         lmtp              3.20
                                                      unset         pipe
                                                      unset         queryprogram      4.00
@@ -498,6 +499,9 @@ server_keytab                        string*         unset         heimdal_gssap
 server_mail_auth_condition           string*         unset         authenticators    3.22
 server_mech                          string          public_name   cyrus_sasl,gsasl  4.43 (cyrus-only) 4.80 (others)
 server_password                      string          unset         gsasl             4.80
 server_mail_auth_condition           string*         unset         authenticators    3.22
 server_mech                          string          public_name   cyrus_sasl,gsasl  4.43 (cyrus-only) 4.80 (others)
 server_password                      string          unset         gsasl             4.80
+server_param1                       string*         unset         tls (auth)        4.86
+server_param2                       string*         unset         tls (auth)        4.86 (tls-only) 4.93 (external)
+server_param3                       string*         unset         tls (auth)        4.86 (tls-only) 4.93 (external)
 server_prompts                       string*         unset         plaintext         3.10
 server_realm                         string          unset         cyrus_sasl,gsasl  4.43 (cyrus-only) 4.80 (others)
 server_scram_iter                    string*         unset         gsasl             4.80
 server_prompts                       string*         unset         plaintext         3.10
 server_realm                         string          unset         cyrus_sasl,gsasl  4.43 (cyrus-only) 4.80 (others)
 server_scram_iter                    string*         unset         gsasl             4.80
index fed3134cffde98812477f093147abacfa2e6cdd9..79bec063b6157e9009b0e39824bb48fbaee2bb4c 100644 (file)
@@ -135,7 +135,7 @@ OBJ_MACRO = macro_predef.o \
        macro-smtp.o macro-accept.o macro-dnslookup.o macro-ipliteral.o macro-iplookup.o \
        macro-manualroute.o macro-queryprogram.o macro-redirect.o \
        macro-auth-spa.o macro-cram_md5.o macro-cyrus_sasl.o macro-dovecot.o macro-gsasl_exim.o \
        macro-smtp.o macro-accept.o macro-dnslookup.o macro-ipliteral.o macro-iplookup.o \
        macro-manualroute.o macro-queryprogram.o macro-redirect.o \
        macro-auth-spa.o macro-cram_md5.o macro-cyrus_sasl.o macro-dovecot.o macro-gsasl_exim.o \
-       macro-heimdal_gssapi.o macro-plaintext.o macro-spa.o macro-authtls.o \
+       macro-heimdal_gssapi.o macro-plaintext.o macro-spa.o macro-authtls.o macro-external.o \
        macro-dkim.o macro-malware.o macro-signing.o
 
 $(OBJ_MACRO):  $(MACRO_HSRC)
        macro-dkim.o macro-malware.o macro-signing.o
 
 $(OBJ_MACRO):  $(MACRO_HSRC)
@@ -212,6 +212,9 @@ macro-cyrus_sasl.o :        auths/cyrus_sasl.c
 macro-dovecot.o:       auths/dovecot.c
        @echo "$(CC) -DMACRO_PREDEF auths/dovecot.c"
        $(FE)$(CC) -c $(CFLAGS) -DMACRO_PREDEF $(INCLUDE) -o $@ auths/dovecot.c
 macro-dovecot.o:       auths/dovecot.c
        @echo "$(CC) -DMACRO_PREDEF auths/dovecot.c"
        $(FE)$(CC) -c $(CFLAGS) -DMACRO_PREDEF $(INCLUDE) -o $@ auths/dovecot.c
+macro-external.o:      auths/external.c
+       @echo "$(CC) -DMACRO_PREDEF auths/external.c"
+       $(FE)$(CC) -c $(CFLAGS) -DMACRO_PREDEF $(INCLUDE) -o $@ auths/external.c
 macro-gsasl_exim.o :   auths/gsasl_exim.c
        @echo "$(CC) -DMACRO_PREDEF auths/gsasl_exim.c"
        $(FE)$(CC) -c $(CFLAGS) -DMACRO_PREDEF $(INCLUDE) -o $@ auths/gsasl_exim.c
 macro-gsasl_exim.o :   auths/gsasl_exim.c
        @echo "$(CC) -DMACRO_PREDEF auths/gsasl_exim.c"
        $(FE)$(CC) -c $(CFLAGS) -DMACRO_PREDEF $(INCLUDE) -o $@ auths/gsasl_exim.c
index 08f4ff1f8d01a56f27d41694b61d772f663aee27..f69bde437d59645d570e8d5f42cfc16a83c8844b 100755 (executable)
@@ -75,7 +75,7 @@ for f in README Makefile call_pam.c call_pwcheck.c \
   gsasl_exim.h get_data.c get_no64_data.c heimdal_gssapi.c heimdal_gssapi.h \
   md5.c xtextencode.c xtextdecode.c cram_md5.c cram_md5.h plaintext.c plaintext.h \
   pwcheck.c pwcheck.h auth-spa.c auth-spa.h dovecot.c dovecot.h sha1.c spa.c \
   gsasl_exim.h get_data.c get_no64_data.c heimdal_gssapi.c heimdal_gssapi.h \
   md5.c xtextencode.c xtextdecode.c cram_md5.c cram_md5.h plaintext.c plaintext.h \
   pwcheck.c pwcheck.h auth-spa.c auth-spa.h dovecot.c dovecot.h sha1.c spa.c \
-  spa.h tls.c tls.h
+  spa.h tls.c tls.h external.c external.h
 do
   ln -s ../../src/auths/$f $f
 done
 do
   ln -s ../../src/auths/$f $f
 done
index cbb080545f125d96c162ce626ad9f8999ae408df..15360db9acd39eed61910e84ba54c3164ce78459 100644 (file)
@@ -643,6 +643,7 @@ FIXED_NEVER_USERS=root
 # AUTH_CRAM_MD5=yes
 # AUTH_CYRUS_SASL=yes
 # AUTH_DOVECOT=yes
 # AUTH_CRAM_MD5=yes
 # AUTH_CYRUS_SASL=yes
 # AUTH_DOVECOT=yes
+# AUTH_EXTERNAL=yes
 # AUTH_GSASL=yes
 # AUTH_GSASL_PC=libgsasl
 # AUTH_HEIMDAL_GSSAPI=yes
 # AUTH_GSASL=yes
 # AUTH_GSASL_PC=libgsasl
 # AUTH_HEIMDAL_GSSAPI=yes
index 62ce9d0a965846ae2756fe1d804d2af0d984b2c8..402f1417a8d2d31920bf566025ffbf3cca001de8 100644 (file)
@@ -7,7 +7,7 @@
 
 OBJ = auth-spa.o call_pam.o call_pwcheck.o \
       call_radius.o check_serv_cond.o cram_md5.o cyrus_sasl.o dovecot.o \
 
 OBJ = auth-spa.o call_pam.o call_pwcheck.o \
       call_radius.o check_serv_cond.o cram_md5.o cyrus_sasl.o dovecot.o \
-      get_data.o get_no64_data.o gsasl_exim.o heimdal_gssapi.o \
+      external.o get_data.o get_no64_data.o gsasl_exim.o heimdal_gssapi.o \
       md5.o plaintext.o pwcheck.o \
       spa.o tls.o xtextdecode.o xtextencode.o
 
       md5.o plaintext.o pwcheck.o \
       spa.o tls.o xtextdecode.o xtextencode.o
 
@@ -36,6 +36,7 @@ xtextencode.o:      $(HDRS) xtextencode.c
 cram_md5.o:         $(HDRS) cram_md5.c cram_md5.h
 cyrus_sasl.o:       $(HDRS) cyrus_sasl.c cyrus_sasl.h
 dovecot.o:          $(HDRS) dovecot.c dovecot.h
 cram_md5.o:         $(HDRS) cram_md5.c cram_md5.h
 cyrus_sasl.o:       $(HDRS) cyrus_sasl.c cyrus_sasl.h
 dovecot.o:          $(HDRS) dovecot.c dovecot.h
+external.o:         $(HDRS) external.c external.h
 gsasl_exim.o:       $(HDRS) gsasl_exim.c gsasl_exim.h
 heimdal_gssapi.o:   $(HDRS) heimdal_gssapi.c heimdal_gssapi.h
 plaintext.o:        $(HDRS) plaintext.c plaintext.h
 gsasl_exim.o:       $(HDRS) gsasl_exim.c gsasl_exim.h
 heimdal_gssapi.o:   $(HDRS) heimdal_gssapi.c heimdal_gssapi.h
 plaintext.o:        $(HDRS) plaintext.c plaintext.h
diff --git a/src/src/auths/external.c b/src/src/auths/external.c
new file mode 100644 (file)
index 0000000..10e1366
--- /dev/null
@@ -0,0 +1,158 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* Copyright (c) Jeremy Harris 2019 */
+/* See the file NOTICE for conditions of use and distribution. */
+
+/* This file provides an Exim authenticator driver for
+a server to verify a client SSL certificate, using the EXTERNAL
+method defined in RFC 4422 Appendix A.
+*/
+
+
+#include "../exim.h"
+#include "external.h"
+
+/* Options specific to the external authentication mechanism. */
+
+optionlist auth_external_options[] = {
+  { "client_send",     opt_stringptr,
+      (void *)(offsetof(auth_external_options_block, client_send)) },
+  { "server_param2",   opt_stringptr,
+      (void *)(offsetof(auth_external_options_block, server_param2)) },
+  { "server_param3",       opt_stringptr,
+      (void *)(offsetof(auth_external_options_block, server_param3)) },
+};
+
+/* Size of the options list. An extern variable has to be used so that its
+address can appear in the tables drtables.c. */
+
+int auth_external_options_count = nelem(auth_external_options);
+
+/* Default private options block for the authentication method. */
+
+auth_external_options_block auth_external_option_defaults = {
+    .server_param2 = NULL,
+    .server_param3 = NULL,
+
+    .client_send = NULL,
+};
+
+
+#ifdef MACRO_PREDEF
+
+/* Dummy values */
+void auth_external_init(auth_instance *ablock) {}
+int auth_external_server(auth_instance *ablock, uschar *data) {return 0;}
+int auth_external_client(auth_instance *ablock, void * sx,
+  int timeout, uschar *buffer, int buffsize) {return 0;}
+
+#else   /*!MACRO_PREDEF*/
+
+
+
+
+/*************************************************
+*          Initialization entry point            *
+*************************************************/
+
+/* Called for each instance, after its options have been read, to
+enable consistency checks to be done, or anything else that needs
+to be set up. */
+
+void
+auth_external_init(auth_instance *ablock)
+{
+auth_external_options_block * ob = (auth_external_options_block *)ablock->options_block;
+if (!ablock->public_name) ablock->public_name = ablock->name;
+if (ablock->server_condition) ablock->server = TRUE;
+if (ob->client_send) ablock->client = TRUE;
+}
+
+
+
+/*************************************************
+*             Server entry point                 *
+*************************************************/
+
+/* For interface, see auths/README */
+
+int
+auth_external_server(auth_instance * ablock, uschar * data)
+{
+auth_external_options_block * ob = (auth_external_options_block *)ablock->options_block;
+int rc;
+
+/* If data was supplied on the AUTH command, decode it, and split it up into
+multiple items at binary zeros. The strings are put into $auth1, $auth2, etc,
+up to a maximum. To retain backwards compatibility, they are also put int $1,
+$2, etc. If the data consists of the string "=" it indicates a single, empty
+string. */
+
+if (*data)
+  if ((rc = auth_read_input(data)) != OK)
+    return rc;
+
+/* Now go through the list of prompt strings. Skip over any whose data has
+already been provided as part of the AUTH command. For the rest, send them
+out as prompts, and get a data item back. If the data item is "*", abandon the
+authentication attempt. Otherwise, split it into items as above. */
+
+if (expand_nmax == 0)  /* skip if rxd data */
+  if ((rc = auth_prompt(CUS"")) != OK)
+    return rc;
+
+if (ob->server_param2)
+  {
+  uschar * s = expand_string(ob->server_param2);
+  auth_vars[expand_nmax] = s;
+  expand_nstring[++expand_nmax] = s;
+  expand_nlength[expand_nmax] = Ustrlen(s);
+  if (ob->server_param3)
+    {
+    s = expand_string(ob->server_param3);
+    auth_vars[expand_nmax] = s;
+    expand_nstring[++expand_nmax] = s;
+    expand_nlength[expand_nmax] = Ustrlen(s);
+    }
+  }
+
+return auth_check_serv_cond(ablock);
+}
+
+
+
+/*************************************************
+*              Client entry point                *
+*************************************************/
+
+/* For interface, see auths/README */
+
+int
+auth_external_client(
+  auth_instance *ablock,                 /* authenticator block */
+  void * sx,                            /* smtp connextion */
+  int timeout,                           /* command timeout */
+  uschar *buffer,                        /* buffer for reading response */
+  int buffsize)                          /* size of buffer */
+{
+auth_external_options_block *ob =
+  (auth_external_options_block *)(ablock->options_block);
+const uschar * text = ob->client_send;
+int rc;
+
+/* We output an AUTH command with one expanded argument, the client_send option */
+
+if ((rc = auth_client_item(sx, ablock, &text, AUTH_ITEM_FIRST | AUTH_ITEM_LAST,
+      timeout, buffer, buffsize)) != OK)
+  return rc == DEFER ? FAIL : rc;
+
+if (text) auth_vars[0] = string_copy(text);
+return OK;
+}
+
+
+
+#endif   /*!MACRO_PREDEF*/
+/* End of external.c */
diff --git a/src/src/auths/external.h b/src/src/auths/external.h
new file mode 100644 (file)
index 0000000..7d43650
--- /dev/null
@@ -0,0 +1,32 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* Copyright (c) Jeremy Harris 2019 */
+/* See the file NOTICE for conditions of use and distribution. */
+
+/* Private structure for the private options. */
+
+typedef struct {
+  uschar * server_param2;
+  uschar * server_param3;
+
+  uschar * client_send;
+} auth_external_options_block;
+
+/* Data for reading the private options. */
+
+extern optionlist auth_external_options[];
+extern int auth_external_options_count;
+
+/* Block containing default values. */
+
+extern auth_external_options_block auth_external_option_defaults;
+
+/* The entry points for the mechanism */
+
+extern void auth_external_init(auth_instance *);
+extern int auth_external_server(auth_instance *, uschar *);
+extern int auth_external_client(auth_instance *, void *, int, uschar *, int);
+
+/* End of external.h */
index 7c2e534f3cc78322966a6825e190e1987e8ff4dd..74ec3f4df68ecfb46af269d357f240793dab8960 100644 (file)
@@ -24,6 +24,7 @@ Do not put spaces between # and the 'define'.
 #define AUTH_CRAM_MD5
 #define AUTH_CYRUS_SASL
 #define AUTH_DOVECOT
 #define AUTH_CRAM_MD5
 #define AUTH_CYRUS_SASL
 #define AUTH_DOVECOT
+#define AUTH_EXTERNAL
 #define AUTH_GSASL
 #define AUTH_HEIMDAL_GSSAPI
 #define AUTH_PLAINTEXT
 #define AUTH_GSASL
 #define AUTH_HEIMDAL_GSSAPI
 #define AUTH_PLAINTEXT
index cd12dd1da8dc8350463e7a0741966b678b9ec1ec..54d03edab455878717585275f49e5cf267924883 100644 (file)
@@ -35,6 +35,10 @@ set to NULL for those that are not compiled into the binary. */
 #include "auths/dovecot.h"
 #endif
 
 #include "auths/dovecot.h"
 #endif
 
+#ifdef AUTH_EXTERNAL
+#include "auths/external.h"
+#endif
+
 #ifdef AUTH_GSASL
 #include "auths/gsasl_exim.h"
 #endif
 #ifdef AUTH_GSASL
 #include "auths/gsasl_exim.h"
 #endif
@@ -101,6 +105,20 @@ auth_info auths_available[] = {
   },
 #endif
 
   },
 #endif
 
+#ifdef AUTH_EXTERNAL
+  {
+  .driver_name =       US"external",
+  .options =           auth_external_options,
+  .options_count =     &auth_external_options_count,
+  .options_block =     &auth_external_option_defaults,
+  .options_len =       sizeof(auth_external_options_block),
+  .init =              auth_external_init,
+  .servercode =                auth_external_server,
+  .clientcode =                auth_external_client,
+  .version_report =    NULL
+  },
+#endif
+
 #ifdef AUTH_GSASL
   {
   .driver_name =       US"gsasl",
 #ifdef AUTH_GSASL
   {
   .driver_name =       US"gsasl",
diff --git a/test/confs/3720 b/test/confs/3720
new file mode 100644 (file)
index 0000000..6d8c467
--- /dev/null
@@ -0,0 +1,93 @@
+# Exim test configuration 3720
+
+SERVER=
+
+.include DIR/aux-var/tls_conf_prefix
+
+primary_hostname = myhost.test.ex
+log_selector = +received_recipients +outgoing_port
+
+# ----- Main settings -----
+
+acl_smtp_auth = log_call
+acl_smtp_mail = check_authd
+acl_smtp_rcpt = check_authd
+acl_smtp_data = ar_header
+
+queue_only
+queue_run_in_order
+trusted_users = CALLER
+
+tls_advertise_hosts = *
+tls_certificate = DIR/aux-fixed/cert1
+
+tls_verify_hosts = *
+tls_verify_certificates = DIR/aux-fixed/cert2
+
+
+# ----- ACL -----
+
+begin acl
+
+log_call:
+  accept   logwrite = Auth ACL called, after smtp cmd "$smtp_command"
+
+check_authd:
+  deny     message = authentication required
+          !authenticated = *
+  accept
+
+ar_header:
+  accept  add_header = :at_start:${authresults {$primary_hostname}}
+
+# ----- Authentication -----
+
+begin authenticators
+
+ext_ccert_cn:
+  driver =             external
+  public_name =                EXTERNAL
+
+  server_advertise_condition = ${if eq{$tls_in_cipher}{}{no}{yes}}
+  server_param2 =      ${certextract {subject,CN} {$tls_in_peercert}}
+  server_condition =   ${if eq {$auth2}{$auth1}}
+  server_set_id =      $auth1
+  server_debug_print = +++TLS \$auth1="$auth1"
+
+  client_send =                "Phil Pennock"
+
+
+# ----- Routers -----
+
+begin routers
+
+server_r:
+  driver =     accept
+  condition =  ${if eq {server}{SERVER}}
+  transport =  file
+
+client_r1:
+  driver =     accept
+  transport =  t1
+
+
+# ----- Transports -----
+
+begin transports
+
+t1:
+  driver = smtp
+  hosts = 127.0.0.1
+  port = PORT_D
+  allow_localhost
+  tls_certificate =            DIR/aux-fixed/cert2
+  tls_verify_certificates =    DIR/aux-fixed/cert1
+  tls_verify_cert_hostnames =  :
+  hosts_try_auth =             *
+
+file:
+  driver = appendfile
+  file = DIR/test-mail/$local_part
+  user = CALLER
+
+# End
diff --git a/test/confs/3721 b/test/confs/3721
new file mode 120000 (symlink)
index 0000000..e401101
--- /dev/null
@@ -0,0 +1 @@
+3720
\ No newline at end of file
diff --git a/test/log/3720 b/test/log/3720
new file mode 100644 (file)
index 0000000..f79e756
--- /dev/null
@@ -0,0 +1,11 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= ok@test.ex U=CALLER P=local S=sss for x@y
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaX-0005vi-00 => x@y R=client_r1 T=t1 H=127.0.0.1 [127.0.0.1]:1225 X=TLS_proto_and_cipher CV=yes A=ext_ccert_cn C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 Auth ACL called, after smtp cmd "AUTH EXTERNAL UGhpbCBQZW5ub2Nr"
+1999-03-02 09:44:33 Auth ACL called, after smtp cmd "AUTH EXTERNAL UGhpbCBQZW5ub2Nr"
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= ok@test.ex H=localhost (myhost.test.ex) [127.0.0.1] P=esmtpsa X=TLS_proto_and_cipher CV=yes A=ext_ccert_cn:Phil Pennock S=sss id=E10HmaX-0005vi-00@myhost.test.ex for x@y
diff --git a/test/log/3721 b/test/log/3721
new file mode 100644 (file)
index 0000000..f79e756
--- /dev/null
@@ -0,0 +1,11 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= ok@test.ex U=CALLER P=local S=sss for x@y
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaX-0005vi-00 => x@y R=client_r1 T=t1 H=127.0.0.1 [127.0.0.1]:1225 X=TLS_proto_and_cipher CV=yes A=ext_ccert_cn C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 Auth ACL called, after smtp cmd "AUTH EXTERNAL UGhpbCBQZW5ub2Nr"
+1999-03-02 09:44:33 Auth ACL called, after smtp cmd "AUTH EXTERNAL UGhpbCBQZW5ub2Nr"
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= ok@test.ex H=localhost (myhost.test.ex) [127.0.0.1] P=esmtpsa X=TLS_proto_and_cipher CV=yes A=ext_ccert_cn:Phil Pennock S=sss id=E10HmaX-0005vi-00@myhost.test.ex for x@y
diff --git a/test/scripts/3720-external-auth-GnuTLS/3720 b/test/scripts/3720-external-auth-GnuTLS/3720
new file mode 100644 (file)
index 0000000..49d9520
--- /dev/null
@@ -0,0 +1,38 @@
+# External authentication (server & client)
+munge tls_anycipher
+#
+exim -DSERVER=server -bd -oX PORT_D
+****
+#
+#
+client-gnutls 127.0.0.1 PORT_D 127.0.0.1 DIR/aux-fixed/cert2 DIR/aux-fixed/cert2
+??? 220
+EHLO tester
+??? 250-
+??? 250-
+??? 250-
+??? 250-
+??? 250-STARTTLS
+??? 250 HELP
+STARTTLS
+??? 220
+EHLO tester
+??? 250-
+??? 250-
+??? 250-
+??? 250-
+??? 250-AUTH EXTERNAL
+??? 250 HELP
+AUTH EXTERNAL UGhpbCBQZW5ub2Nr
+??? 235
+quit
+??? 221
+****
+#
+exim -f ok@test.ex x@y
+****
+exim -q
+****
+#
+killdaemon
+no_msglog_check
diff --git a/test/scripts/3720-external-auth-GnuTLS/REQUIRES b/test/scripts/3720-external-auth-GnuTLS/REQUIRES
new file mode 100644 (file)
index 0000000..9e358e2
--- /dev/null
@@ -0,0 +1,2 @@
+authenticator external
+support GnuTLS
diff --git a/test/scripts/3721-external-auth-OpenSSL/3721 b/test/scripts/3721-external-auth-OpenSSL/3721
new file mode 100644 (file)
index 0000000..310b8d2
--- /dev/null
@@ -0,0 +1,38 @@
+# External authentication (server & client)
+munge tls_anycipher
+#
+exim -DSERVER=server -bd -oX PORT_D
+****
+#
+#
+client-ssl 127.0.0.1 PORT_D 127.0.0.1 DIR/aux-fixed/cert2 DIR/aux-fixed/cert2
+??? 220
+EHLO tester
+??? 250-
+??? 250-
+??? 250-
+??? 250-
+??? 250-STARTTLS
+??? 250 HELP
+STARTTLS
+??? 220
+EHLO tester
+??? 250-
+??? 250-
+??? 250-
+??? 250-
+??? 250-AUTH EXTERNAL
+??? 250 HELP
+AUTH EXTERNAL UGhpbCBQZW5ub2Nr
+??? 235
+quit
+??? 221
+****
+#
+exim -f ok@test.ex x@y
+****
+exim -q
+****
+#
+killdaemon
+no_msglog_check
diff --git a/test/scripts/3721-external-auth-OpenSSL/REQUIRES b/test/scripts/3721-external-auth-OpenSSL/REQUIRES
new file mode 100644 (file)
index 0000000..c0a56a2
--- /dev/null
@@ -0,0 +1,2 @@
+authenticator external
+support OpenSSL
diff --git a/test/stdout/3720 b/test/stdout/3720
new file mode 100644 (file)
index 0000000..049c87d
--- /dev/null
@@ -0,0 +1,43 @@
+Connecting to 127.0.0.1 port 1225 ... connected
+Certificate file = TESTSUITE/aux-fixed/cert2
+Key file = TESTSUITE/aux-fixed/cert2
+??? 220
+<<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> EHLO tester
+??? 250-
+<<< 250-myhost.test.ex Hello tester [127.0.0.1]
+??? 250-
+<<< 250-SIZE 52428800
+??? 250-
+<<< 250-8BITMIME
+??? 250-
+<<< 250-PIPELINING
+??? 250-STARTTLS
+<<< 250-STARTTLS
+??? 250 HELP
+<<< 250 HELP
+>>> STARTTLS
+??? 220
+<<< 220 TLS go ahead
+Attempting to start TLS
+Succeeded in starting TLS
+>>> EHLO tester
+??? 250-
+<<< 250-myhost.test.ex Hello tester [127.0.0.1]
+??? 250-
+<<< 250-SIZE 52428800
+??? 250-
+<<< 250-8BITMIME
+??? 250-
+<<< 250-PIPELINING
+??? 250-AUTH EXTERNAL
+<<< 250-AUTH EXTERNAL
+??? 250 HELP
+<<< 250 HELP
+>>> AUTH EXTERNAL UGhpbCBQZW5ub2Nr
+??? 235
+<<< 235 Authentication succeeded
+>>> quit
+??? 221
+<<< 221 myhost.test.ex closing connection
+End of script
diff --git a/test/stdout/3721 b/test/stdout/3721
new file mode 100644 (file)
index 0000000..81878f9
--- /dev/null
@@ -0,0 +1,44 @@
+Connecting to 127.0.0.1 port 1225 ... connected
+Certificate file = TESTSUITE/aux-fixed/cert2
+Key file = TESTSUITE/aux-fixed/cert2
+??? 220
+<<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> EHLO tester
+??? 250-
+<<< 250-myhost.test.ex Hello tester [127.0.0.1]
+??? 250-
+<<< 250-SIZE 52428800
+??? 250-
+<<< 250-8BITMIME
+??? 250-
+<<< 250-PIPELINING
+??? 250-STARTTLS
+<<< 250-STARTTLS
+??? 250 HELP
+<<< 250 HELP
+>>> STARTTLS
+??? 220
+<<< 220 TLS go ahead
+Attempting to start TLS
+SSL connection using ke-RSA-AES256-SHA
+Succeeded in starting TLS
+>>> EHLO tester
+??? 250-
+<<< 250-myhost.test.ex Hello tester [127.0.0.1]
+??? 250-
+<<< 250-SIZE 52428800
+??? 250-
+<<< 250-8BITMIME
+??? 250-
+<<< 250-PIPELINING
+??? 250-AUTH EXTERNAL
+<<< 250-AUTH EXTERNAL
+??? 250 HELP
+<<< 250 HELP
+>>> AUTH EXTERNAL UGhpbCBQZW5ub2Nr
+??? 235
+<<< 235 Authentication succeeded
+>>> quit
+??? 221
+<<< 221 myhost.test.ex closing connection
+End of script