[exim-cvs] Dovecot auth: inet socket. Bug 2280

Startseite
Nachricht löschen
Nachricht beantworten
Autor: Exim Git Commits Mailing List
Datum:  
To: exim-cvs
Betreff: [exim-cvs] Dovecot auth: inet socket. Bug 2280
Gitweb: https://git.exim.org/exim.git/commitdiff/7d2f2d360f5a8ac6e0055074db813c3c3cfbeeb4
Commit:     7d2f2d360f5a8ac6e0055074db813c3c3cfbeeb4
Parent:     e4a04f2a8de013abe125d9d04219343b0a32662e
Author:     Jeremy Harris <jgh146exb@???>
AuthorDate: Thu Jan 23 15:29:31 2020 +0000
Committer:  Jeremy Harris <jgh146exb@???>
CommitDate: Thu Jan 23 16:48:23 2020 +0000


    Dovecot auth: inet socket.  Bug 2280
---
 doc/doc-txt/NewStuff               |   3 +
 doc/doc-txt/experimental-spec.txt  |  30 ++++++++++
 src/src/auths/dovecot.c            | 110 ++++++++++++++++++++++---------------
 src/src/auths/dovecot.h            |   4 +-
 src/src/functions.h                |   4 +-
 src/src/ip.c                       |  12 ++--
 src/src/malware.c                  |   4 +-
 src/src/spam.c                     |   2 +-
 test/confs/{9350 => 3901}          |  32 ++++++++++-
 test/confs/9350                    |   2 +-
 test/log/3901                      |   7 +++
 test/scripts/3900-Dovecot/3901     |  38 +++++++++++++
 test/scripts/3900-Dovecot/REQUIRES |   1 +
 13 files changed, 190 insertions(+), 59 deletions(-)


diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff
index e214465..8ae95a7 100644
--- a/doc/doc-txt/NewStuff
+++ b/doc/doc-txt/NewStuff
@@ -30,6 +30,9 @@ Version 4.94
  7. Named-list definitions can now be prefixed "hide" so that "-bP" commands do
     not output the content.  Previously this could only be done on options.


+ 8. As an exerimental feature, the dovecot authenticatino driver supports inet
+    sockets.  Previously it was unix-domain sockets only.
+


 Version 4.93
 ------------
diff --git a/doc/doc-txt/experimental-spec.txt b/doc/doc-txt/experimental-spec.txt
index 2569ad3..6e47b95 100644
--- a/doc/doc-txt/experimental-spec.txt
+++ b/doc/doc-txt/experimental-spec.txt
@@ -808,6 +808,36 @@ Issues:
    hosts_require_ocsp will fail



+
+Dovecot authenticator via inet socket
+------------------------------------
+If Dovecot is configured similar to :-
+
+service auth {
+...
+#SASL
+  inet_listener {
+    name = exim
+    port = 12345
+    ssl = yes
+  }
+...
+}
+
+then an Exim authenticator can be configured :-
+
+  dovecot-plain:
+    driver =        dovecot
+    public_name =    PLAIN
+    server_socket =    dovecot_server_name 12345
+    server_tls =    true
+    server_set_id =    $auth1
+
+If the server_socket does not start with a / it is taken as a hostname (or IP);
+and a whitespace-separated port number must be given.
+
+
+
 --------------------------------------------------------------
 End of file
 --------------------------------------------------------------
diff --git a/src/src/auths/dovecot.c b/src/src/auths/dovecot.c
index c337510..9b0d437 100644
--- a/src/src/auths/dovecot.c
+++ b/src/src/auths/dovecot.c
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2004 Andrey Panin <pazke@???>
- * Copyright (c) 2006-2017 The Exim Maintainers
+ * Copyright (c) 2006-2019 The Exim Maintainers
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published
@@ -51,9 +51,8 @@ The cost is the length of an array of pointers on the stack.


 /* Options specific to the authentication mechanism. */
 optionlist auth_dovecot_options[] = {
-       { "server_socket", opt_stringptr,
-     OPT_OFF(auth_dovecot_options_block, server_socket)
-       },
+  { "server_socket", opt_stringptr, OPT_OFF(auth_dovecot_options_block, server_socket) },
+/*{ "server_tls", opt_bool, OPT_OFF(auth_dovecot_options_block, server_tls) },*/
 };


/* Size of the options list. An extern variable has to be used so that its
@@ -64,7 +63,8 @@ int auth_dovecot_options_count = nelem(auth_dovecot_options);
/* Default private options block for the authentication method. */

 auth_dovecot_options_block auth_dovecot_option_defaults = {
-       NULL,                           /* server_socket */
+    .server_socket = NULL,
+/*    .server_tls =    FALSE,*/
 };



@@ -197,7 +197,7 @@ else
C-style buffered I/O gave trouble. */

 static uschar *
-dc_gets(uschar *s, int n, int fd)
+dc_gets(uschar *s, int n, client_conn_ctx * cctx)
 {
 int p = 0;
 int count = 0;
@@ -206,8 +206,15 @@ for (;;)
   {
   if (socket_buffer_left == 0)
     {
-    if ((socket_buffer_left = read(fd, sbuffer, sizeof(sbuffer))) <= 0)
-      if (count == 0) return NULL; else break;
+    if ((socket_buffer_left =
+#ifndef DISABLE_TLS
+    cctx->tls_ctx ? tls_read(cctx->tls_ctx, sbuffer, sizeof(sbuffer)) :
+#endif
+    read(cctx->sock, sbuffer, sizeof(sbuffer))) <= 0)
+      if (count == 0)
+    return NULL;
+      else
+    break;
     p = 0;
     }


@@ -240,14 +247,15 @@ auth_dovecot_server(auth_instance * ablock, uschar * data)
 {
 auth_dovecot_options_block *ob =
        (auth_dovecot_options_block *) ablock->options_block;
-struct sockaddr_un sa;
 uschar buffer[DOVECOT_AUTH_MAXLINELEN];
 uschar *args[DOVECOT_AUTH_MAXFIELDCOUNT];
 uschar *auth_command;
 uschar *auth_extra_data = US"";
 uschar *p;
 int nargs, tmp;
-int crequid = 1, cont = 1, fd = -1, ret = DEFER;
+int crequid = 1, ret = DEFER;
+host_item host;
+client_conn_ctx cctx = {.sock = -1, .tls_ctx = NULL};
 BOOL found = FALSE, have_mech_line = FALSE;


HDEBUG(D_auth) debug_printf("dovecot authentication\n");
@@ -258,50 +266,48 @@ if (!data)
goto out;
}

-memset(&sa, 0, sizeof(sa));
-sa.sun_family = AF_UNIX;
+/*XXX timeout? */
+cctx.sock = ip_streamsocket(ob->server_socket, &auth_defer_msg, 5, &host);
+if (cctx.sock < 0)
+ goto out;

-/* This was the original code here: it is nonsense because strncpy()
-does not return an integer. I have converted this to use the function
-that formats and checks length. PH */
-
-/*
-if (strncpy(sa.sun_path, ob->server_socket, sizeof(sa.sun_path)) < 0) {
-}
-*/
-
-if (!string_format(US sa.sun_path, sizeof(sa.sun_path), "%s",
-          ob->server_socket))
+#ifdef notdef
+# ifndef DISABLE_TLS
+if (ob->server_tls)
   {
-  auth_defer_msg = US"authentication socket path too long";
-  return DEFER;
-  }
-
-auth_defer_msg = US"authentication socket connection error";
+  uschar * s;
+  smtp_connect_args conn_args = { .host = &host };
+  tls_support tls_dummy = {.sni=NULL};
+  uschar * errstr;


-if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
-  return DEFER;
-
-if (connect(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0)
-  goto out;
+  if (!tls_client_start(&cctx, &conn_args, NULL, &tls_dummy, &errstr))
+    {
+    auth_defer_msg = string_sprintf("TLS connect failed: %s", errstr);
+    goto out;
+    }
+  }
+# endif
+#endif


auth_defer_msg = US"authentication socket protocol error";

 socket_buffer_left = 0;  /* Global, used to read more than a line but return by line */
-while (cont)
+for (;;)
   {
-  if (!dc_gets(buffer, sizeof(buffer), fd))
+debug_printf("%s %d\n", __FUNCTION__, __LINE__);
+  if (!dc_gets(buffer, sizeof(buffer), &cctx))
     OUT("authentication socket read error or premature eof");
+debug_printf("%s %d\n", __FUNCTION__, __LINE__);
   p = buffer + Ustrlen(buffer) - 1;
   if (*p != '\n')
     OUT("authentication socket protocol line too long");


*p = '\0';
- HDEBUG(D_auth) debug_printf("received: %s\n", buffer);
+ HDEBUG(D_auth) debug_printf("received: '%s'\n", buffer);

nargs = strcut(buffer, args, nelem(args));

- /* HDEBUG(D_auth) debug_strcut(args, nargs, nelem(args)); */
+ HDEBUG(D_auth) debug_strcut(args, nargs, nelem(args));

   /* Code below rewritten by Kirill Miazine (km@???). Only check commands that
     Exim will need. Original code also failed if Dovecot server sent unknown
@@ -344,7 +350,7 @@ while (cont)
   else if (Ustrcmp(args[0], US"DONE") == 0)
     {
     CHECK_COMMAND("DONE", 0, 0);
-    cont = 0;
+    break;
     }
   }


@@ -399,26 +405,31 @@ auth_command = string_sprintf("VERSION\t%d\t%d\nCPID\t%d\n"
        ablock->public_name, auth_extra_data, sender_host_address,
        interface_address, data);


-if (write(fd, auth_command, Ustrlen(auth_command)) < 0)
+if ((
+#ifndef DISABLE_TLS
+    cctx.tls_ctx ? tls_write(cctx.tls_ctx, auth_command, Ustrlen(auth_command), FALSE) :
+#endif
+    write(cctx.sock, auth_command, Ustrlen(auth_command))) < 0)
   HDEBUG(D_auth) debug_printf("error sending auth_command: %s\n",
     strerror(errno));


-HDEBUG(D_auth) debug_printf("sent: %s", auth_command);
+HDEBUG(D_auth) debug_printf("sent: '%s'\n", auth_command);

while (1)
{
uschar *temp;
uschar *auth_id_pre = NULL;

-  if (!dc_gets(buffer, sizeof(buffer), fd))
+  if (!dc_gets(buffer, sizeof(buffer), &cctx))
     {
     auth_defer_msg = US"authentication socket read error or premature eof";
     goto out;
     }


buffer[Ustrlen(buffer) - 1] = 0;
- HDEBUG(D_auth) debug_printf("received: %s\n", buffer);
+ HDEBUG(D_auth) debug_printf("received: '%s'\n", buffer);
nargs = strcut(buffer, args, nelem(args));
+ HDEBUG(D_auth) debug_strcut(args, nargs, nelem(args));

   if (Uatoi(args[1]) != crequid)
     OUT("authentication socket connection id mismatch");
@@ -444,7 +455,11 @@ while (1)
     }


       temp = string_sprintf("CONT\t%d\t%s\n", crequid, data);
-      if (write(fd, temp, Ustrlen(temp)) < 0)
+      if ((
+#ifndef DISABLE_TLS
+      cctx.tls_ctx ? tls_write(cctx.tls_ctx, temp, Ustrlen(temp), FALSE) :
+#endif
+      write(cctx.sock, temp, Ustrlen(temp))) < 0)
     OUT("authentication socket write error");
       break;


@@ -480,6 +495,7 @@ while (1)
       if (!auth_id_pre)
         OUT("authentication socket protocol error, username missing");


+      auth_defer_msg = NULL;
       ret = OK;
       /* fallthrough */


@@ -490,8 +506,12 @@ while (1)

out:
/* close the socket used by dovecot */
-if (fd >= 0)
- close(fd);
+#ifndef DISABLE_TLS
+if (cctx.tls_ctx)
+ tls_close(cctx.tls_ctx, TRUE);
+#endif
+if (cctx.sock >= 0)
+ close(cctx.sock);

/* Expand server_condition as an authorization check */
return ret == OK ? auth_check_serv_cond(ablock) : ret;
diff --git a/src/src/auths/dovecot.h b/src/src/auths/dovecot.h
index ea3f04d..373f729 100644
--- a/src/src/auths/dovecot.h
+++ b/src/src/auths/dovecot.h
@@ -3,12 +3,14 @@
*************************************************/

/* Copyright (c) University of Cambridge 1995 - 2009 */
+/* Copyright (c) The Exim Maintainters 2019 */
/* See the file NOTICE for conditions of use and distribution. */

/* Private structure for the private options. */

 typedef struct {
-  uschar *server_socket;
+  uschar *    server_socket;
+  BOOL        server_tls;
 } auth_dovecot_options_block;


 /* Data for reading the private options. */
diff --git a/src/src/functions.h b/src/src/functions.h
index 57314a6..473fb87 100644
--- a/src/src/functions.h
+++ b/src/src/functions.h
@@ -287,9 +287,9 @@ extern void    ip_keepalive(int, const uschar *, BOOL);
 extern int     ip_recv(client_conn_ctx *, uschar *, int, time_t);
 extern int     ip_socket(int, int);


-extern int     ip_tcpsocket(const uschar *, uschar **, int);
+extern int     ip_tcpsocket(const uschar *, uschar **, int, host_item *);
 extern int     ip_unixsocket(const uschar *, uschar **);
-extern int     ip_streamsocket(const uschar *, uschar **, int);
+extern int     ip_streamsocket(const uschar *, uschar **, int, host_item *);


 extern int     ipv6_nmtoa(int *, uschar *);


diff --git a/src/src/ip.c b/src/src/ip.c
index bf332b1..a6b7de3 100644
--- a/src/src/ip.c
+++ b/src/src/ip.c
@@ -482,7 +482,8 @@ bad:

/*XXX TFO? */
int
-ip_tcpsocket(const uschar * hostport, uschar ** errstr, int tmo)
+ip_tcpsocket(const uschar * hostport, uschar ** errstr, int tmo,
+ host_item * connhost)
{
int scan;
uschar hostname[256];
@@ -501,7 +502,7 @@ if (scan != 3)
}

 return ip_connectedsocket(SOCK_STREAM, hostname, portlow, porthigh,
-              tmo, NULL, errstr, NULL);
+              tmo, connhost, errstr, NULL);
 }


int
@@ -534,12 +535,15 @@ return sock;
/* spec is either an absolute path (with a leading /), or
a host (name or IP) and port (whitespace-separated).
The port can be a range, dash-separated, or a single number.
+
+For a TCP socket, optionally fill in a host_item.
*/
int
-ip_streamsocket(const uschar * spec, uschar ** errstr, int tmo)
+ip_streamsocket(const uschar * spec, uschar ** errstr, int tmo,
+ host_item * connhost)
{
return *spec == '/'
- ? ip_unixsocket(spec, errstr) : ip_tcpsocket(spec, errstr, tmo);
+ ? ip_unixsocket(spec, errstr) : ip_tcpsocket(spec, errstr, tmo, connhost);
}

 /*************************************************
diff --git a/src/src/malware.c b/src/src/malware.c
index cfff9ee..a4080d0 100644
--- a/src/src/malware.c
+++ b/src/src/malware.c
@@ -654,11 +654,11 @@ if (!malware_ok)
     switch(scanent->conn)
     {
     case MC_TCP:
-      malware_daemon_ctx.sock = ip_tcpsocket(scanner_options, &errstr, 5);    break;
+      malware_daemon_ctx.sock = ip_tcpsocket(scanner_options, &errstr, 5, NULL); break;
     case MC_UNIX:
       malware_daemon_ctx.sock = ip_unixsocket(scanner_options, &errstr);    break;
     case MC_STRM:
-      malware_daemon_ctx.sock = ip_streamsocket(scanner_options, &errstr, 5);    break;
+      malware_daemon_ctx.sock = ip_streamsocket(scanner_options, &errstr, 5, NULL); break;
     default:
       /* compiler quietening */ break;
     }
diff --git a/src/src/spam.c b/src/src/spam.c
index 4cc4a9a..6954bee 100644
--- a/src/src/spam.c
+++ b/src/src/spam.c
@@ -344,7 +344,7 @@ start = time(NULL);
     for (;;)
       {
       /*XXX could potentially use TFO early-data here */
-      if (  (spamd_cctx.sock = ip_streamsocket(sd->hostspec, &errstr, 5)) >= 0
+      if (  (spamd_cctx.sock = ip_streamsocket(sd->hostspec, &errstr, 5, NULL)) >= 0
          || sd->retry <= 0
      )
     break;
diff --git a/test/confs/9350 b/test/confs/3901
similarity index 60%
copy from test/confs/9350
copy to test/confs/3901
index 1ac5ebe..5c7e729 100644
--- a/test/confs/9350
+++ b/test/confs/3901
@@ -1,4 +1,4 @@
-# Exim test configuration 3650
+# Exim test configuration 9351


SERVER=

@@ -10,13 +10,13 @@ primary_hostname = myhost.test.ex

acl_smtp_rcpt = check_recipient

-tls_advertise_hosts = *
tls_certificate = ${if eq {SERVER}{server}{DIR/aux-fixed/cert1}fail}
tls_privatekey = ${if eq {SERVER}{server}{DIR/aux-fixed/cert1}fail}

tls_verify_hosts = HOSTIPV4
tls_verify_certificates = ${if eq {SERVER}{server}{DIR/aux-fixed/cert2}fail}

+queue_only

# ----- ACL -----

@@ -28,6 +28,24 @@ check_recipient:
accept


+# ----- Route -----
+
+begin routers
+
+all:
+  driver = accept
+  transport = server
+  errors_to =
+
+begin transports
+
+server:
+  driver =        smtp
+  hosts =        127.0.0.1
+  allow_localhost
+  port =        PORT_D
+  hosts_require_auth =    *
+
 # ----- Authentication -----


 begin authenticators
@@ -35,7 +53,15 @@ begin authenticators
 dovecot:
   driver = dovecot
   public_name = PLAIN
-  server_socket = /var/run/dovecot/auth-client
+  server_socket = 127.0.0.1 PORT_S
+.ifdef TRUSTED
+  server_tls =    true
+.endif
   server_set_id = $auth1


+client:
+ driver = plaintext
+ public_name = PLAIN
+ client_send = ^username^mysecret
+
# End
diff --git a/test/confs/9350 b/test/confs/9350
index 1ac5ebe..290fadc 100644
--- a/test/confs/9350
+++ b/test/confs/9350
@@ -1,4 +1,4 @@
-# Exim test configuration 3650
+# Exim test configuration 9350

SERVER=

diff --git a/test/log/3901 b/test/log/3901
new file mode 100644
index 0000000..e9cef98
--- /dev/null
+++ b/test/log/3901
@@ -0,0 +1,7 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@??? U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaX-0005vi-00 => a@??? R=all T=server H=127.0.0.1 [127.0.0.1] A=client C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= <> H=localhost (myhost.test.ex) [127.0.0.1] P=esmtpa A=dovecot:goodman S=sss id=E10HmaX-0005vi-00@???
diff --git a/test/scripts/3900-Dovecot/3901 b/test/scripts/3900-Dovecot/3901
new file mode 100644
index 0000000..1fc5001
--- /dev/null
+++ b/test/scripts/3900-Dovecot/3901
@@ -0,0 +1,38 @@
+# dovecot server, inet, PLAIN method
+#
+# This uses a script emulating dovecot so has potential to be wrong.
+# We could do with an independent testcase against a real Dovecot,
+# but that needs to be conditioned on finding that on the test
+# platform and configuring it to match the testcase.
+# See 9350 for a start.
+#
+exim -bd -DSERVER=server -oX PORT_D
+****
+server PORT_S
+>LF>VERSION\x091\x090
+>LF>MECH\x09PLAIN
+>LF>DONE
+<<VERSION\x091\x090
+<CPID
+<AUTH\x091\x09PLAIN\x09service=smtp
+>LF>OK\x091\x09user=goodman
+*eof
+****
+#
+exim -odi a@???
+****
+#
+killdaemon
+#
+#exim -d+all -bd -DSERVER=server -DTRUSTED -oX PORT_D
+#****
+#background
+#perl -e "system('socat OPENSSL-LISTEN:PORT_S,reuseaddr,fork,cert=DIR/aux-fixed/cert1,verify=0 EXEC:\'/bin/echo VERSION\\t1\\t0\\nAUTH\\t1\\tPLAIN\\tservice=smtp\'');"
+#****
+##
+#exim -odi a@???
+#****
+##
+#killdaemon
+no_stdout_check
+no_msglog_check
diff --git a/test/scripts/3900-Dovecot/REQUIRES b/test/scripts/3900-Dovecot/REQUIRES
new file mode 100644
index 0000000..575bc6b
--- /dev/null
+++ b/test/scripts/3900-Dovecot/REQUIRES
@@ -0,0 +1 @@
+authenticator dovecot